diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index d2a017bd2a023..af071c706856e 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -31,3 +31,5 @@ ec2cc761bc7067712ecc7734502f703fe3b024c8 c682aa162b0d41e21cc6748f4fecfe01efb69d1f # reformat with updated edition 2024 1fcae03369abb4c2cc180cd5a49e1f4440a81300 +# Breaking up of compiletest runtest.rs +60600a6fa403216bfd66e04f948b1822f6450af7 diff --git a/.gitattributes b/.gitattributes index d29c15fe712f3..8700d5d6dcff1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,6 +5,7 @@ *.h rust *.rs rust diff=rust *.fixed linguist-language=Rust +*.pp linguist-language=Rust *.mir linguist-language=Rust src/etc/installer/gfx/* binary src/vendor/** -text diff --git a/.github/ISSUE_TEMPLATE/bootstrap.md b/.github/ISSUE_TEMPLATE/bootstrap.md index 8d72eae85931f..344ea30e1fc0b 100644 --- a/.github/ISSUE_TEMPLATE/bootstrap.md +++ b/.github/ISSUE_TEMPLATE/bootstrap.md @@ -32,7 +32,7 @@ Describe what you expected to happen. Describe what actually happened. --> -### Bootstrap configuration (config.toml) +### Bootstrap configuration (bootstrap.toml) ```toml ``` diff --git a/.github/ISSUE_TEMPLATE/library_tracking_issue.md b/.github/ISSUE_TEMPLATE/library_tracking_issue.md index 934312662beb6..d56da9d5d025a 100644 --- a/.github/ISSUE_TEMPLATE/library_tracking_issue.md +++ b/.github/ISSUE_TEMPLATE/library_tracking_issue.md @@ -2,7 +2,7 @@ name: Library Tracking Issue about: A tracking issue for an unstable library feature. title: Tracking Issue for XXX -labels: C-tracking-issue, T-libs-api +labels: C-tracking-issue, T-libs-api, S-tracking-unimplemented --- +(Remember to update the `S-tracking-*` label when checking boxes.) + - [ ] Implementation: #... - [ ] Final comment period (FCP)[^1] - [ ] Stabilization PR diff --git a/.github/ISSUE_TEMPLATE/tracking_issue.md b/.github/ISSUE_TEMPLATE/tracking_issue.md index 3a9d8408b3c9d..aedc15a54c274 100644 --- a/.github/ISSUE_TEMPLATE/tracking_issue.md +++ b/.github/ISSUE_TEMPLATE/tracking_issue.md @@ -41,7 +41,10 @@ for larger features an implementation could be broken up into multiple PRs. - [ ] Implement the RFC (cc @rust-lang/XXX -- can anyone write up mentoring instructions?) - [ ] Adjust documentation ([see instructions on rustc-dev-guide][doc-guide]) -- [ ] Formatting for new syntax has been added to the [Style Guide] ([nightly-style-procedure]) +- [ ] Style updates for any new syntax ([nightly-style-procedure]) + - [ ] Style team decision on new formatting + - [ ] Formatting for new syntax has been added to the [Style Guide] + - [ ] (non-blocking) Formatting has been implemented in `rustfmt` - [ ] Stabilization PR ([see instructions on rustc-dev-guide][stabilization-guide]) [stabilization-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#stabilization-pr diff --git a/.github/ISSUE_TEMPLATE/tracking_issue_future.md b/.github/ISSUE_TEMPLATE/tracking_issue_future.md new file mode 100644 index 0000000000000..f04a458d8a5aa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tracking_issue_future.md @@ -0,0 +1,54 @@ +--- +name: Future Incompatibility Tracking Issue +about: A tracking issue for a future-incompatible lint +title: Tracking Issue for future-incompatibility lint XXX +labels: C-tracking-issue C-future-incompatibility T-compiler A-lints +--- + + +This is the **tracking issue** for the `YOUR_LINT_NAME_HERE` future-compatibility warning and other related errors. The goal of this page is describe why this change was made and how you can fix code that is affected by it. It also provides a place to ask questions or register a complaint if you feel the change should not be made. For more information on the policy around future-compatibility warnings, see our [breaking change policy guidelines][guidelines]. + +[guidelines]: https://rustc-dev-guide.rust-lang.org/bug-fix-procedure.html + +### What is the warning for? + +*Describe the conditions that trigger the warning.* + +### Why was this change made? + +*Explain why this change was made. If there is additional context, like an MCP, link it here.* + +### Example + +```rust +// Include an example here. +``` + +### Recommendations + +*Give some recommendations on how a user can avoid the lint.* + +### When will this warning become a hard error? + +*If known, describe the future plans. For example, how long you anticipate this being a warning, or if there are other factors that will influence the anticipated closure.* + +### Steps + +- [ ] Implement the lint +- [ ] Raise lint level to deny +- [ ] Make lint report in dependencies +- [ ] Switch to a hard error + +### Implementation history + + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73467ad267e8e..93316b9cff7b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,13 +53,20 @@ jobs: steps: - name: Checkout the source code uses: actions/checkout@v4 + # Cache citool to make its build faster, as it's in the critical path. + # The rust-cache doesn't bleed into the main `job`, so it should not affect any other + # Rust compilation. + - name: Cache citool + uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 + with: + workspaces: src/ci/citool - name: Calculate the CI job matrix env: COMMIT_MESSAGE: ${{ github.event.head_commit.message }} run: | cd src/ci/citool - cargo test - cargo run calculate-job-matrix >> $GITHUB_OUTPUT + CARGO_INCREMENTAL=0 cargo test + CARGO_INCREMENTAL=0 cargo run calculate-job-matrix >> $GITHUB_OUTPUT id: jobs job: name: ${{ matrix.full_name }} @@ -68,6 +75,9 @@ jobs: timeout-minutes: 360 env: CI_JOB_NAME: ${{ matrix.name }} + CI_JOB_DOC_URL: ${{ matrix.doc_url }} + GITHUB_WORKFLOW_RUN_ID: ${{ github.run_id }} + GITHUB_REPOSITORY: ${{ github.repository }} CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse # commit of PR sha or commit sha. `GITHUB_SHA` is not accurate for PRs. HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} @@ -81,6 +91,17 @@ jobs: # Check the `calculate_matrix` job to see how is the matrix defined. include: ${{ fromJSON(needs.calculate_matrix.outputs.jobs) }} steps: + - name: Install cargo in AWS CodeBuild + if: matrix.codebuild + run: | + # Check if cargo is installed + if ! command -v cargo &> /dev/null; then + echo "Cargo not found, installing Rust..." + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal + # Make cargo available in PATH + echo "$HOME/.cargo/bin" >> $GITHUB_PATH + fi + - name: disable git crlf conversion run: git config --global core.autocrlf false @@ -115,9 +136,6 @@ jobs: # which then uses log commands to actually set them. EXTRA_VARIABLES: ${{ toJson(matrix.env) }} - - name: setup upstream remote - run: src/ci/scripts/setup-upstream-remote.sh - - name: ensure the channel matches the target branch run: src/ci/scripts/verify-channel.sh @@ -158,6 +176,8 @@ jobs: run: src/ci/scripts/install-ninja.sh - name: enable ipv6 on Docker + # Don't run on codebuild because systemctl is not available + if: ${{ !matrix.codebuild }} run: src/ci/scripts/enable-docker-ipv6.sh # Disable automatic line ending conversion (again). On Windows, when we're @@ -182,23 +202,28 @@ jobs: - name: show the current environment run: src/ci/scripts/dump-environment.sh - # Temporary fix to unblock CI - # Remove the latest Windows SDK for 32-bit Windows MSVC builds. - # See issue https://github.com/rust-lang/rust/issues/137733 for more details. - - name: Remove Windows SDK 10.0.26100.0 - shell: powershell - if: ${{ matrix.name == 'i686-msvc-1' || matrix.name == 'i686-msvc-2' || matrix.name == 'dist-i686-msvc' }} + # Pre-build citool before the following step uninstalls rustup + # Build it into the build directory, to avoid modifying sources + - name: build citool run: | - $kits = (Get-ItemProperty -path 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots').KitsRoot10 - $sdk_version = "10.0.26100.0" - - foreach ($kind in 'Bin', 'Lib', 'Include') { - Remove-Item -Force -Recurse $kits\$kind\$sdk_version -ErrorAction Continue - } + cd src/ci/citool + CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=../../../build/citool cargo build - name: run the build - # Redirect stderr to stdout to avoid reordering the two streams in the GHA logs. - run: src/ci/scripts/run-build-from-ci.sh 2>&1 + run: | + set +e + # Redirect stderr to stdout to avoid reordering the two streams in the GHA logs. + src/ci/scripts/run-build-from-ci.sh 2>&1 + STATUS=$? + set -e + + if [[ "$STATUS" -ne 0 && -n "$CI_JOB_DOC_URL" ]]; then + echo "****************************************************************************" + echo "To find more information about this job, visit the following URL:" + echo "$CI_JOB_DOC_URL" + echo "****************************************************************************" + fi + exit ${STATUS} env: AWS_ACCESS_KEY_ID: ${{ env.CACHES_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }} @@ -232,16 +257,37 @@ jobs: # erroring about invalid credentials instead. if: github.event_name == 'push' || env.DEPLOY == '1' || env.DEPLOY_ALT == '1' + - name: postprocess metrics into the summary + # This step is not critical, and if some I/O problem happens, we don't want + # to cancel the build. + continue-on-error: true + run: | + if [ -f build/metrics.json ]; then + METRICS=build/metrics.json + elif [ -f obj/build/metrics.json ]; then + METRICS=obj/build/metrics.json + else + echo "No metrics.json found" + exit 0 + fi + + # Get closest bors merge commit + PARENT_COMMIT=`git rev-list --author='bors ' -n1 --first-parent HEAD^1` + + ./build/citool/debug/citool postprocess-metrics \ + --job-name ${CI_JOB_NAME} \ + --parent ${PARENT_COMMIT} \ + ${METRICS} >> ${GITHUB_STEP_SUMMARY} + - name: upload job metrics to DataDog + # This step is not critical, and if some I/O problem happens, we don't want + # to cancel the build. + continue-on-error: true if: needs.calculate_matrix.outputs.run_type != 'pr' env: - DATADOG_SITE: datadoghq.com DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }} DD_GITHUB_JOB_NAME: ${{ matrix.full_name }} - run: | - cd src/ci - npm ci - python3 scripts/upload-build-metrics.py ../../build/cpu-usage.csv + run: ./build/citool/debug/citool upload-build-metrics build/cpu-usage.csv # This job isused to tell bors the final status of the build, as there is no practical way to detect # when a workflow is successful listening to webhooks only in our current bors implementation (homu). diff --git a/.github/workflows/ghcr.yml b/.github/workflows/ghcr.yml index 052c9ae72b87e..c2c0c11f0083d 100644 --- a/.github/workflows/ghcr.yml +++ b/.github/workflows/ghcr.yml @@ -5,6 +5,9 @@ # Docker Hub has a rate limit, while ghcr.io doesn't. # Those images are pushed to ghcr.io by this job. # +# While Docker Hub rate limit *shouldn't* be an issue on GitHub Actions, +# it certainly is for AWS codebuild. +# # Note that authenticating to DockerHub or other registries isn't possible # for PR jobs, because forks can't access secrets. # That's why we use ghcr.io: it has no rate limit and it doesn't require authentication. @@ -54,6 +57,10 @@ jobs: "ubuntu:22.04" # Mirrored because used by all linux CI jobs, including mingw-check-tidy "moby/buildkit:buildx-stable-1" + # Mirrored because used when CI is running inside a Docker container + "alpine:3.4" + # Mirrored because used by dist-x86_64-linux + "centos:7" ) # Mirror each image from DockerHub to ghcr.io diff --git a/.github/workflows/post-merge.yml b/.github/workflows/post-merge.yml new file mode 100644 index 0000000000000..ca088ba31fdf9 --- /dev/null +++ b/.github/workflows/post-merge.yml @@ -0,0 +1,53 @@ +# Workflow that runs after a merge to master, analyses changes in test executions +# and posts the result to the merged PR. + +name: Post merge analysis + +on: + push: + branches: + - master + +jobs: + analysis: + runs-on: ubuntu-24.04 + if: github.repository == 'rust-lang/rust' + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + # Make sure that we have enough commits to find the parent merge commit. + # Since all merges should be through merge commits, fetching two commits + # should be enough to get the parent bors merge commit. + fetch-depth: 2 + - name: Perform analysis and send PR + env: + GH_TOKEN: ${{ github.token }} + run: | + # Give GitHub some time to propagate the information that the PR was merged + sleep 60 + + # Get closest bors merge commit + PARENT_COMMIT=`git rev-list --author='bors ' -n1 --first-parent HEAD^1` + echo "Parent: ${PARENT_COMMIT}" + + # Find PR for the current commit + HEAD_PR=`gh pr list --search "${{ github.sha }}" --state merged --json number --jq '.[0].number'` + if [ -z "${HEAD_PR}" ]; then + echo "PR for commit SHA ${{ github.sha }} not found, exiting" + exit 1 + fi + echo "HEAD: ${{ github.sha }} (#${HEAD_PR})" + + cd src/ci/citool + + printf "
\nWhat is this?\n" >> output.log + printf "This is an experimental post-merge analysis report that shows differences in test outcomes between the merged PR and its parent PR.\n" >> output.log + printf "
\n\n" >> output.log + + cargo run --release post-merge-report ${PARENT_COMMIT} ${{ github.sha }} >> output.log + + cat output.log + + gh pr comment ${HEAD_PR} -F output.log diff --git a/.gitignore b/.gitignore index 2184e04f16b27..a549b6e6e56c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # This file should only ignore things that are generated during a `x.py` build, # generated by common IDEs, and optional files controlled by the user that -# affect the build (such as config.toml). +# affect the build (such as bootstrap.toml). # In particular, things like `mir_dump` should not be listed here; they are only # created during manual debugging and many people like to clean up instead of # having git ignore such leftovers. You can use `.git/info/exclude` to @@ -34,6 +34,7 @@ Session.vim !/tests/run-make/thumb-none-qemu/example/.cargo ## Configuration +/bootstrap.toml /config.toml /Makefile config.mk @@ -53,6 +54,7 @@ no_llvm_build /target /library/target /src/bootstrap/target +/src/ci/citool/target /src/tools/x/target # Created by `x vendor` /vendor diff --git a/.gitmodules b/.gitmodules index 97a0c0c54cf9f..fbf2f59b38da1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -45,7 +45,7 @@ shallow = true [submodule "src/tools/enzyme"] path = src/tools/enzyme - url = https://github.com/EnzymeAD/Enzyme.git + url = https://github.com/rust-lang/enzyme.git shallow = true [submodule "src/gcc"] path = src/gcc diff --git a/.ignore b/.ignore index 40d1513978fc6..b0c640fee3dac 100644 --- a/.ignore +++ b/.ignore @@ -1,2 +1,3 @@ -# Make vscode *not* count `config.toml` as ignored, so it is included in search +# Make vscode *not* count `bootstrap.toml` and `config.toml` as ignored, so it is included in search +!/bootstrap.toml !/config.toml diff --git a/.mailmap b/.mailmap index a791daa681d47..c3ce111bfe3b4 100644 --- a/.mailmap +++ b/.mailmap @@ -276,7 +276,7 @@ Jacob Greenfield Jacob Pratt Jacob Pratt Jake Goulding -Jake Goulding +Jake Goulding Jake Goulding Jake Vossen Jakob Degen @@ -292,6 +292,7 @@ James Hinshelwood James Miller James Perry James Sanderson +Jamie Hill-Daniel Jana Dönszelmann Jana Dönszelmann Jana Dönszelmann @@ -408,10 +409,13 @@ Luqman Aden Luqman Aden Lzu Tao Maik Klein +Maja Kądziołka +Maja Kądziołka Malo Jaffré Manish Goregaokar Mara Bos Marcell Pardavi +Marco Ieni <11428655+MarcoIeni@users.noreply.github.com> Marcus Klaas de Vries Margaret Meyerhofer Mark Mansi @@ -565,6 +569,9 @@ Robert Habermeier Robert Millar Roc Yu Rohit Joshi Rohit Joshi +Ross Smyth <18294397+RossSmyth@users.noreply.github.com> +Ross Smyth <18294397+RossSmyth@users.noreply.github.com> +Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Roxane Fruytier Rui Russell Johnston diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5ddff595f5d6..e155e253784aa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ For submodules, changes need to be made against the repository corresponding the submodule, and not the main `rust-lang/rust` repository. For subtrees, prefer sending a PR against the subtree's repository if it does -not need to be made against the main `rust-lang/rust` repostory (e.g. a +not need to be made against the main `rust-lang/rust` repository (e.g. a rustc-dev-guide change that does not accompany a compiler change). ## About the [rustc-dev-guide] diff --git a/Cargo.lock b/Cargo.lock index 72f2d4f6cd390..57157b32b9782 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,19 +4,13 @@ version = 4 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ - "gimli 0.28.1", + "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" @@ -34,18 +28,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy 0.7.35", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -170,12 +152,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" -dependencies = [ - "backtrace", -] +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "ar_archive_writer" @@ -183,7 +162,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01667f6f40216b9a0b2945e05fed5f1ad0ab6470e69cb9378001e37b1c0668e4" dependencies = [ - "object 0.36.7", + "object", ] [[package]] @@ -198,6 +177,48 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "askama" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7" +dependencies = [ + "askama_derive", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac" +dependencies = [ + "askama_parser", + "basic-toml", + "memchr", + "proc-macro2", + "quote", + "rustc-hash 2.1.1", + "serde", + "serde_derive", + "syn 2.0.101", +] + +[[package]] +name = "askama_parser" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f" +dependencies = [ + "memchr", + "serde", + "serde_derive", + "winnow 0.7.10", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -206,17 +227,17 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", - "miniz_oxide 0.7.4", - "object 0.32.2", + "miniz_oxide", + "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -227,9 +248,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "basic-toml" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" dependencies = [ "serde", ] @@ -245,15 +266,15 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "blake3" -version = "1.5.5" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", "arrayvec", @@ -273,9 +294,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "regex-automata 0.4.9", @@ -320,9 +341,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytecount" @@ -330,17 +351,11 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "camino" @@ -355,10 +370,10 @@ dependencies = [ name = "cargo-miri" version = "0.1.0" dependencies = [ - "cargo_metadata 0.18.1", + "cargo_metadata 0.19.2", "directories", "rustc-build-sysroot", - "rustc_tools_util 0.4.0", + "rustc_tools_util 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version", "serde", "serde_json", @@ -389,16 +404,16 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.19.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", "cargo-platform", "semver", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -428,22 +443,22 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "chrono-tz" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c6ac4f2c0bf0f44e9161aec9675e1050aa4a530663c4a9e37e108fa948bca9f" +checksum = "efdce149c370f133a071ca8ef6ea340b7b88748ab0810097a9e2976eaa34b4f3" dependencies = [ "chrono", "chrono-tz-build", @@ -452,9 +467,9 @@ dependencies = [ [[package]] name = "chrono-tz-build" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7" +checksum = "8f10f8c9340e31fc120ff885fcdb54a0b48e474bbd77cab557f0c30a3e569402" dependencies = [ "parse-zoneinfo", "phf_codegen", @@ -472,9 +487,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.26" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -492,9 +507,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.26" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -504,14 +519,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] @@ -522,12 +537,14 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clippy" -version = "0.1.87" +version = "0.1.89" dependencies = [ "anstream", + "askama", "cargo_metadata 0.18.1", "clippy_config", "clippy_lints", + "clippy_lints_internal", "clippy_utils", "color-print", "filetime", @@ -535,25 +552,24 @@ dependencies = [ "if_chain", "itertools", "parking_lot", - "pulldown-cmark 0.11.3", + "pulldown-cmark", "quote", "regex", - "rinja", "rustc_tools_util 0.4.2", "serde", "serde_json", - "syn 2.0.96", + "syn 2.0.101", "tempfile", "termize", "tokio", "toml 0.7.8", - "ui_test 0.26.5", + "ui_test", "walkdir", ] [[package]] name = "clippy_config" -version = "0.1.87" +version = "0.1.89" dependencies = [ "clippy_utils", "itertools", @@ -566,19 +582,17 @@ dependencies = [ name = "clippy_dev" version = "0.0.1" dependencies = [ - "aho-corasick", "chrono", "clap", "indoc", "itertools", "opener", - "shell-escape", "walkdir", ] [[package]] name = "clippy_lints" -version = "0.1.87" +version = "0.1.89" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -586,12 +600,9 @@ dependencies = [ "clippy_utils", "itertools", "quine-mc_cluskey", - "regex", "regex-syntax 0.8.5", "semver", "serde", - "serde_json", - "tempfile", "toml 0.7.8", "unicode-normalization", "unicode-script", @@ -599,9 +610,19 @@ dependencies = [ "walkdir", ] +[[package]] +name = "clippy_lints_internal" +version = "0.0.1" +dependencies = [ + "clippy_config", + "clippy_utils", + "regex", + "rustc-semver", +] + [[package]] name = "clippy_utils" -version = "0.1.87" +version = "0.1.89" dependencies = [ "arrayvec", "itertools", @@ -621,16 +642,16 @@ dependencies = [ [[package]] name = "color-eyre" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +checksum = "e6e1761c0e16f8883bbbb8ce5990867f4f06bf11a0253da6495a04ce4b6ef0ec" dependencies = [ "backtrace", "color-spantrace", "eyre", "indenter", "once_cell", - "owo-colors", + "owo-colors 4.2.0", "tracing-error", ] @@ -652,17 +673,17 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] name = "color-spantrace" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +checksum = "2ddd8d5bfda1e11a501d0a7303f3bfed9aa632ebdb859be40d0fd70478ed70d5" dependencies = [ "once_cell", - "owo-colors", + "owo-colors 4.2.0", "tracing-core", "tracing-error", ] @@ -694,8 +715,8 @@ name = "compiletest" version = "0.0.0" dependencies = [ "anstyle-svg", - "anyhow", "build_helper", + "camino", "colored", "diff", "getopts", @@ -705,6 +726,7 @@ dependencies = [ "libc", "miow", "miropt-test-tools", + "rayon", "regex", "rustfix", "semver", @@ -714,14 +736,14 @@ dependencies = [ "tracing-subscriber", "unified-diff", "walkdir", - "windows 0.59.0", + "windows", ] [[package]] name = "console" -version = "0.15.10" +version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" dependencies = [ "encode_unicode", "libc", @@ -747,18 +769,19 @@ name = "coverage-dump" version = "0.1.0" dependencies = [ "anyhow", + "itertools", "leb128", "md-5", - "miniz_oxide 0.7.4", + "miniz_oxide", "regex", "rustc-demangle", ] [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -774,9 +797,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -818,9 +841,9 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.5" +version = "3.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" +checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" dependencies = [ "nix", "windows-sys 0.59.0", @@ -843,9 +866,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.78+curl-8.11.0" +version = "0.4.80+curl-8.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eec768341c5c7789611ae51cf6c459099f22e64a5d5d0ce4892434e33821eaf" +checksum = "55f7df2eac63200c3ab25bde3b2268ef2ee56af3d238e76d61f01c3c49bff734" dependencies = [ "cc", "libc", @@ -858,9 +881,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -868,27 +891,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] @@ -908,24 +931,15 @@ dependencies = [ "winapi", ] -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - [[package]] name = "derive-where" -version = "1.2.7" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" +checksum = "e73f2692d4bd3cac41dca28934a39894200c9fabf49586d77d0e5954af1d7902" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] @@ -946,7 +960,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] @@ -956,19 +970,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] name = "derive_setters" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" +checksum = "d9c848e86c87e5cc305313041c5677d4d95d60baa71cf95e5f6ea2554bb629ff" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] @@ -989,11 +1003,11 @@ dependencies = [ [[package]] name = "directories" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ - "dirs-sys", + "dirs-sys 0.5.0", ] [[package]] @@ -1002,7 +1016,7 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.1", ] [[package]] @@ -1023,10 +1037,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.6", "windows-sys 0.48.0", ] +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.0", + "windows-sys 0.59.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1034,7 +1060,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -1046,26 +1072,26 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] name = "dissimilar" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" +checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921" [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elsa" -version = "1.11.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2343daaeabe09879d4ea058bb4f1e63da3fc07dadc6634e01bda1b3d6a9d9d2b" +checksum = "9abf33c656a7256451ebb7d0082c5a471820c31269e49d807c538c252352186e" dependencies = [ "stable_deref_trait", ] @@ -1097,28 +1123,28 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", "env_filter", - "humantime", + "jiff", "log", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -1181,12 +1207,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", - "miniz_oxide 0.8.3", + "miniz_oxide", ] [[package]] @@ -1231,9 +1257,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "form_urlencoded" @@ -1325,7 +1351,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] @@ -1363,8 +1389,8 @@ name = "generate-copyright" version = "0.1.0" dependencies = [ "anyhow", + "askama", "cargo_metadata 0.18.1", - "rinja", "serde", "serde_json", "thiserror 1.0.69", @@ -1398,9 +1424,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -1409,31 +1435,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - -[[package]] -name = "gimli" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e1d97fbe9722ba9bbd0c97051c2956e726562b61f86a25a4360398a40edfc9" -dependencies = [ - "fallible-iterator", - "indexmap", - "stable_deref_trait", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -1455,9 +1464,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "globset" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" dependencies = [ "aho-corasick", "bstr", @@ -1477,20 +1486,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ - "ahash", "allocator-api2", -] - -[[package]] -name = "hashbrown" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" -dependencies = [ + "equivalent", "foldhash", "serde", ] @@ -1547,16 +1548,14 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.27.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" +checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", "markup5ever", - "proc-macro2", - "quote", - "syn 2.0.96", + "match_token", ] [[package]] @@ -1570,22 +1569,23 @@ dependencies = [ [[package]] name = "humantime" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core", ] [[package]] @@ -1599,14 +1599,15 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", - "yoke", + "potential_utf", + "yoke 0.8.0", "zerofrom", - "zerovec", + "zerovec 0.11.2", ] [[package]] @@ -1618,16 +1619,29 @@ dependencies = [ "displaydoc", "icu_list_data", "icu_locid_transform", - "icu_provider", + "icu_provider 1.5.0", "regex-automata 0.2.0", - "writeable", + "writeable 0.5.5", ] [[package]] name = "icu_list_data" -version = "1.5.0" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b1a7fbdbf3958f1be8354cb59ac73f165b7b7082d447ff2090355c9a069120" + +[[package]] +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1825170d2c6679cb20dbd96a589d034e49f698aed9a2ef4fafc9a0101ed298f" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap 0.8.0", + "tinystr 0.8.1", + "writeable 0.6.1", + "zerovec 0.11.2", +] [[package]] name = "icu_locid" @@ -1636,10 +1650,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", + "litemap 0.7.5", + "tinystr 0.7.6", + "writeable 0.5.5", + "zerovec 0.10.4", ] [[package]] @@ -1651,61 +1665,59 @@ dependencies = [ "displaydoc", "icu_locid", "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", + "icu_provider 1.5.0", + "tinystr 0.7.6", + "zerovec 0.10.4", ] [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", - "icu_provider", + "icu_provider 2.0.0", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", + "zerovec 0.11.2", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", + "icu_provider 2.0.0", + "potential_utf", + "zerotrie", + "zerovec 0.11.2", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" [[package]] name = "icu_provider" @@ -1717,11 +1729,28 @@ dependencies = [ "icu_locid", "icu_provider_macros", "stable_deref_trait", - "tinystr", - "writeable", - "yoke", + "tinystr 0.7.6", + "writeable 0.5.5", + "yoke 0.7.5", + "zerofrom", + "zerovec 0.10.4", +] + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr 0.8.1", + "writeable 0.6.1", + "yoke 0.8.0", "zerofrom", - "zerovec", + "zerotrie", + "zerovec 0.11.2", ] [[package]] @@ -1732,9 +1761,9 @@ checksum = "d6324dfd08348a8e0374a447ebd334044d766b1839bb8d5ccf2482a99a77c0bc" dependencies = [ "icu_locid", "icu_locid_transform", - "icu_provider", - "tinystr", - "zerovec", + "icu_provider 1.5.0", + "tinystr 0.7.6", + "zerovec 0.10.4", ] [[package]] @@ -1745,7 +1774,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] @@ -1773,9 +1802,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -1811,20 +1840,20 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.7.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown", "serde", ] [[package]] name = "indicatif" -version = "0.17.9" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" dependencies = [ "console", "number_prefix", @@ -1841,9 +1870,9 @@ checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "generic-array", ] @@ -1897,16 +1926,41 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jiff" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.3", "libc", ] @@ -1926,7 +1980,7 @@ version = "0.1.0" dependencies = [ "fs-err", "getopts", - "jsonpath_lib", + "jsonpath-rust", "regex", "serde_json", "shlex", @@ -1946,14 +2000,16 @@ dependencies = [ ] [[package]] -name = "jsonpath_lib" -version = "0.3.0" +name = "jsonpath-rust" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa63191d68230cccb81c5aa23abd53ed64d83337cacbb25a7b8c7979523774f" +checksum = "6a37c2c87b8d16e788ce359660fead0ea5f4ed29ff400d55be74a4e01d1817d9" dependencies = [ - "log", - "serde", + "pest", + "pest_derive", + "regex", "serde_json", + "thiserror 2.0.12", ] [[package]] @@ -1968,6 +2024,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "levenshtein" version = "1.0.5" @@ -1976,15 +2038,15 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "lexopt" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401" +checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libdbus-sys" @@ -1998,9 +2060,9 @@ dependencies = [ [[package]] name = "libffi" -version = "3.2.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2" +checksum = "ebfd30a67b482a08116e753d0656cb626548cf4242543e5cc005be7639d99838" dependencies = [ "libc", "libffi-sys", @@ -2008,28 +2070,28 @@ dependencies = [ [[package]] name = "libffi-sys" -version = "2.3.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36115160c57e8529781b4183c2bb51fdc1f6d6d1ed345591d84be7703befb3c" +checksum = "f003aa318c9f0ee69eb0ada7c78f5c9d2fedd2ceb274173b5c7ff475eee584a3" dependencies = [ "cc", ] [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] name = "libm" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" @@ -2044,9 +2106,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.21" +version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" dependencies = [ "cc", "libc", @@ -2073,15 +2135,21 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.15" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.4" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "litemap" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lld-wrapper" @@ -2110,9 +2178,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "lzma-sys" @@ -2133,9 +2201,9 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "markup5ever" -version = "0.12.1" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" +checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" dependencies = [ "log", "phf", @@ -2145,6 +2213,17 @@ dependencies = [ "tendril", ] +[[package]] +name = "match_token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "matchers" version = "0.1.0" @@ -2166,9 +2245,9 @@ dependencies = [ [[package]] name = "measureme" -version = "11.0.1" +version = "12.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfa4a40f09af7aa6faef38285402a78847d0d72bf8827006cd2a332e1e6e4a8d" +checksum = "570a507d8948a66a97f42cbbaf8a6bb9516a51017d4ee949502ad7a10a864395" dependencies = [ "log", "memmap2", @@ -2193,22 +2272,6 @@ dependencies = [ "libc", ] -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "minifier" version = "0.3.5" @@ -2223,18 +2286,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.3" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -2253,23 +2307,24 @@ name = "miri" version = "0.1.0" dependencies = [ "aes", + "bitflags", "chrono", "chrono-tz", "colored", "directories", - "getrandom 0.3.1", + "getrandom 0.3.3", "libc", "libffi", "libloading", "measureme", - "rand 0.9.0", + "rand 0.9.1", "regex", "rustc_version", "smallvec", "tempfile", "tikv-jemalloc-sys", - "ui_test 0.28.0", - "windows-sys 0.52.0", + "ui_test", + "windows-sys 0.59.0", ] [[package]] @@ -2284,9 +2339,9 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags", "cfg-if", @@ -2365,12 +2420,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" version = "0.1.46" @@ -2428,12 +2477,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] -name = "object" -version = "0.32.2" +name = "objc2-core-foundation" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "memchr", + "bitflags", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" +dependencies = [ + "libc", + "objc2-core-foundation", ] [[package]] @@ -2444,11 +2503,11 @@ checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", "flate2", - "hashbrown 0.15.2", + "hashbrown", "indexmap", "memchr", "ruzstd", - "wasmparser 0.222.0", + "wasmparser 0.222.1", ] [[package]] @@ -2462,9 +2521,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "opener" @@ -2480,15 +2539,15 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" dependencies = [ "cc", "libc", @@ -2511,7 +2570,6 @@ dependencies = [ "humansize", "humantime", "log", - "serde", "serde_json", "sysinfo", "tabled", @@ -2538,6 +2596,12 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "owo-colors" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" + [[package]] name = "pad" version = "0.1.6" @@ -2612,51 +2676,77 @@ dependencies = [ ] [[package]] -name = "phf" -version = "0.11.3" +name = "pest" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ - "phf_shared 0.11.3", + "memchr", + "thiserror 2.0.12", + "ucd-trie", ] [[package]] -name = "phf_codegen" -version = "0.11.3" +name = "pest_derive" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "pest", + "pest_generator", ] [[package]] -name = "phf_generator" -version = "0.10.0" +name = "pest_generator" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] -name = "phf_generator" +name = "pest_meta" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "phf" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_shared 0.11.3", - "rand 0.8.5", + "phf_shared", ] [[package]] -name = "phf_shared" -version = "0.10.0" +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "siphasher 0.3.11", + "phf_shared", + "rand 0.8.5", ] [[package]] @@ -2665,7 +2755,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher 1.0.1", + "siphasher", ] [[package]] @@ -2682,9 +2772,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "polonius-engine" @@ -2699,23 +2789,35 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] -name = "powerfmt" -version = "0.2.0" +name = "portable-atomic-util" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec 0.11.2", +] [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -2730,7 +2832,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abec3fb083c10660b3854367697da94c674e9e82aa7511014dc958beeb7215e9" dependencies = [ - "owo-colors", + "owo-colors 3.5.0", "pad", ] @@ -2742,33 +2844,22 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "psm" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" +checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" dependencies = [ "cc", ] -[[package]] -name = "pulldown-cmark" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" -dependencies = [ - "bitflags", - "memchr", - "unicase", -] - [[package]] name = "pulldown-cmark" version = "0.11.3" @@ -2801,13 +2892,19 @@ checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.8.5" @@ -2821,13 +2918,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.0", - "zerocopy 0.8.14", + "rand_core 0.9.3", ] [[package]] @@ -2847,7 +2943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -2856,26 +2952,25 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] name = "rand_core" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.1", - "zerocopy 0.8.14", + "getrandom 0.3.3", ] [[package]] name = "rand_xoshiro" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" dependencies = [ - "rand_core 0.6.4", + "rand_core 0.9.3", ] [[package]] @@ -2900,9 +2995,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags", ] @@ -2913,11 +3008,22 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", "thiserror 1.0.69", ] +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.12", +] + [[package]] name = "regex" version = "1.11.1" @@ -2993,74 +3099,26 @@ dependencies = [ "walkdir", ] -[[package]] -name = "rinja" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5" -dependencies = [ - "humansize", - "itoa", - "percent-encoding", - "rinja_derive", -] - -[[package]] -name = "rinja_derive" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b" -dependencies = [ - "basic-toml", - "memchr", - "mime", - "mime_guess", - "proc-macro2", - "quote", - "rinja_parser", - "rustc-hash 2.1.1", - "serde", - "syn 2.0.96", -] - -[[package]] -name = "rinja_parser" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610" -dependencies = [ - "memchr", - "nom", - "serde", -] - -[[package]] -name = "rls" -version = "2.0.0" -dependencies = [ - "serde_json", -] - [[package]] name = "run_make_support" version = "0.2.0" dependencies = [ "bstr", "build_helper", - "gimli 0.31.1", + "gimli", "libc", - "object 0.36.7", + "object", "regex", "serde_json", "similar", - "wasmparser 0.219.1", + "wasmparser 0.219.2", ] [[package]] name = "rustc-build-sysroot" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6d984a9db43148467059309bd1e5ad577085162f695d9fe2cf3543aeb25cd38" +checksum = "fb332121f7845c6bd016f9655cf22f03c2999df936694b624a88669a78667d98" dependencies = [ "anyhow", "rustc_version", @@ -3086,6 +3144,12 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc-literal-escaper" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04" + [[package]] name = "rustc-main" version = "0.0.0" @@ -3099,33 +3163,26 @@ dependencies = [ ] [[package]] -name = "rustc-rayon" +name = "rustc-rayon-core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd9fb077db982d7ceb42a90471e5a69a990b58f71e06f0d8340bb2cf35eb751" +checksum = "2f42932dcd3bcbe484b38a3ccf79b7906fac41c02d408b5b1bac26da3416efdb" dependencies = [ - "either", - "indexmap", - "rustc-rayon-core", + "crossbeam-deque", + "crossbeam-utils", ] [[package]] -name = "rustc-rayon-core" -version = "0.5.0" +name = "rustc-semver" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67668daaf00e359c126f6dcb40d652d89b458a008c8afa727a42a2d20fca0b7f" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] +checksum = "5be1bdc7edf596692617627bbfeaba522131b18e06ca4df2b6b689e3c5d5ce84" [[package]] name = "rustc-stable-hash" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2febf9acc5ee5e99d1ad0afcdbccc02d87aa3f857a1f01f825b80eacf8edfcd1" +checksum = "781442f29170c5c93b7185ad559492601acdc71d5bb0706f5868094f45cfcd08" [[package]] name = "rustc-std-workspace-alloc" @@ -3144,7 +3201,7 @@ name = "rustc_abi" version = "0.0.0" dependencies = [ "bitflags", - "rand 0.8.5", + "rand 0.9.1", "rand_xoshiro", "rustc_data_structures", "rustc_hashes", @@ -3178,10 +3235,10 @@ version = "0.0.0" dependencies = [ "bitflags", "memchr", + "rustc-literal-escaper", "rustc_ast_ir", "rustc_data_structures", "rustc_index", - "rustc_lexer", "rustc_macros", "rustc_serialize", "rustc_span", @@ -3197,7 +3254,6 @@ dependencies = [ "rustc_data_structures", "rustc_macros", "rustc_serialize", - "rustc_span", ] [[package]] @@ -3216,7 +3272,6 @@ dependencies = [ "rustc_index", "rustc_macros", "rustc_middle", - "rustc_parse", "rustc_session", "rustc_span", "rustc_target", @@ -3251,7 +3306,6 @@ version = "0.0.0" dependencies = [ "itertools", "rustc_ast", - "rustc_data_structures", "rustc_lexer", "rustc_span", "thin-vec", @@ -3279,14 +3333,12 @@ dependencies = [ "rustc_ast", "rustc_ast_pretty", "rustc_attr_data_structures", - "rustc_data_structures", "rustc_errors", "rustc_feature", "rustc_fluent_macro", "rustc_hir", "rustc_lexer", "rustc_macros", - "rustc_serialize", "rustc_session", "rustc_span", "thin-vec", @@ -3299,8 +3351,8 @@ dependencies = [ "icu_list", "icu_locid", "icu_locid_transform", - "icu_provider", - "zerovec", + "icu_provider 1.5.0", + "zerovec 0.10.4", ] [[package]] @@ -3336,6 +3388,7 @@ version = "0.0.0" dependencies = [ "rustc_ast", "rustc_ast_pretty", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -3362,15 +3415,15 @@ name = "rustc_codegen_llvm" version = "0.0.0" dependencies = [ "bitflags", - "gimli 0.30.0", + "gimli", "itertools", "libc", "measureme", - "object 0.36.7", + "object", "rustc-demangle", "rustc_abi", "rustc_ast", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_codegen_ssa", "rustc_data_structures", "rustc_errors", @@ -3407,13 +3460,13 @@ dependencies = [ "either", "itertools", "libc", - "object 0.36.7", + "object", "pathdiff", "regex", "rustc_abi", "rustc_arena", "rustc_ast", - "rustc_ast_pretty", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -3421,7 +3474,6 @@ dependencies = [ "rustc_fs_util", "rustc_hashes", "rustc_hir", - "rustc_hir_pretty", "rustc_incremental", "rustc_index", "rustc_macros", @@ -3434,15 +3486,14 @@ dependencies = [ "rustc_symbol_mangling", "rustc_target", "rustc_trait_selection", - "rustc_type_ir", "serde_json", "smallvec", "tempfile", "thin-vec", "thorin-dwp", "tracing", - "wasm-encoder 0.219.1", - "windows 0.59.0", + "wasm-encoder 0.219.2", + "windows", ] [[package]] @@ -3453,7 +3504,7 @@ dependencies = [ "rustc_abi", "rustc_apfloat", "rustc_ast", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -3467,7 +3518,6 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "rustc_type_ir", "tracing", ] @@ -3480,6 +3530,7 @@ dependencies = [ "either", "elsa", "ena", + "hashbrown", "indexmap", "jobserver", "libc", @@ -3488,7 +3539,7 @@ dependencies = [ "parking_lot", "portable-atomic", "rustc-hash 2.1.1", - "rustc-rayon", + "rustc-rayon-core", "rustc-stable-hash", "rustc_arena", "rustc_graphviz", @@ -3501,7 +3552,7 @@ dependencies = [ "tempfile", "thin-vec", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -3516,6 +3567,7 @@ name = "rustc_driver_impl" version = "0.0.0" dependencies = [ "ctrlc", + "jiff", "libc", "rustc_abi", "rustc_ast", @@ -3555,16 +3607,15 @@ dependencies = [ "rustc_query_system", "rustc_resolve", "rustc_session", - "rustc_smir", "rustc_span", "rustc_target", "rustc_trait_selection", "rustc_ty_utils", "serde_json", "shlex", - "time", + "stable_mir", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -3619,7 +3670,7 @@ dependencies = [ "termcolor", "termize", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -3629,6 +3680,7 @@ dependencies = [ "rustc_ast", "rustc_ast_passes", "rustc_ast_pretty", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -3666,13 +3718,16 @@ dependencies = [ "fluent-syntax", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", "unic-langid", ] [[package]] name = "rustc_fs_util" version = "0.0.0" +dependencies = [ + "tempfile", +] [[package]] name = "rustc_graphviz" @@ -3714,7 +3769,7 @@ dependencies = [ "rustc_abi", "rustc_arena", "rustc_ast", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -3727,9 +3782,7 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", - "rustc_target", "rustc_trait_selection", - "rustc_type_ir", "smallvec", "tracing", ] @@ -3741,7 +3794,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_hir", "rustc_span", ] @@ -3753,6 +3806,7 @@ dependencies = [ "itertools", "rustc_abi", "rustc_ast", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -3767,8 +3821,8 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", + "rustc_target", "rustc_trait_selection", - "rustc_type_ir", "smallvec", "tracing", ] @@ -3777,7 +3831,7 @@ dependencies = [ name = "rustc_incremental" version = "0.0.0" dependencies = [ - "rand 0.8.5", + "rand 0.9.1", "rustc_ast", "rustc_data_structures", "rustc_errors", @@ -3811,7 +3865,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] @@ -3836,7 +3890,6 @@ dependencies = [ name = "rustc_interface" version = "0.0.0" dependencies = [ - "rustc-rayon", "rustc-rayon-core", "rustc_abi", "rustc_ast", @@ -3872,7 +3925,6 @@ dependencies = [ "rustc_query_impl", "rustc_query_system", "rustc_resolve", - "rustc_serialize", "rustc_session", "rustc_span", "rustc_symbol_mangling", @@ -3900,6 +3952,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -3915,7 +3968,6 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "rustc_type_ir", "smallvec", "tracing", "unicode-security", @@ -3948,7 +4000,6 @@ dependencies = [ name = "rustc_log" version = "0.0.0" dependencies = [ - "rustc_span", "tracing", "tracing-core", "tracing-subscriber", @@ -3961,7 +4012,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", "synstructure", ] @@ -3975,6 +4026,7 @@ dependencies = [ "odht", "rustc_abi", "rustc_ast", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -3991,7 +4043,6 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "rustc_type_ir", "tempfile", "tracing", ] @@ -4009,7 +4060,8 @@ dependencies = [ "rustc_apfloat", "rustc_arena", "rustc_ast", - "rustc_attr_parsing", + "rustc_ast_ir", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_error_messages", "rustc_errors", @@ -4037,13 +4089,11 @@ dependencies = [ name = "rustc_mir_build" version = "0.0.0" dependencies = [ - "either", "itertools", "rustc_abi", "rustc_apfloat", "rustc_arena", "rustc_ast", - "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -4090,7 +4140,7 @@ dependencies = [ "rustc_abi", "rustc_arena", "rustc_ast", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_const_eval", "rustc_data_structures", "rustc_errors", @@ -4106,7 +4156,6 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "rustc_type_ir", "smallvec", "tracing", ] @@ -4117,7 +4166,7 @@ version = "0.0.0" dependencies = [ "rustc_abi", "rustc_ast", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -4141,7 +4190,6 @@ dependencies = [ "rustc_data_structures", "rustc_index", "rustc_macros", - "rustc_serialize", "rustc_type_ir", "rustc_type_ir_macros", "tracing", @@ -4152,6 +4200,7 @@ name = "rustc_parse" version = "0.0.0" dependencies = [ "bitflags", + "rustc-literal-escaper", "rustc_ast", "rustc_ast_pretty", "rustc_data_structures", @@ -4174,6 +4223,7 @@ dependencies = [ name = "rustc_parse_format" version = "0.0.0" dependencies = [ + "rustc-literal-escaper", "rustc_index", "rustc_lexer", ] @@ -4186,7 +4236,7 @@ dependencies = [ "rustc_ast", "rustc_ast_lowering", "rustc_ast_pretty", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_expand", @@ -4232,7 +4282,7 @@ name = "rustc_privacy" version = "0.0.0" dependencies = [ "rustc_ast", - "rustc_attr_parsing", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -4250,9 +4300,7 @@ name = "rustc_query_impl" version = "0.0.0" dependencies = [ "measureme", - "rustc_attr_data_structures", "rustc_data_structures", - "rustc_errors", "rustc_hashes", "rustc_hir", "rustc_index", @@ -4261,7 +4309,6 @@ dependencies = [ "rustc_serialize", "rustc_session", "rustc_span", - "thin-vec", "tracing", ] @@ -4269,6 +4316,7 @@ dependencies = [ name = "rustc_query_system" version = "0.0.0" dependencies = [ + "hashbrown", "parking_lot", "rustc-rayon-core", "rustc_abi", @@ -4286,7 +4334,6 @@ dependencies = [ "rustc_session", "rustc_span", "smallvec", - "thin-vec", "tracing", ] @@ -4295,10 +4342,12 @@ name = "rustc_resolve" version = "0.0.0" dependencies = [ "bitflags", - "pulldown-cmark 0.11.3", + "itertools", + "pulldown-cmark", "rustc_arena", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_data_structures", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -4306,7 +4355,6 @@ dependencies = [ "rustc_feature", "rustc_fluent_macro", "rustc_hir", - "rustc_index", "rustc_macros", "rustc_metadata", "rustc_middle", @@ -4324,7 +4372,6 @@ version = "0.0.0" dependencies = [ "bitflags", "rustc_abi", - "rustc_ast", "rustc_data_structures", "rustc_hir", "rustc_middle", @@ -4354,6 +4401,7 @@ dependencies = [ "bitflags", "getopts", "libc", + "rand 0.9.1", "rustc_abi", "rustc_ast", "rustc_data_structures", @@ -4371,7 +4419,7 @@ dependencies = [ "smallvec", "termize", "tracing", - "windows 0.59.0", + "windows", ] [[package]] @@ -4379,7 +4427,6 @@ name = "rustc_smir" version = "0.0.0" dependencies = [ "rustc_abi", - "rustc_ast", "rustc_data_structures", "rustc_hir", "rustc_hir_pretty", @@ -4388,7 +4435,7 @@ dependencies = [ "rustc_span", "rustc_target", "scoped-tls", - "stable_mir", + "serde", "tracing", ] @@ -4421,7 +4468,6 @@ dependencies = [ "punycode", "rustc-demangle", "rustc_abi", - "rustc_ast", "rustc_data_structures", "rustc_errors", "rustc_hashes", @@ -4437,7 +4483,7 @@ name = "rustc_target" version = "0.0.0" dependencies = [ "bitflags", - "object 0.36.7", + "object", "rustc_abi", "rustc_data_structures", "rustc_fs_util", @@ -4450,13 +4496,13 @@ dependencies = [ [[package]] name = "rustc_tools_util" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3316159ab19e19d1065ecc49278e87f767a9dae9fae80348d2b4d4fa4ae02d4d" +version = "0.4.2" [[package]] name = "rustc_tools_util" version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3b75158011a63889ba12084cf1224baad7bcad50f6ee7c842f772b74aa148ed" [[package]] name = "rustc_trait_selection" @@ -4465,7 +4511,6 @@ dependencies = [ "itertools", "rustc_abi", "rustc_ast", - "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -4478,7 +4523,6 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_transmute", - "rustc_type_ir", "smallvec", "thin-vec", "tracing", @@ -4507,6 +4551,7 @@ dependencies = [ "rustc_hir", "rustc_middle", "rustc_span", + "smallvec", "tracing", ] @@ -4529,7 +4574,6 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "rustc_type_ir", "tracing", ] @@ -4539,6 +4583,7 @@ version = "0.0.0" dependencies = [ "bitflags", "derive-where", + "ena", "indexmap", "rustc-hash 1.1.0", "rustc_ast_ir", @@ -4559,7 +4604,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", "synstructure", ] @@ -4577,15 +4622,14 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "arrayvec", + "askama", "base64", "expect-test", "indexmap", "itertools", "minifier", - "pulldown-cmark 0.9.6", "pulldown-cmark-escape", "regex", - "rinja", "rustdoc-json-types", "serde", "serde_json", @@ -4604,6 +4648,7 @@ name = "rustdoc-gui-test" version = "0.1.0" dependencies = [ "build_helper", + "camino", "compiletest", "getopts", "walkdir", @@ -4649,7 +4694,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] @@ -4683,9 +4728,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.43" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", @@ -4696,9 +4741,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ruzstd" @@ -4711,9 +4756,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -4751,51 +4796,50 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" dependencies = [ - "self_cell 1.1.0", + "self_cell 1.2.0", ] [[package]] name = "self_cell" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" +checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" [[package]] name = "semver" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "indexmap", "itoa", "memchr", "ryu", @@ -4824,9 +4868,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -4842,12 +4886,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shell-escape" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" - [[package]] name = "shlex" version = "1.3.0" @@ -4856,15 +4894,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "similar" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" - -[[package]] -name = "siphasher" -version = "0.3.11" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "siphasher" @@ -4883,15 +4915,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -4907,15 +4939,6 @@ dependencies = [ "color-eyre", ] -[[package]] -name = "spdx" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58b69356da67e2fc1f542c71ea7e654a361a79c938e4424392ecf4fa065d2193" -dependencies = [ - "smallvec", -] - [[package]] name = "spdx-expression" version = "0.5.2" @@ -4955,15 +4978,14 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" name = "stable_mir" version = "0.1.0-preview" dependencies = [ - "scoped-tls", - "serde", + "rustc_smir", ] [[package]] name = "stacker" -version = "0.1.18" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d08feb8f695b465baed819b03c128dc23f57a694510ab1f06c77f763975685e" +checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" dependencies = [ "cc", "cfg-if", @@ -4980,26 +5002,25 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "string_cache" -version = "0.8.7" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", - "once_cell", "parking_lot", - "phf_shared 0.10.0", + "phf_shared", "precomputed-hash", "serde", ] [[package]] name = "string_cache_codegen" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", + "phf_generator", + "phf_shared", "proc-macro2", "quote", ] @@ -5050,9 +5071,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -5061,24 +5082,25 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] name = "sysinfo" -version = "0.31.4" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" +checksum = "b897c8ea620e181c7955369a31be5f48d9a9121cb59fd33ecef9ff2a34323422" dependencies = [ - "core-foundation-sys", "libc", - "windows 0.57.0", + "objc2-core-foundation", + "objc2-io-kit", + "windows", ] [[package]] @@ -5093,9 +5115,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", @@ -5104,13 +5126,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.15.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "cfg-if", "fastrand", - "getrandom 0.2.15", + "getrandom 0.3.3", "once_cell", "rustix", "windows-sys 0.59.0", @@ -5163,16 +5184,16 @@ version = "0.1.0" dependencies = [ "indicatif", "num", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand 0.9.1", + "rand_chacha 0.9.0", "rayon", ] [[package]] name = "thin-vec" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" +checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" [[package]] name = "thiserror" @@ -5185,11 +5206,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.11", + "thiserror-impl 2.0.12", ] [[package]] @@ -5200,29 +5221,29 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] name = "thorin-dwp" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "813ba76597db32dc4f6992fd8bf8f394715b88d352fd97401da67dab6283b4c6" +checksum = "9e9c1e705f82a260173f3eec93f2ff6d7807f23ad5a8cc2e7316a891733ea7a1" dependencies = [ - "gimli 0.30.0", - "hashbrown 0.14.5", - "object 0.36.7", + "gimli", + "hashbrown", + "object", "tracing", ] @@ -5250,7 +5271,7 @@ name = "tidy" version = "0.1.0" dependencies = [ "build_helper", - "cargo_metadata 0.19.1", + "cargo_metadata 0.19.2", "fluent-syntax", "ignore", "miropt-test-tools", @@ -5260,6 +5281,7 @@ dependencies = [ "serde", "similar", "termcolor", + "toml 0.7.8", "walkdir", ] @@ -5278,51 +5300,30 @@ dependencies = [ ] [[package]] -name = "time" -version = "0.3.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.19" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "num-conv", - "time-core", + "displaydoc", + "zerovec 0.10.4", ] [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", - "zerovec", + "zerovec 0.11.2", ] [[package]] name = "tinyvec" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -5335,9 +5336,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.43.0" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -5367,9 +5368,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] @@ -5384,7 +5385,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] @@ -5407,7 +5408,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] @@ -5494,9 +5495,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "ucd-parse" @@ -5508,36 +5509,16 @@ dependencies = [ ] [[package]] -name = "ui_test" -version = "0.26.5" +name = "ucd-trie" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ee4c40e5a5f9fa6864ff976473e5d6a6e9884b6ce68b40690d9f87e1994c83" -dependencies = [ - "annotate-snippets 0.11.5", - "anyhow", - "bstr", - "cargo-platform", - "cargo_metadata 0.18.1", - "color-eyre", - "colored", - "comma", - "crossbeam-channel", - "indicatif", - "levenshtein", - "prettydiff", - "regex", - "rustc_version", - "rustfix", - "serde", - "serde_json", - "spanned", -] +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "ui_test" -version = "0.28.0" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7484683d60d50ca1d1b6433c3dbf6c5ad71d20387acdcfb16fe79573f3fba576" +checksum = "1211b1111c752c73b33073d2958072be08825fd97c9ab4d83444da361a06634b" dependencies = [ "annotate-snippets 0.11.5", "anyhow", @@ -5561,9 +5542,9 @@ dependencies = [ [[package]] name = "unic-langid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" +checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05" dependencies = [ "unic-langid-impl", "unic-langid-macros", @@ -5571,34 +5552,34 @@ dependencies = [ [[package]] name = "unic-langid-impl" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" +checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658" dependencies = [ - "tinystr", + "tinystr 0.8.1", ] [[package]] name = "unic-langid-macros" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da1cd2c042d3c7569a1008806b02039e7a4a2bdf8f8e96bd3c792434a0e275e" +checksum = "d5957eb82e346d7add14182a3315a7e298f04e1ba4baac36f7f0dbfedba5fc25" dependencies = [ "proc-macro-hack", - "tinystr", + "tinystr 0.8.1", "unic-langid-impl", "unic-langid-macros-impl", ] [[package]] name = "unic-langid-macros-impl" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b" +checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.96", + "syn 2.0.101", "unic-langid-impl", ] @@ -5610,9 +5591,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-normalization" @@ -5710,12 +5691,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8-width" version = "0.1.7" @@ -5736,11 +5711,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.12.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.3.3", ] [[package]] @@ -5779,18 +5754,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] [[package]] name = "wasi-preview1-component-adapter-provider" -version = "29.0.1" +version = "31.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcd9f21bbde82ba59e415a8725e6ad0d0d7e9e460b1a3ccbca5bdee952c1a324" +checksum = "86fabda09a0d89ffd1615b297b4a5d4b4d99df9598aeb24685837e63019e927b" [[package]] name = "wasm-bindgen" @@ -5814,7 +5789,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -5836,7 +5811,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5852,9 +5827,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "580305a8e3f1b7a79859a8db897de643533b2851c5eb080fe5800233f16dec88" +checksum = "a60a07a994a3538b57d8c5f8caba19f4793fb4c7156276e5e90e90acbb829e20" dependencies = [ "anyhow", "clap", @@ -5862,7 +5837,7 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.223.0", + "wasmparser 0.229.0", "wat", "windows-sys 0.59.0", "winsplit", @@ -5879,46 +5854,51 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.219.1" +version = "0.219.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cbbd772edcb8e7d524a82ee8cef8dd046fc14033796a754c3ad246d019fa54" +checksum = "8aa79bcd666a043b58f5fa62b221b0b914dd901e6f620e8ab7371057a797f3e1" dependencies = [ "leb128", - "wasmparser 0.219.1", + "wasmparser 0.219.2", ] [[package]] name = "wasm-encoder" -version = "0.223.0" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e636076193fa68103e937ac951b5f2f587624097017d764b8984d9c0f149464" +checksum = "38ba1d491ecacb085a2552025c10a675a6fddcbd03b1fc9b36c536010ce265d2" dependencies = [ - "leb128", - "wasmparser 0.223.0", + "leb128fmt", + "wasmparser 0.229.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.230.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4349d0943718e6e434b51b9639e876293093dca4b96384fb136ab5bd5ce6660" +dependencies = [ + "leb128fmt", + "wasmparser 0.230.0", ] [[package]] name = "wasm-metadata" -version = "0.223.0" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c730c3379d3d20e5a0245b0724b924483e853588ca8fba547c1e21f19e7d735" +checksum = "78fdb7d29a79191ab363dc90c1ddd3a1e880ffd5348d92d48482393a9e6c5f4d" dependencies = [ "anyhow", "indexmap", - "serde", - "serde_derive", - "serde_json", - "spdx", - "url", - "wasm-encoder 0.223.0", - "wasmparser 0.223.0", + "wasm-encoder 0.229.0", + "wasmparser 0.229.0", ] [[package]] name = "wasmparser" -version = "0.219.1" +version = "0.219.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c771866898879073c53b565a6c7b49953795159836714ac56a5befb581227c5" +checksum = "5220ee4c6ffcc0cb9d7c47398052203bc902c8ef3985b0c8134118440c0b2921" dependencies = [ "bitflags", "indexmap", @@ -5926,44 +5906,55 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.222.0" +version = "0.222.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4adf50fde1b1a49c1add6a80d47aea500c88db70551805853aa8b88f3ea27ab5" +checksum = "fa210fd1788e6b37a1d1930f3389c48e1d6ebd1a013d34fa4b7f9e3e3bf03146" dependencies = [ "bitflags", ] [[package]] name = "wasmparser" -version = "0.223.0" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5a99faceb1a5a84dd6084ec4bfa4b2ab153b5793b43fd8f58b89232634afc35" +checksum = "0cc3b1f053f5d41aa55640a1fa9b6d1b8a9e4418d118ce308d20e24ff3575a8c" dependencies = [ "bitflags", - "hashbrown 0.15.2", + "hashbrown", "indexmap", "semver", "serde", ] +[[package]] +name = "wasmparser" +version = "0.230.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" +dependencies = [ + "bitflags", + "indexmap", + "semver", +] + [[package]] name = "wast" -version = "223.0.0" +version = "230.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59b2ba8a2ff9f06194b7be9524f92e45e70149f4dacc0d0c7ad92b59ac875e4" +checksum = "b8edac03c5fa691551531533928443faf3dc61a44f814a235c7ec5d17b7b34f1" dependencies = [ "bumpalo", - "leb128", + "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.223.0", + "wasm-encoder 0.230.0", ] [[package]] name = "wat" -version = "1.223.0" +version = "1.230.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662786915c427e4918ff01eabb3c4756d4d947cd8f635761526b4cc9da2eaaad" +checksum = "0d77d62229e38db83eac32bacb5f61ebb952366ab0dae90cf2b3c07a65eea894" dependencies = [ "wast", ] @@ -6011,136 +6002,114 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.57.0" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ - "windows-core 0.57.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" -dependencies = [ - "windows-core 0.59.0", - "windows-targets 0.53.0", + "windows-collections", + "windows-core", + "windows-future", + "windows-link", + "windows-numerics", ] [[package]] name = "windows-bindgen" -version = "0.59.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7fb600834d7e868f6e5bb748a86101427330fafbf9485c331b9d5f562d54a5" +checksum = "ac1c59c20569610dd9ed784d5f003fb493ec57b4cf39d974eb03a84bb7156c90" dependencies = [ "rayon", + "serde", + "serde_json", ] [[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.57.0" +name = "windows-collections" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets 0.52.6", + "windows-core", ] [[package]] name = "windows-core" -version = "0.59.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.59.0", - "windows-interface 0.59.0", - "windows-result 0.3.0", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", "windows-strings", - "windows-targets 0.53.0", ] [[package]] -name = "windows-implement" -version = "0.57.0" +name = "windows-future" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.96", + "windows-core", + "windows-link", ] [[package]] name = "windows-implement" -version = "0.59.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] name = "windows-interface" -version = "0.57.0" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] -name = "windows-interface" -version = "0.59.0" +name = "windows-link" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.96", -] +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] -name = "windows-result" -version = "0.1.2" +name = "windows-numerics" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-targets 0.52.6", + "windows-core", + "windows-link", ] [[package]] name = "windows-result" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d08106ce80268c4067c0571ca55a9b4e9516518eaa1a1fe9b37ca403ae1d1a34" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ - "windows-targets 0.53.0", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b888f919960b42ea4e11c2f408fadb55f78a9f236d5eef084103c8ce52893491" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" dependencies = [ - "windows-targets 0.53.0", + "windows-link", ] [[package]] @@ -6364,6 +6333,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +dependencies = [ + "memchr", +] + [[package]] name = "winsplit" version = "0.1.0" @@ -6372,18 +6350,18 @@ checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956" [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] [[package]] name = "wit-component" -version = "0.223.0" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c10ed2aeee4c8ec5715875f62f4a3de3608d6987165c116810d8c2908aa9d93b" +checksum = "7f550067740e223bfe6c4878998e81cdbe2529dd9a793dc49248dd6613394e8b" dependencies = [ "anyhow", "bitflags", @@ -6392,17 +6370,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.223.0", + "wasm-encoder 0.229.0", "wasm-metadata", - "wasmparser 0.223.0", + "wasmparser 0.229.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.223.0" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92772f4dcacb804b275981eea1d920b12b377993b53307f1e33d87404e080281" +checksum = "459c6ba62bf511d6b5f2a845a2a736822e38059c1cfa0b644b467bbbfae4efa6" dependencies = [ "anyhow", "id-arena", @@ -6413,29 +6391,32 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.223.0", + "wasmparser 0.229.0", ] [[package]] -name = "write16" -version = "1.0.0" +name = "writeable" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "x" +version = "0.1.1" [[package]] name = "xattr" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" +checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" dependencies = [ "libc", - "linux-raw-sys", "rustix", ] @@ -6465,7 +6446,19 @@ checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", - "yoke-derive", + "yoke-derive 0.7.5", + "zerofrom", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive 0.8.0", "zerofrom", ] @@ -6477,81 +6470,94 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", "synstructure", ] [[package]] -name = "zerocopy" -version = "0.7.35" +name = "yoke-derive" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ - "byteorder", - "zerocopy-derive 0.7.35", + "proc-macro2", + "quote", + "syn 2.0.101", + "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.14" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive 0.8.14", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.96", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.14" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", ] [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", "synstructure", ] +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke 0.8.0", + "zerofrom", +] + [[package]] name = "zerovec" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ - "yoke", + "yoke 0.7.5", "zerofrom", - "zerovec-derive", + "zerovec-derive 0.10.3", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke 0.8.0", + "zerofrom", + "zerovec-derive 0.11.1", ] [[package]] @@ -6562,5 +6568,16 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.101", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] diff --git a/Cargo.toml b/Cargo.toml index 20a43aaaeeb37..16ff0f61593ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,51 +1,53 @@ [workspace] resolver = "2" members = [ +# tidy-alphabetical-start "compiler/rustc", "src/build_helper", "src/etc/test-float-parse", - "src/rustc-std-workspace/rustc-std-workspace-core", "src/rustc-std-workspace/rustc-std-workspace-alloc", + "src/rustc-std-workspace/rustc-std-workspace-core", "src/rustc-std-workspace/rustc-std-workspace-std", "src/rustdoc-json-types", + "src/tools/build-manifest", + "src/tools/bump-stage0", "src/tools/cargotest", "src/tools/clippy", "src/tools/clippy/clippy_dev", + "src/tools/collect-license-metadata", "src/tools/compiletest", - "src/tools/run-make-support", + "src/tools/coverage-dump", + "src/tools/features-status-dump", + "src/tools/generate-copyright", + "src/tools/generate-windows-sys", + "src/tools/html-checker", + "src/tools/jsondocck", + "src/tools/jsondoclint", "src/tools/linkchecker", "src/tools/lint-docs", + "src/tools/lld-wrapper", + "src/tools/llvm-bitcode-linker", + "src/tools/miri", + "src/tools/miri/cargo-miri", "src/tools/miropt-test-tools", - "src/tools/unstable-book-gen", - "src/tools/tidy", - "src/tools/tier-check", - "src/tools/build-manifest", + "src/tools/opt-dist", "src/tools/remote-test-client", "src/tools/remote-test-server", + "src/tools/replace-version-placeholder", + "src/tools/run-make-support", "src/tools/rust-installer", "src/tools/rustdoc", - "src/tools/rls", - "src/tools/rustfmt", - "src/tools/miri", - "src/tools/miri/cargo-miri", + "src/tools/rustdoc-gui-test", "src/tools/rustdoc-themes", - "src/tools/unicode-table-generator", - "src/tools/jsondocck", - "src/tools/jsondoclint", - "src/tools/llvm-bitcode-linker", - "src/tools/html-checker", - "src/tools/bump-stage0", - "src/tools/replace-version-placeholder", - "src/tools/lld-wrapper", - "src/tools/collect-license-metadata", - "src/tools/generate-copyright", + "src/tools/rustfmt", "src/tools/suggest-tests", - "src/tools/generate-windows-sys", - "src/tools/rustdoc-gui-test", - "src/tools/opt-dist", - "src/tools/coverage-dump", + "src/tools/tidy", + "src/tools/tier-check", + "src/tools/unicode-table-generator", + "src/tools/unstable-book-gen", "src/tools/wasm-component-ld", - "src/tools/features-status-dump", + "src/tools/x", +# tidy-alphabetical-end ] exclude = [ @@ -56,11 +58,6 @@ exclude = [ "tests/rustdoc-gui", # HACK(eddyb) This hardcodes the fact that our CI uses `/checkout/obj`. "obj", - # The `x` binary is a thin wrapper that calls `x.py`, which initializes - # submodules, before which workspace members cannot be invoked because - # not all `Cargo.toml` files are available, so we exclude the `x` binary, - # so it can be invoked before the current checkout is set up. - "src/tools/x", ] [profile.release.package.rustc-rayon-core] diff --git a/INSTALL.md b/INSTALL.md index 74fcc58348b43..98eb825cd10f6 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -6,9 +6,9 @@ If you just want to install Rust, check out the [README.md](README.md) instead.* The Rust build system uses a Python script called `x.py` to build the compiler, which manages the bootstrapping process. It lives at the root of the project. -It also uses a file named `config.toml` to determine various configuration +It also uses a file named `bootstrap.toml` to determine various configuration settings for the build. You can see a full list of options in -`config.example.toml`. +`bootstrap.example.toml`. The `x.py` command can be run directly on most Unix systems in the following format: @@ -75,8 +75,31 @@ See [the rustc-dev-guide for more info][sysllvm]. 2. Configure the build settings: + If you're unsure which build configurations to use and need a good default, you + can run the interactive `x.py setup` command. This will guide you through selecting + a config profile, setting up the LSP, configuring a Git hook, etc. + + With `configure` script, you can handle multiple configurations in a single + command which is useful to create complex/advanced config files. For example: + ```sh - ./configure + ./configure --build=aarch64-unknown-linux-gnu \ + --enable-full-tools \ + --enable-profiler \ + --enable-sanitizers \ + --enable-compiler-docs \ + --set target.aarch64-unknown-linux-gnu.linker=clang \ + --set target.aarch64-unknown-linux-gnu.ar=/rustroot/bin/llvm-ar \ + --set target.aarch64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \ + --set llvm.link-shared=true \ + --set llvm.thin-lto=true \ + --set llvm.libzstd=true \ + --set llvm.ninja=false \ + --set rust.debug-assertions=false \ + --set rust.jemalloc \ + --set rust.use-lld=true \ + --set rust.lto=thin \ + --set rust.codegen-units=1 ``` If you plan to use `x.py install` to create an installation, you can either @@ -115,7 +138,7 @@ See [the rustc-dev-guide for more info][sysllvm]. This project provides a configure script and makefile (the latter of which just invokes `x.py`). `./configure` is the recommended way to programmatically -generate a `config.toml`. `make` is not recommended (we suggest using `x.py` +generate a `bootstrap.toml`. `make` is not recommended (we suggest using `x.py` directly), but it is supported and we try not to break it unnecessarily. ```sh @@ -123,7 +146,7 @@ directly), but it is supported and we try not to break it unnecessarily. make && sudo make install ``` -`configure` generates a `config.toml` which can also be used with normal `x.py` +`configure` generates a `bootstrap.toml` which can also be used with normal `x.py` invocations. ## Building on Windows @@ -210,9 +233,13 @@ itself back on after some time). ### MSVC -MSVC builds of Rust additionally require an installation of Visual Studio 2017 -(or later) so `rustc` can use its linker. The simplest way is to get -[Visual Studio], check the "C++ build tools" and "Windows 10 SDK" workload. +MSVC builds of Rust additionally requires an installation of: + +- Visual Studio 2022 (or later) build tools so `rustc` can use its linker. Older + Visual Studio versions such as 2019 *may* work but aren't actively tested. +- A recent Windows 10 or 11 SDK. + +The simplest way is to get [Visual Studio], check the "C++ build tools". [Visual Studio]: https://visualstudio.microsoft.com/downloads/ @@ -251,7 +278,7 @@ Windows build triples are: - `x86_64-pc-windows-msvc` The build triple can be specified by either specifying `--build=` when -invoking `x.py` commands, or by creating a `config.toml` file (as described in +invoking `x.py` commands, or by creating a `bootstrap.toml` file (as described in [Building on a Unix-like system](#building-on-a-unix-like-system)), and passing `--set build.build=` to `./configure`. diff --git a/README.md b/README.md index d84d96a0e9173..611260470f12b 100644 --- a/README.md +++ b/README.md @@ -67,11 +67,11 @@ See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT), and trademarks and logos (the "Rust Trademarks"). If you want to use these names or brands, please read the -[media guide][media-guide]. +[Rust language trademark policy][trademark-policy]. Third-party logos may be subject to third-party copyrights and trademarks. See [Licenses][policies-licenses] for details. -[rust-foundation]: https://foundation.rust-lang.org/ -[media-guide]: https://foundation.rust-lang.org/policies/logo-policy-and-media-guide/ +[rust-foundation]: https://rustfoundation.org/ +[trademark-policy]: https://rustfoundation.org/policy/rust-trademark-policy/ [policies-licenses]: https://www.rust-lang.org/policies/licenses diff --git a/RELEASES.md b/RELEASES.md index 038d7ca639f20..3c72cb1de0a3f 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,303 @@ +Version 1.87.0 (2025-05-15) +========================== + + + +Language +-------- +- [Stabilize `asm_goto` feature](https://github.com/rust-lang/rust/pull/133870) +- [Allow parsing open beginning ranges (`..EXPR`) after unary operators `!`, `-`, and `*`](https://github.com/rust-lang/rust/pull/134900). +- [Don't require method impls for methods with `Self: Sized` bounds in `impl`s for unsized types](https://github.com/rust-lang/rust/pull/135480) +- [Stabilize `feature(precise_capturing_in_traits)` allowing `use<...>` bounds on return position `impl Trait` in `trait`s](https://github.com/rust-lang/rust/pull/138128) + + + +Compiler +-------- +- [x86: make SSE2 required for i686 targets and use it to pass SIMD types](https://github.com/rust-lang/rust/pull/135408) + + + +Platform Support +---------------- +- [Remove `i586-pc-windows-msvc` target](https://github.com/rust-lang/rust/pull/137957) + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + +[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html + + + +Libraries +--------- +- [Stabilize the anonymous pipe API](https://github.com/rust-lang/rust/issues/127154) +- [Add support for unbounded left/right shift operations](https://github.com/rust-lang/rust/issues/129375) +- [Print pointer metadata in `Debug` impl of raw pointers](https://github.com/rust-lang/rust/pull/135080) +- [`Vec::with_capacity` guarantees it allocates with the amount requested, even if `Vec::capacity` returns a different number.](https://github.com/rust-lang/rust/pull/135933) +- Most `std::arch` intrinsics which don't take pointer arguments can now be called from safe code if the caller has the appropriate target features already enabled (https://github.com/rust-lang/stdarch/pull/1714, https://github.com/rust-lang/stdarch/pull/1716, https://github.com/rust-lang/stdarch/pull/1717) +- [Undeprecate `env::home_dir`](https://github.com/rust-lang/rust/pull/137327) +- [Denote `ControlFlow` as `#[must_use]`](https://github.com/rust-lang/rust/pull/137449) +- [Macros such as `assert_eq!` and `vec!` now support `const {...}` expressions](https://github.com/rust-lang/rust/pull/138162) + + + +Stabilized APIs +--------------- + +- [`Vec::extract_if`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.extract_if) +- [`vec::ExtractIf`](https://doc.rust-lang.org/stable/std/vec/struct.ExtractIf.html) +- [`LinkedList::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.LinkedList.html#method.extract_if) +- [`linked_list::ExtractIf`](https://doc.rust-lang.org/stable/std/collections/linked_list/struct.ExtractIf.html) +- [`<[T]>::split_off`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_off) +- [`<[T]>::split_off_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_off_mut) +- [`<[T]>::split_off_first`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_off_first) +- [`<[T]>::split_off_first_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_off_first_mut) +- [`<[T]>::split_off_last`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_off_last) +- [`<[T]>::split_off_last_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_off_last_mut) +- [`String::extend_from_within`](https://doc.rust-lang.org/stable/alloc/string/struct.String.html#method.extend_from_within) +- [`os_str::Display`](https://doc.rust-lang.org/stable/std/ffi/os_str/struct.Display.html) +- [`OsString::display`](https://doc.rust-lang.org/stable/std/ffi/struct.OsString.html#method.display) +- [`OsStr::display`](https://doc.rust-lang.org/stable/std/ffi/struct.OsStr.html#method.display) +- [`io::pipe`](https://doc.rust-lang.org/stable/std/io/fn.pipe.html) +- [`io::PipeReader`](https://doc.rust-lang.org/stable/std/io/struct.PipeReader.html) +- [`io::PipeWriter`](https://doc.rust-lang.org/stable/std/io/struct.PipeWriter.html) +- [`impl From for OwnedHandle`](https://doc.rust-lang.org/stable/std/os/windows/io/struct.OwnedHandle.html#impl-From%3CPipeReader%3E-for-OwnedHandle) +- [`impl From for OwnedHandle`](https://doc.rust-lang.org/stable/std/os/windows/io/struct.OwnedHandle.html#impl-From%3CPipeWriter%3E-for-OwnedHandle) +- [`impl From for Stdio`](https://doc.rust-lang.org/stable/std/process/struct.Stdio.html) +- [`impl From for Stdio`](https://doc.rust-lang.org/stable/std/process/struct.Stdio.html#impl-From%3CPipeWriter%3E-for-Stdio) +- [`impl From for OwnedFd`](https://doc.rust-lang.org/stable/std/os/fd/struct.OwnedFd.html#impl-From%3CPipeReader%3E-for-OwnedFd) +- [`impl From for OwnedFd`](https://doc.rust-lang.org/stable/std/os/fd/struct.OwnedFd.html#impl-From%3CPipeWriter%3E-for-OwnedFd) +- [`Box>::write`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.write) +- [`impl TryFrom> for String`](https://doc.rust-lang.org/stable/std/string/struct.String.html#impl-TryFrom%3CVec%3Cu8%3E%3E-for-String) +- [`<*const T>::offset_from_unsigned`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset_from_unsigned) +- [`<*const T>::byte_offset_from_unsigned`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.byte_offset_from_unsigned) +- [`<*mut T>::offset_from_unsigned`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset_from_unsigned-1) +- [`<*mut T>::byte_offset_from_unsigned`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.byte_offset_from_unsigned-1) +- [`NonNull::offset_from_unsigned`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.offset_from_unsigned) +- [`NonNull::byte_offset_from_unsigned`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.byte_offset_from_unsigned) +- [`::cast_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.cast_signed) +- [`NonZero::::cast_signed`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.cast_signed-5). +- [`::cast_unsigned`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.cast_unsigned). +- [`NonZero::::cast_unsigned`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.cast_unsigned-5). +- [`::is_multiple_of`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.is_multiple_of) +- [`::unbounded_shl`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unbounded_shl) +- [`::unbounded_shr`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unbounded_shr) +- [`::unbounded_shl`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unbounded_shl) +- [`::unbounded_shr`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unbounded_shr) +- [`::midpoint`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.midpoint) +- [`::from_utf8`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.from_utf8) +- [`::from_utf8_mut`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.from_utf8_mut) +- [`::from_utf8_unchecked`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.from_utf8_unchecked) +- [`::from_utf8_unchecked_mut`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.from_utf8_unchecked_mut) + +These previously stable APIs are now stable in const contexts: + +- [`core::str::from_utf8_mut`](https://doc.rust-lang.org/stable/std/str/fn.from_utf8_mut.html) +- [`<[T]>::copy_from_slice`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.copy_from_slice) +- [`SocketAddr::set_ip`](https://doc.rust-lang.org/stable/std/net/enum.SocketAddr.html#method.set_ip) +- [`SocketAddr::set_port`](https://doc.rust-lang.org/stable/std/net/enum.SocketAddr.html#method.set_port), +- [`SocketAddrV4::set_ip`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV4.html#method.set_ip) +- [`SocketAddrV4::set_port`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV4.html#method.set_port), +- [`SocketAddrV6::set_ip`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV6.html#method.set_ip) +- [`SocketAddrV6::set_port`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV6.html#method.set_port) +- [`SocketAddrV6::set_flowinfo`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV6.html#method.set_flowinfo) +- [`SocketAddrV6::set_scope_id`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV6.html#method.set_scope_id) +- [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) +- [`char::is_whitespace`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_whitespace) +- [`<[[T; N]]>::as_flattened`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_flattened) +- [`<[[T; N]]>::as_flattened_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_flattened_mut) +- [`String::into_bytes`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.into_bytes) +- [`String::as_str`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.as_str) +- [`String::capacity`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.capacity) +- [`String::as_bytes`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.as_bytes) +- [`String::len`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.len) +- [`String::is_empty`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.is_empty) +- [`String::as_mut_str`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.as_mut_str) +- [`String::as_mut_vec`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.as_mut_vec) +- [`Vec::as_ptr`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.as_ptr) +- [`Vec::as_slice`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.as_slice) +- [`Vec::capacity`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.capacity) +- [`Vec::len`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.len) +- [`Vec::is_empty`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.is_empty) +- [`Vec::as_mut_slice`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.as_mut_slice) +- [`Vec::as_mut_ptr`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.as_mut_ptr) + + + +Cargo +----- +- [Add terminal integration via ANSI OSC 9;4 sequences](https://github.com/rust-lang/cargo/pull/14615/) +- [chore: bump openssl to v3](https://github.com/rust-lang/cargo/pull/15232/) +- [feat(package): add --exclude-lockfile flag](https://github.com/rust-lang/cargo/pull/15234/) + + + +Compatibility Notes +------------------- +- [Rust now raises an error for macro invocations inside the `#![crate_name]` attribute](https://github.com/rust-lang/rust/pull/127581) +- [Unstable fields are now always considered to be inhabited](https://github.com/rust-lang/rust/pull/133889) +- [Macro arguments of unary operators followed by open beginning ranges may now be matched differently](https://github.com/rust-lang/rust/pull/134900) +- [Make `Debug` impl of raw pointers print metadata if present](https://github.com/rust-lang/rust/pull/135080) +- [Warn against function pointers using unsupported ABI strings in dependencies](https://github.com/rust-lang/rust/pull/135767) +- [Associated types on `dyn` types are no longer deduplicated](https://github.com/rust-lang/rust/pull/136458) +- [Forbid attributes on `..` inside of struct patterns (`let Struct { #[attribute] .. }) =`](https://github.com/rust-lang/rust/pull/136490) +- [Make `ptr_cast_add_auto_to_object` lint into hard error](https://github.com/rust-lang/rust/pull/136764) +- Many `std::arch` intrinsics are now safe to call in some contexts, there may now be new `unused_unsafe` warnings in existing codebases. +- [Limit `width` and `precision` formatting options to 16 bits on all targets](https://github.com/rust-lang/rust/pull/136932) +- [Turn order dependent trait objects future incompat warning into a hard error](https://github.com/rust-lang/rust/pull/136968) +- [Denote `ControlFlow` as `#[must_use]`](https://github.com/rust-lang/rust/pull/137449) +- [Windows: The standard library no longer links `advapi32`, except on win7.](https://github.com/rust-lang/rust/pull/138233) Code such as C libraries that were relying on this assumption may need to explicitly link advapi32. +- [Proc macros can no longer observe expanded `cfg(true)` attributes.](https://github.com/rust-lang/rust/pull/138844) +- [Start changing the internal representation of pasted tokens](https://github.com/rust-lang/rust/pull/124141). Certain invalid declarative macros that were previously accepted in obscure circumstances are now correctly rejected by the compiler. Use of a `tt` fragment specifier can often fix these macros. +- [Don't allow flattened format_args in const.](https://github.com/rust-lang/rust/pull/139624) + + + +Internal Changes +---------------- + +These changes do not affect any public interfaces of Rust, but they represent +significant improvements to the performance or internals of rustc and related +tools. + +- [Update to LLVM 20](https://github.com/rust-lang/rust/pull/135763) + + +Version 1.86.0 (2025-04-03) +========================== + + + +Language +-------- +- [Stabilize upcasting trait objects to supertraits.](https://github.com/rust-lang/rust/pull/134367) +- [Allow safe functions to be marked with the `#[target_feature]` attribute.](https://github.com/rust-lang/rust/pull/134090) +- [The `missing_abi` lint now warns-by-default.](https://github.com/rust-lang/rust/pull/132397) +- Rust now lints about double negations, to catch cases that might have intended to be a prefix decrement operator (`--x`) as written in other languages. This was previously a clippy lint, `clippy::double_neg`, and is [now available directly in Rust as `double_negations`.](https://github.com/rust-lang/rust/pull/126604) +- [More pointers are now detected as definitely not-null based on their alignment in const eval.](https://github.com/rust-lang/rust/pull/133700) +- [Empty `repr()` attribute applied to invalid items are now correctly rejected.](https://github.com/rust-lang/rust/pull/133925) +- [Inner attributes `#![test]` and `#![rustfmt::skip]` are no longer accepted in more places than intended.](https://github.com/rust-lang/rust/pull/134276) + + + +Compiler +-------- +- [Debug-assert that raw pointers are non-null on access.](https://github.com/rust-lang/rust/pull/134424) +- [Change `-O` to mean `-C opt-level=3` instead of `-C opt-level=2` to match Cargo's defaults.](https://github.com/rust-lang/rust/pull/135439) +- [Fix emission of `overflowing_literals` under certain macro environments.](https://github.com/rust-lang/rust/pull/136393) + + + +Platform Support +---------------- +- [Replace `i686-unknown-redox` target with `i586-unknown-redox`.](https://github.com/rust-lang/rust/pull/136698) +- [Increase baseline CPU of `i686-unknown-hurd-gnu` to Pentium 4.](https://github.com/rust-lang/rust/pull/136700) +- New tier 3 targets: + - [`{aarch64-unknown,x86_64-pc}-nto-qnx710_iosock`](https://github.com/rust-lang/rust/pull/133631). + For supporting Neutrino QNX 7.1 with `io-socket` network stack. + - [`{aarch64-unknown,x86_64-pc}-nto-qnx800`](https://github.com/rust-lang/rust/pull/133631). + For supporting Neutrino QNX 8.0 (`no_std`-only). + - [`{x86_64,i686}-win7-windows-gnu`](https://github.com/rust-lang/rust/pull/134609). + Intended for backwards compatibility with Windows 7. `{x86_64,i686}-win7-windows-msvc` are the Windows MSVC counterparts that already exist as Tier 3 targets. + - [`amdgcn-amd-amdhsa`](https://github.com/rust-lang/rust/pull/134740). + - [`x86_64-pc-cygwin`](https://github.com/rust-lang/rust/pull/134999). + - [`{mips,mipsel}-mti-none-elf`](https://github.com/rust-lang/rust/pull/135074). + Initial bare-metal support. + - [`m68k-unknown-none-elf`](https://github.com/rust-lang/rust/pull/135085). + - [`armv7a-nuttx-{eabi,eabihf}`, `aarch64-unknown-nuttx`, and `thumbv7a-nuttx-{eabi,eabihf}`](https://github.com/rust-lang/rust/pull/135757). + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + + + +Libraries +--------- +- The type of `FromBytesWithNulError` in `CStr::from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError>` was [changed from an opaque struct to an enum](https://github.com/rust-lang/rust/pull/134143), allowing users to examine why the conversion failed. +- [Remove `RustcDecodable` and `RustcEncodable`.](https://github.com/rust-lang/rust/pull/134272) +- [Deprecate libtest's `--logfile` option.](https://github.com/rust-lang/rust/pull/134283) +- [On recent versions of Windows, `std::fs::remove_file` will now remove read-only files.](https://github.com/rust-lang/rust/pull/134679) + + + +Stabilized APIs +--------------- + +- [`{float}::next_down`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.next_down) +- [`{float}::next_up`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.next_up) +- [`<[_]>::get_disjoint_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.get_disjoint_mut) +- [`<[_]>::get_disjoint_unchecked_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.get_disjoint_unchecked_mut) +- [`slice::GetDisjointMutError`](https://doc.rust-lang.org/stable/std/slice/enum.GetDisjointMutError.html) +- [`HashMap::get_disjoint_mut`](https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.get_disjoint_mut) +- [`HashMap::get_disjoint_unchecked_mut`](https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.get_disjoint_unchecked_mut) +- [`NonZero::count_ones`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.count_ones) +- [`Vec::pop_if`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.pop_if) +- [`sync::Once::wait`](https://doc.rust-lang.org/stable/std/sync/struct.Once.html#method.wait) +- [`sync::Once::wait_force`](https://doc.rust-lang.org/stable/std/sync/struct.Once.html#method.wait_force) +- [`sync::OnceLock::wait`](https://doc.rust-lang.org/stable/std/sync/struct.OnceLock.html#method.wait) + +These APIs are now stable in const contexts: + +- [`hint::black_box`](https://doc.rust-lang.org/stable/std/hint/fn.black_box.html) +- [`io::Cursor::get_mut`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html#method.get_mut) +- [`io::Cursor::set_position`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html#method.set_position) +- [`str::is_char_boundary`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.is_char_boundary) +- [`str::split_at`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.split_at) +- [`str::split_at_checked`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.split_at_checked) +- [`str::split_at_mut`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.split_at_mut) +- [`str::split_at_mut_checked`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.split_at_mut_checked) + + + +Cargo +----- +- [When merging, replace rather than combine configuration keys that refer to a program path and its arguments.](https://github.com/rust-lang/cargo/pull/15066/) +- [Error if both `--package` and `--workspace` are passed but the requested package is missing.](https://github.com/rust-lang/cargo/pull/15071/) This was previously silently ignored, which was considered a bug since missing packages should be reported. +- [Deprecate the token argument in `cargo login` to avoid shell history leaks.](https://github.com/rust-lang/cargo/pull/15057/) +- [Simplify the implementation of `SourceID` comparisons.](https://github.com/rust-lang/cargo/pull/14980/) This may potentially change behavior if the canonicalized URL compares differently in alternative registries. + + + +Rustdoc +----- +- [Add a sans-serif font setting.](https://github.com/rust-lang/rust/pull/133636) + + + +Compatibility Notes +------------------- +- [The `wasm_c_abi` future compatibility warning is now a hard error.](https://github.com/rust-lang/rust/pull/133951) + Users of `wasm-bindgen` should upgrade to at least version 0.2.89, otherwise compilation will fail. +- [Remove long-deprecated no-op attributes `#![no_start]` and `#![crate_id]`.](https://github.com/rust-lang/rust/pull/134300) +- [The future incompatibility lint `cenum_impl_drop_cast` has been made into a hard error.](https://github.com/rust-lang/rust/pull/135964) This means it is now an error to cast a field-less enum to an integer if the enum implements `Drop`. +- [SSE2 is now required for "i686" 32-bit x86 hard-float targets; disabling it causes a warning that will become a hard error eventually.](https://github.com/rust-lang/rust/pull/137037) + To compile for pre-SSE2 32-bit x86, use a "i586" target instead. + + + +Internal Changes +---------------- + +These changes do not affect any public interfaces of Rust, but they represent +significant improvements to the performance or internals of rustc and related +tools. + +- [Build the rustc on AArch64 Linux with ThinLTO + PGO.](https://github.com/rust-lang/rust/pull/133807) + The ARM 64-bit compiler (AArch64) on Linux is now optimized with ThinLTO and PGO, similar to the optimizations we have already performed for the x86-64 compiler on Linux. This should make it up to 30% faster. + + +Version 1.85.1 (2025-03-18) +========================== + + + +- [Fix the doctest-merging feature of the 2024 Edition.](https://github.com/rust-lang/rust/pull/137899/) +- [Relax some `target_feature` checks when generating docs.](https://github.com/rust-lang/rust/pull/137632/) +- [Fix errors in `std::fs::rename` on Windows 10, version 1607.](https://github.com/rust-lang/rust/pull/137528/) +- [Downgrade bootstrap `cc` to fix custom targets.](https://github.com/rust-lang/rust/pull/137460/) +- [Skip submodule updates when building Rust from a source tarball.](https://github.com/rust-lang/rust/pull/137338/) + Version 1.85.0 (2025-02-20) ========================== @@ -2282,7 +2582,7 @@ Compatibility Notes - [Cargo denies `CARGO_HOME` in the `[env]` configuration table. Cargo itself doesn't pick up this value, but recursive calls to cargo would, which was not intended.](https://github.com/rust-lang/cargo/pull/11644/) - [Debuginfo for build dependencies is now off if not explicitly set. This is expected to improve the overall build time.](https://github.com/rust-lang/cargo/pull/11252/) - [The Rust distribution no longer always includes rustdoc](https://github.com/rust-lang/rust/pull/106886) - If `tools = [...]` is set in config.toml, we will respect a missing rustdoc in that list. By + If `tools = [...]` is set in bootstrap.toml, we will respect a missing rustdoc in that list. By default rustdoc remains included. To retain the prior behavior explicitly add `"rustdoc"` to the list. @@ -5268,7 +5568,7 @@ related tools. - [Building `rustc` from source now uses `ninja` by default over `make`.][74922] You can continue building with `make` by setting `ninja=false` in - your `config.toml`. + your `bootstrap.toml`. - [cg_llvm: `fewer_names` in `uncached_llvm_type`][76030] - [Made `ensure_sufficient_stack()` non-generic][76680] diff --git a/REUSE.toml b/REUSE.toml index 9e873e94eff25..816c6d730c824 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -22,7 +22,7 @@ path = [ "Cargo.lock", "Cargo.toml", "CODE_OF_CONDUCT.md", - "config.example.toml", + "bootstrap.example.toml", "configure", "CONTRIBUTING.md", "COPYRIGHT", diff --git a/bootstrap.example.toml b/bootstrap.example.toml new file mode 100644 index 0000000000000..1371fd6442f96 --- /dev/null +++ b/bootstrap.example.toml @@ -0,0 +1,1016 @@ +# Sample TOML configuration file for building Rust. +# +# To configure bootstrap, run `./configure` or `./x.py setup`. +# See https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html#create-a-bootstraptoml for more information. +# +# All options are commented out by default in this file, and they're commented +# out with their default values. The build system by default looks for +# `bootstrap.toml` in the current directory of a build for build configuration, but +# a custom configuration file can also be specified with `--config` to the build +# system. + +# ============================================================================= +# Global Settings +# ============================================================================= + +# Use different pre-set defaults than the global defaults. +# +# See `src/bootstrap/defaults` for more information. +# Note that this has no default value (x.py uses the defaults in `bootstrap.example.toml`). +#profile = + +# Inherits configuration values from different configuration files (a.k.a. config extensions). +# Supports absolute paths, and uses the current directory (where the bootstrap was invoked) +# as the base if the given path is not absolute. +# +# The overriding logic follows a right-to-left order. For example, in `include = ["a.toml", "b.toml"]`, +# extension `b.toml` overrides `a.toml`. Also, parent extensions always overrides the inner ones. +#include = [] + +# Keeps track of major changes made to this configuration. +# +# This value also represents ID of the PR that caused major changes. Meaning, +# you can visit github.com/rust-lang/rust/pull/{change-id} to check for more details. +# +# A 'major change' includes any of the following +# - A new option +# - A change in the default values +# +# If the change-id does not match the version currently in use, x.py will +# display the changes made to the bootstrap. +# To suppress these warnings, you can set change-id = "ignore". +#change-id = + +# ============================================================================= +# Tweaking how LLVM is compiled +# ============================================================================= +[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 leave this enabled. +# +# Set this to `true` to download if CI llvm available otherwise it builds +# from `src/llvm-project`. +# +# Set this to `"if-unchanged"` to download only if the llvm-project has not +# been modified. You can also use this if you are unsure whether you're on a +# tier 1 target. All tier 1 targets are currently supported. + +# Currently, we 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 = true + +# Indicates whether the LLVM build is a Release or Debug build +#optimize = true + +# Indicates whether LLVM should be built with ThinLTO. Note that this will +# only succeed if you use clang, lld, llvm-ar, and llvm-ranlib in your C/C++ +# toolchain (see the `cc`, `cxx`, `linker`, `ar`, and `ranlib` options below). +# More info at: https://clang.llvm.org/docs/ThinLTO.html#clang-bootstrap +#thin-lto = false + +# Indicates whether an LLVM Release build should include debug info +#release-debuginfo = false + +# Indicates whether the LLVM assertions are enabled or not +# NOTE: When assertions are disabled, bugs in the integration between rustc and LLVM can lead to +# unsoundness (segfaults, etc.) in the rustc process itself, not just in the generated code. +#assertions = false + +# Indicates whether the LLVM testsuite is enabled in the build or not. Does +# not execute the tests as part of the build as part of x.py build et al, +# just makes it possible to do `ninja check-llvm` in the staged LLVM build +# directory when doing LLVM development as part of Rust development. +#tests = false + +# Indicates whether the LLVM plugin is enabled or not +#plugins = false + +# Whether to build Enzyme as AutoDiff backend. +#enzyme = false + +# Whether to build LLVM with support for it's gpu offload runtime. +#offload = false + +# When true, link libstdc++ statically into the rustc_llvm. +# This is useful if you don't want to use the dynamic version of that +# library provided by LLVM. +#static-libstdcpp = false + +# Enable LLVM to use zstd for compression. +#libzstd = 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 +# dependent on LLVM for code generation, turning targets off here WILL lead to +# the resulting rustc being unable to compile for the disabled architectures. +# +# To add support for new targets, see https://rustc-dev-guide.rust-lang.org/building/new-target.html. +#targets = "AArch64;AMDGPU;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;Sparc;SystemZ;WebAssembly;X86" + +# LLVM experimental targets to build support for. These targets are specified in +# the same format as above, but since these targets are experimental, they are +# not built by default and the experimental Rust compilation targets that depend +# on them will not work unless the user opts in to building them. +#experimental-targets = "AVR;M68k;CSKY" + +# Cap the number of parallel linker invocations when compiling LLVM. +# This can be useful when building LLVM with debug info, which significantly +# increases the size of binaries and consequently the memory required by +# each linker process. +# If set to 0, linker invocations are treated like any other job and +# controlled by bootstrap's -j parameter. +#link-jobs = 0 + +# Whether to build LLVM as a dynamically linked library (as opposed to statically linked). +# Under the hood, this passes `--shared` to llvm-config. +# NOTE: To avoid performing LTO multiple times, we suggest setting this to `true` when `thin-lto` is enabled. +#link-shared = llvm.thin-lto + +# When building llvm, this configures what is being appended to the version. +# To use LLVM version as is, provide an empty string. +#version-suffix = if rust.channel == "dev" { "-rust-dev" } else { "-rust-$version-$channel" } + +# On MSVC you can compile LLVM with clang-cl, but the test suite doesn't pass +# with clang-cl, so this is special in that it only compiles LLVM with clang-cl. +# Note that this takes a /path/to/clang-cl, not a boolean. +#clang-cl = cc + +# Pass extra compiler and linker flags to the LLVM CMake build. +#cflags = "" +#cxxflags = "" +#ldflags = "" + +# Use libc++ when building LLVM instead of libstdc++. This is the default on +# platforms already use libc++ as the default C++ library, but this option +# allows you to use libc++ even on platforms when it's not. You need to ensure +# that your host compiler ships with libc++. +#use-libcxx = false + +# The value specified here will be passed as `-DLLVM_USE_LINKER` to CMake. +#use-linker = (path) + +# Whether or not to specify `-DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=YES` +#allow-old-toolchain = false + +# Whether to include the Polly optimizer. +#polly = false + +# Whether to build the clang compiler. +#clang = false + +# Whether to enable llvm compilation warnings. +#enable-warnings = false + +# Custom CMake defines to set when building LLVM. +#build-config = {} + +# ============================================================================= +# Tweaking how GCC is compiled +# ============================================================================= +[gcc] +# Download GCC from CI instead of building it locally. +# Note that this will attempt to download GCC even if there are local +# modifications to the `src/gcc` submodule. +# Currently, this is only supported for the `x86_64-unknown-linux-gnu` target. +#download-ci-gcc = false + +# ============================================================================= +# General build configuration options +# ============================================================================= +[build] + +# The default stage to use for the `check` subcommand +#check-stage = 0 + +# The default stage to use for the `doc` subcommand +#doc-stage = 0 + +# The default stage to use for the `build` subcommand +#build-stage = 1 + +# The default stage to use for the `test` subcommand +#test-stage = 1 + +# The default stage to use for the `dist` subcommand +#dist-stage = 2 + +# The default stage to use for the `install` subcommand +#install-stage = 2 + +# The default stage to use for the `bench` subcommand +#bench-stage = 2 + +# A descriptive string to be appended to version output (e.g., `rustc --version`), +# which is also used in places like debuginfo `DW_AT_producer`. This may be useful for +# supplementary build information, like distro-specific package versions. +# +# The Rust compiler will differentiate between versions of itself, including +# based on this string, which means that if you wish to be compatible with +# upstream Rust you need to set this to "". However, note that if you set this to "" but +# are not actually compatible -- for example if you've backported patches that change +# behavior -- this may lead to miscompilations or other bugs. +#description = "" + +# Build triple for the pre-compiled snapshot compiler. If `rustc` is set, this must match its host +# triple (see `rustc --version --verbose`; cross-compiling the rust build system itself is NOT +# supported). If `rustc` is unset, this must be a platform with pre-compiled host tools +# (https://doc.rust-lang.org/nightly/rustc/platform-support.html). The current platform must be +# able to run binaries of this build triple. +# +# If `rustc` is present in path, this defaults to the host it was compiled for. +# Otherwise, `x.py` will try to infer it from the output of `uname`. +# If `uname` is not found in PATH, we assume this is `x86_64-pc-windows-msvc`. +# This may be changed in the future. +#build = "x86_64-unknown-linux-gnu" (as an example) + +# Which triples to produce a compiler toolchain for. Each of these triples will be bootstrapped from +# the build triple themselves. In other words, this is the list of triples for which to build a +# compiler that can RUN on that triple. +# +# Defaults to just the `build` triple. +#host = [build.build] (list of triples) + +# Which triples to build libraries (core/alloc/std/test/proc_macro) for. Each of these triples will +# be bootstrapped from the build triple themselves. In other words, this is the list of triples for +# which to build a library that can CROSS-COMPILE to that 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 = build.host (list of triples) + +# Use this directory to store build artifacts. Paths are relative to the current directory, not to +# the root of the repository. +#build-dir = "build" + +# Instead of downloading the src/stage0 version of Cargo specified, use +# this Cargo binary instead to build all Rust code +# If you set this, you likely want to set `rustc` as well. +#cargo = "/path/to/cargo" + +# Instead of downloading the src/stage0 version of the compiler +# specified, use this rustc binary instead as the stage0 snapshot compiler. +# If you set this, you likely want to set `cargo` as well. +#rustc = "/path/to/rustc" + +# Instead of downloading the src/stage0 version of rustfmt specified, +# use this rustfmt binary instead as the stage0 snapshot rustfmt. +#rustfmt = "/path/to/rustfmt" + +# Instead of downloading the src/stage0 version of cargo-clippy specified, +# use this cargo-clippy binary instead as the stage0 snapshot cargo-clippy. +# +# Note that this option should be used with the same toolchain as the `rustc` option above. +# Otherwise, clippy is likely to fail due to a toolchain conflict. +#cargo-clippy = "/path/to/cargo-clippy" + +# Whether to build documentation by default. If false, rustdoc and +# friends will still be compiled but they will not be used to generate any +# documentation. +# +# You can still build documentation when this is disabled by explicitly passing paths, +# e.g. `x doc library`. +#docs = true + +# Flag to specify whether CSS, JavaScript, and HTML are minified when +# docs are generated. JSON is always minified, because it's enormous, +# and generated in already-minified form from the beginning. +#docs-minification = true + +# Flag to specify whether private items should be included in the library docs. +#library-docs-private-items = false + +# Indicate whether to build compiler documentation by default. +# You can still build documentation when this is disabled by explicitly passing a path: `x doc compiler`. +#compiler-docs = false + +# Indicate whether git submodules are managed and updated automatically. +#submodules = true + +# The path to (or name of) the GDB executable to use. This is only used for +# executing the debuginfo test suite. +#gdb = "gdb" + +# The path to (or name of) the LLDB executable to use. This is only used for +# executing the debuginfo test suite. +#lldb = "lldb" + +# The node.js executable to use. Note that this is only used for the emscripten +# target when running tests, otherwise this can be omitted. +#nodejs = "node" + +# The npm executable to use. Note that this is used for rustdoc-gui tests, +# otherwise this can be omitted. +# +# Under Windows this should be `npm.cmd` or path to it (verified on nodejs v18.06), or +# error will be emitted. +#npm = "npm" + +# Python interpreter to use for various tasks throughout the build, notably +# rustdoc tests, the lldb python interpreter, and some dist bits and pieces. +# +# Defaults to the Python interpreter used to execute x.py. +#python = "python" + +# The path to the REUSE executable to use. Note that REUSE is not required in +# most cases, as our tooling relies on a cached (and shrunk) copy of the +# REUSE output present in the git repository and in our source tarballs. +# +# REUSE is only needed if your changes caused the overall licensing of the +# repository to change, and the cached copy has to be regenerated. +# +# Defaults to the "reuse" command in the system path. +#reuse = "reuse" + +# Force Cargo to check that Cargo.lock describes the precise dependency +# set that all the Cargo.toml files create, instead of updating it. +#locked-deps = false + +# Indicate whether the vendored sources are used for Rust dependencies or not. +# +# Vendoring requires additional setup. We recommend using the pre-generated source tarballs if you +# want to use vendoring. See https://forge.rust-lang.org/infra/other-installation-methods.html#source-code. +#vendor = if "is a tarball source" && "vendor" dir exists && ".cargo/config.toml" file exists { true } else { false } + +# Typically the build system will build the Rust compiler twice. The second +# compiler, however, will simply use its own libraries to link against. If you +# would rather to perform a full bootstrap, compiling the compiler three times, +# then you can set this option to true. +# +# This is only useful for verifying that rustc generates reproducible builds. +#full-bootstrap = false + +# Set the bootstrap/download cache path. It is useful when building rust +# repeatedly in a CI environment. +#bootstrap-cache-path = /path/to/shared/cache + +# Enable a build of the extended Rust tool set which is not only the compiler +# but also tools such as Cargo. This will also produce "combined installers" +# which are used to install Rust and Cargo together. +# The `tools` (check `bootstrap.example.toml` to see its default value) option specifies +# which tools should be built if `extended = true`. +# +# This is disabled by default. +#extended = false + +# Set of tools to be included in the installation. +# +# If `extended = false`, the only one of these built by default is rustdoc. +# +# If `extended = true`, they are all included. +# +# If any enabled tool fails to build, the installation fails. +#tools = [ +# "cargo", +# "clippy", +# "rustdoc", +# "rustfmt", +# "rust-analyzer", +# "rust-analyzer-proc-macro-srv", +# "analysis", +# "src", +# "wasm-component-ld", +# "miri", "cargo-miri" # for dev/nightly channels +#] + +# Verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose, 3 == print environment variables on each rustc invocation +#verbose = 0 + +# Build the sanitizer runtimes +#sanitizers = false + +# Build the profiler runtime (required when compiling with options that depend +# on this runtime, such as `-C profile-generate` or `-C instrument-coverage`). +#profiler = false + +# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics. +# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt` +# sources are available. +# +# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in +# order to run `x check`. +#optimized-compiler-builtins = if rust.channel == "dev" { false } else { true } + +# Indicates whether the native libraries linked into Cargo will be statically +# linked or not. +#cargo-native-static = false + +# Run the build with low priority, by setting the process group's "nice" value +# to +10 on Unix platforms, and by using a "low priority" job object on Windows. +#low-priority = false + +# Arguments passed to the `./configure` script, used during distcheck. You +# probably won't fill this in but rather it's filled in by the `./configure` +# script. Useful for debugging. +#configure-args = [] + +# Indicates that a local rebuild is occurring instead of a full bootstrap, +# essentially skipping stage0 as the local compiler is recompiling itself again. +# Useful for modifying only the stage2 compiler without having to pass `--keep-stage 0` each time. +#local-rebuild = false + +# Print out how long each bootstrap step took (mostly intended for CI and +# tracking over time) +#print-step-timings = false + +# Print out resource usage data for each bootstrap step, as defined by the Unix +# struct rusage. (Note that this setting is completely unstable: the data it +# captures, what platforms it supports, the format of its associated output, and +# this setting's very existence, are all subject to change.) +#print-step-rusage = false + +# Always patch binaries for usage with Nix toolchains. If `true` then binaries +# will be patched unconditionally. If `false` or unset, binaries will be patched +# only if the current distribution is NixOS. This option is useful when using +# a Nix toolchain on non-NixOS distributions. +#patch-binaries-for-nix = false + +# Collect information and statistics about the current build, and write it to +# disk. Enabling this has no impact on the resulting build output. The +# schema of the file generated by the build metrics feature is unstable, and +# this is not intended to be used during local development. +#metrics = false + +# Specify the location of the Android NDK. Used when targeting Android. +#android-ndk = "/path/to/android-ndk-r26d" + +# Number of parallel jobs to be used for building and testing. If set to `0` or +# omitted, it will be automatically determined. This is the `-j`/`--jobs` flag +# passed to cargo invocations. +#jobs = 0 + +# What custom diff tool to use for displaying compiletest tests. +#compiletest-diff-tool = + +# Whether to use the precompiled stage0 libtest with compiletest. +#compiletest-use-stage0-libtest = true + +# Indicates whether ccache is used when building certain artifacts (e.g. LLVM). +# Set to `true` to use the first `ccache` in PATH, or set an absolute path to use +# a specific version. +#ccache = false + +# List of paths to exclude from the build and test processes. +# For example, exclude = ["tests/ui", "src/tools/tidy"]. +#exclude = [] + +# ============================================================================= +# General install configuration options +# ============================================================================= +[install] + +# Where to install the generated toolchain. Must be an absolute path. +#prefix = "/usr/local" + +# Where to install system configuration files. +# If this is a relative path, it will get installed in `prefix` above +#sysconfdir = "/etc" + +# Where to install documentation in `prefix` above +#docdir = "share/doc/rust" + +# Where to install binaries in `prefix` above +#bindir = "bin" + +# Where to install libraries in `prefix` above +#libdir = "lib" + +# Where to install man pages in `prefix` above +#mandir = "share/man" + +# Where to install data in `prefix` above +#datadir = "share" + +# ============================================================================= +# Options for compiling Rust code itself +# ============================================================================= +[rust] + +# Whether or not to optimize when compiling the compiler and standard library, +# and what level of optimization to use. +# WARNING: Building with optimize = false is NOT SUPPORTED. Due to bootstrapping, +# building without optimizations takes much longer than optimizing. Further, some platforms +# fail to build without this optimization (c.f. #65352). +# The valid options are: +# true - Enable optimizations (same as 3). +# false - Disable optimizations. +# 0 - Disable optimizations. +# 1 - Basic optimizations. +# 2 - Some optimizations. +# 3 - All optimizations. +# "s" - Optimize for binary size. +# "z" - Optimize for binary size, but also turn off loop vectorization. +#optimize = true + +# Indicates that the build should be configured for debugging Rust. A +# `debug`-enabled compiler and standard library will be somewhat +# slower (due to e.g. checking of debug assertions) but should remain +# usable. +# +# Note: If this value is set to `true`, it will affect a number of +# configuration options below as well, if they have been left +# unconfigured in this file. +# +# Note: changes to the `debug` setting do *not* affect `optimize` +# above. In theory, a "maximally debuggable" environment would +# set `optimize` to `false` above to assist the introspection +# facilities of debuggers like lldb and gdb. To recreate such an +# environment, explicitly set `optimize` to `false` and `debug` +# to `true`. In practice, everyone leaves `optimize` set to +# `true`, because an unoptimized rustc with debugging +# enabled becomes *unusably slow* (e.g. rust-lang/rust#24840 +# reported a 25x slowdown) and bootstrapping the supposed +# "maximally debuggable" environment (notably libstd) takes +# hours to build. +# +#debug = false + +# Whether to download the stage 1 and 2 compilers from CI. This is useful if you +# are working on tools, doc-comments, or library (you will be able to build the +# standard library without needing to build the compiler). +# +# Set this to "if-unchanged" if you are working on `src/tools`, `tests` or +# `library` (on CI, `library` changes triggers in-tree compiler build) to speed +# up the build process if you don't need to build a compiler from the latest +# commit from `master`. +# +# Set this to `true` to always download or `false` to always use the in-tree +# compiler. +#download-rustc = false + +# 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. +# +# 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. +# NOTE: building with anything other than 1 is known to occasionally have bugs. +#codegen-units-std = codegen-units + +# Whether or not debug assertions are enabled for the compiler and standard library. +# These can help find bugs at the cost of a small runtime slowdown. +# +# Defaults to rust.debug value +#debug-assertions = rust.debug (boolean) + +# 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 = rust.debug-assertions (boolean) + +# Whether or not debug assertions are enabled for the tools built by bootstrap. +# Overrides the `debug-assertions` option, if defined. +# +# Defaults to rust.debug-assertions value +#debug-assertions-tools = rust.debug-assertions (boolean) + +# Whether or not to leave debug! and trace! calls in the rust binary. +# +# Defaults to rust.debug-assertions value +# +# If you see a message from `tracing` saying "some trace filter directives would enable traces that +# are disabled statically" because `max_level_info` is enabled, set this value to `true`. +#debug-logging = rust.debug-assertions (boolean) + +# Whether or not to build rustc, tools and the libraries with randomized type layout +#randomize-layout = false + +# Whether or not overflow checks are enabled for the compiler and standard +# library. +# +# Defaults to rust.debug value +#overflow-checks = rust.debug (boolean) + +# Whether or not overflow checks are enabled for the standard library. +# Overrides the `overflow-checks` option, if defined. +# +# Defaults to rust.overflow-checks value +#overflow-checks-std = rust.overflow-checks (boolean) + +# Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`. +# See https://doc.rust-lang.org/rustc/codegen-options/index.html#debuginfo for available options. +# +# Can be overridden for specific subsets of Rust code (rustc, std or tools). +# Debuginfo for tests run with compiletest is not controlled by this option +# and needs to be enabled separately with `debuginfo-level-tests`. +# +# Note that debuginfo-level = 2 generates several gigabytes of debuginfo +# and will slow down the linking process significantly. +#debuginfo-level = if rust.debug { 1 } else { 0 } + +# Debuginfo level for the compiler. +#debuginfo-level-rustc = rust.debuginfo-level + +# Debuginfo level for the standard library. +#debuginfo-level-std = rust.debuginfo-level + +# Debuginfo level for the tools. +#debuginfo-level-tools = rust.debuginfo-level + +# Debuginfo level for the test suites run with compiletest. +# FIXME(#61117): Some tests fail when this option is enabled. +#debuginfo-level-tests = 0 + +# Should rustc and the standard library be built with split debuginfo? Default +# is platform dependent. +# +# This field is deprecated, use `target..split-debuginfo` instead. +# +# The value specified here is only used when targeting the `build.build` triple, +# and is overridden by `target..split-debuginfo` if specified. +# +#split-debuginfo = see target..split-debuginfo + +# Whether or not `panic!`s generate backtraces (RUST_BACKTRACE) +#backtrace = true + +# Whether to always use incremental compilation when building rustc +#incremental = false + +# The default linker that will be hard-coded into the generated +# compiler for targets that don't specify a default linker explicitly +# in their target specifications. Note that this is not the linker +# used to link said compiler. It can also be set per-target (via the +# `[target.]` block), which may be useful in a cross-compilation +# setting. +# +# See https://doc.rust-lang.org/rustc/codegen-options/index.html#linker for more information. +#default-linker = (path) + +# The "channel" for the Rust build to produce. The stable/beta channels only +# allow using stable features, whereas the nightly and dev channels allow using +# nightly features. +# +# You can set the channel to "auto-detect" to load the channel name from `src/ci/channel`. +# +# If using tarball sources, default value is "auto-detect", otherwise, it's "dev". +#channel = if "is a tarball source" { "auto-detect" } else { "dev" } + +# The root location of the musl installation directory. The library directory +# will also need to contain libunwind.a for an unwinding implementation. Note +# that this option only makes sense for musl targets that produce statically +# linked binaries. +# +# Defaults to /usr on musl hosts. Has no default otherwise. +#musl-root = (path) + +# By default the `rustc` executable is built with `-Wl,-rpath` flags on Unix +# platforms to ensure that the compiler is usable by default from the build +# directory (as it links to a number of dynamic libraries). This may not be +# desired in distributions, for example. +#rpath = true + +# Indicates whether symbols should be stripped using `-Cstrip=symbols`. +#strip = false + +# Forces frame pointers to be used with `-Cforce-frame-pointers`. +# This can be helpful for profiling at a small performance cost. +#frame-pointers = false + +# Indicates whether stack protectors should be used +# via the unstable option `-Zstack-protector`. +# +# Valid options are : `none`(default),`basic`,`strong`, or `all`. +# `strong` and `basic` options may be buggy and are not recommended, see rust-lang/rust#114903. +#stack-protector = "none" + +# Prints each test name as it is executed, to help debug issues in the test harness itself. +#verbose-tests = if is_verbose { true } else { false } + +# Flag indicating whether tests are compiled with optimizations (the -O flag). +#optimize-tests = true + +# Flag indicating whether codegen tests will be run or not. If you get an error +# saying that the FileCheck executable is missing, you may want to disable this. +# Also see the target's llvm-filecheck option. +#codegen-tests = true + +# Flag indicating whether git info will be retrieved from .git automatically. +# Having the git information can cause a lot of rebuilds during development. +#omit-git-hash = if rust.channel == "dev" { true } else { false } + +# Whether to create a source tarball by default when running `x dist`. +# +# You can still build a source tarball when this is disabled by explicitly passing `x dist rustc-src`. +#dist-src = true + +# After building or testing an optional component (e.g. the nomicon or reference), append the +# result (broken, compiling, testing) into this JSON file. +#save-toolstates = (path) + +# This is an array of the codegen backends that will be compiled for the rustc +# that's being compiled. The default is to only build the LLVM codegen backend, +# and currently the only standard options supported are `"llvm"`, `"cranelift"` +# and `"gcc"`. The first backend in this list will be used as default by rustc +# when no explicit backend is specified. +#codegen-backends = ["llvm"] + +# Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute, and +# whether to set it as rustc's default linker on `x86_64-unknown-linux-gnu`. This will also only be +# when *not* building an external LLVM (so only when using `download-ci-llvm` or building LLVM from +# the in-tree source): setting `llvm-config` in the `[target.x86_64-unknown-linux-gnu]` section will +# make this default to false. +#lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true + +# Indicates whether LLD will be used to link Rust crates during bootstrap on +# supported platforms. +# If set to `true` or `"external"`, a global `lld` binary that has to be in $PATH +# will be used. +# If set to `"self-contained"`, rust-lld from the snapshot compiler will be used. +# +# On MSVC, LLD will not be used if we're cross linking. +# +# 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 +# sysroot. +#llvm-tools = true + +# Indicates whether the `self-contained` llvm-bitcode-linker, will be made available +# in the sysroot. It is required for running nvptx tests. +#llvm-bitcode-linker = false + +# Whether to deny warnings in crates +#deny-warnings = true + +# Print backtrace on internal compiler errors during bootstrap +#backtrace-on-ice = false + +# Whether to verify generated LLVM IR +#verify-llvm-ir = false + +# Compile the compiler with a non-default ThinLTO import limit. This import +# limit controls the maximum size of functions imported by ThinLTO. Decreasing +# will make code compile faster at the expense of lower runtime performance. +#thin-lto-import-instr-limit = if incremental { 10 } else { LLVM default (currently 100) } + +# Map debuginfo paths to `/rust/$sha/...`. +# Useful for reproducible builds. Generally only set for releases +#remap-debuginfo = false + +# Link the compiler and LLVM against `jemalloc` instead of the default libc allocator. +# This option is only tested on Linux and OSX. It can also be configured per-target in the +# [target.] section. +#jemalloc = false + +# Run tests in various test suites with the "nll compare mode" in addition to +# running the tests in normal mode. Largely only used on CI and during local +# development of NLL +#test-compare-mode = false + +# Global default for llvm-libunwind for all targets. See the target-specific +# documentation for llvm-libunwind below. Note that the target-specific +# option will override this if set. +#llvm-libunwind = 'no' + +# Enable Windows Control Flow Guard checks in the standard library. +# This only applies from stage 1 onwards, and only for Windows targets. +#control-flow-guard = false + +# Enable Windows EHCont Guard checks in the standard library. +# This only applies from stage 1 onwards, and only for Windows targets. +#ehcont-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). +# When no setting is given, the new scheme will be used when compiling the +# compiler and its tools and the legacy scheme will be used when compiling the +# standard library. +# If an explicit setting is given, it will be used for all parts of the codebase. +#new-symbol-mangling = true|false (see comment) + +# Select LTO mode that will be used for compiling rustc. By default, thin local LTO +# (LTO within a single crate) is used (like for any Rust crate). You can also select +# "thin" or "fat" to apply Thin/Fat LTO to the `rustc_driver` dylib, or "off" to disable +# LTO entirely. +#lto = "thin-local" + +# Build compiler with the optimization enabled and -Zvalidate-mir, currently only for `std` +#validate-mir-opts = 3 + +# Configure `std` features used during bootstrap. +# +# Default features will be expanded in the following cases: +# - If `rust.llvm-libunwind` or `target.llvm-libunwind` is enabled: +# - "llvm-libunwind" will be added for in-tree LLVM builds. +# - "system-llvm-libunwind" will be added for system LLVM builds. +# - If `rust.backtrace` is enabled, "backtrace" will be added. +# - If `rust.profiler` or `target.profiler` is enabled, "profiler" will be added. +# - If building for a zkvm target, "compiler-builtins-mem" will be added. +# +# Since libstd also builds libcore and liballoc as dependencies and all their features are mirrored +# as libstd features, this option can also be used to configure features such as optimize_for_size. +#std-features = ["panic_unwind"] + +# ============================================================================= +# Options for specific targets +# +# Each of the following options is scoped to the specific target triple in +# question and is used for determining how to compile each target. +# ============================================================================= +[target.x86_64-unknown-linux-gnu] + +# C compiler to be used to compile C 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. +# See `src/bootstrap/src/utils/cc_detect.rs` for details. +#cc = "cc" (path) + +# C++ compiler to be used to compile C++ code (e.g. LLVM and our LLVM shims). +# This is only used for host targets. +# See `src/bootstrap/src/utils/cc_detect.rs` for details. +#cxx = "c++" (path) + +# Archiver to be used to assemble static libraries compiled from C/C++ code. +# Note: an absolute path should be used, otherwise LLVM build will break. +#ar = "ar" (path) + +# Ranlib to be used to assemble static libraries compiled from C/C++ code. +# Note: an absolute path should be used, otherwise LLVM build will break. +#ranlib = "ranlib" (path) + +# Linker to be used to bootstrap 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 when targeting MSVC. +#linker = "cc" (path) + +# Should rustc and the standard library be built with split debuginfo? Default +# is platform dependent. +# +# Valid values are the same as those accepted by `-C split-debuginfo` +# (`off`/`unpacked`/`packed`). +# +# On Linux, split debuginfo is disabled by default. +# +# On Apple platforms, unpacked split debuginfo is used by default. Unpacked +# debuginfo does not run `dsymutil`, which packages debuginfo from disparate +# object files into a single `.dSYM` file. `dsymutil` adds time to builds for +# no clear benefit, and also makes it more difficult for debuggers to find +# debug info. The compiler currently defaults to running `dsymutil` to preserve +# its historical default, but when compiling the compiler itself, we skip it by +# default since we know it's safe to do so in that case. +# +# On Windows platforms, packed debuginfo is the only supported option, +# producing a `.pdb` file. +#split-debuginfo = if linux { off } else if windows { packed } else if apple { unpacked } + +# Path to the `llvm-config` binary of the installation of a custom LLVM to link +# against. Note that if this is specified we don't compile LLVM at all for this +# target. +#llvm-config = (path) + +# Override detection of whether this is a Rust-patched LLVM. This would be used +# in conjunction with either an llvm-config or build.submodules = false. +#llvm-has-rust-patches = if llvm-config { false } else { true } + +# Normally the build system can find LLVM's FileCheck utility, but if +# not, you can specify an explicit file name for it. +#llvm-filecheck = "/path/to/llvm-version/bin/FileCheck" + +# Use LLVM libunwind as the implementation for Rust's unwinder. +# Accepted values are 'in-tree' (formerly true), 'system' or 'no' (formerly false). +# This option only applies for Linux and Fuchsia targets. +# On Linux target, if crt-static is not enabled, 'no' means dynamic link to +# `libgcc_s.so`, 'in-tree' means static link to the in-tree build of llvm libunwind +# and 'system' means dynamic link to `libunwind.so`. If crt-static is enabled, +# the behavior is depend on the libc. On musl target, 'no' and 'in-tree' both +# means static link to the in-tree build of llvm libunwind, and 'system' means +# static link to `libunwind.a` provided by system. Due to the limitation of glibc, +# it must link to `libgcc_eh.a` to get a working output, and this option have no effect. +#llvm-libunwind = 'no' if Linux, 'in-tree' if Fuchsia + +# Build the sanitizer runtimes for this target. +# This option will override the same option under [build] section. +#sanitizers = build.sanitizers (bool) + +# When true, build the profiler runtime for this target (required when compiling +# with options that depend on this runtime, such as `-C profile-generate` or +# `-C instrument-coverage`). This may also be given a path to an existing build +# of the profiling runtime library from LLVM's compiler-rt. +# This option will override the same option under [build] section. +#profiler = build.profiler (bool) + +# This option supports enable `rpath` in each target independently, +# and will override the same option under [rust] section. It only works on Unix platforms +#rpath = rust.rpath (bool) + +# Force static or dynamic linkage of the standard library for this target. If +# this target is a host for rustc, this will also affect the linkage of the +# compiler itself. This is useful for building rustc on targets that normally +# only use static libraries. If unset, the target's default linkage is used. +#crt-static = (bool) + +# The root location of the musl installation directory. The library directory +# will also need to contain libunwind.a for an unwinding implementation. Note +# that this option only makes sense for musl targets that produce statically +# linked binaries. +#musl-root = build.musl-root (path) + +# The full path to the musl libdir. +#musl-libdir = musl-root/lib + +# The root location of the `wasm32-wasip1` sysroot. Only used for WASI +# related targets. Make sure to create a `[target.wasm32-wasip1]` +# section and move this field there (or equivalent for the target being built). +#wasi-root = (path) + +# Used in testing for configuring where the QEMU images are located, you +# probably don't want to use this. +#qemu-rootfs = (path) + +# Skip building the `std` library for this target. Enabled by default for +# target triples containing `-none`, `nvptx`, `switch`, or `-uefi`. +#no-std = (bool) + +# This is an array of the codegen backends that will be +# compiled for this target, overriding the global rust.codegen-backends option. +# See that option for more info. +#codegen-backends = rust.codegen-backends (array) + +# This is a "runner" to pass to `compiletest` when executing tests. Tests will +# execute this tool where the binary-to-test is passed as an argument. Can +# be useful for situations such as when WebAssembly is being tested and a +# runtime needs to be configured. This value is similar to +# Cargo's `CARGO_$target_RUNNER` configuration. +# +# This configuration is a space-separated list of arguments so `foo bar` would +# execute the program `foo` with the first argument as `bar` and the second +# argument as the test binary. +#runner = (string) + +# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics +# on this target. +# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt` +# sources are available. +# +# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in +# order to run `x check`. +#optimized-compiler-builtins = build.optimized-compiler-builtins (bool) + +# Link the compiler and LLVM against `jemalloc` instead of the default libc allocator. +# This overrides the global `rust.jemalloc` option. See that option for more info. +#jemalloc = rust.jemalloc (bool) + +# ============================================================================= +# Distribution options +# +# These options are related to distribution, mostly for the Rust project itself. +# You probably won't need to concern yourself with any of these options +# ============================================================================= +[dist] + +# This is the folder of artifacts that the build system will sign. All files in +# this directory will be signed with the default gpg key using the system `gpg` +# binary. The `asc` and `sha256` files will all be output into the standard dist +# output folder (currently `build/dist`) +# +# This folder should be populated ahead of time before the build system is +# invoked. +#sign-folder = (path) + +# The remote address that all artifacts will eventually be uploaded to. The +# build system generates manifests which will point to these urls, and for the +# manifests to be correct they'll have to have the right URLs encoded. +# +# Note that this address should not contain a trailing slash as file names will +# be appended to it. +#upload-addr = (URL) + +# Whether to build a plain source tarball to upload +# We disable that on Windows not to override the one already uploaded on S3 +# as the one built on Windows will contain backslashes in paths causing problems +# on linux +#src-tarball = true + +# List of compression formats to use when generating dist tarballs. The list of +# formats is provided to rust-installer, which must support all of them. +# +# This list must be non-empty. +#compression-formats = ["gz", "xz"] + +# How much time should be spent compressing the tarballs. The better the +# compression profile, the longer compression will take. +# +# Available options: fast, balanced, best +#compression-profile = "fast" + +# Copy the linker, DLLs, and various libraries from MinGW into the Rust toolchain. +# Only applies when the host or target is pc-windows-gnu. +#include-mingw-linker = true + +# Whether to vendor dependencies for the dist tarball. +#vendor = if "is a tarball source" || "is a git repository" { true } else { false } diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml index 86dc84e2016d6..5f9afc46a1ace 100644 --- a/compiler/rustc_abi/Cargo.toml +++ b/compiler/rustc_abi/Cargo.toml @@ -6,13 +6,13 @@ edition = "2024" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -rand = { version = "0.8.4", default-features = false, optional = true } -rand_xoshiro = { version = "0.6.0", optional = true } -rustc_data_structures = { path = "../rustc_data_structures", optional = true } +rand = { version = "0.9.0", default-features = false, optional = true } +rand_xoshiro = { version = "0.7.0", optional = true } +rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_hashes = { path = "../rustc_hashes" } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } -rustc_serialize = { path = "../rustc_serialize", optional = true } +rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_span = { path = "../rustc_span", optional = true } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index 543c2f8ab12cf..55f4845d21670 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -60,7 +60,6 @@ pub enum ExternAbi { System { unwind: bool, }, - RustIntrinsic, RustCall, /// *Not* a stable ABI, just directly use the Rust types to describe the ABI for LLVM. Even /// normally ABI-compatible Rust types can become ABI-incompatible with this ABI! @@ -128,7 +127,6 @@ abi_impls! { RiscvInterruptS =><= "riscv-interrupt-s", RustCall =><= "rust-call", RustCold =><= "rust-cold", - RustIntrinsic =><= "rust-intrinsic", Stdcall { unwind: false } =><= "stdcall", Stdcall { unwind: true } =><= "stdcall-unwind", System { unwind: false } =><= "system", @@ -191,6 +189,17 @@ impl StableOrd for ExternAbi { } impl ExternAbi { + /// An ABI "like Rust" + /// + /// These ABIs are fully controlled by the Rust compiler, which means they + /// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"` + /// - often diverge from the C ABI + /// - are subject to change between compiler versions + pub fn is_rustic_abi(self) -> bool { + use ExternAbi::*; + matches!(self, Rust | RustCall | RustCold) + } + pub fn supports_varargs(self) -> bool { // * C and Cdecl obviously support varargs. // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs. diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index d3ae6a29f10f4..42250aa173bbd 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -4,6 +4,7 @@ use std::{cmp, iter}; use rustc_hashes::Hash64; use rustc_index::Idx; +use rustc_index::bit_set::BitMatrix; use tracing::debug; use crate::{ @@ -12,6 +13,9 @@ use crate::{ Variants, WrappingRange, }; +mod coroutine; +mod simple; + #[cfg(feature = "nightly")] mod ty; @@ -60,17 +64,28 @@ pub enum LayoutCalculatorError { /// The fields or variants have irreconcilable reprs ReprConflict, + + /// The length of an SIMD type is zero + ZeroLengthSimdType, + + /// The length of an SIMD type exceeds the maximum number of lanes + OversizedSimdType { max_lanes: u64 }, + + /// An element type of an SIMD type isn't a primitive + NonPrimitiveSimdType(F), } impl LayoutCalculatorError { pub fn without_payload(&self) -> LayoutCalculatorError<()> { - match self { - LayoutCalculatorError::UnexpectedUnsized(_) => { - LayoutCalculatorError::UnexpectedUnsized(()) - } - LayoutCalculatorError::SizeOverflow => LayoutCalculatorError::SizeOverflow, - LayoutCalculatorError::EmptyUnion => LayoutCalculatorError::EmptyUnion, - LayoutCalculatorError::ReprConflict => LayoutCalculatorError::ReprConflict, + use LayoutCalculatorError::*; + match *self { + UnexpectedUnsized(_) => UnexpectedUnsized(()), + SizeOverflow => SizeOverflow, + EmptyUnion => EmptyUnion, + ReprConflict => ReprConflict, + ZeroLengthSimdType => ZeroLengthSimdType, + OversizedSimdType { max_lanes } => OversizedSimdType { max_lanes }, + NonPrimitiveSimdType(_) => NonPrimitiveSimdType(()), } } @@ -78,13 +93,15 @@ impl LayoutCalculatorError { /// /// Intended for use by rust-analyzer, as neither it nor `rustc_abi` depend on fluent infra. pub fn fallback_fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use LayoutCalculatorError::*; f.write_str(match self { - LayoutCalculatorError::UnexpectedUnsized(_) => { - "an unsized type was found where a sized type was expected" + UnexpectedUnsized(_) => "an unsized type was found where a sized type was expected", + SizeOverflow => "size overflow", + EmptyUnion => "type is a union with no fields", + ReprConflict => "type has an invalid repr", + ZeroLengthSimdType | OversizedSimdType { .. } | NonPrimitiveSimdType(_) => { + "invalid simd type definition" } - LayoutCalculatorError::SizeOverflow => "size overflow", - LayoutCalculatorError::EmptyUnion => "type is a union with no fields", - LayoutCalculatorError::ReprConflict => "type has an invalid repr", }) } } @@ -102,41 +119,115 @@ impl LayoutCalculator { Self { cx } } - pub fn scalar_pair( + pub fn array_like( &self, - a: Scalar, - b: Scalar, - ) -> LayoutData { - let dl = self.cx.data_layout(); - let b_align = b.align(dl); - let align = a.align(dl).max(b_align).max(dl.aggregate_align); - let b_offset = a.size(dl).align_to(b_align.abi); - let size = (b_offset + b.size(dl)).align_to(align.abi); + element: &LayoutData, + count_if_sized: Option, // None for slices + ) -> LayoutCalculatorResult { + let count = count_if_sized.unwrap_or(0); + let size = + element.size.checked_mul(count, &self.cx).ok_or(LayoutCalculatorError::SizeOverflow)?; - // 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) - .into_iter() - .chain(Niche::from_scalar(dl, Size::ZERO, a)) - .max_by_key(|niche| niche.available(dl)); + Ok(LayoutData { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Array { stride: element.size, count }, + backend_repr: BackendRepr::Memory { sized: count_if_sized.is_some() }, + largest_niche: element.largest_niche.filter(|_| count != 0), + uninhabited: element.uninhabited && count != 0, + align: element.align, + size, + max_repr_align: None, + unadjusted_abi_align: element.align.abi, + randomization_seed: element.randomization_seed.wrapping_add(Hash64::new(count)), + }) + } - let combined_seed = a.size(&self.cx).bytes().wrapping_add(b.size(&self.cx).bytes()); + pub fn simd_type< + FieldIdx: Idx, + VariantIdx: Idx, + F: AsRef> + fmt::Debug, + >( + &self, + element: F, + count: u64, + repr_packed: bool, + ) -> LayoutCalculatorResult { + let elt = element.as_ref(); + if count == 0 { + return Err(LayoutCalculatorError::ZeroLengthSimdType); + } else if count > crate::MAX_SIMD_LANES { + return Err(LayoutCalculatorError::OversizedSimdType { + max_lanes: crate::MAX_SIMD_LANES, + }); + } - LayoutData { + let BackendRepr::Scalar(e_repr) = elt.backend_repr else { + return Err(LayoutCalculatorError::NonPrimitiveSimdType(element)); + }; + + // Compute the size and alignment of the vector + let dl = self.cx.data_layout(); + let size = + elt.size.checked_mul(count, dl).ok_or_else(|| LayoutCalculatorError::SizeOverflow)?; + let (repr, align) = if repr_packed && !count.is_power_of_two() { + // Non-power-of-two vectors have padding up to the next power-of-two. + // If we're a packed repr, remove the padding while keeping the alignment as close + // to a vector as possible. + ( + BackendRepr::Memory { sized: true }, + AbiAndPrefAlign { + abi: Align::max_aligned_factor(size), + pref: dl.llvmlike_vector_align(size).pref, + }, + ) + } else { + (BackendRepr::SimdVector { element: e_repr, count }, dl.llvmlike_vector_align(size)) + }; + let size = size.align_to(align.abi); + + Ok(LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldsShape::Arbitrary { - offsets: [Size::ZERO, b_offset].into(), - memory_index: [0, 1].into(), + offsets: [Size::ZERO].into(), + memory_index: [0].into(), }, - backend_repr: BackendRepr::ScalarPair(a, b), - largest_niche, + backend_repr: repr, + largest_niche: elt.largest_niche, uninhabited: false, - align, size, + align, max_repr_align: None, - unadjusted_abi_align: align.abi, - randomization_seed: Hash64::new(combined_seed), - } + unadjusted_abi_align: elt.align.abi, + randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)), + }) + } + + /// Compute the layout for a coroutine. + /// + /// This uses dedicated code instead of [`Self::layout_of_struct_or_enum`], as coroutine + /// fields may be shared between multiple variants (see the [`coroutine`] module for details). + pub fn coroutine< + 'a, + F: Deref> + fmt::Debug + Copy, + VariantIdx: Idx, + FieldIdx: Idx, + LocalIdx: Idx, + >( + &self, + local_layouts: &IndexSlice, + prefix_layouts: IndexVec, + variant_fields: &IndexSlice>, + storage_conflicts: &BitMatrix, + tag_to_layout: impl Fn(Scalar) -> F, + ) -> LayoutCalculatorResult { + coroutine::layout( + self, + local_layouts, + prefix_layouts, + variant_fields, + storage_conflicts, + tag_to_layout, + ) } pub fn univariant< @@ -214,25 +305,6 @@ impl LayoutCalculator { layout } - pub fn layout_of_never_type( - &self, - ) -> LayoutData { - let dl = self.cx.data_layout(); - // This is also used for uninhabited enums, so we use `Variants::Empty`. - LayoutData { - variants: Variants::Empty, - fields: FieldsShape::Primitive, - backend_repr: BackendRepr::Memory { sized: true }, - largest_niche: None, - uninhabited: true, - align: dl.i8_align, - size: Size::ZERO, - max_repr_align: None, - unadjusted_abi_align: dl.i8_align.abi, - randomization_seed: Hash64::ZERO, - } - } - pub fn layout_of_struct_or_enum< 'a, FieldIdx: Idx, @@ -243,7 +315,7 @@ impl LayoutCalculator { repr: &ReprOptions, variants: &IndexSlice>, is_enum: bool, - is_unsafe_cell: bool, + is_special_no_niche: bool, scalar_valid_range: (Bound, Bound), discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool), discriminants: impl Iterator, @@ -260,7 +332,7 @@ impl LayoutCalculator { Some(present_first) => present_first, // Uninhabited because it has no variants, or only absent ones. None if is_enum => { - return Ok(self.layout_of_never_type()); + return Ok(LayoutData::never_type(&self.cx)); } // If it's a struct, still compute a layout so that we can still compute the // field offsets. @@ -276,7 +348,7 @@ impl LayoutCalculator { repr, variants, is_enum, - is_unsafe_cell, + is_special_no_niche, scalar_valid_range, always_sized, present_first, @@ -433,7 +505,7 @@ impl LayoutCalculator { repr: &ReprOptions, variants: &IndexSlice>, is_enum: bool, - is_unsafe_cell: bool, + is_special_no_niche: bool, scalar_valid_range: (Bound, Bound), always_sized: bool, present_first: VariantIdx, @@ -452,7 +524,7 @@ impl LayoutCalculator { let mut st = self.univariant(&variants[v], repr, kind)?; st.variants = Variants::Single { index: v }; - if is_unsafe_cell { + if is_special_no_niche { let hide_niches = |scalar: &mut _| match scalar { Scalar::Initialized { value, valid_range } => { *valid_range = WrappingRange::full(value.size(dl)) @@ -949,7 +1021,8 @@ impl LayoutCalculator { // Common prim might be uninit. Scalar::Union { value: prim } }; - let pair = self.scalar_pair::(tag, prim_scalar); + let pair = + LayoutData::::scalar_pair(&self.cx, tag, prim_scalar); let pair_offsets = match pair.fields { FieldsShape::Arbitrary { ref offsets, ref memory_index } => { assert_eq!(memory_index.raw, [0, 1]); @@ -1341,7 +1414,8 @@ impl LayoutCalculator { } else { ((j, b), (i, a)) }; - let pair = self.scalar_pair::(a, b); + let pair = + LayoutData::::scalar_pair(&self.cx, a, b); let pair_offsets = match pair.fields { FieldsShape::Arbitrary { ref offsets, ref memory_index } => { assert_eq!(memory_index.raw, [0, 1]); diff --git a/compiler/rustc_abi/src/layout/coroutine.rs b/compiler/rustc_abi/src/layout/coroutine.rs new file mode 100644 index 0000000000000..27e704d538c83 --- /dev/null +++ b/compiler/rustc_abi/src/layout/coroutine.rs @@ -0,0 +1,320 @@ +//! Coroutine layout logic. +//! +//! When laying out coroutines, 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. + +use std::iter; + +use rustc_index::bit_set::{BitMatrix, DenseBitSet}; +use rustc_index::{Idx, IndexSlice, IndexVec}; +use tracing::{debug, trace}; + +use crate::{ + BackendRepr, FieldsShape, HasDataLayout, Integer, LayoutData, Primitive, ReprOptions, Scalar, + StructKind, TagEncoding, Variants, WrappingRange, +}; + +/// Overlap eligibility and variant assignment for each CoroutineSavedLocal. +#[derive(Clone, Debug, PartialEq)] +enum SavedLocalEligibility { + Unassigned, + Assigned(VariantIdx), + Ineligible(Option), +} + +/// Compute the eligibility and assignment of each local. +fn coroutine_saved_local_eligibility( + nb_locals: usize, + variant_fields: &IndexSlice>, + storage_conflicts: &BitMatrix, +) -> (DenseBitSet, IndexVec>) { + use SavedLocalEligibility::*; + + let mut assignments: IndexVec = IndexVec::from_elem_n(Unassigned, nb_locals); + + // The saved locals not eligible for overlap. These will get + // "promoted" to the prefix of our coroutine. + let mut ineligible_locals = DenseBitSet::new_empty(nb_locals); + + // 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 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 storage_conflicts.rows() { + let conflicts_a = storage_conflicts.count(local_a); + if ineligible_locals.contains(local_a) { + continue; + } + + for local_b in storage_conflicts.iter(local_a) { + // local_a and local_b are storage live at the same time, therefore they + // cannot overlap in the coroutine 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 = 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 = DenseBitSet::new_empty(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(FieldIdx::new(idx))); + } + } + debug!("coroutine saved local assignments: {:?}", assignments); + + (ineligible_locals, assignments) +} + +/// Compute the full coroutine layout. +pub(super) fn layout< + 'a, + F: core::ops::Deref> + core::fmt::Debug + Copy, + VariantIdx: Idx, + FieldIdx: Idx, + LocalIdx: Idx, +>( + calc: &super::LayoutCalculator, + local_layouts: &IndexSlice, + mut prefix_layouts: IndexVec, + variant_fields: &IndexSlice>, + storage_conflicts: &BitMatrix, + tag_to_layout: impl Fn(Scalar) -> F, +) -> super::LayoutCalculatorResult { + use SavedLocalEligibility::*; + + let (ineligible_locals, assignments) = + coroutine_saved_local_eligibility(local_layouts.len(), variant_fields, storage_conflicts); + + // 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 = prefix_layouts.len(); + + // `variant_fields` already accounts for the reserved variants, so no need to add them. + let max_discr = (variant_fields.len() - 1) as u128; + let discr_int = Integer::fit_unsigned(max_discr); + let tag = Scalar::Initialized { + value: Primitive::Int(discr_int, /* signed = */ false), + valid_range: WrappingRange { start: 0, end: max_discr }, + }; + + let promoted_layouts = ineligible_locals.iter().map(|local| local_layouts[local]); + prefix_layouts.push(tag_to_layout(tag)); + prefix_layouts.extend(promoted_layouts); + let prefix = + calc.univariant(&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 + // CoroutineLayout. + 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 = memory_index.invert_bijective_mapping(); + + // "a" (`0..b_start`) and "b" (`b_start..`) correspond to + // "outer" and "promoted" fields respectively. + let b_start = FieldIdx::new(tag_index + 1); + let offsets_b = IndexVec::from_raw(offsets.raw.split_off(b_start.index())); + 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: IndexVec = inverse_memory_index + .iter() + .filter_map(|&i| i.index().checked_sub(b_start.index()).map(FieldIdx::new)) + .collect(); + inverse_memory_index.raw.retain(|&i| i.index() < b_start.index()); + 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 = inverse_memory_index_a.invert_bijective_mapping(); + let memory_index_b = inverse_memory_index_b.invert_bijective_mapping(); + + let outer_fields = + FieldsShape::Arbitrary { offsets: offsets_a, memory_index: memory_index_a }; + (outer_fields, offsets_b, memory_index_b) + } + _ => unreachable!(), + }; + + let mut size = prefix.size; + let mut align = prefix.align; + let variants = 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 => unreachable!(), + Assigned(v) if v == index => true, + Assigned(_) => unreachable!("assignment does not match variant"), + Ineligible(_) => false, + }) + .map(|local| local_layouts[*local]); + + let mut variant = calc.univariant( + &variant_only_tys.collect::>(), + &ReprOptions::default(), + StructKind::Prefixed(prefix_size, prefix_align.abi), + )?; + variant.variants = Variants::Single { index }; + + let FieldsShape::Arbitrary { offsets, memory_index } = variant.fields else { + unreachable!(); + }; + + // Now, stitch the promoted and variant-only fields back together in + // the order they are mentioned by our CoroutineLayout. + // 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. + let invalid_field_idx = promoted_memory_index.len() + memory_index.len(); + let mut combined_inverse_memory_index = + IndexVec::from_elem_n(FieldIdx::new(invalid_field_idx), invalid_field_idx); + + let mut offsets_and_memory_index = iter::zip(offsets, memory_index); + let combined_offsets = variant_fields + .iter_enumerated() + .map(|(i, local)| { + let (offset, memory_index) = match assignments[*local] { + Unassigned => unreachable!(), + 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(); + (promoted_offsets[field_idx], promoted_memory_index[field_idx]) + } + }; + combined_inverse_memory_index[memory_index] = i; + offset + }) + .collect(); + + // Remove the unused slots and invert the mapping to obtain the + // combined `memory_index` (also see previous comment). + combined_inverse_memory_index.raw.retain(|&i| i.index() != invalid_field_idx); + let combined_memory_index = combined_inverse_memory_index.invert_bijective_mapping(); + + 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 uninhabited = prefix.uninhabited || variants.iter().all(|v| v.is_uninhabited()); + let abi = BackendRepr::Memory { sized: true }; + + Ok(LayoutData { + variants: Variants::Multiple { + tag, + tag_encoding: TagEncoding::Direct, + tag_field: tag_index, + variants, + }, + fields: outer_fields, + backend_repr: abi, + // Suppress niches inside coroutines. If the niche is inside a field that is aliased (due to + // self-referentiality), getting the discriminant can cause aliasing violations. + // `UnsafeCell` blocks niches for the same reason, but we don't yet have `UnsafePinned` that + // would do the same for us here. + // See , . + // FIXME: Remove when is implemented and aliased coroutine fields are wrapped in `UnsafePinned`. + largest_niche: None, + uninhabited, + size, + align, + max_repr_align: None, + unadjusted_abi_align: align.abi, + randomization_seed: Default::default(), + }) +} diff --git a/compiler/rustc_abi/src/layout/simple.rs b/compiler/rustc_abi/src/layout/simple.rs new file mode 100644 index 0000000000000..0d0706defc2e5 --- /dev/null +++ b/compiler/rustc_abi/src/layout/simple.rs @@ -0,0 +1,148 @@ +use std::num::NonZero; + +use rustc_hashes::Hash64; +use rustc_index::{Idx, IndexVec}; + +use crate::{ + BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size, Variants, +}; + +/// "Simple" layout constructors that cannot fail. +impl LayoutData { + pub fn unit(cx: &C, sized: bool) -> Self { + let dl = cx.data_layout(); + LayoutData { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Arbitrary { + offsets: IndexVec::new(), + memory_index: IndexVec::new(), + }, + backend_repr: BackendRepr::Memory { sized }, + largest_niche: None, + uninhabited: false, + align: dl.i8_align, + size: Size::ZERO, + max_repr_align: None, + unadjusted_abi_align: dl.i8_align.abi, + randomization_seed: Hash64::new(0), + } + } + + pub fn never_type(cx: &C) -> Self { + let dl = cx.data_layout(); + // This is also used for uninhabited enums, so we use `Variants::Empty`. + LayoutData { + variants: Variants::Empty, + fields: FieldsShape::Primitive, + backend_repr: BackendRepr::Memory { sized: true }, + largest_niche: None, + uninhabited: true, + align: dl.i8_align, + size: Size::ZERO, + max_repr_align: None, + unadjusted_abi_align: dl.i8_align.abi, + randomization_seed: Hash64::ZERO, + } + } + + pub fn scalar(cx: &C, scalar: Scalar) -> Self { + let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar); + let size = scalar.size(cx); + let align = scalar.align(cx); + + let range = scalar.valid_range(cx); + + // All primitive types for which we don't have subtype coercions should get a distinct seed, + // so that types wrapping them can use randomization to arrive at distinct layouts. + // + // Some type information is already lost at this point, so as an approximation we derive + // the seed from what remains. For example on 64-bit targets usize and u64 can no longer + // be distinguished. + let randomization_seed = size + .bytes() + .wrapping_add( + match scalar.primitive() { + Primitive::Int(_, true) => 1, + Primitive::Int(_, false) => 2, + Primitive::Float(_) => 3, + Primitive::Pointer(_) => 4, + } << 32, + ) + // distinguishes references from pointers + .wrapping_add((range.start as u64).rotate_right(16)) + // distinguishes char from u32 and bool from u8 + .wrapping_add((range.end as u64).rotate_right(16)); + + LayoutData { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Primitive, + backend_repr: BackendRepr::Scalar(scalar), + largest_niche, + uninhabited: false, + size, + align, + max_repr_align: None, + unadjusted_abi_align: align.abi, + randomization_seed: Hash64::new(randomization_seed), + } + } + + pub fn scalar_pair(cx: &C, a: Scalar, b: Scalar) -> Self { + let dl = cx.data_layout(); + let b_align = b.align(dl); + let align = a.align(dl).max(b_align).max(dl.aggregate_align); + let b_offset = a.size(dl).align_to(b_align.abi); + let size = (b_offset + b.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) + .into_iter() + .chain(Niche::from_scalar(dl, Size::ZERO, a)) + .max_by_key(|niche| niche.available(dl)); + + let combined_seed = a.size(dl).bytes().wrapping_add(b.size(dl).bytes()); + + LayoutData { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Arbitrary { + offsets: [Size::ZERO, b_offset].into(), + memory_index: [0, 1].into(), + }, + backend_repr: BackendRepr::ScalarPair(a, b), + largest_niche, + uninhabited: false, + align, + size, + max_repr_align: None, + unadjusted_abi_align: align.abi, + randomization_seed: Hash64::new(combined_seed), + } + } + + /// Returns a dummy layout for an uninhabited variant. + /// + /// Uninhabited variants get pruned as part of the layout calculation, + /// so this can be used after the fact to reconstitute a layout. + pub fn uninhabited_variant(cx: &C, index: VariantIdx, fields: usize) -> Self { + let dl = cx.data_layout(); + LayoutData { + variants: Variants::Single { index }, + fields: match NonZero::new(fields) { + Some(fields) => FieldsShape::Union(fields), + None => FieldsShape::Arbitrary { + offsets: IndexVec::new(), + memory_index: IndexVec::new(), + }, + }, + backend_repr: BackendRepr::Memory { sized: true }, + largest_niche: None, + uninhabited: true, + align: dl.i8_align, + size: Size::ZERO, + max_repr_align: None, + unadjusted_abi_align: dl.i8_align.abi, + randomization_seed: Hash64::ZERO, + } + } +} diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 03f3f043c218e..4f43c0e6f8e96 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -150,6 +150,12 @@ impl<'a, Ty> Deref for TyAndLayout<'a, Ty> { } } +impl<'a, Ty> AsRef> for TyAndLayout<'a, Ty> { + fn as_ref(&self) -> &LayoutData { + &*self.layout.0.0 + } +} + /// Trait that needs to be implemented by the higher-level type representation /// (e.g. `rustc_middle::ty::Ty`), to provide `rustc_target::abi` functionality. pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug { diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 2197dd68eaf69..59b74d2922145 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -5,7 +5,6 @@ #![cfg_attr(feature = "nightly", feature(rustc_attrs))] #![cfg_attr(feature = "nightly", feature(rustdoc_internals))] #![cfg_attr(feature = "nightly", feature(step_trait))] -#![warn(unreachable_pub)] // tidy-alphabetical-end /*! ABI handling for rustc @@ -53,7 +52,7 @@ use rustc_data_structures::stable_hasher::StableOrd; use rustc_hashes::Hash64; use rustc_index::{Idx, IndexSlice, IndexVec}; #[cfg(feature = "nightly")] -use rustc_macros::{Decodable_Generic, Encodable_Generic, HashStable_Generic}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_Generic}; mod callconv; mod layout; @@ -75,7 +74,10 @@ pub use layout::{LayoutCalculator, LayoutCalculatorError}; pub trait HashStableContext {} #[derive(Clone, Copy, PartialEq, Eq, Default)] -#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic) +)] pub struct ReprFlags(u8); bitflags! { @@ -107,7 +109,10 @@ impl std::fmt::Debug for ReprFlags { } #[derive(Copy, Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic) +)] pub enum IntegerType { /// Pointer-sized integer type, i.e. `isize` and `usize`. The field shows signedness, e.g. /// `Pointer(true)` means `isize`. @@ -128,7 +133,10 @@ impl IntegerType { /// Represents the repr options provided by the user. #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] -#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic) +)] pub struct ReprOptions { pub int: Option, pub align: Option, @@ -205,6 +213,13 @@ impl ReprOptions { } } +/// The maximum supported number of lanes in a SIMD vector. +/// +/// This value is selected based on backend support: +/// * LLVM does not appear to have a vector width limit. +/// * Cranelift stores the base-2 log of the lane count in a 4 bit integer. +pub const MAX_SIMD_LANES: u64 = 1 << 0xF; + /// Parsed [Data layout](https://llvm.org/docs/LangRef.html#data-layout) /// for a target, which contains everything needed to compute layouts. #[derive(Debug, PartialEq, Eq)] @@ -481,7 +496,10 @@ impl FromStr for Endian { /// Size of a type in bytes. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic) +)] pub struct Size { raw: u64, } @@ -707,7 +725,10 @@ impl Step for Size { /// Alignment of a type in bytes (always a power of two). #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic) +)] pub struct Align { pow2: u8, } @@ -796,7 +817,7 @@ impl Align { } #[inline] - pub fn bytes(self) -> u64 { + pub const fn bytes(self) -> u64 { 1 << self.pow2 } @@ -806,7 +827,7 @@ impl Align { } #[inline] - pub fn bits(self) -> u64 { + pub const fn bits(self) -> u64 { self.bytes() * 8 } @@ -866,7 +887,10 @@ impl AbiAndPrefAlign { /// Integers, also used for enum discriminants. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic) +)] pub enum Integer { I8, I16, @@ -1438,7 +1462,8 @@ impl BackendRepr { !self.is_unsized() } - /// Returns `true` if this is a single signed integer scalar + /// Returns `true` if this is a single signed integer scalar. + /// Sanity check: panics if this is not a scalar type (see PR #70189). #[inline] pub fn is_signed(&self) -> bool { match self { @@ -1744,48 +1769,6 @@ impl LayoutData { pub fn is_uninhabited(&self) -> bool { self.uninhabited } - - pub fn scalar(cx: &C, scalar: Scalar) -> Self { - let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar); - let size = scalar.size(cx); - let align = scalar.align(cx); - - let range = scalar.valid_range(cx); - - // All primitive types for which we don't have subtype coercions should get a distinct seed, - // so that types wrapping them can use randomization to arrive at distinct layouts. - // - // Some type information is already lost at this point, so as an approximation we derive - // the seed from what remains. For example on 64-bit targets usize and u64 can no longer - // be distinguished. - let randomization_seed = size - .bytes() - .wrapping_add( - match scalar.primitive() { - Primitive::Int(_, true) => 1, - Primitive::Int(_, false) => 2, - Primitive::Float(_) => 3, - Primitive::Pointer(_) => 4, - } << 32, - ) - // distinguishes references from pointers - .wrapping_add((range.start as u64).rotate_right(16)) - // distinguishes char from u32 and bool from u8 - .wrapping_add((range.end as u64).rotate_right(16)); - - LayoutData { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Primitive, - backend_repr: BackendRepr::Scalar(scalar), - largest_niche, - uninhabited: false, - size, - align, - max_repr_align: None, - unadjusted_abi_align: align.abi, - randomization_seed: Hash64::new(randomization_seed), - } - } } impl fmt::Debug for LayoutData @@ -1812,7 +1795,7 @@ where f.debug_struct("Layout") .field("size", size) .field("align", align) - .field("abi", backend_repr) + .field("backend_repr", backend_repr) .field("fields", fields) .field("largest_niche", largest_niche) .field("uninhabited", uninhabited) @@ -1846,7 +1829,7 @@ pub struct PointeeInfo { pub safe: Option, /// If `safe` is `Some`, then the pointer is either null or dereferenceable for this many bytes. /// On a function argument, "dereferenceable" here means "dereferenceable for the entire duration - /// of this function call", i.e. it is UB for the memory that this pointer points to to be freed + /// of this function call", i.e. it is UB for the memory that this pointer points to be freed /// while this function is still running. /// The size can be zero if the pointer is not dereferenceable. pub size: Size, diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index b21ccba93bb44..d3b7e679d171b 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -21,9 +21,10 @@ #![feature(decl_macro)] #![feature(dropck_eyepatch)] #![feature(maybe_uninit_slice)] +#![feature(never_type)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] +#![feature(unwrap_infallible)] // tidy-alphabetical-end use std::alloc::Layout; @@ -93,7 +94,7 @@ impl ArenaChunk { #[inline] fn end(&mut self) -> *mut T { unsafe { - if mem::size_of::() == 0 { + if size_of::() == 0 { // A pointer as large as possible for zero-sized elements. ptr::without_provenance_mut(!0) } else { @@ -151,7 +152,7 @@ impl TypedArena { } unsafe { - if mem::size_of::() == 0 { + if size_of::() == 0 { self.ptr.set(self.ptr.get().wrapping_byte_add(1)); let ptr = ptr::NonNull::::dangling().as_ptr(); // Don't drop the object. This `write` is equivalent to `forget`. @@ -173,13 +174,13 @@ impl TypedArena { // FIXME: this should *likely* use `offset_from`, but more // investigation is needed (including running tests in miri). let available_bytes = self.end.get().addr() - self.ptr.get().addr(); - let additional_bytes = additional.checked_mul(mem::size_of::()).unwrap(); + let additional_bytes = additional.checked_mul(size_of::()).unwrap(); available_bytes >= additional_bytes } #[inline] fn alloc_raw_slice(&self, len: usize) -> *mut T { - assert!(mem::size_of::() != 0); + assert!(size_of::() != 0); assert!(len != 0); // Ensure the current chunk can fit `len` objects. @@ -201,6 +202,18 @@ impl TypedArena { /// storing the elements in the arena. #[inline] pub fn alloc_from_iter>(&self, iter: I) -> &mut [T] { + self.try_alloc_from_iter(iter.into_iter().map(Ok::)).into_ok() + } + + /// Allocates the elements of this iterator into a contiguous slice in the `TypedArena`. + /// + /// Note: for reasons of reentrancy and panic safety we collect into a `SmallVec<[_; 8]>` before + /// storing the elements in the arena. + #[inline] + pub fn try_alloc_from_iter( + &self, + iter: impl IntoIterator>, + ) -> Result<&mut [T], E> { // Despite the similarlty with `DroplessArena`, we cannot reuse their fast case. The reason // is subtle: these arenas are reentrant. In other words, `iter` may very well be holding a // reference to `self` and adding elements to the arena during iteration. @@ -213,20 +226,21 @@ impl TypedArena { // So we collect all the elements beforehand, which takes care of reentrancy and panic // safety. This function is much less hot than `DroplessArena::alloc_from_iter`, so it // doesn't need to be hyper-optimized. - assert!(mem::size_of::() != 0); + assert!(size_of::() != 0); - let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); + let vec: Result, E> = iter.into_iter().collect(); + let mut vec = vec?; if vec.is_empty() { - return &mut []; + return Ok(&mut []); } // Move the content to the arena by copying and then forgetting it. let len = vec.len(); let start_ptr = self.alloc_raw_slice(len); - unsafe { + Ok(unsafe { vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); vec.set_len(0); slice::from_raw_parts_mut(start_ptr, len) - } + }) } /// Grows the arena. @@ -236,7 +250,7 @@ impl TypedArena { 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 elem_size = cmp::max(1, size_of::()); let mut chunks = self.chunks.borrow_mut(); let mut new_cap; if let Some(last_chunk) = chunks.last_mut() { @@ -246,7 +260,7 @@ impl TypedArena { // FIXME: this should *likely* use `offset_from`, but more // investigation is needed (including running tests in miri). let used_bytes = self.ptr.get().addr() - last_chunk.start().addr(); - last_chunk.entries = used_bytes / mem::size_of::(); + last_chunk.entries = used_bytes / size_of::(); } // If the previous chunk's len is less than HUGE_PAGE @@ -276,7 +290,7 @@ impl TypedArena { let end = self.ptr.get().addr(); // 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 { + let diff = if 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. @@ -284,7 +298,7 @@ impl TypedArena { } else { // FIXME: this should *likely* use `offset_from`, but more // investigation is needed (including running tests in miri). - (end - start) / mem::size_of::() + (end - start) / size_of::() }; // Pass that to the `destroy` method. unsafe { @@ -329,7 +343,7 @@ fn align_up(val: usize, align: usize) -> usize { // Pointer alignment is common in compiler types, so keep `DroplessArena` aligned to them // to optimize away alignment code. -const DROPLESS_ALIGNMENT: usize = mem::align_of::(); +const DROPLESS_ALIGNMENT: usize = align_of::(); /// An arena that can hold objects of multiple different types that impl `Copy` /// and/or satisfy `!mem::needs_drop`. @@ -447,7 +461,7 @@ impl DroplessArena { #[inline] pub fn alloc(&self, object: T) -> &mut T { assert!(!mem::needs_drop::()); - assert!(mem::size_of::() != 0); + assert!(size_of::() != 0); let mem = self.alloc_raw(Layout::new::()) as *mut T; @@ -471,7 +485,7 @@ impl DroplessArena { T: Copy, { assert!(!mem::needs_drop::()); - assert!(mem::size_of::() != 0); + assert!(size_of::() != 0); assert!(!slice.is_empty()); let mem = self.alloc_raw(Layout::for_value::<[T]>(slice)) as *mut T; @@ -546,7 +560,7 @@ impl DroplessArena { // Warning: this function is reentrant: `iter` could hold a reference to `&self` and // allocate additional elements while we're iterating. let iter = iter.into_iter(); - assert!(mem::size_of::() != 0); + assert!(size_of::() != 0); assert!(!mem::needs_drop::()); let size_hint = iter.size_hint(); @@ -567,26 +581,33 @@ impl DroplessArena { // `drop`. unsafe { self.write_from_iter(iter, len, mem) } } - (_, _) => { - outline(move || -> &mut [T] { - // Takes care of reentrancy. - 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) - } - }) - } + (_, _) => outline(move || self.try_alloc_from_iter(iter.map(Ok::)).into_ok()), + } + } + + #[inline] + pub fn try_alloc_from_iter( + &self, + iter: impl IntoIterator>, + ) -> Result<&mut [T], E> { + // Despite the similarlty with `alloc_from_iter`, we cannot reuse their fast case, as we + // cannot know the minimum length of the iterator in this case. + assert!(size_of::() != 0); + + // Takes care of reentrancy. + let vec: Result, E> = iter.into_iter().collect(); + let mut vec = vec?; + if vec.is_empty() { + return Ok(&mut []); } + // Move the content to the arena by copying and then forgetting it. + let len = vec.len(); + Ok(unsafe { + 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) + }) } } diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml index 902287d032802..b2d3b90fc4494 100644 --- a/compiler/rustc_ast/Cargo.toml +++ b/compiler/rustc_ast/Cargo.toml @@ -7,10 +7,10 @@ edition = "2024" # tidy-alphabetical-start bitflags = "2.4.1" memchr = "2.7.4" +rustc-literal-escaper = "0.0.2" rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } -rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5c44fda226241..a16219361c051 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -120,14 +120,34 @@ impl Path { Path { segments: thin_vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None } } + pub fn is_ident(&self, name: Symbol) -> bool { + if let [segment] = self.segments.as_ref() + && segment.args.is_none() + && segment.ident.name == name + { + true + } else { + false + } + } + pub fn is_global(&self) -> bool { self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot) } - /// If this path is a single identifier with no arguments, does not ensure - /// that the path resolves to a const param, the caller should check this. - pub fn is_potential_trivial_const_arg(&self) -> bool { - matches!(self.segments[..], [PathSegment { args: None, .. }]) + /// Check if this path is potentially a trivial const arg, i.e., one that can _potentially_ + /// be represented without an anon const in the HIR. + /// + /// If `allow_mgca_arg` is true (as should be the case in most situations when + /// `#![feature(min_generic_const_args)]` is enabled), then this always returns true + /// because all paths are valid. + /// + /// Otherwise, it returns true iff the path has exactly one segment, and it has no generic args + /// (i.e., it is _potentially_ a const parameter). + #[tracing::instrument(level = "debug", ret)] + pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool { + allow_mgca_arg + || self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none()) } } @@ -288,7 +308,6 @@ impl ParenthesizedArgs { } } -use crate::AstDeref; pub use crate::node_id::{CRATE_NODE_ID, DUMMY_NODE_ID, NodeId}; /// Modifiers on a trait bound like `~const`, `?` and `!`. @@ -417,9 +436,11 @@ impl WhereClause { /// A single predicate in a where-clause. #[derive(Clone, Encodable, Decodable, Debug)] pub struct WherePredicate { + pub attrs: AttrVec, pub kind: WherePredicateKind, pub id: NodeId, pub span: Span, + pub is_placeholder: bool, } /// Predicate kind in where-clause. @@ -534,14 +555,6 @@ pub struct Block { pub rules: BlockCheckMode, pub span: Span, pub tokens: Option, - /// The following *isn't* a parse error, but will cause multiple errors in following stages. - /// ```compile_fail - /// let x = { - /// foo: var - /// }; - /// ``` - /// #34255 - pub could_be_bare_literal: bool, } /// A match pattern. @@ -560,6 +573,7 @@ impl Pat { /// This is intended for use by diagnostics. pub fn to_ty(&self) -> Option> { let kind = match &self.kind { + PatKind::Missing => unreachable!(), // In a type expression `_` is an inference variable. PatKind::Wild => TyKind::Infer, // An IDENT pattern with no binding mode would be valid as path to a type. E.g. `u32`. @@ -596,7 +610,7 @@ impl Pat { /// Walk top-down and call `it` in each place where a pattern occurs /// starting with the root pattern `walk` is called on. If `it` returns /// false then we will descend no further but siblings will be processed. - pub fn walk(&self, it: &mut impl FnMut(&Pat) -> bool) { + pub fn walk<'ast>(&'ast self, it: &mut impl FnMut(&'ast Pat) -> bool) { if !it(self) { return; } @@ -622,7 +636,8 @@ impl Pat { | PatKind::Guard(s, _) => s.walk(it), // These patterns do not contain subpatterns, skip. - PatKind::Wild + PatKind::Missing + | PatKind::Wild | PatKind::Rest | PatKind::Never | PatKind::Expr(_) @@ -673,6 +688,7 @@ impl Pat { /// Return a name suitable for diagnostics. pub fn descr(&self) -> Option { match &self.kind { + PatKind::Missing => unreachable!(), PatKind::Wild => Some("_".to_string()), PatKind::Ident(BindingMode::NONE, ident, None) => Some(format!("{ident}")), PatKind::Ref(pat, mutbl) => pat.descr().map(|d| format!("&{}{d}", mutbl.prefix_str())), @@ -766,6 +782,9 @@ pub enum RangeSyntax { // Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum PatKind { + /// A missing pattern, e.g. for an anonymous param in a bare fn like `fn f(u32)`. + Missing, + /// Represents a wildcard pattern (`_`). Wild, @@ -978,6 +997,75 @@ impl BinOpKind { pub type BinOp = Spanned; +// Sometimes `BinOpKind` and `AssignOpKind` need the same treatment. The +// operations covered by `AssignOpKind` are a subset of those covered by +// `BinOpKind`, so it makes sense to convert `AssignOpKind` to `BinOpKind`. +impl From for BinOpKind { + fn from(op: AssignOpKind) -> BinOpKind { + match op { + AssignOpKind::AddAssign => BinOpKind::Add, + AssignOpKind::SubAssign => BinOpKind::Sub, + AssignOpKind::MulAssign => BinOpKind::Mul, + AssignOpKind::DivAssign => BinOpKind::Div, + AssignOpKind::RemAssign => BinOpKind::Rem, + AssignOpKind::BitXorAssign => BinOpKind::BitXor, + AssignOpKind::BitAndAssign => BinOpKind::BitAnd, + AssignOpKind::BitOrAssign => BinOpKind::BitOr, + AssignOpKind::ShlAssign => BinOpKind::Shl, + AssignOpKind::ShrAssign => BinOpKind::Shr, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] +pub enum AssignOpKind { + /// The `+=` operator (addition) + AddAssign, + /// The `-=` operator (subtraction) + SubAssign, + /// The `*=` operator (multiplication) + MulAssign, + /// The `/=` operator (division) + DivAssign, + /// The `%=` operator (modulus) + RemAssign, + /// The `^=` operator (bitwise xor) + BitXorAssign, + /// The `&=` operator (bitwise and) + BitAndAssign, + /// The `|=` operator (bitwise or) + BitOrAssign, + /// The `<<=` operator (shift left) + ShlAssign, + /// The `>>=` operator (shift right) + ShrAssign, +} + +impl AssignOpKind { + pub fn as_str(&self) -> &'static str { + use AssignOpKind::*; + match self { + AddAssign => "+=", + SubAssign => "-=", + MulAssign => "*=", + DivAssign => "/=", + RemAssign => "%=", + BitXorAssign => "^=", + BitAndAssign => "&=", + BitOrAssign => "|=", + ShlAssign => "<<=", + ShrAssign => ">>=", + } + } + + /// AssignOps are always by value. + pub fn is_by_value(self) -> bool { + true + } +} + +pub type AssignOp = Spanned; + /// Unary operator. /// /// Note that `&data` is not an operator, it's an `AddrOf` expression. @@ -1097,6 +1185,7 @@ pub enum MacStmtStyle { #[derive(Clone, Encodable, Decodable, Debug)] pub struct Local { pub id: NodeId, + pub super_: Option, pub pat: P, pub ty: Option>, pub kind: LocalKind, @@ -1206,22 +1295,31 @@ pub struct Expr { } impl Expr { - /// Could this expr be either `N`, or `{ N }`, where `N` is a const parameter. + /// Check if this expression is potentially a trivial const arg, i.e., one that can _potentially_ + /// be represented without an anon const in the HIR. + /// + /// This will unwrap at most one block level (curly braces). After that, if the expression + /// is a path, it mostly dispatches to [`Path::is_potential_trivial_const_arg`]. + /// See there for more info about `allow_mgca_arg`. /// - /// If this is not the case, name resolution does not resolve `N` when using - /// `min_const_generics` as more complex expressions are not supported. + /// The only additional thing to note is that when `allow_mgca_arg` is false, this function + /// will only allow paths with no qself, before dispatching to the `Path` function of + /// the same name. /// - /// Does not ensure that the path resolves to a const param, the caller should check this. + /// Does not ensure that the path resolves to a const param/item, the caller should check this. /// This also does not consider macros, so it's only correct after macro-expansion. - pub fn is_potential_trivial_const_arg(&self) -> bool { + pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool { let this = self.maybe_unwrap_block(); - - if let ExprKind::Path(None, path) = &this.kind - && path.is_potential_trivial_const_arg() - { - true + if allow_mgca_arg { + matches!(this.kind, ExprKind::Path(..)) } else { - false + if let ExprKind::Path(None, path) = &this.kind + && path.is_potential_trivial_const_arg(allow_mgca_arg) + { + true + } else { + false + } } } @@ -1379,6 +1477,7 @@ impl Expr { // Never need parens ExprKind::Array(_) | ExprKind::Await(..) + | ExprKind::Use(..) | ExprKind::Block(..) | ExprKind::Call(..) | ExprKind::ConstBlock(_) @@ -1533,6 +1632,9 @@ pub enum ExprKind { /// An `if` block, with an optional `else` block. /// /// `if expr { block } else { expr }` + /// + /// If present, the "else" expr is always `ExprKind::Block` (for `else`) or + /// `ExprKind::If` (for `else if`). If(P, P, Option>), /// A while loop, with an optional label. /// @@ -1568,6 +1670,8 @@ pub enum ExprKind { Gen(CaptureBy, P, GenBlockKind, Span), /// An await expression (`my_future.await`). Span is of await keyword. Await(P, Span), + /// A use expression (`x.use`). Span is of use keyword. + Use(P, Span), /// A try block (`try { ... }`). TryBlock(P), @@ -1578,7 +1682,7 @@ pub enum ExprKind { /// An assignment with an operator. /// /// E.g., `a += 1`. - AssignOp(BinOp, P, P), + AssignOp(AssignOp, P, P), /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field. Field(P, Ident), /// An indexing operation (e.g., `foo[2]`). @@ -1634,7 +1738,7 @@ pub enum ExprKind { Try(P), /// A `yield`, with an optional value to be yielded. - Yield(Option>), + Yield(YieldKind), /// A `do yeet` (aka `throw`/`fail`/`bail`/`raise`/whatever), /// with an optional value to be returned. @@ -1737,8 +1841,17 @@ pub enum CaptureBy { /// The span of the `move` keyword. move_kw: Span, }, - /// `move` keyword was not specified. + /// `move` or `use` keywords were not specified. Ref, + /// `use |x| y + x`. + /// + /// Note that if you have a regular closure like `|| x.use`, this will *not* result + /// in a `Use` capture. Instead, the `ExprUseVisitor` will look at the type + /// of `x` and treat `x.use` as either a copy/clone/move as appropriate. + Use { + /// The span of the `use` keyword. + use_kw: Span, + }, } /// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`. @@ -1816,7 +1929,7 @@ impl AttrArgs { } /// Delimited arguments, as used in `#[attr()/[]/{}]` or `mac!()/[]/{}`. -#[derive(Clone, Encodable, Decodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct DelimArgs { pub dspan: DelimSpan, pub delim: Delimiter, // Note: `Delimiter::Invisible` never occurs @@ -1831,18 +1944,6 @@ impl DelimArgs { } } -impl HashStable for DelimArgs -where - CTX: crate::HashStableContext, -{ - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - let DelimArgs { dspan, delim, tokens } = self; - dspan.hash_stable(ctx, hasher); - delim.hash_stable(ctx, hasher); - tokens.hash_stable(ctx, hasher); - } -} - /// Represents a macro definition. #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MacroDef { @@ -1871,6 +1972,44 @@ pub enum MatchKind { Postfix, } +/// The kind of yield expression +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum YieldKind { + /// yield expr { ... } + Prefix(Option>), + /// expr.yield { ... } + Postfix(P), +} + +impl YieldKind { + /// Returns the expression inside the yield expression, if any. + /// + /// For postfix yields, this is guaranteed to be `Some`. + pub const fn expr(&self) -> Option<&P> { + match self { + YieldKind::Prefix(expr) => expr.as_ref(), + YieldKind::Postfix(expr) => Some(expr), + } + } + + /// Returns a mutable reference to the expression being yielded, if any. + pub const fn expr_mut(&mut self) -> Option<&mut P> { + match self { + YieldKind::Prefix(expr) => expr.as_mut(), + YieldKind::Postfix(expr) => Some(expr), + } + } + + /// Returns true if both yields are prefix or both are postfix. + pub const fn same_kind(&self, other: &Self) -> bool { + match (self, other) { + (YieldKind::Prefix(_), YieldKind::Prefix(_)) => true, + (YieldKind::Postfix(_), YieldKind::Postfix(_)) => true, + _ => false, + } + } +} + /// A literal in a meta item. #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MetaItemLit { @@ -2209,7 +2348,7 @@ impl Ty { pub fn is_maybe_parenthesised_infer(&self) -> bool { match &self.kind { TyKind::Infer => true, - TyKind::Paren(inner) => inner.ast_deref().is_maybe_parenthesised_infer(), + TyKind::Paren(inner) => inner.is_maybe_parenthesised_infer(), _ => false, } } @@ -2332,6 +2471,8 @@ pub enum TyPatKind { /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`). Range(Option>, Option>, Spanned), + Or(ThinVec>), + /// Placeholder for a pattern that wasn't syntactically well formed in some way. Err(ErrorGuaranteed), } @@ -2621,6 +2762,8 @@ pub enum SelfKind { Value(Mutability), /// `&'lt self`, `&'lt mut self` Region(Option, Mutability), + /// `&'lt pin const self`, `&'lt pin mut self` + Pinned(Option, Mutability), /// `self: TYPE`, `mut self: TYPE` Explicit(P, Mutability), } @@ -2630,6 +2773,8 @@ impl SelfKind { match self { SelfKind::Region(None, mutbl) => mutbl.ref_prefix_str().to_string(), SelfKind::Region(Some(lt), mutbl) => format!("&{lt} {}", mutbl.prefix_str()), + SelfKind::Pinned(None, mutbl) => format!("&pin {}", mutbl.ptr_str()), + SelfKind::Pinned(Some(lt), mutbl) => format!("&{lt} pin {}", mutbl.ptr_str()), SelfKind::Value(_) | SelfKind::Explicit(_, _) => { unreachable!("if we had an explicit self, we wouldn't be here") } @@ -2646,11 +2791,13 @@ impl Param { if ident.name == kw::SelfLower { return match self.ty.kind { TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))), - TyKind::Ref(lt, MutTy { ref ty, mutbl }) - | TyKind::PinnedRef(lt, MutTy { ref ty, mutbl }) + TyKind::Ref(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => { + Some(respan(self.pat.span, SelfKind::Region(lt, mutbl))) + } + TyKind::PinnedRef(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => { - Some(respan(self.pat.span, SelfKind::Region(lt, mutbl))) + Some(respan(self.pat.span, SelfKind::Pinned(lt, mutbl))) } _ => Some(respan( self.pat.span.to(self.ty.span), @@ -2692,6 +2839,15 @@ impl Param { tokens: None, }), ), + SelfKind::Pinned(lt, mutbl) => ( + mutbl, + P(Ty { + id: DUMMY_NODE_ID, + kind: TyKind::PinnedRef(lt, MutTy { ty: infer_ty, mutbl }), + span, + tokens: None, + }), + ), }; Param { attrs, @@ -3226,9 +3382,6 @@ pub struct Item { pub id: NodeId, pub span: Span, pub vis: Visibility, - /// The name of the item. - /// It might be a dummy name in case of anonymous items. - pub ident: Ident, pub kind: K, @@ -3250,23 +3403,23 @@ impl Item { pub fn opt_generics(&self) -> Option<&Generics> { match &self.kind { - ItemKind::ExternCrate(_) + ItemKind::ExternCrate(..) | ItemKind::Use(_) - | ItemKind::Mod(_, _) + | ItemKind::Mod(..) | ItemKind::ForeignMod(_) | ItemKind::GlobalAsm(_) | ItemKind::MacCall(_) | ItemKind::Delegation(_) | ItemKind::DelegationMac(_) - | ItemKind::MacroDef(_) => None, + | ItemKind::MacroDef(..) => None, ItemKind::Static(_) => None, ItemKind::Const(i) => Some(&i.generics), ItemKind::Fn(i) => Some(&i.generics), ItemKind::TyAlias(i) => Some(&i.generics), - ItemKind::TraitAlias(generics, _) - | ItemKind::Enum(_, generics) - | ItemKind::Struct(_, generics) - | ItemKind::Union(_, generics) => Some(&generics), + ItemKind::TraitAlias(_, generics, _) + | ItemKind::Enum(_, _, generics) + | ItemKind::Struct(_, _, generics) + | ItemKind::Union(_, _, generics) => Some(&generics), ItemKind::Trait(i) => Some(&i.generics), ItemKind::Impl(i) => Some(&i.generics), } @@ -3343,6 +3496,7 @@ impl Default for FnHeader { pub struct Trait { pub safety: Safety, pub is_auto: IsAuto, + pub ident: Ident, pub generics: Generics, pub bounds: GenericBounds, pub items: ThinVec>, @@ -3388,6 +3542,7 @@ pub struct TyAliasWhereClauses { #[derive(Clone, Encodable, Decodable, Debug)] pub struct TyAlias { pub defaultness: Defaultness, + pub ident: Ident, pub generics: Generics, pub where_clauses: TyAliasWhereClauses, pub bounds: GenericBounds, @@ -3416,9 +3571,11 @@ pub struct FnContract { #[derive(Clone, Encodable, Decodable, Debug)] pub struct Fn { pub defaultness: Defaultness, + pub ident: Ident, pub generics: Generics, pub sig: FnSig, pub contract: Option>, + pub define_opaque: Option>, pub body: Option>, } @@ -3428,6 +3585,7 @@ pub struct Delegation { pub id: NodeId, pub qself: Option>, pub path: Path, + pub ident: Ident, pub rename: Option, pub body: Option>, /// The item was expanded from a glob delegation item. @@ -3445,18 +3603,22 @@ pub struct DelegationMac { #[derive(Clone, Encodable, Decodable, Debug)] pub struct StaticItem { + pub ident: Ident, pub ty: P, pub safety: Safety, pub mutability: Mutability, pub expr: Option>, + pub define_opaque: Option>, } #[derive(Clone, Encodable, Decodable, Debug)] pub struct ConstItem { pub defaultness: Defaultness, + pub ident: Ident, pub generics: Generics, pub ty: P, pub expr: Option>, + pub define_opaque: Option>, } // Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`. @@ -3465,7 +3627,7 @@ pub enum ItemKind { /// An `extern crate` item, with the optional *original* crate name if the crate was renamed. /// /// E.g., `extern crate foo` or `extern crate foo_bar as foo`. - ExternCrate(Option), + ExternCrate(Option, Ident), /// A use declaration item (`use`). /// /// E.g., `use foo;`, `use foo::bar;` or `use foo::bar as FooBar;`. @@ -3487,7 +3649,7 @@ pub enum ItemKind { /// E.g., `mod foo;` or `mod foo { .. }`. /// `unsafe` keyword on modules is accepted syntactically for macro DSLs, but not /// semantically by Rust. - Mod(Safety, ModKind), + Mod(Safety, Ident, ModKind), /// An external module (`extern`). /// /// E.g., `extern {}` or `extern "C" {}`. @@ -3501,15 +3663,15 @@ pub enum ItemKind { /// An enum definition (`enum`). /// /// E.g., `enum Foo { C, D }`. - Enum(EnumDef, Generics), + Enum(Ident, EnumDef, Generics), /// A struct definition (`struct`). /// /// E.g., `struct Foo { x: A }`. - Struct(VariantData, Generics), + Struct(Ident, VariantData, Generics), /// A union definition (`union`). /// /// E.g., `union Foo { x: A, y: B }`. - Union(VariantData, Generics), + Union(Ident, VariantData, Generics), /// A trait declaration (`trait`). /// /// E.g., `trait Foo { .. }`, `trait Foo { .. }` or `auto trait Foo {}`. @@ -3517,7 +3679,7 @@ pub enum ItemKind { /// Trait alias. /// /// E.g., `trait Foo = Bar + Quux;`. - TraitAlias(Generics, GenericBounds), + TraitAlias(Ident, Generics, GenericBounds), /// An implementation. /// /// E.g., `impl Foo { .. }` or `impl Trait for Foo { .. }`. @@ -3528,7 +3690,7 @@ pub enum ItemKind { MacCall(P), /// A macro definition. - MacroDef(MacroDef), + MacroDef(Ident, MacroDef), /// A single delegation item (`reuse`). /// @@ -3540,6 +3702,31 @@ pub enum ItemKind { } impl ItemKind { + pub fn ident(&self) -> Option { + match *self { + ItemKind::ExternCrate(_, ident) + | ItemKind::Static(box StaticItem { ident, .. }) + | ItemKind::Const(box ConstItem { ident, .. }) + | ItemKind::Fn(box Fn { ident, .. }) + | ItemKind::Mod(_, ident, _) + | ItemKind::TyAlias(box TyAlias { ident, .. }) + | ItemKind::Enum(ident, ..) + | ItemKind::Struct(ident, ..) + | ItemKind::Union(ident, ..) + | ItemKind::Trait(box Trait { ident, .. }) + | ItemKind::TraitAlias(ident, ..) + | ItemKind::MacroDef(ident, _) + | ItemKind::Delegation(box Delegation { ident, .. }) => Some(ident), + + ItemKind::Use(_) + | ItemKind::ForeignMod(_) + | ItemKind::GlobalAsm(_) + | ItemKind::Impl(_) + | ItemKind::MacCall(_) + | ItemKind::DelegationMac(_) => None, + } + } + /// "a" or "an" pub fn article(&self) -> &'static str { use ItemKind::*; @@ -3580,11 +3767,11 @@ impl ItemKind { Self::Fn(box Fn { generics, .. }) | Self::TyAlias(box TyAlias { generics, .. }) | Self::Const(box ConstItem { generics, .. }) - | Self::Enum(_, generics) - | Self::Struct(_, generics) - | Self::Union(_, generics) + | Self::Enum(_, _, generics) + | Self::Struct(_, _, generics) + | Self::Union(_, _, generics) | Self::Trait(box Trait { generics, .. }) - | Self::TraitAlias(generics, _) + | Self::TraitAlias(_, generics, _) | Self::Impl(box Impl { generics, .. }) => Some(generics), _ => None, } @@ -3620,6 +3807,17 @@ pub enum AssocItemKind { } impl AssocItemKind { + pub fn ident(&self) -> Option { + match *self { + AssocItemKind::Const(box ConstItem { ident, .. }) + | AssocItemKind::Fn(box Fn { ident, .. }) + | AssocItemKind::Type(box TyAlias { ident, .. }) + | AssocItemKind::Delegation(box Delegation { ident, .. }) => Some(ident), + + AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(_) => None, + } + } + pub fn defaultness(&self) -> Defaultness { match *self { Self::Const(box ConstItem { defaultness, .. }) @@ -3666,14 +3864,26 @@ impl TryFrom for AssocItemKind { pub enum ForeignItemKind { /// A foreign static item (`static FOO: u8`). Static(Box), - /// An foreign function. + /// A foreign function. Fn(Box), - /// An foreign type. + /// A foreign type. TyAlias(Box), /// A macro expanding to foreign items. MacCall(P), } +impl ForeignItemKind { + pub fn ident(&self) -> Option { + match *self { + ForeignItemKind::Static(box StaticItem { ident, .. }) + | ForeignItemKind::Fn(box Fn { ident, .. }) + | ForeignItemKind::TyAlias(box TyAlias { ident, .. }) => Some(ident), + + ForeignItemKind::MacCall(_) => None, + } + } +} + impl From for ItemKind { fn from(foreign_item_kind: ForeignItemKind) -> ItemKind { match foreign_item_kind { @@ -3710,23 +3920,23 @@ mod size_asserts { use super::*; // tidy-alphabetical-start - static_assert_size!(AssocItem, 88); + static_assert_size!(AssocItem, 80); static_assert_size!(AssocItemKind, 16); static_assert_size!(Attribute, 32); static_assert_size!(Block, 32); static_assert_size!(Expr, 72); static_assert_size!(ExprKind, 40); - static_assert_size!(Fn, 168); - static_assert_size!(ForeignItem, 88); + static_assert_size!(Fn, 184); + static_assert_size!(ForeignItem, 80); static_assert_size!(ForeignItemKind, 16); static_assert_size!(GenericArg, 24); static_assert_size!(GenericBound, 88); static_assert_size!(Generics, 40); static_assert_size!(Impl, 136); - static_assert_size!(Item, 136); - static_assert_size!(ItemKind, 64); + static_assert_size!(Item, 144); + static_assert_size!(ItemKind, 80); static_assert_size!(LitKind, 24); - static_assert_size!(Local, 80); + static_assert_size!(Local, 96); static_assert_size!(MetaItemLit, 40); static_assert_size!(Param, 40); static_assert_size!(Pat, 72); diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 4a2d955938503..21de7ff7719b6 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -6,42 +6,13 @@ use std::fmt; use std::marker::PhantomData; use crate::ptr::P; -use crate::token::Nonterminal; use crate::tokenstream::LazyAttrTokenStream; use crate::{ Arm, AssocItem, AttrItem, AttrKind, AttrVec, Attribute, Block, Crate, Expr, ExprField, FieldDef, ForeignItem, GenericParam, Item, NodeId, Param, Pat, PatField, Path, Stmt, StmtKind, - Ty, Variant, Visibility, + Ty, Variant, Visibility, WherePredicate, }; -/// A utility trait to reduce boilerplate. -/// Standard `Deref(Mut)` cannot be reused due to coherence. -pub trait AstDeref { - type Target; - fn ast_deref(&self) -> &Self::Target; - fn ast_deref_mut(&mut self) -> &mut Self::Target; -} - -macro_rules! impl_not_ast_deref { - ($($T:ty),+ $(,)?) => { - $( - impl !AstDeref for $T {} - )+ - }; -} - -impl_not_ast_deref!(AssocItem, Expr, ForeignItem, Item, Stmt); - -impl AstDeref for P { - type Target = T; - fn ast_deref(&self) -> &Self::Target { - self - } - fn ast_deref_mut(&mut self) -> &mut Self::Target { - self - } -} - /// A trait for AST nodes having an ID. pub trait HasNodeId { fn node_id(&self) -> NodeId; @@ -79,14 +50,15 @@ impl_has_node_id!( Stmt, Ty, Variant, + WherePredicate, ); -impl> HasNodeId for T { +impl HasNodeId for P { fn node_id(&self) -> NodeId { - self.ast_deref().node_id() + (**self).node_id() } fn node_id_mut(&mut self) -> &mut NodeId { - self.ast_deref_mut().node_id_mut() + (**self).node_id_mut() } } @@ -127,23 +99,32 @@ macro_rules! impl_has_tokens_none { } impl_has_tokens!(AssocItem, AttrItem, Block, Expr, ForeignItem, Item, Pat, Path, Ty, Visibility); -impl_has_tokens_none!(Arm, ExprField, FieldDef, GenericParam, Param, PatField, Variant); +impl_has_tokens_none!( + Arm, + ExprField, + FieldDef, + GenericParam, + Param, + PatField, + Variant, + WherePredicate +); -impl> HasTokens for T { +impl HasTokens for Option { fn tokens(&self) -> Option<&LazyAttrTokenStream> { - self.ast_deref().tokens() + self.as_ref().and_then(|inner| inner.tokens()) } fn tokens_mut(&mut self) -> Option<&mut Option> { - self.ast_deref_mut().tokens_mut() + self.as_mut().and_then(|inner| inner.tokens_mut()) } } -impl HasTokens for Option { +impl HasTokens for P { fn tokens(&self) -> Option<&LazyAttrTokenStream> { - self.as_ref().and_then(|inner| inner.tokens()) + (**self).tokens() } fn tokens_mut(&mut self) -> Option<&mut Option> { - self.as_mut().and_then(|inner| inner.tokens_mut()) + (**self).tokens_mut() } } @@ -196,25 +177,6 @@ impl HasTokens for Attribute { } } -impl HasTokens for Nonterminal { - fn tokens(&self) -> Option<&LazyAttrTokenStream> { - match self { - Nonterminal::NtItem(item) => item.tokens(), - Nonterminal::NtStmt(stmt) => stmt.tokens(), - Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens(), - Nonterminal::NtBlock(block) => block.tokens(), - } - } - fn tokens_mut(&mut self) -> Option<&mut Option> { - match self { - Nonterminal::NtItem(item) => item.tokens_mut(), - Nonterminal::NtStmt(stmt) => stmt.tokens_mut(), - Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(), - Nonterminal::NtBlock(block) => block.tokens_mut(), - } - } -} - /// A trait for AST nodes having (or not having) attributes. pub trait HasAttrs { /// This is `true` if this `HasAttrs` might support 'custom' (proc-macro) inner @@ -279,16 +241,17 @@ impl_has_attrs!( Param, PatField, Variant, + WherePredicate, ); impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility); -impl> HasAttrs for T { - const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::Target::SUPPORTS_CUSTOM_INNER_ATTRS; +impl HasAttrs for P { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; fn attrs(&self) -> &[Attribute] { - self.ast_deref().attrs() + (**self).attrs() } fn visit_attrs(&mut self, f: impl FnOnce(&mut AttrVec)) { - self.ast_deref_mut().visit_attrs(f) + (**self).visit_attrs(f); } } @@ -352,13 +315,22 @@ impl AstNodeWrapper { } } -impl AstDeref for AstNodeWrapper { - type Target = Wrapped; - fn ast_deref(&self) -> &Self::Target { - &self.wrapped +impl HasNodeId for AstNodeWrapper { + fn node_id(&self) -> NodeId { + self.wrapped.node_id() + } + fn node_id_mut(&mut self) -> &mut NodeId { + self.wrapped.node_id_mut() } - fn ast_deref_mut(&mut self) -> &mut Self::Target { - &mut self.wrapped +} + +impl HasAttrs for AstNodeWrapper { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = Wrapped::SUPPORTS_CUSTOM_INNER_ATTRS; + fn attrs(&self) -> &[Attribute] { + self.wrapped.attrs() + } + fn visit_attrs(&mut self, f: impl FnOnce(&mut AttrVec)) { + self.wrapped.visit_attrs(f); } } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 4d613085d793e..f165c4ddcdd4d 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -305,8 +305,8 @@ impl MetaItem { if let [PathSegment { ident, .. }] = self.path.segments[..] { Some(ident) } else { None } } - pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or_else(Ident::empty).name + pub fn name(&self) -> Option { + self.ident().map(|ident| ident.name) } pub fn has_name(&self, name: Symbol) -> bool { @@ -416,10 +416,7 @@ impl MetaItem { // This path is currently unreachable in the test suite. unreachable!() } - Some(TokenTree::Token( - Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. }, - _, - )) => { + Some(TokenTree::Token(Token { kind, .. }, _)) if kind.is_delim() => { panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tt); } _ => return None, @@ -511,13 +508,14 @@ impl MetaItemInner { } } - /// For a single-segment meta item, returns its name; otherwise, returns `None`. + /// For a single-segment meta item, returns its identifier; 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_else(Ident::empty).name + /// For a single-segment meta item, returns its name; otherwise, returns `None`. + pub fn name(&self) -> Option { + self.ident().map(|ident| ident.name) } /// Returns `true` if this list item is a MetaItem with a name of `name`. @@ -570,6 +568,14 @@ impl MetaItemInner { } } + /// Returns the bool if `self` is a boolean `MetaItemInner::Literal`. + pub fn boolean_literal(&self) -> Option { + match self { + MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => Some(*b), + _ => None, + } + } + /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem` or if it's /// `MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. })`. pub fn meta_item_or_bool(&self) -> Option<&MetaItemInner> { @@ -619,7 +625,7 @@ pub fn mk_doc_comment( Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span } } -pub fn mk_attr( +fn mk_attr( g: &AttrIdGenerator, style: AttrStyle, unsafety: Safety, @@ -730,9 +736,9 @@ pub trait AttributeExt: Debug { fn id(&self) -> AttrId; /// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`), - /// return the name of the attribute, else return the empty identifier. - fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or_else(Ident::empty).name + /// return the name of the attribute; otherwise, returns `None`. + fn name(&self) -> Option { + self.ident().map(|ident| ident.name) } /// Get the meta item list, `#[attr(meta item list)]` @@ -744,7 +750,7 @@ pub trait AttributeExt: Debug { /// Gets the span of the value literal, as string, when using `#[attr = value]` fn value_span(&self) -> Option; - /// For a single-segment attribute, returns its name; otherwise, returns `None`. + /// For a single-segment attribute, returns its ident; otherwise, returns `None`. fn ident(&self) -> Option; /// Checks whether the path of this attribute matches the name. @@ -762,6 +768,11 @@ pub trait AttributeExt: Debug { self.ident().map(|x| x.name == name).unwrap_or(false) } + #[inline] + fn has_any_name(&self, names: &[Symbol]) -> bool { + names.iter().any(|&name| self.has_name(name)) + } + /// get the span of the entire attribute fn span(&self) -> Span; @@ -805,8 +816,8 @@ impl Attribute { AttributeExt::id(self) } - pub fn name_or_empty(&self) -> Symbol { - AttributeExt::name_or_empty(self) + pub fn name(&self) -> Option { + AttributeExt::name(self) } pub fn meta_item_list(&self) -> Option> { @@ -838,6 +849,11 @@ impl Attribute { AttributeExt::has_name(self, name) } + #[inline] + pub fn has_any_name(&self, names: &[Symbol]) -> bool { + AttributeExt::has_any_name(self, names) + } + pub fn span(&self) -> Span { AttributeExt::span(self) } diff --git a/compiler/rustc_ast/src/expand/autodiff_attrs.rs b/compiler/rustc_ast/src/expand/autodiff_attrs.rs index c8ec185ee5e29..2f918faaf752b 100644 --- a/compiler/rustc_ast/src/expand/autodiff_attrs.rs +++ b/compiler/rustc_ast/src/expand/autodiff_attrs.rs @@ -50,8 +50,16 @@ pub enum DiffActivity { /// with it. Dual, /// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument + /// with it. It expects the shadow argument to be `width` times larger than the original + /// input/output. + Dualv, + /// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument /// with it. Drop the code which updates the original input/output for maximum performance. DualOnly, + /// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument + /// with it. Drop the code which updates the original input/output for maximum performance. + /// It expects the shadow argument to be `width` times larger than the original input/output. + DualvOnly, /// Reverse Mode, Compute derivatives for this &T or *T input and *add* it to the shadow argument. Duplicated, /// Reverse Mode, Compute derivatives for this &T or *T input and *add* it to the shadow argument. @@ -59,7 +67,15 @@ pub enum DiffActivity { DuplicatedOnly, /// All Integers must be Const, but these are used to mark the integer which represents the /// length of a slice/vec. This is used for safety checks on slices. - FakeActivitySize, + /// The integer (if given) specifies the size of the slice element in bytes. + FakeActivitySize(Option), +} + +impl DiffActivity { + pub fn is_dual_or_const(&self) -> bool { + use DiffActivity::*; + matches!(self, |Dual| DualOnly | Dualv | DualvOnly | Const) + } } /// We generate one of these structs for each `#[autodiff(...)]` attribute. #[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] @@ -77,10 +93,27 @@ pub struct AutoDiffAttrs { /// e.g. in the [JAX /// Documentation](https://jax.readthedocs.io/en/latest/_tutorials/advanced-autodiff.html#how-it-s-made-two-foundational-autodiff-functions). pub mode: DiffMode, + /// A user-provided, batching width. If not given, we will default to 1 (no batching). + /// Calling a differentiated, non-batched function through a loop 100 times is equivalent to: + /// - Calling the function 50 times with a batch size of 2 + /// - Calling the function 25 times with a batch size of 4, + /// etc. A batched function takes more (or longer) arguments, and might be able to benefit from + /// cache locality, better re-usal of primal values, and other optimizations. + /// We will (before LLVM's vectorizer runs) just generate most LLVM-IR instructions `width` + /// times, so this massively increases code size. As such, values like 1024 are unlikely to + /// work. We should consider limiting this to u8 or u16, but will leave it at u32 for + /// experiments for now and focus on documenting the implications of a large width. + pub width: u32, pub ret_activity: DiffActivity, pub input_activity: Vec, } +impl AutoDiffAttrs { + pub fn has_primal_ret(&self) -> bool { + matches!(self.ret_activity, DiffActivity::Active | DiffActivity::Dual) + } +} + impl DiffMode { pub fn is_rev(&self) -> bool { matches!(self, DiffMode::Reverse) @@ -114,11 +147,7 @@ pub fn valid_ret_activity(mode: DiffMode, activity: DiffActivity) -> bool { match mode { DiffMode::Error => false, DiffMode::Source => false, - DiffMode::Forward => { - activity == DiffActivity::Dual - || activity == DiffActivity::DualOnly - || activity == DiffActivity::Const - } + DiffMode::Forward => activity.is_dual_or_const(), DiffMode::Reverse => { activity == DiffActivity::Const || activity == DiffActivity::Active @@ -136,10 +165,8 @@ pub fn valid_ret_activity(mode: DiffMode, activity: DiffActivity) -> bool { pub fn valid_ty_for_activity(ty: &P, activity: DiffActivity) -> bool { use DiffActivity::*; // It's always allowed to mark something as Const, since we won't compute derivatives wrt. it. - if matches!(activity, Const) { - return true; - } - if matches!(activity, Dual | DualOnly) { + // Dual variants also support all types. + if activity.is_dual_or_const() { return true; } // FIXME(ZuseZ4) We should make this more robust to also @@ -155,9 +182,7 @@ pub fn valid_input_activity(mode: DiffMode, activity: DiffActivity) -> bool { return match mode { DiffMode::Error => false, DiffMode::Source => false, - DiffMode::Forward => { - matches!(activity, Dual | DualOnly | Const) - } + DiffMode::Forward => activity.is_dual_or_const(), DiffMode::Reverse => { matches!(activity, Active | ActiveOnly | Duplicated | DuplicatedOnly | Const) } @@ -172,10 +197,12 @@ impl Display for DiffActivity { DiffActivity::Active => write!(f, "Active"), DiffActivity::ActiveOnly => write!(f, "ActiveOnly"), DiffActivity::Dual => write!(f, "Dual"), + DiffActivity::Dualv => write!(f, "Dualv"), DiffActivity::DualOnly => write!(f, "DualOnly"), + DiffActivity::DualvOnly => write!(f, "DualvOnly"), DiffActivity::Duplicated => write!(f, "Duplicated"), DiffActivity::DuplicatedOnly => write!(f, "DuplicatedOnly"), - DiffActivity::FakeActivitySize => write!(f, "FakeActivitySize"), + DiffActivity::FakeActivitySize(s) => write!(f, "FakeActivitySize({:?})", s), } } } @@ -203,7 +230,9 @@ impl FromStr for DiffActivity { "ActiveOnly" => Ok(DiffActivity::ActiveOnly), "Const" => Ok(DiffActivity::Const), "Dual" => Ok(DiffActivity::Dual), + "Dualv" => Ok(DiffActivity::Dualv), "DualOnly" => Ok(DiffActivity::DualOnly), + "DualvOnly" => Ok(DiffActivity::DualvOnly), "Duplicated" => Ok(DiffActivity::Duplicated), "DuplicatedOnly" => Ok(DiffActivity::DuplicatedOnly), _ => Err(()), @@ -222,6 +251,7 @@ impl AutoDiffAttrs { pub const fn error() -> Self { AutoDiffAttrs { mode: DiffMode::Error, + width: 0, ret_activity: DiffActivity::None, input_activity: Vec::new(), } @@ -229,6 +259,7 @@ impl AutoDiffAttrs { pub fn source() -> Self { AutoDiffAttrs { mode: DiffMode::Source, + width: 0, ret_activity: DiffActivity::None, input_activity: Vec::new(), } diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs index 04c8162932369..323a8fab6d592 100644 --- a/compiler/rustc_ast/src/expand/mod.rs +++ b/compiler/rustc_ast/src/expand/mod.rs @@ -13,12 +13,12 @@ pub mod typetree; #[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)] pub struct StrippedCfgItem { pub parent_module: ModId, - pub name: Ident, + pub ident: Ident, pub cfg: MetaItem, } impl StrippedCfgItem { pub fn map_mod_id(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem { - StrippedCfgItem { parent_module: f(self.parent_module), name: self.name, cfg: self.cfg } + StrippedCfgItem { parent_module: f(self.parent_module), ident: self.ident, cfg: self.cfg } } } diff --git a/compiler/rustc_ast/src/format.rs b/compiler/rustc_ast/src/format.rs index b93846c1fe6f3..b611ddea1d9f1 100644 --- a/compiler/rustc_ast/src/format.rs +++ b/compiler/rustc_ast/src/format.rs @@ -266,7 +266,7 @@ pub enum FormatAlignment { #[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)] pub enum FormatCount { /// `{:5}` or `{:.5}` - Literal(usize), + Literal(u16), /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc. Argument(FormatArgPosition), } diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 6372c66050e7c..4fc7c7475d757 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -11,15 +11,16 @@ test(attr(deny(warnings))) )] #![doc(rust_logo)] +#![feature(array_windows)] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(if_let_guard)] -#![feature(let_chains)] +#![feature(macro_metavar_expr)] #![feature(negative_impls)] #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(stmt_expr_attributes)] -#![warn(unreachable_pub)] +#![recursion_limit = "256"] // tidy-alphabetical-end pub mod util { @@ -45,7 +46,7 @@ pub mod tokenstream; pub mod visit; pub use self::ast::*; -pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasTokens}; +pub use self::ast_traits::{AstNodeWrapper, HasAttrs, HasNodeId, HasTokens}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 9be364e320b78..a90349f318c09 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -9,7 +9,6 @@ use std::ops::DerefMut; use std::panic; -use std::sync::Arc; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -20,9 +19,8 @@ use thin_vec::ThinVec; use crate::ast::*; use crate::ptr::P; -use crate::token::{self, Token}; use crate::tokenstream::*; -use crate::visit::{AssocCtxt, BoundKind, FnCtxt}; +use crate::visit::{AssocCtxt, BoundKind, FnCtxt, try_visit}; pub trait ExpectOne { fn expect_one(self, err: &'static str) -> A::Item; @@ -41,7 +39,6 @@ pub trait WalkItemKind { &mut self, span: Span, id: NodeId, - ident: &mut Ident, visibility: &mut Visibility, ctxt: Self::Ctxt, visitor: &mut impl MutVisitor, @@ -49,11 +46,6 @@ pub trait WalkItemKind { } pub trait MutVisitor: Sized { - /// Mutable token visiting only exists for the `macro_rules` token marker and should not be - /// used otherwise. Token visitor would be entirely separate from the regular visitor if - /// the marker didn't have to visit AST fragments in nonterminal tokens. - const VISIT_TOKENS: bool = false; - // Methods in this trait have one of three forms: // // fn visit_t(&mut self, t: &mut T); // common @@ -238,6 +230,10 @@ pub trait MutVisitor: Sized { walk_ident(self, i); } + fn visit_modifiers(&mut self, m: &mut TraitBoundModifiers) { + walk_modifiers(self, m); + } + fn visit_path(&mut self, p: &mut Path) { walk_path(self, p); } @@ -338,8 +334,11 @@ pub trait MutVisitor: Sized { walk_where_clause(self, where_clause); } - fn visit_where_predicate(&mut self, where_predicate: &mut WherePredicate) { - walk_where_predicate(self, where_predicate) + fn flat_map_where_predicate( + &mut self, + where_predicate: WherePredicate, + ) -> SmallVec<[WherePredicate; 1]> { + walk_flat_map_where_predicate(self, where_predicate) } fn visit_where_predicate_kind(&mut self, kind: &mut WherePredicateKind) { @@ -354,6 +353,8 @@ pub trait MutVisitor: Sized { // Do nothing. } + // Span visiting is no longer used, but we keep it for now, + // in case it's needed for something like #127241. fn visit_span(&mut self, _sp: &mut Span) { // Do nothing. } @@ -387,6 +388,8 @@ pub trait MutVisitor: Sized { } } +super::common_visitor_and_walkers!((mut) MutVisitor); + /// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful /// when using a `flat_map_*` or `filter_map_*` method within a `visit_` /// method. @@ -467,12 +470,8 @@ fn visit_attr_args(vis: &mut T, args: &mut AttrArgs) { // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. fn visit_delim_args(vis: &mut T, args: &mut DelimArgs) { - let DelimArgs { dspan, delim: _, tokens } = args; - visit_tts(vis, tokens); - visit_delim_span(vis, dspan); -} - -pub fn visit_delim_span(vis: &mut T, DelimSpan { open, close }: &mut DelimSpan) { + let DelimArgs { dspan, delim: _, tokens: _ } = args; + let DelimSpan { open, close } = dspan; vis.visit_span(open); vis.visit_span(close); } @@ -546,7 +545,7 @@ fn walk_assoc_item_constraint( } pub fn walk_ty(vis: &mut T, ty: &mut P) { - let Ty { id, kind, span, tokens } = ty.deref_mut(); + let Ty { id, kind, span, tokens: _ } = ty.deref_mut(); vis.visit_id(id); match kind { TyKind::Err(_guar) => {} @@ -594,21 +593,20 @@ pub fn walk_ty(vis: &mut T, ty: &mut P) { } TyKind::MacCall(mac) => vis.visit_mac_call(mac), } - visit_lazy_tts(vis, tokens); vis.visit_span(span); } pub fn walk_ty_pat(vis: &mut T, ty: &mut P) { - let TyPat { id, kind, span, tokens } = ty.deref_mut(); + let TyPat { id, kind, span, tokens: _ } = ty.deref_mut(); vis.visit_id(id); match kind { TyPatKind::Range(start, end, _include_end) => { visit_opt(start, |c| vis.visit_anon_const(c)); visit_opt(end, |c| vis.visit_anon_const(c)); } + TyPatKind::Or(variants) => visit_thin_vec(variants, |p| vis.visit_ty_pat(p)), TyPatKind::Err(_) => {} } - visit_lazy_tts(vis, tokens); vis.visit_span(span); } @@ -648,11 +646,10 @@ fn walk_path_segment(vis: &mut T, segment: &mut PathSegment) { visit_opt(args, |args| vis.visit_generic_args(args)); } -fn walk_path(vis: &mut T, Path { segments, span, tokens }: &mut Path) { +fn walk_path(vis: &mut T, Path { segments, span, tokens: _ }: &mut Path) { for segment in segments { vis.visit_path_segment(segment); } - visit_lazy_tts(vis, tokens); vis.visit_span(span); } @@ -698,7 +695,8 @@ fn walk_parenthesized_parameter_data(vis: &mut T, args: &mut Pare } fn walk_local(vis: &mut T, local: &mut P) { - let Local { id, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut(); + let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens: _ } = local.deref_mut(); + visit_opt(super_, |sp| vis.visit_span(sp)); vis.visit_id(id); visit_attrs(vis, attrs); vis.visit_pat(pat); @@ -713,7 +711,6 @@ fn walk_local(vis: &mut T, local: &mut P) { vis.visit_block(els); } } - visit_lazy_tts(vis, tokens); visit_opt(colon_sp, |sp| vis.visit_span(sp)); vis.visit_span(span); } @@ -722,14 +719,10 @@ fn walk_attribute(vis: &mut T, attr: &mut Attribute) { let Attribute { kind, id: _, style: _, span } = attr; match kind { AttrKind::Normal(normal) => { - let NormalAttr { - item: AttrItem { unsafety: _, path, args, tokens }, - tokens: attr_tokens, - } = &mut **normal; + let NormalAttr { item: AttrItem { unsafety: _, path, args, tokens: _ }, tokens: _ } = + &mut **normal; vis.visit_path(path); visit_attr_args(vis, args); - visit_lazy_tts(vis, tokens); - visit_lazy_tts(vis, attr_tokens); } AttrKind::DocComment(_kind, _sym) => {} } @@ -778,138 +771,6 @@ pub fn walk_flat_map_param(vis: &mut T, mut param: Param) -> Smal smallvec![param] } -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -fn visit_attr_tt(vis: &mut T, tt: &mut AttrTokenTree) { - match tt { - AttrTokenTree::Token(token, _spacing) => { - visit_token(vis, token); - } - AttrTokenTree::Delimited(dspan, _spacing, _delim, tts) => { - visit_attr_tts(vis, tts); - visit_delim_span(vis, dspan); - } - AttrTokenTree::AttrsTarget(AttrsTarget { attrs, tokens }) => { - visit_attrs(vis, attrs); - visit_lazy_tts_opt_mut(vis, Some(tokens)); - } - } -} - -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -fn visit_tt(vis: &mut T, tt: &mut TokenTree) { - match tt { - TokenTree::Token(token, _spacing) => { - visit_token(vis, token); - } - TokenTree::Delimited(dspan, _spacing, _delim, tts) => { - visit_tts(vis, tts); - visit_delim_span(vis, dspan); - } - } -} - -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -fn visit_tts(vis: &mut T, TokenStream(tts): &mut TokenStream) { - if T::VISIT_TOKENS && !tts.is_empty() { - let tts = Arc::make_mut(tts); - visit_vec(tts, |tree| visit_tt(vis, tree)); - } -} - -fn visit_attr_tts(vis: &mut T, AttrTokenStream(tts): &mut AttrTokenStream) { - if T::VISIT_TOKENS && !tts.is_empty() { - let tts = Arc::make_mut(tts); - visit_vec(tts, |tree| visit_attr_tt(vis, tree)); - } -} - -fn visit_lazy_tts_opt_mut(vis: &mut T, lazy_tts: Option<&mut LazyAttrTokenStream>) { - if T::VISIT_TOKENS { - if let Some(lazy_tts) = lazy_tts { - let mut tts = lazy_tts.to_attr_token_stream(); - visit_attr_tts(vis, &mut tts); - *lazy_tts = LazyAttrTokenStream::new(tts); - } - } -} - -fn visit_lazy_tts(vis: &mut T, lazy_tts: &mut Option) { - visit_lazy_tts_opt_mut(vis, lazy_tts.as_mut()); -} - -/// Applies ident visitor if it's an ident; applies other visits to interpolated nodes. -/// In practice the ident part is not actually used by specific visitors right now, -/// but there's a test below checking that it works. -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -pub fn visit_token(vis: &mut T, t: &mut Token) { - let Token { kind, span } = t; - match kind { - token::Ident(name, _is_raw) | token::Lifetime(name, _is_raw) => { - let mut ident = Ident::new(*name, *span); - vis.visit_ident(&mut ident); - *name = ident.name; - *span = ident.span; - return; // Avoid visiting the span for the second time. - } - token::NtIdent(ident, _is_raw) => { - vis.visit_ident(ident); - } - token::NtLifetime(ident, _is_raw) => { - vis.visit_ident(ident); - } - token::Interpolated(nt) => { - let nt = Arc::make_mut(nt); - visit_nonterminal(vis, nt); - } - _ => {} - } - vis.visit_span(span); -} - -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -/// Applies the visitor to elements of interpolated nodes. -// -// N.B., this can occur only when applying a visitor to partially expanded -// code, where parsed pieces have gotten implanted ito *other* macro -// invocations. This is relevant for macro hygiene, but possibly not elsewhere. -// -// One problem here occurs because the types for flat_map_item, flat_map_stmt, -// etc., allow the visitor to return *multiple* items; this is a problem for the -// nodes here, because they insist on having exactly one piece. One solution -// would be to mangle the MutVisitor trait to include one-to-many and -// one-to-one versions of these entry points, but that would probably confuse a -// lot of people and help very few. Instead, I'm just going to put in dynamic -// checks. I think the performance impact of this will be pretty much -// nonexistent. The danger is that someone will apply a `MutVisitor` to a -// partially expanded node, and will be confused by the fact that their -// `flat_map_item` or `flat_map_stmt` isn't getting called on `NtItem` or `NtStmt` -// nodes. Hopefully they'll wind up reading this comment, and doing something -// appropriate. -// -// BTW, design choice: I considered just changing the type of, e.g., `NtItem` to -// contain multiple items, but decided against it when I looked at -// `parse_item_or_view_item` and tried to figure out what I would do with -// multiple items there.... -fn visit_nonterminal(vis: &mut T, nt: &mut token::Nonterminal) { - match nt { - token::NtItem(item) => visit_clobber(item, |item| { - // This is probably okay, because the only visitors likely to - // peek inside interpolated nodes will be renamings/markings, - // which map single items to single items. - vis.flat_map_item(item).expect_one("expected visitor to produce exactly one item") - }), - token::NtBlock(block) => vis.visit_block(block), - token::NtStmt(stmt) => visit_clobber(stmt, |stmt| { - // See reasoning above. - stmt.map(|stmt| { - vis.flat_map_stmt(stmt).expect_one("expected visitor to produce exactly one item") - }) - }), - token::NtExpr(expr) => vis.visit_expr(expr), - token::NtLiteral(expr) => vis.visit_expr(expr), - } -} - // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. fn visit_defaultness(vis: &mut T, defaultness: &mut Defaultness) { match defaultness { @@ -918,15 +779,6 @@ fn visit_defaultness(vis: &mut T, defaultness: &mut Defaultness) } } -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -fn visit_safety(vis: &mut T, safety: &mut Safety) { - match safety { - Safety::Unsafe(span) => vis.visit_span(span), - Safety::Safe(span) => vis.visit_span(span), - Safety::Default => {} - } -} - // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. fn visit_polarity(vis: &mut T, polarity: &mut ImplPolarity) { match polarity { @@ -935,14 +787,6 @@ fn visit_polarity(vis: &mut T, polarity: &mut ImplPolarity) { } } -// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -fn visit_constness(vis: &mut T, constness: &mut Const) { - match constness { - Const::Yes(span) => vis.visit_span(span), - Const::No => {} - } -} - fn walk_closure_binder(vis: &mut T, binder: &mut ClosureBinder) { match binder { ClosureBinder::NotPresent => {} @@ -968,12 +812,20 @@ fn walk_fn(vis: &mut T, kind: FnKind<'_>) { match kind { FnKind::Fn( _ctxt, - _ident, _vis, - Fn { defaultness, generics, contract, body, sig: FnSig { header, decl, span } }, + Fn { + defaultness, + ident, + generics, + contract, + body, + sig: FnSig { header, decl, span }, + define_opaque, + }, ) => { - // Identifier and visibility are visited as a part of the item. + // Visibility is visited as a part of the item. visit_defaultness(vis, defaultness); + vis.visit_ident(ident); vis.visit_fn_header(header); vis.visit_generics(generics); vis.visit_fn_decl(decl); @@ -984,6 +836,8 @@ fn walk_fn(vis: &mut T, kind: FnKind<'_>) { vis.visit_block(body); } vis.visit_span(span); + + walk_define_opaques(vis, define_opaque); } FnKind::Closure(binder, coroutine_kind, decl, body) => { vis.visit_closure_binder(binder); @@ -1071,15 +925,6 @@ pub fn walk_flat_map_generic_param( smallvec![param] } -fn walk_label(vis: &mut T, Label { ident }: &mut Label) { - vis.visit_ident(ident); -} - -fn walk_lifetime(vis: &mut T, Lifetime { id, ident }: &mut Lifetime) { - vis.visit_id(id); - vis.visit_ident(ident); -} - fn walk_generics(vis: &mut T, generics: &mut Generics) { let Generics { params, where_clause, span } = generics; params.flat_map_in_place(|param| vis.flat_map_generic_param(param)); @@ -1097,15 +942,20 @@ fn walk_ty_alias_where_clauses(vis: &mut T, tawcs: &mut TyAliasWh fn walk_where_clause(vis: &mut T, wc: &mut WhereClause) { let WhereClause { has_where_token: _, predicates, span } = wc; - visit_thin_vec(predicates, |predicate| vis.visit_where_predicate(predicate)); + predicates.flat_map_in_place(|predicate| vis.flat_map_where_predicate(predicate)); vis.visit_span(span); } -pub fn walk_where_predicate(vis: &mut T, pred: &mut WherePredicate) { - let WherePredicate { kind, id, span } = pred; +pub fn walk_flat_map_where_predicate( + vis: &mut T, + mut pred: WherePredicate, +) -> SmallVec<[WherePredicate; 1]> { + let WherePredicate { attrs, kind, id, span, is_placeholder: _ } = &mut pred; vis.visit_id(id); + visit_attrs(vis, attrs); vis.visit_where_predicate_kind(kind); vis.visit_span(span); + smallvec![pred] } pub fn walk_where_predicate_kind(vis: &mut T, kind: &mut WherePredicateKind) { @@ -1148,12 +998,29 @@ fn walk_trait_ref(vis: &mut T, TraitRef { path, ref_id }: &mut Tr } fn walk_poly_trait_ref(vis: &mut T, p: &mut PolyTraitRef) { - let PolyTraitRef { bound_generic_params, modifiers: _, trait_ref, span } = p; + let PolyTraitRef { bound_generic_params, modifiers, trait_ref, span } = p; + vis.visit_modifiers(modifiers); bound_generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param)); vis.visit_trait_ref(trait_ref); vis.visit_span(span); } +fn walk_modifiers(vis: &mut V, m: &mut TraitBoundModifiers) { + let TraitBoundModifiers { constness, asyncness, polarity } = m; + match constness { + BoundConstness::Never => {} + BoundConstness::Always(span) | BoundConstness::Maybe(span) => vis.visit_span(span), + } + match asyncness { + BoundAsyncness::Normal => {} + BoundAsyncness::Async(span) => vis.visit_span(span), + } + match polarity { + BoundPolarity::Positive => {} + BoundPolarity::Negative(span) | BoundPolarity::Maybe(span) => vis.visit_span(span), + } +} + pub fn walk_field_def(visitor: &mut T, fd: &mut FieldDef) { let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety, default } = fd; visitor.visit_id(id); @@ -1196,10 +1063,9 @@ fn walk_mt(vis: &mut T, MutTy { ty, mutbl: _ }: &mut MutTy) { } pub fn walk_block(vis: &mut T, block: &mut P) { - let Block { id, stmts, rules: _, span, tokens, could_be_bare_literal: _ } = 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)); - visit_lazy_tts(vis, tokens); vis.visit_span(span); } @@ -1207,12 +1073,11 @@ pub fn walk_item_kind( kind: &mut K, span: Span, id: NodeId, - ident: &mut Ident, visibility: &mut Visibility, ctxt: K::Ctxt, vis: &mut impl MutVisitor, ) { - kind.walk(span, id, ident, visibility, ctxt, vis) + kind.walk(span, id, visibility, ctxt, vis) } impl WalkItemKind for ItemKind { @@ -1221,26 +1086,35 @@ impl WalkItemKind for ItemKind { &mut self, span: Span, id: NodeId, - ident: &mut Ident, visibility: &mut Visibility, _ctxt: Self::Ctxt, vis: &mut impl MutVisitor, ) { match self { - ItemKind::ExternCrate(_orig_name) => {} + ItemKind::ExternCrate(_orig_name, ident) => vis.visit_ident(ident), ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree), - ItemKind::Static(box StaticItem { ty, safety: _, mutability: _, expr }) => { + ItemKind::Static(box StaticItem { + ident, + ty, + safety: _, + mutability: _, + expr, + define_opaque, + }) => { + vis.visit_ident(ident); vis.visit_ty(ty); visit_opt(expr, |expr| vis.visit_expr(expr)); + walk_define_opaques(vis, define_opaque); } ItemKind::Const(item) => { - visit_const_item(item, vis); + walk_const_item(vis, item); } ItemKind::Fn(func) => { - vis.visit_fn(FnKind::Fn(FnCtxt::Free, ident, visibility, &mut *func), span, id); + vis.visit_fn(FnKind::Fn(FnCtxt::Free, visibility, &mut *func), span, id); } - ItemKind::Mod(safety, mod_kind) => { + ItemKind::Mod(safety, ident, mod_kind) => { visit_safety(vis, safety); + vis.visit_ident(ident); match mod_kind { ModKind::Loaded( items, @@ -1257,18 +1131,29 @@ impl WalkItemKind for ItemKind { } ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm), ItemKind::GlobalAsm(asm) => vis.visit_inline_asm(asm), - ItemKind::TyAlias(box TyAlias { defaultness, generics, where_clauses, bounds, ty }) => { + ItemKind::TyAlias(box TyAlias { + defaultness, + ident, + generics, + where_clauses, + bounds, + ty, + }) => { visit_defaultness(vis, defaultness); + vis.visit_ident(ident); vis.visit_generics(generics); visit_bounds(vis, bounds, BoundKind::Bound); visit_opt(ty, |ty| vis.visit_ty(ty)); walk_ty_alias_where_clauses(vis, where_clauses); } - ItemKind::Enum(EnumDef { variants }, generics) => { + ItemKind::Enum(ident, EnumDef { variants }, generics) => { + vis.visit_ident(ident); vis.visit_generics(generics); variants.flat_map_in_place(|variant| vis.flat_map_variant(variant)); } - ItemKind::Struct(variant_data, generics) | ItemKind::Union(variant_data, generics) => { + ItemKind::Struct(ident, variant_data, generics) + | ItemKind::Union(ident, variant_data, generics) => { + vis.visit_ident(ident); vis.visit_generics(generics); vis.visit_variant_data(variant_data); } @@ -1289,24 +1174,32 @@ impl WalkItemKind for ItemKind { visit_polarity(vis, polarity); visit_opt(of_trait, |trait_ref| vis.visit_trait_ref(trait_ref)); vis.visit_ty(self_ty); - items.flat_map_in_place(|item| vis.flat_map_assoc_item(item, AssocCtxt::Impl)); + items.flat_map_in_place(|item| { + vis.flat_map_assoc_item(item, AssocCtxt::Impl { of_trait: of_trait.is_some() }) + }); } - ItemKind::Trait(box Trait { safety, is_auto: _, generics, bounds, items }) => { + ItemKind::Trait(box Trait { safety, is_auto: _, ident, generics, bounds, items }) => { visit_safety(vis, safety); + vis.visit_ident(ident); vis.visit_generics(generics); visit_bounds(vis, bounds, BoundKind::Bound); items.flat_map_in_place(|item| vis.flat_map_assoc_item(item, AssocCtxt::Trait)); } - ItemKind::TraitAlias(generics, bounds) => { + ItemKind::TraitAlias(ident, generics, bounds) => { + vis.visit_ident(ident); vis.visit_generics(generics); visit_bounds(vis, bounds, BoundKind::Bound); } ItemKind::MacCall(m) => vis.visit_mac_call(m), - ItemKind::MacroDef(def) => vis.visit_macro_def(def), + ItemKind::MacroDef(ident, def) => { + vis.visit_ident(ident); + vis.visit_macro_def(def) + } ItemKind::Delegation(box Delegation { id, qself, path, + ident, rename, body, from_glob: _, @@ -1314,6 +1207,7 @@ impl WalkItemKind for ItemKind { vis.visit_id(id); vis.visit_qself(qself); vis.visit_path(path); + vis.visit_ident(ident); if let Some(rename) = rename { vis.visit_ident(rename); } @@ -1346,30 +1240,27 @@ impl WalkItemKind for AssocItemKind { &mut self, span: Span, id: NodeId, - ident: &mut Ident, visibility: &mut Visibility, ctxt: Self::Ctxt, visitor: &mut impl MutVisitor, ) { match self { AssocItemKind::Const(item) => { - visit_const_item(item, visitor); + walk_const_item(visitor, item); } AssocItemKind::Fn(func) => { - visitor.visit_fn( - FnKind::Fn(FnCtxt::Assoc(ctxt), ident, visibility, &mut *func), - span, - id, - ); + visitor.visit_fn(FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &mut *func), span, id); } AssocItemKind::Type(box TyAlias { defaultness, + ident, generics, where_clauses, bounds, ty, }) => { visit_defaultness(visitor, defaultness); + visitor.visit_ident(ident); visitor.visit_generics(generics); visit_bounds(visitor, bounds, BoundKind::Bound); visit_opt(ty, |ty| visitor.visit_ty(ty)); @@ -1380,6 +1271,7 @@ impl WalkItemKind for AssocItemKind { id, qself, path, + ident, rename, body, from_glob: _, @@ -1387,6 +1279,7 @@ impl WalkItemKind for AssocItemKind { visitor.visit_id(id); visitor.visit_qself(qself); visitor.visit_path(path); + visitor.visit_ident(ident); if let Some(rename) = rename { visitor.visit_ident(rename); } @@ -1413,21 +1306,14 @@ impl WalkItemKind for AssocItemKind { } } -fn visit_const_item( - ConstItem { defaultness, generics, ty, expr }: &mut ConstItem, - visitor: &mut T, -) { - visit_defaultness(visitor, defaultness); - visitor.visit_generics(generics); - visitor.visit_ty(ty); - visit_opt(expr, |expr| visitor.visit_expr(expr)); -} - -fn walk_fn_header(vis: &mut T, header: &mut FnHeader) { - let FnHeader { safety, coroutine_kind, constness, ext: _ } = header; - visit_constness(vis, constness); - coroutine_kind.as_mut().map(|coroutine_kind| vis.visit_coroutine_kind(coroutine_kind)); - visit_safety(vis, safety); +fn walk_const_item(vis: &mut T, item: &mut ConstItem) { + let ConstItem { defaultness, ident, generics, ty, expr, define_opaque } = item; + visit_defaultness(vis, defaultness); + vis.visit_ident(ident); + vis.visit_generics(generics); + vis.visit_ty(ty); + visit_opt(expr, |expr| vis.visit_expr(expr)); + walk_define_opaques(vis, define_opaque); } pub fn walk_crate(vis: &mut T, krate: &mut Crate) { @@ -1453,13 +1339,11 @@ fn walk_item_ctxt( item: &mut P>, ctxt: K::Ctxt, ) { - let Item { ident, attrs, id, kind, vis, span, tokens } = item.deref_mut(); + let Item { attrs, id, kind, vis, span, tokens: _ } = item.deref_mut(); visitor.visit_id(id); visit_attrs(visitor, attrs); visitor.visit_vis(vis); - visitor.visit_ident(ident); - kind.walk(*span, *id, ident, vis, ctxt, visitor); - visit_lazy_tts(visitor, tokens); + kind.walk(*span, *id, vis, ctxt, visitor); visitor.visit_span(span); } @@ -1491,31 +1375,37 @@ impl WalkItemKind for ForeignItemKind { &mut self, span: Span, id: NodeId, - ident: &mut Ident, visibility: &mut Visibility, _ctxt: Self::Ctxt, visitor: &mut impl MutVisitor, ) { match self { - ForeignItemKind::Static(box StaticItem { ty, mutability: _, expr, safety: _ }) => { + ForeignItemKind::Static(box StaticItem { + ident, + ty, + mutability: _, + expr, + safety: _, + define_opaque, + }) => { + visitor.visit_ident(ident); visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); + walk_define_opaques(visitor, define_opaque); } ForeignItemKind::Fn(func) => { - visitor.visit_fn( - FnKind::Fn(FnCtxt::Foreign, ident, visibility, &mut *func), - span, - id, - ); + visitor.visit_fn(FnKind::Fn(FnCtxt::Foreign, visibility, &mut *func), span, id); } ForeignItemKind::TyAlias(box TyAlias { defaultness, + ident, generics, where_clauses, bounds, ty, }) => { visit_defaultness(visitor, defaultness); + visitor.visit_ident(ident); visitor.visit_generics(generics); visit_bounds(visitor, bounds, BoundKind::Bound); visit_opt(ty, |ty| visitor.visit_ty(ty)); @@ -1527,11 +1417,11 @@ impl WalkItemKind for ForeignItemKind { } pub fn walk_pat(vis: &mut T, pat: &mut P) { - let Pat { id, kind, span, tokens } = pat.deref_mut(); + let Pat { id, kind, span, tokens: _ } = pat.deref_mut(); vis.visit_id(id); match kind { PatKind::Err(_guar) => {} - PatKind::Wild | PatKind::Rest | PatKind::Never => {} + PatKind::Missing | PatKind::Wild | PatKind::Rest | PatKind::Never => {} PatKind::Ident(_binding_mode, ident, sub) => { vis.visit_ident(ident); visit_opt(sub, |sub| vis.visit_pat(sub)); @@ -1569,7 +1459,6 @@ pub fn walk_pat(vis: &mut T, pat: &mut P) { PatKind::Paren(inner) => vis.visit_pat(inner), PatKind::MacCall(mac) => vis.visit_mac_call(mac), } - visit_lazy_tts(vis, tokens); vis.visit_span(span); } @@ -1633,7 +1522,7 @@ fn walk_format_args(vis: &mut T, fmt: &mut FormatArgs) { vis.visit_span(span); } -pub fn walk_expr(vis: &mut T, Expr { kind, id, span, attrs, tokens }: &mut Expr) { +pub fn walk_expr(vis: &mut T, Expr { kind, id, span, attrs, tokens: _ }: &mut Expr) { vis.visit_id(id); visit_attrs(vis, attrs); match kind { @@ -1737,6 +1626,10 @@ pub fn walk_expr(vis: &mut T, Expr { kind, id, span, attrs, token vis.visit_expr(expr); vis.visit_span(await_kw_span); } + ExprKind::Use(expr, use_kw_span) => { + vis.visit_expr(expr); + vis.visit_span(use_kw_span); + } ExprKind::Assign(el, er, span) => { vis.visit_expr(el); vis.visit_expr(er); @@ -1801,8 +1694,11 @@ pub fn walk_expr(vis: &mut T, Expr { kind, id, span, attrs, token ExprKind::Paren(expr) => { vis.visit_expr(expr); } - ExprKind::Yield(expr) => { - visit_opt(expr, |expr| vis.visit_expr(expr)); + ExprKind::Yield(kind) => { + let expr = kind.expr_mut(); + if let Some(expr) = expr { + vis.visit_expr(expr); + } } ExprKind::Try(expr) => vis.visit_expr(expr), ExprKind::TryBlock(body) => vis.visit_block(body), @@ -1817,7 +1713,6 @@ pub fn walk_expr(vis: &mut T, Expr { kind, id, span, attrs, token ExprKind::Err(_guar) => {} ExprKind::Dummy => {} } - visit_lazy_tts(vis, tokens); vis.visit_span(span); } @@ -1859,17 +1754,16 @@ fn walk_flat_map_stmt_kind(vis: &mut T, kind: StmtKind) -> SmallV StmtKind::Semi(expr) => vis.filter_map_expr(expr).into_iter().map(StmtKind::Semi).collect(), StmtKind::Empty => smallvec![StmtKind::Empty], StmtKind::MacCall(mut mac) => { - let MacCallStmt { mac: mac_, style: _, attrs, tokens } = mac.deref_mut(); + let MacCallStmt { mac: mac_, style: _, attrs, tokens: _ } = mac.deref_mut(); visit_attrs(vis, attrs); vis.visit_mac_call(mac_); - visit_lazy_tts(vis, tokens); smallvec![StmtKind::MacCall(mac)] } } } fn walk_vis(vis: &mut T, visibility: &mut Visibility) { - let Visibility { kind, span, tokens } = visibility; + let Visibility { kind, span, tokens: _ } = visibility; match kind { VisibilityKind::Public | VisibilityKind::Inherited => {} VisibilityKind::Restricted { path, id, shorthand: _ } => { @@ -1877,7 +1771,6 @@ fn walk_vis(vis: &mut T, visibility: &mut Visibility) { vis.visit_path(path); } } - visit_lazy_tts(vis, tokens); vis.visit_span(span); } @@ -1887,6 +1780,21 @@ fn walk_capture_by(vis: &mut T, capture_by: &mut CaptureBy) { CaptureBy::Value { move_kw } => { vis.visit_span(move_kw); } + CaptureBy::Use { use_kw } => { + vis.visit_span(use_kw); + } + } +} + +fn walk_define_opaques( + vis: &mut T, + define_opaque: &mut Option>, +) { + if let Some(define_opaque) = define_opaque { + for (id, path) in define_opaque { + vis.visit_id(id); + vis.visit_path(path) + } } } @@ -1921,8 +1829,7 @@ impl DummyAstNode for Item { span: Default::default(), tokens: Default::default(), }, - ident: Ident::empty(), - kind: ItemKind::ExternCrate(None), + kind: ItemKind::ExternCrate(None, Ident::dummy()), tokens: Default::default(), } } @@ -1989,7 +1896,7 @@ impl DummyAstNode for crate::ast_traits::AstNo #[derive(Debug)] pub enum FnKind<'a> { /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. - Fn(FnCtxt, &'a mut Ident, &'a mut Visibility, &'a mut Fn), + Fn(FnCtxt, &'a mut Visibility, &'a mut Fn), /// E.g., `|x, y| body`. Closure( diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 6cd0e15c557f4..54781e8235e2f 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -1,14 +1,10 @@ use std::borrow::Cow; use std::fmt; -use std::sync::Arc; -pub use BinOpToken::*; pub use LitKind::*; -pub use Nonterminal::*; pub use NtExprKind::*; pub use NtPatKind::*; pub use TokenKind::*; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::Edition; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym}; @@ -17,7 +13,6 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym}; use rustc_span::{Ident, Symbol}; use crate::ast; -use crate::ptr::P; use crate::util::case::Case; #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] @@ -26,21 +21,6 @@ pub enum CommentKind { Block, } -#[derive(Clone, PartialEq, Encodable, Decodable, Hash, Debug, Copy)] -#[derive(HashStable_Generic)] -pub enum BinOpToken { - Plus, - Minus, - Star, - Slash, - Percent, - Caret, - And, - Or, - Shl, - Shr, -} - // This type must not implement `Hash` due to the unusual `PartialEq` impl below. #[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)] pub enum InvisibleOrigin { @@ -50,10 +30,18 @@ pub enum InvisibleOrigin { // Converted from `proc_macro::Delimiter` in // `proc_macro::Delimiter::to_internal`, i.e. returned by a proc macro. ProcMacro, +} - // Converted from `TokenKind::Interpolated` in - // `TokenStream::flatten_token`. Treated similarly to `ProcMacro`. - FlattenToken, +impl InvisibleOrigin { + // Should the parser skip these invisible delimiters? Ideally this function + // will eventually disappear and no invisible delimiters will be skipped. + #[inline] + pub fn skip(&self) -> bool { + match self { + InvisibleOrigin::MetaVar(_) => false, + InvisibleOrigin::ProcMacro => true, + } + } } impl PartialEq for InvisibleOrigin { @@ -149,10 +137,7 @@ impl Delimiter { pub fn skip(&self) -> bool { match self { Delimiter::Parenthesis | Delimiter::Bracket | Delimiter::Brace => false, - Delimiter::Invisible(InvisibleOrigin::MetaVar(_)) => false, - Delimiter::Invisible(InvisibleOrigin::FlattenToken | InvisibleOrigin::ProcMacro) => { - true - } + Delimiter::Invisible(origin) => origin.skip(), } } @@ -166,6 +151,24 @@ impl Delimiter { _ => false, } } + + pub fn as_open_token_kind(&self) -> TokenKind { + match *self { + Delimiter::Parenthesis => OpenParen, + Delimiter::Brace => OpenBrace, + Delimiter::Bracket => OpenBracket, + Delimiter::Invisible(origin) => OpenInvisible(origin), + } + } + + pub fn as_close_token_kind(&self) -> TokenKind { + match *self { + Delimiter::Parenthesis => CloseParen, + Delimiter::Brace => CloseBrace, + Delimiter::Bracket => CloseBracket, + Delimiter::Invisible(origin) => CloseInvisible(origin), + } + } } // Note that the suffix is *not* considered when deciding the `LitKind` in this @@ -214,16 +217,17 @@ impl Lit { } } - /// Keep this in sync with `Token::can_begin_literal_maybe_minus` excluding unary negation. + /// Keep this in sync with `Token::can_begin_literal_maybe_minus` and + /// `Parser::eat_token_lit` (excluding unary negation). pub fn from_token(token: &Token) -> Option { match token.uninterpolate().kind { Ident(name, IdentIsRaw::No) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)), Literal(token_lit) => Some(token_lit), - Interpolated(ref nt) - if let NtExpr(expr) | NtLiteral(expr) = &**nt - && let ast::ExprKind::Lit(token_lit) = expr.kind => - { - Some(token_lit) + OpenInvisible(InvisibleOrigin::MetaVar( + MetaVarKind::Literal | MetaVarKind::Expr { .. }, + )) => { + // Unreachable with the current test suite. + panic!("from_token metavar"); } _ => None, } @@ -352,9 +356,7 @@ impl From for bool { } } -// SAFETY: due to the `Clone` impl below, all fields of all variants other than -// `Interpolated` must impl `Copy`. -#[derive(PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum TokenKind { /* Expression-operator symbols. */ /// `=` @@ -376,11 +378,49 @@ pub enum TokenKind { /// `||` OrOr, /// `!` - Not, + Bang, /// `~` Tilde, - BinOp(BinOpToken), - BinOpEq(BinOpToken), + // `+` + Plus, + // `-` + Minus, + // `*` + Star, + // `/` + Slash, + // `%` + Percent, + // `^` + Caret, + // `&` + And, + // `|` + Or, + // `<<` + Shl, + // `>>` + Shr, + // `+=` + PlusEq, + // `-=` + MinusEq, + // `*=` + StarEq, + // `/=` + SlashEq, + // `%=` + PercentEq, + // `^=` + CaretEq, + // `&=` + AndEq, + // `|=` + OrEq, + // `<<=` + ShlEq, + // `>>=` + ShrEq, /* Structural symbols */ /// `@` @@ -415,18 +455,31 @@ pub enum TokenKind { Question, /// Used by proc macros for representing lifetimes, not generated by lexer right now. SingleQuote, - /// An opening delimiter (e.g., `{`). - OpenDelim(Delimiter), - /// A closing delimiter (e.g., `}`). - CloseDelim(Delimiter), + /// `(` + OpenParen, + /// `)` + CloseParen, + /// `{` + OpenBrace, + /// `}` + CloseBrace, + /// `[` + OpenBracket, + /// `]` + CloseBracket, + /// Invisible opening delimiter, produced by a macro. + OpenInvisible(InvisibleOrigin), + /// Invisible closing delimiter, produced by a macro. + CloseInvisible(InvisibleOrigin), /* Literals */ Literal(Lit), /// Identifier token. /// Do not forget about `NtIdent` when you want to match on identifiers. - /// It's recommended to use `Token::(ident,uninterpolate,uninterpolated_span)` to - /// treat regular and interpolated identifiers in the same way. + /// It's recommended to use `Token::{ident,uninterpolate}` and + /// `Parser::token_uninterpolated_span` to treat regular and interpolated + /// identifiers in the same way. Ident(Symbol, IdentIsRaw), /// This identifier (and its span) is the identifier passed to the /// declarative macro. The span in the surrounding `Token` is the span of @@ -435,29 +488,15 @@ pub enum TokenKind { /// Lifetime identifier token. /// Do not forget about `NtLifetime` when you want to match on lifetime identifiers. - /// It's recommended to use `Token::(lifetime,uninterpolate,uninterpolated_span)` to - /// treat regular and interpolated lifetime identifiers in the same way. + /// It's recommended to use `Token::{ident,uninterpolate}` and + /// `Parser::token_uninterpolated_span` to treat regular and interpolated + /// identifiers in the same way. Lifetime(Symbol, IdentIsRaw), /// This identifier (and its span) is the lifetime passed to the /// declarative macro. The span in the surrounding `Token` is the span of /// the `lifetime` metavariable in the macro's RHS. NtLifetime(Ident, IdentIsRaw), - /// An embedded AST node, as produced by a macro. This only exists for - /// historical reasons. We'd like to get rid of it, for multiple reasons. - /// - It's conceptually very strange. Saying a token can contain an AST - /// node is like saying, in natural language, that a word can contain a - /// sentence. - /// - It requires special handling in a bunch of places in the parser. - /// - It prevents `Token` from implementing `Copy`. - /// It adds complexity and likely slows things down. Please don't add new - /// occurrences of this token kind! - /// - /// The span in the surrounding `Token` is that of the metavariable in the - /// macro's RHS. The span within the Nonterminal is that of the fragment - /// passed to the macro at the call site. - Interpolated(Arc), - /// A doc comment token. /// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc) /// similarly to symbols in string literal tokens. @@ -467,20 +506,7 @@ pub enum TokenKind { Eof, } -impl Clone for TokenKind { - fn clone(&self) -> Self { - // `TokenKind` would impl `Copy` if it weren't for `Interpolated`. So - // for all other variants, this implementation of `clone` is just like - // a copy. This is faster than the `derive(Clone)` version which has a - // separate path for every variant. - match self { - Interpolated(nt) => Interpolated(Arc::clone(nt)), - _ => unsafe { std::ptr::read(self) }, - } - } -} - -#[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub struct Token { pub kind: TokenKind, pub span: Span, @@ -500,31 +526,31 @@ impl TokenKind { Some(match (self, n) { (Le, 1) => (Lt, Eq), (EqEq, 1) => (Eq, Eq), - (Ne, 1) => (Not, Eq), + (Ne, 1) => (Bang, Eq), (Ge, 1) => (Gt, Eq), - (AndAnd, 1) => (BinOp(And), BinOp(And)), - (OrOr, 1) => (BinOp(Or), BinOp(Or)), - (BinOp(Shl), 1) => (Lt, Lt), - (BinOp(Shr), 1) => (Gt, Gt), - (BinOpEq(Plus), 1) => (BinOp(Plus), Eq), - (BinOpEq(Minus), 1) => (BinOp(Minus), Eq), - (BinOpEq(Star), 1) => (BinOp(Star), Eq), - (BinOpEq(Slash), 1) => (BinOp(Slash), Eq), - (BinOpEq(Percent), 1) => (BinOp(Percent), Eq), - (BinOpEq(Caret), 1) => (BinOp(Caret), Eq), - (BinOpEq(And), 1) => (BinOp(And), Eq), - (BinOpEq(Or), 1) => (BinOp(Or), Eq), - (BinOpEq(Shl), 1) => (Lt, Le), // `<` + `<=` - (BinOpEq(Shl), 2) => (BinOp(Shl), Eq), // `<<` + `=` - (BinOpEq(Shr), 1) => (Gt, Ge), // `>` + `>=` - (BinOpEq(Shr), 2) => (BinOp(Shr), Eq), // `>>` + `=` + (AndAnd, 1) => (And, And), + (OrOr, 1) => (Or, Or), + (Shl, 1) => (Lt, Lt), + (Shr, 1) => (Gt, Gt), + (PlusEq, 1) => (Plus, Eq), + (MinusEq, 1) => (Minus, Eq), + (StarEq, 1) => (Star, Eq), + (SlashEq, 1) => (Slash, Eq), + (PercentEq, 1) => (Percent, Eq), + (CaretEq, 1) => (Caret, Eq), + (AndEq, 1) => (And, Eq), + (OrEq, 1) => (Or, Eq), + (ShlEq, 1) => (Lt, Le), // `<` + `<=` + (ShlEq, 2) => (Shl, Eq), // `<<` + `=` + (ShrEq, 1) => (Gt, Ge), // `>` + `>=` + (ShrEq, 2) => (Shr, Eq), // `>>` + `=` (DotDot, 1) => (Dot, Dot), (DotDotDot, 1) => (Dot, DotDot), // `.` + `..` (DotDotDot, 2) => (DotDot, Dot), // `..` + `.` (DotDotEq, 2) => (DotDot, Eq), (PathSep, 1) => (Colon, Colon), - (RArrow, 1) => (BinOp(Minus), Gt), - (LArrow, 1) => (Lt, BinOp(Minus)), + (RArrow, 1) => (Minus, Gt), + (LArrow, 1) => (Lt, Minus), (FatArrow, 1) => (Eq, Gt), _ => return None, }) @@ -543,7 +569,38 @@ impl TokenKind { } pub fn should_end_const_arg(&self) -> bool { - matches!(self, Gt | Ge | BinOp(Shr) | BinOpEq(Shr)) + matches!(self, Gt | Ge | Shr | ShrEq) + } + + pub fn is_delim(&self) -> bool { + self.open_delim().is_some() || self.close_delim().is_some() + } + + pub fn open_delim(&self) -> Option { + match *self { + OpenParen => Some(Delimiter::Parenthesis), + OpenBrace => Some(Delimiter::Brace), + OpenBracket => Some(Delimiter::Bracket), + OpenInvisible(origin) => Some(Delimiter::Invisible(origin)), + _ => None, + } + } + + pub fn close_delim(&self) -> Option { + match *self { + CloseParen => Some(Delimiter::Parenthesis), + CloseBrace => Some(Delimiter::Brace), + CloseBracket => Some(Delimiter::Bracket), + CloseInvisible(origin) => Some(Delimiter::Invisible(origin)), + _ => None, + } + } + + pub fn is_close_delim_or_eof(&self) -> bool { + match self { + CloseParen | CloseBrace | CloseBracket | CloseInvisible(_) | Eof => true, + _ => false, + } } } @@ -562,39 +619,26 @@ impl Token { Token::new(Ident(ident.name, ident.is_raw_guess().into()), ident.span) } - /// For interpolated tokens, returns a span of the fragment to which the interpolated - /// token refers. For all other tokens this is just a regular span. - /// It is particularly important to use this for identifiers and lifetimes - /// for which spans affect name resolution and edition checks. - /// Note that keywords are also identifiers, so they should use this - /// if they keep spans or perform edition checks. - pub fn uninterpolated_span(&self) -> Span { - match self.kind { - NtIdent(ident, _) | NtLifetime(ident, _) => ident.span, - Interpolated(ref nt) => nt.use_span(), - _ => self.span, - } - } - pub fn is_range_separator(&self) -> bool { [DotDot, DotDotDot, DotDotEq].contains(&self.kind) } pub fn is_punct(&self) -> bool { match self.kind { - Eq | Lt | Le | EqEq | Ne | Ge | Gt | AndAnd | OrOr | Not | Tilde | BinOp(_) - | BinOpEq(_) | At | Dot | DotDot | DotDotDot | DotDotEq | Comma | Semi | Colon - | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question | SingleQuote => { - true - } - - OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | Ident(..) - | NtIdent(..) | Lifetime(..) | NtLifetime(..) | Interpolated(..) | Eof => false, + Eq | Lt | Le | EqEq | Ne | Ge | Gt | AndAnd | OrOr | Bang | Tilde | Plus | Minus + | Star | Slash | Percent | Caret | And | Or | Shl | Shr | PlusEq | MinusEq | StarEq + | SlashEq | PercentEq | CaretEq | AndEq | OrEq | ShlEq | ShrEq | At | Dot | DotDot + | DotDotDot | DotDotEq | Comma | Semi | Colon | PathSep | RArrow | LArrow + | FatArrow | Pound | Dollar | Question | SingleQuote => true, + + OpenParen | CloseParen | OpenBrace | CloseBrace | OpenBracket | CloseBracket + | OpenInvisible(_) | CloseInvisible(_) | Literal(..) | DocComment(..) | Ident(..) + | NtIdent(..) | Lifetime(..) | NtLifetime(..) | Eof => false, } } pub fn is_like_plus(&self) -> bool { - matches!(self.kind, BinOp(Plus) | BinOpEq(Plus)) + matches!(self.kind, Plus | PlusEq) } /// Returns `true` if the token can appear at the start of an expression. @@ -602,36 +646,31 @@ impl Token { /// **NB**: Take care when modifying this function, since it will change /// the stable set of tokens that are allowed to match an expr nonterminal. pub fn can_begin_expr(&self) -> bool { - use Delimiter::*; match self.uninterpolate().kind { Ident(name, is_raw) => ident_can_begin_expr(name, self.span, is_raw), // value name or keyword - OpenDelim(Parenthesis | Brace | Bracket) | // tuple, array or block + OpenParen | // tuple + OpenBrace | // block + OpenBracket | // array Literal(..) | // literal - Not | // operator not - BinOp(Minus) | // unary minus - BinOp(Star) | // dereference - BinOp(Or) | OrOr | // closure - BinOp(And) | // reference + Bang | // operator not + Minus | // unary minus + Star | // dereference + Or | OrOr | // closure + And | // reference AndAnd | // double reference // DotDotDot is no longer supported, but we need some way to display the error DotDot | DotDotDot | DotDotEq | // range notation - Lt | BinOp(Shl) | // associated path + Lt | Shl | // associated path PathSep | // global path Lifetime(..) | // labeled loop Pound => true, // expression attributes - Interpolated(ref nt) => - matches!(&**nt, - NtBlock(..) | - NtExpr(..) | - NtLiteral(..) - ), - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + OpenInvisible(InvisibleOrigin::MetaVar( MetaVarKind::Block | MetaVarKind::Expr { .. } | MetaVarKind::Literal | MetaVarKind::Path - ))) => true, + )) => true, _ => false, } } @@ -643,32 +682,26 @@ impl Token { match &self.uninterpolate().kind { // box, ref, mut, and other identifiers (can stricten) Ident(..) | NtIdent(..) | - OpenDelim(Delimiter::Parenthesis) | // tuple pattern - OpenDelim(Delimiter::Bracket) | // slice pattern - BinOp(And) | // reference - BinOp(Minus) | // negative literal - AndAnd | // double reference - Literal(_) | // literal - DotDot | // range pattern (future compat) - DotDotDot | // range pattern (future compat) - PathSep | // path - Lt | // path (UFCS constant) - BinOp(Shl) => true, // path (double UFCS) - // leading vert `|` or-pattern - BinOp(Or) => matches!(pat_kind, PatWithOr), - Interpolated(nt) => - matches!(&**nt, - | NtExpr(..) - | NtLiteral(..) - ), - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + OpenParen | // tuple pattern + OpenBracket | // slice pattern + And | // reference + Minus | // negative literal + AndAnd | // double reference + Literal(_) | // literal + DotDot | // range pattern (future compat) + DotDotDot | // range pattern (future compat) + PathSep | // path + Lt | // path (UFCS constant) + Shl => true, // path (double UFCS) + Or => matches!(pat_kind, PatWithOr), // leading vert `|` or-pattern + OpenInvisible(InvisibleOrigin::MetaVar( MetaVarKind::Expr { .. } | MetaVarKind::Literal | MetaVarKind::Meta { .. } | MetaVarKind::Pat(_) | MetaVarKind::Path | MetaVarKind::Ty { .. } - ))) => true, + )) => true, _ => false, } } @@ -676,22 +709,22 @@ impl Token { /// Returns `true` if the token can appear at the start of a type. pub fn can_begin_type(&self) -> bool { match self.uninterpolate().kind { - Ident(name, is_raw) => + Ident(name, is_raw) => ident_can_begin_type(name, self.span, is_raw), // type name or keyword - OpenDelim(Delimiter::Parenthesis) | // tuple - OpenDelim(Delimiter::Bracket) | // array - Not | // never - BinOp(Star) | // raw pointer - BinOp(And) | // reference - AndAnd | // double reference - Question | // maybe bound in trait object - Lifetime(..) | // lifetime bound in trait object - Lt | BinOp(Shl) | // associated path - PathSep => true, // global path - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + OpenParen | // tuple + OpenBracket | // array + Bang | // never + Star | // raw pointer + And | // reference + AndAnd | // double reference + Question | // maybe bound in trait object + Lifetime(..) | // lifetime bound in trait object + Lt | Shl | // associated path + PathSep => true, // global path + OpenInvisible(InvisibleOrigin::MetaVar( MetaVarKind::Ty { .. } | MetaVarKind::Path - ))) => true, + )) => true, // For anonymous structs or unions, which only appear in specific positions // (type of struct fields or union fields), we don't consider them as regular types _ => false, @@ -701,12 +734,11 @@ impl Token { /// Returns `true` if the token can appear at the start of a const param. pub fn can_begin_const_arg(&self) -> bool { match self.kind { - OpenDelim(Delimiter::Brace) | Literal(..) | BinOp(Minus) => true, + OpenBrace | Literal(..) | Minus => true, Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true, - Interpolated(ref nt) => matches!(&**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)), - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + OpenInvisible(InvisibleOrigin::MetaVar( MetaVarKind::Expr { .. } | MetaVarKind::Block | MetaVarKind::Literal, - ))) => true, + )) => true, _ => false, } } @@ -747,23 +779,13 @@ impl Token { /// /// In other words, would this token be a valid start of `parse_literal_maybe_minus`? /// - /// Keep this in sync with and `Lit::from_token`, excluding unary negation. + /// Keep this in sync with `Lit::from_token` and `Parser::eat_token_lit` + /// (excluding unary negation). pub fn can_begin_literal_maybe_minus(&self) -> bool { match self.uninterpolate().kind { - Literal(..) | BinOp(Minus) => true, + Literal(..) | Minus => true, Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true, - Interpolated(ref nt) => match &**nt { - NtLiteral(_) => true, - NtExpr(e) => match &e.kind { - ast::ExprKind::Lit(_) => true, - ast::ExprKind::Unary(ast::UnOp::Neg, e) => { - matches!(&e.kind, ast::ExprKind::Lit(_)) - } - _ => false, - }, - _ => false, - }, - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind { + OpenInvisible(InvisibleOrigin::MetaVar(mv_kind)) => match mv_kind { MetaVarKind::Literal => true, MetaVarKind::Expr { can_begin_literal_maybe_minus, .. } => { can_begin_literal_maybe_minus @@ -777,15 +799,7 @@ impl Token { pub fn can_begin_string_literal(&self) -> bool { match self.uninterpolate().kind { Literal(..) => true, - Interpolated(ref nt) => match &**nt { - NtLiteral(_) => true, - NtExpr(e) => match &e.kind { - ast::ExprKind::Lit(_) => true, - _ => false, - }, - _ => false, - }, - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind { + OpenInvisible(InvisibleOrigin::MetaVar(mv_kind)) => match mv_kind { MetaVarKind::Literal => true, MetaVarKind::Expr { can_begin_string_literal, .. } => can_begin_string_literal, _ => false, @@ -848,25 +862,21 @@ impl Token { /// Is this a pre-parsed expression dropped into the token stream /// (which happens while parsing the result of macro expansion)? - pub fn is_whole_expr(&self) -> bool { - if let Interpolated(nt) = &self.kind - && let NtExpr(_) | NtLiteral(_) | NtBlock(_) = &**nt - { - true - } else { - matches!(self.is_metavar_seq(), Some(MetaVarKind::Path)) - } + pub fn is_metavar_expr(&self) -> bool { + matches!( + self.is_metavar_seq(), + Some( + MetaVarKind::Expr { .. } + | MetaVarKind::Literal + | MetaVarKind::Path + | MetaVarKind::Block + ) + ) } - /// Is the token an interpolated block (`$b:block`)? - pub fn is_whole_block(&self) -> bool { - if let Interpolated(nt) = &self.kind - && let NtBlock(..) = &**nt - { - return true; - } - - false + /// Are we at a block from a metavar (`$b:block`)? + pub fn is_metavar_block(&self) -> bool { + matches!(self.is_metavar_seq(), Some(MetaVarKind::Block)) } /// Returns `true` if the token is either the `mut` or `const` keyword. @@ -875,7 +885,7 @@ impl Token { } pub fn is_qpath_start(&self) -> bool { - self == &Lt || self == &BinOp(Shl) + self == &Lt || self == &Shl } pub fn is_path_start(&self) -> bool { @@ -906,11 +916,6 @@ impl Token { self.is_non_raw_ident_where(Ident::is_path_segment_keyword) } - /// Don't use this unless you're doing something very loose and heuristic-y. - pub fn is_any_keyword(&self) -> bool { - self.is_non_raw_ident_where(Ident::is_any_keyword) - } - /// Returns true for reserved identifiers used internally for elided lifetimes, /// unnamed method parameters, crate root module, error recovery etc. pub fn is_special_ident(&self) -> bool { @@ -961,65 +966,89 @@ impl Token { /// from an expanded metavar? pub fn is_metavar_seq(&self) -> Option { match self.kind { - OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => Some(kind), + OpenInvisible(InvisibleOrigin::MetaVar(kind)) => Some(kind), _ => None, } } pub fn glue(&self, joint: &Token) -> Option { - let kind = match self.kind { - Eq => match joint.kind { - Eq => EqEq, - Gt => FatArrow, - _ => return None, - }, - Lt => match joint.kind { - Eq => Le, - Lt => BinOp(Shl), - Le => BinOpEq(Shl), - BinOp(Minus) => LArrow, - _ => return None, - }, - Gt => match joint.kind { - Eq => Ge, - Gt => BinOp(Shr), - Ge => BinOpEq(Shr), - _ => return None, - }, - Not => match joint.kind { - Eq => Ne, - _ => return None, - }, - BinOp(op) => match joint.kind { - Eq => BinOpEq(op), - BinOp(And) if op == And => AndAnd, - BinOp(Or) if op == Or => OrOr, - Gt if op == Minus => RArrow, - _ => return None, - }, - Dot => match joint.kind { - Dot => DotDot, - DotDot => DotDotDot, - _ => return None, - }, - DotDot => match joint.kind { - Dot => DotDotDot, - Eq => DotDotEq, - _ => return None, - }, - Colon => match joint.kind { - Colon => PathSep, - _ => return None, - }, - SingleQuote => match joint.kind { - Ident(name, is_raw) => Lifetime(Symbol::intern(&format!("'{name}")), is_raw), - _ => return None, - }, + let kind = match (&self.kind, &joint.kind) { + (Eq, Eq) => EqEq, + (Eq, Gt) => FatArrow, + (Eq, _) => return None, + + (Lt, Eq) => Le, + (Lt, Lt) => Shl, + (Lt, Le) => ShlEq, + (Lt, Minus) => LArrow, + (Lt, _) => return None, + + (Gt, Eq) => Ge, + (Gt, Gt) => Shr, + (Gt, Ge) => ShrEq, + (Gt, _) => return None, + + (Bang, Eq) => Ne, + (Bang, _) => return None, + + (Plus, Eq) => PlusEq, + (Plus, _) => return None, + + (Minus, Eq) => MinusEq, + (Minus, Gt) => RArrow, + (Minus, _) => return None, + + (Star, Eq) => StarEq, + (Star, _) => return None, + + (Slash, Eq) => SlashEq, + (Slash, _) => return None, + + (Percent, Eq) => PercentEq, + (Percent, _) => return None, - Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot - | DotDotEq | Comma | Semi | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar - | Question | OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..) | NtIdent(..) - | Lifetime(..) | NtLifetime(..) | Interpolated(..) | DocComment(..) | Eof => { + (Caret, Eq) => CaretEq, + (Caret, _) => return None, + + (And, Eq) => AndEq, + (And, And) => AndAnd, + (And, _) => return None, + + (Or, Eq) => OrEq, + (Or, Or) => OrOr, + (Or, _) => return None, + + (Shl, Eq) => ShlEq, + (Shl, _) => return None, + + (Shr, Eq) => ShrEq, + (Shr, _) => return None, + + (Dot, Dot) => DotDot, + (Dot, DotDot) => DotDotDot, + (Dot, _) => return None, + + (DotDot, Dot) => DotDotDot, + (DotDot, Eq) => DotDotEq, + (DotDot, _) => return None, + + (Colon, Colon) => PathSep, + (Colon, _) => return None, + + (SingleQuote, Ident(name, is_raw)) => { + Lifetime(Symbol::intern(&format!("'{name}")), *is_raw) + } + (SingleQuote, _) => return None, + + ( + Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | PlusEq | MinusEq | StarEq | SlashEq + | PercentEq | CaretEq | AndEq | OrEq | ShlEq | ShrEq | At | DotDotDot | DotDotEq + | Comma | Semi | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question + | OpenParen | CloseParen | OpenBrace | CloseBrace | OpenBracket | CloseBracket + | OpenInvisible(_) | CloseInvisible(_) | Literal(..) | Ident(..) | NtIdent(..) + | Lifetime(..) | NtLifetime(..) | DocComment(..) | Eof, + _, + ) => { return None; } }; @@ -1056,16 +1085,6 @@ pub enum NtExprKind { Expr2021 { inferred: bool }, } -#[derive(Clone, Encodable, Decodable)] -/// For interpolation during macro expansion. -pub enum Nonterminal { - NtItem(P), - NtBlock(P), - NtStmt(P), - NtExpr(P), - NtLiteral(P), -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] pub enum NonterminalKind { Item, @@ -1149,58 +1168,6 @@ impl fmt::Display for NonterminalKind { } } -impl Nonterminal { - pub fn use_span(&self) -> Span { - match self { - NtItem(item) => item.span, - NtBlock(block) => block.span, - NtStmt(stmt) => stmt.span, - NtExpr(expr) | NtLiteral(expr) => expr.span, - } - } - - pub fn descr(&self) -> &'static str { - match self { - NtItem(..) => "item", - NtBlock(..) => "block", - NtStmt(..) => "statement", - NtExpr(..) => "expression", - NtLiteral(..) => "literal", - } - } -} - -impl PartialEq for Nonterminal { - fn eq(&self, _rhs: &Self) -> bool { - // FIXME: Assume that all nonterminals are not equal, we can't compare them - // correctly based on data from AST. This will prevent them from matching each other - // in macros. The comparison will become possible only when each nonterminal has an - // attached token stream from which it was parsed. - false - } -} - -impl fmt::Debug for Nonterminal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - NtItem(..) => f.pad("NtItem(..)"), - NtBlock(..) => f.pad("NtBlock(..)"), - NtStmt(..) => f.pad("NtStmt(..)"), - NtExpr(..) => f.pad("NtExpr(..)"), - NtLiteral(..) => f.pad("NtLiteral(..)"), - } - } -} - -impl HashStable for Nonterminal -where - CTX: crate::HashStableContext, -{ - fn hash_stable(&self, _hcx: &mut CTX, _hasher: &mut StableHasher) { - panic!("interpolated tokens should not be present in the HIR") - } -} - // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { @@ -1210,7 +1177,6 @@ mod size_asserts { // tidy-alphabetical-start static_assert_size!(Lit, 12); static_assert_size!(LitKind, 2); - static_assert_size!(Nonterminal, 16); static_assert_size!(Token, 24); static_assert_size!(TokenKind, 16); // tidy-alphabetical-end diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 49ae8cc78fc5c..636c26bcde04b 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -14,18 +14,20 @@ //! ownership of the original. use std::borrow::Cow; +use std::ops::Range; use std::sync::Arc; -use std::{cmp, fmt, iter}; +use std::{cmp, fmt, iter, mem}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::{Decodable, Encodable}; use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym}; +use thin_vec::ThinVec; -use crate::ast::{AttrStyle, StmtKind}; +use crate::ast::AttrStyle; use crate::ast_traits::{HasAttrs, HasTokens}; -use crate::token::{self, Delimiter, InvisibleOrigin, Nonterminal, Token, TokenKind}; +use crate::token::{self, Delimiter, Token, TokenKind}; use crate::{AttrVec, Attribute}; /// Part of a `TokenStream`. @@ -106,25 +108,30 @@ where } } -pub trait ToAttrTokenStream: sync::DynSend + sync::DynSync { - fn to_attr_token_stream(&self) -> AttrTokenStream; -} - -impl ToAttrTokenStream for AttrTokenStream { - fn to_attr_token_stream(&self) -> AttrTokenStream { - self.clone() - } -} - -/// A lazy version of [`TokenStream`], which defers creation -/// of an actual `TokenStream` until it is needed. -/// `Box` is here only to reduce the structure size. +/// A lazy version of [`AttrTokenStream`], which defers creation of an actual +/// `AttrTokenStream` until it is needed. #[derive(Clone)] -pub struct LazyAttrTokenStream(Arc>); +pub struct LazyAttrTokenStream(Arc); impl LazyAttrTokenStream { - pub fn new(inner: impl ToAttrTokenStream + 'static) -> LazyAttrTokenStream { - LazyAttrTokenStream(Arc::new(Box::new(inner))) + pub fn new_direct(stream: AttrTokenStream) -> LazyAttrTokenStream { + LazyAttrTokenStream(Arc::new(LazyAttrTokenStreamInner::Direct(stream))) + } + + pub fn new_pending( + start_token: (Token, Spacing), + cursor_snapshot: TokenCursor, + num_calls: u32, + break_last_token: u32, + node_replacements: ThinVec, + ) -> LazyAttrTokenStream { + LazyAttrTokenStream(Arc::new(LazyAttrTokenStreamInner::Pending { + start_token, + cursor_snapshot, + num_calls, + break_last_token, + node_replacements, + })) } pub fn to_attr_token_stream(&self) -> AttrTokenStream { @@ -156,6 +163,184 @@ impl HashStable for LazyAttrTokenStream { } } +/// A token range within a `Parser`'s full token stream. +#[derive(Clone, Debug)] +pub struct ParserRange(pub Range); + +/// A token range within an individual AST node's (lazy) token stream, i.e. +/// relative to that node's first token. Distinct from `ParserRange` so the two +/// kinds of range can't be mixed up. +#[derive(Clone, Debug)] +pub struct NodeRange(pub Range); + +/// Indicates a range of tokens that should be replaced by an `AttrsTarget` +/// (replacement) or be replaced by nothing (deletion). This is used in two +/// places during token collection. +/// +/// 1. Replacement. During the parsing of an AST node that may have a +/// `#[derive]` attribute, when we parse a nested AST node that has `#[cfg]` +/// or `#[cfg_attr]`, we replace the entire inner AST node with +/// `FlatToken::AttrsTarget`. This lets us perform eager cfg-expansion on an +/// `AttrTokenStream`. +/// +/// 2. Deletion. We delete inner attributes from all collected token streams, +/// and instead track them through the `attrs` field on the AST node. This +/// lets us manipulate them similarly to outer attributes. When we create a +/// `TokenStream`, the inner attributes are inserted into the proper place +/// in the token stream. +/// +/// Each replacement starts off in `ParserReplacement` form but is converted to +/// `NodeReplacement` form when it is attached to a single AST node, via +/// `LazyAttrTokenStreamImpl`. +pub type ParserReplacement = (ParserRange, Option); + +/// See the comment on `ParserReplacement`. +pub type NodeReplacement = (NodeRange, Option); + +impl NodeRange { + // Converts a range within a parser's tokens to a range within a + // node's tokens beginning at `start_pos`. + // + // For example, imagine a parser with 50 tokens in its token stream, a + // function that spans `ParserRange(20..40)` and an inner attribute within + // that function that spans `ParserRange(30..35)`. We would find the inner + // attribute's range within the function's tokens by subtracting 20, which + // is the position of the function's start token. This gives + // `NodeRange(10..15)`. + pub fn new(ParserRange(parser_range): ParserRange, start_pos: u32) -> NodeRange { + assert!(!parser_range.is_empty()); + assert!(parser_range.start >= start_pos); + NodeRange((parser_range.start - start_pos)..(parser_range.end - start_pos)) + } +} + +enum LazyAttrTokenStreamInner { + // The token stream has already been produced. + Direct(AttrTokenStream), + + // From a value of this type we can reconstruct the `TokenStream` seen by + // the `f` callback passed to a call to `Parser::collect_tokens`, by + // replaying the getting of the tokens. This saves us producing a + // `TokenStream` if it is never needed, e.g. a captured `macro_rules!` + // argument that is never passed to a proc macro. In practice, token stream + // creation happens rarely compared to calls to `collect_tokens` (see some + // statistics in #78736) so we are doing as little up-front work as + // possible. + // + // This also makes `Parser` very cheap to clone, since there is no + // intermediate collection buffer to clone. + Pending { + start_token: (Token, Spacing), + cursor_snapshot: TokenCursor, + num_calls: u32, + break_last_token: u32, + node_replacements: ThinVec, + }, +} + +impl LazyAttrTokenStreamInner { + fn to_attr_token_stream(&self) -> AttrTokenStream { + match self { + LazyAttrTokenStreamInner::Direct(stream) => stream.clone(), + LazyAttrTokenStreamInner::Pending { + start_token, + cursor_snapshot, + num_calls, + break_last_token, + node_replacements, + } => { + // The token produced by the final call to `{,inlined_}next` was not + // actually consumed by the callback. The combination of chaining the + // initial token and using `take` produces the desired result - we + // produce an empty `TokenStream` if no calls were made, and omit the + // final token otherwise. + let mut cursor_snapshot = cursor_snapshot.clone(); + let tokens = iter::once(FlatToken::Token(*start_token)) + .chain(iter::repeat_with(|| FlatToken::Token(cursor_snapshot.next()))) + .take(*num_calls as usize); + + if node_replacements.is_empty() { + make_attr_token_stream(tokens, *break_last_token) + } else { + let mut tokens: Vec<_> = tokens.collect(); + let mut node_replacements = node_replacements.to_vec(); + node_replacements.sort_by_key(|(range, _)| range.0.start); + + #[cfg(debug_assertions)] + for [(node_range, tokens), (next_node_range, next_tokens)] in + node_replacements.array_windows() + { + assert!( + node_range.0.end <= next_node_range.0.start + || node_range.0.end >= next_node_range.0.end, + "Node ranges should be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", + node_range, + tokens, + next_node_range, + next_tokens, + ); + } + + // Process the replace ranges, starting from the highest start + // position and working our way back. If have tokens like: + // + // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` + // + // Then we will generate replace ranges for both + // the `#[cfg(FALSE)] field: bool` and the entire + // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` + // + // By starting processing from the replace range with the greatest + // start position, we ensure that any (outer) replace range which + // encloses another (inner) replace range will fully overwrite the + // inner range's replacement. + for (node_range, target) in node_replacements.into_iter().rev() { + assert!( + !node_range.0.is_empty(), + "Cannot replace an empty node range: {:?}", + node_range.0 + ); + + // Replace the tokens in range with zero or one `FlatToken::AttrsTarget`s, + // plus enough `FlatToken::Empty`s to fill up the rest of the range. This + // keeps the total length of `tokens` constant throughout the replacement + // process, allowing us to do all replacements without adjusting indices. + let target_len = target.is_some() as usize; + tokens.splice( + (node_range.0.start as usize)..(node_range.0.end as usize), + target.into_iter().map(|target| FlatToken::AttrsTarget(target)).chain( + iter::repeat(FlatToken::Empty) + .take(node_range.0.len() - target_len), + ), + ); + } + make_attr_token_stream(tokens.into_iter(), *break_last_token) + } + } + } + } +} + +/// A helper struct used when building an `AttrTokenStream` from +/// a `LazyAttrTokenStream`. Both delimiter and non-delimited tokens +/// are stored as `FlatToken::Token`. A vector of `FlatToken`s +/// is then 'parsed' to build up an `AttrTokenStream` with nested +/// `AttrTokenTree::Delimited` tokens. +#[derive(Debug, Clone)] +enum FlatToken { + /// A token - this holds both delimiter (e.g. '{' and '}') + /// and non-delimiter tokens + Token((Token, Spacing)), + /// Holds the `AttrsTarget` for an AST node. The `AttrsTarget` is inserted + /// directly into the constructed `AttrTokenStream` as an + /// `AttrTokenTree::AttrsTarget`. + AttrsTarget(AttrsTarget), + /// A special 'empty' token that is ignored during the conversion + /// to an `AttrTokenStream`. This is used to simplify the + /// handling of replace ranges. + Empty, +} + /// An `AttrTokenStream` is similar to a `TokenStream`, but with extra /// information about the tokens for attribute targets. This is used /// during expansion to perform early cfg-expansion, and to process attributes @@ -163,6 +348,71 @@ impl HashStable for LazyAttrTokenStream { #[derive(Clone, Debug, Default, Encodable, Decodable)] pub struct AttrTokenStream(pub Arc>); +/// Converts a flattened iterator of tokens (including open and close delimiter tokens) into an +/// `AttrTokenStream`, creating an `AttrTokenTree::Delimited` for each matching pair of open and +/// close delims. +fn make_attr_token_stream( + iter: impl Iterator, + break_last_token: u32, +) -> AttrTokenStream { + #[derive(Debug)] + struct FrameData { + // This is `None` for the first frame, `Some` for all others. + open_delim_sp: Option<(Delimiter, Span, Spacing)>, + inner: Vec, + } + // The stack always has at least one element. Storing it separately makes for shorter code. + let mut stack_top = FrameData { open_delim_sp: None, inner: vec![] }; + let mut stack_rest = vec![]; + for flat_token in iter { + match flat_token { + FlatToken::Token((token @ Token { kind, span }, spacing)) => { + if let Some(delim) = kind.open_delim() { + stack_rest.push(mem::replace( + &mut stack_top, + FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] }, + )); + } else if let Some(delim) = kind.close_delim() { + let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap()); + let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap(); + assert!( + open_delim.eq_ignoring_invisible_origin(&delim), + "Mismatched open/close delims: open={open_delim:?} close={span:?}" + ); + let dspan = DelimSpan::from_pair(open_sp, span); + let dspacing = DelimSpacing::new(open_spacing, spacing); + let stream = AttrTokenStream::new(frame_data.inner); + let delimited = AttrTokenTree::Delimited(dspan, dspacing, delim, stream); + stack_top.inner.push(delimited); + } else { + stack_top.inner.push(AttrTokenTree::Token(token, spacing)) + } + } + FlatToken::AttrsTarget(target) => { + stack_top.inner.push(AttrTokenTree::AttrsTarget(target)) + } + FlatToken::Empty => {} + } + } + + if break_last_token > 0 { + let last_token = stack_top.inner.pop().unwrap(); + if let AttrTokenTree::Token(last_token, spacing) = last_token { + let (unglued, _) = last_token.kind.break_two_token_op(break_last_token).unwrap(); + + // Tokens are always ASCII chars, so we can use byte arithmetic here. + let mut first_span = last_token.span.shrink_to_lo(); + first_span = + first_span.with_hi(first_span.lo() + rustc_span::BytePos(break_last_token)); + + stack_top.inner.push(AttrTokenTree::Token(Token::new(unglued, first_span), spacing)); + } else { + panic!("Unexpected last token {last_token:?}") + } + } + AttrTokenStream::new(stack_top.inner) +} + /// Like `TokenTree`, but for `AttrTokenStream`. #[derive(Clone, Debug, Encodable, Decodable)] pub enum AttrTokenTree { @@ -233,35 +483,52 @@ fn attrs_and_tokens_to_token_trees( // Insert inner attribute tokens. if !inner_attrs.is_empty() { - let mut found = false; - // Check the last two trees (to account for a trailing semi) - for tree in res.iter_mut().rev().take(2) { - if let TokenTree::Delimited(span, spacing, delim, delim_tokens) = tree { - // Inner attributes are only supported on extern blocks, functions, - // impls, and modules. All of these have their inner attributes - // placed at the beginning of the rightmost outermost braced group: - // e.g. fn foo() { #![my_attr] } - // - // Therefore, we can insert them back into the right location - // without needing to do any extra position tracking. - // - // Note: Outline modules are an exception - they can - // have attributes like `#![my_attr]` at the start of a file. - // Support for custom attributes in this position is not - // properly implemented - we always synthesize fake tokens, - // so we never reach this code. + let found = insert_inner_attrs(inner_attrs, res); + assert!(found, "Failed to find trailing delimited group in: {res:?}"); + } + + // Inner attributes are only supported on blocks, functions, impls, and + // modules. All of these have their inner attributes placed at the + // beginning of the rightmost outermost braced group: + // e.g. `fn foo() { #![my_attr] }`. (Note: the braces may be within + // invisible delimiters.) + // + // Therefore, we can insert them back into the right location without + // needing to do any extra position tracking. + // + // Note: Outline modules are an exception - they can have attributes like + // `#![my_attr]` at the start of a file. Support for custom attributes in + // this position is not properly implemented - we always synthesize fake + // tokens, so we never reach this code. + fn insert_inner_attrs(inner_attrs: &[Attribute], tts: &mut Vec) -> bool { + for tree in tts.iter_mut().rev() { + if let TokenTree::Delimited(span, spacing, Delimiter::Brace, stream) = tree { + // Found it: the rightmost, outermost braced group. let mut tts = vec![]; for inner_attr in inner_attrs { tts.extend(inner_attr.token_trees()); } - tts.extend(delim_tokens.0.iter().cloned()); + tts.extend(stream.0.iter().cloned()); let stream = TokenStream::new(tts); - *tree = TokenTree::Delimited(*span, *spacing, *delim, stream); - found = true; - break; + *tree = TokenTree::Delimited(*span, *spacing, Delimiter::Brace, stream); + return true; + } else if let TokenTree::Delimited(span, spacing, Delimiter::Invisible(src), stream) = + tree + { + // Recurse inside invisible delimiters. + let mut vec: Vec<_> = stream.iter().cloned().collect(); + if insert_inner_attrs(inner_attrs, &mut vec) { + *tree = TokenTree::Delimited( + *span, + *spacing, + Delimiter::Invisible(*src), + TokenStream::new(vec), + ); + return true; + } } } - assert!(found, "Failed to find trailing delimited group in: {res:?}"); + false } } @@ -288,11 +555,6 @@ pub struct AttrsTarget { } /// 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 -/// backwards compatibility. #[derive(Clone, Debug, Default, Encodable, Decodable)] pub struct TokenStream(pub(crate) Arc>); @@ -459,68 +721,6 @@ impl TokenStream { TokenStream::new(tts) } - pub fn from_nonterminal_ast(nt: &Nonterminal) -> TokenStream { - match nt { - Nonterminal::NtItem(item) => TokenStream::from_ast(item), - Nonterminal::NtBlock(block) => TokenStream::from_ast(block), - Nonterminal::NtStmt(stmt) if let StmtKind::Empty = stmt.kind => { - // FIXME: Properly collect tokens for empty statements. - TokenStream::token_alone(token::Semi, stmt.span) - } - Nonterminal::NtStmt(stmt) => TokenStream::from_ast(stmt), - Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => TokenStream::from_ast(expr), - } - } - - fn flatten_token(token: &Token, spacing: Spacing) -> TokenTree { - match token.kind { - token::NtIdent(ident, is_raw) => { - TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span), spacing) - } - token::NtLifetime(ident, is_raw) => TokenTree::Delimited( - DelimSpan::from_single(token.span), - DelimSpacing::new(Spacing::JointHidden, spacing), - Delimiter::Invisible(InvisibleOrigin::FlattenToken), - TokenStream::token_alone(token::Lifetime(ident.name, is_raw), ident.span), - ), - token::Interpolated(ref nt) => TokenTree::Delimited( - DelimSpan::from_single(token.span), - DelimSpacing::new(Spacing::JointHidden, spacing), - Delimiter::Invisible(InvisibleOrigin::FlattenToken), - TokenStream::from_nonterminal_ast(&nt).flattened(), - ), - _ => TokenTree::Token(token.clone(), spacing), - } - } - - fn flatten_token_tree(tree: &TokenTree) -> TokenTree { - match tree { - TokenTree::Token(token, spacing) => TokenStream::flatten_token(token, *spacing), - TokenTree::Delimited(span, spacing, delim, tts) => { - TokenTree::Delimited(*span, *spacing, *delim, tts.flattened()) - } - } - } - - #[must_use] - pub fn flattened(&self) -> TokenStream { - fn can_skip(stream: &TokenStream) -> bool { - stream.iter().all(|tree| match tree { - TokenTree::Token(token, _) => !matches!( - token.kind, - token::NtIdent(..) | token::NtLifetime(..) | token::Interpolated(..) - ), - TokenTree::Delimited(.., inner) => can_skip(inner), - }) - } - - if can_skip(self) { - return self.clone(); - } - - self.iter().map(|tree| TokenStream::flatten_token_tree(tree)).collect() - } - // If `vec` is not empty, try to glue `tt` onto its last token. The return // value indicates if gluing took place. fn try_glue_to_last(vec: &mut Vec, tt: &TokenTree) -> bool { @@ -651,7 +851,7 @@ impl TokenStream { if attr_style == AttrStyle::Inner { vec![ TokenTree::token_joint(token::Pound, span), - TokenTree::token_joint_hidden(token::Not, span), + TokenTree::token_joint_hidden(token::Bang, span), body, ] } else { @@ -691,6 +891,104 @@ impl<'t> Iterator for TokenStreamIter<'t> { } } +#[derive(Clone, Debug)] +pub struct TokenTreeCursor { + stream: TokenStream, + /// Points to the current token tree in the stream. In `TokenCursor::curr`, + /// this can be any token tree. In `TokenCursor::stack`, this is always a + /// `TokenTree::Delimited`. + index: usize, +} + +impl TokenTreeCursor { + #[inline] + pub fn new(stream: TokenStream) -> Self { + TokenTreeCursor { stream, index: 0 } + } + + #[inline] + pub fn curr(&self) -> Option<&TokenTree> { + self.stream.get(self.index) + } + + pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { + self.stream.get(self.index + n) + } + + #[inline] + pub fn bump(&mut self) { + self.index += 1; + } +} + +/// A `TokenStream` cursor that produces `Token`s. It's a bit odd that +/// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b) +/// use this type to emit them as a linear sequence. But a linear sequence is +/// what the parser expects, for the most part. +#[derive(Clone, Debug)] +pub struct TokenCursor { + // Cursor for the current (innermost) token stream. The index within the + // cursor can point to any token tree in the stream (or one past the end). + // The delimiters for this token stream are found in `self.stack.last()`; + // if that is `None` we are in the outermost token stream which never has + // delimiters. + pub curr: TokenTreeCursor, + + // Token streams surrounding the current one. The index within each cursor + // always points to a `TokenTree::Delimited`. + pub stack: Vec, +} + +impl TokenCursor { + pub fn next(&mut self) -> (Token, Spacing) { + self.inlined_next() + } + + /// This always-inlined version should only be used on hot code paths. + #[inline(always)] + pub fn inlined_next(&mut self) -> (Token, Spacing) { + loop { + // FIXME: we currently don't return `Delimiter::Invisible` open/close delims. To fix + // #67062 we will need to, whereupon the `delim != Delimiter::Invisible` conditions + // below can be removed. + if let Some(tree) = self.curr.curr() { + match tree { + &TokenTree::Token(token, spacing) => { + debug_assert!(!token.kind.is_delim()); + let res = (token, spacing); + self.curr.bump(); + return res; + } + &TokenTree::Delimited(sp, spacing, delim, ref tts) => { + let trees = TokenTreeCursor::new(tts.clone()); + self.stack.push(mem::replace(&mut self.curr, trees)); + if !delim.skip() { + return (Token::new(delim.as_open_token_kind(), sp.open), spacing.open); + } + // No open delimiter to return; continue on to the next iteration. + } + }; + } else if let Some(parent) = self.stack.pop() { + // We have exhausted this token stream. Move back to its parent token stream. + let Some(&TokenTree::Delimited(span, spacing, delim, _)) = parent.curr() else { + panic!("parent should be Delimited") + }; + self.curr = parent; + self.curr.bump(); // move past the `Delimited` + if !delim.skip() { + return (Token::new(delim.as_close_token_kind(), span.close), spacing.close); + } + // No close delimiter to return; continue on to the next iteration. + } else { + // We have exhausted the outermost token stream. The use of + // `Spacing::Alone` is arbitrary and immaterial, because the + // `Eof` token's spacing is never used. + return (Token::new(token::Eof, DUMMY_SP), Spacing::Alone); + } + } + } +} + #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] pub struct DelimSpan { pub open: Span, @@ -737,6 +1035,7 @@ mod size_asserts { static_assert_size!(AttrTokenStream, 8); static_assert_size!(AttrTokenTree, 32); static_assert_size!(LazyAttrTokenStream, 8); + static_assert_size!(LazyAttrTokenStreamInner, 88); static_assert_size!(Option, 8); // must be small, used in many AST nodes static_assert_size!(TokenStream, 8); static_assert_size!(TokenTree, 32); diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 64f2a98b8a6fc..989ebe14bf8fc 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -108,6 +108,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool { Assign(e, _, _) | AssignOp(_, e, _) | Await(e, _) + | Use(e, _) | Binary(_, e, _) | Call(e, _) | Cast(e, _) @@ -181,11 +182,14 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { | Range(_, Some(e), _) | Ret(Some(e)) | Unary(_, e) - | Yield(Some(e)) | Yeet(Some(e)) | Become(e) => { expr = e; } + Yield(kind) => match kind.expr() { + Some(e) => expr = e, + None => break None, + }, Closure(closure) => { expr = &closure.body; } @@ -216,7 +220,6 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { Break(_, None) | Range(_, None, _) | Ret(None) - | Yield(None) | Array(_) | Call(_, _) | MethodCall(_) @@ -224,6 +227,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { | Lit(_) | Type(_, _) | Await(_, _) + | Use(_, _) | Field(_, _) | Index(_, _, _) | Underscore @@ -235,7 +239,9 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { | Yeet(None) | UnsafeBinderCast(..) | Err(_) - | Dummy => break None, + | Dummy => { + break None; + } } } } diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 6896ac723fa58..b8526cf9d9529 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -2,7 +2,7 @@ use std::{ascii, fmt, str}; -use rustc_lexer::unescape::{ +use rustc_literal_escaper::{ MixedUnit, Mode, byte_from_char, unescape_byte, unescape_char, unescape_mixed, unescape_unicode, }; use rustc_span::{Span, Symbol, kw, sym}; diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 69454967eed62..1e5f414fae1c7 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -1,7 +1,7 @@ use rustc_span::kw; -use crate::ast::{self, BinOpKind, RangeLimits}; -use crate::token::{self, BinOpToken, Token}; +use crate::ast::{self, AssignOpKind, BinOpKind, RangeLimits}; +use crate::token::{self, Token}; /// Associative operator. #[derive(Copy, Clone, PartialEq, Debug)] @@ -9,7 +9,7 @@ pub enum AssocOp { /// A binary op. Binary(BinOpKind), /// `?=` where ? is one of the assignable BinOps - AssignOp(BinOpKind), + AssignOp(AssignOpKind), /// `=` Assign, /// `as` @@ -34,26 +34,26 @@ impl AssocOp { use AssocOp::*; match t.kind { token::Eq => Some(Assign), - token::BinOp(BinOpToken::Plus) => Some(Binary(BinOpKind::Add)), - token::BinOp(BinOpToken::Minus) => Some(Binary(BinOpKind::Sub)), - token::BinOp(BinOpToken::Star) => Some(Binary(BinOpKind::Mul)), - token::BinOp(BinOpToken::Slash) => Some(Binary(BinOpKind::Div)), - token::BinOp(BinOpToken::Percent) => Some(Binary(BinOpKind::Rem)), - token::BinOp(BinOpToken::Caret) => Some(Binary(BinOpKind::BitXor)), - token::BinOp(BinOpToken::And) => Some(Binary(BinOpKind::BitAnd)), - token::BinOp(BinOpToken::Or) => Some(Binary(BinOpKind::BitOr)), - token::BinOp(BinOpToken::Shl) => Some(Binary(BinOpKind::Shl)), - token::BinOp(BinOpToken::Shr) => Some(Binary(BinOpKind::Shr)), - token::BinOpEq(BinOpToken::Plus) => Some(AssignOp(BinOpKind::Add)), - token::BinOpEq(BinOpToken::Minus) => Some(AssignOp(BinOpKind::Sub)), - token::BinOpEq(BinOpToken::Star) => Some(AssignOp(BinOpKind::Mul)), - token::BinOpEq(BinOpToken::Slash) => Some(AssignOp(BinOpKind::Div)), - token::BinOpEq(BinOpToken::Percent) => Some(AssignOp(BinOpKind::Rem)), - token::BinOpEq(BinOpToken::Caret) => Some(AssignOp(BinOpKind::BitXor)), - token::BinOpEq(BinOpToken::And) => Some(AssignOp(BinOpKind::BitAnd)), - token::BinOpEq(BinOpToken::Or) => Some(AssignOp(BinOpKind::BitOr)), - token::BinOpEq(BinOpToken::Shl) => Some(AssignOp(BinOpKind::Shl)), - token::BinOpEq(BinOpToken::Shr) => Some(AssignOp(BinOpKind::Shr)), + token::Plus => Some(Binary(BinOpKind::Add)), + token::Minus => Some(Binary(BinOpKind::Sub)), + token::Star => Some(Binary(BinOpKind::Mul)), + token::Slash => Some(Binary(BinOpKind::Div)), + token::Percent => Some(Binary(BinOpKind::Rem)), + token::Caret => Some(Binary(BinOpKind::BitXor)), + token::And => Some(Binary(BinOpKind::BitAnd)), + token::Or => Some(Binary(BinOpKind::BitOr)), + token::Shl => Some(Binary(BinOpKind::Shl)), + token::Shr => Some(Binary(BinOpKind::Shr)), + token::PlusEq => Some(AssignOp(AssignOpKind::AddAssign)), + token::MinusEq => Some(AssignOp(AssignOpKind::SubAssign)), + token::StarEq => Some(AssignOp(AssignOpKind::MulAssign)), + token::SlashEq => Some(AssignOp(AssignOpKind::DivAssign)), + token::PercentEq => Some(AssignOp(AssignOpKind::RemAssign)), + token::CaretEq => Some(AssignOp(AssignOpKind::BitXorAssign)), + token::AndEq => Some(AssignOp(AssignOpKind::BitAndAssign)), + token::OrEq => Some(AssignOp(AssignOpKind::BitOrAssign)), + token::ShlEq => Some(AssignOp(AssignOpKind::ShlAssign)), + token::ShrEq => Some(AssignOp(AssignOpKind::ShrAssign)), token::Lt => Some(Binary(BinOpKind::Lt)), token::Le => Some(Binary(BinOpKind::Le)), token::Ge => Some(Binary(BinOpKind::Ge)), diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 1cb32b56875c3..e43d7ae065d9e 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -16,6 +16,7 @@ pub use rustc_ast_ir::visit::VisitorResult; pub use rustc_ast_ir::{try_visit, visit_opt, walk_list, walk_visitable_list}; use rustc_span::{Ident, Span}; +use thin_vec::ThinVec; use crate::ast::*; use crate::ptr::P; @@ -23,7 +24,7 @@ use crate::ptr::P; #[derive(Copy, Clone, Debug, PartialEq)] pub enum AssocCtxt { Trait, - Impl, + Impl { of_trait: bool }, } #[derive(Copy, Clone, Debug, PartialEq)] @@ -65,7 +66,7 @@ impl BoundKind { #[derive(Copy, Clone, Debug)] pub enum FnKind<'a> { /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. - Fn(FnCtxt, &'a Ident, &'a Visibility, &'a Fn), + Fn(FnCtxt, &'a Visibility, &'a Fn), /// E.g., `|x, y| body`. Closure(&'a ClosureBinder, &'a Option, &'a FnDecl, &'a Expr), @@ -74,21 +75,21 @@ pub enum FnKind<'a> { impl<'a> FnKind<'a> { pub fn header(&self) -> Option<&'a FnHeader> { match *self { - FnKind::Fn(_, _, _, Fn { sig, .. }) => Some(&sig.header), + FnKind::Fn(_, _, Fn { sig, .. }) => Some(&sig.header), FnKind::Closure(..) => None, } } pub fn ident(&self) -> Option<&Ident> { match self { - FnKind::Fn(_, ident, ..) => Some(ident), + FnKind::Fn(_, _, Fn { ident, .. }) => Some(ident), _ => None, } } pub fn decl(&self) -> &'a FnDecl { match self { - FnKind::Fn(_, _, _, Fn { sig, .. }) => &sig.decl, + FnKind::Fn(_, _, Fn { sig, .. }) => &sig.decl, FnKind::Closure(_, _, decl, _) => decl, } } @@ -117,7 +118,6 @@ pub trait WalkItemKind { &'a self, span: Span, id: NodeId, - ident: &'a Ident, visibility: &'a Visibility, ctxt: Self::Ctxt, visitor: &mut V, @@ -315,6 +315,75 @@ pub trait Visitor<'ast>: Sized { } } +#[macro_export] +macro_rules! common_visitor_and_walkers { + ($(($mut: ident))? $Visitor:ident$(<$lt:lifetime>)?) => { + // this is only used by the MutVisitor. We include this symmetry here to make writing other functions easier + $(${ignore($lt)} + #[expect(unused, rustc::pass_by_value)] + #[inline] + )? + fn visit_span<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, span: &$($lt)? $($mut)? Span) $(-> >::Result)? { + $( + let _ = stringify!($mut); + visitor.visit_span(span); + )? + $(${ignore($lt)}V::Result::output())? + } + + // this is only used by the MutVisitor. We include this symmetry here to make writing other functions easier + $(${ignore($lt)} + #[expect(unused, rustc::pass_by_value)] + #[inline] + )? + fn visit_id<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, id: &$($lt)? $($mut)? NodeId) $(-> >::Result)? { + $( + let _ = stringify!($mut); + visitor.visit_id(id); + )? + $(${ignore($lt)}V::Result::output())? + } + + // this is only used by the MutVisitor. We include this symmetry here to make writing other functions easier + fn visit_safety<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, safety: &$($lt)? $($mut)? Safety) $(-> >::Result)? { + match safety { + Safety::Unsafe(span) => visit_span(vis, span), + Safety::Safe(span) => visit_span(vis, span), + Safety::Default => { $(${ignore($lt)}V::Result::output())? } + } + } + + fn visit_constness<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, constness: &$($lt)? $($mut)? Const) $(-> >::Result)? { + match constness { + Const::Yes(span) => visit_span(vis, span), + Const::No => { + $(>::Result::output())? + } + } + } + + pub fn walk_label<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, Label { ident }: &$($lt)? $($mut)? Label) $(-> >::Result)? { + visitor.visit_ident(ident) + } + + pub fn walk_fn_header<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, header: &$($lt)? $($mut)? FnHeader) $(-> >::Result)? { + let FnHeader { safety, coroutine_kind, constness, ext: _ } = header; + try_visit!(visit_constness(visitor, constness)); + if let Some(coroutine_kind) = coroutine_kind { + try_visit!(visitor.visit_coroutine_kind(coroutine_kind)); + } + visit_safety(visitor, safety) + } + + pub fn walk_lifetime<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, Lifetime { id, ident }: &$($lt)? $($mut)? Lifetime) $(-> >::Result)? { + try_visit!(visit_id(visitor, id)); + visitor.visit_ident(ident) + } + }; +} + +common_visitor_and_walkers!(Visitor<'a>); + pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) -> V::Result { let Crate { attrs, items, spans: _, id: _, is_placeholder: _ } = krate; walk_list!(visitor, visit_attribute, attrs); @@ -323,7 +392,7 @@ pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) -> V::R } pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) -> V::Result { - let Local { id: _, pat, ty, kind, span: _, colon_sp: _, attrs, tokens: _ } = local; + let Local { id: _, super_: _, pat, ty, kind, span: _, colon_sp: _, attrs, tokens: _ } = local; walk_list!(visitor, visit_attribute, attrs); try_visit!(visitor.visit_pat(pat)); visit_opt!(visitor, visit_ty, ty); @@ -334,15 +403,6 @@ pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) -> V::R V::Result::output() } -pub fn walk_label<'a, V: Visitor<'a>>(visitor: &mut V, Label { ident }: &'a Label) -> V::Result { - visitor.visit_ident(ident) -} - -pub fn walk_lifetime<'a, V: Visitor<'a>>(visitor: &mut V, lifetime: &'a Lifetime) -> V::Result { - let Lifetime { id: _, ident } = lifetime; - visitor.visit_ident(ident) -} - pub fn walk_poly_trait_ref<'a, V>(visitor: &mut V, trait_ref: &'a PolyTraitRef) -> V::Result where V: Visitor<'a>, @@ -363,49 +423,72 @@ impl WalkItemKind for ItemKind { &'a self, span: Span, id: NodeId, - ident: &'a Ident, vis: &'a Visibility, _ctxt: Self::Ctxt, visitor: &mut V, ) -> V::Result { match self { - ItemKind::ExternCrate(_rename) => {} + ItemKind::ExternCrate(_rename, ident) => try_visit!(visitor.visit_ident(ident)), ItemKind::Use(use_tree) => try_visit!(visitor.visit_use_tree(use_tree, id, false)), - ItemKind::Static(box StaticItem { ty, safety: _, mutability: _, expr }) => { + ItemKind::Static(box StaticItem { + ident, + ty, + safety: _, + mutability: _, + expr, + define_opaque, + }) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); + try_visit!(walk_define_opaques(visitor, define_opaque)); } - ItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => { + ItemKind::Const(box ConstItem { + defaultness: _, + ident, + generics, + ty, + expr, + define_opaque, + }) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); + try_visit!(walk_define_opaques(visitor, define_opaque)); } ItemKind::Fn(func) => { - let kind = FnKind::Fn(FnCtxt::Free, ident, vis, &*func); + let kind = FnKind::Fn(FnCtxt::Free, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } - ItemKind::Mod(_unsafety, mod_kind) => match mod_kind { - ModKind::Loaded(items, _inline, _inner_span, _) => { - walk_list!(visitor, visit_item, items); + ItemKind::Mod(_unsafety, ident, mod_kind) => { + try_visit!(visitor.visit_ident(ident)); + match mod_kind { + ModKind::Loaded(items, _inline, _inner_span, _) => { + walk_list!(visitor, visit_item, items); + } + ModKind::Unloaded => {} } - ModKind::Unloaded => {} - }, + } ItemKind::ForeignMod(ForeignMod { extern_span: _, safety: _, abi: _, items }) => { walk_list!(visitor, visit_foreign_item, items); } ItemKind::GlobalAsm(asm) => try_visit!(visitor.visit_inline_asm(asm)), ItemKind::TyAlias(box TyAlias { generics, + ident, bounds, ty, defaultness: _, where_clauses: _, }) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); visit_opt!(visitor, visit_ty, ty); } - ItemKind::Enum(enum_definition, generics) => { + ItemKind::Enum(ident, enum_definition, generics) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_enum_def(enum_definition)); } @@ -422,34 +505,54 @@ impl WalkItemKind for ItemKind { try_visit!(visitor.visit_generics(generics)); visit_opt!(visitor, visit_trait_ref, of_trait); try_visit!(visitor.visit_ty(self_ty)); - walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Impl); + walk_list!( + visitor, + visit_assoc_item, + items, + AssocCtxt::Impl { of_trait: of_trait.is_some() } + ); } - ItemKind::Struct(struct_definition, generics) - | ItemKind::Union(struct_definition, generics) => { + ItemKind::Struct(ident, struct_definition, generics) + | ItemKind::Union(ident, struct_definition, generics) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_variant_data(struct_definition)); } - ItemKind::Trait(box Trait { safety: _, is_auto: _, generics, bounds, items }) => { + ItemKind::Trait(box Trait { + safety: _, + is_auto: _, + ident, + generics, + bounds, + items, + }) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); walk_list!(visitor, visit_param_bound, bounds, BoundKind::SuperTraits); walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait); } - ItemKind::TraitAlias(generics, bounds) => { + ItemKind::TraitAlias(ident, generics, bounds) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); } ItemKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)), - ItemKind::MacroDef(ts) => try_visit!(visitor.visit_mac_def(ts, id)), + ItemKind::MacroDef(ident, ts) => { + try_visit!(visitor.visit_ident(ident)); + try_visit!(visitor.visit_mac_def(ts, id)) + } ItemKind::Delegation(box Delegation { id, qself, path, + ident, rename, body, from_glob: _, }) => { try_visit!(visitor.visit_qself(qself)); try_visit!(visitor.visit_path(path, *id)); + try_visit!(visitor.visit_ident(ident)); visit_opt!(visitor, visit_ident, rename); visit_opt!(visitor, visit_block, body); } @@ -565,6 +668,7 @@ pub fn walk_ty_pat<'a, V: Visitor<'a>>(visitor: &mut V, tp: &'a TyPat) -> V::Res visit_opt!(visitor, visit_anon_const, start); visit_opt!(visitor, visit_anon_const, end); } + TyPatKind::Or(variants) => walk_list!(visitor, visit_ty_pat, variants), TyPatKind::Err(_) => {} } V::Result::output() @@ -707,7 +811,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res try_visit!(visitor.visit_pat(subpattern)); try_visit!(visitor.visit_expr(guard_condition)); } - PatKind::Wild | PatKind::Rest | PatKind::Never => {} + PatKind::Missing | PatKind::Wild | PatKind::Rest | PatKind::Never => {} PatKind::Err(_guar) => {} PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { walk_list!(visitor, visit_pat, elems); @@ -723,27 +827,37 @@ impl WalkItemKind for ForeignItemKind { &'a self, span: Span, id: NodeId, - ident: &'a Ident, vis: &'a Visibility, _ctxt: Self::Ctxt, visitor: &mut V, ) -> V::Result { match self { - ForeignItemKind::Static(box StaticItem { ty, mutability: _, expr, safety: _ }) => { + ForeignItemKind::Static(box StaticItem { + ident, + ty, + mutability: _, + expr, + safety: _, + define_opaque, + }) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); + try_visit!(walk_define_opaques(visitor, define_opaque)); } ForeignItemKind::Fn(func) => { - let kind = FnKind::Fn(FnCtxt::Foreign, ident, vis, &*func); + let kind = FnKind::Fn(FnCtxt::Foreign, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } ForeignItemKind::TyAlias(box TyAlias { generics, + ident, bounds, ty, defaultness: _, where_clauses: _, }) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); visit_opt!(visitor, visit_ty, ty); @@ -833,7 +947,8 @@ pub fn walk_where_predicate<'a, V: Visitor<'a>>( visitor: &mut V, predicate: &'a WherePredicate, ) -> V::Result { - let WherePredicate { kind, id: _, span: _ } = predicate; + let WherePredicate { attrs, kind, id: _, span: _, is_placeholder: _ } = predicate; + walk_list!(visitor, visit_attribute, attrs); visitor.visit_where_predicate_kind(kind) } @@ -871,12 +986,6 @@ pub fn walk_fn_ret_ty<'a, V: Visitor<'a>>(visitor: &mut V, ret_ty: &'a FnRetTy) V::Result::output() } -pub fn walk_fn_header<'a, V: Visitor<'a>>(visitor: &mut V, fn_header: &'a FnHeader) -> V::Result { - let FnHeader { safety: _, coroutine_kind, constness: _, ext: _ } = fn_header; - visit_opt!(visitor, visit_coroutine_kind, coroutine_kind.as_ref()); - V::Result::output() -} - pub fn walk_fn_decl<'a, V: Visitor<'a>>( visitor: &mut V, FnDecl { inputs, output }: &'a FnDecl, @@ -889,16 +998,25 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu match kind { FnKind::Fn( _ctxt, - _ident, _vis, - Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, contract, body }, + Fn { + defaultness: _, + ident, + sig: FnSig { header, decl, span: _ }, + generics, + contract, + body, + define_opaque, + }, ) => { - // Identifier and visibility are visited as a part of the item. + // Visibility is visited as a part of the item. + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_fn_header(header)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_fn_decl(decl)); visit_opt!(visitor, visit_contract, contract); visit_opt!(visitor, visit_block, body); + try_visit!(walk_define_opaques(visitor, define_opaque)); } FnKind::Closure(binder, coroutine_kind, decl, body) => { try_visit!(visitor.visit_closure_binder(binder)); @@ -916,29 +1034,39 @@ impl WalkItemKind for AssocItemKind { &'a self, span: Span, id: NodeId, - ident: &'a Ident, vis: &'a Visibility, ctxt: Self::Ctxt, visitor: &mut V, ) -> V::Result { match self { - AssocItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => { + AssocItemKind::Const(box ConstItem { + defaultness: _, + ident, + generics, + ty, + expr, + define_opaque, + }) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); + try_visit!(walk_define_opaques(visitor, define_opaque)); } AssocItemKind::Fn(func) => { - let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, vis, &*func); + let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } AssocItemKind::Type(box TyAlias { generics, + ident, bounds, ty, defaultness: _, where_clauses: _, }) => { try_visit!(visitor.visit_generics(generics)); + try_visit!(visitor.visit_ident(ident)); walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); visit_opt!(visitor, visit_ty, ty); } @@ -949,12 +1077,14 @@ impl WalkItemKind for AssocItemKind { id, qself, path, + ident, rename, body, from_glob: _, }) => { try_visit!(visitor.visit_qself(qself)); try_visit!(visitor.visit_path(path, *id)); + try_visit!(visitor.visit_ident(ident)); visit_opt!(visitor, visit_ident, rename); visit_opt!(visitor, visit_block, body); } @@ -996,11 +1126,10 @@ fn walk_item_ctxt<'a, V: Visitor<'a>, K: WalkItemKind>( item: &'a Item, ctxt: K::Ctxt, ) -> V::Result { - let Item { id, span, ident, vis, attrs, kind, tokens: _ } = item; + let Item { id, span, vis, attrs, kind, tokens: _ } = item; walk_list!(visitor, visit_attribute, attrs); try_visit!(visitor.visit_vis(vis)); - try_visit!(visitor.visit_ident(ident)); - try_visit!(kind.walk(*span, *id, ident, vis, ctxt, visitor)); + try_visit!(kind.walk(*span, *id, vis, ctxt, visitor)); V::Result::output() } @@ -1024,7 +1153,7 @@ pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) } pub fn walk_block<'a, V: Visitor<'a>>(visitor: &mut V, block: &'a Block) -> V::Result { - let Block { stmts, id: _, rules: _, span: _, tokens: _, could_be_bare_literal: _ } = block; + let Block { stmts, id: _, rules: _, span: _, tokens: _ } = block; walk_list!(visitor, visit_stmt, stmts); V::Result::output() } @@ -1202,7 +1331,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V FnKind::Closure(binder, coroutine_kind, fn_decl, body), *span, *id - )) + )); } ExprKind::Block(block, opt_label) => { visit_opt!(visitor, visit_label, opt_label); @@ -1210,6 +1339,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V } ExprKind::Gen(_capt, body, _kind, _decl_span) => try_visit!(visitor.visit_block(body)), ExprKind::Await(expr, _span) => try_visit!(visitor.visit_expr(expr)), + ExprKind::Use(expr, _span) => try_visit!(visitor.visit_expr(expr)), ExprKind::Assign(lhs, rhs, _span) => { try_visit!(visitor.visit_expr(lhs)); try_visit!(visitor.visit_expr(rhs)); @@ -1257,8 +1387,8 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V try_visit!(visitor.visit_ty(container)); walk_list!(visitor, visit_ident, fields.iter()); } - ExprKind::Yield(optional_expression) => { - visit_opt!(visitor, visit_expr, optional_expression); + ExprKind::Yield(kind) => { + visit_opt!(visitor, visit_expr, kind.expr()); } ExprKind::Try(subexpression) => try_visit!(visitor.visit_expr(subexpression)), ExprKind::TryBlock(body) => try_visit!(visitor.visit_block(body)), @@ -1325,3 +1455,15 @@ pub fn walk_attr_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a AttrArgs) - } V::Result::output() } + +fn walk_define_opaques<'a, V: Visitor<'a>>( + visitor: &mut V, + define_opaque: &'a Option>, +) -> V::Result { + if let Some(define_opaque) = define_opaque { + for (id, path) in define_opaque { + try_visit!(visitor.visit_path(path, *id)); + } + } + V::Result::output() +} diff --git a/compiler/rustc_ast_ir/Cargo.toml b/compiler/rustc_ast_ir/Cargo.toml index f54e9687d8c7f..76bdd9f7eb6d3 100644 --- a/compiler/rustc_ast_ir/Cargo.toml +++ b/compiler/rustc_ast_ir/Cargo.toml @@ -8,7 +8,6 @@ edition = "2024" rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } -rustc_span = { path = "../rustc_span", optional = true } # tidy-alphabetical-end [features] @@ -17,5 +16,4 @@ nightly = [ "dep:rustc_serialize", "dep:rustc_data_structures", "dep:rustc_macros", - "dep:rustc_span", ] diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs index 9884e191ea7bd..0898433a74c53 100644 --- a/compiler/rustc_ast_ir/src/lib.rs +++ b/compiler/rustc_ast_ir/src/lib.rs @@ -9,18 +9,20 @@ #![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", feature(never_type))] #![cfg_attr(feature = "nightly", feature(rustc_attrs))] -#![warn(unreachable_pub)] // tidy-alphabetical-end #[cfg(feature = "nightly")] -use rustc_macros::{Decodable, Encodable, HashStable_NoContext}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; pub mod visit; /// The movability of a coroutine / closure literal: /// whether a coroutine contains self-references, causing it to be `!Unpin`. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum Movability { /// May contain self-references, `!Unpin`. Static, @@ -29,7 +31,10 @@ pub enum Movability { } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum Mutability { // N.B. Order is deliberate, so that Not < Mut Not, @@ -88,7 +93,10 @@ impl Mutability { } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum Pinnedness { Not, Pinned, diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index 2ec4f4b055514..6ac258155fe94 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -20,7 +20,6 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } -rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index 906fb213ee779..5ef76fb64aaf2 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -141,9 +141,6 @@ ast_lowering_never_pattern_with_guard = ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed in argument-position `impl Trait` -ast_lowering_no_precise_captures_on_rpitit = `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits - .note = currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope - ast_lowering_previously_used_here = previously used here ast_lowering_register1 = register `{$reg1_name}` @@ -185,8 +182,6 @@ ast_lowering_underscore_expr_lhs_assign = ast_lowering_unstable_inline_assembly = inline assembly is not stable yet on this architecture ast_lowering_unstable_inline_assembly_label_operand_with_outputs = using both label and output operands for inline assembly is unstable -ast_lowering_unstable_inline_assembly_label_operands = - label operands for inline assembly are unstable ast_lowering_unstable_may_unwind = the `may_unwind` option is unstable ast_lowering_use_angle_brackets = use angle brackets instead diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 87af7959a884d..af279e07acc6e 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -38,6 +38,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } if let Some(asm_arch) = asm_arch { // Inline assembly is currently only stable for these architectures. + // (See also compiletest's `has_asm_support`.) let is_stable = matches!( asm_arch, asm::InlineAsmArch::X86 @@ -469,22 +470,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - // Feature gate checking for asm goto. + // Feature gate checking for `asm_goto_with_outputs`. if let Some((_, op_sp)) = operands.iter().find(|(op, _)| matches!(op, hir::InlineAsmOperand::Label { .. })) { - if !self.tcx.features().asm_goto() { - feature_err( - sess, - sym::asm_goto, - *op_sp, - fluent::ast_lowering_unstable_inline_assembly_label_operands, - ) - .emit(); - } - - // In addition, check if an output operand is used. - // This is gated behind an additional feature. + // Check if an output operand is used. let output_operand_used = operands.iter().any(|(op, _)| { matches!( op, diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 1d9ca6bb9c8cb..c3222b79e55c9 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -95,6 +95,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_local(&mut self, l: &Local) -> &'hir hir::LetStmt<'hir> { // Let statements are allowed to have impl trait in bindings. + let super_ = l.super_; let ty = l.ty.as_ref().map(|t| { self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable)) }); @@ -109,7 +110,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let span = self.lower_span(l.span); let source = hir::LocalSource::Normal; self.lower_attrs(hir_id, &l.attrs, l.span); - self.arena.alloc(hir::LetStmt { hir_id, ty, pat, init, els, span, source }) + self.arena.alloc(hir::LetStmt { hir_id, super_, ty, pat, init, els, span, source }) } fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode { diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index d8b7cb0c3225b..93c627f64c967 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -47,7 +47,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_middle::span_bug; use rustc_middle::ty::{Asyncness, ResolverAstLowering}; -use rustc_span::{Ident, Span}; +use rustc_span::{Ident, Span, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; use super::{GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode}; @@ -56,29 +56,38 @@ use crate::{AllowReturnTypeNotation, ImplTraitPosition, ResolverAstLoweringExt}; pub(crate) struct DelegationResults<'hir> { pub body_id: hir::BodyId, pub sig: hir::FnSig<'hir>, + pub ident: Ident, pub generics: &'hir hir::Generics<'hir>, } impl<'hir> LoweringContext<'_, 'hir> { - pub(crate) fn delegation_has_self(&self, item_id: NodeId, path_id: NodeId, span: Span) -> bool { - let sig_id = self.get_delegation_sig_id(item_id, path_id, span); + /// Defines whether the delegatee is an associated function whose first parameter is `self`. + pub(crate) fn delegatee_is_method( + &self, + item_id: NodeId, + path_id: NodeId, + span: Span, + is_in_trait_impl: bool, + ) -> bool { + let sig_id = self.get_delegation_sig_id(item_id, path_id, span, is_in_trait_impl); let Ok(sig_id) = sig_id else { return false; }; - self.has_self(sig_id, span) + self.is_method(sig_id, span) } - fn has_self(&self, def_id: DefId, span: Span) -> bool { - if let Some(local_sig_id) = def_id.as_local() { - // The value may be missing due to recursive delegation. - // Error will be emitted later during HIR ty lowering. - self.resolver.delegation_fn_sigs.get(&local_sig_id).is_some_and(|sig| sig.has_self) - } else { - match self.tcx.def_kind(def_id) { - DefKind::Fn => false, - DefKind::AssocFn => self.tcx.associated_item(def_id).fn_has_self_parameter, - _ => span_bug!(span, "unexpected DefKind for delegation item"), - } + fn is_method(&self, def_id: DefId, span: Span) -> bool { + match self.tcx.def_kind(def_id) { + DefKind::Fn => false, + DefKind::AssocFn => match def_id.as_local() { + Some(local_def_id) => self + .resolver + .delegation_fn_sigs + .get(&local_def_id) + .is_some_and(|sig| sig.has_self), + None => self.tcx.associated_item(def_id).is_method(), + }, + _ => span_bug!(span, "unexpected DefKind for delegation item"), } } @@ -86,18 +95,19 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, delegation: &Delegation, item_id: NodeId, + is_in_trait_impl: bool, ) -> DelegationResults<'hir> { let span = self.lower_span(delegation.path.segments.last().unwrap().ident.span); - let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span); + let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span, is_in_trait_impl); match sig_id { Ok(sig_id) => { let (param_count, c_variadic) = self.param_count(sig_id); let decl = self.lower_delegation_decl(sig_id, param_count, c_variadic, span); let sig = self.lower_delegation_sig(sig_id, decl, span); let body_id = self.lower_delegation_body(delegation, param_count, span); - + let ident = self.lower_ident(delegation.ident); let generics = self.lower_delegation_generics(span); - DelegationResults { body_id, sig, generics } + DelegationResults { body_id, sig, ident, generics } } Err(err) => self.generate_delegation_error(err, span), } @@ -108,8 +118,9 @@ impl<'hir> LoweringContext<'_, 'hir> { item_id: NodeId, path_id: NodeId, span: Span, + is_in_trait_impl: bool, ) -> Result { - let sig_id = if self.is_in_trait_impl { item_id } else { path_id }; + let sig_id = if is_in_trait_impl { item_id } else { path_id }; self.get_resolution_id(sig_id, span) } @@ -188,14 +199,19 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::FnSig<'hir> { let header = if let Some(local_sig_id) = sig_id.as_local() { match self.resolver.delegation_fn_sigs.get(&local_sig_id) { - Some(sig) => self.lower_fn_header( - sig.header, + Some(sig) => { + let parent = self.tcx.parent(sig_id); // HACK: we override the default safety instead of generating attributes from the ether. // We are not forwarding the attributes, as the delegation fn sigs are collected on the ast, // and here we need the hir attributes. - if sig.target_feature { hir::Safety::Unsafe } else { hir::Safety::Safe }, - &[], - ), + let default_safety = + if sig.target_feature || self.tcx.def_kind(parent) == DefKind::ForeignMod { + hir::Safety::Unsafe + } else { + hir::Safety::Safe + }; + self.lower_fn_header(sig.header, default_safety, &[]) + } None => self.generate_header_error(), } } else { @@ -218,12 +234,13 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::FnSig { decl, header, span } } - fn generate_param(&mut self, span: Span) -> (hir::Param<'hir>, NodeId) { + fn generate_param(&mut self, idx: usize, span: Span) -> (hir::Param<'hir>, NodeId) { let pat_node_id = self.next_node_id(); let pat_id = self.lower_node_id(pat_node_id); + let ident = Ident::with_dummy_span(Symbol::intern(&format!("arg{idx}"))); let pat = self.arena.alloc(hir::Pat { hir_id: pat_id, - kind: hir::PatKind::Binding(hir::BindingMode::NONE, pat_id, Ident::empty(), None), + kind: hir::PatKind::Binding(hir::BindingMode::NONE, pat_id, ident, None), span, default_binding_modes: false, }); @@ -231,9 +248,9 @@ impl<'hir> LoweringContext<'_, 'hir> { (hir::Param { hir_id: self.next_id(), pat, ty_span: span, span }, pat_node_id) } - fn generate_arg(&mut self, param_id: HirId, span: Span) -> hir::Expr<'hir> { + fn generate_arg(&mut self, idx: usize, param_id: HirId, span: Span) -> hir::Expr<'hir> { let segments = self.arena.alloc_from_iter(iter::once(hir::PathSegment { - ident: Ident::empty(), + ident: Ident::with_dummy_span(Symbol::intern(&format!("arg{idx}"))), hir_id: self.next_id(), res: Res::Local(param_id), args: None, @@ -257,7 +274,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut args: Vec> = Vec::with_capacity(param_count); for idx in 0..param_count { - let (param, pat_node_id) = this.generate_param(span); + let (param, pat_node_id) = this.generate_param(idx, span); parameters.push(param); let arg = if let Some(block) = block @@ -273,7 +290,7 @@ impl<'hir> LoweringContext<'_, 'hir> { this.ident_and_label_to_local_id.insert(pat_node_id, param.pat.hir_id.local_id); this.lower_target_expr(&block) } else { - this.generate_arg(param.pat.hir_id, span) + this.generate_arg(idx, param.pat.hir_id, span) }; args.push(arg); } @@ -324,10 +341,11 @@ impl<'hir> LoweringContext<'_, 'hir> { let call = if self .get_resolution_id(delegation.id, span) - .and_then(|def_id| Ok(self.has_self(def_id, span))) + .and_then(|def_id| Ok(self.is_method(def_id, span))) .unwrap_or_default() && delegation.qself.is_none() && !has_generic_args + && !args.is_empty() { let ast_segment = delegation.path.segments.last().unwrap(); let segment = self.lower_path_segment( @@ -389,8 +407,9 @@ impl<'hir> LoweringContext<'_, 'hir> { let header = self.generate_header_error(); let sig = hir::FnSig { decl, header, span }; + let ident = Ident::dummy(); let body_id = self.lower_body(|this| (&[], this.mk_expr(hir::ExprKind::Err(err), span))); - DelegationResults { generics, body_id, sig } + DelegationResults { ident, generics, body_id, sig } } fn generate_header_error(&self) -> hir::FnHeader { diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index ceeb5dffbea04..576fa9731e906 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -444,14 +444,6 @@ pub(crate) struct NoPreciseCapturesOnApit { pub span: Span, } -#[derive(Diagnostic)] -#[diag(ast_lowering_no_precise_captures_on_rpitit)] -#[note] -pub(crate) struct NoPreciseCapturesOnRpitit { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(ast_lowering_yield_in_closure)] pub(crate) struct YieldInClosure { diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 9c3db7abc1ccc..9f3aed9216c2d 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -13,7 +13,7 @@ use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; use rustc_session::errors::report_lit_error; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use visit::{Visitor, walk_expr}; @@ -74,14 +74,16 @@ impl<'hir> LoweringContext<'_, 'hir> { // Merge attributes into the inner expression. if !e.attrs.is_empty() { let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]); - self.attrs.insert( - ex.hir_id.local_id, - &*self.arena.alloc_from_iter( - self.lower_attrs_vec(&e.attrs, e.span) - .into_iter() - .chain(old_attrs.iter().cloned()), - ), + let attrs = &*self.arena.alloc_from_iter( + self.lower_attrs_vec(&e.attrs, e.span) + .into_iter() + .chain(old_attrs.iter().cloned()), ); + if attrs.is_empty() { + return ex; + } + + self.attrs.insert(ex.hir_id.local_id, attrs); } return ex; } @@ -207,6 +209,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }, ), ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr), + ExprKind::Use(expr, use_kw_span) => self.lower_expr_use(*use_kw_span, expr), ExprKind::Closure(box Closure { binder, capture_clause, @@ -273,7 +276,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::Assign(el, er, span) => self.lower_expr_assign(el, er, *span, e.span), ExprKind::AssignOp(op, el, er) => hir::ExprKind::AssignOp( - self.lower_binop(*op), + self.lower_assign_op(*op), self.lower_expr(el), self.lower_expr(er), ), @@ -350,7 +353,7 @@ impl<'hir> LoweringContext<'_, 'hir> { rest, ) } - ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), + ExprKind::Yield(kind) => self.lower_expr_yield(e.span, kind.expr().map(|x| &**x)), ExprKind::Err(guar) => hir::ExprKind::Err(*guar), ExprKind::UnsafeBinderCast(kind, expr, ty) => hir::ExprKind::UnsafeBinderCast( @@ -396,12 +399,16 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, expr: &'hir hir::Expr<'hir>, span: Span, - check_ident: Ident, - check_hir_id: HirId, + cond_ident: Ident, + cond_hir_id: HirId, ) -> &'hir hir::Expr<'hir> { - let checker_fn = self.expr_ident(span, check_ident, check_hir_id); - let span = self.mark_span_with_reason(DesugaringKind::Contract, span, None); - self.expr_call(span, checker_fn, std::slice::from_ref(expr)) + let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id); + let call_expr = self.expr_call_lang_item_fn_mut( + span, + hir::LangItem::ContractCheckEnsures, + arena_vec![self; *cond_fn, *expr], + ); + self.arena.alloc(call_expr) } pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock { @@ -442,6 +449,10 @@ impl<'hir> LoweringContext<'_, 'hir> { Spanned { node: b.node, span: self.lower_span(b.span) } } + fn lower_assign_op(&mut self, a: AssignOp) -> AssignOp { + Spanned { node: a.node, span: self.lower_span(a.span) } + } + fn lower_legacy_const_generics( &mut self, mut f: Expr, @@ -481,9 +492,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut generic_args = ThinVec::new(); for (idx, arg) in args.iter().cloned().enumerate() { if legacy_args_idx.contains(&idx) { - let parent_def_id = self.current_hir_id_owner.def_id; let node_id = self.next_node_id(); - self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span); + self.create_def(node_id, None, DefKind::AnonConst, f.span); let mut visitor = WillCreateDefIdsVisitor {}; let const_value = if let ControlFlow::Break(span) = visitor.visit_expr(&arg) { AstP(Expr { @@ -1067,6 +1077,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ) } + fn lower_expr_use(&mut self, use_kw_span: Span, expr: &Expr) -> hir::ExprKind<'hir> { + hir::ExprKind::Use(self.lower_expr(expr), use_kw_span) + } + fn lower_expr_closure( &mut self, binder: &ClosureBinder, @@ -1690,6 +1704,19 @@ impl<'hir> LoweringContext<'_, 'hir> { let yielded = opt_expr.as_ref().map(|x| self.lower_expr(x)).unwrap_or_else(|| self.expr_unit(span)); + if !self.tcx.features().yield_expr() + && !self.tcx.features().coroutines() + && !self.tcx.features().gen_blocks() + { + rustc_session::parse::feature_err( + &self.tcx.sess, + sym::yield_expr, + span, + fluent_generated::ast_lowering_yield, + ) + .emit(); + } + let is_async_gen = match self.coroutine_kind { Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => false, Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)) => true, @@ -1714,28 +1741,8 @@ impl<'hir> LoweringContext<'_, 'hir> { None, ); } - Some(hir::CoroutineKind::Coroutine(_)) => { - if !self.tcx.features().coroutines() { - rustc_session::parse::feature_err( - &self.tcx.sess, - sym::coroutines, - span, - fluent_generated::ast_lowering_yield, - ) - .emit(); - } - false - } + Some(hir::CoroutineKind::Coroutine(_)) => false, None => { - if !self.tcx.features().coroutines() { - rustc_session::parse::feature_err( - &self.tcx.sess, - sym::coroutines, - span, - fluent_generated::ast_lowering_yield, - ) - .emit(); - } let suggestion = self.current_item.map(|s| s.shrink_to_lo()); self.dcx().emit_err(YieldInClosure { span, suggestion }); self.coroutine_kind = Some(hir::CoroutineKind::Coroutine(Movability::Movable)); @@ -2132,31 +2139,24 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[]))) } - pub(super) fn expr_usize(&mut self, sp: Span, value: usize) -> hir::Expr<'hir> { + fn expr_uint(&mut self, sp: Span, ty: ast::UintTy, value: u128) -> hir::Expr<'hir> { let lit = self.arena.alloc(hir::Lit { span: sp, - node: ast::LitKind::Int( - (value as u128).into(), - ast::LitIntType::Unsigned(ast::UintTy::Usize), - ), + node: ast::LitKind::Int(value.into(), ast::LitIntType::Unsigned(ty)), }); self.expr(sp, hir::ExprKind::Lit(lit)) } + pub(super) fn expr_usize(&mut self, sp: Span, value: usize) -> hir::Expr<'hir> { + self.expr_uint(sp, ast::UintTy::Usize, value as u128) + } + pub(super) fn expr_u32(&mut self, sp: Span, value: u32) -> hir::Expr<'hir> { - let lit = self.arena.alloc(hir::Lit { - span: sp, - node: ast::LitKind::Int( - u128::from(value).into(), - ast::LitIntType::Unsigned(ast::UintTy::U32), - ), - }); - self.expr(sp, hir::ExprKind::Lit(lit)) + self.expr_uint(sp, ast::UintTy::U32, value as u128) } - pub(super) fn expr_char(&mut self, sp: Span, value: char) -> hir::Expr<'hir> { - let lit = self.arena.alloc(hir::Lit { span: sp, node: ast::LitKind::Char(value) }); - self.expr(sp, hir::ExprKind::Lit(lit)) + pub(super) fn expr_u16(&mut self, sp: Span, value: u16) -> hir::Expr<'hir> { + self.expr_uint(sp, ast::UintTy::U16, value as u128) } pub(super) fn expr_str(&mut self, sp: Span, value: Symbol) -> hir::Expr<'hir> { diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index 07b94dbc2aebd..0de0319c66763 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -292,7 +292,7 @@ fn make_count<'hir>( hir::LangItem::FormatCount, sym::Is, )); - let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, *n)]); + let value = ctx.arena.alloc_from_iter([ctx.expr_u16(sp, *n)]); ctx.expr_call_mut(sp, count_is, value) } Some(FormatCount::Argument(arg)) => { @@ -323,14 +323,12 @@ fn make_count<'hir>( /// Generates /// /// ```text -/// ::…, // alignment -/// …u32, // flags -/// , // width -/// , // precision -/// ) +/// , +/// width: , +/// } /// ``` fn make_format_spec<'hir>( ctx: &mut LoweringContext<'_, 'hir>, @@ -361,34 +359,36 @@ fn make_format_spec<'hir>( zero_pad, debug_hex, } = &placeholder.format_options; - let fill = ctx.expr_char(sp, fill.unwrap_or(' ')); - let align = ctx.expr_lang_item_type_relative( - sp, - hir::LangItem::FormatAlignment, - match alignment { - Some(FormatAlignment::Left) => sym::Left, - Some(FormatAlignment::Right) => sym::Right, - Some(FormatAlignment::Center) => sym::Center, - None => sym::Unknown, - }, - ); - // This needs to match `Flag` in library/core/src/fmt/rt.rs. - let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32) - | ((sign == Some(FormatSign::Minus)) as u32) << 1 - | (alternate as u32) << 2 - | (zero_pad as u32) << 3 - | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4 - | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5; + let fill = fill.unwrap_or(' '); + // These need to match the constants in library/core/src/fmt/rt.rs. + let align = match alignment { + Some(FormatAlignment::Left) => 0, + Some(FormatAlignment::Right) => 1, + Some(FormatAlignment::Center) => 2, + None => 3, + }; + // This needs to match the constants in library/core/src/fmt/rt.rs. + let flags: u32 = fill as u32 + | ((sign == Some(FormatSign::Plus)) as u32) << 21 + | ((sign == Some(FormatSign::Minus)) as u32) << 22 + | (alternate as u32) << 23 + | (zero_pad as u32) << 24 + | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25 + | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26 + | (width.is_some() as u32) << 27 + | (precision.is_some() as u32) << 28 + | align << 29 + | 1 << 31; // Highest bit always set. let flags = ctx.expr_u32(sp, flags); let precision = make_count(ctx, sp, precision, argmap); let width = make_count(ctx, sp, width, argmap); - let format_placeholder_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative( - sp, - hir::LangItem::FormatPlaceholder, - sym::new, - )); - let args = ctx.arena.alloc_from_iter([position, fill, align, flags, precision, width]); - ctx.expr_call_mut(sp, format_placeholder_new, args) + let position = ctx.expr_field(Ident::new(sym::position, sp), ctx.arena.alloc(position), sp); + let flags = ctx.expr_field(Ident::new(sym::flags, sp), ctx.arena.alloc(flags), sp); + let precision = ctx.expr_field(Ident::new(sym::precision, sp), ctx.arena.alloc(precision), sp); + let width = ctx.expr_field(Ident::new(sym::width, sp), ctx.arena.alloc(width), sp); + let placeholder = ctx.arena.alloc(hir::QPath::LangItem(hir::LangItem::FormatPlaceholder, sp)); + let fields = ctx.arena.alloc_from_iter([position, flags, precision, width]); + ctx.expr(sp, hir::ExprKind::Struct(placeholder, fields, hir::StructTailExpr::None)) } fn expand_format_args<'hir>( diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index d1e23bf252229..26c0e7e5f82af 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -164,7 +164,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { fn visit_item(&mut self, i: &'hir Item<'hir>) { debug_assert_eq!(i.owner_id, self.owner); self.with_parent(i.hir_id(), |this| { - if let ItemKind::Struct(struct_def, _) = &i.kind { + if let ItemKind::Struct(_, struct_def, _) = &i.kind { // If this is a tuple or unit-like struct, register the constructor. if let Some(ctor_hir_id) = struct_def.ctor_hir_id() { this.insert(i.span, ctor_hir_id, Node::Ctor(struct_def)); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 15802101f71a5..e98d6c50ee791 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -3,12 +3,10 @@ use rustc_ast::ptr::P; use rustc_ast::visit::AssocCtxt; use rustc_ast::*; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; -use rustc_hir::PredicateOrigin; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; +use rustc_hir::{self as hir, HirId, LifetimeSource, PredicateOrigin}; use rustc_index::{IndexSlice, IndexVec}; -use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym}; @@ -72,47 +70,32 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { } } - pub(super) fn lower_node(&mut self, def_id: LocalDefId) -> hir::MaybeOwner<'hir> { + pub(super) fn lower_node(&mut self, def_id: LocalDefId) { let owner = self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom); if let hir::MaybeOwner::Phantom = owner { let node = self.ast_index[def_id]; match node { AstOwner::NonOwner => {} - AstOwner::Crate(c) => self.lower_crate(c), - AstOwner::Item(item) => self.lower_item(item), - AstOwner::AssocItem(item, ctxt) => self.lower_assoc_item(item, ctxt), - AstOwner::ForeignItem(item) => self.lower_foreign_item(item), + AstOwner::Crate(c) => { + debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID); + self.with_lctx(CRATE_NODE_ID, |lctx| { + let module = lctx.lower_mod(&c.items, &c.spans); + // FIXME(jdonszelman): is dummy span ever a problem here? + lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP); + hir::OwnerNode::Crate(module) + }) + } + AstOwner::Item(item) => { + self.with_lctx(item.id, |lctx| hir::OwnerNode::Item(lctx.lower_item(item))) + } + AstOwner::AssocItem(item, ctxt) => { + self.with_lctx(item.id, |lctx| lctx.lower_assoc_item(item, ctxt)) + } + AstOwner::ForeignItem(item) => self.with_lctx(item.id, |lctx| { + hir::OwnerNode::ForeignItem(lctx.lower_foreign_item(item)) + }), } } - - self.owners[def_id] - } - - #[instrument(level = "debug", skip(self, c))] - fn lower_crate(&mut self, c: &Crate) { - debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID); - self.with_lctx(CRATE_NODE_ID, |lctx| { - let module = lctx.lower_mod(&c.items, &c.spans); - // FIXME(jdonszelman): is dummy span ever a problem here? - lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP); - hir::OwnerNode::Crate(module) - }) - } - - #[instrument(level = "debug", skip(self))] - fn lower_item(&mut self, item: &Item) { - self.with_lctx(item.id, |lctx| hir::OwnerNode::Item(lctx.lower_item(item))) - } - - fn lower_assoc_item(&mut self, item: &AssocItem, ctxt: AssocCtxt) { - let def_id = self.resolver.node_id_to_def_id[&item.id]; - let parent_id = self.tcx.local_parent(def_id); - let parent_hir = self.lower_node(parent_id).unwrap(); - self.with_lctx(item.id, |lctx| lctx.lower_assoc_item(item, ctxt, parent_hir)) - } - - fn lower_foreign_item(&mut self, item: &ForeignItem) { - self.with_lctx(item.id, |lctx| hir::OwnerNode::ForeignItem(lctx.lower_foreign_item(item))) } } @@ -132,8 +115,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } pub(super) fn lower_item_ref(&mut self, i: &Item) -> SmallVec<[hir::ItemId; 1]> { - let mut node_ids = - smallvec![hir::ItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } }]; + let mut node_ids = smallvec![hir::ItemId { owner_id: self.owner_id(i.id) }]; if let ItemKind::Use(use_tree) = &i.kind { self.lower_item_id_use_tree(use_tree, &mut node_ids); } @@ -144,9 +126,7 @@ impl<'hir> LoweringContext<'_, 'hir> { match &tree.kind { UseTreeKind::Nested { items, .. } => { for &(ref nested, id) in items { - vec.push(hir::ItemId { - owner_id: hir::OwnerId { def_id: self.local_def_id(id) }, - }); + vec.push(hir::ItemId { owner_id: self.owner_id(id) }); self.lower_item_id_use_tree(nested, vec); } } @@ -155,14 +135,12 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> { - let mut ident = i.ident; let vis_span = self.lower_span(i.vis.span); let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); - let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, vis_span, &i.kind); + let kind = self.lower_item_kind(i.span, i.id, hir_id, attrs, vis_span, &i.kind); let item = hir::Item { owner_id: hir_id.expect_owner(), - ident: self.lower_ident(ident), kind, vis_span, span: self.lower_span(i.span), @@ -175,25 +153,44 @@ impl<'hir> LoweringContext<'_, 'hir> { span: Span, id: NodeId, hir_id: hir::HirId, - ident: &mut Ident, attrs: &'hir [hir::Attribute], vis_span: Span, i: &ItemKind, ) -> hir::ItemKind<'hir> { match i { - ItemKind::ExternCrate(orig_name) => hir::ItemKind::ExternCrate(*orig_name), + ItemKind::ExternCrate(orig_name, ident) => { + let ident = self.lower_ident(*ident); + hir::ItemKind::ExternCrate(*orig_name, ident) + } ItemKind::Use(use_tree) => { // Start with an empty prefix. let prefix = Path { segments: ThinVec::new(), span: use_tree.span, tokens: None }; - self.lower_use_tree(use_tree, &prefix, id, vis_span, ident, attrs) + self.lower_use_tree(use_tree, &prefix, id, vis_span, attrs) } - ItemKind::Static(box ast::StaticItem { ty: t, safety: _, mutability: m, expr: e }) => { + ItemKind::Static(box ast::StaticItem { + ident, + ty: t, + safety: _, + mutability: m, + expr: e, + define_opaque, + }) => { + let ident = self.lower_ident(*ident); let (ty, body_id) = self.lower_const_item(t, span, e.as_deref(), ImplTraitPosition::StaticTy); - hir::ItemKind::Static(ty, *m, body_id) + self.lower_define_opaque(hir_id, define_opaque); + hir::ItemKind::Static(ident, ty, *m, body_id) } - ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => { + ItemKind::Const(box ast::ConstItem { + ident, + generics, + ty, + expr, + define_opaque, + .. + }) => { + let ident = self.lower_ident(*ident); let (generics, (ty, body_id)) = self.lower_generics( generics, id, @@ -202,13 +199,16 @@ impl<'hir> LoweringContext<'_, 'hir> { this.lower_const_item(ty, span, expr.as_deref(), ImplTraitPosition::ConstTy) }, ); - hir::ItemKind::Const(ty, generics, body_id) + self.lower_define_opaque(hir_id, &define_opaque); + hir::ItemKind::Const(ident, ty, generics, body_id) } ItemKind::Fn(box Fn { sig: FnSig { decl, header, span: fn_sig_span }, + ident, generics, body, contract, + define_opaque, .. }) => { self.with_new_scopes(*fn_sig_span, |this| { @@ -237,15 +237,26 @@ impl<'hir> LoweringContext<'_, 'hir> { header: this.lower_fn_header(*header, hir::Safety::Safe, attrs), span: this.lower_span(*fn_sig_span), }; - hir::ItemKind::Fn { sig, generics, body: body_id, has_body: body.is_some() } + this.lower_define_opaque(hir_id, define_opaque); + let ident = this.lower_ident(*ident); + hir::ItemKind::Fn { + ident, + sig, + generics, + body: body_id, + has_body: body.is_some(), + } }) } - ItemKind::Mod(_, mod_kind) => match mod_kind { - ModKind::Loaded(items, _, spans, _) => { - hir::ItemKind::Mod(self.lower_mod(items, spans)) + ItemKind::Mod(_, ident, mod_kind) => { + let ident = self.lower_ident(*ident); + match mod_kind { + ModKind::Loaded(items, _, spans, _) => { + hir::ItemKind::Mod(ident, self.lower_mod(items, spans)) + } + ModKind::Unloaded => panic!("`mod` items should have been loaded by now"), } - ModKind::Unloaded => panic!("`mod` items should have been loaded by now"), - }, + } ItemKind::ForeignMod(fm) => hir::ItemKind::ForeignMod { abi: fm.abi.map_or(ExternAbi::FALLBACK, |abi| self.lower_abi(abi)), items: self @@ -258,7 +269,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_body(|this| (&[], this.expr(span, hir::ExprKind::InlineAsm(asm)))); hir::ItemKind::GlobalAsm { asm, fake_body } } - ItemKind::TyAlias(box TyAlias { generics, where_clauses, ty, .. }) => { + ItemKind::TyAlias(box TyAlias { ident, generics, where_clauses, ty, .. }) => { // We lower // // type Foo = impl Trait @@ -267,6 +278,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // // type Foo = Foo1 // opaque type Foo1: Trait + let ident = self.lower_ident(*ident); let mut generics = generics.clone(); add_ty_alias_where_clause(&mut generics, *where_clauses, true); let (generics, ty) = self.lower_generics( @@ -292,9 +304,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ), }, ); - hir::ItemKind::TyAlias(ty, generics) + hir::ItemKind::TyAlias(ident, ty, generics) } - ItemKind::Enum(enum_definition, generics) => { + ItemKind::Enum(ident, enum_definition, generics) => { + let ident = self.lower_ident(*ident); let (generics, variants) = self.lower_generics( generics, id, @@ -305,25 +318,27 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }, ); - hir::ItemKind::Enum(hir::EnumDef { variants }, generics) + hir::ItemKind::Enum(ident, hir::EnumDef { variants }, generics) } - ItemKind::Struct(struct_def, generics) => { + ItemKind::Struct(ident, struct_def, generics) => { + let ident = self.lower_ident(*ident); let (generics, struct_def) = self.lower_generics( generics, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| this.lower_variant_data(hir_id, struct_def), ); - hir::ItemKind::Struct(struct_def, generics) + hir::ItemKind::Struct(ident, struct_def, generics) } - ItemKind::Union(vdata, generics) => { + ItemKind::Union(ident, vdata, generics) => { + let ident = self.lower_ident(*ident); let (generics, vdata) = self.lower_generics( generics, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| this.lower_variant_data(hir_id, vdata), ); - hir::ItemKind::Union(vdata, generics) + hir::ItemKind::Union(ident, vdata, generics) } ItemKind::Impl(box Impl { safety, @@ -374,10 +389,11 @@ impl<'hir> LoweringContext<'_, 'hir> { (trait_ref, lowered_ty) }); - self.is_in_trait_impl = trait_ref.is_some(); - let new_impl_items = self - .arena - .alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item))); + let new_impl_items = self.arena.alloc_from_iter( + impl_items + .iter() + .map(|item| self.lower_impl_item_ref(item, trait_ref.is_some())), + ); // `defaultness.has_value()` is never called for an `impl`, always `true` in order // to not cause an assertion failure inside the `lower_defaultness` function. @@ -399,7 +415,8 @@ impl<'hir> LoweringContext<'_, 'hir> { items: new_impl_items, })) } - ItemKind::Trait(box Trait { is_auto, safety, generics, bounds, items }) => { + ItemKind::Trait(box Trait { is_auto, safety, ident, generics, bounds, items }) => { + let ident = self.lower_ident(*ident); let (generics, (safety, items, bounds)) = self.lower_generics( generics, id, @@ -416,9 +433,10 @@ impl<'hir> LoweringContext<'_, 'hir> { (safety, items, bounds) }, ); - hir::ItemKind::Trait(*is_auto, safety, generics, bounds, items) + hir::ItemKind::Trait(*is_auto, safety, ident, generics, bounds, items) } - ItemKind::TraitAlias(generics, bounds) => { + ItemKind::TraitAlias(ident, generics, bounds) => { + let ident = self.lower_ident(*ident); let (generics, bounds) = self.lower_generics( generics, id, @@ -430,9 +448,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }, ); - hir::ItemKind::TraitAlias(generics, bounds) + hir::ItemKind::TraitAlias(ident, generics, bounds) } - ItemKind::MacroDef(MacroDef { body, macro_rules }) => { + ItemKind::MacroDef(ident, MacroDef { body, macro_rules }) => { + let ident = self.lower_ident(*ident); let body = P(self.lower_delim_args(body)); let def_id = self.local_def_id(id); let def_kind = self.tcx.def_kind(def_id); @@ -443,11 +462,12 @@ impl<'hir> LoweringContext<'_, 'hir> { ); }; let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules }); - hir::ItemKind::Macro(macro_def, macro_kind) + hir::ItemKind::Macro(ident, macro_def, macro_kind) } ItemKind::Delegation(box delegation) => { - let delegation_results = self.lower_delegation(delegation, id); + let delegation_results = self.lower_delegation(delegation, id, false); hir::ItemKind::Fn { + ident: delegation_results.ident, sig: delegation_results.sig, generics: delegation_results.generics, body: delegation_results.body_id, @@ -478,7 +498,6 @@ impl<'hir> LoweringContext<'_, 'hir> { prefix: &Path, id: NodeId, vis_span: Span, - ident: &mut Ident, attrs: &'hir [hir::Attribute], ) -> hir::ItemKind<'hir> { let path = &tree.prefix; @@ -486,7 +505,7 @@ impl<'hir> LoweringContext<'_, 'hir> { match tree.kind { UseTreeKind::Simple(rename) => { - *ident = tree.ident(); + let mut ident = tree.ident(); // First, apply the prefix to the path. let mut path = Path { segments, span: path.span, tokens: None }; @@ -497,13 +516,14 @@ impl<'hir> LoweringContext<'_, 'hir> { { let _ = path.segments.pop(); if rename.is_none() { - *ident = path.segments.last().unwrap().ident; + ident = path.segments.last().unwrap().ident; } } let res = self.lower_import_res(id, path.span); let path = self.lower_use_path(res, &path, ParamMode::Explicit); - hir::ItemKind::Use(path, hir::UseKind::Single) + let ident = self.lower_ident(ident); + hir::ItemKind::Use(path, hir::UseKind::Single(ident)) } UseTreeKind::Glob => { let res = self.expect_full_res(id); @@ -542,7 +562,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // Add all the nested `PathListItem`s to the HIR. for &(ref use_tree, id) in trees { - let new_hir_id = self.local_def_id(id); + let owner_id = self.owner_id(id); // Each `use` import is an item and thus are owners of the // names in the path. Up to this point the nested import is @@ -550,20 +570,16 @@ impl<'hir> LoweringContext<'_, 'hir> { // own its own names, we have to adjust the owner before // lowering the rest of the import. self.with_hir_id_owner(id, |this| { - let mut ident = *ident; - // `prefix` is lowered multiple times, but in different HIR owners. // So each segment gets renewed `HirId` with the same // `ItemLocalId` and the new owner. (See `lower_node_id`) - let kind = - this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs); + let kind = this.lower_use_tree(use_tree, &prefix, id, vis_span, attrs); if !attrs.is_empty() { this.attrs.insert(hir::ItemLocalId::ZERO, attrs); } let item = hir::Item { - owner_id: hir::OwnerId { def_id: new_hir_id }, - ident: this.lower_ident(ident), + owner_id, kind, vis_span, span: this.lower_span(use_tree.span), @@ -592,29 +608,15 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn lower_assoc_item( - &mut self, - item: &AssocItem, - ctxt: AssocCtxt, - parent_hir: &'hir hir::OwnerInfo<'hir>, - ) -> hir::OwnerNode<'hir> { - let parent_item = parent_hir.node().expect_item(); - match parent_item.kind { - hir::ItemKind::Impl(impl_) => { - self.is_in_trait_impl = impl_.of_trait.is_some(); - } - hir::ItemKind::Trait(_, _, _, _, _) => {} - kind => { - span_bug!(item.span, "assoc item has unexpected kind of parent: {}", kind.descr()) - } - } - + fn lower_assoc_item(&mut self, item: &AssocItem, ctxt: AssocCtxt) -> hir::OwnerNode<'hir> { // Evaluate with the lifetimes in `params` in-scope. // This is used to track which lifetimes have already been defined, // and which need to be replicated when lowering an async fn. match ctxt { AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item)), - AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item)), + AssocCtxt::Impl { of_trait } => { + hir::OwnerNode::ImplItem(self.lower_impl_item(item, of_trait)) + } } } @@ -622,47 +624,61 @@ impl<'hir> LoweringContext<'_, 'hir> { let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); let owner_id = hir_id.expect_owner(); let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); - let item = hir::ForeignItem { - owner_id, - ident: self.lower_ident(i.ident), - kind: match &i.kind { - ForeignItemKind::Fn(box Fn { sig, generics, .. }) => { - let fdec = &sig.decl; - let itctx = ImplTraitContext::Universal; - let (generics, (decl, fn_args)) = - self.lower_generics(generics, i.id, itctx, |this| { - ( - // Disallow `impl Trait` in foreign items. - this.lower_fn_decl( - fdec, - i.id, - sig.span, - FnDeclKind::ExternFn, - None, - ), - this.lower_fn_params_to_names(fdec), - ) - }); + let (ident, kind) = match &i.kind { + ForeignItemKind::Fn(box Fn { sig, ident, generics, define_opaque, .. }) => { + let fdec = &sig.decl; + let itctx = ImplTraitContext::Universal; + let (generics, (decl, fn_args)) = + self.lower_generics(generics, i.id, itctx, |this| { + ( + // Disallow `impl Trait` in foreign items. + this.lower_fn_decl(fdec, i.id, sig.span, FnDeclKind::ExternFn, None), + this.lower_fn_params_to_idents(fdec), + ) + }); - // Unmarked safety in unsafe block defaults to unsafe. - let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe, attrs); + // Unmarked safety in unsafe block defaults to unsafe. + let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe, attrs); + if define_opaque.is_some() { + self.dcx().span_err(i.span, "foreign functions cannot define opaque types"); + } + + ( + ident, hir::ForeignItemKind::Fn( hir::FnSig { header, decl, span: self.lower_span(sig.span) }, fn_args, generics, - ) + ), + ) + } + ForeignItemKind::Static(box StaticItem { + ident, + ty, + mutability, + expr: _, + safety, + define_opaque, + }) => { + let ty = + self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); + let safety = self.lower_safety(*safety, hir::Safety::Unsafe); + if define_opaque.is_some() { + self.dcx().span_err(i.span, "foreign statics cannot define opaque types"); } - ForeignItemKind::Static(box StaticItem { ty, mutability, expr: _, safety }) => { - let ty = self - .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); - let safety = self.lower_safety(*safety, hir::Safety::Unsafe); + (ident, hir::ForeignItemKind::Static(ty, *mutability, safety)) + } + ForeignItemKind::TyAlias(box TyAlias { ident, .. }) => { + (ident, hir::ForeignItemKind::Type) + } + ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"), + }; - hir::ForeignItemKind::Static(ty, *mutability, safety) - } - ForeignItemKind::TyAlias(..) => hir::ForeignItemKind::Type, - ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"), - }, + let item = hir::ForeignItem { + owner_id, + ident: self.lower_ident(*ident), + kind, vis_span: self.lower_span(i.vis.span), span: self.lower_span(i.span), }; @@ -671,8 +687,10 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_foreign_item_ref(&mut self, i: &ForeignItem) -> hir::ForeignItemRef { hir::ForeignItemRef { - id: hir::ForeignItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } }, - ident: self.lower_ident(i.ident), + id: hir::ForeignItemId { owner_id: self.owner_id(i.id) }, + // `unwrap` is safe because `ForeignItemKind::MacCall` is the only foreign item kind + // without an identifier and it cannot reach here. + ident: self.lower_ident(i.kind.ident().unwrap()), span: self.lower_span(i.span), } } @@ -763,8 +781,15 @@ impl<'hir> LoweringContext<'_, 'hir> { let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); let trait_item_def_id = hir_id.expect_owner(); - let (generics, kind, has_default) = match &i.kind { - AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => { + let (ident, generics, kind, has_default) = match &i.kind { + AssocItemKind::Const(box ConstItem { + ident, + generics, + ty, + expr, + define_opaque, + .. + }) => { let (generics, kind) = self.lower_generics( generics, i.id, @@ -777,12 +802,26 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::TraitItemKind::Const(ty, body) }, ); - (generics, kind, expr.is_some()) + + if define_opaque.is_some() { + if expr.is_some() { + self.lower_define_opaque(hir_id, &define_opaque); + } else { + self.dcx().span_err( + i.span, + "only trait consts with default bodies can define opaque types", + ); + } + } + + (*ident, generics, kind, expr.is_some()) } - AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => { + AssocItemKind::Fn(box Fn { + sig, ident, generics, body: None, define_opaque, .. + }) => { // FIXME(contracts): Deny contract here since it won't apply to // any impl method or callees. - let names = self.lower_fn_params_to_names(&sig.decl); + let idents = self.lower_fn_params_to_idents(&sig.decl); let (generics, sig) = self.lower_method_sig( generics, sig, @@ -791,9 +830,28 @@ impl<'hir> LoweringContext<'_, 'hir> { sig.header.coroutine_kind, attrs, ); - (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false) + if define_opaque.is_some() { + self.dcx().span_err( + i.span, + "only trait methods with default bodies can define opaque types", + ); + } + ( + *ident, + generics, + hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(idents)), + false, + ) } - AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), contract, .. }) => { + AssocItemKind::Fn(box Fn { + sig, + ident, + generics, + body: Some(body), + contract, + define_opaque, + .. + }) => { let body_id = self.lower_maybe_coroutine_body( sig.span, i.span, @@ -812,9 +870,17 @@ impl<'hir> LoweringContext<'_, 'hir> { sig.header.coroutine_kind, attrs, ); - (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true) + self.lower_define_opaque(hir_id, &define_opaque); + ( + *ident, + generics, + hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), + true, + ) } - AssocItemKind::Type(box TyAlias { generics, where_clauses, bounds, ty, .. }) => { + AssocItemKind::Type(box TyAlias { + ident, generics, where_clauses, bounds, ty, .. + }) => { let mut generics = generics.clone(); add_ty_alias_where_clause(&mut generics, *where_clauses, false); let (generics, kind) = self.lower_generics( @@ -837,15 +903,15 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }, ); - (generics, kind, ty.is_some()) + (*ident, generics, kind, ty.is_some()) } AssocItemKind::Delegation(box delegation) => { - let delegation_results = self.lower_delegation(delegation, i.id); + let delegation_results = self.lower_delegation(delegation, i.id, false); let item_kind = hir::TraitItemKind::Fn( delegation_results.sig, hir::TraitFn::Provided(delegation_results.body_id), ); - (delegation_results.generics, item_kind, true) + (delegation.ident, delegation_results.generics, item_kind, true) } AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => { panic!("macros should have been expanded by now") @@ -854,7 +920,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let item = hir::TraitItem { owner_id: trait_item_def_id, - ident: self.lower_ident(i.ident), + ident: self.lower_ident(ident), generics, kind, span: self.lower_span(i.span), @@ -864,23 +930,28 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_trait_item_ref(&mut self, i: &AssocItem) -> hir::TraitItemRef { - let kind = match &i.kind { - AssocItemKind::Const(..) => hir::AssocItemKind::Const, - AssocItemKind::Type(..) => hir::AssocItemKind::Type, - AssocItemKind::Fn(box Fn { sig, .. }) => { - hir::AssocItemKind::Fn { has_self: sig.decl.has_self() } + let (ident, kind) = match &i.kind { + AssocItemKind::Const(box ConstItem { ident, .. }) => { + (*ident, hir::AssocItemKind::Const) } - AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn { - has_self: self.delegation_has_self(i.id, delegation.id, i.span), - }, + AssocItemKind::Type(box TyAlias { ident, .. }) => (*ident, hir::AssocItemKind::Type), + AssocItemKind::Fn(box Fn { ident, sig, .. }) => { + (*ident, hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }) + } + AssocItemKind::Delegation(box delegation) => ( + delegation.ident, + hir::AssocItemKind::Fn { + has_self: self.delegatee_is_method(i.id, delegation.id, i.span, false), + }, + ), AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => { panic!("macros should have been expanded by now") } }; - let id = hir::TraitItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } }; + let id = hir::TraitItemId { owner_id: self.owner_id(i.id) }; hir::TraitItemRef { id, - ident: self.lower_ident(i.ident), + ident: self.lower_ident(ident), span: self.lower_span(i.span), kind, } @@ -891,27 +962,49 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr(span, hir::ExprKind::Err(guar)) } - fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> { + fn lower_impl_item( + &mut self, + i: &AssocItem, + is_in_trait_impl: bool, + ) -> &'hir hir::ImplItem<'hir> { // Since `default impl` is not yet implemented, this is always true in impls. let has_value = true; let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); - let (generics, kind) = match &i.kind { - AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics( + let (ident, (generics, kind)) = match &i.kind { + AssocItemKind::Const(box ConstItem { + ident, generics, - i.id, - ImplTraitContext::Disallowed(ImplTraitPosition::Generic), - |this| { - let ty = - this.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy)); - let body = this.lower_const_body(i.span, expr.as_deref()); - - hir::ImplItemKind::Const(ty, body) - }, + ty, + expr, + define_opaque, + .. + }) => ( + *ident, + self.lower_generics( + generics, + i.id, + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + |this| { + let ty = this + .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy)); + let body = this.lower_const_body(i.span, expr.as_deref()); + this.lower_define_opaque(hir_id, &define_opaque); + hir::ImplItemKind::Const(ty, body) + }, + ), ), - AssocItemKind::Fn(box Fn { sig, generics, body, contract, .. }) => { + AssocItemKind::Fn(box Fn { + sig, + ident, + generics, + body, + contract, + define_opaque, + .. + }) => { let body_id = self.lower_maybe_coroutine_body( sig.span, i.span, @@ -926,49 +1019,56 @@ impl<'hir> LoweringContext<'_, 'hir> { generics, sig, i.id, - if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent }, + if is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent }, sig.header.coroutine_kind, attrs, ); + self.lower_define_opaque(hir_id, &define_opaque); - (generics, hir::ImplItemKind::Fn(sig, body_id)) + (*ident, (generics, hir::ImplItemKind::Fn(sig, body_id))) } - AssocItemKind::Type(box TyAlias { generics, where_clauses, ty, .. }) => { + AssocItemKind::Type(box TyAlias { ident, generics, where_clauses, ty, .. }) => { let mut generics = generics.clone(); add_ty_alias_where_clause(&mut generics, *where_clauses, false); - self.lower_generics( - &generics, - i.id, - ImplTraitContext::Disallowed(ImplTraitPosition::Generic), - |this| match ty { - None => { - let guar = this.dcx().span_delayed_bug( - i.span, - "expected to lower associated type, but it was missing", - ); - let ty = this.arena.alloc(this.ty(i.span, hir::TyKind::Err(guar))); - hir::ImplItemKind::Type(ty) - } - Some(ty) => { - let ty = this.lower_ty( - ty, - ImplTraitContext::OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { - parent: this.local_def_id(i.id), - in_assoc_ty: true, + ( + *ident, + self.lower_generics( + &generics, + i.id, + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + |this| match ty { + None => { + let guar = this.dcx().span_delayed_bug( + i.span, + "expected to lower associated type, but it was missing", + ); + let ty = this.arena.alloc(this.ty(i.span, hir::TyKind::Err(guar))); + hir::ImplItemKind::Type(ty) + } + Some(ty) => { + let ty = this.lower_ty( + ty, + ImplTraitContext::OpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias { + parent: this.local_def_id(i.id), + in_assoc_ty: true, + }, }, - }, - ); - hir::ImplItemKind::Type(ty) - } - }, + ); + hir::ImplItemKind::Type(ty) + } + }, + ), ) } AssocItemKind::Delegation(box delegation) => { - let delegation_results = self.lower_delegation(delegation, i.id); + let delegation_results = self.lower_delegation(delegation, i.id, is_in_trait_impl); ( - delegation_results.generics, - hir::ImplItemKind::Fn(delegation_results.sig, delegation_results.body_id), + delegation.ident, + ( + delegation_results.generics, + hir::ImplItemKind::Fn(delegation_results.sig, delegation_results.body_id), + ), ) } AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => { @@ -978,7 +1078,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let item = hir::ImplItem { owner_id: hir_id.expect_owner(), - ident: self.lower_ident(i.ident), + ident: self.lower_ident(ident), generics, kind, vis_span: self.lower_span(i.vis.span), @@ -988,10 +1088,12 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(item) } - fn lower_impl_item_ref(&mut self, i: &AssocItem) -> hir::ImplItemRef { + fn lower_impl_item_ref(&mut self, i: &AssocItem, is_in_trait_impl: bool) -> hir::ImplItemRef { hir::ImplItemRef { - id: hir::ImplItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } }, - ident: self.lower_ident(i.ident), + id: hir::ImplItemId { owner_id: self.owner_id(i.id) }, + // `unwrap` is safe because `AssocItemKind::{MacCall,DelegationMac}` are the only + // assoc item kinds without an identifier and they cannot reach here. + ident: self.lower_ident(i.kind.ident().unwrap()), span: self.lower_span(i.span), kind: match &i.kind { AssocItemKind::Const(..) => hir::AssocItemKind::Const, @@ -1000,7 +1102,12 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::AssocItemKind::Fn { has_self: sig.decl.has_self() } } AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn { - has_self: self.delegation_has_self(i.id, delegation.id, i.span), + has_self: self.delegatee_is_method( + i.id, + delegation.id, + i.span, + is_in_trait_impl, + ), }, AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => { panic!("macros should have been expanded by now") @@ -1087,8 +1194,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let precond = if let Some(req) = &contract.requires { // Lower the precondition check intrinsic. let lowered_req = this.lower_expr_mut(&req); + let req_span = this.mark_span_with_reason( + DesugaringKind::Contract, + lowered_req.span, + None, + ); let precond = this.expr_call_lang_item_fn_mut( - req.span, + req_span, hir::LangItem::ContractCheckRequires, &*arena_vec![this; lowered_req], ); @@ -1098,6 +1210,8 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let (postcond, body) = if let Some(ens) = &contract.ensures { let ens_span = this.lower_span(ens.span); + let ens_span = + this.mark_span_with_reason(DesugaringKind::Contract, ens_span, None); // Set up the postcondition `let` statement. let check_ident: Ident = Ident::from_str_and_span("__ensures_checker", ens_span); @@ -1184,7 +1298,9 @@ impl<'hir> LoweringContext<'_, 'hir> { // create a fake body so that the entire rest of the compiler doesn't have to deal with // this as a special case. return self.lower_fn_body(decl, contract, |this| { - if attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic) { + if attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)) + || this.tcx.is_sdylib_interface_build() + { let span = this.lower_span(span); let empty_block = hir::Block { hir_id: this.next_id(), @@ -1657,6 +1773,35 @@ impl<'hir> LoweringContext<'_, 'hir> { (lowered_generics, res) } + pub(super) fn lower_define_opaque( + &mut self, + hir_id: HirId, + define_opaque: &Option>, + ) { + assert_eq!(self.define_opaque, None); + assert!(hir_id.is_owner()); + let Some(define_opaque) = define_opaque.as_ref() else { + return; + }; + let define_opaque = define_opaque.iter().filter_map(|(id, path)| { + let res = self.resolver.get_partial_res(*id); + let Some(did) = res.and_then(|res| res.expect_full_res().opt_def_id()) else { + self.dcx().span_delayed_bug(path.span, "should have errored in resolve"); + return None; + }; + let Some(did) = did.as_local() else { + self.dcx().span_err( + path.span, + "only opaque types defined in the local crate can be defined", + ); + return None; + }; + Some((self.lower_span(path.span), did)) + }); + let define_opaque = self.arena.alloc_from_iter(define_opaque); + self.define_opaque = Some(define_opaque); + } + pub(super) fn lower_generic_bound_predicate( &mut self, ident: Ident, @@ -1675,7 +1820,6 @@ impl<'hir> LoweringContext<'_, 'hir> { let bounds = self.lower_param_bounds(bounds, itctx); - let ident = self.lower_ident(ident); let param_span = ident.span; // Reconstruct the span of the entire predicate from the individual generic bounds. @@ -1694,6 +1838,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let def_id = self.local_def_id(id).to_def_id(); let hir_id = self.next_id(); let res = Res::Def(DefKind::TyParam, def_id); + let ident = self.lower_ident(ident); let ty_path = self.arena.alloc(hir::Path { span: param_span, res, @@ -1712,9 +1857,9 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } GenericParamKind::Lifetime => { - let ident = self.lower_ident(ident); let lt_id = self.next_node_id(); - let lifetime = self.new_named_lifetime(id, lt_id, ident); + let lifetime = + self.new_named_lifetime(id, lt_id, ident, LifetimeSource::Other, ident.into()); hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { lifetime, bounds, @@ -1728,6 +1873,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> { let hir_id = self.lower_node_id(pred.id); let span = self.lower_span(pred.span); + self.lower_attrs(hir_id, &pred.attrs, span); let kind = self.arena.alloc(match &pred.kind { WherePredicateKind::BoundPredicate(WhereBoundPredicate { bound_generic_params, @@ -1746,7 +1892,11 @@ impl<'hir> LoweringContext<'_, 'hir> { }), WherePredicateKind::RegionPredicate(WhereRegionPredicate { lifetime, bounds }) => { hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { - lifetime: self.lower_lifetime(lifetime), + lifetime: self.lower_lifetime( + lifetime, + LifetimeSource::Other, + lifetime.ident.into(), + ), bounds: self.lower_param_bounds( bounds, ImplTraitContext::Disallowed(ImplTraitPosition::Bound), diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 13edcc10c9e2a..422e79ca82ffd 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -35,10 +35,9 @@ #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] +#![feature(exact_size_is_empty)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use std::sync::Arc; @@ -49,12 +48,14 @@ use rustc_attr_parsing::{AttributeParser, OmitDoc}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::spawn; use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; use rustc_hir::{ - self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, ParamName, TraitCandidate, + self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, + LifetimeSource, LifetimeSyntax, ParamName, TraitCandidate, }; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_macros::extension; @@ -98,6 +99,8 @@ struct LoweringContext<'a, 'hir> { /// Bodies inside the owner being lowered. bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>, + /// `#[define_opaque]` attributes + define_opaque: Option<&'hir [(Span, LocalDefId)]>, /// Attributes inside the owner being lowered. attrs: SortedMap, /// Collect items that were created by lowering the current owner. @@ -118,7 +121,6 @@ struct LoweringContext<'a, 'hir> { catch_scope: Option, loop_scope: Option, is_in_loop_condition: bool, - is_in_trait_impl: bool, is_in_dyn_type: bool, current_hir_id_owner: hir::OwnerId, @@ -136,6 +138,7 @@ struct LoweringContext<'a, 'hir> { allow_try_trait: Arc<[Symbol]>, allow_gen_future: Arc<[Symbol]>, + allow_pattern_type: Arc<[Symbol]>, allow_async_iterator: Arc<[Symbol]>, allow_for_await: Arc<[Symbol]>, allow_async_fn_traits: Arc<[Symbol]>, @@ -154,6 +157,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // HirId handling. bodies: Vec::new(), + define_opaque: None, attrs: SortedMap::default(), children: Vec::default(), contract_ensures: None, @@ -168,7 +172,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { catch_scope: None, loop_scope: None, is_in_loop_condition: false, - is_in_trait_impl: false, is_in_dyn_type: false, coroutine_kind: None, task_context: None, @@ -176,6 +179,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { impl_trait_defs: Vec::new(), impl_trait_bounds: Vec::new(), allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(), + allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(), allow_gen_future: if tcx.features().async_fn_track_caller() { [sym::gen_future, sym::closure_track_caller].into() } else { @@ -440,19 +444,24 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> { tcx.definitions_untracked().def_index_count(), ); + let mut lowerer = item::ItemLowerer { + tcx, + resolver: &mut resolver, + ast_index: &ast_index, + owners: &mut owners, + }; for def_id in ast_index.indices() { - item::ItemLowerer { - tcx, - resolver: &mut resolver, - ast_index: &ast_index, - owners: &mut owners, - } - .lower_node(def_id); + lowerer.lower_node(def_id); } - // Drop AST to free memory drop(ast_index); - sess.time("drop_ast", || drop(krate)); + + // Drop AST to free memory. It can be expensive so try to drop it on a separate thread. + let prof = sess.prof.clone(); + spawn(move || { + let _timer = prof.verbose_generic_activity("drop_ast"); + drop(krate); + }); // Don't hash unless necessary, because it's expensive. let opt_hir_hash = @@ -490,12 +499,12 @@ enum GenericArgsMode { impl<'a, 'hir> LoweringContext<'a, 'hir> { fn create_def( &mut self, - parent: LocalDefId, node_id: ast::NodeId, - name: Symbol, + name: Option, def_kind: DefKind, span: Span, ) -> LocalDefId { + let parent = self.current_hir_id_owner.def_id; debug_assert_ne!(node_id, ast::DUMMY_NODE_ID); assert!( self.opt_local_def_id(node_id).is_none(), @@ -505,7 +514,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.tcx.hir_def_key(self.local_def_id(node_id)), ); - let def_id = self.tcx.at(span).create_def(parent, name, def_kind).def_id(); + let def_id = self + .tcx + .at(span) + .create_def(parent, name, def_kind, None, &mut self.resolver.disambiguator) + .def_id(); debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id); self.resolver.node_id_to_def_id.insert(node_id, def_id); @@ -530,6 +543,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{node:?}`")) } + /// Given the id of an owner node in the AST, returns the corresponding `OwnerId`. + fn owner_id(&self, node: NodeId) -> hir::OwnerId { + hir::OwnerId { def_id: self.local_def_id(node) } + } + /// Freshen the `LoweringContext` and ready it to lower a nested item. /// The lowered item is registered into `self.children`. /// @@ -541,18 +559,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { owner: NodeId, f: impl FnOnce(&mut Self) -> hir::OwnerNode<'hir>, ) { - let def_id = self.local_def_id(owner); + let owner_id = self.owner_id(owner); let current_attrs = std::mem::take(&mut self.attrs); let current_bodies = std::mem::take(&mut self.bodies); + let current_define_opaque = std::mem::take(&mut self.define_opaque); let current_ident_and_label_to_local_id = std::mem::take(&mut self.ident_and_label_to_local_id); #[cfg(debug_assertions)] let current_node_id_to_local_id = std::mem::take(&mut self.node_id_to_local_id); let current_trait_map = std::mem::take(&mut self.trait_map); - let current_owner = - std::mem::replace(&mut self.current_hir_id_owner, hir::OwnerId { def_id }); + let current_owner = std::mem::replace(&mut self.current_hir_id_owner, owner_id); let current_local_counter = std::mem::replace(&mut self.item_local_id_counter, hir::ItemLocalId::new(1)); let current_impl_trait_defs = std::mem::take(&mut self.impl_trait_defs); @@ -570,7 +588,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } let item = f(self); - debug_assert_eq!(def_id, item.def_id().def_id); + debug_assert_eq!(owner_id, item.def_id()); // `f` should have consumed all the elements in these vectors when constructing `item`. debug_assert!(self.impl_trait_defs.is_empty()); debug_assert!(self.impl_trait_bounds.is_empty()); @@ -578,6 +596,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.attrs = current_attrs; self.bodies = current_bodies; + self.define_opaque = current_define_opaque; self.ident_and_label_to_local_id = current_ident_and_label_to_local_id; #[cfg(debug_assertions)] @@ -590,13 +609,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.impl_trait_defs = current_impl_trait_defs; self.impl_trait_bounds = current_impl_trait_bounds; - debug_assert!(!self.children.iter().any(|(id, _)| id == &def_id)); - self.children.push((def_id, hir::MaybeOwner::Owner(info))); + debug_assert!(!self.children.iter().any(|(id, _)| id == &owner_id.def_id)); + self.children.push((owner_id.def_id, hir::MaybeOwner::Owner(info))); } fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInfo<'hir> { let attrs = std::mem::take(&mut self.attrs); let mut bodies = std::mem::take(&mut self.bodies); + let define_opaque = std::mem::take(&mut self.define_opaque); let trait_map = std::mem::take(&mut self.trait_map); #[cfg(debug_assertions)] @@ -612,11 +632,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Don't hash unless necessary, because it's expensive. let (opt_hash_including_bodies, attrs_hash) = - self.tcx.hash_owner_nodes(node, &bodies, &attrs); + self.tcx.hash_owner_nodes(node, &bodies, &attrs, define_opaque); let num_nodes = self.item_local_id_counter.as_usize(); let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes); let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies }; - let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash }; + let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash, define_opaque }; self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map }) } @@ -770,9 +790,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { LifetimeRes::Fresh { param, kind, .. } => { // Late resolution delegates to us the creation of the `LocalDefId`. let _def_id = self.create_def( - self.current_hir_id_owner.def_id, param, - kw::UnderscoreLifetime, + Some(kw::UnderscoreLifetime), DefKind::LifetimeParam, ident.span, ); @@ -876,7 +895,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let ret = self.arena.alloc_from_iter(lowered_attrs); // this is possible if an item contained syntactical attribute, - // but none of them parse succesfully or all of them were ignored + // but none of them parse successfully or all of them were ignored // for not being built-in attributes at all. They could be remaining // unexpanded attributes used as markers in proc-macro derives for example. // This will have emitted some diagnostics for the misparse, but will then @@ -905,7 +924,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } fn lower_delim_args(&self, args: &DelimArgs) -> DelimArgs { - DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.flattened() } + args.clone() } /// Lower an associated item constraint. @@ -926,7 +945,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { if let Some(first_char) = constraint.ident.as_str().chars().next() && first_char.is_ascii_lowercase() { - tracing::info!(?data, ?data.inputs); let err = match (&data.inputs[..], &data.output) { ([_, ..], FnRetTy::Default(_)) => { errors::BadReturnTypeNotation::Inputs { span: data.inputs_span } @@ -1069,7 +1087,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { itctx: ImplTraitContext, ) -> hir::GenericArg<'hir> { match arg { - ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(lt)), + ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime( + lt, + LifetimeSource::Path { angle_brackets: hir::AngleBrackets::Full }, + lt.ident.into(), + )), ast::GenericArg::Type(ty) => { // We cannot just match on `TyKind::Infer` as `(_)` is represented as // `TyKind::Paren(TyKind::Infer)` and should also be lowered to `GenericArg::Infer` @@ -1094,7 +1116,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .and_then(|partial_res| partial_res.full_res()) { if !res.matches_ns(Namespace::TypeNS) - && path.is_potential_trivial_const_arg() + && path.is_potential_trivial_const_arg(false) { debug!( "lower_generic_arg: Lowering type argument as const argument: {:?}", @@ -1188,35 +1210,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Ref(region, mt) => { - let region = region.unwrap_or_else(|| { - let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) = - self.resolver.get_lifetime_res(t.id) - { - debug_assert_eq!(start.plus(1), end); - start - } else { - self.next_node_id() - }; - let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi(); - Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id } - }); - let lifetime = self.lower_lifetime(®ion); + let lifetime = self.lower_ty_direct_lifetime(t, *region); hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx)) } TyKind::PinnedRef(region, mt) => { - let region = region.unwrap_or_else(|| { - let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) = - self.resolver.get_lifetime_res(t.id) - { - debug_assert_eq!(start.plus(1), end); - start - } else { - self.next_node_id() - }; - let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi(); - Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id } - }); - let lifetime = self.lower_lifetime(®ion); + let lifetime = self.lower_ty_direct_lifetime(t, *region); let kind = hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx)); let span = self.lower_span(t.span); let arg = hir::Ty { kind, span, hir_id: self.next_id() }; @@ -1236,7 +1234,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { safety: self.lower_safety(f.safety, hir::Safety::Safe), abi: self.lower_extern(f.ext), decl: self.lower_fn_decl(&f.decl, t.id, t.span, FnDeclKind::Pointer, None), - param_names: self.lower_fn_params_to_names(&f.decl), + param_idents: self.lower_fn_params_to_idents(&f.decl), })) } TyKind::UnsafeBinder(f) => { @@ -1292,7 +1290,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } GenericBound::Outlives(lifetime) => { if lifetime_bound.is_none() { - lifetime_bound = Some(this.lower_lifetime(lifetime)); + lifetime_bound = Some(this.lower_lifetime( + lifetime, + LifetimeSource::Other, + lifetime.ident.into(), + )); } None } @@ -1365,7 +1367,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } TyKind::Pat(ty, pat) => { - hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat)) + hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat, ty.span)) } TyKind::MacCall(_) => { span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now") @@ -1383,6 +1385,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.lower_node_id(t.id) } } + fn lower_ty_direct_lifetime( + &mut self, + t: &Ty, + region: Option, + ) -> &'hir hir::Lifetime { + let (region, syntax) = match region { + Some(region) => (region, region.ident.into()), + + None => { + let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) = + self.resolver.get_lifetime_res(t.id) + { + debug_assert_eq!(start.plus(1), end); + start + } else { + self.next_node_id() + }; + let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi(); + let region = Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id }; + (region, LifetimeSyntax::Hidden) + } + }; + self.lower_lifetime(®ion, LifetimeSource::Reference, syntax) + } + /// Lowers a `ReturnPositionOpaqueTy` (`-> impl Trait`) or a `TypeAliasesOpaqueTy` (`type F = /// impl Trait`): this creates the associated Opaque Type (TAIT) definition and then returns a /// HIR type that references the TAIT. @@ -1430,28 +1457,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // frequently opened issues show. let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None); - // Feature gate for RPITIT + use<..> - match origin { - rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => { - if !self.tcx.features().precise_capturing_in_traits() - && let Some(span) = bounds.iter().find_map(|bound| match *bound { - ast::GenericBound::Use(_, span) => Some(span), - _ => None, - }) - { - let mut diag = - self.tcx.dcx().create_err(errors::NoPreciseCapturesOnRpitit { span }); - add_feature_diagnostics( - &mut diag, - self.tcx.sess, - sym::precise_capturing_in_traits, - ); - diag.emit(); - } - } - _ => {} - } - self.lower_opaque_inner(opaque_ty_node_id, origin, opaque_ty_span, |this| { this.lower_param_bounds(bounds, itctx) }) @@ -1486,9 +1491,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { precise_capturing_args: &[PreciseCapturingArg], ) -> &'hir [hir::PreciseCapturingArg<'hir>] { self.arena.alloc_from_iter(precise_capturing_args.iter().map(|arg| match arg { - PreciseCapturingArg::Lifetime(lt) => { - hir::PreciseCapturingArg::Lifetime(self.lower_lifetime(lt)) - } + PreciseCapturingArg::Lifetime(lt) => hir::PreciseCapturingArg::Lifetime( + self.lower_lifetime(lt, LifetimeSource::PreciseCapturing, lt.ident.into()), + ), PreciseCapturingArg::Arg(path, id) => { let [segment] = path.segments.as_slice() else { panic!(); @@ -1505,10 +1510,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { })) } - fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] { + fn lower_fn_params_to_idents(&mut self, decl: &FnDecl) -> &'hir [Option] { self.arena.alloc_from_iter(decl.inputs.iter().map(|param| match param.pat.kind { - PatKind::Ident(_, ident, _) => self.lower_ident(ident), - _ => Ident::new(kw::Empty, self.lower_span(param.pat.span)), + PatKind::Missing => None, + PatKind::Ident(_, ident, _) => Some(self.lower_ident(ident)), + PatKind::Wild => Some(Ident::new(kw::Underscore, self.lower_span(param.pat.span))), + _ => { + self.dcx().span_delayed_bug( + param.pat.span, + "non-missing/ident/wild param pat must trigger an error", + ); + None + } })) } @@ -1743,9 +1756,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> hir::GenericBound<'hir> { match tpb { GenericBound::Trait(p) => hir::GenericBound::Trait(self.lower_poly_trait_ref(p, itctx)), - GenericBound::Outlives(lifetime) => { - hir::GenericBound::Outlives(self.lower_lifetime(lifetime)) - } + GenericBound::Outlives(lifetime) => hir::GenericBound::Outlives(self.lower_lifetime( + lifetime, + LifetimeSource::OutlivesBound, + lifetime.ident.into(), + )), GenericBound::Use(args, span) => hir::GenericBound::Use( self.lower_precise_capturing_args(args), self.lower_span(*span), @@ -1753,39 +1768,28 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - fn lower_lifetime(&mut self, l: &Lifetime) -> &'hir hir::Lifetime { - let ident = self.lower_ident(l.ident); - self.new_named_lifetime(l.id, l.id, ident) + fn lower_lifetime( + &mut self, + l: &Lifetime, + source: LifetimeSource, + syntax: LifetimeSyntax, + ) -> &'hir hir::Lifetime { + self.new_named_lifetime(l.id, l.id, l.ident, source, syntax) } - #[instrument(level = "debug", skip(self))] - fn new_named_lifetime_with_res( + fn lower_lifetime_hidden_in_path( &mut self, id: NodeId, - ident: Ident, - res: LifetimeRes, + span: Span, + angle_brackets: AngleBrackets, ) -> &'hir hir::Lifetime { - let res = match res { - LifetimeRes::Param { param, .. } => hir::LifetimeName::Param(param), - LifetimeRes::Fresh { param, .. } => { - let param = self.local_def_id(param); - hir::LifetimeName::Param(param) - } - LifetimeRes::Infer => hir::LifetimeName::Infer, - LifetimeRes::Static { .. } => hir::LifetimeName::Static, - LifetimeRes::Error => hir::LifetimeName::Error, - res => panic!( - "Unexpected lifetime resolution {:?} for {:?} at {:?}", - res, ident, ident.span - ), - }; - - debug!(?res); - self.arena.alloc(hir::Lifetime { - hir_id: self.lower_node_id(id), - ident: self.lower_ident(ident), - res, - }) + self.new_named_lifetime( + id, + id, + Ident::new(kw::UnderscoreLifetime, span), + LifetimeSource::Path { angle_brackets }, + LifetimeSyntax::Hidden, + ) } #[instrument(level = "debug", skip(self))] @@ -1794,9 +1798,39 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { id: NodeId, new_id: NodeId, ident: Ident, + source: LifetimeSource, + syntax: LifetimeSyntax, ) -> &'hir hir::Lifetime { let res = self.resolver.get_lifetime_res(id).unwrap_or(LifetimeRes::Error); - self.new_named_lifetime_with_res(new_id, ident, res) + let res = match res { + LifetimeRes::Param { param, .. } => hir::LifetimeKind::Param(param), + LifetimeRes::Fresh { param, .. } => { + debug_assert_eq!(ident.name, kw::UnderscoreLifetime); + let param = self.local_def_id(param); + hir::LifetimeKind::Param(param) + } + LifetimeRes::Infer => { + debug_assert_eq!(ident.name, kw::UnderscoreLifetime); + hir::LifetimeKind::Infer + } + LifetimeRes::Static { .. } => { + debug_assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime)); + hir::LifetimeKind::Static + } + LifetimeRes::Error => hir::LifetimeKind::Error, + LifetimeRes::ElidedAnchor { .. } => { + panic!("Unexpected `ElidedAnchar` {:?} at {:?}", ident, ident.span); + } + }; + + debug!(?res); + self.arena.alloc(hir::Lifetime::new( + self.lower_node_id(new_id), + self.lower_ident(ident), + res, + source, + syntax, + )) } fn lower_generic_params_mut( @@ -2030,7 +2064,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } fn lower_array_length_to_const_arg(&mut self, c: &AnonConst) -> &'hir hir::ConstArg<'hir> { - match c.value.kind { + // We cannot just match on `ExprKind::Underscore` as `(_)` is represented as + // `ExprKind::Paren(ExprKind::Underscore)` and should also be lowered to `GenericArg::Infer` + match c.value.peel_parens().kind { ExprKind::Underscore => { if !self.tcx.features().generic_arg_infer() { feature_err( @@ -2061,8 +2097,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> &'hir hir::ConstArg<'hir> { let tcx = self.tcx; - // FIXME(min_generic_const_args): we only allow one-segment const paths for now - let ct_kind = if path.is_potential_trivial_const_arg() + let ct_kind = if path + .is_potential_trivial_const_arg(tcx.features().min_generic_const_args()) && (tcx.features().min_generic_const_args() || matches!(res, Res::Def(DefKind::ConstParam, _))) { @@ -2072,15 +2108,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { path, ParamMode::Optional, AllowReturnTypeNotation::No, - // FIXME(min_generic_const_args): update for `fn foo() -> Bar>` support + // FIXME(mgca): update for `fn foo() -> Bar>` support ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); hir::ConstArgKind::Path(qpath) } else { // Construct an AnonConst where the expr is the "ty"'s path. - - let parent_def_id = self.current_hir_id_owner.def_id; let node_id = self.next_node_id(); let span = self.lower_span(span); @@ -2088,8 +2122,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // We're lowering a const argument that was originally thought to be a type argument, // so the def collector didn't create the def ahead of time. That's why we have to do // it here. - let def_id = - self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, span); + let def_id = self.create_def(node_id, None, DefKind::AnonConst, span); let hir_id = self.lower_node_id(node_id); let path_expr = Expr { @@ -2136,19 +2169,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; let maybe_res = self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res()); - // FIXME(min_generic_const_args): we only allow one-segment const paths for now - if let ExprKind::Path(None, path) = &expr.kind - && path.is_potential_trivial_const_arg() + if let ExprKind::Path(qself, path) = &expr.kind + && path.is_potential_trivial_const_arg(tcx.features().min_generic_const_args()) && (tcx.features().min_generic_const_args() || matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _)))) { let qpath = self.lower_qpath( expr.id, - &None, + qself, path, ParamMode::Optional, AllowReturnTypeNotation::No, - // FIXME(min_generic_const_args): update for `fn foo() -> Bar>` support + // FIXME(mgca): update for `fn foo() -> Bar>` support ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); @@ -2216,6 +2248,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.attrs.insert(hir_id.local_id, a); } let local = hir::LetStmt { + super_: None, hir_id, init, pat, @@ -2382,11 +2415,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// when the bound is written, even if it is written with `'_` like in /// `Box`. In those cases, `lower_lifetime` is invoked. fn elided_dyn_bound(&mut self, span: Span) -> &'hir hir::Lifetime { - let r = hir::Lifetime { - hir_id: self.next_id(), - ident: Ident::new(kw::Empty, self.lower_span(span)), - res: hir::LifetimeName::ImplicitObjectLifetimeDefault, - }; + let r = hir::Lifetime::new( + self.next_id(), + Ident::new(kw::UnderscoreLifetime, self.lower_span(span)), + hir::LifetimeKind::ImplicitObjectLifetimeDefault, + LifetimeSource::Other, + LifetimeSyntax::Hidden, + ); debug!("elided_dyn_bound: r={:?}", r); self.arena.alloc(r) } diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 2dcfe7c745da5..58dea472f1d3b 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -3,11 +3,11 @@ use std::sync::Arc; use rustc_ast::ptr::P; use rustc_ast::*; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_hir as hir; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{self as hir, LangItem}; use rustc_middle::span_bug; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::{Ident, Span}; +use rustc_span::{DesugaringKind, Ident, Span}; use super::errors::{ ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding, @@ -26,6 +26,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let pat_hir_id = self.lower_node_id(pattern.id); let node = loop { match &pattern.kind { + PatKind::Missing => break hir::PatKind::Missing, PatKind::Wild => break hir::PatKind::Wild, PatKind::Never => break hir::PatKind::Never, PatKind::Ident(binding_mode, ident, sub) => { @@ -430,22 +431,128 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind }) } - pub(crate) fn lower_ty_pat(&mut self, pattern: &TyPat) -> &'hir hir::TyPat<'hir> { - self.arena.alloc(self.lower_ty_pat_mut(pattern)) + pub(crate) fn lower_ty_pat( + &mut self, + pattern: &TyPat, + base_type: Span, + ) -> &'hir hir::TyPat<'hir> { + self.arena.alloc(self.lower_ty_pat_mut(pattern, base_type)) } - fn lower_ty_pat_mut(&mut self, pattern: &TyPat) -> hir::TyPat<'hir> { + fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'hir> { // loop here to avoid recursion let pat_hir_id = self.lower_node_id(pattern.id); let node = match &pattern.kind { - TyPatKind::Range(e1, e2, Spanned { node: end, .. }) => hir::TyPatKind::Range( - e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)), - e2.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)), - self.lower_range_end(end, e2.is_some()), + TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range( + e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)).unwrap_or_else(|| { + self.lower_ty_pat_range_end( + hir::LangItem::RangeMin, + span.shrink_to_lo(), + base_type, + ) + }), + e2.as_deref() + .map(|e| match end { + RangeEnd::Included(..) => self.lower_anon_const_to_const_arg(e), + RangeEnd::Excluded => self.lower_excluded_range_end(e), + }) + .unwrap_or_else(|| { + self.lower_ty_pat_range_end( + hir::LangItem::RangeMax, + span.shrink_to_hi(), + base_type, + ) + }), ), + TyPatKind::Or(variants) => { + hir::TyPatKind::Or(self.arena.alloc_from_iter( + variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)), + )) + } TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar), }; hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) } } + + /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1). + /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges. + fn lower_excluded_range_end(&mut self, e: &AnonConst) -> &'hir hir::ConstArg<'hir> { + let span = self.lower_span(e.value.span); + let unstable_span = self.mark_span_with_reason( + DesugaringKind::PatTyRange, + span, + Some(Arc::clone(&self.allow_pattern_type)), + ); + let anon_const = self.with_new_scopes(span, |this| { + let def_id = this.local_def_id(e.id); + let hir_id = this.lower_node_id(e.id); + let body = this.lower_body(|this| { + // Need to use a custom function as we can't just subtract `1` from a `char`. + let kind = hir::ExprKind::Path(this.make_lang_item_qpath( + hir::LangItem::RangeSub, + unstable_span, + None, + )); + let fn_def = this.arena.alloc(hir::Expr { hir_id: this.next_id(), kind, span }); + let args = this.arena.alloc([this.lower_expr_mut(&e.value)]); + ( + &[], + hir::Expr { + hir_id: this.next_id(), + kind: hir::ExprKind::Call(fn_def, args), + span, + }, + ) + }); + hir::AnonConst { def_id, hir_id, body, span } + }); + self.arena.alloc(hir::ConstArg { + hir_id: self.next_id(), + kind: hir::ConstArgKind::Anon(self.arena.alloc(anon_const)), + }) + } + + /// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`), + /// we instead use a constant of the MAX/MIN of the type. + /// This way the type system does not have to handle the lack of a start/end. + fn lower_ty_pat_range_end( + &mut self, + lang_item: LangItem, + span: Span, + base_type: Span, + ) -> &'hir hir::ConstArg<'hir> { + let node_id = self.next_node_id(); + + // Add a definition for the in-band const def. + // We're generating a range end that didn't exist in the AST, + // so the def collector didn't create the def ahead of time. That's why we have to do + // it here. + let def_id = self.create_def(node_id, None, DefKind::AnonConst, span); + let hir_id = self.lower_node_id(node_id); + + let unstable_span = self.mark_span_with_reason( + DesugaringKind::PatTyRange, + self.lower_span(span), + Some(Arc::clone(&self.allow_pattern_type)), + ); + let span = self.lower_span(base_type); + + let path_expr = hir::Expr { + hir_id: self.next_id(), + kind: hir::ExprKind::Path(self.make_lang_item_qpath(lang_item, unstable_span, None)), + span, + }; + + let ct = self.with_new_scopes(span, |this| { + self.arena.alloc(hir::AnonConst { + def_id, + hir_id, + body: this.lower_body(|_this| (&[], path_expr)), + span, + }) + }); + let hir_id = self.next_id(); + self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id }) + } } diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 6b6244b05aa9b..5cda64ce7b4ba 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -1,13 +1,12 @@ use std::sync::Arc; use rustc_ast::{self as ast, *}; -use rustc_hir as hir; -use rustc_hir::GenericArg; use rustc_hir::def::{DefKind, PartialRes, Res}; use rustc_hir::def_id::DefId; -use rustc_middle::span_bug; +use rustc_hir::{self as hir, GenericArg}; +use rustc_middle::{span_bug, ty}; use rustc_session::parse::add_feature_diagnostics; -use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym}; +use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym}; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -268,7 +267,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } GenericArgs::Parenthesized(data) => match generic_args_mode { GenericArgsMode::ReturnTypeNotation => { - tracing::info!(?data, ?data.inputs); let err = match (&data.inputs[..], &data.output) { ([_, ..], FnRetTy::Default(_)) => { BadReturnTypeNotation::Inputs { span: data.inputs_span } @@ -434,27 +432,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Note: these spans are used for diagnostics when they can't be inferred. // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label - let elided_lifetime_span = if generic_args.span.is_empty() { - // If there are no brackets, use the identifier span. + let (elided_lifetime_span, angle_brackets) = if generic_args.span.is_empty() { + // No brackets, e.g. `Path`: use an empty span just past the end of the identifier. // HACK: we use find_ancestor_inside to properly suggest elided spans in paths // originating from macros, since the segment's span might be from a macro arg. - segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span) - } else if generic_args.is_empty() { - // If there are brackets, but not generic arguments, then use the opening bracket - generic_args.span.with_hi(generic_args.span.lo() + BytePos(1)) + ( + segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span), + hir::AngleBrackets::Missing, + ) } else { - // Else use an empty span right after the opening bracket. - generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo() + // Brackets, e.g. `Path<>` or `Path`: use an empty span just after the `<`. + ( + generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo(), + if generic_args.is_empty() { + hir::AngleBrackets::Empty + } else { + hir::AngleBrackets::Full + }, + ) }; generic_args.args.insert_many( 0, - (start.as_u32()..end.as_u32()).map(|i| { - let id = NodeId::from_u32(i); - let l = self.lower_lifetime(&Lifetime { - id, - ident: Ident::new(kw::Empty, elided_lifetime_span), - }); + (start..end).map(|id| { + let l = + self.lower_lifetime_hidden_in_path(id, elided_lifetime_span, angle_brackets); GenericArg::Lifetime(l) }), ); @@ -595,14 +597,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// lowering of `async Fn()` bounds to desugar to another trait like `LendingFn`. fn map_trait_to_async_trait(&self, def_id: DefId) -> Option { let lang_items = self.tcx.lang_items(); - if Some(def_id) == lang_items.fn_trait() { - lang_items.async_fn_trait() - } else if Some(def_id) == lang_items.fn_mut_trait() { - lang_items.async_fn_mut_trait() - } else if Some(def_id) == lang_items.fn_once_trait() { - lang_items.async_fn_once_trait() - } else { - None + match self.tcx.fn_trait_kind_from_def_id(def_id)? { + ty::ClosureKind::Fn => lang_items.async_fn_trait(), + ty::ClosureKind::FnMut => lang_items.async_fn_mut_trait(), + ty::ClosureKind::FnOnce => lang_items.async_fn_once_trait(), } } } diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs index a2004bbb39f09..eb052ba1c6d78 100644 --- a/compiler/rustc_ast_lowering/src/stability.rs +++ b/compiler/rustc_ast_lowering/src/stability.rs @@ -79,10 +79,6 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> { | ExternAbi::SysV64 { .. } | ExternAbi::System { .. } | ExternAbi::EfiApi => Ok(()), - // implementation details - ExternAbi::RustIntrinsic => { - Err(UnstableAbi { abi, feature: sym::intrinsics, explain: GateReason::ImplDetail }) - } ExternAbi::Unadjusted => { Err(UnstableAbi { abi, feature: sym::abi_unadjusted, explain: GateReason::ImplDetail }) } diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 25944392a52a9..80754a8f65a69 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -79,6 +79,10 @@ ast_passes_extern_types_cannot = `type`s inside `extern` blocks cannot have {$de .suggestion = remove the {$remove_descr} .label = `extern` block begins here +ast_passes_extern_without_abi = `extern` declarations without an explicit ABI are disallowed + .suggestion = specify an ABI + .help = prior to Rust 2024, a default ABI was inferred + ast_passes_feature_on_non_nightly = `#![feature]` may not be used on the {$channel} release channel .suggestion = remove the attribute .stable_since = the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index f9f4035cb22f0..cbf4f2f5eb2be 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -47,14 +47,14 @@ enum SelfSemantic { } enum TraitOrTraitImpl { - Trait { span: Span, constness: Option }, - TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref: Span }, + Trait { span: Span, constness_span: Option }, + TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span }, } impl TraitOrTraitImpl { fn constness(&self) -> Option { match self { - Self::Trait { constness: Some(span), .. } + Self::Trait { constness_span: Some(span), .. } | Self::TraitImpl { constness: Const::Yes(span), .. } => Some(*span), _ => None, } @@ -66,7 +66,7 @@ struct AstValidator<'a> { features: &'a Features, /// The span of the `extern` in an `extern { ... }` block, if any. - extern_mod: Option, + extern_mod_span: Option, outer_trait_or_trait_impl: Option, @@ -75,13 +75,17 @@ struct AstValidator<'a> { /// Used to ban nested `impl Trait`, e.g., `impl Into`. /// Nested `impl Trait` _is_ allowed in associated type position, /// e.g., `impl Iterator`. - outer_impl_trait: Option, + outer_impl_trait_span: Option, disallow_tilde_const: Option, /// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe. extern_mod_safety: Option, + lint_node_id: NodeId, + + is_sdylib_interface: bool, + lint_buffer: &'a mut LintBuffer, } @@ -96,17 +100,22 @@ impl<'a> AstValidator<'a> { trait_.map(|(constness, polarity, trait_ref)| TraitOrTraitImpl::TraitImpl { constness, polarity, - trait_ref: trait_ref.path.span, + trait_ref_span: trait_ref.path.span, }), ); f(self); self.outer_trait_or_trait_impl = old; } - fn with_in_trait(&mut self, span: Span, constness: Option, f: impl FnOnce(&mut Self)) { + fn with_in_trait( + &mut self, + span: Span, + constness_span: Option, + f: impl FnOnce(&mut Self), + ) { let old = mem::replace( &mut self.outer_trait_or_trait_impl, - Some(TraitOrTraitImpl::Trait { span, constness }), + Some(TraitOrTraitImpl::Trait { span, constness_span }), ); f(self); self.outer_trait_or_trait_impl = old; @@ -170,10 +179,10 @@ impl<'a> AstValidator<'a> { Err(errors::WhereClauseBeforeTypeAlias { span, sugg }) } - fn with_impl_trait(&mut self, outer: Option, f: impl FnOnce(&mut Self)) { - let old = mem::replace(&mut self.outer_impl_trait, outer); + fn with_impl_trait(&mut self, outer_span: Option, f: impl FnOnce(&mut Self)) { + let old = mem::replace(&mut self.outer_impl_trait_span, outer_span); f(self); - self.outer_impl_trait = old; + self.outer_impl_trait_span = old; } // Mirrors `visit::walk_ty`, but tracks relevant state. @@ -239,7 +248,7 @@ impl<'a> AstValidator<'a> { fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, Option, bool)) { for Param { pat, .. } in &decl.inputs { match pat.kind { - PatKind::Ident(BindingMode::NONE, _, None) | PatKind::Wild => {} + PatKind::Missing | PatKind::Ident(BindingMode::NONE, _, None) | PatKind::Wild => {} PatKind::Ident(BindingMode::MUT, ident, None) => { report_err(pat.span, Some(ident), true) } @@ -258,21 +267,22 @@ impl<'a> AstValidator<'a> { && let TraitOrTraitImpl::TraitImpl { constness: Const::No, polarity: ImplPolarity::Positive, - trait_ref, + trait_ref_span, .. } = parent { - Some(trait_ref.shrink_to_lo()) + Some(trait_ref_span.shrink_to_lo()) } else { None }; - let make_trait_const_sugg = - if const_trait_impl && let TraitOrTraitImpl::Trait { span, constness: None } = parent { - Some(span.shrink_to_lo()) - } else { - None - }; + let make_trait_const_sugg = if const_trait_impl + && let TraitOrTraitImpl::Trait { span, constness_span: None } = parent + { + Some(span.shrink_to_lo()) + } else { + None + }; let parent_constness = parent.constness(); self.dcx().emit_err(errors::TraitFnConst { @@ -334,14 +344,14 @@ impl<'a> AstValidator<'a> { .filter(|attr| { let arr = [ sym::allow, - sym::cfg, - sym::cfg_attr, + sym::cfg_trace, + sym::cfg_attr_trace, sym::deny, sym::expect, sym::forbid, sym::warn, ]; - !arr.contains(&attr.name_or_empty()) && rustc_attr_parsing::is_builtin_attr(*attr) + !attr.has_any_name(&arr) && rustc_attr_parsing::is_builtin_attr(*attr) }) .for_each(|attr| { if attr.is_doc_comment() { @@ -448,13 +458,13 @@ impl<'a> AstValidator<'a> { check_where_clause(where_clauses.after); } - fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body: Option) { - let Some(body) = body else { + fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body_span: Option) { + let Some(body_span) = body_span else { return; }; self.dcx().emit_err(errors::BodyInExtern { span: ident.span, - body, + body: body_span, block: self.current_extern_span(), kind, }); @@ -473,7 +483,7 @@ impl<'a> AstValidator<'a> { } fn current_extern_span(&self) -> Span { - self.sess.source_map().guess_head_span(self.extern_mod.unwrap()) + self.sess.source_map().guess_head_span(self.extern_mod_span.unwrap()) } /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`. @@ -583,9 +593,10 @@ impl<'a> AstValidator<'a> { self.dcx().emit_err(errors::ModuleNonAscii { span: ident.span, name: ident.name }); } - fn deny_generic_params(&self, generics: &Generics, ident: Span) { + fn deny_generic_params(&self, generics: &Generics, ident_span: Span) { if !generics.params.is_empty() { - self.dcx().emit_err(errors::AutoTraitGeneric { span: generics.span, ident }); + self.dcx() + .emit_err(errors::AutoTraitGeneric { span: generics.span, ident: ident_span }); } } @@ -605,11 +616,11 @@ impl<'a> AstValidator<'a> { } } - fn deny_items(&self, trait_items: &[P], ident: Span) { + fn deny_items(&self, trait_items: &[P], ident_span: Span) { if !trait_items.is_empty() { - let spans: Vec<_> = trait_items.iter().map(|i| i.ident.span).collect(); + let spans: Vec<_> = trait_items.iter().map(|i| i.kind.ident().unwrap().span).collect(); let total = trait_items.first().unwrap().span.to(trait_items.last().unwrap().span); - self.dcx().emit_err(errors::AutoTraitItems { spans, total, ident }); + self.dcx().emit_err(errors::AutoTraitItems { spans, total, ident: ident_span }); } } @@ -677,7 +688,7 @@ impl<'a> AstValidator<'a> { self.dcx().emit_err(errors::PatternFnPointer { span }); }); if let Extern::Implicit(extern_span) = bfty.ext { - self.maybe_lint_missing_abi(extern_span, ty.id); + self.handle_missing_abi(extern_span, ty.id); } } TyKind::TraitObject(bounds, ..) => { @@ -694,7 +705,7 @@ impl<'a> AstValidator<'a> { } } TyKind::ImplTrait(_, bounds) => { - if let Some(outer_impl_trait_sp) = self.outer_impl_trait { + if let Some(outer_impl_trait_sp) = self.outer_impl_trait_span { self.dcx().emit_err(errors::NestedImplTrait { span: ty.span, outer: outer_impl_trait_sp, @@ -710,10 +721,12 @@ impl<'a> AstValidator<'a> { } } - fn maybe_lint_missing_abi(&mut self, span: Span, id: NodeId) { + fn handle_missing_abi(&mut self, span: Span, id: NodeId) { // FIXME(davidtwco): This is a hack to detect macros which produce spans of the // call site which do not have a macro backtrace. See #61963. - if self + if span.edition().at_least_edition_future() && self.features.explicit_extern_abis() { + self.dcx().emit_err(errors::MissingAbi { span }); + } else if self .sess .source_map() .span_to_snippet(span) @@ -727,6 +740,19 @@ impl<'a> AstValidator<'a> { ) } } + + // Used within `visit_item` for item kinds where we don't call `visit::walk_item`. + fn visit_attrs_vis(&mut self, attrs: &'a AttrVec, vis: &'a Visibility) { + walk_list!(self, visit_attribute, attrs); + self.visit_vis(vis); + } + + // Used within `visit_item` for item kinds where we don't call `visit::walk_item`. + fn visit_attrs_vis_ident(&mut self, attrs: &'a AttrVec, vis: &'a Visibility, ident: &'a Ident) { + walk_list!(self, visit_attribute, attrs); + self.visit_vis(vis); + self.visit_ident(ident); + } } /// Checks that generic parameters are in the correct order, @@ -804,7 +830,7 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_attribute(&mut self, attr: &Attribute) { - validate_attr::check_attr(&self.sess.psess, attr); + validate_attr::check_attr(&self.sess.psess, attr, self.lint_node_id); } fn visit_ty(&mut self, ty: &'a Ty) { @@ -817,8 +843,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.has_proc_macro_decls = true; } - if attr::contains_name(&item.attrs, sym::no_mangle) { - self.check_nomangle_item_asciionly(item.ident, item.span); + let previous_lint_node_id = mem::replace(&mut self.lint_node_id, item.id); + + if let Some(ident) = item.kind.ident() + && attr::contains_name(&item.attrs, sym::no_mangle) + { + self.check_nomangle_item_asciionly(ident, item.span); } match &item.kind { @@ -832,37 +862,33 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self_ty, items, }) => { - self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| { - this.visibility_not_permitted( - &item.vis, - errors::VisibilityNotPermittedNote::TraitImpl, - ); - if let TyKind::Dummy = self_ty.kind { - // Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering, - // which isn't allowed. Not a problem for this obscure, obsolete syntax. - this.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span }); - } - if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity) - { - this.dcx().emit_err(errors::UnsafeNegativeImpl { - span: sp.to(t.path.span), - negative: sp, - r#unsafe: span, - }); - } + self.visit_attrs_vis(&item.attrs, &item.vis); + self.visibility_not_permitted( + &item.vis, + errors::VisibilityNotPermittedNote::TraitImpl, + ); + if let TyKind::Dummy = self_ty.kind { + // Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering, + // which isn't allowed. Not a problem for this obscure, obsolete syntax. + self.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span }); + } + if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity) { + self.dcx().emit_err(errors::UnsafeNegativeImpl { + span: sp.to(t.path.span), + negative: sp, + r#unsafe: span, + }); + } - this.visit_vis(&item.vis); - this.visit_ident(&item.ident); - let disallowed = matches!(constness, Const::No) - .then(|| TildeConstReason::TraitImpl { span: item.span }); - this.with_tilde_const(disallowed, |this| this.visit_generics(generics)); - this.visit_trait_ref(t); - this.visit_ty(self_ty); + let disallowed = matches!(constness, Const::No) + .then(|| TildeConstReason::TraitImpl { span: item.span }); + self.with_tilde_const(disallowed, |this| this.visit_generics(generics)); + self.visit_trait_ref(t); + self.visit_ty(self_ty); - walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl); + self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| { + walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: true }); }); - walk_list!(self, visit_attribute, &item.attrs); - return; // Avoid visiting again. } ItemKind::Impl(box Impl { safety, @@ -882,47 +908,53 @@ impl<'a> Visitor<'a> for AstValidator<'a> { only_trait, }; - self.with_in_trait_impl(None, |this| { - this.visibility_not_permitted( - &item.vis, - errors::VisibilityNotPermittedNote::IndividualImplItems, - ); - if let &Safety::Unsafe(span) = safety { - this.dcx().emit_err(errors::InherentImplCannotUnsafe { - span: self_ty.span, - annotation_span: span, - annotation: "unsafe", - self_ty: self_ty.span, - }); - } - if let &ImplPolarity::Negative(span) = polarity { - this.dcx().emit_err(error(span, "negative", false)); - } - if let &Defaultness::Default(def_span) = defaultness { - this.dcx().emit_err(error(def_span, "`default`", true)); - } - if let &Const::Yes(span) = constness { - this.dcx().emit_err(error(span, "`const`", true)); - } + self.visit_attrs_vis(&item.attrs, &item.vis); + self.visibility_not_permitted( + &item.vis, + errors::VisibilityNotPermittedNote::IndividualImplItems, + ); + if let &Safety::Unsafe(span) = safety { + self.dcx().emit_err(errors::InherentImplCannotUnsafe { + span: self_ty.span, + annotation_span: span, + annotation: "unsafe", + self_ty: self_ty.span, + }); + } + if let &ImplPolarity::Negative(span) = polarity { + self.dcx().emit_err(error(span, "negative", false)); + } + if let &Defaultness::Default(def_span) = defaultness { + self.dcx().emit_err(error(def_span, "`default`", true)); + } + if let &Const::Yes(span) = constness { + self.dcx().emit_err(error(span, "`const`", true)); + } - this.visit_vis(&item.vis); - this.visit_ident(&item.ident); - this.with_tilde_const( - Some(TildeConstReason::Impl { span: item.span }), - |this| this.visit_generics(generics), - ); - this.visit_ty(self_ty); - walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl); + self.with_tilde_const(Some(TildeConstReason::Impl { span: item.span }), |this| { + this.visit_generics(generics) + }); + self.visit_ty(self_ty); + self.with_in_trait_impl(None, |this| { + walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: false }); }); - walk_list!(self, visit_attribute, &item.attrs); - return; // Avoid visiting again. } - ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, contract: _, body }) => { + ItemKind::Fn( + func @ box Fn { + defaultness, + ident, + generics: _, + sig, + contract: _, + body, + define_opaque: _, + }, + ) => { + self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); self.check_defaultness(item.span, *defaultness); - let is_intrinsic = - item.attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic); - if body.is_none() && !is_intrinsic { + let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)); + if body.is_none() && !is_intrinsic && !self.is_sdylib_interface { self.dcx().emit_err(errors::FnWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), @@ -945,45 +977,40 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); } - self.visit_vis(&item.vis); - self.visit_ident(&item.ident); - let kind = FnKind::Fn(FnCtxt::Free, &item.ident, &item.vis, &*func); + let kind = FnKind::Fn(FnCtxt::Free, &item.vis, &*func); self.visit_fn(kind, item.span, item.id); - walk_list!(self, visit_attribute, &item.attrs); - return; // Avoid visiting again. } ItemKind::ForeignMod(ForeignMod { extern_span, abi, safety, .. }) => { - self.with_in_extern_mod(*safety, |this| { - let old_item = mem::replace(&mut this.extern_mod, Some(item.span)); - this.visibility_not_permitted( - &item.vis, - errors::VisibilityNotPermittedNote::IndividualForeignItems, - ); - - if &Safety::Default == safety { - if item.span.at_least_rust_2024() { - this.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span }); - } else { - this.lint_buffer.buffer_lint( - MISSING_UNSAFE_ON_EXTERN, - item.id, - item.span, - BuiltinLintDiag::MissingUnsafeOnExtern { - suggestion: item.span.shrink_to_lo(), - }, - ); - } + let old_item = mem::replace(&mut self.extern_mod_span, Some(item.span)); + self.visibility_not_permitted( + &item.vis, + errors::VisibilityNotPermittedNote::IndividualForeignItems, + ); + + if &Safety::Default == safety { + if item.span.at_least_rust_2024() { + self.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span }); + } else { + self.lint_buffer.buffer_lint( + MISSING_UNSAFE_ON_EXTERN, + item.id, + item.span, + BuiltinLintDiag::MissingUnsafeOnExtern { + suggestion: item.span.shrink_to_lo(), + }, + ); } + } - if abi.is_none() { - this.maybe_lint_missing_abi(*extern_span, item.id); - } + if abi.is_none() { + self.handle_missing_abi(*extern_span, item.id); + } + self.with_in_extern_mod(*safety, |this| { visit::walk_item(this, item); - this.extern_mod = old_item; }); - return; // Avoid visiting again. + self.extern_mod_span = old_item; } - ItemKind::Enum(def, _) => { + ItemKind::Enum(_, def, _) => { for variant in &def.variants { self.visibility_not_permitted( &variant.vis, @@ -996,36 +1023,33 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ); } } + visit::walk_item(self, item) } - ItemKind::Trait(box Trait { is_auto, generics, bounds, items, .. }) => { + ItemKind::Trait(box Trait { is_auto, generics, ident, bounds, items, .. }) => { + self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); let is_const_trait = attr::find_by_name(&item.attrs, sym::const_trait).map(|attr| attr.span); - self.with_in_trait(item.span, is_const_trait, |this| { - if *is_auto == IsAuto::Yes { - // Auto traits cannot have generics, super traits nor contain items. - this.deny_generic_params(generics, item.ident.span); - this.deny_super_traits(bounds, item.ident.span); - this.deny_where_clause(&generics.where_clause, item.ident.span); - this.deny_items(items, item.ident.span); - } + if *is_auto == IsAuto::Yes { + // Auto traits cannot have generics, super traits nor contain items. + self.deny_generic_params(generics, ident.span); + self.deny_super_traits(bounds, ident.span); + self.deny_where_clause(&generics.where_clause, ident.span); + self.deny_items(items, ident.span); + } - // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound - // context for the supertraits. - this.visit_vis(&item.vis); - this.visit_ident(&item.ident); - let disallowed = is_const_trait - .is_none() - .then(|| TildeConstReason::Trait { span: item.span }); - this.with_tilde_const(disallowed, |this| { - this.visit_generics(generics); - walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits) - }); + // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound + // context for the supertraits. + let disallowed = + is_const_trait.is_none().then(|| TildeConstReason::Trait { span: item.span }); + self.with_tilde_const(disallowed, |this| { + this.visit_generics(generics); + walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits) + }); + self.with_in_trait(item.span, is_const_trait, |this| { walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait); }); - walk_list!(self, visit_attribute, &item.attrs); - return; // Avoid visiting again } - ItemKind::Mod(safety, mod_kind) => { + ItemKind::Mod(safety, ident, mod_kind) => { if let &Safety::Unsafe(span) = safety { self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" }); } @@ -1033,36 +1057,31 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _)) && !attr::contains_name(&item.attrs, sym::path) { - self.check_mod_file_item_asciionly(item.ident); + self.check_mod_file_item_asciionly(*ident); } + visit::walk_item(self, item) } - ItemKind::Struct(vdata, generics) => match vdata { + ItemKind::Struct(ident, vdata, generics) => match vdata { VariantData::Struct { fields, .. } => { - self.visit_vis(&item.vis); - self.visit_ident(&item.ident); + self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); self.visit_generics(generics); // Permit `Anon{Struct,Union}` as field type. walk_list!(self, visit_struct_field_def, fields); - walk_list!(self, visit_attribute, &item.attrs); - return; } - _ => {} + _ => visit::walk_item(self, item), }, - ItemKind::Union(vdata, generics) => { + ItemKind::Union(ident, vdata, generics) => { if vdata.fields().is_empty() { self.dcx().emit_err(errors::FieldlessUnion { span: item.span }); } match vdata { VariantData::Struct { fields, .. } => { - self.visit_vis(&item.vis); - self.visit_ident(&item.ident); + self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); self.visit_generics(generics); // Permit `Anon{Struct,Union}` as field type. walk_list!(self, visit_struct_field_def, fields); - walk_list!(self, visit_attribute, &item.attrs); - return; } - _ => {} + _ => visit::walk_item(self, item), } } ItemKind::Const(box ConstItem { defaultness, expr, .. }) => { @@ -1073,6 +1092,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { replace_span: self.ending_semi_or_hi(item.span), }); } + visit::walk_item(self, item); } ItemKind::Static(box StaticItem { expr, safety, .. }) => { self.check_item_safety(item.span, *safety); @@ -1086,6 +1106,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { replace_span: self.ending_semi_or_hi(item.span), }); } + visit::walk_item(self, item); } ItemKind::TyAlias( ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. }, @@ -1109,23 +1130,25 @@ impl<'a> Visitor<'a> for AstValidator<'a> { help: self.sess.is_nightly_build(), }); } + visit::walk_item(self, item); } - _ => {} + _ => visit::walk_item(self, item), } - visit::walk_item(self, item); + self.lint_node_id = previous_lint_node_id; } fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { match &fi.kind { - ForeignItemKind::Fn(box Fn { defaultness, sig, body, .. }) => { + ForeignItemKind::Fn(box Fn { defaultness, ident, sig, body, .. }) => { self.check_defaultness(fi.span, *defaultness); - self.check_foreign_fn_bodyless(fi.ident, body.as_deref()); + self.check_foreign_fn_bodyless(*ident, body.as_deref()); self.check_foreign_fn_headerless(sig.header); - self.check_foreign_item_ascii_only(fi.ident); + self.check_foreign_item_ascii_only(*ident); } ForeignItemKind::TyAlias(box TyAlias { defaultness, + ident, generics, where_clauses, bounds, @@ -1133,15 +1156,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .. }) => { self.check_defaultness(fi.span, *defaultness); - self.check_foreign_kind_bodyless(fi.ident, "type", ty.as_ref().map(|b| b.span)); + self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span)); self.check_type_no_bounds(bounds, "`extern` blocks"); self.check_foreign_ty_genericless(generics, where_clauses); - self.check_foreign_item_ascii_only(fi.ident); + self.check_foreign_item_ascii_only(*ident); } - ForeignItemKind::Static(box StaticItem { expr, safety, .. }) => { + ForeignItemKind::Static(box StaticItem { ident, safety, expr, .. }) => { self.check_item_safety(fi.span, *safety); - self.check_foreign_kind_bodyless(fi.ident, "static", expr.as_ref().map(|b| b.span)); - self.check_foreign_item_ascii_only(fi.ident); + self.check_foreign_kind_bodyless(*ident, "static", expr.as_ref().map(|b| b.span)); + self.check_foreign_item_ascii_only(*ident); } ForeignItemKind::MacCall(..) => {} } @@ -1348,7 +1371,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } if let FnKind::Fn( - _, _, _, Fn { @@ -1357,11 +1379,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }, ) = fk { - self.maybe_lint_missing_abi(*extern_span, id); + self.handle_missing_abi(*extern_span, id); } // Functions without bodies cannot have patterns. - if let FnKind::Fn(ctxt, _, _, Fn { body: None, sig, .. }) = fk { + if let FnKind::Fn(ctxt, _, Fn { body: None, sig, .. }) = fk { Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| { if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) { if let Some(ident) = ident { @@ -1395,22 +1417,24 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .is_some(); let disallowed = (!tilde_const_allowed).then(|| match fk { - FnKind::Fn(_, ident, _, _) => TildeConstReason::Function { ident: ident.span }, + FnKind::Fn(_, _, f) => TildeConstReason::Function { ident: f.ident.span }, FnKind::Closure(..) => TildeConstReason::Closure, }); self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk)); } fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) { - if attr::contains_name(&item.attrs, sym::no_mangle) { - self.check_nomangle_item_asciionly(item.ident, item.span); + if let Some(ident) = item.kind.ident() + && attr::contains_name(&item.attrs, sym::no_mangle) + { + self.check_nomangle_item_asciionly(ident, item.span); } if ctxt == AssocCtxt::Trait || self.outer_trait_or_trait_impl.is_none() { self.check_defaultness(item.span, item.kind.defaultness()); } - if ctxt == AssocCtxt::Impl { + if let AssocCtxt::Impl { .. } = ctxt { match &item.kind { AssocItemKind::Const(box ConstItem { expr: None, .. }) => { self.dcx().emit_err(errors::AssocConstWithoutBody { @@ -1419,7 +1443,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); } AssocItemKind::Fn(box Fn { body, .. }) => { - if body.is_none() { + if body.is_none() && !self.is_sdylib_interface { self.dcx().emit_err(errors::AssocFnWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), @@ -1463,8 +1487,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } - if let AssocItemKind::Const(..) = item.kind { - self.check_item_named(item.ident, "const"); + if let AssocItemKind::Const(ci) = &item.kind { + self.check_item_named(ci.ident, "const"); } let parent_is_const = @@ -1476,10 +1500,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> { || ctxt == AssocCtxt::Trait || matches!(func.sig.header.constness, Const::Yes(_)) => { - self.visit_vis(&item.vis); - self.visit_ident(&item.ident); - let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.ident, &item.vis, &*func); - walk_list!(self, visit_attribute, &item.attrs); + self.visit_attrs_vis_ident(&item.attrs, &item.vis, &func.ident); + let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.vis, &*func); self.visit_fn(kind, item.span, item.id); } AssocItemKind::Type(_) => { @@ -1584,7 +1606,7 @@ fn deny_equality_constraints( generics.where_clause.span } else { let mut span = predicate_span; - let mut prev: Option = None; + let mut prev_span: Option = None; let mut preds = generics.where_clause.predicates.iter().peekable(); // Find the predicate that shouldn't have been in the where bound list. while let Some(pred) = preds.next() { @@ -1594,12 +1616,12 @@ fn deny_equality_constraints( if let Some(next) = preds.peek() { // This is the first predicate, remove the trailing comma as well. span = span.with_hi(next.span.lo()); - } else if let Some(prev) = prev { + } else if let Some(prev_span) = prev_span { // Remove the previous comma as well. - span = span.with_lo(prev.hi()); + span = span.with_lo(prev_span.hi()); } } - prev = Some(pred.span); + prev_span = Some(pred.span); } span }; @@ -1669,17 +1691,20 @@ pub fn check_crate( sess: &Session, features: &Features, krate: &Crate, + is_sdylib_interface: bool, lints: &mut LintBuffer, ) -> bool { let mut validator = AstValidator { sess, features, - extern_mod: None, + extern_mod_span: None, outer_trait_or_trait_impl: None, has_proc_macro_decls: false, - outer_impl_trait: None, + outer_impl_trait_span: None, disallow_tilde_const: Some(TildeConstReason::Item), extern_mod_safety: None, + lint_node_id: CRATE_NODE_ID, + is_sdylib_interface, lint_buffer: lints, }; visit::walk_crate(&mut validator, krate); diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 8e53e600f7ac6..6f9737e08314e 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -2,7 +2,7 @@ use rustc_ast::ParamKindOrd; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; @@ -394,11 +394,7 @@ pub(crate) struct EmptyLabelManySpans(pub Vec); // The derive for `Vec` does multiple calls to `span_label`, adding commas between each impl Subdiagnostic for EmptyLabelManySpans { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.span_labels(self.0, ""); } } @@ -749,11 +745,7 @@ pub(crate) struct StableFeature { } impl Subdiagnostic for StableFeature { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("name", self.name); diag.arg("since", self.since); diag.help(fluent::ast_passes_stable_since); @@ -823,3 +815,12 @@ pub(crate) struct DuplicatePreciseCapturing { #[label] pub bound2: Span, } + +#[derive(Diagnostic)] +#[diag(ast_passes_extern_without_abi)] +#[help] +pub(crate) struct MissingAbi { + #[primary_span] + #[suggestion(code = "extern \"\"", applicability = "has-placeholders")] + pub span: Span, +} diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 0f80e49320ed4..915613a391374 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -99,6 +99,13 @@ impl<'a> PostExpansionVisitor<'a> { } visit::walk_ty(self, ty); } + + fn visit_anon_const(&mut self, _: &ast::AnonConst) -> Self::Result { + // We don't walk the anon const because it crosses a conceptual boundary: We're no + // longer "inside" the original type. + // Brittle: We assume that the callers of `check_impl_trait` will later recurse into + // the items found in the AnonConst to look for nested TyAliases. + } } ImplTraitVisitor { vis: self, in_associated_ty }.visit_ty(ty); } @@ -229,7 +236,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate!(&self, trait_alias, i.span, "trait aliases are experimental"); } - ast::ItemKind::MacroDef(ast::MacroDef { macro_rules: false, .. }) => { + ast::ItemKind::MacroDef(_, ast::MacroDef { macro_rules: false, .. }) => { let msg = "`macro` is experimental"; gate!(&self, decl_macro, i.span, msg); } @@ -325,17 +332,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::ExprKind::TryBlock(_) => { gate!(&self, try_blocks, e.span, "`try` expression is experimental"); } - ast::ExprKind::Lit(token::Lit { kind: token::LitKind::Float, suffix, .. }) => { - match suffix { - Some(sym::f16) => { - gate!(&self, f16, e.span, "the type `f16` is unstable") - } - Some(sym::f128) => { - gate!(&self, f128, e.span, "the type `f128` is unstable") - } - _ => (), + ast::ExprKind::Lit(token::Lit { + kind: token::LitKind::Float | token::LitKind::Integer, + suffix, + .. + }) => match suffix { + Some(sym::f16) => { + gate!(&self, f16, e.span, "the type `f16` is unstable") } - } + Some(sym::f128) => { + gate!(&self, f128, e.span, "the type `f128` is unstable") + } + _ => (), + }, _ => {} } visit::walk_expr(self, e) @@ -483,12 +492,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { half_open_range_patterns_in_slices, "half-open range patterns in slices are unstable" ); - gate_all!(inline_const_pat, "inline-const in pattern position is experimental"); gate_all!(associated_const_equality, "associated const equality is incomplete"); gate_all!(yeet_expr, "`do yeet` expression is experimental"); gate_all!(dyn_star, "`dyn*` trait objects are experimental"); gate_all!(const_closures, "const closures are experimental"); gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); + gate_all!(ergonomic_clones, "ergonomic clones are experimental"); gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(generic_const_items, "generic const items are experimental"); gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); @@ -503,6 +512,9 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(unsafe_binders, "unsafe binder types are experimental"); gate_all!(contracts, "contracts are incomplete"); gate_all!(contracts_internals, "contract internal machinery is for internal use only"); + gate_all!(where_clause_attrs, "attributes in `where` clause are unstable"); + gate_all!(super_let, "`super let` is experimental"); + gate_all!(frontmatter, "frontmatters are experimental"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index b4ed70d83e570..6517fdb55bd3f 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -8,9 +8,7 @@ #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_is_partitioned)] -#![feature(let_chains)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end pub mod ast_validation; diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml index 2634dd1fdf93e..b704040be9618 100644 --- a/compiler/rustc_ast_pretty/Cargo.toml +++ b/compiler/rustc_ast_pretty/Cargo.toml @@ -7,8 +7,11 @@ edition = "2024" # tidy-alphabetical-start itertools = "0.12" rustc_ast = { path = "../rustc_ast" } -rustc_data_structures = { path = "../rustc_data_structures" } rustc_lexer = { path = "../rustc_lexer" } rustc_span = { path = "../rustc_span" } +# tidy-alphabetical-end + +[dev-dependencies] +# tidy-alphabetical-start thin-vec = "0.2.12" # tidy-alphabetical-end diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs index 602ab69ee5b5d..1079ccccb03e7 100644 --- a/compiler/rustc_ast_pretty/src/lib.rs +++ b/compiler/rustc_ast_pretty/src/lib.rs @@ -2,8 +2,8 @@ #![allow(internal_features)] #![doc(rust_logo)] #![feature(box_patterns)] +#![feature(negative_impls)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end mod helpers; diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs index e4fd7e94fde14..8a0dbadf18cdf 100644 --- a/compiler/rustc_ast_pretty/src/pp.rs +++ b/compiler/rustc_ast_pretty/src/pp.rs @@ -234,6 +234,34 @@ struct BufEntry { size: isize, } +// Boxes opened with methods like `Printer::{cbox,ibox}` must be closed with +// `Printer::end`. Failure to do so can result in bad indenting, or in extreme +// cases, cause no output to be produced at all. +// +// Box opening and closing used to be entirely implicit, which was hard to +// understand and easy to get wrong. This marker type is now returned from the +// box opening methods and forgotten by `Printer::end`. Any marker that isn't +// forgotten will trigger a panic in `drop`. (Closing a box more than once +// isn't possible because `BoxMarker` doesn't implement `Copy` or `Clone`.) +// +// Note: it would be better to make open/close mismatching impossible and avoid +// the need for this marker type altogether by having functions like +// `with_ibox` that open a box, call a closure, and then close the box. That +// would work for simple cases, but box lifetimes sometimes interact with +// complex control flow and across function boundaries in ways that are +// difficult to handle with such a technique. +#[must_use] +pub struct BoxMarker; + +impl !Clone for BoxMarker {} +impl !Copy for BoxMarker {} + +impl Drop for BoxMarker { + fn drop(&mut self) { + panic!("BoxMarker not ended with `Printer::end()`"); + } +} + impl Printer { pub fn new() -> Self { Printer { @@ -270,7 +298,8 @@ impl Printer { } } - fn scan_begin(&mut self, token: BeginToken) { + // This is is where `BoxMarker`s are produced. + fn scan_begin(&mut self, token: BeginToken) -> BoxMarker { if self.scan_stack.is_empty() { self.left_total = 1; self.right_total = 1; @@ -278,15 +307,18 @@ impl Printer { } let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total }); self.scan_stack.push_back(right); + BoxMarker } - fn scan_end(&mut self) { + // This is is where `BoxMarker`s are consumed. + fn scan_end(&mut self, b: BoxMarker) { if self.scan_stack.is_empty() { self.print_end(); } else { let right = self.buf.push(BufEntry { token: Token::End, size: -1 }); self.scan_stack.push_back(right); } + std::mem::forget(b) } fn scan_break(&mut self, token: BreakToken) { diff --git a/compiler/rustc_ast_pretty/src/pp/convenience.rs b/compiler/rustc_ast_pretty/src/pp/convenience.rs index a1c07bb07e42a..9b902b38122c8 100644 --- a/compiler/rustc_ast_pretty/src/pp/convenience.rs +++ b/compiler/rustc_ast_pretty/src/pp/convenience.rs @@ -1,25 +1,27 @@ use std::borrow::Cow; -use crate::pp::{BeginToken, BreakToken, Breaks, IndentStyle, Printer, SIZE_INFINITY, Token}; +use crate::pp::{ + BeginToken, BoxMarker, BreakToken, Breaks, IndentStyle, Printer, SIZE_INFINITY, Token, +}; impl Printer { /// "raw box" - pub fn rbox(&mut self, indent: isize, breaks: Breaks) { + pub fn rbox(&mut self, indent: isize, breaks: Breaks) -> BoxMarker { self.scan_begin(BeginToken { indent: IndentStyle::Block { offset: indent }, breaks }) } /// Inconsistent breaking box - pub fn ibox(&mut self, indent: isize) { + pub fn ibox(&mut self, indent: isize) -> BoxMarker { self.rbox(indent, Breaks::Inconsistent) } /// Consistent breaking box - pub fn cbox(&mut self, indent: isize) { + pub fn cbox(&mut self, indent: isize) -> BoxMarker { self.rbox(indent, Breaks::Consistent) } - pub fn visual_align(&mut self) { - self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent }); + pub fn visual_align(&mut self) -> BoxMarker { + self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent }) } pub fn break_offset(&mut self, n: usize, off: isize) { @@ -30,8 +32,8 @@ impl Printer { }); } - pub fn end(&mut self) { - self.scan_end() + pub fn end(&mut self, b: BoxMarker) { + self.scan_end(b) } pub fn eof(mut self) -> String { diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs index 97cb6e52d5625..a05e2bd6a5d72 100644 --- a/compiler/rustc_ast_pretty/src/pprust/mod.rs +++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs @@ -5,13 +5,11 @@ pub mod state; use std::borrow::Cow; use rustc_ast as ast; -use rustc_ast::token::{Nonterminal, Token, TokenKind}; +use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; -pub use state::{AnnNode, Comments, PpAnn, PrintState, State, print_crate}; - -pub fn nonterminal_to_string(nt: &Nonterminal) -> String { - State::new().nonterminal_to_string(nt) -} +pub use state::{ + AnnNode, Comments, PpAnn, PrintState, State, print_crate, print_crate_as_interface, +}; /// Print the token kind precisely, without converting `$crate` into its respective crate name. pub fn token_kind_to_string(tok: &TokenKind) -> Cow<'static, str> { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 44e956dc37f3e..0990c9b27eb09 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -11,9 +11,7 @@ use std::sync::Arc; use rustc_ast::attr::AttrIdGenerator; use rustc_ast::ptr::P; -use rustc_ast::token::{ - self, BinOpToken, CommentKind, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind, -}; +use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{Comment, CommentStyle}; @@ -26,10 +24,9 @@ use rustc_span::edition::Edition; use rustc_span::source_map::{SourceMap, Spanned}; use rustc_span::symbol::IdentPrinter; use rustc_span::{BytePos, CharPos, DUMMY_SP, FileName, Ident, Pos, Span, Symbol, kw, sym}; -use thin_vec::ThinVec; use crate::pp::Breaks::{Consistent, Inconsistent}; -use crate::pp::{self, Breaks}; +use crate::pp::{self, BoxMarker, Breaks}; use crate::pprust::state::fixup::FixupContext; pub enum MacHeader<'a> { @@ -224,6 +221,7 @@ pub struct State<'a> { pub s: pp::Printer, comments: Option>, ann: &'a (dyn PpAnn + 'a), + is_sdylib_interface: bool, } const INDENT_UNIT: isize = 4; @@ -239,9 +237,41 @@ pub fn print_crate<'a>( is_expanded: bool, edition: Edition, g: &AttrIdGenerator, +) -> String { + let mut s = State { + s: pp::Printer::new(), + comments: Some(Comments::new(sm, filename, input)), + ann, + is_sdylib_interface: false, + }; + + print_crate_inner(&mut s, krate, is_expanded, edition, g); + s.s.eof() +} + +pub fn print_crate_as_interface( + krate: &ast::Crate, + edition: Edition, + g: &AttrIdGenerator, ) -> String { let mut s = - State { s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), ann }; + State { s: pp::Printer::new(), comments: None, ann: &NoAnn, is_sdylib_interface: true }; + + print_crate_inner(&mut s, krate, false, edition, g); + s.s.eof() +} + +fn print_crate_inner<'a>( + s: &mut State<'a>, + krate: &ast::Crate, + is_expanded: bool, + edition: Edition, + g: &AttrIdGenerator, +) { + // We need to print shebang before anything else + // otherwise the resulting code will not compile + // and shebang will be useless. + s.maybe_print_shebang(); if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) { // We need to print `#![no_std]` (and its feature gate) so that @@ -280,8 +310,7 @@ pub fn print_crate<'a>( s.print_item(item); } s.print_remaining_comments(); - s.ann.post(&mut s, AnnNode::Crate(krate)); - s.s.eof() + s.ann.post(s, AnnNode::Crate(krate)); } /// Should two consecutive tokens be printed with a space between them? @@ -319,7 +348,7 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool { (tt1, Tok(Token { kind: Comma | Semi | Dot, .. }, _)) if !is_punct(tt1) => false, // IDENT + `!`: `println!()`, but `if !x { ... }` needs a space after the `if` - (Tok(Token { kind: Ident(sym, is_raw), span }, _), Tok(Token { kind: Not, .. }, _)) + (Tok(Token { kind: Ident(sym, is_raw), span }, _), Tok(Token { kind: Bang, .. }, _)) if !Ident::new(*sym, *span).is_reserved() || matches!(is_raw, IdentIsRaw::Yes) => { false @@ -344,21 +373,6 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool { } } -fn binop_to_string(op: BinOpToken) -> &'static str { - match op { - token::Plus => "+", - token::Minus => "-", - token::Star => "*", - token::Slash => "/", - token::Percent => "%", - token::Caret => "^", - token::And => "&", - token::Or => "|", - token::Shl => "<<", - token::Shr => ">>", - } -} - pub fn doc_comment_to_string( comment_kind: CommentKind, attr_style: ast::AttrStyle, @@ -437,7 +451,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere { let mut it = elts.into_iter(); - self.rbox(0, b); + let rb = self.rbox(0, b); if let Some(first) = it.next() { op(self, first); for elt in it { @@ -448,7 +462,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere op(self, elt); } } - self.end(); + self.end(rb); } fn commasep<'x, T: 'x, F, I>(&mut self, b: Breaks, elts: I, op: F) @@ -479,7 +493,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere self.zerobreak(); } if let Some((last, lines)) = cmnt.lines.split_last() { - self.ibox(0); + let ib = self.ibox(0); for line in lines { self.word(line.clone()); @@ -489,7 +503,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere self.word(last.clone()); self.space(); - self.end(); + self.end(ib); } self.zerobreak() } @@ -512,14 +526,14 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere self.word(line.clone()); self.hardbreak() } else { - self.visual_align(); + let vb = self.visual_align(); for line in &cmnt.lines { if !line.is_empty() { self.word(line.clone()); } self.hardbreak(); } - self.end(); + self.end(vb); } } CommentStyle::BlankLine => { @@ -578,6 +592,20 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere self.word(st) } + fn maybe_print_shebang(&mut self) { + if let Some(cmnt) = self.peek_comment() { + // Comment is a shebang if it's: + // Isolated, starts with #! and doesn't continue with `[` + // See [rustc_lexer::strip_shebang] and [gather_comments] from pprust/state.rs for details + if cmnt.style == CommentStyle::Isolated + && cmnt.lines.first().map_or(false, |l| l.starts_with("#!")) + { + let cmnt = self.next_comment().unwrap(); + self.print_comment(cmnt); + } + } + } + fn print_inner_attributes(&mut self, attrs: &[ast::Attribute]) -> bool { self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true) } @@ -596,11 +624,12 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere let mut printed = false; for attr in attrs { if attr.style == kind { - self.print_attribute_inline(attr, is_inline); - if is_inline { - self.nbsp(); + if self.print_attribute_inline(attr, is_inline) { + if is_inline { + self.nbsp(); + } + printed = true; } - printed = true; } } if printed && trailing_hardbreak && !is_inline { @@ -609,7 +638,12 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere printed } - fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) { + fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) -> bool { + if attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace) { + // It's not a valid identifier, so avoid printing it + // to keep the printed code reasonably parse-able. + return false; + } if !is_inline { self.hardbreak_if_not_bol(); } @@ -628,10 +662,11 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere self.hardbreak() } } + true } fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) { - self.ibox(0); + let ib = self.ibox(0); match item.unsafety { ast::Safety::Unsafe(_) => { self.word("unsafe"); @@ -645,6 +680,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere false, None, *delim, + None, tokens, true, span, @@ -664,7 +700,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere ast::Safety::Unsafe(_) => self.pclose(), ast::Safety::Default | ast::Safety::Safe(_) => {} } - self.end(); + self.end(ib); } /// This doesn't deserve to be called "pretty" printing, but it should be @@ -690,6 +726,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere false, None, *delim, + Some(spacing.open), tts, convert_dollar_crate, dspan.entire(), @@ -746,13 +783,12 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere has_bang: bool, ident: Option, delim: Delimiter, + open_spacing: Option, tts: &TokenStream, convert_dollar_crate: bool, span: Span, ) { - if delim == Delimiter::Brace { - self.cbox(INDENT_UNIT); - } + let cb = (delim == Delimiter::Brace).then(|| self.cbox(INDENT_UNIT)); match header { Some(MacHeader::Path(path)) => self.print_path(path, false, 0), Some(MacHeader::Keyword(kw)) => self.word(kw), @@ -771,22 +807,32 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere self.nbsp(); } self.word("{"); - if !tts.is_empty() { + + // Respect `Alone`, if provided, and print a space. Unless the list is empty. + let open_space = (open_spacing == None || open_spacing == Some(Spacing::Alone)) + && !tts.is_empty(); + if open_space { self.space(); } - self.ibox(0); + let ib = self.ibox(0); self.print_tts(tts, convert_dollar_crate); - self.end(); - let empty = tts.is_empty(); - self.bclose(span, empty); + self.end(ib); + + // Use `open_space` for the spacing *before* the closing delim. + // Because spacing on delimiters is lost when going through + // proc macros, and otherwise we can end up with ugly cases + // like `{ x}`. Symmetry is better. + self.bclose(span, !open_space, cb.unwrap()); } delim => { - let token_str = self.token_kind_to_string(&token::OpenDelim(delim)); + // `open_spacing` is ignored. We never print spaces after + // non-brace opening delims or before non-brace closing delims. + let token_str = self.token_kind_to_string(&delim.as_open_token_kind()); self.word(token_str); - self.ibox(0); + let ib = self.ibox(0); self.print_tts(tts, convert_dollar_crate); - self.end(); - let token_str = self.token_kind_to_string(&token::CloseDelim(delim)); + self.end(ib); + let token_str = self.token_kind_to_string(&delim.as_close_token_kind()); self.word(token_str); } } @@ -810,7 +856,8 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere has_bang, Some(*ident), macro_def.body.delim, - ¯o_def.body.tokens.clone(), + None, + ¯o_def.body.tokens, true, sp, ); @@ -839,37 +886,38 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } } - fn head>>(&mut self, w: S) { + fn head>>(&mut self, w: S) -> (BoxMarker, BoxMarker) { let w = w.into(); // Outer-box is consistent. - self.cbox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); // Head-box is inconsistent. - self.ibox(0); + let ib = self.ibox(0); // Keyword that starts the head. if !w.is_empty() { self.word_nbsp(w); } + (cb, ib) } - fn bopen(&mut self) { + fn bopen(&mut self, ib: BoxMarker) { self.word("{"); - self.end(); // Close the head-box. + self.end(ib); } - fn bclose_maybe_open(&mut self, span: rustc_span::Span, empty: bool, close_box: bool) { + fn bclose_maybe_open(&mut self, span: rustc_span::Span, no_space: bool, cb: Option) { let has_comment = self.maybe_print_comment(span.hi()); - if !empty || has_comment { + if !no_space || has_comment { self.break_offset_if_not_bol(1, -INDENT_UNIT); } self.word("}"); - if close_box { - self.end(); // Close the outer-box. + if let Some(cb) = cb { + self.end(cb); } } - fn bclose(&mut self, span: rustc_span::Span, empty: bool) { - let close_box = true; - self.bclose_maybe_open(span, empty, close_box) + fn bclose(&mut self, span: rustc_span::Span, no_space: bool, cb: BoxMarker) { + let cb = Some(cb); + self.bclose_maybe_open(span, no_space, cb) } fn break_offset_if_not_bol(&mut self, n: usize, off: isize) { @@ -887,14 +935,6 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } } - fn nonterminal_to_string(&self, nt: &Nonterminal) -> String { - // We extract the token stream from the AST fragment and pretty print - // it, rather than using AST pretty printing, because `Nonterminal` is - // slated for removal in #124141. (This method will also then be - // removed.) - self.tts_to_string(&TokenStream::from_nonterminal_ast(nt)) - } - /// Print the token kind precisely, without converting `$crate` into its respective crate name. fn token_kind_to_string(&self, tok: &TokenKind) -> Cow<'static, str> { self.token_kind_to_string_ext(tok, None) @@ -913,12 +953,30 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere token::Ne => "!=".into(), token::Ge => ">=".into(), token::Gt => ">".into(), - token::Not => "!".into(), + token::Bang => "!".into(), token::Tilde => "~".into(), token::OrOr => "||".into(), token::AndAnd => "&&".into(), - token::BinOp(op) => binop_to_string(op).into(), - token::BinOpEq(op) => format!("{}=", binop_to_string(op)).into(), + token::Plus => "+".into(), + token::Minus => "-".into(), + token::Star => "*".into(), + token::Slash => "/".into(), + token::Percent => "%".into(), + token::Caret => "^".into(), + token::And => "&".into(), + token::Or => "|".into(), + token::Shl => "<<".into(), + token::Shr => ">>".into(), + token::PlusEq => "+=".into(), + token::MinusEq => "-=".into(), + token::StarEq => "*=".into(), + token::SlashEq => "/=".into(), + token::PercentEq => "%=".into(), + token::CaretEq => "^=".into(), + token::AndEq => "&=".into(), + token::OrEq => "|=".into(), + token::ShlEq => "<<=".into(), + token::ShrEq => ">>=".into(), /* Structural symbols */ token::At => "@".into(), @@ -933,14 +991,13 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere token::RArrow => "->".into(), token::LArrow => "<-".into(), token::FatArrow => "=>".into(), - token::OpenDelim(Delimiter::Parenthesis) => "(".into(), - token::CloseDelim(Delimiter::Parenthesis) => ")".into(), - token::OpenDelim(Delimiter::Bracket) => "[".into(), - token::CloseDelim(Delimiter::Bracket) => "]".into(), - token::OpenDelim(Delimiter::Brace) => "{".into(), - token::CloseDelim(Delimiter::Brace) => "}".into(), - token::OpenDelim(Delimiter::Invisible(_)) - | token::CloseDelim(Delimiter::Invisible(_)) => "".into(), + token::OpenParen => "(".into(), + token::CloseParen => ")".into(), + token::OpenBracket => "[".into(), + token::CloseBracket => "]".into(), + token::OpenBrace => "{".into(), + token::CloseBrace => "}".into(), + token::OpenInvisible(_) | token::CloseInvisible(_) => "".into(), token::Pound => "#".into(), token::Dollar => "$".into(), token::Question => "?".into(), @@ -969,8 +1026,6 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere doc_comment_to_string(comment_kind, attr_style, data).into() } token::Eof => "".into(), - - token::Interpolated(ref nt) => self.nonterminal_to_string(&nt).into(), } } @@ -1018,11 +1073,8 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere fn block_to_string(&self, blk: &ast::Block) -> String { Self::to_string(|s| { - // Containing cbox, will be closed by `print_block` at `}`. - s.cbox(INDENT_UNIT); - // Head-ibox, will be closed by `print_block` after `{`. - s.ibox(0); - s.print_block(blk) + let (cb, ib) = s.head(""); + s.print_block(blk, cb, ib) }) } @@ -1086,7 +1138,7 @@ impl<'a> PrintState<'a> for State<'a> { impl<'a> State<'a> { pub fn new() -> State<'a> { - State { s: pp::Printer::new(), comments: None, ann: &NoAnn } + State { s: pp::Printer::new(), comments: None, ann: &NoAnn, is_sdylib_interface: false } } fn commasep_cmnt(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G) @@ -1094,7 +1146,7 @@ impl<'a> State<'a> { F: FnMut(&mut State<'_>, &T), G: FnMut(&T) -> rustc_span::Span, { - self.rbox(0, b); + let rb = self.rbox(0, b); let len = elts.len(); let mut i = 0; for elt in elts { @@ -1107,7 +1159,7 @@ impl<'a> State<'a> { self.space_if_not_bol(); } } - self.end(); + self.end(rb); } fn commasep_exprs(&mut self, b: Breaks, exprs: &[P]) { @@ -1166,6 +1218,17 @@ impl<'a> State<'a> { self.print_expr_anon_const(end, &[]); } } + rustc_ast::TyPatKind::Or(variants) => { + let mut first = true; + for pat in variants { + if first { + first = false + } else { + self.word(" | "); + } + self.print_ty_pat(pat); + } + } rustc_ast::TyPatKind::Err(_) => { self.popen(); self.word("/*ERROR*/"); @@ -1176,7 +1239,7 @@ impl<'a> State<'a> { pub fn print_type(&mut self, ty: &ast::Ty) { self.maybe_print_comment(ty.span.lo()); - self.ibox(0); + let ib = self.ibox(0); match &ty.kind { ast::TyKind::Slice(ty) => { self.word("["); @@ -1218,12 +1281,12 @@ impl<'a> State<'a> { self.print_ty_fn(f.ext, f.safety, &f.decl, None, &f.generic_params); } ast::TyKind::UnsafeBinder(f) => { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("unsafe"); self.print_generic_params(&f.generic_params); self.nbsp(); self.print_type(&f.inner_ty); - self.end(); + self.end(ib); } ast::TyKind::Path(None, path) => { self.print_path(path, false, 0); @@ -1281,7 +1344,7 @@ impl<'a> State<'a> { self.print_ty_pat(pat); } } - self.end(); + self.end(ib); } fn print_trait_ref(&mut self, t: &ast::TraitRef) { @@ -1328,12 +1391,15 @@ impl<'a> State<'a> { ast::StmtKind::Let(loc) => { self.print_outer_attributes(&loc.attrs); self.space_if_not_bol(); - self.ibox(INDENT_UNIT); + let ib1 = self.ibox(INDENT_UNIT); + if loc.super_.is_some() { + self.word_nbsp("super"); + } self.word_nbsp("let"); - self.ibox(INDENT_UNIT); + let ib2 = self.ibox(INDENT_UNIT); self.print_local_decl(loc); - self.end(); + self.end(ib2); if let Some((init, els)) = loc.kind.init_else_opt() { self.nbsp(); self.word_space("="); @@ -1343,14 +1409,14 @@ impl<'a> State<'a> { FixupContext::default(), ); if let Some(els) = els { - self.cbox(INDENT_UNIT); - self.ibox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word(" else "); - self.print_block(els); + self.print_block(els, cb, ib); } } self.word(";"); - self.end(); // `let` ibox + self.end(ib1); } ast::StmtKind::Item(item) => self.print_item(item), ast::StmtKind::Expr(expr) => { @@ -1381,23 +1447,30 @@ impl<'a> State<'a> { self.maybe_print_trailing_comment(st.span, None) } - fn print_block(&mut self, blk: &ast::Block) { - self.print_block_with_attrs(blk, &[]) + fn print_block(&mut self, blk: &ast::Block, cb: BoxMarker, ib: BoxMarker) { + self.print_block_with_attrs(blk, &[], cb, ib) } - fn print_block_unclosed_indent(&mut self, blk: &ast::Block) { - self.print_block_maybe_unclosed(blk, &[], false) + fn print_block_unclosed_indent(&mut self, blk: &ast::Block, ib: BoxMarker) { + self.print_block_maybe_unclosed(blk, &[], None, ib) } - fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) { - self.print_block_maybe_unclosed(blk, attrs, true) + fn print_block_with_attrs( + &mut self, + blk: &ast::Block, + attrs: &[ast::Attribute], + cb: BoxMarker, + ib: BoxMarker, + ) { + self.print_block_maybe_unclosed(blk, attrs, Some(cb), ib) } fn print_block_maybe_unclosed( &mut self, blk: &ast::Block, attrs: &[ast::Attribute], - close_box: bool, + cb: Option, + ib: BoxMarker, ) { match blk.rules { BlockCheckMode::Unsafe(..) => self.word_space("unsafe"), @@ -1405,7 +1478,7 @@ impl<'a> State<'a> { } self.maybe_print_comment(blk.span.lo()); self.ann.pre(self, AnnNode::Block(blk)); - self.bopen(); + self.bopen(ib); let has_attrs = self.print_inner_attributes(attrs); @@ -1421,8 +1494,8 @@ impl<'a> State<'a> { } } - let empty = !has_attrs && blk.stmts.is_empty(); - self.bclose_maybe_open(blk.span, empty, close_box); + let no_space = !has_attrs && blk.stmts.is_empty(); + self.bclose_maybe_open(blk.span, no_space, cb); self.ann.post(self, AnnNode::Block(blk)) } @@ -1469,7 +1542,8 @@ impl<'a> State<'a> { true, None, m.args.delim, - &m.args.tokens.clone(), + None, + &m.args.tokens, true, m.span(), ); @@ -1557,8 +1631,8 @@ impl<'a> State<'a> { } } InlineAsmOperand::Label { block } => { - s.head("label"); - s.print_block(block); + let (cb, ib) = s.head("label"); + s.print_block(block, cb, ib); } } } @@ -1615,9 +1689,9 @@ impl<'a> State<'a> { fn print_pat(&mut self, pat: &ast::Pat) { self.maybe_print_comment(pat.span.lo()); self.ann.pre(self, AnnNode::Pat(pat)); - /* Pat isn't normalized, but the beauty of it - is that it doesn't matter */ + /* Pat isn't normalized, but the beauty of it is that it doesn't matter */ match &pat.kind { + PatKind::Missing => unreachable!(), PatKind::Wild => self.word("_"), PatKind::Never => self.word("!"), PatKind::Ident(BindingMode(by_ref, mutbl), ident, sub) => { @@ -1672,13 +1746,13 @@ impl<'a> State<'a> { Consistent, fields, |s, f| { - s.cbox(INDENT_UNIT); + let cb = s.cbox(INDENT_UNIT); if !f.is_shorthand { s.print_ident(f.ident); s.word_nbsp(":"); } s.print_pat(&f.pat); - s.end(); + s.end(cb); }, |f| f.pat.span, ); @@ -1782,6 +1856,13 @@ impl<'a> State<'a> { self.print_mutability(*m, false); self.word("self") } + SelfKind::Pinned(lt, m) => { + self.word("&"); + self.print_opt_lifetime(lt); + self.word("pin "); + self.print_mutability(*m, true); + self.word("self") + } SelfKind::Explicit(typ, m) => { self.print_mutability(*m, false); self.word("self"); @@ -1922,7 +2003,7 @@ impl<'a> State<'a> { } fn print_param(&mut self, input: &ast::Param, is_closure: bool) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.print_outer_attributes_inline(&input.attrs); @@ -1932,12 +2013,7 @@ impl<'a> State<'a> { if let Some(eself) = input.to_self() { self.print_explicit_self(&eself); } else { - let invalid = if let PatKind::Ident(_, ident, _) = input.pat.kind { - ident.name == kw::Empty - } else { - false - }; - if !invalid { + if !matches!(input.pat.kind, PatKind::Missing) { self.print_pat(&input.pat); self.word(":"); self.space(); @@ -1946,16 +2022,16 @@ impl<'a> State<'a> { } } } - self.end(); + self.end(ib); } fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FnRetTy) { if let ast::FnRetTy::Ty(ty) = fn_ret_ty { self.space_if_not_bol(); - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word_space("->"); self.print_type(ty); - self.end(); + self.end(ib); self.maybe_print_comment(ty.span.lo()); } } @@ -1968,20 +2044,12 @@ impl<'a> State<'a> { name: Option, generic_params: &[ast::GenericParam], ) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.print_formal_generic_params(generic_params); - let generics = ast::Generics { - params: ThinVec::new(), - where_clause: ast::WhereClause { - has_where_token: false, - predicates: ThinVec::new(), - span: DUMMY_SP, - }, - span: DUMMY_SP, - }; + let generics = ast::Generics::default(); let header = ast::FnHeader { safety, ext, ..ast::FnHeader::default() }; self.print_fn(decl, header, name, &generics); - self.end(); + self.end(ib); } fn print_fn_header_info(&mut self, header: ast::FnHeader) { @@ -2048,7 +2116,7 @@ impl<'a> State<'a> { } fn print_attribute(&mut self, attr: &ast::Attribute) { - self.print_attribute_inline(attr, false) + self.print_attribute_inline(attr, false); } fn print_meta_list_item(&mut self, item: &ast::MetaItemInner) { @@ -2059,7 +2127,7 @@ impl<'a> State<'a> { } fn print_meta_item(&mut self, item: &ast::MetaItem) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); match &item.kind { ast::MetaItemKind::Word => self.print_path(&item.path, false, 0), ast::MetaItemKind::NameValue(value) => { @@ -2075,7 +2143,7 @@ impl<'a> State<'a> { self.pclose(); } } - self.end(); + self.end(ib); } pub(crate) fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 496323a35b8d9..c9a7e2aebd01b 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -8,7 +8,7 @@ use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast::util::parser::{self, ExprPrecedence, Fixity}; use rustc_ast::{ self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount, - FormatDebugHex, FormatSign, FormatTrait, token, + FormatDebugHex, FormatSign, FormatTrait, YieldKind, token, }; use crate::pp::Breaks::Inconsistent; @@ -21,20 +21,20 @@ impl<'a> State<'a> { match &_else.kind { // Another `else if` block. ast::ExprKind::If(i, then, e) => { - self.cbox(INDENT_UNIT - 1); - self.ibox(0); + let cb = self.cbox(0); + let ib = self.ibox(0); self.word(" else if "); self.print_expr_as_cond(i); self.space(); - self.print_block(then); + self.print_block(then, cb, ib); self.print_else(e.as_deref()) } // Final `else` block. - ast::ExprKind::Block(b, _) => { - self.cbox(INDENT_UNIT - 1); - self.ibox(0); + ast::ExprKind::Block(b, None) => { + let cb = self.cbox(0); + let ib = self.ibox(0); self.word(" else "); - self.print_block(b) + self.print_block(b, cb, ib) } // Constraints would be great here! _ => { @@ -45,10 +45,12 @@ impl<'a> State<'a> { } fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) { - self.head("if"); + let cb = self.cbox(0); + let ib = self.ibox(0); + self.word_nbsp("if"); self.print_expr_as_cond(test); self.space(); - self.print_block(blk); + self.print_block(blk, cb, ib); self.print_else(elseopt) } @@ -110,11 +112,11 @@ impl<'a> State<'a> { } fn print_expr_vec(&mut self, exprs: &[P]) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("["); self.commasep_exprs(Inconsistent, exprs); self.word("]"); - self.end(); + self.end(ib); } pub(super) fn print_expr_anon_const( @@ -122,27 +124,27 @@ impl<'a> State<'a> { expr: &ast::AnonConst, attrs: &[ast::Attribute], ) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("const"); self.nbsp(); if let ast::ExprKind::Block(block, None) = &expr.value.kind { - self.cbox(0); - self.ibox(0); - self.print_block_with_attrs(block, attrs); + let cb = self.cbox(0); + let ib = self.ibox(0); + self.print_block_with_attrs(block, attrs, cb, ib); } else { self.print_expr(&expr.value, FixupContext::default()); } - self.end(); + self.end(ib); } fn print_expr_repeat(&mut self, element: &ast::Expr, count: &ast::AnonConst) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("["); self.print_expr(element, FixupContext::default()); self.word_space(";"); self.print_expr(&count.value, FixupContext::default()); self.word("]"); - self.end(); + self.end(ib); } fn print_expr_struct( @@ -167,7 +169,7 @@ impl<'a> State<'a> { self.word("}"); return; } - self.cbox(0); + let cb = self.cbox(0); for (pos, field) in fields.iter().with_position() { let is_first = matches!(pos, Position::First | Position::Only); let is_last = matches!(pos, Position::Last | Position::Only); @@ -198,7 +200,7 @@ impl<'a> State<'a> { self.space(); } self.offset(-INDENT_UNIT); - self.end(); + self.end(cb); self.word("}"); } @@ -274,22 +276,22 @@ impl<'a> State<'a> { fn print_expr_binary( &mut self, - op: ast::BinOp, + op: ast::BinOpKind, lhs: &ast::Expr, rhs: &ast::Expr, fixup: FixupContext, ) { - let binop_prec = op.node.precedence(); + let binop_prec = op.precedence(); let left_prec = lhs.precedence(); let right_prec = rhs.precedence(); - let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() { + let (mut left_needs_paren, right_needs_paren) = match op.fixity() { Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec), Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec), }; - match (&lhs.kind, op.node) { + match (&lhs.kind, op) { // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead // of `(x as i32) < ...`. We need to convince it _not_ to do that. @@ -312,7 +314,7 @@ impl<'a> State<'a> { self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression()); self.space(); - self.word_space(op.node.as_str()); + self.word_space(op.as_str()); self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression()); } @@ -366,7 +368,7 @@ impl<'a> State<'a> { self.print_outer_attributes(attrs); } - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); // The Match subexpression in `match x {} - 1` must be parenthesized if // it is the leftmost subexpression in a statement: @@ -410,7 +412,7 @@ impl<'a> State<'a> { self.print_expr_method_call(seg, receiver, args, fixup); } ast::ExprKind::Binary(op, lhs, rhs) => { - self.print_expr_binary(*op, lhs, rhs, fixup); + self.print_expr_binary(op.node, lhs, rhs, fixup); } ast::ExprKind::Unary(op, expr) => { self.print_expr_unary(*op, expr, fixup); @@ -438,14 +440,14 @@ impl<'a> State<'a> { ast::ExprKind::Type(expr, ty) => { self.word("builtin # type_ascribe"); self.popen(); - self.ibox(0); + let ib = self.ibox(0); self.print_expr(expr, FixupContext::default()); self.word(","); self.space_if_not_bol(); self.print_type(ty); - self.end(); + self.end(ib); self.pclose(); } ast::ExprKind::Let(pat, scrutinee, _, _) => { @@ -457,20 +459,20 @@ impl<'a> State<'a> { self.print_ident(label.ident); self.word_space(":"); } - self.cbox(0); - self.ibox(0); + let cb = self.cbox(0); + let ib = self.ibox(0); self.word_nbsp("while"); self.print_expr_as_cond(test); self.space(); - self.print_block_with_attrs(blk, attrs); + self.print_block_with_attrs(blk, attrs, cb, ib); } ast::ExprKind::ForLoop { pat, iter, body, label, kind } => { if let Some(label) = label { self.print_ident(label.ident); self.word_space(":"); } - self.cbox(0); - self.ibox(0); + let cb = self.cbox(0); + let ib = self.ibox(0); self.word_nbsp("for"); if kind == &ForLoopKind::ForAwait { self.word_nbsp("await"); @@ -480,21 +482,21 @@ impl<'a> State<'a> { self.word_space("in"); self.print_expr_as_cond(iter); self.space(); - self.print_block_with_attrs(body, attrs); + self.print_block_with_attrs(body, attrs, cb, ib); } ast::ExprKind::Loop(blk, opt_label, _) => { + let cb = self.cbox(0); + let ib = self.ibox(0); if let Some(label) = opt_label { self.print_ident(label.ident); self.word_space(":"); } - self.cbox(0); - self.ibox(0); self.word_nbsp("loop"); - self.print_block_with_attrs(blk, attrs); + self.print_block_with_attrs(blk, attrs, cb, ib); } ast::ExprKind::Match(expr, arms, match_kind) => { - self.cbox(0); - self.ibox(0); + let cb = self.cbox(0); + let ib = self.ibox(0); match match_kind { MatchKind::Prefix => { @@ -512,13 +514,13 @@ impl<'a> State<'a> { } } - self.bopen(); + self.bopen(ib); self.print_inner_attributes_no_trailing_hardbreak(attrs); for arm in arms { self.print_arm(arm); } let empty = attrs.is_empty() && arms.is_empty(); - self.bclose(expr.span, empty); + self.bclose(expr.span, empty, cb); } ast::ExprKind::Closure(box ast::Closure { binder, @@ -540,12 +542,6 @@ impl<'a> State<'a> { self.print_fn_params_and_ret(fn_decl, true); self.space(); self.print_expr(body, FixupContext::default()); - self.end(); // need to close a box - - // a box will be closed by print_expr, but we didn't want an overall - // wrapper so we closed the corresponding opening. so create an - // empty box to satisfy the close. - self.ibox(0); } ast::ExprKind::Block(blk, opt_label) => { if let Some(label) = opt_label { @@ -553,18 +549,18 @@ impl<'a> State<'a> { self.word_space(":"); } // containing cbox, will be closed by print-block at } - self.cbox(0); + let cb = self.cbox(0); // head-box, will be closed by print-block after { - self.ibox(0); - self.print_block_with_attrs(blk, attrs); + let ib = self.ibox(0); + self.print_block_with_attrs(blk, attrs, cb, ib); } ast::ExprKind::Gen(capture_clause, blk, kind, _decl_span) => { self.word_nbsp(kind.modifier()); self.print_capture_clause(*capture_clause); // cbox/ibox in analogy to the `ExprKind::Block` arm above - self.cbox(0); - self.ibox(0); - self.print_block_with_attrs(blk, attrs); + let cb = self.cbox(0); + let ib = self.ibox(0); + self.print_block_with_attrs(blk, attrs, cb, ib); } ast::ExprKind::Await(expr, _) => { self.print_expr_cond_paren( @@ -574,6 +570,14 @@ impl<'a> State<'a> { ); self.word(".await"); } + ast::ExprKind::Use(expr, _) => { + self.print_expr_cond_paren( + expr, + expr.precedence() < ExprPrecedence::Unambiguous, + fixup, + ); + self.word(".use"); + } ast::ExprKind::Assign(lhs, rhs, _) => { self.print_expr_cond_paren( lhs, @@ -597,8 +601,7 @@ impl<'a> State<'a> { fixup.leftmost_subexpression(), ); self.space(); - self.word(op.node.as_str()); - self.word_space("="); + self.word_space(op.node.as_str()); self.print_expr_cond_paren( rhs, rhs.precedence() < ExprPrecedence::Assign, @@ -719,19 +722,19 @@ impl<'a> State<'a> { // FIXME: Print `builtin # format_args` once macro `format_args` uses `builtin_syntax`. self.word("format_args!"); self.popen(); - self.ibox(0); + let ib = self.ibox(0); self.word(reconstruct_format_args_template_string(&fmt.template)); for arg in fmt.arguments.all_args() { self.word_space(","); self.print_expr(&arg.expr, FixupContext::default()); } - self.end(); + self.end(ib); self.pclose(); } ast::ExprKind::OffsetOf(container, fields) => { self.word("builtin # offset_of"); self.popen(); - self.ibox(0); + let ib = self.ibox(0); self.print_type(container); self.word(","); self.space(); @@ -744,7 +747,7 @@ impl<'a> State<'a> { self.print_ident(field); } } - self.end(); + self.end(ib); self.pclose(); } ast::ExprKind::MacCall(m) => self.print_mac(m), @@ -753,7 +756,7 @@ impl<'a> State<'a> { self.print_expr(e, FixupContext::default()); self.pclose(); } - ast::ExprKind::Yield(e) => { + ast::ExprKind::Yield(YieldKind::Prefix(e)) => { self.word("yield"); if let Some(expr) = e { @@ -765,6 +768,14 @@ impl<'a> State<'a> { ); } } + ast::ExprKind::Yield(YieldKind::Postfix(e)) => { + self.print_expr_cond_paren( + e, + e.precedence() < ExprPrecedence::Unambiguous, + fixup.leftmost_subexpression_with_dot(), + ); + self.word(".yield"); + } ast::ExprKind::Try(e) => { self.print_expr_cond_paren( e, @@ -774,10 +785,10 @@ impl<'a> State<'a> { self.word("?") } ast::ExprKind::TryBlock(blk) => { - self.cbox(0); - self.ibox(0); + let cb = self.cbox(0); + let ib = self.ibox(0); self.word_nbsp("try"); - self.print_block_with_attrs(blk, attrs) + self.print_block_with_attrs(blk, attrs, cb, ib) } ast::ExprKind::UnsafeBinderCast(kind, expr, ty) => { self.word("builtin # "); @@ -786,7 +797,7 @@ impl<'a> State<'a> { ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder"), } self.popen(); - self.ibox(0); + let ib = self.ibox(0); self.print_expr(expr, FixupContext::default()); if let Some(ty) = ty { @@ -795,7 +806,7 @@ impl<'a> State<'a> { self.print_type(ty); } - self.end(); + self.end(ib); self.pclose(); } ast::ExprKind::Err(_) => { @@ -816,7 +827,7 @@ impl<'a> State<'a> { self.pclose(); } - self.end(); + self.end(ib); } fn print_arm(&mut self, arm: &ast::Arm) { @@ -824,8 +835,8 @@ impl<'a> State<'a> { if arm.attrs.is_empty() { self.space(); } - self.cbox(INDENT_UNIT); - self.ibox(0); + let cb = self.cbox(INDENT_UNIT); + let ib = self.ibox(0); self.maybe_print_comment(arm.pat.span.lo()); self.print_outer_attributes(&arm.attrs); self.print_pat(&arm.pat); @@ -846,8 +857,7 @@ impl<'a> State<'a> { self.word_space(":"); } - // The block will close the pattern's ibox. - self.print_block_unclosed_indent(blk); + self.print_block_unclosed_indent(blk, ib); // If it is a user-provided unsafe block, print a comma after it. if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules { @@ -855,15 +865,16 @@ impl<'a> State<'a> { } } _ => { - self.end(); // Close the ibox for the pattern. + self.end(ib); self.print_expr(body, FixupContext::new_match_arm()); self.word(","); } } } else { + self.end(ib); self.word(","); } - self.end(); // Close enclosing cbox. + self.end(cb); } fn print_closure_binder(&mut self, binder: &ast::ClosureBinder) { @@ -885,6 +896,7 @@ impl<'a> State<'a> { fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) { match capture_clause { ast::CaptureBy::Value { .. } => self.word_space("move"), + ast::CaptureBy::Use { .. } => self.word_space("use"), ast::CaptureBy::Ref => {} } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index c10b5ad34e102..70cf2f2a45982 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -5,6 +5,7 @@ use rustc_ast::ModKind; use rustc_ast::ptr::P; use rustc_span::Ident; +use crate::pp::BoxMarker; use crate::pp::Breaks::Inconsistent; use crate::pprust::state::fixup::FixupContext; use crate::pprust::state::{AnnNode, INDENT_UNIT, PrintState, State}; @@ -28,36 +29,43 @@ impl<'a> State<'a> { } fn print_foreign_item(&mut self, item: &ast::ForeignItem) { - let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item; + let ast::Item { id, span, ref attrs, ref kind, ref vis, tokens: _ } = *item; self.ann.pre(self, AnnNode::SubItem(id)); self.hardbreak_if_not_bol(); self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { ast::ForeignItemKind::Fn(func) => { - self.print_fn_full(ident, vis, attrs, &*func); - } - ast::ForeignItemKind::Static(box ast::StaticItem { ty, mutability, expr, safety }) => { - self.print_item_const( - ident, - Some(*mutability), - &ast::Generics::default(), - ty, - expr.as_deref(), - vis, - *safety, - ast::Defaultness::Final, - ) + self.print_fn_full(vis, attrs, &*func); } + ast::ForeignItemKind::Static(box ast::StaticItem { + ident, + ty, + mutability, + expr, + safety, + define_opaque, + }) => self.print_item_const( + *ident, + Some(*mutability), + &ast::Generics::default(), + ty, + expr.as_deref(), + vis, + *safety, + ast::Defaultness::Final, + define_opaque.as_deref(), + ), ast::ForeignItemKind::TyAlias(box ast::TyAlias { defaultness, + ident, generics, where_clauses, bounds, ty, }) => { self.print_associated_type( - ident, + *ident, generics, *where_clauses, bounds, @@ -86,8 +94,10 @@ impl<'a> State<'a> { vis: &ast::Visibility, safety: ast::Safety, defaultness: ast::Defaultness, + define_opaque: Option<&[(ast::NodeId, ast::Path)]>, ) { - self.head(""); + self.print_define_opaques(define_opaque); + let (cb, ib) = self.head(""); self.print_visibility(vis); self.print_safety(safety); self.print_defaultness(defaultness); @@ -104,14 +114,14 @@ impl<'a> State<'a> { if body.is_some() { self.space(); } - self.end(); // end the head-ibox + self.end(ib); if let Some(body) = body { self.word_space("="); self.print_expr(body, FixupContext::default()); } self.print_where_clause(&generics.where_clause); self.word(";"); - self.end(); // end the outer cbox + self.end(cb); } fn print_associated_type( @@ -126,7 +136,7 @@ impl<'a> State<'a> { ) { let (before_predicates, after_predicates) = generics.where_clause.predicates.split_at(where_clauses.split); - self.head(""); + let (cb, ib) = self.head(""); self.print_visibility(vis); self.print_defaultness(defaultness); self.word_space("type"); @@ -144,29 +154,33 @@ impl<'a> State<'a> { } self.print_where_clause_parts(where_clauses.after.has_where_token, after_predicates); self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } /// Pretty-prints an item. pub(crate) fn print_item(&mut self, item: &ast::Item) { + if self.is_sdylib_interface && item.span.is_dummy() { + // Do not print prelude for interface files. + return; + } self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); self.print_outer_attributes(&item.attrs); self.ann.pre(self, AnnNode::Item(item)); match &item.kind { - ast::ItemKind::ExternCrate(orig_name) => { - self.head(visibility_qualified(&item.vis, "extern crate")); + ast::ItemKind::ExternCrate(orig_name, ident) => { + let (cb, ib) = self.head(visibility_qualified(&item.vis, "extern crate")); if let &Some(orig_name) = orig_name { self.print_name(orig_name); self.space(); self.word("as"); self.space(); } - self.print_ident(item.ident); + self.print_ident(*ident); self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } ast::ItemKind::Use(tree) => { self.print_visibility(&item.vis); @@ -174,10 +188,17 @@ impl<'a> State<'a> { self.print_use_tree(tree); self.word(";"); } - ast::ItemKind::Static(box StaticItem { ty, safety, mutability: mutbl, expr: body }) => { + ast::ItemKind::Static(box StaticItem { + ident, + ty, + safety, + mutability: mutbl, + expr: body, + define_opaque, + }) => { self.print_safety(*safety); self.print_item_const( - item.ident, + *ident, Some(*mutbl), &ast::Generics::default(), ty, @@ -185,11 +206,19 @@ impl<'a> State<'a> { &item.vis, ast::Safety::Default, ast::Defaultness::Final, + define_opaque.as_deref(), ); } - ast::ItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => { + ast::ItemKind::Const(box ast::ConstItem { + defaultness, + ident, + generics, + ty, + expr, + define_opaque, + }) => { self.print_item_const( - item.ident, + *ident, None, generics, ty, @@ -197,39 +226,40 @@ impl<'a> State<'a> { &item.vis, ast::Safety::Default, *defaultness, + define_opaque.as_deref(), ); } ast::ItemKind::Fn(func) => { - self.print_fn_full(item.ident, &item.vis, &item.attrs, &*func); + self.print_fn_full(&item.vis, &item.attrs, &*func); } - ast::ItemKind::Mod(safety, mod_kind) => { - self.head(Self::to_string(|s| { + ast::ItemKind::Mod(safety, ident, mod_kind) => { + let (cb, ib) = self.head(Self::to_string(|s| { s.print_visibility(&item.vis); s.print_safety(*safety); s.word("mod"); })); - self.print_ident(item.ident); + self.print_ident(*ident); match mod_kind { ModKind::Loaded(items, ..) => { self.nbsp(); - self.bopen(); + self.bopen(ib); self.print_inner_attributes(&item.attrs); for item in items { self.print_item(item); } let empty = item.attrs.is_empty() && items.is_empty(); - self.bclose(item.span, empty); + self.bclose(item.span, empty, cb); } ModKind::Unloaded => { self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } } } ast::ItemKind::ForeignMod(nmod) => { - self.head(Self::to_string(|s| { + let (cb, ib) = self.head(Self::to_string(|s| { s.print_safety(nmod.safety); s.word("extern"); })); @@ -237,28 +267,29 @@ impl<'a> State<'a> { self.print_token_literal(abi.as_token_lit(), abi.span); self.nbsp(); } - self.bopen(); + self.bopen(ib); self.print_foreign_mod(nmod, &item.attrs); let empty = item.attrs.is_empty() && nmod.items.is_empty(); - self.bclose(item.span, empty); + self.bclose(item.span, empty, cb); } ast::ItemKind::GlobalAsm(asm) => { // FIXME: Print `builtin # global_asm` once macro `global_asm` uses `builtin_syntax`. - self.head(visibility_qualified(&item.vis, "global_asm!")); + let (cb, ib) = self.head(visibility_qualified(&item.vis, "global_asm!")); self.print_inline_asm(asm); self.word(";"); - self.end(); - self.end(); + self.end(ib); + self.end(cb); } ast::ItemKind::TyAlias(box ast::TyAlias { defaultness, + ident, generics, where_clauses, bounds, ty, }) => { self.print_associated_type( - item.ident, + *ident, generics, *where_clauses, bounds, @@ -267,16 +298,16 @@ impl<'a> State<'a> { *defaultness, ); } - ast::ItemKind::Enum(enum_definition, params) => { - self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis); + ast::ItemKind::Enum(ident, enum_definition, params) => { + self.print_enum_def(enum_definition, params, *ident, item.span, &item.vis); } - ast::ItemKind::Struct(struct_def, generics) => { - self.head(visibility_qualified(&item.vis, "struct")); - self.print_struct(struct_def, generics, item.ident, item.span, true); + ast::ItemKind::Struct(ident, struct_def, generics) => { + let (cb, ib) = self.head(visibility_qualified(&item.vis, "struct")); + self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib); } - ast::ItemKind::Union(struct_def, generics) => { - self.head(visibility_qualified(&item.vis, "union")); - self.print_struct(struct_def, generics, item.ident, item.span, true); + ast::ItemKind::Union(ident, struct_def, generics) => { + let (cb, ib) = self.head(visibility_qualified(&item.vis, "union")); + self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib); } ast::ItemKind::Impl(box ast::Impl { safety, @@ -288,7 +319,7 @@ impl<'a> State<'a> { self_ty, items, }) => { - self.head(""); + let (cb, ib) = self.head(""); self.print_visibility(&item.vis); self.print_defaultness(*defaultness); self.print_safety(*safety); @@ -317,28 +348,28 @@ impl<'a> State<'a> { self.print_where_clause(&generics.where_clause); self.space(); - self.bopen(); + self.bopen(ib); self.print_inner_attributes(&item.attrs); for impl_item in items { self.print_assoc_item(impl_item); } let empty = item.attrs.is_empty() && items.is_empty(); - self.bclose(item.span, empty); + self.bclose(item.span, empty, cb); } ast::ItemKind::Trait(box ast::Trait { - is_auto, safety, + is_auto, + ident, generics, bounds, items, - .. }) => { - self.head(""); + let (cb, ib) = self.head(""); self.print_visibility(&item.vis); self.print_safety(*safety); self.print_is_auto(*is_auto); self.word_nbsp("trait"); - self.print_ident(item.ident); + self.print_ident(*ident); self.print_generic_params(&generics.params); if !bounds.is_empty() { self.word_nbsp(":"); @@ -346,17 +377,17 @@ impl<'a> State<'a> { } self.print_where_clause(&generics.where_clause); self.word(" "); - self.bopen(); + self.bopen(ib); self.print_inner_attributes(&item.attrs); for trait_item in items { self.print_assoc_item(trait_item); } let empty = item.attrs.is_empty() && items.is_empty(); - self.bclose(item.span, empty); + self.bclose(item.span, empty, cb); } - ast::ItemKind::TraitAlias(generics, bounds) => { - self.head(visibility_qualified(&item.vis, "trait")); - self.print_ident(item.ident); + ast::ItemKind::TraitAlias(ident, generics, bounds) => { + let (cb, ib) = self.head(visibility_qualified(&item.vis, "trait")); + self.print_ident(*ident); self.print_generic_params(&generics.params); self.nbsp(); if !bounds.is_empty() { @@ -365,8 +396,8 @@ impl<'a> State<'a> { } self.print_where_clause(&generics.where_clause); self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } ast::ItemKind::MacCall(mac) => { self.print_mac(mac); @@ -374,8 +405,8 @@ impl<'a> State<'a> { self.word(";"); } } - ast::ItemKind::MacroDef(macro_def) => { - self.print_mac_def(macro_def, &item.ident, item.span, |state| { + ast::ItemKind::MacroDef(ident, macro_def) => { + self.print_mac_def(macro_def, &ident, item.span, |state| { state.print_visibility(&item.vis) }); } @@ -407,28 +438,24 @@ impl<'a> State<'a> { span: rustc_span::Span, visibility: &ast::Visibility, ) { - self.head(visibility_qualified(visibility, "enum")); + let (cb, ib) = self.head(visibility_qualified(visibility, "enum")); self.print_ident(ident); self.print_generic_params(&generics.params); self.print_where_clause(&generics.where_clause); self.space(); - self.print_variants(&enum_definition.variants, span) - } - - fn print_variants(&mut self, variants: &[ast::Variant], span: rustc_span::Span) { - self.bopen(); - for v in variants { + self.bopen(ib); + for v in enum_definition.variants.iter() { self.space_if_not_bol(); self.maybe_print_comment(v.span.lo()); self.print_outer_attributes(&v.attrs); - self.ibox(0); + let ib = self.ibox(0); self.print_variant(v); self.word(","); - self.end(); + self.end(ib); self.maybe_print_trailing_comment(v.span, None); } - let empty = variants.is_empty(); - self.bclose(span, empty) + let empty = enum_definition.variants.is_empty(); + self.bclose(span, empty, cb) } pub(crate) fn print_visibility(&mut self, vis: &ast::Visibility) { @@ -452,33 +479,6 @@ impl<'a> State<'a> { } } - pub(crate) fn print_record_struct_body( - &mut self, - fields: &[ast::FieldDef], - span: rustc_span::Span, - ) { - self.nbsp(); - self.bopen(); - - let empty = fields.is_empty(); - if !empty { - self.hardbreak_if_not_bol(); - - for field in fields { - self.hardbreak_if_not_bol(); - self.maybe_print_comment(field.span.lo()); - self.print_outer_attributes(&field.attrs); - self.print_visibility(&field.vis); - self.print_ident(field.ident.unwrap()); - self.word_nbsp(":"); - self.print_type(&field.ty); - self.word(","); - } - } - - self.bclose(span, empty); - } - fn print_struct( &mut self, struct_def: &ast::VariantData, @@ -486,6 +486,8 @@ impl<'a> State<'a> { ident: Ident, span: rustc_span::Span, print_finalizer: bool, + cb: BoxMarker, + ib: BoxMarker, ) { self.print_ident(ident); self.print_generic_params(&generics.params); @@ -505,21 +507,40 @@ impl<'a> State<'a> { if print_finalizer { self.word(";"); } - self.end(); - self.end(); // Close the outer-box. + self.end(ib); + self.end(cb); } ast::VariantData::Struct { fields, .. } => { self.print_where_clause(&generics.where_clause); - self.print_record_struct_body(fields, span); + self.nbsp(); + self.bopen(ib); + + let empty = fields.is_empty(); + if !empty { + self.hardbreak_if_not_bol(); + + for field in fields { + self.hardbreak_if_not_bol(); + self.maybe_print_comment(field.span.lo()); + self.print_outer_attributes(&field.attrs); + self.print_visibility(&field.vis); + self.print_ident(field.ident.unwrap()); + self.word_nbsp(":"); + self.print_type(&field.ty); + self.word(","); + } + } + + self.bclose(span, empty, cb); } } } pub(crate) fn print_variant(&mut self, v: &ast::Variant) { - self.head(""); + let (cb, ib) = self.head(""); self.print_visibility(&v.vis); let generics = ast::Generics::default(); - self.print_struct(&v.data, &generics, v.ident, v.span, false); + self.print_struct(&v.data, &generics, v.ident, v.span, false, cb, ib); if let Some(d) = &v.disr_expr { self.space(); self.word_space("="); @@ -528,18 +549,25 @@ impl<'a> State<'a> { } fn print_assoc_item(&mut self, item: &ast::AssocItem) { - let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item; + let ast::Item { id, span, ref attrs, ref kind, ref vis, tokens: _ } = *item; self.ann.pre(self, AnnNode::SubItem(id)); self.hardbreak_if_not_bol(); self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { ast::AssocItemKind::Fn(func) => { - self.print_fn_full(ident, vis, attrs, &*func); + self.print_fn_full(vis, attrs, &*func); } - ast::AssocItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => { + ast::AssocItemKind::Const(box ast::ConstItem { + defaultness, + ident, + generics, + ty, + expr, + define_opaque, + }) => { self.print_item_const( - ident, + *ident, None, generics, ty, @@ -547,17 +575,19 @@ impl<'a> State<'a> { vis, ast::Safety::Default, *defaultness, + define_opaque.as_deref(), ); } ast::AssocItemKind::Type(box ast::TyAlias { defaultness, + ident, generics, where_clauses, bounds, ty, }) => { self.print_associated_type( - ident, + *ident, generics, *where_clauses, bounds, @@ -601,9 +631,7 @@ impl<'a> State<'a> { kind: DelegationKind<'_>, body: &Option>, ) { - if body.is_some() { - self.head(""); - } + let body_cb_ib = body.as_ref().map(|body| (body, self.head(""))); self.print_visibility(vis); self.word_nbsp("reuse"); @@ -635,40 +663,58 @@ impl<'a> State<'a> { self.word("*"); } } - if let Some(body) = body { + if let Some((body, (cb, ib))) = body_cb_ib { self.nbsp(); - self.print_block_with_attrs(body, attrs); + self.print_block_with_attrs(body, attrs, cb, ib); } else { self.word(";"); } } - fn print_fn_full( - &mut self, - name: Ident, - vis: &ast::Visibility, - attrs: &[ast::Attribute], - func: &ast::Fn, - ) { - let ast::Fn { defaultness, generics, sig, contract, body } = func; - if body.is_some() { - self.head(""); - } + fn print_fn_full(&mut self, vis: &ast::Visibility, attrs: &[ast::Attribute], func: &ast::Fn) { + let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque } = func; + + self.print_define_opaques(define_opaque.as_deref()); + + let body_cb_ib = body.as_ref().map(|body| (body, self.head(""))); + self.print_visibility(vis); self.print_defaultness(*defaultness); - self.print_fn(&sig.decl, sig.header, Some(name), generics); + self.print_fn(&sig.decl, sig.header, Some(*ident), generics); if let Some(contract) = &contract { self.nbsp(); self.print_contract(contract); } - if let Some(body) = body { + if let Some((body, (cb, ib))) = body_cb_ib { + if self.is_sdylib_interface { + self.word(";"); + self.end(ib); // end inner head-block + self.end(cb); // end outer head-block + return; + } + self.nbsp(); - self.print_block_with_attrs(body, attrs); + self.print_block_with_attrs(body, attrs, cb, ib); } else { self.word(";"); } } + fn print_define_opaques(&mut self, define_opaque: Option<&[(ast::NodeId, ast::Path)]>) { + if let Some(define_opaque) = define_opaque { + self.word("#[define_opaque("); + for (i, (_, path)) in define_opaque.iter().enumerate() { + if i != 0 { + self.word_space(","); + } + + self.print_path(path, false, 0); + } + self.word(")]"); + } + self.hardbreak_if_not_bol(); + } + fn print_contract(&mut self, contract: &ast::FnContract) { if let Some(pred) = &contract.requires { self.word("rustc_requires"); @@ -688,17 +734,17 @@ impl<'a> State<'a> { &mut self, decl: &ast::FnDecl, header: ast::FnHeader, - name: Option, + ident: Option, generics: &ast::Generics, ) { self.print_fn_header_info(header); - if let Some(name) = name { + if let Some(ident) = ident { self.nbsp(); - self.print_ident(name); + self.print_ident(ident); } self.print_generic_params(&generics.params); self.print_fn_params_and_ret(decl, false); - self.print_where_clause(&generics.where_clause) + self.print_where_clause(&generics.where_clause); } pub(crate) fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) { @@ -735,7 +781,8 @@ impl<'a> State<'a> { } pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) { - let ast::WherePredicate { kind, id: _, span: _ } = predicate; + let ast::WherePredicate { attrs, kind, id: _, span: _, is_placeholder: _ } = predicate; + self.print_outer_attributes(attrs); match kind { ast::WherePredicateKind::BoundPredicate(where_bound_predicate) => { self.print_where_bound_predicate(where_bound_predicate); @@ -803,10 +850,10 @@ impl<'a> State<'a> { } else if let [(item, _)] = items.as_slice() { self.print_use_tree(item); } else { - self.cbox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); self.word("{"); self.zerobreak(); - self.ibox(0); + let ib = self.ibox(0); for (pos, use_tree) in items.iter().with_position() { let is_last = matches!(pos, Position::Last | Position::Only); self.print_use_tree(&use_tree.0); @@ -819,11 +866,11 @@ impl<'a> State<'a> { } } } - self.end(); + self.end(ib); self.trailing_comma(); self.offset(-INDENT_UNIT); self.word("}"); - self.end(); + self.end(cb); } } } diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs index 4c42dd1f2023f..786de529c5b89 100644 --- a/compiler/rustc_ast_pretty/src/pprust/tests.rs +++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs @@ -7,14 +7,14 @@ use super::*; fn fun_to_string( decl: &ast::FnDecl, header: ast::FnHeader, - name: Ident, + ident: Ident, generics: &ast::Generics, ) -> String { to_string(|s| { - s.head(""); - s.print_fn(decl, header, Some(name), generics); - s.end(); // Close the head box. - s.end(); // Close the outer box. + let (cb, ib) = s.head(""); + s.print_fn(decl, header, Some(ident), generics); + s.end(ib); + s.end(cb); }) } diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs index e4bb459e6df5a..dbfc95b047ae4 100644 --- a/compiler/rustc_attr_data_structures/src/lib.rs +++ b/compiler/rustc_attr_data_structures/src/lib.rs @@ -1,9 +1,7 @@ // tidy-alphabetical-start #![allow(internal_features)] #![doc(rust_logo)] -#![feature(let_chains)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end mod attributes; @@ -35,13 +33,17 @@ pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStabl /// like [`Span`]s and empty tuples, are gracefully skipped so they don't clutter the /// representation much. pub trait PrintAttribute { - fn print_something(&self) -> bool; + /// Whether or not this will render as something meaningful, or if it's skipped + /// (which will force the containing struct to also skip printing a comma + /// and the field name). + fn should_render(&self) -> bool; + fn print_attribute(&self, p: &mut Printer); } impl PrintAttribute for &T { - fn print_something(&self) -> bool { - T::print_something(self) + fn should_render(&self) -> bool { + T::should_render(self) } fn print_attribute(&self, p: &mut Printer) { @@ -49,9 +51,10 @@ impl PrintAttribute for &T { } } impl PrintAttribute for Option { - fn print_something(&self) -> bool { - self.as_ref().is_some_and(|x| x.print_something()) + fn should_render(&self) -> bool { + self.as_ref().is_some_and(|x| x.should_render()) } + fn print_attribute(&self, p: &mut Printer) { if let Some(i) = self { T::print_attribute(i, p) @@ -59,9 +62,10 @@ impl PrintAttribute for Option { } } impl PrintAttribute for ThinVec { - fn print_something(&self) -> bool { - self.is_empty() || self[0].print_something() + fn should_render(&self) -> bool { + self.is_empty() || self[0].should_render() } + fn print_attribute(&self, p: &mut Printer) { let mut last_printed = false; p.word("["); @@ -70,7 +74,7 @@ impl PrintAttribute for ThinVec { p.word_space(","); } i.print_attribute(p); - last_printed = i.print_something(); + last_printed = i.should_render(); } p.word("]"); } @@ -78,7 +82,7 @@ impl PrintAttribute for ThinVec { macro_rules! print_skip { ($($t: ty),* $(,)?) => {$( impl PrintAttribute for $t { - fn print_something(&self) -> bool { false } + fn should_render(&self) -> bool { false } fn print_attribute(&self, _: &mut Printer) { } })* }; @@ -87,7 +91,7 @@ macro_rules! print_skip { macro_rules! print_disp { ($($t: ty),* $(,)?) => {$( impl PrintAttribute for $t { - fn print_something(&self) -> bool { true } + fn should_render(&self) -> bool { true } fn print_attribute(&self, p: &mut Printer) { p.word(format!("{}", self)); } @@ -97,7 +101,7 @@ macro_rules! print_disp { macro_rules! print_debug { ($($t: ty),* $(,)?) => {$( impl PrintAttribute for $t { - fn print_something(&self) -> bool { true } + fn should_render(&self) -> bool { true } fn print_attribute(&self, p: &mut Printer) { p.word(format!("{:?}", self)); } @@ -106,37 +110,39 @@ macro_rules! print_debug { } macro_rules! print_tup { - (num_print_something $($ts: ident)*) => { 0 $(+ $ts.print_something() as usize)* }; + (num_should_render $($ts: ident)*) => { 0 $(+ $ts.should_render() as usize)* }; () => {}; ($t: ident $($ts: ident)*) => { #[allow(non_snake_case, unused)] impl<$t: PrintAttribute, $($ts: PrintAttribute),*> PrintAttribute for ($t, $($ts),*) { - fn print_something(&self) -> bool { + fn should_render(&self) -> bool { let ($t, $($ts),*) = self; - print_tup!(num_print_something $t $($ts)*) != 0 + print_tup!(num_should_render $t $($ts)*) != 0 } fn print_attribute(&self, p: &mut Printer) { let ($t, $($ts),*) = self; - let parens = print_tup!(num_print_something $t $($ts)*) > 1; + let parens = print_tup!(num_should_render $t $($ts)*) > 1; if parens { - p.word("("); + p.popen(); } - let mut printed_anything = $t.print_something(); + let mut printed_anything = $t.should_render(); $t.print_attribute(p); $( - if printed_anything && $ts.print_something() { - p.word_space(","); + if $ts.should_render() { + if printed_anything { + p.word_space(","); + } printed_anything = true; } $ts.print_attribute(p); )* if parens { - p.word(")"); + p.pclose(); } } } @@ -147,5 +153,46 @@ macro_rules! print_tup { print_tup!(A B C D E F G H); print_skip!(Span, ()); -print_disp!(Symbol, u16, bool, NonZero); -print_debug!(UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency); +print_disp!(u16, bool, NonZero); +print_debug!(Symbol, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency); + +/// Finds attributes in sequences of attributes by pattern matching. +/// +/// A little like `matches` but for attributes. +/// +/// ```rust,ignore (illustrative) +/// // finds the repr attribute +/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) { +/// +/// } +/// +/// // checks if one has matched +/// if find_attr!(attrs, AttributeKind::Repr(_)) { +/// +/// } +/// ``` +/// +/// Often this requires you to first end up with a list of attributes. +/// A common way to get those is through `tcx.get_all_attrs(did)` +#[macro_export] +macro_rules! find_attr { + ($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{ + $crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some() + }}; + + ($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{ + 'done: { + for i in $attributes_list { + let i: &rustc_hir::Attribute = i; + match i { + rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => { + break 'done Some($e); + } + _ => {} + } + } + + None + } + }}; +} diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index c335eeb5f7120..32029137268ba 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -9,14 +9,12 @@ rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } -rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } -rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } thin-vec = "0.2.12" diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index d37ede86cfd2a..c1d95d07f4c65 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -53,7 +53,7 @@ fn parse_unstable<'a>( for param in list.mixed() { let param_span = param.span(); - if let Some(ident) = param.meta_item().and_then(|i| i.word_without_args()) { + if let Some(ident) = param.meta_item().and_then(|i| i.path_without_args().word()) { res.push(ident.name); } else { cx.emit_err(session_diagnostics::ExpectsFeatures { diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 0d6d521b40c61..7cb1fede1741a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -7,7 +7,6 @@ use rustc_session::config::ExpectedValues; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::UNEXPECTED_CFGS; use rustc_session::parse::feature_err; -use rustc_span::symbol::kw; use rustc_span::{Span, Symbol, sym}; use crate::session_diagnostics::{self, UnsupportedLiteralReason}; @@ -89,20 +88,6 @@ pub fn eval_condition( let cfg = match cfg { MetaItemInner::MetaItem(meta_item) => meta_item, MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { - if let Some(features) = features { - // we can't use `try_gate_cfg` as symbols don't differentiate between `r#true` - // and `true`, and we want to keep the former working without feature gate - gate_cfg( - &( - if *b { kw::True } else { kw::False }, - sym::cfg_boolean_literals, - |features: &Features| features.cfg_boolean_literals(), - ), - cfg.span(), - sess, - features, - ); - } return *b; } _ => { @@ -117,7 +102,7 @@ pub fn eval_condition( }; match &cfg.kind { - MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => { + MetaItemKind::List(mis) if cfg.has_name(sym::version) => { try_gate_cfg(sym::version, cfg.span, sess, features); let (min_version, span) = match &mis[..] { [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => { @@ -164,18 +149,18 @@ pub fn eval_condition( // The unwraps below may look dangerous, but we've already asserted // that they won't fail with the loop above. - match cfg.name_or_empty() { - sym::any => mis + match cfg.name() { + Some(sym::any) => mis .iter() // We don't use any() here, because we want to evaluate all cfg condition // as eval_condition can (and does) extra checks .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), - sym::all => mis + Some(sym::all) => mis .iter() // We don't use all() here, because we want to evaluate all cfg condition // as eval_condition can (and does) extra checks .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), - sym::not => { + Some(sym::not) => { let [mi] = mis.as_slice() else { dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); return false; @@ -183,7 +168,7 @@ pub fn eval_condition( !eval_condition(mi, sess, features, eval) } - sym::target => { + Some(sym::target) => { if let Some(features) = features && !features.cfg_target_compact() { diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 7d1417446b21d..fb3d5f57d4fac 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -1,5 +1,4 @@ use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation}; -use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol, sym}; use super::SingleAttributeParser; @@ -13,16 +12,13 @@ pub(crate) struct DeprecationParser; fn get( cx: &AcceptContext<'_>, - ident: Ident, + name: Symbol, param_span: Span, arg: &ArgParser<'_>, item: &Option, ) -> Option { if item.is_some() { - cx.emit_err(session_diagnostics::MultipleItem { - span: param_span, - item: ident.to_string(), - }); + cx.emit_err(session_diagnostics::MultipleItem { span: param_span, item: name.to_string() }); return None; } if let Some(v) = arg.name_value() { @@ -83,16 +79,16 @@ impl SingleAttributeParser for DeprecationParser { return None; }; - let (ident, arg) = param.word_or_empty(); + let ident_name = param.path_without_args().word_sym(); - match ident.name { - sym::since => { - since = Some(get(cx, ident, param_span, arg, &since)?); + match ident_name { + Some(name @ sym::since) => { + since = Some(get(cx, name, param_span, param.args(), &since)?); } - sym::note => { - note = Some(get(cx, ident, param_span, arg, ¬e)?); + Some(name @ sym::note) => { + note = Some(get(cx, name, param_span, param.args(), ¬e)?); } - sym::suggestion => { + Some(name @ sym::suggestion) => { if !features.deprecated_suggestion() { cx.emit_err(session_diagnostics::DeprecatedItemSuggestion { span: param_span, @@ -101,12 +97,12 @@ impl SingleAttributeParser for DeprecationParser { }); } - suggestion = Some(get(cx, ident, param_span, arg, &suggestion)?); + suggestion = Some(get(cx, name, param_span, param.args(), &suggestion)?); } _ => { cx.emit_err(session_diagnostics::UnknownMetaItem { span: param_span, - item: ident.to_string(), + item: param.path_without_args().to_string(), expected: if features.deprecated_suggestion() { &["since", "note", "suggestion"] } else { diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 26ca637faec68..43dfb85a7c411 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -96,58 +96,75 @@ fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option { - cx.emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident.span }); + match (name, args) { + (Some(sym::align), ArgParser::NoArgs) => { + cx.emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident_span }); None } - (sym::align, ArgParser::List(l)) => parse_repr_align(cx, l, param.span(), AlignKind::Align), + (Some(sym::align), ArgParser::List(l)) => { + parse_repr_align(cx, l, param.span(), AlignKind::Align) + } - (sym::packed, ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)), - (sym::packed, ArgParser::List(l)) => { + (Some(sym::packed), ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)), + (Some(sym::packed), ArgParser::List(l)) => { parse_repr_align(cx, l, param.span(), AlignKind::Packed) } - (sym::align | sym::packed, ArgParser::NameValue(l)) => { + (Some(name @ sym::align | name @ sym::packed), ArgParser::NameValue(l)) => { cx.emit_err(session_diagnostics::IncorrectReprFormatGeneric { span: param.span(), // FIXME(jdonszelmann) can just be a string in the diag type - repr_arg: &ident.to_string(), + repr_arg: name, cause: IncorrectReprFormatGenericCause::from_lit_kind( param.span(), &l.value_as_lit().kind, - ident.name.as_str(), + name, ), }); None } - (sym::Rust, ArgParser::NoArgs) => Some(ReprRust), - (sym::C, ArgParser::NoArgs) => Some(ReprC), - (sym::simd, ArgParser::NoArgs) => Some(ReprSimd), - (sym::transparent, ArgParser::NoArgs) => Some(ReprTransparent), - (i @ int_pat!(), ArgParser::NoArgs) => { + (Some(sym::Rust), ArgParser::NoArgs) => Some(ReprRust), + (Some(sym::C), ArgParser::NoArgs) => Some(ReprC), + (Some(sym::simd), ArgParser::NoArgs) => Some(ReprSimd), + (Some(sym::transparent), ArgParser::NoArgs) => Some(ReprTransparent), + (Some(name @ int_pat!()), ArgParser::NoArgs) => { // int_pat!() should make sure it always parses - Some(ReprInt(int_type_of_word(i).unwrap())) + Some(ReprInt(int_type_of_word(name).unwrap())) } ( - sym::Rust | sym::C | sym::simd | sym::transparent | int_pat!(), + Some( + name @ sym::Rust + | name @ sym::C + | name @ sym::simd + | name @ sym::transparent + | name @ int_pat!(), + ), ArgParser::NameValue(_), ) => { - cx.emit_err(session_diagnostics::InvalidReprHintNoValue { - span: param.span(), - name: ident.to_string(), - }); + cx.emit_err(session_diagnostics::InvalidReprHintNoValue { span: param.span(), name }); None } - (sym::Rust | sym::C | sym::simd | sym::transparent | int_pat!(), ArgParser::List(_)) => { - cx.emit_err(session_diagnostics::InvalidReprHintNoParen { - span: param.span(), - name: ident.to_string(), - }); + ( + Some( + name @ sym::Rust + | name @ sym::C + | name @ sym::simd + | name @ sym::transparent + | name @ int_pat!(), + ), + ArgParser::List(_), + ) => { + cx.emit_err(session_diagnostics::InvalidReprHintNoParen { span: param.span(), name }); None } diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 6d76456e83c80..cd1f21d92e7e2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -5,7 +5,7 @@ use rustc_attr_data_structures::{ StableSince, UnstableReason, VERSION_PLACEHOLDER, }; use rustc_errors::ErrorGuaranteed; -use rustc_span::{Span, Symbol, kw, sym}; +use rustc_span::{Span, Symbol, sym}; use super::util::parse_version; use super::{AcceptMapping, AttributeParser, SingleAttributeParser}; @@ -61,11 +61,7 @@ impl AttributeParser for StabilityParser { }), (&[sym::rustc_allowed_through_unstable_modules], |this, cx, args| { reject_outside_std!(cx); - this.allowed_through_unstable_modules = - Some(match args.name_value().and_then(|i| i.value_as_str()) { - Some(msg) => msg, - None => kw::Empty, - }); + this.allowed_through_unstable_modules = args.name_value().and_then(|i| i.value_as_str()) }), ]; @@ -246,9 +242,9 @@ pub(crate) fn parse_stability( return None; }; - match param.word_or_empty_without_args().name { - sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature)?, - sym::since => insert_value_into_option_or_error(cx, ¶m, &mut since)?, + match param.path_without_args().word_sym() { + Some(sym::feature) => insert_value_into_option_or_error(cx, ¶m, &mut feature)?, + Some(sym::since) => insert_value_into_option_or_error(cx, ¶m, &mut since)?, _ => { cx.emit_err(session_diagnostics::UnknownMetaItem { span: param_span, @@ -314,11 +310,10 @@ pub(crate) fn parse_unstability( return None; }; - let (word, args) = param.word_or_empty(); - match word.name { - sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature)?, - sym::reason => insert_value_into_option_or_error(cx, ¶m, &mut reason)?, - sym::issue => { + match param.path_without_args().word_sym() { + Some(sym::feature) => insert_value_into_option_or_error(cx, ¶m, &mut feature)?, + Some(sym::reason) => insert_value_into_option_or_error(cx, ¶m, &mut reason)?, + Some(sym::issue) => { insert_value_into_option_or_error(cx, ¶m, &mut issue)?; // These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item @@ -332,7 +327,7 @@ pub(crate) fn parse_unstability( session_diagnostics::InvalidIssueString { span: param.span(), cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind( - args.name_value().unwrap().value_span, + param.args().name_value().unwrap().value_span, err.kind(), ), }, @@ -342,13 +337,15 @@ pub(crate) fn parse_unstability( }, }; } - sym::soft => { - if !args.no_args() { + Some(sym::soft) => { + if !param.args().no_args() { cx.emit_err(session_diagnostics::SoftNoArgs { span: param.span() }); } is_soft = true; } - sym::implied_by => insert_value_into_option_or_error(cx, ¶m, &mut implied_by)?, + Some(sym::implied_by) => { + insert_value_into_option_or_error(cx, ¶m, &mut implied_by)? + } _ => { cx.emit_err(session_diagnostics::UnknownMetaItem { span: param.span(), diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index ad83a1f7af80c..ce42b0507ed57 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -20,7 +20,7 @@ impl SingleAttributeParser for TransparencyParser { fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option { match args.name_value().and_then(|nv| nv.value_as_str()) { Some(sym::transparent) => Some(Transparency::Transparent), - Some(sym::semitransparent) => Some(Transparency::SemiTransparent), + Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque), Some(sym::opaque) => Some(Transparency::Opaque), Some(other) => { cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`")); diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 05a9029c59aa5..503d2f1fae168 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -26,3 +26,33 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option { first_attr_value_str_by_name(attrs, sym::crate_name) } + +pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>( + attrs: impl Iterator, + symbol: Symbol, +) -> bool { + let doc_attrs = attrs.filter(|attr| attr.has_name(sym::doc)); + for attr in doc_attrs { + let Some(values) = attr.meta_item_list() else { + continue; + }; + let alias_values = values.iter().filter(|v| v.has_name(sym::alias)); + for v in alias_values { + if let Some(nested) = v.meta_item_list() { + // #[doc(alias("foo", "bar"))] + let mut iter = nested.iter().filter_map(|item| item.lit()).map(|item| item.symbol); + if iter.any(|s| s == symbol) { + return true; + } + } else if let Some(meta) = v.meta_item() + && let Some(lit) = meta.name_value_literal() + { + // #[doc(alias = "foo")] + if lit.symbol == symbol { + return true; + } + } + } + } + false +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 0e6b0bab082e9..55c3df003fe16 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -3,13 +3,12 @@ use std::collections::BTreeMap; use std::ops::Deref; use std::sync::LazyLock; -use rustc_ast::{self as ast, DelimArgs}; +use rustc_ast as ast; use rustc_attr_data_structures::AttributeKind; use rustc_errors::{DiagCtxtHandle, Diagnostic}; use rustc_feature::Features; use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId}; use rustc_session::Session; -use rustc_span::symbol::kw; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser}; @@ -221,7 +220,7 @@ impl<'sess> AttributeParser<'sess> { // if we're only looking for a single attribute, // skip all the ones we don't care about if let Some(expected) = self.parse_only { - if attr.name_or_empty() != expected { + if !attr.has_name(expected) { continue; } } @@ -231,7 +230,7 @@ impl<'sess> AttributeParser<'sess> { // that's expanded right? But no, sometimes, when parsing attributes on macros, // we already use the lowering logic and these are still there. So, when `omit_doc` // is set we *also* want to ignore these - if omit_doc == OmitDoc::Skip && attr.name_or_empty() == sym::doc { + if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) { continue; } @@ -249,7 +248,7 @@ impl<'sess> AttributeParser<'sess> { })) } // // FIXME: make doc attributes go through a proper attribute parser - // ast::AttrKind::Normal(n) if n.name_or_empty() == sym::doc => { + // ast::AttrKind::Normal(n) if n.has_name(sym::doc) => { // let p = GenericMetaItemParser::from_attr(&n, self.dcx()); // // attributes.push(Attribute::Parsed(AttributeKind::DocComment { @@ -316,11 +315,7 @@ impl<'sess> AttributeParser<'sess> { fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs { match args { ast::AttrArgs::Empty => AttrArgs::Empty, - ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(DelimArgs { - dspan: args.dspan, - delim: args.delim, - tokens: args.tokens.flattened(), - }), + ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()), // This is an inert key-value attribute - it will never be visible to macros // after it gets lowered to HIR. Therefore, we can extract literals to handle // nonterminals in `#[doc]` (e.g. `#[doc = $e]`). @@ -338,7 +333,7 @@ impl<'sess> AttributeParser<'sess> { "expr in place where literal is expected (builtin attr parsing)", ); ast::MetaItemLit { - symbol: kw::Empty, + symbol: sym::dummy, suffix: None, kind: ast::LitKind::Err(guar), span: DUMMY_SP, diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 9841166b37dbd..63bccf5201827 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -60,7 +60,8 @@ //! `#[stable(...)]` and `#[unstable()]` cannot occur together, and both semantically define //! a "stability" of an item. So, the stability attribute has an //! [`AttributeParser`](attributes::AttributeParser) that recognizes both the `#[stable()]` -//! and `#[unstable()]` syntactic attributes, and at the end produce a single [`AttributeKind::Stability`]. +//! and `#[unstable()]` syntactic attributes, and at the end produce a single +//! [`AttributeKind::Stability`](rustc_attr_data_structures::AttributeKind::Stability). //! //! As a rule of thumb, when a syntactical attribute can be applied more than once, they should be //! combined into a single semantic attribute. For example: @@ -78,9 +79,8 @@ // tidy-alphabetical-start #![allow(internal_features)] #![doc(rust_logo)] -#![feature(let_chains)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] +#![recursion_limit = "256"] // tidy-alphabetical-end #[macro_use] @@ -90,52 +90,9 @@ pub mod parser; mod session_diagnostics; pub use attributes::cfg::*; -pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version}; +pub use attributes::util::{ + find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version, +}; pub use context::{AttributeParser, OmitDoc}; -pub use rustc_attr_data_structures::*; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - -/// Finds attributes in sequences of attributes by pattern matching. -/// -/// A little like `matches` but for attributes. -/// -/// ```rust,ignore (illustrative) -/// // finds the repr attribute -/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) { -/// -/// } -/// -/// // checks if one has matched -/// if find_attr!(attrs, AttributeKind::Repr(_)) { -/// -/// } -/// ``` -/// -/// Often this requires you to first end up with a list of attributes. -/// A common way to get those is through `tcx.get_all_attrs(did)` -#[macro_export] -macro_rules! find_attr { - ($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{ - $crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some() - }}; - - ($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{ - fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator) {} - check_attribute_iterator(&$attributes_list); - - let find_attribute = |iter| { - for i in $attributes_list { - match i { - rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => { - return Some($e); - } - _ => {} - } - } - - None - }; - find_attribute($attributes_list) - }}; -} diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 96fc9d7d9ac47..077d953cfa318 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -12,7 +12,7 @@ use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, Norma use rustc_ast_pretty::pprust; use rustc_errors::DiagCtxtHandle; use rustc_hir::{self as hir, AttrPath}; -use rustc_span::symbol::{Ident, kw}; +use rustc_span::symbol::{Ident, kw, sym}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; pub struct SegmentIterator<'a> { @@ -78,8 +78,8 @@ impl<'a> PathParser<'a> { (self.len() == 1).then(|| **self.segments().next().as_ref().unwrap()) } - pub fn word_or_empty(&self) -> Ident { - self.word().unwrap_or_else(Ident::empty) + pub fn word_sym(&self) -> Option { + self.word().map(|ident| ident.name) } /// Asserts that this MetaItem is some specific word. @@ -284,11 +284,6 @@ impl<'a> MetaItemParser<'a> { Some(self.word()?.0) } - /// Like [`word`](Self::word), but returns an empty symbol instead of None - pub fn word_or_empty_without_args(&self) -> Ident { - self.word_or_empty().0 - } - /// Asserts that this MetaItem starts with a word, or single segment path. /// /// Some examples: @@ -300,12 +295,6 @@ impl<'a> MetaItemParser<'a> { Some((path.word()?, args)) } - /// Like [`word`](Self::word), but returns an empty symbol instead of None - pub fn word_or_empty(&self) -> (Ident, &ArgParser<'a>) { - let (path, args) = self.deconstruct(); - (path.word().unwrap_or(Ident::empty()), args) - } - /// Asserts that this MetaItem starts with some specific word. /// /// See [`word`](Self::word) for examples of what a word is. @@ -360,7 +349,7 @@ fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit span, "expr in place where literal is expected (builtin attr parsing)", ); - MetaItemLit { symbol: kw::Empty, suffix: None, kind: LitKind::Err(guar), span } + MetaItemLit { symbol: sym::dummy, suffix: None, kind: LitKind::Err(guar), span } } } @@ -430,9 +419,7 @@ impl<'a> MetaItemListParserContext<'a> { let span = span.with_hi(segments.last().unwrap().span.hi()); Some(AttrPath { segments: segments.into_boxed_slice(), span }) } - TokenTree::Token(Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. }, _) => { - None - } + TokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => None, _ => { // malformed attributes can get here. We can't crash, but somewhere else should've // already warned for this. @@ -473,28 +460,19 @@ impl<'a> MetaItemListParserContext<'a> { { self.inside_delimiters.next(); return Some(MetaItemOrLitParser::Lit(lit)); + } else if let Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) = + self.inside_delimiters.peek() + { + self.inside_delimiters.next(); + return MetaItemListParserContext { + inside_delimiters: inner_tokens.iter().peekable(), + dcx: self.dcx, + } + .next(); } // or a path. - let path = - if let Some(TokenTree::Token(Token { kind: token::Interpolated(_), span, .. }, _)) = - self.inside_delimiters.peek() - { - self.inside_delimiters.next(); - // We go into this path if an expr ended up in an attribute that - // expansion did not turn into a literal. Say, `#[repr(align(macro!()))]` - // where the macro didn't expand to a literal. An error is already given - // for this at this point, and then we do continue. This makes this path - // reachable... - let e = self.dcx.span_delayed_bug( - *span, - "expr in place where literal is expected (builtin attr parsing)", - ); - - return Some(MetaItemOrLitParser::Err(*span, e)); - } else { - self.next_path()? - }; + let path = self.next_path()?; // Paths can be followed by: // - `(more meta items)` (another list) diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 9d34b807ac2fe..2c434175b4b69 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -204,7 +204,7 @@ pub(crate) struct InvalidReprHintNoParen { #[primary_span] pub span: Span, - pub name: String, + pub name: Symbol, } #[derive(Diagnostic)] @@ -213,7 +213,7 @@ pub(crate) struct InvalidReprHintNoValue { #[primary_span] pub span: Span, - pub name: String, + pub name: Symbol, } /// Error code: E0565 @@ -295,21 +295,21 @@ pub(crate) struct IncorrectReprFormatExpectInteger { #[derive(Diagnostic)] #[diag(attr_parsing_incorrect_repr_format_generic, code = E0693)] -pub(crate) struct IncorrectReprFormatGeneric<'a> { +pub(crate) struct IncorrectReprFormatGeneric { #[primary_span] pub span: Span, - pub repr_arg: &'a str, + pub repr_arg: Symbol, #[subdiagnostic] - pub cause: Option>, + pub cause: Option, } #[derive(Subdiagnostic)] -pub(crate) enum IncorrectReprFormatGenericCause<'a> { +pub(crate) enum IncorrectReprFormatGenericCause { #[suggestion( attr_parsing_suggestion, - code = "{name}({int})", + code = "{name}({value})", applicability = "machine-applicable" )] Int { @@ -317,15 +317,15 @@ pub(crate) enum IncorrectReprFormatGenericCause<'a> { span: Span, #[skip_arg] - name: &'a str, + name: Symbol, #[skip_arg] - int: u128, + value: u128, }, #[suggestion( attr_parsing_suggestion, - code = "{name}({symbol})", + code = "{name}({value})", applicability = "machine-applicable" )] Symbol { @@ -333,20 +333,20 @@ pub(crate) enum IncorrectReprFormatGenericCause<'a> { span: Span, #[skip_arg] - name: &'a str, + name: Symbol, #[skip_arg] - symbol: Symbol, + value: Symbol, }, } -impl<'a> IncorrectReprFormatGenericCause<'a> { - pub(crate) fn from_lit_kind(span: Span, kind: &ast::LitKind, name: &'a str) -> Option { - match kind { - ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => { - Some(Self::Int { span, name, int: int.get() }) +impl IncorrectReprFormatGenericCause { + pub(crate) fn from_lit_kind(span: Span, kind: &ast::LitKind, name: Symbol) -> Option { + match *kind { + ast::LitKind::Int(value, ast::LitIntType::Unsuffixed) => { + Some(Self::Int { span, name, value: value.get() }) } - ast::LitKind::Str(symbol, _) => Some(Self::Symbol { span, name, symbol: *symbol }), + ast::LitKind::Str(value, _) => Some(Self::Symbol { span, name, value }), _ => None, } } diff --git a/compiler/rustc_baked_icu_data/src/lib.rs b/compiler/rustc_baked_icu_data/src/lib.rs index f86a9db61c600..f3f6522f57587 100644 --- a/compiler/rustc_baked_icu_data/src/lib.rs +++ b/compiler/rustc_baked_icu_data/src/lib.rs @@ -23,6 +23,7 @@ // tidy-alphabetical-start #![allow(elided_lifetimes_in_paths)] #![allow(internal_features)] +#![allow(unreachable_pub)] // because this crate is mostly generated code #![doc(rust_logo)] #![feature(rustdoc_internals)] // #![warn(unreachable_pub)] // don't use because this crate is mostly generated code diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index ada20e5c614f8..33b80c4b03d6f 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -162,13 +162,6 @@ borrowck_opaque_type_lifetime_mismatch = .prev_lifetime_label = lifetime `{$prev}` previously used here .note = if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types -borrowck_opaque_type_non_generic_param = - expected generic {$kind} parameter, found `{$ty}` - .label = {STREQ($ty, "'static") -> - [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type - *[other] this generic parameter must be used with a generic {$kind} parameter - } - borrowck_partial_var_move_by_use_in_closure = variable {$is_partial -> [true] partially moved diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 30e94b0bec708..c9be5575da5ce 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -403,6 +403,7 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> { .expect_closure(); let span = match capture_clause { rustc_hir::CaptureBy::Value { move_kw } => move_kw.shrink_to_lo(), + rustc_hir::CaptureBy::Use { use_kw } => use_kw.shrink_to_lo(), rustc_hir::CaptureBy::Ref => fn_decl_span.shrink_to_lo(), }; diag.span_suggestion_verbose( diff --git a/compiler/rustc_borrowck/src/constraints/graph.rs b/compiler/rustc_borrowck/src/constraints/graph.rs index 9d500586f084c..1209d8bf5967a 100644 --- a/compiler/rustc_borrowck/src/constraints/graph.rs +++ b/compiler/rustc_borrowck/src/constraints/graph.rs @@ -1,11 +1,8 @@ use rustc_data_structures::graph; use rustc_index::IndexVec; -use rustc_middle::mir::ConstraintCategory; -use rustc_middle::ty::{RegionVid, VarianceDiagInfo}; -use rustc_span::DUMMY_SP; +use rustc_middle::ty::RegionVid; use crate::constraints::{OutlivesConstraint, OutlivesConstraintIndex, OutlivesConstraintSet}; -use crate::type_check::Locations; /// The construct graph organizes the constraints by their end-points. /// It can be used to view a `R1: R2` constraint as either an edge `R1 @@ -23,8 +20,8 @@ pub(crate) type ReverseConstraintGraph = ConstraintGraph; /// Marker trait that controls whether a `R1: R2` constraint /// represents an edge `R1 -> R2` or `R2 -> R1`. pub(crate) trait ConstraintGraphDirection: Copy + 'static { - fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid; - fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid; + fn start_region(sup: RegionVid, sub: RegionVid) -> RegionVid; + fn end_region(sup: RegionVid, sub: RegionVid) -> RegionVid; fn is_normal() -> bool; } @@ -36,12 +33,12 @@ pub(crate) trait ConstraintGraphDirection: Copy + 'static { pub(crate) struct Normal; impl ConstraintGraphDirection for Normal { - fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid { - c.sup + fn start_region(sup: RegionVid, _sub: RegionVid) -> RegionVid { + sup } - fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid { - c.sub + fn end_region(_sup: RegionVid, sub: RegionVid) -> RegionVid { + sub } fn is_normal() -> bool { @@ -57,12 +54,12 @@ impl ConstraintGraphDirection for Normal { pub(crate) struct Reverse; impl ConstraintGraphDirection for Reverse { - fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid { - c.sub + fn start_region(_sup: RegionVid, sub: RegionVid) -> RegionVid { + sub } - fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid { - c.sup + fn end_region(sup: RegionVid, _sub: RegionVid) -> RegionVid { + sup } fn is_normal() -> bool { @@ -84,7 +81,7 @@ impl ConstraintGraph { let mut next_constraints = IndexVec::from_elem(None, &set.outlives); for (idx, constraint) in set.outlives.iter_enumerated().rev() { - let head = &mut first_constraints[D::start_region(constraint)]; + let head = &mut first_constraints[D::start_region(constraint.sup, constraint.sub)]; let next = &mut next_constraints[idx]; debug_assert!(next.is_none()); *next = *head; @@ -105,63 +102,57 @@ impl ConstraintGraph { RegionGraph::new(set, self, static_region) } + pub(crate) fn is_normal(&self) -> bool { + D::is_normal() + } + /// Given a region `R`, iterate over all constraints `R: R1`. - pub(crate) fn outgoing_edges<'a, 'tcx>( + pub(crate) fn outgoing_edges_from_graph<'a, 'tcx>( &'a self, region_sup: RegionVid, constraints: &'a OutlivesConstraintSet<'tcx>, - static_region: RegionVid, - ) -> Edges<'a, 'tcx, D> { - //if this is the `'static` region and the graph's direction is normal, - //then setup the Edges iterator to return all regions #53178 - if region_sup == static_region && D::is_normal() { - Edges { - graph: self, - constraints, - pointer: None, - next_static_idx: Some(0), - static_region, - } - } else { - //otherwise, just setup the iterator as normal - let first = self.first_constraints[region_sup]; - Edges { graph: self, constraints, pointer: first, next_static_idx: None, static_region } - } + ) -> EdgesFromGraph<'a, 'tcx, D> { + EdgesFromGraph { graph: self, constraints, pointer: self.first_constraints[region_sup] } + } + + /// Returns all regions (#53178). + pub(crate) fn outgoing_edges_from_static(&self) -> EdgesFromStatic { + EdgesFromStatic { next_static_idx: 0, end_static_idx: self.first_constraints.len() } } } -pub(crate) struct Edges<'a, 'tcx, D: ConstraintGraphDirection> { +pub(crate) struct EdgesFromGraph<'a, 'tcx, D: ConstraintGraphDirection> { graph: &'a ConstraintGraph, constraints: &'a OutlivesConstraintSet<'tcx>, pointer: Option, - next_static_idx: Option, - static_region: RegionVid, } -impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for Edges<'a, 'tcx, D> { - type Item = OutlivesConstraint<'tcx>; +impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for EdgesFromGraph<'a, 'tcx, D> { + type Item = &'a OutlivesConstraint<'tcx>; fn next(&mut self) -> Option { if let Some(p) = self.pointer { self.pointer = self.graph.next_constraints[p]; + Some(&self.constraints[p]) + } else { + None + } + } +} + +pub(crate) struct EdgesFromStatic { + next_static_idx: usize, + end_static_idx: usize, +} + +impl Iterator for EdgesFromStatic { + type Item = RegionVid; - Some(self.constraints[p]) - } else if let Some(next_static_idx) = self.next_static_idx { - self.next_static_idx = if next_static_idx == (self.graph.first_constraints.len() - 1) { - None - } else { - Some(next_static_idx + 1) - }; - - Some(OutlivesConstraint { - sup: self.static_region, - sub: next_static_idx.into(), - locations: Locations::All(DUMMY_SP), - span: DUMMY_SP, - category: ConstraintCategory::Internal, - variance_info: VarianceDiagInfo::default(), - from_closure: false, - }) + fn next(&mut self) -> Option { + if self.next_static_idx < self.end_static_idx { + let ret = RegionVid::from_usize(self.next_static_idx); + self.next_static_idx += 1; + Some(ret) } else { None } @@ -193,21 +184,38 @@ impl<'a, 'tcx, D: ConstraintGraphDirection> RegionGraph<'a, 'tcx, D> { /// Given a region `R`, iterate over all regions `R1` such that /// there exists a constraint `R: R1`. pub(crate) fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'a, 'tcx, D> { - Successors { - edges: self.constraint_graph.outgoing_edges(region_sup, self.set, self.static_region), + // If this is the `'static` region and the graph's direction is normal, + // then setup the Edges iterator to return all regions (#53178). + if region_sup == self.static_region && D::is_normal() { + Successors::FromStatic(self.constraint_graph.outgoing_edges_from_static()) + } else { + // Otherwise, just setup the iterator as normal. + Successors::FromGraph( + self.constraint_graph.outgoing_edges_from_graph(region_sup, self.set), + ) } } } -pub(crate) struct Successors<'a, 'tcx, D: ConstraintGraphDirection> { - edges: Edges<'a, 'tcx, D>, +pub(crate) enum Successors<'a, 'tcx, D: ConstraintGraphDirection> { + FromStatic(EdgesFromStatic), + FromGraph(EdgesFromGraph<'a, 'tcx, D>), } impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for Successors<'a, 'tcx, D> { type Item = RegionVid; fn next(&mut self) -> Option { - self.edges.next().map(|c| D::end_region(&c)) + match self { + Successors::FromStatic(edges) => { + // No `D::end_region` call needed here: static successors are only possible when + // the direction is `Normal`, so we can directly use what would be the `sub` value. + edges.next() + } + Successors::FromGraph(edges) => { + edges.next().map(|constraint| D::end_region(constraint.sup, constraint.sub)) + } + } } } diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index a52269df68289..514bbfe359b1d 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo}; use rustc_span::Span; use tracing::{debug, instrument}; -use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker}; +use crate::region_infer::{AnnotatedSccs, ConstraintSccs, RegionDefinition, SccAnnotations}; use crate::type_check::Locations; use crate::universal_regions::UniversalRegions; @@ -61,12 +61,14 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { &self, static_region: RegionVid, definitions: &IndexVec>, - ) -> ConstraintSccs { + ) -> AnnotatedSccs { let constraint_graph = self.graph(definitions.len()); let region_graph = &constraint_graph.region_graph(self, static_region); - ConstraintSccs::new_with_annotation(®ion_graph, |r| { - RegionTracker::new(r, &definitions[r]) - }) + let mut annotation_visitor = SccAnnotations::new(definitions); + ( + ConstraintSccs::new_with_annotation(®ion_graph, &mut annotation_visitor), + annotation_visitor.scc_to_annotation, + ) } /// This method handles Universe errors by rewriting the constraint @@ -79,12 +81,12 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { /// eventually go away. /// /// For a more precise definition, see the documentation for - /// [`RegionTracker::has_incompatible_universes()`]. + /// [`crate::region_infer::RegionTracker`]. /// /// This edge case used to be handled during constraint propagation /// by iterating over the strongly connected components in the constraint /// graph while maintaining a set of bookkeeping mappings similar - /// to what is stored in `RegionTracker` and manually adding 'sttaic as + /// to what is stored in `RegionTracker` and manually adding 'static as /// needed. /// /// It was rewritten as part of the Polonius project with the goal of moving @@ -108,9 +110,9 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { &mut self, universal_regions: &UniversalRegions<'tcx>, definitions: &IndexVec>, - ) -> ConstraintSccs { + ) -> AnnotatedSccs { let fr_static = universal_regions.fr_static; - let sccs = self.compute_sccs(fr_static, definitions); + let (sccs, annotations) = self.compute_sccs(fr_static, definitions); // Changed to `true` if we added any constraints to `self` and need to // recompute SCCs. @@ -124,7 +126,7 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { continue; } - let annotation = sccs.annotation(scc); + let annotation = annotations[scc]; // If this SCC participates in a universe violation, // e.g. if it reaches a region with a universe smaller than @@ -154,7 +156,7 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { self.compute_sccs(fr_static, definitions) } else { // If we didn't add any back-edges; no more work needs doing - sccs + (sccs, annotations) } } } diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 5a89f7c351cf6..1f087b092346e 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -1,7 +1,7 @@ //! This file provides API for compiler consumers. use rustc_hir::def_id::LocalDefId; -use rustc_index::{IndexSlice, IndexVec}; +use rustc_index::IndexVec; use rustc_middle::mir::{Body, Promoted}; use rustc_middle::ty::TyCtxt; @@ -15,6 +15,7 @@ pub use super::polonius::legacy::{ RichLocation, RustcFacts, }; pub use super::region_infer::RegionInferenceContext; +use crate::{BorrowCheckRootCtxt, do_mir_borrowck}; /// Options determining the output behavior of [`get_body_with_borrowck_facts`]. /// @@ -97,11 +98,9 @@ pub struct BodyWithBorrowckFacts<'tcx> { /// * Polonius is highly unstable, so expect regular changes in its signature or other details. pub fn get_body_with_borrowck_facts( tcx: TyCtxt<'_>, - def: LocalDefId, + def_id: LocalDefId, options: ConsumerOptions, ) -> BodyWithBorrowckFacts<'_> { - let (input_body, promoted) = tcx.mir_promoted(def); - let input_body: &Body<'_> = &input_body.borrow(); - let promoted: &IndexSlice<_, _> = &promoted.borrow(); - *super::do_mir_borrowck(tcx, input_body, promoted, Some(options)).1.unwrap() + let mut root_cx = BorrowCheckRootCtxt::new(tcx, def_id); + *do_mir_borrowck(&mut root_cx, def_id, Some(options)).1.unwrap() } diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs index 263f68d6a3dc7..b9ced81c46c19 100644 --- a/compiler/rustc_borrowck/src/def_use.rs +++ b/compiler/rustc_borrowck/src/def_use.rs @@ -77,6 +77,9 @@ pub(crate) fn categorize(context: PlaceContext) -> Option { // Debug info is neither def nor use. PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None, + // Backwards incompatible drop hint is not a use, just a marker for linting. + PlaceContext::NonUse(NonUseContext::BackwardIncompatibleDropHint) => None, + PlaceContext::MutatingUse(MutatingUseContext::Deinit | MutatingUseContext::SetDiscriminant) => { bug!("These statements are not allowed in this MIR phase") } diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index aa968a1e40f3e..0de4bd67f0ce7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -49,7 +49,7 @@ impl<'tcx> UniverseInfo<'tcx> { UniverseInfo::RelateTys { expected, found } } - pub(crate) fn report_error( + pub(crate) fn report_erroneous_element( &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion, @@ -68,7 +68,7 @@ impl<'tcx> UniverseInfo<'tcx> { mbcx.buffer_error(err); } UniverseInfo::TypeOp(ref type_op_info) => { - type_op_info.report_error(mbcx, placeholder, error_element, cause); + type_op_info.report_erroneous_element(mbcx, placeholder, error_element, cause); } UniverseInfo::Other => { // FIXME: This error message isn't great, but it doesn't show @@ -145,8 +145,11 @@ pub(crate) trait TypeOpInfo<'tcx> { error_region: Option>, ) -> Option>; + /// Constraints require that `error_element` appear in the + /// values of `placeholder`, but this cannot be proven to + /// hold. Report an error. #[instrument(level = "debug", skip(self, mbcx))] - fn report_error( + fn report_erroneous_element( &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion, @@ -190,12 +193,7 @@ pub(crate) trait TypeOpInfo<'tcx> { let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region); debug!(?nice_error); - - if let Some(nice_error) = nice_error { - mbcx.buffer_error(nice_error); - } else { - mbcx.buffer_error(self.fallback_error(tcx, span)); - } + mbcx.buffer_error(nice_error.unwrap_or_else(|| self.fallback_error(tcx, span))); } } @@ -406,8 +404,8 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> { // started MIR borrowchecking with, so the region // constraints have already been taken. Use the data from // our `mbcx` instead. - |vid| mbcx.regioncx.var_infos[vid].origin, - |vid| mbcx.regioncx.var_infos[vid].universe, + |vid| RegionVariableOrigin::Nll(mbcx.regioncx.definitions[vid].origin), + |vid| mbcx.regioncx.definitions[vid].universe, ) } } @@ -450,7 +448,8 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>( ty::ReVar(vid) => universe_of_region(vid), _ => ty::UniverseIndex::ROOT, }; - let matches = + // Are the two regions the same? + let regions_the_same = |a_region: Region<'tcx>, b_region: Region<'tcx>| match (a_region.kind(), b_region.kind()) { (RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound, _ => a_region == b_region, @@ -459,7 +458,7 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>( |constraint: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| match *constraint { Constraint::RegSubReg(sub, sup) if ((exact && sup == placeholder_region) - || (!exact && matches(sup, placeholder_region))) + || (!exact && regions_the_same(sup, placeholder_region))) && sup != sub => { Some((sub, cause.clone())) @@ -468,26 +467,24 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>( if (exact && sup == placeholder_region && !universe_of_region(vid).can_name(placeholder_universe)) - || (!exact && matches(sup, placeholder_region)) => + || (!exact && regions_the_same(sup, placeholder_region)) => { Some((ty::Region::new_var(infcx.tcx, vid), cause.clone())) } _ => None, }; - let mut info = region_constraints - .constraints - .iter() - .find_map(|(constraint, cause)| check(constraint, cause, true)); - if info.is_none() { - info = region_constraints + + let mut find_culprit = |exact_match: bool| { + region_constraints .constraints .iter() - .find_map(|(constraint, cause)| check(constraint, cause, false)); - } - let (sub_region, cause) = info?; + .find_map(|(constraint, cause)| check(constraint, cause, exact_match)) + }; + + let (sub_region, cause) = find_culprit(true).or_else(|| find_culprit(false))?; debug!(?sub_region, "cause = {:#?}", cause); - let error = match (error_region, *sub_region) { + let error = match (error_region, sub_region.kind()) { (Some(error_region), ty::ReVar(vid)) => RegionResolutionError::SubSupConflict( vid, region_var_origin(vid), diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 2694a1eda78d7..3b7d31b1b13bd 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -181,7 +181,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err); let mut is_loop_move = false; - let mut in_pattern = false; let mut seen_spans = FxIndexSet::default(); for move_site in &move_site_vec { @@ -204,7 +203,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.suggest_ref_or_clone( mpi, &mut err, - &mut in_pattern, move_spans, moved_place.as_ref(), &mut has_suggest_reborrow, @@ -256,15 +254,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let place = &self.move_data.move_paths[mpi].place; let ty = place.ty(self.body, self.infcx.tcx).ty; - // If we're in pattern, we do nothing in favor of the previous suggestion (#80913). - // Same for if we're in a loop, see #101119. - if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) { - if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { - // We have a `&mut` ref, we need to reborrow on each iteration (#62112). - self.suggest_reborrow(&mut err, span, moved_place); - } - } - if self.infcx.param_env.caller_bounds().iter().any(|c| { c.as_trait_clause().is_some_and(|pred| { pred.skip_binder().self_ty() == ty && self.infcx.tcx.is_fn_trait(pred.def_id()) @@ -330,7 +319,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { &self, mpi: MovePathIndex, err: &mut Diag<'infcx>, - in_pattern: &mut bool, move_spans: UseSpans<'tcx>, moved_place: PlaceRef<'tcx>, has_suggest_reborrow: &mut bool, @@ -545,7 +533,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { && !move_span.is_dummy() && !self.infcx.tcx.sess.source_map().is_imported(move_span) { - *in_pattern = true; let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())]; if let Some(pat) = finder.parent_pat { sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string())); @@ -660,7 +647,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { && tc.polarity() == ty::PredicatePolarity::Positive && supertrait_def_ids(tcx, tc.def_id()) .flat_map(|trait_did| tcx.associated_items(trait_did).in_definition_order()) - .any(|item| item.fn_has_self_parameter) + .any(|item| item.is_method()) }) }) { return None; @@ -1278,12 +1265,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { && let CallKind::FnCall { fn_trait_id, self_ty } = kind && let ty::Param(_) = self_ty.kind() && ty == self_ty - && [ - self.infcx.tcx.lang_items().fn_once_trait(), - self.infcx.tcx.lang_items().fn_mut_trait(), - self.infcx.tcx.lang_items().fn_trait(), - ] - .contains(&Some(fn_trait_id)) + && self.infcx.tcx.fn_trait_kind_from_def_id(fn_trait_id).is_some() { // Do not suggest `F: FnOnce() + Clone`. false @@ -1364,7 +1346,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } // Try to find predicates on *generic params* that would allow copying `ty` let mut suggestion = - if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { + if let Some(symbol) = tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { format!(": {symbol}.clone()") } else { ".clone()".to_owned() @@ -2513,13 +2495,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); let ty::Tuple(params) = tupled_params.kind() else { return }; - // Find the first argument with a matching type, get its name - let Some((_, this_name)) = - params.iter().zip(tcx.hir_body_param_names(closure.body)).find(|(param_ty, name)| { + // Find the first argument with a matching type and get its identifier. + let Some(this_name) = params.iter().zip(tcx.hir_body_param_idents(closure.body)).find_map( + |(param_ty, ident)| { // FIXME: also support deref for stuff like `Rc` arguments - param_ty.peel_refs() == local_ty && name != &Ident::empty() - }) - else { + if param_ty.peel_refs() == local_ty { ident } else { None } + }, + ) else { return; }; @@ -2972,21 +2954,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{name}`")); + let name = format!("`{name}`"); + + let mut err = self.path_does_not_live_long_enough(borrow_span, &name); if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) { let region_name = annotation.emit(self, &mut err); err.span_label( borrow_span, - format!("`{name}` would have to be valid for `{region_name}`..."), + format!("{name} would have to be valid for `{region_name}`..."), ); err.span_label( drop_span, format!( - "...but `{}` will be dropped here, when the {} returns", - name, + "...but {name} will be dropped here, when the {} returns", self.infcx .tcx .opt_item_name(self.mir_def_id().to_def_id()) @@ -3024,7 +3007,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } else { err.span_label(borrow_span, "borrowed value does not live long enough"); - err.span_label(drop_span, format!("`{name}` dropped here while still borrowed")); + err.span_label(drop_span, format!("{name} dropped here while still borrowed")); borrow_spans.args_subdiag(&mut err, |args_span| { crate::session_diagnostics::CaptureArgLabel::Capture { @@ -3389,10 +3372,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) { Ok(string) => { - let coro_prefix = if string.starts_with("async") { - // `async` is 5 chars long. Not using `.len()` to avoid the cast from `usize` - // to `u32`. - Some(5) + let coro_prefix = if let Some(sub) = string.strip_prefix("async") { + let trimmed_sub = sub.trim_end(); + if trimmed_sub.ends_with("gen") { + // `async` is 5 chars long. + Some((trimmed_sub.len() + 5) as _) + } else { + // `async` is 5 chars long. + Some(5) + } } else if string.starts_with("gen") { // `gen` is 3 chars long Some(3) @@ -3787,7 +3775,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { method_args, *fn_span, call_source.from_hir_call(), - Some(self.infcx.tcx.fn_arg_names(method_did)[0]), + self.infcx.tcx.fn_arg_idents(method_did)[0], ) { err.note(format!("borrow occurs due to deref coercion to `{deref_target_ty}`")); diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index f77dda0d386aa..a845431facac1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -95,7 +95,9 @@ impl<'tcx> BorrowExplanation<'tcx> { && let hir::def::Res::Local(hir_id) = p.res && let hir::Node::Pat(pat) = tcx.hir_node(hir_id) { - err.span_label(pat.span, format!("binding `{ident}` declared here")); + if !ident.span.in_external_macro(tcx.sess.source_map()) { + err.span_label(pat.span, format!("binding `{ident}` declared here")); + } } } } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index d1f238331d56f..5e3f3ffa2ea85 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -8,9 +8,7 @@ use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify}; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::{self as hir, CoroutineKind, LangItem}; use rustc_index::IndexSlice; -use rustc_infer::infer::{ - BoundRegionConversionTime, NllRegionVariableOrigin, RegionVariableOrigin, -}; +use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin}; use rustc_infer::traits::SelectionError; use rustc_middle::bug; use rustc_middle::mir::{ @@ -319,6 +317,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { opt: DescribePlaceOpt, ) -> Option { let local = place.local; + if self.body.local_decls[local] + .source_info + .span + .in_external_macro(self.infcx.tcx.sess.source_map()) + { + return None; + } + let mut autoderef_index = None; let mut buf = String::new(); let mut ok = self.append_local_to_string(local, &mut buf); @@ -505,7 +511,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let var_id = self.infcx.tcx.closure_captures(def_id)[field.index()].get_root_variable(); - Some(self.infcx.tcx.hir().name(var_id).to_string()) + Some(self.infcx.tcx.hir_name(var_id).to_string()) } _ => { // Might need a revision when the fields in trait RFC is implemented @@ -587,7 +593,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // this by hooking into the pretty printer and telling it to label the // lifetimes without names with the value `'0`. if let ty::Ref(region, ..) = ty.kind() { - match **region { + match region.kind() { ty::ReBound(_, ty::BoundRegion { kind: br, .. }) | ty::RePlaceholder(ty::PlaceholderRegion { bound: ty::BoundRegion { kind: br, .. }, @@ -607,7 +613,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS); let region = if let ty::Ref(region, ..) = ty.kind() { - match **region { + match region.kind() { ty::ReBound(_, ty::BoundRegion { kind: br, .. }) | ty::RePlaceholder(ty::PlaceholderRegion { bound: ty::BoundRegion { kind: br, .. }, @@ -633,9 +639,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ) { let predicate_span = path.iter().find_map(|constraint| { let outlived = constraint.sub; - if let Some(origin) = self.regioncx.var_infos.get(outlived) - && let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(_)) = - origin.origin + if let Some(origin) = self.regioncx.definitions.get(outlived) + && let NllRegionVariableOrigin::Placeholder(_) = origin.origin && let ConstraintCategory::Predicate(span) = constraint.category { Some(span) @@ -1029,7 +1034,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { method_args, *fn_span, call_source.from_hir_call(), - Some(self.infcx.tcx.fn_arg_names(method_did)[0]), + self.infcx.tcx.fn_arg_idents(method_did)[0], ); return FnSelfUse { @@ -1124,7 +1129,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { def_id, target_place, places ); let hir_id = self.infcx.tcx.local_def_id_to_hir_id(def_id); - let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; + 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(&hir::Closure { kind, fn_decl_span, .. }) = expr { for (captured_place, place) in diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 29cc749877b3e..0394a42ea9c77 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -502,7 +502,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let upvar = &self.upvars[upvar_field.unwrap().index()]; let upvar_hir_id = upvar.get_root_variable(); let upvar_name = upvar.to_string(tcx); - let upvar_span = tcx.hir().span(upvar_hir_id); + let upvar_span = tcx.hir_span(upvar_hir_id); let place_name = self.describe_any_place(move_place.as_ref()); diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index be4a7736b1c20..56cc432758511 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -691,14 +691,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { true, td.is_local(), td.as_local().and_then(|tld| match self.infcx.tcx.hir_node_by_def_id(tld) { - Node::Item(hir::Item { kind: hir::ItemKind::Trait(_, _, _, _, items), .. }) => { + Node::Item(hir::Item { + kind: hir::ItemKind::Trait(_, _, _, _, _, items), .. + }) => { let mut f_in_trait_opt = None; for hir::TraitItemRef { id: fi, kind: k, .. } in *items { let hi = fi.hir_id(); if !matches!(k, hir::AssocItemKind::Fn { .. }) { continue; } - if self.infcx.tcx.hir().name(hi) != self.infcx.tcx.hir().name(my_hir) { + if self.infcx.tcx.hir_name(hi) != self.infcx.tcx.hir_name(my_hir) { continue; } f_in_trait_opt = Some(hi); @@ -823,7 +825,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ) => { capture_reason = format!("mutable borrow of `{upvar}`"); } - ty::UpvarCapture::ByValue => { + ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => { capture_reason = format!("possible mutation of `{upvar}`"); } _ => bug!("upvar `{upvar}` borrowed, but not mutably"), @@ -967,7 +969,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } }; - // If we can detect the expression to be an function or method call where the closure was + // If we can detect the expression to be a function or method call where the closure was // an argument, we point at the function or method definition argument... if let Some((callee_def_id, call_span, call_args)) = get_call_details() { let arg_pos = call_args @@ -980,7 +982,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let arg = match tcx.hir_get_if_local(callee_def_id) { Some( hir::Node::Item(hir::Item { - ident, kind: hir::ItemKind::Fn { sig, .. }, .. + kind: hir::ItemKind::Fn { ident, sig, .. }, .. }) | hir::Node::TraitItem(hir::TraitItem { ident, @@ -1021,7 +1023,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // return type. match tcx.hir_node_by_def_id(tcx.hir_get_parent_item(fn_call_id).def_id) { hir::Node::Item(hir::Item { - ident, kind: hir::ItemKind::Fn { sig, .. }, .. + kind: hir::ItemKind::Fn { ident, sig, .. }, .. }) | hir::Node::TraitItem(hir::TraitItem { ident, @@ -1632,8 +1634,8 @@ fn get_mut_span_in_struct_field<'tcx>( /// If possible, suggest replacing `ref` with `ref mut`. fn suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option { let pattern_str = tcx.sess.source_map().span_to_snippet(span).ok()?; - if pattern_str.starts_with("ref") - && pattern_str["ref".len()..].starts_with(rustc_lexer::is_whitespace) + if let Some(rest) = pattern_str.strip_prefix("ref") + && rest.starts_with(rustc_lexer::is_whitespace) { let span = span.with_lo(span.lo() + BytePos(4)).shrink_to_lo(); Some(span) diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs index 876b8f214b017..7192a889adcbc 100644 --- a/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs +++ b/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs @@ -105,7 +105,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(opaque_def_id) = opaque_def_id.as_local() && let hir::OpaqueTyOrigin::FnReturn { parent, .. } = - tcx.hir().expect_opaque_ty(opaque_def_id).origin + tcx.hir_expect_opaque_ty(opaque_def_id).origin { if let Some(sugg) = impl_trait_overcapture_suggestion( tcx, diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 55b6367f35ff8..3bec07afa0fe0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -14,9 +14,8 @@ use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound}; use rustc_middle::bug; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{AnnotationSource, ConstraintCategory, ReturnConstraint}; -use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{ - self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitor, + self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitor, fold_regions, }; use rustc_span::{Ident, Span, kw}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; @@ -191,7 +190,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { where T: TypeFoldable>, { - fold_regions(tcx, ty, |region, _| match *region { + fold_regions(tcx, ty, |region, _| match region.kind() { ty::ReVar(vid) => self.to_error_region(vid).unwrap_or(region), _ => region, }) @@ -199,7 +198,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// Returns `true` if a closure is inferred to be an `FnMut` closure. fn is_closure_fn_mut(&self, fr: RegionVid) -> bool { - if let Some(ty::ReLateParam(late_param)) = self.to_error_region(fr).as_deref() + if let Some(r) = self.to_error_region(fr) + && let ty::ReLateParam(late_param) = r.kind() && let ty::LateParamRegionKind::ClosureEnv = late_param.kind && let DefiningTy::Closure(_, args) = self.regioncx.universal_regions().defining_ty { @@ -405,7 +405,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let universe = placeholder.universe; let universe_info = self.regioncx.universe_info(universe); - universe_info.report_error(self, placeholder, error_element, cause); + universe_info.report_erroneous_element(self, placeholder, error_element, cause); } RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => { @@ -514,14 +514,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ty::VarianceDiagInfo::Invariant { ty, param_index } => { let (desc, note) = match ty.kind() { ty::RawPtr(ty, mutbl) => { - assert_eq!(*mutbl, rustc_hir::Mutability::Mut); + assert_eq!(*mutbl, hir::Mutability::Mut); ( format!("a mutable pointer to `{}`", ty), "mutable pointers are invariant over their type parameter".to_string(), ) } ty::Ref(_, inner_ty, mutbl) => { - assert_eq!(*mutbl, rustc_hir::Mutability::Mut); + assert_eq!(*mutbl, hir::Mutability::Mut); ( format!("a mutable reference to `{inner_ty}`"), "mutable references are invariant over their type parameter" @@ -629,7 +629,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(def_hir) = defined_hir { let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap(); - let upvar_def_span = self.infcx.tcx.hir().span(def_hir); + let upvar_def_span = self.infcx.tcx.hir_span(def_hir); let upvar_span = upvars_map.get(&def_hir).unwrap().span; diag.subdiagnostic(VarHereDenote::Defined { span: upvar_def_span }); diag.subdiagnostic(VarHereDenote::Captured { span: upvar_span }); @@ -833,7 +833,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let (Some(f), Some(outlived_f)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { - if *outlived_f != ty::ReStatic { + if outlived_f.kind() != ty::ReStatic { return; } let suitable_region = self.infcx.tcx.is_suitable_region(self.mir_def_id(), f); @@ -888,7 +888,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // Skip `async` desugaring `impl Future`. } if let TyKind::TraitObject(_, lt) = alias_ty.kind { - if lt.ident.name == kw::Empty { + if lt.kind == hir::LifetimeKind::ImplicitObjectLifetimeDefault { spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string())); } else { spans_suggs.push((lt.ident.span, "'a".to_string())); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index a15f9744bf310..b08c10983bbc0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -288,10 +288,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { let tcx = self.infcx.tcx; debug!("give_region_a_name: error_region = {:?}", error_region); - match *error_region { + match error_region.kind() { ty::ReEarlyParam(ebr) => ebr.has_name().then(|| { let def_id = tcx.generics_of(self.mir_def_id()).region_param(ebr, tcx).def_id; - let span = tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP); + let span = tcx.hir_span_if_local(def_id).unwrap_or(DUMMY_SP); RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyParamRegion(span) } }), @@ -302,7 +302,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { ty::ReLateParam(late_param) => match late_param.kind { ty::LateParamRegionKind::Named(region_def_id, name) => { // Get the span to point to, even if we don't use the name. - let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP); + let span = tcx.hir_span_if_local(region_def_id).unwrap_or(DUMMY_SP); debug!( "bound region named: {:?}, is_named: {:?}", name, @@ -343,7 +343,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } }; let hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }) = - tcx.hir().expect_expr(self.mir_hir_id()).kind + tcx.hir_expect_expr(self.mir_hir_id()).kind else { bug!("Closure is not defined by a closure expr"); }; @@ -896,7 +896,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { &self, fr: RegionVid, ) -> Option { - let ty::ReEarlyParam(region) = *self.to_error_region(fr)? else { + let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else { return None; }; if region.has_name() { @@ -912,7 +912,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { let found = tcx .any_free_region_meets(&tcx.type_of(region_parent).instantiate_identity(), |r| { - *r == ty::ReEarlyParam(region) + r.kind() == ty::ReEarlyParam(region) }); Some(RegionName { @@ -931,7 +931,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { &self, fr: RegionVid, ) -> Option { - let ty::ReEarlyParam(region) = *self.to_error_region(fr)? else { + let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else { return None; }; if region.has_name() { @@ -1007,7 +1007,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { if data.projection_term.self_ty() == ty => {} _ => return false, } - tcx.any_free_region_meets(pred, |r| *r == ty::ReEarlyParam(region)) + tcx.any_free_region_meets(pred, |r| r.kind() == ty::ReEarlyParam(region)) }) } else { false diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index 191c0444c742e..14ed6a27a7a15 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -69,8 +69,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { let upvar_hir_id = upvars[upvar_index].get_root_variable(); debug!("get_upvar_name_and_span_for_region: upvar_hir_id={upvar_hir_id:?}"); - let upvar_name = tcx.hir().name(upvar_hir_id); - let upvar_span = tcx.hir().span(upvar_hir_id); + let upvar_name = tcx.hir_name(upvar_hir_id); + let upvar_span = tcx.hir_span(upvar_hir_id); debug!( "get_upvar_name_and_span_for_region: upvar_name={upvar_name:?} upvar_span={upvar_span:?}", ); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 68e0ab0933e61..676cb618b725b 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -7,13 +7,12 @@ #![feature(box_patterns)] #![feature(file_buffered)] #![feature(if_let_guard)] -#![feature(let_chains)] +#![feature(negative_impls)] #![feature(never_type)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] #![feature(stmt_expr_attributes)] #![feature(try_blocks)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use std::borrow::Cow; @@ -21,6 +20,8 @@ use std::cell::RefCell; use std::marker::PhantomData; use std::ops::{ControlFlow, Deref}; +use borrow_set::LocalsStateAtExit; +use root_cx::BorrowCheckRootCtxt; use rustc_abi::FieldIdx; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::dominators::Dominators; @@ -35,8 +36,9 @@ use rustc_infer::infer::{ }; use rustc_middle::mir::*; use rustc_middle::query::Providers; -use rustc_middle::ty::fold::fold_regions; -use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt, TypingMode}; +use rustc_middle::ty::{ + self, ParamEnv, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypingMode, fold_regions, +}; use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::impls::{ EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces, @@ -44,9 +46,9 @@ use rustc_mir_dataflow::impls::{ use rustc_mir_dataflow::move_paths::{ InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex, }; -use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results}; +use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor, visit_results}; use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT}; -use rustc_span::{Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -81,6 +83,7 @@ mod polonius; mod prefixes; mod region_infer; mod renumber; +mod root_cx; mod session_diagnostics; mod type_check; mod universal_regions; @@ -102,66 +105,210 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { mir_borrowck, ..*providers }; } -fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> { - let (input_body, promoted) = tcx.mir_promoted(def); +/// Provider for `query mir_borrowck`. Similar to `typeck`, this must +/// only be called for typeck roots which will then borrowck all +/// nested bodies as well. +fn mir_borrowck( + tcx: TyCtxt<'_>, + def: LocalDefId, +) -> Result<&ConcreteOpaqueTypes<'_>, ErrorGuaranteed> { + assert!(!tcx.is_typeck_child(def.to_def_id())); + let (input_body, _) = tcx.mir_promoted(def); debug!("run query mir_borrowck: {}", tcx.def_path_str(def)); let input_body: &Body<'_> = &input_body.borrow(); + if let Some(guar) = input_body.tainted_by_errors { + debug!("Skipping borrowck because of tainted body"); + Err(guar) + } else if input_body.should_skip() { + debug!("Skipping borrowck because of injected body"); + let opaque_types = ConcreteOpaqueTypes(Default::default()); + Ok(tcx.arena.alloc(opaque_types)) + } else { + let mut root_cx = BorrowCheckRootCtxt::new(tcx, def); + // We need to manually borrowck all nested bodies from the HIR as + // we do not generate MIR for dead code. Not doing so causes us to + // never check closures in dead code. + let nested_bodies = tcx.nested_bodies_within(def); + for def_id in nested_bodies { + root_cx.get_or_insert_nested(def_id); + } - if input_body.should_skip() || input_body.tainted_by_errors.is_some() { - debug!("Skipping borrowck because of injected body or tainted body"); - // Let's make up a borrowck result! Fun times! - let result = BorrowCheckResult { - concrete_opaque_types: FxIndexMap::default(), - closure_requirements: None, - used_mut_upvars: SmallVec::new(), - tainted_by_errors: input_body.tainted_by_errors, - }; - return tcx.arena.alloc(result); + let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } = + do_mir_borrowck(&mut root_cx, def, None).0; + debug_assert!(closure_requirements.is_none()); + debug_assert!(used_mut_upvars.is_empty()); + root_cx.finalize() } +} + +/// Data propagated to the typeck parent by nested items. +/// This should always be empty for the typeck root. +#[derive(Debug)] +struct PropagatedBorrowCheckResults<'tcx> { + closure_requirements: Option>, + used_mut_upvars: SmallVec<[FieldIdx; 8]>, +} + +/// 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 coroutine'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 args: +/// +/// ```text +/// ClosureArgs = [ +/// '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 +/// ClosureArgs = [ +/// '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. +#[derive(Clone, Debug)] +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)] +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, - let borrowck_result = do_mir_borrowck(tcx, input_body, &*promoted.borrow(), None).0; - debug!("mir_borrowck done"); + // ... due to this reason. + pub category: ConstraintCategory<'tcx>, +} + +// Make sure this enum doesn't unintentionally grow +#[cfg(target_pointer_width = "64")] +rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); + +/// The subject of a `ClosureOutlivesRequirement` -- that is, the thing +/// that must outlive some region. +#[derive(Copy, Clone, Debug)] +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`. + Ty(ClosureOutlivesSubjectTy<'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), +} - tcx.arena.alloc(borrowck_result) +/// Represents a `ty::Ty` for use in [`ClosureOutlivesSubject`]. +/// +/// This abstraction is necessary because the type may include `ReVar` regions, +/// which is what we use internally within NLL code, and they can't be used in +/// a query response. +#[derive(Copy, Clone, Debug)] +pub struct ClosureOutlivesSubjectTy<'tcx> { + inner: Ty<'tcx>, +} +// DO NOT implement `TypeVisitable` or `TypeFoldable` traits, because this +// type is not recognized as a binder for late-bound region. +impl<'tcx, I> !TypeVisitable for ClosureOutlivesSubjectTy<'tcx> {} +impl<'tcx, I> !TypeFoldable for ClosureOutlivesSubjectTy<'tcx> {} + +impl<'tcx> ClosureOutlivesSubjectTy<'tcx> { + /// All regions of `ty` must be of kind `ReVar` and must represent + /// universal regions *external* to the closure. + pub fn bind(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { + let inner = fold_regions(tcx, ty, |r, depth| match r.kind() { + ty::ReVar(vid) => { + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(vid.index()), + kind: ty::BoundRegionKind::Anon, + }; + ty::Region::new_bound(tcx, depth, br) + } + _ => bug!("unexpected region in ClosureOutlivesSubjectTy: {r:?}"), + }); + + Self { inner } + } + + pub fn instantiate( + self, + tcx: TyCtxt<'tcx>, + mut map: impl FnMut(ty::RegionVid) -> ty::Region<'tcx>, + ) -> Ty<'tcx> { + fold_regions(tcx, self.inner, |r, depth| match r.kind() { + ty::ReBound(debruijn, br) => { + debug_assert_eq!(debruijn, depth); + map(ty::RegionVid::from_usize(br.var.index())) + } + _ => bug!("unexpected region {r:?}"), + }) + } } /// Perform the actual borrow checking. /// /// Use `consumer_options: None` for the default behavior of returning -/// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according -/// to the given [`ConsumerOptions`]. -#[instrument(skip(tcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")] +/// [`PropagatedBorrowCheckResults`] only. Otherwise, return [`BodyWithBorrowckFacts`] +/// according to the given [`ConsumerOptions`]. +/// +/// For nested bodies this should only be called through `root_cx.get_or_insert_nested`. +#[instrument(skip(root_cx), level = "debug")] fn do_mir_borrowck<'tcx>( - tcx: TyCtxt<'tcx>, - input_body: &Body<'tcx>, - input_promoted: &IndexSlice>, + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + def: LocalDefId, consumer_options: Option, -) -> (BorrowCheckResult<'tcx>, Option>>) { - let def = input_body.source.def_id().expect_local(); +) -> (PropagatedBorrowCheckResults<'tcx>, Option>>) { + let tcx = root_cx.tcx; let infcx = BorrowckInferCtxt::new(tcx, def); + let (input_body, promoted) = tcx.mir_promoted(def); + let input_body: &Body<'_> = &input_body.borrow(); + let input_promoted: &IndexSlice<_, _> = &promoted.borrow(); if let Some(e) = input_body.tainted_by_errors { infcx.set_tainted_by_errors(e); - } - - let mut local_names = IndexVec::from_elem(None, &input_body.local_decls); - for var_debug_info in &input_body.var_debug_info { - if let VarDebugInfoContents::Place(place) = var_debug_info.value { - if let Some(local) = place.as_local() { - if let Some(prev_name) = local_names[local] - && 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); - } - } + root_cx.set_tainted_by_errors(e); } // Replace all regions with fresh inference variables. This @@ -170,15 +317,9 @@ fn do_mir_borrowck<'tcx>( // will have a lifetime tied to the inference context. let mut body_owned = input_body.clone(); let mut promoted = input_promoted.to_owned(); - let free_regions = nll::replace_regions_in_mir(&infcx, &mut body_owned, &mut promoted); + let universal_regions = nll::replace_regions_in_mir(&infcx, &mut body_owned, &mut promoted); let body = &body_owned; // no further changes - // FIXME(-Znext-solver): A bit dubious that we're only registering - // predefined opaques in the typeck root. - if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) { - infcx.register_predefined_opaques_for_next_solver(def); - } - let location_table = PoloniusLocationTable::new(body); let move_data = MoveData::gather_moves(body, tcx, |_| true); @@ -193,15 +334,15 @@ fn do_mir_borrowck<'tcx>( // Compute non-lexical lifetimes. let nll::NllOutput { regioncx, - opaque_type_values, polonius_input, polonius_output, opt_closure_req, nll_errors, polonius_diagnostics, } = nll::compute_regions( + root_cx, &infcx, - free_regions, + universal_regions, body, &promoted, &location_table, @@ -214,31 +355,23 @@ fn do_mir_borrowck<'tcx>( // Dump MIR results into a file, if that is enabled. This lets us // write unit-tests, as well as helping with debugging. nll::dump_nll_mir(&infcx, body, ®ioncx, &opt_closure_req, &borrow_set); - - // We also have a `#[rustc_regions]` annotation that causes us to dump - // information. - let diags_buffer = &mut BorrowckDiagnosticsBuffer::default(); - nll::dump_annotation( + polonius::dump_polonius_mir( &infcx, body, ®ioncx, &opt_closure_req, - &opaque_type_values, - diags_buffer, + &borrow_set, + polonius_diagnostics.as_ref(), ); - let movable_coroutine = - // The first argument is the coroutine type passed by value - if let Some(local) = body.local_decls.raw.get(1) - // Get the interior types and args which typeck computed - && let ty::Coroutine(def_id, _) = *local.ty.kind() - && tcx.coroutine_movability(def_id) == hir::Movability::Movable - { - true - } else { - false - }; + // We also have a `#[rustc_regions]` annotation that causes us to dump + // information. + nll::dump_annotation(&infcx, body, ®ioncx, &opt_closure_req); + + let movable_coroutine = body.coroutine.is_some() + && tcx.coroutine_movability(def.to_def_id()) == hir::Movability::Movable; + let diags_buffer = &mut BorrowckDiagnosticsBuffer::default(); // While promoteds should mostly be correct by construction, we need to check them for // invalid moves to detect moving out of arrays:`struct S; fn main() { &([S][0]); }`. for promoted_body in &promoted { @@ -248,6 +381,7 @@ fn do_mir_borrowck<'tcx>( // this check out of `MirBorrowckCtxt`, actually doing so is far from trivial. let move_data = MoveData::gather_moves(promoted_body, tcx, |_| true); let mut promoted_mbcx = MirBorrowckCtxt { + root_cx, infcx: &infcx, body: promoted_body, move_data: &move_data, @@ -255,7 +389,6 @@ fn do_mir_borrowck<'tcx>( location_table: &location_table, movable_coroutine, fn_self_span_reported: Default::default(), - locals_are_invalidated_at_exit, access_place_error_reported: Default::default(), reservation_error_reported: Default::default(), uninitialized_error_reported: Default::default(), @@ -287,13 +420,33 @@ fn do_mir_borrowck<'tcx>( promoted_mbcx.report_move_errors(); } + let mut local_names = IndexVec::from_elem(None, &body.local_decls); + for var_debug_info in &body.var_debug_info { + if let VarDebugInfoContents::Place(place) = var_debug_info.value { + if let Some(local) = place.as_local() { + if let Some(prev_name) = local_names[local] + && 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); + } + } + } + let mut mbcx = MirBorrowckCtxt { + root_cx, infcx: &infcx, body, move_data: &move_data, location_table: &location_table, movable_coroutine, - locals_are_invalidated_at_exit, fn_self_span_reported: Default::default(), access_place_error_reported: Default::default(), reservation_error_reported: Default::default(), @@ -306,35 +459,27 @@ fn do_mir_borrowck<'tcx>( local_names, region_names: RefCell::default(), next_region_name: RefCell::new(1), - polonius_output, move_errors: Vec::new(), diags_buffer, + polonius_output: polonius_output.as_deref(), polonius_diagnostics: polonius_diagnostics.as_ref(), }; // Compute and report region errors, if any. mbcx.report_region_errors(nll_errors); - let mut flow_results = get_flow_results(tcx, body, &move_data, &borrow_set, ®ioncx); + let (mut flow_analysis, flow_entry_states) = + get_flow_results(tcx, body, &move_data, &borrow_set, ®ioncx); visit_results( body, traversal::reverse_postorder(body).map(|(bb, _)| bb), - &mut flow_results, + &mut flow_analysis, + &flow_entry_states, &mut mbcx, ); mbcx.report_move_errors(); - // If requested, dump polonius MIR. - polonius::dump_polonius_mir( - &infcx, - body, - ®ioncx, - &borrow_set, - polonius_diagnostics.as_ref(), - &opt_closure_req, - ); - // 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 @@ -355,17 +500,16 @@ fn do_mir_borrowck<'tcx>( debug!("mbcx.used_mut: {:?}", mbcx.used_mut); mbcx.lint_unused_mut(); - let tainted_by_errors = mbcx.emit_errors(); + if let Some(guar) = mbcx.emit_errors() { + mbcx.root_cx.set_tainted_by_errors(guar); + } - let result = BorrowCheckResult { - concrete_opaque_types: opaque_type_values, + let result = PropagatedBorrowCheckResults { closure_requirements: opt_closure_req, used_mut_upvars: mbcx.used_mut_upvars, - tainted_by_errors, }; let body_with_facts = if consumer_options.is_some() { - let output_facts = mbcx.polonius_output; Some(Box::new(BodyWithBorrowckFacts { body: body_owned, promoted, @@ -373,7 +517,7 @@ fn do_mir_borrowck<'tcx>( region_inference_context: regioncx, location_table: polonius_input.as_ref().map(|_| location_table), input_facts: polonius_input, - output_facts, + output_facts: polonius_output, })) } else { None @@ -390,7 +534,7 @@ fn get_flow_results<'a, 'tcx>( move_data: &'a MoveData<'tcx>, borrow_set: &'a BorrowSet<'tcx>, regioncx: &RegionInferenceContext<'tcx>, -) -> Results<'tcx, Borrowck<'a, 'tcx>> { +) -> (Borrowck<'a, 'tcx>, Results) { // We compute these three analyses individually, but them combine them into // a single results so that `mbcx` can visit them all together. let borrows = Borrows::new(tcx, body, regioncx, borrow_set).iterate_to_fixpoint( @@ -415,14 +559,14 @@ fn get_flow_results<'a, 'tcx>( ever_inits: ever_inits.analysis, }; - assert_eq!(borrows.entry_states.len(), uninits.entry_states.len()); - assert_eq!(borrows.entry_states.len(), ever_inits.entry_states.len()); - let entry_states: EntryStates<'_, Borrowck<'_, '_>> = - itertools::izip!(borrows.entry_states, uninits.entry_states, ever_inits.entry_states) + assert_eq!(borrows.results.len(), uninits.results.len()); + assert_eq!(borrows.results.len(), ever_inits.results.len()); + let results: Results<_> = + itertools::izip!(borrows.results, uninits.results, ever_inits.results) .map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits }) .collect(); - Results { analysis, entry_states } + (analysis, results) } pub(crate) struct BorrowckInferCtxt<'tcx> { @@ -433,7 +577,12 @@ pub(crate) struct BorrowckInferCtxt<'tcx> { impl<'tcx> BorrowckInferCtxt<'tcx> { pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { - let infcx = tcx.infer_ctxt().build(TypingMode::analysis_in_body(tcx, def_id)); + let typing_mode = if tcx.use_typing_mode_borrowck() { + TypingMode::borrowck(tcx, def_id) + } else { + TypingMode::analysis_in_body(tcx, def_id) + }; + let infcx = tcx.infer_ctxt().build(typing_mode); let param_env = tcx.param_env(def_id); BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()), param_env } } @@ -480,28 +629,6 @@ impl<'tcx> BorrowckInferCtxt<'tcx> { next_region } - - /// With the new solver we prepopulate the opaque type storage during - /// MIR borrowck with the hidden types from HIR typeck. This is necessary - /// to avoid ambiguities as earlier goals can rely on the hidden type - /// of an opaque which is only constrained by a later goal. - fn register_predefined_opaques_for_next_solver(&self, def_id: LocalDefId) { - let tcx = self.tcx; - // OK to use the identity arguments for each opaque type key, since - // we remap opaques from HIR typeck back to their definition params. - for data in tcx.typeck(def_id).concrete_opaque_types.iter().map(|(k, v)| (*k, *v)) { - // HIR typeck did not infer the regions of the opaque, so we instantiate - // them with fresh inference variables. - let (key, hidden_ty) = fold_regions(tcx, data, |_, _| { - self.next_nll_region_var_in_universe( - NllRegionVariableOrigin::Existential { from_forall: false }, - ty::UniverseIndex::ROOT, - ) - }); - - self.inject_new_hidden_type_unchecked(key, hidden_ty); - } - } } impl<'tcx> Deref for BorrowckInferCtxt<'tcx> { @@ -513,6 +640,7 @@ impl<'tcx> Deref for BorrowckInferCtxt<'tcx> { } struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { + root_cx: &'a mut BorrowCheckRootCtxt<'tcx>, infcx: &'infcx BorrowckInferCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>, @@ -522,13 +650,6 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { location_table: &'a PoloniusLocationTable, movable_coroutine: 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 @@ -576,12 +697,11 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { /// The counter for generating new region names. next_region_name: RefCell, - /// Results of Polonius analysis. - polonius_output: Option>, - diags_buffer: &'a mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>, move_errors: Vec>, + /// Results of Polonius analysis. + polonius_output: Option<&'a PoloniusOutput>, /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics. polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>, } @@ -591,12 +711,12 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { // 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<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> { +impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> { fn visit_after_early_statement_effect( &mut self, - _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>, + _analysis: &mut Borrowck<'a, 'tcx>, state: &BorrowckDomain, - stmt: &'a Statement<'tcx>, + stmt: &Statement<'tcx>, location: Location, ) { debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {:?}", location, stmt, state); @@ -670,9 +790,9 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt< fn visit_after_early_terminator_effect( &mut self, - _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>, + _analysis: &mut Borrowck<'a, 'tcx>, state: &BorrowckDomain, - term: &'a Terminator<'tcx>, + term: &Terminator<'tcx>, loc: Location, ) { debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {:?}", loc, term, state); @@ -684,7 +804,14 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt< TerminatorKind::SwitchInt { discr, targets: _ } => { self.consume_operand(loc, (discr, span), state); } - TerminatorKind::Drop { place, target: _, unwind: _, replace } => { + TerminatorKind::Drop { + place, + target: _, + unwind: _, + replace, + drop: _, + async_fut: _, + } => { debug!( "visit_terminator_drop \ loc: {:?} term: {:?} place: {:?} span: {:?}", @@ -783,9 +910,9 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt< fn visit_after_primary_terminator_effect( &mut self, - _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>, + _analysis: &mut Borrowck<'a, 'tcx>, state: &BorrowckDomain, - term: &'a Terminator<'tcx>, + term: &Terminator<'tcx>, loc: Location, ) { let span = term.source_info.span; @@ -805,13 +932,20 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt< | TerminatorKind::Return | TerminatorKind::TailCall { .. } | TerminatorKind::CoroutineDrop => { - // 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. - for i in state.borrows.iter() { - let borrow = &self.borrow_set[i]; - self.check_for_invalidation_at_exit(loc, borrow, span); + match self.borrow_set.locals_state_at_exit() { + LocalsStateAtExit::AllAreInvalidated => { + // 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. + for i in state.borrows.iter() { + let borrow = &self.borrow_set[i]; + self.check_for_invalidation_at_exit(loc, borrow, span); + } + } + // If we do not implicitly invalidate all locals on exit, + // we check for conflicts when dropping or moving this local. + LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved: _ } => {} } } @@ -1168,7 +1302,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { error_reported } - /// Through #123739, backward incompatible drops (BIDs) are introduced. + /// Through #123739, `BackwardIncompatibleDropHint`s (BIDs) are introduced. /// We would like to emit lints whether borrow checking fails at these future drop locations. #[instrument(level = "debug", skip(self, state))] fn check_backward_incompatible_drop( @@ -1245,7 +1379,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { fn consume_rvalue( &mut self, location: Location, - (rvalue, span): (&'a Rvalue<'tcx>, Span), + (rvalue, span): (&Rvalue<'tcx>, Span), state: &BorrowckDomain, ) { match rvalue { @@ -1386,11 +1520,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | AggregateKind::CoroutineClosure(def_id, _) | AggregateKind::Coroutine(def_id, _) => { let def_id = def_id.expect_local(); - let BorrowCheckResult { used_mut_upvars, .. } = - self.infcx.tcx.mir_borrowck(def_id); + let used_mut_upvars = self.root_cx.used_mut_upvars(def_id); debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars); - for field in used_mut_upvars { - self.propagate_closure_used_mut_upvar(&operands[*field]); + // FIXME: We're cloning the `SmallVec` here to avoid borrowing `root_cx` + // when calling `propagate_closure_used_mut_upvar`. This should ideally + // be unnecessary. + for field in used_mut_upvars.clone() { + self.propagate_closure_used_mut_upvar(&operands[field]); } } AggregateKind::Adt(..) @@ -1490,14 +1626,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { 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 \ + match stmt.kind { + StatementKind::Assign(box ( + _, + Rvalue::Ref(_, _, source) + | Rvalue::Use(Operand::Copy(source) | Operand::Move(source)), + )) => { + propagate_closure_used_mut_place(self, source); + } + _ => { + bug!( + "closures should only capture user variables \ or references to user variables" - ); + ); + } } } _ => propagate_closure_used_mut_place(self, place), @@ -1510,7 +1652,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { fn consume_operand( &mut self, location: Location, - (operand, span): (&'a Operand<'tcx>, Span), + (operand, span): (&Operand<'tcx>, Span), state: &BorrowckDomain, ) { match *operand { @@ -1575,22 +1717,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // 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 = TyCtxtConsts::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 might_be_alive = 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 = TyCtxtConsts::DEREF_PROJECTION; + true + } else { + false + }; let sd = if might_be_alive { Deep } else { Shallow(None) }; @@ -2474,19 +2609,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { let body = self.body; for local in body.mut_vars_and_args_iter().filter(|local| !self.used_mut.contains(local)) { let local_decl = &body.local_decls[local]; - let lint_root = match &body.source_scopes[local_decl.source_info.scope].local_data { - ClearCrossCrate::Set(data) => data.lint_root, - _ => continue, + let ClearCrossCrate::Set(SourceScopeLocalData { lint_root, .. }) = + body.source_scopes[local_decl.source_info.scope].local_data + else { + continue; }; // Skip over locals that begin with an underscore or have no name - match self.local_names[local] { - Some(name) => { - if name.as_str().starts_with('_') { - continue; - } - } - None => continue, + if self.local_names[local].is_none_or(|name| name.as_str().starts_with('_')) { + continue; } let span = local_decl.source_info.span; diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 2031579dfd50a..fe899bb054fa9 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -1,21 +1,16 @@ //! The entry point of the NLL borrow checker. +use std::io; use std::path::PathBuf; use std::rc::Rc; use std::str::FromStr; -use std::{env, io}; use polonius_engine::{Algorithm, Output}; -use rustc_data_structures::fx::FxIndexMap; -use rustc_hir::def_id::LocalDefId; use rustc_index::IndexSlice; use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options}; -use rustc_middle::mir::{ - Body, ClosureOutlivesSubject, ClosureRegionRequirements, PassWhere, Promoted, create_dump_file, - dump_enabled, dump_mir, -}; +use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir}; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, OpaqueHiddenType, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_mir_dataflow::ResultsCursor; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; @@ -26,7 +21,7 @@ use tracing::{debug, instrument}; use crate::borrow_set::BorrowSet; use crate::consumers::ConsumerOptions; -use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors}; +use crate::diagnostics::RegionErrors; use crate::polonius::PoloniusDiagnosticsContext; use crate::polonius::legacy::{ PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, @@ -34,13 +29,15 @@ use crate::polonius::legacy::{ use crate::region_infer::RegionInferenceContext; use crate::type_check::{self, MirTypeckResults}; use crate::universal_regions::UniversalRegions; -use crate::{BorrowckInferCtxt, polonius, renumber}; +use crate::{ + BorrowCheckRootCtxt, BorrowckInferCtxt, ClosureOutlivesSubject, ClosureRegionRequirements, + polonius, renumber, +}; /// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any /// closure requirements to propagate, and any generated errors. pub(crate) struct NllOutput<'tcx> { pub regioncx: RegionInferenceContext<'tcx>, - pub opaque_type_values: FxIndexMap>, pub polonius_input: Option>, pub polonius_output: Option>, pub opt_closure_req: Option>, @@ -79,6 +76,7 @@ pub(crate) fn replace_regions_in_mir<'tcx>( /// /// This may result in errors being reported. pub(crate) fn compute_regions<'a, 'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, infcx: &BorrowckInferCtxt<'tcx>, universal_regions: UniversalRegions<'tcx>, body: &Body<'tcx>, @@ -106,6 +104,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( opaque_type_values, polonius_context, } = type_check::type_check( + root_cx, infcx, body, promoted, @@ -118,11 +117,6 @@ pub(crate) fn compute_regions<'a, 'tcx>( Rc::clone(&location_map), ); - // Create the region inference context, taking ownership of the - // region inference data that was contained in `infcx`, and the - // base constraints generated by the type-check. - let var_infos = infcx.get_region_var_infos(); - // If requested, emit legacy polonius facts. polonius::legacy::emit_facts( &mut polonius_facts, @@ -135,13 +129,8 @@ pub(crate) fn compute_regions<'a, 'tcx>( &constraints, ); - let mut regioncx = RegionInferenceContext::new( - infcx, - var_infos, - constraints, - universal_region_relations, - location_map, - ); + let mut regioncx = + RegionInferenceContext::new(infcx, constraints, universal_region_relations, location_map); // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints // and use them to compute loan liveness. @@ -160,9 +149,8 @@ pub(crate) fn compute_regions<'a, 'tcx>( } if polonius_output { - let algorithm = - env::var("POLONIUS_ALGORITHM").unwrap_or_else(|_| String::from("Hybrid")); - let algorithm = Algorithm::from_str(&algorithm).unwrap(); + let algorithm = infcx.tcx.env_var("POLONIUS_ALGORITHM").unwrap_or("Hybrid"); + let algorithm = Algorithm::from_str(algorithm).unwrap(); debug!("compute_regions: using polonius algorithm {:?}", algorithm); let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis"); Some(Box::new(Output::compute(polonius_facts, algorithm, false))) @@ -180,11 +168,10 @@ pub(crate) fn compute_regions<'a, 'tcx>( infcx.set_tainted_by_errors(guar); } - let remapped_opaque_tys = regioncx.infer_opaque_types(infcx, opaque_type_values); + regioncx.infer_opaque_types(root_cx, infcx, opaque_type_values); NllOutput { regioncx, - opaque_type_values: remapped_opaque_tys, polonius_input: polonius_facts.map(Box::new), polonius_output, opt_closure_req: closure_region_requirements, @@ -300,8 +287,6 @@ pub(super) fn dump_annotation<'tcx, 'infcx>( body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option>, - opaque_type_values: &FxIndexMap>, - diagnostics_buffer: &mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>, ) { let tcx = infcx.tcx; let base_def_id = tcx.typeck_root_def_id(body.source.def_id()); @@ -317,7 +302,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>( // better. let def_span = tcx.def_span(body.source.def_id()); - let mut err = if let Some(closure_region_requirements) = closure_region_requirements { + let err = if let Some(closure_region_requirements) = closure_region_requirements { let mut err = infcx.dcx().struct_span_note(def_span, "external requirements"); regioncx.annotate(tcx, &mut err); @@ -339,15 +324,11 @@ pub(super) fn dump_annotation<'tcx, 'infcx>( } else { let mut err = infcx.dcx().struct_span_note(def_span, "no external requirements"); regioncx.annotate(tcx, &mut err); - err }; - if !opaque_type_values.is_empty() { - err.note(format!("Inferred opaque type values:\n{opaque_type_values:#?}")); - } - - diagnostics_buffer.buffer_non_error(err); + // FIXME(@lcnr): We currently don't dump the inferred hidden types here. + err.emit(); } fn for_each_region_constraint<'tcx>( diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index aa64a7c4e2a68..6a943e1920821 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -5,7 +5,7 @@ use rustc_index::IndexVec; use rustc_middle::mir::pretty::{ PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, }; -use rustc_middle::mir::{Body, ClosureRegionRequirements, Location}; +use rustc_middle::mir::{Body, Location}; use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::points::PointIndex; use rustc_session::config::MirIncludeSpans; @@ -17,16 +17,16 @@ use crate::polonius::{ }; use crate::region_infer::values::LivenessValues; use crate::type_check::Locations; -use crate::{BorrowckInferCtxt, RegionInferenceContext}; +use crate::{BorrowckInferCtxt, ClosureRegionRequirements, RegionInferenceContext}; /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information. pub(crate) fn dump_polonius_mir<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, + closure_region_requirements: &Option>, borrow_set: &BorrowSet<'tcx>, polonius_diagnostics: Option<&PoloniusDiagnosticsContext>, - closure_region_requirements: &Option>, ) { let tcx = infcx.tcx; if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { @@ -334,7 +334,7 @@ fn emit_mermaid_nll_regions<'tcx>( writeln!(out, "flowchart TD")?; // Emit the region nodes. - for region in regioncx.var_infos.indices() { + for region in regioncx.definitions.indices() { write!(out, "{}[\"", region.as_usize())?; render_region(region, regioncx, out)?; writeln!(out, "\"]")?; @@ -387,7 +387,7 @@ fn emit_mermaid_nll_sccs<'tcx>( // Gather and emit the SCC nodes. let mut nodes_per_scc: IndexVec<_, _> = regioncx.constraint_sccs().all_sccs().map(|_| Vec::new()).collect(); - for region in regioncx.var_infos.indices() { + for region in regioncx.definitions.indices() { let scc = regioncx.constraint_sccs().scc(region); nodes_per_scc[scc].push(region); } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 0d1d8642bcacc..99dd0b2dd4664 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -101,7 +101,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { TerminatorKind::SwitchInt { discr, targets: _ } => { self.consume_operand(location, discr); } - TerminatorKind::Drop { place: drop_place, target: _, unwind: _, replace } => { + TerminatorKind::Drop { + place: drop_place, + target: _, + unwind: _, + replace, + drop: _, + async_fut: _, + } => { let write_kind = if *replace { WriteKind::Replace } else { WriteKind::StorageDeadOrDrop }; self.access_place( diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index a00fce08c9bac..b4ff3d66f3d5b 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -9,23 +9,20 @@ use rustc_errors::Diag; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_index::IndexVec; use rustc_infer::infer::outlives::test_type_match; -use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq}; +use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound, VerifyIfEq}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; use rustc_middle::bug; use rustc_middle::mir::{ - AnnotationSource, BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, - ClosureOutlivesSubjectTy, ClosureRegionRequirements, ConstraintCategory, Local, Location, - ReturnConstraint, TerminatorKind, + AnnotationSource, BasicBlock, Body, ConstraintCategory, Local, Location, ReturnConstraint, + TerminatorKind, }; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; -use rustc_middle::ty::fold::fold_regions; -use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex}; +use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex, fold_regions}; use rustc_mir_dataflow::points::DenseLocationMap; -use rustc_span::Span; use rustc_span::hygiene::DesugaringKind; -use tracing::{debug, instrument, trace}; +use rustc_span::{DUMMY_SP, Span}; +use tracing::{Level, debug, enabled, instrument, trace}; -use crate::BorrowckInferCtxt; use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph}; use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet}; use crate::dataflow::BorrowIndex; @@ -38,6 +35,10 @@ use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, T use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::type_check::{Locations, MirTypeckRegionConstraints}; use crate::universal_regions::UniversalRegions; +use crate::{ + BorrowckInferCtxt, ClosureOutlivesRequirement, ClosureOutlivesSubject, + ClosureOutlivesSubjectTy, ClosureRegionRequirements, +}; mod dump_mir; mod graphviz; @@ -46,12 +47,13 @@ mod reverse_sccs; pub(crate) mod values; -pub(crate) type ConstraintSccs = Sccs; +pub(crate) type ConstraintSccs = Sccs; +pub(crate) type AnnotatedSccs = (ConstraintSccs, IndexVec); /// An annotation for region graph SCCs that tracks -/// the values of its elements. +/// the values of its elements. This annotates a single SCC. #[derive(Copy, Debug, Clone)] -pub struct RegionTracker { +pub(crate) struct RegionTracker { /// The largest universe of a placeholder reached from this SCC. /// This includes placeholders within this SCC. max_placeholder_universe_reached: UniverseIndex, @@ -96,6 +98,32 @@ impl scc::Annotation for RegionTracker { } } +/// A Visitor for SCC annotation construction. +pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> { + pub(crate) scc_to_annotation: IndexVec, + definitions: &'d IndexVec>, +} + +impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> { + pub(crate) fn new(definitions: &'d IndexVec>) -> Self { + Self { scc_to_annotation: IndexVec::new(), definitions } + } +} + +impl scc::Annotations for SccAnnotations<'_, '_, RegionTracker> { + fn new(&self, element: RegionVid) -> RegionTracker { + RegionTracker::new(element, &self.definitions[element]) + } + + fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) { + let idx = self.scc_to_annotation.push(annotation); + assert!(idx == scc); + } + + type Ann = RegionTracker; + type SccIdx = ConstraintSccIndex; +} + impl RegionTracker { pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { let (representative_is_placeholder, representative_is_existential) = match definition.origin @@ -140,13 +168,11 @@ impl RegionTracker { } pub struct RegionInferenceContext<'tcx> { - pub var_infos: VarInfos, - /// Contains the definition for every region variable. Region /// variables are identified by their index (`RegionVid`). The /// definition contains information about where the region came /// from as well as its final inferred value. - definitions: IndexVec>, + pub(crate) definitions: Frozen>>, /// The liveness constraints added to each region. For most /// regions, these start out empty and steadily grow, though for @@ -167,6 +193,8 @@ pub struct RegionInferenceContext<'tcx> { /// compute the values of each region. constraint_sccs: ConstraintSccs, + scc_annotations: IndexVec, + /// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if /// `B: A`. This is used to compute the universal regions that are required /// to outlive a given SCC. Computed lazily. @@ -311,9 +339,11 @@ enum RegionRelationCheckResult { } #[derive(Clone, PartialEq, Eq, Debug)] -enum Trace<'tcx> { +enum Trace<'a, 'tcx> { StartRegion, - FromOutlivesConstraint(OutlivesConstraint<'tcx>), + FromGraph(&'a OutlivesConstraint<'tcx>), + FromStatic(RegionVid), + FromMember(RegionVid, RegionVid, Span), NotVisited, } @@ -326,31 +356,34 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) { let mut var_to_origin_sorted = var_to_origin.clone().into_iter().collect::>(); var_to_origin_sorted.sort_by_key(|vto| vto.0); - let mut reg_vars_to_origins_str = "region variables to origins:\n".to_string(); - for (reg_var, origin) in var_to_origin_sorted.into_iter() { - reg_vars_to_origins_str.push_str(&format!("{reg_var:?}: {origin:?}\n")); + if enabled!(Level::DEBUG) { + let mut reg_vars_to_origins_str = "region variables to origins:\n".to_string(); + for (reg_var, origin) in var_to_origin_sorted.into_iter() { + reg_vars_to_origins_str.push_str(&format!("{reg_var:?}: {origin:?}\n")); + } + debug!("{}", reg_vars_to_origins_str); } - debug!("{}", reg_vars_to_origins_str); let num_components = sccs.num_sccs(); let mut components = vec![FxIndexSet::default(); num_components]; - for (reg_var_idx, scc_idx) in sccs.scc_indices().iter().enumerate() { - let reg_var = ty::RegionVid::from_usize(reg_var_idx); + for (reg_var, scc_idx) in sccs.scc_indices().iter_enumerated() { let origin = var_to_origin.get(®_var).unwrap_or(&RegionCtxt::Unknown); components[scc_idx.as_usize()].insert((reg_var, *origin)); } - let mut components_str = "strongly connected components:".to_string(); - for (scc_idx, reg_vars_origins) in components.iter().enumerate() { - let regions_info = reg_vars_origins.clone().into_iter().collect::>(); - components_str.push_str(&format!( - "{:?}: {:?},\n)", - ConstraintSccIndex::from_usize(scc_idx), - regions_info, - )) + if enabled!(Level::DEBUG) { + let mut components_str = "strongly connected components:".to_string(); + for (scc_idx, reg_vars_origins) in components.iter().enumerate() { + let regions_info = reg_vars_origins.clone().into_iter().collect::>(); + components_str.push_str(&format!( + "{:?}: {:?},\n)", + ConstraintSccIndex::from_usize(scc_idx), + regions_info, + )) + } + debug!("{}", components_str); } - debug!("{}", components_str); // calculate the best representative for each component let components_representatives = components @@ -380,6 +413,26 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) { debug!("SCC edges {:#?}", scc_node_to_edges); } +fn create_definitions<'tcx>( + infcx: &BorrowckInferCtxt<'tcx>, + universal_regions: &UniversalRegions<'tcx>, +) -> Frozen>> { + // Create a RegionDefinition for each inference variable. + let mut definitions: IndexVec<_, _> = infcx + .get_region_var_infos() + .iter() + .map(|info| RegionDefinition::new(info.universe, info.origin)) + .collect(); + + // Add the external name for all universal regions. + for (external_name, variable) in universal_regions.named_universal_regions_iter() { + debug!("region {variable:?} has external name {external_name:?}"); + definitions[variable].external_name = Some(external_name); + } + + Frozen::freeze(definitions) +} + impl<'tcx> RegionInferenceContext<'tcx> { /// Creates a new region inference context with a total of /// `num_region_variables` valid inference variables; the first N @@ -390,7 +443,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// of constraints produced by the MIR type check. pub(crate) fn new( infcx: &BorrowckInferCtxt<'tcx>, - var_infos: VarInfos, constraints: MirTypeckRegionConstraints<'tcx>, universal_region_relations: Frozen>, location_map: Rc, @@ -421,13 +473,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { infcx.set_tainted_by_errors(guar); } - // Create a RegionDefinition for each inference variable. - let definitions: IndexVec<_, _> = var_infos - .iter() - .map(|info| RegionDefinition::new(info.universe, info.origin)) - .collect(); + let definitions = create_definitions(infcx, &universal_regions); - let constraint_sccs = + let (constraint_sccs, scc_annotations) = outlives_constraints.add_outlives_static(&universal_regions, &definitions); let constraints = Frozen::freeze(outlives_constraints); let constraint_graph = Frozen::freeze(constraints.graph(definitions.len())); @@ -448,12 +496,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { Rc::new(member_constraints.into_mapped(|r| constraint_sccs.scc(r))); let mut result = Self { - var_infos, definitions, liveness_constraints, constraints, constraint_graph, constraint_sccs, + scc_annotations, rev_scc_graph: None, member_constraints, member_constraints_applied: Vec::new(), @@ -522,18 +570,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// means that the `R1: !1` constraint here will cause /// `R1` to become `'static`. fn init_free_and_bound_regions(&mut self) { - // Update the names (if any) - // This iterator has unstable order but we collect it all into an IndexVec - for (external_name, variable) in - self.universal_region_relations.universal_regions.named_universal_regions_iter() - { - debug!( - "init_free_and_bound_regions: region {:?} has external name {:?}", - variable, external_name - ); - self.definitions[variable].external_name = Some(external_name); - } - for variable in self.definitions.indices() { let scc = self.constraint_sccs.scc(variable); @@ -792,7 +828,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // If the member region lives in a higher universe, we currently choose // the most conservative option by leaving it unchanged. - if !self.constraint_sccs().annotation(scc).min_universe().is_root() { + if !self.scc_universe(scc).is_root() { return; } @@ -868,8 +904,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// in `scc_a`. Used during constraint propagation, and only once /// the value of `scc_b` has been computed. fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool { - let a_annotation = self.constraint_sccs().annotation(scc_a); - let b_annotation = self.constraint_sccs().annotation(scc_b); + let a_annotation = self.scc_annotations[scc_a]; + let b_annotation = self.scc_annotations[scc_b]; let a_universe = a_annotation.min_universe(); // If scc_b's declared universe is a subset of @@ -985,7 +1021,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { "lower_bound = {:?} r_scc={:?} universe={:?}", lower_bound, r_scc, - self.constraint_sccs.annotation(r_scc).min_universe() + self.scc_universe(r_scc) ); // If the type test requires that `T: 'a` where `'a` is a // placeholder from another universe, that effectively requires @@ -1466,7 +1502,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// The minimum universe of any variable reachable from this /// SCC, inside or outside of it. fn scc_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex { - self.constraint_sccs().annotation(scc).min_universe() + self.scc_annotations[scc].min_universe() } /// Checks the final value for the free region `fr` to see if it @@ -1622,30 +1658,23 @@ impl<'tcx> RegionInferenceContext<'tcx> { let longer_fr_scc = self.constraint_sccs.scc(longer_fr); debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,); - for error_element in self.scc_values.elements_contained_in(longer_fr_scc) { - match error_element { - RegionElement::Location(_) | RegionElement::RootUniversalRegion(_) => {} - // If we have some bound universal region `'a`, then the only - // elements it can contain is itself -- we don't know anything - // else about it! - RegionElement::PlaceholderRegion(placeholder1) => { - if placeholder == placeholder1 { - continue; - } - } - } - + // If we have some bound universal region `'a`, then the only + // elements it can contain is itself -- we don't know anything + // else about it! + if let Some(error_element) = self + .scc_values + .elements_contained_in(longer_fr_scc) + .find(|e| *e != RegionElement::PlaceholderRegion(placeholder)) + { + // Stop after the first error, it gets too noisy otherwise, and does not provide more information. errors_buffer.push(RegionErrorKind::BoundUniversalRegionError { longer_fr, error_element, placeholder, }); - - // Stop after the first error, it gets too noisy otherwise, and does not provide more - // information. - break; + } else { + debug!("check_bound_universal_region: all bounds satisfied"); } - debug!("check_bound_universal_region: all bounds satisfied"); } #[instrument(level = "debug", skip(self, infcx, errors_buffer))] @@ -1764,6 +1793,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions); context[from_region] = Trace::StartRegion; + let fr_static = self.universal_regions().fr_static; + // Use a deque so that we do a breadth-first search. We will // stop at the first match, which ought to be the shortest // path (fewest constraints). @@ -1783,13 +1814,39 @@ impl<'tcx> RegionInferenceContext<'tcx> { if target_test(r) { let mut result = vec![]; let mut p = r; + // This loop is cold and runs at the end, which is why we delay + // `OutlivesConstraint` construction until now. loop { - match context[p].clone() { - Trace::NotVisited => { - bug!("found unvisited region {:?} on path to {:?}", p, r) + match context[p] { + Trace::FromGraph(c) => { + p = c.sup; + result.push(*c); + } + + Trace::FromStatic(sub) => { + let c = OutlivesConstraint { + sup: fr_static, + sub, + locations: Locations::All(DUMMY_SP), + span: DUMMY_SP, + category: ConstraintCategory::Internal, + variance_info: ty::VarianceDiagInfo::default(), + from_closure: false, + }; + p = c.sup; + result.push(c); } - Trace::FromOutlivesConstraint(c) => { + Trace::FromMember(sup, sub, span) => { + let c = OutlivesConstraint { + sup, + sub, + locations: Locations::All(span), + span, + category: ConstraintCategory::OpaqueType, + variance_info: ty::VarianceDiagInfo::default(), + from_closure: false, + }; p = c.sup; result.push(c); } @@ -1798,6 +1855,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { result.reverse(); return Some((result, r)); } + + Trace::NotVisited => { + bug!("found unvisited region {:?} on path to {:?}", p, r) + } } } } @@ -1808,45 +1869,42 @@ impl<'tcx> RegionInferenceContext<'tcx> { // A constraint like `'r: 'x` can come from our constraint // graph. - let fr_static = self.universal_regions().fr_static; - let outgoing_edges_from_graph = - self.constraint_graph.outgoing_edges(r, &self.constraints, fr_static); // Always inline this closure because it can be hot. - let mut handle_constraint = #[inline(always)] - |constraint: OutlivesConstraint<'tcx>| { - debug_assert_eq!(constraint.sup, r); - let sub_region = constraint.sub; - if let Trace::NotVisited = context[sub_region] { - context[sub_region] = Trace::FromOutlivesConstraint(constraint); - deque.push_back(sub_region); + let mut handle_trace = #[inline(always)] + |sub, trace| { + if let Trace::NotVisited = context[sub] { + context[sub] = trace; + deque.push_back(sub); } }; - // This loop can be hot. - for constraint in outgoing_edges_from_graph { - if matches!(constraint.category, ConstraintCategory::IllegalUniverse) { - debug!("Ignoring illegal universe constraint: {constraint:?}"); - continue; + // If this is the `'static` region and the graph's direction is normal, then set up the + // Edges iterator to return all regions (#53178). + if r == fr_static && self.constraint_graph.is_normal() { + for sub in self.constraint_graph.outgoing_edges_from_static() { + handle_trace(sub, Trace::FromStatic(sub)); + } + } else { + let edges = self.constraint_graph.outgoing_edges_from_graph(r, &self.constraints); + // This loop can be hot. + for constraint in edges { + if matches!(constraint.category, ConstraintCategory::IllegalUniverse) { + debug!("Ignoring illegal universe constraint: {constraint:?}"); + continue; + } + debug_assert_eq!(constraint.sup, r); + handle_trace(constraint.sub, Trace::FromGraph(constraint)); } - handle_constraint(constraint); } // Member constraints can also give rise to `'r: 'x` edges that // were not part of the graph initially, so watch out for those. // (But they are extremely rare; this loop is very cold.) for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) { + let sub = constraint.min_choice; let p_c = &self.member_constraints[constraint.member_constraint_index]; - let constraint = OutlivesConstraint { - sup: r, - sub: constraint.min_choice, - locations: Locations::All(p_c.definition_span), - span: p_c.definition_span, - category: ConstraintCategory::OpaqueType, - variance_info: ty::VarianceDiagInfo::default(), - from_closure: false, - }; - handle_constraint(constraint); + handle_trace(sub, Trace::FromMember(r, sub, p_c.definition_span)); } } @@ -2036,7 +2094,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraint.category }; - match category { + let interest = match category { // Returns usually provide a type to blame and have specially written diagnostics, // so prioritize them. ConstraintCategory::Return(_) => 0, @@ -2088,9 +2146,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { // specific, and are not used for relations that would make sense to blame. ConstraintCategory::BoringNoLocation => 6, // Do not blame internal constraints. - ConstraintCategory::Internal => 7, - ConstraintCategory::IllegalUniverse => 8, - } + ConstraintCategory::IllegalUniverse => 7, + ConstraintCategory::Internal => 8, + }; + + debug!("constraint {constraint:?} category: {category:?}, interest: {interest:?}"); + + interest }; let best_choice = if blame_source { @@ -2184,7 +2246,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// they *must* be equal (though not having the same repr does not /// mean they are unequal). fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid { - self.constraint_sccs.annotation(scc).representative + self.scc_annotations[scc].representative } pub(crate) fn liveness_constraints(&self) -> &LivenessValues { diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 54f9e82dbb82d..25cbd579ea1cd 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -1,23 +1,17 @@ use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::ErrorGuaranteed; -use rustc_hir::OpaqueTyOrigin; -use rustc_hir::def_id::LocalDefId; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _}; +use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_macros::extension; -use rustc_middle::ty::fold::fold_regions; -use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{ - self, GenericArgKind, GenericArgs, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, - TypingMode, + self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, + TypeVisitableExt, fold_regions, }; use rustc_span::Span; -use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt; -use rustc_trait_selection::traits::ObligationCtxt; +use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid; use tracing::{debug, instrument}; use super::RegionInferenceContext; -use crate::session_diagnostics::{LifetimeMismatchOpaqueParam, NonGenericOpaqueTypeParam}; +use crate::BorrowCheckRootCtxt; +use crate::session_diagnostics::LifetimeMismatchOpaqueParam; use crate::universal_regions::RegionClassification; impl<'tcx> RegionInferenceContext<'tcx> { @@ -64,13 +58,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// [rustc-dev-guide chapter]: /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html - #[instrument(level = "debug", skip(self, infcx), ret)] + #[instrument(level = "debug", skip(self, root_cx, infcx), ret)] pub(crate) fn infer_opaque_types( &self, + root_cx: &mut BorrowCheckRootCtxt<'tcx>, infcx: &InferCtxt<'tcx>, opaque_ty_decls: FxIndexMap, OpaqueHiddenType<'tcx>>, - ) -> FxIndexMap> { - let mut result: FxIndexMap> = FxIndexMap::default(); + ) { let mut decls_modulo_regions: FxIndexMap, (OpaqueTypeKey<'tcx>, Span)> = FxIndexMap::default(); @@ -145,32 +139,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { continue; } } - // Sometimes two opaque types are the same only after we remap the generic parameters - // back to the opaque type definition. E.g. we may have `OpaqueType` mapped to - // `(X, Y)` and `OpaqueType` mapped to `(Y, X)`, and those are the same, but we - // only know that once we convert the generic parameters to those of the opaque type. - if let Some(prev) = result.get_mut(&opaque_type_key.def_id) { - if prev.ty != ty { - let guar = ty.error_reported().err().unwrap_or_else(|| { - let (Ok(e) | Err(e)) = prev - .build_mismatch_error( - &OpaqueHiddenType { ty, span: concrete_type.span }, - infcx.tcx, - ) - .map(|d| d.emit()); - e - }); - prev.ty = Ty::new_error(infcx.tcx, guar); - } - // Pick a better span if there is one. - // FIXME(oli-obk): collect multiple spans for better diagnostics down the road. - prev.span = prev.span.substitute_dummy(concrete_type.span); - } else { - result.insert( - opaque_type_key.def_id, - OpaqueHiddenType { ty, span: concrete_type.span }, - ); - } + + root_cx.add_concrete_opaque_type( + opaque_type_key.def_id, + OpaqueHiddenType { span: concrete_type.span, ty }, + ); // Check that all opaque types have the same region parameters if they have the same // non-region parameters. This is necessary because within the new solver we perform @@ -195,7 +168,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); } } - result } /// Map the regions in the type to named regions. This is similar to what @@ -214,7 +186,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { where T: TypeFoldable>, { - fold_regions(tcx, ty, |region, _| match *region { + fold_regions(tcx, ty, |region, _| match region.kind() { ty::ReVar(vid) => { let scc = self.constraint_sccs.scc(vid); @@ -295,14 +267,21 @@ impl<'tcx> InferCtxt<'tcx> { return Ty::new_error(self.tcx, e); } - if let Err(guar) = - check_opaque_type_parameter_valid(self, opaque_type_key, instantiated_ty.span) - { - return Ty::new_error(self.tcx, guar); + if let Err(err) = check_opaque_type_parameter_valid( + self, + opaque_type_key, + instantiated_ty.span, + DefiningScopeKind::MirBorrowck, + ) { + return Ty::new_error(self.tcx, err.report(self)); } let definition_ty = instantiated_ty - .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false) + .remap_generic_params_to_declaration_params( + opaque_type_key, + self.tcx, + DefiningScopeKind::MirBorrowck, + ) .ty; if let Err(e) = definition_ty.error_reported() { @@ -312,156 +291,3 @@ impl<'tcx> InferCtxt<'tcx> { definition_ty } } - -/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter]. -/// -/// [rustc-dev-guide chapter]: -/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html -fn check_opaque_type_parameter_valid<'tcx>( - infcx: &InferCtxt<'tcx>, - opaque_type_key: OpaqueTypeKey<'tcx>, - span: Span, -) -> Result<(), ErrorGuaranteed> { - let tcx = infcx.tcx; - let opaque_generics = tcx.generics_of(opaque_type_key.def_id); - let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id); - let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default(); - - for (i, arg) in opaque_type_key.iter_captured_args(tcx) { - let arg_is_param = match arg.unpack() { - GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), - GenericArgKind::Lifetime(lt) => { - matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_)) - || (lt.is_static() && opaque_env.param_equal_static(i)) - } - GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)), - }; - - if arg_is_param { - // Register if the same lifetime appears multiple times in the generic args. - // There is an exception when the opaque type *requires* the lifetimes to be equal. - // See [rustc-dev-guide chapter] § "An exception to uniqueness rule". - let seen_where = seen_params.entry(arg).or_default(); - if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) { - seen_where.push(i); - } - } else { - // Prevent `fn foo() -> Foo` from being defining. - let opaque_param = opaque_generics.param_at(i, tcx); - let kind = opaque_param.kind.descr(); - - opaque_env.param_is_error(i)?; - - return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam { - ty: arg, - kind, - span, - param_span: tcx.def_span(opaque_param.def_id), - })); - } - } - - for (_, indices) in seen_params { - if indices.len() > 1 { - let descr = opaque_generics.param_at(indices[0], tcx).kind.descr(); - let spans: Vec<_> = indices - .into_iter() - .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) - .collect(); - #[allow(rustc::diagnostic_outside_of_impl)] - #[allow(rustc::untranslatable_diagnostic)] - return Err(infcx - .dcx() - .struct_span_err(span, "non-defining opaque type use in defining scope") - .with_span_note(spans, format!("{descr} used multiple times")) - .emit()); - } - } - - Ok(()) -} - -/// Computes if an opaque type requires a lifetime parameter to be equal to -/// another one or to the `'static` lifetime. -/// These requirements are derived from the explicit and implied bounds. -struct LazyOpaqueTyEnv<'tcx> { - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - - /// Equal parameters will have the same name. Computed Lazily. - /// Example: - /// `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;` - /// Identity args: `['a, 'b, 'c]` - /// Canonical args: `['static, 'b, 'b]` - canonical_args: std::cell::OnceCell>, -} - -impl<'tcx> LazyOpaqueTyEnv<'tcx> { - fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { - Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() } - } - - fn param_equal_static(&self, param_index: usize) -> bool { - self.get_canonical_args()[param_index].expect_region().is_static() - } - - fn params_equal(&self, param1: usize, param2: usize) -> bool { - let canonical_args = self.get_canonical_args(); - canonical_args[param1] == canonical_args[param2] - } - - fn param_is_error(&self, param_index: usize) -> Result<(), ErrorGuaranteed> { - self.get_canonical_args()[param_index].error_reported() - } - - fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> { - if let Some(&canonical_args) = self.canonical_args.get() { - return canonical_args; - } - - let &Self { tcx, def_id, .. } = self; - let origin = tcx.local_opaque_ty_origin(def_id); - let parent = match origin { - OpaqueTyOrigin::FnReturn { parent, .. } - | OpaqueTyOrigin::AsyncFn { parent, .. } - | OpaqueTyOrigin::TyAlias { parent, .. } => parent, - }; - let param_env = tcx.param_env(parent); - let args = GenericArgs::identity_for_item(tcx, parent).extend_to( - tcx, - def_id.to_def_id(), - |param, _| { - tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into() - }, - ); - - // FIXME(#132279): It feels wrong to use `non_body_analysis` here given that we're - // in a body here. - let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - let ocx = ObligationCtxt::new(&infcx); - - let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| { - tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds"); - Default::default() - }); - let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys); - - let mut seen = vec![tcx.lifetimes.re_static]; - let canonical_args = fold_regions(tcx, args, |r1, _| { - if r1.is_error() { - r1 - } else if let Some(&r2) = seen.iter().find(|&&r2| { - let free_regions = outlives_env.free_region_map(); - free_regions.sub_free_regions(tcx, r1, r2) - && free_regions.sub_free_regions(tcx, r2, r1) - }) { - r2 - } else { - seen.push(r1); - r1 - } - }); - self.canonical_args.set(canonical_args).unwrap(); - canonical_args - } -} diff --git a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs index b2ed8a3582796..8e04791461b26 100644 --- a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs +++ b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs @@ -7,6 +7,8 @@ use rustc_middle::ty::RegionVid; use crate::RegionInferenceContext; use crate::constraints::ConstraintSccIndex; +use crate::region_infer::ConstraintSccs; +use crate::universal_regions::UniversalRegions; pub(crate) struct ReverseSccGraph { graph: VecGraph, @@ -19,6 +21,29 @@ pub(crate) struct ReverseSccGraph { } impl ReverseSccGraph { + pub(super) fn compute( + constraint_sccs: &ConstraintSccs, + universal_regions: &UniversalRegions<'_>, + ) -> Self { + let graph = constraint_sccs.reverse(); + let mut paired_scc_regions = universal_regions + .universal_regions_iter() + .map(|region| (constraint_sccs.scc(region), region)) + .collect::>(); + paired_scc_regions.sort(); + let universal_regions = paired_scc_regions.iter().map(|&(_, region)| region).collect(); + + let mut scc_regions = FxIndexMap::default(); + let mut start = 0; + for chunk in paired_scc_regions.chunk_by(|&(scc1, _), &(scc2, _)| scc1 == scc2) { + let (scc, _) = chunk[0]; + + scc_regions.insert(scc, start..start + chunk.len()); + start += chunk.len(); + } + ReverseSccGraph { graph, scc_regions, universal_regions } + } + /// Find all universal regions that are required to outlive the given SCC. pub(super) fn upper_bounds(&self, scc0: ConstraintSccIndex) -> impl Iterator { let mut duplicates = FxIndexSet::default(); @@ -40,23 +65,7 @@ impl RegionInferenceContext<'_> { return; } - let graph = self.constraint_sccs.reverse(); - let mut paired_scc_regions = self - .universal_regions() - .universal_regions_iter() - .map(|region| (self.constraint_sccs.scc(region), region)) - .collect::>(); - paired_scc_regions.sort(); - let universal_regions = paired_scc_regions.iter().map(|&(_, region)| region).collect(); - - let mut scc_regions = FxIndexMap::default(); - let mut start = 0; - for chunk in paired_scc_regions.chunk_by(|&(scc1, _), &(scc2, _)| scc1 == scc2) { - let (scc, _) = chunk[0]; - scc_regions.insert(scc, start..start + chunk.len()); - start += chunk.len(); - } - - self.rev_scc_graph = Some(ReverseSccGraph { graph, scc_regions, universal_regions }); + self.rev_scc_graph = + Some(ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions())); } } diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index d9ac5b5cb132a..f1427218cdb02 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -21,7 +21,7 @@ rustc_index::newtype_index! { /// An individual element in a region value -- the value of a /// particular region variable consists of a set of these elements. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub(crate) enum RegionElement { /// A point in the control-flow graph. Location(Location), diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index e355d2b415b03..ff92b4168a86c 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -2,8 +2,7 @@ use rustc_index::IndexSlice; use rustc_infer::infer::NllRegionVariableOrigin; use rustc_middle::mir::visit::{MutVisitor, TyContext}; use rustc_middle::mir::{Body, ConstOperand, Location, Promoted}; -use rustc_middle::ty::fold::fold_regions; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, fold_regions}; use rustc_span::Symbol; use tracing::{debug, instrument}; diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs new file mode 100644 index 0000000000000..66b526fa02a50 --- /dev/null +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -0,0 +1,104 @@ +use rustc_abi::FieldIdx; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::bug; +use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::ErrorGuaranteed; +use smallvec::SmallVec; + +use crate::{ClosureRegionRequirements, ConcreteOpaqueTypes, PropagatedBorrowCheckResults}; + +/// The shared context used by both the root as well as all its nested +/// items. +pub(super) struct BorrowCheckRootCtxt<'tcx> { + pub tcx: TyCtxt<'tcx>, + root_def_id: LocalDefId, + concrete_opaque_types: ConcreteOpaqueTypes<'tcx>, + nested_bodies: FxHashMap>, + tainted_by_errors: Option, +} + +impl<'tcx> BorrowCheckRootCtxt<'tcx> { + pub(super) fn new(tcx: TyCtxt<'tcx>, root_def_id: LocalDefId) -> BorrowCheckRootCtxt<'tcx> { + BorrowCheckRootCtxt { + tcx, + root_def_id, + concrete_opaque_types: Default::default(), + nested_bodies: Default::default(), + tainted_by_errors: None, + } + } + + /// Collect all defining uses of opaque types inside of this typeck root. This + /// expects the hidden type to be mapped to the definition parameters of the opaque + /// and errors if we end up with distinct hidden types. + pub(super) fn add_concrete_opaque_type( + &mut self, + def_id: LocalDefId, + hidden_ty: OpaqueHiddenType<'tcx>, + ) { + // Sometimes two opaque types are the same only after we remap the generic parameters + // back to the opaque type definition. E.g. we may have `OpaqueType` mapped to + // `(X, Y)` and `OpaqueType` mapped to `(Y, X)`, and those are the same, but we + // only know that once we convert the generic parameters to those of the opaque type. + if let Some(prev) = self.concrete_opaque_types.0.get_mut(&def_id) { + if prev.ty != hidden_ty.ty { + let guar = hidden_ty.ty.error_reported().err().unwrap_or_else(|| { + let (Ok(e) | Err(e)) = + prev.build_mismatch_error(&hidden_ty, self.tcx).map(|d| d.emit()); + e + }); + prev.ty = Ty::new_error(self.tcx, guar); + } + // Pick a better span if there is one. + // FIXME(oli-obk): collect multiple spans for better diagnostics down the road. + prev.span = prev.span.substitute_dummy(hidden_ty.span); + } else { + self.concrete_opaque_types.0.insert(def_id, hidden_ty); + } + } + + pub(super) fn set_tainted_by_errors(&mut self, guar: ErrorGuaranteed) { + self.tainted_by_errors = Some(guar); + } + + pub(super) fn get_or_insert_nested( + &mut self, + def_id: LocalDefId, + ) -> &PropagatedBorrowCheckResults<'tcx> { + debug_assert_eq!( + self.tcx.typeck_root_def_id(def_id.to_def_id()), + self.root_def_id.to_def_id() + ); + if !self.nested_bodies.contains_key(&def_id) { + let result = super::do_mir_borrowck(self, def_id, None).0; + if let Some(prev) = self.nested_bodies.insert(def_id, result) { + bug!("unexpected previous nested body: {prev:?}"); + } + } + + self.nested_bodies.get(&def_id).unwrap() + } + + pub(super) fn closure_requirements( + &mut self, + nested_body_def_id: LocalDefId, + ) -> &Option> { + &self.get_or_insert_nested(nested_body_def_id).closure_requirements + } + + pub(super) fn used_mut_upvars( + &mut self, + nested_body_def_id: LocalDefId, + ) -> &SmallVec<[FieldIdx; 8]> { + &self.get_or_insert_nested(nested_body_def_id).used_mut_upvars + } + + pub(super) fn finalize(self) -> Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> { + if let Some(guar) = self.tainted_by_errors { + Err(guar) + } else { + Ok(self.tcx.arena.alloc(self.concrete_opaque_types)) + } + } +} diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 4be5d0dbf4284..5143b2fa20598 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -294,17 +294,6 @@ pub(crate) struct MoveBorrow<'a> { pub borrow_span: Span, } -#[derive(Diagnostic)] -#[diag(borrowck_opaque_type_non_generic_param, code = E0792)] -pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> { - pub ty: GenericArg<'tcx>, - pub kind: &'a str, - #[primary_span] - pub span: Span, - #[label] - pub param_span: Span, -} - #[derive(Diagnostic)] #[diag(borrowck_opaque_type_lifetime_mismatch)] pub(crate) struct LifetimeMismatchOpaqueParam<'tcx> { diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 3b48ca305c45b..57516565147eb 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -1,4 +1,4 @@ -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; @@ -6,9 +6,9 @@ use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; use rustc_infer::infer::{self, InferCtxt, SubregionOrigin}; use rustc_infer::traits::query::type_op::DeeplyNormalize; use rustc_middle::bug; -use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory}; -use rustc_middle::ty::fold::fold_regions; -use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; +use rustc_middle::ty::{ + self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions, +}; use rustc_span::Span; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; use tracing::{debug, instrument}; @@ -17,10 +17,10 @@ use crate::constraints::OutlivesConstraint; use crate::region_infer::TypeTest; use crate::type_check::{Locations, MirTypeckRegionConstraints}; use crate::universal_regions::UniversalRegions; +use crate::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory}; pub(crate) struct ConstraintConversion<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, - tcx: TyCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, /// Each RBP `GK: 'a` is assumed to be true. These encode /// relationships like `T: 'a` that are added via implicit bounds @@ -33,7 +33,6 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> { /// logic expecting to see (e.g.) `ReStatic`, and if we supplied /// our special inference variable there, we would mess that up. region_bound_pairs: &'a RegionBoundPairs<'tcx>, - implicit_region_bound: ty::Region<'tcx>, param_env: ty::ParamEnv<'tcx>, known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], locations: Locations, @@ -48,7 +47,6 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, - implicit_region_bound: ty::Region<'tcx>, param_env: ty::ParamEnv<'tcx>, known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], locations: Locations, @@ -58,10 +56,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { ) -> Self { Self { infcx, - tcx: infcx.tcx, universal_regions, region_bound_pairs, - implicit_region_bound, param_env, known_type_outlives_obligations, locations, @@ -88,17 +84,17 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { pub(crate) fn apply_closure_requirements( &mut self, closure_requirements: &ClosureRegionRequirements<'tcx>, - closure_def_id: DefId, + closure_def_id: LocalDefId, closure_args: ty::GenericArgsRef<'tcx>, ) { // Extract the values of the free regions in `closure_args` // into a vector. These are the regions that we will be // relating to one another. let closure_mapping = &UniversalRegions::closure_mapping( - self.tcx, + self.infcx.tcx, closure_args, closure_requirements.num_external_vids, - closure_def_id.expect_local(), + closure_def_id, ); debug!(?closure_mapping); @@ -110,7 +106,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { let subject = match outlives_requirement.subject { ClosureOutlivesSubject::Region(re) => closure_mapping[re].into(), ClosureOutlivesSubject::Ty(subject_ty) => { - subject_ty.instantiate(self.tcx, |vid| closure_mapping[vid]).into() + subject_ty.instantiate(self.infcx.tcx, |vid| closure_mapping[vid]).into() } }; @@ -126,14 +122,14 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, constraint_category: ConstraintCategory<'tcx>, ) { + let tcx = self.infcx.tcx; debug!("generate: constraints at: {:#?}", self.locations); // Extract out various useful fields we'll need below. let ConstraintConversion { - tcx, infcx, + universal_regions, region_bound_pairs, - implicit_region_bound, known_type_outlives_obligations, .. } = *self; @@ -144,7 +140,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { break; } - if !self.tcx.recursion_limit().value_within_limit(iteration) { + if !tcx.recursion_limit().value_within_limit(iteration) { bug!( "FIXME(-Znext-solver): Overflowed when processing region obligations: {outlives_predicates:#?}" ); @@ -169,10 +165,11 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { ); } + let implicit_region_bound = + ty::Region::new_var(tcx, universal_regions.implicit_region_bound()); // we don't actually use this for anything, but // the `TypeOutlives` code needs an origin. let origin = infer::RelateParamBound(self.span, t1, None); - TypeOutlives::new( &mut *self, tcx, @@ -204,7 +201,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { /// are dealt with during trait solving. fn replace_placeholders_with_nll>>(&mut self, value: T) -> T { if value.has_placeholders() { - fold_regions(self.tcx, value, |r, _| match *r { + fold_regions(self.infcx.tcx, value, |r, _| match r.kind() { ty::RePlaceholder(placeholder) => { self.constraints.placeholder_region(self.infcx, placeholder) } @@ -226,7 +223,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> ty::RegionVid { - if let ty::RePlaceholder(placeholder) = *r { + if let ty::RePlaceholder(placeholder) = r.kind() { self.constraints.placeholder_region(self.infcx, placeholder).as_var() } else { self.universal_regions.to_region_vid(r) diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index eaac633b512d6..92732aba29ba3 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -41,22 +41,20 @@ type NormalizedInputsAndOutput<'tcx> = Vec>; pub(crate) struct CreateResult<'tcx> { pub(crate) universal_region_relations: Frozen>, - pub(crate) region_bound_pairs: RegionBoundPairs<'tcx>, - pub(crate) known_type_outlives_obligations: Vec>, + pub(crate) region_bound_pairs: Frozen>, + pub(crate) known_type_outlives_obligations: Frozen>>, pub(crate) normalized_inputs_and_output: NormalizedInputsAndOutput<'tcx>, } pub(crate) fn create<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - implicit_region_bound: ty::Region<'tcx>, universal_regions: UniversalRegions<'tcx>, constraints: &mut MirTypeckRegionConstraints<'tcx>, ) -> CreateResult<'tcx> { UniversalRegionRelationsBuilder { infcx, param_env, - implicit_region_bound, constraints, universal_regions, region_bound_pairs: Default::default(), @@ -181,7 +179,6 @@ struct UniversalRegionRelationsBuilder<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, universal_regions: UniversalRegions<'tcx>, - implicit_region_bound: ty::Region<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, // outputs: @@ -320,7 +317,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { self.infcx, &self.universal_regions, &self.region_bound_pairs, - self.implicit_region_bound, param_env, &known_type_outlives_obligations, Locations::All(span), @@ -337,8 +333,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { outlives: self.outlives.freeze(), inverse_outlives: self.inverse_outlives.freeze(), }), - known_type_outlives_obligations, - region_bound_pairs: self.region_bound_pairs, + known_type_outlives_obligations: Frozen::freeze(known_type_outlives_obligations), + region_bound_pairs: Frozen::freeze(self.region_bound_pairs), normalized_inputs_and_output, } } diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index f70b17e33624b..c6b29fe36fd9c 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -24,9 +24,9 @@ use crate::universal_regions::DefiningTy; impl<'a, 'tcx> TypeChecker<'a, 'tcx> { /// Check explicit closure signature annotation, /// e.g., `|x: FxIndexMap<_, &'static u32>| ...`. - #[instrument(skip(self, body), level = "debug")] - pub(super) fn check_signature_annotation(&mut self, body: &Body<'tcx>) { - let mir_def_id = body.source.def_id().expect_local(); + #[instrument(skip(self), level = "debug")] + pub(super) fn check_signature_annotation(&mut self) { + let mir_def_id = self.body.source.def_id().expect_local(); if !self.tcx().is_closure_like(mir_def_id.to_def_id()) { return; @@ -38,9 +38,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // (e.g., the `_` in the code above) with fresh variables. // Then replace the bound items in the fn sig with fresh variables, // so that they represent the view from "inside" the closure. - let user_provided_sig = self.instantiate_canonical(body.span, &user_provided_poly_sig); + let user_provided_sig = self.instantiate_canonical(self.body.span, &user_provided_poly_sig); let mut user_provided_sig = self.infcx.instantiate_binder_with_fresh_vars( - body.span, + self.body.span, BoundRegionConversionTime::FnCall, user_provided_sig, ); @@ -66,12 +66,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Ty::new_tup(self.tcx(), user_provided_sig.inputs()), args.tupled_upvars_ty(), args.coroutine_captures_by_ref_ty(), - self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(body.span), || { - RegionCtxt::Unknown - }), + self.infcx + .next_region_var(RegionVariableOrigin::MiscVariable(self.body.span), || { + RegionCtxt::Unknown + }), ); - let next_ty_var = || self.infcx.next_ty_var(body.span); + let next_ty_var = || self.infcx.next_ty_var(self.body.span); let output_ty = Ty::new_coroutine( self.tcx(), self.tcx().coroutine_for_closure(mir_def_id), @@ -107,9 +108,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { for (&user_ty, arg_decl) in user_provided_sig.inputs().iter().zip_eq( // In MIR, closure args begin with an implicit `self`. // Also, coroutines have a resume type which may be implicitly `()`. - body.args_iter() + self.body + .args_iter() .skip(1 + if is_coroutine_with_implicit_resume_ty { 1 } else { 0 }) - .map(|local| &body.local_decls[local]), + .map(|local| &self.body.local_decls[local]), ) { self.ascribe_user_type_skip_wf( arg_decl.ty, @@ -119,7 +121,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } // If the user explicitly annotated the output type, enforce it. - let output_decl = &body.local_decls[RETURN_PLACE]; + let output_decl = &self.body.local_decls[RETURN_PLACE]; self.ascribe_user_type_skip_wf( output_decl.ty, ty::UserType::new(ty::UserTypeKind::Ty(user_provided_sig.output())), @@ -127,12 +129,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - #[instrument(skip(self, body), level = "debug")] - pub(super) fn equate_inputs_and_outputs( - &mut self, - body: &Body<'tcx>, - normalized_inputs_and_output: &[Ty<'tcx>], - ) { + #[instrument(skip(self), level = "debug")] + pub(super) fn equate_inputs_and_outputs(&mut self, normalized_inputs_and_output: &[Ty<'tcx>]) { let (&normalized_output_ty, normalized_input_tys) = normalized_inputs_and_output.split_last().unwrap(); @@ -141,18 +139,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // Equate expected input tys with those in the MIR. for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() { - if argument_index + 1 >= body.local_decls.len() { + if argument_index + 1 >= self.body.local_decls.len() { self.tcx() .dcx() - .span_bug(body.span, "found more normalized_input_ty than local_decls"); + .span_bug(self.body.span, "found more normalized_input_ty than local_decls"); } // In MIR, argument N is stored in local N+1. let local = Local::from_usize(argument_index + 1); - let mir_input_ty = body.local_decls[local].ty; + let mir_input_ty = self.body.local_decls[local].ty; - let mir_input_span = body.local_decls[local].source_info.span; + let mir_input_span = self.body.local_decls[local].source_info.span; self.equate_normalized_input_or_output( normalized_input_ty, mir_input_ty, @@ -160,8 +158,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - if let Some(mir_yield_ty) = body.yield_ty() { - let yield_span = body.local_decls[RETURN_PLACE].source_info.span; + if let Some(mir_yield_ty) = self.body.yield_ty() { + let yield_span = self.body.local_decls[RETURN_PLACE].source_info.span; self.equate_normalized_input_or_output( self.universal_regions.yield_ty.unwrap(), mir_yield_ty, @@ -169,8 +167,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - if let Some(mir_resume_ty) = body.resume_ty() { - let yield_span = body.local_decls[RETURN_PLACE].source_info.span; + if let Some(mir_resume_ty) = self.body.resume_ty() { + let yield_span = self.body.local_decls[RETURN_PLACE].source_info.span; self.equate_normalized_input_or_output( self.universal_regions.resume_ty.unwrap(), mir_resume_ty, @@ -179,8 +177,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } // Return types are a bit more complex. They may contain opaque `impl Trait` types. - let mir_output_ty = body.local_decls[RETURN_PLACE].ty; - let output_span = body.local_decls[RETURN_PLACE].source_info.span; + let mir_output_ty = self.body.local_decls[RETURN_PLACE].ty; + let output_span = self.body.local_decls[RETURN_PLACE].source_info.span; self.equate_normalized_input_or_output(normalized_output_ty, mir_output_ty, output_span); } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index dcc179030020d..b7a21cf48c8f7 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -4,8 +4,7 @@ use rustc_middle::mir::visit::{TyContext, Visitor}; use rustc_middle::mir::{Body, Local, Location, SourceInfo}; use rustc_middle::span_bug; use rustc_middle::ty::relate::Relate; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{GenericArgsRef, Region, RegionVid, Ty, TyCtxt}; +use rustc_middle::ty::{GenericArgsRef, Region, RegionVid, Ty, TyCtxt, TypeVisitable}; use rustc_mir_dataflow::ResultsCursor; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; @@ -31,7 +30,6 @@ mod trace; /// performed before pub(super) fn generate<'a, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, - body: &Body<'tcx>, location_map: &DenseLocationMap, flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, @@ -51,23 +49,16 @@ pub(super) fn generate<'a, 'tcx>( // We do record these regions in the polonius context, since they're used to differentiate // relevant and boring locals, which is a key distinction used later in diagnostics. if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() { - let (_, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body); + let (_, boring_locals) = + compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body); typeck.polonius_liveness.as_mut().unwrap().boring_nll_locals = boring_locals.into_iter().collect(); free_regions = typeck.universal_regions.universal_regions_iter().collect(); } let (relevant_live_locals, boring_locals) = - compute_relevant_live_locals(typeck.tcx(), &free_regions, body); - - trace::trace( - typeck, - body, - location_map, - flow_inits, - move_data, - relevant_live_locals, - boring_locals, - ); + compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body); + + trace::trace(typeck, location_map, flow_inits, move_data, relevant_live_locals, boring_locals); // Mark regions that should be live where they appear within rvalues or within a call: like // args, regions, and types. @@ -76,7 +67,7 @@ pub(super) fn generate<'a, 'tcx>( &mut typeck.constraints.liveness_constraints, &typeck.universal_regions, &mut typeck.polonius_liveness, - body, + typeck.body, ); } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index dc35d5eb89c95..512288a0f7d85 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -39,17 +39,15 @@ use crate::type_check::{NormalizeLocation, TypeChecker}; /// this respects `#[may_dangle]` annotations). pub(super) fn trace<'a, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, - body: &Body<'tcx>, location_map: &DenseLocationMap, flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, relevant_live_locals: Vec, boring_locals: Vec, ) { - let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, body); + let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, typeck.body); let cx = LivenessContext { typeck, - body, flow_inits, location_map, local_use_map, @@ -69,14 +67,13 @@ pub(super) fn trace<'a, 'tcx>( /// Contextual state for the type-liveness coroutine. struct LivenessContext<'a, 'typeck, 'b, 'tcx> { /// Current type-checker, giving us our inference context etc. + /// + /// This also stores the body we're currently analyzing. typeck: &'a mut TypeChecker<'typeck, 'tcx>, /// Defines the `PointIndex` mapping location_map: &'a DenseLocationMap, - /// MIR we are analyzing. - body: &'a Body<'tcx>, - /// Mapping to/from the various indices used for initialization tracking. move_data: &'a MoveData<'tcx>, @@ -139,7 +136,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { self.compute_use_live_points_for(local); self.compute_drop_live_points_for(local); - let local_ty = self.cx.body.local_decls[local].ty; + let local_ty = self.cx.body().local_decls[local].ty; if !self.use_live_at.is_empty() { self.cx.add_use_live_facts_for(local_ty, &self.use_live_at); @@ -164,8 +161,8 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { /// and can therefore safely be dropped. fn dropck_boring_locals(&mut self, boring_locals: Vec) { for local in boring_locals { - let local_ty = self.cx.body.local_decls[local].ty; - let local_span = self.cx.body.local_decls[local].source_info.span; + let local_ty = self.cx.body().local_decls[local].ty; + let local_span = self.cx.body().local_decls[local].source_info.span; let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({ let typeck = &self.cx.typeck; move || LivenessContext::compute_drop_data(typeck, local_ty, local_span) @@ -173,7 +170,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { drop_data.dropck_result.report_overflows( self.cx.typeck.infcx.tcx, - self.cx.body.local_decls[local].source_info.span, + self.cx.typeck.body.local_decls[local].source_info.span, local_ty, ); } @@ -202,7 +199,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { .var_dropped_at .iter() .filter_map(|&(local, location_index)| { - let local_ty = self.cx.body.local_decls[local].ty; + let local_ty = self.cx.body().local_decls[local].ty; if relevant_live_locals.contains(&local) || !local_ty.has_free_regions() { return None; } @@ -278,9 +275,9 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { let block = self.cx.location_map.to_location(block_start).block; self.stack.extend( - self.cx.body.basic_blocks.predecessors()[block] + self.cx.body().basic_blocks.predecessors()[block] .iter() - .map(|&pred_bb| self.cx.body.terminator_loc(pred_bb)) + .map(|&pred_bb| self.cx.body().terminator_loc(pred_bb)) .map(|pred_loc| self.cx.location_map.point_from_location(pred_loc)), ); } @@ -305,7 +302,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { // Find the drops where `local` is initialized. for drop_point in self.cx.local_use_map.drops(local) { let location = self.cx.location_map.to_location(drop_point); - debug_assert_eq!(self.cx.body.terminator_loc(location.block), location,); + debug_assert_eq!(self.cx.body().terminator_loc(location.block), location,); if self.cx.initialized_at_terminator(location.block, mpi) && self.drop_live_at.insert(drop_point) @@ -351,7 +348,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { // block. One of them may be either a definition or use // live point. let term_location = self.cx.location_map.to_location(term_point); - debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,); + debug_assert_eq!(self.cx.body().terminator_loc(term_location.block), term_location,); let block = term_location.block; let entry_point = self.cx.location_map.entry_point(term_location.block); for p in (entry_point..term_point).rev() { @@ -376,7 +373,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { } } - let body = self.cx.body; + let body = self.cx.typeck.body; for &pred_block in body.basic_blocks.predecessors()[block].iter() { debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,); @@ -403,7 +400,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { continue; } - let pred_term_loc = self.cx.body.terminator_loc(pred_block); + let pred_term_loc = self.cx.body().terminator_loc(pred_block); let pred_term_point = self.cx.location_map.point_from_location(pred_term_loc); // If the terminator of this predecessor either *assigns* @@ -463,6 +460,9 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { } impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { + fn body(&self) -> &Body<'tcx> { + self.typeck.body + } /// Returns `true` if the local variable (or some part of it) is initialized at the current /// cursor position. Callers should call one of the `seek` methods immediately before to point /// the cursor to the desired location. @@ -481,7 +481,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { /// DROP of some local variable will have an effect -- note that /// drops, as they may unwind, are always terminators. fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { - self.flow_inits.seek_before_primary_effect(self.body.terminator_loc(block)); + self.flow_inits.seek_before_primary_effect(self.body().terminator_loc(block)); self.initialized_at_curr_loc(mpi) } @@ -491,7 +491,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { /// **Warning:** Does not account for the result of `Call` /// instructions. fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { - self.flow_inits.seek_after_primary_effect(self.body.terminator_loc(block)); + self.flow_inits.seek_after_primary_effect(self.body().terminator_loc(block)); self.initialized_at_curr_loc(mpi) } @@ -526,7 +526,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { values::pretty_print_points(self.location_map, live_at.iter()), ); - let local_span = self.body.local_decls()[dropped_local].source_info.span; + let local_span = self.body().local_decls()[dropped_local].source_info.span; let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ let typeck = &self.typeck; move || Self::compute_drop_data(typeck, dropped_ty, local_span) @@ -544,7 +544,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { drop_data.dropck_result.report_overflows( self.typeck.infcx.tcx, - self.body.source_info(*drop_locations.first().unwrap()).span, + self.typeck.body.source_info(*drop_locations.first().unwrap()).span, dropped_ty, ); @@ -610,7 +610,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { Err(ErrorGuaranteed { .. }) => { // We don't run dropck on HIR, and dropck looks inside fields of // types, so there's no guarantee that it succeeds. We also - // can't rely on the the `ErrorGuaranteed` from `fully_perform` here + // can't rely on the `ErrorGuaranteed` from `fully_perform` here // because it comes from delay_span_bug. // // Do this inside of a probe because we don't particularly care (or want) @@ -621,13 +621,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { &ocx, op, span, ) { Ok(_) => ocx.select_all_or_error(), - Err(e) => { - if e.is_empty() { - ocx.select_all_or_error() - } else { - e - } - } + Err(e) => e, }; // Could have no errors if a type lowering error, say, caused the query diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 6d05696e146f1..8c51225712093 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -24,12 +24,10 @@ use rustc_middle::mir::*; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::cast::CastTy; -use rustc_middle::ty::fold::fold_regions; -use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{ self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt, - Dynamic, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, UserArgs, - UserTypeAnnotationIndex, + Dynamic, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, + TypeVisitableExt, UserArgs, UserTypeAnnotationIndex, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::ResultsCursor; @@ -54,7 +52,7 @@ use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderI use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst}; use crate::type_check::free_region_relations::{CreateResult, UniversalRegionRelations}; use crate::universal_regions::{DefiningTy, UniversalRegions}; -use crate::{BorrowckInferCtxt, path_utils}; +use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, path_utils}; macro_rules! span_mirbug { ($context:expr, $elem:expr, $($message:tt)*) => ({ @@ -103,6 +101,7 @@ mod relate_tys; /// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis /// - `location_map` -- map between MIR `Location` and `PointIndex` pub(crate) fn type_check<'a, 'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice>, @@ -114,7 +113,6 @@ pub(crate) fn type_check<'a, 'tcx>( move_data: &MoveData<'tcx>, location_map: Rc, ) -> MirTypeckResults<'tcx> { - let implicit_region_bound = ty::Region::new_var(infcx.tcx, universal_regions.fr_fn_body); let mut constraints = MirTypeckRegionConstraints { placeholder_indices: PlaceholderIndices::default(), placeholder_index_to_region: IndexVec::default(), @@ -130,13 +128,7 @@ pub(crate) fn type_check<'a, 'tcx>( region_bound_pairs, normalized_inputs_and_output, known_type_outlives_obligations, - } = free_region_relations::create( - infcx, - infcx.param_env, - implicit_region_bound, - universal_regions, - &mut constraints, - ); + } = free_region_relations::create(infcx, infcx.param_env, universal_regions, &mut constraints); let pre_obligations = infcx.take_registered_region_obligations(); assert!( @@ -153,13 +145,14 @@ pub(crate) fn type_check<'a, 'tcx>( }; let mut typeck = TypeChecker { + root_cx, infcx, last_span: body.span, body, + promoted, user_type_annotations: &body.user_type_annotations, - region_bound_pairs, - known_type_outlives_obligations, - implicit_region_bound, + region_bound_pairs: ®ion_bound_pairs, + known_type_outlives_obligations: &known_type_outlives_obligations, reported_errors: Default::default(), universal_regions: &universal_region_relations.universal_regions, location_table, @@ -170,15 +163,11 @@ pub(crate) fn type_check<'a, 'tcx>( }; typeck.check_user_type_annotations(); + typeck.visit_body(body); + typeck.equate_inputs_and_outputs(&normalized_inputs_and_output); + typeck.check_signature_annotation(); - let mut verifier = TypeVerifier { typeck: &mut typeck, promoted, last_span: body.span }; - verifier.visit_body(body); - - typeck.typeck_mir(body); - typeck.equate_inputs_and_outputs(body, &normalized_inputs_and_output); - typeck.check_signature_annotation(body); - - liveness::generate(&mut typeck, body, &location_map, flow_inits, move_data); + liveness::generate(&mut typeck, &location_map, flow_inits, move_data); let opaque_type_values = opaque_types::take_opaques_and_register_member_constraints(&mut typeck); @@ -212,306 +201,301 @@ enum FieldAccessError { OutOfRange { field_count: usize }, } -/// Verifies that MIR types are sane. -/// -/// FIXME: This should be merged with the actual `TypeChecker`. -struct TypeVerifier<'a, 'b, 'tcx> { - typeck: &'a mut TypeChecker<'b, 'tcx>, - promoted: &'b IndexSlice>, +/// 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> { + root_cx: &'a mut BorrowCheckRootCtxt<'tcx>, + infcx: &'a BorrowckInferCtxt<'tcx>, last_span: Span, + body: &'a Body<'tcx>, + /// The bodies of all promoteds. As promoteds have a completely separate CFG + /// recursing into them may corrupt your data structures if you're not careful. + promoted: &'a IndexSlice>, + /// User type annotations are shared between the main MIR and the MIR of + /// all of the promoted items. + user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>, + region_bound_pairs: &'a RegionBoundPairs<'tcx>, + known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], + reported_errors: FxIndexSet<(Ty<'tcx>, Span)>, + universal_regions: &'a UniversalRegions<'tcx>, + location_table: &'a PoloniusLocationTable, + polonius_facts: &'a mut Option, + borrow_set: &'a BorrowSet<'tcx>, + constraints: &'a mut MirTypeckRegionConstraints<'tcx>, + /// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints. + polonius_liveness: Option, } -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; - } - } +/// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions +/// inference computation. +pub(crate) struct MirTypeckResults<'tcx> { + pub(crate) constraints: MirTypeckRegionConstraints<'tcx>, + pub(crate) universal_region_relations: Frozen>, + pub(crate) opaque_type_values: FxIndexMap, OpaqueHiddenType<'tcx>>, + pub(crate) polonius_context: Option, +} - fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { - self.super_place(place, context, location); - let tcx = self.tcx(); - let place_ty = place.ty(self.body(), tcx); - if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { - let trait_ref = ty::TraitRef::new( - tcx, - tcx.require_lang_item(LangItem::Copy, Some(self.last_span)), - [place_ty.ty], - ); +/// A collection of region constraints that must be satisfied for the +/// program to be considered well-typed. +pub(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. + pub(crate) placeholder_indices: PlaceholderIndices, - // 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.typeck.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::CopyBound, - ); + /// 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`. + pub(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. + pub(crate) liveness_constraints: LivenessValues, + + pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>, + + pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>, + + pub(crate) universe_causes: FxIndexMap>, + + pub(crate) type_tests: Vec>, +} + +impl<'tcx> MirTypeckRegionConstraints<'tcx> { + /// Creates a `Region` for a given `PlaceholderRegion`, or returns the + /// region that corresponds to a previously created one. + 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 + } } } +} - fn visit_projection_elem( - &mut self, - place: PlaceRef<'tcx>, - elem: PlaceElem<'tcx>, - context: PlaceContext, - location: Location, - ) { - let tcx = self.tcx(); - let base_ty = place.ty(self.body(), tcx); - match elem { - // All these projections don't add any constraints, so there's nothing to - // do here. We check their invariants in the MIR validator after all. - ProjectionElem::Deref - | ProjectionElem::Index(_) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::Downcast(..) => {} - ProjectionElem::Field(field, fty) => { - let fty = self.typeck.normalize(fty, location); - let ty = base_ty.field_ty(tcx, field); - let ty = self.typeck.normalize(ty, location); - debug!(?fty, ?ty); +/// 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: + /// + /// ```compile_fail,E0515 + /// 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), - if let Err(terr) = self.typeck.relate_types( - ty, - context.ambient_variance(), - fty, - location.to_locations(), - ConstraintCategory::Boring, - ) { - span_mirbug!(self, place, "bad field access ({:?}: {:?}): {:?}", ty, fty, terr); - } - } - ProjectionElem::OpaqueCast(ty) => { - let ty = self.typeck.normalize(ty, location); - self.typeck - .relate_types( - ty, - context.ambient_variance(), - base_ty.ty, - location.to_locations(), - ConstraintCategory::TypeAnnotation(AnnotationSource::OpaqueCast), - ) - .unwrap(); - } - ProjectionElem::UnwrapUnsafeBinder(ty) => { - let ty::UnsafeBinder(binder_ty) = *base_ty.ty.kind() else { - unreachable!(); - }; - let found_ty = self.typeck.infcx.instantiate_binder_with_fresh_vars( - self.body().source_info(location).span, - BoundRegionConversionTime::HigherRankedType, - binder_ty.into(), - ); - self.typeck - .relate_types( - ty, - context.ambient_variance(), - found_ty, - location.to_locations(), - ConstraintCategory::Boring, - ) - .unwrap(); - } - ProjectionElem::Subtype(_) => { - bug!("ProjectionElem::Subtype shouldn't exist in borrowck") - } + /// 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), } } - fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, location: Location) { - debug!(?constant, ?location, "visit_const_operand"); + /// 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, + } + } +} - self.super_const_operand(constant, location); - let ty = constant.const_.ty(); +impl<'a, 'tcx> TypeChecker<'a, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } - self.typeck.infcx.tcx.for_each_free_region(&ty, |live_region| { - let live_region_vid = self.typeck.universal_regions.to_region_vid(live_region); - self.typeck.constraints.liveness_constraints.add_location(live_region_vid, location); - }); + fn body(&self) -> &Body<'tcx> { + self.body + } - // HACK(compiler-errors): Constants that are gathered into Body.required_consts - // have their locations erased... - let locations = if location != Location::START { - location.to_locations() + fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> RegionVid { + if let ty::RePlaceholder(placeholder) = r.kind() { + self.constraints.placeholder_region(self.infcx, placeholder).as_var() } else { - Locations::All(constant.span) - }; + self.universal_regions.to_region_vid(r) + } + } - if let Some(annotation_index) = constant.user_ty { - if let Err(terr) = self.typeck.relate_type_and_user_type( - constant.const_.ty(), - ty::Invariant, - &UserTypeProjection { base: annotation_index, projs: vec![] }, - locations, - ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg), - ) { - let annotation = &self.typeck.user_type_annotations[annotation_index]; - span_mirbug!( - self, - constant, - "bad constant user type {:?} vs {:?}: {:?}", - annotation, - constant.const_.ty(), - terr, - ); - } - } else { - let tcx = self.tcx(); - let maybe_uneval = match constant.const_ { - Const::Ty(_, ct) => match ct.kind() { - ty::ConstKind::Unevaluated(uv) => { - Some(UnevaluatedConst { def: uv.def, args: uv.args, promoted: None }) - } - _ => None, - }, - Const::Unevaluated(uv, _) => Some(uv), - _ => None, - }; - - if let Some(uv) = maybe_uneval { - if let Some(promoted) = uv.promoted { - let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>, - promoted: &Body<'tcx>, - ty, - san_ty| { - if let Err(terr) = verifier.typeck.eq_types( - ty, - san_ty, - locations, - ConstraintCategory::Boring, - ) { - span_mirbug!( - verifier, - promoted, - "bad promoted type ({:?}: {:?}): {:?}", - ty, - san_ty, - terr - ); - }; - }; - - let promoted_body = &self.promoted[promoted]; - self.verify_promoted(promoted_body, location); - - let promoted_ty = promoted_body.return_ty(); - check_err(self, promoted_body, ty, promoted_ty); - } else { - self.typeck.ascribe_user_type( - constant.const_.ty(), - ty::UserType::new(ty::UserTypeKind::TypeOf( - uv.def, - UserArgs { args: uv.args, user_self_ty: None }, - )), - locations.span(self.typeck.body), - ); - } - } else if let Some(static_def_id) = constant.check_static_ptr(tcx) { - let unnormalized_ty = tcx.type_of(static_def_id).instantiate_identity(); - let normalized_ty = self.typeck.normalize(unnormalized_ty, locations); - let literal_ty = constant.const_.ty().builtin_deref(true).unwrap(); + fn unsized_feature_enabled(&self) -> bool { + let features = self.tcx().features(); + features.unsized_locals() || features.unsized_fn_params() + } - if let Err(terr) = self.typeck.eq_types( - literal_ty, - normalized_ty, - locations, - ConstraintCategory::Boring, - ) { - span_mirbug!(self, constant, "bad static type {:?} ({:?})", constant, terr); - } + /// Equate the inferred type and the annotated type for user type annotations + #[instrument(skip(self), level = "debug")] + fn check_user_type_annotations(&mut self) { + debug!(?self.user_type_annotations); + let tcx = self.tcx(); + for user_annotation in self.user_type_annotations { + let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation; + let annotation = self.instantiate_canonical(span, user_ty); + if let ty::UserTypeKind::TypeOf(def, args) = annotation.kind + && let DefKind::InlineConst = tcx.def_kind(def) + { + assert!(annotation.bounds.is_empty()); + self.check_inline_const(inferred_ty, def.expect_local(), args, span); + } else { + self.ascribe_user_type(inferred_ty, annotation, span); } + } + } - if let ty::FnDef(def_id, args) = *constant.const_.ty().kind() { - let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args); - self.typeck.normalize_and_prove_instantiated_predicates( - def_id, - instantiated_predicates, - locations, - ); + #[instrument(skip(self, data), level = "debug")] + fn push_region_constraints( + &mut self, + locations: Locations, + category: ConstraintCategory<'tcx>, + data: &QueryRegionConstraints<'tcx>, + ) { + debug!("constraints generated: {:#?}", data); - assert!(!matches!( - tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)), - Some(DefKind::Impl { of_trait: true }) - )); - self.typeck.prove_predicates( - args.types().map(|ty| ty::ClauseKind::WellFormed(ty.into())), - locations, - ConstraintCategory::Boring, - ); - } - } + constraint_conversion::ConstraintConversion::new( + self.infcx, + self.universal_regions, + self.region_bound_pairs, + self.infcx.param_env, + self.known_type_outlives_obligations, + locations, + locations.span(self.body), + category, + self.constraints, + ) + .convert_all(data); } - fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { - self.super_local_decl(local, local_decl); + /// Try to relate `sub <: sup` + fn sub_types( + &mut self, + sub: Ty<'tcx>, + sup: Ty<'tcx>, + locations: Locations, + category: ConstraintCategory<'tcx>, + ) -> Result<(), NoSolution> { + // Use this order of parameters because the sup type is usually the + // "expected" type in diagnostics. + self.relate_types(sup, ty::Contravariant, sub, locations, category) + } - for user_ty in - local_decl.user_ty.as_deref().into_iter().flat_map(UserTypeProjections::projections) - { - let span = self.typeck.user_type_annotations[user_ty.base].span; + #[instrument(skip(self, category), level = "debug")] + fn eq_types( + &mut self, + expected: Ty<'tcx>, + found: Ty<'tcx>, + locations: Locations, + category: ConstraintCategory<'tcx>, + ) -> Result<(), NoSolution> { + self.relate_types(expected, ty::Invariant, found, locations, category) + } - let ty = if local_decl.is_nonref_binding() { - local_decl.ty - } else if let &ty::Ref(_, rty, _) = local_decl.ty.kind() { - // 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. - rty - } else { - bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty); - }; + #[instrument(skip(self), level = "debug")] + fn relate_type_and_user_type( + &mut self, + a: Ty<'tcx>, + v: ty::Variance, + user_ty: &UserTypeProjection, + locations: Locations, + category: ConstraintCategory<'tcx>, + ) -> Result<(), NoSolution> { + let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty; + trace!(?annotated_type); + let mut curr_projected_ty = PlaceTy::from_ty(annotated_type); - if let Err(terr) = self.typeck.relate_type_and_user_type( - ty, - ty::Invariant, - user_ty, - Locations::All(span), - ConstraintCategory::TypeAnnotation(AnnotationSource::Declaration), - ) { - span_mirbug!( - self, - local, - "bad user type on variable {:?}: {:?} != {:?} ({:?})", - local, - local_decl.ty, - local_decl.user_ty, - terr, - ); + let tcx = self.infcx.tcx; + + for proj in &user_ty.projs { + if !self.infcx.next_trait_solver() + && let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind() + { + // There is nothing that we can compare here if we go through an opaque type. + // We're always in its defining scope as we can otherwise not project through + // it, so we're constraining it anyways. + return Ok(()); } + let projected_ty = curr_projected_ty.projection_ty_core( + tcx, + proj, + |this, field, ()| { + let ty = this.field_ty(tcx, field); + self.structurally_resolve(ty, locations) + }, + |_, _| unreachable!(), + ); + curr_projected_ty = projected_ty; } - } - - fn visit_body(&mut self, body: &Body<'tcx>) { - // The types of local_decls are checked above which is called in super_body. - self.super_body(body); - } -} + trace!(?curr_projected_ty); -impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { - fn body(&self) -> &Body<'tcx> { - self.typeck.body - } + let ty = curr_projected_ty.ty; + self.relate_types(ty, v.xform(ty::Contravariant), a, locations, category)?; - fn tcx(&self) -> TyCtxt<'tcx> { - self.typeck.infcx.tcx + Ok(()) } - fn verify_promoted(&mut self, promoted_body: &'b Body<'tcx>, location: Location) { + fn check_promoted(&mut self, promoted_body: &'a 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.typeck.body, promoted_body); + 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. @@ -519,24 +503,23 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { let mut constraints = Default::default(); let mut liveness_constraints = LivenessValues::without_specific_points(Rc::new(DenseLocationMap::new(promoted_body))); - // Don't try to add borrow_region facts for the promoted MIR + // Don't try to add borrow_region facts for the promoted MIR as they refer + // to the wrong locations. let mut swap_constraints = |this: &mut Self| { - mem::swap(this.typeck.polonius_facts, polonius_facts); - mem::swap(&mut this.typeck.constraints.outlives_constraints, &mut constraints); - mem::swap(&mut this.typeck.constraints.liveness_constraints, &mut liveness_constraints); + mem::swap(this.polonius_facts, polonius_facts); + mem::swap(&mut this.constraints.outlives_constraints, &mut constraints); + mem::swap(&mut this.constraints.liveness_constraints, &mut liveness_constraints); }; swap_constraints(self); self.visit_body(promoted_body); - self.typeck.typeck_mir(promoted_body); + self.body = parent_body; - self.typeck.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; @@ -549,7 +532,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // temporary from the user's point of view. constraint.category = ConstraintCategory::Boring; } - self.typeck.constraints.outlives_constraints.push(constraint) + self.constraints.outlives_constraints.push(constraint) } // If the region is live at least one location in the promoted MIR, // then add a liveness constraint to the main MIR for this region @@ -559,619 +542,343 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // unordered. #[allow(rustc::potential_query_instability)] for region in liveness_constraints.live_regions_unordered() { - self.typeck.constraints.liveness_constraints.add_location(region, location); + self.constraints.liveness_constraints.add_location(region, location); } } -} -/// 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 BorrowckInferCtxt<'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>, - region_bound_pairs: RegionBoundPairs<'tcx>, - known_type_outlives_obligations: Vec>, - implicit_region_bound: ty::Region<'tcx>, - reported_errors: FxIndexSet<(Ty<'tcx>, Span)>, - universal_regions: &'a UniversalRegions<'tcx>, - location_table: &'a PoloniusLocationTable, - polonius_facts: &'a mut Option, - borrow_set: &'a BorrowSet<'tcx>, - constraints: &'a mut MirTypeckRegionConstraints<'tcx>, - /// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints. - polonius_liveness: Option, + fn check_inline_const( + &mut self, + inferred_ty: Ty<'tcx>, + def_id: LocalDefId, + args: UserArgs<'tcx>, + span: Span, + ) { + assert!(args.user_self_ty.is_none()); + let tcx = self.tcx(); + let const_ty = tcx.type_of(def_id).instantiate(tcx, args.args); + if let Err(terr) = + self.eq_types(const_ty, inferred_ty, Locations::All(span), ConstraintCategory::Boring) + { + span_bug!( + span, + "bad inline const pattern: ({:?} = {:?}) {:?}", + const_ty, + inferred_ty, + terr + ); + } + let args = self.infcx.resolve_vars_if_possible(args.args); + let predicates = self.prove_closure_bounds(tcx, def_id, args, Locations::All(span)); + self.normalize_and_prove_instantiated_predicates( + def_id.to_def_id(), + predicates, + Locations::All(span), + ); + } } -/// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions -/// inference computation. -pub(crate) struct MirTypeckResults<'tcx> { - pub(crate) constraints: MirTypeckRegionConstraints<'tcx>, - pub(crate) universal_region_relations: Frozen>, - pub(crate) opaque_type_values: FxIndexMap, OpaqueHiddenType<'tcx>>, - pub(crate) polonius_context: Option, -} +impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { + fn visit_span(&mut self, span: Span) { + if !span.is_dummy() { + debug!(?span); + self.last_span = span; + } + } -/// A collection of region constraints that must be satisfied for the -/// program to be considered well-typed. -pub(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. - pub(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`. - pub(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. - pub(crate) liveness_constraints: LivenessValues, - - pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>, - - pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>, - - pub(crate) universe_causes: FxIndexMap>, - - pub(crate) type_tests: Vec>, -} + #[instrument(skip(self, body), level = "debug")] + fn visit_body(&mut self, body: &Body<'tcx>) { + debug_assert!(std::ptr::eq(self.body, body)); -impl<'tcx> MirTypeckRegionConstraints<'tcx> { - /// Creates a `Region` for a given `PlaceholderRegion`, or returns the - /// region that corresponds to a previously created one. - 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 - } + for (local, local_decl) in body.local_decls.iter_enumerated() { + self.visit_local_decl(local, local_decl); } - } -} - -/// 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: - /// - /// ```compile_fail,E0515 - /// 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), -} + for (block, block_data) in body.basic_blocks.iter_enumerated() { + let mut location = Location { block, statement_index: 0 }; + for stmt in &block_data.statements { + self.visit_statement(stmt, location); + location.statement_index += 1; + } -impl Locations { - pub fn from_location(&self) -> Option { - match self { - Locations::All(_) => None, - Locations::Single(from_location) => Some(*from_location), + self.visit_terminator(block_data.terminator(), location); + self.check_iscleanup(block_data); } } - /// 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, - } - } -} + #[instrument(skip(self), level = "debug")] + fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) { + self.super_statement(stmt, location); + let tcx = self.tcx(); + match &stmt.kind { + StatementKind::Assign(box (place, 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) => { + let defining_ty = &self.universal_regions.defining_ty; + if defining_ty.is_const() { + if tcx.is_static(defining_ty.def_id()) { + ConstraintCategory::UseAsStatic + } else { + ConstraintCategory::UseAsConst + } + } else { + ConstraintCategory::Return(ReturnConstraint::Normal) + } + } + Some(l) + if matches!( + self.body.local_decls[l].local_info(), + LocalInfo::AggregateTemp + ) => + { + ConstraintCategory::Usage + } + Some(l) if !self.body.local_decls[l].is_user_variable() => { + ConstraintCategory::Boring + } + _ => ConstraintCategory::Assignment, + }; + debug!( + "assignment category: {:?} {:?}", + category, + place.as_local().map(|l| &self.body.local_decls[l]) + ); -impl<'a, 'tcx> TypeChecker<'a, 'tcx> { - fn body(&self) -> &Body<'tcx> { - self.body - } + let place_ty = place.ty(self.body, tcx).ty; + debug!(?place_ty); + let place_ty = self.normalize(place_ty, location); + debug!("place_ty normalized: {:?}", place_ty); + let rv_ty = rv.ty(self.body, tcx); + debug!(?rv_ty); + let rv_ty = self.normalize(rv_ty, location); + debug!("normalized rv_ty: {:?}", rv_ty); + if let Err(terr) = + self.sub_types(rv_ty, place_ty, location.to_locations(), category) + { + span_mirbug!( + self, + stmt, + "bad assignment ({:?} = {:?}): {:?}", + place_ty, + rv_ty, + terr + ); + } - fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> RegionVid { - if let ty::RePlaceholder(placeholder) = r.kind() { - self.constraints.placeholder_region(self.infcx, placeholder).as_var() - } else { - self.universal_regions.to_region_vid(r) - } - } + if let Some(annotation_index) = self.rvalue_user_ty(rv) { + if let Err(terr) = self.relate_type_and_user_type( + rv_ty, + ty::Invariant, + &UserTypeProjection { base: annotation_index, projs: vec![] }, + location.to_locations(), + ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg), + ) { + let annotation = &self.user_type_annotations[annotation_index]; + span_mirbug!( + self, + stmt, + "bad user type on rvalue ({:?} = {:?}): {:?}", + annotation, + rv_ty, + terr + ); + } + } - fn unsized_feature_enabled(&self) -> bool { - let features = self.tcx().features(); - features.unsized_locals() || features.unsized_fn_params() + if !self.unsized_feature_enabled() { + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Sized, Some(self.last_span)), + [place_ty], + ); + self.prove_trait_ref( + trait_ref, + location.to_locations(), + ConstraintCategory::SizedBound, + ); + } + } + StatementKind::AscribeUserType(box (place, projection), variance) => { + let place_ty = place.ty(self.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(AnnotationSource::Ascription), + ) { + let annotation = &self.user_type_annotations[projection.base]; + span_mirbug!( + self, + stmt, + "bad type assert ({:?} <: {:?} with projections {:?}): {:?}", + place_ty, + annotation, + projection.projs, + terr + ); + } + } + StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(..)) + | StatementKind::FakeRead(..) + | StatementKind::StorageLive(..) + | StatementKind::StorageDead(..) + | StatementKind::Retag { .. } + | StatementKind::Coverage(..) + | StatementKind::ConstEvalCounter + | StatementKind::PlaceMention(..) + | StatementKind::BackwardIncompatibleDropHint { .. } + | StatementKind::Nop => {} + StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(..)) + | StatementKind::Deinit(..) + | StatementKind::SetDiscriminant { .. } => { + bug!("Statement not allowed in this MIR phase") + } + } } - /// Equate the inferred type and the annotated type for user type annotations #[instrument(skip(self), level = "debug")] - fn check_user_type_annotations(&mut self) { - debug!(?self.user_type_annotations); + fn visit_terminator(&mut self, term: &Terminator<'tcx>, term_location: Location) { + self.super_terminator(term, term_location); let tcx = self.tcx(); - for user_annotation in self.user_type_annotations { - let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation; - let annotation = self.instantiate_canonical(span, user_ty); - if let ty::UserTypeKind::TypeOf(def, args) = annotation.kind - && let DefKind::InlineConst = tcx.def_kind(def) - { - assert!(annotation.bounds.is_empty()); - self.check_inline_const(inferred_ty, def.expect_local(), args, span); - } else { - self.ascribe_user_type(inferred_ty, annotation, span); + debug!("terminator kind: {:?}", term.kind); + match &term.kind { + TerminatorKind::Goto { .. } + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate(_) + | TerminatorKind::Return + | TerminatorKind::CoroutineDrop + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => { + // no checks needed for these } - } - } - - #[instrument(skip(self, data), level = "debug")] - fn push_region_constraints( - &mut self, - locations: Locations, - category: ConstraintCategory<'tcx>, - data: &QueryRegionConstraints<'tcx>, - ) { - debug!("constraints generated: {:#?}", data); - constraint_conversion::ConstraintConversion::new( - self.infcx, - self.universal_regions, - &self.region_bound_pairs, - self.implicit_region_bound, - self.infcx.param_env, - &self.known_type_outlives_obligations, - locations, - locations.span(self.body), - category, - self.constraints, - ) - .convert_all(data); - } + TerminatorKind::SwitchInt { discr, .. } => { + let switch_ty = discr.ty(self.body, tcx); + 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 { func, args, .. } + | TerminatorKind::TailCall { func, args, .. } => { + let call_source = match term.kind { + TerminatorKind::Call { call_source, .. } => call_source, + TerminatorKind::TailCall { .. } => CallSource::Normal, + _ => unreachable!(), + }; - /// Try to relate `sub <: sup` - fn sub_types( - &mut self, - sub: Ty<'tcx>, - sup: Ty<'tcx>, - locations: Locations, - category: ConstraintCategory<'tcx>, - ) -> Result<(), NoSolution> { - // Use this order of parameters because the sup type is usually the - // "expected" type in diagnostics. - self.relate_types(sup, ty::Contravariant, sub, locations, category) - } + let func_ty = func.ty(self.body, tcx); + debug!("func_ty.kind: {:?}", func_ty.kind()); - #[instrument(skip(self, category), level = "debug")] - fn eq_types( - &mut self, - expected: Ty<'tcx>, - found: Ty<'tcx>, - locations: Locations, - category: ConstraintCategory<'tcx>, - ) -> Result<(), NoSolution> { - self.relate_types(expected, ty::Invariant, found, locations, category) - } + 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 (unnormalized_sig, map) = tcx.instantiate_bound_regions(sig, |br| { + use crate::renumber::RegionCtxt; - #[instrument(skip(self), level = "debug")] - fn relate_type_and_user_type( - &mut self, - a: Ty<'tcx>, - v: ty::Variance, - user_ty: &UserTypeProjection, - locations: Locations, - category: ConstraintCategory<'tcx>, - ) -> Result<(), NoSolution> { - let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty; - trace!(?annotated_type); - let mut curr_projected_ty = PlaceTy::from_ty(annotated_type); + let region_ctxt_fn = || { + let reg_info = match br.kind { + ty::BoundRegionKind::Anon => sym::anon, + ty::BoundRegionKind::Named(_, name) => name, + ty::BoundRegionKind::ClosureEnv => sym::env, + }; - let tcx = self.infcx.tcx; + RegionCtxt::LateBound(reg_info) + }; - for proj in &user_ty.projs { - if !self.infcx.next_trait_solver() - && let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind() - { - // There is nothing that we can compare here if we go through an opaque type. - // We're always in its defining scope as we can otherwise not project through - // it, so we're constraining it anyways. - return Ok(()); - } - let projected_ty = curr_projected_ty.projection_ty_core( - tcx, - proj, - |this, field, ()| { - let ty = this.field_ty(tcx, field); - self.structurally_resolve(ty, locations) - }, - |_, _| unreachable!(), - ); - curr_projected_ty = projected_ty; - } - trace!(?curr_projected_ty); + self.infcx.next_region_var( + BoundRegion( + term.source_info.span, + br.kind, + BoundRegionConversionTime::FnCall, + ), + region_ctxt_fn, + ) + }); + debug!(?unnormalized_sig); + // IMPORTANT: We have to prove well formed for the function signature before + // we normalize it, as otherwise types like `<&'a &'b () as Trait>::Assoc` + // get normalized away, causing us to ignore the `'b: 'a` bound used by the function. + // + // Normalization results in a well formed type if the input is well formed, so we + // don't have to check it twice. + // + // See #91068 for an example. + self.prove_predicates( + unnormalized_sig.inputs_and_output.iter().map(|ty| { + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + ty.into(), + ))) + }), + term_location.to_locations(), + ConstraintCategory::Boring, + ); - let ty = curr_projected_ty.ty; - self.relate_types(ty, v.xform(ty::Contravariant), a, locations, category)?; + let sig = self.deeply_normalize(unnormalized_sig, term_location); + // HACK(#114936): `WF(sig)` does not imply `WF(normalized(sig))` + // with built-in `Fn` implementations, since the impl may not be + // well-formed itself. + if sig != unnormalized_sig { + self.prove_predicates( + sig.inputs_and_output.iter().map(|ty| { + ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::WellFormed(ty.into()), + )) + }), + term_location.to_locations(), + ConstraintCategory::Boring, + ); + } - Ok(()) - } + if let TerminatorKind::Call { destination, target, .. } = term.kind { + self.check_call_dest(term, &sig, destination, target, term_location); + } - fn check_inline_const( - &mut self, - inferred_ty: Ty<'tcx>, - def_id: LocalDefId, - args: UserArgs<'tcx>, - span: Span, - ) { - assert!(args.user_self_ty.is_none()); - let tcx = self.tcx(); - let const_ty = tcx.type_of(def_id).instantiate(tcx, args.args); - if let Err(terr) = - self.eq_types(const_ty, inferred_ty, Locations::All(span), ConstraintCategory::Boring) - { - span_bug!( - span, - "bad inline const pattern: ({:?} = {:?}) {:?}", - const_ty, - inferred_ty, - terr - ); - } - let args = self.infcx.resolve_vars_if_possible(args.args); - let predicates = self.prove_closure_bounds(tcx, def_id, args, Locations::All(span)); - self.normalize_and_prove_instantiated_predicates( - def_id.to_def_id(), - predicates, - Locations::All(span), - ); - } + // 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.universal_regions.to_region_vid(late_bound_region); + self.constraints.liveness_constraints.add_location(region_vid, term_location); + } - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } + self.check_call_inputs(term, func, &sig, args, term_location, call_source); + } + TerminatorKind::Assert { cond, msg, .. } => { + let cond_ty = cond.ty(self.body, tcx); + if cond_ty != tcx.types.bool { + span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); + } - #[instrument(skip(self, body), level = "debug")] - fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) { - let tcx = self.tcx(); - debug!("stmt kind: {:?}", stmt.kind); - match &stmt.kind { - StatementKind::Assign(box (place, 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) => { - let defining_ty = &self.universal_regions.defining_ty; - if defining_ty.is_const() { - if tcx.is_static(defining_ty.def_id()) { - ConstraintCategory::UseAsStatic - } else { - ConstraintCategory::UseAsConst - } - } else { - ConstraintCategory::Return(ReturnConstraint::Normal) - } - } - Some(l) - if matches!(body.local_decls[l].local_info(), LocalInfo::AggregateTemp) => - { - ConstraintCategory::Usage + if let AssertKind::BoundsCheck { len, index } = &**msg { + if len.ty(self.body, tcx) != tcx.types.usize { + span_mirbug!(self, len, "bounds-check length non-usize {:?}", len) } - Some(l) if !body.local_decls[l].is_user_variable() => { - ConstraintCategory::Boring - } - _ => ConstraintCategory::Assignment, - }; - debug!( - "assignment category: {:?} {:?}", - category, - place.as_local().map(|l| &body.local_decls[l]) - ); - - let place_ty = place.ty(body, tcx).ty; - debug!(?place_ty); - let place_ty = self.normalize(place_ty, location); - debug!("place_ty normalized: {:?}", place_ty); - let rv_ty = rv.ty(body, tcx); - debug!(?rv_ty); - let rv_ty = self.normalize(rv_ty, location); - debug!("normalized rv_ty: {:?}", rv_ty); - if let Err(terr) = - self.sub_types(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::Invariant, - &UserTypeProjection { base: annotation_index, projs: vec![] }, - location.to_locations(), - ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg), - ) { - 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.unsized_feature_enabled() { - let trait_ref = ty::TraitRef::new( - tcx, - tcx.require_lang_item(LangItem::Sized, Some(self.last_span)), - [place_ty], - ); - self.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::SizedBound, - ); - } - } - StatementKind::AscribeUserType(box (place, 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(AnnotationSource::Ascription), - ) { - let annotation = &self.user_type_annotations[projection.base]; - span_mirbug!( - self, - stmt, - "bad type assert ({:?} <: {:?} with projections {:?}): {:?}", - place_ty, - annotation, - projection.projs, - terr - ); - } - } - StatementKind::Intrinsic(box kind) => match kind { - NonDivergingIntrinsic::Assume(op) => self.check_operand(op, location), - NonDivergingIntrinsic::CopyNonOverlapping(..) => span_bug!( - stmt.source_info.span, - "Unexpected NonDivergingIntrinsic::CopyNonOverlapping, should only appear after lowering_intrinsics", - ), - }, - StatementKind::FakeRead(..) - | StatementKind::StorageLive(..) - | StatementKind::StorageDead(..) - | StatementKind::Retag { .. } - | StatementKind::Coverage(..) - | StatementKind::ConstEvalCounter - | StatementKind::PlaceMention(..) - | StatementKind::BackwardIncompatibleDropHint { .. } - | StatementKind::Nop => {} - StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { - bug!("Statement not allowed in this MIR phase") - } - } - } - - #[instrument(skip(self, body, term_location), level = "debug")] - fn check_terminator( - &mut self, - body: &Body<'tcx>, - term: &Terminator<'tcx>, - term_location: Location, - ) { - let tcx = self.tcx(); - debug!("terminator kind: {:?}", term.kind); - match &term.kind { - TerminatorKind::Goto { .. } - | TerminatorKind::UnwindResume - | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::Return - | TerminatorKind::CoroutineDrop - | TerminatorKind::Unreachable - | TerminatorKind::Drop { .. } - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::InlineAsm { .. } => { - // no checks needed for these - } - - TerminatorKind::SwitchInt { discr, .. } => { - self.check_operand(discr, term_location); - - let switch_ty = discr.ty(body, tcx); - 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 { func, args, .. } - | TerminatorKind::TailCall { func, args, .. } => { - let call_source = match term.kind { - TerminatorKind::Call { call_source, .. } => call_source, - TerminatorKind::TailCall { .. } => CallSource::Normal, - _ => unreachable!(), - }; - - self.check_operand(func, term_location); - for arg in args { - self.check_operand(&arg.node, term_location); - } - - let func_ty = func.ty(body, tcx); - debug!("func_ty.kind: {:?}", func_ty.kind()); - - 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 (unnormalized_sig, map) = tcx.instantiate_bound_regions(sig, |br| { - use crate::renumber::RegionCtxt; - - let region_ctxt_fn = || { - let reg_info = match br.kind { - ty::BoundRegionKind::Anon => sym::anon, - ty::BoundRegionKind::Named(_, name) => name, - ty::BoundRegionKind::ClosureEnv => sym::env, - }; - - RegionCtxt::LateBound(reg_info) - }; - - self.infcx.next_region_var( - BoundRegion( - term.source_info.span, - br.kind, - BoundRegionConversionTime::FnCall, - ), - region_ctxt_fn, - ) - }); - debug!(?unnormalized_sig); - // IMPORTANT: We have to prove well formed for the function signature before - // we normalize it, as otherwise types like `<&'a &'b () as Trait>::Assoc` - // get normalized away, causing us to ignore the `'b: 'a` bound used by the function. - // - // Normalization results in a well formed type if the input is well formed, so we - // don't have to check it twice. - // - // See #91068 for an example. - self.prove_predicates( - unnormalized_sig.inputs_and_output.iter().map(|ty| { - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - ty.into(), - ))) - }), - term_location.to_locations(), - ConstraintCategory::Boring, - ); - - let sig = self.deeply_normalize(unnormalized_sig, term_location); - // HACK(#114936): `WF(sig)` does not imply `WF(normalized(sig))` - // with built-in `Fn` implementations, since the impl may not be - // well-formed itself. - if sig != unnormalized_sig { - self.prove_predicates( - sig.inputs_and_output.iter().map(|ty| { - ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::WellFormed(ty.into()), - )) - }), - term_location.to_locations(), - ConstraintCategory::Boring, - ); - } - - if let TerminatorKind::Call { destination, target, .. } = term.kind { - self.check_call_dest(body, term, &sig, destination, target, term_location); - } - - // 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.universal_regions.to_region_vid(late_bound_region); - self.constraints.liveness_constraints.add_location(region_vid, term_location); - } - - self.check_call_inputs(body, term, func, &sig, args, term_location, call_source); - } - TerminatorKind::Assert { cond, msg, .. } => { - self.check_operand(cond, term_location); - - 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 { len, 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) + if index.ty(self.body, tcx) != tcx.types.usize { + span_mirbug!(self, index, "bounds-check index non-usize {:?}", index) } } } TerminatorKind::Yield { value, resume_arg, .. } => { - self.check_operand(value, term_location); - - match body.yield_ty() { + match self.body.yield_ty() { None => span_mirbug!(self, term, "yield in non-coroutine"), Some(ty) => { - let value_ty = value.ty(body, tcx); + let value_ty = value.ty(self.body, tcx); if let Err(terr) = self.sub_types( value_ty, ty, @@ -1190,10 +897,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - match body.resume_ty() { + match self.body.resume_ty() { None => span_mirbug!(self, term, "yield in non-coroutine"), Some(ty) => { - let resume_ty = resume_arg.ty(body, tcx); + let resume_ty = resume_arg.ty(self.body, tcx); if let Err(terr) = self.sub_types( ty, resume_ty.ty, @@ -1215,469 +922,146 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - fn check_call_dest( - &mut self, - body: &Body<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - destination: Place<'tcx>, - target: Option, - term_location: Location, - ) { - let tcx = self.tcx(); - match target { - Some(_) => { - let dest_ty = destination.ty(body, tcx).ty; - let dest_ty = self.normalize(dest_ty, term_location); - let category = match destination.as_local() { - Some(RETURN_PLACE) => { - if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) = - self.universal_regions.defining_ty - { - 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 - } - // The return type of a call is interesting for diagnostics. - _ => ConstraintCategory::Assignment, - }; + fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { + self.super_local_decl(local, local_decl); - let locations = term_location.to_locations(); - - if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) { - span_mirbug!( - self, - term, - "call dest mismatch ({:?} <- {:?}): {:?}", - dest_ty, - sig.output(), - terr - ); - } - - // When `unsized_fn_params` and `unsized_locals` are both not enabled, - // this check is done at `check_local`. - if self.unsized_feature_enabled() { - let span = term.source_info.span; - self.ensure_place_sized(dest_ty, span); - } - } - None => { - // The signature in this call can reference region variables, - // so erase them before calling a query. - let output_ty = self.tcx().erase_regions(sig.output()); - if !output_ty.is_privately_uninhabited( - self.tcx(), - self.infcx.typing_env(self.infcx.param_env), - ) { - span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); - } - } - } - } - - #[instrument(level = "debug", skip(self, body, term, func, term_location, call_source))] - fn check_call_inputs( - &mut self, - body: &Body<'tcx>, - term: &Terminator<'tcx>, - func: &Operand<'tcx>, - sig: &ty::FnSig<'tcx>, - args: &[Spanned>], - term_location: Location, - call_source: CallSource, - ) { - 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); - } - - let func_ty = func.ty(body, self.infcx.tcx); - if let ty::FnDef(def_id, _) = *func_ty.kind() { - // Some of the SIMD intrinsics are special: they need a particular argument to be a - // constant. (Eventually this should use const-generics, but those are not up for the - // task yet: https://github.com/rust-lang/rust/issues/85229.) - if let Some(name @ (sym::simd_shuffle | sym::simd_insert | sym::simd_extract)) = - self.tcx().intrinsic(def_id).map(|i| i.name) - { - let idx = match name { - sym::simd_shuffle => 2, - _ => 1, - }; - if !matches!(args[idx], Spanned { node: Operand::Constant(_), .. }) { - self.tcx().dcx().emit_err(SimdIntrinsicArgConst { - span: term.source_info.span, - arg: idx + 1, - intrinsic: name.to_string(), - }); - } - } - } - debug!(?func_ty); - - for (n, (fn_arg, op_arg)) in iter::zip(sig.inputs(), args).enumerate() { - let op_arg_ty = op_arg.node.ty(body, self.tcx()); + for user_ty in + local_decl.user_ty.as_deref().into_iter().flat_map(UserTypeProjections::projections) + { + let span = self.user_type_annotations[user_ty.base].span; - let op_arg_ty = self.normalize(op_arg_ty, term_location); - let category = if call_source.from_hir_call() { - ConstraintCategory::CallArgument(Some(self.infcx.tcx.erase_regions(func_ty))) + let ty = if local_decl.is_nonref_binding() { + local_decl.ty + } else if let &ty::Ref(_, rty, _) = local_decl.ty.kind() { + // 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. + rty } else { - ConstraintCategory::Boring + bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty); }; - if let Err(terr) = - self.sub_types(op_arg_ty, *fn_arg, term_location.to_locations(), category) - { + + if let Err(terr) = self.relate_type_and_user_type( + ty, + ty::Invariant, + user_ty, + Locations::All(span), + ConstraintCategory::TypeAnnotation(AnnotationSource::Declaration), + ) { span_mirbug!( self, - term, - "bad arg #{:?} ({:?} <- {:?}): {:?}", - n, - fn_arg, - op_arg_ty, - terr + local, + "bad user type on variable {:?}: {:?} != {:?} ({:?})", + local, + local_decl.ty, + local_decl.user_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.all_targets() { - self.assert_iscleanup(body, block_data, *target, is_cleanup); - } - } - TerminatorKind::UnwindResume => { - if !is_cleanup { - span_mirbug!(self, block_data, "resume on non-cleanup block!") - } - } - TerminatorKind::UnwindTerminate(_) => { - if !is_cleanup { - span_mirbug!(self, block_data, "terminate on non-cleanup block!") - } - } - TerminatorKind::Return => { - if is_cleanup { - span_mirbug!(self, block_data, "return on cleanup block") - } - } - TerminatorKind::TailCall { .. } => { - if is_cleanup { - span_mirbug!(self, block_data, "tailcall on cleanup block") - } - } - TerminatorKind::CoroutineDrop { .. } => { - if is_cleanup { - span_mirbug!(self, block_data, "coroutine_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::Assert { target, unwind, .. } => { - self.assert_iscleanup(body, block_data, target, is_cleanup); - self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup); - } - TerminatorKind::Call { ref target, unwind, .. } => { - if let &Some(target) = target { - self.assert_iscleanup(body, block_data, target, is_cleanup); + // When `unsized_fn_params` or `unsized_locals` is enabled, only function calls + // and nullary ops are checked in `check_call_dest`. + if !self.unsized_feature_enabled() { + match self.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; } - self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup); - } - 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); - self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup); - } - TerminatorKind::InlineAsm { ref targets, unwind, .. } => { - for &target in targets { - self.assert_iscleanup(body, block_data, target, is_cleanup); + LocalKind::Temp => { + let span = local_decl.source_info.span; + let ty = local_decl.ty; + self.ensure_place_sized(ty, span); } - self.assert_iscleanup_unwind(body, block_data, unwind, 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); - } - } + #[instrument(skip(self), level = "debug")] + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); + let tcx = self.tcx(); + let span = self.body.source_info(location).span; + match rvalue { + Rvalue::Aggregate(ak, ops) => self.check_aggregate_rvalue(rvalue, ak, ops, location), - fn assert_iscleanup_unwind( - &mut self, - body: &Body<'tcx>, - ctxt: &dyn fmt::Debug, - unwind: UnwindAction, - is_cleanup: bool, - ) { - match unwind { - UnwindAction::Cleanup(unwind) => { - if is_cleanup { - span_mirbug!(self, ctxt, "unwind on cleanup block") - } - self.assert_iscleanup(body, ctxt, unwind, true); - } - UnwindAction::Continue => { - if is_cleanup { - span_mirbug!(self, ctxt, "unwind on cleanup block") + Rvalue::Repeat(operand, len) => { + let array_ty = rvalue.ty(self.body.local_decls(), tcx); + self.prove_predicate( + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(array_ty.into())), + Locations::Single(location), + ConstraintCategory::Boring, + ); + + // 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_to_target_usize(tcx).is_none_or(|len| len > 1) { + match operand { + Operand::Copy(..) | Operand::Constant(..) => { + // These are always okay: direct use of a const, or a value that can + // evidently be copied. + } + Operand::Move(place) => { + // Make sure that repeated elements implement `Copy`. + let ty = place.ty(self.body, tcx).ty; + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Copy, Some(span)), + [ty], + ); + + self.prove_trait_ref( + trait_ref, + Locations::Single(location), + ConstraintCategory::CopyBound, + ); + } + } } } - UnwindAction::Unreachable | UnwindAction::Terminate(_) => (), - } - } - 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; + &Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, ty) => { + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Sized, Some(span)), + [ty], + ); + + self.prove_trait_ref( + trait_ref, + location.to_locations(), + ConstraintCategory::SizedBound, + ); } - LocalKind::Temp => {} - } + &Rvalue::NullaryOp(NullOp::ContractChecks, _) => {} + &Rvalue::NullaryOp(NullOp::UbChecks, _) => {} - // When `unsized_fn_params` or `unsized_locals` is enabled, only function calls - // and nullary ops are checked in `check_call_dest`. - if !self.unsized_feature_enabled() { - let span = local_decl.source_info.span; - let ty = local_decl.ty; - self.ensure_place_sized(ty, span); - } - } + Rvalue::ShallowInitBox(_operand, ty) => { + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Sized, Some(span)), + [*ty], + ); - 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); - // FIXME(#132279): Using `Ty::is_sized` causes us to incorrectly handle opaques here. - if !erased_ty.is_sized(tcx, self.infcx.typing_env(self.infcx.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() { - // 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 a call expression. - self.tcx().dcx().emit_err(MoveUnsized { ty, span }); - } - } - } - - fn aggregate_field_ty( - &mut self, - ak: &AggregateKind<'tcx>, - field_index: FieldIdx, - location: Location, - ) -> Result, FieldAccessError> { - let tcx = self.tcx(); - - match *ak { - AggregateKind::Adt(adt_did, variant_index, args, _, active_field_index) => { - let def = tcx.adt_def(adt_did); - let variant = &def.variant(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, args), location)) - } else { - Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) - } - } - AggregateKind::Closure(_, args) => { - match args.as_closure().upvar_tys().get(field_index.as_usize()) { - Some(ty) => Ok(*ty), - None => Err(FieldAccessError::OutOfRange { - field_count: args.as_closure().upvar_tys().len(), - }), - } - } - AggregateKind::Coroutine(_, args) => { - // 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 args.as_coroutine().prefix_tys().get(field_index.as_usize()) { - Some(ty) => Ok(*ty), - None => Err(FieldAccessError::OutOfRange { - field_count: args.as_coroutine().prefix_tys().len(), - }), - } - } - AggregateKind::CoroutineClosure(_, args) => { - match args.as_coroutine_closure().upvar_tys().get(field_index.as_usize()) { - Some(ty) => Ok(*ty), - None => Err(FieldAccessError::OutOfRange { - field_count: args.as_coroutine_closure().upvar_tys().len(), - }), - } - } - AggregateKind::Array(ty) => Ok(ty), - AggregateKind::Tuple | AggregateKind::RawPtr(..) => { - unreachable!("This should have been covered in check_rvalues"); - } - } - } - - fn check_operand(&mut self, op: &Operand<'tcx>, location: Location) { - debug!(?op, ?location, "check_operand"); - - if let Operand::Constant(constant) = op { - let maybe_uneval = match constant.const_ { - Const::Val(..) | Const::Ty(_, _) => None, - Const::Unevaluated(uv, _) => Some(uv), - }; - - if let Some(uv) = maybe_uneval { - if uv.promoted.is_none() { - let tcx = self.tcx(); - let def_id = uv.def; - if tcx.def_kind(def_id) == DefKind::InlineConst { - let def_id = def_id.expect_local(); - let predicates = self.prove_closure_bounds( - tcx, - def_id, - uv.args, - location.to_locations(), - ); - self.normalize_and_prove_instantiated_predicates( - def_id.to_def_id(), - predicates, - location.to_locations(), - ); - } - } - } - } - } - - #[instrument(skip(self, body), level = "debug")] - fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { - let tcx = self.tcx(); - let span = body.source_info(location).span; - - match rvalue { - Rvalue::Aggregate(ak, ops) => { - for op in ops { - self.check_operand(op, location); - } - self.check_aggregate_rvalue(body, rvalue, ak, ops, location) - } - - Rvalue::Repeat(operand, len) => { - self.check_operand(operand, location); - - let array_ty = rvalue.ty(body.local_decls(), tcx); - self.prove_predicate( - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(array_ty.into())), - Locations::Single(location), - ConstraintCategory::Boring, - ); - - // 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_to_target_usize(tcx).is_none_or(|len| len > 1) { - match operand { - Operand::Copy(..) | Operand::Constant(..) => { - // These are always okay: direct use of a const, or a value that can - // evidently be copied. - } - Operand::Move(place) => { - // Make sure that repeated elements implement `Copy`. - let ty = place.ty(body, tcx).ty; - let trait_ref = ty::TraitRef::new( - tcx, - tcx.require_lang_item(LangItem::Copy, Some(span)), - [ty], - ); - - self.prove_trait_ref( - trait_ref, - Locations::Single(location), - ConstraintCategory::CopyBound, - ); - } - } - } - } - - &Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, ty) => { - let trait_ref = ty::TraitRef::new( - tcx, - tcx.require_lang_item(LangItem::Sized, Some(span)), - [ty], - ); - - self.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::SizedBound, - ); - } - &Rvalue::NullaryOp(NullOp::ContractChecks, _) => {} - &Rvalue::NullaryOp(NullOp::UbChecks, _) => {} - - Rvalue::ShallowInitBox(operand, ty) => { - self.check_operand(operand, location); - - let trait_ref = ty::TraitRef::new( - tcx, - tcx.require_lang_item(LangItem::Sized, Some(span)), - [*ty], - ); - - self.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::SizedBound, - ); - } + self.prove_trait_ref( + trait_ref, + location.to_locations(), + ConstraintCategory::SizedBound, + ); + } Rvalue::Cast(cast_kind, op, ty) => { - self.check_operand(op, location); - match *cast_kind { CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, coercion_source) => { let is_implicit_coercion = coercion_source == CoercionSource::Implicit; - let src_ty = op.ty(body, tcx); + let src_ty = op.ty(self.body, tcx); let mut src_sig = src_ty.fn_sig(tcx); if let ty::FnDef(def_id, _) = src_ty.kind() && let ty::FnPtr(_, target_hdr) = *ty.kind() @@ -1686,7 +1070,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { && let Some(safe_sig) = tcx.adjust_target_feature_sig( *def_id, src_sig, - body.source.def_id(), + self.body.source.def_id(), ) { src_sig = safe_sig; @@ -1779,7 +1163,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { PointerCoercion::ClosureFnPointer(safety), coercion_source, ) => { - let sig = match op.ty(body, tcx).kind() { + let sig = match op.ty(self.body, tcx).kind() { ty::Closure(_, args) => args.as_closure().sig(), _ => bug!(), }; @@ -1808,7 +1192,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { PointerCoercion::UnsafeFnPointer, coercion_source, ) => { - let fn_sig = op.ty(body, tcx).fn_sig(tcx); + let fn_sig = op.ty(self.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 @@ -1842,7 +1226,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let trait_ref = ty::TraitRef::new( tcx, tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)), - [op.ty(body, tcx), ty], + [op.ty(self.body, tcx), ty], ); let is_implicit_coercion = coercion_source == CoercionSource::Implicit; @@ -1868,7 +1252,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { _ => panic!("Invalid dyn* cast_ty"), }; - let self_ty = op.ty(body, tcx); + let self_ty = op.ty(self.body, tcx); let is_implicit_coercion = coercion_source == CoercionSource::Implicit; self.prove_predicates( @@ -1895,7 +1279,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { PointerCoercion::MutToConstPointer, coercion_source, ) => { - let ty::RawPtr(ty_from, hir::Mutability::Mut) = op.ty(body, tcx).kind() + let ty::RawPtr(ty_from, hir::Mutability::Mut) = + op.ty(self.body, tcx).kind() else { span_mirbug!(self, rvalue, "unexpected base type for cast {:?}", ty,); return; @@ -1923,7 +1308,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::PointerCoercion(PointerCoercion::ArrayToPointer, coercion_source) => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.body, tcx); let opt_ty_elem_mut = match ty_from.kind() { ty::RawPtr(array_ty, array_mut) => match array_ty.kind() { @@ -1986,7 +1371,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::PointerExposeProvenance => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.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) { @@ -2004,7 +1389,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::PointerWithExposedProvenance => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.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) { @@ -2021,7 +1406,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::IntToInt => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.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) { @@ -2038,7 +1423,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::IntToFloat => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.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) { @@ -2055,7 +1440,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::FloatToInt => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.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) { @@ -2072,7 +1457,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::FloatToFloat => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.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) { @@ -2089,7 +1474,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::FnPtrToPtr => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.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) { @@ -2106,7 +1491,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::PtrToPtr => { - let ty_from = op.ty(body, tcx); + let ty_from = op.ty(self.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) { @@ -2120,8 +1505,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // // Note that other checks (such as denying `dyn Send` -> `dyn // Debug`) are in `rustc_hir_typeck`. - if let ty::Dynamic(src_tty, _src_lt, _) = *src_tail.kind() - && let ty::Dynamic(dst_tty, dst_lt, _) = *dst_tail.kind() + if let ty::Dynamic(src_tty, _src_lt, ty::Dyn) = *src_tail.kind() + && let ty::Dynamic(dst_tty, dst_lt, ty::Dyn) = *dst_tail.kind() && src_tty.principal().is_some() && dst_tty.principal().is_some() { @@ -2172,112 +1557,662 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } CastKind::Transmute => { - span_mirbug!( - self, - rvalue, - "Unexpected CastKind::Transmute, which is not permitted in Analysis MIR", - ); + let ty_from = op.ty(self.body, tcx); + match ty_from.kind() { + ty::Pat(base, _) if base == ty => {} + _ => span_mirbug!( + self, + rvalue, + "Unexpected CastKind::Transmute {ty_from:?} -> {ty:?}, which is not permitted in Analysis MIR", + ), + } + } + } + } + + Rvalue::Ref(region, _borrow_kind, borrowed_place) => { + self.add_reborrow_constraint(location, *region, borrowed_place); + } + + Rvalue::BinaryOp( + BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, + box (left, right), + ) => { + let ty_left = left.ty(self.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(self.body, tcx); + let common_ty = + self.infcx.next_ty_var(self.body.source_info(location).span); + self.sub_types( + ty_left, + common_ty, + location.to_locations(), + ConstraintCategory::CallArgument(None), + ) + .unwrap_or_else(|err| { + bug!("Could not equate type variable with {:?}: {:?}", ty_left, err) + }); + if let Err(terr) = self.sub_types( + ty_right, + common_ty, + location.to_locations(), + ConstraintCategory::CallArgument(None), + ) { + 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(self.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(self.body, tcx) + ), + } + } + + Rvalue::WrapUnsafeBinder(op, ty) => { + let operand_ty = op.ty(self.body, self.tcx()); + let ty::UnsafeBinder(binder_ty) = *ty.kind() else { + unreachable!(); + }; + let expected_ty = self.infcx.instantiate_binder_with_fresh_vars( + self.body().source_info(location).span, + BoundRegionConversionTime::HigherRankedType, + binder_ty.into(), + ); + self.sub_types( + operand_ty, + expected_ty, + location.to_locations(), + ConstraintCategory::Boring, + ) + .unwrap(); + } + + Rvalue::Use(_) + | Rvalue::UnaryOp(_, _) + | Rvalue::CopyForDeref(_) + | Rvalue::BinaryOp(..) + | Rvalue::RawPtr(..) + | Rvalue::ThreadLocalRef(..) + | Rvalue::Len(..) + | Rvalue::Discriminant(..) + | Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {} + } + } + + #[instrument(level = "debug", skip(self))] + fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) { + self.super_operand(op, location); + if let Operand::Constant(constant) = op { + let maybe_uneval = match constant.const_ { + Const::Val(..) | Const::Ty(_, _) => None, + Const::Unevaluated(uv, _) => Some(uv), + }; + + if let Some(uv) = maybe_uneval { + if uv.promoted.is_none() { + let tcx = self.tcx(); + let def_id = uv.def; + if tcx.def_kind(def_id) == DefKind::InlineConst { + let def_id = def_id.expect_local(); + let predicates = self.prove_closure_bounds( + tcx, + def_id, + uv.args, + location.to_locations(), + ); + self.normalize_and_prove_instantiated_predicates( + def_id.to_def_id(), + predicates, + location.to_locations(), + ); + } + } + } + } + } + + #[instrument(level = "debug", skip(self))] + fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, location: Location) { + self.super_const_operand(constant, location); + let ty = constant.const_.ty(); + + self.infcx.tcx.for_each_free_region(&ty, |live_region| { + let live_region_vid = self.universal_regions.to_region_vid(live_region); + self.constraints.liveness_constraints.add_location(live_region_vid, location); + }); + + let locations = location.to_locations(); + if let Some(annotation_index) = constant.user_ty { + if let Err(terr) = self.relate_type_and_user_type( + constant.const_.ty(), + ty::Invariant, + &UserTypeProjection { base: annotation_index, projs: vec![] }, + locations, + ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg), + ) { + let annotation = &self.user_type_annotations[annotation_index]; + span_mirbug!( + self, + constant, + "bad constant user type {:?} vs {:?}: {:?}", + annotation, + constant.const_.ty(), + terr, + ); + } + } else { + let tcx = self.tcx(); + let maybe_uneval = match constant.const_ { + Const::Ty(_, ct) => match ct.kind() { + ty::ConstKind::Unevaluated(uv) => { + Some(UnevaluatedConst { def: uv.def, args: uv.args, promoted: None }) + } + _ => None, + }, + Const::Unevaluated(uv, _) => Some(uv), + _ => None, + }; + + if let Some(uv) = maybe_uneval { + if let Some(promoted) = uv.promoted { + let promoted_body = &self.promoted[promoted]; + self.check_promoted(promoted_body, location); + let promoted_ty = promoted_body.return_ty(); + if let Err(terr) = + self.eq_types(ty, promoted_ty, locations, ConstraintCategory::Boring) + { + span_mirbug!( + self, + promoted, + "bad promoted type ({:?}: {:?}): {:?}", + ty, + promoted_ty, + terr + ); + }; + } else { + self.ascribe_user_type( + constant.const_.ty(), + ty::UserType::new(ty::UserTypeKind::TypeOf( + uv.def, + UserArgs { args: uv.args, user_self_ty: None }, + )), + locations.span(self.body), + ); + } + } else if let Some(static_def_id) = constant.check_static_ptr(tcx) { + let unnormalized_ty = tcx.type_of(static_def_id).instantiate_identity(); + let normalized_ty = self.normalize(unnormalized_ty, locations); + let literal_ty = constant.const_.ty().builtin_deref(true).unwrap(); + + if let Err(terr) = + self.eq_types(literal_ty, normalized_ty, locations, ConstraintCategory::Boring) + { + span_mirbug!(self, constant, "bad static type {:?} ({:?})", constant, terr); + } + } else if let Const::Ty(_, ct) = constant.const_ + && let ty::ConstKind::Param(p) = ct.kind() + { + let body_def_id = self.universal_regions.defining_ty.def_id(); + let const_param = tcx.generics_of(body_def_id).const_param(p, tcx); + self.ascribe_user_type( + constant.const_.ty(), + ty::UserType::new(ty::UserTypeKind::TypeOf( + const_param.def_id, + UserArgs { + args: self.universal_regions.defining_ty.args(), + user_self_ty: None, + }, + )), + locations.span(self.body), + ); + } + + if let ty::FnDef(def_id, args) = *constant.const_.ty().kind() { + let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args); + self.normalize_and_prove_instantiated_predicates( + def_id, + instantiated_predicates, + locations, + ); + + assert!(!matches!( + tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)), + Some(DefKind::Impl { of_trait: true }) + )); + self.prove_predicates( + args.types().map(|ty| ty::ClauseKind::WellFormed(ty.into())), + locations, + ConstraintCategory::Boring, + ); + } + } + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + self.super_place(place, context, location); + let tcx = self.tcx(); + let place_ty = place.ty(self.body, tcx); + if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Copy, Some(self.last_span)), + [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.prove_trait_ref(trait_ref, location.to_locations(), ConstraintCategory::CopyBound); + } + } + + fn visit_projection_elem( + &mut self, + place: PlaceRef<'tcx>, + elem: PlaceElem<'tcx>, + context: PlaceContext, + location: Location, + ) { + let tcx = self.tcx(); + let base_ty = place.ty(self.body(), tcx); + match elem { + // All these projections don't add any constraints, so there's nothing to + // do here. We check their invariants in the MIR validator after all. + ProjectionElem::Deref + | ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(..) => {} + ProjectionElem::Field(field, fty) => { + let fty = self.normalize(fty, location); + let ty = base_ty.field_ty(tcx, field); + let ty = self.normalize(ty, location); + debug!(?fty, ?ty); + + if let Err(terr) = self.relate_types( + ty, + context.ambient_variance(), + fty, + location.to_locations(), + ConstraintCategory::Boring, + ) { + span_mirbug!(self, place, "bad field access ({:?}: {:?}): {:?}", ty, fty, terr); + } + } + ProjectionElem::OpaqueCast(ty) => { + let ty = self.normalize(ty, location); + self.relate_types( + ty, + context.ambient_variance(), + base_ty.ty, + location.to_locations(), + ConstraintCategory::TypeAnnotation(AnnotationSource::OpaqueCast), + ) + .unwrap(); + } + ProjectionElem::UnwrapUnsafeBinder(ty) => { + let ty::UnsafeBinder(binder_ty) = *base_ty.ty.kind() else { + unreachable!(); + }; + let found_ty = self.infcx.instantiate_binder_with_fresh_vars( + self.body.source_info(location).span, + BoundRegionConversionTime::HigherRankedType, + binder_ty.into(), + ); + self.relate_types( + ty, + context.ambient_variance(), + found_ty, + location.to_locations(), + ConstraintCategory::Boring, + ) + .unwrap(); + } + ProjectionElem::Subtype(_) => { + bug!("ProjectionElem::Subtype shouldn't exist in borrowck") + } + } + } +} + +impl<'a, 'tcx> TypeChecker<'a, 'tcx> { + fn check_call_dest( + &mut self, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + destination: Place<'tcx>, + target: Option, + term_location: Location, + ) { + let tcx = self.tcx(); + match target { + Some(_) => { + let dest_ty = destination.ty(self.body, tcx).ty; + let dest_ty = self.normalize(dest_ty, term_location); + let category = match destination.as_local() { + Some(RETURN_PLACE) => { + if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) = + self.universal_regions.defining_ty + { + if tcx.is_static(def_id) { + ConstraintCategory::UseAsStatic + } else { + ConstraintCategory::UseAsConst + } + } else { + ConstraintCategory::Return(ReturnConstraint::Normal) + } + } + Some(l) if !self.body.local_decls[l].is_user_variable() => { + ConstraintCategory::Boring + } + // The return type of a call is interesting for diagnostics. + _ => ConstraintCategory::Assignment, + }; + + let locations = term_location.to_locations(); + + if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) { + span_mirbug!( + self, + term, + "call dest mismatch ({:?} <- {:?}): {:?}", + dest_ty, + sig.output(), + terr + ); + } + + // When `unsized_fn_params` and `unsized_locals` are both not enabled, + // this check is done at `check_local`. + if self.unsized_feature_enabled() { + let span = term.source_info.span; + self.ensure_place_sized(dest_ty, span); + } + } + None => { + // The signature in this call can reference region variables, + // so erase them before calling a query. + let output_ty = self.tcx().erase_regions(sig.output()); + if !output_ty.is_privately_uninhabited( + self.tcx(), + self.infcx.typing_env(self.infcx.param_env), + ) { + span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); + } + } + } + } + + #[instrument(level = "debug", skip(self, term, func, term_location, call_source))] + fn check_call_inputs( + &mut self, + term: &Terminator<'tcx>, + func: &Operand<'tcx>, + sig: &ty::FnSig<'tcx>, + args: &[Spanned>], + term_location: Location, + call_source: CallSource, + ) { + 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); + } + + let func_ty = func.ty(self.body, self.infcx.tcx); + if let ty::FnDef(def_id, _) = *func_ty.kind() { + // Some of the SIMD intrinsics are special: they need a particular argument to be a + // constant. (Eventually this should use const-generics, but those are not up for the + // task yet: https://github.com/rust-lang/rust/issues/85229.) + if let Some(name @ (sym::simd_shuffle | sym::simd_insert | sym::simd_extract)) = + self.tcx().intrinsic(def_id).map(|i| i.name) + { + let idx = match name { + sym::simd_shuffle => 2, + _ => 1, + }; + if !matches!(args[idx], Spanned { node: Operand::Constant(_), .. }) { + self.tcx().dcx().emit_err(SimdIntrinsicArgConst { + span: term.source_info.span, + arg: idx + 1, + intrinsic: name.to_string(), + }); } } + } + debug!(?func_ty); - Rvalue::Ref(region, _borrow_kind, borrowed_place) => { - self.add_reborrow_constraint(body, location, *region, borrowed_place); - } + for (n, (fn_arg, op_arg)) in iter::zip(sig.inputs(), args).enumerate() { + let op_arg_ty = op_arg.node.ty(self.body, self.tcx()); - Rvalue::BinaryOp( - BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, - box (left, right), - ) => { - self.check_operand(left, location); - self.check_operand(right, location); + let op_arg_ty = self.normalize(op_arg_ty, term_location); + let category = if call_source.from_hir_call() { + ConstraintCategory::CallArgument(Some(self.infcx.tcx.erase_regions(func_ty))) + } 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 + ); + } + } + } - 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(body.source_info(location).span); - self.sub_types( - ty_left, - common_ty, - location.to_locations(), - ConstraintCategory::CallArgument(None), - ) - .unwrap_or_else(|err| { - bug!("Could not equate type variable with {:?}: {:?}", ty_left, err) - }); - if let Err(terr) = self.sub_types( - ty_right, - common_ty, - location.to_locations(), - ConstraintCategory::CallArgument(None), - ) { - 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) - ), + fn check_iscleanup(&mut self, block_data: &BasicBlockData<'tcx>) { + let is_cleanup = block_data.is_cleanup; + match block_data.terminator().kind { + TerminatorKind::Goto { target } => { + self.assert_iscleanup(block_data, target, is_cleanup) + } + TerminatorKind::SwitchInt { ref targets, .. } => { + for target in targets.all_targets() { + self.assert_iscleanup(block_data, *target, is_cleanup); } } - - Rvalue::Use(operand) | Rvalue::UnaryOp(_, operand) => { - self.check_operand(operand, location); + TerminatorKind::UnwindResume => { + if !is_cleanup { + span_mirbug!(self, block_data, "resume on non-cleanup block!") + } + } + TerminatorKind::UnwindTerminate(_) => { + if !is_cleanup { + span_mirbug!(self, block_data, "terminate on non-cleanup block!") + } + } + TerminatorKind::Return => { + if is_cleanup { + span_mirbug!(self, block_data, "return on cleanup block") + } + } + TerminatorKind::TailCall { .. } => { + if is_cleanup { + span_mirbug!(self, block_data, "tailcall on cleanup block") + } + } + TerminatorKind::CoroutineDrop { .. } => { + if is_cleanup { + span_mirbug!(self, block_data, "coroutine_drop in cleanup block") + } + } + TerminatorKind::Yield { resume, drop, .. } => { + if is_cleanup { + span_mirbug!(self, block_data, "yield in cleanup block") + } + self.assert_iscleanup(block_data, resume, is_cleanup); + if let Some(drop) = drop { + self.assert_iscleanup(block_data, drop, is_cleanup); + } + } + TerminatorKind::Unreachable => {} + TerminatorKind::Drop { target, unwind, drop, .. } => { + self.assert_iscleanup(block_data, target, is_cleanup); + self.assert_iscleanup_unwind(block_data, unwind, is_cleanup); + if let Some(drop) = drop { + self.assert_iscleanup(block_data, drop, is_cleanup); + } + } + TerminatorKind::Assert { target, unwind, .. } => { + self.assert_iscleanup(block_data, target, is_cleanup); + self.assert_iscleanup_unwind(block_data, unwind, is_cleanup); + } + TerminatorKind::Call { ref target, unwind, .. } => { + if let &Some(target) = target { + self.assert_iscleanup(block_data, target, is_cleanup); + } + self.assert_iscleanup_unwind(block_data, unwind, is_cleanup); + } + TerminatorKind::FalseEdge { real_target, imaginary_target } => { + self.assert_iscleanup(block_data, real_target, is_cleanup); + self.assert_iscleanup(block_data, imaginary_target, is_cleanup); + } + TerminatorKind::FalseUnwind { real_target, unwind } => { + self.assert_iscleanup(block_data, real_target, is_cleanup); + self.assert_iscleanup_unwind(block_data, unwind, is_cleanup); } - Rvalue::CopyForDeref(place) => { - let op = &Operand::Copy(*place); - self.check_operand(op, location); + TerminatorKind::InlineAsm { ref targets, unwind, .. } => { + for &target in targets { + self.assert_iscleanup(block_data, target, is_cleanup); + } + self.assert_iscleanup_unwind(block_data, unwind, is_cleanup); } + } + } - Rvalue::BinaryOp(_, box (left, right)) => { - self.check_operand(left, location); - self.check_operand(right, location); + fn assert_iscleanup(&mut self, ctxt: &dyn fmt::Debug, bb: BasicBlock, iscleanuppad: bool) { + if self.body[bb].is_cleanup != iscleanuppad { + span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", bb, iscleanuppad); + } + } + + fn assert_iscleanup_unwind( + &mut self, + ctxt: &dyn fmt::Debug, + unwind: UnwindAction, + is_cleanup: bool, + ) { + match unwind { + UnwindAction::Cleanup(unwind) => { + if is_cleanup { + span_mirbug!(self, ctxt, "unwind on cleanup block") + } + self.assert_iscleanup(ctxt, unwind, true); + } + UnwindAction::Continue => { + if is_cleanup { + span_mirbug!(self, ctxt, "unwind on cleanup block") + } } + UnwindAction::Unreachable | UnwindAction::Terminate(_) => (), + } + } - Rvalue::WrapUnsafeBinder(op, ty) => { - self.check_operand(op, location); - let operand_ty = op.ty(self.body, self.tcx()); + fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) { + let tcx = self.tcx(); - let ty::UnsafeBinder(binder_ty) = *ty.kind() else { - unreachable!(); - }; - let expected_ty = self.infcx.instantiate_binder_with_fresh_vars( - self.body().source_info(location).span, - BoundRegionConversionTime::HigherRankedType, - binder_ty.into(), - ); - self.sub_types( - operand_ty, - expected_ty, - location.to_locations(), - ConstraintCategory::Boring, - ) - .unwrap(); + // 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); + // FIXME(#132279): Using `Ty::is_sized` causes us to incorrectly handle opaques here. + if !erased_ty.is_sized(tcx, self.infcx.typing_env(self.infcx.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() { + // 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 a call expression. + self.tcx().dcx().emit_err(MoveUnsized { ty, span }); } + } + } - Rvalue::RawPtr(..) - | Rvalue::ThreadLocalRef(..) - | Rvalue::Len(..) - | Rvalue::Discriminant(..) - | Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {} + fn aggregate_field_ty( + &mut self, + ak: &AggregateKind<'tcx>, + field_index: FieldIdx, + location: Location, + ) -> Result, FieldAccessError> { + let tcx = self.tcx(); + + match *ak { + AggregateKind::Adt(adt_did, variant_index, args, _, active_field_index) => { + let def = tcx.adt_def(adt_did); + let variant = &def.variant(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, args), location)) + } else { + Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) + } + } + AggregateKind::Closure(_, args) => { + match args.as_closure().upvar_tys().get(field_index.as_usize()) { + Some(ty) => Ok(*ty), + None => Err(FieldAccessError::OutOfRange { + field_count: args.as_closure().upvar_tys().len(), + }), + } + } + AggregateKind::Coroutine(_, args) => { + // 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 args.as_coroutine().prefix_tys().get(field_index.as_usize()) { + Some(ty) => Ok(*ty), + None => Err(FieldAccessError::OutOfRange { + field_count: args.as_coroutine().prefix_tys().len(), + }), + } + } + AggregateKind::CoroutineClosure(_, args) => { + match args.as_coroutine_closure().upvar_tys().get(field_index.as_usize()) { + Some(ty) => Ok(*ty), + None => Err(FieldAccessError::OutOfRange { + field_count: args.as_coroutine_closure().upvar_tys().len(), + }), + } + } + AggregateKind::Array(ty) => Ok(ty), + AggregateKind::Tuple | AggregateKind::RawPtr(..) => { + unreachable!("This should have been covered in check_rvalues"); + } } } @@ -2315,7 +2250,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { fn check_aggregate_rvalue( &mut self, - body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, aggregate_kind: &AggregateKind<'tcx>, operands: &IndexSlice>, @@ -2348,7 +2282,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { continue; } }; - let operand_ty = operand.ty(body, tcx); + let operand_ty = operand.ty(self.body, tcx); let operand_ty = self.normalize(operand_ty, location); if let Err(terr) = self.sub_types( @@ -2378,7 +2312,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { /// - `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>, @@ -2417,7 +2350,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let def = self.body.source.def_id().expect_local(); let upvars = tcx.closure_captures(def); let field = - path_utils::is_upvar_field_projection(tcx, upvars, borrowed_place.as_ref(), body); + path_utils::is_upvar_field_projection(tcx, upvars, borrowed_place.as_ref(), self.body); let category = if let Some(field) = field { ConstraintCategory::ClosureUpvar(field) } else { @@ -2429,7 +2362,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { match elem { ProjectionElem::Deref => { - let base_ty = base.ty(body, tcx).ty; + let base_ty = base.ty(self.body, tcx).ty; debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); match base_ty.kind() { @@ -2438,7 +2371,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { sup: ref_region.as_var(), sub: borrow_region.as_var(), locations: location.to_locations(), - span: location.to_locations().span(body), + span: location.to_locations().span(self.body), category, variance_info: ty::VarianceDiagInfo::default(), from_closure: false, @@ -2569,20 +2502,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { args: GenericArgsRef<'tcx>, locations: Locations, ) -> ty::InstantiatedPredicates<'tcx> { - if let Some(closure_requirements) = &tcx.mir_borrowck(def_id).closure_requirements { + if let Some(closure_requirements) = &self.root_cx.closure_requirements(def_id) { constraint_conversion::ConstraintConversion::new( self.infcx, self.universal_regions, - &self.region_bound_pairs, - self.implicit_region_bound, + self.region_bound_pairs, self.infcx.param_env, - &self.known_type_outlives_obligations, + self.known_type_outlives_obligations, locations, self.body.span, // irrelevant; will be overridden. ConstraintCategory::Boring, // same as above. self.constraints, ) - .apply_closure_requirements(closure_requirements, def_id.to_def_id(), args); + .apply_closure_requirements(closure_requirements, def_id, args); } // Now equate closure args to regions inherited from `typeck_root_def_id`. Fixes #98589. @@ -2622,30 +2554,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { tcx.predicates_of(def_id).instantiate(tcx, args) } - - #[instrument(skip(self, body), level = "debug")] - fn typeck_mir(&mut self, body: &Body<'tcx>) { - self.last_span = body.span; - debug!(?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); - } - } } trait NormalizeLocation: fmt::Debug + Copy { diff --git a/compiler/rustc_borrowck/src/type_check/opaque_types.rs b/compiler/rustc_borrowck/src/type_check/opaque_types.rs index 17482cc0cbd22..341c50c37f6db 100644 --- a/compiler/rustc_borrowck/src/type_check/opaque_types.rs +++ b/compiler/rustc_borrowck/src/type_check/opaque_types.rs @@ -2,10 +2,9 @@ use std::iter; use rustc_data_structures::fx::FxIndexMap; use rustc_middle::span_bug; -use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{ self, GenericArgKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, + TypeVisitable, TypeVisitableExt, TypeVisitor, fold_regions, }; use tracing::{debug, trace}; @@ -180,6 +179,7 @@ pub(super) fn take_opaques_and_register_member_constraints<'tcx>( /// // Equivalent to: /// # mod dummy { use super::*; /// type FooReturn<'a, T> = impl Foo<'a>; +/// #[define_opaque(FooReturn)] /// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> { /// (x, y) /// } @@ -266,12 +266,8 @@ impl<'tcx, OP> TypeVisitor> for ConstrainOpaqueTypeRegionVisitor<'t where OP: FnMut(ty::Region<'tcx>), { - fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { - t.super_visit_with(self); - } - fn visit_region(&mut self, r: ty::Region<'tcx>) { - match *r { + match r.kind() { // ignore bound regions, keep visiting ty::ReBound(_, _) => {} _ => (self.op)(r), diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 59e2eee41d3fc..02a41469c97f4 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -10,9 +10,8 @@ use rustc_middle::mir::ConstraintCategory; use rustc_middle::span_bug; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::fold::FnMutDelegate; use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, FnMutDelegate, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::{Span, Symbol, sym}; use tracing::{debug, instrument}; diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index cfac9b36832fd..c11e14d214c42 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -27,11 +27,10 @@ use rustc_hir::lang_items::LangItem; use rustc_index::IndexVec; use rustc_infer::infer::NllRegionVariableOrigin; use rustc_macros::extension; -use rustc_middle::ty::fold::{TypeFoldable, fold_regions}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ self, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, RegionVid, Ty, - TyCtxt, TypeVisitableExt, + TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_span::{ErrorGuaranteed, kw, sym}; @@ -184,6 +183,20 @@ impl<'tcx> DefiningTy<'tcx> { | DefiningTy::GlobalAsm(def_id) => def_id, } } + + /// Returns the args of the `DefiningTy`. These are equivalent to the identity + /// substs of the body, but replaced with region vids. + pub(crate) fn args(&self) -> ty::GenericArgsRef<'tcx> { + match *self { + DefiningTy::Closure(_, args) + | DefiningTy::Coroutine(_, args) + | DefiningTy::CoroutineClosure(_, args) + | DefiningTy::FnDef(_, args) + | DefiningTy::Const(_, args) + | DefiningTy::InlineConst(_, args) => args, + DefiningTy::GlobalAsm(_) => ty::List::empty(), + } + } } #[derive(Debug)] @@ -425,6 +438,10 @@ impl<'tcx> UniversalRegions<'tcx> { } } + pub(crate) fn implicit_region_bound(&self) -> RegionVid { + self.fr_fn_body + } + pub(crate) fn tainted_by_errors(&self) -> Option { self.indices.tainted_by_errors.get() } @@ -896,19 +913,19 @@ impl<'tcx> UniversalRegionIndices<'tcx> { /// if it is a placeholder. Handling placeholders requires access to the /// `MirTypeckRegionConstraints`. fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid { - if let ty::ReVar(..) = *r { - r.as_var() - } else if let ty::ReError(guar) = *r { - self.tainted_by_errors.set(Some(guar)); - // We use the `'static` `RegionVid` because `ReError` doesn't actually exist in the - // `UniversalRegionIndices`. This is fine because 1) it is a fallback only used if - // errors are being emitted and 2) it leaves the happy path unaffected. - self.fr_static - } else { - *self + match r.kind() { + ty::ReVar(..) => r.as_var(), + ty::ReError(guar) => { + self.tainted_by_errors.set(Some(guar)); + // We use the `'static` `RegionVid` because `ReError` doesn't actually exist in the + // `UniversalRegionIndices`. This is fine because 1) it is a fallback only used if + // errors are being emitted and 2) it leaves the happy path unaffected. + self.fr_static + } + _ => *self .indices .get(&r) - .unwrap_or_else(|| bug!("cannot convert `{:?}` to a region vid", r)) + .unwrap_or_else(|| bug!("cannot convert `{:?}` to a region vid", r)), } } diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index b5f4f2efd1f5b..5c1ae90f7298f 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -3,10 +3,6 @@ name = "rustc_builtin_macros" version = "0.0.0" edition = "2024" - -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(llvm_enzyme)'] } - [lib] doctest = false @@ -14,6 +10,7 @@ doctest = false # tidy-alphabetical-start rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 4cac7cb93f581..73be954cefd76 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -75,9 +75,11 @@ builtin_macros_autodiff_mode = unknown Mode: `{$mode}`. Use `Forward` or `Revers builtin_macros_autodiff_mode_activity = {$act} can not be used in {$mode} Mode builtin_macros_autodiff_not_build = this rustc version does not support autodiff builtin_macros_autodiff_number_activities = expected {$expected} activities, but found {$found} +builtin_macros_autodiff_ret_activity = invalid return activity {$act} in {$mode} Mode builtin_macros_autodiff_ty_activity = {$act} can not be used for this type - builtin_macros_autodiff_unknown_activity = did not recognize Activity: `{$act}` + +builtin_macros_autodiff_width = autodiff width must fit u32, but is {$width} builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `enum`s and `union`s .label = not applicable here .label2 = not a `struct`, `enum` or `union` @@ -229,8 +231,6 @@ builtin_macros_format_unused_args = multiple unused formatting arguments builtin_macros_format_use_positional = consider using a positional formatting argument instead -builtin_macros_invalid_crate_attribute = invalid crate attribute - builtin_macros_multiple_default_attrs = multiple `#[default]` attributes .note = only one `#[default]` attribute is needed .label = `#[default]` used here @@ -247,9 +247,9 @@ builtin_macros_multiple_defaults = multiple declared defaults .suggestion = make `{$ident}` default builtin_macros_naked_functions_testing_attribute = - cannot use `#[naked]` with testing attributes + cannot use `#[unsafe(naked)]` with testing attributes .label = function marked with testing attribute here - .naked_attribute = `#[naked]` is incompatible with testing attributes + .naked_attribute = `#[unsafe(naked)]` is incompatible with testing attributes builtin_macros_no_default_variant = `#[derive(Default)]` on enum with no `#[default]` .label = this enum needs a unit variant marked with `#[default]` diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index 78bf2195975d5..ea406e706660d 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -21,15 +21,15 @@ pub(crate) fn expand( // Allow using `#[alloc_error_handler]` on an item statement // FIXME - if we get deref patterns, use them to reduce duplication here - let (item, is_stmt, sig_span) = if let Annotatable::Item(item) = &item + let (item, ident, is_stmt, sig_span) = if let Annotatable::Item(item) = &item && let ItemKind::Fn(fn_kind) = &item.kind { - (item, false, ecx.with_def_site_ctxt(fn_kind.sig.span)) + (item, fn_kind.ident, false, ecx.with_def_site_ctxt(fn_kind.sig.span)) } else if let Annotatable::Stmt(stmt) = &item && let StmtKind::Item(item) = &stmt.kind && let ItemKind::Fn(fn_kind) = &item.kind { - (item, true, ecx.with_def_site_ctxt(fn_kind.sig.span)) + (item, fn_kind.ident, true, ecx.with_def_site_ctxt(fn_kind.sig.span)) } else { ecx.dcx().emit_err(errors::AllocErrorMustBeFn { span: item.span() }); return vec![orig_item]; @@ -39,7 +39,7 @@ pub(crate) fn expand( let span = ecx.with_def_site_ctxt(item.span); // Generate item statements for the allocator methods. - let stmts = thin_vec![generate_handler(ecx, item.ident, span, sig_span)]; + let stmts = thin_vec![generate_handler(ecx, ident, span, sig_span)]; // Generate anonymous constant serving as container for the allocator methods. let const_ty = ecx.ty(sig_span, TyKind::Tup(ThinVec::new())); @@ -85,13 +85,15 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span let kind = ItemKind::Fn(Box::new(Fn { defaultness: ast::Defaultness::Final, sig, + ident: Ident::from_str_and_span("__rg_oom", span), generics: Generics::default(), contract: None, body, + define_opaque: None, })); let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)]; - let item = cx.item(span, Ident::from_str_and_span("__rg_oom", span), attrs, kind); + let item = cx.item(span, attrs, kind); cx.stmt_item(sig_span, item) } diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index eb5b345e49ecd..62ee71fecc273 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -10,7 +10,7 @@ use rustc_index::bit_set::GrowableBitSet; use rustc_parse::exp; use rustc_parse::parser::{ExpKeywordPair, Parser}; use rustc_session::lint; -use rustc_span::{ErrorGuaranteed, Ident, InnerSpan, Span, Symbol, kw}; +use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw}; use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; use {rustc_ast as ast, rustc_parse_format as parse}; @@ -18,7 +18,31 @@ use {rustc_ast as ast, rustc_parse_format as parse}; use crate::errors; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; -pub struct AsmArgs { +/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise +/// not validated at all. +pub struct AsmArg { + pub kind: AsmArgKind, + pub span: Span, +} + +pub enum AsmArgKind { + Template(P), + Operand(Option, ast::InlineAsmOperand), + Options(Vec), + ClobberAbi(Vec<(Symbol, Span)>), +} + +pub struct AsmOption { + pub symbol: Symbol, + pub span: Span, + // A bitset, with only the bit for this option's symbol set. + pub options: ast::InlineAsmOptions, + // Used when suggesting to remove an option. + pub span_with_comma: Span, +} + +/// Validated assembly arguments, ready for macro expansion. +struct ValidatedAsmArgs { pub templates: Vec>, pub operands: Vec<(ast::InlineAsmOperand, Span)>, named_args: FxIndexMap, @@ -59,41 +83,95 @@ fn eat_operand_keyword<'a>( } } -fn parse_args<'a>( - ecx: &ExtCtxt<'a>, - sp: Span, - tts: TokenStream, +fn parse_asm_operand<'a>( + p: &mut Parser<'a>, asm_macro: AsmMacro, -) -> PResult<'a, AsmArgs> { - let mut p = ecx.new_parser_from_tts(tts); - parse_asm_args(&mut p, sp, asm_macro) +) -> PResult<'a, Option> { + let dcx = p.dcx(); + + Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + ast::InlineAsmOperand::In { reg, expr } + } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { + let reg = parse_reg(p)?; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: false } + } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { + let reg = parse_reg(p)?; + let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::Out { reg, expr, late: true } + } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + if p.eat(exp!(FatArrow)) { + let out_expr = + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } + } else { + ast::InlineAsmOperand::InOut { reg, expr, late: false } + } + } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { + let reg = parse_reg(p)?; + if p.eat_keyword(exp!(Underscore)) { + let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); + return Err(err); + } + let expr = p.parse_expr()?; + if p.eat(exp!(FatArrow)) { + let out_expr = + if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; + ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } + } else { + ast::InlineAsmOperand::InOut { reg, expr, late: true } + } + } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { + let block = p.parse_block()?; + ast::InlineAsmOperand::Label { block } + } else if p.eat_keyword(exp!(Const)) { + let anon_const = p.parse_expr_anon_const()?; + ast::InlineAsmOperand::Const { anon_const } + } else if p.eat_keyword(exp!(Sym)) { + let expr = p.parse_expr()?; + let ast::ExprKind::Path(qself, path) = &expr.kind else { + let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); + return Err(err); + }; + let sym = + ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() }; + ast::InlineAsmOperand::Sym { sym } + } else { + return Ok(None); + })) } -// Primarily public for rustfmt consumption. -// Internal consumers should continue to leverage `expand_asm`/`expand__global_asm` +// Public for rustfmt. pub fn parse_asm_args<'a>( p: &mut Parser<'a>, sp: Span, asm_macro: AsmMacro, -) -> PResult<'a, AsmArgs> { +) -> PResult<'a, Vec> { let dcx = p.dcx(); if p.token == token::Eof { return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp })); } + let mut args = Vec::new(); + let first_template = p.parse_expr()?; - let mut args = AsmArgs { - templates: vec![first_template], - operands: vec![], - named_args: Default::default(), - reg_args: Default::default(), - clobber_abis: Vec::new(), - options: ast::InlineAsmOptions::empty(), - options_spans: vec![], - }; + args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) }); let mut allow_templates = true; + while p.token != token::Eof { if !p.eat(exp!(Comma)) { if allow_templates { @@ -104,27 +182,39 @@ pub fn parse_asm_args<'a>( return Err(p.expect(exp!(Comma)).err().unwrap()); } } + + // Accept trailing commas. if p.token == token::Eof { break; - } // accept trailing commas + } - // Parse clobber_abi + let span_start = p.token.span; + + // Parse `clobber_abi`. if p.eat_keyword(exp!(ClobberAbi)) { - parse_clobber_abi(p, &mut args)?; allow_templates = false; + + args.push(AsmArg { + kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?), + span: span_start.to(p.prev_token.span), + }); + continue; } - // Parse options + // Parse `options`. if p.eat_keyword(exp!(Options)) { - parse_options(p, &mut args, asm_macro)?; allow_templates = false; + + args.push(AsmArg { + kind: AsmArgKind::Options(parse_options(p, asm_macro)?), + span: span_start.to(p.prev_token.span), + }); + continue; } - let span_start = p.token.span; - - // Parse operand names + // Parse operand names. let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) { let (ident, _) = p.token.ident().unwrap(); p.bump(); @@ -135,69 +225,13 @@ pub fn parse_asm_args<'a>( None }; - let mut explicit_reg = false; - let op = if eat_operand_keyword(p, exp!(In), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - ast::InlineAsmOperand::In { reg, expr } - } else if eat_operand_keyword(p, exp!(Out), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; - let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::Out { reg, expr, late: false } - } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; - let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::Out { reg, expr, late: true } - } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - if p.eat(exp!(FatArrow)) { - let out_expr = - if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false } - } else { - ast::InlineAsmOperand::InOut { reg, expr, late: false } - } - } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? { - let reg = parse_reg(p, &mut explicit_reg)?; - if p.eat_keyword(exp!(Underscore)) { - let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); - return Err(err); - } - let expr = p.parse_expr()?; - if p.eat(exp!(FatArrow)) { - let out_expr = - if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) }; - ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true } - } else { - ast::InlineAsmOperand::InOut { reg, expr, late: true } - } - } else if eat_operand_keyword(p, exp!(Label), asm_macro)? { - let block = p.parse_block()?; - ast::InlineAsmOperand::Label { block } - } else if p.eat_keyword(exp!(Const)) { - let anon_const = p.parse_expr_anon_const()?; - ast::InlineAsmOperand::Const { anon_const } - } else if p.eat_keyword(exp!(Sym)) { - let expr = p.parse_expr()?; - let ast::ExprKind::Path(qself, path) = &expr.kind else { - let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span }); - return Err(err); - }; - let sym = ast::InlineAsmSym { - id: ast::DUMMY_NODE_ID, - qself: qself.clone(), - path: path.clone(), - }; - ast::InlineAsmOperand::Sym { sym } + if let Some(op) = parse_asm_operand(p, asm_macro)? { + allow_templates = false; + + args.push(AsmArg { + span: span_start.to(p.prev_token.span), + kind: AsmArgKind::Operand(name, op), + }); } else if allow_templates { let template = p.parse_expr()?; // If it can't possibly expand to a string, provide diagnostics here to include other @@ -217,55 +251,164 @@ pub fn parse_asm_args<'a>( return Err(err); } } - args.templates.push(template); - continue; + + args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) }); } else { p.unexpected_any()? - }; + } + } + + Ok(args) +} - allow_templates = false; - let span = span_start.to(p.prev_token.span); - let slot = args.operands.len(); - args.operands.push((op, span)); - - // Validate the order of named, positional & explicit register operands and - // clobber_abi/options. We do this at the end once we have the full span - // of the argument available. - if explicit_reg { - if name.is_some() { - dcx.emit_err(errors::AsmExplicitRegisterName { span }); +fn parse_args<'a>( + ecx: &ExtCtxt<'a>, + sp: Span, + tts: TokenStream, + asm_macro: AsmMacro, +) -> PResult<'a, ValidatedAsmArgs> { + let args = parse_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?; + validate_asm_args(ecx, asm_macro, args) +} + +fn validate_asm_args<'a>( + ecx: &ExtCtxt<'a>, + asm_macro: AsmMacro, + args: Vec, +) -> PResult<'a, ValidatedAsmArgs> { + let dcx = ecx.dcx(); + + let mut validated = ValidatedAsmArgs { + templates: vec![], + operands: vec![], + named_args: Default::default(), + reg_args: Default::default(), + clobber_abis: Vec::new(), + options: ast::InlineAsmOptions::empty(), + options_spans: vec![], + }; + + let mut allow_templates = true; + + for arg in args { + match arg.kind { + AsmArgKind::Template(template) => { + // The error for the first template is delayed. + if !allow_templates { + match template.kind { + ast::ExprKind::Lit(token_lit) + if matches!( + token_lit.kind, + token::LitKind::Str | token::LitKind::StrRaw(_) + ) => {} + ast::ExprKind::MacCall(..) => {} + _ => { + let err = dcx.create_err(errors::AsmExpectedOther { + span: template.span, + is_inline_asm: matches!(asm_macro, AsmMacro::Asm), + }); + return Err(err); + } + } + } + + validated.templates.push(template); } - args.reg_args.insert(slot); - } else if let Some(name) = name { - if let Some(&prev) = args.named_args.get(&name) { - dcx.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 }); - continue; + AsmArgKind::Operand(name, op) => { + allow_templates = false; + + let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_))); + let span = arg.span; + let slot = validated.operands.len(); + validated.operands.push((op, span)); + + // Validate the order of named, positional & explicit register operands and + // clobber_abi/options. We do this at the end once we have the full span + // of the argument available. + + if explicit_reg { + if name.is_some() { + dcx.emit_err(errors::AsmExplicitRegisterName { span }); + } + validated.reg_args.insert(slot); + } else if let Some(name) = name { + if let Some(&prev) = validated.named_args.get(&name) { + dcx.emit_err(errors::AsmDuplicateArg { + span, + name, + prev: validated.operands[prev].1, + }); + continue; + } + validated.named_args.insert(name, slot); + } else if !validated.named_args.is_empty() || !validated.reg_args.is_empty() { + let named = + validated.named_args.values().map(|p| validated.operands[*p].1).collect(); + let explicit = + validated.reg_args.iter().map(|p| validated.operands[p].1).collect(); + + dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit }); + } } - args.named_args.insert(name, slot); - } else if !args.named_args.is_empty() || !args.reg_args.is_empty() { - let named = args.named_args.values().map(|p| args.operands[*p].1).collect(); - let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect(); + AsmArgKind::Options(new_options) => { + allow_templates = false; + + for asm_option in new_options { + let AsmOption { span, symbol, span_with_comma, options } = asm_option; - dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit }); + if !asm_macro.is_supported_option(options) { + // Tool-only output. + dcx.emit_err(errors::AsmUnsupportedOption { + span, + symbol, + span_with_comma, + macro_name: asm_macro.macro_name(), + }); + } else if validated.options.contains(options) { + // Tool-only output. + dcx.emit_err(errors::AsmOptAlreadyprovided { + span, + symbol, + span_with_comma, + }); + } else { + validated.options |= asm_option.options; + } + } + + validated.options_spans.push(arg.span); + } + AsmArgKind::ClobberAbi(new_abis) => { + allow_templates = false; + + match &new_abis[..] { + // This should have errored above during parsing. + [] => unreachable!(), + [(abi, _span)] => validated.clobber_abis.push((*abi, arg.span)), + _ => validated.clobber_abis.extend(new_abis), + } + } } } - if args.options.contains(ast::InlineAsmOptions::NOMEM) - && args.options.contains(ast::InlineAsmOptions::READONLY) + if validated.options.contains(ast::InlineAsmOptions::NOMEM) + && validated.options.contains(ast::InlineAsmOptions::READONLY) { - let spans = args.options_spans.clone(); + let spans = validated.options_spans.clone(); dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" }); } - if args.options.contains(ast::InlineAsmOptions::PURE) - && args.options.contains(ast::InlineAsmOptions::NORETURN) + if validated.options.contains(ast::InlineAsmOptions::PURE) + && validated.options.contains(ast::InlineAsmOptions::NORETURN) { - let spans = args.options_spans.clone(); + let spans = validated.options_spans.clone(); dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" }); } - if args.options.contains(ast::InlineAsmOptions::PURE) - && !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) + if validated.options.contains(ast::InlineAsmOptions::PURE) + && !validated + .options + .intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) { - let spans = args.options_spans.clone(); + let spans = validated.options_spans.clone(); dcx.emit_err(errors::AsmPureCombine { spans }); } @@ -273,7 +416,7 @@ pub fn parse_asm_args<'a>( let mut outputs_sp = vec![]; let mut regclass_outputs = vec![]; let mut labels_sp = vec![]; - for (op, op_sp) in &args.operands { + for (op, op_sp) in &validated.operands { match op { ast::InlineAsmOperand::Out { reg, expr, .. } | ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => { @@ -296,10 +439,10 @@ pub fn parse_asm_args<'a>( _ => {} } } - if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { - dcx.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() }); + if validated.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { + dcx.emit_err(errors::AsmPureNoOutput { spans: validated.options_spans.clone() }); } - if args.options.contains(ast::InlineAsmOptions::NORETURN) + if validated.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() && labels_sp.is_empty() { @@ -307,15 +450,15 @@ pub fn parse_asm_args<'a>( // Bail out now since this is likely to confuse MIR return Err(err); } - if args.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() { + if validated.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() { dcx.emit_err(errors::AsmMayUnwind { labels_sp }); } - if !args.clobber_abis.is_empty() { + if !validated.clobber_abis.is_empty() { match asm_macro { AsmMacro::GlobalAsm | AsmMacro::NakedAsm => { let err = dcx.create_err(errors::AsmUnsupportedClobberAbi { - spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(), + spans: validated.clobber_abis.iter().map(|(_, span)| *span).collect(), macro_name: asm_macro.macro_name(), }); @@ -326,71 +469,21 @@ pub fn parse_asm_args<'a>( if !regclass_outputs.is_empty() { dcx.emit_err(errors::AsmClobberNoReg { spans: regclass_outputs, - clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(), + clobbers: validated.clobber_abis.iter().map(|(_, span)| *span).collect(), }); } } } } - Ok(args) + Ok(validated) } -/// Report a duplicate option error. -/// -/// This function must be called immediately after the option token is parsed. -/// Otherwise, the suggestion will be incorrect. -fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) { - // Tool-only output - let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span }; - p.dcx().emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); -} - -/// Report an invalid option error. -/// -/// This function must be called immediately after the option token is parsed. -/// Otherwise, the suggestion will be incorrect. -fn err_unsupported_option(p: &Parser<'_>, asm_macro: AsmMacro, symbol: Symbol, span: Span) { - // Tool-only output - let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span }; - p.dcx().emit_err(errors::AsmUnsupportedOption { - span, - symbol, - full_span, - macro_name: asm_macro.macro_name(), - }); -} - -/// Try to set the provided option in the provided `AsmArgs`. -/// If it is already set, report a duplicate option error. -/// -/// This function must be called immediately after the option token is parsed. -/// Otherwise, the error will not point to the correct spot. -fn try_set_option<'a>( - p: &Parser<'a>, - args: &mut AsmArgs, - asm_macro: AsmMacro, - symbol: Symbol, - option: ast::InlineAsmOptions, -) { - if !asm_macro.is_supported_option(option) { - err_unsupported_option(p, asm_macro, symbol, p.prev_token.span); - } else if args.options.contains(option) { - err_duplicate_option(p, symbol, p.prev_token.span); - } else { - args.options |= option; - } -} - -fn parse_options<'a>( - p: &mut Parser<'a>, - args: &mut AsmArgs, - asm_macro: AsmMacro, -) -> PResult<'a, ()> { - let span_start = p.prev_token.span; - +fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec> { p.expect(exp!(OpenParen))?; + let mut asm_options = Vec::new(); + while !p.eat(exp!(CloseParen)) { const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [ (exp!(Pure), ast::InlineAsmOptions::PURE), @@ -405,38 +498,38 @@ fn parse_options<'a>( ]; 'blk: { - for (exp, option) in OPTIONS { - let kw_matched = if asm_macro.is_supported_option(option) { + for (exp, options) in OPTIONS { + // Gives a more accurate list of expected next tokens. + let kw_matched = if asm_macro.is_supported_option(options) { p.eat_keyword(exp) } else { p.eat_keyword_noexpect(exp.kw) }; if kw_matched { - try_set_option(p, args, asm_macro, exp.kw, option); + let span = p.prev_token.span; + let span_with_comma = + if p.token == token::Comma { span.to(p.token.span) } else { span }; + + asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma }); break 'blk; } } - return p.unexpected(); + return p.unexpected_any(); } - // Allow trailing commas + // Allow trailing commas. if p.eat(exp!(CloseParen)) { break; } p.expect(exp!(Comma))?; } - let new_span = span_start.to(p.prev_token.span); - args.options_spans.push(new_span); - - Ok(()) + Ok(asm_options) } -fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> { - let span_start = p.prev_token.span; - +fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> { p.expect(exp!(OpenParen))?; if p.eat(exp!(CloseParen)) { @@ -462,31 +555,14 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, p.expect(exp!(Comma))?; } - let full_span = span_start.to(p.prev_token.span); - - match &new_abis[..] { - // should have errored above during parsing - [] => unreachable!(), - [(abi, _span)] => args.clobber_abis.push((*abi, full_span)), - abis => { - for (abi, span) in abis { - args.clobber_abis.push((*abi, *span)); - } - } - } - - Ok(()) + Ok(new_abis) } -fn parse_reg<'a>( - p: &mut Parser<'a>, - explicit_reg: &mut bool, -) -> PResult<'a, ast::InlineAsmRegOrRegClass> { +fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> { p.expect(exp!(OpenParen))?; let result = match p.token.uninterpolate().kind { token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name), token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => { - *explicit_reg = true; ast::InlineAsmRegOrRegClass::Reg(symbol) } _ => { @@ -503,7 +579,7 @@ fn parse_reg<'a>( fn expand_preparsed_asm( ecx: &mut ExtCtxt<'_>, asm_macro: AsmMacro, - args: AsmArgs, + args: ValidatedAsmArgs, ) -> ExpandResult, ()> { let mut template = vec![]; // Register operands are implicitly used since they are not allowed to be @@ -888,7 +964,6 @@ pub(super) fn expand_global_asm<'cx>( }; match mac { Ok(inline_asm) => MacEager::items(smallvec![P(ast::Item { - ident: Ident::empty(), attrs: ast::AttrVec::new(), id: ast::DUMMY_NODE_ID, kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)), diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index bb9dc651cec25..ea7248ca5393a 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -112,7 +112,6 @@ impl<'cx, 'a> Context<'cx, 'a> { self.span, self.cx.item( self.span, - Ident::empty(), thin_vec![self.cx.attr_nested_word(sym::allow, sym::unused_imports, self.span)], ItemKind::Use(UseTree { prefix: self.cx.path(self.span, self.cx.std_path(&[sym::asserting])), @@ -297,6 +296,7 @@ impl<'cx, 'a> Context<'cx, 'a> { | ExprKind::AssignOp(_, _, _) | ExprKind::Gen(_, _, _, _) | ExprKind::Await(_, _) + | ExprKind::Use(_, _) | ExprKind::Block(_, _) | ExprKind::Break(_, _) | ExprKind::Closure(_) diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 6d9c35756574e..1ff4fc6aaab22 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -8,15 +8,16 @@ mod llvm_enzyme { use std::string::String; use rustc_ast::expand::autodiff_attrs::{ - AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ty_for_activity, + AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity, + valid_ty_for_activity, }; use rustc_ast::ptr::P; - use rustc_ast::token::{Token, TokenKind}; + use rustc_ast::token::{Lit, LitKind, Token, TokenKind}; use rustc_ast::tokenstream::*; use rustc_ast::visit::AssocCtxt::*; use rustc_ast::{ - self as ast, AssocItemKind, BindingMode, FnRetTy, FnSig, Generics, ItemKind, MetaItemInner, - PatKind, TyKind, + self as ast, AssocItemKind, BindingMode, ExprKind, FnRetTy, FnSig, Generics, ItemKind, + MetaItemInner, PatKind, QSelf, TyKind, Visibility, }; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, Symbol, kw, sym}; @@ -25,6 +26,16 @@ mod llvm_enzyme { use crate::errors; + pub(crate) fn outer_normal_attr( + kind: &P, + id: rustc_ast::AttrId, + span: Span, + ) -> rustc_ast::Attribute { + let style = rustc_ast::AttrStyle::Outer; + let kind = rustc_ast::AttrKind::Normal(kind.clone()); + rustc_ast::Attribute { kind, id, style, span } + } + // If we have a default `()` return type or explicitley `()` return type, // then we often can skip doing some work. fn has_ret(ty: &FnRetTy) -> bool { @@ -34,6 +45,16 @@ mod llvm_enzyme { } } fn first_ident(x: &MetaItemInner) -> rustc_span::Ident { + if let Some(l) = x.lit() { + match l.kind { + ast::LitKind::Int(val, _) => { + // get an Ident from a lit + return rustc_span::Ident::from_str(val.get().to_string().as_str()); + } + _ => {} + } + } + let segments = &x.meta_item().unwrap().path.segments; assert!(segments.len() == 1); segments[0].ident @@ -43,6 +64,24 @@ mod llvm_enzyme { first_ident(x).name.to_string() } + fn width(x: &MetaItemInner) -> Option { + let lit = x.lit()?; + match lit.kind { + ast::LitKind::Int(x, _) => Some(x.get()), + _ => return None, + } + } + + // Get information about the function the macro is applied to + fn extract_item_info(iitem: &P) -> Option<(Visibility, FnSig, Ident, Generics)> { + match &iitem.kind { + ItemKind::Fn(box ast::Fn { sig, ident, generics, .. }) => { + Some((iitem.vis.clone(), sig.clone(), ident.clone(), generics.clone())) + } + _ => None, + } + } + pub(crate) fn from_ast( ecx: &mut ExtCtxt<'_>, meta_item: &ThinVec, @@ -54,9 +93,32 @@ mod llvm_enzyme { dcx.emit_err(errors::AutoDiffInvalidMode { span: meta_item[1].span(), mode }); return AutoDiffAttrs::error(); }; + + // Now we check, whether the user wants autodiff in batch/vector mode, or scalar mode. + // If he doesn't specify an integer (=width), we default to scalar mode, thus width=1. + let mut first_activity = 2; + + let width = if let [_, _, x, ..] = &meta_item[..] + && let Some(x) = width(x) + { + first_activity = 3; + match x.try_into() { + Ok(x) => x, + Err(_) => { + dcx.emit_err(errors::AutoDiffInvalidWidth { + span: meta_item[2].span(), + width: x, + }); + return AutoDiffAttrs::error(); + } + } + } else { + 1 + }; + let mut activities: Vec = vec![]; let mut errors = false; - for x in &meta_item[2..] { + for x in &meta_item[first_activity..] { let activity_str = name(&x); let res = DiffActivity::from_str(&activity_str); match res { @@ -87,7 +149,20 @@ mod llvm_enzyme { (&DiffActivity::None, activities.as_slice()) }; - AutoDiffAttrs { mode, ret_activity: *ret_activity, input_activity: input_activity.to_vec() } + AutoDiffAttrs { + mode, + width, + ret_activity: *ret_activity, + input_activity: input_activity.to_vec(), + } + } + + fn meta_item_inner_to_ts(t: &MetaItemInner, ts: &mut Vec) { + let comma: Token = Token::new(TokenKind::Comma, Span::default()); + let val = first_ident(t); + let t = Token::from_ast_ident(val); + ts.push(TokenTree::Token(t, Spacing::Joint)); + ts.push(TokenTree::Token(comma.clone(), Spacing::Alone)); } /// We expand the autodiff macro to generate a new placeholder function which passes @@ -134,38 +209,32 @@ mod llvm_enzyme { return vec![item]; } let dcx = ecx.sess.dcx(); - // first get the annotable item: - let (sig, is_impl): (FnSig, bool) = match &item { - Annotatable::Item(iitem) => { - let sig = match &iitem.kind { - ItemKind::Fn(box ast::Fn { sig, .. }) => sig, - _ => { - dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() }); - return vec![item]; - } - }; - (sig.clone(), false) - } - Annotatable::AssocItem(assoc_item, _) => { - let sig = match &assoc_item.kind { - ast::AssocItemKind::Fn(box ast::Fn { sig, .. }) => sig, - _ => { - dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() }); - return vec![item]; - } - }; - (sig.clone(), true) - } - _ => { - dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() }); - return vec![item]; - } + + // first get information about the annotable item: visibility, signature, name and generic + // parameters. + // these will be used to generate the differentiated version of the function + let Some((vis, sig, primal, generics)) = (match &item { + Annotatable::Item(iitem) => extract_item_info(iitem), + Annotatable::Stmt(stmt) => match &stmt.kind { + ast::StmtKind::Item(iitem) => extract_item_info(iitem), + _ => None, + }, + Annotatable::AssocItem(assoc_item, Impl { .. }) => match &assoc_item.kind { + ast::AssocItemKind::Fn(box ast::Fn { sig, ident, generics, .. }) => { + Some((assoc_item.vis.clone(), sig.clone(), ident.clone(), generics.clone())) + } + _ => None, + }, + _ => None, + }) else { + dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() }); + return vec![item]; }; let meta_item_vec: ThinVec = match meta_item.kind { ast::MetaItemKind::List(ref vec) => vec.clone(), _ => { - dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() }); + dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() }); return vec![item]; } }; @@ -173,40 +242,51 @@ mod llvm_enzyme { let has_ret = has_ret(&sig.decl.output); let sig_span = ecx.with_call_site_ctxt(sig.span); - let (vis, primal) = match &item { - Annotatable::Item(iitem) => (iitem.vis.clone(), iitem.ident.clone()), - Annotatable::AssocItem(assoc_item, _) => { - (assoc_item.vis.clone(), assoc_item.ident.clone()) - } - _ => { - dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() }); - return vec![item]; - } - }; - // create TokenStream from vec elemtents: // meta_item doesn't have a .tokens field - let comma: Token = Token::new(TokenKind::Comma, Span::default()); let mut ts: Vec = vec![]; if meta_item_vec.len() < 2 { // At the bare minimum, we need a fnc name and a mode, even for a dummy function with no // input and output args. dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() }); return vec![item]; + } + + meta_item_inner_to_ts(&meta_item_vec[1], &mut ts); + + // Now, if the user gave a width (vector aka batch-mode ad), then we copy it. + // If it is not given, we default to 1 (scalar mode). + let start_position; + let kind: LitKind = LitKind::Integer; + let symbol; + if meta_item_vec.len() >= 3 + && let Some(width) = width(&meta_item_vec[2]) + { + start_position = 3; + symbol = Symbol::intern(&width.to_string()); } else { - for t in meta_item_vec.clone()[1..].iter() { - let val = first_ident(t); - let t = Token::from_ast_ident(val); - ts.push(TokenTree::Token(t, Spacing::Joint)); - ts.push(TokenTree::Token(comma.clone(), Spacing::Alone)); - } + start_position = 2; + symbol = sym::integer(1); + } + let l: Lit = Lit { kind, symbol, suffix: None }; + let t = Token::new(TokenKind::Literal(l), Span::default()); + let comma = Token::new(TokenKind::Comma, Span::default()); + ts.push(TokenTree::Token(t, Spacing::Joint)); + ts.push(TokenTree::Token(comma.clone(), Spacing::Alone)); + + for t in meta_item_vec.clone()[start_position..].iter() { + meta_item_inner_to_ts(t, &mut ts); } + if !has_ret { // We don't want users to provide a return activity if the function doesn't return anything. // For simplicity, we just add a dummy token to the end of the list. let t = Token::new(TokenKind::Ident(sym::None, false.into()), Span::default()); ts.push(TokenTree::Token(t, Spacing::Joint)); + ts.push(TokenTree::Token(comma, Spacing::Alone)); } + // We remove the last, trailing comma. + ts.pop(); let ts: TokenStream = TokenStream::from_iter(ts); let x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret); @@ -223,30 +303,20 @@ mod llvm_enzyme { .filter(|a| **a == DiffActivity::Active || **a == DiffActivity::ActiveOnly) .count() as u32; let (d_sig, new_args, idents, errored) = gen_enzyme_decl(ecx, &sig, &x, span); - let new_decl_span = d_sig.span; let d_body = gen_enzyme_body( - ecx, - &x, - n_active, - &sig, - &d_sig, - primal, - &new_args, - span, - sig_span, - new_decl_span, - idents, - errored, + ecx, &x, n_active, &sig, &d_sig, primal, &new_args, span, sig_span, idents, errored, + &generics, ); - let d_ident = first_ident(&meta_item_vec[0]); // The first element of it is the name of the function to be generated let asdf = Box::new(ast::Fn { defaultness: ast::Defaultness::Final, sig: d_sig, - generics: Generics::default(), + ident: first_ident(&meta_item_vec[0]), + generics, contract: None, body: Some(d_body), + define_opaque: None, }); let mut rustc_ad_attr = P(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff))); @@ -256,9 +326,9 @@ mod llvm_enzyme { Spacing::Joint, )]; let never_arg = ast::DelimArgs { - dspan: ast::tokenstream::DelimSpan::from_single(span), + dspan: DelimSpan::from_single(span), delim: ast::token::Delimiter::Parenthesis, - tokens: ast::tokenstream::TokenStream::from_iter(ts2), + tokens: TokenStream::from_iter(ts2), }; let inline_item = ast::AttrItem { unsafety: ast::Safety::Default, @@ -268,40 +338,59 @@ mod llvm_enzyme { }; let inline_never_attr = P(ast::NormalAttr { item: inline_item, tokens: None }); let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id(); - let attr: ast::Attribute = ast::Attribute { - kind: ast::AttrKind::Normal(rustc_ad_attr.clone()), - id: new_id, - style: ast::AttrStyle::Outer, - span, - }; + let attr = outer_normal_attr(&rustc_ad_attr, new_id, span); let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id(); - let inline_never: ast::Attribute = ast::Attribute { - kind: ast::AttrKind::Normal(inline_never_attr), - id: new_id, - style: ast::AttrStyle::Outer, - span, - }; + let inline_never = outer_normal_attr(&inline_never_attr, new_id, span); + + // We're avoid duplicating the attributes `#[rustc_autodiff]` and `#[inline(never)]`. + fn same_attribute(attr: &ast::AttrKind, item: &ast::AttrKind) -> bool { + match (attr, item) { + (ast::AttrKind::Normal(a), ast::AttrKind::Normal(b)) => { + let a = &a.item.path; + let b = &b.item.path; + a.segments.len() == b.segments.len() + && a.segments.iter().zip(b.segments.iter()).all(|(a, b)| a.ident == b.ident) + } + _ => false, + } + } // Don't add it multiple times: let orig_annotatable: Annotatable = match item { Annotatable::Item(ref mut iitem) => { - if !iitem.attrs.iter().any(|a| a.id == attr.id) { - iitem.attrs.push(attr.clone()); + if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) { + iitem.attrs.push(attr); } - if !iitem.attrs.iter().any(|a| a.id == inline_never.id) { + if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind)) { iitem.attrs.push(inline_never.clone()); } Annotatable::Item(iitem.clone()) } - Annotatable::AssocItem(ref mut assoc_item, i @ Impl) => { - if !assoc_item.attrs.iter().any(|a| a.id == attr.id) { - assoc_item.attrs.push(attr.clone()); + Annotatable::AssocItem(ref mut assoc_item, i @ Impl { .. }) => { + if !assoc_item.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) { + assoc_item.attrs.push(attr); } - if !assoc_item.attrs.iter().any(|a| a.id == inline_never.id) { + if !assoc_item.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind)) { assoc_item.attrs.push(inline_never.clone()); } Annotatable::AssocItem(assoc_item.clone(), i) } + Annotatable::Stmt(ref mut stmt) => { + match stmt.kind { + ast::StmtKind::Item(ref mut iitem) => { + if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) { + iitem.attrs.push(attr); + } + if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind)) + { + iitem.attrs.push(inline_never.clone()); + } + } + _ => unreachable!("stmt kind checked previously"), + }; + + Annotatable::Stmt(stmt.clone()) + } _ => { unreachable!("annotatable kind checked previously") } @@ -312,34 +401,40 @@ mod llvm_enzyme { delim: rustc_ast::token::Delimiter::Parenthesis, tokens: ts, }); - let d_attr: ast::Attribute = ast::Attribute { - kind: ast::AttrKind::Normal(rustc_ad_attr.clone()), - id: new_id, - style: ast::AttrStyle::Outer, - span, - }; - let d_annotatable = if is_impl { - let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(asdf); - let d_fn = P(ast::AssocItem { - attrs: thin_vec![d_attr.clone(), inline_never], - id: ast::DUMMY_NODE_ID, - span, - vis, - ident: d_ident, - kind: assoc_item, - tokens: None, - }); - Annotatable::AssocItem(d_fn, Impl) - } else { - let mut d_fn = ecx.item( - span, - d_ident, - thin_vec![d_attr.clone(), inline_never], - ItemKind::Fn(asdf), - ); - d_fn.vis = vis; - Annotatable::Item(d_fn) + let d_attr = outer_normal_attr(&rustc_ad_attr, new_id, span); + let d_annotatable = match &item { + Annotatable::AssocItem(_, _) => { + let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(asdf); + let d_fn = P(ast::AssocItem { + attrs: thin_vec![d_attr, inline_never], + id: ast::DUMMY_NODE_ID, + span, + vis, + kind: assoc_item, + tokens: None, + }); + Annotatable::AssocItem(d_fn, Impl { of_trait: false }) + } + Annotatable::Item(_) => { + let mut d_fn = ecx.item(span, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf)); + d_fn.vis = vis; + + Annotatable::Item(d_fn) + } + Annotatable::Stmt(_) => { + let mut d_fn = ecx.item(span, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf)); + d_fn.vis = vis; + + Annotatable::Stmt(P(ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Item(d_fn), + span, + })) + } + _ => { + unreachable!("item kind checked previously") + } }; return vec![orig_annotatable, d_annotatable]; @@ -363,30 +458,28 @@ mod llvm_enzyme { ty } - /// We only want this function to type-check, since we will replace the body - /// later on llvm level. Using `loop {}` does not cover all return types anymore, - /// so instead we build something that should pass. We also add a inline_asm - /// line, as one more barrier for rustc to prevent inlining of this function. - /// FIXME(ZuseZ4): We still have cases of incorrect inlining across modules, see - /// , so this isn't sufficient. - /// It also triggers an Enzyme crash if we due to a bug ever try to differentiate - /// this function (which should never happen, since it is only a placeholder). - /// Finally, we also add back_box usages of all input arguments, to prevent rustc - /// from optimizing any arguments away. - fn gen_enzyme_body( + // Will generate a body of the type: + // ``` + // { + // unsafe { + // asm!("NOP"); + // } + // ::core::hint::black_box(primal(args)); + // ::core::hint::black_box((args, ret)); + // + // } + // ``` + fn init_body_helper( ecx: &ExtCtxt<'_>, - x: &AutoDiffAttrs, - n_active: u32, - sig: &ast::FnSig, - d_sig: &ast::FnSig, + span: Span, primal: Ident, new_names: &[String], - span: Span, sig_span: Span, new_decl_span: Span, - idents: Vec, + idents: &[Ident], errored: bool, - ) -> P { + generics: &Generics, + ) -> (P, P, P, P) { let blackbox_path = ecx.std_path(&[sym::hint, sym::black_box]); let noop = ast::InlineAsm { asm_macro: ast::AsmMacro::Asm, @@ -405,11 +498,10 @@ mod llvm_enzyme { tokens: None, rules: unsf, span, - could_be_bare_literal: false, }; let unsf_expr = ecx.expr_block(P(unsf_block)); let blackbox_call_expr = ecx.expr_path(ecx.path(span, blackbox_path)); - let primal_call = gen_primal_call(ecx, span, primal, idents); + let primal_call = gen_primal_call(ecx, span, primal, idents, generics); let black_box_primal_call = ecx.expr_call( new_decl_span, blackbox_call_expr.clone(), @@ -435,18 +527,67 @@ mod llvm_enzyme { } body.stmts.push(ecx.stmt_semi(black_box_remaining_args)); + (body, primal_call, black_box_primal_call, blackbox_call_expr) + } + + /// We only want this function to type-check, since we will replace the body + /// later on llvm level. Using `loop {}` does not cover all return types anymore, + /// so instead we manually build something that should pass the type checker. + /// We also add a inline_asm line, as one more barrier for rustc to prevent inlining + /// or const propagation. inline_asm will also triggers an Enzyme crash if due to another + /// bug would ever try to accidentially differentiate this placeholder function body. + /// Finally, we also add back_box usages of all input arguments, to prevent rustc + /// from optimizing any arguments away. + fn gen_enzyme_body( + ecx: &ExtCtxt<'_>, + x: &AutoDiffAttrs, + n_active: u32, + sig: &ast::FnSig, + d_sig: &ast::FnSig, + primal: Ident, + new_names: &[String], + span: Span, + sig_span: Span, + idents: Vec, + errored: bool, + generics: &Generics, + ) -> P { + let new_decl_span = d_sig.span; + + // Just adding some default inline-asm and black_box usages to prevent early inlining + // and optimizations which alter the function signature. + // + // The bb_primal_call is the black_box call of the primal function. We keep it around, + // since it has the convenient property of returning the type of the primal function, + // Remember, we only care to match types here. + // No matter which return we pick, we always wrap it into a std::hint::black_box call, + // to prevent rustc from propagating it into the caller. + let (mut body, primal_call, bb_primal_call, bb_call_expr) = init_body_helper( + ecx, + span, + primal, + new_names, + sig_span, + new_decl_span, + &idents, + errored, + generics, + ); + if !has_ret(&d_sig.decl.output) { // there is no return type that we have to match, () works fine. return body; } + // Everything from here onwards just tries to fullfil the return type. Fun! + // having an active-only return means we'll drop the original return type. // So that can be treated identical to not having one in the first place. let primal_ret = has_ret(&sig.decl.output) && !x.has_active_only_ret(); if primal_ret && n_active == 0 && x.mode.is_rev() { // We only have the primal ret. - body.stmts.push(ecx.stmt_expr(black_box_primal_call.clone())); + body.stmts.push(ecx.stmt_expr(bb_primal_call)); return body; } @@ -459,99 +600,70 @@ mod llvm_enzyme { } }; let arg = ty.kind.is_simple_path().unwrap(); - let sl: Vec = vec![arg, kw::Default]; - let tmp = ecx.def_site_path(&sl); + let tmp = ecx.def_site_path(&[arg, kw::Default]); let default_call_expr = ecx.expr_path(ecx.path(span, tmp)); let default_call_expr = ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]); body.stmts.push(ecx.stmt_expr(default_call_expr)); return body; } - let mut exprs = ThinVec::>::new(); - if primal_ret { - // We have both primal ret and active floats. - // primal ret is first, by construction. - exprs.push(primal_call.clone()); - } - - // Now construct default placeholder for each active float. - // Is there something nicer than f32::default() and f64::default()? + let mut exprs: P = primal_call; let d_ret_ty = match d_sig.decl.output { FnRetTy::Ty(ref ty) => ty.clone(), FnRetTy::Default(span) => { panic!("Did not expect Default ret ty: {:?}", span); } }; - let mut d_ret_ty = match d_ret_ty.kind.clone() { - TyKind::Tup(ref tys) => tys.clone(), - TyKind::Path(_, rustc_ast::Path { segments, .. }) => { - if let [segment] = &segments[..] - && segment.args.is_none() - { - let id = vec![segments[0].ident]; - let kind = TyKind::Path(None, ecx.path(span, id)); - let ty = P(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span, tokens: None }); - thin_vec![ty] - } else { - panic!("Expected tuple or simple path return type"); - } - } - _ => { - // We messed up construction of d_sig - panic!("Did not expect non-tuple ret ty: {:?}", d_ret_ty); - } - }; - - if x.mode.is_fwd() && x.ret_activity == DiffActivity::Dual { - assert!(d_ret_ty.len() == 2); - // both should be identical, by construction - let arg = d_ret_ty[0].kind.is_simple_path().unwrap(); - let arg2 = d_ret_ty[1].kind.is_simple_path().unwrap(); - assert!(arg == arg2); - let sl: Vec = vec![arg, kw::Default]; - let tmp = ecx.def_site_path(&sl); - let default_call_expr = ecx.expr_path(ecx.path(span, tmp)); - let default_call_expr = ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]); - exprs.push(default_call_expr); - } else if x.mode.is_rev() { - if primal_ret { - // We have extra handling above for the primal ret - d_ret_ty = d_ret_ty[1..].to_vec().into(); - } - - for arg in d_ret_ty.iter() { - let arg = arg.kind.is_simple_path().unwrap(); - let sl: Vec = vec![arg, kw::Default]; - let tmp = ecx.def_site_path(&sl); - let default_call_expr = ecx.expr_path(ecx.path(span, tmp)); + if x.mode.is_fwd() { + // Fwd mode is easy. If the return activity is Const, we support arbitrary types. + // Otherwise, we only support a scalar, a pair of scalars, or an array of scalars. + // We checked that (on a best-effort base) in the preceding gen_enzyme_decl function. + // In all three cases, we can return `std::hint::black_box(::default())`. + if x.ret_activity == DiffActivity::Const { + // Here we call the primal function, since our dummy function has the same return + // type due to the Const return activity. + exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]); + } else { + let q = QSelf { ty: d_ret_ty, path_span: span, position: 0 }; + let y = + ExprKind::Path(Some(P(q)), ecx.path_ident(span, Ident::from_str("default"))); + let default_call_expr = ecx.expr(span, y); let default_call_expr = ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]); - exprs.push(default_call_expr); - } - } - - let ret: P; - match &exprs[..] { - [] => { - assert!(!has_ret(&d_sig.decl.output)); - // We don't have to match the return type. - return body; + exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![default_call_expr]); } - [arg] => { - ret = ecx.expr_call( - new_decl_span, - blackbox_call_expr.clone(), - thin_vec![arg.clone()], - ); - } - args => { - let ret_tuple: P = ecx.expr_tuple(span, args.into()); - ret = - ecx.expr_call(new_decl_span, blackbox_call_expr.clone(), thin_vec![ret_tuple]); + } else if x.mode.is_rev() { + if x.width == 1 { + // We either have `-> ArbitraryType` or `-> (ArbitraryType, repeated_float_scalars)`. + match d_ret_ty.kind { + TyKind::Tup(ref args) => { + // We have a tuple return type. We need to create a tuple of the same size + // and fill it with default values. + let mut exprs2 = thin_vec![exprs]; + for arg in args.iter().skip(1) { + let arg = arg.kind.is_simple_path().unwrap(); + let tmp = ecx.def_site_path(&[arg, kw::Default]); + let default_call_expr = ecx.expr_path(ecx.path(span, tmp)); + let default_call_expr = + ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]); + exprs2.push(default_call_expr); + } + exprs = ecx.expr_tuple(new_decl_span, exprs2); + } + _ => { + // Interestingly, even the `-> ArbitraryType` case + // ends up getting matched and handled correctly above, + // so we don't have to handle any other case for now. + panic!("Unsupported return type: {:?}", d_ret_ty); + } + } } + exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]); + } else { + unreachable!("Unsupported mode: {:?}", x.mode); } - assert!(has_ret(&d_sig.decl.output)); - body.stmts.push(ecx.stmt_expr(ret)); + + body.stmts.push(ecx.stmt_expr(exprs)); body } @@ -560,18 +672,64 @@ mod llvm_enzyme { ecx: &ExtCtxt<'_>, span: Span, primal: Ident, - idents: Vec, + idents: &[Ident], + generics: &Generics, ) -> P { let has_self = idents.len() > 0 && idents[0].name == kw::SelfLower; + if has_self { let args: ThinVec<_> = idents[1..].iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect(); let self_expr = ecx.expr_self(span); - ecx.expr_method_call(span, self_expr, primal, args.clone()) + ecx.expr_method_call(span, self_expr, primal, args) } else { let args: ThinVec<_> = idents.iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect(); - let primal_call_expr = ecx.expr_path(ecx.path_ident(span, primal)); + let mut primal_path = ecx.path_ident(span, primal); + + let is_generic = !generics.params.is_empty(); + + match (is_generic, primal_path.segments.last_mut()) { + (true, Some(function_path)) => { + let primal_generic_types = generics + .params + .iter() + .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })); + + let generated_generic_types = primal_generic_types + .map(|type_param| { + let generic_param = TyKind::Path( + None, + ast::Path { + span, + segments: thin_vec![ast::PathSegment { + ident: type_param.ident, + args: None, + id: ast::DUMMY_NODE_ID, + }], + tokens: None, + }, + ); + + ast::AngleBracketedArg::Arg(ast::GenericArg::Type(P(ast::Ty { + id: type_param.id, + span, + kind: generic_param, + tokens: None, + }))) + }) + .collect(); + + function_path.args = + Some(P(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs { + span, + args: generated_generic_types, + }))); + } + _ => {} + } + + let primal_call_expr = ecx.expr_path(primal_path); ecx.expr_call(span, primal_call_expr, args) } } @@ -585,6 +743,8 @@ mod llvm_enzyme { // // Error handling: If the user provides an invalid configuration (incorrect numbers, types, or // both), we emit an error and return the original signature. This allows us to continue parsing. + // FIXME(Sa4dUs): make individual activities' span available so errors + // can point to only the activity instead of the entire attribute fn gen_enzyme_decl( ecx: &ExtCtxt<'_>, sig: &ast::FnSig, @@ -632,10 +792,22 @@ mod llvm_enzyme { errors = true; } } + + if has_ret && !valid_ret_activity(x.mode, x.ret_activity) { + dcx.emit_err(errors::AutoDiffInvalidRetAct { + span, + mode: x.mode.to_string(), + act: x.ret_activity.to_string(), + }); + // We don't set `errors = true` to avoid annoying type errors relative + // to the expanded macro type signature + } + if errors { // This is not the right signature, but we can continue parsing. return (sig.clone(), new_inputs, idents, true); } + let unsafe_activities = x .input_activity .iter() @@ -645,55 +817,71 @@ mod llvm_enzyme { match activity { DiffActivity::Active => { act_ret.push(arg.ty.clone()); + // if width =/= 1, then push [arg.ty; width] to act_ret } DiffActivity::ActiveOnly => { // We will add the active scalar to the return type. // This is handled later. } DiffActivity::Duplicated | DiffActivity::DuplicatedOnly => { - let mut shadow_arg = arg.clone(); - // We += into the shadow in reverse mode. - shadow_arg.ty = P(assure_mut_ref(&arg.ty)); - let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind { - ident.name - } else { - debug!("{:#?}", &shadow_arg.pat); - panic!("not an ident?"); - }; - let name: String = format!("d{}", old_name); - new_inputs.push(name.clone()); - let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span); - shadow_arg.pat = P(ast::Pat { - id: ast::DUMMY_NODE_ID, - kind: PatKind::Ident(BindingMode::NONE, ident, None), - span: shadow_arg.pat.span, - tokens: shadow_arg.pat.tokens.clone(), - }); - d_inputs.push(shadow_arg); + for i in 0..x.width { + let mut shadow_arg = arg.clone(); + // We += into the shadow in reverse mode. + shadow_arg.ty = P(assure_mut_ref(&arg.ty)); + let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind { + ident.name + } else { + debug!("{:#?}", &shadow_arg.pat); + panic!("not an ident?"); + }; + let name: String = format!("d{}_{}", old_name, i); + new_inputs.push(name.clone()); + let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span); + shadow_arg.pat = P(ast::Pat { + id: ast::DUMMY_NODE_ID, + kind: PatKind::Ident(BindingMode::NONE, ident, None), + span: shadow_arg.pat.span, + tokens: shadow_arg.pat.tokens.clone(), + }); + d_inputs.push(shadow_arg.clone()); + } } - DiffActivity::Dual | DiffActivity::DualOnly => { - let mut shadow_arg = arg.clone(); - let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind { - ident.name - } else { - debug!("{:#?}", &shadow_arg.pat); - panic!("not an ident?"); - }; - let name: String = format!("b{}", old_name); - new_inputs.push(name.clone()); - let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span); - shadow_arg.pat = P(ast::Pat { - id: ast::DUMMY_NODE_ID, - kind: PatKind::Ident(BindingMode::NONE, ident, None), - span: shadow_arg.pat.span, - tokens: shadow_arg.pat.tokens.clone(), - }); - d_inputs.push(shadow_arg); + DiffActivity::Dual + | DiffActivity::DualOnly + | DiffActivity::Dualv + | DiffActivity::DualvOnly => { + // the *v variants get lowered to enzyme_dupv and enzyme_dupnoneedv, which cause + // Enzyme to not expect N arguments, but one argument (which is instead larger). + let iterations = + if matches!(activity, DiffActivity::Dualv | DiffActivity::DualvOnly) { + 1 + } else { + x.width + }; + for i in 0..iterations { + let mut shadow_arg = arg.clone(); + let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind { + ident.name + } else { + debug!("{:#?}", &shadow_arg.pat); + panic!("not an ident?"); + }; + let name: String = format!("b{}_{}", old_name, i); + new_inputs.push(name.clone()); + let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span); + shadow_arg.pat = P(ast::Pat { + id: ast::DUMMY_NODE_ID, + kind: PatKind::Ident(BindingMode::NONE, ident, None), + span: shadow_arg.pat.span, + tokens: shadow_arg.pat.tokens.clone(), + }); + d_inputs.push(shadow_arg.clone()); + } } DiffActivity::Const => { // Nothing to do here. } - DiffActivity::None | DiffActivity::FakeActivitySize => { + DiffActivity::None | DiffActivity::FakeActivitySize(_) => { panic!("Should not happen"); } } @@ -744,23 +932,48 @@ mod llvm_enzyme { d_decl.inputs = d_inputs.into(); if x.mode.is_fwd() { - if let DiffActivity::Dual = x.ret_activity { - let ty = match d_decl.output { - FnRetTy::Ty(ref ty) => ty.clone(), - FnRetTy::Default(span) => { - panic!("Did not expect Default ret ty: {:?}", span); - } + let ty = match d_decl.output { + FnRetTy::Ty(ref ty) => ty.clone(), + FnRetTy::Default(span) => { + // We want to return std::hint::black_box(()). + let kind = TyKind::Tup(ThinVec::new()); + let ty = P(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span, tokens: None }); + d_decl.output = FnRetTy::Ty(ty.clone()); + assert!(matches!(x.ret_activity, DiffActivity::None)); + // this won't be used below, so any type would be fine. + ty + } + }; + + if matches!(x.ret_activity, DiffActivity::Dual | DiffActivity::Dualv) { + let kind = if x.width == 1 || matches!(x.ret_activity, DiffActivity::Dualv) { + // Dual can only be used for f32/f64 ret. + // In that case we return now a tuple with two floats. + TyKind::Tup(thin_vec![ty.clone(), ty.clone()]) + } else { + // We have to return [T; width+1], +1 for the primal return. + let anon_const = rustc_ast::AnonConst { + id: ast::DUMMY_NODE_ID, + value: ecx.expr_usize(span, 1 + x.width as usize), + }; + TyKind::Array(ty.clone(), anon_const) }; - // Dual can only be used for f32/f64 ret. - // In that case we return now a tuple with two floats. - let kind = TyKind::Tup(thin_vec![ty.clone(), ty.clone()]); let ty = P(rustc_ast::Ty { kind, id: ty.id, span: ty.span, tokens: None }); d_decl.output = FnRetTy::Ty(ty); } - if let DiffActivity::DualOnly = x.ret_activity { + if matches!(x.ret_activity, DiffActivity::DualOnly | DiffActivity::DualvOnly) { // No need to change the return type, - // we will just return the shadow in place - // of the primal return. + // we will just return the shadow in place of the primal return. + // However, if we have a width > 1, then we don't return -> T, but -> [T; width] + if x.width > 1 { + let anon_const = rustc_ast::AnonConst { + id: ast::DUMMY_NODE_ID, + value: ecx.expr_usize(span, x.width as usize), + }; + let kind = TyKind::Array(ty.clone(), anon_const); + let ty = P(rustc_ast::Ty { kind, id: ty.id, span: ty.span, tokens: None }); + d_decl.output = FnRetTy::Ty(ty); + } } } diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 53c61831b4229..da01e3e9607bb 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -92,11 +92,7 @@ impl CfgEval<'_> { // the location of `#[cfg]` and `#[cfg_attr]` in the token stream. The tokenization // process is lossless, so this process is invisible to proc-macros. - // 'Flatten' all nonterminals (i.e. `TokenKind::Interpolated`) - // to `None`-delimited groups containing the corresponding tokens. This - // is normally delayed until the proc-macro server actually needs to - // provide a `TokenKind::Interpolated` to a proc-macro. We do this earlier, - // so that we can handle cases like: + // Interesting cases: // // ```rust // #[cfg_eval] #[cfg] $item @@ -104,8 +100,8 @@ impl CfgEval<'_> { // // where `$item` is `#[cfg_attr] struct Foo {}`. We want to make // sure to evaluate *all* `#[cfg]` and `#[cfg_attr]` attributes - the simplest - // way to do this is to do a single parse of a stream without any nonterminals. - let orig_tokens = annotatable.to_tokens().flattened(); + // way to do this is to do a single parse of the token stream. + let orig_tokens = annotatable.to_tokens(); // Re-parse the tokens, setting the `capture_cfg` flag to save extra information // to the captured `AttrTokenStream` (specifically, we capture @@ -121,18 +117,11 @@ impl CfgEval<'_> { let item = parser.parse_item(ForceCollect::Yes)?.unwrap(); Annotatable::Item(self.flat_map_item(item).pop().unwrap()) } - Annotatable::AssocItem(_, AssocCtxt::Trait) => { + Annotatable::AssocItem(_, ctxt) => { let item = parser.parse_trait_item(ForceCollect::Yes)?.unwrap().unwrap(); Annotatable::AssocItem( - self.flat_map_assoc_item(item, AssocCtxt::Trait).pop().unwrap(), - AssocCtxt::Trait, - ) - } - Annotatable::AssocItem(_, AssocCtxt::Impl) => { - let item = parser.parse_impl_item(ForceCollect::Yes)?.unwrap().unwrap(); - Annotatable::AssocItem( - self.flat_map_assoc_item(item, AssocCtxt::Impl).pop().unwrap(), - AssocCtxt::Impl, + self.flat_map_assoc_item(item, ctxt).pop().unwrap(), + ctxt, ) } Annotatable::ForeignItem(_) => { @@ -140,8 +129,9 @@ impl CfgEval<'_> { Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap()) } Annotatable::Stmt(_) => { - let stmt = - parser.parse_stmt_without_recovery(false, ForceCollect::Yes)?.unwrap(); + let stmt = parser + .parse_stmt_without_recovery(false, ForceCollect::Yes, false)? + .unwrap(); Annotatable::Stmt(P(self.flat_map_stmt(stmt).pop().unwrap())) } Annotatable::Expr(_) => { diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs index 6afd8c4b43b9a..423b6a15b646d 100644 --- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs +++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs @@ -1,44 +1,37 @@ //! Attributes injected into the crate root from command line using `-Z crate-attr`. -use rustc_ast::attr::mk_attr; -use rustc_ast::{self as ast, AttrItem, AttrStyle, token}; -use rustc_parse::parser::ForceCollect; -use rustc_parse::{new_parser_from_source_str, unwrap_or_emit_fatal}; +use rustc_ast::{self as ast}; +use rustc_errors::Diag; +use rustc_parse::parser::attr::InnerAttrPolicy; +use rustc_parse::{parse_in, source_str_to_stream}; use rustc_session::parse::ParseSess; use rustc_span::FileName; -use crate::errors; - pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) { for raw_attr in attrs { - let mut parser = unwrap_or_emit_fatal(new_parser_from_source_str( - psess, - FileName::cli_crate_attr_source_code(raw_attr), - raw_attr.clone(), - )); - - let start_span = parser.token.span; - let AttrItem { unsafety, path, args, tokens: _ } = - match parser.parse_attr_item(ForceCollect::No) { - Ok(ai) => ai, - Err(err) => { + let source = format!("#![{raw_attr}]"); + let parse = || -> Result>> { + let tokens = source_str_to_stream( + psess, + FileName::cli_crate_attr_source_code(raw_attr), + source, + None, + )?; + parse_in(psess, tokens, "", |p| { + p.parse_attribute(InnerAttrPolicy::Permitted) + }) + .map_err(|e| vec![e]) + }; + let meta = match parse() { + Ok(meta) => meta, + Err(errs) => { + for err in errs { err.emit(); - continue; } - }; - let end_span = parser.token.span; - if parser.token != token::Eof { - psess.dcx().emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) }); - continue; - } + continue; + } + }; - krate.attrs.push(mk_attr( - &psess.attr_id_generator, - AttrStyle::Inner, - unsafety, - path, - args, - start_span.to(end_span), - )); + krate.attrs.push(meta); } } diff --git a/compiler/rustc_builtin_macros/src/define_opaque.rs b/compiler/rustc_builtin_macros/src/define_opaque.rs new file mode 100644 index 0000000000000..cd02e81f5689c --- /dev/null +++ b/compiler/rustc_builtin_macros/src/define_opaque.rs @@ -0,0 +1,62 @@ +use rustc_ast::{DUMMY_NODE_ID, ast}; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::Span; + +pub(crate) fn expand( + ecx: &mut ExtCtxt<'_>, + _expand_span: Span, + meta_item: &ast::MetaItem, + mut item: Annotatable, +) -> Vec { + let define_opaque = match &mut item { + Annotatable::Item(p) => match &mut p.kind { + ast::ItemKind::Fn(f) => Some(&mut f.define_opaque), + ast::ItemKind::Const(ct) => Some(&mut ct.define_opaque), + ast::ItemKind::Static(si) => Some(&mut si.define_opaque), + _ => None, + }, + Annotatable::AssocItem(i, _assoc_ctxt) => match &mut i.kind { + ast::AssocItemKind::Fn(func) => Some(&mut func.define_opaque), + ast::AssocItemKind::Const(ct) => Some(&mut ct.define_opaque), + _ => None, + }, + Annotatable::Stmt(s) => match &mut s.kind { + ast::StmtKind::Item(p) => match &mut p.kind { + ast::ItemKind::Fn(f) => Some(&mut f.define_opaque), + ast::ItemKind::Const(ct) => Some(&mut ct.define_opaque), + ast::ItemKind::Static(si) => Some(&mut si.define_opaque), + _ => None, + }, + _ => None, + }, + _ => None, + }; + + let Some(list) = meta_item.meta_item_list() else { + ecx.dcx().span_err(meta_item.span, "expected list of type aliases"); + return vec![item]; + }; + + if let Some(define_opaque) = define_opaque { + *define_opaque = Some( + list.iter() + .filter_map(|entry| match entry { + ast::MetaItemInner::MetaItem(meta_item) if meta_item.is_word() => { + Some((DUMMY_NODE_ID, meta_item.path.clone())) + } + _ => { + ecx.dcx().span_err(entry.span(), "expected path to type alias"); + None + } + }) + .collect(), + ); + } else { + ecx.dcx().span_err( + meta_item.span, + "only functions, statics, and consts can define opaque types", + ); + } + + vec![item] +} diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 2653a9f48b9b6..e259f5b3955be 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -103,7 +103,7 @@ impl MultiItemModifier for Expander { fn dummy_annotatable() -> Annotatable { Annotatable::GenericParam(ast::GenericParam { id: ast::DUMMY_NODE_ID, - ident: Ident::empty(), + ident: Ident::dummy(), attrs: Default::default(), bounds: Default::default(), is_placeholder: false, diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index c3656e8244fe0..44cf215c66227 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -34,8 +34,8 @@ pub(crate) fn expand_deriving_clone( let is_simple; match item { Annotatable::Item(annitem) => match &annitem.kind { - ItemKind::Struct(_, Generics { params, .. }) - | ItemKind::Enum(_, Generics { params, .. }) => { + ItemKind::Struct(_, _, Generics { params, .. }) + | ItemKind::Enum(_, _, Generics { params, .. }) => { let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); let has_derive_copy = cx.resolver.has_derive_copy(container_id); if has_derive_copy diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 7958e037555d5..aa01da3151eb4 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -21,7 +21,7 @@ pub(crate) fn expand_deriving_partial_ord( // Order in which to perform matching let discr_then_data = if let Annotatable::Item(item) = item - && let ItemKind::Enum(def, _) = &item.kind + && let ItemKind::Enum(_, def, _) = &item.kind { let dataful: Vec = def.variants.iter().map(|v| !v.data.fields().is_empty()).collect(); match dataful.iter().filter(|&&b| b).count() { diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 5aed9f76f144f..446d8afeedd7f 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -30,7 +30,7 @@ pub(crate) fn expand_deriving_coerce_pointee( item.visit_with(&mut DetectNonGenericPointeeAttr { cx }); let (name_ident, generics) = if let Annotatable::Item(aitem) = item - && let ItemKind::Struct(struct_data, g) = &aitem.kind + && let ItemKind::Struct(ident, struct_data, g) = &aitem.kind { if !matches!( struct_data, @@ -40,7 +40,7 @@ pub(crate) fn expand_deriving_coerce_pointee( cx.dcx().emit_err(RequireOneField { span }); return; } - (aitem.ident, g) + (*ident, g) } else { cx.dcx().emit_err(RequireTransparent { span }); return; @@ -108,7 +108,6 @@ pub(crate) fn expand_deriving_coerce_pointee( push(Annotatable::Item( cx.item( span, - Ident::empty(), attrs.clone(), ast::ItemKind::Impl(Box::new(ast::Impl { safety: ast::Safety::Default, @@ -153,7 +152,6 @@ pub(crate) fn expand_deriving_coerce_pointee( let trait_ref = cx.trait_ref(trait_path); let item = cx.item( span, - Ident::empty(), attrs.clone(), ast::ItemKind::Impl(Box::new(ast::Impl { safety: ast::Safety::Default, @@ -300,13 +298,16 @@ pub(crate) fn expand_deriving_coerce_pointee( to_ty: &s_ty, rewritten: false, }; - let mut predicate = ast::WherePredicate { - kind: ast::WherePredicateKind::BoundPredicate(bound.clone()), - span: predicate.span, - id: ast::DUMMY_NODE_ID, - }; - substitution.visit_where_predicate(&mut predicate); + let mut kind = ast::WherePredicateKind::BoundPredicate(bound.clone()); + substitution.visit_where_predicate_kind(&mut kind); if substitution.rewritten { + let predicate = ast::WherePredicate { + attrs: predicate.attrs.clone(), + kind, + span: predicate.span, + id: ast::DUMMY_NODE_ID, + is_placeholder: false, + }; impl_generics.where_clause.predicates.push(predicate); } } @@ -388,8 +389,8 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> { } } - fn visit_where_predicate(&mut self, where_predicate: &mut ast::WherePredicate) { - match &mut where_predicate.kind { + fn visit_where_predicate_kind(&mut self, kind: &mut ast::WherePredicateKind) { + match kind { rustc_ast::WherePredicateKind::BoundPredicate(bound) => { bound .bound_generic_params diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 6b59ac2582755..9aa53f9e4f73b 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -185,7 +185,8 @@ use rustc_ast::{ self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, Mutability, PatKind, VariantData, }; -use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprPacked}; +use rustc_attr_data_structures::{AttributeKind, ReprPacked}; +use rustc_attr_parsing::AttributeParser; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_hir::Attribute; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; @@ -487,28 +488,28 @@ impl<'a> TraitDef<'a> { ); let newitem = match &item.kind { - ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def( + ast::ItemKind::Struct(ident, struct_def, generics) => self.expand_struct_def( cx, struct_def, - item.ident, + *ident, generics, from_scratch, is_packed, ), - ast::ItemKind::Enum(enum_def, generics) => { + ast::ItemKind::Enum(ident, enum_def, generics) => { // We ignore `is_packed` here, because `repr(packed)` // enums cause an error later on. // // This can only cause further compilation errors // downstream in blatantly illegal code, so it is fine. - self.expand_enum_def(cx, enum_def, item.ident, generics, from_scratch) + self.expand_enum_def(cx, enum_def, *ident, generics, from_scratch) } - ast::ItemKind::Union(struct_def, generics) => { + ast::ItemKind::Union(ident, struct_def, generics) => { if self.supports_unions { self.expand_struct_def( cx, struct_def, - item.ident, + *ident, generics, from_scratch, is_packed, @@ -527,15 +528,14 @@ impl<'a> TraitDef<'a> { item.attrs .iter() .filter(|a| { - [ + a.has_any_name(&[ sym::allow, sym::warn, sym::deny, sym::forbid, sym::stable, sym::unstable, - ] - .contains(&a.name_or_empty()) + ]) }) .cloned(), ); @@ -596,7 +596,6 @@ impl<'a> TraitDef<'a> { P(ast::AssocItem { id: ast::DUMMY_NODE_ID, span: self.span, - ident, vis: ast::Visibility { span: self.span.shrink_to_lo(), kind: ast::VisibilityKind::Inherited, @@ -605,6 +604,7 @@ impl<'a> TraitDef<'a> { attrs: ast::AttrVec::new(), kind: ast::AssocItemKind::Type(Box::new(ast::TyAlias { defaultness: ast::Defaultness::Final, + ident, generics: Generics::default(), where_clauses: ast::TyAliasWhereClauses::default(), bounds: Vec::new(), @@ -687,9 +687,11 @@ impl<'a> TraitDef<'a> { // and similarly for where clauses where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| { ast::WherePredicate { + attrs: clause.attrs.clone(), kind: clause.kind.clone(), id: ast::DUMMY_NODE_ID, span: clause.span.with_ctxt(ctxt), + is_placeholder: false, } })); @@ -744,8 +746,13 @@ impl<'a> TraitDef<'a> { }; let kind = ast::WherePredicateKind::BoundPredicate(predicate); - let predicate = - ast::WherePredicate { kind, id: ast::DUMMY_NODE_ID, span: self.span }; + let predicate = ast::WherePredicate { + attrs: ThinVec::new(), + kind, + id: ast::DUMMY_NODE_ID, + span: self.span, + is_placeholder: false, + }; where_clause.predicates.push(predicate); } } @@ -782,7 +789,6 @@ impl<'a> TraitDef<'a> { cx.item( self.span, - Ident::empty(), attrs, ast::ItemKind::Impl(Box::new(ast::Impl { safety: ast::Safety::Default, @@ -1026,13 +1032,14 @@ impl<'a> MethodDef<'a> { kind: ast::VisibilityKind::Inherited, tokens: None, }, - ident: method_ident, kind: ast::AssocItemKind::Fn(Box::new(ast::Fn { defaultness, sig, + ident: method_ident, generics: fn_generics, contract: None, body: Some(body_block), + define_opaque: None, })), tokens: None, }) diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index c112589b1319d..50e7b989ed8ab 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -110,7 +110,6 @@ fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P { rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated), span, tokens: None, - could_be_bare_literal: false, })) } diff --git a/compiler/rustc_builtin_macros/src/edition_panic.rs b/compiler/rustc_builtin_macros/src/edition_panic.rs index b39c9861fd643..ccfcc3079ebf8 100644 --- a/compiler/rustc_builtin_macros/src/edition_panic.rs +++ b/compiler/rustc_builtin_macros/src/edition_panic.rs @@ -74,11 +74,11 @@ pub(crate) fn use_panic_2021(mut span: Span) -> bool { // (To avoid using the edition of e.g. the assert!() or debug_assert!() definition.) loop { let expn = span.ctxt().outer_expn_data(); - if let Some(features) = expn.allow_internal_unstable { - if features.iter().any(|&f| f == sym::edition_panic) { - span = expn.call_site; - continue; - } + if let Some(features) = expn.allow_internal_unstable + && features.contains(&sym::edition_panic) + { + span = expn.call_site; + continue; } break expn.edition >= Edition::Edition2021; } diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index 0913dc91a5342..f3ac932e1b7e9 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -8,7 +8,7 @@ use std::env::VarError; use rustc_ast::token::{self, LitKind}; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{AstDeref, ExprKind, GenericArg, Mutability}; +use rustc_ast::{ExprKind, GenericArg, Mutability}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; use rustc_span::{Ident, Span, Symbol, kw, sym}; use thin_vec::thin_vec; @@ -148,13 +148,13 @@ pub(crate) fn expand_env<'cx>( cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar { span, var: *symbol, - var_expr: var_expr.ast_deref(), + var_expr: &var_expr, }) } else { cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar { span, var: *symbol, - var_expr: var_expr.ast_deref(), + var_expr: &var_expr, }) } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index ab1e0d8ee896b..b28f7d312d937 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1,7 +1,7 @@ use rustc_errors::codes::*; use rustc_errors::{ Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan, SingleLabelManySpans, - SubdiagMessageOp, Subdiagnostic, + Subdiagnostic, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; @@ -109,13 +109,6 @@ pub(crate) struct ProcMacro { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(builtin_macros_invalid_crate_attribute)] -pub(crate) struct InvalidCrateAttr { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag(builtin_macros_non_abi)] pub(crate) struct NonABI { @@ -185,6 +178,15 @@ mod autodiff { pub(crate) act: String, } + #[derive(Diagnostic)] + #[diag(builtin_macros_autodiff_ret_activity)] + pub(crate) struct AutoDiffInvalidRetAct { + #[primary_span] + pub(crate) span: Span, + pub(crate) mode: String, + pub(crate) act: String, + } + #[derive(Diagnostic)] #[diag(builtin_macros_autodiff_mode)] pub(crate) struct AutoDiffInvalidMode { @@ -193,6 +195,14 @@ mod autodiff { pub(crate) mode: String, } + #[derive(Diagnostic)] + #[diag(builtin_macros_autodiff_width)] + pub(crate) struct AutoDiffInvalidWidth { + #[primary_span] + pub(crate) span: Span, + pub(crate) width: u128, + } + #[derive(Diagnostic)] #[diag(builtin_macros_autodiff)] pub(crate) struct AutoDiffInvalidApplication { @@ -674,13 +684,9 @@ pub(crate) struct FormatUnusedArg { // Allow the singular form to be a subdiagnostic of the multiple-unused // form of diagnostic. impl Subdiagnostic for FormatUnusedArg { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("named", self.named); - let msg = f(diag, crate::fluent_generated::builtin_macros_format_unused_arg.into()); + let msg = diag.eagerly_translate(crate::fluent_generated::builtin_macros_format_unused_arg); diag.span_label(self.span, msg); } } @@ -904,7 +910,7 @@ pub(crate) struct AsmOptAlreadyprovided { pub(crate) span: Span, pub(crate) symbol: Symbol, #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] - pub(crate) full_span: Span, + pub(crate) span_with_comma: Span, } #[derive(Diagnostic)] @@ -915,7 +921,7 @@ pub(crate) struct AsmUnsupportedOption { pub(crate) span: Span, pub(crate) symbol: Symbol, #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] - pub(crate) full_span: Span, + pub(crate) span_with_comma: Span, pub(crate) macro_name: &'static str, } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 90447da66807b..39f9d5f900514 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -1,3 +1,5 @@ +use std::ops::Range; + use parse::Position::ArgumentNamed; use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; @@ -190,7 +192,8 @@ fn make_format_args( && let [stmt] = block.stmts.as_slice() && let StmtKind::Expr(expr) = &stmt.kind && let ExprKind::Path(None, path) = &expr.kind - && path.is_potential_trivial_const_arg() + && path.segments.len() == 1 + && path.segments[0].args.is_none() { err.multipart_suggestion( "quote your inlined format argument to use as string literal", @@ -334,7 +337,7 @@ fn make_format_args( return ExpandResult::Ready(Err(guar)); } - let to_span = |inner_span: parse::InnerSpan| { + let to_span = |inner_span: Range| { is_source_literal.then(|| { fmt_span.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end }) }) @@ -406,7 +409,7 @@ fn make_format_args( let mut placeholder_index = 0; for piece in &pieces { - match *piece { + match piece.clone() { parse::Piece::Lit(s) => { unfinished_literal.push_str(s); } @@ -416,7 +419,8 @@ fn make_format_args( unfinished_literal.clear(); } - let span = parser.arg_places.get(placeholder_index).and_then(|&s| to_span(s)); + let span = + parser.arg_places.get(placeholder_index).and_then(|s| to_span(s.clone())); placeholder_index += 1; let position_span = to_span(position_span); @@ -608,7 +612,7 @@ fn make_format_args( fn invalid_placeholder_type_error( ecx: &ExtCtxt<'_>, ty: &str, - ty_span: Option, + ty_span: Option>, fmt_span: Span, ) { let sp = ty_span.map(|sp| fmt_span.from_inner(InnerSpan::new(sp.start, sp.end))); @@ -710,11 +714,9 @@ fn report_missing_placeholders( }; let pos = sub.position(); - let sub = String::from(sub.as_str()); - if explained.contains(&sub) { + if !explained.insert(sub.to_string()) { continue; } - explained.insert(sub); if !found_foreign { found_foreign = true; diff --git a/compiler/rustc_builtin_macros/src/format_foreign.rs b/compiler/rustc_builtin_macros/src/format_foreign.rs index 866ec72f11646..13d5b42942ac7 100644 --- a/compiler/rustc_builtin_macros/src/format_foreign.rs +++ b/compiler/rustc_builtin_macros/src/format_foreign.rs @@ -12,14 +12,16 @@ pub(crate) mod printf { Escape((usize, usize)), } - impl<'a> Substitution<'a> { - pub(crate) fn as_str(&self) -> &str { + impl ToString for Substitution<'_> { + fn to_string(&self) -> String { match self { - Substitution::Format(fmt) => fmt.span, - Substitution::Escape(_) => "%%", + Substitution::Format(fmt) => fmt.span.into(), + Substitution::Escape(_) => "%%".into(), } } + } + impl Substitution<'_> { pub(crate) fn position(&self) -> InnerSpan { match self { Substitution::Format(fmt) => fmt.position, @@ -627,15 +629,17 @@ pub(crate) mod shell { Escape((usize, usize)), } - impl Substitution<'_> { - pub(crate) fn as_str(&self) -> String { + impl ToString for Substitution<'_> { + fn to_string(&self) -> String { match self { Substitution::Ordinal(n, _) => format!("${n}"), Substitution::Name(n, _) => format!("${n}"), Substitution::Escape(_) => "$$".into(), } } + } + impl Substitution<'_> { pub(crate) fn position(&self) -> InnerSpan { let (Self::Ordinal(_, pos) | Self::Name(_, pos) | Self::Escape(pos)) = self; InnerSpan::new(pos.0, pos.1) diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 8fdbbf8e704a9..4b1958bce3223 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -25,15 +25,15 @@ pub(crate) fn expand( // Allow using `#[global_allocator]` on an item statement // FIXME - if we get deref patterns, use them to reduce duplication here - let (item, is_stmt, ty_span) = if let Annotatable::Item(item) = &item - && let ItemKind::Static(box ast::StaticItem { ty, .. }) = &item.kind + let (item, ident, is_stmt, ty_span) = if let Annotatable::Item(item) = &item + && let ItemKind::Static(box ast::StaticItem { ident, ty, .. }) = &item.kind { - (item, false, ecx.with_def_site_ctxt(ty.span)) + (item, *ident, false, ecx.with_def_site_ctxt(ty.span)) } else if let Annotatable::Stmt(stmt) = &item && let StmtKind::Item(item) = &stmt.kind - && let ItemKind::Static(box ast::StaticItem { ty, .. }) = &item.kind + && let ItemKind::Static(box ast::StaticItem { ident, ty, .. }) = &item.kind { - (item, true, ecx.with_def_site_ctxt(ty.span)) + (item, *ident, true, ecx.with_def_site_ctxt(ty.span)) } else { ecx.dcx().emit_err(errors::AllocMustStatics { span: item.span() }); return vec![orig_item]; @@ -41,7 +41,7 @@ pub(crate) fn expand( // Generate a bunch of new items using the AllocFnFactory let span = ecx.with_def_site_ctxt(item.span); - let f = AllocFnFactory { span, ty_span, global: item.ident, cx: ecx }; + let f = AllocFnFactory { span, ty_span, global: ident, cx: ecx }; // Generate item statements for the allocator methods. let stmts = ALLOCATOR_METHODS.iter().map(|method| f.allocator_fn(method)).collect(); @@ -80,16 +80,13 @@ impl AllocFnFactory<'_, '_> { let kind = ItemKind::Fn(Box::new(Fn { defaultness: ast::Defaultness::Final, sig, + ident: Ident::from_str_and_span(&global_fn_name(method.name), self.span), generics: Generics::default(), contract: None, body, + define_opaque: None, })); - let item = self.cx.item( - self.span, - Ident::from_str_and_span(&global_fn_name(method.name), self.span), - self.attrs(), - kind, - ); + let item = self.cx.item(self.span, self.attrs(), kind); self.cx.stmt_item(self.ty_span, item) } diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index ca16583a45de7..9cd4d17059a0f 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -12,17 +12,18 @@ #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] #![feature(rustdoc_internals)] #![feature(string_from_utf8_lossy_owned)] #![feature(try_blocks)] -#![warn(unreachable_pub)] +#![recursion_limit = "256"] // tidy-alphabetical-end extern crate proc_macro; +use std::sync::Arc; + use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind}; use rustc_expand::proc_macro::BangProcMacro; use rustc_span::sym; @@ -39,6 +40,7 @@ mod compile_error; mod concat; mod concat_bytes; mod concat_idents; +mod define_opaque; mod derive; mod deriving; mod edition_panic; @@ -66,13 +68,13 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { let mut register = |name, kind| resolver.register_builtin_macro(name, kind); macro register_bang($($name:ident: $f:expr,)*) { - $(register(sym::$name, SyntaxExtensionKind::LegacyBang(Box::new($f as MacroExpanderFn)));)* + $(register(sym::$name, SyntaxExtensionKind::LegacyBang(Arc::new($f as MacroExpanderFn)));)* } macro register_attr($($name:ident: $f:expr,)*) { - $(register(sym::$name, SyntaxExtensionKind::LegacyAttr(Box::new($f)));)* + $(register(sym::$name, SyntaxExtensionKind::LegacyAttr(Arc::new($f)));)* } macro register_derive($($name:ident: $f:expr,)*) { - $(register(sym::$name, SyntaxExtensionKind::LegacyDerive(Box::new(BuiltinDerive($f))));)* + $(register(sym::$name, SyntaxExtensionKind::LegacyDerive(Arc::new(BuiltinDerive($f))));)* } register_bang! { @@ -114,6 +116,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { bench: test::expand_bench, cfg_accessible: cfg_accessible::Expander, cfg_eval: cfg_eval::expand, + define_opaque: define_opaque::expand, derive: derive::Expander { is_const: false }, derive_const: derive::Expander { is_const: true }, global_allocator: global_allocator::expand, @@ -137,9 +140,9 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { } let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote); - register(sym::quote, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client }))); - let requires = SyntaxExtensionKind::Attr(Box::new(contracts::ExpandRequires)); + register(sym::quote, SyntaxExtensionKind::Bang(Arc::new(BangProcMacro { client }))); + let requires = SyntaxExtensionKind::Attr(Arc::new(contracts::ExpandRequires)); register(sym::contracts_requires, requires); - let ensures = SyntaxExtensionKind::Attr(Box::new(contracts::ExpandEnsures)); + let ensures = SyntaxExtensionKind::Attr(Arc::new(contracts::ExpandEnsures)); register(sym::contracts_ensures, ensures); } diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index a55c7e962d098..3529e5525fcd2 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -1,9 +1,10 @@ use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{AnonConst, DUMMY_NODE_ID, Ty, TyPat, TyPatKind, ast}; +use rustc_ast::{AnonConst, DUMMY_NODE_ID, Ty, TyPat, TyPatKind, ast, token}; use rustc_errors::PResult; use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; use rustc_parse::exp; +use rustc_parse::parser::{CommaRecoveryMode, RecoverColon, RecoverComma}; use rustc_span::Span; pub(crate) fn expand<'cx>( @@ -26,19 +27,42 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P let ty = parser.parse_ty()?; parser.expect_keyword(exp!(Is))?; - let pat = parser.parse_pat_no_top_alt(None, None)?.into_inner(); + let pat = pat_to_ty_pat( + cx, + parser + .parse_pat_no_top_guard( + None, + RecoverComma::No, + RecoverColon::No, + CommaRecoveryMode::EitherTupleOrPipe, + )? + .into_inner(), + ); + + if parser.token != token::Eof { + parser.unexpected()?; + } + + Ok((ty, pat)) +} + +fn ty_pat(kind: TyPatKind, span: Span) -> P { + P(TyPat { id: DUMMY_NODE_ID, kind, span, tokens: None }) +} + +fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> P { let kind = match pat.kind { ast::PatKind::Range(start, end, include_end) => TyPatKind::Range( start.map(|value| P(AnonConst { id: DUMMY_NODE_ID, value })), end.map(|value| P(AnonConst { id: DUMMY_NODE_ID, value })), include_end, ), + ast::PatKind::Or(variants) => TyPatKind::Or( + variants.into_iter().map(|pat| pat_to_ty_pat(cx, pat.into_inner())).collect(), + ), ast::PatKind::Err(guar) => TyPatKind::Err(guar), _ => TyPatKind::Err(cx.dcx().span_err(pat.span, "pattern not supported in pattern types")), }; - - let pat = P(TyPat { id: pat.id, kind, span: pat.span, tokens: pat.tokens }); - - Ok((ty, pat)) + ty_pat(kind, pat.span) } diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index ee6475c8b8e91..a91f2d38a93ae 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -20,14 +20,14 @@ use crate::errors; struct ProcMacroDerive { id: NodeId, trait_name: Symbol, - function_name: Ident, + function_ident: Ident, span: Span, attrs: Vec, } struct ProcMacroDef { id: NodeId, - function_name: Ident, + function_ident: Ident, span: Span, } @@ -92,7 +92,12 @@ impl<'a> CollectProcMacros<'a> { } } - fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { + fn collect_custom_derive( + &mut self, + item: &'a ast::Item, + function_ident: Ident, + attr: &'a ast::Attribute, + ) { let Some((trait_name, proc_attrs)) = parse_macro_name_and_helper_attrs(self.dcx, attr, "derive") else { @@ -104,7 +109,7 @@ impl<'a> CollectProcMacros<'a> { id: item.id, span: item.span, trait_name, - function_name: item.ident, + function_ident, attrs: proc_attrs, })); } else { @@ -118,12 +123,12 @@ impl<'a> CollectProcMacros<'a> { } } - fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) { + fn collect_attr_proc_macro(&mut self, item: &'a ast::Item, function_ident: Ident) { if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Attr(ProcMacroDef { id: item.id, span: item.span, - function_name: item.ident, + function_ident, })); } else { let msg = if !self.in_root { @@ -136,12 +141,12 @@ impl<'a> CollectProcMacros<'a> { } } - fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) { + fn collect_bang_proc_macro(&mut self, item: &'a ast::Item, function_ident: Ident) { if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Bang(ProcMacroDef { id: item.id, span: item.span, - function_name: item.ident, + function_ident, })); } else { let msg = if !self.in_root { @@ -165,12 +170,6 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { } } - // First up, make sure we're checking a bare function. If we're not then - // we're just not interested in this item. - // - // If we find one, try to locate a `#[proc_macro_derive]` attribute on it. - let is_fn = matches!(item.kind, ast::ItemKind::Fn(..)); - let mut found_attr: Option<&'a ast::Attribute> = None; for attr in &item.attrs { @@ -214,7 +213,11 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { return; }; - if !is_fn { + // Make sure we're checking a bare function. If we're not then we're + // just not interested any further in this item. + let fn_ident = if let ast::ItemKind::Fn(fn_) = &item.kind { + fn_.ident + } else { self.dcx .create_err(errors::AttributeOnlyBeUsedOnBareFunctions { span: attr.span, @@ -222,7 +225,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { }) .emit(); return; - } + }; if self.is_test_crate { return; @@ -238,12 +241,13 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { return; } + // Try to locate a `#[proc_macro_derive]` attribute. if attr.has_name(sym::proc_macro_derive) { - self.collect_custom_derive(item, attr); + self.collect_custom_derive(item, fn_ident, attr); } else if attr.has_name(sym::proc_macro_attribute) { - self.collect_attr_proc_macro(item); + self.collect_attr_proc_macro(item, fn_ident); } else if attr.has_name(sym::proc_macro) { - self.collect_bang_proc_macro(item); + self.collect_bang_proc_macro(item, fn_ident); }; let prev_in_root = mem::replace(&mut self.in_root, false); @@ -278,7 +282,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id()); let proc_macro = Ident::new(sym::proc_macro, span); - let krate = cx.item(span, proc_macro, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None)); + let krate = cx.item(span, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, proc_macro)); let bridge = Ident::new(sym::bridge, span); let client = Ident::new(sym::client, span); @@ -299,7 +303,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { ProcMacro::Derive(m) => m.span, ProcMacro::Attr(m) | ProcMacro::Bang(m) => m.span, }; - let local_path = |cx: &ExtCtxt<'_>, name| cx.expr_path(cx.path(span, vec![name])); + let local_path = |cx: &ExtCtxt<'_>, ident| cx.expr_path(cx.path(span, vec![ident])); let proc_macro_ty_method_path = |cx: &ExtCtxt<'_>, method| { cx.expr_path(cx.path( span.with_ctxt(harness_span.ctxt()), @@ -323,7 +327,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { .map(|&s| cx.expr_str(span, s)) .collect::>(), ), - local_path(cx, cd.function_name), + local_path(cx, cd.function_ident), ], ) } @@ -341,8 +345,8 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { harness_span, proc_macro_ty_method_path(cx, ident), thin_vec![ - cx.expr_str(span, ca.function_name.name), - local_path(cx, ca.function_name), + cx.expr_str(span, ca.function_ident.name), + local_path(cx, ca.function_ident), ], ) } diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs index 6933ca09349a1..a1ee53b7ca21f 100644 --- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs +++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs @@ -43,9 +43,8 @@ pub fn inject( let item = cx.item( span, - Ident::new(name, ident_span), thin_vec![cx.attr_word(sym::macro_use, span)], - ast::ItemKind::ExternCrate(None), + ast::ItemKind::ExternCrate(None, Ident::new(name, ident_span)), ); krate.items.insert(0, item); @@ -60,6 +59,7 @@ pub fn inject( Edition2018 => sym::rust_2018, Edition2021 => sym::rust_2021, Edition2024 => sym::rust_2024, + EditionFuture => sym::rust_future, }]) .map(|&symbol| Ident::new(symbol, span)) .collect(); @@ -67,7 +67,6 @@ pub fn inject( // Inject the relevant crate's prelude. let use_item = cx.item( span, - Ident::empty(), thin_vec![cx.attr_word(sym::prelude_import, span)], ast::ItemKind::Use(ast::UseTree { prefix: cx.path(span, import_path), diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index a05fff2dcd1c1..1cef4f9514cd7 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -51,21 +51,28 @@ pub(crate) fn expand_test_case( return vec![]; } }; - item = item.map(|mut item| { - let test_path_symbol = Symbol::intern(&item_path( - // skip the name of the root module - &ecx.current_expansion.module.mod_path[1..], - &item.ident, - )); - item.vis = ast::Visibility { - span: item.vis.span, - kind: ast::VisibilityKind::Public, - tokens: None, - }; - item.ident.span = item.ident.span.with_ctxt(sp.ctxt()); - item.attrs.push(ecx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, sp)); - item - }); + + // `#[test_case]` is valid on functions, consts, and statics. Only modify + // the item in those cases. + match &mut item.kind { + ast::ItemKind::Fn(box ast::Fn { ident, .. }) + | ast::ItemKind::Const(box ast::ConstItem { ident, .. }) + | ast::ItemKind::Static(box ast::StaticItem { ident, .. }) => { + ident.span = ident.span.with_ctxt(sp.ctxt()); + let test_path_symbol = Symbol::intern(&item_path( + // skip the name of the root module + &ecx.current_expansion.module.mod_path[1..], + ident, + )); + item.vis = ast::Visibility { + span: item.vis.span, + kind: ast::VisibilityKind::Public, + tokens: None, + }; + item.attrs.push(ecx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, sp)); + } + _ => {} + } let ret = if is_stmt { Annotatable::Stmt(P(ecx.stmt_item(item.span, item))) @@ -162,17 +169,17 @@ pub(crate) fn expand_test_or_bench( let ret_ty_sp = cx.with_def_site_ctxt(fn_.sig.decl.output.span()); let attr_sp = cx.with_def_site_ctxt(attr_sp); - let test_id = Ident::new(sym::test, attr_sp); + let test_ident = Ident::new(sym::test, attr_sp); // creates test::$name - let test_path = |name| cx.path(ret_ty_sp, vec![test_id, Ident::from_str_and_span(name, sp)]); + let test_path = |name| cx.path(ret_ty_sp, vec![test_ident, Ident::from_str_and_span(name, sp)]); // creates test::ShouldPanic::$name let should_panic_path = |name| { cx.path( sp, vec![ - test_id, + test_ident, Ident::from_str_and_span("ShouldPanic", sp), Ident::from_str_and_span(name, sp), ], @@ -184,7 +191,7 @@ pub(crate) fn expand_test_or_bench( cx.path( sp, vec![ - test_id, + test_ident, Ident::from_str_and_span("TestType", sp), Ident::from_str_and_span(name, sp), ], @@ -223,7 +230,7 @@ pub(crate) fn expand_test_or_bench( // super::$test_fn(b) cx.expr_call( ret_ty_sp, - cx.expr_path(cx.path(sp, vec![item.ident])), + cx.expr_path(cx.path(sp, vec![fn_.ident])), thin_vec![cx.expr_ident(sp, b)], ), ], @@ -249,7 +256,7 @@ pub(crate) fn expand_test_or_bench( // $test_fn() cx.expr_call( ret_ty_sp, - cx.expr_path(cx.path(sp, vec![item.ident])), + cx.expr_path(cx.path(sp, vec![fn_.ident])), ThinVec::new(), ), // ) ], @@ -262,15 +269,14 @@ pub(crate) fn expand_test_or_bench( let test_path_symbol = Symbol::intern(&item_path( // skip the name of the root module &cx.current_expansion.module.mod_path[1..], - &item.ident, + &fn_.ident, )); - let location_info = get_location_info(cx, &item); + let location_info = get_location_info(cx, &fn_); let mut test_const = cx.item( sp, - Ident::new(item.ident.name, sp), thin_vec![ // #[cfg(test)] cx.attr_nested_word(sym::cfg, sym::test, attr_sp), @@ -283,8 +289,10 @@ pub(crate) fn expand_test_or_bench( ast::ItemKind::Const( ast::ConstItem { defaultness: ast::Defaultness::Final, + ident: Ident::new(fn_.ident.name, sp), generics: ast::Generics::default(), ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), + define_opaque: None, // test::TestDescAndFn { expr: Some( cx.expr_struct( @@ -379,7 +387,8 @@ pub(crate) fn expand_test_or_bench( }); // extern crate test - let test_extern = cx.item(sp, test_id, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None)); + let test_extern = + cx.item(sp, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, test_ident)); debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const)); @@ -433,8 +442,8 @@ fn not_testable_error(cx: &ExtCtxt<'_>, attr_sp: Span, item: Option<&ast::Item>) .emit(); } -fn get_location_info(cx: &ExtCtxt<'_>, item: &ast::Item) -> (Symbol, usize, usize, usize, usize) { - let span = item.ident.span; +fn get_location_info(cx: &ExtCtxt<'_>, fn_: &ast::Fn) -> (Symbol, usize, usize, usize, usize) { + let span = fn_.ident.span; let (source_file, lo_line, lo_col, hi_line, hi_col) = cx.sess.source_map().span_to_location_info(span); diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 472e16e62d5b0..56a67b0534d98 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -134,27 +134,21 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { if let Some(name) = get_test_name(&item) { debug!("this is a test item"); - let test = Test { span: item.span, ident: item.ident, name }; + // `unwrap` is ok because only functions, consts, and static should reach here. + let test = Test { span: item.span, ident: item.kind.ident().unwrap(), name }; self.tests.push(test); } // We don't want to recurse into anything other than mods, since // mods or tests inside of functions will break things if let ast::ItemKind::Mod( + _, _, ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. }, _), ) = item.kind { let prev_tests = mem::take(&mut self.tests); - walk_item_kind( - &mut item.kind, - item.span, - item.id, - &mut item.ident, - &mut item.vis, - (), - self, - ); + walk_item_kind(&mut item.kind, item.span, item.id, &mut item.vis, (), self); self.add_test_cases(item.id, span, prev_tests); } else { // But in those cases, we emit a lint to warn the user of these missing tests. @@ -181,9 +175,9 @@ impl<'a> Visitor<'a> for InnerItemLinter<'_> { } fn entry_point_type(item: &ast::Item, at_root: bool) -> EntryPointType { - match item.kind { - ast::ItemKind::Fn(..) => { - rustc_ast::entry::entry_point_type(&item.attrs, at_root, Some(item.ident.name)) + match &item.kind { + ast::ItemKind::Fn(fn_) => { + rustc_ast::entry::entry_point_type(&item.attrs, at_root, Some(fn_.ident.name)) } _ => EntryPointType::None, } @@ -295,7 +289,7 @@ fn generate_test_harness( fn mk_main(cx: &mut TestCtxt<'_>) -> P { let sp = cx.def_site; let ecx = &cx.ext_cx; - let test_id = Ident::new(sym::test, sp); + let test_ident = Ident::new(sym::test, sp); let runner_name = match cx.panic_strategy { PanicStrategy::Unwind => "test_main_static", @@ -303,10 +297,9 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { }; // test::test_main_static(...) - let mut test_runner = cx - .test_runner - .clone() - .unwrap_or_else(|| ecx.path(sp, vec![test_id, Ident::from_str_and_span(runner_name, sp)])); + let mut test_runner = cx.test_runner.clone().unwrap_or_else(|| { + ecx.path(sp, vec![test_ident, Ident::from_str_and_span(runner_name, sp)]) + }); test_runner.span = sp; @@ -317,7 +310,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { // extern crate test let test_extern_stmt = ecx.stmt_item( sp, - ecx.item(sp, test_id, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None)), + ecx.item(sp, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, test_ident)), ); // #[rustc_main] @@ -340,22 +333,24 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { let decl = ecx.fn_decl(ThinVec::new(), ast::FnRetTy::Ty(main_ret_ty)); let sig = ast::FnSig { decl, header: ast::FnHeader::default(), span: sp }; let defaultness = ast::Defaultness::Final; + + // Honor the reexport_test_harness_main attribute + let main_ident = match cx.reexport_test_harness_main { + Some(sym) => Ident::new(sym, sp.with_ctxt(SyntaxContext::root())), + None => Ident::new(sym::main, sp), + }; + let main = ast::ItemKind::Fn(Box::new(ast::Fn { defaultness, sig, + ident: main_ident, generics: ast::Generics::default(), contract: None, body: Some(main_body), + define_opaque: None, })); - // Honor the reexport_test_harness_main attribute - let main_id = match cx.reexport_test_harness_main { - Some(sym) => Ident::new(sym, sp.with_ctxt(SyntaxContext::root())), - None => Ident::new(sym::main, sp), - }; - let main = P(ast::Item { - ident: main_id, attrs: thin_vec![main_attr, coverage_attr, doc_hidden_attr], id: ast::DUMMY_NODE_ID, kind: main, diff --git a/compiler/rustc_codegen_cranelift/.cirrus.yml b/compiler/rustc_codegen_cranelift/.cirrus.yml index 1ec99eb3d17a5..ee5de8b42f465 100644 --- a/compiler/rustc_codegen_cranelift/.cirrus.yml +++ b/compiler/rustc_codegen_cranelift/.cirrus.yml @@ -1,20 +1,21 @@ -task: - name: freebsd - freebsd_instance: - image: freebsd-13-2-release-amd64 - setup_rust_script: - - pkg install -y git-tiny binutils - - curl https://sh.rustup.rs -sSf --output rustup.sh - - sh rustup.sh --default-toolchain none -y --profile=minimal - target_cache: - folder: build/cg_clif - prepare_script: - - . $HOME/.cargo/env - - ./y.sh prepare - test_script: - - . $HOME/.cargo/env - # Disabling incr comp reduces cache size and incr comp doesn't save as much - # on CI anyway. - - export CARGO_BUILD_INCREMENTAL=false - # Skip rand as it fails on FreeBSD due to rust-random/rand#1355 - - ./y.sh test --skip-test test.rust-random/rand +# FIXME re-enable once https://github.com/rust-lang/rust/issues/134863 is fixed. +# task: +# name: freebsd +# freebsd_instance: +# image: freebsd-13-2-release-amd64 +# setup_rust_script: +# - pkg install -y git-tiny binutils +# - curl https://sh.rustup.rs -sSf --output rustup.sh +# - sh rustup.sh --default-toolchain none -y --profile=minimal +# target_cache: +# folder: build/cg_clif +# prepare_script: +# - . $HOME/.cargo/env +# - ./y.sh prepare +# test_script: +# - . $HOME/.cargo/env +# # Disabling incr comp reduces cache size and incr comp doesn't save as much +# # on CI anyway. +# - export CARGO_BUILD_INCREMENTAL=false +# # Skip rand as it fails on FreeBSD due to rust-random/rand#1355 +# - ./y.sh test --skip-test test.rust-random/rand diff --git a/compiler/rustc_codegen_cranelift/.github/actions/github-release/README.md b/compiler/rustc_codegen_cranelift/.github/actions/github-release/README.md deleted file mode 100644 index c70ba8f495387..0000000000000 --- a/compiler/rustc_codegen_cranelift/.github/actions/github-release/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# github-release - -An action used to publish GitHub releases for `wasmtime`. - -As of the time of this writing there's a few actions floating around which -perform github releases but they all tend to have their set of drawbacks. -Additionally nothing handles deleting releases which we need for our rolling -`dev` release. - -To handle all this this action rolls-its-own implementation using the -actions/toolkit repository and packages published there. These run in a Docker -container and take various inputs to orchestrate the release from the build. - -More comments can be found in `main.js`. - -Testing this is really hard. If you want to try though run `npm install` and -then `node main.js`. You'll have to configure a bunch of env vars though to get -anything reasonably working. diff --git a/compiler/rustc_codegen_cranelift/.github/actions/github-release/action.yml b/compiler/rustc_codegen_cranelift/.github/actions/github-release/action.yml deleted file mode 100644 index 36e5209f50c3b..0000000000000 --- a/compiler/rustc_codegen_cranelift/.github/actions/github-release/action.yml +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -name: 'rustc_codegen_cranelift github releases' -description: 'rustc_codegen_cranelift github releases' -inputs: - token: - description: '' - required: true - files: - description: '' - required: true -runs: - using: 'node16' - main: 'main.js' diff --git a/compiler/rustc_codegen_cranelift/.github/actions/github-release/main.js b/compiler/rustc_codegen_cranelift/.github/actions/github-release/main.js deleted file mode 100644 index 1eb2b7f23b26c..0000000000000 --- a/compiler/rustc_codegen_cranelift/.github/actions/github-release/main.js +++ /dev/null @@ -1,162 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -const core = require('@actions/core'); -const path = require("path"); -const fs = require("fs"); -const github = require('@actions/github'); -const glob = require('glob'); - -function sleep(milliseconds) { - return new Promise(resolve => setTimeout(resolve, milliseconds)) -} - -async function runOnce() { - // Load all our inputs and env vars. Note that `getInput` reads from `INPUT_*` - const files = core.getInput('files'); - const token = core.getInput('token'); - const slug = process.env.GITHUB_REPOSITORY; - const owner = slug.split('/')[0]; - const repo = slug.split('/')[1]; - const sha = process.env.GITHUB_SHA; - let name = 'dev'; - if (process.env.GITHUB_REF.startsWith('refs/tags/v')) { - name = process.env.GITHUB_REF.substring(10); - } - - core.info(`files: ${files}`); - core.info(`name: ${name}`); - core.info(`token: ${token}`); - - const octokit = github.getOctokit(token); - - // For the `dev` release we may need to update the tag to point to the new - // commit on this branch. All other names should already have tags associated - // with them. - if (name == 'dev') { - let tag = null; - try { - tag = await octokit.request("GET /repos/:owner/:repo/git/refs/tags/:name", { owner, repo, name }); - core.info(`found existing tag`); - console.log("tag: ", JSON.stringify(tag.data, null, 2)); - } catch (e) { - // ignore if this tag doesn't exist - core.info(`no existing tag found`); - } - - if (tag === null || tag.data.object.sha !== sha) { - core.info(`updating existing tag or creating new one`); - - try { - core.info(`updating dev tag`); - await octokit.rest.git.updateRef({ - owner, - repo, - ref: 'tags/dev', - sha, - force: true, - }); - } catch (e) { - console.log("ERROR: ", JSON.stringify(e.response, null, 2)); - core.info(`creating dev tag`); - try { - await octokit.rest.git.createRef({ - owner, - repo, - ref: 'refs/tags/dev', - sha, - }); - } catch (e) { - // we might race with others, so assume someone else has created the - // tag by this point. - console.log("failed to create tag: ", JSON.stringify(e.response, null, 2)); - } - } - - console.log("double-checking tag is correct"); - tag = await octokit.request("GET /repos/:owner/:repo/git/refs/tags/:name", { owner, repo, name }); - if (tag.data.object.sha !== sha) { - console.log("tag: ", JSON.stringify(tag.data, null, 2)); - throw new Error("tag didn't work"); - } - } else { - core.info(`existing tag works`); - } - } - - // Delete a previous release - try { - core.info(`fetching release`); - let release = await octokit.rest.repos.getReleaseByTag({ owner, repo, tag: name }); - console.log("found release: ", JSON.stringify(release.data, null, 2)); - await octokit.rest.repos.deleteRelease({ - owner, - repo, - release_id: release.data.id, - }); - console.log("deleted release"); - } catch (e) { - console.log("ERROR: ", JSON.stringify(e, null, 2)); - } - - console.log("creating a release"); - let release = await octokit.rest.repos.createRelease({ - owner, - repo, - tag_name: name, - prerelease: name === 'dev', - }); - - // Delete all assets from a previous run - for (const asset of release.data.assets) { - console.log(`deleting prior asset ${asset.id}`); - await octokit.rest.repos.deleteReleaseAsset({ - owner, - repo, - asset_id: asset.id, - }); - } - - // Upload all the relevant assets for this release as just general blobs. - for (const file of glob.sync(files)) { - const size = fs.statSync(file).size; - const name = path.basename(file); - core.info(`upload ${file}`); - await octokit.rest.repos.uploadReleaseAsset({ - data: fs.createReadStream(file), - headers: { 'content-length': size, 'content-type': 'application/octet-stream' }, - name, - url: release.data.upload_url, - }); - } -} - -async function run() { - const retries = 10; - for (let i = 0; i < retries; i++) { - try { - await runOnce(); - break; - } catch (e) { - if (i === retries - 1) - throw e; - logError(e); - console.log("RETRYING after 10s"); - await sleep(10000) - } - } -} - -function logError(e) { - console.log("ERROR: ", e.message); - try { - console.log(JSON.stringify(e, null, 2)); - } catch (e) { - // ignore json errors for now - } - console.log(e.stack); -} - -run().catch(err => { - logError(err); - core.setFailed(err.message); -}); diff --git a/compiler/rustc_codegen_cranelift/.github/actions/github-release/package-lock.json b/compiler/rustc_codegen_cranelift/.github/actions/github-release/package-lock.json deleted file mode 100644 index dd3b2a048f094..0000000000000 --- a/compiler/rustc_codegen_cranelift/.github/actions/github-release/package-lock.json +++ /dev/null @@ -1,571 +0,0 @@ -{ - "name": "rustc_codegen_cranelift-github-release", - "version": "0.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "rustc_codegen_cranelift-github-release", - "version": "0.0.0", - "dependencies": { - "@actions/core": "^1.9.1", - "@actions/github": "^5.1.0", - "glob": "^7.1.5" - } - }, - "node_modules/@actions/core": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", - "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", - "dependencies": { - "@actions/http-client": "^2.0.1", - "uuid": "^8.3.2" - } - }, - "node_modules/@actions/github": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.0.tgz", - "integrity": "sha512-tuI80F7JQIhg77ZTTgUAPpVD7ZnP9oHSPN8xw7LOwtA4vEMbAjWJNbmLBfV7xua7r016GyjzWLuec5cs8f/a8A==", - "dependencies": { - "@actions/http-client": "^2.0.1", - "@octokit/core": "^3.6.0", - "@octokit/plugin-paginate-rest": "^2.17.0", - "@octokit/plugin-rest-endpoint-methods": "^5.13.0" - } - }, - "node_modules/@actions/http-client": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", - "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", - "dependencies": { - "tunnel": "^0.0.6" - } - }, - "node_modules/@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "dependencies": { - "@octokit/types": "^6.0.3" - } - }, - "node_modules/@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "dependencies": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "dependencies": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "dependencies": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.21.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", - "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", - "dependencies": { - "@octokit/types": "^6.40.0" - }, - "peerDependencies": { - "@octokit/core": ">=2" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.16.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", - "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", - "dependencies": { - "@octokit/types": "^6.39.0", - "deprecation": "^2.3.1" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "dependencies": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" - } - }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - } - }, - "dependencies": { - "@actions/core": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", - "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", - "requires": { - "@actions/http-client": "^2.0.1", - "uuid": "^8.3.2" - } - }, - "@actions/github": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.0.tgz", - "integrity": "sha512-tuI80F7JQIhg77ZTTgUAPpVD7ZnP9oHSPN8xw7LOwtA4vEMbAjWJNbmLBfV7xua7r016GyjzWLuec5cs8f/a8A==", - "requires": { - "@actions/http-client": "^2.0.1", - "@octokit/core": "^3.6.0", - "@octokit/plugin-paginate-rest": "^2.17.0", - "@octokit/plugin-rest-endpoint-methods": "^5.13.0" - } - }, - "@actions/http-client": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", - "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", - "requires": { - "tunnel": "^0.0.6" - } - }, - "@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "requires": { - "@octokit/types": "^6.0.3" - } - }, - "@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "requires": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "requires": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "requires": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/plugin-paginate-rest": { - "version": "2.21.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", - "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", - "requires": { - "@octokit/types": "^6.40.0" - } - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "5.16.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", - "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", - "requires": { - "@octokit/types": "^6.39.0", - "deprecation": "^2.3.1" - } - }, - "@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "requires": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" - }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - } - } -} diff --git a/compiler/rustc_codegen_cranelift/.github/actions/github-release/package.json b/compiler/rustc_codegen_cranelift/.github/actions/github-release/package.json deleted file mode 100644 index d9c23f8873ece..0000000000000 --- a/compiler/rustc_codegen_cranelift/.github/actions/github-release/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "rustc_codegen_cranelift-github-release", - "version": "0.0.0", - "license": "Apache-2.0 WITH LLVM-exception", - "main": "main.js", - "dependencies": { - "@actions/core": "^1.9.1", - "@actions/github": "^5.1.0", - "glob": "^7.1.5" - } -} diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml index 30dc5cb16154b..6ad041a796c92 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml @@ -25,7 +25,10 @@ jobs: - os: ubuntu-latest env: TARGET_TRIPLE: x86_64-unknown-linux-gnu - - os: macos-latest + - os: ubuntu-24.04-arm + env: + TARGET_TRIPLE: aarch64-unknown-linux-gnu + - os: macos-13 env: TARGET_TRIPLE: x86_64-apple-darwin - os: macos-latest @@ -56,13 +59,6 @@ jobs: if: matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' run: rustup set default-host x86_64-pc-windows-gnu - - name: Use x86_64 compiler on macOS - if: matrix.os == 'macos-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-apple-darwin' - run: rustup set default-host x86_64-apple-darwin - - - name: Prepare dependencies - run: ./y.sh prepare - - name: Build run: ./y.sh build --sysroot none diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 61a4c1270c993..6fd288d195c05 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -53,13 +53,12 @@ jobs: - os: ubuntu-latest env: TARGET_TRIPLE: x86_64-unknown-linux-gnu - - os: macos-latest - env: - TARGET_TRIPLE: x86_64-apple-darwin - - os: ubuntu-latest + - os: ubuntu-24.04-arm env: TARGET_TRIPLE: aarch64-unknown-linux-gnu - apt_deps: gcc-aarch64-linux-gnu qemu-user + - os: macos-13 + env: + TARGET_TRIPLE: x86_64-apple-darwin - os: macos-latest env: TARGET_TRIPLE: aarch64-apple-darwin @@ -95,10 +94,6 @@ jobs: if: matrix.os == 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' run: rustup set default-host x86_64-pc-windows-gnu - - name: Use x86_64 compiler on macOS - if: matrix.os == 'macos-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-apple-darwin' - run: rustup set default-host x86_64-apple-darwin - - name: Install toolchain and emulator if: matrix.apt_deps != null run: | @@ -170,9 +165,6 @@ jobs: sudo apt update sudo apt install -y hyperfine - - name: Prepare dependencies - run: ./y.sh prepare - - name: Build run: ./y.sh build --sysroot none @@ -192,7 +184,10 @@ jobs: - os: ubuntu-22.04 env: TARGET_TRIPLE: x86_64-unknown-linux-gnu - - os: macos-latest + - os: ubuntu-24.04-arm + env: + TARGET_TRIPLE: aarch64-unknown-linux-gnu + - os: macos-13 env: TARGET_TRIPLE: x86_64-apple-darwin - os: macos-latest @@ -218,13 +213,6 @@ jobs: if: matrix.os == 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' run: rustup set default-host x86_64-pc-windows-gnu - - name: Use x86_64 compiler on macOS - if: matrix.os == 'macos-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-apple-darwin' - run: rustup set default-host x86_64-apple-darwin - - - name: Prepare dependencies - run: ./y.sh prepare - - name: Build backend run: ./y.sh build --sysroot none @@ -273,12 +261,9 @@ jobs: rmdir artifacts/ # verify all artifacts are represented in release/ ls -R release/ - - run: npm install --production - working-directory: .github/actions/github-release - - - name: Publish Release - uses: ./.github/actions/github-release - with: - files: "release/*" - token: ${{ github.token }} - continue-on-error: true + - name: Publish release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release delete --cleanup-tag -y dev || true + gh release create --target $GITHUB_SHA --prerelease dev release/* diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml index 70c214ce8b147..9253ab96353c3 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml @@ -22,9 +22,6 @@ jobs: path: build/cg_clif key: ${{ runner.os }}-rustc-test-cargo-build-target-${{ hashFiles('rust-toolchain', 'Cargo.lock') }} - - name: Prepare dependencies - run: ./y.sh prepare - - name: Test run: ./scripts/test_bootstrap.sh @@ -50,8 +47,5 @@ jobs: sudo apt update sudo apt install -y ripgrep - - name: Prepare dependencies - run: ./y.sh prepare - - name: Test run: ./scripts/test_rustc_tests.sh diff --git a/compiler/rustc_codegen_cranelift/.vscode/settings.json b/compiler/rustc_codegen_cranelift/.vscode/settings.json index 491646ce59bb3..68bd93aea8901 100644 --- a/compiler/rustc_codegen_cranelift/.vscode/settings.json +++ b/compiler/rustc_codegen_cranelift/.vscode/settings.json @@ -1,41 +1,40 @@ { "editor.formatOnSave": true, - // in case rustc.source is disabled for performance reasons; disable the errors about this - "rust-analyzer.diagnostics.disabled": ["unresolved-extern-crate", "unresolved-macro-call"], + "rust-analyzer.diagnostics.disabled": [ + "unresolved-extern-crate", + "unresolved-macro-call" + ], "rust-analyzer.rustc.source": "discover", "rust-analyzer.imports.granularity.enforce": true, "rust-analyzer.imports.granularity.group": "module", "rust-analyzer.imports.prefix": "crate", - "rust-analyzer.cargo.features": ["unstable-features"], + "rust-analyzer.cargo.features": [ + "unstable-features" + ], "rust-analyzer.linkedProjects": [ "./Cargo.toml", "./build_system/Cargo.toml", { + "sysroot_src": "./build/stdlib/library", "crates": [ { "root_module": "./example/mini_core.rs", - "edition": "2018", + "edition": "2015", "deps": [], "cfg": [], }, { "root_module": "./example/mini_core_hello_world.rs", - "edition": "2018", - "deps": [{ "crate": 0, "name": "mini_core" }], - "cfg": [], - }, - { - "root_module": "./example/mod_bench.rs", - "edition": "2018", - "deps": [], + "edition": "2015", + "deps": [ + { + "crate": 0, + "name": "mini_core" + } + ], "cfg": [], }, - ] - }, - { - "sysroot_src": "./build/stdlib/library", - "crates": [ { "root_module": "./example/std_example.rs", "edition": "2015", diff --git a/compiler/rustc_codegen_cranelift/.zed/settings.json b/compiler/rustc_codegen_cranelift/.zed/settings.json index e93bed3694921..4338a3473311b 100644 --- a/compiler/rustc_codegen_cranelift/.zed/settings.json +++ b/compiler/rustc_codegen_cranelift/.zed/settings.json @@ -5,7 +5,10 @@ "initialization_options": { "diagnostics": { // in case rustc.source is disabled for performance reasons; disable the errors about this - "disabled": ["unresolved-extern-crate", "unresolved-macro-call"] + "disabled": [ + "unresolved-extern-crate", + "unresolved-macro-call" + ] }, "rustc": { "source": "discover" @@ -18,22 +21,25 @@ "prefix": "crate" }, "cargo": { - "features": ["unstable-features"] + "features": [ + "unstable-features" + ] }, "linkedProjects": [ "./Cargo.toml", "./build_system/Cargo.toml", { + "sysroot_src": "./build/stdlib/library", "crates": [ { "root_module": "./example/mini_core.rs", - "edition": "2018", + "edition": "2015", "deps": [], "cfg": [] }, { "root_module": "./example/mini_core_hello_world.rs", - "edition": "2018", + "edition": "2015", "deps": [ { "crate": 0, @@ -42,17 +48,6 @@ ], "cfg": [] }, - { - "root_module": "./example/mod_bench.rs", - "edition": "2018", - "deps": [], - "cfg": [] - } - ] - }, - { - "sysroot_src": "./build/stdlib/library", - "crates": [ { "root_module": "./example/std_example.rs", "edition": "2015", diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index ca66ec5c6e930..e5f1896b9230b 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -41,28 +41,44 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cranelift-assembler-x64" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4b56ebe316895d3fa37775d0a87b0c889cc933f5c8b253dbcc7c7bcb7fe7e4" +dependencies = [ + "cranelift-assembler-x64-meta", +] + +[[package]] +name = "cranelift-assembler-x64-meta" +version = "0.118.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95cabbc01dfbd7dcd6c329ca44f0212910309c221797ac736a67a5bc8857fe1b" + [[package]] name = "cranelift-bforest" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e15d04a0ce86cb36ead88ad68cf693ffd6cda47052b9e0ac114bc47fd9cd23c4" +checksum = "76ffe46df300a45f1dc6f609dc808ce963f0e3a2e971682c479a2d13e3b9b8ef" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c6e3969a7ce267259ce244b7867c5d3bc9e65b0a87e81039588dfdeaede9f34" +checksum = "b265bed7c51e1921fdae6419791d31af77d33662ee56d7b0fa0704dc8d231cab" [[package]] name = "cranelift-codegen" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c22032c4cb42558371cf516bb47f26cdad1819d3475c133e93c49f50ebf304e" +checksum = "e606230a7e3a6897d603761baee0d19f88d077f17b996bb5089488a29ae96e41" dependencies = [ "bumpalo", + "cranelift-assembler-x64", "cranelift-bforest", "cranelift-bitset", "cranelift-codegen-meta", @@ -71,7 +87,7 @@ dependencies = [ "cranelift-entity", "cranelift-isle", "gimli", - "hashbrown 0.14.5", + "hashbrown", "log", "regalloc2", "rustc-hash", @@ -82,42 +98,43 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c904bc71c61b27fc57827f4a1379f29de64fe95653b620a3db77d59655eee0b8" +checksum = "8a63bffafc23bc60969ad528e138788495999d935f0adcfd6543cb151ca8637d" dependencies = [ + "cranelift-assembler-x64", "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40180f5497572f644ce88c255480981ae2ec1d7bb4d8e0c0136a13b87a2f2ceb" +checksum = "af50281b67324b58e843170a6a5943cf6d387c06f7eeacc9f5696e4ab7ae7d7e" [[package]] name = "cranelift-control" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d132c6d0bd8a489563472afc171759da0707804a65ece7ceb15a8c6d7dd5ef" +checksum = "8c20c1b38d1abfbcebb0032e497e71156c0e3b8dcb3f0a92b9863b7bcaec290c" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d0d9618275474fbf679dd018ac6e009acbd6ae6850f6a67be33fb3b00b323" +checksum = "0c2c67d95507c51b4a1ff3f3555fe4bfec36b9e13c1b684ccc602736f5d5f4a2" dependencies = [ "cranelift-bitset", ] [[package]] name = "cranelift-frontend" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fac41e16729107393174b0c9e3730fb072866100e1e64e80a1a963b2e484d57" +checksum = "4e002691cc69c38b54fc7ec93e5be5b744f627d027031d991cc845d1d512d0ce" dependencies = [ "cranelift-codegen", "log", @@ -127,15 +144,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca20d576e5070044d0a72a9effc2deacf4d6aa650403189d8ea50126483944d" +checksum = "e93588ed1796cbcb0e2ad160403509e2c5d330d80dd6e0014ac6774c7ebac496" [[package]] name = "cranelift-jit" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e65c42755a719b09662b00c700daaf76cc35d5ace1f5c002ad404b591ff1978" +checksum = "17f6682f0b193d6b7873cc8e7ed67e8776a8a26f50eeabf88534e9be618b9a03" dependencies = [ "anyhow", "cranelift-codegen", @@ -153,9 +170,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d55612bebcf16ff7306c8a6f5bdb6d45662b8aa1ee058ecce8807ad87db719b" +checksum = "ff19784c6de05116e63e6a34791012bd927b2a4eac56233039c46f1b6a4edac8" dependencies = [ "anyhow", "cranelift-codegen", @@ -164,9 +181,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dee82f3f1f2c4cba9177f1cc5e350fe98764379bcd29340caa7b01f85076c7" +checksum = "e5b09bdd6407bf5d89661b80cf926ce731c9e8cc184bf49102267a2369a8358e" dependencies = [ "cranelift-codegen", "libc", @@ -175,9 +192,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.116.1" +version = "0.118.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aad5a6d3e379493c3f8b35dc61c93d0bf5f27003bbe20614e0200b0ec372ef52" +checksum = "685e8661a30d1cb69509f589ac643adeee79c5f63c0da316431b9fad29e6d3b4" dependencies = [ "anyhow", "cranelift-codegen", @@ -226,12 +243,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - [[package]] name = "hashbrown" version = "0.15.2" @@ -248,7 +259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown", ] [[package]] @@ -295,7 +306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", - "hashbrown 0.15.2", + "hashbrown", "indexmap", "memchr", ] @@ -326,7 +337,7 @@ checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3" dependencies = [ "allocator-api2", "bumpalo", - "hashbrown 0.15.2", + "hashbrown", "log", "rustc-hash", "smallvec", @@ -425,9 +436,9 @@ checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "wasmtime-jit-icache-coherence" -version = "29.0.1" +version = "31.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec5e8552e01692e6c2e5293171704fed8abdec79d1a6995a0870ab190e5747d1" +checksum = "a54f6c6c7e9d7eeee32dfcc10db7f29d505ee7dd28d00593ea241d5f70698e64" dependencies = [ "anyhow", "cfg-if", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index 670d6f4eef5c1..08b60de14c1f8 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,12 +8,12 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.116.0", default-features = false, features = ["std", "unwind", "all-native-arch"] } -cranelift-frontend = { version = "0.116.0" } -cranelift-module = { version = "0.116.0" } -cranelift-native = { version = "0.116.0" } -cranelift-jit = { version = "0.116.0", optional = true } -cranelift-object = { version = "0.116.0" } +cranelift-codegen = { version = "0.118.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] } +cranelift-frontend = { version = "0.118.0" } +cranelift-module = { version = "0.118.0" } +cranelift-native = { version = "0.118.0" } +cranelift-jit = { version = "0.118.0", optional = true } +cranelift-object = { version = "0.118.0" } target-lexicon = "0.13" gimli = { version = "0.31", default-features = false, features = ["write"] } object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } @@ -24,12 +24,12 @@ smallvec = "1.8.1" [patch.crates-io] # Uncomment to use an unreleased version of cranelift -#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } -#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } -#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } -#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } -#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } -#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } +#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" } +#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" } +#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" } +#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" } +#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" } +#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" } # Uncomment to use local checkout of cranelift #cranelift-codegen = { path = "../wasmtime/cranelift/codegen" } diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index 18a840f8a50e0..28edb5795ce3c 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -49,13 +49,13 @@ If you want to build the backend manually, you can download it from GitHub and b ```bash $ git clone https://github.com/rust-lang/rustc_codegen_cranelift $ cd rustc_codegen_cranelift -$ ./y.sh prepare $ ./y.sh build ``` To run the test suite replace the last command with: ```bash +$ ./y.sh prepare # only needs to be run the first time $ ./test.sh ``` diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs index 11f73bdb61f97..ba5cc9a29f599 100644 --- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs +++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs @@ -91,6 +91,13 @@ impl GitRepo { fn verify_checksum(&self, dirs: &Dirs) { let download_dir = self.download_dir(dirs); + if !download_dir.exists() { + eprintln!( + "Missing directory {download_dir}: Please run ./y.sh prepare to download.", + download_dir = download_dir.display(), + ); + std::process::exit(1); + } let actual_hash = format!("{:016x}", hash_dir(&download_dir)); if actual_hash != self.content_hash { eprintln!( diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index ea7e94c345ad0..eec89c026b26a 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -1,5 +1,4 @@ use std::ffi::OsStr; -use std::fs; use std::path::PathBuf; use std::process::Command; @@ -100,6 +99,34 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ runner.run_out_command("gen_block_iterate", &[]); }), TestCase::build_bin_and_run("aot.raw-dylib", "example/raw-dylib.rs", &[]), + TestCase::custom("test.sysroot", &|runner| { + apply_patches( + &runner.dirs, + "sysroot_tests", + &runner.stdlib_source.join("library"), + &SYSROOT_TESTS_SRC.to_path(&runner.dirs), + ); + + SYSROOT_TESTS.clean(&runner.dirs); + + let mut target_compiler = runner.target_compiler.clone(); + // coretests and alloctests produce a bunch of warnings. When running + // in rust's CI warnings are denied, so we have to override that here. + target_compiler.rustflags.push("--cap-lints=allow".to_owned()); + // The standard library may have been compiled with -Zrandomize-layout. + target_compiler.rustflags.extend(["--cfg".to_owned(), "randomized_layouts".to_owned()]); + + if runner.is_native { + let mut test_cmd = SYSROOT_TESTS.test(&target_compiler, &runner.dirs); + test_cmd.args(["-p", "coretests", "-p", "alloctests", "--tests", "--", "-q"]); + spawn_and_wait(test_cmd); + } else { + eprintln!("Cross-Compiling: Not running tests"); + let mut build_cmd = SYSROOT_TESTS.build(&target_compiler, &runner.dirs); + build_cmd.args(["-p", "coretests", "-p", "alloctests", "--tests"]); + spawn_and_wait(build_cmd); + } + }), ]; pub(crate) static RAND_REPO: GitRepo = GitRepo::github( @@ -126,9 +153,9 @@ static PORTABLE_SIMD_SRC: RelPath = RelPath::build("portable-simd"); static PORTABLE_SIMD: CargoProject = CargoProject::new(&PORTABLE_SIMD_SRC, "portable-simd_target"); -static LIBCORE_TESTS_SRC: RelPath = RelPath::build("coretests"); +static SYSROOT_TESTS_SRC: RelPath = RelPath::build("sysroot_tests"); -static LIBCORE_TESTS: CargoProject = CargoProject::new(&LIBCORE_TESTS_SRC, "coretests_target"); +static SYSROOT_TESTS: CargoProject = CargoProject::new(&SYSROOT_TESTS_SRC, "sysroot_tests_target"); const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ TestCase::custom("test.rust-random/rand", &|runner| { @@ -147,31 +174,6 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ spawn_and_wait(build_cmd); } }), - TestCase::custom("test.libcore", &|runner| { - apply_patches( - &runner.dirs, - "coretests", - &runner.stdlib_source.join("library/coretests"), - &LIBCORE_TESTS_SRC.to_path(&runner.dirs), - ); - - let source_lockfile = runner.dirs.source_dir.join("patches/coretests-lock.toml"); - let target_lockfile = LIBCORE_TESTS_SRC.to_path(&runner.dirs).join("Cargo.lock"); - fs::copy(source_lockfile, target_lockfile).unwrap(); - - LIBCORE_TESTS.clean(&runner.dirs); - - if runner.is_native { - let mut test_cmd = LIBCORE_TESTS.test(&runner.target_compiler, &runner.dirs); - test_cmd.arg("--").arg("-q"); - spawn_and_wait(test_cmd); - } else { - eprintln!("Cross-Compiling: Not running tests"); - let mut build_cmd = LIBCORE_TESTS.build(&runner.target_compiler, &runner.dirs); - build_cmd.arg("--tests"); - spawn_and_wait(build_cmd); - } - }), TestCase::custom("test.regex", &|runner| { REGEX_REPO.patch(&runner.dirs); @@ -330,10 +332,8 @@ impl<'a> TestRunner<'a> { target_compiler.rustflags.extend(rustflags_from_env("RUSTFLAGS")); target_compiler.rustdocflags.extend(rustflags_from_env("RUSTDOCFLAGS")); - let jit_supported = use_unstable_features - && is_native - && target_compiler.triple.contains("x86_64") - && !target_compiler.triple.contains("windows"); + let jit_supported = + use_unstable_features && is_native && !target_compiler.triple.contains("windows"); Self { is_native, jit_supported, skip_tests, dirs, target_compiler, stdlib_source } } @@ -374,21 +374,7 @@ impl<'a> TestRunner<'a> { TestCaseCmd::JitBin { source, args } => { let mut jit_cmd = self.rustc_command([ "-Zunstable-options", - "-Cllvm-args=mode=jit", - "-Cprefer-dynamic", - source, - "--cfg", - "jit", - ]); - if !args.is_empty() { - jit_cmd.env("CG_CLIF_JIT_ARGS", args); - } - spawn_and_wait(jit_cmd); - - eprintln!("[JIT-lazy] {testname}"); - let mut jit_cmd = self.rustc_command([ - "-Zunstable-options", - "-Cllvm-args=mode=jit-lazy", + "-Cllvm-args=jit-mode", "-Cprefer-dynamic", source, "--cfg", diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs index c2114caf8692e..f239976845964 100644 --- a/compiler/rustc_codegen_cranelift/build_system/utils.rs +++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs @@ -105,7 +105,11 @@ impl CargoProject { .arg(self.manifest_path(dirs)) .arg("--target-dir") .arg(self.target_dir(dirs)) - .arg("--locked"); + .arg("--locked") + // bootstrap sets both RUSTC and RUSTC_WRAPPER to the same wrapper. RUSTC is already + // respected by the rustc-clif wrapper, but RUSTC_WRAPPER will misinterpret rustc-clif + // as filename, so we need to unset it. + .env_remove("RUSTC_WRAPPER"); if dirs.frozen { cmd.arg("--frozen"); diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index f578cbef35e68..6ae4767adfdf5 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -32,9 +32,9 @@ aot.issue-59326 aot.neon aot.gen_block_iterate aot.raw-dylib +test.sysroot testsuite.extended_sysroot test.rust-random/rand -test.libcore test.regex test.portable-simd diff --git a/compiler/rustc_codegen_cranelift/docs/usage.md b/compiler/rustc_codegen_cranelift/docs/usage.md index 135a51ce392b5..dbe36109f83e9 100644 --- a/compiler/rustc_codegen_cranelift/docs/usage.md +++ b/compiler/rustc_codegen_cranelift/docs/usage.md @@ -38,14 +38,7 @@ $ $cg_clif_dir/dist/cargo-clif jit or ```bash -$ $cg_clif_dir/dist/rustc-clif -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs -``` - -There is also an experimental lazy jit mode. In this mode functions are only compiled once they are -first called. - -```bash -$ $cg_clif_dir/dist/cargo-clif lazy-jit +$ $cg_clif_dir/dist/rustc-clif -Cllvm-args=jit-mode -Cprefer-dynamic my_crate.rs ``` ## Shell @@ -54,7 +47,7 @@ These are a few functions that allow you to easily run rust code from the shell ```bash function jit_naked() { - echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-options -Cllvm-args=mode=jit-lazy -Cprefer-dynamic + echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-options -Cllvm-args=jit-mode-Cprefer-dynamic } function jit() { diff --git a/compiler/rustc_codegen_cranelift/example/example.rs b/compiler/rustc_codegen_cranelift/example/example.rs index 1ef2aa5dd8ea4..aeb38331edb02 100644 --- a/compiler/rustc_codegen_cranelift/example/example.rs +++ b/compiler/rustc_codegen_cranelift/example/example.rs @@ -1,6 +1,6 @@ #![feature(no_core, unboxed_closures)] #![no_core] -#![allow(dead_code)] +#![allow(dead_code, unnecessary_transmutes)] extern crate mini_core; diff --git a/compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs b/compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs index 25bfe542d228a..de9a3d550eccd 100644 --- a/compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs +++ b/compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs @@ -6,16 +6,25 @@ #![feature(gen_blocks)] fn foo() -> impl Iterator { - gen { yield 42; for x in 3..6 { yield x } } + gen { + yield 42; + for x in 3..6 { + yield x + } + } } fn moved() -> impl Iterator { let mut x = "foo".to_string(); gen move { yield 42; - if x == "foo" { return } + if x == "foo" { + return; + } x.clear(); - for x in 3..6 { yield x } + for x in 3..6 { + yield x + } } } @@ -32,5 +41,4 @@ fn main() { let mut iter = moved(); assert_eq!(iter.next(), Some(42)); assert_eq!(iter.next(), None); - } diff --git a/compiler/rustc_codegen_cranelift/example/issue-72793.rs b/compiler/rustc_codegen_cranelift/example/issue-72793.rs index 2e08fbca8ef27..95d58b90e7981 100644 --- a/compiler/rustc_codegen_cranelift/example/issue-72793.rs +++ b/compiler/rustc_codegen_cranelift/example/issue-72793.rs @@ -2,23 +2,21 @@ #![feature(type_alias_impl_trait)] -mod helper { - pub trait T { - type Item; - } +pub trait T { + type Item; +} - pub type Alias<'a> = impl T; +pub type Alias<'a> = impl T; - struct S; - impl<'a> T for &'a S { - type Item = &'a (); - } +struct S; +impl<'a> T for &'a S { + type Item = &'a (); +} - pub fn filter_positive<'a>() -> Alias<'a> { - &S - } +#[define_opaque(Alias)] +pub fn filter_positive<'a>() -> Alias<'a> { + &S } -use helper::*; fn with_positive(fun: impl Fn(Alias<'_>)) { fun(filter_positive()); diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 72c9df59d833f..6e345b2a6fdf0 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -624,25 +624,25 @@ pub mod intrinsics { #[rustc_intrinsic] pub fn size_of() -> usize; #[rustc_intrinsic] - pub unsafe fn size_of_val(_val: *const T) -> usize; + pub unsafe fn size_of_val(val: *const T) -> usize; #[rustc_intrinsic] pub fn min_align_of() -> usize; #[rustc_intrinsic] - pub unsafe fn min_align_of_val(_val: *const T) -> usize; + pub unsafe fn min_align_of_val(val: *const T) -> usize; #[rustc_intrinsic] - pub unsafe fn copy(_src: *const T, _dst: *mut T, _count: usize); + pub unsafe fn copy(src: *const T, dst: *mut T, count: usize); #[rustc_intrinsic] - pub unsafe fn transmute(_e: T) -> U; + pub unsafe fn transmute(e: T) -> U; #[rustc_intrinsic] - pub unsafe fn ctlz_nonzero(_x: T) -> u32; + pub unsafe fn ctlz_nonzero(x: T) -> u32; #[rustc_intrinsic] pub fn needs_drop() -> bool; #[rustc_intrinsic] - pub fn bitreverse(_x: T) -> T; + pub fn bitreverse(x: T) -> T; #[rustc_intrinsic] - pub fn bswap(_x: T) -> T; + pub fn bswap(x: T) -> T; #[rustc_intrinsic] - pub unsafe fn write_bytes(_dst: *mut T, _val: u8, _count: usize); + pub unsafe fn write_bytes(dst: *mut T, val: u8, count: usize); #[rustc_intrinsic] pub unsafe fn unreachable() -> !; } diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 09d5b73fd3d9d..93ca2e0e42188 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -1,13 +1,4 @@ -#![feature( - no_core, - lang_items, - never_type, - linkage, - extern_types, - naked_functions, - thread_local, - repr_simd -)] +#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local, repr_simd)] #![no_core] #![allow(dead_code, non_camel_case_types, internal_features)] @@ -387,11 +378,9 @@ global_asm! { } #[cfg(all(not(jit), target_arch = "x86_64"))] -#[naked] +#[unsafe(naked)] extern "C" fn naked_test() { - unsafe { - naked_asm!("ret"); - } + naked_asm!("ret") } #[repr(C)] diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index ffdc6a7d48491..2d9de2a5b8d6b 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -8,9 +8,6 @@ unboxed_closures )] #![allow(internal_features)] -// FIXME once abi_unsupported_vector_types is a hard error disable the foo test when the respective -// target feature is not enabled. -#![allow(abi_unsupported_vector_types)] #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; diff --git a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch deleted file mode 100644 index 4a06dc3f7ef85..0000000000000 --- a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch +++ /dev/null @@ -1,40 +0,0 @@ -From ad7ffe71baba46865f2e65266ab025920dfdc20b Mon Sep 17 00:00:00 2001 -From: bjorn3 -Date: Thu, 18 Feb 2021 18:45:28 +0100 -Subject: [PATCH] Disable 128bit atomic operations - -Cranelift doesn't support them yet ---- - library/core/src/panic/unwind_safe.rs | 6 ----- - library/core/src/sync/atomic.rs | 38 --------------------------- - library/core/tests/atomic.rs | 4 --- - 4 files changed, 4 insertions(+), 50 deletions(-) - -diff --git a/tests/lib.rs b/tests/lib.rs -index 1e336bf..35e6f54 100644 ---- a/tests/lib.rs -+++ b/tests/lib.rs -@@ -2,5 +2,4 @@ - // tidy-alphabetical-start --#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] - #![cfg_attr(test, feature(cfg_match))] - #![feature(alloc_layout_extra)] - #![feature(array_chunks)] -diff --git a/tests/atomic.rs b/tests/atomic.rs -index b735957..ea728b6 100644 ---- a/tests/atomic.rs -+++ b/tests/atomic.rs -@@ -185,10 +185,6 @@ fn atomic_alignment() { - assert_eq!(align_of::(), size_of::()); - #[cfg(target_has_atomic = "64")] - assert_eq!(align_of::(), size_of::()); -- #[cfg(target_has_atomic = "128")] -- assert_eq!(align_of::(), size_of::()); -- #[cfg(target_has_atomic = "128")] -- assert_eq!(align_of::(), size_of::()); - #[cfg(target_has_atomic = "ptr")] - assert_eq!(align_of::(), size_of::()); - #[cfg(target_has_atomic = "ptr")] --- -2.26.2.7.g19db9cfb68 - diff --git a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch index 3c81b04c0ead2..d7e3b11127c42 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch @@ -1,4 +1,4 @@ -From ad7ffe71baba46865f2e65266ab025920dfdc20b Mon Sep 17 00:00:00 2001 +From 5d7c709608b01301d4628d2159265936d4440b67 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 18 Feb 2021 18:45:28 +0100 Subject: [PATCH] Disable 128bit atomic operations @@ -7,11 +7,10 @@ Cranelift doesn't support them yet --- library/core/src/panic/unwind_safe.rs | 6 ----- library/core/src/sync/atomic.rs | 38 --------------------------- - library/core/tests/atomic.rs | 4 --- - 4 files changed, 4 insertions(+), 50 deletions(-) + 2 files changed, 44 deletions(-) diff --git a/library/core/src/panic/unwind_safe.rs b/library/core/src/panic/unwind_safe.rs -index 092b7cf..158cf71 100644 +index a60f0799c0e..af056fbf41f 100644 --- a/library/core/src/panic/unwind_safe.rs +++ b/library/core/src/panic/unwind_safe.rs @@ -216,9 +216,6 @@ impl RefUnwindSafe for crate::sync::atomic::AtomicI32 {} @@ -21,7 +20,7 @@ index 092b7cf..158cf71 100644 -#[cfg(target_has_atomic_load_store = "128")] -#[unstable(feature = "integer_atomics", issue = "99069")] -impl RefUnwindSafe for crate::sync::atomic::AtomicI128 {} - + #[cfg(target_has_atomic_load_store = "ptr")] #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] @@ -235,9 +232,6 @@ impl RefUnwindSafe for crate::sync::atomic::AtomicU32 {} @@ -31,14 +30,14 @@ index 092b7cf..158cf71 100644 -#[cfg(target_has_atomic_load_store = "128")] -#[unstable(feature = "integer_atomics", issue = "99069")] -impl RefUnwindSafe for crate::sync::atomic::AtomicU128 {} - + #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs -index d9de37e..8293fce 100644 +index bf2b6d59f88..d5ccce03bbf 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs -@@ -2996,44 +2996,6 @@ atomic_int! { +@@ -3585,44 +3585,6 @@ pub const fn as_ptr(&self) -> *mut $int_type { 8, u64 AtomicU64 } @@ -54,7 +53,7 @@ index d9de37e..8293fce 100644 - unstable(feature = "integer_atomics", issue = "99069"), - rustc_const_unstable(feature = "integer_atomics", issue = "99069"), - rustc_const_unstable(feature = "integer_atomics", issue = "99069"), -- cfg_attr(not(test), rustc_diagnostic_item = "AtomicI128"), +- rustc_diagnostic_item = "AtomicI128", - "i128", - "#![feature(integer_atomics)]\n\n", - atomic_min, atomic_max, @@ -73,7 +72,7 @@ index d9de37e..8293fce 100644 - unstable(feature = "integer_atomics", issue = "99069"), - rustc_const_unstable(feature = "integer_atomics", issue = "99069"), - rustc_const_unstable(feature = "integer_atomics", issue = "99069"), -- cfg_attr(not(test), rustc_diagnostic_item = "AtomicU128"), +- rustc_diagnostic_item = "AtomicU128", - "u128", - "#![feature(integer_atomics)]\n\n", - atomic_umin, atomic_umax, @@ -83,7 +82,6 @@ index d9de37e..8293fce 100644 #[cfg(target_has_atomic_load_store = "ptr")] macro_rules! atomic_int_ptr_sized { - ( $($target_pointer_width:literal $align:literal)* ) => { $( --- -2.26.2.7.g19db9cfb68 +-- +2.48.1 diff --git a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch new file mode 100644 index 0000000000000..16c8488acdb56 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch @@ -0,0 +1,40 @@ +From ad7ffe71baba46865f2e65266ab025920dfdc20b Mon Sep 17 00:00:00 2001 +From: bjorn3 +Date: Thu, 18 Feb 2021 18:45:28 +0100 +Subject: [PATCH] Disable 128bit atomic operations + +Cranelift doesn't support them yet +--- + library/core/src/panic/unwind_safe.rs | 6 ----- + library/core/src/sync/atomic.rs | 38 --------------------------- + library/core/tests/atomic.rs | 4 --- + 4 files changed, 4 insertions(+), 50 deletions(-) + +diff --git a/coretests/tests/lib.rs b/coretests/tests/lib.rs +index 1e336bf..35e6f54 100644 +--- a/coretests/tests/lib.rs ++++ b/coretests/tests/lib.rs +@@ -2,5 +2,4 @@ + // tidy-alphabetical-start +-#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] + #![cfg_attr(test, feature(cfg_match))] + #![feature(alloc_layout_extra)] + #![feature(array_chunks)] +diff --git a/coretests/tests/atomic.rs b/coretests/tests/atomic.rs +index b735957..ea728b6 100644 +--- a/coretests/tests/atomic.rs ++++ b/coretests/tests/atomic.rs +@@ -185,10 +185,6 @@ fn atomic_alignment() { + assert_eq!(align_of::(), size_of::()); + #[cfg(target_has_atomic = "64")] + assert_eq!(align_of::(), size_of::()); +- #[cfg(target_has_atomic = "128")] +- assert_eq!(align_of::(), size_of::()); +- #[cfg(target_has_atomic = "128")] +- assert_eq!(align_of::(), size_of::()); + #[cfg(target_has_atomic = "ptr")] + assert_eq!(align_of::(), size_of::()); + #[cfg(target_has_atomic = "ptr")] +-- +2.26.2.7.g19db9cfb68 + diff --git a/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch b/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch deleted file mode 100644 index f5ae66c0eb13d..0000000000000 --- a/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch +++ /dev/null @@ -1,48 +0,0 @@ -From eb703e627e7a84f1cd8d0d87f0f69da1f0acf765 Mon Sep 17 00:00:00 2001 -From: bjorn3 -Date: Fri, 3 Dec 2021 12:16:30 +0100 -Subject: [PATCH] Disable long running tests - ---- - library/core/tests/slice.rs | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/tests/slice.rs b/tests/slice.rs -index 8402833..84592e0 100644 ---- a/tests/slice.rs -+++ b/tests/slice.rs -@@ -1809,6 +1809,7 @@ fn sort_unstable() { - } - } - -+/* - #[test] - #[cfg(not(target_arch = "wasm32"))] - #[cfg_attr(miri, ignore)] // Miri is too slow -@@ -1914,6 +1915,7 @@ fn select_nth_unstable() { - v.select_nth_unstable(0); - assert!(v == [0xDEADBEEF]); - } -+*/ - - #[test] - #[should_panic(expected = "index 0 greater than length of slice")] -@@ -2462,6 +2462,7 @@ take_tests! { - #[cfg(not(miri))] // unused in Miri - const EMPTY_MAX: &'static [()] = &[(); usize::MAX]; - -+/* - // can't be a constant due to const mutability rules - #[cfg(not(miri))] // unused in Miri - macro_rules! empty_max_mut { -@@ -2485,6 +2486,7 @@ take_tests! { - (split_off_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), - (split_off_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), - } -+*/ - - #[test] - fn test_slice_from_ptr_range() { --- -2.26.2.7.g19db9cfb68 - diff --git a/compiler/rustc_codegen_cranelift/patches/0028-sysroot_tests-Disable-long-running-tests.patch b/compiler/rustc_codegen_cranelift/patches/0028-sysroot_tests-Disable-long-running-tests.patch new file mode 100644 index 0000000000000..357b8d306cf6a --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0028-sysroot_tests-Disable-long-running-tests.patch @@ -0,0 +1,105 @@ +From eb703e627e7a84f1cd8d0d87f0f69da1f0acf765 Mon Sep 17 00:00:00 2001 +From: bjorn3 +Date: Fri, 3 Dec 2021 12:16:30 +0100 +Subject: [PATCH] Disable long running tests + +--- + library/coretests/tests/slice.rs | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/coretests/tests/slice.rs b/coretests/tests/slice.rs +index 8402833..84592e0 100644 +--- a/coretests/tests/slice.rs ++++ b/coretests/tests/slice.rs +@@ -1809,6 +1809,7 @@ fn sort_unstable() { + } + } + ++/* + #[test] + #[cfg(not(target_arch = "wasm32"))] + #[cfg_attr(miri, ignore)] // Miri is too slow +@@ -1914,6 +1915,7 @@ fn select_nth_unstable() { + v.select_nth_unstable(0); + assert!(v == [0xDEADBEEF]); + } ++*/ + + #[test] + #[should_panic(expected = "index 0 greater than length of slice")] +@@ -2462,6 +2462,7 @@ take_tests! { + #[cfg(not(miri))] // unused in Miri + const EMPTY_MAX: &'static [()] = &[(); usize::MAX]; + ++/* + // can't be a constant due to const mutability rules + #[cfg(not(miri))] // unused in Miri + macro_rules! empty_max_mut { +@@ -2485,6 +2486,7 @@ take_tests! { + (split_off_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), + (split_off_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), + } ++*/ + + #[test] + fn test_slice_from_ptr_range() { +diff --git a/alloctests/tests/sort/tests.rs b/alloctests/tests/sort/tests.rs +index d321f8d..8b2040a 100644 +--- a/alloctests/tests/sort/tests.rs ++++ b/alloctests/tests/sort/tests.rs +@@ -1,3 +1,5 @@ ++#![cfg(any())] ++ + use std::cell::Cell; + use std::cmp::Ordering; + use std::fmt::Debug; +diff --git a/alloctests/tests/str.rs b/alloctests/tests/str.rs +index 906fa2d..b82fa99 100644 +--- a/alloctests/tests/str.rs ++++ b/alloctests/tests/str.rs +@@ -2234,7 +2234,7 @@ fn const_str_ptr() { + const C: *const u8 = B as *const u8; + + // Miri does not deduplicate consts (https://github.com/rust-lang/miri/issues/131) +- #[cfg(not(miri))] ++ #[cfg(any())] + { + let foo = &A as *const u8; + assert_eq!(foo, C); +diff --git a/alloctests/tests/task.rs b/alloctests/tests/task.rs +index 390dec1..87df6e6 100644 +--- a/alloctests/tests/task.rs ++++ b/alloctests/tests/task.rs +@@ -4,7 +4,7 @@ use alloc::task::{LocalWake, Wake}; + use core::task::{LocalWaker, Waker}; + + #[test] +-#[cfg_attr(miri, ignore)] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it can fail ++#[ignore] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it can fail + fn test_waker_will_wake_clone() { + struct NoopWaker; + +@@ -20,7 +20,7 @@ fn test_waker_will_wake_clone() { + } + + #[test] +-#[cfg_attr(miri, ignore)] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it can fail ++#[ignore] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it can fail + fn test_local_waker_will_wake_clone() { + struct NoopWaker; + +diff --git a/alloctests/tests/vec.rs b/alloctests/tests/vec.rs +index f430d97..cfbd3cb 100644 +--- a/alloctests/tests/vec.rs ++++ b/alloctests/tests/vec.rs +@@ -762,6 +762,7 @@ fn test_drain_inclusive_range() { + } + + #[test] ++#[ignore] + fn test_drain_max_vec_size() { + let mut v = Vec::<()>::with_capacity(usize::MAX); + unsafe { +-- +2.26.2.7.g19db9cfb68 + diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch deleted file mode 100644 index bedc6ca11b3f6..0000000000000 --- a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 175d52c5e1779764b66777db1e6f172c2dc365ff Mon Sep 17 00:00:00 2001 -From: bjorn3 <17426603+bjorn3@users.noreply.github.com> -Date: Fri, 9 Aug 2024 15:44:51 +0000 -Subject: [PATCH] Disable f16 and f128 in compiler-builtins - ---- - library/liballoc/Cargo.toml | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/library/liballoc/Cargo.toml b/library/liballoc/Cargo.toml -index 7165c3e48af..968552ad435 100644 ---- a/library/alloc/Cargo.toml -+++ b/library/alloc/Cargo.toml -@@ -11,7 +11,7 @@ test = { path = "../test" } - edition = "2021" - - [dependencies] - core = { path = "../core", public = true } --compiler_builtins = { version = "=0.1.148", features = ['rustc-dep-of-std'] } -+compiler_builtins = { version = "=0.1.148", features = ['rustc-dep-of-std', 'no-f16-f128'] } - - [dev-dependencies] - rand = { version = "0.8.5", default-features = false, features = ["alloc"] } --- -2.34.1 - diff --git a/compiler/rustc_codegen_cranelift/patches/coretests-lock.toml b/compiler/rustc_codegen_cranelift/patches/coretests-lock.toml deleted file mode 100644 index af8f28a193bcd..0000000000000 --- a/compiler/rustc_codegen_cranelift/patches/coretests-lock.toml +++ /dev/null @@ -1,35 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "coretests" -version = "0.0.0" -dependencies = [ - "rand", - "rand_xorshift", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index 481903c6afb28..ceff15b1180a7 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2025-02-15" +channel = "nightly-2025-03-30" components = ["rust-src", "rustc-dev", "llvm-tools"] profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/rustfmt.toml b/compiler/rustc_codegen_cranelift/rustfmt.toml index f31fa9c76abc6..35c92663eb904 100644 --- a/compiler/rustc_codegen_cranelift/rustfmt.toml +++ b/compiler/rustc_codegen_cranelift/rustfmt.toml @@ -1,7 +1,3 @@ -ignore = [ - "example/gen_block_iterate.rs", # uses edition 2024 -] - # Matches rustfmt.toml of rustc style_edition = "2024" use_small_heuristics = "Max" diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs index ebbb687961058..e6c63bf5e6508 100644 --- a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs @@ -50,19 +50,7 @@ fn main() { .chain([ "--".to_string(), "-Zunstable-options".to_string(), - "-Cllvm-args=mode=jit".to_string(), - ]) - .collect() - } - Some("lazy-jit") => { - rustflags.push("-Cprefer-dynamic".to_owned()); - args.remove(0); - IntoIterator::into_iter(["rustc".to_string()]) - .chain(args) - .chain([ - "--".to_string(), - "-Zunstable-options".to_string(), - "-Cllvm-args=mode=jit-lazy".to_string(), + "-Cllvm-args=jit-mode".to_string(), ]) .collect() } diff --git a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs index 0252d5b334036..4595063c032dc 100755 --- a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs +++ b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs @@ -4,7 +4,7 @@ pushd $(dirname "$0")/../ RUSTC="$(pwd)/dist/rustc-clif" popd -PROFILE=$1 OUTPUT=$2 exec $RUSTC -Zunstable-options -Cllvm-args=mode=jit -Cprefer-dynamic $0 +PROFILE=$1 OUTPUT=$2 exec $RUSTC -Zunstable-options -Cllvm-args=jit-mode -Cprefer-dynamic $0 #*/ //! This program filters away uninteresting samples and trims uninteresting frames for stackcollapse diff --git a/compiler/rustc_codegen_cranelift/scripts/rustup.sh b/compiler/rustc_codegen_cranelift/scripts/rustup.sh index 355282911c255..152c243aa6adc 100755 --- a/compiler/rustc_codegen_cranelift/scripts/rustup.sh +++ b/compiler/rustc_codegen_cranelift/scripts/rustup.sh @@ -64,7 +64,7 @@ case $1 in cg_clif=$(pwd) pushd ../rust git fetch origin master - git checkout "$RUST_VERS" + git -c advice.detachedHead=false checkout "$RUST_VERS" "$cg_clif/git-fixed-subtree.sh" push --prefix=compiler/rustc_codegen_cranelift/ "$cg_clif" sync_from_rust popd git merge sync_from_rust -m "Sync from rust $RUST_VERS" diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index 54f6baff4fecf..ca6426f2ba9d3 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -43,8 +43,31 @@ verbose-tests = false # disabled bootstrap will crash trying to copy llvm tools for the bootstrap # compiler. llvm-tools = false +std-features = ["panic-unwind", "compiler-builtins-no-f16-f128"] EOF + +cat <128bits not yet supported # requires LTO rm -r tests/run-make/cdylib @@ -120,6 +118,7 @@ rm tests/ui/mir/mir_raw_fat_ptr.rs # same rm tests/ui/consts/issue-33537.rs # same rm tests/ui/consts/const-mut-refs-crate.rs # same rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift +rm tests/ui/abi/simd-abi-checks-avx.rs # attempts to declare function with two different signatures # doesn't work due to the way the rustc test suite is invoked. # should work when using ./x.py test the way it is intended @@ -136,7 +135,6 @@ rm -r tests/run-make/incr-add-rust-src-component # ============ rm -r tests/run-make/extern-fn-explicit-align # argument alignment not yet supported rm -r tests/run-make/panic-abort-eh_frame # .eh_frame emitted with panic=abort -rm tests/ui/deprecation/deprecated_inline_threshold.rs # missing deprecation warning for -Cinline-threshold # bugs in the test suite # ====================== @@ -150,48 +148,8 @@ rm tests/ui/intrinsics/panic-uninitialized-zeroed.rs # same rm tests/ui/process/process-panic-after-fork.rs # same cp ../dist/bin/rustdoc-clif ../dist/bin/rustdoc # some tests expect bin/rustdoc to exist -cp $(../dist/rustc-clif --print target-libdir)/libstd-*.so ../dist/lib/ -# prevent $(RUSTDOC) from picking up the sysroot built by x.py. It conflicts with the one used by -# rustdoc-clif cat < Self { - #[track_caller] - pub fn new() -> Self { - let mut cmd = setup_common(); -- cmd.arg("-L").arg(env_var_os("TARGET_RPATH_DIR")); - Self { cmd } - } - -diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs -index e7ae773ffa1d3..04bc2d7787da7 100644 ---- a/src/tools/compiletest/src/runtest/run_make.rs -+++ b/src/tools/compiletest/src/runtest/run_make.rs -@@ -329,7 +329,6 @@ impl TestCx<'_> { - .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy())) - .arg("--edition=2021") - .arg(&self.testpaths.file.join("rmake.rs")) -- .arg("-Cprefer-dynamic") - // Provide necessary library search paths for rustc. - .env(dylib_env_var(), &env::join_paths(host_dylib_search_paths).unwrap()); - diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs index 30387af428c..f7895b12961 100644 --- a/tests/run-make/linker-warning/rmake.rs @@ -205,7 +163,19 @@ index 30387af428c..f7895b12961 100644 regex::escape(run_make_support::build_root().to_str().unwrap()), "/build-root", ) - .run(); + .normalize(r#""[^"]*\/symbols.o""#, "\\"/symbols.o\\"") +diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs +index 073116933bd..c3e4578204d 100644 +--- a/src/tools/compiletest/src/runtest/run_make.rs ++++ b/src/tools/compiletest/src/runtest/run_make.rs +@@ -109,7 +109,6 @@ pub(super) fn run_rmake_test(&self) { + // library or compiler features. Here, we force the stage 0 rustc to consider itself as + // a stable-channel compiler via \`RUSTC_BOOTSTRAP=-1\` to prevent *any* unstable + // library/compiler usages, even if stage 0 rustc is *actually* a nightly rustc. +- .env("RUSTC_BOOTSTRAP", "-1") + .arg("-o") + .arg(&recipe_bin) + // Specify library search paths for \`run_make_support\`. EOF echo "[TEST] rustc test suite" diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index e8076ce77abcf..5f631405a9a4a 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -441,7 +441,9 @@ pub(crate) fn codegen_terminator_call<'tcx>( Err(instance) => Some(instance), } } - InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) => { + // We don't need AsyncDropGlueCtorShim here because it is not `noop func`, + // it is `func returning noop future` + InstanceKind::DropGlue(_, None) => { // empty drop glue - a nop. let dest = target.expect("Non terminating drop_in_place_real???"); let ret_block = fx.get_block(dest); @@ -641,7 +643,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( .flat_map(|arg_abi| arg_abi.get_abi_param(fx.tcx).into_iter()), ); - if fx.tcx.sess.target.is_like_osx && fx.tcx.sess.target.arch == "aarch64" { + if fx.tcx.sess.target.is_like_darwin && fx.tcx.sess.target.arch == "aarch64" { // Add any padding arguments needed for Apple AArch64. // There's no need to pad the argument list unless variadic arguments are actually being // passed. @@ -707,9 +709,8 @@ pub(crate) fn codegen_drop<'tcx>( let ty = drop_place.layout().ty; let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty); - if let ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) = - drop_instance.def - { + // AsyncDropGlueCtorShim can't be here + if let ty::InstanceKind::DropGlue(_, None) = drop_instance.def { // we don't actually need to drop anything } else { match ty.kind() { diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index 5e33b9d606fa5..9cff8a84db3d9 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -7,6 +7,7 @@ use rustc_ast::expand::allocator::{ }; use rustc_codegen_ssa::base::allocator_kind_for_codegen; use rustc_session::config::OomStrategy; +use rustc_symbol_mangling::mangle_internal_symbol; use crate::prelude::*; @@ -14,6 +15,7 @@ use crate::prelude::*; pub(crate) fn codegen(tcx: TyCtxt<'_>, module: &mut dyn Module) -> bool { let Some(kind) = allocator_kind_for_codegen(tcx) else { return false }; codegen_inner( + tcx, module, kind, tcx.alloc_error_handler_kind(()).unwrap(), @@ -23,6 +25,7 @@ pub(crate) fn codegen(tcx: TyCtxt<'_>, module: &mut dyn Module) -> bool { } fn codegen_inner( + tcx: TyCtxt<'_>, module: &mut dyn Module, kind: AllocatorKind, alloc_error_handler_kind: AllocatorKind, @@ -62,8 +65,8 @@ fn codegen_inner( crate::common::create_wrapper_function( module, sig, - &global_fn_name(method.name), - &default_fn_name(method.name), + &mangle_internal_symbol(tcx, &global_fn_name(method.name)), + &mangle_internal_symbol(tcx, &default_fn_name(method.name)), ); } } @@ -76,19 +79,32 @@ fn codegen_inner( crate::common::create_wrapper_function( module, sig, - "__rust_alloc_error_handler", - &alloc_error_handler_name(alloc_error_handler_kind), + &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), + &mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind)), ); - let data_id = module.declare_data(OomStrategy::SYMBOL, Linkage::Export, false, false).unwrap(); + let data_id = module + .declare_data( + &mangle_internal_symbol(tcx, OomStrategy::SYMBOL), + Linkage::Export, + false, + false, + ) + .unwrap(); let mut data = DataDescription::new(); data.set_align(1); let val = oom_strategy.should_panic(); data.define(Box::new([val])); module.define_data(data_id, &data).unwrap(); - let data_id = - module.declare_data(NO_ALLOC_SHIM_IS_UNSTABLE, Linkage::Export, false, false).unwrap(); + let data_id = module + .declare_data( + &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), + Linkage::Export, + false, + false, + ) + .unwrap(); let mut data = DataDescription::new(); data.set_align(1); data.define(Box::new([0])); diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 125a9201831ca..524e0d9fe35ed 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -8,8 +8,6 @@ use rustc_ast::InlineAsmOptions; use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_index::IndexVec; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mir::InlineAsmMacro; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv}; @@ -18,7 +16,6 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use crate::constant::ConstantCx; use crate::debuginfo::{FunctionDebugContext, TypeDebugContext}; use crate::enable_verifier; -use crate::inline_asm::codegen_naked_asm; use crate::prelude::*; use crate::pretty_clif::CommentWriter; @@ -37,7 +34,7 @@ pub(crate) fn codegen_fn<'tcx>( cached_func: Function, module: &mut dyn Module, instance: Instance<'tcx>, -) -> Option { +) -> CodegenedFunction { debug_assert!(!instance.args.has_infer()); let symbol_name = tcx.symbol_name(instance).name.to_string(); @@ -54,38 +51,6 @@ pub(crate) fn codegen_fn<'tcx>( String::from_utf8_lossy(&buf).into_owned() }); - if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) { - assert_eq!(mir.basic_blocks.len(), 1); - assert!(mir.basic_blocks[START_BLOCK].statements.is_empty()); - - match &mir.basic_blocks[START_BLOCK].terminator().kind { - TerminatorKind::InlineAsm { - asm_macro: InlineAsmMacro::NakedAsm, - template, - operands, - options, - line_spans: _, - targets: _, - unwind: _, - } => { - codegen_naked_asm( - tcx, - cx, - module, - instance, - mir.basic_blocks[START_BLOCK].terminator().source_info.span, - &symbol_name, - template, - operands, - *options, - ); - } - _ => unreachable!(), - } - - return None; - } - // Declare function let sig = get_function_sig(tcx, module.target_config().default_call_conv, instance); let func_id = module.declare_function(&symbol_name, Linkage::Local, &sig).unwrap(); @@ -166,7 +131,7 @@ pub(crate) fn codegen_fn<'tcx>( // Verify function verify_func(tcx, &clif_comments, &func); - Some(CodegenedFunction { symbol_name, func_id, func, clif_comments, func_debug_cx }) + CodegenedFunction { symbol_name, func_id, func, clif_comments, func_debug_cx } } pub(crate) fn compile_fn( @@ -565,7 +530,11 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { | TerminatorKind::CoroutineDrop => { bug!("shouldn't exist at codegen {:?}", bb_data.terminator()); } - TerminatorKind::Drop { place, target, unwind: _, replace: _ } => { + TerminatorKind::Drop { place, target, unwind: _, replace: _, drop, async_fut } => { + assert!( + async_fut.is_none() && drop.is_none(), + "Async Drop must be expanded or reset to sync before codegen" + ); let drop_place = codegen_place(fx, *place); crate::abi::codegen_drop(fx, source_info, drop_place, *target); } @@ -729,8 +698,10 @@ fn codegen_stmt<'tcx>( let to_ty = fx.monomorphize(to_ty); fn is_wide_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.builtin_deref(true) - .is_some_and(|pointee_ty| has_ptr_meta(fx.tcx, pointee_ty)) + ty.builtin_deref(true).is_some_and(|pointee_ty| { + fx.tcx + .type_has_metadata(pointee_ty, ty::TypingEnv::fully_monomorphized()) + }) } if is_wide_ptr(fx, from_ty) { diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 766278d87183b..abe2972ba0cbd 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -71,7 +71,7 @@ fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option pointer_ty(tcx), ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) => { - if has_ptr_meta(tcx, *pointee_ty) { + if tcx.type_has_metadata(*pointee_ty, ty::TypingEnv::fully_monomorphized()) { return None; } else { pointer_ty(tcx) @@ -91,7 +91,7 @@ fn clif_pair_type_from_ty<'tcx>( (clif_type_from_ty(tcx, types[0])?, clif_type_from_ty(tcx, types[1])?) } ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) => { - if has_ptr_meta(tcx, *pointee_ty) { + if tcx.type_has_metadata(*pointee_ty, ty::TypingEnv::fully_monomorphized()) { (pointer_ty(tcx), pointer_ty(tcx)) } else { return None; @@ -101,20 +101,6 @@ fn clif_pair_type_from_ty<'tcx>( }) } -/// Is a pointer to this type a wide ptr? -pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { - if ty.is_sized(tcx, ty::TypingEnv::fully_monomorphized()) { - return false; - } - - let tail = tcx.struct_tail_for_codegen(ty, ty::TypingEnv::fully_monomorphized()); - match tail.kind() { - ty::Foreign(..) => false, - ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, - _ => bug!("unexpected unsized tail: {:?}", tail), - } -} - pub(crate) fn codegen_icmp_imm( fx: &mut FunctionCx<'_, '_, '_>, intcc: IntCC, diff --git a/compiler/rustc_codegen_cranelift/src/config.rs b/compiler/rustc_codegen_cranelift/src/config.rs index d784f6e9d9eb7..d328b33a704f5 100644 --- a/compiler/rustc_codegen_cranelift/src/config.rs +++ b/compiler/rustc_codegen_cranelift/src/config.rs @@ -1,21 +1,10 @@ -/// The mode to use for compilation. -#[derive(Copy, Clone, Debug)] -pub enum CodegenMode { - /// AOT compile the crate. This is the default. - Aot, - /// JIT compile and execute the crate. - Jit, - /// JIT compile and execute the crate, but only compile functions the first time they are used. - JitLazy, -} - /// Configuration of cg_clif as passed in through `-Cllvm-args` and various env vars. #[derive(Clone, Debug)] pub struct BackendConfig { /// Should the crate be AOT compiled or JIT executed. /// - /// Defaults to AOT compilation. Can be set using `-Cllvm-args=mode=...`. - pub codegen_mode: CodegenMode, + /// Defaults to AOT compilation. Can be set using `-Cllvm-args=jit-mode`. + pub jit_mode: bool, /// When JIT mode is enable pass these arguments to the program. /// @@ -27,7 +16,7 @@ impl BackendConfig { /// Parse the configuration passed in using `-Cllvm-args`. pub fn from_opts(opts: &[String]) -> Result { let mut config = BackendConfig { - codegen_mode: CodegenMode::Aot, + jit_mode: false, jit_args: match std::env::var("CG_CLIF_JIT_ARGS") { Ok(args) => args.split(' ').map(|arg| arg.to_string()).collect(), Err(std::env::VarError::NotPresent) => vec![], @@ -43,20 +32,9 @@ impl BackendConfig { // testing cg_clif. continue; } - if let Some((name, value)) = opt.split_once('=') { - match name { - "mode" => { - config.codegen_mode = match value { - "aot" => CodegenMode::Aot, - "jit" => CodegenMode::Jit, - "jit-lazy" => CodegenMode::JitLazy, - _ => return Err(format!("Unknown codegen mode `{}`", value)), - }; - } - _ => return Err(format!("Unknown option `{}`", name)), - } - } else { - return Err(format!("Invalid option `{}`", opt)); + match &**opt { + "jit-mode" => config.jit_mode = true, + _ => return Err(format!("Unknown option `{}`", opt)), } } diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index bcc70f4567fbd..c8527c3a57dfe 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -391,7 +391,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant data.set_align(alloc.align.bytes()); if let Some(section_name) = section_name { - let (segment_name, section_name) = if tcx.sess.target.is_like_osx { + let (segment_name, section_name) = if tcx.sess.target.is_like_darwin { // See https://github.com/llvm/llvm-project/blob/main/llvm/lib/MC/MCSectionMachO.cpp let mut parts = section_name.as_str().split(','); let Some(segment_name) = parts.next() else { diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index bba6567774d7e..286e02b986b3c 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -58,7 +58,7 @@ impl DebugContext { // FIXME this should be configurable // macOS doesn't seem to support DWARF > 3 // 5 version is required for md5 file hash - version: if tcx.sess.target.is_like_osx { + version: if tcx.sess.target.is_like_darwin { 3 } else { // FIXME change to version 5 once the gdb and lldb shipping with the latest debian diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs index 017d7784dc032..25b922c8be4c7 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty, TyCtxt}; -use crate::{DebugContext, FullyMonomorphizedLayoutCx, has_ptr_meta}; +use crate::{DebugContext, FullyMonomorphizedLayoutCx}; #[derive(Default)] pub(crate) struct TypeDebugContext<'tcx> { @@ -129,7 +129,7 @@ impl DebugContext { let name = type_names::compute_debuginfo_type_name(tcx, ptr_type, true); - if !has_ptr_meta(tcx, ptr_type) { + if !tcx.type_has_metadata(ptr_type, ty::TypingEnv::fully_monomorphized()) { let pointer_type_id = self.dwarf.unit.add(self.dwarf.unit.root(), gimli::DW_TAG_pointer_type); let pointer_entry = self.dwarf.unit.get_mut(pointer_type_id); diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index a52b18573b152..5d07c94859f33 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -22,7 +22,10 @@ use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_metadata::EncodedMetadata; use rustc_metadata::fs::copy_to_stdout; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; -use rustc_middle::mir::mono::{CodegenUnit, MonoItem}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::mono::{ + CodegenUnit, Linkage as RLinkage, MonoItem, MonoItemData, Visibility, +}; use rustc_session::Session; use rustc_session::config::{DebugInfo, OutFileName, OutputFilenames, OutputType}; @@ -30,7 +33,7 @@ use crate::CodegenCx; use crate::base::CodegenedFunction; use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken}; use crate::debuginfo::TypeDebugContext; -use crate::global_asm::GlobalAsmConfig; +use crate::global_asm::{GlobalAsmConfig, GlobalAsmContext}; use crate::prelude::*; use crate::unwind_module::UnwindModule; @@ -103,12 +106,14 @@ impl OngoingCodegen { ("o", &module_regular.object.as_ref().unwrap()), ("asm.o", &module_global_asm.object.as_ref().unwrap()), ], + &[], ) } else { rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( sess, &module_regular.name, &[("o", &module_regular.object.as_ref().unwrap())], + &[], ) }; if let Some((work_product_id, work_product)) = work_product { @@ -167,8 +172,11 @@ fn produce_final_output_artifacts( if codegen_results.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(&codegen_results.modules[0].name[..]); - let path = crate_output.temp_path(output_type, module_name); + let path = crate_output.temp_path_for_cgu( + output_type, + &codegen_results.modules[0].name, + sess.invocation_temp.as_deref(), + ); let output = crate_output.path(output_type); if !output_type.is_text_output() && output.is_tty() { sess.dcx() @@ -181,22 +189,16 @@ fn produce_final_output_artifacts( ensure_removed(sess.dcx(), &path); } } else { - let extension = crate_output - .temp_path(output_type, None) - .extension() - .unwrap() - .to_str() - .unwrap() - .to_owned(); - if crate_output.outputs.contains_explicit_name(&output_type) { // 2) Multiple codegen units, with `--emit foo=some_name`. We have // no good solution for this case, so warn the user. - sess.dcx().emit_warn(ssa_errors::IgnoringEmitPath { extension }); + sess.dcx() + .emit_warn(ssa_errors::IgnoringEmitPath { extension: output_type.extension() }); } 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.dcx().emit_warn(ssa_errors::IgnoringOutput { extension }); + sess.dcx() + .emit_warn(ssa_errors::IgnoringOutput { extension: output_type.extension() }); } else { // 4) Multiple codegen units, but no explicit name. We // just leave the `foo.0.x` files in place. @@ -329,7 +331,7 @@ fn produce_final_output_artifacts( } fn make_module(sess: &Session, name: String) -> UnwindModule { - let isa = crate::build_isa(sess); + let isa = crate::build_isa(sess, false); let mut builder = ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap(); @@ -349,6 +351,7 @@ fn make_module(sess: &Session, name: String) -> UnwindModule { fn emit_cgu( output_filenames: &OutputFilenames, + invocation_temp: Option<&str>, prof: &SelfProfilerRef, name: String, module: UnwindModule, @@ -364,6 +367,7 @@ fn emit_cgu( let module_regular = emit_module( output_filenames, + invocation_temp, prof, product.object, ModuleKind::Regular, @@ -381,6 +385,7 @@ fn emit_cgu( bytecode: None, assembly: None, llvm_ir: None, + links_from_incr_cache: Vec::new(), }), existing_work_product: None, }) @@ -388,6 +393,7 @@ fn emit_cgu( fn emit_module( output_filenames: &OutputFilenames, + invocation_temp: Option<&str>, prof: &SelfProfilerRef, mut object: cranelift_object::object::write::Object<'_>, kind: ModuleKind, @@ -406,7 +412,7 @@ fn emit_module( object.set_section_data(comment_section, producer, 1); } - let tmp_file = output_filenames.temp_path(OutputType::Object, Some(&name)); + let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name, invocation_temp); let file = match File::create(&tmp_file) { Ok(file) => file, Err(err) => return Err(format!("error creating object file: {}", err)), @@ -437,6 +443,7 @@ fn emit_module( bytecode: None, assembly: None, llvm_ir: None, + links_from_incr_cache: Vec::new(), }) } @@ -445,8 +452,11 @@ fn reuse_workproduct_for_cgu( cgu: &CodegenUnit<'_>, ) -> Result { let work_product = cgu.previous_work_product(tcx); - let obj_out_regular = - tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu.name().as_str())); + let obj_out_regular = tcx.output_filenames(()).temp_path_for_cgu( + OutputType::Object, + cgu.name().as_str(), + tcx.sess.invocation_temp.as_deref(), + ); let source_file_regular = rustc_incremental::in_incr_comp_dir_sess( &tcx.sess, &work_product.saved_files.get("o").expect("no saved object file in work product"), @@ -460,22 +470,23 @@ fn reuse_workproduct_for_cgu( err )); } + let obj_out_global_asm = crate::global_asm::add_file_stem_postfix(obj_out_regular.clone(), ".asm"); - let has_global_asm = if let Some(asm_o) = work_product.saved_files.get("asm.o") { + let source_file_global_asm = if let Some(asm_o) = work_product.saved_files.get("asm.o") { let source_file_global_asm = rustc_incremental::in_incr_comp_dir_sess(&tcx.sess, asm_o); if let Err(err) = rustc_fs_util::link_or_copy(&source_file_global_asm, &obj_out_global_asm) { return Err(format!( "unable to copy {} to {}: {}", - source_file_regular.display(), - obj_out_regular.display(), + source_file_global_asm.display(), + obj_out_global_asm.display(), err )); } - true + Some(source_file_global_asm) } else { - false + None }; Ok(ModuleCodegenResult { @@ -487,8 +498,9 @@ fn reuse_workproduct_for_cgu( bytecode: None, assembly: None, llvm_ir: None, + links_from_incr_cache: vec![source_file_regular], }, - module_global_asm: has_global_asm.then(|| CompiledModule { + module_global_asm: source_file_global_asm.map(|source_file| CompiledModule { name: cgu.name().to_string(), kind: ModuleKind::Regular, object: Some(obj_out_global_asm), @@ -496,6 +508,7 @@ fn reuse_workproduct_for_cgu( bytecode: None, assembly: None, llvm_ir: None, + links_from_incr_cache: vec![source_file], }), existing_work_product: Some((cgu.work_product_id(), work_product)), }) @@ -520,19 +533,35 @@ fn codegen_cgu_content( let mut type_dbg = TypeDebugContext::default(); super::predefine_mono_items(tcx, module, &mono_items); let mut codegened_functions = vec![]; - for (mono_item, _) in mono_items { + for (mono_item, item_data) in mono_items { match mono_item { - MonoItem::Fn(inst) => { - if let Some(codegened_function) = crate::base::codegen_fn( + MonoItem::Fn(instance) => { + if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) + { + rustc_codegen_ssa::mir::naked_asm::codegen_naked_asm( + &mut GlobalAsmContext { tcx, global_asm: &mut cx.global_asm }, + instance, + MonoItemData { + linkage: RLinkage::External, + visibility: if item_data.linkage == RLinkage::Internal { + Visibility::Hidden + } else { + item_data.visibility + }, + ..item_data + }, + ); + continue; + } + let codegened_function = crate::base::codegen_fn( tcx, &mut cx, &mut type_dbg, Function::new(), module, - inst, - ) { - codegened_functions.push(codegened_function); - } + instance, + ); + codegened_functions.push(codegened_function); } MonoItem::Static(def_id) => { let data_id = crate::constant::codegen_static(tcx, module, def_id); @@ -541,7 +570,10 @@ fn codegen_cgu_content( } } MonoItem::GlobalAsm(item_id) => { - crate::global_asm::codegen_global_asm_item(tcx, &mut cx.global_asm, item_id); + rustc_codegen_ssa::base::codegen_global_asm( + &mut GlobalAsmContext { tcx, global_asm: &mut cx.global_asm }, + item_id, + ); } } } @@ -588,13 +620,19 @@ fn module_codegen( let global_asm_object_file = profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| { - crate::global_asm::compile_global_asm(&global_asm_config, &cgu_name, &cx.global_asm) + crate::global_asm::compile_global_asm( + &global_asm_config, + &cgu_name, + &cx.global_asm, + cx.invocation_temp.as_deref(), + ) })?; let codegen_result = profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| { emit_cgu( &global_asm_config.output_filenames, + cx.invocation_temp.as_deref(), &profiler, cgu_name, module, @@ -619,8 +657,11 @@ fn emit_metadata_module(tcx: TyCtxt<'_>, metadata: &EncodedMetadata) -> Compiled .as_str() .to_string(); - let tmp_file = - tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name)); + let tmp_file = tcx.output_filenames(()).temp_path_for_cgu( + OutputType::Metadata, + &metadata_cgu_name, + tcx.sess.invocation_temp.as_deref(), + ); let symbol_name = rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx); let obj = create_compressed_metadata_file(tcx.sess, metadata, &symbol_name); @@ -637,6 +678,7 @@ fn emit_metadata_module(tcx: TyCtxt<'_>, metadata: &EncodedMetadata) -> Compiled bytecode: None, assembly: None, llvm_ir: None, + links_from_incr_cache: Vec::new(), } } @@ -649,6 +691,7 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { match emit_module( tcx.output_filenames(()), + tcx.sess.invocation_temp.as_deref(), &tcx.sess.prof, product.object, ModuleKind::Allocator, @@ -720,26 +763,27 @@ pub(crate) fn run_aot( let concurrency_limiter = IntoDynSyncSend(ConcurrencyLimiter::new(todo_cgus.len())); - let modules = tcx.sess.time("codegen mono items", || { - let mut modules: Vec<_> = par_map(todo_cgus, |(_, cgu)| { - let dep_node = cgu.codegen_dep_node(tcx); - tcx.dep_graph - .with_task( + let modules: Vec<_> = + tcx.sess.time("codegen mono items", || { + let modules: Vec<_> = par_map(todo_cgus, |(_, cgu)| { + let dep_node = cgu.codegen_dep_node(tcx); + let (module, _) = tcx.dep_graph.with_task( dep_node, tcx, (global_asm_config.clone(), cgu.name(), concurrency_limiter.acquire(tcx.dcx())), module_codegen, Some(rustc_middle::dep_graph::hash_result), - ) - .0 - }); - modules.extend( - done_cgus + ); + IntoDynSyncSend(module) + }); + modules .into_iter() - .map(|(_, cgu)| OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu))), - ); - modules - }); + .map(|module| module.0) + .chain(done_cgus.into_iter().map(|(_, cgu)| { + OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu)) + })) + .collect() + }); let allocator_module = emit_allocator_module(tcx); diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 57c88f4b0f9f5..e368cf4386d01 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -1,75 +1,27 @@ //! The JIT driver uses [`cranelift_jit`] to JIT execute programs without writing any object //! files. -use std::cell::RefCell; use std::ffi::CString; use std::os::raw::{c_char, c_int}; -use std::sync::{Mutex, OnceLock, mpsc}; -use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_jit::{JITBuilder, JITModule}; use rustc_codegen_ssa::CrateInfo; use rustc_middle::mir::mono::MonoItem; use rustc_session::Session; use rustc_span::sym; +use crate::CodegenCx; use crate::debuginfo::TypeDebugContext; use crate::prelude::*; use crate::unwind_module::UnwindModule; -use crate::{CodegenCx, CodegenMode}; -struct JitState { - jit_module: UnwindModule, -} - -thread_local! { - static LAZY_JIT_STATE: RefCell> = const { RefCell::new(None) }; -} - -/// The Sender owned by the rustc thread -static GLOBAL_MESSAGE_SENDER: OnceLock>> = OnceLock::new(); - -/// A message that is sent from the jitted runtime to the rustc thread. -/// Senders are responsible for upholding `Send` semantics. -enum UnsafeMessage { - /// Request that the specified `Instance` be lazily jitted. - /// - /// Nothing accessible through `instance_ptr` may be moved or mutated by the sender after - /// this message is sent. - JitFn { - instance_ptr: *const Instance<'static>, - trampoline_ptr: *const u8, - tx: mpsc::Sender<*const u8>, - }, -} -unsafe impl Send for UnsafeMessage {} - -impl UnsafeMessage { - /// Send the message. - fn send(self) { - thread_local! { - /// The Sender owned by the local thread - static LOCAL_MESSAGE_SENDER: mpsc::Sender = - GLOBAL_MESSAGE_SENDER - .get().unwrap() - .lock().unwrap() - .clone(); - } - LOCAL_MESSAGE_SENDER.with(|sender| { - sender.send(self).expect("rustc thread hung up before lazy JIT request was sent") - }) - } -} - -fn create_jit_module(tcx: TyCtxt<'_>, hotswap: bool) -> (UnwindModule, CodegenCx) { +fn create_jit_module(tcx: TyCtxt<'_>) -> (UnwindModule, CodegenCx) { let crate_info = CrateInfo::new(tcx, "dummy_target_cpu".to_string()); - let isa = crate::build_isa(tcx.sess); + let isa = crate::build_isa(tcx.sess, true); let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); - jit_builder.hotswap(hotswap); crate::compiler_builtins::register_functions_for_jit(&mut jit_builder); jit_builder.symbol_lookup_fn(dep_symbol_lookup_fn(tcx.sess, crate_info)); - jit_builder.symbol("__clif_jit_fn", clif_jit_fn as *const u8); let mut jit_module = UnwindModule::new(JITModule::new(jit_builder), false); let cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, sym::dummy_cgu_name); @@ -79,7 +31,7 @@ fn create_jit_module(tcx: TyCtxt<'_>, hotswap: bool) -> (UnwindModule (jit_module, cx) } -pub(crate) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode, jit_args: Vec) -> ! { +pub(crate) fn run_jit(tcx: TyCtxt<'_>, jit_args: Vec) -> ! { if !tcx.sess.opts.output_types.should_codegen() { tcx.dcx().fatal("JIT mode doesn't work with `cargo check`"); } @@ -88,8 +40,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode, jit_args: Vec< tcx.dcx().fatal("can't jit non-executable crate"); } - let (mut jit_module, mut cx) = - create_jit_module(tcx, matches!(codegen_mode, CodegenMode::JitLazy)); + let (mut jit_module, mut cx) = create_jit_module(tcx); let mut cached_context = Context::new(); let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; @@ -105,21 +56,15 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode, jit_args: Vec< super::predefine_mono_items(tcx, &mut jit_module, &mono_items); for (mono_item, _) in mono_items { match mono_item { - MonoItem::Fn(inst) => match codegen_mode { - CodegenMode::Aot => unreachable!(), - CodegenMode::Jit => { - codegen_and_compile_fn( - tcx, - &mut cx, - &mut cached_context, - &mut jit_module, - inst, - ); - } - CodegenMode::JitLazy => { - codegen_shim(tcx, &mut cached_context, &mut jit_module, inst) - } - }, + MonoItem::Fn(inst) => { + codegen_and_compile_fn( + tcx, + &mut cx, + &mut cached_context, + &mut jit_module, + inst, + ); + } MonoItem::Static(def_id) => { crate::constant::codegen_static(tcx, &mut jit_module, def_id); } @@ -161,41 +106,17 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode, jit_args: Vec< let start_func_id = jit_module.declare_function("main", Linkage::Import, &start_sig).unwrap(); let finalized_start: *const u8 = jit_module.module.get_finalized_function(start_func_id); - LAZY_JIT_STATE.with(|lazy_jit_state| { - let mut lazy_jit_state = lazy_jit_state.borrow_mut(); - assert!(lazy_jit_state.is_none()); - *lazy_jit_state = Some(JitState { jit_module }); - }); - let f: extern "C" fn(c_int, *const *const c_char) -> c_int = unsafe { ::std::mem::transmute(finalized_start) }; - let (tx, rx) = mpsc::channel(); - GLOBAL_MESSAGE_SENDER.set(Mutex::new(tx)).unwrap(); + let mut argv = args.iter().map(|arg| arg.as_ptr()).collect::>(); - // Spawn the jitted runtime in a new thread so that this rustc thread can handle messages - // (eg to lazily JIT further functions as required) - std::thread::spawn(move || { - let mut argv = args.iter().map(|arg| arg.as_ptr()).collect::>(); + // Push a null pointer as a terminating argument. This is required by POSIX and + // useful as some dynamic linkers use it as a marker to jump over. + argv.push(std::ptr::null()); - // Push a null pointer as a terminating argument. This is required by POSIX and - // useful as some dynamic linkers use it as a marker to jump over. - argv.push(std::ptr::null()); - - let ret = f(args.len() as c_int, argv.as_ptr()); - std::process::exit(ret); - }); - - // Handle messages - loop { - match rx.recv().unwrap() { - // lazy JIT compilation request - compile requested instance and return pointer to result - UnsafeMessage::JitFn { instance_ptr, trampoline_ptr, tx } => { - tx.send(jit_fn(instance_ptr, trampoline_ptr)) - .expect("jitted runtime hung up before response to lazy JIT request was sent"); - } - } - } + let ret = f(args.len() as c_int, argv.as_ptr()); + std::process::exit(ret); } pub(crate) fn codegen_and_compile_fn<'tcx>( @@ -205,6 +126,11 @@ pub(crate) fn codegen_and_compile_fn<'tcx>( module: &mut dyn Module, instance: Instance<'tcx>, ) { + if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) { + tcx.dcx() + .span_fatal(tcx.def_span(instance.def_id()), "Naked asm is not supported in JIT mode"); + } + cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler( tcx.prof.clone(), ))); @@ -214,71 +140,18 @@ pub(crate) fn codegen_and_compile_fn<'tcx>( crate::PrintOnPanic(|| format!("{:?} {}", instance, tcx.symbol_name(instance).name)); let cached_func = std::mem::replace(&mut cached_context.func, Function::new()); - if let Some(codegened_func) = crate::base::codegen_fn( + let codegened_func = crate::base::codegen_fn( tcx, cx, &mut TypeDebugContext::default(), cached_func, module, instance, - ) { - crate::base::compile_fn(cx, &tcx.prof, cached_context, module, codegened_func); - } + ); + crate::base::compile_fn(cx, &tcx.prof, cached_context, module, codegened_func); }); } -extern "C" fn clif_jit_fn( - instance_ptr: *const Instance<'static>, - trampoline_ptr: *const u8, -) -> *const u8 { - // send the JIT request to the rustc thread, with a channel for the response - let (tx, rx) = mpsc::channel(); - UnsafeMessage::JitFn { instance_ptr, trampoline_ptr, tx }.send(); - - // block on JIT compilation result - rx.recv().expect("rustc thread hung up before responding to sent lazy JIT request") -} - -fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) -> *const u8 { - rustc_middle::ty::tls::with(|tcx| { - // lift is used to ensure the correct lifetime for instance. - let instance = tcx.lift(unsafe { *instance_ptr }).unwrap(); - - LAZY_JIT_STATE.with(|lazy_jit_state| { - let mut lazy_jit_state = lazy_jit_state.borrow_mut(); - let lazy_jit_state = lazy_jit_state.as_mut().unwrap(); - let jit_module = &mut lazy_jit_state.jit_module; - - let name = tcx.symbol_name(instance).name; - let sig = crate::abi::get_function_sig( - tcx, - jit_module.target_config().default_call_conv, - instance, - ); - let func_id = jit_module.declare_function(name, Linkage::Export, &sig).unwrap(); - - let current_ptr = jit_module.module.read_got_entry(func_id); - - // If the function's GOT entry has already been updated to point at something other - // than the shim trampoline, don't re-jit but just return the new pointer instead. - // This does not need synchronization as this code is executed only by a sole rustc - // thread. - if current_ptr != trampoline_ptr { - return current_ptr; - } - - jit_module.module.prepare_for_function_redefine(func_id).unwrap(); - - let mut cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, sym::dummy_cgu_name); - codegen_and_compile_fn(tcx, &mut cx, &mut Context::new(), jit_module, instance); - - assert!(cx.global_asm.is_empty()); - jit_module.finalize_definitions(); - jit_module.module.get_finalized_function(func_id) - }) - }) -} - fn dep_symbol_lookup_fn( sess: &Session, crate_info: CrateInfo, @@ -326,57 +199,3 @@ fn dep_symbol_lookup_fn( None }) } - -fn codegen_shim<'tcx>( - tcx: TyCtxt<'tcx>, - cached_context: &mut Context, - module: &mut UnwindModule, - inst: Instance<'tcx>, -) { - let pointer_type = module.target_config().pointer_type(); - - let name = tcx.symbol_name(inst).name; - let sig = crate::abi::get_function_sig(tcx, module.target_config().default_call_conv, inst); - let func_id = module.declare_function(name, Linkage::Export, &sig).unwrap(); - - let instance_ptr = Box::into_raw(Box::new(inst)); - - let jit_fn = module - .declare_function( - "__clif_jit_fn", - Linkage::Import, - &Signature { - call_conv: module.target_config().default_call_conv, - params: vec![AbiParam::new(pointer_type), AbiParam::new(pointer_type)], - returns: vec![AbiParam::new(pointer_type)], - }, - ) - .unwrap(); - - let context = cached_context; - context.clear(); - let trampoline = &mut context.func; - trampoline.signature = sig.clone(); - - let mut builder_ctx = FunctionBuilderContext::new(); - let mut trampoline_builder = FunctionBuilder::new(trampoline, &mut builder_ctx); - - let trampoline_fn = module.declare_func_in_func(func_id, trampoline_builder.func); - let jit_fn = module.declare_func_in_func(jit_fn, trampoline_builder.func); - let sig_ref = trampoline_builder.func.import_signature(sig); - - let entry_block = trampoline_builder.create_block(); - trampoline_builder.append_block_params_for_function_params(entry_block); - let fn_args = trampoline_builder.func.dfg.block_params(entry_block).to_vec(); - - trampoline_builder.switch_to_block(entry_block); - let instance_ptr = trampoline_builder.ins().iconst(pointer_type, instance_ptr as u64 as i64); - let trampoline_ptr = trampoline_builder.ins().func_addr(pointer_type, trampoline_fn); - let jitted_fn = trampoline_builder.ins().call(jit_fn, &[instance_ptr, trampoline_ptr]); - let jitted_fn = trampoline_builder.func.dfg.inst_results(jitted_fn)[0]; - let call_inst = trampoline_builder.ins().call_indirect(sig_ref, jitted_fn, &fn_args); - let ret_vals = trampoline_builder.func.dfg.inst_results(call_inst).to_vec(); - trampoline_builder.ins().return_(&ret_vals); - - module.define_function(func_id, context).unwrap(); -} diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 9ea92c300f898..203b443269fa7 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -7,102 +7,139 @@ use std::process::{Command, Stdio}; use std::sync::Arc; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; -use rustc_hir::{InlineAsmOperand, ItemId}; -use rustc_middle::mir::interpret::ErrorHandled; +use rustc_codegen_ssa::traits::{AsmCodegenMethods, GlobalAsmOperandRef}; +use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::layout::{ + FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers, +}; use rustc_session::config::{OutputFilenames, OutputType}; use rustc_target::asm::InlineAsmArch; use crate::prelude::*; -pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String, item_id: ItemId) { - let item = tcx.hir_item(item_id); - if let rustc_hir::ItemKind::GlobalAsm { asm, .. } = item.kind { - let is_x86 = - matches!(tcx.sess.asm_arch.unwrap(), InlineAsmArch::X86 | InlineAsmArch::X86_64); - - if is_x86 { - if !asm.options.contains(InlineAsmOptions::ATT_SYNTAX) { - global_asm.push_str("\n.intel_syntax noprefix\n"); - } else { - global_asm.push_str("\n.att_syntax\n"); - } +pub(crate) struct GlobalAsmContext<'a, 'tcx> { + pub tcx: TyCtxt<'tcx>, + pub global_asm: &'a mut String, +} + +impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> { + fn codegen_global_asm( + &mut self, + template: &[InlineAsmTemplatePiece], + operands: &[GlobalAsmOperandRef<'tcx>], + options: InlineAsmOptions, + _line_spans: &[Span], + ) { + codegen_global_asm_inner(self.tcx, self.global_asm, template, operands, options); + } + + fn mangled_name(&self, instance: Instance<'tcx>) -> String { + let symbol_name = self.tcx.symbol_name(instance).name.to_owned(); + if self.tcx.sess.target.is_like_darwin { format!("_{symbol_name}") } else { symbol_name } + } +} + +impl<'tcx> LayoutOfHelpers<'tcx> for GlobalAsmContext<'_, 'tcx> { + #[inline] + fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { + if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + self.tcx.sess.dcx().span_fatal(span, err.to_string()) + } else { + self.tcx + .sess + .dcx() + .span_fatal(span, format!("failed to get layout for `{}`: {}", ty, err)) } - for piece in asm.template { - match *piece { - InlineAsmTemplatePiece::String(ref s) => global_asm.push_str(s), - InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: op_sp } => { - match asm.operands[operand_idx].0 { - InlineAsmOperand::Const { ref anon_const } => { - match tcx.const_eval_poly(anon_const.def_id.to_def_id()) { - Ok(const_value) => { - let ty = tcx - .typeck_body(anon_const.body) - .node_type(anon_const.hir_id); - let string = rustc_codegen_ssa::common::asm_const_to_str( - tcx, - op_sp, - const_value, - FullyMonomorphizedLayoutCx(tcx).layout_of(ty), - ); - global_asm.push_str(&string); - } - Err(ErrorHandled::Reported { .. }) => { - // An error has already been reported and compilation is - // guaranteed to fail if execution hits this path. - } - Err(ErrorHandled::TooGeneric(_)) => { - span_bug!(op_sp, "asm const cannot be resolved; too generic"); - } - } - } - InlineAsmOperand::SymFn { expr } => { - if cfg!(not(feature = "inline_asm_sym")) { - tcx.dcx().span_err( - item.span, - "asm! and global_asm! sym operands are not yet supported", - ); - } - - let ty = tcx.typeck(item_id.owner_id).expr_ty(expr); - let instance = match ty.kind() { - &ty::FnDef(def_id, args) => Instance::new(def_id, args), - _ => span_bug!(op_sp, "asm sym is not a function"), - }; - let symbol = tcx.symbol_name(instance); - // FIXME handle the case where the function was made private to the - // current codegen unit - global_asm.push_str(symbol.name); - } - InlineAsmOperand::SymStatic { path: _, def_id } => { - if cfg!(not(feature = "inline_asm_sym")) { - tcx.dcx().span_err( - item.span, - "asm! and global_asm! sym operands are not yet supported", - ); - } - - let instance = Instance::mono(tcx, def_id); - let symbol = tcx.symbol_name(instance); - global_asm.push_str(symbol.name); + } +} + +impl<'tcx> FnAbiOfHelpers<'tcx> for GlobalAsmContext<'_, 'tcx> { + #[inline] + fn handle_fn_abi_err( + &self, + err: FnAbiError<'tcx>, + span: Span, + fn_abi_request: FnAbiRequest<'tcx>, + ) -> ! { + FullyMonomorphizedLayoutCx(self.tcx).handle_fn_abi_err(err, span, fn_abi_request) + } +} + +impl<'tcx> HasTyCtxt<'tcx> for GlobalAsmContext<'_, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } +} + +impl<'tcx> rustc_abi::HasDataLayout for GlobalAsmContext<'_, 'tcx> { + fn data_layout(&self) -> &rustc_abi::TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'tcx> HasTypingEnv<'tcx> for GlobalAsmContext<'_, 'tcx> { + fn typing_env(&self) -> ty::TypingEnv<'tcx> { + ty::TypingEnv::fully_monomorphized() + } +} + +fn codegen_global_asm_inner<'tcx>( + tcx: TyCtxt<'tcx>, + global_asm: &mut String, + template: &[InlineAsmTemplatePiece], + operands: &[GlobalAsmOperandRef<'tcx>], + options: InlineAsmOptions, +) { + let is_x86 = matches!(tcx.sess.asm_arch.unwrap(), InlineAsmArch::X86 | InlineAsmArch::X86_64); + + if is_x86 { + if !options.contains(InlineAsmOptions::ATT_SYNTAX) { + global_asm.push_str("\n.intel_syntax noprefix\n"); + } else { + global_asm.push_str("\n.att_syntax\n"); + } + } + for piece in template { + match *piece { + InlineAsmTemplatePiece::String(ref s) => global_asm.push_str(s), + InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span } => { + match operands[operand_idx] { + GlobalAsmOperandRef::Const { ref string } => { + global_asm.push_str(string); + } + GlobalAsmOperandRef::SymFn { instance } => { + if cfg!(not(feature = "inline_asm_sym")) { + tcx.dcx().span_err( + span, + "asm! and global_asm! sym operands are not yet supported", + ); } - InlineAsmOperand::In { .. } - | InlineAsmOperand::Out { .. } - | InlineAsmOperand::InOut { .. } - | InlineAsmOperand::SplitInOut { .. } - | InlineAsmOperand::Label { .. } => { - span_bug!(op_sp, "invalid operand type for global_asm!") + + let symbol = tcx.symbol_name(instance); + // FIXME handle the case where the function was made private to the + // current codegen unit + global_asm.push_str(symbol.name); + } + GlobalAsmOperandRef::SymStatic { def_id } => { + if cfg!(not(feature = "inline_asm_sym")) { + tcx.dcx().span_err( + span, + "asm! and global_asm! sym operands are not yet supported", + ); } + + let instance = Instance::mono(tcx, def_id); + let symbol = tcx.symbol_name(instance); + global_asm.push_str(symbol.name); } } } } + } - global_asm.push('\n'); - if is_x86 { - global_asm.push_str(".att_syntax\n\n"); - } - } else { - bug!("Expected GlobalAsm found {:?}", item); + global_asm.push('\n'); + if is_x86 { + global_asm.push_str(".att_syntax\n\n"); } } @@ -132,6 +169,7 @@ pub(crate) fn compile_global_asm( config: &GlobalAsmConfig, cgu_name: &str, global_asm: &str, + invocation_temp: Option<&str>, ) -> Result, String> { if global_asm.is_empty() { return Ok(None); @@ -146,7 +184,7 @@ pub(crate) fn compile_global_asm( global_asm.push('\n'); let global_asm_object_file = add_file_stem_postfix( - config.output_filenames.temp_path(OutputType::Object, Some(cgu_name)), + config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp), ".asm", ); diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 310b226814d40..afee50955497c 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -161,7 +161,6 @@ pub(crate) fn codegen_inline_asm_inner<'tcx>( stack_slots_input: Vec::new(), stack_slots_output: Vec::new(), stack_slot_size: Size::from_bytes(0), - is_naked: false, }; asm_gen.allocate_registers(); asm_gen.allocate_stack_slots(); @@ -201,114 +200,6 @@ pub(crate) fn codegen_inline_asm_inner<'tcx>( call_inline_asm(fx, &asm_name, asm_gen.stack_slot_size, inputs, outputs); } -pub(crate) fn codegen_naked_asm<'tcx>( - tcx: TyCtxt<'tcx>, - cx: &mut crate::CodegenCx, - module: &mut dyn Module, - instance: Instance<'tcx>, - span: Span, - symbol_name: &str, - template: &[InlineAsmTemplatePiece], - operands: &[InlineAsmOperand<'tcx>], - options: InlineAsmOptions, -) { - // FIXME add .eh_frame unwind info directives - - let operands = operands - .iter() - .map(|operand| match *operand { - InlineAsmOperand::In { .. } - | InlineAsmOperand::Out { .. } - | InlineAsmOperand::InOut { .. } => { - span_bug!(span, "invalid operand type for naked asm") - } - InlineAsmOperand::Const { ref value } => { - let cv = instance.instantiate_mir_and_normalize_erasing_regions( - tcx, - ty::TypingEnv::fully_monomorphized(), - ty::EarlyBinder::bind(value.const_), - ); - let const_value = cv - .eval(tcx, ty::TypingEnv::fully_monomorphized(), value.span) - .expect("erroneous constant missed by mono item collection"); - - let value = rustc_codegen_ssa::common::asm_const_to_str( - tcx, - span, - const_value, - FullyMonomorphizedLayoutCx(tcx).layout_of(cv.ty()), - ); - CInlineAsmOperand::Const { value } - } - InlineAsmOperand::SymFn { ref value } => { - if cfg!(not(feature = "inline_asm_sym")) { - tcx.dcx() - .span_err(span, "asm! and global_asm! sym operands are not yet supported"); - } - - let const_ = instance.instantiate_mir_and_normalize_erasing_regions( - tcx, - ty::TypingEnv::fully_monomorphized(), - ty::EarlyBinder::bind(value.const_), - ); - if let ty::FnDef(def_id, args) = *const_.ty().kind() { - let instance = ty::Instance::resolve_for_fn_ptr( - tcx, - ty::TypingEnv::fully_monomorphized(), - def_id, - args, - ) - .unwrap(); - let symbol = tcx.symbol_name(instance); - - // Pass a wrapper rather than the function itself as the function itself may not - // be exported from the main codegen unit and may thus be unreachable from the - // object file created by an external assembler. - let wrapper_name = format!( - "__inline_asm_{}_wrapper_n{}", - cx.cgu_name.as_str().replace('.', "__").replace('-', "_"), - cx.inline_asm_index - ); - cx.inline_asm_index += 1; - let sig = - get_function_sig(tcx, module.target_config().default_call_conv, instance); - create_wrapper_function(module, sig, &wrapper_name, symbol.name); - - CInlineAsmOperand::Symbol { symbol: wrapper_name } - } else { - span_bug!(span, "invalid type for asm sym (fn)"); - } - } - InlineAsmOperand::SymStatic { def_id } => { - assert!(tcx.is_static(def_id)); - let instance = Instance::mono(tcx, def_id); - CInlineAsmOperand::Symbol { symbol: tcx.symbol_name(instance).name.to_owned() } - } - InlineAsmOperand::Label { .. } => { - span_bug!(span, "asm! label operands are not yet supported"); - } - }) - .collect::>(); - - let asm_gen = InlineAssemblyGenerator { - tcx, - arch: tcx.sess.asm_arch.unwrap(), - enclosing_def_id: instance.def_id(), - template, - operands: &operands, - options, - registers: Vec::new(), - stack_slots_clobber: Vec::new(), - stack_slots_input: Vec::new(), - stack_slots_output: Vec::new(), - stack_slot_size: Size::from_bytes(0), - is_naked: true, - }; - - let generated_asm = asm_gen.generate_asm_wrapper(symbol_name); - cx.global_asm.push_str(&generated_asm); -} - struct InlineAssemblyGenerator<'a, 'tcx> { tcx: TyCtxt<'tcx>, arch: InlineAsmArch, @@ -321,13 +212,10 @@ struct InlineAssemblyGenerator<'a, 'tcx> { stack_slots_input: Vec>, stack_slots_output: Vec>, stack_slot_size: Size, - is_naked: bool, } impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { fn allocate_registers(&mut self) { - assert!(!self.is_naked); - let sess = self.tcx.sess; let map = allocatable_registers( self.arch, @@ -451,8 +339,6 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { } fn allocate_stack_slots(&mut self) { - assert!(!self.is_naked); - let mut slot_size = Size::from_bytes(0); let mut slots_clobber = vec![None; self.operands.len()]; let mut slots_input = vec![None; self.operands.len()]; @@ -582,36 +468,44 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { if is_x86 { generated_asm.push_str(".intel_syntax noprefix\n"); } - if !self.is_naked { - Self::prologue(&mut generated_asm, self.arch); - - // Save clobbered registers - if !self.options.contains(InlineAsmOptions::NORETURN) { - for (reg, slot) in self - .registers - .iter() - .zip(self.stack_slots_clobber.iter().copied()) - .filter_map(|(r, s)| r.zip(s)) - { - Self::save_register(&mut generated_asm, self.arch, reg, slot); - } - } - // Write input registers + Self::prologue(&mut generated_asm, self.arch); + + // Save clobbered registers + if !self.options.contains(InlineAsmOptions::NORETURN) { for (reg, slot) in self .registers .iter() - .zip(self.stack_slots_input.iter().copied()) + .zip(self.stack_slots_clobber.iter().copied()) .filter_map(|(r, s)| r.zip(s)) { - Self::restore_register(&mut generated_asm, self.arch, reg, slot); + Self::save_register(&mut generated_asm, self.arch, reg, slot); } } + // Write input registers + for (reg, slot) in self + .registers + .iter() + .zip(self.stack_slots_input.iter().copied()) + .filter_map(|(r, s)| r.zip(s)) + { + Self::restore_register(&mut generated_asm, self.arch, reg, slot); + } + if is_x86 && self.options.contains(InlineAsmOptions::ATT_SYNTAX) { generated_asm.push_str(".att_syntax\n"); } + if self.arch == InlineAsmArch::AArch64 { + for feature in &self.tcx.codegen_fn_attrs(self.enclosing_def_id).target_features { + if feature.name == sym::neon { + continue; + } + writeln!(generated_asm, ".arch_extension {}", feature.name).unwrap(); + } + } + // The actual inline asm for piece in self.template { match piece { @@ -652,6 +546,20 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { .emit(&mut generated_asm, InlineAsmArch::X86_64, *modifier) .unwrap(), }, + InlineAsmArch::AArch64 => match reg { + InlineAsmReg::AArch64(reg) if reg.vreg_index().is_some() => { + // rustc emits v0 rather than q0 + reg.emit( + &mut generated_asm, + InlineAsmArch::AArch64, + Some(modifier.unwrap_or('q')), + ) + .unwrap() + } + _ => reg + .emit(&mut generated_asm, InlineAsmArch::AArch64, *modifier) + .unwrap(), + }, _ => reg.emit(&mut generated_asm, self.arch, *modifier).unwrap(), } } @@ -665,36 +573,43 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { } generated_asm.push('\n'); + if self.arch == InlineAsmArch::AArch64 { + for feature in &self.tcx.codegen_fn_attrs(self.enclosing_def_id).target_features { + if feature.name == sym::neon { + continue; + } + writeln!(generated_asm, ".arch_extension no{}", feature.name).unwrap(); + } + } + if is_x86 && self.options.contains(InlineAsmOptions::ATT_SYNTAX) { generated_asm.push_str(".intel_syntax noprefix\n"); } - if !self.is_naked { - if !self.options.contains(InlineAsmOptions::NORETURN) { - // Read output registers - for (reg, slot) in self - .registers - .iter() - .zip(self.stack_slots_output.iter().copied()) - .filter_map(|(r, s)| r.zip(s)) - { - Self::save_register(&mut generated_asm, self.arch, reg, slot); - } - - // Restore clobbered registers - for (reg, slot) in self - .registers - .iter() - .zip(self.stack_slots_clobber.iter().copied()) - .filter_map(|(r, s)| r.zip(s)) - { - Self::restore_register(&mut generated_asm, self.arch, reg, slot); - } + if !self.options.contains(InlineAsmOptions::NORETURN) { + // Read output registers + for (reg, slot) in self + .registers + .iter() + .zip(self.stack_slots_output.iter().copied()) + .filter_map(|(r, s)| r.zip(s)) + { + Self::save_register(&mut generated_asm, self.arch, reg, slot); + } - Self::epilogue(&mut generated_asm, self.arch); - } else { - Self::epilogue_noreturn(&mut generated_asm, self.arch); + // Restore clobbered registers + for (reg, slot) in self + .registers + .iter() + .zip(self.stack_slots_clobber.iter().copied()) + .filter_map(|(r, s)| r.zip(s)) + { + Self::restore_register(&mut generated_asm, self.arch, reg, slot); } + + Self::epilogue(&mut generated_asm, self.arch); + } else { + Self::epilogue_noreturn(&mut generated_asm, self.arch); } if is_x86 { @@ -809,7 +724,13 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { } InlineAsmArch::AArch64 => { generated_asm.push_str(" str "); - reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap(); + match reg { + InlineAsmReg::AArch64(reg) if reg.vreg_index().is_some() => { + // rustc emits v0 rather than q0 + reg.emit(generated_asm, InlineAsmArch::AArch64, Some('q')).unwrap() + } + _ => reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap(), + } writeln!(generated_asm, ", [x19, 0x{:x}]", offset.bytes()).unwrap(); } InlineAsmArch::RiscV64 => { @@ -851,7 +772,13 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { } InlineAsmArch::AArch64 => { generated_asm.push_str(" ldr "); - reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap(); + match reg { + InlineAsmReg::AArch64(reg) if reg.vreg_index().is_some() => { + // rustc emits v0 rather than q0 + reg.emit(generated_asm, InlineAsmArch::AArch64, Some('q')).unwrap() + } + _ => reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap(), + } writeln!(generated_asm, ", [x19, 0x{:x}]", offset.bytes()).unwrap(); } InlineAsmArch::RiscV64 => { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs index 720a0d8fbf593..eb0dfbb69c3b7 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -54,6 +54,14 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( ); } + "llvm.fptosi.sat.v4i32.v4f32" => { + intrinsic_args!(fx, args => (a); intrinsic); + + simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| { + fx.bcx.ins().fcvt_to_sint_sat(types::I32, lane) + }); + } + _ => { fx.tcx .dcx() diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs index 4c59c81296bad..387c87d123a39 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs @@ -1,5 +1,9 @@ //! Emulate AArch64 LLVM intrinsics +use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_target::asm::*; + +use crate::inline_asm::{CInlineAsmOperand, codegen_inline_asm_inner}; use crate::intrinsics::*; use crate::prelude::*; @@ -17,7 +21,7 @@ pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( fx.bcx.ins().fence(); } - "llvm.aarch64.neon.ld1x4.v16i8.p0i8" => { + "llvm.aarch64.neon.ld1x4.v16i8.p0" => { intrinsic_args!(fx, args => (ptr); intrinsic); let ptr = ptr.load_scalar(fx); @@ -49,6 +53,121 @@ pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( }); } + "llvm.aarch64.neon.fcvtns.v4i32.v4f32" => { + intrinsic_args!(fx, args => (a); intrinsic); + + // Note: Using inline asm instead of fcvt_to_sint as the latter rounds to zero rather than to nearest + + let a_ptr = a.force_stack(fx).0.get_addr(fx); + let res_place = CPlace::new_stack_slot(fx, ret.layout()); + let res_ptr = res_place.to_ptr().get_addr(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String( + "ldr q0, [x0] + fcvtns v0.4s, v0.4s + str q0, [x1]" + .into(), + )], + &[ + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x0, + )), + value: a_ptr, + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x1, + )), + value: res_ptr, + }, + ], + InlineAsmOptions::NOSTACK, + ); + let res = res_place.to_cvalue(fx); + ret.write_cvalue_transmute(fx, res); + } + + "llvm.aarch64.neon.frecpe.v4f32" => { + intrinsic_args!(fx, args => (a); intrinsic); + + let a_ptr = a.force_stack(fx).0.get_addr(fx); + let res_place = CPlace::new_stack_slot(fx, ret.layout()); + let res_ptr = res_place.to_ptr().get_addr(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String( + "ldr q0, [x0] + frecpe v0.4s, v0.4s + str q0, [x1]" + .into(), + )], + &[ + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x0, + )), + value: a_ptr, + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x1, + )), + value: res_ptr, + }, + ], + InlineAsmOptions::NOSTACK, + ); + let res = res_place.to_cvalue(fx); + ret.write_cvalue_transmute(fx, res); + } + + "llvm.aarch64.neon.frecps.v4f32" => { + intrinsic_args!(fx, args => (a, b); intrinsic); + + let a_ptr = a.force_stack(fx).0.get_addr(fx); + let b_ptr = b.force_stack(fx).0.get_addr(fx); + let res_place = CPlace::new_stack_slot(fx, ret.layout()); + let res_ptr = res_place.to_ptr().get_addr(fx); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String( + "ldr q0, [x0] + ldr q1, [x1] + frecps v0.4s, v0.4s, v1.4s + str q0, [x2]" + .into(), + )], + &[ + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x0, + )), + value: a_ptr, + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x1, + )), + value: b_ptr, + }, + CInlineAsmOperand::In { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::AArch64( + AArch64InlineAsmReg::x2, + )), + value: res_ptr, + }, + ], + InlineAsmOptions::NOSTACK, + ); + let res = res_place.to_cvalue(fx); + ret.write_cvalue_transmute(fx, res); + } + _ if intrinsic.starts_with("llvm.aarch64.neon.sqadd.v") || intrinsic.starts_with("llvm.aarch64.neon.uqadd.v") => { @@ -134,7 +253,7 @@ pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( } let res = CValue::by_val( fx.bcx.ins().uextend(types::I32, res_val), - fx.layout_of(fx.tcx.types.u32), + fx.layout_of(fx.tcx.types.i32), ); ret.write_cvalue(fx, res); } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 6735ae024d1c5..9018d78b00aeb 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1,5 +1,4 @@ -//! Codegen of intrinsics. This includes `extern "rust-intrinsic"`, -//! functions marked with the `#[rustc_intrinsic]` attribute +//! Codegen of intrinsics. This includes functions marked with the `#[rustc_intrinsic]` attribute //! and LLVM intrinsics that have symbol names starting with `llvm.`. macro_rules! intrinsic_args { @@ -1031,7 +1030,7 @@ fn codegen_regular_intrinsic_call<'tcx>( let layout = src.layout(); match layout.ty.kind() { - ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} + ty::Int(_) => {} _ => { report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return Ok(()); @@ -1052,7 +1051,7 @@ fn codegen_regular_intrinsic_call<'tcx>( let layout = src.layout(); match layout.ty.kind() { - ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} + ty::Uint(_) => {} _ => { report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return Ok(()); @@ -1073,7 +1072,7 @@ fn codegen_regular_intrinsic_call<'tcx>( let layout = src.layout(); match layout.ty.kind() { - ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} + ty::Int(_) => {} _ => { report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return Ok(()); @@ -1094,7 +1093,7 @@ fn codegen_regular_intrinsic_call<'tcx>( let layout = src.layout(); match layout.ty.kind() { - ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} + ty::Uint(_) => {} _ => { report_atomic_type_validation_error(fx, intrinsic, source_info.span, layout.ty); return Ok(()); @@ -1110,6 +1109,43 @@ fn codegen_regular_intrinsic_call<'tcx>( ret.write_cvalue(fx, old); } + sym::minimumf32 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + let val = fx.bcx.ins().fmin(a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32)); + ret.write_cvalue(fx, val); + } + sym::minimumf64 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + let val = fx.bcx.ins().fmin(a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); + ret.write_cvalue(fx, val); + } + sym::maximumf32 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + let val = fx.bcx.ins().fmax(a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32)); + ret.write_cvalue(fx, val); + } + sym::maximumf64 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + + let val = fx.bcx.ins().fmax(a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); + ret.write_cvalue(fx, val); + } + sym::minnumf32 => { intrinsic_args!(fx, args => (a, b); intrinsic); let a = a.load_scalar(fx); @@ -1283,7 +1319,7 @@ fn codegen_regular_intrinsic_call<'tcx>( intrinsic.name, ); } - return Err(Instance::new(instance.def_id(), instance.args)); + return Err(Instance::new_raw(instance.def_id(), instance.args)); } } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index a3f4374487578..ab09a6f8b38e1 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -26,6 +26,7 @@ extern crate rustc_index; extern crate rustc_metadata; extern crate rustc_session; extern crate rustc_span; +extern crate rustc_symbol_mangling; extern crate rustc_target; #[macro_use] extern crate tracing; @@ -40,9 +41,8 @@ use std::sync::Arc; use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::settings::{self, Configurable}; -use rustc_codegen_ssa::CodegenResults; -use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_codegen_ssa::{CodegenResults, TargetConfig}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; @@ -124,6 +124,7 @@ impl String> Drop for PrintOnPanic { /// inside a single codegen unit with the exception of the Cranelift [`Module`](cranelift_module::Module). struct CodegenCx { output_filenames: Arc, + invocation_temp: Option, should_write_ir: bool, global_asm: String, inline_asm_index: usize, @@ -142,6 +143,7 @@ impl CodegenCx { }; CodegenCx { output_filenames: tcx.output_filenames(()).clone(), + invocation_temp: tcx.sess.invocation_temp.clone(), should_write_ir: crate::pretty_clif::should_write_ir(tcx), global_asm: String::new(), inline_asm_index: 0, @@ -176,13 +178,9 @@ impl CodegenBackend for CraneliftCodegenBackend { } } - fn target_features_cfg( - &self, - sess: &Session, - _allow_unstable: bool, - ) -> Vec { + fn target_config(&self, sess: &Session) -> TargetConfig { // FIXME return the actually used target features. this is necessary for #[cfg(target_feature)] - if sess.target.arch == "x86_64" && sess.target.os != "none" { + let target_features = if sess.target.arch == "x86_64" && sess.target.os != "none" { // x86_64 mandates SSE2 support and rustc requires the x87 feature to be enabled vec![sym::fsxr, sym::sse, sym::sse2, Symbol::intern("x87")] } else if sess.target.arch == "aarch64" { @@ -196,6 +194,18 @@ impl CodegenBackend for CraneliftCodegenBackend { } } else { vec![] + }; + // FIXME do `unstable_target_features` properly + let unstable_target_features = target_features.clone(); + + TargetConfig { + target_features, + unstable_target_features, + // Cranelift does not yet support f16 or f128 + has_reliable_f16: false, + has_reliable_f16_math: false, + has_reliable_f128: false, + has_reliable_f128_math: false, } } @@ -214,15 +224,14 @@ impl CodegenBackend for CraneliftCodegenBackend { BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) .unwrap_or_else(|err| tcx.sess.dcx().fatal(err)) }); - match config.codegen_mode { - CodegenMode::Aot => driver::aot::run_aot(tcx, metadata, need_metadata_module), - CodegenMode::Jit | CodegenMode::JitLazy => { - #[cfg(feature = "jit")] - driver::jit::run_jit(tcx, config.codegen_mode, config.jit_args); - - #[cfg(not(feature = "jit"))] - tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); - } + if config.jit_mode { + #[cfg(feature = "jit")] + driver::jit::run_jit(tcx, config.jit_args); + + #[cfg(not(feature = "jit"))] + tcx.dcx().fatal("jit support was disabled when compiling rustc_codegen_cranelift"); + } else { + driver::aot::run_aot(tcx, metadata, need_metadata_module) } } @@ -247,21 +256,19 @@ fn enable_verifier(sess: &Session) -> bool { } fn target_triple(sess: &Session) -> target_lexicon::Triple { - // FIXME(madsmtm): Use `sess.target.llvm_target` once target-lexicon supports unversioned macOS. - // See - match versioned_llvm_target(sess).parse() { + match sess.target.llvm_target.parse() { Ok(triple) => triple, Err(err) => sess.dcx().fatal(format!("target not recognized: {}", err)), } } -fn build_isa(sess: &Session) -> Arc { +fn build_isa(sess: &Session, jit: bool) -> Arc { use target_lexicon::BinaryFormat; let target_triple = crate::target_triple(sess); let mut flags_builder = settings::builder(); - flags_builder.enable("is_pic").unwrap(); + flags_builder.set("is_pic", if jit { "false" } else { "true" }).unwrap(); let enable_verifier = if enable_verifier(sess) { "true" } else { "false" }; flags_builder.set("enable_verifier", enable_verifier).unwrap(); flags_builder.set("regalloc_checker", enable_verifier).unwrap(); diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index 6d5df2b00437b..6eef97c14dd28 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -1,6 +1,6 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_hir::LangItem; -use rustc_middle::ty::{AssocKind, GenericArg}; +use rustc_middle::ty::{AssocTag, GenericArg}; use rustc_session::config::EntryFnType; use rustc_span::{DUMMY_SP, Ident}; @@ -104,10 +104,10 @@ pub(crate) fn maybe_create_entry_wrapper( let termination_trait = tcx.require_lang_item(LangItem::Termination, None); let report = tcx .associated_items(termination_trait) - .find_by_name_and_kind( + .find_by_ident_and_kind( tcx, Ident::from_str("report"), - AssocKind::Fn, + AssocTag::Fn, termination_trait, ) .unwrap(); diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index f44e2459a784d..2a4d1e3ae571d 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -395,8 +395,12 @@ pub(crate) fn codegen_ptr_binop<'tcx>( in_lhs: CValue<'tcx>, in_rhs: CValue<'tcx>, ) -> CValue<'tcx> { - let is_thin_ptr = - in_lhs.layout().ty.builtin_deref(true).map(|ty| !has_ptr_meta(fx.tcx, ty)).unwrap_or(true); + let is_thin_ptr = in_lhs + .layout() + .ty + .builtin_deref(true) + .map(|ty| !fx.tcx.type_has_metadata(ty, ty::TypingEnv::fully_monomorphized())) + .unwrap_or(true); if is_thin_ptr { match bin_op { diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index cc739fefcd066..f8a19589fdd72 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -746,7 +746,7 @@ impl<'tcx> CPlace<'tcx> { }; let (field_ptr, field_layout) = codegen_field(fx, base, extra, layout, field); - if has_ptr_meta(fx.tcx, field_layout.ty) { + if fx.tcx.type_has_metadata(field_layout.ty, ty::TypingEnv::fully_monomorphized()) { CPlace::for_ptr_with_extra(field_ptr, extra.unwrap(), field_layout) } else { CPlace::for_ptr(field_ptr, field_layout) @@ -832,7 +832,7 @@ impl<'tcx> CPlace<'tcx> { pub(crate) fn place_deref(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> CPlace<'tcx> { let inner_layout = fx.layout_of(self.layout().ty.builtin_deref(true).unwrap()); - if has_ptr_meta(fx.tcx, inner_layout.ty) { + if fx.tcx.type_has_metadata(inner_layout.ty, ty::TypingEnv::fully_monomorphized()) { let (addr, extra) = self.to_cvalue(fx).load_scalar_pair(fx); CPlace::for_ptr_with_extra(Pointer::new(addr), extra, inner_layout) } else { @@ -845,7 +845,7 @@ impl<'tcx> CPlace<'tcx> { fx: &mut FunctionCx<'_, '_, 'tcx>, layout: TyAndLayout<'tcx>, ) -> CValue<'tcx> { - if has_ptr_meta(fx.tcx, self.layout().ty) { + if fx.tcx.type_has_metadata(self.layout().ty, ty::TypingEnv::fully_monomorphized()) { let (ptr, extra) = self.to_ptr_unsized(); CValue::by_val_pair(ptr.get_addr(fx), extra, layout) } else { diff --git a/compiler/rustc_codegen_gcc/.gitattributes b/compiler/rustc_codegen_gcc/.gitattributes new file mode 100644 index 0000000000000..b9cd1111c8d05 --- /dev/null +++ b/compiler/rustc_codegen_gcc/.gitattributes @@ -0,0 +1 @@ +Cargo.lock linguist-generated=false \ No newline at end of file diff --git a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml index f96912e6b7a83..ef024258ffc86 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml @@ -1,8 +1,10 @@ name: CI on: - - push - - pull_request + push: + branches: + - master + pull_request: permissions: contents: read @@ -121,3 +123,22 @@ jobs: run: | cd build_system cargo test + + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + success: + needs: [build, duplicates, build_system] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml index d080bbfe91fe6..bc42eb1468ea2 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml @@ -2,7 +2,10 @@ name: Failures on: - - pull_request + push: + branches: + - master + pull_request: permissions: contents: read @@ -108,3 +111,22 @@ jobs: echo "Error: 'the compiler unexpectedly panicked' found in output logs. CI Error!!" exit 1 fi + + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + success_failures: + needs: [build] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml b/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml index bb9e020dc6a4c..da9a1506855c3 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml @@ -1,8 +1,10 @@ name: CI libgccjit 12 on: - - push - - pull_request + push: + branches: + - master + pull_request: permissions: contents: read @@ -85,3 +87,22 @@ jobs: #- name: Run tests #run: | #./y.sh test --release --clean --build-sysroot ${{ matrix.commands }} --no-default-features + + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + success_gcc12: + needs: [build] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml b/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml index ed1fc02bd9131..21731f7087e2c 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml @@ -3,8 +3,10 @@ name: m68k CI on: - - push - - pull_request + push: + branches: + - master + pull_request: permissions: contents: read @@ -105,3 +107,22 @@ jobs: - name: Run tests run: | ./y.sh test --release --clean --build-sysroot --sysroot-features compiler_builtins/no-f16-f128 ${{ matrix.commands }} + + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + success_m68k: + needs: [build] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/compiler/rustc_codegen_gcc/.github/workflows/release.yml b/compiler/rustc_codegen_gcc/.github/workflows/release.yml index 886ce90b4713e..47a40286554e4 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/release.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/release.yml @@ -1,8 +1,10 @@ name: CI with sysroot compiled in release mode on: - - push - - pull_request + push: + branches: + - master + pull_request: permissions: contents: read @@ -82,3 +84,22 @@ jobs: echo "Test is done with LTO enabled, hence inlining should occur across crates" exit 1 fi + + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + success_release: + needs: [build] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml index d5ae6144496f1..f26ac3b755fb2 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml @@ -1,8 +1,10 @@ name: stdarch tests with sysroot compiled in release mode on: - - push - - pull_request + push: + branches: + - master + pull_request: permissions: contents: read @@ -99,6 +101,24 @@ jobs: if: ${{ matrix.cargo_runner }} run: | # FIXME: these tests fail when the sysroot is compiled with LTO because of a missing symbol in proc-macro. - # TODO: remove --skip test_mm512_stream_ps when stdarch is updated in rustc. # TODO: remove --skip test_tile_ when it's implemented. - STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features --cfg stdarch_intel_sde" ./y.sh cargo test --manifest-path build/build_sysroot/sysroot_src/library/stdarch/Cargo.toml -- --skip rtm --skip tbm --skip sse4a --skip test_mm512_stream_ps --skip test_tile_ + STDARCH_TEST_SKIP_FUNCTION="xsave,xsaveopt,xsave64,xsaveopt64" STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features" ./y.sh cargo test --manifest-path build/build_sysroot/sysroot_src/library/stdarch/Cargo.toml -- --skip rtm --skip tbm --skip sse4a --skip test_tile_ + + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + success_stdarch: + needs: [build] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/compiler/rustc_codegen_gcc/CONTRIBUTING.md b/compiler/rustc_codegen_gcc/CONTRIBUTING.md new file mode 100644 index 0000000000000..8e313ab08b59d --- /dev/null +++ b/compiler/rustc_codegen_gcc/CONTRIBUTING.md @@ -0,0 +1,101 @@ +# Contributing to rustc_codegen_gcc + +Welcome to the `rustc_codegen_gcc` project! This guide will help you get started as a contributor. The project aims to provide a GCC codegen backend for rustc, allowing Rust compilation on platforms unsupported by LLVM and potentially improving runtime performance through GCC's optimizations. + +## Getting Started + +### Setting Up Your Development Environment + +For detailed setup instructions including dependencies, build steps, and initial testing, please refer to our [README](Readme.md). The README contains the most up-to-date information on: + +- Required dependencies and system packages +- Repository setup and configuration +- Build process +- Basic test verification + +Once you've completed the setup process outlined in the README, you can proceed with the contributor-specific information below. + +## Communication Channels + +- Matrix: Join our [Matrix channel](https://matrix.to/#/#rustc_codegen_gcc:matrix.org) +- IRC: Join us on [IRC](https://web.libera.chat/#rustc_codegen_gcc) +- [GitHub Issues](https://github.com/rust-lang/rustc_codegen_gcc/issues): For bug reports and feature discussions + +We encourage new contributors to join our communication channels and introduce themselves. Feel free to ask questions about where to start or discuss potential contributions. + +## Understanding Core Concepts + +### Common Development Tasks + +#### Running Specific Tests + +To run specific tests, use appropriate flags such as: + +- `./y.sh test --test-libcore` +- `./y.sh test --std-tests` +- `cargo test -- ` + +Additionally, you can run the tests of `libgccjit`: + +```bash +# libgccjit tests +cd gcc-build/gcc +make check-jit +# For a specific test: +make check-jit RUNTESTFLAGS="-v -v -v jit.exp=jit.dg/test-asm.cc" +``` + +#### Debugging Tools + +The project provides several environment variables for debugging: + +- `CG_GCCJIT_DUMP_GIMPLE`: Dumps the GIMPLE IR +- `CG_RUSTFLAGS`: Additional Rust flags +- `CG_GCCJIT_DUMP_MODULE`: Dumps a specific module +- `CG_GCCJIT_DUMP_TO_FILE`: Creates C-like representation + +Full list of debugging options can be found in the [README](Readme.md#env-vars). + +## Making Contributions + +### Finding Issues to Work On + +1. Look for issues labeled with [`good first issue`](https://github.com/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue%20state%3Aopen%20label%3A"good%20first%20issue") or [`help wanted`](https://github.com/rust-lang/rustc_codegen_gcc/issues?q=is%3Aissue%20state%3Aopen%20label%3A"help%20wanted") +2. Check the [progress report](https://blog.antoyo.xyz/rustc_codegen_gcc-progress-report-34#state_of_rustc_codegen_gcc) for larger initiatives +3. Consider improving documentation or investigating [failing tests](https://github.com/rust-lang/rustc_codegen_gcc/tree/master/tests) (except `failing-ui-tests12.txt`) + +### Pull Request Process + +1. Fork the repository and create a new branch +2. Make your changes with clear commit messages +3. Add tests for new functionality +4. Update documentation as needed +5. Submit a PR with a description of your changes + +### Code Style Guidelines + +- Follow Rust standard coding conventions +- Ensure your code passes `rustfmt` and `clippy` +- Add comments explaining complex logic, especially in GCC interface code + +## Additional Resources + +- [Rustc Dev Guide](https://rustc-dev-guide.rust-lang.org/) +- [GCC Internals Documentation](https://gcc.gnu.org/onlinedocs/gccint/) +- Project-specific documentation in the `doc/` directory: + - [Common errors](doc/errors.md) + - [Debugging](doc/debugging.md) + - [Debugging libgccjit](doc/debugging-libgccjit.md) + - [Git subtree sync](doc/subtree.md) + - [List of useful commands](doc/tips.md) + - [Send a patch to GCC](doc/sending-gcc-patch.md) + +## Getting Help + +If you're stuck or unsure about anything: +1. Check the existing documentation in the `doc/` directory +2. Ask in the IRC or Matrix channels +3. Open a GitHub issue for technical problems +4. Comment on the issue you're working on if you need guidance + +Remember that all contributions, including documentation improvements, bug reports, and feature requests, are valuable to the project. diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index 636e75b94a3f2..967a51a1cc64b 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -56,18 +56,18 @@ dependencies = [ [[package]] name = "gccjit" -version = "2.4.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72fd91f4adbf02b53cfc73c97bc33c5f253009043f30c56a5ec08dd5c8094dc8" +checksum = "ae99a89184220d967dd300139f2d2ae7d52c1a69d632b24aacc57c54625254ce" dependencies = [ "gccjit_sys", ] [[package]] name = "gccjit_sys" -version = "0.5.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fb7b8f48a75e2cfe78c3d9a980b32771c34ffd12d196021ab3f98c49fbd2f0d" +checksum = "24edb7bfe2b7b27c6d09ed23eebfcab0b359c8fe978433f902943e6f127a0f1b" dependencies = [ "libc", ] diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml index 63d37358561e6..c692a90f0a4a4 100644 --- a/compiler/rustc_codegen_gcc/Cargo.toml +++ b/compiler/rustc_codegen_gcc/Cargo.toml @@ -2,7 +2,7 @@ name = "rustc_codegen_gcc" version = "0.1.0" authors = ["Antoni Boucher "] -edition = "2018" +edition = "2024" license = "MIT OR Apache-2.0" [lib] @@ -22,7 +22,7 @@ master = ["gccjit/master"] default = ["master"] [dependencies] -gccjit = "2.4" +gccjit = "2.7" #gccjit = { git = "https://github.com/rust-lang/gccjit.rs" } # Local copy. diff --git a/compiler/rustc_codegen_gcc/Readme.md b/compiler/rustc_codegen_gcc/Readme.md index e92c16ece2f10..859bb1568f4eb 100644 --- a/compiler/rustc_codegen_gcc/Readme.md +++ b/compiler/rustc_codegen_gcc/Readme.md @@ -12,22 +12,38 @@ This is a GCC codegen for rustc, which means it can be loaded by the existing ru The primary goal of this project is to be able to compile Rust code on platforms unsupported by LLVM. A secondary goal is to check if using the gcc backend will provide any run-time speed improvement for the programs compiled using rustc. -### Dependencies - -**rustup:** Follow the instructions on the official [website](https://www.rust-lang.org/tools/install) - -**DejaGnu:** Consider to install DejaGnu which is necessary for running the libgccjit test suite. [website](https://www.gnu.org/software/dejagnu/#downloading) - +## Getting Started +Note: **This requires a patched libgccjit in order to work. +You need to use my [fork of gcc](https://github.com/rust-lang/gcc) which already includes these patches.** +The default configuration (see below in the [Quick start](#quick-start) section) will download a `libgccjit` built in the CI that already contains these patches, so you don't need to build this fork yourself if you use the default configuration. -## Building - -**This requires a patched libgccjit in order to work. -You need to use my [fork of gcc](https://github.com/antoyo/gcc) which already includes these patches.** +### Dependencies -```bash -$ cp config.example.toml config.toml -``` +- rustup: follow instructions on the [official website](https://rustup.rs) +- consider to install DejaGnu which is necessary for running the libgccjit test suite. [website](https://www.gnu.org/software/dejagnu/#downloading) +- additional packages: `flex`, `libmpfr-dev`, `libgmp-dev`, `libmpc3`, `libmpc-dev` + +### Quick start + +1. Clone and configure the repository: + ```bash + git clone https://github.com/rust-lang/rustc_codegen_gcc + cd rustc_codegen_gcc + cp config.example.toml config.toml + ``` + +2. Build and test: + ```bash + ./y.sh prepare # downloads and patches sysroot + ./y.sh build --sysroot --release + + # Verify setup with a simple test + ./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml + + # Run full test suite (expect ~100 failing UI tests) + ./y.sh test --release + ``` If don't need to test GCC patches you wrote in our GCC fork, then the default configuration should be all you need. You can update the `rustc_codegen_gcc` without worrying about GCC. @@ -40,7 +56,7 @@ to do a few more things. To build it (most of these instructions come from [here](https://gcc.gnu.org/onlinedocs/jit/internals/index.html), so don't hesitate to take a look there if you encounter an issue): ```bash -$ git clone https://github.com/antoyo/gcc +$ git clone https://github.com/rust-lang/gcc $ sudo apt install flex libmpfr-dev libgmp-dev libmpc3 libmpc-dev $ mkdir gcc-build gcc-install $ cd gcc-build @@ -143,7 +159,7 @@ You can do the same manually (although we don't recommend it): $ LIBRARY_PATH="[gcc-path value]" LD_LIBRARY_PATH="[gcc-path value]" rustc +$(cat $CG_GCCJIT_DIR/rust-toolchain | grep 'channel' | cut -d '=' -f 2 | sed 's/"//g' | sed 's/ //g') -Cpanic=abort -Zcodegen-backend=$CG_GCCJIT_DIR/target/release/librustc_codegen_gcc.so --sysroot $CG_GCCJIT_DIR/build_sysroot/sysroot my_crate.rs ``` -## Env vars +## Environment variables * _**CG_GCCJIT_DUMP_ALL_MODULES**_: Enables dumping of all compilation modules. When set to "1", a dump is created for each module during compilation and stored in `/tmp/reproducers/`. * _**CG_GCCJIT_DUMP_MODULE**_: Enables dumping of a specific module. When set with the module name, e.g., `CG_GCCJIT_DUMP_MODULE=module_name`, a dump of that specific module is created in `/tmp/reproducers/`. diff --git a/compiler/rustc_codegen_gcc/build_system/Cargo.toml b/compiler/rustc_codegen_gcc/build_system/Cargo.toml index d2600ed5a0314..540d82369fdf7 100644 --- a/compiler/rustc_codegen_gcc/build_system/Cargo.toml +++ b/compiler/rustc_codegen_gcc/build_system/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "y" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] boml = "0.3.1" diff --git a/compiler/rustc_codegen_gcc/build_system/src/clone_gcc.rs b/compiler/rustc_codegen_gcc/build_system/src/clone_gcc.rs index e28ee873eb6be..b49dd47f35219 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/clone_gcc.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/clone_gcc.rs @@ -61,7 +61,7 @@ pub fn run() -> Result<(), String> { return Ok(()); }; - let result = git_clone("https://github.com/antoyo/gcc", Some(&args.out_path), false)?; + let result = git_clone("https://github.com/rust-lang/gcc", Some(&args.out_path), false)?; if result.ran_clone { let gcc_commit = args.config_info.get_gcc_commit()?; println!("Checking out GCC commit `{}`...", gcc_commit); diff --git a/compiler/rustc_codegen_gcc/build_system/src/main.rs b/compiler/rustc_codegen_gcc/build_system/src/main.rs index 393617183061b..c70b00e09ae70 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/main.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/main.rs @@ -60,7 +60,9 @@ pub enum Command { fn main() { if env::var("RUST_BACKTRACE").is_err() { - env::set_var("RUST_BACKTRACE", "1"); + unsafe { + env::set_var("RUST_BACKTRACE", "1"); + } } let command = match env::args().nth(1).as_deref() { diff --git a/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs index 6c29c7d1825b9..df4ac85233b02 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/test.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/test.rs @@ -529,20 +529,21 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> { env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string()); - let extra = - if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" }; - - let rustc_args = &format!( - r#"-Zpanic-abort-tests \ - -Zcodegen-backend="{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}" \ - --sysroot "{sysroot_dir}" -Cpanic=abort{extra}"#, + let codegen_backend_path = format!( + "{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}", pwd = std::env::current_dir() .map_err(|error| format!("`current_dir` failed: {:?}", error))? .display(), channel = args.config_info.channel.as_str(), dylib_ext = args.config_info.dylib_ext, - sysroot_dir = args.config_info.sysroot_path, - extra = extra, + ); + + let extra = + if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" }; + + let rustc_args = format!( + "-Zpanic-abort-tests -Zcodegen-backend={codegen_backend_path} --sysroot {} -Cpanic=abort{extra}", + args.config_info.sysroot_path ); run_command_with_env( @@ -677,7 +678,7 @@ fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> { fn test_libcore(env: &Env, args: &TestArg) -> Result<(), String> { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] libcore"); - let path = get_sysroot_dir().join("sysroot_src/library/core/tests"); + let path = get_sysroot_dir().join("sysroot_src/library/coretests"); let _ = remove_dir_all(path.join("target")); run_cargo_command(&[&"test"], Some(&path), env, args)?; Ok(()) diff --git a/compiler/rustc_codegen_gcc/build_system/src/utils.rs b/compiler/rustc_codegen_gcc/build_system/src/utils.rs index 401c23948e5d3..ca177a5feb86a 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/utils.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/utils.rs @@ -10,7 +10,7 @@ use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus, Output}; #[cfg(unix)] -extern "C" { +unsafe extern "C" { fn raise(signal: c_int) -> c_int; } diff --git a/compiler/rustc_codegen_gcc/doc/add-attribute.md b/compiler/rustc_codegen_gcc/doc/add-attribute.md index ae3bcc5e2ebe2..267c181952556 100644 --- a/compiler/rustc_codegen_gcc/doc/add-attribute.md +++ b/compiler/rustc_codegen_gcc/doc/add-attribute.md @@ -14,4 +14,4 @@ Finally, you need to update this repository by calling the relevant API you adde To test it, build `gcc`, run `cargo update -p gccjit` and then you can test the generated output for a given Rust crate. -[gccjit.rs]: https://github.com/antoyo/gccjit.rs +[gccjit.rs]: https://github.com/rust-lang/gccjit.rs diff --git a/compiler/rustc_codegen_gcc/example/example.rs b/compiler/rustc_codegen_gcc/example/example.rs index 03470b74d0a13..888fa89201e13 100644 --- a/compiler/rustc_codegen_gcc/example/example.rs +++ b/compiler/rustc_codegen_gcc/example/example.rs @@ -1,6 +1,6 @@ #![feature(no_core, unboxed_closures)] #![no_core] -#![allow(dead_code)] +#![allow(dead_code, unnecessary_transmutes)] extern crate mini_core; @@ -11,11 +11,7 @@ fn abc(a: u8) -> u8 { } fn bcd(b: bool, a: u8) -> u8 { - if b { - a * 2 - } else { - a * 3 - } + if b { a * 2 } else { a * 3 } } fn call() { diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index 3dad35bc4ce47..d1d8e8fd5bc45 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -1,6 +1,14 @@ #![feature( - no_core, lang_items, intrinsics, unboxed_closures, extern_types, - decl_macro, rustc_attrs, transparent_unions, auto_traits, freeze_impls, + no_core, + lang_items, + intrinsics, + unboxed_closures, + extern_types, + decl_macro, + rustc_attrs, + transparent_unions, + auto_traits, + freeze_impls, thread_local )] #![no_core] @@ -35,13 +43,13 @@ impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} pub trait DispatchFromDyn {} // &T -> &U -impl<'a, T: ?Sized+Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} // &mut T -> &mut U -impl<'a, T: ?Sized+Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} // *const T -> *const U -impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} +impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} // *mut T -> *mut U -impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} +impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} impl, U: ?Sized> DispatchFromDyn> for Box {} #[lang = "legacy_receiver"] @@ -51,6 +59,9 @@ impl LegacyReceiver for &T {} impl LegacyReceiver for &mut T {} impl LegacyReceiver for Box {} +#[lang = "receiver"] +trait Receiver {} + #[lang = "copy"] pub trait Copy {} @@ -63,10 +74,13 @@ impl Copy for u16 {} impl Copy for u32 {} impl Copy for u64 {} impl Copy for usize {} +impl Copy for u128 {} impl Copy for i8 {} impl Copy for i16 {} impl Copy for i32 {} +impl Copy for i64 {} impl Copy for isize {} +impl Copy for i128 {} impl Copy for f32 {} impl Copy for f64 {} impl Copy for char {} @@ -134,6 +148,14 @@ impl Mul for u8 { } } +impl Mul for i32 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + self * rhs + } +} + impl Mul for usize { type Output = Self; @@ -142,6 +164,14 @@ impl Mul for usize { } } +impl Mul for isize { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + self * rhs + } +} + #[lang = "add"] pub trait Add { type Output; @@ -165,6 +195,14 @@ impl Add for i8 { } } +impl Add for i32 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + impl Add for usize { type Output = Self; @@ -196,6 +234,14 @@ impl Sub for usize { } } +impl Sub for isize { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + self - rhs + } +} + impl Sub for u8 { type Output = Self; @@ -220,6 +266,14 @@ impl Sub for i16 { } } +impl Sub for i32 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + self - rhs + } +} + #[lang = "rem"] pub trait Rem { type Output; @@ -292,7 +346,6 @@ impl PartialEq for u32 { } } - impl PartialEq for u64 { fn eq(&self, other: &u64) -> bool { (*self) == (*other) @@ -479,7 +532,11 @@ fn panic_in_cleanup() -> ! { #[track_caller] fn panic_bounds_check(index: usize, len: usize) -> ! { unsafe { - libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index); + libc::printf( + "index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, + len, + index, + ); intrinsics::abort(); } } @@ -507,8 +564,7 @@ pub trait Deref { fn deref(&self) -> &Self::Target; } -pub trait Allocator { -} +pub trait Allocator {} impl Allocator for () {} @@ -590,30 +646,32 @@ pub union MaybeUninit { } pub mod intrinsics { + #[rustc_intrinsic] + pub const fn black_box(_dummy: T) -> T; #[rustc_intrinsic] pub fn abort() -> !; #[rustc_intrinsic] pub fn size_of() -> usize; #[rustc_intrinsic] - pub unsafe fn size_of_val(_val: *const T) -> usize; + pub unsafe fn size_of_val(val: *const T) -> usize; #[rustc_intrinsic] pub fn min_align_of() -> usize; #[rustc_intrinsic] - pub unsafe fn min_align_of_val(_val: *const T) -> usize; + pub unsafe fn min_align_of_val(val: *const T) -> usize; #[rustc_intrinsic] - pub unsafe fn copy(_src: *const T, _dst: *mut T, _count: usize); + pub unsafe fn copy(src: *const T, dst: *mut T, count: usize); #[rustc_intrinsic] - pub unsafe fn transmute(_e: T) -> U; + pub unsafe fn transmute(e: T) -> U; #[rustc_intrinsic] - pub unsafe fn ctlz_nonzero(_x: T) -> u32; + pub unsafe fn ctlz_nonzero(x: T) -> u32; #[rustc_intrinsic] pub fn needs_drop() -> bool; #[rustc_intrinsic] - pub fn bitreverse(_x: T) -> T; + pub fn bitreverse(x: T) -> T; #[rustc_intrinsic] - pub fn bswap(_x: T) -> T; + pub fn bswap(x: T) -> T; #[rustc_intrinsic] - pub unsafe fn write_bytes(_dst: *mut T, _val: u8, _count: usize); + pub unsafe fn write_bytes(dst: *mut T, val: u8, count: usize); #[rustc_intrinsic] pub unsafe fn unreachable() -> !; } @@ -628,6 +686,10 @@ pub mod libc { pub fn memcpy(dst: *mut u8, src: *const u8, size: usize); pub fn memmove(dst: *mut u8, src: *const u8, size: usize); pub fn strncpy(dst: *mut u8, src: *const u8, size: usize); + pub fn fflush(stream: *mut i32) -> i32; + pub fn exit(status: i32); + + pub static stdout: *mut i32; } } @@ -663,19 +725,27 @@ pub struct VaList<'a>(&'a mut VaListImpl); #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro stringify($($t:tt)*) { /* compiler built-in */ } +pub macro stringify($($t:tt)*) { + /* compiler built-in */ +} #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro file() { /* compiler built-in */ } +pub macro file() { + /* compiler built-in */ +} #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro line() { /* compiler built-in */ } +pub macro line() { + /* compiler built-in */ +} #[rustc_builtin_macro] #[rustc_macro_transparency = "semitransparent"] -pub macro cfg() { /* compiler built-in */ } +pub macro cfg() { + /* compiler built-in */ +} pub static A_STATIC: u8 = 42; diff --git a/compiler/rustc_codegen_gcc/libgccjit.version b/compiler/rustc_codegen_gcc/libgccjit.version index 417fd5b03935d..f62154968d3de 100644 --- a/compiler/rustc_codegen_gcc/libgccjit.version +++ b/compiler/rustc_codegen_gcc/libgccjit.version @@ -1 +1 @@ -e607be166673a8de9fc07f6f02c60426e556c5f2 +04ce66d8c918de9273bd7101638ad8724edf5e21 diff --git a/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch deleted file mode 100644 index 70e3e2ba7fee1..0000000000000 --- a/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch +++ /dev/null @@ -1,44 +0,0 @@ -From af0e237f056fa838c77463381a19b0dc993c0a35 Mon Sep 17 00:00:00 2001 -From: None -Date: Sun, 1 Sep 2024 11:42:17 -0400 -Subject: [PATCH] Disable not compiling tests - ---- - library/core/tests/Cargo.toml | 14 ++++++++++++++ - library/core/tests/lib.rs | 1 + - 2 files changed, 15 insertions(+) - create mode 100644 library/core/tests/Cargo.toml - -diff --git a/library/core/tests/Cargo.toml b/library/core/tests/Cargo.toml -new file mode 100644 -index 0000000..ca326ac ---- /dev/null -+++ b/library/core/tests/Cargo.toml -@@ -0,0 +1,14 @@ -+[workspace] -+ -+[package] -+name = "coretests" -+version = "0.0.0" -+edition = "2021" -+ -+[lib] -+name = "coretests" -+path = "lib.rs" -+ -+[dependencies] -+rand = { version = "0.8.5", default-features = false } -+rand_xorshift = { version = "0.3.0", default-features = false } -diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs -index a4a7946..ecfe43f 100644 ---- a/library/core/tests/lib.rs -+++ b/library/core/tests/lib.rs -@@ -1,4 +1,5 @@ - // tidy-alphabetical-start -+#![cfg(test)] - #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] - #![cfg_attr(test, feature(cfg_match))] - #![feature(alloc_layout_extra)] --- -2.47.1 - diff --git a/compiler/rustc_codegen_gcc/patches/0028-core-Disable-long-running-tests.patch b/compiler/rustc_codegen_gcc/patches/0028-core-Disable-long-running-tests.patch index dc1beae6d2e71..20df4245cfdf2 100644 --- a/compiler/rustc_codegen_gcc/patches/0028-core-Disable-long-running-tests.patch +++ b/compiler/rustc_codegen_gcc/patches/0028-core-Disable-long-running-tests.patch @@ -1,17 +1,17 @@ -From eb703e627e7a84f1cd8d0d87f0f69da1f0acf765 Mon Sep 17 00:00:00 2001 -From: bjorn3 -Date: Fri, 3 Dec 2021 12:16:30 +0100 +From ec2d0dc77fb484d926b45bb626b0db6a4bb0ab5c Mon Sep 17 00:00:00 2001 +From: None +Date: Thu, 27 Mar 2025 09:20:41 -0400 Subject: [PATCH] Disable long running tests --- - library/core/tests/slice.rs | 2 ++ + library/coretests/tests/slice.rs | 2 ++ 1 file changed, 2 insertions(+) -diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs -index 8402833..84592e0 100644 ---- a/library/core/tests/slice.rs -+++ b/library/core/tests/slice.rs -@@ -2462,6 +2462,7 @@ take_tests! { +diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs +index d17e681..fba5cd6 100644 +--- a/library/coretests/tests/slice.rs ++++ b/library/coretests/tests/slice.rs +@@ -2486,6 +2486,7 @@ split_off_tests! { #[cfg(not(miri))] // unused in Miri const EMPTY_MAX: &'static [()] = &[(); usize::MAX]; @@ -19,14 +19,14 @@ index 8402833..84592e0 100644 // can't be a constant due to const mutability rules #[cfg(not(miri))] // unused in Miri macro_rules! empty_max_mut { -@@ -2485,6 +2486,7 @@ take_tests! { - (take_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), - (take_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), +@@ -2509,6 +2510,7 @@ split_off_tests! { + (split_off_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), + (split_off_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), } +*/ #[test] fn test_slice_from_ptr_range() { -- -2.26.2.7.g19db9cfb68 +2.49.0 diff --git a/compiler/rustc_codegen_gcc/patches/cross_patches/0001-Disable-libstd-and-libtest-dylib.patch b/compiler/rustc_codegen_gcc/patches/cross_patches/0001-Disable-libstd-and-libtest-dylib.patch index c220f53040f05..fa360fe9e74e7 100644 --- a/compiler/rustc_codegen_gcc/patches/cross_patches/0001-Disable-libstd-and-libtest-dylib.patch +++ b/compiler/rustc_codegen_gcc/patches/cross_patches/0001-Disable-libstd-and-libtest-dylib.patch @@ -1,19 +1,18 @@ -From 966beefe08be6045bfcca26079b76a7a80413080 Mon Sep 17 00:00:00 2001 +From b2911e732d1bf0e28872495c4c47af1dad3c7911 Mon Sep 17 00:00:00 2001 From: None -Date: Thu, 28 Sep 2023 17:37:38 -0400 +Date: Thu, 27 Mar 2025 14:30:10 -0400 Subject: [PATCH] Disable libstd and libtest dylib --- - library/std/Cargo.toml | 2 +- - library/test/Cargo.toml | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) + library/std/Cargo.toml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml -index 5b21355..cb0c49b 100644 +index 176da60..c183cdb 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml -@@ -9,7 +9,7 @@ description = "The Rust Standard Library" - edition = "2021" +@@ -10,7 +10,7 @@ edition = "2024" + autobenches = false [lib] -crate-type = ["dylib", "rlib"] @@ -21,3 +20,6 @@ index 5b21355..cb0c49b 100644 [dependencies] alloc = { path = "../alloc", public = true } +-- +2.49.0 + diff --git a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch index 9ef5e0e4f4672..9d5b2dc537d2c 100644 --- a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch +++ b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch @@ -1,25 +1,17 @@ -From 124a11ce086952a5794d5cfbaa45175809497b81 Mon Sep 17 00:00:00 2001 +From 1a8f6b8e39f343959d4d2e6b6957a6d780ac3fc0 Mon Sep 17 00:00:00 2001 From: None -Date: Sat, 18 Nov 2023 10:50:36 -0500 -Subject: [PATCH] [core] Disable portable-simd test +Date: Thu, 27 Mar 2025 14:32:14 -0400 +Subject: [PATCH] Disable portable-simd test --- - library/core/tests/lib.rs | 2 -- - 1 file changed, 2 deletions(-) + library/coretests/tests/lib.rs | 1 - + 1 file changed, 1 deletion(-) -diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs -index b71786c..cf484d5 100644 ---- a/library/core/tests/lib.rs -+++ b/library/core/tests/lib.rs -@@ -87,7 +87,6 @@ - #![feature(numfmt)] - #![feature(pattern)] - #![feature(pointer_is_aligned_to)] --#![feature(portable_simd)] - #![feature(ptr_metadata)] - #![feature(slice_from_ptr_range)] - #![feature(slice_internals)] -@@ -155,7 +154,6 @@ mod pin; +diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs +index 79022fe..9223b2f 100644 +--- a/library/coretests/tests/lib.rs ++++ b/library/coretests/tests/lib.rs +@@ -165,7 +165,6 @@ mod pin; mod pin_macro; mod ptr; mod result; @@ -27,4 +19,6 @@ index b71786c..cf484d5 100644 mod slice; mod str; mod str_lossy; --- 2.45.2 +-- +2.49.0 + diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain index 940b3de9f7453..a8cda28688c84 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-01-12" +channel = "nightly-2025-05-12" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 717baebcd8cd6..d882d3eecf49a 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -10,13 +10,15 @@ use rustc_middle::ty::layout::LayoutOf; #[cfg(feature = "master")] use rustc_session::config; use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode}; +#[cfg(feature = "master")] +use rustc_target::callconv::{Conv, RiscvInterruptKind}; use crate::builder::Builder; use crate::context::CodegenCx; use crate::intrinsic::ArgAbiExt; use crate::type_of::LayoutGccExt; -impl<'a, 'gcc, 'tcx> AbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { +impl AbiBuilderMethods for Builder<'_, '_, '_> { fn get_param(&mut self, index: usize) -> Self::Value { let func = self.current_func(); let param = func.get_param(index as i32); @@ -105,6 +107,8 @@ pub trait FnAbiGccExt<'gcc, 'tcx> { // TODO(antoyo): return a function pointer type instead? fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> FnAbiGcc<'gcc>; fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>; + #[cfg(feature = "master")] + fn gcc_cconv(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Option>; } impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { @@ -227,4 +231,66 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { ); pointer_type } + + #[cfg(feature = "master")] + fn gcc_cconv(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Option> { + conv_to_fn_attribute(self.conv, &cx.tcx.sess.target.arch) + } +} + +#[cfg(feature = "master")] +pub fn conv_to_fn_attribute<'gcc>(conv: Conv, arch: &str) -> Option> { + let attribute = match conv { + Conv::C | Conv::Rust => return None, + Conv::CCmseNonSecureCall => { + if arch == "arm" { + FnAttribute::ArmCmseNonsecureCall + } else { + return None; + } + } + Conv::CCmseNonSecureEntry => { + if arch == "arm" { + FnAttribute::ArmCmseNonsecureEntry + } else { + return None; + } + } + Conv::Cold => FnAttribute::Cold, + // NOTE: the preserve attributes are not yet implemented in GCC: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110899 + Conv::PreserveMost => return None, + Conv::PreserveAll => return None, + Conv::GpuKernel => { + if arch == "amdgpu" { + FnAttribute::GcnAmdGpuHsaKernel + } else if arch == "nvptx64" { + FnAttribute::NvptxKernel + } else { + panic!("Architecture {} does not support GpuKernel calling convention", arch); + } + } + // TODO(antoyo): check if those AVR attributes are mapped correctly. + Conv::AvrInterrupt => FnAttribute::AvrSignal, + Conv::AvrNonBlockingInterrupt => FnAttribute::AvrInterrupt, + Conv::ArmAapcs => FnAttribute::ArmPcs("aapcs"), + Conv::Msp430Intr => FnAttribute::Msp430Interrupt, + Conv::RiscvInterrupt { kind } => { + let kind = match kind { + RiscvInterruptKind::Machine => "machine", + RiscvInterruptKind::Supervisor => "supervisor", + }; + FnAttribute::RiscvInterrupt(kind) + } + Conv::X86Fastcall => FnAttribute::X86FastCall, + Conv::X86Intr => FnAttribute::X86Interrupt, + Conv::X86Stdcall => FnAttribute::X86Stdcall, + Conv::X86ThisCall => FnAttribute::X86ThisCall, + // NOTE: the vectorcall calling convention is not yet implemented in GCC: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485 + Conv::X86VectorCall => return None, + Conv::X86_64SysV => FnAttribute::X86SysvAbi, + Conv::X86_64Win64 => FnAttribute::X86MsAbi, + }; + Some(attribute) } diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index 416f3231a13c4..f4ebd42ee2dc0 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -8,6 +8,7 @@ use rustc_ast::expand::allocator::{ use rustc_middle::bug; use rustc_middle::ty::TyCtxt; use rustc_session::config::OomStrategy; +use rustc_symbol_mangling::mangle_internal_symbol; use crate::GccContext; #[cfg(feature = "master")] @@ -53,8 +54,8 @@ pub(crate) unsafe fn codegen( panic!("invalid allocator output") } }; - let from_name = global_fn_name(method.name); - let to_name = default_fn_name(method.name); + let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); + let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); create_wrapper_function(tcx, context, &from_name, &to_name, &types, output); } @@ -64,13 +65,13 @@ pub(crate) unsafe fn codegen( create_wrapper_function( tcx, context, - "__rust_alloc_error_handler", - alloc_error_handler_name(alloc_error_handler_kind), + &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), + &mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind)), &[usize, usize], None, ); - let name = OomStrategy::SYMBOL.to_string(); + let name = mangle_internal_symbol(tcx, OomStrategy::SYMBOL); let global = context.new_global(None, GlobalKind::Exported, i8, name); #[cfg(feature = "master")] global.add_attribute(VarAttribute::Visibility(symbol_visibility_to_gcc( @@ -80,7 +81,7 @@ pub(crate) unsafe fn codegen( let value = context.new_rvalue_from_int(i8, value as i32); global.global_set_initializer_rvalue(value); - let name = NO_ALLOC_SHIM_IS_UNSTABLE.to_string(); + let name = mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE); let global = context.new_global(None, GlobalKind::Exported, i8, name); #[cfg(feature = "master")] global.add_attribute(VarAttribute::Visibility(symbol_visibility_to_gcc( diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 415f8affab901..c35337ae7ce0c 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -36,7 +36,8 @@ use crate::type_of::LayoutGccExt; // // 3. Clobbers. GCC has a separate list of clobbers, and clobbers don't have indexes. // Contrary, Rust expresses clobbers through "out" operands that aren't tied to -// a variable (`_`), and such "clobbers" do have index. +// a variable (`_`), and such "clobbers" do have index. Input operands cannot also +// be clobbered. // // 4. Furthermore, GCC Extended Asm does not support explicit register constraints // (like `out("eax")`) directly, offering so-called "local register variables" @@ -161,6 +162,16 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { // Also, we don't emit any asm operands immediately; we save them to // the one of the buffers to be emitted later. + let mut input_registers = vec![]; + + for op in rust_operands { + if let InlineAsmOperandRef::In { reg, .. } = *op + && let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) + { + input_registers.push(reg_name); + } + } + // 1. Normal variables (and saving operands to buffers). for (rust_idx, op) in rust_operands.iter().enumerate() { match *op { @@ -183,25 +194,39 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { continue; } (Register(reg_name), None) => { - // `clobber_abi` can add lots of clobbers that are not supported by the target, - // such as AVX-512 registers, so we just ignore unsupported registers - let is_target_supported = - reg.reg_class().supported_types(asm_arch, true).iter().any( - |&(_, feature)| { - if let Some(feature) = feature { - self.tcx - .asm_target_features(instance.def_id()) - .contains(&feature) - } else { - true // Register class is unconditionally supported - } - }, - ); - - if is_target_supported && !clobbers.contains(®_name) { - clobbers.push(reg_name); + if input_registers.contains(®_name) { + // the `clobber_abi` operand is converted into a series of + // `lateout("reg") _` operands. Of course, a user could also + // explicitly define such an output operand. + // + // GCC does not allow input registers to be clobbered, so if this out register + // is also used as an in register, do not add it to the clobbers list. + // it will be treated as a lateout register with `out_place: None` + if !late { + bug!("input registers can only be used as lateout regisers"); + } + ("r", dummy_output_type(self.cx, reg.reg_class())) + } else { + // `clobber_abi` can add lots of clobbers that are not supported by the target, + // such as AVX-512 registers, so we just ignore unsupported registers + let is_target_supported = + reg.reg_class().supported_types(asm_arch, true).iter().any( + |&(_, feature)| { + if let Some(feature) = feature { + self.tcx + .asm_target_features(instance.def_id()) + .contains(&feature) + } else { + true // Register class is unconditionally supported + } + }, + ); + + if is_target_supported && !clobbers.contains(®_name) { + clobbers.push(reg_name); + } + continue; } - continue; } }; @@ -230,13 +255,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { } InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => { - let constraint = - if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) { - constraint - } else { - // left for the next pass - continue; - }; + let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) else { + // left for the next pass + continue; + }; // Rustc frontend guarantees that input and output types are "compatible", // so we can just use input var's type for the output variable. @@ -589,114 +611,127 @@ fn estimate_template_length( } /// Converts a register class to a GCC constraint code. -fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister { - let constraint = match reg { - // For vector registers LLVM wants the register name to match the type size. +fn reg_to_gcc(reg_or_reg_class: InlineAsmRegOrRegClass) -> ConstraintOrRegister { + match reg_or_reg_class { InlineAsmRegOrRegClass::Reg(reg) => { - match reg { - InlineAsmReg::X86(_) => { - // TODO(antoyo): add support for vector register. - // - // // For explicit registers, we have to create a register variable: https://stackoverflow.com/a/31774784/389119 - return ConstraintOrRegister::Register(match reg.name() { - // Some of registers' names does not map 1-1 from rust to gcc - "st(0)" => "st", + ConstraintOrRegister::Register(explicit_reg_to_gcc(reg)) + } + InlineAsmRegOrRegClass::RegClass(reg_class) => { + ConstraintOrRegister::Constraint(reg_class_to_gcc(reg_class)) + } + } +} - name => name, - }); +fn explicit_reg_to_gcc(reg: InlineAsmReg) -> &'static str { + // For explicit registers, we have to create a register variable: https://stackoverflow.com/a/31774784/389119 + match reg { + InlineAsmReg::X86(reg) => { + // TODO(antoyo): add support for vector register. + match reg.reg_class() { + X86InlineAsmRegClass::reg_byte => { + // GCC does not support the `b` suffix, so we just strip it + // see https://github.com/rust-lang/rustc_codegen_gcc/issues/485 + reg.name().trim_end_matches('b') } + _ => match reg.name() { + // Some of registers' names does not map 1-1 from rust to gcc + "st(0)" => "st", - _ => unimplemented!(), + name => name, + }, } } - // They can be retrieved from https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html - InlineAsmRegOrRegClass::RegClass(reg) => match reg { - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => "r", - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => "w", - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x", - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => "t", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_upper) => "d", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => "r", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => "w", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => "e", - InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w", - InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::preg) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => "a", - InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_data) => "d", - InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => "d", // more specific than "r" - InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => "r", - // https://github.com/gcc-mirror/gcc/blob/master/gcc/config/nvptx/nvptx.md -> look for - // "define_constraint". - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => "h", - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => "r", - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => "l", - - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => "v", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r", - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q", - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q", - InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x", - InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v", - InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "Yk", - InlineAsmRegClass::X86( - X86InlineAsmRegClass::kreg0 - | X86InlineAsmRegClass::x87_reg - | X86InlineAsmRegClass::mmx_reg - | X86InlineAsmRegClass::tmm_reg, - ) => unreachable!("clobber-only"), - InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { - bug!("GCC backend does not support SPIR-V") - } - InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg_addr) => "a", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::vreg) => "v", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::areg) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::yreg) => unreachable!("clobber-only"), - InlineAsmRegClass::Err => unreachable!(), - }, - }; - ConstraintOrRegister::Constraint(constraint) + _ => unimplemented!(), + } +} + +/// They can be retrieved from https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html +fn reg_class_to_gcc(reg_class: InlineAsmRegClass) -> &'static str { + match reg_class { + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => "r", + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => "w", + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x", + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => { + unreachable!("clobber-only") + } + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => "t", + InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_upper) => "d", + InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => "r", + InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => "w", + InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => "e", + InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w", + InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::preg) => { + unreachable!("clobber-only") + } + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => "a", + InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_data) => "d", + InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => "d", // more specific than "r" + InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => "r", + // https://github.com/gcc-mirror/gcc/blob/master/gcc/config/nvptx/nvptx.md -> look for + // "define_constraint". + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => "h", + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => "r", + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => "l", + + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => "v", + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) + | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { + unreachable!("clobber-only") + } + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => { + unreachable!("clobber-only") + } + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r", + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q", + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q", + InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x", + InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v", + InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "Yk", + InlineAsmRegClass::X86( + X86InlineAsmRegClass::kreg0 + | X86InlineAsmRegClass::x87_reg + | X86InlineAsmRegClass::mmx_reg + | X86InlineAsmRegClass::tmm_reg, + ) => unreachable!("clobber-only"), + InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { + bug!("GCC backend does not support SPIR-V") + } + InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r", + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg_addr) => "a", + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::vreg) => "v", + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::areg) => { + unreachable!("clobber-only") + } + InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::yreg) => unreachable!("clobber-only"), + InlineAsmRegClass::Err => unreachable!(), + } } /// Type to use for outputs that are discarded. It doesn't really matter what @@ -794,7 +829,7 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn codegen_global_asm( - &self, + &mut self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index 69b04dd579693..c853c88a6eac9 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -2,25 +2,73 @@ use gccjit::FnAttribute; use gccjit::Function; #[cfg(feature = "master")] -use rustc_attr_parsing::InlineAttr; -use rustc_attr_parsing::InstructionSetAttr; +use rustc_attr_data_structures::InlineAttr; +use rustc_attr_data_structures::InstructionSetAttr; #[cfg(feature = "master")] use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +#[cfg(feature = "master")] +use rustc_middle::mir::TerminatorKind; use rustc_middle::ty; use crate::context::CodegenCx; use crate::gcc_util::to_gcc_features; -/// Get GCC attribute for the provided inline heuristic. +/// Checks if the function `instance` is recursively inline. +/// Returns `false` if a functions is guaranteed to be non-recursive, and `true` if it *might* be recursive. +#[cfg(feature = "master")] +fn resursively_inline<'gcc, 'tcx>( + cx: &CodegenCx<'gcc, 'tcx>, + instance: ty::Instance<'tcx>, +) -> bool { + // No body, so we can't check if this is recursively inline, so we assume it is. + if !cx.tcx.is_mir_available(instance.def_id()) { + return true; + } + // `expect_local` ought to never fail: we should be checking a function within this codegen unit. + let body = cx.tcx.optimized_mir(instance.def_id()); + for block in body.basic_blocks.iter() { + let Some(ref terminator) = block.terminator else { continue }; + // I assume that the recursive-inline issue applies only to functions, and not to drops. + // In principle, a recursive, `#[inline(always)]` drop could(?) exist, but I don't think it does. + let TerminatorKind::Call { ref func, .. } = terminator.kind else { continue }; + let Some((def, _args)) = func.const_fn_def() else { continue }; + // Check if the called function is recursively inline. + if matches!( + cx.tcx.codegen_fn_attrs(def).inline, + InlineAttr::Always | InlineAttr::Force { .. } + ) { + return true; + } + } + false +} + +/// Get GCC attribute for the provided inline heuristic, attached to `instance`. #[cfg(feature = "master")] #[inline] fn inline_attr<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, inline: InlineAttr, + instance: ty::Instance<'tcx>, ) -> Option> { match inline { + InlineAttr::Always => { + // We can't simply always return `always_inline` unconditionally. + // It is *NOT A HINT* and does not work for recursive functions. + // + // So, it can only be applied *if*: + // The current function does not call any functions marked `#[inline(always)]`. + // + // That prevents issues steming from recursive `#[inline(always)]` at a *relatively* small cost. + // We *only* need to check all the terminators of a function marked with this attribute. + if resursively_inline(cx, instance) { + Some(FnAttribute::Inline) + } else { + Some(FnAttribute::AlwaysInline) + } + } InlineAttr::Hint => Some(FnAttribute::Inline), - InlineAttr::Always | InlineAttr::Force { .. } => Some(FnAttribute::AlwaysInline), + InlineAttr::Force { .. } => Some(FnAttribute::AlwaysInline), InlineAttr::Never => { if cx.sess().target.arch != "amdgpu" { Some(FnAttribute::NoInline) @@ -52,7 +100,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>( } else { codegen_fn_attrs.inline }; - if let Some(attr) = inline_attr(cx, inline) { + if let Some(attr) = inline_attr(cx, inline, instance) { if let FnAttribute::AlwaysInline = attr { func.add_attribute(FnAttribute::Inline); } @@ -88,14 +136,8 @@ pub fn from_fn_attrs<'gcc, 'tcx>( let target_features = function_features .iter() .filter_map(|feature| { - // FIXME(antoyo): for some reasons, disabling SSE results in the following error when - // compiling Rust for Linux: - // SSE register return with SSE disabled - // TODO(antoyo): support soft-float and retpoline-external-thunk. - if feature.contains("soft-float") - || feature.contains("retpoline-external-thunk") - || *feature == "-sse" - { + // TODO(antoyo): support soft-float. + if feature.contains("soft-float") { return None; } diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index e5221c7da3197..e9c87f357793c 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -44,7 +44,11 @@ use crate::{GccCodegenBackend, GccContext, SyncContext, to_gcc_opt_level}; pub fn crate_type_allows_lto(crate_type: CrateType) -> bool { match crate_type { - CrateType::Executable | CrateType::Dylib | CrateType::Staticlib | CrateType::Cdylib => true, + CrateType::Executable + | CrateType::Dylib + | CrateType::Staticlib + | CrateType::Cdylib + | CrateType::Sdylib => true, CrateType::Rlib | CrateType::ProcMacro => false, } } @@ -589,7 +593,7 @@ fn thin_lto( Ok((opt_jobs, copy_jobs)) } -pub unsafe fn optimize_thin_module( +pub fn optimize_thin_module( thin_module: ThinModule, _cgcx: &CodegenContext, ) -> Result, FatalError> { diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index 51c5ba73e32bc..09e955acf3900 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -14,7 +14,7 @@ use crate::base::add_pic_option; use crate::errors::CopyBitcode; use crate::{GccCodegenBackend, GccContext}; -pub(crate) unsafe fn codegen( +pub(crate) fn codegen( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, @@ -24,19 +24,23 @@ pub(crate) unsafe fn codegen( { let context = &module.module_llvm.context; - let module_name = module.name.clone(); - let should_combine_object_files = module.module_llvm.should_combine_object_files; - let module_name = Some(&module_name[..]); - // NOTE: Only generate object files with GIMPLE when this environment variable is set for // now because this requires a particular setup (same gcc/lto1/lto-wrapper commit as libgccjit). // TODO(antoyo): remove this environment variable. let fat_lto = env::var("EMBED_LTO_BITCODE").as_deref() == Ok("1"); - let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); - let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); + let bc_out = cgcx.output_filenames.temp_path_for_cgu( + OutputType::Bitcode, + &module.name, + cgcx.invocation_temp.as_deref(), + ); + let obj_out = cgcx.output_filenames.temp_path_for_cgu( + OutputType::Object, + &module.name, + cgcx.invocation_temp.as_deref(), + ); if config.bitcode_needed() { if fat_lto { @@ -117,14 +121,22 @@ pub(crate) unsafe fn codegen( } if config.emit_ir { - let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); + let out = cgcx.output_filenames.temp_path_for_cgu( + OutputType::LlvmAssembly, + &module.name, + cgcx.invocation_temp.as_deref(), + ); std::fs::write(out, "").expect("write file"); } if config.emit_asm { let _timer = cgcx.prof.generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name); - let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); + let path = cgcx.output_filenames.temp_path_for_cgu( + OutputType::Assembly, + &module.name, + cgcx.invocation_temp.as_deref(), + ); context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str")); } @@ -238,6 +250,7 @@ pub(crate) unsafe fn codegen( config.emit_asm, config.emit_ir, &cgcx.output_filenames, + cgcx.invocation_temp.as_deref(), )) } diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 9b495174a3fab..a9d7808c833bb 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -206,7 +206,7 @@ pub fn compile_codegen_unit( let f128_type_supported = target_info.supports_target_dependent_type(CType::Float128); let u128_type_supported = target_info.supports_target_dependent_type(CType::UInt128t); // TODO: improve this to avoid passing that many arguments. - let cx = CodegenCx::new( + let mut cx = CodegenCx::new( &context, cgu, tcx, @@ -223,8 +223,8 @@ pub fn compile_codegen_unit( } // ... and now that we have everything pre-defined, fill out those definitions. - for &(mono_item, _) in &mono_items { - mono_item.define::>(&cx); + for &(mono_item, item_data) in &mono_items { + mono_item.define::>(&mut cx, item_data); } // If this codegen unit contains the main function, also create the diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index c8b7616e64509..4e2163201fd01 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -45,7 +45,7 @@ enum ExtremumOperation { Min, } -pub struct Builder<'a: 'gcc, 'gcc, 'tcx> { +pub struct Builder<'a, 'gcc, 'tcx> { pub cx: &'a CodegenCx<'gcc, 'tcx>, pub block: Block<'gcc>, pub location: Option>, @@ -368,16 +368,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let previous_arg_count = args.len(); let orig_args = args; let args = { - let function_address_names = self.function_address_names.borrow(); - let original_function_name = function_address_names.get(&func_ptr); func_ptr = llvm::adjust_function(self.context, &func_name, func_ptr, args); - llvm::adjust_intrinsic_arguments( - self, - gcc_func, - args.into(), - &func_name, - original_function_name, - ) + llvm::adjust_intrinsic_arguments(self, gcc_func, args.into(), &func_name) }; let args_adjusted = args.len() != previous_arg_count; let args = self.check_ptr_call("call", func_ptr, &args); @@ -576,11 +568,28 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { ) { let mut gcc_cases = vec![]; let typ = self.val_ty(value); - for (on_val, dest) in cases { - let on_val = self.const_uint_big(typ, on_val); - gcc_cases.push(self.context.new_case(on_val, on_val, dest)); + // FIXME(FractalFir): This is a workaround for a libgccjit limitation. + // Currently, libgccjit can't directly create 128 bit integers. + // Since switch cases must be values, and casts are not constant, we can't use 128 bit switch cases. + // In such a case, we will simply fall back to an if-ladder. + // This *may* be slower than a native switch, but a slow working solution is better than none at all. + if typ.is_i128(self) || typ.is_u128(self) { + for (on_val, dest) in cases { + let on_val = self.const_uint_big(typ, on_val); + let is_case = + self.context.new_comparison(self.location, ComparisonOp::Equals, value, on_val); + let next_block = self.current_func().new_block("case"); + self.block.end_with_conditional(self.location, is_case, dest, next_block); + self.block = next_block; + } + self.block.end_with_jump(self.location, default_block); + } else { + for (on_val, dest) in cases { + let on_val = self.const_uint_big(typ, on_val); + gcc_cases.push(self.context.new_case(on_val, on_val, dest)); + } + self.block.end_with_switch(self.location, value, default_block, &gcc_cases); } - self.block.end_with_switch(self.location, value, default_block, &gcc_cases); } #[cfg(feature = "master")] @@ -1271,7 +1280,50 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn fcmp(&mut self, op: RealPredicate, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { - self.context.new_comparison(self.location, op.to_gcc_comparison(), lhs, rhs) + // LLVM has a concept of "unordered compares", where eg ULT returns true if either the two + // arguments are unordered (i.e. either is NaN), or the lhs is less than the rhs. GCC does + // not natively have this concept, so in some cases we must manually handle NaNs + let must_handle_nan = match op { + RealPredicate::RealPredicateFalse => unreachable!(), + RealPredicate::RealOEQ => false, + RealPredicate::RealOGT => false, + RealPredicate::RealOGE => false, + RealPredicate::RealOLT => false, + RealPredicate::RealOLE => false, + RealPredicate::RealONE => false, + RealPredicate::RealORD => unreachable!(), + RealPredicate::RealUNO => unreachable!(), + RealPredicate::RealUEQ => false, + RealPredicate::RealUGT => true, + RealPredicate::RealUGE => true, + RealPredicate::RealULT => true, + RealPredicate::RealULE => true, + RealPredicate::RealUNE => false, + RealPredicate::RealPredicateTrue => unreachable!(), + }; + + let cmp = self.context.new_comparison(self.location, op.to_gcc_comparison(), lhs, rhs); + + if must_handle_nan { + let is_nan = self.context.new_binary_op( + self.location, + BinaryOp::LogicalOr, + self.cx.bool_type, + // compare a value to itself to check whether it is NaN + self.context.new_comparison(self.location, ComparisonOp::NotEquals, lhs, lhs), + self.context.new_comparison(self.location, ComparisonOp::NotEquals, rhs, rhs), + ); + + self.context.new_binary_op( + self.location, + BinaryOp::LogicalOr, + self.cx.bool_type, + is_nan, + cmp, + ) + } else { + cmp + } } /* Miscellaneous instructions */ @@ -2419,7 +2471,6 @@ impl ToGccOrdering for AtomicOrdering { use MemOrdering::*; let ordering = match self { - AtomicOrdering::Unordered => __ATOMIC_RELAXED, AtomicOrdering::Relaxed => __ATOMIC_RELAXED, // TODO(antoyo): check if that's the same. AtomicOrdering::Acquire => __ATOMIC_ACQUIRE, AtomicOrdering::Release => __ATOMIC_RELEASE, @@ -2439,9 +2490,5 @@ fn get_maybe_pointer_size(value: RValue<'_>) -> u32 { #[cfg(not(feature = "master"))] fn get_maybe_pointer_size(value: RValue<'_>) -> u32 { let type_ = value.get_type(); - if type_.get_pointee().is_some() { - std::mem::size_of::<*const ()>() as _ - } else { - type_.get_size() - } + if type_.get_pointee().is_some() { size_of::<*const ()>() as _ } else { type_.get_size() } } diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs index c133ae4fcdd2a..c8130b7c01068 100644 --- a/compiler/rustc_codegen_gcc/src/callee.rs +++ b/compiler/rustc_codegen_gcc/src/callee.rs @@ -106,7 +106,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) // This is a monomorphization of a generic function. if !(cx.tcx.sess.opts.share_generics() || tcx.codegen_fn_attrs(instance_def_id).inline - == rustc_attr_parsing::InlineAttr::Never) + == rustc_attr_data_structures::InlineAttr::Never) { // When not sharing generics, all instances are in the same // crate and have hidden visibility. diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 20a3482aaa27f..918195364ffee 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -33,12 +33,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } pub fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> { - if value.get_type() == self.bool_type.make_pointer() { - if let Some(pointee) = typ.get_pointee() { - if pointee.dyncast_vector().is_some() { - panic!() - } - } + if value.get_type() == self.bool_type.make_pointer() + && let Some(pointee) = typ.get_pointee() + && pointee.dyncast_vector().is_some() + { + panic!() } // NOTE: since bitcast makes a value non-constant, don't bitcast if not necessary as some // SIMD builtins require a constant value. @@ -59,16 +58,11 @@ pub fn type_is_pointer(typ: Type<'_>) -> bool { typ.get_pointee().is_some() } -impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { +impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { fn const_null(&self, typ: Type<'gcc>) -> RValue<'gcc> { if type_is_pointer(typ) { self.context.new_null(typ) } else { self.const_int(typ, 0) } } - fn is_undef(&self, _val: RValue<'gcc>) -> bool { - // FIXME: actually check for undef - false - } - fn const_undef(&self, typ: Type<'gcc>) -> RValue<'gcc> { let local = self.current_func.borrow().expect("func").new_local(None, typ, "undefined"); if typ.is_struct().is_some() { @@ -146,13 +140,12 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } fn const_str(&self, s: &str) -> (RValue<'gcc>, RValue<'gcc>) { - let str_global = *self - .const_str_cache - .borrow_mut() - .raw_entry_mut() - .from_key(s) - .or_insert_with(|| (s.to_owned(), self.global_string(s))) - .1; + let mut const_str_cache = self.const_str_cache.borrow_mut(); + let str_global = const_str_cache.get(s).copied().unwrap_or_else(|| { + let g = self.global_string(s); + const_str_cache.insert(s.to_owned(), g); + g + }); let len = s.len(); let cs = self.const_ptrcast( str_global.get_address(None), @@ -263,7 +256,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } } - fn const_data_from_alloc(&self, alloc: ConstAllocation<'tcx>) -> Self::Value { + fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value { const_alloc_to_gcc(self, alloc) } diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index fb0ca31c54334..033afc0f8fbf4 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -131,7 +131,7 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { // will use load-unaligned instructions instead, and thus avoiding the crash. // // We could remove this hack whenever we decide to drop macOS 10.10 support. - if self.tcx.sess.target.options.is_like_osx { + if self.tcx.sess.target.options.is_like_darwin { // The `inspect` method is okay here because we checked for provenance, and // because we are doing this access to inspect the final interpreter state // (not as part of the interpreter execution). @@ -191,13 +191,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { // TODO(antoyo): check if it's okay that no link_section is set. let typ = self.val_ty(cv).get_aligned(align.bytes()); - let global = self.declare_private_global(&name[..], typ); - global + self.declare_private_global(&name[..], typ) } _ => { let typ = self.val_ty(cv).get_aligned(align.bytes()); - let global = self.declare_unnamed_global(typ); - global + self.declare_unnamed_global(typ) } }; global.global_set_initializer_rvalue(cv); @@ -242,10 +240,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let fn_attrs = self.tcx.codegen_fn_attrs(def_id); let global = if def_id.is_local() && !self.tcx.is_foreign_item(def_id) { - if let Some(global) = self.get_declared_value(sym) { - if self.val_ty(global) != self.type_ptr_to(gcc_type) { - span_bug!(self.tcx.def_span(def_id), "Conflicting types for static"); - } + if let Some(global) = self.get_declared_value(sym) + && self.val_ty(global) != self.type_ptr_to(gcc_type) + { + span_bug!(self.tcx.def_span(def_id), "Conflicting types for static"); } let is_tls = fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); @@ -302,9 +300,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } } -pub fn const_alloc_to_gcc<'gcc, 'tcx>( - cx: &CodegenCx<'gcc, 'tcx>, - alloc: ConstAllocation<'tcx>, +pub fn const_alloc_to_gcc<'gcc>( + cx: &CodegenCx<'gcc, '_>, + alloc: ConstAllocation<'_>, ) -> RValue<'gcc> { let alloc = alloc.inner(); let mut llvals = Vec::with_capacity(alloc.provenance().ptrs().len() + 1); @@ -364,6 +362,7 @@ pub fn const_alloc_to_gcc<'gcc, 'tcx>( llvals.push(cx.const_bytes(bytes)); } + // FIXME(bjorn3) avoid wrapping in a struct when there is only a single element. cx.const_struct(&llvals, true) } diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 1e1f577bb3a15..73718994e6417 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -23,6 +23,8 @@ use rustc_target::spec::{ HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, TlsModel, WasmCAbi, X86Abi, }; +#[cfg(feature = "master")] +use crate::abi::conv_to_fn_attribute; use crate::callee::get_fn; use crate::common::SignType; @@ -213,33 +215,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let bool_type = context.new_type::(); let mut functions = FxHashMap::default(); - let builtins = [ - "__builtin_unreachable", - "abort", - "__builtin_expect", /*"__builtin_expect_with_probability",*/ - "__builtin_constant_p", - "__builtin_add_overflow", - "__builtin_mul_overflow", - "__builtin_saddll_overflow", - /*"__builtin_sadd_overflow",*/ - "__builtin_smulll_overflow", /*"__builtin_smul_overflow",*/ - "__builtin_ssubll_overflow", - /*"__builtin_ssub_overflow",*/ "__builtin_sub_overflow", - "__builtin_uaddll_overflow", - "__builtin_uadd_overflow", - "__builtin_umulll_overflow", - "__builtin_umul_overflow", - "__builtin_usubll_overflow", - "__builtin_usub_overflow", - "__builtin_powif", - "__builtin_powi", - "fabsf", - "fabs", - "copysignf", - "copysign", - "nearbyintf", - "nearbyint", - ]; + let builtins = ["abort"]; for builtin in builtins.iter() { functions.insert(builtin.to_string(), context.get_builtin_function(builtin)); @@ -509,7 +485,11 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn declare_c_main(&self, fn_type: Self::Type) -> Option { let entry_name = self.sess().target.entry_name.as_ref(); if !self.functions.borrow().contains_key(entry_name) { - Some(self.declare_entry_fn(entry_name, fn_type, ())) + #[cfg(feature = "master")] + let conv = conv_to_fn_attribute(self.sess().target.entry_abi, &self.sess().target.arch); + #[cfg(not(feature = "master"))] + let conv = None; + Some(self.declare_entry_fn(entry_name, fn_type, conv)) } else { // If the symbol already exists, it is an error: for example, the user wrote // #[no_mangle] extern "C" fn main(..) {..} @@ -605,7 +585,10 @@ impl<'b, 'tcx> CodegenCx<'b, 'tcx> { let mut name = String::with_capacity(prefix.len() + 6); name.push_str(prefix); name.push('.'); - name.push_str(&(idx as u64).to_base(ALPHANUMERIC_ONLY)); + // Offset the index by the base so that always at least two characters + // are generated. This avoids cases where the suffix is interpreted as + // size by the assembler (for m68k: .b, .w, .l). + name.push_str(&(idx as u64 + ALPHANUMERIC_ONLY as u64).to_base(ALPHANUMERIC_ONLY)); name } } diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 55e01687400aa..e0597d0030d57 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -126,14 +126,15 @@ fn make_mir_scope<'gcc, 'tcx>( return; }; - if let Some(ref vars) = *variables { - if !vars.contains(scope) && scope_data.inlined.is_none() { - // Do not create a DIScope if there are no variables defined in this - // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. - debug_context.scopes[scope] = parent_scope; - instantiated.insert(scope); - return; - } + if let Some(ref vars) = *variables + && !vars.contains(scope) + && scope_data.inlined.is_none() + { + // Do not create a DIScope if there are no variables defined in this + // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. + debug_context.scopes[scope] = parent_scope; + instantiated.insert(scope); + return; } let loc = cx.lookup_debug_loc(scope_data.span.lo()); @@ -288,7 +289,7 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { ) -> Self::DILocation { let pos = span.lo(); let DebugLoc { file, line, col } = self.lookup_debug_loc(pos); - let loc = match file.name { + match file.name { rustc_span::FileName::Real(ref name) => match *name { rustc_span::RealFileName::LocalPath(ref name) => { if let Some(name) = name.to_str() { @@ -313,7 +314,6 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } }, _ => Location::null(), - }; - loc + } } } diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs index 7cdbe3c0c6290..bed82073e2c41 100644 --- a/compiler/rustc_codegen_gcc/src/declare.rs +++ b/compiler/rustc_codegen_gcc/src/declare.rs @@ -58,7 +58,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { variadic: bool, ) -> Function<'gcc> { self.linkage.set(FunctionType::Extern); - declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, params, variadic) + declare_raw_fn(self, name, None, return_type, params, variadic) } pub fn declare_global( @@ -92,7 +92,8 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { &self, name: &str, _fn_type: Type<'gcc>, - callconv: (), /*llvm::CCallConv*/ + #[cfg(feature = "master")] callconv: Option>, + #[cfg(not(feature = "master"))] callconv: Option<()>, ) -> RValue<'gcc> { // TODO(antoyo): use the fn_type parameter. let const_string = self.context.new_type::().make_pointer().make_pointer(); @@ -123,14 +124,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { #[cfg(feature = "master")] fn_attributes, } = fn_abi.gcc_type(self); - let func = declare_raw_fn( - self, - name, - (), /*fn_abi.llvm_cconv()*/ - return_type, - &arguments_type, - is_c_variadic, - ); + #[cfg(feature = "master")] + let conv = fn_abi.gcc_cconv(self); + #[cfg(not(feature = "master"))] + let conv = None; + let func = declare_raw_fn(self, name, conv, return_type, &arguments_type, is_c_variadic); self.on_stack_function_params.borrow_mut().insert(func, on_stack_param_indices); #[cfg(feature = "master")] for fn_attr in fn_attributes { @@ -159,10 +157,12 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { /// /// If there’s a value with the same name already declared, the function will /// update the declaration and return existing Value instead. +#[allow(clippy::let_and_return)] fn declare_raw_fn<'gcc>( cx: &CodegenCx<'gcc, '_>, name: &str, - _callconv: (), /*llvm::CallConv*/ + #[cfg(feature = "master")] callconv: Option>, + #[cfg(not(feature = "master"))] _callconv: Option<()>, return_type: Type<'gcc>, param_types: &[Type<'gcc>], variadic: bool, @@ -192,6 +192,10 @@ fn declare_raw_fn<'gcc>( let name = &mangle_name(name); let func = cx.context.new_function(None, cx.linkage.get(), return_type, ¶ms, name, variadic); + #[cfg(feature = "master")] + if let Some(attribute) = callconv { + func.add_attribute(attribute); + } cx.functions.borrow_mut().insert(name.to_string(), func); #[cfg(feature = "master")] diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 4e8c8aaaf5c8a..2b053abdd190a 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -48,14 +48,14 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec. all_rust_features.push((false, feature)); } else if !feature.is_empty() && diagnostics { @@ -136,14 +136,12 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> fn arch_to_gcc(name: &str) -> &str { match name { + "M68000" => "68000", "M68020" => "68020", _ => name, } diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index f3552d9b12fcb..9b5b0fde6e2f1 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -404,7 +404,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let ret_indirect = matches!(fn_abi.ret.mode, PassMode::Indirect { .. }); - let result = if ret_indirect { + let call = if ret_indirect { let res_value = self.current_func().new_local(self.location, res_type, "result_value"); let res_addr = res_value.get_address(self.location); let res_param_type = res_type.make_pointer(); @@ -432,8 +432,17 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { ); self.context.new_call(self.location, func, &[lhs, rhs, overflow_addr]) }; - - (result, self.context.new_cast(self.location, overflow_value, self.bool_type).to_rvalue()) + // NOTE: we must assign the result of the operation to a variable at this point to make + // sure it will be evaluated by libgccjit now. + // Otherwise, it will only be evaluated when the rvalue for the call is used somewhere else + // and overflow_value will not be initialized at the correct point in the program. + let result = self.current_func().new_local(self.location, res_type, "result"); + self.block.add_assignment(self.location, result, call); + + ( + result.to_rvalue(), + self.context.new_cast(self.location, overflow_value, self.bool_type).to_rvalue(), + ) } pub fn gcc_icmp( @@ -865,6 +874,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let value_type = value.get_type(); if self.is_native_int_type_or_bool(dest_typ) && self.is_native_int_type_or_bool(value_type) { + // TODO: use self.location. self.context.new_cast(None, value, dest_typ) } else if self.is_native_int_type_or_bool(dest_typ) { self.context.new_cast(None, self.low(value), dest_typ) @@ -905,6 +915,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let name_suffix = match self.type_kind(dest_typ) { TypeKind::Float => "tisf", TypeKind::Double => "tidf", + TypeKind::FP128 => "tixf", kind => panic!("cannot cast a non-native integer to type {:?}", kind), }; let sign = if signed { "" } else { "un" }; diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs index b8d1cde1d5dda..5ada535aa41d6 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs @@ -38,6 +38,7 @@ match name { "llvm.aarch64.gcsss" => "__builtin_arm_gcsss", "llvm.aarch64.isb" => "__builtin_arm_isb", "llvm.aarch64.prefetch" => "__builtin_arm_prefetch", + "llvm.aarch64.sme.in.streaming.mode" => "__builtin_arm_in_streaming_mode", "llvm.aarch64.sve.aesd" => "__builtin_sve_svaesd_u8", "llvm.aarch64.sve.aese" => "__builtin_sve_svaese_u8", "llvm.aarch64.sve.aesimc" => "__builtin_sve_svaesimc_u8", @@ -55,6 +56,8 @@ match name { "llvm.aarch64.ttest" => "__builtin_arm_ttest", // amdgcn "llvm.amdgcn.alignbyte" => "__builtin_amdgcn_alignbyte", + "llvm.amdgcn.ashr.pk.i8.i32" => "__builtin_amdgcn_ashr_pk_i8_i32", + "llvm.amdgcn.ashr.pk.u8.i32" => "__builtin_amdgcn_ashr_pk_u8_i32", "llvm.amdgcn.buffer.wbinvl1" => "__builtin_amdgcn_buffer_wbinvl1", "llvm.amdgcn.buffer.wbinvl1.sc" => "__builtin_amdgcn_buffer_wbinvl1_sc", "llvm.amdgcn.buffer.wbinvl1.vol" => "__builtin_amdgcn_buffer_wbinvl1_vol", @@ -64,6 +67,7 @@ match name { "llvm.amdgcn.cubetc" => "__builtin_amdgcn_cubetc", "llvm.amdgcn.cvt.f32.bf8" => "__builtin_amdgcn_cvt_f32_bf8", "llvm.amdgcn.cvt.f32.fp8" => "__builtin_amdgcn_cvt_f32_fp8", + "llvm.amdgcn.cvt.off.f32.i4" => "__builtin_amdgcn_cvt_off_f32_i4", "llvm.amdgcn.cvt.pk.bf8.f32" => "__builtin_amdgcn_cvt_pk_bf8_f32", "llvm.amdgcn.cvt.pk.f32.bf8" => "__builtin_amdgcn_cvt_pk_f32_bf8", "llvm.amdgcn.cvt.pk.f32.fp8" => "__builtin_amdgcn_cvt_pk_f32_fp8", @@ -74,7 +78,58 @@ match name { "llvm.amdgcn.cvt.pknorm.i16" => "__builtin_amdgcn_cvt_pknorm_i16", "llvm.amdgcn.cvt.pknorm.u16" => "__builtin_amdgcn_cvt_pknorm_u16", "llvm.amdgcn.cvt.pkrtz" => "__builtin_amdgcn_cvt_pkrtz", + "llvm.amdgcn.cvt.scalef32.2xpk16.bf6.f32" => "__builtin_amdgcn_cvt_scalef32_2xpk16_bf6_f32", + "llvm.amdgcn.cvt.scalef32.2xpk16.fp6.f32" => "__builtin_amdgcn_cvt_scalef32_2xpk16_fp6_f32", + "llvm.amdgcn.cvt.scalef32.f16.bf8" => "__builtin_amdgcn_cvt_scalef32_f16_bf8", + "llvm.amdgcn.cvt.scalef32.f16.fp8" => "__builtin_amdgcn_cvt_scalef32_f16_fp8", + "llvm.amdgcn.cvt.scalef32.f32.bf8" => "__builtin_amdgcn_cvt_scalef32_f32_bf8", + "llvm.amdgcn.cvt.scalef32.f32.fp8" => "__builtin_amdgcn_cvt_scalef32_f32_fp8", + "llvm.amdgcn.cvt.scalef32.pk.bf16.bf8" => "__builtin_amdgcn_cvt_scalef32_pk_bf16_bf8", + "llvm.amdgcn.cvt.scalef32.pk.bf16.fp4" => "__builtin_amdgcn_cvt_scalef32_pk_bf16_fp4", + "llvm.amdgcn.cvt.scalef32.pk.bf16.fp8" => "__builtin_amdgcn_cvt_scalef32_pk_bf16_fp8", + "llvm.amdgcn.cvt.scalef32.pk.bf8.bf16" => "__builtin_amdgcn_cvt_scalef32_pk_bf8_bf16", + "llvm.amdgcn.cvt.scalef32.pk.bf8.f16" => "__builtin_amdgcn_cvt_scalef32_pk_bf8_f16", + "llvm.amdgcn.cvt.scalef32.pk.bf8.f32" => "__builtin_amdgcn_cvt_scalef32_pk_bf8_f32", + "llvm.amdgcn.cvt.scalef32.pk.f16.bf8" => "__builtin_amdgcn_cvt_scalef32_pk_f16_bf8", + "llvm.amdgcn.cvt.scalef32.pk.f16.fp4" => "__builtin_amdgcn_cvt_scalef32_pk_f16_fp4", + "llvm.amdgcn.cvt.scalef32.pk.f16.fp8" => "__builtin_amdgcn_cvt_scalef32_pk_f16_fp8", + "llvm.amdgcn.cvt.scalef32.pk.f32.bf8" => "__builtin_amdgcn_cvt_scalef32_pk_f32_bf8", + "llvm.amdgcn.cvt.scalef32.pk.f32.fp4" => "__builtin_amdgcn_cvt_scalef32_pk_f32_fp4", + "llvm.amdgcn.cvt.scalef32.pk.f32.fp8" => "__builtin_amdgcn_cvt_scalef32_pk_f32_fp8", + "llvm.amdgcn.cvt.scalef32.pk.fp4.bf16" => "__builtin_amdgcn_cvt_scalef32_pk_fp4_bf16", + "llvm.amdgcn.cvt.scalef32.pk.fp4.f16" => "__builtin_amdgcn_cvt_scalef32_pk_fp4_f16", + "llvm.amdgcn.cvt.scalef32.pk.fp4.f32" => "__builtin_amdgcn_cvt_scalef32_pk_fp4_f32", + "llvm.amdgcn.cvt.scalef32.pk.fp8.bf16" => "__builtin_amdgcn_cvt_scalef32_pk_fp8_bf16", + "llvm.amdgcn.cvt.scalef32.pk.fp8.f16" => "__builtin_amdgcn_cvt_scalef32_pk_fp8_f16", + "llvm.amdgcn.cvt.scalef32.pk.fp8.f32" => "__builtin_amdgcn_cvt_scalef32_pk_fp8_f32", + "llvm.amdgcn.cvt.scalef32.pk32.bf16.bf6" => "__builtin_amdgcn_cvt_scalef32_pk32_bf16_bf6", + "llvm.amdgcn.cvt.scalef32.pk32.bf16.fp6" => "__builtin_amdgcn_cvt_scalef32_pk32_bf16_fp6", + "llvm.amdgcn.cvt.scalef32.pk32.bf6.bf16" => "__builtin_amdgcn_cvt_scalef32_pk32_bf6_bf16", + "llvm.amdgcn.cvt.scalef32.pk32.bf6.f16" => "__builtin_amdgcn_cvt_scalef32_pk32_bf6_f16", + "llvm.amdgcn.cvt.scalef32.pk32.f16.bf6" => "__builtin_amdgcn_cvt_scalef32_pk32_f16_bf6", + "llvm.amdgcn.cvt.scalef32.pk32.f16.fp6" => "__builtin_amdgcn_cvt_scalef32_pk32_f16_fp6", + "llvm.amdgcn.cvt.scalef32.pk32.f32.bf6" => "__builtin_amdgcn_cvt_scalef32_pk32_f32_bf6", + "llvm.amdgcn.cvt.scalef32.pk32.f32.fp6" => "__builtin_amdgcn_cvt_scalef32_pk32_f32_fp6", + "llvm.amdgcn.cvt.scalef32.pk32.fp6.bf16" => "__builtin_amdgcn_cvt_scalef32_pk32_fp6_bf16", + "llvm.amdgcn.cvt.scalef32.pk32.fp6.f16" => "__builtin_amdgcn_cvt_scalef32_pk32_fp6_f16", + "llvm.amdgcn.cvt.scalef32.sr.bf8.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_bf8_bf16", + "llvm.amdgcn.cvt.scalef32.sr.bf8.f16" => "__builtin_amdgcn_cvt_scalef32_sr_bf8_f16", + "llvm.amdgcn.cvt.scalef32.sr.bf8.f32" => "__builtin_amdgcn_cvt_scalef32_sr_bf8_f32", + "llvm.amdgcn.cvt.scalef32.sr.fp8.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_fp8_bf16", + "llvm.amdgcn.cvt.scalef32.sr.fp8.f16" => "__builtin_amdgcn_cvt_scalef32_sr_fp8_f16", + "llvm.amdgcn.cvt.scalef32.sr.fp8.f32" => "__builtin_amdgcn_cvt_scalef32_sr_fp8_f32", + "llvm.amdgcn.cvt.scalef32.sr.pk.fp4.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_pk_fp4_bf16", + "llvm.amdgcn.cvt.scalef32.sr.pk.fp4.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk_fp4_f16", + "llvm.amdgcn.cvt.scalef32.sr.pk.fp4.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk_fp4_f32", + "llvm.amdgcn.cvt.scalef32.sr.pk32.bf6.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_bf6_bf16", + "llvm.amdgcn.cvt.scalef32.sr.pk32.bf6.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_bf6_f16", + "llvm.amdgcn.cvt.scalef32.sr.pk32.bf6.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_bf6_f32", + "llvm.amdgcn.cvt.scalef32.sr.pk32.fp6.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_fp6_bf16", + "llvm.amdgcn.cvt.scalef32.sr.pk32.fp6.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_fp6_f16", + "llvm.amdgcn.cvt.scalef32.sr.pk32.fp6.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk32_fp6_f32", + "llvm.amdgcn.cvt.sr.bf16.f32" => "__builtin_amdgcn_cvt_sr_bf16_f32", "llvm.amdgcn.cvt.sr.bf8.f32" => "__builtin_amdgcn_cvt_sr_bf8_f32", + "llvm.amdgcn.cvt.sr.f16.f32" => "__builtin_amdgcn_cvt_sr_f16_f32", "llvm.amdgcn.cvt.sr.fp8.f32" => "__builtin_amdgcn_cvt_sr_fp8_f32", "llvm.amdgcn.dispatch.id" => "__builtin_amdgcn_dispatch_id", "llvm.amdgcn.dot4.f32.bf8.bf8" => "__builtin_amdgcn_dot4_f32_bf8_bf8", @@ -83,6 +138,7 @@ match name { "llvm.amdgcn.dot4.f32.fp8.fp8" => "__builtin_amdgcn_dot4_f32_fp8_fp8", "llvm.amdgcn.ds.add.gs.reg.rtn" => "__builtin_amdgcn_ds_add_gs_reg_rtn", "llvm.amdgcn.ds.bpermute" => "__builtin_amdgcn_ds_bpermute", + "llvm.amdgcn.ds.bpermute.fi.b32" => "__builtin_amdgcn_ds_bpermute_fi_b32", "llvm.amdgcn.ds.gws.barrier" => "__builtin_amdgcn_ds_gws_barrier", "llvm.amdgcn.ds.gws.init" => "__builtin_amdgcn_ds_gws_init", "llvm.amdgcn.ds.gws.sema.br" => "__builtin_amdgcn_ds_gws_sema_br", @@ -97,6 +153,7 @@ match name { "llvm.amdgcn.fdot2.bf16.bf16" => "__builtin_amdgcn_fdot2_bf16_bf16", "llvm.amdgcn.fdot2.f16.f16" => "__builtin_amdgcn_fdot2_f16_f16", "llvm.amdgcn.fdot2.f32.bf16" => "__builtin_amdgcn_fdot2_f32_bf16", + "llvm.amdgcn.fdot2c.f32.bf16" => "__builtin_amdgcn_fdot2c_f32_bf16", "llvm.amdgcn.fmul.legacy" => "__builtin_amdgcn_fmul_legacy", "llvm.amdgcn.global.load.lds" => "__builtin_amdgcn_global_load_lds", "llvm.amdgcn.groupstaticsize" => "__builtin_amdgcn_groupstaticsize", @@ -118,8 +175,10 @@ match name { "llvm.amdgcn.mfma.f32.16x16x16f16" => "__builtin_amdgcn_mfma_f32_16x16x16f16", "llvm.amdgcn.mfma.f32.16x16x1f32" => "__builtin_amdgcn_mfma_f32_16x16x1f32", "llvm.amdgcn.mfma.f32.16x16x2bf16" => "__builtin_amdgcn_mfma_f32_16x16x2bf16", + "llvm.amdgcn.mfma.f32.16x16x32.bf16" => "__builtin_amdgcn_mfma_f32_16x16x32_bf16", "llvm.amdgcn.mfma.f32.16x16x32.bf8.bf8" => "__builtin_amdgcn_mfma_f32_16x16x32_bf8_bf8", "llvm.amdgcn.mfma.f32.16x16x32.bf8.fp8" => "__builtin_amdgcn_mfma_f32_16x16x32_bf8_fp8", + "llvm.amdgcn.mfma.f32.16x16x32.f16" => "__builtin_amdgcn_mfma_f32_16x16x32_f16", "llvm.amdgcn.mfma.f32.16x16x32.fp8.bf8" => "__builtin_amdgcn_mfma_f32_16x16x32_fp8_bf8", "llvm.amdgcn.mfma.f32.16x16x32.fp8.fp8" => "__builtin_amdgcn_mfma_f32_16x16x32_fp8_fp8", "llvm.amdgcn.mfma.f32.16x16x4bf16.1k" => "__builtin_amdgcn_mfma_f32_16x16x4bf16_1k", @@ -127,8 +186,10 @@ match name { "llvm.amdgcn.mfma.f32.16x16x4f32" => "__builtin_amdgcn_mfma_f32_16x16x4f32", "llvm.amdgcn.mfma.f32.16x16x8.xf32" => "__builtin_amdgcn_mfma_f32_16x16x8_xf32", "llvm.amdgcn.mfma.f32.16x16x8bf16" => "__builtin_amdgcn_mfma_f32_16x16x8bf16", + "llvm.amdgcn.mfma.f32.32x32x16.bf16" => "__builtin_amdgcn_mfma_f32_32x32x16_bf16", "llvm.amdgcn.mfma.f32.32x32x16.bf8.bf8" => "__builtin_amdgcn_mfma_f32_32x32x16_bf8_bf8", "llvm.amdgcn.mfma.f32.32x32x16.bf8.fp8" => "__builtin_amdgcn_mfma_f32_32x32x16_bf8_fp8", + "llvm.amdgcn.mfma.f32.32x32x16.f16" => "__builtin_amdgcn_mfma_f32_32x32x16_f16", "llvm.amdgcn.mfma.f32.32x32x16.fp8.bf8" => "__builtin_amdgcn_mfma_f32_32x32x16_fp8_bf8", "llvm.amdgcn.mfma.f32.32x32x16.fp8.fp8" => "__builtin_amdgcn_mfma_f32_32x32x16_fp8_fp8", "llvm.amdgcn.mfma.f32.32x32x1f32" => "__builtin_amdgcn_mfma_f32_32x32x1f32", @@ -149,7 +210,9 @@ match name { "llvm.amdgcn.mfma.i32.16x16x16i8" => "__builtin_amdgcn_mfma_i32_16x16x16i8", "llvm.amdgcn.mfma.i32.16x16x32.i8" => "__builtin_amdgcn_mfma_i32_16x16x32_i8", "llvm.amdgcn.mfma.i32.16x16x4i8" => "__builtin_amdgcn_mfma_i32_16x16x4i8", + "llvm.amdgcn.mfma.i32.16x16x64.i8" => "__builtin_amdgcn_mfma_i32_16x16x64_i8", "llvm.amdgcn.mfma.i32.32x32x16.i8" => "__builtin_amdgcn_mfma_i32_32x32x16_i8", + "llvm.amdgcn.mfma.i32.32x32x32.i8" => "__builtin_amdgcn_mfma_i32_32x32x32_i8", "llvm.amdgcn.mfma.i32.32x32x4i8" => "__builtin_amdgcn_mfma_i32_32x32x4i8", "llvm.amdgcn.mfma.i32.32x32x8i8" => "__builtin_amdgcn_mfma_i32_32x32x8i8", "llvm.amdgcn.mfma.i32.4x4x4i8" => "__builtin_amdgcn_mfma_i32_4x4x4i8", @@ -159,25 +222,25 @@ match name { "llvm.amdgcn.perm" => "__builtin_amdgcn_perm", "llvm.amdgcn.permlane16.var" => "__builtin_amdgcn_permlane16_var", "llvm.amdgcn.permlanex16.var" => "__builtin_amdgcn_permlanex16_var", + "llvm.amdgcn.prng.b32" => "__builtin_amdgcn_prng_b32", "llvm.amdgcn.qsad.pk.u16.u8" => "__builtin_amdgcn_qsad_pk_u16_u8", "llvm.amdgcn.queue.ptr" => "__builtin_amdgcn_queue_ptr", + "llvm.amdgcn.raw.ptr.buffer.load.lds" => "__builtin_amdgcn_raw_ptr_buffer_load_lds", "llvm.amdgcn.rcp.legacy" => "__builtin_amdgcn_rcp_legacy", "llvm.amdgcn.rsq.legacy" => "__builtin_amdgcn_rsq_legacy", "llvm.amdgcn.s.barrier" => "__builtin_amdgcn_s_barrier", - "llvm.amdgcn.s.barrier.init" => "__builtin_amdgcn_s_barrier_init", - "llvm.amdgcn.s.barrier.join" => "__builtin_amdgcn_s_barrier_join", - "llvm.amdgcn.s.barrier.leave" => "__builtin_amdgcn_s_barrier_leave", "llvm.amdgcn.s.barrier.signal" => "__builtin_amdgcn_s_barrier_signal", "llvm.amdgcn.s.barrier.signal.isfirst" => "__builtin_amdgcn_s_barrier_signal_isfirst", - "llvm.amdgcn.s.barrier.signal.isfirst.var" => "__builtin_amdgcn_s_barrier_signal_isfirst_var", "llvm.amdgcn.s.barrier.signal.var" => "__builtin_amdgcn_s_barrier_signal_var", "llvm.amdgcn.s.barrier.wait" => "__builtin_amdgcn_s_barrier_wait", + "llvm.amdgcn.s.buffer.prefetch.data" => "__builtin_amdgcn_s_buffer_prefetch_data", "llvm.amdgcn.s.dcache.inv" => "__builtin_amdgcn_s_dcache_inv", "llvm.amdgcn.s.dcache.inv.vol" => "__builtin_amdgcn_s_dcache_inv_vol", "llvm.amdgcn.s.dcache.wb" => "__builtin_amdgcn_s_dcache_wb", "llvm.amdgcn.s.dcache.wb.vol" => "__builtin_amdgcn_s_dcache_wb_vol", "llvm.amdgcn.s.decperflevel" => "__builtin_amdgcn_s_decperflevel", "llvm.amdgcn.s.get.barrier.state" => "__builtin_amdgcn_s_get_barrier_state", + "llvm.amdgcn.s.get.named.barrier.state" => "__builtin_amdgcn_s_get_named_barrier_state", "llvm.amdgcn.s.get.waveid.in.workgroup" => "__builtin_amdgcn_s_get_waveid_in_workgroup", "llvm.amdgcn.s.getpc" => "__builtin_amdgcn_s_getpc", "llvm.amdgcn.s.getreg" => "__builtin_amdgcn_s_getreg", @@ -194,7 +257,6 @@ match name { "llvm.amdgcn.s.ttracedata.imm" => "__builtin_amdgcn_s_ttracedata_imm", "llvm.amdgcn.s.wait.event.export.ready" => "__builtin_amdgcn_s_wait_event_export_ready", "llvm.amdgcn.s.waitcnt" => "__builtin_amdgcn_s_waitcnt", - "llvm.amdgcn.s.wakeup.barrier" => "__builtin_amdgcn_s_wakeup_barrier", "llvm.amdgcn.sad.hi.u8" => "__builtin_amdgcn_sad_hi_u8", "llvm.amdgcn.sad.u16" => "__builtin_amdgcn_sad_u16", "llvm.amdgcn.sad.u8" => "__builtin_amdgcn_sad_u8", @@ -203,20 +265,34 @@ match name { "llvm.amdgcn.sdot2" => "__builtin_amdgcn_sdot2", "llvm.amdgcn.sdot4" => "__builtin_amdgcn_sdot4", "llvm.amdgcn.sdot8" => "__builtin_amdgcn_sdot8", + "llvm.amdgcn.smfmac.f32.16x16x128.bf8.bf8" => "__builtin_amdgcn_smfmac_f32_16x16x128_bf8_bf8", + "llvm.amdgcn.smfmac.f32.16x16x128.bf8.fp8" => "__builtin_amdgcn_smfmac_f32_16x16x128_bf8_fp8", + "llvm.amdgcn.smfmac.f32.16x16x128.fp8.bf8" => "__builtin_amdgcn_smfmac_f32_16x16x128_fp8_bf8", + "llvm.amdgcn.smfmac.f32.16x16x128.fp8.fp8" => "__builtin_amdgcn_smfmac_f32_16x16x128_fp8_fp8", "llvm.amdgcn.smfmac.f32.16x16x32.bf16" => "__builtin_amdgcn_smfmac_f32_16x16x32_bf16", "llvm.amdgcn.smfmac.f32.16x16x32.f16" => "__builtin_amdgcn_smfmac_f32_16x16x32_f16", + "llvm.amdgcn.smfmac.f32.16x16x64.bf16" => "__builtin_amdgcn_smfmac_f32_16x16x64_bf16", "llvm.amdgcn.smfmac.f32.16x16x64.bf8.bf8" => "__builtin_amdgcn_smfmac_f32_16x16x64_bf8_bf8", "llvm.amdgcn.smfmac.f32.16x16x64.bf8.fp8" => "__builtin_amdgcn_smfmac_f32_16x16x64_bf8_fp8", + "llvm.amdgcn.smfmac.f32.16x16x64.f16" => "__builtin_amdgcn_smfmac_f32_16x16x64_f16", "llvm.amdgcn.smfmac.f32.16x16x64.fp8.bf8" => "__builtin_amdgcn_smfmac_f32_16x16x64_fp8_bf8", "llvm.amdgcn.smfmac.f32.16x16x64.fp8.fp8" => "__builtin_amdgcn_smfmac_f32_16x16x64_fp8_fp8", "llvm.amdgcn.smfmac.f32.32x32x16.bf16" => "__builtin_amdgcn_smfmac_f32_32x32x16_bf16", "llvm.amdgcn.smfmac.f32.32x32x16.f16" => "__builtin_amdgcn_smfmac_f32_32x32x16_f16", + "llvm.amdgcn.smfmac.f32.32x32x32.bf16" => "__builtin_amdgcn_smfmac_f32_32x32x32_bf16", "llvm.amdgcn.smfmac.f32.32x32x32.bf8.bf8" => "__builtin_amdgcn_smfmac_f32_32x32x32_bf8_bf8", "llvm.amdgcn.smfmac.f32.32x32x32.bf8.fp8" => "__builtin_amdgcn_smfmac_f32_32x32x32_bf8_fp8", + "llvm.amdgcn.smfmac.f32.32x32x32.f16" => "__builtin_amdgcn_smfmac_f32_32x32x32_f16", "llvm.amdgcn.smfmac.f32.32x32x32.fp8.bf8" => "__builtin_amdgcn_smfmac_f32_32x32x32_fp8_bf8", "llvm.amdgcn.smfmac.f32.32x32x32.fp8.fp8" => "__builtin_amdgcn_smfmac_f32_32x32x32_fp8_fp8", + "llvm.amdgcn.smfmac.f32.32x32x64.bf8.bf8" => "__builtin_amdgcn_smfmac_f32_32x32x64_bf8_bf8", + "llvm.amdgcn.smfmac.f32.32x32x64.bf8.fp8" => "__builtin_amdgcn_smfmac_f32_32x32x64_bf8_fp8", + "llvm.amdgcn.smfmac.f32.32x32x64.fp8.bf8" => "__builtin_amdgcn_smfmac_f32_32x32x64_fp8_bf8", + "llvm.amdgcn.smfmac.f32.32x32x64.fp8.fp8" => "__builtin_amdgcn_smfmac_f32_32x32x64_fp8_fp8", + "llvm.amdgcn.smfmac.i32.16x16x128.i8" => "__builtin_amdgcn_smfmac_i32_16x16x128_i8", "llvm.amdgcn.smfmac.i32.16x16x64.i8" => "__builtin_amdgcn_smfmac_i32_16x16x64_i8", "llvm.amdgcn.smfmac.i32.32x32x32.i8" => "__builtin_amdgcn_smfmac_i32_32x32x32_i8", + "llvm.amdgcn.smfmac.i32.32x32x64.i8" => "__builtin_amdgcn_smfmac_i32_32x32x64_i8", "llvm.amdgcn.sudot4" => "__builtin_amdgcn_sudot4", "llvm.amdgcn.sudot8" => "__builtin_amdgcn_sudot8", "llvm.amdgcn.udot2" => "__builtin_amdgcn_udot2", @@ -227,6 +303,9 @@ match name { "llvm.amdgcn.workgroup.id.x" => "__builtin_amdgcn_workgroup_id_x", "llvm.amdgcn.workgroup.id.y" => "__builtin_amdgcn_workgroup_id_y", "llvm.amdgcn.workgroup.id.z" => "__builtin_amdgcn_workgroup_id_z", + "llvm.amdgcn.workitem.id.x" => "__builtin_amdgcn_workitem_id_x", + "llvm.amdgcn.workitem.id.y" => "__builtin_amdgcn_workitem_id_y", + "llvm.amdgcn.workitem.id.z" => "__builtin_amdgcn_workitem_id_z", // arm "llvm.arm.cdp" => "__builtin_arm_cdp", "llvm.arm.cdp2" => "__builtin_arm_cdp2", @@ -342,8 +421,6 @@ match name { "llvm.bpf.pseudo" => "__builtin_bpf_pseudo", // cuda "llvm.cuda.syncthreads" => "__syncthreads", - // dx - "llvm.dx.create.handle" => "__builtin_hlsl_create_handle", // hexagon "llvm.hexagon.A2.abs" => "__builtin_HEXAGON_A2_abs", "llvm.hexagon.A2.absp" => "__builtin_HEXAGON_A2_absp", @@ -1255,6 +1332,10 @@ match name { "llvm.hexagon.SI.to.SXTHI.asrh" => "__builtin_SI_to_SXTHI_asrh", "llvm.hexagon.V6.extractw" => "__builtin_HEXAGON_V6_extractw", "llvm.hexagon.V6.extractw.128B" => "__builtin_HEXAGON_V6_extractw_128B", + "llvm.hexagon.V6.get.qfext" => "__builtin_HEXAGON_V6_get_qfext", + "llvm.hexagon.V6.get.qfext.128B" => "__builtin_HEXAGON_V6_get_qfext_128B", + "llvm.hexagon.V6.get.qfext.oracc" => "__builtin_HEXAGON_V6_get_qfext_oracc", + "llvm.hexagon.V6.get.qfext.oracc.128B" => "__builtin_HEXAGON_V6_get_qfext_oracc_128B", "llvm.hexagon.V6.hi" => "__builtin_HEXAGON_V6_hi", "llvm.hexagon.V6.hi.128B" => "__builtin_HEXAGON_V6_hi_128B", "llvm.hexagon.V6.lo" => "__builtin_HEXAGON_V6_lo", @@ -1281,6 +1362,8 @@ match name { "llvm.hexagon.V6.pred.scalar2v2.128B" => "__builtin_HEXAGON_V6_pred_scalar2v2_128B", "llvm.hexagon.V6.pred.xor" => "__builtin_HEXAGON_V6_pred_xor", "llvm.hexagon.V6.pred.xor.128B" => "__builtin_HEXAGON_V6_pred_xor_128B", + "llvm.hexagon.V6.set.qfext" => "__builtin_HEXAGON_V6_set_qfext", + "llvm.hexagon.V6.set.qfext.128B" => "__builtin_HEXAGON_V6_set_qfext_128B", "llvm.hexagon.V6.shuffeqh" => "__builtin_HEXAGON_V6_shuffeqh", "llvm.hexagon.V6.shuffeqh.128B" => "__builtin_HEXAGON_V6_shuffeqh_128B", "llvm.hexagon.V6.shuffeqw" => "__builtin_HEXAGON_V6_shuffeqw", @@ -1301,6 +1384,8 @@ match name { "llvm.hexagon.V6.vS32b.nt.qpred.ai.128B" => "__builtin_HEXAGON_V6_vS32b_nt_qpred_ai_128B", "llvm.hexagon.V6.vS32b.qpred.ai" => "__builtin_HEXAGON_V6_vS32b_qpred_ai", "llvm.hexagon.V6.vS32b.qpred.ai.128B" => "__builtin_HEXAGON_V6_vS32b_qpred_ai_128B", + "llvm.hexagon.V6.vabs.f8" => "__builtin_HEXAGON_V6_vabs_f8", + "llvm.hexagon.V6.vabs.f8.128B" => "__builtin_HEXAGON_V6_vabs_f8_128B", "llvm.hexagon.V6.vabs.hf" => "__builtin_HEXAGON_V6_vabs_hf", "llvm.hexagon.V6.vabs.hf.128B" => "__builtin_HEXAGON_V6_vabs_hf_128B", "llvm.hexagon.V6.vabs.sf" => "__builtin_HEXAGON_V6_vabs_sf", @@ -1327,6 +1412,8 @@ match name { "llvm.hexagon.V6.vabsw.sat.128B" => "__builtin_HEXAGON_V6_vabsw_sat_128B", "llvm.hexagon.V6.vadd.hf" => "__builtin_HEXAGON_V6_vadd_hf", "llvm.hexagon.V6.vadd.hf.128B" => "__builtin_HEXAGON_V6_vadd_hf_128B", + "llvm.hexagon.V6.vadd.hf.f8" => "__builtin_HEXAGON_V6_vadd_hf_f8", + "llvm.hexagon.V6.vadd.hf.f8.128B" => "__builtin_HEXAGON_V6_vadd_hf_f8_128B", "llvm.hexagon.V6.vadd.hf.hf" => "__builtin_HEXAGON_V6_vadd_hf_hf", "llvm.hexagon.V6.vadd.hf.hf.128B" => "__builtin_HEXAGON_V6_vadd_hf_hf_128B", "llvm.hexagon.V6.vadd.qf16" => "__builtin_HEXAGON_V6_vadd_qf16", @@ -1549,10 +1636,14 @@ match name { "llvm.hexagon.V6.vcvt.b.hf.128B" => "__builtin_HEXAGON_V6_vcvt_b_hf_128B", "llvm.hexagon.V6.vcvt.bf.sf" => "__builtin_HEXAGON_V6_vcvt_bf_sf", "llvm.hexagon.V6.vcvt.bf.sf.128B" => "__builtin_HEXAGON_V6_vcvt_bf_sf_128B", + "llvm.hexagon.V6.vcvt.f8.hf" => "__builtin_HEXAGON_V6_vcvt_f8_hf", + "llvm.hexagon.V6.vcvt.f8.hf.128B" => "__builtin_HEXAGON_V6_vcvt_f8_hf_128B", "llvm.hexagon.V6.vcvt.h.hf" => "__builtin_HEXAGON_V6_vcvt_h_hf", "llvm.hexagon.V6.vcvt.h.hf.128B" => "__builtin_HEXAGON_V6_vcvt_h_hf_128B", "llvm.hexagon.V6.vcvt.hf.b" => "__builtin_HEXAGON_V6_vcvt_hf_b", "llvm.hexagon.V6.vcvt.hf.b.128B" => "__builtin_HEXAGON_V6_vcvt_hf_b_128B", + "llvm.hexagon.V6.vcvt.hf.f8" => "__builtin_HEXAGON_V6_vcvt_hf_f8", + "llvm.hexagon.V6.vcvt.hf.f8.128B" => "__builtin_HEXAGON_V6_vcvt_hf_f8_128B", "llvm.hexagon.V6.vcvt.hf.h" => "__builtin_HEXAGON_V6_vcvt_hf_h", "llvm.hexagon.V6.vcvt.hf.h.128B" => "__builtin_HEXAGON_V6_vcvt_hf_h_128B", "llvm.hexagon.V6.vcvt.hf.sf" => "__builtin_HEXAGON_V6_vcvt_hf_sf", @@ -1567,6 +1658,14 @@ match name { "llvm.hexagon.V6.vcvt.ub.hf.128B" => "__builtin_HEXAGON_V6_vcvt_ub_hf_128B", "llvm.hexagon.V6.vcvt.uh.hf" => "__builtin_HEXAGON_V6_vcvt_uh_hf", "llvm.hexagon.V6.vcvt.uh.hf.128B" => "__builtin_HEXAGON_V6_vcvt_uh_hf_128B", + "llvm.hexagon.V6.vcvt2.b.hf" => "__builtin_HEXAGON_V6_vcvt2_b_hf", + "llvm.hexagon.V6.vcvt2.b.hf.128B" => "__builtin_HEXAGON_V6_vcvt2_b_hf_128B", + "llvm.hexagon.V6.vcvt2.hf.b" => "__builtin_HEXAGON_V6_vcvt2_hf_b", + "llvm.hexagon.V6.vcvt2.hf.b.128B" => "__builtin_HEXAGON_V6_vcvt2_hf_b_128B", + "llvm.hexagon.V6.vcvt2.hf.ub" => "__builtin_HEXAGON_V6_vcvt2_hf_ub", + "llvm.hexagon.V6.vcvt2.hf.ub.128B" => "__builtin_HEXAGON_V6_vcvt2_hf_ub_128B", + "llvm.hexagon.V6.vcvt2.ub.hf" => "__builtin_HEXAGON_V6_vcvt2_ub_hf", + "llvm.hexagon.V6.vcvt2.ub.hf.128B" => "__builtin_HEXAGON_V6_vcvt2_ub_hf_128B", "llvm.hexagon.V6.vd0" => "__builtin_HEXAGON_V6_vd0", "llvm.hexagon.V6.vd0.128B" => "__builtin_HEXAGON_V6_vd0_128B", "llvm.hexagon.V6.vdd0" => "__builtin_HEXAGON_V6_vdd0", @@ -1649,14 +1748,20 @@ match name { "llvm.hexagon.V6.veqw.or.128B" => "__builtin_HEXAGON_V6_veqw_or_128B", "llvm.hexagon.V6.veqw.xor" => "__builtin_HEXAGON_V6_veqw_xor", "llvm.hexagon.V6.veqw.xor.128B" => "__builtin_HEXAGON_V6_veqw_xor_128B", + "llvm.hexagon.V6.vfmax.f8" => "__builtin_HEXAGON_V6_vfmax_f8", + "llvm.hexagon.V6.vfmax.f8.128B" => "__builtin_HEXAGON_V6_vfmax_f8_128B", "llvm.hexagon.V6.vfmax.hf" => "__builtin_HEXAGON_V6_vfmax_hf", "llvm.hexagon.V6.vfmax.hf.128B" => "__builtin_HEXAGON_V6_vfmax_hf_128B", "llvm.hexagon.V6.vfmax.sf" => "__builtin_HEXAGON_V6_vfmax_sf", "llvm.hexagon.V6.vfmax.sf.128B" => "__builtin_HEXAGON_V6_vfmax_sf_128B", + "llvm.hexagon.V6.vfmin.f8" => "__builtin_HEXAGON_V6_vfmin_f8", + "llvm.hexagon.V6.vfmin.f8.128B" => "__builtin_HEXAGON_V6_vfmin_f8_128B", "llvm.hexagon.V6.vfmin.hf" => "__builtin_HEXAGON_V6_vfmin_hf", "llvm.hexagon.V6.vfmin.hf.128B" => "__builtin_HEXAGON_V6_vfmin_hf_128B", "llvm.hexagon.V6.vfmin.sf" => "__builtin_HEXAGON_V6_vfmin_sf", "llvm.hexagon.V6.vfmin.sf.128B" => "__builtin_HEXAGON_V6_vfmin_sf_128B", + "llvm.hexagon.V6.vfneg.f8" => "__builtin_HEXAGON_V6_vfneg_f8", + "llvm.hexagon.V6.vfneg.f8.128B" => "__builtin_HEXAGON_V6_vfneg_f8_128B", "llvm.hexagon.V6.vfneg.hf" => "__builtin_HEXAGON_V6_vfneg_hf", "llvm.hexagon.V6.vfneg.hf.128B" => "__builtin_HEXAGON_V6_vfneg_hf_128B", "llvm.hexagon.V6.vfneg.sf" => "__builtin_HEXAGON_V6_vfneg_sf", @@ -1807,6 +1912,8 @@ match name { "llvm.hexagon.V6.vmaxuh.128B" => "__builtin_HEXAGON_V6_vmaxuh_128B", "llvm.hexagon.V6.vmaxw" => "__builtin_HEXAGON_V6_vmaxw", "llvm.hexagon.V6.vmaxw.128B" => "__builtin_HEXAGON_V6_vmaxw_128B", + "llvm.hexagon.V6.vmerge.qf" => "__builtin_HEXAGON_V6_vmerge_qf", + "llvm.hexagon.V6.vmerge.qf.128B" => "__builtin_HEXAGON_V6_vmerge_qf_128B", "llvm.hexagon.V6.vmin.bf" => "__builtin_HEXAGON_V6_vmin_bf", "llvm.hexagon.V6.vmin.bf.128B" => "__builtin_HEXAGON_V6_vmin_bf_128B", "llvm.hexagon.V6.vmin.hf" => "__builtin_HEXAGON_V6_vmin_hf", @@ -1849,6 +1956,10 @@ match name { "llvm.hexagon.V6.vmpauhuhsat.128B" => "__builtin_HEXAGON_V6_vmpauhuhsat_128B", "llvm.hexagon.V6.vmpsuhuhsat" => "__builtin_HEXAGON_V6_vmpsuhuhsat", "llvm.hexagon.V6.vmpsuhuhsat.128B" => "__builtin_HEXAGON_V6_vmpsuhuhsat_128B", + "llvm.hexagon.V6.vmpy.hf.f8" => "__builtin_HEXAGON_V6_vmpy_hf_f8", + "llvm.hexagon.V6.vmpy.hf.f8.128B" => "__builtin_HEXAGON_V6_vmpy_hf_f8_128B", + "llvm.hexagon.V6.vmpy.hf.f8.acc" => "__builtin_HEXAGON_V6_vmpy_hf_f8_acc", + "llvm.hexagon.V6.vmpy.hf.f8.acc.128B" => "__builtin_HEXAGON_V6_vmpy_hf_f8_acc_128B", "llvm.hexagon.V6.vmpy.hf.hf" => "__builtin_HEXAGON_V6_vmpy_hf_hf", "llvm.hexagon.V6.vmpy.hf.hf.128B" => "__builtin_HEXAGON_V6_vmpy_hf_hf_128B", "llvm.hexagon.V6.vmpy.hf.hf.acc" => "__builtin_HEXAGON_V6_vmpy_hf_hf_acc", @@ -1869,6 +1980,12 @@ match name { "llvm.hexagon.V6.vmpy.qf32.qf16.128B" => "__builtin_HEXAGON_V6_vmpy_qf32_qf16_128B", "llvm.hexagon.V6.vmpy.qf32.sf" => "__builtin_HEXAGON_V6_vmpy_qf32_sf", "llvm.hexagon.V6.vmpy.qf32.sf.128B" => "__builtin_HEXAGON_V6_vmpy_qf32_sf_128B", + "llvm.hexagon.V6.vmpy.rt.hf" => "__builtin_HEXAGON_V6_vmpy_rt_hf", + "llvm.hexagon.V6.vmpy.rt.hf.128B" => "__builtin_HEXAGON_V6_vmpy_rt_hf_128B", + "llvm.hexagon.V6.vmpy.rt.qf16" => "__builtin_HEXAGON_V6_vmpy_rt_qf16", + "llvm.hexagon.V6.vmpy.rt.qf16.128B" => "__builtin_HEXAGON_V6_vmpy_rt_qf16_128B", + "llvm.hexagon.V6.vmpy.rt.sf" => "__builtin_HEXAGON_V6_vmpy_rt_sf", + "llvm.hexagon.V6.vmpy.rt.sf.128B" => "__builtin_HEXAGON_V6_vmpy_rt_sf_128B", "llvm.hexagon.V6.vmpy.sf.bf" => "__builtin_HEXAGON_V6_vmpy_sf_bf", "llvm.hexagon.V6.vmpy.sf.bf.128B" => "__builtin_HEXAGON_V6_vmpy_sf_bf_128B", "llvm.hexagon.V6.vmpy.sf.bf.acc" => "__builtin_HEXAGON_V6_vmpy_sf_bf_acc", @@ -2127,6 +2244,8 @@ match name { "llvm.hexagon.V6.vshufoh.128B" => "__builtin_HEXAGON_V6_vshufoh_128B", "llvm.hexagon.V6.vsub.hf" => "__builtin_HEXAGON_V6_vsub_hf", "llvm.hexagon.V6.vsub.hf.128B" => "__builtin_HEXAGON_V6_vsub_hf_128B", + "llvm.hexagon.V6.vsub.hf.f8" => "__builtin_HEXAGON_V6_vsub_hf_f8", + "llvm.hexagon.V6.vsub.hf.f8.128B" => "__builtin_HEXAGON_V6_vsub_hf_f8_128B", "llvm.hexagon.V6.vsub.hf.hf" => "__builtin_HEXAGON_V6_vsub_hf_hf", "llvm.hexagon.V6.vsub.hf.hf.128B" => "__builtin_HEXAGON_V6_vsub_hf_hf_128B", "llvm.hexagon.V6.vsub.qf16" => "__builtin_HEXAGON_V6_vsub_qf16", @@ -4445,8 +4564,6 @@ match name { "llvm.mips.xor.v" => "__builtin_msa_xor_v", "llvm.mips.xori.b" => "__builtin_msa_xori_b", // nvvm - "llvm.nvvm.abs.bf16" => "__nvvm_abs_bf16", - "llvm.nvvm.abs.bf16x2" => "__nvvm_abs_bf16x2", "llvm.nvvm.abs.i" => "__nvvm_abs_i", "llvm.nvvm.abs.ll" => "__nvvm_abs_ll", "llvm.nvvm.activemask" => "__nvvm_activemask", @@ -4473,6 +4590,10 @@ match name { "llvm.nvvm.barrier0.and" => "__nvvm_bar0_and", "llvm.nvvm.barrier0.or" => "__nvvm_bar0_or", "llvm.nvvm.barrier0.popc" => "__nvvm_bar0_popc", + "llvm.nvvm.bf16x2.to.ue8m0x2.rp" => "__nvvm_bf16x2_to_ue8m0x2_rp", + "llvm.nvvm.bf16x2.to.ue8m0x2.rp.satfinite" => "__nvvm_bf16x2_to_ue8m0x2_rp_satfinite", + "llvm.nvvm.bf16x2.to.ue8m0x2.rz" => "__nvvm_bf16x2_to_ue8m0x2_rz", + "llvm.nvvm.bf16x2.to.ue8m0x2.rz.satfinite" => "__nvvm_bf16x2_to_ue8m0x2_rz_satfinite", "llvm.nvvm.bf2h.rn" => "__nvvm_bf2h_rn", "llvm.nvvm.bf2h.rn.ftz" => "__nvvm_bf2h_rn_ftz", "llvm.nvvm.bitcast.d2ll" => "__nvvm_bitcast_d2ll", @@ -4523,6 +4644,8 @@ match name { "llvm.nvvm.d2ull.rz" => "__nvvm_d2ull_rz", "llvm.nvvm.div.approx.f" => "__nvvm_div_approx_f", "llvm.nvvm.div.approx.ftz.f" => "__nvvm_div_approx_ftz_f", + "llvm.nvvm.div.full" => "__nvvm_div_full", + "llvm.nvvm.div.full.ftz" => "__nvvm_div_full_ftz", "llvm.nvvm.div.rm.d" => "__nvvm_div_rm_d", "llvm.nvvm.div.rm.f" => "__nvvm_div_rm_f", "llvm.nvvm.div.rm.ftz.f" => "__nvvm_div_rm_ftz_f", @@ -4535,6 +4658,10 @@ match name { "llvm.nvvm.div.rz.d" => "__nvvm_div_rz_d", "llvm.nvvm.div.rz.f" => "__nvvm_div_rz_f", "llvm.nvvm.div.rz.ftz.f" => "__nvvm_div_rz_ftz_f", + "llvm.nvvm.e2m3x2.to.f16x2.rn" => "__nvvm_e2m3x2_to_f16x2_rn", + "llvm.nvvm.e2m3x2.to.f16x2.rn.relu" => "__nvvm_e2m3x2_to_f16x2_rn_relu", + "llvm.nvvm.e3m2x2.to.f16x2.rn" => "__nvvm_e3m2x2_to_f16x2_rn", + "llvm.nvvm.e3m2x2.to.f16x2.rn.relu" => "__nvvm_e3m2x2_to_f16x2_rn_relu", "llvm.nvvm.e4m3x2.to.f16x2.rn" => "__nvvm_e4m3x2_to_f16x2_rn", "llvm.nvvm.e4m3x2.to.f16x2.rn.relu" => "__nvvm_e4m3x2_to_f16x2_rn_relu", "llvm.nvvm.e5m2x2.to.f16x2.rn" => "__nvvm_e5m2x2_to_f16x2_rn", @@ -4569,7 +4696,16 @@ match name { "llvm.nvvm.f2ll.rp.ftz" => "__nvvm_f2ll_rp_ftz", "llvm.nvvm.f2ll.rz" => "__nvvm_f2ll_rz", "llvm.nvvm.f2ll.rz.ftz" => "__nvvm_f2ll_rz_ftz", + "llvm.nvvm.f2tf32.rn" => "__nvvm_f2tf32_rn", + "llvm.nvvm.f2tf32.rn.relu" => "__nvvm_f2tf32_rn_relu", + "llvm.nvvm.f2tf32.rn.relu.satfinite" => "__nvvm_f2tf32_rn_relu_satfinite", + "llvm.nvvm.f2tf32.rn.satfinite" => "__nvvm_f2tf32_rn_satfinite", "llvm.nvvm.f2tf32.rna" => "__nvvm_f2tf32_rna", + "llvm.nvvm.f2tf32.rna.satfinite" => "__nvvm_f2tf32_rna_satfinite", + "llvm.nvvm.f2tf32.rz" => "__nvvm_f2tf32_rz", + "llvm.nvvm.f2tf32.rz.relu" => "__nvvm_f2tf32_rz_relu", + "llvm.nvvm.f2tf32.rz.relu.satfinite" => "__nvvm_f2tf32_rz_relu_satfinite", + "llvm.nvvm.f2tf32.rz.satfinite" => "__nvvm_f2tf32_rz_satfinite", "llvm.nvvm.f2ui.rm" => "__nvvm_f2ui_rm", "llvm.nvvm.f2ui.rm.ftz" => "__nvvm_f2ui_rm_ftz", "llvm.nvvm.f2ui.rn" => "__nvvm_f2ui_rn", @@ -4589,10 +4725,18 @@ match name { "llvm.nvvm.fabs.d" => "__nvvm_fabs_d", "llvm.nvvm.fabs.f" => "__nvvm_fabs_f", "llvm.nvvm.fabs.ftz.f" => "__nvvm_fabs_ftz_f", + "llvm.nvvm.ff.to.e2m3x2.rn.relu.satfinite" => "__nvvm_ff_to_e2m3x2_rn_relu_satfinite", + "llvm.nvvm.ff.to.e2m3x2.rn.satfinite" => "__nvvm_ff_to_e2m3x2_rn_satfinite", + "llvm.nvvm.ff.to.e3m2x2.rn.relu.satfinite" => "__nvvm_ff_to_e3m2x2_rn_relu_satfinite", + "llvm.nvvm.ff.to.e3m2x2.rn.satfinite" => "__nvvm_ff_to_e3m2x2_rn_satfinite", "llvm.nvvm.ff.to.e4m3x2.rn" => "__nvvm_ff_to_e4m3x2_rn", "llvm.nvvm.ff.to.e4m3x2.rn.relu" => "__nvvm_ff_to_e4m3x2_rn_relu", "llvm.nvvm.ff.to.e5m2x2.rn" => "__nvvm_ff_to_e5m2x2_rn", "llvm.nvvm.ff.to.e5m2x2.rn.relu" => "__nvvm_ff_to_e5m2x2_rn_relu", + "llvm.nvvm.ff.to.ue8m0x2.rp" => "__nvvm_ff_to_ue8m0x2_rp", + "llvm.nvvm.ff.to.ue8m0x2.rp.satfinite" => "__nvvm_ff_to_ue8m0x2_rp_satfinite", + "llvm.nvvm.ff.to.ue8m0x2.rz" => "__nvvm_ff_to_ue8m0x2_rz", + "llvm.nvvm.ff.to.ue8m0x2.rz.satfinite" => "__nvvm_ff_to_ue8m0x2_rz_satfinite", "llvm.nvvm.ff2bf16x2.rn" => "__nvvm_ff2bf16x2_rn", "llvm.nvvm.ff2bf16x2.rn.relu" => "__nvvm_ff2bf16x2_rn_relu", "llvm.nvvm.ff2bf16x2.rz" => "__nvvm_ff2bf16x2_rz", @@ -4862,6 +5006,14 @@ match name { // [DUPLICATE]: "llvm.nvvm.read.ptx.sreg.warpsize" => "__nvvm_read_ptx_sreg_", "llvm.nvvm.redux.sync.add" => "__nvvm_redux_sync_add", "llvm.nvvm.redux.sync.and" => "__nvvm_redux_sync_and", + "llvm.nvvm.redux.sync.fmax" => "__nvvm_redux_sync_fmax", + "llvm.nvvm.redux.sync.fmax.NaN" => "__nvvm_redux_sync_fmax_NaN", + "llvm.nvvm.redux.sync.fmax.abs" => "__nvvm_redux_sync_fmax_abs", + "llvm.nvvm.redux.sync.fmax.abs.NaN" => "__nvvm_redux_sync_fmax_abs_NaN", + "llvm.nvvm.redux.sync.fmin" => "__nvvm_redux_sync_fmin", + "llvm.nvvm.redux.sync.fmin.NaN" => "__nvvm_redux_sync_fmin_NaN", + "llvm.nvvm.redux.sync.fmin.abs" => "__nvvm_redux_sync_fmin_abs", + "llvm.nvvm.redux.sync.fmin.abs.NaN" => "__nvvm_redux_sync_fmin_abs_NaN", "llvm.nvvm.redux.sync.max" => "__nvvm_redux_sync_max", "llvm.nvvm.redux.sync.min" => "__nvvm_redux_sync_min", "llvm.nvvm.redux.sync.or" => "__nvvm_redux_sync_or", @@ -5149,6 +5301,7 @@ match name { "llvm.nvvm.txq.num.mipmap.levels" => "__nvvm_txq_num_mipmap_levels", "llvm.nvvm.txq.num.samples" => "__nvvm_txq_num_samples", "llvm.nvvm.txq.width" => "__nvvm_txq_width", + "llvm.nvvm.ue8m0x2.to.bf16x2" => "__nvvm_ue8m0x2_to_bf16x2", "llvm.nvvm.ui2d.rm" => "__nvvm_ui2d_rm", "llvm.nvvm.ui2d.rn" => "__nvvm_ui2d_rn", "llvm.nvvm.ui2d.rp" => "__nvvm_ui2d_rp", @@ -5783,6 +5936,9 @@ match name { "llvm.r600.read.tgid.x" => "__builtin_r600_read_tgid_x", "llvm.r600.read.tgid.y" => "__builtin_r600_read_tgid_y", "llvm.r600.read.tgid.z" => "__builtin_r600_read_tgid_z", + "llvm.r600.read.tidig.x" => "__builtin_r600_read_tidig_x", + "llvm.r600.read.tidig.y" => "__builtin_r600_read_tidig_y", + "llvm.r600.read.tidig.z" => "__builtin_r600_read_tidig_z", // riscv "llvm.riscv.aes32dsi" => "__builtin_riscv_aes32dsi", "llvm.riscv.aes32dsmi" => "__builtin_riscv_aes32dsmi", @@ -5806,6 +5962,8 @@ match name { "llvm.riscv.sha512sum1" => "__builtin_riscv_sha512sum1", "llvm.riscv.sha512sum1r" => "__builtin_riscv_sha512sum1r", // s390 + "llvm.s390.bdepg" => "__builtin_s390_bdepg", + "llvm.s390.bextg" => "__builtin_s390_bextg", "llvm.s390.efpc" => "__builtin_s390_efpc", "llvm.s390.etnd" => "__builtin_tx_nesting_depth", "llvm.s390.lcbb" => "__builtin_s390_lcbb", @@ -5828,6 +5986,8 @@ match name { "llvm.s390.vavglf" => "__builtin_s390_vavglf", "llvm.s390.vavglg" => "__builtin_s390_vavglg", "llvm.s390.vavglh" => "__builtin_s390_vavglh", + "llvm.s390.vavglq" => "__builtin_s390_vavglq", + "llvm.s390.vavgq" => "__builtin_s390_vavgq", "llvm.s390.vbperm" => "__builtin_s390_vbperm", "llvm.s390.vcfn" => "__builtin_s390_vcfn", "llvm.s390.vcksm" => "__builtin_s390_vcksm", @@ -5839,6 +5999,7 @@ match name { "llvm.s390.verimf" => "__builtin_s390_verimf", "llvm.s390.verimg" => "__builtin_s390_verimg", "llvm.s390.verimh" => "__builtin_s390_verimh", + "llvm.s390.veval" => "__builtin_s390_veval", "llvm.s390.vfaeb" => "__builtin_s390_vfaeb", "llvm.s390.vfaef" => "__builtin_s390_vfaef", "llvm.s390.vfaeh" => "__builtin_s390_vfaeh", @@ -5857,6 +6018,11 @@ match name { "llvm.s390.vfenezb" => "__builtin_s390_vfenezb", "llvm.s390.vfenezf" => "__builtin_s390_vfenezf", "llvm.s390.vfenezh" => "__builtin_s390_vfenezh", + "llvm.s390.vgemb" => "__builtin_s390_vgemb", + "llvm.s390.vgemf" => "__builtin_s390_vgemf", + "llvm.s390.vgemg" => "__builtin_s390_vgemg", + "llvm.s390.vgemh" => "__builtin_s390_vgemh", + "llvm.s390.vgemq" => "__builtin_s390_vgemq", "llvm.s390.vgfmab" => "__builtin_s390_vgfmab", "llvm.s390.vgfmaf" => "__builtin_s390_vgfmaf", "llvm.s390.vgfmag" => "__builtin_s390_vgfmag", @@ -5873,39 +6039,55 @@ match name { "llvm.s390.vlrl" => "__builtin_s390_vlrlr", "llvm.s390.vmaeb" => "__builtin_s390_vmaeb", "llvm.s390.vmaef" => "__builtin_s390_vmaef", + "llvm.s390.vmaeg" => "__builtin_s390_vmaeg", "llvm.s390.vmaeh" => "__builtin_s390_vmaeh", "llvm.s390.vmahb" => "__builtin_s390_vmahb", "llvm.s390.vmahf" => "__builtin_s390_vmahf", + "llvm.s390.vmahg" => "__builtin_s390_vmahg", "llvm.s390.vmahh" => "__builtin_s390_vmahh", + "llvm.s390.vmahq" => "__builtin_s390_vmahq", "llvm.s390.vmaleb" => "__builtin_s390_vmaleb", "llvm.s390.vmalef" => "__builtin_s390_vmalef", + "llvm.s390.vmaleg" => "__builtin_s390_vmaleg", "llvm.s390.vmaleh" => "__builtin_s390_vmaleh", "llvm.s390.vmalhb" => "__builtin_s390_vmalhb", "llvm.s390.vmalhf" => "__builtin_s390_vmalhf", + "llvm.s390.vmalhg" => "__builtin_s390_vmalhg", "llvm.s390.vmalhh" => "__builtin_s390_vmalhh", + "llvm.s390.vmalhq" => "__builtin_s390_vmalhq", "llvm.s390.vmalob" => "__builtin_s390_vmalob", "llvm.s390.vmalof" => "__builtin_s390_vmalof", + "llvm.s390.vmalog" => "__builtin_s390_vmalog", "llvm.s390.vmaloh" => "__builtin_s390_vmaloh", "llvm.s390.vmaob" => "__builtin_s390_vmaob", "llvm.s390.vmaof" => "__builtin_s390_vmaof", + "llvm.s390.vmaog" => "__builtin_s390_vmaog", "llvm.s390.vmaoh" => "__builtin_s390_vmaoh", "llvm.s390.vmeb" => "__builtin_s390_vmeb", "llvm.s390.vmef" => "__builtin_s390_vmef", + "llvm.s390.vmeg" => "__builtin_s390_vmeg", "llvm.s390.vmeh" => "__builtin_s390_vmeh", "llvm.s390.vmhb" => "__builtin_s390_vmhb", "llvm.s390.vmhf" => "__builtin_s390_vmhf", + "llvm.s390.vmhg" => "__builtin_s390_vmhg", "llvm.s390.vmhh" => "__builtin_s390_vmhh", + "llvm.s390.vmhq" => "__builtin_s390_vmhq", "llvm.s390.vmleb" => "__builtin_s390_vmleb", "llvm.s390.vmlef" => "__builtin_s390_vmlef", + "llvm.s390.vmleg" => "__builtin_s390_vmleg", "llvm.s390.vmleh" => "__builtin_s390_vmleh", "llvm.s390.vmlhb" => "__builtin_s390_vmlhb", "llvm.s390.vmlhf" => "__builtin_s390_vmlhf", + "llvm.s390.vmlhg" => "__builtin_s390_vmlhg", "llvm.s390.vmlhh" => "__builtin_s390_vmlhh", + "llvm.s390.vmlhq" => "__builtin_s390_vmlhq", "llvm.s390.vmlob" => "__builtin_s390_vmlob", "llvm.s390.vmlof" => "__builtin_s390_vmlof", + "llvm.s390.vmlog" => "__builtin_s390_vmlog", "llvm.s390.vmloh" => "__builtin_s390_vmloh", "llvm.s390.vmob" => "__builtin_s390_vmob", "llvm.s390.vmof" => "__builtin_s390_vmof", + "llvm.s390.vmog" => "__builtin_s390_vmog", "llvm.s390.vmoh" => "__builtin_s390_vmoh", "llvm.s390.vmslg" => "__builtin_s390_vmslg", "llvm.s390.vpdi" => "__builtin_s390_vpdi", @@ -5950,18 +6132,20 @@ match name { "llvm.s390.vtm" => "__builtin_s390_vtm", "llvm.s390.vuphb" => "__builtin_s390_vuphb", "llvm.s390.vuphf" => "__builtin_s390_vuphf", + "llvm.s390.vuphg" => "__builtin_s390_vuphg", "llvm.s390.vuphh" => "__builtin_s390_vuphh", "llvm.s390.vuplb" => "__builtin_s390_vuplb", "llvm.s390.vuplf" => "__builtin_s390_vuplf", + "llvm.s390.vuplg" => "__builtin_s390_vuplg", "llvm.s390.vuplhb" => "__builtin_s390_vuplhb", "llvm.s390.vuplhf" => "__builtin_s390_vuplhf", + "llvm.s390.vuplhg" => "__builtin_s390_vuplhg", "llvm.s390.vuplhh" => "__builtin_s390_vuplhh", "llvm.s390.vuplhw" => "__builtin_s390_vuplhw", "llvm.s390.vupllb" => "__builtin_s390_vupllb", "llvm.s390.vupllf" => "__builtin_s390_vupllf", + "llvm.s390.vupllg" => "__builtin_s390_vupllg", "llvm.s390.vupllh" => "__builtin_s390_vupllh", - // spv - "llvm.spv.create.handle" => "__builtin_hlsl_create_handle", // ve "llvm.ve.vl.andm.MMM" => "__builtin_ve_vl_andm_MMM", "llvm.ve.vl.andm.mmm" => "__builtin_ve_vl_andm_mmm", @@ -7328,6 +7512,27 @@ match name { "llvm.x86.avx.vtestz.ps.256" => "__builtin_ia32_vtestzps256", "llvm.x86.avx.vzeroall" => "__builtin_ia32_vzeroall", "llvm.x86.avx.vzeroupper" => "__builtin_ia32_vzeroupper", + "llvm.x86.avx10.mask.getexp.bf16.128" => "__builtin_ia32_vgetexpbf16128_mask", + "llvm.x86.avx10.mask.getexp.bf16.256" => "__builtin_ia32_vgetexpbf16256_mask", + "llvm.x86.avx10.mask.getexp.bf16.512" => "__builtin_ia32_vgetexpbf16512_mask", + "llvm.x86.avx10.mask.getmant.bf16.128" => "__builtin_ia32_vgetmantbf16128_mask", + "llvm.x86.avx10.mask.getmant.bf16.256" => "__builtin_ia32_vgetmantbf16256_mask", + "llvm.x86.avx10.mask.getmant.bf16.512" => "__builtin_ia32_vgetmantbf16512_mask", + "llvm.x86.avx10.mask.rcp.bf16.128" => "__builtin_ia32_vrcpbf16128_mask", + "llvm.x86.avx10.mask.rcp.bf16.256" => "__builtin_ia32_vrcpbf16256_mask", + "llvm.x86.avx10.mask.rcp.bf16.512" => "__builtin_ia32_vrcpbf16512_mask", + "llvm.x86.avx10.mask.reduce.bf16.128" => "__builtin_ia32_vreducebf16128_mask", + "llvm.x86.avx10.mask.reduce.bf16.256" => "__builtin_ia32_vreducebf16256_mask", + "llvm.x86.avx10.mask.reduce.bf16.512" => "__builtin_ia32_vreducebf16512_mask", + "llvm.x86.avx10.mask.rndscale.bf16.128" => "__builtin_ia32_vrndscalebf16_128_mask", + "llvm.x86.avx10.mask.rndscale.bf16.256" => "__builtin_ia32_vrndscalebf16_256_mask", + "llvm.x86.avx10.mask.rndscale.bf16.512" => "__builtin_ia32_vrndscalebf16_mask", + "llvm.x86.avx10.mask.rsqrt.bf16.128" => "__builtin_ia32_vrsqrtbf16128_mask", + "llvm.x86.avx10.mask.rsqrt.bf16.256" => "__builtin_ia32_vrsqrtbf16256_mask", + "llvm.x86.avx10.mask.rsqrt.bf16.512" => "__builtin_ia32_vrsqrtbf16512_mask", + "llvm.x86.avx10.mask.scalef.bf16.128" => "__builtin_ia32_vscalefbf16128_mask", + "llvm.x86.avx10.mask.scalef.bf16.256" => "__builtin_ia32_vscalefbf16256_mask", + "llvm.x86.avx10.mask.scalef.bf16.512" => "__builtin_ia32_vscalefbf16512_mask", "llvm.x86.avx10.mask.vcvt2ps2phx.128" => "__builtin_ia32_vcvt2ps2phx128_mask", "llvm.x86.avx10.mask.vcvt2ps2phx.256" => "__builtin_ia32_vcvt2ps2phx256_mask", "llvm.x86.avx10.mask.vcvt2ps2phx.512" => "__builtin_ia32_vcvt2ps2phx512_mask", @@ -7346,171 +7551,194 @@ match name { "llvm.x86.avx10.mask.vcvthf82ph128" => "__builtin_ia32_vcvthf8_2ph128_mask", "llvm.x86.avx10.mask.vcvthf82ph256" => "__builtin_ia32_vcvthf8_2ph256_mask", "llvm.x86.avx10.mask.vcvthf82ph512" => "__builtin_ia32_vcvthf8_2ph512_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8128" => "__builtin_ia32_vcvtneph2bf8_128_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8256" => "__builtin_ia32_vcvtneph2bf8_256_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8512" => "__builtin_ia32_vcvtneph2bf8_512_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8s128" => "__builtin_ia32_vcvtneph2bf8s_128_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8s256" => "__builtin_ia32_vcvtneph2bf8s_256_mask", - "llvm.x86.avx10.mask.vcvtneph2bf8s512" => "__builtin_ia32_vcvtneph2bf8s_512_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8128" => "__builtin_ia32_vcvtneph2hf8_128_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8256" => "__builtin_ia32_vcvtneph2hf8_256_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8512" => "__builtin_ia32_vcvtneph2hf8_512_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8s128" => "__builtin_ia32_vcvtneph2hf8s_128_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8s256" => "__builtin_ia32_vcvtneph2hf8s_256_mask", - "llvm.x86.avx10.mask.vcvtneph2hf8s512" => "__builtin_ia32_vcvtneph2hf8s_512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2dq256" => "__builtin_ia32_vcvtpd2dq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2ph256" => "__builtin_ia32_vcvtpd2ph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2ps256" => "__builtin_ia32_vcvtpd2ps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2qq256" => "__builtin_ia32_vcvtpd2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2udq256" => "__builtin_ia32_vcvtpd2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtpd2uqq256" => "__builtin_ia32_vcvtpd2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2dq256" => "__builtin_ia32_vcvtph2dq256_round_mask", + "llvm.x86.avx10.mask.vcvtph2bf8128" => "__builtin_ia32_vcvtph2bf8_128_mask", + "llvm.x86.avx10.mask.vcvtph2bf8256" => "__builtin_ia32_vcvtph2bf8_256_mask", + "llvm.x86.avx10.mask.vcvtph2bf8512" => "__builtin_ia32_vcvtph2bf8_512_mask", + "llvm.x86.avx10.mask.vcvtph2bf8s128" => "__builtin_ia32_vcvtph2bf8s_128_mask", + "llvm.x86.avx10.mask.vcvtph2bf8s256" => "__builtin_ia32_vcvtph2bf8s_256_mask", + "llvm.x86.avx10.mask.vcvtph2bf8s512" => "__builtin_ia32_vcvtph2bf8s_512_mask", + "llvm.x86.avx10.mask.vcvtph2hf8128" => "__builtin_ia32_vcvtph2hf8_128_mask", + "llvm.x86.avx10.mask.vcvtph2hf8256" => "__builtin_ia32_vcvtph2hf8_256_mask", + "llvm.x86.avx10.mask.vcvtph2hf8512" => "__builtin_ia32_vcvtph2hf8_512_mask", + "llvm.x86.avx10.mask.vcvtph2hf8s128" => "__builtin_ia32_vcvtph2hf8s_128_mask", + "llvm.x86.avx10.mask.vcvtph2hf8s256" => "__builtin_ia32_vcvtph2hf8s_256_mask", + "llvm.x86.avx10.mask.vcvtph2hf8s512" => "__builtin_ia32_vcvtph2hf8s_512_mask", "llvm.x86.avx10.mask.vcvtph2ibs128" => "__builtin_ia32_vcvtph2ibs128_mask", "llvm.x86.avx10.mask.vcvtph2ibs256" => "__builtin_ia32_vcvtph2ibs256_mask", "llvm.x86.avx10.mask.vcvtph2ibs512" => "__builtin_ia32_vcvtph2ibs512_mask", "llvm.x86.avx10.mask.vcvtph2iubs128" => "__builtin_ia32_vcvtph2iubs128_mask", "llvm.x86.avx10.mask.vcvtph2iubs256" => "__builtin_ia32_vcvtph2iubs256_mask", "llvm.x86.avx10.mask.vcvtph2iubs512" => "__builtin_ia32_vcvtph2iubs512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2pd256" => "__builtin_ia32_vcvtph2pd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2psx256" => "__builtin_ia32_vcvtph2psx256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2qq256" => "__builtin_ia32_vcvtph2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2udq256" => "__builtin_ia32_vcvtph2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2uqq256" => "__builtin_ia32_vcvtph2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2uw256" => "__builtin_ia32_vcvtph2uw256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtph2w256" => "__builtin_ia32_vcvtph2w256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2dq256" => "__builtin_ia32_vcvtps2dq256_round_mask", "llvm.x86.avx10.mask.vcvtps2ibs128" => "__builtin_ia32_vcvtps2ibs128_mask", "llvm.x86.avx10.mask.vcvtps2ibs256" => "__builtin_ia32_vcvtps2ibs256_mask", "llvm.x86.avx10.mask.vcvtps2ibs512" => "__builtin_ia32_vcvtps2ibs512_mask", "llvm.x86.avx10.mask.vcvtps2iubs128" => "__builtin_ia32_vcvtps2iubs128_mask", "llvm.x86.avx10.mask.vcvtps2iubs256" => "__builtin_ia32_vcvtps2iubs256_mask", "llvm.x86.avx10.mask.vcvtps2iubs512" => "__builtin_ia32_vcvtps2iubs512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2pd256" => "__builtin_ia32_vcvtps2pd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2ph256" => "__builtin_ia32_vcvtps2ph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2phx256" => "__builtin_ia32_vcvtps2phx256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2qq256" => "__builtin_ia32_vcvtps2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2udq256" => "__builtin_ia32_vcvtps2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvtps2uqq256" => "__builtin_ia32_vcvtps2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2dq256" => "__builtin_ia32_vcvttpd2dq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2qq256" => "__builtin_ia32_vcvttpd2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2udq256" => "__builtin_ia32_vcvttpd2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2uqq256" => "__builtin_ia32_vcvttpd2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2dq256" => "__builtin_ia32_vcvttph2dq256_round_mask", + "llvm.x86.avx10.mask.vcvttpd2dqs.128" => "__builtin_ia32_vcvttpd2dqs128_mask", + "llvm.x86.avx10.mask.vcvttpd2dqs.256" => "__builtin_ia32_vcvttpd2dqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2dqs.round.512" => "__builtin_ia32_vcvttpd2dqs512_round_mask", + "llvm.x86.avx10.mask.vcvttpd2qqs.128" => "__builtin_ia32_vcvttpd2qqs128_mask", + "llvm.x86.avx10.mask.vcvttpd2qqs.256" => "__builtin_ia32_vcvttpd2qqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2qqs.round.512" => "__builtin_ia32_vcvttpd2qqs512_round_mask", + "llvm.x86.avx10.mask.vcvttpd2udqs.128" => "__builtin_ia32_vcvttpd2udqs128_mask", + "llvm.x86.avx10.mask.vcvttpd2udqs.256" => "__builtin_ia32_vcvttpd2udqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2udqs.round.512" => "__builtin_ia32_vcvttpd2udqs512_round_mask", + "llvm.x86.avx10.mask.vcvttpd2uqqs.128" => "__builtin_ia32_vcvttpd2uqqs128_mask", + "llvm.x86.avx10.mask.vcvttpd2uqqs.256" => "__builtin_ia32_vcvttpd2uqqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttpd2uqqs.round.512" => "__builtin_ia32_vcvttpd2uqqs512_round_mask", "llvm.x86.avx10.mask.vcvttph2ibs128" => "__builtin_ia32_vcvttph2ibs128_mask", "llvm.x86.avx10.mask.vcvttph2ibs256" => "__builtin_ia32_vcvttph2ibs256_mask", "llvm.x86.avx10.mask.vcvttph2ibs512" => "__builtin_ia32_vcvttph2ibs512_mask", "llvm.x86.avx10.mask.vcvttph2iubs128" => "__builtin_ia32_vcvttph2iubs128_mask", "llvm.x86.avx10.mask.vcvttph2iubs256" => "__builtin_ia32_vcvttph2iubs256_mask", "llvm.x86.avx10.mask.vcvttph2iubs512" => "__builtin_ia32_vcvttph2iubs512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2qq256" => "__builtin_ia32_vcvttph2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2udq256" => "__builtin_ia32_vcvttph2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2uqq256" => "__builtin_ia32_vcvttph2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2uw256" => "__builtin_ia32_vcvttph2uw256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttph2w256" => "__builtin_ia32_vcvttph2w256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2dq256" => "__builtin_ia32_vcvttps2dq256_round_mask", + "llvm.x86.avx10.mask.vcvttps2dqs.128" => "__builtin_ia32_vcvttps2dqs128_mask", + "llvm.x86.avx10.mask.vcvttps2dqs.256" => "__builtin_ia32_vcvttps2dqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2dqs.round.512" => "__builtin_ia32_vcvttps2dqs512_round_mask", "llvm.x86.avx10.mask.vcvttps2ibs128" => "__builtin_ia32_vcvttps2ibs128_mask", "llvm.x86.avx10.mask.vcvttps2ibs256" => "__builtin_ia32_vcvttps2ibs256_mask", "llvm.x86.avx10.mask.vcvttps2ibs512" => "__builtin_ia32_vcvttps2ibs512_mask", "llvm.x86.avx10.mask.vcvttps2iubs128" => "__builtin_ia32_vcvttps2iubs128_mask", "llvm.x86.avx10.mask.vcvttps2iubs256" => "__builtin_ia32_vcvttps2iubs256_mask", "llvm.x86.avx10.mask.vcvttps2iubs512" => "__builtin_ia32_vcvttps2iubs512_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2qq256" => "__builtin_ia32_vcvttps2qq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2udq256" => "__builtin_ia32_vcvttps2udq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2uqq256" => "__builtin_ia32_vcvttps2uqq256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfcmaddcph256" => "__builtin_ia32_vfcmaddcph256_round_mask3", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfcmulcph256" => "__builtin_ia32_vfcmulcph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfixupimmpd256" => "__builtin_ia32_vfixupimmpd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfixupimmps256" => "__builtin_ia32_vfixupimmps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfmaddcph256" => "__builtin_ia32_vfmaddcph256_round_mask3", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vfmulcph256" => "__builtin_ia32_vfmulcph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetexppd256" => "__builtin_ia32_vgetexppd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetexpph256" => "__builtin_ia32_vgetexpph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetexpps256" => "__builtin_ia32_vgetexpps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetmantpd256" => "__builtin_ia32_vgetmantpd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetmantph256" => "__builtin_ia32_vgetmantph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vgetmantps256" => "__builtin_ia32_vgetmantps256_round_mask", + "llvm.x86.avx10.mask.vcvttps2qqs.128" => "__builtin_ia32_vcvttps2qqs128_mask", + "llvm.x86.avx10.mask.vcvttps2qqs.256" => "__builtin_ia32_vcvttps2qqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2qqs.round.512" => "__builtin_ia32_vcvttps2qqs512_round_mask", + "llvm.x86.avx10.mask.vcvttps2udqs.128" => "__builtin_ia32_vcvttps2udqs128_mask", + "llvm.x86.avx10.mask.vcvttps2udqs.256" => "__builtin_ia32_vcvttps2udqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2udqs.round.512" => "__builtin_ia32_vcvttps2udqs512_round_mask", + "llvm.x86.avx10.mask.vcvttps2uqqs.128" => "__builtin_ia32_vcvttps2uqqs128_mask", + "llvm.x86.avx10.mask.vcvttps2uqqs.256" => "__builtin_ia32_vcvttps2uqqs256_mask", + // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vcvttps2uqqs.round.512" => "__builtin_ia32_vcvttps2uqqs512_round_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxpd.round" => "__builtin_ia32_vminmaxpd512_round_mask", "llvm.x86.avx10.mask.vminmaxpd128" => "__builtin_ia32_vminmaxpd128_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxpd256.round" => "__builtin_ia32_vminmaxpd256_round_mask", + "llvm.x86.avx10.mask.vminmaxpd256" => "__builtin_ia32_vminmaxpd256_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxph.round" => "__builtin_ia32_vminmaxph512_round_mask", "llvm.x86.avx10.mask.vminmaxph128" => "__builtin_ia32_vminmaxph128_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxph256.round" => "__builtin_ia32_vminmaxph256_round_mask", + "llvm.x86.avx10.mask.vminmaxph256" => "__builtin_ia32_vminmaxph256_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxps.round" => "__builtin_ia32_vminmaxps512_round_mask", "llvm.x86.avx10.mask.vminmaxps128" => "__builtin_ia32_vminmaxps128_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxps256.round" => "__builtin_ia32_vminmaxps256_round_mask", + "llvm.x86.avx10.mask.vminmaxps256" => "__builtin_ia32_vminmaxps256_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxsd.round" => "__builtin_ia32_vminmaxsd_round_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxsh.round" => "__builtin_ia32_vminmaxsh_round_mask", // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vminmaxss.round" => "__builtin_ia32_vminmaxss_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrangepd256" => "__builtin_ia32_vrangepd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrangeps256" => "__builtin_ia32_vrangeps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vreducepd256" => "__builtin_ia32_vreducepd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vreduceph256" => "__builtin_ia32_vreduceph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vreduceps256" => "__builtin_ia32_vreduceps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrndscalepd256" => "__builtin_ia32_vrndscalepd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrndscaleph256" => "__builtin_ia32_vrndscaleph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vrndscaleps256" => "__builtin_ia32_vrndscaleps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vscalefpd256" => "__builtin_ia32_vscalefpd256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vscalefph256" => "__builtin_ia32_vscalefph256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.mask.vscalefps256" => "__builtin_ia32_vscalefps256_round_mask", - // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfcmaddcph256" => "__builtin_ia32_vfcmaddcph256_round_maskz", - // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfixupimmpd256" => "__builtin_ia32_vfixupimmpd256_round_maskz", - // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfixupimmps256" => "__builtin_ia32_vfixupimmps256_round_maskz", - // [INVALID CONVERSION]: "llvm.x86.avx10.maskz.vfmaddcph256" => "__builtin_ia32_vfmaddcph256_round_maskz", + "llvm.x86.avx10.vaddbf16128" => "__builtin_ia32_vaddbf16128", + "llvm.x86.avx10.vaddbf16256" => "__builtin_ia32_vaddbf16256", + "llvm.x86.avx10.vaddbf16512" => "__builtin_ia32_vaddbf16512", "llvm.x86.avx10.vaddpd256" => "__builtin_ia32_vaddpd256_round", "llvm.x86.avx10.vaddph256" => "__builtin_ia32_vaddph256_round", "llvm.x86.avx10.vaddps256" => "__builtin_ia32_vaddps256_round", - "llvm.x86.avx10.vcvtne2ph2bf8128" => "__builtin_ia32_vcvtne2ph2bf8_128", - "llvm.x86.avx10.vcvtne2ph2bf8256" => "__builtin_ia32_vcvtne2ph2bf8_256", - "llvm.x86.avx10.vcvtne2ph2bf8512" => "__builtin_ia32_vcvtne2ph2bf8_512", - "llvm.x86.avx10.vcvtne2ph2bf8s128" => "__builtin_ia32_vcvtne2ph2bf8s_128", - "llvm.x86.avx10.vcvtne2ph2bf8s256" => "__builtin_ia32_vcvtne2ph2bf8s_256", - "llvm.x86.avx10.vcvtne2ph2bf8s512" => "__builtin_ia32_vcvtne2ph2bf8s_512", - "llvm.x86.avx10.vcvtne2ph2hf8128" => "__builtin_ia32_vcvtne2ph2hf8_128", - "llvm.x86.avx10.vcvtne2ph2hf8256" => "__builtin_ia32_vcvtne2ph2hf8_256", - "llvm.x86.avx10.vcvtne2ph2hf8512" => "__builtin_ia32_vcvtne2ph2hf8_512", - "llvm.x86.avx10.vcvtne2ph2hf8s128" => "__builtin_ia32_vcvtne2ph2hf8s_128", - "llvm.x86.avx10.vcvtne2ph2hf8s256" => "__builtin_ia32_vcvtne2ph2hf8s_256", - "llvm.x86.avx10.vcvtne2ph2hf8s512" => "__builtin_ia32_vcvtne2ph2hf8s_512", - "llvm.x86.avx10.vcvtnebf162ibs128" => "__builtin_ia32_vcvtnebf162ibs128", - "llvm.x86.avx10.vcvtnebf162ibs256" => "__builtin_ia32_vcvtnebf162ibs256", - "llvm.x86.avx10.vcvtnebf162ibs512" => "__builtin_ia32_vcvtnebf162ibs512", - "llvm.x86.avx10.vcvtnebf162iubs128" => "__builtin_ia32_vcvtnebf162iubs128", - "llvm.x86.avx10.vcvtnebf162iubs256" => "__builtin_ia32_vcvtnebf162iubs256", - "llvm.x86.avx10.vcvtnebf162iubs512" => "__builtin_ia32_vcvtnebf162iubs512", - "llvm.x86.avx10.vcvttnebf162ibs128" => "__builtin_ia32_vcvttnebf162ibs128", - "llvm.x86.avx10.vcvttnebf162ibs256" => "__builtin_ia32_vcvttnebf162ibs256", - "llvm.x86.avx10.vcvttnebf162ibs512" => "__builtin_ia32_vcvttnebf162ibs512", - "llvm.x86.avx10.vcvttnebf162iubs128" => "__builtin_ia32_vcvttnebf162iubs128", - "llvm.x86.avx10.vcvttnebf162iubs256" => "__builtin_ia32_vcvttnebf162iubs256", - "llvm.x86.avx10.vcvttnebf162iubs512" => "__builtin_ia32_vcvttnebf162iubs512", - "llvm.x86.avx10.vdivpd256" => "__builtin_ia32_vdivpd256_round", - "llvm.x86.avx10.vdivph256" => "__builtin_ia32_vdivph256_round", - "llvm.x86.avx10.vdivps256" => "__builtin_ia32_vdivps256_round", + "llvm.x86.avx10.vcomisbf16eq" => "__builtin_ia32_vcomisbf16eq", + "llvm.x86.avx10.vcomisbf16ge" => "__builtin_ia32_vcomisbf16ge", + "llvm.x86.avx10.vcomisbf16gt" => "__builtin_ia32_vcomisbf16gt", + "llvm.x86.avx10.vcomisbf16le" => "__builtin_ia32_vcomisbf16le", + "llvm.x86.avx10.vcomisbf16lt" => "__builtin_ia32_vcomisbf16lt", + "llvm.x86.avx10.vcomisbf16neq" => "__builtin_ia32_vcomisbf16neq", + "llvm.x86.avx10.vcvt2ph2bf8128" => "__builtin_ia32_vcvt2ph2bf8_128", + "llvm.x86.avx10.vcvt2ph2bf8256" => "__builtin_ia32_vcvt2ph2bf8_256", + "llvm.x86.avx10.vcvt2ph2bf8512" => "__builtin_ia32_vcvt2ph2bf8_512", + "llvm.x86.avx10.vcvt2ph2bf8s128" => "__builtin_ia32_vcvt2ph2bf8s_128", + "llvm.x86.avx10.vcvt2ph2bf8s256" => "__builtin_ia32_vcvt2ph2bf8s_256", + "llvm.x86.avx10.vcvt2ph2bf8s512" => "__builtin_ia32_vcvt2ph2bf8s_512", + "llvm.x86.avx10.vcvt2ph2hf8128" => "__builtin_ia32_vcvt2ph2hf8_128", + "llvm.x86.avx10.vcvt2ph2hf8256" => "__builtin_ia32_vcvt2ph2hf8_256", + "llvm.x86.avx10.vcvt2ph2hf8512" => "__builtin_ia32_vcvt2ph2hf8_512", + "llvm.x86.avx10.vcvt2ph2hf8s128" => "__builtin_ia32_vcvt2ph2hf8s_128", + "llvm.x86.avx10.vcvt2ph2hf8s256" => "__builtin_ia32_vcvt2ph2hf8s_256", + "llvm.x86.avx10.vcvt2ph2hf8s512" => "__builtin_ia32_vcvt2ph2hf8s_512", + "llvm.x86.avx10.vcvtbf162ibs128" => "__builtin_ia32_vcvtbf162ibs128", + "llvm.x86.avx10.vcvtbf162ibs256" => "__builtin_ia32_vcvtbf162ibs256", + "llvm.x86.avx10.vcvtbf162ibs512" => "__builtin_ia32_vcvtbf162ibs512", + "llvm.x86.avx10.vcvtbf162iubs128" => "__builtin_ia32_vcvtbf162iubs128", + "llvm.x86.avx10.vcvtbf162iubs256" => "__builtin_ia32_vcvtbf162iubs256", + "llvm.x86.avx10.vcvtbf162iubs512" => "__builtin_ia32_vcvtbf162iubs512", + "llvm.x86.avx10.vcvttbf162ibs128" => "__builtin_ia32_vcvttbf162ibs128", + "llvm.x86.avx10.vcvttbf162ibs256" => "__builtin_ia32_vcvttbf162ibs256", + "llvm.x86.avx10.vcvttbf162ibs512" => "__builtin_ia32_vcvttbf162ibs512", + "llvm.x86.avx10.vcvttbf162iubs128" => "__builtin_ia32_vcvttbf162iubs128", + "llvm.x86.avx10.vcvttbf162iubs256" => "__builtin_ia32_vcvttbf162iubs256", + "llvm.x86.avx10.vcvttbf162iubs512" => "__builtin_ia32_vcvttbf162iubs512", + "llvm.x86.avx10.vcvttsd2sis" => "__builtin_ia32_vcvttsd2sis32", + "llvm.x86.avx10.vcvttsd2sis64" => "__builtin_ia32_vcvttsd2sis64", + "llvm.x86.avx10.vcvttsd2usis" => "__builtin_ia32_vcvttsd2usis32", + "llvm.x86.avx10.vcvttsd2usis64" => "__builtin_ia32_vcvttsd2usis64", + "llvm.x86.avx10.vcvttss2sis" => "__builtin_ia32_vcvttss2sis32", + "llvm.x86.avx10.vcvttss2sis64" => "__builtin_ia32_vcvttss2sis64", + "llvm.x86.avx10.vcvttss2usis" => "__builtin_ia32_vcvttss2usis32", + "llvm.x86.avx10.vcvttss2usis64" => "__builtin_ia32_vcvttss2usis64", + "llvm.x86.avx10.vdivbf16128" => "__builtin_ia32_vdivbf16128", + "llvm.x86.avx10.vdivbf16256" => "__builtin_ia32_vdivbf16256", + "llvm.x86.avx10.vdivbf16512" => "__builtin_ia32_vdivbf16512", "llvm.x86.avx10.vdpphps.128" => "__builtin_ia32_vdpphps128", "llvm.x86.avx10.vdpphps.256" => "__builtin_ia32_vdpphps256", "llvm.x86.avx10.vdpphps.512" => "__builtin_ia32_vdpphps512", - "llvm.x86.avx10.vfmaddsubpd256" => "__builtin_ia32_vfmaddsubpd256_round", - "llvm.x86.avx10.vfmaddsubph256" => "__builtin_ia32_vfmaddsubph256_round", - "llvm.x86.avx10.vfmaddsubps256" => "__builtin_ia32_vfmaddsubps256_round", - "llvm.x86.avx10.vmaxpd256" => "__builtin_ia32_vmaxpd256_round", - "llvm.x86.avx10.vmaxph256" => "__builtin_ia32_vmaxph256_round", - "llvm.x86.avx10.vmaxps256" => "__builtin_ia32_vmaxps256_round", - "llvm.x86.avx10.vminmaxnepbf16128" => "__builtin_ia32_vminmaxnepbf16128", - "llvm.x86.avx10.vminmaxnepbf16256" => "__builtin_ia32_vminmaxnepbf16256", - "llvm.x86.avx10.vminmaxnepbf16512" => "__builtin_ia32_vminmaxnepbf16512", + "llvm.x86.avx10.vfmadd132bf16128" => "__builtin_ia32_vfmadd132bf16128", + "llvm.x86.avx10.vfmadd132bf16256" => "__builtin_ia32_vfmadd132bf16256", + "llvm.x86.avx10.vfmadd132bf16512" => "__builtin_ia32_vfmadd132bf16512", + "llvm.x86.avx10.vfmadd213bf16128" => "__builtin_ia32_vfmadd213bf16128", + "llvm.x86.avx10.vfmadd213bf16256" => "__builtin_ia32_vfmadd213bf16256", + "llvm.x86.avx10.vfmadd231bf16128" => "__builtin_ia32_vfmadd231bf16128", + "llvm.x86.avx10.vfmadd231bf16256" => "__builtin_ia32_vfmadd231bf16256", + "llvm.x86.avx10.vfmadd231bf16512" => "__builtin_ia32_vfmadd231bf16512", + "llvm.x86.avx10.vfmsub132bf16128" => "__builtin_ia32_vfmsub132bf16128", + "llvm.x86.avx10.vfmsub132bf16256" => "__builtin_ia32_vfmsub132bf16256", + "llvm.x86.avx10.vfmsub132bf16512" => "__builtin_ia32_vfmsub132bf16512", + "llvm.x86.avx10.vfmsub213bf16128" => "__builtin_ia32_vfmsub213bf16128", + "llvm.x86.avx10.vfmsub213bf16256" => "__builtin_ia32_vfmsub213bf16256", + "llvm.x86.avx10.vfmsub213bf16512" => "__builtin_ia32_vfmsub213bf16512", + "llvm.x86.avx10.vfmsub231bf16128" => "__builtin_ia32_vfmsub231bf16128", + "llvm.x86.avx10.vfmsub231bf16256" => "__builtin_ia32_vfmsub231bf16256", + "llvm.x86.avx10.vfmsub231bf16512" => "__builtin_ia32_vfmsub231bf16512", + "llvm.x86.avx10.vfnmadd132bf16128" => "__builtin_ia32_vfnmadd132bf16128", + "llvm.x86.avx10.vfnmadd132bf16256" => "__builtin_ia32_vfnmadd132bf16256", + "llvm.x86.avx10.vfnmadd132bf16512" => "__builtin_ia32_vfnmadd132bf16512", + "llvm.x86.avx10.vfnmadd213bf16128" => "__builtin_ia32_vfnmadd213bf16128", + "llvm.x86.avx10.vfnmadd213bf16256" => "__builtin_ia32_vfnmadd213bf16256", + "llvm.x86.avx10.vfnmadd213bf16512" => "__builtin_ia32_vfnmadd213bf16512", + "llvm.x86.avx10.vfnmadd231bf16128" => "__builtin_ia32_vfnmadd231bf16128", + "llvm.x86.avx10.vfnmadd231bf16256" => "__builtin_ia32_vfnmadd231bf16256", + "llvm.x86.avx10.vfnmadd231bf16512" => "__builtin_ia32_vfnmadd231bf16512", + "llvm.x86.avx10.vfnmsub132bf16128" => "__builtin_ia32_vfnmsub132bf16128", + "llvm.x86.avx10.vfnmsub132bf16256" => "__builtin_ia32_vfnmsub132bf16256", + "llvm.x86.avx10.vfnmsub132bf16512" => "__builtin_ia32_vfnmsub132bf16512", + "llvm.x86.avx10.vfnmsub213bf16128" => "__builtin_ia32_vfnmsub213bf16128", + "llvm.x86.avx10.vfnmsub213bf16256" => "__builtin_ia32_vfnmsub213bf16256", + "llvm.x86.avx10.vfnmsub213bf16512" => "__builtin_ia32_vfnmsub213bf16512", + "llvm.x86.avx10.vfnmsub231bf16128" => "__builtin_ia32_vfnmsub231bf16128", + "llvm.x86.avx10.vfnmsub231bf16256" => "__builtin_ia32_vfnmsub231bf16256", + "llvm.x86.avx10.vfnmsub231bf16512" => "__builtin_ia32_vfnmsub231bf16512", + "llvm.x86.avx10.vmaxbf16128" => "__builtin_ia32_vmaxbf16128", + "llvm.x86.avx10.vmaxbf16256" => "__builtin_ia32_vmaxbf16256", + "llvm.x86.avx10.vmaxbf16512" => "__builtin_ia32_vmaxbf16512", + "llvm.x86.avx10.vminbf16128" => "__builtin_ia32_vminbf16128", + "llvm.x86.avx10.vminbf16256" => "__builtin_ia32_vminbf16256", + "llvm.x86.avx10.vminbf16512" => "__builtin_ia32_vminbf16512", + "llvm.x86.avx10.vminmaxbf16128" => "__builtin_ia32_vminmaxbf16128", + "llvm.x86.avx10.vminmaxbf16256" => "__builtin_ia32_vminmaxbf16256", + "llvm.x86.avx10.vminmaxbf16512" => "__builtin_ia32_vminmaxbf16512", "llvm.x86.avx10.vminmaxpd128" => "__builtin_ia32_vminmaxpd128", "llvm.x86.avx10.vminmaxpd256" => "__builtin_ia32_vminmaxpd256", "llvm.x86.avx10.vminmaxph128" => "__builtin_ia32_vminmaxph128", "llvm.x86.avx10.vminmaxph256" => "__builtin_ia32_vminmaxph256", "llvm.x86.avx10.vminmaxps128" => "__builtin_ia32_vminmaxps128", "llvm.x86.avx10.vminmaxps256" => "__builtin_ia32_vminmaxps256", - "llvm.x86.avx10.vminpd256" => "__builtin_ia32_vminpd256_round", - "llvm.x86.avx10.vminph256" => "__builtin_ia32_vminph256_round", - "llvm.x86.avx10.vminps256" => "__builtin_ia32_vminps256_round", + "llvm.x86.avx10.vmovrsb128" => "__builtin_ia32_vmovrsb128", + "llvm.x86.avx10.vmovrsb256" => "__builtin_ia32_vmovrsb256", + "llvm.x86.avx10.vmovrsb512" => "__builtin_ia32_vmovrsb512", + "llvm.x86.avx10.vmovrsd128" => "__builtin_ia32_vmovrsd128", + "llvm.x86.avx10.vmovrsd256" => "__builtin_ia32_vmovrsd256", + "llvm.x86.avx10.vmovrsd512" => "__builtin_ia32_vmovrsd512", + "llvm.x86.avx10.vmovrsq128" => "__builtin_ia32_vmovrsq128", + "llvm.x86.avx10.vmovrsq256" => "__builtin_ia32_vmovrsq256", + "llvm.x86.avx10.vmovrsq512" => "__builtin_ia32_vmovrsq512", + "llvm.x86.avx10.vmovrsw128" => "__builtin_ia32_vmovrsw128", + "llvm.x86.avx10.vmovrsw256" => "__builtin_ia32_vmovrsw256", + "llvm.x86.avx10.vmovrsw512" => "__builtin_ia32_vmovrsw512", "llvm.x86.avx10.vmpsadbw.512" => "__builtin_ia32_mpsadbw512", - "llvm.x86.avx10.vmulpd256" => "__builtin_ia32_vmulpd256_round", - "llvm.x86.avx10.vmulph256" => "__builtin_ia32_vmulph256_round", - "llvm.x86.avx10.vmulps256" => "__builtin_ia32_vmulps256_round", + "llvm.x86.avx10.vmulbf16128" => "__builtin_ia32_vmulbf16128", + "llvm.x86.avx10.vmulbf16256" => "__builtin_ia32_vmulbf16256", + "llvm.x86.avx10.vmulbf16512" => "__builtin_ia32_vmulbf16512", "llvm.x86.avx10.vpdpbssd.512" => "__builtin_ia32_vpdpbssd512", "llvm.x86.avx10.vpdpbssds.512" => "__builtin_ia32_vpdpbssds512", "llvm.x86.avx10.vpdpbsud.512" => "__builtin_ia32_vpdpbsud512", @@ -7523,12 +7751,9 @@ match name { "llvm.x86.avx10.vpdpwusds.512" => "__builtin_ia32_vpdpwusds512", "llvm.x86.avx10.vpdpwuud.512" => "__builtin_ia32_vpdpwuud512", "llvm.x86.avx10.vpdpwuuds.512" => "__builtin_ia32_vpdpwuuds512", - "llvm.x86.avx10.vsqrtpd256" => "__builtin_ia32_vsqrtpd256_round", - "llvm.x86.avx10.vsqrtph256" => "__builtin_ia32_vsqrtph256_round", - "llvm.x86.avx10.vsqrtps256" => "__builtin_ia32_vsqrtps256_round", - "llvm.x86.avx10.vsubpd256" => "__builtin_ia32_vsubpd256_round", - "llvm.x86.avx10.vsubph256" => "__builtin_ia32_vsubph256_round", - "llvm.x86.avx10.vsubps256" => "__builtin_ia32_vsubps256_round", + "llvm.x86.avx10.vsubbf16128" => "__builtin_ia32_vsubbf16128", + "llvm.x86.avx10.vsubbf16256" => "__builtin_ia32_vsubbf16256", + "llvm.x86.avx10.vsubbf16512" => "__builtin_ia32_vsubbf16512", "llvm.x86.avx2.gather.d.d" => "__builtin_ia32_gatherd_d", "llvm.x86.avx2.gather.d.d.256" => "__builtin_ia32_gatherd_d256", "llvm.x86.avx2.gather.d.pd" => "__builtin_ia32_gatherd_pd", @@ -9279,10 +9504,15 @@ match name { "llvm.x86.mmx.femms" => "__builtin_ia32_femms", "llvm.x86.monitorx" => "__builtin_ia32_monitorx", "llvm.x86.movdir64b" => "__builtin_ia32_movdir64b", + "llvm.x86.movrsdi" => "__builtin_ia32_movrsdi", + "llvm.x86.movrshi" => "__builtin_ia32_movrshi", + "llvm.x86.movrsqi" => "__builtin_ia32_movrsqi", + "llvm.x86.movrssi" => "__builtin_ia32_movrssi", "llvm.x86.mwaitx" => "__builtin_ia32_mwaitx", "llvm.x86.pclmulqdq" => "__builtin_ia32_pclmulqdq128", "llvm.x86.pclmulqdq.256" => "__builtin_ia32_pclmulqdq256", "llvm.x86.pclmulqdq.512" => "__builtin_ia32_pclmulqdq512", + "llvm.x86.prefetchrs" => "__builtin_ia32_prefetchrs", "llvm.x86.ptwrite32" => "__builtin_ia32_ptwrite32", "llvm.x86.ptwrite64" => "__builtin_ia32_ptwrite64", "llvm.x86.rdfsbase.32" => "__builtin_ia32_rdfsbase32", @@ -9536,14 +9766,40 @@ match name { "llvm.x86.stui" => "__builtin_ia32_stui", "llvm.x86.subborrow.u32" => "__builtin_ia32_subborrow_u32", "llvm.x86.subborrow.u64" => "__builtin_ia32_subborrow_u64", + "llvm.x86.t2rpntlvwz0" => "__builtin_ia32_t2rpntlvwz0", + "llvm.x86.t2rpntlvwz0rs" => "__builtin_ia32_t2rpntlvwz0rs", + "llvm.x86.t2rpntlvwz0rst1" => "__builtin_ia32_t2rpntlvwz0rst1", + "llvm.x86.t2rpntlvwz0t1" => "__builtin_ia32_t2rpntlvwz0t1", + "llvm.x86.t2rpntlvwz1" => "__builtin_ia32_t2rpntlvwz1", + "llvm.x86.t2rpntlvwz1rs" => "__builtin_ia32_t2rpntlvwz1rs", + "llvm.x86.t2rpntlvwz1rst1" => "__builtin_ia32_t2rpntlvwz1rst1", + "llvm.x86.t2rpntlvwz1t1" => "__builtin_ia32_t2rpntlvwz1t1", "llvm.x86.tbm.bextri.u32" => "__builtin_ia32_bextri_u32", "llvm.x86.tbm.bextri.u64" => "__builtin_ia32_bextri_u64", "llvm.x86.tcmmimfp16ps" => "__builtin_ia32_tcmmimfp16ps", "llvm.x86.tcmmimfp16ps.internal" => "__builtin_ia32_tcmmimfp16ps_internal", "llvm.x86.tcmmrlfp16ps" => "__builtin_ia32_tcmmrlfp16ps", "llvm.x86.tcmmrlfp16ps.internal" => "__builtin_ia32_tcmmrlfp16ps_internal", + "llvm.x86.tconjtcmmimfp16ps" => "__builtin_ia32_tconjtcmmimfp16ps", + "llvm.x86.tconjtcmmimfp16ps.internal" => "__builtin_ia32_tconjtcmmimfp16ps_internal", + "llvm.x86.tconjtfp16" => "__builtin_ia32_tconjtfp16", + "llvm.x86.tconjtfp16.internal" => "__builtin_ia32_tconjtfp16_internal", + "llvm.x86.tcvtrowd2ps" => "__builtin_ia32_tcvtrowd2ps", + "llvm.x86.tcvtrowd2ps.internal" => "__builtin_ia32_tcvtrowd2ps_internal", + "llvm.x86.tcvtrowps2bf16h" => "__builtin_ia32_tcvtrowps2bf16h", + "llvm.x86.tcvtrowps2bf16h.internal" => "__builtin_ia32_tcvtrowps2bf16h_internal", + "llvm.x86.tcvtrowps2bf16l" => "__builtin_ia32_tcvtrowps2bf16l", + "llvm.x86.tcvtrowps2bf16l.internal" => "__builtin_ia32_tcvtrowps2bf16l_internal", + "llvm.x86.tcvtrowps2phh" => "__builtin_ia32_tcvtrowps2phh", + "llvm.x86.tcvtrowps2phh.internal" => "__builtin_ia32_tcvtrowps2phh_internal", + "llvm.x86.tcvtrowps2phl" => "__builtin_ia32_tcvtrowps2phl", + "llvm.x86.tcvtrowps2phl.internal" => "__builtin_ia32_tcvtrowps2phl_internal", "llvm.x86.tdpbf16ps" => "__builtin_ia32_tdpbf16ps", "llvm.x86.tdpbf16ps.internal" => "__builtin_ia32_tdpbf16ps_internal", + "llvm.x86.tdpbf8ps" => "__builtin_ia32_tdpbf8ps", + "llvm.x86.tdpbf8ps.internal" => "__builtin_ia32_tdpbf8ps_internal", + "llvm.x86.tdpbhf8ps" => "__builtin_ia32_tdpbhf8ps", + "llvm.x86.tdpbhf8ps.internal" => "__builtin_ia32_tdpbhf8ps_internal", "llvm.x86.tdpbssd" => "__builtin_ia32_tdpbssd", "llvm.x86.tdpbssd.internal" => "__builtin_ia32_tdpbssd_internal", "llvm.x86.tdpbsud" => "__builtin_ia32_tdpbsud", @@ -9554,17 +9810,41 @@ match name { "llvm.x86.tdpbuud.internal" => "__builtin_ia32_tdpbuud_internal", "llvm.x86.tdpfp16ps" => "__builtin_ia32_tdpfp16ps", "llvm.x86.tdpfp16ps.internal" => "__builtin_ia32_tdpfp16ps_internal", + "llvm.x86.tdphbf8ps" => "__builtin_ia32_tdphbf8ps", + "llvm.x86.tdphbf8ps.internal" => "__builtin_ia32_tdphbf8ps_internal", + "llvm.x86.tdphf8ps" => "__builtin_ia32_tdphf8ps", + "llvm.x86.tdphf8ps.internal" => "__builtin_ia32_tdphf8ps_internal", "llvm.x86.testui" => "__builtin_ia32_testui", "llvm.x86.tileloadd64" => "__builtin_ia32_tileloadd64", "llvm.x86.tileloadd64.internal" => "__builtin_ia32_tileloadd64_internal", + "llvm.x86.tileloaddrs64" => "__builtin_ia32_tileloaddrs64", + "llvm.x86.tileloaddrs64.internal" => "__builtin_ia32_tileloaddrs64_internal", + "llvm.x86.tileloaddrst164" => "__builtin_ia32_tileloaddrst164", + "llvm.x86.tileloaddrst164.internal" => "__builtin_ia32_tileloaddrst164_internal", "llvm.x86.tileloaddt164" => "__builtin_ia32_tileloaddt164", "llvm.x86.tileloaddt164.internal" => "__builtin_ia32_tileloaddt164_internal", + "llvm.x86.tilemovrow" => "__builtin_ia32_tilemovrow", + "llvm.x86.tilemovrow.internal" => "__builtin_ia32_tilemovrow_internal", "llvm.x86.tilerelease" => "__builtin_ia32_tilerelease", "llvm.x86.tilestored64" => "__builtin_ia32_tilestored64", "llvm.x86.tilestored64.internal" => "__builtin_ia32_tilestored64_internal", "llvm.x86.tilezero" => "__builtin_ia32_tilezero", "llvm.x86.tilezero.internal" => "__builtin_ia32_tilezero_internal", + "llvm.x86.tmmultf32ps" => "__builtin_ia32_tmmultf32ps", + "llvm.x86.tmmultf32ps.internal" => "__builtin_ia32_tmmultf32ps_internal", "llvm.x86.tpause" => "__builtin_ia32_tpause", + "llvm.x86.ttcmmimfp16ps" => "__builtin_ia32_ttcmmimfp16ps", + "llvm.x86.ttcmmimfp16ps.internal" => "__builtin_ia32_ttcmmimfp16ps_internal", + "llvm.x86.ttcmmrlfp16ps" => "__builtin_ia32_ttcmmrlfp16ps", + "llvm.x86.ttcmmrlfp16ps.internal" => "__builtin_ia32_ttcmmrlfp16ps_internal", + "llvm.x86.ttdpbf16ps" => "__builtin_ia32_ttdpbf16ps", + "llvm.x86.ttdpbf16ps.internal" => "__builtin_ia32_ttdpbf16ps_internal", + "llvm.x86.ttdpfp16ps" => "__builtin_ia32_ttdpfp16ps", + "llvm.x86.ttdpfp16ps.internal" => "__builtin_ia32_ttdpfp16ps_internal", + "llvm.x86.ttmmultf32ps" => "__builtin_ia32_ttmmultf32ps", + "llvm.x86.ttmmultf32ps.internal" => "__builtin_ia32_ttmmultf32ps_internal", + "llvm.x86.ttransposed" => "__builtin_ia32_ttransposed", + "llvm.x86.ttransposed.internal" => "__builtin_ia32_ttransposed_internal", "llvm.x86.umonitor" => "__builtin_ia32_umonitor", "llvm.x86.umwait" => "__builtin_ia32_umwait", "llvm.x86.urdmsr" => "__builtin_ia32_urdmsr", @@ -9604,8 +9884,10 @@ match name { "llvm.x86.vsm3rnds2" => "__builtin_ia32_vsm3rnds2", "llvm.x86.vsm4key4128" => "__builtin_ia32_vsm4key4128", "llvm.x86.vsm4key4256" => "__builtin_ia32_vsm4key4256", + "llvm.x86.vsm4key4512" => "__builtin_ia32_vsm4key4512", "llvm.x86.vsm4rnds4128" => "__builtin_ia32_vsm4rnds4128", "llvm.x86.vsm4rnds4256" => "__builtin_ia32_vsm4rnds4256", + "llvm.x86.vsm4rnds4512" => "__builtin_ia32_vsm4rnds4512", "llvm.x86.wbinvd" => "__builtin_ia32_wbinvd", "llvm.x86.wbnoinvd" => "__builtin_ia32_wbnoinvd", "llvm.x86.wrfsbase.32" => "__builtin_ia32_wrfsbase32", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs index 2d731f88d7d36..0eebd21001a90 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs @@ -1,11 +1,90 @@ use std::borrow::Cow; -use gccjit::{CType, Context, Function, FunctionPtrType, RValue, ToRValue, UnaryOp}; +use gccjit::{CType, Context, Field, Function, FunctionPtrType, RValue, ToRValue, Type}; use rustc_codegen_ssa::traits::BuilderMethods; use crate::builder::Builder; use crate::context::CodegenCx; +fn encode_key_128_type<'a, 'gcc, 'tcx>( + builder: &Builder<'a, 'gcc, 'tcx>, +) -> (Type<'gcc>, Field<'gcc>, Field<'gcc>) { + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let field1 = builder.context.new_field(None, builder.u32_type, "field1"); + let field2 = builder.context.new_field(None, m128i, "field2"); + let field3 = builder.context.new_field(None, m128i, "field3"); + let field4 = builder.context.new_field(None, m128i, "field4"); + let field5 = builder.context.new_field(None, m128i, "field5"); + let field6 = builder.context.new_field(None, m128i, "field6"); + let field7 = builder.context.new_field(None, m128i, "field7"); + let encode_type = builder.context.new_struct_type( + None, + "EncodeKey128Output", + &[field1, field2, field3, field4, field5, field6, field7], + ); + #[cfg(feature = "master")] + encode_type.as_type().set_packed(); + (encode_type.as_type(), field1, field2) +} + +fn encode_key_256_type<'a, 'gcc, 'tcx>( + builder: &Builder<'a, 'gcc, 'tcx>, +) -> (Type<'gcc>, Field<'gcc>, Field<'gcc>) { + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let field1 = builder.context.new_field(None, builder.u32_type, "field1"); + let field2 = builder.context.new_field(None, m128i, "field2"); + let field3 = builder.context.new_field(None, m128i, "field3"); + let field4 = builder.context.new_field(None, m128i, "field4"); + let field5 = builder.context.new_field(None, m128i, "field5"); + let field6 = builder.context.new_field(None, m128i, "field6"); + let field7 = builder.context.new_field(None, m128i, "field7"); + let field8 = builder.context.new_field(None, m128i, "field8"); + let encode_type = builder.context.new_struct_type( + None, + "EncodeKey256Output", + &[field1, field2, field3, field4, field5, field6, field7, field8], + ); + #[cfg(feature = "master")] + encode_type.as_type().set_packed(); + (encode_type.as_type(), field1, field2) +} + +fn aes_output_type<'a, 'gcc, 'tcx>( + builder: &Builder<'a, 'gcc, 'tcx>, +) -> (Type<'gcc>, Field<'gcc>, Field<'gcc>) { + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let field1 = builder.context.new_field(None, builder.u8_type, "field1"); + let field2 = builder.context.new_field(None, m128i, "field2"); + let aes_output_type = builder.context.new_struct_type(None, "AesOutput", &[field1, field2]); + let typ = aes_output_type.as_type(); + #[cfg(feature = "master")] + typ.set_packed(); + (typ, field1, field2) +} + +fn wide_aes_output_type<'a, 'gcc, 'tcx>( + builder: &Builder<'a, 'gcc, 'tcx>, +) -> (Type<'gcc>, Field<'gcc>, Field<'gcc>) { + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let field1 = builder.context.new_field(None, builder.u8_type, "field1"); + let field2 = builder.context.new_field(None, m128i, "field2"); + let field3 = builder.context.new_field(None, m128i, "field3"); + let field4 = builder.context.new_field(None, m128i, "field4"); + let field5 = builder.context.new_field(None, m128i, "field5"); + let field6 = builder.context.new_field(None, m128i, "field6"); + let field7 = builder.context.new_field(None, m128i, "field7"); + let field8 = builder.context.new_field(None, m128i, "field8"); + let field9 = builder.context.new_field(None, m128i, "field9"); + let aes_output_type = builder.context.new_struct_type( + None, + "WideAesOutput", + &[field1, field2, field3, field4, field5, field6, field7, field8, field9], + ); + #[cfg(feature = "master")] + aes_output_type.as_type().set_packed(); + (aes_output_type.as_type(), field1, field2) +} + #[cfg_attr(not(feature = "master"), allow(unused_variables))] pub fn adjust_function<'gcc>( context: &'gcc Context<'gcc>, @@ -43,7 +122,6 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( gcc_func: FunctionPtrType<'gcc>, mut args: Cow<'b, [RValue<'gcc>]>, func_name: &str, - original_function_name: Option<&String>, ) -> Cow<'b, [RValue<'gcc>]> { // TODO: this might not be a good way to workaround the missing tile builtins. if func_name == "__builtin_trap" { @@ -504,6 +582,72 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( let arg4 = builder.context.new_rvalue_from_int(arg4_type, -1); args = vec![a, b, c, arg4, new_args[3]].into(); } + "__builtin_ia32_encodekey128_u32" => { + let mut new_args = args.to_vec(); + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let array_type = builder.context.new_array_type(None, m128i, 6); + let result = builder.current_func().new_local(None, array_type, "result"); + new_args.push(result.get_address(None)); + args = new_args.into(); + } + "__builtin_ia32_encodekey256_u32" => { + let mut new_args = args.to_vec(); + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let array_type = builder.context.new_array_type(None, m128i, 7); + let result = builder.current_func().new_local(None, array_type, "result"); + new_args.push(result.get_address(None)); + args = new_args.into(); + } + "__builtin_ia32_aesenc128kl_u8" + | "__builtin_ia32_aesdec128kl_u8" + | "__builtin_ia32_aesenc256kl_u8" + | "__builtin_ia32_aesdec256kl_u8" => { + let mut new_args = vec![]; + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let result = builder.current_func().new_local(None, m128i, "result"); + new_args.push(result.get_address(None)); + new_args.extend(args.to_vec()); + args = new_args.into(); + } + "__builtin_ia32_aesencwide128kl_u8" + | "__builtin_ia32_aesdecwide128kl_u8" + | "__builtin_ia32_aesencwide256kl_u8" + | "__builtin_ia32_aesdecwide256kl_u8" => { + let mut new_args = vec![]; + + let mut old_args = args.to_vec(); + let handle = old_args.swap_remove(0); // Called __P in GCC. + let first_value = old_args.swap_remove(0); + + let element_type = first_value.get_type(); + let array_type = builder.context.new_array_type(None, element_type, 8); + let result = builder.current_func().new_local(None, array_type, "result"); + new_args.push(result.get_address(None)); + + let array = builder.current_func().new_local(None, array_type, "array"); + let input = builder.context.new_array_constructor( + None, + array_type, + &[ + first_value, + old_args.swap_remove(0), + old_args.swap_remove(0), + old_args.swap_remove(0), + old_args.swap_remove(0), + old_args.swap_remove(0), + old_args.swap_remove(0), + old_args.swap_remove(0), + ], + ); + builder.llbb().add_assignment(None, array, input); + let input_ptr = array.get_address(None); + let arg2_type = gcc_func.get_param_type(1); + let input_ptr = builder.context.new_cast(None, input_ptr, arg2_type); + new_args.push(input_ptr); + + new_args.push(handle); + args = new_args.into(); + } _ => (), } } else { @@ -541,33 +685,6 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( let c = builder.context.new_rvalue_from_vector(None, arg3_type, &[new_args[2]; 2]); args = vec![a, b, c, new_args[3]].into(); } - "__builtin_ia32_vfmaddsubpd256" - | "__builtin_ia32_vfmaddsubps" - | "__builtin_ia32_vfmaddsubps256" - | "__builtin_ia32_vfmaddsubpd" => { - if let Some(original_function_name) = original_function_name { - match &**original_function_name { - "llvm.x86.fma.vfmsubadd.pd.256" - | "llvm.x86.fma.vfmsubadd.ps" - | "llvm.x86.fma.vfmsubadd.ps.256" - | "llvm.x86.fma.vfmsubadd.pd" => { - // NOTE: since both llvm.x86.fma.vfmsubadd.ps and llvm.x86.fma.vfmaddsub.ps maps to - // __builtin_ia32_vfmaddsubps, only add minus if this comes from a - // subadd LLVM intrinsic, e.g. _mm256_fmsubadd_pd. - let mut new_args = args.to_vec(); - let arg3 = &mut new_args[2]; - *arg3 = builder.context.new_unary_op( - None, - UnaryOp::Minus, - arg3.get_type(), - *arg3, - ); - args = new_args.into(); - } - _ => (), - } - } - } "__builtin_ia32_ldmxcsr" => { // The builtin __builtin_ia32_ldmxcsr takes an integer value while llvm.x86.sse.ldmxcsr takes a pointer, // so dereference the pointer. @@ -728,6 +845,96 @@ pub fn adjust_intrinsic_return_value<'a, 'gcc, 'tcx>( let f16_type = builder.context.new_c_type(CType::Float16); return_value = builder.context.new_cast(None, return_value, f16_type); } + "__builtin_ia32_encodekey128_u32" => { + // The builtin __builtin_ia32_encodekey128_u32 writes the result in its pointer argument while + // llvm.x86.encodekey128 returns a value. + // We added a result pointer argument and now need to assign its value to the return_value expected by + // the LLVM intrinsic. + let (encode_type, field1, field2) = encode_key_128_type(builder); + let result = builder.current_func().new_local(None, encode_type, "result"); + let field1 = result.access_field(None, field1); + builder.llbb().add_assignment(None, field1, return_value); + let field2 = result.access_field(None, field2); + let field2_type = field2.to_rvalue().get_type(); + let array_type = builder.context.new_array_type(None, field2_type, 6); + let ptr = builder.context.new_cast(None, args[2], array_type.make_pointer()); + let field2_ptr = + builder.context.new_cast(None, field2.get_address(None), array_type.make_pointer()); + builder.llbb().add_assignment( + None, + field2_ptr.dereference(None), + ptr.dereference(None), + ); + return_value = result.to_rvalue(); + } + "__builtin_ia32_encodekey256_u32" => { + // The builtin __builtin_ia32_encodekey256_u32 writes the result in its pointer argument while + // llvm.x86.encodekey256 returns a value. + // We added a result pointer argument and now need to assign its value to the return_value expected by + // the LLVM intrinsic. + let (encode_type, field1, field2) = encode_key_256_type(builder); + let result = builder.current_func().new_local(None, encode_type, "result"); + let field1 = result.access_field(None, field1); + builder.llbb().add_assignment(None, field1, return_value); + let field2 = result.access_field(None, field2); + let field2_type = field2.to_rvalue().get_type(); + let array_type = builder.context.new_array_type(None, field2_type, 7); + let ptr = builder.context.new_cast(None, args[3], array_type.make_pointer()); + let field2_ptr = + builder.context.new_cast(None, field2.get_address(None), array_type.make_pointer()); + builder.llbb().add_assignment( + None, + field2_ptr.dereference(None), + ptr.dereference(None), + ); + return_value = result.to_rvalue(); + } + "__builtin_ia32_aesdec128kl_u8" + | "__builtin_ia32_aesenc128kl_u8" + | "__builtin_ia32_aesdec256kl_u8" + | "__builtin_ia32_aesenc256kl_u8" => { + // The builtin for aesdec/aesenc writes the result in its pointer argument while + // llvm.x86.aesdec128kl returns a value. + // We added a result pointer argument and now need to assign its value to the return_value expected by + // the LLVM intrinsic. + let (aes_output_type, field1, field2) = aes_output_type(builder); + let result = builder.current_func().new_local(None, aes_output_type, "result"); + let field1 = result.access_field(None, field1); + builder.llbb().add_assignment(None, field1, return_value); + let field2 = result.access_field(None, field2); + let ptr = builder.context.new_cast( + None, + args[0], + field2.to_rvalue().get_type().make_pointer(), + ); + builder.llbb().add_assignment(None, field2, ptr.dereference(None)); + return_value = result.to_rvalue(); + } + "__builtin_ia32_aesencwide128kl_u8" + | "__builtin_ia32_aesdecwide128kl_u8" + | "__builtin_ia32_aesencwide256kl_u8" + | "__builtin_ia32_aesdecwide256kl_u8" => { + // The builtin for aesdecwide/aesencwide writes the result in its pointer argument while + // llvm.x86.aesencwide128kl returns a value. + // We added a result pointer argument and now need to assign its value to the return_value expected by + // the LLVM intrinsic. + let (aes_output_type, field1, field2) = wide_aes_output_type(builder); + let result = builder.current_func().new_local(None, aes_output_type, "result"); + let field1 = result.access_field(None, field1); + builder.llbb().add_assignment(None, field1, return_value); + let field2 = result.access_field(None, field2); + let field2_type = field2.to_rvalue().get_type(); + let array_type = builder.context.new_array_type(None, field2_type, 8); + let ptr = builder.context.new_cast(None, args[0], array_type.make_pointer()); + let field2_ptr = + builder.context.new_cast(None, field2.get_address(None), array_type.make_pointer()); + builder.llbb().add_assignment( + None, + field2_ptr.dereference(None), + ptr.dereference(None), + ); + return_value = result.to_rvalue(); + } _ => (), } @@ -915,16 +1122,6 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.ctlz.v4i64" => "__builtin_ia32_vplzcntq_256_mask", "llvm.ctlz.v2i64" => "__builtin_ia32_vplzcntq_128_mask", "llvm.ctpop.v32i16" => "__builtin_ia32_vpopcountw_v32hi", - "llvm.x86.fma.vfmsub.sd" => "__builtin_ia32_vfmsubsd3", - "llvm.x86.fma.vfmsub.ss" => "__builtin_ia32_vfmsubss3", - "llvm.x86.fma.vfmsubadd.pd" => "__builtin_ia32_vfmaddsubpd", - "llvm.x86.fma.vfmsubadd.pd.256" => "__builtin_ia32_vfmaddsubpd256", - "llvm.x86.fma.vfmsubadd.ps" => "__builtin_ia32_vfmaddsubps", - "llvm.x86.fma.vfmsubadd.ps.256" => "__builtin_ia32_vfmaddsubps256", - "llvm.x86.fma.vfnmadd.sd" => "__builtin_ia32_vfnmaddsd3", - "llvm.x86.fma.vfnmadd.ss" => "__builtin_ia32_vfnmaddss3", - "llvm.x86.fma.vfnmsub.sd" => "__builtin_ia32_vfnmsubsd3", - "llvm.x86.fma.vfnmsub.ss" => "__builtin_ia32_vfnmsubss3", "llvm.x86.avx512.conflict.d.512" => "__builtin_ia32_vpconflictsi_512_mask", "llvm.x86.avx512.conflict.d.256" => "__builtin_ia32_vpconflictsi_256_mask", "llvm.x86.avx512.conflict.d.128" => "__builtin_ia32_vpconflictsi_128_mask", @@ -1002,8 +1199,6 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.fshr.v32i16" => "__builtin_ia32_vpshrdv_v32hi", "llvm.fshr.v16i16" => "__builtin_ia32_vpshrdv_v16hi", "llvm.fshr.v8i16" => "__builtin_ia32_vpshrdv_v8hi", - "llvm.x86.fma.vfmadd.sd" => "__builtin_ia32_vfmaddsd3", - "llvm.x86.fma.vfmadd.ss" => "__builtin_ia32_vfmaddss3", "llvm.x86.rdrand.64" => "__builtin_ia32_rdrand64_step", // The above doc points to unknown builtins for the following, so override them: @@ -1324,6 +1519,16 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512fp16.mask.vfmadd.cph.256" => "__builtin_ia32_vfmaddcph256_mask3", "llvm.x86.avx512fp16.mask.vfcmadd.cph.128" => "__builtin_ia32_vfcmaddcph128_mask3", "llvm.x86.avx512fp16.mask.vfmadd.cph.128" => "__builtin_ia32_vfmaddcph128_mask3", + "llvm.x86.encodekey128" => "__builtin_ia32_encodekey128_u32", + "llvm.x86.encodekey256" => "__builtin_ia32_encodekey256_u32", + "llvm.x86.aesenc128kl" => "__builtin_ia32_aesenc128kl_u8", + "llvm.x86.aesdec128kl" => "__builtin_ia32_aesdec128kl_u8", + "llvm.x86.aesenc256kl" => "__builtin_ia32_aesenc256kl_u8", + "llvm.x86.aesdec256kl" => "__builtin_ia32_aesdec256kl_u8", + "llvm.x86.aesencwide128kl" => "__builtin_ia32_aesencwide128kl_u8", + "llvm.x86.aesdecwide128kl" => "__builtin_ia32_aesdecwide128kl_u8", + "llvm.x86.aesencwide256kl" => "__builtin_ia32_aesencwide256kl_u8", + "llvm.x86.aesdecwide256kl" => "__builtin_ia32_aesdecwide256kl_u8", // TODO: support the tile builtins: "llvm.x86.ldtilecfg" => "__builtin_trap", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index f38622074f18b..ba65c8205a500 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -4,9 +4,7 @@ mod simd; #[cfg(feature = "master")] use std::iter; -#[cfg(feature = "master")] -use gccjit::FunctionType; -use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp}; +use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp}; #[cfg(feature = "master")] use rustc_abi::ExternAbi; use rustc_abi::{BackendRepr, HasDataLayout}; @@ -74,10 +72,47 @@ fn get_simple_intrinsic<'gcc, 'tcx>( sym::fabsf64 => "fabs", sym::minnumf32 => "fminf", sym::minnumf64 => "fmin", + sym::minimumf32 => "fminimumf", + sym::minimumf64 => "fminimum", + sym::minimumf128 => { + // GCC doesn't have the intrinsic we want so we use the compiler-builtins one + // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fminimumf128.html + let f128_type = cx.type_f128(); + return Some(cx.context.new_function( + None, + FunctionType::Extern, + f128_type, + &[ + cx.context.new_parameter(None, f128_type, "a"), + cx.context.new_parameter(None, f128_type, "b"), + ], + "fminimumf128", + false, + )); + } sym::maxnumf32 => "fmaxf", sym::maxnumf64 => "fmax", + sym::maximumf32 => "fmaximumf", + sym::maximumf64 => "fmaximum", + sym::maximumf128 => { + // GCC doesn't have the intrinsic we want so we use the compiler-builtins one + // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fmaximumf128.html + let f128_type = cx.type_f128(); + return Some(cx.context.new_function( + None, + FunctionType::Extern, + f128_type, + &[ + cx.context.new_parameter(None, f128_type, "a"), + cx.context.new_parameter(None, f128_type, "b"), + ], + "fmaximumf128", + false, + )); + } sym::copysignf32 => "copysignf", sym::copysignf64 => "copysign", + sym::copysignf128 => "copysignl", sym::floorf32 => "floorf", sym::floorf64 => "floor", sym::ceilf32 => "ceilf", @@ -95,6 +130,72 @@ fn get_simple_intrinsic<'gcc, 'tcx>( Some(cx.context.get_builtin_function(gcc_name)) } +// TODO(antoyo): We can probably remove these and use the fallback intrinsic implementation. +fn get_simple_function<'gcc, 'tcx>( + cx: &CodegenCx<'gcc, 'tcx>, + name: Symbol, +) -> Option> { + let (return_type, parameters, func_name) = match name { + sym::minimumf32 => { + let parameters = [ + cx.context.new_parameter(None, cx.float_type, "a"), + cx.context.new_parameter(None, cx.float_type, "b"), + ]; + (cx.float_type, parameters, "fminimumf") + } + sym::minimumf64 => { + let parameters = [ + cx.context.new_parameter(None, cx.double_type, "a"), + cx.context.new_parameter(None, cx.double_type, "b"), + ]; + (cx.double_type, parameters, "fminimum") + } + sym::minimumf128 => { + let f128_type = cx.type_f128(); + // GCC doesn't have the intrinsic we want so we use the compiler-builtins one + // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fminimumf128.html + let parameters = [ + cx.context.new_parameter(None, f128_type, "a"), + cx.context.new_parameter(None, f128_type, "b"), + ]; + (f128_type, parameters, "fminimumf128") + } + sym::maximumf32 => { + let parameters = [ + cx.context.new_parameter(None, cx.float_type, "a"), + cx.context.new_parameter(None, cx.float_type, "b"), + ]; + (cx.float_type, parameters, "fmaximumf") + } + sym::maximumf64 => { + let parameters = [ + cx.context.new_parameter(None, cx.double_type, "a"), + cx.context.new_parameter(None, cx.double_type, "b"), + ]; + (cx.double_type, parameters, "fmaximum") + } + sym::maximumf128 => { + let f128_type = cx.type_f128(); + // GCC doesn't have the intrinsic we want so we use the compiler-builtins one + // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fmaximumf128.html + let parameters = [ + cx.context.new_parameter(None, f128_type, "a"), + cx.context.new_parameter(None, f128_type, "b"), + ]; + (f128_type, parameters, "fmaximumf128") + } + _ => return None, + }; + Some(cx.context.new_function( + None, + FunctionType::Extern, + return_type, + ¶meters, + func_name, + false, + )) +} + impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { fn codegen_intrinsic_call( &mut self, @@ -123,6 +224,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); let simple = get_simple_intrinsic(self, name); + let simple_func = get_simple_function(self, name); // FIXME(tempdragon): Re-enable `clippy::suspicious_else_formatting` if the following issue is solved: // https://github.com/rust-lang/rust-clippy/issues/12497 @@ -130,7 +232,15 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc #[allow(clippy::suspicious_else_formatting)] let value = match name { _ if simple.is_some() => { - let func = simple.expect("simple function"); + let func = simple.expect("simple intrinsic function"); + self.cx.context.new_call( + self.location, + func, + &args.iter().map(|arg| arg.immediate()).collect::>(), + ) + } + _ if simple_func.is_some() => { + let func = simple_func.expect("simple function"); self.cx.context.new_call( self.location, func, @@ -398,7 +508,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc } // Fall back to default body - _ => return Err(Instance::new(instance.def_id(), instance.args)), + _ => return Err(Instance::new_raw(instance.def_id(), instance.args)), }; if !fn_abi.ret.is_ignore() { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 8b454ab2a4241..b897d07924914 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -399,7 +399,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( } #[cfg(feature = "master")] - if name == sym::simd_insert { + if name == sym::simd_insert || name == sym::simd_insert_dyn { require!( in_elem == arg_tys[2], InvalidMonomorphization::InsertedType { @@ -410,6 +410,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( out_ty: arg_tys[2] } ); + + // TODO(antoyo): For simd_insert, check if the index is a constant of the correct size. let vector = args[0].immediate(); let index = args[1].immediate(); let value = args[2].immediate(); @@ -422,13 +424,15 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( } #[cfg(feature = "master")] - if name == sym::simd_extract { + if name == sym::simd_extract || name == sym::simd_extract_dyn { require!( ret_ty == in_elem, InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } ); + // TODO(antoyo): For simd_extract, check if the index is a constant of the correct size. let vector = args[0].immediate(); - return Ok(bx.context.new_vector_access(None, vector, args[1].immediate()).to_rvalue()); + let index = args[1].immediate(); + return Ok(bx.context.new_vector_access(None, vector, index).to_rvalue()); } if name == sym::simd_select { @@ -443,9 +447,14 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( m_len == v_len, InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len } ); + // TODO: also support unsigned integers. match *m_elem_ty.kind() { ty::Int(_) => {} - _ => return_error!(InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }), + _ => return_error!(InvalidMonomorphization::MaskWrongElementType { + span, + name, + ty: m_elem_ty + }), } return Ok(bx.vector_select(args[0].immediate(), args[1].immediate(), args[2].immediate())); } @@ -987,19 +996,15 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( assert_eq!(pointer_count - 1, ptr_count(element_ty0)); assert_eq!(underlying_ty, non_ptr(element_ty0)); - // The element type of the third argument must be a signed integer type of any width: + // The element type of the third argument must be an integer type of any width: + // TODO: also support unsigned integers. let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx()); match *element_ty2.kind() { ty::Int(_) => (), _ => { require!( false, - InvalidMonomorphization::ThirdArgElementType { - span, - name, - expected_element: element_ty2, - third_arg: arg_tys[2] - } + InvalidMonomorphization::MaskWrongElementType { span, name, ty: element_ty2 } ); } } @@ -1105,17 +1110,13 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( assert_eq!(underlying_ty, non_ptr(element_ty0)); // The element type of the third argument must be a signed integer type of any width: + // TODO: also support unsigned integers. match *element_ty2.kind() { ty::Int(_) => (), _ => { require!( false, - InvalidMonomorphization::ThirdArgElementType { - span, - name, - expected_element: element_ty2, - third_arg: arg_tys[2] - } + InvalidMonomorphization::MaskWrongElementType { span, name, ty: element_ty2 } ); } } diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 9d91aab72ab3c..6994c385fc831 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -16,13 +16,13 @@ #![allow(internal_features)] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![feature(rustc_private, decl_macro, never_type, trusted_len, hash_raw_entry, let_chains)] +#![feature(rustc_private, decl_macro, never_type, trusted_len)] #![allow(broken_intra_doc_links)] #![recursion_limit = "256"] #![warn(rust_2018_idioms)] #![warn(unused_lifetimes)] #![deny(clippy::pattern_type_mismatch)] -#![allow(clippy::needless_lifetimes)] +#![allow(clippy::needless_lifetimes, clippy::uninlined_format_args)] // Some "regular" crates we want to share with rustc extern crate object; @@ -37,7 +37,7 @@ extern crate tracing; extern crate rustc_abi; extern crate rustc_apfloat; extern crate rustc_ast; -extern crate rustc_attr_parsing; +extern crate rustc_attr_data_structures; extern crate rustc_codegen_ssa; extern crate rustc_data_structures; extern crate rustc_errors; @@ -52,6 +52,7 @@ extern crate rustc_metadata; extern crate rustc_middle; extern crate rustc_session; extern crate rustc_span; +extern crate rustc_symbol_mangling; extern crate rustc_target; // This prevents duplicating functions and statics that are already part of the host rustc process. @@ -101,7 +102,7 @@ use rustc_codegen_ssa::back::write::{ }; use rustc_codegen_ssa::base::codegen_crate; use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods}; -use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen}; +use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::IntoDynSyncSend; use rustc_errors::DiagCtxtHandle; @@ -187,10 +188,10 @@ impl CodegenBackend for GccCodegenBackend { crate::DEFAULT_LOCALE_RESOURCE } - fn init(&self, sess: &Session) { + fn init(&self, _sess: &Session) { #[cfg(feature = "master")] { - let target_cpu = target_cpu(sess); + let target_cpu = target_cpu(_sess); // Get the second TargetInfo with the correct CPU features by setting the arch. let context = Context::default(); @@ -259,8 +260,8 @@ impl CodegenBackend for GccCodegenBackend { .join(sess) } - fn target_features_cfg(&self, sess: &Session, allow_unstable: bool) -> Vec { - target_features_cfg(sess, allow_unstable, &self.target_info) + fn target_config(&self, sess: &Session) -> TargetConfig { + target_config(sess, &self.target_info) } } @@ -453,7 +454,7 @@ impl WriteBackendMethods for GccCodegenBackend { } /// This is the entrypoint for a hot plugged rustc_codegen_gccjit -#[no_mangle] +#[unsafe(no_mangle)] pub fn __rustc_codegen_backend() -> Box { #[cfg(feature = "master")] let info = { @@ -484,37 +485,49 @@ fn to_gcc_opt_level(optlevel: Option) -> OptimizationLevel { } /// Returns the features that should be set in `cfg(target_feature)`. -fn target_features_cfg( - sess: &Session, - allow_unstable: bool, - target_info: &LockedTargetInfo, -) -> Vec { +fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig { // TODO(antoyo): use global_gcc_features. - sess.target - .rust_target_features() - .iter() - .filter_map(|&(feature, gate, _)| { - if allow_unstable - || (gate.in_cfg() && (sess.is_nightly_build() || gate.requires_nightly().is_none())) - { - Some(feature) - } else { - None - } - }) - .filter(|feature| { - // TODO: we disable Neon for now since we don't support the LLVM intrinsics for it. - if *feature == "neon" { - return false; - } - target_info.cpu_supports(feature) - /* - adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma, - avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq, - bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm, - sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves - */ - }) - .map(Symbol::intern) - .collect() + let f = |allow_unstable| { + sess.target + .rust_target_features() + .iter() + .filter_map(|&(feature, gate, _)| { + if allow_unstable + || (gate.in_cfg() + && (sess.is_nightly_build() || gate.requires_nightly().is_none())) + { + Some(feature) + } else { + None + } + }) + .filter(|feature| { + // TODO: we disable Neon for now since we don't support the LLVM intrinsics for it. + if *feature == "neon" { + return false; + } + target_info.cpu_supports(feature) + /* + adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma, + avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq, + bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm, + sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves + */ + }) + .map(Symbol::intern) + .collect() + }; + + let target_features = f(false); + let unstable_target_features = f(true); + + TargetConfig { + target_features, + unstable_target_features, + // There are no known bugs with GCC support for f16 or f128 + has_reliable_f16: true, + has_reliable_f16_math: true, + has_reliable_f128: true, + has_reliable_f128_math: true, + } } diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index cb08723431a9a..4e0a250b5509a 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -123,7 +123,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } } -impl<'gcc, 'tcx> BaseTypeCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { +impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> { fn type_i8(&self) -> Type<'gcc> { self.i8_type } diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index ae98b3d0b56e2..5745acce6fee7 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -102,10 +102,10 @@ fn uncached_gcc_type<'gcc, 'tcx>( let mut name = with_no_trimmed_paths!(layout.ty.to_string()); if let (&ty::Adt(def, _), &Variants::Single { index }) = (layout.ty.kind(), &layout.variants) + && def.is_enum() + && !def.variants().is_empty() { - if def.is_enum() && !def.variants().is_empty() { - write!(&mut name, "::{}", def.variant(index).name).unwrap(); - } + write!(&mut name, "::{}", def.variant(index).name).unwrap(); } if let (&ty::Coroutine(_, _), &Variants::Single { index }) = (layout.ty.kind(), &layout.variants) @@ -264,10 +264,10 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { } fn immediate_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> { - if let BackendRepr::Scalar(ref scalar) = self.backend_repr { - if scalar.is_bool() { - return cx.type_i1(); - } + if let BackendRepr::Scalar(ref scalar) = self.backend_repr + && scalar.is_bool() + { + return cx.type_i1(); } self.gcc_type(cx) } diff --git a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt index 082958bfe1f85..0a01a661c3577 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt @@ -1,18 +1,16 @@ tests/ui/allocator/no_std-alloc-error-handler-custom.rs tests/ui/allocator/no_std-alloc-error-handler-default.rs tests/ui/asm/may_unwind.rs -tests/ui/asm/x86_64/multiple-clobber-abi.rs tests/ui/functions-closures/parallel-codegen-closures.rs tests/ui/linkage-attr/linkage1.rs tests/ui/lto/dylib-works.rs -tests/ui/numbers-arithmetic/saturating-float-casts.rs tests/ui/sepcomp/sepcomp-cci.rs tests/ui/sepcomp/sepcomp-extern.rs tests/ui/sepcomp/sepcomp-fns-backwards.rs tests/ui/sepcomp/sepcomp-fns.rs tests/ui/sepcomp/sepcomp-statics.rs tests/ui/asm/x86_64/may_unwind.rs -tests/ui/catch-unwind-bang.rs +tests/ui/panics/catch-unwind-bang.rs tests/ui/drop/dynamic-drop-async.rs tests/ui/cfg/cfg-panic-abort.rs tests/ui/drop/repeat-drop.rs @@ -33,7 +31,6 @@ tests/ui/unwind-no-uwtable.rs tests/ui/parser/unclosed-delimiter-in-dep.rs tests/ui/consts/missing_span_in_backtrace.rs tests/ui/drop/dynamic-drop.rs -tests/ui/issues/issue-40883.rs tests/ui/issues/issue-43853.rs tests/ui/issues/issue-47364.rs tests/ui/macros/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs @@ -97,28 +94,18 @@ tests/ui/simd/intrinsic/generic-as.rs tests/ui/backtrace/backtrace.rs tests/ui/lifetimes/tail-expr-lock-poisoning.rs tests/ui/runtime/rt-explody-panic-payloads.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/function.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/basic.rs tests/ui/codegen/equal-pointers-unequal/as-cast/inline1.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/print.rs tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/print3.rs tests/ui/codegen/equal-pointers-unequal/as-cast/segfault.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline1.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print3.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/segfault.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline1.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/print3.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs tests/ui/simd/simd-bitmask-notpow2.rs +tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs +tests/ui/uninhabited/uninhabited-transparent-return-abi.rs diff --git a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs index 64c932a265819..bdcf14b4b26d4 100644 --- a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs +++ b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs @@ -1,5 +1,7 @@ //! The common code for `tests/lang_tests_*.rs` +#![allow(clippy::uninlined_format_args)] + use std::env::{self, current_dir}; use std::path::{Path, PathBuf}; use std::process::Command; @@ -40,7 +42,9 @@ pub fn main_inner(profile: Profile) { .expect("failed to get absolute path of `gcc-path`") .display() .to_string(); - env::set_var("LD_LIBRARY_PATH", gcc_path); + unsafe { + env::set_var("LD_LIBRARY_PATH", gcc_path); + } fn rust_filter(path: &Path) -> bool { path.is_file() && path.extension().expect("extension").to_str().expect("to_str") == "rs" @@ -65,15 +69,14 @@ pub fn main_inner(profile: Profile) { .test_dir("tests/run") .test_path_filter(filter) .test_extract(|path| { - let lines = std::fs::read_to_string(path) + std::fs::read_to_string(path) .expect("read file") .lines() .skip_while(|l| !l.starts_with("//")) .take_while(|l| l.starts_with("//")) .map(|l| &l[2..]) .collect::>() - .join("\n"); - lines + .join("\n") }) .test_cmds(move |path| { // Test command 1: Compile `x.rs` into `tempdir/x`. diff --git a/compiler/rustc_codegen_gcc/tests/run/abort1.rs b/compiler/rustc_codegen_gcc/tests/run/abort1.rs index fe46d9ae41849..ff2bb75ece22a 100644 --- a/compiler/rustc_codegen_gcc/tests/run/abort1.rs +++ b/compiler/rustc_codegen_gcc/tests/run/abort1.rs @@ -3,45 +3,13 @@ // Run-time: // status: signal -#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -mod intrinsics { - use super::Sized; - - #[rustc_nounwind] - #[rustc_intrinsic] - pub fn abort() -> !; -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; fn test_fail() -> ! { unsafe { intrinsics::abort() }; diff --git a/compiler/rustc_codegen_gcc/tests/run/abort2.rs b/compiler/rustc_codegen_gcc/tests/run/abort2.rs index 4123f4f4beebc..781f518e0b222 100644 --- a/compiler/rustc_codegen_gcc/tests/run/abort2.rs +++ b/compiler/rustc_codegen_gcc/tests/run/abort2.rs @@ -3,45 +3,13 @@ // Run-time: // status: signal -#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -mod intrinsics { - use super::Sized; - - #[rustc_nounwind] - #[rustc_intrinsic] - pub fn abort() -> !; -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; fn fail() -> i32 { unsafe { intrinsics::abort() }; diff --git a/compiler/rustc_codegen_gcc/tests/run/always_inline.rs b/compiler/rustc_codegen_gcc/tests/run/always_inline.rs new file mode 100644 index 0000000000000..ebd741ee090c8 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/always_inline.rs @@ -0,0 +1,53 @@ +// Compiler: +// +// Run-time: +// status: 0 + +#![feature(no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use mini_core::*; + +#[inline(always)] +fn fib(n: u8) -> u8 { + if n == 0 { + return 1; + } + if n == 1 { + return 1; + } + fib(n - 1) + fib(n - 2) +} + +#[inline(always)] +fn fib_b(n: u8) -> u8 { + if n == 0 { + return 1; + } + if n == 1 { + return 1; + } + fib_a(n - 1) + fib_a(n - 2) +} + +#[inline(always)] +fn fib_a(n: u8) -> u8 { + if n == 0 { + return 1; + } + if n == 1 { + return 1; + } + fib_b(n - 1) + fib_b(n - 2) +} + +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { + if fib(2) != fib_a(2) { + intrinsics::abort(); + } + 0 +} diff --git a/compiler/rustc_codegen_gcc/tests/run/array.rs b/compiler/rustc_codegen_gcc/tests/run/array.rs index e18a4ced6bc46..3ab0c309fdeae 100644 --- a/compiler/rustc_codegen_gcc/tests/run/array.rs +++ b/compiler/rustc_codegen_gcc/tests/run/array.rs @@ -8,20 +8,12 @@ // 10 #![feature(no_core)] - #![no_std] #![no_core] #![no_main] extern crate mini_core; - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - pub fn puts(s: *const u8) -> i32; - } -} +use mini_core::*; static mut ONE: usize = 1; diff --git a/compiler/rustc_codegen_gcc/tests/run/asm.rs b/compiler/rustc_codegen_gcc/tests/run/asm.rs index 4e05d026868e7..2dbf43be664dc 100644 --- a/compiler/rustc_codegen_gcc/tests/run/asm.rs +++ b/compiler/rustc_codegen_gcc/tests/run/asm.rs @@ -174,6 +174,59 @@ fn asm() { mem_cpy(array2.as_mut_ptr(), array1.as_ptr(), 3); } assert_eq!(array1, array2); + + // in and clobber registers cannot overlap. This tests that the lateout register without an + // output place (indicated by the `_`) is not added to the list of clobbered registers + let x = 8; + let y: i32; + unsafe { + asm!( + "mov rax, rdi", + in("rdi") x, + lateout("rdi") _, + out("rax") y, + ); + } + assert_eq!((x, y), (8, 8)); + + // sysv64 is the default calling convention on unix systems. The rdi register is + // used to pass arguments in the sysv64 calling convention, so this register will be clobbered + #[cfg(unix)] + { + let x = 16; + let y: i32; + unsafe { + asm!( + "mov rax, rdi", + in("rdi") x, + out("rax") y, + clobber_abi("sysv64"), + ); + } + assert_eq!((x, y), (16, 16)); + } + + // the `b` suffix for registers in the `reg_byte` register class is not supported in GCC + // and needs to be stripped in order to use these registers. + unsafe { + core::arch::asm!( + "", + out("al") _, + out("bl") _, + out("cl") _, + out("dl") _, + out("sil") _, + out("dil") _, + out("r8b") _, + out("r9b") _, + out("r10b") _, + out("r11b") _, + out("r12b") _, + out("r13b") _, + out("r14b") _, + out("r15b") _, + ); + } } #[cfg(not(target_arch = "x86_64"))] diff --git a/compiler/rustc_codegen_gcc/tests/run/assign.rs b/compiler/rustc_codegen_gcc/tests/run/assign.rs index 286155852d50f..4535ab5778e93 100644 --- a/compiler/rustc_codegen_gcc/tests/run/assign.rs +++ b/compiler/rustc_codegen_gcc/tests/run/assign.rs @@ -5,130 +5,13 @@ // 7 8 // 10 -#![allow(internal_features, unused_attributes)] -#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs, track_caller)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for *mut i32 {} -impl Copy for usize {} -impl Copy for u8 {} -impl Copy for i8 {} -impl Copy for i32 {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -#[lang = "panic_location"] -struct PanicLocation { - file: &'static str, - line: u32, - column: u32, -} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn puts(s: *const u8) -> i32; - pub fn fflush(stream: *mut i32) -> i32; - pub fn printf(format: *const i8, ...) -> i32; - - pub static stdout: *mut i32; - } -} - -mod intrinsics { - #[rustc_nounwind] - #[rustc_intrinsic] - pub fn abort() -> !; -} - -#[lang = "panic"] -#[track_caller] -#[no_mangle] -pub fn panic(_msg: &'static str) -> ! { - unsafe { - libc::puts("Panicking\0" as *const str as *const u8); - libc::fflush(libc::stdout); - intrinsics::abort(); - } -} - -#[lang = "add"] -trait Add { - type Output; - - fn add(self, rhs: RHS) -> Self::Output; -} - -impl Add for u8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i32 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for usize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for isize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -#[track_caller] -#[lang = "panic_const_add_overflow"] -pub fn panic_const_add_overflow() -> ! { - panic("attempt to add with overflow"); -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; fn inc_ref(num: &mut isize) -> isize { *num = *num + 5; @@ -139,9 +22,8 @@ fn inc(num: isize) -> isize { num + 1 } - #[no_mangle] -extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { +extern "C" fn main(mut argc: isize, _argv: *const *const u8) -> i32 { argc = inc(argc); unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc); diff --git a/compiler/rustc_codegen_gcc/tests/run/closure.rs b/compiler/rustc_codegen_gcc/tests/run/closure.rs index c7a236f74f9e6..a8a3fadfed47b 100644 --- a/compiler/rustc_codegen_gcc/tests/run/closure.rs +++ b/compiler/rustc_codegen_gcc/tests/run/closure.rs @@ -9,55 +9,38 @@ // Both args: 11 #![feature(no_core)] - #![no_std] #![no_core] #![no_main] extern crate mini_core; - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} +use mini_core::*; #[no_mangle] -extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { +extern "C" fn main(argc: isize, _argv: *const *const u8) -> i32 { let string = "Arg: %d\n\0"; - let mut closure = || { - unsafe { - libc::printf(string as *const str as *const i8, argc); - } + let mut closure = || unsafe { + libc::printf(string as *const str as *const i8, argc); }; closure(); - let mut closure = || { - unsafe { - libc::printf("Argument: %d\n\0" as *const str as *const i8, argc); - } + let mut closure = || unsafe { + libc::printf("Argument: %d\n\0" as *const str as *const i8, argc); }; closure(); - let mut closure = |string| { - unsafe { - libc::printf(string as *const str as *const i8, argc); - } + let mut closure = |string| unsafe { + libc::printf(string as *const str as *const i8, argc); }; closure("String arg: %d\n\0"); - let mut closure = |arg: isize| { - unsafe { - libc::printf("Int argument: %d\n\0" as *const str as *const i8, arg); - } + let mut closure = |arg: isize| unsafe { + libc::printf("Int argument: %d\n\0" as *const str as *const i8, arg); }; closure(argc + 1); - let mut closure = |string, arg: isize| { - unsafe { - libc::printf(string as *const str as *const i8, arg); - } + let mut closure = |string, arg: isize| unsafe { + libc::printf(string as *const str as *const i8, arg); }; closure("Both args: %d\n\0", argc + 10); diff --git a/compiler/rustc_codegen_gcc/tests/run/condition.rs b/compiler/rustc_codegen_gcc/tests/run/condition.rs index b02359702ed2e..bd3b6f7497fbd 100644 --- a/compiler/rustc_codegen_gcc/tests/run/condition.rs +++ b/compiler/rustc_codegen_gcc/tests/run/condition.rs @@ -6,19 +6,12 @@ // 1 #![feature(no_core)] - #![no_std] #![no_core] #![no_main] extern crate mini_core; - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} +use mini_core::*; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { @@ -27,15 +20,14 @@ extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { libc::printf(b"true\n\0" as *const u8 as *const i8); } - let string = - match argc { - 1 => b"1\n\0", - 2 => b"2\n\0", - 3 => b"3\n\0", - 4 => b"4\n\0", - 5 => b"5\n\0", - _ => b"_\n\0", - }; + let string = match argc { + 1 => b"1\n\0", + 2 => b"2\n\0", + 3 => b"3\n\0", + 4 => b"4\n\0", + 5 => b"5\n\0", + _ => b"_\n\0", + }; libc::printf(string as *const u8 as *const i8); } 0 diff --git a/compiler/rustc_codegen_gcc/tests/run/empty_main.rs b/compiler/rustc_codegen_gcc/tests/run/empty_main.rs index 042e44080c53a..fe3df5a2389ca 100644 --- a/compiler/rustc_codegen_gcc/tests/run/empty_main.rs +++ b/compiler/rustc_codegen_gcc/tests/run/empty_main.rs @@ -3,37 +3,13 @@ // Run-time: // status: 0 -#![feature(auto_traits, lang_items, no_core)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { diff --git a/compiler/rustc_codegen_gcc/tests/run/exit.rs b/compiler/rustc_codegen_gcc/tests/run/exit.rs index 9a7c91c0adb20..e0a59174bd383 100644 --- a/compiler/rustc_codegen_gcc/tests/run/exit.rs +++ b/compiler/rustc_codegen_gcc/tests/run/exit.rs @@ -3,44 +3,13 @@ // Run-time: // status: 2 -#![feature(auto_traits, lang_items, no_core, intrinsics)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -mod libc { - #[link(name = "c")] - extern "C" { - pub fn exit(status: i32); - } -} - -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { diff --git a/compiler/rustc_codegen_gcc/tests/run/exit_code.rs b/compiler/rustc_codegen_gcc/tests/run/exit_code.rs index c50d2b0d7107c..376824da845c3 100644 --- a/compiler/rustc_codegen_gcc/tests/run/exit_code.rs +++ b/compiler/rustc_codegen_gcc/tests/run/exit_code.rs @@ -3,37 +3,13 @@ // Run-time: // status: 1 -#![feature(auto_traits, lang_items, no_core)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { diff --git a/compiler/rustc_codegen_gcc/tests/run/float.rs b/compiler/rustc_codegen_gcc/tests/run/float.rs new file mode 100644 index 0000000000000..424fa1cf4ad53 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/float.rs @@ -0,0 +1,28 @@ +// Compiler: +// +// Run-time: +// status: 0 + +#![feature(const_black_box)] + +fn main() { + use std::hint::black_box; + + macro_rules! check { + ($ty:ty, $expr:expr) => {{ + const EXPECTED: $ty = $expr; + assert_eq!($expr, EXPECTED); + }}; + } + + check!(i32, (black_box(0.0f32) as i32)); + + check!(u64, (black_box(f32::NAN) as u64)); + check!(u128, (black_box(f32::NAN) as u128)); + + check!(i64, (black_box(f64::NAN) as i64)); + check!(u64, (black_box(f64::NAN) as u64)); + + check!(i16, (black_box(f32::MIN) as i16)); + check!(i16, (black_box(f32::MAX) as i16)); +} diff --git a/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs b/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs index 98b351e504495..93b9baee1b24a 100644 --- a/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs +++ b/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs @@ -5,19 +5,12 @@ // stdout: 1 #![feature(no_core)] - #![no_std] #![no_core] #![no_main] extern crate mini_core; - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} +use mini_core::*; fn i16_as_i8(a: i16) -> i8 { a as i8 diff --git a/compiler/rustc_codegen_gcc/tests/run/int.rs b/compiler/rustc_codegen_gcc/tests/run/int.rs index 58a26801b678c..47b5dea46f8de 100644 --- a/compiler/rustc_codegen_gcc/tests/run/int.rs +++ b/compiler/rustc_codegen_gcc/tests/run/int.rs @@ -3,9 +3,7 @@ // Run-time: // status: 0 -/* - * Code - */ +#![feature(const_black_box)] fn main() { use std::hint::black_box; diff --git a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs index b0215860406e5..fa50d5bc5d3d3 100644 --- a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs +++ b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs @@ -1,4 +1,3 @@ - // Compiler: // // Run-time: @@ -7,139 +6,20 @@ // 6 // 11 -#![allow(internal_features, unused_attributes)] -#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs, track_caller)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for *mut i32 {} -impl Copy for usize {} -impl Copy for u8 {} -impl Copy for i8 {} -impl Copy for i32 {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -#[lang = "panic_location"] -struct PanicLocation { - file: &'static str, - line: u32, - column: u32, -} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn puts(s: *const u8) -> i32; - pub fn fflush(stream: *mut i32) -> i32; - pub fn printf(format: *const i8, ...) -> i32; - - pub static stdout: *mut i32; - } -} - -mod intrinsics { - #[rustc_nounwind] - #[rustc_intrinsic] - pub fn abort() -> !; -} - -#[lang = "panic"] -#[track_caller] -#[no_mangle] -pub fn panic(_msg: &'static str) -> ! { - unsafe { - libc::puts("Panicking\0" as *const str as *const u8); - libc::fflush(libc::stdout); - intrinsics::abort(); - } -} - -#[lang = "add"] -trait Add { - type Output; - - fn add(self, rhs: RHS) -> Self::Output; -} - -impl Add for u8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i32 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for usize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for isize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -#[track_caller] -#[lang = "panic_const_add_overflow"] -pub fn panic_const_add_overflow() -> ! { - panic("attempt to add with overflow"); -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; struct Test { field: isize, } fn test(num: isize) -> Test { - Test { - field: num + 1, - } + Test { field: num + 1 } } fn update_num(num: &mut isize) { @@ -147,7 +27,7 @@ fn update_num(num: &mut isize) { } #[no_mangle] -extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { +extern "C" fn main(mut argc: isize, _argv: *const *const u8) -> i32 { let mut test = test(argc); unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field); diff --git a/compiler/rustc_codegen_gcc/tests/run/operations.rs b/compiler/rustc_codegen_gcc/tests/run/operations.rs index 8ba7a4c5ed8ce..a1b0772f76b62 100644 --- a/compiler/rustc_codegen_gcc/tests/run/operations.rs +++ b/compiler/rustc_codegen_gcc/tests/run/operations.rs @@ -5,229 +5,13 @@ // 39 // 10 -#![allow(internal_features, unused_attributes)] -#![feature(auto_traits, lang_items, no_core, intrinsics, arbitrary_self_types, rustc_attrs)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for *mut i32 {} -impl Copy for usize {} -impl Copy for u8 {} -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} - -#[lang = "deref"] -pub trait Deref { - type Target: ?Sized; - - fn deref(&self) -> &Self::Target; -} - -#[lang = "legacy_receiver"] -trait LegacyReceiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -#[lang = "panic_location"] -struct PanicLocation { - file: &'static str, - line: u32, - column: u32, -} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - pub fn puts(s: *const u8) -> i32; - pub fn fflush(stream: *mut i32) -> i32; - - pub static stdout: *mut i32; - } -} - -mod intrinsics { - #[rustc_nounwind] - #[rustc_intrinsic] - pub fn abort() -> !; -} - -#[lang = "panic"] -#[track_caller] -#[no_mangle] -pub fn panic(_msg: &'static str) -> ! { - unsafe { - libc::puts("Panicking\0" as *const str as *const u8); - libc::fflush(libc::stdout); - intrinsics::abort(); - } -} - -#[lang = "add"] -trait Add { - type Output; - - fn add(self, rhs: RHS) -> Self::Output; -} - -impl Add for u8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i32 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for usize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for isize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -#[lang = "sub"] -pub trait Sub { - type Output; - - fn sub(self, rhs: RHS) -> Self::Output; -} - -impl Sub for usize { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for isize { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for u8 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for i8 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for i16 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -#[lang = "mul"] -pub trait Mul { - type Output; - - #[must_use] - fn mul(self, rhs: RHS) -> Self::Output; -} - -impl Mul for u8 { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - self * rhs - } -} - -impl Mul for usize { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - self * rhs - } -} - -impl Mul for isize { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - self * rhs - } -} - -#[track_caller] -#[lang = "panic_const_add_overflow"] -pub fn panic_const_add_overflow() -> ! { - panic("attempt to add with overflow"); -} - -#[track_caller] -#[lang = "panic_const_sub_overflow"] -pub fn panic_const_sub_overflow() -> ! { - panic("attempt to subtract with overflow"); -} - -#[track_caller] -#[lang = "panic_const_mul_overflow"] -pub fn panic_const_mul_overflow() -> ! { - panic("attempt to multiply with overflow"); -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { diff --git a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs index 0ba49e7187fca..e627886a9d575 100644 --- a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs +++ b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs @@ -2,32 +2,36 @@ // // Run-time: // status: 0 -// stdout: 1 +// stdout: 10 +// 10 +// 42 +// 1 #![feature(no_core)] - #![no_std] #![no_core] #![no_main] extern crate mini_core; +use mini_core::*; -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } +fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u32) { + ( + a as u8, a as u16, a as u32, a as usize, a as i8, a as i16, a as i32, a as isize, b as u8, + b as u32, + ) } static mut ONE: usize = 1; -fn make_array() -> [u8; 3] { - [42, 10, 5] -} - #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { + let (a, b, c, d, e, f, g, h, i, j) = int_cast(10, 42); unsafe { + libc::printf(b"%d\n\0" as *const u8 as *const i8, c); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, d); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, j); + let ptr = ONE as *mut usize; let value = ptr as usize; libc::printf(b"%ld\n\0" as *const u8 as *const i8, value); diff --git a/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs b/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs index 3cc1e274001e7..c1254c51ce91d 100644 --- a/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs +++ b/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs @@ -6,54 +6,13 @@ // 10 // 42 -#![feature(auto_traits, lang_items, no_core, intrinsics)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -#[lang = "copy"] -pub unsafe trait Copy {} - -impl Copy for bool {} -impl Copy for u8 {} -impl Copy for u16 {} -impl Copy for u32 {} -impl Copy for u64 {} -impl Copy for usize {} -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for isize {} -impl Copy for f32 {} -impl Copy for char {} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} - -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "legacy_receiver"] -trait LegacyReceiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u32) { ( diff --git a/compiler/rustc_codegen_gcc/tests/run/slice.rs b/compiler/rustc_codegen_gcc/tests/run/slice.rs index 825fcb8a081e7..449ccabef7fe3 100644 --- a/compiler/rustc_codegen_gcc/tests/run/slice.rs +++ b/compiler/rustc_codegen_gcc/tests/run/slice.rs @@ -5,26 +5,17 @@ // stdout: 5 #![feature(no_core)] - #![no_std] #![no_core] #![no_main] extern crate mini_core; - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} +use mini_core::*; static mut TWO: usize = 2; fn index_slice(s: &[u32]) -> u32 { - unsafe { - s[TWO] - } + unsafe { s[TWO] } } #[no_mangle] diff --git a/compiler/rustc_codegen_gcc/tests/run/static.rs b/compiler/rustc_codegen_gcc/tests/run/static.rs index c3c8121b1e195..1e36cf4f3d316 100644 --- a/compiler/rustc_codegen_gcc/tests/run/static.rs +++ b/compiler/rustc_codegen_gcc/tests/run/static.rs @@ -9,70 +9,13 @@ // 12 // 1 -#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "destruct"] -pub trait Destruct {} - -#[lang = "drop"] -pub trait Drop {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for *mut T {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -mod intrinsics { - use super::Sized; - - #[rustc_nounwind] - #[rustc_intrinsic] - pub fn abort() -> !; -} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} - -#[lang = "structural_peq"] -pub trait StructuralPartialEq {} - -#[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. - drop_in_place(to_drop); -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; struct Test { field: isize, @@ -84,20 +27,14 @@ struct WithRef { static mut CONSTANT: isize = 10; -static mut TEST: Test = Test { - field: 12, -}; +static mut TEST: Test = Test { field: 12 }; -static mut TEST2: Test = Test { - field: 14, -}; +static mut TEST2: Test = Test { field: 14 }; -static mut WITH_REF: WithRef = WithRef { - refe: unsafe { &TEST }, -}; +static mut WITH_REF: WithRef = WithRef { refe: unsafe { &TEST } }; #[no_mangle] -extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { +extern "C" fn main(argc: isize, _argv: *const *const u8) -> i32 { unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, CONSTANT); libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST2.field); diff --git a/compiler/rustc_codegen_gcc/tests/run/structs.rs b/compiler/rustc_codegen_gcc/tests/run/structs.rs index 59b8f358863f2..da73cbed9ae97 100644 --- a/compiler/rustc_codegen_gcc/tests/run/structs.rs +++ b/compiler/rustc_codegen_gcc/tests/run/structs.rs @@ -5,44 +5,13 @@ // stdout: 1 // 2 -#![feature(auto_traits, lang_items, no_core, intrinsics)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; struct Test { field: isize, diff --git a/compiler/rustc_codegen_gcc/tests/run/switchint_128bit.rs b/compiler/rustc_codegen_gcc/tests/run/switchint_128bit.rs new file mode 100644 index 0000000000000..decae5bfcd78d --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/switchint_128bit.rs @@ -0,0 +1,37 @@ +// Compiler: +// +// Run-time: +// status: 0 + +#![feature(no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use intrinsics::black_box; +use mini_core::*; + +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { + // 1st. Check that small 128 bit values work. + let val = black_box(64_u128); + match val { + 0 => return 1, + 1 => return 2, + 64 => (), + _ => return 3, + } + // 2nd check that *large* values work. + const BIG: u128 = 0xDEAD_C0FE_BEEF_DECAF_BADD_DECAF_BEEF_u128; + let val = black_box(BIG); + match val { + 0 => return 4, + 1 => return 5, + // Check that we will not match on the lower u64, if the upper qword is different! + 0xcafbadddecafbeef => return 6, + 0xDEAD_C0FE_BEEF_DECAF_BADD_DECAF_BEEF_u128 => (), + _ => return 7, + } + 0 +} diff --git a/compiler/rustc_codegen_gcc/tests/run/tuple.rs b/compiler/rustc_codegen_gcc/tests/run/tuple.rs index ed60a56a68c4c..e0f2e95f6289b 100644 --- a/compiler/rustc_codegen_gcc/tests/run/tuple.rs +++ b/compiler/rustc_codegen_gcc/tests/run/tuple.rs @@ -4,44 +4,13 @@ // status: 0 // stdout: 3 -#![feature(auto_traits, lang_items, no_core, intrinsics)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { diff --git a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py index 8efed3e43af85..181f1e501a40a 100644 --- a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py +++ b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py @@ -12,7 +12,7 @@ def run_command(command, cwd=None): sys.exit(1) -def clone_repository(repo_name, path, repo_url, sub_paths=None): +def clone_repository(repo_name, path, repo_url, branch="master", sub_paths=None): if os.path.exists(path): while True: choice = input("There is already a `{}` folder, do you want to update it? [y/N]".format(path)) @@ -21,7 +21,7 @@ def clone_repository(repo_name, path, repo_url, sub_paths=None): return elif choice.lower() == "y": print("Updating repository...") - run_command(["git", "pull", "origin"], cwd=path) + run_command(["git", "pull", "origin", branch], cwd=path) return else: print("Didn't understand answer...") @@ -209,6 +209,7 @@ def main(): "llvm-project", llvm_path, "https://github.com/llvm/llvm-project", + branch="main", sub_paths=["llvm/include/llvm/IR", "llvm/include/llvm/CodeGen/"], ) clone_repository( diff --git a/compiler/rustc_codegen_gcc/triagebot.toml b/compiler/rustc_codegen_gcc/triagebot.toml new file mode 100644 index 0000000000000..13da0a87def3c --- /dev/null +++ b/compiler/rustc_codegen_gcc/triagebot.toml @@ -0,0 +1,7 @@ +# Documentation at https://forge.rust-lang.org/triagebot/index.html + +# Prevents un-canonicalized issue links (to avoid wrong issues being linked in r-l/rust) +[issue-links] + +# Prevents mentions in commits to avoid users being spammed +[no-mentions] diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index d3ce7c5a1130f..bf8ec8c3b9158 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -11,15 +11,15 @@ test = false bitflags = "2.4.1" # To avoid duplicate dependencies, this should match the version of gimli used # by `rustc_codegen_ssa` via its `thorin-dwp` dependency. -gimli = "0.30" +gimli = "0.31" itertools = "0.12" libc = "0.2" -measureme = "11" +measureme = "12.0.1" object = { version = "0.36.3", default-features = false, features = ["std", "read"] } rustc-demangle = "0.1.21" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index 17f2e7ca9f702..41391b096cca8 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -56,6 +56,8 @@ codegen_llvm_prepare_thin_lto_module_with_llvm_err = failed to prepare thin LTO codegen_llvm_run_passes = failed to run LLVM passes codegen_llvm_run_passes_with_llvm_err = failed to run LLVM passes: {$llvm_err} +codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0 = `-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later. + codegen_llvm_sanitizer_memtag_requires_mte = `-Zsanitizer=memtag` requires `-Ctarget-feature=+mte` diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 8c75125e009b9..8294e29d07df6 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -17,14 +17,13 @@ use rustc_target::callconv::{ use rustc_target::spec::SanitizerSet; use smallvec::SmallVec; -use crate::attributes::llfn_attrs_from_instance; +use crate::attributes::{self, llfn_attrs_from_instance}; use crate::builder::Builder; use crate::context::CodegenCx; use crate::llvm::{self, Attribute, AttributePlace}; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use crate::{attributes, llvm_util}; trait ArgAttributesExt { fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value); @@ -437,7 +436,6 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { let apply_range_attr = |idx: AttributePlace, scalar: rustc_abi::Scalar| { if cx.sess().opts.optimize != config::OptLevel::No - && llvm_util::get_version() >= (19, 0, 0) && matches!(scalar.primitive(), Primitive::Int(..)) // If the value is a boolean, the range is 0..2 and that ultimately // become 0..0 when the type becomes i1, which would be rejected @@ -571,19 +569,6 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } _ => {} } - if bx.cx.sess().opts.optimize != config::OptLevel::No - && llvm_util::get_version() < (19, 0, 0) - && let BackendRepr::Scalar(scalar) = self.ret.layout.backend_repr - && matches!(scalar.primitive(), Primitive::Int(..)) - // If the value is a boolean, the range is 0..2 and that ultimately - // become 0..0 when the type becomes i1, which would be rejected - // by the LLVM verifier. - && !scalar.is_bool() - // LLVM also rejects full range. - && !scalar.is_always_valid(bx) - { - bx.range_metadata(callsite, scalar.valid_range(bx)); - } for arg in self.args.iter() { match &arg.mode { PassMode::Ignore => {} @@ -654,7 +639,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } } -impl<'tcx> AbiBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { +impl AbiBuilderMethods for Builder<'_, '_, '_> { fn get_param(&mut self, index: usize) -> Self::Value { llvm::get_param(self.llfn(), index as c_uint) } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 66723cbf88206..4a78e69497994 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -3,33 +3,32 @@ use rustc_ast::expand::allocator::{ ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, alloc_error_handler_name, default_fn_name, global_fn_name, }; +use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, OomStrategy}; +use rustc_symbol_mangling::mangle_internal_symbol; -use crate::common::AsCCharPtr; -use crate::llvm::{self, Context, False, Module, True, Type}; -use crate::{ModuleLlvm, attributes, debuginfo}; +use crate::builder::SBuilder; +use crate::declare::declare_simple_fn; +use crate::llvm::{self, False, True, Type}; +use crate::{SimpleCx, attributes, debuginfo}; pub(crate) unsafe fn codegen( tcx: TyCtxt<'_>, - module_llvm: &mut ModuleLlvm, + cx: SimpleCx<'_>, module_name: &str, kind: AllocatorKind, alloc_error_handler_kind: AllocatorKind, ) { - let llcx = &*module_llvm.llcx; - let llmod = module_llvm.llmod(); - let usize = unsafe { - match tcx.sess.target.pointer_width { - 16 => llvm::LLVMInt16TypeInContext(llcx), - 32 => llvm::LLVMInt32TypeInContext(llcx), - 64 => llvm::LLVMInt64TypeInContext(llcx), - tws => bug!("Unsupported target word size for int: {}", tws), - } + let usize = match tcx.sess.target.pointer_width { + 16 => cx.type_i16(), + 32 => cx.type_i32(), + 64 => cx.type_i64(), + tws => bug!("Unsupported target word size for int: {}", tws), }; - let i8 = unsafe { llvm::LLVMInt8TypeInContext(llcx) }; - let i8p = unsafe { llvm::LLVMPointerTypeInContext(llcx, 0) }; + let i8 = cx.type_i8(); + let i8p = cx.type_ptr(); if kind == AllocatorKind::Default { for method in ALLOCATOR_METHODS { @@ -55,20 +54,19 @@ pub(crate) unsafe fn codegen( } }; - let from_name = global_fn_name(method.name); - let to_name = default_fn_name(method.name); + let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); + let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); - create_wrapper_function(tcx, llcx, llmod, &from_name, &to_name, &args, output, false); + create_wrapper_function(tcx, &cx, &from_name, &to_name, &args, output, false); } } // rust alloc error handler create_wrapper_function( tcx, - llcx, - llmod, - "__rust_alloc_error_handler", - alloc_error_handler_name(alloc_error_handler_kind), + &cx, + &mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), + &mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind)), &[usize, usize], // size, align None, true, @@ -76,22 +74,22 @@ pub(crate) unsafe fn codegen( unsafe { // __rust_alloc_error_handler_should_panic - let name = OomStrategy::SYMBOL; - let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_c_char_ptr(), name.len(), i8); + let name = mangle_internal_symbol(tcx, OomStrategy::SYMBOL); + let ll_g = cx.declare_global(&name, i8); llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility())); let val = tcx.sess.opts.unstable_opts.oom.should_panic(); let llval = llvm::LLVMConstInt(i8, val as u64, False); llvm::set_initializer(ll_g, llval); - let name = NO_ALLOC_SHIM_IS_UNSTABLE; - let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_c_char_ptr(), name.len(), i8); + let name = mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE); + let ll_g = cx.declare_global(&name, i8); llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility())); let llval = llvm::LLVMConstInt(i8, 0, False); llvm::set_initializer(ll_g, llval); } if tcx.sess.opts.debuginfo != DebugInfo::None { - let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod); + let dbg_cx = debuginfo::CodegenUnitDebugContext::new(cx.llmod); debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx); dbg_cx.finalize(tcx.sess); } @@ -99,77 +97,64 @@ pub(crate) unsafe fn codegen( fn create_wrapper_function( tcx: TyCtxt<'_>, - llcx: &Context, - llmod: &Module, + cx: &SimpleCx<'_>, from_name: &str, to_name: &str, args: &[&Type], output: Option<&Type>, no_return: bool, ) { - unsafe { - let ty = llvm::LLVMFunctionType( - output.unwrap_or_else(|| llvm::LLVMVoidTypeInContext(llcx)), - args.as_ptr(), - args.len() as c_uint, - False, - ); - let llfn = llvm::LLVMRustGetOrInsertFunction( - llmod, - from_name.as_c_char_ptr(), - from_name.len(), - ty, - ); - let no_return = if no_return { - // -> ! DIFlagNoReturn - let no_return = llvm::AttributeKind::NoReturn.create_attr(llcx); - attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[no_return]); - Some(no_return) - } else { - None - }; - - llvm::set_visibility(llfn, llvm::Visibility::from_generic(tcx.sess.default_visibility())); - - if tcx.sess.must_emit_unwind_tables() { - let uwtable = - attributes::uwtable_attr(llcx, tcx.sess.opts.unstable_opts.use_sync_unwind); - attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]); - } + let ty = cx.type_func(args, output.unwrap_or_else(|| cx.type_void())); + let llfn = declare_simple_fn( + &cx, + from_name, + llvm::CallConv::CCallConv, + llvm::UnnamedAddr::Global, + llvm::Visibility::from_generic(tcx.sess.default_visibility()), + ty, + ); + let no_return = if no_return { + // -> ! DIFlagNoReturn + let no_return = llvm::AttributeKind::NoReturn.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[no_return]); + Some(no_return) + } else { + None + }; - let callee = - llvm::LLVMRustGetOrInsertFunction(llmod, to_name.as_c_char_ptr(), to_name.len(), ty); - if let Some(no_return) = no_return { - // -> ! DIFlagNoReturn - attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]); - } - llvm::set_visibility(callee, llvm::Visibility::Hidden); - - let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, c"entry".as_ptr()); - - let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); - llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); - let args = args - .iter() - .enumerate() - .map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint)) - .collect::>(); - let ret = llvm::LLVMBuildCallWithOperandBundles( - llbuilder, - ty, - callee, - args.as_ptr(), - args.len() as c_uint, - [].as_ptr(), - 0 as c_uint, - c"".as_ptr(), - ); - llvm::LLVMSetTailCall(ret, True); - if output.is_some() { - llvm::LLVMBuildRet(llbuilder, ret); - } else { - llvm::LLVMBuildRetVoid(llbuilder); - } - llvm::LLVMDisposeBuilder(llbuilder); + if tcx.sess.must_emit_unwind_tables() { + let uwtable = + attributes::uwtable_attr(cx.llcx, tcx.sess.opts.unstable_opts.use_sync_unwind); + attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]); + } + + let callee = declare_simple_fn( + &cx, + to_name, + llvm::CallConv::CCallConv, + llvm::UnnamedAddr::Global, + llvm::Visibility::Hidden, + ty, + ); + if let Some(no_return) = no_return { + // -> ! DIFlagNoReturn + attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]); + } + llvm::set_visibility(callee, llvm::Visibility::Hidden); + + let llbb = unsafe { llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, c"entry".as_ptr()) }; + + let mut bx = SBuilder::build(&cx, llbb); + let args = args + .iter() + .enumerate() + .map(|(i, _)| llvm::get_param(llfn, i as c_uint)) + .collect::>(); + let ret = bx.call(ty, callee, &args, None); + llvm::LLVMSetTailCall(ret, True); + if output.is_some() { + bx.ret(ret); + } else { + bx.ret_void() } } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index e8a697431571f..9e3893d5314ae 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -1,6 +1,5 @@ use std::assert_matches::assert_matches; -use libc::{c_char, c_uint}; use rustc_abi::{BackendRepr, Float, Integer, Primitive, Scalar}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::mir::operand::OperandValue; @@ -15,7 +14,7 @@ use smallvec::SmallVec; use tracing::debug; use crate::builder::Builder; -use crate::common::{AsCCharPtr, Funclet}; +use crate::common::Funclet; use crate::context::CodegenCx; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; @@ -377,7 +376,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { fn codegen_global_asm( - &self, + &mut self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, @@ -436,13 +435,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { template_str.push_str("\n.att_syntax\n"); } - unsafe { - llvm::LLVMAppendModuleInlineAsm( - self.llmod, - template_str.as_c_char_ptr(), - template_str.len(), - ); - } + llvm::append_module_inline_asm(self.llmod, template_str.as_bytes()); } fn mangled_name(&self, instance: Instance<'tcx>) -> String { @@ -483,70 +476,67 @@ pub(crate) fn inline_asm_call<'ll>( debug!("Asm Output Type: {:?}", output); let fty = bx.cx.type_func(&argtys, output); - unsafe { - // Ask LLVM to verify that the constraints are well-formed. - let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons.as_c_char_ptr(), cons.len()); - debug!("constraint verification result: {:?}", constraints_ok); - if constraints_ok { - let v = llvm::LLVMRustInlineAsm( - fty, - asm.as_c_char_ptr(), - asm.len(), - cons.as_c_char_ptr(), - cons.len(), - volatile, - alignstack, - dia, - can_throw, - ); - let call = if !labels.is_empty() { - assert!(catch_funclet.is_none()); - bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None, None) - } else if let Some((catch, funclet)) = catch_funclet { - bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet, None) - } else { - bx.call(fty, None, None, v, inputs, None, None) - }; + // Ask LLVM to verify that the constraints are well-formed. + let constraints_ok = unsafe { llvm::LLVMRustInlineAsmVerify(fty, cons.as_ptr(), cons.len()) }; + debug!("constraint verification result: {:?}", constraints_ok); + if !constraints_ok { + // LLVM has detected an issue with our constraints, so bail out. + return None; + } - // Store mark in a metadata node so we can map LLVM errors - // back to source locations. See #17552. - let key = "srcloc"; - let kind = llvm::LLVMGetMDKindIDInContext( - bx.llcx, - key.as_ptr().cast::(), - key.len() as c_uint, - ); + let v = unsafe { + llvm::LLVMGetInlineAsm( + fty, + asm.as_ptr(), + asm.len(), + cons.as_ptr(), + cons.len(), + volatile, + alignstack, + dia, + can_throw, + ) + }; - // `srcloc` contains one 64-bit integer for each line of assembly code, - // where the lower 32 bits hold the lo byte position and the upper 32 bits - // hold the hi byte position. - let mut srcloc = vec![]; - if dia == llvm::AsmDialect::Intel && line_spans.len() > 1 { - // LLVM inserts an extra line to add the ".intel_syntax", so add - // a dummy srcloc entry for it. - // - // Don't do this if we only have 1 line span since that may be - // due to the asm template string coming from a macro. LLVM will - // default to the first srcloc for lines that don't have an - // associated srcloc. - srcloc.push(llvm::LLVMValueAsMetadata(bx.const_u64(0))); - } - srcloc.extend(line_spans.iter().map(|span| { - llvm::LLVMValueAsMetadata(bx.const_u64( - u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32), - )) - })); - let md = llvm::LLVMMDNodeInContext2(bx.llcx, srcloc.as_ptr(), srcloc.len()); - let md = llvm::LLVMMetadataAsValue(&bx.llcx, md); - llvm::LLVMSetMetadata(call, kind, md); + let call = if !labels.is_empty() { + assert!(catch_funclet.is_none()); + bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None, None) + } else if let Some((catch, funclet)) = catch_funclet { + bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet, None) + } else { + bx.call(fty, None, None, v, inputs, None, None) + }; - Some(call) - } else { - // LLVM has detected an issue with our constraints, bail out - None - } + // Store mark in a metadata node so we can map LLVM errors + // back to source locations. See #17552. + let key = "srcloc"; + let kind = bx.get_md_kind_id(key); + + // `srcloc` contains one 64-bit integer for each line of assembly code, + // where the lower 32 bits hold the lo byte position and the upper 32 bits + // hold the hi byte position. + let mut srcloc = vec![]; + if dia == llvm::AsmDialect::Intel && line_spans.len() > 1 { + // LLVM inserts an extra line to add the ".intel_syntax", so add + // a dummy srcloc entry for it. + // + // Don't do this if we only have 1 line span since that may be + // due to the asm template string coming from a macro. LLVM will + // default to the first srcloc for lines that don't have an + // associated srcloc. + srcloc.push(llvm::LLVMValueAsMetadata(bx.const_u64(0))); } + srcloc.extend(line_spans.iter().map(|span| { + llvm::LLVMValueAsMetadata( + bx.const_u64(u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32)), + ) + })); + let md = unsafe { llvm::LLVMMDNodeInContext2(bx.llcx, srcloc.as_ptr(), srcloc.len()) }; + let md = bx.get_metadata_value(md); + llvm::LLVMSetMetadata(call, kind, md); + + Some(call) } /// If the register is an xmm/ymm/zmm register then return its index. diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 3d7afa17bdf3d..443c2eace554d 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -1,6 +1,5 @@ //! Set and unset common attributes on LLVM values. - -use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_attr_data_structures::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_codegen_ssa::traits::*; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry}; @@ -28,6 +27,22 @@ pub(crate) fn apply_to_callsite(callsite: &Value, idx: AttributePlace, attrs: &[ } } +pub(crate) fn has_attr(llfn: &Value, idx: AttributePlace, attr: AttributeKind) -> bool { + llvm::HasAttributeAtIndex(llfn, idx, attr) +} + +pub(crate) fn has_string_attr(llfn: &Value, name: &str) -> bool { + llvm::HasStringAttribute(llfn, name) +} + +pub(crate) fn remove_from_llfn(llfn: &Value, place: AttributePlace, kind: AttributeKind) { + llvm::RemoveRustEnumAttributeAtIndex(llfn, place, kind); +} + +pub(crate) fn remove_string_attr_from_llfn(llfn: &Value, name: &str) { + llvm::RemoveStringAttrFromFn(llfn, name); +} + /// Get LLVM attribute for the provided inline heuristic. #[inline] fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll Attribute> { @@ -407,30 +422,28 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( // Do not set sanitizer attributes for naked functions. to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); - if llvm_util::get_version() >= (19, 0, 0) { - // For non-naked functions, set branch protection attributes on aarch64. - if let Some(BranchProtection { bti, pac_ret }) = - cx.sess().opts.unstable_opts.branch_protection - { - assert!(cx.sess().target.arch == "aarch64"); - if bti { - to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement")); - } - if let Some(PacRet { leaf, pc, key }) = pac_ret { - if pc { - to_add.push(llvm::CreateAttrString(cx.llcx, "branch-protection-pauth-lr")); - } - to_add.push(llvm::CreateAttrStringValue( - cx.llcx, - "sign-return-address", - if leaf { "all" } else { "non-leaf" }, - )); - to_add.push(llvm::CreateAttrStringValue( - cx.llcx, - "sign-return-address-key", - if key == PAuthKey::A { "a_key" } else { "b_key" }, - )); + // For non-naked functions, set branch protection attributes on aarch64. + if let Some(BranchProtection { bti, pac_ret }) = + cx.sess().opts.unstable_opts.branch_protection + { + assert!(cx.sess().target.arch == "aarch64"); + if bti { + to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement")); + } + if let Some(PacRet { leaf, pc, key }) = pac_ret { + if pc { + to_add.push(llvm::CreateAttrString(cx.llcx, "branch-protection-pauth-lr")); } + to_add.push(llvm::CreateAttrStringValue( + cx.llcx, + "sign-return-address", + if leaf { "all" } else { "non-leaf" }, + )); + to_add.push(llvm::CreateAttrStringValue( + cx.llcx, + "sign-return-address-key", + if key == PAuthKey::A { "a_key" } else { "b_key" }, + )); } } } @@ -510,12 +523,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(), InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(), })) - // HACK: LLVM versions 19+ do not have the FPMR feature and treat it as always enabled - // It only exists as a feature in LLVM 18, cannot be passed down for any other version - .chain(match &*cx.tcx.sess.target.arch { - "aarch64" if llvm_util::get_version().0 == 18 => vec!["+fpmr".to_string()], - _ => vec![], - }) .collect::>(); if cx.tcx.sess.target.is_like_wasm { diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 668795191a29a..cb329323f5d72 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -28,8 +28,9 @@ use crate::back::write::{ use crate::errors::{ DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib, LtoProcMacro, }; +use crate::llvm::AttributePlace::Function; use crate::llvm::{self, build_string}; -use crate::{LlvmCodegenBackend, ModuleLlvm}; +use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx, attributes}; /// We keep track of the computed LTO cache keys from the previous /// session to determine which CGUs we can reuse. @@ -41,7 +42,8 @@ fn crate_type_allows_lto(crate_type: CrateType) -> bool { | CrateType::Dylib | CrateType::Staticlib | CrateType::Cdylib - | CrateType::ProcMacro => true, + | CrateType::ProcMacro + | CrateType::Sdylib => true, CrateType::Rlib => false, } } @@ -584,12 +586,10 @@ fn thin_lto( } } -fn enable_autodiff_settings(ad: &[config::AutoDiff], module: &mut ModuleCodegen) { +fn enable_autodiff_settings(ad: &[config::AutoDiff]) { for &val in ad { + // We intentionally don't use a wildcard, to not forget handling anything new. match val { - config::AutoDiff::PrintModBefore => { - unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) }; - } config::AutoDiff::PrintPerf => { llvm::set_print_perf(true); } @@ -603,15 +603,23 @@ fn enable_autodiff_settings(ad: &[config::AutoDiff], module: &mut ModuleCodegen< llvm::set_inline(true); } config::AutoDiff::LooseTypes => { - llvm::set_loose_types(false); + llvm::set_loose_types(true); } config::AutoDiff::PrintSteps => { llvm::set_print(true); } - // We handle this below + // We handle this in the PassWrapper.cpp + config::AutoDiff::PrintPasses => {} + // We handle this in the PassWrapper.cpp + config::AutoDiff::PrintModBefore => {} + // We handle this in the PassWrapper.cpp config::AutoDiff::PrintModAfter => {} + // We handle this in the PassWrapper.cpp + config::AutoDiff::PrintModFinal => {} // This is required and already checked config::AutoDiff::Enable => {} + // We handle this below + config::AutoDiff::NoPostopt => {} } } // This helps with handling enums for now. @@ -645,26 +653,57 @@ pub(crate) fn run_pass_manager( // We then run the llvm_optimize function a second time, to optimize the code which we generated // in the enzyme differentiation pass. let enable_ad = config.autodiff.contains(&config::AutoDiff::Enable); - let stage = - if enable_ad { write::AutodiffStage::DuringAD } else { write::AutodiffStage::PostAD }; + let stage = if thin { + write::AutodiffStage::PreAD + } else { + if enable_ad { write::AutodiffStage::DuringAD } else { write::AutodiffStage::PostAD } + }; if enable_ad { - enable_autodiff_settings(&config.autodiff, module); + enable_autodiff_settings(&config.autodiff); } unsafe { write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?; } - if cfg!(llvm_enzyme) && enable_ad { + if cfg!(llvm_enzyme) && enable_ad && !thin { + let cx = + SimpleCx::new(module.module_llvm.llmod(), &module.module_llvm.llcx, cgcx.pointer_size); + + for function in cx.get_functions() { + let enzyme_marker = "enzyme_marker"; + if attributes::has_string_attr(function, enzyme_marker) { + // Sanity check: Ensure 'noinline' is present before replacing it. + assert!( + !attributes::has_attr(function, Function, llvm::AttributeKind::NoInline), + "Expected __enzyme function to have 'noinline' before adding 'alwaysinline'" + ); + + attributes::remove_from_llfn(function, Function, llvm::AttributeKind::NoInline); + attributes::remove_string_attr_from_llfn(function, enzyme_marker); + + assert!( + !attributes::has_string_attr(function, enzyme_marker), + "Expected function to not have 'enzyme_marker'" + ); + + let always_inline = llvm::AttributeKind::AlwaysInline.create_attr(cx.llcx); + attributes::apply_to_llfn(function, Function, &[always_inline]); + } + } + let opt_stage = llvm::OptStage::FatLTO; let stage = write::AutodiffStage::PostAD; - unsafe { - write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?; + if !config.autodiff.contains(&config::AutoDiff::NoPostopt) { + unsafe { + write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?; + } } - // This is the final IR, so people should be able to inspect the optimized autodiff output. - if config.autodiff.contains(&config::AutoDiff::PrintModAfter) { + // This is the final IR, so people should be able to inspect the optimized autodiff output, + // for manual inspection. + if config.autodiff.contains(&config::AutoDiff::PrintModFinal) { unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) }; } } @@ -728,7 +767,7 @@ impl ThinBuffer { } } - pub unsafe fn from_raw_ptr(ptr: *mut llvm::ThinLTOBuffer) -> ThinBuffer { + pub(crate) unsafe fn from_raw_ptr(ptr: *mut llvm::ThinLTOBuffer) -> ThinBuffer { let mut ptr = NonNull::new(ptr).unwrap(); ThinBuffer(unsafe { ptr.as_mut() }) } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index bead4c82a8120..20721c7460878 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -119,14 +119,18 @@ pub(crate) fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> OwnedTar tcx.output_filenames(()).split_dwarf_path( tcx.sess.split_debuginfo(), tcx.sess.opts.unstable_opts.split_dwarf_kind, - Some(mod_name), + mod_name, + tcx.sess.invocation_temp.as_deref(), ) } else { None }; - let output_obj_file = - Some(tcx.output_filenames(()).temp_path(OutputType::Object, Some(mod_name))); + let output_obj_file = Some(tcx.output_filenames(()).temp_path_for_cgu( + OutputType::Object, + mod_name, + tcx.sess.invocation_temp.as_deref(), + )); let config = TargetMachineFactoryConfig { split_dwarf_file, output_obj_file }; target_machine_factory( @@ -330,8 +334,11 @@ pub(crate) fn save_temp_bitcode( return; } let ext = format!("{name}.bc"); - let cgu = Some(&module.name[..]); - let path = cgcx.output_filenames.temp_path_ext(&ext, cgu); + let path = cgcx.output_filenames.temp_path_ext_for_cgu( + &ext, + &module.name, + cgcx.invocation_temp.as_deref(), + ); write_bitcode_to_file(module, &path) } @@ -439,12 +446,9 @@ fn report_inline_asm( let span = if cookie == 0 || matches!(cgcx.lto, Lto::Fat | Lto::Thin) { SpanData::default() } else { - let lo = BytePos::from_u32(cookie as u32); - let hi = BytePos::from_u32((cookie >> 32) as u32); SpanData { - lo, - // LLVM version < 19 silently truncates the cookie to 32 bits in some situations. - hi: if hi.to_u32() != 0 { hi } else { lo }, + lo: BytePos::from_u32(cookie as u32), + hi: BytePos::from_u32((cookie >> 32) as u32), ctxt: SyntaxContext::root(), parent: None, } @@ -568,6 +572,10 @@ pub(crate) unsafe fn llvm_optimize( let consider_ad = cfg!(llvm_enzyme) && config.autodiff.contains(&config::AutoDiff::Enable); let run_enzyme = autodiff_stage == AutodiffStage::DuringAD; + let print_before_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModBefore); + let print_after_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModAfter); + let print_passes = config.autodiff.contains(&config::AutoDiff::PrintPasses); + let merge_functions; let unroll_loops; let vectorize_slp; let vectorize_loop; @@ -575,13 +583,20 @@ pub(crate) unsafe fn llvm_optimize( // When we build rustc with enzyme/autodiff support, we want to postpone size-increasing // optimizations until after differentiation. Our pipeline is thus: (opt + enzyme), (full opt). // We therefore have two calls to llvm_optimize, if autodiff is used. + // + // We also must disable merge_functions, since autodiff placeholder/dummy bodies tend to be + // identical. We run opts before AD, so there is a chance that LLVM will merge our dummies. + // In that case, we lack some dummy bodies and can't replace them with the real AD code anymore. + // We then would need to abort compilation. This was especially common in test cases. if consider_ad && autodiff_stage != AutodiffStage::PostAD { + merge_functions = false; unroll_loops = false; vectorize_slp = false; vectorize_loop = false; } else { unroll_loops = opt_level != config::OptLevel::Size && opt_level != config::OptLevel::SizeMin; + merge_functions = config.merge_functions; vectorize_slp = config.vectorize_slp; vectorize_loop = config.vectorize_loop; } @@ -659,13 +674,16 @@ pub(crate) unsafe fn llvm_optimize( thin_lto_buffer, config.emit_thin_lto, config.emit_thin_lto_summary, - config.merge_functions, + merge_functions, unroll_loops, vectorize_slp, vectorize_loop, config.no_builtins, config.emit_lifetime_markers, run_enzyme, + print_before_enzyme, + print_after_enzyme, + print_passes, sanitizer_options.as_ref(), pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), @@ -697,11 +715,12 @@ pub(crate) unsafe fn optimize( let llcx = &*module.module_llvm.llcx; let _handlers = DiagnosticHandlers::new(cgcx, dcx, llcx, module, CodegenDiagnosticsStage::Opt); - let module_name = module.name.clone(); - let module_name = Some(&module_name[..]); - if config.emit_no_opt_bc { - let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name); + let out = cgcx.output_filenames.temp_path_ext_for_cgu( + "no-opt.bc", + &module.name, + cgcx.invocation_temp.as_deref(), + ); write_bitcode_to_file(module, &out) } @@ -746,8 +765,11 @@ pub(crate) unsafe fn optimize( if let Some(thin_lto_buffer) = thin_lto_buffer { let thin_lto_buffer = unsafe { ThinBuffer::from_raw_ptr(thin_lto_buffer) }; module.thin_lto_buffer = Some(thin_lto_buffer.data().to_vec()); - let bc_summary_out = - cgcx.output_filenames.temp_path(OutputType::ThinLinkBitcode, module_name); + let bc_summary_out = cgcx.output_filenames.temp_path_for_cgu( + OutputType::ThinLinkBitcode, + &module.name, + cgcx.invocation_temp.as_deref(), + ); if config.emit_thin_lto_summary && let Some(thin_link_bitcode_filename) = bc_summary_out.file_name() { @@ -804,8 +826,6 @@ pub(crate) unsafe fn codegen( let llmod = module.module_llvm.llmod(); let llcx = &*module.module_llvm.llcx; let tm = &*module.module_llvm.tm; - let module_name = module.name.clone(); - let module_name = Some(&module_name[..]); let _handlers = DiagnosticHandlers::new(cgcx, dcx, llcx, &module, CodegenDiagnosticsStage::Codegen); @@ -817,8 +837,16 @@ pub(crate) unsafe fn codegen( // copy it to the .o file, and delete the bitcode if it wasn't // otherwise requested. - let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); - let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); + let bc_out = cgcx.output_filenames.temp_path_for_cgu( + OutputType::Bitcode, + &module.name, + cgcx.invocation_temp.as_deref(), + ); + let obj_out = cgcx.output_filenames.temp_path_for_cgu( + OutputType::Object, + &module.name, + cgcx.invocation_temp.as_deref(), + ); if config.bitcode_needed() { if config.emit_bc || config.emit_obj == EmitObj::Bitcode { @@ -860,7 +888,11 @@ pub(crate) unsafe fn codegen( if config.emit_ir { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name); - let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); + let out = cgcx.output_filenames.temp_path_for_cgu( + OutputType::LlvmAssembly, + &module.name, + cgcx.invocation_temp.as_deref(), + ); let out_c = path_to_c_string(&out); extern "C" fn demangle_callback( @@ -902,7 +934,11 @@ pub(crate) unsafe fn codegen( if config.emit_asm { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name); - let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); + let path = cgcx.output_filenames.temp_path_for_cgu( + OutputType::Assembly, + &module.name, + cgcx.invocation_temp.as_deref(), + ); // We can't use the same module for asm and object code output, // because that triggers various errors like invalid IR or broken @@ -932,7 +968,9 @@ pub(crate) unsafe fn codegen( .prof .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name); - let dwo_out = cgcx.output_filenames.temp_path_dwo(module_name); + let dwo_out = cgcx + .output_filenames + .temp_path_dwo_for_cgu(&module.name, cgcx.invocation_temp.as_deref()); let dwo_out = match (cgcx.split_debuginfo, cgcx.split_dwarf_kind) { // Don't change how DWARF is emitted when disabled. (SplitDebuginfo::Off, _) => None, @@ -997,6 +1035,7 @@ pub(crate) unsafe fn codegen( config.emit_asm, config.emit_ir, &cgcx.output_filenames, + cgcx.invocation_temp.as_deref(), )) } @@ -1024,7 +1063,7 @@ fn create_section_with_flags_asm(section_name: &str, section_flags: &str, data: } pub(crate) fn bitcode_section_name(cgcx: &CodegenContext) -> &'static CStr { - if cgcx.target_is_like_osx { + if cgcx.target_is_like_darwin { c"__LLVM,__bitcode" } else if cgcx.target_is_like_aix { c".ipa" @@ -1077,7 +1116,7 @@ unsafe fn embed_bitcode( // and COFF we emit the sections using module level inline assembly for that // reason (see issue #90326 for historical background). unsafe { - if cgcx.target_is_like_osx + if cgcx.target_is_like_darwin || cgcx.target_is_like_aix || cgcx.target_arch == "wasm32" || cgcx.target_arch == "wasm64" @@ -1096,7 +1135,7 @@ unsafe fn embed_bitcode( let llglobal = llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.cmdline"); llvm::set_initializer(llglobal, llconst); - let section = if cgcx.target_is_like_osx { + let section = if cgcx.target_is_like_darwin { c"__LLVM,__cmdline" } else if cgcx.target_is_like_aix { c".info" @@ -1109,9 +1148,9 @@ unsafe fn embed_bitcode( // We need custom section flags, so emit module-level inline assembly. let section_flags = if cgcx.is_pe_coff { "n" } else { "e" }; let asm = create_section_with_flags_asm(".llvmbc", section_flags, bitcode); - llvm::LLVMAppendModuleInlineAsm(llmod, asm.as_c_char_ptr(), asm.len()); + llvm::append_module_inline_asm(llmod, &asm); let asm = create_section_with_flags_asm(".llvmcmd", section_flags, cmdline.as_bytes()); - llvm::LLVMAppendModuleInlineAsm(llmod, asm.as_c_char_ptr(), asm.len()); + llvm::append_module_inline_asm(llmod, &asm); } } } diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 6bd27914dbd16..e4fac35aa4499 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -83,15 +83,15 @@ pub(crate) fn compile_codegen_unit( // Instantiate monomorphizations without filling out definitions yet... let llvm_module = ModuleLlvm::new(tcx, cgu_name.as_str()); { - let cx = CodegenCx::new(tcx, cgu, &llvm_module); + let mut cx = CodegenCx::new(tcx, cgu, &llvm_module); let mono_items = cx.codegen_unit.items_in_deterministic_order(cx.tcx); for &(mono_item, data) in &mono_items { mono_item.predefine::>(&cx, data.linkage, data.visibility); } // ... and now that we have everything pre-defined, fill out those definitions. - for &(mono_item, _) in &mono_items { - mono_item.define::>(&cx); + for &(mono_item, item_data) in &mono_items { + mono_item.define::>(&mut cx, item_data); } // If this codegen unit contains the main function, also create the diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 3f20350d0efd5..5238755c8eb94 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -14,6 +14,7 @@ use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; +use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers, @@ -29,26 +30,26 @@ use smallvec::SmallVec; use tracing::{debug, instrument}; use crate::abi::FnAbiLlvmExt; +use crate::attributes; use crate::common::Funclet; -use crate::context::{CodegenCx, SimpleCx}; +use crate::context::{CodegenCx, FullCx, GenericCx, SCx}; use crate::llvm::{ self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, GEPNoWrapFlags, Metadata, True, }; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use crate::{attributes, llvm_util}; #[must_use] -pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow>> { +pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow>> { pub llbuilder: &'ll mut llvm::Builder<'ll>, - pub cx: &'a CX, + pub cx: &'a GenericCx<'ll, CX>, } -pub(crate) type SBuilder<'a, 'll> = GenericBuilder<'a, 'll, SimpleCx<'ll>>; -pub(crate) type Builder<'a, 'll, 'tcx> = GenericBuilder<'a, 'll, CodegenCx<'ll, 'tcx>>; +pub(crate) type SBuilder<'a, 'll> = GenericBuilder<'a, 'll, SCx<'ll>>; +pub(crate) type Builder<'a, 'll, 'tcx> = GenericBuilder<'a, 'll, FullCx<'ll, 'tcx>>; -impl<'a, 'll, CX: Borrow>> Drop for GenericBuilder<'a, 'll, CX> { +impl<'a, 'll, CX: Borrow>> Drop for GenericBuilder<'a, 'll, CX> { fn drop(&mut self) { unsafe { llvm::LLVMDisposeBuilder(&mut *(self.llbuilder as *mut _)); @@ -57,7 +58,7 @@ impl<'a, 'll, CX: Borrow>> Drop for GenericBuilder<'a, 'll, CX> { } impl<'a, 'll> SBuilder<'a, 'll> { - fn call( + pub(crate) fn call( &mut self, llty: &'ll Type, llfn: &'ll Value, @@ -87,97 +88,54 @@ impl<'a, 'll> SBuilder<'a, 'll> { }; call } +} - fn with_scx(scx: &'a SimpleCx<'ll>) -> Self { +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { + fn with_cx(scx: &'a GenericCx<'ll, CX>) -> Self { // Create a fresh builder from the simple context. - let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(scx.llcx) }; - SBuilder { llbuilder, cx: scx } + let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(scx.deref().borrow().llcx) }; + GenericBuilder { llbuilder, cx: scx } } -} -impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { + pub(crate) fn bitcast(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty, UNNAMED) } } - fn ret_void(&mut self) { - unsafe { - llvm::LLVMBuildRetVoid(self.llbuilder); - } + pub(crate) fn ret_void(&mut self) { + llvm::LLVMBuildRetVoid(self.llbuilder); } - fn ret(&mut self, v: &'ll Value) { + pub(crate) fn ret(&mut self, v: &'ll Value) { unsafe { llvm::LLVMBuildRet(self.llbuilder, v); } } -} -impl<'a, 'll> SBuilder<'a, 'll> { - fn build(cx: &'a SimpleCx<'ll>, llbb: &'ll BasicBlock) -> SBuilder<'a, 'll> { - let bx = SBuilder::with_scx(cx); + + pub(crate) fn build(cx: &'a GenericCx<'ll, CX>, llbb: &'ll BasicBlock) -> Self { + let bx = Self::with_cx(cx); unsafe { llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb); } bx } - - fn check_call<'b>( - &mut self, - typ: &str, - fn_ty: &'ll Type, - llfn: &'ll Value, - args: &'b [&'ll Value], - ) -> Cow<'b, [&'ll Value]> { - assert!( - self.cx.type_kind(fn_ty) == TypeKind::Function, - "builder::{typ} not passed a function, but {fn_ty:?}" - ); - - let param_tys = self.cx.func_params_types(fn_ty); - - let all_args_match = iter::zip(¶m_tys, args.iter().map(|&v| self.cx.val_ty(v))) - .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty); - - if all_args_match { - return Cow::Borrowed(args); - } - - let casted_args: Vec<_> = iter::zip(param_tys, args) - .enumerate() - .map(|(i, (expected_ty, &actual_val))| { - let actual_ty = self.cx.val_ty(actual_val); - if expected_ty != actual_ty { - debug!( - "type mismatch in function call of {:?}. \ - Expected {:?} for param {}, got {:?}; injecting bitcast", - llfn, expected_ty, i, actual_ty - ); - self.bitcast(actual_val, expected_ty) - } else { - actual_val - } - }) - .collect(); - - Cow::Owned(casted_args) - } } /// Empty string, to be used where LLVM expects an instruction name, indicating /// that the instruction is to be left unnamed (i.e. numbered, in textual IR). // FIXME(eddyb) pass `&CStr` directly to FFI once it's a thin pointer. -const UNNAMED: *const c_char = c"".as_ptr(); - -impl<'ll, 'tcx> BackendTypes for Builder<'_, 'll, 'tcx> { - type Value = as BackendTypes>::Value; - type Metadata = as BackendTypes>::Metadata; - type Function = as BackendTypes>::Function; - type BasicBlock = as BackendTypes>::BasicBlock; - type Type = as BackendTypes>::Type; - type Funclet = as BackendTypes>::Funclet; - - type DIScope = as BackendTypes>::DIScope; - type DILocation = as BackendTypes>::DILocation; - type DIVariable = as BackendTypes>::DIVariable; +pub(crate) const UNNAMED: *const c_char = c"".as_ptr(); + +impl<'ll, CX: Borrow>> BackendTypes for GenericBuilder<'_, 'll, CX> { + type Value = as BackendTypes>::Value; + type Metadata = as BackendTypes>::Metadata; + type Function = as BackendTypes>::Function; + type BasicBlock = as BackendTypes>::BasicBlock; + type Type = as BackendTypes>::Type; + type Funclet = as BackendTypes>::Funclet; + + type DIScope = as BackendTypes>::DIScope; + type DILocation = as BackendTypes>::DILocation; + type DIVariable = as BackendTypes>::DIVariable; } impl abi::HasDataLayout for Builder<'_, '_, '_> { @@ -293,9 +251,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn ret_void(&mut self) { - unsafe { - llvm::LLVMBuildRetVoid(self.llbuilder); - } + llvm::LLVMBuildRetVoid(self.llbuilder); } fn ret(&mut self, v: &'ll Value) { @@ -356,8 +312,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // This function handles switch instructions with more than 2 targets and it needs to // emit branch weights metadata instead of using the intrinsic. // The values 1 and 2000 are the same as the values used by the `llvm.expect` intrinsic. - let cold_weight = unsafe { llvm::LLVMValueAsMetadata(self.cx.const_u32(1)) }; - let hot_weight = unsafe { llvm::LLVMValueAsMetadata(self.cx.const_u32(2000)) }; + let cold_weight = llvm::LLVMValueAsMetadata(self.cx.const_u32(1)); + let hot_weight = llvm::LLVMValueAsMetadata(self.cx.const_u32(2000)); let weight = |is_cold: bool| -> &Metadata { if is_cold { cold_weight } else { hot_weight } }; @@ -405,7 +361,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // Emit KCFI operand bundle let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn); - if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|b| b.raw()) { + if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|b| b.as_ref()) { bundles.push(kcfi_bundle); } @@ -638,6 +594,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value { unsafe { let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED); + let align = align.min(self.cx().tcx.sess.target.max_reliable_alignment()); llvm::LLVMSetAlignment(load, align.bytes() as c_uint); load } @@ -851,6 +808,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { assert_eq!(self.cx.type_kind(self.cx.val_ty(ptr)), TypeKind::Pointer); unsafe { let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); + let align = align.min(self.cx().tcx.sess.target.max_reliable_alignment()); let align = if flags.contains(MemFlags::UNALIGNED) { 1 } else { align.bytes() as c_uint }; llvm::LLVMSetAlignment(store, align); @@ -971,11 +929,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { debug_assert_ne!(self.val_ty(val), dest_ty); let trunc = self.trunc(val, dest_ty); - if llvm_util::get_version() >= (19, 0, 0) { - unsafe { - if llvm::LLVMIsAInstruction(trunc).is_some() { - llvm::LLVMSetNUW(trunc, True); - } + unsafe { + if llvm::LLVMIsAInstruction(trunc).is_some() { + llvm::LLVMSetNUW(trunc, True); } } trunc @@ -985,11 +941,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { debug_assert_ne!(self.val_ty(val), dest_ty); let trunc = self.trunc(val, dest_ty); - if llvm_util::get_version() >= (19, 0, 0) { - unsafe { - if llvm::LLVMIsAInstruction(trunc).is_some() { - llvm::LLVMSetNSW(trunc, True); - } + unsafe { + if llvm::LLVMIsAInstruction(trunc).is_some() { + llvm::LLVMSetNSW(trunc, True); } } trunc @@ -1119,6 +1073,35 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { unsafe { llvm::LLVMBuildFCmp(self.llbuilder, op as c_uint, lhs, rhs, UNNAMED) } } + fn three_way_compare( + &mut self, + ty: Ty<'tcx>, + lhs: Self::Value, + rhs: Self::Value, + ) -> Option { + // FIXME: See comment on the definition of `three_way_compare`. + if crate::llvm_util::get_version() < (20, 0, 0) { + return None; + } + + let name = match (ty.is_signed(), ty.primitive_size(self.tcx).bits()) { + (true, 8) => "llvm.scmp.i8.i8", + (true, 16) => "llvm.scmp.i8.i16", + (true, 32) => "llvm.scmp.i8.i32", + (true, 64) => "llvm.scmp.i8.i64", + (true, 128) => "llvm.scmp.i8.i128", + + (false, 8) => "llvm.ucmp.i8.i8", + (false, 16) => "llvm.ucmp.i8.i16", + (false, 32) => "llvm.ucmp.i8.i32", + (false, 64) => "llvm.ucmp.i8.i64", + (false, 128) => "llvm.ucmp.i8.i128", + + _ => bug!("three-way compare unsupported for type {ty:?}"), + }; + Some(self.call_intrinsic(name, &[lhs, rhs])) + } + /* Miscellaneous instructions */ fn memcpy( &mut self, @@ -1433,7 +1416,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // Emit KCFI operand bundle let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn); - if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|b| b.raw()) { + if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|b| b.as_ref()) { bundles.push(kcfi_bundle); } @@ -1476,26 +1459,12 @@ impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> { } impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { - fn build(cx: &'a CodegenCx<'ll, 'tcx>, llbb: &'ll BasicBlock) -> Builder<'a, 'll, 'tcx> { - let bx = Builder::with_cx(cx); - unsafe { - llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb); - } - bx - } - - fn with_cx(cx: &'a CodegenCx<'ll, 'tcx>) -> Self { - // Create a fresh builder from the crate context. - let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(cx.llcx) }; - Builder { llbuilder, cx } - } - pub(crate) fn llfn(&self) -> &'ll Value { unsafe { llvm::LLVMGetBasicBlockParent(self.llbb()) } } } -impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { fn position_at_start(&mut self, llbb: &'ll BasicBlock) { unsafe { llvm::LLVMRustPositionBuilderAtStart(self.llbuilder, llbb); @@ -1525,7 +1494,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { } } } -impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { pub(crate) fn minnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { unsafe { llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs) } } @@ -1626,9 +1595,7 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { let ret = unsafe { llvm::LLVMBuildCatchRet(self.llbuilder, funclet.cleanuppad(), unwind) }; ret.expect("LLVM does not have support for catchret") } -} -impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn check_call<'b>( &mut self, typ: &str, @@ -1643,7 +1610,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { let param_tys = self.cx.func_params_types(fn_ty); - let all_args_match = iter::zip(¶m_tys, args.iter().map(|&v| self.val_ty(v))) + let all_args_match = iter::zip(¶m_tys, args.iter().map(|&v| self.cx.val_ty(v))) .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty); if all_args_match { @@ -1653,7 +1620,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { let casted_args: Vec<_> = iter::zip(param_tys, args) .enumerate() .map(|(i, (expected_ty, &actual_val))| { - let actual_ty = self.val_ty(actual_val); + let actual_ty = self.cx.val_ty(actual_val); if expected_ty != actual_ty { debug!( "type mismatch in function call of {:?}. \ @@ -1669,12 +1636,12 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { Cow::Owned(casted_args) } -} -impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { + pub(crate) fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) } } } + impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value { let (ty, f) = self.cx.get_intrinsic(intrinsic); @@ -1694,7 +1661,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { self.call_intrinsic(intrinsic, &[self.cx.const_u64(size), ptr]); } } -impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { pub(crate) fn phi( &mut self, ty: &'ll Type, @@ -1782,7 +1749,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { // Emit KCFI operand bundle let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn); - if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|b| b.raw()) { + if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|b| b.as_ref()) { bundles.push(kcfi_bundle); } @@ -1869,7 +1836,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, instance: Option>, llfn: &'ll Value, - ) -> Option> { + ) -> Option> { let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) }; let kcfi_bundle = if self.tcx.sess.is_sanitizer_kcfi_enabled() && let Some(fn_abi) = fn_abi @@ -1895,7 +1862,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { kcfi::typeid_for_fnabi(self.tcx, fn_abi, options) }; - Some(llvm::OperandBundleOwned::new("kcfi", &[self.const_u32(kcfi_typeid)])) + Some(llvm::OperandBundleBox::new("kcfi", &[self.const_u32(kcfi_typeid)])) } else { None }; @@ -1930,10 +1897,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { hash: &'ll Value, bitmap_bits: &'ll Value, ) { - assert!( - crate::llvm_util::get_version() >= (19, 0, 0), - "MCDC intrinsics require LLVM 19 or later" - ); self.call_intrinsic("llvm.instrprof.mcdc.parameters", &[fn_name, hash, bitmap_bits]); } @@ -1945,10 +1908,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { bitmap_index: &'ll Value, mcdc_temp: &'ll Value, ) { - assert!( - crate::llvm_util::get_version() >= (19, 0, 0), - "MCDC intrinsics require LLVM 19 or later" - ); let args = &[fn_name, hash, bitmap_index, mcdc_temp]; self.call_intrinsic("llvm.instrprof.mcdc.tvbitmap.update", args); } @@ -1960,10 +1919,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { #[instrument(level = "debug", skip(self))] pub(crate) fn mcdc_condbitmap_update(&mut self, cond_index: &'ll Value, mcdc_temp: &'ll Value) { - assert!( - crate::llvm_util::get_version() >= (19, 0, 0), - "MCDC intrinsics require LLVM 19 or later" - ); let align = self.tcx.data_layout.i32_align.abi; let current_tv_index = self.load(self.cx.type_i32(), mcdc_temp, align); let new_tv_index = self.add(current_tv_index, cond_index); diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 2c7899975e3ee..c5c13ac097a27 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -3,11 +3,14 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, AutoDiffItem, DiffActivity, DiffMode}; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::back::write::ModuleConfig; +use rustc_codegen_ssa::common::TypeKind; +use rustc_codegen_ssa::traits::BaseTypeCodegenMethods; use rustc_errors::FatalError; +use rustc_middle::bug; use tracing::{debug, trace}; use crate::back::write::llvm_err; -use crate::builder::SBuilder; +use crate::builder::{SBuilder, UNNAMED}; use crate::context::SimpleCx; use crate::declare::declare_simple_fn; use crate::errors::{AutoDiffWithoutEnable, LlvmError}; @@ -17,14 +20,264 @@ use crate::value::Value; use crate::{CodegenContext, LlvmCodegenBackend, ModuleLlvm, attributes, llvm}; fn get_params(fnc: &Value) -> Vec<&Value> { + let param_num = llvm::LLVMCountParams(fnc) as usize; + let mut fnc_args: Vec<&Value> = vec![]; + fnc_args.reserve(param_num); unsafe { - let param_num = llvm::LLVMCountParams(fnc) as usize; - let mut fnc_args: Vec<&Value> = vec![]; - fnc_args.reserve(param_num); llvm::LLVMGetParams(fnc, fnc_args.as_mut_ptr()); fnc_args.set_len(param_num); - fnc_args } + fnc_args +} + +fn has_sret(fnc: &Value) -> bool { + let num_args = llvm::LLVMCountParams(fnc) as usize; + if num_args == 0 { + false + } else { + unsafe { llvm::LLVMRustHasAttributeAtIndex(fnc, 0, llvm::AttributeKind::StructRet) } + } +} + +// When we call the `__enzyme_autodiff` or `__enzyme_fwddiff` function, we need to pass all the +// original inputs, as well as metadata and the additional shadow arguments. +// This function matches the arguments from the outer function to the inner enzyme call. +// +// This function also considers that Rust level arguments not always match the llvm-ir level +// arguments. A slice, `&[f32]`, for example, is represented as a pointer and a length on +// llvm-ir level. The number of activities matches the number of Rust level arguments, so we +// need to match those. +// FIXME(ZuseZ4): This logic is a bit more complicated than it should be, can we simplify it +// using iterators and peek()? +fn match_args_from_caller_to_enzyme<'ll>( + cx: &SimpleCx<'ll>, + builder: &SBuilder<'ll, 'll>, + width: u32, + args: &mut Vec<&'ll llvm::Value>, + inputs: &[DiffActivity], + outer_args: &[&'ll llvm::Value], + has_sret: bool, +) { + debug!("matching autodiff arguments"); + // We now handle the issue that Rust level arguments not always match the llvm-ir level + // arguments. A slice, `&[f32]`, for example, is represented as a pointer and a length on + // llvm-ir level. The number of activities matches the number of Rust level arguments, so we + // need to match those. + // FIXME(ZuseZ4): This logic is a bit more complicated than it should be, can we simplify it + // using iterators and peek()? + let mut outer_pos: usize = 0; + let mut activity_pos = 0; + + if has_sret { + // Then the first outer arg is the sret pointer. Enzyme doesn't know about sret, so the + // inner function will still return something. We increase our outer_pos by one, + // and once we're done with all other args we will take the return of the inner call and + // update the sret pointer with it + outer_pos = 1; + } + + let enzyme_const = cx.create_metadata("enzyme_const".to_string()).unwrap(); + let enzyme_out = cx.create_metadata("enzyme_out".to_string()).unwrap(); + let enzyme_dup = cx.create_metadata("enzyme_dup".to_string()).unwrap(); + let enzyme_dupv = cx.create_metadata("enzyme_dupv".to_string()).unwrap(); + let enzyme_dupnoneed = cx.create_metadata("enzyme_dupnoneed".to_string()).unwrap(); + let enzyme_dupnoneedv = cx.create_metadata("enzyme_dupnoneedv".to_string()).unwrap(); + + while activity_pos < inputs.len() { + let diff_activity = inputs[activity_pos as usize]; + // Duplicated arguments received a shadow argument, into which enzyme will write the + // gradient. + let (activity, duplicated): (&Metadata, bool) = match diff_activity { + DiffActivity::None => panic!("not a valid input activity"), + DiffActivity::Const => (enzyme_const, false), + DiffActivity::Active => (enzyme_out, false), + DiffActivity::ActiveOnly => (enzyme_out, false), + DiffActivity::Dual => (enzyme_dup, true), + DiffActivity::Dualv => (enzyme_dupv, true), + DiffActivity::DualOnly => (enzyme_dupnoneed, true), + DiffActivity::DualvOnly => (enzyme_dupnoneedv, true), + DiffActivity::Duplicated => (enzyme_dup, true), + DiffActivity::DuplicatedOnly => (enzyme_dupnoneed, true), + DiffActivity::FakeActivitySize(_) => (enzyme_const, false), + }; + let outer_arg = outer_args[outer_pos]; + args.push(cx.get_metadata_value(activity)); + if matches!(diff_activity, DiffActivity::Dualv) { + let next_outer_arg = outer_args[outer_pos + 1]; + let elem_bytes_size: u64 = match inputs[activity_pos + 1] { + DiffActivity::FakeActivitySize(Some(s)) => s.into(), + _ => bug!("incorrect Dualv handling recognized."), + }; + // stride: sizeof(T) * n_elems. + // n_elems is the next integer. + // Now we multiply `4 * next_outer_arg` to get the stride. + let mul = unsafe { + llvm::LLVMBuildMul( + builder.llbuilder, + cx.get_const_i64(elem_bytes_size), + next_outer_arg, + UNNAMED, + ) + }; + args.push(mul); + } + args.push(outer_arg); + if duplicated { + // We know that duplicated args by construction have a following argument, + // so this can not be out of bounds. + let next_outer_arg = outer_args[outer_pos + 1]; + let next_outer_ty = cx.val_ty(next_outer_arg); + // FIXME(ZuseZ4): We should add support for Vec here too, but it's less urgent since + // vectors behind references (&Vec) are already supported. Users can not pass a + // Vec by value for reverse mode, so this would only help forward mode autodiff. + let slice = { + if activity_pos + 1 >= inputs.len() { + // If there is no arg following our ptr, it also can't be a slice, + // since that would lead to a ptr, int pair. + false + } else { + let next_activity = inputs[activity_pos + 1]; + // We analyze the MIR types and add this dummy activity if we visit a slice. + matches!(next_activity, DiffActivity::FakeActivitySize(_)) + } + }; + if slice { + // A duplicated slice will have the following two outer_fn arguments: + // (..., ptr1, int1, ptr2, int2, ...). We add the following llvm-ir to our __enzyme call: + // (..., metadata! enzyme_dup, ptr, ptr, int1, ...). + // FIXME(ZuseZ4): We will upstream a safety check later which asserts that + // int2 >= int1, which means the shadow vector is large enough to store the gradient. + assert_eq!(cx.type_kind(next_outer_ty), TypeKind::Integer); + + let iterations = + if matches!(diff_activity, DiffActivity::Dualv) { 1 } else { width as usize }; + + for i in 0..iterations { + let next_outer_arg2 = outer_args[outer_pos + 2 * (i + 1)]; + let next_outer_ty2 = cx.val_ty(next_outer_arg2); + assert_eq!(cx.type_kind(next_outer_ty2), TypeKind::Pointer); + let next_outer_arg3 = outer_args[outer_pos + 2 * (i + 1) + 1]; + let next_outer_ty3 = cx.val_ty(next_outer_arg3); + assert_eq!(cx.type_kind(next_outer_ty3), TypeKind::Integer); + args.push(next_outer_arg2); + } + args.push(cx.get_metadata_value(enzyme_const)); + args.push(next_outer_arg); + outer_pos += 2 + 2 * iterations; + activity_pos += 2; + } else { + // A duplicated pointer will have the following two outer_fn arguments: + // (..., ptr, ptr, ...). We add the following llvm-ir to our __enzyme call: + // (..., metadata! enzyme_dup, ptr, ptr, ...). + if matches!(diff_activity, DiffActivity::Duplicated | DiffActivity::DuplicatedOnly) + { + assert_eq!(cx.type_kind(next_outer_ty), TypeKind::Pointer); + } + // In the case of Dual we don't have assumptions, e.g. f32 would be valid. + args.push(next_outer_arg); + outer_pos += 2; + activity_pos += 1; + + // Now, if width > 1, we need to account for that + for _ in 1..width { + let next_outer_arg = outer_args[outer_pos]; + args.push(next_outer_arg); + outer_pos += 1; + } + } + } else { + // We do not differentiate with resprect to this argument. + // We already added the metadata and argument above, so just increase the counters. + outer_pos += 1; + activity_pos += 1; + } + } +} + +// On LLVM-IR, we can luckily declare __enzyme_ functions without specifying the input +// arguments. We do however need to declare them with their correct return type. +// We already figured the correct return type out in our frontend, when generating the outer_fn, +// so we can now just go ahead and use that. This is not always trivial, e.g. because sret. +// Beyond sret, this article describes our challenges nicely: +// +// I.e. (i32, f32) will get merged into i64, but we don't handle that yet. +fn compute_enzyme_fn_ty<'ll>( + cx: &SimpleCx<'ll>, + attrs: &AutoDiffAttrs, + fn_to_diff: &'ll Value, + outer_fn: &'ll Value, +) -> &'ll llvm::Type { + let fn_ty = cx.get_type_of_global(outer_fn); + let mut ret_ty = cx.get_return_type(fn_ty); + + let has_sret = has_sret(outer_fn); + + if has_sret { + // Now we don't just forward the return type, so we have to figure it out based on the + // primal return type, in combination with the autodiff settings. + let fn_ty = cx.get_type_of_global(fn_to_diff); + let inner_ret_ty = cx.get_return_type(fn_ty); + + let void_ty = unsafe { llvm::LLVMVoidTypeInContext(cx.llcx) }; + if inner_ret_ty == void_ty { + // This indicates that even the inner function has an sret. + // Right now I only look for an sret in the outer function. + // This *probably* needs some extra handling, but I never ran + // into such a case. So I'll wait for user reports to have a test case. + bug!("sret in inner function"); + } + + if attrs.width == 1 { + // Enzyme returns a struct of style: + // `{ original_ret(if requested), float, float, ... }` + let mut struct_elements = vec![]; + if attrs.has_primal_ret() { + struct_elements.push(inner_ret_ty); + } + // Next, we push the list of active floats, since they will be lowered to `enzyme_out`, + // and therefore part of the return struct. + let param_tys = cx.func_params_types(fn_ty); + for (act, param_ty) in attrs.input_activity.iter().zip(param_tys) { + if matches!(act, DiffActivity::Active) { + // Now find the float type at position i based on the fn_ty, + // to know what (f16/f32/f64/...) to add to the struct. + struct_elements.push(param_ty); + } + } + ret_ty = cx.type_struct(&struct_elements, false); + } else { + // First we check if we also have to deal with the primal return. + match attrs.mode { + DiffMode::Forward => match attrs.ret_activity { + DiffActivity::Dual => { + let arr_ty = + unsafe { llvm::LLVMArrayType2(inner_ret_ty, attrs.width as u64 + 1) }; + ret_ty = arr_ty; + } + DiffActivity::DualOnly => { + let arr_ty = + unsafe { llvm::LLVMArrayType2(inner_ret_ty, attrs.width as u64) }; + ret_ty = arr_ty; + } + DiffActivity::Const => { + todo!("Not sure, do we need to do something here?"); + } + _ => { + bug!("unreachable"); + } + }, + DiffMode::Reverse => { + todo!("Handle sret for reverse mode"); + } + _ => { + bug!("unreachable"); + } + } + } + } + + // LLVM can figure out the input types on it's own, so we take a shortcut here. + unsafe { llvm::LLVMFunctionType(ret_ty, ptr::null(), 0, True) } } /// When differentiating `fn_to_diff`, take a `outer_fn` and generate another @@ -42,9 +295,6 @@ fn generate_enzyme_call<'ll>( outer_fn: &'ll Value, attrs: AutoDiffAttrs, ) { - let inputs = attrs.input_activity; - let output = attrs.ret_activity; - // We have to pick the name depending on whether we want forward or reverse mode autodiff. let mut ad_name: String = match attrs.mode { DiffMode::Forward => "__enzyme_fwddiff", @@ -92,17 +342,9 @@ fn generate_enzyme_call<'ll>( // } // ``` unsafe { - // On LLVM-IR, we can luckily declare __enzyme_ functions without specifying the input - // arguments. We do however need to declare them with their correct return type. - // We already figured the correct return type out in our frontend, when generating the outer_fn, - // so we can now just go ahead and use that. FIXME(ZuseZ4): This doesn't handle sret yet. - let fn_ty = llvm::LLVMGlobalGetValueType(outer_fn); - let ret_ty = llvm::LLVMGetReturnType(fn_ty); + let enzyme_ty = compute_enzyme_fn_ty(cx, &attrs, fn_to_diff, outer_fn); - // LLVM can figure out the input types on it's own, so we take a shortcut here. - let enzyme_ty = llvm::LLVMFunctionType(ret_ty, ptr::null(), 0, True); - - //FIXME(ZuseZ4): the CC/Addr/Vis values are best effort guesses, we should look at tests and + // FIXME(ZuseZ4): the CC/Addr/Vis values are best effort guesses, we should look at tests and // think a bit more about what should go here. let cc = llvm::LLVMGetFunctionCallConv(outer_fn); let ad_fn = declare_simple_fn( @@ -119,6 +361,11 @@ fn generate_enzyme_call<'ll>( let attr = llvm::AttributeKind::NoInline.create_attr(cx.llcx); attributes::apply_to_llfn(ad_fn, Function, &[attr]); + // We add a made-up attribute just such that we can recognize it after AD to update + // (no)-inline attributes. We'll then also remove this attribute. + let enzyme_marker_attr = llvm::CreateAttrString(cx.llcx, "enzyme_marker"); + attributes::apply_to_llfn(outer_fn, Function, &[enzyme_marker_attr]); + // first, remove all calls from fnc let entry = llvm::LLVMGetFirstBasicBlock(outer_fn); let br = llvm::LLVMRustGetTerminator(entry); @@ -131,116 +378,32 @@ fn generate_enzyme_call<'ll>( let mut args = Vec::with_capacity(num_args as usize + 1); args.push(fn_to_diff); - let enzyme_const = cx.create_metadata("enzyme_const".to_string()).unwrap(); - let enzyme_out = cx.create_metadata("enzyme_out".to_string()).unwrap(); - let enzyme_dup = cx.create_metadata("enzyme_dup".to_string()).unwrap(); - let enzyme_dupnoneed = cx.create_metadata("enzyme_dupnoneed".to_string()).unwrap(); let enzyme_primal_ret = cx.create_metadata("enzyme_primal_return".to_string()).unwrap(); - - match output { - DiffActivity::Dual => { - args.push(cx.get_metadata_value(enzyme_primal_ret)); - } - DiffActivity::Active => { - args.push(cx.get_metadata_value(enzyme_primal_ret)); - } - _ => {} + if matches!(attrs.ret_activity, DiffActivity::Dual | DiffActivity::Active) { + args.push(cx.get_metadata_value(enzyme_primal_ret)); + } + if attrs.width > 1 { + let enzyme_width = cx.create_metadata("enzyme_width".to_string()).unwrap(); + args.push(cx.get_metadata_value(enzyme_width)); + args.push(cx.get_const_i64(attrs.width as u64)); } - debug!("matching autodiff arguments"); - // We now handle the issue that Rust level arguments not always match the llvm-ir level - // arguments. A slice, `&[f32]`, for example, is represented as a pointer and a length on - // llvm-ir level. The number of activities matches the number of Rust level arguments, so we - // need to match those. - // FIXME(ZuseZ4): This logic is a bit more complicated than it should be, can we simplify it - // using iterators and peek()? - let mut outer_pos: usize = 0; - let mut activity_pos = 0; + let has_sret = has_sret(outer_fn); let outer_args: Vec<&llvm::Value> = get_params(outer_fn); - while activity_pos < inputs.len() { - let diff_activity = inputs[activity_pos as usize]; - // Duplicated arguments received a shadow argument, into which enzyme will write the - // gradient. - let (activity, duplicated): (&Metadata, bool) = match diff_activity { - DiffActivity::None => panic!("not a valid input activity"), - DiffActivity::Const => (enzyme_const, false), - DiffActivity::Active => (enzyme_out, false), - DiffActivity::ActiveOnly => (enzyme_out, false), - DiffActivity::Dual => (enzyme_dup, true), - DiffActivity::DualOnly => (enzyme_dupnoneed, true), - DiffActivity::Duplicated => (enzyme_dup, true), - DiffActivity::DuplicatedOnly => (enzyme_dupnoneed, true), - DiffActivity::FakeActivitySize => (enzyme_const, false), - }; - let outer_arg = outer_args[outer_pos]; - args.push(cx.get_metadata_value(activity)); - args.push(outer_arg); - if duplicated { - // We know that duplicated args by construction have a following argument, - // so this can not be out of bounds. - let next_outer_arg = outer_args[outer_pos + 1]; - let next_outer_ty = cx.val_ty(next_outer_arg); - // FIXME(ZuseZ4): We should add support for Vec here too, but it's less urgent since - // vectors behind references (&Vec) are already supported. Users can not pass a - // Vec by value for reverse mode, so this would only help forward mode autodiff. - let slice = { - if activity_pos + 1 >= inputs.len() { - // If there is no arg following our ptr, it also can't be a slice, - // since that would lead to a ptr, int pair. - false - } else { - let next_activity = inputs[activity_pos + 1]; - // We analyze the MIR types and add this dummy activity if we visit a slice. - next_activity == DiffActivity::FakeActivitySize - } - }; - if slice { - // A duplicated slice will have the following two outer_fn arguments: - // (..., ptr1, int1, ptr2, int2, ...). We add the following llvm-ir to our __enzyme call: - // (..., metadata! enzyme_dup, ptr, ptr, int1, ...). - // FIXME(ZuseZ4): We will upstream a safety check later which asserts that - // int2 >= int1, which means the shadow vector is large enough to store the gradient. - assert!(llvm::LLVMRustGetTypeKind(next_outer_ty) == llvm::TypeKind::Integer); - let next_outer_arg2 = outer_args[outer_pos + 2]; - let next_outer_ty2 = cx.val_ty(next_outer_arg2); - assert!(llvm::LLVMRustGetTypeKind(next_outer_ty2) == llvm::TypeKind::Pointer); - let next_outer_arg3 = outer_args[outer_pos + 3]; - let next_outer_ty3 = cx.val_ty(next_outer_arg3); - assert!(llvm::LLVMRustGetTypeKind(next_outer_ty3) == llvm::TypeKind::Integer); - args.push(next_outer_arg2); - args.push(cx.get_metadata_value(enzyme_const)); - args.push(next_outer_arg); - outer_pos += 4; - activity_pos += 2; - } else { - // A duplicated pointer will have the following two outer_fn arguments: - // (..., ptr, ptr, ...). We add the following llvm-ir to our __enzyme call: - // (..., metadata! enzyme_dup, ptr, ptr, ...). - if matches!( - diff_activity, - DiffActivity::Duplicated | DiffActivity::DuplicatedOnly - ) { - assert!( - llvm::LLVMRustGetTypeKind(next_outer_ty) == llvm::TypeKind::Pointer - ); - } - // In the case of Dual we don't have assumptions, e.g. f32 would be valid. - args.push(next_outer_arg); - outer_pos += 2; - activity_pos += 1; - } - } else { - // We do not differentiate with resprect to this argument. - // We already added the metadata and argument above, so just increase the counters. - outer_pos += 1; - activity_pos += 1; - } - } + match_args_from_caller_to_enzyme( + &cx, + &builder, + attrs.width, + &mut args, + &attrs.input_activity, + &outer_args, + has_sret, + ); let call = builder.call(enzyme_ty, ad_fn, &args, None); // This part is a bit iffy. LLVM requires that a call to an inlineable function has some - // metadata attachted to it, but we just created this code oota. Given that the + // metadata attached to it, but we just created this code oota. Given that the // differentiated function already has partly confusing metadata, and given that this // affects nothing but the auttodiff IR, we take a shortcut and just steal metadata from the // dummy code which we inserted at a higher level. @@ -261,7 +424,26 @@ fn generate_enzyme_call<'ll>( // Now that we copied the metadata, get rid of dummy code. llvm::LLVMRustEraseInstUntilInclusive(entry, last_inst); - if cx.val_ty(call) == cx.type_void() { + if cx.val_ty(call) == cx.type_void() || has_sret { + if has_sret { + // This is what we already have in our outer_fn (shortened): + // define void @_foo(ptr <..> sret([32 x i8]) initializes((0, 32)) %0, <...>) { + // %7 = call [4 x double] (...) @__enzyme_fwddiff_foo(ptr @square, metadata !"enzyme_width", i64 4, <...>) + // + // store [4 x double] %7, ptr %0, align 8 + // ret void + // } + + // now store the result of the enzyme call into the sret pointer. + let sret_ptr = outer_args[0]; + let call_ty = cx.val_ty(call); + if attrs.width == 1 { + assert_eq!(cx.type_kind(call_ty), TypeKind::Struct); + } else { + assert_eq!(cx.type_kind(call_ty), TypeKind::Array); + } + llvm::LLVMBuildStore(&builder.llbuilder, call, sret_ptr); + } builder.ret_void(); } else { builder.ret(call); @@ -286,17 +468,17 @@ pub(crate) fn differentiate<'ll>( } let diag_handler = cgcx.create_dcx(); - let cx = SimpleCx { llmod: module.module_llvm.llmod(), llcx: module.module_llvm.llcx }; + + let cx = SimpleCx::new(module.module_llvm.llmod(), module.module_llvm.llcx, cgcx.pointer_size); // First of all, did the user try to use autodiff without using the -Zautodiff=Enable flag? if !diff_items.is_empty() && !cgcx.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::Enable) { - let dcx = cgcx.create_dcx(); - return Err(dcx.handle().emit_almost_fatal(AutoDiffWithoutEnable)); + return Err(diag_handler.handle().emit_almost_fatal(AutoDiffWithoutEnable)); } - // Before dumping the module, we want all the TypeTrees to become part of the module. + // Here we replace the placeholder code with the actual autodiff code, which calls Enzyme. for item in diff_items.iter() { let name = item.source.clone(); let fn_def: Option<&llvm::Value> = cx.get_function(&name); diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index ea9ab5c02bd6c..6d68eca60afc1 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -103,7 +103,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t // This is a monomorphization of a generic function. if !(cx.tcx.sess.opts.share_generics() || tcx.codegen_fn_attrs(instance_def_id).inline - == rustc_attr_parsing::InlineAttr::Never) + == rustc_attr_data_structures::InlineAttr::Never) { // When not sharing generics, all instances are in the same // crate and have hidden visibility. diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 0621b893e7550..3cfa96393e920 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -1,9 +1,11 @@ //! Code that is useful in various codegen modules. +use std::borrow::Borrow; + use libc::{c_char, c_uint}; use rustc_abi as abi; +use rustc_abi::HasDataLayout; use rustc_abi::Primitive::Pointer; -use rustc_abi::{AddressSpace, HasDataLayout}; use rustc_ast::Mutability; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; @@ -18,6 +20,7 @@ use tracing::debug; use crate::consts::const_alloc_to_llvm; pub(crate) use crate::context::CodegenCx; +use crate::context::{GenericCx, SCx}; use crate::llvm::{self, BasicBlock, Bool, ConstantInt, False, Metadata, True}; use crate::type_::Type; use crate::value::Value; @@ -64,12 +67,12 @@ use crate::value::Value; /// the `OperandBundleDef` value created for MSVC landing pads. pub(crate) struct Funclet<'ll> { cleanuppad: &'ll Value, - operand: llvm::OperandBundleOwned<'ll>, + operand: llvm::OperandBundleBox<'ll>, } impl<'ll> Funclet<'ll> { pub(crate) fn new(cleanuppad: &'ll Value) -> Self { - Funclet { cleanuppad, operand: llvm::OperandBundleOwned::new("funclet", &[cleanuppad]) } + Funclet { cleanuppad, operand: llvm::OperandBundleBox::new("funclet", &[cleanuppad]) } } pub(crate) fn cleanuppad(&self) -> &'ll Value { @@ -77,11 +80,11 @@ impl<'ll> Funclet<'ll> { } pub(crate) fn bundle(&self) -> &llvm::OperandBundle<'ll> { - self.operand.raw() + self.operand.as_ref() } } -impl<'ll> BackendTypes for CodegenCx<'ll, '_> { +impl<'ll, CX: Borrow>> BackendTypes for GenericCx<'ll, CX> { type Value = &'ll Value; type Metadata = &'ll Metadata; // FIXME(eddyb) replace this with a `Function` "subclass" of `Value`. @@ -118,7 +121,7 @@ impl<'ll> CodegenCx<'ll, '_> { } } -impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { +impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { fn const_null(&self, t: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMConstNull(t) } } @@ -127,10 +130,6 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMGetUndef(t) } } - fn is_undef(&self, v: &'ll Value) -> bool { - unsafe { llvm::LLVMIsUndef(v) == True } - } - fn const_poison(&self, t: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMGetPoison(t) } } @@ -209,28 +208,24 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn const_str(&self, s: &str) -> (&'ll Value, &'ll Value) { - let str_global = *self - .const_str_cache - .borrow_mut() - .raw_entry_mut() - .from_key(s) - .or_insert_with(|| { - let sc = self.const_bytes(s.as_bytes()); - let sym = self.generate_local_symbol_name("str"); - let g = self.define_global(&sym, self.val_ty(sc)).unwrap_or_else(|| { - bug!("symbol `{}` is already defined", sym); - }); - llvm::set_initializer(g, sc); - unsafe { - llvm::LLVMSetGlobalConstant(g, True); - llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global); - } - llvm::set_linkage(g, llvm::Linkage::InternalLinkage); - // Cast to default address space if globals are in a different addrspace - let g = self.const_pointercast(g, self.type_ptr()); - (s.to_owned(), g) - }) - .1; + let mut const_str_cache = self.const_str_cache.borrow_mut(); + let str_global = const_str_cache.get(s).copied().unwrap_or_else(|| { + let sc = self.const_bytes(s.as_bytes()); + let sym = self.generate_local_symbol_name("str"); + let g = self.define_global(&sym, self.val_ty(sc)).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + llvm::set_initializer(g, sc); + unsafe { + llvm::LLVMSetGlobalConstant(g, True); + llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global); + } + llvm::set_linkage(g, llvm::Linkage::InternalLinkage); + // Cast to default address space if globals are in a different addrspace + let g = self.const_pointercast(g, self.type_ptr()); + const_str_cache.insert(s.to_owned(), g); + g + }); let len = s.len(); (str_global, self.const_usize(len as u64)) } @@ -274,7 +269,8 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } Scalar::Ptr(ptr, _size) => { let (prov, offset) = ptr.into_parts(); - let (base_addr, base_addr_space) = match self.tcx.global_alloc(prov.alloc_id()) { + let global_alloc = self.tcx.global_alloc(prov.alloc_id()); + let base_addr = match global_alloc { GlobalAlloc::Memory(alloc) => { // For ZSTs directly codegen an aligned pointer. // This avoids generating a zero-sized constant value and actually needing a @@ -306,12 +302,10 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { format!("alloc_{hash:032x}").as_bytes(), ); } - (value, AddressSpace::DATA) + value } } - GlobalAlloc::Function { instance, .. } => { - (self.get_fn_addr(instance), self.data_layout().instruction_address_space) - } + GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance), GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx @@ -324,14 +318,15 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { .unwrap_memory(); let init = const_alloc_to_llvm(self, alloc, /*static*/ false); let value = self.static_addr_of_impl(init, alloc.inner().align, None); - (value, AddressSpace::DATA) + value } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); assert!(!self.tcx.is_thread_local_static(def_id)); - (self.get_static(def_id), AddressSpace::DATA) + self.get_static(def_id) } }; + let base_addr_space = global_alloc.address_space(self); let llval = unsafe { llvm::LLVMConstInBoundsGEP2( self.type_i8(), @@ -350,7 +345,7 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } - fn const_data_from_alloc(&self, alloc: ConstAllocation<'tcx>) -> Self::Value { + fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value { const_alloc_to_llvm(self, alloc, /*static*/ false) } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 330e8a8f4069d..bf81eb648f8ae 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -5,6 +5,7 @@ use rustc_abi::{ }; use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; +use rustc_hir::LangItem; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; @@ -12,11 +13,10 @@ use rustc_middle::mir::interpret::{ Allocation, ConstAllocation, ErrorHandled, InitChunk, Pointer, Scalar as InterpScalar, read_target_uint, }; -use rustc_middle::mir::mono::MonoItem; -use rustc_middle::ty::Instance; +use rustc_middle::mir::mono::{Linkage, MonoItem}; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; +use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; -use rustc_session::config::Lto; use tracing::{debug, instrument, trace}; use crate::common::{AsCCharPtr, CodegenCx}; @@ -129,7 +129,12 @@ pub(crate) fn const_alloc_to_llvm<'ll>( append_chunks_of_init_and_uninit_bytes(&mut llvals, cx, alloc, range); } - cx.const_struct(&llvals, true) + // Avoid wrapping in a struct if there is only a single value. This ensures + // that LLVM is able to perform the string merging optimization if the constant + // is a valid C string. LLVM only considers bare arrays for this optimization, + // not arrays wrapped in a struct. LLVM handles this at: + // https://github.com/rust-lang/llvm-project/blob/acaea3d2bb8f351b740db7ebce7d7a40b9e21488/llvm/lib/Target/TargetLoweringObjectFile.cpp#L249-L280 + if let &[data] = &*llvals { data } else { cx.const_struct(&llvals, true) } } fn codegen_static_initializer<'ll, 'tcx>( @@ -172,8 +177,27 @@ fn check_and_apply_linkage<'ll, 'tcx>( if let Some(linkage) = attrs.import_linkage { debug!("get_static: sym={} linkage={:?}", sym, linkage); - // Declare a symbol `foo` with the desired linkage. - let g1 = cx.declare_global(sym, cx.type_i8()); + // Declare a symbol `foo`. If `foo` is an extern_weak symbol, we declare + // an extern_weak function, otherwise a global with the desired linkage. + let g1 = if matches!(attrs.import_linkage, Some(Linkage::ExternalWeak)) { + // An `extern_weak` function is represented as an `Option`, + // we extract the function signature and declare it as an extern_weak function + // instead of an extern_weak i8. + let instance = Instance::mono(cx.tcx, def_id); + if let ty::Adt(struct_def, args) = instance.ty(cx.tcx, cx.typing_env()).kind() + && cx.tcx.is_lang_item(struct_def.did(), LangItem::Option) + && let ty::FnPtr(sig, header) = args.type_at(0).kind() + { + let fn_sig = sig.with(*header); + + let fn_abi = cx.fn_abi_of_fn_ptr(fn_sig, ty::List::empty()); + cx.declare_fn(sym, &fn_abi, None) + } else { + cx.declare_global(sym, cx.type_i8()) + } + } else { + cx.declare_global(sym, cx.type_i8()) + }; llvm::set_linkage(g1, base::linkage_to_llvm(linkage)); // Declare an internal global `extern_with_linkage_foo` which @@ -344,11 +368,11 @@ impl<'ll> CodegenCx<'ll, '_> { // Local definitions can never be imported, so we must not apply // the DLLImport annotation. && !dso_local - // ThinLTO can't handle this workaround in all cases, so we don't - // emit the attrs. Instead we make them unnecessary by disallowing - // dynamic linking when linker plugin based LTO is enabled. - && !self.tcx.sess.opts.cg.linker_plugin_lto.enabled() - && self.tcx.sess.lto() != Lto::Thin; + // Linker plugin ThinLTO doesn't create the self-dllimport Rust uses for rlibs + // as the code generation happens out of process. Instead we assume static linkage + // and disallow dynamic linking when linker plugin based LTO is enabled. + // Regular in-process ThinLTO doesn't need this workaround. + && !self.tcx.sess.opts.cg.linker_plugin_lto.enabled(); // If this assertion triggers, there's something wrong with commandline // argument validation. @@ -406,7 +430,7 @@ impl<'ll> CodegenCx<'ll, '_> { let val_llty = self.val_ty(v); let g = self.get_static_inner(def_id, val_llty); - let llty = llvm::LLVMGlobalGetValueType(g); + let llty = self.get_type_of_global(g); let g = if val_llty == llty { g @@ -490,7 +514,7 @@ impl<'ll> CodegenCx<'ll, '_> { llvm::LLVMMDStringInContext2(self.llcx, bytes.as_c_char_ptr(), bytes.len()); let data = [section, alloc]; let meta = llvm::LLVMMDNodeInContext2(self.llcx, data.as_ptr(), data.len()); - let val = llvm::LLVMMetadataAsValue(self.llcx, meta); + let val = self.get_metadata_value(meta); llvm::LLVMAddNamedMetadataOperand( self.llmod, c"wasm.custom_sections".as_ptr(), diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index ed8426ae19746..b0d8e11d1fb57 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1,10 +1,11 @@ use std::borrow::Borrow; use std::cell::{Cell, RefCell}; use std::ffi::{CStr, c_char, c_uint}; +use std::marker::PhantomData; use std::ops::Deref; use std::str; -use rustc_abi::{HasDataLayout, TargetDataLayout, VariantIdx}; +use rustc_abi::{HasDataLayout, Size, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh}; use rustc_codegen_ssa::common::TypeKind; @@ -27,34 +28,36 @@ use rustc_session::config::{ }; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Span}; +use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel}; use smallvec::SmallVec; use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; -use crate::common::{self, AsCCharPtr}; +use crate::common::AsCCharPtr; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; -use crate::llvm::{Metadata, MetadataType}; +use crate::llvm::Metadata; use crate::type_::Type; use crate::value::Value; -use crate::{attributes, coverageinfo, debuginfo, llvm, llvm_util}; +use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util}; /// `TyCtxt` (and related cache datastructures) can't be move between threads. /// However, there are various cx related functions which we want to be available to the builder and /// other compiler pieces. Here we define a small subset which has enough information and can be /// moved around more freely. -pub(crate) struct SimpleCx<'ll> { +pub(crate) struct SCx<'ll> { pub llmod: &'ll llvm::Module, pub llcx: &'ll llvm::Context, + pub isize_ty: &'ll Type, } -impl<'ll> Borrow> for CodegenCx<'ll, '_> { - fn borrow(&self) -> &SimpleCx<'ll> { +impl<'ll> Borrow> for FullCx<'ll, '_> { + fn borrow(&self) -> &SCx<'ll> { &self.scx } } -impl<'ll, 'tcx> Deref for CodegenCx<'ll, 'tcx> { +impl<'ll, 'tcx> Deref for FullCx<'ll, 'tcx> { type Target = SimpleCx<'ll>; #[inline] @@ -63,10 +66,25 @@ impl<'ll, 'tcx> Deref for CodegenCx<'ll, 'tcx> { } } +pub(crate) struct GenericCx<'ll, T: Borrow>>(T, PhantomData>); + +impl<'ll, T: Borrow>> Deref for GenericCx<'ll, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub(crate) type SimpleCx<'ll> = GenericCx<'ll, SCx<'ll>>; + /// There is one `CodegenCx` per codegen unit. Each one has its own LLVM /// `llvm::Context` so that several codegen units may be processed in parallel. /// All other LLVM data structures in the `CodegenCx` are tied to that `llvm::Context`. -pub(crate) struct CodegenCx<'ll, 'tcx> { +pub(crate) type CodegenCx<'ll, 'tcx> = GenericCx<'ll, FullCx<'ll, 'tcx>>; + +pub(crate) struct FullCx<'ll, 'tcx> { pub tcx: TyCtxt<'tcx>, pub scx: SimpleCx<'ll>, pub use_dll_storage_attrs: bool, @@ -104,8 +122,6 @@ pub(crate) struct CodegenCx<'ll, 'tcx> { /// Mapping of scalar types to llvm types. pub scalar_lltypes: RefCell, &'ll Type>>, - pub isize_ty: &'ll Type, - /// Extra per-CGU codegen state needed when coverage instrumentation is enabled. pub coverage_cx: Option>, pub dbg_cx: Option>, @@ -148,23 +164,6 @@ pub(crate) unsafe fn create_module<'ll>( let mut target_data_layout = sess.target.data_layout.to_string(); let llvm_version = llvm_util::get_version(); - if llvm_version < (19, 0, 0) { - if sess.target.arch == "aarch64" || sess.target.arch.starts_with("arm64") { - // LLVM 19 sets -Fn32 in its data layout string for 64-bit ARM - // Earlier LLVMs leave this default, so remove it. - // See https://github.com/llvm/llvm-project/pull/90702 - target_data_layout = target_data_layout.replace("-Fn32", ""); - } - } - - if llvm_version < (19, 0, 0) { - if sess.target.arch == "loongarch64" { - // LLVM 19 updates the LoongArch64 data layout. - // See https://github.com/llvm/llvm-project/pull/93814 - target_data_layout = target_data_layout.replace("-n32:64", "-n64"); - } - } - if llvm_version < (20, 0, 0) { if sess.target.arch == "aarch64" || sess.target.arch.starts_with("arm64") { // LLVM 20 defines three additional address spaces for alternate @@ -312,6 +311,22 @@ pub(crate) unsafe fn create_module<'ll>( pfe.prefix().into(), ); } + + // Add "kcfi-arity" module flag if KCFI arity indicator is enabled. (See + // https://github.com/llvm/llvm-project/pull/117121.) + if sess.is_sanitizer_kcfi_arity_enabled() { + // KCFI arity indicator requires LLVM 21.0.0 or later. + if llvm_version < (21, 0, 0) { + tcx.dcx().emit_err(crate::errors::SanitizerKcfiArityRequiresLLVM2100); + } + + llvm::add_module_flag_u32( + llmod, + llvm::ModuleFlagMergeBehavior::Override, + "kcfi-arity", + 1, + ); + } } // Control Flow Guard is currently only supported by MSVC and LLVM on Windows. @@ -579,33 +594,33 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { None }; - let isize_ty = Type::ix_llcx(llcx, tcx.data_layout.pointer_size.bits()); - - CodegenCx { - tcx, - scx: SimpleCx { llcx, llmod }, - use_dll_storage_attrs, - tls_model, - codegen_unit, - instances: Default::default(), - vtables: Default::default(), - const_str_cache: Default::default(), - const_globals: Default::default(), - statics_to_rauw: RefCell::new(Vec::new()), - used_statics: RefCell::new(Vec::new()), - compiler_used_statics: RefCell::new(Vec::new()), - type_lowering: Default::default(), - scalar_lltypes: Default::default(), - isize_ty, - coverage_cx, - dbg_cx, - eh_personality: Cell::new(None), - eh_catch_typeinfo: Cell::new(None), - rust_try_fn: Cell::new(None), - intrinsics: Default::default(), - local_gen_sym_counter: Cell::new(0), - renamed_statics: Default::default(), - } + GenericCx( + FullCx { + tcx, + scx: SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size), + use_dll_storage_attrs, + tls_model, + codegen_unit, + instances: Default::default(), + vtables: Default::default(), + const_str_cache: Default::default(), + const_globals: Default::default(), + statics_to_rauw: RefCell::new(Vec::new()), + used_statics: RefCell::new(Vec::new()), + compiler_used_statics: RefCell::new(Vec::new()), + type_lowering: Default::default(), + scalar_lltypes: Default::default(), + coverage_cx, + dbg_cx, + eh_personality: Cell::new(None), + eh_catch_typeinfo: Cell::new(None), + rust_try_fn: Cell::new(None), + intrinsics: Default::default(), + local_gen_sym_counter: Cell::new(0), + renamed_statics: Default::default(), + }, + PhantomData, + ) } pub(crate) fn statics_to_rauw(&self) -> &RefCell> { @@ -629,23 +644,49 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } } impl<'ll> SimpleCx<'ll> { + pub(crate) fn get_return_type(&self, ty: &'ll Type) -> &'ll Type { + assert_eq!(self.type_kind(ty), TypeKind::Function); + unsafe { llvm::LLVMGetReturnType(ty) } + } + pub(crate) fn get_type_of_global(&self, val: &'ll Value) -> &'ll Type { + unsafe { llvm::LLVMGlobalGetValueType(val) } + } pub(crate) fn val_ty(&self, v: &'ll Value) -> &'ll Type { common::val_ty(v) } +} +impl<'ll> SimpleCx<'ll> { + pub(crate) fn new( + llmod: &'ll llvm::Module, + llcx: &'ll llvm::Context, + pointer_size: Size, + ) -> Self { + let isize_ty = llvm::Type::ix_llcx(llcx, pointer_size.bits()); + Self(SCx { llmod, llcx, isize_ty }, PhantomData) + } +} +impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { pub(crate) fn get_metadata_value(&self, metadata: &'ll Metadata) -> &'ll Value { - unsafe { llvm::LLVMMetadataAsValue(self.llcx, metadata) } + llvm::LLVMMetadataAsValue(self.llcx(), metadata) + } + + // FIXME(autodiff): We should split `ConstCodegenMethods` to pull the reusable parts + // onto a trait that is also implemented for GenericCx. + pub(crate) fn get_const_i64(&self, n: u64) -> &'ll Value { + let ty = unsafe { llvm::LLVMInt64TypeInContext(self.llcx()) }; + unsafe { llvm::LLVMConstInt(ty, n, llvm::False) } } pub(crate) fn get_function(&self, name: &str) -> Option<&'ll Value> { let name = SmallCStr::new(name); - unsafe { llvm::LLVMGetNamedFunction(self.llmod, name.as_ptr()) } + unsafe { llvm::LLVMGetNamedFunction((**self).borrow().llmod, name.as_ptr()) } } - pub(crate) fn get_md_kind_id(&self, name: &str) -> u32 { + pub(crate) fn get_md_kind_id(&self, name: &str) -> llvm::MetadataKindId { unsafe { llvm::LLVMGetMDKindIDInContext( - self.llcx, + self.llcx(), name.as_ptr() as *const c_char, name.len() as c_uint, ) @@ -654,12 +695,18 @@ impl<'ll> SimpleCx<'ll> { pub(crate) fn create_metadata(&self, name: String) -> Option<&'ll Metadata> { Some(unsafe { - llvm::LLVMMDStringInContext2(self.llcx, name.as_ptr() as *const c_char, name.len()) + llvm::LLVMMDStringInContext2(self.llcx(), name.as_ptr() as *const c_char, name.len()) }) } - pub(crate) fn type_kind(&self, ty: &'ll Type) -> TypeKind { - unsafe { llvm::LLVMRustGetTypeKind(ty).to_generic() } + pub(crate) fn get_functions(&self) -> Vec<&'ll Value> { + let mut functions = vec![]; + let mut func = unsafe { llvm::LLVMGetFirstFunction(self.llmod()) }; + while let Some(f) = func { + functions.push(f); + func = unsafe { llvm::LLVMGetNextFunction(f) } + } + functions } } @@ -962,11 +1009,27 @@ impl<'ll> CodegenCx<'ll, '_> { ifn!("llvm.minnum.f64", fn(t_f64, t_f64) -> t_f64); ifn!("llvm.minnum.f128", fn(t_f128, t_f128) -> t_f128); + ifn!("llvm.minimum.f16", fn(t_f16, t_f16) -> t_f16); + ifn!("llvm.minimum.f32", fn(t_f32, t_f32) -> t_f32); + ifn!("llvm.minimum.f64", fn(t_f64, t_f64) -> t_f64); + // There are issues on x86_64 and aarch64 with the f128 variant. + // - https://github.com/llvm/llvm-project/issues/139380 + // - https://github.com/llvm/llvm-project/issues/139381 + // ifn!("llvm.minimum.f128", fn(t_f128, t_f128) -> t_f128); + ifn!("llvm.maxnum.f16", fn(t_f16, t_f16) -> t_f16); ifn!("llvm.maxnum.f32", fn(t_f32, t_f32) -> t_f32); ifn!("llvm.maxnum.f64", fn(t_f64, t_f64) -> t_f64); ifn!("llvm.maxnum.f128", fn(t_f128, t_f128) -> t_f128); + ifn!("llvm.maximum.f16", fn(t_f16, t_f16) -> t_f16); + ifn!("llvm.maximum.f32", fn(t_f32, t_f32) -> t_f32); + ifn!("llvm.maximum.f64", fn(t_f64, t_f64) -> t_f64); + // There are issues on x86_64 and aarch64 with the f128 variant. + // - https://github.com/llvm/llvm-project/issues/139380 + // - https://github.com/llvm/llvm-project/issues/139381 + // ifn!("llvm.maximum.f128", fn(t_f128, t_f128) -> t_f128); + ifn!("llvm.floor.f16", fn(t_f16) -> t_f16); ifn!("llvm.floor.f32", fn(t_f32) -> t_f32); ifn!("llvm.floor.f64", fn(t_f64) -> t_f64); @@ -1108,6 +1171,18 @@ impl<'ll> CodegenCx<'ll, '_> { ifn!("llvm.usub.sat.i64", fn(t_i64, t_i64) -> t_i64); ifn!("llvm.usub.sat.i128", fn(t_i128, t_i128) -> t_i128); + ifn!("llvm.scmp.i8.i8", fn(t_i8, t_i8) -> t_i8); + ifn!("llvm.scmp.i8.i16", fn(t_i16, t_i16) -> t_i8); + ifn!("llvm.scmp.i8.i32", fn(t_i32, t_i32) -> t_i8); + ifn!("llvm.scmp.i8.i64", fn(t_i64, t_i64) -> t_i8); + ifn!("llvm.scmp.i8.i128", fn(t_i128, t_i128) -> t_i8); + + ifn!("llvm.ucmp.i8.i8", fn(t_i8, t_i8) -> t_i8); + ifn!("llvm.ucmp.i8.i16", fn(t_i16, t_i16) -> t_i8); + ifn!("llvm.ucmp.i8.i32", fn(t_i32, t_i32) -> t_i8); + ifn!("llvm.ucmp.i8.i64", fn(t_i64, t_i64) -> t_i8); + ifn!("llvm.ucmp.i8.i128", fn(t_i128, t_i128) -> t_i8); + ifn!("llvm.lifetime.start.p0i8", fn(t_i64, ptr) -> void); ifn!("llvm.lifetime.end.p0i8", fn(t_i64, ptr) -> void); @@ -1152,10 +1227,8 @@ impl<'ll> CodegenCx<'ll, '_> { if self.sess().instrument_coverage() { ifn!("llvm.instrprof.increment", fn(ptr, t_i64, t_i32, t_i32) -> void); - if crate::llvm_util::get_version() >= (19, 0, 0) { - ifn!("llvm.instrprof.mcdc.parameters", fn(ptr, t_i64, t_i32) -> void); - ifn!("llvm.instrprof.mcdc.tvbitmap.update", fn(ptr, t_i64, t_i32, ptr) -> void); - } + ifn!("llvm.instrprof.mcdc.parameters", fn(ptr, t_i64, t_i32) -> void); + ifn!("llvm.instrprof.mcdc.tvbitmap.update", fn(ptr, t_i64, t_i32, ptr) -> void); } ifn!("llvm.type.test", fn(ptr, t_metadata) -> i1); @@ -1181,7 +1254,7 @@ impl<'ll> CodegenCx<'ll, '_> { Some(def_id) => self.get_static(def_id), _ => { let ty = self.type_struct(&[self.type_ptr(), self.type_ptr()], false); - self.declare_global("rust_eh_catch_typeinfo", ty) + self.declare_global(&mangle_internal_symbol(self.tcx, "rust_eh_catch_typeinfo"), ty) } }; self.eh_catch_typeinfo.set(Some(eh_catch_typeinfo)); @@ -1203,27 +1276,18 @@ impl CodegenCx<'_, '_> { name.push_str(&(idx as u64).to_base(ALPHANUMERIC_ONLY)); name } - - /// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`. - pub(crate) fn set_metadata<'a>(&self, val: &'a Value, kind_id: MetadataType, md: &'a Metadata) { - unsafe { - let node = llvm::LLVMMetadataAsValue(&self.llcx, md); - llvm::LLVMSetMetadata(val, kind_id as c_uint, node); - } - } } -// This is a duplication of the set_metadata function above. However, so far it's the only one -// shared between both contexts, so it doesn't seem worth it to make the Cx generic like we did it -// for the Builder. -impl SimpleCx<'_> { - #[allow(unused)] +impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { /// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`. - pub(crate) fn set_metadata<'a>(&self, val: &'a Value, kind_id: MetadataType, md: &'a Metadata) { - unsafe { - let node = llvm::LLVMMetadataAsValue(&self.llcx, md); - llvm::LLVMSetMetadata(val, kind_id as c_uint, node); - } + pub(crate) fn set_metadata<'a>( + &self, + val: &'a Value, + kind_id: impl Into, + md: &'ll Metadata, + ) { + let node = self.get_metadata_value(md); + llvm::LLVMSetMetadata(val, kind_id.into(), node); } } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index b617f4d37f5bf..c207df2fb0b45 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -146,6 +146,7 @@ pub(crate) struct CoverageSpan { #[derive(Clone, Debug, Default)] pub(crate) struct Regions { pub(crate) code_regions: Vec, + pub(crate) expansion_regions: Vec, pub(crate) branch_regions: Vec, pub(crate) mcdc_branch_regions: Vec, pub(crate) mcdc_decision_regions: Vec, @@ -154,13 +155,35 @@ pub(crate) struct Regions { impl Regions { /// Returns true if none of this structure's tables contain any regions. pub(crate) fn has_no_regions(&self) -> bool { - let Self { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } = - self; + // Every region has a span, so if there are no spans then there are no regions. + self.all_cov_spans().next().is_none() + } - code_regions.is_empty() - && branch_regions.is_empty() - && mcdc_branch_regions.is_empty() - && mcdc_decision_regions.is_empty() + pub(crate) fn all_cov_spans(&self) -> impl Iterator { + macro_rules! iter_cov_spans { + ( $( $regions:expr ),* $(,)? ) => { + std::iter::empty() + $( + .chain( $regions.iter().map(|region| ®ion.cov_span) ) + )* + } + } + + let Self { + code_regions, + expansion_regions, + branch_regions, + mcdc_branch_regions, + mcdc_decision_regions, + } = self; + + iter_cov_spans!( + code_regions, + expansion_regions, + branch_regions, + mcdc_branch_regions, + mcdc_decision_regions, + ) } } @@ -172,6 +195,14 @@ pub(crate) struct CodeRegion { pub(crate) counter: Counter, } +/// Must match the layout of `LLVMRustCoverageExpansionRegion`. +#[derive(Clone, Debug)] +#[repr(C)] +pub(crate) struct ExpansionRegion { + pub(crate) cov_span: CoverageSpan, + pub(crate) expanded_file_id: u32, +} + /// Must match the layout of `LLVMRustCoverageBranchRegion`. #[derive(Clone, Debug)] #[repr(C)] diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs index 2cd7fa3225acd..907d6d41a1fb5 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs @@ -63,8 +63,18 @@ pub(crate) fn write_function_mappings_to_buffer( expressions: &[ffi::CounterExpression], regions: &ffi::Regions, ) -> Vec { - let ffi::Regions { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } = - regions; + let ffi::Regions { + code_regions, + expansion_regions, + branch_regions, + mcdc_branch_regions, + mcdc_decision_regions, + } = regions; + + // SAFETY: + // - All types are FFI-compatible and have matching representations in Rust/C++. + // - For pointer/length pairs, the pointer and length come from the same vector or slice. + // - C++ code does not retain any pointers after the call returns. llvm::build_byte_buffer(|buffer| unsafe { llvm::LLVMRustCoverageWriteFunctionMappingsToBuffer( virtual_file_mapping.as_ptr(), @@ -73,6 +83,8 @@ pub(crate) fn write_function_mappings_to_buffer( expressions.len(), code_regions.as_ptr(), code_regions.len(), + expansion_regions.as_ptr(), + expansion_regions.len(), branch_regions.as_ptr(), branch_regions.len(), mcdc_branch_regions.as_ptr(), diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 9a2473d6cf23d..55b1e728b70db 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -5,15 +5,11 @@ use rustc_abi::Align; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_data_structures::fx::FxIndexMap; use rustc_index::IndexVec; -use rustc_middle::mir; -use rustc_middle::mir::mono::MonoItemPartitions; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_session::RemapFileNameExt; use rustc_session::config::RemapPathScopeComponents; -use rustc_span::def_id::DefIdSet; use rustc_span::{SourceFile, StableSourceFileId}; use tracing::debug; @@ -24,6 +20,7 @@ use crate::llvm; mod covfun; mod spans; +mod unused; /// Generates and exports the coverage map, which is embedded in special /// linker sections in the final binary. @@ -56,13 +53,6 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { None => return, }; - // The order of entries in this global file table needs to be deterministic, - // and ideally should also be independent of the details of stable-hashing, - // because coverage tests snapshots (`.cov-map`) can observe the order and - // would need to be re-blessed if it changes. As long as those requirements - // are satisfied, the order can be arbitrary. - let mut global_file_table = GlobalFileTable::new(); - let mut covfun_records = instances_used .iter() .copied() @@ -70,18 +60,13 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { // order that doesn't depend on the stable-hash-based order in which // instances were visited during codegen. .sorted_by_cached_key(|&instance| tcx.symbol_name(instance).name) - .filter_map(|instance| prepare_covfun_record(tcx, &mut global_file_table, instance, true)) + .filter_map(|instance| prepare_covfun_record(tcx, instance, true)) .collect::>(); // In a single designated CGU, also prepare covfun records for functions // in this crate that were instrumented for coverage, but are unused. if cx.codegen_unit.is_code_coverage_dead_code_cgu() { - let mut unused_instances = gather_unused_function_instances(cx); - // Sort the unused instances by symbol name, for the same reason as the used ones. - unused_instances.sort_by_cached_key(|&instance| tcx.symbol_name(instance).name); - covfun_records.extend(unused_instances.into_iter().filter_map(|instance| { - prepare_covfun_record(tcx, &mut global_file_table, instance, false) - })); + unused::prepare_covfun_records_for_unused_functions(cx, &mut covfun_records); } // If there are no covfun records for this CGU, don't generate a covmap record. @@ -93,91 +78,88 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { return; } - // Encode all filenames referenced by coverage mappings in this CGU. - let filenames_buffer = global_file_table.make_filenames_buffer(tcx); - // The `llvm-cov` tool uses this hash to associate each covfun record with - // its corresponding filenames table, since the final binary will typically - // contain multiple covmap records from different compilation units. - let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer); - - let mut unused_function_names = vec![]; + // Prepare the global file table for this CGU, containing all paths needed + // by one or more covfun records. + let global_file_table = + GlobalFileTable::build(tcx, covfun_records.iter().flat_map(|c| c.all_source_files())); for covfun in &covfun_records { - unused_function_names.extend(covfun.mangled_function_name_if_unused()); - - covfun::generate_covfun_record(cx, filenames_hash, covfun) - } - - // For unused functions, we need to take their mangled names and store them - // in a specially-named global array. LLVM's `InstrProfiling` pass will - // detect this global and include those names in its `__llvm_prf_names` - // section. (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.) - if !unused_function_names.is_empty() { - assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu()); - - let name_globals = unused_function_names - .into_iter() - .map(|mangled_function_name| cx.const_str(mangled_function_name).0) - .collect::>(); - let initializer = cx.const_array(cx.type_ptr(), &name_globals); - - let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), c"__llvm_coverage_names"); - llvm::set_global_constant(array, true); - llvm::set_linkage(array, llvm::Linkage::InternalLinkage); - llvm::set_initializer(array, initializer); + covfun::generate_covfun_record(cx, &global_file_table, covfun) } // Generate the coverage map header, which contains the filenames used by // this CGU's coverage mappings, and store it in a well-known global. // (This is skipped if we returned early due to having no covfun records.) - generate_covmap_record(cx, covmap_version, &filenames_buffer); + generate_covmap_record(cx, covmap_version, &global_file_table.filenames_buffer); } -/// Maps "global" (per-CGU) file ID numbers to their underlying source files. +/// Maps "global" (per-CGU) file ID numbers to their underlying source file paths. +#[derive(Debug)] struct GlobalFileTable { /// This "raw" table doesn't include the working dir, so a file's /// global ID is its index in this set **plus one**. - raw_file_table: FxIndexMap>, + raw_file_table: FxIndexMap, + + /// The file table in encoded form (possibly compressed), which can be + /// included directly in this CGU's `__llvm_covmap` record. + filenames_buffer: Vec, + + /// Truncated hash of the bytes in `filenames_buffer`. + /// + /// The `llvm-cov` tool uses this hash to associate each covfun record with + /// its corresponding filenames table, since the final binary will typically + /// contain multiple covmap records from different compilation units. + filenames_hash: u64, } impl GlobalFileTable { - fn new() -> Self { - Self { raw_file_table: FxIndexMap::default() } - } + /// Builds a "global file table" for this CGU, mapping numeric IDs to + /// path strings. + fn build<'a>(tcx: TyCtxt<'_>, all_files: impl Iterator) -> Self { + let mut raw_file_table = FxIndexMap::default(); + + for file in all_files { + raw_file_table.entry(file.stable_id).or_insert_with(|| { + file.name + .for_scope(tcx.sess, RemapPathScopeComponents::MACRO) + .to_string_lossy() + .into_owned() + }); + } - fn global_file_id_for_file(&mut self, file: &Arc) -> GlobalFileId { - // Ensure the given file has a table entry, and get its index. - let entry = self.raw_file_table.entry(file.stable_id); - let raw_id = entry.index(); - entry.or_insert_with(|| Arc::clone(file)); + // FIXME(Zalathar): Consider sorting the file table here, but maybe + // only after adding filename support to coverage-dump, so that the + // table order isn't directly visible in `.coverage-map` snapshots. - // The raw file table doesn't include an entry for the working dir - // (which has ID 0), so add 1 to get the correct ID. - GlobalFileId::from_usize(raw_id + 1) - } + let mut table = Vec::with_capacity(raw_file_table.len() + 1); - fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec { - let mut table = Vec::with_capacity(self.raw_file_table.len() + 1); - - // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5) - // requires setting the first filename to the compilation directory. - // Since rustc generates coverage maps with relative paths, the - // compilation directory can be combined with the relative paths - // to get absolute paths, if needed. - table.push( - tcx.sess - .opts - .working_dir - .for_scope(tcx.sess, RemapPathScopeComponents::MACRO) - .to_string_lossy(), - ); + // Since version 6 of the LLVM coverage mapping format, the first entry + // in the global file table is treated as a base directory, used to + // resolve any other entries that are stored as relative paths. + let base_dir = tcx + .sess + .opts + .working_dir + .for_scope(tcx.sess, RemapPathScopeComponents::MACRO) + .to_string_lossy(); + table.push(base_dir.as_ref()); // Add the regular entries after the base directory. - table.extend(self.raw_file_table.values().map(|file| { - file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy() - })); + table.extend(raw_file_table.values().map(|name| name.as_str())); - llvm_cov::write_filenames_to_buffer(&table) + // Encode the file table into a buffer, and get the hash of its encoded + // bytes, so that we can embed that hash in `__llvm_covfun` records. + let filenames_buffer = llvm_cov::write_filenames_to_buffer(&table); + let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer); + + Self { raw_file_table, filenames_buffer, filenames_hash } + } + + fn get_existing_id(&self, file: &SourceFile) -> Option { + let raw_id = self.raw_file_table.get_index_of(&file.stable_id)?; + // The raw file table doesn't include an entry for the base dir + // (which has ID 0), so add 1 to get the correct ID. + Some(GlobalFileId::from_usize(raw_id + 1)) } } @@ -193,26 +175,31 @@ rustc_index::newtype_index! { struct LocalFileId {} } -/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU) -/// file IDs. +/// Holds a mapping from "local" (per-function) file IDs to their corresponding +/// source files. #[derive(Debug, Default)] struct VirtualFileMapping { - local_to_global: IndexVec, - global_to_local: FxIndexMap, + local_file_table: IndexVec>, } impl VirtualFileMapping { - fn local_id_for_global(&mut self, global_file_id: GlobalFileId) -> LocalFileId { - *self - .global_to_local - .entry(global_file_id) - .or_insert_with(|| self.local_to_global.push(global_file_id)) + fn push_file(&mut self, source_file: &Arc) -> LocalFileId { + self.local_file_table.push(Arc::clone(source_file)) } - fn to_vec(&self) -> Vec { - // This clone could be avoided by transmuting `&[GlobalFileId]` to `&[u32]`, - // but it isn't hot or expensive enough to justify the extra unsafety. - self.local_to_global.iter().map(|&global| GlobalFileId::as_u32(global)).collect() + /// Resolves all of the filenames in this local file mapping to a list of + /// global file IDs in its CGU, for inclusion in this function's + /// `__llvm_covfun` record. + /// + /// The global file IDs are returned as `u32` to make FFI easier. + fn resolve_all(&self, global_file_table: &GlobalFileTable) -> Option> { + self.local_file_table + .iter() + .map(|file| try { + let id = global_file_table.get_existing_id(file)?; + GlobalFileId::as_u32(id) + }) + .collect::>>() } } @@ -249,121 +236,3 @@ fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_ cx.add_used_global(covmap_global); } - -/// Each CGU will normally only emit coverage metadata for the functions that it actually generates. -/// But since we don't want unused functions to disappear from coverage reports, we also scan for -/// functions that were instrumented but are not participating in codegen. -/// -/// These unused functions don't need to be codegenned, but we do need to add them to the function -/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them. -/// We also end up adding their symbol names to a special global array that LLVM will include in -/// its embedded coverage data. -fn gather_unused_function_instances<'tcx>(cx: &CodegenCx<'_, 'tcx>) -> Vec> { - assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu()); - - let tcx = cx.tcx; - let usage = prepare_usage_sets(tcx); - - let is_unused_fn = |def_id: LocalDefId| -> bool { - // Usage sets expect `DefId`, so convert from `LocalDefId`. - let d: DefId = LocalDefId::to_def_id(def_id); - // To be potentially eligible for "unused function" mappings, a definition must: - // - Be eligible for coverage instrumentation - // - Not participate directly in codegen (or have lost all its coverage statements) - // - Not have any coverage statements inlined into codegenned functions - tcx.is_eligible_for_coverage(def_id) - && (!usage.all_mono_items.contains(&d) || usage.missing_own_coverage.contains(&d)) - && !usage.used_via_inlining.contains(&d) - }; - - // FIXME(#79651): Consider trying to filter out dummy instantiations of - // unused generic functions from library crates, because they can produce - // "unused instantiation" in coverage reports even when they are actually - // used by some downstream crate in the same binary. - - tcx.mir_keys(()) - .iter() - .copied() - .filter(|&def_id| is_unused_fn(def_id)) - .map(|def_id| make_dummy_instance(tcx, def_id)) - .collect::>() -} - -struct UsageSets<'tcx> { - all_mono_items: &'tcx DefIdSet, - used_via_inlining: FxHashSet, - missing_own_coverage: FxHashSet, -} - -/// Prepare sets of definitions that are relevant to deciding whether something -/// is an "unused function" for coverage purposes. -fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> { - let MonoItemPartitions { all_mono_items, codegen_units, .. } = - tcx.collect_and_partition_mono_items(()); - - // Obtain a MIR body for each function participating in codegen, via an - // arbitrary instance. - let mut def_ids_seen = FxHashSet::default(); - let def_and_mir_for_all_mono_fns = codegen_units - .iter() - .flat_map(|cgu| cgu.items().keys()) - .filter_map(|item| match item { - mir::mono::MonoItem::Fn(instance) => Some(instance), - mir::mono::MonoItem::Static(_) | mir::mono::MonoItem::GlobalAsm(_) => None, - }) - // We only need one arbitrary instance per definition. - .filter(move |instance| def_ids_seen.insert(instance.def_id())) - .map(|instance| { - // We don't care about the instance, just its underlying MIR. - let body = tcx.instance_mir(instance.def); - (instance.def_id(), body) - }); - - // Functions whose coverage statements were found inlined into other functions. - let mut used_via_inlining = FxHashSet::default(); - // Functions that were instrumented, but had all of their coverage statements - // removed by later MIR transforms (e.g. UnreachablePropagation). - let mut missing_own_coverage = FxHashSet::default(); - - for (def_id, body) in def_and_mir_for_all_mono_fns { - let mut saw_own_coverage = false; - - // Inspect every coverage statement in the function's MIR. - for stmt in body - .basic_blocks - .iter() - .flat_map(|block| &block.statements) - .filter(|stmt| matches!(stmt.kind, mir::StatementKind::Coverage(_))) - { - if let Some(inlined) = stmt.source_info.scope.inlined_instance(&body.source_scopes) { - // This coverage statement was inlined from another function. - used_via_inlining.insert(inlined.def_id()); - } else { - // Non-inlined coverage statements belong to the enclosing function. - saw_own_coverage = true; - } - } - - if !saw_own_coverage && body.function_coverage_info.is_some() { - missing_own_coverage.insert(def_id); - } - } - - UsageSets { all_mono_items, used_via_inlining, missing_own_coverage } -} - -fn make_dummy_instance<'tcx>(tcx: TyCtxt<'tcx>, local_def_id: LocalDefId) -> ty::Instance<'tcx> { - let def_id = local_def_id.to_def_id(); - - // Make a dummy instance that fills in all generics with placeholders. - ty::Instance::new( - def_id, - ty::GenericArgs::for_item(tcx, def_id, |param, _| { - if let ty::GenericParamDefKind::Lifetime = param.kind { - tcx.lifetimes.re_erased.into() - } else { - tcx.mk_param_from_def(param) - } - }), - ) -} diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs index c53ea6d466610..d3a815fabe7a9 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs @@ -5,17 +5,19 @@ //! [^win]: On Windows the section name is `.lcovfun`. use std::ffi::CString; +use std::sync::Arc; use rustc_abi::Align; use rustc_codegen_ssa::traits::{ - BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, + BaseTypeCodegenMethods as _, ConstCodegenMethods, StaticCodegenMethods, }; +use rustc_index::IndexVec; use rustc_middle::mir::coverage::{ BasicCoverageBlock, CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op, }; use rustc_middle::ty::{Instance, TyCtxt}; -use rustc_span::Span; +use rustc_span::{SourceFile, Span}; use rustc_target::spec::HasTargetSpec; use tracing::debug; @@ -38,16 +40,15 @@ pub(crate) struct CovfunRecord<'tcx> { } impl<'tcx> CovfunRecord<'tcx> { - /// FIXME(Zalathar): Make this the responsibility of the code that determines - /// which functions are unused. - pub(crate) fn mangled_function_name_if_unused(&self) -> Option<&'tcx str> { - (!self.is_used).then_some(self.mangled_function_name) + /// Iterator that yields all source files referred to by this function's + /// coverage mappings. Used to build the global file table for the CGU. + pub(crate) fn all_source_files(&self) -> impl Iterator { + self.virtual_file_mapping.local_file_table.iter().map(Arc::as_ref) } } pub(crate) fn prepare_covfun_record<'tcx>( tcx: TyCtxt<'tcx>, - global_file_table: &mut GlobalFileTable, instance: Instance<'tcx>, is_used: bool, ) -> Option> { @@ -65,7 +66,7 @@ pub(crate) fn prepare_covfun_record<'tcx>( regions: ffi::Regions::default(), }; - fill_region_tables(tcx, global_file_table, fn_cov_info, ids_info, &mut covfun); + fill_region_tables(tcx, fn_cov_info, ids_info, &mut covfun); if covfun.regions.has_no_regions() { debug!(?covfun, "function has no mappings to embed; skipping"); @@ -100,53 +101,59 @@ fn prepare_expressions(ids_info: &CoverageIdsInfo) -> Vec( tcx: TyCtxt<'tcx>, - global_file_table: &mut GlobalFileTable, fn_cov_info: &'tcx FunctionCoverageInfo, ids_info: &'tcx CoverageIdsInfo, covfun: &mut CovfunRecord<'tcx>, ) { - // Currently a function's mappings must all be in the same file as its body span. - let source_map = tcx.sess.source_map(); - let source_file = source_map.lookup_source_file(fn_cov_info.body_span.lo()); + // If this function is unused, replace all counters with zero. + let counter_for_bcb = |bcb: BasicCoverageBlock| -> ffi::Counter { + let term = if covfun.is_used { + ids_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term") + } else { + CovTerm::Zero + }; + ffi::Counter::from_term(term) + }; - // Look up the global file ID for that file. - let global_file_id = global_file_table.global_file_id_for_file(&source_file); + // Currently a function's mappings must all be in the same file, so use the + // first mapping's span to determine the file. + let source_map = tcx.sess.source_map(); + let Some(first_span) = (try { fn_cov_info.mappings.first()?.span }) else { + debug_assert!(false, "function has no mappings: {:?}", covfun.mangled_function_name); + return; + }; + let source_file = source_map.lookup_source_file(first_span.lo()); - // Associate that global file ID with a local file ID for this function. - let local_file_id = covfun.virtual_file_mapping.local_id_for_global(global_file_id); + let local_file_id = covfun.virtual_file_mapping.push_file(&source_file); - let ffi::Regions { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } = - &mut covfun.regions; + // If this testing flag is set, add an extra unused entry to the local + // file table, to help test the code for detecting unused file IDs. + if tcx.sess.coverage_inject_unused_local_file() { + covfun.virtual_file_mapping.push_file(&source_file); + } - let make_cov_span = |span: Span| { - spans::make_coverage_span(local_file_id, source_map, fn_cov_info, &source_file, span) - }; + // In rare cases, _all_ of a function's spans are discarded, and coverage + // codegen needs to handle that gracefully to avoid #133606. + // It's hard for tests to trigger this organically, so instead we set + // `-Zcoverage-options=discard-all-spans-in-codegen` to force it to occur. let discard_all = tcx.sess.coverage_discard_all_spans_in_codegen(); + let make_coords = |span: Span| { + if discard_all { None } else { spans::make_coords(source_map, &source_file, span) } + }; + + let ffi::Regions { + code_regions, + expansion_regions: _, // FIXME(Zalathar): Fill out support for expansion regions + branch_regions, + mcdc_branch_regions, + mcdc_decision_regions, + } = &mut covfun.regions; // For each counter/region pair in this function+file, convert it to a // form suitable for FFI. for &Mapping { ref kind, span } in &fn_cov_info.mappings { - // If this function is unused, replace all counters with zero. - let counter_for_bcb = |bcb: BasicCoverageBlock| -> ffi::Counter { - let term = if covfun.is_used { - ids_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term") - } else { - CovTerm::Zero - }; - ffi::Counter::from_term(term) - }; - - // Convert the `Span` into coordinates that we can pass to LLVM, or - // discard the span if conversion fails. In rare, cases _all_ of a - // function's spans are discarded, and the rest of coverage codegen - // needs to handle that gracefully to avoid a repeat of #133606. - // We don't have a good test case for triggering that organically, so - // instead we set `-Zcoverage-options=discard-all-spans-in-codegen` - // to force it to occur. - let Some(cov_span) = make_cov_span(span) else { continue }; - if discard_all { - continue; - } + let Some(coords) = make_coords(span) else { continue }; + let cov_span = coords.make_coverage_span(local_file_id); match *kind { MappingKind::Code { bcb } => { @@ -177,14 +184,31 @@ fn fill_region_tables<'tcx>( } } +/// LLVM requires all local file IDs to have at least one mapping region. +/// If that's not the case, skip this function, to avoid an assertion failure +/// (or worse) in LLVM. +fn check_local_file_table(covfun: &CovfunRecord<'_>) -> bool { + let mut local_file_id_seen = + IndexVec::::from_elem_n(false, covfun.virtual_file_mapping.local_file_table.len()); + for cov_span in covfun.regions.all_cov_spans() { + local_file_id_seen[cov_span.file_id] = true; + } + + local_file_id_seen.into_iter().all(|seen| seen) +} + /// Generates the contents of the covfun record for this function, which /// contains the function's coverage mapping data. The record is then stored /// as a global variable in the `__llvm_covfun` section. pub(crate) fn generate_covfun_record<'tcx>( cx: &CodegenCx<'_, 'tcx>, - filenames_hash: u64, + global_file_table: &GlobalFileTable, covfun: &CovfunRecord<'tcx>, ) { + if !check_local_file_table(covfun) { + return; + } + let &CovfunRecord { mangled_function_name, source_hash, @@ -194,12 +218,19 @@ pub(crate) fn generate_covfun_record<'tcx>( ref regions, } = covfun; + let Some(local_file_table) = virtual_file_mapping.resolve_all(global_file_table) else { + debug_assert!( + false, + "all local files should be present in the global file table: \ + global_file_table = {global_file_table:?}, \ + virtual_file_mapping = {virtual_file_mapping:?}" + ); + return; + }; + // Encode the function's coverage mappings into a buffer. - let coverage_mapping_buffer = llvm_cov::write_function_mappings_to_buffer( - &virtual_file_mapping.to_vec(), - expressions, - regions, - ); + let coverage_mapping_buffer = + llvm_cov::write_function_mappings_to_buffer(&local_file_table, expressions, regions); // A covfun record consists of four target-endian integers, followed by the // encoded mapping data in bytes. Note that the length field is 32 bits. @@ -212,7 +243,7 @@ pub(crate) fn generate_covfun_record<'tcx>( cx.const_u64(func_name_hash), cx.const_u32(coverage_mapping_buffer.len() as u32), cx.const_u64(source_hash), - cx.const_u64(filenames_hash), + cx.const_u64(global_file_table.filenames_hash), cx.const_bytes(&coverage_mapping_buffer), ], // This struct needs to be packed, so that the 32-bit length field diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs index 6d1d91340c295..574463be7ffe0 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs @@ -1,4 +1,3 @@ -use rustc_middle::mir::coverage::FunctionCoverageInfo; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Pos, SourceFile, Span}; use tracing::debug; @@ -6,24 +5,44 @@ use tracing::debug; use crate::coverageinfo::ffi; use crate::coverageinfo::mapgen::LocalFileId; +/// Line and byte-column coordinates of a source code span within some file. +/// The file itself must be tracked separately. +#[derive(Clone, Copy, Debug)] +pub(crate) struct Coords { + /// 1-based starting line of the source code span. + pub(crate) start_line: u32, + /// 1-based starting column (in bytes) of the source code span. + pub(crate) start_col: u32, + /// 1-based ending line of the source code span. + pub(crate) end_line: u32, + /// 1-based ending column (in bytes) of the source code span. High bit must be unset. + pub(crate) end_col: u32, +} + +impl Coords { + /// Attaches a local file ID to these coordinates to produce an `ffi::CoverageSpan`. + pub(crate) fn make_coverage_span(&self, local_file_id: LocalFileId) -> ffi::CoverageSpan { + let &Self { start_line, start_col, end_line, end_col } = self; + let file_id = local_file_id.as_u32(); + ffi::CoverageSpan { file_id, start_line, start_col, end_line, end_col } + } +} + /// Converts the span into its start line and column, and end line and column. /// /// Line numbers and column numbers are 1-based. Unlike most column numbers emitted by /// the compiler, these column numbers are denoted in **bytes**, because that's what /// LLVM's `llvm-cov` tool expects to see in coverage maps. /// -/// Returns `None` if the conversion failed for some reason. This shouldn't happen, +/// Returns `None` if the conversion failed for some reason. This should be uncommon, /// but it's hard to rule out entirely (especially in the presence of complex macros /// or other expansions), and if it does happen then skipping a span or function is /// better than an ICE or `llvm-cov` failure that the user might have no way to avoid. -pub(crate) fn make_coverage_span( - file_id: LocalFileId, - source_map: &SourceMap, - fn_cov_info: &FunctionCoverageInfo, - file: &SourceFile, - span: Span, -) -> Option { - let span = ensure_non_empty_span(source_map, fn_cov_info, span)?; +pub(crate) fn make_coords(source_map: &SourceMap, file: &SourceFile, span: Span) -> Option { + if span.is_empty() { + debug_assert!(false, "can't make coords from empty span: {span:?}"); + return None; + } let lo = span.lo(); let hi = span.hi(); @@ -46,8 +65,7 @@ pub(crate) fn make_coverage_span( start_line = source_map.doctest_offset_line(&file.name, start_line); end_line = source_map.doctest_offset_line(&file.name, end_line); - check_coverage_span(ffi::CoverageSpan { - file_id: file_id.as_u32(), + check_coords(Coords { start_line: start_line as u32, start_col: start_col as u32, end_line: end_line as u32, @@ -55,49 +73,12 @@ pub(crate) fn make_coverage_span( }) } -fn ensure_non_empty_span( - source_map: &SourceMap, - fn_cov_info: &FunctionCoverageInfo, - span: Span, -) -> Option { - if !span.is_empty() { - return Some(span); - } - - let lo = span.lo(); - let hi = span.hi(); - - // The span is empty, so try to expand it to cover an adjacent '{' or '}', - // but only within the bounds of the body span. - let try_next = hi < fn_cov_info.body_span.hi(); - let try_prev = fn_cov_info.body_span.lo() < lo; - if !(try_next || try_prev) { - return None; - } - - source_map - .span_to_source(span, |src, start, end| try { - // Adjusting span endpoints by `BytePos(1)` is normally a bug, - // but in this case we have specifically checked that the character - // we're skipping over is one of two specific ASCII characters, so - // adjusting by exactly 1 byte is correct. - if try_next && src.as_bytes()[end] == b'{' { - Some(span.with_hi(hi + BytePos(1))) - } else if try_prev && src.as_bytes()[start - 1] == b'}' { - Some(span.with_lo(lo - BytePos(1))) - } else { - None - } - }) - .ok()? -} - /// If `llvm-cov` sees a source region that is improperly ordered (end < start), /// it will immediately exit with a fatal error. To prevent that from happening, /// discard regions that are improperly ordered, or might be interpreted in a /// way that makes them improperly ordered. -fn check_coverage_span(cov_span: ffi::CoverageSpan) -> Option { - let ffi::CoverageSpan { file_id: _, start_line, start_col, end_line, end_col } = cov_span; +fn check_coords(coords: Coords) -> Option { + let Coords { start_line, start_col, end_line, end_col } = coords; // Line/column coordinates are supposed to be 1-based. If we ever emit // coordinates of 0, `llvm-cov` might misinterpret them. @@ -110,17 +91,17 @@ fn check_coverage_span(cov_span: ffi::CoverageSpan) -> Option let is_ordered = (start_line, start_col) <= (end_line, end_col); if all_nonzero && end_col_has_high_bit_unset && is_ordered { - Some(cov_span) + Some(coords) } else { debug!( - ?cov_span, + ?coords, ?all_nonzero, ?end_col_has_high_bit_unset, ?is_ordered, "Skipping source region that would be misinterpreted or rejected by LLVM" ); // If this happens in a debug build, ICE to make it easier to notice. - debug_assert!(false, "Improper source region: {cov_span:?}"); + debug_assert!(false, "Improper source region: {coords:?}"); None } } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/unused.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/unused.rs new file mode 100644 index 0000000000000..fe3a7a1580b53 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/unused.rs @@ -0,0 +1,170 @@ +use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods}; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::mir; +use rustc_middle::mir::mono::MonoItemPartitions; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::def_id::DefIdSet; + +use crate::common::CodegenCx; +use crate::coverageinfo::mapgen::covfun::{CovfunRecord, prepare_covfun_record}; +use crate::llvm; + +/// Each CGU will normally only emit coverage metadata for the functions that it actually generates. +/// But since we don't want unused functions to disappear from coverage reports, we also scan for +/// functions that were instrumented but are not participating in codegen. +/// +/// These unused functions don't need to be codegenned, but we do need to add them to the function +/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them. +/// We also end up adding their symbol names to a special global array that LLVM will include in +/// its embedded coverage data. +pub(crate) fn prepare_covfun_records_for_unused_functions<'tcx>( + cx: &CodegenCx<'_, 'tcx>, + covfun_records: &mut Vec>, +) { + assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu()); + + let mut unused_instances = gather_unused_function_instances(cx); + // Sort the unused instances by symbol name, so that their order isn't hash-sensitive. + unused_instances.sort_by_key(|instance| instance.symbol_name); + + // Try to create a covfun record for each unused function. + let mut name_globals = Vec::with_capacity(unused_instances.len()); + covfun_records.extend(unused_instances.into_iter().filter_map(|unused| try { + let record = prepare_covfun_record(cx.tcx, unused.instance, false)?; + // If successful, also store its symbol name in a global constant. + name_globals.push(cx.const_str(unused.symbol_name.name).0); + record + })); + + // Store the names of unused functions in a specially-named global array. + // LLVM's `InstrProfilling` pass will detect this array, and include the + // referenced names in its `__llvm_prf_names` section. + // (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.) + if !name_globals.is_empty() { + let initializer = cx.const_array(cx.type_ptr(), &name_globals); + + let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), c"__llvm_coverage_names"); + llvm::set_global_constant(array, true); + llvm::set_linkage(array, llvm::Linkage::InternalLinkage); + llvm::set_initializer(array, initializer); + } +} + +/// Holds a dummy function instance along with its symbol name, to avoid having +/// to repeatedly query for the name. +struct UnusedInstance<'tcx> { + instance: ty::Instance<'tcx>, + symbol_name: ty::SymbolName<'tcx>, +} + +fn gather_unused_function_instances<'tcx>(cx: &CodegenCx<'_, 'tcx>) -> Vec> { + assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu()); + + let tcx = cx.tcx; + let usage = prepare_usage_sets(tcx); + + let is_unused_fn = |def_id: LocalDefId| -> bool { + // Usage sets expect `DefId`, so convert from `LocalDefId`. + let d: DefId = LocalDefId::to_def_id(def_id); + // To be potentially eligible for "unused function" mappings, a definition must: + // - Be eligible for coverage instrumentation + // - Not participate directly in codegen (or have lost all its coverage statements) + // - Not have any coverage statements inlined into codegenned functions + tcx.is_eligible_for_coverage(def_id) + && (!usage.all_mono_items.contains(&d) || usage.missing_own_coverage.contains(&d)) + && !usage.used_via_inlining.contains(&d) + }; + + // FIXME(#79651): Consider trying to filter out dummy instantiations of + // unused generic functions from library crates, because they can produce + // "unused instantiation" in coverage reports even when they are actually + // used by some downstream crate in the same binary. + + tcx.mir_keys(()) + .iter() + .copied() + .filter(|&def_id| is_unused_fn(def_id)) + .map(|def_id| make_dummy_instance(tcx, def_id)) + .map(|instance| UnusedInstance { instance, symbol_name: tcx.symbol_name(instance) }) + .collect::>() +} + +struct UsageSets<'tcx> { + all_mono_items: &'tcx DefIdSet, + used_via_inlining: FxHashSet, + missing_own_coverage: FxHashSet, +} + +/// Prepare sets of definitions that are relevant to deciding whether something +/// is an "unused function" for coverage purposes. +fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> { + let MonoItemPartitions { all_mono_items, codegen_units, .. } = + tcx.collect_and_partition_mono_items(()); + + // Obtain a MIR body for each function participating in codegen, via an + // arbitrary instance. + let mut def_ids_seen = FxHashSet::default(); + let def_and_mir_for_all_mono_fns = codegen_units + .iter() + .flat_map(|cgu| cgu.items().keys()) + .filter_map(|item| match item { + mir::mono::MonoItem::Fn(instance) => Some(instance), + mir::mono::MonoItem::Static(_) | mir::mono::MonoItem::GlobalAsm(_) => None, + }) + // We only need one arbitrary instance per definition. + .filter(move |instance| def_ids_seen.insert(instance.def_id())) + .map(|instance| { + // We don't care about the instance, just its underlying MIR. + let body = tcx.instance_mir(instance.def); + (instance.def_id(), body) + }); + + // Functions whose coverage statements were found inlined into other functions. + let mut used_via_inlining = FxHashSet::default(); + // Functions that were instrumented, but had all of their coverage statements + // removed by later MIR transforms (e.g. UnreachablePropagation). + let mut missing_own_coverage = FxHashSet::default(); + + for (def_id, body) in def_and_mir_for_all_mono_fns { + let mut saw_own_coverage = false; + + // Inspect every coverage statement in the function's MIR. + for stmt in body + .basic_blocks + .iter() + .flat_map(|block| &block.statements) + .filter(|stmt| matches!(stmt.kind, mir::StatementKind::Coverage(_))) + { + if let Some(inlined) = stmt.source_info.scope.inlined_instance(&body.source_scopes) { + // This coverage statement was inlined from another function. + used_via_inlining.insert(inlined.def_id()); + } else { + // Non-inlined coverage statements belong to the enclosing function. + saw_own_coverage = true; + } + } + + if !saw_own_coverage && body.function_coverage_info.is_some() { + missing_own_coverage.insert(def_id); + } + } + + UsageSets { all_mono_items, used_via_inlining, missing_own_coverage } +} + +fn make_dummy_instance<'tcx>(tcx: TyCtxt<'tcx>, local_def_id: LocalDefId) -> ty::Instance<'tcx> { + let def_id = local_def_id.to_def_id(); + + // Make a dummy instance that fills in all generics with placeholders. + ty::Instance::new_raw( + def_id, + ty::GenericArgs::for_item(tcx, def_id, |param, _| { + if let ty::GenericParamDefKind::Lifetime = param.kind { + tcx.lifetimes.re_erased.into() + } else { + tcx.mk_param_from_def(param) + } + }), + ) +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs index f52991b369797..d2591139d6edc 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -3,7 +3,6 @@ use std::collections::hash_map::Entry; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext}; use rustc_codegen_ssa::traits::*; use rustc_data_structures::fx::FxHashMap; -use rustc_index::Idx; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{Body, SourceScope}; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv}; @@ -43,8 +42,7 @@ pub(crate) fn compute_mir_scopes<'ll, 'tcx>( let mut instantiated = DenseBitSet::new_empty(mir.source_scopes.len()); let mut discriminators = FxHashMap::default(); // Instantiate all scopes. - for idx in 0..mir.source_scopes.len() { - let scope = SourceScope::new(idx); + for scope in mir.source_scopes.indices() { make_mir_scope( cx, instance, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 4ffe551df09b5..8f0948b8183bf 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -95,7 +95,11 @@ pub(crate) fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool { // in the `.debug_gdb_scripts` section. For that reason, we make sure that the // section is only emitted for leaf crates. let embed_visualizers = cx.tcx.crate_types().iter().any(|&crate_type| match crate_type { - CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Staticlib => { + CrateType::Executable + | CrateType::Dylib + | CrateType::Cdylib + | CrateType::Staticlib + | CrateType::Sdylib => { // These are crate types for which we will embed pretty printers since they // are treated as leaf crates. true diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 98d59f5a8ae06..7f3e486ca310d 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::fmt::{self, Write}; use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; +use std::sync::Arc; use std::{iter, ptr}; use libc::{c_char, c_longlong, c_uint}; @@ -38,8 +39,8 @@ use crate::debuginfo::metadata::type_map::build_type_with_children; use crate::debuginfo::utils::{WidePtrKind, wide_pointer_kind}; use crate::llvm; use crate::llvm::debuginfo::{ - DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, DebugEmissionKind, - DebugNameTableKind, + DIBasicType, DIBuilder, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock, + DIScope, DIType, DebugEmissionKind, DebugNameTableKind, }; use crate::value::Value; @@ -68,7 +69,8 @@ pub(super) const UNKNOWN_COLUMN_NUMBER: c_uint = 0; const NO_SCOPE_METADATA: Option<&DIScope> = None; /// A function that returns an empty list of generic parameter debuginfo nodes. -const NO_GENERICS: for<'ll> fn(&CodegenCx<'ll, '_>) -> SmallVec<&'ll DIType> = |_| SmallVec::new(); +const NO_GENERICS: for<'ll> fn(&CodegenCx<'ll, '_>) -> SmallVec> = + |_| SmallVec::new(); // SmallVec is used quite a bit in this module, so create a shorthand. // The actual number of elements is not so important. @@ -243,7 +245,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( cx, owner, addr_field_name, - (addr_field.size, addr_field.align.abi), + addr_field, layout.fields.offset(WIDE_PTR_ADDR), DIFlags::FlagZero, data_ptr_type_di_node, @@ -253,7 +255,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( cx, owner, extra_field_name, - (extra_field.size, extra_field.align.abi), + extra_field, layout.fields.offset(WIDE_PTR_EXTRA), DIFlags::FlagZero, type_di_node(cx, extra_field.ty), @@ -311,12 +313,7 @@ fn build_subroutine_type_di_node<'ll, 'tcx>( debug_context(cx).type_map.unique_id_to_di_node.borrow_mut().remove(&unique_type_id); - let fn_di_node = unsafe { - llvm::LLVMRustDIBuilderCreateSubroutineType( - DIB(cx), - create_DIArray(DIB(cx), &signature_di_nodes[..]), - ) - }; + let fn_di_node = create_subroutine_type(cx, create_DIArray(DIB(cx), &signature_di_nodes[..])); // This is actually a function pointer, so wrap it in pointer DI. let name = compute_debuginfo_type_name(cx.tcx, fn_ty, false); @@ -340,6 +337,13 @@ fn build_subroutine_type_di_node<'ll, 'tcx>( DINodeCreationResult::new(di_node, false) } +pub(super) fn create_subroutine_type<'ll>( + cx: &CodegenCx<'ll, '_>, + signature: &'ll DICompositeType, +) -> &'ll DICompositeType { + unsafe { llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(cx), signature) } +} + /// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs /// we with the correct type name (e.g. "dyn SomeTrait + Sync"). fn build_dyn_type_di_node<'ll, 'tcx>( @@ -487,26 +491,22 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> // FIXME(mw): Cache this via a regular UniqueTypeId instead of an extra field in the debug context. fn recursion_marker_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> &'ll DIType { *debug_context(cx).recursion_marker_type.get_or_init(move || { - unsafe { - // The choice of type here is pretty arbitrary - - // anything reading the debuginfo for a recursive - // type is going to see *something* weird - the only - // question is what exactly it will see. - // - // FIXME: the name `` does not fit the naming scheme - // of other types. - // - // FIXME: it might make sense to use an actual pointer type here - // so that debuggers can show the address. - let name = ""; - llvm::LLVMRustDIBuilderCreateBasicType( - DIB(cx), - name.as_c_char_ptr(), - name.len(), - cx.tcx.data_layout.pointer_size.bits(), - dwarf_const::DW_ATE_unsigned, - ) - } + // The choice of type here is pretty arbitrary - + // anything reading the debuginfo for a recursive + // type is going to see *something* weird - the only + // question is what exactly it will see. + // + // FIXME: the name `` does not fit the naming scheme + // of other types. + // + // FIXME: it might make sense to use an actual pointer type here + // so that debuggers can show the address. + create_basic_type( + cx, + "", + cx.tcx.data_layout.pointer_size, + dwarf_const::DW_ATE_unsigned, + ) }) } @@ -620,42 +620,38 @@ pub(crate) fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFi let source = cx.sess().opts.unstable_opts.embed_source.then_some(()).and(source_file.src.as_ref()); - unsafe { - llvm::LLVMRustDIBuilderCreateFile( - DIB(cx), - file_name.as_c_char_ptr(), - file_name.len(), - directory.as_c_char_ptr(), - directory.len(), - hash_kind, - hash_value.as_c_char_ptr(), - hash_value.len(), - source.map_or(ptr::null(), |x| x.as_c_char_ptr()), - source.map_or(0, |x| x.len()), - ) - } + create_file(DIB(cx), &file_name, &directory, &hash_value, hash_kind, source) } } fn unknown_file_metadata<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll DIFile { - debug_context(cx).created_files.borrow_mut().entry(None).or_insert_with(|| unsafe { - let file_name = ""; - let directory = ""; - let hash_value = ""; + debug_context(cx).created_files.borrow_mut().entry(None).or_insert_with(|| { + create_file(DIB(cx), "", "", "", llvm::ChecksumKind::None, None) + }) +} +fn create_file<'ll>( + builder: &DIBuilder<'ll>, + file_name: &str, + directory: &str, + hash_value: &str, + hash_kind: llvm::ChecksumKind, + source: Option<&Arc>, +) -> &'ll DIFile { + unsafe { llvm::LLVMRustDIBuilderCreateFile( - DIB(cx), + builder, file_name.as_c_char_ptr(), file_name.len(), directory.as_c_char_ptr(), directory.len(), - llvm::ChecksumKind::None, + hash_kind, hash_value.as_c_char_ptr(), hash_value.len(), - ptr::null(), - 0, + source.map_or(ptr::null(), |x| x.as_c_char_ptr()), + source.map_or(0, |x| x.len()), ) - }) + } } trait MsvcBasicName { @@ -742,7 +738,7 @@ fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreation cx, float_di_node, "bits", - cx.size_and_align_of(bits_ty), + cx.layout_of(bits_ty), Size::ZERO, DIFlags::FlagZero, type_di_node(cx, bits_ty), @@ -788,15 +784,7 @@ fn build_basic_type_di_node<'ll, 'tcx>( _ => bug!("debuginfo::build_basic_type_di_node - `t` is invalid type"), }; - let ty_di_node = unsafe { - llvm::LLVMRustDIBuilderCreateBasicType( - DIB(cx), - name.as_c_char_ptr(), - name.len(), - cx.size_of(t).bits(), - encoding, - ) - }; + let ty_di_node = create_basic_type(cx, name, cx.size_of(t), encoding); if !cpp_like_debuginfo { return DINodeCreationResult::new(ty_di_node, false); @@ -824,6 +812,23 @@ fn build_basic_type_di_node<'ll, 'tcx>( DINodeCreationResult::new(typedef_di_node, false) } +fn create_basic_type<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + name: &str, + size: Size, + encoding: u32, +) -> &'ll DIBasicType { + unsafe { + llvm::LLVMRustDIBuilderCreateBasicType( + DIB(cx), + name.as_c_char_ptr(), + name.len(), + size.bits(), + encoding, + ) + } +} + fn build_foreign_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>, @@ -905,7 +910,8 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( && let Some(f) = output_filenames.split_dwarf_path( tcx.sess.split_debuginfo(), tcx.sess.opts.unstable_opts.split_dwarf_kind, - Some(codegen_unit_name), + codegen_unit_name, + tcx.sess.invocation_temp.as_deref(), ) { // We get a path relative to the working directory from split_dwarf_path Some(tcx.sess.source_map().path_mapping().to_real_filename(f)) @@ -929,17 +935,13 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( }; unsafe { - let compile_unit_file = llvm::LLVMRustDIBuilderCreateFile( + let compile_unit_file = create_file( debug_context.builder.as_ref(), - name_in_debuginfo.as_c_char_ptr(), - name_in_debuginfo.len(), - work_dir.as_c_char_ptr(), - work_dir.len(), + &name_in_debuginfo, + &work_dir, + "", llvm::ChecksumKind::None, - ptr::null(), - 0, - ptr::null(), - 0, + None, ); let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit( @@ -971,7 +973,7 @@ fn build_field_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, owner: &'ll DIScope, name: &str, - size_and_align: (Size, Align), + layout: TyAndLayout<'tcx>, offset: Size, flags: DIFlags, type_di_node: &'ll DIType, @@ -983,6 +985,30 @@ fn build_field_di_node<'ll, 'tcx>( } else { (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) }; + create_member_type( + cx, + owner, + name, + file_metadata, + line_number, + layout, + offset, + flags, + type_di_node, + ) +} + +fn create_member_type<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + owner: &'ll DIScope, + name: &str, + file_metadata: &'ll DIType, + line_number: u32, + layout: TyAndLayout<'tcx>, + offset: Size, + flags: DIFlags, + type_di_node: &'ll DIType, +) -> &'ll DIType { unsafe { llvm::LLVMRustDIBuilderCreateMemberType( DIB(cx), @@ -991,8 +1017,8 @@ fn build_field_di_node<'ll, 'tcx>( name.len(), file_metadata, line_number, - size_and_align.0.bits(), - size_and_align.1.bits() as u32, + layout.size.bits(), + layout.align.abi.bits() as u32, offset.bits(), flags, type_di_node, @@ -1076,7 +1102,7 @@ fn build_struct_type_di_node<'ll, 'tcx>( cx, owner, &field_name[..], - (field_layout.size, field_layout.align.abi), + field_layout, struct_type_and_layout.fields.offset(i), visibility_di_flags(cx, f.did, adt_def.did()), type_di_node(cx, field_layout.ty), @@ -1126,7 +1152,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>( cx, closure_or_coroutine_di_node, capture_name.as_str(), - cx.size_and_align_of(up_var_ty), + cx.layout_of(up_var_ty), layout.fields.offset(index), DIFlags::FlagZero, type_di_node(cx, up_var_ty), @@ -1171,7 +1197,7 @@ fn build_tuple_type_di_node<'ll, 'tcx>( cx, tuple_di_node, &tuple_field_name(index), - cx.size_and_align_of(component_type), + cx.layout_of(component_type), tuple_type_and_layout.fields.offset(index), DIFlags::FlagZero, type_di_node(cx, component_type), @@ -1269,7 +1295,7 @@ fn build_union_type_di_node<'ll, 'tcx>( cx, owner, f.name.as_str(), - size_and_align_of(field_layout), + field_layout, Size::ZERO, DIFlags::FlagZero, type_di_node(cx, field_layout.ty), @@ -1287,7 +1313,7 @@ fn build_union_type_di_node<'ll, 'tcx>( fn build_generic_type_param_di_nodes<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, -) -> SmallVec<&'ll DIType> { +) -> SmallVec> { if let ty::Adt(def, args) = *ty.kind() { if args.types().next().is_some() { let generics = cx.tcx.generics_of(def.did()); @@ -1297,16 +1323,7 @@ fn build_generic_type_param_di_nodes<'ll, 'tcx>( kind.as_type().map(|ty| { let actual_type = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty); let actual_type_di_node = type_di_node(cx, actual_type); - let name = name.as_str(); - unsafe { - llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( - DIB(cx), - None, - name.as_c_char_ptr(), - name.len(), - actual_type_di_node, - ) - } + Some(cx.create_template_type_parameter(name.as_str(), actual_type_di_node)) }) }) .collect(); @@ -1416,7 +1433,9 @@ fn build_vtable_type_di_node<'ll, 'tcx>( let void_pointer_ty = Ty::new_imm_ptr(tcx, tcx.types.unit); let void_pointer_type_di_node = type_di_node(cx, void_pointer_ty); let usize_di_node = type_di_node(cx, tcx.types.usize); - let (pointer_size, pointer_align) = cx.size_and_align_of(void_pointer_ty); + let pointer_layout = cx.layout_of(void_pointer_ty); + let pointer_size = pointer_layout.size; + let pointer_align = pointer_layout.align.abi; // If `usize` is not pointer-sized and -aligned then the size and alignment computations // for the vtable as a whole would be wrong. Let's make sure this holds even on weird // platforms. @@ -1472,7 +1491,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>( cx, vtable_type_di_node, &field_name, - (pointer_size, pointer_align), + pointer_layout, field_offset, DIFlags::FlagZero, field_type_di_node, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index a72e205c9b249..e9574108696ba 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -17,8 +17,8 @@ use crate::debuginfo::metadata::enums::DiscrResult; use crate::debuginfo::metadata::type_map::{self, Stub, UniqueTypeId}; use crate::debuginfo::metadata::{ DINodeCreationResult, NO_GENERICS, NO_SCOPE_METADATA, SmallVec, UNKNOWN_LINE_NUMBER, - build_field_di_node, file_metadata, file_metadata_from_def_id, size_and_align_of, type_di_node, - unknown_file_metadata, visibility_di_flags, + build_field_di_node, create_member_type, file_metadata, file_metadata_from_def_id, + size_and_align_of, type_di_node, unknown_file_metadata, visibility_di_flags, }; use crate::debuginfo::utils::DIB; use crate::llvm::debuginfo::{DIFile, DIFlags, DIType}; @@ -370,9 +370,9 @@ fn build_single_variant_union_fields<'ll, 'tcx>( cx, enum_type_di_node, &variant_union_field_name(variant_index), - // NOTE: We use the size and align of the entire type, not from variant_layout + // NOTE: We use the layout of the entire type, not from variant_layout // since the later is sometimes smaller (if it has fewer fields). - size_and_align_of(enum_type_and_layout), + enum_type_and_layout, Size::ZERO, visibility_flags, variant_struct_type_wrapper_di_node, @@ -560,7 +560,7 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( cx, wrapper_struct_type_di_node, "value", - size_and_align_of(enum_or_coroutine_type_and_layout), + enum_or_coroutine_type_and_layout, Size::ZERO, DIFlags::FlagZero, variant_struct_type_di_node, @@ -721,8 +721,7 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>( _ => unreachable!(), }; - let coroutine_layout = - cx.tcx.coroutine_layout(coroutine_def_id, coroutine_args.kind_ty()).unwrap(); + let coroutine_layout = cx.tcx.coroutine_layout(coroutine_def_id, coroutine_args.args).unwrap(); let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id); let variant_range = coroutine_args.variant_range(coroutine_def_id, cx.tcx); @@ -820,7 +819,6 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>( .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)); let field_name = variant_union_field_name(variant_member_info.variant_index); - let (size, align) = size_and_align_of(enum_type_and_layout); let variant_struct_type_wrapper = build_variant_struct_wrapper_type_di_node( cx, @@ -840,27 +838,23 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>( }, ); - // We use LLVMRustDIBuilderCreateMemberType() member type directly because + // We use create_member_type() member type directly because // the build_field_di_node() function does not support specifying a source location, // which is something that we don't do anywhere else. - unsafe { - llvm::LLVMRustDIBuilderCreateMemberType( - DIB(cx), - enum_type_di_node, - field_name.as_c_char_ptr(), - field_name.len(), - file_di_node, - line_number, - // NOTE: We use the size and align of the entire type, not from variant_layout - // since the later is sometimes smaller (if it has fewer fields). - size.bits(), - align.bits() as u32, - // Union fields are always at offset zero - Size::ZERO.bits(), - di_flags, - variant_struct_type_wrapper, - ) - } + create_member_type( + cx, + enum_type_di_node, + &field_name, + file_di_node, + line_number, + // NOTE: We use the layout of the entire type, not from variant_layout + // since the later is sometimes smaller (if it has fewer fields). + enum_type_and_layout, + // Union fields are always at offset zero + Size::ZERO, + di_flags, + variant_struct_type_wrapper, + ) })); assert_eq!( @@ -874,7 +868,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>( if is_128_bits { let type_di_node = type_di_node(cx, cx.tcx.types.u64); - let size_and_align = cx.size_and_align_of(cx.tcx.types.u64); + let u64_layout = cx.layout_of(cx.tcx.types.u64); let (lo_offset, hi_offset) = match cx.tcx.data_layout.endian { Endian::Little => (0, 8), @@ -889,7 +883,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>( cx, enum_type_di_node, TAG_FIELD_NAME_128_LO, - size_and_align, + u64_layout, lo_offset, di_flags, type_di_node, @@ -900,7 +894,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>( cx, enum_type_di_node, TAG_FIELD_NAME_128_HI, - size_and_align, + u64_layout, hi_offset, DIFlags::FlagZero, type_di_node, @@ -911,7 +905,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>( cx, enum_type_di_node, TAG_FIELD_NAME, - cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty), + enum_type_and_layout.field(cx, tag_field), enum_type_and_layout.fields.offset(tag_field), di_flags, tag_base_type_di_node, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index 9f6a5cc89e023..7c701926d2c5e 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -249,7 +249,7 @@ fn build_enum_variant_struct_type_di_node<'ll, 'tcx>( cx, struct_type_di_node, &field_name, - (field_layout.size, field_layout.align.abi), + field_layout, variant_layout.fields.offset(field_index), di_flags, type_di_node(cx, field_layout.ty), @@ -332,7 +332,7 @@ fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( cx, variant_struct_type_di_node, &field_name, - cx.size_and_align_of(field_type), + cx.layout_of(field_type), variant_layout.fields.offset(field_index), DIFlags::FlagZero, type_di_node(cx, field_type), @@ -352,7 +352,7 @@ fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( cx, variant_struct_type_di_node, upvar_name.as_str(), - cx.size_and_align_of(upvar_ty), + cx.layout_of(upvar_ty), coroutine_type_and_layout.fields.offset(index), DIFlags::FlagZero, type_di_node(cx, upvar_ty), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index 187d97c54c873..20a841f2287ae 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -13,9 +13,9 @@ use smallvec::smallvec; use crate::common::{AsCCharPtr, CodegenCx}; use crate::debuginfo::metadata::type_map::{self, Stub, StubInfo, UniqueTypeId}; use crate::debuginfo::metadata::{ - DINodeCreationResult, NO_GENERICS, SmallVec, UNKNOWN_LINE_NUMBER, file_metadata, - file_metadata_from_def_id, size_and_align_of, type_di_node, unknown_file_metadata, - visibility_di_flags, + DINodeCreationResult, NO_GENERICS, SmallVec, UNKNOWN_LINE_NUMBER, create_member_type, + file_metadata, file_metadata_from_def_id, size_and_align_of, type_di_node, + unknown_file_metadata, visibility_di_flags, }; use crate::debuginfo::utils::{DIB, create_DIArray, get_namespace_for_item}; use crate::llvm::debuginfo::{DIFile, DIFlags, DIType}; @@ -174,10 +174,8 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( DIFlags::FlagZero, ), |cx, coroutine_type_di_node| { - let coroutine_layout = cx - .tcx - .coroutine_layout(coroutine_def_id, coroutine_args.as_coroutine().kind_ty()) - .unwrap(); + let coroutine_layout = + cx.tcx.coroutine_layout(coroutine_def_id, coroutine_args).unwrap(); let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } = coroutine_type_and_layout.variants @@ -363,23 +361,22 @@ fn build_discr_member_di_node<'ll, 'tcx>( &Variants::Multiple { tag_field, .. } => { let tag_base_type = tag_base_type(cx.tcx, enum_or_coroutine_type_and_layout); - let (size, align) = cx.size_and_align_of(tag_base_type); - - unsafe { - Some(llvm::LLVMRustDIBuilderCreateMemberType( - DIB(cx), - containing_scope, - tag_name.as_c_char_ptr(), - tag_name.len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - size.bits(), - align.bits() as u32, - enum_or_coroutine_type_and_layout.fields.offset(tag_field).bits(), - DIFlags::FlagArtificial, - type_di_node(cx, tag_base_type), - )) - } + let ty = type_di_node(cx, tag_base_type); + let file = unknown_file_metadata(cx); + + let layout = cx.layout_of(tag_base_type); + + Some(create_member_type( + cx, + containing_scope, + &tag_name, + file, + UNKNOWN_LINE_NUMBER, + layout, + enum_or_coroutine_type_and_layout.fields.offset(tag_field), + DIFlags::FlagArtificial, + ty, + )) } } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index af1d503ad6a43..56fb12d3c22ea 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -247,6 +247,16 @@ pub(super) fn stub<'ll, 'tcx>( StubInfo { metadata, unique_type_id } } +struct AdtStackPopGuard<'ll, 'tcx, 'a> { + cx: &'a CodegenCx<'ll, 'tcx>, +} + +impl<'ll, 'tcx, 'a> Drop for AdtStackPopGuard<'ll, 'tcx, 'a> { + fn drop(&mut self) { + debug_context(self.cx).adt_stack.borrow_mut().pop(); + } +} + /// This function enables creating debuginfo nodes that can recursively refer to themselves. /// It will first insert the given stub into the type map and only then execute the `members` /// and `generics` closures passed in. These closures have access to the stub so they can @@ -257,16 +267,53 @@ pub(super) fn build_type_with_children<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, stub_info: StubInfo<'ll, 'tcx>, members: impl FnOnce(&CodegenCx<'ll, 'tcx>, &'ll DIType) -> SmallVec<&'ll DIType>, - generics: impl FnOnce(&CodegenCx<'ll, 'tcx>) -> SmallVec<&'ll DIType>, + generics: impl FnOnce(&CodegenCx<'ll, 'tcx>) -> SmallVec>, ) -> DINodeCreationResult<'ll> { assert_eq!(debug_context(cx).type_map.di_node_for_unique_id(stub_info.unique_type_id), None); + let mut _adt_stack_pop_guard = None; + if let UniqueTypeId::Ty(ty, ..) = stub_info.unique_type_id + && let ty::Adt(adt_def, args) = ty.kind() + { + let def_id = adt_def.did(); + // If any sub type reference the original type definition and the sub type has a type + // parameter that strictly contains the original parameter, the original type is a recursive + // type that can expanding indefinitely. Example, + // ``` + // enum Recursive { + // Recurse(*const Recursive>), + // Item(T), + // } + // ``` + let is_expanding_recursive = + debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| { + if def_id == *parent_def_id { + args.iter().zip(parent_args.iter()).any(|(arg, parent_arg)| { + if let (Some(arg), Some(parent_arg)) = (arg.as_type(), parent_arg.as_type()) + { + arg != parent_arg && arg.contains(parent_arg) + } else { + false + } + }) + } else { + false + } + }); + if is_expanding_recursive { + // FIXME: indicate that this is an expanding recursive type in stub metadata? + return DINodeCreationResult::new(stub_info.metadata, false); + } else { + debug_context(cx).adt_stack.borrow_mut().push((def_id, args)); + _adt_stack_pop_guard = Some(AdtStackPopGuard { cx }); + } + } + debug_context(cx).type_map.insert(stub_info.unique_type_id, stub_info.metadata); let members: SmallVec<_> = members(cx, stub_info.metadata).into_iter().map(|node| Some(node)).collect(); - let generics: SmallVec> = - generics(cx).into_iter().map(|node| Some(node)).collect(); + let generics = generics(cx); if !(members.is_empty() && generics.is_empty()) { unsafe { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 10819a53b1df8..c508592792347 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -6,6 +6,7 @@ use std::sync::Arc; use std::{iter, ptr}; use libc::c_uint; +use metadata::create_subroutine_type; use rustc_abi::Size; use rustc_codegen_ssa::debuginfo::type_names; use rustc_codegen_ssa::mir::debuginfo::VariableKind::*; @@ -34,8 +35,8 @@ use crate::builder::Builder; use crate::common::{AsCCharPtr, CodegenCx}; use crate::llvm; use crate::llvm::debuginfo::{ - DIArray, DIBuilderBox, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DIType, - DIVariable, + DIArray, DIBuilderBox, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, + DITemplateTypeParameter, DIType, DIVariable, }; use crate::value::Value; @@ -65,6 +66,7 @@ pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { created_files: RefCell, &'ll DIFile>>, type_map: metadata::TypeMap<'ll, 'tcx>, + adt_stack: RefCell)>>, namespace_map: RefCell>, recursion_marker_type: OnceCell<&'ll DIType>, } @@ -79,6 +81,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { builder, created_files: Default::default(), type_map: Default::default(), + adt_stack: Default::default(), namespace_map: RefCell::new(Default::default()), recursion_marker_type: OnceCell::new(), } @@ -251,7 +254,7 @@ struct DebugLoc { col: u32, } -impl CodegenCx<'_, '_> { +impl<'ll> CodegenCx<'ll, '_> { /// Looks up debug source information about a `BytePos`. // FIXME(eddyb) rename this to better indicate it's a duplicate of // `lookup_char_pos` rather than `dbg_loc`, perhaps by making @@ -279,6 +282,22 @@ impl CodegenCx<'_, '_> { DebugLoc { file, line, col } } } + + fn create_template_type_parameter( + &self, + name: &str, + actual_type_metadata: &'ll DIType, + ) -> &'ll DITemplateTypeParameter { + unsafe { + llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( + DIB(self), + None, + name.as_c_char_ptr(), + name.len(), + actual_type_metadata, + ) + } + } } impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { @@ -325,10 +344,8 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let loc = self.lookup_debug_loc(span.lo()); let file_metadata = file_metadata(self, &loc.file); - let function_type_metadata = unsafe { - let fn_signature = get_function_signature(self, fn_abi); - llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(self), fn_signature) - }; + let function_type_metadata = + create_subroutine_type(self, get_function_signature(self, fn_abi)); let mut name = String::with_capacity(64); type_names::push_item_name(tcx, def_id, false, &mut name); @@ -483,16 +500,10 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { kind.as_type().map(|ty| { let actual_type = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty); let actual_type_metadata = type_di_node(cx, actual_type); - let name = name.as_str(); - unsafe { - Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( - DIB(cx), - None, - name.as_c_char_ptr(), - name.len(), - actual_type_metadata, - )) - } + Some(cx.create_template_type_parameter( + name.as_str(), + actual_type_metadata, + )) }) }) .collect() diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index e79662ebc6472..2419ec1f88854 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -11,6 +11,8 @@ //! * Use define_* family of methods when you might be defining the Value. //! * When in doubt, define. +use std::borrow::Borrow; + use itertools::Itertools; use rustc_codegen_ssa::traits::TypeMembershipCodegenMethods; use rustc_data_structures::fx::FxIndexSet; @@ -22,7 +24,7 @@ use tracing::debug; use crate::abi::FnAbiLlvmExt; use crate::common::AsCCharPtr; -use crate::context::{CodegenCx, SimpleCx}; +use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx}; use crate::llvm::AttributePlace::Function; use crate::llvm::Visibility; use crate::type_::Type; @@ -81,16 +83,25 @@ pub(crate) fn declare_raw_fn<'ll, 'tcx>( llfn } -impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { +impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { /// Declare a global value. /// /// If there’s a value with the same name already declared, the function will /// return its Value instead. pub(crate) fn declare_global(&self, name: &str, ty: &'ll Type) -> &'ll Value { debug!("declare_global(name={:?})", name); - unsafe { llvm::LLVMRustGetOrInsertGlobal(self.llmod, name.as_c_char_ptr(), name.len(), ty) } + unsafe { + llvm::LLVMRustGetOrInsertGlobal( + (**self).borrow().llmod, + name.as_c_char_ptr(), + name.len(), + ty, + ) + } } +} +impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { /// Declare a C ABI function. /// /// Only use this for foreign function ABIs and glue. For Rust functions use diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 4c5a78ca74fe4..ecf108f988f0d 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -217,3 +217,7 @@ pub(crate) struct MismatchedDataLayout<'a> { pub(crate) struct FixedX18InvalidArch<'a> { pub arch: &'a str, } + +#[derive(Diagnostic)] +#[diag(codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0)] +pub(crate) struct SanitizerKcfiArityRequiresLLVM2100; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 0272667e22399..5ca5737529229 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -14,6 +14,7 @@ use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, GenericArgsRef, Ty}; use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol, sym}; +use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::callconv::{FnAbi, PassMode}; use rustc_target::spec::{HasTargetSpec, PanicStrategy}; use tracing::debug; @@ -102,11 +103,23 @@ fn get_simple_intrinsic<'ll>( sym::minnumf64 => "llvm.minnum.f64", sym::minnumf128 => "llvm.minnum.f128", + sym::minimumf16 => "llvm.minimum.f16", + sym::minimumf32 => "llvm.minimum.f32", + sym::minimumf64 => "llvm.minimum.f64", + // There are issues on x86_64 and aarch64 with the f128 variant, + // let's instead use the instrinsic fallback body. + // sym::minimumf128 => "llvm.minimum.f128", sym::maxnumf16 => "llvm.maxnum.f16", sym::maxnumf32 => "llvm.maxnum.f32", sym::maxnumf64 => "llvm.maxnum.f64", sym::maxnumf128 => "llvm.maxnum.f128", + sym::maximumf16 => "llvm.maximum.f16", + sym::maximumf32 => "llvm.maximum.f32", + sym::maximumf64 => "llvm.maximum.f64", + // There are issues on x86_64 and aarch64 with the f128 variant, + // let's instead use the instrinsic fallback body. + // sym::maximumf128 => "llvm.maximum.f128", sym::copysignf16 => "llvm.copysign.f16", sym::copysignf32 => "llvm.copysign.f32", sym::copysignf64 => "llvm.copysign.f64", @@ -612,7 +625,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { _ => { debug!("unknown intrinsic '{}' -- falling back to default body", name); // Call the fallback body instead of generating the intrinsic code - return Err(ty::Instance::new(instance.def_id(), instance.args)); + return Err(ty::Instance::new_raw(instance.def_id(), instance.args)); } }; @@ -649,7 +662,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn type_test(&mut self, pointer: Self::Value, typeid: Self::Metadata) -> Self::Value { // Test the called operand using llvm.type.test intrinsic. The LowerTypeTests link-time // optimization pass replaces calls to this intrinsic with code to test type membership. - let typeid = unsafe { llvm::LLVMMetadataAsValue(&self.llcx, typeid) }; + let typeid = self.get_metadata_value(typeid); self.call_intrinsic("llvm.type.test", &[pointer, typeid]) } @@ -659,7 +672,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { vtable_byte_offset: u64, typeid: &'ll Metadata, ) -> Self::Value { - let typeid = unsafe { llvm::LLVMMetadataAsValue(&self.llcx, typeid) }; + let typeid = self.get_metadata_value(typeid); let vtable_byte_offset = self.const_i32(vtable_byte_offset as i32); let type_checked_load = self.call_intrinsic("llvm.type.checked.load", &[llvtable, vtable_byte_offset, typeid]); @@ -812,7 +825,10 @@ fn codegen_msvc_try<'ll>( let type_name = bx.const_bytes(b"rust_panic\0"); let type_info = bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_ptr()), type_name], false); - let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info)); + let tydesc = bx.declare_global( + &mangle_internal_symbol(bx.tcx, "__rust_panic_type_info"), + bx.val_ty(type_info), + ); llvm::set_linkage(tydesc, llvm::Linkage::LinkOnceODRLinkage); if bx.cx.tcx.sess.target.supports_comdat() { @@ -1180,18 +1196,6 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }}; } - /// Returns the bitwidth of the `$ty` argument if it is an `Int` type. - macro_rules! require_int_ty { - ($ty: expr, $diag: expr) => { - match $ty { - ty::Int(i) => i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), - _ => { - return_error!($diag); - } - } - }; - } - /// Returns the bitwidth of the `$ty` argument if it is an `Int` or `Uint` type. macro_rules! require_int_or_uint_ty { ($ty: expr, $diag: expr) => { @@ -1417,7 +1421,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( return Ok(bx.shuffle_vector(args[0].immediate(), args[1].immediate(), indices)); } - if name == sym::simd_insert { + if name == sym::simd_insert || name == sym::simd_insert_dyn { require!( in_elem == arg_tys[2], InvalidMonomorphization::InsertedType { @@ -1428,40 +1432,49 @@ fn generic_simd_intrinsic<'ll, 'tcx>( out_ty: arg_tys[2] } ); - let idx = bx - .const_to_opt_u128(args[1].immediate(), false) - .expect("typeck should have ensure that this is a const"); - if idx >= in_len.into() { - return_error!(InvalidMonomorphization::SimdIndexOutOfBounds { - span, - name, - arg_idx: 1, - total_len: in_len.into(), - }); - } - return Ok(bx.insert_element( - args[0].immediate(), - args[2].immediate(), - bx.const_i32(idx as i32), - )); + + let index_imm = if name == sym::simd_insert { + let idx = bx + .const_to_opt_u128(args[1].immediate(), false) + .expect("typeck should have ensure that this is a const"); + if idx >= in_len.into() { + return_error!(InvalidMonomorphization::SimdIndexOutOfBounds { + span, + name, + arg_idx: 1, + total_len: in_len.into(), + }); + } + bx.const_i32(idx as i32) + } else { + args[1].immediate() + }; + + return Ok(bx.insert_element(args[0].immediate(), args[2].immediate(), index_imm)); } - if name == sym::simd_extract { + if name == sym::simd_extract || name == sym::simd_extract_dyn { require!( ret_ty == in_elem, InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } ); - let idx = bx - .const_to_opt_u128(args[1].immediate(), false) - .expect("typeck should have ensure that this is a const"); - if idx >= in_len.into() { - return_error!(InvalidMonomorphization::SimdIndexOutOfBounds { - span, - name, - arg_idx: 1, - total_len: in_len.into(), - }); - } - return Ok(bx.extract_element(args[0].immediate(), bx.const_i32(idx as i32))); + let index_imm = if name == sym::simd_extract { + let idx = bx + .const_to_opt_u128(args[1].immediate(), false) + .expect("typeck should have ensure that this is a const"); + if idx >= in_len.into() { + return_error!(InvalidMonomorphization::SimdIndexOutOfBounds { + span, + name, + arg_idx: 1, + total_len: in_len.into(), + }); + } + bx.const_i32(idx as i32) + } else { + args[1].immediate() + }; + + return Ok(bx.extract_element(args[0].immediate(), index_imm)); } if name == sym::simd_select { @@ -1472,9 +1485,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( m_len == v_len, InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len } ); - let in_elem_bitwidth = require_int_ty!( + let in_elem_bitwidth = require_int_or_uint_ty!( m_elem_ty.kind(), - InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty } + InvalidMonomorphization::MaskWrongElementType { span, name, ty: m_elem_ty } ); let m_i1s = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, m_len); return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); @@ -1495,7 +1508,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // Integer vector : let in_elem_bitwidth = require_int_or_uint_ty!( in_elem.kind(), - InvalidMonomorphization::VectorArgument { span, name, in_ty, in_elem } + InvalidMonomorphization::MaskWrongElementType { span, name, ty: in_elem } ); let i1xn = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, in_len); @@ -1719,14 +1732,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); - let mask_elem_bitwidth = require_int_ty!( + let mask_elem_bitwidth = require_int_or_uint_ty!( element_ty2.kind(), - InvalidMonomorphization::ThirdArgElementType { - span, - name, - expected_element: element_ty2, - third_arg: arg_tys[2] - } + InvalidMonomorphization::MaskWrongElementType { span, name, ty: element_ty2 } ); // Alignment of T, must be a constant integer value: @@ -1821,14 +1829,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); - let m_elem_bitwidth = require_int_ty!( + let m_elem_bitwidth = require_int_or_uint_ty!( mask_elem.kind(), - InvalidMonomorphization::ThirdArgElementType { - span, - name, - expected_element: values_elem, - third_arg: mask_ty, - } + InvalidMonomorphization::MaskWrongElementType { span, name, ty: mask_elem } ); let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len); @@ -1911,14 +1914,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); - let m_elem_bitwidth = require_int_ty!( + let m_elem_bitwidth = require_int_or_uint_ty!( mask_elem.kind(), - InvalidMonomorphization::ThirdArgElementType { - span, - name, - expected_element: values_elem, - third_arg: mask_ty, - } + InvalidMonomorphization::MaskWrongElementType { span, name, ty: mask_elem } ); let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len); @@ -2006,15 +2004,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); - // The element type of the third argument must be a signed integer type of any width: - let mask_elem_bitwidth = require_int_ty!( + // The element type of the third argument must be an integer type of any width: + let mask_elem_bitwidth = require_int_or_uint_ty!( element_ty2.kind(), - InvalidMonomorphization::ThirdArgElementType { - span, - name, - expected_element: element_ty2, - third_arg: arg_tys[2] - } + InvalidMonomorphization::MaskWrongElementType { span, name, ty: element_ty2 } ); // Alignment of T, must be a constant integer value: diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index c88372db4913b..5736314b96a36 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -12,15 +12,12 @@ #![feature(exact_size_is_empty)] #![feature(extern_types)] #![feature(file_buffered)] -#![feature(hash_raw_entry)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(rustdoc_internals)] #![feature(slice_as_array)] #![feature(try_blocks)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use std::any::Any; @@ -29,8 +26,9 @@ use std::mem::ManuallyDrop; use back::owned_target_machine::OwnedTargetMachine; use back::write::{create_informational_target_machine, create_target_machine}; +use context::SimpleCx; use errors::{AutoDiffWithoutLTO, ParseTargetMachineConfig}; -pub(crate) use llvm_util::target_features_cfg; +use llvm_util::target_config; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; @@ -38,7 +36,7 @@ use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, }; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen}; +use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_metadata::EncodedMetadata; @@ -72,9 +70,7 @@ mod debuginfo; mod declare; mod errors; mod intrinsic; -// FIXME(Zalathar): Fix all the unreachable-pub warnings that would occur if -// this isn't pub, then make it not pub. -pub mod llvm; +mod llvm; mod llvm_util; mod mono_item; mod type_; @@ -116,9 +112,11 @@ impl ExtraBackendMethods for LlvmCodegenBackend { kind: AllocatorKind, alloc_error_handler_kind: AllocatorKind, ) -> ModuleLlvm { - let mut module_llvm = ModuleLlvm::new_metadata(tcx, module_name); + let module_llvm = ModuleLlvm::new_metadata(tcx, module_name); + let cx = + SimpleCx::new(module_llvm.llmod(), &module_llvm.llcx, tcx.data_layout.pointer_size); unsafe { - allocator::codegen(tcx, &mut module_llvm, module_name, kind, alloc_error_handler_kind); + allocator::codegen(tcx, cx, module_name, kind, alloc_error_handler_kind); } module_llvm } @@ -339,8 +337,8 @@ impl CodegenBackend for LlvmCodegenBackend { llvm_util::print_version(); } - fn target_features_cfg(&self, sess: &Session, allow_unstable: bool) -> Vec { - target_features_cfg(sess, allow_unstable) + fn target_config(&self, sess: &Session) -> TargetConfig { + target_config(sess) } fn codegen_crate<'tcx>( diff --git a/compiler/rustc_codegen_llvm/src/llvm/archive_ro.rs b/compiler/rustc_codegen_llvm/src/llvm/archive_ro.rs index 63b2b15c5145e..51bcc4d123d31 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/archive_ro.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/archive_ro.rs @@ -5,17 +5,17 @@ use std::{slice, str}; use rustc_fs_util::path_to_c_string; -pub struct ArchiveRO { +pub(crate) struct ArchiveRO { pub raw: &'static mut super::Archive, } unsafe impl Send for ArchiveRO {} -pub struct Iter<'a> { +pub(crate) struct Iter<'a> { raw: &'a mut super::ArchiveIterator<'a>, } -pub struct Child<'a> { +pub(crate) struct Child<'a> { pub raw: &'a mut super::ArchiveChild<'a>, } diff --git a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs index 11043b664f526..0e0f2b0eab016 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs @@ -3,13 +3,13 @@ use libc::c_uint; use rustc_span::InnerSpan; -pub use self::Diagnostic::*; -pub use self::OptimizationDiagnosticKind::*; +pub(crate) use self::Diagnostic::*; +use self::OptimizationDiagnosticKind::*; use super::{DiagnosticInfo, SMDiagnostic}; use crate::value::Value; #[derive(Copy, Clone, Debug)] -pub enum OptimizationDiagnosticKind { +pub(crate) enum OptimizationDiagnosticKind { OptimizationRemark, OptimizationMissed, OptimizationAnalysis, @@ -19,9 +19,10 @@ pub enum OptimizationDiagnosticKind { OptimizationRemarkOther, } -pub struct OptimizationDiagnostic<'ll> { +pub(crate) struct OptimizationDiagnostic<'ll> { pub kind: OptimizationDiagnosticKind, pub pass_name: String, + #[expect(dead_code)] pub function: &'ll Value, pub line: c_uint, pub column: c_uint, @@ -73,14 +74,14 @@ impl<'ll> OptimizationDiagnostic<'ll> { } } -pub struct SrcMgrDiagnostic { +pub(crate) struct SrcMgrDiagnostic { pub level: super::DiagnosticLevel, pub message: String, pub source: Option<(String, Vec)>, } impl SrcMgrDiagnostic { - pub unsafe fn unpack(diag: &SMDiagnostic) -> SrcMgrDiagnostic { + pub(crate) unsafe fn unpack(diag: &SMDiagnostic) -> SrcMgrDiagnostic { // Recover the post-substitution assembly code from LLVM for better // diagnostics. let mut have_source = false; @@ -120,7 +121,7 @@ impl SrcMgrDiagnostic { } #[derive(Clone)] -pub struct InlineAsmDiagnostic { +pub(crate) struct InlineAsmDiagnostic { pub level: super::DiagnosticLevel, pub cookie: u64, pub message: String, @@ -158,7 +159,7 @@ impl InlineAsmDiagnostic { } } -pub enum Diagnostic<'ll> { +pub(crate) enum Diagnostic<'ll> { Optimization(OptimizationDiagnostic<'ll>), InlineAsm(InlineAsmDiagnostic), PGO(&'ll DiagnosticInfo), @@ -166,11 +167,12 @@ pub enum Diagnostic<'ll> { Unsupported(&'ll DiagnosticInfo), /// LLVM has other types that we do not wrap here. + #[expect(dead_code)] UnknownDiagnostic(&'ll DiagnosticInfo), } impl<'ll> Diagnostic<'ll> { - pub unsafe fn unpack(di: &'ll DiagnosticInfo) -> Self { + pub(crate) unsafe fn unpack(di: &'ll DiagnosticInfo) -> Self { use super::DiagnosticKind as Dk; unsafe { diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 25ca349880345..2ad39fc853819 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -3,19 +3,35 @@ use libc::{c_char, c_uint}; -use super::ffi::{BasicBlock, Metadata, Module, Type, Value}; +use super::MetadataKindId; +use super::ffi::{AttributeKind, BasicBlock, Metadata, Module, Type, Value}; use crate::llvm::Bool; #[link(name = "llvm-wrapper", kind = "static")] unsafe extern "C" { // Enzyme - pub(crate) fn LLVMRustHasMetadata(I: &Value, KindID: c_uint) -> bool; + pub(crate) safe fn LLVMRustHasMetadata(I: &Value, KindID: MetadataKindId) -> bool; pub(crate) fn LLVMRustEraseInstUntilInclusive(BB: &BasicBlock, I: &Value); pub(crate) fn LLVMRustGetLastInstruction<'a>(BB: &BasicBlock) -> Option<&'a Value>; pub(crate) fn LLVMRustDIGetInstMetadata(I: &Value) -> Option<&Metadata>; pub(crate) fn LLVMRustEraseInstFromParent(V: &Value); pub(crate) fn LLVMRustGetTerminator<'a>(B: &BasicBlock) -> &'a Value; pub(crate) fn LLVMRustVerifyFunction(V: &Value, action: LLVMRustVerifierFailureAction) -> Bool; + pub(crate) fn LLVMRustHasAttributeAtIndex(V: &Value, i: c_uint, Kind: AttributeKind) -> bool; + pub(crate) fn LLVMRustGetArrayNumElements(Ty: &Type) -> u64; + pub(crate) fn LLVMRustHasFnAttribute( + F: &Value, + Name: *const c_char, + NameLen: libc::size_t, + ) -> bool; + pub(crate) fn LLVMRustRemoveFnAttribute(F: &Value, Name: *const c_char, NameLen: libc::size_t); + pub(crate) fn LLVMGetFirstFunction(M: &Module) -> Option<&Value>; + pub(crate) fn LLVMGetNextFunction(Fn: &Value) -> Option<&Value>; + pub(crate) fn LLVMRustRemoveEnumAttributeAtIndex( + Fn: &Value, + index: c_uint, + kind: AttributeKind, + ); } unsafe extern "C" { @@ -30,20 +46,20 @@ unsafe extern "C" { #[repr(C)] #[derive(Copy, Clone, PartialEq)] -pub enum LLVMRustVerifierFailureAction { +pub(crate) enum LLVMRustVerifierFailureAction { LLVMAbortProcessAction = 0, LLVMPrintMessageAction = 1, LLVMReturnStatusAction = 2, } #[cfg(llvm_enzyme)] -pub use self::Enzyme_AD::*; +pub(crate) use self::Enzyme_AD::*; #[cfg(llvm_enzyme)] -pub mod Enzyme_AD { +pub(crate) mod Enzyme_AD { use libc::c_void; unsafe extern "C" { - pub fn EnzymeSetCLBool(arg1: *mut ::std::os::raw::c_void, arg2: u8); + pub(crate) fn EnzymeSetCLBool(arg1: *mut ::std::os::raw::c_void, arg2: u8); } unsafe extern "C" { static mut EnzymePrintPerf: c_void; @@ -55,42 +71,42 @@ pub mod Enzyme_AD { static mut EnzymeInline: c_void; static mut RustTypeRules: c_void; } - pub fn set_print_perf(print: bool) { + pub(crate) fn set_print_perf(print: bool) { unsafe { EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrintPerf), print as u8); } } - pub fn set_print_activity(print: bool) { + pub(crate) fn set_print_activity(print: bool) { unsafe { EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrintActivity), print as u8); } } - pub fn set_print_type(print: bool) { + pub(crate) fn set_print_type(print: bool) { unsafe { EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrintType), print as u8); } } - pub fn set_print(print: bool) { + pub(crate) fn set_print(print: bool) { unsafe { EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrint), print as u8); } } - pub fn set_strict_aliasing(strict: bool) { + pub(crate) fn set_strict_aliasing(strict: bool) { unsafe { EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymeStrictAliasing), strict as u8); } } - pub fn set_loose_types(loose: bool) { + pub(crate) fn set_loose_types(loose: bool) { unsafe { EnzymeSetCLBool(std::ptr::addr_of_mut!(looseTypeAnalysis), loose as u8); } } - pub fn set_inline(val: bool) { + pub(crate) fn set_inline(val: bool) { unsafe { EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymeInline), val as u8); } } - pub fn set_rust_rules(val: bool) { + pub(crate) fn set_rust_rules(val: bool) { unsafe { EnzymeSetCLBool(std::ptr::addr_of_mut!(RustTypeRules), val as u8); } @@ -98,34 +114,34 @@ pub mod Enzyme_AD { } #[cfg(not(llvm_enzyme))] -pub use self::Fallback_AD::*; +pub(crate) use self::Fallback_AD::*; #[cfg(not(llvm_enzyme))] -pub mod Fallback_AD { +pub(crate) mod Fallback_AD { #![allow(unused_variables)] - pub fn set_inline(val: bool) { + pub(crate) fn set_inline(val: bool) { unimplemented!() } - pub fn set_print_perf(print: bool) { + pub(crate) fn set_print_perf(print: bool) { unimplemented!() } - pub fn set_print_activity(print: bool) { + pub(crate) fn set_print_activity(print: bool) { unimplemented!() } - pub fn set_print_type(print: bool) { + pub(crate) fn set_print_type(print: bool) { unimplemented!() } - pub fn set_print(print: bool) { + pub(crate) fn set_print(print: bool) { unimplemented!() } - pub fn set_strict_aliasing(strict: bool) { + pub(crate) fn set_strict_aliasing(strict: bool) { unimplemented!() } - pub fn set_loose_types(loose: bool) { + pub(crate) fn set_loose_types(loose: bool) { unimplemented!() } - pub fn set_rust_rules(val: bool) { + pub(crate) fn set_rust_rules(val: bool) { unimplemented!() } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 2dc14e4613d25..67a66e6ec795f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1,7 +1,7 @@ //! Bindings to the LLVM-C API (`LLVM*`), and to our own `extern "C"` wrapper //! functions around the unstable LLVM C++ API (`LLVMRust*`). //! -//! ## Passing pointer/length strings as `*const c_uchar` +//! ## Passing pointer/length strings as `*const c_uchar` (PTR_LEN_STR) //! //! Normally it's a good idea for Rust-side bindings to match the corresponding //! C-side function declarations as closely as possible. But when passing `&str` @@ -32,10 +32,10 @@ use crate::llvm; /// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, /// which has a different ABI from Rust or C++ `bool`. -pub type Bool = c_int; +pub(crate) type Bool = c_int; -pub const True: Bool = 1 as Bool; -pub const False: Bool = 0 as Bool; +pub(crate) const True: Bool = 1 as Bool; +pub(crate) const False: Bool = 0 as Bool; /// Wrapper for a raw enum value returned from LLVM's C APIs. /// @@ -44,7 +44,7 @@ pub const False: Bool = 0 as Bool; /// value and returns it. Instead, return this raw wrapper, then convert to the /// Rust-side enum explicitly. #[repr(transparent)] -pub struct RawEnum { +pub(crate) struct RawEnum { value: u32, /// We don't own or consume a `T`, but we can produce one. _rust_side_type: PhantomData T>, @@ -64,7 +64,7 @@ impl> RawEnum { #[derive(Copy, Clone, PartialEq)] #[repr(C)] #[allow(dead_code)] // Variants constructed by C++. -pub enum LLVMRustResult { +pub(crate) enum LLVMRustResult { Success, Failure, } @@ -83,7 +83,7 @@ pub enum LLVMRustResult { /// C++ API. #[derive(Copy, Clone, PartialEq)] #[repr(C)] -pub enum ModuleFlagMergeBehavior { +pub(crate) enum ModuleFlagMergeBehavior { Error = 1, Warning = 2, Require = 3, @@ -101,7 +101,7 @@ pub enum ModuleFlagMergeBehavior { /// See #[derive(Copy, Clone, PartialEq, Debug, TryFromU32)] #[repr(C)] -pub enum CallConv { +pub(crate) enum CallConv { CCallConv = 0, FastCallConv = 8, ColdCallConv = 9, @@ -126,7 +126,7 @@ pub enum CallConv { /// Must match the layout of `LLVMLinkage`. #[derive(Copy, Clone, PartialEq, TryFromU32)] #[repr(C)] -pub enum Linkage { +pub(crate) enum Linkage { ExternalLinkage = 0, AvailableExternallyLinkage = 1, LinkOnceAnyLinkage = 2, @@ -153,7 +153,7 @@ pub enum Linkage { /// Must match the layout of `LLVMVisibility`. #[repr(C)] #[derive(Copy, Clone, PartialEq, TryFromU32)] -pub enum Visibility { +pub(crate) enum Visibility { Default = 0, Hidden = 1, Protected = 2, @@ -171,8 +171,9 @@ impl Visibility { /// LLVMUnnamedAddr #[repr(C)] -pub enum UnnamedAddr { +pub(crate) enum UnnamedAddr { No, + #[expect(dead_code)] Local, Global, } @@ -180,7 +181,7 @@ pub enum UnnamedAddr { /// LLVMDLLStorageClass #[derive(Copy, Clone)] #[repr(C)] -pub enum DLLStorageClass { +pub(crate) enum DLLStorageClass { #[allow(dead_code)] Default = 0, DllImport = 1, // Function to be imported from DLL. @@ -193,7 +194,8 @@ pub enum DLLStorageClass { /// though it is not ABI compatible (since it's a C++ enum) #[repr(C)] #[derive(Copy, Clone, Debug)] -pub enum AttributeKind { +#[expect(dead_code, reason = "Some variants are unused, but are kept to match the C++")] +pub(crate) enum AttributeKind { AlwaysInline = 0, ByVal = 1, Cold = 2, @@ -241,7 +243,7 @@ pub enum AttributeKind { /// LLVMIntPredicate #[derive(Copy, Clone)] #[repr(C)] -pub enum IntPredicate { +pub(crate) enum IntPredicate { IntEQ = 32, IntNE = 33, IntUGT = 34, @@ -275,7 +277,7 @@ impl IntPredicate { /// LLVMRealPredicate #[derive(Copy, Clone)] #[repr(C)] -pub enum RealPredicate { +pub(crate) enum RealPredicate { RealPredicateFalse = 0, RealOEQ = 1, RealOGT = 2, @@ -321,7 +323,8 @@ impl RealPredicate { /// LLVMTypeKind #[derive(Copy, Clone, PartialEq, Debug)] #[repr(C)] -pub enum TypeKind { +#[expect(dead_code, reason = "Some variants are unused, but are kept to match LLVM-C")] +pub(crate) enum TypeKind { Void = 0, Half = 1, Float = 2, @@ -373,7 +376,7 @@ impl TypeKind { /// LLVMAtomicRmwBinOp #[derive(Copy, Clone)] #[repr(C)] -pub enum AtomicRmwBinOp { +pub(crate) enum AtomicRmwBinOp { AtomicXchg = 0, AtomicAdd = 1, AtomicSub = 2, @@ -409,9 +412,10 @@ impl AtomicRmwBinOp { /// LLVMAtomicOrdering #[derive(Copy, Clone)] #[repr(C)] -pub enum AtomicOrdering { +pub(crate) enum AtomicOrdering { #[allow(dead_code)] NotAtomic = 0, + #[allow(dead_code)] Unordered = 1, Monotonic = 2, // Consume = 3, // Not specified yet. @@ -425,7 +429,6 @@ impl AtomicOrdering { pub(crate) fn from_generic(ao: rustc_codegen_ssa::common::AtomicOrdering) -> Self { use rustc_codegen_ssa::common::AtomicOrdering as Common; match ao { - Common::Unordered => Self::Unordered, Common::Relaxed => Self::Monotonic, Common::Acquire => Self::Acquire, Common::Release => Self::Release, @@ -438,7 +441,7 @@ impl AtomicOrdering { /// LLVMRustFileType #[derive(Copy, Clone)] #[repr(C)] -pub enum FileType { +pub(crate) enum FileType { AssemblyFile, ObjectFile, } @@ -446,7 +449,8 @@ pub enum FileType { /// LLVMMetadataType #[derive(Copy, Clone)] #[repr(C)] -pub enum MetadataType { +#[expect(dead_code, reason = "Some variants are unused, but are kept to match LLVM-C")] +pub(crate) enum MetadataType { MD_dbg = 0, MD_tbaa = 1, MD_prof = 2, @@ -467,10 +471,10 @@ pub enum MetadataType { MD_kcfi_type = 36, } -/// LLVMRustAsmDialect +/// Must match the layout of `LLVMInlineAsmDialect`. #[derive(Copy, Clone, PartialEq)] #[repr(C)] -pub enum AsmDialect { +pub(crate) enum AsmDialect { Att, Intel, } @@ -478,7 +482,7 @@ pub enum AsmDialect { /// LLVMRustCodeGenOptLevel #[derive(Copy, Clone, PartialEq)] #[repr(C)] -pub enum CodeGenOptLevel { +pub(crate) enum CodeGenOptLevel { None, Less, Default, @@ -487,7 +491,7 @@ pub enum CodeGenOptLevel { /// LLVMRustPassBuilderOptLevel #[repr(C)] -pub enum PassBuilderOptLevel { +pub(crate) enum PassBuilderOptLevel { O0, O1, O2, @@ -499,7 +503,7 @@ pub enum PassBuilderOptLevel { /// LLVMRustOptStage #[derive(PartialEq)] #[repr(C)] -pub enum OptStage { +pub(crate) enum OptStage { PreLinkNoLTO, PreLinkThinLTO, PreLinkFatLTO, @@ -509,7 +513,7 @@ pub enum OptStage { /// LLVMRustSanitizerOptions #[repr(C)] -pub struct SanitizerOptions { +pub(crate) struct SanitizerOptions { pub sanitize_address: bool, pub sanitize_address_recover: bool, pub sanitize_cfi: bool, @@ -530,7 +534,7 @@ pub struct SanitizerOptions { /// LLVMRustRelocModel #[derive(Copy, Clone, PartialEq)] #[repr(C)] -pub enum RelocModel { +pub(crate) enum RelocModel { Static, PIC, DynamicNoPic, @@ -542,7 +546,7 @@ pub enum RelocModel { /// LLVMRustFloatABI #[derive(Copy, Clone, PartialEq)] #[repr(C)] -pub enum FloatAbi { +pub(crate) enum FloatAbi { Default, Soft, Hard, @@ -551,7 +555,7 @@ pub enum FloatAbi { /// LLVMRustCodeModel #[derive(Copy, Clone)] #[repr(C)] -pub enum CodeModel { +pub(crate) enum CodeModel { Tiny, Small, Kernel, @@ -564,7 +568,7 @@ pub enum CodeModel { #[derive(Copy, Clone)] #[repr(C)] #[allow(dead_code)] // Variants constructed by C++. -pub enum DiagnosticKind { +pub(crate) enum DiagnosticKind { Other, InlineAsm, StackSize, @@ -587,7 +591,7 @@ pub enum DiagnosticKind { #[derive(Copy, Clone)] #[repr(C)] #[allow(dead_code)] // Variants constructed by C++. -pub enum DiagnosticLevel { +pub(crate) enum DiagnosticLevel { Error, Warning, Note, @@ -597,7 +601,7 @@ pub enum DiagnosticLevel { /// LLVMRustArchiveKind #[derive(Copy, Clone)] #[repr(C)] -pub enum ArchiveKind { +pub(crate) enum ArchiveKind { K_GNU, K_BSD, K_DARWIN, @@ -607,15 +611,15 @@ pub enum ArchiveKind { unsafe extern "C" { // LLVMRustThinLTOData - pub type ThinLTOData; + pub(crate) type ThinLTOData; // LLVMRustThinLTOBuffer - pub type ThinLTOBuffer; + pub(crate) type ThinLTOBuffer; } /// LLVMRustThinLTOModule #[repr(C)] -pub struct ThinLTOModule { +pub(crate) struct ThinLTOModule { pub identifier: *const c_char, pub data: *const u8, pub len: usize, @@ -624,7 +628,8 @@ pub struct ThinLTOModule { /// LLVMThreadLocalMode #[derive(Copy, Clone)] #[repr(C)] -pub enum ThreadLocalMode { +pub(crate) enum ThreadLocalMode { + #[expect(dead_code)] NotThreadLocal, GeneralDynamic, LocalDynamic, @@ -635,7 +640,7 @@ pub enum ThreadLocalMode { /// LLVMRustChecksumKind #[derive(Copy, Clone)] #[repr(C)] -pub enum ChecksumKind { +pub(crate) enum ChecksumKind { None, MD5, SHA1, @@ -645,7 +650,7 @@ pub enum ChecksumKind { /// LLVMRustMemoryEffects #[derive(Copy, Clone)] #[repr(C)] -pub enum MemoryEffects { +pub(crate) enum MemoryEffects { None, ReadOnly, InaccessibleMemOnly, @@ -654,7 +659,8 @@ pub enum MemoryEffects { /// LLVMOpcode #[derive(Copy, Clone, PartialEq, Eq)] #[repr(C)] -pub enum Opcode { +#[expect(dead_code, reason = "Some variants are unused, but are kept to match LLVM-C")] +pub(crate) enum Opcode { Ret = 1, Br = 2, Switch = 3, @@ -735,48 +741,48 @@ struct InvariantOpaque<'a> { // Opaque pointer types unsafe extern "C" { - pub type Module; - pub type Context; - pub type Type; - pub type Value; - pub type ConstantInt; - pub type Attribute; - pub type Metadata; - pub type BasicBlock; - pub type Comdat; + pub(crate) type Module; + pub(crate) type Context; + pub(crate) type Type; + pub(crate) type Value; + pub(crate) type ConstantInt; + pub(crate) type Attribute; + pub(crate) type Metadata; + pub(crate) type BasicBlock; + pub(crate) type Comdat; } #[repr(C)] -pub struct Builder<'a>(InvariantOpaque<'a>); +pub(crate) struct Builder<'a>(InvariantOpaque<'a>); #[repr(C)] -pub struct PassManager<'a>(InvariantOpaque<'a>); +pub(crate) struct PassManager<'a>(InvariantOpaque<'a>); unsafe extern "C" { pub type TargetMachine; - pub type Archive; + pub(crate) type Archive; } #[repr(C)] -pub struct ArchiveIterator<'a>(InvariantOpaque<'a>); +pub(crate) struct ArchiveIterator<'a>(InvariantOpaque<'a>); #[repr(C)] -pub struct ArchiveChild<'a>(InvariantOpaque<'a>); +pub(crate) struct ArchiveChild<'a>(InvariantOpaque<'a>); unsafe extern "C" { - pub type Twine; - pub type DiagnosticInfo; - pub type SMDiagnostic; + pub(crate) type Twine; + pub(crate) type DiagnosticInfo; + pub(crate) type SMDiagnostic; } #[repr(C)] -pub struct RustArchiveMember<'a>(InvariantOpaque<'a>); +pub(crate) struct RustArchiveMember<'a>(InvariantOpaque<'a>); /// Opaque pointee of `LLVMOperandBundleRef`. #[repr(C)] pub(crate) struct OperandBundle<'a>(InvariantOpaque<'a>); #[repr(C)] -pub struct Linker<'a>(InvariantOpaque<'a>); +pub(crate) struct Linker<'a>(InvariantOpaque<'a>); unsafe extern "C" { - pub type DiagnosticHandler; + pub(crate) type DiagnosticHandler; } -pub type DiagnosticHandlerTy = unsafe extern "C" fn(&DiagnosticInfo, *mut c_void); +pub(crate) type DiagnosticHandlerTy = unsafe extern "C" fn(&DiagnosticInfo, *mut c_void); -pub mod debuginfo { +pub(crate) mod debuginfo { use std::ptr; use bitflags::bitflags; @@ -793,7 +799,7 @@ pub mod debuginfo { /// builder reference typically has a shorter lifetime than the LLVM /// session (`'ll`) that it participates in. #[repr(C)] - pub struct DIBuilder<'ll>(InvariantOpaque<'ll>); + pub(crate) struct DIBuilder<'ll>(InvariantOpaque<'ll>); /// Owning pointer to a `DIBuilder<'ll>` that will dispose of the builder /// when dropped. Use `.as_ref()` to get the underlying `&DIBuilder` @@ -822,22 +828,22 @@ pub mod debuginfo { } } - pub type DIDescriptor = Metadata; - pub type DILocation = Metadata; - pub type DIScope = DIDescriptor; - pub type DIFile = DIScope; - pub type DILexicalBlock = DIScope; - pub type DISubprogram = DIScope; - pub type DIType = DIDescriptor; - pub type DIBasicType = DIType; - pub type DIDerivedType = DIType; - pub type DICompositeType = DIDerivedType; - pub type DIVariable = DIDescriptor; - pub type DIGlobalVariableExpression = DIDescriptor; - pub type DIArray = DIDescriptor; - pub type DISubrange = DIDescriptor; - pub type DIEnumerator = DIDescriptor; - pub type DITemplateTypeParameter = DIDescriptor; + pub(crate) type DIDescriptor = Metadata; + pub(crate) type DILocation = Metadata; + pub(crate) type DIScope = DIDescriptor; + pub(crate) type DIFile = DIScope; + pub(crate) type DILexicalBlock = DIScope; + pub(crate) type DISubprogram = DIScope; + pub(crate) type DIType = DIDescriptor; + pub(crate) type DIBasicType = DIType; + pub(crate) type DIDerivedType = DIType; + pub(crate) type DICompositeType = DIDerivedType; + pub(crate) type DIVariable = DIDescriptor; + pub(crate) type DIGlobalVariableExpression = DIDescriptor; + pub(crate) type DIArray = DIDescriptor; + pub(crate) type DISubrange = DIDescriptor; + pub(crate) type DIEnumerator = DIDescriptor; + pub(crate) type DITemplateTypeParameter = DIDescriptor; bitflags! { /// Must match the layout of `LLVMDIFlags` in the LLVM-C API. @@ -846,7 +852,7 @@ pub mod debuginfo { /// assertions in `RustWrapper.cpp` used by `fromRust(LLVMDIFlags)`. #[repr(transparent)] #[derive(Clone, Copy, Default)] - pub struct DIFlags: u32 { + pub(crate) struct DIFlags: u32 { const FlagZero = 0; const FlagPrivate = 1; const FlagProtected = 2; @@ -886,7 +892,7 @@ pub mod debuginfo { bitflags! { #[repr(transparent)] #[derive(Clone, Copy, Default)] - pub struct DISPFlags: u32 { + pub(crate) struct DISPFlags: u32 { const SPFlagZero = 0; const SPFlagVirtual = 1; const SPFlagPureVirtual = 2; @@ -900,7 +906,7 @@ pub mod debuginfo { /// LLVMRustDebugEmissionKind #[derive(Copy, Clone)] #[repr(C)] - pub enum DebugEmissionKind { + pub(crate) enum DebugEmissionKind { NoDebug, FullDebug, LineTablesOnly, @@ -932,8 +938,9 @@ pub mod debuginfo { /// LLVMRustDebugNameTableKind #[derive(Clone, Copy)] #[repr(C)] - pub enum DebugNameTableKind { + pub(crate) enum DebugNameTableKind { Default, + #[expect(dead_code)] Gnu, None, } @@ -943,7 +950,7 @@ pub mod debuginfo { bitflags! { #[repr(transparent)] #[derive(Default)] - pub struct AllocKindFlags : u64 { + pub(crate) struct AllocKindFlags : u64 { const Unknown = 0; const Alloc = 1; const Realloc = 1 << 1; @@ -966,15 +973,26 @@ bitflags! { } unsafe extern "C" { - pub type ModuleBuffer; + pub(crate) type ModuleBuffer; } -pub type SelfProfileBeforePassCallback = +pub(crate) type SelfProfileBeforePassCallback = unsafe extern "C" fn(*mut c_void, *const c_char, *const c_char); -pub type SelfProfileAfterPassCallback = unsafe extern "C" fn(*mut c_void); +pub(crate) type SelfProfileAfterPassCallback = unsafe extern "C" fn(*mut c_void); + +pub(crate) type GetSymbolsCallback = + unsafe extern "C" fn(*mut c_void, *const c_char) -> *mut c_void; +pub(crate) type GetSymbolsErrorCallback = unsafe extern "C" fn(*const c_char) -> *mut c_void; + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub(crate) struct MetadataKindId(c_uint); -pub type GetSymbolsCallback = unsafe extern "C" fn(*mut c_void, *const c_char) -> *mut c_void; -pub type GetSymbolsErrorCallback = unsafe extern "C" fn(*const c_char) -> *mut c_void; +impl From for MetadataKindId { + fn from(value: MetadataType) -> Self { + Self(value as c_uint) + } +} unsafe extern "C" { // Create and destroy contexts. @@ -983,7 +1001,7 @@ unsafe extern "C" { C: &Context, Name: *const c_char, SLen: c_uint, - ) -> c_uint; + ) -> MetadataKindId; // Create modules. pub(crate) fn LLVMModuleCreateWithNameInContext( @@ -996,8 +1014,25 @@ unsafe extern "C" { pub(crate) fn LLVMGetDataLayoutStr(M: &Module) -> *const c_char; pub(crate) fn LLVMSetDataLayout(M: &Module, Triple: *const c_char); - /// See Module::setModuleInlineAsm. - pub(crate) fn LLVMAppendModuleInlineAsm(M: &Module, Asm: *const c_char, Len: size_t); + /// Append inline assembly to a module. See `Module::appendModuleInlineAsm`. + pub(crate) fn LLVMAppendModuleInlineAsm( + M: &Module, + Asm: *const c_uchar, // See "PTR_LEN_STR". + Len: size_t, + ); + + /// Create the specified uniqued inline asm string. See `InlineAsm::get()`. + pub(crate) fn LLVMGetInlineAsm<'ll>( + Ty: &'ll Type, + AsmString: *const c_uchar, // See "PTR_LEN_STR". + AsmStringSize: size_t, + Constraints: *const c_uchar, // See "PTR_LEN_STR". + ConstraintsSize: size_t, + HasSideEffects: llvm::Bool, + IsAlignStack: llvm::Bool, + Dialect: AsmDialect, + CanThrow: llvm::Bool, + ) -> &'ll Value; // Operations on integer types pub(crate) fn LLVMInt1TypeInContext(C: &Context) -> &Type; @@ -1046,14 +1081,13 @@ unsafe extern "C" { pub(crate) fn LLVMMetadataTypeInContext(C: &Context) -> &Type; // Operations on all values - pub(crate) fn LLVMIsUndef(Val: &Value) -> Bool; pub(crate) fn LLVMTypeOf(Val: &Value) -> &Type; pub(crate) fn LLVMGetValueName2(Val: &Value, Length: *mut size_t) -> *const c_char; pub(crate) fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t); pub(crate) fn LLVMReplaceAllUsesWith<'a>(OldVal: &'a Value, NewVal: &'a Value); - pub(crate) fn LLVMSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Node: &'a Value); + pub(crate) safe fn LLVMSetMetadata<'a>(Val: &'a Value, KindID: MetadataKindId, Node: &'a Value); pub(crate) fn LLVMGlobalSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata); - pub(crate) fn LLVMValueAsMetadata(Node: &Value) -> &Metadata; + pub(crate) safe fn LLVMValueAsMetadata(Node: &Value) -> &Metadata; // Operations on constants of any type pub(crate) fn LLVMConstNull(Ty: &Type) -> &Value; @@ -1147,7 +1181,7 @@ unsafe extern "C" { pub(crate) fn LLVMSetThreadLocalMode(GlobalVar: &Value, Mode: ThreadLocalMode); pub(crate) fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool; pub(crate) fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool); - pub(crate) fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool); + pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool); // Operations on attributes pub(crate) fn LLVMCreateStringAttribute( @@ -1163,7 +1197,7 @@ unsafe extern "C" { // Operations on parameters pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>; - pub(crate) fn LLVMCountParams(Fn: &Value) -> c_uint; + pub(crate) safe fn LLVMCountParams(Fn: &Value) -> c_uint; pub(crate) fn LLVMGetParam(Fn: &Value, Index: c_uint) -> &Value; // Operations on basic blocks @@ -1204,7 +1238,7 @@ unsafe extern "C" { pub(crate) fn LLVMGetCurrentDebugLocation2<'a>(Builder: &Builder<'a>) -> Option<&'a Metadata>; // Terminators - pub(crate) fn LLVMBuildRetVoid<'a>(B: &Builder<'a>) -> &'a Value; + pub(crate) safe fn LLVMBuildRetVoid<'a>(B: &Builder<'a>) -> &'a Value; pub(crate) fn LLVMBuildRet<'a>(B: &Builder<'a>, V: &'a Value) -> &'a Value; pub(crate) fn LLVMBuildBr<'a>(B: &Builder<'a>, Dest: &'a BasicBlock) -> &'a Value; pub(crate) fn LLVMBuildCondBr<'a>( @@ -1680,7 +1714,7 @@ unsafe extern "C" { Packed: Bool, ); - pub(crate) fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; + pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; pub(crate) fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); @@ -1749,7 +1783,7 @@ unsafe extern "C" { pub(crate) fn LLVMDIBuilderCreateNameSpace<'ll>( Builder: &DIBuilder<'ll>, ParentScope: Option<&'ll Metadata>, - Name: *const c_uchar, + Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: size_t, ExportSymbols: llvm::Bool, ) -> &'ll Metadata; @@ -1977,21 +2011,9 @@ unsafe extern "C" { /// Prints the statistics collected by `-Zprint-codegen-stats`. pub(crate) fn LLVMRustPrintStatistics(OutStr: &RustString); - /// Prepares inline assembly. - pub(crate) fn LLVMRustInlineAsm( - Ty: &Type, - AsmString: *const c_char, - AsmStringLen: size_t, - Constraints: *const c_char, - ConstraintsLen: size_t, - SideEffects: Bool, - AlignStack: Bool, - Dialect: AsmDialect, - CanThrow: Bool, - ) -> &Value; pub(crate) fn LLVMRustInlineAsmVerify( Ty: &Type, - Constraints: *const c_char, + Constraints: *const c_uchar, // See "PTR_LEN_STR". ConstraintsLen: size_t, ) -> bool; @@ -2010,6 +2032,8 @@ unsafe extern "C" { NumExpressions: size_t, CodeRegions: *const crate::coverageinfo::ffi::CodeRegion, NumCodeRegions: size_t, + ExpansionRegions: *const crate::coverageinfo::ffi::ExpansionRegion, + NumExpansionRegions: size_t, BranchRegions: *const crate::coverageinfo::ffi::BranchRegion, NumBranchRegions: size_t, MCDCBranchRegions: *const crate::coverageinfo::ffi::MCDCBranchRegion, @@ -2435,6 +2459,9 @@ unsafe extern "C" { DisableSimplifyLibCalls: bool, EmitLifetimeMarkers: bool, RunEnzyme: bool, + PrintBeforeEnzyme: bool, + PrintAfterEnzyme: bool, + PrintPasses: bool, SanitizerOptions: Option<&SanitizerOptions>, PGOGenPath: *const c_char, PGOUsePath: *const c_char, diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index a36226b25a249..ed23f91193013 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -9,18 +9,18 @@ use libc::c_uint; use rustc_abi::{Align, Size, WrappingRange}; use rustc_llvm::RustString; -pub use self::CallConv::*; -pub use self::CodeGenOptSize::*; -pub use self::MetadataType::*; -pub use self::ffi::*; +pub(crate) use self::CallConv::*; +pub(crate) use self::CodeGenOptSize::*; +pub(crate) use self::MetadataType::*; +pub(crate) use self::ffi::*; use crate::common::AsCCharPtr; -pub mod archive_ro; -pub mod diagnostic; -pub mod enzyme_ffi; +pub(crate) mod archive_ro; +pub(crate) mod diagnostic; +pub(crate) mod enzyme_ffi; mod ffi; -pub use self::enzyme_ffi::*; +pub(crate) use self::enzyme_ffi::*; impl LLVMRustResult { pub(crate) fn into_result(self) -> Result<(), ()> { @@ -41,6 +41,32 @@ pub(crate) fn AddFunctionAttributes<'ll>( } } +pub(crate) fn HasAttributeAtIndex<'ll>( + llfn: &'ll Value, + idx: AttributePlace, + kind: AttributeKind, +) -> bool { + unsafe { LLVMRustHasAttributeAtIndex(llfn, idx.as_uint(), kind) } +} + +pub(crate) fn HasStringAttribute<'ll>(llfn: &'ll Value, name: &str) -> bool { + unsafe { LLVMRustHasFnAttribute(llfn, name.as_c_char_ptr(), name.len()) } +} + +pub(crate) fn RemoveStringAttrFromFn<'ll>(llfn: &'ll Value, name: &str) { + unsafe { LLVMRustRemoveFnAttribute(llfn, name.as_c_char_ptr(), name.len()) } +} + +pub(crate) fn RemoveRustEnumAttributeAtIndex( + llfn: &Value, + place: AttributePlace, + kind: AttributeKind, +) { + unsafe { + LLVMRustRemoveEnumAttributeAtIndex(llfn, place.as_uint(), kind); + } +} + pub(crate) fn AddCallSiteAttributes<'ll>( callsite: &'ll Value, idx: AttributePlace, @@ -127,7 +153,7 @@ pub(crate) fn CreateRangeAttr(llcx: &Context, size: Size, range: WrappingRange) } #[derive(Copy, Clone)] -pub enum AttributePlace { +pub(crate) enum AttributePlace { ReturnValue, Argument(u32), Function, @@ -145,7 +171,7 @@ impl AttributePlace { #[derive(Copy, Clone, PartialEq)] #[repr(C)] -pub enum CodeGenOptSize { +pub(crate) enum CodeGenOptSize { CodeGenOptSizeNone = 0, CodeGenOptSizeDefault = 1, CodeGenOptSizeAggressive = 2, @@ -337,12 +363,13 @@ pub(crate) fn last_error() -> Option { } } -/// Owns an [`OperandBundle`], and will dispose of it when dropped. -pub(crate) struct OperandBundleOwned<'a> { +/// Owning pointer to an [`OperandBundle`] that will dispose of the bundle +/// when dropped. +pub(crate) struct OperandBundleBox<'a> { raw: ptr::NonNull>, } -impl<'a> OperandBundleOwned<'a> { +impl<'a> OperandBundleBox<'a> { pub(crate) fn new(name: &str, vals: &[&'a Value]) -> Self { let raw = unsafe { LLVMCreateOperandBundle( @@ -352,21 +379,21 @@ impl<'a> OperandBundleOwned<'a> { vals.len() as c_uint, ) }; - OperandBundleOwned { raw: ptr::NonNull::new(raw).unwrap() } + Self { raw: ptr::NonNull::new(raw).unwrap() } } - /// Returns inner `OperandBundle` type. + /// Dereferences to the underlying `&OperandBundle`. /// - /// This could be a `Deref` implementation, but `OperandBundle` contains an extern type and - /// `Deref::Target: ?Sized`. - pub(crate) fn raw(&self) -> &OperandBundle<'a> { + /// This can't be a `Deref` implementation because `OperandBundle` transitively + /// contains an extern type, which is incompatible with `Deref::Target: ?Sized`. + pub(crate) fn as_ref(&self) -> &OperandBundle<'a> { // SAFETY: The returned reference is opaque and can only used for FFI. // It is valid for as long as `&self` is. unsafe { self.raw.as_ref() } } } -impl Drop for OperandBundleOwned<'_> { +impl Drop for OperandBundleBox<'_> { fn drop(&mut self) { unsafe { LLVMDisposeOperandBundle(self.raw); @@ -414,3 +441,11 @@ pub(crate) fn set_dso_local<'ll>(v: &'ll Value) { LLVMRustSetDSOLocal(v, true); } } + +/// Safe wrapper for `LLVMAppendModuleInlineAsm`, which delegates to +/// `Module::appendModuleInlineAsm`. +pub(crate) fn append_module_inline_asm<'ll>(llmod: &'ll Module, asm: &[u8]) { + unsafe { + LLVMAppendModuleInlineAsm(llmod, asm.as_ptr(), asm.len()); + } +} diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 5cc4f4ab9e673..8f57f0983abb9 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -6,6 +6,7 @@ use std::sync::Once; use std::{ptr, slice, str}; use libc::c_int; +use rustc_codegen_ssa::TargetConfig; use rustc_codegen_ssa::base::wants_wasm_eh; use rustc_codegen_ssa::codegen_attrs::check_tied_features; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -18,6 +19,7 @@ use rustc_session::config::{PrintKind, PrintRequest}; use rustc_span::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport}; use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES}; +use smallvec::{SmallVec, smallvec}; use crate::back::write::create_informational_target_machine; use crate::errors::{ @@ -179,27 +181,27 @@ impl<'a> TargetFeatureFoldStrength<'a> { pub(crate) struct LLVMFeature<'a> { llvm_feature_name: &'a str, - dependency: Option>, + dependencies: SmallVec<[TargetFeatureFoldStrength<'a>; 1]>, } impl<'a> LLVMFeature<'a> { fn new(llvm_feature_name: &'a str) -> Self { - Self { llvm_feature_name, dependency: None } + Self { llvm_feature_name, dependencies: SmallVec::new() } } - fn with_dependency( + fn with_dependencies( llvm_feature_name: &'a str, - dependency: TargetFeatureFoldStrength<'a>, + dependencies: SmallVec<[TargetFeatureFoldStrength<'a>; 1]>, ) -> Self { - Self { llvm_feature_name, dependency: Some(dependency) } + Self { llvm_feature_name, dependencies } } - fn contains(&self, feat: &str) -> bool { + fn contains(&'a self, feat: &str) -> bool { self.iter().any(|dep| dep == feat) } fn iter(&'a self) -> impl Iterator { - let dependencies = self.dependency.iter().map(|feat| feat.as_str()); + let dependencies = self.dependencies.iter().map(|feat| feat.as_str()); std::iter::once(self.llvm_feature_name).chain(dependencies) } } @@ -209,7 +211,7 @@ impl<'a> IntoIterator for LLVMFeature<'a> { type IntoIter = impl Iterator; fn into_iter(self) -> Self::IntoIter { - let dependencies = self.dependency.into_iter().map(|feat| feat.as_str()); + let dependencies = self.dependencies.into_iter().map(|feat| feat.as_str()); std::iter::once(self.llvm_feature_name).chain(dependencies) } } @@ -239,9 +241,9 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::with_dependency( + ("x86", "sse4.2") => Some(LLVMFeature::with_dependencies( "sse4.2", - TargetFeatureFoldStrength::EnableOnly("crc32"), + smallvec![TargetFeatureFoldStrength::EnableOnly("crc32")], )), ("x86", "pclmulqdq") => Some(LLVMFeature::new("pclmul")), ("x86", "rdrand") => Some(LLVMFeature::new("rdrnd")), @@ -256,48 +258,67 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::new("perfmon")), ("aarch64", "paca") => Some(LLVMFeature::new("pauth")), ("aarch64", "pacg") => Some(LLVMFeature::new("pauth")), - ("aarch64", "pauth-lr") if get_version().0 < 19 => None, // Before LLVM 20 those two features were packaged together as b16b16 ("aarch64", "sve-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")), ("aarch64", "sme-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")), ("aarch64", "flagm2") => Some(LLVMFeature::new("altnzcv")), // Rust ties fp and neon together. - ("aarch64", "neon") => { - Some(LLVMFeature::with_dependency("neon", TargetFeatureFoldStrength::Both("fp-armv8"))) - } + ("aarch64", "neon") => Some(LLVMFeature::with_dependencies( + "neon", + smallvec![TargetFeatureFoldStrength::Both("fp-armv8")], + )), // In LLVM neon implicitly enables fp, but we manually enable // neon when a feature only implicitly enables fp ("aarch64", "fhm") => Some(LLVMFeature::new("fp16fml")), ("aarch64", "fp16") => Some(LLVMFeature::new("fullfp16")), // Filter out features that are not supported by the current LLVM version - ("aarch64", "fpmr") if get_version().0 != 18 => None, + ("aarch64", "fpmr") => None, // only existed in 18 ("arm", "fp16") => Some(LLVMFeature::new("fullfp16")), - // In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single - // feature called `fast-unaligned-access`. In LLVM 19, it was split back out. - ("riscv32" | "riscv64", "unaligned-scalar-mem") if get_version().0 == 18 => { - Some(LLVMFeature::new("fast-unaligned-access")) + // Filter out features that are not supported by the current LLVM version + ("loongarch64", "div32" | "lam-bh" | "lamcas" | "ld-seq-sa" | "scq") + if get_version().0 < 20 => + { + None } // Filter out features that are not supported by the current LLVM version - ("riscv32" | "riscv64", "zaamo") if get_version().0 < 19 => None, - ("riscv32" | "riscv64", "zabha") if get_version().0 < 19 => None, - ("riscv32" | "riscv64", "zalrsc") if get_version().0 < 19 => None, - ("riscv32" | "riscv64", "zama16b") if get_version().0 < 19 => None, ("riscv32" | "riscv64", "zacas") if get_version().0 < 20 => None, // Enable the evex512 target feature if an avx512 target feature is enabled. - ("x86", s) if s.starts_with("avx512") => { - Some(LLVMFeature::with_dependency(s, TargetFeatureFoldStrength::EnableOnly("evex512"))) - } + ("x86", s) if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies( + s, + smallvec![TargetFeatureFoldStrength::EnableOnly("evex512")], + )), // Support for `wide-arithmetic` will first land in LLVM 20 as part of // llvm/llvm-project#111598 ("wasm32" | "wasm64", "wide-arithmetic") if get_version() < (20, 0, 0) => None, ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")), // In LLVM 19, there is no `v8plus` feature and `v9` means "SPARC-V9 instruction available and SPARC-V8+ ABI used". // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L27-L28 - // Before LLVM 19, there is no `v8plus` feature and `v9` means "SPARC-V9 instruction available". + // Before LLVM 19, there was no `v8plus` feature and `v9` means "SPARC-V9 instruction available". // https://github.com/llvm/llvm-project/blob/llvmorg-18.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L26 ("sparc", "v8plus") if get_version().0 == 19 => Some(LLVMFeature::new("v9")), - ("sparc", "v8plus") if get_version().0 < 19 => None, ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")), + // These new `amx` variants and `movrs` were introduced in LLVM20 + ("x86", "amx-avx512" | "amx-fp8" | "amx-movrs" | "amx-tf32" | "amx-transpose") + if get_version().0 < 20 => + { + None + } + ("x86", "movrs") if get_version().0 < 20 => None, + ("x86", "avx10.1") => Some(LLVMFeature::new("avx10.1-512")), + ("x86", "avx10.2") if get_version().0 < 20 => None, + ("x86", "avx10.2") if get_version().0 >= 20 => Some(LLVMFeature::new("avx10.2-512")), + ("x86", "apxf") => Some(LLVMFeature::with_dependencies( + "egpr", + smallvec![ + TargetFeatureFoldStrength::Both("push2pop2"), + TargetFeatureFoldStrength::Both("ppx"), + TargetFeatureFoldStrength::Both("ndd"), + TargetFeatureFoldStrength::Both("ccmp"), + TargetFeatureFoldStrength::Both("cf"), + TargetFeatureFoldStrength::Both("nf"), + TargetFeatureFoldStrength::Both("zu"), + ], + )), (_, s) => Some(LLVMFeature::new(s)), } } @@ -306,45 +327,44 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Vec { - let mut features: FxHashSet = Default::default(); - +pub(crate) fn target_config(sess: &Session) -> TargetConfig { // Add base features for the target. // We do *not* add the -Ctarget-features there, and instead duplicate the logic for that below. // The reason is that if LLVM considers a feature implied but we do not, we don't want that to // show up in `cfg`. That way, `cfg` is entirely under our control -- except for the handling of - // the target CPU, that is still expanded to target features (with all their implied features) by - // LLVM. + // the target CPU, that is still expanded to target features (with all their implied features) + // by LLVM. let target_machine = create_informational_target_machine(sess, true); - // Compute which of the known target features are enabled in the 'base' target machine. - // We only consider "supported" features; "forbidden" features are not reflected in `cfg` as of now. - features.extend( - sess.target - .rust_target_features() - .iter() - .filter(|(feature, _, _)| { - // skip checking special features, as LLVM may not understand them - if RUSTC_SPECIAL_FEATURES.contains(feature) { - return true; - } - // check that all features in a given smallvec are enabled - if let Some(feat) = to_llvm_features(sess, feature) { - for llvm_feature in feat { - let cstr = SmallCStr::new(llvm_feature); - if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } - { - return false; - } + // Compute which of the known target features are enabled in the 'base' target machine. We only + // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now. + let mut features: FxHashSet = sess + .target + .rust_target_features() + .iter() + .filter(|(feature, _, _)| { + // skip checking special features, as LLVM may not understand them + if RUSTC_SPECIAL_FEATURES.contains(feature) { + return true; + } + if let Some(feat) = to_llvm_features(sess, feature) { + for llvm_feature in feat { + let cstr = SmallCStr::new(llvm_feature); + // `LLVMRustHasFeature` is moderately expensive. On targets with many + // features (e.g. x86) these calls take a non-trivial fraction of runtime + // when compiling very small programs. + if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } { + return false; } - true - } else { - false } - }) - .map(|(feature, _, _)| Symbol::intern(feature)), - ); + true + } else { + false + } + }) + .map(|(feature, _, _)| Symbol::intern(feature)) + .collect(); - // Add enabled features + // Add enabled and remove disabled features. for (enabled, feature) in sess.opts.cg.target_feature.split(',').filter_map(|s| match s.chars().next() { Some('+') => Some((true, Symbol::intern(&s[1..]))), @@ -360,7 +380,7 @@ pub(crate) fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec Vec Vec + ("s390x", _) => false, + // Unsupported + ("arm64ec", _) => false, + // MinGW ABI bugs + ("x86_64", "windows") if target_env == "gnu" && target_abi != "llvm" => false, + // Infinite recursion + ("csky", _) => false, + ("hexagon", _) => false, + ("powerpc" | "powerpc64", _) => false, + ("sparc" | "sparc64", _) => false, + ("wasm32" | "wasm64", _) => false, + // `f16` support only requires that symbols converting to and from `f32` are available. We + // provide these in `compiler-builtins`, so `f16` should be available on all platforms that + // do not have other ABI issues or LLVM crashes. + _ => true, + }; + + cfg.has_reliable_f128 = match (target_arch, target_os) { + // Unsupported + ("arm64ec", _) => false, + // Selection bug + ("mips64" | "mips64r6", _) => false, + // Selection bug + ("nvptx64", _) => false, + // ABI bugs et al. (full + // list at ) + ("powerpc" | "powerpc64", _) => false, + // ABI unsupported + ("sparc", _) => false, + // Stack alignment bug . NB: tests may + // not fail if our compiler-builtins is linked. + ("x86", _) => false, + // MinGW ABI bugs + ("x86_64", "windows") if target_env == "gnu" && target_abi != "llvm" => false, + // There are no known problems on other platforms, so the only requirement is that symbols + // are available. `compiler-builtins` provides all symbols required for core `f128` + // support, so this should work for everything else. + _ => true, + }; + + // Assume that working `f16` means working `f16` math for most platforms, since + // operations just go through `f32`. + cfg.has_reliable_f16_math = cfg.has_reliable_f16; + + cfg.has_reliable_f128_math = match (target_arch, target_os) { + // LLVM lowers `fp128` math to `long double` symbols even on platforms where + // `long double` is not IEEE binary128. See + // . + // + // This rules out anything that doesn't have `long double` = `binary128`; <= 32 bits + // (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86` + // (ld is 80-bit extended precision). + ("x86_64", _) => false, + (_, "linux") if target_pointer_width == 64 => true, + _ => false, + } && cfg.has_reliable_f128; } pub(crate) fn print_version() { @@ -682,14 +782,14 @@ pub(crate) fn global_llvm_features( for feature in sess.opts.cg.target_feature.split(',') { if let Some(feature) = feature.strip_prefix('+') { all_rust_features.extend( - UnordSet::from(sess.target.implied_target_features(std::iter::once(feature))) + UnordSet::from(sess.target.implied_target_features(feature)) .to_sorted_stable_ord() .iter() .map(|&&s| (true, s)), ) } else if let Some(feature) = feature.strip_prefix('-') { // FIXME: Why do we not remove implied features on "-" here? - // We do the equivalent above in `target_features_cfg`. + // We do the equivalent above in `target_config`. // See . all_rust_features.push((false, feature)); } else if !feature.is_empty() { @@ -768,7 +868,7 @@ pub(crate) fn global_llvm_features( "{}{}", enable_disable, llvm_feature.llvm_feature_name )) - .chain(llvm_feature.dependency.into_iter().filter_map( + .chain(llvm_feature.dependencies.into_iter().filter_map( move |feat| match (enable, feat) { (_, TargetFeatureFoldStrength::Both(f)) | (true, TargetFeatureFoldStrength::EnableOnly(f)) => { diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index a64627eaf5986..fdf62a08065c2 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -120,7 +120,7 @@ impl CodegenCx<'_, '_> { } // Match clang by only supporting COFF and ELF for now. - if self.tcx.sess.target.is_like_osx { + if self.tcx.sess.target.is_like_darwin { return false; } diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index d61ce41756270..169036f515298 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -1,3 +1,4 @@ +use std::borrow::Borrow; use std::{fmt, ptr}; use libc::{c_char, c_uint}; @@ -11,7 +12,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_target::callconv::{CastTarget, FnAbi}; use crate::abi::{FnAbiLlvmExt, LlvmType}; -use crate::context::{CodegenCx, SimpleCx}; +use crate::context::{CodegenCx, GenericCx, SCx}; pub(crate) use crate::llvm::Type; use crate::llvm::{Bool, False, Metadata, True}; use crate::type_of::LayoutLlvmExt; @@ -36,29 +37,29 @@ impl fmt::Debug for Type { } impl<'ll> CodegenCx<'ll, '_> {} -impl<'ll> SimpleCx<'ll> { +impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { pub(crate) fn type_named_struct(&self, name: &str) -> &'ll Type { let name = SmallCStr::new(name); - unsafe { llvm::LLVMStructCreateNamed(self.llcx, name.as_ptr()) } + unsafe { llvm::LLVMStructCreateNamed(self.llcx(), name.as_ptr()) } } pub(crate) fn set_struct_body(&self, ty: &'ll Type, els: &[&'ll Type], packed: bool) { unsafe { llvm::LLVMStructSetBody(ty, els.as_ptr(), els.len() as c_uint, packed as Bool) } } pub(crate) fn type_void(&self) -> &'ll Type { - unsafe { llvm::LLVMVoidTypeInContext(self.llcx) } + unsafe { llvm::LLVMVoidTypeInContext(self.llcx()) } } pub(crate) fn type_token(&self) -> &'ll Type { - unsafe { llvm::LLVMTokenTypeInContext(self.llcx) } + unsafe { llvm::LLVMTokenTypeInContext(self.llcx()) } } pub(crate) fn type_metadata(&self) -> &'ll Type { - unsafe { llvm::LLVMMetadataTypeInContext(self.llcx) } + unsafe { llvm::LLVMMetadataTypeInContext(self.llcx()) } } ///x Creates an integer type with the given number of bits, e.g., i24 pub(crate) fn type_ix(&self, num_bits: u64) -> &'ll Type { - unsafe { llvm::LLVMIntTypeInContext(self.llcx, num_bits as c_uint) } + unsafe { llvm::LLVMIntTypeInContext(self.llcx(), num_bits as c_uint) } } pub(crate) fn type_vector(&self, ty: &'ll Type, len: u64) -> &'ll Type { @@ -121,19 +122,32 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { self.type_array(self.type_from_integer(unit), size / unit_size) } } -impl<'ll> SimpleCx<'ll> { + +impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { + pub(crate) fn llcx(&self) -> &'ll llvm::Context { + (**self).borrow().llcx + } + + pub(crate) fn llmod(&self) -> &'ll llvm::Module { + (**self).borrow().llmod + } + + pub(crate) fn isize_ty(&self) -> &'ll Type { + (**self).borrow().isize_ty + } + pub(crate) fn type_variadic_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type { unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, True) } } pub(crate) fn type_i1(&self) -> &'ll Type { - unsafe { llvm::LLVMInt1TypeInContext(self.llcx) } + unsafe { llvm::LLVMInt1TypeInContext(self.llcx()) } } pub(crate) fn type_struct(&self, els: &[&'ll Type], packed: bool) -> &'ll Type { unsafe { llvm::LLVMStructTypeInContext( - self.llcx, + self.llcx(), els.as_ptr(), els.len() as c_uint, packed as Bool, @@ -142,45 +156,45 @@ impl<'ll> SimpleCx<'ll> { } } -impl<'ll, 'tcx> BaseTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { +impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { fn type_i8(&self) -> &'ll Type { - unsafe { llvm::LLVMInt8TypeInContext(self.llcx) } + unsafe { llvm::LLVMInt8TypeInContext(self.llcx()) } } fn type_i16(&self) -> &'ll Type { - unsafe { llvm::LLVMInt16TypeInContext(self.llcx) } + unsafe { llvm::LLVMInt16TypeInContext(self.llcx()) } } fn type_i32(&self) -> &'ll Type { - unsafe { llvm::LLVMInt32TypeInContext(self.llcx) } + unsafe { llvm::LLVMInt32TypeInContext(self.llcx()) } } fn type_i64(&self) -> &'ll Type { - unsafe { llvm::LLVMInt64TypeInContext(self.llcx) } + unsafe { llvm::LLVMInt64TypeInContext(self.llcx()) } } fn type_i128(&self) -> &'ll Type { - unsafe { llvm::LLVMIntTypeInContext(self.llcx, 128) } + unsafe { llvm::LLVMIntTypeInContext(self.llcx(), 128) } } fn type_isize(&self) -> &'ll Type { - self.isize_ty + self.isize_ty() } fn type_f16(&self) -> &'ll Type { - unsafe { llvm::LLVMHalfTypeInContext(self.llcx) } + unsafe { llvm::LLVMHalfTypeInContext(self.llcx()) } } fn type_f32(&self) -> &'ll Type { - unsafe { llvm::LLVMFloatTypeInContext(self.llcx) } + unsafe { llvm::LLVMFloatTypeInContext(self.llcx()) } } fn type_f64(&self) -> &'ll Type { - unsafe { llvm::LLVMDoubleTypeInContext(self.llcx) } + unsafe { llvm::LLVMDoubleTypeInContext(self.llcx()) } } fn type_f128(&self) -> &'ll Type { - unsafe { llvm::LLVMFP128TypeInContext(self.llcx) } + unsafe { llvm::LLVMFP128TypeInContext(self.llcx()) } } fn type_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type { @@ -196,7 +210,7 @@ impl<'ll, 'tcx> BaseTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn type_ptr_ext(&self, address_space: AddressSpace) -> &'ll Type { - unsafe { llvm::LLVMPointerTypeInContext(self.llcx, address_space.0) } + unsafe { llvm::LLVMPointerTypeInContext(self.llcx(), address_space.0) } } fn element_type(&self, ty: &'ll Type) -> &'ll Type { diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 8baa69cefe1e2..c216f0f4a09d0 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -399,7 +399,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), false) } // macOS / iOS AArch64 - "aarch64" if target.is_like_osx => { + "aarch64" if target.is_like_darwin => { emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), true) } "aarch64" => emit_aapcs_va_arg(bx, addr, target_ty), diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 1346efcb87c2a..337c694417790 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -19,7 +19,7 @@ regex = "1.4" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -27,7 +27,6 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } -rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } @@ -40,12 +39,11 @@ rustc_span = { path = "../rustc_span" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -rustc_type_ir = { path = "../rustc_type_ir" } serde_json = "1.0.59" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tempfile = "3.2" thin-vec = "0.2.12" -thorin-dwp = "0.8" +thorin-dwp = "0.9" tracing = "0.1" wasm-encoder = "0.219" # tidy-alphabetical-end @@ -61,5 +59,5 @@ default-features = false features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"] [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = ["Win32_Globalization"] diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index f15d6fba5066c..2621935eecf94 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -4,20 +4,14 @@ codegen_ssa_add_native_library = failed to add native library {$library_path}: { codegen_ssa_aix_strip_not_used = using host's `strip` binary to cross-compile to AIX which is not guaranteed to work -codegen_ssa_apple_deployment_target_invalid = - failed to parse deployment target specified in {$env_var}: {$error} - -codegen_ssa_apple_deployment_target_too_low = - deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min} - -codegen_ssa_apple_sdk_error_sdk_path = failed to get {$sdk_name} SDK path: {$error} - codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error} codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering codegen_ssa_autodiff_without_lto = using the autodiff feature requires using fat-lto +codegen_ssa_bare_instruction_set = `#[instruction_set]` requires an argument + codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty codegen_ssa_cgu_not_recorded = @@ -52,6 +46,10 @@ codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$err codegen_ssa_error_writing_def_file = Error writing .DEF file: {$error} +codegen_ssa_expected_name_value_pair = expected name value pair + +codegen_ssa_expected_one_argument = expected one argument + codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified @@ -88,9 +86,17 @@ codegen_ssa_incorrect_cgu_reuse_type = codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and is not sufficient. +codegen_ssa_invalid_argument = invalid argument + .help = valid inline arguments are `always` and `never` + +codegen_ssa_invalid_instruction_set = invalid instruction set specified + codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]` .note = the attribute requires exactly one argument +codegen_ssa_invalid_literal_value = invalid literal value + .label = value must be an integer between `0` and `255` + codegen_ssa_invalid_monomorphization_basic_float_type = invalid monomorphization of `{$name}` intrinsic: expected basic float type, found `{$ty}` codegen_ssa_invalid_monomorphization_basic_integer_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}` @@ -119,8 +125,7 @@ codegen_ssa_invalid_monomorphization_inserted_type = invalid monomorphization of codegen_ssa_invalid_monomorphization_invalid_bitmask = invalid monomorphization of `{$name}` intrinsic: invalid bitmask `{$mask_ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]` -codegen_ssa_invalid_monomorphization_mask_type = invalid monomorphization of `{$name}` intrinsic: found mask element type is `{$ty}`, expected a signed integer type - .note = the mask may be widened, which only has the correct behavior for signed integers +codegen_ssa_invalid_monomorphization_mask_wrong_element_type = invalid monomorphization of `{$name}` intrinsic: expected mask element type to be an integer, found `{$ty}` codegen_ssa_invalid_monomorphization_mismatched_lengths = invalid monomorphization of `{$name}` intrinsic: mismatched lengths: mask length `{$m_len}` != other vector length `{$v_len}` @@ -152,8 +157,6 @@ codegen_ssa_invalid_monomorphization_simd_shuffle = invalid monomorphization of codegen_ssa_invalid_monomorphization_simd_third = invalid monomorphization of `{$name}` intrinsic: expected SIMD third type, found non-SIMD `{$ty}` -codegen_ssa_invalid_monomorphization_third_arg_element_type = invalid monomorphization of `{$name}` intrinsic: expected element type `{$expected_element}` of third argument `{$third_arg}` to be a signed integer type - codegen_ssa_invalid_monomorphization_third_argument_length = invalid monomorphization of `{$name}` intrinsic: expected third argument with length {$in_len} (same as input type `{$in_ty}`), found `{$arg_ty}` with length {$out_len} codegen_ssa_invalid_monomorphization_unrecognized_intrinsic = invalid monomorphization of `{$name}` intrinsic: unrecognized intrinsic `{$name}` @@ -166,8 +169,6 @@ codegen_ssa_invalid_monomorphization_unsupported_symbol = invalid monomorphizati codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size = invalid monomorphization of `{$name}` intrinsic: unsupported {$symbol} from `{$in_ty}` with element `{$in_elem}` of size `{$size}` to `{$ret_ty}` -codegen_ssa_invalid_monomorphization_vector_argument = invalid monomorphization of `{$name}` intrinsic: vector argument `{$in_ty}`'s element type `{$in_elem}`, expected integer element type - codegen_ssa_invalid_no_sanitize = invalid argument for `no_sanitize` .note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` @@ -217,6 +218,8 @@ codegen_ssa_msvc_missing_linker = the msvc targets depend on the msvc linker but codegen_ssa_multiple_external_func_decl = multiple declarations of external function `{$function}` from library `{$library_name}` have different calling conventions +codegen_ssa_multiple_instruction_set = cannot specify more than one instruction set + codegen_ssa_multiple_main_functions = entry symbol `main` declared multiple times .help = did you use `#[no_mangle]` on `fn main`? Use `#![no_main]` to suppress the usual Rust-generated entry point @@ -229,6 +232,11 @@ codegen_ssa_no_natvis_directory = error enumerating natvis directory: {$error} codegen_ssa_no_saved_object_file = cached cgu {$cgu_name} should have an object file, but doesn't +codegen_ssa_null_on_export = `export_name` may not contain null characters + +codegen_ssa_out_of_range_integer = integer value out of range + .label = value must be between `0` and `255` + codegen_ssa_processing_dymutil_failed = processing debug info with `dsymutil` failed: {$status} .note = {$output} @@ -236,6 +244,8 @@ codegen_ssa_read_file = failed to read file: {$message} codegen_ssa_repair_vs_build_tools = the Visual Studio build tools may need to be repaired using the Visual Studio installer +codegen_ssa_requires_rust_abi = `#[track_caller]` requires Rust ABI + codegen_ssa_rlib_archive_build_failure = failed to build archive from rlib at `{$path}`: {$error} codegen_ssa_rlib_incompatible_dependency_formats = `{$ty1}` and `{$ty2}` do not have equivalent dependency formats (`{$list1}` vs `{$list2}`) @@ -356,6 +366,9 @@ codegen_ssa_unable_to_run_dsymutil = unable to run `dsymutil`: {$error} codegen_ssa_unable_to_write_debugger_visualizer = Unable to write debugger visualizer file `{$path}`: {$error} +codegen_ssa_unexpected_parameter_name = unexpected parameter name + .label = expected `{$prefix_nops}` or `{$entry_nops}` + codegen_ssa_unknown_archive_kind = Don't know how to build archive of type: {$kind} @@ -365,7 +378,7 @@ codegen_ssa_unknown_atomic_ordering = unknown ordering in atomic intrinsic codegen_ssa_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified -codegen_ssa_unsupported_arch = unsupported arch `{$arch}` for os `{$os}` +codegen_ssa_unsupported_instruction_set = target does not support `#[instruction_set]` codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target @@ -374,3 +387,20 @@ codegen_ssa_use_cargo_directive = use the `cargo:rustc-link-lib` directive to sp codegen_ssa_version_script_write_failure = failed to write version script: {$error} codegen_ssa_visual_studio_not_installed = you may need to install Visual Studio build tools with the "C++ build tools" workload + +codegen_ssa_xcrun_command_line_tools_insufficient = + when compiling for iOS, tvOS, visionOS or watchOS, you need a full installation of Xcode + +codegen_ssa_xcrun_failed_invoking = invoking `{$command_formatted}` to find {$sdk_name}.sdk failed: {$error} + +codegen_ssa_xcrun_found_developer_dir = found active developer directory at "{$developer_dir}" + +# `xcrun` already outputs a message about missing Xcode installation, so we only augment it with details about env vars. +codegen_ssa_xcrun_no_developer_dir = + pass the path of an Xcode installation via the DEVELOPER_DIR environment variable, or an SDK with the SDKROOT environment variable + +codegen_ssa_xcrun_sdk_path_warning = output of `xcrun` while finding {$sdk_name}.sdk + .note = {$stderr} + +codegen_ssa_xcrun_unsuccessful = failed running `{$command_formatted}` to find {$sdk_name}.sdk + .note = {$stdout}{$stderr} diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index 32f689608f816..3710625ac12d7 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -63,7 +63,7 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTr }, }; - for attr in tcx.hir().attrs(rustc_hir::CRATE_HIR_ID) { + for attr in tcx.hir_attrs(rustc_hir::CRATE_HIR_ID) { ams.check_attr(attr); } diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs index bfa7635a869f8..d242efaf4fd42 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple.rs @@ -1,16 +1,38 @@ -use std::env; -use std::fmt::{Display, from_fn}; -use std::num::ParseIntError; +use std::ffi::OsString; +use std::path::PathBuf; +use std::process::Command; +use itertools::Itertools; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::Session; use rustc_target::spec::Target; +pub(super) use rustc_target::spec::apple::OSVersion; +use tracing::debug; -use crate::errors::AppleDeploymentTarget; +use crate::errors::{XcrunError, XcrunSdkPathWarning}; +use crate::fluent_generated as fluent; #[cfg(test)] mod tests; +/// The canonical name of the desired SDK for a given target. +pub(super) fn sdk_name(target: &Target) -> &'static str { + match (&*target.os, &*target.abi) { + ("macos", "") => "MacOSX", + ("ios", "") => "iPhoneOS", + ("ios", "sim") => "iPhoneSimulator", + // Mac Catalyst uses the macOS SDK + ("ios", "macabi") => "MacOSX", + ("tvos", "") => "AppleTVOS", + ("tvos", "sim") => "AppleTVSimulator", + ("visionos", "") => "XROS", + ("visionos", "sim") => "XRSimulator", + ("watchos", "") => "WatchOS", + ("watchos", "sim") => "WatchSimulator", + (os, abi) => unreachable!("invalid os '{os}' / abi '{abi}' combination for Apple target"), + } +} + pub(super) fn macho_platform(target: &Target) -> u32 { match (&*target.os, &*target.abi) { ("macos", _) => object::macho::PLATFORM_MACOS, @@ -110,146 +132,155 @@ pub(super) fn add_data_and_relocation( Ok(()) } -/// Deployment target or SDK version. -/// -/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`. -type OSVersion = (u16, u8, u8); - -/// Parse an OS version triple (SDK version or deployment target). -fn parse_version(version: &str) -> Result { - if let Some((major, minor)) = version.split_once('.') { - let major = major.parse()?; - if let Some((minor, patch)) = minor.split_once('.') { - Ok((major, minor.parse()?, patch.parse()?)) - } else { - Ok((major, minor.parse()?, 0)) - } +pub(super) fn add_version_to_llvm_target( + llvm_target: &str, + deployment_target: OSVersion, +) -> String { + let mut components = llvm_target.split("-"); + let arch = components.next().expect("apple target should have arch"); + let vendor = components.next().expect("apple target should have vendor"); + let os = components.next().expect("apple target should have os"); + let environment = components.next(); + assert_eq!(components.next(), None, "too many LLVM triple components"); + + assert!( + !os.contains(|c: char| c.is_ascii_digit()), + "LLVM target must not already be versioned" + ); + + let version = deployment_target.fmt_full(); + if let Some(env) = environment { + // Insert version into OS, before environment + format!("{arch}-{vendor}-{os}{version}-{env}") } else { - Ok((version.parse()?, 0, 0)) + format!("{arch}-{vendor}-{os}{version}") } } -pub fn pretty_version(version: OSVersion) -> impl Display { - let (major, minor, patch) = version; - from_fn(move |f| { - write!(f, "{major}.{minor}")?; - if patch != 0 { - write!(f, ".{patch}")?; +pub(super) fn get_sdk_root(sess: &Session) -> Option { + let sdk_name = sdk_name(&sess.target); + + match xcrun_show_sdk_path(sdk_name, sess.verbose_internals()) { + Ok((path, stderr)) => { + // Emit extra stderr, such as if `-verbose` was passed, or if `xcrun` emitted a warning. + if !stderr.is_empty() { + sess.dcx().emit_warn(XcrunSdkPathWarning { sdk_name, stderr }); + } + Some(path) } - Ok(()) - }) -} + Err(err) => { + let mut diag = sess.dcx().create_err(err); -/// Minimum operating system versions currently supported by `rustc`. -fn os_minimum_deployment_target(os: &str) -> OSVersion { - // When bumping a version in here, remember to update the platform-support docs too. - // - // NOTE: The defaults may change in future `rustc` versions, so if you are looking for the - // default deployment target, prefer: - // ``` - // $ rustc --print deployment-target - // ``` - match os { - "macos" => (10, 12, 0), - "ios" => (10, 0, 0), - "tvos" => (10, 0, 0), - "watchos" => (5, 0, 0), - "visionos" => (1, 0, 0), - _ => unreachable!("tried to get deployment target for non-Apple platform"), + // Recognize common error cases, and give more Rust-specific error messages for those. + if let Some(developer_dir) = xcode_select_developer_dir() { + diag.arg("developer_dir", &developer_dir); + diag.note(fluent::codegen_ssa_xcrun_found_developer_dir); + if developer_dir.as_os_str().to_string_lossy().contains("CommandLineTools") { + if sdk_name != "MacOSX" { + diag.help(fluent::codegen_ssa_xcrun_command_line_tools_insufficient); + } + } + } else { + diag.help(fluent::codegen_ssa_xcrun_no_developer_dir); + } + + diag.emit(); + None + } } } -/// The deployment target for the given target. +/// Invoke `xcrun --sdk $sdk_name --show-sdk-path` to get the SDK path. /// -/// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense -/// to raise the minimum OS version. +/// The exact logic that `xcrun` uses is unspecified (see `man xcrun` for a few details), and may +/// change between macOS and Xcode versions, but it roughly boils down to finding the active +/// developer directory, and then invoking `xcodebuild -sdk $sdk_name -version` to get the SDK +/// details. /// -/// This matches what LLVM does, see in part: -/// -fn minimum_deployment_target(target: &Target) -> OSVersion { - match (&*target.os, &*target.arch, &*target.abi) { - ("macos", "aarch64", _) => (11, 0, 0), - ("ios", "aarch64", "macabi") => (14, 0, 0), - ("ios", "aarch64", "sim") => (14, 0, 0), - ("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0), - // Mac Catalyst defaults to 13.1 in Clang. - ("ios", _, "macabi") => (13, 1, 0), - ("tvos", "aarch64", "sim") => (14, 0, 0), - ("watchos", "aarch64", "sim") => (7, 0, 0), - (os, _, _) => os_minimum_deployment_target(os), +/// Finding the developer directory is roughly done by looking at, in order: +/// - The `DEVELOPER_DIR` environment variable. +/// - The `/var/db/xcode_select_link` symlink (set by `xcode-select --switch`). +/// - `/Applications/Xcode.app` (hardcoded fallback path). +/// - `/Library/Developer/CommandLineTools` (hardcoded fallback path). +/// +/// Note that `xcrun` caches its result, but with a cold cache this whole operation can be quite +/// slow, especially so the first time it's run after a reboot. +fn xcrun_show_sdk_path( + sdk_name: &'static str, + verbose: bool, +) -> Result<(PathBuf, String), XcrunError> { + let mut cmd = Command::new("xcrun"); + if verbose { + cmd.arg("--verbose"); } -} + // The `--sdk` parameter is the same as in xcodebuild, namely either an absolute path to an SDK, + // or the (lowercase) canonical name of an SDK. + cmd.arg("--sdk"); + cmd.arg(&sdk_name.to_lowercase()); + cmd.arg("--show-sdk-path"); -/// Name of the environment variable used to fetch the deployment target on the given OS. -pub fn deployment_target_env_var(os: &str) -> &'static str { - match os { - "macos" => "MACOSX_DEPLOYMENT_TARGET", - "ios" => "IPHONEOS_DEPLOYMENT_TARGET", - "watchos" => "WATCHOS_DEPLOYMENT_TARGET", - "tvos" => "TVOS_DEPLOYMENT_TARGET", - "visionos" => "XROS_DEPLOYMENT_TARGET", - _ => unreachable!("tried to get deployment target env var for non-Apple platform"), + // We do not stream stdout/stderr lines directly to the user, since whether they are warnings or + // errors depends on the status code at the end. + let output = cmd.output().map_err(|error| XcrunError::FailedInvoking { + sdk_name, + command_formatted: format!("{cmd:?}"), + error, + })?; + + // It is fine to do lossy conversion here, non-UTF-8 paths are quite rare on macOS nowadays + // (only possible with the HFS+ file system), and we only use it for error messages. + let stderr = String::from_utf8_lossy_owned(output.stderr); + if !stderr.is_empty() { + debug!(stderr, "original xcrun stderr"); } -} -/// Get the deployment target based on the standard environment variables, or fall back to the -/// minimum version supported by `rustc`. -pub fn deployment_target(sess: &Session) -> OSVersion { - let min = minimum_deployment_target(&sess.target); - let env_var = deployment_target_env_var(&sess.target.os); - - if let Ok(deployment_target) = env::var(env_var) { - match parse_version(&deployment_target) { - Ok(version) => { - let os_min = os_minimum_deployment_target(&sess.target.os); - // It is common that the deployment target is set a bit too low, for example on - // macOS Aarch64 to also target older x86_64. So we only want to warn when variable - // is lower than the minimum OS supported by rustc, not when the variable is lower - // than the minimum for a specific target. - if version < os_min { - sess.dcx().emit_warn(AppleDeploymentTarget::TooLow { - env_var, - version: pretty_version(version).to_string(), - os_min: pretty_version(os_min).to_string(), - }); - } + // Some versions of `xcodebuild` output beefy errors when invoked via `xcrun`, + // but these are usually red herrings. + let stderr = stderr + .lines() + .filter(|line| { + !line.contains("Writing error result bundle") + && !line.contains("Requested but did not find extension point with identifier") + }) + .join("\n"); - // Raise the deployment target to the minimum supported. - version.max(min) - } - Err(error) => { - sess.dcx().emit_err(AppleDeploymentTarget::Invalid { env_var, error }); - min - } - } + if output.status.success() { + Ok((stdout_to_path(output.stdout), stderr)) } else { - // If no deployment target variable is set, default to the minimum found above. - min + // Output both stdout and stderr, since shims of `xcrun` (such as the one provided by + // nixpkgs), do not always use stderr for errors. + let stdout = String::from_utf8_lossy_owned(output.stdout).trim().to_string(); + Err(XcrunError::Unsuccessful { + sdk_name, + command_formatted: format!("{cmd:?}"), + stdout, + stderr, + }) } } -pub(super) fn add_version_to_llvm_target( - llvm_target: &str, - deployment_target: OSVersion, -) -> String { - let mut components = llvm_target.split("-"); - let arch = components.next().expect("apple target should have arch"); - let vendor = components.next().expect("apple target should have vendor"); - let os = components.next().expect("apple target should have os"); - let environment = components.next(); - assert_eq!(components.next(), None, "too many LLVM triple components"); - - let (major, minor, patch) = deployment_target; - - assert!( - !os.contains(|c: char| c.is_ascii_digit()), - "LLVM target must not already be versioned" - ); +/// Invoke `xcode-select --print-path`, and return the current developer directory. +/// +/// NOTE: We don't do any error handling here, this is only used as a canary in diagnostics (`xcrun` +/// will have already emitted the relevant error information). +fn xcode_select_developer_dir() -> Option { + let mut cmd = Command::new("xcode-select"); + cmd.arg("--print-path"); + let output = cmd.output().ok()?; + if !output.status.success() { + return None; + } + Some(stdout_to_path(output.stdout)) +} - if let Some(env) = environment { - // Insert version into OS, before environment - format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}-{env}") - } else { - format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}") +fn stdout_to_path(mut stdout: Vec) -> PathBuf { + // Remove trailing newline. + if let Some(b'\n') = stdout.last() { + let _ = stdout.pop().unwrap(); } + #[cfg(unix)] + let path = ::from_vec(stdout); + #[cfg(not(unix))] // Unimportant, this is only used on macOS + let path = OsString::from(String::from_utf8(stdout).unwrap()); + PathBuf::from(path) } diff --git a/compiler/rustc_codegen_ssa/src/back/apple/tests.rs b/compiler/rustc_codegen_ssa/src/back/apple/tests.rs index 7ccda5a8190c7..5afe79b71954a 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple/tests.rs @@ -1,21 +1,79 @@ -use super::{add_version_to_llvm_target, parse_version}; +use super::*; #[test] fn test_add_version_to_llvm_target() { assert_eq!( - add_version_to_llvm_target("aarch64-apple-macosx", (10, 14, 1)), + add_version_to_llvm_target("aarch64-apple-macosx", OSVersion::new(10, 14, 1)), "aarch64-apple-macosx10.14.1" ); assert_eq!( - add_version_to_llvm_target("aarch64-apple-ios-simulator", (16, 1, 0)), + add_version_to_llvm_target("aarch64-apple-ios-simulator", OSVersion::new(16, 1, 0)), "aarch64-apple-ios16.1.0-simulator" ); } #[test] -fn test_parse_version() { - assert_eq!(parse_version("10"), Ok((10, 0, 0))); - assert_eq!(parse_version("10.12"), Ok((10, 12, 0))); - assert_eq!(parse_version("10.12.6"), Ok((10, 12, 6))); - assert_eq!(parse_version("9999.99.99"), Ok((9999, 99, 99))); +#[cfg_attr(not(target_os = "macos"), ignore = "xcode-select is only available on macOS")] +fn lookup_developer_dir() { + let _developer_dir = xcode_select_developer_dir().unwrap(); +} + +#[test] +#[cfg_attr(not(target_os = "macos"), ignore = "xcrun is only available on macOS")] +fn lookup_sdk() { + let (sdk_path, stderr) = xcrun_show_sdk_path("MacOSX", false).unwrap(); + // Check that the found SDK is valid. + assert!(sdk_path.join("SDKSettings.plist").exists()); + assert_eq!(stderr, ""); + + // Test that the SDK root is a subdir of the developer directory. + if let Some(developer_dir) = xcode_select_developer_dir() { + // Only run this test if SDKROOT is not set (otherwise xcrun may look up via. that). + if std::env::var_os("SDKROOT").is_some() { + assert!(sdk_path.starts_with(&developer_dir)); + } + } +} + +#[test] +#[cfg_attr(not(target_os = "macos"), ignore = "xcrun is only available on macOS")] +fn lookup_sdk_verbose() { + let (_, stderr) = xcrun_show_sdk_path("MacOSX", true).unwrap(); + // Newer xcrun versions should emit something like this: + // + // xcrun: note: looking up SDK with 'xcodebuild -sdk macosx -version Path' + // xcrun: note: xcrun_db = '/var/.../xcrun_db' + // xcrun: note: lookup resolved to: '...' + // xcrun: note: database key is: ... + // + // Or if the value is already cached, something like this: + // + // xcrun: note: database key is: ... + // xcrun: note: lookup resolved in '/var/.../xcrun_db' : '...' + assert!( + stderr.contains("xcrun: note: lookup resolved"), + "stderr should contain lookup note: {stderr}", + ); +} + +#[test] +#[cfg_attr(not(target_os = "macos"), ignore = "xcrun is only available on macOS")] +fn try_lookup_invalid_sdk() { + // As a proxy for testing all the different ways that `xcrun` can fail, + // test the case where an SDK was not found. + let err = xcrun_show_sdk_path("invalid", false).unwrap_err(); + let XcrunError::Unsuccessful { stderr, .. } = err else { + panic!("unexpected error kind: {err:?}"); + }; + // Either one of (depending on if using Command Line Tools or full Xcode): + // xcrun: error: SDK "invalid" cannot be located + // xcodebuild: error: SDK "invalid" cannot be located. + assert!( + stderr.contains(r#"error: SDK "invalid" cannot be located"#), + "stderr should contain xcodebuild note: {stderr}", + ); + assert!( + stderr.contains("xcrun: error: unable to lookup item 'Path' in SDK 'invalid'"), + "stderr should contain xcrun note: {stderr}", + ); } diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 60af462b6b619..1e1bdfb5977af 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -13,9 +13,9 @@ use object::read::archive::ArchiveFile; use object::read::macho::FatArch; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; +use rustc_fs_util::TempDirBuilder; use rustc_session::Session; use rustc_span::Symbol; -use tempfile::Builder as TempFileBuilder; use tracing::trace; use super::metadata::search_for_section; @@ -389,11 +389,10 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { mut skip: Box bool + 'static>, ) -> io::Result<()> { let mut archive_path = archive_path.to_path_buf(); - if self.sess.target.llvm_target.contains("-apple-macosx") { - if let Some(new_archive_path) = try_extract_macho_fat_archive(self.sess, &archive_path)? - { - archive_path = new_archive_path - } + if self.sess.target.llvm_target.contains("-apple-macosx") + && let Some(new_archive_path) = try_extract_macho_fat_archive(self.sess, &archive_path)? + { + archive_path = new_archive_path } if self.src_archives.iter().any(|archive| archive.0 == archive_path) { @@ -502,7 +501,7 @@ impl<'a> ArArchiveBuilder<'a> { // it creates. We need it to be the default mode for back compat reasons however. (See // #107495) To handle this we are telling tempfile to create a temporary directory instead // and then inside this directory create a file using File::create. - let archive_tmpdir = TempFileBuilder::new() + let archive_tmpdir = TempDirBuilder::new() .suffix(".temp-archive") .tempdir_in(output.parent().unwrap_or_else(|| Path::new(""))) .map_err(|err| { diff --git a/compiler/rustc_codegen_ssa/src/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs index 63023fdba20fc..05351bd6ca379 100644 --- a/compiler/rustc_codegen_ssa/src/back/command.rs +++ b/compiler/rustc_codegen_ssa/src/back/command.rs @@ -137,17 +137,40 @@ impl Command { /// Returns a `true` if we're pretty sure that this'll blow OS spawn limits, /// or `false` if we should attempt to spawn and see what the OS says. pub(crate) fn very_likely_to_exceed_some_spawn_limit(&self) -> bool { - // We mostly only care about Windows in this method, on Unix the limits - // can be gargantuan anyway so we're pretty unlikely to hit them - if cfg!(unix) { + #[cfg(not(any(windows, unix)))] + { return false; } - // Right now LLD doesn't support the `@` syntax of passing an argument - // through files, so regardless of the platform we try to go to the OS - // on this one. - if let Program::Lld(..) = self.program { - return false; + // On Unix the limits can be gargantuan anyway so we're pretty + // unlikely to hit them, but might still exceed it. + // We consult ARG_MAX here to get an estimate. + #[cfg(unix)] + { + let ptr_size = mem::size_of::(); + // arg + \0 + pointer + let args_size = self.args.iter().fold(0usize, |acc, a| { + let arg = a.as_encoded_bytes().len(); + let nul = 1; + acc.saturating_add(arg).saturating_add(nul).saturating_add(ptr_size) + }); + // key + `=` + value + \0 + pointer + let envs_size = self.env.iter().fold(0usize, |acc, (k, v)| { + let k = k.as_encoded_bytes().len(); + let eq = 1; + let v = v.as_encoded_bytes().len(); + let nul = 1; + acc.saturating_add(k) + .saturating_add(eq) + .saturating_add(v) + .saturating_add(nul) + .saturating_add(ptr_size) + }); + let arg_max = match unsafe { libc::sysconf(libc::_SC_ARG_MAX) } { + -1 => return false, // Go to OS anyway. + max => max as usize, + }; + return args_size.saturating_add(envs_size) > arg_max; } // Ok so on Windows to spawn a process is 32,768 characters in its @@ -172,9 +195,14 @@ impl Command { // // [1]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa // [2]: https://devblogs.microsoft.com/oldnewthing/?p=41553 - - let estimated_command_line_len = self.args.iter().map(|a| a.len()).sum::(); - estimated_command_line_len > 1024 * 6 + #[cfg(windows)] + { + let estimated_command_line_len = self + .args + .iter() + .fold(0usize, |acc, a| acc.saturating_add(a.as_encoded_bytes().len())); + return estimated_command_line_len > 1024 * 6; + } } } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 5054ae561c043..c5792da267819 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1,3 +1,5 @@ +mod raw_dylib; + use std::collections::BTreeSet; use std::ffi::OsString; use std::fs::{File, OpenOptions, read}; @@ -12,15 +14,17 @@ use itertools::Itertools; use regex::Regex; use rustc_arena::TypedArena; use rustc_ast::CRATE_NODE_ID; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{DiagCtxtHandle, LintDiagnostic}; -use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; +use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_macros::LintDiagnostic; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; -use rustc_metadata::{find_native_static_library, walk_native_lib_search_dirs}; +use rustc_metadata::{ + NativeLibSearchFallback, find_native_static_library, walk_native_lib_search_dirs, +}; use rustc_middle::bug; use rustc_middle::lint::lint_level; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; @@ -30,7 +34,6 @@ use rustc_session::config::{ self, CFGuard, CrateType, DebugInfo, LinkerFeaturesCli, OutFileName, OutputFilenames, OutputType, PrintKind, SplitDwarfKind, Strip, }; -use rustc_session::cstore::DllImport; use rustc_session::lint::builtin::LINKER_MESSAGES; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; use rustc_session::search_paths::PathKind; @@ -41,22 +44,20 @@ use rustc_session::{Session, filesearch}; use rustc_span::Symbol; use rustc_target::spec::crt_objects::CrtObjects; use rustc_target::spec::{ - Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFeatures, - LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, - SplitDebuginfo, + BinaryFormat, Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, + LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, + SanitizerSet, SplitDebuginfo, }; -use tempfile::Builder as TempFileBuilder; use tracing::{debug, info, warn}; -use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder, ImportLibraryItem}; +use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder}; use super::command::Command; use super::linker::{self, Linker}; use super::metadata::{MetadataPosition, create_wrapper_file}; use super::rpath::{self, RPathConfig}; use super::{apple, versioned_llvm_target}; use crate::{ - CodegenResults, CompiledModule, CrateInfo, NativeLib, common, errors, - looks_like_rust_object_file, + CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file, }; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { @@ -98,7 +99,7 @@ pub fn link_binary( }); if outputs.outputs.should_link() { - let tmpdir = TempFileBuilder::new() + let tmpdir = TempDirBuilder::new() .prefix("rustc") .tempdir() .unwrap_or_else(|error| sess.dcx().emit_fatal(errors::CreateTempDir { error })); @@ -110,8 +111,12 @@ pub fn link_binary( codegen_results.crate_info.local_crate_name, ); let crate_name = format!("{}", codegen_results.crate_info.local_crate_name); - let out_filename = - output.file_for_writing(outputs, OutputType::Exe, Some(crate_name.as_str())); + let out_filename = output.file_for_writing( + outputs, + OutputType::Exe, + &crate_name, + sess.invocation_temp.as_deref(), + ); match crate_type { CrateType::Rlib => { let _timer = sess.timer("link_rlib"); @@ -149,17 +154,17 @@ pub fn link_binary( sess.dcx().emit_artifact_notification(&out_filename, "link"); } - if sess.prof.enabled() { - if let Some(artifact_name) = out_filename.file_name() { - // Record size for self-profiling - let file_size = std::fs::metadata(&out_filename).map(|m| m.len()).unwrap_or(0); - - sess.prof.artifact_size( - "linked_artifact", - artifact_name.to_string_lossy(), - file_size, - ); - } + if sess.prof.enabled() + && let Some(artifact_name) = out_filename.file_name() + { + // Record size for self-profiling + let file_size = std::fs::metadata(&out_filename).map(|m| m.len()).unwrap_or(0); + + sess.prof.artifact_size( + "linked_artifact", + artifact_name.to_string_lossy(), + file_size, + ); } if output.is_stdout() { @@ -184,16 +189,12 @@ pub fn link_binary( let maybe_remove_temps_from_module = |preserve_objects: bool, preserve_dwarf_objects: bool, module: &CompiledModule| { - if !preserve_objects { - if let Some(ref obj) = module.object { - ensure_removed(sess.dcx(), obj); - } + if !preserve_objects && let Some(ref obj) = module.object { + ensure_removed(sess.dcx(), obj); } - if !preserve_dwarf_objects { - if let Some(ref dwo_obj) = module.dwarf_object { - ensure_removed(sess.dcx(), dwo_obj); - } + if !preserve_dwarf_objects && let Some(ref dwo_obj) = module.dwarf_object { + ensure_removed(sess.dcx(), dwo_obj); } }; @@ -296,7 +297,7 @@ fn link_rlib<'a>( let (metadata, metadata_position) = create_wrapper_file( sess, ".rmeta".to_string(), - codegen_results.metadata.raw_data(), + codegen_results.metadata.stub_or_full(), ); let metadata = emit_wrapper_file(sess, &metadata, tmpdir, METADATA_FILENAME); match metadata_position { @@ -376,16 +377,22 @@ fn link_rlib<'a>( } } - for output_path in create_dll_import_libs( - sess, - archive_builder_builder, - codegen_results.crate_info.used_libraries.iter(), - tmpdir.as_ref(), - true, - ) { - ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { - sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error }); - }); + // On Windows, we add the raw-dylib import libraries to the rlibs already. + // But on ELF, this is not possible, as a shared object cannot be a member of a static library. + // Instead, we add all raw-dylibs to the final link on ELF. + if sess.target.is_like_windows { + for output_path in raw_dylib::create_raw_dylib_dll_import_libs( + sess, + archive_builder_builder, + codegen_results.crate_info.used_libraries.iter(), + tmpdir.as_ref(), + true, + ) { + ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { + sess.dcx() + .emit_fatal(errors::AddNativeLibrary { library_path: output_path, error }); + }); + } } if let Some(trailing_metadata) = trailing_metadata { @@ -426,108 +433,6 @@ fn link_rlib<'a>( ab } -/// Extract all symbols defined in raw-dylib libraries, collated by library name. -/// -/// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library, -/// then the CodegenResults value contains one NativeLib instance for each block. However, the -/// linker appears to expect only a single import library for each library used, so we need to -/// collate the symbols together by library name before generating the import libraries. -fn collate_raw_dylibs<'a>( - sess: &Session, - used_libraries: impl IntoIterator, -) -> Vec<(String, Vec)> { - // Use index maps to preserve original order of imports and libraries. - let mut dylib_table = FxIndexMap::>::default(); - - for lib in used_libraries { - if lib.kind == NativeLibKind::RawDylib { - let ext = if lib.verbatim { "" } else { ".dll" }; - let name = format!("{}{}", lib.name, ext); - let imports = dylib_table.entry(name.clone()).or_default(); - for import in &lib.dll_imports { - if let Some(old_import) = imports.insert(import.name, import) { - // FIXME: when we add support for ordinals, figure out if we need to do anything - // if we have two DllImport values with the same name but different ordinals. - if import.calling_convention != old_import.calling_convention { - sess.dcx().emit_err(errors::MultipleExternalFuncDecl { - span: import.span, - function: import.name, - library_name: &name, - }); - } - } - } - } - } - sess.dcx().abort_if_errors(); - dylib_table - .into_iter() - .map(|(name, imports)| { - (name, imports.into_iter().map(|(_, import)| import.clone()).collect()) - }) - .collect() -} - -fn create_dll_import_libs<'a>( - sess: &Session, - archive_builder_builder: &dyn ArchiveBuilderBuilder, - used_libraries: impl IntoIterator, - tmpdir: &Path, - is_direct_dependency: bool, -) -> Vec { - collate_raw_dylibs(sess, used_libraries) - .into_iter() - .map(|(raw_dylib_name, raw_dylib_imports)| { - let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; - let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib")); - - let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target); - - let items: Vec = raw_dylib_imports - .iter() - .map(|import: &DllImport| { - if sess.target.arch == "x86" { - ImportLibraryItem { - name: common::i686_decorated_name( - import, - mingw_gnu_toolchain, - false, - false, - ), - ordinal: import.ordinal(), - symbol_name: import.is_missing_decorations().then(|| { - common::i686_decorated_name( - import, - mingw_gnu_toolchain, - false, - true, - ) - }), - is_data: !import.is_fn, - } - } else { - ImportLibraryItem { - name: import.name.to_string(), - ordinal: import.ordinal(), - symbol_name: None, - is_data: !import.is_fn, - } - } - }) - .collect(); - - archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - items, - &output_path, - ); - - output_path - }) - .collect() -} - /// Create a static archive. /// /// This is essentially the same thing as an rlib, but it also involves adding all of the upstream @@ -768,7 +673,7 @@ fn link_natively( ) { info!("preparing {:?} to {:?}", crate_type, out_filename); let (linker_path, flavor) = linker_and_flavor(sess); - let self_contained_components = self_contained_components(sess, crate_type); + let self_contained_components = self_contained_components(sess, crate_type, &linker_path); // On AIX, we ship all libraries as .a big_af archive // the expected format is lib.a(libname.so) for the actual @@ -863,7 +768,7 @@ fn link_natively( && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-fuse-ld=lld") { info!("linker output: {:?}", out); - warn!("The linker driver does not support `-fuse-ld=lld`. Retrying without it."); + info!("The linker driver does not support `-fuse-ld=lld`. Retrying without it."); for arg in cmd.take_args() { if arg.to_string_lossy() != "-fuse-ld=lld" { cmd.arg(arg); @@ -1057,9 +962,9 @@ fn link_natively( } } - let (level, src) = codegen_results.crate_info.lint_levels.linker_messages; + let level = codegen_results.crate_info.lint_levels.linker_messages; let lint = |msg| { - lint_level(sess, LINKER_MESSAGES, level, src, None, |diag| { + lint_level(sess, LINKER_MESSAGES, level, None, |diag| { LinkerOutput { inner: msg }.decorate_lint(diag) }) }; @@ -1110,7 +1015,7 @@ fn link_natively( // On macOS the external `dsymutil` tool is used to create the packed // debug information. Note that this will read debug information from // the objects on the filesystem which we'll clean up later. - SplitDebuginfo::Packed if sess.target.is_like_osx => { + SplitDebuginfo::Packed if sess.target.is_like_darwin => { let prog = Command::new("dsymutil").arg(out_filename).output(); match prog { Ok(prog) => { @@ -1141,16 +1046,17 @@ fn link_natively( let strip = sess.opts.cg.strip; - if sess.target.is_like_osx { + if sess.target.is_like_darwin { let stripcmd = "rust-objcopy"; match (strip, crate_type) { (Strip::Debuginfo, _) => { strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-debug"]) } // Per the manpage, `-x` is the maximum safe strip level for dynamic libraries. (#93988) - (Strip::Symbols, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro) => { - strip_with_external_utility(sess, stripcmd, out_filename, &["-x"]) - } + ( + Strip::Symbols, + CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib, + ) => strip_with_external_utility(sess, stripcmd, out_filename, &["-x"]), (Strip::Symbols, _) => { strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-all"]) } @@ -1273,7 +1179,7 @@ mod win { let mut cp: u32 = 0; // We're using the `LOCALE_RETURN_NUMBER` flag to return a u32. // But the API requires us to pass the data as though it's a [u16] string. - let len = std::mem::size_of::() / std::mem::size_of::(); + let len = size_of::() / size_of::(); let data = std::slice::from_raw_parts_mut(&mut cp as *mut u32 as *mut u16, len); let len_written = GetLocaleInfoEx( LOCALE_NAME_SYSTEM_DEFAULT, @@ -1338,8 +1244,10 @@ fn add_sanitizer_libraries( // which should be linked to both executables and dynamic libraries. // Everywhere else the runtimes are currently distributed as static // libraries which should be linked to executables only. - if matches!(crate_type, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro) - && !(sess.target.is_like_osx || sess.target.is_like_msvc) + if matches!( + crate_type, + CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro | CrateType::Sdylib + ) && !(sess.target.is_like_darwin || sess.target.is_like_msvc) { return; } @@ -1382,8 +1290,7 @@ fn link_sanitizer_runtime( if path.exists() { sess.target_tlib_path.dir.clone() } else { - let default_sysroot = - filesearch::get_or_default_sysroot().expect("Failed finding sysroot"); + let default_sysroot = filesearch::get_or_default_sysroot(); let default_tlib = filesearch::make_target_lib_path(&default_sysroot, sess.opts.target_triple.tuple()); default_tlib @@ -1393,7 +1300,7 @@ fn link_sanitizer_runtime( let channel = option_env!("CFG_RELEASE_CHANNEL").map(|channel| format!("-{channel}")).unwrap_or_default(); - if sess.target.is_like_osx { + if sess.target.is_like_darwin { // On Apple platforms, the sanitizer is always built as a dylib, and // LLVM will link to `@rpath/*.dylib`, so we need to specify an // rpath to the library as well (the rpath should be absolute, see @@ -1593,7 +1500,8 @@ fn print_native_static_libs( | NativeLibKind::Unspecified => { let verbatim = lib.verbatim; if sess.target.is_like_msvc { - Some(format!("{}{}", name, if verbatim { "" } else { ".lib" })) + let (prefix, suffix) = sess.staticlib_components(verbatim); + Some(format!("{prefix}{name}{suffix}")) } else if sess.target.linker_flavor.is_gnu() { Some(format!("-l{}{}", if verbatim { ":" } else { "" }, name)) } else { @@ -1633,8 +1541,13 @@ fn print_native_static_libs( } let stem = path.file_stem().unwrap().to_str().unwrap(); // Convert library file-stem into a cc -l argument. - let prefix = if stem.starts_with("lib") && !sess.target.is_like_windows { 3 } else { 0 }; - let lib = &stem[prefix..]; + let lib = if let Some(lib) = stem.strip_prefix("lib") + && !sess.target.is_like_windows + { + lib + } else { + stem + }; let path = parent.unwrap_or_else(|| Path::new("")); if sess.target.is_like_msvc { // When producing a dll, the MSVC linker may not actually emit a @@ -1653,17 +1566,13 @@ fn print_native_static_libs( match out { OutFileName::Real(path) => { out.overwrite(&lib_args.join(" "), sess); - if !lib_args.is_empty() { - sess.dcx().emit_note(errors::StaticLibraryNativeArtifactsToFile { path }); - } + sess.dcx().emit_note(errors::StaticLibraryNativeArtifactsToFile { path }); } OutFileName::Stdout => { - if !lib_args.is_empty() { - sess.dcx().emit_note(errors::StaticLibraryNativeArtifacts); - // Prefix for greppability - // Note: This must not be translated as tools are allowed to depend on this exact string. - sess.dcx().note(format!("native-static-libs: {}", lib_args.join(" "))); - } + sess.dcx().emit_note(errors::StaticLibraryNativeArtifacts); + // Prefix for greppability + // Note: This must not be translated as tools are allowed to depend on this exact string. + sess.dcx().note(format!("native-static-libs: {}", lib_args.join(" "))); } } } @@ -1877,8 +1786,7 @@ 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); +fn detect_self_contained_mingw(sess: &Session, linker: &Path) -> bool { // Assume `-C linker=rust-lld` as self-contained mode if linker == Path::new("rust-lld") { return true; @@ -1886,7 +1794,7 @@ fn detect_self_contained_mingw(sess: &Session) -> bool { let linker_with_extension = if cfg!(windows) && linker.extension().is_none() { linker.with_extension("exe") } else { - linker + linker.to_path_buf() }; for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { let full_path = dir.join(&linker_with_extension); @@ -1901,7 +1809,11 @@ fn detect_self_contained_mingw(sess: &Session) -> bool { /// Various toolchain components used during linking are used from rustc distribution /// instead of being found somewhere on the host system. /// We only provide such support for a very limited number of targets. -fn self_contained_components(sess: &Session, crate_type: CrateType) -> LinkSelfContainedComponents { +fn self_contained_components( + sess: &Session, + crate_type: CrateType, + linker: &Path, +) -> LinkSelfContainedComponents { // Turn the backwards compatible bool values for `self_contained` into fully inferred // `LinkSelfContainedComponents`. let self_contained = @@ -1930,7 +1842,7 @@ fn self_contained_components(sess: &Session, crate_type: CrateType) -> LinkSelfC LinkSelfContainedDefault::InferredForMingw => { sess.host == sess.target && sess.target.vendor != "uwp" - && detect_self_contained_mingw(sess) + && detect_self_contained_mingw(sess, linker) } } }; @@ -2029,6 +1941,7 @@ fn add_late_link_args( codegen_results: &CodegenResults, ) { let any_dynamic_crate = crate_type == CrateType::Dylib + || crate_type == CrateType::Sdylib || codegen_results.crate_info.dependency_formats.iter().any(|(ty, list)| { *ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic) }); @@ -2105,6 +2018,12 @@ fn add_linked_symbol_object( file.set_mangling(object::write::Mangling::None); } + if file.format() == object::BinaryFormat::MachO { + // Divide up the sections into sub-sections via symbols for dead code stripping. + // Without this flag, unused `#[no_mangle]` or `#[used]` cannot be discard on MachO targets. + file.set_subsections_via_symbols(); + } + // ld64 requires a relocation to load undefined symbols, see below. // Not strictly needed if linking with lld, but might as well do it there too. let ld64_section_helper = if file.format() == object::BinaryFormat::MachO { @@ -2206,11 +2125,11 @@ fn add_local_crate_metadata_objects( // When linking a dynamic library, we put the metadata into a section of the // executable. This metadata is in a separate object file from the main // object file, so we link that in here. - if crate_type == CrateType::Dylib || crate_type == CrateType::ProcMacro { - if let Some(obj) = codegen_results.metadata_module.as_ref().and_then(|m| m.object.as_ref()) - { - cmd.add_object(obj); - } + if matches!(crate_type, CrateType::Dylib | CrateType::ProcMacro) + && let Some(m) = &codegen_results.metadata_module + && let Some(obj) = &m.object + { + cmd.add_object(obj); } } @@ -2225,19 +2144,15 @@ fn add_library_search_dirs( return; } - walk_native_lib_search_dirs( - sess, - self_contained_components, - apple_sdk_root, - |dir, is_framework| { - if is_framework { - cmd.framework_path(dir); - } else { - cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); - } - ControlFlow::<()>::Continue(()) - }, - ); + let fallback = Some(NativeLibSearchFallback { self_contained_components, apple_sdk_root }); + let _ = walk_native_lib_search_dirs(sess, fallback, |dir, is_framework| { + if is_framework { + cmd.framework_path(dir); + } else { + cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); + } + ControlFlow::<()>::Continue(()) + }); } /// Add options making relocation sections in the produced ELF files read-only @@ -2280,7 +2195,7 @@ fn add_rpath_args( let rpath_config = RPathConfig { libs: &*libs, out_filename: out_filename.to_path_buf(), - is_like_osx: sess.target.is_like_osx, + is_like_darwin: sess.target.is_like_darwin, linker_is_gnu: sess.target.linker_flavor.is_gnu(), }; cmd.link_args(&rpath::get_rpath_linker_args(&rpath_config)); @@ -2422,15 +2337,39 @@ fn linker_with_args( link_output_kind, ); + // Raw-dylibs from all crates. + let raw_dylib_dir = tmpdir.join("raw-dylibs"); + if sess.target.binary_format == BinaryFormat::Elf { + // On ELF we can't pass the raw-dylibs stubs to the linker as a path, + // instead we need to pass them via -l. To find the stub, we need to add + // the directory of the stub to the linker search path. + // We make an extra directory for this to avoid polluting the search path. + if let Err(error) = fs::create_dir(&raw_dylib_dir) { + sess.dcx().emit_fatal(errors::CreateTempDir { error }) + } + cmd.include_path(&raw_dylib_dir); + } + // Link with the import library generated for any raw-dylib functions. - for output_path in create_dll_import_libs( - sess, - archive_builder_builder, - codegen_results.crate_info.used_libraries.iter(), - tmpdir, - true, - ) { - cmd.add_object(&output_path); + if sess.target.is_like_windows { + for output_path in raw_dylib::create_raw_dylib_dll_import_libs( + sess, + archive_builder_builder, + codegen_results.crate_info.used_libraries.iter(), + tmpdir, + true, + ) { + cmd.add_object(&output_path); + } + } else { + for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects( + sess, + codegen_results.crate_info.used_libraries.iter(), + &raw_dylib_dir, + ) { + // Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects. + cmd.link_dylib_by_name(&link_path, true, false); + } } // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case // they are used within inlined functions or instantiated generic functions. We do this *after* @@ -2449,19 +2388,35 @@ fn linker_with_args( .native_libraries .iter() .filter_map(|(&cnum, libraries)| { - (dependency_linkage[cnum] != Linkage::Static).then_some(libraries) + if sess.target.is_like_windows { + (dependency_linkage[cnum] != Linkage::Static).then_some(libraries) + } else { + Some(libraries) + } }) .flatten() .collect::>(); native_libraries_from_nonstatics.sort_unstable_by(|a, b| a.name.as_str().cmp(b.name.as_str())); - for output_path in create_dll_import_libs( - sess, - archive_builder_builder, - native_libraries_from_nonstatics, - tmpdir, - false, - ) { - cmd.add_object(&output_path); + + if sess.target.is_like_windows { + for output_path in raw_dylib::create_raw_dylib_dll_import_libs( + sess, + archive_builder_builder, + native_libraries_from_nonstatics, + tmpdir, + false, + ) { + cmd.add_object(&output_path); + } + } else { + for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects( + sess, + native_libraries_from_nonstatics, + &raw_dylib_dir, + ) { + // Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects. + cmd.link_dylib_by_name(&link_path, true, false); + } } // Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make @@ -2594,10 +2549,11 @@ fn add_order_independent_options( cmd.output_filename(out_filename); - if crate_type == CrateType::Executable && sess.target.is_like_windows { - if let Some(ref s) = codegen_results.crate_info.windows_subsystem { - cmd.subsystem(s); - } + if crate_type == CrateType::Executable + && sess.target.is_like_windows + && let Some(s) = &codegen_results.crate_info.windows_subsystem + { + cmd.subsystem(s); } // Try to strip as much out of the generated object by removing unused @@ -3101,7 +3057,7 @@ pub(crate) fn are_upstream_rust_objects_already_included(sess: &Session) -> bool /// - The deployment target. /// - The SDK version. fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { - if !sess.target.is_like_osx { + if !sess.target.is_like_darwin { return; } let LinkerFlavor::Darwin(cc, _) = flavor else { @@ -3172,8 +3128,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo _ => bug!("invalid OS/ABI combination for Apple target: {target_os}, {target_abi}"), }; - let (major, minor, patch) = apple::deployment_target(sess); - let min_version = format!("{major}.{minor}.{patch}"); + let min_version = sess.apple_deployment_target().fmt_full().to_string(); // The SDK version is used at runtime when compiling with a newer SDK / version of Xcode: // - By dyld to give extra warnings and errors, see e.g.: @@ -3242,10 +3197,10 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo // The presence of `-mmacosx-version-min` makes CC default to // macOS, and it sets the deployment target. - let (major, minor, patch) = apple::deployment_target(sess); + let version = sess.apple_deployment_target().fmt_full(); // Intentionally pass this as a single argument, Clang doesn't // seem to like it otherwise. - cmd.cc_arg(&format!("-mmacosx-version-min={major}.{minor}.{patch}")); + cmd.cc_arg(&format!("-mmacosx-version-min={version}")); // macOS has no environment, so with these two, we've told CC the // four desired parameters. @@ -3258,9 +3213,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo } fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> Option { - let arch = &sess.target.arch; let os = &sess.target.os; - let llvm_target = &sess.target.llvm_target; if sess.target.vendor != "apple" || !matches!(os.as_ref(), "ios" | "tvos" | "watchos" | "visionos" | "macos") || !matches!(flavor, LinkerFlavor::Darwin(..)) @@ -3272,37 +3225,7 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> return None; } - let sdk_name = match (arch.as_ref(), os.as_ref()) { - ("aarch64", "tvos") if llvm_target.ends_with("-simulator") => "appletvsimulator", - ("aarch64", "tvos") => "appletvos", - ("x86_64", "tvos") => "appletvsimulator", - ("arm", "ios") => "iphoneos", - ("aarch64", "ios") if llvm_target.contains("macabi") => "macosx", - ("aarch64", "ios") if llvm_target.ends_with("-simulator") => "iphonesimulator", - ("aarch64", "ios") => "iphoneos", - ("x86", "ios") => "iphonesimulator", - ("x86_64", "ios") if llvm_target.contains("macabi") => "macosx", - ("x86_64", "ios") => "iphonesimulator", - ("x86_64", "watchos") => "watchsimulator", - ("arm64_32", "watchos") => "watchos", - ("aarch64", "watchos") if llvm_target.ends_with("-simulator") => "watchsimulator", - ("aarch64", "watchos") => "watchos", - ("aarch64", "visionos") if llvm_target.ends_with("-simulator") => "xrsimulator", - ("aarch64", "visionos") => "xros", - ("arm", "watchos") => "watchos", - (_, "macos") => "macosx", - _ => { - sess.dcx().emit_err(errors::UnsupportedArch { arch, os }); - return None; - } - }; - let sdk_root = match get_apple_sdk_root(sdk_name) { - Ok(s) => s, - Err(e) => { - sess.dcx().emit_err(e); - return None; - } - }; + let sdk_root = sess.time("get_apple_sdk_root", || get_apple_sdk_root(sess))?; match flavor { LinkerFlavor::Darwin(Cc::Yes, _) => { @@ -3312,28 +3235,32 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> // This is admittedly a bit strange, as on most targets // `-isysroot` only applies to include header files, but on Apple // targets this also applies to libraries and frameworks. - cmd.cc_args(&["-isysroot", &sdk_root]); + cmd.cc_arg("-isysroot"); + cmd.cc_arg(&sdk_root); } LinkerFlavor::Darwin(Cc::No, _) => { - cmd.link_args(&["-syslibroot", &sdk_root]); + cmd.link_arg("-syslibroot"); + cmd.link_arg(&sdk_root); } _ => unreachable!(), } - Some(sdk_root.into()) + Some(sdk_root) } -fn get_apple_sdk_root(sdk_name: &str) -> Result> { - // Following what clang does - // (https://github.com/llvm/llvm-project/blob/ - // 296a80102a9b72c3eda80558fb78a3ed8849b341/clang/lib/Driver/ToolChains/Darwin.cpp#L1661-L1678) - // to allow the SDK path to be set. (For clang, xcrun sets - // SDKROOT; for rustc, the user or build system can set it, or we - // can fall back to checking for xcrun on PATH.) +fn get_apple_sdk_root(sess: &Session) -> Option { if let Ok(sdkroot) = env::var("SDKROOT") { - let p = Path::new(&sdkroot); - match sdk_name { - // Ignore `SDKROOT` if it's clearly set for the wrong platform. + let p = PathBuf::from(&sdkroot); + + // Ignore invalid SDKs, similar to what clang does: + // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.6/clang/lib/Driver/ToolChains/Darwin.cpp#L2212-L2229 + // + // NOTE: Things are complicated here by the fact that `rustc` can be run by Cargo to compile + // build scripts and proc-macros for the host, and thus we need to ignore SDKROOT if it's + // clearly set for the wrong platform. + // + // FIXME(madsmtm): Make this more robust (maybe read `SDKSettings.json` like Clang does?). + match &*apple::sdk_name(&sess.target).to_lowercase() { "appletvos" if sdkroot.contains("TVSimulator.platform") || sdkroot.contains("MacOSX.platform") => {} @@ -3360,26 +3287,11 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result {} // Ignore `SDKROOT` if it's not a valid path. _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} - _ => return Ok(sdkroot), + _ => return Some(p), } } - let res = - Command::new("xcrun").arg("--show-sdk-path").arg("-sdk").arg(sdk_name).output().and_then( - |output| { - if output.status.success() { - Ok(String::from_utf8(output.stdout).unwrap()) - } else { - let error = String::from_utf8(output.stderr); - let error = format!("process exit with error: {}", error.unwrap()); - Err(io::Error::new(io::ErrorKind::Other, &error[..])) - } - }, - ); - match res { - Ok(output) => Ok(output.trim().to_string()), - Err(error) => Err(errors::AppleSdkRootError::SdkPath { sdk_name, error }), - } + apple::get_sdk_root(sess) } /// When using the linker flavors opting in to `lld`, add the necessary paths and arguments to @@ -3438,6 +3350,35 @@ fn add_lld_args( // this, `wasm-component-ld`, which is overridden if this option is passed. if !sess.target.is_like_wasm { cmd.cc_arg("-fuse-ld=lld"); + + // On ELF platforms like at least x64 linux, GNU ld and LLD have opposite defaults on some + // section garbage-collection features. For example, the somewhat popular `linkme` crate and + // its dependents rely in practice on this difference: when using lld, they need `-z + // nostart-stop-gc` to prevent encapsulation symbols and sections from being + // garbage-collected. + // + // More information about all this can be found in: + // - https://maskray.me/blog/2021-01-31-metadata-sections-comdat-and-shf-link-order + // - https://lld.llvm.org/ELF/start-stop-gc + // + // So when using lld, we restore, for now, the traditional behavior to help migration, but + // will remove it in the future. + // Since this only disables an optimization, it shouldn't create issues, but is in theory + // slightly suboptimal. However, it: + // - doesn't have any visible impact on our benchmarks + // - reduces the need to disable lld for the crates that depend on this + // + // Note that lld can detect some cases where this difference is relied on, and emits a + // dedicated error to add this link arg. We could make use of this error to emit an FCW. As + // of writing this, we don't do it, because lld is already enabled by default on nightly + // without this mitigation: no working project would see the FCW, so we do this to help + // stabilization. + // + // FIXME: emit an FCW if linking fails due its absence, and then remove this link-arg in the + // future. + if sess.target.llvm_target == "x86_64-unknown-linux-gnu" { + cmd.link_arg("-znostart-stop-gc"); + } } if !flavor.is_gnu() { diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs new file mode 100644 index 0000000000000..2c24378afe13b --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs @@ -0,0 +1,373 @@ +use std::fs; +use std::io::{BufWriter, Write}; +use std::path::{Path, PathBuf}; + +use rustc_abi::Endian; +use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; +use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_hashes::Hash128; +use rustc_session::Session; +use rustc_session::cstore::DllImport; +use rustc_session::utils::NativeLibKind; +use rustc_span::Symbol; + +use crate::back::archive::ImportLibraryItem; +use crate::back::link::ArchiveBuilderBuilder; +use crate::errors::ErrorCreatingImportLibrary; +use crate::{NativeLib, common, errors}; + +/// Extract all symbols defined in raw-dylib libraries, collated by library name. +/// +/// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library, +/// then the CodegenResults value contains one NativeLib instance for each block. However, the +/// linker appears to expect only a single import library for each library used, so we need to +/// collate the symbols together by library name before generating the import libraries. +fn collate_raw_dylibs_windows<'a>( + sess: &Session, + used_libraries: impl IntoIterator, +) -> Vec<(String, Vec)> { + // Use index maps to preserve original order of imports and libraries. + let mut dylib_table = FxIndexMap::>::default(); + + for lib in used_libraries { + if lib.kind == NativeLibKind::RawDylib { + let ext = if lib.verbatim { "" } else { ".dll" }; + let name = format!("{}{}", lib.name, ext); + let imports = dylib_table.entry(name.clone()).or_default(); + for import in &lib.dll_imports { + if let Some(old_import) = imports.insert(import.name, import) { + // FIXME: when we add support for ordinals, figure out if we need to do anything + // if we have two DllImport values with the same name but different ordinals. + if import.calling_convention != old_import.calling_convention { + sess.dcx().emit_err(errors::MultipleExternalFuncDecl { + span: import.span, + function: import.name, + library_name: &name, + }); + } + } + } + } + } + sess.dcx().abort_if_errors(); + dylib_table + .into_iter() + .map(|(name, imports)| { + (name, imports.into_iter().map(|(_, import)| import.clone()).collect()) + }) + .collect() +} + +pub(super) fn create_raw_dylib_dll_import_libs<'a>( + sess: &Session, + archive_builder_builder: &dyn ArchiveBuilderBuilder, + used_libraries: impl IntoIterator, + tmpdir: &Path, + is_direct_dependency: bool, +) -> Vec { + collate_raw_dylibs_windows(sess, used_libraries) + .into_iter() + .map(|(raw_dylib_name, raw_dylib_imports)| { + let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; + let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib")); + + let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target); + + let items: Vec = raw_dylib_imports + .iter() + .map(|import: &DllImport| { + if sess.target.arch == "x86" { + ImportLibraryItem { + name: common::i686_decorated_name( + import, + mingw_gnu_toolchain, + false, + false, + ), + ordinal: import.ordinal(), + symbol_name: import.is_missing_decorations().then(|| { + common::i686_decorated_name( + import, + mingw_gnu_toolchain, + false, + true, + ) + }), + is_data: !import.is_fn, + } + } else { + ImportLibraryItem { + name: import.name.to_string(), + ordinal: import.ordinal(), + symbol_name: None, + is_data: !import.is_fn, + } + } + }) + .collect(); + + archive_builder_builder.create_dll_import_lib( + sess, + &raw_dylib_name, + items, + &output_path, + ); + + output_path + }) + .collect() +} + +/// Extract all symbols defined in raw-dylib libraries, collated by library name. +/// +/// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library, +/// then the CodegenResults value contains one NativeLib instance for each block. However, the +/// linker appears to expect only a single import library for each library used, so we need to +/// collate the symbols together by library name before generating the import libraries. +fn collate_raw_dylibs_elf<'a>( + sess: &Session, + used_libraries: impl IntoIterator, +) -> Vec<(String, Vec)> { + // Use index maps to preserve original order of imports and libraries. + let mut dylib_table = FxIndexMap::>::default(); + + for lib in used_libraries { + if lib.kind == NativeLibKind::RawDylib { + let filename = if lib.verbatim { + lib.name.as_str().to_owned() + } else { + let ext = sess.target.dll_suffix.as_ref(); + let prefix = sess.target.dll_prefix.as_ref(); + format!("{prefix}{}{ext}", lib.name) + }; + + let imports = dylib_table.entry(filename.clone()).or_default(); + for import in &lib.dll_imports { + imports.insert(import.name, import); + } + } + } + sess.dcx().abort_if_errors(); + dylib_table + .into_iter() + .map(|(name, imports)| { + (name, imports.into_iter().map(|(_, import)| import.clone()).collect()) + }) + .collect() +} + +pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>( + sess: &Session, + used_libraries: impl IntoIterator, + raw_dylib_so_dir: &Path, +) -> Vec { + collate_raw_dylibs_elf(sess, used_libraries) + .into_iter() + .map(|(load_filename, raw_dylib_imports)| { + use std::hash::Hash; + + // `load_filename` is the *target/loader* filename that will end up in NEEDED. + // Usually this will be something like `libc.so` or `libc.so.6` but with + // verbatim it might also be an absolute path. + // To be able to support this properly, we always put this load filename + // into the SONAME of the library and link it via a temporary file with a random name. + // This also avoids naming conflicts with non-raw-dylib linkage of the same library. + + let shared_object = create_elf_raw_dylib_stub(sess, &load_filename, &raw_dylib_imports); + + let mut file_name_hasher = StableHasher::new(); + load_filename.hash(&mut file_name_hasher); + for raw_dylib in raw_dylib_imports { + raw_dylib.name.as_str().hash(&mut file_name_hasher); + } + + let library_filename: Hash128 = file_name_hasher.finish(); + let temporary_lib_name = format!( + "{}{}{}", + sess.target.dll_prefix, + library_filename.as_u128().to_base_fixed_len(CASE_INSENSITIVE), + sess.target.dll_suffix + ); + let link_path = raw_dylib_so_dir.join(&temporary_lib_name); + + let file = match fs::File::create_new(&link_path) { + Ok(file) => file, + Err(error) => sess.dcx().emit_fatal(ErrorCreatingImportLibrary { + lib_name: &load_filename, + error: error.to_string(), + }), + }; + if let Err(error) = BufWriter::new(file).write_all(&shared_object) { + sess.dcx().emit_fatal(ErrorCreatingImportLibrary { + lib_name: &load_filename, + error: error.to_string(), + }); + }; + + temporary_lib_name + }) + .collect() +} + +/// Create an ELF .so stub file for raw-dylib. +/// It exports all the provided symbols, but is otherwise empty. +fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]) -> Vec { + use object::write::elf as write; + use object::{Architecture, elf}; + + let mut stub_buf = Vec::new(); + + // Build the stub ELF using the object crate. + // The high-level portable API does not allow for the fine-grained control we need, + // so this uses the low-level object::write::elf API. + // The low-level API consists of two stages: reservation and writing. + // We first reserve space for all the things in the binary and then write them. + // It is important that the order of reservation matches the order of writing. + // The object crate contains many debug asserts that fire if you get this wrong. + + let endianness = match sess.target.options.endian { + Endian::Little => object::Endianness::Little, + Endian::Big => object::Endianness::Big, + }; + let mut stub = write::Writer::new(endianness, true, &mut stub_buf); + + // These initial reservations don't reserve any bytes in the binary yet, + // they just allocate in the internal data structures. + + // First, we crate the dynamic symbol table. It starts with a null symbol + // and then all the symbols and their dynamic strings. + stub.reserve_null_dynamic_symbol_index(); + + let dynstrs = symbols + .iter() + .map(|sym| { + stub.reserve_dynamic_symbol_index(); + (sym, stub.add_dynamic_string(sym.name.as_str().as_bytes())) + }) + .collect::>(); + + let soname = stub.add_dynamic_string(soname.as_bytes()); + + // Reserve the sections. + // We have the minimal sections for a dynamic SO and .text where we point our dummy symbols to. + stub.reserve_shstrtab_section_index(); + let text_section_name = stub.add_section_name(".text".as_bytes()); + let text_section = stub.reserve_section_index(); + stub.reserve_dynstr_section_index(); + stub.reserve_dynsym_section_index(); + stub.reserve_dynamic_section_index(); + + // These reservations now determine the actual layout order of the object file. + stub.reserve_file_header(); + stub.reserve_shstrtab(); + stub.reserve_section_headers(); + stub.reserve_dynstr(); + stub.reserve_dynsym(); + stub.reserve_dynamic(2); // DT_SONAME, DT_NULL + + // First write the ELF header with the arch information. + let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features) + else { + sess.dcx().fatal(format!( + "raw-dylib is not supported for the architecture `{}`", + sess.target.arch + )); + }; + let e_machine = match (arch, sub_arch) { + (Architecture::Aarch64, None) => elf::EM_AARCH64, + (Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64, + (Architecture::Arm, None) => elf::EM_ARM, + (Architecture::Avr, None) => elf::EM_AVR, + (Architecture::Bpf, None) => elf::EM_BPF, + (Architecture::Csky, None) => elf::EM_CSKY, + (Architecture::E2K32, None) => elf::EM_MCST_ELBRUS, + (Architecture::E2K64, None) => elf::EM_MCST_ELBRUS, + (Architecture::I386, None) => elf::EM_386, + (Architecture::X86_64, None) => elf::EM_X86_64, + (Architecture::X86_64_X32, None) => elf::EM_X86_64, + (Architecture::Hexagon, None) => elf::EM_HEXAGON, + (Architecture::LoongArch64, None) => elf::EM_LOONGARCH, + (Architecture::M68k, None) => elf::EM_68K, + (Architecture::Mips, None) => elf::EM_MIPS, + (Architecture::Mips64, None) => elf::EM_MIPS, + (Architecture::Mips64_N32, None) => elf::EM_MIPS, + (Architecture::Msp430, None) => elf::EM_MSP430, + (Architecture::PowerPc, None) => elf::EM_PPC, + (Architecture::PowerPc64, None) => elf::EM_PPC64, + (Architecture::Riscv32, None) => elf::EM_RISCV, + (Architecture::Riscv64, None) => elf::EM_RISCV, + (Architecture::S390x, None) => elf::EM_S390, + (Architecture::Sbf, None) => elf::EM_SBF, + (Architecture::Sharc, None) => elf::EM_SHARC, + (Architecture::Sparc, None) => elf::EM_SPARC, + (Architecture::Sparc32Plus, None) => elf::EM_SPARC32PLUS, + (Architecture::Sparc64, None) => elf::EM_SPARCV9, + (Architecture::Xtensa, None) => elf::EM_XTENSA, + _ => { + sess.dcx().fatal(format!( + "raw-dylib is not supported for the architecture `{}`", + sess.target.arch + )); + } + }; + + stub.write_file_header(&write::FileHeader { + os_abi: crate::back::metadata::elf_os_abi(sess), + abi_version: 0, + e_type: object::elf::ET_DYN, + e_machine, + e_entry: 0, + e_flags: crate::back::metadata::elf_e_flags(arch, sess), + }) + .unwrap(); + + // .shstrtab + stub.write_shstrtab(); + + // Section headers + stub.write_null_section_header(); + stub.write_shstrtab_section_header(); + // Create a dummy .text section for our dummy symbols. + stub.write_section_header(&write::SectionHeader { + name: Some(text_section_name), + sh_type: elf::SHT_PROGBITS, + sh_flags: 0, + sh_addr: 0, + sh_offset: 0, + sh_size: 0, + sh_link: 0, + sh_info: 0, + sh_addralign: 1, + sh_entsize: 0, + }); + stub.write_dynstr_section_header(0); + stub.write_dynsym_section_header(0, 1); + stub.write_dynamic_section_header(0); + + // .dynstr + stub.write_dynstr(); + + // .dynsym + stub.write_null_dynamic_symbol(); + for (_, name) in dynstrs { + stub.write_dynamic_symbol(&write::Sym { + name: Some(name), + st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE, + st_other: elf::STV_DEFAULT, + section: Some(text_section), + st_shndx: 0, // ignored by object in favor of the `section` field + st_value: 0, + st_size: 0, + }); + } + + // .dynamic + // the DT_SONAME will be used by the linker to populate DT_NEEDED + // which the loader uses to find the library. + // DT_NULL terminates the .dynamic table. + stub.write_dynamic_string(elf::DT_SONAME, soname); + stub.write_dynamic(elf::DT_NULL, 0); + + stub_buf +} diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 818edaf66031b..8fc83908efbcc 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -111,24 +111,22 @@ pub(crate) fn get_linker<'a>( // PATH for the child. let mut new_path = sess.get_tools_search_paths(self_contained); let mut msvc_changed_path = false; - if sess.target.is_like_msvc { - if let Some(ref tool) = msvc_tool { - cmd.args(tool.args()); - for (k, v) in tool.env() { - if k == "PATH" { - new_path.extend(env::split_paths(v)); - msvc_changed_path = true; - } else { - cmd.env(k, v); - } + if sess.target.is_like_msvc + && let Some(ref tool) = msvc_tool + { + cmd.args(tool.args()); + for (k, v) in tool.env() { + if k == "PATH" { + new_path.extend(env::split_paths(v)); + msvc_changed_path = true; + } else { + cmd.env(k, v); } } } - if !msvc_changed_path { - if let Some(path) = env::var_os("PATH") { - new_path.extend(env::split_paths(&path)); - } + if !msvc_changed_path && let Some(path) = env::var_os("PATH") { + new_path.extend(env::split_paths(&path)); } cmd.env("PATH", env::join_paths(new_path).unwrap()); @@ -375,7 +373,7 @@ impl<'a> GccLinker<'a> { // * On OSX they have their own linker, not binutils' // * For WebAssembly the only functional linker is LLD, which doesn't // support hint flags - !self.sess.target.is_like_osx && !self.sess.target.is_like_wasm + !self.sess.target.is_like_darwin && !self.sess.target.is_like_wasm } // Some platforms take hints about whether a library is static or dynamic. @@ -427,7 +425,7 @@ impl<'a> GccLinker<'a> { fn build_dylib(&mut self, crate_type: CrateType, out_filename: &Path) { // On mac we need to tell the linker to let this library be rpathed - if self.sess.target.is_like_osx { + if self.sess.target.is_like_darwin { if self.is_cc() { // `-dynamiclib` makes `cc` pass `-dylib` to the linker. self.cc_arg("-dynamiclib"); @@ -452,9 +450,10 @@ impl<'a> GccLinker<'a> { // The output filename already contains `dll_suffix` so // the resulting import library will have a name in the // form of libfoo.dll.a - let mut implib_name = OsString::from(&*self.sess.target.staticlib_prefix); + let (prefix, suffix) = self.sess.staticlib_components(false); + let mut implib_name = OsString::from(prefix); implib_name.push(name); - implib_name.push(&*self.sess.target.staticlib_suffix); + implib_name.push(suffix); let mut out_implib = OsString::from("--out-implib="); out_implib.push(out_filename.with_file_name(implib_name)); self.link_arg(out_implib); @@ -472,7 +471,7 @@ impl<'a> GccLinker<'a> { fn with_as_needed(&mut self, as_needed: bool, f: impl FnOnce(&mut Self)) { if !as_needed { - if self.sess.target.is_like_osx { + if self.sess.target.is_like_darwin { // FIXME(81490): ld64 doesn't support these flags but macOS 11 // has -needed-l{} / -needed_library {} // but we have no way to detect that here. @@ -487,7 +486,7 @@ impl<'a> GccLinker<'a> { f(self); if !as_needed { - if self.sess.target.is_like_osx { + if self.sess.target.is_like_darwin { // See above FIXME comment } else if self.is_gnu && !self.sess.target.is_like_windows { self.link_arg("--as-needed"); @@ -620,7 +619,7 @@ impl<'a> Linker for GccLinker<'a> { let colon = if verbatim && self.is_gnu { ":" } else { "" }; if !whole_archive { self.link_or_cc_arg(format!("-l{colon}{name}")); - } else if self.sess.target.is_like_osx { + } else if self.sess.target.is_like_darwin { // -force_load is the macOS equivalent of --whole-archive, but it // involves passing the full path to the library to link. self.link_arg("-force_load"); @@ -636,7 +635,7 @@ impl<'a> Linker for GccLinker<'a> { self.hint_static(); if !whole_archive { self.link_or_cc_arg(path); - } else if self.sess.target.is_like_osx { + } else if self.sess.target.is_like_darwin { self.link_arg("-force_load").link_arg(path); } else { self.link_arg("--whole-archive").link_arg(path).link_arg("--no-whole-archive"); @@ -671,7 +670,7 @@ impl<'a> Linker for GccLinker<'a> { // -dead_strip can't be part of the pre_link_args because it's also used // for partial linking when using multiple codegen units (-r). So we // insert it here. - if self.sess.target.is_like_osx { + if self.sess.target.is_like_darwin { self.link_arg("-dead_strip"); // If we're building a dylib, we don't use --gc-sections because LLVM @@ -729,7 +728,7 @@ impl<'a> Linker for GccLinker<'a> { fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) { // MacOS linker doesn't support stripping symbols directly anymore. - if self.sess.target.is_like_osx { + if self.sess.target.is_like_darwin { return; } @@ -796,7 +795,7 @@ impl<'a> Linker for GccLinker<'a> { debug!("EXPORTED SYMBOLS:"); - if self.sess.target.is_like_osx { + if self.sess.target.is_like_darwin { // Write a plain, newline-separated list of symbols let res: io::Result<()> = try { let mut f = File::create_buffered(&path)?; @@ -817,7 +816,9 @@ impl<'a> Linker for GccLinker<'a> { writeln!(f, "EXPORTS")?; for symbol in symbols { debug!(" _{symbol}"); - writeln!(f, " {symbol}")?; + // Quote the name in case it's reserved by linker in some way + // (this accounts for names with dots in particular). + writeln!(f, " \"{symbol}\"")?; } }; if let Err(error) = res { @@ -842,7 +843,7 @@ impl<'a> Linker for GccLinker<'a> { } } - if self.sess.target.is_like_osx { + if self.sess.target.is_like_darwin { self.link_arg("-exported_symbols_list").link_arg(path); } else if self.sess.target.is_like_solaris { self.link_arg("-M").link_arg(path); @@ -960,9 +961,9 @@ impl<'a> Linker for MsvcLinker<'a> { if let Some(path) = try_find_native_static_library(self.sess, name, verbatim) { self.link_staticlib_by_path(&path, whole_archive); } else { - let prefix = if whole_archive { "/WHOLEARCHIVE:" } else { "" }; - let suffix = if verbatim { "" } else { ".lib" }; - self.link_arg(format!("{prefix}{name}{suffix}")); + let opts = if whole_archive { "/WHOLEARCHIVE:" } else { "" }; + let (prefix, suffix) = self.sess.staticlib_components(verbatim); + self.link_arg(format!("{opts}{prefix}{name}{suffix}")); } } @@ -1655,9 +1656,9 @@ impl<'a> Linker for AixLinker<'a> { } } - fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) { + fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, _as_needed: bool) { self.hint_dynamic(); - self.link_or_cc_arg(format!("-l{name}")); + self.link_or_cc_arg(if verbatim { String::from(name) } else { format!("-l{name}") }); } fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) { @@ -1668,7 +1669,7 @@ impl<'a> Linker for AixLinker<'a> { fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { self.hint_static(); if !whole_archive { - self.link_or_cc_arg(format!("-l{name}")); + self.link_or_cc_arg(if verbatim { String::from(name) } else { format!("-l{name}") }); } else { let mut arg = OsString::from("-bkeepfile:"); arg.push(find_native_static_library(name, verbatim, self.sess)); @@ -1784,7 +1785,10 @@ fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) - let mut symbols = Vec::new(); let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { - if info.level.is_below_threshold(export_threshold) { + // Do not export mangled symbols from cdylibs and don't attempt to export compiler-builtins + // from any cdylib. The latter doesn't work anyway as we use hidden visibility for + // compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning. + if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) { symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate( tcx, symbol, cnum, )); @@ -1813,7 +1817,7 @@ pub(crate) fn linked_symbols( crate_type: CrateType, ) -> Vec<(String, SymbolExportKind)> { match crate_type { - CrateType::Executable | CrateType::Cdylib | CrateType::Dylib => (), + CrateType::Executable | CrateType::Cdylib | CrateType::Dylib | CrateType::Sdylib => (), CrateType::Staticlib | CrateType::ProcMacro | CrateType::Rlib => { return Vec::new(); } @@ -1823,7 +1827,9 @@ pub(crate) fn linked_symbols( let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { - if info.level.is_below_threshold(export_threshold) || info.used { + if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) + || info.used + { symbols.push(( symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum), info.kind, diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 3aacbcde1a783..ec46c71b0e401 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -9,8 +9,7 @@ use itertools::Itertools; use object::write::{self, StandardSegment, Symbol, SymbolSection}; use object::{ Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection, ObjectSymbol, - SectionFlags, SectionKind, SubArchitecture, SymbolFlags, SymbolKind, SymbolScope, elf, pe, - xcoff, + SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, elf, pe, xcoff, }; use rustc_abi::Endian; use rustc_data_structures::memmap::Mmap; @@ -206,57 +205,16 @@ pub(crate) fn create_object_file(sess: &Session) -> Option Endianness::Little, Endian::Big => Endianness::Big, }; - let (architecture, sub_architecture) = match &sess.target.arch[..] { - "arm" => (Architecture::Arm, None), - "aarch64" => ( - if sess.target.pointer_width == 32 { - Architecture::Aarch64_Ilp32 - } else { - Architecture::Aarch64 - }, - None, - ), - "x86" => (Architecture::I386, None), - "s390x" => (Architecture::S390x, None), - "mips" | "mips32r6" => (Architecture::Mips, None), - "mips64" | "mips64r6" => (Architecture::Mips64, None), - "x86_64" => ( - if sess.target.pointer_width == 32 { - Architecture::X86_64_X32 - } else { - Architecture::X86_64 - }, - None, - ), - "powerpc" => (Architecture::PowerPc, None), - "powerpc64" => (Architecture::PowerPc64, None), - "riscv32" => (Architecture::Riscv32, None), - "riscv64" => (Architecture::Riscv64, None), - "sparc" => { - if sess.unstable_target_features.contains(&sym::v8plus) { - // Target uses V8+, aka EM_SPARC32PLUS, aka 64-bit V9 but in 32-bit mode - (Architecture::Sparc32Plus, None) - } else { - // Target uses V7 or V8, aka EM_SPARC - (Architecture::Sparc, None) - } - } - "sparc64" => (Architecture::Sparc64, None), - "avr" => (Architecture::Avr, None), - "msp430" => (Architecture::Msp430, None), - "hexagon" => (Architecture::Hexagon, None), - "bpf" => (Architecture::Bpf, None), - "loongarch64" => (Architecture::LoongArch64, None), - "csky" => (Architecture::Csky, None), - "arm64ec" => (Architecture::Aarch64, Some(SubArchitecture::Arm64EC)), - // Unsupported architecture. - _ => return None, + let Some((architecture, sub_architecture)) = + sess.target.object_architecture(&sess.unstable_target_features) + else { + return None; }; let binary_format = sess.target.binary_format.to_object(); let mut file = write::Object::new(binary_format, architecture, endianness); file.set_sub_architecture(sub_architecture); - if sess.target.is_like_osx { + if sess.target.is_like_darwin { if macho_is_arm64e(&sess.target) { file.set_macho_cpu_subtype(object::macho::CPU_SUBTYPE_ARM64E); } @@ -292,46 +250,81 @@ pub(crate) fn create_object_file(sess: &Session) -> Option { - let arch = match sess.target.options.cpu.as_ref() { - "mips1" => elf::EF_MIPS_ARCH_1, - "mips2" => elf::EF_MIPS_ARCH_2, + let e_flags = elf_e_flags(architecture, sess); + // adapted from LLVM's `MCELFObjectTargetWriter::getOSABI` + let os_abi = elf_os_abi(sess); + let abi_version = 0; + add_gnu_property_note(&mut file, architecture, binary_format, endianness); + file.flags = FileFlags::Elf { os_abi, abi_version, e_flags }; + Some(file) +} + +pub(super) fn elf_os_abi(sess: &Session) -> u8 { + match sess.target.options.os.as_ref() { + "hermit" => elf::ELFOSABI_STANDALONE, + "freebsd" => elf::ELFOSABI_FREEBSD, + "solaris" => elf::ELFOSABI_SOLARIS, + _ => elf::ELFOSABI_NONE, + } +} + +pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 { + match architecture { + Architecture::Mips | Architecture::Mips64 | Architecture::Mips64_N32 => { + // "N32" indicates an "ILP32" data model on a 64-bit MIPS CPU + // like SPARC's "v8+", x86_64's "x32", or the watchOS "arm64_32". + let is_32bit = architecture == Architecture::Mips; + let mut e_flags = match sess.target.options.cpu.as_ref() { + "mips1" if is_32bit => elf::EF_MIPS_ARCH_1, + "mips2" if is_32bit => elf::EF_MIPS_ARCH_2, "mips3" => elf::EF_MIPS_ARCH_3, "mips4" => elf::EF_MIPS_ARCH_4, "mips5" => elf::EF_MIPS_ARCH_5, - s if s.contains("r6") => elf::EF_MIPS_ARCH_32R6, - _ => elf::EF_MIPS_ARCH_32R2, + "mips32r2" if is_32bit => elf::EF_MIPS_ARCH_32R2, + "mips32r6" if is_32bit => elf::EF_MIPS_ARCH_32R6, + "mips64r2" if !is_32bit => elf::EF_MIPS_ARCH_64R2, + "mips64r6" if !is_32bit => elf::EF_MIPS_ARCH_64R6, + s if s.starts_with("mips32") && !is_32bit => { + sess.dcx().fatal(format!("invalid CPU `{}` for 64-bit MIPS target", s)) + } + s if s.starts_with("mips64") && is_32bit => { + sess.dcx().fatal(format!("invalid CPU `{}` for 32-bit MIPS target", s)) + } + _ if is_32bit => elf::EF_MIPS_ARCH_32R2, + _ => elf::EF_MIPS_ARCH_64R2, }; - let mut e_flags = elf::EF_MIPS_CPIC | arch; - - // If the ABI is explicitly given, use it or default to O32. - match sess.target.options.llvm_abiname.to_lowercase().as_str() { - "n32" => e_flags |= elf::EF_MIPS_ABI2, - "o32" => e_flags |= elf::EF_MIPS_ABI_O32, - _ => e_flags |= elf::EF_MIPS_ABI_O32, + // If the ABI is explicitly given, use it, or default to O32 on 32-bit MIPS, + // which is the only "true" 32-bit option that LLVM supports. + match sess.target.options.llvm_abiname.as_ref() { + "o32" if is_32bit => e_flags |= elf::EF_MIPS_ABI_O32, + "n32" if !is_32bit => e_flags |= elf::EF_MIPS_ABI2, + "n64" if !is_32bit => {} + "" if is_32bit => e_flags |= elf::EF_MIPS_ABI_O32, + "" => sess.dcx().fatal("LLVM ABI must be specifed for 64-bit MIPS targets"), + s if is_32bit => { + sess.dcx().fatal(format!("invalid LLVM ABI `{}` for 32-bit MIPS target", s)) + } + s => sess.dcx().fatal(format!("invalid LLVM ABI `{}` for 64-bit MIPS target", s)), }; if sess.target.options.relocation_model != RelocModel::Static { - e_flags |= elf::EF_MIPS_PIC; + // PIC means position-independent code. CPIC means "calls PIC". + // CPIC was mutually exclusive with PIC according to + // the SVR4 MIPS ABI https://refspecs.linuxfoundation.org/elf/mipsabi.pdf + // and should have only appeared on static objects with dynamically calls. + // At some point someone (GCC?) decided to set CPIC even for PIC. + // Nowadays various things expect both set on the same object file + // and may even error if you mix CPIC and non-CPIC object files, + // despite that being the entire point of the CPIC ABI extension! + // As we are in Rome, we do as the Romans do. + e_flags |= elf::EF_MIPS_PIC | elf::EF_MIPS_CPIC; } if sess.target.options.cpu.contains("r6") { e_flags |= elf::EF_MIPS_NAN2008; } e_flags } - Architecture::Mips64 => { - // copied from `mips64el-linux-gnuabi64-gcc foo.c -c` - let e_flags = elf::EF_MIPS_CPIC - | elf::EF_MIPS_PIC - | if sess.target.options.cpu.contains("r6") { - elf::EF_MIPS_ARCH_64R6 | elf::EF_MIPS_NAN2008 - } else { - elf::EF_MIPS_ARCH_64R2 - }; - e_flags - } Architecture::Riscv32 | Architecture::Riscv64 => { // Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc let mut e_flags: u32 = 0x0; @@ -387,18 +380,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option 0, - }; - // adapted from LLVM's `MCELFObjectTargetWriter::getOSABI` - let os_abi = match sess.target.options.os.as_ref() { - "hermit" => elf::ELFOSABI_STANDALONE, - "freebsd" => elf::ELFOSABI_FREEBSD, - "solaris" => elf::ELFOSABI_SOLARIS, - _ => elf::ELFOSABI_NONE, - }; - let abi_version = 0; - add_gnu_property_note(&mut file, architecture, binary_format, endianness); - file.flags = FileFlags::Elf { os_abi, abi_version, e_flags }; - Some(file) + } } /// Mach-O files contain information about: @@ -422,13 +404,13 @@ pub(crate) fn create_object_file(sess: &Session) -> Option object::write::MachOBuildVersion { /// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz" /// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200 - fn pack_version((major, minor, patch): (u16, u8, u8)) -> u32 { + fn pack_version(apple::OSVersion { major, minor, patch }: apple::OSVersion) -> u32 { let (major, minor, patch) = (major as u32, minor as u32, patch as u32); (major << 16) | (minor << 8) | patch } let platform = apple::macho_platform(&sess.target); - let min_os = apple::deployment_target(sess); + let min_os = sess.apple_deployment_target(); let mut build_version = object::write::MachOBuildVersion::default(); build_version.platform = platform; @@ -574,8 +556,8 @@ pub fn create_compressed_metadata_file( symbol_name: &str, ) -> Vec { let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec(); - packed_metadata.write_all(&(metadata.raw_data().len() as u64).to_le_bytes()).unwrap(); - packed_metadata.extend(metadata.raw_data()); + packed_metadata.write_all(&(metadata.stub_or_full().len() as u64).to_le_bytes()).unwrap(); + packed_metadata.extend(metadata.stub_or_full()); let Some(mut file) = create_object_file(sess) else { if sess.target.is_like_wasm { diff --git a/compiler/rustc_codegen_ssa/src/back/mod.rs b/compiler/rustc_codegen_ssa/src/back/mod.rs index 64b5d4569ecce..8d1adb9993038 100644 --- a/compiler/rustc_codegen_ssa/src/back/mod.rs +++ b/compiler/rustc_codegen_ssa/src/back/mod.rs @@ -19,8 +19,8 @@ pub mod write; /// /// Certain optimizations also depend on the deployment target. pub fn versioned_llvm_target(sess: &Session) -> Cow<'_, str> { - if sess.target.is_like_osx { - apple::add_version_to_llvm_target(&sess.target.llvm_target, apple::deployment_target(sess)) + if sess.target.is_like_darwin { + apple::add_version_to_llvm_target(&sess.target.llvm_target, sess.apple_deployment_target()) .into() } else { // FIXME(madsmtm): Certain other targets also include a version, diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index d633cc98ac87e..7bb8979e8820f 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -9,7 +9,7 @@ use tracing::debug; pub(super) struct RPathConfig<'a> { pub libs: &'a [&'a Path], pub out_filename: PathBuf, - pub is_like_osx: bool, + pub is_like_darwin: bool, pub linker_is_gnu: bool, } @@ -63,7 +63,7 @@ fn get_rpaths_relative_to_output(config: &RPathConfig<'_>) -> Vec { fn get_rpath_relative_to_output(config: &RPathConfig<'_>, lib: &Path) -> OsString { // Mac doesn't appear to support $ORIGIN - let prefix = if config.is_like_osx { "@loader_path" } else { "$ORIGIN" }; + let prefix = if config.is_like_darwin { "@loader_path" } else { "$ORIGIN" }; // Strip filenames let lib = lib.parent().unwrap(); diff --git a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs index f1a30105c5908..ab8fbedb81297 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs @@ -28,7 +28,7 @@ fn test_rpath_relative() { if cfg!(target_os = "macos") { let config = &mut RPathConfig { libs: &[], - is_like_osx: true, + is_like_darwin: true, linker_is_gnu: false, out_filename: PathBuf::from("bin/rustc"), }; @@ -38,7 +38,7 @@ fn test_rpath_relative() { let config = &mut RPathConfig { libs: &[], out_filename: PathBuf::from("bin/rustc"), - is_like_osx: false, + is_like_darwin: false, linker_is_gnu: true, }; let res = get_rpath_relative_to_output(config, Path::new("lib/libstd.so")); @@ -51,7 +51,7 @@ fn test_rpath_relative_issue_119571() { let config = &mut RPathConfig { libs: &[], out_filename: PathBuf::from("rustc"), - is_like_osx: false, + is_like_darwin: false, linker_is_gnu: true, }; // Should not panic when out_filename only contains filename. diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 459f4329d6e92..96aec9769d2ca 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -1,6 +1,6 @@ use std::collections::hash_map::Entry::*; -use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE}; +use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name}; use rustc_data_structures::unord::UnordMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId}; @@ -13,6 +13,7 @@ use rustc_middle::query::LocalCrate; use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, Instance, SymbolName, Ty, TyCtxt}; use rustc_middle::util::Providers; use rustc_session::config::{CrateType, OomStrategy}; +use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::callconv::Conv; use rustc_target::spec::{SanitizerSet, TlsModel}; use tracing::debug; @@ -28,7 +29,7 @@ fn crate_export_threshold(crate_type: CrateType) -> SymbolExportLevel { CrateType::Executable | CrateType::Staticlib | CrateType::ProcMacro | CrateType::Cdylib => { SymbolExportLevel::C } - CrateType::Rlib | CrateType::Dylib => SymbolExportLevel::Rust, + CrateType::Rlib | CrateType::Dylib | CrateType::Sdylib => SymbolExportLevel::Rust, } } @@ -44,7 +45,7 @@ pub fn crates_export_threshold(crate_types: &[CrateType]) -> SymbolExportLevel { } fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap { - if !tcx.sess.opts.output_types.should_codegen() { + if !tcx.sess.opts.output_types.should_codegen() && !tcx.is_sdylib_interface_build() { return Default::default(); } @@ -94,16 +95,11 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap, def_id: DefId) -> b tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) } -fn exported_symbols_provider_local( - tcx: TyCtxt<'_>, +fn exported_symbols_provider_local<'tcx>( + tcx: TyCtxt<'tcx>, _: LocalCrate, -) -> &[(ExportedSymbol<'_>, SymbolExportInfo)] { - if !tcx.sess.opts.output_types.should_codegen() { +) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { + if !tcx.sess.opts.output_types.should_codegen() && !tcx.is_sdylib_interface_build() { return &[]; } @@ -219,8 +215,11 @@ fn exported_symbols_provider_local( if allocator_kind_for_codegen(tcx).is_some() { for symbol_name in ALLOCATOR_METHODS .iter() - .map(|method| format!("__rust_{}", method.name)) - .chain(["__rust_alloc_error_handler".to_string(), OomStrategy::SYMBOL.to_string()]) + .map(|method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str())) + .chain([ + mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), + mangle_internal_symbol(tcx, OomStrategy::SYMBOL), + ]) { let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name)); @@ -234,8 +233,10 @@ fn exported_symbols_provider_local( )); } - let exported_symbol = - ExportedSymbol::NoDefId(SymbolName::new(tcx, NO_ALLOC_SHIM_IS_UNSTABLE)); + let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new( + tcx, + &mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), + )); symbols.push(( exported_symbol, SymbolExportInfo { @@ -320,6 +321,38 @@ fn exported_symbols_provider_local( let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; + // Do not export symbols that cannot be instantiated by downstream crates. + let reachable_set = tcx.reachable_set(()); + let is_local_to_current_crate = |ty: Ty<'_>| { + let no_refs = ty.peel_refs(); + let root_def_id = match no_refs.kind() { + ty::Closure(closure, _) => *closure, + ty::FnDef(def_id, _) => *def_id, + ty::Coroutine(def_id, _) => *def_id, + ty::CoroutineClosure(def_id, _) => *def_id, + ty::CoroutineWitness(def_id, _) => *def_id, + _ => return false, + }; + let Some(root_def_id) = root_def_id.as_local() else { + return false; + }; + + let is_local = !reachable_set.contains(&root_def_id); + is_local + }; + + let is_instantiable_downstream = + |did: Option, generic_args: GenericArgsRef<'tcx>| { + generic_args + .types() + .chain(did.into_iter().map(move |did| tcx.type_of(did).skip_binder())) + .all(move |arg| { + arg.walk().all(|ty| { + ty.as_type().map_or(true, |ty| !is_local_to_current_crate(ty)) + }) + }) + }; + // The symbols created in this loop are sorted below it #[allow(rustc::potential_query_instability)] for (mono_item, data) in cgus.iter().flat_map(|cgu| cgu.items().iter()) { @@ -337,7 +370,7 @@ fn exported_symbols_provider_local( if !tcx.sess.opts.share_generics() { if tcx.codegen_fn_attrs(mono_item.def_id()).inline - == rustc_attr_parsing::InlineAttr::Never + == rustc_attr_data_structures::InlineAttr::Never { // this is OK, we explicitly allow sharing inline(never) across crates even // without share-generics. @@ -348,7 +381,12 @@ fn exported_symbols_provider_local( match *mono_item { MonoItem::Fn(Instance { def: InstanceKind::Item(def), args }) => { - if args.non_erasable_generics().next().is_some() { + let has_generics = args.non_erasable_generics().next().is_some(); + + let should_export = + has_generics && is_instantiable_downstream(Some(def), &args); + + if should_export { let symbol = ExportedSymbol::Generic(def, args); symbols.push(( symbol, @@ -363,8 +401,33 @@ fn exported_symbols_provider_local( MonoItem::Fn(Instance { def: InstanceKind::DropGlue(_, Some(ty)), args }) => { // A little sanity-check assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty))); + + // Drop glue did is always going to be non-local outside of libcore, thus we don't need to check it's locality (which includes invoking `type_of` query). + let should_export = match ty.kind() { + ty::Adt(_, args) => is_instantiable_downstream(None, args), + ty::Closure(_, args) => is_instantiable_downstream(None, args), + _ => true, + }; + + if should_export { + symbols.push(( + ExportedSymbol::DropGlue(ty), + SymbolExportInfo { + level: SymbolExportLevel::Rust, + kind: SymbolExportKind::Text, + used: false, + }, + )); + } + } + MonoItem::Fn(Instance { + def: InstanceKind::AsyncDropGlueCtorShim(_, ty), + args, + }) => { + // A little sanity-check + assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty))); symbols.push(( - ExportedSymbol::DropGlue(ty), + ExportedSymbol::AsyncDropGlueCtorShim(ty), SymbolExportInfo { level: SymbolExportLevel::Rust, kind: SymbolExportKind::Text, @@ -372,14 +435,9 @@ fn exported_symbols_provider_local( }, )); } - MonoItem::Fn(Instance { - def: InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)), - args, - }) => { - // A little sanity-check - assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty))); + MonoItem::Fn(Instance { def: InstanceKind::AsyncDropGlue(def, ty), args: _ }) => { symbols.push(( - ExportedSymbol::AsyncDropGlueCtorShim(ty), + ExportedSymbol::AsyncDropGlue(def, ty), SymbolExportInfo { level: SymbolExportLevel::Rust, kind: SymbolExportKind::Text, @@ -428,11 +486,10 @@ fn upstream_monomorphizations_provider( if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id { (async_drop_in_place_fn_def_id, tcx.mk_args(&[ty.into()])) } else { - // `drop_in_place` in place does not exist, don't try - // to use it. continue; } } + ExportedSymbol::AsyncDropGlue(def_id, ty) => (def_id, tcx.mk_args(&[ty.into()])), ExportedSymbol::NonGeneric(..) | ExportedSymbol::ThreadLocalShim(..) | ExportedSymbol::NoDefId(..) => { @@ -555,7 +612,7 @@ pub(crate) fn symbol_name_for_instance_in_crate<'tcx>( ExportedSymbol::Generic(def_id, args) => { rustc_symbol_mangling::symbol_name_for_instance_in_crate( tcx, - Instance::new(def_id, args), + Instance::new_raw(def_id, args), instantiating_crate, ) } @@ -581,6 +638,13 @@ pub(crate) fn symbol_name_for_instance_in_crate<'tcx>( instantiating_crate, ) } + ExportedSymbol::AsyncDropGlue(def_id, ty) => { + rustc_symbol_mangling::symbol_name_for_instance_in_crate( + tcx, + Instance::resolve_async_drop_in_place_poll(tcx, def_id, ty), + instantiating_crate, + ) + } ExportedSymbol::NoDefId(symbol_name) => symbol_name.to_string(), } } @@ -596,13 +660,14 @@ fn calling_convention_for_symbol<'tcx>( None } ExportedSymbol::NonGeneric(def_id) => Some(Instance::mono(tcx, def_id)), - ExportedSymbol::Generic(def_id, args) => Some(Instance::new(def_id, args)), + ExportedSymbol::Generic(def_id, args) => Some(Instance::new_raw(def_id, args)), // DropGlue always use the Rust calling convention and thus follow the target's default // symbol decoration scheme. ExportedSymbol::DropGlue(..) => None, // AsyncDropGlueCtorShim always use the Rust calling convention and thus follow the // target's default symbol decoration scheme. ExportedSymbol::AsyncDropGlueCtorShim(..) => None, + ExportedSymbol::AsyncDropGlue(..) => None, // NoDefId always follow the target's default symbol decoration scheme. ExportedSymbol::NoDefId(..) => None, // ThreadLocalShim always follow the target's default symbol decoration scheme. diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c8bb229998ea3..0fd4ed8475b49 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -6,6 +6,7 @@ use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender, channel}; use std::{fs, io, mem, str, thread}; +use rustc_abi::Size; use rustc_ast::attr; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; @@ -305,14 +306,18 @@ impl TargetMachineFactoryConfig { cgcx.output_filenames.split_dwarf_path( cgcx.split_debuginfo, cgcx.split_dwarf_kind, - Some(module_name), + module_name, + cgcx.invocation_temp.as_deref(), ) } else { None }; - let output_obj_file = - Some(cgcx.output_filenames.temp_path(OutputType::Object, Some(module_name))); + let output_obj_file = Some(cgcx.output_filenames.temp_path_for_cgu( + OutputType::Object, + module_name, + cgcx.invocation_temp.as_deref(), + )); TargetMachineFactoryConfig { split_dwarf_file, output_obj_file } } } @@ -343,6 +348,7 @@ pub struct CodegenContext { pub crate_types: Vec, pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, pub output_filenames: Arc, + pub invocation_temp: Option, pub regular_module_config: Arc, pub metadata_module_config: Arc, pub allocator_module_config: Arc, @@ -351,10 +357,11 @@ pub struct CodegenContext { pub is_pe_coff: bool, pub target_can_use_split_dwarf: bool, pub target_arch: String, - pub target_is_like_osx: bool, + pub target_is_like_darwin: bool, pub target_is_like_aix: bool, pub split_debuginfo: rustc_target::spec::SplitDebuginfo, pub split_dwarf_kind: rustc_session::config::SplitDwarfKind, + pub pointer_size: Size, /// All commandline args used to invoke the compiler, with @file args fully expanded. /// This will only be used within debug info, e.g. in the pdb file on windows @@ -473,7 +480,7 @@ pub(crate) fn start_async_codegen( ) -> OngoingCodegen { let (coordinator_send, coordinator_receive) = channel(); - let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); + let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID); let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins); let crate_info = CrateInfo::new(tcx, target_cpu); @@ -544,9 +551,12 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( if let Some(path) = &module.bytecode { files.push((OutputType::Bitcode.extension(), path.as_path())); } - if let Some((id, product)) = - copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, files.as_slice()) - { + if let Some((id, product)) = copy_cgu_workproduct_to_incr_comp_cache_dir( + sess, + &module.name, + files.as_slice(), + &module.links_from_incr_cache, + ) { work_products.insert(id, product); } } @@ -564,24 +574,24 @@ fn produce_final_output_artifacts( // Produce final compile outputs. let copy_gracefully = |from: &Path, to: &OutFileName| match to { - OutFileName::Stdout => { - if let Err(e) = copy_to_stdout(from) { - sess.dcx().emit_err(errors::CopyPath::new(from, to.as_path(), e)); - } + OutFileName::Stdout if let Err(e) = copy_to_stdout(from) => { + sess.dcx().emit_err(errors::CopyPath::new(from, to.as_path(), e)); } - OutFileName::Real(path) => { - if let Err(e) = fs::copy(from, path) { - sess.dcx().emit_err(errors::CopyPath::new(from, path, e)); - } + OutFileName::Real(path) if let Err(e) = fs::copy(from, path) => { + sess.dcx().emit_err(errors::CopyPath::new(from, path, e)); } + _ => {} }; let copy_if_one_unit = |output_type: OutputType, keep_numbered: bool| { if let [module] = &compiled_modules.modules[..] { // 1) Only one codegen unit. In this case it's no difficulty // to copy `foo.0.x` to `foo.x`. - let module_name = Some(&module.name[..]); - let path = crate_output.temp_path(output_type, module_name); + let path = crate_output.temp_path_for_cgu( + output_type, + &module.name, + sess.invocation_temp.as_deref(), + ); let output = crate_output.path(output_type); if !output_type.is_text_output() && output.is_tty() { sess.dcx() @@ -594,22 +604,15 @@ fn produce_final_output_artifacts( ensure_removed(sess.dcx(), &path); } } else { - let extension = crate_output - .temp_path(output_type, None) - .extension() - .unwrap() - .to_str() - .unwrap() - .to_owned(); - if crate_output.outputs.contains_explicit_name(&output_type) { // 2) Multiple codegen units, with `--emit foo=some_name`. We have // no good solution for this case, so warn the user. - sess.dcx().emit_warn(errors::IgnoringEmitPath { extension }); + sess.dcx() + .emit_warn(errors::IgnoringEmitPath { extension: output_type.extension() }); } 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.dcx().emit_warn(errors::IgnoringOutput { extension }); + sess.dcx().emit_warn(errors::IgnoringOutput { extension: output_type.extension() }); } else { // 4) Multiple codegen units, but no explicit name. We // just leave the `foo.0.x` files in place. @@ -683,14 +686,12 @@ fn produce_final_output_artifacts( needs_crate_object || (user_wants_objects && sess.codegen_units().as_usize() > 1); for module in compiled_modules.modules.iter() { - if let Some(ref path) = module.object { - if !keep_numbered_objects { + if !keep_numbered_objects { + if let Some(ref path) = module.object { ensure_removed(sess.dcx(), path); } - } - if let Some(ref path) = module.dwarf_object { - if !keep_numbered_objects { + if let Some(ref path) = module.dwarf_object { ensure_removed(sess.dcx(), path); } } @@ -702,12 +703,11 @@ fn produce_final_output_artifacts( } } - if !user_wants_bitcode { - if let Some(ref allocator_module) = compiled_modules.allocator_module { - if let Some(ref path) = allocator_module.bytecode { - ensure_removed(sess.dcx(), path); - } - } + if !user_wants_bitcode + && let Some(ref allocator_module) = compiled_modules.allocator_module + && let Some(ref path) = allocator_module.bytecode + { + ensure_removed(sess.dcx(), path); } } @@ -938,7 +938,9 @@ fn execute_copy_from_cache_work_item( ) -> WorkItemResult { let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap(); - let load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { + let mut links_from_incr_cache = Vec::new(); + + let mut load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { let source_file = in_incr_comp_dir(incr_comp_session_dir, saved_path); debug!( "copying preexisting module `{}` from {:?} to {}", @@ -947,7 +949,10 @@ fn execute_copy_from_cache_work_item( output_path.display() ); match link_or_copy(&source_file, &output_path) { - Ok(_) => Some(output_path), + Ok(_) => { + links_from_incr_cache.push(source_file); + Some(output_path) + } Err(error) => { cgcx.create_dcx().handle().emit_err(errors::CopyPathBuf { source_file, @@ -963,17 +968,26 @@ fn execute_copy_from_cache_work_item( module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| { let dwarf_obj_out = cgcx .output_filenames - .split_dwarf_path(cgcx.split_debuginfo, cgcx.split_dwarf_kind, Some(&module.name)) + .split_dwarf_path( + cgcx.split_debuginfo, + cgcx.split_dwarf_kind, + &module.name, + cgcx.invocation_temp.as_deref(), + ) .expect( "saved dwarf object in work product but `split_dwarf_path` returned `None`", ); load_from_incr_comp_dir(dwarf_obj_out, saved_dwarf_object_file) }); - let load_from_incr_cache = |perform, output_type: OutputType| { + let mut load_from_incr_cache = |perform, output_type: OutputType| { if perform { let saved_file = module.source.saved_files.get(output_type.extension())?; - let output_path = cgcx.output_filenames.temp_path(output_type, Some(&module.name)); + let output_path = cgcx.output_filenames.temp_path_for_cgu( + output_type, + &module.name, + cgcx.invocation_temp.as_deref(), + ); load_from_incr_comp_dir(output_path, &saved_file) } else { None @@ -990,6 +1004,7 @@ fn execute_copy_from_cache_work_item( } WorkItemResult::Finished(CompiledModule { + links_from_incr_cache, name: module.name, kind: ModuleKind::Regular, object, @@ -1211,11 +1226,13 @@ fn start_executing_work( is_pe_coff: tcx.sess.target.is_like_windows, target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(), target_arch: tcx.sess.target.arch.to_string(), - target_is_like_osx: tcx.sess.target.is_like_osx, + target_is_like_darwin: tcx.sess.target.is_like_darwin, target_is_like_aix: tcx.sess.target.is_like_aix, split_debuginfo: tcx.sess.split_debuginfo(), split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind, parallel: backend.supports_parallel() && !sess.opts.unstable_opts.no_parallel_backend, + pointer_size: tcx.data_layout.pointer_size, + invocation_temp: sess.invocation_temp.clone(), }; // This is the "main loop" of parallel work happening for parallel codegen. @@ -2180,7 +2197,7 @@ fn msvc_imps_needed(tcx: TyCtxt<'_>) -> bool { // indirectly from ThinLTO. In theory these are not needed as ThinLTO could resolve // these, but it currently does not do so. let can_have_static_objects = - tcx.sess.lto() == Lto::Thin || tcx.crate_types().iter().any(|ct| *ct == CrateType::Rlib); + tcx.sess.lto() == Lto::Thin || tcx.crate_types().contains(&CrateType::Rlib); tcx.sess.target.is_like_windows && can_have_static_objects && diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 63f2f8fa3d177..1890119dca770 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -7,27 +7,30 @@ use itertools::Itertools; use rustc_abi::FIRST_VARIANT; use rustc_ast as ast; use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name}; -use rustc_attr_parsing::OptimizeAttr; +use rustc_attr_data_structures::OptimizeAttr; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; -use rustc_data_structures::sync::par_map; +use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_data_structures::unord::UnordMap; +use rustc_hir::ItemId; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_metadata::EncodedMetadata; -use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_middle::middle::{exported_symbols, lang_items}; use rustc_middle::mir::BinOp; +use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem, MonoItemPartitions}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_session::Session; use rustc_session::config::{self, CrateType, EntryFnType, OutputType}; use rustc_span::{DUMMY_SP, Symbol, sym}; +use rustc_symbol_mangling::mangle_internal_symbol; use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::{debug, info}; @@ -416,6 +419,75 @@ pub(crate) fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( mir::codegen_mir::(cx, instance); } +pub fn codegen_global_asm<'tcx, Cx>(cx: &mut Cx, item_id: ItemId) +where + Cx: LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>> + AsmCodegenMethods<'tcx>, +{ + let item = cx.tcx().hir_item(item_id); + if let rustc_hir::ItemKind::GlobalAsm { asm, .. } = item.kind { + let operands: Vec<_> = asm + .operands + .iter() + .map(|(op, op_sp)| match *op { + rustc_hir::InlineAsmOperand::Const { ref anon_const } => { + match cx.tcx().const_eval_poly(anon_const.def_id.to_def_id()) { + Ok(const_value) => { + let ty = + cx.tcx().typeck_body(anon_const.body).node_type(anon_const.hir_id); + let string = common::asm_const_to_str( + cx.tcx(), + *op_sp, + const_value, + cx.layout_of(ty), + ); + GlobalAsmOperandRef::Const { string } + } + Err(ErrorHandled::Reported { .. }) => { + // An error has already been reported and + // compilation is guaranteed to fail if execution + // hits this path. So an empty string instead of + // a stringified constant value will suffice. + GlobalAsmOperandRef::Const { string: String::new() } + } + Err(ErrorHandled::TooGeneric(_)) => { + span_bug!(*op_sp, "asm const cannot be resolved; too generic") + } + } + } + rustc_hir::InlineAsmOperand::SymFn { expr } => { + let ty = cx.tcx().typeck(item_id.owner_id).expr_ty(expr); + let instance = match ty.kind() { + &ty::FnDef(def_id, args) => Instance::expect_resolve( + cx.tcx(), + ty::TypingEnv::fully_monomorphized(), + def_id, + args, + expr.span, + ), + _ => span_bug!(*op_sp, "asm sym is not a function"), + }; + + GlobalAsmOperandRef::SymFn { instance } + } + rustc_hir::InlineAsmOperand::SymStatic { path: _, def_id } => { + GlobalAsmOperandRef::SymStatic { def_id } + } + rustc_hir::InlineAsmOperand::In { .. } + | rustc_hir::InlineAsmOperand::Out { .. } + | rustc_hir::InlineAsmOperand::InOut { .. } + | rustc_hir::InlineAsmOperand::SplitInOut { .. } + | rustc_hir::InlineAsmOperand::Label { .. } => { + span_bug!(*op_sp, "invalid operand type for global_asm!") + } + }) + .collect(); + + cx.codegen_global_asm(asm.template, &operands, asm.options, asm.line_spans); + } else { + span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") + } +} + /// 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>>( @@ -639,8 +711,11 @@ pub fn codegen_crate( let metadata_cgu_name = cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata")).to_string(); tcx.sess.time("write_compressed_metadata", || { - let file_name = - tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name)); + let file_name = tcx.output_filenames(()).temp_path_for_cgu( + OutputType::Metadata, + &metadata_cgu_name, + tcx.sess.invocation_temp.as_deref(), + ); let data = create_compressed_metadata_file( tcx.sess, &metadata, @@ -657,6 +732,7 @@ pub fn codegen_crate( bytecode: None, assembly: None, llvm_ir: None, + links_from_incr_cache: Vec::new(), } }) }); @@ -755,7 +831,7 @@ pub fn codegen_crate( let pre_compiled_cgus = par_map(cgus, |(i, _)| { let module = backend.compile_codegen_unit(tcx, codegen_units[i].name()); - (i, module) + (i, IntoDynSyncSend(module)) }); total_codegen_time += start_time.elapsed(); @@ -775,7 +851,7 @@ pub fn codegen_crate( match cgu_reuse { CguReuse::No => { let (module, cost) = if let Some(cgu) = pre_compiled_cgus.remove(&i) { - cgu + cgu.0 } else { let start_time = Instant::now(); let module = backend.compile_codegen_unit(tcx, cgu.name()); @@ -876,7 +952,7 @@ impl CrateInfo { let linked_symbols = crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect(); let local_crate_name = tcx.crate_name(LOCAL_CRATE); - let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); + let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID); let subsystem = ast::attr::first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem); let windows_subsystem = subsystem.map(|subsystem| { @@ -989,7 +1065,12 @@ impl CrateInfo { .for_each(|(_, linked_symbols)| { let mut symbols = missing_weak_lang_items .iter() - .map(|item| (format!("{prefix}{item}"), SymbolExportKind::Text)) + .map(|item| { + ( + format!("{prefix}{}", mangle_internal_symbol(tcx, item.as_str())), + SymbolExportKind::Text, + ) + }) .collect::>(); symbols.sort_unstable_by(|a, b| a.0.cmp(&b.0)); linked_symbols.extend(symbols); @@ -1002,7 +1083,13 @@ impl CrateInfo { // errors. linked_symbols.extend(ALLOCATOR_METHODS.iter().map(|method| { ( - format!("{prefix}{}", global_fn_name(method.name).as_str()), + format!( + "{prefix}{}", + mangle_internal_symbol( + tcx, + global_fn_name(method.name).as_str() + ) + ), SymbolExportKind::Text, ) })); @@ -1011,7 +1098,7 @@ impl CrateInfo { } let embed_visualizers = tcx.crate_types().iter().any(|&crate_type| match crate_type { - CrateType::Executable | CrateType::Dylib | CrateType::Cdylib => { + CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Sdylib => { // These are crate types for which we invoke the linker and can embed // NatVis visualizers. true @@ -1092,11 +1179,12 @@ pub fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> // 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() - ); + tcx.dep_graph.assert_dep_node_not_yet_allocated_in_current_session(&dep_node, || { + format!( + "CompileCodegenUnit dep-node for CGU `{}` already exists before marking.", + cgu.name() + ) + }); if tcx.try_mark_green(&dep_node) { // We can re-use either the pre- or the post-thinlto state. If no LTO is diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 673740b4aab9f..aa55a0e0f1488 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,15 +1,11 @@ use std::str::FromStr; use rustc_abi::ExternAbi; -use rustc_ast::expand::autodiff_attrs::{ - AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity, -}; -use rustc_ast::{MetaItem, MetaItemInner, attr}; -use rustc_attr_parsing::ReprAttr::ReprAlign; -use rustc_attr_parsing::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; +use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; +use rustc_attr_data_structures::ReprAttr::ReprAlign; +use rustc_attr_data_structures::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::codes::*; -use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; @@ -64,7 +60,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { ); } - let attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)); + let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did)); let mut codegen_fn_attrs = CodegenFnAttrs::new(); if tcx.should_inherit_track_caller(did) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; @@ -79,7 +75,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // When `no_builtins` is applied at the crate level, we should add the // `no-builtins` attribute to each function to ensure it takes effect in LTO. - let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); + let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID); let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins); if no_builtins { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS; @@ -91,6 +87,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let mut link_ordinal_span = None; let mut no_sanitize_span = None; let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default(); + let mut no_mangle_span = None; for attr in attrs.iter() { // In some cases, attribute are only valid on functions, but it's the `check_attr` @@ -118,7 +115,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { AttributeKind::Repr(reprs) => { codegen_fn_attrs.alignment = reprs .iter() - .find_map(|(r, _)| if let ReprAlign(x) = r { Some(*x) } else { None }); + .filter_map(|(r, _)| if let ReprAlign(x) = r { Some(*x) } else { None }) + .max(); } _ => {} @@ -142,6 +140,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, sym::no_mangle => { + no_mangle_span = Some(attr.span()); if tcx.opt_item_name(did.to_def_id()).is_some() { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; mixed_export_name_no_mangle_lint_state.track_no_mangle( @@ -217,7 +216,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // somewhat, and is subject to change in the future (which // is a good thing, because this would ideally be a bit // more firmed up). - let is_like_elf = !(tcx.sess.target.is_like_osx + let is_like_elf = !(tcx.sess.target.is_like_darwin || tcx.sess.target.is_like_windows || tcx.sess.target.is_like_wasm); codegen_fn_attrs.flags |= if is_like_elf { @@ -236,13 +235,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { && let Some(fn_sig) = fn_sig() && fn_sig.skip_binder().abi() != ExternAbi::Rust { - struct_span_code_err!( - tcx.dcx(), - attr.span(), - E0737, - "`#[track_caller]` requires Rust ABI" - ) - .emit(); + tcx.dcx().emit_err(errors::RequiresRustAbi { span: attr.span() }); } if is_closure && !tcx.features().closure_track_caller() @@ -263,13 +256,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { 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_code_err!( - tcx.dcx(), - attr.span(), - E0648, - "`export_name` may not contain null characters" - ) - .emit(); + tcx.dcx().emit_err(errors::NullOnExport { span: attr.span() }); } codegen_fn_attrs.export_name = Some(s); mixed_export_name_no_mangle_lint_state.track_export_name(attr.span()); @@ -361,20 +348,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { no_sanitize_span = Some(attr.span()); if let Some(list) = attr.meta_item_list() { for item in list.iter() { - match item.name_or_empty() { - sym::address => { + match item.name() { + Some(sym::address) => { codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS } - sym::cfi => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI, - sym::kcfi => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI, - sym::memory => codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY, - sym::memtag => codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG, - sym::shadow_call_stack => { + Some(sym::cfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI, + Some(sym::kcfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI, + Some(sym::memory) => { + codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY + } + Some(sym::memtag) => { + codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG + } + Some(sym::shadow_call_stack) => { codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK } - sym::thread => codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD, - sym::hwaddress => { + Some(sym::thread) => { + codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD + } + Some(sym::hwaddress) => { codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS } _ => { @@ -394,47 +387,28 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { [sym::arm, sym::a32 | sym::t32] if !tcx.sess.target.has_thumb_interworking => { - struct_span_code_err!( - tcx.dcx(), - attr.span(), - E0779, - "target does not support `#[instruction_set]`" - ) - .emit(); + tcx.dcx().emit_err(errors::UnsupportedInstructionSet { + span: attr.span(), + }); None } [sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32), [sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32), _ => { - struct_span_code_err!( - tcx.dcx(), - attr.span(), - E0779, - "invalid instruction set specified", - ) - .emit(); + tcx.dcx().emit_err(errors::InvalidInstructionSet { + span: attr.span(), + }); None } } } [] => { - struct_span_code_err!( - tcx.dcx(), - attr.span(), - E0778, - "`#[instruction_set]` requires an argument" - ) - .emit(); + tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() }); None } _ => { - struct_span_code_err!( - tcx.dcx(), - attr.span(), - E0779, - "cannot specify more than one instruction set" - ) - .emit(); + tcx.dcx() + .emit_err(errors::MultipleInstructionSet { span: attr.span() }); None } }) @@ -445,58 +419,38 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let mut entry = None; for item in l { let Some(meta_item) = item.meta_item() else { - tcx.dcx().span_err(item.span(), "expected name value pair"); + tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); continue; }; let Some(name_value_lit) = meta_item.name_value_literal() else { - tcx.dcx().span_err(item.span(), "expected name value pair"); + tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); continue; }; - fn emit_error_with_label( - tcx: TyCtxt<'_>, - span: Span, - error: impl Into, - label: impl Into, - ) { - let mut err: rustc_errors::Diag<'_, _> = - tcx.dcx().struct_span_err(span, error); - err.span_label(span, label); - err.emit(); - } - - let attrib_to_write = match meta_item.name_or_empty() { - sym::prefix_nops => &mut prefix, - sym::entry_nops => &mut entry, + let attrib_to_write = match meta_item.name() { + Some(sym::prefix_nops) => &mut prefix, + Some(sym::entry_nops) => &mut entry, _ => { - emit_error_with_label( - tcx, - item.span(), - "unexpected parameter name", - format!("expected {} or {}", sym::prefix_nops, sym::entry_nops), - ); + tcx.dcx().emit_err(errors::UnexpectedParameterName { + span: item.span(), + prefix_nops: sym::prefix_nops, + entry_nops: sym::entry_nops, + }); continue; } }; let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else { - emit_error_with_label( - tcx, - name_value_lit.span, - "invalid literal value", - "value must be an integer between `0` and `255`", - ); + tcx.dcx().emit_err(errors::InvalidLiteralValue { + span: name_value_lit.span, + }); continue; }; let Ok(val) = val.get().try_into() else { - emit_error_with_label( - tcx, - name_value_lit.span, - "integer value out of range", - "value must be between `0` and `255`", - ); + tcx.dcx() + .emit_err(errors::OutOfRangeInteger { span: name_value_lit.span }); continue; }; @@ -533,7 +487,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { inline_span = Some(attr.span()); let [item] = &items[..] else { - struct_span_code_err!(tcx.dcx(), attr.span(), E0534, "expected one argument").emit(); + tcx.dcx().emit_err(errors::ExpectedOneArgument { span: attr.span() }); return InlineAttr::None; }; @@ -542,9 +496,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } else if item.has_name(sym::never) { InlineAttr::Never } else { - struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument") - .with_help("valid inline arguments are `always` and `never`") - .emit(); + tcx.dcx().emit_err(errors::InvalidArgument { span: items[0].span() }); InlineAttr::None } @@ -575,9 +527,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if !attr.has_name(sym::optimize) { return ia; } - let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit(); if attr.is_word() { - err(attr.span(), "expected one argument"); + tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() }); return ia; } let Some(ref items) = attr.meta_item_list() else { @@ -586,7 +537,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { inline_span = Some(attr.span()); let [item] = &items[..] else { - err(attr.span(), "expected one argument"); + tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() }); return OptimizeAttr::Default; }; if item.has_name(sym::size) { @@ -596,7 +547,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } else if item.has_name(sym::none) { OptimizeAttr::DoNotOptimize } else { - err(item.span(), "invalid argument"); + tcx.dcx().emit_err(errors::InvalidArgumentOptimize { span: item.span() }); OptimizeAttr::Default } }); @@ -659,23 +610,45 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // strippable by the linker. // // Additionally weak lang items have predetermined symbol names. - if WEAK_LANG_ITEMS.iter().any(|&l| tcx.lang_items().get(l) == Some(did.to_def_id())) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; - } if let Some((name, _)) = lang_items::extract(attrs) && let Some(lang_item) = LangItem::from_name(name) - && let Some(link_name) = lang_item.link_name() { - codegen_fn_attrs.export_name = Some(link_name); - codegen_fn_attrs.link_name = Some(link_name); + if WEAK_LANG_ITEMS.contains(&lang_item) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; + } + if let Some(link_name) = lang_item.link_name() { + codegen_fn_attrs.export_name = Some(link_name); + codegen_fn_attrs.link_name = Some(link_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; + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) + && codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) + { + let lang_item = + lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name)); + let mut err = tcx + .dcx() + .struct_span_err( + no_mangle_span.unwrap_or_default(), + "`#[no_mangle]` cannot be used on internal language items", + ) + .with_note("Rustc requires this item to have a specific mangled name.") + .with_span_label(tcx.def_span(did), "should be the internal language item"); + if let Some(lang_item) = lang_item { + if let Some(link_name) = lang_item.link_name() { + err = err + .with_note("If you are trying to prevent mangling to ease debugging, many") + .with_note(format!( + "debuggers support a command such as `rbreak {link_name}` to" + )) + .with_note(format!( + "match `.*{link_name}.*` instead of `break {link_name}` on a specific name" + )) + } + } + err.emit(); } // Any linkage to LLVM intrinsics for now forcibly marks them all as never @@ -849,21 +822,14 @@ impl<'a> MixedExportNameAndNoMangleState<'a> { fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { let attrs = tcx.get_attrs(id, sym::rustc_autodiff); - let attrs = - attrs.filter(|attr| attr.name_or_empty() == sym::rustc_autodiff).collect::>(); + let attrs = attrs.filter(|attr| attr.has_name(sym::rustc_autodiff)).collect::>(); // check for exactly one autodiff attribute on placeholder functions. // There should only be one, since we generate a new placeholder per ad macro. - // FIXME(ZuseZ4): re-enable this check. Currently we add multiple, which doesn't cause harm but - // looks strange e.g. under cargo-expand. let attr = match &attrs[..] { [] => return None, [attr] => attr, - // These two attributes are the same and unfortunately duplicated due to a previous bug. - [attr, _attr2] => attr, _ => { - //FIXME(ZuseZ4): Once we fixed our parser, we should also prohibit the two-attribute - //branch above. span_bug!(attrs[1].span(), "cg_ssa: rustc_autodiff should only exist once per source"); } }; @@ -875,8 +841,8 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { return Some(AutoDiffAttrs::source()); } - let [mode, input_activities @ .., ret_activity] = &list[..] else { - span_bug!(attr.span(), "rustc_autodiff attribute must contain mode and activities"); + let [mode, width_meta, input_activities @ .., ret_activity] = &list[..] else { + span_bug!(attr.span(), "rustc_autodiff attribute must contain mode, width and activities"); }; let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode { p1.segments.first().unwrap().ident @@ -893,6 +859,30 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { } }; + let width: u32 = match width_meta { + MetaItemInner::MetaItem(MetaItem { path: p1, .. }) => { + let w = p1.segments.first().unwrap().ident; + match w.as_str().parse() { + Ok(val) => val, + Err(_) => { + span_bug!(w.span, "rustc_autodiff width should fit u32"); + } + } + } + MetaItemInner::Lit(lit) => { + if let LitKind::Int(val, _) = lit.kind { + match val.get().try_into() { + Ok(val) => val, + Err(_) => { + span_bug!(lit.span, "rustc_autodiff width should fit u32"); + } + } + } else { + span_bug!(lit.span, "rustc_autodiff width should be an integer"); + } + } + }; + // First read the ret symbol from the attribute let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity { p1.segments.first().unwrap().ident @@ -930,16 +920,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { } } - for &input in &arg_activities { - if !valid_input_activity(mode, input) { - span_bug!(attr.span(), "Invalid input activity {} for {} mode", input, mode); - } - } - if !valid_ret_activity(mode, ret_activity) { - span_bug!(attr.span(), "Invalid return activity {} for {} mode", ret_activity, mode); - } - - Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities }) + Some(AutoDiffAttrs { mode, width, ret_activity, input_activity: arg_activities }) } pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 965bd34ac147b..6d0c9d8d06645 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -61,7 +61,6 @@ pub enum AtomicRmwBinOp { #[derive(Copy, Clone, Debug)] pub enum AtomicOrdering { - Unordered, Relaxed, Acquire, Release, diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 84703a0a15692..18279a4d05fe0 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -555,11 +555,9 @@ pub fn compute_debuginfo_vtable_name<'tcx>( pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &mut String) { let def_key = tcx.def_key(def_id); - if qualified { - if let Some(parent) = def_key.parent { - push_item_name(tcx, DefId { krate: def_id.krate, index: parent }, true, output); - output.push_str("::"); - } + if qualified && let Some(parent) = def_key.parent { + push_item_name(tcx, DefId { krate: def_id.krate, index: parent }, true, output); + output.push_str("::"); } push_unqualified_item_name(tcx, def_id, def_key.disambiguated_data, output); diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index ccf6d12977f6a..d49aac75d0582 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -3,7 +3,6 @@ use std::borrow::Cow; use std::ffi::OsString; use std::io::Error; -use std::num::ParseIntError; use std::path::{Path, PathBuf}; use std::process::ExitStatus; @@ -12,10 +11,9 @@ use rustc_errors::{ Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; -use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutError; +use rustc_middle::ty::{FloatTy, Ty}; use rustc_span::{Span, Symbol}; -use rustc_type_ir::FloatTy; use crate::assert_module_sources::CguReuse; use crate::back::command::Command; @@ -135,6 +133,110 @@ pub(crate) struct NoSavedObjectFile<'a> { pub cgu_name: &'a str, } +#[derive(Diagnostic)] +#[diag(codegen_ssa_requires_rust_abi, code = E0737)] +pub(crate) struct RequiresRustAbi { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_null_on_export, code = E0648)] +pub(crate) struct NullOnExport { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_unsupported_instruction_set, code = E0779)] +pub(crate) struct UnsupportedInstructionSet { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_invalid_instruction_set, code = E0779)] +pub(crate) struct InvalidInstructionSet { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_bare_instruction_set, code = E0778)] +pub(crate) struct BareInstructionSet { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_multiple_instruction_set, code = E0779)] +pub(crate) struct MultipleInstructionSet { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_expected_name_value_pair)] +pub(crate) struct ExpectedNameValuePair { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_unexpected_parameter_name)] +pub(crate) struct UnexpectedParameterName { + #[primary_span] + #[label] + pub span: Span, + pub prefix_nops: Symbol, + pub entry_nops: Symbol, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_invalid_literal_value)] +pub(crate) struct InvalidLiteralValue { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_out_of_range_integer)] +pub(crate) struct OutOfRangeInteger { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_expected_one_argument, code = E0534)] +pub(crate) struct ExpectedOneArgument { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_expected_one_argument, code = E0722)] +pub(crate) struct ExpectedOneArgumentOptimize { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_invalid_argument, code = E0535)] +#[help] +pub(crate) struct InvalidArgument { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_invalid_argument, code = E0722)] +pub(crate) struct InvalidArgumentOptimize { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(codegen_ssa_copy_path_buf)] pub(crate) struct CopyPathBuf { @@ -175,13 +277,13 @@ pub struct BinaryOutputToTty { #[derive(Diagnostic)] #[diag(codegen_ssa_ignoring_emit_path)] pub struct IgnoringEmitPath { - pub extension: String, + pub extension: &'static str, } #[derive(Diagnostic)] #[diag(codegen_ssa_ignoring_output)] pub struct IgnoringOutput { - pub extension: String, + pub extension: &'static str, } #[derive(Diagnostic)] @@ -635,27 +737,6 @@ pub enum ExtractBundledLibsError<'a> { ExtractSection { rlib: &'a Path, error: Box }, } -#[derive(Diagnostic)] -#[diag(codegen_ssa_unsupported_arch)] -pub(crate) struct UnsupportedArch<'a> { - pub arch: &'a str, - pub os: &'a str, -} - -#[derive(Diagnostic)] -pub(crate) enum AppleDeploymentTarget { - #[diag(codegen_ssa_apple_deployment_target_invalid)] - Invalid { env_var: &'static str, error: ParseIntError }, - #[diag(codegen_ssa_apple_deployment_target_too_low)] - TooLow { env_var: &'static str, version: String, os_min: String }, -} - -#[derive(Diagnostic)] -pub(crate) enum AppleSdkRootError<'a> { - #[diag(codegen_ssa_apple_sdk_error_sdk_path)] - SdkPath { sdk_name: &'a str, error: Error }, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_read_file)] pub(crate) struct ReadFileError { @@ -956,24 +1037,14 @@ pub enum InvalidMonomorphization<'tcx> { v_len: u64, }, - #[diag(codegen_ssa_invalid_monomorphization_mask_type, code = E0511)] - #[note] - MaskType { + #[diag(codegen_ssa_invalid_monomorphization_mask_wrong_element_type, code = E0511)] + MaskWrongElementType { #[primary_span] span: Span, name: Symbol, ty: Ty<'tcx>, }, - #[diag(codegen_ssa_invalid_monomorphization_vector_argument, code = E0511)] - VectorArgument { - #[primary_span] - span: Span, - name: Symbol, - in_ty: Ty<'tcx>, - in_elem: Ty<'tcx>, - }, - #[diag(codegen_ssa_invalid_monomorphization_cannot_return, code = E0511)] CannotReturn { #[primary_span] @@ -996,15 +1067,6 @@ pub enum InvalidMonomorphization<'tcx> { mutability: ExpectedPointerMutability, }, - #[diag(codegen_ssa_invalid_monomorphization_third_arg_element_type, code = E0511)] - ThirdArgElementType { - #[primary_span] - span: Span, - name: Symbol, - expected_element: Ty<'tcx>, - third_arg: Ty<'tcx>, - }, - #[diag(codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size, code = E0511)] UnsupportedSymbolOfSize { #[primary_span] @@ -1231,3 +1293,26 @@ pub(crate) struct MixedExportNameAndNoMangle { #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] pub removal_span: Span, } + +#[derive(Diagnostic, Debug)] +pub(crate) enum XcrunError { + #[diag(codegen_ssa_xcrun_failed_invoking)] + FailedInvoking { sdk_name: &'static str, command_formatted: String, error: std::io::Error }, + + #[diag(codegen_ssa_xcrun_unsuccessful)] + #[note] + Unsuccessful { + sdk_name: &'static str, + command_formatted: String, + stdout: String, + stderr: String, + }, +} + +#[derive(Diagnostic, Debug)] +#[diag(codegen_ssa_xcrun_sdk_path_warning)] +#[note] +pub(crate) struct XcrunSdkPathWarning { + pub sdk_name: &'static str, + pub stderr: String, +} diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 4e758bfdec394..84919645cf071 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -6,15 +6,14 @@ #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] -#![feature(debug_closure_helpers)] #![feature(file_buffered)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(negative_impls)] #![feature(rustdoc_internals)] +#![feature(string_from_utf8_lossy_owned)] #![feature(trait_alias)] #![feature(try_blocks)] -#![warn(unreachable_pub)] +#![recursion_limit = "256"] // tidy-alphabetical-end //! This crate contains codegen code that is used by all codegen backends (LLVM and others). @@ -33,7 +32,7 @@ use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::CrateNum; use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_middle::dep_graph::WorkProduct; -use rustc_middle::lint::LintLevelSource; +use rustc_middle::lint::LevelAndSource; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::middle::exported_symbols::SymbolExportKind; @@ -44,7 +43,6 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_session::Session; use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT}; use rustc_session::cstore::{self, CrateSource}; -use rustc_session::lint::Level; use rustc_session::lint::builtin::LINKER_MESSAGES; use rustc_session::utils::NativeLibKind; use rustc_span::Symbol; @@ -106,13 +104,19 @@ impl ModuleCodegen { emit_asm: bool, emit_ir: bool, outputs: &OutputFilenames, + invocation_temp: Option<&str>, ) -> CompiledModule { - let object = emit_obj.then(|| outputs.temp_path(OutputType::Object, Some(&self.name))); - let dwarf_object = emit_dwarf_obj.then(|| outputs.temp_path_dwo(Some(&self.name))); - let bytecode = emit_bc.then(|| outputs.temp_path(OutputType::Bitcode, Some(&self.name))); - let assembly = emit_asm.then(|| outputs.temp_path(OutputType::Assembly, Some(&self.name))); - let llvm_ir = - emit_ir.then(|| outputs.temp_path(OutputType::LlvmAssembly, Some(&self.name))); + let object = emit_obj + .then(|| outputs.temp_path_for_cgu(OutputType::Object, &self.name, invocation_temp)); + let dwarf_object = + emit_dwarf_obj.then(|| outputs.temp_path_dwo_for_cgu(&self.name, invocation_temp)); + let bytecode = emit_bc + .then(|| outputs.temp_path_for_cgu(OutputType::Bitcode, &self.name, invocation_temp)); + let assembly = emit_asm + .then(|| outputs.temp_path_for_cgu(OutputType::Assembly, &self.name, invocation_temp)); + let llvm_ir = emit_ir.then(|| { + outputs.temp_path_for_cgu(OutputType::LlvmAssembly, &self.name, invocation_temp) + }); CompiledModule { name: self.name.clone(), @@ -122,6 +126,7 @@ impl ModuleCodegen { bytecode, assembly, llvm_ir, + links_from_incr_cache: Vec::new(), } } } @@ -135,6 +140,7 @@ pub struct CompiledModule { pub bytecode: Option, pub assembly: Option, // --emit=asm pub llvm_ir: Option, // --emit=llvm-ir, llvm-bc is in bytecode + pub links_from_incr_cache: Vec, } impl CompiledModule { @@ -229,6 +235,24 @@ pub struct CrateInfo { pub lint_levels: CodegenLintLevels, } +/// Target-specific options that get set in `cfg(...)`. +/// +/// RUSTC_SPECIFIC_FEATURES should be skipped here, those are handled outside codegen. +pub struct TargetConfig { + /// Options to be set in `cfg(target_features)`. + pub target_features: Vec, + /// Options to be set in `cfg(target_features)`, but including unstable features. + pub unstable_target_features: Vec, + /// Option for `cfg(target_has_reliable_f16)`, true if `f16` basic arithmetic works. + pub has_reliable_f16: bool, + /// Option for `cfg(target_has_reliable_f16_math)`, true if `f16` math calls work. + pub has_reliable_f16_math: bool, + /// Option for `cfg(target_has_reliable_f128)`, true if `f128` basic arithmetic works. + pub has_reliable_f128: bool, + /// Option for `cfg(target_has_reliable_f128_math)`, true if `f128` math calls work. + pub has_reliable_f128_math: bool, +} + #[derive(Encodable, Decodable)] pub struct CodegenResults { pub modules: Vec, @@ -338,7 +362,7 @@ impl CodegenResults { /// Instead, encode exactly the information we need. #[derive(Copy, Clone, Debug, Encodable, Decodable)] pub struct CodegenLintLevels { - linker_messages: (Level, LintLevelSource), + linker_messages: LevelAndSource, } impl CodegenLintLevels { diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 23baab3124ec6..99f35b7920864 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -205,7 +205,12 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer | PlaceContext::MutatingUse(MutatingUseContext::Retag) => {} PlaceContext::NonMutatingUse( - NonMutatingUseContext::Copy | NonMutatingUseContext::Move, + NonMutatingUseContext::Copy + | NonMutatingUseContext::Move + // Inspect covers things like `PtrMetadata` and `Discriminant` + // which we can treat similar to `Copy` use for the purpose of + // whether we can use SSA variables for things. + | NonMutatingUseContext::Inspect, ) => match &mut self.locals[local] { LocalKind::ZST => {} LocalKind::Memory => {} @@ -229,8 +234,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer | MutatingUseContext::Projection, ) | PlaceContext::NonMutatingUse( - NonMutatingUseContext::Inspect - | NonMutatingUseContext::SharedBorrow + NonMutatingUseContext::SharedBorrow | NonMutatingUseContext::FakeBorrow | NonMutatingUseContext::RawBorrow | NonMutatingUseContext::Projection, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index e2a9b540d3060..950f19a6f0f4e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -3,6 +3,7 @@ use std::cmp; use rustc_abi::{BackendRepr, ExternAbi, HasDataLayout, Reg, WrappingRange}; use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_data_structures::packed::Pu128; use rustc_hir::lang_items::LangItem; use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; @@ -162,25 +163,25 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { mergeable_succ: bool, ) -> MergingSucc { let tcx = bx.tcx(); - if let Some(instance) = instance { - if is_call_from_compiler_builtins_to_upstream_monomorphization(tcx, instance) { - if destination.is_some() { - let caller_def = fx.instance.def_id(); - let e = CompilerBuiltinsCannotCall { - span: tcx.def_span(caller_def), - caller: with_no_trimmed_paths!(tcx.def_path_str(caller_def)), - callee: with_no_trimmed_paths!(tcx.def_path_str(instance.def_id())), - }; - tcx.dcx().emit_err(e); - } else { - info!( - "compiler_builtins call to diverging function {:?} replaced with abort", - instance.def_id() - ); - bx.abort(); - bx.unreachable(); - return MergingSucc::False; - } + if let Some(instance) = instance + && is_call_from_compiler_builtins_to_upstream_monomorphization(tcx, instance) + { + if destination.is_some() { + let caller_def = fx.instance.def_id(); + let e = CompilerBuiltinsCannotCall { + span: tcx.def_span(caller_def), + caller: with_no_trimmed_paths!(tcx.def_path_str(caller_def)), + callee: with_no_trimmed_paths!(tcx.def_path_str(instance.def_id())), + }; + tcx.dcx().emit_err(e); + } else { + info!( + "compiler_builtins call to diverging function {:?} replaced with abort", + instance.def_id() + ); + bx.abort(); + bx.unreachable(); + return MergingSucc::False; } } @@ -406,6 +407,39 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let cmp = bx.icmp(IntPredicate::IntEQ, discr_value, llval); bx.cond_br_with_expect(cmp, lltarget, llotherwise, expect); } + } else if target_iter.len() == 2 + && self.mir[targets.otherwise()].is_empty_unreachable() + && targets.all_values().contains(&Pu128(0)) + && targets.all_values().contains(&Pu128(1)) + { + // This is the really common case for `bool`, `Option`, etc. + // By using `trunc nuw` we communicate that other values are + // impossible without needing `switch` or `assume`s. + let true_bb = targets.target_for_value(1); + let false_bb = targets.target_for_value(0); + let true_ll = helper.llbb_with_cleanup(self, true_bb); + let false_ll = helper.llbb_with_cleanup(self, false_bb); + + let expected_cond_value = if self.cx.sess().opts.optimize == OptLevel::No { + None + } else { + match (self.cold_blocks[true_bb], self.cold_blocks[false_bb]) { + // Same coldness, no expectation + (true, true) | (false, false) => None, + // Different coldness, expect the non-cold one + (true, false) => Some(false), + (false, true) => Some(true), + } + }; + + let bool_ty = bx.tcx().types.bool; + let cond = if switch_ty == bool_ty { + discr_value + } else { + let bool_llty = bx.immediate_backend_type(bx.layout_of(bool_ty)); + bx.unchecked_utrunc(discr_value, bool_llty) + }; + bx.cond_br_with_expect(cond, true_ll, false_ll, expected_cond_value); } else if self.cx.sess().opts.optimize == OptLevel::No && target_iter.len() == 2 && self.mir[targets.otherwise()].is_empty_unreachable() @@ -892,10 +926,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let def = instance.map(|i| i.def); - if let Some( - ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None), - ) = def - { + // We don't need AsyncDropGlueCtorShim here because it is not `noop func`, + // it is `func returning noop future` + if let Some(ty::InstanceKind::DropGlue(_, None)) = def { // Empty drop glue; a no-op. let target = target.unwrap(); return helper.funclet_br(self, bx, target, mergeable_succ); @@ -1352,8 +1385,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { MergingSucc::False } - mir::TerminatorKind::Drop { place, target, unwind, replace: _ } => self - .codegen_drop_terminator( + mir::TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut } => { + assert!( + async_fut.is_none() && drop.is_none(), + "Async Drop must be expanded or reset to sync before codegen" + ); + self.codegen_drop_terminator( helper, bx, &terminator.source_info, @@ -1361,7 +1398,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { target, unwind, mergeable_succ(), - ), + ) + } mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, unwind } => self .codegen_assert_terminator( diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index eafc551501c13..68a56044a07c1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -43,7 +43,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Const::Ty(_, c) => match c.kind() { // A constant that came from a const generic but was then used as an argument to // old-style simd_shuffle (passing as argument instead of as a generic param). - rustc_type_ir::ConstKind::Value(cv) => return Ok(Ok(cv.valtree)), + ty::ConstKind::Value(cv) => return Ok(Ok(cv.valtree)), other => span_bug!(constant.span, "{other:#?}"), }, // We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index b34e966ba6ce6..b0fcfee2adf5f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -62,7 +62,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let callee_ty = instance.ty(bx.tcx(), bx.typing_env()); let ty::FnDef(def_id, fn_args) = *callee_ty.kind() else { - bug!("expected fn item type, found {}", callee_ty); + span_bug!(span, "expected fn item type, found {}", callee_ty); }; let sig = callee_ty.fn_sig(bx.tcx()); @@ -325,14 +325,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - sym::discriminant_value => { - if ret_ty.is_integral() { - args[0].deref(bx.cx()).codegen_get_discr(bx, ret_ty) - } else { - span_bug!(span, "Invalid discriminant type for `{:?}`", arg_tys[0]) - } - } - // This requires that atomic intrinsics follow a specific naming pattern: // "atomic_[_]" name if let Some(atomic) = name_str.strip_prefix("atomic_") => { @@ -344,7 +336,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; let parse_ordering = |bx: &Bx, s| match s { - "unordered" => Unordered, "relaxed" => Relaxed, "acquire" => Acquire, "release" => Release, @@ -441,6 +432,40 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } // These are all AtomicRMW ops + "max" | "min" => { + let atom_op = if instruction == "max" { + AtomicRmwBinOp::AtomicMax + } else { + AtomicRmwBinOp::AtomicMin + }; + + let ty = fn_args.type_at(0); + if matches!(ty.kind(), ty::Int(_)) { + let ptr = args[0].immediate(); + let val = args[1].immediate(); + bx.atomic_rmw(atom_op, ptr, val, parse_ordering(bx, ordering)) + } else { + invalid_monomorphization(ty); + return Ok(()); + } + } + "umax" | "umin" => { + let atom_op = if instruction == "umax" { + AtomicRmwBinOp::AtomicUMax + } else { + AtomicRmwBinOp::AtomicUMin + }; + + let ty = fn_args.type_at(0); + if matches!(ty.kind(), ty::Uint(_)) { + let ptr = args[0].immediate(); + let val = args[1].immediate(); + bx.atomic_rmw(atom_op, ptr, val, parse_ordering(bx, ordering)) + } else { + invalid_monomorphization(ty); + return Ok(()); + } + } op => { let atom_op = match op { "xchg" => AtomicRmwBinOp::AtomicXchg, @@ -450,10 +475,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { "nand" => AtomicRmwBinOp::AtomicNand, "or" => AtomicRmwBinOp::AtomicOr, "xor" => AtomicRmwBinOp::AtomicXor, - "max" => AtomicRmwBinOp::AtomicMax, - "min" => AtomicRmwBinOp::AtomicMin, - "umax" => AtomicRmwBinOp::AtomicUMax, - "umin" => AtomicRmwBinOp::AtomicUMin, _ => bx.sess().dcx().emit_fatal(errors::UnknownAtomicOperation), }; diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 0758e5d045673..96a04473aba2e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -3,7 +3,7 @@ use std::iter; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mir::{Local, UnwindTerminateReason, traversal}; +use rustc_middle::mir::{Body, Local, UnwindTerminateReason, traversal}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_middle::{bug, mir, span_bug}; @@ -20,7 +20,7 @@ mod coverageinfo; pub mod debuginfo; mod intrinsic; mod locals; -mod naked_asm; +pub mod naked_asm; pub mod operand; pub mod place; mod rvalue; @@ -170,19 +170,24 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) { assert!(!instance.args.has_infer()); + let tcx = cx.tcx(); let llfn = cx.get_fn(instance); - let mir = cx.tcx().instance_mir(instance.def); + let mut mir = tcx.instance_mir(instance.def); let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); debug!("fn_abi: {:?}", fn_abi); - if cx.tcx().codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) { - crate::mir::naked_asm::codegen_naked_asm::(cx, &mir, instance); - return; + if tcx.features().ergonomic_clones() { + let monomorphized_mir = instance.instantiate_mir_and_normalize_erasing_regions( + tcx, + ty::TypingEnv::fully_monomorphized(), + ty::EarlyBinder::bind(mir.clone()), + ); + mir = tcx.arena.alloc(optimize_use_clone::(cx, monomorphized_mir)); } - let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, mir); + let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, &mir); let start_llbb = Bx::append_block(cx, llfn, "start"); let mut start_bx = Bx::build(cx, start_llbb); @@ -194,7 +199,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } let cleanup_kinds = - base::wants_new_eh_instructions(cx.tcx().sess).then(|| analyze::cleanup_kinds(mir)); + base::wants_new_eh_instructions(tcx.sess).then(|| analyze::cleanup_kinds(&mir)); let cached_llbbs: IndexVec> = mir.basic_blocks @@ -217,7 +222,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cleanup_kinds, landing_pads: IndexVec::from_elem(None, &mir.basic_blocks), funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()), - cold_blocks: find_cold_blocks(cx.tcx(), mir), + cold_blocks: find_cold_blocks(tcx, mir), locals: locals::Locals::empty(), debug_context, per_local_var_debug_info: None, @@ -233,7 +238,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx.compute_per_local_var_debug_info(&mut start_bx).unzip(); fx.per_local_var_debug_info = per_local_var_debug_info; - let traversal_order = traversal::mono_reachable_reverse_postorder(mir, cx.tcx(), instance); + let traversal_order = traversal::mono_reachable_reverse_postorder(mir, tcx, instance); let memory_locals = analyze::non_ssa_locals(&fx, &traversal_order); // Allocate variable and temp allocas @@ -310,6 +315,61 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } +// FIXME: Move this function to mir::transform when post-mono MIR passes land. +fn optimize_use_clone<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + cx: &'a Bx::CodegenCx, + mut mir: Body<'tcx>, +) -> Body<'tcx> { + let tcx = cx.tcx(); + + if tcx.features().ergonomic_clones() { + for bb in mir.basic_blocks.as_mut() { + let mir::TerminatorKind::Call { + args, + destination, + target, + call_source: mir::CallSource::Use, + .. + } = &bb.terminator().kind + else { + continue; + }; + + // CallSource::Use calls always use 1 argument. + assert_eq!(args.len(), 1); + let arg = &args[0]; + + // These types are easily available from locals, so check that before + // doing DefId lookups to figure out what we're actually calling. + let arg_ty = arg.node.ty(&mir.local_decls, tcx); + + let ty::Ref(_region, inner_ty, mir::Mutability::Not) = *arg_ty.kind() else { continue }; + + if !tcx.type_is_copy_modulo_regions(cx.typing_env(), inner_ty) { + continue; + } + + let Some(arg_place) = arg.node.place() else { continue }; + + let destination_block = target.unwrap(); + + bb.statements.push(mir::Statement { + source_info: bb.terminator().source_info, + kind: mir::StatementKind::Assign(Box::new(( + *destination, + mir::Rvalue::Use(mir::Operand::Copy( + arg_place.project_deeper(&[mir::ProjectionElem::Deref], tcx), + )), + ))), + }); + + bb.terminator_mut().kind = mir::TerminatorKind::Goto { target: destination_block }; + } + } + + mir +} + /// Produces, for each argument, a `Value` pointing at the /// argument's value. As arguments are places, these are always /// indirect. diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 96d1ab018f60d..46fb9a895133b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,23 +1,33 @@ use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind}; -use rustc_attr_parsing::InstructionSetAttr; +use rustc_attr_data_structures::InstructionSetAttr; use rustc_hir::def_id::DefId; -use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility}; -use rustc_middle::mir::{Body, InlineAsmOperand}; -use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; -use rustc_middle::ty::{Instance, Ty, TyCtxt}; +use rustc_middle::mir::mono::{Linkage, MonoItemData, Visibility}; +use rustc_middle::mir::{InlineAsmOperand, START_BLOCK}; +use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; +use rustc_middle::ty::{Instance, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug, ty}; use rustc_span::sym; use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; use rustc_target::spec::{BinaryFormat, WasmCAbi}; use crate::common; -use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods}; - -pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - cx: &'a Bx::CodegenCx, - mir: &Body<'tcx>, +use crate::mir::AsmCodegenMethods; +use crate::traits::GlobalAsmOperandRef; + +pub fn codegen_naked_asm< + 'a, + 'tcx, + Cx: LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>> + + FnAbiOf<'tcx, FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>> + + AsmCodegenMethods<'tcx>, +>( + cx: &'a mut Cx, instance: Instance<'tcx>, + item_data: MonoItemData, ) { + assert!(!instance.args.has_infer()); + let mir = cx.tcx().instance_mir(instance.def); + let rustc_middle::mir::TerminatorKind::InlineAsm { asm_macro: _, template, @@ -26,15 +36,14 @@ pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( line_spans, targets: _, unwind: _, - } = mir.basic_blocks.iter().next().unwrap().terminator().kind + } = mir.basic_blocks[START_BLOCK].terminator().kind else { bug!("#[naked] functions should always terminate with an asm! block") }; let operands: Vec<_> = - operands.iter().map(|op| inline_to_global_operand::(cx, instance, op)).collect(); + operands.iter().map(|op| inline_to_global_operand::(cx, instance, op)).collect(); - let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap(); let name = cx.mangled_name(instance); let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data, fn_abi); @@ -47,8 +56,8 @@ pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx.codegen_global_asm(&template_vec, &operands, options, line_spans); } -fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - cx: &'a Bx::CodegenCx, +fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>>>( + cx: &'a Cx, instance: Instance<'tcx>, op: &InlineAsmOperand<'tcx>, ) -> GlobalAsmOperandRef<'tcx> { @@ -86,7 +95,9 @@ fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ); let instance = match mono_type.kind() { - &ty::FnDef(def_id, args) => Instance::new(def_id, args), + &ty::FnDef(def_id, args) => { + Instance::expect_resolve(cx.tcx(), cx.typing_env(), def_id, args, value.span) + } _ => bug!("asm sym is not a function"), }; @@ -108,7 +119,7 @@ fn prefix_and_suffix<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, asm_name: &str, - item_data: &MonoItemData, + item_data: MonoItemData, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, ) -> (String, String) { use std::fmt::Write; @@ -125,7 +136,8 @@ fn prefix_and_suffix<'tcx>( // the alignment from a `#[repr(align())]` is used if it specifies a higher alignment. // if no alignment is specified, an alignment of 4 bytes is used. let min_function_alignment = tcx.sess.opts.unstable_opts.min_function_alignment; - let align = Ord::max(min_function_alignment, attrs.alignment).map(|a| a.bytes()).unwrap_or(4); + let align_bytes = + Ord::max(min_function_alignment, attrs.alignment).map(|a| a.bytes()).unwrap_or(4); // In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`. let (arch_prefix, arch_suffix) = if is_arm { @@ -157,12 +169,16 @@ fn prefix_and_suffix<'tcx>( } Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR => { match asm_binary_format { - BinaryFormat::Elf - | BinaryFormat::Coff - | BinaryFormat::Wasm - | BinaryFormat::Xcoff => { + BinaryFormat::Elf | BinaryFormat::Coff | BinaryFormat::Wasm => { writeln!(w, ".weak {asm_name}")?; } + BinaryFormat::Xcoff => { + // FIXME: there is currently no way of defining a weak symbol in inline assembly + // for AIX. See https://github.com/llvm/llvm-project/issues/130269 + emit_fatal( + "cannot create weak symbols from inline assembly for this target", + ) + } BinaryFormat::MachO => { writeln!(w, ".globl {asm_name}")?; writeln!(w, ".weak_definition {asm_name}")?; @@ -189,7 +205,7 @@ fn prefix_and_suffix<'tcx>( let mut begin = String::new(); let mut end = String::new(); match asm_binary_format { - BinaryFormat::Elf | BinaryFormat::Xcoff => { + BinaryFormat::Elf => { let section = link_section.unwrap_or(format!(".text.{asm_name}")); let progbits = match is_arm { @@ -203,10 +219,12 @@ fn prefix_and_suffix<'tcx>( }; writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap(); - writeln!(begin, ".balign {align}").unwrap(); + writeln!(begin, ".balign {align_bytes}").unwrap(); write_linkage(&mut begin).unwrap(); - if let Visibility::Hidden = item_data.visibility { - writeln!(begin, ".hidden {asm_name}").unwrap(); + match item_data.visibility { + Visibility::Default => {} + Visibility::Protected => writeln!(begin, ".protected {asm_name}").unwrap(), + Visibility::Hidden => writeln!(begin, ".hidden {asm_name}").unwrap(), } writeln!(begin, ".type {asm_name}, {function}").unwrap(); if !arch_prefix.is_empty() { @@ -224,10 +242,11 @@ fn prefix_and_suffix<'tcx>( BinaryFormat::MachO => { let section = link_section.unwrap_or("__TEXT,__text".to_string()); writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap(); - writeln!(begin, ".balign {align}").unwrap(); + writeln!(begin, ".balign {align_bytes}").unwrap(); write_linkage(&mut begin).unwrap(); - if let Visibility::Hidden = item_data.visibility { - writeln!(begin, ".private_extern {asm_name}").unwrap(); + match item_data.visibility { + Visibility::Default | Visibility::Protected => {} + Visibility::Hidden => writeln!(begin, ".private_extern {asm_name}").unwrap(), } writeln!(begin, "{asm_name}:").unwrap(); @@ -240,12 +259,12 @@ fn prefix_and_suffix<'tcx>( BinaryFormat::Coff => { let section = link_section.unwrap_or(format!(".text.{asm_name}")); writeln!(begin, ".pushsection {},\"xr\"", section).unwrap(); - writeln!(begin, ".balign {align}").unwrap(); + writeln!(begin, ".balign {align_bytes}").unwrap(); write_linkage(&mut begin).unwrap(); writeln!(begin, ".def {asm_name}").unwrap(); writeln!(begin, ".scl 2").unwrap(); writeln!(begin, ".type 32").unwrap(); - writeln!(begin, ".endef {asm_name}").unwrap(); + writeln!(begin, ".endef").unwrap(); writeln!(begin, "{asm_name}:").unwrap(); writeln!(end).unwrap(); @@ -279,6 +298,33 @@ fn prefix_and_suffix<'tcx>( // .size is ignored for function symbols, so we can skip it writeln!(end, "end_function").unwrap(); } + BinaryFormat::Xcoff => { + // the LLVM XCOFFAsmParser is extremely incomplete and does not implement many of the + // documented directives. + // + // - https://github.com/llvm/llvm-project/blob/1b25c0c4da968fe78921ce77736e5baef4db75e3/llvm/lib/MC/MCParser/XCOFFAsmParser.cpp + // - https://www.ibm.com/docs/en/ssw_aix_71/assembler/assembler_pdf.pdf + // + // Consequently, we try our best here but cannot do as good a job as for other binary + // formats. + + // FIXME: start a section. `.csect` is not currently implemented in LLVM + + // fun fact: according to the assembler documentation, .align takes an exponent, + // but LLVM only accepts powers of 2 (but does emit the exponent) + // so when we hand `.align 32` to LLVM, the assembly output will contain `.align 5` + writeln!(begin, ".align {}", align_bytes).unwrap(); + + write_linkage(&mut begin).unwrap(); + if let Visibility::Hidden = item_data.visibility { + // FIXME apparently `.globl {asm_name}, hidden` is valid + // but due to limitations with `.weak` (see above) we can't really use that in general yet + } + writeln!(begin, "{asm_name}:").unwrap(); + + writeln!(end).unwrap(); + // FIXME: end the section? + } } (begin, end) @@ -300,7 +346,7 @@ fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, def_id // please also add `wasm32-unknown-unknown` back in `tests/assembly/wasm32-naked-fn.rs` // basically the commit introducing this comment should be reverted if let PassMode::Pair { .. } = fn_abi.ret.mode { - let _ = WasmCAbi::Legacy; + let _ = WasmCAbi::Legacy { with_lint: true }; span_bug!( tcx.def_span(def_id), "cannot return a pair (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666" @@ -352,7 +398,7 @@ fn wasm_type<'tcx>( BackendRepr::SimdVector { .. } => "v128", BackendRepr::Memory { .. } => { // FIXME: remove this branch once the wasm32-unknown-unknown ABI is fixed - let _ = WasmCAbi::Legacy; + let _ = WasmCAbi::Legacy { with_lint: true }; span_bug!( tcx.def_span(def_id), "cannot use memory args (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666" diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 4b33bdeadba4d..eade9e52de95a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -3,16 +3,18 @@ use std::fmt; use arrayvec::ArrayVec; use either::Either; use rustc_abi as abi; -use rustc_abi::{Align, BackendRepr, Size}; +use rustc_abi::{Align, BackendRepr, FIRST_VARIANT, Primitive, Size, TagEncoding, Variants}; use rustc_middle::mir::interpret::{Pointer, Scalar, alloc_range}; use rustc_middle::mir::{self, ConstValue}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::{bug, span_bug}; -use tracing::debug; +use rustc_session::config::OptLevel; +use tracing::{debug, instrument}; use super::place::{PlaceRef, PlaceValue}; use super::{FunctionCx, LocalRef}; +use crate::common::IntPredicate; use crate::traits::*; use crate::{MemFlags, size_of_val}; @@ -203,30 +205,14 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let alloc_align = alloc.inner().align; assert!(alloc_align >= layout.align.abi); - // Returns `None` when the value is partially undefined or any byte of it has provenance. - // Otherwise returns the value or (if the entire value is undef) returns an undef. let read_scalar = |start, size, s: abi::Scalar, ty| { - let range = alloc_range(start, size); match alloc.0.read_scalar( bx, - range, + alloc_range(start, size), /*read_provenance*/ matches!(s.primitive(), abi::Primitive::Pointer(_)), ) { - Ok(val) => Some(bx.scalar_to_backend(val, s, ty)), - Err(_) => { - // We may have failed due to partial provenance or unexpected provenance, - // continue down the normal code path if so. - if alloc.0.provenance().range_empty(range, &bx.tcx()) - // Since `read_scalar` failed, but there were no relocations involved, the - // bytes must be partially or fully uninitialized. Thus we can now unwrap the - // information about the range of uninit bytes and check if it's the full range. - && alloc.0.init_mask().is_range_initialized(range).unwrap_err() == range - { - Some(bx.const_undef(ty)) - } else { - None - } - } + Ok(val) => bx.scalar_to_backend(val, s, ty), + Err(_) => bx.const_poison(ty), } }; @@ -237,14 +223,16 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { // check that walks over the type of `mplace` to make sure it is truly correct to treat this // like a `Scalar` (or `ScalarPair`). match layout.backend_repr { - BackendRepr::Scalar(s) => { + BackendRepr::Scalar(s @ abi::Scalar::Initialized { .. }) => { let size = s.size(bx); assert_eq!(size, layout.size, "abi::Scalar size does not match layout size"); - if let Some(val) = read_scalar(offset, size, s, bx.immediate_backend_type(layout)) { - return OperandRef { val: OperandValue::Immediate(val), layout }; - } + let val = read_scalar(offset, size, s, bx.immediate_backend_type(layout)); + OperandRef { val: OperandValue::Immediate(val), layout } } - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair( + a @ abi::Scalar::Initialized { .. }, + b @ abi::Scalar::Initialized { .. }, + ) => { let (a_size, b_size) = (a.size(bx), b.size(bx)); let b_offset = (offset + a_size).align_to(b.align(bx).abi); assert!(b_offset.bytes() > 0); @@ -260,21 +248,20 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { b, bx.scalar_pair_element_backend_type(layout, 1, true), ); - if let (Some(a_val), Some(b_val)) = (a_val, b_val) { - return OperandRef { val: OperandValue::Pair(a_val, b_val), layout }; - } + OperandRef { val: OperandValue::Pair(a_val, b_val), layout } + } + _ if layout.is_zst() => OperandRef::zero_sized(layout), + _ => { + // Neither a scalar nor scalar pair. Load from a place + // FIXME: should we cache `const_data_from_alloc` to avoid repeating this for the + // same `ConstAllocation`? + let init = bx.const_data_from_alloc(alloc); + let base_addr = bx.static_addr_of(init, alloc_align, None); + + let llval = bx.const_ptr_byte_offset(base_addr, offset); + bx.load_operand(PlaceRef::new_sized(llval, layout)) } - _ if layout.is_zst() => return OperandRef::zero_sized(layout), - _ => {} } - // Neither a scalar nor scalar pair. Load from a place - // FIXME: should we cache `const_data_from_alloc` to avoid repeating this for the - // same `ConstAllocation`? - let init = bx.const_data_from_alloc(alloc); - let base_addr = bx.static_addr_of(init, alloc_align, None); - - let llval = bx.const_ptr_byte_offset(base_addr, offset); - bx.load_operand(PlaceRef::new_sized(llval, layout)) } /// Asserts that this operand refers to a scalar and returns @@ -430,6 +417,178 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { OperandRef { val, layout: field } } + + /// Obtain the actual discriminant of a value. + #[instrument(level = "trace", skip(fx, bx))] + pub fn codegen_get_discr>( + self, + fx: &mut FunctionCx<'a, 'tcx, Bx>, + bx: &mut Bx, + cast_to: Ty<'tcx>, + ) -> V { + let dl = &bx.tcx().data_layout; + let cast_to_layout = bx.cx().layout_of(cast_to); + let cast_to = bx.cx().immediate_backend_type(cast_to_layout); + + // We check uninhabitedness separately because a type like + // `enum Foo { Bar(i32, !) }` is still reported as `Variants::Single`, + // *not* as `Variants::Empty`. + if self.layout.is_uninhabited() { + return bx.cx().const_poison(cast_to); + } + + let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants { + Variants::Empty => unreachable!("we already handled uninhabited types"), + Variants::Single { index } => { + let discr_val = + if let Some(discr) = self.layout.ty.discriminant_for_variant(bx.tcx(), index) { + discr.val + } else { + // This arm is for types which are neither enums nor coroutines, + // and thus for which the only possible "variant" should be the first one. + assert_eq!(index, FIRST_VARIANT); + // There's thus no actual discriminant to return, so we return + // what it would have been if this was a single-variant enum. + 0 + }; + return bx.cx().const_uint_big(cast_to, discr_val); + } + Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => { + (tag, tag_encoding, tag_field) + } + }; + + // Read the tag/niche-encoded discriminant from memory. + let tag_op = match self.val { + OperandValue::ZeroSized => bug!(), + OperandValue::Immediate(_) | OperandValue::Pair(_, _) => { + self.extract_field(fx, bx, tag_field) + } + OperandValue::Ref(place) => { + let tag = place.with_type(self.layout).project_field(bx, tag_field); + bx.load_operand(tag) + } + }; + let tag_imm = tag_op.immediate(); + + // Decode the discriminant (specifically if it's niche-encoded). + match *tag_encoding { + TagEncoding::Direct => { + let signed = match tag_scalar.primitive() { + // 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`. + Primitive::Int(_, signed) => !tag_scalar.is_bool() && signed, + _ => false, + }; + bx.intcast(tag_imm, cast_to, signed) + } + TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => { + // Cast to an integer so we don't have to treat a pointer as a + // special case. + let (tag, tag_llty) = match tag_scalar.primitive() { + // FIXME(erikdesjardins): handle non-default addrspace ptr sizes + Primitive::Pointer(_) => { + let t = bx.type_from_integer(dl.ptr_sized_integer()); + let tag = bx.ptrtoint(tag_imm, t); + (tag, t) + } + _ => (tag_imm, bx.cx().immediate_backend_type(tag_op.layout)), + }; + + // Layout ensures that we only get here for cases where the discriminant + // value and the variant index match, since that's all `Niche` can encode. + // But for emphasis and debugging, let's double-check one anyway. + debug_assert_eq!( + self.layout + .ty + .discriminant_for_variant(bx.tcx(), untagged_variant) + .unwrap() + .val, + u128::from(untagged_variant.as_u32()), + ); + + let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); + + // We have a subrange `niche_start..=niche_end` inside `range`. + // If the value of the tag is inside this subrange, it's a + // "niche value", an increment of the discriminant. Otherwise it + // indicates the untagged variant. + // A general algorithm to extract the discriminant from the tag + // is: + // relative_tag = tag - niche_start + // is_niche = relative_tag <= (ule) relative_max + // discr = if is_niche { + // cast(relative_tag) + niche_variants.start() + // } else { + // untagged_variant + // } + // However, we will likely be able to emit simpler code. + let (is_niche, tagged_discr, delta) = if relative_max == 0 { + // Best case scenario: only one tagged variant. This will + // likely become just a comparison and a jump. + // The algorithm is: + // is_niche = tag == niche_start + // discr = if is_niche { + // niche_start + // } else { + // untagged_variant + // } + let niche_start = bx.cx().const_uint_big(tag_llty, niche_start); + let is_niche = bx.icmp(IntPredicate::IntEQ, tag, niche_start); + let tagged_discr = + bx.cx().const_uint(cast_to, niche_variants.start().as_u32() as u64); + (is_niche, tagged_discr, 0) + } else { + // The special cases don't apply, so we'll have to go with + // the general algorithm. + let relative_discr = bx.sub(tag, bx.cx().const_uint_big(tag_llty, niche_start)); + let cast_tag = bx.intcast(relative_discr, cast_to, false); + let is_niche = bx.icmp( + IntPredicate::IntULE, + relative_discr, + bx.cx().const_uint(tag_llty, relative_max as u64), + ); + + // Thanks to parameter attributes and load metadata, LLVM already knows + // the general valid range of the tag. It's possible, though, for there + // to be an impossible value *in the middle*, which those ranges don't + // communicate, so it's worth an `assume` to let the optimizer know. + if niche_variants.contains(&untagged_variant) + && bx.cx().sess().opts.optimize != OptLevel::No + { + let impossible = + u64::from(untagged_variant.as_u32() - niche_variants.start().as_u32()); + let impossible = bx.cx().const_uint(tag_llty, impossible); + let ne = bx.icmp(IntPredicate::IntNE, relative_discr, impossible); + bx.assume(ne); + } + + (is_niche, cast_tag, niche_variants.start().as_u32() as u128) + }; + + let tagged_discr = if delta == 0 { + tagged_discr + } else { + bx.add(tagged_discr, bx.cx().const_uint_big(cast_to, delta)) + }; + + let discr = bx.select( + is_niche, + tagged_discr, + bx.cx().const_uint(cast_to, untagged_variant.as_u32() as u64), + ); + + // In principle we could insert assumes on the possible range of `discr`, but + // currently in LLVM this isn't worth it because the original `tag` will + // have either a `range` parameter attribute or `!range` metadata, + // or come from a `transmute` that already `assume`d it. + + discr + } + } + } } impl<'a, 'tcx, V: CodegenObject> OperandValue { diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 00e01e47fee3a..31db7fa9a1888 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -1,4 +1,3 @@ -use rustc_abi::Primitive::{Int, Pointer}; use rustc_abi::{Align, BackendRepr, FieldsShape, Size, TagEncoding, VariantIdx, Variants}; use rustc_middle::mir::PlaceTy; use rustc_middle::mir::interpret::Scalar; @@ -133,7 +132,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { Self::alloca(bx, ptr_layout) } - pub fn len>(&self, cx: &Cx) -> V { + pub fn len>(&self, cx: &Cx) -> V { if let FieldsShape::Array { count, .. } = self.layout.fields { if self.layout.is_unsized() { assert_eq!(count, 0); @@ -233,129 +232,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { val.with_type(field) } - /// Obtain the actual discriminant of a value. - #[instrument(level = "trace", skip(bx))] - pub fn codegen_get_discr>( - self, - bx: &mut Bx, - cast_to: Ty<'tcx>, - ) -> V { - let dl = &bx.tcx().data_layout; - let cast_to_layout = bx.cx().layout_of(cast_to); - let cast_to = bx.cx().immediate_backend_type(cast_to_layout); - if self.layout.is_uninhabited() { - return bx.cx().const_poison(cast_to); - } - let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants { - Variants::Empty => unreachable!("we already handled uninhabited types"), - 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 { 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_op = bx.load_operand(tag); - let tag_imm = tag_op.immediate(); - - // Decode the discriminant (specifically if it's niche-encoded). - match *tag_encoding { - TagEncoding::Direct => { - let signed = match tag_scalar.primitive() { - // 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_imm, cast_to, signed) - } - TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => { - // Cast to an integer so we don't have to treat a pointer as a - // special case. - let (tag, tag_llty) = match tag_scalar.primitive() { - // FIXME(erikdesjardins): handle non-default addrspace ptr sizes - Pointer(_) => { - let t = bx.type_from_integer(dl.ptr_sized_integer()); - let tag = bx.ptrtoint(tag_imm, t); - (tag, t) - } - _ => (tag_imm, bx.cx().immediate_backend_type(tag_op.layout)), - }; - - let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); - - // We have a subrange `niche_start..=niche_end` inside `range`. - // If the value of the tag is inside this subrange, it's a - // "niche value", an increment of the discriminant. Otherwise it - // indicates the untagged variant. - // A general algorithm to extract the discriminant from the tag - // is: - // relative_tag = tag - niche_start - // is_niche = relative_tag <= (ule) relative_max - // discr = if is_niche { - // cast(relative_tag) + niche_variants.start() - // } else { - // untagged_variant - // } - // However, we will likely be able to emit simpler code. - let (is_niche, tagged_discr, delta) = if relative_max == 0 { - // Best case scenario: only one tagged variant. This will - // likely become just a comparison and a jump. - // The algorithm is: - // is_niche = tag == niche_start - // discr = if is_niche { - // niche_start - // } else { - // untagged_variant - // } - let niche_start = bx.cx().const_uint_big(tag_llty, niche_start); - let is_niche = bx.icmp(IntPredicate::IntEQ, tag, niche_start); - let tagged_discr = - bx.cx().const_uint(cast_to, niche_variants.start().as_u32() as u64); - (is_niche, tagged_discr, 0) - } else { - // The special cases don't apply, so we'll have to go with - // the general algorithm. - let relative_discr = bx.sub(tag, bx.cx().const_uint_big(tag_llty, niche_start)); - let cast_tag = bx.intcast(relative_discr, cast_to, false); - let is_niche = bx.icmp( - IntPredicate::IntULE, - relative_discr, - bx.cx().const_uint(tag_llty, relative_max as u64), - ); - (is_niche, cast_tag, niche_variants.start().as_u32() as u128) - }; - - let tagged_discr = if delta == 0 { - tagged_discr - } else { - bx.add(tagged_discr, bx.cx().const_uint_big(cast_to, delta)) - }; - - let discr = bx.select( - is_niche, - tagged_discr, - bx.cx().const_uint(cast_to, untagged_variant.as_u32() as u64), - ); - - // In principle we could insert assumes on the possible range of `discr`, but - // currently in LLVM this seems to be a pessimization. - - discr - } - } - } - /// Sets the discriminant for a new value of the given case of the given /// representation. pub fn codegen_set_discr>( diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 67555d4033207..5c14fe5cd10b7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_middle::{bug, mir, span_bug}; use rustc_session::config::OptLevel; use rustc_span::{DUMMY_SP, Span}; -use tracing::{debug, instrument, trace}; +use tracing::{debug, instrument}; use super::operand::{OperandRef, OperandValue}; use super::place::PlaceRef; @@ -86,15 +86,30 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::Rvalue::Repeat(ref elem, count) => { - let cg_elem = self.codegen_operand(bx, elem); - // Do not generate the loop for zero-sized elements or empty arrays. if dest.layout.is_zst() { return; } - // If `v` is an integer constant whose value is just a single byte repeated N times, - // emit a `memset` filling the entire `dest` with that byte. + // When the element is a const with all bytes uninit, emit a single memset that + // writes undef to the entire destination. + if let mir::Operand::Constant(const_op) = elem { + let val = self.eval_mir_constant(const_op); + if val.all_bytes_uninit(self.cx.tcx()) { + let size = bx.const_usize(dest.layout.size.bytes()); + bx.memset( + dest.val.llval, + bx.const_undef(bx.type_i8()), + size, + dest.val.align, + MemFlags::empty(), + ); + return; + } + } + + let cg_elem = self.codegen_operand(bx, elem); + let try_init_all_same = |bx: &mut Bx, v| { let start = dest.val.llval; let size = bx.const_usize(dest.layout.size.bytes()); @@ -119,33 +134,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { false }; - trace!(?cg_elem.val); match cg_elem.val { OperandValue::Immediate(v) => { if try_init_all_same(bx, v) { return; } } - OperandValue::Pair(a, b) => { - let a_is_undef = bx.cx().is_undef(a); - match (a_is_undef, bx.cx().is_undef(b)) { - // Can happen for uninit unions - (true, true) => { - // FIXME: can we produce better output here? - } - (false, true) | (true, false) => { - let val = if a_is_undef { b } else { a }; - if try_init_all_same(bx, val) { - return; - } - } - (false, false) => { - // FIXME: if both are the same value, use try_init_all_same - } - } - } - OperandValue::ZeroSized => unreachable!("checked above"), - OperandValue::Ref(..) => {} + _ => (), } let count = self @@ -386,6 +381,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) -> Bx::Value { assert_eq!(from_scalar.size(self.cx), to_scalar.size(self.cx)); + // While optimizations will remove no-op transmutes, they might still be + // there in debug or things that aren't no-op in MIR because they change + // the Rust type but not the underlying layout/niche. + if from_scalar == to_scalar && from_backend_ty == to_backend_ty { + return imm; + } + use abi::Primitive::*; imm = bx.from_immediate(imm); @@ -721,7 +723,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Discriminant(ref place) => { let discr_ty = rvalue.ty(self.mir, bx.tcx()); let discr_ty = self.monomorphize(discr_ty); - let discr = self.codegen_place(bx, place.as_ref()).codegen_get_discr(bx, discr_ty); + let operand = self.codegen_consume(bx, place.as_ref()); + let discr = operand.codegen_get_discr(self, bx, discr_ty); OperandRef { val: OperandValue::Immediate(discr), layout: self.cx.layout_of(discr_ty), @@ -761,7 +764,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let tcx = self.cx.tcx(); OperandRef { val: OperandValue::Immediate(val), - layout: self.cx.layout_of(tcx.types.usize), + layout: self.cx.layout_of(null_op.ty(tcx)), } } @@ -851,15 +854,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn evaluate_array_len(&mut self, bx: &mut Bx, place: mir::Place<'tcx>) -> Bx::Value { // ZST are passed as operands and require special handling // because codegen_place() panics if Local is operand. - if let Some(index) = place.as_local() { - if let LocalRef::Operand(op) = self.locals[index] { - if let ty::Array(_, n) = op.layout.ty.kind() { - let n = n - .try_to_target_usize(bx.tcx()) - .expect("expected monomorphic const in codegen"); - return bx.cx().const_usize(n); - } - } + if let Some(index) = place.as_local() + && let LocalRef::Operand(op) = self.locals[index] + && let ty::Array(_, n) = op.layout.ty.kind() + { + let n = n.try_to_target_usize(bx.tcx()).expect("expected monomorphic const in codegen"); + return bx.cx().const_usize(n); } // use common size calculation for non zero-sized types let cg_value = self.codegen_place(bx, place.as_ref()); @@ -1005,6 +1005,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::BinOp::Cmp => { use std::cmp::Ordering; assert!(!is_float); + if let Some(value) = bx.three_way_compare(lhs_ty, lhs, rhs) { + return value; + } let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed); if bx.cx().tcx().sess.opts.optimize == OptLevel::No { // FIXME: This actually generates tighter assembly, and is a classic trick diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index f6af889fd6ecb..c2067e52afecd 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -1,17 +1,18 @@ -use rustc_hir as hir; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility}; -use rustc_middle::ty::Instance; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; -use rustc_middle::{span_bug, ty}; +use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility}; +use rustc_middle::ty::layout::HasTyCtxt; use tracing::debug; +use crate::base; +use crate::mir::naked_asm; use crate::traits::*; -use crate::{base, common}; pub trait MonoItemExt<'a, 'tcx> { - fn define>(&self, cx: &'a Bx::CodegenCx); + fn define>( + &self, + cx: &'a mut Bx::CodegenCx, + item_data: MonoItemData, + ); fn predefine>( &self, cx: &'a Bx::CodegenCx, @@ -22,7 +23,11 @@ pub trait MonoItemExt<'a, 'tcx> { } impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { - fn define>(&self, cx: &'a Bx::CodegenCx) { + fn define>( + &self, + cx: &'a mut Bx::CodegenCx, + item_data: MonoItemData, + ) { debug!( "BEGIN IMPLEMENTING '{} ({})' in cgu {}", self, @@ -35,71 +40,19 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { cx.codegen_static(def_id); } MonoItem::GlobalAsm(item_id) => { - let item = cx.tcx().hir_item(item_id); - if let hir::ItemKind::GlobalAsm { asm, .. } = item.kind { - let operands: Vec<_> = asm - .operands - .iter() - .map(|(op, op_sp)| match *op { - hir::InlineAsmOperand::Const { ref anon_const } => { - match cx.tcx().const_eval_poly(anon_const.def_id.to_def_id()) { - Ok(const_value) => { - let ty = cx - .tcx() - .typeck_body(anon_const.body) - .node_type(anon_const.hir_id); - let string = common::asm_const_to_str( - cx.tcx(), - *op_sp, - const_value, - cx.layout_of(ty), - ); - GlobalAsmOperandRef::Const { string } - } - Err(ErrorHandled::Reported { .. }) => { - // An error has already been reported and - // compilation is guaranteed to fail if execution - // hits this path. So an empty string instead of - // a stringified constant value will suffice. - GlobalAsmOperandRef::Const { string: String::new() } - } - Err(ErrorHandled::TooGeneric(_)) => { - span_bug!( - *op_sp, - "asm const cannot be resolved; too generic" - ) - } - } - } - hir::InlineAsmOperand::SymFn { expr } => { - let ty = cx.tcx().typeck(item_id.owner_id).expr_ty(expr); - let instance = match ty.kind() { - &ty::FnDef(def_id, args) => Instance::new(def_id, args), - _ => span_bug!(*op_sp, "asm sym is not a function"), - }; - - GlobalAsmOperandRef::SymFn { instance } - } - hir::InlineAsmOperand::SymStatic { path: _, def_id } => { - GlobalAsmOperandRef::SymStatic { def_id } - } - hir::InlineAsmOperand::In { .. } - | hir::InlineAsmOperand::Out { .. } - | hir::InlineAsmOperand::InOut { .. } - | hir::InlineAsmOperand::SplitInOut { .. } - | hir::InlineAsmOperand::Label { .. } => { - span_bug!(*op_sp, "invalid operand type for global_asm!") - } - }) - .collect(); - - cx.codegen_global_asm(asm.template, &operands, asm.options, asm.line_spans); - } else { - span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") - } + base::codegen_global_asm(cx, item_id); } MonoItem::Fn(instance) => { - base::codegen_instance::(cx, instance); + if cx + .tcx() + .codegen_fn_attrs(instance.def_id()) + .flags + .contains(CodegenFnAttrFlags::NAKED) + { + naked_asm::codegen_naked_asm::(cx, instance, item_data); + } else { + base::codegen_instance::(cx, instance); + } } } diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 95a5e96fe46e4..3ecea522837a1 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,4 +1,4 @@ -use rustc_attr_parsing::InstructionSetAttr; +use rustc_attr_data_structures::InstructionSetAttr; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Applicability; @@ -190,7 +190,7 @@ pub(crate) fn provide(providers: &mut Providers) { }, implied_target_features: |tcx, feature: Symbol| { let feature = feature.as_str(); - UnordSet::from(tcx.sess.target.implied_target_features(std::iter::once(feature))) + UnordSet::from(tcx.sess.target.implied_target_features(feature)) .into_sorted_stable_ord() .into_iter() .map(|s| Symbol::intern(s)) diff --git a/compiler/rustc_codegen_ssa/src/traits/abi.rs b/compiler/rustc_codegen_ssa/src/traits/abi.rs index 60d8f2a9ece48..49c51caa996e4 100644 --- a/compiler/rustc_codegen_ssa/src/traits/abi.rs +++ b/compiler/rustc_codegen_ssa/src/traits/abi.rs @@ -1,5 +1,5 @@ use super::BackendTypes; -pub trait AbiBuilderMethods<'tcx>: BackendTypes { +pub trait AbiBuilderMethods: BackendTypes { fn get_param(&mut self, index: usize) -> Self::Value; } diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index 7767bffbfbfd6..cc7a6a3f19e9e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -62,7 +62,7 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes { pub trait AsmCodegenMethods<'tcx> { fn codegen_global_asm( - &self, + &mut self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index ebcf118b90380..e2f1458d06232 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -18,7 +18,7 @@ use super::write::WriteBackendMethods; use crate::back::archive::ArArchiveBuilderBuilder; use crate::back::link::link_binary; use crate::back::write::TargetMachineFactoryFn; -use crate::{CodegenResults, ModuleCodegen}; +use crate::{CodegenResults, ModuleCodegen, TargetConfig}; pub trait BackendTypes { type Value: CodegenObject; @@ -45,10 +45,19 @@ pub trait CodegenBackend { fn print(&self, _req: &PrintRequest, _out: &mut String, _sess: &Session) {} - /// Returns the features that should be set in `cfg(target_features)`. - /// RUSTC_SPECIFIC_FEATURES should be skipped here, those are handled outside codegen. - fn target_features_cfg(&self, _sess: &Session, _allow_unstable: bool) -> Vec { - vec![] + /// Collect target-specific options that should be set in `cfg(...)`, including + /// `target_feature` and support for unstable float types. + fn target_config(&self, _sess: &Session) -> TargetConfig { + TargetConfig { + target_features: vec![], + unstable_target_features: vec![], + // `true` is used as a default so backends need to acknowledge when they do not + // support the float types, rather than accidentally quietly skipping all tests. + has_reliable_f16: true, + has_reliable_f16_math: true, + has_reliable_f128: true, + has_reliable_f128_math: true, + } } fn print_passes(&self) {} diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 99fd6b6510ffe..f66309cf340cc 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -40,7 +40,7 @@ pub trait BuilderMethods<'a, 'tcx>: + CoverageInfoBuilderMethods<'tcx> + DebugInfoBuilderMethods + ArgAbiBuilderMethods<'tcx> - + AbiBuilderMethods<'tcx> + + AbiBuilderMethods + IntrinsicCallBuilderMethods<'tcx> + AsmBuilderMethods<'tcx> + StaticBuilderMethods @@ -397,6 +397,18 @@ pub trait BuilderMethods<'a, 'tcx>: 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; + /// Returns `-1` if `lhs < rhs`, `0` if `lhs == rhs`, and `1` if `lhs > rhs`. + // FIXME: Move the default implementation from `codegen_scalar_binop` into this method and + // remove the `Option` return once LLVM 20 is the minimum version. + fn three_way_compare( + &mut self, + _ty: Ty<'tcx>, + _lhs: Self::Value, + _rhs: Self::Value, + ) -> Option { + None + } + fn memcpy( &mut self, dst: Self::Value, diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 5cfb56ebacee8..d83a04d814be3 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -3,13 +3,12 @@ use rustc_middle::mir::interpret::{ConstAllocation, Scalar}; use super::BackendTypes; -pub trait ConstCodegenMethods<'tcx>: BackendTypes { +pub trait ConstCodegenMethods: BackendTypes { // Constant constructors fn const_null(&self, t: Self::Type) -> Self::Value; /// Generate an uninitialized value (matching uninitialized memory in MIR). /// Whether memory is initialized or not is tracked byte-for-byte. fn const_undef(&self, t: Self::Type) -> Self::Value; - fn is_undef(&self, v: Self::Value) -> bool; /// Generate a fake value. Poison always affects the entire value, even if just a single byte is /// poison. This can only be used in codepaths that are already UB, i.e., UB-free Rust code /// (including code that e.g. copies uninit memory with `MaybeUninit`) can never encounter a @@ -38,7 +37,7 @@ pub trait ConstCodegenMethods<'tcx>: BackendTypes { fn const_to_opt_uint(&self, v: Self::Value) -> Option; fn const_to_opt_u128(&self, v: Self::Value, sign_ext: bool) -> Option; - fn const_data_from_alloc(&self, alloc: ConstAllocation<'tcx>) -> Self::Value; + fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value; fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 90fcfbe4da7a1..239857a4298df 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -55,7 +55,7 @@ pub trait CodegenObject = Copy + PartialEq + fmt::Debug; pub trait CodegenMethods<'tcx> = LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>> + FnAbiOf<'tcx, FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>> + TypeCodegenMethods<'tcx> - + ConstCodegenMethods<'tcx> + + ConstCodegenMethods + StaticCodegenMethods + DebugInfoCodegenMethods<'tcx> + AsmCodegenMethods<'tcx> diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index fbd927d0d663e..32d9f27d32d34 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -9,7 +9,7 @@ use super::misc::MiscCodegenMethods; use crate::common::TypeKind; use crate::mir::place::PlaceRef; -pub trait BaseTypeCodegenMethods<'tcx>: BackendTypes { +pub trait BaseTypeCodegenMethods: BackendTypes { fn type_i8(&self) -> Self::Type; fn type_i16(&self) -> Self::Type; fn type_i32(&self) -> Self::Type; @@ -41,7 +41,7 @@ pub trait BaseTypeCodegenMethods<'tcx>: BackendTypes { } pub trait DerivedTypeCodegenMethods<'tcx>: - BaseTypeCodegenMethods<'tcx> + MiscCodegenMethods<'tcx> + HasTyCtxt<'tcx> + HasTypingEnv<'tcx> + BaseTypeCodegenMethods + MiscCodegenMethods<'tcx> + HasTyCtxt<'tcx> + HasTypingEnv<'tcx> { fn type_int(&self) -> Self::Type { match &self.sess().target.c_int_width[..] { @@ -87,10 +87,7 @@ pub trait DerivedTypeCodegenMethods<'tcx>: } impl<'tcx, T> DerivedTypeCodegenMethods<'tcx> for T where - Self: BaseTypeCodegenMethods<'tcx> - + MiscCodegenMethods<'tcx> - + HasTyCtxt<'tcx> - + HasTypingEnv<'tcx> + Self: BaseTypeCodegenMethods + MiscCodegenMethods<'tcx> + HasTyCtxt<'tcx> + HasTypingEnv<'tcx> { } diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml index a0cc2c65e6e03..93d0d5b9a71e6 100644 --- a/compiler/rustc_const_eval/Cargo.toml +++ b/compiler/rustc_const_eval/Cargo.toml @@ -9,7 +9,7 @@ either = "1" rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } @@ -23,6 +23,5 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -rustc_type_ir = { path = "../rustc_type_ir" } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index ccf9b240d4088..f4defd2aa1343 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -12,6 +12,27 @@ const_eval_already_reported = const_eval_assume_false = `assume` called with `false` +const_eval_bad_pointer_op = {$operation -> + [MemoryAccess] memory access failed + [InboundsPointerArithmetic] in-bounds pointer arithmetic failed + *[Dereferenceable] pointer not dereferenceable +} +const_eval_bad_pointer_op_attempting = {const_eval_bad_pointer_op}: {$operation -> + [MemoryAccess] attempting to access {$inbounds_size -> + [1] 1 byte + *[x] {$inbounds_size} bytes + } + [InboundsPointerArithmetic] attempting to offset pointer by {$inbounds_size -> + [1] 1 byte + *[x] {$inbounds_size} bytes + } + *[Dereferenceable] pointer must {$inbounds_size -> + [0] point to some allocation + [1] be dereferenceable for 1 byte + *[x] be dereferenceable for {$inbounds_size} bytes + } + } + const_eval_bounds_check_failed = indexing out of bounds: the len is {$len} but the index is {$index} const_eval_call_nonzero_intrinsic = @@ -39,9 +60,9 @@ const_eval_copy_nonoverlapping_overlapping = `copy_nonoverlapping` called on overlapping ranges const_eval_dangling_int_pointer = - {$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got {$pointer} which is a dangling pointer (it has no provenance) + {const_eval_bad_pointer_op_attempting}, but got {$pointer} which is a dangling pointer (it has no provenance) const_eval_dangling_null_pointer = - {$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got a null pointer + {const_eval_bad_pointer_op_attempting}, but got null pointer const_eval_dangling_ptr_in_final = encountered dangling pointer in final value of {const_eval_intern_kind} const_eval_dead_local = @@ -77,23 +98,8 @@ const_eval_error = {$error_kind -> const_eval_exact_div_has_remainder = exact_div: {$a} cannot be divided by {$b} without remainder -const_eval_expected_inbounds_pointer = - expected a pointer to {$inbounds_size_abs -> - [0] some allocation - *[x] {$inbounds_size_is_neg -> - [false] {$inbounds_size_abs -> - [1] 1 byte of memory - *[x] {$inbounds_size_abs} bytes of memory - } - *[true] the end of {$inbounds_size_abs -> - [1] 1 byte of memory - *[x] {$inbounds_size_abs} bytes of memory - } - } - } - const_eval_extern_static = - cannot access extern static ({$did}) + cannot access extern static `{$did}` const_eval_extern_type_field = `extern type` field does not have a known offset const_eval_fn_ptr_call = @@ -111,7 +117,6 @@ const_eval_frame_note_inner = inside {$where_ -> const_eval_frame_note_last = the failure occurred here -const_eval_in_bounds_test = out-of-bounds pointer use const_eval_incompatible_calling_conventions = calling a function with calling convention {$callee_conv} using calling convention {$caller_conv} @@ -206,7 +211,6 @@ const_eval_long_running = const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id} -const_eval_memory_access_test = memory access failed const_eval_memory_exhausted = tried to allocate more memory than available to compiler @@ -287,8 +291,6 @@ const_eval_offset_from_out_of_bounds = `{$name}` called on two different pointers where the memory range between them is not in-bounds of an allocation const_eval_offset_from_overflow = `{$name}` called when first pointer is too far ahead of second -const_eval_offset_from_test = - out-of-bounds `offset_from` origin const_eval_offset_from_underflow = `{$name}` called when first pointer is too far before second const_eval_offset_from_unsigned_overflow = @@ -312,27 +314,25 @@ const_eval_partial_pointer_overwrite = unable to overwrite parts of a pointer in memory at {$ptr} const_eval_pointer_arithmetic_overflow = overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize` -const_eval_pointer_arithmetic_test = out-of-bounds pointer arithmetic + const_eval_pointer_out_of_bounds = - {$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got {$pointer} {$ptr_offset_is_neg -> - [true] which points to before the beginning of the allocation - *[false] {$inbounds_size_is_neg -> - [true] {$ptr_offset_abs -> - [0] which is at the beginning of the allocation - *[other] which does not have enough space to the beginning of the allocation - } - *[false] {$alloc_size_minus_ptr_offset -> - [0] which is at or beyond the end of the allocation of size {$alloc_size -> + {const_eval_bad_pointer_op_attempting}, but got {$pointer} which {$inbounds_size_is_neg -> + [false] {$alloc_size_minus_ptr_offset -> + [0] is at or beyond the end of the allocation of size {$alloc_size -> [1] 1 byte *[x] {$alloc_size} bytes } - [1] which is only 1 byte from the end of the allocation - *[x] which is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation + [1] is only 1 byte from the end of the allocation + *[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation + } + *[true] {$ptr_offset_abs -> + [0] is at the beginning of the allocation + *[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation } - } } + const_eval_pointer_use_after_free = - {$bad_pointer_message}: {$alloc_id} has been freed, so this pointer is dangling + {const_eval_bad_pointer_op}: {$alloc_id} has been freed, so this pointer is dangling const_eval_ptr_as_bytes_1 = this code performed an operation that depends on the underlying bytes representing a pointer const_eval_ptr_as_bytes_2 = @@ -381,7 +381,7 @@ const_eval_thread_local_access = thread-local statics cannot be accessed at compile-time const_eval_thread_local_static = - cannot access thread local static ({$did}) + cannot access thread local static `{$did}` const_eval_too_generic = encountered overly generic constant const_eval_too_many_caller_args = diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 90002d3f10905..b67a3ce03a945 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -6,7 +6,7 @@ use std::mem; use std::num::NonZero; use std::ops::Deref; -use rustc_attr_parsing::{ConstStability, StabilityLevel}; +use rustc_attr_data_structures as attrs; use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -34,7 +34,7 @@ use crate::check_consts::is_fn_or_trait_safe_to_expose_on_stable; use crate::errors; type QualifResults<'mir, 'tcx, Q> = - rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>; + rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'tcx, Q>>; #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum ConstConditionsHold { @@ -335,7 +335,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { self.tcx.dcx().span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`"); } if let Some(def_id) = def_id.as_local() - && let Err(guar) = self.tcx.at(span).check_well_formed(hir::OwnerId { def_id }) + && let Err(guar) = self.tcx.ensure_ok().check_well_formed(hir::OwnerId { def_id }) { self.error_emitted = Some(guar); } @@ -475,7 +475,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { /// Check the const stability of the given item (fn or trait). fn check_callee_stability(&mut self, def_id: DefId) { match self.tcx.lookup_const_stability(def_id) { - Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { + Some(attrs::ConstStability { level: attrs::StabilityLevel::Stable { .. }, .. }) => { // All good. } None => { @@ -491,8 +491,8 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { }); } } - Some(ConstStability { - level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, + Some(attrs::ConstStability { + level: attrs::StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, feature, .. }) => { @@ -918,8 +918,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { }); } } - Some(ConstStability { - level: StabilityLevel::Unstable { .. }, + Some(attrs::ConstStability { + level: attrs::StabilityLevel::Unstable { .. }, feature, .. }) => { @@ -930,7 +930,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { suggestion: self.crate_inject_span(), }); } - Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { + Some(attrs::ConstStability { + level: attrs::StabilityLevel::Stable { .. }, + .. + }) => { // All good. Note that a `#[rustc_const_stable]` intrinsic (meaning it // can be *directly* invoked from stable const code) does not always // have the `#[rustc_intrinsic_const_stable_indirect]` attribute (which controls diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index 607cb2e497d8d..d84214152255c 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -4,13 +4,12 @@ //! has interior mutability or needs to be dropped, as well as the visitor that emits errors when //! it finds operations that are invalid in a certain context. -use rustc_attr_parsing::{AttributeKind, find_attr}; use rustc_errors::DiagCtxtHandle; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::{self, PolyFnSig, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::Symbol; +use {rustc_attr_data_structures as attrs, rustc_hir as hir}; pub use self::qualifs::Qualif; @@ -81,9 +80,9 @@ pub fn rustc_allow_const_fn_unstable( def_id: LocalDefId, feature_gate: Symbol, ) -> bool { - let attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(def_id)); + let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); - find_attr!(attrs, AttributeKind::AllowConstFnUnstable(syms) if syms.contains(&feature_gate)) + attrs::find_attr!(attrs, attrs::AttributeKind::AllowConstFnUnstable(syms) if syms.contains(&feature_gate)) } /// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable". diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 7756e51c4c5f2..1e5b98675c4f0 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -352,7 +352,7 @@ fn build_error_for_const_call<'tcx>( ); err } - _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => { + _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::FmtArgumentsNew) => { ccx.dcx().create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind(), diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 8cee282311f03..9f7fcc509a587 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -22,17 +22,17 @@ use super::{ConstCx, Qualif, qualifs}; /// qualified immediately after it is borrowed or its address escapes. The borrow must allow for /// mutation, which includes shared borrows of places with interior mutability. The type of /// borrowed place must contain the qualif. -struct TransferFunction<'a, 'mir, 'tcx, Q> { - ccx: &'a ConstCx<'mir, 'tcx>, - state: &'a mut State, +struct TransferFunction<'mir, 'tcx, Q> { + ccx: &'mir ConstCx<'mir, 'tcx>, + state: &'mir mut State, _qualif: PhantomData, } -impl<'a, 'mir, 'tcx, Q> TransferFunction<'a, 'mir, 'tcx, Q> +impl<'mir, 'tcx, Q> TransferFunction<'mir, 'tcx, Q> where Q: Qualif, { - fn new(ccx: &'a ConstCx<'mir, 'tcx>, state: &'a mut State) -> Self { + fn new(ccx: &'mir ConstCx<'mir, 'tcx>, state: &'mir mut State) -> Self { TransferFunction { ccx, state, _qualif: PhantomData } } @@ -124,7 +124,7 @@ where } } -impl<'tcx, Q> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx, Q> +impl<'tcx, Q> Visitor<'tcx> for TransferFunction<'_, 'tcx, Q> where Q: Qualif, { @@ -228,20 +228,20 @@ where } /// The dataflow analysis used to propagate qualifs on arbitrary CFGs. -pub(super) struct FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> { - ccx: &'a ConstCx<'mir, 'tcx>, +pub(super) struct FlowSensitiveAnalysis<'mir, 'tcx, Q> { + ccx: &'mir ConstCx<'mir, 'tcx>, _qualif: PhantomData, } -impl<'a, 'mir, 'tcx, Q> FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> +impl<'mir, 'tcx, Q> FlowSensitiveAnalysis<'mir, 'tcx, Q> where Q: Qualif, { - pub(super) fn new(_: Q, ccx: &'a ConstCx<'mir, 'tcx>) -> Self { + pub(super) fn new(_: Q, ccx: &'mir ConstCx<'mir, 'tcx>) -> Self { FlowSensitiveAnalysis { ccx, _qualif: PhantomData } } - fn transfer_function(&self, state: &'a mut State) -> TransferFunction<'a, 'mir, 'tcx, Q> { + fn transfer_function(&self, state: &'mir mut State) -> TransferFunction<'mir, 'tcx, Q> { TransferFunction::::new(self.ccx, state) } } @@ -313,7 +313,7 @@ impl JoinSemiLattice for State { } } -impl<'tcx, Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q> +impl<'tcx, Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, 'tcx, Q> where Q: Qualif, { diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 4db862afd9f6d..7c7daed525b2d 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -22,7 +22,7 @@ use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, - GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, + GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, RangeSet, Scalar, compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; @@ -502,6 +502,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { RemainderByZero(op) => RemainderByZero(eval_to_int(op)?), ResumedAfterReturn(coroutine_kind) => ResumedAfterReturn(*coroutine_kind), ResumedAfterPanic(coroutine_kind) => ResumedAfterPanic(*coroutine_kind), + ResumedAfterDrop(coroutine_kind) => ResumedAfterDrop(*coroutine_kind), MisalignedPointerDereference { required, found } => MisalignedPointerDereference { required: eval_to_int(required)?, found: eval_to_int(found)?, @@ -546,7 +547,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL, hir_id, ) - .0 + .level .is_error(); let span = ecx.cur_span(); ecx.tcx.emit_node_span_lint( @@ -688,6 +689,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { _tcx: TyCtxtAt<'tcx>, _machine: &mut Self, _alloc_extra: &mut Self::AllocExtra, + _ptr: Pointer>, (_alloc_id, immutable): (AllocId, bool), range: AllocRange, ) -> InterpResult<'tcx> { diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 3776fb55c2e5f..34239ae1d1527 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -256,7 +256,7 @@ pub(crate) fn eval_to_valtree<'tcx>( Err(err) => { let did = cid.instance.def_id(); let global_const_id = cid.display(tcx); - let span = tcx.hir().span_if_local(did); + let span = tcx.hir_span_if_local(did); match err { ValTreeCreationError::NodesOverflow => { let handled = diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index b020eeccf717f..826ea0e58ecca 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -5,15 +5,14 @@ use either::Either; use rustc_abi::WrappingRange; use rustc_errors::codes::*; use rustc_errors::{ - Diag, DiagArgValue, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, Level, - MultiSpan, SubdiagMessageOp, Subdiagnostic, + Diag, DiagArgValue, DiagMessage, Diagnostic, EmissionGuarantee, Level, MultiSpan, Subdiagnostic, }; use rustc_hir::ConstContext; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::mir::interpret::{ - CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, - InvalidProgramInfo, Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, - UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, + CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, InvalidProgramInfo, + Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, + UnsupportedOpInfo, ValidationErrorInfo, }; use rustc_middle::ty::{self, Mutability, Ty}; use rustc_span::{Span, Symbol}; @@ -290,11 +289,7 @@ pub struct FrameNote { } impl Subdiagnostic for FrameNote { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("times", self.times); diag.arg("where_", self.where_); diag.arg("instance", self.instance); @@ -302,7 +297,7 @@ impl Subdiagnostic for FrameNote { if self.has_label && !self.span.is_dummy() { span.push_span_label(self.span, fluent::const_eval_frame_note_last); } - let msg = f(diag, fluent::const_eval_frame_note.into()); + let msg = diag.eagerly_translate(fluent::const_eval_frame_note); diag.span_note(span, msg); } } @@ -502,19 +497,6 @@ pub trait ReportErrorExt { } } -fn bad_pointer_message(msg: CheckInAllocMsg, dcx: DiagCtxtHandle<'_>) -> String { - use crate::fluent_generated::*; - - let msg = match msg { - CheckInAllocMsg::MemoryAccessTest => const_eval_memory_access_test, - CheckInAllocMsg::PointerArithmeticTest => const_eval_pointer_arithmetic_test, - CheckInAllocMsg::OffsetFromTest => const_eval_offset_from_test, - CheckInAllocMsg::InboundsTest => const_eval_in_bounds_test, - }; - - dcx.eagerly_translate_to_string(msg, [].into_iter()) -} - impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { fn diagnostic_message(&self) -> DiagMessage { use UndefinedBehaviorInfo::*; @@ -568,7 +550,6 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { fn add_args(self, diag: &mut Diag<'_, G>) { use UndefinedBehaviorInfo::*; - let dcx = diag.dcx; match self { Ub(_) => {} Custom(custom) => { @@ -616,12 +597,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { diag.arg("vtable_dyn_type", vtable_dyn_type.to_string()); } PointerUseAfterFree(alloc_id, msg) => { - diag.arg("alloc_id", alloc_id) - .arg("bad_pointer_message", bad_pointer_message(msg, dcx)); + diag.arg("alloc_id", alloc_id).arg("operation", format!("{:?}", msg)); } PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, inbounds_size, msg } => { diag.arg("alloc_size", alloc_size.bytes()); - diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx)); diag.arg("pointer", { let mut out = format!("{:?}", alloc_id); if ptr_offset > 0 { @@ -631,14 +610,17 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { } out }); + diag.arg("inbounds_size", inbounds_size); diag.arg("inbounds_size_is_neg", inbounds_size < 0); diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs()); + diag.arg("ptr_offset", ptr_offset); diag.arg("ptr_offset_is_neg", ptr_offset < 0); diag.arg("ptr_offset_abs", ptr_offset.unsigned_abs()); diag.arg( "alloc_size_minus_ptr_offset", alloc_size.bytes().saturating_sub(ptr_offset as u64), ); + diag.arg("operation", format!("{:?}", msg)); } DanglingIntPointer { addr, inbounds_size, msg } => { if addr != 0 { @@ -648,9 +630,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { ); } + diag.arg("inbounds_size", inbounds_size); diag.arg("inbounds_size_is_neg", inbounds_size < 0); diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs()); - diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx)); + diag.arg("operation", format!("{:?}", msg)); } AlignmentCheckFailed(Misalignment { required, has }, msg) => { diag.arg("required", required.bytes()); @@ -898,6 +881,7 @@ impl ReportErrorExt for UnsupportedOpInfo { UnsupportedOpInfo::ExternStatic(_) => const_eval_extern_static, } } + fn add_args(self, diag: &mut Diag<'_, G>) { use UnsupportedOpInfo::*; @@ -917,9 +901,9 @@ impl ReportErrorExt for UnsupportedOpInfo { OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => { diag.arg("ptr", ptr); } - ThreadLocalStatic(did) | ExternStatic(did) => { - diag.arg("did", format!("{did:?}")); - } + ThreadLocalStatic(did) | ExternStatic(did) => rustc_middle::ty::tls::with(|tcx| { + diag.arg("did", tcx.def_path_str(did)); + }), } } } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 29f819cca1fb6..405208e94f4b8 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -353,8 +353,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if caller_fn_abi.conv != callee_fn_abi.conv { throw_ub_custom!( fluent::const_eval_incompatible_calling_conventions, - callee_conv = format!("{:?}", callee_fn_abi.conv), - caller_conv = format!("{:?}", caller_fn_abi.conv), + callee_conv = format!("{}", callee_fn_abi.conv), + caller_conv = format!("{}", caller_fn_abi.conv), ) } @@ -570,6 +570,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::InstanceKind::FnPtrAddrShim(..) | ty::InstanceKind::ThreadLocalShim(..) | ty::InstanceKind::AsyncDropGlueCtorShim(..) + | ty::InstanceKind::AsyncDropGlue(..) + | ty::InstanceKind::FutureDropPollShim(..) | ty::InstanceKind::Item(_) => { // We need MIR for this fn. // Note that this can be an intrinsic, if we are executing its fallback body. diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 7b706334ed85e..643a5805019ff 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -9,7 +9,6 @@ use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, FloatTy, Ty}; use rustc_middle::{bug, span_bug}; -use rustc_type_ir::TyKind::*; use tracing::trace; use super::util::ensure_monomorphic_enough; @@ -182,9 +181,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { src: &ImmTy<'tcx, M::Provenance>, cast_to: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { - use rustc_type_ir::TyKind::*; - - let Float(fty) = src.layout.ty.kind() else { + let ty::Float(fty) = src.layout.ty.kind() else { bug!("FloatToFloat/FloatToInt cast: source type {} is not a float type", src.layout.ty) }; let val = match fty { @@ -277,19 +274,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let signed = src_layout.backend_repr.is_signed(); // Also asserts that abi is `Scalar`. let v = match src_layout.ty.kind() { - Uint(_) | RawPtr(..) | FnPtr(..) => scalar.to_uint(src_layout.size)?, - Int(_) => scalar.to_int(src_layout.size)? as u128, // we will cast back to `i128` below if the sign matters - Bool => scalar.to_bool()?.into(), - Char => scalar.to_char()?.into(), + ty::Uint(_) | ty::RawPtr(..) | ty::FnPtr(..) => scalar.to_uint(src_layout.size)?, + ty::Int(_) => scalar.to_int(src_layout.size)? as u128, // we will cast back to `i128` below if the sign matters + ty::Bool => scalar.to_bool()?.into(), + ty::Char => scalar.to_char()?.into(), _ => span_bug!(self.cur_span(), "invalid int-like cast from {}", src_layout.ty), }; interp_ok(match *cast_ty.kind() { // int -> int - Int(_) | Uint(_) => { + ty::Int(_) | ty::Uint(_) => { let size = match *cast_ty.kind() { - Int(t) => Integer::from_int_ty(self, t).size(), - Uint(t) => Integer::from_uint_ty(self, t).size(), + ty::Int(t) => Integer::from_int_ty(self, t).size(), + ty::Uint(t) => Integer::from_uint_ty(self, t).size(), _ => bug!(), }; let v = size.truncate(v); @@ -297,7 +294,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // signed int -> float - Float(fty) if signed => { + ty::Float(fty) if signed => { let v = v as i128; match fty { FloatTy::F16 => Scalar::from_f16(Half::from_i128(v).value), @@ -307,7 +304,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } // unsigned int -> float - Float(fty) => match fty { + ty::Float(fty) => match fty { FloatTy::F16 => Scalar::from_f16(Half::from_u128(v).value), FloatTy::F32 => Scalar::from_f32(Single::from_u128(v).value), FloatTy::F64 => Scalar::from_f64(Double::from_u128(v).value), @@ -315,7 +312,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }, // u8 -> char - Char => Scalar::from_u32(u8::try_from(v).unwrap().into()), + ty::Char => 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), @@ -332,11 +329,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { + FloatConvert + FloatConvert, { - use rustc_type_ir::TyKind::*; - match *dest_ty.kind() { // float -> uint - Uint(t) => { + ty::Uint(t) => { let size = Integer::from_uint_ty(self, 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). @@ -345,7 +340,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Scalar::from_uint(v, size) } // float -> int - Int(t) => { + ty::Int(t) => { let size = Integer::from_int_ty(self, 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). @@ -353,7 +348,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Scalar::from_int(v, size) } // float -> float - Float(fty) => match fty { + ty::Float(fty) => match fty { FloatTy::F16 => { Scalar::from_f16(self.adjust_nan(f.convert(&mut false).value, &[f])) } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index c1948e9f31f10..b69bc0918be82 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -268,7 +268,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Call this on things you got out of the MIR (so it is as generic as the current /// stack frame), to bring it into the proper environment for this interpreter. - pub(super) fn instantiate_from_current_frame_and_normalize_erasing_regions< + pub fn instantiate_from_current_frame_and_normalize_erasing_regions< T: TypeFoldable>, >( &self, @@ -279,9 +279,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Call this on things you got out of the MIR (so it is as generic as the provided /// stack frame), to bring it into the proper environment for this interpreter. - pub(super) fn instantiate_from_frame_and_normalize_erasing_regions< - T: TypeFoldable>, - >( + pub fn instantiate_from_frame_and_normalize_erasing_regions>>( &self, frame: &Frame<'tcx, M::Provenance, M::FrameExtra>, value: T, diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 43631330f89bf..1dd96297d1fda 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -17,19 +17,20 @@ use hir::def::DefKind; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir as hir; +use rustc_hir::definitions::{DefPathData, DisambiguatorState}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult}; use rustc_middle::query::TyCtxtAt; use rustc_middle::span_bug; use rustc_middle::ty::layout::TyAndLayout; use rustc_span::def_id::LocalDefId; -use rustc_span::sym; use tracing::{instrument, trace}; use super::{ AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub, interp_ok, }; use crate::const_eval; +use crate::const_eval::DummyMachine; use crate::errors::NestedStaticInThreadLocal; pub trait CompileTimeMachine<'tcx, T> = Machine< @@ -65,6 +66,7 @@ fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>( ecx: &mut InterpCx<'tcx, M>, alloc_id: AllocId, mutability: Mutability, + disambiguator: Option<&mut DisambiguatorState>, ) -> Result + 'tcx, ()> { trace!("intern_shallow {:?}", alloc_id); // remove allocation @@ -87,7 +89,13 @@ fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>( // link the alloc id to the actual allocation let alloc = ecx.tcx.mk_const_alloc(alloc); if let Some(static_id) = ecx.machine.static_def_id() { - intern_as_new_static(ecx.tcx, static_id, alloc_id, alloc); + intern_as_new_static( + ecx.tcx, + static_id, + alloc_id, + alloc, + disambiguator.expect("disambiguator needed"), + ); } else { ecx.tcx.set_alloc_id_memory(alloc_id, alloc); } @@ -101,11 +109,18 @@ fn intern_as_new_static<'tcx>( static_id: LocalDefId, alloc_id: AllocId, alloc: ConstAllocation<'tcx>, + disambiguator: &mut DisambiguatorState, ) { + // `intern_const_alloc_recursive` is called once per static and it contains the `DisambiguatorState`. + // The `::{{nested}}` path is thus unique to `intern_const_alloc_recursive` and the + // `DisambiguatorState` ensures the generated path is unique for this call as we generate + // `::{{nested#n}}` where `n` is the `n`th `intern_as_new_static` call. let feed = tcx.create_def( static_id, - sym::nested, + None, DefKind::Static { safety: hir::Safety::Safe, mutability: alloc.0.mutability, nested: true }, + Some(DefPathData::NestedStatic), + disambiguator, ); tcx.set_nested_alloc_id_static(alloc_id, feed.def_id()); @@ -153,6 +168,8 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval intern_kind: InternKind, ret: &MPlaceTy<'tcx>, ) -> Result<(), InternResult> { + let mut disambiguator = DisambiguatorState::new(); + // We are interning recursively, and for mutability we are distinguishing the "root" allocation // that we are starting in, and all other allocations that we are encountering recursively. let (base_mutability, inner_mutability, is_static) = match intern_kind { @@ -196,7 +213,9 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval alloc.1.mutability = base_mutability; alloc.1.provenance().ptrs().iter().map(|&(_, prov)| prov).collect() } else { - intern_shallow(ecx, base_alloc_id, base_mutability).unwrap().collect() + intern_shallow(ecx, base_alloc_id, base_mutability, Some(&mut disambiguator)) + .unwrap() + .collect() }; // We need to distinguish "has just been interned" from "was already in `tcx`", // so we track this in a separate set. @@ -290,7 +309,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval // okay with losing some potential for immutability here. This can anyway only affect // `static mut`. just_interned.insert(alloc_id); - match intern_shallow(ecx, alloc_id, inner_mutability) { + match intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator)) { Ok(nested) => todo.extend(nested), Err(()) => { ecx.tcx.dcx().delayed_bug("found dangling pointer during const interning"); @@ -312,8 +331,9 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>> return interp_ok(()); } // Move allocation to `tcx`. - if let Some(_) = - (intern_shallow(ecx, alloc_id, Mutability::Not).map_err(|()| err_ub!(DeadLocal))?).next() + if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None) + .map_err(|()| err_ub!(DeadLocal))? + .next() { // We are not doing recursive interning, so we don't currently support provenance. // (If this assertion ever triggers, we should just implement a @@ -323,20 +343,23 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>> interp_ok(()) } -impl<'tcx, M: super::intern::CompileTimeMachine<'tcx, !>> InterpCx<'tcx, M> { +impl<'tcx> InterpCx<'tcx, DummyMachine> { /// 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 interned as read-only into the global memory. pub fn intern_with_temp_alloc( &mut self, layout: TyAndLayout<'tcx>, - f: impl FnOnce(&mut InterpCx<'tcx, M>, &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, ()>, + f: impl FnOnce( + &mut InterpCx<'tcx, DummyMachine>, + &PlaceTy<'tcx, CtfeProvenance>, + ) -> InterpResult<'tcx, ()>, ) -> InterpResult<'tcx, AllocId> { // `allocate` picks a fresh AllocId that we will associate with its data below. let dest = self.allocate(layout, MemoryKind::Stack)?; f(self, &dest.clone().into())?; let alloc_id = dest.ptr().provenance.unwrap().alloc_id(); // this was just allocated, it must have provenance - for prov in intern_shallow(self, alloc_id, Mutability::Not).unwrap() { + for prov in intern_shallow(self, alloc_id, Mutability::Not, None).unwrap() { // We are not doing recursive interning, so we don't currently support provenance. // (If this assertion ever triggers, we should just implement a // proper recursive interning loop -- or just call `intern_const_alloc_recursive`. diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 4ca317e3a1e53..090b2a692cfc3 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -61,16 +61,21 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( ensure_monomorphic_enough(tcx, tp_ty)?; ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128()) } - sym::variant_count => match tp_ty.kind() { + sym::variant_count => match match tp_ty.kind() { + // Pattern types have the same number of variants as their base type. + // Even if we restrict e.g. which variants are valid, the variants are essentially just uninhabited. + // And `Result<(), !>` still has two variants according to `variant_count`. + ty::Pat(base, _) => *base, + _ => tp_ty, + } + .kind() + { // Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough. ty::Adt(adt, _) => ConstValue::from_target_usize(adt.variants().len() as u64, &tcx), ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => { throw_inval!(TooGeneric) } - ty::Pat(_, pat) => match **pat { - ty::PatternKind::Range { .. } => ConstValue::from_target_usize(0u64, &tcx), - // Future pattern kinds may have more variants - }, + ty::Pat(..) => unreachable!(), ty::Bound(_, _) => bug!("bound ty during ctfe"), ty::Bool | ty::Char @@ -158,6 +163,30 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.copy_op(&val, dest)?; } + sym::fadd_algebraic + | sym::fsub_algebraic + | sym::fmul_algebraic + | sym::fdiv_algebraic + | sym::frem_algebraic => { + let a = self.read_immediate(&args[0])?; + let b = self.read_immediate(&args[1])?; + + let op = match intrinsic_name { + sym::fadd_algebraic => BinOp::Add, + sym::fsub_algebraic => BinOp::Sub, + sym::fmul_algebraic => BinOp::Mul, + sym::fdiv_algebraic => BinOp::Div, + sym::frem_algebraic => BinOp::Rem, + + _ => bug!(), + }; + + let res = self.binary_op(op, &a, &b)?; + // `binary_op` already called `generate_nan` if needed. + let res = M::apply_float_nondet(self, res)?; + self.write_immediate(*res, dest)?; + } + sym::ctpop | sym::cttz | sym::cttz_nonzero @@ -319,7 +348,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Check that the memory between them is dereferenceable at all, starting from the // origin pointer: `dist` is `a - b`, so it is based on `b`. - self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest) + self.check_ptr_access_signed(b, dist, CheckInAllocMsg::Dereferenceable) .map_err_kind(|_| { // This could mean they point to different allocations, or they point to the same allocation // but not the entire range between the pointers is in-bounds. @@ -343,7 +372,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.check_ptr_access_signed( a, dist.checked_neg().unwrap(), // i64::MIN is impossible as no allocation can be that large - CheckInAllocMsg::OffsetFromTest, + CheckInAllocMsg::Dereferenceable, ) .map_err_kind(|_| { // Make the error more specific. @@ -464,11 +493,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::minnumf64 => self.float_min_intrinsic::(args, dest)?, sym::minnumf128 => self.float_min_intrinsic::(args, dest)?, + sym::minimumf16 => self.float_minimum_intrinsic::(args, dest)?, + sym::minimumf32 => self.float_minimum_intrinsic::(args, dest)?, + sym::minimumf64 => self.float_minimum_intrinsic::(args, dest)?, + sym::minimumf128 => self.float_minimum_intrinsic::(args, dest)?, + sym::maxnumf16 => self.float_max_intrinsic::(args, dest)?, sym::maxnumf32 => self.float_max_intrinsic::(args, dest)?, sym::maxnumf64 => self.float_max_intrinsic::(args, dest)?, sym::maxnumf128 => self.float_max_intrinsic::(args, dest)?, + sym::maximumf16 => self.float_maximum_intrinsic::(args, dest)?, + sym::maximumf32 => self.float_maximum_intrinsic::(args, dest)?, + sym::maximumf64 => self.float_maximum_intrinsic::(args, dest)?, + sym::maximumf128 => self.float_maximum_intrinsic::(args, dest)?, + sym::copysignf16 => self.float_copysign_intrinsic::(args, dest)?, sym::copysignf32 => self.float_copysign_intrinsic::(args, dest)?, sym::copysignf64 => self.float_copysign_intrinsic::(args, dest)?, @@ -622,7 +661,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { offset_bytes: i64, ) -> InterpResult<'tcx, Pointer>> { // The offset must be in bounds starting from `ptr`. - self.check_ptr_access_signed(ptr, offset_bytes, CheckInAllocMsg::PointerArithmeticTest)?; + self.check_ptr_access_signed( + ptr, + offset_bytes, + CheckInAllocMsg::InboundsPointerArithmetic, + )?; // This also implies that there is no overflow, so we are done. interp_ok(ptr.wrapping_signed_offset(offset_bytes, self)) } @@ -797,6 +840,38 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(()) } + fn float_minimum_intrinsic( + &mut self, + args: &[OpTy<'tcx, M::Provenance>], + dest: &MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ()> + where + F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, + { + let a: F = self.read_scalar(&args[0])?.to_float()?; + let b: F = self.read_scalar(&args[1])?.to_float()?; + let res = a.minimum(b); + let res = self.adjust_nan(res, &[a, b]); + self.write_scalar(res, dest)?; + interp_ok(()) + } + + fn float_maximum_intrinsic( + &mut self, + args: &[OpTy<'tcx, M::Provenance>], + dest: &MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ()> + where + F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, + { + let a: F = self.read_scalar(&args[0])?.to_float()?; + let b: F = self.read_scalar(&args[1])?.to_float()?; + let res = a.maximum(b); + let res = self.adjust_nan(res, &[a, b]); + self.write_scalar(res, dest)?; + interp_ok(()) + } + fn float_copysign_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 1a799f5dea5b5..d13e17a481a46 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -8,7 +8,6 @@ use std::hash::Hash; use rustc_abi::{Align, Size}; use rustc_apfloat::{Float, FloatConvert}; -use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::TyAndLayout; @@ -21,7 +20,6 @@ use super::{ AllocBytes, AllocId, AllocKind, AllocRange, Allocation, CTFE_ALLOC_SALT, ConstAllocation, CtfeProvenance, FnArg, Frame, ImmTy, InterpCx, InterpResult, MPlaceTy, MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, interp_ok, throw_unsup, - throw_unsup_format, }; /// Data returned by [`Machine::after_stack_pop`], and consumed by @@ -149,6 +147,12 @@ pub trait Machine<'tcx>: Sized { /// already been checked before. const ALL_CONSTS_ARE_PRECHECKED: bool = true; + /// Determines whether rustc_const_eval functions that make use of the [Machine] should make + /// tracing calls (to the `tracing` library). By default this is `false`, meaning the tracing + /// calls will supposedly be optimized out. This flag is set to `true` inside Miri, to allow + /// tracing the interpretation steps, among other things. + const TRACING_ENABLED: bool = false; + /// Whether memory accesses should be alignment-checked. fn enforce_alignment(ecx: &InterpCx<'tcx, Self>) -> bool; @@ -278,6 +282,14 @@ pub trait Machine<'tcx>: Sized { F2::NAN } + /// Apply non-determinism to float operations that do not return a precise result. + fn apply_float_nondet( + _ecx: &mut InterpCx<'tcx, Self>, + val: ImmTy<'tcx, Self::Provenance>, + ) -> InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> { + interp_ok(val) + } + /// Determines the result of `min`/`max` on floats when the arguments are equal. fn equal_float_min_max(_ecx: &InterpCx<'tcx, Self>, a: F, _b: F) -> F { // By default, we pick the left argument. @@ -361,6 +373,19 @@ pub trait Machine<'tcx>: Sized { size: i64, ) -> Option<(AllocId, Size, Self::ProvenanceExtra)>; + /// Return a "root" pointer for the given allocation: the one that is used for direct + /// accesses to this static/const/fn allocation, or the one returned from the heap allocator. + /// + /// Not called on `extern` or thread-local statics (those use the methods above). + /// + /// `kind` is the kind of the allocation the pointer points to; it can be `None` when + /// it's a global and `GLOBAL_KIND` is `None`. + fn adjust_alloc_root_pointer( + ecx: &InterpCx<'tcx, Self>, + ptr: Pointer, + kind: Option>, + ) -> InterpResult<'tcx, Pointer>; + /// Called to adjust global allocations to the Provenance and AllocExtra of this machine. /// /// If `alloc` contains pointers, then they are all pointing to globals. @@ -375,11 +400,12 @@ pub trait Machine<'tcx>: Sized { alloc: &'b Allocation, ) -> InterpResult<'tcx, Cow<'b, Allocation>>; - /// Initialize the extra state of an allocation. + /// Initialize the extra state of an allocation local to this machine. /// - /// This is guaranteed to be called exactly once on all allocations that are accessed by the - /// program. - fn init_alloc_extra( + /// This is guaranteed to be called exactly once on all allocations local to this machine. + /// It will not be called automatically for global allocations; `adjust_global_allocation` + /// has to do that itself if that is desired. + fn init_local_allocation( ecx: &InterpCx<'tcx, Self>, id: AllocId, kind: MemoryKind, @@ -387,35 +413,9 @@ pub trait Machine<'tcx>: Sized { align: Align, ) -> InterpResult<'tcx, Self::AllocExtra>; - /// Return a "root" pointer for the given allocation: the one that is used for direct - /// accesses to this static/const/fn allocation, or the one returned from the heap allocator. - /// - /// Not called on `extern` or thread-local statics (those use the methods above). - /// - /// `kind` is the kind of the allocation the pointer points to; it can be `None` when - /// it's a global and `GLOBAL_KIND` is `None`. - fn adjust_alloc_root_pointer( - ecx: &InterpCx<'tcx, Self>, - ptr: Pointer, - kind: Option>, - ) -> InterpResult<'tcx, Pointer>; - - /// Evaluate the inline assembly. - /// - /// This should take care of jumping to the next block (one of `targets`) when asm goto - /// is triggered, `targets[0]` when the assembly falls through, or diverge in case of - /// naked_asm! or `InlineAsmOptions::NORETURN` being set. - fn eval_inline_asm( - _ecx: &mut InterpCx<'tcx, Self>, - _template: &'tcx [InlineAsmTemplatePiece], - _operands: &[mir::InlineAsmOperand<'tcx>], - _options: InlineAsmOptions, - _targets: &[mir::BasicBlock], - ) -> InterpResult<'tcx> { - throw_unsup_format!("inline assembly is not supported") - } - /// Hook for performing extra checks on a memory read access. + /// `ptr` will always be a pointer with the provenance in `prov` pointing to the beginning of + /// `range`. /// /// This will *not* be called during validation! /// @@ -429,6 +429,7 @@ pub trait Machine<'tcx>: Sized { _tcx: TyCtxtAt<'tcx>, _machine: &Self, _alloc_extra: &Self::AllocExtra, + _ptr: Pointer>, _prov: (AllocId, Self::ProvenanceExtra), _range: AllocRange, ) -> InterpResult<'tcx> { @@ -448,11 +449,14 @@ pub trait Machine<'tcx>: Sized { /// Hook for performing extra checks on a memory write access. /// This is not invoked for ZST accesses, as no write actually happens. + /// `ptr` will always be a pointer with the provenance in `prov` pointing to the beginning of + /// `range`. #[inline(always)] fn before_memory_write( _tcx: TyCtxtAt<'tcx>, _machine: &mut Self, _alloc_extra: &mut Self::AllocExtra, + _ptr: Pointer>, _prov: (AllocId, Self::ProvenanceExtra), _range: AllocRange, ) -> InterpResult<'tcx> { @@ -460,11 +464,14 @@ pub trait Machine<'tcx>: Sized { } /// Hook for performing extra operations on a memory deallocation. + /// `ptr` will always be a pointer with the provenance in `prov` pointing to the beginning of + /// the allocation. #[inline(always)] fn before_memory_deallocation( _tcx: TyCtxtAt<'tcx>, _machine: &mut Self, _alloc_extra: &mut Self::AllocExtra, + _ptr: Pointer>, _prov: (AllocId, Self::ProvenanceExtra), _size: Size, _align: Align, @@ -699,7 +706,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { interp_ok(Cow::Borrowed(alloc)) } - fn init_alloc_extra( + fn init_local_allocation( _ecx: &InterpCx<$tcx, Self>, _id: AllocId, _kind: MemoryKind, diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 4f4b6785844dc..43bf48a9b9612 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -8,8 +8,9 @@ use std::assert_matches::assert_matches; use std::borrow::{Borrow, Cow}; +use std::cell::Cell; use std::collections::VecDeque; -use std::{fmt, mem, ptr}; +use std::{fmt, ptr}; use rustc_abi::{Align, HasDataLayout, Size}; use rustc_ast::Mutability; @@ -131,7 +132,7 @@ pub struct Memory<'tcx, M: Machine<'tcx>> { /// This stores whether we are currently doing reads purely for the purpose of validation. /// Those reads do not trigger the machine's hooks for memory reads. /// Needless to say, this must only be set with great care! - validation_in_progress: bool, + validation_in_progress: Cell, } /// A reference to some allocation that was already bounds-checked for the given region @@ -158,7 +159,7 @@ impl<'tcx, M: Machine<'tcx>> Memory<'tcx, M> { alloc_map: M::MemoryMap::default(), extra_fn_ptr_map: FxIndexMap::default(), dead_alloc_map: FxIndexMap::default(), - validation_in_progress: false, + validation_in_progress: Cell::new(false), } } @@ -263,9 +264,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { M::GLOBAL_KIND.map(MemoryKind::Machine), "dynamically allocating global memory" ); - // We have set things up so we don't need to call `adjust_from_tcx` here, - // so we avoid copying the entire allocation contents. - let extra = M::init_alloc_extra(self, id, kind, alloc.size(), alloc.align)?; + // This cannot be merged with the `adjust_global_allocation` code path + // since here we have an allocation that already uses `M::Bytes`. + let extra = M::init_local_allocation(self, id, kind, alloc.size(), alloc.align)?; let alloc = alloc.with_extra(extra); self.memory.alloc_map.insert(id, (kind, alloc)); M::adjust_alloc_root_pointer(self, Pointer::from(id), Some(kind)) @@ -350,7 +351,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { kind = "static_mem" ) } - None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccessTest)), + None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccess)), }) .into(); }; @@ -385,6 +386,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.tcx, &mut self.machine, &mut alloc.extra, + ptr, (alloc_id, prov), size, alloc.align, @@ -412,10 +414,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self, ptr, size, - CheckInAllocMsg::MemoryAccessTest, + CheckInAllocMsg::MemoryAccess, |this, alloc_id, offset, prov| { - let (size, align) = this - .get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccessTest)?; + let (size, align) = + this.get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccess)?; interp_ok((size, align, (alloc_id, offset, prov))) }, ) @@ -611,7 +613,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)), Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)), - None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccessTest)), + None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)), Some(GlobalAlloc::Static(def_id)) => { assert!(self.tcx.is_static(def_id)); // Thread-local statics do not have a constant address. They *must* be accessed via @@ -705,7 +707,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self, ptr, size_i64, - CheckInAllocMsg::MemoryAccessTest, + CheckInAllocMsg::MemoryAccess, |this, alloc_id, offset, prov| { let alloc = this.get_alloc_raw(alloc_id)?; interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc))) @@ -714,7 +716,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized // accesses. That means we cannot rely on the closure above or the `Some` branch below. We // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked. - if !self.memory.validation_in_progress { + if !self.memory.validation_in_progress.get() { if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr, size_i64) { M::before_alloc_read(self, alloc_id)?; } @@ -722,11 +724,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc { let range = alloc_range(offset, size); - if !self.memory.validation_in_progress { + if !self.memory.validation_in_progress.get() { M::before_memory_read( self.tcx, &self.machine, &alloc.extra, + ptr, (alloc_id, prov), range, )?; @@ -799,14 +802,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, Option>> { let tcx = self.tcx; - let validation_in_progress = self.memory.validation_in_progress; + let validation_in_progress = self.memory.validation_in_progress.get(); let size_i64 = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes let ptr_and_alloc = Self::check_and_deref_ptr( self, ptr, size_i64, - CheckInAllocMsg::MemoryAccessTest, + CheckInAllocMsg::MemoryAccess, |this, alloc_id, offset, prov| { let (alloc, machine) = this.get_alloc_raw_mut(alloc_id)?; interp_ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc, machine))) @@ -816,7 +819,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some((alloc_id, offset, prov, alloc, machine)) = ptr_and_alloc { let range = alloc_range(offset, size); if !validation_in_progress { - M::before_memory_write(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?; + M::before_memory_write( + tcx, + machine, + &mut alloc.extra, + ptr, + (alloc_id, prov), + range, + )?; } interp_ok(Some(AllocRefMut { alloc, range, tcx: *tcx, alloc_id })) } else { @@ -862,8 +872,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // # Function pointers // (both global from `alloc_map` and local from `extra_fn_ptr_map`) - if self.get_fn_alloc(id).is_some() { - return AllocInfo::new(Size::ZERO, Align::ONE, AllocKind::Function, Mutability::Not); + if let Some(fn_val) = self.get_fn_alloc(id) { + let align = match fn_val { + FnVal::Instance(instance) => { + // Function alignment can be set globally with the `-Zmin-function-alignment=` flag; + // the alignment from a `#[repr(align())]` is used if it specifies a higher alignment. + let fn_align = self.tcx.codegen_fn_attrs(instance.def_id()).alignment; + let global_align = self.tcx.sess.opts.unstable_opts.min_function_alignment; + + Ord::max(global_align, fn_align).unwrap_or(Align::ONE) + } + // Machine-specific extra functions currently do not support alignment restrictions. + FnVal::Other(_) => Align::ONE, + }; + + return AllocInfo::new(Size::ZERO, align, AllocKind::Function, Mutability::Not); } // # Global allocations @@ -955,18 +978,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Handle the effect an FFI call might have on the state of allocations. /// This overapproximates the modifications which external code might make to memory: - /// We set all reachable allocations as initialized, mark all provenances as exposed + /// We set all reachable allocations as initialized, mark all reachable provenances as exposed /// and overwrite them with `Provenance::WILDCARD`. - pub fn prepare_for_native_call( - &mut self, - id: AllocId, - initial_prov: M::Provenance, - ) -> InterpResult<'tcx> { - // Expose provenance of the root allocation. - M::expose_provenance(self, initial_prov)?; - + /// + /// The allocations in `ids` are assumed to be already exposed. + pub fn prepare_for_native_call(&mut self, ids: Vec) -> InterpResult<'tcx> { let mut done = FxHashSet::default(); - let mut todo = vec![id]; + let mut todo = ids; while let Some(id) = todo.pop() { if !done.insert(id) { // We already saw this allocation before, don't process it again. @@ -987,6 +1005,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { todo.push(id); } } + // Also expose the provenance of the interpreter-level allocation, so it can + // be read by FFI. The `black_box` is defensive programming as LLVM likes + // to (incorrectly) optimize away ptr2int casts whose result is unused. + std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance()); // Prepare for possible write from native code if mutable. if info.mutbl.is_mut() { @@ -1079,23 +1101,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// /// We do this so Miri's allocation access tracking does not show the validation /// reads as spurious accesses. - pub fn run_for_validation(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + pub fn run_for_validation_mut(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + // This deliberately uses `==` on `bool` to follow the pattern + // `assert!(val.replace(new) == old)`. + assert!( + self.memory.validation_in_progress.replace(true) == false, + "`validation_in_progress` was already set" + ); + let res = f(self); + assert!( + self.memory.validation_in_progress.replace(false) == true, + "`validation_in_progress` was unset by someone else" + ); + res + } + + /// Runs the closure in "validation" mode, which means the machine's memory read hooks will be + /// suppressed. Needless to say, this must only be set with great care! Cannot be nested. + /// + /// We do this so Miri's allocation access tracking does not show the validation + /// reads as spurious accesses. + pub fn run_for_validation_ref(&self, f: impl FnOnce(&Self) -> R) -> R { // This deliberately uses `==` on `bool` to follow the pattern // `assert!(val.replace(new) == old)`. assert!( - mem::replace(&mut self.memory.validation_in_progress, true) == false, + self.memory.validation_in_progress.replace(true) == false, "`validation_in_progress` was already set" ); let res = f(self); assert!( - mem::replace(&mut self.memory.validation_in_progress, false) == true, + self.memory.validation_in_progress.replace(false) == true, "`validation_in_progress` was unset by someone else" ); res } pub(super) fn validation_in_progress(&self) -> bool { - self.memory.validation_in_progress + self.memory.validation_in_progress.get() } } @@ -1367,13 +1409,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; let src_alloc = self.get_alloc_raw(src_alloc_id)?; let src_range = alloc_range(src_offset, size); - assert!(!self.memory.validation_in_progress, "we can't be copying during validation"); + assert!(!self.memory.validation_in_progress.get(), "we can't be copying during validation"); // For the overlapping case, it is crucial that we trigger the read hook // before the write hook -- the aliasing model cares about the order. M::before_memory_read( tcx, &self.machine, &src_alloc.extra, + src, (src_alloc_id, src_prov), src_range, )?; @@ -1404,6 +1447,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { tcx, extra, &mut dest_alloc.extra, + dest, (dest_alloc_id, dest_prov), dest_range, )?; @@ -1571,7 +1615,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { err_ub!(DanglingIntPointer { addr: offset, inbounds_size: size, - msg: CheckInAllocMsg::InboundsTest + msg: CheckInAllocMsg::Dereferenceable }) }) .into() diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 6a17da61c8b7b..363ceee1970ee 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -14,7 +14,7 @@ use tracing::{info, instrument, trace}; use super::{ FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, - Projectable, Scalar, interp_ok, throw_ub, + Projectable, Scalar, interp_ok, throw_ub, throw_unsup_format, }; use crate::util; @@ -539,7 +539,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - Drop { place, target, unwind, replace: _ } => { + Drop { place, target, unwind, replace: _, drop, async_fut } => { + assert!( + async_fut.is_none() && drop.is_none(), + "Async Drop must be expanded or reset to sync in runtime MIR" + ); let place = self.eval_place(place)?; let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); if let ty::InstanceKind::DropGlue(_, None) = instance.def { @@ -590,8 +594,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { terminator.kind ), - InlineAsm { template, ref operands, options, ref targets, .. } => { - M::eval_inline_asm(self, template, operands, options, targets)?; + InlineAsm { .. } => { + throw_unsup_format!("inline assembly is not supported"); } } diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index ba579e25f036a..847905e834310 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -45,3 +45,22 @@ pub(crate) fn create_static_alloc<'tcx>( assert!(ecx.memory.alloc_map.insert(alloc_id, (MemoryKind::Stack, alloc)).is_none()); interp_ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout)) } + +/// This struct is needed to enforce `#[must_use]` on [tracing::span::EnteredSpan] +/// while wrapping them in an `Option`. +#[must_use] +pub enum MaybeEnteredSpan { + Some(tracing::span::EnteredSpan), + None, +} + +#[macro_export] +macro_rules! enter_trace_span { + ($machine:ident, $($tt:tt)*) => { + if $machine::TRACING_ENABLED { + $crate::interpret::tracing_utils::MaybeEnteredSpan::Some(tracing::info_span!($($tt)*).entered()) + } else { + $crate::interpret::tracing_utils::MaybeEnteredSpan::None + } + } +} diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index eb3f552cd2787..8f39afa642aef 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -510,7 +510,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.ecx.check_ptr_access( place.ptr(), size, - CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message + CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message ), self.path, Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind }, @@ -1248,6 +1248,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // Range patterns are precisely reflected into `valid_range` and thus // handled fully by `visit_scalar` (called below). ty::PatternKind::Range { .. } => {}, + + // FIXME(pattern_types): check that the value is covered by one of the variants. + // For now, we rely on layout computation setting the scalar's `valid_range` to + // match the pattern. However, this cannot always work; the layout may + // pessimistically cover actually illegal ranges and Miri would miss that UB. + // The consolation here is that codegen also will miss that UB, so at least + // we won't see optimizations actually breaking such programs. + ty::PatternKind::Or(_patterns) => {} } } _ => { @@ -1322,7 +1330,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("validate_operand_internal: {:?}, {:?}", *val, val.layout.ty); // Run the visitor. - self.run_for_validation(|ecx| { + self.run_for_validation_mut(|ecx| { let reset_padding = reset_provenance_and_padding && { // Check if `val` is actually stored in memory. If not, padding is not even // represented and we need not reset it. diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index ed5489652fba6..bf7a79dcb20f0 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -6,7 +6,6 @@ #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(slice_ptr_get)] @@ -16,7 +15,6 @@ #![feature(unqualified_local_imports)] #![feature(yeet_expr)] #![warn(unqualified_local_imports)] -#![warn(unreachable_pub)] // tidy-alphabetical-end pub mod check_consts; diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index e14cd603c5828..30e96ae414359 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -56,7 +56,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { | ty::Coroutine(def_id, args) => self.print_def_path(def_id, args), ty::Foreign(def_id) => self.print_def_path(def_id, &[]), - ty::Alias(ty::Weak, _) => bug!("type_name: unexpected weak projection"), + ty::Alias(ty::Free, _) => bug!("type_name: unexpected free alias"), ty::Alias(ty::Inherent, _) => bug!("type_name: unexpected inherent projection"), ty::CoroutineWitness(..) => bug!("type_name: unexpected `CoroutineWitness`"), } diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index bdf5494f2107b..f6a0201161851 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -12,9 +12,9 @@ elsa = "1.11.0" ena = "0.14.3" indexmap = "2.4.0" jobserver_crate = { version = "0.1.28", package = "jobserver" } -measureme = "11" +measureme = "12.0.1" rustc-hash = "2.0.0" -rustc-rayon = { version = "0.5.1", features = ["indexmap"] } +rustc-rayon-core = { version = "0.5.0" } rustc-stable-hash = { version = "0.1.0", features = ["nightly"] } rustc_arena = { path = "../rustc_arena" } rustc_graphviz = { path = "../rustc_graphviz" } @@ -29,11 +29,16 @@ thin-vec = "0.2.12" tracing = "0.1" # tidy-alphabetical-end +[dependencies.hashbrown] +version = "0.15.2" +default-features = false +features = ["nightly"] # for may_dangle + [dependencies.parking_lot] version = "0.12" [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_Storage_FileSystem", diff --git a/compiler/rustc_data_structures/src/aligned.rs b/compiler/rustc_data_structures/src/aligned.rs index 0e5ecfd9bff6e..a636d09fcae23 100644 --- a/compiler/rustc_data_structures/src/aligned.rs +++ b/compiler/rustc_data_structures/src/aligned.rs @@ -2,10 +2,8 @@ use std::ptr::Alignment; /// Returns the ABI-required minimum alignment of a type in bytes. /// -/// This is equivalent to [`mem::align_of`], but also works for some unsized +/// This is equivalent to [`align_of`], but also works for some unsized /// types (e.g. slices or rustc's `List`s). -/// -/// [`mem::align_of`]: std::mem::align_of pub const fn align_of() -> Alignment { T::ALIGN } @@ -15,10 +13,10 @@ pub const fn align_of() -> Alignment { /// # Safety /// /// `Self::ALIGN` must be equal to the alignment of `Self`. For sized types it -/// is [`mem::align_of()`], for unsized types it depends on the type, for +/// is [`align_of::()`], for unsized types it depends on the type, for /// example `[T]` has alignment of `T`. /// -/// [`mem::align_of()`]: std::mem::align_of +/// [`align_of::()`]: align_of pub unsafe trait Aligned { /// Alignment of `Self`. const ALIGN: Alignment; diff --git a/compiler/rustc_data_structures/src/captures.rs b/compiler/rustc_data_structures/src/captures.rs deleted file mode 100644 index 677ccb31454ea..0000000000000 --- a/compiler/rustc_data_structures/src/captures.rs +++ /dev/null @@ -1,8 +0,0 @@ -/// "Signaling" trait used in impl trait to tag lifetimes that you may -/// need to capture but don't really need for other reasons. -/// Basically a workaround; see [this comment] for details. -/// -/// [this comment]: https://github.com/rust-lang/rust/issues/34511#issuecomment-373423999 -pub trait Captures<'a> {} - -impl<'a, T: ?Sized> Captures<'a> for T {} diff --git a/compiler/rustc_data_structures/src/fx.rs b/compiler/rustc_data_structures/src/fx.rs index 80e72250470c0..f0db9623b674e 100644 --- a/compiler/rustc_data_structures/src/fx.rs +++ b/compiler/rustc_data_structures/src/fx.rs @@ -9,6 +9,8 @@ pub type FxIndexSet = indexmap::IndexSet>; pub type IndexEntry<'a, K, V> = indexmap::map::Entry<'a, K, V>; pub type IndexOccupiedEntry<'a, K, V> = indexmap::map::OccupiedEntry<'a, K, V>; +pub use indexmap::set::MutableValues; + #[macro_export] macro_rules! define_id_collections { ($map_name:ident, $set_name:ident, $entry_name:ident, $key:ty) => { diff --git a/compiler/rustc_data_structures/src/graph/implementation/mod.rs b/compiler/rustc_data_structures/src/graph/implementation/mod.rs deleted file mode 100644 index a80365938b96c..0000000000000 --- a/compiler/rustc_data_structures/src/graph/implementation/mod.rs +++ /dev/null @@ -1,347 +0,0 @@ -//! A graph module for use in dataflow, region resolution, and elsewhere. -//! -//! # Interface details -//! -//! You customize the graph by specifying a "node data" type `N` and an -//! "edge data" type `E`. You can then later gain access (mutable or -//! immutable) to these "user-data" bits. Currently, you can only add -//! nodes or edges to the graph. You cannot remove or modify them once -//! added. This could be changed if we have a need. -//! -//! # Implementation details -//! -//! The main tricky thing about this code is the way that edges are -//! stored. The edges are stored in a central array, but they are also -//! threaded onto two linked lists for each node, one for incoming edges -//! and one for outgoing edges. Note that every edge is a member of some -//! incoming list and some outgoing list. Basically you can load the -//! first index of the linked list from the node data structures (the -//! field `first_edge`) and then, for each edge, load the next index from -//! the field `next_edge`). Each of those fields is an array that should -//! be indexed by the direction (see the type `Direction`). - -use std::fmt::Debug; - -use rustc_index::bit_set::DenseBitSet; -use tracing::debug; - -#[cfg(test)] -mod tests; - -pub struct Graph { - nodes: Vec>, - edges: Vec>, -} - -pub struct Node { - first_edge: [EdgeIndex; 2], // see module comment - pub data: N, -} - -#[derive(Debug)] -pub struct Edge { - next_edge: [EdgeIndex; 2], // see module comment - source: NodeIndex, - target: NodeIndex, - pub data: E, -} - -#[derive(Copy, Clone, PartialEq, Debug)] -pub struct NodeIndex(pub usize); - -#[derive(Copy, Clone, PartialEq, Debug)] -pub struct EdgeIndex(pub usize); - -pub const INVALID_EDGE_INDEX: EdgeIndex = EdgeIndex(usize::MAX); - -// Use a private field here to guarantee no more instances are created: -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct Direction { - repr: usize, -} - -pub const OUTGOING: Direction = Direction { repr: 0 }; - -pub const INCOMING: Direction = Direction { repr: 1 }; - -impl NodeIndex { - /// Returns unique ID (unique with respect to the graph holding associated node). - pub fn node_id(self) -> usize { - self.0 - } -} - -impl Graph { - pub fn new() -> Graph { - Graph { nodes: Vec::new(), edges: Vec::new() } - } - - pub fn with_capacity(nodes: usize, edges: usize) -> Graph { - Graph { nodes: Vec::with_capacity(nodes), edges: Vec::with_capacity(edges) } - } - - // # Simple accessors - - #[inline] - pub fn all_nodes(&self) -> &[Node] { - &self.nodes - } - - #[inline] - pub fn len_nodes(&self) -> usize { - self.nodes.len() - } - - #[inline] - pub fn all_edges(&self) -> &[Edge] { - &self.edges - } - - #[inline] - pub fn len_edges(&self) -> usize { - self.edges.len() - } - - // # Node construction - - pub fn next_node_index(&self) -> NodeIndex { - NodeIndex(self.nodes.len()) - } - - pub fn add_node(&mut self, data: N) -> NodeIndex { - let idx = self.next_node_index(); - self.nodes.push(Node { first_edge: [INVALID_EDGE_INDEX, INVALID_EDGE_INDEX], data }); - idx - } - - pub fn mut_node_data(&mut self, idx: NodeIndex) -> &mut N { - &mut self.nodes[idx.0].data - } - - pub fn node_data(&self, idx: NodeIndex) -> &N { - &self.nodes[idx.0].data - } - - pub fn node(&self, idx: NodeIndex) -> &Node { - &self.nodes[idx.0] - } - - // # Edge construction and queries - - pub fn next_edge_index(&self) -> EdgeIndex { - EdgeIndex(self.edges.len()) - } - - pub fn add_edge(&mut self, source: NodeIndex, target: NodeIndex, data: E) -> EdgeIndex { - debug!("graph: add_edge({:?}, {:?}, {:?})", source, target, data); - - let idx = self.next_edge_index(); - - // read current first of the list of edges from each node - let source_first = self.nodes[source.0].first_edge[OUTGOING.repr]; - let target_first = self.nodes[target.0].first_edge[INCOMING.repr]; - - // create the new edge, with the previous firsts from each node - // as the next pointers - self.edges.push(Edge { next_edge: [source_first, target_first], source, target, data }); - - // adjust the firsts for each node target be the next object. - self.nodes[source.0].first_edge[OUTGOING.repr] = idx; - self.nodes[target.0].first_edge[INCOMING.repr] = idx; - - idx - } - - pub fn edge(&self, idx: EdgeIndex) -> &Edge { - &self.edges[idx.0] - } - - // # Iterating over nodes, edges - - pub fn enumerated_nodes(&self) -> impl Iterator)> { - self.nodes.iter().enumerate().map(|(idx, n)| (NodeIndex(idx), n)) - } - - pub fn enumerated_edges(&self) -> impl Iterator)> { - self.edges.iter().enumerate().map(|(idx, e)| (EdgeIndex(idx), e)) - } - - pub fn each_node<'a>(&'a self, mut f: impl FnMut(NodeIndex, &'a Node) -> bool) -> bool { - //! Iterates over all edges defined in the graph. - self.enumerated_nodes().all(|(node_idx, node)| f(node_idx, node)) - } - - pub fn each_edge<'a>(&'a self, mut f: impl FnMut(EdgeIndex, &'a Edge) -> bool) -> bool { - //! Iterates over all edges defined in the graph - self.enumerated_edges().all(|(edge_idx, edge)| f(edge_idx, edge)) - } - - pub fn outgoing_edges(&self, source: NodeIndex) -> AdjacentEdges<'_, N, E> { - self.adjacent_edges(source, OUTGOING) - } - - pub fn incoming_edges(&self, source: NodeIndex) -> AdjacentEdges<'_, N, E> { - self.adjacent_edges(source, INCOMING) - } - - pub fn adjacent_edges( - &self, - source: NodeIndex, - direction: Direction, - ) -> AdjacentEdges<'_, N, E> { - let first_edge = self.node(source).first_edge[direction.repr]; - AdjacentEdges { graph: self, direction, next: first_edge } - } - - pub fn successor_nodes(&self, source: NodeIndex) -> impl Iterator { - self.outgoing_edges(source).targets() - } - - pub fn predecessor_nodes(&self, target: NodeIndex) -> impl Iterator { - self.incoming_edges(target).sources() - } - - pub fn depth_traverse( - &self, - start: NodeIndex, - direction: Direction, - ) -> DepthFirstTraversal<'_, N, E> { - DepthFirstTraversal::with_start_node(self, start, direction) - } - - pub fn nodes_in_postorder( - &self, - direction: Direction, - entry_node: NodeIndex, - ) -> Vec { - let mut visited = DenseBitSet::new_empty(self.len_nodes()); - let mut stack = vec![]; - let mut result = Vec::with_capacity(self.len_nodes()); - let mut push_node = |stack: &mut Vec<_>, node: NodeIndex| { - if visited.insert(node.0) { - stack.push((node, self.adjacent_edges(node, direction))); - } - }; - - for node in - Some(entry_node).into_iter().chain(self.enumerated_nodes().map(|(node, _)| node)) - { - push_node(&mut stack, node); - while let Some((node, mut iter)) = stack.pop() { - if let Some((_, child)) = iter.next() { - let target = child.source_or_target(direction); - // the current node needs more processing, so - // add it back to the stack - stack.push((node, iter)); - // and then push the new node - push_node(&mut stack, target); - } else { - result.push(node); - } - } - } - - assert_eq!(result.len(), self.len_nodes()); - result - } -} - -// # Iterators - -pub struct AdjacentEdges<'g, N, E> { - graph: &'g Graph, - direction: Direction, - next: EdgeIndex, -} - -impl<'g, N: Debug, E: Debug> AdjacentEdges<'g, N, E> { - fn targets(self) -> impl Iterator { - self.map(|(_, edge)| edge.target) - } - - fn sources(self) -> impl Iterator { - self.map(|(_, edge)| edge.source) - } -} - -impl<'g, N: Debug, E: Debug> Iterator for AdjacentEdges<'g, N, E> { - type Item = (EdgeIndex, &'g Edge); - - fn next(&mut self) -> Option<(EdgeIndex, &'g Edge)> { - let edge_index = self.next; - if edge_index == INVALID_EDGE_INDEX { - return None; - } - - let edge = self.graph.edge(edge_index); - self.next = edge.next_edge[self.direction.repr]; - Some((edge_index, edge)) - } - - fn size_hint(&self) -> (usize, Option) { - // At most, all the edges in the graph. - (0, Some(self.graph.len_edges())) - } -} - -pub struct DepthFirstTraversal<'g, N, E> { - graph: &'g Graph, - stack: Vec, - visited: DenseBitSet, - direction: Direction, -} - -impl<'g, N: Debug, E: Debug> DepthFirstTraversal<'g, N, E> { - pub fn with_start_node( - graph: &'g Graph, - start_node: NodeIndex, - direction: Direction, - ) -> Self { - let mut visited = DenseBitSet::new_empty(graph.len_nodes()); - visited.insert(start_node.node_id()); - DepthFirstTraversal { graph, stack: vec![start_node], visited, direction } - } - - fn visit(&mut self, node: NodeIndex) { - if self.visited.insert(node.node_id()) { - self.stack.push(node); - } - } -} - -impl<'g, N: Debug, E: Debug> Iterator for DepthFirstTraversal<'g, N, E> { - type Item = NodeIndex; - - fn next(&mut self) -> Option { - let next = self.stack.pop(); - if let Some(idx) = next { - for (_, edge) in self.graph.adjacent_edges(idx, self.direction) { - let target = edge.source_or_target(self.direction); - self.visit(target); - } - } - next - } - - fn size_hint(&self) -> (usize, Option) { - // We will visit every node in the graph exactly once. - let remaining = self.graph.len_nodes() - self.visited.count(); - (remaining, Some(remaining)) - } -} - -impl<'g, N: Debug, E: Debug> ExactSizeIterator for DepthFirstTraversal<'g, N, E> {} - -impl Edge { - pub fn source(&self) -> NodeIndex { - self.source - } - - pub fn target(&self) -> NodeIndex { - self.target - } - - pub fn source_or_target(&self, direction: Direction) -> NodeIndex { - if direction == OUTGOING { self.target } else { self.source } - } -} diff --git a/compiler/rustc_data_structures/src/graph/implementation/tests.rs b/compiler/rustc_data_structures/src/graph/implementation/tests.rs deleted file mode 100644 index 32a6d9ec881a9..0000000000000 --- a/compiler/rustc_data_structures/src/graph/implementation/tests.rs +++ /dev/null @@ -1,132 +0,0 @@ -use tracing::debug; - -use crate::graph::implementation::*; - -type TestGraph = Graph<&'static str, &'static str>; - -fn create_graph() -> TestGraph { - let mut graph = Graph::new(); - - // Create a simple graph - // - // F - // | - // V - // A --> B --> C - // | ^ - // v | - // D --> E - - let a = graph.add_node("A"); - let b = graph.add_node("B"); - let c = graph.add_node("C"); - let d = graph.add_node("D"); - let e = graph.add_node("E"); - let f = graph.add_node("F"); - - graph.add_edge(a, b, "AB"); - graph.add_edge(b, c, "BC"); - graph.add_edge(b, d, "BD"); - graph.add_edge(d, e, "DE"); - graph.add_edge(e, c, "EC"); - graph.add_edge(f, b, "FB"); - - return graph; -} - -#[test] -fn each_node() { - let graph = create_graph(); - let expected = ["A", "B", "C", "D", "E", "F"]; - graph.each_node(|idx, node| { - assert_eq!(&expected[idx.0], graph.node_data(idx)); - assert_eq!(expected[idx.0], node.data); - true - }); -} - -#[test] -fn each_edge() { - let graph = create_graph(); - let expected = ["AB", "BC", "BD", "DE", "EC", "FB"]; - graph.each_edge(|idx, edge| { - assert_eq!(expected[idx.0], edge.data); - true - }); -} - -fn test_adjacent_edges( - graph: &Graph, - start_index: NodeIndex, - start_data: N, - expected_incoming: &[(E, N)], - expected_outgoing: &[(E, N)], -) { - assert!(graph.node_data(start_index) == &start_data); - - let mut counter = 0; - for (edge_index, edge) in graph.incoming_edges(start_index) { - assert!(counter < expected_incoming.len()); - debug!( - "counter={:?} expected={:?} edge_index={:?} edge={:?}", - counter, expected_incoming[counter], edge_index, edge - ); - match &expected_incoming[counter] { - (e, n) => { - assert!(e == &edge.data); - assert!(n == graph.node_data(edge.source())); - assert!(start_index == edge.target); - } - } - counter += 1; - } - assert_eq!(counter, expected_incoming.len()); - - let mut counter = 0; - for (edge_index, edge) in graph.outgoing_edges(start_index) { - assert!(counter < expected_outgoing.len()); - debug!( - "counter={:?} expected={:?} edge_index={:?} edge={:?}", - counter, expected_outgoing[counter], edge_index, edge - ); - match &expected_outgoing[counter] { - (e, n) => { - assert!(e == &edge.data); - assert!(start_index == edge.source); - assert!(n == graph.node_data(edge.target)); - } - } - counter += 1; - } - assert_eq!(counter, expected_outgoing.len()); -} - -#[test] -fn each_adjacent_from_a() { - let graph = create_graph(); - test_adjacent_edges(&graph, NodeIndex(0), "A", &[], &[("AB", "B")]); -} - -#[test] -fn each_adjacent_from_b() { - let graph = create_graph(); - test_adjacent_edges( - &graph, - NodeIndex(1), - "B", - &[("FB", "F"), ("AB", "A")], - &[("BD", "D"), ("BC", "C")], - ); -} - -#[test] -fn each_adjacent_from_c() { - let graph = create_graph(); - test_adjacent_edges(&graph, NodeIndex(2), "C", &[("EC", "E"), ("BC", "B")], &[]); -} - -#[test] -fn each_adjacent_from_d() { - let graph = create_graph(); - test_adjacent_edges(&graph, NodeIndex(3), "D", &[("BD", "B")], &[("DE", "E")]); -} diff --git a/compiler/rustc_data_structures/src/graph/linked_graph/mod.rs b/compiler/rustc_data_structures/src/graph/linked_graph/mod.rs new file mode 100644 index 0000000000000..ecb0095626b4a --- /dev/null +++ b/compiler/rustc_data_structures/src/graph/linked_graph/mod.rs @@ -0,0 +1,363 @@ +//! See [`LinkedGraph`]. +//! +//! # Interface details +//! +//! You customize the graph by specifying a "node data" type `N` and an +//! "edge data" type `E`. You can then later gain access (mutable or +//! immutable) to these "user-data" bits. Currently, you can only add +//! nodes or edges to the graph. You cannot remove or modify them once +//! added. This could be changed if we have a need. +//! +//! # Implementation details +//! +//! The main tricky thing about this code is the way that edges are +//! stored. The edges are stored in a central array, but they are also +//! threaded onto two linked lists for each node, one for incoming edges +//! and one for outgoing edges. Note that every edge is a member of some +//! incoming list and some outgoing list. Basically you can load the +//! first index of the linked list from the node data structures (the +//! field `first_edge`) and then, for each edge, load the next index from +//! the field `next_edge`). Each of those fields is an array that should +//! be indexed by the direction (see the type `Direction`). + +use std::fmt::Debug; + +use rustc_index::bit_set::DenseBitSet; +use tracing::debug; + +#[cfg(test)] +mod tests; + +/// A concrete graph implementation that supports: +/// - Nodes and/or edges labelled with custom data types (`N` and `E` respectively). +/// - Incremental addition of new nodes/edges (but not removal). +/// - Flat storage of node/edge data in a pair of vectors. +/// - Iteration over any node's out-edges or in-edges, via linked lists +/// threaded through the node/edge data. +/// +/// # Caution +/// This is an older graph implementation that is still used by some pieces +/// of diagnostic/debugging code. New code that needs a graph data structure +/// should consider using `VecGraph` instead, or implementing its own +/// special-purpose graph with the specific features needed. +/// +/// This graph implementation predates the later [graph traits](crate::graph), +/// and does not implement those traits, so it has its own implementations of a +/// few basic graph algorithms. +pub struct LinkedGraph { + nodes: Vec>, + edges: Vec>, +} + +pub struct Node { + first_edge: [EdgeIndex; 2], // see module comment + pub data: N, +} + +#[derive(Debug)] +pub struct Edge { + next_edge: [EdgeIndex; 2], // see module comment + source: NodeIndex, + target: NodeIndex, + pub data: E, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub struct NodeIndex(pub usize); + +#[derive(Copy, Clone, PartialEq, Debug)] +pub struct EdgeIndex(pub usize); + +pub const INVALID_EDGE_INDEX: EdgeIndex = EdgeIndex(usize::MAX); + +// Use a private field here to guarantee no more instances are created: +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Direction { + repr: usize, +} + +pub const OUTGOING: Direction = Direction { repr: 0 }; + +pub const INCOMING: Direction = Direction { repr: 1 }; + +impl NodeIndex { + /// Returns unique ID (unique with respect to the graph holding associated node). + pub fn node_id(self) -> usize { + self.0 + } +} + +impl LinkedGraph { + pub fn new() -> Self { + Self { nodes: Vec::new(), edges: Vec::new() } + } + + pub fn with_capacity(nodes: usize, edges: usize) -> Self { + Self { nodes: Vec::with_capacity(nodes), edges: Vec::with_capacity(edges) } + } + + // # Simple accessors + + #[inline] + pub fn all_nodes(&self) -> &[Node] { + &self.nodes + } + + #[inline] + pub fn len_nodes(&self) -> usize { + self.nodes.len() + } + + #[inline] + pub fn all_edges(&self) -> &[Edge] { + &self.edges + } + + #[inline] + pub fn len_edges(&self) -> usize { + self.edges.len() + } + + // # Node construction + + pub fn next_node_index(&self) -> NodeIndex { + NodeIndex(self.nodes.len()) + } + + pub fn add_node(&mut self, data: N) -> NodeIndex { + let idx = self.next_node_index(); + self.nodes.push(Node { first_edge: [INVALID_EDGE_INDEX, INVALID_EDGE_INDEX], data }); + idx + } + + pub fn mut_node_data(&mut self, idx: NodeIndex) -> &mut N { + &mut self.nodes[idx.0].data + } + + pub fn node_data(&self, idx: NodeIndex) -> &N { + &self.nodes[idx.0].data + } + + pub fn node(&self, idx: NodeIndex) -> &Node { + &self.nodes[idx.0] + } + + // # Edge construction and queries + + pub fn next_edge_index(&self) -> EdgeIndex { + EdgeIndex(self.edges.len()) + } + + pub fn add_edge(&mut self, source: NodeIndex, target: NodeIndex, data: E) -> EdgeIndex { + debug!("graph: add_edge({:?}, {:?}, {:?})", source, target, data); + + let idx = self.next_edge_index(); + + // read current first of the list of edges from each node + let source_first = self.nodes[source.0].first_edge[OUTGOING.repr]; + let target_first = self.nodes[target.0].first_edge[INCOMING.repr]; + + // create the new edge, with the previous firsts from each node + // as the next pointers + self.edges.push(Edge { next_edge: [source_first, target_first], source, target, data }); + + // adjust the firsts for each node target be the next object. + self.nodes[source.0].first_edge[OUTGOING.repr] = idx; + self.nodes[target.0].first_edge[INCOMING.repr] = idx; + + idx + } + + pub fn edge(&self, idx: EdgeIndex) -> &Edge { + &self.edges[idx.0] + } + + // # Iterating over nodes, edges + + pub fn enumerated_nodes(&self) -> impl Iterator)> { + self.nodes.iter().enumerate().map(|(idx, n)| (NodeIndex(idx), n)) + } + + pub fn enumerated_edges(&self) -> impl Iterator)> { + self.edges.iter().enumerate().map(|(idx, e)| (EdgeIndex(idx), e)) + } + + pub fn each_node<'a>(&'a self, mut f: impl FnMut(NodeIndex, &'a Node) -> bool) -> bool { + //! Iterates over all edges defined in the graph. + self.enumerated_nodes().all(|(node_idx, node)| f(node_idx, node)) + } + + pub fn each_edge<'a>(&'a self, mut f: impl FnMut(EdgeIndex, &'a Edge) -> bool) -> bool { + //! Iterates over all edges defined in the graph + self.enumerated_edges().all(|(edge_idx, edge)| f(edge_idx, edge)) + } + + pub fn outgoing_edges(&self, source: NodeIndex) -> AdjacentEdges<'_, N, E> { + self.adjacent_edges(source, OUTGOING) + } + + pub fn incoming_edges(&self, source: NodeIndex) -> AdjacentEdges<'_, N, E> { + self.adjacent_edges(source, INCOMING) + } + + pub fn adjacent_edges( + &self, + source: NodeIndex, + direction: Direction, + ) -> AdjacentEdges<'_, N, E> { + let first_edge = self.node(source).first_edge[direction.repr]; + AdjacentEdges { graph: self, direction, next: first_edge } + } + + pub fn successor_nodes(&self, source: NodeIndex) -> impl Iterator { + self.outgoing_edges(source).targets() + } + + pub fn predecessor_nodes(&self, target: NodeIndex) -> impl Iterator { + self.incoming_edges(target).sources() + } + + pub fn depth_traverse( + &self, + start: NodeIndex, + direction: Direction, + ) -> DepthFirstTraversal<'_, N, E> { + DepthFirstTraversal::with_start_node(self, start, direction) + } + + pub fn nodes_in_postorder( + &self, + direction: Direction, + entry_node: NodeIndex, + ) -> Vec { + let mut visited = DenseBitSet::new_empty(self.len_nodes()); + let mut stack = vec![]; + let mut result = Vec::with_capacity(self.len_nodes()); + let mut push_node = |stack: &mut Vec<_>, node: NodeIndex| { + if visited.insert(node.0) { + stack.push((node, self.adjacent_edges(node, direction))); + } + }; + + for node in + Some(entry_node).into_iter().chain(self.enumerated_nodes().map(|(node, _)| node)) + { + push_node(&mut stack, node); + while let Some((node, mut iter)) = stack.pop() { + if let Some((_, child)) = iter.next() { + let target = child.source_or_target(direction); + // the current node needs more processing, so + // add it back to the stack + stack.push((node, iter)); + // and then push the new node + push_node(&mut stack, target); + } else { + result.push(node); + } + } + } + + assert_eq!(result.len(), self.len_nodes()); + result + } +} + +// # Iterators + +pub struct AdjacentEdges<'g, N, E> { + graph: &'g LinkedGraph, + direction: Direction, + next: EdgeIndex, +} + +impl<'g, N: Debug, E: Debug> AdjacentEdges<'g, N, E> { + fn targets(self) -> impl Iterator { + self.map(|(_, edge)| edge.target) + } + + fn sources(self) -> impl Iterator { + self.map(|(_, edge)| edge.source) + } +} + +impl<'g, N: Debug, E: Debug> Iterator for AdjacentEdges<'g, N, E> { + type Item = (EdgeIndex, &'g Edge); + + fn next(&mut self) -> Option<(EdgeIndex, &'g Edge)> { + let edge_index = self.next; + if edge_index == INVALID_EDGE_INDEX { + return None; + } + + let edge = self.graph.edge(edge_index); + self.next = edge.next_edge[self.direction.repr]; + Some((edge_index, edge)) + } + + fn size_hint(&self) -> (usize, Option) { + // At most, all the edges in the graph. + (0, Some(self.graph.len_edges())) + } +} + +pub struct DepthFirstTraversal<'g, N, E> { + graph: &'g LinkedGraph, + stack: Vec, + visited: DenseBitSet, + direction: Direction, +} + +impl<'g, N: Debug, E: Debug> DepthFirstTraversal<'g, N, E> { + pub fn with_start_node( + graph: &'g LinkedGraph, + start_node: NodeIndex, + direction: Direction, + ) -> Self { + let mut visited = DenseBitSet::new_empty(graph.len_nodes()); + visited.insert(start_node.node_id()); + DepthFirstTraversal { graph, stack: vec![start_node], visited, direction } + } + + fn visit(&mut self, node: NodeIndex) { + if self.visited.insert(node.node_id()) { + self.stack.push(node); + } + } +} + +impl<'g, N: Debug, E: Debug> Iterator for DepthFirstTraversal<'g, N, E> { + type Item = NodeIndex; + + fn next(&mut self) -> Option { + let next = self.stack.pop(); + if let Some(idx) = next { + for (_, edge) in self.graph.adjacent_edges(idx, self.direction) { + let target = edge.source_or_target(self.direction); + self.visit(target); + } + } + next + } + + fn size_hint(&self) -> (usize, Option) { + // We will visit every node in the graph exactly once. + let remaining = self.graph.len_nodes() - self.visited.count(); + (remaining, Some(remaining)) + } +} + +impl<'g, N: Debug, E: Debug> ExactSizeIterator for DepthFirstTraversal<'g, N, E> {} + +impl Edge { + pub fn source(&self) -> NodeIndex { + self.source + } + + pub fn target(&self) -> NodeIndex { + self.target + } + + pub fn source_or_target(&self, direction: Direction) -> NodeIndex { + if direction == OUTGOING { self.target } else { self.source } + } +} diff --git a/compiler/rustc_data_structures/src/graph/linked_graph/tests.rs b/compiler/rustc_data_structures/src/graph/linked_graph/tests.rs new file mode 100644 index 0000000000000..357aa81a57ca3 --- /dev/null +++ b/compiler/rustc_data_structures/src/graph/linked_graph/tests.rs @@ -0,0 +1,132 @@ +use tracing::debug; + +use super::{Debug, LinkedGraph, NodeIndex}; + +type TestGraph = LinkedGraph<&'static str, &'static str>; + +fn create_graph() -> TestGraph { + let mut graph = LinkedGraph::new(); + + // Create a simple graph + // + // F + // | + // V + // A --> B --> C + // | ^ + // v | + // D --> E + + let a = graph.add_node("A"); + let b = graph.add_node("B"); + let c = graph.add_node("C"); + let d = graph.add_node("D"); + let e = graph.add_node("E"); + let f = graph.add_node("F"); + + graph.add_edge(a, b, "AB"); + graph.add_edge(b, c, "BC"); + graph.add_edge(b, d, "BD"); + graph.add_edge(d, e, "DE"); + graph.add_edge(e, c, "EC"); + graph.add_edge(f, b, "FB"); + + return graph; +} + +#[test] +fn each_node() { + let graph = create_graph(); + let expected = ["A", "B", "C", "D", "E", "F"]; + graph.each_node(|idx, node| { + assert_eq!(&expected[idx.0], graph.node_data(idx)); + assert_eq!(expected[idx.0], node.data); + true + }); +} + +#[test] +fn each_edge() { + let graph = create_graph(); + let expected = ["AB", "BC", "BD", "DE", "EC", "FB"]; + graph.each_edge(|idx, edge| { + assert_eq!(expected[idx.0], edge.data); + true + }); +} + +fn test_adjacent_edges( + graph: &LinkedGraph, + start_index: NodeIndex, + start_data: N, + expected_incoming: &[(E, N)], + expected_outgoing: &[(E, N)], +) { + assert!(graph.node_data(start_index) == &start_data); + + let mut counter = 0; + for (edge_index, edge) in graph.incoming_edges(start_index) { + assert!(counter < expected_incoming.len()); + debug!( + "counter={:?} expected={:?} edge_index={:?} edge={:?}", + counter, expected_incoming[counter], edge_index, edge + ); + match &expected_incoming[counter] { + (e, n) => { + assert!(e == &edge.data); + assert!(n == graph.node_data(edge.source())); + assert!(start_index == edge.target); + } + } + counter += 1; + } + assert_eq!(counter, expected_incoming.len()); + + let mut counter = 0; + for (edge_index, edge) in graph.outgoing_edges(start_index) { + assert!(counter < expected_outgoing.len()); + debug!( + "counter={:?} expected={:?} edge_index={:?} edge={:?}", + counter, expected_outgoing[counter], edge_index, edge + ); + match &expected_outgoing[counter] { + (e, n) => { + assert!(e == &edge.data); + assert!(start_index == edge.source); + assert!(n == graph.node_data(edge.target)); + } + } + counter += 1; + } + assert_eq!(counter, expected_outgoing.len()); +} + +#[test] +fn each_adjacent_from_a() { + let graph = create_graph(); + test_adjacent_edges(&graph, NodeIndex(0), "A", &[], &[("AB", "B")]); +} + +#[test] +fn each_adjacent_from_b() { + let graph = create_graph(); + test_adjacent_edges( + &graph, + NodeIndex(1), + "B", + &[("FB", "F"), ("AB", "A")], + &[("BD", "D"), ("BC", "C")], + ); +} + +#[test] +fn each_adjacent_from_c() { + let graph = create_graph(); + test_adjacent_edges(&graph, NodeIndex(2), "C", &[("EC", "E"), ("BC", "B")], &[]); +} + +#[test] +fn each_adjacent_from_d() { + let graph = create_graph(); + test_adjacent_edges(&graph, NodeIndex(3), "D", &[("BD", "B")], &[("DE", "E")]); +} diff --git a/compiler/rustc_data_structures/src/graph/mod.rs b/compiler/rustc_data_structures/src/graph/mod.rs index 4a1e5db6768db..20416b472b210 100644 --- a/compiler/rustc_data_structures/src/graph/mod.rs +++ b/compiler/rustc_data_structures/src/graph/mod.rs @@ -1,8 +1,8 @@ use rustc_index::Idx; pub mod dominators; -pub mod implementation; pub mod iterate; +pub mod linked_graph; mod reference; pub mod reversed; pub mod scc; diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index e7c4ea3daae45..518817ea0f53a 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -10,10 +10,11 @@ use std::assert_matches::debug_assert_matches; use std::fmt::Debug; +use std::marker::PhantomData; use std::ops::Range; use rustc_index::{Idx, IndexSlice, IndexVec}; -use tracing::{debug, instrument}; +use tracing::{debug, instrument, trace}; use crate::fx::FxHashSet; use crate::graph::vec_graph::VecGraph; @@ -48,6 +49,25 @@ pub trait Annotation: Debug + Copy { } } +/// An accumulator for annotations. +pub trait Annotations { + type Ann: Annotation; + type SccIdx: Idx + Ord; + + fn new(&self, element: N) -> Self::Ann; + fn annotate_scc(&mut self, scc: Self::SccIdx, annotation: Self::Ann); +} + +/// The nil annotation accumulator, which does nothing. +struct NoAnnotations(PhantomData); + +impl Annotations for NoAnnotations { + type SccIdx = S; + type Ann = (); + fn new(&self, _element: N) {} + fn annotate_scc(&mut self, _scc: S, _annotation: ()) {} +} + /// The empty annotation, which does nothing. impl Annotation for () { fn merge_reached(self, _other: Self) -> Self { @@ -62,23 +82,20 @@ impl Annotation for () { /// the index type for the graph nodes and `S` is the index type for /// the SCCs. We can map from each node to the SCC that it /// participates in, and we also have the successors of each SCC. -pub struct Sccs { +pub struct Sccs { /// For each node, what is the SCC index of the SCC to which it /// belongs. scc_indices: IndexVec, /// Data about all the SCCs. - scc_data: SccData, + scc_data: SccData, } /// Information about an invidividual SCC node. -struct SccDetails { +struct SccDetails { /// For this SCC, the range of `all_successors` where its /// successors can be found. range: Range, - - /// User-specified metadata about the SCC. - annotation: A, } // The name of this struct should discourage you from making it public and leaking @@ -87,10 +104,10 @@ struct SccDetails { // is difficult when it's publicly inspectable. // // Obey the law of Demeter! -struct SccData { +struct SccData { /// Maps SCC indices to their metadata, including /// offsets into `all_successors`. - scc_details: IndexVec>, + scc_details: IndexVec, /// Contains the successors for all the Sccs, concatenated. The /// range of indices corresponding to a given SCC is found in its @@ -98,24 +115,18 @@ struct SccData { all_successors: Vec, } -impl Sccs { +impl Sccs { /// Compute SCCs without annotations. pub fn new(graph: &impl Successors) -> Self { - Self::new_with_annotation(graph, |_| ()) + Self::new_with_annotation(graph, &mut NoAnnotations(PhantomData::)) } -} -impl Sccs { /// Compute SCCs and annotate them with a user-supplied annotation - pub fn new_with_annotation A>( + pub fn new_with_annotation>( graph: &impl Successors, - to_annotation: F, + annotations: &mut A, ) -> Self { - SccsConstruction::construct(graph, to_annotation) - } - - pub fn annotation(&self, scc: S) -> A { - self.scc_data.annotation(scc) + SccsConstruction::construct(graph, annotations) } pub fn scc_indices(&self) -> &IndexSlice { @@ -160,7 +171,7 @@ impl Sccs { } } -impl DirectedGraph for Sccs { +impl DirectedGraph for Sccs { type Node = S; fn num_nodes(&self) -> usize { @@ -168,19 +179,19 @@ impl DirectedGraph for Sccs { } } -impl NumEdges for Sccs { +impl NumEdges for Sccs { fn num_edges(&self) -> usize { self.scc_data.all_successors.len() } } -impl Successors for Sccs { +impl Successors for Sccs { fn successors(&self, node: S) -> impl Iterator { self.successors(node).iter().cloned() } } -impl SccData { +impl SccData { /// Number of SCCs, fn len(&self) -> usize { self.scc_details.len() @@ -192,9 +203,8 @@ impl SccData { } /// Creates a new SCC with `successors` as its successors and - /// the maximum weight of its internal nodes `scc_max_weight` and /// returns the resulting index. - fn create_scc(&mut self, successors: impl IntoIterator, annotation: A) -> S { + fn create_scc(&mut self, successors: impl IntoIterator) -> S { // Store the successors on `scc_successors_vec`, remembering // the range of indices. let all_successors_start = self.all_successors.len(); @@ -202,35 +212,28 @@ impl SccData { let all_successors_end = self.all_successors.len(); debug!( - "create_scc({:?}) successors={:?}, annotation={:?}", + "create_scc({:?}) successors={:?}", self.len(), &self.all_successors[all_successors_start..all_successors_end], - annotation ); let range = all_successors_start..all_successors_end; - let metadata = SccDetails { range, annotation }; + let metadata = SccDetails { range }; self.scc_details.push(metadata) } - - fn annotation(&self, scc: S) -> A { - self.scc_details[scc].annotation - } } -struct SccsConstruction<'c, G, S, A, F> +struct SccsConstruction<'c, 'a, G, A> where G: DirectedGraph + Successors, - S: Idx, - A: Annotation, - F: Fn(G::Node) -> A, + A: Annotations, { graph: &'c G, /// The state of each node; used during walk to record the stack /// and after walk to record what cycle each node ended up being /// in. - node_states: IndexVec>, + node_states: IndexVec>, /// The stack of nodes that we are visiting as part of the DFS. node_stack: Vec, @@ -239,23 +242,21 @@ where /// position in this stack, and when we encounter a successor SCC, /// we push it on the stack. When we complete an SCC, we can pop /// everything off the stack that was found along the way. - successors_stack: Vec, + successors_stack: Vec, /// A set used to strip duplicates. As we accumulate successors /// into the successors_stack, we sometimes get duplicate entries. /// We use this set to remove those -- we also keep its storage /// around between successors to amortize memory allocation costs. - duplicate_set: FxHashSet, + duplicate_set: FxHashSet, - scc_data: SccData, + scc_data: SccData, - /// A function that constructs an initial SCC annotation - /// out of a single node. - to_annotation: F, + annotations: &'a mut A, } #[derive(Copy, Clone, Debug)] -enum NodeState { +enum NodeState { /// This node has not yet been visited as part of the DFS. /// /// After SCC construction is complete, this state ought to be @@ -286,7 +287,7 @@ enum NodeState { /// The state of walking a given node. #[derive(Copy, Clone, Debug)] -enum WalkReturn { +enum WalkReturn { /// The walk found a cycle, but the entire component is not known to have /// been fully walked yet. We only know the minimum depth of this /// component in a minimum spanning tree of the graph. This component @@ -299,12 +300,10 @@ enum WalkReturn { Complete { scc_index: S, annotation: A }, } -impl<'c, G, S, A, F> SccsConstruction<'c, G, S, A, F> +impl<'c, 'a, G, A> SccsConstruction<'c, 'a, G, A> where G: DirectedGraph + Successors, - S: Idx, - F: Fn(G::Node) -> A, - A: Annotation, + A: Annotations, { /// Identifies SCCs in the graph `G` and computes the resulting /// DAG. This uses a variant of [Tarjan's @@ -320,7 +319,7 @@ where /// Additionally, we keep track of a current annotation of the SCC. /// /// [wikipedia]: https://bit.ly/2EZIx84 - fn construct(graph: &'c G, to_annotation: F) -> Sccs { + fn construct(graph: &'c G, annotations: &'a mut A) -> Sccs { let num_nodes = graph.num_nodes(); let mut this = Self { @@ -330,7 +329,7 @@ where successors_stack: Vec::new(), scc_data: SccData { scc_details: IndexVec::new(), all_successors: Vec::new() }, duplicate_set: FxHashSet::default(), - to_annotation, + annotations, }; let scc_indices = graph @@ -346,7 +345,7 @@ where Sccs { scc_indices, scc_data: this.scc_data } } - fn start_walk_from(&mut self, node: G::Node) -> WalkReturn { + fn start_walk_from(&mut self, node: G::Node) -> WalkReturn { self.inspect_node(node).unwrap_or_else(|| self.walk_unvisited_node(node)) } @@ -362,7 +361,7 @@ where /// Otherwise, we are looking at a node that has already been /// completely visited. We therefore return `WalkReturn::Complete` /// with its associated SCC index. - fn inspect_node(&mut self, node: G::Node) -> Option> { + fn inspect_node(&mut self, node: G::Node) -> Option> { Some(match self.find_state(node) { NodeState::InCycle { scc_index, annotation } => { WalkReturn::Complete { scc_index, annotation } @@ -385,7 +384,7 @@ where /// of `r2` (and updates `r` to reflect current result). This is /// basically the "find" part of a standard union-find algorithm /// (with path compression). - fn find_state(&mut self, mut node: G::Node) -> NodeState { + fn find_state(&mut self, mut node: G::Node) -> NodeState { // To avoid recursion we temporarily reuse the `parent` of each // InCycleWith link to encode a downwards link while compressing // the path. After we have found the root or deepest node being @@ -408,7 +407,7 @@ where // a potentially derived version of the root state for non-root nodes in the chain. let (root_state, assigned_state) = { loop { - debug!("find_state(r = {node:?} in state {:?})", self.node_states[node]); + trace!("find_state(r = {node:?} in state {:?})", self.node_states[node]); match self.node_states[node] { // This must have been the first and only state since it is unexplored*; // no update needed! * Unless there is a bug :') @@ -482,7 +481,7 @@ where if previous_node == node { return root_state; } - debug!("Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}"); + trace!("Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}"); // Update to previous node in the link. match self.node_states[previous_node] { @@ -507,9 +506,9 @@ where /// Call this method when `inspect_node` has returned `None`. Having the /// caller decide avoids mutual recursion between the two methods and allows /// us to maintain an allocated stack for nodes on the path between calls. - #[instrument(skip(self, initial), level = "debug")] - fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn { - debug!("Walk unvisited node: {initial:?}"); + #[instrument(skip(self, initial), level = "trace")] + fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn { + trace!("Walk unvisited node: {initial:?}"); struct VisitingNodeFrame { node: G::Node, successors: Option, @@ -537,7 +536,7 @@ where successors_len: 0, min_cycle_root: initial, successor_node: initial, - current_component_annotation: (self.to_annotation)(initial), + current_component_annotation: self.annotations.new(initial), }]; let mut return_value = None; @@ -556,11 +555,7 @@ where let node = *node; let depth = *depth; - // node is definitely in the current component, add it to the annotation. - if node != initial { - current_component_annotation.update_scc((self.to_annotation)(node)); - } - debug!( + trace!( "Visiting {node:?} at depth {depth:?}, annotation: {current_component_annotation:?}" ); @@ -568,7 +563,7 @@ where Some(successors) => successors, None => { // This None marks that we still have the initialize this node's frame. - debug!(?depth, ?node); + trace!(?depth, ?node); debug_assert_matches!(self.node_states[node], NodeState::NotVisited); @@ -598,7 +593,7 @@ where return_value.take().into_iter().map(|walk| (*successor_node, Some(walk))); let successor_walk = successors.map(|successor_node| { - debug!(?node, ?successor_node); + trace!(?node, ?successor_node); (successor_node, self.inspect_node(successor_node)) }); for (successor_node, walk) in returned_walk.chain(successor_walk) { @@ -609,13 +604,13 @@ where min_depth: successor_min_depth, annotation: successor_annotation, }) => { - debug!( + trace!( "Cycle found from {node:?}, minimum depth: {successor_min_depth:?}, annotation: {successor_annotation:?}" ); // Track the minimum depth we can reach. assert!(successor_min_depth <= depth); if successor_min_depth < *min_depth { - debug!(?node, ?successor_min_depth); + trace!(?node, ?successor_min_depth); *min_depth = successor_min_depth; *min_cycle_root = successor_node; } @@ -627,20 +622,20 @@ where scc_index: successor_scc_index, annotation: successor_annotation, }) => { - debug!( + trace!( "Complete; {node:?} is root of complete-visited SCC idx {successor_scc_index:?} with annotation {successor_annotation:?}" ); // Push the completed SCC indices onto // the `successors_stack` for later. - debug!(?node, ?successor_scc_index); + trace!(?node, ?successor_scc_index); successors_stack.push(successor_scc_index); current_component_annotation.update_reachable(successor_annotation); } // `node` has no more (direct) successors; search recursively. None => { let depth = depth + 1; - debug!("Recursing down into {successor_node:?} at depth {depth:?}"); - debug!(?depth, ?successor_node); + trace!("Recursing down into {successor_node:?} at depth {depth:?}"); + trace!(?depth, ?successor_node); // Remember which node the return value will come from. frame.successor_node = successor_node; // Start a new stack frame, then step into it. @@ -652,14 +647,14 @@ where min_depth: depth, min_cycle_root: successor_node, successor_node, - current_component_annotation: (self.to_annotation)(successor_node), + current_component_annotation: self.annotations.new(successor_node), }); continue 'recurse; } } } - debug!("Finished walk from {node:?} with annotation: {current_component_annotation:?}"); + trace!("Finished walk from {node:?} with annotation: {current_component_annotation:?}"); // Completed walk, remove `node` from the stack. let r = self.node_stack.pop(); @@ -691,8 +686,9 @@ where debug!("Creating SCC rooted in {node:?} with successor {:?}", frame.successor_node); - let scc_index = - self.scc_data.create_scc(deduplicated_successors, current_component_annotation); + let scc_index = self.scc_data.create_scc(deduplicated_successors); + + self.annotations.annotate_scc(scc_index, current_component_annotation); self.node_states[node] = NodeState::InCycle { scc_index, annotation: current_component_annotation }; diff --git a/compiler/rustc_data_structures/src/graph/scc/tests.rs b/compiler/rustc_data_structures/src/graph/scc/tests.rs index 373f87bfdbcfa..8f04baf51f355 100644 --- a/compiler/rustc_data_structures/src/graph/scc/tests.rs +++ b/compiler/rustc_data_structures/src/graph/scc/tests.rs @@ -5,8 +5,31 @@ use crate::graph::tests::TestGraph; #[derive(Copy, Clone, Debug)] struct MaxReached(usize); -type UsizeSccs = Sccs; -type MaxReachedSccs = Sccs; +struct Maxes(IndexVec, fn(usize) -> usize); +type UsizeSccs = Sccs; + +impl Annotations for Maxes { + fn new(&self, element: usize) -> MaxReached { + MaxReached(self.1(element)) + } + + fn annotate_scc(&mut self, scc: usize, annotation: MaxReached) { + let i = self.0.push(annotation); + assert!(i == scc); + } + + type Ann = MaxReached; + type SccIdx = usize; +} + +impl Maxes { + fn annotation(&self, scc: usize) -> MaxReached { + self.0[scc] + } + fn new(mapping: fn(usize) -> usize) -> Self { + Self(IndexVec::new(), mapping) + } +} impl Annotation for MaxReached { fn merge_scc(self, other: Self) -> Self { @@ -14,7 +37,7 @@ impl Annotation for MaxReached { } fn merge_reached(self, other: Self) -> Self { - self.merge_scc(other) + Self(std::cmp::max(other.0, self.0)) } } @@ -24,17 +47,32 @@ impl PartialEq for MaxReached { } } -impl MaxReached { - fn from_usize(nr: usize) -> Self { - Self(nr) - } -} - #[derive(Copy, Clone, Debug)] struct MinMaxIn { min: usize, max: usize, } +struct MinMaxes(IndexVec, fn(usize) -> MinMaxIn); + +impl MinMaxes { + fn annotation(&self, scc: usize) -> MinMaxIn { + self.0[scc] + } +} + +impl Annotations for MinMaxes { + fn new(&self, element: usize) -> MinMaxIn { + self.1(element) + } + + fn annotate_scc(&mut self, scc: usize, annotation: MinMaxIn) { + let i = self.0.push(annotation); + assert!(i == scc); + } + + type Ann = MinMaxIn; + type SccIdx = usize; +} impl Annotation for MinMaxIn { fn merge_scc(self, other: Self) -> Self { @@ -261,67 +299,68 @@ fn bench_sccc(b: &mut test::Bencher) { #[test] fn test_max_self_loop() { let graph = TestGraph::new(0, &[(0, 0)]); - let sccs: MaxReachedSccs = - Sccs::new_with_annotation(&graph, |n| if n == 0 { MaxReached(17) } else { MaxReached(0) }); - assert_eq!(sccs.annotation(0), 17); + let mut annotations = Maxes(IndexVec::new(), |n| if n == 0 { 17 } else { 0 }); + Sccs::new_with_annotation(&graph, &mut annotations); + assert_eq!(annotations.0[0], 17); } #[test] fn test_max_branch() { let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 4)]); - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize); - assert_eq!(sccs.annotation(sccs.scc(0)), 4); - assert_eq!(sccs.annotation(sccs.scc(1)), 3); - assert_eq!(sccs.annotation(sccs.scc(2)), 4); + let mut annotations = Maxes(IndexVec::new(), |n| n); + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); + assert_eq!(annotations.0[sccs.scc(0)], 4); + assert_eq!(annotations.0[sccs.scc(1)], 3); + assert_eq!(annotations.0[sccs.scc(2)], 4); } + #[test] fn test_single_cycle_max() { let graph = TestGraph::new(0, &[(0, 2), (2, 3), (2, 4), (4, 1), (1, 2)]); - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize); - assert_eq!(sccs.annotation(sccs.scc(2)), 4); - assert_eq!(sccs.annotation(sccs.scc(0)), 4); -} - -#[test] -fn test_simple_cycle_max() { - let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 0)]); - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize); - assert_eq!(sccs.num_sccs(), 1); + let mut annotations = Maxes(IndexVec::new(), |n| n); + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); + assert_eq!(annotations.0[sccs.scc(2)], 4); + assert_eq!(annotations.0[sccs.scc(0)], 4); } #[test] fn test_double_cycle_max() { let graph = TestGraph::new(0, &[(0, 1), (1, 2), (1, 4), (2, 3), (2, 4), (3, 5), (4, 1), (5, 4)]); - let sccs: MaxReachedSccs = - Sccs::new_with_annotation(&graph, |n| if n == 5 { MaxReached(2) } else { MaxReached(1) }); + let mut annotations = Maxes(IndexVec::new(), |n| if n == 5 { 2 } else { 1 }); + + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); - assert_eq!(sccs.annotation(sccs.scc(0)).0, 2); + assert_eq!(annotations.0[sccs.scc(0)].0, 2); } #[test] fn test_bug_minimised() { let graph = TestGraph::new(0, &[(0, 3), (0, 1), (3, 2), (2, 3), (1, 4), (4, 5), (5, 4)]); - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |n| match n { - 3 => MaxReached(1), - _ => MaxReached(0), + let mut annotations = Maxes(IndexVec::new(), |n| match n { + 3 => 1, + _ => 0, }); - assert_eq!(sccs.annotation(sccs.scc(2)), 1); - assert_eq!(sccs.annotation(sccs.scc(1)), 0); - assert_eq!(sccs.annotation(sccs.scc(4)), 0); + + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); + assert_eq!(annotations.annotation(sccs.scc(2)), 1); + assert_eq!(annotations.annotation(sccs.scc(1)), 0); + assert_eq!(annotations.annotation(sccs.scc(4)), 0); } #[test] fn test_bug_max_leak_minimised() { let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3)]); - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { - 4 => MaxReached(1), - _ => MaxReached(0), + let mut annotations = Maxes(IndexVec::new(), |w| match w { + 4 => 1, + _ => 0, }); - assert_eq!(sccs.annotation(sccs.scc(2)), 0); - assert_eq!(sccs.annotation(sccs.scc(3)), 1); - assert_eq!(sccs.annotation(sccs.scc(0)), 1); + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); + + assert_eq!(annotations.annotation(sccs.scc(2)), 0); + assert_eq!(annotations.annotation(sccs.scc(3)), 1); + assert_eq!(annotations.annotation(sccs.scc(0)), 1); } #[test] @@ -369,48 +408,49 @@ fn test_bug_max_leak() { (23, 24), ], ); - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { - 22 => MaxReached(1), - 24 => MaxReached(2), - 27 => MaxReached(2), - _ => MaxReached(0), + let mut annotations = Maxes::new(|w| match w { + 22 => 1, + 24 => 2, + 27 => 2, + _ => 0, }); - - assert_eq!(sccs.annotation(sccs.scc(2)), 0); - assert_eq!(sccs.annotation(sccs.scc(7)), 0); - assert_eq!(sccs.annotation(sccs.scc(8)), 2); - assert_eq!(sccs.annotation(sccs.scc(23)), 2); - assert_eq!(sccs.annotation(sccs.scc(3)), 2); - assert_eq!(sccs.annotation(sccs.scc(0)), 2); + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); + + assert_eq!(annotations.annotation(sccs.scc(2)), 0); + assert_eq!(annotations.annotation(sccs.scc(7)), 0); + assert_eq!(annotations.annotation(sccs.scc(8)), 2); + assert_eq!(annotations.annotation(sccs.scc(23)), 2); + assert_eq!(annotations.annotation(sccs.scc(3)), 2); + assert_eq!(annotations.annotation(sccs.scc(0)), 2); } #[test] fn test_bug_max_zero_stick_shape() { let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 3), (3, 2), (3, 4)]); - - let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { - 4 => MaxReached(1), - _ => MaxReached(0), + let mut annotations = Maxes::new(|w| match w { + 4 => 1, + _ => 0, }); + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); - assert_eq!(sccs.annotation(sccs.scc(0)), 1); - assert_eq!(sccs.annotation(sccs.scc(1)), 1); - assert_eq!(sccs.annotation(sccs.scc(2)), 1); - assert_eq!(sccs.annotation(sccs.scc(3)), 1); - assert_eq!(sccs.annotation(sccs.scc(4)), 1); + assert_eq!(annotations.annotation(sccs.scc(0)), 1); + assert_eq!(annotations.annotation(sccs.scc(1)), 1); + assert_eq!(annotations.annotation(sccs.scc(2)), 1); + assert_eq!(annotations.annotation(sccs.scc(3)), 1); + assert_eq!(annotations.annotation(sccs.scc(4)), 1); } #[test] fn test_min_max_in() { let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3), (3, 5)]); - let sccs: Sccs = - Sccs::new_with_annotation(&graph, |w| MinMaxIn { min: w, max: w }); - - assert_eq!(sccs.annotation(sccs.scc(2)).min, 2); - assert_eq!(sccs.annotation(sccs.scc(2)).max, 2); - assert_eq!(sccs.annotation(sccs.scc(0)).min, 0); - assert_eq!(sccs.annotation(sccs.scc(0)).max, 4); - assert_eq!(sccs.annotation(sccs.scc(3)).min, 0); - assert_eq!(sccs.annotation(sccs.scc(3)).max, 4); - assert_eq!(sccs.annotation(sccs.scc(5)).min, 5); + let mut annotations = MinMaxes(IndexVec::new(), |w| MinMaxIn { min: w, max: w }); + let sccs = Sccs::new_with_annotation(&graph, &mut annotations); + + assert_eq!(annotations.annotation(sccs.scc(2)).min, 2); + assert_eq!(annotations.annotation(sccs.scc(2)).max, 2); + assert_eq!(annotations.annotation(sccs.scc(0)).min, 0); + assert_eq!(annotations.annotation(sccs.scc(0)).max, 4); + assert_eq!(annotations.annotation(sccs.scc(3)).min, 0); + assert_eq!(annotations.annotation(sccs.scc(3)).max, 4); + assert_eq!(annotations.annotation(sccs.scc(5)).min, 5); } diff --git a/compiler/rustc_data_structures/src/graph/tests.rs b/compiler/rustc_data_structures/src/graph/tests.rs index b69b9dbc4a8e6..e48b9686c260f 100644 --- a/compiler/rustc_data_structures/src/graph/tests.rs +++ b/compiler/rustc_data_structures/src/graph/tests.rs @@ -3,7 +3,7 @@ use std::cmp::max; use super::*; use crate::fx::FxHashMap; -pub struct TestGraph { +pub(super) struct TestGraph { num_nodes: usize, start_node: usize, successors: FxHashMap>, @@ -11,7 +11,7 @@ pub struct TestGraph { } impl TestGraph { - pub fn new(start_node: usize, edges: &[(usize, usize)]) -> Self { + pub(super) fn new(start_node: usize, edges: &[(usize, usize)]) -> Self { let mut graph = TestGraph { num_nodes: start_node + 1, start_node, diff --git a/compiler/rustc_data_structures/src/jobserver.rs b/compiler/rustc_data_structures/src/jobserver.rs index 1204f2d692d6c..3ed1ea7543f40 100644 --- a/compiler/rustc_data_structures/src/jobserver.rs +++ b/compiler/rustc_data_structures/src/jobserver.rs @@ -1,7 +1,8 @@ -use std::sync::{LazyLock, OnceLock}; +use std::sync::{Arc, LazyLock, OnceLock}; pub use jobserver_crate::{Acquired, Client, HelperThread}; use jobserver_crate::{FromEnv, FromEnvErrorKind}; +use parking_lot::{Condvar, Mutex}; // We can only call `from_env_ext` once per process @@ -71,10 +72,93 @@ pub fn client() -> Client { GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).clone() } -pub fn acquire_thread() { - GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).acquire_raw().ok(); +struct ProxyData { + /// The number of tokens assigned to threads. + /// If this is 0, a single token is still assigned to this process, but is unused. + used: u16, + + /// The number of threads requesting a token + pending: u16, +} + +/// This is a jobserver proxy used to ensure that we hold on to at least one token. +pub struct Proxy { + client: Client, + data: Mutex, + + /// Threads which are waiting on a token will wait on this. + wake_pending: Condvar, + + helper: OnceLock, } -pub fn release_thread() { - GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).release_raw().ok(); +impl Proxy { + pub fn new() -> Arc { + let proxy = Arc::new(Proxy { + client: client(), + data: Mutex::new(ProxyData { used: 1, pending: 0 }), + wake_pending: Condvar::new(), + helper: OnceLock::new(), + }); + let proxy_ = Arc::clone(&proxy); + let helper = proxy + .client + .clone() + .into_helper_thread(move |token| { + if let Ok(token) = token { + let mut data = proxy_.data.lock(); + if data.pending > 0 { + // Give the token to a waiting thread + token.drop_without_releasing(); + assert!(data.used > 0); + data.used += 1; + data.pending -= 1; + proxy_.wake_pending.notify_one(); + } else { + // The token is no longer needed, drop it. + drop(data); + drop(token); + } + } + }) + .expect("failed to create helper thread"); + proxy.helper.set(helper).unwrap(); + proxy + } + + pub fn acquire_thread(&self) { + let mut data = self.data.lock(); + + if data.used == 0 { + // There was a free token around. This can + // happen when all threads release their token. + assert_eq!(data.pending, 0); + data.used += 1; + } else { + // Request a token from the helper thread. We can't directly use `acquire_raw` + // as we also need to be able to wait for the final token in the process which + // does not get a corresponding `release_raw` call. + self.helper.get().unwrap().request_token(); + data.pending += 1; + self.wake_pending.wait(&mut data); + } + } + + pub fn release_thread(&self) { + let mut data = self.data.lock(); + + if data.pending > 0 { + // Give the token to a waiting thread + data.pending -= 1; + self.wake_pending.notify_one(); + } else { + data.used -= 1; + + // Release the token unless it's the last one in the process + if data.used > 0 { + drop(data); + self.client.release_raw().ok(); + } + } + } } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 66d3834d85784..865424fd6bbdc 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -24,7 +24,6 @@ #![feature(dropck_eyepatch)] #![feature(extend_one)] #![feature(file_buffered)] -#![feature(hash_raw_entry)] #![feature(macro_metavar_expr)] #![feature(map_try_insert)] #![feature(min_specialization)] @@ -48,7 +47,6 @@ pub use rustc_index::static_assert_size; pub mod aligned; pub mod base_n; pub mod binary_search_util; -pub mod captures; pub mod fingerprint; pub mod flat_map_in_place; pub mod flock; diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index 6ae97222f77fd..e0df1b232e134 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -1,11 +1,13 @@ -#[rustc_on_unimplemented(message = "`{Self}` doesn't implement `DynSend`. \ +use std::alloc::Allocator; + +#[diagnostic::on_unimplemented(message = "`{Self}` doesn't implement `DynSend`. \ Add it to `rustc_data_structures::marker` or use `IntoDynSyncSend` if it's already `Send`")] // This is an auto trait for types which can be sent across threads if `sync::is_dyn_thread_safe()` // is true. These types can be wrapped in a `FromDyn` to get a `Send` type. Wrapping a // `Send` type in `IntoDynSyncSend` will create a `DynSend` type. pub unsafe auto trait DynSend {} -#[rustc_on_unimplemented(message = "`{Self}` doesn't implement `DynSync`. \ +#[diagnostic::on_unimplemented(message = "`{Self}` doesn't implement `DynSync`. \ Add it to `rustc_data_structures::marker` or use `IntoDynSyncSend` if it's already `Sync`")] // This is an auto trait for types which can be shared across threads if `sync::is_dyn_thread_safe()` // is true. These types can be wrapped in a `FromDyn` to get a `Sync` type. Wrapping a @@ -28,8 +30,8 @@ impls_dyn_send_neg!( [*const T where T: ?Sized] [*mut T where T: ?Sized] [std::ptr::NonNull where T: ?Sized] - [std::rc::Rc where T: ?Sized] - [std::rc::Weak where T: ?Sized] + [std::rc::Rc where T: ?Sized, A: Allocator] + [std::rc::Weak where T: ?Sized, A: Allocator] [std::sync::MutexGuard<'_, T> where T: ?Sized] [std::sync::RwLockReadGuard<'_, T> where T: ?Sized] [std::sync::RwLockWriteGuard<'_, T> where T: ?Sized] @@ -37,8 +39,15 @@ impls_dyn_send_neg!( [std::io::StderrLock<'_>] ); -#[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] -// Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms +#[cfg(any( + unix, + target_os = "hermit", + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "solid_asp3", + target_os = "wasi", + target_os = "xous" +))] +// Consistent with `std`, `env_imp::Env` is `!Sync` in these platforms impl !DynSend for std::env::VarsOs {} macro_rules! already_send { @@ -50,8 +59,8 @@ macro_rules! already_send { // These structures are already `Send`. already_send!( [std::backtrace::Backtrace][std::io::Stdout][std::io::Stderr][std::io::Error][std::fs::File] - [rustc_arena::DroplessArena][crate::memmap::Mmap][crate::profiling::SelfProfiler] - [crate::owned_slice::OwnedSlice] + [rustc_arena::DroplessArena][jobserver_crate::Client][jobserver_crate::HelperThread] + [crate::memmap::Mmap][crate::profiling::SelfProfiler][crate::owned_slice::OwnedSlice] ); macro_rules! impl_dyn_send { @@ -74,6 +83,7 @@ impl_dyn_send!( [crate::sync::RwLock where T: DynSend] [crate::tagged_ptr::TaggedRef<'a, P, T> where 'a, P: Sync, T: Send + crate::tagged_ptr::Tag] [rustc_arena::TypedArena where T: DynSend] + [hashbrown::HashTable where T: DynSend] [indexmap::IndexSet where V: DynSend, S: DynSend] [indexmap::IndexMap where K: DynSend, V: DynSend, S: DynSend] [thin_vec::ThinVec where T: DynSend] @@ -96,15 +106,22 @@ impls_dyn_sync_neg!( [std::cell::RefCell where T: ?Sized] [std::cell::UnsafeCell where T: ?Sized] [std::ptr::NonNull where T: ?Sized] - [std::rc::Rc where T: ?Sized] - [std::rc::Weak where T: ?Sized] + [std::rc::Rc where T: ?Sized, A: Allocator] + [std::rc::Weak where T: ?Sized, A: Allocator] [std::cell::OnceCell where T] [std::sync::mpsc::Receiver where T] [std::sync::mpsc::Sender where T] ); -#[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] -// Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms +#[cfg(any( + unix, + target_os = "hermit", + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "solid_asp3", + target_os = "wasi", + target_os = "xous" +))] +// Consistent with `std`, `env_imp::Env` is `!Sync` in these platforms impl !DynSync for std::env::VarsOs {} macro_rules! already_sync { @@ -117,8 +134,8 @@ macro_rules! already_sync { already_sync!( [std::sync::atomic::AtomicBool][std::sync::atomic::AtomicUsize][std::sync::atomic::AtomicU8] [std::sync::atomic::AtomicU32][std::backtrace::Backtrace][std::io::Error][std::fs::File] - [jobserver_crate::Client][crate::memmap::Mmap][crate::profiling::SelfProfiler] - [crate::owned_slice::OwnedSlice] + [jobserver_crate::Client][jobserver_crate::HelperThread][crate::memmap::Mmap] + [crate::profiling::SelfProfiler][crate::owned_slice::OwnedSlice] ); // Use portable AtomicU64 for targets without native 64-bit atomics @@ -151,6 +168,7 @@ impl_dyn_sync!( [crate::tagged_ptr::TaggedRef<'a, P, T> where 'a, P: Sync, T: Sync + crate::tagged_ptr::Tag] [parking_lot::lock_api::Mutex where R: DynSync, T: ?Sized + DynSend] [parking_lot::lock_api::RwLock where R: DynSync, T: ?Sized + DynSend + DynSync] + [hashbrown::HashTable where T: DynSync] [indexmap::IndexSet where V: DynSync, S: DynSync] [indexmap::IndexMap where K: DynSync, V: DynSync, S: DynSync] [smallvec::SmallVec where A: smallvec::Array + DynSync] @@ -175,6 +193,12 @@ impl FromDyn { FromDyn(val) } + #[inline(always)] + pub fn derive(&self, val: O) -> FromDyn { + // We already did the check for `sync::is_dyn_thread_safe()` when creating `Self` + FromDyn(val) + } + #[inline(always)] pub fn into_inner(self) -> T { self.0 @@ -196,6 +220,13 @@ impl std::ops::Deref for FromDyn { } } +impl std::ops::DerefMut for FromDyn { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + // A wrapper to convert a struct that is already a `Send` or `Sync` into // an instance of `DynSend` and `DynSync`, since the compiler cannot infer // it automatically in some cases. (e.g. Box) diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index 78d69a66edc8b..2c62034c6e87e 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -313,8 +313,9 @@ pub struct Error { mod helper { use super::*; - pub type ObligationTreeIdGenerator = impl Iterator; + pub(super) type ObligationTreeIdGenerator = impl Iterator; impl ObligationForest { + #[define_opaque(ObligationTreeIdGenerator)] pub fn new() -> ObligationForest { ObligationForest { nodes: vec![], diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 39db551adfb84..60f007083baf3 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -863,15 +863,13 @@ fn get_thread_id() -> u32 { cfg_match! { windows => { pub fn get_resident_set_size() -> Option { - use std::mem; - use windows::{ Win32::System::ProcessStatus::{K32GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS}, Win32::System::Threading::GetCurrentProcess, }; let mut pmc = PROCESS_MEMORY_COUNTERS::default(); - let pmc_size = mem::size_of_val(&pmc); + let pmc_size = size_of_val(&pmc); unsafe { K32GetProcessMemoryInfo( GetCurrentProcess(), @@ -889,7 +887,7 @@ cfg_match! { pub fn get_resident_set_size() -> Option { use libc::{c_int, c_void, getpid, proc_pidinfo, proc_taskinfo, PROC_PIDTASKINFO}; use std::mem; - const PROC_TASKINFO_SIZE: c_int = mem::size_of::() as c_int; + const PROC_TASKINFO_SIZE: c_int = size_of::() as c_int; unsafe { let mut info: proc_taskinfo = mem::zeroed(); diff --git a/compiler/rustc_data_structures/src/sharded.rs b/compiler/rustc_data_structures/src/sharded.rs index 5a53f8af5f8c4..5de9413cf15df 100644 --- a/compiler/rustc_data_structures/src/sharded.rs +++ b/compiler/rustc_data_structures/src/sharded.rs @@ -1,11 +1,11 @@ use std::borrow::Borrow; -use std::collections::hash_map::RawEntryMut; use std::hash::{Hash, Hasher}; use std::{iter, mem}; use either::Either; +use hashbrown::hash_table::{Entry, HashTable}; -use crate::fx::{FxHashMap, FxHasher}; +use crate::fx::FxHasher; use crate::sync::{CacheAligned, Lock, LockGuard, Mode, is_dyn_thread_safe}; // 32 shards is sufficient to reduce contention on an 8-core Ryzen 7 1700, @@ -43,10 +43,10 @@ impl Sharded { /// The shard is selected by hashing `val` with `FxHasher`. #[inline] - pub fn get_shard_by_value(&self, _val: &K) -> &Lock { + pub fn get_shard_by_value(&self, val: &K) -> &Lock { match self { Self::Single(single) => single, - Self::Shards(..) => self.get_shard_by_hash(make_hash(_val)), + Self::Shards(..) => self.get_shard_by_hash(make_hash(val)), } } @@ -56,12 +56,12 @@ impl Sharded { } #[inline] - pub fn get_shard_by_index(&self, _i: usize) -> &Lock { + pub fn get_shard_by_index(&self, i: usize) -> &Lock { match self { Self::Single(single) => single, Self::Shards(shards) => { // SAFETY: The index gets ANDed with the shard mask, ensuring it is always inbounds. - unsafe { &shards.get_unchecked(_i & (SHARDS - 1)).0 } + unsafe { &shards.get_unchecked(i & (SHARDS - 1)).0 } } } } @@ -69,7 +69,7 @@ impl Sharded { /// The shard is selected by hashing `val` with `FxHasher`. #[inline] #[track_caller] - pub fn lock_shard_by_value(&self, _val: &K) -> LockGuard<'_, T> { + pub fn lock_shard_by_value(&self, val: &K) -> LockGuard<'_, T> { match self { Self::Single(single) => { // Synchronization is disabled so use the `lock_assume_no_sync` method optimized @@ -79,7 +79,7 @@ impl Sharded { // `might_be_dyn_thread_safe` was also false. unsafe { single.lock_assume(Mode::NoSync) } } - Self::Shards(..) => self.lock_shard_by_hash(make_hash(_val)), + Self::Shards(..) => self.lock_shard_by_hash(make_hash(val)), } } @@ -91,7 +91,7 @@ impl Sharded { #[inline] #[track_caller] - pub fn lock_shard_by_index(&self, _i: usize) -> LockGuard<'_, T> { + pub fn lock_shard_by_index(&self, i: usize) -> LockGuard<'_, T> { match self { Self::Single(single) => { // Synchronization is disabled so use the `lock_assume_no_sync` method optimized @@ -109,7 +109,7 @@ impl Sharded { // always inbounds. // SAFETY (lock_assume_sync): We know `is_dyn_thread_safe` was true when creating // the lock thus `might_be_dyn_thread_safe` was also true. - unsafe { shards.get_unchecked(_i & (SHARDS - 1)).0.lock_assume(Mode::Sync) } + unsafe { shards.get_unchecked(i & (SHARDS - 1)).0.lock_assume(Mode::Sync) } } } } @@ -140,17 +140,67 @@ pub fn shards() -> usize { 1 } -pub type ShardedHashMap = Sharded>; +pub type ShardedHashMap = Sharded>; impl ShardedHashMap { pub fn with_capacity(cap: usize) -> Self { - Self::new(|| FxHashMap::with_capacity_and_hasher(cap, rustc_hash::FxBuildHasher::default())) + Self::new(|| HashTable::with_capacity(cap)) } pub fn len(&self) -> usize { self.lock_shards().map(|shard| shard.len()).sum() } } +impl ShardedHashMap { + #[inline] + pub fn get(&self, key: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq, + V: Clone, + { + let hash = make_hash(key); + let shard = self.lock_shard_by_hash(hash); + let (_, value) = shard.find(hash, |(k, _)| k.borrow() == key)?; + Some(value.clone()) + } + + #[inline] + pub fn get_or_insert_with(&self, key: K, default: impl FnOnce() -> V) -> V + where + V: Copy, + { + let hash = make_hash(&key); + let mut shard = self.lock_shard_by_hash(hash); + + match table_entry(&mut shard, hash, &key) { + Entry::Occupied(e) => e.get().1, + Entry::Vacant(e) => { + let value = default(); + e.insert((key, value)); + value + } + } + } + + #[inline] + pub fn insert(&self, key: K, value: V) -> Option { + let hash = make_hash(&key); + let mut shard = self.lock_shard_by_hash(hash); + + match table_entry(&mut shard, hash, &key) { + Entry::Occupied(e) => { + let previous = mem::replace(&mut e.into_mut().1, value); + Some(previous) + } + Entry::Vacant(e) => { + e.insert((key, value)); + None + } + } + } +} + impl ShardedHashMap { #[inline] pub fn intern_ref(&self, value: &Q, make: impl FnOnce() -> K) -> K @@ -160,13 +210,12 @@ impl ShardedHashMap { { let hash = make_hash(value); let mut shard = self.lock_shard_by_hash(hash); - let entry = shard.raw_entry_mut().from_key_hashed_nocheck(hash, value); - match entry { - RawEntryMut::Occupied(e) => *e.key(), - RawEntryMut::Vacant(e) => { + match table_entry(&mut shard, hash, value) { + Entry::Occupied(e) => e.get().0, + Entry::Vacant(e) => { let v = make(); - e.insert_hashed_nocheck(hash, v, ()); + e.insert((v, ())); v } } @@ -180,13 +229,12 @@ impl ShardedHashMap { { let hash = make_hash(&value); let mut shard = self.lock_shard_by_hash(hash); - let entry = shard.raw_entry_mut().from_key_hashed_nocheck(hash, &value); - match entry { - RawEntryMut::Occupied(e) => *e.key(), - RawEntryMut::Vacant(e) => { + match table_entry(&mut shard, hash, &value) { + Entry::Occupied(e) => e.get().0, + Entry::Vacant(e) => { let v = make(value); - e.insert_hashed_nocheck(hash, v, ()); + e.insert((v, ())); v } } @@ -203,7 +251,7 @@ impl ShardedHashMap { let hash = make_hash(&value); let shard = self.lock_shard_by_hash(hash); let value = value.into_pointer(); - shard.raw_entry().from_hash(hash, |entry| entry.into_pointer() == value).is_some() + shard.find(hash, |(k, ())| k.into_pointer() == value).is_some() } } @@ -214,6 +262,19 @@ pub fn make_hash(val: &K) -> u64 { state.finish() } +#[inline] +fn table_entry<'a, K, V, Q>( + table: &'a mut HashTable<(K, V)>, + hash: u64, + key: &Q, +) -> Entry<'a, (K, V)> +where + K: Hash + Borrow, + Q: ?Sized + Eq, +{ + table.entry(hash, move |(k, _)| k.borrow() == key, |(k, _)| make_hash(k)) +} + /// Get a shard with a pre-computed hash value. If `get_shard_by_value` is /// ever used in combination with `get_shard_by_hash` on a single `Sharded` /// instance, then `hash` must be computed with `FxHasher`. Otherwise, @@ -221,7 +282,7 @@ pub fn make_hash(val: &K) -> u64 { /// consistently for each `Sharded` instance. #[inline] fn get_shard_hash(hash: u64) -> usize { - let hash_len = mem::size_of::(); + let hash_len = size_of::(); // Ignore the top 7 bits as hashbrown uses these and get the next SHARD_BITS highest bits. // hashbrown also uses the lowest bits, so we can't use those (hash >> (hash_len * 8 - 7 - SHARD_BITS)) as usize diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index a01a420dfbd66..c002d47815b17 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -4,7 +4,7 @@ use std::fmt::Debug; use std::mem; use std::ops::{Bound, Index, IndexMut, RangeBounds}; -use rustc_macros::{Decodable_Generic, Encodable_Generic}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext}; use crate::stable_hasher::{HashStable, StableHasher, StableOrd}; @@ -20,7 +20,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, Encodable_Generic, Decodable_Generic)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable_NoContext, Decodable_NoContext)] pub struct SortedMap { data: Vec<(K, V)>, } diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index ffbe54d620612..3a64c924cc224 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -564,6 +564,8 @@ where } } +impl_stable_traits_for_trivial_type!(::std::ffi::OsStr); + impl_stable_traits_for_trivial_type!(::std::path::Path); impl_stable_traits_for_trivial_type!(::std::path::PathBuf); diff --git a/compiler/rustc_data_structures/src/svh.rs b/compiler/rustc_data_structures/src/svh.rs index 391a7c9f30dbf..f51fcb8ed22d3 100644 --- a/compiler/rustc_data_structures/src/svh.rs +++ b/compiler/rustc_data_structures/src/svh.rs @@ -7,12 +7,12 @@ use std::fmt; -use rustc_macros::{Decodable_Generic, Encodable_Generic}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext}; use crate::fingerprint::Fingerprint; use crate::stable_hasher; -#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable_Generic, Decodable_Generic, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable_NoContext, Decodable_NoContext, Hash)] pub struct Svh { hash: Fingerprint, } diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 37b54fe38ff74..b28c333d860c6 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -18,42 +18,54 @@ //! //! | Type | Serial version | Parallel version | //! | ----------------------- | ------------------- | ------------------------------- | -//! | `LRef<'a, T>` [^2] | `&'a mut T` | `&'a T` | -//! | | | | //! | `Lock` | `RefCell` | `RefCell` or | //! | | | `parking_lot::Mutex` | //! | `RwLock` | `RefCell` | `parking_lot::RwLock` | //! | `MTLock` [^1] | `T` | `Lock` | -//! | `MTLockRef<'a, T>` [^2] | `&'a mut MTLock` | `&'a MTLock` | //! | | | | //! | `ParallelIterator` | `Iterator` | `rayon::iter::ParallelIterator` | //! //! [^1]: `MTLock` is similar to `Lock`, but the serial version avoids the cost //! of a `RefCell`. This is appropriate when interior mutability is not //! required. -//! -//! [^2]: `MTRef`, `MTLockRef` are type aliases. use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; -pub use crate::marker::*; +pub use parking_lot::{ + MappedRwLockReadGuard as MappedReadGuard, MappedRwLockWriteGuard as MappedWriteGuard, + RwLockReadGuard as ReadGuard, RwLockWriteGuard as WriteGuard, +}; -mod lock; +pub use self::atomic::AtomicU64; +pub use self::freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard}; #[doc(no_inline)] -pub use lock::{Lock, LockGuard, Mode}; - -mod worker_local; -pub use worker_local::{Registry, WorkerLocal}; +pub use self::lock::{Lock, LockGuard, Mode}; +pub use self::mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; +pub use self::parallel::{ + broadcast, join, par_for_each_in, par_map, parallel_guard, scope, spawn, try_par_for_each_in, +}; +pub use self::vec::{AppendOnlyIndexVec, AppendOnlyVec}; +pub use self::worker_local::{Registry, WorkerLocal}; +pub use crate::marker::*; +mod freeze; +mod lock; mod parallel; -pub use parallel::{join, par_for_each_in, par_map, parallel_guard, scope, try_par_for_each_in}; -pub use vec::{AppendOnlyIndexVec, AppendOnlyVec}; - mod vec; +mod worker_local; -mod freeze; -pub use freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard}; +/// Keep the conditional imports together in a submodule, so that import-sorting +/// doesn't split them up. +mod atomic { + // Most hosts can just use a regular AtomicU64. + #[cfg(target_has_atomic = "64")] + pub use std::sync::atomic::AtomicU64; + + // Some 32-bit hosts don't have AtomicU64, so use a fallback. + #[cfg(not(target_has_atomic = "64"))] + pub use portable_atomic::AtomicU64; +} mod mode { use std::sync::atomic::{AtomicU8, Ordering}; @@ -76,7 +88,7 @@ mod mode { // Whether thread safety might be enabled. #[inline] - pub fn might_be_dyn_thread_safe() -> bool { + pub(super) fn might_be_dyn_thread_safe() -> bool { DYN_THREAD_SAFE_MODE.load(Ordering::Relaxed) != DYN_NOT_THREAD_SAFE } @@ -97,21 +109,6 @@ mod mode { // FIXME(parallel_compiler): Get rid of these aliases across the compiler. -pub use std::sync::OnceLock; -// Use portable AtomicU64 for targets without native 64-bit atomics -#[cfg(target_has_atomic = "64")] -pub use std::sync::atomic::AtomicU64; - -pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; -pub use parking_lot::{ - MappedRwLockReadGuard as MappedReadGuard, MappedRwLockWriteGuard as MappedWriteGuard, - RwLockReadGuard as ReadGuard, RwLockWriteGuard as WriteGuard, -}; -#[cfg(not(target_has_atomic = "64"))] -pub use portable_atomic::AtomicU64; - -pub type LRef<'a, T> = &'a T; - #[derive(Debug, Default)] pub struct MTLock(Lock); @@ -142,14 +139,10 @@ impl MTLock { } } -use parking_lot::RwLock as InnerRwLock; - /// 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; -pub type MTLockRef<'a, T> = LRef<'a, MTLock>; - #[derive(Default)] #[repr(align(64))] pub struct CacheAligned(pub T); @@ -167,12 +160,12 @@ impl HashMapExt for HashMap } #[derive(Debug, Default)] -pub struct RwLock(InnerRwLock); +pub struct RwLock(parking_lot::RwLock); impl RwLock { #[inline(always)] pub fn new(inner: T) -> Self { - RwLock(InnerRwLock::new(inner)) + RwLock(parking_lot::RwLock::new(inner)) } #[inline(always)] diff --git a/compiler/rustc_data_structures/src/sync/freeze.rs b/compiler/rustc_data_structures/src/sync/freeze.rs index 9720b22ea7d1b..6338afb92c34b 100644 --- a/compiler/rustc_data_structures/src/sync/freeze.rs +++ b/compiler/rustc_data_structures/src/sync/freeze.rs @@ -88,7 +88,7 @@ impl FreezeLock { #[inline] #[track_caller] pub fn write(&self) -> FreezeWriteGuard<'_, T> { - self.try_write().expect("still mutable") + self.try_write().expect("data should not be frozen if we're still attempting to mutate it") } #[inline] diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs index 1ba631b862376..ab65c7f3a6b5f 100644 --- a/compiler/rustc_data_structures/src/sync/parallel.rs +++ b/compiler/rustc_data_structures/src/sync/parallel.rs @@ -7,7 +7,6 @@ use std::any::Any; use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; use parking_lot::Mutex; -use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator}; use crate::FatalErrorMarker; use crate::sync::{DynSend, DynSync, FromDyn, IntoDynSyncSend, mode}; @@ -46,7 +45,7 @@ pub fn parallel_guard(f: impl FnOnce(&ParallelGuard) -> R) -> R { ret } -pub fn serial_join(oper_a: A, oper_b: B) -> (RA, RB) +fn serial_join(oper_a: A, oper_b: B) -> (RA, RB) where A: FnOnce() -> RA, B: FnOnce() -> RB, @@ -94,14 +93,25 @@ macro_rules! parallel { }; } +pub fn spawn(func: impl FnOnce() + DynSend + 'static) { + if mode::is_dyn_thread_safe() { + let func = FromDyn::from(func); + rayon_core::spawn(|| { + (func.into_inner())(); + }); + } else { + func() + } +} + // This function only works when `mode::is_dyn_thread_safe()`. pub fn scope<'scope, OP, R>(op: OP) -> R where - OP: FnOnce(&rayon::Scope<'scope>) -> R + DynSend, + OP: FnOnce(&rayon_core::Scope<'scope>) -> R + DynSend, R: DynSend, { let op = FromDyn::from(op); - rayon::scope(|s| FromDyn::from(op.into_inner()(s))).into_inner() + rayon_core::scope(|s| FromDyn::from(op.into_inner()(s))).into_inner() } #[inline] @@ -114,7 +124,7 @@ where let oper_a = FromDyn::from(oper_a); let oper_b = FromDyn::from(oper_b); let (a, b) = parallel_guard(|guard| { - rayon::join( + rayon_core::join( move || guard.run(move || FromDyn::from(oper_a.into_inner()())), move || guard.run(move || FromDyn::from(oper_b.into_inner()())), ) @@ -125,58 +135,115 @@ where } } -pub fn par_for_each_in + IntoParallelIterator>( +fn par_slice( + items: &mut [I], + guard: &ParallelGuard, + for_each: impl Fn(&mut I) + DynSync + DynSend, +) { + struct State<'a, F> { + for_each: FromDyn, + guard: &'a ParallelGuard, + group: usize, + } + + fn par_rec( + items: &mut [I], + state: &State<'_, F>, + ) { + if items.len() <= state.group { + for item in items { + state.guard.run(|| (state.for_each)(item)); + } + } else { + let (left, right) = items.split_at_mut(items.len() / 2); + let mut left = state.for_each.derive(left); + let mut right = state.for_each.derive(right); + rayon_core::join(move || par_rec(*left, state), move || par_rec(*right, state)); + } + } + + let state = State { + for_each: FromDyn::from(for_each), + guard, + group: std::cmp::max(items.len() / 128, 1), + }; + par_rec(items, &state) +} + +pub fn par_for_each_in>( t: T, - for_each: impl Fn(I) + DynSync + DynSend, + for_each: impl Fn(&I) + DynSync + DynSend, ) { parallel_guard(|guard| { if mode::is_dyn_thread_safe() { - let for_each = FromDyn::from(for_each); - t.into_par_iter().for_each(|i| { - guard.run(|| for_each(i)); - }); + let mut items: Vec<_> = t.into_iter().collect(); + par_slice(&mut items, guard, |i| for_each(&*i)) } else { t.into_iter().for_each(|i| { - guard.run(|| for_each(i)); + guard.run(|| for_each(&i)); }); } }); } -pub fn try_par_for_each_in< - T: IntoIterator + IntoParallelIterator::Item>, - E: Send, ->( +/// This runs `for_each` in parallel for each iterator item. If one or more of the +/// `for_each` calls returns `Err`, the function will also return `Err`. The error returned +/// will be non-deterministic, but this is expected to be used with `ErrorGuaranteed` which +/// are all equivalent. +pub fn try_par_for_each_in( t: T, - for_each: impl Fn(::Item) -> Result<(), E> + DynSync + DynSend, -) -> Result<(), E> { + for_each: impl Fn(&::Item) -> Result<(), E> + DynSync + DynSend, +) -> Result<(), E> +where + ::Item: DynSend, +{ parallel_guard(|guard| { if mode::is_dyn_thread_safe() { - let for_each = FromDyn::from(for_each); - t.into_par_iter() - .filter_map(|i| guard.run(|| for_each(i))) - .reduce(|| Ok(()), Result::and) + let mut items: Vec<_> = t.into_iter().collect(); + + let error = Mutex::new(None); + + par_slice(&mut items, guard, |i| { + if let Err(err) = for_each(&*i) { + *error.lock() = Some(err); + } + }); + + if let Some(err) = error.into_inner() { Err(err) } else { Ok(()) } } else { - t.into_iter().filter_map(|i| guard.run(|| for_each(i))).fold(Ok(()), Result::and) + t.into_iter().filter_map(|i| guard.run(|| for_each(&i))).fold(Ok(()), Result::and) } }) } -pub fn par_map< - I, - T: IntoIterator + IntoParallelIterator, - R: std::marker::Send, - C: FromIterator + FromParallelIterator, ->( +pub fn par_map, R: DynSend, C: FromIterator>( t: T, map: impl Fn(I) -> R + DynSync + DynSend, ) -> C { parallel_guard(|guard| { if mode::is_dyn_thread_safe() { let map = FromDyn::from(map); - t.into_par_iter().filter_map(|i| guard.run(|| map(i))).collect() + + let mut items: Vec<(Option, Option)> = + t.into_iter().map(|i| (Some(i), None)).collect(); + + par_slice(&mut items, guard, |i| { + i.1 = Some(map(i.0.take().unwrap())); + }); + + items.into_iter().filter_map(|i| i.1).collect() } else { t.into_iter().filter_map(|i| guard.run(|| map(i))).collect() } }) } + +pub fn broadcast(op: impl Fn(usize) -> R + DynSync) -> Vec { + if mode::is_dyn_thread_safe() { + let op = FromDyn::from(op); + let results = rayon_core::broadcast(|context| op.derive(op(context.index()))); + results.into_iter().map(|r| r.into_inner()).collect() + } else { + vec![op(0)] + } +} diff --git a/compiler/rustc_data_structures/src/sync/worker_local.rs b/compiler/rustc_data_structures/src/sync/worker_local.rs index 402ec9827bbaa..d75af00985047 100644 --- a/compiler/rustc_data_structures/src/sync/worker_local.rs +++ b/compiler/rustc_data_structures/src/sync/worker_local.rs @@ -106,6 +106,12 @@ pub struct WorkerLocal { registry: Registry, } +// This is safe because the `deref` call will return a reference to a `T` unique to each thread +// or it will panic for threads without an associated local. So there isn't a need for `T` to do +// it's own synchronization. The `verify` method on `RegistryId` has an issue where the id +// can be reused, but `WorkerLocal` has a reference to `Registry` which will prevent any reuse. +unsafe impl Sync for WorkerLocal {} + impl WorkerLocal { /// Creates a new worker local where the `initial` closure computes the /// value this worker local should take for each thread in the registry. @@ -132,11 +138,6 @@ impl Deref for WorkerLocal { fn deref(&self) -> &T { // This is safe because `verify` will only return values less than // `self.registry.thread_limit` which is the size of the `self.locals` array. - - // The `deref` call will return a reference to a `T` unique to each thread - // or it will panic for threads without an associated local. So there isn't a need for `T` to do - // it's own synchronization. The `verify` method on `RegistryId` has an issue where the id - // can be reused, but `WorkerLocal` has a reference to `Registry` which will prevent any reuse. unsafe { &self.locals.get_unchecked(self.registry.id().verify()).0 } } } diff --git a/compiler/rustc_data_structures/src/tagged_ptr/tests.rs b/compiler/rustc_data_structures/src/tagged_ptr/tests.rs index 9c1e4cefa6923..85b21a7c8ecdf 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr/tests.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr/tests.rs @@ -7,7 +7,7 @@ use crate::stable_hasher::{HashStable, StableHasher}; /// A tag type used in [`TaggedRef`] tests. #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Tag2 { +enum Tag2 { B00 = 0b00, B01 = 0b01, B10 = 0b10, diff --git a/compiler/rustc_data_structures/src/temp_dir.rs b/compiler/rustc_data_structures/src/temp_dir.rs index 4dbe11d707d77..9adae049261e4 100644 --- a/compiler/rustc_data_structures/src/temp_dir.rs +++ b/compiler/rustc_data_structures/src/temp_dir.rs @@ -17,7 +17,7 @@ impl Drop for MaybeTempDir { // occur. let dir = unsafe { ManuallyDrop::take(&mut self.dir) }; if self.keep { - let _ = dir.into_path(); + let _ = dir.keep(); } } } diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs index 34895d3efe6ca..3d44fb1fd48db 100644 --- a/compiler/rustc_data_structures/src/unord.rs +++ b/compiler/rustc_data_structures/src/unord.rs @@ -9,7 +9,7 @@ use std::iter::{Product, Sum}; use std::ops::Index; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_macros::{Decodable_Generic, Encodable_Generic}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext}; use crate::fingerprint::Fingerprint; use crate::stable_hasher::{HashStable, StableCompare, StableHasher, ToStableHashKey}; @@ -109,6 +109,16 @@ impl> UnordItems { pub fn collect>>(self) -> C { self.into() } + + /// If the iterator has only one element, returns it, otherwise returns `None`. + #[track_caller] + pub fn get_only(mut self) -> Option { + let item = self.0.next(); + if self.0.next().is_some() { + return None; + } + item + } } impl UnordItems> { @@ -224,7 +234,7 @@ trait UnordCollection {} /// /// See [MCP 533](https://github.com/rust-lang/compiler-team/issues/533) /// for more information. -#[derive(Debug, Eq, PartialEq, Clone, Encodable_Generic, Decodable_Generic)] +#[derive(Debug, Eq, PartialEq, Clone, Encodable_NoContext, Decodable_NoContext)] pub struct UnordSet { inner: FxHashSet, } @@ -259,6 +269,12 @@ impl UnordSet { self.inner.is_empty() } + /// If the set has only one element, returns it, otherwise returns `None`. + #[inline] + pub fn get_only(&self) -> Option<&V> { + if self.inner.len() == 1 { self.inner.iter().next() } else { None } + } + #[inline] pub fn insert(&mut self, v: V) -> bool { self.inner.insert(v) @@ -415,7 +431,7 @@ impl> HashStable for UnordSet { /// /// See [MCP 533](https://github.com/rust-lang/compiler-team/issues/533) /// for more information. -#[derive(Debug, Eq, PartialEq, Clone, Encodable_Generic, Decodable_Generic)] +#[derive(Debug, Eq, PartialEq, Clone, Encodable_NoContext, Decodable_NoContext)] pub struct UnordMap { inner: FxHashMap, } @@ -639,7 +655,7 @@ impl, V: HashStable> HashStable fo /// /// See [MCP 533](https://github.com/rust-lang/compiler-team/issues/533) /// for more information. -#[derive(Default, Debug, Eq, PartialEq, Clone, Encodable_Generic, Decodable_Generic)] +#[derive(Default, Debug, Eq, PartialEq, Clone, Encodable_NoContext, Decodable_NoContext)] pub struct UnordBag { inner: Vec, } diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 8593d1faba264..1971d06aad64d 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.0" edition = "2024" [dependencies] +jiff = { version = "0.2.5", default-features = false, features = ["std"] } # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } @@ -43,24 +44,23 @@ rustc_privacy = { path = "../rustc_privacy" } rustc_query_system = { path = "../rustc_query_system" } rustc_resolve = { path = "../rustc_resolve" } rustc_session = { path = "../rustc_session" } -rustc_smir = { path = "../rustc_smir" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_ty_utils = { path = "../rustc_ty_utils" } serde_json = "1.0.59" shlex = "1.0" -time = { version = "0.3.36", default-features = false, features = ["alloc", "formatting", "macros"] } +stable_mir = { path = "../stable_mir", features = ["rustc_internal"] } tracing = { version = "0.1.35" } # tidy-alphabetical-end -[target.'cfg(unix)'.dependencies] +[target.'cfg(all(unix, any(target_env = "gnu", target_os = "macos")))'.dependencies] # tidy-alphabetical-start libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_System_Diagnostics_Debug", ] diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 05e11c4527f81..2c6a0291ac29b 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -1,3 +1,5 @@ +driver_impl_cant_emit_mir = could not emit MIR: {$error} + driver_impl_ice = the compiler unexpectedly panicked. this is a bug. driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url} driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index a2ddff7183e60..056c476d5e120 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -10,17 +10,15 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(decl_macro)] -#![feature(let_chains)] #![feature(panic_backtrace_config)] #![feature(panic_update_hook)] #![feature(result_flattening)] #![feature(rustdoc_internals)] #![feature(try_blocks)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use std::cmp::max; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::ffi::OsString; use std::fmt::Write as _; use std::fs::{self, File}; @@ -30,11 +28,10 @@ use std::path::{Path, PathBuf}; use std::process::{self, Command, Stdio}; use std::sync::OnceLock; use std::sync::atomic::{AtomicBool, Ordering}; -use std::time::{Instant, SystemTime}; +use std::time::Instant; use std::{env, str}; use rustc_ast as ast; -use rustc_codegen_ssa::back::apple; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CodegenErrors, CodegenResults}; use rustc_data_structures::profiling::{ @@ -44,6 +41,10 @@ use rustc_errors::emitter::stderr_destination; use rustc_errors::registry::Registry; use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown}; use rustc_feature::find_gated_cfg; +// This avoids a false positive with `-Wunused_crate_dependencies`. +// `rust_index` isn't used in this crate's code, but it must be named in the +// `Cargo.toml` for the `rustc_randomized_layouts` feature. +use rustc_index as _; use rustc_interface::util::{self, get_codegen_backend}; use rustc_interface::{Linker, create_and_enter_global_ctxt, interface, passes}; use rustc_lint::unerased_lint_store; @@ -52,18 +53,17 @@ use rustc_metadata::locator; use rustc_middle::ty::TyCtxt; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::config::{ - CG_OPTIONS, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, UnstableOptions, - Z_OPTIONS, nightly_options, parse_target_triple, + CG_OPTIONS, CrateType, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, + UnstableOptions, Z_OPTIONS, nightly_options, parse_target_triple, }; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; -use rustc_session::output::collect_crate_types; +use rustc_session::output::{CRATE_TYPES, collect_crate_types, invalid_output_for_target}; use rustc_session::{EarlyDiagCtxt, Session, config, filesearch}; use rustc_span::FileName; +use rustc_span::def_id::LOCAL_CRATE; use rustc_target::json::ToJson; use rustc_target::spec::{Target, TargetTuple}; -use time::OffsetDateTime; -use time::macros::format_description; use tracing::trace; #[allow(unused_macros)] @@ -89,6 +89,10 @@ pub mod pretty; #[macro_use] mod print; mod session_diagnostics; + +// Keep the OS parts of this `cfg` in sync with the `cfg` on the `libc` +// dependency in `compiler/rustc_driver/Cargo.toml`, to keep +// `-Wunused-crated-dependencies` satisfied. #[cfg(all(not(miri), unix, any(target_env = "gnu", target_os = "macos")))] mod signal_handler; @@ -100,7 +104,7 @@ mod signal_handler { } use crate::session_diagnostics::{ - RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch, + CantEmitMIR, RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch, RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, UnstableFeatureUsage, }; @@ -235,12 +239,17 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) return; } + let input = make_input(&default_early_dcx, &matches.free); + let has_input = input.is_some(); let (odir, ofile) = make_output(&matches); + + drop(default_early_dcx); + let mut config = interface::Config { opts: sopts, crate_cfg: matches.opt_strs("cfg"), crate_check_cfg: matches.opt_strs("check-cfg"), - input: Input::File(PathBuf::new()), + input: input.unwrap_or(Input::File(PathBuf::new())), output_file: ofile, output_dir: odir, ice_file, @@ -251,22 +260,13 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) hash_untracked_state: None, register_lints: None, override_queries: None, + extra_symbols: Vec::new(), make_codegen_backend: None, registry: diagnostics_registry(), using_internal_features: &USING_INTERNAL_FEATURES, expanded_args: args, }; - let has_input = match make_input(&default_early_dcx, &matches.free) { - Some(input) => { - config.input = input; - true // has input: normal compilation - } - None => false, // no input: we will exit early - }; - - drop(default_early_dcx); - callbacks.config(&mut config); let registered_lints = config.register_lints.is_some(); @@ -345,16 +345,14 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) // Make sure name resolution and macro expansion is run. let _ = tcx.resolver_for_lowering(); - if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { - dump_feature_usage_metrics(tcx, metrics_dir); - } - if callbacks.after_expansion(compiler, tcx) == Compilation::Stop { return early_exit(); } passes::write_dep_info(tcx); + passes::write_interface(tcx); + if sess.opts.output_types.contains_key(&OutputType::DepInfo) && sess.opts.output_types.len() == 1 { @@ -367,10 +365,20 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) tcx.ensure_ok().analysis(()); + if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { + dump_feature_usage_metrics(tcx, metrics_dir); + } + if callbacks.after_analysis(compiler, tcx) == Compilation::Stop { return early_exit(); } + if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) { + if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx) { + tcx.dcx().emit_fatal(CantEmitMIR { error }); + } + } + Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)) }); @@ -383,14 +391,10 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) } fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &Path) { - let output_filenames = tcxt.output_filenames(()); - let mut metrics_file_name = std::ffi::OsString::from("unstable_feature_usage_metrics-"); - let mut metrics_path = output_filenames.with_directory_and_extension(metrics_dir, "json"); - let metrics_file_stem = - metrics_path.file_name().expect("there should be a valid default output filename"); - metrics_file_name.push(metrics_file_stem); - metrics_path.pop(); - metrics_path.push(metrics_file_name); + let hash = tcxt.crate_hash(LOCAL_CRATE); + let crate_name = tcxt.crate_name(LOCAL_CRATE); + let metrics_file_name = format!("unstable_feature_usage_metrics-{crate_name}-{hash}.json"); + let metrics_path = metrics_dir.join(metrics_file_name); if let Err(error) = tcxt.features().dump_feature_usage_metrics(metrics_path) { // FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit // default metrics" to only produce a warning when metrics are enabled by default and emit @@ -399,7 +403,7 @@ fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &Path) { } } -// Extract output directory and file from matches. +/// 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| match o.as_str() { @@ -457,8 +461,8 @@ pub enum Compilation { fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, color: ColorConfig) { // Allow "E0123" or "0123" form. let upper_cased_code = code.to_ascii_uppercase(); - let start = if upper_cased_code.starts_with('E') { 1 } else { 0 }; - if let Ok(code) = upper_cased_code[start..].parse::() + if let Ok(code) = upper_cased_code.strip_prefix('E').unwrap_or(&upper_cased_code).parse::() + && code <= ErrCode::MAX_AS_U32 && let Ok(description) = registry.try_find_description(ErrCode::from_u32(code)) { let mut is_in_code_block = false; @@ -649,10 +653,10 @@ fn print_crate_info( HostTuple => println_info!("{}", rustc_session::config::host_tuple()), Sysroot => println_info!("{}", sess.sysroot.display()), TargetLibdir => println_info!("{}", sess.target_tlib_path.dir.display()), - TargetSpec => { + TargetSpecJson => { println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap()); } - AllTargetSpecs => { + AllTargetSpecsJson => { let mut targets = BTreeMap::new(); for name in rustc_target::spec::TARGETS { let triple = TargetTuple::from_tuple(name); @@ -683,6 +687,34 @@ fn print_crate_info( }; println_info!("{}", passes::get_crate_name(sess, attrs)); } + CrateRootLintLevels => { + let Some(attrs) = attrs.as_ref() else { + // no crate attributes, print out an error and exit + return Compilation::Continue; + }; + let crate_name = passes::get_crate_name(sess, attrs); + let lint_store = crate::unerased_lint_store(sess); + let registered_tools = rustc_resolve::registered_tools_ast(sess.dcx(), attrs); + let features = rustc_expand::config::features(sess, attrs, crate_name); + let lint_levels = rustc_lint::LintLevelsBuilder::crate_root( + sess, + &features, + true, + lint_store, + ®istered_tools, + attrs, + ); + for lint in lint_store.get_lints() { + if let Some(feature_symbol) = lint.feature_gate + && !features.enabled(feature_symbol) + { + // lint is unstable and feature gate isn't active, don't print + continue; + } + let level = lint_levels.lint_level(lint).level; + println_info!("{}={}", lint.name_lower(), level.as_str()); + } + } Cfg => { let mut cfgs = sess .psess @@ -771,17 +803,28 @@ fn print_crate_info( } } DeploymentTarget => { - if sess.target.is_like_osx { + if sess.target.is_like_darwin { println_info!( "{}={}", - apple::deployment_target_env_var(&sess.target.os), - apple::pretty_version(apple::deployment_target(sess)), + rustc_target::spec::apple::deployment_target_env_var(&sess.target.os), + sess.apple_deployment_target().fmt_pretty(), ) } else { #[allow(rustc::diagnostic_outside_of_impl)] sess.dcx().fatal("only Apple targets currently support deployment version info") } } + SupportedCrateTypes => { + let supported_crate_types = CRATE_TYPES + .iter() + .filter(|(_, crate_type)| !invalid_output_for_target(&sess, *crate_type)) + .filter(|(_, crate_type)| *crate_type != CrateType::Sdylib) + .map(|(crate_type_sym, _)| *crate_type_sym) + .collect::>(); + for supported_crate_type in supported_crate_types { + println_info!("{}", supported_crate_type.as_str()); + } + } } req.out.overwrite(&crate_info, sess); @@ -1258,13 +1301,8 @@ fn ice_path_with_config(config: Option<&UnstableOptions>) -> &'static Option(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { let tcx = ex.tcx(); let f = |annotation: &dyn pprust_hir::PpAnn| { let sm = sess.source_map(); - let hir_map = tcx.hir(); - let attrs = |id| hir_map.attrs(id); + let attrs = |id| tcx.hir_attrs(id); pprust_hir::print_crate( sm, tcx.hir_root_module(), diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index e06c56539d1ca..774221fd396a6 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -2,6 +2,12 @@ use std::error::Error; use rustc_macros::{Diagnostic, Subdiagnostic}; +#[derive(Diagnostic)] +#[diag(driver_impl_cant_emit_mir)] +pub struct CantEmitMIR { + pub error: std::io::Error, +} + #[derive(Diagnostic)] #[diag(driver_impl_rlink_unable_to_read)] pub(crate) struct RlinkUnableToRead { diff --git a/compiler/rustc_error_codes/src/error_codes/E0092.md b/compiler/rustc_error_codes/src/error_codes/E0092.md index 84ec0656d1ac1..be459d040c28d 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0092.md +++ b/compiler/rustc_error_codes/src/error_codes/E0092.md @@ -6,10 +6,9 @@ Erroneous code example: #![feature(intrinsics)] #![allow(internal_features)] -extern "rust-intrinsic" { - fn atomic_foo(); // error: unrecognized atomic operation - // function -} +#[rustc_intrinsic] +unsafe fn atomic_foo(); // error: unrecognized atomic operation + // function ``` Please check you didn't make a mistake in the function's name. All intrinsic @@ -20,7 +19,6 @@ functions are defined in `compiler/rustc_codegen_llvm/src/intrinsic.rs` and in #![feature(intrinsics)] #![allow(internal_features)] -extern "rust-intrinsic" { - fn atomic_fence_seqcst(); // ok! -} +#[rustc_intrinsic] +unsafe fn atomic_fence_seqcst(); // ok! ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0093.md b/compiler/rustc_error_codes/src/error_codes/E0093.md index 2bda4d74f726d..9929a0699273d 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0093.md +++ b/compiler/rustc_error_codes/src/error_codes/E0093.md @@ -6,9 +6,8 @@ Erroneous code example: #![feature(intrinsics)] #![allow(internal_features)] -extern "rust-intrinsic" { - fn foo(); // error: unrecognized intrinsic function: `foo` -} +#[rustc_intrinsic] +unsafe fn foo(); // error: unrecognized intrinsic function: `foo` fn main() { unsafe { @@ -25,9 +24,8 @@ functions are defined in `compiler/rustc_codegen_llvm/src/intrinsic.rs` and in #![feature(intrinsics)] #![allow(internal_features)] -extern "rust-intrinsic" { - fn atomic_fence_seqcst(); // ok! -} +#[rustc_intrinsic] +unsafe fn atomic_fence_seqcst(); // ok! fn main() { unsafe { diff --git a/compiler/rustc_error_codes/src/error_codes/E0207.md b/compiler/rustc_error_codes/src/error_codes/E0207.md index 5b35748f4723c..f80b0093ecc53 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0207.md +++ b/compiler/rustc_error_codes/src/error_codes/E0207.md @@ -195,7 +195,7 @@ impl<'a> Contains for Foo { Please note that unconstrained lifetime parameters are not supported if they are being used by an associated type. -In cases where the associated type's lifetime is meant to be tied to the the +In cases where the associated type's lifetime is meant to be tied to the self type, and none of the methods on the trait need ownership or different mutability, then an option is to implement the trait on a borrowed type: diff --git a/compiler/rustc_error_codes/src/error_codes/E0211.md b/compiler/rustc_error_codes/src/error_codes/E0211.md index 7aa42628549f8..c702f14d4d6d4 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0211.md +++ b/compiler/rustc_error_codes/src/error_codes/E0211.md @@ -7,9 +7,8 @@ used. Erroneous code examples: #![feature(intrinsics)] #![allow(internal_features)] -extern "rust-intrinsic" { - fn unreachable(); // error: intrinsic has wrong type -} +#[rustc_intrinsic] +unsafe fn unreachable(); // error: intrinsic has wrong type // or: @@ -43,9 +42,8 @@ For the first code example, please check the function definition. Example: #![feature(intrinsics)] #![allow(internal_features)] -extern "rust-intrinsic" { - fn unreachable() -> !; // ok! -} +#[rustc_intrinsic] +unsafe fn unreachable() -> !; // ok! ``` The second case example is a bit particular: the main function must always diff --git a/compiler/rustc_error_codes/src/error_codes/E0253.md b/compiler/rustc_error_codes/src/error_codes/E0253.md index 705d1bfc53e59..628f5e252fbb6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0253.md +++ b/compiler/rustc_error_codes/src/error_codes/E0253.md @@ -1,9 +1,13 @@ +#### Note: this error code is no longer emitted by the compiler. + Attempt was made to import an unimportable type. This can happen when trying to import a type from a trait. Erroneous code example: -```compile_fail,E0253 +``` +#![feature(import_trait_associated_functions)] + mod foo { pub trait MyTrait { type SomeType; diff --git a/compiler/rustc_error_codes/src/error_codes/E0373.md b/compiler/rustc_error_codes/src/error_codes/E0373.md index d4d26007aa50e..b9db807259728 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0373.md +++ b/compiler/rustc_error_codes/src/error_codes/E0373.md @@ -3,7 +3,7 @@ A captured variable in a closure may not live long enough. Erroneous code example: ```compile_fail,E0373 -fn foo() -> Box u32> { +fn foo() -> Box u32> { let x = 0u32; Box::new(|y| x + y) } @@ -42,7 +42,7 @@ This approach moves (or copies, where possible) data into the closure, rather than taking references to it. For example: ``` -fn foo() -> Box u32> { +fn foo() -> Box u32> { let x = 0u32; Box::new(move |y| x + y) } diff --git a/compiler/rustc_error_codes/src/error_codes/E0511.md b/compiler/rustc_error_codes/src/error_codes/E0511.md index 45ff49bdebb29..0fb1cfda67dc6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0511.md +++ b/compiler/rustc_error_codes/src/error_codes/E0511.md @@ -5,9 +5,8 @@ Erroneous code example: ```compile_fail,E0511 #![feature(intrinsics)] -extern "rust-intrinsic" { - fn simd_add(a: T, b: T) -> T; -} +#[rustc_intrinsic] +unsafe fn simd_add(a: T, b: T) -> T; fn main() { unsafe { simd_add(0, 1); } @@ -25,9 +24,8 @@ The generic type has to be a SIMD type. Example: #[derive(Copy, Clone)] struct i32x2([i32; 2]); -extern "rust-intrinsic" { - fn simd_add(a: T, b: T) -> T; -} +#[rustc_intrinsic] +unsafe fn simd_add(a: T, b: T) -> T; unsafe { simd_add(i32x2([0, 0]), i32x2([1, 2])); } // ok! ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0515.md b/compiler/rustc_error_codes/src/error_codes/E0515.md index 0f4fbf672239c..87bbe4aea705f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0515.md +++ b/compiler/rustc_error_codes/src/error_codes/E0515.md @@ -17,10 +17,13 @@ fn get_dangling_iterator<'a>() -> Iter<'a, i32> { } ``` -Local variables, function parameters and temporaries are all dropped before the -end of the function body. So a reference to them cannot be returned. +Local variables, function parameters and temporaries are all dropped before +the end of the function body. A returned reference (or struct containing a +reference) to such a dropped value would immediately be invalid. Therefore +it is not allowed to return such a reference. -Consider returning an owned value instead: +Consider returning a value that takes ownership of local data instead of +referencing it: ``` use std::vec::IntoIter; diff --git a/compiler/rustc_error_codes/src/error_codes/E0622.md b/compiler/rustc_error_codes/src/error_codes/E0622.md index 4cb605b636d2e..9b8131a061e39 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0622.md +++ b/compiler/rustc_error_codes/src/error_codes/E0622.md @@ -1,13 +1,16 @@ +#### Note: this error code is no longer emitted by the compiler. + An intrinsic was declared without being a function. Erroneous code example: -```compile_fail,E0622 +```no_run #![feature(intrinsics)] #![allow(internal_features)] -extern "rust-intrinsic" { - pub static atomic_singlethreadfence_seqcst: fn(); +extern "C" { + #[rustc_intrinsic] + pub static atomic_singlethreadfence_seqcst: unsafe fn(); // error: intrinsic must be a function } @@ -22,9 +25,8 @@ error, just declare a function. Example: #![feature(intrinsics)] #![allow(internal_features)] -extern "rust-intrinsic" { - pub fn atomic_singlethreadfence_seqcst(); // ok! -} +#[rustc_intrinsic] +pub unsafe fn atomic_singlethreadfence_seqcst(); // ok! fn main() { unsafe { atomic_singlethreadfence_seqcst(); } } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0736.md b/compiler/rustc_error_codes/src/error_codes/E0736.md index cb7633b7068a3..66d5fbb80cf29 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0736.md +++ b/compiler/rustc_error_codes/src/error_codes/E0736.md @@ -11,7 +11,7 @@ Erroneous code example: ```compile_fail,E0736 #[inline] -#[naked] +#[unsafe(naked)] fn foo() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0755.md b/compiler/rustc_error_codes/src/error_codes/E0755.md index 88b7f48496906..b67f078c78ec7 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0755.md +++ b/compiler/rustc_error_codes/src/error_codes/E0755.md @@ -5,7 +5,7 @@ Erroneous code example: ```compile_fail,E0755 #![feature(ffi_pure)] -#[ffi_pure] // error! +#[unsafe(ffi_pure)] // error! pub fn foo() {} # fn main() {} ``` @@ -17,7 +17,7 @@ side effects or infinite loops: #![feature(ffi_pure)] extern "C" { - #[ffi_pure] // ok! + #[unsafe(ffi_pure)] // ok! pub fn strlen(s: *const i8) -> isize; } # fn main() {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0756.md b/compiler/rustc_error_codes/src/error_codes/E0756.md index ffdc421aab584..aadde038d12c9 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0756.md +++ b/compiler/rustc_error_codes/src/error_codes/E0756.md @@ -6,7 +6,7 @@ Erroneous code example: ```compile_fail,E0756 #![feature(ffi_const)] -#[ffi_const] // error! +#[unsafe(ffi_const)] // error! pub fn foo() {} # fn main() {} ``` @@ -18,7 +18,7 @@ which have no side effects except for their return value: #![feature(ffi_const)] extern "C" { - #[ffi_const] // ok! + #[unsafe(ffi_const)] // ok! pub fn strlen(s: *const i8) -> i32; } # fn main() {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0757.md b/compiler/rustc_error_codes/src/error_codes/E0757.md index 41b06b23c4f2b..fb75b028f45c8 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0757.md +++ b/compiler/rustc_error_codes/src/error_codes/E0757.md @@ -6,8 +6,9 @@ Erroneous code example: #![feature(ffi_const, ffi_pure)] extern "C" { - #[ffi_const] - #[ffi_pure] // error: `#[ffi_const]` function cannot be `#[ffi_pure]` + #[unsafe(ffi_const)] + #[unsafe(ffi_pure)] + //~^ ERROR `#[ffi_const]` function cannot be `#[ffi_pure]` pub fn square(num: i32) -> i32; } ``` @@ -19,7 +20,7 @@ As `ffi_const` provides stronger guarantees than `ffi_pure`, remove the #![feature(ffi_const)] extern "C" { - #[ffi_const] + #[unsafe(ffi_const)] pub fn square(num: i32) -> i32; } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0773.md b/compiler/rustc_error_codes/src/error_codes/E0773.md index aa65a475a4f9d..5ebb43c6683e5 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0773.md +++ b/compiler/rustc_error_codes/src/error_codes/E0773.md @@ -1,40 +1,4 @@ -A builtin-macro was defined more than once. +#### this error code is no longer emitted by the compiler. -Erroneous code example: - -```compile_fail,E0773 -#![feature(decl_macro)] -#![feature(rustc_attrs)] -#![allow(internal_features)] - -#[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)] -#![allow(internal_features)] - -#[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 +This was triggered when multiple macro definitions used the same +`#[rustc_builtin_macro(..)]`. This is no longer an error. diff --git a/compiler/rustc_error_codes/src/error_codes/E0787.md b/compiler/rustc_error_codes/src/error_codes/E0787.md index f5c5faa066b6b..b7f92c8feb587 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0787.md +++ b/compiler/rustc_error_codes/src/error_codes/E0787.md @@ -3,9 +3,7 @@ An unsupported naked function definition. Erroneous code example: ```compile_fail,E0787 -#![feature(naked_functions)] - -#[naked] +#[unsafe(naked)] pub extern "C" fn f() -> u32 { 42 } diff --git a/compiler/rustc_error_codes/src/error_codes/E0789.md b/compiler/rustc_error_codes/src/error_codes/E0789.md index 2c0018cc799f4..c7bc6cfde5134 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0789.md +++ b/compiler/rustc_error_codes/src/error_codes/E0789.md @@ -14,7 +14,7 @@ Erroneous code example: #![unstable(feature = "foo_module", reason = "...", issue = "123")] -#[rustc_allowed_through_unstable_modules] +#[rustc_allowed_through_unstable_modules = "deprecation message"] // #[stable(feature = "foo", since = "1.0")] struct Foo; // ^^^ error: `rustc_allowed_through_unstable_modules` attribute must be diff --git a/compiler/rustc_error_codes/src/error_codes/E0792.md b/compiler/rustc_error_codes/src/error_codes/E0792.md index 5e3dcc4aa7277..033e1c65192ba 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0792.md +++ b/compiler/rustc_error_codes/src/error_codes/E0792.md @@ -7,6 +7,7 @@ This means type Foo = impl std::fmt::Debug; +#[define_opaque(Foo)] fn foo() -> Foo { 5u32 } @@ -19,6 +20,7 @@ is not accepted. If it were accepted, one could create unsound situations like type Foo = impl Default; +#[define_opaque(Foo)] fn foo() -> Foo { 5u32 } @@ -36,6 +38,7 @@ Instead you need to make the function generic: type Foo = impl std::fmt::Debug; +#[define_opaque(Foo)] fn foo() -> Foo { 5u32 } @@ -56,6 +59,7 @@ use std::fmt::Debug; type Foo = impl Debug; +#[define_opaque(Foo)] fn foo() -> Foo { Vec::::new() } diff --git a/compiler/rustc_error_codes/src/error_codes/E0804.md b/compiler/rustc_error_codes/src/error_codes/E0804.md new file mode 100644 index 0000000000000..9a6937c0b52e0 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0804.md @@ -0,0 +1,41 @@ +An auto trait cannot be added to the bounds of a `dyn Trait` type via +a pointer cast. + +Erroneous code example: + +```rust,edition2021,compile_fail,E0804 +let ptr: *const dyn core::any::Any = &(); +_ = ptr as *const (dyn core::any::Any + Send); +``` + +Adding an auto trait can make the vtable invalid, potentially causing +UB in safe code afterwards. For example: + +```rust,edition2021,no_run +use core::{mem::transmute, ptr::NonNull}; + +trait Trait { + fn f(&self) + where + Self: Send; +} + +impl Trait for NonNull<()> { + fn f(&self) { + unreachable!() + } +} + +fn main() { + let unsend: &dyn Trait = &NonNull::dangling(); + let bad: &(dyn Trait + Send) = unsafe { transmute(unsend) }; + // This crashes, since the vtable for `NonNull as dyn Trait` does + // not have an entry for `Trait::f`. + bad.f(); +} +``` + +To fix this error, you can use `transmute` rather than pointer casts, +but you must ensure that the vtable is valid for the pointer's type +before calling a method on the trait object or allowing other code to +do so. diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 098ca42be2b44..2488d870899ce 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -6,7 +6,6 @@ #![deny(rustdoc::invalid_codeblock_attributes)] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end // This higher-order macro defines the error codes that are in use. It is used @@ -398,7 +397,7 @@ E0618: 0618, E0619: 0619, E0620: 0620, E0621: 0621, -E0622: 0622, +E0622: 0622, // REMOVED: rustc-intrinsic ABI was removed E0623: 0623, E0624: 0624, E0625: 0625, @@ -547,6 +546,7 @@ E0800: 0800, E0801: 0801, E0802: 0802, E0803: 0803, +E0804: 0804, ); ) } diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index ba1c3e185c2d9..3c6df147b1ba5 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -4,7 +4,6 @@ #![feature(rustc_attrs)] #![feature(rustdoc_internals)] #![feature(type_alias_impl_trait)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use std::borrow::Cow; @@ -107,8 +106,8 @@ impl From> for TranslationBundleError { /// (overriding any conflicting messages). #[instrument(level = "trace")] pub fn fluent_bundle( - mut user_provided_sysroot: Option, - mut sysroot_candidates: Vec, + sysroot: PathBuf, + sysroot_candidates: Vec, requested_locale: Option, additional_ftl_path: Option<&Path>, with_directionality_markers: bool, @@ -142,7 +141,7 @@ pub fn fluent_bundle( // If the user requests the default locale then don't try to load anything. if let Some(requested_locale) = requested_locale { let mut found_resources = false; - for sysroot in user_provided_sysroot.iter_mut().chain(sysroot_candidates.iter_mut()) { + for mut sysroot in Some(sysroot).into_iter().chain(sysroot_candidates.into_iter()) { sysroot.push("share"); sysroot.push("locale"); sysroot.push(requested_locale.to_string()); @@ -209,6 +208,7 @@ pub type LazyFallbackBundle = Arc Fluent /// Return the default `FluentBundle` with standard "en-US" diagnostic messages. #[instrument(level = "trace", skip(resources))] +#[define_opaque(LazyFallbackBundle)] pub fn fallback_fluent_bundle( resources: Vec<&'static str>, with_directionality_markers: bool, diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index b11793c190a14..82e7468211db0 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -33,7 +33,7 @@ tracing = "0.1" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_Security", diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index f0636b600b70b..f3aeb8d224b9d 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -91,13 +91,13 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::Level { Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => { annotate_snippets::Level::Error } - Level::ForceWarning(_) | Level::Warning => annotate_snippets::Level::Warning, + Level::ForceWarning | Level::Warning => annotate_snippets::Level::Warning, Level::Note | Level::OnceNote => annotate_snippets::Level::Note, Level::Help | Level::OnceHelp => annotate_snippets::Level::Help, // FIXME(#59346): Not sure how to map this level Level::FailureNote => annotate_snippets::Level::Error, Level::Allow => panic!("Should not call with Allow"), - Level::Expect(_) => panic!("Should not call with Expect"), + Level::Expect => panic!("Should not call with Expect"), } } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 7fffeaddb866d..539ecfbb42e2c 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -9,7 +9,7 @@ use std::thread::panicking; use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::{FluentValue, fluent_value_from_str_list_sep_by_and}; -use rustc_lint_defs::Applicability; +use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Span, Symbol}; @@ -181,22 +181,9 @@ where Self: Sized, { /// Add a subdiagnostic to an existing diagnostic. - fn add_to_diag(self, diag: &mut Diag<'_, G>) { - self.add_to_diag_with(diag, &|_, m| m); - } - - /// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used - /// (to optionally perform eager translation). - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ); + fn add_to_diag(self, diag: &mut Diag<'_, G>); } -pub trait SubdiagMessageOp = - Fn(&mut Diag<'_, G>, SubdiagMessage) -> SubdiagMessage; - /// Trait implemented by lint types. This should not be implemented manually. Instead, use /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. #[rustc_diagnostic_item = "LintDiagnostic"] @@ -296,6 +283,7 @@ pub struct DiagInner { pub messages: Vec<(DiagMessage, Style)>, pub code: Option, + pub lint_id: Option, pub span: MultiSpan, pub children: Vec, pub suggestions: Suggestions, @@ -324,6 +312,7 @@ impl DiagInner { pub fn new_with_messages(level: Level, messages: Vec<(DiagMessage, Style)>) -> Self { DiagInner { level, + lint_id: None, messages, code: None, span: MultiSpan::new(), @@ -346,7 +335,7 @@ impl DiagInner { match self.level { Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true, - Level::ForceWarning(_) + Level::ForceWarning | Level::Warning | Level::Note | Level::OnceNote @@ -354,7 +343,7 @@ impl DiagInner { | Level::OnceHelp | Level::FailureNote | Level::Allow - | Level::Expect(_) => false, + | Level::Expect => false, } } @@ -365,7 +354,7 @@ impl DiagInner { pub(crate) fn is_force_warn(&self) -> bool { match self.level { - Level::ForceWarning(_) => { + Level::ForceWarning => { assert!(self.is_lint.is_some()); true } @@ -490,7 +479,7 @@ pub struct Diag<'a, G: EmissionGuarantee = ErrorGuaranteed> { // would be bad. impl !Clone for Diag<'_, G> {} -rustc_data_structures::static_assert_size!(Diag<'_, ()>, 3 * std::mem::size_of::()); +rustc_data_structures::static_assert_size!(Diag<'_, ()>, 3 * size_of::()); impl Deref for Diag<'_, G> { type Target = DiagInner; @@ -645,9 +634,9 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { #[rustc_lint_diagnostics] pub fn note_expected_found( &mut self, - expected_label: &dyn fmt::Display, + expected_label: &str, expected: DiagStyledString, - found_label: &dyn fmt::Display, + found_label: &str, found: DiagStyledString, ) -> &mut Self { self.note_expected_found_extra( @@ -663,9 +652,9 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { #[rustc_lint_diagnostics] pub fn note_expected_found_extra( &mut self, - expected_label: &dyn fmt::Display, + expected_label: &str, expected: DiagStyledString, - found_label: &dyn fmt::Display, + found_label: &str, found: DiagStyledString, expected_extra: DiagStyledString, found_extra: DiagStyledString, @@ -1225,15 +1214,21 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// interpolated variables). #[rustc_lint_diagnostics] pub fn subdiagnostic(&mut self, subdiagnostic: impl Subdiagnostic) -> &mut Self { - let dcx = self.dcx; - subdiagnostic.add_to_diag_with(self, &|diag, msg| { - let args = diag.args.iter(); - let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); - dcx.eagerly_translate(msg, args) - }); + subdiagnostic.add_to_diag(self); self } + /// Fluent variables are not namespaced from each other, so when + /// `Diagnostic`s and `Subdiagnostic`s use the same variable name, + /// one value will clobber the other. Eagerly translating the + /// diagnostic uses the variables defined right then, before the + /// clobbering occurs. + pub fn eagerly_translate(&self, msg: impl Into) -> SubdiagMessage { + let args = self.args.iter(); + let msg = self.subdiagnostic_message_to_diagnostic_message(msg.into()); + self.dcx.eagerly_translate(msg, args) + } + with_fn! { with_span, /// Add a span. #[rustc_lint_diagnostics] @@ -1259,6 +1254,17 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } } + with_fn! { with_lint_id, + /// Add an argument. + #[rustc_lint_diagnostics] + pub fn lint_id( + &mut self, + id: LintExpectationId, + ) -> &mut Self { + self.lint_id = Some(id); + self + } } + with_fn! { with_primary_message, /// Add a primary message. #[rustc_lint_diagnostics] diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index cb2e1769fa1cf..8b59ba9984c10 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -19,7 +19,7 @@ use {rustc_ast as ast, rustc_hir as hir}; use crate::diagnostic::DiagLocation; use crate::{ Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, ErrCode, IntoDiagArg, Level, - SubdiagMessageOp, Subdiagnostic, fluent_generated as fluent, + Subdiagnostic, fluent_generated as fluent, }; pub struct DiagArgFromDisplay<'a>(pub &'a dyn fmt::Display); @@ -384,11 +384,7 @@ pub struct SingleLabelManySpans { pub label: &'static str, } impl Subdiagnostic for SingleLabelManySpans { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.span_labels(self.spans, self.label); } } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index f7f842393084b..fe01e289334cc 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -113,24 +113,11 @@ impl Margin { self.computed_left > 0 } - fn was_cut_right(&self, line_len: usize) -> bool { - let right = - if self.computed_right == self.span_right || self.computed_right == self.label_right { - // FIXME: This comment refers to the only callsite of this method. - // Rephrase it or refactor it, so it can stand on its own. - // Account for the "..." padding given above. Otherwise we end up with code lines - // that do fit but end in "..." as if they were trimmed. - // FIXME: Don't hard-code this offset. Is this meant to represent - // `2 * str_width(self.margin())`? - self.computed_right - 6 - } else { - self.computed_right - }; - right < line_len && self.computed_left + self.column_width < line_len - } - fn compute(&mut self, max_line_len: usize) { // When there's a lot of whitespace (>20), we want to trim it as it is useless. + // FIXME: this doesn't account for '\t', but to do so correctly we need to perform that + // calculation later, right before printing in order to be accurate with both unicode + // handling and trimming of long lines. self.computed_left = if self.whitespace_left > 20 { self.whitespace_left - 16 // We want some padding. } else { @@ -310,7 +297,9 @@ pub trait Emitter: Translate { // are some which do actually involve macros. ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None, - ExpnKind::Macro(macro_kind, name) => Some((macro_kind, name)), + ExpnKind::Macro(macro_kind, name) => { + Some((macro_kind, name, expn_data.hide_backtrace)) + } } }) .collect(); @@ -322,13 +311,17 @@ pub trait Emitter: Translate { self.render_multispans_macro_backtrace(span, children, backtrace); if !backtrace { - if let Some((macro_kind, name)) = has_macro_spans.first() { + // Skip builtin macros, as their expansion isn't relevant to the end user. This includes + // actual intrinsics, like `asm!`. + if let Some((macro_kind, name, _)) = has_macro_spans.first() + && let Some((_, _, false)) = has_macro_spans.last() + { // Mark the actual macro this originates from - let and_then = if let Some((macro_kind, last_name)) = has_macro_spans.last() + let and_then = if let Some((macro_kind, last_name, _)) = has_macro_spans.last() && last_name != name { let descr = macro_kind.descr(); - format!(" which comes from the expansion of the {descr} `{last_name}`",) + format!(" which comes from the expansion of the {descr} `{last_name}`") } else { "".to_string() }; @@ -616,7 +609,6 @@ pub struct HumanEmitter { #[setters(skip)] fallback_bundle: LazyFallbackBundle, short_message: bool, - teach: bool, ui_testing: bool, ignored_directories_in_source_blocks: Vec, diagnostic_width: Option, @@ -642,7 +634,6 @@ impl HumanEmitter { fluent_bundle: None, fallback_bundle, short_message: false, - teach: false, ui_testing: false, ignored_directories_in_source_blocks: Vec::new(), diagnostic_width: None, @@ -670,43 +661,43 @@ impl HumanEmitter { width_offset: usize, code_offset: usize, margin: Margin, - ) { - // Tabs are assumed to have been replaced by spaces in calling code. - debug_assert!(!source_string.contains('\t')); + ) -> usize { let line_len = source_string.len(); // Create the source line we will highlight. let left = margin.left(line_len); let right = margin.right(line_len); // FIXME: The following code looks fishy. See #132860. // On long lines, we strip the source line, accounting for unicode. - let mut taken = 0; let code: String = source_string .chars() - .skip(left) - .take_while(|ch| { - // Make sure that the trimming on the right will fall within the terminal width. - let next = char_width(*ch); - if taken + next > right - left { - return false; - } - taken += next; - true - }) + .enumerate() + .skip_while(|(i, _)| *i < left) + .take_while(|(i, _)| *i < right) + .map(|(_, c)| c) .collect(); + let code = normalize_whitespace(&code); + let was_cut_right = + source_string.chars().enumerate().skip_while(|(i, _)| *i < right).next().is_some(); buffer.puts(line_offset, code_offset, &code, Style::Quotation); let placeholder = self.margin(); if margin.was_cut_left() { // We have stripped some code/whitespace from the beginning, make it clear. buffer.puts(line_offset, code_offset, placeholder, Style::LineNumber); } - if margin.was_cut_right(line_len) { + if was_cut_right { let padding = str_width(placeholder); // We have stripped some code after the rightmost span end, make it clear we did so. - buffer.puts(line_offset, code_offset + taken - padding, placeholder, Style::LineNumber); + buffer.puts( + line_offset, + code_offset + str_width(&code) - padding, + placeholder, + Style::LineNumber, + ); } buffer.puts(line_offset, 0, &self.maybe_anonymized(line_index), Style::LineNumber); self.draw_col_separator_no_space(buffer, line_offset, width_offset - 2); + left } #[instrument(level = "trace", skip(self), ret)] @@ -738,22 +729,16 @@ impl HumanEmitter { return Vec::new(); } - let source_string = match file.get_line(line.line_index - 1) { - Some(s) => normalize_whitespace(&s), - None => return Vec::new(), + let Some(source_string) = file.get_line(line.line_index - 1) else { + return Vec::new(); }; trace!(?source_string); let line_offset = buffer.num_lines(); - // Left trim - let left = margin.left(source_string.len()); - + // Left trim. // FIXME: This looks fishy. See #132860. - // Account for unicode characters of width !=0 that were removed. - let left = source_string.chars().take(left).map(|ch| char_width(ch)).sum(); - - self.draw_line( + let left = self.draw_line( buffer, &source_string, line.line_index, @@ -784,7 +769,7 @@ impl HumanEmitter { let mut short_start = true; for ann in &line.annotations { if let AnnotationType::MultilineStart(depth) = ann.annotation_type { - if source_string.chars().take(ann.start_col.display).all(|c| c.is_whitespace()) { + if source_string.chars().take(ann.start_col.file).all(|c| c.is_whitespace()) { let uline = self.underline(ann.is_primary); let chr = uline.multiline_whole_line; annotations.push((depth, uline.style)); @@ -903,11 +888,16 @@ impl HumanEmitter { // | x_span // // + let mut overlap = vec![false; annotations.len()]; let mut annotations_position = vec![]; let mut line_len: usize = 0; let mut p = 0; for (i, annotation) in annotations.iter().enumerate() { for (j, next) in annotations.iter().enumerate() { + if overlaps(next, annotation, 0) && j > i { + overlap[i] = true; + overlap[j] = true; + } if overlaps(next, annotation, 0) // This label overlaps with another one and both && annotation.has_label() // take space (they have text and are not && j > i // multiline lines). @@ -1035,24 +1025,21 @@ impl HumanEmitter { let pos = pos + 1; match annotation.annotation_type { AnnotationType::MultilineStart(depth) | AnnotationType::MultilineEnd(depth) => { + let pre: usize = source_string + .chars() + .take(annotation.start_col.file) + .skip(left) + .map(|c| char_width(c)) + .sum(); self.draw_range( buffer, underline.multiline_horizontal, line_offset + pos, width_offset + depth, - (code_offset + annotation.start_col.display).saturating_sub(left), + code_offset + pre, underline.style, ); } - _ if self.teach => { - buffer.set_style_range( - line_offset, - (code_offset + annotation.start_col.display).saturating_sub(left), - (code_offset + annotation.end_col.display).saturating_sub(left), - underline.style, - annotation.is_primary, - ); - } _ => {} } } @@ -1072,11 +1059,18 @@ impl HumanEmitter { let underline = self.underline(annotation.is_primary); let pos = pos + 1; + let code_offset = code_offset + + source_string + .chars() + .take(annotation.start_col.file) + .skip(left) + .map(|c| char_width(c)) + .sum::(); if pos > 1 && (annotation.has_label() || annotation.takes_space()) { for p in line_offset + 1..=line_offset + pos { buffer.putc( p, - (code_offset + annotation.start_col.display).saturating_sub(left), + code_offset, match annotation.annotation_type { AnnotationType::MultilineLine(_) => underline.multiline_vertical, _ => underline.vertical_text_line, @@ -1087,7 +1081,7 @@ impl HumanEmitter { if let AnnotationType::MultilineStart(_) = annotation.annotation_type { buffer.putc( line_offset + pos, - (code_offset + annotation.start_col.display).saturating_sub(left), + code_offset, underline.bottom_right, underline.style, ); @@ -1097,7 +1091,7 @@ impl HumanEmitter { { buffer.putc( line_offset + pos, - (code_offset + annotation.start_col.display).saturating_sub(left), + code_offset, underline.multiline_bottom_right_with_text, underline.style, ); @@ -1155,13 +1149,30 @@ impl HumanEmitter { let style = if annotation.is_primary { Style::LabelPrimary } else { Style::LabelSecondary }; let (pos, col) = if pos == 0 { - if annotation.end_col.display == 0 { - (pos + 1, (annotation.end_col.display + 2).saturating_sub(left)) + let pre: usize = source_string + .chars() + .take(annotation.end_col.file) + .skip(left) + .map(|c| char_width(c)) + .sum(); + if annotation.end_col.file == 0 { + (pos + 1, (pre + 2)) } else { - (pos + 1, (annotation.end_col.display + 1).saturating_sub(left)) + let pad = if annotation.end_col.file - annotation.start_col.file == 0 { + 2 + } else { + 1 + }; + (pos + 1, (pre + pad)) } } else { - (pos + 2, annotation.start_col.display.saturating_sub(left)) + let pre: usize = source_string + .chars() + .take(annotation.start_col.file) + .skip(left) + .map(|c| char_width(c)) + .sum(); + (pos + 2, pre) }; if let Some(ref label) = annotation.label { buffer.puts(line_offset + pos, code_offset + col, label, style); @@ -1194,14 +1205,35 @@ impl HumanEmitter { // | _^ test for &(pos, annotation) in &annotations_position { let uline = self.underline(annotation.is_primary); - for p in annotation.start_col.display..annotation.end_col.display { + let width = annotation.end_col.file - annotation.start_col.file; + let previous: String = + source_string.chars().take(annotation.start_col.file).skip(left).collect(); + let underlined: String = + source_string.chars().skip(annotation.start_col.file).take(width).collect(); + debug!(?previous, ?underlined); + let code_offset = code_offset + + source_string + .chars() + .take(annotation.start_col.file) + .skip(left) + .map(|c| char_width(c)) + .sum::(); + let ann_width: usize = source_string + .chars() + .skip(annotation.start_col.file) + .take(width) + .map(|c| char_width(c)) + .sum(); + let ann_width = if ann_width == 0 + && matches!(annotation.annotation_type, AnnotationType::Singleline) + { + 1 + } else { + ann_width + }; + for p in 0..ann_width { // The default span label underline. - buffer.putc( - line_offset + 1, - (code_offset + p).saturating_sub(left), - uline.underline, - uline.style, - ); + buffer.putc(line_offset + 1, code_offset + p, uline.underline, uline.style); } if pos == 0 @@ -1213,7 +1245,7 @@ impl HumanEmitter { // The beginning of a multiline span with its leftward moving line on the same line. buffer.putc( line_offset + 1, - (code_offset + annotation.start_col.display).saturating_sub(left), + code_offset, match annotation.annotation_type { AnnotationType::MultilineStart(_) => uline.top_right_flat, AnnotationType::MultilineEnd(_) => uline.multiline_end_same_line, @@ -1231,7 +1263,7 @@ impl HumanEmitter { // so we start going down first. buffer.putc( line_offset + 1, - (code_offset + annotation.start_col.display).saturating_sub(left), + code_offset, match annotation.annotation_type { AnnotationType::MultilineStart(_) => uline.multiline_start_down, AnnotationType::MultilineEnd(_) => uline.multiline_end_up, @@ -1241,11 +1273,37 @@ impl HumanEmitter { ); } else if pos != 0 && annotation.has_label() { // The beginning of a span label with an actual label, we'll point down. - buffer.putc( + buffer.putc(line_offset + 1, code_offset, uline.label_start, uline.style); + } + } + + // We look for individual *long* spans, and we trim the *middle*, so that we render + // LL | ...= [0, 0, 0, ..., 0, 0]; + // | ^^^^^^^^^^...^^^^^^^ expected `&[u8]`, found `[{integer}; 1680]` + for (i, (_pos, annotation)) in annotations_position.iter().enumerate() { + // Skip cases where multiple spans overlap each other. + if overlap[i] { + continue; + }; + let AnnotationType::Singleline = annotation.annotation_type else { continue }; + let width = annotation.end_col.display - annotation.start_col.display; + if width > margin.column_width * 2 && width > 10 { + // If the terminal is *too* small, we keep at least a tiny bit of the span for + // display. + let pad = max(margin.column_width / 3, 5); + // Code line + buffer.replace( + line_offset, + annotation.start_col.file + pad, + annotation.end_col.file - pad, + self.margin(), + ); + // Underline line + buffer.replace( line_offset + 1, - (code_offset + annotation.start_col.display).saturating_sub(left), - uline.label_start, - uline.style, + annotation.start_col.file + pad, + annotation.end_col.file - pad, + self.margin(), ); } } @@ -1702,17 +1760,11 @@ impl HumanEmitter { // non-rustc_lexer::is_whitespace() chars are reported as an // error (ex. no-break-spaces \u{a0}), and thus can't be considered // for removal during error reporting. + // FIXME: doesn't account for '\t' properly. let leading_whitespace = source_string .chars() .take_while(|c| rustc_lexer::is_whitespace(*c)) - .map(|c| { - match c { - // Tabs are displayed as 4 spaces - '\t' => 4, - _ => 1, - } - }) - .sum(); + .count(); if source_string.chars().any(|c| !rustc_lexer::is_whitespace(c)) { whitespace_margin = min(whitespace_margin, leading_whitespace); } @@ -1726,8 +1778,8 @@ impl HumanEmitter { let mut span_left_margin = usize::MAX; for line in &annotated_file.lines { for ann in &line.annotations { - span_left_margin = min(span_left_margin, ann.start_col.display); - span_left_margin = min(span_left_margin, ann.end_col.display); + span_left_margin = min(span_left_margin, ann.start_col.file); + span_left_margin = min(span_left_margin, ann.end_col.file); } } if span_left_margin == usize::MAX { @@ -1747,12 +1799,12 @@ impl HumanEmitter { .map_or(0, |s| s.len()), ); for ann in &line.annotations { - span_right_margin = max(span_right_margin, ann.start_col.display); - span_right_margin = max(span_right_margin, ann.end_col.display); + span_right_margin = max(span_right_margin, ann.start_col.file); + span_right_margin = max(span_right_margin, ann.end_col.file); // FIXME: account for labels not in the same line let label_right = ann.label.as_ref().map_or(0, |l| l.len() + 1); label_right_margin = - max(label_right_margin, ann.end_col.display + label_right); + max(label_right_margin, ann.end_col.file + label_right); } } @@ -1763,15 +1815,7 @@ impl HumanEmitter { width_offset + annotated_file.multiline_depth + 1 }; - let column_width = if let Some(width) = self.diagnostic_width { - width.saturating_sub(code_offset) - } else if self.ui_testing || cfg!(miri) { - DEFAULT_COLUMN_WIDTH - } else { - termize::dimensions() - .map(|(w, _)| w.saturating_sub(code_offset)) - .unwrap_or(DEFAULT_COLUMN_WIDTH) - }; + let column_width = self.column_width(code_offset); let margin = Margin::new( whitespace_margin, @@ -1928,6 +1972,18 @@ impl HumanEmitter { Ok(()) } + fn column_width(&self, code_offset: usize) -> usize { + if let Some(width) = self.diagnostic_width { + width.saturating_sub(code_offset) + } else if self.ui_testing || cfg!(miri) { + DEFAULT_COLUMN_WIDTH + } else { + termize::dimensions() + .map(|(w, _)| w.saturating_sub(code_offset)) + .unwrap_or(DEFAULT_COLUMN_WIDTH) + } + } + fn emit_suggestion_default( &mut self, span: &MultiSpan, diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 7d7f364fec236..a6583407b7e7e 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -144,7 +144,7 @@ impl Emitter for JsonEmitter { // // So to avoid ICEs and confused users we "upgrade" the lint level for // those `FutureBreakageItem` to warn. - if matches!(diag.level, crate::Level::Allow | crate::Level::Expect(..)) { + if matches!(diag.level, crate::Level::Allow | crate::Level::Expect) { diag.level = crate::Level::Warning; } FutureBreakageItem { diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index f2b133f56773b..f8e19e5077893 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -14,9 +14,9 @@ #![feature(associated_type_defaults)] #![feature(box_into_inner)] #![feature(box_patterns)] +#![feature(default_field_values)] #![feature(error_reporter)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] #![feature(rustc_attrs)] @@ -24,7 +24,6 @@ #![feature(trait_alias)] #![feature(try_blocks)] #![feature(yeet_expr)] -#![warn(unreachable_pub)] // tidy-alphabetical-end extern crate self as rustc_errors; @@ -47,7 +46,7 @@ pub use codes::*; pub use diagnostic::{ BugAbort, Diag, DiagArg, DiagArgMap, DiagArgName, DiagArgValue, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee, FatalAbort, IntoDiagArg, LintDiagnostic, StringPart, Subdiag, - SubdiagMessageOp, Subdiagnostic, + Subdiagnostic, }; pub use diagnostic_impls::{ DiagArgFromDisplay, DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, @@ -589,7 +588,8 @@ struct DiagCtxtInner { /// add more information). All stashed diagnostics must be emitted with /// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped, /// otherwise an assertion failure will occur. - stashed_diagnostics: FxIndexMap<(Span, StashKey), (DiagInner, Option)>, + stashed_diagnostics: + FxIndexMap)>>, future_breakage_diagnostics: Vec, @@ -626,7 +626,6 @@ pub enum StashKey { MaybeFruTypo, CallAssocMethod, AssociatedTypeSuggestion, - MaybeForgetReturn, /// Query cycle detected, stashing in favor of a better error. Cycle, UndeterminedMacroResolution, @@ -906,15 +905,19 @@ impl<'a> DiagCtxtHandle<'a> { DelayedBug => { return self.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors); } - ForceWarning(_) | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow - | Expect(_) => None, + ForceWarning | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow + | Expect => None, }; // 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. - let key = (span.with_parent(None), key); - self.inner.borrow_mut().stashed_diagnostics.insert(key, (diag, guar)); + self.inner + .borrow_mut() + .stashed_diagnostics + .entry(key) + .or_default() + .insert(span.with_parent(None), (diag, guar)); guar } @@ -923,9 +926,10 @@ impl<'a> DiagCtxtHandle<'a> { /// and [`StashKey`] as the key. Panics if the found diagnostic is an /// error. pub fn steal_non_err(self, span: Span, key: StashKey) -> Option> { - let key = (span.with_parent(None), key); // FIXME(#120456) - is `swap_remove` correct? - let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key)?; + let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then( + |stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)), + )?; assert!(!diag.is_error()); assert!(guar.is_none()); Some(Diag::new_diagnostic(self, diag)) @@ -944,9 +948,10 @@ impl<'a> DiagCtxtHandle<'a> { where F: FnMut(&mut Diag<'_>), { - let key = (span.with_parent(None), key); // FIXME(#120456) - is `swap_remove` correct? - let err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key); + let err = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then( + |stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)), + ); err.map(|(err, guar)| { // The use of `::` is safe because level is `Level::Error`. assert_eq!(err.level, Error); @@ -967,9 +972,10 @@ impl<'a> DiagCtxtHandle<'a> { key: StashKey, new_err: Diag<'_>, ) -> ErrorGuaranteed { - let key = (span.with_parent(None), key); // FIXME(#120456) - is `swap_remove` correct? - let old_err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key); + let old_err = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then( + |stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)), + ); match old_err { Some((old_err, guar)) => { assert_eq!(old_err.level, Error); @@ -984,7 +990,14 @@ impl<'a> DiagCtxtHandle<'a> { } pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool { - self.inner.borrow().stashed_diagnostics.get(&(span.with_parent(None), key)).is_some() + let inner = self.inner.borrow(); + if let Some(stashed_diagnostics) = inner.stashed_diagnostics.get(&key) + && !stashed_diagnostics.is_empty() + { + stashed_diagnostics.contains_key(&span.with_parent(None)) + } else { + false + } } /// Emit all stashed diagnostics. @@ -998,7 +1011,11 @@ impl<'a> DiagCtxtHandle<'a> { let inner = self.inner.borrow(); inner.err_guars.len() + inner.lint_err_guars.len() - + inner.stashed_diagnostics.values().filter(|(_diag, guar)| guar.is_some()).count() + + inner + .stashed_diagnostics + .values() + .map(|a| a.values().filter(|(_, guar)| guar.is_some()).count()) + .sum::() } /// This excludes lint errors and delayed bugs. Unless absolutely @@ -1046,7 +1063,7 @@ impl<'a> DiagCtxtHandle<'a> { // Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a // configuration like `--cap-lints allow --force-warn bare_trait_objects`. inner.emit_diagnostic( - DiagInner::new(ForceWarning(None), DiagMessage::Str(warnings)), + DiagInner::new(ForceWarning, DiagMessage::Str(warnings)), None, ); } @@ -1451,7 +1468,7 @@ impl<'a> DiagCtxtHandle<'a> { #[rustc_lint_diagnostics] #[track_caller] pub fn struct_expect(self, msg: impl Into, id: LintExpectationId) -> Diag<'a, ()> { - Diag::new(self, Expect(id), msg) + Diag::new(self, Expect, msg).with_lint_id(id) } } @@ -1487,16 +1504,18 @@ impl DiagCtxtInner { fn emit_stashed_diagnostics(&mut self) -> Option { let mut guar = None; let has_errors = !self.err_guars.is_empty(); - for (_, (diag, _guar)) in std::mem::take(&mut self.stashed_diagnostics).into_iter() { - if !diag.is_error() { - // Unless they're forced, don't flush stashed warnings when - // there are errors, to avoid causing warning overload. The - // stash would've been stolen already if it were important. - if !diag.is_force_warn() && has_errors { - continue; + for (_, stashed_diagnostics) in std::mem::take(&mut self.stashed_diagnostics).into_iter() { + for (_, (diag, _guar)) in stashed_diagnostics { + if !diag.is_error() { + // Unless they're forced, don't flush stashed warnings when + // there are errors, to avoid causing warning overload. The + // stash would've been stolen already if it were important. + if !diag.is_force_warn() && has_errors { + continue; + } } + guar = guar.or(self.emit_diagnostic(diag, None)); } - guar = guar.or(self.emit_diagnostic(diag, None)); } guar } @@ -1511,7 +1530,7 @@ impl DiagCtxtInner { // Future breakages aren't emitted if they're `Level::Allow` or // `Level::Expect`, but they still need to be constructed and // stashed below, so they'll trigger the must_produce_diag check. - assert_matches!(diagnostic.level, Error | Warning | Allow | Expect(_)); + assert_matches!(diagnostic.level, Error | Warning | Allow | Expect); self.future_breakage_diagnostics.push(diagnostic.clone()); } @@ -1559,7 +1578,7 @@ impl DiagCtxtInner { }; } } - ForceWarning(None) => {} // `ForceWarning(Some(...))` is below, with `Expect` + ForceWarning if diagnostic.lint_id.is_none() => {} // `ForceWarning(Some(...))` is below, with `Expect` Warning => { if !self.flags.can_emit_warnings { // We are not emitting warnings. @@ -1581,9 +1600,9 @@ impl DiagCtxtInner { } return None; } - Expect(expect_id) | ForceWarning(Some(expect_id)) => { - self.fulfilled_expectations.insert(expect_id); - if let Expect(_) = diagnostic.level { + Expect | ForceWarning => { + self.fulfilled_expectations.insert(diagnostic.lint_id.unwrap()); + if let Expect = diagnostic.level { // Nothing emitted here for expected lints. TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); self.suppressed_expected_diag = true; @@ -1632,7 +1651,7 @@ impl DiagCtxtInner { if is_error { self.deduplicated_err_count += 1; - } else if matches!(diagnostic.level, ForceWarning(_) | Warning) { + } else if matches!(diagnostic.level, ForceWarning | Warning) { self.deduplicated_warn_count += 1; } self.has_printed = true; @@ -1689,6 +1708,7 @@ impl DiagCtxtInner { if let Some((_diag, guar)) = self .stashed_diagnostics .values() + .flat_map(|stashed_diagnostics| stashed_diagnostics.values()) .find(|(diag, guar)| guar.is_some() && diag.is_lint.is_none()) { *guar @@ -1701,13 +1721,9 @@ impl DiagCtxtInner { fn has_errors(&self) -> Option { self.err_guars.get(0).copied().or_else(|| self.lint_err_guars.get(0).copied()).or_else( || { - if let Some((_diag, guar)) = - self.stashed_diagnostics.values().find(|(_diag, guar)| guar.is_some()) - { - *guar - } else { - None - } + self.stashed_diagnostics.values().find_map(|stashed_diagnostics| { + stashed_diagnostics.values().find_map(|(_, guar)| *guar) + }) }, ) } @@ -1900,9 +1916,9 @@ pub enum Level { /// A `force-warn` lint warning about the code being compiled. Does not prevent compilation /// from finishing. /// - /// The [`LintExpectationId`] is used for expected lint diagnostics. In all other cases this + /// Requires a [`LintExpectationId`] for expected lint diagnostics. In all other cases this /// should be `None`. - ForceWarning(Option), + ForceWarning, /// A warning about the code being compiled. Does not prevent compilation from finishing. /// Will be skipped if `can_emit_warnings` is false. @@ -1927,8 +1943,8 @@ pub enum Level { /// Only used for lints. Allow, - /// Only used for lints. - Expect(LintExpectationId), + /// Only used for lints. Requires a [`LintExpectationId`] for silencing the lints. + Expect, } impl fmt::Display for Level { @@ -1944,7 +1960,7 @@ impl Level { Bug | Fatal | Error | DelayedBug => { spec.set_fg(Some(Color::Red)).set_intense(true); } - ForceWarning(_) | Warning => { + ForceWarning | Warning => { spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows)); } Note | OnceNote => { @@ -1954,7 +1970,7 @@ impl Level { spec.set_fg(Some(Color::Cyan)).set_intense(true); } FailureNote => {} - Allow | Expect(_) => unreachable!(), + Allow | Expect => unreachable!(), } spec } @@ -1963,11 +1979,11 @@ impl Level { match self { Bug | DelayedBug => "error: internal compiler error", Fatal | Error => "error", - ForceWarning(_) | Warning => "warning", + ForceWarning | Warning => "warning", Note | OnceNote => "note", Help | OnceHelp => "help", FailureNote => "failure-note", - Allow | Expect(_) => unreachable!(), + Allow | Expect => unreachable!(), } } @@ -1978,8 +1994,7 @@ impl Level { // Can this level be used in a subdiagnostic message? fn can_be_subdiag(&self) -> bool { match self { - Bug | DelayedBug | Fatal | Error | ForceWarning(_) | FailureNote | Allow - | Expect(_) => false, + Bug | DelayedBug | Fatal | Error | ForceWarning | FailureNote | Allow | Expect => false, Warning | Note | Help | OnceNote | OnceHelp => true, } diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs index 7a991a2ace71b..f02387d833595 100644 --- a/compiler/rustc_errors/src/markdown/parse.rs +++ b/compiler/rustc_errors/src/markdown/parse.rs @@ -40,11 +40,13 @@ type ParseResult<'a> = Option>; /// Parsing context #[derive(Clone, Copy, Debug, PartialEq)] +// The default values are the most common setting for non top-level parsing: not top block, not at +// line start (yes leading whitespace, not escaped). struct Context { /// If true, we are at a the topmost level (not recursing a nested tt) - top_block: bool, + top_block: bool = false, /// Previous character - prev: Prev, + prev: Prev = Prev::Whitespace, } /// Character class preceding this one @@ -57,14 +59,6 @@ enum Prev { Any, } -impl Default for Context { - /// Most common setting for non top-level parsing: not top block, not at - /// line start (yes leading whitespace, not escaped) - fn default() -> Self { - Self { top_block: false, prev: Prev::Whitespace } - } -} - /// Flags to simple parser function #[derive(Clone, Copy, Debug, PartialEq)] enum ParseOpt { @@ -248,7 +242,7 @@ fn parse_heading(buf: &[u8]) -> ParseResult<'_> { } let (txt, rest) = parse_to_newline(&buf[1..]); - let ctx = Context { top_block: false, prev: Prev::Whitespace }; + let ctx = Context { .. }; let stream = parse_recursive(txt, ctx); Some((MdTree::Heading(level.try_into().unwrap(), stream), rest)) @@ -257,7 +251,7 @@ fn parse_heading(buf: &[u8]) -> ParseResult<'_> { /// Bulleted list fn parse_unordered_li(buf: &[u8]) -> Parsed<'_> { let (txt, rest) = get_indented_section(&buf[2..]); - let ctx = Context { top_block: false, prev: Prev::Whitespace }; + let ctx = Context { .. }; let stream = parse_recursive(trim_ascii_start(txt), ctx); (MdTree::UnorderedListItem(stream), rest) } @@ -266,7 +260,7 @@ fn parse_unordered_li(buf: &[u8]) -> Parsed<'_> { fn parse_ordered_li(buf: &[u8]) -> Parsed<'_> { let (num, pos) = ord_list_start(buf).unwrap(); // success tested in caller let (txt, rest) = get_indented_section(&buf[pos..]); - let ctx = Context { top_block: false, prev: Prev::Whitespace }; + let ctx = Context { .. }; let stream = parse_recursive(trim_ascii_start(txt), ctx); (MdTree::OrderedListItem(num, stream), rest) } diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs index 8485d7087cfc5..f09c2ed5534a5 100644 --- a/compiler/rustc_errors/src/snippet.rs +++ b/compiler/rustc_errors/src/snippet.rs @@ -159,11 +159,7 @@ impl Annotation { /// Length of this annotation as displayed in the stderr output pub(crate) fn len(&self) -> usize { // Account for usize underflows - if self.end_col.display > self.start_col.display { - self.end_col.display - self.start_col.display - } else { - self.start_col.display - self.end_col.display - } + self.end_col.display.abs_diff(self.start_col.display) } pub(crate) fn has_label(&self) -> bool { diff --git a/compiler/rustc_errors/src/styled_buffer.rs b/compiler/rustc_errors/src/styled_buffer.rs index 5ca9e9b18f341..790efd0286e9d 100644 --- a/compiler/rustc_errors/src/styled_buffer.rs +++ b/compiler/rustc_errors/src/styled_buffer.rs @@ -89,6 +89,19 @@ impl StyledBuffer { } } + pub(crate) fn replace(&mut self, line: usize, start: usize, end: usize, string: &str) { + if start == end { + return; + } + if start > self.lines[line].len() || end > self.lines[line].len() { + return; + } + let _ = self.lines[line].drain(start..(end - string.chars().count())); + for (i, c) in string.chars().enumerate() { + self.lines[line][start + i] = StyledChar::new(c, Style::LineNumber); + } + } + /// For given `line` inserts `string` with `style` before old content of that line, /// adding lines if needed pub(crate) fn prepend(&mut self, line: usize, string: &str, style: Style) { diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 0ba139ea5cc57..e8fd2f54d76cb 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -12,6 +12,7 @@ doctest = false rustc_ast = { path = "../rustc_ast" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 4a250145308cf..55751aa49089b 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -7,11 +7,11 @@ use std::sync::Arc; use rustc_ast::attr::{AttributeExt, MarkedAttrs}; use rustc_ast::ptr::P; -use rustc_ast::token::Nonterminal; +use rustc_ast::token::MetaVarKind; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; -use rustc_attr_parsing::{AttributeKind, Deprecation, Stability, find_attr}; +use rustc_attr_data_structures::{AttributeKind, Deprecation, Stability, find_attr}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; @@ -19,7 +19,7 @@ use rustc_feature::Features; use rustc_hir as hir; use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools}; use rustc_parse::MACRO_ARGUMENTS; -use rustc_parse::parser::Parser; +use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::config::CollapseMacroDebuginfo; use rustc_session::parse::ParseSess; use rustc_session::{Limit, Session}; @@ -53,6 +53,7 @@ pub enum Annotatable { Param(ast::Param), FieldDef(ast::FieldDef), Variant(ast::Variant), + WherePredicate(ast::WherePredicate), Crate(ast::Crate), } @@ -71,6 +72,7 @@ impl Annotatable { Annotatable::Param(p) => p.span, Annotatable::FieldDef(sf) => sf.span, Annotatable::Variant(v) => v.span, + Annotatable::WherePredicate(wp) => wp.span, Annotatable::Crate(c) => c.spans.inner_span, } } @@ -89,6 +91,7 @@ impl Annotatable { Annotatable::Param(p) => p.visit_attrs(f), Annotatable::FieldDef(sf) => sf.visit_attrs(f), Annotatable::Variant(v) => v.visit_attrs(f), + Annotatable::WherePredicate(wp) => wp.visit_attrs(f), Annotatable::Crate(c) => c.visit_attrs(f), } } @@ -107,6 +110,7 @@ impl Annotatable { Annotatable::Param(p) => visitor.visit_param(p), Annotatable::FieldDef(sf) => visitor.visit_field_def(sf), Annotatable::Variant(v) => visitor.visit_variant(v), + Annotatable::WherePredicate(wp) => visitor.visit_where_predicate(wp), Annotatable::Crate(c) => visitor.visit_crate(c), } } @@ -128,6 +132,7 @@ impl Annotatable { | Annotatable::Param(..) | Annotatable::FieldDef(..) | Annotatable::Variant(..) + | Annotatable::WherePredicate(..) | Annotatable::Crate(..) => panic!("unexpected annotatable"), } } @@ -148,7 +153,7 @@ impl Annotatable { pub fn expect_impl_item(self) -> P { match self { - Annotatable::AssocItem(i, AssocCtxt::Impl) => i, + Annotatable::AssocItem(i, AssocCtxt::Impl { .. }) => i, _ => panic!("expected Item"), } } @@ -223,6 +228,13 @@ impl Annotatable { } } + pub fn expect_where_predicate(self) -> ast::WherePredicate { + match self { + Annotatable::WherePredicate(wp) => wp, + _ => panic!("expected where predicate"), + } + } + pub fn expect_crate(self) -> ast::Crate { match self { Annotatable::Crate(krate) => krate, @@ -391,6 +403,11 @@ pub trait MacResult { None } + /// Creates zero or more impl items. + fn make_trait_impl_items(self: Box) -> Option; 1]>> { + None + } + /// Creates zero or more trait items. fn make_trait_items(self: Box) -> Option; 1]>> { None @@ -446,6 +463,10 @@ pub trait MacResult { None } + fn make_where_predicates(self: Box) -> Option> { + None + } + fn make_crate(self: Box) -> Option { // Fn-like macros cannot produce a crate. unreachable!() @@ -500,6 +521,10 @@ impl MacResult for MacEager { self.impl_items } + fn make_trait_impl_items(self: Box) -> Option; 1]>> { + self.impl_items + } + fn make_trait_items(self: Box) -> Option; 1]>> { self.trait_items } @@ -597,6 +622,10 @@ impl MacResult for DummyResult { Some(SmallVec::new()) } + fn make_trait_impl_items(self: Box) -> Option; 1]>> { + Some(SmallVec::new()) + } + fn make_trait_items(self: Box) -> Option; 1]>> { Some(SmallVec::new()) } @@ -665,17 +694,18 @@ impl MacResult for DummyResult { } /// A syntax extension kind. +#[derive(Clone)] pub enum SyntaxExtensionKind { /// A token-based function-like macro. Bang( /// An expander with signature TokenStream -> TokenStream. - Box, + Arc, ), /// An AST-based function-like macro. LegacyBang( /// An expander with signature TokenStream -> AST. - Box, + Arc, ), /// A token-based attribute macro. @@ -683,7 +713,7 @@ pub enum SyntaxExtensionKind { /// An expander with signature (TokenStream, TokenStream) -> TokenStream. /// The first TokenStream is the attribute itself, the second is the annotated item. /// The produced TokenStream replaces the input TokenStream. - Box, + Arc, ), /// An AST-based attribute macro. @@ -691,7 +721,7 @@ pub enum SyntaxExtensionKind { /// 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, + Arc, ), /// A trivial attribute "macro" that does nothing, @@ -708,18 +738,18 @@ pub enum SyntaxExtensionKind { /// is handled identically to `LegacyDerive`. It should be migrated to /// a token-based representation like `Bang` and `Attr`, instead of /// using `MultiItemModifier`. - Box, + Arc, ), /// An AST-based derive macro. LegacyDerive( /// An expander with signature AST -> AST. /// The produced AST fragment is appended to the input AST fragment. - Box, + Arc, ), /// A glob delegation. - GlobDelegation(Box), + GlobDelegation(Arc), } /// A struct representing a macro definition in "lowered" form ready for expansion. @@ -794,10 +824,10 @@ impl SyntaxExtension { return Err(item.span); } - match item.name_or_empty() { - sym::no => Ok(CollapseMacroDebuginfo::No), - sym::external => Ok(CollapseMacroDebuginfo::External), - sym::yes => Ok(CollapseMacroDebuginfo::Yes), + match item.name() { + Some(sym::no) => Ok(CollapseMacroDebuginfo::No), + Some(sym::external) => Ok(CollapseMacroDebuginfo::External), + Some(sym::yes) => Ok(CollapseMacroDebuginfo::Yes), _ => Err(item.path.span), } } @@ -873,16 +903,16 @@ impl SyntaxExtension { }) .unwrap_or_else(|| (None, helper_attrs)); - let stability = find_attr!(attrs, AttributeKind::Stability{stability, ..} => *stability); + let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability); // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem - if let Some(sp) = find_attr!(attrs, AttributeKind::ConstStability{span, ..} => *span) { + if let Some(sp) = find_attr!(attrs, AttributeKind::ConstStability { span, .. } => *span) { sess.dcx().emit_err(errors::MacroConstStability { span: sp, head_span: sess.source_map().guess_head_span(span), }); } - if let Some(sp) = find_attr!(attrs, AttributeKind::BodyStability{span, ..} => *span) { + if let Some(sp) = find_attr!(attrs, AttributeKind::BodyStability{ span, .. } => *span) { sess.dcx().emit_err(errors::MacroBodyStability { span: sp, head_span: sess.source_map().guess_head_span(span), @@ -896,7 +926,10 @@ impl SyntaxExtension { // FIXME(jdonszelmann): avoid the into_iter/collect? .then(|| allow_internal_unstable.iter().map(|i| i.0).collect::>().into()), stability, - deprecation: find_attr!(attrs, AttributeKind::Deprecation{deprecation, ..} => *deprecation), + deprecation: find_attr!( + attrs, + AttributeKind::Deprecation { deprecation, .. } => *deprecation + ), helper_attrs, edition, builtin_name, @@ -918,7 +951,7 @@ impl SyntaxExtension { cx.dcx().span_delayed_bug(span, "expanded a dummy bang macro"), )) } - SyntaxExtension::default(SyntaxExtensionKind::LegacyBang(Box::new(expander)), edition) + SyntaxExtension::default(SyntaxExtensionKind::LegacyBang(Arc::new(expander)), edition) } /// A dummy derive macro `#[derive(Foo)]`. @@ -931,7 +964,7 @@ impl SyntaxExtension { ) -> Vec { Vec::new() } - SyntaxExtension::default(SyntaxExtensionKind::Derive(Box::new(expander)), edition) + SyntaxExtension::default(SyntaxExtensionKind::Derive(Arc::new(expander)), edition) } pub fn non_macro_attr(edition: Edition) -> SyntaxExtension { @@ -961,7 +994,7 @@ impl SyntaxExtension { } let expander = GlobDelegationExpanderImpl { trait_def_id, impl_def_id }; - SyntaxExtension::default(SyntaxExtensionKind::GlobDelegation(Box::new(expander)), edition) + SyntaxExtension::default(SyntaxExtensionKind::GlobDelegation(Arc::new(expander)), edition) } pub fn expn_data( @@ -984,6 +1017,7 @@ impl SyntaxExtension { self.allow_internal_unsafe, self.local_inner_macros, self.collapse_debuginfo, + self.builtin_name.is_some(), ) } } @@ -1068,7 +1102,7 @@ pub trait ResolverExpand { /// HIR proc macros items back to their harness items. fn declare_proc_macro(&mut self, id: NodeId); - fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem); + fn append_stripped_cfg_item(&mut self, parent_node: NodeId, ident: Ident, cfg: ast::MetaItem); /// Tools registered with `#![register_tool]` and used by tool attributes and lints. fn registered_tools(&self) -> &RegisteredTools; @@ -1389,13 +1423,12 @@ pub fn parse_macro_name_and_helper_attrs( /// If this item looks like a specific enums from `rental`, emit a fatal error. /// See #73345 and #83125 for more details. /// FIXME(#73933): Remove this eventually. -fn pretty_printing_compatibility_hack(item: &Item, sess: &Session) { - let name = item.ident.name; - if name == sym::ProceduralMasqueradeDummyType - && let ast::ItemKind::Enum(enum_def, _) = &item.kind +fn pretty_printing_compatibility_hack(item: &Item, psess: &ParseSess) { + if let ast::ItemKind::Enum(ident, enum_def, _) = &item.kind + && ident.name == sym::ProceduralMasqueradeDummyType && let [variant] = &*enum_def.variants && variant.ident.name == sym::Input - && let FileName::Real(real) = sess.source_map().span_to_filename(item.ident.span) + && let FileName::Real(real) = psess.source_map().span_to_filename(ident.span) && let Some(c) = real .local_path() .unwrap_or(Path::new("")) @@ -1413,7 +1446,7 @@ fn pretty_printing_compatibility_hack(item: &Item, sess: &Session) { }; if crate_matches { - sess.dcx().emit_fatal(errors::ProcMacroBackCompat { + psess.dcx().emit_fatal(errors::ProcMacroBackCompat { crate_name: "rental".to_string(), fixed_version: "0.5.6".to_string(), }); @@ -1421,7 +1454,7 @@ fn pretty_printing_compatibility_hack(item: &Item, sess: &Session) { } } -pub(crate) fn ann_pretty_printing_compatibility_hack(ann: &Annotatable, sess: &Session) { +pub(crate) fn ann_pretty_printing_compatibility_hack(ann: &Annotatable, psess: &ParseSess) { let item = match ann { Annotatable::Item(item) => item, Annotatable::Stmt(stmt) => match &stmt.kind { @@ -1430,17 +1463,36 @@ pub(crate) fn ann_pretty_printing_compatibility_hack(ann: &Annotatable, sess: &S }, _ => return, }; - pretty_printing_compatibility_hack(item, sess) + pretty_printing_compatibility_hack(item, psess) } -pub(crate) fn nt_pretty_printing_compatibility_hack(nt: &Nonterminal, sess: &Session) { - let item = match nt { - Nonterminal::NtItem(item) => item, - Nonterminal::NtStmt(stmt) => match &stmt.kind { - ast::StmtKind::Item(item) => item, - _ => return, - }, +pub(crate) fn stream_pretty_printing_compatibility_hack( + kind: MetaVarKind, + stream: &TokenStream, + psess: &ParseSess, +) { + let item = match kind { + MetaVarKind::Item => { + let mut parser = Parser::new(psess, stream.clone(), None); + // No need to collect tokens for this simple check. + parser + .parse_item(ForceCollect::No) + .expect("failed to reparse item") + .expect("an actual item") + } + MetaVarKind::Stmt => { + let mut parser = Parser::new(psess, stream.clone(), None); + // No need to collect tokens for this simple check. + let stmt = parser + .parse_stmt(ForceCollect::No) + .expect("failed to reparse") + .expect("an actual stmt"); + match &stmt.kind { + ast::StmtKind::Item(item) => item.clone(), + _ => return, + } + } _ => return, }; - pretty_printing_compatibility_hack(item, sess) + pretty_printing_compatibility_hack(&item, psess) } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index ee7f68cc2f01c..14b8cc90d97d6 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -1,8 +1,10 @@ use rustc_ast::ptr::P; +use rustc_ast::token::Delimiter; +use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::literal; use rustc_ast::{ self as ast, AnonConst, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp, - attr, token, + attr, token, tokenstream, }; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; @@ -55,13 +57,13 @@ impl<'a> ExtCtxt<'a> { &self, span: Span, path: ast::Path, - delim: ast::token::Delimiter, - tokens: ast::tokenstream::TokenStream, + delim: Delimiter, + tokens: TokenStream, ) -> P { P(ast::MacCall { path, args: P(ast::DelimArgs { - dspan: ast::tokenstream::DelimSpan { open: span, close: span }, + dspan: tokenstream::DelimSpan { open: span, close: span }, delim, tokens, }), @@ -230,6 +232,7 @@ impl<'a> ExtCtxt<'a> { self.pat_ident(sp, ident) }; let local = P(ast::Local { + super_: None, pat, ty, id: ast::DUMMY_NODE_ID, @@ -245,6 +248,7 @@ impl<'a> ExtCtxt<'a> { /// 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 { + super_: None, pat: self.pat_wild(span), ty: Some(ty), id: ast::DUMMY_NODE_ID, @@ -286,7 +290,6 @@ impl<'a> ExtCtxt<'a> { rules: BlockCheckMode::Default, span, tokens: None, - could_be_bare_literal: false, }) } @@ -479,8 +482,8 @@ impl<'a> ExtCtxt<'a> { span, [sym::std, sym::unreachable].map(|s| Ident::new(s, span)).to_vec(), ), - ast::token::Delimiter::Parenthesis, - ast::tokenstream::TokenStream::default(), + Delimiter::Parenthesis, + TokenStream::default(), ), ) } @@ -663,15 +666,8 @@ impl<'a> ExtCtxt<'a> { P(ast::FnDecl { inputs, output }) } - pub fn item( - &self, - span: Span, - name: Ident, - attrs: ast::AttrVec, - kind: ast::ItemKind, - ) -> P { + pub fn item(&self, span: Span, attrs: ast::AttrVec, kind: ast::ItemKind) -> P { P(ast::Item { - ident: name, attrs, id: ast::DUMMY_NODE_ID, kind, @@ -688,18 +684,24 @@ impl<'a> ExtCtxt<'a> { pub fn item_static( &self, span: Span, - name: Ident, + ident: Ident, ty: P, mutability: ast::Mutability, expr: P, ) -> P { self.item( span, - name, AttrVec::new(), ast::ItemKind::Static( - ast::StaticItem { ty, safety: ast::Safety::Default, mutability, expr: Some(expr) } - .into(), + ast::StaticItem { + ident, + ty, + safety: ast::Safety::Default, + mutability, + expr: Some(expr), + define_opaque: None, + } + .into(), ), ) } @@ -707,22 +709,23 @@ impl<'a> ExtCtxt<'a> { pub fn item_const( &self, span: Span, - name: Ident, + ident: Ident, ty: P, expr: P, ) -> P { let defaultness = ast::Defaultness::Final; self.item( span, - name, AttrVec::new(), ast::ItemKind::Const( ast::ConstItem { defaultness, + ident, // FIXME(generic_const_items): Pass the generics as a parameter. generics: ast::Generics::default(), ty, expr: Some(expr), + define_opaque: None, } .into(), ), diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 83255b820178f..0994813ecb9c5 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -1,12 +1,15 @@ //! Conditional compilation stripping. +use std::iter; + use rustc_ast::ptr::P; use rustc_ast::token::{Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{ AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree, }; use rustc_ast::{ - self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId, + self as ast, AttrKind, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, + NodeId, NormalAttr, }; use rustc_attr_parsing as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; @@ -153,6 +156,19 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec .collect() } +pub(crate) fn attr_into_trace(mut attr: Attribute, trace_name: Symbol) -> Attribute { + match &mut attr.kind { + AttrKind::Normal(normal) => { + let NormalAttr { item, tokens } = &mut **normal; + item.path.segments[0].ident.name = trace_name; + // This makes the trace attributes unobservable to token-based proc macros. + *tokens = Some(LazyAttrTokenStream::new_direct(AttrTokenStream::default())); + } + AttrKind::DocComment(..) => unreachable!(), + } + attr +} + #[macro_export] macro_rules! configure { ($this:ident, $node:ident) => { @@ -176,7 +192,7 @@ impl<'a> StripUnconfigured<'a> { if self.config_tokens { if let Some(Some(tokens)) = node.tokens_mut() { let attr_stream = tokens.to_attr_token_stream(); - *tokens = LazyAttrTokenStream::new(self.configure_tokens(&attr_stream)); + *tokens = LazyAttrTokenStream::new_direct(self.configure_tokens(&attr_stream)); } } } @@ -207,7 +223,7 @@ impl<'a> StripUnconfigured<'a> { target.attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr)); if self.in_cfg(&target.attrs) { - target.tokens = LazyAttrTokenStream::new( + target.tokens = LazyAttrTokenStream::new_direct( self.configure_tokens(&target.tokens.to_attr_token_stream()), ); Some(AttrTokenTree::AttrsTarget(target)) @@ -221,22 +237,7 @@ impl<'a> StripUnconfigured<'a> { inner = self.configure_tokens(&inner); Some(AttrTokenTree::Delimited(sp, spacing, delim, inner)) } - AttrTokenTree::Token( - Token { - kind: - TokenKind::NtIdent(..) - | TokenKind::NtLifetime(..) - | TokenKind::Interpolated(..), - .. - }, - _, - ) => { - panic!("Nonterminal should have been flattened: {:?}", tree); - } - AttrTokenTree::Token( - Token { kind: TokenKind::OpenDelim(_) | TokenKind::CloseDelim(_), .. }, - _, - ) => { + AttrTokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => { panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tree); } AttrTokenTree::Token(token, spacing) => Some(AttrTokenTree::Token(token, spacing)), @@ -273,12 +274,21 @@ impl<'a> StripUnconfigured<'a> { /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec { - validate_attr::check_attribute_safety(&self.sess.psess, AttributeSafety::Normal, &cfg_attr); + validate_attr::check_attribute_safety( + &self.sess.psess, + Some(AttributeSafety::Normal), + &cfg_attr, + ast::CRATE_NODE_ID, + ); + + // A trace attribute left in AST in place of the original `cfg_attr` attribute. + // It can later be used by lints or other diagnostics. + let trace_attr = attr_into_trace(cfg_attr.clone(), sym::cfg_attr_trace); let Some((cfg_predicate, expanded_attrs)) = rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess) else { - return vec![]; + return vec![trace_attr]; }; // Lint on zero attributes in source. @@ -292,22 +302,21 @@ impl<'a> StripUnconfigured<'a> { } if !attr::cfg_matches(&cfg_predicate, &self.sess, self.lint_node_id, self.features) { - return vec![]; + return vec![trace_attr]; } if recursive { // 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 + let expanded_attrs = expanded_attrs .into_iter() - .flat_map(|item| self.process_cfg_attr(&self.expand_cfg_attr_item(cfg_attr, item))) - .collect() + .flat_map(|item| self.process_cfg_attr(&self.expand_cfg_attr_item(cfg_attr, item))); + iter::once(trace_attr).chain(expanded_attrs).collect() } else { - expanded_attrs - .into_iter() - .map(|item| self.expand_cfg_attr_item(cfg_attr, item)) - .collect() + let expanded_attrs = + expanded_attrs.into_iter().map(|item| self.expand_cfg_attr_item(cfg_attr, item)); + iter::once(trace_attr).chain(expanded_attrs).collect() } } @@ -328,7 +337,7 @@ impl<'a> StripUnconfigured<'a> { // For inner attributes, we do the same thing for the `!` in `#![attr]`. let mut trees = if cfg_attr.style == AttrStyle::Inner { - let Some(TokenTree::Token(bang_token @ Token { kind: TokenKind::Not, .. }, _)) = + let Some(TokenTree::Token(bang_token @ Token { kind: TokenKind::Bang, .. }, _)) = orig_trees.next() else { panic!("Bad tokens for attribute {cfg_attr:?}"); @@ -357,7 +366,7 @@ impl<'a> StripUnconfigured<'a> { .to_attr_token_stream(), )); - let tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::new(trees))); + let tokens = Some(LazyAttrTokenStream::new_direct(AttrTokenStream::new(trees))); let attr = ast::attr::mk_attr_from_item( &self.sess.psess.attr_id_generator, item, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index e3f31ebeca3f3..81d4d59ee0451 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,4 +1,3 @@ -use std::ops::Deref; use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; @@ -7,13 +6,12 @@ use std::{iter, mem}; use rustc_ast as ast; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list}; use rustc_ast::{ AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind, - NodeId, PatKind, StmtKind, TyKind, + NodeId, PatKind, StmtKind, TyKind, token, }; use rustc_ast_pretty::pprust; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; @@ -33,7 +31,7 @@ use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, sym}; use smallvec::SmallVec; use crate::base::*; -use crate::config::StripUnconfigured; +use crate::config::{StripUnconfigured, attr_into_trace}; use crate::errors::{ EmptyDelegationMac, GlobDelegationOutsideImpls, GlobDelegationTraitlessQpath, IncompleteParse, RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, @@ -188,9 +186,15 @@ ast_fragments! { ImplItems(SmallVec<[P; 1]>) { "impl item"; many fn flat_map_assoc_item; - fn visit_assoc_item(AssocCtxt::Impl); + fn visit_assoc_item(AssocCtxt::Impl { of_trait: false }); fn make_impl_items; } + TraitImplItems(SmallVec<[P; 1]>) { + "impl item"; + many fn flat_map_assoc_item; + fn visit_assoc_item(AssocCtxt::Impl { of_trait: true }); + fn make_trait_impl_items; + } ForeignItems(SmallVec<[P; 1]>) { "foreign item"; many fn flat_map_foreign_item; @@ -227,6 +231,12 @@ ast_fragments! { Variants(SmallVec<[ast::Variant; 1]>) { "variant"; many fn flat_map_variant; fn visit_variant(); fn make_variants; } + WherePredicates(SmallVec<[ast::WherePredicate; 1]>) { + "where predicate"; + many fn flat_map_where_predicate; + fn visit_where_predicate(); + fn make_where_predicates; + } Crate(ast::Crate) { "crate"; one fn visit_crate; fn visit_crate; fn make_crate; } } @@ -251,6 +261,7 @@ impl AstFragmentKind { AstFragmentKind::Items | AstFragmentKind::TraitItems | AstFragmentKind::ImplItems + | AstFragmentKind::TraitImplItems | AstFragmentKind::ForeignItems | AstFragmentKind::Crate => SupportsMacroExpansion::Yes { supports_inner_attrs: true }, AstFragmentKind::Arms @@ -259,7 +270,8 @@ impl AstFragmentKind { | AstFragmentKind::GenericParams | AstFragmentKind::Params | AstFragmentKind::FieldDefs - | AstFragmentKind::Variants => SupportsMacroExpansion::No, + | AstFragmentKind::Variants + | AstFragmentKind::WherePredicates => SupportsMacroExpansion::No, } } @@ -290,12 +302,18 @@ impl AstFragmentKind { AstFragmentKind::Variants => { AstFragment::Variants(items.map(Annotatable::expect_variant).collect()) } + AstFragmentKind::WherePredicates => AstFragment::WherePredicates( + items.map(Annotatable::expect_where_predicate).collect(), + ), AstFragmentKind::Items => { AstFragment::Items(items.map(Annotatable::expect_item).collect()) } AstFragmentKind::ImplItems => { AstFragment::ImplItems(items.map(Annotatable::expect_impl_item).collect()) } + AstFragmentKind::TraitImplItems => { + AstFragment::TraitImplItems(items.map(Annotatable::expect_impl_item).collect()) + } AstFragmentKind::TraitItems => { AstFragment::TraitItems(items.map(Annotatable::expect_trait_item).collect()) } @@ -337,10 +355,10 @@ pub enum InvocationKind { }, Attr { attr: ast::Attribute, - // Re-insertion position for inert attributes. + /// Re-insertion position for inert attributes. pos: usize, item: Annotatable, - // Required for resolving derive helper attributes. + /// Required for resolving derive helper attributes. derives: Vec, }, Derive { @@ -350,6 +368,8 @@ pub enum InvocationKind { }, GlobDelegation { item: P, + /// Whether this is a trait impl or an inherent impl + of_trait: bool, }, } @@ -378,7 +398,7 @@ impl Invocation { InvocationKind::Bang { span, .. } => *span, InvocationKind::Attr { attr, .. } => attr.span, InvocationKind::Derive { path, .. } => path.span, - InvocationKind::GlobDelegation { item } => item.span, + InvocationKind::GlobDelegation { item, .. } => item.span, } } @@ -387,7 +407,7 @@ impl Invocation { InvocationKind::Bang { span, .. } => span, InvocationKind::Attr { attr, .. } => &mut attr.span, InvocationKind::Derive { path, .. } => &mut path.span, - InvocationKind::GlobDelegation { item } => &mut item.span, + InvocationKind::GlobDelegation { item, .. } => &mut item.span, } } } @@ -721,6 +741,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { && matches!( item_inner.kind, ItemKind::Mod( + _, _, ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _, _), ) @@ -770,8 +791,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } Err(err) => { - let guar = err.emit(); - fragment_kind.dummy(span, guar) + let _guar = err.emit(); + fragment_kind.expect_from_annotatables(iter::once(item)) } } } @@ -810,7 +831,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } _ => unreachable!(), }, - InvocationKind::GlobDelegation { item } => { + InvocationKind::GlobDelegation { item, of_trait } => { let AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() }; let suffixes = match ext { SyntaxExtensionKind::GlobDelegation(expander) => match expander.expand(self.cx) @@ -819,7 +840,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { ExpandResult::Retry(()) => { // Reassemble the original invocation for retrying. return ExpandResult::Retry(Invocation { - kind: InvocationKind::GlobDelegation { item }, + kind: InvocationKind::GlobDelegation { item, of_trait }, ..invoc }); } @@ -837,7 +858,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx, deleg, &item, &suffixes, item.span, true, ); fragment_kind.expect_from_annotatables( - single_delegations.map(|item| Annotatable::AssocItem(P(item), AssocCtxt::Impl)), + single_delegations + .map(|item| Annotatable::AssocItem(P(item), AssocCtxt::Impl { of_trait })), ) } }) @@ -865,7 +887,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { | Annotatable::GenericParam(..) | Annotatable::Param(..) | Annotatable::FieldDef(..) - | Annotatable::Variant(..) => panic!("unexpected annotatable"), + | Annotatable::Variant(..) + | Annotatable::WherePredicate(..) => panic!("unexpected annotatable"), }; if self.cx.ecfg.features.proc_macro_hygiene() { return; @@ -887,7 +910,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { impl<'ast, 'a> Visitor<'ast> for GateProcMacroInput<'a> { fn visit_item(&mut self, item: &'ast ast::Item) { match &item.kind { - ItemKind::Mod(_, mod_kind) + ItemKind::Mod(_, _, mod_kind) if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _)) => { feature_err( @@ -962,6 +985,13 @@ pub fn parse_ast_fragment<'a>( } AstFragment::ImplItems(items) } + AstFragmentKind::TraitImplItems => { + let mut items = SmallVec::new(); + while let Some(item) = this.parse_impl_item(ForceCollect::No)? { + items.extend(item); + } + AstFragment::TraitImplItems(items) + } AstFragmentKind::ForeignItems => { let mut items = SmallVec::new(); while let Some(item) = this.parse_foreign_item(ForceCollect::No)? { @@ -972,7 +1002,7 @@ pub fn parse_ast_fragment<'a>( AstFragmentKind::Stmts => { let mut stmts = SmallVec::new(); // Won't make progress on a `}`. - while this.token != token::Eof && this.token != token::CloseDelim(Delimiter::Brace) { + while this.token != token::Eof && this.token != token::CloseBrace { if let Some(stmt) = this.parse_full_stmt(AttemptLocalParseRecovery::Yes)? { stmts.push(stmt); } @@ -1002,7 +1032,8 @@ pub fn parse_ast_fragment<'a>( | AstFragmentKind::GenericParams | AstFragmentKind::Params | AstFragmentKind::FieldDefs - | AstFragmentKind::Variants => panic!("unexpected AST fragment kind"), + | AstFragmentKind::Variants + | AstFragmentKind::WherePredicates => panic!("unexpected AST fragment kind"), }) } @@ -1085,7 +1116,6 @@ enum AddSemicolon { /// of functionality used by `InvocationCollector`. trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { type OutputTy = SmallVec<[Self; 1]>; - type AttrsTy: Deref = ast::AttrVec; type ItemKind = ItemKind; const KIND: AstFragmentKind; fn to_annotatable(self) -> Annotatable; @@ -1102,7 +1132,7 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { fn is_mac_call(&self) -> bool { false } - fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { unreachable!() } fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { @@ -1136,9 +1166,9 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { collector.cx.dcx().emit_err(RemoveNodeNotSupported { span, descr: Self::descr() }); } - /// All of the names (items) declared by this node. + /// All of the identifiers (items) declared by this node. /// This is an approximation and should only be used for diagnostics. - fn declared_names(&self) -> Vec { + fn declared_idents(&self) -> Vec { vec![] } } @@ -1157,7 +1187,7 @@ impl InvocationCollectorNode for P { fn is_mac_call(&self) -> bool { matches!(self.kind, ItemKind::MacCall(..)) } - fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { let node = self.into_inner(); match node.kind { ItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), @@ -1189,9 +1219,8 @@ impl InvocationCollectorNode for P { } // Work around borrow checker not seeing through `P`'s deref. - let (ident, span, mut attrs) = (node.ident, node.span, mem::take(&mut node.attrs)); - let ItemKind::Mod(_, mod_kind) = &mut node.kind else { unreachable!() }; - + let (span, mut attrs) = (node.span, mem::take(&mut node.attrs)); + let ItemKind::Mod(_, ident, ref mut mod_kind) = node.kind else { unreachable!() }; let ecx = &mut collector.cx; let (file_path, dir_path, dir_ownership) = match mod_kind { ModKind::Loaded(_, inline, _, _) => { @@ -1273,7 +1302,8 @@ impl InvocationCollectorNode for P { collector.cx.current_expansion.module = orig_module; res } - fn declared_names(&self) -> Vec { + + fn declared_idents(&self) -> Vec { if let ItemKind::Use(ut) = &self.kind { fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec) { match &ut.kind { @@ -1292,7 +1322,7 @@ impl InvocationCollectorNode for P { return idents; } - vec![self.ident] + if let Some(ident) = self.kind.ident() { vec![ident] } else { vec![] } } } @@ -1313,7 +1343,7 @@ impl InvocationCollectorNode for AstNodeWrapper, TraitItemTag> fn is_mac_call(&self) -> bool { matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) } - fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { let item = self.wrapped.into_inner(); match item.kind { AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), @@ -1343,18 +1373,18 @@ impl InvocationCollectorNode for AstNodeWrapper, ImplItemTag> type ItemKind = AssocItemKind; const KIND: AstFragmentKind = AstFragmentKind::ImplItems; fn to_annotatable(self) -> Annotatable { - Annotatable::AssocItem(self.wrapped, AssocCtxt::Impl) + Annotatable::AssocItem(self.wrapped, AssocCtxt::Impl { of_trait: false }) } fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { fragment.make_impl_items() } fn walk_flat_map(self, visitor: &mut V) -> Self::OutputTy { - walk_flat_map_assoc_item(visitor, self.wrapped, AssocCtxt::Impl) + walk_flat_map_assoc_item(visitor, self.wrapped, AssocCtxt::Impl { of_trait: false }) } fn is_mac_call(&self) -> bool { matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) } - fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { let item = self.wrapped.into_inner(); match item.kind { AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), @@ -1378,6 +1408,47 @@ impl InvocationCollectorNode for AstNodeWrapper, ImplItemTag> } } +struct TraitImplItemTag; +impl InvocationCollectorNode for AstNodeWrapper, TraitImplItemTag> { + type OutputTy = SmallVec<[P; 1]>; + type ItemKind = AssocItemKind; + const KIND: AstFragmentKind = AstFragmentKind::TraitImplItems; + fn to_annotatable(self) -> Annotatable { + Annotatable::AssocItem(self.wrapped, AssocCtxt::Impl { of_trait: true }) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_trait_impl_items() + } + fn walk_flat_map(self, visitor: &mut V) -> Self::OutputTy { + walk_flat_map_assoc_item(visitor, self.wrapped, AssocCtxt::Impl { of_trait: true }) + } + fn is_mac_call(&self) -> bool { + matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { + let item = self.wrapped.into_inner(); + match item.kind { + AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + match &self.wrapped.kind { + AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), + _ => None, + } + } + fn delegation_item_kind(deleg: Box) -> Self::ItemKind { + AssocItemKind::Delegation(deleg) + } + fn from_item(item: ast::Item) -> Self { + AstNodeWrapper::new(P(item), TraitImplItemTag) + } + fn flatten_outputs(items: impl Iterator) -> Self::OutputTy { + items.flatten().collect() + } +} + impl InvocationCollectorNode for P { const KIND: AstFragmentKind = AstFragmentKind::ForeignItems; fn to_annotatable(self) -> Annotatable { @@ -1392,7 +1463,7 @@ impl InvocationCollectorNode for P { fn is_mac_call(&self) -> bool { matches!(self.kind, ForeignItemKind::MacCall(..)) } - fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { let node = self.into_inner(); match node.kind { ForeignItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), @@ -1414,6 +1485,19 @@ impl InvocationCollectorNode for ast::Variant { } } +impl InvocationCollectorNode for ast::WherePredicate { + const KIND: AstFragmentKind = AstFragmentKind::WherePredicates; + fn to_annotatable(self) -> Annotatable { + Annotatable::WherePredicate(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_where_predicates() + } + fn walk_flat_map(self, visitor: &mut V) -> Self::OutputTy { + walk_flat_map_where_predicate(visitor, self) + } +} + impl InvocationCollectorNode for ast::FieldDef { const KIND: AstFragmentKind = AstFragmentKind::FieldDefs; fn to_annotatable(self) -> Annotatable { @@ -1493,7 +1577,6 @@ impl InvocationCollectorNode for ast::Arm { } impl InvocationCollectorNode for ast::Stmt { - type AttrsTy = ast::AttrVec; const KIND: AstFragmentKind = AstFragmentKind::Stmts; fn to_annotatable(self) -> Annotatable { Annotatable::Stmt(P(self)) @@ -1513,7 +1596,7 @@ impl InvocationCollectorNode for ast::Stmt { StmtKind::Let(..) | StmtKind::Empty => false, } } - fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { // We pull macro invocations (both attributes and fn-like macro calls) out of their // `StmtKind`s and treat them as statement macro invocations, not as items or expressions. let (add_semicolon, mac, attrs) = match self.kind { @@ -1607,7 +1690,7 @@ impl InvocationCollectorNode for P { fn is_mac_call(&self) -> bool { matches!(self.kind, ast::TyKind::MacCall(..)) } - fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { let node = self.into_inner(); match node.kind { TyKind::MacCall(mac) => (mac, AttrVec::new(), AddSemicolon::No), @@ -1631,7 +1714,7 @@ impl InvocationCollectorNode for P { fn is_mac_call(&self) -> bool { matches!(self.kind, PatKind::MacCall(..)) } - fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { let node = self.into_inner(); match node.kind { PatKind::MacCall(mac) => (mac, AttrVec::new(), AddSemicolon::No), @@ -1642,7 +1725,6 @@ impl InvocationCollectorNode for P { impl InvocationCollectorNode for P { type OutputTy = P; - type AttrsTy = ast::AttrVec; const KIND: AstFragmentKind = AstFragmentKind::Expr; fn to_annotatable(self) -> Annotatable { Annotatable::Expr(self) @@ -1659,7 +1741,7 @@ impl InvocationCollectorNode for P { fn is_mac_call(&self) -> bool { matches!(self.kind, ExprKind::MacCall(..)) } - fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { let node = self.into_inner(); match node.kind { ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), @@ -1671,7 +1753,6 @@ impl InvocationCollectorNode for P { struct OptExprTag; impl InvocationCollectorNode for AstNodeWrapper, OptExprTag> { type OutputTy = Option>; - type AttrsTy = ast::AttrVec; const KIND: AstFragmentKind = AstFragmentKind::OptExpr; fn to_annotatable(self) -> Annotatable { Annotatable::Expr(self.wrapped) @@ -1686,7 +1767,7 @@ impl InvocationCollectorNode for AstNodeWrapper, OptExprTag> { fn is_mac_call(&self) -> bool { matches!(self.wrapped.kind, ast::ExprKind::MacCall(..)) } - fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { let node = self.wrapped.into_inner(); match node.kind { ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), @@ -1708,7 +1789,6 @@ impl DummyAstNode for MethodReceiverTag { } impl InvocationCollectorNode for AstNodeWrapper, MethodReceiverTag> { type OutputTy = Self; - type AttrsTy = ast::AttrVec; const KIND: AstFragmentKind = AstFragmentKind::MethodReceiverExpr; fn descr() -> &'static str { "an expression" @@ -1725,7 +1805,7 @@ impl InvocationCollectorNode for AstNodeWrapper, MethodReceiverTag> fn is_mac_call(&self) -> bool { matches!(self.wrapped.kind, ast::ExprKind::MacCall(..)) } - fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + fn take_mac_call(self) -> (P, ast::AttrVec, AddSemicolon) { let node = self.wrapped.into_inner(); match node.kind { ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), @@ -1758,11 +1838,11 @@ fn build_single_delegations<'a, Node: InvocationCollectorNode>( id: ast::DUMMY_NODE_ID, span: if from_glob { item_span } else { ident.span }, vis: item.vis.clone(), - ident: rename.unwrap_or(ident), kind: Node::delegation_item_kind(Box::new(ast::Delegation { id: ast::DUMMY_NODE_ID, qself: deleg.qself.clone(), path, + ident: rename.unwrap_or(ident), rename, body: deleg.body.clone(), from_glob, @@ -1830,9 +1910,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect_glob_delegation( &mut self, item: P, + of_trait: bool, kind: AstFragmentKind, ) -> AstFragment { - self.collect(kind, InvocationKind::GlobDelegation { item }) + self.collect(kind, InvocationKind::GlobDelegation { item, of_trait }) } /// If `item` is an attribute invocation, remove the attribute and return it together with @@ -1896,7 +1977,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let mut span: Option = None; while let Some(attr) = attrs.next() { rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features); - validate_attr::check_attr(&self.cx.sess.psess, attr); + validate_attr::check_attr( + &self.cx.sess.psess, + attr, + self.cx.current_expansion.lint_node_id, + ); let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; span = Some(current_span); @@ -1916,7 +2001,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let attr_name = attr.ident().unwrap().name; // `#[cfg]` and `#[cfg_attr]` are special - they are // eagerly evaluated. - if attr_name != sym::cfg && attr_name != sym::cfg_attr { + if attr_name != sym::cfg_trace && attr_name != sym::cfg_attr_trace { self.cx.sess.psess.buffer_lint( UNUSED_ATTRIBUTES, attr.span, @@ -1940,11 +2025,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { ) -> (bool, Option) { let (res, meta_item) = self.cfg().cfg_true(&attr); if res { - // FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion, - // and some tools like rustdoc and clippy rely on that. Find a way to remove them - // while keeping the tools working. - self.cx.expanded_inert_attrs.mark(&attr); - node.visit_attrs(|attrs| attrs.insert(pos, attr)); + // A trace attribute left in AST in place of the original `cfg` attribute. + // It can later be used by lints or other diagnostics. + let trace_attr = attr_into_trace(attr, sym::cfg_trace); + node.visit_attrs(|attrs| attrs.insert(pos, trace_attr)); } (res, meta_item) @@ -1966,25 +2050,25 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { ) -> Node::OutputTy { loop { return match self.take_first_attr(&mut node) { - Some((attr, pos, derives)) => match attr.name_or_empty() { - sym::cfg => { + Some((attr, pos, derives)) => match attr.name() { + Some(sym::cfg) => { let (res, meta_item) = self.expand_cfg_true(&mut node, attr, pos); if res { continue; } if let Some(meta_item) = meta_item { - for name in node.declared_names() { + for ident in node.declared_idents() { self.cx.resolver.append_stripped_cfg_item( self.cx.current_expansion.lint_node_id, - name, + ident, meta_item.clone(), ) } } Default::default() } - sym::cfg_attr => { + Some(sym::cfg_attr) => { self.expand_cfg_attr(&mut node, &attr, pos); continue; } @@ -2005,8 +2089,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let Some(suffixes) = &deleg.suffixes else { let traitless_qself = matches!(&deleg.qself, Some(qself) if qself.position == 0); - let item = match node.to_annotatable() { - Annotatable::AssocItem(item, AssocCtxt::Impl) => item, + let (item, of_trait) = match node.to_annotatable() { + Annotatable::AssocItem(item, AssocCtxt::Impl { of_trait }) => { + (item, of_trait) + } ann @ (Annotatable::Item(_) | Annotatable::AssocItem(..) | Annotatable::Stmt(_)) => { @@ -2021,7 +2107,9 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.cx.dcx().emit_err(GlobDelegationTraitlessQpath { span }); return Default::default(); } - return self.collect_glob_delegation(item, Node::KIND).make_ast::(); + return self + .collect_glob_delegation(item, of_trait, Node::KIND) + .make_ast::(); }; let single_delegations = build_single_delegations::( @@ -2053,8 +2141,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { ) { loop { return match self.take_first_attr(node) { - Some((attr, pos, derives)) => match attr.name_or_empty() { - sym::cfg => { + Some((attr, pos, derives)) => match attr.name() { + Some(sym::cfg) => { let span = attr.span; if self.expand_cfg_true(node, attr, pos).0 { continue; @@ -2063,7 +2151,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { node.expand_cfg_false(self, pos, span); continue; } - sym::cfg_attr => { + Some(sym::cfg_attr) => { self.expand_cfg_attr(node, &attr, pos); continue; } @@ -2101,7 +2189,12 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { ) -> SmallVec<[P; 1]> { match ctxt { AssocCtxt::Trait => self.flat_map_node(AstNodeWrapper::new(node, TraitItemTag)), - AssocCtxt::Impl => self.flat_map_node(AstNodeWrapper::new(node, ImplItemTag)), + AssocCtxt::Impl { of_trait: false } => { + self.flat_map_node(AstNodeWrapper::new(node, ImplItemTag)) + } + AssocCtxt::Impl { of_trait: true } => { + self.flat_map_node(AstNodeWrapper::new(node, TraitImplItemTag)) + } } } @@ -2116,6 +2209,13 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { self.flat_map_node(node) } + fn flat_map_where_predicate( + &mut self, + node: ast::WherePredicate, + ) -> SmallVec<[ast::WherePredicate; 1]> { + self.flat_map_node(node) + } + fn flat_map_field_def(&mut self, node: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> { self.flat_map_node(node) } diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 777044e3f33bf..cd744977bb323 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -5,7 +5,6 @@ #![feature(array_windows)] #![feature(associated_type_defaults)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(macro_metavar_expr)] #![feature(map_try_insert)] #![feature(proc_macro_diagnostic)] @@ -13,7 +12,6 @@ #![feature(rustdoc_internals)] #![feature(try_blocks)] #![feature(yeet_expr)] -#![warn(unreachable_pub)] // tidy-alphabetical-end extern crate proc_macro as pm; diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index e60a9e83184fb..698492f42e28f 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream::TokenStream; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage}; use rustc_macros::Subdiagnostic; @@ -66,10 +66,8 @@ pub(super) fn failed_to_match_macro( } if let MatcherLoc::Token { token: expected_token } = &remaining_matcher - && (matches!(expected_token.kind, TokenKind::Interpolated(_)) - || matches!(token.kind, TokenKind::Interpolated(_)) - || matches!(expected_token.kind, TokenKind::OpenDelim(Delimiter::Invisible(_))) - || matches!(token.kind, TokenKind::OpenDelim(Delimiter::Invisible(_)))) + && (matches!(expected_token.kind, token::OpenInvisible(_)) + || matches!(token.kind, token::OpenInvisible(_))) { err.note("captured metavariables except for `:tt`, `:ident` and `:lifetime` cannot be compared to other tokens"); err.note("see for more information"); @@ -162,7 +160,7 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match .is_none_or(|failure| failure.is_better_position(*approx_position)) { self.best_failure = Some(BestFailure { - token: token.clone(), + token: *token, position_in_tokenstream: *approx_position, msg, remaining_matcher: self diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 729dec2bfbdb1..1a2db233b7a64 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -432,7 +432,7 @@ fn check_nested_occurrences( } ( NestedMacroState::MacroRules, - &TokenTree::Token(Token { kind: TokenKind::Not, .. }), + &TokenTree::Token(Token { kind: TokenKind::Bang, .. }), ) => { state = NestedMacroState::MacroRulesNot; } diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index d709fd792817a..c78beb40688fe 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -179,11 +179,11 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec { for tt in tts { match tt { TokenTree::Token(token) => { - locs.push(MatcherLoc::Token { token: token.clone() }); + locs.push(MatcherLoc::Token { token: *token }); } TokenTree::Delimited(span, _, delimited) => { - let open_token = Token::new(token::OpenDelim(delimited.delim), span.open); - let close_token = Token::new(token::CloseDelim(delimited.delim), span.close); + let open_token = Token::new(delimited.delim.as_open_token_kind(), span.open); + let close_token = Token::new(delimited.delim.as_close_token_kind(), span.close); locs.push(MatcherLoc::Delimited); locs.push(MatcherLoc::Token { token: open_token }); @@ -648,7 +648,7 @@ impl TtParser { // There are no possible next positions AND we aren't waiting for the black-box // parser: syntax error. return Failure(T::build_failure( - parser.token.clone(), + parser.token, parser.approx_token_stream_pos(), "no rules expected this token in macro call", )); diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index cc7e3e651050a..783f061ec6c58 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -1,17 +1,18 @@ use std::borrow::Cow; use std::collections::hash_map::Entry; +use std::sync::Arc; use std::{mem, slice}; use ast::token::IdentIsRaw; use rustc_ast::token::NtPatKind::*; use rustc_ast::token::TokenKind::*; -use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind}; +use rustc_ast::token::{self, NonterminalKind, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId}; use rustc_ast_pretty::pprust; -use rustc_attr_parsing::{AttributeKind, find_attr}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; -use rustc_errors::{Applicability, ErrorGuaranteed}; +use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; use rustc_feature::Features; use rustc_hir as hir; use rustc_lint_defs::BuiltinLintDiag; @@ -26,19 +27,18 @@ use rustc_span::hygiene::Transparency; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, kw, sym}; use tracing::{debug, instrument, trace, trace_span}; -use super::diagnostics; use super::macro_parser::{NamedMatches, NamedParseResult}; +use super::{SequenceRepetition, diagnostics}; use crate::base::{ DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult, SyntaxExtension, SyntaxExtensionKind, TTMacroExpander, }; use crate::expand::{AstFragment, AstFragmentKind, ensure_complete_parse, parse_ast_fragment}; -use crate::mbe; use crate::mbe::diagnostics::{annotate_doc_comment, parse_failure_msg}; -use crate::mbe::macro_check; use crate::mbe::macro_parser::NamedMatch::*; use crate::mbe::macro_parser::{Error, ErrorReported, Failure, MatcherLoc, Success, TtParser}; use crate::mbe::transcribe::transcribe; +use crate::mbe::{self, KleeneOp, macro_check}; pub(crate) struct ParserAnyMacro<'a> { parser: Parser<'a>, @@ -388,7 +388,7 @@ pub fn compile_declarative_macro( node_id != DUMMY_NODE_ID, ) }; - let dummy_syn_ext = |guar| (mk_syn_ext(Box::new(DummyExpander(guar))), Vec::new()); + let dummy_syn_ext = |guar| (mk_syn_ext(Arc::new(DummyExpander(guar))), Vec::new()); let lhs_nm = Ident::new(sym::lhs, span); let rhs_nm = Ident::new(sym::rhs, span); @@ -582,7 +582,7 @@ pub fn compile_declarative_macro( }) .collect(); - let expander = Box::new(MacroRulesMacroExpander { + let expander = Arc::new(MacroRulesMacroExpander { name: ident, span, node_id, @@ -639,6 +639,37 @@ fn is_empty_token_tree(sess: &Session, seq: &mbe::SequenceRepetition) -> bool { } } +/// Checks if a `vis` nonterminal fragment is unnecessarily wrapped in an optional repetition. +/// +/// When a `vis` fragment (which can already be empty) is wrapped in `$(...)?`, +/// this suggests removing the redundant repetition syntax since it provides no additional benefit. +fn check_redundant_vis_repetition( + err: &mut Diag<'_>, + sess: &Session, + seq: &SequenceRepetition, + span: &DelimSpan, +) { + let is_zero_or_one: bool = seq.kleene.op == KleeneOp::ZeroOrOne; + let is_vis = seq.tts.first().map_or(false, |tt| { + matches!(tt, mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis))) + }); + + if is_vis && is_zero_or_one { + err.note("a `vis` fragment can already be empty"); + err.multipart_suggestion( + "remove the `$(` and `)?`", + vec![ + ( + sess.source_map().span_extend_to_prev_char_before(span.open, '$', true), + "".to_string(), + ), + (span.close.with_hi(seq.kleene.span.hi()), "".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } +} + /// Checks that the lhs contains no repetition which could match an empty token /// tree, because then the matcher would hang indefinitely. fn check_lhs_no_empty_seq(sess: &Session, tts: &[mbe::TokenTree]) -> Result<(), ErrorGuaranteed> { @@ -653,8 +684,10 @@ fn check_lhs_no_empty_seq(sess: &Session, tts: &[mbe::TokenTree]) -> Result<(), TokenTree::Sequence(span, seq) => { if is_empty_token_tree(sess, seq) { let sp = span.entire(); - let guar = sess.dcx().span_err(sp, "repetition matches empty token tree"); - return Err(guar); + let mut err = + sess.dcx().struct_span_err(sp, "repetition matches empty token tree"); + check_redundant_vis_repetition(&mut err, sess, seq, span); + return Err(err.emit()); } check_lhs_no_empty_seq(sess, &seq.tts)? } @@ -690,7 +723,7 @@ fn has_compile_error_macro(rhs: &mbe::TokenTree) -> bool { && let TokenKind::Ident(ident, _) = ident.kind && ident == sym::compile_error && let mbe::TokenTree::Token(bang) = bang - && let TokenKind::Not = bang.kind + && let TokenKind::Bang = bang.kind && let mbe::TokenTree::Delimited(.., del) = args && !del.delim.skip() { @@ -751,7 +784,7 @@ impl<'tt> FirstSets<'tt> { TokenTree::Delimited(span, _, delimited) => { build_recur(sets, &delimited.tts); first.replace_with(TtHandle::from_token_kind( - token::OpenDelim(delimited.delim), + delimited.delim.as_open_token_kind(), span.open, )); } @@ -777,7 +810,7 @@ impl<'tt> FirstSets<'tt> { // token could be the separator token itself. if let (Some(sep), true) = (&seq_rep.separator, subfirst.maybe_empty) { - first.add_one_maybe(TtHandle::from_token(sep.clone())); + first.add_one_maybe(TtHandle::from_token(*sep)); } // Reverse scan: Sequence comes before `first`. @@ -819,7 +852,7 @@ impl<'tt> FirstSets<'tt> { } TokenTree::Delimited(span, _, delimited) => { first.add_one(TtHandle::from_token_kind( - token::OpenDelim(delimited.delim), + delimited.delim.as_open_token_kind(), span.open, )); return first; @@ -840,7 +873,7 @@ impl<'tt> FirstSets<'tt> { // If the sequence contents can be empty, then the first // token could be the separator token itself. if let (Some(sep), true) = (&seq_rep.separator, subfirst.maybe_empty) { - first.add_one_maybe(TtHandle::from_token(sep.clone())); + first.add_one_maybe(TtHandle::from_token(*sep)); } assert!(first.maybe_empty); @@ -916,7 +949,7 @@ impl<'tt> Clone for TtHandle<'tt> { // This variant *must* contain a `mbe::TokenTree::Token`, and not // any other variant of `mbe::TokenTree`. TtHandle::Token(mbe::TokenTree::Token(tok)) => { - TtHandle::Token(mbe::TokenTree::Token(tok.clone())) + TtHandle::Token(mbe::TokenTree::Token(*tok)) } _ => unreachable!(), @@ -1066,7 +1099,7 @@ fn check_matcher_core<'tt>( } TokenTree::Delimited(span, _, d) => { let my_suffix = TokenSet::singleton(TtHandle::from_token_kind( - token::CloseDelim(d.delim), + d.delim.as_close_token_kind(), span.close, )); check_matcher_core(sess, node_id, first_sets, &d.tts, &my_suffix)?; @@ -1092,7 +1125,7 @@ fn check_matcher_core<'tt>( let mut new; let my_suffix = if let Some(sep) = &seq_rep.separator { new = suffix_first.clone(); - new.add_one_maybe(TtHandle::from_token(sep.clone())); + new.add_one_maybe(TtHandle::from_token(*sep)); &new } else { &suffix_first @@ -1135,7 +1168,7 @@ fn check_matcher_core<'tt>( && matches!(kind, NonterminalKind::Pat(PatParam { inferred: true })) && matches!( next_token, - TokenTree::Token(token) if *token == BinOp(token::BinOpToken::Or) + TokenTree::Token(token) if *token == token::Or ) { // It is suggestion to use pat_param, for example: $x:pat -> $x:pat_param. @@ -1177,7 +1210,7 @@ fn check_matcher_core<'tt>( if kind == NonterminalKind::Pat(PatWithOr) && sess.psess.edition.at_least_rust_2021() - && next_token.is_token(&BinOp(token::BinOpToken::Or)) + && next_token.is_token(&token::Or) { let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( span, @@ -1266,7 +1299,9 @@ enum IsInFollow { fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { use mbe::TokenTree; - if let TokenTree::Token(Token { kind: token::CloseDelim(_), .. }) = *tok { + if let TokenTree::Token(Token { kind, .. }) = tok + && kind.close_delim().is_some() + { // closing a token tree can never be matched by any fragment; // iow, we always require that `(` and `)` match, etc. IsInFollow::Yes @@ -1296,7 +1331,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { - FatArrow | Comma | Eq | BinOp(token::Or) => IsInFollow::Yes, + FatArrow | Comma | Eq | Or => IsInFollow::Yes, Ident(name, IdentIsRaw::No) if name == kw::If || name == kw::In => { IsInFollow::Yes } @@ -1325,16 +1360,8 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { ]; match tok { TokenTree::Token(token) => match token.kind { - OpenDelim(Delimiter::Brace) - | OpenDelim(Delimiter::Bracket) - | Comma - | FatArrow - | Colon - | Eq - | Gt - | BinOp(token::Shr) - | Semi - | BinOp(token::Or) => IsInFollow::Yes, + OpenBrace | OpenBracket | Comma | FatArrow | Colon | Eq | Gt | Shr + | Semi | Or => IsInFollow::Yes, Ident(name, IdentIsRaw::No) if name == kw::As || name == kw::Where => { IsInFollow::Yes } diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index b9a0615790790..0c2362f23bcfc 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -181,7 +181,10 @@ fn parse_tree<'a>( if delim != Delimiter::Parenthesis { span_dollar_dollar_or_metavar_in_the_lhs_err( sess, - &Token { kind: token::OpenDelim(delim), span: delim_span.entire() }, + &Token { + kind: delim.as_open_token_kind(), + span: delim_span.entire(), + }, ); } } else { @@ -217,7 +220,8 @@ fn parse_tree<'a>( } Delimiter::Parenthesis => {} _ => { - let token = pprust::token_kind_to_string(&token::OpenDelim(delim)); + let token = + pprust::token_kind_to_string(&delim.as_open_token_kind()); sess.dcx().emit_err(errors::ExpectedParenOrBrace { span: delim_span.entire(), token, @@ -274,7 +278,7 @@ fn parse_tree<'a>( let msg = format!("expected identifier, found `{}`", pprust::token_to_string(token),); sess.dcx().span_err(token.span, msg); - TokenTree::MetaVar(token.span, Ident::empty()) + TokenTree::MetaVar(token.span, Ident::dummy()) } // There are no more tokens. Just return the `$` we already have. @@ -283,7 +287,7 @@ fn parse_tree<'a>( } // `tree` is an arbitrary token. Keep it. - tokenstream::TokenTree::Token(token, _) => TokenTree::Token(token.clone()), + tokenstream::TokenTree::Token(token, _) => TokenTree::Token(*token), // `tree` is the beginning of a delimited set of tokens (e.g., `(` or `{`). We need to // descend into the delimited set and further parse it. @@ -302,8 +306,8 @@ fn parse_tree<'a>( /// `None`. fn kleene_op(token: &Token) -> Option { match token.kind { - token::BinOp(token::Star) => Some(KleeneOp::ZeroOrMore), - token::BinOp(token::Plus) => Some(KleeneOp::OneOrMore), + token::Star => Some(KleeneOp::ZeroOrMore), + token::Plus => Some(KleeneOp::OneOrMore), token::Question => Some(KleeneOp::ZeroOrOne), _ => None, } @@ -321,7 +325,7 @@ fn parse_kleene_op( match iter.next() { Some(tokenstream::TokenTree::Token(token, _)) => match kleene_op(token) { Some(op) => Ok(Ok((op, token.span))), - None => Ok(Err(token.clone())), + None => Ok(Err(*token)), }, tree => Err(tree.map_or(span, tokenstream::TokenTree::span)), } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 70107c80147fe..2d3fd7702da5b 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -1,13 +1,10 @@ use std::mem; -use std::sync::Arc; -use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{ - self, Delimiter, IdentIsRaw, InvisibleOrigin, Lit, LitKind, MetaVarKind, Nonterminal, Token, - TokenKind, + self, Delimiter, IdentIsRaw, InvisibleOrigin, Lit, LitKind, MetaVarKind, Token, TokenKind, }; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; -use rustc_ast::{ExprKind, TyKind}; +use rustc_ast::{ExprKind, StmtKind, TyKind, UnOp}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Diag, DiagCtxtHandle, PResult, pluralize}; use rustc_parse::lexer::nfc_normalize; @@ -31,10 +28,8 @@ use crate::mbe::{self, KleeneOp, MetaVarExpr}; // A Marker adds the given mark to the syntax context. struct Marker(LocalExpnId, Transparency, FxHashMap); -impl MutVisitor for Marker { - const VISIT_TOKENS: bool = true; - - fn visit_span(&mut self, span: &mut Span) { +impl Marker { + fn mark_span(&mut self, span: &mut Span) { // `apply_mark` is a relatively expensive operation, both due to taking hygiene lock, and // by itself. All tokens in a macro body typically have the same syntactic context, unless // it's some advanced case with macro-generated macros. So if we cache the marked version @@ -166,7 +161,7 @@ pub(super) fn transcribe<'a>( if repeat_idx < repeat_len { frame.idx = 0; if let Some(sep) = sep { - result.push(TokenTree::Token(sep.clone(), Spacing::Alone)); + result.push(TokenTree::Token(*sep, Spacing::Alone)); } continue; } @@ -294,7 +289,7 @@ pub(super) fn transcribe<'a>( // Emit as a token stream within `Delimiter::Invisible` to maintain // parsing priorities. - marker.visit_span(&mut sp); + marker.mark_span(&mut sp); with_metavar_spans(|mspans| mspans.insert(mk_span, sp)); // Both the open delim and close delim get the same span, which covers the // `$foo` in the decl macro RHS. @@ -308,26 +303,69 @@ pub(super) fn transcribe<'a>( let tt = match cur_matched { MatchedSingle(ParseNtResult::Tt(tt)) => { // `tt`s are emitted into the output stream directly as "raw tokens", - // without wrapping them into groups. + // without wrapping them into groups. Other variables are emitted into + // the output stream as groups with `Delimiter::Invisible` to maintain + // parsing priorities. maybe_use_metavar_location(psess, &stack, sp, tt, &mut marker) } MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => { - marker.visit_span(&mut sp); + marker.mark_span(&mut sp); with_metavar_spans(|mspans| mspans.insert(ident.span, sp)); let kind = token::NtIdent(*ident, *is_raw); TokenTree::token_alone(kind, sp) } MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => { - marker.visit_span(&mut sp); + marker.mark_span(&mut sp); with_metavar_spans(|mspans| mspans.insert(ident.span, sp)); let kind = token::NtLifetime(*ident, *is_raw); TokenTree::token_alone(kind, sp) } + MatchedSingle(ParseNtResult::Item(item)) => { + mk_delimited(item.span, MetaVarKind::Item, TokenStream::from_ast(item)) + } + MatchedSingle(ParseNtResult::Block(block)) => mk_delimited( + block.span, + MetaVarKind::Block, + TokenStream::from_ast(block), + ), + MatchedSingle(ParseNtResult::Stmt(stmt)) => { + let stream = if let StmtKind::Empty = stmt.kind { + // FIXME: Properly collect tokens for empty statements. + TokenStream::token_alone(token::Semi, stmt.span) + } else { + TokenStream::from_ast(stmt) + }; + mk_delimited(stmt.span, MetaVarKind::Stmt, stream) + } MatchedSingle(ParseNtResult::Pat(pat, pat_kind)) => mk_delimited( pat.span, MetaVarKind::Pat(*pat_kind), TokenStream::from_ast(pat), ), + MatchedSingle(ParseNtResult::Expr(expr, kind)) => { + let (can_begin_literal_maybe_minus, can_begin_string_literal) = + match &expr.kind { + ExprKind::Lit(_) => (true, true), + ExprKind::Unary(UnOp::Neg, e) + if matches!(&e.kind, ExprKind::Lit(_)) => + { + (true, false) + } + _ => (false, false), + }; + mk_delimited( + expr.span, + MetaVarKind::Expr { + kind: *kind, + can_begin_literal_maybe_minus, + can_begin_string_literal, + }, + TokenStream::from_ast(expr), + ) + } + MatchedSingle(ParseNtResult::Literal(lit)) => { + mk_delimited(lit.span, MetaVarKind::Literal, TokenStream::from_ast(lit)) + } MatchedSingle(ParseNtResult::Ty(ty)) => { let is_path = matches!(&ty.kind, TyKind::Path(None, _path)); mk_delimited( @@ -350,15 +388,6 @@ pub(super) fn transcribe<'a>( MatchedSingle(ParseNtResult::Vis(vis)) => { mk_delimited(vis.span, MetaVarKind::Vis, TokenStream::from_ast(vis)) } - MatchedSingle(ParseNtResult::Nt(nt)) => { - // Other variables are emitted into the output stream as groups with - // `Delimiter::Invisible` to maintain parsing priorities. - // `Interpolated` is currently used for such groups in rustc parser. - marker.visit_span(&mut sp); - let use_span = nt.use_span(); - with_metavar_spans(|mspans| mspans.insert(use_span, sp)); - TokenTree::token_alone(token::Interpolated(Arc::clone(nt)), sp) - } MatchedSeq(..) => { // We were unable to descend far enough. This is an error. return Err(dcx.create_err(VarStillRepeating { span: sp, ident })); @@ -368,8 +397,8 @@ pub(super) fn transcribe<'a>( } else { // If we aren't able to match the meta-var, we push it back into the result but // with modified syntax context. (I believe this supports nested macros). - marker.visit_span(&mut sp); - marker.visit_ident(&mut original_ident); + marker.mark_span(&mut sp); + marker.mark_span(&mut original_ident.span); result.push(TokenTree::token_joint_hidden(token::Dollar, sp)); result.push(TokenTree::Token( Token::from_ast_ident(original_ident), @@ -398,16 +427,19 @@ pub(super) fn transcribe<'a>( // jump back out of the Delimited, pop the result_stack and add the new results back to // the previous results (from outside the Delimited). &mbe::TokenTree::Delimited(mut span, ref spacing, ref delimited) => { - mut_visit::visit_delim_span(&mut marker, &mut span); + marker.mark_span(&mut span.open); + marker.mark_span(&mut span.close); stack.push(Frame::new_delimited(delimited, span, *spacing)); result_stack.push(mem::take(&mut result)); } // Nothing much to do here. Just push the token to the result, being careful to // preserve syntax context. - mbe::TokenTree::Token(token) => { - let mut token = token.clone(); - mut_visit::visit_token(&mut marker, &mut token); + &mbe::TokenTree::Token(mut token) => { + marker.mark_span(&mut token.span); + if let token::NtIdent(ident, _) | token::NtLifetime(ident, _) = &mut token.kind { + marker.mark_span(&mut ident.span); + } let tt = TokenTree::Token(token, Spacing::Alone); result.push(tt); } @@ -472,7 +504,7 @@ fn maybe_use_metavar_location( return orig_tt.clone(); } - marker.visit_span(&mut metavar_span); + marker.mark_span(&mut metavar_span); let no_collision = match orig_tt { TokenTree::Token(token, ..) => { with_metavar_spans(|mspans| mspans.insert(token.span, metavar_span)) @@ -742,7 +774,7 @@ fn transcribe_metavar_expr<'a>( ) -> PResult<'a, ()> { let mut visited_span = || { let mut span = sp.entire(); - marker.visit_span(&mut span); + marker.mark_span(&mut span); span }; match *expr { @@ -857,10 +889,8 @@ fn extract_symbol_from_pnr<'a>( }, _, )) => Ok(*symbol), - ParseNtResult::Nt(nt) - if let Nonterminal::NtLiteral(expr) = &**nt - && let ExprKind::Lit(Lit { kind: LitKind::Str, symbol, suffix: None }) = - &expr.kind => + ParseNtResult::Literal(expr) + if let ExprKind::Lit(Lit { kind: LitKind::Str, symbol, suffix: None }) = &expr.kind => { Ok(*symbol) } diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index e969f2d4fb55f..0136292decbcf 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -26,7 +26,7 @@ pub(crate) fn placeholder( }) } - let ident = Ident::empty(); + let ident = Ident::dummy(); let attrs = ast::AttrVec::new(); let vis = vis.unwrap_or(ast::Visibility { span: DUMMY_SP, @@ -62,7 +62,6 @@ pub(crate) fn placeholder( AstFragmentKind::Items => AstFragment::Items(smallvec![P(ast::Item { id, span, - ident, vis, attrs, kind: ast::ItemKind::MacCall(mac_placeholder()), @@ -71,7 +70,6 @@ pub(crate) fn placeholder( AstFragmentKind::TraitItems => AstFragment::TraitItems(smallvec![P(ast::AssocItem { id, span, - ident, vis, attrs, kind: ast::AssocItemKind::MacCall(mac_placeholder()), @@ -80,17 +78,25 @@ pub(crate) fn placeholder( AstFragmentKind::ImplItems => AstFragment::ImplItems(smallvec![P(ast::AssocItem { id, span, - ident, vis, attrs, kind: ast::AssocItemKind::MacCall(mac_placeholder()), tokens: None, })]), + AstFragmentKind::TraitImplItems => { + AstFragment::TraitImplItems(smallvec![P(ast::AssocItem { + id, + span, + vis, + attrs, + kind: ast::AssocItemKind::MacCall(mac_placeholder()), + tokens: None, + })]) + } AstFragmentKind::ForeignItems => { AstFragment::ForeignItems(smallvec![P(ast::ForeignItem { id, span, - ident, vis, attrs, kind: ast::ForeignItemKind::MacCall(mac_placeholder()), @@ -188,6 +194,19 @@ pub(crate) fn placeholder( vis, is_placeholder: true, }]), + AstFragmentKind::WherePredicates => { + AstFragment::WherePredicates(smallvec![ast::WherePredicate { + attrs: Default::default(), + id, + span, + kind: ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate { + bound_generic_params: Default::default(), + bounded_ty: ty(), + bounds: Default::default(), + }), + is_placeholder: true, + }]) + } } } @@ -267,6 +286,17 @@ impl MutVisitor for PlaceholderExpander { } } + fn flat_map_where_predicate( + &mut self, + predicate: ast::WherePredicate, + ) -> SmallVec<[ast::WherePredicate; 1]> { + if predicate.is_placeholder { + self.remove(predicate.id).make_where_predicates() + } else { + walk_flat_map_where_predicate(self, predicate) + } + } + fn flat_map_item(&mut self, item: P) -> SmallVec<[P; 1]> { match item.kind { ast::ItemKind::MacCall(_) => self.remove(item.id).make_items(), @@ -284,7 +314,8 @@ impl MutVisitor for PlaceholderExpander { let it = self.remove(item.id); match ctxt { AssocCtxt::Trait => it.make_trait_items(), - AssocCtxt::Impl => it.make_impl_items(), + AssocCtxt::Impl { of_trait: false } => it.make_impl_items(), + AssocCtxt::Impl { of_trait: true } => it.make_trait_impl_items(), } } _ => walk_flat_map_assoc_item(self, item, ctxt), diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index a6cdeaee176f1..d5af9849e759f 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -122,7 +122,7 @@ impl MultiItemModifier for DeriveProcMacro { // We had a lint for a long time, but now we just emit a hard error. // Eventually we might remove the special case hard error check // altogether. See #73345. - crate::base::ann_pretty_printing_compatibility_hack(&item, &ecx.sess); + crate::base::ann_pretty_printing_compatibility_hack(&item, &ecx.sess.psess); let input = item.to_tokens(); let stream = { let _timer = diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 5581a6e65089a..f00201ad202af 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -1,5 +1,4 @@ use std::ops::{Bound, Range}; -use std::sync::Arc; use ast::token::IdentIsRaw; use pm::bridge::{ @@ -18,7 +17,7 @@ use rustc_parse::parser::Parser; use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; use rustc_session::parse::ParseSess; use rustc_span::def_id::CrateNum; -use rustc_span::{BytePos, FileName, Pos, SourceFile, Span, Symbol, sym}; +use rustc_span::{BytePos, FileName, Pos, Span, Symbol, sym}; use smallvec::{SmallVec, smallvec}; use crate::base::ExtCtxt; @@ -115,11 +114,43 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec { - let delimiter = pm::Delimiter::from_internal(delim); + tokenstream::TokenTree::Delimited(span, _, mut delim, mut stream) => { + // We used to have an alternative behaviour for crates that + // needed it: a hack used to pass AST fragments to + // attribute and derive macros as a single nonterminal + // token instead of a token stream. Such token needs to be + // "unwrapped" and not represented as a delimited group. We + // had a lint for a long time, but now we just emit a hard + // error. Eventually we might remove the special case hard + // error check altogether. See #73345. + if let Delimiter::Invisible(InvisibleOrigin::MetaVar(kind)) = delim { + crate::base::stream_pretty_printing_compatibility_hack( + kind, + &stream, + rustc.psess(), + ); + } + + // In `mk_delimited` we avoid nesting invisible delimited + // of the same `MetaVarKind`. Here we do the same but + // ignore the `MetaVarKind` because it is discarded when we + // convert it to a `Group`. + while let Delimiter::Invisible(InvisibleOrigin::MetaVar(_)) = delim { + if stream.len() == 1 + && let tree = stream.iter().next().unwrap() + && let tokenstream::TokenTree::Delimited(_, _, delim2, stream2) = tree + && let Delimiter::Invisible(InvisibleOrigin::MetaVar(_)) = delim2 + { + delim = *delim2; + stream = stream2.clone(); + } else { + break; + } + } + trees.push(TokenTree::Group(Group { - delimiter, - stream: Some(tts), + delimiter: pm::Delimiter::from_internal(delim), + stream: Some(stream), span: DelimSpan { open: span.open, close: span.close, @@ -180,28 +211,28 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec op(">"), AndAnd => op("&&"), OrOr => op("||"), - Not => op("!"), + Bang => op("!"), Tilde => op("~"), - BinOp(Plus) => op("+"), - BinOp(Minus) => op("-"), - BinOp(Star) => op("*"), - BinOp(Slash) => op("/"), - BinOp(Percent) => op("%"), - BinOp(Caret) => op("^"), - BinOp(And) => op("&"), - BinOp(Or) => op("|"), - BinOp(Shl) => op("<<"), - BinOp(Shr) => op(">>"), - BinOpEq(Plus) => op("+="), - BinOpEq(Minus) => op("-="), - BinOpEq(Star) => op("*="), - BinOpEq(Slash) => op("/="), - BinOpEq(Percent) => op("%="), - BinOpEq(Caret) => op("^="), - BinOpEq(And) => op("&="), - BinOpEq(Or) => op("|="), - BinOpEq(Shl) => op("<<="), - BinOpEq(Shr) => op(">>="), + Plus => op("+"), + Minus => op("-"), + Star => op("*"), + Slash => op("/"), + Percent => op("%"), + Caret => op("^"), + And => op("&"), + Or => op("|"), + Shl => op("<<"), + Shr => op(">>"), + PlusEq => op("+="), + MinusEq => op("-="), + StarEq => op("*="), + SlashEq => op("/="), + PercentEq => op("%="), + CaretEq => op("^="), + AndEq => op("&="), + OrEq => op("|="), + ShlEq => op("<<="), + ShrEq => op(">>="), At => op("@"), Dot => op("."), DotDot => op(".."), @@ -277,26 +308,8 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec { - let stream = TokenStream::from_nonterminal_ast(&nt); - // We used to have an alternative behaviour for crates that - // needed it: a hack used to pass AST fragments to - // attribute and derive macros as a single nonterminal - // token instead of a token stream. Such token needs to be - // "unwrapped" and not represented as a delimited group. We - // had a lint for a long time, but now we just emit a hard - // error. Eventually we might remove the special case hard - // error check altogether. See #73345. - crate::base::nt_pretty_printing_compatibility_hack(&nt, rustc.ecx.sess); - trees.push(TokenTree::Group(Group { - delimiter: pm::Delimiter::None, - stream: Some(stream), - span: DelimSpan::from_single(span), - })) - } - - OpenDelim(..) | CloseDelim(..) => unreachable!(), - Eof => unreachable!(), + OpenParen | CloseParen | OpenBrace | CloseBrace | OpenBracket | CloseBracket + | OpenInvisible(_) | CloseInvisible(_) | Eof => unreachable!(), } } trees @@ -322,16 +335,16 @@ impl ToInternal> b'=' => Eq, b'<' => Lt, b'>' => Gt, - b'!' => Not, + b'!' => Bang, b'~' => Tilde, - b'+' => BinOp(Plus), - b'-' => BinOp(Minus), - b'*' => BinOp(Star), - b'/' => BinOp(Slash), - b'%' => BinOp(Percent), - b'^' => BinOp(Caret), - b'&' => BinOp(And), - b'|' => BinOp(Or), + b'+' => Plus, + b'-' => Minus, + b'*' => Star, + b'/' => Slash, + b'%' => Percent, + b'^' => Caret, + b'&' => And, + b'|' => Or, b'@' => At, b'.' => Dot, b',' => Comma, @@ -371,11 +384,10 @@ impl ToInternal> symbol, suffix, span, - }) if symbol.as_str().starts_with('-') => { - let minus = BinOp(BinOpToken::Minus); - let symbol = Symbol::intern(&symbol.as_str()[1..]); + }) if let Some(symbol) = symbol.as_str().strip_prefix('-') => { + let symbol = Symbol::intern(symbol); let integer = TokenKind::lit(token::Integer, symbol, suffix); - let a = tokenstream::TokenTree::token_joint_hidden(minus, span); + let a = tokenstream::TokenTree::token_joint_hidden(Minus, span); let b = tokenstream::TokenTree::token_alone(integer, span); smallvec![a, b] } @@ -384,11 +396,10 @@ impl ToInternal> symbol, suffix, span, - }) if symbol.as_str().starts_with('-') => { - let minus = BinOp(BinOpToken::Minus); - let symbol = Symbol::intern(&symbol.as_str()[1..]); + }) if let Some(symbol) = symbol.as_str().strip_prefix('-') => { + let symbol = Symbol::intern(symbol); let float = TokenKind::lit(token::Float, symbol, suffix); - let a = tokenstream::TokenTree::token_joint_hidden(minus, span); + let a = tokenstream::TokenTree::token_joint_hidden(Minus, span); let b = tokenstream::TokenTree::token_alone(float, span); smallvec![a, b] } @@ -446,7 +457,6 @@ impl<'a, 'b> Rustc<'a, 'b> { impl server::Types for Rustc<'_, '_> { type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; - type SourceFile = Arc; type Span = Span; type Symbol = Symbol; } @@ -599,10 +609,7 @@ impl server::TokenStream for Rustc<'_, '_> { Ok(Self::TokenStream::from_iter([ // FIXME: The span of the `-` token is lost when // parsing, so we cannot faithfully recover it here. - tokenstream::TokenTree::token_joint_hidden( - token::BinOp(token::Minus), - e.span, - ), + tokenstream::TokenTree::token_joint_hidden(token::Minus, e.span), tokenstream::TokenTree::token_alone(token::Literal(*token_lit), e.span), ])) } @@ -655,28 +662,6 @@ impl server::TokenStream for Rustc<'_, '_> { } } -impl server::SourceFile for Rustc<'_, '_> { - fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { - Arc::ptr_eq(file1, file2) - } - - fn path(&mut self, file: &Self::SourceFile) -> String { - match &file.name { - FileName::Real(name) => name - .local_path() - .expect("attempting to get a file path in an imported file in `proc_macro::SourceFile::path`") - .to_str() - .expect("non-UTF8 file path in `proc_macro::SourceFile::path`") - .to_string(), - _ => file.name.prefer_local().to_string(), - } - } - - fn is_real(&mut self, file: &Self::SourceFile) -> bool { - file.is_real_file() - } -} - impl server::Span for Rustc<'_, '_> { fn debug(&mut self, span: Self::Span) -> String { if self.ecx.ecfg.span_debug { @@ -686,8 +671,29 @@ impl server::Span for Rustc<'_, '_> { } } - fn source_file(&mut self, span: Self::Span) -> Self::SourceFile { - self.psess().source_map().lookup_char_pos(span.lo()).file + fn file(&mut self, span: Self::Span) -> String { + self.psess() + .source_map() + .lookup_char_pos(span.lo()) + .file + .name + .prefer_remapped_unconditionaly() + .to_string() + } + + fn local_file(&mut self, span: Self::Span) -> Option { + self.psess() + .source_map() + .lookup_char_pos(span.lo()) + .file + .name + .clone() + .into_local_path() + .map(|p| { + p.to_str() + .expect("non-UTF8 file path in `proc_macro::SourceFile::path`") + .to_string() + }) } fn parent(&mut self, span: Self::Span) -> Option { diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index d50fe8c6a3e6b..820af9ac84b2c 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -62,6 +62,8 @@ declare_features! ( (accepted, arbitrary_enum_discriminant, "1.66.0", Some(60553)), /// Allows using `const` operands in inline assembly. (accepted, asm_const, "1.82.0", Some(93332)), + /// Allows using `label` operands in inline assembly. + (accepted, asm_goto, "1.87.0", Some(119364)), /// Allows using `sym` operands in inline assembly. (accepted, asm_sym, "1.66.0", Some(93333)), /// Allows the definition of associated constants in `trait` or `impl` blocks. @@ -80,6 +82,8 @@ declare_features! ( (accepted, attr_literals, "1.30.0", Some(34981)), /// Allows overloading augmented assignment operations like `a += b`. (accepted, augmented_assignments, "1.8.0", Some(28235)), + /// Allows using `avx512*` target features. + (accepted, avx512_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), /// Allows mixing bind-by-move in patterns and references to those identifiers in guards. (accepted, bind_by_move_pattern_guards, "1.39.0", Some(15287)), /// Allows bindings in the subpattern of a binding pattern. @@ -93,6 +97,8 @@ declare_features! ( (accepted, c_unwind, "1.81.0", Some(74990)), /// Allows `#[cfg_attr(predicate, multiple, attributes, here)]`. (accepted, cfg_attr_multi, "1.33.0", Some(54881)), + /// Allows the use of `#[cfg()]`. + (accepted, cfg_boolean_literals, "1.88.0", Some(131204)), /// Allows the use of `#[cfg(doctest)]`, set when rustdoc is collecting doctests. (accepted, cfg_doctest, "1.40.0", Some(62210)), /// Enables `#[cfg(panic = "...")]` config key. @@ -296,6 +302,8 @@ declare_features! ( /// Allows patterns with concurrent by-move and by-ref bindings. /// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref. (accepted, move_ref_pattern, "1.49.0", Some(68354)), + /// Allows using `#[naked]` on functions. + (accepted, naked_functions, "1.88.0", Some(90957)), /// Allows specifying modifiers in the link attribute: `#[link(modifiers = "...")]` (accepted, native_link_modifiers, "1.61.0", Some(81490)), /// Allows specifying the bundle link modifier @@ -329,6 +337,8 @@ declare_features! ( (accepted, pattern_parentheses, "1.31.0", Some(51087)), /// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args. (accepted, precise_capturing, "1.82.0", Some(123432)), + /// Allows `use<..>` precise capturign on impl Trait in traits. + (accepted, precise_capturing_in_traits, "1.87.0", Some(130044)), /// Allows procedural macros in `proc-macro` crates. (accepted, proc_macro, "1.29.0", Some(38356)), /// Allows multi-segment paths in attributes and derives. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 306535bf7642e..c117e0fcf7ccc 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -6,6 +6,7 @@ use AttributeDuplicates::*; use AttributeGate::*; use AttributeType::*; use rustc_data_structures::fx::FxHashMap; +use rustc_span::edition::Edition; use rustc_span::{Symbol, sym}; use crate::{Features, Stability}; @@ -39,6 +40,26 @@ const GATED_CFGS: &[GatedCfg] = &[ // this is consistent with naming of the compiler flag it's for (sym::fmt_debug, sym::fmt_debug, Features::fmt_debug), (sym::emscripten_wasm_eh, sym::cfg_emscripten_wasm_eh, Features::cfg_emscripten_wasm_eh), + ( + sym::target_has_reliable_f16, + sym::cfg_target_has_reliable_f16_f128, + Features::cfg_target_has_reliable_f16_f128, + ), + ( + sym::target_has_reliable_f16_math, + sym::cfg_target_has_reliable_f16_f128, + Features::cfg_target_has_reliable_f16_f128, + ), + ( + sym::target_has_reliable_f128, + sym::cfg_target_has_reliable_f16_f128, + Features::cfg_target_has_reliable_f16_f128, + ), + ( + sym::target_has_reliable_f128_math, + sym::cfg_target_has_reliable_f16_f128, + Features::cfg_target_has_reliable_f16_f128, + ), ]; /// Find a gated cfg determined by the `pred`icate which is given the cfg's name. @@ -65,9 +86,12 @@ pub enum AttributeSafety { /// Normal attribute that does not need `#[unsafe(...)]` Normal, - /// Unsafe attribute that requires safety obligations - /// to be discharged - Unsafe, + /// Unsafe attribute that requires safety obligations to be discharged. + /// + /// An error is emitted when `#[unsafe(...)]` is omitted, except when the attribute's edition + /// is less than the one stored in `unsafe_since`. This handles attributes that were safe in + /// earlier editions, but become unsafe in later ones. + Unsafe { unsafe_since: Option }, } #[derive(Clone, Copy)] @@ -187,12 +211,23 @@ macro_rules! template { } macro_rules! ungated { + (unsafe($edition:ident) $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, + safety: AttributeSafety::Unsafe { unsafe_since: Some(Edition::$edition) }, + template: $tpl, + gate: Ungated, + duplicates: $duplicates, + } + }; (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, type_: $typ, - safety: AttributeSafety::Unsafe, + safety: AttributeSafety::Unsafe { unsafe_since: None }, template: $tpl, gate: Ungated, duplicates: $duplicates, @@ -217,7 +252,7 @@ macro_rules! gated { name: sym::$attr, encode_cross_crate: $encode_cross_crate, type_: $typ, - safety: AttributeSafety::Unsafe, + safety: AttributeSafety::Unsafe { unsafe_since: None }, template: $tpl, duplicates: $duplicates, gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate), @@ -228,7 +263,7 @@ macro_rules! gated { name: sym::$attr, encode_cross_crate: $encode_cross_crate, type_: $typ, - safety: AttributeSafety::Unsafe, + safety: AttributeSafety::Unsafe { unsafe_since: None }, template: $tpl, duplicates: $duplicates, gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr), @@ -423,11 +458,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), ungated!(no_link, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, EncodeCrossCrate::No), - ungated!(unsafe export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), - ungated!(unsafe link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), - ungated!(unsafe no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), + ungated!(unsafe(Edition2024) export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), + ungated!(unsafe(Edition2024) link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), + ungated!(unsafe(Edition2024) no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No), ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes), + ungated!(unsafe naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), // Limits: ungated!( @@ -502,8 +538,8 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Linking: gated!( - naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, - naked_functions, experimental!(naked) + export_stable, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, experimental!(export_stable) ), // Testing: @@ -576,6 +612,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::Yes, experimental!(patchable_function_entry) ), + // Probably temporary component of min_generic_const_args. + // `#[type_const] const ASSOC: usize;` + gated!( + type_const, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::Yes, min_generic_const_args, experimental!(type_const), + ), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== @@ -737,7 +780,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( rustc_macro_transparency, Normal, - template!(NameValueStr: "transparent|semitransparent|opaque"), ErrorFollowing, + template!(NameValueStr: "transparent|semiopaque|opaque"), ErrorFollowing, EncodeCrossCrate::Yes, "used internally for testing macro hygiene", ), rustc_attr!( @@ -745,6 +788,18 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(Word, List: r#""...""#), DuplicatesOk, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE ), + // Traces that are left when `cfg` and `cfg_attr` attributes are expanded. + // The attributes are not gated, to avoid stability errors, but they cannot be used in stable + // or unstable code directly because `sym::cfg_(attr_)trace` are not valid identifiers, they + // can only be generated by the compiler. + ungated!( + cfg_trace, Normal, template!(Word /* irrelevant */), DuplicatesOk, + EncodeCrossCrate::No + ), + ungated!( + cfg_attr_trace, Normal, template!(Word /* irrelevant */), DuplicatesOk, + EncodeCrossCrate::No + ), // ========================================================================== // Internal attributes, Diagnostics related: @@ -889,6 +944,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::Yes, "#[rustc_never_returns_null_ptr] is used to mark functions returning non-null pointers." ), + rustc_attr!( + rustc_no_implicit_autorefs, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, + "`#[rustc_no_implicit_autorefs]` is used to mark functions for which an autoref to the dereference of a raw pointer should not be used as an argument." + ), rustc_attr!( rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No, "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`." @@ -1060,9 +1119,9 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( - TEST, rustc_error, Normal, - template!(Word, List: "delayed_bug_from_inside_query"), - WarnFollowingWordOnly, EncodeCrossCrate::Yes + TEST, rustc_delayed_bug_from_inside_query, Normal, + template!(Word), + WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( TEST, rustc_dump_user_args, Normal, template!(Word), diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 0b034a2ae1075..25764755a8fc9 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -15,7 +15,6 @@ #![allow(internal_features)] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end mod accepted; diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index fc58b66ad35ec..687d859df5359 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -142,6 +142,9 @@ declare_features! ( /// Allows inferring `'static` outlives requirements (RFC 2093). (removed, infer_static_outlives_requirements, "1.63.0", Some(54185), Some("removed as it caused some confusion and discussion was inactive for years")), + /// Allow anonymous constants from an inline `const` block in pattern position + (removed, inline_const_pat, "1.88.0", Some(76001), + Some("removed due to implementation concerns as it requires significant refactorings")), /// Lazily evaluate constants. This allows constants to depend on type parameters. (removed, lazy_normalization_consts, "1.46.0", Some(72219), Some("superseded by `generic_const_exprs`")), /// Changes `impl Trait` to capture all lifetimes in scope. @@ -244,6 +247,8 @@ declare_features! ( /// Allows unnamed fields of struct and union type (removed, unnamed_fields, "1.83.0", Some(49804), Some("feature needs redesign")), (removed, unsafe_no_drop_flag, "1.0.0", None, None), + (removed, unsized_tuple_coercion, "1.87.0", Some(42877), + Some("The feature restricts possible layouts for tuples, and this restriction is not worth it.")), /// Allows `union` fields that don't implement `Copy` as long as they don't have any drop glue. (removed, untagged_unions, "1.13.0", Some(55149), Some("unions with `Copy` and `ManuallyDrop` fields are stable; there is no intent to stabilize more")), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 0acd3b0f8002a..6cdcf451f37e3 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -1,6 +1,7 @@ //! List of the unstable feature gates. use std::path::PathBuf; +use std::time::{SystemTime, UNIX_EPOCH}; use rustc_data_structures::fx::FxHashSet; use rustc_span::{Span, Symbol, sym}; @@ -204,13 +205,15 @@ declare_features! ( (unstable, anonymous_lifetime_in_impl_trait, "1.63.0", None), /// Allows access to the emscripten_wasm_eh config, used by panic_unwind and unwind (internal, cfg_emscripten_wasm_eh, "1.86.0", None), + /// Allows checking whether or not the backend correctly supports unstable float types. + (internal, cfg_target_has_reliable_f16_f128, "1.88.0", None), /// Allows identifying the `compiler_builtins` crate. (internal, compiler_builtins, "1.13.0", None), /// Allows writing custom MIR (internal, custom_mir, "1.65.0", None), /// Outputs useful `assert!` messages (unstable, generic_assert, "1.63.0", None), - /// Allows using the `rust-intrinsic`'s "ABI". + /// Allows using the #[rustc_intrinsic] attribute. (internal, intrinsics, "1.0.0", None), /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic. (internal, lang_items, "1.0.0", None), @@ -239,7 +242,7 @@ declare_features! ( /// Added for testing unstable lints; perma-unstable. (internal, test_unstable_lint, "1.60.0", None), /// Helps with formatting for `group_imports = "StdExternalCrate"`. - (unstable, unqualified_local_imports, "1.83.0", None), + (unstable, unqualified_local_imports, "1.83.0", Some(138299)), /// Use for stable + negative coherence and strict coherence depending on trait's /// rustc_strict_coherence value. (unstable, with_negative_coherence, "1.60.0", None), @@ -313,8 +316,8 @@ declare_features! ( // Unstable `#[target_feature]` directives. (unstable, aarch64_unstable_target_feature, "1.82.0", Some(44839)), (unstable, aarch64_ver_target_feature, "1.27.0", Some(44839)), + (unstable, apx_target_feature, "1.88.0", Some(139284)), (unstable, arm_target_feature, "1.27.0", Some(44839)), - (unstable, avx512_target_feature, "1.27.0", Some(44839)), (unstable, bpf_target_feature, "1.54.0", Some(44839)), (unstable, csky_target_feature, "1.73.0", Some(44839)), (unstable, ermsb_target_feature, "1.49.0", Some(44839)), @@ -323,6 +326,7 @@ declare_features! ( (unstable, loongarch_target_feature, "1.73.0", Some(44839)), (unstable, m68k_target_feature, "1.85.0", Some(134328)), (unstable, mips_target_feature, "1.27.0", Some(44839)), + (unstable, movrs_target_feature, "1.88.0", Some(137976)), (unstable, powerpc_target_feature, "1.27.0", Some(44839)), (unstable, prfchw_target_feature, "1.78.0", Some(44839)), (unstable, riscv_target_feature, "1.45.0", Some(44839)), @@ -371,8 +375,6 @@ declare_features! ( (unstable, asm_experimental_arch, "1.58.0", Some(93335)), /// Enables experimental register support in inline assembly. (unstable, asm_experimental_reg, "1.85.0", Some(133416)), - /// Allows using `label` operands in inline assembly. - (unstable, asm_goto, "1.78.0", Some(119364)), /// Allows using `label` operands in inline assembly together with output operands. (unstable, asm_goto_with_outputs, "1.85.0", Some(119364)), /// Allows the `may_unwind` option in inline assembly. @@ -381,6 +383,8 @@ declare_features! ( (unstable, associated_const_equality, "1.58.0", Some(92827)), /// Allows associated type defaults. (unstable, associated_type_defaults, "1.2.0", Some(29661)), + /// Allows implementing `AsyncDrop`. + (incomplete, async_drop, "1.88.0", Some(126482)), /// Allows async functions to be called from `dyn Trait`. (incomplete, async_fn_in_dyn_trait, "1.85.0", Some(133119)), /// Allows `#[track_caller]` on async functions. @@ -389,10 +393,10 @@ declare_features! ( (unstable, async_for_loop, "1.77.0", Some(118898)), /// Allows `async` trait bound modifier. (unstable, async_trait_bounds, "1.85.0", Some(62290)), + /// Allows using Intel AVX10 target features and intrinsics + (unstable, avx10_target_feature, "1.88.0", Some(138843)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), - /// Allows the use of `#[cfg()]`. - (unstable, cfg_boolean_literals, "1.83.0", Some(131204)), /// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled. (unstable, cfg_contract_checks, "1.86.0", Some(128044)), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. @@ -473,10 +477,16 @@ declare_features! ( (unstable, doc_masked, "1.21.0", Some(44027)), /// Allows `dyn* Trait` objects. (incomplete, dyn_star, "1.65.0", Some(102425)), + /// Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }` + (incomplete, ergonomic_clones, "1.87.0", Some(132290)), /// Allows exhaustive pattern matching on types that contain uninhabited types. (unstable, exhaustive_patterns, "1.13.0", Some(51085)), + /// Disallows `extern` without an explicit ABI. + (unstable, explicit_extern_abis, "1.88.0", Some(134986)), /// Allows explicit tail calls via `become` expression. (incomplete, explicit_tail_calls, "1.72.0", Some(112788)), + /// Allows using `#[export_stable]` which indicates that an item is exportable. + (incomplete, export_stable, "1.88.0", Some(139939)), /// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions /// for functions with varargs. (unstable, extended_varargs_abi_support, "1.65.0", Some(100189)), @@ -500,6 +510,8 @@ declare_features! ( (incomplete, fn_delegation, "1.76.0", Some(118212)), /// Allows impls for the Freeze trait. (internal, freeze_impls, "1.78.0", Some(121675)), + /// Frontmatter `---` blocks for use by external tools. + (unstable, frontmatter, "1.88.0", Some(136889)), /// Allows defining gen blocks and `gen fn`. (unstable, gen_blocks, "1.75.0", Some(117078)), /// Infer generic args for both consts and types. @@ -509,7 +521,7 @@ declare_features! ( /// Allows generic parameters and where-clauses on free & associated const items. (incomplete, generic_const_items, "1.73.0", Some(113521)), /// Allows the type of const generics to depend on generic parameters - (incomplete, generic_const_parameter_types, "CURRENT_RUSTC_VERSION", Some(137626)), + (incomplete, generic_const_parameter_types, "1.87.0", Some(137626)), /// Allows any generic constants being used as pattern type range ends (incomplete, generic_pattern_types, "1.86.0", Some(136574)), /// Allows registering static items globally, possibly across crates, to iterate over at runtime. @@ -530,8 +542,6 @@ declare_features! ( (unstable, import_trait_associated_functions, "1.86.0", Some(134691)), /// Allows associated types in inherent impls. (incomplete, inherent_associated_types, "1.52.0", Some(8995)), - /// Allow anonymous constants from an inline `const` block in pattern position - (unstable, inline_const_pat, "1.58.0", Some(76001)), /// Allows using `pointer` and `reference` in intra-doc links (unstable, intra_doc_pointers, "1.51.0", Some(80896)), // Allows using the `kl` and `widekl` target features and the associated intrinsics @@ -563,8 +573,10 @@ declare_features! ( (unstable, must_not_suspend, "1.57.0", Some(83310)), /// Allows `mut ref` and `mut ref mut` identifier patterns. (incomplete, mut_ref, "1.79.0", Some(123076)), - /// Allows using `#[naked]` on functions. - (unstable, naked_functions, "1.9.0", Some(90957)), + /// Allows using `#[naked]` on `extern "Rust"` functions. + (unstable, naked_functions_rustic_abi, "1.88.0", Some(138997)), + /// Allows using `#[target_feature(enable = "...")]` on `#[naked]` on functions. + (unstable, naked_functions_target_feature, "1.86.0", Some(138568)), /// Allows specifying the as-needed link modifier (unstable, native_link_modifiers_as_needed, "1.53.0", Some(81490)), /// Allow negative trait implementations. @@ -597,10 +609,10 @@ declare_features! ( (incomplete, pin_ergonomics, "1.83.0", Some(130494)), /// Allows postfix match `expr.match { ... }` (unstable, postfix_match, "1.79.0", Some(121618)), - /// Allows `use<..>` precise capturign on impl Trait in traits. - (unstable, precise_capturing_in_traits, "1.83.0", Some(130044)), /// Allows macro attributes on expressions, statements and non-inline modules. (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), + /// Allows the use of raw-dylibs on ELF platforms + (incomplete, raw_dylib_elf, "1.87.0", Some(135694)), /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024. (incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)), /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant @@ -627,6 +639,8 @@ declare_features! ( (unstable, strict_provenance_lints, "1.61.0", Some(130351)), /// Allows string patterns to dereference values to match them. (unstable, string_deref_patterns, "1.67.0", Some(87121)), + /// Allows `super let` statements. + (unstable, super_let, "1.88.0", Some(139076)), /// Allows subtrait items to shadow supertrait items. (unstable, supertrait_item_shadowing, "1.86.0", Some(89151)), /// Allows using `#[thread_local]` on `static` items. @@ -657,16 +671,17 @@ declare_features! ( (internal, unsized_fn_params, "1.49.0", Some(48055)), /// Allows unsized rvalues at arguments and parameters. (incomplete, unsized_locals, "1.30.0", Some(48055)), - /// Allows unsized tuple coercion. - (unstable, unsized_tuple_coercion, "1.20.0", Some(42877)), /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute. (unstable, used_with_arg, "1.60.0", Some(93798)), + /// Allows use of attributes in `where` clauses. + (unstable, where_clause_attrs, "1.87.0", Some(115590)), /// Allows use of x86 `AMX` target-feature attributes and intrinsics (unstable, x86_amx_intrinsics, "1.81.0", Some(126622)), /// Allows use of the `xop` target-feature (unstable, xop_target_feature, "1.81.0", Some(127208)), /// Allows `do yeet` expressions (unstable, yeet_expr, "1.62.0", Some(96373)), + (unstable, yield_expr, "1.87.0", Some(43122)), // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! @@ -683,11 +698,13 @@ impl Features { ) -> Result<(), Box> { #[derive(serde::Serialize)] struct LibFeature { + timestamp: u128, symbol: String, } #[derive(serde::Serialize)] struct LangFeature { + timestamp: u128, symbol: String, since: Option, } @@ -701,10 +718,20 @@ impl Features { let metrics_file = std::fs::File::create(metrics_path)?; let metrics_file = std::io::BufWriter::new(metrics_file); + let now = || { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time should always be greater than the unix epoch") + .as_nanos() + }; + let lib_features = self .enabled_lib_features .iter() - .map(|EnabledLibFeature { gate_name, .. }| LibFeature { symbol: gate_name.to_string() }) + .map(|EnabledLibFeature { gate_name, .. }| LibFeature { + symbol: gate_name.to_string(), + timestamp: now(), + }) .collect(); let lang_features = self @@ -713,6 +740,7 @@ impl Features { .map(|EnabledLangFeature { gate_name, stable_since, .. }| LangFeature { symbol: gate_name.to_string(), since: stable_since.map(|since| since.to_string()), + timestamp: now(), }) .collect(); diff --git a/compiler/rustc_fluent_macro/src/fluent.rs b/compiler/rustc_fluent_macro/src/fluent.rs index d4604c27e6da3..d58c70674f6aa 100644 --- a/compiler/rustc_fluent_macro/src/fluent.rs +++ b/compiler/rustc_fluent_macro/src/fluent.rs @@ -25,7 +25,7 @@ fn invocation_relative_path_to_absolute(span: Span, path: &str) -> PathBuf { path.to_path_buf() } else { // `/a/b/c/foo/bar.rs` contains the current macro invocation - let mut source_file_path = span.source_file().path(); + let mut source_file_path = span.local_file().unwrap(); // `/a/b/c/foo/` source_file_path.pop(); // `/a/b/c/foo/../locales/en-US/example.ftl` @@ -78,8 +78,8 @@ fn failed(crate_name: &Ident) -> proc_macro::TokenStream { /// See [rustc_fluent_macro::fluent_messages]. pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let crate_name = std::env::var("CARGO_PKG_NAME") - // If `CARGO_PKG_NAME` is missing, then we're probably running in a test, so use + let crate_name = std::env::var("CARGO_CRATE_NAME") + // If `CARGO_CRATE_NAME` is missing, then we're probably running in a test, so use // `no_crate`. .unwrap_or_else(|_| "no_crate".to_string()) .replace("rustc_", ""); diff --git a/compiler/rustc_fluent_macro/src/lib.rs b/compiler/rustc_fluent_macro/src/lib.rs index 3ad51fa1e64d0..c6e0484b92106 100644 --- a/compiler/rustc_fluent_macro/src/lib.rs +++ b/compiler/rustc_fluent_macro/src/lib.rs @@ -7,7 +7,6 @@ #![feature(proc_macro_span)] #![feature(rustdoc_internals)] #![feature(track_path)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use proc_macro::TokenStream; diff --git a/compiler/rustc_fs_util/Cargo.toml b/compiler/rustc_fs_util/Cargo.toml index baca3bc7d49eb..90a6acade8b03 100644 --- a/compiler/rustc_fs_util/Cargo.toml +++ b/compiler/rustc_fs_util/Cargo.toml @@ -5,4 +5,5 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +tempfile = "3.7.1" # tidy-alphabetical-end diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs index 4e9d21c900df6..7a883a13b72da 100644 --- a/compiler/rustc_fs_util/src/lib.rs +++ b/compiler/rustc_fs_util/src/lib.rs @@ -1,6 +1,8 @@ -use std::ffi::CString; +use std::ffi::{CString, OsStr}; use std::path::{Path, PathBuf, absolute}; -use std::{fs, io}; +use std::{env, fs, io}; + +use tempfile::TempDir; // Unfortunately, on windows, it looks like msvcrt.dll is silently translating // verbatim paths under the hood to non-verbatim paths! This manifests itself as @@ -55,25 +57,31 @@ pub enum LinkOrCopy { Copy, } -/// Copies `p` into `q`, preferring to use hard-linking if possible. If -/// `q` already exists, it is removed first. +/// Copies `p` into `q`, preferring to use hard-linking if possible. /// The result indicates which of the two operations has been performed. pub fn link_or_copy, Q: AsRef>(p: P, q: Q) -> io::Result { + // Creating a hard-link will fail if the destination path already exists. We could defensively + // call remove_file in this function, but that pessimizes callers who can avoid such calls. + // Incremental compilation calls this function a lot, and is able to avoid calls that + // would fail the first hard_link attempt. + let p = p.as_ref(); let q = q.as_ref(); - match fs::remove_file(q) { - Ok(()) => (), - Err(err) if err.kind() == io::ErrorKind::NotFound => (), - Err(err) => return Err(err), - } - match fs::hard_link(p, q) { - Ok(()) => Ok(LinkOrCopy::Link), - Err(_) => match fs::copy(p, q) { - Ok(_) => Ok(LinkOrCopy::Copy), - Err(e) => Err(e), - }, + let err = match fs::hard_link(p, q) { + Ok(()) => return Ok(LinkOrCopy::Link), + Err(err) => err, + }; + + if err.kind() == io::ErrorKind::AlreadyExists { + fs::remove_file(q)?; + if fs::hard_link(p, q).is_ok() { + return Ok(LinkOrCopy::Link); + } } + + // Hard linking failed, fall back to copying. + fs::copy(p, q).map(|_| LinkOrCopy::Copy) } #[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))] @@ -96,3 +104,43 @@ pub fn path_to_c_string(p: &Path) -> CString { pub fn try_canonicalize>(path: P) -> io::Result { fs::canonicalize(&path).or_else(|_| absolute(&path)) } + +pub struct TempDirBuilder<'a, 'b> { + builder: tempfile::Builder<'a, 'b>, +} + +impl<'a, 'b> TempDirBuilder<'a, 'b> { + pub fn new() -> Self { + Self { builder: tempfile::Builder::new() } + } + + pub fn prefix + ?Sized>(&mut self, prefix: &'a S) -> &mut Self { + self.builder.prefix(prefix); + self + } + + pub fn suffix + ?Sized>(&mut self, suffix: &'b S) -> &mut Self { + self.builder.suffix(suffix); + self + } + + pub fn tempdir_in>(&self, dir: P) -> io::Result { + let dir = dir.as_ref(); + // On Windows in CI, we had been getting fairly frequent "Access is denied" + // errors when creating temporary directories. + // So this implements a simple retry with backoff loop. + #[cfg(windows)] + for wait in 1..11 { + match self.builder.tempdir_in(dir) { + Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {} + t => return t, + } + std::thread::sleep(std::time::Duration::from_millis(1 << wait)); + } + self.builder.tempdir_in(dir) + } + + pub fn tempdir(&self) -> io::Result { + self.tempdir_in(env::temp_dir()) + } +} diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index b5774f64b66b2..c8f8fd5be0237 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -277,7 +277,6 @@ )] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use std::borrow::Cow; diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 7e3a8561da08b..507c94aca8b9a 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -253,7 +253,9 @@ impl DefKind { } } - pub fn def_path_data(self, name: Symbol) -> DefPathData { + // Some `DefKind`s require a name, some don't. Panics if one is needed but + // not provided. (`AssocTy` is an exception, see below.) + pub fn def_path_data(self, name: Option) -> DefPathData { match self { DefKind::Mod | DefKind::Struct @@ -264,21 +266,22 @@ impl DefKind { | DefKind::TyAlias | DefKind::ForeignTy | DefKind::TraitAlias - | DefKind::AssocTy | DefKind::TyParam - | DefKind::ExternCrate => DefPathData::TypeNs(name), - // It's not exactly an anon const, but wrt DefPathData, there - // is no difference. - DefKind::Static { nested: true, .. } => DefPathData::AnonConst, + | DefKind::ExternCrate => DefPathData::TypeNs(name.unwrap()), + + // An associated type name will be missing for an RPITIT (DefPathData::AnonAssocTy), + // but those provide their own DefPathData. + DefKind::AssocTy => DefPathData::TypeNs(name.unwrap()), + DefKind::Fn | DefKind::Const | DefKind::ConstParam | DefKind::Static { .. } | DefKind::AssocFn | DefKind::AssocConst - | DefKind::Field => DefPathData::ValueNs(name), - DefKind::Macro(..) => DefPathData::MacroNs(name), - DefKind::LifetimeParam => DefPathData::LifetimeNs(name), + | DefKind::Field => DefPathData::ValueNs(name.unwrap()), + DefKind::Macro(..) => DefPathData::MacroNs(name.unwrap()), + DefKind::LifetimeParam => DefPathData::LifetimeNs(name.unwrap()), DefKind::Ctor(..) => DefPathData::Ctor, DefKind::Use => DefPathData::Use, DefKind::ForeignMod => DefPathData::ForeignMod, @@ -288,7 +291,7 @@ impl DefKind { DefKind::GlobalAsm => DefPathData::GlobalAsm, DefKind::Impl { .. } => DefPathData::Impl, DefKind::Closure => DefPathData::Closure, - DefKind::SyntheticCoroutineBody => DefPathData::Closure, + DefKind::SyntheticCoroutineBody => DefPathData::SyntheticCoroutineBody, } } @@ -429,7 +432,7 @@ pub enum Res { /// mention any generic parameters to allow the following with `min_const_generics`: /// ``` /// # struct Foo; - /// impl Foo { fn test() -> [u8; std::mem::size_of::()] { todo!() } } + /// impl Foo { fn test() -> [u8; size_of::()] { todo!() } } /// /// struct Bar([u8; baz::()]); /// const fn baz() -> usize { 10 } @@ -439,7 +442,7 @@ pub enum Res { /// compat lint: /// ``` /// fn foo() { - /// let _bar = [1_u8; std::mem::size_of::<*mut T>()]; + /// let _bar = [1_u8; size_of::<*mut T>()]; /// } /// ``` // FIXME(generic_const_exprs): Remove this bodge once that feature is stable. diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index c4c309e77e194..f93b9e5af5345 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -47,12 +47,9 @@ impl DefPathTable { debug_assert_eq!(self.stable_crate_id, def_path_hash.stable_crate_id()); let local_hash = def_path_hash.local_hash(); - let index = { - let index = DefIndex::from(self.index_to_key.len()); - debug!("DefPathTable::insert() - {:?} <-> {:?}", key, index); - self.index_to_key.push(key); - index - }; + let index = self.index_to_key.push(key); + debug!("DefPathTable::insert() - {key:?} <-> {index:?}"); + self.def_path_hashes.push(local_hash); debug_assert!(self.def_path_hashes.len() == self.index_to_key.len()); @@ -71,7 +68,7 @@ impl DefPathTable { // // See the documentation for DefPathHash for more information. panic!( - "found DefPathHash collision between {def_path1:?} and {def_path2:?}. \ + "found DefPathHash collision between {def_path1:#?} and {def_path2:#?}. \ Compilation cannot continue." ); } @@ -100,13 +97,31 @@ impl DefPathTable { } } +#[derive(Debug)] +pub struct DisambiguatorState { + next: UnordMap<(LocalDefId, DefPathData), u32>, +} + +impl DisambiguatorState { + pub fn new() -> Self { + Self { next: Default::default() } + } + + /// Creates a `DisambiguatorState` where the next allocated `(LocalDefId, DefPathData)` pair + /// will have `index` as the disambiguator. + pub fn with(def_id: LocalDefId, data: DefPathData, index: u32) -> Self { + let mut this = Self::new(); + this.next.insert((def_id, data), index); + this + } +} + /// The definition table containing node definitions. /// It holds the `DefPathTable` for `LocalDefId`s/`DefPath`s. /// It also stores mappings to convert `LocalDefId`s to/from `HirId`s. #[derive(Debug)] pub struct Definitions { table: DefPathTable, - next_disambiguator: UnordMap<(LocalDefId, DefPathData), u32>, } /// A unique identifier that we can use to lookup a definition @@ -130,7 +145,7 @@ impl DefKey { let DisambiguatedDefPathData { ref data, disambiguator } = self.disambiguated_data; std::mem::discriminant(data).hash(&mut hasher); - if let Some(name) = data.get_opt_name() { + if let Some(name) = data.hashed_symbol() { // Get a stable hash by considering the symbol chars rather than // the symbol index. name.as_str().hash(&mut hasher); @@ -176,7 +191,11 @@ impl DisambiguatedDefPathData { } } DefPathDataName::Anon { namespace } => { - write!(writer, "{{{}#{}}}", namespace, self.disambiguator) + if let DefPathData::AnonAssocTy(method) = self.data { + write!(writer, "{}::{{{}#{}}}", method, namespace, self.disambiguator) + } else { + write!(writer, "{{{}#{}}}", namespace, self.disambiguator) + } } } } @@ -290,6 +309,15 @@ pub enum DefPathData { /// An existential `impl Trait` type node. /// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name. OpaqueTy, + /// Used for remapped captured lifetimes in an existential `impl Trait` type node. + OpaqueLifetime(Symbol), + /// An anonymous associated type from an RPITIT. The symbol refers to the name of the method + /// that defined the type. + AnonAssocTy(Symbol), + /// A synthetic body for a coroutine's by-move body. + SyntheticCoroutineBody, + /// Additional static data referred to by a static. + NestedStatic, } impl Definitions { @@ -341,11 +369,20 @@ impl Definitions { let root = LocalDefId { local_def_index: table.allocate(key, def_path_hash) }; assert_eq!(root.local_def_index, CRATE_DEF_INDEX); - Definitions { table, next_disambiguator: Default::default() } + Definitions { table } } - /// Adds a definition with a parent definition. - pub fn create_def(&mut self, parent: LocalDefId, data: DefPathData) -> LocalDefId { + /// Creates a definition with a parent definition. + /// If there are multiple definitions with the same DefPathData and the same parent, use + /// `disambiguator` to differentiate them. Distinct `DisambiguatorState` instances are not + /// guaranteed to generate unique disambiguators and should instead ensure that the `parent` + /// and `data` pair is distinct from other instances. + pub fn create_def( + &mut self, + parent: LocalDefId, + data: DefPathData, + disambiguator: &mut DisambiguatorState, + ) -> LocalDefId { // We can't use `Debug` implementation for `LocalDefId` here, since it tries to acquire a // reference to `Definitions` and we're already holding a mutable reference. debug!( @@ -353,12 +390,12 @@ impl Definitions { self.def_path(parent).to_string_no_crate_verbose(), ); - // The root node must be created with `create_root_def()`. + // The root node must be created in `new()`. assert!(data != DefPathData::CrateRoot); // Find the next free disambiguator for this key. let disambiguator = { - let next_disamb = self.next_disambiguator.entry((parent, data)).or_insert(0); + let next_disamb = disambiguator.next.entry((parent, data)).or_insert(0); let disambiguator = *next_disamb; *next_disamb = next_disamb.checked_add(1).expect("disambiguator overflow"); disambiguator @@ -410,23 +447,49 @@ impl DefPathData { pub fn get_opt_name(&self) -> Option { use self::DefPathData::*; match *self { - TypeNs(name) if name == kw::Empty => None, - TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name), + TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) + | OpaqueLifetime(name) => Some(name), + + Impl + | ForeignMod + | CrateRoot + | Use + | GlobalAsm + | Closure + | Ctor + | AnonConst + | OpaqueTy + | AnonAssocTy(..) + | SyntheticCoroutineBody + | NestedStatic => None, + } + } - Impl | ForeignMod | CrateRoot | Use | GlobalAsm | Closure | Ctor | AnonConst - | OpaqueTy => None, + fn hashed_symbol(&self) -> Option { + use self::DefPathData::*; + match *self { + TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) | AnonAssocTy(name) + | OpaqueLifetime(name) => Some(name), + + Impl + | ForeignMod + | CrateRoot + | Use + | GlobalAsm + | Closure + | Ctor + | AnonConst + | OpaqueTy + | SyntheticCoroutineBody + | NestedStatic => None, } } pub fn name(&self) -> DefPathDataName { use self::DefPathData::*; match *self { - TypeNs(name) if name == kw::Empty => { - DefPathDataName::Anon { namespace: sym::synthetic } - } - TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => { - DefPathDataName::Named(name) - } + TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) + | OpaqueLifetime(name) => DefPathDataName::Named(name), // Note that this does not show up in user print-outs. CrateRoot => DefPathDataName::Anon { namespace: kw::Crate }, Impl => DefPathDataName::Anon { namespace: kw::Impl }, @@ -437,6 +500,9 @@ impl DefPathData { Ctor => DefPathDataName::Anon { namespace: sym::constructor }, AnonConst => DefPathDataName::Anon { namespace: sym::constant }, OpaqueTy => DefPathDataName::Anon { namespace: sym::opaque }, + AnonAssocTy(..) => DefPathDataName::Anon { namespace: sym::anon_assoc }, + SyntheticCoroutineBody => DefPathDataName::Anon { namespace: sym::synthetic }, + NestedStatic => DefPathDataName::Anon { namespace: sym::nested }, } } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 399f1f4b23730..fa1d1ec0a8601 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -10,9 +10,9 @@ use rustc_ast::{ LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, }; pub use rustc_ast::{ - AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, - ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, MetaItemInner, MetaItemLit, Movability, - Mutability, UnOp, + AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, + BoundConstness, BoundPolarity, ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, + MetaItemInner, MetaItemLit, Movability, Mutability, UnOp, }; use rustc_attr_data_structures::AttributeKind; use rustc_data_structures::fingerprint::Fingerprint; @@ -35,20 +35,137 @@ use crate::def_id::{DefId, LocalDefIdMap}; pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId}; use crate::intravisit::{FnKind, VisitorExt}; +#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] +pub enum AngleBrackets { + /// E.g. `Path`. + Missing, + /// E.g. `Path<>`. + Empty, + /// E.g. `Path`. + Full, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] +pub enum LifetimeSource { + /// E.g. `&Type`, `&'_ Type`, `&'a Type`, `&mut Type`, `&'_ mut Type`, `&'a mut Type` + Reference, + + /// E.g. `ContainsLifetime`, `ContainsLifetime<>`, `ContainsLifetime<'_>`, + /// `ContainsLifetime<'a>` + Path { angle_brackets: AngleBrackets }, + + /// E.g. `impl Trait + '_`, `impl Trait + 'a` + OutlivesBound, + + /// E.g. `impl Trait + use<'_>`, `impl Trait + use<'a>` + PreciseCapturing, + + /// Other usages which have not yet been categorized. Feel free to + /// add new sources that you find useful. + /// + /// Some non-exhaustive examples: + /// - `where T: 'a` + /// - `fn(_: dyn Trait + 'a)` + Other, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] +pub enum LifetimeSyntax { + /// E.g. `&Type`, `ContainsLifetime` + Hidden, + + /// E.g. `&'_ Type`, `ContainsLifetime<'_>`, `impl Trait + '_`, `impl Trait + use<'_>` + Anonymous, + + /// E.g. `&'a Type`, `ContainsLifetime<'a>`, `impl Trait + 'a`, `impl Trait + use<'a>` + Named, +} + +impl From for LifetimeSyntax { + fn from(ident: Ident) -> Self { + let name = ident.name; + + if name == kw::Empty { + unreachable!("A lifetime name should never be empty"); + } else if name == kw::UnderscoreLifetime { + LifetimeSyntax::Anonymous + } else { + debug_assert!(name.as_str().starts_with('\'')); + LifetimeSyntax::Named + } + } +} + +/// A lifetime. The valid field combinations are non-obvious and not all +/// combinations are possible. The following example shows some of +/// them. See also the comments on `LifetimeKind` and `LifetimeSource`. +/// +/// ``` +/// #[repr(C)] +/// struct S<'a>(&'a u32); // res=Param, name='a, source=Reference, syntax=Named +/// unsafe extern "C" { +/// fn f1(s: S); // res=Param, name='_, source=Path, syntax=Hidden +/// fn f2(s: S<'_>); // res=Param, name='_, source=Path, syntax=Anonymous +/// fn f3<'a>(s: S<'a>); // res=Param, name='a, source=Path, syntax=Named +/// } +/// +/// struct St<'a> { x: &'a u32 } // res=Param, name='a, source=Reference, syntax=Named +/// fn f() { +/// _ = St { x: &0 }; // res=Infer, name='_, source=Path, syntax=Hidden +/// _ = St::<'_> { x: &0 }; // res=Infer, name='_, source=Path, syntax=Anonymous +/// } +/// +/// struct Name<'a>(&'a str); // res=Param, name='a, source=Reference, syntax=Named +/// const A: Name = Name("a"); // res=Static, name='_, source=Path, syntax=Hidden +/// const B: &str = ""; // res=Static, name='_, source=Reference, syntax=Hidden +/// static C: &'_ str = ""; // res=Static, name='_, source=Reference, syntax=Anonymous +/// static D: &'static str = ""; // res=Static, name='static, source=Reference, syntax=Named +/// +/// trait Tr {} +/// fn tr(_: Box) {} // res=ImplicitObjectLifetimeDefault, name='_, source=Other, syntax=Hidden +/// +/// fn capture_outlives<'a>() -> +/// impl FnOnce() + 'a // res=Param, ident='a, source=OutlivesBound, syntax=Named +/// { +/// || {} +/// } +/// +/// fn capture_precise<'a>() -> +/// impl FnOnce() + use<'a> // res=Param, ident='a, source=PreciseCapturing, syntax=Named +/// { +/// || {} +/// } +/// +/// // (commented out because these cases trigger errors) +/// // struct S1<'a>(&'a str); // res=Param, name='a, source=Reference, syntax=Named +/// // struct S2(S1); // res=Error, name='_, source=Path, syntax=Hidden +/// // struct S3(S1<'_>); // res=Error, name='_, source=Path, syntax=Anonymous +/// // struct S4(S1<'a>); // res=Error, name='a, source=Path, syntax=Named +/// ``` +/// +/// Some combinations that cannot occur are `LifetimeSyntax::Hidden` with +/// `LifetimeSource::OutlivesBound` or `LifetimeSource::PreciseCapturing` +/// — there's no way to "elide" these lifetimes. #[derive(Debug, Copy, Clone, HashStable_Generic)] pub struct Lifetime { #[stable_hasher(ignore)] pub hir_id: HirId, - /// Either "`'a`", referring to a named lifetime definition, - /// `'_` referring to an anonymous lifetime (either explicitly `'_` or `&type`), - /// or "``" (i.e., `kw::Empty`) when appearing in path. - /// - /// See `Lifetime::suggestion_position` for practical use. + /// Either a named lifetime definition (e.g. `'a`, `'static`) or an + /// anonymous lifetime (`'_`, either explicitly written, or inserted for + /// things like `&type`). pub ident: Ident, /// Semantics of this lifetime. - pub res: LifetimeName, + pub kind: LifetimeKind, + + /// The context in which the lifetime occurred. See `Lifetime::suggestion` + /// for example use. + pub source: LifetimeSource, + + /// The syntax that the user used to declare this lifetime. See + /// `Lifetime::suggestion` for example use. + pub syntax: LifetimeSyntax, } #[derive(Debug, Copy, Clone, HashStable_Generic)] @@ -90,7 +207,7 @@ impl ParamName { } #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)] -pub enum LifetimeName { +pub enum LifetimeKind { /// User-given names or fresh (synthetic) names. Param(LocalDefId), @@ -111,83 +228,109 @@ pub enum LifetimeName { /// that was already reported. Error, - /// User wrote an anonymous lifetime, either `'_` or nothing. - /// The semantics of this lifetime should be inferred by typechecking code. + /// User wrote an anonymous lifetime, either `'_` or nothing (which gets + /// converted to `'_`). The semantics of this lifetime should be inferred + /// by typechecking code. Infer, - /// User wrote `'static`. + /// User wrote `'static` or nothing (which gets converted to `'_`). Static, } -impl LifetimeName { +impl LifetimeKind { fn is_elided(&self) -> bool { match self { - LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Infer => true, + LifetimeKind::ImplicitObjectLifetimeDefault | LifetimeKind::Infer => true, // It might seem surprising that `Fresh` counts as not *elided* // -- but this is because, as far as the code in the compiler is // concerned -- `Fresh` variants act equivalently to "some fresh name". // They correspond to early-bound regions on an impl, in other words. - LifetimeName::Error | LifetimeName::Param(..) | LifetimeName::Static => false, + LifetimeKind::Error | LifetimeKind::Param(..) | LifetimeKind::Static => false, } } } impl fmt::Display for Lifetime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.ident.name != kw::Empty { self.ident.name.fmt(f) } else { "'_".fmt(f) } + self.ident.name.fmt(f) } } -pub enum LifetimeSuggestionPosition { - /// The user wrote `'a` or `'_`. - Normal, - /// The user wrote `&type` or `&mut type`. - Ampersand, - /// The user wrote `Path` and omitted the `<'_>`. - ElidedPath, - /// The user wrote `Path`, and omitted the `'_,`. - ElidedPathArgument, - /// The user wrote `dyn Trait` and omitted the `+ '_`. - ObjectDefault, -} - impl Lifetime { + pub fn new( + hir_id: HirId, + ident: Ident, + kind: LifetimeKind, + source: LifetimeSource, + syntax: LifetimeSyntax, + ) -> Lifetime { + let lifetime = Lifetime { hir_id, ident, kind, source, syntax }; + + // Sanity check: elided lifetimes form a strict subset of anonymous lifetimes. + #[cfg(debug_assertions)] + match (lifetime.is_elided(), lifetime.is_anonymous()) { + (false, false) => {} // e.g. `'a` + (false, true) => {} // e.g. explicit `'_` + (true, true) => {} // e.g. `&x` + (true, false) => panic!("bad Lifetime"), + } + + lifetime + } + pub fn is_elided(&self) -> bool { - self.res.is_elided() + self.kind.is_elided() } pub fn is_anonymous(&self) -> bool { - self.ident.name == kw::Empty || self.ident.name == kw::UnderscoreLifetime + self.ident.name == kw::UnderscoreLifetime } - pub fn suggestion_position(&self) -> (LifetimeSuggestionPosition, Span) { - if self.ident.name == kw::Empty { - if self.ident.span.is_empty() { - (LifetimeSuggestionPosition::ElidedPathArgument, self.ident.span) - } else { - (LifetimeSuggestionPosition::ElidedPath, self.ident.span.shrink_to_hi()) - } - } else if self.res == LifetimeName::ImplicitObjectLifetimeDefault { - (LifetimeSuggestionPosition::ObjectDefault, self.ident.span) - } else if self.ident.span.is_empty() { - (LifetimeSuggestionPosition::Ampersand, self.ident.span) - } else { - (LifetimeSuggestionPosition::Normal, self.ident.span) - } + pub fn is_syntactically_hidden(&self) -> bool { + matches!(self.syntax, LifetimeSyntax::Hidden) + } + + pub fn is_syntactically_anonymous(&self) -> bool { + matches!(self.syntax, LifetimeSyntax::Anonymous) + } + + pub fn is_static(&self) -> bool { + self.kind == LifetimeKind::Static } pub fn suggestion(&self, new_lifetime: &str) -> (Span, String) { + use LifetimeSource::*; + use LifetimeSyntax::*; + debug_assert!(new_lifetime.starts_with('\'')); - let (pos, span) = self.suggestion_position(); - let code = match pos { - LifetimeSuggestionPosition::Normal => format!("{new_lifetime}"), - LifetimeSuggestionPosition::Ampersand => format!("{new_lifetime} "), - LifetimeSuggestionPosition::ElidedPath => format!("<{new_lifetime}>"), - LifetimeSuggestionPosition::ElidedPathArgument => format!("{new_lifetime}, "), - LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lifetime}"), - }; - (span, code) + + match (self.syntax, self.source) { + // The user wrote `'a` or `'_`. + (Named | Anonymous, _) => (self.ident.span, format!("{new_lifetime}")), + + // The user wrote `Path`, and omitted the `'_,`. + (Hidden, Path { angle_brackets: AngleBrackets::Full }) => { + (self.ident.span, format!("{new_lifetime}, ")) + } + + // The user wrote `Path<>`, and omitted the `'_`.. + (Hidden, Path { angle_brackets: AngleBrackets::Empty }) => { + (self.ident.span, format!("{new_lifetime}")) + } + + // The user wrote `Path` and omitted the `<'_>`. + (Hidden, Path { angle_brackets: AngleBrackets::Missing }) => { + (self.ident.span.shrink_to_hi(), format!("<{new_lifetime}>")) + } + + // The user wrote `&type` or `&mut type`. + (Hidden, Reference) => (self.ident.span, format!("{new_lifetime} ")), + + (Hidden, source) => { + unreachable!("can't suggest for a hidden lifetime of {source:?}") + } + } } } @@ -243,7 +386,7 @@ impl<'hir> PathSegment<'hir> { } pub fn invalid() -> Self { - Self::new(Ident::empty(), HirId::INVALID, Res::Err) + Self::new(Ident::dummy(), HirId::INVALID, Res::Err) } pub fn args(&self) -> &GenericArgs<'hir> { @@ -975,7 +1118,7 @@ pub struct WhereRegionPredicate<'hir> { impl<'hir> WhereRegionPredicate<'hir> { /// Returns `true` if `param_def_id` matches the `lifetime` of this predicate. fn is_param_bound(&self, param_def_id: LocalDefId) -> bool { - self.lifetime.res == LifetimeName::Param(param_def_id) + self.lifetime.kind == LifetimeKind::Param(param_def_id) } } @@ -1198,7 +1341,7 @@ impl AttributeExt for Attribute { Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => { Some((*comment, *kind)) } - Attribute::Unparsed(_) if self.name_or_empty() == sym::doc => { + Attribute::Unparsed(_) if self.has_name(sym::doc) => { self.value_str().map(|s| (s, CommentKind::Line)) } _ => None, @@ -1223,8 +1366,8 @@ impl Attribute { } #[inline] - pub fn name_or_empty(&self) -> Symbol { - AttributeExt::name_or_empty(self) + pub fn name(&self) -> Option { + AttributeExt::name(self) } #[inline] @@ -1262,6 +1405,11 @@ impl Attribute { AttributeExt::has_name(self, name) } + #[inline] + pub fn has_any_name(&self, names: &[Symbol]) -> bool { + AttributeExt::has_any_name(self, names) + } + #[inline] pub fn span(&self) -> Span { AttributeExt::span(self) @@ -1307,13 +1455,18 @@ impl Attribute { #[derive(Debug)] pub struct AttributeMap<'tcx> { pub map: SortedMap, + /// Preprocessed `#[define_opaque]` attribute. + pub define_opaque: Option<&'tcx [(Span, LocalDefId)]>, // Only present when the crate hash is needed. pub opt_hash: Option, } impl<'tcx> AttributeMap<'tcx> { - pub const EMPTY: &'static AttributeMap<'static> = - &AttributeMap { map: SortedMap::new(), opt_hash: Some(Fingerprint::ZERO) }; + pub const EMPTY: &'static AttributeMap<'static> = &AttributeMap { + map: SortedMap::new(), + opt_hash: Some(Fingerprint::ZERO), + define_opaque: None, + }; #[inline] pub fn get(&self, id: ItemLocalId) -> &'tcx [Attribute] { @@ -1511,6 +1664,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { + Missing => unreachable!(), Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => true, Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it), Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), @@ -1538,7 +1692,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { - Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => {} + Missing | Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => {} Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it), Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), @@ -1600,7 +1754,7 @@ pub struct PatField<'hir> { pub span: Span, } -#[derive(Copy, Clone, PartialEq, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Debug, HashStable_Generic, Hash, Eq, Encodable, Decodable)] pub enum RangeEnd { Included, Excluded, @@ -1668,7 +1822,10 @@ pub enum PatExprKind<'hir> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum TyPatKind<'hir> { /// A range pattern (e.g., `1..=2` or `1..2`). - Range(Option<&'hir ConstArg<'hir>>, Option<&'hir ConstArg<'hir>>, RangeEnd), + Range(&'hir ConstArg<'hir>, &'hir ConstArg<'hir>), + + /// A list of patterns where only one needs to be satisfied + Or(&'hir [TyPat<'hir>]), /// A placeholder for a pattern that wasn't well formed in some way. Err(ErrorGuaranteed), @@ -1676,6 +1833,9 @@ pub enum TyPatKind<'hir> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum PatKind<'hir> { + /// A missing pattern, e.g. for an anonymous param in a bare fn like `fn f(u32)`. + Missing, + /// Represents a wildcard pattern (i.e., `_`). Wild, @@ -1708,7 +1868,7 @@ pub enum PatKind<'hir> { Never, /// A tuple pattern (e.g., `(a, b)`). - /// If the `..` pattern fragment is present, then `Option` denotes its position. + /// If the `..` pattern fragment is present, then `DotDotPos` denotes its position. /// `0 <= position <= subpats.len()` Tuple(&'hir [Pat<'hir>], DotDotPos), @@ -1773,6 +1933,8 @@ pub enum StmtKind<'hir> { /// Represents a `let` statement (i.e., `let : = ;`). #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct LetStmt<'hir> { + /// Span of `super` in `super let`. + pub super_: Option, pub pat: &'hir Pat<'hir>, /// Type annotation, if any (otherwise the type will be inferred). pub ty: Option<&'hir Ty<'hir>>, @@ -2079,7 +2241,7 @@ pub type Lit = Spanned; /// explicit discriminant values for enum variants. /// /// You can check if this anon const is a default in a const param -/// `const N: usize = { ... }` with `tcx.hir().opt_const_param_default_param_def_id(..)` +/// `const N: usize = { ... }` with `tcx.hir_opt_const_param_default_param_def_id(..)` #[derive(Copy, Clone, Debug, HashStable_Generic)] pub struct AnonConst { #[stable_hasher(ignore)] @@ -2166,6 +2328,7 @@ impl Expr<'_> { | ExprKind::Tup(_) | ExprKind::Type(..) | ExprKind::UnsafeBinderCast(..) + | ExprKind::Use(..) | ExprKind::Err(_) => ExprPrecedence::Unambiguous, ExprKind::DropTemps(expr, ..) => expr.precedence(), @@ -2212,6 +2375,7 @@ impl Expr<'_> { ExprKind::Path(QPath::TypeRelative(..)) | ExprKind::Call(..) | ExprKind::MethodCall(..) + | ExprKind::Use(..) | ExprKind::Struct(..) | ExprKind::Tup(..) | ExprKind::If(..) @@ -2285,7 +2449,9 @@ impl Expr<'_> { pub fn can_have_side_effects(&self) -> bool { match self.peel_drop_temps().kind { - ExprKind::Path(_) | ExprKind::Lit(_) | ExprKind::OffsetOf(..) => false, + ExprKind::Path(_) | ExprKind::Lit(_) | ExprKind::OffsetOf(..) | ExprKind::Use(..) => { + false + } ExprKind::Type(base, _) | ExprKind::Unary(_, base) | ExprKind::Field(base, _) @@ -2547,6 +2713,8 @@ pub enum ExprKind<'hir> { /// /// [`type_dependent_def_id`]: ../../rustc_middle/ty/struct.TypeckResults.html#method.type_dependent_def_id MethodCall(&'hir PathSegment<'hir>, &'hir Expr<'hir>, &'hir [Expr<'hir>], Span), + /// An use expression (e.g., `var.use`). + Use(&'hir Expr<'hir>, Span), /// A tuple (e.g., `(a, b, c, d)`). Tup(&'hir [Expr<'hir>]), /// A binary operation (e.g., `a + b`, `a * b`). @@ -2573,6 +2741,11 @@ pub enum ExprKind<'hir> { /// An `if` block, with an optional else block. /// /// I.e., `if { } else { }`. + /// + /// The "then" expr is always `ExprKind::Block`. If present, the "else" expr is always + /// `ExprKind::Block` (for `else`) or `ExprKind::If` (for `else if`). + /// Note that using an `Expr` instead of a `Block` for the "then" part is intentional, + /// as it simplifies the type coercion machinery. If(&'hir Expr<'hir>, &'hir Expr<'hir>, Option<&'hir Expr<'hir>>), /// A conditionless loop (can be exited with `break`, `continue`, or `return`). /// @@ -2598,7 +2771,7 @@ pub enum ExprKind<'hir> { /// An assignment with an operator. /// /// E.g., `a += 1`. - AssignOp(BinOp, &'hir Expr<'hir>, &'hir Expr<'hir>), + AssignOp(AssignOp, &'hir Expr<'hir>, &'hir Expr<'hir>), /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct or tuple field. Field(&'hir Expr<'hir>, Ident), /// An indexing operation (`foo[2]`). @@ -2938,7 +3111,7 @@ impl<'hir> TraitItem<'hir> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum TraitFn<'hir> { /// No default body in the trait, just a signature. - Required(&'hir [Ident]), + Required(&'hir [Option]), /// Both signature and body are provided in the trait. Provided(BodyId), @@ -3343,7 +3516,9 @@ pub struct BareFnTy<'hir> { pub abi: ExternAbi, pub generic_params: &'hir [GenericParam<'hir>], pub decl: &'hir FnDecl<'hir>, - pub param_names: &'hir [Ident], + // `Option` because bare fn parameter identifiers are optional. We also end up + // with `None` in some error cases, e.g. invalid parameter patterns. + pub param_idents: &'hir [Option], } #[derive(Debug, Clone, Copy, HashStable_Generic)] @@ -3362,13 +3537,16 @@ pub struct OpaqueTy<'hir> { pub span: Span, } -#[derive(Debug, Clone, Copy, HashStable_Generic)] -pub enum PreciseCapturingArg<'hir> { - Lifetime(&'hir Lifetime), +#[derive(Debug, Clone, Copy, HashStable_Generic, Encodable, Decodable)] +pub enum PreciseCapturingArgKind { + Lifetime(T), /// Non-lifetime argument (type or const) - Param(PreciseCapturingNonLifetimeArg), + Param(U), } +pub type PreciseCapturingArg<'hir> = + PreciseCapturingArgKind<&'hir Lifetime, PreciseCapturingNonLifetimeArg>; + impl PreciseCapturingArg<'_> { pub fn hir_id(self) -> HirId { match self { @@ -3741,7 +3919,10 @@ pub enum UseKind { /// One import, e.g., `use foo::bar` or `use foo::bar as baz`. /// Also produced for each element of a list `use`, e.g. /// `use foo::{a, b}` lowers to `use foo::a; use foo::b;`. - Single, + /// + /// The identifier is the name defined by the import. E.g. for `use + /// foo::bar` it is `bar`, for `use foo::bar as baz` it is `baz`. + Single(Ident), /// Glob import, e.g., `use foo::*`. Glob, @@ -3883,8 +4064,6 @@ impl ItemId { /// An item /// -/// The name might be a dummy name in case of anonymous items -/// /// For more details, see the [rust lang reference]. /// Note that the reference does not document nightly-only features. /// There may be also slight differences in the names and representation of AST nodes between @@ -3893,7 +4072,6 @@ impl ItemId { /// [rust lang reference]: https://doc.rust-lang.org/reference/items.html #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Item<'hir> { - pub ident: Ident, pub owner_id: OwnerId, pub kind: ItemKind<'hir>, pub span: Span, @@ -3923,46 +4101,56 @@ impl<'hir> Item<'hir> { } expect_methods_self_kind! { - expect_extern_crate, Option, ItemKind::ExternCrate(s), *s; + expect_extern_crate, (Option, Ident), + ItemKind::ExternCrate(s, ident), (*s, *ident); expect_use, (&'hir UsePath<'hir>, UseKind), ItemKind::Use(p, uk), (p, *uk); - expect_static, (&'hir Ty<'hir>, Mutability, BodyId), - ItemKind::Static(ty, mutbl, body), (ty, *mutbl, *body); + expect_static, (Ident, &'hir Ty<'hir>, Mutability, BodyId), + ItemKind::Static(ident, ty, mutbl, body), (*ident, ty, *mutbl, *body); - expect_const, (&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId), - ItemKind::Const(ty, generics, body), (ty, generics, *body); + expect_const, (Ident, &'hir Ty<'hir>, &'hir Generics<'hir>, BodyId), + ItemKind::Const(ident, ty, generics, body), (*ident, ty, generics, *body); - expect_fn, (&FnSig<'hir>, &'hir Generics<'hir>, BodyId), - ItemKind::Fn { sig, generics, body, .. }, (sig, generics, *body); + expect_fn, (Ident, &FnSig<'hir>, &'hir Generics<'hir>, BodyId), + ItemKind::Fn { ident, sig, generics, body, .. }, (*ident, sig, generics, *body); - expect_macro, (&ast::MacroDef, MacroKind), ItemKind::Macro(def, mk), (def, *mk); + expect_macro, (Ident, &ast::MacroDef, MacroKind), + ItemKind::Macro(ident, def, mk), (*ident, def, *mk); - expect_mod, &'hir Mod<'hir>, ItemKind::Mod(m), m; + expect_mod, (Ident, &'hir Mod<'hir>), ItemKind::Mod(ident, m), (*ident, m); expect_foreign_mod, (ExternAbi, &'hir [ForeignItemRef]), ItemKind::ForeignMod { abi, items }, (*abi, items); expect_global_asm, &'hir InlineAsm<'hir>, ItemKind::GlobalAsm { asm, .. }, asm; - expect_ty_alias, (&'hir Ty<'hir>, &'hir Generics<'hir>), - ItemKind::TyAlias(ty, generics), (ty, generics); + expect_ty_alias, (Ident, &'hir Ty<'hir>, &'hir Generics<'hir>), + ItemKind::TyAlias(ident, ty, generics), (*ident, ty, generics); - expect_enum, (&EnumDef<'hir>, &'hir Generics<'hir>), ItemKind::Enum(def, generics), (def, generics); + expect_enum, (Ident, &EnumDef<'hir>, &'hir Generics<'hir>), + ItemKind::Enum(ident, def, generics), (*ident, def, generics); - expect_struct, (&VariantData<'hir>, &'hir Generics<'hir>), - ItemKind::Struct(data, generics), (data, generics); + expect_struct, (Ident, &VariantData<'hir>, &'hir Generics<'hir>), + ItemKind::Struct(ident, data, generics), (*ident, data, generics); - expect_union, (&VariantData<'hir>, &'hir Generics<'hir>), - ItemKind::Union(data, generics), (data, generics); + expect_union, (Ident, &VariantData<'hir>, &'hir Generics<'hir>), + ItemKind::Union(ident, data, generics), (*ident, data, generics); expect_trait, - (IsAuto, Safety, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemRef]), - ItemKind::Trait(is_auto, safety, generics, bounds, items), - (*is_auto, *safety, generics, bounds, items); - - expect_trait_alias, (&'hir Generics<'hir>, GenericBounds<'hir>), - ItemKind::TraitAlias(generics, bounds), (generics, bounds); + ( + IsAuto, + Safety, + Ident, + &'hir Generics<'hir>, + GenericBounds<'hir>, + &'hir [TraitItemRef] + ), + ItemKind::Trait(is_auto, safety, ident, generics, bounds, items), + (*is_auto, *safety, *ident, generics, bounds, items); + + expect_trait_alias, (Ident, &'hir Generics<'hir>, GenericBounds<'hir>), + ItemKind::TraitAlias(ident, generics, bounds), (*ident, generics, bounds); expect_impl, &'hir Impl<'hir>, ItemKind::Impl(imp), imp; } @@ -4080,7 +4268,7 @@ pub enum ItemKind<'hir> { /// An `extern crate` item, with optional *original* crate name if the crate was renamed. /// /// E.g., `extern crate foo` or `extern crate foo_bar as foo`. - ExternCrate(Option), + ExternCrate(Option, Ident), /// `use foo::bar::*;` or `use foo::bar::baz as quux;` /// @@ -4090,11 +4278,12 @@ pub enum ItemKind<'hir> { Use(&'hir UsePath<'hir>, UseKind), /// A `static` item. - Static(&'hir Ty<'hir>, Mutability, BodyId), + Static(Ident, &'hir Ty<'hir>, Mutability, BodyId), /// A `const` item. - Const(&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId), + Const(Ident, &'hir Ty<'hir>, &'hir Generics<'hir>, BodyId), /// A function declaration. Fn { + ident: Ident, sig: FnSig<'hir>, generics: &'hir Generics<'hir>, body: BodyId, @@ -4104,9 +4293,9 @@ pub enum ItemKind<'hir> { has_body: bool, }, /// A MBE macro definition (`macro_rules!` or `macro`). - Macro(&'hir ast::MacroDef, MacroKind), + Macro(Ident, &'hir ast::MacroDef, MacroKind), /// A module. - Mod(&'hir Mod<'hir>), + Mod(Ident, &'hir Mod<'hir>), /// An external module, e.g. `extern { .. }`. ForeignMod { abi: ExternAbi, items: &'hir [ForeignItemRef] }, /// Module-level inline assembly (from `global_asm!`). @@ -4120,17 +4309,17 @@ pub enum ItemKind<'hir> { fake_body: BodyId, }, /// A type alias, e.g., `type Foo = Bar`. - TyAlias(&'hir Ty<'hir>, &'hir Generics<'hir>), - /// An enum definition, e.g., `enum Foo {C, D}`. - Enum(EnumDef<'hir>, &'hir Generics<'hir>), + TyAlias(Ident, &'hir Ty<'hir>, &'hir Generics<'hir>), + /// An enum definition, e.g., `enum Foo { C, D }`. + Enum(Ident, EnumDef<'hir>, &'hir Generics<'hir>), /// A struct definition, e.g., `struct Foo {x: A}`. - Struct(VariantData<'hir>, &'hir Generics<'hir>), + Struct(Ident, VariantData<'hir>, &'hir Generics<'hir>), /// A union definition, e.g., `union Foo {x: A, y: B}`. - Union(VariantData<'hir>, &'hir Generics<'hir>), + Union(Ident, VariantData<'hir>, &'hir Generics<'hir>), /// A trait definition. - Trait(IsAuto, Safety, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemRef]), + Trait(IsAuto, Safety, Ident, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemRef]), /// A trait alias. - TraitAlias(&'hir Generics<'hir>, GenericBounds<'hir>), + TraitAlias(Ident, &'hir Generics<'hir>, GenericBounds<'hir>), /// An implementation, e.g., `impl Trait for Foo { .. }`. Impl(&'hir Impl<'hir>), @@ -4159,16 +4348,39 @@ pub struct Impl<'hir> { } impl ItemKind<'_> { + pub fn ident(&self) -> Option { + match *self { + ItemKind::ExternCrate(_, ident) + | ItemKind::Use(_, UseKind::Single(ident)) + | ItemKind::Static(ident, ..) + | ItemKind::Const(ident, ..) + | ItemKind::Fn { ident, .. } + | ItemKind::Macro(ident, ..) + | ItemKind::Mod(ident, ..) + | ItemKind::TyAlias(ident, ..) + | ItemKind::Enum(ident, ..) + | ItemKind::Struct(ident, ..) + | ItemKind::Union(ident, ..) + | ItemKind::Trait(_, _, ident, ..) + | ItemKind::TraitAlias(ident, ..) => Some(ident), + + ItemKind::Use(_, UseKind::Glob | UseKind::ListStem) + | ItemKind::ForeignMod { .. } + | ItemKind::GlobalAsm { .. } + | ItemKind::Impl(_) => None, + } + } + pub fn generics(&self) -> Option<&Generics<'_>> { Some(match self { ItemKind::Fn { generics, .. } - | ItemKind::TyAlias(_, generics) - | ItemKind::Const(_, generics, _) - | ItemKind::Enum(_, generics) - | ItemKind::Struct(_, generics) - | ItemKind::Union(_, generics) - | ItemKind::Trait(_, _, generics, _, _) - | ItemKind::TraitAlias(generics, _) + | ItemKind::TyAlias(_, _, generics) + | ItemKind::Const(_, _, generics, _) + | ItemKind::Enum(_, _, generics) + | ItemKind::Struct(_, _, generics) + | ItemKind::Union(_, _, generics) + | ItemKind::Trait(_, _, _, generics, _, _) + | ItemKind::TraitAlias(_, generics, _) | ItemKind::Impl(Impl { generics, .. }) => generics, _ => return None, }) @@ -4287,7 +4499,12 @@ impl ForeignItem<'_> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum ForeignItemKind<'hir> { /// A foreign function. - Fn(FnSig<'hir>, &'hir [Ident], &'hir Generics<'hir>), + /// + /// All argument idents are actually always present (i.e. `Some`), but + /// `&[Option]` is used because of code paths shared with `TraitFn` + /// and `BareFnTy`. The sharing is due to all of these cases not allowing + /// arbitrary patterns for parameters. + Fn(FnSig<'hir>, &'hir [Option], &'hir Generics<'hir>), /// A foreign static item (`static ext: u8`). Static(&'hir Ty<'hir>, Mutability, Safety), /// A foreign type. @@ -4321,16 +4538,6 @@ pub enum OwnerNode<'hir> { } impl<'hir> OwnerNode<'hir> { - pub fn ident(&self) -> Option { - match self { - OwnerNode::Item(Item { ident, .. }) - | OwnerNode::ForeignItem(ForeignItem { ident, .. }) - | OwnerNode::ImplItem(ImplItem { ident, .. }) - | OwnerNode::TraitItem(TraitItem { ident, .. }) => Some(*ident), - OwnerNode::Crate(..) | OwnerNode::Synthetic => None, - } - } - pub fn span(&self) -> Span { match self { OwnerNode::Item(Item { span, .. }) @@ -4370,8 +4577,8 @@ impl<'hir> OwnerNode<'hir> { match self { OwnerNode::Item(Item { kind: - ItemKind::Static(_, _, body) - | ItemKind::Const(_, _, body) + ItemKind::Static(_, _, _, body) + | ItemKind::Const(_, _, _, body) | ItemKind::Fn { body, .. }, .. }) @@ -4514,12 +4721,12 @@ impl<'hir> Node<'hir> { /// ``` pub fn ident(&self) -> Option { match self { + Node::Item(item) => item.kind.ident(), Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) | Node::ForeignItem(ForeignItem { ident, .. }) | Node::Field(FieldDef { ident, .. }) | Node::Variant(Variant { ident, .. }) - | Node::Item(Item { ident, .. }) | Node::PathSegment(PathSegment { ident, .. }) => Some(*ident), Node::Lifetime(lt) => Some(lt.ident), Node::GenericParam(p) => Some(p.name.ident()), @@ -4595,9 +4802,9 @@ impl<'hir> Node<'hir> { pub fn ty(self) -> Option<&'hir Ty<'hir>> { match self { Node::Item(it) => match it.kind { - ItemKind::TyAlias(ty, _) - | ItemKind::Static(ty, _, _) - | ItemKind::Const(ty, _, _) => Some(ty), + ItemKind::TyAlias(_, ty, _) + | ItemKind::Static(_, ty, _, _) + | ItemKind::Const(_, ty, _, _) => Some(ty), ItemKind::Impl(impl_item) => Some(&impl_item.self_ty), _ => None, }, @@ -4617,7 +4824,7 @@ impl<'hir> Node<'hir> { pub fn alias_ty(self) -> Option<&'hir Ty<'hir>> { match self { - Node::Item(Item { kind: ItemKind::TyAlias(ty, ..), .. }) => Some(ty), + Node::Item(Item { kind: ItemKind::TyAlias(_, ty, _), .. }) => Some(ty), _ => None, } } @@ -4628,7 +4835,9 @@ impl<'hir> Node<'hir> { Node::Item(Item { owner_id, kind: - ItemKind::Const(_, _, body) | ItemKind::Static(.., body) | ItemKind::Fn { body, .. }, + ItemKind::Const(_, _, _, body) + | ItemKind::Static(.., body) + | ItemKind::Fn { body, .. }, .. }) | Node::TraitItem(TraitItem { @@ -4689,8 +4898,8 @@ impl<'hir> Node<'hir> { pub fn fn_kind(self) -> Option> { match self { Node::Item(i) => match i.kind { - ItemKind::Fn { sig, generics, .. } => { - Some(FnKind::ItemFn(i.ident, generics, sig.header)) + ItemKind::Fn { ident, sig, generics, .. } => { + Some(FnKind::ItemFn(ident, generics, sig.header)) } _ => None, }, @@ -4763,8 +4972,8 @@ mod size_asserts { static_assert_size!(ImplItem<'_>, 88); static_assert_size!(ImplItemKind<'_>, 40); static_assert_size!(Item<'_>, 88); - static_assert_size!(ItemKind<'_>, 56); - static_assert_size!(LetStmt<'_>, 64); + static_assert_size!(ItemKind<'_>, 64); + static_assert_size!(LetStmt<'_>, 72); static_assert_size!(Param<'_>, 32); static_assert_size!(Pat<'_>, 72); static_assert_size!(Path<'_>, 40); diff --git a/compiler/rustc_hir/src/hir/tests.rs b/compiler/rustc_hir/src/hir/tests.rs index f75b9662132e8..8684adee29c99 100644 --- a/compiler/rustc_hir/src/hir/tests.rs +++ b/compiler/rustc_hir/src/hir/tests.rs @@ -50,19 +50,14 @@ fn trait_object_roundtrips() { } fn trait_object_roundtrips_impl(syntax: TraitObjectSyntax) { - let unambig = TyKind::TraitObject::<'_, ()>( - &[], - TaggedRef::new( - &const { - Lifetime { - hir_id: HirId::INVALID, - ident: Ident::new(sym::name, DUMMY_SP), - res: LifetimeName::Static, - } - }, - syntax, - ), - ); + let lt = Lifetime { + hir_id: HirId::INVALID, + ident: Ident::new(sym::name, DUMMY_SP), + kind: LifetimeKind::Static, + source: LifetimeSource::Other, + syntax: LifetimeSyntax::Hidden, + }; + let unambig = TyKind::TraitObject::<'_, ()>(&[], TaggedRef::new(<, syntax)); let unambig_to_ambig = unsafe { std::mem::transmute::<_, TyKind<'_, AmbigArg>>(unambig) }; match unambig_to_ambig { diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index 3fa06620ea8d1..b48a081d3714d 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -83,6 +83,12 @@ pub struct HirId { pub local_id: ItemLocalId, } +// To ensure correctness of incremental compilation, +// `HirId` must not implement `Ord` or `PartialOrd`. +// See https://github.com/rust-lang/rust/issues/90317. +impl !Ord for HirId {} +impl !PartialOrd for HirId {} + impl Debug for HirId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Example: HirId(DefId(0:1 ~ aa[7697]::{use#0}).10) @@ -116,10 +122,6 @@ impl HirId { pub fn make_owner(owner: LocalDefId) -> Self { Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::ZERO } } - - pub fn index(self) -> (usize, usize) { - (rustc_index::Idx::index(self.owner.def_id), rustc_index::Idx::index(self.local_id)) - } } impl fmt::Display for HirId { @@ -128,18 +130,6 @@ impl fmt::Display for HirId { } } -impl Ord for HirId { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - (self.index()).cmp(&(other.index())) - } -} - -impl PartialOrd for HirId { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - rustc_data_structures::define_stable_id_collections!(HirIdMap, HirIdSet, HirIdMapEntry, HirId); rustc_data_structures::define_id_collections!( ItemLocalMap, diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index e349e23f7dcf5..a60de4b1fc31b 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -6,7 +6,7 @@ //! 1. **Shallow visit**: Get a simple callback for every item (or item-like thing) in the HIR. //! - Example: find all items with a `#[foo]` attribute on them. //! - How: Use the `hir_crate_items` or `hir_module_items` query to traverse over item-like ids -//! (ItemId, TraitItemId, etc.) and use tcx.def_kind and `tcx.hir().item*(id)` to filter and +//! (ItemId, TraitItemId, etc.) and use tcx.def_kind and `tcx.hir_item*(id)` to filter and //! access actual item-like thing, respectively. //! - Pro: Efficient; just walks the lists of item ids and gives users control whether to access //! the hir_owners themselves or not. @@ -316,8 +316,8 @@ pub trait Visitor<'v>: Sized { fn visit_ident(&mut self, ident: Ident) -> Self::Result { walk_ident(self, ident) } - fn visit_mod(&mut self, m: &'v Mod<'v>, _s: Span, n: HirId) -> Self::Result { - walk_mod(self, m, n) + fn visit_mod(&mut self, m: &'v Mod<'v>, _s: Span, _n: HirId) -> Self::Result { + walk_mod(self, m) } fn visit_foreign_item(&mut self, i: &'v ForeignItem<'v>) -> Self::Result { walk_foreign_item(self, i) @@ -363,7 +363,7 @@ pub trait Visitor<'v>: Sized { /// See the doc comments on [`Ty`] for an explanation of what it means for a type to be /// ambiguous. /// - /// The [`Visitor::visit_infer`] method should be overriden in order to handle infer vars. + /// The [`Visitor::visit_infer`] method should be overridden in order to handle infer vars. fn visit_ty(&mut self, t: &'v Ty<'v, AmbigArg>) -> Self::Result { walk_ty(self, t) } @@ -374,7 +374,7 @@ pub trait Visitor<'v>: Sized { /// See the doc comments on [`ConstArg`] for an explanation of what it means for a const to be /// ambiguous. /// - /// The [`Visitor::visit_infer`] method should be overriden in order to handle infer vars. + /// The [`Visitor::visit_infer`] method should be overridden in order to handle infer vars. fn visit_const_arg(&mut self, c: &'v ConstArg<'v, AmbigArg>) -> Self::Result { walk_ambig_const_arg(self, c) } @@ -464,8 +464,8 @@ pub trait Visitor<'v>: Sized { fn visit_field_def(&mut self, s: &'v FieldDef<'v>) -> Self::Result { walk_field_def(self, s) } - fn visit_enum_def(&mut self, enum_definition: &'v EnumDef<'v>, item_id: HirId) -> Self::Result { - walk_enum_def(self, enum_definition, item_id) + fn visit_enum_def(&mut self, enum_definition: &'v EnumDef<'v>) -> Self::Result { + walk_enum_def(self, enum_definition) } fn visit_variant(&mut self, v: &'v Variant<'v>) -> Self::Result { walk_variant(self, v) @@ -532,64 +532,66 @@ pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) -> } pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::Result { - try_visit!(visitor.visit_ident(item.ident)); + try_visit!(visitor.visit_id(item.hir_id())); match item.kind { - ItemKind::ExternCrate(orig_name) => { - try_visit!(visitor.visit_id(item.hir_id())); + ItemKind::ExternCrate(orig_name, ident) => { visit_opt!(visitor, visit_name, orig_name); + try_visit!(visitor.visit_ident(ident)); } - ItemKind::Use(ref path, _) => { + ItemKind::Use(ref path, kind) => { try_visit!(visitor.visit_use(path, item.hir_id())); + match kind { + UseKind::Single(ident) => try_visit!(visitor.visit_ident(ident)), + UseKind::Glob | UseKind::ListStem => {} + } } - ItemKind::Static(ref typ, _, body) => { - try_visit!(visitor.visit_id(item.hir_id())); + ItemKind::Static(ident, ref typ, _, body) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_ty_unambig(typ)); try_visit!(visitor.visit_nested_body(body)); } - ItemKind::Const(ref typ, ref generics, body) => { - try_visit!(visitor.visit_id(item.hir_id())); + ItemKind::Const(ident, ref typ, ref generics, body) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_ty_unambig(typ)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_nested_body(body)); } - ItemKind::Fn { sig, generics, body: body_id, .. } => { - try_visit!(visitor.visit_id(item.hir_id())); + ItemKind::Fn { ident, sig, generics, body: body_id, .. } => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_fn( - FnKind::ItemFn(item.ident, generics, sig.header), + FnKind::ItemFn(ident, generics, sig.header), sig.decl, body_id, item.span, item.owner_id.def_id, )); } - ItemKind::Macro(..) => { - try_visit!(visitor.visit_id(item.hir_id())); + ItemKind::Macro(ident, _def, _kind) => { + try_visit!(visitor.visit_ident(ident)); } - ItemKind::Mod(ref module) => { - // `visit_mod()` takes care of visiting the `Item`'s `HirId`. + ItemKind::Mod(ident, ref module) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_mod(module, item.span, item.hir_id())); } ItemKind::ForeignMod { abi: _, items } => { - try_visit!(visitor.visit_id(item.hir_id())); walk_list!(visitor, visit_foreign_item_ref, items); } ItemKind::GlobalAsm { asm: _, fake_body } => { - try_visit!(visitor.visit_id(item.hir_id())); // Visit the fake body, which contains the asm statement. // Therefore we should not visit the asm statement again // outside of the body, or some visitors won't have their // typeck results set correctly. try_visit!(visitor.visit_nested_body(fake_body)); } - ItemKind::TyAlias(ref ty, ref generics) => { - try_visit!(visitor.visit_id(item.hir_id())); + ItemKind::TyAlias(ident, ref ty, ref generics) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_ty_unambig(ty)); try_visit!(visitor.visit_generics(generics)); } - ItemKind::Enum(ref enum_definition, ref generics) => { + ItemKind::Enum(ident, ref enum_definition, ref generics) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); - // `visit_enum_def()` takes care of visiting the `Item`'s `HirId`. - try_visit!(visitor.visit_enum_def(enum_definition, item.hir_id())); + try_visit!(visitor.visit_enum_def(enum_definition)); } ItemKind::Impl(Impl { constness: _, @@ -602,26 +604,25 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: self_ty, items, }) => { - try_visit!(visitor.visit_id(item.hir_id())); try_visit!(visitor.visit_generics(generics)); visit_opt!(visitor, visit_trait_ref, of_trait); try_visit!(visitor.visit_ty_unambig(self_ty)); walk_list!(visitor, visit_impl_item_ref, *items); } - ItemKind::Struct(ref struct_definition, ref generics) - | ItemKind::Union(ref struct_definition, ref generics) => { + ItemKind::Struct(ident, ref struct_definition, ref generics) + | ItemKind::Union(ident, ref struct_definition, ref generics) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); - try_visit!(visitor.visit_id(item.hir_id())); try_visit!(visitor.visit_variant_data(struct_definition)); } - ItemKind::Trait(.., ref generics, bounds, trait_item_refs) => { - try_visit!(visitor.visit_id(item.hir_id())); + ItemKind::Trait(_is_auto, _safety, ident, ref generics, bounds, trait_item_refs) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_trait_item_ref, trait_item_refs); } - ItemKind::TraitAlias(ref generics, bounds) => { - try_visit!(visitor.visit_id(item.hir_id())); + ItemKind::TraitAlias(ident, ref generics, bounds) => { + try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); walk_list!(visitor, visit_param_bound, bounds); } @@ -638,12 +639,7 @@ pub fn walk_ident<'v, V: Visitor<'v>>(visitor: &mut V, ident: Ident) -> V::Resul visitor.visit_name(ident.name) } -pub fn walk_mod<'v, V: Visitor<'v>>( - visitor: &mut V, - module: &'v Mod<'v>, - mod_hir_id: HirId, -) -> V::Result { - try_visit!(visitor.visit_id(mod_hir_id)); +pub fn walk_mod<'v, V: Visitor<'v>>(visitor: &mut V, module: &'v Mod<'v>) -> V::Result { walk_list!(visitor, visit_nested_item, module.item_ids.iter().copied()); V::Result::output() } @@ -656,10 +652,12 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_ident(foreign_item.ident)); match foreign_item.kind { - ForeignItemKind::Fn(ref sig, param_names, ref generics) => { + ForeignItemKind::Fn(ref sig, param_idents, ref generics) => { try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_fn_decl(sig.decl)); - walk_list!(visitor, visit_ident, param_names.iter().copied()); + for ident in param_idents.iter().copied() { + visit_opt!(visitor, visit_ident, ident); + } } ForeignItemKind::Static(ref typ, _, _) => { try_visit!(visitor.visit_ty_unambig(typ)); @@ -708,10 +706,11 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) -> V::Res pub fn walk_ty_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v TyPat<'v>) -> V::Result { try_visit!(visitor.visit_id(pattern.hir_id)); match pattern.kind { - TyPatKind::Range(lower_bound, upper_bound, _) => { - visit_opt!(visitor, visit_const_arg_unambig, lower_bound); - visit_opt!(visitor, visit_const_arg_unambig, upper_bound); + TyPatKind::Range(lower_bound, upper_bound) => { + try_visit!(visitor.visit_const_arg_unambig(lower_bound)); + try_visit!(visitor.visit_const_arg_unambig(upper_bound)); } + TyPatKind::Or(patterns) => walk_list!(visitor, visit_pattern_type_pattern, patterns), TyPatKind::Err(_) => (), } V::Result::output() @@ -746,7 +745,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: visit_opt!(visitor, visit_pat_expr, lower_bound); visit_opt!(visitor, visit_pat_expr, upper_bound); } - PatKind::Never | PatKind::Wild | PatKind::Err(_) => (), + PatKind::Missing | PatKind::Never | PatKind::Wild | PatKind::Err(_) => (), PatKind::Slice(prepatterns, ref slice_pattern, postpatterns) => { walk_list!(visitor, visit_pat, prepatterns); visit_opt!(visitor, visit_pat, slice_pattern); @@ -821,6 +820,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) try_visit!(visitor.visit_expr(receiver)); walk_list!(visitor, visit_expr, arguments); } + ExprKind::Use(expr, _) => { + try_visit!(visitor.visit_expr(expr)); + } ExprKind::Binary(_, ref left_expression, ref right_expression) => { try_visit!(visitor.visit_expr(left_expression)); try_visit!(visitor.visit_expr(right_expression)); @@ -1145,7 +1147,6 @@ pub fn walk_use<'v, V: Visitor<'v>>( path: &'v UsePath<'v>, hir_id: HirId, ) -> V::Result { - try_visit!(visitor.visit_id(hir_id)); let UsePath { segments, ref res, span } = *path; for &res in res { try_visit!(visitor.visit_path(&Path { segments, res, span }, hir_id)); @@ -1169,9 +1170,11 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_ty_unambig(ty)); visit_opt!(visitor, visit_nested_body, default); } - TraitItemKind::Fn(ref sig, TraitFn::Required(param_names)) => { + TraitItemKind::Fn(ref sig, TraitFn::Required(param_idents)) => { try_visit!(visitor.visit_fn_decl(sig.decl)); - walk_list!(visitor, visit_ident, param_names.iter().copied()); + for ident in param_idents.iter().copied() { + visit_opt!(visitor, visit_ident, ident); + } } TraitItemKind::Fn(ref sig, TraitFn::Provided(body_id)) => { try_visit!(visitor.visit_fn( @@ -1326,9 +1329,7 @@ pub fn walk_field_def<'v, V: Visitor<'v>>( pub fn walk_enum_def<'v, V: Visitor<'v>>( visitor: &mut V, enum_definition: &'v EnumDef<'v>, - item_id: HirId, ) -> V::Result { - try_visit!(visitor.visit_id(item_id)); walk_list!(visitor, visit_variant, enum_definition.variants); V::Result::output() } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index f5626937ec456..21d36ed54cdf4 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -171,6 +171,7 @@ language_item_table! { Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0); Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None; CloneFn, sym::clone_fn, clone_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + UseCloned, sym::use_cloned, use_cloned_trait, Target::Trait, GenericRequirement::None; Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0); DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None; /// The associated item of the `DiscriminantKind` trait. @@ -181,25 +182,15 @@ language_item_table! { DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None; Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0); + UnsafeUnpin, sym::unsafe_unpin, unsafe_unpin_trait, Target::Trait, GenericRequirement::Exact(0); FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0); FnPtrAddr, sym::fn_ptr_addr, fn_ptr_addr, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None; Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None; - - AsyncDrop, sym::async_drop, async_drop_trait, Target::Trait, GenericRequirement::Exact(0); - AsyncDestruct, sym::async_destruct, async_destruct_trait, Target::Trait, GenericRequirement::Exact(0); + AsyncDrop, sym::async_drop, async_drop_trait, Target::Trait, GenericRequirement::None; AsyncDropInPlace, sym::async_drop_in_place, async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); - SurfaceAsyncDropInPlace, sym::surface_async_drop_in_place, surface_async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); - AsyncDropSurfaceDropInPlace, sym::async_drop_surface_drop_in_place, async_drop_surface_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); - AsyncDropSlice, sym::async_drop_slice, async_drop_slice_fn, Target::Fn, GenericRequirement::Exact(1); - AsyncDropChain, sym::async_drop_chain, async_drop_chain_fn, Target::Fn, GenericRequirement::Exact(2); - AsyncDropNoop, sym::async_drop_noop, async_drop_noop_fn, Target::Fn, GenericRequirement::Exact(0); - AsyncDropDeferredDropInPlace, sym::async_drop_deferred_drop_in_place, async_drop_deferred_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); - AsyncDropFuse, sym::async_drop_fuse, async_drop_fuse_fn, Target::Fn, GenericRequirement::Exact(1); - AsyncDropDefer, sym::async_drop_defer, async_drop_defer_fn, Target::Fn, GenericRequirement::Exact(1); - AsyncDropEither, sym::async_drop_either, async_drop_either_fn, Target::Fn, GenericRequirement::Exact(3); CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1); DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1); @@ -234,6 +225,8 @@ language_item_table! { IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1); UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None; + UnsafePinned, sym::unsafe_pinned, unsafe_pinned_type, Target::Struct, GenericRequirement::None; + VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None; Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); @@ -317,11 +310,14 @@ language_item_table! { PanicAsyncGenFnResumedPanic, sym::panic_const_async_gen_fn_resumed_panic, panic_const_async_gen_fn_resumed_panic, Target::Fn, GenericRequirement::None; PanicGenFnNonePanic, sym::panic_const_gen_fn_none_panic, panic_const_gen_fn_none_panic, Target::Fn, GenericRequirement::None; PanicNullPointerDereference, sym::panic_null_pointer_dereference, panic_null_pointer_dereference, Target::Fn, GenericRequirement::None; + PanicCoroutineResumedDrop, sym::panic_const_coroutine_resumed_drop, panic_const_coroutine_resumed_drop, Target::Fn, GenericRequirement::None; + PanicAsyncFnResumedDrop, sym::panic_const_async_fn_resumed_drop, panic_const_async_fn_resumed_drop, Target::Fn, GenericRequirement::None; + PanicAsyncGenFnResumedDrop, sym::panic_const_async_gen_fn_resumed_drop, panic_const_async_gen_fn_resumed_drop, Target::Fn, GenericRequirement::None; + PanicGenFnNoneDrop, sym::panic_const_gen_fn_none_drop, panic_const_gen_fn_none_drop, Target::Fn, GenericRequirement::None; /// libstd panic entry point. Necessary for const eval to be able to catch it BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; // Lang items needed for `format_args!()`. - FormatAlignment, sym::format_alignment, format_alignment, Target::Enum, GenericRequirement::None; FormatArgument, sym::format_argument, format_argument, Target::Struct, GenericRequirement::None; FormatArguments, sym::format_arguments, format_arguments, Target::Struct, GenericRequirement::None; FormatCount, sym::format_count, format_count, Target::Enum, GenericRequirement::None; @@ -330,7 +326,6 @@ language_item_table! { ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); - FallbackSurfaceDrop, sym::fallback_surface_drop, fallback_surface_drop_fn, Target::Fn, GenericRequirement::None; AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; /// For all binary crates without `#![no_main]`, Rust will generate a "main" function. @@ -418,6 +413,9 @@ language_item_table! { Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None; RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None; RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None; + RangeMax, sym::RangeMax, range_max, Target::AssocConst, GenericRequirement::Exact(0); + RangeMin, sym::RangeMin, range_min, Target::AssocConst, GenericRequirement::Exact(0); + RangeSub, sym::RangeSub, range_sub, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::Exact(0); // `new_range` types that are `Copy + IntoIterator` RangeFromCopy, sym::RangeFromCopy, range_from_copy_struct, Target::Struct, GenericRequirement::None; @@ -430,6 +428,14 @@ language_item_table! { // Experimental lang items for implementing contract pre- and post-condition checking. ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None; ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None; + + // Experimental lang items for `MCP: Low level components for async drop`(https://github.com/rust-lang/compiler-team/issues/727) + DefaultTrait4, sym::default_trait4, default_trait4_trait, Target::Trait, GenericRequirement::None; + DefaultTrait3, sym::default_trait3, default_trait3_trait, Target::Trait, GenericRequirement::None; + DefaultTrait2, sym::default_trait2, default_trait2_trait, Target::Trait, GenericRequirement::None; + DefaultTrait1, sym::default_trait1, default_trait1_trait, Target::Trait, GenericRequirement::None; + + ContractCheckEnsures, sym::contract_check_ensures, contract_check_ensures_fn, Target::Fn, GenericRequirement::None; } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 270d4fbec30f8..7a5ff8906896e 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -9,11 +9,11 @@ #![feature(closure_track_caller)] #![feature(debug_closure_helpers)] #![feature(exhaustive_patterns)] -#![feature(let_chains)] +#![feature(negative_impls)] #![feature(never_type)] #![feature(rustc_attrs)] #![feature(variant_count)] -#![warn(unreachable_pub)] +#![recursion_limit = "256"] // tidy-alphabetical-end extern crate self as rustc_hir; diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 2709a826549c5..91ea88cae4776 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -106,7 +106,7 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable for AttributeMap fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { // We ignore the `map` since it refers to information included in `opt_hash` which is // hashed in the collector and used for the crate hash. - let AttributeMap { opt_hash, map: _ } = *self; + let AttributeMap { opt_hash, define_opaque: _, map: _ } = *self; opt_hash.unwrap().hash_stable(hcx, hasher); } } diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index 70e95a84e68cc..601898023fc39 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -56,6 +56,7 @@ pub enum Target { Param, PatField, ExprField, + WherePredicate, } impl Display for Target { @@ -96,7 +97,8 @@ impl Target { | Target::MacroDef | Target::Param | Target::PatField - | Target::ExprField => false, + | Target::ExprField + | Target::WherePredicate => false, } } @@ -217,6 +219,7 @@ impl Target { Target::Param => "function param", Target::PatField => "pattern field", Target::ExprField => "struct field", + Target::WherePredicate => "where predicate", } } } diff --git a/compiler/rustc_hir/src/tests.rs b/compiler/rustc_hir/src/tests.rs index 0837444ffdbe5..18c2bfdac8ce1 100644 --- a/compiler/rustc_hir/src/tests.rs +++ b/compiler/rustc_hir/src/tests.rs @@ -17,7 +17,7 @@ fn def_path_hash_depends_on_crate_id() { // the crate by changing the crate disambiguator (e.g. via bumping the // crate's version number). - create_session_globals_then(Edition::Edition2024, None, || { + create_session_globals_then(Edition::Edition2024, &[], None, || { let id0 = StableCrateId::new(Symbol::intern("foo"), false, vec!["1".to_string()], ""); let id1 = StableCrateId::new(Symbol::intern("foo"), false, vec!["2".to_string()], ""); diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 55a816a855af4..f2b82c679b933 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -13,7 +13,7 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } @@ -26,9 +26,7 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -rustc_type_ir = { path = "../rustc_type_ir" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 3f75cce009225..f7f2b78f05207 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -1,5 +1,5 @@ -hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$qself}` - .label = ambiguous associated {$assoc_kind} `{$assoc_name}` +hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}` + .label = ambiguous associated {$assoc_kind} `{$assoc_ident}` hir_analysis_ambiguous_lifetime_bound = ambiguous lifetime bound, explicit lifetime bound required @@ -12,13 +12,13 @@ hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private .label = private {$kind} .defined_here_label = the {$kind} is defined here -hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$qself}` +hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_ident}` not found for `{$qself}` hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named -> [true] an *[false] a similarly named } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}` -hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found +hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_ident}` not found hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind} hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg = consider fully qualifying{$identically_named -> @@ -244,9 +244,6 @@ hir_analysis_inherent_ty_outside_relevant = cannot define inherent `impl` for a .help = consider moving this inherent impl into the crate defining the type if possible .span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items -hir_analysis_invalid_base_type = `{$ty}` is not a valid base type for range patterns - .note = range patterns only support integers - hir_analysis_invalid_generic_receiver_ty = invalid generic `self` parameter type: `{$receiver_ty}` .note = type of `self` must not be a method generic parameter type @@ -278,13 +275,6 @@ hir_analysis_invalid_union_field = hir_analysis_invalid_union_field_sugg = wrap the field type in `ManuallyDrop<...>` -hir_analysis_invalid_unsafe_field = - field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be unsafe - .note = unsafe fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` - -hir_analysis_invalid_unsafe_field_sugg = - wrap the field type in `ManuallyDrop<...>` - hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl .label = const parameter declared here @@ -460,9 +450,6 @@ hir_analysis_recursive_generic_parameter = {$param_def_kind} `{$param_name}` is hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` -hir_analysis_register_type_unstable = - type `{$ty}` cannot be used with this register class in stable - hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}` hir_analysis_return_type_notation_equality_bound = @@ -496,6 +483,9 @@ hir_analysis_self_in_impl_self = `Self` is not valid in the self type of an impl block .note = replace `Self` with a different type +hir_analysis_self_in_type_alias = `Self` is not allowed in type aliases + .label = `Self` is only available in impls, traits, and concrete type definitions + hir_analysis_self_ty_not_captured = `impl Trait` must mention the `Self` type of the trait in `use<...>` .label = `Self` type parameter is implicitly captured by this `impl Trait` .note = currently, all type parameters are required to be mentioned in the precise captures list @@ -514,12 +504,9 @@ hir_analysis_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by hir_analysis_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait -hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature - .note = this item must mention the opaque type in its signature in order to be able to register hidden types - -hir_analysis_tait_forward_compat2 = item does not constrain `{$opaque_type}`, but has it in its signature - .note = consider moving the opaque type's declaration and defining uses into a separate module - .opaque = this opaque type is in the signature +hir_analysis_tait_forward_compat2 = item does not constrain `{$opaque_type}` + .note = consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` + .opaque = this opaque type is supposed to be constrained hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[target_feature]` @@ -535,7 +522,7 @@ hir_analysis_trait_object_declared_with_no_traits = at least one trait is required for an object type .alias_span = this alias does not contain a trait -hir_analysis_traits_with_defualt_impl = traits with a default impl, like `{$traits}`, cannot be implemented for {$problematic_kind} `{$self_ty}` +hir_analysis_traits_with_default_impl = traits with a default impl, like `{$traits}`, cannot be implemented for {$problematic_kind} `{$self_ty}` .note = a trait object implements `{$traits}` if and only if `{$traits}` is one of the trait object's trait bounds hir_analysis_transparent_enum_variant = transparent enum needs exactly one variant, but has {$number} @@ -620,6 +607,8 @@ hir_analysis_variances_of = {$variances} hir_analysis_where_clause_on_main = `main` function is not allowed to have a `where` clause .label = `main` cannot have a `where` clause +hir_analysis_within_macro = due to this macro variable + hir_analysis_wrong_number_of_generic_arguments_to_intrinsic = intrinsic has wrong number of {$descr} parameters: found {$found}, expected {$expected} .label = expected {$expected} {$descr} {$expected -> diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index b3eade8c8ae47..99e495d926690 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -2,8 +2,8 @@ use rustc_infer::infer::InferCtxt; use rustc_infer::traits::PredicateObligations; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Limit; -use rustc_span::Span; use rustc_span::def_id::{LOCAL_CRATE, LocalDefId}; +use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument}; @@ -259,7 +259,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { } } -pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { +pub fn report_autoderef_recursion_limit_error<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + ty: Ty<'tcx>, +) -> ErrorGuaranteed { // We've reached the recursion limit, error gracefully. let suggested_limit = match tcx.recursion_limit() { Limit(0) => Limit(2), @@ -270,5 +274,5 @@ pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Spa ty, suggested_limit, crate_name: tcx.crate_name(LOCAL_CRATE), - }); + }) } diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs new file mode 100644 index 0000000000000..58c3020f60ede --- /dev/null +++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs @@ -0,0 +1,296 @@ +//! This module contains methods that assist in checking that impls are general +//! enough, i.e. that they always apply to every valid instantaiton of the ADT +//! they're implemented for. +//! +//! This is necessary for `Drop` and negative impls to be well-formed. + +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::codes::*; +use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; +use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; +use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; +use rustc_middle::span_bug; +use rustc_middle::ty::util::CheckRegions; +use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypingMode}; +use rustc_trait_selection::regions::InferCtxtRegionExt; +use rustc_trait_selection::traits::{self, ObligationCtxt}; + +use crate::errors; +use crate::hir::def_id::{DefId, LocalDefId}; + +/// This function confirms that the `Drop` implementation identified by +/// `drop_impl_did` is not any more specialized than the type it is +/// attached to (Issue #8142). +/// +/// This means: +/// +/// 1. The self type must be nominal (this is already checked during +/// coherence), +/// +/// 2. The generic region/type parameters of the impl's self type must +/// all be parameters of the Drop impl itself (i.e., no +/// specialization like `impl Drop for Foo`), and, +/// +/// 3. Any bounds on the generic parameters must be reflected in the +/// struct/enum definition for the nominal type itself (i.e. +/// cannot do `struct S; impl Drop for S { ... }`). +pub(crate) fn check_drop_impl( + tcx: TyCtxt<'_>, + drop_impl_did: LocalDefId, +) -> Result<(), ErrorGuaranteed> { + match tcx.impl_polarity(drop_impl_did) { + ty::ImplPolarity::Positive => {} + ty::ImplPolarity::Negative => { + return Err(tcx.dcx().emit_err(errors::DropImplPolarity::Negative { + span: tcx.def_span(drop_impl_did), + })); + } + ty::ImplPolarity::Reservation => { + return Err(tcx.dcx().emit_err(errors::DropImplPolarity::Reservation { + span: tcx.def_span(drop_impl_did), + })); + } + } + + tcx.ensure_ok().orphan_check_impl(drop_impl_did)?; + + let self_ty = tcx.type_of(drop_impl_did).instantiate_identity(); + + match self_ty.kind() { + ty::Adt(adt_def, adt_to_impl_args) => { + ensure_impl_params_and_item_params_correspond( + tcx, + drop_impl_did, + adt_def.did(), + adt_to_impl_args, + )?; + + ensure_impl_predicates_are_implied_by_item_defn( + tcx, + drop_impl_did, + adt_def.did(), + adt_to_impl_args, + ) + } + _ => { + span_bug!(tcx.def_span(drop_impl_did), "incoherent impl of Drop"); + } + } +} + +pub(crate) fn check_negative_auto_trait_impl<'tcx>( + tcx: TyCtxt<'tcx>, + impl_def_id: LocalDefId, + impl_trait_ref: ty::TraitRef<'tcx>, + polarity: ty::ImplPolarity, +) -> Result<(), ErrorGuaranteed> { + let ty::ImplPolarity::Negative = polarity else { + return Ok(()); + }; + + if !tcx.trait_is_auto(impl_trait_ref.def_id) { + return Ok(()); + } + + if tcx.defaultness(impl_def_id).is_default() { + tcx.dcx().span_delayed_bug(tcx.def_span(impl_def_id), "default impl cannot be negative"); + } + + tcx.ensure_ok().orphan_check_impl(impl_def_id)?; + + match impl_trait_ref.self_ty().kind() { + ty::Adt(adt_def, adt_to_impl_args) => { + ensure_impl_params_and_item_params_correspond( + tcx, + impl_def_id, + adt_def.did(), + adt_to_impl_args, + )?; + + ensure_impl_predicates_are_implied_by_item_defn( + tcx, + impl_def_id, + adt_def.did(), + adt_to_impl_args, + ) + } + _ => { + if tcx.features().auto_traits() { + // NOTE: We ignore the applicability check for negative auto impls + // defined in libcore. In the (almost impossible) future where we + // stabilize auto impls, then the proper applicability check MUST + // be implemented here to handle non-ADT rigid types. + Ok(()) + } else { + Err(tcx.dcx().span_delayed_bug( + tcx.def_span(impl_def_id), + "incoherent impl of negative auto trait", + )) + } + } + } +} + +fn ensure_impl_params_and_item_params_correspond<'tcx>( + tcx: TyCtxt<'tcx>, + impl_def_id: LocalDefId, + adt_def_id: DefId, + adt_to_impl_args: GenericArgsRef<'tcx>, +) -> Result<(), ErrorGuaranteed> { + let Err(arg) = tcx.uses_unique_generic_params(adt_to_impl_args, CheckRegions::OnlyParam) else { + return Ok(()); + }; + + let impl_span = tcx.def_span(impl_def_id); + let item_span = tcx.def_span(adt_def_id); + let self_descr = tcx.def_descr(adt_def_id); + let polarity = match tcx.impl_polarity(impl_def_id) { + ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", + ty::ImplPolarity::Negative => "!", + }; + let trait_name = tcx + .item_name(tcx.trait_id_of_impl(impl_def_id.to_def_id()).expect("expected impl of trait")); + let mut err = struct_span_code_err!( + tcx.dcx(), + impl_span, + E0366, + "`{polarity}{trait_name}` impls cannot be specialized", + ); + match arg { + ty::util::NotUniqueParam::DuplicateParam(arg) => { + err.note(format!("`{arg}` is mentioned multiple times")) + } + ty::util::NotUniqueParam::NotParam(arg) => { + err.note(format!("`{arg}` is not a generic parameter")) + } + }; + err.span_note( + item_span, + format!( + "use the same sequence of generic lifetime, type and const parameters \ + as the {self_descr} definition", + ), + ); + Err(err.emit()) +} + +/// Confirms that all predicates defined on the `Drop` impl (`drop_impl_def_id`) are able to be +/// proven from within `adt_def_id`'s environment. I.e. all the predicates on the impl are +/// implied by the ADT being well formed. +fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>( + tcx: TyCtxt<'tcx>, + impl_def_id: LocalDefId, + adt_def_id: DefId, + adt_to_impl_args: GenericArgsRef<'tcx>, +) -> Result<(), ErrorGuaranteed> { + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + + let impl_span = tcx.def_span(impl_def_id.to_def_id()); + let trait_name = tcx + .item_name(tcx.trait_id_of_impl(impl_def_id.to_def_id()).expect("expected impl of trait")); + let polarity = match tcx.impl_polarity(impl_def_id) { + ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", + ty::ImplPolarity::Negative => "!", + }; + // Take the param-env of the adt and instantiate the args that show up in + // the implementation's self type. This gives us the assumptions that the + // self ty of the implementation is allowed to know just from it being a + // well-formed adt, since that's all we're allowed to assume while proving + // the Drop implementation is not specialized. + // + // We don't need to normalize this param-env or anything, since we're only + // instantiating it with free params, so no additional param-env normalization + // can occur on top of what has been done in the param_env query itself. + // + // Note: Ideally instead of instantiating the `ParamEnv` with the arguments from the impl ty we + // could instead use identity args for the adt. Unfortunately this would cause any errors to + // reference the params from the ADT instead of from the impl which is bad UX. To resolve + // this we "rename" the ADT's params to be the impl's params which should not affect behaviour. + let impl_adt_ty = Ty::new_adt(tcx, tcx.adt_def(adt_def_id), adt_to_impl_args); + let adt_env = + ty::EarlyBinder::bind(tcx.param_env(adt_def_id)).instantiate(tcx, adt_to_impl_args); + + let fresh_impl_args = infcx.fresh_args_for_item(impl_span, impl_def_id.to_def_id()); + let fresh_adt_ty = + tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, fresh_impl_args).self_ty(); + + ocx.eq(&ObligationCause::dummy_with_span(impl_span), adt_env, fresh_adt_ty, impl_adt_ty) + .expect("equating fully generic trait ref should never fail"); + + for (clause, span) in tcx.predicates_of(impl_def_id).instantiate(tcx, fresh_impl_args) { + let normalize_cause = traits::ObligationCause::misc(span, impl_def_id); + let pred = ocx.normalize(&normalize_cause, adt_env, clause); + let cause = traits::ObligationCause::new( + span, + impl_def_id, + ObligationCauseCode::AlwaysApplicableImpl, + ); + ocx.register_obligation(traits::Obligation::new(tcx, cause, adt_env, pred)); + } + + // All of the custom error reporting logic is to preserve parity with the old + // error messages. + // + // They can probably get removed with better treatment of the new `DropImpl` + // obligation cause code, and perhaps some custom logic in `report_region_errors`. + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + let mut guar = None; + let mut root_predicates = FxHashSet::default(); + for error in errors { + let root_predicate = error.root_obligation.predicate; + if root_predicates.insert(root_predicate) { + let item_span = tcx.def_span(adt_def_id); + let self_descr = tcx.def_descr(adt_def_id); + guar = Some( + struct_span_code_err!( + tcx.dcx(), + error.root_obligation.cause.span, + E0367, + "`{polarity}{trait_name}` impl requires `{root_predicate}` \ + but the {self_descr} it is implemented for does not", + ) + .with_span_note(item_span, "the implementor must specify the same requirement") + .emit(), + ); + } + } + return Err(guar.unwrap()); + } + + let errors = ocx.infcx.resolve_regions(impl_def_id, adt_env, []); + if !errors.is_empty() { + let mut guar = None; + for error in errors { + let item_span = tcx.def_span(adt_def_id); + let self_descr = tcx.def_descr(adt_def_id); + let outlives = match error { + RegionResolutionError::ConcreteFailure(_, a, b) => format!("{b}: {a}"), + RegionResolutionError::GenericBoundFailure(_, generic, r) => { + format!("{generic}: {r}") + } + RegionResolutionError::SubSupConflict(_, _, _, a, _, b, _) => format!("{b}: {a}"), + RegionResolutionError::UpperBoundUniverseConflict(a, _, _, _, b) => { + format!("{b}: {a}", a = ty::Region::new_var(tcx, a)) + } + RegionResolutionError::CannotNormalize(..) => unreachable!(), + }; + guar = Some( + struct_span_code_err!( + tcx.dcx(), + error.origin().span(), + E0367, + "`{polarity}{trait_name}` impl requires `{outlives}` \ + but the {self_descr} it is implemented for does not", + ) + .with_span_note(item_span, "the implementor must specify the same requirement") + .emit(), + ); + } + return Err(guar.unwrap()); + } + + Ok(()) +} diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index f2331f3fd8e13..06814eaefe84f 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -2,8 +2,7 @@ use std::cell::LazyCell; use std::ops::ControlFlow; use rustc_abi::FieldIdx; -use rustc_attr_parsing::AttributeKind; -use rustc_attr_parsing::ReprAttr::ReprPacked; +use rustc_attr_data_structures::ReprAttr::ReprPacked; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::MultiSpan; use rustc_errors::codes::*; @@ -18,21 +17,20 @@ use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::error::TypeErrorToStringExt; -use rustc_middle::ty::fold::{BottomUpFolder, fold_regions}; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{ - AdtDef, GenericArgKind, RegionKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + AdtDef, BottomUpFolder, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, fold_regions, }; use rustc_session::lint::builtin::UNINHABITED_STATIC; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_type_ir::fold::TypeFoldable; use tracing::{debug, instrument}; use ty::TypingMode; -use {rustc_attr_parsing as attr, rustc_hir as hir}; +use {rustc_attr_data_structures as attrs, rustc_hir as hir}; use super::compare_impl_item::check_type_bounds; use super::*; @@ -70,7 +68,6 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_transparent(tcx, def); check_packed(tcx, span, def); - check_unsafe_fields(tcx, def_id); } fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) { @@ -144,36 +141,6 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b true } -/// Check that the unsafe fields do not need dropping. -fn check_unsafe_fields(tcx: TyCtxt<'_>, item_def_id: LocalDefId) { - let span = tcx.def_span(item_def_id); - let def = tcx.adt_def(item_def_id); - - let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); - let args = ty::GenericArgs::identity_for_item(tcx, item_def_id); - - for field in def.all_fields() { - if !field.safety.is_unsafe() { - continue; - } - - if !allowed_union_or_unsafe_field(tcx, field.ty(tcx, args), typing_env, span) { - let hir::Node::Field(field) = tcx.hir_node_by_def_id(field.did.expect_local()) else { - unreachable!("field has to correspond to hir field") - }; - let ty_span = field.ty.span; - tcx.dcx().emit_err(errors::InvalidUnsafeField { - field_span: field.span, - sugg: errors::InvalidUnsafeFieldSuggestion { - lo: ty_span.shrink_to_lo(), - hi: ty_span.shrink_to_hi(), - }, - note: (), - }); - } - } -} - /// Check that a `static` is inhabited. fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { // Make sure statics are inhabited. @@ -216,7 +183,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { /// 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: TyCtxt<'_>, def_id: LocalDefId) { - let hir::OpaqueTy { origin, .. } = *tcx.hir().expect_opaque_ty(def_id); + let hir::OpaqueTy { origin, .. } = *tcx.hir_expect_opaque_ty(def_id); // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting // `async-std` (and `pub async fn` in general). @@ -423,8 +390,17 @@ fn best_definition_site_of_opaque<'tcx>( return ControlFlow::Continue(()); } - if let Some(hidden_ty) = - self.tcx.mir_borrowck(item_def_id).concrete_opaque_types.get(&self.opaque_def_id) + let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id); + // Don't try to check items that cannot possibly constrain the type. + if !opaque_types_defined_by.contains(&self.opaque_def_id) { + return ControlFlow::Continue(()); + } + + if let Some(hidden_ty) = self + .tcx + .mir_borrowck(item_def_id) + .ok() + .and_then(|opaque_types| opaque_types.0.get(&self.opaque_def_id)) { ControlFlow::Break((hidden_ty.span, item_def_id)) } else { @@ -439,9 +415,6 @@ fn best_definition_site_of_opaque<'tcx>( self.tcx } fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) -> Self::Result { - if let hir::ExprKind::Closure(closure) = ex.kind { - self.check(closure.def_id)?; - } intravisit::walk_expr(self, ex) } fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) -> Self::Result { @@ -469,32 +442,20 @@ fn best_definition_site_of_opaque<'tcx>( let impl_def_id = tcx.local_parent(parent); for assoc in tcx.associated_items(impl_def_id).in_definition_order() { match assoc.kind { - ty::AssocKind::Const | ty::AssocKind::Fn => { + ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => { if let ControlFlow::Break(span) = locator.check(assoc.def_id.expect_local()) { return Some(span); } } - ty::AssocKind::Type => {} + ty::AssocKind::Type { .. } => {} } } None } hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => { - let scope = tcx.hir_get_defining_scope(tcx.local_def_id_to_hir_id(opaque_def_id)); - let found = if scope == hir::CRATE_HIR_ID { - tcx.hir_walk_toplevel_module(&mut locator) - } else { - match tcx.hir_node(scope) { - Node::Item(it) => locator.visit_item(it), - Node::ImplItem(it) => locator.visit_impl_item(it), - Node::TraitItem(it) => locator.visit_trait_item(it), - Node::ForeignItem(it) => locator.visit_foreign_item(it), - other => bug!("{:?} is not a valid scope for an opaque type item", other), - } - }; - found.break_value() + tcx.hir_walk_toplevel_module(&mut locator).break_value() } } } @@ -616,10 +577,8 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe } } _ => { - tcx.dcx().span_delayed_bug( - tcx.hir().span(hir_id), - "parameter should have been resolved", - ); + tcx.dcx() + .span_delayed_bug(tcx.hir_span(hir_id), "parameter should have been resolved"); } } } @@ -745,14 +704,10 @@ fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) { pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { match tcx.def_kind(def_id) { DefKind::Static { .. } => { - tcx.ensure_ok().typeck(def_id); - maybe_check_static_with_link_section(tcx, def_id); check_static_inhabited(tcx, def_id); check_static_linkage(tcx, def_id); } - DefKind::Const => { - tcx.ensure_ok().typeck(def_id); - } + DefKind::Const => {} DefKind::Enum => { check_enum(tcx, def_id); } @@ -763,10 +718,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { def_id, tcx.def_ident_span(def_id).unwrap(), i.name, - ExternAbi::Rust, ) } - // Everything else is checked entirely within check_item_body } DefKind::Impl { of_trait } => { if of_trait && let Some(impl_trait_header) = tcx.impl_trait_header(def_id) { @@ -776,7 +729,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { .is_ok() { check_impl_items_against_trait(tcx, def_id, impl_trait_header); - check_on_unimplemented(tcx, def_id); } } } @@ -786,11 +738,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { for &assoc_item in assoc_items.in_definition_order() { match assoc_item.kind { - ty::AssocKind::Fn => { - let abi = tcx.fn_sig(assoc_item.def_id).skip_binder().abi(); - forbid_intrinsic_abi(tcx, assoc_item.ident(tcx).span, abi); - } - ty::AssocKind::Type if assoc_item.defaultness(tcx).has_value() => { + ty::AssocKind::Type { .. } if assoc_item.defaultness(tcx).has_value() => { let trait_args = GenericArgs::identity_for_item(tcx, def_id); let _: Result<_, rustc_errors::ErrorGuaranteed> = check_type_bounds( tcx, @@ -827,71 +775,55 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_type_alias_type_params_are_used(tcx, def_id); } DefKind::ForeignMod => { - let it = tcx.hir().expect_item(def_id); + let it = tcx.hir_expect_item(def_id); let hir::ItemKind::ForeignMod { abi, items } = it.kind else { return; }; check_abi(tcx, it.span, abi); - match abi { - ExternAbi::RustIntrinsic => { - for item in items { - intrinsic::check_intrinsic_type( - tcx, - item.id.owner_id.def_id, - item.span, - item.ident.name, - abi, - ); - } + for item in items { + let def_id = item.id.owner_id.def_id; + + let generics = tcx.generics_of(def_id); + let own_counts = generics.own_counts(); + if generics.own_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_code_err!( + tcx.dcx(), + item.span, + E0044, + "foreign items may not have {kinds} parameters", + ) + .with_span_label(item.span, format!("can't have {kinds} parameters")) + .with_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(); } - _ => { - for item in items { - let def_id = item.id.owner_id.def_id; - let generics = tcx.generics_of(def_id); - let own_counts = generics.own_counts(); - if generics.own_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_code_err!( - tcx.dcx(), - item.span, - E0044, - "foreign items may not have {kinds} parameters", - ) - .with_span_label(item.span, format!("can't have {kinds} parameters")) - .with_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(); - } - - let item = tcx.hir_foreign_item(item.id); - match &item.kind { - hir::ForeignItemKind::Fn(sig, _, _) => { - require_c_abi_if_c_variadic(tcx, sig.decl, abi, item.span); - } - hir::ForeignItemKind::Static(..) => { - check_static_inhabited(tcx, def_id); - check_static_linkage(tcx, def_id); - } - _ => {} - } + let item = tcx.hir_foreign_item(item.id); + match &item.kind { + hir::ForeignItemKind::Fn(sig, _, _) => { + require_c_abi_if_c_variadic(tcx, sig.decl, abi, item.span); + } + hir::ForeignItemKind::Static(..) => { + check_static_inhabited(tcx, def_id); + check_static_linkage(tcx, def_id); } + _ => {} } } } @@ -992,31 +924,7 @@ fn check_impl_items_against_trait<'tcx>( let trait_def = tcx.trait_def(trait_ref.def_id); - let infcx = tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis()); - - let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let cause = ObligationCause::misc(tcx.def_span(impl_id), impl_id); - let param_env = tcx.param_env(impl_id); - - let self_is_guaranteed_unsized = match tcx - .struct_tail_raw( - trait_ref.self_ty(), - |ty| { - ocx.structurally_normalize_ty(&cause, param_env, ty).unwrap_or_else(|_| { - Ty::new_error_with_message( - tcx, - tcx.def_span(impl_id), - "struct tail should be computable", - ) - }) - }, - || (), - ) - .kind() - { - ty::Dynamic(_, _, ty::DynKind::Dyn) | ty::Slice(_) | ty::Str => true, - _ => false, - }; + let self_is_guaranteed_unsize_self = tcx.impl_self_is_guaranteed_unsized(impl_id); for &impl_item in impl_item_refs { let ty_impl_item = tcx.associated_item(impl_item); @@ -1032,7 +940,7 @@ fn check_impl_items_against_trait<'tcx>( if res.is_ok() { match ty_impl_item.kind { - ty::AssocKind::Fn => { + ty::AssocKind::Fn { .. } => { compare_impl_item::refine::check_refining_return_position_impl_trait_in_trait( tcx, ty_impl_item, @@ -1042,12 +950,12 @@ fn check_impl_items_against_trait<'tcx>( .instantiate_identity(), ); } - ty::AssocKind::Const => {} - ty::AssocKind::Type => {} + ty::AssocKind::Const { .. } => {} + ty::AssocKind::Type { .. } => {} } } - if self_is_guaranteed_unsized && tcx.generics_require_sized_self(ty_trait_item.def_id) { + if self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(ty_trait_item.def_id) { tcx.emit_node_span_lint( rustc_lint_defs::builtin::DEAD_CODE, tcx.local_def_id_to_hir_id(ty_impl_item.def_id.expect_local()), @@ -1082,7 +990,7 @@ fn check_impl_items_against_trait<'tcx>( if !is_implemented && tcx.defaultness(impl_id).is_final() // unsized types don't need to implement methods that have `Self: Sized` bounds. - && !(self_is_guaranteed_unsized && tcx.generics_require_sized_self(trait_item_id)) + && !(self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(trait_item_id)) { missing_items.push(tcx.associated_item(trait_item_id)); } @@ -1092,7 +1000,7 @@ fn check_impl_items_against_trait<'tcx>( leaf_def.as_ref().is_some_and(|node_item| !node_item.defining_node.is_from_trait()); if !is_implemented_here { - let full_impl_span = tcx.hir().span_with_body(tcx.local_def_id_to_hir_id(impl_id)); + let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_id)); match tcx.eval_default_body_stability(trait_item_id, full_impl_span) { EvalResult::Deny { feature, reason, issue, .. } => default_body_is_unstable( tcx, @@ -1148,7 +1056,7 @@ fn check_impl_items_against_trait<'tcx>( } if !missing_items.is_empty() { - let full_impl_span = tcx.hir().span_with_body(tcx.local_def_id_to_hir_id(impl_id)); + let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_id)); missing_items_err(tcx, impl_id, &missing_items, full_impl_span); } @@ -1245,7 +1153,7 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { let repr = def.repr(); if repr.packed() { if let Some(reprs) = - attr::find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::Repr(r) => r) + attrs::find_attr!(tcx.get_all_attrs(def.did()), attrs::AttributeKind::Repr(r) => r) { for (r, _) in reprs { if let ReprPacked(pack) = r @@ -1364,7 +1272,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) let typing_env = ty::TypingEnv::non_body_analysis(tcx, field.did); let layout = tcx.layout_of(typing_env.as_query_input(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 span = tcx.hir_span_if_local(field.did).unwrap(); let trivial = layout.is_ok_and(|layout| layout.is_1zst()); if !trivial { return (span, trivial, None); @@ -1462,9 +1370,9 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { def.destructor(tcx); // force the destructor to be evaluated if def.variants().is_empty() { - attr::find_attr!( + attrs::find_attr!( tcx.get_all_attrs(def_id), - AttributeKind::Repr(rs) => { + attrs::AttributeKind::Repr(rs) => { struct_span_code_err!( tcx.dcx(), rs.first().unwrap().1, @@ -1517,7 +1425,6 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { detect_discriminant_duplicate(tcx, def); check_transparent(tcx, def); - check_unsafe_fields(tcx, def_id); } /// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal @@ -1758,7 +1665,7 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, opaque_def_id: LocalDefId) -> ErrorG opaques: Vec, closures: Vec, } - impl<'tcx> ty::visit::TypeVisitor> for OpaqueTypeCollector { + impl<'tcx> ty::TypeVisitor> for OpaqueTypeCollector { fn visit_ty(&mut self, t: Ty<'tcx>) { match *t.kind() { ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => { @@ -1846,17 +1753,19 @@ pub(super) fn check_coroutine_obligations( debug!(?typeck_results.coroutine_stalled_predicates); let mode = if tcx.next_trait_solver_globally() { - TypingMode::post_borrowck_analysis(tcx, def_id) + // This query is conceptually between HIR typeck and + // MIR borrowck. We use the opaque types defined by HIR + // and ignore region constraints. + TypingMode::borrowck(tcx, def_id) } else { TypingMode::analysis_in_body(tcx, def_id) }; - let infcx = tcx - .infer_ctxt() - // typeck writeback gives us predicates with their regions erased. - // As borrowck already has checked lifetimes, we do not need to do it again. - .ignoring_regions() - .build(mode); + // Typeck writeback gives us predicates with their regions erased. + // We only need to check the goals while ignoring lifetimes to give good + // error message and to avoid breaking the assumption of `mir_borrowck` + // that all obligations already hold modulo regions. + let infcx = tcx.infer_ctxt().ignoring_regions().build(mode); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); for (predicate, cause) in &typeck_results.coroutine_stalled_predicates { @@ -1877,6 +1786,10 @@ pub(super) fn check_coroutine_obligations( let key = infcx.resolve_vars_if_possible(key); sanity_check_found_hidden_type(tcx, key, hidden_type)?; } + } else { + // We're not checking region constraints here, so we can simply drop the + // added opaque type uses in `TypingMode::Borrowck`. + let _ = infcx.take_opaque_types(); } Ok(()) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index c193aad2afd00..2ee41e2efbd33 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -3,23 +3,21 @@ use std::borrow::Cow; use std::iter; use hir::def_id::{DefId, DefIdMap, LocalDefId}; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; -use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; +use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::VisitorExt; use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisit}; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::fold::BottomUpFolder; -use rustc_middle::ty::util::ExplicitSelf; use rustc_middle::ty::{ - self, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeVisitableExt, TypingMode, Upcast, + self, BottomUpFolder, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast, }; use rustc_middle::{bug, span_bug}; -use rustc_span::Span; +use rustc_span::{DUMMY_SP, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::regions::InferCtxtRegionExt; @@ -45,9 +43,11 @@ pub(super) fn compare_impl_item( debug!(?impl_trait_ref); match impl_item.kind { - ty::AssocKind::Fn => compare_impl_method(tcx, impl_item, trait_item, impl_trait_ref), - ty::AssocKind::Type => compare_impl_ty(tcx, impl_item, trait_item, impl_trait_ref), - ty::AssocKind::Const => compare_impl_const(tcx, impl_item, trait_item, impl_trait_ref), + ty::AssocKind::Fn { .. } => compare_impl_method(tcx, impl_item, trait_item, impl_trait_ref), + ty::AssocKind::Type { .. } => compare_impl_ty(tcx, impl_item, trait_item, impl_trait_ref), + ty::AssocKind::Const { .. } => { + compare_impl_const(tcx, impl_item, trait_item, impl_trait_ref) + } } } @@ -356,61 +356,14 @@ fn compare_method_predicate_entailment<'tcx>( } if !(impl_sig, trait_sig).references_error() { - // Select obligations to make progress on inference before processing - // the wf obligation below. - // FIXME(-Znext-solver): Not needed when the hack below is removed. - let errors = ocx.select_where_possible(); - if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(errors); - return Err(reported); - } - - // See #108544. Annoying, we can end up in cases where, because of winnowing, - // we pick param env candidates over a more general impl, leading to more - // stricter lifetime requirements than we would otherwise need. This can - // trigger the lint. Instead, let's only consider type outlives and - // region outlives obligations. - // - // FIXME(-Znext-solver): Try removing this hack again once the new - // solver is stable. We should just be able to register a WF pred for - // the fn sig. - let mut wf_args: smallvec::SmallVec<[_; 4]> = - unnormalized_impl_sig.inputs_and_output.iter().map(|ty| ty.into()).collect(); - // Annoyingly, asking for the WF predicates of an array (with an unevaluated const (only?)) - // will give back the well-formed predicate of the same array. - let mut wf_args_seen: FxHashSet<_> = wf_args.iter().copied().collect(); - while let Some(arg) = wf_args.pop() { - let Some(obligations) = rustc_trait_selection::traits::wf::obligations( - infcx, - param_env, - impl_m_def_id, - 0, - arg, - impl_m_span, - ) else { - continue; - }; - for obligation in obligations { - debug!(?obligation); - match obligation.predicate.kind().skip_binder() { - // We need to register Projection oblgiations too, because we may end up with - // an implied `X::Item: 'a`, which gets desugared into `X::Item = ?0`, `?0: 'a`. - // If we only register the region outlives obligation, this leads to an unconstrained var. - // See `implied_bounds_entailment_alias_var.rs` test. - ty::PredicateKind::Clause( - ty::ClauseKind::RegionOutlives(..) - | ty::ClauseKind::TypeOutlives(..) - | ty::ClauseKind::Projection(..), - ) => ocx.register_obligation(obligation), - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - if wf_args_seen.insert(arg) { - wf_args.push(arg) - } - } - _ => {} - } - } - } + ocx.register_obligation(traits::Obligation::new( + infcx.tcx, + cause, + param_env, + ty::ClauseKind::WellFormed( + Ty::new_fn_ptr(tcx, ty::Binder::dummy(unnormalized_impl_sig)).into(), + ), + )); } // Check that all obligations are satisfied by the implementation's @@ -444,7 +397,7 @@ impl<'tcx> TypeFolder> for RemapLateParam<'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - if let ty::ReLateParam(fr) = *r { + if let ty::ReLateParam(fr) = r.kind() { ty::Region::new_late_param( self.tcx, fr.scope, @@ -608,7 +561,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // with placeholders, which imply nothing about outlives bounds, and then // prove below that the hidden types are well formed. let universe = infcx.create_next_universe(); - let mut idx = 0; + let mut idx = ty::BoundVar::ZERO; let mapping: FxIndexMap<_, _> = collector .types .iter() @@ -625,10 +578,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( tcx, ty::Placeholder { universe, - bound: ty::BoundTy { - var: ty::BoundVar::from_usize(idx), - kind: ty::BoundTyKind::Anon, - }, + bound: ty::BoundTy { var: idx, kind: ty::BoundTyKind::Anon }, }, ), ) @@ -656,7 +606,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( cause.span, E0053, "method `{}` has an incompatible return type for trait", - trait_m.name + trait_m.name() ); infcx.err_ctxt().note_type_err( &mut diag, @@ -996,6 +946,26 @@ impl<'tcx> ty::FallibleTypeFolder> for RemapHiddenTyRegions<'tcx> { } } +/// Gets the string for an explicit self declaration, e.g. "self", "&self", +/// etc. +fn get_self_string<'tcx, P>(self_arg_ty: Ty<'tcx>, is_self_ty: P) -> String +where + P: Fn(Ty<'tcx>) -> bool, +{ + if is_self_ty(self_arg_ty) { + "self".to_owned() + } else if let ty::Ref(_, ty, mutbl) = self_arg_ty.kind() + && is_self_ty(*ty) + { + match mutbl { + hir::Mutability::Not => "&self".to_owned(), + hir::Mutability::Mut => "&mut self".to_owned(), + } + } else { + format!("self: {self_arg_ty}") + } +} + fn report_trait_method_mismatch<'tcx>( infcx: &InferCtxt<'tcx>, mut cause: ObligationCause<'tcx>, @@ -1014,28 +984,29 @@ fn report_trait_method_mismatch<'tcx>( impl_err_span, E0053, "method `{}` has an incompatible type for trait", - trait_m.name + trait_m.name() ); match &terr { TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) - if trait_m.fn_has_self_parameter => + if trait_m.is_method() => { let ty = trait_sig.inputs()[0]; - let sugg = match ExplicitSelf::determine(ty, |ty| ty == impl_trait_ref.self_ty()) { - ExplicitSelf::ByValue => "self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), - _ => format!("self: {ty}"), - }; + let sugg = get_self_string(ty, |ty| ty == impl_trait_ref.self_ty()); // When the `impl` receiver is an arbitrary self type, like `self: Box`, the // span points only at the type `Box, but we want to cover the whole // argument pattern and type. - let (sig, body) = tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).expect_fn(); + let (sig, body) = tcx.hir_expect_impl_item(impl_m.def_id.expect_local()).expect_fn(); let span = tcx - .hir_body_param_names(body) + .hir_body_param_idents(body) .zip(sig.decl.inputs.iter()) - .map(|(param, ty)| param.span.to(ty.span)) + .map(|(param_ident, ty)| { + if let Some(param_ident) = param_ident { + param_ident.span.to(ty.span) + } else { + ty.span + } + }) .next() .unwrap_or(impl_err_span); @@ -1051,7 +1022,7 @@ fn report_trait_method_mismatch<'tcx>( // Suggestion to change output type. We do not suggest in `async` functions // to avoid complex logic or incorrect output. if let ImplItemKind::Fn(sig, _) = - &tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind + &tcx.hir_expect_impl_item(impl_m.def_id.expect_local()).kind && !sig.header.asyncness.is_async() { let msg = "change the output type to match the trait"; @@ -1119,65 +1090,319 @@ fn check_region_bounds_on_impl_item<'tcx>( // but found 0" it's confusing, because it looks like there // are zero. Since I don't quite know how to phrase things at // the moment, give a kind of vague error message. - if trait_params != impl_params { - let span = tcx - .hir_get_generics(impl_m.def_id.expect_local()) - .expect("expected impl item to have generics or else we can't compare them") - .span; - - let mut generics_span = None; - let mut bounds_span = vec![]; - let mut where_span = None; - if let Some(trait_node) = tcx.hir_get_if_local(trait_m.def_id) - && let Some(trait_generics) = trait_node.generics() - { - generics_span = Some(trait_generics.span); - // FIXME: we could potentially look at the impl's bounds to not point at bounds that - // *are* present in the impl. - for p in trait_generics.predicates { - if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind { - for b in pred.bounds { + if trait_params == impl_params { + return Ok(()); + } + + if !delay && let Some(guar) = check_region_late_boundedness(tcx, impl_m, trait_m) { + return Err(guar); + } + + let span = tcx + .hir_get_generics(impl_m.def_id.expect_local()) + .expect("expected impl item to have generics or else we can't compare them") + .span; + + let mut generics_span = None; + let mut bounds_span = vec![]; + let mut where_span = None; + + if let Some(trait_node) = tcx.hir_get_if_local(trait_m.def_id) + && let Some(trait_generics) = trait_node.generics() + { + generics_span = Some(trait_generics.span); + // FIXME: we could potentially look at the impl's bounds to not point at bounds that + // *are* present in the impl. + for p in trait_generics.predicates { + match p.kind { + hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { + bounds, + .. + }) + | hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { + bounds, + .. + }) => { + for b in *bounds { if let hir::GenericBound::Outlives(lt) = b { bounds_span.push(lt.ident.span); } } } + _ => {} } - if let Some(impl_node) = tcx.hir_get_if_local(impl_m.def_id) - && let Some(impl_generics) = impl_node.generics() - { - let mut impl_bounds = 0; - for p in impl_generics.predicates { - if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind { - for b in pred.bounds { + } + if let Some(impl_node) = tcx.hir_get_if_local(impl_m.def_id) + && let Some(impl_generics) = impl_node.generics() + { + let mut impl_bounds = 0; + for p in impl_generics.predicates { + match p.kind { + hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { + bounds, + .. + }) + | hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { + bounds, + .. + }) => { + for b in *bounds { if let hir::GenericBound::Outlives(_) = b { impl_bounds += 1; } } } + _ => {} + } + } + if impl_bounds == bounds_span.len() { + bounds_span = vec![]; + } else if impl_generics.has_where_clause_predicates { + where_span = Some(impl_generics.where_clause_span); + } + } + } + + let reported = tcx + .dcx() + .create_err(LifetimesOrBoundsMismatchOnTrait { + span, + item_kind: impl_m.descr(), + ident: impl_m.ident(tcx), + generics_span, + bounds_span, + where_span, + }) + .emit_unless(delay); + + Err(reported) +} + +#[allow(unused)] +enum LateEarlyMismatch<'tcx> { + EarlyInImpl(DefId, DefId, ty::Region<'tcx>), + LateInImpl(DefId, DefId, ty::Region<'tcx>), +} + +fn check_region_late_boundedness<'tcx>( + tcx: TyCtxt<'tcx>, + impl_m: ty::AssocItem, + trait_m: ty::AssocItem, +) -> Option { + if !impl_m.is_fn() { + return None; + } + + let (infcx, param_env) = tcx + .infer_ctxt() + .build_with_typing_env(ty::TypingEnv::non_body_analysis(tcx, impl_m.def_id)); + + let impl_m_args = infcx.fresh_args_for_item(DUMMY_SP, impl_m.def_id); + let impl_m_sig = tcx.fn_sig(impl_m.def_id).instantiate(tcx, impl_m_args); + let impl_m_sig = tcx.liberate_late_bound_regions(impl_m.def_id, impl_m_sig); + + let trait_m_args = infcx.fresh_args_for_item(DUMMY_SP, trait_m.def_id); + let trait_m_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_m_args); + let trait_m_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_m_sig); + + let ocx = ObligationCtxt::new(&infcx); + + // Equate the signatures so that we can infer whether a late-bound param was present where + // an early-bound param was expected, since we replace the late-bound lifetimes with + // `ReLateParam`, and early-bound lifetimes with infer vars, so the early-bound args will + // resolve to `ReLateParam` if there is a mismatch. + let Ok(()) = ocx.eq( + &ObligationCause::dummy(), + param_env, + ty::Binder::dummy(trait_m_sig), + ty::Binder::dummy(impl_m_sig), + ) else { + return None; + }; + + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + return None; + } + + let mut mismatched = vec![]; + + let impl_generics = tcx.generics_of(impl_m.def_id); + for (id_arg, arg) in + std::iter::zip(ty::GenericArgs::identity_for_item(tcx, impl_m.def_id), impl_m_args) + { + if let ty::GenericArgKind::Lifetime(r) = arg.unpack() + && let ty::ReVar(vid) = r.kind() + && let r = infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(tcx, vid) + && let ty::ReLateParam(ty::LateParamRegion { + kind: ty::LateParamRegionKind::Named(trait_param_def_id, _), + .. + }) = r.kind() + && let ty::ReEarlyParam(ebr) = id_arg.expect_region().kind() + { + mismatched.push(LateEarlyMismatch::EarlyInImpl( + impl_generics.region_param(ebr, tcx).def_id, + trait_param_def_id, + id_arg.expect_region(), + )); + } + } + + let trait_generics = tcx.generics_of(trait_m.def_id); + for (id_arg, arg) in + std::iter::zip(ty::GenericArgs::identity_for_item(tcx, trait_m.def_id), trait_m_args) + { + if let ty::GenericArgKind::Lifetime(r) = arg.unpack() + && let ty::ReVar(vid) = r.kind() + && let r = infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(tcx, vid) + && let ty::ReLateParam(ty::LateParamRegion { + kind: ty::LateParamRegionKind::Named(impl_param_def_id, _), + .. + }) = r.kind() + && let ty::ReEarlyParam(ebr) = id_arg.expect_region().kind() + { + mismatched.push(LateEarlyMismatch::LateInImpl( + impl_param_def_id, + trait_generics.region_param(ebr, tcx).def_id, + id_arg.expect_region(), + )); + } + } + + if mismatched.is_empty() { + return None; + } + + let spans: Vec<_> = mismatched + .iter() + .map(|param| { + let (LateEarlyMismatch::EarlyInImpl(impl_param_def_id, ..) + | LateEarlyMismatch::LateInImpl(impl_param_def_id, ..)) = param; + tcx.def_span(impl_param_def_id) + }) + .collect(); + + let mut diag = tcx + .dcx() + .struct_span_err(spans, "lifetime parameters do not match the trait definition") + .with_note("lifetime parameters differ in whether they are early- or late-bound") + .with_code(E0195); + for mismatch in mismatched { + match mismatch { + LateEarlyMismatch::EarlyInImpl( + impl_param_def_id, + trait_param_def_id, + early_bound_region, + ) => { + let mut multispan = MultiSpan::from_spans(vec![ + tcx.def_span(impl_param_def_id), + tcx.def_span(trait_param_def_id), + ]); + multispan + .push_span_label(tcx.def_span(tcx.parent(impl_m.def_id)), "in this impl..."); + multispan + .push_span_label(tcx.def_span(tcx.parent(trait_m.def_id)), "in this trait..."); + multispan.push_span_label( + tcx.def_span(impl_param_def_id), + format!("`{}` is early-bound", tcx.item_name(impl_param_def_id)), + ); + multispan.push_span_label( + tcx.def_span(trait_param_def_id), + format!("`{}` is late-bound", tcx.item_name(trait_param_def_id)), + ); + if let Some(span) = + find_region_in_predicates(tcx, impl_m.def_id, early_bound_region) + { + multispan.push_span_label( + span, + format!( + "this lifetime bound makes `{}` early-bound", + tcx.item_name(impl_param_def_id) + ), + ); } - if impl_bounds == bounds_span.len() { - bounds_span = vec![]; - } else if impl_generics.has_where_clause_predicates { - where_span = Some(impl_generics.where_clause_span); + diag.span_note( + multispan, + format!( + "`{}` differs between the trait and impl", + tcx.item_name(impl_param_def_id) + ), + ); + } + LateEarlyMismatch::LateInImpl( + impl_param_def_id, + trait_param_def_id, + early_bound_region, + ) => { + let mut multispan = MultiSpan::from_spans(vec![ + tcx.def_span(impl_param_def_id), + tcx.def_span(trait_param_def_id), + ]); + multispan + .push_span_label(tcx.def_span(tcx.parent(impl_m.def_id)), "in this impl..."); + multispan + .push_span_label(tcx.def_span(tcx.parent(trait_m.def_id)), "in this trait..."); + multispan.push_span_label( + tcx.def_span(impl_param_def_id), + format!("`{}` is late-bound", tcx.item_name(impl_param_def_id)), + ); + multispan.push_span_label( + tcx.def_span(trait_param_def_id), + format!("`{}` is early-bound", tcx.item_name(trait_param_def_id)), + ); + if let Some(span) = + find_region_in_predicates(tcx, trait_m.def_id, early_bound_region) + { + multispan.push_span_label( + span, + format!( + "this lifetime bound makes `{}` early-bound", + tcx.item_name(trait_param_def_id) + ), + ); } + diag.span_note( + multispan, + format!( + "`{}` differs between the trait and impl", + tcx.item_name(impl_param_def_id) + ), + ); } } - let reported = tcx - .dcx() - .create_err(LifetimesOrBoundsMismatchOnTrait { - span, - item_kind: impl_m.descr(), - ident: impl_m.ident(tcx), - generics_span, - bounds_span, - where_span, - }) - .emit_unless(delay); - return Err(reported); } - Ok(()) + Some(diag.emit()) +} + +fn find_region_in_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + early_bound_region: ty::Region<'tcx>, +) -> Option { + for (pred, span) in tcx.explicit_predicates_of(def_id).instantiate_identity(tcx) { + if pred.visit_with(&mut FindRegion(early_bound_region)).is_break() { + return Some(span); + } + } + + struct FindRegion<'tcx>(ty::Region<'tcx>); + impl<'tcx> TypeVisitor> for FindRegion<'tcx> { + type Result = ControlFlow<()>; + fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result { + if r == self.0 { ControlFlow::Break(()) } else { ControlFlow::Continue(()) } + } + } + + None } #[instrument(level = "debug", skip(infcx))] @@ -1190,12 +1415,12 @@ fn extract_spans_for_error_reporting<'tcx>( ) -> (Span, Option) { let tcx = infcx.tcx; let mut impl_args = { - let (sig, _) = tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).expect_fn(); + let (sig, _) = tcx.hir_expect_impl_item(impl_m.def_id.expect_local()).expect_fn(); sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())) }; let trait_args = trait_m.def_id.as_local().map(|def_id| { - let (sig, _) = tcx.hir().expect_trait_item(def_id).expect_fn(); + let (sig, _) = tcx.hir_expect_trait_item(def_id).expect_fn(); sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())) }); @@ -1203,7 +1428,7 @@ fn extract_spans_for_error_reporting<'tcx>( TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => { (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i))) } - _ => (cause.span, tcx.hir().span_if_local(trait_m.def_id)), + _ => (cause.span, tcx.hir_span_if_local(trait_m.def_id)), } } @@ -1233,15 +1458,10 @@ fn compare_self_type<'tcx>( .build_with_typing_env(ty::TypingEnv::non_body_analysis(tcx, method.def_id)); let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty); let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty); - match ExplicitSelf::determine(self_arg_ty, can_eq_self) { - ExplicitSelf::ByValue => "self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), - _ => format!("self: {self_arg_ty}"), - } + get_self_string(self_arg_ty, can_eq_self) }; - match (trait_m.fn_has_self_parameter, impl_m.fn_has_self_parameter) { + match (trait_m.is_method(), impl_m.is_method()) { (false, false) | (true, true) => {} (false, true) => { @@ -1252,14 +1472,14 @@ fn compare_self_type<'tcx>( impl_m_span, E0185, "method `{}` has a `{}` declaration in the impl, but not in the trait", - trait_m.name, + trait_m.name(), self_descr ); err.span_label(impl_m_span, format!("`{self_descr}` used in impl")); - if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { + if let Some(span) = tcx.hir_span_if_local(trait_m.def_id) { err.span_label(span, format!("trait method declared without `{self_descr}`")); } else { - err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); + err.note_trait_signature(trait_m.name(), trait_m.signature(tcx)); } return Err(err.emit_unless(delay)); } @@ -1272,14 +1492,14 @@ fn compare_self_type<'tcx>( impl_m_span, E0186, "method `{}` has a `{}` declaration in the trait, but not in the impl", - trait_m.name, + trait_m.name(), self_descr ); err.span_label(impl_m_span, format!("expected `{self_descr}` in impl")); - if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { + if let Some(span) = tcx.hir_span_if_local(trait_m.def_id) { err.span_label(span, format!("`{self_descr}` used in trait")); } else { - err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); + err.note_trait_signature(trait_m.name(), trait_m.signature(tcx)); } return Err(err.emit_unless(delay)); @@ -1349,7 +1569,7 @@ fn compare_number_of_generics<'tcx>( let mut err_occurred = None; for (kind, trait_count, impl_count) in matchings { if impl_count != trait_count { - let arg_spans = |kind: ty::AssocKind, generics: &hir::Generics<'_>| { + let arg_spans = |item: &ty::AssocItem, generics: &hir::Generics<'_>| { let mut spans = generics .params .iter() @@ -1359,7 +1579,7 @@ fn compare_number_of_generics<'tcx>( } => { // A fn can have an arbitrary number of extra elided lifetimes for the // same signature. - !matches!(kind, ty::AssocKind::Fn) + !item.is_fn() } _ => true, }) @@ -1371,8 +1591,8 @@ fn compare_number_of_generics<'tcx>( spans }; let (trait_spans, impl_trait_spans) = if let Some(def_id) = trait_.def_id.as_local() { - let trait_item = tcx.hir().expect_trait_item(def_id); - let arg_spans: Vec = arg_spans(trait_.kind, trait_item.generics); + let trait_item = tcx.hir_expect_trait_item(def_id); + let arg_spans: Vec = arg_spans(&trait_, trait_item.generics); let impl_trait_spans: Vec = trait_item .generics .params @@ -1384,11 +1604,11 @@ fn compare_number_of_generics<'tcx>( .collect(); (Some(arg_spans), impl_trait_spans) } else { - let trait_span = tcx.hir().span_if_local(trait_.def_id); + let trait_span = tcx.hir_span_if_local(trait_.def_id); (trait_span.map(|s| vec![s]), vec![]) }; - let impl_item = tcx.hir().expect_impl_item(impl_.def_id.expect_local()); + let impl_item = tcx.hir_expect_impl_item(impl_.def_id.expect_local()); let impl_item_impl_trait_spans: Vec = impl_item .generics .params @@ -1398,7 +1618,7 @@ fn compare_number_of_generics<'tcx>( _ => None, }) .collect(); - let spans = arg_spans(impl_.kind, impl_item.generics); + let spans = arg_spans(&impl_, impl_item.generics); let span = spans.first().copied(); let mut err = tcx.dcx().struct_span_err( @@ -1407,7 +1627,7 @@ fn compare_number_of_generics<'tcx>( "{} `{}` has {} {kind} parameter{} but its trait \ declaration has {} {kind} parameter{}", item_kind, - trait_.name, + trait_.name(), impl_count, pluralize!(impl_count), trait_count, @@ -1466,7 +1686,7 @@ fn compare_number_of_method_arguments<'tcx>( .def_id .as_local() .and_then(|def_id| { - let (trait_m_sig, _) = &tcx.hir().expect_trait_item(def_id).expect_fn(); + let (trait_m_sig, _) = &tcx.hir_expect_trait_item(def_id).expect_fn(); let pos = trait_number_args.saturating_sub(1); trait_m_sig.decl.inputs.get(pos).map(|arg| { if pos == 0 { @@ -1476,9 +1696,9 @@ fn compare_number_of_method_arguments<'tcx>( } }) }) - .or_else(|| tcx.hir().span_if_local(trait_m.def_id)); + .or_else(|| tcx.hir_span_if_local(trait_m.def_id)); - let (impl_m_sig, _) = &tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).expect_fn(); + let (impl_m_sig, _) = &tcx.hir_expect_impl_item(impl_m.def_id.expect_local()).expect_fn(); let pos = impl_number_args.saturating_sub(1); let impl_span = impl_m_sig .decl @@ -1498,7 +1718,7 @@ fn compare_number_of_method_arguments<'tcx>( impl_span, E0050, "method `{}` has {} but the declaration in trait `{}` has {}", - trait_m.name, + trait_m.name(), potentially_plural_count(impl_number_args, "parameter"), tcx.def_path_str(trait_m.def_id), trait_number_args @@ -1513,7 +1733,7 @@ fn compare_number_of_method_arguments<'tcx>( ), ); } else { - err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); + err.note_trait_signature(trait_m.name(), trait_m.signature(tcx)); } err.span_label( @@ -1567,7 +1787,7 @@ fn compare_synthetic_generics<'tcx>( impl_span, E0643, "method `{}` has incompatible signature for trait", - trait_m.name + trait_m.name() ); err.span_label(trait_span, "declaration in trait here"); if impl_synthetic { @@ -1580,10 +1800,10 @@ fn compare_synthetic_generics<'tcx>( // as another generic argument let new_name = tcx.opt_item_name(trait_def_id)?; let trait_m = trait_m.def_id.as_local()?; - let trait_m = tcx.hir().expect_trait_item(trait_m); + let trait_m = tcx.hir_expect_trait_item(trait_m); let impl_m = impl_m.def_id.as_local()?; - let impl_m = tcx.hir().expect_impl_item(impl_m); + let impl_m = tcx.hir_expect_impl_item(impl_m); // in case there are no generics, take the spot between the function name // and the opening paren of the argument list @@ -1613,7 +1833,7 @@ fn compare_synthetic_generics<'tcx>( err.span_label(impl_span, "expected `impl Trait`, found generic parameter"); let _: Option<_> = try { let impl_m = impl_m.def_id.as_local()?; - let impl_m = tcx.hir().expect_impl_item(impl_m); + let impl_m = tcx.hir_expect_impl_item(impl_m); let (sig, _) = impl_m.expect_fn(); let input_tys = sig.decl.inputs; @@ -1689,7 +1909,7 @@ fn compare_generic_param_kinds<'tcx>( trait_item: ty::AssocItem, delay: bool, ) -> Result<(), ErrorGuaranteed> { - assert_eq!(impl_item.kind, trait_item.kind); + assert_eq!(impl_item.as_tag(), trait_item.as_tag()); let ty_const_params_of = |def_id| { tcx.generics_of(def_id).own_params.iter().filter(|param| { @@ -1727,7 +1947,7 @@ fn compare_generic_param_kinds<'tcx>( E0053, "{} `{}` has an incompatible generic parameter for trait `{}`", impl_item.descr(), - trait_item.name, + trait_item.name(), &tcx.def_path_str(tcx.parent(trait_item.def_id)) ); @@ -1855,7 +2075,7 @@ fn compare_const_predicate_entailment<'tcx>( debug!(?impl_ty, ?trait_ty); // Locate the Span containing just the type of the offending impl - let (ty, _) = tcx.hir().expect_impl_item(impl_ct_def_id).expect_const(); + let (ty, _) = tcx.hir_expect_impl_item(impl_ct_def_id).expect_const(); cause.span = ty.span; let mut diag = struct_span_code_err!( @@ -1863,12 +2083,12 @@ fn compare_const_predicate_entailment<'tcx>( cause.span, E0326, "implemented const `{}` has an incompatible type for trait", - trait_ct.name + trait_ct.name() ); let trait_c_span = trait_ct.def_id.as_local().map(|trait_ct_def_id| { // Add a label to the Span containing just the type of the const - let (ty, _) = tcx.hir().expect_trait_item(trait_ct_def_id).expect_const(); + let (ty, _) = tcx.hir_expect_trait_item(trait_ct_def_id).expect_const(); ty.span }); @@ -2221,16 +2441,19 @@ fn param_env_with_gat_bounds<'tcx>( // of the RPITITs associated with the same body. This is because checking // the item bounds of RPITITs often involves nested RPITITs having to prove // bounds about themselves. - let impl_tys_to_install = match impl_ty.opt_rpitit_info { - None => vec![impl_ty], - Some( - ty::ImplTraitInTraitData::Impl { fn_def_id } - | ty::ImplTraitInTraitData::Trait { fn_def_id, .. }, - ) => tcx + let impl_tys_to_install = match impl_ty.kind { + ty::AssocKind::Type { + data: + ty::AssocTypeData::Rpitit( + ty::ImplTraitInTraitData::Impl { fn_def_id } + | ty::ImplTraitInTraitData::Trait { fn_def_id, .. }, + ), + } => tcx .associated_types_for_impl_traits_in_associated_fn(fn_def_id) .iter() .map(|def_id| tcx.associated_item(*def_id)) .collect(), + _ => vec![impl_ty], }; for impl_ty in impl_tys_to_install { @@ -2361,7 +2584,7 @@ fn try_report_async_mismatch<'tcx>( return Err(tcx.sess.dcx().emit_err(MethodShouldReturnFuture { span: tcx.def_span(impl_m.def_id), method_name: tcx.item_ident(impl_m.def_id), - trait_item_span: tcx.hir().span_if_local(trait_m.def_id), + trait_item_span: tcx.hir_span_if_local(trait_m.def_id), })); } } diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs deleted file mode 100644 index d7dfe482da449..0000000000000 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ /dev/null @@ -1,226 +0,0 @@ -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::codes::*; -use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; -use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; -use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; -use rustc_middle::ty::util::CheckRegions; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypingMode}; -use rustc_trait_selection::regions::InferCtxtRegionExt; -use rustc_trait_selection::traits::{self, ObligationCtxt}; - -use crate::errors; -use crate::hir::def_id::{DefId, LocalDefId}; - -/// This function confirms that the `Drop` implementation identified by -/// `drop_impl_did` is not any more specialized than the type it is -/// attached to (Issue #8142). -/// -/// This means: -/// -/// 1. The self type must be nominal (this is already checked during -/// coherence), -/// -/// 2. The generic region/type parameters of the impl's self type must -/// all be parameters of the Drop impl itself (i.e., no -/// specialization like `impl Drop for Foo`), and, -/// -/// 3. Any bounds on the generic parameters must be reflected in the -/// struct/enum definition for the nominal type itself (i.e. -/// cannot do `struct S; impl Drop for S { ... }`). -/// -pub(crate) fn check_drop_impl( - tcx: TyCtxt<'_>, - drop_impl_did: DefId, -) -> Result<(), ErrorGuaranteed> { - match tcx.impl_polarity(drop_impl_did) { - ty::ImplPolarity::Positive => {} - ty::ImplPolarity::Negative => { - return Err(tcx.dcx().emit_err(errors::DropImplPolarity::Negative { - span: tcx.def_span(drop_impl_did), - })); - } - ty::ImplPolarity::Reservation => { - return Err(tcx.dcx().emit_err(errors::DropImplPolarity::Reservation { - span: tcx.def_span(drop_impl_did), - })); - } - } - let dtor_self_type = tcx.type_of(drop_impl_did).instantiate_identity(); - match dtor_self_type.kind() { - ty::Adt(adt_def, adt_to_impl_args) => { - ensure_drop_params_and_item_params_correspond( - tcx, - drop_impl_did.expect_local(), - adt_def.did(), - adt_to_impl_args, - )?; - - ensure_drop_predicates_are_implied_by_item_defn( - tcx, - drop_impl_did.expect_local(), - adt_def.did().expect_local(), - adt_to_impl_args, - ) - } - _ => { - // Destructors only work on nominal types. This was - // already checked by coherence, but compilation may - // not have been terminated. - let span = tcx.def_span(drop_impl_did); - let reported = tcx.dcx().span_delayed_bug( - span, - format!("should have been rejected by coherence check: {dtor_self_type}"), - ); - Err(reported) - } - } -} - -fn ensure_drop_params_and_item_params_correspond<'tcx>( - tcx: TyCtxt<'tcx>, - drop_impl_did: LocalDefId, - self_type_did: DefId, - adt_to_impl_args: GenericArgsRef<'tcx>, -) -> Result<(), ErrorGuaranteed> { - let Err(arg) = tcx.uses_unique_generic_params(adt_to_impl_args, CheckRegions::OnlyParam) else { - return Ok(()); - }; - - let drop_impl_span = tcx.def_span(drop_impl_did); - let item_span = tcx.def_span(self_type_did); - let self_descr = tcx.def_descr(self_type_did); - let mut err = struct_span_code_err!( - tcx.dcx(), - drop_impl_span, - E0366, - "`Drop` impls cannot be specialized" - ); - match arg { - ty::util::NotUniqueParam::DuplicateParam(arg) => { - err.note(format!("`{arg}` is mentioned multiple times")) - } - ty::util::NotUniqueParam::NotParam(arg) => { - err.note(format!("`{arg}` is not a generic parameter")) - } - }; - err.span_note( - item_span, - format!( - "use the same sequence of generic lifetime, type and const parameters \ - as the {self_descr} definition", - ), - ); - Err(err.emit()) -} - -/// Confirms that all predicates defined on the `Drop` impl (`drop_impl_def_id`) are able to be -/// proven from within `adt_def_id`'s environment. I.e. all the predicates on the impl are -/// implied by the ADT being well formed. -fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( - tcx: TyCtxt<'tcx>, - drop_impl_def_id: LocalDefId, - adt_def_id: LocalDefId, - adt_to_impl_args: GenericArgsRef<'tcx>, -) -> Result<(), ErrorGuaranteed> { - let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - - let impl_span = tcx.def_span(drop_impl_def_id.to_def_id()); - - // Take the param-env of the adt and instantiate the args that show up in - // the implementation's self type. This gives us the assumptions that the - // self ty of the implementation is allowed to know just from it being a - // well-formed adt, since that's all we're allowed to assume while proving - // the Drop implementation is not specialized. - // - // We don't need to normalize this param-env or anything, since we're only - // instantiating it with free params, so no additional param-env normalization - // can occur on top of what has been done in the param_env query itself. - // - // Note: Ideally instead of instantiating the `ParamEnv` with the arguments from the impl ty we - // could instead use identity args for the adt. Unfortunately this would cause any errors to - // reference the params from the ADT instead of from the impl which is bad UX. To resolve - // this we "rename" the ADT's params to be the impl's params which should not affect behaviour. - let impl_adt_ty = Ty::new_adt(tcx, tcx.adt_def(adt_def_id), adt_to_impl_args); - let adt_env = - ty::EarlyBinder::bind(tcx.param_env(adt_def_id)).instantiate(tcx, adt_to_impl_args); - - let fresh_impl_args = infcx.fresh_args_for_item(impl_span, drop_impl_def_id.to_def_id()); - let fresh_adt_ty = - tcx.impl_trait_ref(drop_impl_def_id).unwrap().instantiate(tcx, fresh_impl_args).self_ty(); - - ocx.eq(&ObligationCause::dummy_with_span(impl_span), adt_env, fresh_adt_ty, impl_adt_ty) - .unwrap(); - - for (clause, span) in tcx.predicates_of(drop_impl_def_id).instantiate(tcx, fresh_impl_args) { - let normalize_cause = traits::ObligationCause::misc(span, adt_def_id); - let pred = ocx.normalize(&normalize_cause, adt_env, clause); - let cause = traits::ObligationCause::new(span, adt_def_id, ObligationCauseCode::DropImpl); - ocx.register_obligation(traits::Obligation::new(tcx, cause, adt_env, pred)); - } - - // All of the custom error reporting logic is to preserve parity with the old - // error messages. - // - // They can probably get removed with better treatment of the new `DropImpl` - // obligation cause code, and perhaps some custom logic in `report_region_errors`. - - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - let mut guar = None; - let mut root_predicates = FxHashSet::default(); - for error in errors { - let root_predicate = error.root_obligation.predicate; - if root_predicates.insert(root_predicate) { - let item_span = tcx.def_span(adt_def_id); - let self_descr = tcx.def_descr(adt_def_id.to_def_id()); - guar = Some( - struct_span_code_err!( - tcx.dcx(), - error.root_obligation.cause.span, - E0367, - "`Drop` impl requires `{root_predicate}` \ - but the {self_descr} it is implemented for does not", - ) - .with_span_note(item_span, "the implementor must specify the same requirement") - .emit(), - ); - } - } - return Err(guar.unwrap()); - } - - let errors = ocx.infcx.resolve_regions(adt_def_id, adt_env, []); - if !errors.is_empty() { - let mut guar = None; - for error in errors { - let item_span = tcx.def_span(adt_def_id); - let self_descr = tcx.def_descr(adt_def_id.to_def_id()); - let outlives = match error { - RegionResolutionError::ConcreteFailure(_, a, b) => format!("{b}: {a}"), - RegionResolutionError::GenericBoundFailure(_, generic, r) => { - format!("{generic}: {r}") - } - RegionResolutionError::SubSupConflict(_, _, _, a, _, b, _) => format!("{b}: {a}"), - RegionResolutionError::UpperBoundUniverseConflict(a, _, _, _, b) => { - format!("{b}: {a}", a = ty::Region::new_var(tcx, a)) - } - RegionResolutionError::CannotNormalize(..) => unreachable!(), - }; - guar = Some( - struct_span_code_err!( - tcx.dcx(), - error.origin().span(), - E0367, - "`Drop` impl requires `{outlives}` \ - but the {self_descr} it is implemented for does not", - ) - .with_span_note(item_span, "the implementor must specify the same requirement") - .emit(), - ); - } - return Err(guar.unwrap()); - } - - Ok(()) -} diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 42d785c8dd0fe..9fd158ad154d8 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -1,10 +1,8 @@ -//! Type-checking for the rust-intrinsic intrinsics that the compiler exposes. +//! Type-checking for the `#[rustc_intrinsic]` intrinsics that the compiler exposes. use rustc_abi::ExternAbi; -use rustc_errors::codes::*; -use rustc_errors::{DiagMessage, struct_span_code_err}; -use rustc_hir::{self as hir, Safety}; -use rustc_middle::bug; +use rustc_errors::DiagMessage; +use rustc_hir::{self as hir, LangItem}; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; @@ -26,17 +24,10 @@ fn equate_intrinsic_type<'tcx>( sig: ty::PolyFnSig<'tcx>, ) { let (generics, span) = match tcx.hir_node_by_def_id(def_id) { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { generics, .. }, .. }) - | hir::Node::ForeignItem(hir::ForeignItem { - kind: hir::ForeignItemKind::Fn(_, _, generics), - .. - }) => (tcx.generics_of(def_id), generics.span), - _ => { - struct_span_code_err!(tcx.dcx(), span, E0622, "intrinsic must be a function") - .with_span_label(span, "expected a function") - .emit(); - return; + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { generics, .. }, .. }) => { + (tcx.generics_of(def_id), generics.span) } + _ => tcx.dcx().span_bug(span, "intrinsic must be a function"), }; let own_counts = generics.own_counts(); @@ -70,13 +61,7 @@ fn equate_intrinsic_type<'tcx>( } /// Returns the unsafety of the given intrinsic. -pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hir::Safety { - let has_safe_attr = if tcx.has_attr(intrinsic_id, sym::rustc_intrinsic) { - tcx.fn_sig(intrinsic_id).skip_binder().safety() - } else { - // Old-style intrinsics are never safe - Safety::Unsafe - }; +fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hir::Safety { let is_in_list = match tcx.item_name(intrinsic_id.into()) { // When adding a new intrinsic to this list, // it's usually worth updating that intrinsic's documentation @@ -118,10 +103,18 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::minnumf32 | sym::minnumf64 | sym::minnumf128 + | sym::minimumf16 + | sym::minimumf32 + | sym::minimumf64 + | sym::minimumf128 | sym::maxnumf16 | sym::maxnumf32 | sym::maxnumf64 | sym::maxnumf128 + | sym::maximumf16 + | sym::maximumf32 + | sym::maximumf64 + | sym::maximumf128 | sym::rustc_peek | sym::type_name | sym::forget @@ -148,7 +141,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - _ => hir::Safety::Unsafe, }; - if has_safe_attr != is_in_list { + if tcx.fn_sig(intrinsic_id).skip_binder().safety() != is_in_list { tcx.dcx().struct_span_err( tcx.def_span(intrinsic_id), DiagMessage::from(format!( @@ -163,12 +156,11 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - /// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`, /// and in `library/core/src/intrinsics.rs`. -pub fn check_intrinsic_type( +pub(crate) fn check_intrinsic_type( tcx: TyCtxt<'_>, intrinsic_id: LocalDefId, span: Span, intrinsic_name: Symbol, - abi: ExternAbi, ) { let generics = tcx.generics_of(intrinsic_id); let param = |n| { @@ -188,23 +180,22 @@ pub fn check_intrinsic_type( ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv), ]); let mk_va_list_ty = |mutbl| { - tcx.lang_items().va_list().map(|did| { - let region = ty::Region::new_bound( - tcx, - ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }, - ); - let env_region = ty::Region::new_bound( - tcx, - ty::INNERMOST, - ty::BoundRegion { - var: ty::BoundVar::from_u32(2), - kind: ty::BoundRegionKind::ClosureEnv, - }, - ); - let va_list_ty = tcx.type_of(did).instantiate(tcx, &[region.into()]); - (Ty::new_ref(tcx, env_region, va_list_ty, mutbl), va_list_ty) - }) + let did = tcx.require_lang_item(LangItem::VaList, Some(span)); + let region = ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }, + ); + let env_region = ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { + var: ty::BoundVar::from_u32(2), + kind: ty::BoundRegionKind::ClosureEnv, + }, + ); + let va_list_ty = tcx.type_of(did).instantiate(tcx, &[region.into()]); + (Ty::new_ref(tcx, env_region, va_list_ty, mutbl), va_list_ty) }; let (n_tps, n_lts, n_cts, inputs, output, safety) = if name_str.starts_with("atomic_") { @@ -232,15 +223,11 @@ pub fn check_intrinsic_type( }; (n_tps, 0, 0, inputs, output, hir::Safety::Unsafe) } else if intrinsic_name == sym::contract_check_ensures { - // contract_check_ensures::<'a, Ret, C>(&'a Ret, C) - // where C: impl Fn(&'a Ret) -> bool, + // contract_check_ensures::(Ret, C) -> Ret + // where C: for<'a> Fn(&'a Ret) -> bool, // - // so: two type params, one lifetime param, 0 const params, two inputs, no return - - let p = generics.param_at(0, tcx); - let r = ty::Region::new_early_param(tcx, p.to_early_bound_region_data()); - let ref_ret = Ty::new_imm_ref(tcx, r, param(1)); - (2, 1, 0, vec![ref_ret, param(2)], tcx.types.unit, hir::Safety::Safe) + // so: two type params, 0 lifetime param, 0 const params, two inputs, no return + (2, 0, 0, vec![param(0), param(1)], param(1), hir::Safety::Safe) } else { let safety = intrinsic_operation_unsafety(tcx, intrinsic_id); let (n_tps, n_cts, inputs, output) = match intrinsic_name { @@ -395,11 +382,21 @@ pub fn check_intrinsic_type( sym::minnumf64 => (0, 0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), sym::minnumf128 => (0, 0, vec![tcx.types.f128, tcx.types.f128], tcx.types.f128), + sym::minimumf16 => (0, 0, vec![tcx.types.f16, tcx.types.f16], tcx.types.f16), + sym::minimumf32 => (0, 0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), + sym::minimumf64 => (0, 0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), + sym::minimumf128 => (0, 0, vec![tcx.types.f128, tcx.types.f128], tcx.types.f128), + sym::maxnumf16 => (0, 0, vec![tcx.types.f16, tcx.types.f16], tcx.types.f16), sym::maxnumf32 => (0, 0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), sym::maxnumf64 => (0, 0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), sym::maxnumf128 => (0, 0, vec![tcx.types.f128, tcx.types.f128], tcx.types.f128), + sym::maximumf16 => (0, 0, vec![tcx.types.f16, tcx.types.f16], tcx.types.f16), + sym::maximumf32 => (0, 0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), + sym::maximumf64 => (0, 0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), + sym::maximumf128 => (0, 0, vec![tcx.types.f128, tcx.types.f128], tcx.types.f128), + sym::copysignf16 => (0, 0, vec![tcx.types.f16, tcx.types.f16], tcx.types.f16), sym::copysignf32 => (0, 0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), sym::copysignf64 => (0, 0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), @@ -567,23 +564,17 @@ pub fn check_intrinsic_type( ) } - sym::va_start | sym::va_end => match mk_va_list_ty(hir::Mutability::Mut) { - Some((va_list_ref_ty, _)) => (0, 0, vec![va_list_ref_ty], tcx.types.unit), - None => bug!("`va_list` lang item needed for C-variadic intrinsics"), - }, + sym::va_start | sym::va_end => { + (0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit) + } - sym::va_copy => match mk_va_list_ty(hir::Mutability::Not) { - Some((va_list_ref_ty, va_list_ty)) => { - let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty); - (0, 0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.types.unit) - } - None => bug!("`va_list` lang item needed for C-variadic intrinsics"), - }, + sym::va_copy => { + let (va_list_ref_ty, va_list_ty) = mk_va_list_ty(hir::Mutability::Not); + let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty); + (0, 0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.types.unit) + } - sym::va_arg => match mk_va_list_ty(hir::Mutability::Mut) { - Some((va_list_ref_ty, _)) => (1, 0, vec![va_list_ref_ty], param(0)), - None => bug!("`va_list` lang item needed for C-variadic intrinsics"), - }, + sym::va_arg => (1, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], param(0)), sym::nontemporal_store => { (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit) @@ -674,8 +665,12 @@ pub fn check_intrinsic_type( sym::simd_masked_load => (3, 0, vec![param(0), param(1), param(2)], param(2)), sym::simd_masked_store => (3, 0, vec![param(0), param(1), param(2)], tcx.types.unit), sym::simd_scatter => (3, 0, vec![param(0), param(1), param(2)], tcx.types.unit), - sym::simd_insert => (2, 0, vec![param(0), tcx.types.u32, param(1)], param(0)), - sym::simd_extract => (2, 0, vec![param(0), tcx.types.u32], param(1)), + sym::simd_insert | sym::simd_insert_dyn => { + (2, 0, vec![param(0), tcx.types.u32, param(1)], param(0)) + } + sym::simd_extract | sym::simd_extract_dyn => { + (2, 0, vec![param(0), tcx.types.u32], param(1)) + } sym::simd_cast | sym::simd_as | sym::simd_cast_ptr @@ -706,7 +701,7 @@ pub fn check_intrinsic_type( }; (n_tps, 0, n_cts, inputs, output, safety) }; - let sig = tcx.mk_fn_sig(inputs, output, false, safety, abi); + let sig = tcx.mk_fn_sig(inputs, output, false, safety, ExternAbi::Rust); let sig = ty::Binder::bind_with_vars(sig, bound_vars); equate_intrinsic_type(tcx, span, intrinsic_id, n_tps, n_lts, n_cts, sig) } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs deleted file mode 100644 index 590ade516ec64..0000000000000 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ /dev/null @@ -1,541 +0,0 @@ -use rustc_abi::FieldIdx; -use rustc_ast::InlineAsmTemplatePiece; -use rustc_data_structures::fx::FxIndexSet; -use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem}; -use rustc_middle::bug; -use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; -use rustc_session::lint; -use rustc_span::def_id::LocalDefId; -use rustc_span::{Symbol, sym}; -use rustc_target::asm::{ - InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo, -}; - -use crate::errors::RegisterTypeUnstable; - -pub struct InlineAsmCtxt<'a, 'tcx: 'a> { - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - target_features: &'tcx FxIndexSet, - expr_ty: Box) -> Ty<'tcx> + 'a>, - node_ty: Box Ty<'tcx> + 'a>, -} - -enum NonAsmTypeReason<'tcx> { - UnevaluatedSIMDArrayLength(DefId, ty::Const<'tcx>), - Invalid(Ty<'tcx>), - InvalidElement(DefId, Ty<'tcx>), - NotSizedPtr(Ty<'tcx>), -} - -impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { - pub fn new( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - typing_env: ty::TypingEnv<'tcx>, - expr_ty: impl Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a, - node_ty: impl Fn(hir::HirId) -> Ty<'tcx> + 'a, - ) -> Self { - InlineAsmCtxt { - tcx, - typing_env, - target_features: tcx.asm_target_features(def_id), - expr_ty: Box::new(expr_ty), - node_ty: Box::new(node_ty), - } - } - - fn expr_ty(&self, expr: &hir::Expr<'tcx>) -> Ty<'tcx> { - (self.expr_ty)(expr) - } - - fn node_ty(&self, hir_id: hir::HirId) -> Ty<'tcx> { - (self.node_ty)(hir_id) - } - - // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()` - fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool { - // Type still may have region variables, but `Sized` does not depend - // on those, so just erase them before querying. - if ty.is_sized(self.tcx, self.typing_env) { - return true; - } - if let ty::Foreign(..) = ty.kind() { - return true; - } - false - } - - fn get_asm_ty(&self, ty: Ty<'tcx>) -> Result> { - let asm_ty_isize = match self.tcx.sess.target.pointer_width { - 16 => InlineAsmType::I16, - 32 => InlineAsmType::I32, - 64 => InlineAsmType::I64, - width => bug!("unsupported pointer width: {width}"), - }; - - match *ty.kind() { - ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::I8), - ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::I16), - ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::I32), - ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::I64), - ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Ok(InlineAsmType::I128), - ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Ok(asm_ty_isize), - ty::Float(FloatTy::F16) => Ok(InlineAsmType::F16), - ty::Float(FloatTy::F32) => Ok(InlineAsmType::F32), - ty::Float(FloatTy::F64) => Ok(InlineAsmType::F64), - ty::Float(FloatTy::F128) => Ok(InlineAsmType::F128), - ty::FnPtr(..) => Ok(asm_ty_isize), - ty::RawPtr(elem_ty, _) => { - if self.is_thin_ptr_ty(elem_ty) { - Ok(asm_ty_isize) - } else { - Err(NonAsmTypeReason::NotSizedPtr(ty)) - } - } - ty::Adt(adt, args) if adt.repr().simd() => { - let fields = &adt.non_enum_variant().fields; - let field = &fields[FieldIdx::ZERO]; - let elem_ty = field.ty(self.tcx, args); - - let (size, ty) = match elem_ty.kind() { - ty::Array(ty, len) => { - let len = self.tcx.normalize_erasing_regions(self.typing_env, *len); - if let Some(len) = len.try_to_target_usize(self.tcx) { - (len, *ty) - } else { - return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength( - field.did, len, - )); - } - } - _ => (fields.len() as u64, elem_ty), - }; - - match ty.kind() { - ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::VecI8(size)), - ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::VecI16(size)), - ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::VecI32(size)), - ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::VecI64(size)), - ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => { - Ok(InlineAsmType::VecI128(size)) - } - ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => { - Ok(match self.tcx.sess.target.pointer_width { - 16 => InlineAsmType::VecI16(size), - 32 => InlineAsmType::VecI32(size), - 64 => InlineAsmType::VecI64(size), - width => bug!("unsupported pointer width: {width}"), - }) - } - ty::Float(FloatTy::F16) => Ok(InlineAsmType::VecF16(size)), - ty::Float(FloatTy::F32) => Ok(InlineAsmType::VecF32(size)), - ty::Float(FloatTy::F64) => Ok(InlineAsmType::VecF64(size)), - ty::Float(FloatTy::F128) => Ok(InlineAsmType::VecF128(size)), - _ => Err(NonAsmTypeReason::InvalidElement(field.did, ty)), - } - } - ty::Infer(_) => bug!("unexpected infer ty in asm operand"), - _ => Err(NonAsmTypeReason::Invalid(ty)), - } - } - - fn check_asm_operand_type( - &self, - idx: usize, - reg: InlineAsmRegOrRegClass, - expr: &'tcx hir::Expr<'tcx>, - template: &[InlineAsmTemplatePiece], - is_input: bool, - tied_input: Option<(&'tcx hir::Expr<'tcx>, Option)>, - ) -> Option { - let ty = self.expr_ty(expr); - if ty.has_non_region_infer() { - bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty); - } - - let asm_ty = match *ty.kind() { - // `!` is allowed for input but not for output (issue #87802) - ty::Never if is_input => return None, - _ if ty.references_error() => return None, - ty::Adt(adt, args) if self.tcx.is_lang_item(adt.did(), LangItem::MaybeUninit) => { - let fields = &adt.non_enum_variant().fields; - let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx, args); - // FIXME: Are we just trying to map to the `T` in `MaybeUninit`? - // If so, just get it from the args. - let ty::Adt(ty, args) = ty.kind() else { - unreachable!("expected first field of `MaybeUninit` to be an ADT") - }; - assert!( - ty.is_manually_drop(), - "expected first field of `MaybeUninit` to be `ManuallyDrop`" - ); - let fields = &ty.non_enum_variant().fields; - let ty = fields[FieldIdx::ZERO].ty(self.tcx, args); - self.get_asm_ty(ty) - } - _ => self.get_asm_ty(ty), - }; - let asm_ty = match asm_ty { - Ok(asm_ty) => asm_ty, - Err(reason) => { - match reason { - NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => { - let msg = format!("cannot evaluate SIMD vector length `{len}`"); - self.tcx - .dcx() - .struct_span_err(self.tcx.def_span(did), msg) - .with_span_note( - expr.span, - "SIMD vector length needs to be known statically for use in `asm!`", - ) - .emit(); - } - NonAsmTypeReason::Invalid(ty) => { - let msg = format!("cannot use value of type `{ty}` for inline assembly"); - self.tcx.dcx().struct_span_err(expr.span, msg).with_note( - "only integers, floats, SIMD vectors, pointers and function pointers \ - can be used as arguments for inline assembly", - ).emit(); - } - NonAsmTypeReason::NotSizedPtr(ty) => { - let msg = format!( - "cannot use value of unsized pointer type `{ty}` for inline assembly" - ); - self.tcx - .dcx() - .struct_span_err(expr.span, msg) - .with_note("only sized pointers can be used in inline assembly") - .emit(); - } - NonAsmTypeReason::InvalidElement(did, ty) => { - let msg = format!( - "cannot use SIMD vector with element type `{ty}` for inline assembly" - ); - self.tcx.dcx() - .struct_span_err(self.tcx.def_span(did), msg).with_span_note( - expr.span, - "only integers, floats, SIMD vectors, pointers and function pointers \ - can be used as arguments for inline assembly", - ).emit(); - } - } - return None; - } - }; - - // Check that the type implements Copy. The only case where this can - // possibly fail is for SIMD types which don't #[derive(Copy)]. - if !self.tcx.type_is_copy_modulo_regions(self.typing_env, ty) { - let msg = "arguments for inline assembly must be copyable"; - self.tcx - .dcx() - .struct_span_err(expr.span, msg) - .with_note(format!("`{ty}` does not implement the Copy trait")) - .emit(); - } - - // Ideally we wouldn't need to do this, but LLVM's register allocator - // really doesn't like it when tied operands have different types. - // - // This is purely an LLVM limitation, but we have to live with it since - // there is no way to hide this with implicit conversions. - // - // For the purposes of this check we only look at the `InlineAsmType`, - // which means that pointers and integers are treated as identical (modulo - // size). - if let Some((in_expr, Some(in_asm_ty))) = tied_input { - if in_asm_ty != asm_ty { - let msg = "incompatible types for asm inout argument"; - let in_expr_ty = self.expr_ty(in_expr); - self.tcx - .dcx() - .struct_span_err(vec![in_expr.span, expr.span], msg) - .with_span_label(in_expr.span, format!("type `{in_expr_ty}`")) - .with_span_label(expr.span, format!("type `{ty}`")) - .with_note( - "asm inout arguments must have the same type, \ - unless they are both pointers or integers of the same size", - ) - .emit(); - } - - // All of the later checks have already been done on the input, so - // let's not emit errors and warnings twice. - return Some(asm_ty); - } - - // Check the type against the list of types supported by the selected - // register class. - let asm_arch = self.tcx.sess.asm_arch.unwrap(); - let allow_experimental_reg = self.tcx.features().asm_experimental_reg(); - let reg_class = reg.reg_class(); - let supported_tys = reg_class.supported_types(asm_arch, allow_experimental_reg); - let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else { - let mut err = if !allow_experimental_reg - && reg_class.supported_types(asm_arch, true).iter().any(|&(t, _)| t == asm_ty) - { - self.tcx.sess.create_feature_err( - RegisterTypeUnstable { span: expr.span, ty }, - sym::asm_experimental_reg, - ) - } else { - let msg = format!("type `{ty}` cannot be used with this register class"); - let mut err = self.tcx.dcx().struct_span_err(expr.span, msg); - let supported_tys: Vec<_> = - supported_tys.iter().map(|(t, _)| t.to_string()).collect(); - err.note(format!( - "register class `{}` supports these types: {}", - reg_class.name(), - supported_tys.join(", "), - )); - err - }; - if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) { - err.help(format!("consider using the `{}` register class instead", suggest.name())); - } - err.emit(); - return Some(asm_ty); - }; - - // Check whether the selected type requires a target feature. Note that - // this is different from the feature check we did earlier. While the - // previous check checked that this register class is usable at all - // with the currently enabled features, some types may only be usable - // with a register class when a certain feature is enabled. We check - // this here since it depends on the results of typeck. - // - // Also note that this check isn't run when the operand type is never - // (!). In that case we still need the earlier check to verify that the - // register class is usable at all. - if let Some(feature) = feature { - if !self.target_features.contains(feature) { - let msg = format!("`{feature}` target feature is not enabled"); - self.tcx - .dcx() - .struct_span_err(expr.span, msg) - .with_note(format!( - "this is required to use type `{}` with register class `{}`", - ty, - reg_class.name(), - )) - .emit(); - return Some(asm_ty); - } - } - - // Check whether a modifier is suggested for using this type. - if let Some(ModifierInfo { - modifier: suggested_modifier, - result: suggested_result, - size: suggested_size, - }) = reg_class.suggest_modifier(asm_arch, asm_ty) - { - // Search for any use of this operand without a modifier and emit - // the suggestion for them. - let mut spans = vec![]; - for piece in template { - if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece - { - if operand_idx == idx && modifier.is_none() { - spans.push(span); - } - } - } - if !spans.is_empty() { - let ModifierInfo { - modifier: default_modifier, - result: default_result, - size: default_size, - } = reg_class.default_modifier(asm_arch).unwrap(); - self.tcx.node_span_lint( - lint::builtin::ASM_SUB_REGISTER, - expr.hir_id, - spans, - |lint| { - lint.primary_message("formatting may not be suitable for sub-register argument"); - lint.span_label(expr.span, "for this argument"); - lint.help(format!( - "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)", - )); - lint.help(format!( - "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)", - )); - }, - ); - } - } - - Some(asm_ty) - } - - pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) { - let Some(asm_arch) = self.tcx.sess.asm_arch else { - self.tcx.dcx().delayed_bug("target architecture does not support asm"); - return; - }; - let allow_experimental_reg = self.tcx.features().asm_experimental_reg(); - for (idx, &(op, op_sp)) in asm.operands.iter().enumerate() { - // Validate register classes against currently enabled target - // features. We check that at least one type is available for - // the enabled features. - // - // We ignore target feature requirements for clobbers: if the - // feature is disabled then the compiler doesn't care what we - // do with the registers. - // - // Note that this is only possible for explicit register - // operands, which cannot be used in the asm string. - if let Some(reg) = op.reg() { - // Some explicit registers cannot be used depending on the - // target. Reject those here. - if let InlineAsmRegOrRegClass::Reg(reg) = reg { - if let InlineAsmReg::Err = reg { - // `validate` will panic on `Err`, as an error must - // already have been reported. - continue; - } - if let Err(msg) = reg.validate( - asm_arch, - self.tcx.sess.relocation_model(), - self.target_features, - &self.tcx.sess.target, - op.is_clobber(), - ) { - let msg = format!("cannot use register `{}`: {}", reg.name(), msg); - self.tcx.dcx().span_err(op_sp, msg); - continue; - } - } - - if !op.is_clobber() { - let mut missing_required_features = vec![]; - let reg_class = reg.reg_class(); - if let InlineAsmRegClass::Err = reg_class { - continue; - } - for &(_, feature) in reg_class.supported_types(asm_arch, allow_experimental_reg) - { - match feature { - Some(feature) => { - if self.target_features.contains(&feature) { - missing_required_features.clear(); - break; - } else { - missing_required_features.push(feature); - } - } - None => { - missing_required_features.clear(); - break; - } - } - } - - // We are sorting primitive strs here and can use unstable sort here - missing_required_features.sort_unstable(); - missing_required_features.dedup(); - match &missing_required_features[..] { - [] => {} - [feature] => { - let msg = format!( - "register class `{}` requires the `{}` target feature", - reg_class.name(), - feature - ); - self.tcx.dcx().span_err(op_sp, msg); - // register isn't enabled, don't do more checks - continue; - } - features => { - let msg = format!( - "register class `{}` requires at least one of the following target features: {}", - reg_class.name(), - features - .iter() - .map(|f| f.as_str()) - .intersperse(", ") - .collect::(), - ); - self.tcx.dcx().span_err(op_sp, msg); - // register isn't enabled, don't do more checks - continue; - } - } - } - } - - match op { - hir::InlineAsmOperand::In { reg, expr } => { - self.check_asm_operand_type(idx, reg, expr, asm.template, true, None); - } - hir::InlineAsmOperand::Out { reg, late: _, expr } => { - if let Some(expr) = expr { - self.check_asm_operand_type(idx, reg, expr, asm.template, false, None); - } - } - hir::InlineAsmOperand::InOut { reg, late: _, expr } => { - self.check_asm_operand_type(idx, reg, expr, asm.template, false, None); - } - hir::InlineAsmOperand::SplitInOut { reg, late: _, in_expr, out_expr } => { - let in_ty = - self.check_asm_operand_type(idx, reg, in_expr, asm.template, true, None); - if let Some(out_expr) = out_expr { - self.check_asm_operand_type( - idx, - reg, - out_expr, - asm.template, - false, - Some((in_expr, in_ty)), - ); - } - } - hir::InlineAsmOperand::Const { anon_const } => { - let ty = self.node_ty(anon_const.hir_id); - match ty.kind() { - ty::Error(_) => {} - _ if ty.is_integral() => {} - _ => { - self.tcx - .dcx() - .struct_span_err(op_sp, "invalid type for `const` operand") - .with_span_label( - self.tcx.def_span(anon_const.def_id), - format!("is {} `{}`", ty.kind().article(), ty), - ) - .with_help("`const` operands must be of an integer type") - .emit(); - } - } - } - // Typeck has checked that SymFn refers to a function. - hir::InlineAsmOperand::SymFn { expr } => { - let ty = self.expr_ty(expr); - match ty.kind() { - ty::FnDef(..) => {} - ty::Error(_) => {} - _ => { - self.tcx - .dcx() - .struct_span_err(op_sp, "invalid `sym` operand") - .with_span_label( - expr.span, - format!("is {} `{}`", ty.kind().article(), ty), - ) - .with_help( - "`sym` operands must refer to either a function or a static", - ) - .emit(); - } - } - } - // AST lowering guarantees that SymStatic points to a static. - hir::InlineAsmOperand::SymStatic { .. } => {} - // No special checking is needed for labels. - hir::InlineAsmOperand::Label { .. } => {} - } - } - } -} diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 1c5455710db8a..fad8abf5fae85 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -62,12 +62,11 @@ a type parameter). */ +pub mod always_applicable; mod check; mod compare_impl_item; -pub mod dropck; mod entry; pub mod intrinsic; -pub mod intrinsicck; mod region; pub mod wfcheck; @@ -84,6 +83,7 @@ use rustc_infer::infer::{self, TyCtxtInferExt as _}; use rustc_infer::traits::ObligationCause; use rustc_middle::query::Providers; use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::print::with_types_for_signature; use rustc_middle::ty::{self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypingMode}; use rustc_middle::{bug, span_bug}; use rustc_session::parse::feature_err; @@ -113,11 +113,11 @@ pub fn provide(providers: &mut Providers) { } fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - tcx.calculate_dtor(def_id.to_def_id(), dropck::check_drop_impl) + tcx.calculate_dtor(def_id, always_applicable::check_drop_impl) } fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - tcx.calculate_async_dtor(def_id.to_def_id(), dropck::check_drop_impl) + tcx.calculate_async_dtor(def_id, always_applicable::check_drop_impl) } /// Given a `DefId` for an opaque type in return position, find its parent item's return @@ -136,26 +136,16 @@ fn get_owner_return_paths( }) } -/// Forbid defining intrinsics in Rust code, -/// as they must always be defined by the compiler. -// FIXME: Move this to a more appropriate place. -pub fn forbid_intrinsic_abi(tcx: TyCtxt<'_>, sp: Span, abi: ExternAbi) { - if let ExternAbi::RustIntrinsic = abi { - tcx.dcx().span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block"); - } -} - -fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { +pub(super) fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { // Only restricted on wasm target for now if !tcx.sess.target.is_like_wasm { return; } // If `#[link_section]` is missing, then nothing to verify - let attrs = tcx.codegen_fn_attrs(id); - if attrs.link_section.is_none() { + let Some(link_section) = tcx.codegen_fn_attrs(id).link_section else { return; - } + }; // For the wasm32 target statics with `#[link_section]` other than `.init_array` // are placed into custom sections of the final output file, but this isn't like @@ -181,11 +171,8 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { // continue to work, but would no longer be necessary. if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id()) - && alloc.inner().provenance().ptrs().len() != 0 - && attrs - .link_section - .map(|link_section| !link_section.as_str().starts_with(".init_array")) - .unwrap() + && !alloc.inner().provenance().ptrs().is_empty() + && !link_section.as_str().starts_with(".init_array") { let msg = "statics with a custom `#[link_section]` must be a \ simple list of bytes on the wasm target with no \ @@ -217,7 +204,7 @@ fn missing_items_err( let missing_items_msg = missing_items .clone() - .map(|trait_item| trait_item.name.to_string()) + .map(|trait_item| trait_item.name().to_string()) .collect::>() .join("`, `"); @@ -240,15 +227,15 @@ fn missing_items_err( (Vec::new(), Vec::new(), Vec::new()); for &trait_item in missing_items { - let snippet = suggestion_signature( + let snippet = with_types_for_signature!(suggestion_signature( tcx, trait_item, tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(), - ); + )); let code = format!("{padding}{snippet}\n{padding}"); - if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) { + if let Some(span) = tcx.hir_span_if_local(trait_item.def_id) { missing_trait_item_label - .push(errors::MissingTraitItemLabel { span, item: trait_item.name }); + .push(errors::MissingTraitItemLabel { span, item: trait_item.name() }); missing_trait_item.push(errors::MissingTraitItemSuggestion { span: sugg_sp, code, @@ -344,9 +331,8 @@ fn bounds_from_generic_predicates<'tcx>( ty::ClauseKind::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. + if !tcx.is_default_trait(def_id) { + // Do not add that restriction to the list if it is a positive requirement. entry.push(trait_predicate.def_id()); } } @@ -420,14 +406,14 @@ fn fn_sig_suggestion<'tcx>( .enumerate() .map(|(i, ty)| { Some(match ty.kind() { - ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(), + ty::Param(_) if assoc.is_method() && i == 0 => "self".to_string(), ty::Ref(reg, ref_ty, mutability) if i == 0 => { let reg = format!("{reg} "); let reg = match ®[..] { "'_ " | " " => "", reg => reg, }; - if assoc.fn_has_self_parameter { + if assoc.is_method() { match ref_ty.kind() { ty::Param(param) if param.name == kw::SelfUpper => { format!("&{}{}self", reg, mutability.prefix_str()) @@ -440,7 +426,7 @@ fn fn_sig_suggestion<'tcx>( } } _ => { - if assoc.fn_has_self_parameter && i == 0 { + if assoc.is_method() && i == 0 { format!("self: {ty}") } else { format!("_: {ty}") @@ -502,7 +488,7 @@ fn suggestion_signature<'tcx>( ); match assoc.kind { - ty::AssocKind::Fn => fn_sig_suggestion( + ty::AssocKind::Fn { .. } => fn_sig_suggestion( tcx, tcx.liberate_late_bound_regions( assoc.def_id, @@ -512,14 +498,14 @@ fn suggestion_signature<'tcx>( tcx.predicates_of(assoc.def_id).instantiate_own(tcx, args), assoc, ), - ty::AssocKind::Type => { + ty::AssocKind::Type { .. } => { let (generics, where_clauses) = bounds_from_generic_predicates( tcx, tcx.predicates_of(assoc.def_id).instantiate_own(tcx, args), ); - format!("type {}{generics} = /* Type */{where_clauses};", assoc.name) + format!("type {}{generics} = /* Type */{where_clauses};", assoc.name()) } - ty::AssocKind::Const => { + ty::AssocKind::Const { name } => { let ty = tcx.type_of(assoc.def_id).instantiate_identity(); let val = tcx .infer_ctxt() @@ -527,7 +513,7 @@ fn suggestion_signature<'tcx>( .err_ctxt() .ty_kind_suggestion(tcx.param_env(assoc.def_id), ty) .unwrap_or_else(|| "value".to_string()); - format!("const {}: {} = {};", assoc.name, ty, val) + format!("const {}: {} = {};", name, ty, val) } } } @@ -537,7 +523,7 @@ fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, d let variant_spans: Vec<_> = adt .variants() .iter() - .map(|variant| tcx.hir().span_if_local(variant.def_id).unwrap()) + .map(|variant| tcx.hir_span_if_local(variant.def_id).unwrap()) .collect(); let (mut spans, mut many) = (Vec::new(), None); if let [start @ .., end] = &*variant_spans { diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 255f5fee52a80..7cb31a64e13e6 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -8,7 +8,7 @@ use std::mem; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; @@ -23,12 +23,11 @@ use tracing::debug; #[derive(Debug, Copy, Clone)] struct Context { - /// The scope that contains any new variables declared, plus its depth in - /// the scope tree. - var_parent: Option<(Scope, ScopeDepth)>, + /// The scope that contains any new variables declared. + var_parent: Option, - /// Region parent of expressions, etc., plus its depth in the scope tree. - parent: Option<(Scope, ScopeDepth)>, + /// Region parent of expressions, etc. + parent: Option, } struct ScopeResolutionVisitor<'tcx> { @@ -47,27 +46,7 @@ struct ScopeResolutionVisitor<'tcx> { cx: Context, - /// `terminating_scopes` is a set containing the ids of each - /// statement, or conditional/repeating expression. These scopes - /// are calling "terminating scopes" because, when attempting to - /// find the scope of a temporary, by default we search up the - /// enclosing scopes until we encounter the terminating scope. A - /// conditional/repeating expression is one which is not - /// guaranteed to execute exactly once upon entering the parent - /// scope. This could be because the expression only executes - /// conditionally, such as the expression `b` in `a && b`, or - /// because the expression may execute many times, such as a loop - /// body. The reason that we distinguish such expressions is that, - /// upon exiting the parent scope, we cannot statically know how - /// many times the expression executed, and thus if the expression - /// creates temporaries we cannot know statically how many such - /// temporaries we would have to cleanup. Therefore, we ensure that - /// the temporaries never outlast the conditional/repeating - /// expression, preventing the need for dynamic checks and/or - /// arbitrary amounts of stack space. Terminating scopes end - /// up being contained in a DestructionScope that contains the - /// destructor's execution. - terminating_scopes: FxHashSet, + extended_super_lets: FxHashMap>, } /// Records the lifetime of a local variable as `cx.var_parent` @@ -78,11 +57,15 @@ fn record_var_lifetime(visitor: &mut ScopeResolutionVisitor<'_>, var_id: hir::It // // extern fn isalnum(c: c_int) -> c_int } - Some((parent_scope, _)) => visitor.scope_tree.record_var_scope(var_id, parent_scope), + Some(parent_scope) => visitor.scope_tree.record_var_scope(var_id, parent_scope), } } -fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hir::Block<'tcx>) { +fn resolve_block<'tcx>( + visitor: &mut ScopeResolutionVisitor<'tcx>, + blk: &'tcx hir::Block<'tcx>, + terminating: bool, +) { debug!("resolve_block(blk.hir_id={:?})", blk.hir_id); let prev_cx = visitor.cx; @@ -112,7 +95,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi // `other_argument()` has run and also the call to `quux(..)` // itself has returned. - visitor.enter_node_scope_with_dtor(blk.hir_id.local_id); + visitor.enter_node_scope_with_dtor(blk.hir_id.local_id, terminating); visitor.cx.var_parent = visitor.cx.parent; { @@ -141,8 +124,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi // the sequence of visits agree with the order in the default // `hir::intravisit` visitor. mem::swap(&mut prev_cx, &mut visitor.cx); - visitor.terminating_scopes.insert(els.hir_id.local_id); - visitor.visit_block(els); + resolve_block(visitor, els, true); // From now on, we continue normally. visitor.cx = prev_cx; } @@ -170,12 +152,12 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi if let Some(tail_expr) = blk.expr { let local_id = tail_expr.hir_id.local_id; let edition = blk.span.edition(); - if edition.at_least_rust_2024() { - visitor.terminating_scopes.insert(local_id); - } else if !visitor - .tcx - .lints_that_dont_need_to_run(()) - .contains(&lint::LintId::of(lint::builtin::TAIL_EXPR_DROP_ORDER)) + let terminating = edition.at_least_rust_2024(); + if !terminating + && !visitor + .tcx + .lints_that_dont_need_to_run(()) + .contains(&lint::LintId::of(lint::builtin::TAIL_EXPR_DROP_ORDER)) { // If this temporary scope will be changing once the codebase adopts Rust 2024, // and we are linting about possible semantic changes that would result, @@ -186,7 +168,7 @@ fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hi .backwards_incompatible_scope .insert(local_id, Scope { local_id, data: ScopeData::Node }); } - visitor.visit_expr(tail_expr); + resolve_expr(visitor, tail_expr, terminating); } } @@ -204,25 +186,19 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir: let prev_cx = visitor.cx; - visitor.terminating_scopes.insert(arm.hir_id.local_id); - - visitor.enter_node_scope_with_dtor(arm.hir_id.local_id); + visitor.enter_node_scope_with_dtor(arm.hir_id.local_id, true); visitor.cx.var_parent = visitor.cx.parent; - if let Some(expr) = arm.guard - && !has_let_expr(expr) - { - visitor.terminating_scopes.insert(expr.hir_id.local_id); + resolve_pat(visitor, arm.pat); + if let Some(guard) = arm.guard { + resolve_expr(visitor, guard, !has_let_expr(guard)); } - - intravisit::walk_arm(visitor, arm); + resolve_expr(visitor, arm.body, false); visitor.cx = prev_cx; } fn resolve_pat<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) { - visitor.record_child_scope(Scope { local_id: pat.hir_id.local_id, data: ScopeData::Node }); - // If this is a binding then record the lifetime of that binding. if let PatKind::Binding(..) = pat.kind { record_var_lifetime(visitor, pat.hir_id.local_id); @@ -241,131 +217,40 @@ fn resolve_stmt<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, stmt: &'tcx hi let stmt_id = stmt.hir_id.local_id; debug!("resolve_stmt(stmt.id={:?})", stmt_id); - // Every statement will clean up the temporaries created during - // execution of that statement. Therefore each statement has an - // associated destruction scope that represents the scope of the - // statement plus its destructors, and thus the scope for which - // regions referenced by the destructors need to survive. - visitor.terminating_scopes.insert(stmt_id); + if let hir::StmtKind::Let(LetStmt { super_: Some(_), .. }) = stmt.kind { + // `super let` statement does not start a new scope, such that + // + // { super let x = identity(&temp()); &x }.method(); + // + // behaves exactly as + // + // (&identity(&temp()).method(); + intravisit::walk_stmt(visitor, stmt); + } else { + // Every statement will clean up the temporaries created during + // execution of that statement. Therefore each statement has an + // associated destruction scope that represents the scope of the + // statement plus its destructors, and thus the scope for which + // regions referenced by the destructors need to survive. - let prev_parent = visitor.cx.parent; - visitor.enter_node_scope_with_dtor(stmt_id); + let prev_parent = visitor.cx.parent; + visitor.enter_node_scope_with_dtor(stmt_id, true); - intravisit::walk_stmt(visitor, stmt); + intravisit::walk_stmt(visitor, stmt); - visitor.cx.parent = prev_parent; + visitor.cx.parent = prev_parent; + } } -fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) { +fn resolve_expr<'tcx>( + visitor: &mut ScopeResolutionVisitor<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + terminating: bool, +) { debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr); let prev_cx = visitor.cx; - visitor.enter_node_scope_with_dtor(expr.hir_id.local_id); - - { - let terminating_scopes = &mut visitor.terminating_scopes; - let mut terminating = |id: hir::ItemLocalId| { - terminating_scopes.insert(id); - }; - match expr.kind { - // Conditional or repeating scopes are always terminating - // scopes, meaning that temporaries cannot outlive them. - // This ensures fixed size stacks. - hir::ExprKind::Binary( - source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, - l, - r, - ) => { - // expr is a short circuiting operator (|| or &&). As its - // functionality can't be overridden by traits, it always - // processes bool sub-expressions. bools are Copy and thus we - // can drop any temporaries in evaluation (read) order - // (with the exception of potentially failing let expressions). - // We achieve this by enclosing the operands in a terminating - // scope, both the LHS and the RHS. - - // We optimize this a little in the presence of chains. - // Chains like a && b && c get lowered to AND(AND(a, b), c). - // In here, b and c are RHS, while a is the only LHS operand in - // that chain. This holds true for longer chains as well: the - // leading operand is always the only LHS operand that is not a - // binop itself. Putting a binop like AND(a, b) into a - // terminating scope is not useful, thus we only put the LHS - // into a terminating scope if it is not a binop. - - let terminate_lhs = match l.kind { - // let expressions can create temporaries that live on - hir::ExprKind::Let(_) => false, - // binops already drop their temporaries, so there is no - // need to put them into a terminating scope. - // This is purely an optimization to reduce the number of - // terminating scopes. - hir::ExprKind::Binary( - source_map::Spanned { - node: hir::BinOpKind::And | hir::BinOpKind::Or, .. - }, - .., - ) => false, - // otherwise: mark it as terminating - _ => true, - }; - if terminate_lhs { - terminating(l.hir_id.local_id); - } - - // `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries - // should live beyond the immediate expression - if !matches!(r.kind, hir::ExprKind::Let(_)) { - terminating(r.hir_id.local_id); - } - } - hir::ExprKind::If(_, then, Some(otherwise)) => { - terminating(then.hir_id.local_id); - terminating(otherwise.hir_id.local_id); - } - - hir::ExprKind::If(_, then, None) => { - terminating(then.hir_id.local_id); - } - - hir::ExprKind::Loop(body, _, _, _) => { - terminating(body.hir_id.local_id); - } - - hir::ExprKind::DropTemps(expr) => { - // `DropTemps(expr)` does not denote a conditional scope. - // Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`. - terminating(expr.hir_id.local_id); - } - - hir::ExprKind::AssignOp(..) - | hir::ExprKind::Index(..) - | hir::ExprKind::Unary(..) - | hir::ExprKind::Call(..) - | hir::ExprKind::MethodCall(..) => { - // FIXME(https://github.com/rust-lang/rfcs/issues/811) Nested method calls - // - // The lifetimes for a call or method call look as follows: - // - // call.id - // - arg0.id - // - ... - // - argN.id - // - call.callee_id - // - // The idea is that call.callee_id represents *the time when - // the invoked function is actually running* and call.id - // represents *the time to prepare the arguments and make the - // call*. See the section "Borrows in Calls" borrowck/README.md - // for an extended explanation of why this distinction is - // important. - // - // record_superlifetime(new_cx, expr.callee_id); - } - - _ => {} - } - } + visitor.enter_node_scope_with_dtor(expr.hir_id.local_id, terminating); let prev_pessimistic = visitor.pessimistic_yield; @@ -420,6 +305,53 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi // properly, we can't miss any types. match expr.kind { + // Conditional or repeating scopes are always terminating + // scopes, meaning that temporaries cannot outlive them. + // This ensures fixed size stacks. + hir::ExprKind::Binary( + source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + left, + right, + ) => { + // expr is a short circuiting operator (|| or &&). As its + // functionality can't be overridden by traits, it always + // processes bool sub-expressions. bools are Copy and thus we + // can drop any temporaries in evaluation (read) order + // (with the exception of potentially failing let expressions). + // We achieve this by enclosing the operands in a terminating + // scope, both the LHS and the RHS. + + // We optimize this a little in the presence of chains. + // Chains like a && b && c get lowered to AND(AND(a, b), c). + // In here, b and c are RHS, while a is the only LHS operand in + // that chain. This holds true for longer chains as well: the + // leading operand is always the only LHS operand that is not a + // binop itself. Putting a binop like AND(a, b) into a + // terminating scope is not useful, thus we only put the LHS + // into a terminating scope if it is not a binop. + + let terminate_lhs = match left.kind { + // let expressions can create temporaries that live on + hir::ExprKind::Let(_) => false, + // binops already drop their temporaries, so there is no + // need to put them into a terminating scope. + // This is purely an optimization to reduce the number of + // terminating scopes. + hir::ExprKind::Binary( + source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + .., + ) => false, + // otherwise: mark it as terminating + _ => true, + }; + + // `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries + // should live beyond the immediate expression + let terminate_rhs = !matches!(right.kind, hir::ExprKind::Let(_)); + + resolve_expr(visitor, left, terminate_lhs); + resolve_expr(visitor, right, terminate_rhs); + } // Manually recurse over closures, because they are nested bodies // that share the parent environment. We handle const blocks in // `visit_inline_const`. @@ -488,9 +420,9 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data }); visitor.cx.var_parent = visitor.cx.parent; visitor.visit_expr(cond); - visitor.visit_expr(then); + resolve_expr(visitor, then, true); visitor.cx = expr_cx; - visitor.visit_expr(otherwise); + resolve_expr(visitor, otherwise, true); } hir::ExprKind::If(cond, then, None) => { @@ -503,10 +435,20 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data }); visitor.cx.var_parent = visitor.cx.parent; visitor.visit_expr(cond); - visitor.visit_expr(then); + resolve_expr(visitor, then, true); visitor.cx = expr_cx; } + hir::ExprKind::Loop(body, _, _, _) => { + resolve_block(visitor, body, true); + } + + hir::ExprKind::DropTemps(expr) => { + // `DropTemps(expr)` does not denote a conditional scope. + // Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`. + resolve_expr(visitor, expr, true); + } + _ => intravisit::walk_expr(visitor, expr), } @@ -518,14 +460,11 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi // Mark this expr's scope and all parent scopes as containing `yield`. let mut scope = Scope { local_id: expr.hir_id.local_id, data: ScopeData::Node }; loop { - let span = match expr.kind { - hir::ExprKind::Yield(expr, hir::YieldSource::Await { .. }) => { - expr.span.shrink_to_hi().to(expr.span) - } - _ => expr.span, + let data = YieldData { + span: expr.span, + expr_and_pat_count: visitor.expr_and_pat_count, + source: *source, }; - let data = - YieldData { span, expr_and_pat_count: visitor.expr_and_pat_count, source: *source }; match visitor.scope_tree.yield_in_scope.get_mut(&scope) { Some(yields) => yields.push(data), None => { @@ -541,7 +480,7 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi // Keep traversing up while we can. match visitor.scope_tree.parent_map.get(&scope) { // Don't cross from closure bodies to their parent. - Some(&(superscope, _)) => match superscope.data { + Some(&superscope) => match superscope.data { ScopeData::CallSite => break, _ => scope = superscope, }, @@ -553,14 +492,19 @@ fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hi visitor.cx = prev_cx; } +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum LetKind { + Regular, + Super, +} + fn resolve_local<'tcx>( visitor: &mut ScopeResolutionVisitor<'tcx>, pat: Option<&'tcx hir::Pat<'tcx>>, init: Option<&'tcx hir::Expr<'tcx>>, + let_kind: LetKind, ) { - debug!("resolve_local(pat={:?}, init={:?})", pat, init); - - let blk_scope = visitor.cx.var_parent.map(|(p, _)| p); + debug!("resolve_local(pat={:?}, init={:?}, let_kind={:?})", pat, init, let_kind); // As an exception to the normal rules governing temporary // lifetimes, initializers in a let have a temporary lifetime @@ -618,16 +562,49 @@ fn resolve_local<'tcx>( // A, but the inner rvalues `a()` and `b()` have an extended lifetime // due to rule C. + if let_kind == LetKind::Super { + if let Some(scope) = visitor.extended_super_lets.remove(&pat.unwrap().hir_id.local_id) { + // This expression was lifetime-extended by a parent let binding. E.g. + // + // let a = { + // super let b = temp(); + // &b + // }; + // + // (Which needs to behave exactly as: let a = &temp();) + // + // Processing of `let a` will have already decided to extend the lifetime of this + // `super let` to its own var_scope. We use that scope. + visitor.cx.var_parent = scope; + } else { + // This `super let` is not subject to lifetime extension from a parent let binding. E.g. + // + // identity({ super let x = temp(); &x }).method(); + // + // (Which needs to behave exactly as: identity(&temp()).method();) + // + // Iterate up to the enclosing destruction scope to find the same scope that will also + // be used for the result of the block itself. + while let Some(s) = visitor.cx.var_parent { + let parent = visitor.scope_tree.parent_map.get(&s).cloned(); + if let Some(Scope { data: ScopeData::Destruction, .. }) = parent { + break; + } + visitor.cx.var_parent = parent; + } + } + } + if let Some(expr) = init { - record_rvalue_scope_if_borrow_expr(visitor, expr, blk_scope); + record_rvalue_scope_if_borrow_expr(visitor, expr, visitor.cx.var_parent); if let Some(pat) = pat { if is_binding_pat(pat) { visitor.scope_tree.record_rvalue_candidate( expr.hir_id, - RvalueCandidateType::Pattern { + RvalueCandidate { target: expr.hir_id.local_id, - lifetime: blk_scope, + lifetime: visitor.cx.var_parent, }, ); } @@ -640,6 +617,7 @@ fn resolve_local<'tcx>( if let Some(expr) = init { visitor.visit_expr(expr); } + if let Some(pat) = pat { visitor.visit_pat(pat); } @@ -701,6 +679,7 @@ fn resolve_local<'tcx>( PatKind::Ref(_, _) | PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..) + | PatKind::Missing | PatKind::Wild | PatKind::Never | PatKind::Expr(_) @@ -717,6 +696,7 @@ fn resolve_local<'tcx>( /// | [ ..., E&, ... ] /// | ( ..., E&, ... ) /// | {...; E&} + /// | { super let ... = E&; ... } /// | if _ { ...; E& } else { ...; E& } /// | match _ { ..., _ => E&, ... } /// | box E& @@ -733,10 +713,7 @@ fn resolve_local<'tcx>( record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id); visitor.scope_tree.record_rvalue_candidate( subexpr.hir_id, - RvalueCandidateType::Borrow { - target: subexpr.hir_id.local_id, - lifetime: blk_id, - }, + RvalueCandidate { target: subexpr.hir_id.local_id, lifetime: blk_id }, ); } hir::ExprKind::Struct(_, fields, _) => { @@ -756,6 +733,13 @@ fn resolve_local<'tcx>( if let Some(subexpr) = block.expr { record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id); } + for stmt in block.stmts { + if let hir::StmtKind::Let(local) = stmt.kind + && let Some(_) = local.super_ + { + visitor.extended_super_lets.insert(local.pat.hir_id.local_id, blk_id); + } + } } hir::ExprKind::If(_, then_block, else_block) => { record_rvalue_scope_if_borrow_expr(visitor, then_block, blk_id); @@ -783,28 +767,24 @@ fn resolve_local<'tcx>( impl<'tcx> ScopeResolutionVisitor<'tcx> { /// Records the current parent (if any) as the parent of `child_scope`. - /// Returns the depth of `child_scope`. - fn record_child_scope(&mut self, child_scope: Scope) -> ScopeDepth { + fn record_child_scope(&mut self, child_scope: Scope) { let parent = self.cx.parent; self.scope_tree.record_scope_parent(child_scope, parent); - // If `child_scope` has no parent, it must be the root node, and so has - // a depth of 1. Otherwise, its depth is one more than its parent's. - parent.map_or(1, |(_p, d)| d + 1) } /// Records the current parent (if any) as the parent of `child_scope`, /// and sets `child_scope` as the new current parent. fn enter_scope(&mut self, child_scope: Scope) { - let child_depth = self.record_child_scope(child_scope); - self.cx.parent = Some((child_scope, child_depth)); + self.record_child_scope(child_scope); + self.cx.parent = Some(child_scope); } - fn enter_node_scope_with_dtor(&mut self, id: hir::ItemLocalId) { + fn enter_node_scope_with_dtor(&mut self, id: hir::ItemLocalId, terminating: bool) { // If node was previously marked as a terminating scope during the // recursive visit of its parent node in the HIR, then we need to // account for the destruction scope representing the scope of // the destructors that run immediately after it completes. - if self.terminating_scopes.contains(&id) { + if terminating { self.enter_scope(Scope { local_id: id, data: ScopeData::Destruction }); } self.enter_scope(Scope { local_id: id, data: ScopeData::Node }); @@ -816,13 +796,11 @@ impl<'tcx> ScopeResolutionVisitor<'tcx> { // visited the body. let outer_ec = mem::replace(&mut self.expr_and_pat_count, 0); let outer_cx = self.cx; - let outer_ts = mem::take(&mut self.terminating_scopes); // The 'pessimistic yield' flag is set to true when we are // processing a `+=` statement and have to make pessimistic // control flow assumptions. This doesn't apply to nested // bodies within the `+=` statements. See #69307. let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false); - self.terminating_scopes.insert(hir_id.local_id); self.enter_scope(Scope { local_id: hir_id.local_id, data: ScopeData::CallSite }); self.enter_scope(Scope { local_id: hir_id.local_id, data: ScopeData::Arguments }); @@ -832,14 +810,13 @@ impl<'tcx> ScopeResolutionVisitor<'tcx> { // Restore context we had at the start. self.expr_and_pat_count = outer_ec; self.cx = outer_cx; - self.terminating_scopes = outer_ts; self.pessimistic_yield = outer_pessimistic_yield; } } impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> { fn visit_block(&mut self, b: &'tcx Block<'tcx>) { - resolve_block(self, b); + resolve_block(self, b, false); } fn visit_body(&mut self, body: &hir::Body<'tcx>) { @@ -857,14 +834,13 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> { self.enter_body(body.value.hir_id, |this| { if this.tcx.hir_body_owner_kind(owner_id).is_fn_or_closure() { // The arguments and `self` are parented to the fn. - this.cx.var_parent = this.cx.parent.take(); + this.cx.var_parent = this.cx.parent; for param in body.params { this.visit_pat(param.pat); } // The body of the every fn is a root scope. - this.cx.parent = this.cx.var_parent; - this.visit_expr(body.value) + resolve_expr(this, body.value, true); } else { // Only functions have an outer terminating (drop) scope, while // temporaries in constant initializers may be 'static, but only @@ -885,7 +861,11 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> { // (i.e., `'static`), which means that after `g` returns, it drops, // and all the associated destruction scope rules apply. this.cx.var_parent = None; - resolve_local(this, None, Some(body.value)); + this.enter_scope(Scope { + local_id: body.value.hir_id.local_id, + data: ScopeData::Destruction, + }); + resolve_local(this, None, Some(body.value), LetKind::Regular); } }) } @@ -900,10 +880,14 @@ impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> { resolve_stmt(self, s); } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - resolve_expr(self, ex); + resolve_expr(self, ex, false); } fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) { - resolve_local(self, Some(l.pat), l.init) + let let_kind = match l.super_ { + Some(_) => LetKind::Super, + None => LetKind::Regular, + }; + resolve_local(self, Some(l.pat), l.init, let_kind); } fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { let body = self.tcx.hir_body(c.body); @@ -930,9 +914,9 @@ pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { scope_tree: ScopeTree::default(), expr_and_pat_count: 0, cx: Context { parent: None, var_parent: None }, - terminating_scopes: Default::default(), pessimistic_yield: false, fixup_scopes: vec![], + extended_super_lets: Default::default(), }; visitor.scope_tree.root_body = Some(body.value.hir_id); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index d72cf00293f0c..2ec14b2f018c3 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -16,10 +16,12 @@ use rustc_lint_defs::builtin::SUPERTRAIT_ITEM_SHADOWING_DEFINITION; use rustc_macros::LintDiagnostic; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::query::Providers; +use rustc_middle::traits::solve::NoSolution; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ - self, AdtKind, GenericArgKind, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeFoldable, - TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast, + self, AdtKind, GenericArgKind, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeFlags, + TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, + Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_session::parse::feature_err; @@ -34,8 +36,6 @@ use rustc_trait_selection::traits::{ self, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc, }; -use rustc_type_ir::TypeFlags; -use rustc_type_ir::solve::NoSolution; use tracing::{debug, instrument}; use {rustc_ast as ast, rustc_hir as hir}; @@ -76,12 +76,37 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { ) } - fn register_wf_obligation( - &self, - span: Span, - loc: Option, - arg: ty::GenericArg<'tcx>, - ) { + /// Convenience function to *deeply* normalize during wfcheck. In the old solver, + /// this just dispatches to [`WfCheckingCtxt::normalize`], but in the new solver + /// this calls `deeply_normalize` and reports errors if they are encountered. + /// + /// This function should be called in favor of `normalize` in cases where we will + /// then check the well-formedness of the type, since we only use the normalized + /// signature types for implied bounds when checking regions. + // FIXME(-Znext-solver): This should be removed when we compute implied outlives + // bounds using the unnormalized signature of the function we're checking. + fn deeply_normalize(&self, span: Span, loc: Option, value: T) -> T + where + T: TypeFoldable>, + { + if self.infcx.next_trait_solver() { + match self.ocx.deeply_normalize( + &ObligationCause::new(span, self.body_def_id, ObligationCauseCode::WellFormed(loc)), + self.param_env, + value.clone(), + ) { + Ok(value) => value, + Err(errors) => { + self.infcx.err_ctxt().report_fulfillment_errors(errors); + value + } + } + } else { + self.normalize(span, loc, value) + } + } + + fn register_wf_obligation(&self, span: Span, loc: Option, term: ty::Term<'tcx>) { let cause = traits::ObligationCause::new( span, self.body_def_id, @@ -91,7 +116,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { self.tcx(), cause, self.param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg))), + ty::ClauseKind::WellFormed(term), )); } } @@ -126,13 +151,14 @@ where let infcx_compat = infcx.fork(); - // We specifically want to call the non-compat version of `implied_bounds_tys`; we do this always. + // We specifically want to *disable* the implied bounds hack, first, + // so we can detect when failures are due to bevy's implied bounds. let outlives_env = OutlivesEnvironment::new_with_implied_bounds_compat( &infcx, body_def_id, param_env, assumed_wf_types.iter().copied(), - false, + true, ); lint_redundant_lifetimes(tcx, body_def_id, &outlives_env); @@ -142,53 +168,22 @@ where return Ok(()); } - let is_bevy = assumed_wf_types.visit_with(&mut ContainsBevyParamSet { tcx }).is_break(); - - // If we have set `no_implied_bounds_compat`, then do not attempt compatibility. - // We could also just always enter if `is_bevy`, and call `implied_bounds_tys`, - // but that does result in slightly more work when this option is set and - // just obscures what we mean here anyways. Let's just be explicit. - if is_bevy && !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat { - let outlives_env = OutlivesEnvironment::new_with_implied_bounds_compat( - &infcx, - body_def_id, - param_env, - assumed_wf_types, - true, - ); - let errors_compat = infcx_compat.resolve_regions_with_outlives_env(&outlives_env); - if errors_compat.is_empty() { - Ok(()) - } else { - Err(infcx_compat.err_ctxt().report_region_errors(body_def_id, &errors_compat)) - } + let outlives_env = OutlivesEnvironment::new_with_implied_bounds_compat( + &infcx_compat, + body_def_id, + param_env, + assumed_wf_types, + // Don't *disable* the implied bounds hack; though this will only apply + // the implied bounds hack if this contains `bevy_ecs`'s `ParamSet` type. + false, + ); + let errors_compat = infcx_compat.resolve_regions_with_outlives_env(&outlives_env); + if errors_compat.is_empty() { + // FIXME: Once we fix bevy, this would be the place to insert a warning + // to upgrade bevy. + Ok(()) } else { - Err(infcx.err_ctxt().report_region_errors(body_def_id, &errors)) - } -} - -struct ContainsBevyParamSet<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> TypeVisitor> for ContainsBevyParamSet<'tcx> { - type Result = ControlFlow<()>; - - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { - // We only care to match `ParamSet` or `&ParamSet`. - match t.kind() { - ty::Adt(def, _) => { - if self.tcx.item_name(def.did()) == sym::ParamSet - && self.tcx.crate_name(def.did().krate) == sym::bevy_ecs - { - return ControlFlow::Break(()); - } - } - ty::Ref(_, ty, _) => ty.visit_with(self)?, - _ => {} - } - - ControlFlow::Continue(()) + Err(infcx_compat.err_ctxt().report_region_errors(body_def_id, &errors_compat)) } } @@ -201,7 +196,7 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua hir::Node::ImplItem(item) => check_impl_item(tcx, item), hir::Node::ForeignItem(item) => check_foreign_item(tcx, item), hir::Node::OpaqueTy(_) => Ok(crate::check::check::check_item_type(tcx, def_id)), - _ => unreachable!(), + _ => unreachable!("{node:?}"), }; if let Some(generics) = node.generics() { @@ -218,10 +213,10 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua /// definition itself. For example, this definition would be illegal: /// /// ```rust -/// struct Ref<'a, T> { x: &'a T } +/// struct StaticRef { x: &'static T } /// ``` /// -/// because the type did not declare that `T:'a`. +/// because the type did not declare that `T: 'static`. /// /// We do this check as a pre-pass before checking fn bodies because if these constraints are /// not included it frequently leads to confusing errors in fn bodies. So it's better to check @@ -299,26 +294,26 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<() } res } - hir::ItemKind::Fn { sig, .. } => { - check_item_fn(tcx, def_id, item.ident, item.span, sig.decl) + hir::ItemKind::Fn { ident, sig, .. } => { + check_item_fn(tcx, def_id, ident, item.span, sig.decl) } - hir::ItemKind::Static(ty, ..) => { + hir::ItemKind::Static(_, ty, ..) => { check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid) } - hir::ItemKind::Const(ty, ..) => { + hir::ItemKind::Const(_, ty, ..) => { check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid) } - hir::ItemKind::Struct(_, hir_generics) => { + hir::ItemKind::Struct(_, _, hir_generics) => { let res = check_type_defn(tcx, item, false); check_variances_for_type_defn(tcx, item, hir_generics); res } - hir::ItemKind::Union(_, hir_generics) => { + hir::ItemKind::Union(_, _, hir_generics) => { let res = check_type_defn(tcx, item, true); check_variances_for_type_defn(tcx, item, hir_generics); res } - hir::ItemKind::Enum(_, hir_generics) => { + hir::ItemKind::Enum(_, _, hir_generics) => { let res = check_type_defn(tcx, item, true); check_variances_for_type_defn(tcx, item, hir_generics); res @@ -327,10 +322,13 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<() hir::ItemKind::TraitAlias(..) => check_trait(tcx, item), // `ForeignItem`s are handled separately. hir::ItemKind::ForeignMod { .. } => Ok(()), - hir::ItemKind::TyAlias(hir_ty, hir_generics) if tcx.type_alias_is_lazy(item.owner_id) => { + hir::ItemKind::TyAlias(_, hir_ty, hir_generics) + if tcx.type_alias_is_lazy(item.owner_id) => + { let res = enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| { let ty = tcx.type_of(def_id).instantiate_identity(); - let item_ty = wfcx.normalize(hir_ty.span, Some(WellFormedLoc::Ty(def_id)), ty); + let item_ty = + wfcx.deeply_normalize(hir_ty.span, Some(WellFormedLoc::Ty(def_id)), ty); wfcx.register_wf_obligation( hir_ty.span, Some(WellFormedLoc::Ty(def_id)), @@ -436,7 +434,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { let gat_def_id = gat_item.def_id.expect_local(); let gat_item = tcx.associated_item(gat_def_id); // If this item is not an assoc ty, or has no args, then it's not a GAT - if gat_item.kind != ty::AssocKind::Type { + if !gat_item.is_type() { continue; } let gat_generics = tcx.generics_of(gat_def_id); @@ -460,7 +458,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { let item_required_bounds = match tcx.associated_item(item_def_id).kind { // In our example, this corresponds to `into_iter` method - ty::AssocKind::Fn => { + ty::AssocKind::Fn { .. } => { // For methods, we check the function signature's return type for any GATs // to constrain. In the `into_iter` case, we see that the return type // `Self::Iter<'a>` is a GAT we want to gather any potential missing bounds from. @@ -481,7 +479,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { ) } // In our example, this corresponds to the `Iter` and `Item` associated types - ty::AssocKind::Type => { + ty::AssocKind::Type { .. } => { // If our associated item is a GAT with missing bounds, add them to // the param-env here. This allows this GAT to propagate missing bounds // to other GATs. @@ -502,7 +500,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { gat_generics, ) } - ty::AssocKind::Const => None, + ty::AssocKind::Const { .. } => None, }; if let Some(item_required_bounds) = item_required_bounds { @@ -543,7 +541,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { continue; } - let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id); + let gat_item_hir = tcx.hir_expect_trait_item(gat_def_id); debug!(?required_bounds); let param_env = tcx.param_env(gat_def_id); @@ -659,7 +657,7 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( // Ignore `'static` lifetimes for the purpose of this lint: it's // because we know it outlives everything and so doesn't give meaningful // clues. Also ignore `ReError`, to avoid knock-down errors. - if let ty::ReStatic | ty::ReError(_) = **region_a { + if let ty::ReStatic | ty::ReError(_) = region_a.kind() { continue; } // For each region argument (e.g., `'a` in our example), check for a @@ -700,7 +698,7 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( // Again, skip `'static` because it outlives everything. Also, we trivially // know that a region outlives itself. Also ignore `ReError`, to avoid // knock-down errors. - if matches!(**region_b, ty::ReStatic | ty::ReError(_)) || region_a == region_b { + if matches!(region_b.kind(), ty::ReStatic | ty::ReError(_)) || region_a == region_b { continue; } if region_known_to_outlive(tcx, item_def_id, param_env, wf_tys, *region_a, *region_b) { @@ -852,10 +850,10 @@ fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { /// /// In such cases, suggest using `Self` instead. fn check_dyn_incompatible_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem<'_>) { - let (trait_name, trait_def_id) = + let (trait_ident, trait_def_id) = match tcx.hir_node_by_def_id(tcx.hir_get_parent_item(item.hir_id()).def_id) { hir::Node::Item(item) => match item.kind { - hir::ItemKind::Trait(..) => (item.ident, item.owner_id), + hir::ItemKind::Trait(_, _, ident, ..) => (ident, item.owner_id), _ => return, }, _ => return, @@ -892,7 +890,7 @@ fn check_dyn_incompatible_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitI trait_should_be_self, "associated item referring to unboxed trait object for its own trait", ) - .with_span_label(trait_name.span, "in this trait") + .with_span_label(trait_ident.span, "in this trait") .with_multipart_suggestion( "you might have meant to use `Self` to refer to the implementing type", sugg, @@ -1104,14 +1102,20 @@ fn check_associated_item( }; match item.kind { - ty::AssocKind::Const => { + ty::AssocKind::Const { .. } => { let ty = tcx.type_of(item.def_id).instantiate_identity(); - let ty = wfcx.normalize(span, Some(WellFormedLoc::Ty(item_id)), ty); + let ty = wfcx.deeply_normalize(span, Some(WellFormedLoc::Ty(item_id)), ty); wfcx.register_wf_obligation(span, loc, ty.into()); - check_sized_if_body(wfcx, item.def_id.expect_local(), ty, Some(span)); + check_sized_if_body( + wfcx, + item.def_id.expect_local(), + ty, + Some(span), + ObligationCauseCode::SizedConstOrStatic, + ); Ok(()) } - ty::AssocKind::Fn => { + ty::AssocKind::Fn { .. } => { let sig = tcx.fn_sig(item.def_id).instantiate_identity(); let hir_sig = sig_if_method.expect("bad signature for method"); check_fn_or_method( @@ -1123,13 +1127,13 @@ fn check_associated_item( ); check_method_receiver(wfcx, hir_sig, item, self_ty) } - ty::AssocKind::Type => { + ty::AssocKind::Type { .. } => { if let ty::AssocItemContainer::Trait = item.container { check_associated_type_bounds(wfcx, item, span) } if item.defaultness(tcx).has_value() { let ty = tcx.type_of(item.def_id).instantiate_identity(); - let ty = wfcx.normalize(span, Some(WellFormedLoc::Ty(item_id)), ty); + let ty = wfcx.deeply_normalize(span, Some(WellFormedLoc::Ty(item_id)), ty); wfcx.register_wf_obligation(span, loc, ty.into()); } Ok(()) @@ -1176,7 +1180,7 @@ fn check_type_defn<'tcx>( let field_id = field.did.expect_local(); let hir::FieldDef { ty: hir_ty, .. } = tcx.hir_node_by_def_id(field_id).expect_field(); - let ty = wfcx.normalize( + let ty = wfcx.deeply_normalize( hir_ty.span, None, tcx.type_of(field.did).instantiate_identity(), @@ -1337,7 +1341,7 @@ fn check_item_type( enter_wf_checking_ctxt(tcx, ty_span, item_id, |wfcx| { let ty = tcx.type_of(item_id).instantiate_identity(); - let item_ty = wfcx.normalize(ty_span, Some(WellFormedLoc::Ty(item_id)), ty); + let item_ty = wfcx.deeply_normalize(ty_span, Some(WellFormedLoc::Ty(item_id)), ty); let forbid_unsized = match unsized_handling { UnsizedHandling::Forbid => true, @@ -1354,7 +1358,7 @@ fn check_item_type( traits::ObligationCause::new( ty_span, wfcx.body_def_id, - ObligationCauseCode::WellFormed(None), + ObligationCauseCode::SizedConstOrStatic, ), wfcx.param_env, item_ty, @@ -1402,7 +1406,7 @@ fn check_impl<'tcx>( // other `Foo` impls are incoherent. tcx.ensure_ok().coherent_trait(trait_ref.def_id)?; let trait_span = hir_trait_ref.path.span; - let trait_ref = wfcx.normalize( + let trait_ref = wfcx.deeply_normalize( trait_span, Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), trait_ref, @@ -1462,7 +1466,7 @@ fn check_impl<'tcx>( } None => { let self_ty = tcx.type_of(item.owner_id).instantiate_identity(); - let self_ty = wfcx.normalize( + let self_ty = wfcx.deeply_normalize( item.span, Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), self_ty, @@ -1508,8 +1512,41 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id tcx.def_span(param.def_id), matches!(param.kind, GenericParamDefKind::Type { .. }) .then(|| WellFormedLoc::Ty(param.def_id.expect_local())), - default, + default.as_term().unwrap(), ); + } else { + // If we've got a generic const parameter we still want to check its + // type is correct in case both it and the param type are fully concrete. + let GenericArgKind::Const(ct) = default.unpack() else { + continue; + }; + + let ct_ty = match ct.kind() { + ty::ConstKind::Infer(_) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Bound(_, _) => unreachable!(), + ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => continue, + ty::ConstKind::Value(cv) => cv.ty, + ty::ConstKind::Unevaluated(uv) => { + infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args) + } + ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(wfcx.param_env), + }; + + let param_ty = tcx.type_of(param.def_id).instantiate_identity(); + if !ct_ty.has_param() && !param_ty.has_param() { + let cause = traits::ObligationCause::new( + tcx.def_span(param.def_id), + wfcx.body_def_id, + ObligationCauseCode::WellFormed(None), + ); + wfcx.register_obligation(Obligation::new( + tcx, + cause, + wfcx.param_env, + ty::ClauseKind::ConstArgHasType(ct, param_ty), + )); + } } } } @@ -1544,7 +1581,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id struct CountParams { params: FxHashSet, } - impl<'tcx> ty::visit::TypeVisitor> for CountParams { + impl<'tcx> ty::TypeVisitor> for CountParams { type Result = ControlFlow<()>; fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { if let ty::Param(param) = t.kind() { @@ -1634,7 +1671,7 @@ fn check_fn_or_method<'tcx>( sig.inputs_and_output = tcx.mk_type_list_from_iter(sig.inputs_and_output.iter().enumerate().map(|(idx, ty)| { - wfcx.normalize( + wfcx.deeply_normalize( arg_span(idx), Some(WellFormedLoc::Param { function: def_id, @@ -1698,6 +1735,7 @@ fn check_fn_or_method<'tcx>( hir::FnRetTy::Return(ty) => Some(ty.span), hir::FnRetTy::DefaultReturn(_) => None, }, + ObligationCauseCode::SizedReturnType, ); } @@ -1706,13 +1744,14 @@ fn check_sized_if_body<'tcx>( def_id: LocalDefId, ty: Ty<'tcx>, maybe_span: Option, + code: ObligationCauseCode<'tcx>, ) { let tcx = wfcx.tcx(); if let Some(body) = tcx.hir_maybe_body_owned_by(def_id) { let span = maybe_span.unwrap_or(body.value.span); wfcx.register_bound( - ObligationCause::new(span, def_id, traits::ObligationCauseCode::SizedReturnType), + ObligationCause::new(span, def_id, code), wfcx.param_env, ty, tcx.require_lang_item(LangItem::Sized, Some(span)), @@ -1736,7 +1775,7 @@ fn check_method_receiver<'tcx>( ) -> Result<(), ErrorGuaranteed> { let tcx = wfcx.tcx(); - if !method.fn_has_self_parameter { + if !method.is_method() { return Ok(()); } @@ -2011,7 +2050,7 @@ fn check_variances_for_type_defn<'tcx>( ItemKind::TyAlias(..) => { assert!( tcx.type_alias_is_lazy(item.owner_id), - "should not be computing variance of non-weak type alias" + "should not be computing variance of non-free type alias" ); } kind => span_bug!(item.span, "cannot compute the variances of {kind:?}"), @@ -2243,7 +2282,7 @@ impl<'tcx> TypeVisitor> for IsProbablyCyclical<'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<(), ()> { let def_id = match ty.kind() { ty::Adt(adt_def, _) => Some(adt_def.did()), - ty::Alias(ty::Weak, alias_ty) => Some(alias_ty.def_id), + ty::Alias(ty::Free, alias_ty) => Some(alias_ty.def_id), _ => None, }; if let Some(def_id) = def_id { diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs index 750c09887a1e5..464ffa8711a53 100644 --- a/compiler/rustc_hir_analysis/src/check_unused.rs +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -31,7 +31,7 @@ fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) { if used_trait_imports.contains(&id) { continue; } - let item = tcx.hir().expect_item(id); + let item = tcx.hir_expect_item(id); if item.span.is_dummy() { continue; } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index cee2f487639db..b92d1d7104f0a 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -82,7 +82,7 @@ fn visit_implementation_of_drop(checker: &Checker<'_>) -> Result<(), ErrorGuaran _ => {} } - let impl_ = tcx.hir().expect_item(impl_did).expect_impl(); + let impl_ = tcx.hir_expect_item(impl_did).expect_impl(); Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem { span: impl_.self_ty.span })) } @@ -109,7 +109,7 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran match type_allowed_to_implement_copy(tcx, param_env, self_type, cause, impl_header.safety) { Ok(()) => Ok(()), Err(CopyImplementationError::InfringingFields(fields)) => { - let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; + let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; Err(infringing_fields_error( tcx, fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)), @@ -119,15 +119,15 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran )) } Err(CopyImplementationError::NotAnAdt) => { - let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; + let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; Err(tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span })) } Err(CopyImplementationError::HasDestructor) => { - let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; + let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span })) } Err(CopyImplementationError::HasUnsafeFields) => { - let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; + let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; Err(tcx .dcx() .span_delayed_bug(span, format!("cannot implement `Copy` for `{}`", self_type))) @@ -157,7 +157,7 @@ fn visit_implementation_of_const_param_ty( match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, kind, cause) { Ok(()) => Ok(()), Err(ConstParamTyImplementationError::InfrigingFields(fields)) => { - let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; + let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; Err(infringing_fields_error( tcx, fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)), @@ -167,11 +167,11 @@ fn visit_implementation_of_const_param_ty( )) } Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => { - let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; + let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span })) } Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(infringing_tys)) => { - let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; + let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; Err(infringing_fields_error( tcx, infringing_tys.into_iter().map(|(ty, reason)| (span, ty, reason)), @@ -181,7 +181,7 @@ fn visit_implementation_of_const_param_ty( )) } Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired) => { - let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; + let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnUnsized { span })) } } @@ -214,11 +214,9 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() let span = tcx.def_span(impl_did); let trait_name = "DispatchFromDyn"; - let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span)); - let source = trait_ref.self_ty(); let target = { - assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait); + assert!(tcx.is_lang_item(trait_ref.def_id, LangItem::DispatchFromDyn)); trait_ref.args.type_at(1) }; @@ -250,13 +248,16 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() // trait, they *do* satisfy the repr(transparent) rules, and then we assume that everything else // in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent) // even if they do not carry that attribute. - use rustc_type_ir::TyKind::*; match (source.kind(), target.kind()) { - (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) if r_a == *r_b && mutbl_a == *mutbl_b => { + (&ty::Ref(r_a, _, mutbl_a), ty::Ref(r_b, _, mutbl_b)) + if r_a == *r_b && mutbl_a == *mutbl_b => + { Ok(()) } - (&RawPtr(_, a_mutbl), &RawPtr(_, b_mutbl)) if a_mutbl == b_mutbl => Ok(()), - (&Adt(def_a, args_a), &Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => { + (&ty::RawPtr(_, a_mutbl), &ty::RawPtr(_, b_mutbl)) if a_mutbl == b_mutbl => Ok(()), + (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_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()); @@ -336,7 +337,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() tcx, cause.clone(), param_env, - ty::TraitRef::new(tcx, dispatch_from_dyn_trait, [ty_a, ty_b]), + ty::TraitRef::new(tcx, trait_ref.def_id, [ty_a, ty_b]), )); let errors = ocx.select_all_or_error(); if !errors.is_empty() { @@ -526,7 +527,7 @@ pub(crate) fn coerce_unsized_info<'tcx>( note: true, })); } else if diff_fields.len() > 1 { - let item = tcx.hir().expect_item(impl_did); + let item = tcx.hir_expect_item(impl_did); let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind { t.path.span } else { @@ -653,7 +654,7 @@ fn infringing_fields_error<'tcx>( .entry((ty.clone(), predicate.clone())) .or_default() .push(origin.span()); - if let ty::RegionKind::ReEarlyParam(ebr) = *b + if let ty::RegionKind::ReEarlyParam(ebr) = b.kind() && ebr.has_name() { bounds.push((b.to_string(), a.to_string(), None)); @@ -747,7 +748,7 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err ObligationCause::misc(impl_span, checker.impl_def_id), param_env, nontrivial_field_ty, - tcx.lang_items().pointer_like().unwrap(), + tcx.require_lang_item(LangItem::PointerLike, Some(impl_span)), ); // FIXME(dyn-star): We should regionck this implementation. if ocx.select_all_or_error().is_empty() { diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index c9a9180c5c9dc..bd25b4a326086 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -150,7 +150,7 @@ impl<'tcx> InherentCollect<'tcx> { let id = id.owner_id.def_id; let item_span = self.tcx.def_span(id); let self_ty = self.tcx.type_of(id).instantiate_identity(); - let mut self_ty = self.tcx.peel_off_weak_alias_tys(self_ty); + let mut self_ty = self.tcx.peel_off_free_alias_tys(self_ty); // We allow impls on pattern types exactly when we allow impls on the base type. // FIXME(pattern_types): Figure out the exact coherence rules we want here. while let ty::Pat(base, _) = *self_ty.kind() { @@ -188,7 +188,7 @@ impl<'tcx> InherentCollect<'tcx> { | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) - | ty::Alias(ty::Weak, _) + | ty::Alias(ty::Free, _) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) => { diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index dc616576c9c1b..242639125b19a 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -51,7 +51,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { for &item1 in impl_items1.in_definition_order() { let collision = impl_items2 - .filter_by_name_unhygienic(item1.name) + .filter_by_name_unhygienic(item1.name()) .any(|&item2| self.compare_hygienically(item1, item2)); if collision { @@ -64,7 +64,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { fn compare_hygienically(&self, item1: ty::AssocItem, item2: ty::AssocItem) -> bool { // Symbols and namespace match, compare hygienically. - item1.kind.namespace() == item2.kind.namespace() + item1.namespace() == item2.namespace() && item1.ident(self.tcx).normalize_to_macros_2_0() == item2.ident(self.tcx).normalize_to_macros_2_0() } @@ -113,7 +113,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { let mut res = Ok(()); for &item1 in impl_items1.in_definition_order() { let collision = impl_items2 - .filter_by_name_unhygienic(item1.name) + .filter_by_name_unhygienic(item1.name()) .find(|&&item2| self.compare_hygienically(item1, item2)); if let Some(item2) = collision { @@ -230,11 +230,11 @@ impl<'tcx> InherentOverlapChecker<'tcx> { let mut ids = impl_items .in_definition_order() .filter_map(|item| { - let entry = connected_region_ids.entry(item.name); + let entry = connected_region_ids.entry(item.name()); if let IndexEntry::Occupied(e) = &entry { Some(*e.get()) } else { - idents_to_add.push(item.name); + idents_to_add.push(item.name()); None } }) diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 0245d4c9fe4b6..16bac4304910c 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -10,12 +10,12 @@ use rustc_errors::struct_span_code_err; use rustc_hir::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, elaborate}; use rustc_session::parse::feature_err; use rustc_span::{ErrorGuaranteed, sym}; -use rustc_type_ir::elaborate; use tracing::debug; +use crate::check::always_applicable; use crate::errors; mod builtin; @@ -24,11 +24,12 @@ mod inherent_impls_overlap; mod orphan; mod unsafety; -fn check_impl( - tcx: TyCtxt<'_>, +fn check_impl<'tcx>( + tcx: TyCtxt<'tcx>, impl_def_id: LocalDefId, - trait_ref: ty::TraitRef<'_>, - trait_def: &ty::TraitDef, + trait_ref: ty::TraitRef<'tcx>, + trait_def: &'tcx ty::TraitDef, + polarity: ty::ImplPolarity, ) -> Result<(), ErrorGuaranteed> { debug!( "(checking implementation) adding impl for trait '{:?}', item '{}'", @@ -44,6 +45,12 @@ fn check_impl( enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id, trait_def) .and(enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id, trait_def)) + .and(always_applicable::check_negative_auto_trait_impl( + tcx, + impl_def_id, + trait_ref, + polarity, + )) } fn enforce_trait_manually_implementable( @@ -146,24 +153,27 @@ pub(crate) fn provide(providers: &mut Providers) { } fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed> { + let impls = tcx.local_trait_impls(def_id); // If there are no impls for the trait, then "all impls" are trivially coherent and we won't check anything // anyway. Thus we bail out even before the specialization graph, avoiding the dep_graph edge. - let Some(impls) = tcx.all_local_trait_impls(()).get(&def_id) else { return Ok(()) }; + if impls.is_empty() { + return Ok(()); + } // Trigger building the specialization graph for the trait. This will detect and report any // overlap errors. let mut res = tcx.ensure_ok().specialization_graph_of(def_id); for &impl_def_id in impls { - let trait_header = tcx.impl_trait_header(impl_def_id).unwrap(); - let trait_ref = trait_header.trait_ref.instantiate_identity(); + let impl_header = tcx.impl_trait_header(impl_def_id).unwrap(); + let trait_ref = impl_header.trait_ref.instantiate_identity(); let trait_def = tcx.trait_def(trait_ref.def_id); res = res - .and(check_impl(tcx, impl_def_id, trait_ref, trait_def)) + .and(check_impl(tcx, impl_def_id, trait_ref, trait_def, impl_header.polarity)) .and(check_object_overlap(tcx, impl_def_id, trait_ref)) - .and(unsafety::check_item(tcx, impl_def_id, trait_header, trait_def)) + .and(unsafety::check_item(tcx, impl_def_id, impl_header, trait_def)) .and(tcx.ensure_ok().orphan_check_impl(impl_def_id)) - .and(builtin::check_trait(tcx, def_id, impl_def_id, trait_header)); + .and(builtin::check_trait(tcx, def_id, impl_def_id, impl_header)); } res diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index dbf7a7378f5ab..c75fef9f716d6 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -3,11 +3,10 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::ErrorGuaranteed; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_lint_defs::builtin::UNCOVERED_PARAM_IN_PROJECTION; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, + self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::{DefId, LocalDefId}; @@ -190,7 +189,7 @@ pub(crate) fn orphan_check_impl( ty::Projection => "associated type", // type Foo = (impl Sized, bool) // impl AutoTrait for Foo {} - ty::Weak => "type alias", + ty::Free => "type alias", // type Opaque = impl Trait; // impl AutoTrait for Opaque {} ty::Opaque => "opaque type", @@ -356,13 +355,20 @@ fn orphan_check<'tcx>( }) } OrphanCheckErr::NonLocalInputType(tys) => { - let generics = tcx.generics_of(impl_def_id); - let tys = tys - .into_iter() - .map(|(ty, is_target_ty)| { - (ty.fold_with(&mut TyVarReplacer { infcx: &infcx, generics }), is_target_ty) - }) - .collect(); + let tys = infcx.probe(|_| { + // Map the unconstrained args back to their params, + // ignoring any type unification errors. + for (arg, id_arg) in + std::iter::zip(args, ty::GenericArgs::identity_for_item(tcx, impl_def_id)) + { + let _ = infcx.at(&cause, ty::ParamEnv::empty()).eq( + DefineOpaqueTypes::No, + arg, + id_arg, + ); + } + infcx.resolve_vars_if_possible(tys) + }); OrphanCheckErr::NonLocalInputType(tys) } }) @@ -376,7 +382,7 @@ fn emit_orphan_check_error<'tcx>( ) -> ErrorGuaranteed { match err { traits::OrphanCheckErr::NonLocalInputType(tys) => { - let item = tcx.hir().expect_item(impl_def_id); + let item = tcx.hir_expect_item(impl_def_id); let impl_ = item.expect_impl(); let hir_trait_ref = impl_.of_trait.as_ref().unwrap(); @@ -536,40 +542,3 @@ impl<'tcx> TypeVisitor> for UncoveredTyParamCollector<'_, 'tcx> { } } } - -struct TyVarReplacer<'cx, 'tcx> { - infcx: &'cx InferCtxt<'tcx>, - generics: &'tcx ty::Generics, -} - -impl<'cx, 'tcx> TypeFolder> for TyVarReplacer<'cx, 'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if !ty.has_type_flags(ty::TypeFlags::HAS_TY_INFER) { - return ty; - } - let ty::Infer(ty::TyVar(vid)) = *ty.kind() else { - return ty.super_fold_with(self); - }; - let origin = self.infcx.type_var_origin(vid); - if let Some(def_id) = origin.param_def_id { - // The generics of an `impl` don't have a parent, we can index directly. - let index = self.generics.param_def_id_to_index[&def_id]; - let name = self.generics.own_params[index as usize].name; - - Ty::new_param(self.infcx.tcx, index, name) - } else { - ty - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - if !ct.has_type_flags(ty::TypeFlags::HAS_TY_INFER) { - return ct; - } - ct.super_fold_with(self) - } -} diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 027aa5554a40c..4520fbe352cea 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -14,6 +14,7 @@ //! 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 std::assert_matches::assert_matches; use std::cell::Cell; use std::iter; use std::ops::Bound; @@ -28,14 +29,13 @@ use rustc_errors::{ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt, walk_generics}; -use rustc_hir::{self as hir, GenericParamKind, HirId, Node}; +use rustc_hir::{self as hir, GenericParamKind, HirId, Node, PreciseCapturingArgKind}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::hir::nested_filter; use rustc_middle::query::Providers; -use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::util::{Discr, IntTypeExt}; -use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypingMode}; +use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypingMode, fold_regions}; use rustc_middle::{bug, span_bug}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; @@ -43,8 +43,8 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument}; -use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::errors; +use crate::hir_ty_lowering::errors::assoc_tag_str; use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason}; pub(crate) mod dump; @@ -61,6 +61,7 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { type_of: type_of::type_of, type_of_opaque: type_of::type_of_opaque, + type_of_opaque_hir_typeck: type_of::type_of_opaque_hir_typeck, type_alias_is_lazy: type_of::type_alias_is_lazy, item_bounds: item_bounds::item_bounds, explicit_item_bounds: item_bounds::explicit_item_bounds, @@ -247,13 +248,13 @@ fn reject_placeholder_type_signatures_in_item<'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::Union(_, _, generics) + | hir::ItemKind::Enum(_, _, generics) + | hir::ItemKind::TraitAlias(_, generics, _) + | hir::ItemKind::Trait(_, _, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }) - | hir::ItemKind::Struct(_, generics) => (generics, true), - hir::ItemKind::TyAlias(_, generics) => (generics, false), + | hir::ItemKind::Struct(_, _, generics) => (generics, true), + hir::ItemKind::TyAlias(_, _, generics) => (generics, false), // `static`, `fn` and `const` are handled elsewhere to suggest appropriate type. _ => return, }; @@ -438,18 +439,19 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { &self, span: Span, def_id: LocalDefId, - assoc_name: Ident, + assoc_ident: Ident, ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_name)) + self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_ident)) } - fn lower_assoc_ty( + fn lower_assoc_shared( &self, span: Span, item_def_id: DefId, - item_segment: &hir::PathSegment<'tcx>, + item_segment: &rustc_hir::PathSegment<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Ty<'tcx> { + assoc_tag: ty::AssocTag, + ) -> Result<(DefId, ty::GenericArgsRef<'tcx>), ErrorGuaranteed> { if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { let item_args = self.lowerer().lower_generic_args_of_assoc_item( span, @@ -457,7 +459,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { item_segment, trait_ref.args, ); - Ty::new_projection_from_args(self.tcx(), item_def_id, item_args) + Ok((item_def_id, item_args)) } else { // There are no late-bound regions; we can just ignore the binder. let (mut mpart_sugg, mut inferred_sugg) = (None, None); @@ -467,12 +469,11 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { 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()).def_id); + .hir_expect_item(self.tcx.hir_get_parent_item(self.hir_id()).def_id); match &item.kind { - hir::ItemKind::Enum(_, generics) - | hir::ItemKind::Struct(_, generics) - | hir::ItemKind::Union(_, generics) => { + 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}>")), @@ -518,16 +519,14 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { } _ => {} } - Ty::new_error( - self.tcx(), - self.tcx().dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams { - span, - inferred_sugg, - bound, - mpart_sugg, - what: "type", - }), - ) + + Err(self.tcx().dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams { + span, + inferred_sugg, + bound, + mpart_sugg, + what: assoc_tag_str(assoc_tag), + })) } } @@ -669,16 +668,16 @@ fn get_new_lifetime_name<'tcx>( #[instrument(level = "debug", skip_all)] fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { let it = tcx.hir_item(item_id); - debug!(item = %it.ident, id = %it.hir_id()); + debug!(item = ?it.kind.ident(), id = %it.hir_id()); let def_id = item_id.owner_id.def_id; let icx = ItemCtxt::new(tcx, def_id); match &it.kind { // These don't define types. - hir::ItemKind::ExternCrate(_) + hir::ItemKind::ExternCrate(..) | hir::ItemKind::Use(..) | hir::ItemKind::Macro(..) - | hir::ItemKind::Mod(_) + | hir::ItemKind::Mod(..) | hir::ItemKind::GlobalAsm { .. } => {} hir::ItemKind::ForeignMod { items, .. } => { for item in *items { @@ -738,7 +737,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.at(it.span).explicit_super_predicates_of(def_id); tcx.ensure_ok().predicates_of(def_id); } - hir::ItemKind::Struct(struct_def, _) | hir::ItemKind::Union(struct_def, _) => { + hir::ItemKind::Struct(_, struct_def, _) | hir::ItemKind::Union(_, struct_def, _) => { tcx.ensure_ok().generics_of(def_id); tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); @@ -760,7 +759,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure_ok().predicates_of(def_id); } - hir::ItemKind::Static(ty, ..) | hir::ItemKind::Const(ty, ..) => { + hir::ItemKind::Static(_, ty, ..) | hir::ItemKind::Const(_, ty, ..) => { tcx.ensure_ok().generics_of(def_id); tcx.ensure_ok().type_of(def_id); tcx.ensure_ok().predicates_of(def_id); @@ -1091,7 +1090,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { let repr = tcx.repr_options_of_def(def_id); let (kind, variants) = match &item.kind { - ItemKind::Enum(def, _) => { + ItemKind::Enum(_, def, _) => { let mut distance_from_explicit = 0; let variants = def .variants @@ -1119,7 +1118,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { (AdtKind::Enum, variants) } - ItemKind::Struct(def, _) | ItemKind::Union(def, _) => { + ItemKind::Struct(ident, def, _) | ItemKind::Union(ident, def, _) => { let adt_kind = match item.kind { ItemKind::Struct(..) => AdtKind::Struct, _ => AdtKind::Union, @@ -1127,7 +1126,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { let variants = std::iter::once(lower_variant( tcx, None, - item.ident, + *ident, ty::VariantDiscr::Relative(0), def, adt_kind, @@ -1143,7 +1142,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { } fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { - let item = tcx.hir().expect_item(def_id); + let item = tcx.hir_expect_item(def_id); let (is_alias, is_auto, safety, items) = match item.kind { hir::ItemKind::Trait(is_auto, safety, .., items) => { @@ -1342,11 +1341,12 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn ), ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(sig, _, _), .. }) => { - let abi = tcx.hir().get_foreign_abi(hir_id); + let abi = tcx.hir_get_foreign_abi(hir_id); compute_sig_of_foreign_fn_decl(tcx, def_id, sig.decl, abi, sig.header.safety()) } - Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor().is_some() => { + Ctor(data) => { + assert_matches!(data.ctor(), Some(_)); let adt_def_id = tcx.hir_get_parent_item(hir_id).def_id.to_def_id(); let ty = tcx.type_of(adt_def_id).instantiate_identity(); let inputs = data.fields().iter().map(|f| tcx.type_of(f.def_id).instantiate_identity()); @@ -1418,7 +1418,7 @@ fn recover_infer_ret_ty<'tcx>( GenericParamKind::Lifetime { .. } => true, _ => false, }); - let fn_sig = fold_regions(tcx, fn_sig, |r, _| match *r { + let fn_sig = fold_regions(tcx, fn_sig, |r, _| match r.kind() { ty::ReErased => { if has_region_params { ty::Region::new_error_with_message( @@ -1597,7 +1597,7 @@ pub fn suggest_impl_trait<'tcx>( fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option> { let icx = ItemCtxt::new(tcx, def_id); - let item = tcx.hir().expect_item(def_id); + let item = tcx.hir_expect_item(def_id); let impl_ = item.expect_impl(); impl_.of_trait.as_ref().map(|ast_trait_ref| { let selfty = tcx.type_of(def_id).instantiate_identity(); @@ -1705,18 +1705,13 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( abi: ExternAbi, safety: hir::Safety, ) -> ty::PolyFnSig<'tcx> { - let safety = if abi == ExternAbi::RustIntrinsic { - intrinsic_operation_unsafety(tcx, def_id) - } else { - safety - }; let hir_id = tcx.local_def_id_to_hir_id(def_id); let fty = ItemCtxt::new(tcx, def_id).lowerer().lower_fn_ty(hir_id, safety, abi, decl, None, None); // Feature gate SIMD types in FFI, since I am not sure that the // ABIs are handled at all correctly. -huonw - if abi != ExternAbi::RustIntrinsic && !tcx.features().simd_ffi() { + if !tcx.features().simd_ffi() { let check = |hir_ty: &hir::Ty<'_>, ty: Ty<'_>| { if ty.is_simd() { let snip = tcx @@ -1792,7 +1787,7 @@ fn opaque_ty_origin<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> hir::OpaqueT fn rendered_precise_capturing_args<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, -) -> Option<&'tcx [Symbol]> { +) -> Option<&'tcx [PreciseCapturingArgKind]> { if let Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) = tcx.opt_rpitit_info(def_id.to_def_id()) { @@ -1801,7 +1796,12 @@ fn rendered_precise_capturing_args<'tcx>( tcx.hir_node_by_def_id(def_id).expect_opaque_ty().bounds.iter().find_map(|bound| match bound { hir::GenericBound::Use(args, ..) => { - Some(&*tcx.arena.alloc_from_iter(args.iter().map(|arg| arg.name()))) + Some(&*tcx.arena.alloc_from_iter(args.iter().map(|arg| match arg { + PreciseCapturingArgKind::Lifetime(_) => { + PreciseCapturingArgKind::Lifetime(arg.name()) + } + PreciseCapturingArgKind::Param(_) => PreciseCapturingArgKind::Param(arg.name()), + }))) } _ => None, }) diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index 4debd3977f50a..c3f965d845693 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -13,7 +13,7 @@ pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) { for id in tcx.hir_crate_items(()).opaques() { if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. } = - tcx.hir().expect_opaque_ty(id).origin + tcx.hir_expect_opaque_ty(id).origin && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id) && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn() { @@ -134,7 +134,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { }; tcx.vtable_entries(trait_ref) } - hir::ItemKind::TyAlias(_, _) => { + hir::ItemKind::TyAlias(..) => { let ty = tcx.type_of(def_id).instantiate_identity(); if ty.has_non_region_param() { tcx.dcx().span_err( diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index af1338e50d007..2bed28d7b710c 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -116,8 +116,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { { // enum variant discriminants are not allowed to use any kind of generics None - } else if let Some(param_id) = - tcx.hir().opt_const_param_default_param_def_id(hir_id) + } else if let Some(param_id) = tcx.hir_opt_const_param_default_param_def_id(hir_id) { // If the def_id we are calling generics_of on is an anon ct default i.e: // @@ -187,6 +186,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { Some(parent_did) } Node::TyPat(_) => Some(parent_did), + // Field default values inherit the ADT's generics. + Node::Field(_) => Some(parent_did), _ => None, } } diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 1c1a246cc1519..279b1e82a716f 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -1,14 +1,13 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir as hir; use rustc_infer::traits::util; -use rustc_middle::ty::fold::shift_vars; use rustc_middle::ty::{ self, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + Upcast, shift_vars, }; use rustc_middle::{bug, span_bug}; use rustc_span::Span; use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_type_ir::Upcast; use tracing::{debug, instrument}; use super::ItemCtxt; @@ -39,13 +38,13 @@ fn associated_type_bounds<'tcx>( let icx = ItemCtxt::new(tcx, assoc_item_def_id); let mut bounds = Vec::new(); icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter); - // Associated types are implicitly sized unless a `?Sized` bound is found + // Implicit bounds are added to associated types unless a `?Trait` bound is found match filter { PredicateFilter::All | PredicateFilter::SelfOnly | PredicateFilter::SelfTraitThatDefines(_) | PredicateFilter::SelfAndAssociatedTypeBounds => { - icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); + icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span); } // `ConstIfConst` is only interested in `~const` bounds. PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {} @@ -328,14 +327,13 @@ fn opaque_type_bounds<'tcx>( let icx = ItemCtxt::new(tcx, opaque_def_id); let mut bounds = Vec::new(); icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter); - // Opaque types are implicitly sized unless a `?Sized` bound is found + // Implicit bounds are added to opaque types unless a `?Trait` bound is found match filter { PredicateFilter::All | PredicateFilter::SelfOnly | PredicateFilter::SelfTraitThatDefines(_) | PredicateFilter::SelfAndAssociatedTypeBounds => { - // Associated types are implicitly sized unless a `?Sized` bound is found - icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); + icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span); } //`ConstIfConst` is only interested in `~const` bounds. PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {} diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 2022127a15b05..ce0f83d0ec288 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -163,13 +163,20 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } } - ItemKind::Trait(_, _, _, self_bounds, ..) | ItemKind::TraitAlias(_, self_bounds) => { - is_trait = Some(self_bounds); + ItemKind::Trait(_, _, _, _, self_bounds, ..) + | ItemKind::TraitAlias(_, _, self_bounds) => { + is_trait = Some((self_bounds, item.span)); } _ => {} } }; + if let Node::TraitItem(item) = node { + let mut bounds = Vec::new(); + icx.lowerer().add_default_trait_item_bounds(item, &mut bounds); + predicates.extend(bounds); + } + let generics = tcx.generics_of(def_id); // Below we'll consider the bounds on the type parameters (including `Self`) @@ -180,11 +187,18 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen let mut bounds = Vec::new(); icx.lowerer().lower_bounds( tcx.types.self_param, - self_bounds, + self_bounds.0, &mut bounds, ty::List::empty(), PredicateFilter::All, ); + icx.lowerer().add_default_super_traits( + def_id, + &mut bounds, + self_bounds.0, + hir_generics, + self_bounds.1, + ); predicates.extend(bounds); } @@ -209,8 +223,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen GenericParamKind::Type { .. } => { let param_ty = icx.lowerer().lower_ty_param(param.hir_id); let mut bounds = Vec::new(); - // Params are implicitly sized unless a `?Sized` bound is found - icx.lowerer().add_sized_bound( + // Implicit bounds are added to type params unless a `?Trait` bound is found + icx.lowerer().add_default_traits( &mut bounds, param_ty, &[], @@ -345,7 +359,7 @@ fn compute_bidirectional_outlives_predicates<'tcx>( ) { for param in opaque_own_params { let orig_lifetime = tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()); - if let ty::ReEarlyParam(..) = *orig_lifetime { + if let ty::ReEarlyParam(..) = orig_lifetime.kind() { let dup_lifetime = ty::Region::new_early_param( tcx, ty::EarlyParamRegion { index: param.index, name: param.name }, @@ -507,7 +521,7 @@ pub(super) fn explicit_predicates_of<'tcx>( if matches!(def_kind, DefKind::AnonConst) && tcx.features().generic_const_exprs() && let Some(defaulted_param_def_id) = - tcx.hir().opt_const_param_default_param_def_id(tcx.local_def_id_to_hir_id(def_id)) + tcx.hir_opt_const_param_default_param_def_id(tcx.local_def_id_to_hir_id(def_id)) { // In `generics_of` we set the generics' parent to be our parent's parent which means that // we lose out on the predicates of our actual parent if we dont return those predicates here. @@ -570,12 +584,12 @@ pub(super) fn explicit_super_predicates_of<'tcx>( pub(super) fn explicit_supertraits_containing_assoc_item<'tcx>( tcx: TyCtxt<'tcx>, - (trait_def_id, assoc_name): (DefId, Ident), + (trait_def_id, assoc_ident): (DefId, Ident), ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { implied_predicates_with_filter( tcx, trait_def_id, - PredicateFilter::SelfTraitThatDefines(assoc_name), + PredicateFilter::SelfTraitThatDefines(assoc_ident), ) } @@ -603,7 +617,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>( filter: PredicateFilter, ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { let Some(trait_def_id) = trait_def_id.as_local() else { - // if `assoc_name` is None, then the query should've been redirected to an + // if `assoc_ident` is None, then the query should've been redirected to an // external provider assert_matches!(filter, PredicateFilter::SelfTraitThatDefines(_)); return tcx.explicit_super_predicates_of(trait_def_id); @@ -615,7 +629,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>( let (generics, superbounds) = match item.kind { hir::ItemKind::Trait(.., generics, supertraits, _) => (generics, supertraits), - hir::ItemKind::TraitAlias(generics, supertraits) => (generics, supertraits), + hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits), _ => span_bug!(item.span, "super_predicates invoked on non-trait"), }; @@ -624,6 +638,22 @@ pub(super) fn implied_predicates_with_filter<'tcx>( let self_param_ty = tcx.types.self_param; let mut bounds = Vec::new(); icx.lowerer().lower_bounds(self_param_ty, superbounds, &mut bounds, ty::List::empty(), filter); + match filter { + PredicateFilter::All + | PredicateFilter::SelfOnly + | PredicateFilter::SelfTraitThatDefines(_) + | PredicateFilter::SelfAndAssociatedTypeBounds => { + icx.lowerer().add_default_super_traits( + trait_def_id, + &mut bounds, + superbounds, + generics, + item.span, + ); + } + //`ConstIfConst` is only interested in `~const` bounds. + PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {} + } let where_bounds_that_match = icx.probe_ty_param_bounds_in_generics(generics, item.owner_id.def_id, filter); @@ -804,11 +834,11 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>( #[instrument(level = "trace", skip(tcx))] pub(super) fn type_param_predicates<'tcx>( tcx: TyCtxt<'tcx>, - (item_def_id, def_id, assoc_name): (LocalDefId, LocalDefId, Ident), + (item_def_id, def_id, assoc_ident): (LocalDefId, LocalDefId, Ident), ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { match tcx.opt_rpitit_info(item_def_id.to_def_id()) { Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => { - return tcx.type_param_predicates((opaque_def_id.expect_local(), def_id, assoc_name)); + return tcx.type_param_predicates((opaque_def_id.expect_local(), def_id, assoc_ident)); } Some(ty::ImplTraitInTraitData::Impl { .. }) => { unreachable!("should not be lowering bounds on RPITIT in impl") @@ -833,7 +863,7 @@ pub(super) fn type_param_predicates<'tcx>( let result = if let Some(parent) = parent { let icx = ItemCtxt::new(tcx, parent); - icx.probe_ty_param_bounds(DUMMY_SP, def_id, assoc_name) + icx.probe_ty_param_bounds(DUMMY_SP, def_id, assoc_ident) } else { ty::EarlyBinder::bind(&[] as &[_]) }; @@ -859,7 +889,7 @@ pub(super) fn type_param_predicates<'tcx>( let extra_predicates = extend.into_iter().chain(icx.probe_ty_param_bounds_in_generics( hir_generics, def_id, - PredicateFilter::SelfTraitThatDefines(assoc_name), + PredicateFilter::SelfTraitThatDefines(assoc_ident), )); let bounds = @@ -878,7 +908,7 @@ pub(super) fn type_param_predicates<'tcx>( _ => unreachable!(), }; assert_only_contains_predicates_from( - PredicateFilter::SelfTraitThatDefines(assoc_name), + PredicateFilter::SelfTraitThatDefines(assoc_ident), bounds, self_ty, ); @@ -959,7 +989,7 @@ pub(super) fn const_conditions<'tcx>( Node::Item(item) => match item.kind { hir::ItemKind::Impl(impl_) => (impl_.generics, None, false), hir::ItemKind::Fn { generics, .. } => (generics, None, false), - hir::ItemKind::Trait(_, _, generics, supertraits, _) => { + hir::ItemKind::Trait(_, _, _, generics, supertraits, _) => { (generics, Some((item.owner_id.def_id, supertraits)), false) } _ => bug!("const_conditions called on wrong item: {def_id:?}"), diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 6b61d317d3f50..709446d09cd25 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -14,9 +14,10 @@ use rustc_ast::visit::walk_list; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, Res}; +use rustc_hir::definitions::{DefPathData, DisambiguatorState}; use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt}; use rustc_hir::{ - self as hir, AmbigArg, GenericArg, GenericParam, GenericParamKind, HirId, LifetimeName, Node, + self as hir, AmbigArg, GenericArg, GenericParam, GenericParamKind, HirId, LifetimeKind, Node, }; use rustc_macros::extension; use rustc_middle::hir::nested_filter; @@ -63,6 +64,7 @@ impl ResolvedArg { struct BoundVarContext<'a, 'tcx> { tcx: TyCtxt<'tcx>, rbv: &'a mut ResolveBoundVars, + disambiguator: &'a mut DisambiguatorState, scope: ScopeRef<'a>, } @@ -245,8 +247,12 @@ pub(crate) fn provide(providers: &mut Providers) { #[instrument(level = "debug", skip(tcx))] fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBoundVars { let mut rbv = ResolveBoundVars::default(); - let mut visitor = - BoundVarContext { tcx, rbv: &mut rbv, scope: &Scope::Root { opt_parent_item: None } }; + let mut visitor = BoundVarContext { + tcx, + rbv: &mut rbv, + scope: &Scope::Root { opt_parent_item: None }, + disambiguator: &mut DisambiguatorState::new(), + }; match tcx.hir_owner_node(local_def_id) { hir::OwnerNode::Item(item) => visitor.visit_item(item), hir::OwnerNode::ForeignItem(item) => visitor.visit_foreign_item(item), @@ -515,9 +521,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { let capture_all_in_scope_lifetimes = opaque_captures_all_in_scope_lifetimes(opaque); if capture_all_in_scope_lifetimes { + let tcx = self.tcx; let lifetime_ident = |def_id: LocalDefId| { - let name = self.tcx.item_name(def_id.to_def_id()); - let span = self.tcx.def_span(def_id); + let name = tcx.item_name(def_id.to_def_id()); + let span = tcx.def_span(def_id); Ident::new(name, span) }; @@ -617,7 +624,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }); } - hir::ItemKind::ExternCrate(_) + hir::ItemKind::ExternCrate(..) | hir::ItemKind::Use(..) | hir::ItemKind::Macro(..) | hir::ItemKind::Mod(..) @@ -627,13 +634,13 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // These sorts of items have no lifetime parameters at all. intravisit::walk_item(self, item); } - hir::ItemKind::TyAlias(_, generics) - | hir::ItemKind::Const(_, generics, _) - | hir::ItemKind::Enum(_, generics) - | hir::ItemKind::Struct(_, generics) - | hir::ItemKind::Union(_, generics) - | hir::ItemKind::Trait(_, _, generics, ..) - | hir::ItemKind::TraitAlias(generics, ..) + hir::ItemKind::TyAlias(_, _, generics) + | hir::ItemKind::Const(_, _, generics, _) + | hir::ItemKind::Enum(_, _, generics) + | hir::ItemKind::Struct(_, _, generics) + | hir::ItemKind::Union(_, _, generics) + | hir::ItemKind::Trait(_, _, _, generics, ..) + | hir::ItemKind::TraitAlias(_, generics, ..) | hir::ItemKind::Impl(&hir::Impl { generics, .. }) => { // These kinds of items have only early-bound lifetime parameters. self.visit_early(item.hir_id(), generics, |this| intravisit::walk_item(this, item)); @@ -646,14 +653,14 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { arg: &'tcx hir::PreciseCapturingArg<'tcx>, ) -> Self::Result { match *arg { - hir::PreciseCapturingArg::Lifetime(lt) => match lt.res { - LifetimeName::Param(def_id) => { + hir::PreciseCapturingArg::Lifetime(lt) => match lt.kind { + LifetimeKind::Param(def_id) => { self.resolve_lifetime_ref(def_id, lt); } - LifetimeName::Error => {} - LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Infer - | LifetimeName::Static => { + LifetimeKind::Error => {} + LifetimeKind::ImplicitObjectLifetimeDefault + | LifetimeKind::Infer + | LifetimeKind::Static => { self.tcx.dcx().emit_err(errors::BadPreciseCapture { span: lt.ident.span, kind: "lifetime", @@ -774,26 +781,26 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { ); } }); - match lifetime.res { - LifetimeName::ImplicitObjectLifetimeDefault => { + match lifetime.kind { + LifetimeKind::ImplicitObjectLifetimeDefault => { // If the user does not write *anything*, we // use the object lifetime defaulting // rules. So e.g., `Box` becomes // `Box`. self.resolve_object_lifetime_default(&*lifetime) } - LifetimeName::Infer => { + LifetimeKind::Infer => { // If the user writes `'_`, we use the *ordinary* elision // rules. So the `'_` in e.g., `Box` will be // resolved the same as the `'_` in `&'_ Foo`. // // cc #48468 } - LifetimeName::Param(..) | LifetimeName::Static => { + LifetimeKind::Param(..) | LifetimeKind::Static => { // If the user wrote an explicit name, use that. self.visit_lifetime(&*lifetime); } - LifetimeName::Error => {} + LifetimeKind::Error => {} } } hir::TyKind::Ref(lifetime_ref, ref mt) => { @@ -873,17 +880,17 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { - match lifetime_ref.res { - hir::LifetimeName::Static => { + match lifetime_ref.kind { + hir::LifetimeKind::Static => { self.insert_lifetime(lifetime_ref, ResolvedArg::StaticLifetime) } - hir::LifetimeName::Param(param_def_id) => { + hir::LifetimeKind::Param(param_def_id) => { self.resolve_lifetime_ref(param_def_id, lifetime_ref) } // If we've already reported an error, just ignore `lifetime_ref`. - hir::LifetimeName::Error => {} + hir::LifetimeKind::Error => {} // Those will be resolved by typechecking. - hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => {} + hir::LifetimeKind::ImplicitObjectLifetimeDefault | hir::LifetimeKind::Infer => {} } } @@ -1063,15 +1070,15 @@ fn object_lifetime_default(tcx: TyCtxt<'_>, param_def_id: LocalDefId) -> ObjectL for bound in bound.bounds { if let hir::GenericBound::Outlives(lifetime) = bound { - set.insert(lifetime.res); + set.insert(lifetime.kind); } } } match set { Set1::Empty => ObjectLifetimeDefault::Empty, - Set1::One(hir::LifetimeName::Static) => ObjectLifetimeDefault::Static, - Set1::One(hir::LifetimeName::Param(param_def_id)) => { + Set1::One(hir::LifetimeKind::Static) => ObjectLifetimeDefault::Static, + Set1::One(hir::LifetimeKind::Param(param_def_id)) => { ObjectLifetimeDefault::Param(param_def_id.to_def_id()) } _ => ObjectLifetimeDefault::Ambiguous, @@ -1091,8 +1098,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { where F: for<'b> FnOnce(&mut BoundVarContext<'b, 'tcx>), { - let BoundVarContext { tcx, rbv, .. } = self; - let mut this = BoundVarContext { tcx: *tcx, rbv, scope: &wrap_scope }; + let BoundVarContext { tcx, rbv, disambiguator, .. } = self; + let mut this = BoundVarContext { tcx: *tcx, rbv, disambiguator, scope: &wrap_scope }; let span = debug_span!("scope", scope = ?this.scope.debug_truncated()); { let _enter = span.enter(); @@ -1241,7 +1248,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // Fresh lifetimes in APIT used to be allowed in async fns and forbidden in // regular fns. if let Some(hir::PredicateOrigin::ImplTrait) = where_bound_origin - && let hir::LifetimeName::Param(param_id) = lifetime_ref.res + && let hir::LifetimeKind::Param(param_id) = lifetime_ref.kind && let Some(generics) = self.tcx.hir_get_generics(self.tcx.local_parent(param_id)) && let Some(param) = generics.params.iter().find(|p| p.def_id == param_id) @@ -1446,7 +1453,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { #[instrument(level = "trace", skip(self, opaque_capture_scopes), ret)] fn remap_opaque_captures( - &self, + &mut self, opaque_capture_scopes: &Vec<(LocalDefId, &RefCell>)>, mut lifetime: ResolvedArg, ident: Ident, @@ -1462,7 +1469,17 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { for &(opaque_def_id, captures) in opaque_capture_scopes.iter().rev() { let mut captures = captures.borrow_mut(); let remapped = *captures.entry(lifetime).or_insert_with(|| { - let feed = self.tcx.create_def(opaque_def_id, ident.name, DefKind::LifetimeParam); + // `opaque_def_id` is unique to the `BoundVarContext` pass which is executed once + // per `resolve_bound_vars` query. This is the only location that creates + // `OpaqueLifetime` paths. `::OpaqueLifetime(..)` is thus unique + // to this query and duplicates within the query are handled by `self.disambiguator`. + let feed = self.tcx.create_def( + opaque_def_id, + None, + DefKind::LifetimeParam, + Some(DefPathData::OpaqueLifetime(ident.name)), + &mut self.disambiguator, + ); feed.def_span(ident.span); feed.def_ident_span(Some(ident.span)); feed.def_id() @@ -1528,7 +1545,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { if let ResolvedArg::LateBound(..) = def && let Some(what) = crossed_late_boundary { - let use_span = self.tcx.hir().span(hir_id); + let use_span = self.tcx.hir_span(hir_id); let def_span = self.tcx.def_span(param_def_id); let guar = match self.tcx.def_kind(param_def_id) { DefKind::ConstParam => { @@ -1575,11 +1592,11 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } => { let guar = self.tcx.dcx().emit_err(match self.tcx.def_kind(param_def_id) { DefKind::TyParam => errors::LateBoundInApit::Type { - span: self.tcx.hir().span(hir_id), + span: self.tcx.hir_span(hir_id), param_span: self.tcx.def_span(param_def_id), }, DefKind::ConstParam => errors::LateBoundInApit::Const { - span: self.tcx.hir().span(hir_id), + span: self.tcx.hir_span(hir_id), param_span: self.tcx.def_span(param_def_id), }, kind => { @@ -1604,7 +1621,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { self.tcx .dcx() - .span_bug(self.tcx.hir().span(hir_id), format!("could not resolve {param_def_id:?}")); + .span_bug(self.tcx.hir_span(hir_id), format!("could not resolve {param_def_id:?}")); } #[instrument(level = "debug", skip(self))] @@ -1810,7 +1827,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { self.tcx, type_def_id, constraint.ident, - ty::AssocKind::Fn, + ty::AssocTag::Fn, ) { bound_vars.extend( self.tcx @@ -1842,7 +1859,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { self.tcx, type_def_id, constraint.ident, - ty::AssocKind::Type, + ty::AssocTag::Type, ) .map(|(bound_vars, _)| bound_vars); self.with(scope, |this| { @@ -1873,14 +1890,14 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { fn supertrait_hrtb_vars( tcx: TyCtxt<'tcx>, def_id: DefId, - assoc_name: Ident, - assoc_kind: ty::AssocKind, + assoc_ident: Ident, + assoc_tag: ty::AssocTag, ) -> Option<(Vec, &'tcx ty::AssocItem)> { let trait_defines_associated_item_named = |trait_def_id: DefId| { - tcx.associated_items(trait_def_id).find_by_name_and_kind( + tcx.associated_items(trait_def_id).find_by_ident_and_kind( tcx, - assoc_name, - assoc_kind, + assoc_ident, + assoc_tag, trait_def_id, ) }; @@ -1893,8 +1910,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { let Some((def_id, bound_vars)) = stack.pop() else { break None; }; - // See issue #83753. If someone writes an associated type on a non-trait, just treat it as - // there being no supertrait HRTBs. + // See issue #83753. If someone writes an associated type on a non-trait, just treat it + // as there being no supertrait HRTBs. match tcx.def_kind(def_id) { DefKind::Trait | DefKind::TraitAlias | DefKind::Impl { .. } => {} _ => break None, @@ -1903,7 +1920,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { if let Some(assoc_item) = trait_defines_associated_item_named(def_id) { break Some((bound_vars.into_iter().collect(), assoc_item)); } - let predicates = tcx.explicit_supertraits_containing_assoc_item((def_id, assoc_name)); + let predicates = tcx.explicit_supertraits_containing_assoc_item((def_id, assoc_ident)); let obligations = predicates.iter_identity_copied().filter_map(|(pred, _)| { let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { @@ -2066,7 +2083,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { self.tcx, trait_def_id, item_segment.ident, - ty::AssocKind::Fn, + ty::AssocTag::Fn, ) }); @@ -2111,7 +2128,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { self.tcx, trait_def_id, item_segment.ident, - ty::AssocKind::Fn, + ty::AssocTag::Fn, ) else { return; }; @@ -2439,7 +2456,7 @@ fn is_late_bound_map( } fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) { - if let hir::LifetimeName::Param(def_id) = lifetime_ref.res { + if let hir::LifetimeKind::Param(def_id) = lifetime_ref.kind { self.regions.insert(def_id); } } @@ -2452,7 +2469,7 @@ fn is_late_bound_map( impl<'tcx> Visitor<'tcx> for AllCollector { fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { - if let hir::LifetimeName::Param(def_id) = lifetime_ref.res { + if let hir::LifetimeKind::Param(def_id) = lifetime_ref.kind { self.regions.insert(def_id); } } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 6936544838c81..c20b14df7704b 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -5,10 +5,11 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::VisitorExt; use rustc_hir::{self as hir, AmbigArg, HirId}; use rustc_middle::query::plumbing::CyclePlaceholder; -use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{ + self, DefiningScopeKind, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, fold_regions, +}; use rustc_middle::{bug, span_bug}; use rustc_span::{DUMMY_SP, Ident, Span}; @@ -93,10 +94,12 @@ fn const_arg_anon_type_of<'tcx>(icx: &ItemCtxt<'tcx>, arg_hir_id: HirId, span: S } Node::TyPat(pat) => { - let hir::TyKind::Pat(ty, p) = tcx.parent_hir_node(pat.hir_id).expect_ty().kind else { - bug!() + let node = match tcx.parent_hir_node(pat.hir_id) { + // Or patterns can be nested one level deep + Node::TyPat(p) => tcx.parent_hir_node(p.hir_id), + other => other, }; - assert_eq!(p.hir_id, pat.hir_id); + let hir::TyKind::Pat(ty, _) = node.expect_ty().kind else { bug!() }; icx.lower_ty(ty) } @@ -203,35 +206,35 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ }, Node::Item(item) => match item.kind { - ItemKind::Static(ty, .., body_id) => { + ItemKind::Static(ident, ty, .., body_id) => { if ty.is_suggestable_infer_ty() { infer_placeholder_type( icx.lowerer(), def_id, body_id, ty.span, - item.ident, + ident, "static variable", ) } else { icx.lower_ty(ty) } } - ItemKind::Const(ty, _, body_id) => { + ItemKind::Const(ident, ty, _, body_id) => { if ty.is_suggestable_infer_ty() { infer_placeholder_type( icx.lowerer(), def_id, body_id, ty.span, - item.ident, + ident, "constant", ) } else { icx.lower_ty(ty) } } - ItemKind::TyAlias(self_ty, _) => icx.lower_ty(self_ty), + ItemKind::TyAlias(_, self_ty, _) => icx.lower_ty(self_ty), ItemKind::Impl(hir::Impl { self_ty, .. }) => match self_ty.find_self_aliases() { spans if spans.len() > 0 => { let guar = tcx @@ -325,10 +328,18 @@ pub(super) fn type_of_opaque( if let Some(def_id) = def_id.as_local() { Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin { hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => { - opaque::find_opaque_ty_constraints_for_tait(tcx, def_id) + opaque::find_opaque_ty_constraints_for_tait( + tcx, + def_id, + DefiningScopeKind::MirBorrowck, + ) } hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. } => { - opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id) + opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type( + tcx, + def_id, + DefiningScopeKind::MirBorrowck, + ) } // Opaque types desugared from `impl Trait`. hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl } @@ -341,7 +352,12 @@ pub(super) fn type_of_opaque( "tried to get type of this RPITIT with no definition" ); } - opaque::find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) + opaque::find_opaque_ty_constraints_for_rpit( + tcx, + def_id, + owner, + DefiningScopeKind::MirBorrowck, + ) } })) } else { @@ -351,6 +367,42 @@ pub(super) fn type_of_opaque( } } +pub(super) fn type_of_opaque_hir_typeck( + tcx: TyCtxt<'_>, + def_id: LocalDefId, +) -> ty::EarlyBinder<'_, Ty<'_>> { + ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin { + hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => { + opaque::find_opaque_ty_constraints_for_tait(tcx, def_id, DefiningScopeKind::HirTypeck) + } + hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. } => { + opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type( + tcx, + def_id, + DefiningScopeKind::HirTypeck, + ) + } + // Opaque types desugared from `impl Trait`. + hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl } + | hir::OpaqueTyOrigin::AsyncFn { parent: owner, in_trait_or_impl } => { + if in_trait_or_impl == Some(hir::RpitContext::Trait) + && !tcx.defaultness(owner).has_value() + { + span_bug!( + tcx.def_span(def_id), + "tried to get type of this RPITIT with no definition" + ); + } + opaque::find_opaque_ty_constraints_for_rpit( + tcx, + def_id, + owner, + DefiningScopeKind::HirTypeck, + ) + } + }) +} + fn infer_placeholder_type<'tcx>( cx: &dyn HirTyLowerer<'tcx>, def_id: LocalDefId, @@ -480,5 +532,5 @@ pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> } } } - HasTait.visit_ty_unambig(tcx.hir().expect_item(def_id).expect_ty_alias().0).is_break() + HasTait.visit_ty_unambig(tcx.hir_expect_item(def_id).expect_ty_alias().1).is_break() } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index 399c4fbe55a9c..50e20a19edaef 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -1,14 +1,12 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem, def}; +use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem, def, intravisit}; use rustc_middle::bug; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::DUMMY_SP; +use rustc_middle::ty::{self, DefiningScopeKind, Ty, TyCtxt, TypeVisitableExt}; use tracing::{debug, instrument, trace}; -use crate::errors::{TaitForwardCompat, TaitForwardCompat2, UnconstrainedOpaqueType}; +use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType}; /// Checks "defining uses" of opaque `impl Trait` in associated types. /// These can only be defined by associated items of the same trait. @@ -16,6 +14,7 @@ use crate::errors::{TaitForwardCompat, TaitForwardCompat2, UnconstrainedOpaqueTy pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( tcx: TyCtxt<'_>, def_id: LocalDefId, + opaque_types_from: DefiningScopeKind, ) -> Ty<'_> { let mut parent_def_id = def_id; while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy { @@ -28,37 +27,28 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( other => bug!("invalid impl trait in assoc type parent: {other:?}"), } - let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] }; + let mut locator = TaitConstraintLocator { def_id, tcx, found: None, opaque_types_from }; for &assoc_id in tcx.associated_item_def_ids(impl_def_id) { let assoc = tcx.associated_item(assoc_id); match assoc.kind { - ty::AssocKind::Const | ty::AssocKind::Fn => locator.check(assoc_id.expect_local()), + ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => { + locator.check(assoc_id.expect_local()) + } // Associated types don't have bodies, so they can't constrain hidden types - ty::AssocKind::Type => {} + ty::AssocKind::Type { .. } => {} } } if let Some(hidden) = locator.found { - // Only check against typeck if we didn't already error - if !hidden.ty.references_error() { - for concrete_type in locator.typeck_types { - if concrete_type.ty != tcx.erase_regions(hidden.ty) { - if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) { - d.emit(); - } - } - } - } - hidden.ty } else { - let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType { + let guar = tcx.dcx().emit_err(UnconstrainedOpaqueType { span: tcx.def_span(def_id), name: tcx.item_ident(parent_def_id.to_def_id()), what: "impl", }); - Ty::new_error(tcx, reported) + Ty::new_error(tcx, guar) } } @@ -81,52 +71,16 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( /// fn b() -> Foo { .. } /// ``` #[instrument(skip(tcx), level = "debug")] -pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { - let hir_id = tcx.local_def_id_to_hir_id(def_id); - let scope = tcx.hir_get_defining_scope(hir_id); - let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] }; - - debug!(?scope); +pub(super) fn find_opaque_ty_constraints_for_tait( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + opaque_types_from: DefiningScopeKind, +) -> Ty<'_> { + let mut locator = TaitConstraintLocator { def_id, tcx, found: None, opaque_types_from }; - if scope == hir::CRATE_HIR_ID { - tcx.hir_walk_toplevel_module(&mut locator); - } else { - trace!("scope={:#?}", tcx.hir_node(scope)); - match tcx.hir_node(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(it) => locator.visit_item(it), - Node::ImplItem(it) => locator.visit_impl_item(it), - Node::TraitItem(it) => locator.visit_trait_item(it), - Node::ForeignItem(it) => locator.visit_foreign_item(it), - other => bug!("{:?} is not a valid scope for an opaque type item", other), - } - } + tcx.hir_walk_toplevel_module(&mut locator); if let Some(hidden) = locator.found { - // Only check against typeck if we didn't already error - if !hidden.ty.references_error() { - for concrete_type in locator.typeck_types { - if concrete_type.ty != tcx.erase_regions(hidden.ty) { - if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) { - d.emit(); - } - } - } - } - hidden.ty } else { let mut parent_def_id = def_id; @@ -134,17 +88,12 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local // Account for `type Alias = impl Trait;` (#116031) parent_def_id = tcx.local_parent(parent_def_id); } - let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType { + let guar = tcx.dcx().emit_err(UnconstrainedOpaqueType { span: tcx.def_span(def_id), name: tcx.item_ident(parent_def_id.to_def_id()), - what: match tcx.hir_node(scope) { - _ if scope == hir::CRATE_HIR_ID => "module", - Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module", - Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => "impl", - _ => "item", - }, + what: "crate", }); - Ty::new_error(tcx, reported) + Ty::new_error(tcx, guar) } } @@ -161,26 +110,55 @@ struct TaitConstraintLocator<'tcx> { /// type). found: Option>, - /// In the presence of dead code, typeck may figure out a hidden type - /// while borrowck will not. We collect these cases here and check at - /// the end that we actually found a type that matches (modulo regions). - typeck_types: Vec>, + opaque_types_from: DefiningScopeKind, } -impl TaitConstraintLocator<'_> { +impl<'tcx> TaitConstraintLocator<'tcx> { + fn insert_found(&mut self, hidden_ty: ty::OpaqueHiddenType<'tcx>) { + if let Some(prev) = &mut self.found { + if hidden_ty.ty != prev.ty { + let (Ok(guar) | Err(guar)) = + prev.build_mismatch_error(&hidden_ty, self.tcx).map(|d| d.emit()); + prev.ty = Ty::new_error(self.tcx, guar); + } + } else { + self.found = Some(hidden_ty); + } + } + + fn non_defining_use_in_defining_scope(&mut self, item_def_id: LocalDefId) { + let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 { + span: self + .tcx + .def_ident_span(item_def_id) + .unwrap_or_else(|| self.tcx.def_span(item_def_id)), + opaque_type_span: self.tcx.def_span(self.def_id), + opaque_type: self.tcx.def_path_str(self.def_id), + }); + self.insert_found(ty::OpaqueHiddenType::new_error(self.tcx, guar)); + } + #[instrument(skip(self), level = "debug")] fn check(&mut self, item_def_id: LocalDefId) { // Don't try to check items that cannot possibly constrain the type. - if !self.tcx.has_typeck_results(item_def_id) { + let tcx = self.tcx; + if !tcx.has_typeck_results(item_def_id) { debug!("no constraint: no typeck results"); return; } + let opaque_types_defined_by = tcx.opaque_types_defined_by(item_def_id); + // Don't try to check items that cannot possibly constrain the type. + if !opaque_types_defined_by.contains(&self.def_id) { + debug!("no constraint: no opaque types defined"); + return; + } + // Function items with `_` in their return type already emit an error, skip any // "non-defining use" errors for them. // Note that we use `Node::fn_sig` instead of `Node::fn_decl` here, because the former // excludes closures, which are allowed to have `_` in their return type. - let hir_node = self.tcx.hir_node_by_def_id(item_def_id); + let hir_node = tcx.hir_node_by_def_id(item_def_id); debug_assert!( !matches!(hir_node, Node::ForeignItem(..)), "foreign items cannot constrain opaque types", @@ -192,105 +170,38 @@ impl TaitConstraintLocator<'_> { hir_sig.decl.output.span(), "inferring return types and opaque types do not mix well", ); - self.found = - Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) }); - return; - } - - // Calling `mir_borrowck` can lead to cycle errors through - // const-checking, avoid calling it if we don't have to. - // ```rust - // type Foo = impl Fn() -> usize; // when computing type for this - // const fn bar() -> Foo { - // || 0usize - // } - // const BAZR: Foo = bar(); // we would mir-borrowck this, causing cycles - // // because we again need to reveal `Foo` so we can check whether the - // // constant does not contain interior mutability. - // ``` - let tables = self.tcx.typeck(item_def_id); - if let Some(guar) = tables.tainted_by_errors { - self.found = - Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) }); - return; - } - - let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id); - - let mut constrained = false; - for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types { - if opaque_type_key.def_id != self.def_id { - continue; - } - constrained = true; - - if !opaque_types_defined_by.contains(&self.def_id) { - let guar = self.tcx.dcx().emit_err(TaitForwardCompat { - span: hidden_type.span, - item_span: self - .tcx - .def_ident_span(item_def_id) - .unwrap_or_else(|| self.tcx.def_span(item_def_id)), - }); - // Avoid "opaque type not constrained" errors on the opaque itself. - self.found = Some(ty::OpaqueHiddenType { - span: DUMMY_SP, - ty: Ty::new_error(self.tcx, guar), - }); - } - let concrete_type = - self.tcx.erase_regions(hidden_type.remap_generic_params_to_declaration_params( - opaque_type_key, - self.tcx, - true, - )); - if self.typeck_types.iter().all(|prev| prev.ty != concrete_type.ty) { - self.typeck_types.push(concrete_type); - } - } - - if !constrained { - debug!("no constraints in typeck results"); - if opaque_types_defined_by.contains(&self.def_id) { - let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 { - span: self - .tcx - .def_ident_span(item_def_id) - .unwrap_or_else(|| self.tcx.def_span(item_def_id)), - opaque_type_span: self.tcx.def_span(self.def_id), - opaque_type: self.tcx.def_path_str(self.def_id), - }); - // Avoid "opaque type not constrained" errors on the opaque itself. - self.found = Some(ty::OpaqueHiddenType { - span: DUMMY_SP, - ty: Ty::new_error(self.tcx, guar), - }); - } - return; - }; - - // Use borrowck to get the type with unerased regions. - let borrowck_results = &self.tcx.mir_borrowck(item_def_id); - - // If the body was tainted, then assume the opaque may have been constrained and just set it to error. - if let Some(guar) = borrowck_results.tainted_by_errors { - self.found = - Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) }); + self.found = Some(ty::OpaqueHiddenType::new_error(tcx, guar)); return; } - debug!(?borrowck_results.concrete_opaque_types); - if let Some(&concrete_type) = borrowck_results.concrete_opaque_types.get(&self.def_id) { - debug!(?concrete_type, "found constraint"); - if let Some(prev) = &mut self.found { - if concrete_type.ty != prev.ty { - let (Ok(guar) | Err(guar)) = - prev.build_mismatch_error(&concrete_type, self.tcx).map(|d| d.emit()); - prev.ty = Ty::new_error(self.tcx, guar); + match self.opaque_types_from { + DefiningScopeKind::HirTypeck => { + let tables = tcx.typeck(item_def_id); + if let Some(guar) = tables.tainted_by_errors { + self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar)); + } else if let Some(&hidden_type) = tables.concrete_opaque_types.get(&self.def_id) { + self.insert_found(hidden_type); + } else { + self.non_defining_use_in_defining_scope(item_def_id); } - } else { - self.found = Some(concrete_type); } + DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(item_def_id) { + Err(guar) => self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar)), + Ok(concrete_opaque_types) => { + if let Some(&hidden_type) = concrete_opaque_types.0.get(&self.def_id) { + debug!(?hidden_type, "found constraint"); + self.insert_found(hidden_type); + } else if let Err(guar) = tcx + .type_of_opaque_hir_typeck(self.def_id) + .instantiate_identity() + .error_reported() + { + self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar)); + } else { + self.non_defining_use_in_defining_scope(item_def_id); + } + } + }, } } } @@ -302,26 +213,17 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { self.tcx } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Closure(closure) = ex.kind { - self.check(closure.def_id); - } intravisit::walk_expr(self, ex); } fn visit_item(&mut self, it: &'tcx Item<'tcx>) { trace!(?it.owner_id); - // The opaque type itself or its children are not within its reveal scope. - if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); - intravisit::walk_item(self, it); - } + self.check(it.owner_id.def_id); + intravisit::walk_item(self, it); } fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { trace!(?it.owner_id); - // The opaque type itself or its children are not within its reveal scope. - if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); - intravisit::walk_impl_item(self, it); - } + self.check(it.owner_id.def_id); + intravisit::walk_impl_item(self, it); } fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { trace!(?it.owner_id); @@ -340,135 +242,42 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, owner_def_id: LocalDefId, + opaque_types_from: DefiningScopeKind, ) -> Ty<'tcx> { - let tables = tcx.typeck(owner_def_id); - - // Check that all of the opaques we inferred during HIR are compatible. - // FIXME: We explicitly don't check that the types inferred during HIR - // typeck are compatible with the one that we infer during borrowck, - // because that one actually sometimes has consts evaluated eagerly so - // using strict type equality will fail. - let mut hir_opaque_ty: Option> = None; - if tables.tainted_by_errors.is_none() { - for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types { - if opaque_type_key.def_id != def_id { - continue; - } - let concrete_type = tcx.erase_regions( - hidden_type.remap_generic_params_to_declaration_params(opaque_type_key, tcx, true), - ); - if let Some(prev) = &mut hir_opaque_ty { - if concrete_type.ty != prev.ty { - if let Ok(d) = prev.build_mismatch_error(&concrete_type, tcx) { - d.emit(); - } - } + match opaque_types_from { + DefiningScopeKind::HirTypeck => { + let tables = tcx.typeck(owner_def_id); + if let Some(guar) = tables.tainted_by_errors { + Ty::new_error(tcx, guar) + } else if let Some(hidden_ty) = tables.concrete_opaque_types.get(&def_id) { + hidden_ty.ty } else { - hir_opaque_ty = Some(concrete_type); + // FIXME(-Znext-solver): This should not be necessary and we should + // instead rely on inference variable fallback inside of typeck itself. + + // We failed to resolve the opaque type or it + // resolves to itself. We interpret this as the + // no values of the hidden type ever being constructed, + // so we can just make the hidden type be `!`. + // For backwards compatibility reasons, we fall back to + // `()` until we the diverging default is changed. + Ty::new_diverging_default(tcx) } } - } - - let mir_opaque_ty = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied(); - if let Some(mir_opaque_ty) = mir_opaque_ty { - if mir_opaque_ty.references_error() { - return mir_opaque_ty.ty; - } - - debug!(?owner_def_id); - let mut locator = RpitConstraintChecker { def_id, tcx, found: mir_opaque_ty }; - - match tcx.hir_node_by_def_id(owner_def_id) { - Node::Item(it) => intravisit::walk_item(&mut locator, it), - Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it), - Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it), - other => bug!("{:?} is not a valid scope for an opaque type item", other), - } - - mir_opaque_ty.ty - } else if let Some(guar) = tables.tainted_by_errors { - // Some error in the owner fn prevented us from populating - // the `concrete_opaque_types` table. - Ty::new_error(tcx, guar) - } else { - // Fall back to the RPIT we inferred during HIR typeck - if let Some(hir_opaque_ty) = hir_opaque_ty { - hir_opaque_ty.ty - } else { - // We failed to resolve the opaque type or it - // resolves to itself. We interpret this as the - // no values of the hidden type ever being constructed, - // so we can just make the hidden type be `!`. - // For backwards compatibility reasons, we fall back to - // `()` until we the diverging default is changed. - Ty::new_diverging_default(tcx) - } - } -} - -struct RpitConstraintChecker<'tcx> { - tcx: TyCtxt<'tcx>, - - /// def_id of the opaque type whose defining uses are being checked - def_id: LocalDefId, - - found: ty::OpaqueHiddenType<'tcx>, -} - -impl RpitConstraintChecker<'_> { - #[instrument(skip(self), level = "debug")] - fn check(&self, def_id: LocalDefId) { - // Use borrowck to get the type with unerased regions. - let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types; - debug!(?concrete_opaque_types); - for (&def_id, &concrete_type) in concrete_opaque_types { - if def_id != self.def_id { - // Ignore constraints for other opaque types. - continue; - } - - debug!(?concrete_type, "found constraint"); - - if concrete_type.ty != self.found.ty { - if let Ok(d) = self.found.build_mismatch_error(&concrete_type, self.tcx) { - d.emit(); + DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(owner_def_id) { + Ok(concrete_opaque_types) => { + if let Some(hidden_ty) = concrete_opaque_types.0.get(&def_id) { + hidden_ty.ty + } else { + let hir_ty = tcx.type_of_opaque_hir_typeck(def_id).instantiate_identity(); + if let Err(guar) = hir_ty.error_reported() { + Ty::new_error(tcx, guar) + } else { + hir_ty + } } } - } - } -} - -impl<'tcx> intravisit::Visitor<'tcx> for RpitConstraintChecker<'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.tcx - } - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Closure(closure) = ex.kind { - self.check(closure.def_id); - } - intravisit::walk_expr(self, ex); - } - fn visit_item(&mut self, it: &'tcx Item<'tcx>) { - trace!(?it.owner_id); - // The opaque type itself or its children are not within its reveal scope. - if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); - intravisit::walk_item(self, it); - } - } - fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { - trace!(?it.owner_id); - // The opaque type itself or its children are not within its reveal scope. - if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); - intravisit::walk_impl_item(self, it); - } - } - fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { - trace!(?it.owner_id); - self.check(it.owner_id.def_id); - intravisit::walk_trait_item(self, it); + Err(guar) => Ty::new_error(tcx, guar), + }, } } diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 6a9ae0de1c1ab..366b3943a0589 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -1,9 +1,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; -use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitor}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitor}; use rustc_span::Span; -use rustc_type_ir::fold::TypeFoldable; use tracing::debug; #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -51,7 +49,7 @@ pub(crate) fn parameters_for<'tcx>( include_nonconstraining: bool, ) -> Vec { let mut collector = ParameterCollector { parameters: vec![], include_nonconstraining }; - let value = if !include_nonconstraining { tcx.expand_weak_alias_tys(value) } else { value }; + let value = if !include_nonconstraining { tcx.expand_free_alias_tys(value) } else { value }; value.visit_with(&mut collector); collector.parameters } @@ -70,9 +68,9 @@ impl<'tcx> TypeVisitor> for ParameterCollector { { return; } - // All weak alias types should've been expanded beforehand. - ty::Alias(ty::Weak, _) if !self.include_nonconstraining => { - bug!("unexpected weak alias type") + // All free alias types should've been expanded beforehand. + ty::Alias(ty::Free, _) if !self.include_nonconstraining => { + bug!("unexpected free alias type") } ty::Param(param) => self.parameters.push(Parameter::from(param)), _ => {} @@ -82,7 +80,7 @@ impl<'tcx> TypeVisitor> for ParameterCollector { } fn visit_region(&mut self, r: ty::Region<'tcx>) { - if let ty::ReEarlyParam(data) = *r { + if let ty::ReEarlyParam(data) = r.kind() { self.parameters.push(Parameter::from(data)); } } diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index 4dbdfa3d85a9b..b907ec090e5d8 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -7,10 +7,10 @@ use std::assert_matches::debug_assert_matches; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, +}; use rustc_span::{ErrorGuaranteed, Span}; -use rustc_type_ir::visit::TypeVisitableExt; type RemapTable = FxHashMap; @@ -404,11 +404,16 @@ fn check_constraints<'tcx>( }; if let Some(local_sig_id) = sig_id.as_local() - && tcx.hir().opt_delegation_sig_id(local_sig_id).is_some() + && tcx.hir_opt_delegation_sig_id(local_sig_id).is_some() { emit("recursive delegation is not supported yet"); } + if tcx.fn_sig(sig_id).skip_binder().skip_binder().c_variadic { + // See issue #127443 for explanation. + emit("delegation to C-variadic functions is not allowed"); + } + ret } @@ -416,7 +421,7 @@ pub(crate) fn inherit_sig_for_delegation_item<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, ) -> &'tcx [Ty<'tcx>] { - let sig_id = tcx.hir().opt_delegation_sig_id(def_id).unwrap(); + let sig_id = tcx.hir_opt_delegation_sig_id(def_id).unwrap(); let caller_sig = tcx.fn_sig(sig_id); if let Err(err) = check_constraints(tcx, def_id, sig_id) { let sig_len = caller_sig.instantiate_identity().skip_binder().inputs().len() + 1; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 852533ff5c954..152714b340731 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -11,8 +11,6 @@ use rustc_middle::ty::Ty; use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; -mod pattern_types; -pub(crate) use pattern_types::*; pub(crate) mod wrong_number_of_generic_args; mod precise_captures; @@ -25,7 +23,7 @@ pub(crate) struct AmbiguousAssocItem<'a> { #[label] pub span: Span, pub assoc_kind: &'static str, - pub assoc_name: Ident, + pub assoc_ident: Ident, pub qself: &'a str, } @@ -77,13 +75,15 @@ pub(crate) struct AssocItemIsPrivate { pub(crate) struct AssocItemNotFound<'a> { #[primary_span] pub span: Span, - pub assoc_name: Ident, + pub assoc_ident: Ident, pub assoc_kind: &'static str, pub qself: &'a str, #[subdiagnostic] pub label: Option>, #[subdiagnostic] pub sugg: Option>, + #[label(hir_analysis_within_macro)] + pub within_macro_span: Option, } #[derive(Subdiagnostic)] @@ -424,16 +424,6 @@ pub(crate) struct UnconstrainedOpaqueType { pub what: &'static str, } -#[derive(Diagnostic)] -#[diag(hir_analysis_tait_forward_compat)] -#[note] -pub(crate) struct TaitForwardCompat { - #[primary_span] - pub span: Span, - #[note] - pub item_span: Span, -} - #[derive(Diagnostic)] #[diag(hir_analysis_tait_forward_compat2)] #[note] @@ -710,17 +700,6 @@ pub(crate) struct InvalidUnionField { pub note: (), } -#[derive(Diagnostic)] -#[diag(hir_analysis_invalid_unsafe_field, code = E0740)] -pub(crate) struct InvalidUnsafeField { - #[primary_span] - pub field_span: Span, - #[subdiagnostic] - pub sugg: InvalidUnsafeFieldSuggestion, - #[note] - pub note: (), -} - #[derive(Diagnostic)] #[diag(hir_analysis_return_type_notation_on_non_rpitit)] pub(crate) struct ReturnTypeNotationOnNonRpitit<'tcx> { @@ -742,18 +721,6 @@ pub(crate) struct InvalidUnionFieldSuggestion { pub hi: Span, } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - hir_analysis_invalid_unsafe_field_sugg, - applicability = "machine-applicable" -)] -pub(crate) struct InvalidUnsafeFieldSuggestion { - #[suggestion_part(code = "std::mem::ManuallyDrop<")] - pub lo: Span, - #[suggestion_part(code = ">")] - pub hi: Span, -} - #[derive(Diagnostic)] #[diag(hir_analysis_return_type_notation_equality_bound)] pub(crate) struct ReturnTypeNotationEqualityBound { @@ -1360,7 +1327,7 @@ pub(crate) struct ImplForTyRequires { } #[derive(Diagnostic)] -#[diag(hir_analysis_traits_with_defualt_impl, code = E0321)] +#[diag(hir_analysis_traits_with_default_impl, code = E0321)] #[note] pub(crate) struct TraitsWithDefaultImpl<'a> { #[primary_span] @@ -1708,14 +1675,6 @@ pub(crate) struct CmseEntryGeneric { pub span: Span, } -#[derive(Diagnostic)] -#[diag(hir_analysis_register_type_unstable)] -pub(crate) struct RegisterTypeUnstable<'a> { - #[primary_span] - pub span: Span, - pub ty: Ty<'a>, -} - #[derive(LintDiagnostic)] #[diag(hir_analysis_supertrait_item_shadowing)] pub(crate) struct SupertraitItemShadowing { @@ -1740,3 +1699,11 @@ pub(crate) enum SupertraitItemShadowee { traits: DiagSymbolList, }, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_self_in_type_alias, code = E0411)] +pub(crate) struct SelfInTypeAlias { + #[primary_span] + #[label] + pub span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/errors/pattern_types.rs b/compiler/rustc_hir_analysis/src/errors/pattern_types.rs deleted file mode 100644 index ec7b3aaa1c14c..0000000000000 --- a/compiler/rustc_hir_analysis/src/errors/pattern_types.rs +++ /dev/null @@ -1,14 +0,0 @@ -use rustc_macros::Diagnostic; -use rustc_middle::ty::Ty; -use rustc_span::Span; - -#[derive(Diagnostic)] -#[diag(hir_analysis_invalid_base_type)] -pub(crate) struct InvalidBaseType<'tcx> { - pub ty: Ty<'tcx>, - #[primary_span] - pub ty_span: Span, - pub pat: &'static str, - #[note] - pub pat_span: Span, -} diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 610b293a114e6..a3c8ce620b366 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -4,7 +4,7 @@ use GenericArgsInfo::*; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, Diagnostic, EmissionGuarantee, MultiSpan, pluralize}; use rustc_hir as hir; -use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt}; +use rustc_middle::ty::{self as ty, AssocItems, TyCtxt}; use rustc_span::def_id::DefId; use tracing::debug; @@ -486,15 +486,15 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let items: &AssocItems = self.tcx.associated_items(self.def_id); items .in_definition_order() - .filter(|item| item.kind == AssocKind::Type) .filter(|item| { - !self - .gen_args - .constraints - .iter() - .any(|constraint| constraint.ident.name == item.name) + item.is_type() + && !item.is_impl_trait_in_trait() + && !self + .gen_args + .constraints + .iter() + .any(|constraint| constraint.ident.name == item.name()) }) - .filter(|item| !item.is_impl_trait_in_trait()) .map(|item| self.tcx.item_ident(item.def_id).to_string()) .collect() } else { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index dd346ed1f97a5..4419d5dc7d663 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -4,14 +4,16 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; -use rustc_hir::HirId; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{AmbigArg, HirId}; use rustc_middle::bug; -use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt, Upcast}; +use rustc_middle::ty::{ + self as ty, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, Upcast, +}; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::traits; -use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -22,25 +24,231 @@ use crate::hir_ty_lowering::{ }; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { - /// Add a `Sized` bound to the `bounds` if appropriate. - /// - /// Doesn't add the bound if the HIR bounds contain any of `Sized`, `?Sized` or `!Sized`. - pub(crate) fn add_sized_bound( + pub(crate) fn add_default_traits( &self, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, self_ty: Ty<'tcx>, + hir_bounds: &[hir::GenericBound<'tcx>], + self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, + span: Span, + ) { + self.add_default_traits_with_filter( + bounds, + self_ty, + hir_bounds, + self_ty_where_predicates, + span, + |_| true, + ); + } + + /// Checks whether `Self: DefaultAutoTrait` bounds should be added on trait super bounds + /// or associated items. + /// + /// To keep backward compatibility with existing code, `experimental_default_bounds` bounds + /// should be added everywhere, including super bounds. However this causes a huge performance + /// costs. For optimization purposes instead of adding default supertraits, bounds + /// are added to the associated items: + /// + /// ```ignore(illustrative) + /// // Default bounds are generated in the following way: + /// trait Trait { + /// fn foo(&self) where Self: Leak {} + /// } + /// + /// // instead of this: + /// trait Trait: Leak { + /// fn foo(&self) {} + /// } + /// ``` + /// It is not always possible to do this because of backward compatibility: + /// + /// ```ignore(illustrative) + /// pub trait Trait {} + /// pub trait Trait1 : Trait {} + /// //~^ ERROR: `Rhs` requires `DefaultAutoTrait`, but `Self` is not `DefaultAutoTrait` + /// ``` + /// + /// or: + /// + /// ```ignore(illustrative) + /// trait Trait { + /// type Type where Self: Sized; + /// } + /// trait Trait2 : Trait {} + /// //~^ ERROR: `DefaultAutoTrait` required for `Trait2`, by implicit `Self: DefaultAutoTrait` in `Trait::Type` + /// ``` + /// + /// Therefore, `experimental_default_bounds` are still being added to supertraits if + /// the `SelfTyParam` or `AssocItemConstraint` were found in a trait header. + fn requires_default_supertraits( + &self, hir_bounds: &'tcx [hir::GenericBound<'tcx>], + hir_generics: &'tcx hir::Generics<'tcx>, + ) -> bool { + struct TraitInfoCollector; + + impl<'tcx> hir::intravisit::Visitor<'tcx> for TraitInfoCollector { + type Result = ControlFlow<()>; + + fn visit_assoc_item_constraint( + &mut self, + _constraint: &'tcx hir::AssocItemConstraint<'tcx>, + ) -> Self::Result { + ControlFlow::Break(()) + } + + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { + if matches!( + &t.kind, + hir::TyKind::Path(hir::QPath::Resolved( + _, + hir::Path { res: hir::def::Res::SelfTyParam { .. }, .. }, + )) + ) { + return ControlFlow::Break(()); + } + hir::intravisit::walk_ty(self, t) + } + } + + let mut found = false; + for bound in hir_bounds { + found |= hir::intravisit::walk_param_bound(&mut TraitInfoCollector, bound).is_break(); + } + found |= hir::intravisit::walk_generics(&mut TraitInfoCollector, hir_generics).is_break(); + found + } + + /// Implicitly add `Self: DefaultAutoTrait` clauses on trait associated items if + /// they are not added as super trait bounds to the trait itself. See + /// `requires_default_supertraits` for more information. + pub(crate) fn add_default_trait_item_bounds( + &self, + trait_item: &hir::TraitItem<'tcx>, + bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, + ) { + let tcx = self.tcx(); + if !tcx.sess.opts.unstable_opts.experimental_default_bounds { + return; + } + + let parent = tcx.local_parent(trait_item.hir_id().owner.def_id); + let hir::Node::Item(parent_trait) = tcx.hir_node_by_def_id(parent) else { + unreachable!(); + }; + + let (trait_generics, trait_bounds) = match parent_trait.kind { + hir::ItemKind::Trait(_, _, _, generics, supertraits, _) => (generics, supertraits), + hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits), + _ => unreachable!(), + }; + + if !self.requires_default_supertraits(trait_bounds, trait_generics) { + let self_ty_where_predicates = (parent, trait_item.generics.predicates); + self.add_default_traits_with_filter( + bounds, + tcx.types.self_param, + &[], + Some(self_ty_where_predicates), + trait_item.span, + |tr| tr != hir::LangItem::Sized, + ); + } + } + + /// Lazily sets `experimental_default_bounds` to true on trait super bounds. + /// See `requires_default_supertraits` for more information. + pub(crate) fn add_default_super_traits( + &self, + trait_def_id: LocalDefId, + bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, + hir_bounds: &'tcx [hir::GenericBound<'tcx>], + hir_generics: &'tcx hir::Generics<'tcx>, + span: Span, + ) { + if !self.tcx().sess.opts.unstable_opts.experimental_default_bounds { + return; + } + + assert!(matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias)); + if self.requires_default_supertraits(hir_bounds, hir_generics) { + let self_ty_where_predicates = (trait_def_id, hir_generics.predicates); + self.add_default_traits_with_filter( + bounds, + self.tcx().types.self_param, + hir_bounds, + Some(self_ty_where_predicates), + span, + |default_trait| default_trait != hir::LangItem::Sized, + ); + } + } + + pub(crate) fn add_default_traits_with_filter( + &self, + bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, + self_ty: Ty<'tcx>, + hir_bounds: &[hir::GenericBound<'tcx>], self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, span: Span, + f: impl Fn(hir::LangItem) -> bool, ) { + self.tcx().default_traits().iter().filter(|&&default_trait| f(default_trait)).for_each( + |default_trait| { + self.add_default_trait( + *default_trait, + bounds, + self_ty, + hir_bounds, + self_ty_where_predicates, + span, + ); + }, + ); + } + + /// Add a `Sized` or `experimental_default_bounds` bounds to the `bounds` if appropriate. + /// + /// Doesn't add the bound if the HIR bounds contain any of `Trait`, `?Trait` or `!Trait`. + pub(crate) fn add_default_trait( + &self, + trait_: hir::LangItem, + bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, + self_ty: Ty<'tcx>, + hir_bounds: &[hir::GenericBound<'tcx>], + self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, + span: Span, + ) { + let trait_id = self.tcx().lang_items().get(trait_); + if let Some(trait_id) = trait_id + && self.do_not_provide_default_trait_bound( + trait_id, + hir_bounds, + self_ty_where_predicates, + ) + { + // There was no `?Trait` or `!Trait` bound; + // add `Trait` if it's available. + let trait_ref = ty::TraitRef::new(self.tcx(), trait_id, [self_ty]); + // Preferable to put this obligation first, since we report better errors for sized ambiguity. + bounds.insert(0, (trait_ref.upcast(self.tcx()), span)); + } + } + + fn do_not_provide_default_trait_bound<'a>( + &self, + trait_def_id: DefId, + hir_bounds: &'a [hir::GenericBound<'tcx>], + self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, + ) -> bool { let tcx = self.tcx(); - let sized_def_id = tcx.lang_items().sized_trait(); - let mut seen_negative_sized_bound = false; - let mut seen_positive_sized_bound = false; + let mut seen_negative_bound = false; + let mut seen_positive_bound = false; // Try to find an unbound in bounds. let mut unbounds: SmallVec<[_; 1]> = SmallVec::new(); - let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| { + let mut search_bounds = |hir_bounds: &'a [hir::GenericBound<'tcx>]| { for hir_bound in hir_bounds { let hir::GenericBound::Trait(ptr) = hir_bound else { continue; @@ -48,17 +256,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { match ptr.modifiers.polarity { hir::BoundPolarity::Maybe(_) => unbounds.push(ptr), hir::BoundPolarity::Negative(_) => { - if let Some(sized_def_id) = sized_def_id - && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) - { - seen_negative_sized_bound = true; + if ptr.trait_ref.path.res == Res::Def(DefKind::Trait, trait_def_id) { + seen_negative_bound = true; } } hir::BoundPolarity::Positive => { - if let Some(sized_def_id) = sized_def_id - && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) - { - seen_positive_sized_bound = true; + if ptr.trait_ref.path.res == Res::Def(DefKind::Trait, trait_def_id) { + seen_positive_bound = true; } } } @@ -93,32 +297,35 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; } - let mut seen_sized_unbound = false; + let mut seen_unbound = false; for unbound in unbounds { - if let Some(sized_def_id) = sized_def_id - && unbound.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) - { - seen_sized_unbound = true; - continue; + let unbound_def_id = unbound.trait_ref.trait_def_id(); + if unbound_def_id == Some(trait_def_id) { + seen_unbound = true; + } + let emit_relax_err = || { + let unbound_traits = match tcx.sess.opts.unstable_opts.experimental_default_bounds { + true => "`?Sized` and `experimental_default_bounds`", + false => "`?Sized`", + }; + // There was a `?Trait` bound, but it was neither `?Sized` nor `experimental_default_bounds`. + tcx.dcx().span_err( + unbound.span, + format!( + "relaxing a default bound only does something for {}; \ + all other traits are not bound by default", + unbound_traits + ), + ); + }; + match unbound_def_id { + Some(def_id) if !tcx.is_default_trait(def_id) => emit_relax_err(), + None => emit_relax_err(), + _ => {} } - // There was a `?Trait` bound, but it was not `?Sized` - self.dcx().span_err( - unbound.span, - "relaxing a default bound only does something for `?Sized`; \ - all other traits are not bound by default", - ); } - if seen_sized_unbound || seen_negative_sized_bound || seen_positive_sized_bound { - // There was in fact a `?Sized`, `!Sized` or explicit `Sized` bound; - // we don't need to do anything. - } else if let Some(sized_def_id) = sized_def_id { - // There was no `?Sized`, `!Sized` or explicit `Sized` bound; - // add `Sized` if it's available. - let trait_ref = ty::TraitRef::new(tcx, sized_def_id, [self_ty]); - // Preferable to put this obligation first, since we report better errors for sized ambiguity. - bounds.insert(0, (trait_ref.upcast(tcx), span)); - } + !(seen_unbound || seen_negative_bound || seen_positive_bound) } /// Lower HIR bounds into `bounds` given the self type `param_ty` and the overarching late-bound vars if any. @@ -156,10 +363,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { for hir_bound in hir_bounds { // In order to avoid cycles, when we're lowering `SelfTraitThatDefines`, // we skip over any traits that don't define the given associated type. - if let PredicateFilter::SelfTraitThatDefines(assoc_name) = predicate_filter { + if let PredicateFilter::SelfTraitThatDefines(assoc_ident) = predicate_filter { if let Some(trait_ref) = hir_bound.trait_ref() && let Some(trait_did) = trait_ref.trait_def_id() - && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name) + && self.tcx().trait_may_define_assoc_item(trait_did, assoc_ident) { // Okay } else { @@ -224,16 +431,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) -> Result<(), ErrorGuaranteed> { let tcx = self.tcx(); - let assoc_kind = if constraint.gen_args.parenthesized + let assoc_tag = if constraint.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation { - ty::AssocKind::Fn + ty::AssocTag::Fn } else if let hir::AssocItemConstraintKind::Equality { term: hir::Term::Const(_) } = constraint.kind { - ty::AssocKind::Const + ty::AssocTag::Const } else { - ty::AssocKind::Type + ty::AssocTag::Type }; // Given something like `U: Trait`, we want to produce a predicate like @@ -246,7 +453,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // trait SuperTrait { type T; } let candidate = if self.probe_trait_that_defines_assoc_item( trait_ref.def_id(), - assoc_kind, + assoc_tag, constraint.ident, ) { // Simple case: The assoc item is defined in the current trait. @@ -257,7 +464,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.probe_single_bound_for_assoc_item( || traits::supertraits(tcx, trait_ref), AssocItemQSelf::Trait(trait_ref.def_id()), - assoc_kind, + assoc_tag, constraint.ident, path_span, Some(constraint), @@ -267,7 +474,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let assoc_item = self .probe_assoc_item( constraint.ident, - assoc_kind, + assoc_tag, hir_ref_id, constraint.span, candidate.def_id(), @@ -286,7 +493,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }) .or_insert(constraint.span); - let projection_term = if let ty::AssocKind::Fn = assoc_kind { + let projection_term = if let ty::AssocTag::Fn = assoc_tag { let bound_vars = tcx.late_bound_vars(constraint.hir_id); ty::Binder::bind_with_vars( self.lower_return_type_notation_ty(candidate, assoc_item.def_id, path_span)?.into(), @@ -335,7 +542,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; match constraint.kind { - hir::AssocItemConstraintKind::Equality { .. } if let ty::AssocKind::Fn = assoc_kind => { + hir::AssocItemConstraintKind::Equality { .. } if let ty::AssocTag::Fn = assoc_tag => { return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound { span: constraint.span, })); @@ -468,11 +675,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Good error for `where Trait::method(..): Send`. let Some(self_ty) = opt_self_ty else { - return self.error_missing_qpath_self_ty( + let guar = self.error_missing_qpath_self_ty( trait_def_id, hir_ty.span, item_segment, + ty::AssocTag::Type, ); + return Ty::new_error(tcx, guar); }; let self_ty = self.lower_ty(self_ty); @@ -562,7 +771,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) }, AssocItemQSelf::SelfTyAlias, - ty::AssocKind::Fn, + ty::AssocTag::Fn, assoc_ident, span, None, @@ -574,7 +783,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) => self.probe_single_ty_param_bound_for_assoc_item( param_did.expect_local(), qself.span, - ty::AssocKind::Fn, + ty::AssocTag::Fn, assoc_ident, span, )?, @@ -614,7 +823,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let trait_def_id = bound.def_id(); let assoc_ty = self - .probe_assoc_item(assoc_ident, ty::AssocKind::Fn, qpath_hir_id, span, trait_def_id) + .probe_assoc_item(assoc_ident, ty::AssocTag::Fn, qpath_hir_id, span, trait_def_id) .expect("failed to find associated type"); Ok((bound, assoc_ty.def_id)) @@ -680,7 +889,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit { span: path_span, ty: tcx.liberate_late_bound_regions(item_def_id, output), - fn_span: tcx.hir().span_if_local(item_def_id), + fn_span: tcx.hir_span_if_local(item_def_id), note: (), })); }; @@ -787,7 +996,7 @@ struct GenericParamAndBoundVarCollector<'a, 'tcx> { impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'_, 'tcx> { type Result = ControlFlow; - fn visit_binder>>( + fn visit_binder>>( &mut self, binder: &ty::Binder<'tcx, T>, ) -> Self::Result { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 5fed2e352879c..d1ee5a5494c00 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -32,7 +32,7 @@ pub(crate) fn validate_cmse_abi<'tcx>( span, .. }) => *span, - _ => tcx.hir().span(hir_id), + _ => tcx.hir_span(hir_id), }; struct_span_code_err!( tcx.dcx(), @@ -49,11 +49,13 @@ pub(crate) fn validate_cmse_abi<'tcx>( Ok(Err(index)) => { // fn(x: u32, u32, u32, u16, y: u16) -> u32, // ^^^^^^ - let span = bare_fn_ty.param_names[index] - .span - .to(bare_fn_ty.decl.inputs[index].span) - .to(bare_fn_ty.decl.inputs.last().unwrap().span); - let plural = bare_fn_ty.param_names.len() - index != 1; + let span = if let Some(ident) = bare_fn_ty.param_idents[index] { + ident.span.to(bare_fn_ty.decl.inputs[index].span) + } else { + bare_fn_ty.decl.inputs[index].span + } + .to(bare_fn_ty.decl.inputs.last().unwrap().span); + let plural = bare_fn_ty.param_idents.len() - index != 1; dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi }); } Err(layout_err) => { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 3eb4945ebf80e..f6e5149bd2b17 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -4,19 +4,19 @@ use rustc_errors::struct_span_code_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; -use rustc_middle::ty::fold::BottomUpFolder; +use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan; use rustc_middle::ty::{ - self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, + self, BottomUpFolder, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations}; -use rustc_type_ir::elaborate::ClauseWithSupertraitSpan; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; use super::HirTyLowerer; +use crate::errors::SelfInTypeAlias; use crate::hir_ty_lowering::{ GenericArgCountMismatch, GenericArgCountResult, PredicateFilter, RegionInferReason, }; @@ -58,6 +58,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + let ast_bounds: Vec<_> = + hir_bounds.iter().map(|&trait_ref| hir::GenericBound::Trait(trait_ref)).collect(); + + self.add_default_traits_with_filter( + &mut user_written_bounds, + dummy_self, + &ast_bounds, + None, + span, + |tr| tr != hir::LangItem::Sized, + ); + let (elaborated_trait_bounds, elaborated_projection_bounds) = traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied()); let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds @@ -114,6 +126,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // ``` let mut projection_bounds = FxIndexMap::default(); for (proj, proj_span) in elaborated_projection_bounds { + let proj = proj.map_bound(|mut b| { + if let Some(term_ty) = &b.term.as_type() { + let references_self = term_ty.walk().any(|arg| arg == dummy_self.into()); + if references_self { + // With trait alias and type alias combined, type resolver + // may not be able to catch all illegal `Self` usages (issue 139082) + let guar = tcx.dcx().emit_err(SelfInTypeAlias { span }); + b.term = replace_dummy_self_with_error(tcx, b.term, guar); + } + } + b + }); + let key = ( proj.skip_binder().projection_term.def_id, tcx.anonymize_bound_vars( @@ -147,7 +172,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let principal_trait = regular_traits.into_iter().next(); - let mut needed_associated_types = vec![]; + // A stable ordering of associated types from the principal trait and all its + // supertraits. We use this to ensure that different substitutions of a trait + // don't result in `dyn Trait` types with different projections lists, which + // can be unsound: . + // We achieve a stable ordering by walking over the unsubstituted principal + // trait ref. + let mut ordered_associated_types = vec![]; + if let Some((principal_trait, ref spans)) = principal_trait { let principal_trait = principal_trait.map_bound(|trait_pred| { assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive); @@ -172,16 +204,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // FIXME(negative_bounds): Handle this correctly... let trait_ref = tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref)); - needed_associated_types.extend( + ordered_associated_types.extend( tcx.associated_items(pred.trait_ref.def_id) .in_definition_order() // We only care about associated types. - .filter(|item| item.kind == ty::AssocKind::Type) - // No RPITITs -- even with `async_fn_in_dyn_trait`, they are implicit. + .filter(|item| item.is_type()) + // No RPITITs -- they're not dyn-compatible for now. .filter(|item| !item.is_impl_trait_in_trait()) - // If the associated type has a `where Self: Sized` bound, - // we do not need to constrain the associated type. - .filter(|item| !tcx.generics_require_sized_self(item.def_id)) .map(|item| (item.def_id, trait_ref)), ); } @@ -253,14 +282,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + // We compute the list of projection bounds taking the ordered associated types, + // and check if there was an entry in the collected `projection_bounds`. Those + // are computed by first taking the user-written associated types, then elaborating + // the principal trait ref, and only using those if there was no user-written. + // See note below about how we handle missing associated types with `Self: Sized`, + // which are not required to be provided, but are still used if they are provided. let mut missing_assoc_types = FxIndexSet::default(); - let projection_bounds: Vec<_> = needed_associated_types + let projection_bounds: Vec<_> = ordered_associated_types .into_iter() .filter_map(|key| { if let Some(assoc) = projection_bounds.get(&key) { Some(*assoc) } else { - missing_assoc_types.insert(key); + // If the associated type has a `where Self: Sized` bound, then + // we do not need to provide the associated type. This results in + // a `dyn Trait` type that has a different number of projection + // bounds, which may lead to type mismatches. + if !tcx.generics_require_sized_self(key.0) { + missing_assoc_types.insert(key); + } None } }) @@ -390,7 +431,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lower_lifetime(lifetime, RegionInferReason::ExplicitObjectLifetime) } else { let reason = - if let hir::LifetimeName::ImplicitObjectLifetimeDefault = lifetime.res { + if let hir::LifetimeKind::ImplicitObjectLifetimeDefault = lifetime.kind { if let hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(parent_lifetime, _), .. diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 922578792ba8b..3759a224ff75b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -116,8 +116,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, all_candidates: impl Fn() -> I, qself: AssocItemQSelf, - assoc_kind: ty::AssocKind, - assoc_name: Ident, + assoc_tag: ty::AssocTag, + assoc_ident: Ident, span: Span, constraint: Option<&hir::AssocItemConstraint<'tcx>>, ) -> ErrorGuaranteed @@ -129,28 +129,35 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // First and foremost, provide a more user-friendly & “intuitive” error on kind mismatches. if let Some(assoc_item) = all_candidates().find_map(|r| { tcx.associated_items(r.def_id()) - .filter_by_name_unhygienic(assoc_name.name) - .find(|item| tcx.hygienic_eq(assoc_name, item.ident(tcx), r.def_id())) + .filter_by_name_unhygienic(assoc_ident.name) + .find(|item| tcx.hygienic_eq(assoc_ident, item.ident(tcx), r.def_id())) }) { return self.complain_about_assoc_kind_mismatch( - assoc_item, assoc_kind, assoc_name, span, constraint, + assoc_item, + assoc_tag, + assoc_ident, + span, + constraint, ); } - let assoc_kind_str = assoc_kind_str(assoc_kind); + let assoc_kind_str = assoc_tag_str(assoc_tag); let qself_str = qself.to_string(tcx); // 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 is_dummy = assoc_name.span == DUMMY_SP; + let is_dummy = assoc_ident.span == DUMMY_SP; let mut err = errors::AssocItemNotFound { - span: if is_dummy { span } else { assoc_name.span }, - assoc_name, + span: if is_dummy { span } else { assoc_ident.span }, + assoc_ident, assoc_kind: assoc_kind_str, qself: &qself_str, label: None, sugg: None, + // Try to get the span of the identifier within the path's syntax context + // (if that's different). + within_macro_span: assoc_ident.span.within_macro(span, tcx.sess.source_map()), }; if is_dummy { @@ -161,15 +168,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let all_candidate_names: Vec<_> = all_candidates() .flat_map(|r| tcx.associated_items(r.def_id()).in_definition_order()) .filter_map(|item| { - (!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name) + if !item.is_impl_trait_in_trait() && item.as_tag() == assoc_tag { + item.opt_name() + } else { + None + } }) .collect(); if let Some(suggested_name) = - find_best_match_for_name(&all_candidate_names, assoc_name.name, None) + find_best_match_for_name(&all_candidate_names, assoc_ident.name, None) { err.sugg = Some(errors::AssocItemNotFoundSugg::Similar { - span: assoc_name.span, + span: assoc_ident.span, assoc_kind: assoc_kind_str, suggested_name, }); @@ -193,12 +204,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .iter() .flat_map(|trait_def_id| tcx.associated_items(*trait_def_id).in_definition_order()) .filter_map(|item| { - (!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name) + (!item.is_impl_trait_in_trait() && item.as_tag() == assoc_tag).then(|| item.name()) }) .collect(); if let Some(suggested_name) = - find_best_match_for_name(&wider_candidate_names, assoc_name.name, None) + find_best_match_for_name(&wider_candidate_names, assoc_ident.name, None) { if let [best_trait] = visible_traits .iter() @@ -206,17 +217,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .filter(|trait_def_id| { tcx.associated_items(trait_def_id) .filter_by_name_unhygienic(suggested_name) - .any(|item| item.kind == assoc_kind) + .any(|item| item.as_tag() == assoc_tag) }) .collect::>()[..] { let trait_name = tcx.def_path_str(best_trait); err.label = Some(errors::AssocItemNotFoundLabel::FoundInOtherTrait { - span: assoc_name.span, + span: assoc_ident.span, assoc_kind: assoc_kind_str, trait_name: &trait_name, suggested_name, - identically_named: suggested_name == assoc_name.name, + identically_named: suggested_name == assoc_ident.name, }); if let AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span) = qself // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're @@ -243,7 +254,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // The type param already has a bound for `trait_name`, we just need to // change the associated item. err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait { - span: assoc_name.span, + span: assoc_ident.span, assoc_kind: assoc_kind_str, suggested_name, }); @@ -262,7 +273,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Applicability::MaybeIncorrect }; - let identically_named = suggested_name == assoc_name.name; + let identically_named = suggested_name == assoc_ident.name; if let DefKind::TyAlias = tcx.def_kind(item_def_id) && !tcx.type_alias_is_lazy(item_def_id) @@ -270,7 +281,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTraitQPath { lo: ty_param_span.shrink_to_lo(), mi: ty_param_span.shrink_to_hi(), - hi: (!identically_named).then_some(assoc_name.span), + hi: (!identically_named).then_some(assoc_ident.span), trait_ref, identically_named, suggested_name, @@ -291,7 +302,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // We suggested constraining a type parameter, but the associated item on it // was also not an exact match, so we also suggest changing it. err.span_suggestion_verbose( - assoc_name.span, + assoc_ident.span, fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, suggested_name, Applicability::MaybeIncorrect, @@ -308,13 +319,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // suggest using it. if let [candidate_name] = all_candidate_names.as_slice() { err.sugg = Some(errors::AssocItemNotFoundSugg::Other { - span: assoc_name.span, + span: assoc_ident.span, qself: &qself_str, assoc_kind: assoc_kind_str, suggested_name: *candidate_name, }); } else { - err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_name.span }); + err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_ident.span }); } self.dcx().emit_err(err) @@ -323,14 +334,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn complain_about_assoc_kind_mismatch( &self, assoc_item: &ty::AssocItem, - assoc_kind: ty::AssocKind, + assoc_tag: ty::AssocTag, ident: Ident, span: Span, constraint: Option<&hir::AssocItemConstraint<'tcx>>, ) -> ErrorGuaranteed { let tcx = self.tcx(); - let bound_on_assoc_const_label = if let ty::AssocKind::Const = assoc_item.kind + let bound_on_assoc_const_label = if let ty::AssocKind::Const { .. } = assoc_item.kind && let Some(constraint) = constraint && let hir::AssocItemConstraintKind::Bound { .. } = constraint.kind { @@ -368,31 +379,34 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::Term::Ty(ty) => ty.span, hir::Term::Const(ct) => ct.span(), }; - (span, Some(ident.span), assoc_item.kind, assoc_kind) + (span, Some(ident.span), assoc_item.as_tag(), assoc_tag) } else { - (ident.span, None, assoc_kind, assoc_item.kind) + (ident.span, None, assoc_tag, assoc_item.as_tag()) }; self.dcx().emit_err(errors::AssocKindMismatch { span, - expected: assoc_kind_str(expected), - got: assoc_kind_str(got), + expected: assoc_tag_str(expected), + got: assoc_tag_str(got), expected_because_label, - assoc_kind: assoc_kind_str(assoc_item.kind), + assoc_kind: assoc_tag_str(assoc_item.as_tag()), def_span: tcx.def_span(assoc_item.def_id), bound_on_assoc_const_label, wrap_in_braces_sugg, }) } - pub(super) fn report_ambiguous_assoc_ty( + pub(super) fn report_ambiguous_assoc( &self, span: Span, types: &[String], traits: &[String], name: Symbol, + assoc_tag: ty::AssocTag, ) -> ErrorGuaranteed { - let mut err = struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type"); + let kind_str = assoc_tag_str(assoc_tag); + let mut err = + struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated {kind_str}"); if self .tcx() .resolutions(()) @@ -417,7 +431,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span, format!( "if there were a type named `Type` that implements a trait named \ - `Trait` with associated type `{name}`, you could use the \ + `Trait` with associated {kind_str} `{name}`, you could use the \ fully-qualified path", ), format!("::{name}"), @@ -440,7 +454,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span, format!( "if there were a type named `Example` that implemented one of the \ - traits with associated type `{name}`, you could use the \ + traits with associated {kind_str} `{name}`, you could use the \ fully-qualified path", ), traits.iter().map(|trait_str| format!("::{name}")), @@ -451,7 +465,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.span_suggestion_verbose( span, format!( - "if there were a trait named `Example` with associated type `{name}` \ + "if there were a trait named `Example` with associated {kind_str} `{name}` \ implemented for `{type_str}`, you could use the fully-qualified path", ), format!("<{type_str} as Example>::{name}"), @@ -462,7 +476,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.span_suggestions( span, format!( - "if there were a trait named `Example` with associated type `{name}` \ + "if there were a trait named `Example` with associated {kind_str} `{name}` \ implemented for one of the types, you could use the fully-qualified \ path", ), @@ -491,7 +505,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.emit() } - pub(crate) fn complain_about_ambiguous_inherent_assoc_ty( + pub(crate) fn complain_about_ambiguous_inherent_assoc( &self, name: Ident, candidates: Vec, @@ -552,13 +566,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } // FIXME(inherent_associated_types): Find similarly named associated types and suggest them. - pub(crate) fn complain_about_inherent_assoc_ty_not_found( + pub(crate) fn complain_about_inherent_assoc_not_found( &self, name: Ident, self_ty: Ty<'tcx>, candidates: Vec<(DefId, (DefId, DefId))>, fulfillment_errors: Vec>, span: Span, + assoc_tag: ty::AssocTag, ) -> ErrorGuaranteed { // FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`. // Either @@ -568,12 +583,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); + let assoc_tag_str = assoc_tag_str(assoc_tag); let adt_did = self_ty.ty_adt_def().map(|def| def.did()); let add_def_label = |err: &mut Diag<'_>| { if let Some(did) = adt_did { err.span_label( tcx.def_span(did), - format!("associated item `{name}` not found for this {}", tcx.def_descr(did)), + format!( + "associated {assoc_tag_str} `{name}` not found for this {}", + tcx.def_descr(did) + ), ); } }; @@ -600,11 +619,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.dcx(), name.span, E0220, - "associated type `{name}` not found for `{self_ty}` in the current scope" + "associated {assoc_tag_str} `{name}` not found for `{self_ty}` in the current scope" ); err.span_label(name.span, format!("associated item not found in `{self_ty}`")); err.note(format!( - "the associated type was found for\n{type_candidates}{additional_types}", + "the associated {assoc_tag_str} was found for\n{type_candidates}{additional_types}", )); add_def_label(&mut err); return err.emit(); @@ -685,7 +704,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut err = self.dcx().struct_span_err( name.span, - format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied") + format!("the associated {assoc_tag_str} `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied") ); if !bounds.is_empty() { err.note(format!( @@ -695,7 +714,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } err.span_label( name.span, - format!("associated type cannot be referenced on `{self_ty}` due to unsatisfied trait bounds") + format!("associated {assoc_tag_str} cannot be referenced on `{self_ty}` due to unsatisfied trait bounds") ); for (span, mut bounds) in bound_spans { @@ -746,7 +765,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // `issue-22560.rs`. let mut dyn_compatibility_violations = Ok(()); for (assoc_item, trait_ref) in &missing_assoc_types { - names.entry(trait_ref).or_default().push(assoc_item.name); + names.entry(trait_ref).or_default().push(assoc_item.name()); names_len += 1; let violations = @@ -794,10 +813,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return None; }; - let assoc_item = tcx.associated_items(trait_def).find_by_name_and_kind( + let assoc_item = tcx.associated_items(trait_def).find_by_ident_and_kind( tcx, ident, - ty::AssocKind::Type, + ty::AssocTag::Type, trait_def, ); @@ -837,16 +856,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut names: UnordMap<_, usize> = Default::default(); for (item, _) in &missing_assoc_types { types_count += 1; - *names.entry(item.name).or_insert(0) += 1; + *names.entry(item.name()).or_insert(0) += 1; } let mut dupes = false; let mut shadows = false; for (item, trait_ref) in &missing_assoc_types { - let prefix = if names[&item.name] > 1 { + let name = item.name(); + let prefix = if names[&name] > 1 { let trait_def_id = trait_ref.def_id(); dupes = true; format!("{}::", tcx.def_path_str(trait_def_id)) - } else if bound_names.get(&item.name).is_some_and(|x| *x != item) { + } else if bound_names.get(&name).is_some_and(|x| *x != item) { let trait_def_id = trait_ref.def_id(); shadows = true; format!("{}::", tcx.def_path_str(trait_def_id)) @@ -856,7 +876,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut is_shadowed = false; - if let Some(assoc_item) = bound_names.get(&item.name) + if let Some(assoc_item) = bound_names.get(&name) && *assoc_item != item { is_shadowed = true; @@ -865,17 +885,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" }; err.span_label( tcx.def_span(assoc_item.def_id), - format!("`{}{}` shadowed here{}", prefix, item.name, rename_message), + format!("`{}{}` shadowed here{}", prefix, name, rename_message), ); } let rename_message = if is_shadowed { ", consider renaming it" } else { "" }; - if let Some(sp) = tcx.hir().span_if_local(item.def_id) { - err.span_label( - sp, - format!("`{}{}` defined here{}", prefix, item.name, rename_message), - ); + if let Some(sp) = tcx.hir_span_if_local(item.def_id) { + err.span_label(sp, format!("`{}{}` defined here{}", prefix, name, rename_message)); } } if potential_assoc_types.len() == missing_assoc_types.len() { @@ -888,14 +905,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { { let types: Vec<_> = missing_assoc_types .iter() - .map(|(item, _)| format!("{} = Type", item.name)) + .map(|(item, _)| format!("{} = Type", item.name())) .collect(); - let code = if snippet.ends_with('>') { + let code = if let Some(snippet) = snippet.strip_suffix('>') { // 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(", ")) + format!("{}, {}>", snippet, types.join(", ")) } else if in_expr_or_pat { // 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::`. @@ -923,16 +940,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut names: FxIndexMap<_, usize> = FxIndexMap::default(); for (item, _) in &missing_assoc_types { types_count += 1; - *names.entry(item.name).or_insert(0) += 1; + *names.entry(item.name()).or_insert(0) += 1; } let mut label = vec![]; for (item, trait_ref) in &missing_assoc_types { - let postfix = if names[&item.name] > 1 { + let name = item.name(); + let postfix = if names[&name] > 1 { format!(" (from trait `{}`)", trait_ref.print_trait_sugared()) } else { String::new() }; - label.push(format!("`{}`{}", item.name, postfix)); + label.push(format!("`{}`{}", name, postfix)); } if !label.is_empty() { err.span_label( @@ -1007,12 +1025,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .map(|simple_ty| tcx.incoherent_impls(simple_ty)) }) && let name = Symbol::intern(&format!("{ident2}_{ident3}")) - && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = inherent_impls + && let Some(item) = inherent_impls .iter() .flat_map(|inherent_impl| { tcx.associated_items(inherent_impl).filter_by_name_unhygienic(name) }) .next() + && item.is_fn() { Err(struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type") .with_span_suggestion_verbose( @@ -1370,7 +1389,7 @@ pub(crate) fn fn_trait_to_string( .find_map(|c| { if c.ident.name == sym::Output && let Some(ty) = c.ty() - && ty.span != tcx.hir().span(trait_segment.hir_id) + && ty.span != tcx.hir_span(trait_segment.hir_id) { tcx.sess.source_map().span_to_snippet(ty.span).ok() } else { @@ -1509,7 +1528,7 @@ fn generics_args_err_extend<'a>( }) .collect(); if args.len() > 1 - && let Some(span) = args.into_iter().last() + && let Some(span) = args.into_iter().next_back() { err.note( "generic arguments are not allowed on both an enum and its variant's path \ @@ -1614,10 +1633,10 @@ fn generics_args_err_extend<'a>( } } -pub(super) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str { - match kind { - ty::AssocKind::Fn => "function", - ty::AssocKind::Const => "constant", - ty::AssocKind::Type => "type", +pub(crate) fn assoc_tag_str(assoc_tag: ty::AssocTag) -> &'static str { + match assoc_tag { + ty::AssocTag::Fn => "function", + ty::AssocTag::Const => "constant", + ty::AssocTag::Type => "type", } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index fa58f12c553c1..21f0f9648ea30 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -92,7 +92,7 @@ fn generic_arg_mismatch_err( GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), GenericParamDefKind::Const { .. }, ) if tcx.type_of(param.def_id).skip_binder() == tcx.types.usize => { - let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id)); + let snippet = sess.source_map().span_to_snippet(tcx.hir_span(len.hir_id)); if let Ok(snippet) = snippet { err.span_suggestion( arg.span(), @@ -115,7 +115,7 @@ fn generic_arg_mismatch_err( body.value.kind && let Res::Def(DefKind::Fn { .. }, id) = path.res { - // FIXME(min_generic_const_args): this branch is dead once new const path lowering + // FIXME(mgca): this branch is dead once new const path lowering // (for single-segment paths) is no longer gated err.help(format!("`{}` is a function item, not a type", tcx.item_name(id))); err.help("function item types cannot be named directly"); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index f5e075367f336..483b61add3380 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -78,15 +78,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } if self_ty.span.edition().at_least_rust_2021() { - let msg = "expected a type, found a trait"; - let label = "you can add the `dyn` keyword if you want a trait object"; - let mut diag = - rustc_errors::struct_span_code_err!(self.dcx(), self_ty.span, E0782, "{}", msg); + let mut diag = rustc_errors::struct_span_code_err!( + self.dcx(), + self_ty.span, + E0782, + "{}", + "expected a type, found a trait" + ); if self_ty.span.can_be_used_for_suggestions() + && poly_trait_ref.trait_ref.trait_def_id().is_some() && !self.maybe_suggest_impl_trait(self_ty, &mut diag) + && !self.maybe_suggest_dyn_trait(self_ty, sugg, &mut diag) { - // FIXME: Only emit this suggestion if the trait is dyn-compatible. - diag.multipart_suggestion_verbose(label, sugg, Applicability::MachineApplicable); + self.maybe_suggest_add_generic_impl_trait(self_ty, &mut diag); } // Check if the impl trait that we are considering is an impl of a local trait. self.maybe_suggest_blanket_trait_impl(self_ty, &mut diag); @@ -123,6 +127,65 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + /// For a struct or enum with an invalid bare trait object field, suggest turning + /// it into a generic type bound. + fn maybe_suggest_add_generic_impl_trait( + &self, + self_ty: &hir::Ty<'_>, + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + + let parent_hir_id = tcx.parent_hir_id(self_ty.hir_id); + let parent_item = tcx.hir_get_parent_item(self_ty.hir_id).def_id; + + let generics = match tcx.hir_node_by_def_id(parent_item) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(_, variant, generics), + .. + }) => { + if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) { + return false; + } + generics + } + hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, def, generics), .. }) => { + if !def + .variants + .iter() + .flat_map(|variant| variant.data.fields().iter()) + .any(|field| field.hir_id == parent_hir_id) + { + return false; + } + generics + } + _ => return false, + }; + + let Ok(rendered_ty) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { + return false; + }; + + let param = "TUV" + .chars() + .map(|c| c.to_string()) + .chain((0..).map(|i| format!("P{i}"))) + .find(|s| !generics.params.iter().any(|param| param.name.ident().as_str() == s)) + .expect("we definitely can find at least one param name to generate"); + let mut sugg = vec![(self_ty.span, param.to_string())]; + if let Some(insertion_span) = generics.span_for_param_suggestion() { + sugg.push((insertion_span, format!(", {param}: {}", rendered_ty))); + } else { + sugg.push((generics.where_clause_span, format!("<{param}: {}>", rendered_ty))); + } + diag.multipart_suggestion_verbose( + "you might be missing a type parameter", + sugg, + Applicability::MachineApplicable, + ); + true + } /// Make sure that we are in the condition to suggest the blanket implementation. fn maybe_suggest_blanket_trait_impl( &self, @@ -171,6 +234,65 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + /// Try our best to approximate when adding `dyn` would be helpful for a bare + /// trait object. + /// + /// Right now, this is if the type is either directly nested in another ty, + /// or if it's in the tail field within a struct. This approximates what the + /// user would've gotten on edition 2015, except for the case where we have + /// an *obvious* knock-on `Sized` error. + fn maybe_suggest_dyn_trait( + &self, + self_ty: &hir::Ty<'_>, + sugg: Vec<(Span, String)>, + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + + // Look at the direct HIR parent, since we care about the relationship between + // the type and the thing that directly encloses it. + match tcx.parent_hir_node(self_ty.hir_id) { + // These are all generally ok. Namely, when a trait object is nested + // into another expression or ty, it's either very certain that they + // missed the ty (e.g. `&Trait`) or it's not really possible to tell + // what their intention is, so let's not give confusing suggestions and + // just mention `dyn`. The user can make up their mind what to do here. + hir::Node::Ty(_) + | hir::Node::Expr(_) + | hir::Node::PatExpr(_) + | hir::Node::PathSegment(_) + | hir::Node::AssocItemConstraint(_) + | hir::Node::TraitRef(_) + | hir::Node::Item(_) + | hir::Node::WherePredicate(_) => {} + + hir::Node::Field(field) => { + // Enums can't have unsized fields, fields can only have an unsized tail field. + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(_, variant, _), .. + }) = tcx.parent_hir_node(field.hir_id) + && variant + .fields() + .last() + .is_some_and(|tail_field| tail_field.hir_id == field.hir_id) + { + // Ok + } else { + return false; + } + } + _ => return false, + } + + // FIXME: Only emit this suggestion if the trait is dyn-compatible. + diag.multipart_suggestion_verbose( + "you can add the `dyn` keyword if you want a trait object", + sugg, + Applicability::MachineApplicable, + ); + true + } + fn add_generic_param_suggestion( &self, generics: &hir::Generics<'_>, @@ -291,14 +413,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Applicability::MachineApplicable, ); if !is_dyn_compatible { - diag.note(format!("`{trait_name}` it is dyn-incompatible, so it can't be `dyn`")); + diag.note(format!( + "`{trait_name}` is dyn-incompatible, otherwise a trait object could be used" + )); } else { // No ampersand in suggestion if it's borrowed already let (dyn_str, paren_dyn_str) = if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; let sugg = if let hir::TyKind::TraitObject([_, _, ..], _) = self_ty.kind { - // There are more than one trait bound, we need surrounding parentheses. + // There is more than one trait bound, we need surrounding parentheses. vec![ (self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()), (self_ty.span.shrink_to_hi(), ")".to_string()), @@ -377,8 +501,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let names: Vec<_> = tcx .associated_items(trait_def_id) .in_definition_order() - .filter(|assoc| assoc.kind.namespace() == Namespace::ValueNS) - .map(|cand| cand.name) + .filter(|assoc| assoc.namespace() == Namespace::ValueNS) + .map(|cand| cand.name()) .collect(); if let Some(typo) = find_best_match_for_name(&names, segment.ident.name, None) { diag.span_suggestion_verbose( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index a0f848f07c4b2..5e79e93201531 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -29,33 +29,31 @@ use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err, }; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::mir::interpret::LitToConstInput; -use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::print::PrintPolyTraitRefExt as _; use rustc_middle::ty::{ - self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt, - TypeVisitableExt, TypingMode, + self, AssocTag, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, + TyCtxt, TypeVisitableExt, TypingMode, Upcast, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; +use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; -use rustc_type_ir::Upcast; use tracing::{debug, instrument}; +use self::errors::assoc_tag_str; use crate::check::check_abi_fn_ptr; -use crate::errors::{ - AmbiguousLifetimeBound, BadReturnTypeNotation, InvalidBaseType, NoVariantNamed, -}; +use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoVariantNamed}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; @@ -149,10 +147,10 @@ pub trait HirTyLowerer<'tcx> { &self, span: Span, def_id: LocalDefId, - assoc_name: Ident, + assoc_ident: Ident, ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]>; - /// Lower an associated type to a projection. + /// Lower an associated type/const (from a trait) to a projection. /// /// This method has to be defined by the concrete lowering context because /// dealing with higher-ranked trait references depends on its capabilities: @@ -164,13 +162,14 @@ pub trait HirTyLowerer<'tcx> { /// /// The canonical example of this is associated type `T::P` where `T` is a type /// param constrained by `T: for<'a> Trait<'a>` and where `Trait` defines `P`. - fn lower_assoc_ty( + fn lower_assoc_shared( &self, span: Span, item_def_id: DefId, item_segment: &hir::PathSegment<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Ty<'tcx>; + assoc_tag: ty::AssocTag, + ) -> Result<(DefId, GenericArgsRef<'tcx>), ErrorGuaranteed>; fn lower_fn_sig( &self, @@ -245,6 +244,43 @@ pub enum FeedConstTy<'a, 'tcx> { No, } +#[derive(Debug, Clone, Copy)] +enum LowerAssocMode { + Type { permit_variants: bool }, + Const, +} + +impl LowerAssocMode { + fn assoc_tag(self) -> ty::AssocTag { + match self { + LowerAssocMode::Type { .. } => ty::AssocTag::Type, + LowerAssocMode::Const => ty::AssocTag::Const, + } + } + + fn def_kind(self) -> DefKind { + match self { + LowerAssocMode::Type { .. } => DefKind::AssocTy, + LowerAssocMode::Const => DefKind::AssocConst, + } + } + + fn permit_variants(self) -> bool { + match self { + LowerAssocMode::Type { permit_variants } => permit_variants, + // FIXME(mgca): Support paths like `Option::::None` or `Option::::Some` which + // resolve to const ctors/fn items respectively. + LowerAssocMode::Const => false, + } + } +} + +#[derive(Debug, Clone, Copy)] +enum LoweredAssoc<'tcx> { + Term(DefId, GenericArgsRef<'tcx>), + Variant { adt: Ty<'tcx>, variant_did: DefId }, +} + /// New-typed boolean indicating whether explicit late-bound lifetimes /// are present in a set of generic arguments. /// @@ -333,7 +369,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { #[instrument(level = "debug", skip(self), ret)] pub fn lower_resolved_lifetime(&self, resolved: rbv::ResolvedArg) -> ty::Region<'tcx> { let tcx = self.tcx(); - let lifetime_name = |def_id| tcx.hir().name(tcx.local_def_id_to_hir_id(def_id)); + let lifetime_name = |def_id| tcx.hir_name(tcx.local_def_id_to_hir_id(def_id)); match resolved { rbv::ResolvedArg::StaticLifetime => tcx.lifetimes.re_static, @@ -630,7 +666,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { (args, arg_count) } - #[instrument(level = "debug", skip_all)] + #[instrument(level = "debug", skip(self))] pub fn lower_generic_args_of_assoc_item( &self, span: Span, @@ -638,7 +674,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { item_segment: &hir::PathSegment<'tcx>, parent_args: GenericArgsRef<'tcx>, ) -> GenericArgsRef<'tcx> { - debug!(?span, ?item_def_id, ?item_segment); let (args, _) = self.lower_generic_args_of_path(span, item_def_id, parent_args, item_segment, None); if let Some(c) = item_segment.args().constraints.first() { @@ -804,7 +839,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | PredicateFilter::SelfOnly | PredicateFilter::SelfAndAssociatedTypeBounds => { match constness { - hir::BoundConstness::Always(span) => { + hir::BoundConstness::Always(_) => { if polarity == ty::PredicatePolarity::Positive { bounds.push(( poly_trait_ref @@ -830,7 +865,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // in `lower_assoc_item_constraint`. PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => { match constness { - hir::BoundConstness::Maybe(span) => { + hir::BoundConstness::Maybe(_) => { if polarity == ty::PredicatePolarity::Positive { bounds.push(( poly_trait_ref @@ -898,12 +933,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn probe_trait_that_defines_assoc_item( &self, trait_def_id: DefId, - assoc_kind: ty::AssocKind, - assoc_name: Ident, + assoc_tag: AssocTag, + assoc_ident: Ident, ) -> bool { self.tcx() .associated_items(trait_def_id) - .find_by_name_and_kind(self.tcx(), assoc_name, assoc_kind, trait_def_id) + .find_by_ident_and_kind(self.tcx(), assoc_ident, assoc_tag, trait_def_id) .is_some() } @@ -923,14 +958,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // feature `lazy_type_alias` enabled get encoded as a type alias that normalization will // then actually instantiate the where bounds of. let alias_ty = ty::AliasTy::new_from_args(tcx, did, args); - Ty::new_alias(tcx, ty::Weak, alias_ty) + Ty::new_alias(tcx, ty::Free, alias_ty) } else { tcx.at(span).type_of(did).instantiate(tcx, args) } } /// Search for a trait bound on a type parameter whose trait defines the associated item - /// given by `assoc_name` and `kind`. + /// given by `assoc_ident` and `kind`. /// /// This fails if there is no such bound in the list of candidates or if there are multiple /// candidates in which case it reports ambiguity. @@ -941,14 +976,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, ty_param_def_id: LocalDefId, ty_param_span: Span, - kind: ty::AssocKind, - assoc_name: Ident, + assoc_tag: AssocTag, + assoc_ident: Ident, span: Span, ) -> Result, ErrorGuaranteed> { - debug!(?ty_param_def_id, ?assoc_name, ?span); + debug!(?ty_param_def_id, ?assoc_ident, ?span); let tcx = self.tcx(); - let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name); + let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_ident); debug!("predicates={:#?}", predicates); self.probe_single_bound_for_assoc_item( @@ -956,17 +991,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let trait_refs = predicates .iter_identity_copied() .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))); - traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name) + traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_ident) }, AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span), - kind, - assoc_name, + assoc_tag, + assoc_ident, span, None, ) } - /// Search for a single trait bound whose trait defines the associated item given by `assoc_name`. + /// Search for a single trait bound whose trait defines the associated item given by + /// `assoc_ident`. /// /// This fails if there is no such bound in the list of candidates or if there are multiple /// candidates in which case it reports ambiguity. @@ -975,8 +1011,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, all_candidates: impl Fn() -> I, qself: AssocItemQSelf, - assoc_kind: ty::AssocKind, - assoc_name: Ident, + assoc_tag: AssocTag, + assoc_ident: Ident, span: Span, constraint: Option<&hir::AssocItemConstraint<'tcx>>, ) -> Result, ErrorGuaranteed> @@ -986,15 +1022,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); let mut matching_candidates = all_candidates().filter(|r| { - self.probe_trait_that_defines_assoc_item(r.def_id(), assoc_kind, assoc_name) + self.probe_trait_that_defines_assoc_item(r.def_id(), assoc_tag, assoc_ident) }); let Some(bound) = matching_candidates.next() else { let reported = self.complain_about_assoc_item_not_found( all_candidates, qself, - assoc_kind, - assoc_name, + assoc_tag, + assoc_ident, span, constraint, ); @@ -1005,12 +1041,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let Some(bound2) = matching_candidates.next() { debug!(?bound2); - let assoc_kind_str = errors::assoc_kind_str(assoc_kind); + let assoc_kind_str = errors::assoc_tag_str(assoc_tag); let qself_str = qself.to_string(tcx); let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { span, assoc_kind: assoc_kind_str, - assoc_name, + assoc_ident, qself: &qself_str, }); // Provide a more specific error code index entry for equality bindings. @@ -1024,20 +1060,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }, ); - // FIXME(#97583): Print associated item bindings properly (i.e., not as equality predicates!). + // FIXME(#97583): Print associated item bindings properly (i.e., not as equality + // predicates!). // FIXME: Turn this into a structured, translateable & more actionable suggestion. let mut where_bounds = vec![]; for bound in [bound, bound2].into_iter().chain(matching_candidates) { let bound_id = bound.def_id(); let bound_span = tcx .associated_items(bound_id) - .find_by_name_and_kind(tcx, assoc_name, assoc_kind, bound_id) - .and_then(|item| tcx.hir().span_if_local(item.def_id)); + .find_by_ident_and_kind(tcx, assoc_ident, assoc_tag, bound_id) + .and_then(|item| tcx.hir_span_if_local(item.def_id)); if let Some(bound_span) = bound_span { err.span_label( bound_span, - format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),), + format!("ambiguous `{assoc_ident}` from `{}`", bound.print_trait_sugared(),), ); if let Some(constraint) = constraint { match constraint.kind { @@ -1053,7 +1090,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } // FIXME(#97583): This isn't syntactically well-formed! where_bounds.push(format!( - " T: {trait}::{assoc_name} = {term}", + " T: {trait}::{assoc_ident} = {term}", trait = bound.print_only_trait_path(), )); } @@ -1062,7 +1099,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } else { err.span_suggestion_verbose( - span.with_hi(assoc_name.span.lo()), + span.with_hi(assoc_ident.span.lo()), "use fully-qualified syntax to disambiguate", format!("<{qself_str} as {}>::", bound.print_only_trait_path()), Applicability::MaybeIncorrect, @@ -1070,7 +1107,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } else { err.note(format!( - "associated {assoc_kind_str} `{assoc_name}` could derive from `{}`", + "associated {assoc_kind_str} `{assoc_ident}` could derive from `{}`", bound.print_only_trait_path(), )); } @@ -1118,7 +1155,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // NOTE: When this function starts resolving `Trait::AssocTy` successfully // it should also start reporting the `BARE_TRAIT_OBJECTS` lint. #[instrument(level = "debug", skip_all, ret)] - pub fn lower_assoc_path( + pub fn lower_assoc_path_ty( &self, hir_ref_id: HirId, span: Span, @@ -1127,6 +1164,72 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assoc_segment: &'tcx hir::PathSegment<'tcx>, permit_variants: bool, ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> { + let tcx = self.tcx(); + match self.lower_assoc_path_shared( + hir_ref_id, + span, + qself_ty, + qself, + assoc_segment, + LowerAssocMode::Type { permit_variants }, + )? { + LoweredAssoc::Term(def_id, args) => { + let alias_ty = ty::AliasTy::new_from_args(tcx, def_id, args); + let ty = Ty::new_alias(tcx, alias_ty.kind(tcx), alias_ty); + Ok((ty, tcx.def_kind(def_id), def_id)) + } + LoweredAssoc::Variant { adt, variant_did } => Ok((adt, DefKind::Variant, variant_did)), + } + } + + #[instrument(level = "debug", skip_all, ret)] + fn lower_assoc_path_const( + &self, + hir_ref_id: HirId, + span: Span, + qself_ty: Ty<'tcx>, + qself: &'tcx hir::Ty<'tcx>, + assoc_segment: &'tcx hir::PathSegment<'tcx>, + ) -> Result, ErrorGuaranteed> { + let tcx = self.tcx(); + let (def_id, args) = match self.lower_assoc_path_shared( + hir_ref_id, + span, + qself_ty, + qself, + assoc_segment, + LowerAssocMode::Const, + )? { + LoweredAssoc::Term(def_id, args) => { + if !tcx.associated_item(def_id).is_type_const_capable(tcx) { + let mut err = tcx.dcx().struct_span_err( + span, + "use of trait associated const without `#[type_const]`", + ); + err.note("the declaration in the trait must be marked with `#[type_const]`"); + return Err(err.emit()); + } + (def_id, args) + } + // FIXME(mgca): implement support for this once ready to support all adt ctor expressions, + // not just const ctors + LoweredAssoc::Variant { .. } => { + span_bug!(span, "unexpected variant res for type associated const path") + } + }; + Ok(Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(def_id, args))) + } + + #[instrument(level = "debug", skip_all, ret)] + fn lower_assoc_path_shared( + &self, + hir_ref_id: HirId, + span: Span, + qself_ty: Ty<'tcx>, + qself: &'tcx hir::Ty<'tcx>, + assoc_segment: &'tcx hir::PathSegment<'tcx>, + mode: LowerAssocMode, + ) -> Result, ErrorGuaranteed> { debug!(%qself_ty, ?assoc_segment.ident); let tcx = self.tcx(); @@ -1141,13 +1244,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .iter() .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did())); if let Some(variant_def) = variant_def { - if permit_variants { + if mode.permit_variants() { tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None); let _ = self.prohibit_generic_args( slice::from_ref(assoc_segment).iter(), GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def }, ); - return Ok((qself_ty, DefKind::Variant, variant_def.def_id)); + return Ok(LoweredAssoc::Variant { + adt: qself_ty, + variant_did: variant_def.def_id, + }); } else { variant_resolution = Some(variant_def.def_id); } @@ -1155,15 +1261,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } // FIXME(inherent_associated_types, #106719): Support self types other than ADTs. - if let Some((ty, did)) = self.probe_inherent_assoc_ty( - assoc_ident, + if let Some((did, args)) = self.probe_inherent_assoc_shared( assoc_segment, adt_def.did(), qself_ty, hir_ref_id, span, + mode.assoc_tag(), )? { - return Ok((ty, DefKind::AssocTy, did)); + return Ok(LoweredAssoc::Term(did, args)); } } @@ -1192,7 +1298,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) }, AssocItemQSelf::SelfTyAlias, - ty::AssocKind::Type, + mode.assoc_tag(), assoc_ident, span, None, @@ -1204,14 +1310,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) => self.probe_single_ty_param_bound_for_assoc_item( param_did.expect_local(), qself.span, - ty::AssocKind::Type, + mode.assoc_tag(), assoc_ident, span, )?, _ => { + let kind_str = assoc_tag_str(mode.assoc_tag()); let reported = if variant_resolution.is_some() { // Variant in type position - let msg = format!("expected type, found variant `{assoc_ident}`"); + let msg = format!("expected {kind_str}, found variant `{assoc_ident}`"); self.dcx().span_err(span, msg) } else if qself_ty.is_enum() { let mut err = self.dcx().create_err(NoVariantNamed { @@ -1296,7 +1403,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } - if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) { + if let Some(sp) = tcx.hir_span_if_local(adt_def.did()) { err.span_label(sp, format!("variant `{assoc_ident}` not found here")); } @@ -1310,11 +1417,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident); // Don't print `ty::Error` to the user. - self.report_ambiguous_assoc_ty( + self.report_ambiguous_assoc( span, &[qself_ty.to_string()], &traits, assoc_ident.name, + mode.assoc_tag(), ) }; return Err(reported); @@ -1322,10 +1430,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; let trait_did = bound.def_id(); - let assoc_ty = self - .probe_assoc_item(assoc_ident, ty::AssocKind::Type, hir_ref_id, span, trait_did) - .expect("failed to find associated type"); - let ty = self.lower_assoc_ty(span, assoc_ty.def_id, assoc_segment, bound); + let assoc_item = self + .probe_assoc_item(assoc_ident, mode.assoc_tag(), hir_ref_id, span, trait_did) + .expect("failed to find associated item"); + let (def_id, args) = self.lower_assoc_shared( + span, + assoc_item.def_id, + assoc_segment, + bound, + mode.assoc_tag(), + )?; + let result = LoweredAssoc::Term(def_id, args); if let Some(variant_def_id) = variant_resolution { tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| { @@ -1341,7 +1456,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(DefKind::AssocTy, assoc_ty.def_id, " also"); + could_refer_to(mode.def_kind(), assoc_item.def_id, " also"); lint.span_suggestion( span, @@ -1351,36 +1466,53 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); }); } - Ok((ty, DefKind::AssocTy, assoc_ty.def_id)) + Ok(result) } - fn probe_inherent_assoc_ty( + fn probe_inherent_assoc_shared( &self, - name: Ident, segment: &hir::PathSegment<'tcx>, adt_did: DefId, self_ty: Ty<'tcx>, block: HirId, span: Span, - ) -> Result, DefId)>, ErrorGuaranteed> { + assoc_tag: ty::AssocTag, + ) -> Result)>, ErrorGuaranteed> { let tcx = self.tcx(); - // Don't attempt to look up inherent associated types when the feature is not enabled. - // Theoretically it'd be fine to do so since we feature-gate their definition site. - // However, due to current limitations of the implementation (caused by us performing - // selection during HIR ty lowering instead of in the trait solver), IATs can lead to cycle - // errors (#108491) which mask the feature-gate error, needlessly confusing users - // who use IATs by accident (#113265). if !tcx.features().inherent_associated_types() { - return Ok(None); + match assoc_tag { + // Don't attempt to look up inherent associated types when the feature is not + // enabled. Theoretically it'd be fine to do so since we feature-gate their + // definition site. However, due to current limitations of the implementation + // (caused by us performing selection during HIR ty lowering instead of in the + // trait solver), IATs can lead to cycle errors (#108491) which mask the + // feature-gate error, needlessly confusing users who use IATs by accident + // (#113265). + ty::AssocTag::Type => return Ok(None), + ty::AssocTag::Const => { + // We also gate the mgca codepath for type-level uses of inherent consts + // with the inherent_associated_types feature gate since it relies on the + // same machinery and has similar rough edges. + return Err(feature_err( + &tcx.sess, + sym::inherent_associated_types, + span, + "inherent associated types are unstable", + ) + .emit()); + } + ty::AssocTag::Fn => unreachable!(), + } } + let name = segment.ident; let candidates: Vec<_> = tcx .inherent_impls(adt_did) .iter() .filter_map(|&impl_| { let (item, scope) = - self.probe_assoc_item_unchecked(name, ty::AssocKind::Type, block, impl_)?; + self.probe_assoc_item_unchecked(name, assoc_tag, block, impl_)?; Some((impl_, (item.def_id, scope))) }) .collect(); @@ -1395,13 +1527,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // In contexts that have no inference context, just make a new one. // We do need a local variable to store it, though. - let infcx_; let infcx = match self.infcx() { Some(infcx) => infcx, None => { assert!(!self_ty.has_infer()); - infcx_ = tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis()); - &infcx_ + &tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis()) } }; @@ -1420,8 +1550,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &mut universes, self_ty, |self_ty| { - self.select_inherent_assoc_type_candidates( - infcx, name, span, self_ty, param_env, candidates, + self.select_inherent_assoc_candidates( + infcx, name, span, self_ty, param_env, candidates, assoc_tag, ) }, )?; @@ -1438,13 +1568,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .chain(args.into_iter().skip(parent_args.len())), ); - let ty = - Ty::new_alias(tcx, ty::Inherent, ty::AliasTy::new_from_args(tcx, assoc_item, args)); - - Ok(Some((ty, assoc_item))) + Ok(Some((assoc_item, args))) } - fn select_inherent_assoc_type_candidates( + fn select_inherent_assoc_candidates( &self, infcx: &InferCtxt<'tcx>, name: Ident, @@ -1452,6 +1579,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty: Ty<'tcx>, param_env: ParamEnv<'tcx>, candidates: Vec<(DefId, (DefId, DefId))>, + assoc_tag: ty::AssocTag, ) -> Result<(DefId, (DefId, DefId)), ErrorGuaranteed> { let tcx = self.tcx(); let mut fulfillment_errors = Vec::new(); @@ -1496,17 +1624,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect(); match &applicable_candidates[..] { - &[] => Err(self.complain_about_inherent_assoc_ty_not_found( + &[] => Err(self.complain_about_inherent_assoc_not_found( name, self_ty, candidates, fulfillment_errors, span, + assoc_tag, )), &[applicable_candidate] => Ok(applicable_candidate), - &[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc_ty( + &[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc( name, applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(), span, @@ -1520,12 +1649,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn probe_assoc_item( &self, ident: Ident, - kind: ty::AssocKind, + assoc_tag: ty::AssocTag, block: HirId, span: Span, scope: DefId, ) -> Option { - let (item, scope) = self.probe_assoc_item_unchecked(ident, kind, block, scope)?; + let (item, scope) = self.probe_assoc_item_unchecked(ident, assoc_tag, block, scope)?; self.check_assoc_item(item.def_id, ident, scope, block, span); Some(item) } @@ -1537,7 +1666,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn probe_assoc_item_unchecked( &self, ident: Ident, - kind: ty::AssocKind, + assoc_tag: ty::AssocTag, block: HirId, scope: DefId, ) -> Option<(ty::AssocItem, /*scope*/ DefId)> { @@ -1550,7 +1679,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let item = tcx .associated_items(scope) .filter_by_name_unhygienic(ident.name) - .find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == ident)?; + .find(|i| i.as_tag() == assoc_tag && i.ident(tcx).normalize_to_macros_2_0() == ident)?; Some((*item, def_scope)) } @@ -1602,34 +1731,32 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.associated_items(*trait_def_id) .in_definition_order() .any(|i| { - i.kind.namespace() == Namespace::TypeNS + i.is_type() + && !i.is_impl_trait_in_trait() && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident - && matches!(i.kind, ty::AssocKind::Type) }) // Consider only accessible traits && tcx.visibility(*trait_def_id) .is_accessible_from(self.item_def_id(), tcx) && tcx.all_impls(*trait_def_id) .any(|impl_def_id| { - let impl_header = tcx.impl_trait_header(impl_def_id); - impl_header.is_some_and(|header| { - let trait_ref = header.trait_ref.instantiate( - tcx, - infcx.fresh_args_for_item(DUMMY_SP, impl_def_id), - ); - - let value = fold_regions(tcx, qself_ty, |_, _| tcx.lifetimes.re_erased); - // FIXME: Don't bother dealing with non-lifetime binders here... - if value.has_escaping_bound_vars() { - return false; - } - infcx - .can_eq( - ty::ParamEnv::empty(), - trait_ref.self_ty(), - value, - ) && header.polarity != ty::ImplPolarity::Negative - }) + let header = tcx.impl_trait_header(impl_def_id).unwrap(); + let trait_ref = header.trait_ref.instantiate( + tcx, + infcx.fresh_args_for_item(DUMMY_SP, impl_def_id), + ); + + let value = fold_regions(tcx, qself_ty, |_, _| tcx.lifetimes.re_erased); + // FIXME: Don't bother dealing with non-lifetime binders here... + if value.has_escaping_bound_vars() { + return false; + } + infcx + .can_eq( + ty::ParamEnv::empty(), + trait_ref.self_ty(), + value, + ) && header.polarity != ty::ImplPolarity::Negative }) }) .map(|trait_def_id| tcx.def_path_str(trait_def_id)) @@ -1638,32 +1765,88 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Lower a qualified path to a type. #[instrument(level = "debug", skip_all)] - fn lower_qpath( + fn lower_qpath_ty( &self, span: Span, opt_self_ty: Option>, item_def_id: DefId, - trait_segment: &hir::PathSegment<'tcx>, + trait_segment: Option<&hir::PathSegment<'tcx>>, item_segment: &hir::PathSegment<'tcx>, ) -> Ty<'tcx> { + match self.lower_qpath_shared( + span, + opt_self_ty, + item_def_id, + trait_segment, + item_segment, + ty::AssocTag::Type, + ) { + Ok((item_def_id, item_args)) => { + Ty::new_projection_from_args(self.tcx(), item_def_id, item_args) + } + Err(guar) => Ty::new_error(self.tcx(), guar), + } + } + + /// Lower a qualified path to a const. + #[instrument(level = "debug", skip_all)] + fn lower_qpath_const( + &self, + span: Span, + opt_self_ty: Option>, + item_def_id: DefId, + trait_segment: Option<&hir::PathSegment<'tcx>>, + item_segment: &hir::PathSegment<'tcx>, + ) -> Const<'tcx> { + match self.lower_qpath_shared( + span, + opt_self_ty, + item_def_id, + trait_segment, + item_segment, + ty::AssocTag::Const, + ) { + Ok((item_def_id, item_args)) => { + let uv = ty::UnevaluatedConst::new(item_def_id, item_args); + Const::new_unevaluated(self.tcx(), uv) + } + Err(guar) => Const::new_error(self.tcx(), guar), + } + } + + #[instrument(level = "debug", skip_all)] + fn lower_qpath_shared( + &self, + span: Span, + opt_self_ty: Option>, + item_def_id: DefId, + trait_segment: Option<&hir::PathSegment<'tcx>>, + item_segment: &hir::PathSegment<'tcx>, + assoc_tag: ty::AssocTag, + ) -> Result<(DefId, GenericArgsRef<'tcx>), ErrorGuaranteed> { let tcx = self.tcx(); let trait_def_id = tcx.parent(item_def_id); debug!(?trait_def_id); let Some(self_ty) = opt_self_ty else { - return self.error_missing_qpath_self_ty(trait_def_id, span, item_segment); + return Err(self.error_missing_qpath_self_ty( + trait_def_id, + span, + item_segment, + assoc_tag, + )); }; debug!(?self_ty); let trait_ref = - self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false); + self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment.unwrap(), false); debug!(?trait_ref); let item_args = self.lower_generic_args_of_assoc_item(span, item_def_id, item_segment, trait_ref.args); - Ty::new_projection_from_args(tcx, item_def_id, item_args) + Ok((item_def_id, item_args)) } fn error_missing_qpath_self_ty( @@ -1671,7 +1854,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_def_id: DefId, span: Span, item_segment: &hir::PathSegment<'tcx>, - ) -> Ty<'tcx> { + assoc_tag: ty::AssocTag, + ) -> ErrorGuaranteed { let tcx = self.tcx(); let path_str = tcx.def_path_str(trait_def_id); @@ -1707,9 +1891,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that // references the trait. Relevant for the first case in // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs` - let reported = - self.report_ambiguous_assoc_ty(span, &type_names, &[path_str], item_segment.ident.name); - Ty::new_error(tcx, reported) + self.report_ambiguous_assoc( + span, + &type_names, + &[path_str], + item_segment.ident.name, + assoc_tag, + ) } pub fn prohibit_generic_args<'a>( @@ -2008,16 +2196,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } Res::Def(DefKind::AssocTy, def_id) => { - debug_assert!(path.segments.len() >= 2); - let _ = self.prohibit_generic_args( - path.segments[..path.segments.len() - 2].iter(), - GenericsArgsErrExtend::None, - ); - self.lower_qpath( + let trait_segment = if let [modules @ .., trait_, _item] = path.segments { + let _ = self.prohibit_generic_args(modules.iter(), GenericsArgsErrExtend::None); + Some(trait_) + } else { + None + }; + self.lower_qpath_ty( span, opt_self_ty, def_id, - &path.segments[path.segments.len() - 2], + trait_segment, path.segments.last().unwrap(), ) } @@ -2123,18 +2312,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { { let anon_const_type = tcx.type_of(param_def_id).instantiate(tcx, args); - // We must error if the instantiated type has any inference variables as we will - // use this type to feed the `type_of` and query results must not contain inference - // variables otherwise we will ICE. - // + // FIXME(generic_const_parameter_types): Ideally we remove these errors below when + // we have the ability to intermix typeck of anon const const args with the parent + // bodies typeck. + // We also error if the type contains any regions as effectively any region will wind // up as a region variable in mir borrowck. It would also be somewhat concerning if // hir typeck was using equality but mir borrowck wound up using subtyping as that could // result in a non-infer in hir typeck but a region variable in borrowck. - // - // FIXME(generic_const_parameter_types): Ideally we remove these errors one day when - // we have the ability to intermix typeck of anon const const args with the parent - // bodies typeck. if tcx.features().generic_const_parameter_types() && (anon_const_type.has_free_regions() || anon_const_type.has_erased_regions()) { @@ -2145,6 +2330,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e))); return ty::Const::new_error(tcx, e); } + // We must error if the instantiated type has any inference variables as we will + // use this type to feed the `type_of` and query results must not contain inference + // variables otherwise we will ICE. if anon_const_type.has_non_region_infer() { let e = tcx.dcx().span_err( const_arg.span(), @@ -2153,6 +2341,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e))); return ty::Const::new_error(tcx, e); } + // We error when the type contains unsubstituted generics since we do not currently + // give the anon const any of the generics from the parent. + if anon_const_type.has_non_region_param() { + let e = tcx.dcx().span_err( + const_arg.span(), + "anonymous constants referencing generics are not yet supported", + ); + tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e))); + return ty::Const::new_error(tcx, e); + } tcx.feed_anon_const_type( anon.def_id, @@ -2167,11 +2365,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself)); self.lower_const_path_resolved(opt_self_ty, path, hir_id) } - hir::ConstArgKind::Path(qpath) => ty::Const::new_error_with_message( - tcx, - qpath.span(), - format!("Const::lower_const_arg: invalid qpath {qpath:?}"), - ), + hir::ConstArgKind::Path(hir::QPath::TypeRelative(qself, segment)) => { + debug!(?qself, ?segment); + let ty = self.lower_ty(qself); + self.lower_assoc_path_const(hir_id, const_arg.span(), ty, qself, segment) + .unwrap_or_else(|guar| Const::new_error(tcx, guar)) + } + hir::ConstArgKind::Path(qpath @ hir::QPath::LangItem(..)) => { + ty::Const::new_error_with_message( + tcx, + qpath.span(), + format!("Const::lower_const_arg: invalid qpath {qpath:?}"), + ) + } hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon), hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span), } @@ -2207,6 +2413,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(did, args)) } + Res::Def(DefKind::AssocConst, did) => { + let trait_segment = if let [modules @ .., trait_, _item] = path.segments { + let _ = self.prohibit_generic_args(modules.iter(), GenericsArgsErrExtend::None); + Some(trait_) + } else { + None + }; + self.lower_qpath_const( + span, + opt_self_ty, + did, + trait_segment, + path.segments.last().unwrap(), + ) + } Res::Def(DefKind::Static { .. }, _) => { span_bug!(span, "use of bare `static` ConstArgKind::Path's not yet supported") } @@ -2223,7 +2444,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Exhaustive match to be clear about what exactly we're considering to be // an invalid Res for a const path. - Res::Def( + res @ (Res::Def( DefKind::Mod | DefKind::Enum | DefKind::Variant @@ -2237,7 +2458,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | DefKind::Union | DefKind::Trait | DefKind::ForeignTy - | DefKind::AssocConst | DefKind::TyParam | DefKind::Macro(_) | DefKind::LifetimeParam @@ -2260,12 +2480,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | Res::Local(_) | Res::ToolMod | Res::NonMacroAttr(_) - | Res::Err => Const::new_error_with_message(tcx, span, "invalid Res for const path"), + | Res::Err) => Const::new_error_with_message( + tcx, + span, + format!("invalid Res {res:?} for const path"), + ), } } - /// Literals and const generic parameters are eagerly converted to a constant, everything else - /// becomes `Unevaluated`. + /// Literals are eagerly converted to a constant, everything else becomes `Unevaluated`. #[instrument(skip(self), level = "debug")] fn lower_anon_const(&self, anon: &AnonConst) -> Const<'tcx> { let tcx = self.tcx(); @@ -2464,7 +2687,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => { debug!(?qself, ?segment); let ty = self.lower_ty(qself); - self.lower_assoc_path(hir_ty.hir_id, hir_ty.span, ty, qself, segment, false) + self.lower_assoc_path_ty(hir_ty.hir_id, hir_ty.span, ty, qself, segment, false) .map(|(ty, _, _)| ty) .unwrap_or_else(|guar| Ty::new_error(tcx, guar)) } @@ -2494,32 +2717,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::TyKind::Pat(ty, pat) => { let ty_span = ty.span; let ty = self.lower_ty(ty); - let pat_ty = match pat.kind { - hir::TyPatKind::Range(start, end, include_end) => { - let ty = match ty.kind() { - ty::Int(_) | ty::Uint(_) | ty::Char => ty, - _ => Ty::new_error( - tcx, - self.dcx().emit_err(InvalidBaseType { - ty, - pat: "range", - ty_span, - pat_span: pat.span, - }), - ), - }; - let start = start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)); - let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)); - - let include_end = match include_end { - hir::RangeEnd::Included => true, - hir::RangeEnd::Excluded => false, - }; - - let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end }); - Ty::new_pat(tcx, ty, pat) - } - hir::TyPatKind::Err(e) => Ty::new_error(tcx, e), + let pat_ty = match self.lower_pat_ty_pat(ty, ty_span, pat) { + Ok(kind) => Ty::new_pat(tcx, ty, tcx.mk_pat(kind)), + Err(guar) => Ty::new_error(tcx, guar), }; self.record_ty(pat.hir_id, ty, pat.span); pat_ty @@ -2531,6 +2731,39 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { result_ty } + fn lower_pat_ty_pat( + &self, + ty: Ty<'tcx>, + ty_span: Span, + pat: &hir::TyPat<'tcx>, + ) -> Result, ErrorGuaranteed> { + let tcx = self.tcx(); + match pat.kind { + hir::TyPatKind::Range(start, end) => { + match ty.kind() { + // Keep this list of types in sync with the list of types that + // the `RangePattern` trait is implemented for. + ty::Int(_) | ty::Uint(_) | ty::Char => { + let start = self.lower_const_arg(start, FeedConstTy::No); + let end = self.lower_const_arg(end, FeedConstTy::No); + Ok(ty::PatternKind::Range { start, end }) + } + _ => Err(self + .dcx() + .span_delayed_bug(ty_span, "invalid base type for range pattern")), + } + } + hir::TyPatKind::Or(patterns) => { + self.tcx() + .mk_patterns_from_iter(patterns.iter().map(|pat| { + self.lower_pat_ty_pat(ty, ty_span, pat).map(|pat| tcx.mk_pat(pat)) + })) + .map(ty::PatternKind::Or) + } + hir::TyPatKind::Err(e) => Err(e), + } + } + /// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR. #[instrument(level = "debug", skip(self), ret)] fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: bool) -> Ty<'tcx> { @@ -2660,10 +2893,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let trait_ref = self.lower_impl_trait_ref(i.of_trait.as_ref()?, self.lower_ty(i.self_ty)); - let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind( + let assoc = tcx.associated_items(trait_ref.def_id).find_by_ident_and_kind( tcx, *ident, - ty::AssocKind::Fn, + ty::AssocTag::Fn, trait_ref.def_id, )?; diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 0b1be8e4f7a99..64c1a78bd1c8b 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -4,8 +4,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ObligationCause, WellFormedLoc}; use rustc_middle::bug; use rustc_middle::query::Providers; -use rustc_middle::ty::fold::fold_regions; -use rustc_middle::ty::{self, TyCtxt, TypingMode}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode, fold_regions}; use rustc_span::def_id::LocalDefId; use rustc_trait_selection::traits::{self, ObligationCtxt}; use tracing::debug; @@ -78,6 +77,15 @@ fn diagnostic_hir_wf_check<'tcx>( let tcx_ty = fold_regions(self.tcx, tcx_ty, |r, _| { if r.is_bound() { self.tcx.lifetimes.re_erased } else { r } }); + + // We may be checking the WFness of a type in an opaque with a non-lifetime bound. + // Perhaps we could rebind all the escaping bound vars, but they're coming from + // arbitrary debruijn indices and aren't particularly important anyways, since they + // are only coming from `feature(non_lifetime_binders)` anyways. + if tcx_ty.has_escaping_bound_vars() { + return; + } + let cause = traits::ObligationCause::new( ty.span, self.def_id, @@ -137,9 +145,9 @@ fn diagnostic_hir_wf_check<'tcx>( ref item => bug!("Unexpected TraitItem {:?}", item), }, hir::Node::Item(item) => match item.kind { - hir::ItemKind::TyAlias(ty, _) - | hir::ItemKind::Static(ty, _, _) - | hir::ItemKind::Const(ty, _, _) => vec![ty], + hir::ItemKind::TyAlias(_, ty, _) + | hir::ItemKind::Static(_, ty, _, _) + | hir::ItemKind::Const(_, ty, _, _) => vec![ty], hir::ItemKind::Impl(impl_) => match &impl_.of_trait { Some(t) => t .path @@ -171,7 +179,7 @@ fn diagnostic_hir_wf_check<'tcx>( .. }) => vec![*ty], hir::Node::AnonConst(_) => { - if let Some(const_param_id) = tcx.hir().opt_const_param_default_param_def_id(hir_id) + if let Some(const_param_id) = tcx.hir_opt_const_param_default_param_def_id(hir_id) && let hir::Node::GenericParam(hir::GenericParam { kind: hir::GenericParamKind::Const { ty, .. }, .. diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index c30b39dfe7656..cbdc501291bc8 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -112,14 +112,14 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained( .flat_map(|def_id| { let item = tcx.associated_item(def_id); match item.kind { - ty::AssocKind::Type => { + ty::AssocKind::Type { .. } => { if item.defaultness(tcx).has_value() { cgp::parameters_for(tcx, tcx.type_of(def_id).instantiate_identity(), true) } else { vec![] } } - ty::AssocKind::Fn | ty::AssocKind::Const => vec![], + ty::AssocKind::Fn { .. } | ty::AssocKind::Const { .. } => vec![], } }) .collect(); diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index af1107b499f4e..309221f9a127a 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -374,9 +374,12 @@ fn check_predicates<'tcx>( // Include the well-formed predicates of the type parameters of the impl. for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().instantiate_identity().args { + let Some(term) = arg.as_term() else { + continue; + }; let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let obligations = - wf::obligations(infcx, tcx.param_env(impl1_def_id), impl1_def_id, 0, arg, span) + wf::obligations(infcx, tcx.param_env(impl1_def_id), impl1_def_id, 0, term, span) .unwrap(); assert!(!obligations.has_infer()); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index cf18bab950ae2..010c6c376fe3d 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -67,13 +67,11 @@ This API is completely unstable and subject to change. #![feature(if_let_guard)] #![feature(iter_from_coroutine)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(slice_partition_dedup)] #![feature(try_blocks)] #![feature(unwrap_infallible)] -#![warn(unreachable_pub)] // tidy-alphabetical-end // These are used by Clippy. @@ -196,44 +194,40 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let _: R = tcx.ensure_ok().crate_inherent_impls_overlap_check(()); }); - if tcx.features().rustc_attrs() { - tcx.sess.time("dumping_rustc_attr_data", || { - outlives::dump::inferred_outlives(tcx); - variance::dump::variances(tcx); - collect::dump::opaque_hidden_types(tcx); - collect::dump::predicates_and_item_bounds(tcx); - collect::dump::def_parents(tcx); - collect::dump::vtables(tcx); - }); - } - // Make sure we evaluate all static and (non-associated) const items, even if unused. // If any of these fail to evaluate, we do not want this crate to pass compilation. tcx.par_hir_body_owners(|item_def_id| { let def_kind = tcx.def_kind(item_def_id); match def_kind { - DefKind::Static { .. } => tcx.ensure_ok().eval_static_initializer(item_def_id), + DefKind::Static { .. } => { + tcx.ensure_ok().eval_static_initializer(item_def_id); + check::maybe_check_static_with_link_section(tcx, item_def_id); + } DefKind::Const if tcx.generics_of(item_def_id).is_empty() => { - let instance = ty::Instance::new(item_def_id.into(), ty::GenericArgs::empty()); + let instance = ty::Instance::new_raw(item_def_id.into(), ty::GenericArgs::empty()); let cid = GlobalId { instance, promoted: None }; let typing_env = ty::TypingEnv::fully_monomorphized(); tcx.ensure_ok().eval_to_const_value_raw(typing_env.as_query_input(cid)); } _ => (), } - }); - - // FIXME: Remove this when we implement creating `DefId`s - // for anon constants during their parents' typeck. - // Typeck all body owners in parallel will produce queries - // cycle errors because it may typeck on anon constants directly. - tcx.par_hir_body_owners(|item_def_id| { - let def_kind = tcx.def_kind(item_def_id); + // Skip `AnonConst`s because we feed their `type_of`. if !matches!(def_kind, DefKind::AnonConst) { tcx.ensure_ok().typeck(item_def_id); } }); + if tcx.features().rustc_attrs() { + tcx.sess.time("dumping_rustc_attr_data", || { + outlives::dump::inferred_outlives(tcx); + variance::dump::variances(tcx); + collect::dump::opaque_hidden_types(tcx); + collect::dump::predicates_and_item_bounds(tcx); + collect::dump::def_parents(tcx); + collect::dump::vtables(tcx); + }); + } + tcx.ensure_ok().check_unused_traits(()); } @@ -252,7 +246,9 @@ pub fn lower_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { // def-ID that will be used to determine the traits/predicates in // scope. This is derived from the enclosing item-like thing. let env_def_id = tcx.hir_get_parent_item(hir_ty.hir_id); - collect::ItemCtxt::new(tcx, env_def_id.def_id).lower_ty(hir_ty) + collect::ItemCtxt::new(tcx, env_def_id.def_id) + .lowerer() + .lower_ty_maybe_return_type_notation(hir_ty) } /// This is for rustdoc. diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index a0faa5e8429e5..c99eb12efcca2 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -24,8 +24,8 @@ pub(super) fn infer_predicates( // If new predicates were added then we need to re-calculate // all crates since there could be new implied predicates. - loop { - let mut predicates_added = false; + for i in 0.. { + let mut predicates_added = vec![]; // Visit all the crates and infer predicates for id in tcx.hir_free_items() { @@ -83,14 +83,27 @@ pub(super) fn infer_predicates( .get(&item_did.to_def_id()) .map_or(0, |p| p.as_ref().skip_binder().len()); if item_required_predicates.len() > item_predicates_len { - predicates_added = true; + predicates_added.push(item_did); global_inferred_outlives .insert(item_did.to_def_id(), ty::EarlyBinder::bind(item_required_predicates)); } } - if !predicates_added { + if predicates_added.is_empty() { + // We've reached a fixed point. break; + } else if !tcx.recursion_limit().value_within_limit(i) { + let msg = if let &[id] = &predicates_added[..] { + format!("overflow computing implied lifetime bounds for `{}`", tcx.def_path_str(id),) + } else { + "overflow computing implied lifetime bounds".to_string() + }; + tcx.dcx() + .struct_span_fatal( + predicates_added.iter().map(|id| tcx.def_span(*id)).collect::>(), + msg, + ) + .emit(); } } @@ -144,10 +157,10 @@ fn insert_required_predicates_to_be_wf<'tcx>( ); } - ty::Alias(ty::Weak, alias) => { + ty::Alias(ty::Free, alias) => { // This corresponds to a type like `Type<'a, T>`. // We check inferred and explicit predicates. - debug!("Weak"); + debug!("Free"); check_inferred_predicates( tcx, alias.def_id, diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs index 32b05dcc569ef..daa908c8c78e4 100644 --- a/compiler/rustc_hir_analysis/src/outlives/mod.rs +++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs @@ -25,7 +25,7 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clau } DefKind::AnonConst if tcx.features().generic_const_exprs() => { let id = tcx.local_def_id_to_hir_id(item_def_id); - if tcx.hir().opt_const_param_default_param_def_id(id).is_some() { + if tcx.hir_opt_const_param_default_param_def_id(id).is_some() { // In `generics_of` we set the generics' parent to be our parent's parent which means that // we lose out on the predicates of our actual parent if we dont return those predicates here. // (See comment in `generics_of` for more information on why the parent shenanigans is necessary) diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 0c9f5ba8b6fa8..044fb64ca8216 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxIndexMap; +use rustc_middle::ty::outlives::{Component, push_outlives_components}; use rustc_middle::ty::{self, GenericArg, GenericArgKind, Region, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::Span; -use rustc_type_ir::outlives::{Component, push_outlives_components}; use smallvec::smallvec; /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred @@ -146,7 +146,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>( fn is_free_region(region: Region<'_>) -> bool { // First, screen for regions that might appear in a type header. - match *region { + match region.kind() { // These correspond to `T: 'a` relationships: // // struct Foo<'a, T> { diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index e954d2b9ea48e..92cfece77c473 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -107,7 +107,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { let current_item = &CurrentItem { inferred_start }; let ty = tcx.type_of(def_id).instantiate_identity(); - // The type as returned by `type_of` is the underlying type and generally not a weak projection. + // The type as returned by `type_of` is the underlying type and generally not a free alias. // Therefore we need to check the `DefKind` first. if let DefKind::TyAlias = tcx.def_kind(def_id) && tcx.type_alias_is_lazy(def_id) @@ -251,16 +251,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } ty::Pat(typ, pat) => { - match *pat { - ty::PatternKind::Range { start, end, include_end: _ } => { - if let Some(start) = start { - self.add_constraints_from_const(current, start, variance); - } - if let Some(end) = end { - self.add_constraints_from_const(current, end, variance); - } - } - } + self.add_constraints_from_pat(current, variance, pat); self.add_constraints_from_ty(current, typ, variance); } @@ -286,7 +277,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_invariant_args(current, data.args, variance); } - ty::Alias(ty::Weak, ref data) => { + ty::Alias(ty::Free, ref data) => { self.add_constraints_from_args(current, data.def_id, data.args, variance); } @@ -338,6 +329,25 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } } + fn add_constraints_from_pat( + &mut self, + current: &CurrentItem, + variance: VarianceTermPtr<'a>, + pat: ty::Pattern<'tcx>, + ) { + match *pat { + ty::PatternKind::Range { start, end } => { + self.add_constraints_from_const(current, start, variance); + self.add_constraints_from_const(current, end, variance); + } + ty::PatternKind::Or(patterns) => { + for pat in patterns { + self.add_constraints_from_pat(current, variance, pat) + } + } + } + } + /// Adds constraints appropriate for a nominal type (enum, struct, /// object, etc) appearing in a context with ambient variance `variance` fn add_constraints_from_args( @@ -432,7 +442,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { region: ty::Region<'tcx>, variance: VarianceTermPtr<'a>, ) { - match *region { + match region.kind() { ty::ReEarlyParam(ref data) => { self.add_constraint(current, data.index, variance); } diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 0800d99e9452e..dbba45dc7bb58 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -44,13 +44,13 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] { return &[]; } - match tcx.def_kind(item_def_id) { + let kind = tcx.def_kind(item_def_id); + match kind { DefKind::Fn | DefKind::AssocFn | DefKind::Enum | DefKind::Struct | DefKind::Union - | DefKind::Variant | DefKind::Ctor(..) => { // These are inferred. let crate_map = tcx.crate_variances(()); @@ -89,7 +89,11 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] { } // Variance not relevant. - span_bug!(tcx.def_span(item_def_id), "asked to compute variance for wrong kind of item"); + span_bug!( + tcx.def_span(item_def_id), + "asked to compute variance for {}", + kind.descr(item_def_id.to_def_id()) + ); } #[derive(Debug, Copy, Clone)] diff --git a/compiler/rustc_hir_pretty/Cargo.toml b/compiler/rustc_hir_pretty/Cargo.toml index 86989d1e55b4d..91da8cb3fc5fb 100644 --- a/compiler/rustc_hir_pretty/Cargo.toml +++ b/compiler/rustc_hir_pretty/Cargo.toml @@ -8,7 +8,7 @@ edition = "2024" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_hir = { path = "../rustc_hir" } rustc_span = { path = "../rustc_span" } # tidy-alphabetical-end diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index e06398cf3c418..04f9c831b0ac0 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -3,7 +3,6 @@ // tidy-alphabetical-start #![recursion_limit = "256"] -#![warn(unreachable_pub)] // tidy-alphabetical-end use std::cell::Cell; @@ -13,10 +12,10 @@ use rustc_abi::ExternAbi; use rustc_ast::util::parser::{self, ExprPrecedence, Fixity}; use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs}; use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; -use rustc_ast_pretty::pp::{self, Breaks}; +use rustc_ast_pretty::pp::{self, BoxMarker, Breaks}; use rustc_ast_pretty::pprust::state::MacHeader; use rustc_ast_pretty::pprust::{Comments, PrintState}; -use rustc_attr_parsing::{AttributeKind, PrintAttribute}; +use rustc_attr_data_structures::{AttributeKind, PrintAttribute}; use rustc_hir::{ BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, @@ -110,6 +109,7 @@ impl<'a> State<'a> { } self.print_attr_item(&unparsed, unparsed.span); self.word("]"); + self.hardbreak() } hir::Attribute::Parsed(AttributeKind::DocComment { style, kind, comment, .. }) => { self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string( @@ -118,16 +118,16 @@ impl<'a> State<'a> { self.hardbreak() } hir::Attribute::Parsed(pa) => { - self.word("#[attr=\""); + self.word("#[attr = "); pa.print_attribute(self); - self.word("\")]"); + self.word("]"); self.hardbreak() } } } fn print_attr_item(&mut self, item: &hir::AttrItem, span: Span) { - self.ibox(0); + let ib = self.ibox(0); let path = ast::Path { span, segments: item @@ -146,6 +146,7 @@ impl<'a> State<'a> { false, None, *delim, + None, &tokens, true, span, @@ -161,7 +162,7 @@ impl<'a> State<'a> { self.word(token_str); } } - self.end(); + self.end(ib); } fn print_node(&mut self, node: Node<'_>) { @@ -182,7 +183,7 @@ impl<'a> State<'a> { Node::Ty(a) => self.print_type(a), Node::AssocItemConstraint(a) => self.print_assoc_item_constraint(a), Node::TraitRef(a) => self.print_trait_ref(a), - Node::OpaqueTy(o) => self.print_opaque_ty(o), + Node::OpaqueTy(_) => panic!("cannot print Node::OpaqueTy"), Node::Pat(a) => self.print_pat(a), Node::TyPat(a) => self.print_ty_pat(a), Node::PatField(a) => self.print_patfield(a), @@ -192,10 +193,10 @@ impl<'a> State<'a> { Node::PreciseCapturingNonLifetimeArg(param) => self.print_ident(param.ident), Node::Block(a) => { // Containing cbox, will be closed by print-block at `}`. - self.cbox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); // Head-ibox, will be closed by print-block after `{`. - self.ibox(0); - self.print_block(a); + let ib = self.ibox(0); + self.print_block(a, cb, ib); } Node::Lifetime(a) => self.print_lifetime(a), Node::GenericParam(_) => panic!("cannot print Node::GenericParam"), @@ -315,17 +316,17 @@ pub fn item_to_string(ann: &dyn PpAnn, pat: &hir::Item<'_>) -> String { } impl<'a> State<'a> { - fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) { + fn bclose_maybe_open(&mut self, span: rustc_span::Span, cb: Option) { self.maybe_print_comment(span.hi()); self.break_offset_if_not_bol(1, -INDENT_UNIT); self.word("}"); - if close_box { - self.end(); // close the outer-box + if let Some(cb) = cb { + self.end(cb); } } - fn bclose(&mut self, span: rustc_span::Span) { - self.bclose_maybe_open(span, true) + fn bclose(&mut self, span: rustc_span::Span, cb: BoxMarker) { + self.bclose_maybe_open(span, Some(cb)) } fn commasep_cmnt(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G) @@ -333,7 +334,7 @@ impl<'a> State<'a> { F: FnMut(&mut State<'_>, &T), G: FnMut(&T) -> rustc_span::Span, { - self.rbox(0, b); + let rb = self.rbox(0, b); let len = elts.len(); let mut i = 0; for elt in elts { @@ -346,7 +347,7 @@ impl<'a> State<'a> { self.space_if_not_bol(); } } - self.end(); + self.end(rb); } fn commasep_exprs(&mut self, b: Breaks, exprs: &[hir::Expr<'_>]) { @@ -369,7 +370,7 @@ impl<'a> State<'a> { fn print_type(&mut self, ty: &hir::Ty<'_>) { self.maybe_print_comment(ty.span.lo()); - self.ibox(0); + let ib = self.ibox(0); match ty.kind { hir::TyKind::Slice(ty) => { self.word("["); @@ -397,7 +398,7 @@ impl<'a> State<'a> { self.pclose(); } hir::TyKind::BareFn(f) => { - self.print_ty_fn(f.abi, f.safety, f.decl, None, f.generic_params, f.param_names); + self.print_ty_fn(f.abi, f.safety, f.decl, None, f.generic_params, f.param_idents); } hir::TyKind::UnsafeBinder(unsafe_binder) => { self.print_unsafe_binder(unsafe_binder); @@ -456,16 +457,16 @@ impl<'a> State<'a> { self.print_ty_pat(pat); } } - self.end() + self.end(ib) } fn print_unsafe_binder(&mut self, unsafe_binder: &hir::UnsafeBinderTy<'_>) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("unsafe"); self.print_generic_params(unsafe_binder.generic_params); self.nbsp(); self.print_type(unsafe_binder.inner_ty); - self.end(); + self.end(ib); } fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) { @@ -473,23 +474,23 @@ impl<'a> State<'a> { self.maybe_print_comment(item.span.lo()); self.print_attrs_as_outer(self.attrs(item.hir_id())); match item.kind { - hir::ForeignItemKind::Fn(sig, arg_names, generics) => { - self.head(""); + hir::ForeignItemKind::Fn(sig, arg_idents, generics) => { + let (cb, ib) = self.head(""); self.print_fn( sig.decl, sig.header, Some(item.ident.name), generics, - arg_names, + arg_idents, None, ); - self.end(); // end head-ibox + self.end(ib); self.word(";"); - self.end() // end the outer fn box + self.end(cb) } hir::ForeignItemKind::Static(t, m, safety) => { self.print_safety(safety); - self.head("static"); + let (cb, ib) = self.head("static"); if m.is_mut() { self.word_space("mut"); } @@ -497,15 +498,15 @@ impl<'a> State<'a> { self.word_space(":"); self.print_type(t); self.word(";"); - self.end(); // end the head-ibox - self.end() // end the outer cbox + self.end(ib); + self.end(cb) } hir::ForeignItemKind::Type => { - self.head("type"); + let (cb, ib) = self.head("type"); self.print_ident(item.ident); self.word(";"); - self.end(); // end the head-ibox - self.end() // end the outer cbox + self.end(ib); + self.end(cb) } } } @@ -553,24 +554,6 @@ impl<'a> State<'a> { self.word(";") } - fn print_item_type( - &mut self, - item: &hir::Item<'_>, - generics: &hir::Generics<'_>, - inner: impl Fn(&mut Self), - ) { - self.head("type"); - self.print_ident(item.ident); - self.print_generic_params(generics.params); - self.end(); // end the inner ibox - - self.print_where_clause(generics); - self.space(); - inner(self); - self.word(";"); - self.end(); // end the outer ibox - } - fn print_item(&mut self, item: &hir::Item<'_>) { self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); @@ -578,126 +561,128 @@ impl<'a> State<'a> { self.print_attrs_as_outer(attrs); self.ann.pre(self, AnnNode::Item(item)); match item.kind { - hir::ItemKind::ExternCrate(orig_name) => { - self.head("extern crate"); + hir::ItemKind::ExternCrate(orig_name, ident) => { + let (cb, ib) = self.head("extern crate"); if let Some(orig_name) = orig_name { self.print_name(orig_name); self.space(); self.word("as"); self.space(); } - self.print_ident(item.ident); + self.print_ident(ident); self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } hir::ItemKind::Use(path, kind) => { - self.head("use"); + let (cb, ib) = self.head("use"); self.print_path(path, false); match kind { - hir::UseKind::Single => { - if path.segments.last().unwrap().ident != item.ident { + hir::UseKind::Single(ident) => { + if path.segments.last().unwrap().ident != ident { self.space(); self.word_space("as"); - self.print_ident(item.ident); + self.print_ident(ident); } self.word(";"); } hir::UseKind::Glob => self.word("::*;"), hir::UseKind::ListStem => self.word("::{};"), } - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } - hir::ItemKind::Static(ty, m, expr) => { - self.head("static"); + hir::ItemKind::Static(ident, ty, m, expr) => { + let (cb, ib) = self.head("static"); if m.is_mut() { self.word_space("mut"); } - self.print_ident(item.ident); + self.print_ident(ident); self.word_space(":"); self.print_type(ty); self.space(); - self.end(); // end the head-ibox + self.end(ib); self.word_space("="); self.ann.nested(self, Nested::Body(expr)); self.word(";"); - self.end(); // end the outer cbox + self.end(cb); } - hir::ItemKind::Const(ty, generics, expr) => { - self.head("const"); - self.print_ident(item.ident); + hir::ItemKind::Const(ident, ty, generics, expr) => { + let (cb, ib) = self.head("const"); + self.print_ident(ident); self.print_generic_params(generics.params); self.word_space(":"); self.print_type(ty); self.space(); - self.end(); // end the head-ibox + self.end(ib); self.word_space("="); self.ann.nested(self, Nested::Body(expr)); self.print_where_clause(generics); self.word(";"); - self.end(); // end the outer cbox + self.end(cb); } - hir::ItemKind::Fn { sig, generics, body, .. } => { - self.head(""); - self.print_fn( - sig.decl, - sig.header, - Some(item.ident.name), - generics, - &[], - Some(body), - ); + hir::ItemKind::Fn { ident, sig, generics, body, .. } => { + let (cb, ib) = self.head(""); + self.print_fn(sig.decl, sig.header, Some(ident.name), generics, &[], Some(body)); self.word(" "); - self.end(); // need to close a box - self.end(); // need to close a box + self.end(ib); + self.end(cb); self.ann.nested(self, Nested::Body(body)); } - hir::ItemKind::Macro(macro_def, _) => { - self.print_mac_def(macro_def, &item.ident, item.span, |_| {}); + hir::ItemKind::Macro(ident, macro_def, _) => { + self.print_mac_def(macro_def, &ident, item.span, |_| {}); } - hir::ItemKind::Mod(_mod) => { - self.head("mod"); - self.print_ident(item.ident); + hir::ItemKind::Mod(ident, mod_) => { + let (cb, ib) = self.head("mod"); + self.print_ident(ident); self.nbsp(); - self.bopen(); - self.print_mod(_mod, attrs); - self.bclose(item.span); + self.bopen(ib); + self.print_mod(mod_, attrs); + self.bclose(item.span, cb); } hir::ItemKind::ForeignMod { abi, items } => { - self.head("extern"); + let (cb, ib) = self.head("extern"); self.word_nbsp(abi.to_string()); - self.bopen(); + self.bopen(ib); self.print_attrs_as_inner(self.attrs(item.hir_id())); for item in items { self.ann.nested(self, Nested::ForeignItem(item.id)); } - self.bclose(item.span); + self.bclose(item.span, cb); } hir::ItemKind::GlobalAsm { asm, .. } => { - self.head("global_asm!"); + let (cb, ib) = self.head("global_asm!"); self.print_inline_asm(asm); - self.end() + self.word(";"); + self.end(cb); + self.end(ib); } - hir::ItemKind::TyAlias(ty, generics) => { - self.print_item_type(item, generics, |state| { - state.word_space("="); - state.print_type(ty); - }); + hir::ItemKind::TyAlias(ident, ty, generics) => { + let (cb, ib) = self.head("type"); + self.print_ident(ident); + self.print_generic_params(generics.params); + self.end(ib); + + self.print_where_clause(generics); + self.space(); + self.word_space("="); + self.print_type(ty); + self.word(";"); + self.end(cb); } - hir::ItemKind::Enum(ref enum_definition, params) => { - self.print_enum_def(enum_definition, params, item.ident.name, item.span); + hir::ItemKind::Enum(ident, ref enum_definition, params) => { + self.print_enum_def(enum_definition, params, ident.name, item.span); } - hir::ItemKind::Struct(ref struct_def, generics) => { - self.head("struct"); - self.print_struct(struct_def, generics, item.ident.name, item.span, true); + hir::ItemKind::Struct(ident, ref struct_def, generics) => { + let (cb, ib) = self.head("struct"); + self.print_struct(struct_def, generics, ident.name, item.span, true, cb, ib); } - hir::ItemKind::Union(ref struct_def, generics) => { - self.head("union"); - self.print_struct(struct_def, generics, item.ident.name, item.span, true); + hir::ItemKind::Union(ident, ref struct_def, generics) => { + let (cb, ib) = self.head("union"); + self.print_struct(struct_def, generics, ident.name, item.span, true, cb, ib); } hir::ItemKind::Impl(&hir::Impl { constness, @@ -710,7 +695,7 @@ impl<'a> State<'a> { self_ty, items, }) => { - self.head(""); + let (cb, ib) = self.head(""); self.print_defaultness(defaultness); self.print_safety(safety); self.word_nbsp("impl"); @@ -738,39 +723,39 @@ impl<'a> State<'a> { self.print_where_clause(generics); self.space(); - self.bopen(); + self.bopen(ib); self.print_attrs_as_inner(attrs); for impl_item in items { self.ann.nested(self, Nested::ImplItem(impl_item.id)); } - self.bclose(item.span); + self.bclose(item.span, cb); } - hir::ItemKind::Trait(is_auto, safety, generics, bounds, trait_items) => { - self.head(""); + hir::ItemKind::Trait(is_auto, safety, ident, generics, bounds, trait_items) => { + let (cb, ib) = self.head(""); self.print_is_auto(is_auto); self.print_safety(safety); self.word_nbsp("trait"); - self.print_ident(item.ident); + self.print_ident(ident); self.print_generic_params(generics.params); self.print_bounds(":", bounds); self.print_where_clause(generics); self.word(" "); - self.bopen(); + self.bopen(ib); for trait_item in trait_items { self.ann.nested(self, Nested::TraitItem(trait_item.id)); } - self.bclose(item.span); + self.bclose(item.span, cb); } - hir::ItemKind::TraitAlias(generics, bounds) => { - self.head("trait"); - self.print_ident(item.ident); + hir::ItemKind::TraitAlias(ident, generics, bounds) => { + let (cb, ib) = self.head("trait"); + self.print_ident(ident); self.print_generic_params(generics.params); self.nbsp(); self.print_bounds("=", bounds); self.print_where_clause(generics); self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } } self.ann.post(self, AnnNode::Item(item)) @@ -780,13 +765,6 @@ impl<'a> State<'a> { self.print_path(t.path, false); } - fn print_opaque_ty(&mut self, o: &hir::OpaqueTy<'_>) { - self.head("opaque"); - self.word("{"); - self.print_bounds("impl", o.bounds); - self.word("}"); - } - fn print_formal_generic_params(&mut self, generic_params: &[hir::GenericParam<'_>]) { if !generic_params.is_empty() { self.word("for"); @@ -818,27 +796,33 @@ impl<'a> State<'a> { name: Symbol, span: rustc_span::Span, ) { - self.head("enum"); + let (cb, ib) = self.head("enum"); self.print_name(name); self.print_generic_params(generics.params); self.print_where_clause(generics); self.space(); - self.print_variants(enum_definition.variants, span); + self.print_variants(enum_definition.variants, span, cb, ib); } - fn print_variants(&mut self, variants: &[hir::Variant<'_>], span: rustc_span::Span) { - self.bopen(); + fn print_variants( + &mut self, + variants: &[hir::Variant<'_>], + span: rustc_span::Span, + cb: BoxMarker, + ib: BoxMarker, + ) { + self.bopen(ib); for v in variants { self.space_if_not_bol(); self.maybe_print_comment(v.span.lo()); self.print_attrs_as_outer(self.attrs(v.hir_id)); - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.print_variant(v); self.word(","); - self.end(); + self.end(ib); self.maybe_print_trailing_comment(v.span, None); } - self.bclose(span) + self.bclose(span, cb) } fn print_defaultness(&mut self, defaultness: hir::Defaultness) { @@ -855,6 +839,8 @@ impl<'a> State<'a> { name: Symbol, span: rustc_span::Span, print_finalizer: bool, + cb: BoxMarker, + ib: BoxMarker, ) { self.print_name(name); self.print_generic_params(generics.params); @@ -873,38 +859,34 @@ impl<'a> State<'a> { if print_finalizer { self.word(";"); } - self.end(); - self.end() // close the outer-box + self.end(ib); + self.end(cb); } hir::VariantData::Struct { .. } => { self.print_where_clause(generics); - self.print_variant_struct(span, struct_def.fields()) - } - } - } - - fn print_variant_struct(&mut self, span: rustc_span::Span, fields: &[hir::FieldDef<'_>]) { - self.nbsp(); - self.bopen(); - self.hardbreak_if_not_bol(); + self.nbsp(); + self.bopen(ib); + self.hardbreak_if_not_bol(); + + for field in struct_def.fields() { + self.hardbreak_if_not_bol(); + self.maybe_print_comment(field.span.lo()); + self.print_attrs_as_outer(self.attrs(field.hir_id)); + self.print_ident(field.ident); + self.word_nbsp(":"); + self.print_type(field.ty); + self.word(","); + } - for field in fields { - self.hardbreak_if_not_bol(); - self.maybe_print_comment(field.span.lo()); - self.print_attrs_as_outer(self.attrs(field.hir_id)); - self.print_ident(field.ident); - self.word_nbsp(":"); - self.print_type(field.ty); - self.word(","); + self.bclose(span, cb) + } } - - self.bclose(span) } pub fn print_variant(&mut self, v: &hir::Variant<'_>) { - self.head(""); + let (cb, ib) = self.head(""); let generics = hir::Generics::empty(); - self.print_struct(&v.data, generics, v.ident.name, v.span, false); + self.print_struct(&v.data, generics, v.ident.name, v.span, false, cb, ib); if let Some(ref d) = v.disr_expr { self.space(); self.word_space("="); @@ -917,10 +899,10 @@ impl<'a> State<'a> { ident: Ident, m: &hir::FnSig<'_>, generics: &hir::Generics<'_>, - arg_names: &[Ident], + arg_idents: &[Option], body_id: Option, ) { - self.print_fn(m.decl, m.header, Some(ident.name), generics, arg_names, body_id); + self.print_fn(m.decl, m.header, Some(ident.name), generics, arg_idents, body_id); } fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) { @@ -932,16 +914,16 @@ impl<'a> State<'a> { hir::TraitItemKind::Const(ty, default) => { self.print_associated_const(ti.ident, ti.generics, ty, default); } - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(arg_names)) => { - self.print_method_sig(ti.ident, sig, ti.generics, arg_names, None); + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(arg_idents)) => { + self.print_method_sig(ti.ident, sig, ti.generics, arg_idents, None); self.word(";"); } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - self.head(""); + let (cb, ib) = self.head(""); self.print_method_sig(ti.ident, sig, ti.generics, &[], Some(body)); self.nbsp(); - self.end(); // need to close a box - self.end(); // need to close a box + self.end(ib); + self.end(cb); self.ann.nested(self, Nested::Body(body)); } hir::TraitItemKind::Type(bounds, default) => { @@ -962,11 +944,11 @@ impl<'a> State<'a> { self.print_associated_const(ii.ident, ii.generics, ty, Some(expr)); } hir::ImplItemKind::Fn(ref sig, body) => { - self.head(""); + let (cb, ib) = self.head(""); self.print_method_sig(ii.ident, sig, ii.generics, &[], Some(body)); self.nbsp(); - self.end(); // need to close a box - self.end(); // need to close a box + self.end(ib); + self.end(cb); self.ann.nested(self, Nested::Body(body)); } hir::ImplItemKind::Type(ty) => { @@ -978,17 +960,21 @@ impl<'a> State<'a> { fn print_local( &mut self, + super_: bool, init: Option<&hir::Expr<'_>>, els: Option<&hir::Block<'_>>, decl: impl Fn(&mut Self), ) { self.space_if_not_bol(); - self.ibox(INDENT_UNIT); + let ibm1 = self.ibox(INDENT_UNIT); + if super_ { + self.word_nbsp("super"); + } self.word_nbsp("let"); - self.ibox(INDENT_UNIT); + let ibm2 = self.ibox(INDENT_UNIT); decl(self); - self.end(); + self.end(ibm2); if let Some(init) = init { self.nbsp(); @@ -1000,20 +986,22 @@ impl<'a> State<'a> { self.nbsp(); self.word_space("else"); // containing cbox, will be closed by print-block at `}` - self.cbox(0); + let cb = self.cbox(0); // head-box, will be closed by print-block after `{` - self.ibox(0); - self.print_block(els); + let ib = self.ibox(0); + self.print_block(els, cb, ib); } - self.end() + self.end(ibm1) } fn print_stmt(&mut self, st: &hir::Stmt<'_>) { self.maybe_print_comment(st.span.lo()); match st.kind { hir::StmtKind::Let(loc) => { - self.print_local(loc.init, loc.els, |this| this.print_local_decl(loc)); + self.print_local(loc.super_.is_some(), loc.init, loc.els, |this| { + this.print_local_decl(loc) + }); } hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)), hir::StmtKind::Expr(expr) => { @@ -1032,23 +1020,30 @@ impl<'a> State<'a> { self.maybe_print_trailing_comment(st.span, None) } - fn print_block(&mut self, blk: &hir::Block<'_>) { - self.print_block_with_attrs(blk, &[]) + fn print_block(&mut self, blk: &hir::Block<'_>, cb: BoxMarker, ib: BoxMarker) { + self.print_block_with_attrs(blk, &[], cb, ib) } - fn print_block_unclosed(&mut self, blk: &hir::Block<'_>) { - self.print_block_maybe_unclosed(blk, &[], false) + fn print_block_unclosed(&mut self, blk: &hir::Block<'_>, ib: BoxMarker) { + self.print_block_maybe_unclosed(blk, &[], None, ib) } - fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[hir::Attribute]) { - self.print_block_maybe_unclosed(blk, attrs, true) + fn print_block_with_attrs( + &mut self, + blk: &hir::Block<'_>, + attrs: &[hir::Attribute], + cb: BoxMarker, + ib: BoxMarker, + ) { + self.print_block_maybe_unclosed(blk, attrs, Some(cb), ib) } fn print_block_maybe_unclosed( &mut self, blk: &hir::Block<'_>, attrs: &[hir::Attribute], - close_box: bool, + cb: Option, + ib: BoxMarker, ) { match blk.rules { hir::BlockCheckMode::UnsafeBlock(..) => self.word_space("unsafe"), @@ -1056,7 +1051,7 @@ impl<'a> State<'a> { } self.maybe_print_comment(blk.span.lo()); self.ann.pre(self, AnnNode::Block(blk)); - self.bopen(); + self.bopen(ib); self.print_attrs_as_inner(attrs); @@ -1068,7 +1063,7 @@ impl<'a> State<'a> { self.print_expr(expr); self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi())); } - self.bclose_maybe_open(blk.span, close_box); + self.bclose_maybe_open(blk.span, cb); self.ann.post(self, AnnNode::Block(blk)) } @@ -1076,21 +1071,21 @@ impl<'a> State<'a> { if let Some(els_inner) = els { match els_inner.kind { // Another `else if` block. - hir::ExprKind::If(i, then, e) => { - self.cbox(INDENT_UNIT - 1); - self.ibox(0); + hir::ExprKind::If(i, hir::Expr { kind: hir::ExprKind::Block(t, None), .. }, e) => { + let cb = self.cbox(0); + let ib = self.ibox(0); self.word(" else if "); self.print_expr_as_cond(i); self.space(); - self.print_expr(then); + self.print_block(t, cb, ib); self.print_else(e); } // Final `else` block. - hir::ExprKind::Block(b, _) => { - self.cbox(INDENT_UNIT - 1); - self.ibox(0); + hir::ExprKind::Block(b, None) => { + let cb = self.cbox(0); + let ib = self.ibox(0); self.word(" else "); - self.print_block(b); + self.print_block(b, cb, ib); } // Constraints would be great here! _ => { @@ -1106,11 +1101,18 @@ impl<'a> State<'a> { blk: &hir::Expr<'_>, elseopt: Option<&hir::Expr<'_>>, ) { - self.head("if"); - self.print_expr_as_cond(test); - self.space(); - self.print_expr(blk); - self.print_else(elseopt) + match blk.kind { + hir::ExprKind::Block(blk, None) => { + let cb = self.cbox(0); + let ib = self.ibox(0); + self.word_nbsp("if"); + self.print_expr_as_cond(test); + self.space(); + self.print_block(blk, cb, ib); + self.print_else(elseopt) + } + _ => panic!("non-block then expr"), + } } fn print_anon_const(&mut self, constant: &hir::AnonConst) { @@ -1180,28 +1182,28 @@ impl<'a> State<'a> { } fn print_expr_vec(&mut self, exprs: &[hir::Expr<'_>]) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("["); self.commasep_exprs(Inconsistent, exprs); self.word("]"); - self.end() + self.end(ib) } fn print_inline_const(&mut self, constant: &hir::ConstBlock) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word_space("const"); self.ann.nested(self, Nested::Body(constant.body)); - self.end() + self.end(ib) } fn print_expr_repeat(&mut self, element: &hir::Expr<'_>, count: &hir::ConstArg<'_>) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("["); self.print_expr(element); self.word_space(";"); self.print_const_arg(count); self.word("]"); - self.end() + self.end(ib) } fn print_expr_struct( @@ -1211,50 +1213,44 @@ impl<'a> State<'a> { wth: hir::StructTailExpr<'_>, ) { self.print_qpath(qpath, true); - self.word("{"); + self.nbsp(); + self.word_space("{"); self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span); match wth { hir::StructTailExpr::Base(expr) => { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); if !fields.is_empty() { self.word(","); self.space(); } self.word(".."); self.print_expr(expr); - self.end(); + self.end(ib); } hir::StructTailExpr::DefaultFields(_) => { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); if !fields.is_empty() { self.word(","); self.space(); } self.word(".."); - self.end(); - } - hir::StructTailExpr::None => { - if !fields.is_empty() { - self.word(","); - } + self.end(ib); } + hir::StructTailExpr::None => {} } - + self.space(); self.word("}"); } fn print_expr_field(&mut self, field: &hir::ExprField<'_>) { - if self.attrs(field.hir_id).is_empty() { - self.space(); - } - self.cbox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); self.print_attrs_as_outer(self.attrs(field.hir_id)); if !field.is_shorthand { self.print_ident(field.ident); self.word_space(":"); } self.print_expr(field.expr); - self.end() + self.end(cb) } fn print_expr_tup(&mut self, exprs: &[hir::Expr<'_>]) { @@ -1295,18 +1291,18 @@ impl<'a> State<'a> { self.print_call_post(base_args) } - fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { - let binop_prec = op.node.precedence(); + fn print_expr_binary(&mut self, op: hir::BinOpKind, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { + let binop_prec = op.precedence(); let left_prec = lhs.precedence(); let right_prec = rhs.precedence(); - let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() { + let (mut left_needs_paren, right_needs_paren) = match op.fixity() { Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec), Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec), }; - match (&lhs.kind, op.node) { + match (&lhs.kind, op) { // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead // of `(x as i32) < ...`. We need to convince it _not_ to do that. @@ -1321,7 +1317,7 @@ impl<'a> State<'a> { self.print_expr_cond_paren(lhs, left_needs_paren); self.space(); - self.word_space(op.node.as_str()); + self.word_space(op.as_str()); self.print_expr_cond_paren(rhs, right_needs_paren); } @@ -1427,8 +1423,8 @@ impl<'a> State<'a> { s.print_qpath(path, true); } hir::InlineAsmOperand::Label { block } => { - s.head("label"); - s.print_block(block); + let (cb, ib) = s.head("label"); + s.print_block(block, cb, ib); } }, AsmArg::Options(opts) => { @@ -1446,7 +1442,7 @@ impl<'a> State<'a> { fn print_expr(&mut self, expr: &hir::Expr<'_>) { self.maybe_print_comment(expr.span.lo()); self.print_attrs_as_outer(self.attrs(expr.hir_id)); - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.ann.pre(self, AnnNode::Expr(expr)); match expr.kind { hir::ExprKind::Array(exprs) => { @@ -1470,8 +1466,12 @@ impl<'a> State<'a> { hir::ExprKind::MethodCall(segment, receiver, args, _) => { self.print_expr_method_call(segment, receiver, args); } + hir::ExprKind::Use(expr, _) => { + self.print_expr(expr); + self.word(".use"); + } hir::ExprKind::Binary(op, lhs, rhs) => { - self.print_expr_binary(op, lhs, rhs); + self.print_expr_binary(op.node, lhs, rhs); } hir::ExprKind::Unary(op, expr) => { self.print_expr_unary(op, expr); @@ -1490,25 +1490,25 @@ impl<'a> State<'a> { } hir::ExprKind::Type(expr, ty) => { self.word("type_ascribe!("); - self.ibox(0); + let ib = self.ibox(0); self.print_expr(expr); self.word(","); self.space_if_not_bol(); self.print_type(ty); - self.end(); + self.end(ib); self.word(")"); } hir::ExprKind::DropTemps(init) => { // Print `{`: - self.cbox(INDENT_UNIT); - self.ibox(0); - self.bopen(); + let cb = self.cbox(0); + let ib = self.ibox(0); + self.bopen(ib); // Print `let _t = $init;`: let temp = Ident::from_str("_t"); - self.print_local(Some(init), None, |this| this.print_ident(temp)); + self.print_local(false, Some(init), None, |this| this.print_ident(temp)); self.word(";"); // Print `_t`: @@ -1516,7 +1516,7 @@ impl<'a> State<'a> { self.print_ident(temp); // Print `}`: - self.bclose_maybe_open(expr.span, true); + self.bclose_maybe_open(expr.span, Some(cb)); } hir::ExprKind::Let(&hir::LetExpr { pat, ty, init, .. }) => { self.print_let(pat, ty, init); @@ -1525,24 +1525,26 @@ impl<'a> State<'a> { self.print_if(test, blk, elseopt); } hir::ExprKind::Loop(blk, opt_label, _, _) => { + let cb = self.cbox(0); + let ib = self.ibox(0); if let Some(label) = opt_label { self.print_ident(label.ident); self.word_space(":"); } - self.head("loop"); - self.print_block(blk); + self.word_nbsp("loop"); + self.print_block(blk, cb, ib); } hir::ExprKind::Match(expr, arms, _) => { - self.cbox(INDENT_UNIT); - self.ibox(INDENT_UNIT); + let cb = self.cbox(0); + let ib = self.ibox(0); self.word_nbsp("match"); self.print_expr_as_cond(expr); self.space(); - self.bopen(); + self.bopen(ib); for arm in arms { self.print_arm(arm); } - self.bclose(expr.span); + self.bclose(expr.span, cb); } hir::ExprKind::Closure(&hir::Closure { binder, @@ -1565,12 +1567,6 @@ impl<'a> State<'a> { // This is a bare expression. self.ann.nested(self, Nested::Body(body)); - self.end(); // need to close a box - - // A box will be closed by `print_expr`, but we didn't want an overall - // wrapper so we closed the corresponding opening. so create an - // empty box to satisfy the close. - self.ibox(0); } hir::ExprKind::Block(blk, opt_label) => { if let Some(label) = opt_label { @@ -1578,10 +1574,10 @@ impl<'a> State<'a> { self.word_space(":"); } // containing cbox, will be closed by print-block at `}` - self.cbox(0); + let cb = self.cbox(0); // head-box, will be closed by print-block after `{` - self.ibox(0); - self.print_block(blk); + let ib = self.ibox(0); + self.print_block(blk, cb, ib); } hir::ExprKind::Assign(lhs, rhs, _) => { self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign); @@ -1592,8 +1588,7 @@ impl<'a> State<'a> { hir::ExprKind::AssignOp(op, lhs, rhs) => { self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign); self.space(); - self.word(op.node.as_str()); - self.word_space("="); + self.word_space(op.node.as_str()); self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign); } hir::ExprKind::Field(expr, ident) => { @@ -1683,7 +1678,7 @@ impl<'a> State<'a> { } } self.ann.post(self, AnnNode::Expr(expr)); - self.end() + self.end(ib) } fn print_local_decl(&mut self, loc: &hir::LetStmt<'_>) { @@ -1869,17 +1864,23 @@ impl<'a> State<'a> { // Pat isn't normalized, but the beauty of it // is that it doesn't matter match pat.kind { - TyPatKind::Range(begin, end, end_kind) => { - if let Some(expr) = begin { - self.print_const_arg(expr); - } - match end_kind { - RangeEnd::Included => self.word("..."), - RangeEnd::Excluded => self.word(".."), - } - if let Some(expr) = end { - self.print_const_arg(expr); + TyPatKind::Range(begin, end) => { + self.print_const_arg(begin); + self.word("..="); + self.print_const_arg(end); + } + TyPatKind::Or(patterns) => { + self.popen(); + let mut first = true; + for pat in patterns { + if first { + first = false; + } else { + self.word(" | "); + } + self.print_ty_pat(pat); } + self.pclose(); } TyPatKind::Err(_) => { self.popen(); @@ -1893,9 +1894,11 @@ impl<'a> State<'a> { fn print_pat(&mut self, pat: &hir::Pat<'_>) { self.maybe_print_comment(pat.span.lo()); self.ann.pre(self, AnnNode::Pat(pat)); - // Pat isn't normalized, but the beauty of it - // is that it doesn't matter + // Pat isn't normalized, but the beauty of it is that it doesn't matter. match pat.kind { + // Printing `_` isn't ideal for a missing pattern, but it's easy and good enough. + // E.g. `fn(u32)` gets printed as `fn(_: u32)`. + PatKind::Missing => self.word("_"), PatKind::Wild => self.word("_"), PatKind::Never => self.word("!"), PatKind::Binding(BindingMode(by_ref, mutbl), _, ident, sub) => { @@ -2056,14 +2059,14 @@ impl<'a> State<'a> { if self.attrs(field.hir_id).is_empty() { self.space(); } - self.cbox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); self.print_attrs_as_outer(self.attrs(field.hir_id)); if !field.is_shorthand { self.print_ident(field.ident); self.word_nbsp(":"); } self.print_pat(field.pat); - self.end(); + self.end(cb); } fn print_param(&mut self, arg: &hir::Param<'_>) { @@ -2099,9 +2102,9 @@ impl<'a> State<'a> { if self.attrs(arm.hir_id).is_empty() { self.space(); } - self.cbox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); self.ann.pre(self, AnnNode::Arm(arm)); - self.ibox(0); + let ib = self.ibox(0); self.print_attrs_as_outer(self.attrs(arm.hir_id)); self.print_pat(arm.pat); self.space(); @@ -2118,8 +2121,7 @@ impl<'a> State<'a> { self.print_ident(label.ident); self.word_space(":"); } - // the block will close the pattern's ibox - self.print_block_unclosed(blk); + self.print_block_unclosed(blk, ib); // If it is a user-provided unsafe block, print a comma after it if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = blk.rules @@ -2128,13 +2130,13 @@ impl<'a> State<'a> { } } _ => { - self.end(); // close the ibox for the pattern + self.end(ib); self.print_expr(arm.body); self.word(","); } } self.ann.post(self, AnnNode::Arm(arm)); - self.end() // close enclosing cbox + self.end(cb) } fn print_fn( @@ -2143,7 +2145,7 @@ impl<'a> State<'a> { header: hir::FnHeader, name: Option, generics: &hir::Generics<'_>, - arg_names: &[Ident], + arg_idents: &[Option], body_id: Option, ) { self.print_fn_header_info(header); @@ -2155,17 +2157,19 @@ impl<'a> State<'a> { self.print_generic_params(generics.params); self.popen(); - // Make sure we aren't supplied *both* `arg_names` and `body_id`. - assert!(arg_names.is_empty() || body_id.is_none()); + // Make sure we aren't supplied *both* `arg_idents` and `body_id`. + assert!(arg_idents.is_empty() || body_id.is_none()); let mut i = 0; let mut print_arg = |s: &mut Self, ty: Option<&hir::Ty<'_>>| { if i == 0 && decl.implicit_self.has_implicit_self() { s.print_implicit_self(&decl.implicit_self); } else { - if let Some(arg_name) = arg_names.get(i) { - s.word(arg_name.to_string()); - s.word(":"); - s.space(); + if let Some(arg_ident) = arg_idents.get(i) { + if let Some(arg_ident) = arg_ident { + s.word(arg_ident.to_string()); + s.word(":"); + s.space(); + } } else if let Some(body_id) = body_id { s.ann.nested(s, Nested::BodyParamPat(body_id, i)); s.word(":"); @@ -2178,12 +2182,14 @@ impl<'a> State<'a> { i += 1; }; self.commasep(Inconsistent, decl.inputs, |s, ty| { - s.ibox(INDENT_UNIT); + let ib = s.ibox(INDENT_UNIT); print_arg(s, Some(ty)); - s.end(); + s.end(ib); }); if decl.c_variadic { - self.word(", "); + if !decl.inputs.is_empty() { + self.word(", "); + } print_arg(self, None); self.word("..."); } @@ -2197,7 +2203,7 @@ impl<'a> State<'a> { self.word("|"); let mut i = 0; self.commasep(Inconsistent, decl.inputs, |s, ty| { - s.ibox(INDENT_UNIT); + let ib = s.ibox(INDENT_UNIT); s.ann.nested(s, Nested::BodyParamPat(body_id, i)); i += 1; @@ -2209,7 +2215,7 @@ impl<'a> State<'a> { s.space(); s.print_type(ty); } - s.end(); + s.end(ib); }); self.word("|"); @@ -2227,6 +2233,7 @@ impl<'a> State<'a> { fn print_capture_clause(&mut self, capture_clause: hir::CaptureBy) { match capture_clause { hir::CaptureBy::Value { .. } => self.word_space("move"), + hir::CaptureBy::Use { .. } => self.word_space("use"), hir::CaptureBy::Ref => {} } } @@ -2386,6 +2393,7 @@ impl<'a> State<'a> { } fn print_where_predicate(&mut self, predicate: &hir::WherePredicate<'_>) { + self.print_attrs_as_outer(self.attrs(predicate.hir_id)); match *predicate.kind { hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { bound_generic_params, @@ -2449,16 +2457,16 @@ impl<'a> State<'a> { match decl.output { hir::FnRetTy::Return(ty) => { self.space_if_not_bol(); - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word_space("->"); self.print_type(ty); - } - hir::FnRetTy::DefaultReturn(..) => return, - } - self.end(); + self.end(ib); - if let hir::FnRetTy::Return(output) = decl.output { - self.maybe_print_comment(output.span.lo()); + if let hir::FnRetTy::Return(output) = decl.output { + self.maybe_print_comment(output.span.lo()); + } + } + hir::FnRetTy::DefaultReturn(..) => {} } } @@ -2469,9 +2477,9 @@ impl<'a> State<'a> { decl: &hir::FnDecl<'_>, name: Option, generic_params: &[hir::GenericParam<'_>], - arg_names: &[Ident], + arg_idents: &[Option], ) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.print_formal_generic_params(generic_params); let generics = hir::Generics::empty(); self.print_fn( @@ -2484,10 +2492,10 @@ impl<'a> State<'a> { }, name, generics, - arg_names, + arg_idents, None, ); - self.end(); + self.end(ib); } fn print_fn_header_info(&mut self, header: hir::FnHeader) { diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index f1afb7b712d59..c963f0ddefd4c 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -22,8 +23,8 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -rustc_type_ir = { path = "../rustc_type_ir" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index ed80bc3e7be7e..23309102c4da5 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -148,7 +148,7 @@ hir_typeck_never_type_fallback_flowing_into_unsafe_path = never type fallback af hir_typeck_never_type_fallback_flowing_into_unsafe_union_field = never type fallback affects this union access .help = specify the type explicitly -hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method -> +hir_typeck_no_associated_item = no {$item_kind} named `{$item_ident}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method -> [true] {""} *[other] {" "}in the current scope } @@ -171,10 +171,16 @@ hir_typeck_pass_to_variadic_function = can't pass `{$ty}` to variadic function .suggestion = cast the value to `{$cast_ty}` .teach_help = certain types, like `{$ty}`, must be casted before passing them to a variadic function, because of arcane ABI rules dictated by the C standard -hir_typeck_ptr_cast_add_auto_to_object = adding {$traits_len -> - [1] an auto trait {$traits} +hir_typeck_ptr_cast_add_auto_to_object = cannot add {$traits_len -> + [1] auto trait {$traits} *[other] auto traits {$traits} -} to a trait object in a pointer cast may cause UB later on +} to dyn bound via pointer cast + .note = this could allow UB elsewhere + .help = use `transmute` if you're sure this is sound + .label = unsupported cast + +hir_typeck_register_type_unstable = + type `{$ty}` cannot be used with this register class in stable hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression hir_typeck_remove_semi_for_coerce_expr = this could be implicitly returned but it is a statement, not a tail expression diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 38319862334a8..61dd8c5730734 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -11,7 +11,7 @@ use rustc_trait_selection::traits::{ use tracing::{debug, instrument}; use crate::coercion::{AsCoercionSite, CoerceMany}; -use crate::{Diverges, Expectation, FnCtxt, Needs}; +use crate::{Diverges, Expectation, FnCtxt, GatherLocalsVisitor, Needs}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug", ret)] @@ -43,6 +43,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // #55810: Type check patterns first so we get types for all bindings. let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span); for arm in arms { + GatherLocalsVisitor::gather_from_arm(self, arm); + self.check_pat_top(arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut), None); } @@ -601,7 +603,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME(-Znext-solver): Remove this branch once `replace_opaque_types_with_infer` is gone. ty::Infer(ty::TyVar(_)) => self .inner - .borrow() + .borrow_mut() + .opaque_types() .iter_opaque_types() .find(|(_, v)| v.ty == expected_ty) .map(|(k, _)| (k.def_id, k.args))?, diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 5e00161f693f6..f555d116c52db 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::adjustment::{ use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; -use rustc_span::{Ident, Span, sym}; +use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::traits::DefIdOrName; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -34,11 +34,9 @@ pub(crate) fn check_legal_trait_for_method_call( receiver: Option, expr_span: Span, trait_id: DefId, - body_id: DefId, + _body_id: DefId, ) -> Result<(), ErrorGuaranteed> { - if tcx.is_lang_item(trait_id, LangItem::Drop) - && tcx.lang_items().fallback_surface_drop_fn() != Some(body_id) - { + if tcx.is_lang_item(trait_id, LangItem::Drop) { let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) { errors::ExplicitDestructorCallSugg::Snippet { lo: expr_span.shrink_to_lo(), @@ -89,14 +87,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let output = match result { None => { - // this will report an error since original_callee_ty is not a fn - self.confirm_builtin_call( - call_expr, - callee_expr, - original_callee_ty, - arg_exprs, - expected, - ) + // Check all of the arg expressions, but with no expectations + // since we don't have a signature to compare them to. + for arg in arg_exprs { + self.check_expr(arg); + } + + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind + && let [segment] = path.segments + { + self.dcx().try_steal_modify_and_emit_err( + segment.ident.span, + StashKey::CallIntoMethod, + |err| { + // Try suggesting `foo(a)` -> `a.foo()` if possible. + self.suggest_call_as_method( + err, segment, arg_exprs, call_expr, expected, + ); + }, + ); + } + + let guar = self.report_invalid_callee(call_expr, callee_expr, expr_ty, arg_exprs); + Ty::new_error(self.tcx, guar) } Some(CallStep::Builtin(callee_ty)) => { @@ -298,9 +311,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span))) }); - if let Some(ok) = self.lookup_method_in_trait( + if let Some(ok) = self.lookup_method_for_operator( self.misc(call_expr.span), - Ident::with_dummy_span(method_name), + method_name, trait_def_id, adjusted_ty, opt_input_type, @@ -463,32 +476,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } (fn_sig, Some(def_id)) } + // FIXME(const_trait_impl): these arms should error because we can't enforce them ty::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None), - _ => { - for arg in arg_exprs { - self.check_expr(arg); - } - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind - && let [segment] = path.segments - { - self.dcx().try_steal_modify_and_emit_err( - segment.ident.span, - StashKey::CallIntoMethod, - |err| { - // Try suggesting `foo(a)` -> `a.foo()` if possible. - self.suggest_call_as_method( - err, segment, arg_exprs, call_expr, expected, - ); - }, - ); - } - - let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs); - - return Ty::new_error(self.tcx, err); - } + _ => unreachable!(), }; // Replace any late-bound regions that appear in the function @@ -713,8 +705,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for id in self.tcx.hir_free_items() { if let Some(node) = self.tcx.hir_get_if_local(id.owner_id.into()) && let hir::Node::Item(item) = node - && let hir::ItemKind::Fn { .. } = item.kind - && item.ident.name == segment.ident.name + && let hir::ItemKind::Fn { ident, .. } = item.kind + && ident.name == segment.ident.name { err.span_label( self.tcx.def_span(id.owner_id), @@ -771,7 +763,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("this {descr} returns an unsized value `{output_ty}`, so it cannot be called") ); if let DefIdOrName::DefId(def_id) = maybe_def - && let Some(def_span) = self.tcx.hir().span_if_local(def_id) + && let Some(def_span) = self.tcx.hir_span_if_local(def_id) { err.span_label(def_span, "the callable type is defined here"); } @@ -780,7 +772,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - if let Some(span) = self.tcx.hir().res_span(def) { + 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((_, kind, path)), _) => { @@ -799,7 +791,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Emit a different diagnostic for local variables, as they are not // type definitions themselves, but rather variables *of* that type. Res::Local(hir_id) => { - err.arg("local_name", self.tcx.hir().name(hir_id)); + err.arg("local_name", self.tcx.hir_name(hir_id)); Some(fluent_generated::hir_typeck_invalid_local) } Res::Def(kind, def_id) if kind.ns() == Some(Namespace::ValueNS) => { @@ -910,19 +902,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, - method_callee: MethodCallee<'tcx>, + method: MethodCallee<'tcx>, ) -> Ty<'tcx> { - let output_type = self.check_method_argument_types( + self.check_argument_types( call_expr.span, call_expr, - Ok(method_callee), + &method.sig.inputs()[1..], + method.sig.output(), + expected, arg_exprs, + method.sig.c_variadic, TupleArgumentsFlag::TupleArguments, - expected, + Some(method.def_id), ); - self.write_method_call_and_enforce_effects(call_expr.hir_id, call_expr.span, method_callee); - output_type + self.write_method_call_and_enforce_effects(call_expr.hir_id, call_expr.span, method); + + method.sig.output() } } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index ea4ec345ad0c9..c044c4f7c37de 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -32,6 +32,7 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; +use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, ExprKind}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_macros::{TypeFoldable, TypeVisitable}; @@ -39,13 +40,11 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, VariantDef}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, VariantDef, elaborate}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; -use rustc_span::def_id::LOCAL_CRATE; use rustc_span::{DUMMY_SP, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_type_ir::elaborate; use tracing::{debug, instrument}; use super::FnCtxt; @@ -155,7 +154,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } -#[derive(Copy, Clone, Debug)] +#[derive(Debug)] enum CastError<'tcx> { ErrorGuaranteed(ErrorGuaranteed), @@ -182,6 +181,7 @@ enum CastError<'tcx> { /// when we're typechecking a type parameter with a ?Sized bound. IntToWideCast(Option<&'static str>), ForeignNonExhaustiveAdt, + PtrPtrAddingAutoTrait(Vec), } impl From for CastError<'_> { @@ -490,23 +490,32 @@ impl<'a, 'tcx> CastCheck<'tcx> { && let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::From) { 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); if fcx .infcx .type_implements_trait(from_trait, [ty, expr_ty], fcx.param_env) .must_apply_modulo_regions() { label = false; - err.span_suggestion( - self.span, - "consider using the `From` trait instead", - format!("{}::from({})", self.cast_ty, snippet), - Applicability::MaybeIncorrect, - ); + if let ty::Adt(def, args) = self.cast_ty.kind() { + err.span_suggestion_verbose( + self.span, + "consider using the `From` trait instead", + format!( + "{}::from({})", + fcx.tcx.value_path_str_with_args(def.did(), args), + snippet + ), + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion( + self.span, + "consider using the `From` trait instead", + format!("{}::from({})", self.cast_ty, snippet), + Applicability::MaybeIncorrect, + ); + }; } } @@ -596,6 +605,21 @@ impl<'a, 'tcx> CastCheck<'tcx> { .with_note("cannot cast an enum with a non-exhaustive variant when it's defined in another crate") .emit(); } + CastError::PtrPtrAddingAutoTrait(added) => { + fcx.dcx().emit_err(errors::PtrCastAddAutoToObject { + span: self.span, + traits_len: added.len(), + traits: { + let mut traits: Vec<_> = added + .into_iter() + .map(|trait_did| fcx.tcx.def_path_str(trait_did)) + .collect(); + + traits.sort(); + traits.into() + }, + }); + } } } @@ -789,7 +813,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { _ => return Err(CastError::NonScalar), }; if let ty::Adt(adt_def, _) = *self.expr_ty.kind() - && adt_def.did().krate != LOCAL_CRATE + && !adt_def.did().is_local() && adt_def.variants().iter().any(VariantDef::is_field_list_non_exhaustive) { return Err(CastError::ForeignNonExhaustiveAdt); @@ -940,23 +964,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { .collect::>(); if !added.is_empty() { - tcx.emit_node_span_lint( - lint::builtin::PTR_CAST_ADD_AUTO_TO_OBJECT, - self.expr.hir_id, - self.span, - errors::PtrCastAddAutoToObject { - traits_len: added.len(), - traits: { - let mut traits: Vec<_> = added - .into_iter() - .map(|trait_did| tcx.def_path_str(trait_did)) - .collect(); - - traits.sort(); - traits.into() - }, - }, - ) + return Err(CastError::PtrPtrAddingAutoTrait(added)); } Ok(CastKind::PtrPtrCast) @@ -1039,34 +1047,34 @@ impl<'a, 'tcx> CastCheck<'tcx> { fn check_ref_cast( &self, fcx: &FnCtxt<'a, 'tcx>, - m_expr: ty::TypeAndMut<'tcx>, - m_cast: ty::TypeAndMut<'tcx>, + mut m_expr: ty::TypeAndMut<'tcx>, + mut m_cast: ty::TypeAndMut<'tcx>, ) -> Result> { // array-ptr-cast: allow mut-to-mut, mut-to-const, const-to-const - if m_expr.mutbl >= m_cast.mutbl { - 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 RawPtr in MIR. - let array_ptr_type = Ty::new_ptr(fcx.tcx, m_expr.ty, m_expr.mutbl); - fcx.coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None) - .unwrap_or_else(|_| { - bug!( + m_expr.ty = fcx.try_structurally_resolve_type(self.expr_span, m_expr.ty); + m_cast.ty = fcx.try_structurally_resolve_type(self.cast_span, m_cast.ty); + + if m_expr.mutbl >= m_cast.mutbl + && let ty::Array(ety, _) = m_expr.ty.kind() + && fcx.can_eq(fcx.param_env, *ety, m_cast.ty) + { + // Due to historical reasons we allow directly casting references of + // arrays into raw pointers of their element type. + + // Coerce to a raw pointer so that we generate RawPtr in MIR. + let array_ptr_type = Ty::new_ptr(fcx.tcx, m_expr.ty, m_expr.mutbl); + fcx.coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None) + .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); - } + // this will report a type mismatch if needed + fcx.demand_eqtype(self.span, *ety, m_cast.ty); + return Ok(CastKind::ArrayPtrCast); } Err(CastError::IllegalCast) diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index dabae7b1d094c..99103f14d6823 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -3,9 +3,8 @@ use std::cell::RefCell; use rustc_abi::ExternAbi; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir_analysis::check::{check_function_signature, forbid_intrinsic_abi}; +use rustc_hir_analysis::check::check_function_signature; use rustc_infer::infer::RegionVariableOrigin; use rustc_infer::traits::WellFormedLoc; use rustc_middle::ty::{self, Binder, Ty, TyCtxt}; @@ -50,9 +49,9 @@ pub(super) fn check_fn<'a, 'tcx>( let span = body.value.span; - forbid_intrinsic_abi(tcx, span, fn_sig.abi); - - GatherLocalsVisitor::new(fcx).visit_body(body); + for param in body.params { + GatherLocalsVisitor::gather_from_param(fcx, param); + } // 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). diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 71a0664bbdff6..b1cb3ef4d7967 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -12,13 +12,14 @@ use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::span_bug; -use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::{self, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; +use rustc_middle::ty::{ + self, ClosureKind, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, +}; use rustc_span::def_id::LocalDefId; use rustc_span::{DUMMY_SP, Span}; use rustc_trait_selection::error_reporting::traits::ArgKind; use rustc_trait_selection::traits; -use rustc_type_ir::ClosureKind; use tracing::{debug, instrument, trace}; use super::{CoroutineTypes, Expectation, FnCtxt, check_fn}; @@ -162,12 +163,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Resume type defaults to `()` if the coroutine has no argument. let resume_ty = liberated_sig.inputs().get(0).copied().unwrap_or(tcx.types.unit); - let interior = self.next_ty_var(expr_span); - self.deferred_coroutine_interiors.borrow_mut().push(( - expr_def_id, - body.id(), - interior, - )); + // In the new solver, we can just instantiate this eagerly + // with the witness. This will ensure that goals that don't need + // to stall on interior types will get processed eagerly. + let interior = if self.next_trait_solver() { + Ty::new_coroutine_witness(tcx, expr_def_id.to_def_id(), parent_args) + } else { + self.next_ty_var(expr_span) + }; + + self.deferred_coroutine_interiors.borrow_mut().push((expr_def_id, interior)); // Coroutines that come from coroutine closures have not yet determined // their kind ty, so make a fresh infer var which will be constrained @@ -973,7 +978,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result); // Normalize only after registering in `user_provided_sigs`. - self.normalize(self.tcx.hir().span(hir_id), result) + self.normalize(self.tcx.def_span(expr_def_id), result) } /// Invoked when we are translating the coroutine that results @@ -1075,15 +1080,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check that this is a projection from the `Future` trait. let trait_def_id = predicate.projection_term.trait_def_id(self.tcx); - let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(cause_span)); - if trait_def_id != future_trait { + if !self.tcx.is_lang_item(trait_def_id, LangItem::Future) { debug!("deduce_future_output_from_projection: not a future"); return None; } // The `Future` trait has only one associated item, `Output`, // so check that this is what we see. - let output_assoc_item = self.tcx.associated_item_def_ids(future_trait)[0]; + let output_assoc_item = self.tcx.associated_item_def_ids(trait_def_id)[0]; if output_assoc_item != predicate.projection_term.def_id { span_bug!( cause_span, diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index a5108e7a0326d..9c07f97a73400 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -37,8 +37,7 @@ use std::ops::Deref; -use rustc_abi::ExternAbi; -use rustc_attr_parsing::InlineAttr; +use rustc_attr_data_structures::InlineAttr; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, struct_span_code_err}; use rustc_hir as hir; @@ -51,15 +50,12 @@ use rustc_infer::traits::{ PredicateObligations, }; use rustc_middle::span_bug; -use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, }; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; -use rustc_session::parse::feature_err; -use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span, sym}; +use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ @@ -107,15 +103,6 @@ fn coerce_mutbls<'tcx>( if from_mutbl >= to_mutbl { Ok(()) } else { Err(TypeError::Mutability) } } -/// Do not require any adjustments, i.e. coerce `x -> x`. -fn identity(_: Ty<'_>) -> Vec> { - vec![] -} - -fn simple<'tcx>(kind: Adjust) -> impl FnOnce(Ty<'tcx>) -> Vec> { - move |target| vec![Adjustment { kind, target }] -} - /// This always returns `Ok(...)`. fn success<'tcx>( adj: Vec>, @@ -135,7 +122,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Coerce { fcx, cause, allow_two_phase, use_lub: false, coerce_never } } - fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { + fn unify_raw(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub); self.commit_if_ok(|_| { let at = self.at(&self.cause, self.fcx.param_env); @@ -165,13 +152,30 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { }) } + /// Unify two types (using sub or lub). + fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + self.unify_raw(a, b) + .and_then(|InferOk { value: ty, obligations }| success(vec![], ty, obligations)) + } + /// Unify two types (using sub or lub) and produce a specific coercion. - fn unify_and(&self, a: Ty<'tcx>, b: Ty<'tcx>, f: F) -> CoerceResult<'tcx> - where - F: FnOnce(Ty<'tcx>) -> Vec>, - { - self.unify(a, b) - .and_then(|InferOk { value: ty, obligations }| success(f(ty), ty, obligations)) + fn unify_and( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + adjustments: impl IntoIterator>, + final_adjustment: Adjust, + ) -> CoerceResult<'tcx> { + self.unify_raw(a, b).and_then(|InferOk { value: ty, obligations }| { + success( + adjustments + .into_iter() + .chain(std::iter::once(Adjustment { target: ty, kind: final_adjustment })) + .collect(), + ty, + obligations, + ) + }) } #[instrument(skip(self))] @@ -184,10 +188,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Coercing from `!` to any type is allowed: if a.is_never() { if self.coerce_never { - return success(simple(Adjust::NeverToAny)(b), b, PredicateObligations::new()); + return success( + vec![Adjustment { kind: Adjust::NeverToAny, target: b }], + b, + PredicateObligations::new(), + ); } else { // Otherwise the only coercion we can do is unification. - return self.unify_and(a, b, identity); + return self.unify(a, b); } } @@ -195,7 +203,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // we have no information about the source type. This will always // ultimately fall back to some form of subtyping. if a.is_ty_var() { - return self.coerce_from_inference_variable(a, b, identity); + return self.coerce_from_inference_variable(a, b); } // Consider coercing the subtype to a DST @@ -251,7 +259,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::FnPtr(a_sig_tys, a_hdr) => { // We permit coercion of fn pointers to drop the // unsafe qualifier. - self.coerce_from_fn_pointer(a, a_sig_tys.with(a_hdr), b) + self.coerce_from_fn_pointer(a_sig_tys.with(a_hdr), b) } ty::Closure(closure_def_id_a, args_a) => { // Non-capturing closures are coercible to @@ -261,7 +269,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } _ => { // Otherwise, just use unification rules. - self.unify_and(a, b, identity) + self.unify(a, b) } } } @@ -269,12 +277,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { /// Coercing *from* an inference variable. In this case, we have no information /// about the source type, so we can't really do a true coercion and we always /// fall back to subtyping (`unify_and`). - fn coerce_from_inference_variable( - &self, - a: Ty<'tcx>, - b: Ty<'tcx>, - make_adjustments: impl FnOnce(Ty<'tcx>) -> Vec>, - ) -> CoerceResult<'tcx> { + fn coerce_from_inference_variable(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b); assert!(a.is_ty_var() && self.shallow_resolve(a) == a); assert!(self.shallow_resolve(b) == b); @@ -302,12 +305,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { "coerce_from_inference_variable: two inference variables, target_ty={:?}, obligations={:?}", target_ty, obligations ); - let adjustments = make_adjustments(target_ty); - InferResult::Ok(InferOk { value: (adjustments, target_ty), obligations }) + success(vec![], target_ty, obligations) } else { // One unresolved type variable: just apply subtyping, we may be able // to do something useful. - self.unify_and(a, b, make_adjustments) + self.unify(a, b) } } @@ -335,7 +337,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { coerce_mutbls(mt_a.mutbl, mutbl_b)?; (r_a, mt_a) } - _ => return self.unify_and(a, b, identity), + _ => return self.unify(a, b), }; let span = self.cause.span; @@ -441,7 +443,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { referent_ty, mutbl_b, // [1] above ); - match self.unify(derefd_ty_a, b) { + match self.unify_raw(derefd_ty_a, b) { Ok(ok) => { found = Some(ok); break; @@ -583,13 +585,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // We only have the latter, so we use an inference variable // for the former and let type inference do the rest. let coerce_target = self.next_ty_var(self.cause.span); - let mut coercion = self.unify_and(coerce_target, target, |target| { - let unsize = Adjustment { kind: Adjust::Pointer(PointerCoercion::Unsize), target }; - match reborrow { - None => vec![unsize], - Some((ref deref, ref autoref)) => vec![deref.clone(), autoref.clone(), unsize], - } - })?; + + let mut coercion = self.unify_and( + coerce_target, + target, + reborrow.into_iter().flat_map(|(deref, autoref)| [deref, autoref]), + Adjust::Pointer(PointerCoercion::Unsize), + )?; let mut selcx = traits::SelectionContext::new(self); @@ -610,8 +612,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]) )]; - let mut has_unsized_tuple_coercion = false; - // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where // inference might unify those two inner type variables later. @@ -690,31 +690,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // be silent, as it causes a type mismatch later. } - Ok(Some(impl_source)) => { - // Some builtin coercions are still unstable so we detect - // these here and emit a feature error if coercion doesn't fail - // due to another reason. - match impl_source { - traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { - has_unsized_tuple_coercion = true; - } - _ => {} - } - queue.extend(impl_source.nested_obligations()) - } + Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), } } - if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion() { - feature_err( - &self.tcx.sess, - sym::unsized_tuple_coercion, - self.cause.span, - "unsized tuple coercion is not stable enough for use and is subject to change", - ) - .emit(); - } - Ok(coercion) } @@ -735,7 +714,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { && let ty::Dynamic(b_data, _, ty::DynStar) = b.kind() && a_data.principal_def_id() == b_data.principal_def_id() { - return self.unify_and(a, b, |_| vec![]); + return self.unify(a, b); } // Check the obligations of the cast -- for example, when casting @@ -835,23 +814,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // To complete the reborrow, we need to make sure we can unify the inner types, and if so we // add the adjustments. - self.unify_and(a, b, |_inner_ty| { - vec![Adjustment { kind: Adjust::ReborrowPin(mut_b), target: b }] - }) + self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b)) } - fn coerce_from_safe_fn( + fn coerce_from_safe_fn( &self, - a: Ty<'tcx>, fn_ty_a: ty::PolyFnSig<'tcx>, b: Ty<'tcx>, - to_unsafe: F, - normal: G, - ) -> CoerceResult<'tcx> - where - F: FnOnce(Ty<'tcx>) -> Vec>, - G: FnOnce(Ty<'tcx>) -> Vec>, - { + adjustment: Option, + ) -> CoerceResult<'tcx> { self.commit_if_ok(|snapshot| { let outer_universe = self.infcx.universe(); @@ -860,9 +831,19 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { && hdr_b.safety.is_unsafe() { let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a); - self.unify_and(unsafe_a, b, to_unsafe) + self.unify_and( + unsafe_a, + b, + adjustment + .map(|kind| Adjustment { kind, target: Ty::new_fn_ptr(self.tcx, fn_ty_a) }), + Adjust::Pointer(PointerCoercion::UnsafeFnPointer), + ) } else { - self.unify_and(a, b, normal) + let a = Ty::new_fn_ptr(self.tcx, fn_ty_a); + match adjustment { + Some(adjust) => self.unify_and(a, b, [], adjust), + None => self.unify(a, b), + } }; // FIXME(#73154): This is a hack. Currently LUB can generate @@ -879,7 +860,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { fn coerce_from_fn_pointer( &self, - a: Ty<'tcx>, fn_ty_a: ty::PolyFnSig<'tcx>, b: Ty<'tcx>, ) -> CoerceResult<'tcx> { @@ -888,15 +868,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { //! let b = self.shallow_resolve(b); - debug!("coerce_from_fn_pointer(a={:?}, b={:?})", a, b); - - self.coerce_from_safe_fn( - a, - fn_ty_a, - b, - simple(Adjust::Pointer(PointerCoercion::UnsafeFnPointer)), - identity, - ) + debug!(?fn_ty_a, ?b, "coerce_from_fn_pointer"); + + self.coerce_from_safe_fn(fn_ty_a, b, None) } fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { @@ -943,30 +917,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.at(&self.cause, self.param_env).normalize(a_sig); obligations.extend(o1); - let a_fn_pointer = Ty::new_fn_ptr(self.tcx, a_sig); let InferOk { value, obligations: o2 } = self.coerce_from_safe_fn( - a_fn_pointer, a_sig, b, - |unsafe_ty| { - vec![ - Adjustment { - kind: Adjust::Pointer(PointerCoercion::ReifyFnPointer), - target: a_fn_pointer, - }, - Adjustment { - kind: Adjust::Pointer(PointerCoercion::UnsafeFnPointer), - target: unsafe_ty, - }, - ] - }, - simple(Adjust::Pointer(PointerCoercion::ReifyFnPointer)), + Some(Adjust::Pointer(PointerCoercion::ReifyFnPointer)), )?; obligations.extend(o2); Ok(InferOk { value, obligations }) } - _ => self.unify_and(a, b, identity), + _ => self.unify(a, b), } } @@ -1010,10 +970,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.unify_and( pointer_ty, b, - simple(Adjust::Pointer(PointerCoercion::ClosureFnPointer(safety))), + [], + Adjust::Pointer(PointerCoercion::ClosureFnPointer(safety)), ) } - _ => self.unify_and(a, b, identity), + _ => self.unify(a, b), } } @@ -1028,7 +989,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let (is_ref, mt_a) = match *a.kind() { ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }), ty::RawPtr(ty, mutbl) => (false, ty::TypeAndMut { ty, mutbl }), - _ => return self.unify_and(a, b, identity), + _ => return self.unify(a, b), }; coerce_mutbls(mt_a.mutbl, mutbl_b)?; @@ -1038,16 +999,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // representation, we still register an Adjust::DerefRef so that // regionck knows that the region for `a` must be valid here. if is_ref { - self.unify_and(a_raw, b, |target| { - vec![ - Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }, - Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), target }, - ] - }) + self.unify_and( + a_raw, + b, + [Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }], + Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), + ) } else if mt_a.mutbl != mutbl_b { - self.unify_and(a_raw, b, simple(Adjust::Pointer(PointerCoercion::MutToConstPointer))) + self.unify_and(a_raw, b, [], Adjust::Pointer(PointerCoercion::MutToConstPointer)) } else { - self.unify_and(a_raw, b, identity) + self.unify(a_raw, b) } } } @@ -1145,9 +1106,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable); // We don't ever need two-phase here since we throw out the result of the coercion. let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true); - coerce - .autoderef(DUMMY_SP, expr_ty) - .find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target)).ok().map(|_| steps)) + coerce.autoderef(DUMMY_SP, expr_ty).find_map(|(ty, steps)| { + self.probe(|_| coerce.unify_raw(ty, target)).ok().map(|_| steps) + }) } /// Given a type, this function will calculate and return the type given @@ -1229,9 +1190,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (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. - match self - .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) - { + match self.commit_if_ok(|_| { + // We need to eagerly handle nested obligations due to lazy norm. + if self.next_trait_solver() { + let ocx = ObligationCtxt::new(self); + let value = ocx.lub(cause, self.param_env, prev_ty, new_ty)?; + if ocx.select_where_possible().is_empty() { + Ok(InferOk { + value, + obligations: ocx.into_pending_obligations(), + }) + } else { + Err(TypeError::Mismatch) + } + } else { + self.at(cause, self.param_env).lub(prev_ty, new_ty) + } + }) { // We have a LUB of prev_ty and new_ty, just return it. Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)), Err(_) => { @@ -1266,10 +1241,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) { - // Intrinsics are not coercible to function pointers. - if a_sig.abi() == ExternAbi::RustIntrinsic || b_sig.abi() == ExternAbi::RustIntrinsic { - return Err(TypeError::IntrinsicCast); - } // The signature must match. let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig)); let sig = self diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 85131f6195f83..8182851a015bf 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -6,9 +6,8 @@ use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::bug; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, AssocItem, Ty, TypeFoldable, TypeVisitableExt}; +use rustc_middle::ty::{self, AssocItem, BottomUpFolder, Ty, TypeFoldable, TypeVisitableExt}; use rustc_span::{DUMMY_SP, Ident, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCause; @@ -85,7 +84,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.annotate_expected_due_to_let_ty(err, expr, error); self.annotate_loop_expected_due_to_inference(err, expr, error); - if self.annotate_mut_binding_to_immutable_binding(err, expr, error) { + if self.annotate_mut_binding_to_immutable_binding(err, expr, expr_ty, expected, error) { return; } @@ -722,8 +721,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, )) => { if let Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Static(ty, ..) | hir::ItemKind::Const(ty, ..), + kind: + hir::ItemKind::Static(ident, ty, ..) + | hir::ItemKind::Const(ident, ty, ..), .. })) = self.tcx.hir_get_if_local(*def_id) { @@ -799,17 +799,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Detect the following case /// /// ```text - /// fn change_object(mut a: &Ty) { + /// fn change_object(mut b: &Ty) { /// let a = Ty::new(); /// b = a; /// } /// ``` /// - /// where the user likely meant to modify the value behind there reference, use `a` as an out + /// where the user likely meant to modify the value behind there reference, use `b` as an out /// parameter, instead of mutating the local binding. When encountering this we suggest: /// /// ```text - /// fn change_object(a: &'_ mut Ty) { + /// fn change_object(b: &'_ mut Ty) { /// let a = Ty::new(); /// *b = a; /// } @@ -818,13 +818,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diag<'_>, expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + expected: Ty<'tcx>, error: Option>, ) -> bool { - if let Some(TypeError::Sorts(ExpectedFound { expected, found })) = error + if let Some(TypeError::Sorts(ExpectedFound { .. })) = error && let ty::Ref(_, inner, hir::Mutability::Not) = expected.kind() // The difference between the expected and found values is one level of borrowing. - && self.can_eq(self.param_env, *inner, found) + && self.can_eq(self.param_env, *inner, expr_ty) // We have an `ident = expr;` assignment. && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(lhs, rhs, _), .. }) = @@ -865,10 +867,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty` vec![( ty_ref.1.ty.span.shrink_to_lo(), - format!( - "{}mut ", - if ty_ref.0.ident.span.lo() == ty_ref.0.ident.span.hi() { "" } else { " " }, - ), + format!("{}mut ", if ty_ref.0.ident.span.is_empty() { "" } else { " " },), )] }; sugg.extend([ @@ -999,10 +998,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let container = with_no_trimmed_paths!(self.tcx.def_path_str(container_id)); for def_id in pick.import_ids { let hir_id = self.tcx.local_def_id_to_hir_id(def_id); - path_span.push_span_label( - self.tcx.hir().span(hir_id), - format!("`{container}` imported here"), - ); + path_span + .push_span_label(self.tcx.hir_span(hir_id), format!("`{container}` imported here")); } let tail = with_no_trimmed_paths!(match &other_methods_in_scope[..] { [] => return, @@ -1094,14 +1091,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// This function checks whether the method is not static and does not accept other parameters than `self`. fn has_only_self_parameter(&self, method: &AssocItem) -> bool { - match method.kind { - ty::AssocKind::Fn => { - method.fn_has_self_parameter - && self.tcx.fn_sig(method.def_id).skip_binder().inputs().skip_binder().len() - == 1 - } - _ => false, - } + method.is_method() + && self.tcx.fn_sig(method.def_id).skip_binder().inputs().skip_binder().len() == 1 } /// If the given `HirId` corresponds to a block with a trailing expression, return that expression @@ -1265,7 +1256,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { CallableKind::Function }; - maybe_emit_help(def_id, path.segments[0].ident, args, callable_kind); + maybe_emit_help(def_id, path.segments.last().unwrap().ident, args, callable_kind); } hir::ExprKind::MethodCall(method, _receiver, args, _span) => { let Some(def_id) = diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 20688a5b949d1..06103fe1c91bc 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagSymbolList, EmissionGuarantee, IntoDiagArg, MultiSpan, - SubdiagMessageOp, Subdiagnostic, + Subdiagnostic, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; @@ -270,11 +270,7 @@ pub(crate) struct SuggestAnnotations { pub suggestions: Vec, } impl Subdiagnostic for SuggestAnnotations { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { if self.suggestions.is_empty() { return; } @@ -337,11 +333,7 @@ pub(crate) struct TypeMismatchFruTypo { } impl Subdiagnostic for TypeMismatchFruTypo { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("expr", self.expr.as_deref().unwrap_or("NONE")); // Only explain that `a ..b` is a range if it's split up @@ -373,9 +365,14 @@ pub(crate) struct LossyProvenanceInt2Ptr<'tcx> { pub sugg: LossyProvenanceInt2PtrSuggestion, } -#[derive(LintDiagnostic)] -#[diag(hir_typeck_ptr_cast_add_auto_to_object)] +#[derive(Diagnostic)] +#[diag(hir_typeck_ptr_cast_add_auto_to_object, code = E0804)] +#[note] +#[help] pub(crate) struct PtrCastAddAutoToObject { + #[primary_span] + #[label] + pub span: Span, pub traits_len: usize, pub traits: DiagSymbolList, } @@ -594,11 +591,7 @@ pub(crate) struct RemoveSemiForCoerce { } impl Subdiagnostic for RemoveSemiForCoerce { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { let mut multispan: MultiSpan = self.semi.into(); multispan.push_span_label(self.expr, fluent::hir_typeck_remove_semi_for_coerce_expr); multispan.push_span_label(self.ret, fluent::hir_typeck_remove_semi_for_coerce_ret); @@ -687,6 +680,18 @@ pub(crate) enum SuggestBoxing { hir_typeck_suggest_boxing_when_appropriate, applicability = "machine-applicable" )] + ExprFieldShorthand { + #[suggestion_part(code = "{ident}: Box::new(")] + start: Span, + #[suggestion_part(code = ")")] + end: Span, + ident: Ident, + }, + #[note(hir_typeck_suggest_boxing_note)] + #[multipart_suggestion( + hir_typeck_suggest_boxing_when_appropriate, + applicability = "machine-applicable" + )] Other { #[suggestion_part(code = "Box::new(")] start: Span, @@ -722,7 +727,7 @@ pub(crate) struct NoAssociatedItem { #[primary_span] pub span: Span, pub item_kind: &'static str, - pub item_name: Ident, + pub item_ident: Ident, pub ty_prefix: Cow<'static, str>, pub ty_str: String, pub trait_missing_method: bool, @@ -773,20 +778,16 @@ pub(crate) enum CastUnknownPointerSub { } impl rustc_errors::Subdiagnostic for CastUnknownPointerSub { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { CastUnknownPointerSub::To(span) => { - let msg = f(diag, crate::fluent_generated::hir_typeck_label_to); + let msg = diag.eagerly_translate(fluent::hir_typeck_label_to); diag.span_label(span, msg); - let msg = f(diag, crate::fluent_generated::hir_typeck_note); + let msg = diag.eagerly_translate(fluent::hir_typeck_note); diag.note(msg); } CastUnknownPointerSub::From(span) => { - let msg = f(diag, crate::fluent_generated::hir_typeck_label_from); + let msg = diag.eagerly_translate(fluent::hir_typeck_label_from); diag.span_label(span, msg); } } @@ -974,3 +975,11 @@ pub(crate) enum SupertraitItemShadowee { traits: DiagSymbolList, }, } + +#[derive(Diagnostic)] +#[diag(hir_typeck_register_type_unstable)] +pub(crate) struct RegisterTypeUnstable<'a> { + #[primary_span] + pub span: Span, + pub ty: Ty<'a>, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index dec1779d92ca4..a1a33885b944c 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -16,7 +16,6 @@ use rustc_errors::{ }; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath}; use rustc_hir_analysis::NoVariantNamed; @@ -40,7 +39,6 @@ use tracing::{debug, instrument, trace}; use {rustc_ast as ast, rustc_hir as hir}; use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; -use crate::TupleArgumentsFlag::DontTupleArguments; use crate::coercion::{CoerceMany, DynamicCoerceMany}; use crate::errors::{ AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, @@ -51,8 +49,8 @@ use crate::errors::{ YieldExprOutsideOfCoroutine, }; use crate::{ - BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, Needs, cast, fatally_break_rust, - report_unexpected_variant_res, type_error_struct, + BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, GatherLocalsVisitor, Needs, + TupleArgumentsFlag, cast, fatally_break_rust, report_unexpected_variant_res, type_error_struct, }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -298,7 +296,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Combine the diverging and has_error flags. self.diverges.set(self.diverges.get() | old_diverges); - debug!("type of {} is...", self.tcx.hir().node_to_string(expr.hir_id)); + debug!("type of {} is...", self.tcx.hir_id_to_string(expr.hir_id)); debug!("... {:?}, expected is {:?}", ty, expected); ty @@ -362,6 +360,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Any expression child of these expressions constitute reads. ExprKind::Array(_) | ExprKind::Call(_, _) + | ExprKind::Use(_, _) | ExprKind::MethodCall(_, _, _, _) | ExprKind::Tup(_) | ExprKind::Binary(_, _, _) @@ -481,7 +480,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // All of these constitute a read, or match on something that isn't `!`, // which would require a `NeverToAny` coercion. - hir::PatKind::Binding(_, _, _, _) + hir::PatKind::Missing + | hir::PatKind::Binding(_, _, _, _) | hir::PatKind::Struct(_, _, _) | hir::PatKind::TupleStruct(_, _, _) | hir::PatKind::Tuple(_, _) @@ -511,7 +511,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_assign(expr, expected, lhs, rhs, span) } ExprKind::AssignOp(op, lhs, rhs) => { - self.check_expr_binop_assign(expr, op, lhs, rhs, expected) + self.check_expr_assign_op(expr, op, lhs, rhs, expected) } ExprKind::Unary(unop, oprnd) => self.check_expr_unop(unop, oprnd, expected, expr), ExprKind::AddrOf(kind, mutbl, oprnd) => { @@ -532,14 +532,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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. - Ty::new_misc_error(tcx) - } - } + ExprKind::Continue(destination) => self.check_expr_continue(destination, expr), ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr), ExprKind::Become(call) => self.check_expr_become(call, expr), ExprKind::Let(let_expr) => self.check_expr_let(let_expr, expr.hir_id), @@ -552,6 +545,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Closure(closure) => self.check_expr_closure(closure, expr.span, expected), ExprKind::Block(body, _) => self.check_expr_block(body, expected), ExprKind::Call(callee, args) => self.check_expr_call(expr, callee, args, expected), + ExprKind::Use(used_expr, _) => self.check_expr_use(used_expr, expected), ExprKind::MethodCall(segment, receiver, args, _) => { self.check_expr_method_call(expr, segment, receiver, args, expected) } @@ -988,6 +982,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn check_expr_continue( + &self, + destination: hir::Destination, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + if let Ok(target_id) = destination.target_id { + if let hir::Node::Expr(hir::Expr { kind: ExprKind::Loop(..), .. }) = + self.tcx.hir_node(target_id) + { + self.tcx.types.never + } else { + // Liveness linting assumes `continue`s all point to loops. We'll report an error + // in `check_mod_loops`, but make sure we don't run liveness (#113379, #121623). + let guar = self.dcx().span_delayed_bug( + expr.span, + "found `continue` not pointing to loop, but no error reported", + ); + Ty::new_error(self.tcx, guar) + } + } else { + // There was an error; make type-check fail. + Ty::new_misc_error(self.tcx) + } + } + fn check_expr_return( &self, expr_opt: Option<&'tcx hir::Expr<'tcx>>, @@ -1516,11 +1535,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let_expr: &'tcx hir::LetExpr<'tcx>, hir_id: HirId, ) -> Ty<'tcx> { + GatherLocalsVisitor::gather_from_let_expr(self, let_expr, hir_id); + // for let statements, this is done in check_stmt let init = let_expr.init; self.warn_if_unreachable(init.hir_id, init.span, "block in `let` expression"); + // otherwise check exactly as a let statement self.check_decl((let_expr, hir_id).into()); + // but return a bool, for this is a boolean expression if let ast::Recovered::Yes(error_guaranteed) = let_expr.recovered { self.set_tainted_by_errors(error_guaranteed); @@ -1588,32 +1611,54 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // no need to check for bot/err -- callee does that let rcvr_t = self.structurally_resolve_type(rcvr.span, rcvr_t); - let method = match self.lookup_method(rcvr_t, segment, segment.ident.span, expr, rcvr, args) - { + match self.lookup_method(rcvr_t, segment, segment.ident.span, expr, rcvr, args) { Ok(method) => { - // We could add a "consider `foo::`" suggestion here, but I wasn't able to - // trigger this codepath causing `structurally_resolve_type` to emit an error. self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method); - Ok(method) + + self.check_argument_types( + segment.ident.span, + expr, + &method.sig.inputs()[1..], + method.sig.output(), + expected, + args, + method.sig.c_variadic, + TupleArgumentsFlag::DontTupleArguments, + Some(method.def_id), + ); + + method.sig.output() } Err(error) => { - if segment.ident.name == kw::Empty { - span_bug!(rcvr.span, "empty method name") - } else { - Err(self.report_method_error(expr.hir_id, rcvr_t, error, expected, false)) - } + let guar = self.report_method_error(expr.hir_id, rcvr_t, error, expected, false); + + let err_inputs = self.err_args(args.len(), guar); + let err_output = Ty::new_error(self.tcx, guar); + + self.check_argument_types( + segment.ident.span, + expr, + &err_inputs, + err_output, + NoExpectation, + args, + false, + TupleArgumentsFlag::DontTupleArguments, + None, + ); + + err_output } - }; + } + } - // Call the generic checker. - self.check_method_argument_types( - segment.ident.span, - expr, - method, - args, - DontTupleArguments, - expected, - ) + /// Checks use `x.use`. + fn check_expr_use( + &self, + used_expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + self.check_expr_with_expectation(used_expr, expected) } fn check_expr_cast( @@ -1803,10 +1848,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Create a new function context. let def_id = block.def_id; let fcx = FnCtxt::new(self, self.param_env, def_id); - crate::GatherLocalsVisitor::new(&fcx).visit_body(body); let ty = fcx.check_expr_with_expectation(body.value, expected); - fcx.require_type_is_sized(ty, body.value.span, ObligationCauseCode::ConstSized); + fcx.require_type_is_sized(ty, body.value.span, ObligationCauseCode::SizedConstOrStatic); fcx.write_ty(block.hir_id, ty); ty } @@ -1966,7 +2010,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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() { + if variant.field_list_has_applicable_non_exhaustive() { self.dcx() .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() }); } @@ -2049,7 +2093,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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.hir_id), field.span, None); + tcx.check_stability(v_field.did, Some(field.hir_id), field.span, None); } self.field_ty(field.span, v_field, args) @@ -2193,8 +2237,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fields = listify(&missing_mandatory_fields, |f| format!("`{f}`")).unwrap(); self.dcx() .struct_span_err( - span.shrink_to_hi(), - format!("missing mandatory field{s} {fields}"), + span.shrink_to_lo(), + format!("missing field{s} {fields} in initializer"), + ) + .with_span_label( + span.shrink_to_lo(), + "fields that do not have a defaulted value must be provided explicitly", ) .emit(); return; @@ -2572,9 +2620,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .into_iter() .flat_map(|i| self.tcx.associated_items(i).in_definition_order()) // Only assoc fn with no receivers. - .filter(|item| { - matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter - }) + .filter(|item| item.is_fn() && !item.is_method()) .filter_map(|item| { // Only assoc fns that return `Self` let fn_sig = self.tcx.fn_sig(item.def_id).skip_binder(); @@ -2587,8 +2633,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } let input_len = fn_sig.inputs().skip_binder().len(); - let order = !item.name.as_str().starts_with("new"); - Some((order, item.name, input_len)) + let name = item.name(); + let order = !name.as_str().starts_with("new"); + Some((order, name, input_len)) }) .collect::>(); items.sort_by_key(|(order, _, _)| *order); @@ -2904,8 +2951,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // We failed to check the expression, report an error. - // Emits an error if we deref an infer variable, like calling `.field` on a base type of &_. - self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false)); + // Emits an error if we deref an infer variable, like calling `.field` on a base type + // of `&_`. We can also use this to suppress unnecessary "missing field" errors that + // will follow ambiguity errors. + let final_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false)); + if let ty::Error(_) = final_ty.kind() { + return final_ty; + } if let Some((adjustments, did)) = private_candidate { // (#90483) apply adjustments to avoid ExprUseVisitor from @@ -2921,9 +2973,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Ty::new_error(self.tcx(), guar); } - let guar = if field.name == kw::Empty { - self.dcx().span_delayed_bug(field.span, "field name with no name") - } else if self.method_exists_for_diagnostic( + let guar = if self.method_exists_for_diagnostic( field, base_ty, expr.hir_id, @@ -3069,7 +3119,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}", ident, base, expr, base_ty ); - let mut err = self.no_such_field_err(ident, base_ty, base.hir_id); + let mut err = self.no_such_field_err(ident, base_ty, expr); match *base_ty.peel_refs().kind() { ty::Array(_, len) => { @@ -3228,7 +3278,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(x) => self.tcx.local_def_id_to_hir_id(x), None => return, }; - let param_span = self.tcx.hir().span(param_hir_id); + let param_span = self.tcx.hir_span(param_hir_id); let param_name = self.tcx.hir_ty_param_name(param_def_id.expect_local()); err.span_label(param_span, format!("type parameter '{param_name}' declared here")); @@ -3275,25 +3325,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.multipart_suggestion( format!("{val} is a raw pointer; try dereferencing it"), vec![ - (base.span.shrink_to_lo(), "(*".to_string()), - (base.span.shrink_to_hi(), ")".to_string()), + (base.span.shrink_to_lo(), "(*".into()), + (base.span.between(field.span), format!(").")), ], Applicability::MaybeIncorrect, ); } - fn no_such_field_err(&self, field: Ident, expr_t: Ty<'tcx>, id: HirId) -> Diag<'_> { + fn no_such_field_err( + &self, + field: Ident, + base_ty: Ty<'tcx>, + expr: &hir::Expr<'tcx>, + ) -> Diag<'_> { let span = field.span; - debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t); + debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, base_ty); - let mut err = self.dcx().create_err(NoFieldOnType { span, ty: expr_t, field }); - if expr_t.references_error() { + let mut err = self.dcx().create_err(NoFieldOnType { span, ty: base_ty, field }); + if base_ty.references_error() { err.downgrade_to_delayed_bug(); } + if let Some(within_macro_span) = span.within_macro(expr.span, self.tcx.sess.source_map()) { + err.span_label(within_macro_span, "due to this macro variable"); + } + // try to add a suggestion in case the field is a nested field of a field of the Adt - let mod_id = self.tcx.parent_module(id).to_def_id(); - let (ty, unwrap) = if let ty::Adt(def, args) = expr_t.kind() + let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id(); + let (ty, unwrap) = if let ty::Adt(def, args) = base_ty.kind() && (self.tcx.is_diagnostic_item(sym::Result, def.did()) || self.tcx.is_diagnostic_item(sym::Option, def.did())) && let Some(arg) = args.get(0) @@ -3301,10 +3360,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { (ty, "unwrap().") } else { - (expr_t, "") + (base_ty, "") }; for (found_fields, args) in - self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, id) + self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, expr.hir_id) { let field_names = found_fields.iter().map(|field| field.name).collect::>(); let mut candidate_fields: Vec<_> = found_fields @@ -3317,7 +3376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args, vec![], mod_id, - id, + expr.hir_id, ) }) .map(|mut field_path| { @@ -3328,7 +3387,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { candidate_fields.sort(); let len = candidate_fields.len(); - if len > 0 { + // Don't suggest `.field` if the base expr is from a different + // syntax context than the field. + if len > 0 && expr.span.eq_ctxt(field.span) { err.span_suggestions( field.span.shrink_to_lo(), format!( @@ -3963,7 +4024,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => (), }; - self.no_such_field_err(field, container, expr.hir_id).emit(); + self.no_such_field_err(field, container, expr).emit(); break; } diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index c0617119d67c7..3493d359028d9 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -1,6 +1,9 @@ //! A different sort of visitor for walking fn bodies. Unlike the //! normal visitor, which just walks the entire body in one shot, the //! `ExprUseVisitor` determines how expressions are being used. +//! +//! In the compiler, this is only used for upvar inference, but there +//! are many uses within clippy. use std::cell::{Ref, RefCell}; use std::ops::Deref; @@ -20,12 +23,12 @@ use rustc_middle::hir::place::ProjectionKind; pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{ - self, AdtKind, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment, + self, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment, }; use rustc_middle::{bug, span_bug}; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::infer::InferCtxtExt; -use tracing::{debug, trace}; +use tracing::{debug, instrument, trace}; use crate::fn_ctxt::FnCtxt; @@ -35,7 +38,19 @@ pub trait Delegate<'tcx> { /// The value found at `place` is moved, depending /// on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`. /// - /// Use of a `Copy` type in a ByValue context is considered a use + /// If the value is `Copy`, [`copy`][Self::copy] is called instead, which + /// by default falls back to [`borrow`][Self::borrow]. + /// + /// The parameter `diag_expr_id` indicates the HIR id that ought to be used for + /// diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic + /// id will be the id of the expression `expr` but the place itself will have + /// the id of the binding in the pattern `pat`. + fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId); + + /// The value found at `place` is used, depending + /// on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`. + /// + /// Use of a `Copy` type in a ByUse context is considered a use /// by `ImmBorrow` and `borrow` is called instead. This is because /// a shared borrow is the "minimum access" that would be needed /// to perform a copy. @@ -45,7 +60,7 @@ pub trait Delegate<'tcx> { /// diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic /// id will be the id of the expression `expr` but the place itself will have /// the id of the binding in the pattern `pat`. - fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId); + fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId); /// The value found at `place` is being borrowed with kind `bk`. /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details). @@ -58,6 +73,10 @@ pub trait Delegate<'tcx> { /// The value found at `place` is being copied. /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details). + /// + /// If an implementation is not provided, use of a `Copy` type in a ByValue context is instead + /// considered a use by `ImmBorrow` and `borrow` is called instead. This is because a shared + /// borrow is the "minimum access" that would be needed to perform a copy. fn copy(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { // In most cases, copying data from `x` is equivalent to doing `*&x`, so by default // we treat a copy of `x` as a borrow of `x`. @@ -91,6 +110,10 @@ impl<'tcx, D: Delegate<'tcx>> Delegate<'tcx> for &mut D { (**self).consume(place_with_id, diag_expr_id) } + fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { + (**self).use_cloned(place_with_id, diag_expr_id) + } + fn borrow( &mut self, place_with_id: &PlaceWithHirId<'tcx>, @@ -122,6 +145,8 @@ impl<'tcx, D: Delegate<'tcx>> Delegate<'tcx> for &mut D { } } +/// This trait makes `ExprUseVisitor` usable with both [`FnCtxt`] +/// and [`LateContext`], depending on where in the compiler it is used. pub trait TypeInformationCtxt<'tcx> { type TypeckResults<'a>: Deref> where @@ -133,9 +158,9 @@ pub trait TypeInformationCtxt<'tcx> { fn resolve_vars_if_possible>>(&self, t: T) -> T; - fn try_structurally_resolve_type(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>; + fn structurally_resolve_type(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>; - fn report_error(&self, span: Span, msg: impl ToString) -> Self::Error; + fn report_bug(&self, span: Span, msg: impl ToString) -> Self::Error; fn error_reported_in_ty(&self, ty: Ty<'tcx>) -> Result<(), Self::Error>; @@ -143,6 +168,8 @@ pub trait TypeInformationCtxt<'tcx> { fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool; + fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool; + fn body_owner_def_id(&self) -> LocalDefId; fn tcx(&self) -> TyCtxt<'tcx>; @@ -164,11 +191,11 @@ impl<'tcx> TypeInformationCtxt<'tcx> for &FnCtxt<'_, 'tcx> { self.infcx.resolve_vars_if_possible(t) } - fn try_structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - (**self).try_structurally_resolve_type(sp, ty) + fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + (**self).structurally_resolve_type(sp, ty) } - fn report_error(&self, span: Span, msg: impl ToString) -> Self::Error { + fn report_bug(&self, span: Span, msg: impl ToString) -> Self::Error { self.dcx().span_delayed_bug(span, msg.to_string()) } @@ -184,6 +211,10 @@ impl<'tcx> TypeInformationCtxt<'tcx> for &FnCtxt<'_, 'tcx> { self.infcx.type_is_copy_modulo_regions(self.param_env, ty) } + fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool { + self.infcx.type_is_use_cloned_modulo_regions(self.param_env, ty) + } + fn body_owner_def_id(&self) -> LocalDefId { self.body_id } @@ -205,7 +236,7 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) { self.0.maybe_typeck_results().expect("expected typeck results") } - fn try_structurally_resolve_type(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + fn structurally_resolve_type(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { // FIXME: Maybe need to normalize here. ty } @@ -214,7 +245,7 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) { t } - fn report_error(&self, span: Span, msg: impl ToString) -> ! { + fn report_bug(&self, span: Span, msg: impl ToString) -> ! { span_bug!(span, "{}", msg.to_string()) } @@ -230,6 +261,10 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) { self.0.type_is_copy_modulo_regions(ty) } + fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool { + self.0.type_is_use_cloned_modulo_regions(ty) + } + fn body_owner_def_id(&self) -> LocalDefId { self.1 } @@ -239,9 +274,9 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) { } } -/// The ExprUseVisitor type +/// A visitor that reports how each expression is being used. /// -/// This is the code that actually walks the tree. +/// See [module-level docs][self] and [`Delegate`] for details. pub struct ExprUseVisitor<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> { cx: Cx, /// We use a `RefCell` here so that delegates can mutate themselves, but we can @@ -285,11 +320,27 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx Ok(()) } + #[instrument(skip(self), level = "debug")] fn consume_or_copy(&self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { - debug!("delegate_consume(place_with_id={:?})", place_with_id); + if self.cx.type_is_copy_modulo_regions(place_with_id.place.ty()) { + self.delegate.borrow_mut().copy(place_with_id, diag_expr_id); + } else { + self.delegate.borrow_mut().consume(place_with_id, diag_expr_id); + } + } + #[instrument(skip(self), level = "debug")] + pub fn consume_clone_or_copy(&self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { + // `x.use` will do one of the following + // * if it implements `Copy`, it will be a copy + // * if it implements `UseCloned`, it will be a call to `clone` + // * otherwise, it is a move + // + // we do a conservative approximation of this, treating it as a move unless we know that it implements copy or `UseCloned` if self.cx.type_is_copy_modulo_regions(place_with_id.place.ty()) { self.delegate.borrow_mut().copy(place_with_id, diag_expr_id); + } else if self.cx.type_is_use_cloned_modulo_regions(place_with_id.place.ty()) { + self.delegate.borrow_mut().use_cloned(place_with_id, diag_expr_id); } else { self.delegate.borrow_mut().consume(place_with_id, diag_expr_id); } @@ -304,15 +355,22 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } // FIXME: It's suspicious that this is public; clippy should probably use `walk_expr`. + #[instrument(skip(self), level = "debug")] pub fn consume_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> { - debug!("consume_expr(expr={:?})", expr); - let place_with_id = self.cat_expr(expr)?; self.consume_or_copy(&place_with_id, place_with_id.hir_id); self.walk_expr(expr)?; Ok(()) } + #[instrument(skip(self), level = "debug")] + pub fn consume_or_clone_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> { + let place_with_id = self.cat_expr(expr)?; + self.consume_clone_or_copy(&place_with_id, place_with_id.hir_id); + self.walk_expr(expr)?; + Ok(()) + } + fn mutate_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> { let place_with_id = self.cat_expr(expr)?; self.delegate.borrow_mut().mutate(&place_with_id, place_with_id.hir_id); @@ -320,17 +378,15 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx Ok(()) } + #[instrument(skip(self), level = "debug")] fn borrow_expr(&self, expr: &hir::Expr<'_>, bk: ty::BorrowKind) -> Result<(), Cx::Error> { - debug!("borrow_expr(expr={:?}, bk={:?})", expr, bk); - let place_with_id = self.cat_expr(expr)?; self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk); self.walk_expr(expr) } + #[instrument(skip(self), level = "debug")] pub fn walk_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> { - debug!("walk_expr(expr={:?})", expr); - self.walk_adjustment(expr)?; match expr.kind { @@ -366,6 +422,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.consume_exprs(args)?; } + hir::ExprKind::Use(expr, _) => { + self.consume_or_clone_expr(expr)?; + } + hir::ExprKind::MethodCall(.., receiver, args, _) => { // callee.m(args) self.consume_expr(receiver)?; @@ -551,6 +611,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx for pat in pats { self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| { match &pat.kind { + PatKind::Missing => unreachable!(), PatKind::Binding(.., opt_sub_pat) => { // If the opt_sub_pat is None, then the binding does not count as // a wildcard for the purpose of borrowing discr. @@ -673,9 +734,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx /// Indicates that the value of `blk` will be consumed, meaning either copied or moved /// depending on its type. + #[instrument(skip(self), level = "debug")] fn walk_block(&self, blk: &hir::Block<'_>) -> Result<(), Cx::Error> { - debug!("walk_block(blk.hir_id={})", blk.hir_id); - for stmt in blk.stmts { self.walk_stmt(stmt)?; } @@ -716,7 +776,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // Select just those fields of the `with` // expression that will actually be used - match self.cx.try_structurally_resolve_type(with_expr.span, with_place.place.ty()).kind() { + match self.cx.structurally_resolve_type(with_expr.span, with_place.place.ty()).kind() { ty::Adt(adt, args) 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_enumerated() { @@ -801,7 +861,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } /// Walks the autoref `autoref` applied to the autoderef'd - /// `expr`. `base_place` is the mem-categorized form of `expr` + /// `expr`. `base_place` is `expr` represented as a place, /// after all relevant autoderefs have occurred. fn walk_autoref( &self, @@ -882,14 +942,13 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } /// The core driver for walking a pattern + #[instrument(skip(self), level = "debug")] fn walk_pat( &self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>, has_guard: bool, ) -> Result<(), Cx::Error> { - debug!("walk_pat(discr_place={:?}, pat={:?}, has_guard={:?})", discr_place, pat, has_guard); - let tcx = self.cx.tcx(); self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| { match pat.kind { @@ -941,11 +1000,15 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // determines whether to borrow *at the level of the deref pattern* rather than // borrowing the bound place (since that inner place is inside the temporary that // stores the result of calling `deref()`/`deref_mut()` so can't be captured). - let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpattern); - let mutability = - if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; - let bk = ty::BorrowKind::from_mutbl(mutability); - self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk); + // Deref patterns on boxes don't borrow, so we ignore them here. + // HACK: this could be a fake pattern corresponding to a deref inserted by match + // ergonomics, in which case `pat.hir_id` will be the id of the subpattern. + if let hir::ByRef::Yes(mutability) = + self.cx.typeck_results().deref_pat_borrow_mode(place.place.ty(), subpattern) + { + let bk = ty::BorrowKind::from_mutbl(mutability); + self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk); + } } PatKind::Never => { // A `!` pattern always counts as an immutable read of the discriminant, @@ -982,6 +1045,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx /// /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing /// closure as the DefId. + #[instrument(skip(self), level = "debug")] fn walk_captures(&self, closure_expr: &hir::Closure<'_>) -> Result<(), Cx::Error> { fn upvar_is_local_variable( upvars: Option<&FxIndexMap>, @@ -991,8 +1055,6 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx upvars.map(|upvars| !upvars.contains_key(&upvar_id)).unwrap_or(body_owner_is_closure) } - debug!("walk_captures({:?})", closure_expr); - let tcx = self.cx.tcx(); let closure_def_id = closure_expr.def_id; // For purposes of this function, coroutine and closures are equivalent. @@ -1085,6 +1147,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx ty::UpvarCapture::ByValue => { self.consume_or_copy(&place_with_id, place_with_id.hir_id); } + ty::UpvarCapture::ByUse => { + self.consume_clone_or_copy(&place_with_id, place_with_id.hir_id); + } ty::UpvarCapture::ByRef(upvar_borrow) => { self.delegate.borrow_mut().borrow( &place_with_id, @@ -1101,55 +1166,17 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } } -/// The job of the categorization methods is to analyze an expression to -/// determine what kind of memory is used in evaluating it (for example, -/// where dereferences occur and what kind of pointer is dereferenced; -/// whether the memory is mutable, etc.). -/// -/// Categorization effectively transforms all of our expressions into -/// expressions of the following forms (the actual enum has many more -/// possibilities, naturally, but they are all variants of these base -/// forms): -/// ```ignore (not-rust) -/// E = rvalue // some computed rvalue -/// | x // address of a local variable or argument -/// | *E // deref of a ptr -/// | E.comp // access to an interior component -/// ``` -/// Imagine a routine ToAddr(Expr) that evaluates an expression and returns an -/// address where the result is to be found. If Expr is a place, then this -/// is the address of the place. If `Expr` is an rvalue, this is the address of -/// some temporary spot in memory where the result is stored. -/// -/// Now, `cat_expr()` classifies the expression `Expr` and the address `A = ToAddr(Expr)` -/// as follows: -/// -/// - `cat`: what kind of expression was this? This is a subset of the -/// full expression forms which only includes those that we care about -/// for the purpose of the analysis. -/// - `mutbl`: mutability of the address `A`. -/// - `ty`: the type of data found at the address `A`. -/// -/// The resulting categorization tree differs somewhat from the expressions -/// themselves. For example, auto-derefs are explicit. Also, an index `a[b]` is -/// decomposed into two operations: a dereference to reach the array data and -/// then an index to jump forward to the relevant item. -/// -/// ## By-reference upvars -/// -/// One part of the codegen which may be non-obvious is that we translate -/// closure upvars into the dereference of a borrowed pointer; this more closely -/// resembles the runtime codegen. So, for example, if we had: -/// -/// let mut x = 3; -/// let y = 5; -/// let inc = || x += y; +/// The job of the methods whose name starts with `cat_` is to analyze +/// expressions and construct the corresponding [`Place`]s. The `cat` +/// stands for "categorize", this is a leftover from long ago when +/// places were called "categorizations". /// -/// Then when we categorize `x` (*within* the closure) we would yield a -/// result of `*x'`, effectively, where `x'` is a `Categorization::Upvar` reference -/// tied to `x`. The type of `x'` will be a borrowed pointer. +/// Note that a [`Place`] differs somewhat from the expression itself. For +/// example, auto-derefs are explicit. Also, an index `a[b]` is decomposed into +/// two operations: a dereference to reach the array data and then an index to +/// jump forward to the relevant item. impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx, Cx, D> { - fn resolve_type_vars_or_error( + fn expect_and_resolve_type( &self, id: HirId, ty: Option>, @@ -1158,36 +1185,26 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx Some(ty) => { let ty = self.cx.resolve_vars_if_possible(ty); self.cx.error_reported_in_ty(ty)?; - if ty.is_ty_var() { - debug!("resolve_type_vars_or_error: infer var from {:?}", ty); - Err(self - .cx - .report_error(self.cx.tcx().hir().span(id), "encountered type variable")) - } else { - Ok(ty) - } + Ok(ty) } None => { // FIXME: We shouldn't be relying on the infcx being tainted. self.cx.tainted_by_errors()?; - bug!( - "no type for node {} in mem_categorization", - self.cx.tcx().hir().node_to_string(id) - ); + bug!("no type for node {} in ExprUseVisitor", self.cx.tcx().hir_id_to_string(id)); } } } fn node_ty(&self, hir_id: HirId) -> Result, Cx::Error> { - self.resolve_type_vars_or_error(hir_id, self.cx.typeck_results().node_type_opt(hir_id)) + self.expect_and_resolve_type(hir_id, self.cx.typeck_results().node_type_opt(hir_id)) } fn expr_ty(&self, expr: &hir::Expr<'_>) -> Result, Cx::Error> { - self.resolve_type_vars_or_error(expr.hir_id, self.cx.typeck_results().expr_ty_opt(expr)) + self.expect_and_resolve_type(expr.hir_id, self.cx.typeck_results().expr_ty_opt(expr)) } fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> Result, Cx::Error> { - self.resolve_type_vars_or_error( + self.expect_and_resolve_type( expr.hir_id, self.cx.typeck_results().expr_ty_adjusted_opt(expr), ) @@ -1209,9 +1226,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // actually this is somewhat "disjoint" from the code below // that aims to account for `ref x`. if let Some(vec) = self.cx.typeck_results().pat_adjustments().get(pat.hir_id) { - if let Some(first_ty) = vec.first() { - debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty); - return Ok(*first_ty); + if let Some(first_adjust) = vec.first() { + debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust); + return Ok(first_adjust.source); } } else if let PatKind::Ref(subpat, _) = pat.kind && self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id) @@ -1222,7 +1239,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.pat_ty_unadjusted(pat) } - /// Like `TypeckResults::pat_ty`, but ignores implicit `&` patterns. + /// Like [`Self::pat_ty_adjusted`], but ignores implicit `&` patterns. fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> Result, Cx::Error> { let base_ty = self.node_ty(pat.hir_id)?; trace!(?base_ty); @@ -1242,17 +1259,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // a bind-by-ref means that the base_ty will be the type of the ident itself, // but what we want here is the type of the underlying value being borrowed. // So peel off one-level, turning the &T into T. - match self - .cx - .try_structurally_resolve_type(pat.span, base_ty) - .builtin_deref(false) + match self.cx.structurally_resolve_type(pat.span, base_ty).builtin_deref(false) { Some(ty) => Ok(ty), None => { debug!("By-ref binding of non-derefable type"); Err(self .cx - .report_error(pat.span, "by-ref binding of non-derefable type")) + .report_bug(pat.span, "by-ref binding of non-derefable type")) } } } else { @@ -1386,6 +1400,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx hir::ExprKind::AddrOf(..) | hir::ExprKind::Call(..) + | hir::ExprKind::Use(..) | hir::ExprKind::Assign(..) | hir::ExprKind::AssignOp(..) | hir::ExprKind::Closure { .. } @@ -1447,7 +1462,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } } - def => span_bug!(span, "unexpected definition in memory categorization: {:?}", def), + def => span_bug!(span, "unexpected definition in ExprUseVisitor: {:?}", def), } } @@ -1483,19 +1498,18 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx let mut projections = base_place.place.projections; let node_ty = self.cx.typeck_results().node_type(node); - // Opaque types can't have field projections, but we can instead convert - // the current place in-place (heh) to the hidden type, and then apply all - // follow up projections on that. - if node_ty != place_ty - && self - .cx - .try_structurally_resolve_type( - self.cx.tcx().hir().span(base_place.hir_id), - place_ty, - ) - .is_impl_trait() - { - projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty }); + if !self.cx.tcx().next_trait_solver_globally() { + // Opaque types can't have field projections, but we can instead convert + // the current place in-place (heh) to the hidden type, and then apply all + // follow up projections on that. + if node_ty != place_ty + && self + .cx + .structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), place_ty) + .is_impl_trait() + { + projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty }); + } } projections.push(Projection { kind, ty }); PlaceWithHirId::new(node, base_place.place.base_ty, base_place.place.base, projections) @@ -1513,7 +1527,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx let base_ty = self.expr_ty_adjusted(base)?; let ty::Ref(region, _, mutbl) = - *self.cx.try_structurally_resolve_type(base.span, base_ty).kind() + *self.cx.structurally_resolve_type(base.span, base_ty).kind() else { span_bug!(expr.span, "cat_overloaded_place: base is not a reference"); }; @@ -1531,17 +1545,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx let base_curr_ty = base_place.place.ty(); let deref_ty = match self .cx - .try_structurally_resolve_type( - self.cx.tcx().hir().span(base_place.hir_id), - base_curr_ty, - ) + .structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), base_curr_ty) .builtin_deref(true) { Some(ty) => ty, None => { debug!("explicit deref of non-derefable type: {:?}", base_curr_ty); - return Err(self.cx.report_error( - self.cx.tcx().hir().span(node), + return Err(self.cx.report_bug( + self.cx.tcx().hir_span(node), "explicit deref of non-derefable type", )); } @@ -1562,10 +1573,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx ) -> Result { let res = self.cx.typeck_results().qpath_res(qpath, pat_hir_id); let ty = self.cx.typeck_results().node_type(pat_hir_id); - let ty::Adt(adt_def, _) = self.cx.try_structurally_resolve_type(span, ty).kind() else { + let ty::Adt(adt_def, _) = self.cx.structurally_resolve_type(span, ty).kind() else { return Err(self .cx - .report_error(span, "struct or tuple struct pattern not applied to an ADT")); + .report_bug(span, "struct or tuple struct pattern not applied to an ADT")); }; match res { @@ -1594,7 +1605,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx span: Span, ) -> Result { let ty = self.cx.typeck_results().node_type(pat_hir_id); - match self.cx.try_structurally_resolve_type(span, ty).kind() { + match self.cx.structurally_resolve_type(span, ty).kind() { ty::Adt(adt_def, _) => Ok(adt_def.variant(variant_index).fields.len()), _ => { self.cx @@ -1609,9 +1620,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx /// Here `pat_hir_id` is the HirId of the pattern itself. fn total_fields_in_tuple(&self, pat_hir_id: HirId, span: Span) -> Result { let ty = self.cx.typeck_results().node_type(pat_hir_id); - match self.cx.try_structurally_resolve_type(span, ty).kind() { + match self.cx.structurally_resolve_type(span, ty).kind() { ty::Tuple(args) => Ok(args.len()), - _ => Err(self.cx.report_error(span, "tuple pattern not applied to a tuple")), + _ => Err(self.cx.report_bug(span, "tuple pattern not applied to a tuple")), } } @@ -1662,12 +1673,32 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // Then we see that to get the same result, we must start with // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. - for _ in - 0..self.cx.typeck_results().pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len()) - { + let typeck_results = self.cx.typeck_results(); + let adjustments: &[adjustment::PatAdjustment<'tcx>] = + typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); + let mut adjusts = adjustments.iter().peekable(); + while let Some(adjust) = adjusts.next() { debug!("applying adjustment to place_with_id={:?}", place_with_id); - place_with_id = self.cat_deref(pat.hir_id, place_with_id)?; + place_with_id = match adjust.kind { + adjustment::PatAdjust::BuiltinDeref => self.cat_deref(pat.hir_id, place_with_id)?, + adjustment::PatAdjust::OverloadedDeref => { + // This adjustment corresponds to an overloaded deref; unless it's on a box, it + // borrows the scrutinee to call `Deref::deref` or `DerefMut::deref_mut`. Invoke + // the callback before setting `place_with_id` to the temporary storing the + // result of the deref. + // HACK(dianne): giving the callback a fake deref pattern makes sure it behaves the + // same as it would if this were an explicit deref pattern (including for boxes). + op(&place_with_id, &hir::Pat { kind: PatKind::Deref(pat), ..*pat })?; + let target_ty = match adjusts.peek() { + Some(&&next_adjust) => next_adjust.source, + // At the end of the deref chain, we get `pat`'s scrutinee. + None => self.pat_ty_unadjusted(pat)?, + }; + self.pat_deref_place(pat.hir_id, place_with_id, pat, target_ty)? + } + }; } + drop(typeck_results); // explicitly release borrow of typeck results, just in case. let place_with_id = place_with_id; // lose mutability debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id); @@ -1770,26 +1801,21 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.cat_pattern(subplace, subpat, op)?; } PatKind::Deref(subpat) => { - let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpat); - let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; - let re_erased = self.cx.tcx().lifetimes.re_erased; let ty = self.pat_ty_adjusted(subpat)?; - let ty = Ty::new_ref(self.cx.tcx(), re_erased, ty, mutability); - // A deref pattern generates a temporary. - let place = self.cat_rvalue(pat.hir_id, ty); + let place = self.pat_deref_place(pat.hir_id, place_with_id, subpat, ty)?; self.cat_pattern(place, subpat, op)?; } PatKind::Slice(before, ref slice, after) => { let Some(element_ty) = self .cx - .try_structurally_resolve_type(pat.span, place_with_id.place.ty()) + .structurally_resolve_type(pat.span, place_with_id.place.ty()) .builtin_index() else { debug!("explicit index of non-indexable type {:?}", place_with_id); return Err(self .cx - .report_error(pat.span, "explicit index of non-indexable type")); + .report_bug(pat.span, "explicit index of non-indexable type")); }; let elt_place = self.cat_projection( pat.hir_id, @@ -1819,6 +1845,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx | PatKind::Expr(..) | PatKind::Range(..) | PatKind::Never + | PatKind::Missing | PatKind::Wild | PatKind::Err(_) => { // always ok @@ -1828,20 +1855,36 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx Ok(()) } + /// Represents the place matched on by a deref pattern's interior. + fn pat_deref_place( + &self, + hir_id: HirId, + base_place: PlaceWithHirId<'tcx>, + inner: &hir::Pat<'_>, + target_ty: Ty<'tcx>, + ) -> Result, Cx::Error> { + match self.cx.typeck_results().deref_pat_borrow_mode(base_place.place.ty(), inner) { + // Deref patterns on boxes are lowered using a built-in deref. + hir::ByRef::No => self.cat_deref(hir_id, base_place), + // For other types, we create a temporary to match on. + hir::ByRef::Yes(mutability) => { + let re_erased = self.cx.tcx().lifetimes.re_erased; + let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability); + // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ... + let base = self.cat_rvalue(hir_id, ty); + // ... and the inner pattern matches on the place behind that reference. + self.cat_deref(hir_id, base) + } + } + } + fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool { - if let ty::Adt(def, _) = self.cx.try_structurally_resolve_type(span, ty).kind() { + if let ty::Adt(def, _) = self.cx.structurally_resolve_type(span, ty).kind() { // Note that if a non-exhaustive SingleVariant is defined in another crate, we need // to assume that more cases will be added to the variant in the future. This mean // that we should handle non-exhaustive SingleVariant the same way we would handle // a MultiVariant. - // If the variant is not local it must be defined in another crate. - let is_non_exhaustive = match def.adt_kind() { - AdtKind::Struct | AdtKind::Union => { - def.non_enum_variant().is_field_list_non_exhaustive() - } - AdtKind::Enum => def.is_variant_list_non_exhaustive(), - }; - def.variants().len() > 1 || (!def.did().is_local() && is_non_exhaustive) + def.variants().len() > 1 || def.variant_list_has_applicable_non_exhaustive() } else { false } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 054e3bcb67c3d..362c7d8efac08 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -21,17 +21,15 @@ use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryRespons use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, GenericArgKind, GenericArgsRef, GenericParamDefKind, - IsIdentity, Ty, TyCtxt, UserArgs, UserSelfTy, + self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, Ty, TyCtxt, + TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, UserSelfTy, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; -use rustc_span::{Span, kw}; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::traits::{ self, NormalizeExt, ObligationCauseCode, StructurallyNormalizeExt, @@ -140,7 +138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn local_ty(&self, span: Span, nid: HirId) -> Ty<'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)) + span_bug!(span, "no type for local variable {}", self.tcx.hir_id_to_string(nid)) }) } @@ -159,7 +157,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Lots of that diagnostics code relies on subtle effects of re-lowering, so we'll // let it keep doing that and just ensure that compilation won't succeed. self.dcx().span_delayed_bug( - self.tcx.hir().span(id), + self.tcx.hir_span(id), format!("`{prev}` overridden by `{ty}` for {id:?} in {:?}", self.body_id), ); } @@ -220,6 +218,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { debug!("fcx {}", self.tag()); + // Don't write user type annotations for const param types, since we give them + // identity args just so that we can trivially substitute their `EarlyBinder`. + // We enforce that they match their type in MIR later on. + if matches!(self.tcx.def_kind(def_id), DefKind::ConstParam) { + return; + } + if Self::can_contain_user_lifetime_bounds((args, user_self_ty)) { let canonicalized = self.canonicalize_user_type_annotation(ty::UserType::new( ty::UserTypeKind::TypeOf(def_id, UserArgs { args, user_self_ty }), @@ -527,7 +532,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ct = self.lowerer().lower_const_arg(const_arg, feed); self.register_wf_obligation( ct.into(), - self.tcx.hir().span(const_arg.hir_id), + self.tcx.hir_span(const_arg.hir_id), ObligationCauseCode::WellFormed(None), ); ct @@ -552,11 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(&t) => t, None if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx, e), None => { - bug!( - "no type for node {} in fcx {}", - self.tcx.hir().node_to_string(id), - self.tag() - ); + bug!("no type for node {} in fcx {}", self.tcx.hir_id_to_string(id), self.tag()); } } } @@ -572,7 +573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. pub(crate) fn register_wf_obligation( &self, - arg: ty::GenericArg<'tcx>, + term: ty::Term<'tcx>, span: Span, code: traits::ObligationCauseCode<'tcx>, ) { @@ -582,16 +583,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx, cause, self.param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg))), + ty::ClauseKind::WellFormed(term), )); } /// Registers obligations that all `args` are well-formed. pub(crate) fn add_wf_bounds(&self, args: GenericArgsRef<'tcx>, span: Span) { - for arg in args.iter().filter(|arg| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) { - self.register_wf_obligation(arg, span, ObligationCauseCode::WellFormed(None)); + for term in args.iter().filter_map(ty::GenericArg::as_term) { + self.register_wf_obligation(term, span, ObligationCauseCode::WellFormed(None)); } } @@ -632,35 +631,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let coroutines = std::mem::take(&mut *self.deferred_coroutine_interiors.borrow_mut()); debug!(?coroutines); - for &(expr_def_id, body_id, interior) in coroutines.iter() { - debug!(?expr_def_id); + let mut obligations = vec![]; + + if !self.next_trait_solver() { + for &(coroutine_def_id, interior) in coroutines.iter() { + debug!(?coroutine_def_id); + + // Create the `CoroutineWitness` type that we will unify with `interior`. + let args = ty::GenericArgs::identity_for_item( + self.tcx, + self.tcx.typeck_root_def_id(coroutine_def_id.to_def_id()), + ); + let witness = + Ty::new_coroutine_witness(self.tcx, coroutine_def_id.to_def_id(), args); + + // Unify `interior` with `witness` and collect all the resulting obligations. + let span = self.tcx.hir_body_owned_by(coroutine_def_id).value.span; + let ty::Infer(ty::InferTy::TyVar(_)) = interior.kind() else { + span_bug!(span, "coroutine interior witness not infer: {:?}", interior.kind()) + }; + let ok = self + .at(&self.misc(span), self.param_env) + // Will never define opaque types, as all we do is instantiate a type variable. + .eq(DefineOpaqueTypes::Yes, interior, witness) + .expect("Failed to unify coroutine interior type"); + + obligations.extend(ok.obligations); + } + } - // Create the `CoroutineWitness` type that we will unify with `interior`. - let args = ty::GenericArgs::identity_for_item( - self.tcx, - self.tcx.typeck_root_def_id(expr_def_id.to_def_id()), + if !coroutines.is_empty() { + obligations.extend( + self.fulfillment_cx + .borrow_mut() + .drain_stalled_obligations_for_coroutines(&self.infcx), ); - let witness = Ty::new_coroutine_witness(self.tcx, expr_def_id.to_def_id(), args); - - // Unify `interior` with `witness` and collect all the resulting obligations. - let span = self.tcx.hir_body(body_id).value.span; - let ty::Infer(ty::InferTy::TyVar(_)) = interior.kind() else { - span_bug!(span, "coroutine interior witness not infer: {:?}", interior.kind()) - }; - let ok = self - .at(&self.misc(span), self.param_env) - // Will never define opaque types, as all we do is instantiate a type variable. - .eq(DefineOpaqueTypes::Yes, interior, witness) - .expect("Failed to unify coroutine interior type"); - let mut obligations = ok.obligations; - - // Also collect the obligations that were unstalled by this unification. - obligations - .extend(self.fulfillment_cx.borrow_mut().drain_unstalled_obligations(&self.infcx)); - - let obligations = obligations.into_iter().map(|o| (o.predicate, o.cause)); - self.typeck_results.borrow_mut().coroutine_stalled_predicates.extend(obligations); } + + self.typeck_results + .borrow_mut() + .coroutine_stalled_predicates + .extend(obligations.into_iter().map(|o| (o.predicate, o.cause))); } #[instrument(skip(self), level = "debug")] @@ -669,12 +680,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !errors.is_empty() { self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); - let errors_causecode = errors - .iter() - .map(|e| (e.obligation.cause.span, e.root_obligation.cause.code().clone())) - .collect::>(); self.err_ctxt().report_fulfillment_errors(errors); - self.collect_unused_stmts_for_coerce_return_ty(errors_causecode); } } @@ -830,15 +836,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trait_missing_method = matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait(); - if item_name.name != kw::Empty { - self.report_method_error( - hir_id, - ty.normalized, - error, - Expectation::NoExpectation, - trait_missing_method && span.edition().at_least_rust_2021(), // emits missing method for trait only after edition 2021 - ); - } + self.report_method_error( + hir_id, + ty.normalized, + error, + Expectation::NoExpectation, + trait_missing_method && span.edition().at_least_rust_2021(), // emits missing method for trait only after edition 2021 + ); result }); @@ -1314,27 +1318,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { infer_args: bool, ) -> ty::GenericArg<'tcx> { let tcx = self.fcx.tcx(); - match param.kind { - GenericParamDefKind::Lifetime => self - .fcx - .re_infer( - self.span, - rustc_hir_analysis::hir_ty_lowering::RegionInferReason::Param(param), - ) - .into(), - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { - if !infer_args && let Some(default) = param.default_value(tcx) { - // If we have a default, then it doesn't matter that we're not inferring - // the type/const arguments: We provide the default where any is missing. - return default.instantiate(tcx, preceding_args); - } - // If no type/const 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/const parameter. - // Using inference instead of `Error` gives better error messages. - self.fcx.var_for_def(self.span, param) - } + if !infer_args && let Some(default) = param.default_value(tcx) { + // If we have a default, then it doesn't matter that we're not inferring + // the type/const arguments: We provide the default where any is missing. + return default.instantiate(tcx, preceding_args); } + // If no type/const 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/const parameter. + // Using inference instead of `Error` gives better error messages. + self.fcx.var_for_def(self.span, param) } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index e60a4bb47b575..d2cdfe22a3ad0 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -3,25 +3,21 @@ use std::{fmt, iter, mem}; use itertools::Itertools; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::codes::*; -use rustc_errors::{ - Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, a_or_an, listify, pluralize, -}; +use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, listify, pluralize}; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, HirId, Node, QPath}; -use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_index::IndexVec; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TypeTrace}; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt}; +use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::Session; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, kw, sym}; use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext}; @@ -36,13 +32,13 @@ use crate::errors::SuggestPtrNullMut; use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx}; use crate::fn_ctxt::infer::FnCall; use crate::gather_locals::Declaration; -use crate::method::MethodCallee; +use crate::inline_asm::InlineAsmCtxt; use crate::method::probe::IsSuggestion; use crate::method::probe::Mode::MethodCall; use crate::method::probe::ProbeScope::TraitsInScope; use crate::{ - BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy, Needs, TupleArgumentsFlag, errors, - struct_span_code_err, + BreakableCtxt, Diverges, Expectation, FnCtxt, GatherLocalsVisitor, LoweredTy, Needs, + TupleArgumentsFlag, errors, struct_span_code_err, }; rustc_index::newtype_index! { @@ -101,24 +97,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len()); for (asm, hir_id) in deferred_asm_checks.drain(..) { let enclosing_id = self.tcx.hir_enclosing_body_owner(hir_id); - let expr_ty = |expr: &hir::Expr<'tcx>| { - let ty = self.typeck_results.borrow().expr_ty_adjusted(expr); - let ty = self.resolve_vars_if_possible(ty); - if ty.has_non_region_infer() { - Ty::new_misc_error(self.tcx) - } else { - self.tcx.erase_regions(ty) - } - }; - let node_ty = |hir_id: HirId| self.typeck_results.borrow().node_type(hir_id); - InlineAsmCtxt::new( - self.tcx, - enclosing_id, - self.typing_env(self.param_env), - expr_ty, - node_ty, - ) - .check_asm(asm); + InlineAsmCtxt::new(self, enclosing_id).check_asm(asm); } } @@ -147,61 +126,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(in super::super) fn check_method_argument_types( - &self, - sp: Span, - expr: &'tcx hir::Expr<'tcx>, - method: Result, ErrorGuaranteed>, - args_no_rcvr: &'tcx [hir::Expr<'tcx>], - tuple_arguments: TupleArgumentsFlag, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let has_error = match method { - Ok(method) => method.args.error_reported().and(method.sig.error_reported()), - Err(guar) => Err(guar), - }; - if let Err(guar) = has_error { - let err_inputs = self.err_args( - method.map_or(args_no_rcvr.len(), |method| method.sig.inputs().len() - 1), - guar, - ); - let err_output = Ty::new_error(self.tcx, guar); - - let err_inputs = match tuple_arguments { - DontTupleArguments => err_inputs, - TupleArguments => vec![Ty::new_tup(self.tcx, &err_inputs)], - }; - - self.check_argument_types( - sp, - expr, - &err_inputs, - err_output, - NoExpectation, - args_no_rcvr, - false, - tuple_arguments, - method.ok().map(|method| method.def_id), - ); - return err_output; - } - - let method = method.unwrap(); - self.check_argument_types( - sp, - expr, - &method.sig.inputs()[1..], - method.sig.output(), - expected, - args_no_rcvr, - method.sig.c_variadic, - tuple_arguments, - Some(method.def_id), - ); - - method.sig.output() - } - /// Generic function that factors out common logic from function calls, /// method calls and overloaded operators. pub(in super::super) fn check_argument_types( @@ -630,7 +554,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some((DefKind::AssocFn, def_id)) = self.typeck_results.borrow().type_dependent_def(call_expr.hir_id) && let Some(assoc) = tcx.opt_associated_item(def_id) - && assoc.fn_has_self_parameter + && assoc.is_method() { Some(*receiver) } else { @@ -656,8 +580,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { TraitsInScope, |mut ctxt| ctxt.probe_for_similar_candidate(), ) - && let ty::AssocKind::Fn = assoc.kind - && assoc.fn_has_self_parameter + && assoc.is_method() { let args = self.infcx.fresh_args_for_item(call_name.span, assoc.def_id); let fn_sig = tcx.fn_sig(assoc.def_id).instantiate(tcx, args); @@ -698,10 +621,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .all(|(expected, found)| self.may_coerce(*expected, *found)) && fn_sig.inputs()[1..].len() == input_types.len() { + let assoc_name = assoc.name(); err.span_suggestion_verbose( call_name.span, - format!("you might have meant to use `{}`", assoc.name), - assoc.name, + format!("you might have meant to use `{}`", assoc_name), + assoc_name, Applicability::MaybeIncorrect, ); return; @@ -721,7 +645,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.def_span(assoc.def_id), format!( "there's is a method with similar name `{}`, but the arguments don't match", - assoc.name, + assoc.name(), ), ); return; @@ -733,7 +657,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!( "there's is a method with similar name `{}`, but their argument count \ doesn't match", - assoc.name, + assoc.name(), ), ); return; @@ -939,7 +863,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let detect_dotdot = |err: &mut Diag<'_>, ty: Ty<'_>, expr: &hir::Expr<'_>| { if let ty::Adt(adt, _) = ty.kind() - && self.tcx().lang_items().get(hir::LangItem::RangeFull) == Some(adt.did()) + && self.tcx().is_lang_item(adt.did(), hir::LangItem::RangeFull) && let hir::ExprKind::Struct( hir::QPath::LangItem(hir::LangItem::RangeFull, _), [], @@ -1149,8 +1073,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && self.tcx.def_kind(fn_def_id).is_fn_like() && let self_implicit = matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize - && let Some(arg) = - self.tcx.fn_arg_names(fn_def_id).get(expected_idx.as_usize() + self_implicit) + && let Some(Some(arg)) = + self.tcx.fn_arg_idents(fn_def_id).get(expected_idx.as_usize() + self_implicit) && arg.name != kw::SelfLower { format!("/* {} */", arg.name) @@ -1649,7 +1573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast::LitKind::Char(_) => tcx.types.char, ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => Ty::new_int(tcx, ty::int_ty(t)), ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => Ty::new_uint(tcx, ty::uint_ty(t)), - ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { + ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) => { let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { ty::Int(_) | ty::Uint(_) => Some(ty), // These exist to direct casts like `0x61 as char` to use @@ -1658,6 +1582,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Char => Some(tcx.types.u8), ty::RawPtr(..) => Some(tcx.types.usize), ty::FnDef(..) | ty::FnPtr(..) => Some(tcx.types.usize), + &ty::Pat(base, _) if base.is_integral() => { + let layout = tcx + .layout_of(self.typing_env(self.param_env).as_query_input(ty)) + .ok()?; + assert!(!layout.uninhabited); + + match layout.backend_repr { + rustc_abi::BackendRepr::Scalar(scalar) => { + scalar.valid_range(&tcx).contains(u128::from(i.get())).then_some(ty) + } + _ => unreachable!(), + } + } _ => None, }); opt_ty.unwrap_or_else(|| self.next_int_var()) @@ -1828,6 +1765,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Type check a `let` statement. fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) { + GatherLocalsVisitor::gather_from_local(self, local); + let ty = self.check_decl(local.into()); self.write_ty(local.hir_id, ty); if local.pat.is_never_pattern() { @@ -2174,7 +2113,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let result = self .lowerer() - .lower_assoc_path(hir_id, path_span, ty.raw, qself, segment, true); + .lower_assoc_path_ty(hir_id, path_span, ty.raw, qself, segment, true); let ty = result .map(|(ty, _, _)| ty) .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)); @@ -2193,62 +2132,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(super) fn collect_unused_stmts_for_coerce_return_ty( - &self, - errors_causecode: Vec<(Span, ObligationCauseCode<'tcx>)>, - ) { - for (span, code) in errors_causecode { - self.dcx().try_steal_modify_and_emit_err(span, StashKey::MaybeForgetReturn, |err| { - if let Some(fn_sig) = self.body_fn_sig() - && let ObligationCauseCode::WhereClauseInExpr(_, _, binding_hir_id, ..) = code - && !fn_sig.output().is_unit() - { - let mut block_num = 0; - let mut found_semi = false; - for (hir_id, node) in self.tcx.hir_parent_iter(binding_hir_id) { - // Don't proceed into parent bodies - if hir_id.owner != binding_hir_id.owner { - break; - } - match node { - hir::Node::Stmt(stmt) => { - if let hir::StmtKind::Semi(expr) = stmt.kind { - let expr_ty = self.typeck_results.borrow().expr_ty(expr); - let return_ty = fn_sig.output(); - if !matches!(expr.kind, hir::ExprKind::Ret(..)) - && self.may_coerce(expr_ty, return_ty) - { - found_semi = true; - } - } - } - hir::Node::Block(_block) => { - if found_semi { - block_num += 1; - } - } - hir::Node::Item(item) => { - if let hir::ItemKind::Fn { .. } = item.kind { - break; - } - } - _ => {} - } - } - if block_num > 1 && found_semi { - err.span_suggestion_verbose( - // use the span of the *whole* expr - self.tcx.hir().span(binding_hir_id).shrink_to_lo(), - "you might have meant to return this to infer its type parameters", - "return ", - Applicability::MaybeIncorrect, - ); - } - } - }); - } - } - /// Given a vector of fulfillment errors, try to adjust the spans of the /// errors to more accurately point at the cause of the failure. /// @@ -2456,7 +2339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if !mismatched_params.is_empty() { - // For each mismatched paramter, create a two-way link to each matched parameter + // For each mismatched parameter, create a two-way link to each matched parameter // of the same type. let mut dependants = IndexVec::::from_fn_n( |_| SmallVec::<[u32; 4]>::new(), @@ -2676,7 +2559,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_method: bool, ) -> Option<(IndexVec, FnParam<'_>)>, &hir::Generics<'_>)> { - let (sig, generics, body_id, param_names) = match self.tcx.hir_get_if_local(def_id)? { + let (sig, generics, body_id, params) = match self.tcx.hir_get_if_local(def_id)? { hir::Node::TraitItem(&hir::TraitItem { generics, kind: hir::TraitItemKind::Fn(sig, trait_fn), @@ -2718,7 +2601,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } }); - match (body_id, param_names) { + match (body_id, params) { (Some(_), Some(_)) | (None, None) => unreachable!(), (Some(body), None) => { let params = self.tcx.hir_body(body).params; @@ -2735,7 +2618,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?; debug_assert_eq!(params.len(), fn_inputs.len()); Some(( - fn_inputs.zip(params.iter().map(|param| FnParam::Name(param))).collect(), + fn_inputs.zip(params.iter().map(|&ident| FnParam::Ident(ident))).collect(), generics, )) } @@ -2766,23 +2649,20 @@ impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> { #[derive(Clone, Copy)] enum FnParam<'hir> { Param(&'hir hir::Param<'hir>), - Name(&'hir Ident), + Ident(Option), } + impl FnParam<'_> { fn span(&self) -> Span { match self { - Self::Param(x) => x.span, - Self::Name(x) => x.span, - } - } - - fn name(&self) -> Option { - match self { - Self::Param(x) if let hir::PatKind::Binding(_, _, ident, _) = x.pat.kind => { - Some(ident.name) + Self::Param(param) => param.span, + Self::Ident(ident) => { + if let Some(ident) = ident { + ident.span + } else { + DUMMY_SP + } } - Self::Name(x) if x.name != kw::Empty => Some(x.name), - _ => None, } } @@ -2790,8 +2670,24 @@ impl FnParam<'_> { struct D<'a>(FnParam<'a>, usize); impl fmt::Display for D<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(name) = self.0.name() { - write!(f, "`{name}`") + // A "unique" param name is one that (a) exists, and (b) is guaranteed to be unique + // among the parameters, i.e. `_` does not count. + let unique_name = match self.0 { + FnParam::Param(param) + if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind => + { + Some(ident.name) + } + FnParam::Ident(ident) + if let Some(ident) = ident + && ident.name != kw::Underscore => + { + Some(ident.name) + } + _ => None, + }; + if let Some(unique_name) = unique_name { + write!(f, "`{unique_name}`") } else { write!(f, "parameter #{}", self.1 + 1) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index dc10b53fd8390..e068e60790277 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -1,13 +1,12 @@ //! A utility module to inspect currently ambiguous obligations in the current context. use rustc_infer::traits::{self, ObligationCause, PredicateObligations}; -use rustc_middle::traits::solve::Goal; +use rustc_middle::traits::solve::GoalSource; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_span::Span; use rustc_trait_selection::solve::inspect::{ InspectConfig, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor, }; -use rustc_type_ir::solve::GoalSource; use tracing::{debug, instrument, trace}; use crate::FnCtxt; @@ -86,7 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { root_cause: &obligation.cause, }; - let goal = Goal::new(self.tcx, obligation.param_env, obligation.predicate); + let goal = obligation.as_goal(); self.visit_proof_tree(goal, &mut visitor); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 42236ac6d8087..de189b301092c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -17,7 +17,7 @@ use rustc_infer::infer; use rustc_infer::traits::Obligation; use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; -use rustc_span::{self, DUMMY_SP, Ident, Span, sym}; +use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym}; use rustc_trait_selection::error_reporting::TypeErrCtxt; use rustc_trait_selection::error_reporting::infer::sub_relations::SubRelations; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; @@ -308,15 +308,17 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { )) } - fn lower_assoc_ty( + fn lower_assoc_shared( &self, span: Span, item_def_id: DefId, - item_segment: &hir::PathSegment<'tcx>, + item_segment: &rustc_hir::PathSegment<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Ty<'tcx> { + _assoc_tag: ty::AssocTag, + ) -> Result<(DefId, ty::GenericArgsRef<'tcx>), ErrorGuaranteed> { let trait_ref = self.instantiate_binder_with_fresh_vars( span, + // FIXME(mgca): this should be assoc const if that is the `kind` infer::BoundRegionConversionTime::AssocTypeProjection(item_def_id), poly_trait_ref, ); @@ -328,14 +330,14 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { trait_ref.args, ); - Ty::new_projection_from_args(self.tcx(), item_def_id, item_args) + Ok((item_def_id, item_args)) } fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option> { match ty.kind() { ty::Adt(adt_def, _) => Some(*adt_def), // FIXME(#104767): Should we handle bound regions here? - ty::Alias(ty::Projection | ty::Inherent | ty::Weak, _) + ty::Alias(ty::Projection | ty::Inherent | ty::Free, _) if !ty.has_escaping_bound_vars() => { if self.next_trait_solver() { @@ -355,7 +357,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { // WF obligations that are registered elsewhere, but they have a // better cause code assigned to them in `add_required_obligations_for_hir`. // This means that they should shadow obligations with worse spans. - if let ty::Alias(ty::Projection | ty::Weak, ty::AliasTy { args, def_id, .. }) = + if let ty::Alias(ty::Projection | ty::Free, ty::AliasTy { args, def_id, .. }) = ty.kind() { self.add_required_obligations_for_hir(span, *def_id, args, hir_id); @@ -486,7 +488,7 @@ fn parse_never_type_options_attr( item.span(), format!( "unknown or duplicate never type option: `{}` (supported: `fallback`, `diverging_block_default`)", - item.name_or_empty() + item.name().unwrap() ), ); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 35a3491f7c089..1079262b5af84 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -351,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.suggest_fn_call(err, expr, found, |output| self.may_coerce(output, expected)) && let ty::FnDef(def_id, ..) = *found.kind() - && let Some(sp) = self.tcx.hir().span_if_local(def_id) + && let Some(sp) = self.tcx.hir_span_if_local(def_id) { let name = self.tcx.item_name(def_id); let kind = self.tcx.def_kind(def_id); @@ -381,9 +381,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut suggestions = methods .iter() .filter_map(|conversion_method| { + let conversion_method_name = conversion_method.name(); let receiver_method_ident = expr.method_ident(); if let Some(method_ident) = receiver_method_ident - && method_ident.name == conversion_method.name + && method_ident.name == conversion_method_name { return None; // do not suggest code that is already there (#53348) } @@ -391,23 +392,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let method_call_list = [sym::to_vec, sym::to_string]; let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind && receiver_method.ident.name == sym::clone - && method_call_list.contains(&conversion_method.name) + && method_call_list.contains(&conversion_method_name) // If receiver is `.clone()` and found type has one of those methods, // we guess that the user wants to convert from a slice type (`&[]` or `&str`) // to an owned type (`Vec` or `String`). These conversions clone internally, // so we remove the user's `clone` call. { - vec![(receiver_method.ident.span, conversion_method.name.to_string())] + vec![(receiver_method.ident.span, conversion_method_name.to_string())] } else if expr.precedence() < ExprPrecedence::Unambiguous { vec![ (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), + (expr.span.shrink_to_hi(), format!(").{}()", conversion_method_name)), ] } else { - vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] + vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method_name))] }; let struct_pat_shorthand_field = - self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr); + self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr); if let Some(name) = struct_pat_shorthand_field { sugg.insert(0, (expr.span.shrink_to_lo(), format!("{name}: "))); } @@ -449,7 +450,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let prefix_wrap = |sugg: &str| { - if let Some(name) = self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { + if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { format!(": {}{}", name, sugg) } else { sugg.to_string() @@ -584,6 +585,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { errors::SuggestBoxing::AsyncBody } + _ if let Node::ExprField(expr_field) = self.tcx.parent_hir_node(hir_id) + && expr_field.is_shorthand => + { + errors::SuggestBoxing::ExprFieldShorthand { + start: span.shrink_to_lo(), + end: span.shrink_to_hi(), + ident: expr_field.ident, + } + } _ => errors::SuggestBoxing::Other { start: span.shrink_to_lo(), end: span.shrink_to_hi(), @@ -613,7 +623,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .take(4) .map(|(var_hir_id, upvar)| { - let var_name = self.tcx.hir().name(*var_hir_id).to_string(); + let var_name = self.tcx.hir_name(*var_hir_id).to_string(); let msg = format!("`{var_name}` captured here"); (upvar.span, msg) }) @@ -671,7 +681,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => { let prefix = if let Some(name) = - self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) + self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { format!("{}: ", name) } else { @@ -904,7 +914,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Assumes given function doesn't have a explicit return type. fn can_add_return_type(&self, fn_id: LocalDefId) -> bool { match self.tcx.hir_node_by_def_id(fn_id) { - Node::Item(&hir::Item { ident, .. }) => { + Node::Item(item) => { + let (ident, _, _, _) = item.expect_fn(); // 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. @@ -1152,7 +1163,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if can_return && let Some(span) = expr.span.find_ancestor_inside( - self.tcx.hir().span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)), + self.tcx.hir_span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)), ) { // When the expr is in a match arm's body, we shouldn't add semicolon ';' at the end. @@ -1274,7 +1285,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .must_apply_modulo_regions() { - let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { + let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { Some(ident) => format!(": {ident}.clone()"), None => ".clone()".to_string(), }; @@ -1380,7 +1391,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (span.shrink_to_hi(), ").into()".to_owned()), ] }; - if let Some(name) = self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { + if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name))); } diag.multipart_suggestion( @@ -1435,7 +1446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } - let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { + let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { Some(ident) => format!(": {ident}.is_some()"), None => ".is_some()".to_string(), }; @@ -1532,30 +1543,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.may_coerce(blk_ty, *elem_ty) && blk.stmts.is_empty() && blk.rules == hir::BlockCheckMode::DefaultBlock + && let source_map = self.tcx.sess.source_map() + && let Ok(snippet) = source_map.span_to_snippet(blk.span) + && snippet.starts_with('{') + && snippet.ends_with('}') { - let source_map = self.tcx.sess.source_map(); - if let Ok(snippet) = source_map.span_to_snippet(blk.span) { - if snippet.starts_with('{') && snippet.ends_with('}') { - diag.multipart_suggestion_verbose( - "to create an array, use square brackets instead of curly braces", - vec![ - ( - blk.span - .shrink_to_lo() - .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)), - "[".to_string(), - ), - ( - blk.span - .shrink_to_hi() - .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)), - "]".to_string(), - ), - ], - Applicability::MachineApplicable, - ); - } - } + diag.multipart_suggestion_verbose( + "to create an array, use square brackets instead of curly braces", + vec![ + ( + blk.span + .shrink_to_lo() + .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)), + "[".to_string(), + ), + ( + blk.span + .shrink_to_hi() + .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)), + "]".to_string(), + ), + ], + Applicability::MachineApplicable, + ); } } } @@ -2032,7 +2042,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }; - let sugg = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { + let sugg = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { + Some(_) if expr.span.from_expansion() => return false, Some(ident) => format!(": {ident}{sugg}"), None => sugg.to_string(), }; @@ -2289,7 +2300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Unroll desugaring, to make sure this works for `for` loops etc. loop { parent = self.tcx.parent_hir_id(id); - let parent_span = self.tcx.hir().span(parent); + let parent_span = self.tcx.hir_span(parent); if parent_span.find_ancestor_inside(expr.span).is_some() { // The parent node is part of the same span, so is the result of the // same expansion/desugaring and not the 'real' parent node. @@ -2378,7 +2389,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect(); let suggestions_for = |variant: &_, ctor_kind, field_name| { - let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { + let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { Some(ident) => format!("{ident}: "), None => String::new(), }; @@ -2700,8 +2711,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); } - let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) - { + let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { Some(ident) => format!("{ident}: "), None => String::new(), }; @@ -2911,7 +2921,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let prefix = - match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { + match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { Some(ident) => format!("{ident}: "), None => String::new(), }; @@ -2983,7 +2993,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } - let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) else { + let span = if let hir::ExprKind::Lit(lit) = &expr.kind { lit.span } else { expr.span }; + let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) else { return false; }; @@ -3078,10 +3089,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Remove fractional part from literal, for example `42.0f32` into `42` let src = src.trim_end_matches(&checked_ty.to_string()); let len = src.split('.').next().unwrap().len(); - expr.span.with_lo(expr.span.lo() + BytePos(len as u32)) + span.with_lo(span.lo() + BytePos(len as u32)) } else { let len = src.trim_end_matches(&checked_ty.to_string()).len(); - expr.span.with_lo(expr.span.lo() + BytePos(len as u32)) + span.with_lo(span.lo() + BytePos(len as u32)) }, if expr.precedence() < ExprPrecedence::Unambiguous { // Readd `)` @@ -3477,30 +3488,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty: Ty<'tcx>, rhs_expr: &'tcx hir::Expr<'tcx>, lhs_expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, ) { - match op.node { - hir::BinOpKind::Eq => { - if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait() - && self - .infcx - .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env) - .must_apply_modulo_regions() - { - let sm = self.tcx.sess.source_map(); - if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span) - && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span) - { - err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`")); - err.multipart_suggestion( - "consider swapping the equality", - vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)], - Applicability::MaybeIncorrect, - ); - } - } + if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait() + && self + .infcx + .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env) + .must_apply_modulo_regions() + { + let sm = self.tcx.sess.source_map(); + if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span) + && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span) + { + err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`")); + err.multipart_suggestion( + "consider swapping the equality", + vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)], + Applicability::MaybeIncorrect, + ); } - _ => {} } } } diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 48fd5f1f98249..7d99b0e78694e 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -43,7 +43,7 @@ pub(super) struct Declaration<'a> { impl<'a> From<&'a hir::LetStmt<'a>> for Declaration<'a> { fn from(local: &'a hir::LetStmt<'a>) -> Self { - let hir::LetStmt { hir_id, pat, ty, span, init, els, source: _ } = *local; + let hir::LetStmt { hir_id, super_: _, pat, ty, span, init, els, source: _ } = *local; Declaration { hir_id, pat, ty, span, init, origin: DeclOrigin::LocalDecl { els } } } } @@ -55,6 +55,14 @@ impl<'a> From<(&'a hir::LetExpr<'a>, HirId)> for Declaration<'a> { } } +/// The `GatherLocalsVisitor` is responsible for initializing local variable types +/// in the [`ty::TypeckResults`] for all subpatterns in statements and expressions +/// like `let`, `match`, and params of function bodies. It also adds `Sized` bounds +/// for these types (with exceptions for unsized feature gates like `unsized_fn_params`). +/// +/// Failure to visit locals will cause an ICE in writeback when the local's type is +/// resolved. Visiting locals twice will ICE in the `GatherLocalsVisitor`, since it +/// will overwrite the type previously stored in the local. pub(super) struct GatherLocalsVisitor<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, // parameters are special cases of patterns, but we want to handle them as @@ -63,12 +71,50 @@ pub(super) struct GatherLocalsVisitor<'a, 'tcx> { outermost_fn_param_pat: Option<(Span, HirId)>, } +// N.B. additional `gather_*` functions should be careful to only walk the pattern +// for new expressions, since visiting sub-expressions or nested bodies may initialize +// locals which are not conceptually owned by the gathered statement or expression. impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { - pub(super) fn new(fcx: &'a FnCtxt<'a, 'tcx>) -> Self { - Self { fcx, outermost_fn_param_pat: None } + pub(crate) fn gather_from_local(fcx: &'a FnCtxt<'a, 'tcx>, local: &'tcx hir::LetStmt<'tcx>) { + let mut visitor = GatherLocalsVisitor { fcx, outermost_fn_param_pat: None }; + visitor.declare(local.into()); + visitor.visit_pat(local.pat); + } + + pub(crate) fn gather_from_let_expr( + fcx: &'a FnCtxt<'a, 'tcx>, + let_expr: &'tcx hir::LetExpr<'tcx>, + expr_hir_id: hir::HirId, + ) { + let mut visitor = GatherLocalsVisitor { fcx, outermost_fn_param_pat: None }; + visitor.declare((let_expr, expr_hir_id).into()); + visitor.visit_pat(let_expr.pat); + } + + pub(crate) fn gather_from_param(fcx: &'a FnCtxt<'a, 'tcx>, param: &'tcx hir::Param<'tcx>) { + let mut visitor = GatherLocalsVisitor { + fcx, + outermost_fn_param_pat: Some((param.ty_span, param.hir_id)), + }; + visitor.visit_pat(param.pat); + } + + pub(crate) fn gather_from_arm(fcx: &'a FnCtxt<'a, 'tcx>, local: &'tcx hir::Arm<'tcx>) { + let mut visitor = GatherLocalsVisitor { fcx, outermost_fn_param_pat: None }; + visitor.visit_pat(local.pat); } fn assign(&mut self, span: Span, nid: HirId, ty_opt: Option>) -> Ty<'tcx> { + // We evaluate expressions twice occasionally in diagnostics for better + // type information or because it needs type information out-of-order. + // In order to not ICE and not lead to knock-on ambiguity errors, if we + // try to re-assign a type to a local, then just take out the previous + // type and delay a bug. + if let Some(&local) = self.fcx.locals.borrow_mut().get(&nid) { + self.fcx.dcx().span_delayed_bug(span, "evaluated expression more than once"); + return local; + } + match ty_opt { None => { // Infer the variable's type. @@ -133,13 +179,6 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { intravisit::walk_expr(self, expr) } - fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - let old_outermost_fn_param_pat = - self.outermost_fn_param_pat.replace((param.ty_span, param.hir_id)); - intravisit::walk_param(self, param); - self.outermost_fn_param_pat = old_outermost_fn_param_pat; - } - // Add pattern bindings. fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { if let PatKind::Binding(_, _, ident, _) = p.kind { @@ -179,7 +218,12 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { ); } let old_outermost_fn_param_pat = self.outermost_fn_param_pat.take(); - intravisit::walk_pat(self, p); + if let PatKind::Guard(subpat, _) = p.kind { + // We'll visit the guard when checking it. Don't gather its locals twice. + self.visit_pat(subpat); + } else { + intravisit::walk_pat(self, p); + } self.outermost_fn_param_pat = old_outermost_fn_param_pat; } diff --git a/compiler/rustc_hir_typeck/src/inline_asm.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs new file mode 100644 index 0000000000000..6399f0a78ae2f --- /dev/null +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -0,0 +1,555 @@ +use rustc_abi::FieldIdx; +use rustc_ast::InlineAsmTemplatePiece; +use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, LangItem}; +use rustc_middle::bug; +use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; +use rustc_session::lint; +use rustc_span::def_id::LocalDefId; +use rustc_span::{Span, Symbol, sym}; +use rustc_target::asm::{ + InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo, +}; +use rustc_trait_selection::infer::InferCtxtExt; + +use crate::FnCtxt; +use crate::errors::RegisterTypeUnstable; + +pub(crate) struct InlineAsmCtxt<'a, 'tcx> { + target_features: &'tcx FxIndexSet, + fcx: &'a FnCtxt<'a, 'tcx>, +} + +enum NonAsmTypeReason<'tcx> { + UnevaluatedSIMDArrayLength(DefId, ty::Const<'tcx>), + Invalid(Ty<'tcx>), + InvalidElement(DefId, Ty<'tcx>), + NotSizedPtr(Ty<'tcx>), + EmptySIMDArray(Ty<'tcx>), +} + +impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { + pub(crate) fn new(fcx: &'a FnCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { + InlineAsmCtxt { target_features: fcx.tcx.asm_target_features(def_id), fcx } + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.fcx.tcx + } + + fn expr_ty(&self, expr: &hir::Expr<'tcx>) -> Ty<'tcx> { + let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted(expr); + let ty = self.fcx.try_structurally_resolve_type(expr.span, ty); + if ty.has_non_region_infer() { + Ty::new_misc_error(self.tcx()) + } else { + self.tcx().erase_regions(ty) + } + } + + // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()` + fn is_thin_ptr_ty(&self, span: Span, ty: Ty<'tcx>) -> bool { + // Type still may have region variables, but `Sized` does not depend + // on those, so just erase them before querying. + if self.fcx.type_is_sized_modulo_regions(self.fcx.param_env, ty) { + return true; + } + if let ty::Foreign(..) = self.fcx.try_structurally_resolve_type(span, ty).kind() { + return true; + } + false + } + + fn get_asm_ty( + &self, + span: Span, + ty: Ty<'tcx>, + ) -> Result> { + let asm_ty_isize = match self.tcx().sess.target.pointer_width { + 16 => InlineAsmType::I16, + 32 => InlineAsmType::I32, + 64 => InlineAsmType::I64, + width => bug!("unsupported pointer width: {width}"), + }; + + match *ty.kind() { + ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::I8), + ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::I16), + ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::I32), + ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::I64), + ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Ok(InlineAsmType::I128), + ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Ok(asm_ty_isize), + ty::Float(FloatTy::F16) => Ok(InlineAsmType::F16), + ty::Float(FloatTy::F32) => Ok(InlineAsmType::F32), + ty::Float(FloatTy::F64) => Ok(InlineAsmType::F64), + ty::Float(FloatTy::F128) => Ok(InlineAsmType::F128), + ty::FnPtr(..) => Ok(asm_ty_isize), + ty::RawPtr(elem_ty, _) => { + if self.is_thin_ptr_ty(span, elem_ty) { + Ok(asm_ty_isize) + } else { + Err(NonAsmTypeReason::NotSizedPtr(ty)) + } + } + ty::Adt(adt, args) if adt.repr().simd() => { + let fields = &adt.non_enum_variant().fields; + if fields.is_empty() { + return Err(NonAsmTypeReason::EmptySIMDArray(ty)); + } + let field = &fields[FieldIdx::ZERO]; + let elem_ty = field.ty(self.tcx(), args); + + let (size, ty) = match *elem_ty.kind() { + ty::Array(ty, len) => { + // FIXME: `try_structurally_resolve_const` doesn't eval consts + // in the old solver. + let len = if self.fcx.next_trait_solver() { + self.fcx.try_structurally_resolve_const(span, len) + } else { + self.fcx.tcx.normalize_erasing_regions( + self.fcx.typing_env(self.fcx.param_env), + len, + ) + }; + if let Some(len) = len.try_to_target_usize(self.tcx()) { + (len, ty) + } else { + return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength( + field.did, len, + )); + } + } + _ => (fields.len() as u64, elem_ty), + }; + + match ty.kind() { + ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::VecI8(size)), + ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::VecI16(size)), + ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::VecI32(size)), + ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::VecI64(size)), + ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => { + Ok(InlineAsmType::VecI128(size)) + } + ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => { + Ok(match self.tcx().sess.target.pointer_width { + 16 => InlineAsmType::VecI16(size), + 32 => InlineAsmType::VecI32(size), + 64 => InlineAsmType::VecI64(size), + width => bug!("unsupported pointer width: {width}"), + }) + } + ty::Float(FloatTy::F16) => Ok(InlineAsmType::VecF16(size)), + ty::Float(FloatTy::F32) => Ok(InlineAsmType::VecF32(size)), + ty::Float(FloatTy::F64) => Ok(InlineAsmType::VecF64(size)), + ty::Float(FloatTy::F128) => Ok(InlineAsmType::VecF128(size)), + _ => Err(NonAsmTypeReason::InvalidElement(field.did, ty)), + } + } + ty::Infer(_) => bug!("unexpected infer ty in asm operand"), + _ => Err(NonAsmTypeReason::Invalid(ty)), + } + } + + fn check_asm_operand_type( + &self, + idx: usize, + reg: InlineAsmRegOrRegClass, + expr: &'tcx hir::Expr<'tcx>, + template: &[InlineAsmTemplatePiece], + is_input: bool, + tied_input: Option<(&'tcx hir::Expr<'tcx>, Option)>, + ) -> Option { + let ty = self.expr_ty(expr); + if ty.has_non_region_infer() { + bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty); + } + + let asm_ty = match *ty.kind() { + // `!` is allowed for input but not for output (issue #87802) + ty::Never if is_input => return None, + _ if ty.references_error() => return None, + ty::Adt(adt, args) if self.tcx().is_lang_item(adt.did(), LangItem::MaybeUninit) => { + let fields = &adt.non_enum_variant().fields; + let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx(), args); + // FIXME: Are we just trying to map to the `T` in `MaybeUninit`? + // If so, just get it from the args. + let ty::Adt(ty, args) = ty.kind() else { + unreachable!("expected first field of `MaybeUninit` to be an ADT") + }; + assert!( + ty.is_manually_drop(), + "expected first field of `MaybeUninit` to be `ManuallyDrop`" + ); + let fields = &ty.non_enum_variant().fields; + let ty = fields[FieldIdx::ZERO].ty(self.tcx(), args); + self.get_asm_ty(expr.span, ty) + } + _ => self.get_asm_ty(expr.span, ty), + }; + let asm_ty = match asm_ty { + Ok(asm_ty) => asm_ty, + Err(reason) => { + match reason { + NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => { + let msg = format!("cannot evaluate SIMD vector length `{len}`"); + self.fcx + .dcx() + .struct_span_err(self.tcx().def_span(did), msg) + .with_span_note( + expr.span, + "SIMD vector length needs to be known statically for use in `asm!`", + ) + .emit(); + } + NonAsmTypeReason::Invalid(ty) => { + let msg = format!("cannot use value of type `{ty}` for inline assembly"); + self.fcx.dcx().struct_span_err(expr.span, msg).with_note( + "only integers, floats, SIMD vectors, pointers and function pointers \ + can be used as arguments for inline assembly", + ).emit(); + } + NonAsmTypeReason::NotSizedPtr(ty) => { + let msg = format!( + "cannot use value of unsized pointer type `{ty}` for inline assembly" + ); + self.fcx + .dcx() + .struct_span_err(expr.span, msg) + .with_note("only sized pointers can be used in inline assembly") + .emit(); + } + NonAsmTypeReason::InvalidElement(did, ty) => { + let msg = format!( + "cannot use SIMD vector with element type `{ty}` for inline assembly" + ); + self.fcx.dcx() + .struct_span_err(self.tcx().def_span(did), msg).with_span_note( + expr.span, + "only integers, floats, SIMD vectors, pointers and function pointers \ + can be used as arguments for inline assembly", + ).emit(); + } + NonAsmTypeReason::EmptySIMDArray(ty) => { + let msg = format!("use of empty SIMD vector `{ty}`"); + self.fcx.dcx().struct_span_err(expr.span, msg).emit(); + } + } + return None; + } + }; + + // Check that the type implements Copy. The only case where this can + // possibly fail is for SIMD types which don't #[derive(Copy)]. + if !self.fcx.type_is_copy_modulo_regions(self.fcx.param_env, ty) { + let msg = "arguments for inline assembly must be copyable"; + self.fcx + .dcx() + .struct_span_err(expr.span, msg) + .with_note(format!("`{ty}` does not implement the Copy trait")) + .emit(); + } + + // Ideally we wouldn't need to do this, but LLVM's register allocator + // really doesn't like it when tied operands have different types. + // + // This is purely an LLVM limitation, but we have to live with it since + // there is no way to hide this with implicit conversions. + // + // For the purposes of this check we only look at the `InlineAsmType`, + // which means that pointers and integers are treated as identical (modulo + // size). + if let Some((in_expr, Some(in_asm_ty))) = tied_input { + if in_asm_ty != asm_ty { + let msg = "incompatible types for asm inout argument"; + let in_expr_ty = self.expr_ty(in_expr); + self.fcx + .dcx() + .struct_span_err(vec![in_expr.span, expr.span], msg) + .with_span_label(in_expr.span, format!("type `{in_expr_ty}`")) + .with_span_label(expr.span, format!("type `{ty}`")) + .with_note( + "asm inout arguments must have the same type, \ + unless they are both pointers or integers of the same size", + ) + .emit(); + } + + // All of the later checks have already been done on the input, so + // let's not emit errors and warnings twice. + return Some(asm_ty); + } + + // Check the type against the list of types supported by the selected + // register class. + let asm_arch = self.tcx().sess.asm_arch.unwrap(); + let allow_experimental_reg = self.tcx().features().asm_experimental_reg(); + let reg_class = reg.reg_class(); + let supported_tys = reg_class.supported_types(asm_arch, allow_experimental_reg); + let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else { + let mut err = if !allow_experimental_reg + && reg_class.supported_types(asm_arch, true).iter().any(|&(t, _)| t == asm_ty) + { + self.tcx().sess.create_feature_err( + RegisterTypeUnstable { span: expr.span, ty }, + sym::asm_experimental_reg, + ) + } else { + let msg = format!("type `{ty}` cannot be used with this register class"); + let mut err = self.fcx.dcx().struct_span_err(expr.span, msg); + let supported_tys: Vec<_> = + supported_tys.iter().map(|(t, _)| t.to_string()).collect(); + err.note(format!( + "register class `{}` supports these types: {}", + reg_class.name(), + supported_tys.join(", "), + )); + err + }; + if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) { + err.help(format!("consider using the `{}` register class instead", suggest.name())); + } + err.emit(); + return Some(asm_ty); + }; + + // Check whether the selected type requires a target feature. Note that + // this is different from the feature check we did earlier. While the + // previous check checked that this register class is usable at all + // with the currently enabled features, some types may only be usable + // with a register class when a certain feature is enabled. We check + // this here since it depends on the results of typeck. + // + // Also note that this check isn't run when the operand type is never + // (!). In that case we still need the earlier check to verify that the + // register class is usable at all. + if let Some(feature) = feature { + if !self.target_features.contains(feature) { + let msg = format!("`{feature}` target feature is not enabled"); + self.fcx + .dcx() + .struct_span_err(expr.span, msg) + .with_note(format!( + "this is required to use type `{}` with register class `{}`", + ty, + reg_class.name(), + )) + .emit(); + return Some(asm_ty); + } + } + + // Check whether a modifier is suggested for using this type. + if let Some(ModifierInfo { + modifier: suggested_modifier, + result: suggested_result, + size: suggested_size, + }) = reg_class.suggest_modifier(asm_arch, asm_ty) + { + // Search for any use of this operand without a modifier and emit + // the suggestion for them. + let mut spans = vec![]; + for piece in template { + if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece + { + if operand_idx == idx && modifier.is_none() { + spans.push(span); + } + } + } + if !spans.is_empty() { + let ModifierInfo { + modifier: default_modifier, + result: default_result, + size: default_size, + } = reg_class.default_modifier(asm_arch).unwrap(); + self.tcx().node_span_lint( + lint::builtin::ASM_SUB_REGISTER, + expr.hir_id, + spans, + |lint| { + lint.primary_message("formatting may not be suitable for sub-register argument"); + lint.span_label(expr.span, "for this argument"); + lint.help(format!( + "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)", + )); + lint.help(format!( + "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)", + )); + }, + ); + } + } + + Some(asm_ty) + } + + pub(crate) fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) { + let Some(asm_arch) = self.tcx().sess.asm_arch else { + self.fcx.dcx().delayed_bug("target architecture does not support asm"); + return; + }; + let allow_experimental_reg = self.tcx().features().asm_experimental_reg(); + for (idx, &(op, op_sp)) in asm.operands.iter().enumerate() { + // Validate register classes against currently enabled target + // features. We check that at least one type is available for + // the enabled features. + // + // We ignore target feature requirements for clobbers: if the + // feature is disabled then the compiler doesn't care what we + // do with the registers. + // + // Note that this is only possible for explicit register + // operands, which cannot be used in the asm string. + if let Some(reg) = op.reg() { + // Some explicit registers cannot be used depending on the + // target. Reject those here. + if let InlineAsmRegOrRegClass::Reg(reg) = reg { + if let InlineAsmReg::Err = reg { + // `validate` will panic on `Err`, as an error must + // already have been reported. + continue; + } + if let Err(msg) = reg.validate( + asm_arch, + self.tcx().sess.relocation_model(), + self.target_features, + &self.tcx().sess.target, + op.is_clobber(), + ) { + let msg = format!("cannot use register `{}`: {}", reg.name(), msg); + self.fcx.dcx().span_err(op_sp, msg); + continue; + } + } + + if !op.is_clobber() { + let mut missing_required_features = vec![]; + let reg_class = reg.reg_class(); + if let InlineAsmRegClass::Err = reg_class { + continue; + } + for &(_, feature) in reg_class.supported_types(asm_arch, allow_experimental_reg) + { + match feature { + Some(feature) => { + if self.target_features.contains(&feature) { + missing_required_features.clear(); + break; + } else { + missing_required_features.push(feature); + } + } + None => { + missing_required_features.clear(); + break; + } + } + } + + // We are sorting primitive strs here and can use unstable sort here + missing_required_features.sort_unstable(); + missing_required_features.dedup(); + match &missing_required_features[..] { + [] => {} + [feature] => { + let msg = format!( + "register class `{}` requires the `{}` target feature", + reg_class.name(), + feature + ); + self.fcx.dcx().span_err(op_sp, msg); + // register isn't enabled, don't do more checks + continue; + } + features => { + let msg = format!( + "register class `{}` requires at least one of the following target features: {}", + reg_class.name(), + features + .iter() + .map(|f| f.as_str()) + .intersperse(", ") + .collect::(), + ); + self.fcx.dcx().span_err(op_sp, msg); + // register isn't enabled, don't do more checks + continue; + } + } + } + } + + match op { + hir::InlineAsmOperand::In { reg, expr } => { + self.check_asm_operand_type(idx, reg, expr, asm.template, true, None); + } + hir::InlineAsmOperand::Out { reg, late: _, expr } => { + if let Some(expr) = expr { + self.check_asm_operand_type(idx, reg, expr, asm.template, false, None); + } + } + hir::InlineAsmOperand::InOut { reg, late: _, expr } => { + self.check_asm_operand_type(idx, reg, expr, asm.template, false, None); + } + hir::InlineAsmOperand::SplitInOut { reg, late: _, in_expr, out_expr } => { + let in_ty = + self.check_asm_operand_type(idx, reg, in_expr, asm.template, true, None); + if let Some(out_expr) = out_expr { + self.check_asm_operand_type( + idx, + reg, + out_expr, + asm.template, + false, + Some((in_expr, in_ty)), + ); + } + } + hir::InlineAsmOperand::Const { anon_const } => { + let ty = self.expr_ty(self.tcx().hir_body(anon_const.body).value); + match ty.kind() { + ty::Error(_) => {} + _ if ty.is_integral() => {} + _ => { + self.fcx + .dcx() + .struct_span_err(op_sp, "invalid type for `const` operand") + .with_span_label( + self.tcx().def_span(anon_const.def_id), + format!("is {} `{}`", ty.kind().article(), ty), + ) + .with_help("`const` operands must be of an integer type") + .emit(); + } + } + } + // Typeck has checked that SymFn refers to a function. + hir::InlineAsmOperand::SymFn { expr } => { + let ty = self.expr_ty(expr); + match ty.kind() { + ty::FnDef(..) => {} + ty::Error(_) => {} + _ => { + self.fcx + .dcx() + .struct_span_err(op_sp, "invalid `sym` operand") + .with_span_label( + expr.span, + format!("is {} `{}`", ty.kind().article(), ty), + ) + .with_help( + "`sym` operands must refer to either a function or a static", + ) + .emit(); + } + } + } + // AST lowering guarantees that SymStatic points to a static. + hir::InlineAsmOperand::SymStatic { .. } => {} + // No special checking is needed for labels. + hir::InlineAsmOperand::Label { .. } => {} + } + } + } +} diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 54e9e699353fa..194e420b606ea 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -45,7 +45,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) { let tcx = self.tcx; let dl = &tcx.data_layout; - let span = tcx.hir().span(hir_id); + let span = tcx.hir_span(hir_id); let normalize = |ty| { let ty = self.resolve_vars_if_possible(ty); if let Ok(ty) = diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 8b9c2b4a6ca0f..a7444b45ab0f2 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -2,13 +2,12 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![feature(array_windows)] +#![feature(assert_matches)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(never_type)] #![feature(try_blocks)] -#![warn(unreachable_pub)] // tidy-alphabetical-end mod _match; @@ -24,6 +23,7 @@ mod diverges; mod errors; mod expectation; mod expr; +mod inline_asm; // Used by clippy; pub mod expr_use_visitor; mod fallback; @@ -32,6 +32,7 @@ mod gather_locals; mod intrinsicck; mod method; mod op; +mod opaque_types; mod pat; mod place_op; mod rvalue_scopes; @@ -46,7 +47,6 @@ use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::Visitor; use rustc_hir::{HirId, HirIdMap, Node}; use rustc_hir_analysis::check::check_abi; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; @@ -117,7 +117,7 @@ fn typeck_with_inspect<'tcx>( let id = tcx.local_def_id_to_hir_id(def_id); let node = tcx.hir_node(id); - let span = tcx.hir().span(id); + let span = tcx.def_span(def_id); // Figure out what primary body this item has. let body_id = node.body_id().unwrap_or_else(|| { @@ -191,9 +191,6 @@ fn typeck_with_inspect<'tcx>( let wf_code = ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(def_id))); fcx.register_wf_obligation(expected_type.into(), body.value.span, wf_code); - // Gather locals in statics (because of block expressions). - GatherLocalsVisitor::new(&fcx).visit_body(body); - fcx.check_expr_coercible_to_type(body.value, expected_type, None); fcx.write_ty(id, expected_type); @@ -249,9 +246,7 @@ fn typeck_with_inspect<'tcx>( let typeck_results = fcx.resolve_type_vars_in_body(body); - // We clone the defined opaque types during writeback in the new solver - // because we have to use them during normalization. - let _ = fcx.infcx.take_opaque_types(); + fcx.detect_opaque_types_added_during_writeback(); // Consistency check our TypeckResults instance can hold all ItemLocalIds // it will need to hold. @@ -266,7 +261,7 @@ fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Opti let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer(()), span, .. }) = node.ty() { if let Some(item) = tcx.opt_associated_item(def_id.into()) - && let ty::AssocKind::Const = item.kind + && let ty::AssocKind::Const { .. } = item.kind && let ty::AssocItemContainer::Impl = item.container && let Some(trait_item_def_id) = item.trait_item_def_id { diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 0483164ca0300..a614b4f00ffe4 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -15,9 +15,9 @@ use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, }; -use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, UserArgs, + self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypeFoldable, + TypeVisitableExt, UserArgs, }; use rustc_middle::{bug, span_bug}; use rustc_span::{DUMMY_SP, Span}; diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 4008021c3a857..34bbb7d7c05e6 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -19,7 +19,7 @@ use rustc_middle::ty::{ self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TypeVisitableExt, }; use rustc_middle::{bug, span_bug}; -use rustc_span::{ErrorGuaranteed, Ident, Span}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{self, NormalizeExt}; use tracing::{debug, instrument}; @@ -329,10 +329,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// an obligation for a particular trait with the given self type and checks /// whether that trait is implemented. #[instrument(level = "debug", skip(self))] - pub(super) fn lookup_method_in_trait( + pub(super) fn lookup_method_for_operator( &self, cause: ObligationCause<'tcx>, - m_name: Ident, + method_name: Symbol, trait_def_id: DefId, self_ty: Ty<'tcx>, opt_rhs_ty: Option>, @@ -374,13 +374,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Trait must have a method named `m_name` and it should not have // type parameters or early-bound regions. let tcx = self.tcx; - let Some(method_item) = self.associated_value(trait_def_id, m_name) else { + // We use `Ident::with_dummy_span` since no built-in operator methods have + // any macro-specific hygeine, so the span's context doesn't really matter. + let Some(method_item) = + self.associated_value(trait_def_id, Ident::with_dummy_span(method_name)) + else { bug!("expected associated item for operator trait") }; let def_id = method_item.def_id; - if method_item.kind != ty::AssocKind::Fn { - span_bug!(tcx.def_span(def_id), "expected `{m_name}` to be an associated function"); + if !method_item.is_fn() { + span_bug!( + tcx.def_span(def_id), + "expected `{method_name}` to be an associated function" + ); } debug!("lookup_in_trait_adjusted: method_item={:?}", method_item); @@ -529,17 +536,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let def_kind = pick.item.kind.as_def_kind(); + let def_kind = pick.item.as_def_kind(); tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span)); Ok((def_kind, pick.item.def_id)) } - /// Finds item with name `item_name` defined in impl/trait `def_id` + /// Finds item with name `item_ident` defined in impl/trait `def_id` /// and return it, or `None`, if no such item was defined there. - fn associated_value(&self, def_id: DefId, item_name: Ident) -> Option { + fn associated_value(&self, def_id: DefId, item_ident: Ident) -> Option { self.tcx .associated_items(def_id) - .find_by_name_and_namespace(self.tcx, item_name, Namespace::ValueNS, def_id) + .find_by_ident_and_namespace(self.tcx, item_ident, Namespace::ValueNS, def_id) .copied() } } diff --git a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs index 69d7a6c97cb36..0037d5ac042bc 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -363,24 +363,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let import_items: Vec<_> = applicable_trait .import_ids .iter() - .map(|&import_id| self.tcx.hir().expect_item(import_id)) + .map(|&import_id| self.tcx.hir_expect_item(import_id)) .collect(); // Find an identifier with which this trait was imported (note that `_` doesn't count). - let any_id = import_items.iter().find_map(|item| { - if item.ident.name != kw::Underscore { Some(item.ident) } else { None } - }); - if let Some(any_id) = any_id { - if any_id.name == kw::Empty { - // Glob import, so just use its name. - return None; - } else { - return Some(format!("{any_id}")); + for item in import_items.iter() { + let (_, kind) = item.expect_use(); + match kind { + hir::UseKind::Single(ident) => { + if ident.name != kw::Underscore { + return Some(format!("{}", ident.name)); + } + } + hir::UseKind::Glob => return None, // Glob import, so just use its name. + hir::UseKind::ListStem => unreachable!(), } } - // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick, - // so just take the first one. + // All that is left is `_`! We need to use the full path. It doesn't matter which one we + // pick, so just take the first one. match import_items[0].kind { ItemKind::Use(path, _) => Some( path.segments diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index f87e5b5202ab5..725240b480ba9 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1,7 +1,9 @@ +use std::assert_matches::debug_assert_matches; use std::cell::{Cell, RefCell}; use std::cmp::max; use std::ops::Deref; +use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; use rustc_errors::Applicability; @@ -14,7 +16,8 @@ use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::middle::stability; use rustc_middle::query::Providers; -use rustc_middle::ty::fast_reject::{TreatParams, simplify_type}; +use rustc_middle::ty::elaborate::supertrait_def_ids; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}; use rustc_middle::ty::{ self, AssocItem, AssocItemContainer, GenericArgs, GenericArgsRef, GenericParamDefKind, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt, Upcast, @@ -34,7 +37,6 @@ use rustc_trait_selection::traits::query::method_autoderef::{ CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult, }; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt}; -use rustc_type_ir::elaborate::supertrait_def_ids; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -806,8 +808,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ); } } - ty::Param(p) => { - self.assemble_inherent_candidates_from_param(p); + ty::Param(_) => { + self.assemble_inherent_candidates_from_param(raw_self_ty); } ty::Bool | ty::Char @@ -908,18 +910,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) { + fn assemble_inherent_candidates_from_param(&mut self, param_ty: Ty<'tcx>) { + debug_assert_matches!(param_ty.kind(), ty::Param(_)); + + let tcx = self.tcx; let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| { let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { - ty::ClauseKind::Trait(trait_predicate) => { - match *trait_predicate.trait_ref.self_ty().kind() { - ty::Param(p) if p == param_ty => { - Some(bound_predicate.rebind(trait_predicate.trait_ref)) - } - _ => None, - } - } + ty::ClauseKind::Trait(trait_predicate) => DeepRejectCtxt::relate_rigid_rigid(tcx) + .types_may_unify(param_ty, trait_predicate.trait_ref.self_ty()) + .then(|| bound_predicate.rebind(trait_predicate.trait_ref)), ty::ClauseKind::RegionOutlives(_) | ty::ClauseKind::TypeOutlives(_) | ty::ClauseKind::Projection(_) @@ -992,7 +992,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn matches_return_type(&self, method: ty::AssocItem, expected: Ty<'tcx>) -> bool { match method.kind { - ty::AssocKind::Fn => self.probe(|_| { + ty::AssocKind::Fn { .. } => self.probe(|_| { let args = self.fresh_args_for_item(self.span, method.def_id); let fty = self.tcx.fn_sig(method.def_id).instantiate(self.tcx, args); let fty = self.instantiate_binder_with_fresh_vars(self.span, infer::FnCall, fty); @@ -1213,7 +1213,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { debug!("pick_all_method: step={:?}", step); // skip types that are from a type error or that would require dereferencing // a raw pointer - !step.self_ty.references_error() && !step.from_unsafe_deref + !step.self_ty.value.references_error() && !step.from_unsafe_deref }) .find_map(|step| { let InferOk { value: self_ty, obligations: _ } = self @@ -1583,7 +1583,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }, None, ) { - self.private_candidate.set(Some((pick.item.kind.as_def_kind(), pick.item.def_id))); + self.private_candidate.set(Some((pick.item.as_def_kind(), pick.item.def_id))); } } None @@ -1671,16 +1671,7 @@ impl<'tcx> Pick<'tcx> { /// Do not use for type checking. pub(crate) fn differs_from(&self, other: &Self) -> bool { let Self { - item: - AssocItem { - def_id, - name: _, - kind: _, - container: _, - trait_item_def_id: _, - fn_has_self_parameter: _, - opt_rpitit_info: _, - }, + item: AssocItem { def_id, kind: _, container: _, trait_item_def_id: _ }, kind: _, import_ids: _, autoderefs: _, @@ -1703,7 +1694,7 @@ impl<'tcx> Pick<'tcx> { if self.unstable_candidates.is_empty() { return; } - let def_kind = self.item.kind.as_def_kind(); + let def_kind = self.item.as_def_kind(); tcx.node_span_lint(lint::builtin::UNSTABLE_NAME_COLLISIONS, scope_expr_id, span, |lint| { lint.primary_message(format!( "{} {} with this name may be added to the standard library in the future", @@ -1712,7 +1703,7 @@ impl<'tcx> Pick<'tcx> { )); match (self.item.kind, self.item.container) { - (ty::AssocKind::Fn, _) => { + (ty::AssocKind::Fn { .. }, _) => { // FIXME: This should be a `span_suggestion` instead of `help` // However `self.span` only // highlights the method name, so we can't use it. Also consider reusing @@ -1723,17 +1714,12 @@ impl<'tcx> Pick<'tcx> { tcx.def_path_str(self.item.def_id), )); } - (ty::AssocKind::Const, ty::AssocItemContainer::Trait) => { + (ty::AssocKind::Const { name }, ty::AssocItemContainer::Trait) => { let def_id = self.item.container_id(tcx); lint.span_suggestion( span, "use the fully qualified path to the associated const", - format!( - "<{} as {}>::{}", - self.self_ty, - tcx.def_path_str(def_id), - self.item.name - ), + format!("<{} as {}>::{}", self.self_ty, tcx.def_path_str(def_id), name), Applicability::MachineApplicable, ); } @@ -2222,7 +2208,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let best_name = { let names = applicable_close_candidates .iter() - .map(|cand| cand.name) + .map(|cand| cand.name()) .collect::>(); find_best_match_for_name_with_substrings( &names, @@ -2234,10 +2220,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { applicable_close_candidates .iter() .find(|cand| self.matches_by_doc_alias(cand.def_id)) - .map(|cand| cand.name) + .map(|cand| cand.name()) }); Ok(best_name.and_then(|best_name| { - applicable_close_candidates.into_iter().find(|method| method.name == best_name) + applicable_close_candidates + .into_iter() + .find(|method| method.name() == best_name) })) } }) @@ -2252,10 +2240,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // In Path mode (i.e., resolving a value like `T::next`), consider any // associated value (i.e., methods, constants) but not types. match self.mode { - Mode::MethodCall => item.fn_has_self_parameter, + Mode::MethodCall => item.is_method(), Mode::Path => match item.kind { - ty::AssocKind::Type => false, - ty::AssocKind::Fn | ty::AssocKind::Const => true, + ty::AssocKind::Type { .. } => false, + ty::AssocKind::Fn { .. } | ty::AssocKind::Const { .. } => true, }, } // FIXME -- check for types that deref to `Self`, @@ -2277,7 +2265,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { impl_ty: Ty<'tcx>, args: GenericArgsRef<'tcx>, ) -> (Ty<'tcx>, Option>) { - if item.kind == ty::AssocKind::Fn && self.mode == Mode::MethodCall { + if item.is_fn() && self.mode == Mode::MethodCall { let sig = self.xform_method_sig(item.def_id, args); (sig.inputs()[0], Some(sig.output())) } else { @@ -2328,8 +2316,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// Determine if the given associated item type is relevant in the current context. fn is_relevant_kind_for_mode(&self, kind: ty::AssocKind) -> bool { match (self.mode, kind) { - (Mode::MethodCall, ty::AssocKind::Fn) => true, - (Mode::Path, ty::AssocKind::Const | ty::AssocKind::Fn) => true, + (Mode::MethodCall, ty::AssocKind::Fn { .. }) => true, + (Mode::Path, ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. }) => true, _ => false, } } @@ -2344,10 +2332,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return false; }; let hir_id = self.fcx.tcx.local_def_id_to_hir_id(local_def_id); - let attrs = self.fcx.tcx.hir().attrs(hir_id); + let attrs = self.fcx.tcx.hir_attrs(hir_id); + + if is_doc_alias_attrs_contain_symbol(attrs.into_iter(), method.name) { + return true; + } + for attr in attrs { - if sym::doc == attr.name_or_empty() { - } else if sym::rustc_confusables == attr.name_or_empty() { + if attr.has_name(sym::rustc_confusables) { let Some(confusables) = attr.meta_item_list() else { continue; }; @@ -2359,33 +2351,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return true; } } - continue; - } else { - continue; - }; - let Some(values) = attr.meta_item_list() else { - continue; - }; - for v in values { - if v.name_or_empty() != sym::alias { - continue; - } - if let Some(nested) = v.meta_item_list() { - // #[doc(alias("foo", "bar"))] - for n in nested { - if let Some(lit) = n.lit() - && method.name == lit.symbol - { - return true; - } - } - } else if let Some(meta) = v.meta_item() - && let Some(lit) = meta.name_value_literal() - && method.name == lit.symbol - { - // #[doc(alias = "foo")] - return true; - } } } false @@ -2411,7 +2376,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } match edit_distance_with_substrings( name.as_str(), - x.name.as_str(), + x.name().as_str(), max_dist, ) { Some(d) => d > 0, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 18218a7a0a641..342eed751a589 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; use hir::Expr; use rustc_ast::ast::Mutability; -use rustc_attr_parsing::{AttributeKind, find_attr}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; @@ -25,6 +25,7 @@ use rustc_middle::bug; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}; use rustc_middle::ty::print::{ PrintTraitRefExt as _, with_crate_prefix, with_forced_trimmed_paths, + with_no_visible_paths_if_doc_hidden, }; use rustc_middle::ty::{self, GenericArgKind, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::def_id::DefIdSet; @@ -158,7 +159,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow_mut().used_trait_imports.insert(import_id); } - let (span, sugg_span, source, item_name, args) = match self.tcx.hir_node(call_id) { + let (span, expr_span, source, item_name, args) = match self.tcx.hir_node(call_id) { hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::MethodCall(segment, rcvr, args, _), span, @@ -194,6 +195,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { node => unreachable!("{node:?}"), }; + // Try to get the span of the identifier within the expression's syntax context + // (if that's different). + let within_macro_span = span.within_macro(expr_span, self.tcx.sess.source_map()); + // Avoid suggestions when we don't know what's going on. if let Err(guar) = rcvr_ty.error_reported() { return guar; @@ -207,10 +212,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_id, source, args, - sugg_span, + expr_span, &mut no_match_data, expected, trait_missing_method, + within_macro_span, ), MethodError::Ambiguity(mut sources) => { @@ -221,6 +227,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "multiple applicable items in scope" ); err.span_label(item_name.span, format!("multiple `{item_name}` found")); + if let Some(within_macro_span) = within_macro_span { + err.span_label(within_macro_span, "due to this macro variable"); + } self.note_candidates_on_method_error( rcvr_ty, @@ -230,7 +239,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, &mut err, &mut sources, - Some(sugg_span), + Some(expr_span), ); err.emit() } @@ -246,12 +255,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name ); err.span_label(item_name.span, format!("private {kind}")); - let sp = self - .tcx - .hir() - .span_if_local(def_id) - .unwrap_or_else(|| self.tcx.def_span(def_id)); + let sp = + self.tcx.hir_span_if_local(def_id).unwrap_or_else(|| self.tcx.def_span(def_id)); err.span_label(sp, format!("private {kind} defined here")); + if let Some(within_macro_span) = within_macro_span { + err.span_label(within_macro_span, "due to this macro variable"); + } self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true); err.emit() } @@ -268,6 +277,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !needs_mut { err.span_label(bound_span, "this has a `Sized` requirement"); } + if let Some(within_macro_span) = within_macro_span { + err.span_label(within_macro_span, "due to this macro variable"); + } if !candidates.is_empty() { let help = format!( "{an}other candidate{s} {were} found in the following trait{s}", @@ -519,7 +531,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } _ => { - intravisit::walk_pat(self, p); + let _ = intravisit::walk_pat(self, p); } } ControlFlow::Continue(()) @@ -542,7 +554,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method_name, sugg_let: None, }; - let_visitor.visit_body(&body); + let _ = let_visitor.visit_body(&body); if let Some(sugg_let) = let_visitor.sugg_let && let Some(self_ty) = self.node_ty_opt(sugg_let.init_hir_id) { @@ -552,7 +564,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span.push_span_label(sugg_let.span, format!("`{rcvr_name}` of type `{self_ty}` that has method `{method_name}` defined earlier here")); span.push_span_label( - self.tcx.hir().span(recv_id), + self.tcx.hir_span(recv_id), format!( "earlier `{rcvr_name}` shadowed here with type `{ty_str_reported}`" ), @@ -573,7 +585,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, mut span: Span, rcvr_ty: Ty<'tcx>, - item_name: Ident, + item_ident: Ident, expr_id: hir::HirId, source: SelfSource<'tcx>, args: Option<&'tcx [hir::Expr<'tcx>]>, @@ -581,6 +593,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { no_match_data: &mut NoMatchData<'tcx>, expected: Expectation<'tcx>, trait_missing_method: bool, + within_macro_span: Option, ) -> ErrorGuaranteed { let mode = no_match_data.mode; let tcx = self.tcx; @@ -603,7 +616,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if rcvr_ty.is_enum() { "variant or associated item" } else { - match (item_name.as_str().chars().next(), rcvr_ty.is_fresh_ty()) { + match (item_ident.as_str().chars().next(), rcvr_ty.is_fresh_ty()) { (Some(name), false) if name.is_lowercase() => "function or associated item", (Some(_), false) => "associated item", (Some(_), true) | (None, false) => "variant or associated item", @@ -618,7 +631,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty, source, span, - item_name, + item_ident, &short_ty_str, &mut ty_file, ) { @@ -630,13 +643,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source, span, item_kind, - item_name, + item_ident, &short_ty_str, &mut ty_file, ) { return guar; } - span = item_name.span; + span = item_ident.span; // Don't show generic arguments when the method can't be found in any implementation (#81576). let mut ty_str_reported = ty_str.clone(); @@ -648,7 +661,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx .inherent_impls(adt_def.did()) .into_iter() - .any(|def_id| self.associated_value(*def_id, item_name).is_some()) + .any(|def_id| self.associated_value(*def_id, item_ident).is_some()) } else { false } @@ -665,14 +678,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.is_some_and(|def_id| { tcx.is_diagnostic_item(sym::write_macro, def_id) || tcx.is_diagnostic_item(sym::writeln_macro, def_id) - }) && item_name.name == sym::write_fmt; + }) && item_ident.name == sym::write_fmt; let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source { self.suggest_missing_writer(rcvr_ty, rcvr_expr) } else { let mut err = self.dcx().create_err(NoAssociatedItem { span, item_kind, - item_name, + item_ident, ty_prefix: if trait_missing_method { // FIXME(mu001999) E0599 maybe not suitable here because it is for types Cow::from("trait") @@ -686,7 +699,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if is_method { self.suggest_use_shadowed_binding_with_method( source, - item_name, + item_ident, &ty_str_reported, &mut err, ); @@ -697,10 +710,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind && let Res::SelfTyAlias { alias_to: impl_def_id, .. } = path.res && let DefKind::Impl { .. } = self.tcx.def_kind(impl_def_id) - && let Some(candidate) = tcx.associated_items(impl_def_id).find_by_name_and_kind( + && let Some(candidate) = tcx.associated_items(impl_def_id).find_by_ident_and_kind( self.tcx, - item_name, - ty::AssocKind::Type, + item_ident, + ty::AssocTag::Type, impl_def_id, ) && let Some(adt_def) = tcx.type_of(candidate.def_id).skip_binder().ty_adt_def() @@ -709,7 +722,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let def_path = tcx.def_path_str(adt_def.did()); err.span_suggestion( - ty.span.to(item_name.span), + ty.span.to(item_ident.span), format!("to construct a value of type `{}`, use the explicit path", def_path), def_path, Applicability::MachineApplicable, @@ -721,6 +734,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); } + if let Some(within_macro_span) = within_macro_span { + err.span_label(within_macro_span, "due to this macro variable"); + } if short_ty_str.len() < ty_str.len() && ty_str.len() > 10 { ty_str = short_ty_str; @@ -734,7 +750,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.find_builder_fn(&mut err, rcvr_ty, expr_id); } - if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll { + if tcx.ty_is_opaque_future(rcvr_ty) && item_ident.name == sym::poll { err.help(format!( "method `poll` found on `Pin<&mut {ty_str}>`, \ see documentation for `std::pin::Pin`" @@ -749,7 +765,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { self.suggest_await_before_method( &mut err, - item_name, + item_ident, rcvr_ty, cal, span, @@ -771,9 +787,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let SelfSource::MethodCall(rcvr_expr) = source && let ty::RawPtr(ty, ptr_mutbl) = *rcvr_ty.kind() && let Ok(pick) = self.lookup_probe_for_diagnostic( - item_name, + item_ident, Ty::new_ref(tcx, ty::Region::new_error_misc(tcx), ty, ptr_mutbl), - self.tcx.hir().expect_expr(self.tcx.parent_hir_id(rcvr_expr.hir_id)), + self.tcx.hir_expect_expr(self.tcx.parent_hir_id(rcvr_expr.hir_id)), ProbeScope::TraitsInScope, None, ) @@ -792,7 +808,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; err.span_note( tcx.def_span(pick.item.def_id), - format!("the method `{item_name}` exists on the type `{ty}`", ty = pick.self_ty), + format!("the method `{item_ident}` exists on the type `{ty}`", ty = pick.self_ty), ); let mut_str = ptr_mutbl.ptr_str(); err.note(format!( @@ -816,10 +832,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let SelfSource::MethodCall(rcvr_expr) = source { self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { - let call_expr = - self.tcx.hir().expect_expr(self.tcx.parent_hir_id(rcvr_expr.hir_id)); + let call_expr = self.tcx.hir_expect_expr(self.tcx.parent_hir_id(rcvr_expr.hir_id)); let probe = self.lookup_probe_for_diagnostic( - item_name, + item_ident, output_ty, call_expr, ProbeScope::AllTraits, @@ -858,13 +873,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { static_candidates, rcvr_ty, source, - item_name, + item_ident, args, sugg_span, ); self.note_candidates_on_method_error( rcvr_ty, - item_name, + item_ident, source, args, span, @@ -875,7 +890,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if static_candidates.len() > 1 { self.note_candidates_on_method_error( rcvr_ty, - item_name, + item_ident, source, args, span, @@ -889,7 +904,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut restrict_type_params = false; let mut suggested_derive = false; let mut unsatisfied_bounds = false; - if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) { + if item_ident.name == sym::count && self.is_slice_ty(rcvr_ty, span) { let msg = "consider using `len` instead"; if let SelfSource::MethodCall(_expr) = source { err.span_suggestion_short(span, msg, "len", Applicability::MachineApplicable); @@ -1187,13 +1202,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Some( Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..), + kind: + hir::ItemKind::Trait(_, _, ident, ..) + | hir::ItemKind::TraitAlias(ident, ..), .. }) // We may also encounter unsatisfied GAT or method bounds | Node::TraitItem(hir::TraitItem { ident, .. }) - | Node::ImplItem(hir::ImplItem { ident, .. }), + | Node::ImplItem(hir::ImplItem { ident, .. }) ) => { skip_list.insert(p); let entry = spanned_predicates.entry(ident.span); @@ -1333,7 +1349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let primary_message = primary_message.unwrap_or_else(|| { format!( - "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, \ + "the {item_kind} `{item_ident}` exists for {actual_prefix} `{ty_str}`, \ but its trait bounds were not satisfied" ) }); @@ -1363,7 +1379,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `Pin<&Self>`. if targs.len() == 1 { let mut item_segment = hir::PathSegment::invalid(); - item_segment.ident = item_name; + item_segment.ident = item_ident; for t in [Ty::new_mut_ref, Ty::new_imm_ref, |_, _, t| t] { let new_args = tcx.mk_args_from_iter(targs.iter().map(|arg| match arg.as_type() { @@ -1407,9 +1423,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Adt(adt, _) => self.tcx.is_lang_item(adt.did(), LangItem::String), _ => false, }; - if is_string_or_ref_str && item_name.name == sym::iter { + if is_string_or_ref_str && item_ident.name == sym::iter { err.span_suggestion_verbose( - item_name.span, + item_ident.span, "because of the in-memory representation of `&str`, to obtain \ an `Iterator` over each of its codepoint use method `chars`", "chars", @@ -1423,10 +1439,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .into_iter() .copied() .filter(|def_id| { - if let Some(assoc) = self.associated_value(*def_id, item_name) { + if let Some(assoc) = self.associated_value(*def_id, item_ident) { // Check for both mode is the same so we avoid suggesting // incorrect associated item. - match (mode, assoc.fn_has_self_parameter, source) { + match (mode, assoc.is_method(), source) { (Mode::MethodCall, true, SelfSource::MethodCall(_)) => { // We check that the suggest type is actually // different from the received one @@ -1484,7 +1500,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the method name is the name of a field with a function or closure type, // give a helping note that it has to be called as `(x.f)(...)`. if let SelfSource::MethodCall(expr) = source { - if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err) + if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_ident, &mut err) && similar_candidate.is_none() && !custom_span_label { @@ -1497,7 +1513,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let confusable_suggested = self.confusable_method_name( &mut err, rcvr_ty, - item_name, + item_ident, args.map(|args| { args.iter() .map(|expr| { @@ -1515,12 +1531,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source, span, rcvr_ty, - item_name, + item_ident, expected.only_has_type(self), ); } - self.suggest_unwrapping_inner_self(&mut err, source, rcvr_ty, item_name); + self.suggest_unwrapping_inner_self(&mut err, source, rcvr_ty, item_ident); for (span, mut bounds) in bound_spans { if !tcx.sess.source_map().is_span_accessible(span) { @@ -1531,7 +1547,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pre = if Some(span) == ty_span { ty_span.take(); format!( - "{item_kind} `{item_name}` not found for this {} because it ", + "{item_kind} `{item_ident}` not found for this {} because it ", rcvr_ty.prefix_string(self.tcx) ) } else { @@ -1551,7 +1567,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label( span, format!( - "{item_kind} `{item_name}` not found for this {}", + "{item_kind} `{item_ident}` not found for this {}", rcvr_ty.prefix_string(self.tcx) ), ); @@ -1563,7 +1579,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut err, span, rcvr_ty, - item_name, + item_ident, args.map(|args| args.len() + 1), source, no_match_data.out_of_scope_traits.clone(), @@ -1580,7 +1596,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); if let Some(var_name) = edit_distance::find_best_match_for_name( &adt_def.variants().iter().map(|s| s.name).collect::>(), - item_name.name, + item_ident.name, None, ) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == var_name) { @@ -1706,7 +1722,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // that had unsatisfied trait bounds if unsatisfied_predicates.is_empty() // ...or if we already suggested that name because of `rustc_confusable` annotation. - && Some(similar_candidate.name) != confusable_suggested + && Some(similar_candidate.name()) != confusable_suggested { self.find_likely_intended_associated_item( &mut err, @@ -1721,14 +1737,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !find_candidate_for_method { self.lookup_segments_chain_for_no_match_method( &mut err, - item_name, + item_ident, item_kind, source, no_match_data, ); } - self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected); + self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected); err.emit() } @@ -1803,12 +1819,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mode: Mode, ) { let tcx = self.tcx; - let def_kind = similar_candidate.kind.as_def_kind(); + let def_kind = similar_candidate.as_def_kind(); let an = self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id); + let similar_candidate_name = similar_candidate.name(); let msg = format!( "there is {an} {} `{}` with a similar name", self.tcx.def_kind_descr(def_kind, similar_candidate.def_id), - similar_candidate.name, + similar_candidate_name, ); // Methods are defined within the context of a struct and their first parameter // is always `self`, which represents the instance of the struct the method is @@ -1818,7 +1835,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty_args = self.infcx.fresh_args_for_item(span, similar_candidate.def_id); let fn_sig = tcx.fn_sig(similar_candidate.def_id).instantiate(tcx, ty_args); let fn_sig = self.instantiate_binder_with_fresh_vars(span, infer::FnCall, fn_sig); - if similar_candidate.fn_has_self_parameter { + if similar_candidate.is_method() { if let Some(args) = args && fn_sig.inputs()[1..].len() == args.len() { @@ -1827,7 +1844,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestion_verbose( span, msg, - similar_candidate.name, + similar_candidate_name, Applicability::MaybeIncorrect, ); } else { @@ -1849,7 +1866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestion_verbose( span, msg, - similar_candidate.name, + similar_candidate_name, Applicability::MaybeIncorrect, ); } else { @@ -1862,7 +1879,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestion_verbose( span, msg, - similar_candidate.name, + similar_candidate_name, Applicability::MaybeIncorrect, ); } else { @@ -1886,7 +1903,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::Confusables{symbols, ..} => symbols) && candidates.contains(&item_name.name) - && let ty::AssocKind::Fn = inherent_method.kind + && inherent_method.is_fn() { let args = ty::GenericArgs::identity_for_item(self.tcx, inherent_method.def_id) @@ -1902,6 +1919,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { infer::FnCall, fn_sig, ); + let name = inherent_method.name(); if let Some(ref args) = call_args && fn_sig.inputs()[1..] .iter() @@ -1911,20 +1929,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { err.span_suggestion_verbose( item_name.span, - format!("you might have meant to use `{}`", inherent_method.name), - inherent_method.name, + format!("you might have meant to use `{}`", name), + name, Applicability::MaybeIncorrect, ); - return Some(inherent_method.name); + return Some(name); } else if let None = call_args { err.span_note( self.tcx.def_span(inherent_method.def_id), - format!( - "you might have meant to use method `{}`", - inherent_method.name, - ), + format!("you might have meant to use method `{}`", name), ); - return Some(inherent_method.name); + return Some(name); } } } @@ -2100,8 +2115,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Only assoc fn with no receivers and only if // they are resolvable .filter(|item| { - matches!(item.kind, ty::AssocKind::Fn) - && !item.fn_has_self_parameter + matches!(item.kind, ty::AssocKind::Fn { has_self: false, .. }) && self .probe_for_name( Mode::Path, @@ -2245,7 +2259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let assoc = self.associated_value(assoc_did, item_name)?; - if assoc.kind != ty::AssocKind::Fn { + if !assoc.is_fn() { return None; } @@ -2355,7 +2369,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } else { - let call_expr = tcx.hir().expect_expr(tcx.parent_hir_id(expr.hir_id)); + let call_expr = tcx.hir_expect_expr(tcx.parent_hir_id(expr.hir_id)); if let Some(span) = call_expr.span.trim_start(item_name.span) { err.span_suggestion( @@ -2543,7 +2557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Path(QPath::Resolved(_, path)) => { // local binding if let hir::def::Res::Local(hir_id) = path.res { - let span = tcx.hir().span(hir_id); + let span = tcx.hir_span(hir_id); let filename = tcx.sess.source_map().span_to_filename(span); let parent_node = self.tcx.parent_hir_node(hir_id); @@ -2662,7 +2676,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mod_id, expr.hir_id, ) { - let call_expr = self.tcx.hir().expect_expr(self.tcx.parent_hir_id(expr.hir_id)); + let call_expr = self.tcx.hir_expect_expr(self.tcx.parent_hir_id(expr.hir_id)); let lang_items = self.tcx.lang_items(); let never_mention_traits = [ @@ -2739,7 +2753,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let SelfSource::MethodCall(expr) = source else { return; }; - let call_expr = tcx.hir().expect_expr(tcx.parent_hir_id(expr.hir_id)); + let call_expr = tcx.hir_expect_expr(tcx.parent_hir_id(expr.hir_id)); let ty::Adt(kind, args) = actual.kind() else { return; @@ -3192,7 +3206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If this method receives `&self`, then the provided // argument _should_ coerce, so it's valid to suggest // just changing the path. - && pick.item.fn_has_self_parameter + && pick.item.is_method() && let Some(self_ty) = self.tcx.fn_sig(pick.item.def_id).instantiate_identity().inputs().skip_binder().get(0) && self_ty.is_ref() @@ -3313,7 +3327,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let path_strings = candidates.iter().map(|trait_did| { format!( "{prefix}{}{postfix}\n", - with_crate_prefix!(self.tcx.def_path_str(*trait_did)), + with_no_visible_paths_if_doc_hidden!(with_crate_prefix!( + self.tcx.def_path_str(*trait_did) + )), ) }); @@ -3321,7 +3337,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let parent_did = parent_map.get(trait_did).unwrap(); format!( "{prefix}{}::*{postfix} // trait {}\n", - with_crate_prefix!(self.tcx.def_path_str(*parent_did)), + with_no_visible_paths_if_doc_hidden!(with_crate_prefix!( + self.tcx.def_path_str(*parent_did) + )), self.tcx.item_name(*trait_did), ) }); @@ -3540,7 +3558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || (("Pin::new" == *pre) && ((sym::as_ref == item_name.name) || !unpin)) || inputs_len.is_some_and(|inputs_len| { - pick.item.kind == ty::AssocKind::Fn + pick.item.is_fn() && self .tcx .fn_sig(pick.item.def_id) @@ -3598,7 +3616,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && pick.autoderefs == 0 // Check that the method of the same name that was found on the new `Pin` // receiver has the same number of arguments that appear in the user's code. - && inputs_len.is_some_and(|inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().skip_binder().inputs().len() == inputs_len) + && inputs_len.is_some_and(|inputs_len| pick.item.is_fn() && self.tcx.fn_sig(pick.item.def_id).skip_binder().skip_binder().inputs().len() == inputs_len) { let indent = self .tcx @@ -3736,7 +3754,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && self .associated_value(info.def_id, item_name) .filter(|item| { - if let ty::AssocKind::Fn = item.kind { + if item.is_fn() { let id = item .def_id .as_local() @@ -3748,7 +3766,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let self_first_arg = match method { hir::TraitFn::Required([ident, ..]) => { - ident.name == kw::SelfLower + matches!(ident, Some(Ident { name: kw::SelfLower, .. })) } hir::TraitFn::Provided(body_id) => { self.tcx.hir_body(*body_id).params.first().is_some_and( @@ -3943,8 +3961,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } Node::Item(hir::Item { - kind: hir::ItemKind::Trait(.., bounds, _), - ident, + kind: hir::ItemKind::Trait(_, _, ident, _, bounds, _), .. }) => { let (sp, sep, article) = if bounds.is_empty() { @@ -4260,17 +4277,17 @@ fn print_disambiguation_help<'tcx>( item: ty::AssocItem, ) -> Option { let trait_impl_type = trait_ref.self_ty().peel_refs(); - let trait_ref = if item.fn_has_self_parameter { + let trait_ref = if item.is_method() { trait_ref.print_only_trait_name().to_string() } else { format!("<{} as {}>", trait_ref.args[0], trait_ref.print_only_trait_name()) }; Some( - if matches!(item.kind, ty::AssocKind::Fn) + if item.is_fn() && let SelfSource::MethodCall(receiver) = source && let Some(args) = args { - let def_kind_descr = tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id); + let def_kind_descr = tcx.def_kind_descr(item.as_def_kind(), item.def_id); let item_name = item.ident(tcx); let first_input = tcx.fn_sig(item.def_id).instantiate_identity().skip_binder().inputs().get(0); @@ -4285,7 +4302,7 @@ fn print_disambiguation_help<'tcx>( let args = if let Some(first_arg_type) = first_arg_type && (first_arg_type == tcx.types.self_param || first_arg_type == trait_impl_type - || item.fn_has_self_parameter) + || item.is_method()) { Some(receiver) } else { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 1750c2af1c7ff..7f7921b66b572 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -4,18 +4,17 @@ use rustc_data_structures::packed::Pu128; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, struct_span_code_err}; use rustc_infer::traits::ObligationCauseCode; +use rustc_middle::bug; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; -use rustc_middle::{bug, span_bug}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; -use rustc_span::{Ident, Span, sym}; +use rustc_span::{Span, Symbol, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt}; -use rustc_type_ir::TyKind::*; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; @@ -25,24 +24,27 @@ use crate::Expectation; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Checks a `a = b` - pub(crate) fn check_expr_binop_assign( + pub(crate) fn check_expr_assign_op( &self, expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, + op: hir::AssignOp, lhs: &'tcx hir::Expr<'tcx>, rhs: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { let (lhs_ty, rhs_ty, return_ty) = - self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected); - - let ty = - if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) { - self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op); - self.tcx.types.unit - } else { - return_ty - }; + self.check_overloaded_binop(expr, lhs, rhs, Op::AssignOp(op), expected); + + let category = BinOpCategory::from(op.node); + let ty = if !lhs_ty.is_ty_var() + && !rhs_ty.is_ty_var() + && is_builtin_binop(lhs_ty, rhs_ty, category) + { + self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, category); + self.tcx.types.unit + } else { + return_ty + }; self.check_lhs_assignable(lhs, E0067, op.span, |err| { if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { @@ -50,7 +52,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lookup_op_method( (lhs, lhs_deref_ty), Some((rhs, rhs_ty)), - Op::Binary(op, IsAssign::Yes), + lang_item_for_binop(self.tcx, Op::AssignOp(op)), + op.span, expected, ) .is_ok() @@ -61,7 +64,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lookup_op_method( (lhs, lhs_ty), Some((rhs, rhs_ty)), - Op::Binary(op, IsAssign::Yes), + lang_item_for_binop(self.tcx, Op::AssignOp(op)), + op.span, expected, ) .is_err() @@ -99,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr.hir_id, expr, op, lhs_expr, rhs_expr ); - match BinOpCategory::from(op) { + match BinOpCategory::from(op.node) { BinOpCategory::Shortcircuit => { // && and || are a simple case. self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None); @@ -115,14 +119,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Otherwise, we always treat operators as if they are // overloaded. This is the way to be most flexible w/r/t // types that get inferred. - let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop( - expr, - lhs_expr, - rhs_expr, - op, - IsAssign::No, - expected, - ); + let (lhs_ty, rhs_ty, return_ty) = + self.check_overloaded_binop(expr, lhs_expr, rhs_expr, Op::BinOp(op), expected); // Supply type inference hints if relevant. Probably these // hints should be enforced during select as part of the @@ -136,16 +134,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // deduce that the result type should be `u32`, even // though we don't know yet what type 2 has and hence // can't pin this down to a specific impl. + let category = BinOpCategory::from(op.node); if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() - && is_builtin_binop(lhs_ty, rhs_ty, op) + && is_builtin_binop(lhs_ty, rhs_ty, category) { let builtin_return_ty = self.enforce_builtin_binop_types( lhs_expr.span, lhs_ty, rhs_expr.span, rhs_ty, - op, + category, ); self.demand_eqtype(expr.span, builtin_return_ty, return_ty); builtin_return_ty @@ -162,16 +161,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty: Ty<'tcx>, rhs_span: Span, rhs_ty: Ty<'tcx>, - op: hir::BinOp, + category: BinOpCategory, ) -> Ty<'tcx> { - debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op)); + debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, category)); // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. // (See https://github.com/rust-lang/rust/issues/57447.) let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty)); let tcx = self.tcx; - match BinOpCategory::from(op) { + match category { BinOpCategory::Shortcircuit => { self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty); self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty); @@ -202,17 +201,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, lhs_expr: &'tcx hir::Expr<'tcx>, rhs_expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, - is_assign: IsAssign, + op: Op, expected: Expectation<'tcx>, ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) { - debug!( - "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})", - expr.hir_id, op, is_assign - ); + debug!("check_overloaded_binop(expr.hir_id={}, op={:?})", expr.hir_id, op); - let lhs_ty = match is_assign { - IsAssign::No => { + let lhs_ty = match op { + Op::BinOp(_) => { // Find a suitable supertype of the LHS expression's type, by coercing to // a type variable, to pass as the `Self` to the trait, avoiding invariant // trait matching creating lifetime constraints that are too strict. @@ -222,7 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fresh_var = self.next_ty_var(lhs_expr.span); self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No) } - IsAssign::Yes => { + Op::AssignOp(_) => { // rust-lang/rust#52126: We have to use strict // equivalence on the LHS of an assign-op like `+=`; // overwritten or mutably-borrowed places cannot be @@ -239,11 +234,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // us do better coercions than we would be able to do otherwise, // particularly for things like `String + &String`. let rhs_ty_var = self.next_ty_var(rhs_expr.span); - let result = self.lookup_op_method( (lhs_expr, lhs_ty), Some((rhs_expr, rhs_ty_var)), - Op::Binary(op, is_assign), + lang_item_for_binop(self.tcx, op), + op.span(), expected, ); @@ -253,15 +248,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty_var, Some(lhs_expr), |err, ty| { - self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr, op); + if let Op::BinOp(binop) = op + && binop.node == hir::BinOpKind::Eq + { + self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr); + } }, ); let rhs_ty = self.resolve_vars_with_obligations(rhs_ty); let return_ty = match result { Ok(method) => { - let by_ref_binop = !op.node.is_by_value(); - if is_assign == IsAssign::Yes || by_ref_binop { + let by_ref_binop = !op.is_by_value(); + if matches!(op, Op::AssignOp(_)) || by_ref_binop { if let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() { let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes); let autoref = Adjustment { @@ -302,32 +301,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_misc_error(self.tcx) } Err(errors) => { - let (_, trait_def_id) = - lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span); + let (_, trait_def_id) = lang_item_for_binop(self.tcx, op); let missing_trait = trait_def_id .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id))); let mut path = None; let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path); let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path); - let (mut err, output_def_id) = match is_assign { - IsAssign::Yes => { + let (mut err, output_def_id) = match op { + Op::AssignOp(assign_op) => { + let s = assign_op.node.as_str(); let mut err = struct_span_code_err!( self.dcx(), expr.span, E0368, - "binary assignment operation `{}=` cannot be applied to type `{}`", - op.node.as_str(), + "binary assignment operation `{}` cannot be applied to type `{}`", + s, lhs_ty_str, ); err.span_label( lhs_expr.span, - format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty_str), + format!("cannot use `{}` on type `{}`", s, lhs_ty_str), ); self.note_unmet_impls_on_type(&mut err, errors, false); (err, None) } - IsAssign::No => { - let message = match op.node { + Op::BinOp(bin_op) => { + let message = match bin_op.node { hir::BinOpKind::Add => { format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`") } @@ -363,8 +362,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => format!( "binary operation `{}` cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty_str, + bin_op.node.as_str(), + lhs_ty_str ), }; let output_def_id = trait_def_id.and_then(|def_id| { @@ -372,12 +371,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .associated_item_def_ids(def_id) .iter() .find(|item_def_id| { - self.tcx.associated_item(*item_def_id).name == sym::Output + self.tcx.associated_item(*item_def_id).name() == sym::Output }) .cloned() }); let mut err = - struct_span_code_err!(self.dcx(), op.span, E0369, "{message}"); + struct_span_code_err!(self.dcx(), bin_op.span, E0369, "{message}"); if !lhs_expr.span.eq(&rhs_expr.span) { err.span_label(lhs_expr.span, lhs_ty_str.clone()); err.span_label(rhs_expr.span, rhs_ty_str); @@ -410,18 +409,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lookup_op_method( (lhs_expr, lhs_deref_ty), Some((rhs_expr, rhs_ty)), - Op::Binary(op, is_assign), + lang_item_for_binop(self.tcx, op), + op.span(), expected, ) .is_ok() { let msg = format!( - "`{}{}` can be used on `{}` if you dereference the left-hand side", - op.node.as_str(), - match is_assign { - IsAssign::Yes => "=", - IsAssign::No => "", - }, + "`{}` can be used on `{}` if you dereference the left-hand side", + op.as_str(), self.tcx.short_string(lhs_deref_ty, err.long_ty_path()), ); err.span_suggestion_verbose( @@ -443,14 +439,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lookup_op_method( (lhs_expr, lhs_adjusted_ty), Some((rhs_expr, rhs_adjusted_ty)), - Op::Binary(op, is_assign), + lang_item_for_binop(self.tcx, op), + op.span(), expected, ) .is_ok() { let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path()); let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path()); - let op = op.node.as_str(); + let op = op.as_str(); err.note(format!("an implementation for `{lhs} {op} {rhs}` exists")); if let Some(lhs_new_mutbl) = lhs_new_mutbl @@ -500,7 +497,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.lookup_op_method( (lhs_expr, lhs_ty), Some((rhs_expr, rhs_ty)), - Op::Binary(op, is_assign), + lang_item_for_binop(self.tcx, op), + op.span(), expected, ) .is_ok() @@ -512,14 +510,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest // `a += b` => `*a += b` if a is a mut ref. - if !op.span.can_be_used_for_suggestions() { + if !op.span().can_be_used_for_suggestions() { // Suppress suggestions when lhs and rhs are not in the same span as the error - } else if is_assign == IsAssign::Yes + } else if let Op::AssignOp(_) = op && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { suggest_deref_binop(&mut err, lhs_deref_ty); - } else if is_assign == IsAssign::No - && let Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind() + } else if let Op::BinOp(_) = op + && let ty::Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind() { if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty) { suggest_deref_binop(&mut err, *lhs_deref_ty); @@ -536,7 +534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, ); - if let Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() { + if let ty::Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() { let rhs_inv_mutbl = mutbl.invert(); let rhs_inv_mutbl_ty = Ty::new_ref(self.tcx, *region, *rhs_deref_ty, rhs_inv_mutbl); @@ -573,10 +571,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(missing_trait) = missing_trait { - if op.node == hir::BinOpKind::Add - && self.check_str_addition( - lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op, - ) + if matches!( + op, + Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. }) + | Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. }) + ) && self + .check_str_addition(lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, op) { // This has nothing here because it means we did string // concatenation (e.g., "Hello " + "World!"). This means @@ -593,7 +593,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lookup_op_method( (lhs_expr, lhs_ty), Some((rhs_expr, rhs_ty)), - Op::Binary(op, is_assign), + lang_item_for_binop(self.tcx, op), + op.span(), expected, ) .unwrap_err(); @@ -643,9 +644,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Suggest using `add`, `offset` or `offset_from` for pointer - {integer}, // pointer + {integer} or pointer - pointer. - if op.span.can_be_used_for_suggestions() { - match op.node { - hir::BinOpKind::Add if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() => { + if op.span().can_be_used_for_suggestions() { + match op { + Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. }) + if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() => + { err.multipart_suggestion( "consider using `wrapping_add` or `add` for pointer + {integer}", vec![ @@ -658,7 +661,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - hir::BinOpKind::Sub => { + Op::BinOp(Spanned { node: hir::BinOpKind::Sub, .. }) => { if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() { err.multipart_suggestion( "consider using `wrapping_sub` or `sub` for \ @@ -694,6 +697,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + let lhs_name_str = match lhs_expr.kind { + hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => { + path.segments.last().map_or("_".to_string(), |s| s.ident.to_string()) + } + _ => self + .tcx + .sess + .source_map() + .span_to_snippet(lhs_expr.span) + .unwrap_or("_".to_string()), + }; + + if op.span().can_be_used_for_suggestions() { + match op { + Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. }) + if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() => + { + err.multipart_suggestion( + "consider using `add` or `wrapping_add` to do pointer arithmetic", + vec![ + (lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)), + ( + lhs_expr.span.between(rhs_expr.span), + ".wrapping_add(".to_owned(), + ), + (rhs_expr.span.shrink_to_hi(), ")".to_owned()), + ], + Applicability::MaybeIncorrect, + ); + } + Op::AssignOp(Spanned { node: hir::AssignOpKind::SubAssign, .. }) => { + if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() { + err.multipart_suggestion( + "consider using `sub` or `wrapping_sub` to do pointer arithmetic", + vec![ + (lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)), + ( + lhs_expr.span.between(rhs_expr.span), + ".wrapping_sub(".to_owned(), + + ), + (rhs_expr.span.shrink_to_hi(), ")".to_owned()), + ], + Applicability::MaybeIncorrect, + ); + } + } + _ => {} + } + } + let reported = err.emit(); Ty::new_error(self.tcx, reported) } @@ -714,8 +768,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty: Ty<'tcx>, rhs_ty: Ty<'tcx>, err: &mut Diag<'_>, - is_assign: IsAssign, - op: hir::BinOp, + op: Op, ) -> bool { let str_concat_note = "string concatenation requires an owned `String` on the left"; let rm_borrow_msg = "remove the borrow to obtain an owned `String`"; @@ -726,16 +779,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |ty: Ty<'tcx>| ty.ty_adt_def().is_some_and(|ty_def| Some(ty_def.did()) == string_type); 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 + (&ty::Ref(_, l_ty, _), &ty::Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str + if (*l_ty.kind() == ty::Str || is_std_string(l_ty)) + && (*r_ty.kind() == ty::Str || is_std_string(r_ty) || matches!( - r_ty.kind(), Ref(_, inner_ty, _) if *inner_ty.kind() == Str + r_ty.kind(), ty::Ref(_, inner_ty, _) if *inner_ty.kind() == ty::Str )) => { - if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str` - err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings"); + if let Op::BinOp(_) = op { // Do not supply this message if `&str += &str` + err.span_label( + op.span(), + "`+` cannot be used to concatenate two `&str` strings" + ); err.note(str_concat_note); if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { err.span_suggestion_verbose( @@ -755,15 +811,15 @@ 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) => + (&ty::Ref(_, l_ty, _), &ty::Adt(..)) // Handle `&str` & `&String` + `String` + if (*l_ty.kind() == ty::Str || is_std_string(l_ty)) && is_std_string(rhs_ty) => { err.span_label( - op.span, + op.span(), "`+` cannot be used to concatenate a `&str` with a `String`", ); - match is_assign { - IsAssign::No => { + match op { + Op::BinOp(_) => { let sugg_msg; let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { sugg_msg = "remove the borrow on the left and add one on the right"; @@ -782,7 +838,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } - IsAssign::Yes => { + Op::AssignOp(_) => { err.note(str_concat_note); } } @@ -800,7 +856,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, ) -> Ty<'tcx> { assert!(op.is_by_value()); - match self.lookup_op_method((ex, operand_ty), None, Op::Unary(op, ex.span), expected) { + match self.lookup_op_method( + (ex, operand_ty), + None, + lang_item_for_unop(self.tcx, op), + ex.span, + expected, + ) { Ok(method) => { self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method); method.sig.output() @@ -847,7 +909,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } else { match actual.kind() { - Uint(_) if op == hir::UnOp::Neg => { + ty::Uint(_) if op == hir::UnOp::Neg => { err.note("unsigned values cannot be negated"); if let hir::ExprKind::Unary( @@ -881,8 +943,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } - Str | Never | Char | Tuple(_) | Array(_, _) => {} - Ref(_, lty, _) if *lty.kind() == Str => {} + ty::Str | ty::Never | ty::Char | ty::Tuple(_) | ty::Array(_, _) => {} + ty::Ref(_, lty, _) if *lty.kind() == ty::Str => {} _ => { self.note_unmet_impls_on_type(&mut err, errors, true); } @@ -899,24 +961,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, (lhs_expr, lhs_ty): (&'tcx hir::Expr<'tcx>, Ty<'tcx>), opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>, - op: Op, + (opname, trait_did): (Symbol, Option), + span: Span, expected: Expectation<'tcx>, ) -> Result, Vec>> { - let span = match op { - Op::Binary(op, _) => op.span, - Op::Unary(_, span) => span, - }; - let (opname, Some(trait_did)) = lang_item_for_op(self.tcx, op, span) else { + let Some(trait_did) = trait_did else { // Bail if the operator trait is not defined. return Err(vec![]); }; debug!( - "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})", - lhs_ty, op, opname, trait_did + "lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})", + lhs_ty, opname, trait_did ); - let opname = Ident::with_dummy_span(opname); let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip(); let cause = self.cause( span, @@ -931,7 +989,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let method = - self.lookup_method_in_trait(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty); + self.lookup_method_for_operator(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty); match method { Some(ok) => { let method = self.register_infer_ok_obligations(ok); @@ -981,37 +1039,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } -fn lang_item_for_op( - tcx: TyCtxt<'_>, - op: Op, - span: Span, -) -> (rustc_span::Symbol, Option) { +fn lang_item_for_binop(tcx: TyCtxt<'_>, op: Op) -> (Symbol, Option) { let lang = tcx.lang_items(); - if let Op::Binary(op, IsAssign::Yes) = op { - match op.node { - hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()), - hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()), - hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()), - hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()), - hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()), - hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()), - hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()), - hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()), - hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()), - hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()), - hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt - | hir::BinOpKind::Eq - | hir::BinOpKind::Ne - | hir::BinOpKind::And - | hir::BinOpKind::Or => { - span_bug!(span, "impossible assignment operation: {}=", op.node.as_str()) - } - } - } else if let Op::Binary(op, IsAssign::No) = op { - match op.node { + match op { + Op::AssignOp(op) => match op.node { + hir::AssignOpKind::AddAssign => (sym::add_assign, lang.add_assign_trait()), + hir::AssignOpKind::SubAssign => (sym::sub_assign, lang.sub_assign_trait()), + hir::AssignOpKind::MulAssign => (sym::mul_assign, lang.mul_assign_trait()), + hir::AssignOpKind::DivAssign => (sym::div_assign, lang.div_assign_trait()), + hir::AssignOpKind::RemAssign => (sym::rem_assign, lang.rem_assign_trait()), + hir::AssignOpKind::BitXorAssign => (sym::bitxor_assign, lang.bitxor_assign_trait()), + hir::AssignOpKind::BitAndAssign => (sym::bitand_assign, lang.bitand_assign_trait()), + hir::AssignOpKind::BitOrAssign => (sym::bitor_assign, lang.bitor_assign_trait()), + hir::AssignOpKind::ShlAssign => (sym::shl_assign, lang.shl_assign_trait()), + hir::AssignOpKind::ShrAssign => (sym::shr_assign, lang.shr_assign_trait()), + }, + Op::BinOp(op) => match op.node { hir::BinOpKind::Add => (sym::add, lang.add_trait()), hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()), hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()), @@ -1029,20 +1072,24 @@ fn lang_item_for_op( hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()), hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()), hir::BinOpKind::And | hir::BinOpKind::Or => { - span_bug!(span, "&& and || are not overloadable") + bug!("&& and || are not overloadable") } - } - } else if let Op::Unary(hir::UnOp::Not, _) = op { - (sym::not, lang.not_trait()) - } else if let Op::Unary(hir::UnOp::Neg, _) = op { - (sym::neg, lang.neg_trait()) - } else { - bug!("lookup_op_method: op not supported: {:?}", op) + }, + } +} + +fn lang_item_for_unop(tcx: TyCtxt<'_>, op: hir::UnOp) -> (Symbol, Option) { + let lang = tcx.lang_items(); + match op { + hir::UnOp::Not => (sym::not, lang.not_trait()), + hir::UnOp::Neg => (sym::neg, lang.neg_trait()), + hir::UnOp::Deref => bug!("Deref is not overloadable"), } } // Binary operator categories. These categories summarize the behavior // with respect to the builtin operations supported. +#[derive(Clone, Copy)] enum BinOpCategory { /// &&, || -- cannot be overridden Shortcircuit, @@ -1064,44 +1111,58 @@ enum BinOpCategory { Comparison, } -impl BinOpCategory { - fn from(op: hir::BinOp) -> BinOpCategory { - match op.node { - hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift, - - hir::BinOpKind::Add - | hir::BinOpKind::Sub - | hir::BinOpKind::Mul - | hir::BinOpKind::Div - | hir::BinOpKind::Rem => BinOpCategory::Math, - - hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => { - BinOpCategory::Bitwise - } - - hir::BinOpKind::Eq - | hir::BinOpKind::Ne - | hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt => BinOpCategory::Comparison, +impl From for BinOpCategory { + fn from(op: hir::BinOpKind) -> BinOpCategory { + use hir::BinOpKind::*; + match op { + Shl | Shr => BinOpCategory::Shift, + Add | Sub | Mul | Div | Rem => BinOpCategory::Math, + BitXor | BitAnd | BitOr => BinOpCategory::Bitwise, + Eq | Ne | Lt | Le | Ge | Gt => BinOpCategory::Comparison, + And | Or => BinOpCategory::Shortcircuit, + } + } +} - hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit, +impl From for BinOpCategory { + fn from(op: hir::AssignOpKind) -> BinOpCategory { + use hir::AssignOpKind::*; + match op { + ShlAssign | ShrAssign => BinOpCategory::Shift, + AddAssign | SubAssign | MulAssign | DivAssign | RemAssign => BinOpCategory::Math, + BitXorAssign | BitAndAssign | BitOrAssign => BinOpCategory::Bitwise, } } } -/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`) +/// An assignment op (e.g. `a += b`), or a binary op (e.g. `a + b`). #[derive(Clone, Copy, Debug, PartialEq)] -enum IsAssign { - No, - Yes, +enum Op { + BinOp(hir::BinOp), + AssignOp(hir::AssignOp), } -#[derive(Clone, Copy, Debug)] -enum Op { - Binary(hir::BinOp, IsAssign), - Unary(hir::UnOp, Span), +impl Op { + fn span(&self) -> Span { + match self { + Op::BinOp(op) => op.span, + Op::AssignOp(op) => op.span, + } + } + + fn as_str(&self) -> &'static str { + match self { + Op::BinOp(op) => op.node.as_str(), + Op::AssignOp(op) => op.node.as_str(), + } + } + + fn is_by_value(&self) -> bool { + match self { + Op::BinOp(op) => op.node.is_by_value(), + Op::AssignOp(op) => op.node.is_by_value(), + } + } } /// Dereferences a single level of immutable referencing. @@ -1128,27 +1189,24 @@ fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> { /// Reason #2 is the killer. I tried for a while to always use /// overloaded logic and just check the types in constants/codegen after /// the fact, and it worked fine, except for SIMD types. -nmatsakis -fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool { +fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) -> bool { // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. // (See https://github.com/rust-lang/rust/issues/57447.) let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs)); - match BinOpCategory::from(op) { + match category.into() { BinOpCategory::Shortcircuit => true, - BinOpCategory::Shift => { lhs.references_error() || rhs.references_error() || lhs.is_integral() && rhs.is_integral() } - BinOpCategory::Math => { lhs.references_error() || rhs.references_error() || lhs.is_integral() && rhs.is_integral() || lhs.is_floating_point() && rhs.is_floating_point() } - BinOpCategory::Bitwise => { lhs.references_error() || rhs.references_error() @@ -1156,7 +1214,6 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool || lhs.is_floating_point() && rhs.is_floating_point() || lhs.is_bool() && rhs.is_bool() } - BinOpCategory::Comparison => { lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar() } diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs new file mode 100644 index 0000000000000..e0224f8c6e1b4 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -0,0 +1,26 @@ +use super::FnCtxt; +impl<'tcx> FnCtxt<'_, 'tcx> { + /// We may in theory add further uses of an opaque after cloning the opaque + /// types storage during writeback when computing the defining uses. + /// + /// Silently ignoring them is dangerous and could result in ICE or even in + /// unsoundness, so we make sure we catch such cases here. There's currently + /// no known code where this actually happens, even with the new solver which + /// does normalize types in writeback after cloning the opaque type storage. + /// + /// FIXME(@lcnr): I believe this should be possible in theory and would like + /// an actual test here. After playing around with this for an hour, I wasn't + /// able to do anything which didn't already try to normalize the opaque before + /// then, either allowing compilation to succeed or causing an ambiguity error. + pub(super) fn detect_opaque_types_added_during_writeback(&self) { + let num_entries = self.checked_opaque_types_storage_entries.take().unwrap(); + for (key, hidden_type) in + self.inner.borrow_mut().opaque_types().opaque_types_added_since(num_entries) + { + let opaque_type_string = self.tcx.def_path_str(key.def_id); + let msg = format!("unexpected cyclic definition of `{opaque_type_string}`"); + self.dcx().span_delayed_bug(hidden_type.span, msg); + } + let _ = self.take_opaque_types(); + } +} diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 19ae3e3899c93..17d48184dd971 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -9,11 +9,13 @@ use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err, }; use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def_id::DefId; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{ self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatExpr, PatExprKind, PatKind, expr_needs_parens, }; +use rustc_hir_analysis::autoderef::report_autoderef_recursion_limit_error; use rustc_infer::infer; use rustc_middle::traits::PatternOriginExpr; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -29,11 +31,12 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use tracing::{debug, instrument, trace}; use ty::VariantDef; +use ty::adjustment::{PatAdjust, PatAdjustment}; use super::report_unexpected_variant_res; use crate::expectation::Expectation; use crate::gather_locals::DeclOrigin; -use crate::{FnCtxt, LoweredTy, errors}; +use crate::{FnCtxt, errors}; const CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ @@ -87,10 +90,10 @@ struct TopInfo<'tcx> { } #[derive(Copy, Clone)] -struct PatInfo<'a, 'tcx> { +struct PatInfo<'tcx> { binding_mode: ByRef, max_ref_mutbl: MutblCap, - top_info: &'a TopInfo<'tcx>, + top_info: TopInfo<'tcx>, decl_origin: Option>, /// The depth of current pattern @@ -161,15 +164,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Mode for adjusting the expected type and binding mode. #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum AdjustMode { - /// Peel off all immediate reference types. - Peel, - /// Reset binding mode to the initial mode. - /// Used for destructuring assignment, where we don't want any match ergonomics. - Reset, + /// Peel off all immediate reference types. If the `deref_patterns` feature is enabled, this + /// also peels smart pointer ADTs. + Peel { kind: PeelKind }, /// Pass on the input binding mode and expected type. Pass, } +/// Restrictions on what types to peel when adjusting the expected type and binding mode. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum PeelKind { + /// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference + /// any number of `&`/`&mut` references, plus a single smart pointer. + ExplicitDerefPat, + /// Implicitly peel references, and if `deref_patterns` is enabled, smart pointer ADTs. + Implicit { + /// The ADT the pattern is a constructor for, if applicable, so that we don't peel it. See + /// [`ResolvedPat`] for more information. + until_adt: Option, + /// The number of references at the head of the pattern's type, so we can leave that many + /// untouched. This is `1` for string literals, and `0` for most patterns. + pat_ref_layers: usize, + }, +} + +impl AdjustMode { + const fn peel_until_adt(opt_adt_def: Option) -> AdjustMode { + AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def, pat_ref_layers: 0 } } + } + const fn peel_all() -> AdjustMode { + AdjustMode::peel_until_adt(None) + } +} + /// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference. /// Normally, the borrow checker enforces this, but for (currently experimental) match ergonomics, /// we track this when typing patterns for two purposes: @@ -245,6 +272,47 @@ enum InheritedRefMatchRule { }, } +/// When checking patterns containing paths, we need to know the path's resolution to determine +/// whether to apply match ergonomics and implicitly dereference the scrutinee. For instance, when +/// the `deref_patterns` feature is enabled and we're matching against a scrutinee of type +/// `Cow<'a, Option>`, we insert an implicit dereference to allow the pattern `Some(_)` to type, +/// but we must not dereference it when checking the pattern `Cow::Borrowed(_)`. +/// +/// `ResolvedPat` contains the information from resolution needed to determine match ergonomics +/// adjustments, and to finish checking the pattern once we know its adjusted type. +#[derive(Clone, Copy, Debug)] +struct ResolvedPat<'tcx> { + /// The type of the pattern, to be checked against the type of the scrutinee after peeling. This + /// is also used to avoid peeling the scrutinee's constructors (see the `Cow` example above). + ty: Ty<'tcx>, + kind: ResolvedPatKind<'tcx>, +} + +#[derive(Clone, Copy, Debug)] +enum ResolvedPatKind<'tcx> { + Path { res: Res, pat_res: Res, segments: &'tcx [hir::PathSegment<'tcx>] }, + Struct { variant: &'tcx VariantDef }, + TupleStruct { res: Res, variant: &'tcx VariantDef }, +} + +impl<'tcx> ResolvedPat<'tcx> { + fn adjust_mode(&self) -> AdjustMode { + if let ResolvedPatKind::Path { res, .. } = self.kind + && matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _)) + { + // 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. + AdjustMode::Pass + } else { + // The remaining possible resolutions for path, struct, and tuple struct patterns are + // ADT constructors. As such, we may peel references freely, but we must not peel the + // ADT itself from the scrutinee if it's a smart pointer. + AdjustMode::peel_until_adt(self.ty.ty_adt_def().map(|adt| adt.did())) + } + } +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Experimental pattern feature: after matching against a shared reference, do we limit the /// default binding mode in subpatterns to be `ref` when it would otherwise be `ref mut`? @@ -303,11 +371,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { origin_expr: Option<&'tcx hir::Expr<'tcx>>, decl_origin: Option>, ) { - let info = TopInfo { expected, origin_expr, span, hir_id: pat.hir_id }; + let top_info = TopInfo { expected, origin_expr, span, hir_id: pat.hir_id }; let pat_info = PatInfo { binding_mode: ByRef::No, max_ref_mutbl: MutblCap::Mut, - top_info: &info, + top_info, decl_origin, current_depth: 0, }; @@ -320,78 +388,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Outside of this module, `check_pat_top` should always be used. /// Conversely, inside this module, `check_pat_top` should never be used. #[instrument(level = "debug", skip(self, pat_info))] - fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'_, 'tcx>) { - let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info; - - let path_res = match pat.kind { + fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx>) { + // For patterns containing paths, we need the path's resolution to determine whether to + // implicitly dereference the scrutinee before matching. + let opt_path_res = match pat.kind { PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => { - Some(self.resolve_ty_and_res_fully_qualified_call(qpath, *hir_id, *span)) + Some(self.resolve_pat_path(*hir_id, *span, qpath)) } + PatKind::Struct(ref qpath, ..) => Some(self.resolve_pat_struct(pat, qpath)), + PatKind::TupleStruct(ref qpath, ..) => Some(self.resolve_pat_tuple_struct(pat, qpath)), _ => None, }; - let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); - let (expected, binding_mode, max_ref_mutbl) = - self.calc_default_binding_mode(pat, expected, binding_mode, adjust_mode, max_ref_mutbl); - let pat_info = PatInfo { - binding_mode, - max_ref_mutbl, - top_info: ti, - decl_origin: pat_info.decl_origin, - current_depth: current_depth + 1, - }; - - let ty = match pat.kind { - PatKind::Wild | PatKind::Err(_) => expected, - // We allow any type here; we ensure that the type is uninhabited during match checking. - PatKind::Never => expected, - PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => { - let ty = self.check_pat_path( - *hir_id, - pat.hir_id, - *span, - qpath, - path_res.unwrap(), - expected, - ti, - ); - self.write_ty(*hir_id, ty); - ty - } - PatKind::Expr(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, ident, sub) => { - self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info) - } - PatKind::TupleStruct(ref qpath, subpats, ddpos) => { - self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info) - } - PatKind::Struct(ref qpath, fields, has_rest_pat) => { - self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info) - } - PatKind::Guard(pat, cond) => { - self.check_pat(pat, expected, pat_info); - self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {}); - expected - } - PatKind::Or(pats) => { - for pat in pats { - self.check_pat(pat, expected, pat_info); - } - expected - } - PatKind::Tuple(elements, ddpos) => { - self.check_pat_tuple(pat.span, elements, ddpos, expected, pat_info) - } - PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info), - PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info), - PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info), - PatKind::Slice(before, slice, after) => { - self.check_pat_slice(pat.span, before, slice, after, expected, pat_info) - } - }; - + let adjust_mode = self.calc_adjust_mode(pat, opt_path_res); + let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info); self.write_ty(pat.hir_id, ty); + // If we implicitly inserted overloaded dereferences before matching, check the pattern to + // see if the dereferenced types need `DerefMut` bounds. + if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id) + && derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref) + { + self.register_deref_mut_bounds_if_needed( + pat.span, + pat, + derefed_tys.iter().filter_map(|adjust| match adjust.kind { + PatAdjust::OverloadedDeref => Some(adjust.source), + PatAdjust::BuiltinDeref => None, + }), + ); + } + // (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. @@ -435,77 +461,257 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, } - /// 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( + // Helper to avoid resolving the same path pattern several times. + fn check_pat_inner( &self, pat: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - def_br: ByRef, + opt_path_res: Option, ErrorGuaranteed>>, adjust_mode: AdjustMode, - max_ref_mutbl: MutblCap, - ) -> (Ty<'tcx>, ByRef, MutblCap) { + expected: Ty<'tcx>, + pat_info: PatInfo<'tcx>, + ) -> Ty<'tcx> { #[cfg(debug_assertions)] - if def_br == ByRef::Yes(Mutability::Mut) - && max_ref_mutbl != MutblCap::Mut + if pat_info.binding_mode == ByRef::Yes(Mutability::Mut) + && pat_info.max_ref_mutbl != MutblCap::Mut && self.downgrade_mut_inside_shared() { span_bug!(pat.span, "Pattern mutability cap violated!"); } - match adjust_mode { - AdjustMode::Pass => (expected, def_br, max_ref_mutbl), - AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut), - AdjustMode::Peel => self.peel_off_references(pat, expected, def_br, max_ref_mutbl), + + // Resolve type if needed. + let expected = if let AdjustMode::Peel { .. } = adjust_mode + && pat.default_binding_modes + { + self.try_structurally_resolve_type(pat.span, expected) + } else { + expected + }; + let old_pat_info = pat_info; + let pat_info = PatInfo { current_depth: old_pat_info.current_depth + 1, ..old_pat_info }; + + match pat.kind { + // Peel off a `&` or `&mut` from the scrutinee type. See the examples in + // `tests/ui/rfcs/rfc-2005-default-binding-mode`. + _ if let AdjustMode::Peel { kind: peel_kind } = adjust_mode + && pat.default_binding_modes + && let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() + && self.should_peel_ref(peel_kind, expected) => + { + debug!("inspecting {:?}", expected); + + debug!("current discriminant is Ref, inserting implicit deref"); + // Preserve the reference type. We'll need it later during THIR lowering. + self.typeck_results + .borrow_mut() + .pat_adjustments_mut() + .entry(pat.hir_id) + .or_default() + .push(PatAdjustment { kind: PatAdjust::BuiltinDeref, source: expected }); + + let mut binding_mode = ByRef::Yes(match pat_info.binding_mode { + // If default binding mode is by value, make it `ref` or `ref mut` + // (depending on whether we observe `&` or `&mut`). + ByRef::No | + // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). + ByRef::Yes(Mutability::Mut) => inner_mutability, + // Once a `ref`, always a `ref`. + // This is because a `& &mut` cannot mutate the underlying value. + ByRef::Yes(Mutability::Not) => Mutability::Not, + }); + + let mut max_ref_mutbl = pat_info.max_ref_mutbl; + if self.downgrade_mut_inside_shared() { + binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl()); + } + if binding_mode == ByRef::Yes(Mutability::Not) { + max_ref_mutbl = MutblCap::Not; + } + debug!("default binding mode is now {:?}", binding_mode); + + // Use the old pat info to keep `current_depth` to its old value. + let new_pat_info = PatInfo { binding_mode, max_ref_mutbl, ..old_pat_info }; + // Recurse with the new expected type. + self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info) + } + // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the + // examples in `tests/ui/pattern/deref_patterns/`. + _ if self.tcx.features().deref_patterns() + && let AdjustMode::Peel { kind: peel_kind } = adjust_mode + && pat.default_binding_modes + && self.should_peel_smart_pointer(peel_kind, expected) => + { + debug!("scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref"); + // The scrutinee is a smart pointer; implicitly dereference it. This adds a + // requirement that `expected: DerefPure`. + let mut inner_ty = self.deref_pat_target(pat.span, expected); + // Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any + // `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`. + + let mut typeck_results = self.typeck_results.borrow_mut(); + let mut pat_adjustments_table = typeck_results.pat_adjustments_mut(); + let pat_adjustments = pat_adjustments_table.entry(pat.hir_id).or_default(); + // We may reach the recursion limit if a user matches on a type `T` satisfying + // `T: Deref`; error gracefully in this case. + // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move + // this check out of this branch. Alternatively, this loop could be implemented with + // autoderef and this check removed. For now though, don't break code compiling on + // stable with lots of `&`s and a low recursion limit, if anyone's done that. + if self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) { + // Preserve the smart pointer type for THIR lowering and closure upvar analysis. + pat_adjustments + .push(PatAdjustment { kind: PatAdjust::OverloadedDeref, source: expected }); + } else { + let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected); + inner_ty = Ty::new_error(self.tcx, guar); + } + drop(typeck_results); + + // Recurse, using the old pat info to keep `current_depth` to its old value. + // Peeling smart pointers does not update the default binding mode. + self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, old_pat_info) + } + PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected, + // We allow any type here; we ensure that the type is uninhabited during match checking. + PatKind::Never => expected, + PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), hir_id, .. }) => { + let ty = match opt_path_res.unwrap() { + Ok(ref pr) => { + self.check_pat_path(pat.hir_id, pat.span, pr, expected, &pat_info.top_info) + } + Err(guar) => Ty::new_error(self.tcx, guar), + }; + self.write_ty(*hir_id, ty); + ty + } + PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, &pat_info.top_info), + PatKind::Range(lhs, rhs, _) => { + self.check_pat_range(pat.span, lhs, rhs, expected, &pat_info.top_info) + } + PatKind::Binding(ba, var_id, ident, sub) => { + self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info) + } + PatKind::TupleStruct(ref qpath, subpats, ddpos) => match opt_path_res.unwrap() { + Ok(ResolvedPat { ty, kind: ResolvedPatKind::TupleStruct { res, variant } }) => self + .check_pat_tuple_struct( + pat, qpath, subpats, ddpos, res, ty, variant, expected, pat_info, + ), + Err(guar) => { + let ty_err = Ty::new_error(self.tcx, guar); + for subpat in subpats { + self.check_pat(subpat, ty_err, pat_info); + } + ty_err + } + Ok(pr) => span_bug!(pat.span, "tuple struct pattern resolved to {pr:?}"), + }, + PatKind::Struct(_, fields, has_rest_pat) => match opt_path_res.unwrap() { + Ok(ResolvedPat { ty, kind: ResolvedPatKind::Struct { variant } }) => self + .check_pat_struct(pat, fields, has_rest_pat, ty, variant, expected, pat_info), + Err(guar) => { + let ty_err = Ty::new_error(self.tcx, guar); + for field in fields { + self.check_pat(field.pat, ty_err, pat_info); + } + ty_err + } + Ok(pr) => span_bug!(pat.span, "struct pattern resolved to {pr:?}"), + }, + PatKind::Guard(pat, cond) => { + self.check_pat(pat, expected, pat_info); + self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {}); + expected + } + PatKind::Or(pats) => { + for pat in pats { + self.check_pat(pat, expected, pat_info); + } + expected + } + PatKind::Tuple(elements, ddpos) => { + self.check_pat_tuple(pat.span, elements, ddpos, expected, pat_info) + } + PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info), + PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info), + PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info), + PatKind::Slice(before, slice, after) => { + self.check_pat_slice(pat.span, before, slice, after, expected, pat_info) + } } } /// 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 { - // When we perform destructuring assignment, we disable default match bindings, which are - // unintuitive in this context. - if !pat.default_binding_modes { - return AdjustMode::Reset; - } + /// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`. + fn calc_adjust_mode( + &self, + pat: &'tcx Pat<'tcx>, + opt_path_res: Option, ErrorGuaranteed>>, + ) -> 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::Tuple(..) | PatKind::Range(..) | PatKind::Slice(..) => AdjustMode::peel_all(), + // When checking an explicit deref pattern, only peel reference types. + // FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box + // patterns may want `PeelKind::Implicit`, stopping on encountering a box. + PatKind::Box(_) | PatKind::Deref(_) => { + AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat } + } + // A never pattern behaves somewhat like a literal or unit variant. + PatKind::Never => AdjustMode::peel_all(), + // For patterns with paths, how we peel the scrutinee depends on the path's resolution. PatKind::Struct(..) | PatKind::TupleStruct(..) - | PatKind::Tuple(..) - | PatKind::Box(_) - | PatKind::Deref(_) - | PatKind::Range(..) - | PatKind::Slice(..) => AdjustMode::Peel, - // A never pattern behaves somewhat like a literal or unit variant. - PatKind::Never => AdjustMode::Peel, - PatKind::Expr(PatExpr { kind: PatExprKind::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 expressiveness. - _ => AdjustMode::Peel, - }, + | PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => { + // If there was an error resolving the path, default to peeling everything. + opt_path_res.unwrap().map_or(AdjustMode::peel_all(), |pr| pr.adjust_mode()) + } // 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" {}`. - // - // Call `resolve_vars_if_possible` here for inline const blocks. - PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() { - ty::Ref(..) => AdjustMode::Pass, - _ => AdjustMode::Peel, - }, + // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}` unless + // `deref_patterns` is enabled. + PatKind::Expr(lt) => { + // Path patterns have already been handled, and inline const blocks currently + // aren't possible to write, so any handling for them would be untested. + if cfg!(debug_assertions) + && self.tcx.features().deref_patterns() + && !matches!(lt.kind, PatExprKind::Lit { .. }) + { + span_bug!( + lt.span, + "FIXME(deref_patterns): adjust mode unimplemented for {:?}", + lt.kind + ); + } + // Call `resolve_vars_if_possible` here for inline const blocks. + let lit_ty = self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)); + // If `deref_patterns` is enabled, allow `if let "foo" = &&"foo" {}`. + if self.tcx.features().deref_patterns() { + let mut peeled_ty = lit_ty; + let mut pat_ref_layers = 0; + while let ty::Ref(_, inner_ty, mutbl) = + *self.try_structurally_resolve_type(pat.span, peeled_ty).kind() + { + // We rely on references at the head of constants being immutable. + debug_assert!(mutbl.is_not()); + pat_ref_layers += 1; + peeled_ty = inner_ty; + } + AdjustMode::Peel { + kind: PeelKind::Implicit { until_adt: None, pat_ref_layers }, + } + } else { + if lit_ty.is_ref() { AdjustMode::Pass } else { AdjustMode::peel_all() } + } + } // Ref patterns are complicated, we handle them in `check_pat_ref`. - PatKind::Ref(..) => AdjustMode::Pass, + PatKind::Ref(..) + // No need to do anything on a missing pattern. + | PatKind::Missing // A `_` pattern works with any expected type, so there's no need to do anything. - PatKind::Wild + | PatKind::Wild // A malformed pattern doesn't have an expected type, so let's just accept any type. | PatKind::Err(_) // Bindings also work with whatever the expected type is, @@ -522,62 +728,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - /// 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_br: ByRef, - mut max_ref_mutbl: MutblCap, - ) -> (Ty<'tcx>, ByRef, MutblCap) { - let mut expected = self.try_structurally_resolve_type(pat.span, 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 = self.try_structurally_resolve_type(pat.span, inner_ty); - def_br = ByRef::Yes(match def_br { - // If default binding mode is by value, make it `ref` or `ref mut` - // (depending on whether we observe `&` or `&mut`). - ByRef::No | - // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). - ByRef::Yes(Mutability::Mut) => inner_mutability, - // Once a `ref`, always a `ref`. - // This is because a `& &mut` cannot mutate the underlying value. - ByRef::Yes(Mutability::Not) => Mutability::Not, - }); - } + /// Assuming `expected` is a reference type, determine whether to peel it before matching. + fn should_peel_ref(&self, peel_kind: PeelKind, mut expected: Ty<'tcx>) -> bool { + debug_assert!(expected.is_ref()); + let pat_ref_layers = match peel_kind { + PeelKind::ExplicitDerefPat => 0, + PeelKind::Implicit { pat_ref_layers, .. } => pat_ref_layers, + }; - if self.downgrade_mut_inside_shared() { - def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl()); - } - if def_br == ByRef::Yes(Mutability::Not) { - max_ref_mutbl = MutblCap::Not; + // Most patterns don't have reference types, so we'll want to peel all references from the + // scrutinee before matching. To optimize for the common case, return early. + if pat_ref_layers == 0 { + return true; } + debug_assert!( + self.tcx.features().deref_patterns(), + "Peeling for patterns with reference types is gated by `deref_patterns`." + ); - if !pat_adjustments.is_empty() { - debug!("default binding mode is now {:?}", def_br); - self.typeck_results - .borrow_mut() - .pat_adjustments_mut() - .insert(pat.hir_id, pat_adjustments); + // If the pattern has as many or more layers of reference as the expected type, we can match + // without peeling more, unless we find a smart pointer or `&mut` that we also need to peel. + // We don't treat `&` and `&mut` as interchangeable, but by peeling `&mut`s before matching, + // we can still, e.g., match on a `&mut str` with a string literal pattern. This is because + // string literal patterns may be used where `str` is expected. + let mut expected_ref_layers = 0; + while let ty::Ref(_, inner_ty, mutbl) = *expected.kind() { + if mutbl.is_mut() { + // Mutable references can't be in the final value of constants, thus they can't be + // at the head of their types, thus we should always peel `&mut`. + return true; + } + expected_ref_layers += 1; + expected = inner_ty; } + pat_ref_layers < expected_ref_layers || self.should_peel_smart_pointer(peel_kind, expected) + } - (expected, def_br, max_ref_mutbl) + /// Determine whether `expected` is a smart pointer type that should be peeled before matching. + fn should_peel_smart_pointer(&self, peel_kind: PeelKind, expected: Ty<'tcx>) -> bool { + // Explicit `deref!(_)` patterns match against smart pointers; don't peel in that case. + if let PeelKind::Implicit { until_adt, .. } = peel_kind + // For simplicity, only apply overloaded derefs if `expected` is a known ADT. + // FIXME(deref_patterns): we'll get better diagnostics for users trying to + // implicitly deref generics if we allow them here, but primitives, tuples, and + // inference vars definitely should be stopped. Figure out what makes most sense. + && let ty::Adt(scrutinee_adt, _) = *expected.kind() + // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if + // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern. + && until_adt != Some(scrutinee_adt.did()) + // At this point, the pattern isn't able to match `expected` without peeling. Check + // that it implements `Deref` before assuming it's a smart pointer, to get a normal + // type error instead of a missing impl error if not. This only checks for `Deref`, + // not `DerefPure`: we require that too, but we want a trait error if it's missing. + && let Some(deref_trait) = self.tcx.lang_items().deref_trait() + && self.type_implements_trait(deref_trait, [expected], self.param_env).may_apply() + { + true + } else { + false + } } fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> { @@ -619,26 +828,54 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Byte string patterns behave the same way as array patterns // They can denote both statically and dynamically-sized byte arrays. + // Additionally, when `deref_patterns` is enabled, byte string literal patterns may have + // types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec`. let mut pat_ty = ty; if let hir::PatExprKind::Lit { lit: Spanned { node: ast::LitKind::ByteStr(..), .. }, .. } = lt.kind { + let tcx = self.tcx; let expected = self.structurally_resolve_type(span, expected); - if let ty::Ref(_, inner_ty, _) = *expected.kind() - && self.try_structurally_resolve_type(span, inner_ty).is_slice() - { - let tcx = self.tcx; - trace!(?lt.hir_id.local_id, "polymorphic byte string lit"); - self.typeck_results - .borrow_mut() - .treat_byte_string_as_slice - .insert(lt.hir_id.local_id); - pat_ty = - Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_slice(tcx, tcx.types.u8)); + match *expected.kind() { + // Allow `b"...": &[u8]` + ty::Ref(_, inner_ty, _) + if self.try_structurally_resolve_type(span, inner_ty).is_slice() => + { + trace!(?lt.hir_id.local_id, "polymorphic byte string lit"); + pat_ty = Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_static, + Ty::new_slice(tcx, tcx.types.u8), + ); + } + // Allow `b"...": [u8; 3]` for `deref_patterns` + ty::Array(..) if tcx.features().deref_patterns() => { + pat_ty = match *ty.kind() { + ty::Ref(_, inner_ty, _) => inner_ty, + _ => span_bug!(span, "found byte string literal with non-ref type {ty:?}"), + } + } + // Allow `b"...": [u8]` for `deref_patterns` + ty::Slice(..) if tcx.features().deref_patterns() => { + pat_ty = Ty::new_slice(tcx, tcx.types.u8); + } + // Otherwise, `b"...": &[u8; 3]` + _ => {} } } + // When `deref_patterns` is enabled, in order to allow `deref!("..."): String`, we allow + // string literal patterns to have type `str`. This is accounted for when lowering to MIR. + if self.tcx.features().deref_patterns() + && let hir::PatExprKind::Lit { + lit: Spanned { node: ast::LitKind::Str(..), .. }, .. + } = lt.kind + && self.try_structurally_resolve_type(span, expected).is_str() + { + pat_ty = self.tcx.types.str_; + } + if self.tcx.features().string_deref_patterns() && let hir::PatExprKind::Lit { lit: Spanned { node: ast::LitKind::Str(..), .. }, .. @@ -698,6 +935,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // be peeled to `str` while ty here is still `&str`, if we don't // err early here, a rather confusing unification error will be // emitted instead). + let ty = self.try_structurally_resolve_type(expr.span, ty); let fail = !(ty.is_numeric() || ty.is_char() || ty.is_ty_var() || ty.references_error()); Some((fail, ty, expr.span)) @@ -818,7 +1056,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ident: Ident, sub: Option<&'tcx Pat<'tcx>>, expected: Ty<'tcx>, - pat_info: PatInfo<'_, 'tcx>, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { let PatInfo { binding_mode: def_br, top_info: ti, .. } = pat_info; @@ -914,12 +1152,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // We have a concrete type for the local, so we do not need to taint it and hide follow up errors *using* the local. - let _ = self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti); + let _ = 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(user_bind_annot, pat.span, var_id, local_ty, ti); + self.check_binding_alt_eq_ty(user_bind_annot, pat.span, var_id, local_ty, &ti); } if let Some(p) = sub { @@ -942,10 +1180,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let var_ty = self.local_ty(span, var_id); if let Err(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) { - let hir = self.tcx.hir(); let var_ty = self.resolve_vars_if_possible(var_ty); let msg = format!("first introduced with type `{var_ty}` here"); - err.span_label(hir.span(var_id), msg); + err.span_label(self.tcx.hir_span(var_id), msg); let in_match = self.tcx.hir_parent_iter(var_id).any(|(_, n)| { matches!( n, @@ -1035,7 +1272,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::Tuple(..) | PatKind::Slice(..) => "binding", - PatKind::Wild + PatKind::Missing + | PatKind::Wild | PatKind::Never | PatKind::Binding(..) | PatKind::Box(..) @@ -1142,29 +1380,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(()) } - fn check_pat_struct( + fn resolve_pat_struct( &self, pat: &'tcx Pat<'tcx>, qpath: &hir::QPath<'tcx>, + ) -> Result, ErrorGuaranteed> { + // Resolve the path and check the definition for errors. + let (variant, pat_ty) = self.check_struct_path(qpath, pat.hir_id)?; + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Struct { variant } }) + } + + fn check_pat_struct( + &self, + pat: &'tcx Pat<'tcx>, fields: &'tcx [hir::PatField<'tcx>], has_rest_pat: bool, + pat_ty: Ty<'tcx>, + variant: &'tcx VariantDef, expected: Ty<'tcx>, - pat_info: PatInfo<'_, 'tcx>, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { - // Resolve the path and check the definition for errors. - let (variant, pat_ty) = match self.check_struct_path(qpath, pat.hir_id) { - Ok(data) => data, - Err(guar) => { - let err = Ty::new_error(self.tcx, guar); - for field in fields { - self.check_pat(field.pat, err, pat_info); - } - return err; - } - }; - // Type-check the path. - let _ = self.demand_eqtype_pat(pat.span, expected, pat_ty, pat_info.top_info); + let _ = self.demand_eqtype_pat(pat.span, expected, pat_ty, &pat_info.top_info); // Type-check subpatterns. match self.check_struct_pat_fields(pat_ty, pat, variant, fields, has_rest_pat, pat_info) { @@ -1173,31 +1410,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_pat_path( + fn resolve_pat_path( &self, path_id: HirId, - pat_id_for_diag: HirId, span: Span, - qpath: &hir::QPath<'_>, - path_resolution: (Res, Option>, &'tcx [hir::PathSegment<'tcx>]), - expected: Ty<'tcx>, - ti: &TopInfo<'tcx>, - ) -> Ty<'tcx> { + qpath: &'tcx hir::QPath<'_>, + ) -> Result, ErrorGuaranteed> { let tcx = self.tcx; - // We have already resolved the path. - let (res, opt_ty, segments) = path_resolution; + let (res, opt_ty, segments) = + self.resolve_ty_and_res_fully_qualified_call(qpath, path_id, span); match res { Res::Err => { let e = self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); - return Ty::new_error(tcx, e); + return Err(e); } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { let expected = "unit struct, unit variant or constant"; let e = report_unexpected_variant_res(tcx, res, None, qpath, span, E0533, expected); - return Ty::new_error(tcx, e); + return Err(e); } Res::SelfCtor(def_id) => { if let ty::Adt(adt_def, _) = *tcx.type_of(def_id).skip_binder().kind() @@ -1215,7 +1448,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { E0533, "unit struct", ); - return Ty::new_error(tcx, e); + return Err(e); } } Res::Def( @@ -1228,15 +1461,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => bug!("unexpected pattern resolution: {:?}", res), } - // Type-check the path. + // Find the type of the path pattern, for later checking. let (pat_ty, pat_res) = self.instantiate_value_path(segments, opt_ty, res, span, span, path_id); + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Path { res, pat_res, segments } }) + } + + fn check_pat_path( + &self, + pat_id_for_diag: HirId, + span: Span, + resolved: &ResolvedPat<'tcx>, + expected: Ty<'tcx>, + ti: &TopInfo<'tcx>, + ) -> Ty<'tcx> { if let Err(err) = - self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, pat_ty) + self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, resolved.ty) { - self.emit_bad_pat_path(err, pat_id_for_diag, span, res, pat_res, pat_ty, segments); + self.emit_bad_pat_path(err, pat_id_for_diag, span, resolved); } - pat_ty + resolved.ty } fn maybe_suggest_range_literal( @@ -1248,12 +1492,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match opt_def_id { Some(def_id) => match self.tcx.hir_get_if_local(def_id) { Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Const(_, _, body_id), + kind: hir::ItemKind::Const(_, _, _, body_id), .. })) => match self.tcx.hir_node(body_id.hir_id) { hir::Node::Expr(expr) => { if hir::is_range_literal(expr) { - let span = self.tcx.hir().span(body_id.hir_id); + let span = self.tcx.hir_span(body_id.hir_id); if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) { e.span_suggestion_verbose( ident.span, @@ -1279,12 +1523,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mut e: Diag<'_>, hir_id: HirId, pat_span: Span, - res: Res, - pat_res: Res, - pat_ty: Ty<'tcx>, - segments: &'tcx [hir::PathSegment<'tcx>], + resolved_pat: &ResolvedPat<'tcx>, ) { - if let Some(span) = self.tcx.hir().res_span(pat_res) { + let ResolvedPatKind::Path { res, pat_res, segments } = resolved_pat.kind else { + span_bug!(pat_span, "unexpected resolution for path pattern: {resolved_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( @@ -1306,7 +1551,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } _ => { - let (type_def_id, item_def_id) = match pat_ty.kind() { + let (type_def_id, item_def_id) = match resolved_pat.ty.kind() { ty::Adt(def, _) => match res { Res::Def(DefKind::Const, def_id) => (Some(def.did()), Some(def_id)), _ => (None, None), @@ -1314,15 +1559,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => (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 type_def_id != None && ranges.contains(&type_def_id) { + let is_range = match type_def_id.and_then(|id| self.tcx.as_lang_item(id)) { + Some( + LangItem::Range + | LangItem::RangeFrom + | LangItem::RangeTo + | LangItem::RangeFull + | LangItem::RangeInclusiveStruct + | LangItem::RangeToInclusive, + ) => true, + _ => false, + }; + if is_range { if !self.maybe_suggest_range_literal(&mut e, item_def_id, *ident) { let msg = "constants only support matching by type, \ if you meant to match against a range of values, \ @@ -1346,26 +1594,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { e.emit(); } - fn check_pat_tuple_struct( + fn resolve_pat_tuple_struct( &self, pat: &'tcx Pat<'tcx>, qpath: &'tcx hir::QPath<'tcx>, - subpats: &'tcx [Pat<'tcx>], - ddpos: hir::DotDotPos, - expected: Ty<'tcx>, - pat_info: PatInfo<'_, 'tcx>, - ) -> Ty<'tcx> { + ) -> Result, ErrorGuaranteed> { let tcx = self.tcx; - let on_error = |e| { - for pat in subpats { - self.check_pat(pat, Ty::new_error(tcx, e), pat_info); - } - }; let report_unexpected_res = |res: Res| { let expected = "tuple struct or tuple variant"; let e = report_unexpected_variant_res(tcx, res, None, qpath, pat.span, E0164, expected); - on_error(e); - e + Err(e) }; // Resolve the path and check the definition for errors. @@ -1374,16 +1612,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if res == Res::Err { let e = self.dcx().span_delayed_bug(pat.span, "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); - on_error(e); - return Ty::new_error(tcx, e); + return Err(e); } // Type-check the path. let (pat_ty, res) = self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id); if !pat_ty.is_fn() { - let e = report_unexpected_res(res); - return Ty::new_error(tcx, e); + return report_unexpected_res(res); } let variant = match res { @@ -1391,8 +1627,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.dcx().span_bug(pat.span, "`Res::Err` but no error emitted"); } Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { - let e = report_unexpected_res(res); - return Ty::new_error(tcx, e); + return report_unexpected_res(res); } Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res), _ => bug!("unexpected pattern resolution: {:?}", res), @@ -1402,8 +1637,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pat_ty = pat_ty.fn_sig(tcx).output(); let pat_ty = pat_ty.no_bound_vars().expect("expected fn type"); + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::TupleStruct { res, variant } }) + } + + fn check_pat_tuple_struct( + &self, + pat: &'tcx Pat<'tcx>, + qpath: &'tcx hir::QPath<'tcx>, + subpats: &'tcx [Pat<'tcx>], + ddpos: hir::DotDotPos, + res: Res, + pat_ty: Ty<'tcx>, + variant: &'tcx VariantDef, + expected: Ty<'tcx>, + pat_info: PatInfo<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let on_error = |e| { + for pat in subpats { + self.check_pat(pat, Ty::new_error(tcx, e), pat_info); + } + }; + // Type-check the tuple struct pattern against the expected type. - let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, pat_info.top_info); + let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, &pat_info.top_info); let had_err = diag.map_err(|diag| diag.emit()); // Type-check subpatterns. @@ -1420,7 +1677,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.check_stability( variant.fields[FieldIdx::from_usize(i)].did, - Some(pat.hir_id), + Some(subpat.hir_id), subpat.span, None, ); @@ -1610,7 +1867,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { elements: &'tcx [Pat<'tcx>], ddpos: hir::DotDotPos, expected: Ty<'tcx>, - pat_info: PatInfo<'_, 'tcx>, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; let mut expected_len = elements.len(); @@ -1625,7 +1882,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let element_tys_iter = (0..max_len).map(|_| self.next_ty_var(span)); let element_tys = tcx.mk_type_list_from_iter(element_tys_iter); let pat_ty = Ty::new_tup(tcx, element_tys); - if let Err(reported) = self.demand_eqtype_pat(span, expected, pat_ty, pat_info.top_info) { + if let Err(reported) = self.demand_eqtype_pat(span, expected, pat_ty, &pat_info.top_info) { // 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(|_| Ty::new_error(tcx, reported)); @@ -1648,7 +1905,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant: &'tcx ty::VariantDef, fields: &'tcx [hir::PatField<'tcx>], has_rest_pat: bool, - pat_info: PatInfo<'_, 'tcx>, + pat_info: PatInfo<'tcx>, ) -> Result<(), ErrorGuaranteed> { let tcx = self.tcx; @@ -1684,7 +1941,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .get(&ident) .map(|(i, f)| { self.write_field_index(field.hir_id, *i); - self.tcx.check_stability(f.did, Some(pat.hir_id), span, None); + self.tcx.check_stability(f.did, Some(field.hir_id), span, None); self.field_ty(span, f, args) }) .unwrap_or_else(|| { @@ -1722,7 +1979,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Require `..` if struct has non_exhaustive attribute. - let non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did().is_local(); + let non_exhaustive = variant.field_list_has_applicable_non_exhaustive(); if non_exhaustive && !has_rest_pat { self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty()); } @@ -2257,7 +2514,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, inner: &'tcx Pat<'tcx>, expected: Ty<'tcx>, - pat_info: PatInfo<'_, 'tcx>, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; let (box_ty, inner_ty) = self @@ -2267,7 +2524,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // think any errors can be introduced by using `demand::eqtype`. let inner_ty = self.next_ty_var(inner.span); let box_ty = Ty::new_box(tcx, inner_ty); - self.demand_eqtype_pat(span, expected, box_ty, pat_info.top_info)?; + self.demand_eqtype_pat(span, expected, box_ty, &pat_info.top_info)?; Ok((box_ty, inner_ty)) }) .unwrap_or_else(|guar| { @@ -2283,38 +2540,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, inner: &'tcx Pat<'tcx>, expected: Ty<'tcx>, - pat_info: PatInfo<'_, 'tcx>, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { - let tcx = self.tcx; + let target_ty = self.deref_pat_target(span, expected); + self.check_pat(inner, target_ty, pat_info); + self.register_deref_mut_bounds_if_needed(span, inner, [expected]); + expected + } + + fn deref_pat_target(&self, span: Span, source_ty: Ty<'tcx>) -> Ty<'tcx> { // Register a `DerefPure` bound, which is required by all `deref!()` pats. + let tcx = self.tcx; self.register_bound( - expected, + source_ty, tcx.require_lang_item(hir::LangItem::DerefPure, Some(span)), self.misc(span), ); - // ::Target - let ty = Ty::new_projection( + // The expected type for the deref pat's inner pattern is `::Target`. + let target_ty = Ty::new_projection( tcx, tcx.require_lang_item(hir::LangItem::DerefTarget, Some(span)), - [expected], + [source_ty], ); - let ty = self.normalize(span, ty); - let ty = self.try_structurally_resolve_type(span, ty); - self.check_pat(inner, ty, pat_info); - - // Check if the pattern has any `ref mut` bindings, which would require - // `DerefMut` to be emitted in MIR building instead of just `Deref`. - // We do this *after* checking the inner pattern, since we want to make - // sure to apply any match-ergonomics adjustments. + let target_ty = self.normalize(span, target_ty); + self.try_structurally_resolve_type(span, target_ty) + } + + /// Check if the interior of a deref pattern (either explicit or implicit) has any `ref mut` + /// bindings, which would require `DerefMut` to be emitted in MIR building instead of just + /// `Deref`. We do this *after* checking the inner pattern, since we want to make sure to + /// account for `ref mut` binding modes inherited from implicitly dereferencing `&mut` refs. + fn register_deref_mut_bounds_if_needed( + &self, + span: Span, + inner: &'tcx Pat<'tcx>, + derefed_tys: impl IntoIterator>, + ) { if self.typeck_results.borrow().pat_has_ref_mut_binding(inner) { - self.register_bound( - expected, - tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)), - self.misc(span), - ); + for mutably_derefed_ty in derefed_tys { + self.register_bound( + mutably_derefed_ty, + self.tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)), + self.misc(span), + ); + } } - - expected } // Precondition: Pat is Ref(inner) @@ -2324,7 +2594,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { inner: &'tcx Pat<'tcx>, pat_mutbl: Mutability, mut expected: Ty<'tcx>, - mut pat_info: PatInfo<'_, 'tcx>, + mut pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; @@ -2482,7 +2752,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat.span, expected, ref_ty, - pat_info.top_info, + &pat_info.top_info, ); // Look for a case like `fn foo(&foo: u32)` and suggest @@ -2605,7 +2875,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { slice: Option<&'tcx Pat<'tcx>>, after: &'tcx [Pat<'tcx>], expected: Ty<'tcx>, - pat_info: PatInfo<'_, 'tcx>, + pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { let expected = self.try_structurally_resolve_type(span, expected); @@ -2767,7 +3037,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, span: Span, expected_ty: Ty<'tcx>, - pat_info: PatInfo<'_, 'tcx>, + pat_info: PatInfo<'tcx>, ) -> ErrorGuaranteed { let PatInfo { top_info: ti, current_depth, .. } = pat_info; diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 4fc903cf68b88..fedc75abe4927 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::adjustment::{ PointerCoercion, }; use rustc_middle::ty::{self, Ty}; -use rustc_span::{Ident, Span, sym}; +use rustc_span::{Span, sym}; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; @@ -211,13 +211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - self.lookup_method_in_trait( - self.misc(span), - Ident::with_dummy_span(imm_op), - imm_tr, - base_ty, - opt_rhs_ty, - ) + self.lookup_method_for_operator(self.misc(span), imm_op, imm_tr, base_ty, opt_rhs_ty) } fn try_mutable_overloaded_place_op( @@ -237,13 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - self.lookup_method_in_trait( - self.misc(span), - Ident::with_dummy_span(mut_op), - mut_tr, - base_ty, - opt_rhs_ty, - ) + self.lookup_method_for_operator(self.misc(span), mut_op, mut_tr, base_ty, opt_rhs_ty) } /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` diff --git a/compiler/rustc_hir_typeck/src/rvalue_scopes.rs b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs index 98d7f777d6b8a..973dc7141e64d 100644 --- a/compiler/rustc_hir_typeck/src/rvalue_scopes.rs +++ b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs @@ -2,7 +2,7 @@ use hir::Node; use hir::def_id::DefId; use rustc_hir as hir; use rustc_middle::bug; -use rustc_middle::middle::region::{RvalueCandidateType, Scope, ScopeTree}; +use rustc_middle::middle::region::{RvalueCandidate, Scope, ScopeTree}; use rustc_middle::ty::RvalueScopes; use tracing::debug; @@ -55,15 +55,11 @@ fn record_rvalue_scope_rec( fn record_rvalue_scope( rvalue_scopes: &mut RvalueScopes, expr: &hir::Expr<'_>, - candidate: &RvalueCandidateType, + candidate: &RvalueCandidate, ) { debug!("resolve_rvalue_scope(expr={expr:?}, candidate={candidate:?})"); - match candidate { - RvalueCandidateType::Borrow { lifetime, .. } - | RvalueCandidateType::Pattern { lifetime, .. } => { - record_rvalue_scope_rec(rvalue_scopes, expr, *lifetime) - } // FIXME(@dingxiangfei2009): handle the candidates in the function call arguments - } + record_rvalue_scope_rec(rvalue_scopes, expr, candidate.lifetime) + // FIXME(@dingxiangfei2009): handle the candidates in the function call arguments } pub(crate) fn resolve_rvalue_scopes<'a, 'tcx>( diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index 381606a9fb020..26be5fc6d1904 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -1,14 +1,12 @@ -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::ops::Deref; use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{HirId, HirIdMap}; -use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_hir::{self as hir, HirId, HirIdMap, LangItem}; +use rustc_infer::infer::{InferCtxt, InferOk, OpaqueTypeStorageEntries, TyCtxtInferExt}; use rustc_middle::span_bug; -use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_span::Span; use rustc_span::def_id::LocalDefIdMap; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -39,6 +37,11 @@ pub(crate) struct TypeckRootCtxt<'tcx> { pub(super) fulfillment_cx: RefCell>>>, + // Used to detect opaque types uses added after we've already checked them. + // + // See [FnCtxt::detect_opaque_types_added_during_writeback] for more details. + pub(super) checked_opaque_types_storage_entries: Cell>, + /// Some additional `Sized` obligations badly affect type inference. /// These obligations are added in a later stage of typeck. /// Removing these may also cause additional complications, see #101066. @@ -60,7 +63,7 @@ pub(crate) struct TypeckRootCtxt<'tcx> { pub(super) deferred_asm_checks: RefCell, HirId)>>, - pub(super) deferred_coroutine_interiors: RefCell)>>, + pub(super) deferred_coroutine_interiors: RefCell)>>, pub(super) deferred_repeat_expr_checks: RefCell, Ty<'tcx>, ty::Const<'tcx>)>>, @@ -85,14 +88,16 @@ impl<'tcx> TypeckRootCtxt<'tcx> { let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner; let infcx = - tcx.infer_ctxt().ignoring_regions().build(TypingMode::analysis_in_body(tcx, def_id)); + tcx.infer_ctxt().ignoring_regions().build(TypingMode::typeck_for_body(tcx, def_id)); let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner)); + let fulfillment_cx = RefCell::new(>::new(&infcx)); TypeckRootCtxt { - typeck_results, - fulfillment_cx: RefCell::new(>::new(&infcx)), infcx, + typeck_results, locals: RefCell::new(Default::default()), + fulfillment_cx, + checked_opaque_types_storage_entries: Cell::new(None), deferred_sized_obligations: RefCell::new(Vec::new()), deferred_call_resolutions: RefCell::new(Default::default()), deferred_cast_checks: RefCell::new(Vec::new()), @@ -138,7 +143,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> { obligation.predicate.kind().skip_binder() && let Some(ty) = self.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| self.root_var(t)) - && self.tcx.lang_items().sized_trait().is_some_and(|st| st != tpred.trait_ref.def_id) + && !self.tcx.is_lang_item(tpred.trait_ref.def_id, LangItem::Sized) { let new_self_ty = self.tcx.types.unit; diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 9a0b22470585f..8ab71e5220bb7 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -18,12 +18,12 @@ //! from there). //! //! The fact that we are inferring borrow kinds as we go results in a -//! semi-hacky interaction with mem-categorization. In particular, -//! mem-categorization will query the current borrow kind as it -//! categorizes, and we'll return the *current* value, but this may get +//! semi-hacky interaction with the way `ExprUseVisitor` is computing +//! `Place`s. In particular, it will query the current borrow kind as it +//! goes, and we'll return the *current* value, but this may get //! adjusted later. Therefore, in this module, we generally ignore the -//! borrow kind (and derived mutabilities) that are returned from -//! mem-categorization, since they may be inaccurate. (Another option +//! borrow kind (and derived mutabilities) that `ExprUseVisitor` returns +//! within `Place`s, since they may be inaccurate. (Another option //! would be to use a unification scheme, where instead of returning a //! concrete borrow kind like `ty::ImmBorrow`, we return a //! `ty::InferBorrow(upvar_id)` or something like that, but this would @@ -85,7 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Intermediate format to store the hir_id pointing to the use that resulted in the /// corresponding place being captured and a String which contains the captured value's /// name (i.e: a.b.c) -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] enum UpvarMigrationInfo { /// We previously captured all of `x`, but now we capture some sub-path. CapturingPrecise { source_expr: Option, var_name: String }, @@ -635,7 +635,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (place, capture_kind) = truncate_capture_for_optimization(place, capture_kind); let usage_span = if let Some(usage_expr) = capture_info.path_expr_id { - self.tcx.hir().span(usage_expr) + self.tcx.hir_span(usage_expr) } else { unreachable!() }; @@ -671,6 +671,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (place, capture_kind) = match capture_clause { hir::CaptureBy::Value { .. } => adjust_for_move_closure(place, capture_kind), + hir::CaptureBy::Use { .. } => adjust_for_use_closure(place, capture_kind), hir::CaptureBy::Ref => adjust_for_non_move_closure(place, capture_kind), }; @@ -780,7 +781,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, base => bug!("Expected upvar, found={:?}", base), }; - let var_ident = self.tcx.hir().ident(var_hir_id); + let var_ident = self.tcx.hir_ident(var_hir_id); let Some(min_cap_list) = root_var_min_capture_list.get_mut(&var_hir_id) else { let mutability = self.determine_capture_mutability(&typeck_results, &place); @@ -985,15 +986,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for lint_note in diagnostics_info.iter() { match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { source_expr: Some(capture_expr_id), var_name: captured_name } => { - let cause_span = self.tcx.hir().span(*capture_expr_id); + let cause_span = self.tcx.hir_span(*capture_expr_id); lint.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`", - self.tcx.hir().name(*var_hir_id), + self.tcx.hir_name(*var_hir_id), captured_name, )); } UpvarMigrationInfo::CapturingNothing { use_span } => { lint.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", - self.tcx.hir().name(*var_hir_id), + self.tcx.hir_name(*var_hir_id), )); } @@ -1008,13 +1009,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { lint.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", - self.tcx.hir().name(*var_hir_id), + self.tcx.hir_name(*var_hir_id), captured_name, )); } UpvarMigrationInfo::CapturingNothing { use_span: _ } => { lint.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", - v = self.tcx.hir().name(*var_hir_id), + v = self.tcx.hir_name(*var_hir_id), )); } } @@ -1025,7 +1026,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // not capturing something anymore cannot cause a trait to fail to be implemented: match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { - let var_name = self.tcx.hir().name(*var_hir_id); + let var_name = self.tcx.hir_name(*var_hir_id); lint.span_label(closure_head_span, format!("\ in Rust 2018, this closure implements {missing_trait} \ as `{var_name}` implements {missing_trait}, but in Rust 2021, \ @@ -1046,13 +1047,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "add a dummy let to cause {migrated_variables_concat} to be fully captured" ); - let closure_span = self.tcx.hir().span_with_body(closure_hir_id); + let closure_span = self.tcx.hir_span_with_body(closure_hir_id); let mut closure_body_span = { // If the body was entirely expanded from a macro // invocation, i.e. the body is not contained inside the // closure span, then we walk up the expansion until we // find the span before the expansion. - let s = self.tcx.hir().span_with_body(body_id.hir_id); + let s = self.tcx.hir_span_with_body(body_id.hir_id); s.find_ancestor_inside(closure_span).unwrap_or(s) }; @@ -1165,7 +1166,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = match closure_clause { hir::CaptureBy::Value { .. } => ty, // For move closure the capture kind should be by value - hir::CaptureBy::Ref => { + hir::CaptureBy::Ref | hir::CaptureBy::Use { .. } => { // For non move closure the capture kind is the max capture kind of all captures // according to the ordering ImmBorrow < UniqueImmBorrow < MutBorrow < ByValue let mut max_capture_info = root_var_min_capture_list.first().unwrap().info; @@ -1292,7 +1293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .insert(UpvarMigrationInfo::CapturingNothing { use_span: upvar.span }); return Some(diagnostics_info); } - hir::CaptureBy::Ref => {} + hir::CaptureBy::Ref | hir::CaptureBy::Use { .. } => {} } return None; @@ -1305,7 +1306,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for captured_place in root_var_min_capture_list.iter() { match captured_place.info.capture_kind { // Only care about captures that are moved into the closure - ty::UpvarCapture::ByValue => { + ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => { projections_list.push(captured_place.place.projections.as_slice()); diagnostics_info.insert(UpvarMigrationInfo::CapturingPrecise { source_expr: captured_place.info.path_expr_id, @@ -1395,14 +1396,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { FxIndexSet::default() }; - // Combine all the captures responsible for needing migrations into one HashSet + // Combine all the captures responsible for needing migrations into one IndexSet let mut capture_diagnostic = drop_reorder_diagnostic.clone(); for key in auto_trait_diagnostic.keys() { capture_diagnostic.insert(key.clone()); } let mut capture_diagnostic = capture_diagnostic.into_iter().collect::>(); - capture_diagnostic.sort(); + capture_diagnostic.sort_by_cached_key(|info| match info { + UpvarMigrationInfo::CapturingPrecise { source_expr: _, var_name } => { + (0, Some(var_name.clone())) + } + UpvarMigrationInfo::CapturingNothing { use_span: _ } => (1, None), + }); for captures_info in capture_diagnostic { // Get the auto trait reasons of why migration is needed because of that capture, if there are any let capture_trait_reasons = @@ -1689,10 +1695,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // If the data will be moved out of this place, then the place will be truncated // at the first Deref in `adjust_for_move_closure` and then moved into the closure. + // + // For example: + // + // struct Buffer<'a> { + // x: &'a String, + // y: Vec, + // } + // + // fn get<'a>(b: Buffer<'a>) -> impl Sized + 'a { + // let c = move || b.x; + // drop(b); + // c + // } + // + // Even though the closure is declared as move, when we are capturing borrowed data (in + // this case, *b.x) we prefer to capture by reference. + // Otherwise you'd get an error in 2021 immediately because you'd be trying to take + // ownership of the (borrowed) String or else you'd take ownership of b, as in 2018 and + // before, which is also an error. hir::CaptureBy::Value { .. } if !place.deref_tys().any(Ty::is_ref) => { ty::UpvarCapture::ByValue } - hir::CaptureBy::Value { .. } | hir::CaptureBy::Ref => { + hir::CaptureBy::Use { .. } if !place.deref_tys().any(Ty::is_ref) => { + ty::UpvarCapture::ByUse + } + hir::CaptureBy::Value { .. } | hir::CaptureBy::Use { .. } | hir::CaptureBy::Ref => { ty::UpvarCapture::ByRef(BorrowKind::Immutable) } } @@ -1729,8 +1757,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let capture_str = construct_capture_info_string(self.tcx, place, capture_info); let output_str = format!("Capturing {capture_str}"); - let span = - capture_info.path_expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); + let span = capture_info.path_expr_id.map_or(closure_span, |e| self.tcx.hir_span(e)); diag.span_note(span, output_str); } diag.emit(); @@ -1757,10 +1784,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if capture.info.path_expr_id != capture.info.capture_kind_expr_id { let path_span = capture_info .path_expr_id - .map_or(closure_span, |e| self.tcx.hir().span(e)); + .map_or(closure_span, |e| self.tcx.hir_span(e)); let capture_kind_span = capture_info .capture_kind_expr_id - .map_or(closure_span, |e| self.tcx.hir().span(e)); + .map_or(closure_span, |e| self.tcx.hir_span(e)); let mut multi_span: MultiSpan = MultiSpan::from_spans(vec![path_span, capture_kind_span]); @@ -1776,7 +1803,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { let span = capture_info .path_expr_id - .map_or(closure_span, |e| self.tcx.hir().span(e)); + .map_or(closure_span, |e| self.tcx.hir_span(e)); diag.span_note(span, output_str); }; @@ -1805,8 +1832,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut is_mutbl = bm.1; for pointer_ty in place.deref_tys() { - match self.structurally_resolve_type(self.tcx.hir().span(var_hir_id), pointer_ty).kind() - { + match self.structurally_resolve_type(self.tcx.hir_span(var_hir_id), pointer_ty).kind() { // We don't capture derefs of raw ptrs ty::RawPtr(_, _) => unreachable!(), @@ -1821,7 +1847,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Adt(def, ..) if def.is_box() => {} unexpected_ty => span_bug!( - self.tcx.hir().span(var_hir_id), + self.tcx.hir_span(var_hir_id), "deref of unexpected pointer type {:?}", unexpected_ty ), @@ -1839,8 +1865,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// (1.) Are we borrowing data owned by the parent closure? We can determine if /// that is the case by checking if the parent capture is by move, EXCEPT if we -/// apply a deref projection, which means we're reborrowing a reference that we -/// captured by move. +/// apply a deref projection of an immutable reference, reborrows of immutable +/// references which aren't restricted to the LUB of the lifetimes of the deref +/// chain. This is why `&'short mut &'long T` can be reborrowed as `&'long T`. /// /// ```rust /// let x = &1i32; // Let's call this lifetime `'1`. @@ -1879,10 +1906,22 @@ fn should_reborrow_from_env_of_parent_coroutine_closure<'tcx>( ) -> bool { // (1.) (!parent_capture.is_by_ref() - && !matches!( - child_capture.place.projections.get(parent_capture.place.projections.len()), - Some(Projection { kind: ProjectionKind::Deref, .. }) - )) + // This is just inlined `place.deref_tys()` but truncated to just + // the child projections. Namely, look for a `&T` deref, since we + // can always extend `&'short mut &'long T` to `&'long T`. + && !child_capture + .place + .projections + .iter() + .enumerate() + .skip(parent_capture.place.projections.len()) + .any(|(idx, proj)| { + matches!(proj.kind, ProjectionKind::Deref) + && matches!( + child_capture.place.ty_before_projection(idx).kind(), + ty::Ref(.., ty::Mutability::Not) + ) + })) // (2.) || matches!(child_capture.info.capture_kind, UpvarCapture::ByRef(ty::BorrowKind::Mutable)) } @@ -1927,7 +1966,7 @@ fn apply_capture_kind_on_capture_ty<'tcx>( region: ty::Region<'tcx>, ) -> Ty<'tcx> { match capture_kind { - ty::UpvarCapture::ByValue => ty, + ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => ty, ty::UpvarCapture::ByRef(kind) => Ty::new_ref(tcx, region, ty, kind.to_mutbl_lossy()), } } @@ -1939,14 +1978,14 @@ fn drop_location_span(tcx: TyCtxt<'_>, hir_id: HirId) -> Span { let owner_node = tcx.hir_node(owner_id); let owner_span = match owner_node { hir::Node::Item(item) => match item.kind { - hir::ItemKind::Fn { body: owner_id, .. } => tcx.hir().span(owner_id.hir_id), + hir::ItemKind::Fn { body: owner_id, .. } => tcx.hir_span(owner_id.hir_id), _ => { bug!("Drop location span error: need to handle more ItemKind '{:?}'", item.kind); } }, - hir::Node::Block(block) => tcx.hir().span(block.hir_id), - hir::Node::TraitItem(item) => tcx.hir().span(item.hir_id()), - hir::Node::ImplItem(item) => tcx.hir().span(item.hir_id()), + hir::Node::Block(block) => tcx.hir_span(block.hir_id), + hir::Node::TraitItem(item) => tcx.hir_span(item.hir_id()), + hir::Node::ImplItem(item) => tcx.hir_span(item.hir_id()), _ => { bug!("Drop location span error: need to handle more Node '{:?}'", owner_node); } @@ -2023,6 +2062,21 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { )); } + #[instrument(skip(self), level = "debug")] + fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { + let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; + assert_eq!(self.closure_def_id, upvar_id.closure_expr_id); + + self.capture_information.push(( + place_with_id.place.clone(), + ty::CaptureInfo { + capture_kind_expr_id: Some(diag_expr_id), + path_expr_id: Some(diag_expr_id), + capture_kind: ty::UpvarCapture::ByUse, + }, + )); + } + #[instrument(skip(self), level = "debug")] fn borrow( &mut self, @@ -2164,6 +2218,20 @@ fn adjust_for_move_closure( (place, ty::UpvarCapture::ByValue) } +/// Truncate deref of any reference. +fn adjust_for_use_closure( + mut place: Place<'_>, + mut kind: ty::UpvarCapture, +) -> (Place<'_>, ty::UpvarCapture) { + let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref); + + if let Some(idx) = first_deref { + truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx); + } + + (place, ty::UpvarCapture::ByUse) +} + /// Adjust closure capture just that if taking ownership of data, only move data /// from enclosing stack frame. fn adjust_for_non_move_closure( @@ -2174,7 +2242,7 @@ fn adjust_for_non_move_closure( place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref); match kind { - ty::UpvarCapture::ByValue => { + ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => { if let Some(idx) = contains_deref { truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx); } @@ -2219,6 +2287,7 @@ fn construct_capture_kind_reason_string<'tcx>( let capture_kind_str = match capture_info.capture_kind { ty::UpvarCapture::ByValue => "ByValue".into(), + ty::UpvarCapture::ByUse => "ByUse".into(), ty::UpvarCapture::ByRef(kind) => format!("{kind:?}"), }; @@ -2240,13 +2309,14 @@ fn construct_capture_info_string<'tcx>( let capture_kind_str = match capture_info.capture_kind { ty::UpvarCapture::ByValue => "ByValue".into(), + ty::UpvarCapture::ByUse => "ByUse".into(), ty::UpvarCapture::ByRef(kind) => format!("{kind:?}"), }; format!("{place_str} -> {capture_kind_str}") } fn var_name(tcx: TyCtxt<'_>, var_hir_id: HirId) -> Symbol { - tcx.hir().name(var_hir_id) + tcx.hir_name(var_hir_id) } #[instrument(level = "debug", skip(tcx))] @@ -2258,8 +2328,9 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis( return false; } - let (level, _) = - tcx.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id); + let level = tcx + .lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id) + .level; !matches!(level, lint::Level::Allow) } @@ -2335,8 +2406,11 @@ fn determine_capture_info( // expressions. let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { (ty::UpvarCapture::ByValue, ty::UpvarCapture::ByValue) => true, + (ty::UpvarCapture::ByUse, ty::UpvarCapture::ByUse) => true, (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => ref_a == ref_b, - (ty::UpvarCapture::ByValue, _) | (ty::UpvarCapture::ByRef(_), _) => false, + (ty::UpvarCapture::ByValue, _) + | (ty::UpvarCapture::ByUse, _) + | (ty::UpvarCapture::ByRef(_), _) => false, }; if eq_capture_kind { @@ -2346,10 +2420,20 @@ fn determine_capture_info( } } else { // We select the CaptureKind which ranks higher based the following priority order: - // ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow + // (ByUse | ByValue) > MutBorrow > UniqueImmBorrow > ImmBorrow match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByValue, _) => capture_info_a, - (_, ty::UpvarCapture::ByValue) => capture_info_b, + (ty::UpvarCapture::ByUse, ty::UpvarCapture::ByValue) + | (ty::UpvarCapture::ByValue, ty::UpvarCapture::ByUse) => { + bug!("Same capture can't be ByUse and ByValue at the same time") + } + (ty::UpvarCapture::ByValue, ty::UpvarCapture::ByValue) + | (ty::UpvarCapture::ByUse, ty::UpvarCapture::ByUse) + | (ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse, ty::UpvarCapture::ByRef(_)) => { + capture_info_a + } + (ty::UpvarCapture::ByRef(_), ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse) => { + capture_info_b + } (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { match (ref_a, ref_b) { // Take LHS: @@ -2401,7 +2485,7 @@ fn truncate_place_to_len_and_update_capture_kind<'tcx>( } ty::UpvarCapture::ByRef(..) => {} - ty::UpvarCapture::ByValue => {} + ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {} } place.projections.truncate(len); diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index d7d7d6a20aca4..b2497cb0de16b 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -1,21 +1,33 @@ -// Type resolution: the phase that finds all the types in the AST with -// unresolved type variables and replaces "ty_var" types with their -// generic parameters. +//! During type inference, partially inferred terms are +//! represented using inference variables (ty::Infer). These don't appear in +//! the final [`ty::TypeckResults`] since all of the types should have been +//! inferred once typeck is done. +//! +//! When type inference is running however, having to update the typeck results +//! every time a new type is inferred would be unreasonably slow, so instead all +//! of the replacement happens at the end in [`FnCtxt::resolve_type_vars_in_body`], +//! which creates a new `TypeckResults` which doesn't contain any inference variables. use std::mem; +use std::ops::ControlFlow; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::ExtendUnord; -use rustc_errors::ErrorGuaranteed; +use rustc_errors::{E0720, ErrorGuaranteed}; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, InferKind, Visitor}; use rustc_hir::{self as hir, AmbigArg, HirId}; -use rustc_middle::span_bug; +use rustc_infer::traits::solve::Goal; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, fold_regions}; -use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperFoldable}; +use rustc_middle::ty::{ + self, DefiningScopeKind, OpaqueHiddenType, Ty, TyCtxt, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + fold_regions, +}; use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; +use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid; use rustc_trait_selection::solve; use tracing::{debug, instrument}; @@ -24,15 +36,6 @@ use crate::FnCtxt; /////////////////////////////////////////////////////////////////////////// // Entry point -// During type inference, partially inferred types are -// represented using Type variables (ty::Infer). These don't appear in -// the final TypeckResults since all of the types should have been -// inferred once typeck is done. -// When type inference is running however, having to update the typeck -// typeck results every time a new type is inferred would be unreasonably slow, -// so instead all of the replacement happens at the end in -// resolve_type_vars_in_body, which creates a new TypeTables which -// doesn't contain any inference types. impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn resolve_type_vars_in_body( &self, @@ -81,23 +84,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports); wbcx.typeck_results.used_trait_imports = used_trait_imports; - wbcx.typeck_results.treat_byte_string_as_slice = - mem::take(&mut self.typeck_results.borrow_mut().treat_byte_string_as_slice); - debug!("writeback: typeck results for {:?} are {:#?}", item_def_id, wbcx.typeck_results); self.tcx.arena.alloc(wbcx.typeck_results) } } -/////////////////////////////////////////////////////////////////////////// -// The Writeback context. This visitor walks the HIR, checking the -// fn-specific typeck results to find references to types or regions. It -// resolves those regions to remove inference variables and writes the -// final result back into the master typeck results in the tcx. Here and -// there, it applies a few ad-hoc checks that were not convenient to -// do elsewhere. - +/// The Writeback context. This visitor walks the HIR, checking the +/// fn-specific typeck results to find inference variables. It resolves +/// those inference variables and writes the final result into the +/// `TypeckResults`. It also applies a few ad-hoc checks that were not +/// convenient to do elsewhere. struct WritebackCx<'cx, 'tcx> { fcx: &'cx FnCtxt<'cx, 'tcx>, @@ -160,7 +157,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.node_args_mut().remove(e.hir_id); } } - hir::ExprKind::Binary(ref op, lhs, rhs) | hir::ExprKind::AssignOp(ref op, lhs, rhs) => { + hir::ExprKind::Binary(ref op, lhs, rhs) => { let lhs_ty = self.typeck_results.node_type(lhs.hir_id); let rhs_ty = self.typeck_results.node_type(rhs.hir_id); @@ -168,25 +165,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); self.typeck_results.node_args_mut().remove(e.hir_id); - match e.kind { - hir::ExprKind::Binary(..) => { - if !op.node.is_by_value() { - let mut adjustments = self.typeck_results.adjustments_mut(); - if let Some(a) = adjustments.get_mut(lhs.hir_id) { - a.pop(); - } - if let Some(a) = adjustments.get_mut(rhs.hir_id) { - a.pop(); - } - } + if !op.node.is_by_value() { + let mut adjustments = self.typeck_results.adjustments_mut(); + if let Some(a) = adjustments.get_mut(lhs.hir_id) { + a.pop(); } - hir::ExprKind::AssignOp(..) - if let Some(a) = - self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) => - { + if let Some(a) = adjustments.get_mut(rhs.hir_id) { a.pop(); } - _ => {} + } + } + } + hir::ExprKind::AssignOp(_, lhs, rhs) => { + let lhs_ty = self.typeck_results.node_type(lhs.hir_id); + let rhs_ty = self.typeck_results.node_type(rhs.hir_id); + + if lhs_ty.is_scalar() && rhs_ty.is_scalar() { + self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); + self.typeck_results.node_args_mut().remove(e.hir_id); + + if let Some(a) = self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) { + a.pop(); } } } @@ -491,7 +490,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { if let ty::UserTypeKind::TypeOf(_, user_args) = c_ty.value.kind { // This is a unit-testing mechanism. - let span = self.tcx().hir().span(hir_id); + let span = self.tcx().hir_span(hir_id); // We need to buffer the errors in order to guarantee a consistent // order when emitting them. let err = @@ -511,15 +510,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.user_provided_types_mut().extend( fcx_typeck_results.user_provided_types().items().map(|(local_id, c_ty)| { let hir_id = HirId { owner: common_hir_owner, local_id }; - - if cfg!(debug_assertions) && c_ty.has_infer() { - span_bug!( - hir_id.to_span(self.fcx.tcx), - "writeback: `{:?}` has inference variables", - c_ty - ); - }; - (hir_id, *c_ty) }), ); @@ -530,17 +520,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); self.typeck_results.user_provided_sigs.extend_unord( - fcx_typeck_results.user_provided_sigs.items().map(|(&def_id, c_sig)| { - if cfg!(debug_assertions) && c_sig.has_infer() { - span_bug!( - self.fcx.tcx.def_span(def_id), - "writeback: `{:?}` has inference variables", - c_sig - ); - }; - - (def_id, *c_sig) - }), + fcx_typeck_results.user_provided_sigs.items().map(|(def_id, c_sig)| (*def_id, *c_sig)), ); } @@ -548,22 +528,21 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let fcx_typeck_results = self.fcx.typeck_results.borrow(); assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); for (predicate, cause) in &fcx_typeck_results.coroutine_stalled_predicates { - let (predicate, cause) = self.resolve((*predicate, cause.clone()), &cause.span); + let (predicate, cause) = + self.resolve_coroutine_predicate((*predicate, cause.clone()), &cause.span); self.typeck_results.coroutine_stalled_predicates.insert((predicate, cause)); } } #[instrument(skip(self), level = "debug")] fn visit_opaque_types(&mut self) { + let tcx = self.tcx(); // We clone the opaques instead of stealing them here as they are still used for // normalization in the next generation trait solver. - // - // FIXME(-Znext-solver): Opaque types defined after this would simply get dropped - // at the end of typeck. While this seems unlikely to happen in practice this - // should still get fixed. Either by preventing writeback from defining new opaque - // types or by using this function at the end of writeback and running it as a - // fixpoint. let opaque_types = self.fcx.infcx.clone_opaque_types(); + let num_entries = self.fcx.inner.borrow_mut().opaque_types().num_entries(); + let prev = self.fcx.checked_opaque_types_storage_entries.replace(Some(num_entries)); + debug_assert_eq!(prev, None); for (opaque_type_key, hidden_type) in opaque_types { let hidden_type = self.resolve(hidden_type, &hidden_type.span); let opaque_type_key = self.resolve(opaque_type_key, &hidden_type.span); @@ -577,18 +556,78 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } - // Here we only detect impl trait definition conflicts when they - // are equal modulo regions. - if let Some(last_opaque_ty) = - self.typeck_results.concrete_opaque_types.insert(opaque_type_key, hidden_type) - && last_opaque_ty.ty != hidden_type.ty + if let Err(err) = check_opaque_type_parameter_valid( + &self.fcx, + opaque_type_key, + hidden_type.span, + DefiningScopeKind::HirTypeck, + ) { + self.typeck_results.concrete_opaque_types.insert( + opaque_type_key.def_id, + ty::OpaqueHiddenType::new_error(tcx, err.report(self.fcx)), + ); + } + + let hidden_type = hidden_type.remap_generic_params_to_declaration_params( + opaque_type_key, + tcx, + DefiningScopeKind::HirTypeck, + ); + + if let Some(prev) = self + .typeck_results + .concrete_opaque_types + .insert(opaque_type_key.def_id, hidden_type) { - assert!(!self.fcx.next_trait_solver()); - if let Ok(d) = hidden_type.build_mismatch_error(&last_opaque_ty, self.tcx()) { - d.emit(); + let entry = &mut self + .typeck_results + .concrete_opaque_types + .get_mut(&opaque_type_key.def_id) + .unwrap(); + if prev.ty != hidden_type.ty { + if let Some(guar) = self.typeck_results.tainted_by_errors { + entry.ty = Ty::new_error(tcx, guar); + } else { + let (Ok(guar) | Err(guar)) = + prev.build_mismatch_error(&hidden_type, tcx).map(|d| d.emit()); + entry.ty = Ty::new_error(tcx, guar); + } } + + // Pick a better span if there is one. + // FIXME(oli-obk): collect multiple spans for better diagnostics down the road. + entry.span = prev.span.substitute_dummy(hidden_type.span); } } + + let recursive_opaques: Vec<_> = self + .typeck_results + .concrete_opaque_types + .iter() + .filter(|&(&def_id, hidden_ty)| { + hidden_ty + .ty + .visit_with(&mut HasRecursiveOpaque { + def_id, + seen: Default::default(), + opaques: &self.typeck_results.concrete_opaque_types, + tcx, + }) + .is_break() + }) + .map(|(def_id, hidden_ty)| (*def_id, hidden_ty.span)) + .collect(); + for (def_id, span) in recursive_opaques { + let guar = self + .fcx + .dcx() + .struct_span_err(span, "cannot resolve opaque type") + .with_code(E0720) + .emit(); + self.typeck_results + .concrete_opaque_types + .insert(def_id, OpaqueHiddenType { span, ty: Ty::new_error(tcx, guar) }); + } } fn visit_field_id(&mut self, hir_id: HirId) { @@ -730,7 +769,55 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { T: TypeFoldable>, { let value = self.fcx.resolve_vars_if_possible(value); - let value = value.fold_with(&mut Resolver::new(self.fcx, span, self.body)); + + let mut goals = vec![]; + let value = + value.fold_with(&mut Resolver::new(self.fcx, span, self.body, true, &mut goals)); + + // Ensure that we resolve goals we get from normalizing coroutine interiors, + // but we shouldn't expect those goals to need normalizing (or else we'd get + // into a somewhat awkward fixpoint situation, and we don't need it anyways). + let mut unexpected_goals = vec![]; + self.typeck_results.coroutine_stalled_predicates.extend( + goals + .into_iter() + .map(|pred| { + self.fcx.resolve_vars_if_possible(pred).fold_with(&mut Resolver::new( + self.fcx, + span, + self.body, + false, + &mut unexpected_goals, + )) + }) + // FIXME: throwing away the param-env :( + .map(|goal| (goal.predicate, self.fcx.misc(span.to_span(self.fcx.tcx)))), + ); + assert_eq!(unexpected_goals, vec![]); + + assert!(!value.has_infer()); + + // We may have introduced e.g. `ty::Error`, if inference failed, make sure + // to mark the `TypeckResults` as tainted in that case, so that downstream + // users of the typeck results don't produce extra errors, or worse, ICEs. + if let Err(guar) = value.error_reported() { + self.typeck_results.tainted_by_errors = Some(guar); + } + + value + } + + fn resolve_coroutine_predicate(&mut self, value: T, span: &dyn Locatable) -> T + where + T: TypeFoldable>, + { + let value = self.fcx.resolve_vars_if_possible(value); + + let mut goals = vec![]; + let value = + value.fold_with(&mut Resolver::new(self.fcx, span, self.body, false, &mut goals)); + assert_eq!(goals, vec![]); + assert!(!value.has_infer()); // We may have introduced e.g. `ty::Error`, if inference failed, make sure @@ -756,7 +843,7 @@ impl Locatable for Span { impl Locatable for HirId { fn to_span(&self, tcx: TyCtxt<'_>) -> Span { - tcx.hir().span(*self) + tcx.hir_span(*self) } } @@ -767,6 +854,7 @@ struct Resolver<'cx, 'tcx> { /// Whether we should normalize using the new solver, disabled /// both when using the old solver and when resolving predicates. should_normalize: bool, + nested_goals: &'cx mut Vec>>, } impl<'cx, 'tcx> Resolver<'cx, 'tcx> { @@ -774,11 +862,13 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { fcx: &'cx FnCtxt<'cx, 'tcx>, span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, + should_normalize: bool, + nested_goals: &'cx mut Vec>>, ) -> Resolver<'cx, 'tcx> { - Resolver { fcx, span, body, should_normalize: fcx.next_trait_solver() } + Resolver { fcx, span, body, nested_goals, should_normalize } } - fn report_error(&self, p: impl Into>) -> ErrorGuaranteed { + fn report_error(&self, p: impl Into>) -> ErrorGuaranteed { if let Some(guar) = self.fcx.tainted_by_errors() { guar } else { @@ -802,23 +892,28 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { new_err: impl Fn(TyCtxt<'tcx>, ErrorGuaranteed) -> T, ) -> T where - T: Into> + TypeSuperFoldable> + Copy, + T: Into> + TypeSuperFoldable> + Copy, { let tcx = self.fcx.tcx; - // We must deeply normalize in the new solver, since later lints - // expect that types that show up in the typeck are fully - // normalized. - let mut value = if self.should_normalize { + // We must deeply normalize in the new solver, since later lints expect + // that types that show up in the typeck are fully normalized. + let mut value = if self.should_normalize && self.fcx.next_trait_solver() { let body_id = tcx.hir_body_owner_def_id(self.body.id()); let cause = ObligationCause::misc(self.span.to_span(tcx), body_id); let at = self.fcx.at(&cause, self.fcx.param_env); let universes = vec![None; outer_exclusive_binder(value).as_usize()]; - solve::deeply_normalize_with_skipped_universes(at, value, universes).unwrap_or_else( - |errors| { + match solve::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + at, value, universes, + ) { + Ok((value, goals)) => { + self.nested_goals.extend(goals); + value + } + Err(errors) => { let guar = self.fcx.err_ctxt().report_fulfillment_errors(errors); new_err(tcx, guar) - }, - ) + } + } } else { value }; @@ -864,20 +959,15 @@ impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - self.handle_term(ct, ty::Const::outer_exclusive_binder, |tcx, guar| { - ty::Const::new_error(tcx, guar) - }) - .super_fold_with(self) + self.handle_term(ct, ty::Const::outer_exclusive_binder, ty::Const::new_error) } fn fold_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - // Do not normalize predicates in the new solver. The new solver is - // supposed to handle unnormalized predicates and incorrectly normalizing - // them can be unsound, e.g. for `WellFormed` predicates. - let prev = mem::replace(&mut self.should_normalize, false); - let predicate = predicate.super_fold_with(self); - self.should_normalize = prev; - predicate + assert!( + !self.should_normalize, + "normalizing predicates in writeback is not generally sound" + ); + predicate.super_fold_with(self) } } @@ -902,3 +992,34 @@ impl<'tcx> TypeFolder> for EagerlyNormalizeConsts<'tcx> { self.tcx.try_normalize_erasing_regions(self.typing_env, ct).unwrap_or(ct) } } + +struct HasRecursiveOpaque<'a, 'tcx> { + def_id: LocalDefId, + seen: FxHashSet, + opaques: &'a FxIndexMap>, + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> TypeVisitor> for HasRecursiveOpaque<'_, 'tcx> { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + if let ty::Alias(ty::Opaque, alias_ty) = *t.kind() + && let Some(def_id) = alias_ty.def_id.as_local() + { + if self.def_id == def_id { + return ControlFlow::Break(()); + } + + if self.seen.insert(def_id) + && let Some(hidden_ty) = self.opaques.get(&def_id) + { + ty::EarlyBinder::bind(hidden_ty.ty) + .instantiate(self.tcx, alias_ty.args) + .visit_with(self)?; + } + } + + t.super_visit_with(self) + } +} diff --git a/compiler/rustc_incremental/Cargo.toml b/compiler/rustc_incremental/Cargo.toml index 4939bfb3a1cdd..db0a584188755 100644 --- a/compiler/rustc_incremental/Cargo.toml +++ b/compiler/rustc_incremental/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -rand = "0.8.4" +rand = "0.9.0" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_incremental/messages.ftl b/compiler/rustc_incremental/messages.ftl index 2a65101d360d3..bbc1fab05dfeb 100644 --- a/compiler/rustc_incremental/messages.ftl +++ b/compiler/rustc_incremental/messages.ftl @@ -93,7 +93,7 @@ incremental_undefined_clean_dirty_assertions = incremental_undefined_clean_dirty_assertions_item = clean/dirty auto-assertions not yet defined for Node::Item.node={$kind} -incremental_unknown_item = unknown item `{$name}` +incremental_unknown_rustc_clean_argument = unknown `rustc_clean` argument incremental_unrecognized_depnode = unrecognized `DepNode` variant: {$name} diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 8f2ca6babea88..0e04a2a784ec8 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -38,7 +38,7 @@ use std::fs::{self, File}; use std::io::Write; use rustc_data_structures::fx::FxIndexSet; -use rustc_data_structures::graph::implementation::{Direction, INCOMING, NodeIndex, OUTGOING}; +use rustc_data_structures::graph::linked_graph::{Direction, INCOMING, NodeIndex, OUTGOING}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::dep_graph::{ @@ -123,7 +123,7 @@ impl<'tcx> IfThisChanged<'tcx> { fn process_attrs(&mut self, def_id: LocalDefId) { let def_path_hash = self.tcx.def_path_hash(def_id.to_def_id()); let hir_id = self.tcx.local_def_id_to_hir_id(def_id); - let attrs = self.tcx.hir().attrs(hir_id); + let attrs = self.tcx.hir_attrs(hir_id); for attr in attrs { if attr.has_name(sym::rustc_if_this_changed) { let dep_node_interned = self.argument(attr); diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs index b4a207386dc44..dbc72d085be99 100644 --- a/compiler/rustc_incremental/src/errors.rs +++ b/compiler/rustc_incremental/src/errors.rs @@ -107,11 +107,10 @@ pub(crate) struct NotLoaded<'a> { } #[derive(Diagnostic)] -#[diag(incremental_unknown_item)] -pub(crate) struct UnknownItem { +#[diag(incremental_unknown_rustc_clean_argument)] +pub(crate) struct UnknownRustcCleanArgument { #[primary_span] pub span: Span, - pub name: Symbol, } #[derive(Diagnostic)] diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 563ed7614c609..299ee4876389c 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -7,7 +7,6 @@ #![doc(rust_logo)] #![feature(file_buffered)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end mod assert_dep_graph; diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index d40a0d514f6f9..64166255fa485 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -405,8 +405,7 @@ fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool { debug!("check_config: searching for cfg {:?}", value); cfg = Some(config.contains(&(value, None))); } else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) { - tcx.dcx() - .emit_err(errors::UnknownItem { span: attr.span(), name: item.name_or_empty() }); + tcx.dcx().emit_err(errors::UnknownRustcCleanArgument { span: item.span() }); } } diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs index 4c664ea6ba0f4..9cec2702a4171 100644 --- a/compiler/rustc_incremental/src/persist/file_format.rs +++ b/compiler/rustc_incremental/src/persist/file_format.rs @@ -123,7 +123,7 @@ pub(crate) fn read_file( // Check HEADER_FORMAT_VERSION { - debug_assert!(::std::mem::size_of_val(&HEADER_FORMAT_VERSION) == 2); + debug_assert!(size_of_val(&HEADER_FORMAT_VERSION) == 2); let mut header_format_version = [0u8; 2]; file.read_exact(&mut header_format_version)?; let header_format_version = diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 19cca48af6127..f0d24d27e85a3 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -108,7 +108,7 @@ use std::io::{self, ErrorKind}; use std::path::{Path, PathBuf}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use rand::{RngCore, thread_rng}; +use rand::{RngCore, rng}; use rustc_data_structures::base_n::{BaseNString, CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::svh::Svh; @@ -290,7 +290,7 @@ pub(crate) fn prepare_session_directory(sess: &Session, crate_name: Symbol) { // Try to remove the session directory we just allocated. We don't // know if there's any garbage in it from the failed copy action. - if let Err(err) = safe_remove_dir_all(&session_dir) { + if let Err(err) = std_fs::remove_dir_all(&session_dir) { sess.dcx().emit_warn(errors::DeletePartial { path: &session_dir, err }); } @@ -324,7 +324,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option) { incr_comp_session_dir.display() ); - if let Err(err) = safe_remove_dir_all(&*incr_comp_session_dir) { + if let Err(err) = std_fs::remove_dir_all(&*incr_comp_session_dir) { sess.dcx().emit_warn(errors::DeleteFull { path: &incr_comp_session_dir, err }); } @@ -445,7 +445,7 @@ fn copy_files(sess: &Session, target_dir: &Path, source_dir: &Path) -> Result PathBuf { let timestamp = timestamp_to_string(SystemTime::now()); debug!("generate_session_dir_path: timestamp = {}", timestamp); - let random_number = thread_rng().next_u32(); + let random_number = rng().next_u32(); debug!("generate_session_dir_path: random_number = {}", random_number); // Chop the first 3 characters off the timestamp. Those 3 bytes will be zero for a while. @@ -715,7 +715,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result< for directory_name in session_directories { if !lock_file_to_session_dir.items().any(|(_, dir)| *dir == directory_name) { let path = crate_directory.join(directory_name); - if let Err(err) = safe_remove_dir_all(&path) { + if let Err(err) = std_fs::remove_dir_all(&path) { sess.dcx().emit_warn(errors::InvalidGcFailed { path: &path, err }); } } @@ -821,7 +821,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result< all_except_most_recent(deletion_candidates).into_items().all(|(path, lock)| { debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); - if let Err(err) = safe_remove_dir_all(&path) { + if let Err(err) = std_fs::remove_dir_all(&path) { sess.dcx().emit_warn(errors::FinalizedGcFailed { path: &path, err }); } else { delete_session_dir_lock_file(sess, &lock_file_path(&path)); @@ -839,7 +839,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result< fn delete_old(sess: &Session, path: &Path) { debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); - if let Err(err) = safe_remove_dir_all(path) { + if let Err(err) = std_fs::remove_dir_all(path) { sess.dcx().emit_warn(errors::SessionGcFailed { path, err }); } else { delete_session_dir_lock_file(sess, &lock_file_path(path)); @@ -862,30 +862,8 @@ fn all_except_most_recent( } } -/// Since paths of artifacts within session directories can get quite long, we -/// need to support deleting files with very long paths. The regular -/// WinApi functions only support paths up to 260 characters, however. In order -/// to circumvent this limitation, we canonicalize the path of the directory -/// before passing it to std::fs::remove_dir_all(). This will convert the path -/// into the '\\?\' format, which supports much longer paths. -fn safe_remove_dir_all(p: &Path) -> io::Result<()> { - let canonicalized = match try_canonicalize(p) { - Ok(canonicalized) => canonicalized, - Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()), - Err(err) => return Err(err), - }; - - std_fs::remove_dir_all(canonicalized) -} - fn safe_remove_file(p: &Path) -> io::Result<()> { - let canonicalized = match try_canonicalize(p) { - Ok(canonicalized) => canonicalized, - Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()), - Err(err) => return Err(err), - }; - - match std_fs::remove_file(canonicalized) { + match std_fs::remove_file(p) { Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(()), result => result, } diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 50e47533ab6ca..0e646b136c452 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -91,7 +91,10 @@ fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) { work_product::delete_workproduct_files(sess, &swp.work_product); } -fn load_dep_graph(sess: &Session) -> LoadResult<(Arc, WorkProductMap)> { +fn load_dep_graph( + sess: &Session, + deps: &DepsType, +) -> LoadResult<(Arc, WorkProductMap)> { let prof = sess.prof.clone(); if sess.opts.incremental.is_none() { @@ -171,7 +174,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc, WorkPr return LoadResult::DataOutOfDate; } - let dep_graph = SerializedDepGraph::decode::(&mut decoder); + let dep_graph = SerializedDepGraph::decode::(&mut decoder, deps); LoadResult::Ok { data: (dep_graph, prev_work_products) } } @@ -205,11 +208,11 @@ pub fn load_query_result_cache(sess: &Session) -> Option { /// Setups the dependency graph by loading an existing graph from disk and set up streaming of a /// new graph to an incremental session directory. -pub fn setup_dep_graph(sess: &Session, crate_name: Symbol) -> DepGraph { +pub fn setup_dep_graph(sess: &Session, crate_name: Symbol, deps: &DepsType) -> DepGraph { // `load_dep_graph` can only be called after `prepare_session_directory`. prepare_session_directory(sess, crate_name); - let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess)); + let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess, deps)); if sess.opts.incremental.is_some() { sess.time("incr_comp_garbage_collect_session_directories", || { diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 8fc50ca1b4354..58fea3278a839 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -44,10 +44,6 @@ pub(crate) fn save_dep_graph(tcx: TyCtxt<'_>) { sess.time("assert_dep_graph", || assert_dep_graph(tcx)); sess.time("check_dirty_clean", || dirty_clean::check_dirty_clean_annotations(tcx)); - if sess.opts.unstable_opts.incremental_info { - tcx.dep_graph.print_incremental_info() - } - join( move || { sess.time("incr_comp_persist_dep_graph", || { @@ -172,12 +168,5 @@ pub(crate) fn build_dep_graph( // First encode the commandline arguments hash sess.opts.dep_tracking_hash(false).encode(&mut encoder); - Some(DepGraph::new( - &sess.prof, - prev_graph, - prev_work_products, - encoder, - sess.opts.unstable_opts.query_dep_graph, - sess.opts.unstable_opts.incremental_info, - )) + Some(DepGraph::new(sess, prev_graph, prev_work_products, encoder)) } diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 048981f0d5ce3..7b1eb0a82e397 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -3,7 +3,7 @@ //! [work products]: WorkProduct use std::fs as std_fs; -use std::path::Path; +use std::path::{Path, PathBuf}; use rustc_data_structures::unord::UnordMap; use rustc_fs_util::link_or_copy; @@ -20,6 +20,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( sess: &Session, cgu_name: &str, files: &[(&'static str, &Path)], + known_links: &[PathBuf], ) -> Option<(WorkProductId, WorkProduct)> { debug!(?cgu_name, ?files); sess.opts.incremental.as_ref()?; @@ -28,6 +29,10 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( for (ext, path) in files { let file_name = format!("{cgu_name}.{ext}"); let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); + if known_links.contains(&path_in_incr_dir) { + let _ = saved_files.insert(ext.to_string(), file_name); + continue; + } match link_or_copy(path, &path_in_incr_dir) { Ok(_) => { let _ = saved_files.insert(ext.to_string(), file_name); diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 6dc044a6d380d..07934389158e5 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -1,11 +1,13 @@ use std::marker::PhantomData; +#[cfg(not(feature = "nightly"))] +use std::mem; use std::ops::{BitAnd, BitAndAssign, BitOrAssign, Bound, Not, Range, RangeBounds, Shl}; use std::rc::Rc; -use std::{fmt, iter, mem, slice}; +use std::{fmt, iter, slice}; use Chunk::*; #[cfg(feature = "nightly")] -use rustc_macros::{Decodable_Generic, Encodable_Generic}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext}; use smallvec::{SmallVec, smallvec}; use crate::{Idx, IndexVec}; @@ -14,7 +16,7 @@ use crate::{Idx, IndexVec}; mod tests; type Word = u64; -const WORD_BYTES: usize = mem::size_of::(); +const WORD_BYTES: usize = size_of::(); const WORD_BITS: usize = WORD_BYTES * 8; // The choice of chunk size has some trade-offs. @@ -112,7 +114,7 @@ macro_rules! bit_relations_inherent_impls { /// to or greater than the domain size. All operations that involve two bitsets /// will panic if the bitsets have differing domain sizes. /// -#[cfg_attr(feature = "nightly", derive(Decodable_Generic, Encodable_Generic))] +#[cfg_attr(feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext))] #[derive(Eq, PartialEq, Hash)] pub struct DenseBitSet { domain_size: usize, @@ -1390,7 +1392,7 @@ impl From> for GrowableBitSet { /// /// All operations that involve a row and/or column index will panic if the /// index exceeds the relevant bound. -#[cfg_attr(feature = "nightly", derive(Decodable_Generic, Encodable_Generic))] +#[cfg_attr(feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext))] #[derive(Clone, Eq, PartialEq, Hash)] pub struct BitMatrix { num_rows: usize, @@ -1814,7 +1816,7 @@ impl std::fmt::Debug for FiniteBitSet { /// A fixed-sized bitset type represented by an integer type. Indices outwith than the range /// representable by `T` are considered set. -#[cfg_attr(feature = "nightly", derive(Decodable_Generic, Encodable_Generic))] +#[cfg_attr(feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext))] #[derive(Copy, Clone, Eq, PartialEq)] pub struct FiniteBitSet(pub T); diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index 3441a5f65c785..cc680838e7e7b 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -4,7 +4,6 @@ #![cfg_attr(feature = "nightly", feature(extend_one, step_trait, test))] #![cfg_attr(feature = "nightly", feature(new_range_api))] #![cfg_attr(feature = "nightly", feature(new_zeroed_alloc))] -#![warn(unreachable_pub)] // tidy-alphabetical-end pub mod bit_set; diff --git a/compiler/rustc_index/src/slice.rs b/compiler/rustc_index/src/slice.rs index f17ea9e4b59ad..d2702bdb0571d 100644 --- a/compiler/rustc_index/src/slice.rs +++ b/compiler/rustc_index/src/slice.rs @@ -1,6 +1,7 @@ use std::fmt; use std::marker::PhantomData; -use std::ops::{Index, IndexMut}; +use std::ops::{Index, IndexMut, RangeBounds}; +use std::slice::GetDisjointMutError::*; use std::slice::{self, SliceIndex}; use crate::{Idx, IndexVec, IntoSliceIdx}; @@ -65,6 +66,8 @@ impl IndexSlice { #[inline] pub fn iter_enumerated(&self) -> impl DoubleEndedIterator + ExactSizeIterator { + // Allow the optimizer to elide the bounds checking when creating each index. + let _ = I::new(self.len()); self.raw.iter().enumerate().map(|(n, t)| (I::new(n), t)) } @@ -72,6 +75,8 @@ impl IndexSlice { pub fn indices( &self, ) -> impl DoubleEndedIterator + ExactSizeIterator + Clone + 'static { + // Allow the optimizer to elide the bounds checking when creating each index. + let _ = I::new(self.len()); (0..self.len()).map(|n| I::new(n)) } @@ -84,6 +89,8 @@ impl IndexSlice { pub fn iter_enumerated_mut( &mut self, ) -> impl DoubleEndedIterator + ExactSizeIterator { + // Allow the optimizer to elide the bounds checking when creating each index. + let _ = I::new(self.len()); self.raw.iter_mut().enumerate().map(|(n, t)| (I::new(n), t)) } @@ -97,6 +104,17 @@ impl IndexSlice { self.raw.swap(a.index(), b.index()) } + #[inline] + pub fn copy_within( + &mut self, + src: impl IntoSliceIdx>, + dest: I, + ) where + T: Copy, + { + self.raw.copy_within(src.into_slice_idx(), dest.index()); + } + #[inline] pub fn get>( &self, @@ -115,32 +133,36 @@ impl IndexSlice { /// Returns mutable references to two distinct elements, `a` and `b`. /// - /// Panics if `a == b`. + /// Panics if `a == b` or if some of them are out of bounds. #[inline] pub fn pick2_mut(&mut self, a: I, b: I) -> (&mut T, &mut T) { let (ai, bi) = (a.index(), b.index()); - assert!(ai != bi); - - if ai < bi { - let (c1, c2) = self.raw.split_at_mut(bi); - (&mut c1[ai], &mut c2[0]) - } else { - let (c2, c1) = self.pick2_mut(b, a); - (c1, c2) + + match self.raw.get_disjoint_mut([ai, bi]) { + Ok([a, b]) => (a, b), + Err(OverlappingIndices) => panic!("Indices {ai:?} and {bi:?} are not disjoint!"), + Err(IndexOutOfBounds) => { + panic!("Some indices among ({ai:?}, {bi:?}) are out of bounds") + } } } /// Returns mutable references to three distinct elements. /// - /// Panics if the elements are not distinct. + /// Panics if the elements are not distinct or if some of them are out of bounds. #[inline] pub fn pick3_mut(&mut self, a: I, b: I, c: I) -> (&mut T, &mut T, &mut T) { let (ai, bi, ci) = (a.index(), b.index(), c.index()); - assert!(ai != bi && bi != ci && ci != ai); - let len = self.raw.len(); - assert!(ai < len && bi < len && ci < len); - let ptr = self.raw.as_mut_ptr(); - unsafe { (&mut *ptr.add(ai), &mut *ptr.add(bi), &mut *ptr.add(ci)) } + + match self.raw.get_disjoint_mut([ai, bi, ci]) { + Ok([a, b, c]) => (a, b, c), + Err(OverlappingIndices) => { + panic!("Indices {ai:?}, {bi:?} and {ci:?} are not disjoint!") + } + Err(IndexOutOfBounds) => { + panic!("Some indices among ({ai:?}, {bi:?}, {ci:?}) are out of bounds") + } + } } #[inline] diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index 7f3f3ead5f2af..13f0dda180be9 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs @@ -93,6 +93,8 @@ impl IndexVec { /// be allocated only once, with a capacity of at least `n`.) #[inline] pub fn from_fn_n(func: impl FnMut(I) -> T, n: usize) -> Self { + // Allow the optimizer to elide the bounds checking when creating each index. + let _ = I::new(n); IndexVec::from_raw((0..n).map(I::new).map(func).collect()) } @@ -128,6 +130,8 @@ impl IndexVec { pub fn into_iter_enumerated( self, ) -> impl DoubleEndedIterator + ExactSizeIterator { + // Allow the optimizer to elide the bounds checking when creating each index. + let _ = I::new(self.len()); self.raw.into_iter().enumerate().map(|(n, t)| (I::new(n), t)) } diff --git a/compiler/rustc_index/src/vec/tests.rs b/compiler/rustc_index/src/vec/tests.rs index 381d79c24fcba..5b0ca4b2b924c 100644 --- a/compiler/rustc_index/src/vec/tests.rs +++ b/compiler/rustc_index/src/vec/tests.rs @@ -9,8 +9,6 @@ crate::newtype_index! { #[test] fn index_size_is_optimized() { - use std::mem::size_of; - assert_eq!(size_of::(), 4); // Uses 0xFFFF_FFFB assert_eq!(size_of::>(), 4); diff --git a/compiler/rustc_index_macros/src/newtype.rs b/compiler/rustc_index_macros/src/newtype.rs index 67ec776113399..eedbe630cf2c4 100644 --- a/compiler/rustc_index_macros/src/newtype.rs +++ b/compiler/rustc_index_macros/src/newtype.rs @@ -257,6 +257,13 @@ impl Parse for Newtype { } } + impl std::ops::AddAssign for #name { + #[inline] + fn add_assign(&mut self, other: usize) { + *self = *self + other; + } + } + impl rustc_index::Idx for #name { #[inline] fn new(value: usize) -> Self { @@ -305,7 +312,7 @@ impl Parse for Newtype { } } -pub fn newtype(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub(crate) fn newtype(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as Newtype); input.0.into() } diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index c47f27e871f7a..a1a0926cd8188 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -8,9 +8,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_index::Idx; use rustc_middle::bug; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{ - self, BoundVar, GenericArg, InferConst, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt, + self, BoundVar, GenericArg, InferConst, List, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, }; use smallvec::SmallVec; use tracing::debug; @@ -159,7 +159,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { ) -> ty::Region<'tcx> { let infcx = canonicalizer.infcx.unwrap(); - if let ty::ReVar(vid) = *r { + if let ty::ReVar(vid) = r.kind() { r = infcx .inner .borrow_mut() @@ -171,7 +171,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { ); }; - match *r { + match r.kind() { ty::ReLateParam(_) | ty::ReErased | ty::ReStatic | ty::ReEarlyParam(..) => r, ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( @@ -227,7 +227,7 @@ impl CanonicalizeMode for CanonicalizeUserTypeAnnotation { canonicalizer: &mut Canonicalizer<'_, 'tcx>, r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { - match *r { + match r.kind() { ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReErased @@ -321,7 +321,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { + match r.kind() { ty::ReBound(index, ..) => { if index >= self.binder_index { bug!("escaping late-bound region during canonicalization"); diff --git a/compiler/rustc_infer/src/infer/canonical/instantiate.rs b/compiler/rustc_infer/src/infer/canonical/instantiate.rs index c10df2ec02e1d..f5ee5702d09cc 100644 --- a/compiler/rustc_infer/src/infer/canonical/instantiate.rs +++ b/compiler/rustc_infer/src/infer/canonical/instantiate.rs @@ -8,8 +8,7 @@ use rustc_macros::extension; use rustc_middle::bug; -use rustc_middle::ty::fold::{FnMutDelegate, TypeFoldable}; -use rustc_middle::ty::{self, GenericArgKind, TyCtxt}; +use rustc_middle::ty::{self, FnMutDelegate, GenericArgKind, TyCtxt, TypeFoldable}; use crate::infer::canonical::{Canonical, CanonicalVarValues}; diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index fb5fc3a53fe61..3be07dbe208f9 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -24,8 +24,7 @@ pub use instantiate::CanonicalExt; use rustc_index::IndexVec; pub use rustc_middle::infer::canonical::*; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::{self, GenericArg, List, Ty, TyCtxt}; +use rustc_middle::ty::{self, GenericArg, List, Ty, TyCtxt, TypeFoldable}; use rustc_span::Span; use crate::infer::{InferCtxt, RegionVariableOrigin}; diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 8a61631c69c0b..1ae864c454f28 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -13,8 +13,7 @@ use std::iter; use rustc_index::{Idx, IndexVec}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::{self, BoundVar, GenericArg, GenericArgKind, Ty, TyCtxt}; +use rustc_middle::ty::{self, BoundVar, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use rustc_middle::{bug, span_bug}; use tracing::{debug, instrument}; @@ -133,7 +132,13 @@ impl<'tcx> InferCtxt<'tcx> { let certainty = if errors.is_empty() { Certainty::Proven } else { Certainty::Ambiguous }; - let opaque_types = self.take_opaque_types_for_query_response(); + let opaque_types = self + .inner + .borrow_mut() + .opaque_type_storage + .take_opaque_types() + .map(|(k, v)| (k, v.ty)) + .collect(); Ok(QueryResponse { var_values: inference_vars, @@ -144,24 +149,6 @@ impl<'tcx> InferCtxt<'tcx> { }) } - /// Used by the new solver as that one takes the opaque types at the end of a probe - /// to deal with multiple candidates without having to recompute them. - pub fn clone_opaque_types_for_query_response( - &self, - ) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { - self.inner - .borrow() - .opaque_type_storage - .opaque_types - .iter() - .map(|(k, v)| (*k, v.ty)) - .collect() - } - - fn take_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { - self.take_opaque_types().into_iter().map(|(k, v)| (k, v.ty)).collect() - } - /// Given the (canonicalized) result to a canonical query, /// instantiates the result so it can be used, plugging in the /// values from the canonical query. (Note that the result may @@ -433,7 +420,7 @@ impl<'tcx> InferCtxt<'tcx> { } GenericArgKind::Lifetime(result_value) => { // e.g., here `result_value` might be `'?1` in the example above... - if let ty::ReBound(debruijn, br) = *result_value { + if let ty::ReBound(debruijn, br) = result_value.kind() { // ... in which case we would set `canonical_vars[0]` to `Some('static)`. // We only allow a `ty::INNERMOST` index in generic parameters. diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index eae69ec3e0fe8..359b9da11ced6 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -1,13 +1,15 @@ ///! Definition of `InferCtxtLike` from the librarified type layer. use rustc_hir::def_id::DefId; use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::relate::combine::PredicateEmittingRelation; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; -use super::{BoundRegionConversionTime, InferCtxt, RegionVariableOrigin, SubregionOrigin}; +use super::{ + BoundRegionConversionTime, InferCtxt, OpaqueTypeStorageEntries, RegionVariableOrigin, + SubregionOrigin, +}; impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { type Interner = TyCtxt<'tcx>; @@ -122,19 +124,19 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.enter_forall(value, f) } - fn equate_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) { + fn equate_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { self.inner.borrow_mut().type_variables().equate(a, b); } - fn equate_int_vids_raw(&self, a: rustc_type_ir::IntVid, b: rustc_type_ir::IntVid) { + fn equate_int_vids_raw(&self, a: ty::IntVid, b: ty::IntVid) { self.inner.borrow_mut().int_unification_table().union(a, b); } - fn equate_float_vids_raw(&self, a: rustc_type_ir::FloatVid, b: rustc_type_ir::FloatVid) { + fn equate_float_vids_raw(&self, a: ty::FloatVid, b: ty::FloatVid) { self.inner.borrow_mut().float_unification_table().union(a, b); } - fn equate_const_vids_raw(&self, a: rustc_type_ir::ConstVid, b: rustc_type_ir::ConstVid) { + fn equate_const_vids_raw(&self, a: ty::ConstVid, b: ty::ConstVid) { self.inner.borrow_mut().const_unification_table().union(a, b); } @@ -142,8 +144,8 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { &self, relation: &mut R, target_is_expected: bool, - target_vid: rustc_type_ir::TyVid, - instantiation_variance: rustc_type_ir::Variance, + target_vid: ty::TyVid, + instantiation_variance: ty::Variance, source_ty: Ty<'tcx>, ) -> RelateResult<'tcx, ()> { self.instantiate_ty_var( @@ -155,19 +157,11 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { ) } - fn instantiate_int_var_raw( - &self, - vid: rustc_type_ir::IntVid, - value: rustc_type_ir::IntVarValue, - ) { + fn instantiate_int_var_raw(&self, vid: ty::IntVid, value: ty::IntVarValue) { self.inner.borrow_mut().int_unification_table().union_value(vid, value); } - fn instantiate_float_var_raw( - &self, - vid: rustc_type_ir::FloatVid, - value: rustc_type_ir::FloatVarValue, - ) { + fn instantiate_float_var_raw(&self, vid: ty::FloatVid, value: ty::FloatVarValue) { self.inner.borrow_mut().float_unification_table().union_value(vid, value); } @@ -175,7 +169,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { &self, relation: &mut R, target_is_expected: bool, - target_vid: rustc_type_ir::ConstVid, + target_vid: ty::ConstVid, source_ct: ty::Const<'tcx>, ) -> RelateResult<'tcx, ()> { self.instantiate_const_var(relation, target_is_expected, target_vid, source_ct) @@ -222,4 +216,58 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>, span: Span) { self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(span)); } + + type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries; + fn opaque_types_storage_num_entries(&self) -> OpaqueTypeStorageEntries { + self.inner.borrow_mut().opaque_types().num_entries() + } + fn clone_opaque_types_lookup_table(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { + self.inner.borrow_mut().opaque_types().iter_lookup_table().map(|(k, h)| (k, h.ty)).collect() + } + fn clone_duplicate_opaque_types(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { + self.inner + .borrow_mut() + .opaque_types() + .iter_duplicate_entries() + .map(|(k, h)| (k, h.ty)) + .collect() + } + fn clone_opaque_types_added_since( + &self, + prev_entries: OpaqueTypeStorageEntries, + ) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { + self.inner + .borrow_mut() + .opaque_types() + .opaque_types_added_since(prev_entries) + .map(|(k, h)| (k, h.ty)) + .collect() + } + + fn register_hidden_type_in_storage( + &self, + opaque_type_key: ty::OpaqueTypeKey<'tcx>, + hidden_ty: Ty<'tcx>, + span: Span, + ) -> Option> { + self.register_hidden_type_in_storage( + opaque_type_key, + ty::OpaqueHiddenType { span, ty: hidden_ty }, + ) + } + fn add_duplicate_opaque_type( + &self, + opaque_type_key: ty::OpaqueTypeKey<'tcx>, + hidden_ty: Ty<'tcx>, + span: Span, + ) { + self.inner + .borrow_mut() + .opaque_types() + .add_duplicate(opaque_type_key, ty::OpaqueHiddenType { span, ty: hidden_ty }) + } + + fn reset_opaque_types(&self) { + let _ = self.take_opaque_types(); + } } diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index 74c8b463fc899..cae674165f0ef 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -35,8 +35,9 @@ use std::collections::hash_map::Entry; use rustc_data_structures::fx::FxHashMap; use rustc_middle::bug; -use rustc_middle::ty::fold::TypeFolder; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitableExt}; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, +}; use super::InferCtxt; @@ -108,7 +109,7 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { + match r.kind() { ty::ReBound(..) => { // leave bound regions alone r diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 03c4614af139b..2185886901e57 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -3,16 +3,15 @@ use std::fmt; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::graph::implementation::{ - Direction, Graph, INCOMING, NodeIndex, OUTGOING, +use rustc_data_structures::graph::linked_graph::{ + Direction, INCOMING, LinkedGraph, NodeIndex, OUTGOING, }; use rustc_data_structures::intern::Interned; use rustc_data_structures::unord::UnordSet; use rustc_index::{IndexSlice, IndexVec}; -use rustc_middle::ty::fold::{TypeFoldable, fold_regions}; use rustc_middle::ty::{ self, ReBound, ReEarlyParam, ReErased, ReError, ReLateParam, RePlaceholder, ReStatic, ReVar, - Region, RegionVid, Ty, TyCtxt, + Region, RegionVid, Ty, TyCtxt, TypeFoldable, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_span::Span; @@ -119,7 +118,7 @@ struct RegionAndOrigin<'tcx> { origin: SubregionOrigin<'tcx>, } -type RegionGraph<'tcx> = Graph<(), Constraint<'tcx>>; +type RegionGraph<'tcx> = LinkedGraph<(), Constraint<'tcx>>; struct LexicalResolver<'cx, 'tcx> { region_rels: &'cx RegionRelations<'cx, 'tcx>, @@ -219,7 +218,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { true } VarValue::Value(cur_region) => { - match *cur_region { + match cur_region.kind() { // If this empty region is from a universe that can name the // placeholder universe, then the LUB is the Placeholder region // (which is the cur_region). Otherwise, the LUB is the Static @@ -311,7 +310,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { match *b_data { VarValue::Empty(empty_ui) => { - let lub = match *a_region { + let lub = match a_region.kind() { RePlaceholder(placeholder) => { // If this empty region is from a universe that can // name the placeholder, then the placeholder is @@ -351,7 +350,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // tighter bound than `'static`. // // (This might e.g. arise from being asked to prove `for<'a> { 'b: 'a }`.) - if let ty::RePlaceholder(p) = *lub + if let ty::RePlaceholder(p) = lub.kind() && b_universe.cannot_name(p.universe) { lub = self.tcx().lifetimes.re_static; @@ -378,7 +377,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { a_ui.min(b_ui) == b_ui } (VarValue::Value(a), VarValue::Empty(_)) => { - match *a { + match a.kind() { // this is always on an error path, // so it doesn't really matter if it's shorter or longer than an empty region ReError(_) => false, @@ -411,7 +410,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } (VarValue::Empty(a_ui), VarValue::Value(b)) => { - match *b { + match b.kind() { // this is always on an error path, // so it doesn't really matter if it's shorter or longer than an empty region ReError(_) => false, @@ -480,7 +479,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// term "concrete regions"). #[instrument(level = "trace", skip(self), ret)] fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { - match (*a, *b) { + match (a.kind(), b.kind()) { (ReBound(..), _) | (_, ReBound(..)) | (ReErased, _) | (_, ReErased) => { bug!("cannot relate region: LUB({:?}, {:?})", a, b); } @@ -669,7 +668,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { fn construct_graph(&self) -> RegionGraph<'tcx> { let num_vars = self.num_vars(); - let mut graph = Graph::new(); + let mut graph = LinkedGraph::new(); for _ in 0..num_vars { graph.add_node(()); @@ -726,7 +725,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // SubSupConflict(ReLateParam, ReLateParam) when reporting error, and so // the user will more likely get a specific suggestion. fn region_order_key(x: &RegionAndOrigin<'_>) -> u8 { - match *x.region { + match x.region.kind() { ReEarlyParam(_) => 0, ReLateParam(_) => 1, _ => 2, @@ -738,7 +737,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let node_universe = self.var_infos[node_idx].universe; for lower_bound in &lower_bounds { - let effective_lower_bound = if let ty::RePlaceholder(p) = *lower_bound.region { + let effective_lower_bound = if let ty::RePlaceholder(p) = lower_bound.region.kind() { if node_universe.cannot_name(p.universe) { self.tcx().lifetimes.re_static } else { @@ -786,7 +785,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { .expect("lower_vid_bounds should at least include `node_idx`"); for upper_bound in &upper_bounds { - if let ty::RePlaceholder(p) = *upper_bound.region { + if let ty::RePlaceholder(p) = upper_bound.region.kind() { if min_universe.cannot_name(p.universe) { let origin = self.var_infos[node_idx].origin; errors.push(RegionResolutionError::UpperBoundUniverseConflict( @@ -914,7 +913,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { generic_ty: Ty<'tcx>, min: ty::Region<'tcx>, ) -> bool { - if let ty::ReError(_) = *min { + if let ty::ReError(_) = min.kind() { return true; } @@ -932,18 +931,18 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } VerifyBound::OutlivedBy(r) => { - let a = match *min { + let a = match min.kind() { ty::ReVar(rid) => var_values.values[rid], _ => VarValue::Value(min), }; - let b = match **r { + let b = match r.kind() { ty::ReVar(rid) => var_values.values[rid], _ => VarValue::Value(*r), }; self.sub_region_values(a, b) } - VerifyBound::IsEmpty => match *min { + VerifyBound::IsEmpty => match min.kind() { ty::ReVar(rid) => match var_values.values[rid] { VarValue::ErrorValue => false, VarValue::Empty(_) => true, @@ -990,7 +989,7 @@ impl<'tcx> LexicalRegionResolutions<'tcx> { tcx: TyCtxt<'tcx>, r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { - let result = match *r { + let result = match r.kind() { ty::ReVar(rid) => match self.values[rid] { VarValue::Empty(_) => r, VarValue::Value(r) => r, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 537e8119adaf6..b408d76010d7b 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -9,7 +9,7 @@ use free_regions::RegionRelations; pub use freshen::TypeFreshener; use lexical_region_resolve::LexicalRegionResolutions; pub use lexical_region_resolve::RegionResolutionError; -use opaque_types::OpaqueTypeStorage; +pub use opaque_types::{OpaqueTypeStorage, OpaqueTypeStorageEntries, OpaqueTypeTable}; use region_constraints::{ GenericKind, RegionConstraintCollector, RegionConstraintStorage, VarInfos, VerifyBound, }; @@ -27,15 +27,13 @@ use rustc_middle::bug; use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::select; +use rustc_middle::traits::solve::Goal; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::fold::{ - BoundVarReplacerDelegate, TypeFoldable, TypeFolder, TypeSuperFoldable, fold_regions, -}; -use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{ - self, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, GenericArgsRef, - GenericParamDefKind, InferConst, IntVid, PseudoCanonicalInput, Ty, TyCtxt, TyVid, - TypeVisitable, TypingEnv, TypingMode, + self, BoundVarReplacerDelegate, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, + GenericArgsRef, GenericParamDefKind, InferConst, IntVid, OpaqueHiddenType, OpaqueTypeKey, + PseudoCanonicalInput, Term, TermKind, Ty, TyCtxt, TyVid, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypingEnv, TypingMode, fold_regions, }; use rustc_span::{Span, Symbol}; use snapshot::undo_log::InferCtxtUndoLogs; @@ -200,7 +198,7 @@ impl<'tcx> InferCtxtInner<'tcx> { } #[inline] - fn opaque_types(&mut self) -> opaque_types::OpaqueTypeTable<'_, 'tcx> { + pub fn opaque_types(&mut self) -> opaque_types::OpaqueTypeTable<'_, 'tcx> { self.opaque_type_storage.with_log(&mut self.undo_log) } @@ -226,15 +224,6 @@ impl<'tcx> InferCtxtInner<'tcx> { .expect("region constraints already solved") .with_log(&mut self.undo_log) } - - // Iterates through the opaque type definitions without taking them; this holds the - // `InferCtxtInner` lock, so make sure to not do anything with `InferCtxt` side-effects - // while looping through this. - pub fn iter_opaque_types( - &self, - ) -> impl Iterator, ty::OpaqueHiddenType<'tcx>)> { - self.opaque_type_storage.opaque_types.iter().map(|(&k, &v)| (k, v)) - } } pub struct InferCtxt<'tcx> { @@ -271,7 +260,7 @@ pub struct InferCtxt<'tcx> { /// The set of predicates on which errors have been reported, to /// avoid reporting the same error twice. pub reported_trait_errors: - RefCell>, ErrorGuaranteed)>>, + RefCell>>, ErrorGuaranteed)>>, pub reported_signature_mismatch: RefCell)>>, @@ -956,20 +945,23 @@ impl<'tcx> InferCtxt<'tcx> { } #[instrument(level = "debug", skip(self), ret)] - pub fn take_opaque_types(&self) -> opaque_types::OpaqueTypeMap<'tcx> { - std::mem::take(&mut self.inner.borrow_mut().opaque_type_storage.opaque_types) + pub fn take_opaque_types(&self) -> Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)> { + self.inner.borrow_mut().opaque_type_storage.take_opaque_types().collect() } #[instrument(level = "debug", skip(self), ret)] - pub fn clone_opaque_types(&self) -> opaque_types::OpaqueTypeMap<'tcx> { - self.inner.borrow().opaque_type_storage.opaque_types.clone() + pub fn clone_opaque_types(&self) -> Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)> { + self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect() } #[inline(always)] pub fn can_define_opaque_ty(&self, id: impl Into) -> bool { debug_assert!(!self.next_trait_solver()); match self.typing_mode() { - TypingMode::Analysis { defining_opaque_types } => { + TypingMode::Analysis { + defining_opaque_types_and_generators: defining_opaque_types, + } + | TypingMode::Borrowck { defining_opaque_types } => { id.into().as_local().is_some_and(|def_id| defining_opaque_types.contains(&def_id)) } // FIXME(#132279): This function is quite weird in post-analysis @@ -1263,7 +1255,8 @@ impl<'tcx> InferCtxt<'tcx> { // to handle them without proper canonicalization. This means we may cause cycle // errors and fail to reveal opaques while inside of bodies. We should rename this // function and require explicit comments on all use-sites in the future. - ty::TypingMode::Analysis { defining_opaque_types: _ } => { + ty::TypingMode::Analysis { defining_opaque_types_and_generators: _ } + | ty::TypingMode::Borrowck { defining_opaque_types: _ } => { TypingMode::non_body_analysis() } mode @ (ty::TypingMode::Coherence @@ -1399,6 +1392,16 @@ impl<'tcx> TyOrConstInferVar { } } + /// 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_term(term: Term<'tcx>) -> Option { + match term.unpack() { + TermKind::Ty(ty) => Self::maybe_from_ty(ty), + TermKind::Const(ct) => Self::maybe_from_const(ct), + } + } + /// Tries to extract an inference variable from a type, returns `None` /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`). fn maybe_from_ty(ty: Ty<'tcx>) -> Option { diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index f6ef3f40e622d..220d5e9bda2d1 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -1,26 +1,24 @@ use hir::def_id::{DefId, LocalDefId}; -use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_middle::bug; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::solve::Goal; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::{ - self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, + self, BottomUpFolder, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, + TypeVisitableExt, }; use rustc_span::Span; use tracing::{debug, instrument}; -use super::DefineOpaqueTypes; +use super::{DefineOpaqueTypes, RegionVariableOrigin}; use crate::errors::OpaqueHiddenTypeDiag; use crate::infer::{InferCtxt, InferOk}; use crate::traits::{self, Obligation, PredicateObligations}; mod table; -pub(crate) type OpaqueTypeMap<'tcx> = FxIndexMap, OpaqueHiddenType<'tcx>>; -pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable}; +pub use table::{OpaqueTypeStorage, OpaqueTypeStorageEntries, OpaqueTypeTable}; impl<'tcx> InferCtxt<'tcx> { /// This is a backwards compatibility hack to prevent breaking changes from @@ -198,13 +196,12 @@ impl<'tcx> InferCtxt<'tcx> { /// it hasn't previously been defined. This does not emit any /// constraints and it's the responsibility of the caller to make /// sure that the item bounds of the opaque are checked. - pub fn inject_new_hidden_type_unchecked( + pub fn register_hidden_type_in_storage( &self, opaque_type_key: OpaqueTypeKey<'tcx>, hidden_ty: OpaqueHiddenType<'tcx>, - ) { - let prev = self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty); - assert_eq!(prev, None); + ) -> Option> { + self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty) } /// Insert a hidden type into the opaque type storage, equating it @@ -222,6 +219,7 @@ impl<'tcx> InferCtxt<'tcx> { hidden_ty: Ty<'tcx>, goals: &mut Vec>>, ) -> Result<(), TypeError<'tcx>> { + let tcx = self.tcx; // Ideally, we'd get the span where *this specific `ty` came // from*, but right now we just use the span from the overall // value being folded. In simple cases like `-> impl Foo`, @@ -232,7 +230,7 @@ impl<'tcx> InferCtxt<'tcx> { // During intercrate we do not define opaque types but instead always // force ambiguity unless the hidden type is known to not implement // our trait. - goals.push(Goal::new(self.tcx, param_env, ty::PredicateKind::Ambiguous)); + goals.push(Goal::new(tcx, param_env, ty::PredicateKind::Ambiguous)); } ty::TypingMode::Analysis { .. } => { let prev = self @@ -246,11 +244,40 @@ impl<'tcx> InferCtxt<'tcx> { .eq(DefineOpaqueTypes::Yes, prev, hidden_ty)? .obligations .into_iter() - // FIXME: Shuttling between obligations and goals is awkward. - .map(Goal::from), + .map(|obligation| obligation.as_goal()), ); } } + ty::TypingMode::Borrowck { .. } => { + let prev = self + .inner + .borrow_mut() + .opaque_types() + .register(opaque_type_key, OpaqueHiddenType { ty: hidden_ty, span }); + + // We either equate the new hidden type with the previous entry or with the type + // inferred by HIR typeck. + let actual = prev.unwrap_or_else(|| { + let actual = tcx + .type_of_opaque_hir_typeck(opaque_type_key.def_id) + .instantiate(self.tcx, opaque_type_key.args); + let actual = ty::fold_regions(tcx, actual, |re, _dbi| match re.kind() { + ty::ReErased => { + self.next_region_var(RegionVariableOrigin::MiscVariable(span)) + } + _ => re, + }); + actual + }); + + goals.extend( + self.at(&ObligationCause::dummy_with_span(span), param_env) + .eq(DefineOpaqueTypes::Yes, hidden_ty, actual)? + .obligations + .into_iter() + .map(|obligation| obligation.as_goal()), + ); + } mode @ (ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => { bug!("insert hidden type in {mode:?}") } diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs index ba6cc0d783dd3..46752840e1bab 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/table.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs @@ -1,18 +1,27 @@ +use std::ops::Deref; + +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::bug; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty}; use tracing::instrument; -use super::OpaqueTypeMap; use crate::infer::snapshot::undo_log::{InferCtxtUndoLogs, UndoLog}; #[derive(Default, Debug, Clone)] -pub(crate) struct OpaqueTypeStorage<'tcx> { - /// 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' OpaqueHiddenType, after typeck is done with all functions. - pub opaque_types: OpaqueTypeMap<'tcx>, +pub struct OpaqueTypeStorage<'tcx> { + opaque_types: FxIndexMap, OpaqueHiddenType<'tcx>>, + duplicate_entries: Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)>, +} + +/// The number of entries in the opaque type storage at a given point. +/// +/// Used to check that we haven't added any new opaque types after checking +/// the opaque types currently in the storage. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +pub struct OpaqueTypeStorageEntries { + opaque_types: usize, + duplicate_entries: usize, } impl<'tcx> OpaqueTypeStorage<'tcx> { @@ -33,6 +42,70 @@ impl<'tcx> OpaqueTypeStorage<'tcx> { } } + pub(crate) fn pop_duplicate_entry(&mut self) { + let entry = self.duplicate_entries.pop(); + assert!(entry.is_some()); + } + + pub(crate) fn is_empty(&self) -> bool { + let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + opaque_types.is_empty() && duplicate_entries.is_empty() + } + + pub(crate) fn take_opaque_types( + &mut self, + ) -> impl Iterator, OpaqueHiddenType<'tcx>)> { + let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + std::mem::take(opaque_types).into_iter().chain(std::mem::take(duplicate_entries)) + } + + pub fn num_entries(&self) -> OpaqueTypeStorageEntries { + OpaqueTypeStorageEntries { + opaque_types: self.opaque_types.len(), + duplicate_entries: self.duplicate_entries.len(), + } + } + + pub fn opaque_types_added_since( + &self, + prev_entries: OpaqueTypeStorageEntries, + ) -> impl Iterator, OpaqueHiddenType<'tcx>)> { + self.opaque_types + .iter() + .skip(prev_entries.opaque_types) + .map(|(k, v)| (*k, *v)) + .chain(self.duplicate_entries.iter().skip(prev_entries.duplicate_entries).copied()) + } + + /// Only returns the opaque types from the lookup table. These are used + /// when normalizing opaque types and have a unique key. + /// + /// Outside of canonicalization one should generally use `iter_opaque_types` + /// to also consider duplicate entries. + pub fn iter_lookup_table( + &self, + ) -> impl Iterator, OpaqueHiddenType<'tcx>)> { + self.opaque_types.iter().map(|(k, v)| (*k, *v)) + } + + /// Only returns the opaque types which are stored in `duplicate_entries`. + /// + /// These have to considered when checking all opaque type uses but are e.g. + /// irrelevant for canonical inputs as nested queries never meaningfully + /// accesses them. + pub fn iter_duplicate_entries( + &self, + ) -> impl Iterator, OpaqueHiddenType<'tcx>)> { + self.duplicate_entries.iter().copied() + } + + pub fn iter_opaque_types( + &self, + ) -> impl Iterator, OpaqueHiddenType<'tcx>)> { + let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + opaque_types.iter().map(|(k, v)| (*k, *v)).chain(duplicate_entries.iter().copied()) + } + #[inline] pub(crate) fn with_log<'a>( &'a mut self, @@ -44,21 +117,27 @@ impl<'tcx> OpaqueTypeStorage<'tcx> { impl<'tcx> Drop for OpaqueTypeStorage<'tcx> { fn drop(&mut self) { - if !self.opaque_types.is_empty() { + if !self.is_empty() { ty::tls::with(|tcx| tcx.dcx().delayed_bug(format!("{:?}", self.opaque_types))); } } } -pub(crate) struct OpaqueTypeTable<'a, 'tcx> { +pub struct OpaqueTypeTable<'a, 'tcx> { storage: &'a mut OpaqueTypeStorage<'tcx>, undo_log: &'a mut InferCtxtUndoLogs<'tcx>, } +impl<'tcx> Deref for OpaqueTypeTable<'_, 'tcx> { + type Target = OpaqueTypeStorage<'tcx>; + fn deref(&self) -> &Self::Target { + self.storage + } +} impl<'a, 'tcx> OpaqueTypeTable<'a, 'tcx> { #[instrument(skip(self), level = "debug")] - pub(crate) fn register( + pub fn register( &mut self, key: OpaqueTypeKey<'tcx>, hidden_type: OpaqueHiddenType<'tcx>, @@ -72,4 +151,9 @@ impl<'a, 'tcx> OpaqueTypeTable<'a, 'tcx> { self.undo_log.push(UndoLog::OpaqueTypes(key, None)); None } + + pub fn add_duplicate(&mut self, key: OpaqueTypeKey<'tcx>, hidden_type: OpaqueHiddenType<'tcx>) { + self.storage.duplicate_entries.push((key, hidden_type)); + self.undo_log.push(UndoLog::DuplicateOpaqueType); + } } diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index e924c974a02c9..cb5a33c5c972a 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -69,7 +69,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> { region_bound_pairs .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a)); } - OutlivesBound::RegionSubRegion(r_a, r_b) => match (*r_a, *r_b) { + OutlivesBound::RegionSubRegion(r_a, r_b) => match (r_a.kind(), r_b.kind()) { ( ty::ReStatic | ty::ReEarlyParam(_) | ty::ReLateParam(_), ty::ReStatic | ty::ReEarlyParam(_) | ty::ReLateParam(_), diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs index 379410641fe5b..2a4b9776f68cb 100644 --- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs +++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs @@ -24,12 +24,8 @@ impl<'tcx, OP> TypeVisitor> for FreeRegionsVisitor<'tcx, OP> where OP: FnMut(ty::Region<'tcx>), { - fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { - t.super_visit_with(self); - } - fn visit_region(&mut self, r: ty::Region<'tcx>) { - match *r { + match r.kind() { // ignore bound regions, keep visiting ty::ReBound(_, _) => {} _ => (self.op)(r), diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index a89cef50c9b40..5fd98e35e5ce8 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -63,11 +63,11 @@ use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::bug; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::outlives::{Component, push_outlives_components}; use rustc_middle::ty::{ self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesPredicate, Region, Ty, TyCtxt, TypeFoldable as _, TypeVisitableExt, }; -use rustc_type_ir::outlives::{Component, push_outlives_components}; use smallvec::smallvec; use tracing::{debug, instrument}; @@ -98,6 +98,14 @@ impl<'tcx> InferCtxt<'tcx> { sub_region: Region<'tcx>, cause: &ObligationCause<'tcx>, ) { + // `is_global` means the type has no params, infer, placeholder, or non-`'static` + // free regions. If the type has none of these things, then we can skip registering + // this outlives obligation since it has no components which affect lifetime + // checking in an interesting way. + if sup_type.is_global() { + return; + } + debug!(?sup_type, ?sub_region, ?cause); let origin = SubregionOrigin::from_obligation_cause(cause, || { infer::RelateParamBound( diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index c14c288c6e4e6..69feecfe30a49 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -1,7 +1,7 @@ use std::assert_matches::assert_matches; +use rustc_middle::ty::outlives::{Component, compute_alias_components_recursive}; use rustc_middle::ty::{self, OutlivesPredicate, Ty, TyCtxt}; -use rustc_type_ir::outlives::{Component, compute_alias_components_recursive}; use smallvec::smallvec; use tracing::{debug, instrument, trace}; diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs index 1bee9632110bd..2a4f9db8963c8 100644 --- a/compiler/rustc_infer/src/infer/projection.rs +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -1,7 +1,8 @@ use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty; use super::InferCtxt; +use crate::infer::Term; use crate::traits::{Obligation, PredicateObligations}; impl<'tcx> InferCtxt<'tcx> { @@ -11,24 +12,32 @@ impl<'tcx> InferCtxt<'tcx> { /// of the given projection. This allows us to proceed with projections /// while they cannot be resolved yet due to missing information or /// simply due to the lack of access to the trait resolution machinery. - pub fn projection_ty_to_infer( + pub fn projection_term_to_infer( &self, param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::AliasTy<'tcx>, + alias_term: ty::AliasTerm<'tcx>, cause: ObligationCause<'tcx>, recursion_depth: usize, obligations: &mut PredicateObligations<'tcx>, - ) -> Ty<'tcx> { + ) -> Term<'tcx> { debug_assert!(!self.next_trait_solver()); - let ty_var = self.next_ty_var(self.tcx.def_span(projection_ty.def_id)); + + let span = self.tcx.def_span(alias_term.def_id); + let infer_var = if alias_term.kind(self.tcx).is_type() { + self.next_ty_var(span).into() + } else { + self.next_const_var(span).into() + }; + let projection = ty::PredicateKind::Clause(ty::ClauseKind::Projection(ty::ProjectionPredicate { - projection_term: projection_ty.into(), - term: ty_var.into(), + projection_term: alias_term, + term: infer_var, })); let obligation = Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection); obligations.push(obligation); - ty_var + + infer_var } } diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index 3cfc58dea05bd..e332b6d0447a9 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -155,7 +155,7 @@ impl<'a, 'tcx> LeakCheck<'a, 'tcx> { self.scc_universes[scc].take_min(universe, *region); // Detect those SCCs that directly contain a placeholder - if let ty::RePlaceholder(placeholder) = **region { + if let ty::RePlaceholder(placeholder) = region.kind() { if self.outer_universe.cannot_name(placeholder.universe) { // Update `scc_placeholders` to account for the fact that `P: S` must hold. match self.scc_placeholders[scc] { diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 57555db37abd5..40e2e654b2ea7 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -463,7 +463,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { // cannot add constraints once regions are resolved debug!("origin = {:#?}", origin); - match (*sub, *sup) { + match (sub.kind(), sup.kind()) { (ReBound(..), _) | (_, ReBound(..)) => { span_bug!(origin.span(), "cannot relate bound region: {:?} <= {:?}", sub, sup); } @@ -595,7 +595,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { } pub fn universe(&mut self, region: Region<'tcx>) -> ty::UniverseIndex { - match *region { + match region.kind() { ty::ReStatic | ty::ReErased | ty::ReLateParam(..) @@ -618,9 +618,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { RegionVid::from(value_count)..RegionVid::from(self.storage.unification_table.len()); ( range.clone(), - (range.start.index()..range.end.index()) - .map(|index| self.storage.var_infos[ty::RegionVid::from(index)].origin) - .collect(), + (range.start..range.end).map(|index| self.storage.var_infos[index].origin).collect(), ) } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index ce2d07f4af996..210b8f37d883d 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -5,10 +5,9 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::visit::MaxUniverse; use rustc_middle::ty::{ - self, AliasRelationDirection, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt, - TypingMode, + self, AliasRelationDirection, InferConst, MaxUniverse, Term, Ty, TyCtxt, TypeVisitable, + TypeVisitableExt, TypingMode, }; use rustc_span::Span; use tracing::{debug, instrument, warn}; @@ -114,7 +113,7 @@ impl<'tcx> InferCtxt<'tcx> { }]); } // The old solver only accepts projection predicates for associated types. - ty::Alias(ty::Inherent | ty::Weak | ty::Opaque, _) => { + ty::Alias(ty::Inherent | ty::Free | ty::Opaque, _) => { return Err(TypeError::CyclicTy(source_ty)); } _ => bug!("generalized `{source_ty:?} to infer, not an alias"), @@ -572,7 +571,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { ) -> RelateResult<'tcx, ty::Region<'tcx>> { assert_eq!(r, r2); // we are misusing TypeRelation here; both LHS and RHS ought to be == - match *r { + match r.kind() { // Never make variables for regions bound within the type itself, // nor for erased regions. ty::ReBound(..) | ty::ReErased => { diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs index a12d83db91f24..2143f72a3b0a5 100644 --- a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs +++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs @@ -1,9 +1,7 @@ //! Helper routines for higher-ranked things. See the `doc` module at //! the end of the file for details. -use rustc_middle::ty::fold::FnMutDelegate; -use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, FnMutDelegate, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use tracing::{debug, instrument}; use super::RelateResult; diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs index e6d1003cab61e..6d25dfeb85933 100644 --- a/compiler/rustc_infer/src/infer/relate/mod.rs +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -2,9 +2,8 @@ //! (except for some relations used for diagnostics and heuristics in the compiler). //! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc). -pub use rustc_middle::ty::relate::RelateResult; -pub use rustc_type_ir::relate::combine::PredicateEmittingRelation; -pub use rustc_type_ir::relate::*; +pub use rustc_middle::ty::relate::combine::PredicateEmittingRelation; +pub use rustc_middle::ty::relate::{RelateResult, *}; mod generalize; mod higher_ranked; diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index 009271a8378f3..04ff776594e66 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -3,9 +3,8 @@ use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys} use rustc_middle::ty::relate::{ Relate, RelateResult, TypeRelation, relate_args_invariantly, relate_args_with_variances, }; -use rustc_middle::ty::{self, Ty, TyCtxt, TyVar}; +use rustc_middle::ty::{self, DelayedSet, Ty, TyCtxt, TyVar}; use rustc_span::Span; -use rustc_type_ir::data_structures::DelayedSet; use tracing::{debug, instrument}; use crate::infer::BoundRegionConversionTime::HigherRankedType; diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 6ec2e0152f0dc..4b0ace8c554d6 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -1,8 +1,8 @@ use rustc_middle::bug; -use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; -use rustc_type_ir::data_structures::DelayedMap; +use rustc_middle::ty::{ + self, Const, DelayedMap, FallibleTypeFolder, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, +}; use super::{FixupError, FixupResult, InferCtxt}; @@ -88,7 +88,7 @@ impl<'a, 'tcx> TypeFolder> for OpportunisticRegionResolver<'a, 'tcx } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { + match r.kind() { ty::ReVar(vid) => self .infcx .inner @@ -152,7 +152,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for FullTypeResolver<'a, 'tcx> { } fn try_fold_region(&mut self, r: ty::Region<'tcx>) -> Result, Self::Error> { - match *r { + match r.kind() { ty::ReVar(_) => Ok(self .infcx .lexical_region_resolutions diff --git a/compiler/rustc_infer/src/infer/snapshot/fudge.rs b/compiler/rustc_infer/src/infer/snapshot/fudge.rs index 3a47b13665d45..e210479581ba6 100644 --- a/compiler/rustc_infer/src/infer/snapshot/fudge.rs +++ b/compiler/rustc_infer/src/infer/snapshot/fudge.rs @@ -1,9 +1,10 @@ use std::ops::Range; use rustc_data_structures::{snapshot_vec as sv, unify as ut}; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; -use rustc_type_ir::visit::TypeVisitableExt; +use rustc_middle::ty::{ + self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, +}; use tracing::instrument; use ut::UnifyKey; @@ -28,11 +29,12 @@ fn const_vars_since_snapshot<'tcx>( snapshot_var_len: usize, ) -> (Range, Vec) { let range = vars_since_snapshot(table, snapshot_var_len); + let range = range.start.vid..range.end.vid; ( - range.start.vid..range.end.vid, - (range.start.index()..range.end.index()) - .map(|index| match table.probe_value(ConstVid::from_u32(index)) { + range.clone(), + range + .map(|index| match table.probe_value(index) { ConstVariableValue::Known { value: _ } => { ConstVariableOrigin { param_def_id: None, span: rustc_span::DUMMY_SP } } diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index ba7d8f588e68e..b7412d3d6a6da 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -17,6 +17,7 @@ pub struct Snapshot<'tcx> { /// Records the "undo" data for a single operation that affects some form of inference variable. #[derive(Clone)] pub(crate) enum UndoLog<'tcx> { + DuplicateOpaqueType, OpaqueTypes(OpaqueTypeKey<'tcx>, Option>), TypeVariables(sv::UndoLog>>), ConstUnificationTable(sv::UndoLog>>), @@ -58,6 +59,7 @@ impl_from! { impl<'tcx> Rollback> for InferCtxtInner<'tcx> { fn reverse(&mut self, undo: UndoLog<'tcx>) { match undo { + UndoLog::DuplicateOpaqueType => self.opaque_type_storage.pop_duplicate_entry(), UndoLog::OpaqueTypes(key, idx) => self.opaque_type_storage.remove(key, idx), UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo), UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo), diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index a04b2bb2b08b5..ab7b7060c0922 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -21,10 +21,8 @@ #![feature(assert_matches)] #![feature(extend_one)] #![feature(iterator_try_collect)] -#![feature(let_chains)] #![feature(rustdoc_internals)] #![recursion_limit = "512"] // For rustdoc -#![warn(unreachable_pub)] // tidy-alphabetical-end mod errors; diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 1eae10673b62b..9e51a53ae95fa 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -94,7 +94,7 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { /// Among all pending obligations, collect those are stalled on a inference variable which has /// changed since the last call to `select_where_possible`. Those obligations are marked as /// successful and returned. - fn drain_unstalled_obligations( + fn drain_stalled_obligations_for_coroutines( &mut self, infcx: &InferCtxt<'tcx>, ) -> PredicateObligations<'tcx>; diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index ac641ef565228..6d5ad96e31c90 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -12,6 +12,7 @@ use std::hash::{Hash, Hasher}; use hir::def_id::LocalDefId; use rustc_hir as hir; +use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::Certainty; pub use rustc_middle::traits::*; @@ -35,9 +36,11 @@ use crate::infer::InferCtxt; /// either identifying an `impl` (e.g., `impl Eq for i32`) that /// satisfies the obligation, or else finding a bound that is in /// scope. The eventual result is usually a `Selection` (defined below). -#[derive(Clone)] +#[derive(Clone, TypeFoldable, TypeVisitable)] pub struct Obligation<'tcx, T> { /// The reason we have to prove this thing. + #[type_foldable(identity)] + #[type_visitable(ignore)] pub cause: ObligationCause<'tcx>, /// The environment in which we should prove this thing. @@ -51,9 +54,17 @@ pub struct Obligation<'tcx, T> { /// If this goes over a certain threshold, we abort compilation -- /// in such cases, we can not say whether or not the predicate /// holds for certain. Stupid halting problem; such a drag. + #[type_foldable(identity)] + #[type_visitable(ignore)] pub recursion_depth: usize, } +impl<'tcx, T: Copy> Obligation<'tcx, T> { + pub fn as_goal(&self) -> solve::Goal<'tcx, T> { + solve::Goal { param_env: self.param_env, predicate: self.predicate } + } +} + impl<'tcx, T: PartialEq> PartialEq> for Obligation<'tcx, T> { #[inline] fn eq(&self, other: &Obligation<'tcx, T>) -> bool { @@ -75,12 +86,6 @@ impl Hash for Obligation<'_, T> { } } -impl<'tcx, P> From> for solve::Goal<'tcx, P> { - fn from(value: Obligation<'tcx, P>) -> Self { - solve::Goal { param_env: value.param_env, predicate: value.predicate } - } -} - pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>; pub type TraitObligation<'tcx> = Obligation<'tcx, ty::TraitPredicate<'tcx>>; pub type PolyTraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>; diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index b346e193d0b84..03661ebf7ec53 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -1,8 +1,6 @@ use std::fmt; -use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable}; -use rustc_middle::ty::visit::{TypeVisitable, TypeVisitor, try_visit}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty; use crate::traits; use crate::traits::project::Normalized; @@ -34,31 +32,3 @@ impl<'tcx> fmt::Debug for traits::MismatchedProjectionTypes<'tcx> { write!(f, "MismatchedProjectionTypes({:?})", self.err) } } - -/////////////////////////////////////////////////////////////////////////// -// TypeFoldable implementations. - -impl<'tcx, O: TypeFoldable>> TypeFoldable> - for traits::Obligation<'tcx, O> -{ - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - Ok(traits::Obligation { - cause: self.cause, - recursion_depth: self.recursion_depth, - predicate: self.predicate.try_fold_with(folder)?, - param_env: self.param_env.try_fold_with(folder)?, - }) - } -} - -impl<'tcx, O: TypeVisitable>> TypeVisitable> - for traits::Obligation<'tcx, O> -{ - fn visit_with>>(&self, visitor: &mut V) -> V::Result { - try_visit!(self.predicate.visit_with(visitor)); - self.param_env.visit_with(visitor) - } -} diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 66ed49fe32676..6461fbe0d33bb 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -1,7 +1,7 @@ use rustc_data_structures::fx::FxHashSet; +pub use rustc_middle::ty::elaborate::*; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{Ident, Span}; -pub use rustc_type_ir::elaborate::*; use crate::traits::{self, Obligation, ObligationCauseCode, PredicateObligation}; diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 9c9660cf50469..ff28dbeaee698 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -5,9 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -rustc-rayon = { version = "0.5.0" } rustc-rayon-core = { version = "0.5.0" } -rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } @@ -41,7 +39,6 @@ rustc_privacy = { path = "../rustc_privacy" } rustc_query_impl = { path = "../rustc_query_impl" } rustc_query_system = { path = "../rustc_query_system" } rustc_resolve = { path = "../rustc_resolve" } -rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } @@ -52,6 +49,11 @@ rustc_ty_utils = { path = "../rustc_ty_utils" } tracing = "0.1" # tidy-alphabetical-end +[dev-dependencies] +# tidy-alphabetical-start +rustc_abi = { path = "../rustc_abi" } +# tidy-alphabetical-end + [features] # tidy-alphabetical-start llvm = ['dep:rustc_codegen_llvm'] diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index adc7ed54e1472..4b9c71d71725c 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -3,9 +3,6 @@ interface_abi_required_feature = .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! interface_abi_required_feature_issue = for more information, see issue #116344 -interface_cant_emit_mir = - could not emit MIR: {$error} - interface_crate_name_does_not_match = `--crate-name` and `#[crate_name]` are required to match, but `{$crate_name}` != `{$attr_crate_name}` interface_crate_name_invalid = crate names cannot start with a `-`, but `{$crate_name}` has a leading hyphen @@ -53,11 +50,5 @@ interface_out_dir_error = interface_proc_macro_crate_panic_abort = building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic -interface_rustc_error_fatal = - fatal error triggered by #[rustc_error] - -interface_rustc_error_unexpected_annotation = - unexpected annotation used with `#[rustc_error(...)]`! - interface_temps_dir_error = failed to find or create the directory specified by `--temps-dir` diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index f66b9eb3a2856..7c6b7157f71a5 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -14,6 +14,7 @@ use std::fmt; use rustc_errors::{DiagInner, TRACK_DIAGNOSTIC}; use rustc_middle::dep_graph::{DepNodeExt, TaskDepsRef}; use rustc_middle::ty::tls; +use rustc_query_impl::QueryCtxt; use rustc_query_system::dep_graph::dep_node::default_dep_kind_debug; use rustc_query_system::dep_graph::{DepContext, DepKind, DepNode}; @@ -41,9 +42,7 @@ fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) { fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { tls::with_context_opt(|icx| { if let Some(icx) = icx { - if let Some(diagnostics) = icx.diagnostics { - diagnostics.lock().extend(Some(diagnostic.clone())); - } + icx.tcx.dep_graph.record_diagnostic(QueryCtxt::new(icx.tcx), &diagnostic); // Diagnostics are tracked, we can ignore the dependency. let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..icx.clone() }; diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index eed729a1777fc..6b39b4f18911c 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -73,26 +73,6 @@ pub struct TempsDirError; #[diag(interface_out_dir_error)] pub struct OutDirError; -#[derive(Diagnostic)] -#[diag(interface_cant_emit_mir)] -pub struct CantEmitMIR { - pub error: io::Error, -} - -#[derive(Diagnostic)] -#[diag(interface_rustc_error_fatal)] -pub struct RustcErrorFatal { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(interface_rustc_error_unexpected_annotation)] -pub struct RustcErrorUnexpectedAnnotation { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(interface_failed_writing_file)] pub struct FailedWritingFile<'a> { diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index b35703d8e737b..cf494f8d686e8 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use rustc_ast::{LitKind, MetaItemKind, token}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::jobserver; +use rustc_data_structures::jobserver::{self, Proxy}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_errors::registry::Registry; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; @@ -18,7 +18,7 @@ use rustc_parse::parser::attr::AllowLeadingUnsafe; use rustc_query_impl::QueryCtxt; use rustc_query_system::query::print_query_stack; use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName}; -use rustc_session::filesearch::{self, sysroot_candidates}; +use rustc_session::filesearch::sysroot_candidates; use rustc_session::parse::ParseSess; use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, lint}; use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMapInputs}; @@ -40,7 +40,12 @@ pub struct Compiler { pub sess: Session, pub codegen_backend: Box, pub(crate) override_queries: Option, + + /// A reference to the current `GlobalCtxt` which we pass on to `GlobalCtxt`. pub(crate) current_gcx: CurrentGcx, + + /// A jobserver reference which we pass on to `GlobalCtxt`. + pub(crate) jobserver_proxy: Arc, } /// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`. @@ -204,6 +209,14 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch error!("`cfg()` names cannot be after values"); } names.push(ident); + } else if let Some(boolean) = arg.boolean_literal() { + if values_specified { + error!("`cfg()` names cannot be after values"); + } + names.push(rustc_span::Ident::new( + if boolean { rustc_span::kw::True } else { rustc_span::kw::False }, + arg.span(), + )); } else if arg.has_name(sym::any) && let Some(args) = arg.meta_item_list() { @@ -340,6 +353,10 @@ pub struct Config { /// the list of queries. pub override_queries: Option, + /// An extra set of symbols to add to the symbol interner, the symbol indices + /// will start at [`PREDEFINED_SYMBOLS_COUNT`](rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT) + pub extra_symbols: Vec<&'static str>, + /// This is a callback from the driver that is called to create a codegen backend. /// /// Has no uses within this repository, but is used by bjorn3 for "the @@ -390,7 +407,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se crate::callbacks::setup_callbacks(); - let sysroot = filesearch::materialize_sysroot(config.opts.maybe_sysroot.clone()); + let sysroot = config.opts.sysroot.clone(); let target = config::build_target_config(&early_dcx, &config.opts.target_triple, &sysroot); let file_loader = config.file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); let path_mapping = config.opts.file_path_mapping(); @@ -401,8 +418,9 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se &early_dcx, config.opts.edition, config.opts.unstable_opts.threads, + &config.extra_symbols, SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }, - |current_gcx| { + |current_gcx, jobserver_proxy| { // The previous `early_dcx` can't be reused here because it doesn't // impl `Send`. Creating a new one is fine. let early_dcx = EarlyDiagCtxt::new(config.opts.error_format); @@ -424,7 +442,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); let bundle = match rustc_errors::fluent_bundle( - config.opts.maybe_sysroot.clone(), + config.opts.sysroot.clone(), sysroot_candidates().to_vec(), config.opts.unstable_opts.translate_lang.clone(), config.opts.unstable_opts.translate_additional_ftl.as_deref(), @@ -498,6 +516,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se codegen_backend, override_queries: config.override_queries, current_gcx, + jobserver_proxy, }; // There are two paths out of `f`. diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 54cd341698f0b..ce2398fab9197 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -2,9 +2,7 @@ #![feature(decl_macro)] #![feature(file_buffered)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(try_blocks)] -#![warn(unreachable_pub)] // tidy-alphabetical-end mod callbacks; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 553215ca0af02..75d92ae7a2ed6 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1,15 +1,16 @@ use std::any::Any; -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::io::{self, BufWriter, Write}; use std::path::{Path, PathBuf}; -use std::sync::{Arc, LazyLock}; +use std::sync::{Arc, LazyLock, OnceLock}; use std::{env, fs, iter}; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::parallel; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, OnceLock, WorkerLocal}; +use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal}; use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_feature::Features; use rustc_fs_util::try_canonicalize; @@ -19,6 +20,7 @@ use rustc_incremental::setup_dep_graph; use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_store}; use rustc_metadata::creader::CStore; use rustc_middle::arena::Arena; +use rustc_middle::dep_graph::DepsType; use rustc_middle::ty::{self, CurrentGcx, GlobalCtxt, RegisteredTools, TyCtxt}; use rustc_middle::util::Providers; use rustc_parse::{ @@ -29,10 +31,11 @@ use rustc_resolve::Resolver; use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::cstore::Untracked; use rustc_session::output::{collect_crate_types, filename_for_input}; +use rustc_session::parse::feature_err; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; use rustc_span::{ - ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym, + DUMMY_SP, ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym, }; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::traits; @@ -42,7 +45,7 @@ use crate::interface::Compiler; use crate::{errors, limits, proc_macro_decls, util}; pub fn parse<'a>(sess: &'a Session) -> ast::Crate { - let krate = sess + let mut krate = sess .time("parse_crate", || { let mut parser = unwrap_or_emit_fatal(match &sess.io.input { Input::File(file) => new_parser_from_file(&sess.psess, file, None), @@ -61,6 +64,12 @@ pub fn parse<'a>(sess: &'a Session) -> ast::Crate { input_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS", "ast-stats-1"); } + rustc_builtin_macros::cmdline_attrs::inject( + &mut krate, + &sess.psess, + &sess.opts.unstable_opts.crate_attr, + ); + krate } @@ -235,6 +244,7 @@ fn configure_and_expand( sess, features, &krate, + tcx.is_sdylib_interface_build(), resolver.lint_buffer(), ) }); @@ -251,6 +261,9 @@ fn configure_and_expand( sess.dcx().emit_err(errors::MixedProcMacroCrate); } } + if crate_types.contains(&CrateType::Sdylib) && !tcx.features().export_stable() { + feature_err(sess, sym::export_stable, DUMMY_SP, "`sdylib` crate type is unstable").emit(); + } if is_proc_macro_crate && sess.panic_strategy() == PanicStrategy::Abort { sess.dcx().emit_warn(errors::ProcMacroCratePanicAbort); @@ -275,6 +288,7 @@ fn configure_and_expand( resolver.resolve_crate(&krate); CStore::from_tcx(tcx).report_incompatible_target_modifiers(tcx, &krate); + CStore::from_tcx(tcx).report_incompatible_async_drop_feature(tcx, &krate); krate } @@ -360,6 +374,31 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { ) } +fn env_var_os<'tcx>(tcx: TyCtxt<'tcx>, key: &'tcx OsStr) -> Option<&'tcx OsStr> { + let value = env::var_os(key); + + let value_tcx = value.as_ref().map(|value| { + let encoded_bytes = tcx.arena.alloc_slice(value.as_encoded_bytes()); + debug_assert_eq!(value.as_encoded_bytes(), encoded_bytes); + // SAFETY: The bytes came from `as_encoded_bytes`, and we assume that + // `alloc_slice` is implemented correctly, and passes the same bytes + // back (debug asserted above). + unsafe { OsStr::from_encoded_bytes_unchecked(encoded_bytes) } + }); + + // Also add the variable to Cargo's dependency tracking + // + // NOTE: This only works for passes run before `write_dep_info`. See that + // for extension points for configuring environment variables to be + // properly change-tracked. + tcx.sess.psess.env_depinfo.borrow_mut().insert(( + Symbol::intern(&key.to_string_lossy()), + value.as_ref().and_then(|value| value.to_str()).map(|value| Symbol::intern(&value)), + )); + + value_tcx +} + // Returns all the paths that correspond to generated files. fn generated_output_paths( tcx: TyCtxt<'_>, @@ -715,6 +754,25 @@ pub fn write_dep_info(tcx: TyCtxt<'_>) { } } +pub fn write_interface<'tcx>(tcx: TyCtxt<'tcx>) { + if !tcx.crate_types().contains(&rustc_session::config::CrateType::Sdylib) { + return; + } + let _timer = tcx.sess.timer("write_interface"); + let (_, krate) = &*tcx.resolver_for_lowering().borrow(); + + let krate = rustc_ast_pretty::pprust::print_crate_as_interface( + krate, + tcx.sess.psess.edition, + &tcx.sess.psess.attr_id_generator, + ); + let export_output = tcx.output_filenames(()).interface_path(); + let mut file = fs::File::create_buffered(export_output).unwrap(); + if let Err(err) = write!(file, "{}", krate) { + tcx.dcx().fatal(format!("error writing interface file: {}", err)); + } +} + pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { let providers = &mut Providers::default(); providers.analysis = analysis; @@ -724,6 +782,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { |tcx, _| tcx.arena.alloc_from_iter(tcx.resolutions(()).stripped_cfg_items.steal()); providers.resolutions = |tcx, ()| tcx.resolver_for_lowering_raw(()).1; providers.early_lint_checks = early_lint_checks; + providers.env_var_os = env_var_os; limits::provide(providers); proc_macro_decls::provide(providers); rustc_const_eval::provide(providers); @@ -752,17 +811,11 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( compiler: &Compiler, - mut krate: rustc_ast::Crate, + krate: rustc_ast::Crate, f: F, ) -> T { let sess = &compiler.sess; - rustc_builtin_macros::cmdline_attrs::inject( - &mut krate, - &sess.psess, - &sess.opts.unstable_opts.crate_attr, - ); - let pre_configured_attrs = rustc_expand::config::pre_configure_attrs(sess, &krate.attrs); let crate_name = get_crate_name(sess, &pre_configured_attrs); @@ -773,8 +826,11 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( sess.opts.cg.metadata.clone(), sess.cfg_version, ); + let outputs = util::build_output_filenames(&pre_configured_attrs, sess); - let dep_graph = setup_dep_graph(sess, crate_name); + + let dep_type = DepsType { dep_names: rustc_query_impl::dep_kind_names() }; + let dep_graph = setup_dep_graph(sess, crate_name, &dep_type); let cstore = FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _); @@ -811,12 +867,13 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( dyn for<'tcx> FnOnce( &'tcx Session, CurrentGcx, + Arc, &'tcx OnceLock>, &'tcx WorkerLocal>, &'tcx WorkerLocal>, F, ) -> T, - > = Box::new(move |sess, current_gcx, gcx_cell, arena, hir_arena, f| { + > = Box::new(move |sess, current_gcx, jobserver_proxy, gcx_cell, arena, hir_arena, f| { TyCtxt::create_global_ctxt( gcx_cell, sess, @@ -835,6 +892,7 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( ), providers.hooks, current_gcx, + jobserver_proxy, |tcx| { let feed = tcx.create_crate_num(stable_crate_id).unwrap(); assert_eq!(feed.key(), LOCAL_CRATE); @@ -850,14 +908,22 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( feed.output_filenames(Arc::new(outputs)); let res = f(tcx); - // FIXME maybe run finish even when a fatal error occured? or at least tcx.alloc_self_profile_query_strings()? + // FIXME maybe run finish even when a fatal error occurred? or at least tcx.alloc_self_profile_query_strings()? tcx.finish(); res }, ) }); - inner(&compiler.sess, compiler.current_gcx.clone(), &gcx_cell, &arena, &hir_arena, f) + inner( + &compiler.sess, + compiler.current_gcx.clone(), + Arc::clone(&compiler.jobserver_proxy), + &gcx_cell, + &arena, + &hir_arena, + f, + ) } /// Runs all analyses that we guarantee to run, even if errors were reported in earlier analyses. @@ -870,6 +936,12 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { // is not defined. So we need to cfg it out. #[cfg(all(not(doc), debug_assertions))] rustc_passes::hir_id_validator::check_crate(tcx); + + // Prefetch this to prevent multiple threads from blocking on it later. + // This is needed since the `hir_id_validator::check_crate` call above is not guaranteed + // to use `hir_crate`. + tcx.ensure_done().hir_crate(()); + let sess = tcx.sess; sess.time("misc_checking_1", || { parallel!( @@ -883,6 +955,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { CStore::from_tcx(tcx).report_unused_deps(tcx); }, { + tcx.ensure_ok().exportable_items(LOCAL_CRATE); + tcx.ensure_ok().stable_order_of_exportable_impls(LOCAL_CRATE); tcx.par_hir_for_each_module(|module| { tcx.ensure_ok().check_mod_loops(module); tcx.ensure_ok().check_mod_attrs(module); @@ -923,14 +997,11 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { sess.time("MIR_borrow_checking", || { tcx.par_hir_body_owners(|def_id| { - // Run unsafety check because it's responsible for stealing and - // deallocating THIR. - tcx.ensure_ok().check_unsafety(def_id); - tcx.ensure_ok().mir_borrowck(def_id) - }); - }); - sess.time("MIR_effect_checking", || { - tcx.par_hir_body_owners(|def_id| { + if !tcx.is_typeck_child(def_id.to_def_id()) { + // Child unsafety and borrowck happens together with the parent + tcx.ensure_ok().check_unsafety(def_id); + tcx.ensure_ok().mir_borrowck(def_id) + } tcx.ensure_ok().has_ffi_unwind_calls(def_id); // If we need to codegen, ensure that we emit all errors from @@ -947,14 +1018,16 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { tcx.par_hir_body_owners(|def_id| { if tcx.is_coroutine(def_id.to_def_id()) { tcx.ensure_ok().mir_coroutine_witnesses(def_id); - tcx.ensure_ok().check_coroutine_obligations( + let _ = tcx.ensure_ok().check_coroutine_obligations( tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(), ); - // Eagerly check the unsubstituted layout for cycles. - tcx.ensure_ok().layout_of( - ty::TypingEnv::post_analysis(tcx, def_id.to_def_id()) - .as_query_input(tcx.type_of(def_id).instantiate_identity()), - ); + if !tcx.is_async_drop_in_place_coroutine(def_id.to_def_id()) { + // Eagerly check the unsubstituted layout for cycles. + tcx.ensure_ok().layout_of( + ty::TypingEnv::post_analysis(tcx, def_id.to_def_id()) + .as_query_input(tcx.type_of(def_id).instantiate_identity()), + ); + } } }); }); @@ -1038,46 +1111,25 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) { }); } -/// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used -/// to write UI tests that actually test that compilation succeeds without reporting -/// an error. -fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { - let Some((def_id, _)) = tcx.entry_fn(()) else { return }; - for attr in tcx.get_attrs(def_id, sym::rustc_error) { - match attr.meta_item_list() { - // Check if there is a `#[rustc_error(delayed_bug_from_inside_query)]`. - Some(list) - if list.iter().any(|list_item| { - matches!( - list_item.ident().map(|i| i.name), - Some(sym::delayed_bug_from_inside_query) - ) - }) => - { - tcx.ensure_ok().trigger_delayed_bug(def_id); - } - - // Bare `#[rustc_error]`. - None => { - tcx.dcx().emit_fatal(errors::RustcErrorFatal { span: tcx.def_span(def_id) }); - } - - // Some other attribute. - Some(_) => { - tcx.dcx().emit_warn(errors::RustcErrorUnexpectedAnnotation { - span: tcx.def_span(def_id), - }); - } - } - } -} - /// Runs the codegen backend, after which the AST and analysis can /// be discarded. pub(crate) fn start_codegen<'tcx>( codegen_backend: &dyn CodegenBackend, tcx: TyCtxt<'tcx>, ) -> Box { + // Hook for tests. + if let Some((def_id, _)) = tcx.entry_fn(()) + && tcx.has_attr(def_id, sym::rustc_delayed_bug_from_inside_query) + { + tcx.ensure_ok().trigger_delayed_bug(def_id); + } + + // Don't run this test assertions when not doing codegen. Compiletest tries to build + // build-fail tests in check mode first and expects it to not give an error in that case. + if tcx.sess.opts.output_types.should_codegen() { + rustc_symbol_mangling::test::report_symbol_names(tcx); + } + // Don't do code generation if there were any errors. Likewise if // there were any delayed bugs, because codegen will likely cause // more ICEs, obscuring the original problem. @@ -1085,9 +1137,6 @@ pub(crate) fn start_codegen<'tcx>( guar.raise_fatal(); } - // Hook for UI tests. - check_for_rustc_errors_attr(tcx); - info!("Pre-codegen\n{:?}", tcx.debug_stats()); let (metadata, need_metadata_module) = rustc_metadata::fs::encode_and_write_metadata(tcx); @@ -1096,20 +1145,8 @@ pub(crate) fn start_codegen<'tcx>( codegen_backend.codegen_crate(tcx, metadata, need_metadata_module) }); - // Don't run this test assertions when not doing codegen. Compiletest tries to build - // build-fail tests in check mode first and expects it to not give an error in that case. - if tcx.sess.opts.output_types.should_codegen() { - rustc_symbol_mangling::test::report_symbol_names(tcx); - } - info!("Post-codegen\n{:?}", tcx.debug_stats()); - if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) { - if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx) { - tcx.dcx().emit_fatal(errors::CantEmitMIR { error }); - } - } - // This must run after monomorphization so that all generic types // have been instantiated. if tcx.sess.opts.unstable_opts.print_type_sizes { diff --git a/compiler/rustc_interface/src/proc_macro_decls.rs b/compiler/rustc_interface/src/proc_macro_decls.rs index 00600abb5f1e3..a2c1f1dbeda6b 100644 --- a/compiler/rustc_interface/src/proc_macro_decls.rs +++ b/compiler/rustc_interface/src/proc_macro_decls.rs @@ -8,7 +8,7 @@ fn proc_macro_decls_static(tcx: TyCtxt<'_>, (): ()) -> Option { let mut decls = None; for id in tcx.hir_free_items() { - let attrs = tcx.hir().attrs(id.hir_id()); + let attrs = tcx.hir_attrs(id.hir_id()); if attr::contains_name(attrs, sym::rustc_proc_macro_decls) { decls = Some(id.owner_id.def_id); } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index aabd235bcabed..20e081d33600e 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -1,7 +1,7 @@ #![allow(rustc::bad_opt_access)] -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; use std::num::NonZero; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::atomic::AtomicBool; use rustc_abi::Align; @@ -21,7 +21,7 @@ use rustc_session::config::{ use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; -use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, filesearch, getopts}; +use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, getopts}; use rustc_span::edition::{DEFAULT_EDITION, Edition}; use rustc_span::source_map::{RealFileLoader, SourceMapInputs}; use rustc_span::{FileName, SourceFileHashAlgorithm, sym}; @@ -41,7 +41,7 @@ where let matches = optgroups().parse(args).unwrap(); let sessopts = build_session_options(&mut early_dcx, &matches); - let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone()); + let sysroot = sessopts.sysroot.clone(); let target = rustc_session::config::build_target_config(&early_dcx, &sessopts.target_triple, &sysroot); let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target); @@ -53,7 +53,7 @@ where checksum_hash_kind, }); - rustc_span::create_session_globals_then(DEFAULT_EDITION, sm_inputs, || { + rustc_span::create_session_globals_then(DEFAULT_EDITION, &[], sm_inputs, || { let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); let io = CompilerIO { input: Input::Str { name: FileName::Custom(String::new()), input: String::new() }, @@ -89,8 +89,8 @@ where S: Into, I: IntoIterator, { - let locations: BTreeSet = - locations.into_iter().map(|s| CanonicalizedPath::new(Path::new(&s.into()))).collect(); + let locations = + locations.into_iter().map(|s| CanonicalizedPath::new(PathBuf::from(s.into()))).collect(); ExternEntry { location: ExternLocation::ExactPaths(locations), @@ -614,6 +614,7 @@ fn test_codegen_options_tracking_hash() { tracked!(control_flow_guard, CFGuard::Checks); tracked!(debug_assertions, Some(true)); tracked!(debuginfo, DebugInfo::Limited); + tracked!(dwarf_version, Some(5)); tracked!(embed_bitcode, false); tracked!(force_frame_pointers, FramePointer::Always); tracked!(force_unwind_tables, Some(true)); @@ -719,7 +720,7 @@ fn test_unstable_options_tracking_hash() { untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); untracked!(print_codegen_stats, true); untracked!(print_llvm_passes, true); - untracked!(print_mono_items, Some(String::from("abc"))); + untracked!(print_mono_items, true); untracked!(print_type_sizes, true); untracked!(proc_macro_backtrace, true); untracked!(proc_macro_execution_strategy, ProcMacroExecutionStrategy::CrossThread); @@ -775,7 +776,8 @@ fn test_unstable_options_tracking_hash() { CoverageOptions { level: CoverageLevel::Mcdc, no_mir_spans: true, - discard_all_spans_in_codegen: true + discard_all_spans_in_codegen: true, + inject_unused_local_file: true, } ); tracked!(crate_attr, vec!["abc".to_string()]); @@ -787,6 +789,7 @@ fn test_unstable_options_tracking_hash() { tracked!(direct_access_external_data, Some(true)); tracked!(dual_proc_macros, true); tracked!(dwarf_version, Some(5)); + tracked!(embed_metadata, false); tracked!(embed_source, true); tracked!(emit_thin_lto, false); tracked!(emscripten_wasm_eh, true); @@ -816,8 +819,8 @@ fn test_unstable_options_tracking_hash() { tracked!(min_function_alignment, Some(Align::EIGHT)); tracked!(mir_emit_retag, true); tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]); - tracked!(mir_keep_place_mention, true); tracked!(mir_opt_level, Some(4)); + tracked!(mir_preserve_ub, true); tracked!(move_size_limit, Some(4096)); tracked!(mutable_noalias, false); tracked!(next_solver, NextSolverConfig { coherence: true, globally: true }); @@ -852,6 +855,7 @@ fn test_unstable_options_tracking_hash() { tracked!(sanitizer_cfi_generalize_pointers, Some(true)); tracked!(sanitizer_cfi_normalize_integers, Some(true)); tracked!(sanitizer_dataflow_abilist, vec![String::from("/rustc/abc")]); + tracked!(sanitizer_kcfi_arity, Some(true)); tracked!(sanitizer_memory_track_origins, 2); tracked!(sanitizer_recover, SanitizerSet::ADDRESS); tracked!(saturating_float_casts, Some(true)); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index bc2aae7cd87e2..087b11fdf9d9c 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -1,11 +1,12 @@ use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; use std::path::{Path, PathBuf}; -use std::sync::OnceLock; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, OnceLock}; use std::{env, iter, thread}; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync; use rustc_metadata::{DylibError, load_symbol_from_dylib}; use rustc_middle::ty::CurrentGcx; @@ -18,7 +19,7 @@ use rustc_session::{EarlyDiagCtxt, Session, filesearch}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; -use rustc_span::{Symbol, sym}; +use rustc_span::{SessionGlobals, Symbol, sym}; use rustc_target::spec::Target; use tracing::info; @@ -38,14 +39,25 @@ pub(crate) fn add_configuration( codegen_backend: &dyn CodegenBackend, ) { let tf = sym::target_feature; + let tf_cfg = codegen_backend.target_config(sess); - let unstable_target_features = codegen_backend.target_features_cfg(sess, true); - sess.unstable_target_features.extend(unstable_target_features.iter().cloned()); + sess.unstable_target_features.extend(tf_cfg.unstable_target_features.iter().copied()); + sess.target_features.extend(tf_cfg.target_features.iter().copied()); - let target_features = codegen_backend.target_features_cfg(sess, false); - sess.target_features.extend(target_features.iter().cloned()); + cfg.extend(tf_cfg.target_features.into_iter().map(|feat| (tf, Some(feat)))); - cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat)))); + if tf_cfg.has_reliable_f16 { + cfg.insert((sym::target_has_reliable_f16, None)); + } + if tf_cfg.has_reliable_f16_math { + cfg.insert((sym::target_has_reliable_f16_math, None)); + } + if tf_cfg.has_reliable_f128 { + cfg.insert((sym::target_has_reliable_f128, None)); + } + if tf_cfg.has_reliable_f128_math { + cfg.insert((sym::target_has_reliable_f128_math, None)); + } if sess.crt_static(None) { cfg.insert((tf, Some(sym::crt_dash_static))); @@ -113,10 +125,11 @@ fn init_stack_size(early_dcx: &EarlyDiagCtxt) -> usize { }) } -fn run_in_thread_with_globals R + Send, R: Send>( +fn run_in_thread_with_globals) -> R + Send, R: Send>( thread_stack_size: usize, edition: Edition, sm_inputs: SourceMapInputs, + extra_symbols: &[&'static str], f: F, ) -> R { // The "thread pool" is a single spawned thread in the non-parallel @@ -134,9 +147,12 @@ fn run_in_thread_with_globals R + Send, R: Send>( // name contains null bytes. let r = builder .spawn_scoped(s, move || { - rustc_span::create_session_globals_then(edition, Some(sm_inputs), || { - f(CurrentGcx::new()) - }) + rustc_span::create_session_globals_then( + edition, + extra_symbols, + Some(sm_inputs), + || f(CurrentGcx::new(), Proxy::new()), + ) }) .unwrap() .join(); @@ -148,17 +164,21 @@ fn run_in_thread_with_globals R + Send, R: Send>( }) } -pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( +pub(crate) fn run_in_thread_pool_with_globals< + F: FnOnce(CurrentGcx, Arc) -> R + Send, + R: Send, +>( thread_builder_diag: &EarlyDiagCtxt, edition: Edition, threads: usize, + extra_symbols: &[&'static str], sm_inputs: SourceMapInputs, f: F, ) -> R { use std::process; + use rustc_data_structures::defer; use rustc_data_structures::sync::FromDyn; - use rustc_data_structures::{defer, jobserver}; use rustc_middle::ty::tls; use rustc_query_impl::QueryCtxt; use rustc_query_system::query::{QueryContext, break_query_cycles}; @@ -168,45 +188,68 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap()); if !sync::is_dyn_thread_safe() { - return run_in_thread_with_globals(thread_stack_size, edition, sm_inputs, |current_gcx| { - // Register the thread for use with the `WorkerLocal` type. - registry.register(); - - f(current_gcx) - }); + return run_in_thread_with_globals( + thread_stack_size, + edition, + sm_inputs, + extra_symbols, + |current_gcx, jobserver_proxy| { + // Register the thread for use with the `WorkerLocal` type. + registry.register(); + + f(current_gcx, jobserver_proxy) + }, + ); } let current_gcx = FromDyn::from(CurrentGcx::new()); let current_gcx2 = current_gcx.clone(); - let builder = rayon::ThreadPoolBuilder::new() + let proxy = Proxy::new(); + + let proxy_ = Arc::clone(&proxy); + let proxy__ = Arc::clone(&proxy); + let builder = rayon_core::ThreadPoolBuilder::new() .thread_name(|_| "rustc".to_string()) - .acquire_thread_handler(jobserver::acquire_thread) - .release_thread_handler(jobserver::release_thread) + .acquire_thread_handler(move || proxy_.acquire_thread()) + .release_thread_handler(move || proxy__.release_thread()) .num_threads(threads) .deadlock_handler(move || { // On deadlock, creates a new thread and forwards information in thread // locals to it. The new thread runs the deadlock handler. - // Get a `GlobalCtxt` reference from `CurrentGcx` as we cannot rely on having a - // `TyCtxt` TLS reference here. - let query_map = current_gcx2.access(|gcx| { - tls::enter_context(&tls::ImplicitCtxt::new(gcx), || { - tls::with(|tcx| QueryCtxt::new(tcx).collect_active_jobs()) - }) - }); - let query_map = FromDyn::from(query_map); + let current_gcx2 = current_gcx2.clone(); let registry = rayon_core::Registry::current(); + let session_globals = rustc_span::with_session_globals(|session_globals| { + session_globals as *const SessionGlobals as usize + }); thread::Builder::new() .name("rustc query cycle handler".to_string()) .spawn(move || { let on_panic = defer(|| { - eprintln!("query cycle handler thread panicked, aborting process"); + eprintln!("internal compiler error: query cycle handler thread panicked, aborting process"); // We need to abort here as we failed to resolve the deadlock, // otherwise the compiler could just hang, process::abort(); }); - break_query_cycles(query_map.into_inner(), ®istry); + + // Get a `GlobalCtxt` reference from `CurrentGcx` as we cannot rely on having a + // `TyCtxt` TLS reference here. + current_gcx2.access(|gcx| { + tls::enter_context(&tls::ImplicitCtxt::new(gcx), || { + tls::with(|tcx| { + // Accessing session globals is sound as they outlive `GlobalCtxt`. + // They are needed to hash query keys containing spans or symbols. + let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || { + // Ensure there was no errors collecting all active jobs. + // We need the complete map to ensure we find a cycle to break. + QueryCtxt::new(tcx).collect_active_jobs().ok().expect("failed to collect active queries in deadlock handler") + }); + break_query_cycles(query_map, ®istry); + }) + }) + }); + on_panic.disable(); }) .unwrap(); @@ -217,13 +260,13 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, // pool. Upon creation, each worker thread created gets a copy of the // session globals in TLS. This is possible because `SessionGlobals` impls // `Send` in the parallel compiler. - rustc_span::create_session_globals_then(edition, Some(sm_inputs), || { + rustc_span::create_session_globals_then(edition, extra_symbols, Some(sm_inputs), || { rustc_span::with_session_globals(|session_globals| { let session_globals = FromDyn::from(session_globals); builder .build_scoped( // Initialize each new worker thread when created. - move |thread: rayon::ThreadBuilder| { + move |thread: rayon_core::ThreadBuilder| { // Register the thread for use with the `WorkerLocal` type. registry.register(); @@ -232,7 +275,9 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, }) }, // Run `f` on the first thread in the thread pool. - move |pool: &rayon::ThreadPool| pool.install(|| f(current_gcx.into_inner())), + move |pool: &rayon_core::ThreadPool| { + pool.install(|| f(current_gcx.into_inner(), proxy)) + }, ) .unwrap() }) diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index e0e3bd0e30b16..526693d3de1f0 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -1,5 +1,10 @@ use std::str::Chars; +pub enum FrontmatterAllowed { + Yes, + No, +} + /// Peekable iterator over a char sequence. /// /// Next characters can be peeked via `first` method, @@ -8,6 +13,7 @@ pub struct Cursor<'a> { len_remaining: usize, /// Iterator over chars. Slightly faster than a &str. chars: Chars<'a>, + pub(crate) frontmatter_allowed: FrontmatterAllowed, #[cfg(debug_assertions)] prev: char, } @@ -15,10 +21,11 @@ pub struct Cursor<'a> { pub(crate) const EOF_CHAR: char = '\0'; impl<'a> Cursor<'a> { - pub fn new(input: &'a str) -> Cursor<'a> { + pub fn new(input: &'a str, frontmatter_allowed: FrontmatterAllowed) -> Cursor<'a> { Cursor { len_remaining: input.len(), chars: input.chars(), + frontmatter_allowed, #[cfg(debug_assertions)] prev: EOF_CHAR, } @@ -95,6 +102,11 @@ impl<'a> Cursor<'a> { Some(c) } + /// Moves to a substring by a number of bytes. + pub(crate) fn bump_bytes(&mut self, n: usize) { + self.chars = self.as_str()[n..].chars(); + } + /// Eats symbols while predicate returns true or until the end of file is reached. pub(crate) fn eat_while(&mut self, mut predicate: impl FnMut(char) -> bool) { // It was tried making optimized version of this for eg. line comments, but diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index bf18845a0830d..2374f38825099 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -23,11 +23,9 @@ // We want to be able to build this crate with a stable compiler, // so no `#![feature]` attributes should be added. #![deny(unstable_features)] -#![warn(unreachable_pub)] // tidy-alphabetical-end mod cursor; -pub mod unescape; #[cfg(test)] mod tests; @@ -37,8 +35,8 @@ pub use unicode_xid::UNICODE_VERSION as UNICODE_XID_VERSION; use self::LiteralKind::*; use self::TokenKind::*; -pub use crate::cursor::Cursor; use crate::cursor::EOF_CHAR; +pub use crate::cursor::{Cursor, FrontmatterAllowed}; /// Parsed token. /// It doesn't contain information about data that has been parsed, @@ -59,17 +57,27 @@ impl Token { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum TokenKind { /// A line comment, e.g. `// comment`. - LineComment { doc_style: Option }, + LineComment { + doc_style: Option, + }, /// A block comment, e.g. `/* block comment */`. /// /// Block comments can be recursive, so a sequence like `/* /* */` /// will not be considered terminated and will result in a parsing error. - BlockComment { doc_style: Option, terminated: bool }, + BlockComment { + doc_style: Option, + terminated: bool, + }, /// Any whitespace character sequence. Whitespace, + Frontmatter { + has_invalid_preceding_whitespace: bool, + invalid_infostring: bool, + }, + /// An identifier or keyword, e.g. `ident` or `continue`. Ident, @@ -111,10 +119,15 @@ pub enum TokenKind { /// this type will need to check for and reject that case. /// /// See [LiteralKind] for more details. - Literal { kind: LiteralKind, suffix_start: u32 }, + Literal { + kind: LiteralKind, + suffix_start: u32, + }, /// A lifetime, e.g. `'a`. - Lifetime { starts_with_number: bool }, + Lifetime { + starts_with_number: bool, + }, /// `;` Semi, @@ -282,7 +295,7 @@ pub fn strip_shebang(input: &str) -> Option { #[inline] pub fn validate_raw_str(input: &str, prefix_len: u32) -> Result<(), RawStrError> { debug_assert!(!input.is_empty()); - let mut cursor = Cursor::new(input); + let mut cursor = Cursor::new(input, FrontmatterAllowed::No); // Move past the leading `r` or `br`. for _ in 0..prefix_len { cursor.bump().unwrap(); @@ -292,7 +305,7 @@ pub fn validate_raw_str(input: &str, prefix_len: u32) -> Result<(), RawStrError> /// Creates an iterator that produces tokens from the input string. pub fn tokenize(input: &str) -> impl Iterator { - let mut cursor = Cursor::new(input); + let mut cursor = Cursor::new(input, FrontmatterAllowed::No); std::iter::from_fn(move || { let token = cursor.advance_token(); if token.kind != TokenKind::Eof { Some(token) } else { None } @@ -363,7 +376,34 @@ impl Cursor<'_> { Some(c) => c, None => return Token::new(TokenKind::Eof, 0), }; + let token_kind = match first_char { + c if matches!(self.frontmatter_allowed, FrontmatterAllowed::Yes) + && is_whitespace(c) => + { + let mut last = first_char; + while is_whitespace(self.first()) { + let Some(c) = self.bump() else { + break; + }; + last = c; + } + // invalid frontmatter opening as whitespace preceding it isn't newline. + // combine the whitespace and the frontmatter to a single token as we shall + // error later. + if last != '\n' && self.as_str().starts_with("---") { + self.bump(); + self.frontmatter(true) + } else { + Whitespace + } + } + '-' if matches!(self.frontmatter_allowed, FrontmatterAllowed::Yes) + && self.as_str().starts_with("--") => + { + // happy path + self.frontmatter(false) + } // Slash, comment or block comment. '/' => match self.first() { '/' => self.line_comment(), @@ -466,11 +506,110 @@ impl Cursor<'_> { c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident(), _ => Unknown, }; + if matches!(self.frontmatter_allowed, FrontmatterAllowed::Yes) + && !matches!(token_kind, Whitespace) + { + // stop allowing frontmatters after first non-whitespace token + self.frontmatter_allowed = FrontmatterAllowed::No; + } let res = Token::new(token_kind, self.pos_within_token()); self.reset_pos_within_token(); res } + /// Given that one `-` was eaten, eat the rest of the frontmatter. + fn frontmatter(&mut self, has_invalid_preceding_whitespace: bool) -> TokenKind { + debug_assert_eq!('-', self.prev()); + + let pos = self.pos_within_token(); + self.eat_while(|c| c == '-'); + + // one `-` is eaten by the caller. + let length_opening = self.pos_within_token() - pos + 1; + + // must be ensured by the caller + debug_assert!(length_opening >= 3); + + // whitespace between the opening and the infostring. + self.eat_while(|ch| ch != '\n' && is_whitespace(ch)); + + // copied from `eat_identifier`, but allows `.` in infostring to allow something like + // `---Cargo.toml` as a valid opener + if is_id_start(self.first()) { + self.bump(); + self.eat_while(|c| is_id_continue(c) || c == '.'); + } + + self.eat_while(|ch| ch != '\n' && is_whitespace(ch)); + let invalid_infostring = self.first() != '\n'; + + let mut s = self.as_str(); + let mut found = false; + while let Some(closing) = s.find(&"-".repeat(length_opening as usize)) { + let preceding_chars_start = s[..closing].rfind("\n").map_or(0, |i| i + 1); + if s[preceding_chars_start..closing].chars().all(is_whitespace) { + // candidate found + self.bump_bytes(closing); + // in case like + // ---cargo + // --- blahblah + // or + // ---cargo + // ---- + // combine those stuff into this frontmatter token such that it gets detected later. + self.eat_until(b'\n'); + found = true; + break; + } else { + s = &s[closing + length_opening as usize..]; + } + } + + if !found { + // recovery strategy: a closing statement might have precending whitespace/newline + // but not have enough dashes to properly close. In this case, we eat until there, + // and report a mismatch in the parser. + let mut rest = self.as_str(); + // We can look for a shorter closing (starting with four dashes but closing with three) + // and other indications that Rust has started and the infostring has ended. + let mut potential_closing = rest + .find("\n---") + // n.b. only in the case where there are dashes, we move the index to the line where + // the dashes start as we eat to include that line. For other cases those are Rust code + // and not included in the frontmatter. + .map(|x| x + 1) + .or_else(|| rest.find("\nuse ")) + .or_else(|| rest.find("\n//!")) + .or_else(|| rest.find("\n#![")); + + if potential_closing.is_none() { + // a less fortunate recovery if all else fails which finds any dashes preceded by whitespace + // on a standalone line. Might be wrong. + while let Some(closing) = rest.find("---") { + let preceding_chars_start = rest[..closing].rfind("\n").map_or(0, |i| i + 1); + if rest[preceding_chars_start..closing].chars().all(is_whitespace) { + // candidate found + potential_closing = Some(closing); + break; + } else { + rest = &rest[closing + 3..]; + } + } + } + + if let Some(potential_closing) = potential_closing { + // bump to the potential closing, and eat everything on that line. + self.bump_bytes(potential_closing); + self.eat_until(b'\n'); + } else { + // eat everything. this will get reported as an unclosed frontmatter. + self.eat_while(|_| true); + } + } + + Frontmatter { has_invalid_preceding_whitespace, invalid_infostring } + } + fn line_comment(&mut self) -> TokenKind { debug_assert!(self.prev() == '/' && self.first() == '/'); self.bump(); diff --git a/compiler/rustc_lexer/src/tests.rs b/compiler/rustc_lexer/src/tests.rs index 8203ae70b0700..fc8d9b9d57bc4 100644 --- a/compiler/rustc_lexer/src/tests.rs +++ b/compiler/rustc_lexer/src/tests.rs @@ -4,7 +4,7 @@ use super::*; fn check_raw_str(s: &str, expected: Result) { let s = &format!("r{}", s); - let mut cursor = Cursor::new(s); + let mut cursor = Cursor::new(s, FrontmatterAllowed::No); cursor.bump(); let res = cursor.raw_double_quoted_string(0); assert_eq!(res, expected); diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs deleted file mode 100644 index d6ea4249247f3..0000000000000 --- a/compiler/rustc_lexer/src/unescape.rs +++ /dev/null @@ -1,438 +0,0 @@ -//! Utilities for validating string and char literals and turning them into -//! values they represent. - -use std::ops::Range; -use std::str::Chars; - -use Mode::*; - -#[cfg(test)] -mod tests; - -/// Errors and warnings that can occur during string unescaping. They mostly -/// relate to malformed escape sequences, but there are a few that are about -/// other problems. -#[derive(Debug, PartialEq, Eq)] -pub enum EscapeError { - /// Expected 1 char, but 0 were found. - ZeroChars, - /// Expected 1 char, but more than 1 were found. - MoreThanOneChar, - - /// Escaped '\' character without continuation. - LoneSlash, - /// Invalid escape character (e.g. '\z'). - InvalidEscape, - /// Raw '\r' encountered. - BareCarriageReturn, - /// Raw '\r' encountered in raw string. - BareCarriageReturnInRawString, - /// Unescaped character that was expected to be escaped (e.g. raw '\t'). - EscapeOnlyChar, - - /// Numeric character escape is too short (e.g. '\x1'). - TooShortHexEscape, - /// Invalid character in numeric escape (e.g. '\xz') - InvalidCharInHexEscape, - /// Character code in numeric escape is non-ascii (e.g. '\xFF'). - OutOfRangeHexEscape, - - /// '\u' not followed by '{'. - NoBraceInUnicodeEscape, - /// Non-hexadecimal value in '\u{..}'. - InvalidCharInUnicodeEscape, - /// '\u{}' - EmptyUnicodeEscape, - /// No closing brace in '\u{..}', e.g. '\u{12'. - UnclosedUnicodeEscape, - /// '\u{_12}' - LeadingUnderscoreUnicodeEscape, - /// More than 6 characters in '\u{..}', e.g. '\u{10FFFF_FF}' - OverlongUnicodeEscape, - /// Invalid in-bound unicode character code, e.g. '\u{DFFF}'. - LoneSurrogateUnicodeEscape, - /// Out of bounds unicode character code, e.g. '\u{FFFFFF}'. - OutOfRangeUnicodeEscape, - - /// Unicode escape code in byte literal. - UnicodeEscapeInByte, - /// Non-ascii character in byte literal, byte string literal, or raw byte string literal. - NonAsciiCharInByte, - - // `\0` in a C string literal. - NulInCStr, - - /// After a line ending with '\', the next line contains whitespace - /// characters that are not skipped. - UnskippedWhitespaceWarning, - - /// After a line ending with '\', multiple lines are skipped. - MultipleSkippedLinesWarning, -} - -impl EscapeError { - /// Returns true for actual errors, as opposed to warnings. - pub fn is_fatal(&self) -> bool { - !matches!( - self, - EscapeError::UnskippedWhitespaceWarning | EscapeError::MultipleSkippedLinesWarning - ) - } -} - -/// Takes the contents of a unicode-only (non-mixed-utf8) literal (without -/// quotes) and produces a sequence of escaped characters or errors. -/// -/// Values are returned by invoking `callback`. For `Char` and `Byte` modes, -/// the callback will be called exactly once. -pub fn unescape_unicode(src: &str, mode: Mode, callback: &mut F) -where - F: FnMut(Range, Result), -{ - match mode { - Char | Byte => { - let mut chars = src.chars(); - let res = unescape_char_or_byte(&mut chars, mode); - callback(0..(src.len() - chars.as_str().len()), res); - } - Str | ByteStr => unescape_non_raw_common(src, mode, callback), - RawStr | RawByteStr => check_raw_common(src, mode, callback), - RawCStr => check_raw_common(src, mode, &mut |r, mut result| { - if let Ok('\0') = result { - result = Err(EscapeError::NulInCStr); - } - callback(r, result) - }), - CStr => unreachable!(), - } -} - -/// Used for mixed utf8 string literals, i.e. those that allow both unicode -/// chars and high bytes. -pub enum MixedUnit { - /// Used for ASCII chars (written directly or via `\x00`..`\x7f` escapes) - /// and Unicode chars (written directly or via `\u` escapes). - /// - /// For example, if '¥' appears in a string it is represented here as - /// `MixedUnit::Char('¥')`, and it will be appended to the relevant byte - /// string as the two-byte UTF-8 sequence `[0xc2, 0xa5]` - Char(char), - - /// Used for high bytes (`\x80`..`\xff`). - /// - /// For example, if `\xa5` appears in a string it is represented here as - /// `MixedUnit::HighByte(0xa5)`, and it will be appended to the relevant - /// byte string as the single byte `0xa5`. - HighByte(u8), -} - -impl From for MixedUnit { - fn from(c: char) -> Self { - MixedUnit::Char(c) - } -} - -impl From for MixedUnit { - fn from(n: u8) -> Self { - if n.is_ascii() { MixedUnit::Char(n as char) } else { MixedUnit::HighByte(n) } - } -} - -/// Takes the contents of a mixed-utf8 literal (without quotes) and produces -/// a sequence of escaped characters or errors. -/// -/// Values are returned by invoking `callback`. -pub fn unescape_mixed(src: &str, mode: Mode, callback: &mut F) -where - F: FnMut(Range, Result), -{ - match mode { - CStr => unescape_non_raw_common(src, mode, &mut |r, mut result| { - if let Ok(MixedUnit::Char('\0')) = result { - result = Err(EscapeError::NulInCStr); - } - callback(r, result) - }), - Char | Byte | Str | RawStr | ByteStr | RawByteStr | RawCStr => unreachable!(), - } -} - -/// Takes a contents of a char literal (without quotes), and returns an -/// unescaped char or an error. -pub fn unescape_char(src: &str) -> Result { - unescape_char_or_byte(&mut src.chars(), Char) -} - -/// Takes a contents of a byte literal (without quotes), and returns an -/// unescaped byte or an error. -pub fn unescape_byte(src: &str) -> Result { - unescape_char_or_byte(&mut src.chars(), Byte).map(byte_from_char) -} - -/// What kind of literal do we parse. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Mode { - Char, - - Byte, - - Str, - RawStr, - - ByteStr, - RawByteStr, - - CStr, - RawCStr, -} - -impl Mode { - pub fn in_double_quotes(self) -> bool { - match self { - Str | RawStr | ByteStr | RawByteStr | CStr | RawCStr => true, - Char | Byte => false, - } - } - - /// Are `\x80`..`\xff` allowed? - fn allow_high_bytes(self) -> bool { - match self { - Char | Str => false, - Byte | ByteStr | CStr => true, - RawStr | RawByteStr | RawCStr => unreachable!(), - } - } - - /// Are unicode (non-ASCII) chars allowed? - #[inline] - fn allow_unicode_chars(self) -> bool { - match self { - Byte | ByteStr | RawByteStr => false, - Char | Str | RawStr | CStr | RawCStr => true, - } - } - - /// Are unicode escapes (`\u`) allowed? - fn allow_unicode_escapes(self) -> bool { - match self { - Byte | ByteStr => false, - Char | Str | CStr => true, - RawByteStr | RawStr | RawCStr => unreachable!(), - } - } - - pub fn prefix_noraw(self) -> &'static str { - match self { - Char | Str | RawStr => "", - Byte | ByteStr | RawByteStr => "b", - CStr | RawCStr => "c", - } - } -} - -fn scan_escape + From>( - chars: &mut Chars<'_>, - mode: Mode, -) -> Result { - // Previous character was '\\', unescape what follows. - let res: char = match chars.next().ok_or(EscapeError::LoneSlash)? { - '"' => '"', - 'n' => '\n', - 'r' => '\r', - 't' => '\t', - '\\' => '\\', - '\'' => '\'', - '0' => '\0', - 'x' => { - // Parse hexadecimal character code. - - let hi = chars.next().ok_or(EscapeError::TooShortHexEscape)?; - let hi = hi.to_digit(16).ok_or(EscapeError::InvalidCharInHexEscape)?; - - let lo = chars.next().ok_or(EscapeError::TooShortHexEscape)?; - let lo = lo.to_digit(16).ok_or(EscapeError::InvalidCharInHexEscape)?; - - let value = (hi * 16 + lo) as u8; - - return if !mode.allow_high_bytes() && !value.is_ascii() { - Err(EscapeError::OutOfRangeHexEscape) - } else { - // This may be a high byte, but that will only happen if `T` is - // `MixedUnit`, because of the `allow_high_bytes` check above. - Ok(T::from(value)) - }; - } - 'u' => return scan_unicode(chars, mode.allow_unicode_escapes()).map(T::from), - _ => return Err(EscapeError::InvalidEscape), - }; - Ok(T::from(res)) -} - -fn scan_unicode(chars: &mut Chars<'_>, allow_unicode_escapes: bool) -> Result { - // We've parsed '\u', now we have to parse '{..}'. - - if chars.next() != Some('{') { - return Err(EscapeError::NoBraceInUnicodeEscape); - } - - // First character must be a hexadecimal digit. - let mut n_digits = 1; - let mut value: u32 = match chars.next().ok_or(EscapeError::UnclosedUnicodeEscape)? { - '_' => return Err(EscapeError::LeadingUnderscoreUnicodeEscape), - '}' => return Err(EscapeError::EmptyUnicodeEscape), - c => c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?, - }; - - // First character is valid, now parse the rest of the number - // and closing brace. - loop { - match chars.next() { - None => return Err(EscapeError::UnclosedUnicodeEscape), - Some('_') => continue, - Some('}') => { - if n_digits > 6 { - return Err(EscapeError::OverlongUnicodeEscape); - } - - // Incorrect syntax has higher priority for error reporting - // than unallowed value for a literal. - if !allow_unicode_escapes { - return Err(EscapeError::UnicodeEscapeInByte); - } - - break std::char::from_u32(value).ok_or({ - if value > 0x10FFFF { - EscapeError::OutOfRangeUnicodeEscape - } else { - EscapeError::LoneSurrogateUnicodeEscape - } - }); - } - Some(c) => { - let digit: u32 = c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?; - n_digits += 1; - if n_digits > 6 { - // Stop updating value since we're sure that it's incorrect already. - continue; - } - value = value * 16 + digit; - } - }; - } -} - -#[inline] -fn ascii_check(c: char, allow_unicode_chars: bool) -> Result { - if allow_unicode_chars || c.is_ascii() { Ok(c) } else { Err(EscapeError::NonAsciiCharInByte) } -} - -fn unescape_char_or_byte(chars: &mut Chars<'_>, mode: Mode) -> Result { - let c = chars.next().ok_or(EscapeError::ZeroChars)?; - let res = match c { - '\\' => scan_escape(chars, mode), - '\n' | '\t' | '\'' => Err(EscapeError::EscapeOnlyChar), - '\r' => Err(EscapeError::BareCarriageReturn), - _ => ascii_check(c, mode.allow_unicode_chars()), - }?; - if chars.next().is_some() { - return Err(EscapeError::MoreThanOneChar); - } - Ok(res) -} - -/// Takes a contents of a string literal (without quotes) and produces a -/// sequence of escaped characters or errors. -fn unescape_non_raw_common + From>(src: &str, mode: Mode, callback: &mut F) -where - F: FnMut(Range, Result), -{ - let mut chars = src.chars(); - let allow_unicode_chars = mode.allow_unicode_chars(); // get this outside the loop - - // The `start` and `end` computation here is complicated because - // `skip_ascii_whitespace` makes us to skip over chars without counting - // them in the range computation. - while let Some(c) = chars.next() { - let start = src.len() - chars.as_str().len() - c.len_utf8(); - let res = match c { - '\\' => { - match chars.clone().next() { - Some('\n') => { - // Rust language specification requires us to skip whitespaces - // if unescaped '\' character is followed by '\n'. - // For details see [Rust language reference] - // (https://doc.rust-lang.org/reference/tokens.html#string-literals). - skip_ascii_whitespace(&mut chars, start, &mut |range, err| { - callback(range, Err(err)) - }); - continue; - } - _ => scan_escape::(&mut chars, mode), - } - } - '"' => Err(EscapeError::EscapeOnlyChar), - '\r' => Err(EscapeError::BareCarriageReturn), - _ => ascii_check(c, allow_unicode_chars).map(T::from), - }; - let end = src.len() - chars.as_str().len(); - callback(start..end, res); - } -} - -fn skip_ascii_whitespace(chars: &mut Chars<'_>, start: usize, callback: &mut F) -where - F: FnMut(Range, EscapeError), -{ - let tail = chars.as_str(); - let first_non_space = tail - .bytes() - .position(|b| b != b' ' && b != b'\t' && b != b'\n' && b != b'\r') - .unwrap_or(tail.len()); - if tail[1..first_non_space].contains('\n') { - // The +1 accounts for the escaping slash. - let end = start + first_non_space + 1; - callback(start..end, EscapeError::MultipleSkippedLinesWarning); - } - let tail = &tail[first_non_space..]; - if let Some(c) = tail.chars().next() { - if c.is_whitespace() { - // For error reporting, we would like the span to contain the character that was not - // skipped. The +1 is necessary to account for the leading \ that started the escape. - let end = start + first_non_space + c.len_utf8() + 1; - callback(start..end, EscapeError::UnskippedWhitespaceWarning); - } - } - *chars = tail.chars(); -} - -/// Takes a contents of a string literal (without quotes) and produces a -/// sequence of characters or errors. -/// NOTE: Raw strings do not perform any explicit character escaping, here we -/// only produce errors on bare CR. -fn check_raw_common(src: &str, mode: Mode, callback: &mut F) -where - F: FnMut(Range, Result), -{ - let mut chars = src.chars(); - let allow_unicode_chars = mode.allow_unicode_chars(); // get this outside the loop - - // The `start` and `end` computation here matches the one in - // `unescape_non_raw_common` for consistency, even though this function - // doesn't have to worry about skipping any chars. - while let Some(c) = chars.next() { - let start = src.len() - chars.as_str().len() - c.len_utf8(); - let res = match c { - '\r' => Err(EscapeError::BareCarriageReturnInRawString), - _ => ascii_check(c, allow_unicode_chars), - }; - let end = src.len() - chars.as_str().len(); - callback(start..end, res); - } -} - -#[inline] -pub fn byte_from_char(c: char) -> u8 { - let res = c as u32; - debug_assert!(res <= u8::MAX as u32, "guaranteed because of ByteStr"); - res as u8 -} diff --git a/compiler/rustc_lexer/src/unescape/tests.rs b/compiler/rustc_lexer/src/unescape/tests.rs deleted file mode 100644 index 5b99495f47581..0000000000000 --- a/compiler/rustc_lexer/src/unescape/tests.rs +++ /dev/null @@ -1,286 +0,0 @@ -use super::*; - -#[test] -fn test_unescape_char_bad() { - fn check(literal_text: &str, expected_error: EscapeError) { - assert_eq!(unescape_char(literal_text), Err(expected_error)); - } - - check("", EscapeError::ZeroChars); - check(r"\", EscapeError::LoneSlash); - - check("\n", EscapeError::EscapeOnlyChar); - check("\t", EscapeError::EscapeOnlyChar); - check("'", EscapeError::EscapeOnlyChar); - check("\r", EscapeError::BareCarriageReturn); - - check("spam", EscapeError::MoreThanOneChar); - check(r"\x0ff", EscapeError::MoreThanOneChar); - check(r#"\"a"#, EscapeError::MoreThanOneChar); - check(r"\na", EscapeError::MoreThanOneChar); - check(r"\ra", EscapeError::MoreThanOneChar); - check(r"\ta", EscapeError::MoreThanOneChar); - check(r"\\a", EscapeError::MoreThanOneChar); - check(r"\'a", EscapeError::MoreThanOneChar); - check(r"\0a", EscapeError::MoreThanOneChar); - check(r"\u{0}x", EscapeError::MoreThanOneChar); - check(r"\u{1F63b}}", EscapeError::MoreThanOneChar); - - check(r"\v", EscapeError::InvalidEscape); - check(r"\💩", EscapeError::InvalidEscape); - check(r"\●", EscapeError::InvalidEscape); - check("\\\r", EscapeError::InvalidEscape); - - check(r"\x", EscapeError::TooShortHexEscape); - check(r"\x0", EscapeError::TooShortHexEscape); - check(r"\xf", EscapeError::TooShortHexEscape); - check(r"\xa", EscapeError::TooShortHexEscape); - check(r"\xx", EscapeError::InvalidCharInHexEscape); - check(r"\xы", EscapeError::InvalidCharInHexEscape); - check(r"\x🦀", EscapeError::InvalidCharInHexEscape); - check(r"\xtt", EscapeError::InvalidCharInHexEscape); - check(r"\xff", EscapeError::OutOfRangeHexEscape); - check(r"\xFF", EscapeError::OutOfRangeHexEscape); - check(r"\x80", EscapeError::OutOfRangeHexEscape); - - check(r"\u", EscapeError::NoBraceInUnicodeEscape); - check(r"\u[0123]", EscapeError::NoBraceInUnicodeEscape); - check(r"\u{0x}", EscapeError::InvalidCharInUnicodeEscape); - check(r"\u{", EscapeError::UnclosedUnicodeEscape); - check(r"\u{0000", EscapeError::UnclosedUnicodeEscape); - check(r"\u{}", EscapeError::EmptyUnicodeEscape); - check(r"\u{_0000}", EscapeError::LeadingUnderscoreUnicodeEscape); - check(r"\u{0000000}", EscapeError::OverlongUnicodeEscape); - check(r"\u{FFFFFF}", EscapeError::OutOfRangeUnicodeEscape); - check(r"\u{ffffff}", EscapeError::OutOfRangeUnicodeEscape); - check(r"\u{ffffff}", EscapeError::OutOfRangeUnicodeEscape); - - check(r"\u{DC00}", EscapeError::LoneSurrogateUnicodeEscape); - check(r"\u{DDDD}", EscapeError::LoneSurrogateUnicodeEscape); - check(r"\u{DFFF}", EscapeError::LoneSurrogateUnicodeEscape); - - check(r"\u{D800}", EscapeError::LoneSurrogateUnicodeEscape); - check(r"\u{DAAA}", EscapeError::LoneSurrogateUnicodeEscape); - check(r"\u{DBFF}", EscapeError::LoneSurrogateUnicodeEscape); -} - -#[test] -fn test_unescape_char_good() { - fn check(literal_text: &str, expected_char: char) { - assert_eq!(unescape_char(literal_text), Ok(expected_char)); - } - - check("a", 'a'); - check("ы", 'ы'); - check("🦀", '🦀'); - - check(r#"\""#, '"'); - check(r"\n", '\n'); - check(r"\r", '\r'); - check(r"\t", '\t'); - check(r"\\", '\\'); - check(r"\'", '\''); - check(r"\0", '\0'); - - check(r"\x00", '\0'); - check(r"\x5a", 'Z'); - check(r"\x5A", 'Z'); - check(r"\x7f", 127 as char); - - check(r"\u{0}", '\0'); - check(r"\u{000000}", '\0'); - check(r"\u{41}", 'A'); - check(r"\u{0041}", 'A'); - check(r"\u{00_41}", 'A'); - check(r"\u{4__1__}", 'A'); - check(r"\u{1F63b}", '😻'); -} - -#[test] -fn test_unescape_str_warn() { - fn check(literal: &str, expected: &[(Range, Result)]) { - let mut unescaped = Vec::with_capacity(literal.len()); - unescape_unicode(literal, Mode::Str, &mut |range, res| unescaped.push((range, res))); - assert_eq!(unescaped, expected); - } - - // Check we can handle escaped newlines at the end of a file. - check("\\\n", &[]); - check("\\\n ", &[]); - - check( - "\\\n \u{a0} x", - &[ - (0..5, Err(EscapeError::UnskippedWhitespaceWarning)), - (3..5, Ok('\u{a0}')), - (5..6, Ok(' ')), - (6..7, Ok('x')), - ], - ); - check("\\\n \n x", &[(0..7, Err(EscapeError::MultipleSkippedLinesWarning)), (7..8, Ok('x'))]); -} - -#[test] -fn test_unescape_str_good() { - fn check(literal_text: &str, expected: &str) { - let mut buf = Ok(String::with_capacity(literal_text.len())); - unescape_unicode(literal_text, Mode::Str, &mut |range, c| { - if let Ok(b) = &mut buf { - match c { - Ok(c) => b.push(c), - Err(e) => buf = Err((range, e)), - } - } - }); - assert_eq!(buf.as_deref(), Ok(expected)) - } - - check("foo", "foo"); - check("", ""); - check(" \t\n", " \t\n"); - - check("hello \\\n world", "hello world"); - check("thread's", "thread's") -} - -#[test] -fn test_unescape_byte_bad() { - fn check(literal_text: &str, expected_error: EscapeError) { - assert_eq!(unescape_byte(literal_text), Err(expected_error)); - } - - check("", EscapeError::ZeroChars); - check(r"\", EscapeError::LoneSlash); - - check("\n", EscapeError::EscapeOnlyChar); - check("\t", EscapeError::EscapeOnlyChar); - check("'", EscapeError::EscapeOnlyChar); - check("\r", EscapeError::BareCarriageReturn); - - check("spam", EscapeError::MoreThanOneChar); - check(r"\x0ff", EscapeError::MoreThanOneChar); - check(r#"\"a"#, EscapeError::MoreThanOneChar); - check(r"\na", EscapeError::MoreThanOneChar); - check(r"\ra", EscapeError::MoreThanOneChar); - check(r"\ta", EscapeError::MoreThanOneChar); - check(r"\\a", EscapeError::MoreThanOneChar); - check(r"\'a", EscapeError::MoreThanOneChar); - check(r"\0a", EscapeError::MoreThanOneChar); - - check(r"\v", EscapeError::InvalidEscape); - check(r"\💩", EscapeError::InvalidEscape); - check(r"\●", EscapeError::InvalidEscape); - - check(r"\x", EscapeError::TooShortHexEscape); - check(r"\x0", EscapeError::TooShortHexEscape); - check(r"\xa", EscapeError::TooShortHexEscape); - check(r"\xf", EscapeError::TooShortHexEscape); - check(r"\xx", EscapeError::InvalidCharInHexEscape); - check(r"\xы", EscapeError::InvalidCharInHexEscape); - check(r"\x🦀", EscapeError::InvalidCharInHexEscape); - check(r"\xtt", EscapeError::InvalidCharInHexEscape); - - check(r"\u", EscapeError::NoBraceInUnicodeEscape); - check(r"\u[0123]", EscapeError::NoBraceInUnicodeEscape); - check(r"\u{0x}", EscapeError::InvalidCharInUnicodeEscape); - check(r"\u{", EscapeError::UnclosedUnicodeEscape); - check(r"\u{0000", EscapeError::UnclosedUnicodeEscape); - check(r"\u{}", EscapeError::EmptyUnicodeEscape); - check(r"\u{_0000}", EscapeError::LeadingUnderscoreUnicodeEscape); - check(r"\u{0000000}", EscapeError::OverlongUnicodeEscape); - - check("ы", EscapeError::NonAsciiCharInByte); - check("🦀", EscapeError::NonAsciiCharInByte); - - check(r"\u{0}", EscapeError::UnicodeEscapeInByte); - check(r"\u{000000}", EscapeError::UnicodeEscapeInByte); - check(r"\u{41}", EscapeError::UnicodeEscapeInByte); - check(r"\u{0041}", EscapeError::UnicodeEscapeInByte); - check(r"\u{00_41}", EscapeError::UnicodeEscapeInByte); - check(r"\u{4__1__}", EscapeError::UnicodeEscapeInByte); - check(r"\u{1F63b}", EscapeError::UnicodeEscapeInByte); - check(r"\u{0}x", EscapeError::UnicodeEscapeInByte); - check(r"\u{1F63b}}", EscapeError::UnicodeEscapeInByte); - check(r"\u{FFFFFF}", EscapeError::UnicodeEscapeInByte); - check(r"\u{ffffff}", EscapeError::UnicodeEscapeInByte); - check(r"\u{ffffff}", EscapeError::UnicodeEscapeInByte); - check(r"\u{DC00}", EscapeError::UnicodeEscapeInByte); - check(r"\u{DDDD}", EscapeError::UnicodeEscapeInByte); - check(r"\u{DFFF}", EscapeError::UnicodeEscapeInByte); - check(r"\u{D800}", EscapeError::UnicodeEscapeInByte); - check(r"\u{DAAA}", EscapeError::UnicodeEscapeInByte); - check(r"\u{DBFF}", EscapeError::UnicodeEscapeInByte); -} - -#[test] -fn test_unescape_byte_good() { - fn check(literal_text: &str, expected_byte: u8) { - assert_eq!(unescape_byte(literal_text), Ok(expected_byte)); - } - - check("a", b'a'); - - check(r#"\""#, b'"'); - check(r"\n", b'\n'); - check(r"\r", b'\r'); - check(r"\t", b'\t'); - check(r"\\", b'\\'); - check(r"\'", b'\''); - check(r"\0", b'\0'); - - check(r"\x00", b'\0'); - check(r"\x5a", b'Z'); - check(r"\x5A", b'Z'); - check(r"\x7f", 127); - check(r"\x80", 128); - check(r"\xff", 255); - check(r"\xFF", 255); -} - -#[test] -fn test_unescape_byte_str_good() { - fn check(literal_text: &str, expected: &[u8]) { - let mut buf = Ok(Vec::with_capacity(literal_text.len())); - unescape_unicode(literal_text, Mode::ByteStr, &mut |range, c| { - if let Ok(b) = &mut buf { - match c { - Ok(c) => b.push(byte_from_char(c)), - Err(e) => buf = Err((range, e)), - } - } - }); - assert_eq!(buf.as_deref(), Ok(expected)) - } - - check("foo", b"foo"); - check("", b""); - check(" \t\n", b" \t\n"); - - check("hello \\\n world", b"hello world"); - check("thread's", b"thread's") -} - -#[test] -fn test_unescape_raw_str() { - fn check(literal: &str, expected: &[(Range, Result)]) { - let mut unescaped = Vec::with_capacity(literal.len()); - unescape_unicode(literal, Mode::RawStr, &mut |range, res| unescaped.push((range, res))); - assert_eq!(unescaped, expected); - } - - check("\r", &[(0..1, Err(EscapeError::BareCarriageReturnInRawString))]); - check("\rx", &[(0..1, Err(EscapeError::BareCarriageReturnInRawString)), (1..2, Ok('x'))]); -} - -#[test] -fn test_unescape_raw_byte_str() { - fn check(literal: &str, expected: &[(Range, Result)]) { - let mut unescaped = Vec::with_capacity(literal.len()); - unescape_unicode(literal, Mode::RawByteStr, &mut |range, res| unescaped.push((range, res))); - assert_eq!(unescaped, expected); - } - - check("\r", &[(0..1, Err(EscapeError::BareCarriageReturnInRawString))]); - check("🦀", &[(0..4, Err(EscapeError::NonAsciiCharInByte))]); - check("🦀a", &[(0..4, Err(EscapeError::NonAsciiCharInByte)), (4..5, Ok('a'))]); -} diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index d6014f5006ad0..64751eaf1feae 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -23,7 +24,6 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -rustc_type_ir = { path = "../rustc_type_ir" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" unicode-security = "0.1.0" diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index d51865810b9a8..08180bf8f8b2c 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -271,7 +271,7 @@ lint_expectation = this lint expectation is unfulfilled lint_extern_crate_not_idiomatic = `extern crate` is not idiomatic in the new edition .suggestion = convert it to a `use` -lint_extern_without_abi = extern declarations without an explicit ABI are deprecated +lint_extern_without_abi = `extern` declarations without an explicit ABI are deprecated .label = ABI should be specified here .suggestion = explicitly specify the {$default_abi} ABI @@ -360,6 +360,14 @@ lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than pos lint_impl_trait_redundant_captures = all possible in-scope parameters are already captured, so `use<...>` syntax is redundant .suggestion = remove the `use<...>` syntax +lint_implicit_unsafe_autorefs = implicit autoref creates a reference to the dereference of a raw pointer + .note = creating a reference requires the pointer target to be valid and imposes aliasing requirements + .raw_ptr = this raw pointer has type `{$raw_ptr_ty}` + .autoref = autoref is being applied to this expression, resulting in: `{$autoref_ty}` + .overloaded_deref = references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations + .method_def = method calls to `{$method_name}` require a reference + .suggestion = try using a raw pointer method instead; or if this reference is intentional, make it explicit + lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe .label = not FFI-safe .note = the type is defined here @@ -456,6 +464,10 @@ lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be dir lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable +lint_invalid_null_arguments = calling this function with a null pointer is undefined behavior, even if the result of the function is unused + .origin = null pointer originates from here + .doc = for more information, visit and + lint_invalid_reference_casting_assign_to_ref = assigning to `&T` is undefined behavior, consider using an `UnsafeCell` .label = casting happened here @@ -680,15 +692,6 @@ lint_private_extern_crate_reexport = extern crate `{$ident}` is private and cann lint_proc_macro_derive_resolution_fallback = cannot find {$ns} `{$ident}` in this scope .label = names from parent modules are not accessible without an explicit import -lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false - .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value - .label = expression has type `{$orig_ty}` - -lint_ptr_null_checks_fn_ret = returned pointer of `{$fn_name}` call is never null, so checking it for null will always return false - -lint_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false - .label = expression has type `{$orig_ty}` - lint_query_instability = using `{$query}` can result in unstable query results .note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale @@ -762,7 +765,7 @@ lint_single_use_lifetime = lifetime parameter `{$ident}` only used once lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` -lint_static_mut_refs_lint = creating a {$shared_label}reference to mutable static is discouraged +lint_static_mut_refs_lint = creating a {$shared_label}reference to mutable static .label = {$shared_label}reference to mutable static .suggestion = use `&raw const` instead to create a raw pointer .suggestion_mut = use `&raw mut` instead to create a raw pointer @@ -799,6 +802,9 @@ lint_tykind_kind = usage of `ty::TyKind::` lint_type_ir_inherent_usage = do not use `rustc_type_ir::inherent` unless you're inside of the trait solver .note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler +lint_type_ir_trait_usage = do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver + .note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler + lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing .label = argument has type `{$arg_ty}` .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value @@ -978,6 +984,15 @@ lint_unused_result = unused result of type `{$ty}` lint_use_let_underscore_ignore_suggestion = use `let _ = ...` to ignore the expression or result +lint_useless_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false + .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value + .label = expression has type `{$orig_ty}` + +lint_useless_ptr_null_checks_fn_ret = returned pointer of `{$fn_name}` call is never null, so checking it for null will always return false + +lint_useless_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false + .label = expression has type `{$orig_ty}` + lint_uses_power_alignment = repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type lint_variant_size_differences = diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs new file mode 100644 index 0000000000000..5de2cbf9939db --- /dev/null +++ b/compiler/rustc_lint/src/autorefs.rs @@ -0,0 +1,176 @@ +use rustc_ast::{BorrowKind, UnOp}; +use rustc_hir::{Expr, ExprKind, Mutability}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDeref}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::sym; + +use crate::lints::{ + ImplicitUnsafeAutorefsDiag, ImplicitUnsafeAutorefsMethodNote, ImplicitUnsafeAutorefsOrigin, + ImplicitUnsafeAutorefsSuggestion, +}; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `dangerous_implicit_autorefs` lint checks for implicitly taken references + /// to dereferences of raw pointers. + /// + /// ### Example + /// + /// ```rust + /// unsafe fn fun(ptr: *mut [u8]) -> *mut [u8] { + /// unsafe { &raw mut (*ptr)[..16] } + /// // ^^^^^^ this calls `IndexMut::index_mut(&mut ..., ..16)`, + /// // implicitly creating a reference + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// When working with raw pointers it's usually undesirable to create references, + /// since they inflict additional safety requirements. Unfortunately, it's possible + /// to take a reference to the dereference of a raw pointer implicitly, which inflicts + /// the usual reference requirements. + /// + /// If you are sure that you can soundly take a reference, then you can take it explicitly: + /// + /// ```rust + /// unsafe fn fun(ptr: *mut [u8]) -> *mut [u8] { + /// unsafe { &raw mut (&mut *ptr)[..16] } + /// } + /// ``` + /// + /// Otherwise try to find an alternative way to achive your goals using only raw pointers: + /// + /// ```rust + /// use std::ptr; + /// + /// fn fun(ptr: *mut [u8]) -> *mut [u8] { + /// ptr::slice_from_raw_parts_mut(ptr.cast(), 16) + /// } + /// ``` + pub DANGEROUS_IMPLICIT_AUTOREFS, + Warn, + "implicit reference to a dereference of a raw pointer", + report_in_external_macro +} + +declare_lint_pass!(ImplicitAutorefs => [DANGEROUS_IMPLICIT_AUTOREFS]); + +impl<'tcx> LateLintPass<'tcx> for ImplicitAutorefs { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // This logic has mostly been taken from + // + + // 5. Either of the following: + // a. A deref followed by any non-deref place projection (that intermediate + // deref will typically be auto-inserted). + // b. A method call annotated with `#[rustc_no_implicit_refs]`. + // c. A deref followed by a `&raw const` or `&raw mut`. + let mut is_coming_from_deref = false; + let inner = match expr.kind { + ExprKind::AddrOf(BorrowKind::Raw, _, inner) => match inner.kind { + ExprKind::Unary(UnOp::Deref, inner) => { + is_coming_from_deref = true; + inner + } + _ => return, + }, + ExprKind::Index(base, _, _) => base, + ExprKind::MethodCall(_, inner, _, _) => { + // PERF: Checking of `#[rustc_no_implicit_refs]` is deferred below + // because checking for attribute is a bit costly. + inner + } + ExprKind::Field(inner, _) => inner, + _ => return, + }; + + let typeck = cx.typeck_results(); + let adjustments_table = typeck.adjustments(); + + if let Some(adjustments) = adjustments_table.get(inner.hir_id) + // 4. Any number of automatically inserted deref/derefmut calls. + && let adjustments = peel_derefs_adjustments(&**adjustments) + // 3. An automatically inserted reference (might come from a deref). + && let [adjustment] = adjustments + && let Some((borrow_mutbl, through_overloaded_deref)) = has_implicit_borrow(adjustment) + && let ExprKind::Unary(UnOp::Deref, dereferenced) = + // 2. Any number of place projections. + peel_place_mappers(inner).kind + // 1. Deref of a raw pointer. + && typeck.expr_ty(dereferenced).is_raw_ptr() + && let method_did = match expr.kind { + // PERF: 5. b. A method call annotated with `#[rustc_no_implicit_refs]` + ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), + _ => None, + } + && method_did.map(|did| cx.tcx.has_attr(did, sym::rustc_no_implicit_autorefs)).unwrap_or(true) + { + cx.emit_span_lint( + DANGEROUS_IMPLICIT_AUTOREFS, + expr.span.source_callsite(), + ImplicitUnsafeAutorefsDiag { + raw_ptr_span: dereferenced.span, + raw_ptr_ty: typeck.expr_ty(dereferenced), + origin: if through_overloaded_deref { + ImplicitUnsafeAutorefsOrigin::OverloadedDeref + } else { + ImplicitUnsafeAutorefsOrigin::Autoref { + autoref_span: inner.span, + autoref_ty: typeck.expr_ty_adjusted(inner), + } + }, + method: method_did.map(|did| ImplicitUnsafeAutorefsMethodNote { + def_span: cx.tcx.def_span(did), + method_name: cx.tcx.item_name(did), + }), + suggestion: ImplicitUnsafeAutorefsSuggestion { + mutbl: borrow_mutbl.ref_prefix_str(), + deref: if is_coming_from_deref { "*" } else { "" }, + start_span: inner.span.shrink_to_lo(), + end_span: inner.span.shrink_to_hi(), + }, + }, + ) + } + } +} + +/// Peels expressions from `expr` that can map a place. +fn peel_place_mappers<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + loop { + match expr.kind { + ExprKind::Index(base, _idx, _) => expr = &base, + ExprKind::Field(e, _) => expr = &e, + _ => break expr, + } + } +} + +/// Peel derefs adjustments until the last last element. +fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustment<'a>] { + while let [Adjustment { kind: Adjust::Deref(_), .. }, end @ ..] = adjs + && !end.is_empty() + { + adjs = end; + } + adjs +} + +/// Test if some adjustment has some implicit borrow. +/// +/// Returns `Some((mutability, was_an_overloaded_deref))` if the argument adjustment is +/// an implicit borrow (or has an implicit borrow via an overloaded deref). +fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Mutability, bool)> { + match kind { + &Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some((mutbl, true)), + &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some((mutbl.into(), false)), + Adjust::NeverToAny + | Adjust::Pointer(..) + | Adjust::ReborrowPin(..) + | Adjust::Deref(None) + | Adjust::Borrow(AutoBorrow::RawPtr(..)) => None, + } +} diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index f7be37dc4a2c3..95e31e4af1e39 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -29,6 +29,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::FnKind as HirFnKind; use rustc_hir::{Body, FnDecl, GenericParamKind, PatKind, PredicateOrigin}; use rustc_middle::bug; +use rustc_middle::lint::LevelAndSource; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef}; @@ -330,7 +331,6 @@ impl EarlyLintPass for UnsafeCode { if let FnKind::Fn( ctxt, _, - _, ast::Fn { sig: ast::FnSig { header: ast::FnHeader { safety: ast::Safety::Unsafe(_), .. }, .. }, body, @@ -419,7 +419,7 @@ impl MissingDoc { return; } - let attrs = cx.tcx.hir().attrs(cx.tcx.local_def_id_to_hir_id(def_id)); + let attrs = cx.tcx.hir_attrs(cx.tcx.local_def_id_to_hir_id(def_id)); let has_doc = attrs.iter().any(has_doc); if !has_doc { cx.emit_span_lint( @@ -441,7 +441,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { // so we will continue to exclude them for compatibility. // // The documentation on `ExternCrate` is not used at the moment so no need to warn for it. - if let hir::ItemKind::Impl(..) | hir::ItemKind::Use(..) | hir::ItemKind::ExternCrate(_) = + if let hir::ItemKind::Impl(..) | hir::ItemKind::Use(..) | hir::ItemKind::ExternCrate(..) = it.kind { return; @@ -545,21 +545,21 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { return; } let (def, ty) = match item.kind { - hir::ItemKind::Struct(_, ast_generics) => { + hir::ItemKind::Struct(_, _, ast_generics) => { if !ast_generics.params.is_empty() { return; } let def = cx.tcx.adt_def(item.owner_id); (def, Ty::new_adt(cx.tcx, def, ty::List::empty())) } - hir::ItemKind::Union(_, ast_generics) => { + hir::ItemKind::Union(_, _, ast_generics) => { if !ast_generics.params.is_empty() { return; } let def = cx.tcx.adt_def(item.owner_id); (def, Ty::new_adt(cx.tcx, def, ty::List::empty())) } - hir::ItemKind::Enum(_, ast_generics) => { + hir::ItemKind::Enum(_, _, ast_generics) => { if !ast_generics.params.is_empty() { return; } @@ -695,7 +695,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { } // Avoid listing trait impls if the trait is allowed. - let (level, _) = cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id()); + let LevelAndSource { level, .. } = + cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id()); if level == Level::Allow { return; } @@ -778,21 +779,19 @@ impl EarlyLintPass for AnonymousParameters { } if let ast::AssocItemKind::Fn(box 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::Empty { - let ty_snip = cx.sess().source_map().span_to_snippet(arg.ty.span); + if let ast::PatKind::Missing = arg.pat.kind { + 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) - }; - cx.emit_span_lint( - ANONYMOUS_PARAMETERS, - arg.pat.span, - BuiltinAnonymousParams { suggestion: (arg.pat.span, appl), ty_snip }, - ); - } + let (ty_snip, appl) = if let Ok(ref snip) = ty_snip { + (snip.as_str(), Applicability::MachineApplicable) + } else { + ("", Applicability::HasPlaceholders) + }; + cx.emit_span_lint( + ANONYMOUS_PARAMETERS, + arg.pat.span, + BuiltinAnonymousParams { suggestion: (arg.pat.span, appl), ty_snip }, + ); } } } @@ -949,7 +948,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,compile_fail,edition2021 /// #[no_mangle] /// const FOO: i32 = 5; /// ``` @@ -977,6 +976,9 @@ declare_lint! { /// ```rust /// #[unsafe(no_mangle)] /// fn foo(t: T) {} + /// + /// #[unsafe(export_name = "bar")] + /// fn bar(t: T) {} /// ``` /// /// {{produces}} @@ -984,10 +986,11 @@ declare_lint! { /// ### Explanation /// /// A function with generics must have its symbol mangled to accommodate - /// the generic parameter. The [`no_mangle` attribute] has no effect in - /// this situation, and should be removed. + /// the generic parameter. The [`no_mangle`] and [`export_name`] attributes + /// have no effect in this situation, and should be removed. /// - /// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute + /// [`no_mangle`]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute + /// [`export_name`]: https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute NO_MANGLE_GENERIC_ITEMS, Warn, "generic items must be mangled" @@ -997,8 +1000,8 @@ declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GEN impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - let attrs = cx.tcx.hir().attrs(it.hir_id()); - let check_no_mangle_on_generic_fn = |no_mangle_attr: &hir::Attribute, + let attrs = cx.tcx.hir_attrs(it.hir_id()); + let check_no_mangle_on_generic_fn = |attr: &hir::Attribute, impl_generics: Option<&hir::Generics<'_>>, generics: &hir::Generics<'_>, span| { @@ -1011,7 +1014,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { cx.emit_span_lint( NO_MANGLE_GENERIC_ITEMS, span, - BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span() }, + BuiltinNoMangleGeneric { suggestion: attr.span() }, ); break; } @@ -1020,8 +1023,10 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { }; match it.kind { hir::ItemKind::Fn { generics, .. } => { - if let Some(no_mangle_attr) = attr::find_by_name(attrs, sym::no_mangle) { - check_no_mangle_on_generic_fn(no_mangle_attr, None, generics, it.span); + if let Some(attr) = attr::find_by_name(attrs, sym::export_name) + .or_else(|| attr::find_by_name(attrs, sym::no_mangle)) + { + check_no_mangle_on_generic_fn(attr, None, generics, it.span); } } hir::ItemKind::Const(..) => { @@ -1049,11 +1054,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { hir::ItemKind::Impl(hir::Impl { generics, items, .. }) => { for it in *items { if let hir::AssocItemKind::Fn { .. } = it.kind { - if let Some(no_mangle_attr) = - attr::find_by_name(cx.tcx.hir().attrs(it.id.hir_id()), sym::no_mangle) + let attrs = cx.tcx.hir_attrs(it.id.hir_id()); + if let Some(attr) = attr::find_by_name(attrs, sym::export_name) + .or_else(|| attr::find_by_name(attrs, sym::no_mangle)) { check_no_mangle_on_generic_fn( - no_mangle_attr, + attr, Some(generics), cx.tcx.hir_get_generics(it.id.owner_id.def_id).unwrap(), it.span, @@ -1416,7 +1422,7 @@ impl TypeAliasBounds { impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - let hir::ItemKind::TyAlias(hir_ty, generics) = item.kind else { return }; + let hir::ItemKind::TyAlias(_, hir_ty, generics) = item.kind else { return }; // There must not be a where clause. if generics.predicates.is_empty() { @@ -1989,7 +1995,7 @@ impl ExplicitOutlivesRequirements { inferred_outlives .filter_map(|(clause, _)| match clause.kind().skip_binder() { - ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a { + ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match a.kind() { ty::ReEarlyParam(ebr) if item_generics.region_param(ebr, tcx).def_id == lifetime.to_def_id() => { @@ -2039,7 +2045,7 @@ impl ExplicitOutlivesRequirements { let is_inferred = match tcx.named_bound_var(lifetime.hir_id) { Some(ResolvedArg::EarlyBound(def_id)) => inferred_outlives .iter() - .any(|r| matches!(**r, ty::ReEarlyParam(ebr) if { item_generics.region_param(ebr, tcx).def_id == def_id.to_def_id() })), + .any(|r| matches!(r.kind(), ty::ReEarlyParam(ebr) if { item_generics.region_param(ebr, tcx).def_id == def_id.to_def_id() })), _ => false, }; @@ -2119,9 +2125,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { use rustc_middle::middle::resolve_bound_vars::ResolvedArg; let def_id = item.owner_id.def_id; - if let hir::ItemKind::Struct(_, hir_generics) - | hir::ItemKind::Enum(_, hir_generics) - | hir::ItemKind::Union(_, hir_generics) = item.kind + if let hir::ItemKind::Struct(_, _, hir_generics) + | hir::ItemKind::Enum(_, _, hir_generics) + | hir::ItemKind::Union(_, _, hir_generics) = item.kind { let inferred_outlives = cx.tcx.inferred_outlives_of(def_id); if inferred_outlives.is_empty() { @@ -2257,7 +2263,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { // 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 { + if let hir::ItemKind::Struct(_, hir::VariantData::Tuple(..), _) = item.kind { where_span } else { hir_generics.span.shrink_to_hi().to(where_span) @@ -2582,34 +2588,35 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { ) -> Option { let ty = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty); - use rustc_type_ir::TyKind::*; match ty.kind() { // Primitive types that don't like 0 as a value. - Ref(..) => Some("references must be non-null".into()), - Adt(..) if ty.is_box() => Some("`Box` must be non-null".into()), - FnPtr(..) => Some("function pointers must be non-null".into()), - Never => Some("the `!` type has no valid value".into()), - RawPtr(ty, _) if matches!(ty.kind(), Dynamic(..)) => + ty::Ref(..) => Some("references must be non-null".into()), + ty::Adt(..) if ty.is_box() => Some("`Box` must be non-null".into()), + ty::FnPtr(..) => Some("function pointers must be non-null".into()), + ty::Never => Some("the `!` type has no valid value".into()), + ty::RawPtr(ty, _) if matches!(ty.kind(), ty::Dynamic(..)) => // raw ptr to dyn Trait { Some("the vtable of a wide raw pointer must be non-null".into()) } // Primitive types with other constraints. - Bool if init == InitKind::Uninit => { + ty::Bool if init == InitKind::Uninit => { Some("booleans must be either `true` or `false`".into()) } - Char if init == InitKind::Uninit => { + ty::Char if init == InitKind::Uninit => { Some("characters must be a valid Unicode codepoint".into()) } - Int(_) | Uint(_) if init == InitKind::Uninit => { + ty::Int(_) | ty::Uint(_) if init == InitKind::Uninit => { Some("integers must be initialized".into()) } - Float(_) if init == InitKind::Uninit => Some("floats must be initialized".into()), - RawPtr(_, _) if init == InitKind::Uninit => { + ty::Float(_) if init == InitKind::Uninit => { + Some("floats must be initialized".into()) + } + ty::RawPtr(_, _) if init == InitKind::Uninit => { Some("raw pointers must be initialized".into()) } // Recurse and checks for some compound types. (but not unions) - Adt(adt_def, args) if !adt_def.is_union() => { + ty::Adt(adt_def, args) if !adt_def.is_union() => { // Handle structs. if adt_def.is_struct() { return variant_find_init_error( @@ -2675,11 +2682,11 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { // We couldn't find anything wrong here. None } - Tuple(..) => { + ty::Tuple(..) => { // Proceed recursively, check all fields. ty.tuple_fields().iter().find_map(|field| ty_find_init_error(cx, field, init)) } - Array(ty, len) => { + ty::Array(ty, len) => { if matches!(len.try_to_target_usize(cx.tcx), Some(v) if v > 0) { // Array length known at array non-empty -- recurse. ty_find_init_error(cx, *ty, init) @@ -3115,6 +3122,7 @@ impl EarlyLintPass for SpecialModuleName { for item in &krate.items { if let ast::ItemKind::Mod( _, + ident, ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _, _), ) = item.kind { @@ -3122,7 +3130,7 @@ impl EarlyLintPass for SpecialModuleName { continue; } - match item.ident.name.as_str() { + match ident.name.as_str() { "lib" => cx.emit_span_lint( SPECIAL_MODULE_NAME, item.span, diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 74663e6b4bbe1..5679d4566dcd4 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -6,6 +6,7 @@ use std::cell::Cell; use std::slice; +use rustc_ast::BindingMode; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; @@ -14,14 +15,14 @@ use rustc_feature::Features; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_hir::{Pat, PatKind}; use rustc_middle::bug; +use rustc_middle::lint::LevelAndSource; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths}; use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode}; -use rustc_session::lint::{ - FutureIncompatibleInfo, Level, Lint, LintBuffer, LintExpectationId, LintId, -}; +use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintBuffer, LintExpectationId, LintId}; use rustc_session::{LintStoreMarker, Session}; use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::{Ident, Span, Symbol, sym}; @@ -82,11 +83,6 @@ enum TargetLint { Ignored, } -pub enum FindLintError { - NotFound, - Removed, -} - struct LintAlias { name: &'static str, /// Whether deprecation warnings should be suppressed for this alias. @@ -230,13 +226,24 @@ impl LintStore { } } - pub fn register_group_alias(&mut self, lint_name: &'static str, alias: &'static str) { - self.lint_groups.insert( + fn insert_group(&mut self, name: &'static str, group: LintGroup) { + let previous = self.lint_groups.insert(name, group); + if previous.is_some() { + bug!("group {name:?} already exists"); + } + } + + pub fn register_group_alias(&mut self, group_name: &'static str, alias: &'static str) { + let Some(LintGroup { lint_ids, .. }) = self.lint_groups.get(group_name) else { + bug!("group alias {alias:?} points to unregistered group {group_name:?}") + }; + + self.insert_group( alias, LintGroup { - lint_ids: vec![], + lint_ids: lint_ids.clone(), is_externally_loaded: false, - depr: Some(LintAlias { name: lint_name, silent: true }), + depr: Some(LintAlias { name: group_name, silent: true }), }, ); } @@ -248,24 +255,17 @@ impl LintStore { deprecated_name: Option<&'static str>, to: Vec, ) { - let new = self - .lint_groups - .insert(name, LintGroup { lint_ids: to, is_externally_loaded, depr: None }) - .is_none(); if let Some(deprecated) = deprecated_name { - self.lint_groups.insert( + self.insert_group( deprecated, LintGroup { - lint_ids: vec![], + lint_ids: to.clone(), is_externally_loaded, depr: Some(LintAlias { name, silent: false }), }, ); } - - if !new { - bug!("duplicate specification of lint group {}", name); - } + self.insert_group(name, LintGroup { lint_ids: to, is_externally_loaded, depr: None }); } /// This lint should give no warning and have no effect. @@ -291,23 +291,15 @@ impl LintStore { self.by_name.insert(name.into(), Removed(reason.into())); } - pub fn find_lints(&self, mut lint_name: &str) -> Result, FindLintError> { + pub fn find_lints(&self, lint_name: &str) -> Option<&[LintId]> { 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), - Some(&Ignored) => Ok(vec![]), - 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), - }; + Some(Id(lint_id)) => Some(slice::from_ref(lint_id)), + Some(Renamed(_, lint_id)) => Some(slice::from_ref(lint_id)), + Some(Removed(_)) => None, + Some(Ignored) => Some(&[]), + None => match self.lint_groups.get(lint_name) { + Some(LintGroup { lint_ids, .. }) => Some(lint_ids), + None => None, }, } } @@ -373,8 +365,12 @@ impl LintStore { CheckLintNameResult::MissingTool }; } - Some(LintGroup { lint_ids, .. }) => { - return CheckLintNameResult::Tool(lint_ids, None); + Some(LintGroup { lint_ids, depr, .. }) => { + return if let &Some(LintAlias { name, silent: false }) = depr { + CheckLintNameResult::Tool(lint_ids, Some(name.to_string())) + } else { + CheckLintNameResult::Tool(lint_ids, None) + }; } }, Some(Id(id)) => return CheckLintNameResult::Tool(slice::from_ref(id), None), @@ -392,15 +388,11 @@ impl LintStore { 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(lint_ids, Some((*name).to_string())) - }; + if let &Some(LintAlias { name, silent: false }) = depr { + CheckLintNameResult::Tool(lint_ids, Some(name.to_string())) + } else { + CheckLintNameResult::Ok(lint_ids) } - CheckLintNameResult::Ok(lint_ids) } }, Some(Id(id)) => CheckLintNameResult::Ok(slice::from_ref(id)), @@ -411,7 +403,7 @@ impl LintStore { fn no_lint_suggestion(&self, lint_name: &str, tool_name: &str) -> CheckLintNameResult<'_> { let name_lower = lint_name.to_lowercase(); - if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_ok() { + if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_some() { // First check if the lint name is (partly) in upper case instead of lower case... return CheckLintNameResult::NoLint(Some((Symbol::intern(&name_lower), false))); } @@ -454,18 +446,8 @@ impl LintStore { None => match self.lint_groups.get(&*complete_name) { // Now we are sure, that this lint exists nowhere None => self.no_lint_suggestion(lint_name, tool_name), - 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(); - if *silent { - CheckLintNameResult::Tool(lint_ids, Some(complete_name)) - } else { - CheckLintNameResult::Tool(lint_ids, Some((*name).to_string())) - } - } else { - CheckLintNameResult::Tool(lint_ids, Some(complete_name)) - } + Some(LintGroup { lint_ids, .. }) => { + CheckLintNameResult::Tool(lint_ids, Some(complete_name)) } }, Some(Id(id)) => CheckLintNameResult::Tool(slice::from_ref(id), Some(complete_name)), @@ -571,7 +553,7 @@ pub trait LintContext { } /// This returns the lint level for the given lint at the current location. - fn get_lint_level(&self, lint: &'static Lint) -> Level; + fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource; /// This function can be used to manually fulfill an expectation. This can /// be used for lints which contain several spans, and should be suppressed, @@ -640,8 +622,8 @@ impl<'tcx> LintContext for LateContext<'tcx> { } } - fn get_lint_level(&self, lint: &'static Lint) -> Level { - self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs).0 + fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource { + self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs) } } @@ -661,8 +643,8 @@ impl LintContext for EarlyContext<'_> { self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorate) } - fn get_lint_level(&self, lint: &'static Lint) -> Level { - self.builder.lint_level(lint).0 + fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource { + self.builder.lint_level(lint) } } @@ -683,6 +665,10 @@ impl<'tcx> LateContext<'tcx> { self.tcx.type_is_copy_modulo_regions(self.typing_env(), ty) } + pub fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool { + self.tcx.type_is_use_cloned_modulo_regions(self.typing_env(), ty) + } + /// 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>> { @@ -826,7 +812,10 @@ impl<'tcx> LateContext<'tcx> { return Ok(()); } - self.path.push(Symbol::intern(&disambiguated_data.data.to_string())); + self.path.push(match disambiguated_data.data.get_opt_name() { + Some(sym) => sym, + None => Symbol::intern(&disambiguated_data.data.to_string()), + }); Ok(()) } @@ -850,11 +839,11 @@ impl<'tcx> LateContext<'tcx> { &self, self_ty: Ty<'tcx>, trait_id: DefId, - name: &str, + name: Symbol, ) -> Option> { let tcx = self.tcx; tcx.associated_items(trait_id) - .find_by_name_and_kind(tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id) + .find_by_ident_and_kind(tcx, Ident::with_dummy_span(name), ty::AssocTag::Type, trait_id) .and_then(|assoc| { let proj = Ty::new_projection(tcx, assoc.def_id, [self_ty]); tcx.try_normalize_erasing_regions(self.typing_env(), proj).ok() @@ -886,7 +875,12 @@ impl<'tcx> LateContext<'tcx> { } && let Some(init) = match parent_node { hir::Node::Expr(expr) => Some(expr), - hir::Node::LetStmt(hir::LetStmt { init, .. }) => *init, + hir::Node::LetStmt(hir::LetStmt { + init, + // Binding is immutable, init cannot be re-assigned + pat: Pat { kind: PatKind::Binding(BindingMode::NONE, ..), .. }, + .. + }) => *init, _ => None, } { @@ -931,7 +925,12 @@ impl<'tcx> LateContext<'tcx> { } && let Some(init) = match parent_node { hir::Node::Expr(expr) => Some(expr), - hir::Node::LetStmt(hir::LetStmt { init, .. }) => *init, + hir::Node::LetStmt(hir::LetStmt { + init, + // Binding is immutable, init cannot be re-assigned + pat: Pat { kind: PatKind::Binding(BindingMode::NONE, ..), .. }, + .. + }) => *init, hir::Node::Item(item) => match item.kind { hir::ItemKind::Const(.., body_id) | hir::ItemKind::Static(.., body_id) => { Some(self.tcx.hir_body(body_id).value) diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index fd6b3e90adabc..91c7922638de5 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -159,7 +159,10 @@ fn is_temporary_rvalue(expr: &Expr<'_>) -> bool { ExprKind::Path(..) => false, // Calls return rvalues. - ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) => true, + ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Use(..) + | ExprKind::Binary(..) => true, // Inner blocks are rvalues. ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::Block(..) => true, diff --git a/compiler/rustc_lint/src/default_could_be_derived.rs b/compiler/rustc_lint/src/default_could_be_derived.rs index 59e38a882dde5..78d129642dc78 100644 --- a/compiler/rustc_lint/src/default_could_be_derived.rs +++ b/compiler/rustc_lint/src/default_could_be_derived.rs @@ -93,7 +93,11 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived { let orig_fields = match cx.tcx.hir_get_if_local(type_def_id) { Some(hir::Node::Item(hir::Item { kind: - hir::ItemKind::Struct(hir::VariantData::Struct { fields, recovered: _ }, _generics), + hir::ItemKind::Struct( + _, + hir::VariantData::Struct { fields, recovered: _ }, + _generics, + ), .. })) => fields.iter().map(|f| (f.ident.name, f)).collect::>(), _ => return, @@ -133,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived { return; } - // At least one of the fields with a default value have been overriden in + // At least one of the fields with a default value have been overridden in // the `Default` implementation. We suggest removing it and relying on `..` // instead. let any_default_field_given = diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index ec8f84415759f..5989ef9519cd7 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { && let ty::Dynamic(data, _, ty::Dyn) = self_ty.kind() && let Some(self_principal) = data.principal() // `::Target` is `dyn target_principal` - && let Some(target) = cx.get_associated_type(self_ty, did, "Target") + && let Some(target) = cx.get_associated_type(self_ty, did, sym::Target) && let ty::Dynamic(data, _, ty::Dyn) = target.kind() && let Some(target_principal) = data.principal() // `target_principal` is a supertrait of `t_principal` diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 723b894c43bc4..f9601fa5ef1d8 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -241,7 +241,7 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> ast_visit::AssocCtxt::Trait => { lint_callback!(cx, check_trait_item, item); } - ast_visit::AssocCtxt::Impl => { + ast_visit::AssocCtxt::Impl { .. } => { lint_callback!(cx, check_impl_item, item); } } @@ -250,7 +250,7 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> ast_visit::AssocCtxt::Trait => { lint_callback!(cx, check_trait_item_post, item); } - ast_visit::AssocCtxt::Impl => { + ast_visit::AssocCtxt::Impl { .. } => { lint_callback!(cx, check_impl_item_post, item); } } diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs index 7ead8eafbd5aa..179f2bcf07f59 100644 --- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs +++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs @@ -1,6 +1,5 @@ use rustc_hir as hir; -use rustc_middle::ty::Ty; -use rustc_middle::ty::visit::TypeVisitableExt; +use rustc_middle::ty::{Ty, TypeVisitableExt}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::{Span, sym}; diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index d109a5c90305e..586e55c8055c9 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -1,5 +1,5 @@ use rustc_errors::codes::*; -use rustc_errors::{Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic}; +use rustc_errors::{Diag, EmissionGuarantee, Subdiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::lint::Level; use rustc_span::{Span, Symbol}; @@ -26,11 +26,7 @@ pub(crate) enum OverruledAttributeSub { } impl Subdiagnostic for OverruledAttributeSub { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { OverruledAttributeSub::DefaultSource { id } => { diag.note(fluent::lint_default_source); diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 9ca148e1f2549..4c2b82a9a23a6 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -38,7 +38,7 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option) { } LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { // We are an `eval_always` query, so looking at the attribute's `AttrId` is ok. - let attr_id = tcx.hir().attrs(hir_id)[attr_index as usize].id(); + let attr_id = tcx.hir_attrs(hir_id)[attr_index as usize].id(); (attr_id, lint_index) } diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index 757fc1f58bd51..a56b753bda726 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -49,6 +49,8 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let Some((pat, arg)) = extract_for_loop(expr) else { return }; + let arg_span = arg.span.source_callsite(); + let ty = cx.typeck_results().expr_ty(arg); let (adt, args, ref_mutability) = match ty.kind() { @@ -78,27 +80,27 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span) { ForLoopsOverFalliblesLoopSub::RemoveNext { - suggestion: recv.span.between(arg.span.shrink_to_hi()), + suggestion: recv.span.between(arg_span.shrink_to_hi()), recv_snip, } } else { ForLoopsOverFalliblesLoopSub::UseWhileLet { start_span: expr.span.with_hi(pat.span.lo()), - end_span: pat.span.between(arg.span), + end_span: pat.span.between(arg_span), var, } }; let question_mark = suggest_question_mark(cx, adt, args, expr.span) - .then(|| ForLoopsOverFalliblesQuestionMark { suggestion: arg.span.shrink_to_hi() }); + .then(|| ForLoopsOverFalliblesQuestionMark { suggestion: arg_span.shrink_to_hi() }); let suggestion = ForLoopsOverFalliblesSuggestion { var, start_span: expr.span.with_hi(pat.span.lo()), - end_span: pat.span.between(arg.span), + end_span: pat.span.between(arg_span), }; cx.emit_span_lint( FOR_LOOPS_OVER_FALLIBLES, - arg.span, + arg_span, ForLoopsOverFalliblesDiag { article, ref_prefix, ty, sub, question_mark, suggestion }, ); } diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 6175415c31f98..d0668794198ac 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -104,7 +104,7 @@ impl ClashingExternDeclarations { /// for the item, return its HirId without updating the set. fn insert(&mut self, tcx: TyCtxt<'_>, fi: hir::ForeignItemId) -> Option { let did = fi.owner_id.to_def_id(); - let instance = Instance::new(did, ty::List::identity_for_item(tcx, did)); + let instance = Instance::new_raw(did, ty::List::identity_for_item(tcx, did)); let name = Symbol::intern(tcx.symbol_name(instance).name); if let Some(&existing_id) = self.seen_decls.get(&name) { // Avoid updating the map with the new entry when we do find a collision. We want to @@ -270,14 +270,12 @@ fn structurally_same_type_impl<'tcx>( true } else { // Do a full, depth-first comparison between the two. - use rustc_type_ir::TyKind::*; - let is_primitive_or_pointer = - |ty: Ty<'tcx>| ty.is_primitive() || matches!(ty.kind(), RawPtr(..) | Ref(..)); + |ty: Ty<'tcx>| ty.is_primitive() || matches!(ty.kind(), ty::RawPtr(..) | ty::Ref(..)); ensure_sufficient_stack(|| { match (a.kind(), b.kind()) { - (&Adt(a_def, a_gen_args), &Adt(b_def, b_gen_args)) => { + (&ty::Adt(a_def, a_gen_args), &ty::Adt(b_def, b_gen_args)) => { // Only `repr(C)` types can be compared structurally. if !(a_def.repr().c() && b_def.repr().c()) { return false; @@ -308,30 +306,30 @@ fn structurally_same_type_impl<'tcx>( }, ) } - (Array(a_ty, a_len), Array(b_ty, b_len)) => { + (ty::Array(a_ty, a_len), ty::Array(b_ty, b_len)) => { // For arrays, we also check the length. a_len == b_len && structurally_same_type_impl( seen_types, tcx, typing_env, *a_ty, *b_ty, ckind, ) } - (Slice(a_ty), Slice(b_ty)) => { + (ty::Slice(a_ty), ty::Slice(b_ty)) => { structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty, ckind) } - (RawPtr(a_ty, a_mutbl), RawPtr(b_ty, b_mutbl)) => { + (ty::RawPtr(a_ty, a_mutbl), ty::RawPtr(b_ty, b_mutbl)) => { a_mutbl == b_mutbl && structurally_same_type_impl( seen_types, tcx, typing_env, *a_ty, *b_ty, ckind, ) } - (Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => { + (ty::Ref(_a_region, a_ty, a_mut), ty::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, tcx, typing_env, *a_ty, *b_ty, ckind, ) } - (FnDef(..), FnDef(..)) => { + (ty::FnDef(..), ty::FnDef(..)) => { let a_poly_sig = a.fn_sig(tcx); let b_poly_sig = b.fn_sig(tcx); @@ -354,35 +352,38 @@ fn structurally_same_type_impl<'tcx>( ckind, ) } - (Tuple(..), Tuple(..)) => { + (ty::Tuple(..), ty::Tuple(..)) => { // Tuples are not `repr(C)` so these cannot be compared structurally. false } // 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(..)) - | (Coroutine(..), Coroutine(..)) - | (CoroutineWitness(..), CoroutineWitness(..)) - | (Alias(ty::Projection, ..), Alias(ty::Projection, ..)) - | (Alias(ty::Inherent, ..), Alias(ty::Inherent, ..)) - | (Alias(ty::Opaque, ..), Alias(ty::Opaque, ..)) => false, + (ty::Dynamic(..), ty::Dynamic(..)) + | (ty::Error(..), ty::Error(..)) + | (ty::Closure(..), ty::Closure(..)) + | (ty::Coroutine(..), ty::Coroutine(..)) + | (ty::CoroutineWitness(..), ty::CoroutineWitness(..)) + | (ty::Alias(ty::Projection, ..), ty::Alias(ty::Projection, ..)) + | (ty::Alias(ty::Inherent, ..), ty::Alias(ty::Inherent, ..)) + | (ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => false, // These definitely should have been caught above. - (Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(), + (ty::Bool, ty::Bool) + | (ty::Char, ty::Char) + | (ty::Never, ty::Never) + | (ty::Str, ty::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(..) | Pat(..), _) if is_primitive_or_pointer(b) => { + (ty::Adt(..) | ty::Pat(..), _) if is_primitive_or_pointer(b) => { if let Some(a_inner) = types::repr_nullable_ptr(tcx, typing_env, a, ckind) { a_inner == b } else { false } } - (_, Adt(..) | Pat(..)) if is_primitive_or_pointer(a) => { + (_, ty::Adt(..) | ty::Pat(..)) if is_primitive_or_pointer(a) => { if let Some(b_inner) = types::repr_nullable_ptr(tcx, typing_env, b, ckind) { b_inner == a } else { diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 39ea8d8e3246c..a9b04511c6b47 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -3,9 +3,7 @@ use std::ops::ControlFlow; use hir::intravisit::{self, Visitor}; use rustc_ast::Recovered; -use rustc_errors::{ - Applicability, Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic, SuggestionStyle, -}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic, SuggestionStyle}; use rustc_hir::{self as hir, HirIdSet}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::adjustment::Adjust; @@ -327,11 +325,7 @@ struct IfLetRescopeRewrite { } impl Subdiagnostic for IfLetRescopeRewrite { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { let mut suggestions = vec![]; for match_head in self.match_heads { match match_head { @@ -360,7 +354,7 @@ impl Subdiagnostic for IfLetRescopeRewrite { .chain(repeat('}').take(closing_brackets.count)) .collect(), )); - let msg = f(diag, crate::fluent_generated::lint_suggestion); + let msg = diag.eagerly_translate(crate::fluent_generated::lint_suggestion); diag.multipart_suggestion_with_style( msg, suggestions, diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 0b3af7d6aba98..a8f45d043be97 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -15,7 +15,8 @@ use rustc_middle::ty::relate::{ Relate, RelateResult, TypeRelation, structurally_relate_consts, structurally_relate_tys, }; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + TypeVisitor, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint::FutureIncompatibilityReason; @@ -41,7 +42,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,compile_fail,edition2021 /// # #![deny(impl_trait_overcaptures)] /// # use std::fmt::Display; /// let mut x = vec![]; @@ -209,7 +210,7 @@ where VarFn: FnOnce() -> FxHashMap, OutlivesFn: FnOnce() -> OutlivesEnvironment<'tcx>, { - fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { + fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { // When we get into a binder, we need to add its own bound vars to the scope. let mut added = vec![]; for arg in t.bound_vars() { @@ -392,7 +393,7 @@ where } _ => { self.tcx.dcx().span_delayed_bug( - self.tcx.hir().span(arg.hir_id()), + self.tcx.hir_span(arg.hir_id()), "no valid for captured arg", ); } @@ -461,7 +462,7 @@ fn extract_def_id_from_arg<'tcx>( arg: ty::GenericArg<'tcx>, ) -> DefId { match arg.unpack() { - ty::GenericArgKind::Lifetime(re) => match *re { + ty::GenericArgKind::Lifetime(re) => match re.kind() { ty::ReEarlyParam(ebr) => generics.region_param(ebr, tcx).def_id, ty::ReBound( _, @@ -506,9 +507,9 @@ impl<'tcx> TypeRelation> for FunctionalVariances<'tcx> { self.tcx } - fn relate_with_variance>>( + fn relate_with_variance>>( &mut self, - variance: rustc_type_ir::Variance, + variance: ty::Variance, _: ty::VarianceDiagInfo>, a: T, b: T, @@ -530,7 +531,7 @@ impl<'tcx> TypeRelation> for FunctionalVariances<'tcx> { a: ty::Region<'tcx>, _: ty::Region<'tcx>, ) -> RelateResult<'tcx, ty::Region<'tcx>> { - let def_id = match *a { + let def_id = match a.kind() { ty::ReEarlyParam(ebr) => self.generics.region_param(ebr, self.tcx).def_id, ty::ReBound( _, diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 1f999bbea5fe7..1d4be24ea9fa0 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -1,24 +1,21 @@ //! Some lints that are only useful in the compiler or crates that use compiler internals, such as //! Clippy. -use rustc_ast as ast; +use rustc_hir::HirId; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; -use rustc_hir::{ - AmbigArg, BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, - PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Ty, TyKind, -}; use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::{Span, sym}; use tracing::debug; +use {rustc_ast as ast, rustc_hir as hir}; use crate::lints::{ BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag, SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrInherentUsage, - UntranslatableDiag, + TypeIrTraitUsage, UntranslatableDiag, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; @@ -37,9 +34,12 @@ declare_tool_lint! { declare_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]); impl LateLintPass<'_> for DefaultHashTypes { - fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) { + fn check_path(&mut self, cx: &LateContext<'_>, path: &hir::Path<'_>, hir_id: HirId) { let Res::Def(rustc_hir::def::DefKind::Struct, def_id) = path.res else { return }; - if matches!(cx.tcx.hir_node(hir_id), Node::Item(Item { kind: ItemKind::Use(..), .. })) { + if matches!( + cx.tcx.hir_node(hir_id), + hir::Node::Item(hir::Item { kind: hir::ItemKind::Use(..), .. }) + ) { // Don't lint imports, only actual usages. return; } @@ -60,10 +60,10 @@ impl LateLintPass<'_> for DefaultHashTypes { /// get the `DefId` and `GenericArgsRef` of the function. fn typeck_results_of_method_fn<'tcx>( cx: &LateContext<'tcx>, - expr: &Expr<'_>, + expr: &hir::Expr<'_>, ) -> Option<(Span, DefId, ty::GenericArgsRef<'tcx>)> { match expr.kind { - ExprKind::MethodCall(segment, ..) + hir::ExprKind::MethodCall(segment, ..) if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => { Some((segment.ident.span, def_id, cx.typeck_results().node_args(expr.hir_id))) @@ -102,7 +102,7 @@ declare_tool_lint! { declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]); impl LateLintPass<'_> for QueryStability { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return }; if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args) { @@ -164,21 +164,25 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { } } - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx, AmbigArg>) { + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { match &ty.kind { - TyKind::Path(QPath::Resolved(_, path)) => { + hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { if lint_ty_kind_usage(cx, &path.res) { let span = match cx.tcx.parent_hir_node(ty.hir_id) { - Node::PatExpr(PatExpr { kind: PatExprKind::Path(qpath), .. }) - | Node::Pat(Pat { - kind: PatKind::TupleStruct(qpath, ..) | PatKind::Struct(qpath, ..), + hir::Node::PatExpr(hir::PatExpr { + kind: hir::PatExprKind::Path(qpath), + .. + }) + | hir::Node::Pat(hir::Pat { + kind: + hir::PatKind::TupleStruct(qpath, ..) | hir::PatKind::Struct(qpath, ..), .. }) - | Node::Expr( - Expr { kind: ExprKind::Path(qpath), .. } - | &Expr { kind: ExprKind::Struct(qpath, ..), .. }, + | hir::Node::Expr( + hir::Expr { kind: hir::ExprKind::Path(qpath), .. } + | &hir::Expr { kind: hir::ExprKind::Struct(qpath, ..), .. }, ) => { - if let QPath::TypeRelative(qpath_ty, ..) = qpath + if let hir::QPath::TypeRelative(qpath_ty, ..) = qpath && qpath_ty.hir_id == ty.hir_id { Some(path.span) @@ -223,7 +227,7 @@ fn lint_ty_kind_usage(cx: &LateContext<'_>, res: &Res) -> bool { } } -fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &Path<'_>) -> Option { +fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &hir::Path<'_>) -> Option { match &path.res { Res::Def(_, def_id) => { if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(*def_id) { @@ -244,13 +248,17 @@ fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &Path<'_>) -> Option { None } -fn gen_args(segment: &PathSegment<'_>) -> String { +fn gen_args(segment: &hir::PathSegment<'_>) -> String { if let Some(args) = &segment.args { let lifetimes = args .args .iter() .filter_map(|arg| { - if let GenericArg::Lifetime(lt) = arg { Some(lt.ident.to_string()) } else { None } + if let hir::GenericArg::Lifetime(lt) = arg { + Some(lt.ident.to_string()) + } else { + None + } }) .collect::>(); @@ -272,7 +280,7 @@ declare_tool_lint! { } declare_tool_lint! { - /// The `usage_of_type_ir_inherent` lint detects usage `rustc_type_ir::inherent`. + /// The `usage_of_type_ir_inherent` lint detects usage of `rustc_type_ir::inherent`. /// /// This module should only be used within the trait solver. pub rustc::USAGE_OF_TYPE_IR_INHERENT, @@ -281,10 +289,43 @@ declare_tool_lint! { report_in_external_macro: true } -declare_lint_pass!(TypeIr => [NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_INHERENT]); +declare_tool_lint! { + /// The `usage_of_type_ir_traits` lint detects usage of `rustc_type_ir::Interner`, + /// or `rustc_infer::InferCtxtLike`. + /// + /// Methods of this trait should only be used within the type system abstraction layer, + /// and in the generic next trait solver implementation. Look for an analogously named + /// method on `TyCtxt` or `InferCtxt` (respectively). + pub rustc::USAGE_OF_TYPE_IR_TRAITS, + Allow, + "usage `rustc_type_ir`-specific abstraction traits outside of trait system", + report_in_external_macro: true +} + +declare_lint_pass!(TypeIr => [NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_TRAITS]); impl<'tcx> LateLintPass<'tcx> for TypeIr { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let res_def_id = match expr.kind { + hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => path.res.opt_def_id(), + hir::ExprKind::Path(hir::QPath::TypeRelative(..)) | hir::ExprKind::MethodCall(..) => { + cx.typeck_results().type_dependent_def_id(expr.hir_id) + } + _ => return, + }; + let Some(res_def_id) = res_def_id else { + return; + }; + if let Some(assoc_item) = cx.tcx.opt_associated_item(res_def_id) + && let Some(trait_def_id) = assoc_item.trait_container(cx.tcx) + && (cx.tcx.is_diagnostic_item(sym::type_ir_interner, trait_def_id) + | cx.tcx.is_diagnostic_item(sym::type_ir_infer_ctxt_like, trait_def_id)) + { + cx.emit_span_lint(USAGE_OF_TYPE_IR_TRAITS, expr.span, TypeIrTraitUsage); + } + } + + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { let rustc_hir::ItemKind::Use(path, kind) = item.kind else { return }; let is_mod_inherent = |def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id); @@ -308,18 +349,18 @@ impl<'tcx> LateLintPass<'tcx> for TypeIr { [.., penultimate, segment] if penultimate.res.opt_def_id().is_some_and(is_mod_inherent) => { - (segment.ident.span, item.ident.span, "*") + (segment.ident.span, item.kind.ident().unwrap().span, "*") } [.., segment] if path.res.iter().flat_map(Res::opt_def_id).any(is_mod_inherent) - && let rustc_hir::UseKind::Single = kind => + && let rustc_hir::UseKind::Single(ident) = kind => { let (lo, snippet) = match cx.tcx.sess.source_map().span_to_snippet(path.span).as_deref() { Ok("self") => (path.span, "*"), _ => (segment.ident.span.shrink_to_hi(), "::*"), }; - (lo, if segment.ident == item.ident { lo } else { item.ident.span }, snippet) + (lo, if segment.ident == ident { lo } else { ident.span }, snippet) } _ => return, }; @@ -394,15 +435,15 @@ declare_tool_lint! { declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]); impl LateLintPass<'_> for Diagnostics { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let collect_args_tys_and_spans = |args: &[Expr<'_>], reserve_one_extra: bool| { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { + let collect_args_tys_and_spans = |args: &[hir::Expr<'_>], reserve_one_extra: bool| { let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra)); result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span))); result }; // Only check function calls and method calls. let (span, def_id, fn_gen_args, arg_tys_and_spans) = match expr.kind { - ExprKind::Call(callee, args) => { + hir::ExprKind::Call(callee, args) => { match cx.typeck_results().node_type(callee.hir_id).kind() { &ty::FnDef(def_id, fn_gen_args) => { (callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false)) @@ -410,7 +451,7 @@ impl LateLintPass<'_> for Diagnostics { _ => return, // occurs for fns passed as args } } - ExprKind::MethodCall(_segment, _recv, args, _span) => { + hir::ExprKind::MethodCall(_segment, _recv, args, _span) => { let Some((span, def_id, fn_gen_args)) = typeck_results_of_method_fn(cx, expr) else { return; @@ -514,8 +555,8 @@ impl Diagnostics { let mut is_inside_appropriate_impl = false; for (_hir_id, parent) in cx.tcx.hir_parent_iter(current_id) { debug!(?parent); - if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent - && let Impl { of_trait: Some(of_trait), .. } = impl_ + if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) = parent + && let hir::Impl { of_trait: Some(of_trait), .. } = impl_ && let Some(def_id) = of_trait.trait_def_id() && let Some(name) = cx.tcx.get_diagnostic_name(def_id) && matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic) @@ -543,8 +584,8 @@ declare_tool_lint! { declare_lint_pass!(BadOptAccess => [BAD_OPT_ACCESS]); impl LateLintPass<'_> for BadOptAccess { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let ExprKind::Field(base, target) = expr.kind else { return }; + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { + let hir::ExprKind::Field(base, target) = expr.kind else { return }; let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return }; // Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be // avoided. @@ -581,9 +622,12 @@ declare_tool_lint! { declare_lint_pass!(SpanUseEqCtxt => [SPAN_USE_EQ_CTXT]); impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { - if let ExprKind::Binary(BinOp { node: BinOpKind::Eq | BinOpKind::Ne, .. }, lhs, rhs) = - expr.kind + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { + if let hir::ExprKind::Binary( + hir::BinOp { node: hir::BinOpKind::Eq | hir::BinOpKind::Ne, .. }, + lhs, + rhs, + ) = expr.kind { if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) { cx.emit_span_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag); @@ -592,9 +636,9 @@ impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt { } } -fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { +fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { match &expr.kind { - ExprKind::MethodCall(..) => cx + hir::ExprKind::MethodCall(..) => cx .typeck_results() .type_dependent_def_id(expr.hir_id) .is_some_and(|call_did| cx.tcx.is_diagnostic_item(sym::SpanCtxt, call_did)), @@ -617,11 +661,11 @@ declare_lint_pass!(SymbolInternStringLiteral => [SYMBOL_INTERN_STRING_LITERAL]); impl<'tcx> LateLintPass<'tcx> for SymbolInternStringLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - if let ExprKind::Call(path, [arg]) = expr.kind - && let ExprKind::Path(ref qpath) = path.kind + if let hir::ExprKind::Call(path, [arg]) = expr.kind + && let hir::ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::SymbolIntern, def_id) - && let ExprKind::Lit(kind) = arg.kind + && let hir::ExprKind::Lit(kind) = arg.kind && let rustc_ast::LitKind::Str(_, _) = kind.node { cx.emit_span_lint( diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index d22515d62d60e..852bb01c09655 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -48,7 +48,7 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { where F: FnOnce(&mut Self), { - let attrs = self.context.tcx.hir().attrs(id); + let attrs = self.context.tcx.hir_attrs(id); let prev = self.context.last_node_with_lint_attrs; self.context.last_node_with_lint_attrs = id; debug!("late context: enter_attrs({:?})", attrs); @@ -74,7 +74,7 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { fn process_mod(&mut self, m: &'tcx hir::Mod<'tcx>, n: HirId) { lint_callback!(self, check_mod, m, n); - hir_visit::walk_mod(self, m, n); + hir_visit::walk_mod(self, m); } } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 4ede9b4408798..00775647ac61a 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,10 +1,11 @@ use rustc_ast::attr::AttributeExt; use rustc_ast_pretty::pprust; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::unord::UnordSet; use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; use rustc_feature::{Features, GateIssue}; +use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{CRATE_HIR_ID, HirId}; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::hir::nested_filter; @@ -84,10 +85,10 @@ impl LintLevelSets { ) -> LevelAndSource { let lint = LintId::of(lint); let (level, mut src) = self.raw_lint_id_level(lint, idx, aux); - let level = reveal_actual_level(level, &mut src, sess, lint, |id| { + let (level, lint_id) = reveal_actual_level(level, &mut src, sess, lint, |id| { self.raw_lint_id_level(id, idx, aux) }); - (level, src) + LevelAndSource { level, lint_id, src } } fn raw_lint_id_level( @@ -95,17 +96,17 @@ impl LintLevelSets { id: LintId, mut idx: LintStackIndex, aux: Option<&FxIndexMap>, - ) -> (Option, LintLevelSource) { + ) -> (Option<(Level, Option)>, LintLevelSource) { if let Some(specs) = aux - && let Some(&(level, src)) = specs.get(&id) + && let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id) { - return (Some(level), src); + return (Some((level, lint_id)), src); } loop { let LintSet { ref specs, parent } = self.list[idx]; - if let Some(&(level, src)) = specs.get(&id) { - return (Some(level), src); + if let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id) { + return (Some((level, lint_id)), src); } if idx == COMMAND_LINE { return (None, LintLevelSource::Default); @@ -115,12 +116,11 @@ impl LintLevelSets { } } -fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { +fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet { let store = unerased_lint_store(&tcx.sess); + let root_map = tcx.shallow_lint_levels_on(hir::CRATE_OWNER_ID); - let map = tcx.shallow_lint_levels_on(rustc_hir::CRATE_OWNER_ID); - - let dont_need_to_run: FxIndexSet = store + let mut dont_need_to_run: FxHashSet = store .get_lints() .into_iter() .filter(|lint| { @@ -129,30 +129,37 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { lint.future_incompatible.is_some_and(|fut| fut.reason.has_future_breakage()); !has_future_breakage && !lint.eval_always }) - .filter_map(|lint| { - let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID); - if matches!(lint_level, (Level::Allow, ..)) - || (matches!(lint_level, (.., LintLevelSource::Default))) - && lint.default_level(tcx.sess.edition()) == Level::Allow - { - Some(LintId::of(lint)) - } else { - None - } + .filter(|lint| { + let lint_level = + root_map.lint_level_id_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID); + // Only include lints that are allowed at crate root or by default. + matches!(lint_level.level, Level::Allow) + || (matches!(lint_level.src, LintLevelSource::Default) + && lint.default_level(tcx.sess.edition()) == Level::Allow) }) + .map(|lint| LintId::of(*lint)) .collect(); - let mut visitor = LintLevelMaximum { tcx, dont_need_to_run }; - visitor.process_opts(); - tcx.hir_walk_attributes(&mut visitor); + for owner in tcx.hir_crate_items(()).owners() { + let map = tcx.shallow_lint_levels_on(owner); - visitor.dont_need_to_run + // All lints that appear with a non-allow level must be run. + for (_, specs) in map.specs.iter() { + for (lint, level_and_source) in specs.iter() { + if !matches!(level_and_source.level, Level::Allow) { + dont_need_to_run.remove(lint); + } + } + } + } + + dont_need_to_run.into() } #[instrument(level = "trace", skip(tcx), ret)] fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap { let store = unerased_lint_store(tcx.sess); - let attrs = tcx.hir_attrs(owner); + let attrs = tcx.hir_attr_map(owner); let mut levels = LintLevelsBuilder { sess: tcx.sess, @@ -299,6 +306,11 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { intravisit::walk_expr(self, e); } + fn visit_pat_field(&mut self, f: &'tcx hir::PatField<'tcx>) -> Self::Result { + self.add_id(f.hir_id); + intravisit::walk_pat_field(self, f); + } + fn visit_expr_field(&mut self, f: &'tcx hir::ExprField<'tcx>) { self.add_id(f.hir_id); intravisit::walk_expr_field(self, f); @@ -335,82 +347,6 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { } } -/// Visitor with the only function of visiting every item-like in a crate and -/// computing the highest level that every lint gets put to. -/// -/// E.g., if a crate has a global #![allow(lint)] attribute, but a single item -/// uses #[warn(lint)], this visitor will set that lint level as `Warn` -struct LintLevelMaximum<'tcx> { - tcx: TyCtxt<'tcx>, - /// The actual list of detected lints. - dont_need_to_run: FxIndexSet, -} - -impl<'tcx> LintLevelMaximum<'tcx> { - fn process_opts(&mut self) { - let store = unerased_lint_store(self.tcx.sess); - for (lint_group, level) in &self.tcx.sess.opts.lint_opts { - if *level != Level::Allow { - let Ok(lints) = store.find_lints(lint_group) else { - return; - }; - for lint in lints { - self.dont_need_to_run.swap_remove(&lint); - } - } - } - } -} - -impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> { - type NestedFilter = nested_filter::All; - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.tcx - } - - /// FIXME(blyxyas): In a future revision, we should also graph #![allow]s, - /// but that is handled with more care - fn visit_attribute(&mut self, attribute: &'tcx hir::Attribute) { - if matches!( - Level::from_attr(attribute), - Some( - Level::Warn - | Level::Deny - | Level::Forbid - | Level::Expect(..) - | Level::ForceWarn(..), - ) - ) { - let store = unerased_lint_store(self.tcx.sess); - // Lint attributes are always a metalist inside a - // metalist (even with just one lint). - let Some(meta_item_list) = attribute.meta_item_list() else { return }; - - for meta_list in meta_item_list { - // Convert Path to String - let Some(meta_item) = meta_list.meta_item() else { return }; - let ident: &str = &meta_item - .path - .segments - .iter() - .map(|segment| segment.ident.as_str()) - .collect::>() - .join("::"); - let Ok(lints) = store.find_lints( - // Lint attributes can only have literals - ident, - ) else { - return; - }; - for lint in lints { - self.dont_need_to_run.swap_remove(&lint); - } - } - } - } -} - pub struct LintLevelsBuilder<'s, P> { sess: &'s Session, features: &'s Features, @@ -445,6 +381,19 @@ impl<'s> LintLevelsBuilder<'s, TopDown> { builder } + pub fn crate_root( + sess: &'s Session, + features: &'s Features, + lint_added_lints: bool, + store: &'s LintStore, + registered_tools: &'s RegisteredTools, + crate_attrs: &[ast::Attribute], + ) -> Self { + let mut builder = Self::new(sess, features, lint_added_lints, store, registered_tools); + builder.add(crate_attrs, true, None); + builder + } + fn process_command_line(&mut self) { self.provider.cur = self .provider @@ -523,9 +472,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { for &(ref lint_name, level) in &self.sess.opts.lint_opts { // Checks the validity of lint names derived from the command line. let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name); - if lint_name_only == crate::WARNINGS.name_lower() - && matches!(level, Level::ForceWarn(_)) - { + if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn) { self.sess .dcx() .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() }); @@ -568,24 +515,23 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { _ => {} }; - let orig_level = level; let lint_flag_val = Symbol::intern(lint_name); - let Ok(ids) = self.store.find_lints(lint_name) else { + let Some(ids) = self.store.find_lints(lint_name) else { // errors already handled above continue; }; - for id in ids { + for &id in ids { // ForceWarn and Forbid cannot be overridden - if let Some((Level::ForceWarn(_) | Level::Forbid, _)) = + if let Some(LevelAndSource { level: Level::ForceWarn | Level::Forbid, .. }) = self.current_specs().get(&id) { continue; } if self.check_gated_lint(id, DUMMY_SP, true) { - let src = LintLevelSource::CommandLine(lint_flag_val, orig_level); - self.insert(id, (level, src)); + let src = LintLevelSource::CommandLine(lint_flag_val, level); + self.insert(id, LevelAndSource { level, lint_id: None, src }); } } } @@ -594,8 +540,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful /// (e.g. if a forbid was already inserted on the same scope), then emits a /// diagnostic with no change to `specs`. - fn insert_spec(&mut self, id: LintId, (level, src): LevelAndSource) { - let (old_level, old_src) = self.provider.get_lint_level(id.lint, self.sess); + fn insert_spec(&mut self, id: LintId, LevelAndSource { level, lint_id, src }: LevelAndSource) { + let LevelAndSource { level: old_level, src: old_src, .. } = + self.provider.get_lint_level(id.lint, self.sess); // Setting to a non-forbid level is an error if the lint previously had // a forbid level. Note that this is not necessarily true even with a @@ -667,7 +614,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // The lint `unfulfilled_lint_expectations` can't be expected, as it would suppress itself. // Handling expectations of this lint would add additional complexity with little to no // benefit. The expect level for this lint will therefore be ignored. - if let Level::Expect(_) = level + if let Level::Expect = level && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS) { return; @@ -675,13 +622,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { match (old_level, level) { // If the new level is an expectation store it in `ForceWarn` - (Level::ForceWarn(_), Level::Expect(expectation_id)) => { - self.insert(id, (Level::ForceWarn(Some(expectation_id)), old_src)) + (Level::ForceWarn, Level::Expect) => { + self.insert(id, LevelAndSource { level: Level::ForceWarn, lint_id, src: old_src }) } // Keep `ForceWarn` level but drop the expectation - (Level::ForceWarn(_), _) => self.insert(id, (Level::ForceWarn(None), old_src)), + (Level::ForceWarn, _) => self.insert( + id, + LevelAndSource { level: Level::ForceWarn, lint_id: None, src: old_src }, + ), // Set the lint level as normal - _ => self.insert(id, (level, src)), + _ => self.insert(id, LevelAndSource { level, lint_id, src }), }; } @@ -696,7 +646,11 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { if attr.has_name(sym::automatically_derived) { self.insert( LintId::of(SINGLE_USE_LIFETIMES), - (Level::Allow, LintLevelSource::Default), + LevelAndSource { + level: Level::Allow, + lint_id: None, + src: LintLevelSource::Default, + }, ); continue; } @@ -707,15 +661,22 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { .meta_item_list() .is_some_and(|l| ast::attr::list_contains_name(&l, sym::hidden)) { - self.insert(LintId::of(MISSING_DOCS), (Level::Allow, LintLevelSource::Default)); + self.insert( + LintId::of(MISSING_DOCS), + LevelAndSource { + level: Level::Allow, + lint_id: None, + src: LintLevelSource::Default, + }, + ); continue; } - let level = match Level::from_attr(attr) { + let (level, lint_id) = match Level::from_attr(attr) { None => continue, // This is the only lint level with a `LintExpectationId` that can be created from // an attribute. - Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => { + Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => { let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id else { bug!("stable id Level::from_attr") @@ -727,9 +688,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { lint_index: None, }; - Level::Expect(stable_id) + (Level::Expect, Some(stable_id)) } - Some(lvl) => lvl, + Some((lvl, id)) => (lvl, id), }; let Some(mut metas) = attr.meta_item_list() else { continue }; @@ -777,13 +738,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } for (lint_index, li) in metas.iter_mut().enumerate() { - let level = match level { - Level::Expect(mut id) => { - id.set_lint_index(Some(lint_index as u16)); - Level::Expect(id) - } - level => level, - }; + let mut lint_id = lint_id; + if let Some(id) = &mut lint_id { + id.set_lint_index(Some(lint_index as u16)); + } let sp = li.span(); let meta_item = match li { @@ -915,7 +873,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let src = LintLevelSource::Node { name, span: sp, reason }; for &id in ids { if self.check_gated_lint(id, sp, false) { - self.insert_spec(id, (level, src)); + self.insert_spec(id, LevelAndSource { level, lint_id, src }); } } @@ -924,7 +882,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // overriding the lint level but instead add an expectation that can't be // fulfilled. The lint message will include an explanation, that the // `unfulfilled_lint_expectations` lint can't be expected. - if let Level::Expect(expect_id) = level { + if let (Level::Expect, Some(expect_id)) = (level, lint_id) { // The `unfulfilled_lint_expectations` lint is not part of any lint // groups. Therefore. we only need to check the slice if it contains a // single lint. @@ -946,7 +904,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } if self.lint_added_lints && !is_crate_node { - for (id, &(level, ref src)) in self.current_specs().iter() { + for (id, &LevelAndSource { level, ref src, .. }) in self.current_specs().iter() { if !id.lint.crate_level_only { continue; } @@ -984,10 +942,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { if self.lint_added_lints { let lint = builtin::UNKNOWN_LINTS; - let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS); + let level = self.lint_level(builtin::UNKNOWN_LINTS); // FIXME: make this translatable #[allow(rustc::diagnostic_outside_of_impl)] - lint_level(self.sess, lint, level, src, Some(span.into()), |lint| { + lint_level(self.sess, lint, level, Some(span.into()), |lint| { lint.primary_message(fluent::lint_unknown_gated_lint); lint.arg("name", lint_id.lint.name_lower()); lint.note(fluent::lint_note); @@ -1022,8 +980,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { span: Option, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), ) { - let (level, src) = self.lint_level(lint); - lint_level(self.sess, lint, level, src, span, decorate) + let level = self.lint_level(lint); + lint_level(self.sess, lint, level, span, decorate) } #[track_caller] @@ -1033,16 +991,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { span: MultiSpan, decorate: impl for<'a> LintDiagnostic<'a, ()>, ) { - let (level, src) = self.lint_level(lint); - lint_level(self.sess, lint, level, src, Some(span), |lint| { + let level = self.lint_level(lint); + lint_level(self.sess, lint, level, Some(span), |lint| { decorate.decorate_lint(lint); }); } #[track_caller] pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> LintDiagnostic<'a, ()>) { - let (level, src) = self.lint_level(lint); - lint_level(self.sess, lint, level, src, None, |lint| { + let level = self.lint_level(lint); + lint_level(self.sess, lint, level, None, |lint| { decorate.decorate_lint(lint); }); } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 5c5b3b350dd43..4ff586a79a6ec 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -28,15 +28,14 @@ #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_order_by)] -#![feature(let_chains)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] #![feature(try_blocks)] -#![warn(unreachable_pub)] // tidy-alphabetical-end mod async_closures; mod async_fn_in_trait; +mod autorefs; pub mod builtin; mod context; mod dangling; @@ -80,9 +79,11 @@ mod types; mod unit_bindings; mod unqualified_local_imports; mod unused; +mod utils; use async_closures::AsyncClosureUsage; use async_fn_in_trait::AsyncFnInTrait; +use autorefs::*; use builtin::*; use dangling::*; use default_could_be_derived::DefaultCouldBeDerived; @@ -124,11 +125,10 @@ use unused::*; #[rustfmt::skip] pub use builtin::{MissingDoc, SoftLints}; -pub use context::{ - CheckLintNameResult, EarlyContext, FindLintError, LateContext, LintContext, LintStore, -}; +pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore}; pub use early::{EarlyCheckNode, check_ast_node}; pub use late::{check_crate, late_lint_mod, unerased_lint_store}; +pub use levels::LintLevelsBuilder; pub use passes::{EarlyLintPass, LateLintPass}; pub use rustc_session::lint::Level::{self, *}; pub use rustc_session::lint::{ @@ -201,6 +201,7 @@ late_lint_methods!( PathStatements: PathStatements, LetUnderscore: LetUnderscore, InvalidReferenceCasting: InvalidReferenceCasting, + ImplicitAutorefs: ImplicitAutorefs, // Depends on referenced function signatures in expressions UnusedResults: UnusedResults, UnitBindings: UnitBindings, @@ -599,6 +600,21 @@ fn register_builtins(store: &mut LintStore) { "converted into hard error, \ see for more information", ); + store.register_removed( + "ptr_cast_add_auto_to_object", + "converted into hard error, see issue #127323 \ + for more information", + ); + store.register_removed( + "undefined_naked_function_abi", + "converted into hard error, see PR #139001 \ + for more information", + ); + store.register_removed( + "abi_unsupported_vector_types", + "converted into hard error, \ + see for more information", + ); } fn register_internals(store: &mut LintStore) { @@ -640,6 +656,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(USAGE_OF_QUALIFIED_TY), LintId::of(NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT), LintId::of(USAGE_OF_TYPE_IR_INHERENT), + LintId::of(USAGE_OF_TYPE_IR_TRAITS), LintId::of(BAD_OPT_ACCESS), LintId::of(SPAN_USE_EQ_CTXT), ], diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 0058630957291..7268a7f704fcb 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -6,7 +6,7 @@ use rustc_abi::ExternAbi; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag, - EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp, Subdiagnostic, SuggestionStyle, + EmissionGuarantee, LintDiagnostic, MultiSpan, Subdiagnostic, SuggestionStyle, }; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; @@ -55,6 +55,53 @@ pub(crate) enum ShadowedIntoIterDiagSub { }, } +// autorefs.rs +#[derive(LintDiagnostic)] +#[diag(lint_implicit_unsafe_autorefs)] +#[note] +pub(crate) struct ImplicitUnsafeAutorefsDiag<'a> { + #[label(lint_raw_ptr)] + pub raw_ptr_span: Span, + pub raw_ptr_ty: Ty<'a>, + #[subdiagnostic] + pub origin: ImplicitUnsafeAutorefsOrigin<'a>, + #[subdiagnostic] + pub method: Option, + #[subdiagnostic] + pub suggestion: ImplicitUnsafeAutorefsSuggestion, +} + +#[derive(Subdiagnostic)] +pub(crate) enum ImplicitUnsafeAutorefsOrigin<'a> { + #[note(lint_autoref)] + Autoref { + #[primary_span] + autoref_span: Span, + autoref_ty: Ty<'a>, + }, + #[note(lint_overloaded_deref)] + OverloadedDeref, +} + +#[derive(Subdiagnostic)] +#[note(lint_method_def)] +pub(crate) struct ImplicitUnsafeAutorefsMethodNote { + #[primary_span] + pub def_span: Span, + pub method_name: Symbol, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")] +pub(crate) struct ImplicitUnsafeAutorefsSuggestion { + pub mutbl: &'static str, + pub deref: &'static str, + #[suggestion_part(code = "({mutbl}{deref}")] + pub start_span: Span, + #[suggestion_part(code = ")")] + pub end_span: Span, +} + // builtin.rs #[derive(LintDiagnostic)] #[diag(lint_builtin_while_true)] @@ -449,11 +496,7 @@ pub(crate) struct BuiltinUnpermittedTypeInitSub { } impl Subdiagnostic for BuiltinUnpermittedTypeInitSub { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { let mut err = self.err; loop { if let Some(span) = err.span { @@ -504,16 +547,12 @@ pub(crate) struct BuiltinClashingExternSub<'a> { } impl Subdiagnostic for BuiltinClashingExternSub<'_> { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { let mut expected_str = DiagStyledString::new(); expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false); let mut found_str = DiagStyledString::new(); found_str.push(self.found.fn_sig(self.tcx).to_string(), true); - diag.note_expected_found(&"", expected_str, &"", found_str); + diag.note_expected_found("", expected_str, "", found_str); } } @@ -591,24 +630,40 @@ pub(crate) struct ExpectationNote { // ptr_nulls.rs #[derive(LintDiagnostic)] -pub(crate) enum PtrNullChecksDiag<'a> { - #[diag(lint_ptr_null_checks_fn_ptr)] - #[help(lint_help)] +pub(crate) enum UselessPtrNullChecksDiag<'a> { + #[diag(lint_useless_ptr_null_checks_fn_ptr)] + #[help] FnPtr { orig_ty: Ty<'a>, #[label] label: Span, }, - #[diag(lint_ptr_null_checks_ref)] + #[diag(lint_useless_ptr_null_checks_ref)] Ref { orig_ty: Ty<'a>, #[label] label: Span, }, - #[diag(lint_ptr_null_checks_fn_ret)] + #[diag(lint_useless_ptr_null_checks_fn_ret)] FnRet { fn_name: Ident }, } +#[derive(LintDiagnostic)] +pub(crate) enum InvalidNullArgumentsDiag { + #[diag(lint_invalid_null_arguments)] + #[help(lint_doc)] + NullPtrInline { + #[label(lint_origin)] + null_span: Span, + }, + #[diag(lint_invalid_null_arguments)] + #[help(lint_doc)] + NullPtrThroughBinding { + #[note(lint_origin)] + null_span: Span, + }, +} + // for_loops_over_fallibles.rs #[derive(LintDiagnostic)] #[diag(lint_for_loops_over_fallibles)] @@ -808,11 +863,7 @@ pub(crate) struct HiddenUnicodeCodepointsDiagLabels { } impl Subdiagnostic for HiddenUnicodeCodepointsDiagLabels { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { for (c, span) in self.spans { diag.span_label(span, format!("{c:?}")); } @@ -826,11 +877,7 @@ pub(crate) enum HiddenUnicodeCodepointsDiagSub { // Used because of multiple multipart_suggestion and note impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { HiddenUnicodeCodepointsDiagSub::Escape { spans } => { diag.multipart_suggestion_with_style( @@ -943,6 +990,11 @@ pub(crate) struct TyQualified { #[note] pub(crate) struct TypeIrInherentUsage; +#[derive(LintDiagnostic)] +#[diag(lint_type_ir_trait_usage)] +#[note] +pub(crate) struct TypeIrTraitUsage; + #[derive(LintDiagnostic)] #[diag(lint_non_glob_import_type_ir_inherent)] pub(crate) struct NonGlobImportTypeIrInherent { @@ -994,11 +1046,7 @@ pub(crate) struct NonBindingLetSub { } impl Subdiagnostic for NonBindingLetSub { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { let can_suggest_binding = self.drop_fn_start_end.is_some() || !self.is_assign_desugar; if can_suggest_binding { @@ -1282,11 +1330,7 @@ pub(crate) enum NonSnakeCaseDiagSub { } impl Subdiagnostic for NonSnakeCaseDiagSub { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { NonSnakeCaseDiagSub::Label { span } => { diag.span_label(span, fluent::lint_label); @@ -1608,11 +1652,7 @@ pub(crate) enum OverflowingBinHexSign { } impl Subdiagnostic for OverflowingBinHexSign { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { OverflowingBinHexSign::Positive => { diag.note(fluent::lint_positive_note); diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index 35db8632625ec..dea7c8ac70877 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { let def_id = item.owner_id.to_def_id(); // NOTE(nbdd0121): use `dyn_compatibility_violations` instead of `is_dyn_compatible` because // the latter will report `where_clause_object_safety` lint. - if let hir::ItemKind::Trait(_, _, _, _, _) = item.kind + if let hir::ItemKind::Trait(_, _, ident, ..) = item.kind && cx.tcx.is_dyn_compatible(def_id) { let direct_super_traits_iter = cx @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { cx.emit_span_lint( MULTIPLE_SUPERTRAIT_UPCASTABLE, cx.tcx.def_span(def_id), - crate::lints::MultipleSupertraitUpcastable { ident: item.ident }, + crate::lints::MultipleSupertraitUpcastable { ident }, ); } } diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 66e207a451ef0..9c11fb41aa6d6 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -159,12 +159,13 @@ impl EarlyLintPass for NonAsciiIdents { use rustc_span::Span; use unicode_security::GeneralSecurityProfile; - let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).0 != Level::Allow; + let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).level != Level::Allow; let check_uncommon_codepoints = - cx.builder.lint_level(UNCOMMON_CODEPOINTS).0 != Level::Allow; - let check_confusable_idents = cx.builder.lint_level(CONFUSABLE_IDENTS).0 != Level::Allow; + cx.builder.lint_level(UNCOMMON_CODEPOINTS).level != Level::Allow; + let check_confusable_idents = + cx.builder.lint_level(CONFUSABLE_IDENTS).level != Level::Allow; let check_mixed_script_confusables = - cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).0 != Level::Allow; + cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).level != Level::Allow; if !check_non_ascii_idents && !check_uncommon_codepoints diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index ac9f8d92dacb7..16c061008085d 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -7,7 +7,7 @@ use rustc_parse_format::{ParseMode, Parser, Piece}; use rustc_session::lint::FutureIncompatibilityReason; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::edition::Edition; -use rustc_span::{InnerSpan, Span, Symbol, hygiene, kw, sym}; +use rustc_span::{InnerSpan, Span, Symbol, hygiene, sym}; use rustc_trait_selection::infer::InferCtxtExt; use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused}; @@ -167,7 +167,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc .get_diagnostic_item(sym::Debug) .is_some_and(|t| infcx.type_implements_trait(t, [ty], param_env).may_apply()); - let suggest_panic_any = !is_str && panic == sym::std_panic_macro; + let suggest_panic_any = !is_str && panic == Some(sym::std_panic_macro); let fmt_applicability = if suggest_panic_any { // If we can use panic_any, use that as the MachineApplicable suggestion. @@ -297,10 +297,13 @@ fn find_delimiters(cx: &LateContext<'_>, span: Span) -> Option<(Span, Span, char )) } -fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol, Symbol) { +fn panic_call<'tcx>( + cx: &LateContext<'tcx>, + f: &'tcx hir::Expr<'tcx>, +) -> (Span, Option, Symbol) { let mut expn = f.span.ctxt().outer_expn_data(); - let mut panic_macro = kw::Empty; + let mut panic_macro = None; // Unwrap more levels of macro expansion, as panic_2015!() // was likely expanded from panic!() and possibly from @@ -320,7 +323,7 @@ fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, break; } expn = parent; - panic_macro = name; + panic_macro = Some(name); } let macro_symbol = diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 0890890d12cbf..9ed11d9cc82ff 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -104,8 +104,10 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { // determining if we are in a doctest context can't currently be determined // by the code itself (there are no specific attributes), but fortunately rustdoc // sets a perma-unstable env var for libtest so we just reuse that for now - let is_at_toplevel_doctest = - || self.body_depth == 2 && std::env::var("UNSTABLE_RUSTDOC_TEST_PATH").is_ok(); + let is_at_toplevel_doctest = || { + self.body_depth == 2 + && cx.tcx.env_var_os("UNSTABLE_RUSTDOC_TEST_PATH".as_ref()).is_some() + }; match item.kind { ItemKind::Impl(impl_) => { @@ -181,10 +183,10 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { && parent_opt_item_name != Some(kw::Underscore) && let Some(parent) = parent.as_local() && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) - && let ItemKind::Const(ty, _, _) = item.kind + && let ItemKind::Const(ident, ty, _, _) = item.kind && let TyKind::Tup(&[]) = ty.kind { - Some(item.ident.span) + Some(ident.span) } else { None }; @@ -238,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { }, ) } - ItemKind::Macro(_macro, MacroKind::Bang) + ItemKind::Macro(_, _macro, MacroKind::Bang) if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) => { cx.emit_span_lint( diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 49f9ad39780a3..048d377b78fbc 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,5 +1,6 @@ use rustc_abi::ExternAbi; -use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprAttr}; +use rustc_attr_data_structures::{AttributeKind, ReprAttr}; +use rustc_attr_parsing::AttributeParser; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind}; @@ -172,20 +173,22 @@ impl EarlyLintPass for NonCamelCaseTypes { } match &it.kind { - ast::ItemKind::TyAlias(..) - | ast::ItemKind::Enum(..) - | ast::ItemKind::Struct(..) - | ast::ItemKind::Union(..) => self.check_case(cx, "type", &it.ident), - ast::ItemKind::Trait(..) => self.check_case(cx, "trait", &it.ident), - ast::ItemKind::TraitAlias(..) => self.check_case(cx, "trait alias", &it.ident), + ast::ItemKind::TyAlias(box ast::TyAlias { ident, .. }) + | ast::ItemKind::Enum(ident, ..) + | ast::ItemKind::Struct(ident, ..) + | ast::ItemKind::Union(ident, ..) => self.check_case(cx, "type", ident), + ast::ItemKind::Trait(box ast::Trait { ident, .. }) => { + self.check_case(cx, "trait", ident) + } + ast::ItemKind::TraitAlias(ident, _, _) => self.check_case(cx, "trait alias", ident), // N.B. This check is only for inherent associated types, so that we don't lint against // trait impls where we should have warned for the trait definition already. ast::ItemKind::Impl(box ast::Impl { of_trait: None, items, .. }) => { for it in items { // FIXME: this doesn't respect `#[allow(..)]` on the item itself. - if let ast::AssocItemKind::Type(..) = it.kind { - self.check_case(cx, "associated type", &it.ident); + if let ast::AssocItemKind::Type(alias) = &it.kind { + self.check_case(cx, "associated type", &alias.ident); } } } @@ -194,8 +197,8 @@ impl EarlyLintPass for NonCamelCaseTypes { } fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) { - if let ast::AssocItemKind::Type(..) = it.kind { - self.check_case(cx, "associated type", &it.ident); + if let ast::AssocItemKind::Type(alias) = &it.kind { + self.check_case(cx, "associated type", &alias.ident); } } @@ -274,18 +277,13 @@ impl NonSnakeCase { let ident = ident.trim_start_matches('\''); let ident = ident.trim_matches('_'); - let mut allow_underscore = true; - ident.chars().all(|c| { - allow_underscore = match c { - '_' if !allow_underscore => return false, - '_' => false, - // It would be more obvious to use `c.is_lowercase()`, - // but some characters do not have a lowercase form - c if !c.is_uppercase() => true, - _ => return false, - }; - true - }) + if ident.contains("__") { + return false; + } + + // This correctly handles letters in languages with and without + // cases, as well as numbers and underscores. + !ident.chars().any(char::is_uppercase) } let name = ident.name.as_str(); @@ -342,8 +340,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 { - ast::attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name) - .and_then(|attr| { + ast::attr::find_by_name(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), sym::crate_name).and_then( + |attr| { if let Attribute::Unparsed(n) = attr && let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: lit }, .. } = n.as_ref() @@ -371,7 +369,8 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { } else { None } - }) + }, + ) }; if let Some(ident) = &crate_ident { @@ -419,16 +418,28 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { } fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - if let hir::ItemKind::Mod(_) = it.kind { - self.check_snake_case(cx, "module", &it.ident); + if let hir::ItemKind::Mod(ident, _) = it.kind { + self.check_snake_case(cx, "module", &ident); + } + } + + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_, hir::AmbigArg>) { + if let hir::TyKind::BareFn(hir::BareFnTy { param_idents, .. }) = &ty.kind { + for param_ident in *param_idents { + if let Some(param_ident) = param_ident { + self.check_snake_case(cx, "variable", param_ident); + } + } } } fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &hir::TraitItem<'_>) { - if let hir::TraitItemKind::Fn(_, hir::TraitFn::Required(pnames)) = item.kind { + if let hir::TraitItemKind::Fn(_, hir::TraitFn::Required(param_idents)) = item.kind { self.check_snake_case(cx, "trait method", &item.ident); - for param_name in pnames { - self.check_snake_case(cx, "variable", param_name); + for param_ident in param_idents { + if let Some(param_ident) = param_ident { + self.check_snake_case(cx, "variable", param_ident); + } } } } @@ -500,13 +511,15 @@ impl NonUpperCaseGlobals { impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - let attrs = cx.tcx.hir().attrs(it.hir_id()); + let attrs = cx.tcx.hir_attrs(it.hir_id()); match it.kind { - hir::ItemKind::Static(..) if !ast::attr::contains_name(attrs, sym::no_mangle) => { - NonUpperCaseGlobals::check_upper_case(cx, "static variable", &it.ident); + hir::ItemKind::Static(ident, ..) + if !ast::attr::contains_name(attrs, sym::no_mangle) => + { + NonUpperCaseGlobals::check_upper_case(cx, "static variable", &ident); } - hir::ItemKind::Const(..) => { - NonUpperCaseGlobals::check_upper_case(cx, "constant", &it.ident); + hir::ItemKind::Const(ident, ..) => { + NonUpperCaseGlobals::check_upper_case(cx, "constant", &ident); } _ => {} } diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 7eaf83f5acf31..f836094191e1d 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -1,9 +1,8 @@ use rustc_hir::{self as hir, AmbigArg}; use rustc_infer::infer::TyCtxtInferExt; use rustc_macros::{LintDiagnostic, Subdiagnostic}; -use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::print::{PrintTraitPredicateExt as _, TraitPredPrintModifiersAndPath}; -use rustc_middle::ty::{self, Ty, TypeFoldable}; +use rustc_middle::ty::{self, BottomUpFolder, Ty, TypeFoldable}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::{Span, kw}; use rustc_trait_selection::traits::{self, ObligationCtxt}; @@ -43,6 +42,7 @@ declare_lint! { /// /// type Tait = impl Sized; /// + /// #[define_opaque(Tait)] /// fn test() -> impl Trait { /// 42 /// } diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs index 1489f9de81991..826bce2c31506 100644 --- a/compiler/rustc_lint/src/ptr_nulls.rs +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -1,9 +1,11 @@ use rustc_ast::LitKind; use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; +use rustc_middle::ty::RawPtr; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::sym; +use rustc_span::{Span, sym}; -use crate::lints::PtrNullChecksDiag; +use crate::lints::{InvalidNullArgumentsDiag, UselessPtrNullChecksDiag}; +use crate::utils::peel_casts; use crate::{LateContext, LateLintPass, LintContext}; declare_lint! { @@ -31,17 +33,40 @@ declare_lint! { "useless checking of non-null-typed pointer" } -declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]); +declare_lint! { + /// The `invalid_null_arguments` lint checks for invalid usage of null pointers in arguments. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// # use std::{slice, ptr}; + /// // Undefined behavior + /// # let _slice: &[u8] = + /// unsafe { slice::from_raw_parts(ptr::null(), 0) }; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Calling methods whos safety invariants requires non-null ptr with a null pointer + /// is [Undefined Behavior](https://doc.rust-lang.org/reference/behavior-considered-undefined.html)! + INVALID_NULL_ARGUMENTS, + Deny, + "invalid null pointer in arguments" +} + +declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS, INVALID_NULL_ARGUMENTS]); /// This function checks if the expression is from a series of consecutive casts, /// ie. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either /// a fn ptr, a reference, or a function call whose definition is /// annotated with `#![rustc_never_returns_null_ptr]`. /// If this situation is present, the function returns the appropriate diagnostic. -fn incorrect_check<'a, 'tcx: 'a>( +fn useless_check<'a, 'tcx: 'a>( cx: &'a LateContext<'tcx>, mut e: &'a Expr<'a>, -) -> Option> { +) -> Option> { let mut had_at_least_one_cast = false; loop { e = e.peel_blocks(); @@ -50,14 +75,14 @@ fn incorrect_check<'a, 'tcx: 'a>( && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) { - return Some(PtrNullChecksDiag::FnRet { fn_name }); + return Some(UselessPtrNullChecksDiag::FnRet { fn_name }); } else if let ExprKind::Call(path, _args) = e.kind && let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) { - return Some(PtrNullChecksDiag::FnRet { fn_name }); + return Some(UselessPtrNullChecksDiag::FnRet { fn_name }); } e = if let ExprKind::Cast(expr, t) = e.kind && let TyKind::Ptr(_) = t.kind @@ -73,9 +98,9 @@ fn incorrect_check<'a, 'tcx: 'a>( } else if had_at_least_one_cast { let orig_ty = cx.typeck_results().expr_ty(e); return if orig_ty.is_fn() { - Some(PtrNullChecksDiag::FnPtr { orig_ty, label: e.span }) + Some(UselessPtrNullChecksDiag::FnPtr { orig_ty, label: e.span }) } else if orig_ty.is_ref() { - Some(PtrNullChecksDiag::Ref { orig_ty, label: e.span }) + Some(UselessPtrNullChecksDiag::Ref { orig_ty, label: e.span }) } else { None }; @@ -85,6 +110,25 @@ fn incorrect_check<'a, 'tcx: 'a>( } } +/// Checks if the given expression is a null pointer (modulo casting) +fn is_null_ptr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { + let (expr, _) = peel_casts(cx, expr); + + if let ExprKind::Call(path, []) = expr.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id) + { + (diag_item == sym::ptr_null || diag_item == sym::ptr_null_mut).then_some(expr.span) + } else if let ExprKind::Lit(spanned) = expr.kind + && let LitKind::Int(v, _) = spanned.node + { + (v == 0).then_some(expr.span) + } else { + None + } +} + impl<'tcx> LateLintPass<'tcx> for PtrNullChecks { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { match expr.kind { @@ -97,11 +141,67 @@ impl<'tcx> LateLintPass<'tcx> for PtrNullChecks { cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_const_is_null | sym::ptr_is_null) ) - && let Some(diag) = incorrect_check(cx, arg) => + && let Some(diag) = useless_check(cx, arg) => { cx.emit_span_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag) } + // Catching: + // (arg...) where `arg` is null-ptr and `path` is a fn that expect non-null-ptr + ExprKind::Call(path, args) + if let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && let Some(diag_name) = cx.tcx.get_diagnostic_name(def_id) => + { + // `arg` positions where null would cause U.B and whenever ZST are allowed. + // + // We should probably have a `rustc` attribute, but checking them is costly, + // maybe if we checked for null ptr first, it would be acceptable? + let (arg_indices, are_zsts_allowed): (&[_], _) = match diag_name { + sym::ptr_read + | sym::ptr_read_unaligned + | sym::ptr_read_volatile + | sym::ptr_replace + | sym::ptr_write + | sym::ptr_write_bytes + | sym::ptr_write_unaligned + | sym::ptr_write_volatile => (&[0], true), + sym::slice_from_raw_parts | sym::slice_from_raw_parts_mut => (&[0], false), + sym::ptr_copy + | sym::ptr_copy_nonoverlapping + | sym::ptr_swap + | sym::ptr_swap_nonoverlapping => (&[0, 1], true), + _ => return, + }; + + for &arg_idx in arg_indices { + if let Some(arg) = args.get(arg_idx) + && let Some(null_span) = is_null_ptr(cx, arg) + && let Some(ty) = cx.typeck_results().expr_ty_opt(arg) + && let RawPtr(ty, _mutbl) = ty.kind() + { + // If ZST are fine, don't lint on them + let typing_env = cx.typing_env(); + if are_zsts_allowed + && cx + .tcx + .layout_of(typing_env.as_query_input(*ty)) + .is_ok_and(|layout| layout.is_1zst()) + { + break; + } + + let diag = if arg.span.contains(null_span) { + InvalidNullArgumentsDiag::NullPtrInline { null_span } + } else { + InvalidNullArgumentsDiag::NullPtrThroughBinding { null_span } + }; + + cx.emit_span_lint(INVALID_NULL_ARGUMENTS, expr.span, diag) + } + } + } + // Catching: // (fn_ptr as * ).is_null() ExprKind::MethodCall(_, receiver, _, _) @@ -110,18 +210,18 @@ impl<'tcx> LateLintPass<'tcx> for PtrNullChecks { cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_const_is_null | sym::ptr_is_null) ) - && let Some(diag) = incorrect_check(cx, receiver) => + && let Some(diag) = useless_check(cx, receiver) => { cx.emit_span_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag) } ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => { let to_check: &Expr<'_>; - let diag: PtrNullChecksDiag<'_>; - if let Some(ddiag) = incorrect_check(cx, left) { + let diag: UselessPtrNullChecksDiag<'_>; + if let Some(ddiag) = useless_check(cx, left) { to_check = right; diag = ddiag; - } else if let Some(ddiag) = incorrect_check(cx, right) { + } else if let Some(ddiag) = useless_check(cx, right) { to_check = left; diag = ddiag; } else { diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs index 7c6656f91c99b..1d4d380cb685e 100644 --- a/compiler/rustc_lint/src/reference_casting.rs +++ b/compiler/rustc_lint/src/reference_casting.rs @@ -6,6 +6,7 @@ use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; use crate::lints::InvalidReferenceCastingDiag; +use crate::utils::peel_casts; use crate::{LateContext, LateLintPass, LintContext}; declare_lint! { @@ -235,46 +236,3 @@ fn is_cast_to_bigger_memory_layout<'tcx>( None } } - -fn peel_casts<'tcx>(cx: &LateContext<'tcx>, mut e: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, bool) { - let mut gone_trough_unsafe_cell_raw_get = false; - - loop { - e = e.peel_blocks(); - // as ... - e = if let ExprKind::Cast(expr, _) = e.kind { - expr - // .cast(), .cast_mut() or .cast_const() - } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind - && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && matches!( - cx.tcx.get_diagnostic_name(def_id), - Some(sym::ptr_cast | sym::const_ptr_cast | sym::ptr_cast_mut | sym::ptr_cast_const) - ) - { - expr - // ptr::from_ref(), UnsafeCell::raw_get() or mem::transmute<_, _>() - } else if let ExprKind::Call(path, [arg]) = e.kind - && let ExprKind::Path(ref qpath) = path.kind - && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() - && matches!( - cx.tcx.get_diagnostic_name(def_id), - Some(sym::ptr_from_ref | sym::unsafe_cell_raw_get | sym::transmute) - ) - { - if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) { - gone_trough_unsafe_cell_raw_get = true; - } - arg - } else { - let init = cx.expr_or_init(e); - if init.hir_id != e.hir_id { - init - } else { - break; - } - }; - } - - (e, gone_trough_unsafe_cell_raw_get) -} diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs index 571cab934fd6a..00fa0499556cb 100644 --- a/compiler/rustc_lint/src/shadowed_into_iter.rs +++ b/compiler/rustc_lint/src/shadowed_into_iter.rs @@ -1,4 +1,4 @@ -use rustc_hir as hir; +use rustc_hir::{self as hir, LangItem}; use rustc_middle::ty::{self, Ty}; use rustc_session::lint::FutureIncompatibilityReason; use rustc_session::{declare_lint, impl_lint_pass}; @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter { let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { return; }; - if Some(method_def_id) != cx.tcx.lang_items().into_iter_fn() { + if !cx.tcx.is_lang_item(method_def_id, LangItem::IntoIterIntoIter) { return; } diff --git a/compiler/rustc_lint/src/static_mut_refs.rs b/compiler/rustc_lint/src/static_mut_refs.rs index 50021157ddab7..4dda3c7951b87 100644 --- a/compiler/rustc_lint/src/static_mut_refs.rs +++ b/compiler/rustc_lint/src/static_mut_refs.rs @@ -51,7 +51,7 @@ declare_lint! { /// This lint is "warn" by default on editions up to 2021, in 2024 is "deny". pub STATIC_MUT_REFS, Warn, - "shared references or mutable references of mutable static is discouraged", + "creating a shared reference to mutable static", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), reference: "", diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index cb83d405cc3ed..f1c06dfe6ce0e 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,7 +1,9 @@ use std::iter; use std::ops::ControlFlow; -use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, VariantIdx, Variants, WrappingRange}; +use rustc_abi::{ + BackendRepr, Integer, IntegerType, TagEncoding, VariantIdx, Variants, WrappingRange, +}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; use rustc_hir::intravisit::VisitorExt; @@ -14,7 +16,7 @@ use rustc_middle::ty::{ }; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, Symbol, source_map, sym}; +use rustc_span::{Span, Symbol, sym}; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; @@ -223,7 +225,7 @@ impl TypeLimits { fn lint_nan<'tcx>( cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>, - binop: hir::BinOp, + binop: hir::BinOpKind, l: &'tcx hir::Expr<'tcx>, r: &'tcx hir::Expr<'tcx>, ) { @@ -262,19 +264,19 @@ fn lint_nan<'tcx>( InvalidNanComparisons::EqNe { suggestion } } - let lint = match binop.node { + let lint = match binop { hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => { eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { nan_plus_binop: l_span.until(r_span), float: r_span.shrink_to_hi(), - neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()), + neg: (binop == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()), }) } hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => { eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { nan_plus_binop: l_span.shrink_to_hi().to(r_span), float: l_span.shrink_to_hi(), - neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()), + neg: (binop == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()), }) } hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge @@ -560,11 +562,11 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } } hir::ExprKind::Binary(binop, ref l, ref r) => { - if is_comparison(binop) { - if !check_limits(cx, binop, l, r) { + if is_comparison(binop.node) { + if !check_limits(cx, binop.node, l, r) { cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); } else { - lint_nan(cx, e, binop, l, r); + lint_nan(cx, e, binop.node, l, r); let cmpop = ComparisonOp::BinOp(binop.node); lint_wide_pointer(cx, e, cmpop, l, r); lint_fn_pointer(cx, e, cmpop, l, r); @@ -591,8 +593,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { _ => {} }; - fn is_valid(binop: hir::BinOp, v: T, min: T, max: T) -> bool { - match binop.node { + fn is_valid(binop: hir::BinOpKind, v: T, min: T, max: T) -> bool { + match binop { hir::BinOpKind::Lt => v > min && v <= max, hir::BinOpKind::Le => v >= min && v < max, hir::BinOpKind::Gt => v >= min && v < max, @@ -602,22 +604,19 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } } - fn rev_binop(binop: hir::BinOp) -> hir::BinOp { - source_map::respan( - binop.span, - match binop.node { - hir::BinOpKind::Lt => hir::BinOpKind::Gt, - hir::BinOpKind::Le => hir::BinOpKind::Ge, - hir::BinOpKind::Gt => hir::BinOpKind::Lt, - hir::BinOpKind::Ge => hir::BinOpKind::Le, - _ => return binop, - }, - ) + fn rev_binop(binop: hir::BinOpKind) -> hir::BinOpKind { + match binop { + hir::BinOpKind::Lt => hir::BinOpKind::Gt, + hir::BinOpKind::Le => hir::BinOpKind::Ge, + hir::BinOpKind::Gt => hir::BinOpKind::Lt, + hir::BinOpKind::Ge => hir::BinOpKind::Le, + _ => binop, + } } fn check_limits( cx: &LateContext<'_>, - binop: hir::BinOp, + binop: hir::BinOpKind, l: &hir::Expr<'_>, r: &hir::Expr<'_>, ) -> bool { @@ -659,9 +658,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } } - fn is_comparison(binop: hir::BinOp) -> bool { + fn is_comparison(binop: hir::BinOpKind) -> bool { matches!( - binop.node, + binop, hir::BinOpKind::Eq | hir::BinOpKind::Lt | hir::BinOpKind::Le @@ -756,10 +755,10 @@ declare_lint! { /// *subsequent* fields of the associated structs to use an alignment value /// where the floating-point type is aligned on a 4-byte boundary. /// - /// The power alignment rule for structs needed for C compatibility is - /// unimplementable within `repr(C)` in the compiler without building in - /// handling of references to packed fields and infectious nested layouts, - /// so a warning is produced in these situations. + /// Effectively, subsequent floating-point fields act as-if they are `repr(packed(4))`. This + /// would be unsound to do in a `repr(C)` type without all the restrictions that come with + /// `repr(packed)`. Rust instead chooses a layout that maintains soundness of Rust code, at the + /// expense of incompatibility with C code. /// /// ### Example /// @@ -791,8 +790,10 @@ declare_lint! { /// - offset_of!(Floats, a) == 0 /// - offset_of!(Floats, b) == 8 /// - offset_of!(Floats, c) == 12 - /// However, rust currently aligns `c` at offset_of!(Floats, c) == 16. - /// Thus, a warning should be produced for the above struct in this case. + /// + /// However, Rust currently aligns `c` at `offset_of!(Floats, c) == 16`. + /// Using offset 12 would be unsound since `f64` generally must be 8-aligned on this target. + /// Thus, a warning is produced for the above struct. USES_POWER_ALIGNMENT, Warn, "Structs do not follow the power alignment rule under repr(C)" @@ -867,8 +868,8 @@ fn ty_is_known_nonnull<'tcx>( return true; } - // `UnsafeCell` has its niche hidden. - if def.is_unsafe_cell() { + // `UnsafeCell` and `UnsafePinned` have their niche hidden. + if def.is_unsafe_cell() || def.is_unsafe_pinned() { return false; } @@ -879,39 +880,36 @@ fn ty_is_known_nonnull<'tcx>( } ty::Pat(base, pat) => { ty_is_known_nonnull(tcx, typing_env, *base, mode) - || Option::unwrap_or_default( - try { - match **pat { - ty::PatternKind::Range { start, end, include_end } => { - match (start, end) { - (Some(start), None) => { - start.try_to_value()?.try_to_bits(tcx, typing_env)? > 0 - } - (Some(start), Some(end)) => { - let start = - start.try_to_value()?.try_to_bits(tcx, typing_env)?; - let end = - end.try_to_value()?.try_to_bits(tcx, typing_env)?; - - if include_end { - // This also works for negative numbers, as we just need - // to ensure we aren't wrapping over zero. - start > 0 && end >= start - } else { - start > 0 && end > start - } - } - _ => false, - } - } - } - }, - ) + || pat_ty_is_known_nonnull(tcx, typing_env, *pat) } _ => false, } } +fn pat_ty_is_known_nonnull<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + pat: ty::Pattern<'tcx>, +) -> bool { + Option::unwrap_or_default( + try { + match *pat { + ty::PatternKind::Range { start, end } => { + let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?; + let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?; + + // This also works for negative numbers, as we just need + // to ensure we aren't wrapping over zero. + start > 0 && end >= start + } + ty::PatternKind::Or(patterns) => { + patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat)) + } + } + }, + ) +} + /// 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>( @@ -1053,13 +1051,29 @@ pub(crate) fn repr_nullable_ptr<'tcx>( } None } - ty::Pat(base, pat) => match **pat { - ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, *base), - }, + ty::Pat(base, pat) => get_nullable_type_from_pat(tcx, typing_env, *base, *pat), _ => None, } } +fn get_nullable_type_from_pat<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + base: Ty<'tcx>, + pat: ty::Pattern<'tcx>, +) -> Option> { + match *pat { + ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, base), + ty::PatternKind::Or(patterns) => { + let first = get_nullable_type_from_pat(tcx, typing_env, base, patterns[0])?; + for &pat in &patterns[1..] { + assert_eq!(first, get_nullable_type_from_pat(tcx, typing_env, base, pat)?); + } + Some(first) + } + } +} + 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 { @@ -1207,9 +1221,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; } - let is_non_exhaustive = - def.non_enum_variant().is_field_list_non_exhaustive(); - if is_non_exhaustive && !def.did().is_local() { + if def.non_enum_variant().field_list_has_applicable_non_exhaustive() { return FfiUnsafe { ty, reason: if def.is_struct() { @@ -1262,14 +1274,20 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; } - use improper_ctypes::{ - check_non_exhaustive_variant, non_local_and_non_exhaustive, - }; + if let Some(IntegerType::Fixed(Integer::I128, _)) = def.repr().int { + return FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_128bit, + help: None, + }; + } + + use improper_ctypes::check_non_exhaustive_variant; - let non_local_def = non_local_and_non_exhaustive(def); + let non_exhaustive = def.variant_list_has_applicable_non_exhaustive(); // Check the contained variants. let ret = def.variants().iter().try_for_each(|variant| { - check_non_exhaustive_variant(non_local_def, variant) + check_non_exhaustive_variant(non_exhaustive, variant) .map_break(|reason| FfiUnsafe { ty, reason, help: None })?; match self.check_variant_for_ffi(acc, ty, def, variant, args) { @@ -1349,7 +1367,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::FnPtr(sig_tys, hdr) => { let sig = sig_tys.with(hdr); - if self.is_internal_abi(sig.abi()) { + if sig.abi().is_rustic_abi() { return FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_fnptr_reason, @@ -1392,7 +1410,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), ty::Param(..) - | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) + | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) | ty::Infer(..) | ty::Bound(..) | ty::Error(_) @@ -1421,7 +1439,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { CItemKind::Definition => "fn", }; let span_note = if let ty::Adt(def, _) = ty.kind() - && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) + && let Some(sp) = self.cx.tcx.hir_span_if_local(def.did()) { Some(sp) } else { @@ -1436,7 +1454,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { struct ProhibitOpaqueTypes; - impl<'tcx> ty::visit::TypeVisitor> for ProhibitOpaqueTypes { + impl<'tcx> ty::TypeVisitor> for ProhibitOpaqueTypes { type Result = ControlFlow>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { @@ -1552,13 +1570,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { self.check_type_for_ffi_and_report_errors(span, ty, true, false); } - fn is_internal_abi(&self, abi: ExternAbi) -> bool { - matches!( - abi, - ExternAbi::Rust | ExternAbi::RustCall | ExternAbi::RustCold | ExternAbi::RustIntrinsic - ) - } - /// Find any fn-ptr types with external ABIs in `ty`. /// /// For example, `Option` returns `extern "C" fn()` @@ -1567,17 +1578,16 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { hir_ty: &hir::Ty<'tcx>, ty: Ty<'tcx>, ) -> Vec<(Ty<'tcx>, Span)> { - struct FnPtrFinder<'a, 'b, 'tcx> { - visitor: &'a ImproperCTypesVisitor<'b, 'tcx>, + struct FnPtrFinder<'tcx> { spans: Vec, tys: Vec>, } - impl<'a, 'b, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'a, 'b, 'tcx> { + impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> { fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { debug!(?ty); if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind - && !self.visitor.is_internal_abi(*abi) + && !abi.is_rustic_abi() { self.spans.push(ty.span); } @@ -1586,12 +1596,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - impl<'a, 'b, 'tcx> ty::visit::TypeVisitor> for FnPtrFinder<'a, 'b, 'tcx> { + impl<'tcx> ty::TypeVisitor> for FnPtrFinder<'tcx> { type Result = (); fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { if let ty::FnPtr(_, hdr) = ty.kind() - && !self.visitor.is_internal_abi(hdr.abi) + && !hdr.abi.is_rustic_abi() { self.tys.push(ty); } @@ -1600,7 +1610,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - let mut visitor = FnPtrFinder { visitor: self, spans: Vec::new(), tys: Vec::new() }; + let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() }; ty.visit_with(&mut visitor); visitor.visit_ty_unambig(hir_ty); @@ -1611,17 +1621,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) { let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration }; - let abi = cx.tcx.hir().get_foreign_abi(it.hir_id()); + let abi = cx.tcx.hir_get_foreign_abi(it.hir_id()); match it.kind { hir::ForeignItemKind::Fn(sig, _, _) => { - if vis.is_internal_abi(abi) { + if abi.is_rustic_abi() { vis.check_fn(it.owner_id.def_id, sig.decl) } else { vis.check_foreign_fn(it.owner_id.def_id, sig.decl); } } - hir::ForeignItemKind::Static(ty, _, _) if !vis.is_internal_abi(abi) => { + hir::ForeignItemKind::Static(ty, _, _) if !abi.is_rustic_abi() => { vis.check_foreign_static(it.owner_id, ty.span); } hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), @@ -1647,19 +1657,20 @@ impl ImproperCTypesDefinitions { cx: &LateContext<'tcx>, ty: Ty<'tcx>, ) -> bool { + assert!(cx.tcx.sess.target.os == "aix"); // Structs (under repr(C)) follow the power alignment rule if: // - the first field of the struct is a floating-point type that // is greater than 4-bytes, or // - the first field of the struct is an aggregate whose // recursively first field is a floating-point type greater than // 4 bytes. - if cx.tcx.sess.target.os != "aix" { - return false; - } if ty.is_floating_point() && ty.primitive_size(cx.tcx).bytes() > 4 { return true; } else if let Adt(adt_def, _) = ty.kind() && adt_def.is_struct() + && adt_def.repr().c() + && !adt_def.repr().packed() + && adt_def.repr().align.is_none() { let struct_variant = adt_def.variant(VariantIdx::ZERO); // Within a nested struct, all fields are examined to correctly @@ -1681,27 +1692,23 @@ impl ImproperCTypesDefinitions { item: &'tcx hir::Item<'tcx>, ) { let adt_def = cx.tcx.adt_def(item.owner_id.to_def_id()); + // repr(C) structs also with packed or aligned representation + // should be ignored. if adt_def.repr().c() && !adt_def.repr().packed() + && adt_def.repr().align.is_none() && cx.tcx.sess.target.os == "aix" && !adt_def.all_fields().next().is_none() { - let struct_variant_data = item.expect_struct().0; - for (index, ..) in struct_variant_data.fields().iter().enumerate() { + let struct_variant_data = item.expect_struct().1; + for field_def in struct_variant_data.fields().iter().skip(1) { // Struct fields (after the first field) are checked for the // power alignment rule, as fields after the first are likely // to be the fields that are misaligned. - if index != 0 { - let first_field_def = struct_variant_data.fields()[index]; - let def_id = first_field_def.def_id; - let ty = cx.tcx.type_of(def_id).instantiate_identity(); - if self.check_arg_for_power_alignment(cx, ty) { - cx.emit_span_lint( - USES_POWER_ALIGNMENT, - first_field_def.span, - UsesPowerAlignment, - ); - } + let def_id = field_def.def_id; + let ty = cx.tcx.type_of(def_id).instantiate_identity(); + if self.check_arg_for_power_alignment(cx, ty) { + cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment); } } } @@ -1718,9 +1725,9 @@ impl ImproperCTypesDefinitions { impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { match item.kind { - hir::ItemKind::Static(ty, ..) - | hir::ItemKind::Const(ty, ..) - | hir::ItemKind::TyAlias(ty, ..) => { + hir::ItemKind::Static(_, ty, ..) + | hir::ItemKind::Const(_, ty, ..) + | hir::ItemKind::TyAlias(_, ty, ..) => { self.check_ty_maybe_containing_foreign_fnptr( cx, ty, @@ -1775,7 +1782,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { }; let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition }; - if vis.is_internal_abi(abi) { + if abi.is_rustic_abi() { vis.check_fn(id, decl); } else { vis.check_foreign_fn(id, decl); @@ -1787,7 +1794,7 @@ declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]); impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - if let hir::ItemKind::Enum(ref enum_definition, _) = it.kind { + if let hir::ItemKind::Enum(_, ref enum_definition, _) = it.kind { let t = cx.tcx.type_of(it.owner_id).instantiate_identity(); let ty = cx.tcx.erase_regions(t); let Ok(layout) = cx.layout_of(ty) else { return }; diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 1030101c545da..13afa540afcf6 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -15,13 +15,13 @@ use crate::fluent_generated as fluent; /// so we don't need the lint to account for it. /// e.g. going from enum Foo { A, B, C } to enum Foo { A, B, C, D(u32) }. pub(crate) fn check_non_exhaustive_variant( - non_local_def: bool, + non_exhaustive_variant_list: bool, variant: &ty::VariantDef, ) -> ControlFlow { // non_exhaustive suggests it is possible that someone might break ABI // see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344 // so warn on complex enums being used outside their crate - if non_local_def { + if non_exhaustive_variant_list { // which is why we only warn about really_tagged_union reprs from https://rust.tf/rfc2195 // with an enum like `#[repr(u8)] enum Enum { A(DataA), B(DataB), }` // but exempt enums with unit ctors like C's (e.g. from rust-bindgen) @@ -30,8 +30,7 @@ pub(crate) fn check_non_exhaustive_variant( } } - let non_exhaustive_variant_fields = variant.is_field_list_non_exhaustive(); - if non_exhaustive_variant_fields && !variant.def_id.is_local() { + if variant.field_list_has_applicable_non_exhaustive() { return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant); } @@ -42,10 +41,3 @@ fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool { // CtorKind::Const means a "unit" ctor !matches!(variant.ctor_kind(), Some(CtorKind::Const)) } - -// non_exhaustive suggests it is possible that someone might break ABI -// see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344 -// so warn on complex enums being used outside their crate -pub(crate) fn non_local_and_non_exhaustive(def: ty::AdtDef<'_>) -> bool { - def.is_variant_list_non_exhaustive() && !def.did().is_local() -} diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index 7cb00262b6ffa..d44f45177bde0 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::{bug, ty}; use rustc_span::Span; -use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_attr_data_structures as attrs, rustc_hir as hir}; use crate::LateContext; use crate::context::LintContext; @@ -131,18 +131,18 @@ fn report_bin_hex_error( cx: &LateContext<'_>, hir_id: HirId, span: Span, - ty: attr::IntType, + ty: attrs::IntType, size: Size, repr_str: String, val: u128, negative: bool, ) { let (t, actually) = match ty { - attr::IntType::SignedInt(t) => { + attrs::IntType::SignedInt(t) => { let actually = if negative { -(size.sign_extend(val)) } else { size.sign_extend(val) }; (t.name_str(), actually.to_string()) } - attr::IntType::UnsignedInt(t) => { + attrs::IntType::UnsignedInt(t) => { let actually = size.truncate(val); (t.name_str(), actually.to_string()) } @@ -264,7 +264,7 @@ fn lint_int_literal<'tcx>( cx, hir_id, span, - attr::IntType::SignedInt(ty::ast_int_ty(t)), + attrs::IntType::SignedInt(ty::ast_int_ty(t)), Integer::from_int_ty(cx, t).size(), repr_str, v, @@ -336,7 +336,7 @@ fn lint_uint_literal<'tcx>( cx, hir_id, span, - attr::IntType::UnsignedInt(ty::ast_uint_ty(t)), + attrs::IntType::UnsignedInt(ty::ast_uint_ty(t)), Integer::from_uint_ty(cx, t).size(), repr_str, lit_val, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 7b43aac90c741..50a27d7e84f58 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -942,6 +942,22 @@ trait UnusedDelimLint { match s.kind { StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { if let Some((init, els)) = local.kind.init_else_opt() { + if els.is_some() + && let ExprKind::Paren(paren) = &init.kind + && !init.span.eq_ctxt(paren.span) + { + // This branch prevents cases where parentheses wrap an expression + // resulting from macro expansion, such as: + // ``` + // macro_rules! x { + // () => { None:: }; + // } + // let Some(_) = (x!{}) else { return }; + // // -> let Some(_) = (None::) else { return }; + // // ~ ~ No Lint + // ``` + return; + } let ctx = match els { None => UnusedDelimsCtx::AssignedValue, Some(_) => UnusedDelimsCtx::AssignedValueLetElse, @@ -1201,7 +1217,8 @@ impl EarlyLintPass for UnusedParens { // Do not lint on `(..)` as that will result in the other arms being useless. Paren(_) // The other cases do not contain sub-patterns. - | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) | Err(_) => {}, + | Missing | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None) + | Path(..) | Err(_) => {}, // These are list-like patterns; parens can always be removed. TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { self.check_unused_parens_pat(cx, p, false, false, keep_space); diff --git a/compiler/rustc_lint/src/utils.rs b/compiler/rustc_lint/src/utils.rs new file mode 100644 index 0000000000000..a7295d9c5326c --- /dev/null +++ b/compiler/rustc_lint/src/utils.rs @@ -0,0 +1,55 @@ +use rustc_hir::{Expr, ExprKind}; +use rustc_span::sym; + +use crate::LateContext; + +/// Given an expression, peel all of casts (` as ...`, `.cast{,_mut,_const}()`, +/// `ptr::from_ref()`, ...) and init expressions. +/// +/// Returns the innermost expression and a boolean representing if one of the casts was +/// `UnsafeCell::raw_get()` +pub(crate) fn peel_casts<'tcx>( + cx: &LateContext<'tcx>, + mut e: &'tcx Expr<'tcx>, +) -> (&'tcx Expr<'tcx>, bool) { + let mut gone_trough_unsafe_cell_raw_get = false; + + loop { + e = e.peel_blocks(); + // as ... + e = if let ExprKind::Cast(expr, _) = e.kind { + expr + // .cast(), .cast_mut() or .cast_const() + } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::ptr_cast | sym::const_ptr_cast | sym::ptr_cast_mut | sym::ptr_cast_const) + ) + { + expr + // ptr::from_ref(), UnsafeCell::raw_get() or mem::transmute<_, _>() + } else if let ExprKind::Call(path, [arg]) = e.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::ptr_from_ref | sym::unsafe_cell_raw_get | sym::transmute) + ) + { + if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) { + gone_trough_unsafe_cell_raw_get = true; + } + arg + } else { + let init = cx.expr_or_init(e); + if init.hir_id != e.hir_id { + init + } else { + break; + } + }; + } + + (e, gone_trough_unsafe_cell_raw_get) +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 97850a2afc11e..3cea24634feee 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -16,7 +16,6 @@ declare_lint_pass! { /// that are used by other parts of the compiler. HardwiredLints => [ // tidy-alphabetical-start - ABI_UNSUPPORTED_VECTOR_TYPES, ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_ASSOCIATED_ITEMS, AMBIGUOUS_GLOB_IMPORTS, @@ -73,14 +72,12 @@ declare_lint_pass! { NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, NON_CONTIGUOUS_RANGE_ENDPOINTS, NON_EXHAUSTIVE_OMITTED_PATTERNS, - ORDER_DEPENDENT_TRAIT_OBJECTS, OUT_OF_SCOPE_MACRO_CALLS, OVERLAPPING_RANGE_ENDPOINTS, PATTERNS_IN_FNS_WITHOUT_BODY, PRIVATE_BOUNDS, PRIVATE_INTERFACES, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, - PTR_CAST_ADD_AUTO_TO_OBJECT, PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_IMPORTS, @@ -112,7 +109,6 @@ declare_lint_pass! { UNCONDITIONAL_PANIC, UNCONDITIONAL_RECURSION, UNCOVERED_PARAM_IN_PROJECTION, - UNDEFINED_NAKED_FUNCTION_ABI, UNEXPECTED_CFGS, UNFULFILLED_LINT_EXPECTATIONS, UNINHABITED_STATIC, @@ -121,6 +117,7 @@ declare_lint_pass! { UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNNAMEABLE_TEST_ITEMS, UNNAMEABLE_TYPES, + UNNECESSARY_TRANSMUTES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, UNSAFE_ATTR_OUTSIDE_UNSAFE, @@ -145,6 +142,7 @@ declare_lint_pass! { UNUSED_VARIABLES, USELESS_DEPRECATED, WARNINGS, + WASM_C_ABI, // tidy-alphabetical-end ] } @@ -1426,7 +1424,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,compile_fail,edition2021 /// macro_rules! foo { /// () => {}; /// ($name) => { }; @@ -1502,42 +1500,6 @@ declare_lint! { }; } -declare_lint! { - /// The `order_dependent_trait_objects` lint detects a trait coherency - /// violation that would allow creating two trait impls for the same - /// dynamic trait object involving marker traits. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// pub trait Trait {} - /// - /// impl Trait for dyn Send + Sync { } - /// impl Trait for dyn Sync + Send { } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// A previous bug caused the compiler to interpret traits with different - /// orders (such as `Send + Sync` and `Sync + Send`) as distinct types - /// when they were intended to be treated the same. This allowed code to - /// define separate trait implementations when there should be a coherence - /// error. This is a [future-incompatible] lint to transition this to a - /// hard error in the future. See [issue #56484] for more details. - /// - /// [issue #56484]: https://github.com/rust-lang/rust/issues/56484 - /// [future-incompatible]: ../index.md#future-incompatible-lints - pub ORDER_DEPENDENT_TRAIT_OBJECTS, - Deny, - "trait-object types were treated as different depending on marker-trait order", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #56484 ", - }; -} - declare_lint! { /// The `coherence_leak_check` lint detects conflicting implementations of /// a trait that are only distinguished by the old leak-check code. @@ -2390,37 +2352,11 @@ declare_lint! { } declare_lint! { - /// The `soft_unstable` lint detects unstable features that were - /// unintentionally allowed on stable. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #[cfg(test)] - /// extern crate test; - /// - /// #[bench] - /// fn name(b: &mut test::Bencher) { - /// b.iter(|| 123) - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// The [`bench` attribute] was accidentally allowed to be specified on - /// the [stable release channel]. Turning this to a hard error would have - /// broken some projects. This lint allows those projects to continue to - /// build correctly when [`--cap-lints`] is used, but otherwise signal an - /// error that `#[bench]` should not be used on the stable channel. This - /// is a [future-incompatible] lint to transition this to a hard error in - /// the future. See [issue #64266] for more details. + /// The `soft_unstable` lint detects unstable features that were unintentionally allowed on + /// stable. This is a [future-incompatible] lint to transition this to a hard error in the + /// future. See [issue #64266] for more details. /// /// [issue #64266]: https://github.com/rust-lang/rust/issues/64266 - /// [`bench` attribute]: https://doc.rust-lang.org/nightly/unstable-book/library-features/test.html - /// [stable release channel]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html - /// [`--cap-lints`]: https://doc.rust-lang.org/rustc/lints/levels.html#capping-lints /// [future-incompatible]: ../index.md#future-incompatible-lints pub SOFT_UNSTABLE, Deny, @@ -2711,7 +2647,7 @@ declare_lint! { /// /// ```rust /// const fn foo() -> usize { - /// if std::mem::size_of::<*mut T>() < 8 { // size of *mut T does not depend on T + /// if size_of::<*mut T>() < 8 { // size of *mut T does not depend on T /// 4 /// } else { /// 8 @@ -2867,39 +2803,6 @@ declare_lint! { "detects deprecation attributes with no effect", } -declare_lint! { - /// The `undefined_naked_function_abi` lint detects naked function definitions that - /// either do not specify an ABI or specify the Rust ABI. - /// - /// ### Example - /// - /// ```rust - /// #![feature(asm_experimental_arch, naked_functions)] - /// - /// use std::arch::naked_asm; - /// - /// #[naked] - /// pub fn default_abi() -> u32 { - /// unsafe { naked_asm!(""); } - /// } - /// - /// #[naked] - /// pub extern "Rust" fn rust_abi() -> u32 { - /// unsafe { naked_asm!(""); } - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// The Rust ABI is currently undefined. Therefore, naked functions should - /// specify a non-Rust ABI. - pub UNDEFINED_NAKED_FUNCTION_ABI, - Warn, - "undefined naked function ABI" -} - declare_lint! { /// The `ineffective_unstable_trait_impl` lint detects `#[unstable]` attributes which are not used. /// @@ -3782,7 +3685,7 @@ declare_lint! { Warn, "use of unsupported calling convention for function pointer", @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, reference: "issue #130260 ", }; } @@ -4199,7 +4102,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,compile_fail,edition2021 /// #![deny(dependency_on_unit_never_type_fallback)] /// fn main() { /// if true { @@ -4827,58 +4730,6 @@ declare_lint! { }; } -declare_lint! { - /// The `ptr_cast_add_auto_to_object` lint detects casts of raw pointers to trait - /// objects, which add auto traits. - /// - /// ### Example - /// - /// ```rust,edition2021,compile_fail - /// let ptr: *const dyn core::any::Any = &(); - /// _ = ptr as *const dyn core::any::Any + Send; - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Adding an auto trait can make the vtable invalid, potentially causing - /// UB in safe code afterwards. For example: - /// - /// ```ignore (causes a warning) - /// #![feature(arbitrary_self_types)] - /// - /// trait Trait { - /// fn f(self: *const Self) - /// where - /// Self: Send; - /// } - /// - /// impl Trait for *const () { - /// fn f(self: *const Self) { - /// unreachable!() - /// } - /// } - /// - /// fn main() { - /// let unsend: *const () = &(); - /// let unsend: *const dyn Trait = &unsend; - /// let send_bad: *const (dyn Trait + Send) = unsend as _; - /// send_bad.f(); // this crashes, since vtable for `*const ()` does not have an entry for `f` - /// } - /// ``` - /// - /// Generally you must ensure that vtable is right for the pointer's type, - /// before passing the pointer to safe code. - pub PTR_CAST_ADD_AUTO_TO_OBJECT, - Warn, - "detects `as` casts from pointers to `dyn Trait` to pointers to `dyn Trait + Auto`", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #127323 ", - }; -} - declare_lint! { /// The `out_of_scope_macro_calls` lint detects `macro_rules` called when they are not in scope, /// above their definition, which may happen in key-value attributes. @@ -5033,6 +4884,30 @@ declare_lint! { "detects pointer to integer transmutes in const functions and associated constants", } +declare_lint! { + /// The `unnecessary_transmutes` lint detects transmutations that have safer alternatives. + /// + /// ### Example + /// + /// ```rust + /// fn bytes_at_home(x: [u8; 4]) -> u32 { + /// unsafe { std::mem::transmute(x) } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Using an explicit method is preferable over calls to + /// [`transmute`](https://doc.rust-lang.org/std/mem/fn.transmute.html) as + /// they more clearly communicate the intent, are easier to review, and + /// are less likely to accidentally result in unsoundness. + pub UNNECESSARY_TRANSMUTES, + Warn, + "detects transmutes that are shadowed by std methods" +} + declare_lint! { /// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location, /// that runs a custom `Drop` destructor. @@ -5151,67 +5026,44 @@ declare_lint! { } declare_lint! { - /// The `abi_unsupported_vector_types` lint detects function definitions and calls - /// whose ABI depends on enabling certain target features, but those features are not enabled. + /// The `wasm_c_abi` lint detects usage of the `extern "C"` ABI of wasm that is affected + /// by a planned ABI change that has the goal of aligning Rust with the standard C ABI + /// of this target. /// /// ### Example /// - /// ```rust,ignore (fails on non-x86_64) - /// extern "C" fn missing_target_feature(_: std::arch::x86_64::__m256) { - /// todo!() - /// } + /// ```rust,ignore (needs wasm32-unknown-unknown) + /// #[repr(C)] + /// struct MyType(i32, i32); /// - /// #[target_feature(enable = "avx")] - /// unsafe extern "C" fn with_target_feature(_: std::arch::x86_64::__m256) { - /// todo!() - /// } - /// - /// fn main() { - /// let v = unsafe { std::mem::zeroed() }; - /// unsafe { with_target_feature(v); } - /// } + /// extern "C" my_fun(x: MyType) {} /// ``` /// - /// ```text - /// warning: ABI error: this function call uses a avx vector type, which is not enabled in the caller - /// --> lint_example.rs:18:12 - /// | - /// | unsafe { with_target_feature(v); } - /// | ^^^^^^^^^^^^^^^^^^^^^^ function called here - /// | - /// = 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 #116558 - /// = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) - /// = note: `#[warn(abi_unsupported_vector_types)]` on by default - /// + /// This will produce: /// - /// warning: ABI error: this function definition uses a avx vector type, which is not enabled - /// --> lint_example.rs:3:1 - /// | - /// | pub extern "C" fn with_target_feature(_: std::arch::x86_64::__m256) { - /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - /// | - /// = 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 #116558 - /// = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) + /// ```text + /// error: this function function definition is affected by the wasm ABI transition: it passes an argument of non-scalar type `MyType` + /// --> $DIR/wasm_c_abi_transition.rs:17:1 + /// | + /// | pub extern "C" fn my_fun(_x: MyType) {} + /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + /// | + /// = 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 #138762 + /// = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target /// ``` /// - /// - /// /// ### Explanation /// - /// The C ABI for `__m256` requires the value to be passed in an AVX register, - /// which is only possible when the `avx` target feature is enabled. - /// Therefore, `missing_target_feature` cannot be compiled without that target feature. - /// A similar (but complementary) message is triggered when `with_target_feature` is called - /// by a function that does not enable the `avx` target feature. - /// - /// Note that this lint is very similar to the `-Wpsabi` warning in `gcc`/`clang`. - pub ABI_UNSUPPORTED_VECTOR_TYPES, + /// Rust has historically implemented a non-spec-compliant C ABI on wasm32-unknown-unknown. This + /// has caused incompatibilities with other compilers and Wasm targets. In a future version + /// of Rust, this will be fixed, and therefore code relying on the non-spec-compliant C ABI will + /// stop functioning. + pub WASM_C_ABI, Warn, - "this function call or definition uses a vector type which is not enabled", + "detects code relying on rustc's non-spec-compliant wasm C ABI", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #116558 ", + reference: "issue #138762 ", }; } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index e564235c41a5e..b4069b317bfa1 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -1,7 +1,3 @@ -// tidy-alphabetical-start -#![warn(unreachable_pub)] -// tidy-alphabetical-end - use rustc_abi::ExternAbi; use rustc_ast::AttrId; use rustc_ast::attr::AttributeExt; @@ -12,7 +8,8 @@ use rustc_data_structures::stable_hasher::{ }; use rustc_error_messages::{DiagMessage, MultiSpan}; use rustc_hir::def::Namespace; -use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind}; +use rustc_hir::def_id::DefPathHash; +use rustc_hir::{HashStableContext, HirId, ItemLocalId, MissingLifetimeKind}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::edition::Edition; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; @@ -106,7 +103,7 @@ pub enum Applicability { /// The index values have a type of `u16` to reduce the size of the `LintExpectationId`. /// It's reasonable to assume that no user will define 2^16 attributes on one node or /// have that amount of lints listed. `u16` values should therefore suffice. -#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Encodable, Decodable)] pub enum LintExpectationId { /// Used for lints emitted during the `EarlyLintPass`. This id is not /// hash stable and should not be cached. @@ -160,13 +157,14 @@ impl HashStable for LintExpectationId { } impl ToStableHashKey for LintExpectationId { - type KeyType = (HirId, u16, u16); + type KeyType = (DefPathHash, ItemLocalId, u16, u16); #[inline] - fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { + fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType { match self { LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { - (*hir_id, *attr_index, *lint_index) + let (def_path_hash, lint_idx) = hir_id.to_stable_hash_key(hcx); + (def_path_hash, lint_idx, *attr_index, *lint_index) } _ => { unreachable!("HashStable should only be called for a filled `LintExpectationId`") @@ -203,9 +201,9 @@ pub enum Level { /// /// See RFC 2383. /// - /// The [`LintExpectationId`] is used to later link a lint emission to the actual + /// Requires a [`LintExpectationId`] to later link a lint emission to the actual /// expectation. It can be ignored in most cases. - Expect(LintExpectationId), + Expect, /// The `warn` level will produce a warning if the lint was violated, however the /// compiler will continue with its execution. Warn, @@ -213,9 +211,9 @@ pub enum Level { /// to ensure that a lint can't be suppressed. This lint level can currently only be set /// via the console and is therefore session specific. /// - /// The [`LintExpectationId`] is intended to fulfill expectations marked via the + /// Requires a [`LintExpectationId`] to fulfill expectations marked via the /// `#[expect]` attribute, that will still be suppressed due to the level. - ForceWarn(Option), + ForceWarn, /// The `deny` level will produce an error and stop further execution after the lint /// pass is complete. Deny, @@ -229,9 +227,9 @@ impl Level { pub fn as_str(self) -> &'static str { match self { Level::Allow => "allow", - Level::Expect(_) => "expect", + Level::Expect => "expect", Level::Warn => "warn", - Level::ForceWarn(_) => "force-warn", + Level::ForceWarn => "force-warn", Level::Deny => "deny", Level::Forbid => "forbid", } @@ -250,24 +248,30 @@ impl Level { } /// Converts an `Attribute` to a level. - pub fn from_attr(attr: &impl AttributeExt) -> Option { - Self::from_symbol(attr.name_or_empty(), || Some(attr.id())) + pub fn from_attr(attr: &impl AttributeExt) -> Option<(Self, Option)> { + attr.name().and_then(|name| Self::from_symbol(name, || Some(attr.id()))) } /// Converts a `Symbol` to a level. - pub fn from_symbol(s: Symbol, id: impl FnOnce() -> Option) -> Option { + pub fn from_symbol( + s: Symbol, + id: impl FnOnce() -> Option, + ) -> Option<(Self, Option)> { match s { - sym::allow => Some(Level::Allow), + sym::allow => Some((Level::Allow, None)), sym::expect => { if let Some(attr_id) = id() { - Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None })) + Some(( + Level::Expect, + Some(LintExpectationId::Unstable { attr_id, lint_index: None }), + )) } else { None } } - sym::warn => Some(Level::Warn), - sym::deny => Some(Level::Deny), - sym::forbid => Some(Level::Forbid), + sym::warn => Some((Level::Warn, None)), + sym::deny => Some((Level::Deny, None)), + sym::forbid => Some((Level::Forbid, None)), _ => None, } } @@ -278,8 +282,8 @@ impl Level { Level::Deny => "-D", Level::Forbid => "-F", Level::Allow => "-A", - Level::ForceWarn(_) => "--force-warn", - Level::Expect(_) => { + Level::ForceWarn => "--force-warn", + Level::Expect => { unreachable!("the expect level does not have a commandline flag") } } @@ -287,17 +291,10 @@ impl Level { pub fn is_error(self) -> bool { match self { - Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn(_) => false, + Level::Allow | Level::Expect | Level::Warn | Level::ForceWarn => false, Level::Deny | Level::Forbid => true, } } - - pub fn get_expectation_id(&self) -> Option { - match self { - Level::Expect(id) | Level::ForceWarn(Some(id)) => Some(*id), - _ => None, - } - } } /// Specification of a single lint. diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 3d1f3b2cd4dd4..a662694ac38f2 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -241,7 +241,7 @@ fn main() { println!("cargo:rustc-link-lib=kstat"); } - if (target.starts_with("arm") && !target.contains("freebsd")) + if (target.starts_with("arm") && !target.contains("freebsd")) && !target.contains("ohos") || target.starts_with("mips-") || target.starts_with("mipsel-") || target.starts_with("powerpc-") @@ -255,6 +255,7 @@ fn main() { } else if target.contains("haiku") || target.contains("darwin") || (is_crossed && (target.contains("dragonfly") || target.contains("solaris"))) + || target.contains("cygwin") { println!("cargo:rustc-link-lib=z"); } else if target.contains("netbsd") { @@ -371,6 +372,7 @@ fn main() { || target.contains("freebsd") || target.contains("windows-gnullvm") || target.contains("aix") + || target.contains("ohos") { "c++" } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() { diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index 0471baa1f9cad..4695de8ea09a3 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -47,7 +47,6 @@ struct LLVMRustMCDCBranchParameters { int16_t ConditionIDs[2]; }; -#if LLVM_VERSION_GE(19, 0) static coverage::mcdc::BranchParameters fromRust(LLVMRustMCDCBranchParameters Params) { return coverage::mcdc::BranchParameters( @@ -59,7 +58,6 @@ fromRust(LLVMRustMCDCDecisionParameters Params) { return coverage::mcdc::DecisionParameters(Params.BitmapIdx, Params.NumConditions); } -#endif // Must match the layout of // `rustc_codegen_llvm::coverageinfo::ffi::CoverageSpan`. @@ -77,6 +75,13 @@ struct LLVMRustCoverageCodeRegion { LLVMRustCounter Count; }; +// Must match the layout of +// `rustc_codegen_llvm::coverageinfo::ffi::ExpansionRegion`. +struct LLVMRustCoverageExpansionRegion { + LLVMRustCoverageSpan Span; + uint32_t ExpandedFileID; +}; + // Must match the layout of // `rustc_codegen_llvm::coverageinfo::ffi::BranchRegion`. struct LLVMRustCoverageBranchRegion { @@ -151,6 +156,8 @@ extern "C" void LLVMRustCoverageWriteFunctionMappingsToBuffer( const unsigned *VirtualFileMappingIDs, size_t NumVirtualFileMappingIDs, const LLVMRustCounterExpression *RustExpressions, size_t NumExpressions, const LLVMRustCoverageCodeRegion *CodeRegions, size_t NumCodeRegions, + const LLVMRustCoverageExpansionRegion *ExpansionRegions, + size_t NumExpansionRegions, const LLVMRustCoverageBranchRegion *BranchRegions, size_t NumBranchRegions, const LLVMRustCoverageMCDCBranchRegion *MCDCBranchRegions, size_t NumMCDCBranchRegions, @@ -179,6 +186,13 @@ extern "C" void LLVMRustCoverageWriteFunctionMappingsToBuffer( Region.Span.ColumnStart, Region.Span.LineEnd, Region.Span.ColumnEnd)); } + // Expansion regions: + for (const auto &Region : ArrayRef(ExpansionRegions, NumExpansionRegions)) { + MappingRegions.push_back(coverage::CounterMappingRegion::makeExpansion( + Region.Span.FileID, Region.ExpandedFileID, Region.Span.LineStart, + Region.Span.ColumnStart, Region.Span.LineEnd, Region.Span.ColumnEnd)); + } + // Branch regions: for (const auto &Region : ArrayRef(BranchRegions, NumBranchRegions)) { MappingRegions.push_back(coverage::CounterMappingRegion::makeBranchRegion( @@ -187,7 +201,6 @@ extern "C" void LLVMRustCoverageWriteFunctionMappingsToBuffer( Region.Span.LineEnd, Region.Span.ColumnEnd)); } -#if LLVM_VERSION_GE(19, 0) // MC/DC branch regions: for (const auto &Region : ArrayRef(MCDCBranchRegions, NumMCDCBranchRegions)) { MappingRegions.push_back(coverage::CounterMappingRegion::makeBranchRegion( @@ -205,7 +218,6 @@ extern "C" void LLVMRustCoverageWriteFunctionMappingsToBuffer( Region.Span.LineStart, Region.Span.ColumnStart, Region.Span.LineEnd, Region.Span.ColumnEnd)); } -#endif // Write the converted expressions and mappings to a byte buffer. auto CoverageMappingWriter = coverage::CoverageMappingWriter( diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 6e607baebb5c2..d4a05fbbbc5d1 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -14,6 +14,7 @@ #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/Verifier.h" +#include "llvm/IRPrinter/IRPrintingPasses.h" #include "llvm/LTO/LTO.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/TargetRegistry.h" @@ -47,10 +48,7 @@ #include // Conditional includes prevent clang-format from fully sorting the list, -// so keep them separate. -#if LLVM_VERSION_GE(19, 0) -#include "llvm/Support/PGOOptions.h" -#endif +// so if any are needed, keep them separate down here. using namespace llvm; @@ -432,31 +430,15 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( } if (!strcmp("zlib", DebugInfoCompression) && llvm::compression::zlib::isAvailable()) { -#if LLVM_VERSION_GE(19, 0) Options.MCOptions.CompressDebugSections = DebugCompressionType::Zlib; -#else - Options.CompressDebugSections = DebugCompressionType::Zlib; -#endif } else if (!strcmp("zstd", DebugInfoCompression) && llvm::compression::zstd::isAvailable()) { -#if LLVM_VERSION_GE(19, 0) Options.MCOptions.CompressDebugSections = DebugCompressionType::Zstd; -#else - Options.CompressDebugSections = DebugCompressionType::Zstd; -#endif } else if (!strcmp("none", DebugInfoCompression)) { -#if LLVM_VERSION_GE(19, 0) Options.MCOptions.CompressDebugSections = DebugCompressionType::None; -#else - Options.CompressDebugSections = DebugCompressionType::None; -#endif } -#if LLVM_VERSION_GE(19, 0) Options.MCOptions.X86RelaxRelocations = RelaxELFRelocations; -#else - Options.RelaxELFRelocations = RelaxELFRelocations; -#endif Options.UseInitArray = UseInitArray; Options.EmulatedTLS = UseEmulatedTls; @@ -484,7 +466,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( if (ArgsCstrBuff != nullptr) { #if LLVM_VERSION_GE(20, 0) - int buffer_offset = 0; + size_t buffer_offset = 0; assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); auto Arg0 = std::string(ArgsCstrBuff); buffer_offset = Arg0.size() + 1; @@ -502,7 +484,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.MCOptions.Argv0 = Arg0; Options.MCOptions.CommandlineArgs = CommandlineArgs; #else - int buffer_offset = 0; + size_t buffer_offset = 0; assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); const size_t arg0_len = std::strlen(ArgsCstrBuff); @@ -511,13 +493,13 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( arg0[arg0_len] = '\0'; buffer_offset += arg0_len + 1; - const int num_cmd_arg_strings = std::count( + const size_t num_cmd_arg_strings = std::count( &ArgsCstrBuff[buffer_offset], &ArgsCstrBuff[ArgsCstrBuffLen], '\0'); std::string *cmd_arg_strings = new std::string[num_cmd_arg_strings]; - for (int i = 0; i < num_cmd_arg_strings; ++i) { + for (size_t i = 0; i < num_cmd_arg_strings; ++i) { assert(buffer_offset < ArgsCstrBuffLen); - const int len = std::strlen(ArgsCstrBuff + buffer_offset); + const size_t len = std::strlen(ArgsCstrBuff + buffer_offset); cmd_arg_strings[i] = std::string(&ArgsCstrBuff[buffer_offset], len); buffer_offset += len + 1; } @@ -530,8 +512,13 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( #endif } +#if LLVM_VERSION_GE(21, 0) + TargetMachine *TM = TheTarget->createTargetMachine(Trip, CPU, Feature, + Options, RM, CM, OptLevel); +#else TargetMachine *TM = TheTarget->createTargetMachine( Trip.getTriple(), CPU, Feature, Options, RM, CM, OptLevel); +#endif return wrap(TM); } @@ -722,7 +709,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( bool LintIR, LLVMRustThinLTOBuffer **ThinLTOBufferRef, bool EmitThinLTO, bool EmitThinLTOSummary, bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, bool DisableSimplifyLibCalls, - bool EmitLifetimeMarkers, bool RunEnzyme, + bool EmitLifetimeMarkers, bool RunEnzyme, bool PrintBeforeEnzyme, + bool PrintAfterEnzyme, bool PrintPasses, LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, const char *PGOUsePath, bool InstrumentCoverage, const char *InstrProfileOutput, const char *PGOSampleUsePath, @@ -753,34 +741,23 @@ extern "C" LLVMRustResult LLVMRustOptimize( auto FS = vfs::getRealFileSystem(); if (PGOGenPath) { assert(!PGOUsePath && !PGOSampleUsePath); - PGOOpt = PGOOptions(PGOGenPath, "", "", "", FS, PGOOptions::IRInstr, - PGOOptions::NoCSAction, -#if LLVM_VERSION_GE(19, 0) - PGOOptions::ColdFuncOpt::Default, -#endif - DebugInfoForProfiling); + PGOOpt = PGOOptions( + PGOGenPath, "", "", "", FS, PGOOptions::IRInstr, PGOOptions::NoCSAction, + PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } else if (PGOUsePath) { assert(!PGOSampleUsePath); - PGOOpt = PGOOptions(PGOUsePath, "", "", "", FS, PGOOptions::IRUse, - PGOOptions::NoCSAction, -#if LLVM_VERSION_GE(19, 0) - PGOOptions::ColdFuncOpt::Default, -#endif - DebugInfoForProfiling); + PGOOpt = PGOOptions( + PGOUsePath, "", "", "", FS, PGOOptions::IRUse, PGOOptions::NoCSAction, + PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } else if (PGOSampleUsePath) { - PGOOpt = PGOOptions(PGOSampleUsePath, "", "", "", FS, PGOOptions::SampleUse, - PGOOptions::NoCSAction, -#if LLVM_VERSION_GE(19, 0) - PGOOptions::ColdFuncOpt::Default, -#endif - DebugInfoForProfiling); + PGOOpt = + PGOOptions(PGOSampleUsePath, "", "", "", FS, PGOOptions::SampleUse, + PGOOptions::NoCSAction, PGOOptions::ColdFuncOpt::Default, + DebugInfoForProfiling); } else if (DebugInfoForProfiling) { - PGOOpt = PGOOptions("", "", "", "", FS, PGOOptions::NoAction, - PGOOptions::NoCSAction, -#if LLVM_VERSION_GE(19, 0) - PGOOptions::ColdFuncOpt::Default, -#endif - DebugInfoForProfiling); + PGOOpt = PGOOptions( + "", "", "", "", FS, PGOOptions::NoAction, PGOOptions::NoCSAction, + PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } auto PB = PassBuilder(TM, PTO, PGOOpt, &PIC); @@ -855,10 +832,15 @@ extern "C" LLVMRustResult LLVMRustOptimize( } if (LintIR) { - PipelineStartEPCallbacks.push_back( - [](ModulePassManager &MPM, OptimizationLevel Level) { - MPM.addPass(createModuleToFunctionPassAdaptor(LintPass())); - }); + PipelineStartEPCallbacks.push_back([](ModulePassManager &MPM, + OptimizationLevel Level) { +#if LLVM_VERSION_GE(21, 0) + MPM.addPass( + createModuleToFunctionPassAdaptor(LintPass(/*AbortOnError=*/true))); +#else + MPM.addPass(createModuleToFunctionPassAdaptor(LintPass())); +#endif + }); } if (InstrumentCoverage) { @@ -1073,14 +1055,38 @@ extern "C" LLVMRustResult LLVMRustOptimize( // now load "-enzyme" pass: #ifdef ENZYME if (RunEnzyme) { - registerEnzymeAndPassPipeline(PB, true); + + if (PrintBeforeEnzyme) { + // Handle the Rust flag `-Zautodiff=PrintModBefore`. + std::string Banner = "Module before EnzymeNewPM"; + MPM.addPass(PrintModulePass(outs(), Banner, true, false)); + } + + registerEnzymeAndPassPipeline(PB, false); if (auto Err = PB.parsePassPipeline(MPM, "enzyme")) { std::string ErrMsg = toString(std::move(Err)); LLVMRustSetLastError(ErrMsg.c_str()); return LLVMRustResult::Failure; } + + if (PrintAfterEnzyme) { + // Handle the Rust flag `-Zautodiff=PrintModAfter`. + std::string Banner = "Module after EnzymeNewPM"; + MPM.addPass(PrintModulePass(outs(), Banner, true, false)); + } } #endif + if (PrintPasses) { + // Print all passes from the PM: + std::string Pipeline; + raw_string_ostream SOS(Pipeline); + MPM.printPipeline(SOS, [&PIC](StringRef ClassName) { + auto PassName = PIC.getPassNameForClassName(ClassName); + return PassName.empty() ? ClassName : PassName; + }); + outs() << Pipeline; + outs() << "\n"; + } // Upgrade all calls to old intrinsics first. for (Module::iterator I = TheModule->begin(), E = TheModule->end(); I != E;) @@ -1364,7 +1370,12 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, size_t num_modules, // Convert the preserved symbols set from string to GUID, this is then needed // for internalization. for (size_t i = 0; i < num_symbols; i++) { +#if LLVM_VERSION_GE(21, 0) + auto GUID = + GlobalValue::getGUIDAssumingExternalLinkage(preserved_symbols[i]); +#else auto GUID = GlobalValue::getGUID(preserved_symbols[i]); +#endif Ret->GUIDPreservedSymbols.insert(GUID); } @@ -1682,12 +1693,21 @@ extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut, #endif // Based on the 'InProcessThinBackend' constructor in LLVM +#if LLVM_VERSION_GE(21, 0) + for (auto &Name : Data->Index.cfiFunctionDefs().symbols()) + CfiFunctionDefs.insert(GlobalValue::getGUIDAssumingExternalLinkage( + GlobalValue::dropLLVMManglingEscape(Name))); + for (auto &Name : Data->Index.cfiFunctionDecls().symbols()) + CfiFunctionDecls.insert(GlobalValue::getGUIDAssumingExternalLinkage( + GlobalValue::dropLLVMManglingEscape(Name))); +#else for (auto &Name : Data->Index.cfiFunctionDefs()) CfiFunctionDefs.insert( GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name))); for (auto &Name : Data->Index.cfiFunctionDecls()) CfiFunctionDecls.insert( GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name))); +#endif #if LLVM_VERSION_GE(20, 0) Key = llvm::computeLTOCacheKey(conf, Data->Index, ModId, ImportList, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index aea2a8dd09787..90aa9188c8300 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -152,8 +152,12 @@ extern "C" LLVMContextRef LLVMRustContextCreate(bool shouldDiscardNames) { } extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M, - const char *Triple) { - unwrap(M)->setTargetTriple(Triple::normalize(Triple)); + const char *Target) { +#if LLVM_VERSION_GE(21, 0) + unwrap(M)->setTargetTriple(Triple(Triple::normalize(Target))); +#else + unwrap(M)->setTargetTriple(Triple::normalize(Target)); +#endif } extern "C" void LLVMRustPrintPassTimings(RustStringRef OutBuf) { @@ -380,6 +384,12 @@ static inline void AddAttributes(T *t, unsigned Index, LLVMAttributeRef *Attrs, t->setAttributes(PALNew); } +extern "C" bool LLVMRustHasAttributeAtIndex(LLVMValueRef Fn, unsigned Index, + LLVMRustAttributeKind RustAttr) { + Function *F = unwrap(Fn); + return F->hasParamAttribute(Index, fromRust(RustAttr)); +} + extern "C" void LLVMRustAddFunctionAttributes(LLVMValueRef Fn, unsigned Index, LLVMAttributeRef *Attrs, size_t AttrsLen) { @@ -463,12 +473,8 @@ extern "C" LLVMAttributeRef LLVMRustCreateRangeAttribute(LLVMContextRef C, unsigned NumBits, const uint64_t LowerWords[], const uint64_t UpperWords[]) { -#if LLVM_VERSION_GE(19, 0) return LLVMCreateConstantRangeAttribute(C, Attribute::Range, NumBits, LowerWords, UpperWords); -#else - report_fatal_error("LLVM 19.0 is required for Range Attribute"); -#endif } // These values **must** match ffi::AllocKindFlags. @@ -616,31 +622,8 @@ extern "C" LLVMValueRef LLVMRustBuildAtomicStore(LLVMBuilderRef B, return wrap(SI); } -enum class LLVMRustAsmDialect { - Att, - Intel, -}; - -static InlineAsm::AsmDialect fromRust(LLVMRustAsmDialect Dialect) { - switch (Dialect) { - case LLVMRustAsmDialect::Att: - return InlineAsm::AD_ATT; - case LLVMRustAsmDialect::Intel: - return InlineAsm::AD_Intel; - default: - report_fatal_error("bad AsmDialect."); - } -} - -extern "C" LLVMValueRef -LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen, - char *Constraints, size_t ConstraintsLen, - LLVMBool HasSideEffects, LLVMBool IsAlignStack, - LLVMRustAsmDialect Dialect, LLVMBool CanThrow) { - return wrap(InlineAsm::get( - unwrap(Ty), StringRef(AsmString, AsmStringLen), - StringRef(Constraints, ConstraintsLen), HasSideEffects, IsAlignStack, - fromRust(Dialect), CanThrow)); +extern "C" uint64_t LLVMRustGetArrayNumElements(LLVMTypeRef Ty) { + return unwrap(Ty)->getArrayNumElements(); } extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, @@ -963,6 +946,27 @@ extern "C" LLVMMetadataRef LLVMRustDIGetInstMetadata(LLVMValueRef x) { return nullptr; } +extern "C" void +LLVMRustRemoveEnumAttributeAtIndex(LLVMValueRef F, size_t index, + LLVMRustAttributeKind RustAttr) { + LLVMRemoveEnumAttributeAtIndex(F, index, fromRust(RustAttr)); +} + +extern "C" bool LLVMRustHasFnAttribute(LLVMValueRef F, const char *Name, + size_t NameLen) { + if (auto *Fn = dyn_cast(unwrap(F))) { + return Fn->hasFnAttribute(StringRef(Name, NameLen)); + } + return false; +} + +extern "C" void LLVMRustRemoveFnAttribute(LLVMValueRef Fn, const char *Name, + size_t NameLen) { + if (auto *F = dyn_cast(unwrap(Fn))) { + F->removeFnAttr(StringRef(Name, NameLen)); + } +} + extern "C" void LLVMRustGlobalAddMetadata(LLVMValueRef Global, unsigned Kind, LLVMMetadataRef MD) { unwrap(Global)->addMetadata(Kind, *unwrap(MD)); @@ -1587,43 +1591,6 @@ extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, LLVMValueRef Dst, MaybeAlign(DstAlign), IsVolatile)); } -// Polyfill for `LLVMBuildCallBr`, which was added in LLVM 19. -// -// FIXME: Remove when Rust's minimum supported LLVM version reaches 19. -#if LLVM_VERSION_LT(19, 0) -DEFINE_SIMPLE_CONVERSION_FUNCTIONS(OperandBundleDef, LLVMOperandBundleRef) - -extern "C" LLVMValueRef -LLVMBuildCallBr(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, - LLVMBasicBlockRef DefaultDest, LLVMBasicBlockRef *IndirectDests, - unsigned NumIndirectDests, LLVMValueRef *Args, unsigned NumArgs, - LLVMOperandBundleRef *Bundles, unsigned NumBundles, - const char *Name) { - Value *Callee = unwrap(Fn); - FunctionType *FTy = unwrap(Ty); - - // FIXME: Is there a way around this? - std::vector IndirectDestsUnwrapped; - IndirectDestsUnwrapped.reserve(NumIndirectDests); - for (unsigned i = 0; i < NumIndirectDests; ++i) { - IndirectDestsUnwrapped.push_back(unwrap(IndirectDests[i])); - } - - // FIXME: Is there a way around this? - SmallVector OpBundles; - OpBundles.reserve(NumBundles); - for (unsigned i = 0; i < NumBundles; ++i) { - OpBundles.push_back(*unwrap(Bundles[i])); - } - - return wrap( - unwrap(B)->CreateCallBr(FTy, Callee, unwrap(DefaultDest), - ArrayRef(IndirectDestsUnwrapped), - ArrayRef(unwrap(Args), NumArgs), - ArrayRef(OpBundles), Name)); -} -#endif - extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, LLVMBasicBlockRef BB) { auto Point = unwrap(BB)->getFirstInsertionPt(); @@ -1767,24 +1734,6 @@ extern "C" LLVMValueRef LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS), unwrap(RHS))); } -#if LLVM_VERSION_LT(19, 0) -enum { - LLVMGEPFlagInBounds = (1 << 0), - LLVMGEPFlagNUSW = (1 << 1), - LLVMGEPFlagNUW = (1 << 2), -}; -extern "C" LLVMValueRef -LLVMBuildGEPWithNoWrapFlags(LLVMBuilderRef B, LLVMTypeRef Ty, - LLVMValueRef Pointer, LLVMValueRef *Indices, - unsigned NumIndices, const char *Name, - unsigned NoWrapFlags) { - if (NoWrapFlags & LLVMGEPFlagInBounds) - return LLVMBuildInBoundsGEP2(B, Ty, Pointer, Indices, NumIndices, Name); - else - return LLVMBuildGEP2(B, Ty, Pointer, Indices, NumIndices, Name); -} -#endif - // Transfers ownership of DiagnosticHandler unique_ptr to the caller. extern "C" DiagnosticHandler * LLVMRustContextGetDiagnosticHandler(LLVMContextRef C) { @@ -1852,11 +1801,7 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler( } } if (DiagnosticHandlerCallback) { -#if LLVM_VERSION_GE(19, 0) DiagnosticHandlerCallback(&DI, DiagnosticHandlerContext); -#else - DiagnosticHandlerCallback(DI, DiagnosticHandlerContext); -#endif return true; } return false; @@ -2004,21 +1949,3 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) { MD.NoHWAddress = true; GV.setSanitizerMetadata(MD); } - -// Operations on composite constants. -// These are clones of LLVM api functions that will become available in future -// releases. They can be removed once Rust's minimum supported LLVM version -// supports them. See https://github.com/rust-lang/rust/issues/121868 See -// https://llvm.org/doxygen/group__LLVMCCoreValueConstantComposite.html - -// FIXME: Remove when Rust's minimum supported LLVM version reaches 19. -// https://github.com/llvm/llvm-project/commit/e1405e4f71c899420ebf8262d5e9745598419df8 -#if LLVM_VERSION_LT(19, 0) -extern "C" LLVMValueRef LLVMConstStringInContext2(LLVMContextRef C, - const char *Str, - size_t Length, - bool DontNullTerminate) { - return wrap(ConstantDataArray::getString(*unwrap(C), StringRef(Str, Length), - !DontNullTerminate)); -} -#endif diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index eda9b2b1fc092..ed5edeef1617d 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -4,7 +4,6 @@ #![doc(rust_logo)] #![feature(extern_types)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use std::cell::RefCell; @@ -230,6 +229,7 @@ pub fn initialize_available_targets() { LLVMInitializeXtensaTargetInfo, LLVMInitializeXtensaTarget, LLVMInitializeXtensaTargetMC, + LLVMInitializeXtensaAsmPrinter, LLVMInitializeXtensaAsmParser ); init_target!( diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml index 30f6e9ba8053f..c673d51a1d456 100644 --- a/compiler/rustc_log/Cargo.toml +++ b/compiler/rustc_log/Cargo.toml @@ -11,11 +11,6 @@ tracing-subscriber = { version = "0.3.3", default-features = false, features = [ tracing-tree = "0.3.1" # tidy-alphabetical-end -[dev-dependencies] -# tidy-alphabetical-start -rustc_span = { path = "../rustc_span" } -# tidy-alphabetical-end - [features] # tidy-alphabetical-start max_level_info = ['tracing/max_level_info'] diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index d0ef82f4a6ce2..1bb502ca3d062 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -9,17 +9,12 @@ //! [dependencies] //! rustc_ast = { path = "../rust/compiler/rustc_ast" } //! rustc_log = { path = "../rust/compiler/rustc_log" } -//! rustc_span = { path = "../rust/compiler/rustc_span" } //! ``` //! //! ``` //! fn main() { //! rustc_log::init_logger(rustc_log::LoggerConfig::from_env("LOG")).unwrap(); -//! -//! let edition = rustc_span::edition::Edition::Edition2021; -//! rustc_span::create_session_globals_then(edition, None, || { -//! /* ... */ -//! }); +//! /* ... */ //! } //! ``` //! @@ -42,6 +37,7 @@ use std::env::{self, VarError}; use std::fmt::{self, Display}; use std::io::{self, IsTerminal}; +use tracing::dispatcher::SetGlobalDefaultError; use tracing_core::{Event, Subscriber}; use tracing_subscriber::filter::{Directive, EnvFilter, LevelFilter}; use tracing_subscriber::fmt::FmtContext; @@ -136,10 +132,10 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> { .without_time() .event_format(BacktraceFormatter { backtrace_target }); let subscriber = subscriber.with(fmt_layer); - tracing::subscriber::set_global_default(subscriber).unwrap(); + tracing::subscriber::set_global_default(subscriber)?; } Err(_) => { - tracing::subscriber::set_global_default(subscriber).unwrap(); + tracing::subscriber::set_global_default(subscriber)?; } }; @@ -185,6 +181,7 @@ pub enum Error { InvalidColorValue(String), NonUnicodeColorValue, InvalidWraptree(String), + AlreadyInit(SetGlobalDefaultError), } impl std::error::Error for Error {} @@ -204,6 +201,13 @@ impl Display for Error { formatter, "invalid log WRAPTREE value '{value}': expected a non-negative integer", ), + Error::AlreadyInit(tracing_error) => Display::fmt(tracing_error, formatter), } } } + +impl From for Error { + fn from(tracing_error: SetGlobalDefaultError) -> Self { + Error::AlreadyInit(tracing_error) + } +} diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 91398f1a9da9d..55228248188e5 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -55,8 +55,7 @@ use synstructure::Structure; /// /// See rustc dev guide for more examples on using the `#[derive(Diagnostic)]`: /// -pub(super) fn diagnostic_derive(mut s: Structure<'_>) -> TokenStream { - s.underscore_const(true); +pub(super) fn diagnostic_derive(s: Structure<'_>) -> TokenStream { DiagnosticDerive::new(s).into_tokens() } @@ -102,8 +101,7 @@ pub(super) fn diagnostic_derive(mut s: Structure<'_>) -> TokenStream { /// /// See rustc dev guide for more examples on using the `#[derive(LintDiagnostic)]`: /// -pub(super) fn lint_diagnostic_derive(mut s: Structure<'_>) -> TokenStream { - s.underscore_const(true); +pub(super) fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { LintDiagnosticDerive::new(s).into_tokens() } @@ -153,7 +151,6 @@ pub(super) fn lint_diagnostic_derive(mut s: Structure<'_>) -> TokenStream { /// /// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident }); /// ``` -pub(super) fn subdiagnostic_derive(mut s: Structure<'_>) -> TokenStream { - s.underscore_const(true); +pub(super) fn subdiagnostic_derive(s: Structure<'_>) -> TokenStream { SubdiagnosticDerive::new().into_tokens(s) } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 909083d5e8652..bc9516b2e0c67 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -20,14 +20,12 @@ use crate::diagnostics::utils::{ /// The central struct for constructing the `add_to_diag` method from an annotated struct. pub(crate) struct SubdiagnosticDerive { diag: syn::Ident, - f: syn::Ident, } impl SubdiagnosticDerive { pub(crate) fn new() -> Self { let diag = format_ident!("diag"); - let f = format_ident!("f"); - Self { diag, f } + Self { diag } } pub(crate) fn into_tokens(self, mut structure: Structure<'_>) -> TokenStream { @@ -86,19 +84,16 @@ impl SubdiagnosticDerive { }; let diag = &self.diag; - let f = &self.f; // FIXME(edition_2024): Fix the `keyword_idents_2024` lint to not trigger here? #[allow(keyword_idents_2024)] let ret = structure.gen_impl(quote! { gen impl rustc_errors::Subdiagnostic for @Self { - fn add_to_diag_with<__G, __F>( + fn add_to_diag<__G>( self, #diag: &mut rustc_errors::Diag<'_, __G>, - #f: &__F ) where __G: rustc_errors::EmissionGuarantee, - __F: rustc_errors::SubdiagMessageOp<__G>, { #implementation } @@ -384,11 +379,10 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { Ok(quote! {}) } "subdiagnostic" => { - let f = &self.parent.f; let diag = &self.parent.diag; let binding = &info.binding; self.has_subdiagnostic = true; - Ok(quote! { #binding.add_to_diag_with(#diag, #f); }) + Ok(quote! { #binding.add_to_diag(#diag); }) } _ => { let mut span_attrs = vec![]; @@ -531,12 +525,11 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let span_field = self.span_field.value_ref(); let diag = &self.parent.diag; - let f = &self.parent.f; let mut calls = TokenStream::new(); for (kind, slug, no_span) in kind_slugs { let message = format_ident!("__message"); calls.extend( - quote! { let #message = #f(#diag, crate::fluent_generated::#slug.into()); }, + quote! { let #message = #diag.eagerly_translate(crate::fluent_generated::#slug); }, ); let name = format_ident!( diff --git a/compiler/rustc_macros/src/hash_stable.rs b/compiler/rustc_macros/src/hash_stable.rs index 6b3210cad7be6..a6396ba687d11 100644 --- a/compiler/rustc_macros/src/hash_stable.rs +++ b/compiler/rustc_macros/src/hash_stable.rs @@ -74,8 +74,6 @@ fn hash_stable_derive_with_mode( HashStableMode::Generic | HashStableMode::NoContext => parse_quote!(__CTX), }; - s.underscore_const(true); - // no_context impl is able to derive by-field, which is closer to a perfect derive. s.add_bounds(match mode { HashStableMode::Normal | HashStableMode::Generic => synstructure::AddBounds::Generics, diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 34fc0f00320c0..81817018cb14d 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -1,12 +1,10 @@ // tidy-alphabetical-start #![allow(rustc::default_hash_types)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(never_type)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] #![feature(proc_macro_tracked_env)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use proc_macro::TokenStream; @@ -75,8 +73,8 @@ decl_derive!( hash_stable::hash_stable_no_context_derive ); -decl_derive!([Decodable_Generic] => serialize::decodable_generic_derive); -decl_derive!([Encodable_Generic] => serialize::encodable_generic_derive); +decl_derive!([Decodable_NoContext] => serialize::decodable_nocontext_derive); +decl_derive!([Encodable_NoContext] => serialize::encodable_nocontext_derive); decl_derive!([Decodable] => serialize::decodable_derive); decl_derive!([Encodable] => serialize::encodable_derive); decl_derive!([TyDecodable] => serialize::type_decodable_derive); diff --git a/compiler/rustc_macros/src/lift.rs b/compiler/rustc_macros/src/lift.rs index 341447f7e6f26..03ea396a42c75 100644 --- a/compiler/rustc_macros/src/lift.rs +++ b/compiler/rustc_macros/src/lift.rs @@ -4,7 +4,6 @@ use syn::parse_quote; pub(super) fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { s.add_bounds(synstructure::AddBounds::Generics); s.bind_with(|_| synstructure::BindStyle::Move); - s.underscore_const(true); let tcx: syn::Lifetime = parse_quote!('tcx); let newtcx: syn::GenericParam = parse_quote!('__lifted); diff --git a/compiler/rustc_macros/src/print_attribute.rs b/compiler/rustc_macros/src/print_attribute.rs index 3c6e30b851bf7..42d94e72ee942 100644 --- a/compiler/rustc_macros/src/print_attribute.rs +++ b/compiler/rustc_macros/src/print_attribute.rs @@ -16,12 +16,14 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok let name = field.ident.as_ref().unwrap(); let string_name = name.to_string(); disps.push(quote! { - if __printed_anything && #name.print_something() { - __p.word_space(","); + if #name.should_render() { + if __printed_anything { + __p.word_space(","); + } + __p.word(#string_name); + __p.word_space(":"); __printed_anything = true; } - __p.word(#string_name); - __p.word_space(":"); #name.print_attribute(__p); }); field_names.push(name); @@ -31,10 +33,11 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok quote! { {#(#field_names),*} }, quote! { __p.word(#string_name); - if true #(&& !#field_names.print_something())* { + if true #(&& !#field_names.should_render())* { return; } + __p.nbsp(); __p.word("{"); #(#disps)* __p.word("}"); @@ -48,8 +51,10 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok for idx in 0..fields_unnamed.unnamed.len() { let name = format_ident!("f{idx}"); disps.push(quote! { - if __printed_anything && #name.print_something() { - __p.word_space(","); + if #name.should_render() { + if __printed_anything { + __p.word_space(","); + } __printed_anything = true; } #name.print_attribute(__p); @@ -62,13 +67,13 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok quote! { __p.word(#string_name); - if true #(&& !#field_names.print_something())* { + if true #(&& !#field_names.should_render())* { return; } - __p.word("("); + __p.popen(); #(#disps)* - __p.word(")"); + __p.pclose(); }, quote! { true }, ) @@ -138,7 +143,7 @@ pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream { input.gen_impl(quote! { #[allow(unused)] gen impl PrintAttribute for @Self { - fn print_something(&self) -> bool { #printed } + fn should_render(&self) -> bool { #printed } fn print_attribute(&self, __p: &mut rustc_ast_pretty::pp::Printer) { #code } } }) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 62bf34ad5adce..2196f71299a53 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -51,6 +51,7 @@ impl Parse for Query { let key = Pat::parse_single(&arg_content)?; arg_content.parse::()?; let arg = arg_content.parse()?; + let _ = arg_content.parse::>()?; let result = input.parse()?; // Parse the query modifiers @@ -267,6 +268,18 @@ fn add_query_desc_cached_impl( ) { let Query { name, key, modifiers, .. } = &query; + // This dead code exists to instruct rust-analyzer about the link between the `rustc_queries` + // query names and the corresponding produced provider. The issue is that by nature of this + // macro producing a higher order macro that has all its token in the macro declaration we lose + // any meaningful spans, resulting in rust-analyzer being unable to make the connection between + // the query name and the corresponding providers field. The trick to fix this is to have + // `rustc_queries` emit a field access with the given name's span which allows it to succesfully + // show references / go to definition to the correspondig provider assignment which is usually + // the more interesting place. + let ra_hint = quote! { + let crate::query::Providers { #name: _, .. }; + }; + // Find out if we should cache the query on disk let cache = if let Some((args, expr)) = modifiers.cache.as_ref() { let tcx = args.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ }); @@ -277,6 +290,7 @@ fn add_query_desc_cached_impl( #[allow(unused_variables, unused_braces, rustc::pass_by_value)] #[inline] pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::query::queries::#name::Key<'tcx>) -> bool { + #ra_hint #expr } } @@ -286,6 +300,7 @@ fn add_query_desc_cached_impl( #[allow(rustc::pass_by_value)] #[inline] pub fn #name<'tcx>(_: TyCtxt<'tcx>, _: &crate::query::queries::#name::Key<'tcx>) -> bool { + #ra_hint false } } @@ -407,11 +422,23 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { } TokenStream::from(quote! { + /// Higher-order macro that invokes the specified macro with a prepared + /// list of all query signatures (including modifiers). + /// + /// This allows multiple simpler macros to each have access to the list + /// of queries. #[macro_export] - macro_rules! rustc_query_append { - ($macro:ident! $( [$($other:tt)*] )?) => { + macro_rules! rustc_with_all_queries { + ( + // The macro to invoke once, on all queries (plus extras). + $macro:ident! + + // Within [], an optional list of extra "query" signatures to + // pass to the given macro, in addition to the actual queries. + $( [$($extra_fake_queries:tt)*] )? + ) => { $macro! { - $( $($other)* )? + $( $($extra_fake_queries)* )? #query_stream } } diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 2c33a0ac0aafe..c7aaaf0da4679 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -6,18 +6,12 @@ use syn::spanned::Spanned; pub(super) fn type_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 }); + } let decoder_ty = quote! { __D }; - let bound = if s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { - quote! { > } - } else if s.ast().generics.type_params().any(|ty| ty.ident == "I") { - quote! { } - } else { - quote! {} - }; - - s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_type_ir::codec::TyDecoder #bound }); + s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_middle::ty::codec::TyDecoder<'tcx> }); s.add_bounds(synstructure::AddBounds::Fields); - s.underscore_const(true); decodable_body(s, decoder_ty) } @@ -31,7 +25,6 @@ pub(super) fn meta_decodable_derive( s.add_impl_generic(parse_quote! { '__a }); let decoder_ty = quote! { DecodeContext<'__a, 'tcx> }; s.add_bounds(synstructure::AddBounds::Generics); - s.underscore_const(true); decodable_body(s, decoder_ty) } @@ -40,24 +33,22 @@ pub(super) fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro let decoder_ty = quote! { __D }; s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_span::SpanDecoder }); s.add_bounds(synstructure::AddBounds::Generics); - s.underscore_const(true); decodable_body(s, decoder_ty) } -pub(super) fn decodable_generic_derive( +pub(super) fn decodable_nocontext_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); - s.underscore_const(true); + s.add_bounds(synstructure::AddBounds::Fields); decodable_body(s, decoder_ty) } fn decodable_body( - mut s: synstructure::Structure<'_>, + s: synstructure::Structure<'_>, decoder_ty: TokenStream, ) -> proc_macro2::TokenStream { if let syn::Data::Union(_) = s.ast().data { @@ -103,7 +94,6 @@ fn decodable_body( } } }; - s.underscore_const(true); s.bound_impl( quote!(::rustc_serialize::Decodable<#decoder_ty>), @@ -132,18 +122,12 @@ fn decode_field(field: &syn::Field) -> proc_macro2::TokenStream { pub(super) fn type_encodable_derive( mut s: synstructure::Structure<'_>, ) -> proc_macro2::TokenStream { - let bound = if s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { - quote! { > } - } else if s.ast().generics.type_params().any(|ty| ty.ident == "I") { - quote! { } - } else { - quote! {} - }; - let encoder_ty = quote! { __E }; - s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_type_ir::codec::TyEncoder #bound }); + if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { + s.add_impl_generic(parse_quote! { 'tcx }); + } + s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_middle::ty::codec::TyEncoder<'tcx> }); s.add_bounds(synstructure::AddBounds::Fields); - s.underscore_const(true); encodable_body(s, encoder_ty, false) } @@ -157,7 +141,6 @@ pub(super) fn meta_encodable_derive( s.add_impl_generic(parse_quote! { '__a }); let encoder_ty = quote! { EncodeContext<'__a, 'tcx> }; s.add_bounds(synstructure::AddBounds::Generics); - s.underscore_const(true); encodable_body(s, encoder_ty, true) } @@ -166,18 +149,16 @@ pub(super) fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro let encoder_ty = quote! { __E }; s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder }); s.add_bounds(synstructure::AddBounds::Generics); - s.underscore_const(true); encodable_body(s, encoder_ty, false) } -pub(super) fn encodable_generic_derive( +pub(super) fn encodable_nocontext_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); - s.underscore_const(true); + s.add_bounds(synstructure::AddBounds::Fields); encodable_body(s, encoder_ty, false) } @@ -191,7 +172,6 @@ fn encodable_body( panic!("cannot derive on union") } - s.underscore_const(true); s.bind_with(|binding| { // Handle the lack of a blanket reference impl. if let syn::Type::Reference(_) = binding.ast().ty { diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index 37200f62eb5a2..2b00b7dd27aaf 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -142,13 +142,13 @@ pub(super) fn symbols(input: TokenStream) -> TokenStream { output } -struct Preinterned { +struct Predefined { idx: u32, span_of_name: Span, } struct Entries { - map: HashMap, + map: HashMap, } impl Entries { @@ -163,7 +163,7 @@ impl Entries { prev.idx } else { let idx = self.len(); - self.map.insert(s.to_string(), Preinterned { idx, span_of_name: span }); + self.map.insert(s.to_string(), Predefined { idx, span_of_name: span }); idx } } @@ -295,10 +295,14 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { } let symbol_digits_base = entries.map["0"].idx; - let preinterned_symbols_count = entries.len(); + let predefined_symbols_count = entries.len(); let output = quote! { const SYMBOL_DIGITS_BASE: u32 = #symbol_digits_base; - const PREINTERNED_SYMBOLS_COUNT: u32 = #preinterned_symbols_count; + + /// The number of predefined symbols; this is the first index for + /// extra pre-interned symbols in an Interner created via + /// [`Interner::with_extra_symbols`]. + pub const PREDEFINED_SYMBOLS_COUNT: u32 = #predefined_symbols_count; #[doc(hidden)] #[allow(non_upper_case_globals)] @@ -315,10 +319,13 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { } impl Interner { - pub(crate) fn fresh() -> Self { - Interner::prefill(&[ - #prefill_stream - ]) + /// Creates an `Interner` with the predefined symbols from the `symbols!` macro and + /// any extra symbols provided by external drivers such as Clippy + pub(crate) fn with_extra_symbols(extra_symbols: &[&'static str]) -> Self { + Interner::prefill( + &[#prefill_stream], + extra_symbols, + ) } } }; diff --git a/compiler/rustc_macros/src/type_foldable.rs b/compiler/rustc_macros/src/type_foldable.rs index bc3b82c2893fa..91b747f18569d 100644 --- a/compiler/rustc_macros/src/type_foldable.rs +++ b/compiler/rustc_macros/src/type_foldable.rs @@ -6,53 +6,77 @@ pub(super) fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_m panic!("cannot derive on union") } - s.underscore_const(true); - if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { s.add_impl_generic(parse_quote! { 'tcx }); } s.add_bounds(synstructure::AddBounds::Generics); s.bind_with(|_| synstructure::BindStyle::Move); - let body_fold = s.each_variant(|vi| { + let try_body_fold = s.each_variant(|vi| { let bindings = vi.bindings(); vi.construct(|_, index| { let bind = &bindings[index]; - let mut fixed = false; - // retain value of fields with #[type_foldable(identity)] - bind.ast().attrs.iter().for_each(|x| { - if !x.path().is_ident("type_foldable") { - return; + if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") { + bind.to_token_stream() + } else { + quote! { + ::rustc_middle::ty::TypeFoldable::try_fold_with(#bind, __folder)? } - let _ = x.parse_nested_meta(|nested| { - if nested.path.is_ident("identity") { - fixed = true; - } - Ok(()) - }); - }); - - if fixed { + } + }) + }); + + let body_fold = s.each_variant(|vi| { + let bindings = vi.bindings(); + vi.construct(|_, index| { + let bind = &bindings[index]; + + // retain value of fields with #[type_foldable(identity)] + if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") { bind.to_token_stream() } else { quote! { - ::rustc_middle::ty::fold::TypeFoldable::try_fold_with(#bind, __folder)? + ::rustc_middle::ty::TypeFoldable::fold_with(#bind, __folder) } } }) }); s.bound_impl( - quote!(::rustc_middle::ty::fold::TypeFoldable<::rustc_middle::ty::TyCtxt<'tcx>>), + quote!(::rustc_middle::ty::TypeFoldable<::rustc_middle::ty::TyCtxt<'tcx>>), quote! { - fn try_fold_with<__F: ::rustc_middle::ty::fold::FallibleTypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>( + fn try_fold_with<__F: ::rustc_middle::ty::FallibleTypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>( self, __folder: &mut __F ) -> Result { - Ok(match self { #body_fold }) + Ok(match self { #try_body_fold }) + } + + fn fold_with<__F: ::rustc_middle::ty::TypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>( + self, + __folder: &mut __F + ) -> Self { + match self { #body_fold } } }, ) } + +fn has_ignore_attr(attrs: &[syn::Attribute], name: &'static str, meta: &'static str) -> bool { + let mut ignored = false; + attrs.iter().for_each(|attr| { + if !attr.path().is_ident(name) { + return; + } + let _ = attr.parse_nested_meta(|nested| { + if nested.path.is_ident(meta) { + ignored = true; + } + Ok(()) + }); + }); + + ignored +} diff --git a/compiler/rustc_macros/src/type_visitable.rs b/compiler/rustc_macros/src/type_visitable.rs index a7906d50d0f2d..f99c5113a6053 100644 --- a/compiler/rustc_macros/src/type_visitable.rs +++ b/compiler/rustc_macros/src/type_visitable.rs @@ -8,8 +8,6 @@ pub(super) fn type_visitable_derive( panic!("cannot derive on union") } - s.underscore_const(true); - // ignore fields with #[type_visitable(ignore)] s.filter(|bi| { let mut ignored = false; @@ -36,12 +34,12 @@ pub(super) fn type_visitable_derive( s.add_bounds(synstructure::AddBounds::Generics); let body_visit = s.each(|bind| { quote! { - match ::rustc_middle::ty::visit::VisitorResult::branch( - ::rustc_middle::ty::visit::TypeVisitable::visit_with(#bind, __visitor) + match ::rustc_middle::ty::VisitorResult::branch( + ::rustc_middle::ty::TypeVisitable::visit_with(#bind, __visitor) ) { ::core::ops::ControlFlow::Continue(()) => {}, ::core::ops::ControlFlow::Break(r) => { - return ::rustc_middle::ty::visit::VisitorResult::from_residual(r); + return ::rustc_middle::ty::VisitorResult::from_residual(r); }, } } @@ -49,14 +47,14 @@ pub(super) fn type_visitable_derive( s.bind_with(|_| synstructure::BindStyle::Move); s.bound_impl( - quote!(::rustc_middle::ty::visit::TypeVisitable<::rustc_middle::ty::TyCtxt<'tcx>>), + quote!(::rustc_middle::ty::TypeVisitable<::rustc_middle::ty::TyCtxt<'tcx>>), quote! { - fn visit_with<__V: ::rustc_middle::ty::visit::TypeVisitor<::rustc_middle::ty::TyCtxt<'tcx>>>( + fn visit_with<__V: ::rustc_middle::ty::TypeVisitor<::rustc_middle::ty::TyCtxt<'tcx>>>( &self, __visitor: &mut __V ) -> __V::Result { match *self { #body_visit } - <__V::Result as ::rustc_middle::ty::visit::VisitorResult>::output() + <__V::Result as ::rustc_middle::ty::VisitorResult>::output() } }, ) diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index a8821640f048e..cfe412e99d8d5 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -6,11 +6,11 @@ edition = "2024" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -libc = "0.2" libloading = "0.8.0" odht = { version = "0.3.1", features = ["nightly"] } rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -27,7 +27,11 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -rustc_type_ir = { path = "../rustc_type_ir" } -tempfile = "3.2" +tempfile = "3.7.1" tracing = "0.1" # tidy-alphabetical-end + +[target.'cfg(target_os = "aix")'.dependencies] +# tidy-alphabetical-start +libc = "0.2" +# tidy-alphabetical-end diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index df0a25712cca4..bccffe3924373 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -1,6 +1,10 @@ metadata_as_needed_compatibility = linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds +metadata_async_drop_types_in_dependency = + found async drop types in dependency `{$extern_crate}`, but async_drop feature is disabled for `{$local_crate}` + .help = if async drop type will be dropped in a crate without `feature(async_drop)`, sync Drop will be used + metadata_bad_panic_strategy = the linked panic runtime `{$runtime}` is not compiled with this crate's panic strategy `{$strategy}` @@ -97,6 +101,10 @@ metadata_found_staticlib = found staticlib `{$crate_name}` instead of rlib or dylib{$add_info} .help = please recompile that crate using --crate-type lib +metadata_full_metadata_not_found = + only metadata stub found for `{$flavor}` dependency `{$crate_name}` + please provide path to the corresponding .rmeta file with full metadata + metadata_global_alloc_required = no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait @@ -118,12 +126,23 @@ metadata_incompatible_rustc = metadata_incompatible_target_modifiers = mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}` - .note = `{$flag_name_prefixed}={$flag_local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$flag_extern_value}` in dependency `{$extern_crate}` + .note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$extern_value}` in dependency `{$extern_crate}` .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely - metadata_incompatible_target_modifiers_help_allow = if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch={$flag_name}` to silence this error -metadata_incompatible_target_modifiers_help_fix = set `{$flag_name_prefixed}={$flag_extern_value}` in this crate or `{$flag_name_prefixed}={$flag_local_value}` in `{$extern_crate}` +metadata_incompatible_target_modifiers_help_fix = set `{$flag_name_prefixed}={$extern_value}` in this crate or `{$flag_name_prefixed}={$local_value}` in `{$extern_crate}` + +metadata_incompatible_target_modifiers_help_fix_l_missed = set `{$flag_name_prefixed}={$extern_value}` in this crate or unset `{$flag_name_prefixed}` in `{$extern_crate}` + +metadata_incompatible_target_modifiers_help_fix_r_missed = unset `{$flag_name_prefixed}` in this crate or set `{$flag_name_prefixed}={$local_value}` in `{$extern_crate}` +metadata_incompatible_target_modifiers_l_missed = + mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}` + .note = unset `{$flag_name_prefixed}` in this crate is incompatible with `{$flag_name_prefixed}={$extern_value}` in dependency `{$extern_crate}` + .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely +metadata_incompatible_target_modifiers_r_missed = + mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}` + .note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with unset `{$flag_name_prefixed}` in dependency `{$extern_crate}` + .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely metadata_incompatible_wasm_link = `wasm_import_module` is incompatible with other arguments in `#[link]` attributes @@ -244,6 +263,9 @@ metadata_prev_alloc_error_handler = metadata_prev_global_alloc = previous global allocator defined here +metadata_raw_dylib_elf_unstable = + link kind `raw-dylib` is unstable on ELF platforms + metadata_raw_dylib_no_nul = link name must not contain NUL characters if link kind is `raw-dylib` diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 12503ffd1a67f..c7e9a2936f5dd 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -148,7 +148,7 @@ impl<'a> std::fmt::Debug for CrateDump<'a> { writeln!(fmt, " hash: {}", data.hash())?; writeln!(fmt, " reqd: {:?}", data.dep_kind())?; writeln!(fmt, " priv: {:?}", data.is_private_dep())?; - let CrateSource { dylib, rlib, rmeta } = data.source(); + let CrateSource { dylib, rlib, rmeta, sdylib_interface } = data.source(); if let Some(dylib) = dylib { writeln!(fmt, " dylib: {}", dylib.0.display())?; } @@ -158,6 +158,9 @@ impl<'a> std::fmt::Debug for CrateDump<'a> { if let Some(rmeta) = rmeta { writeln!(fmt, " rmeta: {}", rmeta.0.display())?; } + if let Some(sdylib_interface) = sdylib_interface { + writeln!(fmt, " sdylib interface: {}", sdylib_interface.0.display())?; + } } Ok(()) } @@ -340,7 +343,7 @@ impl CStore { } let level = tcx .lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID) - .0; + .level; if level != lint::Level::Allow { let unused_externs = self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::>(); @@ -358,30 +361,58 @@ impl CStore { ) { let span = krate.spans.inner_span.shrink_to_lo(); let allowed_flag_mismatches = &tcx.sess.opts.cg.unsafe_allow_abi_mismatch; - let name = tcx.crate_name(LOCAL_CRATE); + let local_crate = tcx.crate_name(LOCAL_CRATE); let tmod_extender = |tmod: &TargetModifier| (tmod.extend(), tmod.clone()); let report_diff = |prefix: &String, opt_name: &String, - flag_local_value: &String, - flag_extern_value: &String| { + flag_local_value: Option<&String>, + flag_extern_value: Option<&String>| { if allowed_flag_mismatches.contains(&opt_name) { return; } - tcx.dcx().emit_err(errors::IncompatibleTargetModifiers { - span, - extern_crate: data.name(), - local_crate: name, - flag_name: opt_name.clone(), - flag_name_prefixed: format!("-{}{}", prefix, opt_name), - flag_local_value: flag_local_value.to_string(), - flag_extern_value: flag_extern_value.to_string(), - }); + let extern_crate = data.name(); + let flag_name = opt_name.clone(); + let flag_name_prefixed = format!("-{}{}", prefix, opt_name); + + match (flag_local_value, flag_extern_value) { + (Some(local_value), Some(extern_value)) => { + tcx.dcx().emit_err(errors::IncompatibleTargetModifiers { + span, + extern_crate, + local_crate, + flag_name, + flag_name_prefixed, + local_value: local_value.to_string(), + extern_value: extern_value.to_string(), + }) + } + (None, Some(extern_value)) => { + tcx.dcx().emit_err(errors::IncompatibleTargetModifiersLMissed { + span, + extern_crate, + local_crate, + flag_name, + flag_name_prefixed, + extern_value: extern_value.to_string(), + }) + } + (Some(local_value), None) => { + tcx.dcx().emit_err(errors::IncompatibleTargetModifiersRMissed { + span, + extern_crate, + local_crate, + flag_name, + flag_name_prefixed, + local_value: local_value.to_string(), + }) + } + (None, None) => panic!("Incorrect target modifiers report_diff(None, None)"), + }; }; let mut it1 = mods.iter().map(tmod_extender); let mut it2 = dep_mods.iter().map(tmod_extender); let mut left_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None; let mut right_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None; - let no_val = "*".to_string(); loop { left_name_val = left_name_val.or_else(|| it1.next()); right_name_val = right_name_val.or_else(|| it2.next()); @@ -389,26 +420,31 @@ impl CStore { (Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) { cmp::Ordering::Equal => { if l.0.tech_value != r.0.tech_value { - report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &r.1.value_name); + report_diff( + &l.0.prefix, + &l.0.name, + Some(&l.1.value_name), + Some(&r.1.value_name), + ); } left_name_val = None; right_name_val = None; } cmp::Ordering::Greater => { - report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name); + report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name)); right_name_val = None; } cmp::Ordering::Less => { - report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val); + report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None); left_name_val = None; } }, (Some(l), None) => { - report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val); + report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None); left_name_val = None; } (None, Some(r)) => { - report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name); + report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name)); right_name_val = None; } (None, None) => break, @@ -437,6 +473,27 @@ impl CStore { } } + // Report about async drop types in dependency if async drop feature is disabled + pub fn report_incompatible_async_drop_feature(&self, tcx: TyCtxt<'_>, krate: &Crate) { + if tcx.features().async_drop() { + return; + } + for (_cnum, data) in self.iter_crate_data() { + if data.is_proc_macro_crate() { + continue; + } + if data.has_async_drops() { + let extern_crate = data.name(); + let local_crate = tcx.crate_name(LOCAL_CRATE); + tcx.dcx().emit_warn(errors::AsyncDropTypesInDependency { + span: krate.spans.inner_span.shrink_to_lo(), + extern_crate, + local_crate, + }); + } + } + } + pub fn new(metadata_loader: Box) -> CStore { CStore { metadata_loader, @@ -999,14 +1056,19 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } fn inject_allocator_crate(&mut self, krate: &ast::Crate) { - self.cstore.has_global_allocator = match &*global_allocator_spans(krate) { - [span1, span2, ..] => { - self.dcx().emit_err(errors::NoMultipleGlobalAlloc { span2: *span2, span1: *span1 }); - true - } - spans => !spans.is_empty(), - }; - self.cstore.has_alloc_error_handler = match &*alloc_error_handler_spans(krate) { + self.cstore.has_global_allocator = + match &*fn_spans(krate, Symbol::intern(&global_fn_name(sym::alloc))) { + [span1, span2, ..] => { + self.dcx() + .emit_err(errors::NoMultipleGlobalAlloc { span2: *span2, span1: *span1 }); + true + } + spans => !spans.is_empty(), + }; + self.cstore.has_alloc_error_handler = match &*fn_spans( + krate, + Symbol::intern(alloc_error_handler_name(AllocatorKind::Global)), + ) { [span1, span2, ..] => { self.dcx() .emit_err(errors::NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 }); @@ -1277,17 +1339,14 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { definitions: &Definitions, ) -> Option { match item.kind { - ast::ItemKind::ExternCrate(orig_name) => { - debug!( - "resolving extern crate stmt. ident: {} orig_name: {:?}", - item.ident, orig_name - ); + ast::ItemKind::ExternCrate(orig_name, ident) => { + debug!("resolving extern crate stmt. ident: {} orig_name: {:?}", ident, orig_name); let name = match orig_name { Some(orig_name) => { validate_crate_name(self.sess, orig_name, Some(item.span)); orig_name } - None => item.ident.name, + None => ident.name, }; let dep_kind = if attr::contains_name(&item.attrs, sym::no_link) { CrateDepKind::MacrosOnly @@ -1335,36 +1394,15 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } } -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(&global_fn_name(sym::alloc)); - let mut f = Finder { name, spans: Vec::new() }; - visit::walk_crate(&mut f, krate); - f.spans -} - -fn alloc_error_handler_spans(krate: &ast::Crate) -> Vec { +fn fn_spans(krate: &ast::Crate, name: Symbol) -> 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 + if let Some(ident) = item.kind.ident() + && ident.name == self.name && attr::contains_name(&item.attrs, sym::rustc_std_internal_symbol) { self.spans.push(item.span); @@ -1373,7 +1411,6 @@ fn alloc_error_handler_spans(krate: &ast::Crate) -> Vec { } } - let name = Symbol::intern(alloc_error_handler_name(AllocatorKind::Global)); let mut f = Finder { 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 index 582c2215d92e1..fcae33c73c9c0 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -84,49 +84,46 @@ pub(crate) fn calculate(tcx: TyCtxt<'_>) -> Dependencies { fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { let sess = &tcx.sess; - if !sess.opts.output_types.should_codegen() { + if !sess.opts.output_types.should_link() { return IndexVec::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 and staticlibs 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 or staticlib. For staticlibs we use - // `-Z staticlib-prefer-dynamic` for now. This may be merged into - // `-C prefer-dynamic` in the future. - CrateType::Dylib | CrateType::Cdylib => { - if sess.opts.cg.prefer_dynamic { - Linkage::Dynamic - } else { - Linkage::Static + 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 and staticlibs 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 or staticlib. For staticlibs we use + // `-Z staticlib-prefer-dynamic` for now. This may be merged into + // `-C prefer-dynamic` in the future. + CrateType::Dylib | CrateType::Cdylib | CrateType::Sdylib => { + if sess.opts.cg.prefer_dynamic { Linkage::Dynamic } else { Linkage::Static } } - } - CrateType::Staticlib => { - if sess.opts.unstable_opts.staticlib_prefer_dynamic { - Linkage::Dynamic - } else { - Linkage::Static + CrateType::Staticlib => { + if sess.opts.unstable_opts.staticlib_prefer_dynamic { + Linkage::Dynamic + } else { + Linkage::Static + } } - } - // 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, + // 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, + // 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, - }; + // 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, + }; let mut unavailable_as_static = Vec::new(); @@ -165,7 +162,9 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { let all_dylibs = || { tcx.crates(()).iter().filter(|&&cnum| { - !tcx.dep_kind(cnum).macros_only() && tcx.used_crate_source(cnum).dylib.is_some() + !tcx.dep_kind(cnum).macros_only() + && (tcx.used_crate_source(cnum).dylib.is_some() + || tcx.used_crate_source(cnum).sdylib_interface.is_some()) }) }; @@ -273,7 +272,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { match *kind { Linkage::NotLinked | Linkage::IncludedFromDylib => {} Linkage::Static if src.rlib.is_some() => continue, - Linkage::Dynamic if src.dylib.is_some() => continue, + Linkage::Dynamic if src.dylib.is_some() || src.sdylib_interface.is_some() => continue, kind => { let kind = match kind { Linkage::Static => "rlib", diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index 2ad6389c0b4ac..16f59793e6326 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -525,6 +525,15 @@ impl Diagnostic<'_, G> for MultipleCandidates { } } +#[derive(Diagnostic)] +#[diag(metadata_full_metadata_not_found)] +pub(crate) struct FullMetadataNotFound { + #[primary_span] + pub span: Span, + pub flavor: CrateFlavor, + pub crate_name: Symbol, +} + #[derive(Diagnostic)] #[diag(metadata_symbol_conflicts_current, code = E0519)] pub struct SymbolConflictsCurrent { @@ -759,8 +768,40 @@ pub struct IncompatibleTargetModifiers { pub local_crate: Symbol, pub flag_name: String, pub flag_name_prefixed: String, - pub flag_local_value: String, - pub flag_extern_value: String, + pub local_value: String, + pub extern_value: String, +} + +#[derive(Diagnostic)] +#[diag(metadata_incompatible_target_modifiers_l_missed)] +#[help] +#[note] +#[help(metadata_incompatible_target_modifiers_help_fix_l_missed)] +#[help(metadata_incompatible_target_modifiers_help_allow)] +pub struct IncompatibleTargetModifiersLMissed { + #[primary_span] + pub span: Span, + pub extern_crate: Symbol, + pub local_crate: Symbol, + pub flag_name: String, + pub flag_name_prefixed: String, + pub extern_value: String, +} + +#[derive(Diagnostic)] +#[diag(metadata_incompatible_target_modifiers_r_missed)] +#[help] +#[note] +#[help(metadata_incompatible_target_modifiers_help_fix_r_missed)] +#[help(metadata_incompatible_target_modifiers_help_allow)] +pub struct IncompatibleTargetModifiersRMissed { + #[primary_span] + pub span: Span, + pub extern_crate: Symbol, + pub local_crate: Symbol, + pub flag_name: String, + pub flag_name_prefixed: String, + pub local_value: String, } #[derive(Diagnostic)] @@ -770,3 +811,13 @@ pub struct UnknownTargetModifierUnsafeAllowed { pub span: Span, pub flag_name: String, } + +#[derive(Diagnostic)] +#[diag(metadata_async_drop_types_in_dependency)] +#[help] +pub struct AsyncDropTypesInDependency { + #[primary_span] + pub span: Span, + pub extern_crate: Symbol, + pub local_crate: Symbol, +} diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index 4450d050c8e1f..e57534b847ef0 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -2,11 +2,11 @@ use std::path::{Path, PathBuf}; use std::{fs, io}; use rustc_data_structures::temp_dir::MaybeTempDir; +use rustc_fs_util::TempDirBuilder; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{OutFileName, OutputType}; +use rustc_session::config::{CrateType, OutFileName, OutputType}; use rustc_session::output::filename_for_metadata; use rustc_session::{MetadataKind, Session}; -use tempfile::Builder as TempFileBuilder; use crate::errors::{ BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile, @@ -45,12 +45,19 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { // 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() + let metadata_tmpdir = TempDirBuilder::new() .prefix("rmeta") .tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new(""))) .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailedCreateTempdir { err })); let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps); - let metadata_filename = metadata_tmpdir.as_ref().join(METADATA_FILENAME); + let metadata_filename = metadata_tmpdir.as_ref().join("full.rmeta"); + let metadata_stub_filename = if !tcx.sess.opts.unstable_opts.embed_metadata + && !tcx.crate_types().contains(&CrateType::ProcMacro) + { + Some(metadata_tmpdir.as_ref().join("stub.rmeta")) + } else { + None + }; // Always create a file at `metadata_filename`, even if we have nothing to write to it. // This simplifies the creation of the output `out_filename` when requested. @@ -60,9 +67,15 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { std::fs::File::create(&metadata_filename).unwrap_or_else(|err| { tcx.dcx().emit_fatal(FailedCreateFile { filename: &metadata_filename, err }); }); + if let Some(metadata_stub_filename) = &metadata_stub_filename { + std::fs::File::create(metadata_stub_filename).unwrap_or_else(|err| { + tcx.dcx() + .emit_fatal(FailedCreateFile { filename: &metadata_stub_filename, err }); + }); + } } MetadataKind::Uncompressed | MetadataKind::Compressed => { - encode_metadata(tcx, &metadata_filename); + encode_metadata(tcx, &metadata_filename, metadata_stub_filename.as_deref()) } }; @@ -100,9 +113,10 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { // Load metadata back to memory: codegen may need to include it in object files. let metadata = - EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| { - tcx.dcx().emit_fatal(FailedCreateEncodedMetadata { err }); - }); + EncodedMetadata::from_path(metadata_filename, metadata_stub_filename, metadata_tmpdir) + .unwrap_or_else(|err| { + tcx.dcx().emit_fatal(FailedCreateEncodedMetadata { err }); + }); let need_metadata_module = metadata_kind == MetadataKind::Compressed; diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index ebcc0efd5a67a..97b67140fa2d4 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -8,14 +8,12 @@ #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(iter_from_coroutine)] -#![feature(let_chains)] #![feature(macro_metavar_expr)] #![feature(min_specialization)] #![feature(never_type)] #![feature(proc_macro_internals)] #![feature(rustdoc_internals)] #![feature(trusted_len)] -#![warn(unreachable_pub)] // tidy-alphabetical-end extern crate proc_macro; @@ -35,8 +33,8 @@ pub mod locator; pub use creader::{DylibError, load_symbol_from_dylib}; pub use fs::{METADATA_FILENAME, emit_wrapper_file}; pub use native_libs::{ - find_native_static_library, try_find_native_dynamic_library, try_find_native_static_library, - walk_native_lib_search_dirs, + NativeLibSearchFallback, find_native_static_library, try_find_native_dynamic_library, + try_find_native_static_library, walk_native_lib_search_dirs, }; pub use rmeta::{EncodedMetadata, METADATA_HEADER, encode_metadata, rendered_const}; diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index d5dd5059aacc6..10123cb9a9ddb 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -220,7 +220,7 @@ use std::{cmp, fmt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::memmap::Mmap; -use rustc_data_structures::owned_slice::slice_owned; +use rustc_data_structures::owned_slice::{OwnedSlice, slice_owned}; use rustc_data_structures::svh::Svh; use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_fs_util::try_canonicalize; @@ -231,6 +231,7 @@ use rustc_session::search_paths::PathKind; use rustc_session::utils::CanonicalizedPath; use rustc_span::{Span, Symbol}; use rustc_target::spec::{Target, TargetTuple}; +use tempfile::Builder as TempFileBuilder; use tracing::{debug, info}; use crate::creader::{Library, MetadataLoader}; @@ -277,6 +278,7 @@ pub(crate) enum CrateFlavor { Rlib, Rmeta, Dylib, + SDylib, } impl fmt::Display for CrateFlavor { @@ -285,6 +287,7 @@ impl fmt::Display for CrateFlavor { CrateFlavor::Rlib => "rlib", CrateFlavor::Rmeta => "rmeta", CrateFlavor::Dylib => "dylib", + CrateFlavor::SDylib => "sdylib", }) } } @@ -295,6 +298,7 @@ impl IntoDiagArg for CrateFlavor { CrateFlavor::Rlib => DiagArgValue::Str(Cow::Borrowed("rlib")), CrateFlavor::Rmeta => DiagArgValue::Str(Cow::Borrowed("rmeta")), CrateFlavor::Dylib => DiagArgValue::Str(Cow::Borrowed("dylib")), + CrateFlavor::SDylib => DiagArgValue::Str(Cow::Borrowed("sdylib")), } } } @@ -379,14 +383,18 @@ impl<'a> CrateLocator<'a> { &format!("{}{}{}", self.target.dll_prefix, self.crate_name, extra_prefix); let staticlib_prefix = &format!("{}{}{}", self.target.staticlib_prefix, self.crate_name, extra_prefix); + let interface_prefix = rmeta_prefix; let rmeta_suffix = ".rmeta"; let rlib_suffix = ".rlib"; let dylib_suffix = &self.target.dll_suffix; let staticlib_suffix = &self.target.staticlib_suffix; + let interface_suffix = ".rs"; - let mut candidates: FxIndexMap<_, (FxIndexMap<_, _>, FxIndexMap<_, _>, FxIndexMap<_, _>)> = - Default::default(); + let mut candidates: FxIndexMap< + _, + (FxIndexMap<_, _>, FxIndexMap<_, _>, FxIndexMap<_, _>, FxIndexMap<_, _>), + > = Default::default(); // First, find all possible candidate rlibs and dylibs purely based on // the name of the files themselves. We're trying to match against an @@ -417,6 +425,7 @@ impl<'a> CrateLocator<'a> { (rlib_prefix.as_str(), rlib_suffix, CrateFlavor::Rlib), (rmeta_prefix.as_str(), rmeta_suffix, CrateFlavor::Rmeta), (dylib_prefix, dylib_suffix, CrateFlavor::Dylib), + (interface_prefix, interface_suffix, CrateFlavor::SDylib), ] { if prefix == staticlib_prefix && suffix == staticlib_suffix { should_check_staticlibs = false; @@ -425,18 +434,28 @@ impl<'a> CrateLocator<'a> { for (hash, spf) in matches { info!("lib candidate: {}", spf.path.display()); - let (rlibs, rmetas, dylibs) = + let (rlibs, rmetas, dylibs, interfaces) = candidates.entry(hash.to_string()).or_default(); - let path = - try_canonicalize(&spf.path).unwrap_or_else(|_| spf.path.to_path_buf()); - if seen_paths.contains(&path) { - continue; - }; - seen_paths.insert(path.clone()); + { + // As a perforamnce optimisation we canonicalize the path and skip + // ones we've already seeen. This allows us to ignore crates + // we know are exactual equal to ones we've already found. + // Going to the same crate through different symlinks does not change the result. + let path = try_canonicalize(&spf.path) + .unwrap_or_else(|_| spf.path.to_path_buf()); + if seen_paths.contains(&path) { + continue; + }; + seen_paths.insert(path); + } + // Use the original path (potentially with unresolved symlinks), + // filesystem code should not care, but this is nicer for diagnostics. + let path = spf.path.to_path_buf(); match kind { CrateFlavor::Rlib => rlibs.insert(path, search_path.kind), CrateFlavor::Rmeta => rmetas.insert(path, search_path.kind), CrateFlavor::Dylib => dylibs.insert(path, search_path.kind), + CrateFlavor::SDylib => interfaces.insert(path, search_path.kind), }; } } @@ -463,8 +482,8 @@ impl<'a> CrateLocator<'a> { // libraries corresponds to the crate id and hash criteria that this // search is being performed for. let mut libraries = FxIndexMap::default(); - for (_hash, (rlibs, rmetas, dylibs)) in candidates { - if let Some((svh, lib)) = self.extract_lib(rlibs, rmetas, dylibs)? { + for (_hash, (rlibs, rmetas, dylibs, interfaces)) in candidates { + if let Some((svh, lib)) = self.extract_lib(rlibs, rmetas, dylibs, interfaces)? { libraries.insert(svh, lib); } } @@ -499,6 +518,7 @@ impl<'a> CrateLocator<'a> { rlibs: FxIndexMap, rmetas: FxIndexMap, dylibs: FxIndexMap, + interfaces: FxIndexMap, ) -> Result, CrateError> { let mut slot = None; // Order here matters, rmeta should come first. @@ -506,12 +526,17 @@ impl<'a> CrateLocator<'a> { // Make sure there's at most one rlib and at most one dylib. // // See comment in `extract_one` below. - let source = CrateSource { - rmeta: self.extract_one(rmetas, CrateFlavor::Rmeta, &mut slot)?, - rlib: self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot)?, - dylib: self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot)?, - }; - Ok(slot.map(|(svh, metadata, _)| (svh, Library { source, metadata }))) + let rmeta = self.extract_one(rmetas, CrateFlavor::Rmeta, &mut slot)?; + let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot)?; + let sdylib_interface = self.extract_one(interfaces, CrateFlavor::SDylib, &mut slot)?; + let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot)?; + + if sdylib_interface.is_some() && dylib.is_none() { + return Err(CrateError::FullMetadataNotFound(self.crate_name, CrateFlavor::SDylib)); + } + + let source = CrateSource { rmeta, rlib, dylib, sdylib_interface }; + Ok(slot.map(|(svh, metadata, _, _)| (svh, Library { source, metadata }))) } fn needs_crate_flavor(&self, flavor: CrateFlavor) -> bool { @@ -541,7 +566,7 @@ impl<'a> CrateLocator<'a> { &mut self, m: FxIndexMap, flavor: CrateFlavor, - slot: &mut Option<(Svh, MetadataBlob, PathBuf)>, + slot: &mut Option<(Svh, MetadataBlob, PathBuf, CrateFlavor)>, ) -> Result, CrateError> { // If we are producing an rlib, and we've already loaded metadata, then // we should not attempt to discover further crate sources (unless we're @@ -577,6 +602,7 @@ impl<'a> CrateLocator<'a> { &lib, self.metadata_loader, self.cfg_version, + Some(self.crate_name), ) { Ok(blob) => { if let Some(h) = self.crate_matches(&blob, &lib) { @@ -601,6 +627,11 @@ impl<'a> CrateLocator<'a> { } Err(MetadataError::LoadFailure(err)) => { info!("no metadata found: {}", err); + // Metadata was loaded from interface file earlier. + if let Some((.., CrateFlavor::SDylib)) = slot { + ret = Some((lib, kind)); + continue; + } // The file was present and created by the same compiler version, but we // couldn't load it for some reason. Give a hard error instead of silently // ignoring it, but only if we would have given an error anyway. @@ -654,7 +685,24 @@ impl<'a> CrateLocator<'a> { continue; } } - *slot = Some((hash, metadata, lib.clone())); + + // We error eagerly here. If we're locating a rlib, then in theory the full metadata + // could still be in a (later resolved) dylib. In practice, if the rlib and dylib + // were produced in a way where one has full metadata and the other hasn't, it would + // mean that they were compiled using different compiler flags and probably also have + // a different SVH value. + if metadata.get_header().is_stub { + // `is_stub` should never be true for .rmeta files. + assert_ne!(flavor, CrateFlavor::Rmeta); + + // Because rmeta files are resolved before rlib/dylib files, if this is a stub and + // we haven't found a slot already, it means that the full metadata is missing. + if slot.is_none() { + return Err(CrateError::FullMetadataNotFound(self.crate_name, flavor)); + } + } else { + *slot = Some((hash, metadata, lib.clone(), flavor)); + } ret = Some((lib, kind)); } @@ -710,6 +758,7 @@ impl<'a> CrateLocator<'a> { let mut rlibs = FxIndexMap::default(); let mut rmetas = FxIndexMap::default(); let mut dylibs = FxIndexMap::default(); + let mut sdylib_interfaces = FxIndexMap::default(); for loc in &self.exact_paths { let loc_canon = loc.canonicalized(); let loc_orig = loc.original(); @@ -728,41 +777,33 @@ impl<'a> CrateLocator<'a> { let Some(file) = loc_orig.file_name().and_then(|s| s.to_str()) else { return Err(CrateError::ExternLocationNotFile(self.crate_name, loc_orig.clone())); }; - // FnMut cannot return reference to captured value, so references - // must be taken outside the closure. - let rlibs = &mut rlibs; - let rmetas = &mut rmetas; - let dylibs = &mut dylibs; - let type_via_filename = (|| { - if file.starts_with("lib") { - if file.ends_with(".rlib") { - return Some(rlibs); - } - if file.ends_with(".rmeta") { - return Some(rmetas); - } - } - let dll_prefix = self.target.dll_prefix.as_ref(); - let dll_suffix = self.target.dll_suffix.as_ref(); - if file.starts_with(dll_prefix) && file.ends_with(dll_suffix) { - return Some(dylibs); + if file.starts_with("lib") { + if file.ends_with(".rlib") { + rlibs.insert(loc_canon.clone(), PathKind::ExternFlag); + continue; } - None - })(); - match type_via_filename { - Some(type_via_filename) => { - type_via_filename.insert(loc_canon.clone(), PathKind::ExternFlag); + if file.ends_with(".rmeta") { + rmetas.insert(loc_canon.clone(), PathKind::ExternFlag); + continue; } - None => { - self.crate_rejections - .via_filename - .push(CrateMismatch { path: loc_orig.clone(), got: String::new() }); + if file.ends_with(".rs") { + sdylib_interfaces.insert(loc_canon.clone(), PathKind::ExternFlag); } } + let dll_prefix = self.target.dll_prefix.as_ref(); + let dll_suffix = self.target.dll_suffix.as_ref(); + if file.starts_with(dll_prefix) && file.ends_with(dll_suffix) { + dylibs.insert(loc_canon.clone(), PathKind::ExternFlag); + continue; + } + self.crate_rejections + .via_filename + .push(CrateMismatch { path: loc_orig.clone(), got: String::new() }); } // Extract the dylib/rlib/rmeta triple. - self.extract_lib(rlibs, rmetas, dylibs).map(|opt| opt.map(|(_, lib)| lib)) + self.extract_lib(rlibs, rmetas, dylibs, sdylib_interfaces) + .map(|opt| opt.map(|(_, lib)| lib)) } pub(crate) fn into_error(self, dep_root: Option) -> CrateError { @@ -783,6 +824,7 @@ fn get_metadata_section<'p>( filename: &'p Path, loader: &dyn MetadataLoader, cfg_version: &'static str, + crate_name: Option, ) -> Result> { if !filename.exists() { return Err(MetadataError::NotPresent(filename)); @@ -791,6 +833,55 @@ fn get_metadata_section<'p>( CrateFlavor::Rlib => { loader.get_rlib_metadata(target, filename).map_err(MetadataError::LoadFailure)? } + CrateFlavor::SDylib => { + let compiler = std::env::current_exe().map_err(|_err| { + MetadataError::LoadFailure( + "couldn't obtain current compiler binary when loading sdylib interface" + .to_string(), + ) + })?; + + let tmp_path = match TempFileBuilder::new().prefix("rustc").tempdir() { + Ok(tmp_path) => tmp_path, + Err(error) => { + return Err(MetadataError::LoadFailure(format!( + "couldn't create a temp dir: {}", + error + ))); + } + }; + + let crate_name = crate_name.unwrap(); + debug!("compiling {}", filename.display()); + // FIXME: This will need to be done either within the current compiler session or + // as a separate compiler session in the same process. + let res = std::process::Command::new(compiler) + .arg(&filename) + .arg("--emit=metadata") + .arg(format!("--crate-name={}", crate_name)) + .arg(format!("--out-dir={}", tmp_path.path().display())) + .arg("-Zbuild-sdylib-interface") + .output() + .map_err(|err| { + MetadataError::LoadFailure(format!("couldn't compile interface: {}", err)) + })?; + + if !res.status.success() { + return Err(MetadataError::LoadFailure(format!( + "couldn't compile interface: {}", + std::str::from_utf8(&res.stderr).unwrap_or_default() + ))); + } + + // Load interface metadata instead of crate metadata. + let interface_metadata_name = format!("lib{}.rmeta", crate_name); + let rmeta_file = tmp_path.path().join(interface_metadata_name); + debug!("loading interface metadata from {}", rmeta_file.display()); + let rmeta = get_rmeta_metadata_section(&rmeta_file)?; + let _ = std::fs::remove_file(rmeta_file); + + rmeta + } CrateFlavor::Dylib => { let buf = loader.get_dylib_metadata(target, filename).map_err(MetadataError::LoadFailure)?; @@ -820,24 +911,7 @@ fn get_metadata_section<'p>( // Header is okay -> inflate the actual metadata buf.slice(|buf| &buf[data_start..(data_start + metadata_len)]) } - CrateFlavor::Rmeta => { - // mmap the file, because only a small fraction of it is read. - let file = std::fs::File::open(filename).map_err(|_| { - MetadataError::LoadFailure(format!( - "failed to open rmeta metadata: '{}'", - filename.display() - )) - })?; - let mmap = unsafe { Mmap::map(file) }; - let mmap = mmap.map_err(|_| { - MetadataError::LoadFailure(format!( - "failed to mmap rmeta metadata: '{}'", - filename.display() - )) - })?; - - slice_owned(mmap, Deref::deref) - } + CrateFlavor::Rmeta => get_rmeta_metadata_section(filename)?, }; let Ok(blob) = MetadataBlob::new(raw_bytes) else { return Err(MetadataError::LoadFailure(format!( @@ -863,6 +937,25 @@ fn get_metadata_section<'p>( } } +fn get_rmeta_metadata_section<'a, 'p>(filename: &'p Path) -> Result> { + // mmap the file, because only a small fraction of it is read. + let file = std::fs::File::open(filename).map_err(|_| { + MetadataError::LoadFailure(format!( + "failed to open rmeta metadata: '{}'", + filename.display() + )) + })?; + let mmap = unsafe { Mmap::map(file) }; + let mmap = mmap.map_err(|_| { + MetadataError::LoadFailure(format!( + "failed to mmap rmeta metadata: '{}'", + filename.display() + )) + })?; + + Ok(slice_owned(mmap, Deref::deref)) +} + /// A diagnostic function for dumping crate metadata to an output stream. pub fn list_file_metadata( target: &Target, @@ -873,7 +966,7 @@ pub fn list_file_metadata( cfg_version: &'static str, ) -> IoResult<()> { let flavor = get_flavor_from_path(path); - match get_metadata_section(target, flavor, path, metadata_loader, cfg_version) { + match get_metadata_section(target, flavor, path, metadata_loader, cfg_version, None) { Ok(metadata) => metadata.list_crate_metadata(out, ls_kinds), Err(msg) => write!(out, "{msg}\n"), } @@ -928,6 +1021,7 @@ pub(crate) enum CrateError { ExternLocationNotExist(Symbol, PathBuf), ExternLocationNotFile(Symbol, PathBuf), MultipleCandidates(Symbol, CrateFlavor, Vec), + FullMetadataNotFound(Symbol, CrateFlavor), SymbolConflictsCurrent(Symbol), StableCrateIdCollision(Symbol, Symbol), DlOpen(String, String), @@ -978,6 +1072,9 @@ impl CrateError { CrateError::MultipleCandidates(crate_name, flavor, candidates) => { dcx.emit_err(errors::MultipleCandidates { span, crate_name, flavor, candidates }); } + CrateError::FullMetadataNotFound(crate_name, flavor) => { + dcx.emit_err(errors::FullMetadataNotFound { span, crate_name, flavor }); + } CrateError::SymbolConflictsCurrent(root_name) => { dcx.emit_err(errors::SymbolConflictsCurrent { span, crate_name: root_name }); } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index e2d043f47c0ef..cee9cff077503 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -17,14 +17,21 @@ use rustc_session::search_paths::PathKind; use rustc_session::utils::NativeLibKind; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::{Symbol, sym}; -use rustc_target::spec::LinkSelfContainedComponents; +use rustc_target::spec::{BinaryFormat, LinkSelfContainedComponents}; use crate::{errors, fluent_generated}; +/// The fallback directories are passed to linker, but not used when rustc does the search, +/// because in the latter case the set of fallback directories cannot always be determined +/// consistently at the moment. +pub struct NativeLibSearchFallback<'a> { + pub self_contained_components: LinkSelfContainedComponents, + pub apple_sdk_root: Option<&'a Path>, +} + pub fn walk_native_lib_search_dirs( sess: &Session, - self_contained_components: LinkSelfContainedComponents, - apple_sdk_root: Option<&Path>, + fallback: Option>, mut f: impl FnMut(&Path, bool /*is_framework*/) -> ControlFlow, ) -> ControlFlow { // Library search paths explicitly supplied by user (`-L` on the command line). @@ -38,6 +45,11 @@ pub fn walk_native_lib_search_dirs( } } + let Some(NativeLibSearchFallback { self_contained_components, apple_sdk_root }) = fallback + else { + return ControlFlow::Continue(()); + }; + // The toolchain ships some native library components and self-contained linking was enabled. // Add the self-contained library directory to search paths. if self_contained_components.intersects( @@ -61,7 +73,7 @@ pub fn walk_native_lib_search_dirs( || sess.target.os == "linux" || sess.target.os == "fuchsia" || sess.target.is_like_aix - || sess.target.is_like_osx && !sess.opts.unstable_opts.sanitizer.is_empty() + || sess.target.is_like_darwin && !sess.opts.unstable_opts.sanitizer.is_empty() { f(&sess.target_tlib_path.dir, false)?; } @@ -83,33 +95,27 @@ pub fn try_find_native_static_library( name: &str, verbatim: bool, ) -> Option { + let default = sess.staticlib_components(verbatim); let formats = if verbatim { - vec![("".into(), "".into())] + vec![default] } else { - let os = (sess.target.staticlib_prefix.clone(), sess.target.staticlib_suffix.clone()); // On Windows, static libraries sometimes show up as libfoo.a and other // times show up as foo.lib - let unix = ("lib".into(), ".a".into()); - if os == unix { vec![os] } else { vec![os, unix] } + let unix = ("lib", ".a"); + if default == unix { vec![default] } else { vec![default, unix] } }; - // FIXME: Account for self-contained linking settings and Apple SDK. - walk_native_lib_search_dirs( - sess, - LinkSelfContainedComponents::empty(), - None, - |dir, is_framework| { - if !is_framework { - for (prefix, suffix) in &formats { - let test = dir.join(format!("{prefix}{name}{suffix}")); - if test.exists() { - return ControlFlow::Break(test); - } + walk_native_lib_search_dirs(sess, None, |dir, is_framework| { + if !is_framework { + for (prefix, suffix) in &formats { + let test = dir.join(format!("{prefix}{name}{suffix}")); + if test.exists() { + return ControlFlow::Break(test); } } - ControlFlow::Continue(()) - }, - ) + } + ControlFlow::Continue(()) + }) .break_value() } @@ -118,36 +124,30 @@ pub fn try_find_native_dynamic_library( name: &str, verbatim: bool, ) -> Option { + let default = sess.staticlib_components(verbatim); let formats = if verbatim { - vec![("".into(), "".into())] + vec![default] } else { // While the official naming convention for MSVC import libraries - // is foo.lib... - let os = (sess.target.staticlib_prefix.clone(), sess.target.staticlib_suffix.clone()); - // ... Meson follows the libfoo.dll.a convention to + // is foo.lib, Meson follows the libfoo.dll.a convention to // disambiguate .a for static libraries - let meson = ("lib".into(), ".dll.a".into()); + let meson = ("lib", ".dll.a"); // and MinGW uses .a altogether - let mingw = ("lib".into(), ".a".into()); - vec![os, meson, mingw] + let mingw = ("lib", ".a"); + vec![default, meson, mingw] }; - walk_native_lib_search_dirs( - sess, - LinkSelfContainedComponents::empty(), - None, - |dir, is_framework| { - if !is_framework { - for (prefix, suffix) in &formats { - let test = dir.join(format!("{prefix}{name}{suffix}")); - if test.exists() { - return ControlFlow::Break(test); - } + walk_native_lib_search_dirs(sess, None, |dir, is_framework| { + if !is_framework { + for (prefix, suffix) in &formats { + let test = dir.join(format!("{prefix}{name}{suffix}")); + if test.exists() { + return ControlFlow::Break(test); } } - ControlFlow::Continue(()) - }, - ) + } + ControlFlow::Continue(()) + }) .break_value() } @@ -207,7 +207,7 @@ impl<'tcx> Collector<'tcx> { let sess = self.tcx.sess; - if matches!(abi, ExternAbi::Rust | ExternAbi::RustIntrinsic) { + if matches!(abi, ExternAbi::Rust) { return; } @@ -226,8 +226,8 @@ impl<'tcx> Collector<'tcx> { let mut wasm_import_module = None; let mut import_name_type = None; for item in items.iter() { - match item.name_or_empty() { - sym::name => { + match item.name() { + Some(sym::name) => { if name.is_some() { sess.dcx().emit_err(errors::MultipleNamesInLink { span: item.span() }); continue; @@ -242,7 +242,7 @@ impl<'tcx> Collector<'tcx> { } name = Some((link_name, span)); } - sym::kind => { + Some(sym::kind) => { if kind.is_some() { sess.dcx().emit_err(errors::MultipleKindsInLink { span: item.span() }); continue; @@ -257,15 +257,32 @@ impl<'tcx> Collector<'tcx> { "static" => NativeLibKind::Static { bundle: None, whole_archive: None }, "dylib" => NativeLibKind::Dylib { as_needed: None }, "framework" => { - if !sess.target.is_like_osx { + if !sess.target.is_like_darwin { sess.dcx().emit_err(errors::LinkFrameworkApple { span }); } NativeLibKind::Framework { as_needed: None } } "raw-dylib" => { - if !sess.target.is_like_windows { + if sess.target.is_like_windows { + // raw-dylib is stable and working on Windows + } else if sess.target.binary_format == BinaryFormat::Elf + && features.raw_dylib_elf() + { + // raw-dylib is unstable on ELF, but the user opted in + } else if sess.target.binary_format == BinaryFormat::Elf + && sess.is_nightly_build() + { + feature_err( + sess, + sym::raw_dylib_elf, + span, + fluent_generated::metadata_raw_dylib_elf_unstable, + ) + .emit(); + } else { sess.dcx().emit_err(errors::RawDylibOnlyWindows { span }); } + NativeLibKind::RawDylib } "link-arg" => { @@ -287,7 +304,7 @@ impl<'tcx> Collector<'tcx> { }; kind = Some(link_kind); } - sym::modifiers => { + Some(sym::modifiers) => { if modifiers.is_some() { sess.dcx() .emit_err(errors::MultipleLinkModifiers { span: item.span() }); @@ -299,7 +316,7 @@ impl<'tcx> Collector<'tcx> { }; modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap())); } - sym::cfg => { + Some(sym::cfg) => { if cfg.is_some() { sess.dcx().emit_err(errors::MultipleCfgs { span: item.span() }); continue; @@ -329,7 +346,7 @@ impl<'tcx> Collector<'tcx> { } cfg = Some(link_cfg.clone()); } - sym::wasm_import_module => { + Some(sym::wasm_import_module) => { if wasm_import_module.is_some() { sess.dcx().emit_err(errors::MultipleWasmImport { span: item.span() }); continue; @@ -340,7 +357,7 @@ impl<'tcx> Collector<'tcx> { }; wasm_import_module = Some((link_wasm_import_module, item.span())); } - sym::import_name_type => { + Some(sym::import_name_type) => { if import_name_type.is_some() { sess.dcx() .emit_err(errors::MultipleImportNameType { span: item.span() }); @@ -514,7 +531,7 @@ impl<'tcx> Collector<'tcx> { let mut renames = FxHashSet::default(); for lib in &self.tcx.sess.opts.libs { if let NativeLibKind::Framework { .. } = lib.kind - && !self.tcx.sess.target.is_like_osx + && !self.tcx.sess.target.is_like_darwin { // Cannot check this when parsing options because the target is not yet available. self.tcx.dcx().emit_err(errors::LibFrameworkApple); diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index e8d9d17872f70..2e4352ca532aa 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -2,7 +2,7 @@ use std::iter::TrustedLen; use std::path::Path; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use std::{io, iter, mem}; pub(super) use cstore_impl::provide; @@ -11,7 +11,7 @@ use rustc_ast as ast; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::owned_slice::OwnedSlice; -use rustc_data_structures::sync::{Lock, OnceLock}; +use rustc_data_structures::sync::Lock; use rustc_data_structures::unhash::UnhashMap; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro}; @@ -386,13 +386,11 @@ impl<'a, 'tcx> DecodeContext<'a, 'tcx> { } } -impl<'a, 'tcx> TyDecoder for DecodeContext<'a, 'tcx> { +impl<'a, 'tcx> TyDecoder<'tcx> for DecodeContext<'a, 'tcx> { const CLEAR_CROSS_CRATE: bool = true; - type I = TyCtxt<'tcx>; - #[inline] - fn interner(&self) -> Self::I { + fn interner(&self) -> TyCtxt<'tcx> { self.tcx() } @@ -564,9 +562,9 @@ impl<'a, 'tcx> SpanDecoder for DecodeContext<'a, 'tcx> { Symbol::intern(s) }) } - SYMBOL_PREINTERNED => { + SYMBOL_PREDEFINED => { let symbol_index = self.read_u32(); - Symbol::new_from_decoded(symbol_index) + Symbol::new(symbol_index) } _ => unreachable!(), } @@ -1055,15 +1053,15 @@ impl<'a> CrateMetadataRef<'a> { attributes.iter().cloned().map(Symbol::intern).collect::>(); ( trait_name, - SyntaxExtensionKind::Derive(Box::new(DeriveProcMacro { client })), + SyntaxExtensionKind::Derive(Arc::new(DeriveProcMacro { client })), helper_attrs, ) } ProcMacro::Attr { name, client } => { - (name, SyntaxExtensionKind::Attr(Box::new(AttrProcMacro { client })), Vec::new()) + (name, SyntaxExtensionKind::Attr(Arc::new(AttrProcMacro { client })), Vec::new()) } ProcMacro::Bang { name, client } => { - (name, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client })), Vec::new()) + (name, SyntaxExtensionKind::Bang(Arc::new(BangProcMacro { client })), Vec::new()) } }; @@ -1315,12 +1313,12 @@ impl<'a> CrateMetadataRef<'a> { fn get_fn_has_self_parameter(self, id: DefIndex, sess: &'a Session) -> bool { self.root .tables - .fn_arg_names + .fn_arg_idents .get(self, id) .expect("argument names not encoded for a function") .decode((self, sess)) .nth(0) - .is_some_and(|ident| ident.name == kw::SelfLower) + .is_some_and(|ident| matches!(ident, Some(Ident { name: kw::SelfLower, .. }))) } fn get_associated_item_or_field_def_ids(self, id: DefIndex) -> impl Iterator { @@ -1334,29 +1332,30 @@ impl<'a> CrateMetadataRef<'a> { } fn get_associated_item(self, id: DefIndex, sess: &'a Session) -> ty::AssocItem { - let name = if self.root.tables.opt_rpitit_info.get(self, id).is_some() { - kw::Empty - } else { - self.item_name(id) - }; - let (kind, has_self) = match self.def_kind(id) { - DefKind::AssocConst => (ty::AssocKind::Const, false), - DefKind::AssocFn => (ty::AssocKind::Fn, self.get_fn_has_self_parameter(id, sess)), - DefKind::AssocTy => (ty::AssocKind::Type, false), + let kind = match self.def_kind(id) { + DefKind::AssocConst => ty::AssocKind::Const { name: self.item_name(id) }, + DefKind::AssocFn => ty::AssocKind::Fn { + name: self.item_name(id), + has_self: self.get_fn_has_self_parameter(id, sess), + }, + DefKind::AssocTy => { + let data = if let Some(rpitit_info) = self.root.tables.opt_rpitit_info.get(self, id) + { + ty::AssocTypeData::Rpitit(rpitit_info.decode(self)) + } else { + ty::AssocTypeData::Normal(self.item_name(id)) + }; + ty::AssocKind::Type { data } + } _ => bug!("cannot get associated-item of `{:?}`", self.def_key(id)), }; let container = self.root.tables.assoc_container.get(self, id).unwrap(); - let opt_rpitit_info = - self.root.tables.opt_rpitit_info.get(self, id).map(|d| d.decode(self)); ty::AssocItem { - name, kind, def_id: self.local_def_id(id), trait_item_def_id: self.get_trait_item_def_id(id), container, - fn_has_self_parameter: has_self, - opt_rpitit_info, } } @@ -1490,6 +1489,17 @@ impl<'a> CrateMetadataRef<'a> { tcx.arena.alloc_from_iter(self.root.lang_items_missing.decode(self)) } + fn get_exportable_items(self) -> impl Iterator { + self.root.exportable_items.decode(self).map(move |index| self.local_def_id(index)) + } + + fn get_stable_order_of_exportable_impls(self) -> impl Iterator { + self.root + .stable_order_of_exportable_impls + .decode(self) + .map(move |v| (self.local_def_id(v.0), v.1)) + } + fn exported_symbols<'tcx>( self, tcx: TyCtxt<'tcx>, @@ -1974,6 +1984,10 @@ impl CrateMetadata { self.root.header.hash } + pub(crate) fn has_async_drops(&self) -> bool { + self.root.tables.adt_async_destructor.len > 0 + } + fn num_def_ids(&self) -> usize { self.root.tables.def_keys.size() } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 776b081a4630f..97d3156573367 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -2,7 +2,7 @@ use std::any::Any; use std::mem; use std::sync::Arc; -use rustc_attr_parsing::Deprecation; +use rustc_attr_data_structures::Deprecation; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; @@ -286,7 +286,7 @@ provide! { tcx, def_id, other, cdata, rendered_const => { table } rendered_precise_capturing_args => { table } asyncness => { table_direct } - fn_arg_names => { table } + fn_arg_idents => { table } coroutine_kind => { table_direct } coroutine_for_closure => { table } coroutine_by_move_body_def_id => { table } @@ -330,14 +330,8 @@ provide! { tcx, def_id, other, cdata, visibility => { cdata.get_visibility(def_id.index) } adt_def => { cdata.get_adt_def(def_id.index, tcx) } - adt_destructor => { - let _ = cdata; - tcx.calculate_dtor(def_id, |_,_| Ok(())) - } - adt_async_destructor => { - let _ = cdata; - tcx.calculate_async_dtor(def_id, |_,_| Ok(())) - } + adt_destructor => { table } + adt_async_destructor => { table } associated_item_def_ids => { tcx.arena.alloc_from_iter(cdata.get_associated_item_or_field_def_ids(def_id.index)) } @@ -412,6 +406,8 @@ provide! { tcx, def_id, other, cdata, used_crate_source => { Arc::clone(&cdata.source) } debugger_visualizers => { cdata.get_debugger_visualizers() } + exportable_items => { tcx.arena.alloc_from_iter(cdata.get_exportable_items()) } + stable_order_of_exportable_impls => { tcx.arena.alloc(cdata.get_stable_order_of_exportable_impls().collect()) } exported_symbols => { let syms = cdata.exported_symbols(tcx); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 88a88847e6b8b..7ac72ef814a9c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -201,9 +201,9 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { } fn encode_symbol(&mut self, symbol: Symbol) { - // if symbol preinterned, emit tag and symbol index - if symbol.is_preinterned() { - self.opaque.emit_u8(SYMBOL_PREINTERNED); + // if symbol predefined, emit tag and symbol index + if symbol.is_predefined() { + self.opaque.emit_u8(SYMBOL_PREDEFINED); self.opaque.emit_u32(symbol.as_u32()); } else { // otherwise write it as string or as offset to it @@ -378,11 +378,9 @@ impl<'a, 'tcx> Encodable> for [u8] { } } -impl<'a, 'tcx> TyEncoder for EncodeContext<'a, 'tcx> { +impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { const CLEAR_CROSS_CRATE: bool = true; - type I = TyCtxt<'tcx>; - fn position(&self) -> usize { self.opaque.position() } @@ -553,8 +551,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { match source_file.name { FileName::Real(ref original_file_name) => { - // FIXME: This should probably to conditionally remapped under - // a RemapPathScopeComponents but which one? let adapted_file_name = source_map .path_mapping() .to_embeddable_absolute_path(original_file_name.clone(), working_directory); @@ -675,6 +671,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let debugger_visualizers = stat!("debugger-visualizers", || self.encode_debugger_visualizers()); + let exportable_items = stat!("exportable-items", || self.encode_exportable_items()); + + let stable_order_of_exportable_impls = + stat!("exportable-items", || self.encode_stable_order_of_exportable_impls()); + // Encode exported symbols info. This is prefetched in `encode_metadata`. let exported_symbols = stat!("exported-symbols", || { self.encode_exported_symbols(tcx.exported_symbols(LOCAL_CRATE)) @@ -703,6 +704,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { triple: tcx.sess.opts.target_triple.clone(), hash: tcx.crate_hash(LOCAL_CRATE), is_proc_macro_crate: proc_macro_data.is_some(), + is_stub: false, }, extra_filename: tcx.sess.opts.cg.extra_filename.clone(), stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(), @@ -741,6 +743,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { traits, impls, incoherent_impls, + exportable_items, + stable_order_of_exportable_impls, exported_symbols, interpret_alloc_index, tables, @@ -822,7 +826,9 @@ struct AnalyzeAttrState<'a> { #[inline] fn analyze_attr(attr: &impl AttributeExt, state: &mut AnalyzeAttrState<'_>) -> bool { let mut should_encode = false; - if !rustc_feature::encode_cross_crate(attr.name_or_empty()) { + if let Some(name) = attr.name() + && !rustc_feature::encode_cross_crate(name) + { // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates. } else if attr.doc_str().is_some() { // We keep all doc comments reachable to rustdoc because they might be "imported" into @@ -845,9 +851,8 @@ fn analyze_attr(attr: &impl AttributeExt, state: &mut AnalyzeAttrState<'_>) -> b } } } - } else if attr.path().starts_with(&[sym::diagnostic]) && attr.path().len() == 2 { - should_encode = - rustc_feature::is_stable_diagnostic_attribute(attr.path()[1], state.features); + } else if let &[sym::diagnostic, seg] = &*attr.path() { + should_encode = rustc_feature::is_stable_diagnostic_attribute(seg, state.features); } else { should_encode = true; } @@ -1101,7 +1106,6 @@ fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: Def DefKind::Struct | DefKind::Union | DefKind::Enum - | DefKind::Variant | DefKind::OpaqueTy | DefKind::Fn | DefKind::Ctor(..) @@ -1111,6 +1115,7 @@ fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: Def matches!(tcx.opt_rpitit_info(def_id), Some(ty::ImplTraitInTraitData::Trait { .. })) } DefKind::Mod + | DefKind::Variant | DefKind::Field | DefKind::AssocConst | DefKind::TyParam @@ -1340,7 +1345,7 @@ fn should_encode_const(def_kind: DefKind) -> bool { fn should_encode_fn_impl_trait_in_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { if let Some(assoc_item) = tcx.opt_associated_item(def_id) && assoc_item.container == ty::AssocItemContainer::Trait - && assoc_item.kind == ty::AssocKind::Fn + && assoc_item.is_fn() { true } else { @@ -1357,8 +1362,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { features: &tcx.features(), }; let attr_iter = tcx - .hir() - .attrs(tcx.local_def_id_to_hir_id(def_id)) + .hir_attrs(tcx.local_def_id_to_hir_id(def_id)) .iter() .filter(|attr| analyze_attr(*attr, &mut state)); @@ -1472,7 +1476,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::Fn | DefKind::AssocFn = def_kind { self.tables.asyncness.set_some(def_id.index, tcx.asyncness(def_id)); - record_array!(self.tables.fn_arg_names[def_id] <- tcx.fn_arg_names(def_id)); + record_array!(self.tables.fn_arg_idents[def_id] <- tcx.fn_arg_idents(def_id)); } if let Some(name) = tcx.intrinsic(def_id) { record!(self.tables.intrinsic[def_id] <- name); @@ -1637,6 +1641,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record!(self.tables.fn_sig[variant.def_id] <- fn_sig); } } + + if let Some(destructor) = tcx.adt_destructor(local_def_id) { + record!(self.tables.adt_destructor[def_id] <- destructor); + } + + if let Some(destructor) = tcx.adt_async_destructor(local_def_id) { + record!(self.tables.adt_async_destructor[def_id] <- destructor); + } } #[instrument(level = "debug", skip(self))] @@ -1686,7 +1698,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { match item.container { AssocItemContainer::Trait => { - if let ty::AssocKind::Type = item.kind { + if item.is_type() { self.encode_explicit_item_bounds(def_id); self.encode_explicit_item_self_bounds(def_id); if tcx.is_conditionally_const(def_id) { @@ -1701,7 +1713,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } } - if let Some(rpitit_info) = item.opt_rpitit_info { + if let ty::AssocKind::Type { data: ty::AssocTypeData::Rpitit(rpitit_info) } = item.kind { record!(self.tables.opt_rpitit_info[def_id] <- rpitit_info); if matches!(rpitit_info, ty::ImplTraitInTraitData::Trait { .. }) { record_array!( @@ -1839,7 +1851,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_info_for_macro(&mut self, def_id: LocalDefId) { let tcx = self.tcx; - let hir::ItemKind::Macro(macro_def, _) = tcx.hir().expect_item(def_id).kind else { bug!() }; + let (_, macro_def, _) = tcx.hir_expect_item(def_id).expect_macro(); self.tables.is_macro_rules.set(def_id.local_def_index, macro_def.macro_rules); record!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body); } @@ -1885,8 +1897,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let is_proc_macro = self.tcx.crate_types().contains(&CrateType::ProcMacro); if is_proc_macro { let tcx = self.tcx; - let hir = tcx.hir(); - let proc_macro_decls_static = tcx.proc_macro_decls_static(()).unwrap().local_def_index; let stability = tcx.lookup_stability(CRATE_DEF_ID); let macros = @@ -1918,11 +1928,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { for &proc_macro in &tcx.resolutions(()).proc_macros { let id = proc_macro; let proc_macro = tcx.local_def_id_to_hir_id(proc_macro); - let mut name = hir.name(proc_macro); - let span = hir.span(proc_macro); + let mut name = tcx.hir_name(proc_macro); + let span = tcx.hir_span(proc_macro); // Proc-macros may have attributes like `#[allow_internal_unstable]`, // so downstream crates need access to them. - let attrs = hir.attrs(proc_macro); + let attrs = tcx.hir_attrs(proc_macro); let macro_kind = if ast::attr::contains_name(attrs, sym::proc_macro) { MacroKind::Bang } else if ast::attr::contains_name(attrs, sym::proc_macro_attribute) { @@ -2144,6 +2154,20 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(&all_impls) } + fn encode_exportable_items(&mut self) -> LazyArray { + empty_proc_macro!(self); + self.lazy_array(self.tcx.exportable_items(LOCAL_CRATE).iter().map(|def_id| def_id.index)) + } + + fn encode_stable_order_of_exportable_impls(&mut self) -> LazyArray<(DefIndex, usize)> { + empty_proc_macro!(self); + let stable_order_of_exportable_impls = + self.tcx.stable_order_of_exportable_impls(LOCAL_CRATE); + self.lazy_array( + stable_order_of_exportable_impls.iter().map(|(def_id, idx)| (def_id.index, *idx)), + ) + } + // Encodes all symbols exported from this crate into the metadata. // // This pass is seeded off the reachability list calculated in the @@ -2196,7 +2220,7 @@ fn prefetch_mir(tcx: TyCtxt<'_>) { } let reachable_set = tcx.reachable_set(()); - par_for_each_in(tcx.mir_keys(()), |&def_id| { + par_for_each_in(tcx.mir_keys(()), |&&def_id| { let (encode_const, encode_opt) = should_encode_mir(tcx, reachable_set, def_id); if encode_const { @@ -2235,8 +2259,12 @@ fn prefetch_mir(tcx: TyCtxt<'_>) { // generated regardless of trailing bytes that end up in it. pub struct EncodedMetadata { - // The declaration order matters because `mmap` should be dropped before `_temp_dir`. - mmap: Option, + // The declaration order matters because `full_metadata` should be dropped + // before `_temp_dir`. + full_metadata: Option, + // This is an optional stub metadata containing only the crate header. + // The header should be very small, so we load it directly into memory. + stub_metadata: Option>, // We need to carry MaybeTempDir to avoid deleting the temporary // directory while accessing the Mmap. _temp_dir: Option, @@ -2244,33 +2272,50 @@ pub struct EncodedMetadata { impl EncodedMetadata { #[inline] - pub fn from_path(path: PathBuf, temp_dir: Option) -> std::io::Result { + pub fn from_path( + path: PathBuf, + stub_path: Option, + temp_dir: Option, + ) -> std::io::Result { let file = std::fs::File::open(&path)?; let file_metadata = file.metadata()?; if file_metadata.len() == 0 { - return Ok(Self { mmap: None, _temp_dir: None }); + return Ok(Self { full_metadata: None, stub_metadata: None, _temp_dir: None }); } - let mmap = unsafe { Some(Mmap::map(file)?) }; - Ok(Self { mmap, _temp_dir: temp_dir }) + let full_mmap = unsafe { Some(Mmap::map(file)?) }; + + let stub = + if let Some(stub_path) = stub_path { Some(std::fs::read(stub_path)?) } else { None }; + + Ok(Self { full_metadata: full_mmap, stub_metadata: stub, _temp_dir: temp_dir }) } #[inline] - pub fn raw_data(&self) -> &[u8] { - self.mmap.as_deref().unwrap_or_default() + pub fn full(&self) -> &[u8] { + &self.full_metadata.as_deref().unwrap_or_default() + } + + #[inline] + pub fn stub_or_full(&self) -> &[u8] { + self.stub_metadata.as_deref().unwrap_or(self.full()) } } impl Encodable for EncodedMetadata { fn encode(&self, s: &mut S) { - let slice = self.raw_data(); + self.stub_metadata.encode(s); + + let slice = self.full(); slice.encode(s) } } impl Decodable for EncodedMetadata { fn decode(d: &mut D) -> Self { + let stub = >>::decode(d); + let len = d.read_usize(); - let mmap = if len > 0 { + let full_metadata = if len > 0 { let mut mmap = MmapMut::map_anon(len).unwrap(); mmap.copy_from_slice(d.read_raw_bytes(len)); Some(mmap.make_read_only().unwrap()) @@ -2278,11 +2323,11 @@ impl Decodable for EncodedMetadata { None }; - Self { mmap, _temp_dir: None } + Self { full_metadata, stub_metadata: stub, _temp_dir: None } } } -pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) { +pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata"); // Since encoding metadata is not in a query, and nothing is cached, @@ -2296,6 +2341,42 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) { join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE)); } + with_encode_metadata_header(tcx, path, |ecx| { + // Encode all the entries and extra information in the crate, + // culminating in the `CrateRoot` which points to all of it. + let root = ecx.encode_crate_root(); + + // Flush buffer to ensure backing file has the correct size. + ecx.opaque.flush(); + // Record metadata size for self-profiling + tcx.prof.artifact_size( + "crate_metadata", + "crate_metadata", + ecx.opaque.file().metadata().unwrap().len(), + ); + + root.position.get() + }); + + if let Some(ref_path) = ref_path { + with_encode_metadata_header(tcx, ref_path, |ecx| { + let header: LazyValue = ecx.lazy(CrateHeader { + name: tcx.crate_name(LOCAL_CRATE), + triple: tcx.sess.opts.target_triple.clone(), + hash: tcx.crate_hash(LOCAL_CRATE), + is_proc_macro_crate: false, + is_stub: true, + }); + header.position.get() + }); + } +} + +fn with_encode_metadata_header( + tcx: TyCtxt<'_>, + path: &Path, + f: impl FnOnce(&mut EncodeContext<'_, '_>) -> usize, +) { let mut encoder = opaque::FileEncoder::new(path) .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailCreateFileEncoder { err })); encoder.emit_raw_bytes(METADATA_HEADER); @@ -2330,9 +2411,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) { // Encode the rustc version string in a predictable location. rustc_version(tcx.sess.cfg_version).encode(&mut ecx); - // Encode all the entries and extra information in the crate, - // culminating in the `CrateRoot` which points to all of it. - let root = ecx.encode_crate_root(); + let root_position = f(&mut ecx); // Make sure we report any errors from writing to the file. // If we forget this, compilation can succeed with an incomplete rmeta file, @@ -2342,12 +2421,9 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) { } let file = ecx.opaque.file(); - if let Err(err) = encode_root_position(file, root.position.get()) { + if let Err(err) = encode_root_position(file, root_position) { tcx.dcx().emit_fatal(FailWriteFile { path: ecx.opaque.path(), err }); } - - // Record metadata size for self-profiling - tcx.prof.artifact_size("crate_metadata", "crate_metadata", file.metadata().unwrap().len()); } fn encode_root_position(mut file: &File, pos: usize) -> Result<(), std::io::Error> { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 7b34e605c5307..d3d928aa88e5c 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -10,6 +10,7 @@ use rustc_abi::{FieldIdx, ReprOptions, VariantIdx}; use rustc_ast::expand::StrippedCfgItem; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; +use rustc_hir::PreciseCapturingArgKind; use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIndex, DefPathHash, StableCrateId}; use rustc_hir::definitions::DefKey; @@ -35,11 +36,11 @@ use rustc_serialize::opaque::FileEncoder; use rustc_session::config::{SymbolManglingVersion, TargetModifier}; use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib}; use rustc_span::edition::Edition; -use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextData}; +use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextKey}; use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Ident, Span, Symbol}; use rustc_target::spec::{PanicStrategy, TargetTuple}; use table::TableBuilder; -use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_attr_data_structures as attrs, rustc_hir as hir}; use crate::creader::CrateMetadataRef; @@ -55,7 +56,7 @@ pub(crate) fn rustc_version(cfg_version: &'static str) -> String { /// 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 = 9; +const METADATA_VERSION: u8 = 10; /// Metadata header which includes `METADATA_VERSION`. /// @@ -192,14 +193,14 @@ enum LazyState { Previous(NonZero), } -type SyntaxContextTable = LazyTable>>; +type SyntaxContextTable = LazyTable>>; type ExpnDataTable = LazyTable>>; type ExpnHashTable = LazyTable>>; #[derive(MetadataEncodable, MetadataDecodable)] pub(crate) struct ProcMacroData { proc_macro_decls_static: DefIndex, - stability: Option, + stability: Option, macros: LazyArray, } @@ -220,6 +221,12 @@ pub(crate) struct CrateHeader { /// This is separate from [`ProcMacroData`] to avoid having to update [`METADATA_VERSION`] every /// time ProcMacroData changes. pub(crate) is_proc_macro_crate: bool, + /// Whether this crate metadata section is just a stub. + /// Stubs do not contain the full metadata (it will be typically stored + /// in a separate rmeta file). + /// + /// This is used inside rlibs and dylibs when using `-Zembed-metadata=no`. + pub(crate) is_stub: bool, } /// Serialized `.rmeta` data for a crate. @@ -273,6 +280,8 @@ pub(crate) struct CrateRoot { tables: LazyTables, debugger_visualizers: LazyArray, + exportable_items: LazyArray, + stable_order_of_exportable_impls: LazyArray<(DefIndex, usize)>, exported_symbols: LazyArray<(ExportedSymbol<'static>, SymbolExportInfo)>, syntax_contexts: SyntaxContextTable, @@ -413,10 +422,10 @@ define_tables! { safety: Table, def_span: Table>, def_ident_span: Table>, - lookup_stability: Table>, - lookup_const_stability: Table>, - lookup_default_body_stability: Table>, - lookup_deprecation_entry: Table>, + lookup_stability: Table>, + lookup_const_stability: Table>, + lookup_default_body_stability: Table>, + lookup_deprecation_entry: Table>, explicit_predicates_of: Table>>, generics_of: Table>, type_of: Table>>>, @@ -440,11 +449,13 @@ define_tables! { coerce_unsized_info: Table>, mir_const_qualif: Table>, rendered_const: Table>, - rendered_precise_capturing_args: Table>, + rendered_precise_capturing_args: Table>>, asyncness: Table, - fn_arg_names: Table>, + fn_arg_idents: Table>>, coroutine_kind: Table, coroutine_for_closure: Table, + adt_destructor: Table>, + adt_async_destructor: Table>, coroutine_by_move_body_def_id: Table, eval_static_initializer: Table>>, trait_def: Table>, @@ -575,7 +586,7 @@ impl SpanTag { // Tags for encoding Symbol's const SYMBOL_STR: u8 = 0; const SYMBOL_OFFSET: u8 = 1; -const SYMBOL_PREINTERNED: u8 = 2; +const SYMBOL_PREDEFINED: u8 = 2; pub fn provide(providers: &mut Providers) { encoder::provide(providers); diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index aebd2181f31e1..43c1af642dd56 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -14,7 +14,8 @@ rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_ast_ir = { path = "../rustc_ast_ir" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_messages = { path = "../rustc_error_messages" } # Used for intra-doc links rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 0b3c0be1a4e1a..3d27e587b6cb4 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -1,7 +1,11 @@ +middle_assert_async_resume_after_drop = `async fn` resumed after async drop + middle_assert_async_resume_after_panic = `async fn` resumed after panicking middle_assert_async_resume_after_return = `async fn` resumed after completion +middle_assert_coroutine_resume_after_drop = coroutine resumed after async drop + middle_assert_coroutine_resume_after_panic = coroutine resumed after panicking middle_assert_coroutine_resume_after_return = coroutine resumed after completion @@ -9,6 +13,8 @@ middle_assert_coroutine_resume_after_return = coroutine resumed after completion middle_assert_divide_by_zero = attempt to divide `{$val}` by zero +middle_assert_gen_resume_after_drop = `gen` fn or block cannot be further iterated on after it async dropped + middle_assert_gen_resume_after_panic = `gen` fn or block cannot be further iterated on after it panicked middle_assert_misaligned_ptr_deref = diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index aef56ea46e957..a0f4597408939 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -9,6 +9,7 @@ macro_rules! arena_types { ($macro:path) => ( $macro!([ [] layout: rustc_abi::LayoutData, + [] proxy_coroutine_layout: rustc_middle::mir::CoroutineLayout<'tcx>, [] fn_abi: rustc_target::callconv::FnAbi<'tcx, rustc_middle::ty::Ty<'tcx>>, // AdtDef are interned and compared by address [decode] adt_def: rustc_middle::ty::AdtDefData, @@ -28,7 +29,7 @@ macro_rules! arena_types { rustc_middle::mir::Body<'tcx> >, [decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>, - [decode] borrowck_result: rustc_middle::mir::BorrowCheckResult<'tcx>, + [decode] borrowck_result: rustc_middle::mir::ConcreteOpaqueTypes<'tcx>, [] resolver: rustc_data_structures::steal::Steal<( rustc_middle::ty::ResolverAstLowering, std::sync::Arc, @@ -89,8 +90,9 @@ macro_rules! arena_types { [] name_set: rustc_data_structures::unord::UnordSet, [] autodiff_item: rustc_ast::expand::autodiff_attrs::AutoDiffItem, [] ordered_name_set: rustc_data_structures::fx::FxIndexSet, - [] pats: rustc_middle::ty::PatternKind<'tcx>, [] valtree: rustc_middle::ty::ValTreeKind<'tcx>, + [] stable_order_of_exportable_impls: + rustc_data_structures::fx::FxIndexMap, // Note that this deliberately duplicates items in the `rustc_hir::arena`, // since we need to allocate this type on both the `rustc_hir` arena diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 7525aeb922729..0c998a2cbb38b 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -13,14 +13,26 @@ use crate::ty::TyCtxt; macro_rules! define_dep_nodes { ( - $($(#[$attr:meta])* - [$($modifiers:tt)*] fn $variant:ident($($K:tt)*) -> $V:ty,)*) => { + $( + $(#[$attr:meta])* + [$($modifiers:tt)*] fn $variant:ident($($K:tt)*) -> $V:ty, + )* + ) => { #[macro_export] macro_rules! make_dep_kind_array { ($mod:ident) => {[ $($mod::$variant()),* ]}; } + #[macro_export] + macro_rules! make_dep_kind_name_array { + ($mod:ident) => { + vec! { + $(*$mod::$variant().name),* + } + }; + } + /// This enum serves as an index into arrays built by `make_dep_kind_array`. // This enum has more than u8::MAX variants so we need some kind of multi-byte // encoding. The derived Encodable/Decodable uses leb128 encoding which is @@ -74,11 +86,15 @@ macro_rules! define_dep_nodes { }; } -rustc_query_append!(define_dep_nodes![ +// Create various data structures for each query, and also for a few things +// that aren't queries. +rustc_with_all_queries!(define_dep_nodes![ /// We use this for most things when incr. comp. is turned off. [] fn Null() -> (), /// We use this to create a forever-red node. [] fn Red() -> (), + [] fn SideEffect() -> (), + [] fn AnonZeroDeps() -> (), [] fn TraitSelect() -> (), [] fn CompileCodegenUnit() -> (), [] fn CompileMonoItem() -> (), diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 2090c3e6da6fa..931d67087acab 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -20,7 +20,9 @@ pub type DepGraph = rustc_query_system::dep_graph::DepGraph; pub type DepKindStruct<'tcx> = rustc_query_system::dep_graph::DepKindStruct>; #[derive(Clone)] -pub struct DepsType; +pub struct DepsType { + pub dep_names: Vec<&'static str>, +} impl Deps for DepsType { fn with_deps(task_deps: TaskDepsRef<'_>, op: OP) -> R @@ -44,8 +46,14 @@ impl Deps for DepsType { }) } + fn name(&self, dep_kind: DepKind) -> &'static str { + self.dep_names[dep_kind.as_usize()] + } + const DEP_KIND_NULL: DepKind = dep_kinds::Null; const DEP_KIND_RED: DepKind = dep_kinds::Red; + const DEP_KIND_SIDE_EFFECT: DepKind = dep_kinds::SideEffect; + const DEP_KIND_ANON_ZERO_DEPS: DepKind = dep_kinds::AnonZeroDeps; const DEP_KIND_MAX: u16 = dep_node::DEP_KIND_VARIANTS - 1; } diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index fad8c7dcbcbcb..fee707f7b4c90 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -1,3 +1,7 @@ +//! This module used to contain a type called `Map`. That type has since been +//! eliminated, and all its methods are now on `TyCtxt`. But the module name +//! stays as `map` because there isn't an obviously better name for it. + use rustc_abi::ExternAbi; use rustc_ast::visit::{VisitorResult, walk_list}; use rustc_data_structures::fingerprint::Fingerprint; @@ -18,15 +22,6 @@ use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::LocalCrate; use crate::ty::TyCtxt; -// FIXME: the structure was necessary in the past but now it -// only serves as "namespace" for HIR-related methods, and can be -// removed if all the methods are reasonably renamed and moved to tcx -// (https://github.com/rust-lang/rust/pull/118256#issuecomment-1826442834). -#[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)`. struct ParentHirIterator<'tcx> { @@ -273,17 +268,18 @@ impl<'tcx> TyCtxt<'tcx> { self.hir_maybe_body_owned_by(id).unwrap_or_else(|| { let hir_id = self.local_def_id_to_hir_id(id); span_bug!( - self.hir().span(hir_id), + self.hir_span(hir_id), "body_owned_by: {} has no associated body", - self.hir().node_to_string(hir_id) + self.hir_id_to_string(hir_id) ); }) } - pub fn hir_body_param_names(self, id: BodyId) -> impl Iterator { - self.hir_body(id).params.iter().map(|arg| match arg.pat.kind { - PatKind::Binding(_, _, ident, _) => ident, - _ => Ident::empty(), + pub fn hir_body_param_idents(self, id: BodyId) -> impl Iterator> { + self.hir_body(id).params.iter().map(|param| match param.pat.kind { + PatKind::Binding(_, _, ident, _) => Some(ident), + PatKind::Wild => Some(Ident::new(kw::Underscore, param.pat.span)), + _ => None, }) } @@ -333,7 +329,7 @@ impl<'tcx> TyCtxt<'tcx> { /// 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()`. + /// themselves, you can do `self.hir_crate(()).body_ids.iter()`. #[inline] pub fn hir_body_owners(self) -> impl Iterator { self.hir_crate_items(()).body_owners.iter().copied() @@ -341,7 +337,7 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn par_hir_body_owners(self, f: impl Fn(LocalDefId) + DynSend + DynSync) { - par_for_each_in(&self.hir_crate_items(()).body_owners[..], |&def_id| f(def_id)); + par_for_each_in(&self.hir_crate_items(()).body_owners[..], |&&def_id| f(def_id)); } pub fn hir_ty_param_owner(self, def_id: LocalDefId) -> LocalDefId { @@ -366,15 +362,11 @@ impl<'tcx> TyCtxt<'tcx> { } } - pub fn hir_trait_impls(self, trait_did: DefId) -> &'tcx [LocalDefId] { - self.all_local_trait_impls(()).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 hir_krate_attrs(self) -> &'tcx [Attribute] { - self.hir().attrs(CRATE_HIR_ID) + self.hir_attrs(CRATE_HIR_ID) } pub fn hir_rustc_coherence_is_core(self) -> bool { @@ -384,7 +376,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn hir_get_module(self, module: LocalModDefId) -> (&'tcx Mod<'tcx>, Span, HirId) { let hir_id = HirId::make_owner(module.to_local_def_id()); match self.hir_owner_node(hir_id.owner) { - OwnerNode::Item(&Item { span, kind: ItemKind::Mod(m), .. }) => (m, span, hir_id), + OwnerNode::Item(&Item { span, kind: ItemKind::Mod(_, m), .. }) => (m, span, hir_id), OwnerNode::Crate(item) => (item, item.spans.inner_span, hir_id), node => panic!("not a module: {node:?}"), } @@ -674,105 +666,178 @@ impl<'tcx> TyCtxt<'tcx> { } } } -} -impl<'hir> Map<'hir> { - pub fn get_foreign_abi(self, hir_id: HirId) -> ExternAbi { - let parent = self.tcx.hir_get_parent_item(hir_id); + /// Get a representation of this `id` for debugging purposes. + /// NOTE: Do NOT use this in diagnostics! + pub fn hir_id_to_string(self, id: HirId) -> String { + let path_str = |def_id: LocalDefId| self.def_path_str(def_id); + + let span_str = + || self.sess.source_map().span_to_snippet(self.hir_span(id)).unwrap_or_default(); + let node_str = |prefix| format!("{id} ({prefix} `{}`)", span_str()); + + match self.hir_node(id) { + 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::Macro(..) => "macro", + ItemKind::Mod(..) => "mod", + ItemKind::ForeignMod { .. } => "foreign mod", + ItemKind::GlobalAsm { .. } => "global asm", + ItemKind::TyAlias(..) => "ty", + ItemKind::Enum(..) => "enum", + ItemKind::Struct(..) => "struct", + ItemKind::Union(..) => "union", + ItemKind::Trait(..) => "trait", + ItemKind::TraitAlias(..) => "trait alias", + ItemKind::Impl { .. } => "impl", + }; + format!("{id} ({item_str} {})", path_str(item.owner_id.def_id)) + } + Node::ForeignItem(item) => { + format!("{id} (foreign item {})", path_str(item.owner_id.def_id)) + } + Node::ImplItem(ii) => { + let kind = match ii.kind { + ImplItemKind::Const(..) => "associated constant", + ImplItemKind::Fn(fn_sig, _) => match fn_sig.decl.implicit_self { + ImplicitSelfKind::None => "associated function", + _ => "method", + }, + ImplItemKind::Type(_) => "associated type", + }; + format!("{id} ({kind} `{}` in {})", ii.ident, path_str(ii.owner_id.def_id)) + } + Node::TraitItem(ti) => { + let kind = match ti.kind { + TraitItemKind::Const(..) => "associated constant", + TraitItemKind::Fn(fn_sig, _) => match fn_sig.decl.implicit_self { + ImplicitSelfKind::None => "associated function", + _ => "trait method", + }, + TraitItemKind::Type(..) => "associated type", + }; + + format!("{id} ({kind} `{}` in {})", ti.ident, path_str(ti.owner_id.def_id)) + } + Node::Variant(variant) => { + format!("{id} (variant `{}` in {})", variant.ident, path_str(variant.def_id)) + } + Node::Field(field) => { + format!("{id} (field `{}` in {})", field.ident, path_str(field.def_id)) + } + Node::AnonConst(_) => node_str("const"), + Node::ConstBlock(_) => node_str("const"), + Node::ConstArg(_) => node_str("const"), + Node::Expr(_) => node_str("expr"), + Node::ExprField(_) => node_str("expr field"), + Node::Stmt(_) => node_str("stmt"), + Node::PathSegment(_) => node_str("path segment"), + Node::Ty(_) => node_str("type"), + Node::AssocItemConstraint(_) => node_str("assoc item constraint"), + Node::TraitRef(_) => node_str("trait ref"), + Node::OpaqueTy(_) => node_str("opaque type"), + Node::Pat(_) => node_str("pat"), + Node::TyPat(_) => node_str("pat ty"), + Node::PatField(_) => node_str("pattern field"), + Node::PatExpr(_) => node_str("pattern literal"), + Node::Param(_) => node_str("param"), + Node::Arm(_) => node_str("arm"), + Node::Block(_) => node_str("block"), + Node::Infer(_) => node_str("infer"), + Node::LetStmt(_) => node_str("local"), + Node::Ctor(ctor) => format!( + "{id} (ctor {})", + ctor.ctor_def_id().map_or("".into(), |def_id| path_str(def_id)), + ), + Node::Lifetime(_) => node_str("lifetime"), + Node::GenericParam(param) => { + format!("{id} (generic_param {})", path_str(param.def_id)) + } + Node::Crate(..) => String::from("(root_crate)"), + Node::WherePredicate(_) => node_str("where predicate"), + Node::Synthetic => unreachable!(), + Node::Err(_) => node_str("error"), + Node::PreciseCapturingNonLifetimeArg(_param) => node_str("parameter"), + } + } + + pub fn hir_get_foreign_abi(self, hir_id: HirId) -> ExternAbi { + let parent = self.hir_get_parent_item(hir_id); if let OwnerNode::Item(Item { kind: ItemKind::ForeignMod { abi, .. }, .. }) = - self.tcx.hir_owner_node(parent) + self.hir_owner_node(parent) { return *abi; } bug!( "expected foreign mod or inlined parent, found {}", - self.node_to_string(HirId::make_owner(parent.def_id)) + self.hir_id_to_string(HirId::make_owner(parent.def_id)) ) } - pub fn expect_item(self, id: LocalDefId) -> &'hir Item<'hir> { - match self.tcx.expect_hir_owner_node(id) { + pub fn hir_expect_item(self, id: LocalDefId) -> &'tcx Item<'tcx> { + match self.expect_hir_owner_node(id) { OwnerNode::Item(item) => item, - _ => bug!("expected item, found {}", self.node_to_string(HirId::make_owner(id))), + _ => bug!("expected item, found {}", self.hir_id_to_string(HirId::make_owner(id))), } } - pub fn expect_impl_item(self, id: LocalDefId) -> &'hir ImplItem<'hir> { - match self.tcx.expect_hir_owner_node(id) { + pub fn hir_expect_impl_item(self, id: LocalDefId) -> &'tcx ImplItem<'tcx> { + match self.expect_hir_owner_node(id) { OwnerNode::ImplItem(item) => item, - _ => bug!("expected impl item, found {}", self.node_to_string(HirId::make_owner(id))), + _ => bug!("expected impl item, found {}", self.hir_id_to_string(HirId::make_owner(id))), } } - pub fn expect_trait_item(self, id: LocalDefId) -> &'hir TraitItem<'hir> { - match self.tcx.expect_hir_owner_node(id) { + pub fn hir_expect_trait_item(self, id: LocalDefId) -> &'tcx TraitItem<'tcx> { + match self.expect_hir_owner_node(id) { OwnerNode::TraitItem(item) => item, - _ => bug!("expected trait item, found {}", self.node_to_string(HirId::make_owner(id))), - } - } - - pub fn get_fn_output(self, def_id: LocalDefId) -> Option<&'hir FnRetTy<'hir>> { - Some(&self.tcx.opt_hir_owner_node(def_id)?.fn_decl()?.output) - } - - pub fn expect_variant(self, id: HirId) -> &'hir Variant<'hir> { - match self.tcx.hir_node(id) { - Node::Variant(variant) => variant, - _ => bug!("expected variant, found {}", self.node_to_string(id)), - } - } - - pub fn expect_field(self, id: HirId) -> &'hir FieldDef<'hir> { - match self.tcx.hir_node(id) { - Node::Field(field) => field, - _ => bug!("expected field, found {}", self.node_to_string(id)), - } - } - - pub fn expect_foreign_item(self, id: OwnerId) -> &'hir ForeignItem<'hir> { - match self.tcx.hir_owner_node(id) { - OwnerNode::ForeignItem(item) => item, _ => { - bug!( - "expected foreign item, found {}", - self.node_to_string(HirId::make_owner(id.def_id)) - ) + bug!("expected trait item, found {}", self.hir_id_to_string(HirId::make_owner(id))) } } } + pub fn hir_get_fn_output(self, def_id: LocalDefId) -> Option<&'tcx FnRetTy<'tcx>> { + Some(&self.opt_hir_owner_node(def_id)?.fn_decl()?.output) + } + #[track_caller] - pub fn expect_opaque_ty(self, id: LocalDefId) -> &'hir OpaqueTy<'hir> { - match self.tcx.hir_node_by_def_id(id) { + pub fn hir_expect_opaque_ty(self, id: LocalDefId) -> &'tcx OpaqueTy<'tcx> { + match self.hir_node_by_def_id(id) { Node::OpaqueTy(opaq) => opaq, _ => { bug!( "expected opaque type definition, found {}", - self.node_to_string(self.tcx.local_def_id_to_hir_id(id)) + self.hir_id_to_string(self.local_def_id_to_hir_id(id)) ) } } } - pub fn expect_expr(self, id: HirId) -> &'hir Expr<'hir> { - match self.tcx.hir_node(id) { + pub fn hir_expect_expr(self, id: HirId) -> &'tcx Expr<'tcx> { + match self.hir_node(id) { Node::Expr(expr) => expr, - _ => bug!("expected expr, found {}", self.node_to_string(id)), + _ => bug!("expected expr, found {}", self.hir_id_to_string(id)), } } - pub fn opt_delegation_sig_id(self, def_id: LocalDefId) -> Option { - self.tcx.opt_hir_owner_node(def_id)?.fn_decl()?.opt_delegation_sig_id() + pub fn hir_opt_delegation_sig_id(self, def_id: LocalDefId) -> Option { + self.opt_hir_owner_node(def_id)?.fn_decl()?.opt_delegation_sig_id() } #[inline] - fn opt_ident(self, id: HirId) -> Option { - match self.tcx.hir_node(id) { + fn hir_opt_ident(self, id: HirId) -> Option { + match self.hir_node(id) { Node::Pat(&Pat { kind: PatKind::Binding(_, _, ident, _), .. }) => Some(ident), // A `Ctor` doesn't have an identifier itself, but its parent // struct/variant does. Compare with `hir::Map::span`. - Node::Ctor(..) => match self.tcx.parent_hir_node(id) { - Node::Item(item) => Some(item.ident), + Node::Ctor(..) => match self.parent_hir_node(id) { + Node::Item(item) => Some(item.kind.ident().unwrap()), Node::Variant(variant) => Some(variant.ident), _ => unreachable!(), }, @@ -781,33 +846,33 @@ impl<'hir> Map<'hir> { } #[inline] - pub(super) fn opt_ident_span(self, id: HirId) -> Option { - self.opt_ident(id).map(|ident| ident.span) + pub(super) fn hir_opt_ident_span(self, id: HirId) -> Option { + self.hir_opt_ident(id).map(|ident| ident.span) } #[inline] - pub fn ident(self, id: HirId) -> Ident { - self.opt_ident(id).unwrap() + pub fn hir_ident(self, id: HirId) -> Ident { + self.hir_opt_ident(id).unwrap() } #[inline] - pub fn opt_name(self, id: HirId) -> Option { - self.opt_ident(id).map(|ident| ident.name) + pub fn hir_opt_name(self, id: HirId) -> Option { + self.hir_opt_ident(id).map(|ident| ident.name) } - pub fn name(self, id: HirId) -> Symbol { - self.opt_name(id).unwrap_or_else(|| bug!("no name for {}", self.node_to_string(id))) + pub fn hir_name(self, id: HirId) -> Symbol { + self.hir_opt_name(id).unwrap_or_else(|| bug!("no name for {}", self.hir_id_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 [Attribute] { - self.tcx.hir_attrs(id.owner).get(id.local_id) + pub fn hir_attrs(self, id: HirId) -> &'tcx [Attribute] { + self.hir_attr_map(id.owner).get(id.local_id) } /// Gets the span of the definition of the specified HIR node. /// This is used by `tcx.def_span`. - pub fn span(self, hir_id: HirId) -> Span { + pub fn hir_span(self, hir_id: HirId) -> Span { fn until_within(outer: Span, end: Span) -> Span { if let Some(end) = end.find_ancestor_inside(outer) { outer.with_hi(end.hi()) @@ -817,21 +882,17 @@ impl<'hir> Map<'hir> { } fn named_span(item_span: Span, ident: Ident, generics: Option<&Generics<'_>>) -> Span { - if ident.name != kw::Empty { - let mut span = until_within(item_span, ident.span); - if let Some(g) = generics - && !g.span.is_dummy() - && let Some(g_span) = g.span.find_ancestor_inside(item_span) - { - span = span.to(g_span); - } - span - } else { - item_span + let mut span = until_within(item_span, ident.span); + if let Some(g) = generics + && !g.span.is_dummy() + && let Some(g_span) = g.span.find_ancestor_inside(item_span) + { + span = span.to(g_span); } + span } - let span = match self.tcx.hir_node(hir_id) { + let span = match self.hir_node(hir_id) { // Function-like. Node::Item(Item { kind: ItemKind::Fn { sig, .. }, span: outer_span, .. }) | Node::TraitItem(TraitItem { @@ -859,7 +920,7 @@ impl<'hir> Map<'hir> { }) => until_within(*outer_span, generics.where_clause_span), // Constants and Statics. Node::Item(Item { - kind: ItemKind::Const(ty, ..) | ItemKind::Static(ty, ..), + kind: ItemKind::Const(_, ty, ..) | ItemKind::Static(_, ty, ..), span: outer_span, .. }) @@ -880,7 +941,7 @@ impl<'hir> Map<'hir> { }) => until_within(*outer_span, ty.span), // With generics and bounds. Node::Item(Item { - kind: ItemKind::Trait(_, _, generics, bounds, _), + kind: ItemKind::Trait(_, _, _, generics, bounds, _), span: outer_span, .. }) @@ -900,12 +961,18 @@ impl<'hir> Map<'hir> { // SyntaxContext of the path. path.span.find_ancestor_in_same_ctxt(item.span).unwrap_or(item.span) } - _ => named_span(item.span, item.ident, item.kind.generics()), + _ => { + if let Some(ident) = item.kind.ident() { + named_span(item.span, ident, item.kind.generics()) + } else { + item.span + } + } }, Node::Variant(variant) => named_span(variant.span, variant.ident, None), Node::ImplItem(item) => named_span(item.span, item.ident, Some(item.generics)), Node::ForeignItem(item) => named_span(item.span, item.ident, None), - Node::Ctor(_) => return self.span(self.tcx.parent_hir_id(hir_id)), + Node::Ctor(_) => return self.hir_span(self.parent_hir_id(hir_id)), Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl_span, .. }), span, @@ -914,16 +981,16 @@ impl<'hir> Map<'hir> { // Ensure that the returned span has the item's SyntaxContext. fn_decl_span.find_ancestor_inside(*span).unwrap_or(*span) } - _ => self.span_with_body(hir_id), + _ => self.hir_span_with_body(hir_id), }; - debug_assert_eq!(span.ctxt(), self.span_with_body(hir_id).ctxt()); + debug_assert_eq!(span.ctxt(), self.hir_span_with_body(hir_id).ctxt()); span } - /// Like `hir.span()`, but includes the body of items + /// Like `hir_span()`, but includes the body of items /// (instead of just the item header) - pub fn span_with_body(self, hir_id: HirId) -> Span { - match self.tcx.hir_node(hir_id) { + pub fn hir_span_with_body(self, hir_id: HirId) -> Span { + match self.hir_node(hir_id) { Node::Param(param) => param.span, Node::Item(item) => item.span, Node::ForeignItem(foreign_item) => foreign_item.span, @@ -932,7 +999,7 @@ impl<'hir> Map<'hir> { Node::Variant(variant) => variant.span, Node::Field(field) => field.span, Node::AnonConst(constant) => constant.span, - Node::ConstBlock(constant) => self.tcx.hir_body(constant.body).value.span, + Node::ConstBlock(constant) => self.hir_body(constant.body).value.span, Node::ConstArg(const_arg) => const_arg.span(), Node::Expr(expr) => expr.span, Node::ExprField(field) => field.span, @@ -952,7 +1019,7 @@ impl<'hir> Map<'hir> { Node::PatExpr(lit) => lit.span, Node::Arm(arm) => arm.span, Node::Block(block) => block.span, - Node::Ctor(..) => self.span_with_body(self.tcx.parent_hir_id(hir_id)), + Node::Ctor(..) => self.hir_span_with_body(self.parent_hir_id(hir_id)), Node::Lifetime(lifetime) => lifetime.ident.span, Node::GenericParam(param) => param.span, Node::Infer(i) => i.span, @@ -965,29 +1032,23 @@ impl<'hir> Map<'hir> { } } - pub fn span_if_local(self, id: DefId) -> Option { - id.is_local().then(|| self.tcx.def_span(id)) + pub fn hir_span_if_local(self, id: DefId) -> Option { + id.is_local().then(|| self.def_span(id)) } - pub fn res_span(self, res: Res) -> Option { + pub fn hir_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()?), + Res::Local(id) => Some(self.hir_span(id)), + res => self.hir_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) - } - /// Returns the HirId of `N` in `struct Foo` when /// called with the HirId for the `{ ... }` anon const - pub fn opt_const_param_default_param_def_id(self, anon_const: HirId) -> Option { - let const_arg = self.tcx.parent_hir_id(anon_const); - match self.tcx.parent_hir_node(const_arg) { + pub fn hir_opt_const_param_default_param_def_id(self, anon_const: HirId) -> Option { + let const_arg = self.parent_hir_id(anon_const); + match self.parent_hir_node(const_arg) { Node::GenericParam(GenericParam { def_id: param_id, kind: GenericParamKind::Const { .. }, @@ -997,7 +1058,7 @@ impl<'hir> Map<'hir> { } } - pub fn maybe_get_struct_pattern_shorthand_field(&self, expr: &Expr<'_>) -> Option { + pub fn hir_maybe_get_struct_pattern_shorthand_field(self, expr: &Expr<'_>) -> Option { let local = match expr { Expr { kind: @@ -1012,7 +1073,7 @@ impl<'hir> Map<'hir> { _ => None, }?; - match self.tcx.parent_hir_node(expr.hir_id) { + match self.parent_hir_node(expr.hir_id) { Node::ExprField(field) => { if field.ident.name == local.name && field.is_shorthand { return Some(local.name); @@ -1103,15 +1164,14 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { debugger_visualizers.hash_stable(&mut hcx, &mut stable_hasher); if tcx.sess.opts.incremental.is_some() { let definitions = tcx.untracked().definitions.freeze(); - let mut owner_spans: Vec<_> = krate - .owners - .iter_enumerated() - .filter_map(|(def_id, info)| { - let _ = info.as_owner()?; + let mut owner_spans: Vec<_> = tcx + .hir_crate_items(()) + .definitions() + .map(|def_id| { let def_path_hash = definitions.def_path_hash(def_id); let span = tcx.source_span(def_id); debug_assert_eq!(span.parent(), None); - Some((def_path_hash, span)) + (def_path_hash, span) }) .collect(); owner_spans.sort_unstable_by_key(|bn| bn.0); @@ -1147,102 +1207,6 @@ fn upstream_crates(tcx: TyCtxt<'_>) -> Vec<(StableCrateId, Svh)> { upstream_crates } -fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { - let path_str = |def_id: LocalDefId| map.tcx.def_path_str(def_id); - - let span_str = || map.tcx.sess.source_map().span_to_snippet(map.span(id)).unwrap_or_default(); - let node_str = |prefix| format!("{id} ({prefix} `{}`)", span_str()); - - match map.tcx.hir_node(id) { - 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::Macro(..) => "macro", - ItemKind::Mod(..) => "mod", - ItemKind::ForeignMod { .. } => "foreign mod", - ItemKind::GlobalAsm { .. } => "global asm", - ItemKind::TyAlias(..) => "ty", - ItemKind::Enum(..) => "enum", - ItemKind::Struct(..) => "struct", - ItemKind::Union(..) => "union", - ItemKind::Trait(..) => "trait", - ItemKind::TraitAlias(..) => "trait alias", - ItemKind::Impl { .. } => "impl", - }; - format!("{id} ({item_str} {})", path_str(item.owner_id.def_id)) - } - Node::ForeignItem(item) => { - format!("{id} (foreign item {})", path_str(item.owner_id.def_id)) - } - Node::ImplItem(ii) => { - let kind = match ii.kind { - ImplItemKind::Const(..) => "associated constant", - ImplItemKind::Fn(fn_sig, _) => match fn_sig.decl.implicit_self { - ImplicitSelfKind::None => "associated function", - _ => "method", - }, - ImplItemKind::Type(_) => "associated type", - }; - format!("{id} ({kind} `{}` in {})", ii.ident, path_str(ii.owner_id.def_id)) - } - Node::TraitItem(ti) => { - let kind = match ti.kind { - TraitItemKind::Const(..) => "associated constant", - TraitItemKind::Fn(fn_sig, _) => match fn_sig.decl.implicit_self { - ImplicitSelfKind::None => "associated function", - _ => "trait method", - }, - TraitItemKind::Type(..) => "associated type", - }; - - format!("{id} ({kind} `{}` in {})", ti.ident, path_str(ti.owner_id.def_id)) - } - Node::Variant(variant) => { - format!("{id} (variant `{}` in {})", variant.ident, path_str(variant.def_id)) - } - Node::Field(field) => { - format!("{id} (field `{}` in {})", field.ident, path_str(field.def_id)) - } - Node::AnonConst(_) => node_str("const"), - Node::ConstBlock(_) => node_str("const"), - Node::ConstArg(_) => node_str("const"), - Node::Expr(_) => node_str("expr"), - Node::ExprField(_) => node_str("expr field"), - Node::Stmt(_) => node_str("stmt"), - Node::PathSegment(_) => node_str("path segment"), - Node::Ty(_) => node_str("type"), - Node::AssocItemConstraint(_) => node_str("assoc item constraint"), - Node::TraitRef(_) => node_str("trait ref"), - Node::OpaqueTy(_) => node_str("opaque type"), - Node::Pat(_) => node_str("pat"), - Node::TyPat(_) => node_str("pat ty"), - Node::PatField(_) => node_str("pattern field"), - Node::PatExpr(_) => node_str("pattern literal"), - Node::Param(_) => node_str("param"), - Node::Arm(_) => node_str("arm"), - Node::Block(_) => node_str("block"), - Node::Infer(_) => node_str("infer"), - Node::LetStmt(_) => node_str("local"), - Node::Ctor(ctor) => format!( - "{id} (ctor {})", - ctor.ctor_def_id().map_or("".into(), |def_id| path_str(def_id)), - ), - Node::Lifetime(_) => node_str("lifetime"), - Node::GenericParam(param) => { - format!("{id} (generic_param {})", path_str(param.def_id)) - } - Node::Crate(..) => String::from("(root_crate)"), - Node::WherePredicate(_) => node_str("where predicate"), - Node::Synthetic => unreachable!(), - Node::Err(_) => node_str("error"), - Node::PreciseCapturingNonLifetimeArg(_param) => node_str("parameter"), - } -} - pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> ModuleItems { let mut collector = ItemCollector::new(tcx, false); @@ -1352,11 +1316,11 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { self.items.push(item.item_id()); // Items that are modules are handled here instead of in visit_mod. - if let ItemKind::Mod(module) = &item.kind { + if let ItemKind::Mod(_, module) = &item.kind { self.submodules.push(item.owner_id); // A module collector does not recurse inside nested modules. if self.crate_collector { - intravisit::walk_mod(self, module, item.hir_id()); + intravisit::walk_mod(self, module); } } else { intravisit::walk_item(self, item) diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 6071a58367ed1..a28dcb0cb8efd 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -14,7 +14,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::*; use rustc_macros::{Decodable, Encodable, HashStable}; -use rustc_span::{ErrorGuaranteed, ExpnId}; +use rustc_span::{ErrorGuaranteed, ExpnId, Span}; use crate::query::Providers; use crate::ty::{EarlyBinder, ImplSubject, TyCtxt}; @@ -83,44 +83,39 @@ impl ModuleItems { &self, f: impl Fn(ItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, ) -> Result<(), ErrorGuaranteed> { - try_par_for_each_in(&self.free_items[..], |&id| f(id)) + try_par_for_each_in(&self.free_items[..], |&&id| f(id)) } pub fn par_trait_items( &self, f: impl Fn(TraitItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, ) -> Result<(), ErrorGuaranteed> { - try_par_for_each_in(&self.trait_items[..], |&id| f(id)) + try_par_for_each_in(&self.trait_items[..], |&&id| f(id)) } pub fn par_impl_items( &self, f: impl Fn(ImplItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, ) -> Result<(), ErrorGuaranteed> { - try_par_for_each_in(&self.impl_items[..], |&id| f(id)) + try_par_for_each_in(&self.impl_items[..], |&&id| f(id)) } pub fn par_foreign_items( &self, f: impl Fn(ForeignItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, ) -> Result<(), ErrorGuaranteed> { - try_par_for_each_in(&self.foreign_items[..], |&id| f(id)) + try_par_for_each_in(&self.foreign_items[..], |&&id| f(id)) } pub fn par_opaques( &self, f: impl Fn(LocalDefId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, ) -> Result<(), ErrorGuaranteed> { - try_par_for_each_in(&self.opaques[..], |&id| f(id)) + try_par_for_each_in(&self.opaques[..], |&&id| f(id)) } } impl<'tcx> TyCtxt<'tcx> { - #[inline(always)] - pub fn hir(self) -> map::Map<'tcx> { - map::Map { tcx: self } - } - pub fn parent_module(self, id: HirId) -> LocalModDefId { if !id.is_owner() && self.def_kind(id.owner) == DefKind::Mod { LocalModDefId::new_unchecked(id.owner.def_id) @@ -157,6 +152,7 @@ impl<'tcx> TyCtxt<'tcx> { node: OwnerNode<'_>, bodies: &SortedMap>, attrs: &SortedMap, + define_opaque: Option<&[(Span, LocalDefId)]>, ) -> (Option, Option) { if self.needs_crate_hash() { self.with_stable_hashing_context(|mut hcx| { @@ -168,6 +164,10 @@ impl<'tcx> TyCtxt<'tcx> { let mut stable_hasher = StableHasher::new(); attrs.hash_stable(&mut hcx, &mut stable_hasher); + + // Hash the defined opaque types, which are not present in the attrs. + define_opaque.hash_stable(&mut hcx, &mut stable_hasher); + let h2 = stable_hasher.finish(); (Some(h1), Some(h2)) }) @@ -202,18 +202,17 @@ pub fn provide(providers: &mut Providers) { } }) }; - providers.hir_attrs = |tcx, id| { + providers.hir_attr_map = |tcx, id| { tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs) }; - providers.def_span = |tcx, def_id| tcx.hir().span(tcx.local_def_id_to_hir_id(def_id)); + providers.def_span = |tcx, def_id| tcx.hir_span(tcx.local_def_id_to_hir_id(def_id)); providers.def_ident_span = |tcx, def_id| { let hir_id = tcx.local_def_id_to_hir_id(def_id); - tcx.hir().opt_ident_span(hir_id) + tcx.hir_opt_ident_span(hir_id) }; - providers.fn_arg_names = |tcx, def_id| { - let hir = tcx.hir(); + providers.fn_arg_idents = |tcx, def_id| { if let Some(body_id) = tcx.hir_node_by_def_id(def_id).body_id() { - tcx.arena.alloc_from_iter(tcx.hir_body_param_names(body_id)) + tcx.arena.alloc_from_iter(tcx.hir_body_param_idents(body_id)) } else if let Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(_, TraitFn::Required(idents)), .. @@ -226,13 +225,15 @@ pub fn provide(providers: &mut Providers) { idents } else { span_bug!( - hir.span(tcx.local_def_id_to_hir_id(def_id)), - "fn_arg_names: unexpected item {:?}", + tcx.hir_span(tcx.local_def_id_to_hir_id(def_id)), + "fn_arg_idents: unexpected item {:?}", def_id ); } }; providers.all_local_trait_impls = |tcx, ()| &tcx.resolutions(()).trait_impls; + providers.local_trait_impls = + |tcx, trait_id| tcx.resolutions(()).trait_impls.get(&trait_id).map_or(&[], |xs| &xs[..]); providers.expn_that_defined = |tcx, id| tcx.resolutions(()).expn_that_defined.get(&id).copied().unwrap_or(ExpnId::root()); providers.in_scope_traits_map = |tcx, id| { diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs index 316ad80eb9857..c3d10615cf10c 100644 --- a/compiler/rustc_middle/src/hir/place.rs +++ b/compiler/rustc_middle/src/hir/place.rs @@ -40,6 +40,8 @@ pub enum ProjectionKind { /// A conversion from an opaque type to its hidden type so we can /// do further projections on it. + /// + /// This is unused if `-Znext-solver` is enabled. OpaqueCast, } @@ -53,7 +55,10 @@ pub struct Projection<'tcx> { pub kind: ProjectionKind, } -/// A `Place` represents how a value is located in memory. +/// A `Place` represents how a value is located in memory. This does not +/// always correspond to a syntactic place expression. For example, when +/// processing a pattern, a `Place` can be used to refer to the sub-value +/// currently being inspected. /// /// This is an HIR version of [`rustc_middle::mir::Place`]. #[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] @@ -67,7 +72,10 @@ pub struct Place<'tcx> { pub projections: Vec>, } -/// A `PlaceWithHirId` represents how a value is located in memory. +/// A `PlaceWithHirId` represents how a value is located in memory. This does not +/// always correspond to a syntactic place expression. For example, when +/// processing a pattern, a `Place` can be used to refer to the sub-value +/// currently being inspected. /// /// This is an HIR version of [`rustc_middle::mir::Place`]. #[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 3cd148cd44278..5b8603744961c 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -39,15 +39,6 @@ pub type CanonicalVarInfo<'tcx> = ir::CanonicalVarInfo>; pub type CanonicalVarValues<'tcx> = ir::CanonicalVarValues>; pub type CanonicalVarInfos<'tcx> = &'tcx List>; -impl<'tcx> ty::TypeFoldable> for CanonicalVarInfos<'tcx> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - ty::util::fold_list(self, folder, |tcx, v| tcx.mk_canonical_var_infos(v)) - } -} - /// When we canonicalize a value to form a query, we wind up replacing /// various parts of it with canonical variables. This struct stores /// those replaced bits to remember for when we process the query diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 48ea7df5c23fd..cb3fdd4d3f76e 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -27,7 +27,6 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] -#![allow(rustc::potential_query_instability)] #![allow(rustc::untranslatable_diagnostic)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] @@ -48,7 +47,6 @@ #![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(iter_from_coroutine)] -#![feature(let_chains)] #![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] @@ -61,7 +59,7 @@ #![feature(try_trait_v2_yeet)] #![feature(type_alias_impl_trait)] #![feature(yeet_expr)] -#![warn(unreachable_pub)] +#![recursion_limit = "256"] // tidy-alphabetical-end #[cfg(test)] diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 88bf17070b9c5..d5a408fdfa6a3 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -51,8 +51,13 @@ impl LintLevelSource { } } -/// A tuple of a lint level and its source. -pub type LevelAndSource = (Level, LintLevelSource); +/// Convenience helper for moving things around together that frequently are paired +#[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)] +pub struct LevelAndSource { + pub level: Level, + pub lint_id: Option, + pub src: LintLevelSource, +} /// Return type for the `shallow_lint_levels_on` query. /// @@ -69,14 +74,18 @@ pub struct ShallowLintLevelMap { /// /// The return of this function is suitable for diagnostics. pub fn reveal_actual_level( - level: Option, + level: Option<(Level, Option)>, src: &mut LintLevelSource, sess: &Session, lint: LintId, - probe_for_lint_level: impl FnOnce(LintId) -> (Option, LintLevelSource), -) -> Level { + probe_for_lint_level: impl FnOnce( + LintId, + ) + -> (Option<(Level, Option)>, LintLevelSource), +) -> (Level, Option) { // If `level` is none then we actually assume the default level for this lint. - let mut level = level.unwrap_or_else(|| lint.lint.default_level(sess.edition())); + let (mut level, mut lint_id) = + level.unwrap_or_else(|| (lint.lint.default_level(sess.edition()), None)); // If we're about to issue a warning, check at the last minute for any // directives against the warnings "lint". If, for example, there's an @@ -88,16 +97,17 @@ pub fn reveal_actual_level( // future compatibility warning. if level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) { let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS)); - if let Some(configured_warning_level) = warnings_level { + if let Some((configured_warning_level, configured_lint_id)) = warnings_level { if configured_warning_level != Level::Warn { level = configured_warning_level; + lint_id = configured_lint_id; *src = warnings_src; } } } // Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn - level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src { + level = if let LintLevelSource::CommandLine(_, Level::ForceWarn) = src { level } else { cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid)) @@ -108,7 +118,7 @@ pub fn reveal_actual_level( level = cmp::min(*driver_level, level); } - level + (level, lint_id) } impl ShallowLintLevelMap { @@ -121,11 +131,11 @@ impl ShallowLintLevelMap { tcx: TyCtxt<'_>, id: LintId, start: HirId, - ) -> (Option, LintLevelSource) { + ) -> (Option<(Level, Option)>, LintLevelSource) { if let Some(map) = self.specs.get(&start.local_id) - && let Some(&(level, src)) = map.get(&id) + && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id) { - return (Some(level), src); + return (Some((level, lint_id)), src); } let mut owner = start.owner; @@ -137,9 +147,9 @@ impl ShallowLintLevelMap { specs = &tcx.shallow_lint_levels_on(owner).specs; } if let Some(map) = specs.get(&parent.local_id) - && let Some(&(level, src)) = map.get(&id) + && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id) { - return (Some(level), src); + return (Some((level, lint_id)), src); } } @@ -153,18 +163,18 @@ impl ShallowLintLevelMap { tcx: TyCtxt<'_>, lint: LintId, cur: HirId, - ) -> (Level, LintLevelSource) { + ) -> LevelAndSource { let (level, mut src) = self.probe_for_lint_level(tcx, lint, cur); - let level = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| { + let (level, lint_id) = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| { self.probe_for_lint_level(tcx, lint, cur) }); - (level, src) + LevelAndSource { level, lint_id, src } } } impl TyCtxt<'_> { /// Fetch and return the user-visible lint level for the given lint at the given HirId. - pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> (Level, LintLevelSource) { + pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> LevelAndSource { self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(self, LintId::of(lint), id) } } @@ -267,8 +277,7 @@ fn explain_lint_level_source( pub fn lint_level( sess: &Session, lint: &'static Lint, - level: Level, - src: LintLevelSource, + level: LevelAndSource, span: Option, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), ) { @@ -278,11 +287,12 @@ pub fn lint_level( fn lint_level_impl( sess: &Session, lint: &'static Lint, - level: Level, - src: LintLevelSource, + level: LevelAndSource, span: Option, decorate: Box FnOnce(&'b mut Diag<'a, ()>)>, ) { + let LevelAndSource { level, lint_id, src } = level; + // Check for future incompatibility lints and issue a stronger warning. let future_incompatible = lint.future_incompatible; @@ -301,7 +311,7 @@ pub fn lint_level( return; } } - Level::Expect(expect_id) => { + Level::Expect => { // This case is special as we actually allow the lint itself in this context, but // we can't return early like in the case for `Level::Allow` because we still // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`. @@ -309,10 +319,9 @@ pub fn lint_level( // We can also not mark the lint expectation as fulfilled here right away, as it // can still be cancelled in the decorate function. All of this means that we simply // create a `Diag` and continue as we would for warnings. - rustc_errors::Level::Expect(expect_id) + rustc_errors::Level::Expect } - Level::ForceWarn(Some(expect_id)) => rustc_errors::Level::ForceWarning(Some(expect_id)), - Level::ForceWarn(None) => rustc_errors::Level::ForceWarning(None), + Level::ForceWarn => rustc_errors::Level::ForceWarning, Level::Warn => rustc_errors::Level::Warning, Level::Deny | Level::Forbid => rustc_errors::Level::Error, }; @@ -320,6 +329,9 @@ pub fn lint_level( if let Some(span) = span { err.span(span); } + if let Some(lint_id) = lint_id { + err.lint_id(lint_id); + } // If this code originates in a foreign macro, aka something that this crate // did not itself author, then it's likely that there's nothing this crate @@ -350,7 +362,7 @@ pub fn lint_level( // the compiler. It is therefore not necessary to add any information for the user. // This will therefore directly call the decorate function which will in turn emit // the diagnostic. - if let Level::Expect(_) = level { + if let Level::Expect = level { decorate(&mut err); err.emit(); return; @@ -419,5 +431,5 @@ pub fn lint_level( explain_lint_level_source(lint, level, src, &mut err); err.emit() } - lint_level_impl(sess, lint, level, src, span, Box::new(decorate)) + lint_level_impl(sess, lint, level, span, Box::new(decorate)) } diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index b5f3a0e1482a9..0ae774ebee795 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -59,8 +59,8 @@ macro_rules! TrivialLiftImpls { macro_rules! TrivialTypeTraversalImpls { ($($ty:ty),+ $(,)?) => { $( - impl<'tcx> $crate::ty::fold::TypeFoldable<$crate::ty::TyCtxt<'tcx>> for $ty { - fn try_fold_with>>( + impl<'tcx> $crate::ty::TypeFoldable<$crate::ty::TyCtxt<'tcx>> for $ty { + fn try_fold_with>>( self, _: &mut F, ) -> ::std::result::Result { @@ -68,7 +68,7 @@ macro_rules! TrivialTypeTraversalImpls { } #[inline] - fn fold_with>>( + fn fold_with>>( self, _: &mut F, ) -> Self { @@ -76,14 +76,14 @@ macro_rules! TrivialTypeTraversalImpls { } } - impl<'tcx> $crate::ty::visit::TypeVisitable<$crate::ty::TyCtxt<'tcx>> for $ty { + impl<'tcx> $crate::ty::TypeVisitable<$crate::ty::TyCtxt<'tcx>> for $ty { #[inline] - fn visit_with>>( + fn visit_with>>( &self, _: &mut F) -> F::Result { - ::output() + ::output() } } )+ diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 311bc60c3cd39..00da1a6aeec7d 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,6 +1,6 @@ use rustc_abi::Align; use rustc_ast::expand::autodiff_attrs::AutoDiffAttrs; -use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_attr_data_structures::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; @@ -172,8 +172,11 @@ impl CodegenFnAttrs { /// * `#[no_mangle]` is present /// * `#[export_name(...)]` is present /// * `#[linkage]` is present + /// + /// Keep this in sync with the logic for the unused_attributes for `#[inline]` lint. pub fn contains_extern_indicator(&self) -> bool { self.flags.contains(CodegenFnAttrFlags::NO_MANGLE) + || self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) || self.export_name.is_some() || match self.linkage { // These are private, so make sure we don't try to consider diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index 0bfbd39879747..1d67d0fe3bbf4 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -44,6 +44,7 @@ pub enum ExportedSymbol<'tcx> { Generic(DefId, GenericArgsRef<'tcx>), DropGlue(Ty<'tcx>), AsyncDropGlueCtorShim(Ty<'tcx>), + AsyncDropGlue(DefId, Ty<'tcx>), ThreadLocalShim(DefId), NoDefId(ty::SymbolName<'tcx>), } @@ -55,7 +56,7 @@ impl<'tcx> ExportedSymbol<'tcx> { match *self { ExportedSymbol::NonGeneric(def_id) => tcx.symbol_name(ty::Instance::mono(tcx, def_id)), ExportedSymbol::Generic(def_id, args) => { - tcx.symbol_name(ty::Instance::new(def_id, args)) + tcx.symbol_name(ty::Instance::new_raw(def_id, args)) } ExportedSymbol::DropGlue(ty) => { tcx.symbol_name(ty::Instance::resolve_drop_in_place(tcx, ty)) @@ -63,6 +64,9 @@ impl<'tcx> ExportedSymbol<'tcx> { ExportedSymbol::AsyncDropGlueCtorShim(ty) => { tcx.symbol_name(ty::Instance::resolve_async_drop_in_place(tcx, ty)) } + ExportedSymbol::AsyncDropGlue(def_id, ty) => { + tcx.symbol_name(ty::Instance::resolve_async_drop_in_place_poll(tcx, def_id, ty)) + } ExportedSymbol::ThreadLocalShim(def_id) => tcx.symbol_name(ty::Instance { def: ty::InstanceKind::ThreadLocalShim(def_id), args: ty::GenericArgs::empty(), diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index 7a91bfad4836c..0f92c1910f1bb 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -35,11 +35,10 @@ impl<'tcx> TyCtxt<'tcx> { /// returns a corresponding [`ty::ClosureKind`]. /// For any other [`DefId`] return `None`. pub fn fn_trait_kind_from_def_id(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), + match self.as_lang_item(id)? { + LangItem::Fn => Some(ty::ClosureKind::Fn), + LangItem::FnMut => Some(ty::ClosureKind::FnMut), + LangItem::FnOnce => Some(ty::ClosureKind::FnOnce), _ => None, } } @@ -48,11 +47,10 @@ impl<'tcx> TyCtxt<'tcx> { /// returns a corresponding [`ty::ClosureKind`]. /// For any other [`DefId`] return `None`. pub fn async_fn_trait_kind_from_def_id(self, id: DefId) -> Option { - let items = self.lang_items(); - match Some(id) { - x if x == items.async_fn_trait() => Some(ty::ClosureKind::Fn), - x if x == items.async_fn_mut_trait() => Some(ty::ClosureKind::FnMut), - x if x == items.async_fn_once_trait() => Some(ty::ClosureKind::FnOnce), + match self.as_lang_item(id)? { + LangItem::AsyncFn => Some(ty::ClosureKind::Fn), + LangItem::AsyncFnMut => Some(ty::ClosureKind::FnMut), + LangItem::AsyncFnOnce => Some(ty::ClosureKind::FnOnce), _ => None, } } diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 66861519e17c8..92eab59dd0274 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -175,7 +175,7 @@ impl Scope { let Some(hir_id) = self.hir_id(scope_tree) else { return DUMMY_SP; }; - let span = tcx.hir().span(hir_id); + let span = tcx.hir_span(hir_id); if let ScopeData::Remainder(first_statement_index) = self.data { if let Node::Block(blk) = tcx.hir_node(hir_id) { // Want span for scope starting after the @@ -199,8 +199,6 @@ impl Scope { } } -pub type ScopeDepth = u32; - /// The region scope tree encodes information about region relationships. #[derive(Default, Debug, HashStable)] pub struct ScopeTree { @@ -213,7 +211,7 @@ pub struct ScopeTree { /// 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: FxIndexMap, + pub parent_map: FxIndexMap, /// Maps from a variable or binding ID to the block in which that /// variable is declared. @@ -224,7 +222,7 @@ pub struct ScopeTree { /// and not the enclosing *statement*. Expressions that are not present in this /// table are not rvalue candidates. The set of rvalue candidates is computed /// during type check based on a traversal of the AST. - pub rvalue_candidates: HirIdMap, + pub rvalue_candidates: HirIdMap, /// Backwards incompatible scoping that will be introduced in future editions. /// This information is used later for linting to identify locals and @@ -308,15 +306,14 @@ pub struct ScopeTree { pub yield_in_scope: UnordMap>, } -/// Identifies the reason that a given expression is an rvalue candidate -/// (see the `rvalue_candidates` field for more information what rvalue -/// candidates in general). In constants, the `lifetime` field is None -/// to indicate that certain expressions escape into 'static and -/// should have no local cleanup scope. +/// See the `rvalue_candidates` field for more information on rvalue +/// candidates in general. +/// The `lifetime` field is None to indicate that certain expressions escape +/// into 'static and should have no local cleanup scope. #[derive(Debug, Copy, Clone, HashStable)] -pub enum RvalueCandidateType { - Borrow { target: hir::ItemLocalId, lifetime: Option }, - Pattern { target: hir::ItemLocalId, lifetime: Option }, +pub struct RvalueCandidate { + pub target: hir::ItemLocalId, + pub lifetime: Option, } #[derive(Debug, Copy, Clone, HashStable)] @@ -329,7 +326,7 @@ pub struct YieldData { } impl ScopeTree { - pub fn record_scope_parent(&mut self, child: Scope, parent: Option<(Scope, ScopeDepth)>) { + pub fn record_scope_parent(&mut self, child: Scope, parent: Option) { debug!("{:?}.parent = {:?}", child, parent); if let Some(p) = parent { @@ -344,21 +341,17 @@ impl ScopeTree { self.var_map.insert(var, lifetime); } - pub fn record_rvalue_candidate(&mut self, var: HirId, candidate_type: RvalueCandidateType) { - debug!("record_rvalue_candidate(var={var:?}, type={candidate_type:?})"); - match &candidate_type { - RvalueCandidateType::Borrow { lifetime: Some(lifetime), .. } - | RvalueCandidateType::Pattern { lifetime: Some(lifetime), .. } => { - assert!(var.local_id != lifetime.local_id) - } - _ => {} + pub fn record_rvalue_candidate(&mut self, var: HirId, candidate: RvalueCandidate) { + debug!("record_rvalue_candidate(var={var:?}, candidate={candidate:?})"); + if let Some(lifetime) = &candidate.lifetime { + assert!(var.local_id != lifetime.local_id) } - self.rvalue_candidates.insert(var, candidate_type); + self.rvalue_candidates.insert(var, candidate); } /// 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) + self.parent_map.get(&id).cloned() } /// Returns the lifetime of the local variable `var_id`, if any. diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 2260cad41b97c..454ab8c107f0a 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -4,8 +4,8 @@ use std::num::NonZero; use rustc_ast::NodeId; -use rustc_attr_parsing::{ - self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability, +use rustc_attr_data_structures::{ + self as attrs, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability, }; use rustc_data_structures::unord::UnordMap; use rustc_errors::{Applicability, Diag, EmissionGuarantee}; @@ -255,7 +255,7 @@ fn late_report_deprecation( // Calculating message for lint involves calling `self.def_path_str`, // which will by default invoke the expensive `visible_parent_map` query. // Skip all that work if the lint is allowed anyway. - if tcx.lint_level_at_node(lint, hir_id).0 == Level::Allow { + if tcx.lint_level_at_node(lint, hir_id).level == Level::Allow { return; } @@ -411,7 +411,7 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(Stability { - level: attr::StabilityLevel::Unstable { reason, issue, is_soft, implied_by }, + level: attrs::StabilityLevel::Unstable { reason, issue, is_soft, implied_by }, feature, .. }) => { @@ -494,7 +494,7 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(DefaultBodyStability { - level: attr::StabilityLevel::Unstable { reason, issue, is_soft, .. }, + level: attrs::StabilityLevel::Unstable { reason, issue, is_soft, .. }, feature, }) => { if span.allows_unstable(feature) { @@ -643,7 +643,7 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(ConstStability { - level: attr::StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. }, + level: attrs::StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. }, feature, .. }) => { diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index 171542d1279c7..d0dbf64dc5959 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -1,8 +1,9 @@ +use std::sync::OnceLock; + use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph; use rustc_data_structures::graph::dominators::{Dominators, dominators}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::OnceLock; use rustc_index::{IndexSlice, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 923160cc0cc85..2b2ffa7162880 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -6,10 +6,12 @@ use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, Typ use rustc_session::RemapFileNameExt; use rustc_session::config::RemapPathScopeComponents; use rustc_span::{DUMMY_SP, Span, Symbol}; -use rustc_type_ir::visit::TypeVisitableExt; +use rustc_type_ir::TypeVisitableExt; use super::interpret::ReportedErrorInfo; -use crate::mir::interpret::{AllocId, ConstAllocation, ErrorHandled, Scalar, alloc_range}; +use crate::mir::interpret::{ + AllocId, AllocRange, ConstAllocation, ErrorHandled, GlobalAlloc, Scalar, alloc_range, +}; use crate::mir::{Promoted, pretty_print_const_value}; use crate::ty::print::{pretty_print_const, with_no_trimmed_paths}; use crate::ty::{self, ConstKind, GenericArgsRef, ScalarInt, Ty, TyCtxt}; @@ -192,9 +194,31 @@ impl<'tcx> ConstValue<'tcx> { .unwrap_memory() .inner() .provenance() - .range_empty(super::AllocRange::from(offset..offset + size), &tcx), + .range_empty(AllocRange::from(offset..offset + size), &tcx), } } + + /// Check if a constant only contains uninitialized bytes. + pub fn all_bytes_uninit(&self, tcx: TyCtxt<'tcx>) -> bool { + let ConstValue::Indirect { alloc_id, .. } = self else { + return false; + }; + let alloc = tcx.global_alloc(*alloc_id); + let GlobalAlloc::Memory(alloc) = alloc else { + return false; + }; + let init_mask = alloc.0.init_mask(); + let init_range = init_mask.is_range_initialized(AllocRange { + start: Size::ZERO, + size: Size::from_bytes(alloc.0.len()), + }); + if let Err(range) = init_range { + if range.size == alloc.0.size() { + return true; + } + } + false + } } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 8c6b11a681ef6..e26575b552ee1 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -194,7 +194,6 @@ pub struct Mapping { #[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct FunctionCoverageInfo { pub function_source_hash: u64, - pub body_span: Span, /// Used in conjunction with `priority_list` to create physical counters /// and counter expressions, after MIR optimizations. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 95bc9b71fe0ad..57aafbb26bc8b 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -15,7 +15,8 @@ use provenance_map::*; use rustc_abi::{Align, HasDataLayout, Size}; use rustc_ast::Mutability; use rustc_data_structures::intern::Interned; -use rustc_macros::{HashStable, TyDecodable, TyEncodable}; +use rustc_macros::HashStable; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use super::{ AllocId, BadBytesAccess, CtfeProvenance, InterpErrorKind, InterpResult, Pointer, @@ -77,7 +78,7 @@ impl AllocBytes for Box<[u8]> { /// module provides higher-level access. // Note: for performance reasons when interning, some of the `Allocation` fields can be partially // hashed. (see the `Hash` impl below for more details), so the impl is not derived. -#[derive(Clone, Eq, PartialEq, TyEncodable, TyDecodable)] +#[derive(Clone, Eq, PartialEq)] #[derive(HashStable)] pub struct Allocation> { /// The actual bytes of the allocation. @@ -101,6 +102,117 @@ pub struct Allocation Encodable for AllocFlags { + fn encode(&self, encoder: &mut E) { + // Make sure Align::MAX can be stored with the high 2 bits unset. + const { + let max_supported_align_repr = u8::MAX >> 2; + let max_supported_align = 1 << max_supported_align_repr; + assert!(Align::MAX.bytes() <= max_supported_align) + } + + let mut flags = self.align.bytes().trailing_zeros() as u8; + flags |= match self.mutability { + Mutability::Not => 0, + Mutability::Mut => 1 << 6, + }; + flags |= (self.all_zero as u8) << 7; + flags.encode(encoder); + } +} + +impl Decodable for AllocFlags { + fn decode(decoder: &mut D) -> Self { + let flags: u8 = Decodable::decode(decoder); + let align = flags & 0b0011_1111; + let mutability = flags & 0b0100_0000; + let all_zero = flags & 0b1000_0000; + + let align = Align::from_bytes(1 << align).unwrap(); + let mutability = match mutability { + 0 => Mutability::Not, + _ => Mutability::Mut, + }; + let all_zero = all_zero > 0; + + AllocFlags { align, mutability, all_zero } + } +} + +/// Efficiently detect whether a slice of `u8` is all zero. +/// +/// This is used in encoding of [`Allocation`] to special-case all-zero allocations. It is only +/// optimized a little, because for many allocations the encoding of the actual bytes does not +/// dominate runtime. +#[inline] +fn all_zero(buf: &[u8]) -> bool { + // In the empty case we wouldn't encode any contents even without this system where we + // special-case allocations whose contents are all 0. We can return anything in the empty case. + if buf.is_empty() { + return true; + } + // Just fast-rejecting based on the first element significantly reduces the amount that we end + // up walking the whole array. + if buf[0] != 0 { + return false; + } + + // This strategy of combining all slice elements with & or | is unbeatable for the large + // all-zero case because it is so well-understood by autovectorization. + buf.iter().fold(true, |acc, b| acc & (*b == 0)) +} + +/// Custom encoder for [`Allocation`] to more efficiently represent the case where all bytes are 0. +impl Encodable for Allocation +where + Bytes: AllocBytes, + ProvenanceMap: Encodable, + Extra: Encodable, +{ + fn encode(&self, encoder: &mut E) { + let all_zero = all_zero(&self.bytes); + AllocFlags { align: self.align, mutability: self.mutability, all_zero }.encode(encoder); + + encoder.emit_usize(self.bytes.len()); + if !all_zero { + encoder.emit_raw_bytes(&self.bytes); + } + self.provenance.encode(encoder); + self.init_mask.encode(encoder); + self.extra.encode(encoder); + } +} + +impl Decodable for Allocation +where + Bytes: AllocBytes, + ProvenanceMap: Decodable, + Extra: Decodable, +{ + fn decode(decoder: &mut D) -> Self { + let AllocFlags { align, mutability, all_zero } = Decodable::decode(decoder); + + let len = decoder.read_usize(); + let bytes = if all_zero { vec![0u8; len] } else { decoder.read_raw_bytes(len).to_vec() }; + let bytes = Bytes::from_bytes(bytes, align); + + let provenance = Decodable::decode(decoder); + let init_mask = Decodable::decode(decoder); + let extra = Decodable::decode(decoder); + + Self { bytes, provenance, init_mask, align, mutability, extra } + } +} + /// This is the maximum size we will hash at a time, when interning an `Allocation` and its /// `InitMask`. Note, we hash that amount of bytes twice: at the start, and at the end of a buffer. /// Used when these two structures are large: we only partially hash the larger fields in that @@ -222,7 +334,7 @@ impl AllocError { } /// The information that makes up a memory access: offset and size. -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone)] pub struct AllocRange { pub start: Size, pub size: Size, @@ -470,7 +582,7 @@ impl Allocation // Find the provenance. let (offset, _prov) = self .provenance - .range_get_ptrs(range, cx) + .range_ptrs_get(range, cx) .first() .copied() .expect("there must be provenance somewhere here"); @@ -679,6 +791,11 @@ impl Allocation // Set provenance of all bytes to wildcard. self.provenance.write_wildcards(self.len()); + // Also expose the provenance of the interpreter-level allocation, so it can + // be written by FFI. The `black_box` is defensive programming as LLVM likes + // to (incorrectly) optimize away ptr2int casts whose result is unused. + std::hint::black_box(self.get_bytes_unchecked_raw_mut().expose_provenance()); + Ok(()) } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs b/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs index cc6389e2989ef..20492cda4e23c 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs @@ -5,9 +5,8 @@ use std::ops::Range; use std::{hash, iter}; use rustc_abi::Size; -use rustc_macros::{HashStable, TyDecodable, TyEncodable}; -use rustc_serialize::{Decodable, Encodable}; -use rustc_type_ir::{TyDecoder, TyEncoder}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable}; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use super::AllocRange; @@ -19,13 +18,13 @@ type Block = u64; /// possible. Currently, if all the blocks have the same value, then the mask represents either a /// fully initialized or fully uninitialized const allocation, so we can only store that single /// value. -#[derive(Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(Clone, Debug, Eq, PartialEq, Encodable_NoContext, Decodable_NoContext, Hash, HashStable)] pub struct InitMask { blocks: InitMaskBlocks, len: Size, } -#[derive(Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(Clone, Debug, Eq, PartialEq, Encodable_NoContext, Decodable_NoContext, Hash, HashStable)] enum InitMaskBlocks { Lazy { /// Whether the lazy init mask is fully initialized or uninitialized. @@ -194,7 +193,7 @@ struct InitMaskMaterialized { // and also produces more output when the high bits of each `u64` are occupied. // Note: There is probably a remaining optimization for masks that do not use an entire // `Block`. -impl Encodable for InitMaskMaterialized { +impl Encodable for InitMaskMaterialized { fn encode(&self, encoder: &mut E) { encoder.emit_usize(self.blocks.len()); for block in &self.blocks { @@ -204,7 +203,7 @@ impl Encodable for InitMaskMaterialized { } // This implementation is deliberately not derived, see the matching `Encodable` impl. -impl Decodable for InitMaskMaterialized { +impl Decodable for InitMaskMaterialized { fn decode(decoder: &mut D) -> Self { let num_blocks = decoder.read_usize(); let mut blocks = Vec::with_capacity(num_blocks); @@ -223,8 +222,8 @@ impl Decodable for InitMaskMaterialized { // large. impl hash::Hash for InitMaskMaterialized { fn hash(&self, state: &mut H) { - const MAX_BLOCKS_TO_HASH: usize = super::MAX_BYTES_TO_HASH / std::mem::size_of::(); - const MAX_BLOCKS_LEN: usize = super::MAX_HASHED_BUFFER_LEN / std::mem::size_of::(); + const MAX_BLOCKS_TO_HASH: usize = super::MAX_BYTES_TO_HASH / size_of::(); + const MAX_BLOCKS_LEN: usize = super::MAX_HASHED_BUFFER_LEN / size_of::(); // Partially hash the `blocks` buffer when it is large. To limit collisions with common // prefixes and suffixes, we hash the length and some slices of the buffer. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 82fb5f33b4c81..c9525df1f7940 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -67,7 +67,7 @@ impl ProvenanceMap { } impl ProvenanceMap { - fn adjusted_range(range: AllocRange, cx: &impl HasDataLayout) -> Range { + fn adjusted_range_ptrs(range: AllocRange, cx: &impl HasDataLayout) -> Range { // We have to go back `pointer_size - 1` bytes, as that one would still overlap with // the beginning of this range. let adjusted_start = Size::from_bytes( @@ -79,26 +79,21 @@ impl ProvenanceMap { /// Returns all ptr-sized provenance in the given range. /// If the range has length 0, returns provenance that crosses the edge between `start-1` and /// `start`. - pub(super) fn range_get_ptrs( + pub(super) fn range_ptrs_get( &self, range: AllocRange, cx: &impl HasDataLayout, ) -> &[(Size, Prov)] { - self.ptrs.range(Self::adjusted_range(range, cx)) + self.ptrs.range(Self::adjusted_range_ptrs(range, cx)) } - /// `pm.range_get_ptrs_is_empty(r, cx)` == `pm.range_get_ptrs(r, cx).is_empty()`, but is - /// faster. - pub(super) fn range_get_ptrs_is_empty( - &self, - range: AllocRange, - cx: &impl HasDataLayout, - ) -> bool { - self.ptrs.range_is_empty(Self::adjusted_range(range, cx)) + /// `pm.range_ptrs_is_empty(r, cx)` == `pm.range_ptrs_get(r, cx).is_empty()`, but is faster. + pub(super) fn range_ptrs_is_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool { + self.ptrs.range_is_empty(Self::adjusted_range_ptrs(range, cx)) } /// Returns all byte-wise provenance in the given range. - fn range_get_bytes(&self, range: AllocRange) -> &[(Size, Prov)] { + fn range_bytes_get(&self, range: AllocRange) -> &[(Size, Prov)] { if let Some(bytes) = self.bytes.as_ref() { bytes.range(range.start..range.end()) } else { @@ -106,9 +101,14 @@ impl ProvenanceMap { } } + /// Same as `range_bytes_get(range).is_empty()`, but faster. + fn range_bytes_is_empty(&self, range: AllocRange) -> bool { + self.bytes.as_ref().is_none_or(|bytes| bytes.range_is_empty(range.start..range.end())) + } + /// Get the provenance of a single byte. pub fn get(&self, offset: Size, cx: &impl HasDataLayout) -> Option { - let prov = self.range_get_ptrs(alloc_range(offset, Size::from_bytes(1)), cx); + let prov = self.range_ptrs_get(alloc_range(offset, Size::from_bytes(1)), cx); debug_assert!(prov.len() <= 1); if let Some(entry) = prov.first() { // If it overlaps with this byte, it is on this byte. @@ -132,7 +132,7 @@ impl ProvenanceMap { /// limit access to provenance outside of the `Allocation` abstraction. /// pub fn range_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool { - self.range_get_ptrs_is_empty(range, cx) && self.range_get_bytes(range).is_empty() + self.range_ptrs_is_empty(range, cx) && self.range_bytes_is_empty(range) } /// Yields all the provenances stored in this map. @@ -164,14 +164,14 @@ impl ProvenanceMap { // provenance that overlaps with the given range. let (first, last) = { // Find all provenance overlapping the given range. - if self.range_get_ptrs_is_empty(range, cx) { + if self.range_ptrs_is_empty(range, cx) { // No provenance in this range, we are done. This is the common case. return Ok(()); } // This redoes some of the work of `range_get_ptrs_is_empty`, but this path is much // colder than the early return above, so it's worth it. - let provenance = self.range_get_ptrs(range, cx); + let provenance = self.range_ptrs_get(range, cx); ( provenance.first().unwrap().0, provenance.last().unwrap().0 + cx.data_layout().pointer_size, @@ -284,8 +284,8 @@ impl ProvenanceMap { // This includes the existing bytewise provenance in the range, and ptr provenance // that overlaps with the begin/end of the range. let mut dest_bytes_box = None; - let begin_overlap = self.range_get_ptrs(alloc_range(src.start, Size::ZERO), cx).first(); - let end_overlap = self.range_get_ptrs(alloc_range(src.end(), Size::ZERO), cx).first(); + let begin_overlap = self.range_ptrs_get(alloc_range(src.start, Size::ZERO), cx).first(); + let end_overlap = self.range_ptrs_get(alloc_range(src.end(), Size::ZERO), cx).first(); if !Prov::OFFSET_IS_ADDR { // There can't be any bytewise provenance, and we cannot split up the begin/end overlap. if let Some(entry) = begin_overlap { @@ -308,10 +308,10 @@ impl ProvenanceMap { } else { trace!("no start overlapping entry"); } + // Then the main part, bytewise provenance from `self.bytes`. - if let Some(all_bytes) = self.bytes.as_ref() { - bytes.extend(all_bytes.range(src.start..src.end())); - } + bytes.extend(self.range_bytes_get(src)); + // And finally possibly parts of a pointer at the end. if let Some(entry) = end_overlap { trace!("end overlapping entry: {entry:?}"); diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 890756a17cae7..6ff3cac049b3a 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -221,13 +221,11 @@ pub enum InvalidProgramInfo<'tcx> { #[derive(Debug, Copy, Clone)] pub enum CheckInAllocMsg { /// We are access memory. - MemoryAccessTest, + MemoryAccess, /// We are doing pointer arithmetic. - PointerArithmeticTest, - /// We are doing pointer offset_from. - OffsetFromTest, + InboundsPointerArithmetic, /// None of the above -- generic/unspecific inbounds test. - InboundsTest, + Dereferenceable, } /// Details of which pointer is not aligned. diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index c48cfffa05cc4..c2438af6a1e19 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -105,7 +105,7 @@ enum AllocDiscriminant { Static, } -pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder>>( +pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( encoder: &mut E, tcx: TyCtxt<'tcx>, alloc_id: AllocId, @@ -175,7 +175,7 @@ impl<'s> AllocDecodingSession<'s> { /// Decodes an `AllocId` in a thread-safe way. pub fn decode_alloc_id<'tcx, D>(&self, decoder: &mut D) -> AllocId where - D: TyDecoder>, + D: TyDecoder<'tcx>, { // Read the index of the allocation. let idx = usize::try_from(decoder.read_u32()).unwrap(); @@ -452,12 +452,7 @@ impl<'tcx> TyCtxt<'tcx> { } let id = self.alloc_map.reserve(); debug!("creating alloc {:?} with id {id:?}", alloc_salt.0); - let had_previous = self - .alloc_map - .to_alloc - .lock_shard_by_value(&id) - .insert(id, alloc_salt.0.clone()) - .is_some(); + let had_previous = self.alloc_map.to_alloc.insert(id, alloc_salt.0.clone()).is_some(); // We just reserved, so should always be unique. assert!(!had_previous); dedup.insert(alloc_salt, id); @@ -510,7 +505,7 @@ impl<'tcx> TyCtxt<'tcx> { /// local dangling pointers and allocations in constants/statics. #[inline] pub fn try_get_global_alloc(self, id: AllocId) -> Option> { - self.alloc_map.to_alloc.lock_shard_by_value(&id).get(&id).cloned() + self.alloc_map.to_alloc.get(&id) } #[inline] @@ -529,9 +524,7 @@ impl<'tcx> TyCtxt<'tcx> { /// 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: ConstAllocation<'tcx>) { - if let Some(old) = - self.alloc_map.to_alloc.lock_shard_by_value(&id).insert(id, GlobalAlloc::Memory(mem)) - { + if let Some(old) = self.alloc_map.to_alloc.insert(id, GlobalAlloc::Memory(mem)) { bug!("tried to set allocation ID {id:?}, but it was already existing as {old:#?}"); } } @@ -539,11 +532,8 @@ impl<'tcx> TyCtxt<'tcx> { /// Freezes an `AllocId` created with `reserve` by pointing it at a static item. Trying to /// call this function twice, even with the same `DefId` will ICE the compiler. pub fn set_nested_alloc_id_static(self, id: AllocId, def_id: LocalDefId) { - if let Some(old) = self - .alloc_map - .to_alloc - .lock_shard_by_value(&id) - .insert(id, GlobalAlloc::Static(def_id.to_def_id())) + if let Some(old) = + self.alloc_map.to_alloc.insert(id, GlobalAlloc::Static(def_id.to_def_id())) { bug!("tried to set allocation ID {id:?}, but it was already existing as {old:#?}"); } @@ -573,7 +563,7 @@ pub fn write_target_uint( #[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::()]; + let mut buf = [0u8; size_of::()]; // So we do not read exactly 16 bytes into the u128, just the "payload". let uint = match endianness { Endian::Little => { diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 78749428c6df3..4a5c42c721c12 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -10,8 +10,7 @@ use super::{ }; use crate::mir; use crate::query::TyCtxtEnsureOk; -use crate::ty::visit::TypeVisitableExt; -use crate::ty::{self, GenericArgs, TyCtxt}; +use crate::ty::{self, GenericArgs, TyCtxt, TypeVisitableExt}; impl<'tcx> TyCtxt<'tcx> { /// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts @@ -24,7 +23,7 @@ impl<'tcx> TyCtxt<'tcx> { // into `const_eval` which will return `ErrorHandled::TooGeneric` if any of them are // encountered. let args = GenericArgs::identity_for_item(self, def_id); - let instance = ty::Instance::new(def_id, args); + let instance = ty::Instance::new_raw(def_id, args); let cid = GlobalId { instance, promoted: None }; let typing_env = ty::TypingEnv::post_analysis(self, def_id); self.const_eval_global_id(typing_env, cid, DUMMY_SP) @@ -40,7 +39,7 @@ impl<'tcx> TyCtxt<'tcx> { // into `const_eval` which will return `ErrorHandled::TooGeneric` if any of them are // encountered. let args = GenericArgs::identity_for_item(self, def_id); - let instance = ty::Instance::new(def_id, args); + let instance = ty::Instance::new_raw(def_id, args); let cid = GlobalId { instance, promoted: None }; let typing_env = ty::TypingEnv::post_analysis(self, def_id); let inputs = self.erase_regions(typing_env.as_query_input(cid)); @@ -116,15 +115,16 @@ impl<'tcx> TyCtxt<'tcx> { // @lcnr believes that successfully evaluating even though there are // used generic parameters is a bug of evaluation, so checking for it // here does feel somewhat sensible. - if !self.features().generic_const_exprs() && ct.args.has_non_region_param() { - let def_kind = self.def_kind(instance.def_id()); - assert!( - matches!( - def_kind, - DefKind::InlineConst | DefKind::AnonConst | DefKind::AssocConst - ), - "{cid:?} is {def_kind:?}", - ); + if !self.features().generic_const_exprs() + && ct.args.has_non_region_param() + // We only FCW for anon consts as repeat expr counts with anon consts are the only place + // that we have a back compat hack for. We don't need to check this is a const argument + // as only anon consts as const args should get evaluated "for the type system". + // + // If we don't *only* FCW anon consts we can wind up incorrectly FCW'ing uses of assoc + // consts in pattern positions. #140447 + && self.def_kind(instance.def_id()) == DefKind::AnonConst + { let mir_body = self.mir_for_ctfe(instance.def_id()); if mir_body.is_polymorphic { let Some(local_def_id) = ct.def.as_local() else { return }; @@ -209,7 +209,7 @@ impl<'tcx> TyCtxtEnsureOk<'tcx> { // into `const_eval` which will return `ErrorHandled::TooGeneric` if any of them are // encountered. let args = GenericArgs::identity_for_item(self.tcx, def_id); - let instance = ty::Instance::new(def_id, self.tcx.erase_regions(args)); + let instance = ty::Instance::new_raw(def_id, self.tcx.erase_regions(args)); let cid = GlobalId { instance, promoted: None }; let typing_env = ty::TypingEnv::post_analysis(self.tcx, def_id); // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index ea0bb5feb1220..adc100941a39a 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -32,10 +32,9 @@ pub use self::query::*; use crate::mir::interpret::{AllocRange, Scalar}; use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::print::{FmtPrinter, Printer, pretty_print_const, with_no_trimmed_paths}; -use crate::ty::visit::TypeVisitableExt; use crate::ty::{ - self, AdtDef, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, TypingEnv, - UserTypeAnnotationIndex, + self, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, TypeVisitableExt, + TypingEnv, UserTypeAnnotationIndex, }; mod basic_blocks; @@ -201,7 +200,13 @@ pub struct CoroutineInfo<'tcx> { /// Coroutine drop glue. This field is populated after the state transform pass. pub coroutine_drop: Option>, - /// The layout of a coroutine. This field is populated after the state transform pass. + /// Coroutine async drop glue. + pub coroutine_drop_async: Option>, + + /// When coroutine has sync drop, this is async proxy calling `coroutine_drop` sync impl. + pub coroutine_drop_proxy_async: Option>, + + /// The layout of a coroutine. Produced by the state transformation. pub coroutine_layout: Option>, /// If this is a coroutine then record the type of source expression that caused this coroutine @@ -221,6 +226,8 @@ impl<'tcx> CoroutineInfo<'tcx> { yield_ty: Some(yield_ty), resume_ty: Some(resume_ty), coroutine_drop: None, + coroutine_drop_async: None, + coroutine_drop_proxy_async: None, coroutine_layout: None, } } @@ -332,13 +339,13 @@ pub struct Body<'tcx> { /// /// ```rust /// fn test() { - /// let _ = [0; std::mem::size_of::<*mut T>()]; + /// let _ = [0; 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. + /// potentially allow things like `[u8; size_of::() * 0]` due to this. pub is_polymorphic: bool, /// The phase at which this MIR should be "injected" into the compilation process. @@ -588,6 +595,26 @@ impl<'tcx> Body<'tcx> { self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_drop.as_ref()) } + #[inline] + pub fn coroutine_drop_async(&self) -> Option<&Body<'tcx>> { + self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_drop_async.as_ref()) + } + + #[inline] + pub fn coroutine_requires_async_drop(&self) -> bool { + self.coroutine_drop_async().is_some() + } + + #[inline] + pub fn future_drop_poll(&self) -> Option<&Body<'tcx>> { + self.coroutine.as_ref().and_then(|coroutine| { + coroutine + .coroutine_drop_async + .as_ref() + .or(coroutine.coroutine_drop_proxy_async.as_ref()) + }) + } + #[inline] pub fn coroutine_kind(&self) -> Option { self.coroutine.as_ref().map(|coroutine| coroutine.coroutine_kind) @@ -791,7 +818,7 @@ impl ClearCrossCrate { const TAG_CLEAR_CROSS_CRATE_CLEAR: u8 = 0; const TAG_CLEAR_CROSS_CRATE_SET: u8 = 1; -impl> Encodable for ClearCrossCrate { +impl<'tcx, E: TyEncoder<'tcx>, T: Encodable> Encodable for ClearCrossCrate { #[inline] fn encode(&self, e: &mut E) { if E::CLEAR_CROSS_CRATE { @@ -807,7 +834,7 @@ impl> Encodable for ClearCrossCrate { } } } -impl> Decodable for ClearCrossCrate { +impl<'tcx, D: TyDecoder<'tcx>, T: Decodable> Decodable for ClearCrossCrate { #[inline] fn decode(d: &mut D) -> ClearCrossCrate { if D::CLEAR_CROSS_CRATE { @@ -1483,53 +1510,10 @@ pub struct UserTypeProjections { pub contents: Vec, } -impl<'tcx> UserTypeProjections { - pub fn none() -> Self { - UserTypeProjections { contents: vec![] } - } - - pub fn is_empty(&self) -> bool { - self.contents.is_empty() - } - +impl UserTypeProjections { pub fn projections(&self) -> impl Iterator + ExactSizeIterator { self.contents.iter() } - - pub fn push_user_type(mut self, base_user_type: UserTypeAnnotationIndex) -> Self { - self.contents.push(UserTypeProjection { base: base_user_type, projs: vec![] }); - self - } - - fn map_projections(mut self, f: impl FnMut(UserTypeProjection) -> UserTypeProjection) -> Self { - self.contents = self.contents.into_iter().map(f).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: FieldIdx) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.leaf(field)) - } - - pub fn variant( - self, - adt_def: AdtDef<'tcx>, - variant_index: VariantIdx, - field_index: FieldIdx, - ) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.variant(adt_def, variant_index, field_index)) - } } /// Encodes the effect of a user-supplied type annotation on the @@ -1554,42 +1538,6 @@ pub struct UserTypeProjection { pub projs: Vec, } -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: FieldIdx) -> Self { - self.projs.push(ProjectionElem::Field(field, ())); - self - } - - pub(crate) fn variant( - mut self, - adt_def: AdtDef<'_>, - variant_index: VariantIdx, - field_index: FieldIdx, - ) -> Self { - self.projs.push(ProjectionElem::Downcast( - Some(adt_def.variant(variant_index).name), - variant_index, - )); - self.projs.push(ProjectionElem::Field(field_index, ())); - self - } -} - rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] @@ -1716,8 +1664,8 @@ pub fn find_self_call<'tcx>( &body[block].terminator && let Operand::Constant(box ConstOperand { const_, .. }) = func && let ty::FnDef(def_id, fn_args) = *const_.ty().kind() - && let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) = - tcx.opt_associated_item(def_id) + && let Some(item) = tcx.opt_associated_item(def_id) + && item.is_method() && let [Spanned { node: Operand::Move(self_place) | Operand::Copy(self_place), .. }, ..] = **args { diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 58d5c94d03326..7243f87ee6380 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -2,7 +2,7 @@ use std::fmt; use std::hash::Hash; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; -use rustc_attr_parsing::InlineAttr; +use rustc_attr_data_structures::InlineAttr; use rustc_data_structures::base_n::{BaseNString, CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexMap; @@ -21,7 +21,7 @@ use tracing::debug; use crate::dep_graph::{DepNode, WorkProduct, WorkProductId}; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use crate::ty::{GenericArgs, Instance, InstanceKind, SymbolName, TyCtxt}; +use crate::ty::{self, GenericArgs, Instance, InstanceKind, SymbolName, Ty, TyCtxt}; /// Describes how a monomorphization will be instantiated in object files. #[derive(PartialEq)] @@ -55,6 +55,39 @@ pub enum MonoItem<'tcx> { GlobalAsm(ItemId), } +fn opt_incr_drop_glue_mode<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InstantiationMode { + // Non-ADTs can't have a Drop impl. This case is mostly hit by closures whose captures require + // dropping. + let ty::Adt(adt_def, _) = ty.kind() else { + return InstantiationMode::LocalCopy; + }; + + // Types that don't have a direct Drop impl, but have fields that require dropping. + let Some(dtor) = adt_def.destructor(tcx) else { + // We use LocalCopy for drops of enums only; this code is inherited from + // https://github.com/rust-lang/rust/pull/67332 and the theory is that we get to optimize + // out code like drop_in_place(Option::None) before crate-local ThinLTO, which improves + // compile time. At the time of writing, simply removing this entire check does seem to + // regress incr-opt compile times. But it sure seems like a more sophisticated check could + // do better here. + if adt_def.is_enum() { + return InstantiationMode::LocalCopy; + } else { + return InstantiationMode::GloballyShared { may_conflict: true }; + } + }; + + // We've gotten to a drop_in_place for a type that directly implements Drop. + // The drop glue is a wrapper for the Drop::drop impl, and we are an optimized build, so in an + // effort to coordinate with the mode that the actual impl will get, we make the glue also + // LocalCopy. + if tcx.cross_crate_inlinable(dtor.did) { + InstantiationMode::LocalCopy + } else { + InstantiationMode::GloballyShared { may_conflict: true } + } +} + impl<'tcx> MonoItem<'tcx> { /// Returns `true` if the mono item is user-defined (i.e. not compiler-generated, like shims). pub fn is_user_defined(&self) -> bool { @@ -117,6 +150,7 @@ impl<'tcx> MonoItem<'tcx> { // If the function is #[naked] or contains any other attribute that requires exactly-once // instantiation: + // We emit an unused_attributes lint for this case, which should be kept in sync if possible. let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id()); if codegen_fn_attrs.contains_extern_indicator() || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) @@ -124,16 +158,36 @@ impl<'tcx> MonoItem<'tcx> { return InstantiationMode::GloballyShared { may_conflict: false }; } - // FIXME: The logic for which functions are permitted to get LocalCopy is actually spread - // across 4 functions: - // * cross_crate_inlinable(def_id) - // * InstanceKind::requires_inline - // * InstanceKind::generate_cgu_internal_copy - // * MonoItem::instantiation_mode - // Since reachable_non_generics calls InstanceKind::generates_cgu_internal_copy to decide - // which symbols this crate exports, we are obligated to only generate LocalCopy when - // generates_cgu_internal_copy returns true. - if !instance.def.generates_cgu_internal_copy(tcx) { + // This is technically a heuristic even though it's in the "not a heuristic" part of + // instantiation mode selection. + // It is surely possible to untangle this; the root problem is that the way we instantiate + // InstanceKind other than Item is very complicated. + // + // The fallback case is to give everything else GloballyShared at OptLevel::No and + // LocalCopy at all other opt levels. This is a good default, except for one specific build + // configuration: Optimized incremental builds. + // In the current compiler architecture there is a fundamental tension between + // optimizations (which want big CGUs with as many things LocalCopy as possible) and + // incrementality (which wants small CGUs with as many things GloballyShared as possible). + // The heuristics implemented here do better than a completely naive approach in the + // compiler benchmark suite, but there is no reason to believe they are optimal. + if let InstanceKind::DropGlue(_, Some(ty)) = instance.def { + if tcx.sess.opts.optimize == OptLevel::No { + return InstantiationMode::GloballyShared { may_conflict: false }; + } + if tcx.sess.opts.incremental.is_none() { + return InstantiationMode::LocalCopy; + } + return opt_incr_drop_glue_mode(tcx, ty); + } + + // We need to ensure that we do not decide the InstantiationMode of an exported symbol is + // LocalCopy. Since exported symbols are computed based on the output of + // cross_crate_inlinable, we are beholden to our previous decisions. + // + // Note that just like above, this check for requires_inline is technically a heuristic + // even though it's in the "not a heuristic" part of instantiation mode selection. + if !tcx.cross_crate_inlinable(instance.def_id()) && !instance.def.requires_inline(tcx) { return InstantiationMode::GloballyShared { may_conflict: false }; } @@ -264,7 +318,7 @@ impl<'tcx> fmt::Display for MonoItem<'tcx> { match *self { MonoItem::Fn(instance) => write!(f, "fn {instance}"), MonoItem::Static(def_id) => { - write!(f, "static {}", Instance::new(def_id, GenericArgs::empty())) + write!(f, "static {}", Instance::new_raw(def_id, GenericArgs::empty())) } MonoItem::GlobalAsm(..) => write!(f, "global_asm"), } @@ -476,6 +530,8 @@ impl<'tcx> CodegenUnit<'tcx> { | InstanceKind::CloneShim(..) | InstanceKind::ThreadLocalShim(..) | InstanceKind::FnPtrAddrShim(..) + | InstanceKind::AsyncDropGlue(..) + | InstanceKind::FutureDropPollShim(..) | InstanceKind::AsyncDropGlueCtorShim(..) => None, } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index f880b1364c2cd..57ae7dc55c5c8 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -253,9 +253,7 @@ fn dump_path<'tcx>( })); s } - ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) => { - // Unfortunately, pretty-printed typed are not very filename-friendly. - // We dome some filtering. + ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => { let mut s = ".".to_owned(); s.extend(ty.to_string().chars().filter_map(|c| match c { ' ' => None, @@ -264,6 +262,34 @@ fn dump_path<'tcx>( })); s } + ty::InstanceKind::AsyncDropGlue(_, ty) => { + let ty::Coroutine(_, args) = ty.kind() else { + bug!(); + }; + let ty = args.first().unwrap().expect_ty(); + let mut s = ".".to_owned(); + s.extend(ty.to_string().chars().filter_map(|c| match c { + ' ' => None, + ':' | '<' | '>' => Some('_'), + c => Some(c), + })); + s + } + ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => { + let mut s = ".".to_owned(); + s.extend(proxy_cor.to_string().chars().filter_map(|c| match c { + ' ' => None, + ':' | '<' | '>' => Some('_'), + c => Some(c), + })); + s.push('.'); + s.extend(impl_cor.to_string().chars().filter_map(|c| match c { + ' ' => None, + ':' | '<' | '>' => Some('_'), + c => Some(c), + })); + s + } _ => String::new(), }; @@ -531,12 +557,12 @@ fn write_mir_intro<'tcx>( // 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() { + for (index, scope_data) in body.source_scopes.iter_enumerated() { if let Some(parent) = scope_data.parent_scope { - scope_tree.entry(parent).or_default().push(SourceScope::new(index)); + scope_tree.entry(parent).or_default().push(index); } else { // Only the argument scope has no parent, because it's the root. - assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index()); + assert_eq!(index, OUTERMOST_SOURCE_SCOPE); } } @@ -620,9 +646,8 @@ fn write_function_coverage_info( function_coverage_info: &coverage::FunctionCoverageInfo, w: &mut dyn io::Write, ) -> io::Result<()> { - let coverage::FunctionCoverageInfo { body_span, mappings, .. } = function_coverage_info; + let coverage::FunctionCoverageInfo { mappings, .. } = function_coverage_info; - writeln!(w, "{INDENT}coverage body span: {body_span:?}")?; for coverage::Mapping { kind, span } in mappings { writeln!(w, "{INDENT}coverage {kind:?} => {span:?};")?; } @@ -653,7 +678,10 @@ fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io: write!(w, "static mut ")? } (_, _) if is_function => write!(w, "fn ")?, - (DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item + // things like anon const, not an item + (DefKind::AnonConst | DefKind::InlineConst, _) => {} + // `global_asm!` have fake bodies, which we may dump after mir-build + (DefKind::GlobalAsm, _) => {} _ => bug!("Unexpected def kind {:?}", kind), } @@ -857,7 +885,7 @@ impl Debug for Statement<'_> { BackwardIncompatibleDropHint { ref place, reason: _ } => { // For now, we don't record the reason because there is only one use case, // which is to report breaking change in drop order by Edition 2024 - write!(fmt, "backward incompatible drop({place:?})") + write!(fmt, "BackwardIncompatibleDropHint({place:?})") } } } @@ -1048,7 +1076,13 @@ impl<'tcx> TerminatorKind<'tcx> { Call { target: None, unwind: _, .. } => vec![], Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()], Yield { drop: None, .. } => vec!["resume".into()], - Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()], + Drop { unwind: UnwindAction::Cleanup(_), drop: Some(_), .. } => { + vec!["return".into(), "unwind".into(), "drop".into()] + } + Drop { unwind: UnwindAction::Cleanup(_), drop: None, .. } => { + vec!["return".into(), "unwind".into()] + } + Drop { unwind: _, drop: Some(_), .. } => vec!["return".into(), "drop".into()], Drop { unwind: _, .. } => vec!["return".into()], Assert { unwind: UnwindAction::Cleanup(_), .. } => { vec!["success".into(), "unwind".into()] @@ -1200,7 +1234,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { && let Some(upvars) = tcx.upvars_mentioned(def_id) { for (&var_id, place) in iter::zip(upvars.keys(), places) { - let var_name = tcx.hir().name(var_id); + let var_name = tcx.hir_name(var_id); struct_fmt.field(var_name.as_str(), place); } } else { @@ -1221,7 +1255,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { && let Some(upvars) = tcx.upvars_mentioned(def_id) { for (&var_id, place) in iter::zip(upvars.keys(), places) { - let var_name = tcx.hir().name(var_id); + let var_name = tcx.hir_name(var_id); struct_fmt.field(var_name.as_str(), place); } } else { diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 50494355e3e49..3fc05f2caf2ad 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -6,15 +6,13 @@ use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::LocalDefId; +use rustc_index::IndexVec; use rustc_index::bit_set::BitMatrix; -use rustc_index::{Idx, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::{Span, Symbol}; -use smallvec::SmallVec; use super::{ConstValue, SourceInfo}; -use crate::ty::fold::fold_regions; -use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty, TyCtxt}; +use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty}; rustc_index::newtype_index! { #[derive(HashStable)] @@ -86,16 +84,11 @@ impl Debug for CoroutineLayout<'_> { } } -#[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: FxIndexMap>, - pub closure_requirements: Option>, - pub used_mut_upvars: SmallVec<[FieldIdx; 8]>, - pub tainted_by_errors: Option, -} +/// All the opaque types that are restricted to concrete types +/// by this function. Unlike the value in `TypeckResults`, this has +/// unerased regions. +#[derive(Default, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct ConcreteOpaqueTypes<'tcx>(pub FxIndexMap>); /// The result of the `mir_const_qualif` query. /// @@ -109,84 +102,6 @@ pub struct ConstQualifs { pub needs_non_const_drop: bool, pub tainted_by_errors: Option, } - -/// 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 coroutine'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 args: -/// -/// ```text -/// ClosureArgs = [ -/// '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 -/// ClosureArgs = [ -/// '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. -#[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<'tcx>, -} - -// Make sure this enum doesn't unintentionally grow -#[cfg(target_pointer_width = "64")] -rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); - /// 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. @@ -254,66 +169,6 @@ pub enum AnnotationSource { GenericArg, } -/// 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`. - Ty(ClosureOutlivesSubjectTy<'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), -} - -/// Represents a `ty::Ty` for use in [`ClosureOutlivesSubject`]. -/// -/// This abstraction is necessary because the type may include `ReVar` regions, -/// which is what we use internally within NLL code, and they can't be used in -/// a query response. -/// -/// DO NOT implement `TypeVisitable` or `TypeFoldable` traits, because this -/// type is not recognized as a binder for late-bound region. -#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] -pub struct ClosureOutlivesSubjectTy<'tcx> { - inner: Ty<'tcx>, -} - -impl<'tcx> ClosureOutlivesSubjectTy<'tcx> { - /// All regions of `ty` must be of kind `ReVar` and must represent - /// universal regions *external* to the closure. - pub fn bind(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { - let inner = fold_regions(tcx, ty, |r, depth| match r.kind() { - ty::ReVar(vid) => { - let br = ty::BoundRegion { - var: ty::BoundVar::new(vid.index()), - kind: ty::BoundRegionKind::Anon, - }; - ty::Region::new_bound(tcx, depth, br) - } - _ => bug!("unexpected region in ClosureOutlivesSubjectTy: {r:?}"), - }); - - Self { inner } - } - - pub fn instantiate( - self, - tcx: TyCtxt<'tcx>, - mut map: impl FnMut(ty::RegionVid) -> ty::Region<'tcx>, - ) -> Ty<'tcx> { - fold_regions(tcx, self.inner, |r, depth| match r.kind() { - ty::ReBound(debruijn, br) => { - debug_assert_eq!(debruijn, depth); - map(ty::RegionVid::new(br.var.index())) - } - _ => bug!("unexpected region {r:?}"), - }) - } -} - /// The constituent parts of a mir constant of kind ADT or array. #[derive(Copy, Clone, Debug, HashStable)] pub struct DestructuredConstant<'tcx> { diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index f05a798949b7e..d59b6df44ed5d 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -774,6 +774,15 @@ impl BorrowKind { } } +impl<'tcx> NullOp<'tcx> { + pub fn ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match self { + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) => tcx.types.usize, + NullOp::UbChecks | NullOp::ContractChecks => tcx.types.bool, + } + } +} + impl<'tcx> UnOp { pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> { match self { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 4f86703e95376..bb068f3821db8 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -77,6 +77,8 @@ pub enum MirPhase { /// exception is fields of packed structs. In analysis MIR, `Drop(P)` for a `P` that might be /// misaligned for this reason implicitly moves `P` to a temporary before dropping. Runtime /// MIR has no such rules, and dropping a misaligned place is simply UB. + /// - Async drops: after drop elaboration some drops may become async (`drop`, `async_fut` fields). + /// StateTransform pass will expand those async drops or reset to sync. /// - Unwinding: in analysis MIR, unwinding from a function which may not unwind aborts. In /// runtime MIR, this is UB. /// - Retags: If `-Zmir-emit-retag` is enabled, analysis MIR has "implicit" retags in the same @@ -334,14 +336,19 @@ pub enum StatementKind<'tcx> { /// See [`Rvalue`] documentation for details on each of those. 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. + /// When executed at runtime, this is a nop. /// - /// 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: ! = ..;` + /// During static analysis, a fake read: + /// - requires that the value being read is initialized (or, in the case + /// of closures, that it was fully initialized at some point in the past) + /// - constitutes a use of a value for the purposes of NLL (i.e. if the + /// value being fake-read is a reference, the lifetime of that reference + /// will be extended to cover the `FakeRead`) + /// - but, unlike an actual read, does *not* invalidate any exclusive + /// borrows. /// - /// When executed at runtime this is a nop. + /// See [`FakeReadCause`] for more details on the situations in which a + /// `FakeRead` is emitted. /// /// Disallowed after drop elaboration. FakeRead(Box<(FakeReadCause, Place<'tcx>)>), @@ -518,28 +525,59 @@ pub enum RetagKind { /// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists. #[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, Hash, HashStable, PartialEq)] pub enum FakeReadCause { - /// Inject a fake read of the borrowed input at the end of each guards - /// code. + /// A fake read injected into a match guard to ensure that the discriminants + /// that are being matched on aren't modified while the match guard is being + /// evaluated. + /// + /// At the beginning of each match guard, a [fake borrow][FakeBorrowKind] is + /// inserted for each discriminant accessed in the entire `match` statement. + /// + /// Then, at the end of the match guard, a `FakeRead(ForMatchGuard)` is + /// inserted to keep the fake borrows alive until that point. /// /// 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. + /// Fake read of the scrutinee of a `match` or destructuring `let` + /// (i.e. `let` with non-trivial pattern). + /// + /// In `match x { ... }`, we generate a `FakeRead(ForMatchedPlace, x)` + /// and insert it into the `otherwise_block` (which is supposed to be + /// unreachable for irrefutable pattern-matches like `match` or `let`). /// - /// If a closure pattern matches a Place starting with an Upvar, then we introduce a - /// FakeRead for that Place outside the closure, in such a case this option would be - /// Some(closure_def_id). - /// Otherwise, the value of the optional LocalDefId will be None. + /// This is necessary because `let x: !; match x {}` doesn't generate any + /// actual read of x, so we need to generate a `FakeRead` to check that it + /// is initialized. + /// + /// If the `FakeRead(ForMatchedPlace)` is being performed with a closure + /// that doesn't capture the required upvars, the `FakeRead` within the + /// closure is omitted entirely. + /// + /// To make sure that this is still sound, if a closure matches against + /// a Place starting with an Upvar, we hoist the `FakeRead` to the + /// definition point of the closure. + /// + /// If the `FakeRead` comes from being hoisted out of a closure like this, + /// we record the `LocalDefId` of the closure. Otherwise, the `Option` will be `None`. // // We can use LocalDefId here since fake read statements are removed // before codegen in the `CleanupNonCodegenStatements` pass. ForMatchedPlace(Option), - /// A fake read of the RefWithinGuard version of a bind-by-value variable - /// in a match guard to ensure that its value hasn't change by the time - /// we create the OutsideGuard version. + /// A fake read injected into a match guard to ensure that the places + /// bound by the pattern are immutable for the duration of the match guard. + /// + /// Within a match guard, references are created for each place that the + /// pattern creates a binding for — this is known as the `RefWithinGuard` + /// version of the variables. To make sure that the references stay + /// alive until the end of the match guard, and properly prevent the + /// places in question from being modified, a `FakeRead(ForGuardBinding)` + /// is inserted at the end of the match guard. + /// + /// For details on how these references are created, see the extensive + /// documentation on `bind_matched_candidate_for_guard` in + /// `rustc_mir_build`. ForGuardBinding, /// Officially, the semantics of @@ -552,22 +590,42 @@ pub enum FakeReadCause { /// 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. /// - /// If a closure pattern matches a Place starting with an Upvar, then we introduce a - /// FakeRead for that Place outside the closure, in such a case this option would be - /// Some(closure_def_id). - /// Otherwise, the value of the optional DefId will be None. + /// Therefore, we insert a `FakeRead(ForLet)` immediately after each `let` + /// with a trivial pattern. + /// + /// FIXME: `ExprUseVisitor` has an entirely different opinion on what `FakeRead(ForLet)` + /// is supposed to mean. If it was accurate to what MIR lowering does, + /// would it even make sense to hoist these out of closures like + /// `ForMatchedPlace`? ForLet(Option), - /// If we have an index expression like + /// Currently, index expressions overloaded through the `Index` trait + /// get lowered differently than index expressions with builtin semantics + /// for arrays and slices — the latter will emit code to perform + /// bound checks, and then return a MIR place that will only perform the + /// indexing "for real" when it gets incorporated into an instruction. + /// + /// This is observable in the fact that the following compiles: + /// + /// ``` + /// fn f(x: &mut [&mut [u32]], i: usize) { + /// x[i][x[i].len() - 1] += 1; + /// } + /// ``` + /// + /// However, we need to be careful to not let the user invalidate the + /// bound check with an expression like /// - /// (*x)[1][{ x = y; 4}] + /// `(*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. + /// Here, the first bounds check would be invalidated when we evaluate the + /// second index expression. To make sure that this doesn't happen, we + /// create a fake borrow of `x` and hold it while we evaluate the second + /// index. + /// + /// This borrow is kept alive by a `FakeRead(ForIndex)` at the end of its + /// scope. ForIndex, } @@ -596,6 +654,8 @@ pub enum CallSource { /// Other types of desugaring that did not come from the HIR, but we don't care about /// for diagnostics (yet). Misc, + /// Use of value, generating a clone function call + Use, /// Normal function call, no special source Normal, } @@ -710,7 +770,34 @@ pub enum TerminatorKind<'tcx> { /// The `replace` flag indicates whether this terminator was created as part of an assignment. /// This should only be used for diagnostic purposes, and does not have any operational /// meaning. - Drop { place: Place<'tcx>, target: BasicBlock, unwind: UnwindAction, replace: bool }, + /// + /// Async drop processing: + /// In compiler/rustc_mir_build/src/build/scope.rs we detect possible async drop: + /// drop of object with `needs_async_drop`. + /// Async drop later, in StateTransform pass, may be expanded into additional yield-point + /// for poll-loop of async drop future. + /// So we need prepared 'drop' target block in the similar way as for `Yield` terminator + /// (see `drops.build_mir::` in scopes.rs). + /// In compiler/rustc_mir_transform/src/elaborate_drops.rs for object implementing `AsyncDrop` trait + /// we need to prepare async drop feature - resolve `AsyncDrop::drop` and codegen call. + /// `async_fut` is set to the corresponding local. + /// For coroutine drop we don't need this logic because coroutine drop works with the same + /// layout object as coroutine itself. So `async_fut` will be `None` for coroutine drop. + /// Both `drop` and `async_fut` fields are only used in compiler/rustc_mir_transform/src/coroutine.rs, + /// StateTransform pass. In `expand_async_drops` async drops are expanded + /// into one or two yield points with poll ready/pending switch. + /// When a coroutine has any internal async drop, the coroutine drop function will be async + /// (generated by `create_coroutine_drop_shim_async`, not `create_coroutine_drop_shim`). + Drop { + place: Place<'tcx>, + target: BasicBlock, + unwind: UnwindAction, + replace: bool, + /// Cleanup to be done if the coroutine is dropped at this suspend point (for async drop). + drop: Option, + /// Prepared async future local (for async drop) + async_fut: Option, + }, /// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of /// the referred to function. The operand types must match the argument types of the function. @@ -873,6 +960,8 @@ pub enum TerminatorKind<'tcx> { asm_macro: InlineAsmMacro, /// The template for the inline assembly, with placeholders. + #[type_foldable(identity)] + #[type_visitable(ignore)] template: &'tcx [InlineAsmTemplatePiece], /// The operands for the inline assembly, as `Operand`s or `Place`s. @@ -883,6 +972,8 @@ pub enum TerminatorKind<'tcx> { /// 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. + #[type_foldable(identity)] + #[type_visitable(ignore)] line_spans: &'tcx [Span], /// Valid targets for the inline assembly. @@ -981,6 +1072,7 @@ pub enum AssertKind { RemainderByZero(O), ResumedAfterReturn(CoroutineKind), ResumedAfterPanic(CoroutineKind), + ResumedAfterDrop(CoroutineKind), MisalignedPointerDereference { required: O, found: O }, NullPointerDereference, } @@ -1180,6 +1272,8 @@ pub enum ProjectionElem { /// Like an explicit cast from an opaque type to a concrete type, but without /// requiring an intermediate variable. + /// + /// This is unused with `-Znext-solver`. OpaqueCast(T), /// A transmute from an unsafe binder to the type that it wraps. This is a projection @@ -1612,6 +1706,42 @@ pub enum BinOp { Offset, } +// Assignment operators, e.g. `+=`. See comments on the corresponding variants +// in `BinOp` for details. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] +pub enum AssignOp { + AddAssign, + SubAssign, + MulAssign, + DivAssign, + RemAssign, + BitXorAssign, + BitAndAssign, + BitOrAssign, + ShlAssign, + ShrAssign, +} + +// Sometimes `BinOp` and `AssignOp` need the same treatment. The operations +// covered by `AssignOp` are a subset of those covered by `BinOp`, so it makes +// sense to convert `AssignOp` to `BinOp`. +impl From for BinOp { + fn from(op: AssignOp) -> BinOp { + match op { + AssignOp::AddAssign => BinOp::Add, + AssignOp::SubAssign => BinOp::Sub, + AssignOp::MulAssign => BinOp::Mul, + AssignOp::DivAssign => BinOp::Div, + AssignOp::RemAssign => BinOp::Rem, + AssignOp::BitXorAssign => BinOp::BitXor, + AssignOp::BitAndAssign => BinOp::BitAnd, + AssignOp::BitOrAssign => BinOp::BitOr, + AssignOp::ShlAssign => BinOp::Shl, + AssignOp::ShrAssign => BinOp::Shr, + } + } +} + // Some nodes are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index bc77f22af67b3..0834fa8844c00 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -208,6 +208,16 @@ impl AssertKind { LangItem::PanicGenFnNonePanic } NullPointerDereference => LangItem::PanicNullPointerDereference, + ResumedAfterDrop(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedDrop, + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { + LangItem::PanicAsyncFnResumedDrop + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { + LangItem::PanicAsyncGenFnResumedDrop + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { + LangItem::PanicGenFnNoneDrop + } BoundsCheck { .. } | MisalignedPointerDereference { .. } => { bug!("Unexpected AssertKind") @@ -298,6 +308,18 @@ impl AssertKind { ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { write!(f, "\"`gen fn` should just keep returning `None` after panicking\"") } + ResumedAfterDrop(CoroutineKind::Coroutine(_)) => { + write!(f, "\"coroutine resumed after async drop\"") + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { + write!(f, "\"`async fn` resumed after async drop\"") + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { + write!(f, "\"`async gen fn` resumed after async drop\"") + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { + write!(f, "\"`gen fn` resumed after drop\"") + } } } @@ -345,6 +367,19 @@ impl AssertKind { middle_assert_coroutine_resume_after_panic } NullPointerDereference => middle_assert_null_ptr_deref, + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { + middle_assert_async_resume_after_drop + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { + todo!() + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { + middle_assert_gen_resume_after_drop + } + ResumedAfterDrop(CoroutineKind::Coroutine(_)) => { + middle_assert_coroutine_resume_after_drop + } + MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref, } } @@ -377,7 +412,10 @@ impl AssertKind { add!("left", format!("{left:#?}")); add!("right", format!("{right:#?}")); } - ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => {} + ResumedAfterReturn(_) + | ResumedAfterPanic(_) + | NullPointerDereference + | ResumedAfterDrop(_) => {} MisalignedPointerDereference { required, found } => { add!("required", format!("{required:#?}")); add!("found", format!("{found:#?}")); @@ -399,8 +437,8 @@ impl<'tcx> Terminator<'tcx> { } #[inline] - pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { - self.kind.successors_mut() + pub fn successors_mut<'a>(&'a mut self, f: impl FnMut(&'a mut BasicBlock)) { + self.kind.successors_mut(f) } #[inline] @@ -448,30 +486,41 @@ pub use helper::*; mod helper { use super::*; pub type Successors<'a> = impl DoubleEndedIterator + 'a; - pub type SuccessorsMut<'a> = impl DoubleEndedIterator + 'a; impl SwitchTargets { /// Like [`SwitchTargets::target_for_value`], but returning the same type as /// [`Terminator::successors`]. #[inline] + #[define_opaque(Successors)] pub fn successors_for_value(&self, value: u128) -> Successors<'_> { let target = self.target_for_value(value); - (&[]).into_iter().copied().chain(Some(target)) + (&[]).into_iter().copied().chain(Some(target).into_iter().chain(None)) } } impl<'tcx> TerminatorKind<'tcx> { #[inline] + #[define_opaque(Successors)] pub fn successors(&self) -> Successors<'_> { use self::TerminatorKind::*; match *self { + // 3-successors for async drop: target, unwind, dropline (parent coroutine drop) + Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: Some(d), .. } => { + slice::from_ref(t) + .into_iter() + .copied() + .chain(Some(u).into_iter().chain(Some(d))) + } + // 2-successors Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. } | Yield { resume: ref t, drop: Some(u), .. } - | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), .. } + | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: None, .. } + | Drop { target: ref t, unwind: _, drop: Some(u), .. } | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. } | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => { - slice::from_ref(t).into_iter().copied().chain(Some(u)) + slice::from_ref(t).into_iter().copied().chain(Some(u).into_iter().chain(None)) } + // single successor Goto { target: ref t } | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. } | Call { target: Some(ref t), unwind: _, .. } @@ -479,63 +528,94 @@ mod helper { | Drop { target: ref t, unwind: _, .. } | Assert { target: ref t, unwind: _, .. } | FalseUnwind { real_target: ref t, unwind: _ } => { - slice::from_ref(t).into_iter().copied().chain(None) + slice::from_ref(t).into_iter().copied().chain(None.into_iter().chain(None)) } + // No successors UnwindResume | UnwindTerminate(_) | CoroutineDrop | Return | Unreachable | TailCall { .. } - | Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None), + | Call { target: None, unwind: _, .. } => { + (&[]).into_iter().copied().chain(None.into_iter().chain(None)) + } + // Multiple successors InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => { - targets.iter().copied().chain(Some(u)) + targets.iter().copied().chain(Some(u).into_iter().chain(None)) + } + InlineAsm { ref targets, unwind: _, .. } => { + targets.iter().copied().chain(None.into_iter().chain(None)) } - InlineAsm { ref targets, unwind: _, .. } => targets.iter().copied().chain(None), - SwitchInt { ref targets, .. } => targets.targets.iter().copied().chain(None), - FalseEdge { ref real_target, imaginary_target } => { - slice::from_ref(real_target).into_iter().copied().chain(Some(imaginary_target)) + SwitchInt { ref targets, .. } => { + targets.targets.iter().copied().chain(None.into_iter().chain(None)) } + // FalseEdge + FalseEdge { ref real_target, imaginary_target } => slice::from_ref(real_target) + .into_iter() + .copied() + .chain(Some(imaginary_target).into_iter().chain(None)), } } #[inline] - pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { + pub fn successors_mut<'a>(&'a mut self, mut f: impl FnMut(&'a mut BasicBlock)) { use self::TerminatorKind::*; - match *self { - Call { - target: Some(ref mut t), unwind: UnwindAction::Cleanup(ref mut u), .. + match self { + Drop { target, unwind, drop, .. } => { + f(target); + if let UnwindAction::Cleanup(u) = unwind { + f(u) + } + if let Some(d) = drop { + f(d) + } + } + Call { target, unwind, .. } => { + if let Some(target) = target { + f(target); + } + if let UnwindAction::Cleanup(u) = unwind { + f(u) + } } - | Yield { resume: ref mut t, drop: Some(ref mut u), .. } - | Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. } - | Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. } - | FalseUnwind { - real_target: ref mut t, - unwind: UnwindAction::Cleanup(ref mut u), - } => slice::from_mut(t).into_iter().chain(Some(u)), - Goto { target: ref mut t } - | Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. } - | Call { target: Some(ref mut t), unwind: _, .. } - | Yield { resume: ref mut t, drop: None, .. } - | Drop { target: ref mut t, unwind: _, .. } - | Assert { target: ref mut t, unwind: _, .. } - | FalseUnwind { real_target: ref mut t, unwind: _ } => { - slice::from_mut(t).into_iter().chain(None) + Yield { resume, drop, .. } => { + f(resume); + if let Some(d) = drop { + f(d) + } + } + Assert { target, unwind, .. } | FalseUnwind { real_target: target, unwind } => { + f(target); + if let UnwindAction::Cleanup(u) = unwind { + f(u) + } + } + Goto { target } => { + f(target); } UnwindResume | UnwindTerminate(_) | CoroutineDrop | Return | Unreachable - | TailCall { .. } - | Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None), - InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => { - targets.iter_mut().chain(Some(u)) + | TailCall { .. } => {} + InlineAsm { targets, unwind, .. } => { + for target in targets { + f(target); + } + if let UnwindAction::Cleanup(u) = unwind { + f(u) + } + } + SwitchInt { targets, .. } => { + for target in &mut targets.targets { + f(target); + } } - InlineAsm { ref mut targets, unwind: _, .. } => targets.iter_mut().chain(None), - SwitchInt { ref mut targets, .. } => targets.targets.iter_mut().chain(None), - FalseEdge { ref mut real_target, ref mut imaginary_target } => { - slice::from_mut(real_target).into_iter().chain(Some(imaginary_target)) + FalseEdge { real_target, imaginary_target } => { + f(real_target); + f(imaginary_target); } } } @@ -668,8 +748,10 @@ impl<'tcx> TerminatorKind<'tcx> { Goto { target } => TerminatorEdges::Single(target), + // FIXME: Maybe we need also TerminatorEdges::Trio for async drop + // (target + unwind + dropline) Assert { target, unwind, expected: _, msg: _, cond: _ } - | Drop { target, unwind, place: _, replace: _ } + | Drop { target, unwind, place: _, replace: _, drop: _, async_fut: _ } | FalseUnwind { real_target: target, unwind } => match unwind { UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind), UnwindAction::Continue | UnwindAction::Terminate(_) | UnwindAction::Unreachable => { diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs index 5950ac295af19..9308570d89d18 100644 --- a/compiler/rustc_middle/src/mir/traversal.rs +++ b/compiler/rustc_middle/src/mir/traversal.rs @@ -23,19 +23,13 @@ pub struct Preorder<'a, 'tcx> { body: &'a Body<'tcx>, visited: DenseBitSet, worklist: Vec, - root_is_start_block: bool, } impl<'a, 'tcx> Preorder<'a, 'tcx> { pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> Preorder<'a, 'tcx> { let worklist = vec![root]; - Preorder { - body, - visited: DenseBitSet::new_empty(body.basic_blocks.len()), - worklist, - root_is_start_block: root == START_BLOCK, - } + Preorder { body, visited: DenseBitSet::new_empty(body.basic_blocks.len()), worklist } } } @@ -71,15 +65,11 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> { } fn size_hint(&self) -> (usize, Option) { - // All the blocks, minus the number of blocks we've visited. - let upper = self.body.basic_blocks.len() - self.visited.count(); + // The worklist might be only things already visited. + let lower = 0; - let lower = if self.root_is_start_block { - // We will visit all remaining blocks exactly once. - upper - } else { - self.worklist.len() - }; + // This is extremely loose, but it's not worth a popcnt loop to do better. + let upper = self.body.basic_blocks.len(); (lower, Some(upper)) } @@ -108,7 +98,6 @@ pub struct Postorder<'a, 'tcx> { basic_blocks: &'a IndexSlice>, visited: DenseBitSet, visit_stack: Vec<(BasicBlock, Successors<'a>)>, - root_is_start_block: bool, /// A non-empty `extra` allows for a precise calculation of the successors. extra: Option<(TyCtxt<'tcx>, Instance<'tcx>)>, } @@ -123,7 +112,6 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> { basic_blocks, visited: DenseBitSet::new_empty(basic_blocks.len()), visit_stack: Vec::new(), - root_is_start_block: root == START_BLOCK, extra, }; @@ -211,16 +199,13 @@ impl<'tcx> Iterator for Postorder<'_, 'tcx> { } fn size_hint(&self) -> (usize, Option) { - // All the blocks, minus the number of blocks we've visited. - let upper = self.basic_blocks.len() - self.visited.count(); - - let lower = if self.root_is_start_block { - // We will visit all remaining blocks exactly once. - upper - } else { - self.visit_stack.len() - }; + // These bounds are not at all tight, but that's fine. + // It's not worth a popcnt loop in `DenseBitSet` to improve the upper, + // and in mono-reachable we can't be precise anyway. + // Leaning on amortized growth is fine. + let lower = self.visit_stack.len(); + let upper = self.basic_blocks.len(); (lower, Some(upper)) } } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 3c83d962900ae..1777756174bf9 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -353,17 +353,21 @@ macro_rules! make_mir_visitor { coroutine_closure_def_id: _def_id, receiver_by_ref: _, } - | ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, None) | ty::InstanceKind::DropGlue(_def_id, None) => {} ty::InstanceKind::FnPtrShim(_def_id, ty) | ty::InstanceKind::DropGlue(_def_id, Some(ty)) | ty::InstanceKind::CloneShim(_def_id, ty) | ty::InstanceKind::FnPtrAddrShim(_def_id, ty) - | ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, Some(ty)) => { + | ty::InstanceKind::AsyncDropGlue(_def_id, ty) + | ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, ty) => { // FIXME(eddyb) use a better `TyContext` here. self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } + ty::InstanceKind::FutureDropPollShim(_def_id, proxy_ty, impl_ty) => { + self.visit_ty($(& $mutability)? *proxy_ty, TyContext::Location(location)); + self.visit_ty($(& $mutability)? *impl_ty, TyContext::Location(location)); + } } self.visit_args(callee_args, location); } @@ -457,9 +461,15 @@ macro_rules! make_mir_visitor { } } } + StatementKind::BackwardIncompatibleDropHint { place, .. } => { + self.visit_place( + place, + PlaceContext::NonUse(NonUseContext::BackwardIncompatibleDropHint), + location + ); + } StatementKind::ConstEvalCounter => {} StatementKind::Nop => {} - StatementKind::BackwardIncompatibleDropHint { .. } => {} } } @@ -515,7 +525,14 @@ macro_rules! make_mir_visitor { self.visit_operand(discr, location); } - TerminatorKind::Drop { place, target: _, unwind: _, replace: _ } => { + TerminatorKind::Drop { + place, + target: _, + unwind: _, + replace: _, + drop: _, + async_fut: _, + } => { self.visit_place( place, PlaceContext::MutatingUse(MutatingUseContext::Drop), @@ -628,7 +645,7 @@ macro_rules! make_mir_visitor { OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => { self.visit_operand(op, location); } - ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => { + ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference | ResumedAfterDrop(_) => { // Nothing to visit } MisalignedPointerDereference { required, found } => { @@ -1348,6 +1365,8 @@ pub enum NonUseContext { AscribeUserTy(ty::Variance), /// The data of a user variable, for debug info. VarDebugInfo, + /// A `BackwardIncompatibleDropHint` statement, meant for edition 2024 lints. + BackwardIncompatibleDropHint, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -1422,7 +1441,9 @@ impl PlaceContext { use NonUseContext::*; match self { PlaceContext::MutatingUse(_) => ty::Invariant, - PlaceContext::NonUse(StorageDead | StorageLive | VarDebugInfo) => ty::Invariant, + PlaceContext::NonUse( + StorageDead | StorageLive | VarDebugInfo | BackwardIncompatibleDropHint, + ) => ty::Invariant, PlaceContext::NonMutatingUse( Inspect | Copy | Move | PlaceMention | SharedBorrow | FakeBorrow | RawBorrow | Projection, diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index cbd60920bc572..5bd111fa2f22d 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -1,3 +1,4 @@ +use std::ffi::OsStr; use std::intrinsics::transmute_unchecked; use std::mem::MaybeUninit; @@ -24,10 +25,11 @@ pub trait EraseType: Copy { pub type Erase = Erased; #[inline(always)] +#[define_opaque(Erase)] pub fn erase(src: T) -> Erase { // Ensure the sizes match const { - if std::mem::size_of::() != std::mem::size_of::() { + if size_of::() != size_of::() { panic!("size of T must match erased type T::Result") } }; @@ -47,6 +49,7 @@ pub fn erase(src: T) -> Erase { /// Restores an erased value. #[inline(always)] +#[define_opaque(Erase)] pub fn restore(value: Erase) -> T { let value: Erased<::Result> = value; // See comment in `erase` for why we use `transmute_unchecked`. @@ -65,6 +68,10 @@ impl EraseType for &'_ [T] { type Result = [u8; size_of::<&'static [()]>()]; } +impl EraseType for &'_ OsStr { + type Result = [u8; size_of::<&'static OsStr>()]; +} + impl EraseType for &'_ ty::List { type Result = [u8; size_of::<&'static ty::List<()>>()]; } @@ -172,6 +179,10 @@ impl EraseType for Option<&'_ [T]> { type Result = [u8; size_of::>()]; } +impl EraseType for Option<&'_ OsStr> { + type Result = [u8; size_of::>()]; +} + impl EraseType for Option> { type Result = [u8; size_of::>>()]; } @@ -231,9 +242,9 @@ trivial! { bool, Option<(rustc_span::def_id::DefId, rustc_session::config::EntryFnType)>, Option, - Option, - Option, - Option, + Option, + Option, + Option, Option, Option, Option, @@ -256,10 +267,10 @@ trivial! { Result, rustc_abi::ReprOptions, rustc_ast::expand::allocator::AllocatorKind, - rustc_attr_parsing::ConstStability, - rustc_attr_parsing::DefaultBodyStability, - rustc_attr_parsing::Deprecation, - rustc_attr_parsing::Stability, + rustc_attr_data_structures::ConstStability, + rustc_attr_data_structures::DefaultBodyStability, + rustc_attr_data_structures::Deprecation, + rustc_attr_data_structures::Stability, rustc_data_structures::svh::Svh, rustc_errors::ErrorGuaranteed, rustc_hir::Constness, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 1489d57aba645..9ed1f10455ad3 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -1,5 +1,7 @@ //! Defines the set of legal keys that can be used in queries. +use std::ffi::OsStr; + use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId, ModDefId}; use rustc_hir::hir_id::{HirId, OwnerId}; use rustc_query_system::dep_graph::DepNodeIndex; @@ -498,6 +500,14 @@ impl Key for Option { } } +impl<'tcx> Key for &'tcx OsStr { + type Cache = DefaultCache; + + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + /// Canonical query goals correspond to abstract trait operations that /// are not tied to any crate in particular. impl<'tcx, T: Clone> Key for CanonicalQueryInput<'tcx, T> { @@ -508,6 +518,14 @@ impl<'tcx, T: Clone> Key for CanonicalQueryInput<'tcx, T> { } } +impl<'tcx, T: Clone> Key for (CanonicalQueryInput<'tcx, T>, bool) { + type Cache = DefaultCache; + + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + impl Key for (Symbol, u32, u32) { type Cache = DefaultCache; @@ -568,7 +586,7 @@ impl Key for HirId { type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - tcx.hir().span(*self) + tcx.hir_span(*self) } #[inline(always)] @@ -581,7 +599,7 @@ impl Key for (LocalDefId, HirId) { type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - tcx.hir().span(self.1) + tcx.hir_span(self.1) } #[inline(always)] diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 1302027aabb36..b2133fea08cc3 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -6,6 +6,7 @@ #![allow(unused_parens)] +use std::ffi::OsStr; use std::mem; use std::path::PathBuf; use std::sync::Arc; @@ -25,12 +26,14 @@ use rustc_hir::def_id::{ CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, }; use rustc_hir::lang_items::{LangItem, LanguageItems}; -use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, TraitCandidate}; +use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, PreciseCapturingArgKind, TraitCandidate}; use rustc_index::IndexVec; use rustc_lint_defs::LintId; use rustc_macros::rustc_queries; use rustc_query_system::ich::StableHashingContext; -use rustc_query_system::query::{QueryCache, QueryMode, QueryState, try_get_cached}; +use rustc_query_system::query::{ + QueryCache, QueryMode, QueryStackDeferred, QueryState, try_get_cached, +}; use rustc_session::Limits; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; use rustc_session::cstore::{ @@ -41,7 +44,7 @@ use rustc_span::def_id::LOCAL_CRATE; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_target::spec::PanicStrategy; -use {rustc_abi as abi, rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; +use {rustc_abi as abi, rustc_ast as ast, rustc_attr_data_structures as attr, rustc_hir as hir}; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; @@ -119,6 +122,21 @@ rustc_queries! { desc { "perform lints prior to AST lowering" } } + /// Tracked access to environment variables. + /// + /// Useful for the implementation of `std::env!`, `proc-macro`s change + /// detection and other changes in the compiler's behaviour that is easier + /// to control with an environment variable than a flag. + /// + /// NOTE: This currently does not work with dependency info in the + /// analysis, codegen and linking passes, place extra code at the top of + /// `rustc_interface::passes::write_dep_info` to make that work. + query env_var_os(key: &'tcx OsStr) -> Option<&'tcx OsStr> { + // Environment variables are global state + eval_always + desc { "get the value of an environment variable" } + } + query resolutions(_: ()) -> &'tcx ty::ResolverGlobalCtxt { no_hash desc { "getting the resolver outputs" } @@ -143,11 +161,11 @@ rustc_queries! { /// 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_crate()`), - /// 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_crate()`; instead, - /// prefer wrappers like [`TyCtxt::hir_visit_all_item_likes_in_crate`]. + /// If you call `tcx.hir_crate(())` 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_crate(())`; instead, prefer wrappers like + /// [`TyCtxt::hir_visit_all_item_likes_in_crate`]. query hir_crate(key: ()) -> &'tcx Crate<'tcx> { arena_cache eval_always @@ -179,7 +197,7 @@ rustc_queries! { /// Gives access to the HIR node's parent for the HIR owner `key`. /// - /// This can be conveniently accessed by methods on `tcx.hir()`. + /// This can be conveniently accessed by `tcx.hir_*` methods. /// Avoid calling this query directly. query hir_owner_parent(key: hir::OwnerId) -> hir::HirId { desc { |tcx| "getting HIR parent of `{}`", tcx.def_path_str(key) } @@ -187,7 +205,7 @@ rustc_queries! { /// Gives access to the HIR nodes and bodies inside `key` if it's a HIR owner. /// - /// This can be conveniently accessed by methods on `tcx.hir()`. + /// This can be conveniently accessed by `tcx.hir_*` methods. /// Avoid calling this query directly. query opt_hir_owner_nodes(key: LocalDefId) -> Option<&'tcx hir::OwnerNodes<'tcx>> { desc { |tcx| "getting HIR owner items in `{}`", tcx.def_path_str(key) } @@ -196,9 +214,9 @@ rustc_queries! { /// Gives access to the HIR attributes inside the HIR owner `key`. /// - /// This can be conveniently accessed by methods on `tcx.hir()`. + /// This can be conveniently accessed by `tcx.hir_*` methods. /// Avoid calling this query directly. - query hir_attrs(key: hir::OwnerId) -> &'tcx hir::AttributeMap<'tcx> { + query hir_attr_map(key: hir::OwnerId) -> &'tcx hir::AttributeMap<'tcx> { desc { |tcx| "getting HIR owner attributes in `{}`", tcx.def_path_str(key) } feedable } @@ -249,6 +267,8 @@ rustc_queries! { /// /// This is a specialized instance of [`Self::type_of`] that detects query cycles. /// Unless `CyclePlaceholder` needs to be handled separately, call [`Self::type_of`] instead. + /// This is used to improve the error message in cases where revealing the hidden type + /// for auto-trait leakage cycles. /// /// # Panics /// @@ -260,10 +280,16 @@ rustc_queries! { } cycle_stash } + query type_of_opaque_hir_typeck(key: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { + desc { |tcx| + "computing type of opaque `{path}` via HIR typeck", + path = tcx.def_path_str(key), + } + } /// Returns whether the type alias given by `DefId` is lazy. /// - /// I.e., if the type alias expands / ought to expand to a [weak] [alias type] + /// I.e., if the type alias expands / ought to expand to a [free] [alias type] /// instead of the underyling aliased type. /// /// Relevant for features `lazy_type_alias` and `type_alias_impl_trait`. @@ -272,7 +298,7 @@ rustc_queries! { /// /// This query *may* panic if the given definition is not a type alias. /// - /// [weak]: rustc_middle::ty::Weak + /// [free]: rustc_middle::ty::Free /// [alias type]: rustc_middle::ty::AliasTy query type_alias_is_lazy(key: DefId) -> bool { desc { |tcx| @@ -361,6 +387,15 @@ rustc_queries! { } } + query nested_bodies_within( + key: LocalDefId + ) -> &'tcx ty::List { + desc { + |tcx| "computing the coroutines defined within `{}`", + tcx.def_path_str(key.to_def_id()) + } + } + /// Returns the explicitly user-written *bounds* on the associated or opaque type given by `DefId` /// that must be proven true at definition site (and which can be assumed at usage sites). /// @@ -458,7 +493,7 @@ rustc_queries! { desc { "computing `#[expect]`ed lints in this crate" } } - query lints_that_dont_need_to_run(_: ()) -> &'tcx FxIndexSet { + query lints_that_dont_need_to_run(_: ()) -> &'tcx UnordSet { arena_cache desc { "Computing all lints that are explicitly enabled or with a default level greater than Allow" } } @@ -594,6 +629,7 @@ rustc_queries! { query check_coroutine_obligations(key: LocalDefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "verify auto trait bounds for coroutine interior type `{}`", tcx.def_path_str(key) } + return_result_from_ensure_ok } /// MIR after our optimization passes have run. This is MIR that is ready @@ -999,10 +1035,11 @@ rustc_queries! { separate_provide_extern } - query self_ty_of_trait_impl_enabling_order_dep_trait_object_hack( - key: DefId - ) -> Option>> { - desc { |tcx| "computing self type wrt issue #33140 `{}`", tcx.def_path_str(key) } + /// Given an `impl_def_id`, return true if the self type is guaranteed to be unsized due + /// to either being one of the built-in unsized types (str/slice/dyn) or to be a struct + /// whose tail is one of those types. + query impl_self_is_guaranteed_unsized(impl_def_id: DefId) -> bool { + desc { |tcx| "computing whether `{}` has a guaranteed unsized self type", tcx.def_path_str(impl_def_id) } } /// Maps a `DefId` of a type to a list of its inherent impls. @@ -1021,13 +1058,12 @@ rustc_queries! { /// Unsafety-check this `LocalDefId`. query check_unsafety(key: LocalDefId) { desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { true } } /// Checks well-formedness of tail calls (`become f()`). query check_tail_calls(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { desc { |tcx| "tail-call-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { true } + return_result_from_ensure_ok } /// Returns the types assumed to be well formed while "inside" of the given item. @@ -1133,11 +1169,10 @@ rustc_queries! { return_result_from_ensure_ok } - /// 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> { + /// Borrow-checks the given typeck root, e.g. functions, const/static items, + /// and its children, e.g. closures, inline consts. + query mir_borrowck(key: LocalDefId) -> Result<&'tcx mir::ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> { desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) } } /// Gets a complete map from all types to their inherent impls. @@ -1296,7 +1331,7 @@ rustc_queries! { query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { true } + return_result_from_ensure_ok } /// Performs part of the privacy check and computes effective visibilities. @@ -1324,7 +1359,11 @@ rustc_queries! { /// Generates a MIR body for the shim. query mir_shims(key: ty::InstanceKind<'tcx>) -> &'tcx mir::Body<'tcx> { arena_cache - desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) } + desc { + |tcx| "generating MIR shim for `{}`, instance={:?}", + tcx.def_path_str(key.def_id()), + key + } } /// The `symbol_name` query provides the symbol name for calling a @@ -1416,8 +1455,8 @@ rustc_queries! { desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) } } - query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::Ident] { - desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } + query fn_arg_idents(def_id: DefId) -> &'tcx [Option] { + desc { |tcx| "looking up function parameter identifiers for `{}`", tcx.def_path_str(def_id) } separate_provide_extern } @@ -1430,7 +1469,7 @@ rustc_queries! { } /// Gets the rendered precise capturing args for an opaque for use in rustdoc. - query rendered_precise_capturing_args(def_id: DefId) -> Option<&'tcx [Symbol]> { + query rendered_precise_capturing_args(def_id: DefId) -> Option<&'tcx [PreciseCapturingArgKind]> { desc { |tcx| "rendering precise capturing args for `{}`", tcx.def_path_str(def_id) } separate_provide_extern } @@ -1490,6 +1529,11 @@ rustc_queries! { desc { "finding local trait impls" } } + /// Return all `impl` blocks of the given trait in the current crate. + query local_trait_impls(trait_id: DefId) -> &'tcx [LocalDefId] { + desc { "finding local trait impls of `{}`", tcx.def_path_str(trait_id) } + } + /// Given a trait `trait_id`, return all known `impl` blocks. query trait_impls_of(trait_id: DefId) -> &'tcx ty::trait_def::TraitImpls { arena_cache @@ -1533,6 +1577,11 @@ rustc_queries! { query is_copy_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Copy`", env.value } } + /// Trait selection queries. These are best used by invoking `ty.is_use_cloned_modulo_regions()`, + /// `ty.is_use_cloned()`, etc, since that will prune the environment where possible. + query is_use_cloned_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `UseCloned`", env.value } + } /// Query backing `Ty::is_sized`. query is_sized_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Sized`", env.value } @@ -1545,6 +1594,10 @@ rustc_queries! { query is_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Unpin`", env.value } } + /// Query backing `Ty::is_async_drop`. + query is_async_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `AsyncDrop`", env.value } + } /// Query backing `Ty::needs_drop`. query needs_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` needs drop", env.value } @@ -1577,6 +1630,14 @@ rustc_queries! { cache_on_disk_if { true } } + /// A list of types where the ADT requires async drop if and only if any of + /// those types require async drop. If the ADT is known to always need async drop + /// then `Err(AlwaysRequiresDrop)` is returned. + query adt_async_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { + desc { |tcx| "computing when `{}` needs async drop", tcx.def_path_str(def_id) } + cache_on_disk_if { true } + } + /// A list of types where the ADT requires drop if and only if any of those types /// has significant drop. A type marked with the attribute `rustc_insignificant_dtor` /// is considered to not be significant. A drop is significant if it is implemented @@ -1585,7 +1646,6 @@ rustc_queries! { /// `Err(AlwaysRequiresDrop)` is returned. query adt_significant_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { desc { |tcx| "computing when `{}` has a significant destructor", tcx.def_path_str(def_id) } - cache_on_disk_if { false } } /// Returns a list of types which (a) have a potentially significant destructor @@ -1607,7 +1667,6 @@ rustc_queries! { /// Otherwise, there is a risk of query cycles. query list_significant_drop_tys(ty: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> &'tcx ty::List> { desc { |tcx| "computing when `{}` has a significant destructor", ty.value } - cache_on_disk_if { false } } /// Computes the layout of a type. Note that this implicitly @@ -1866,6 +1925,11 @@ rustc_queries! { // The macro which defines `rustc_metadata::provide_extern` depends on this query's name. // Changing the name should cause a compiler error, but in case that changes, be aware. + // + // The hash should not be calculated before the `analysis` pass is complete, specifically + // until `tcx.untracked().definitions.freeze()` has been called, otherwise if incremental + // compilation is enabled calculating this hash can freeze this structure too early in + // compilation and cause subsequent crashes when attempting to write to `definitions` query crate_hash(_: CrateNum) -> Svh { eval_always desc { "looking up the hash a crate" } @@ -2141,7 +2205,7 @@ rustc_queries! { query maybe_unused_trait_imports(_: ()) -> &'tcx FxIndexSet { desc { "fetching potentially unused trait imports" } } - query names_imported_by_glob_use(def_id: LocalDefId) -> &'tcx UnordSet { + query names_imported_by_glob_use(def_id: LocalDefId) -> &'tcx FxIndexSet { desc { |tcx| "finding names imported by glob use for `{}`", tcx.def_path_str(def_id) } } @@ -2175,6 +2239,16 @@ rustc_queries! { separate_provide_extern } + query stable_order_of_exportable_impls(_: CrateNum) -> &'tcx FxIndexMap { + desc { "fetching the stable impl's order" } + separate_provide_extern + } + + query exportable_items(_: CrateNum) -> &'tcx [DefId] { + desc { "fetching all exportable items in a crate" } + separate_provide_extern + } + /// The list of symbols exported from the given crate. /// /// - All names contained in `exported_symbols(cnum)` are guaranteed to @@ -2232,7 +2306,7 @@ rustc_queries! { /// Do not call this query directly: Invoke `normalize` instead. /// /// - query normalize_canonicalized_weak_ty( + query normalize_canonicalized_free_alias( goal: CanonicalAliasGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, @@ -2262,22 +2336,13 @@ rustc_queries! { desc { "normalizing `{}`", goal.value } } - query implied_outlives_bounds_compat( - goal: CanonicalImpliedOutlivesBoundsGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, - NoSolution, - > { - desc { "computing implied outlives bounds for `{}`", goal.canonical.value.value.ty } - } - query implied_outlives_bounds( - goal: CanonicalImpliedOutlivesBoundsGoal<'tcx> + key: (CanonicalImpliedOutlivesBoundsGoal<'tcx>, bool) ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, NoSolution, > { - desc { "computing implied outlives bounds v2 for `{}`", goal.canonical.value.value.ty } + desc { "computing implied outlives bounds for `{}` (hack disabled = {:?})", key.0.canonical.value.value.ty, key.1 } } /// Do not call this query directly: @@ -2416,7 +2481,7 @@ rustc_queries! { query resolve_instance_raw( key: ty::PseudoCanonicalInput<'tcx, (DefId, GenericArgsRef<'tcx>)> ) -> Result>, ErrorGuaranteed> { - desc { "resolving instance `{}`", ty::Instance::new(key.value.0, key.value.1) } + desc { "resolving instance `{}`", ty::Instance::new_raw(key.value.0, key.value.1) } } query reveal_opaque_types_in_bounds(key: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> { @@ -2504,7 +2569,6 @@ rustc_queries! { /// monomorphized. query check_mono_item(key: ty::Instance<'tcx>) { desc { "monomorphization-time checking" } - cache_on_disk_if { true } } /// Builds the set of functions that should be skipped for the move-size check. @@ -2524,5 +2588,5 @@ rustc_queries! { } } -rustc_query_append! { define_callbacks! } +rustc_with_all_queries! { define_callbacks! } rustc_feedable_queries! { define_feedable! } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index d9035efaf56d0..e1876f8f0f9b6 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -2,7 +2,7 @@ use std::collections::hash_map::Entry; use std::mem; use std::sync::Arc; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::sync::{HashMapExt, Lock, RwLock}; use rustc_data_structures::unhash::UnhashMap; @@ -11,12 +11,12 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE, LocalDefId, Stab use rustc_hir::definitions::DefPathHash; use rustc_index::{Idx, IndexVec}; use rustc_macros::{Decodable, Encodable}; -use rustc_query_system::query::QuerySideEffects; +use rustc_query_system::query::QuerySideEffect; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_session::Session; use rustc_span::hygiene::{ - ExpnId, HygieneDecodeContext, HygieneEncodeContext, SyntaxContext, SyntaxContextData, + ExpnId, HygieneDecodeContext, HygieneEncodeContext, SyntaxContext, SyntaxContextKey, }; use rustc_span::source_map::Spanned; use rustc_span::{ @@ -45,7 +45,7 @@ const TAG_EXPN_DATA: u8 = 1; // Tags for encoding Symbol's const SYMBOL_STR: u8 = 0; const SYMBOL_OFFSET: u8 = 1; -const SYMBOL_PREINTERNED: u8 = 2; +const SYMBOL_PREDEFINED: u8 = 2; /// Provides an interface to incremental compilation data cached from the /// previous compilation session. This data will eventually include the results @@ -55,9 +55,9 @@ pub struct OnDiskCache { // The complete cache data in serialized form. serialized_data: RwLock>, - // Collects all `QuerySideEffects` created during the current compilation + // Collects all `QuerySideEffect` created during the current compilation // session. - current_side_effects: Lock>, + current_side_effects: Lock>, file_index_to_stable_id: FxHashMap, @@ -68,7 +68,7 @@ pub struct OnDiskCache { // `serialized_data`. query_result_index: FxHashMap, - // A map from dep-node to the position of any associated `QuerySideEffects` in + // A map from dep-node to the position of any associated `QuerySideEffect` in // `serialized_data`. prev_side_effects_index: FxHashMap, @@ -270,10 +270,10 @@ impl OnDiskCache { .current_side_effects .borrow() .iter() - .map(|(dep_node_index, side_effects)| { + .map(|(dep_node_index, side_effect)| { let pos = AbsoluteBytePos::new(encoder.position()); let dep_node_index = SerializedDepNodeIndex::new(dep_node_index.index()); - encoder.encode_tagged(dep_node_index, side_effects); + encoder.encode_tagged(dep_node_index, side_effect); (dep_node_index, pos) }) @@ -352,24 +352,23 @@ impl OnDiskCache { }) } - /// Loads a `QuerySideEffects` created during the previous compilation session. - pub fn load_side_effects( + /// Loads a `QuerySideEffect` created during the previous compilation session. + pub fn load_side_effect( &self, tcx: TyCtxt<'_>, dep_node_index: SerializedDepNodeIndex, - ) -> QuerySideEffects { - let side_effects: Option = + ) -> Option { + let side_effect: Option = self.load_indexed(tcx, dep_node_index, &self.prev_side_effects_index); - - side_effects.unwrap_or_default() + side_effect } - /// Stores a `QuerySideEffects` emitted during the current compilation session. - /// Anything stored like this will be available via `load_side_effects` in + /// Stores a `QuerySideEffect` emitted during the current compilation session. + /// Anything stored like this will be available via `load_side_effect` in /// the next compilation session. - pub fn store_side_effects(&self, dep_node_index: DepNodeIndex, side_effects: QuerySideEffects) { + pub fn store_side_effect(&self, dep_node_index: DepNodeIndex, side_effect: QuerySideEffect) { let mut current_side_effects = self.current_side_effects.borrow_mut(); - let prev = current_side_effects.insert(dep_node_index, side_effects); + let prev = current_side_effects.insert(dep_node_index, side_effect); debug_assert!(prev.is_none()); } @@ -395,21 +394,6 @@ impl OnDiskCache { opt_value } - /// Stores side effect 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`. - pub fn store_side_effects_for_anon_node( - &self, - dep_node_index: DepNodeIndex, - side_effects: QuerySideEffects, - ) { - let mut current_side_effects = self.current_side_effects.borrow_mut(); - - let x = current_side_effects.entry(dep_node_index).or_default(); - x.append(side_effects); - } - fn load_indexed<'tcx, T>( &self, tcx: TyCtxt<'tcx>, @@ -518,8 +502,7 @@ where value } -impl<'a, 'tcx> TyDecoder for CacheDecoder<'a, 'tcx> { - type I = TyCtxt<'tcx>; +impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> { const CLEAR_CROSS_CRATE: bool = false; #[inline] @@ -583,7 +566,7 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { // 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); + let data: SyntaxContextKey = decode_tagged(decoder, TAG_SYNTAX_CONTEXT); data }) }) @@ -690,9 +673,9 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { Symbol::intern(s) }) } - SYMBOL_PREINTERNED => { + SYMBOL_PREDEFINED => { let symbol_index = self.read_u32(); - Symbol::new_from_decoded(symbol_index) + Symbol::new(symbol_index) } _ => unreachable!(), } @@ -908,9 +891,9 @@ impl<'a, 'tcx> SpanEncoder for CacheEncoder<'a, 'tcx> { // copy&paste impl from rustc_metadata fn encode_symbol(&mut self, symbol: Symbol) { - // if symbol preinterned, emit tag and symbol index - if symbol.is_preinterned() { - self.encoder.emit_u8(SYMBOL_PREINTERNED); + // if symbol predefined, emit tag and symbol index + if symbol.is_predefined() { + self.encoder.emit_u8(SYMBOL_PREDEFINED); self.encoder.emit_u32(symbol.as_u32()); } else { // otherwise write it as string or as offset to it @@ -943,8 +926,7 @@ impl<'a, 'tcx> SpanEncoder for CacheEncoder<'a, 'tcx> { } } -impl<'a, 'tcx> TyEncoder for CacheEncoder<'a, 'tcx> { - type I = TyCtxt<'tcx>; +impl<'a, 'tcx> TyEncoder<'tcx> for CacheEncoder<'a, 'tcx> { const CLEAR_CROSS_CRATE: bool = false; #[inline] diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 66a9e5fed4c30..769df1ffd6f91 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -165,7 +165,7 @@ impl<'tcx> TyCtxt<'tcx> { } } -#[inline] +#[inline(always)] pub fn query_get_at<'tcx, Cache>( tcx: TyCtxt<'tcx>, execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, @@ -313,8 +313,11 @@ macro_rules! separate_provide_extern_default { macro_rules! define_callbacks { ( - $($(#[$attr:meta])* - [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { + $( + $(#[$attr:meta])* + [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty, + )* + ) => { #[allow(unused_lifetimes)] pub mod queries { @@ -366,11 +369,11 @@ macro_rules! define_callbacks { pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache>; - // Ensure that keys grow no larger than 80 bytes by accident. + // Ensure that keys grow no larger than 88 bytes by accident. // Increase this limit if necessary, but do try to keep the size low if possible #[cfg(target_pointer_width = "64")] const _: () = { - if mem::size_of::>() > 88 { + if size_of::>() > 88 { panic!("{}", concat!( "the query `", stringify!($name), @@ -386,7 +389,7 @@ macro_rules! define_callbacks { #[cfg(target_pointer_width = "64")] #[cfg(not(feature = "rustc_randomized_layouts"))] const _: () = { - if mem::size_of::>() > 64 { + if size_of::>() > 64 { panic!("{}", concat!( "the query `", stringify!($name), @@ -488,7 +491,7 @@ macro_rules! define_callbacks { #[derive(Default)] pub struct QueryStates<'tcx> { $( - pub $name: QueryState<$($K)*>, + pub $name: QueryState<$($K)*, QueryStackDeferred<'tcx>>, )* } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index e5592de81cd78..b9a014d14c0d0 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -19,7 +19,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd}; use rustc_index::{IndexVec, newtype_index}; -use rustc_macros::{HashStable, TypeVisitable}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeVisitable}; use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span, Symbol}; use rustc_target::asm::InlineAsmRegOrRegClass; @@ -27,7 +27,8 @@ use tracing::instrument; use crate::middle::region; use crate::mir::interpret::AllocId; -use crate::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp}; +use crate::mir::{self, AssignOp, BinOp, BorrowKind, FakeReadCause, UnOp}; +use crate::thir::visit::for_each_immediate_subpat; use crate::ty::adjustment::PointerCoercion; use crate::ty::layout::IntegerExt; use crate::ty::{ @@ -49,10 +50,13 @@ macro_rules! thir_with_elements { } )* + // Note: Making `Thir` implement `Clone` is useful for external tools that need access to + // THIR bodies even after the `Steal` query result has been stolen. + // One such tool is https://github.com/rust-corpus/qrates/. /// A container for a THIR body. /// /// This can be indexed directly by any THIR index (e.g. [`ExprId`]). - #[derive(Debug, HashStable)] + #[derive(Debug, HashStable, Clone)] pub struct Thir<'tcx> { pub body_type: BodyTy<'tcx>, $( @@ -90,7 +94,7 @@ thir_with_elements! { params: ParamId => Param<'tcx> => "p{}", } -#[derive(Debug, HashStable)] +#[derive(Debug, HashStable, Clone)] pub enum BodyTy<'tcx> { Const(Ty<'tcx>), Fn(FnSig<'tcx>), @@ -98,7 +102,7 @@ pub enum BodyTy<'tcx> { } /// Description of a type-checked function parameter. -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct Param<'tcx> { /// The pattern that appears in the parameter list, or None for implicit parameters. pub pat: Option>>, @@ -118,7 +122,7 @@ pub enum LintLevel { Explicit(HirId), } -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct Block { /// Whether the block itself has a label. Used by `label: {}` /// and `try` blocks. @@ -138,7 +142,7 @@ pub struct Block { type UserTy<'tcx> = Option>>; -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct AdtExpr<'tcx> { /// The ADT we're constructing. pub adt_def: AdtDef<'tcx>, @@ -155,7 +159,7 @@ pub struct AdtExpr<'tcx> { pub base: AdtExprBase<'tcx>, } -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub enum AdtExprBase<'tcx> { /// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`. None, @@ -168,7 +172,7 @@ pub enum AdtExprBase<'tcx> { DefaultFields(Box<[Ty<'tcx>]>), } -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct ClosureExpr<'tcx> { pub closure_id: LocalDefId, pub args: UpvarArgs<'tcx>, @@ -177,7 +181,7 @@ pub struct ClosureExpr<'tcx> { pub fake_reads: Vec<(ExprId, FakeReadCause, HirId)>, } -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct InlineAsmExpr<'tcx> { pub asm_macro: AsmMacro, pub template: &'tcx [InlineAsmTemplatePiece], @@ -195,12 +199,12 @@ pub enum BlockSafety { ExplicitUnsafe(HirId), } -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct Stmt<'tcx> { pub kind: StmtKind<'tcx>, } -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub enum StmtKind<'tcx> { /// An expression with a trailing semicolon. Expr { @@ -240,11 +244,11 @@ pub enum StmtKind<'tcx> { }, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, HashStable)] +#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] pub struct LocalVarId(pub HirId); /// A THIR expression. -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct Expr<'tcx> { /// kind of expression pub kind: ExprKind<'tcx>, @@ -271,7 +275,7 @@ pub struct TempLifetime { pub backwards_incompatible: Option, } -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub enum ExprKind<'tcx> { /// `Scope`s are used to explicitly mark destruction scopes, /// and to track the `HirId` of the expressions within the scope. @@ -288,7 +292,10 @@ pub enum ExprKind<'tcx> { If { if_then_scope: region::Scope, cond: ExprId, + /// `then` is always `ExprKind::Block`. then: ExprId, + /// If present, the `else_opt` expr is always `ExprKind::Block` (for + /// `else`) or `ExprKind::If` (for `else if`). else_opt: Option, }, /// A function call. Method calls and overloaded operators are converted to plain function calls. @@ -312,6 +319,14 @@ pub enum ExprKind<'tcx> { /// (e.g. `foo(a, b)` in `x.foo(a, b)`). fn_span: Span, }, + /// A use expression `x.use`. + ByUse { + /// The expression on which use is applied. + expr: ExprId, + /// The span of use, without the dot and receiver + /// (e.g. `use` in `x.use`). + span: Span, + }, /// A *non-overloaded* dereference. Deref { arg: ExprId, @@ -391,7 +406,7 @@ pub enum ExprKind<'tcx> { }, /// A *non-overloaded* operation assignment, e.g. `lhs += rhs`. AssignOp { - op: BinOp, + op: AssignOp, lhs: ExprId, rhs: ExprId, }, @@ -548,20 +563,20 @@ pub enum ExprKind<'tcx> { /// Represents the association of a field identifier and an expression. /// /// This is used in struct constructors. -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct FieldExpr { pub name: FieldIdx, pub expr: ExprId, } -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct FruInfo<'tcx> { pub base: ExprId, pub field_types: Box<[Ty<'tcx>]>, } /// A `match` arm. -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub struct Arm<'tcx> { pub pattern: Box>, pub guard: Option, @@ -579,7 +594,7 @@ pub enum LogicalOp { Or, } -#[derive(Debug, HashStable)] +#[derive(Clone, Debug, HashStable)] pub enum InlineAsmOperand<'tcx> { In { reg: InlineAsmRegOrRegClass, @@ -616,13 +631,13 @@ pub enum InlineAsmOperand<'tcx> { }, } -#[derive(Debug, HashStable, TypeVisitable)] +#[derive(Clone, Debug, HashStable, TypeVisitable)] pub struct FieldPat<'tcx> { pub field: FieldIdx, pub pattern: Pat<'tcx>, } -#[derive(Debug, HashStable, TypeVisitable)] +#[derive(Clone, Debug, HashStable, TypeVisitable)] pub struct Pat<'tcx> { pub ty: Ty<'tcx>, pub span: Span, @@ -661,27 +676,7 @@ impl<'tcx> Pat<'tcx> { return; } - use PatKind::*; - match &self.kind { - Wild - | Never - | Range(..) - | Binding { subpattern: None, .. } - | Constant { .. } - | Error(_) => {} - AscribeUserType { subpattern, .. } - | Binding { subpattern: Some(subpattern), .. } - | Deref { subpattern } - | DerefPattern { subpattern, .. } - | ExpandedConstant { subpattern, .. } => subpattern.walk_(it), - Leaf { subpatterns } | Variant { subpatterns, .. } => { - subpatterns.iter().for_each(|field| field.pattern.walk_(it)) - } - Or { pats } => pats.iter().for_each(|p| p.walk_(it)), - Array { box prefix, slice, box suffix } | Slice { box prefix, slice, box suffix } => { - prefix.iter().chain(slice.as_deref()).chain(suffix.iter()).for_each(|p| p.walk_(it)) - } - } + for_each_immediate_subpat(self, |p| p.walk_(it)); } /// Whether the pattern has a `PatKind::Error` nested within. @@ -729,7 +724,7 @@ impl<'tcx> Pat<'tcx> { } } -#[derive(Debug, HashStable, TypeVisitable)] +#[derive(Clone, Debug, HashStable, TypeVisitable)] pub struct Ascription<'tcx> { pub annotation: CanonicalUserTypeAnnotation<'tcx>, /// Variance to use when relating the `user_ty` to the **type of the value being @@ -753,8 +748,11 @@ pub struct Ascription<'tcx> { pub variance: ty::Variance, } -#[derive(Debug, HashStable, TypeVisitable)] +#[derive(Clone, Debug, HashStable, TypeVisitable)] pub enum PatKind<'tcx> { + /// A missing pattern, e.g. for an anonymous param in a bare fn like `fn f(u32)`. + Missing, + /// A wildcard pattern: `_`. Wild, @@ -772,8 +770,12 @@ pub enum PatKind<'tcx> { var: LocalVarId, ty: Ty<'tcx>, subpattern: Option>>, + /// Is this the leftmost occurrence of the binding, i.e., is `var` the /// `HirId` of this pattern? + /// + /// (The same binding can occur multiple times in different branches of + /// an or-pattern, but only one of them will be primary.) is_primary: bool, }, @@ -800,13 +802,18 @@ pub enum PatKind<'tcx> { /// Deref pattern, written `box P` for now. DerefPattern { subpattern: Box>, - mutability: hir::Mutability, + /// Whether the pattern scrutinee needs to be borrowed in order to call `Deref::deref` or + /// `DerefMut::deref_mut`, and if so, which. This is `ByRef::No` for deref patterns on + /// boxes; they are lowered using a built-in deref rather than a method call, thus they + /// don't borrow the scrutinee. + #[type_visitable(ignore)] + borrow: ByRef, }, /// One of the following: - /// * `&str`/`&[u8]` (represented as a valtree), which will be handled as a string/slice pattern - /// and thus exhaustiveness checking will detect if you use the same string/slice twice in - /// different patterns. + /// * `&str` (represented as a valtree), which will be handled as a string pattern and thus + /// exhaustiveness checking will detect if you use the same string twice in different + /// patterns. /// * integer, bool, char or float (represented as a valtree), which will be handled by /// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are /// much simpler. @@ -816,23 +823,17 @@ pub enum PatKind<'tcx> { }, /// Pattern obtained by converting a constant (inline or named) to its pattern - /// representation using `const_to_pat`. + /// representation using `const_to_pat`. This is used for unsafety checking. ExpandedConstant { - /// [DefId] of the constant, we need this so that we have a - /// reference that can be used by unsafety checking to visit nested - /// unevaluated constants and for diagnostics. If the `DefId` doesn't - /// correspond to a local crate, it points at the `const` item. + /// [DefId] of the constant item. def_id: DefId, - /// If `false`, then `def_id` points at a `const` item, otherwise it - /// corresponds to a local inline const. - is_inline: bool, - /// If the inline constant is used in a range pattern, this subpattern - /// represents the range (if both ends are inline constants, there will - /// be multiple InlineConstant wrappers). + /// The pattern that the constant lowered to. /// - /// Otherwise, the actual pattern that the constant lowered to. As with - /// other constants, inline constants are matched structurally where - /// possible. + /// HACK: we need to keep the `DefId` of inline constants around for unsafety checking; + /// therefore when a range pattern contains inline constants, we re-wrap the range pattern + /// with the `ExpandedConstant` nodes that correspond to the range endpoints. Hence + /// `subpattern` may actually be a range pattern, and `def_id` be the constant for one of + /// its endpoints. subpattern: Box>, }, diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index a9df4d1625be3..f3da2a5cc8e4e 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -59,6 +59,9 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( visitor.visit_expr(&visitor.thir()[arg]); } } + ByUse { expr, span: _ } => { + visitor.visit_expr(&visitor.thir()[expr]); + } Deref { arg } => visitor.visit_expr(&visitor.thir()[arg]), Binary { lhs, rhs, op: _ } | LogicalOp { lhs, rhs, op: _ } => { visitor.visit_expr(&visitor.thir()[lhs]); @@ -237,36 +240,46 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( visitor: &mut V, pat: &'thir Pat<'tcx>, ) { - use PatKind::*; + for_each_immediate_subpat(pat, |p| visitor.visit_pat(p)); +} + +/// Invokes `callback` on each immediate subpattern of `pat`, if any. +/// A building block for assembling THIR pattern visitors. +pub(crate) fn for_each_immediate_subpat<'a, 'tcx>( + pat: &'a Pat<'tcx>, + mut callback: impl FnMut(&'a Pat<'tcx>), +) { match &pat.kind { - AscribeUserType { subpattern, ascription: _ } - | Deref { subpattern } - | DerefPattern { subpattern, .. } - | Binding { subpattern: Some(subpattern), .. } => visitor.visit_pat(subpattern), - Binding { .. } | Wild | Never | Error(_) => {} - Variant { subpatterns, adt_def: _, args: _, variant_index: _ } | Leaf { subpatterns } => { - for subpattern in subpatterns { - visitor.visit_pat(&subpattern.pattern); + PatKind::Missing + | PatKind::Wild + | PatKind::Binding { subpattern: None, .. } + | PatKind::Constant { value: _ } + | PatKind::Range(_) + | PatKind::Never + | PatKind::Error(_) => {} + + PatKind::AscribeUserType { subpattern, .. } + | PatKind::Binding { subpattern: Some(subpattern), .. } + | PatKind::Deref { subpattern } + | PatKind::DerefPattern { subpattern, .. } + | PatKind::ExpandedConstant { subpattern, .. } => callback(subpattern), + + PatKind::Variant { subpatterns, .. } | PatKind::Leaf { subpatterns } => { + for field_pat in subpatterns { + callback(&field_pat.pattern); } } - Constant { value: _ } => {} - ExpandedConstant { def_id: _, is_inline: _, subpattern } => visitor.visit_pat(subpattern), - Range(_) => {} - Slice { prefix, slice, suffix } | Array { prefix, slice, suffix } => { - for subpattern in prefix.iter() { - visitor.visit_pat(subpattern); - } - if let Some(pat) = slice { - visitor.visit_pat(pat); - } - for subpattern in suffix.iter() { - visitor.visit_pat(subpattern); + + PatKind::Slice { prefix, slice, suffix } | PatKind::Array { prefix, slice, suffix } => { + for pat in prefix.iter().chain(slice.as_deref()).chain(suffix) { + callback(pat); } } - Or { pats } => { - for pat in pats.iter() { - visitor.visit_pat(pat); + + PatKind::Or { pats } => { + for pat in pats { + callback(pat); } } - }; + } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index d033ecc75dbe5..27079af06fcd3 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -266,7 +266,7 @@ pub enum ObligationCauseCode<'tcx> { }, /// Constant expressions must be sized. - ConstSized, + SizedConstOrStatic, /// `static` items must have `Sync` type. SharedStatic, @@ -397,14 +397,14 @@ pub enum ObligationCauseCode<'tcx> { RustCall, - /// Obligations to prove that a `std::ops::Drop` impl is not stronger than + /// Obligations to prove that a `Drop` or negative auto trait impl is not stronger than /// the ADT it's being implemented for. - DropImpl, + AlwaysApplicableImpl, /// Requirement for a `const N: Ty` to implement `Ty: ConstParamTy` ConstParam(Ty<'tcx>), - /// Obligations emitted during the normalization of a weak type alias. + /// Obligations emitted during the normalization of a free type alias. TypeAlias(ObligationCauseCodeHandle<'tcx>, Span, DefId), } @@ -980,12 +980,9 @@ pub enum CodegenObligationError { /// overflow bug, since I believe this is the only case /// where ambiguity can result. Ambiguity, - /// 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. - /// This can also trigger when we have a global bound that is not actually satisfied, - /// but was included during typeck due to the trivial_bounds feature. + /// This can trigger when we have a global bound that is not actually satisfied + /// due to trivial bounds. Unimplemented, - FulfillmentError, /// The selected impl has unconstrained generic parameters. This will emit an error /// during impl WF checking. UnconstrainedParam(ErrorGuaranteed), diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 76f3d2bab9cf4..3f6faa1a572d9 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -181,7 +181,7 @@ pub struct MethodAutoderefBadTy<'tcx> { pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, } -/// Result of the `normalize_canonicalized_{{,inherent_}projection,weak}_ty` queries. +/// Result of the `normalize_canonicalized_{{,inherent_}projection,free}_ty` queries. #[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct NormalizationResult<'tcx> { /// Result of the normalization. diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 811bd8fb4588a..aa2ee756bc502 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -95,10 +95,16 @@ pub type EvaluationCache<'tcx, ENV> = Cache<(ENV, ty::PolyTraitPredicate<'tcx>), /// parameter environment. #[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)] pub enum SelectionCandidate<'tcx> { + /// A built-in implementation for the `Sized` trait. This is preferred + /// over all other candidates. + SizedCandidate { + has_nested: bool, + }, + /// A builtin implementation for some specific traits, used in cases /// where we cannot rely an ordinary library implementations. /// - /// The most notable examples are `sized`, `Copy` and `Clone`. This is also + /// The most notable examples are `Copy` and `Clone`. This is also /// used for the `DiscriminantKind` and `Pointee` trait, both of which have /// an associated type. BuiltinCandidate { diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 9c74f6263b3ce..145561b76c4e2 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -3,9 +3,9 @@ use rustc_macros::HashStable; use rustc_type_ir as ir; pub use rustc_type_ir::solve::*; -use crate::ty::visit::try_visit; use crate::ty::{ self, FallibleTypeFolder, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, + try_visit, }; pub type Goal<'tcx, P> = ir::solve::Goal, P>; diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 9ce5373b03118..05c19db4caa8c 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -6,8 +6,7 @@ use rustc_span::sym; use crate::error::StrictCoherenceNeedsNegativeCoherence; use crate::ty::fast_reject::SimplifiedType; -use crate::ty::visit::TypeVisitableExt; -use crate::ty::{self, TyCtxt}; +use crate::ty::{self, TyCtxt, TypeVisitableExt}; /// 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 @@ -72,7 +71,7 @@ impl OverlapMode { .as_local() .into_iter() .flat_map(|local_def_id| { - tcx.hir().attrs(tcx.local_def_id_to_hir_id(local_def_id)) + tcx.hir_attrs(tcx.local_def_id_to_hir_id(local_def_id)) }) .find(|attr| attr.has_name(sym::rustc_strict_coherence)) .map(|attr| attr.span()); diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index f8ab555305f05..a61a6c571a2cd 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -5,7 +5,7 @@ use rustc_hir::lang_items::LangItem; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::Span; -use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{Ty, TyCtxt}; #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] pub enum PointerCoercion { @@ -133,7 +133,7 @@ impl OverloadedDeref { }; tcx.associated_items(trait_def_id) .in_definition_order() - .find(|m| m.kind == ty::AssocKind::Fn) + .find(|item| item.is_fn()) .unwrap() .def_id } @@ -214,3 +214,25 @@ pub enum CustomCoerceUnsized { /// Records the index of the field being coerced. Struct(FieldIdx), } + +/// Represents an implicit coercion applied to the scrutinee of a match before testing a pattern +/// against it. Currently, this is used only for implicit dereferences. +#[derive(Clone, Copy, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] +pub struct PatAdjustment<'tcx> { + pub kind: PatAdjust, + /// The type of the scrutinee before the adjustment is applied, or the "adjusted type" of the + /// pattern. + pub source: Ty<'tcx>, +} + +/// Represents implicit coercions of patterns' types, rather than values' types. +#[derive(Clone, Copy, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] +pub enum PatAdjust { + /// An implicit dereference before matching, such as when matching the pattern `0` against a + /// scrutinee of type `&u8` or `&mut u8`. + BuiltinDeref, + /// An implicit call to `Deref(Mut)::deref(_mut)` before matching, such as when matching the + /// pattern `[..]` against a scrutinee of type `Vec`. + OverloadedDeref, +} diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 3585f28b4a550..d92b4f9c06beb 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -53,8 +53,8 @@ bitflags::bitflags! { const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8; /// Indicates whether the type is `UnsafeCell`. const IS_UNSAFE_CELL = 1 << 9; - /// Indicates whether the type is anonymous. - const IS_ANONYMOUS = 1 << 10; + /// Indicates whether the type is `UnsafePinned`. + const IS_UNSAFE_PINNED = 1 << 10; } } rustc_data_structures::external_bitflags_debug! { AdtFlags } @@ -238,7 +238,7 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { } fn destructor(self, tcx: TyCtxt<'tcx>) -> Option { - Some(match self.destructor(tcx)?.constness { + Some(match tcx.constness(self.destructor(tcx)?.did) { hir::Constness::Const => AdtDestructorKind::Const, hir::Constness::NotConst => AdtDestructorKind::NotConst, }) @@ -304,6 +304,9 @@ impl AdtDefData { if tcx.is_lang_item(did, LangItem::UnsafeCell) { flags |= AdtFlags::IS_UNSAFE_CELL; } + if tcx.is_lang_item(did, LangItem::UnsafePinned) { + flags |= AdtFlags::IS_UNSAFE_PINNED; + } AdtDefData { did, variants, flags, repr } } @@ -329,11 +332,22 @@ impl<'tcx> AdtDef<'tcx> { } /// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`. + /// + /// Note that this function will return `true` even if the ADT has been + /// defined in the crate currently being compiled. If that's not what you + /// want, see [`Self::variant_list_has_applicable_non_exhaustive`]. #[inline] pub fn is_variant_list_non_exhaustive(self) -> bool { self.flags().contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE) } + /// Returns `true` if the variant list of this ADT is `#[non_exhaustive]` + /// and has been defined in another crate. + #[inline] + pub fn variant_list_has_applicable_non_exhaustive(self) -> bool { + self.is_variant_list_non_exhaustive() && !self.did().is_local() + } + /// Returns the kind of the ADT. #[inline] pub fn adt_kind(self) -> AdtKind { @@ -396,16 +410,16 @@ impl<'tcx> AdtDef<'tcx> { self.flags().contains(AdtFlags::IS_UNSAFE_CELL) } - /// Returns `true` if this is `ManuallyDrop`. + /// Returns `true` if this is `UnsafePinned`. #[inline] - pub fn is_manually_drop(self) -> bool { - self.flags().contains(AdtFlags::IS_MANUALLY_DROP) + pub fn is_unsafe_pinned(self) -> bool { + self.flags().contains(AdtFlags::IS_UNSAFE_PINNED) } - /// Returns `true` if this is an anonymous adt + /// Returns `true` if this is `ManuallyDrop`. #[inline] - pub fn is_anonymous(self) -> bool { - self.flags().contains(AdtFlags::IS_ANONYMOUS) + pub fn is_manually_drop(self) -> bool { + self.flags().contains(AdtFlags::IS_MANUALLY_DROP) } /// Returns `true` if this type has a destructor. diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 6309dd2e490ff..78b2e265b488c 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -3,7 +3,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_macros::{Decodable, Encodable, HashStable}; -use rustc_span::{Ident, Symbol}; +use rustc_span::{Ident, Symbol, sym}; use super::{TyCtxt, Visibility}; use crate::ty; @@ -18,27 +18,33 @@ pub enum AssocItemContainer { #[derive(Copy, Clone, Debug, PartialEq, HashStable, Eq, Hash, Encodable, Decodable)] pub struct AssocItem { pub def_id: DefId, - pub name: Symbol, pub kind: AssocKind, pub container: AssocItemContainer, /// If this is an item in an impl of a trait then this is the `DefId` of /// the associated item on the trait that this implements. pub trait_item_def_id: Option, - - /// Whether this is a method with an explicit self - /// as its first parameter, allowing method calls. - pub fn_has_self_parameter: bool, - - /// `Some` if the associated item (an associated type) comes from the - /// return-position `impl Trait` in trait desugaring. The `ImplTraitInTraitData` - /// provides additional information about its source. - pub opt_rpitit_info: Option, } impl AssocItem { + // Gets the identifier, if it has one. + pub fn opt_name(&self) -> Option { + match self.kind { + ty::AssocKind::Type { data: AssocTypeData::Normal(name) } => Some(name), + ty::AssocKind::Type { data: AssocTypeData::Rpitit(_) } => None, + ty::AssocKind::Const { name } => Some(name), + ty::AssocKind::Fn { name, .. } => Some(name), + } + } + + // Gets the identifier name. Aborts if it lacks one, i.e. is an RPITIT + // associated type. + pub fn name(&self) -> Symbol { + self.opt_name().expect("name of non-Rpitit assoc item") + } + pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident { - Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap()) + Ident::new(self.name(), tcx.def_ident_span(self.def_id).unwrap()) } /// Gets the defaultness of the associated item. @@ -78,58 +84,115 @@ impl AssocItem { pub fn signature(&self, tcx: TyCtxt<'_>) -> String { match self.kind { - ty::AssocKind::Fn => { + 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).instantiate_identity().skip_binder().to_string() } - ty::AssocKind::Type => format!("type {};", self.name), - ty::AssocKind::Const => { - format!( - "const {}: {:?};", - self.name, - tcx.type_of(self.def_id).instantiate_identity() - ) + ty::AssocKind::Type { .. } => format!("type {};", self.name()), + ty::AssocKind::Const { name } => { + format!("const {}: {:?};", name, tcx.type_of(self.def_id).instantiate_identity()) } } } pub fn descr(&self) -> &'static str { match self.kind { - ty::AssocKind::Const => "const", - ty::AssocKind::Fn if self.fn_has_self_parameter => "method", - ty::AssocKind::Fn => "associated function", - ty::AssocKind::Type => "type", + ty::AssocKind::Const { .. } => "associated const", + ty::AssocKind::Fn { has_self: true, .. } => "method", + ty::AssocKind::Fn { has_self: false, .. } => "associated function", + ty::AssocKind::Type { .. } => "associated type", + } + } + + pub fn namespace(&self) -> Namespace { + match self.kind { + ty::AssocKind::Type { .. } => Namespace::TypeNS, + ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => Namespace::ValueNS, + } + } + + pub fn as_def_kind(&self) -> DefKind { + match self.kind { + AssocKind::Const { .. } => DefKind::AssocConst, + AssocKind::Fn { .. } => DefKind::AssocFn, + AssocKind::Type { .. } => DefKind::AssocTy, + } + } + pub fn is_type(&self) -> bool { + matches!(self.kind, ty::AssocKind::Type { .. }) + } + + pub fn is_fn(&self) -> bool { + matches!(self.kind, ty::AssocKind::Fn { .. }) + } + + pub fn is_method(&self) -> bool { + matches!(self.kind, ty::AssocKind::Fn { has_self: true, .. }) + } + + pub fn as_tag(&self) -> AssocTag { + match self.kind { + AssocKind::Const { .. } => AssocTag::Const, + AssocKind::Fn { .. } => AssocTag::Fn, + AssocKind::Type { .. } => AssocTag::Type, } } pub fn is_impl_trait_in_trait(&self) -> bool { - self.opt_rpitit_info.is_some() + matches!(self.kind, AssocKind::Type { data: AssocTypeData::Rpitit(_) }) + } + + /// Returns true if: + /// - This trait associated item has the `#[type_const]` attribute, + /// - If it is in a trait impl, the item from the original trait has this attribute, or + /// - It is an inherent assoc const. + pub fn is_type_const_capable(&self, tcx: TyCtxt<'_>) -> bool { + if !matches!(self.kind, ty::AssocKind::Const { .. }) { + return false; + } + + let def_id = match (self.container, self.trait_item_def_id) { + (AssocItemContainer::Trait, _) => self.def_id, + (AssocItemContainer::Impl, Some(trait_item_did)) => trait_item_did, + // Inherent impl but this attr is only applied to trait assoc items. + (AssocItemContainer::Impl, None) => return true, + }; + tcx.has_attr(def_id, sym::type_const) } } +#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)] +pub enum AssocTypeData { + Normal(Symbol), + /// The associated type comes from an RPITIT. It has no name, and the + /// `ImplTraitInTraitData` provides additional information about its + /// source. + Rpitit(ty::ImplTraitInTraitData), +} + #[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)] pub enum AssocKind { - Const, - Fn, - Type, + Const { name: Symbol }, + Fn { name: Symbol, has_self: bool }, + Type { data: AssocTypeData }, } impl AssocKind { pub fn namespace(&self) -> Namespace { match *self { - ty::AssocKind::Type => Namespace::TypeNS, - ty::AssocKind::Const | ty::AssocKind::Fn => Namespace::ValueNS, + 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, + AssocKind::Const { .. } => DefKind::AssocConst, + AssocKind::Fn { .. } => DefKind::AssocFn, + AssocKind::Type { .. } => DefKind::AssocTy, } } } @@ -137,13 +200,22 @@ impl AssocKind { impl std::fmt::Display for AssocKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - AssocKind::Fn => write!(f, "method"), - AssocKind::Const => write!(f, "associated const"), - AssocKind::Type => write!(f, "associated type"), + AssocKind::Fn { has_self: true, .. } => write!(f, "method"), + AssocKind::Fn { has_self: false, .. } => write!(f, "associated function"), + AssocKind::Const { .. } => write!(f, "associated const"), + AssocKind::Type { .. } => write!(f, "associated type"), } } } +// Like `AssocKind`, but just the tag, no fields. Used in various kinds of matching. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum AssocTag { + Const, + Fn, + Type, +} + /// 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 @@ -151,17 +223,17 @@ impl std::fmt::Display for AssocKind { /// done only on items with the same name. #[derive(Debug, Clone, PartialEq, HashStable)] pub struct AssocItems { - items: SortedIndexMultiMap, + items: SortedIndexMultiMap, ty::AssocItem>, } impl AssocItems { /// 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.name, item)).collect(); + let items = items_in_def_order.into_iter().map(|item| (item.opt_name(), item)).collect(); AssocItems { items } } - /// Returns a slice of associated items in the order they were defined. + /// Returns an iterator over 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. @@ -174,40 +246,33 @@ impl AssocItems { } /// Returns an iterator over all associated items with the given name, ignoring hygiene. + /// + /// Panics if `name.is_empty()` returns `true`. pub fn filter_by_name_unhygienic( &self, name: Symbol, ) -> impl '_ + Iterator { - self.items.get_by_key(name) + assert!(!name.is_empty()); + self.items.get_by_key(Some(name)) } - /// Returns the associated item with the given name and `AssocKind`, if one exists. - pub fn find_by_name_and_kind( + /// Returns the associated item with the given identifier and `AssocKind`, if one exists. + /// The identifier is matched hygienically. + pub fn find_by_ident_and_kind( &self, tcx: TyCtxt<'_>, ident: Ident, - kind: AssocKind, + assoc_tag: AssocTag, parent_def_id: DefId, ) -> Option<&ty::AssocItem> { self.filter_by_name_unhygienic(ident.name) - .filter(|item| item.kind == kind) + .filter(|item| item.as_tag() == assoc_tag) .find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id)) } - /// Returns the associated item with the given name and any of `AssocKind`, if one exists. - pub fn find_by_name_and_kinds( - &self, - tcx: TyCtxt<'_>, - ident: Ident, - // Sorted in order of what kinds to look at - kinds: &[AssocKind], - parent_def_id: DefId, - ) -> Option<&ty::AssocItem> { - kinds.iter().find_map(|kind| self.find_by_name_and_kind(tcx, ident, *kind, 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( + /// Returns the associated item with the given identifier in the given `Namespace`, if one + /// exists. The identifier is matched hygienically. + pub fn find_by_ident_and_namespace( &self, tcx: TyCtxt<'_>, ident: Ident, @@ -215,7 +280,7 @@ impl AssocItems { parent_def_id: DefId, ) -> Option<&ty::AssocItem> { self.filter_by_name_unhygienic(ident.name) - .filter(|item| item.kind.namespace() == ns) + .filter(|item| item.namespace() == ns) .find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id)) } } diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 3605f2402e7ff..ff9096695d4db 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -51,6 +51,9 @@ pub enum UpvarCapture { /// depending on inference. ByValue, + /// Upvar is captured by use. This is true when the closure is labeled `use`. + ByUse, + /// Upvar is captured by reference. ByRef(BorrowKind), } @@ -147,9 +150,9 @@ impl<'tcx> CapturedPlace<'tcx> { /// Return span pointing to use that resulted in selecting the captured path pub fn get_path_span(&self, tcx: TyCtxt<'tcx>) -> Span { if let Some(path_expr_id) = self.info.path_expr_id { - tcx.hir().span(path_expr_id) + tcx.hir_span(path_expr_id) } else if let Some(capture_kind_expr_id) = self.info.capture_kind_expr_id { - tcx.hir().span(capture_kind_expr_id) + tcx.hir_span(capture_kind_expr_id) } else { // Fallback on upvars mentioned if neither path or capture expr id is captured @@ -163,9 +166,9 @@ impl<'tcx> CapturedPlace<'tcx> { /// Return span pointing to use that resulted in selecting the current capture kind pub fn get_capture_kind_span(&self, tcx: TyCtxt<'tcx>) -> Span { if let Some(capture_kind_expr_id) = self.info.capture_kind_expr_id { - tcx.hir().span(capture_kind_expr_id) + tcx.hir_span(capture_kind_expr_id) } else if let Some(path_expr_id) = self.info.path_expr_id { - tcx.hir().span(path_expr_id) + tcx.hir_span(path_expr_id) } else { // Fallback on upvars mentioned if neither path or capture expr id is captured @@ -178,7 +181,7 @@ impl<'tcx> CapturedPlace<'tcx> { pub fn is_by_ref(&self) -> bool { match self.info.capture_kind { - ty::UpvarCapture::ByValue => false, + ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => false, ty::UpvarCapture::ByRef(..) => true, } } @@ -214,7 +217,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn closure_captures(self, def_id: LocalDefId) -> &'tcx [&'tcx ty::CapturedPlace<'tcx>] { if !self.is_closure_like(def_id.to_def_id()) { return &[]; - }; + } self.closure_typeinfo(def_id).captures } } @@ -293,7 +296,7 @@ pub struct CaptureInfo { pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String { let mut curr_string: String = match place.base { - HirPlaceBase::Upvar(upvar_id) => tcx.hir().name(upvar_id.var_path.hir_id).to_string(), + HirPlaceBase::Upvar(upvar_id) => tcx.hir_name(upvar_id.var_path.hir_id).to_string(), _ => bug!("Capture_information should only contain upvars"), }; diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index fe42a224d9f22..5ff87959a800d 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -14,9 +14,8 @@ use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; use rustc_serialize::{Decodable, Encodable}; -use rustc_span::Span; use rustc_span::source_map::Spanned; -pub use rustc_type_ir::{TyDecoder, TyEncoder}; +use rustc_span::{Span, SpanDecoder, SpanEncoder}; use crate::arena::ArenaAllocatable; use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos}; @@ -31,13 +30,45 @@ use crate::ty::{self, AdtDef, GenericArgsRef, Ty, TyCtxt}; /// This offset is also chosen so that the first byte is never < 0x80. pub const SHORTHAND_OFFSET: usize = 0x80; -pub trait EncodableWithShorthand: Copy + Eq + Hash { +pub trait TyEncoder<'tcx>: SpanEncoder { + const CLEAR_CROSS_CRATE: bool; + + 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); +} + +pub trait TyDecoder<'tcx>: SpanDecoder { + const CLEAR_CROSS_CRATE: bool; + + fn interner(&self) -> TyCtxt<'tcx>; + + fn cached_ty_for_shorthand(&mut self, shorthand: usize, or_insert_with: F) -> Ty<'tcx> + where + F: FnOnce(&mut Self) -> Ty<'tcx>; + + fn with_position(&mut self, pos: usize, f: F) -> R + where + F: FnOnce(&mut Self) -> R; + + fn positioned_at_shorthand(&self) -> bool { + (self.peek_byte() & (SHORTHAND_OFFSET as u8)) != 0 + } + + fn decode_alloc_id(&mut self) -> AllocId; +} + +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>> EncodableWithShorthand for Ty<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> EncodableWithShorthand<'tcx, E> for Ty<'tcx> { type Variant = ty::TyKind<'tcx>; #[inline] @@ -46,7 +77,7 @@ impl<'tcx, E: TyEncoder>> EncodableWithShorthand for Ty<'tcx } } -impl<'tcx, E: TyEncoder>> EncodableWithShorthand for ty::PredicateKind<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> EncodableWithShorthand<'tcx, E> for ty::PredicateKind<'tcx> { type Variant = ty::PredicateKind<'tcx>; #[inline] @@ -65,16 +96,16 @@ impl<'tcx, E: TyEncoder>> EncodableWithShorthand for ty::Pre /// /// `Decodable` can still be implemented in cases where `Decodable` is required /// by a trait bound. -pub trait RefDecodable<'tcx, D: TyDecoder>> { +pub trait RefDecodable<'tcx, D: TyDecoder<'tcx>> { fn decode(d: &mut D) -> &'tcx Self; } /// Encode the given value or a previously cached shorthand. pub fn encode_with_shorthand<'tcx, E, T, M>(encoder: &mut E, value: &T, cache: M) where - E: TyEncoder>, + E: TyEncoder<'tcx>, M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, - T: EncodableWithShorthand, + T: EncodableWithShorthand<'tcx, E>, // The discriminant and shorthand must have the same size. T::Variant: DiscriminantKind, { @@ -108,13 +139,13 @@ where } } -impl<'tcx, E: TyEncoder>> Encodable for Ty<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for Ty<'tcx> { fn encode(&self, e: &mut E) { encode_with_shorthand(e, self, TyEncoder::type_shorthands); } } -impl<'tcx, E: TyEncoder>> Encodable for ty::Predicate<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Predicate<'tcx> { fn encode(&self, e: &mut E) { let kind = self.kind(); kind.bound_vars().encode(e); @@ -122,76 +153,72 @@ impl<'tcx, E: TyEncoder>> Encodable for ty::Predicate<'tcx> } } -impl<'tcx, E: TyEncoder>> Encodable for ty::Clause<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Clause<'tcx> { fn encode(&self, e: &mut E) { self.as_predicate().encode(e); } } -impl<'tcx, E: TyEncoder>> Encodable for ty::Region<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Region<'tcx> { fn encode(&self, e: &mut E) { self.kind().encode(e); } } -impl<'tcx, E: TyEncoder>> Encodable for ty::Const<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Const<'tcx> { fn encode(&self, e: &mut E) { self.0.0.encode(e); } } -impl<'tcx, E: TyEncoder>> Encodable for ty::Pattern<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Pattern<'tcx> { fn encode(&self, e: &mut E) { self.0.0.encode(e); } } -impl<'tcx, E: TyEncoder>> Encodable for ty::ValTree<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::ValTree<'tcx> { fn encode(&self, e: &mut E) { self.0.0.encode(e); } } -impl<'tcx, E: TyEncoder>> Encodable for ConstAllocation<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ConstAllocation<'tcx> { fn encode(&self, e: &mut E) { self.inner().encode(e) } } -impl<'tcx, E: TyEncoder>> Encodable for AdtDef<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for AdtDef<'tcx> { fn encode(&self, e: &mut E) { self.0.0.encode(e) } } -impl<'tcx, E: TyEncoder>> Encodable for AllocId { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for AllocId { fn encode(&self, e: &mut E) { e.encode_alloc_id(self) } } -impl<'tcx, E: TyEncoder>> Encodable for CtfeProvenance { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for CtfeProvenance { fn encode(&self, e: &mut E) { self.into_parts().encode(e); } } -impl<'tcx, E: TyEncoder>> Encodable for ty::ParamEnv<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::ParamEnv<'tcx> { fn encode(&self, e: &mut E) { self.caller_bounds().encode(e); } } #[inline] -fn decode_arena_allocable< - 'tcx, - D: TyDecoder>, - T: ArenaAllocatable<'tcx> + Decodable, ->( +fn decode_arena_allocable<'tcx, D: TyDecoder<'tcx>, T: ArenaAllocatable<'tcx> + Decodable>( decoder: &mut D, ) -> &'tcx T where - D: TyDecoder, + D: TyDecoder<'tcx>, { decoder.interner().arena.alloc(Decodable::decode(decoder)) } @@ -199,18 +226,18 @@ where #[inline] fn decode_arena_allocable_slice< 'tcx, - D: TyDecoder>, + D: TyDecoder<'tcx>, T: ArenaAllocatable<'tcx> + Decodable, >( decoder: &mut D, ) -> &'tcx [T] where - D: TyDecoder, + D: TyDecoder<'tcx>, { decoder.interner().arena.alloc_from_iter( as Decodable>::decode(decoder)) } -impl<'tcx, D: TyDecoder>> Decodable for Ty<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for Ty<'tcx> { #[allow(rustc::usage_of_ty_tykind)] fn decode(decoder: &mut D) -> Ty<'tcx> { // Handle shorthands first, if we have a usize > 0x80. @@ -229,7 +256,7 @@ impl<'tcx, D: TyDecoder>> Decodable for Ty<'tcx> { } } -impl<'tcx, D: TyDecoder>> Decodable for ty::Predicate<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Predicate<'tcx> { fn decode(decoder: &mut D) -> ty::Predicate<'tcx> { let bound_vars = Decodable::decode(decoder); // Handle shorthands first, if we have a usize > 0x80. @@ -249,14 +276,14 @@ impl<'tcx, D: TyDecoder>> Decodable for ty::Predicate<'tcx> } } -impl<'tcx, D: TyDecoder>> Decodable for ty::Clause<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Clause<'tcx> { fn decode(decoder: &mut D) -> ty::Clause<'tcx> { let pred: ty::Predicate<'tcx> = Decodable::decode(decoder); pred.expect_clause() } } -impl<'tcx, D: TyDecoder>> Decodable for GenericArgsRef<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for GenericArgsRef<'tcx> { fn decode(decoder: &mut D) -> Self { let len = decoder.read_usize(); let tcx = decoder.interner(); @@ -266,7 +293,7 @@ impl<'tcx, D: TyDecoder>> Decodable for GenericArgsRef<'tcx> } } -impl<'tcx, D: TyDecoder>> Decodable for mir::Place<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for mir::Place<'tcx> { fn decode(decoder: &mut D) -> Self { let local: mir::Local = Decodable::decode(decoder); let len = decoder.read_usize(); @@ -277,13 +304,13 @@ impl<'tcx, D: TyDecoder>> Decodable for mir::Place<'tcx> { } } -impl<'tcx, D: TyDecoder>> Decodable for ty::Region<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Region<'tcx> { fn decode(decoder: &mut D) -> Self { ty::Region::new_from_kind(decoder.interner(), Decodable::decode(decoder)) } } -impl<'tcx, D: TyDecoder>> Decodable for CanonicalVarInfos<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for CanonicalVarInfos<'tcx> { fn decode(decoder: &mut D) -> Self { let len = decoder.read_usize(); decoder.interner().mk_canonical_var_infos_from_iter( @@ -292,26 +319,26 @@ impl<'tcx, D: TyDecoder>> Decodable for CanonicalVarInfos<'t } } -impl<'tcx, D: TyDecoder>> Decodable for AllocId { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for AllocId { fn decode(decoder: &mut D) -> Self { decoder.decode_alloc_id() } } -impl<'tcx, D: TyDecoder>> Decodable for CtfeProvenance { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for CtfeProvenance { fn decode(decoder: &mut D) -> Self { let parts = Decodable::decode(decoder); CtfeProvenance::from_parts(parts) } } -impl<'tcx, D: TyDecoder>> Decodable for ty::SymbolName<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::SymbolName<'tcx> { fn decode(decoder: &mut D) -> Self { ty::SymbolName::new(decoder.interner(), decoder.read_str()) } } -impl<'tcx, D: TyDecoder>> Decodable for ty::ParamEnv<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::ParamEnv<'tcx> { fn decode(d: &mut D) -> Self { let caller_bounds = Decodable::decode(d); ty::ParamEnv::new(caller_bounds) @@ -320,7 +347,7 @@ impl<'tcx, D: TyDecoder>> Decodable for ty::ParamEnv<'tcx> { macro_rules! impl_decodable_via_ref { ($($t:ty,)+) => { - $(impl<'tcx, D: TyDecoder>> Decodable for $t { + $(impl<'tcx, D: TyDecoder<'tcx>> Decodable for $t { fn decode(decoder: &mut D) -> Self { RefDecodable::decode(decoder) } @@ -328,7 +355,7 @@ macro_rules! impl_decodable_via_ref { } } -impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List> { +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { fn decode(decoder: &mut D) -> &'tcx Self { let len = decoder.read_usize(); decoder @@ -337,7 +364,7 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List>> RefDecodable<'tcx, D> +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { fn decode(decoder: &mut D) -> &'tcx Self { @@ -348,38 +375,38 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> } } -impl<'tcx, D: TyDecoder>> Decodable for ty::Const<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Const<'tcx> { fn decode(decoder: &mut D) -> Self { let kind: ty::ConstKind<'tcx> = Decodable::decode(decoder); decoder.interner().mk_ct_from_kind(kind) } } -impl<'tcx, D: TyDecoder>> Decodable for ty::Pattern<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Pattern<'tcx> { fn decode(decoder: &mut D) -> Self { decoder.interner().mk_pat(Decodable::decode(decoder)) } } -impl<'tcx, D: TyDecoder>> Decodable for ty::ValTree<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::ValTree<'tcx> { fn decode(decoder: &mut D) -> Self { decoder.interner().intern_valtree(Decodable::decode(decoder)) } } -impl<'tcx, D: TyDecoder>> Decodable for ConstAllocation<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ConstAllocation<'tcx> { fn decode(decoder: &mut D) -> Self { decoder.interner().mk_const_alloc(Decodable::decode(decoder)) } } -impl<'tcx, D: TyDecoder>> Decodable for AdtDef<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for AdtDef<'tcx> { fn decode(decoder: &mut D) -> Self { decoder.interner().mk_adt_def_from_data(Decodable::decode(decoder)) } } -impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for [(ty::Clause<'tcx>, Span)] { +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [(ty::Clause<'tcx>, Span)] { fn decode(decoder: &mut D) -> &'tcx Self { decoder .interner() @@ -388,9 +415,7 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for [(ty::Claus } } -impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> - for [(ty::PolyTraitRef<'tcx>, Span)] -{ +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [(ty::PolyTraitRef<'tcx>, Span)] { fn decode(decoder: &mut D) -> &'tcx Self { decoder .interner() @@ -399,7 +424,7 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> } } -impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for [Spanned>] { +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [Spanned>] { fn decode(decoder: &mut D) -> &'tcx Self { decoder .interner() @@ -408,9 +433,7 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for [Spanned>> RefDecodable<'tcx, D> - for ty::List -{ +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List { fn decode(decoder: &mut D) -> &'tcx Self { let len = decoder.read_usize(); decoder.interner().mk_bound_variable_kinds_from_iter( @@ -419,7 +442,16 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> } } -impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List> { +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { + fn decode(decoder: &mut D) -> &'tcx Self { + let len = decoder.read_usize(); + decoder.interner().mk_patterns_from_iter( + (0..len).map::, _>(|_| Decodable::decode(decoder)), + ) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { fn decode(decoder: &mut D) -> &'tcx Self { let len = decoder.read_usize(); decoder.interner().mk_const_list_from_iter( @@ -428,7 +460,7 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List>> RefDecodable<'tcx, D> +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::ListWithCachedTypeInfo> { fn decode(decoder: &mut D) -> &'tcx Self { @@ -439,7 +471,7 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> } } -impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List { +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List { fn decode(decoder: &mut D) -> &'tcx Self { let len = decoder.read_usize(); decoder @@ -448,7 +480,7 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List>> RefDecodable<'tcx, D> for ty::List { +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List { fn decode(decoder: &mut D) -> &'tcx Self { let len = decoder.read_usize(); decoder.interner().mk_local_def_ids_from_iter( @@ -457,15 +489,13 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List>> Decodable for &'tcx ty::List { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for &'tcx ty::List { fn decode(d: &mut D) -> Self { RefDecodable::decode(d) } } -impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> - for ty::List<(VariantIdx, FieldIdx)> -{ +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List<(VariantIdx, FieldIdx)> { fn decode(decoder: &mut D) -> &'tcx Self { let len = decoder.read_usize(); decoder.interner().mk_offset_of_from_iter( @@ -480,8 +510,9 @@ impl_decodable_via_ref! { &'tcx ty::List>, &'tcx traits::ImplSource<'tcx, ()>, &'tcx mir::Body<'tcx>, - &'tcx mir::BorrowCheckResult<'tcx>, + &'tcx mir::ConcreteOpaqueTypes<'tcx>, &'tcx ty::List, + &'tcx ty::List>, &'tcx ty::ListWithCachedTypeInfo>, &'tcx ty::List, &'tcx ty::List<(VariantIdx, FieldIdx)>, @@ -503,14 +534,14 @@ macro_rules! impl_arena_allocatable_decoder { ([]$args:tt) => {}; ([decode $(, $attrs:ident)*] [$name:ident: $ty:ty]) => { - impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for $ty { + impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for $ty { #[inline] fn decode(decoder: &mut D) -> &'tcx Self { decode_arena_allocable(decoder) } } - impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for [$ty] { + impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [$ty] { #[inline] fn decode(decoder: &mut D) -> &'tcx Self { decode_arena_allocable_slice(decoder) @@ -532,14 +563,14 @@ arena_types!(impl_arena_allocatable_decoders); macro_rules! impl_arena_copy_decoder { (<$tcx:tt> $($ty:ty,)*) => { - $(impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for $ty { + $(impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for $ty { #[inline] fn decode(decoder: &mut D) -> &'tcx Self { decoder.interner().arena.alloc(Decodable::decode(decoder)) } } - impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for [$ty] { + impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [$ty] { #[inline] fn decode(decoder: &mut D) -> &'tcx Self { decoder.interner().arena.alloc_from_iter( as Decodable>::decode(decoder)) diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index d30520a0222fc..dc5fe2d8f8b06 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use rustc_data_structures::intern::Interned; use rustc_error_messages::MultiSpan; use rustc_macros::HashStable; +use rustc_type_ir::walk::TypeWalker; use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; use crate::ty::{self, Ty, TyCtxt}; @@ -34,7 +35,7 @@ impl<'tcx> rustc_type_ir::inherent::IntoKind for Const<'tcx> { } } -impl<'tcx> rustc_type_ir::visit::Flags for Const<'tcx> { +impl<'tcx> rustc_type_ir::Flags for Const<'tcx> { fn flags(&self) -> TypeFlags { self.0.flags } @@ -243,4 +244,18 @@ impl<'tcx> Const<'tcx> { pub fn is_ct_infer(self) -> bool { matches!(self.kind(), ty::ConstKind::Infer(_)) } + + /// Iterator that walks `self` and any types reachable from + /// `self`, in depth-first order. Note that just walks the types + /// that appear in `self`, it does not descend into the fields of + /// structs or variants. For example: + /// + /// ```text + /// isize => { isize } + /// Foo> => { Foo>, Bar, isize } + /// [isize] => { [isize], isize } + /// ``` + pub fn walk(self) -> TypeWalker> { + TypeWalker::new(self.into()) + } } diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 7c9280fae16d6..9f5e31d894cb3 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -408,7 +408,7 @@ macro_rules! from_x_for_scalar_int { fn from(u: $ty) -> Self { Self { data: u128::from(u), - size: NonZero::new(std::mem::size_of::<$ty>() as u8).unwrap(), + size: NonZero::new(size_of::<$ty>() as u8).unwrap(), } } } @@ -424,7 +424,7 @@ macro_rules! from_scalar_int_for_x { fn from(int: ScalarInt) -> Self { // The `unwrap` cannot fail because to_bits (if it succeeds) // is guaranteed to return a value that fits into the size. - int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>())) + int.to_bits(Size::from_bytes(size_of::<$ty>())) .try_into().unwrap() } } diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 72263d8458085..2f21d19e03c70 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -33,7 +33,7 @@ pub enum ValTreeKind<'tcx> { /// The fields of any kind of aggregate. Structs, tuples and arrays are represented by /// listing their fields' values in order. /// - /// Enums are represented by storing their discriminant as a field, followed by all + /// Enums are represented by storing their variant index as a u32 field, followed by all /// the fields of the variant. /// /// ZST types are represented as an empty slice. diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index d0aa2b8cbdad3..0759fa3da428a 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -7,6 +7,8 @@ pub mod tls; use std::assert_matches::{assert_matches, debug_assert_matches}; use std::borrow::Borrow; use std::cmp::Ordering; +use std::env::VarError; +use std::ffi::OsStr; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::ops::{Bound, Deref}; @@ -19,6 +21,7 @@ use rustc_data_structures::defer; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; +use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -26,13 +29,12 @@ use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{ self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal, }; -use rustc_data_structures::unord::UnordSet; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan, }; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; -use rustc_hir::definitions::Definitions; +use rustc_hir::definitions::{DefPathData, Definitions, DisambiguatorState}; use rustc_hir::intravisit::VisitorExt; use rustc_hir::lang_items::LangItem; use rustc_hir::{self as hir, Attribute, HirId, Node, TraitCandidate}; @@ -49,11 +51,10 @@ use rustc_session::{Limit, MetadataKind, Session}; use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_type_ir::TyKind::*; -use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::lang_items::TraitSolverLangItem; pub use rustc_type_ir::lift::Lift; use rustc_type_ir::{ - CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo, elaborate, search_graph, + CollectAndApply, Interner, TypeFlags, TypeFoldable, WithCachedTypeInfo, elaborate, search_graph, }; use tracing::{debug, instrument}; @@ -105,7 +106,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) -> Self::PredefinedOpaques { self.mk_predefined_opaques_in_body(data) } - type DefiningOpaqueTypes = &'tcx ty::List; + type LocalDefIds = &'tcx ty::List; type CanonicalVars = CanonicalVarInfos<'tcx>; fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo]) -> Self::CanonicalVars { self.mk_canonical_var_infos(infos) @@ -135,6 +136,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type AllocId = crate::mir::interpret::AllocId; type Pat = Pattern<'tcx>; + type PatList = &'tcx List>; type Safety = hir::Safety; type Abi = ExternAbi; type Const = ty::Const<'tcx>; @@ -205,6 +207,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { self.type_of(def_id) } + fn type_of_opaque_hir_typeck(self, def_id: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { + self.type_of_opaque_hir_typeck(def_id) + } type AdtDef = ty::AdtDef<'tcx>; fn adt_def(self, adt_def_id: DefId) -> Self::AdtDef { @@ -222,7 +227,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } } DefKind::OpaqueTy => ty::Opaque, - DefKind::TyAlias => ty::Weak, + DefKind::TyAlias => ty::Free, kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), } } @@ -237,10 +242,18 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ty::AliasTermKind::ProjectionTy } } + DefKind::AssocConst => { + if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(alias.def_id)) + { + ty::AliasTermKind::InherentConst + } else { + ty::AliasTermKind::ProjectionConst + } + } DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy, - DefKind::TyAlias => ty::AliasTermKind::WeakTy, - DefKind::AssocConst => ty::AliasTermKind::ProjectionConst, - DefKind::AnonConst | DefKind::Const | DefKind::Ctor(_, CtorKind::Const) => { + DefKind::TyAlias => ty::AliasTermKind::FreeTy, + DefKind::Const => ty::AliasTermKind::FreeConst, + DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => { ty::AliasTermKind::UnevaluatedConst } kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), @@ -433,6 +446,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) } + fn impl_self_is_guaranteed_unsized(self, impl_def_id: DefId) -> bool { + self.impl_self_is_guaranteed_unsized(impl_def_id) + } + fn has_target_features(self, def_id: DefId) -> bool { !self.codegen_fn_attrs(def_id).target_features.is_empty() } @@ -445,6 +462,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.is_lang_item(def_id, trait_lang_item_to_lang_item(lang_item)) } + fn is_default_trait(self, def_id: DefId) -> bool { + self.is_default_trait(def_id) + } + fn as_lang_item(self, def_id: DefId) -> Option { lang_item_to_trait_lang_item(self.lang_items().from_def_id(def_id)?) } @@ -452,7 +473,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator { self.associated_items(def_id) .in_definition_order() - .filter(|assoc_item| matches!(assoc_item.kind, ty::AssocKind::Type)) + .filter(|assoc_item| assoc_item.is_type()) .map(|assoc_item| assoc_item.def_id) } @@ -578,6 +599,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.defaultness(def_id).has_value() } + fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool { + self.specializes((impl_def_id, victim_def_id)) + } + fn impl_is_default(self, impl_def_id: DefId) -> bool { self.defaultness(impl_def_id).is_default() } @@ -662,9 +687,26 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.anonymize_bound_vars(binder) } - fn opaque_types_defined_by(self, defining_anchor: LocalDefId) -> Self::DefiningOpaqueTypes { + fn opaque_types_defined_by(self, defining_anchor: LocalDefId) -> Self::LocalDefIds { self.opaque_types_defined_by(defining_anchor) } + + fn opaque_types_and_coroutines_defined_by( + self, + defining_anchor: Self::LocalDefId, + ) -> Self::LocalDefIds { + if self.next_trait_solver_globally() { + let coroutines_defined_by = self + .nested_bodies_within(defining_anchor) + .iter() + .filter(|def_id| self.is_coroutine(def_id.to_def_id())); + self.mk_local_def_ids_from_iter( + self.opaque_types_defined_by(defining_anchor).iter().chain(coroutines_defined_by), + ) + } else { + self.opaque_types_defined_by(defining_anchor) + } + } } macro_rules! bidirectional_lang_item_map { @@ -686,7 +728,6 @@ macro_rules! bidirectional_lang_item_map { bidirectional_lang_item_map! { // tidy-alphabetical-start - AsyncDestruct, AsyncFn, AsyncFnKindHelper, AsyncFnKindUpvars, @@ -812,6 +853,7 @@ pub struct CtxtInterners<'tcx> { captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>, offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>, valtree: InternedSet<'tcx, ty::ValTreeKind<'tcx>>, + patterns: InternedSet<'tcx, List>>, } impl<'tcx> CtxtInterners<'tcx> { @@ -848,6 +890,7 @@ impl<'tcx> CtxtInterners<'tcx> { captures: InternedSet::with_capacity(N), offset_of: InternedSet::with_capacity(N), valtree: InternedSet::with_capacity(N), + patterns: InternedSet::with_capacity(N), } } @@ -858,7 +901,7 @@ impl<'tcx> CtxtInterners<'tcx> { Ty(Interned::new_unchecked( self.type_ .intern(kind, |kind| { - let flags = super::flags::FlagComputation::for_kind(&kind); + let flags = ty::FlagComputation::>::for_kind(&kind); let stable_hash = self.stable_hash(&flags, sess, untracked, &kind); InternedInSet(self.arena.alloc(WithCachedTypeInfo { @@ -884,7 +927,7 @@ impl<'tcx> CtxtInterners<'tcx> { Const(Interned::new_unchecked( self.const_ .intern(kind, |kind: ty::ConstKind<'_>| { - let flags = super::flags::FlagComputation::for_const_kind(&kind); + let flags = ty::FlagComputation::>::for_const_kind(&kind); let stable_hash = self.stable_hash(&flags, sess, untracked, &kind); InternedInSet(self.arena.alloc(WithCachedTypeInfo { @@ -900,7 +943,7 @@ impl<'tcx> CtxtInterners<'tcx> { fn stable_hash<'a, T: HashStable>>( &self, - flags: &ty::flags::FlagComputation, + flags: &ty::FlagComputation>, sess: &'a Session, untracked: &'a Untracked, val: &T, @@ -928,7 +971,7 @@ impl<'tcx> CtxtInterners<'tcx> { Predicate(Interned::new_unchecked( self.predicate .intern(kind, |kind| { - let flags = super::flags::FlagComputation::for_predicate(kind); + let flags = ty::FlagComputation::>::for_predicate(kind); let stable_hash = self.stable_hash(&flags, sess, untracked, &kind); @@ -949,7 +992,7 @@ impl<'tcx> CtxtInterners<'tcx> { } else { self.clauses .intern_ref(clauses, || { - let flags = super::flags::FlagComputation::for_clauses(clauses); + let flags = ty::FlagComputation::>::for_clauses(clauses); InternedInSet(ListWithCachedTypeInfo::from_arena( &*self.arena, @@ -1289,7 +1332,8 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> { let bodies = Default::default(); let attrs = hir::AttributeMap::EMPTY; - let (opt_hash_including_bodies, _) = self.tcx.hash_owner_nodes(node, &bodies, &attrs.map); + let (opt_hash_including_bodies, _) = + self.tcx.hash_owner_nodes(node, &bodies, &attrs.map, attrs.define_opaque); let node = node.into(); self.opt_hir_owner_nodes(Some(self.tcx.arena.alloc(hir::OwnerNodes { opt_hash_including_bodies, @@ -1299,7 +1343,7 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> { ), bodies, }))); - self.feed_owner_id().hir_attrs(attrs); + self.feed_owner_id().hir_attr_map(attrs); } } @@ -1327,6 +1371,11 @@ pub struct TyCtxt<'tcx> { gcx: &'tcx GlobalCtxt<'tcx>, } +// Explicitly implement `DynSync` and `DynSend` for `TyCtxt` to short circuit trait resolution. Its +// field are asserted to implement these traits below, so this is trivially safe, and it greatly +// speeds-up compilation of this crate and its dependents. +unsafe impl DynSend for TyCtxt<'_> {} +unsafe impl DynSync for TyCtxt<'_> {} fn _assert_tcx_fields() { sync::assert_dyn_sync::<&'_ GlobalCtxt<'_>>(); sync::assert_dyn_send::<&'_ GlobalCtxt<'_>>(); @@ -1402,6 +1451,9 @@ pub struct GlobalCtxt<'tcx> { pub(crate) alloc_map: interpret::AllocMap<'tcx>, current_gcx: CurrentGcx, + + /// A jobserver reference used to release then acquire a token while waiting on a query. + pub jobserver_proxy: Arc, } impl<'tcx> GlobalCtxt<'tcx> { @@ -1532,6 +1584,25 @@ impl<'tcx> TyCtxt<'tcx> { self.reserve_and_set_memory_dedup(alloc, salt) } + pub fn default_traits(self) -> &'static [rustc_hir::LangItem] { + match self.sess.opts.unstable_opts.experimental_default_bounds { + true => &[ + LangItem::Sized, + LangItem::DefaultTrait1, + LangItem::DefaultTrait2, + LangItem::DefaultTrait3, + LangItem::DefaultTrait4, + ], + false => &[LangItem::Sized], + } + } + + pub fn is_default_trait(self, def_id: DefId) -> bool { + self.default_traits() + .iter() + .any(|&default_trait| self.lang_items().get(default_trait) == Some(def_id)) + } + /// Returns a range of the start/end indices specified with the /// `rustc_layout_scalar_valid_range` attribute. // FIXME(eddyb) this is an awkward spot for this method, maybe move it? @@ -1587,6 +1658,7 @@ impl<'tcx> TyCtxt<'tcx> { query_system: QuerySystem<'tcx>, hooks: crate::hooks::Providers, current_gcx: CurrentGcx, + jobserver_proxy: Arc, f: impl FnOnce(TyCtxt<'tcx>) -> T, ) -> T { let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| { @@ -1621,6 +1693,7 @@ impl<'tcx> TyCtxt<'tcx> { data_layout, alloc_map: interpret::AllocMap::new(), current_gcx, + jobserver_proxy, }); // This is a separate function to work around a crash with parallel rustc (#135870) @@ -1659,6 +1732,10 @@ impl<'tcx> TyCtxt<'tcx> { self.coroutine_kind(def_id).is_some() } + pub fn is_async_drop_in_place_coroutine(self, def_id: DefId) -> bool { + self.is_lang_item(self.parent(def_id), LangItem::AsyncDropInPlace) + } + /// Returns the movability of the coroutine of `def_id`, or panics /// if given a `def_id` that is not a coroutine. pub fn coroutine_movability(self, def_id: DefId) -> hir::Movability { @@ -1752,9 +1829,10 @@ impl<'tcx> TyCtxt<'tcx> { self.crate_types() .iter() .map(|ty| match *ty { - CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => { - MetadataKind::None - } + CrateType::Executable + | CrateType::Staticlib + | CrateType::Cdylib + | CrateType::Sdylib => MetadataKind::None, CrateType::Rlib => MetadataKind::Uncompressed, CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed, }) @@ -1774,10 +1852,15 @@ impl<'tcx> TyCtxt<'tcx> { // - needs_metadata: for putting into crate metadata. // - instrument_coverage: for putting into coverage data (see // `hash_mir_source`). + // - metrics_dir: metrics use the strict version hash in the filenames + // for dumped metrics files to prevent overwriting distinct metrics + // for similar source builds (may change in the future, this is part + // of the proof of concept impl for the metrics initiative project goal) cfg!(debug_assertions) || self.sess.opts.incremental.is_some() || self.needs_metadata() || self.sess.instrument_coverage() + || self.sess.opts.unstable_opts.metrics_dir.is_some() } #[inline] @@ -1878,6 +1961,15 @@ impl<'tcx> TyCtxt<'tcx> { } None } + + /// Helper to get a tracked environment variable via. [`TyCtxt::env_var_os`] and converting to + /// UTF-8 like [`std::env::var`]. + pub fn env_var>(self, key: &'tcx K) -> Result<&'tcx str, VarError> { + match self.env_var_os(key.as_ref()) { + Some(value) => value.to_str().ok_or_else(|| VarError::NotUnicode(value.to_os_string())), + None => Err(VarError::NotPresent), + } + } } impl<'tcx> TyCtxtAt<'tcx> { @@ -1885,10 +1977,13 @@ impl<'tcx> TyCtxtAt<'tcx> { pub fn create_def( self, parent: LocalDefId, - name: Symbol, + name: Option, def_kind: DefKind, + override_def_path_data: Option, + disambiguator: &mut DisambiguatorState, ) -> TyCtxtFeed<'tcx, LocalDefId> { - let feed = self.tcx.create_def(parent, name, def_kind); + let feed = + self.tcx.create_def(parent, name, def_kind, override_def_path_data, disambiguator); feed.def_span(self.span); feed @@ -1900,10 +1995,12 @@ impl<'tcx> TyCtxt<'tcx> { pub fn create_def( self, parent: LocalDefId, - name: Symbol, + name: Option, def_kind: DefKind, + override_def_path_data: Option, + disambiguator: &mut DisambiguatorState, ) -> TyCtxtFeed<'tcx, LocalDefId> { - let data = def_kind.def_path_data(name); + let data = override_def_path_data.unwrap_or_else(|| def_kind.def_path_data(name)); // The following call has the side effect of modifying the tables inside `definitions`. // These very tables are relied on by the incr. comp. engine to decode DepNodes and to // decode the on-disk cache. @@ -1913,12 +2010,7 @@ impl<'tcx> TyCtxt<'tcx> { // - has been created by this call to `create_def`. // As a consequence, this LocalDefId is always re-created before it is needed by the incr. // comp. engine itself. - // - // This call also writes to the value of `source_span` and `expn_that_defined` queries. - // This is fine because: - // - those queries are `eval_always` so we won't miss their result changing; - // - this write will have happened before these queries are called. - let def_id = self.untracked.definitions.write().create_def(parent, data); + let def_id = self.untracked.definitions.write().create_def(parent, data, disambiguator); // This function modifies `self.definitions` using a side-effect. // We need to ensure that these side effects are re-run by the incr. comp. engine. @@ -2043,7 +2135,8 @@ impl<'tcx> TyCtxt<'tcx> { CrateType::Executable | CrateType::Staticlib | CrateType::ProcMacro - | CrateType::Cdylib => false, + | CrateType::Cdylib + | CrateType::Sdylib => false, // FIXME rust-lang/rust#64319, rust-lang/rust#64872: // We want to block export of generics from dylibs, @@ -2096,7 +2189,7 @@ impl<'tcx> TyCtxt<'tcx> { return vec![]; }; - let mut v = TraitObjectVisitor(vec![], self.hir()); + let mut v = TraitObjectVisitor(vec![]); v.visit_ty_unambig(hir_output); v.0 } @@ -2109,7 +2202,7 @@ impl<'tcx> TyCtxt<'tcx> { scope_def_id: LocalDefId, ) -> Option<(Vec<&'tcx hir::Ty<'tcx>>, Span, Option)> { let hir_id = self.local_def_id_to_hir_id(scope_def_id); - let mut v = TraitObjectVisitor(vec![], self.hir()); + let mut v = TraitObjectVisitor(vec![]); // when the return type is a type alias if let Some(hir::FnDecl { output: hir::FnRetTy::Return(hir_output), .. }) = self.hir_fn_decl_by_hir_id(hir_id) && let hir::TyKind::Path(hir::QPath::Resolved( @@ -2209,7 +2302,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns the origin of the opaque type `def_id`. #[instrument(skip(self), level = "trace", ret)] pub fn local_opaque_ty_origin(self, def_id: LocalDefId) -> hir::OpaqueTyOrigin { - self.hir().expect_opaque_ty(def_id).origin + self.hir_expect_opaque_ty(def_id).origin } pub fn finish(self) { @@ -2331,8 +2424,10 @@ macro_rules! sty_debug_print { $(let mut $variant = total;)* for shard in tcx.interners.type_.lock_shards() { - let types = shard.keys(); - for &InternedInSet(t) in types { + // It seems that ordering doesn't affect anything here. + #[allow(rustc::potential_query_instability)] + let types = shard.iter(); + for &(InternedInSet(t), ()) in types { let variant = match t.internee { ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Str | ty::Never => continue, @@ -2589,6 +2684,7 @@ slice_interners!( local_def_ids: intern_local_def_ids(LocalDefId), captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>), offset_of: pub mk_offset_of((VariantIdx, FieldIdx)), + patterns: pub mk_patterns(Pattern<'tcx>), ); impl<'tcx> TyCtxt<'tcx> { @@ -2855,11 +2951,19 @@ impl<'tcx> TyCtxt<'tcx> { self.interners.intern_clauses(clauses) } - pub fn mk_local_def_ids(self, clauses: &[LocalDefId]) -> &'tcx List { + pub fn mk_local_def_ids(self, def_ids: &[LocalDefId]) -> &'tcx List { // FIXME consider asking the input slice to be sorted to avoid // re-interning permutations, in which case that would be asserted // here. - self.intern_local_def_ids(clauses) + self.intern_local_def_ids(def_ids) + } + + pub fn mk_patterns_from_iter(self, iter: I) -> T::Output + where + I: Iterator, + T: CollectAndApply, &'tcx List>>, + { + T::collect_and_apply(iter, |xs| self.mk_patterns(xs)) } pub fn mk_local_def_ids_from_iter(self, iter: I) -> T::Output @@ -3006,8 +3110,8 @@ impl<'tcx> TyCtxt<'tcx> { span: impl Into, decorator: impl for<'a> LintDiagnostic<'a, ()>, ) { - let (level, src) = self.lint_level_at_node(lint, hir_id); - lint_level(self.sess, lint, level, src, Some(span.into()), |lint| { + let level = self.lint_level_at_node(lint, hir_id); + lint_level(self.sess, lint, level, Some(span.into()), |lint| { decorator.decorate_lint(lint); }) } @@ -3024,8 +3128,8 @@ impl<'tcx> TyCtxt<'tcx> { span: impl Into, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), ) { - let (level, src) = self.lint_level_at_node(lint, hir_id); - lint_level(self.sess, lint, level, src, Some(span.into()), decorate); + let level = self.lint_level_at_node(lint, hir_id); + lint_level(self.sess, lint, level, Some(span.into()), decorate); } /// Find the crate root and the appropriate span where `use` and outer attributes can be @@ -3092,8 +3196,8 @@ impl<'tcx> TyCtxt<'tcx> { id: HirId, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), ) { - let (level, src) = self.lint_level_at_node(lint, id); - lint_level(self.sess, lint, level, src, None, decorate); + let level = self.lint_level_at_node(lint, id); + lint_level(self.sess, lint, level, None, decorate); } pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate]> { @@ -3113,9 +3217,11 @@ impl<'tcx> TyCtxt<'tcx> { pub fn late_bound_vars(self, id: HirId) -> &'tcx List { self.mk_bound_variable_kinds( - &self.late_bound_vars_map(id.owner).get(&id.local_id).cloned().unwrap_or_else(|| { - bug!("No bound vars found for {}", self.hir().node_to_string(id)) - }), + &self + .late_bound_vars_map(id.owner) + .get(&id.local_id) + .cloned() + .unwrap_or_else(|| bug!("No bound vars found for {}", self.hir_id_to_string(id))), ) } @@ -3214,6 +3320,10 @@ impl<'tcx> TyCtxt<'tcx> { && self.impl_trait_header(def_id).unwrap().constness == hir::Constness::Const } + pub fn is_sdylib_interface_build(self) -> bool { + self.sess.opts.unstable_opts.build_sdylib_interface + } + pub fn intrinsic(self, def_id: impl IntoQueryParam + Copy) -> Option { match self.def_kind(def_id) { DefKind::Fn | DefKind::AssocFn => {} @@ -3230,6 +3340,11 @@ impl<'tcx> TyCtxt<'tcx> { self.sess.opts.unstable_opts.next_solver.coherence } + #[allow(rustc::bad_opt_access)] + pub fn use_typing_mode_borrowck(self) -> bool { + self.next_trait_solver_globally() || self.sess.opts.unstable_opts.typing_mode_borrowck + } + pub fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { self.opt_rpitit_info(def_id).is_some() } @@ -3301,9 +3416,7 @@ pub fn provide(providers: &mut Providers) { providers.maybe_unused_trait_imports = |tcx, ()| &tcx.resolutions(()).maybe_unused_trait_imports; providers.names_imported_by_glob_use = |tcx, id| { - tcx.arena.alloc(UnordSet::from( - tcx.resolutions(()).glob_map.get(&id).cloned().unwrap_or_default(), - )) + tcx.arena.alloc(tcx.resolutions(()).glob_map.get(&id).cloned().unwrap_or_default()) }; providers.extern_mod_stmt_cnum = diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index eaab8474dd20c..5fc80bc793673 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -1,8 +1,6 @@ use std::{mem, ptr}; -use rustc_data_structures::sync::{self, Lock}; -use rustc_errors::DiagInner; -use thin_vec::ThinVec; +use rustc_data_structures::sync; use super::{GlobalCtxt, TyCtxt}; use crate::dep_graph::TaskDepsRef; @@ -22,10 +20,6 @@ pub struct ImplicitCtxt<'a, 'tcx> { /// `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 queries from calling too deeply. pub query_depth: usize, @@ -37,13 +31,7 @@ pub struct ImplicitCtxt<'a, 'tcx> { impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> { pub fn new(gcx: &'tcx GlobalCtxt<'tcx>) -> Self { let tcx = TyCtxt { gcx }; - ImplicitCtxt { - tcx, - query: None, - diagnostics: None, - query_depth: 0, - task_deps: TaskDepsRef::Ignore, - } + ImplicitCtxt { tcx, query: None, query_depth: 0, task_deps: TaskDepsRef::Ignore } } } diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 881381a5ee61e..b122ada0925d6 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -3,7 +3,7 @@ use std::fmt::Write; use std::ops::ControlFlow; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{ Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display, listify, pluralize, }; @@ -142,8 +142,8 @@ pub fn suggest_arbitrary_trait_bound<'tcx>( if let Some((name, term)) = associated_ty { // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err. // That should be extracted into a helper function. - if constraint.ends_with('>') { - constraint = format!("{}, {} = {}>", &constraint[..constraint.len() - 1], name, term); + if let Some(stripped) = constraint.strip_suffix('>') { + constraint = format!("{stripped}, {name} = {term}>"); } else { constraint.push_str(&format!("<{name} = {term}>")); } @@ -287,7 +287,7 @@ pub fn suggest_constraining_type_params<'a>( param_names_and_constraints: impl Iterator)>, span_to_replace: Option, ) -> bool { - let mut grouped = FxHashMap::default(); + let mut grouped = FxIndexMap::default(); let mut unstable_suggestion = false; param_names_and_constraints.for_each(|(param_name, constraint, def_id)| { let stable = match def_id { @@ -571,15 +571,15 @@ pub fn suggest_constraining_type_params<'a>( } /// 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>); +pub(crate) struct TraitObjectVisitor<'tcx>(pub(crate) Vec<&'tcx hir::Ty<'tcx>>); impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { match ty.kind { hir::TyKind::TraitObject(_, tagged_ptr) if let hir::Lifetime { - res: - hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static, + kind: + hir::LifetimeKind::ImplicitObjectLifetimeDefault | hir::LifetimeKind::Static, .. } = tagged_ptr.pointer() => { @@ -592,18 +592,6 @@ impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { } } -/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for. -pub struct StaticLifetimeVisitor<'tcx>(pub Vec, pub crate::hir::map::Map<'tcx>); - -impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> { - fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) { - if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static = lt.res - { - self.0.push(lt.ident.span); - } - } -} - pub struct IsSuggestableVisitor<'tcx> { tcx: TyCtxt<'tcx>, infer_suggestable: bool, @@ -641,21 +629,19 @@ impl<'tcx> TypeVisitor> for IsSuggestableVisitor<'tcx> { } } - Alias(Projection, AliasTy { def_id, .. }) => { - if self.tcx.def_kind(def_id) != DefKind::AssocTy { - return ControlFlow::Break(()); - } + Alias(Projection, AliasTy { def_id, .. }) + if self.tcx.def_kind(def_id) != DefKind::AssocTy => + { + return ControlFlow::Break(()); } - Param(param) => { - // FIXME: It would be nice to make this not use string manipulation, - // but it's pretty hard to do this, since `ty::ParamTy` is missing - // sufficient info to determine if it is synthetic, and we don't - // always have a convenient way of getting `ty::Generics` at the call - // sites we invoke `IsSuggestable::is_suggestable`. - if param.name.as_str().starts_with("impl ") { - return ControlFlow::Break(()); - } + // FIXME: It would be nice to make this not use string manipulation, + // but it's pretty hard to do this, since `ty::ParamTy` is missing + // sufficient info to determine if it is synthetic, and we don't + // always have a convenient way of getting `ty::Generics` at the call + // sites we invoke `IsSuggestable::is_suggestable`. + Param(param) if param.name.as_str().starts_with("impl ") => { + return ControlFlow::Break(()); } _ => {} @@ -733,17 +719,13 @@ impl<'tcx> FallibleTypeFolder> for MakeSuggestableFolder<'tcx> { } } - Param(param) => { - // FIXME: It would be nice to make this not use string manipulation, - // but it's pretty hard to do this, since `ty::ParamTy` is missing - // sufficient info to determine if it is synthetic, and we don't - // always have a convenient way of getting `ty::Generics` at the call - // sites we invoke `IsSuggestable::is_suggestable`. - if param.name.as_str().starts_with("impl ") { - return Err(()); - } - - t + // FIXME: It would be nice to make this not use string manipulation, + // but it's pretty hard to do this, since `ty::ParamTy` is missing + // sufficient info to determine if it is synthetic, and we don't + // always have a convenient way of getting `ty::Generics` at the call + // sites we invoke `IsSuggestable::is_suggestable`. + Param(param) if param.name.as_str().starts_with("impl ") => { + return Err(()); } _ => t, diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs index ecca1d4490769..45a0b1288db87 100644 --- a/compiler/rustc_middle/src/ty/erase_regions.rs +++ b/compiler/rustc_middle/src/ty/erase_regions.rs @@ -1,8 +1,9 @@ use tracing::debug; use crate::query::Providers; -use crate::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; -use crate::ty::{self, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; +use crate::ty::{ + self, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, +}; pub(super) fn provide(providers: &mut Providers) { *providers = Providers { erase_regions_ty, ..*providers }; @@ -43,7 +44,13 @@ impl<'tcx> TypeFolder> for RegionEraserVisitor<'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_infer() { ty.super_fold_with(self) } else { self.tcx.erase_regions_ty(ty) } + if !ty.has_type_flags(TypeFlags::HAS_BINDER_VARS | TypeFlags::HAS_FREE_REGIONS) { + ty + } else if ty.has_infer() { + ty.super_fold_with(self) + } else { + self.tcx.erase_regions_ty(ty) + } } fn fold_binder(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> @@ -58,9 +65,25 @@ impl<'tcx> TypeFolder> for RegionEraserVisitor<'tcx> { // We must not erase bound regions. `for<'a> fn(&'a ())` and // `fn(&'free ())` are different types: they may implement different // traits and have a different `TypeId`. - match *r { + match r.kind() { ty::ReBound(..) => r, _ => self.tcx.lifetimes.re_erased, } } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if ct.has_type_flags(TypeFlags::HAS_BINDER_VARS | TypeFlags::HAS_FREE_REGIONS) { + ct.super_fold_with(self) + } else { + ct + } + } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.has_type_flags(TypeFlags::HAS_BINDER_VARS | TypeFlags::HAS_FREE_REGIONS) { + p.super_fold_with(self) + } else { + p + } + } } diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index a0e67929c5289..13723874ad3a1 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -205,7 +205,7 @@ impl<'tcx> Ty<'tcx> { ty::Placeholder(..) => "higher-ranked type".into(), ty::Bound(..) => "bound type variable".into(), ty::Alias(ty::Projection | ty::Inherent, _) => "associated type".into(), - ty::Alias(ty::Weak, _) => "type alias".into(), + ty::Alias(ty::Free, _) => "type alias".into(), ty::Param(_) => "type parameter".into(), ty::Alias(ty::Opaque, ..) => "opaque type".into(), } @@ -279,7 +279,7 @@ impl<'tcx> TyCtxt<'tcx> { p.hash(&mut s); let hash = s.finish(); *path = Some(path.take().unwrap_or_else(|| { - self.output_filenames(()).temp_path_ext(&format!("long-type-{hash}.txt"), None) + self.output_filenames(()).temp_path_for_diagnostic(&format!("long-type-{hash}.txt")) })); let Ok(mut file) = File::options().create(true).read(true).append(true).open(&path.as_ref().unwrap()) diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs deleted file mode 100644 index ec0498b168c01..0000000000000 --- a/compiler/rustc_middle/src/ty/flags.rs +++ /dev/null @@ -1,422 +0,0 @@ -use std::slice; - -use crate::ty::{self, GenericArg, GenericArgKind, InferConst, Ty, TypeFlags}; - -#[derive(Debug)] -pub struct FlagComputation { - pub flags: TypeFlags, - - /// see `Ty::outer_exclusive_binder` for details - pub outer_exclusive_binder: ty::DebruijnIndex, -} - -impl FlagComputation { - fn new() -> FlagComputation { - FlagComputation { flags: TypeFlags::empty(), outer_exclusive_binder: ty::INNERMOST } - } - - #[allow(rustc::usage_of_ty_tykind)] - pub fn for_kind(kind: &ty::TyKind<'_>) -> FlagComputation { - let mut result = FlagComputation::new(); - result.add_kind(kind); - result - } - - pub fn for_predicate(binder: ty::Binder<'_, ty::PredicateKind<'_>>) -> FlagComputation { - let mut result = FlagComputation::new(); - result.add_predicate(binder); - result - } - - pub fn for_const_kind(kind: &ty::ConstKind<'_>) -> FlagComputation { - let mut result = FlagComputation::new(); - result.add_const_kind(kind); - result - } - - pub fn for_clauses(clauses: &[ty::Clause<'_>]) -> FlagComputation { - let mut result = FlagComputation::new(); - for c in clauses { - result.add_flags(c.as_predicate().flags()); - result.add_exclusive_binder(c.as_predicate().outer_exclusive_binder()); - } - result - } - - fn add_flags(&mut self, flags: TypeFlags) { - self.flags = self.flags | flags; - } - - /// indicates that `self` refers to something at binding level `binder` - fn add_bound_var(&mut self, binder: ty::DebruijnIndex) { - let exclusive_binder = binder.shifted_in(1); - self.add_exclusive_binder(exclusive_binder); - } - - /// indicates that `self` refers to something *inside* binding - /// level `binder` -- not bound by `binder`, but bound by the next - /// binder internal to it - fn add_exclusive_binder(&mut self, exclusive_binder: ty::DebruijnIndex) { - self.outer_exclusive_binder = self.outer_exclusive_binder.max(exclusive_binder); - } - - /// Adds the flags/depth from a set of types that appear within the current type, but within a - /// region binder. - fn bound_computation(&mut self, value: ty::Binder<'_, T>, f: F) - where - F: FnOnce(&mut Self, T), - { - let mut computation = FlagComputation::new(); - - if !value.bound_vars().is_empty() { - computation.add_flags(TypeFlags::HAS_BINDER_VARS); - } - - f(&mut computation, value.skip_binder()); - - self.add_flags(computation.flags); - - // The types that contributed to `computation` occurred within - // a region binder, so subtract one from the region depth - // within when adding the depth to `self`. - let outer_exclusive_binder = computation.outer_exclusive_binder; - if outer_exclusive_binder > ty::INNERMOST { - self.add_exclusive_binder(outer_exclusive_binder.shifted_out(1)); - } // otherwise, this binder captures nothing - } - - #[allow(rustc::usage_of_ty_tykind)] - fn add_kind(&mut self, kind: &ty::TyKind<'_>) { - match kind { - &ty::Bool - | &ty::Char - | &ty::Int(_) - | &ty::Float(_) - | &ty::Uint(_) - | &ty::Never - | &ty::Str - | &ty::Foreign(..) => {} - - &ty::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), - - &ty::Param(_) => { - self.add_flags(TypeFlags::HAS_TY_PARAM); - self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); - } - - ty::Coroutine(_, args) => { - let args = args.as_coroutine(); - let should_remove_further_specializable = - !self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE); - self.add_args(args.parent_args()); - if should_remove_further_specializable { - self.flags -= TypeFlags::STILL_FURTHER_SPECIALIZABLE; - } - - self.add_ty(args.resume_ty()); - self.add_ty(args.return_ty()); - self.add_ty(args.witness()); - self.add_ty(args.yield_ty()); - self.add_ty(args.tupled_upvars_ty()); - } - - ty::CoroutineWitness(_, args) => { - let should_remove_further_specializable = - !self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE); - self.add_args(args); - if should_remove_further_specializable { - self.flags -= TypeFlags::STILL_FURTHER_SPECIALIZABLE; - } - self.add_flags(TypeFlags::HAS_TY_COROUTINE); - } - - &ty::Closure(_, args) => { - let args = args.as_closure(); - let should_remove_further_specializable = - !self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE); - self.add_args(args.parent_args()); - if should_remove_further_specializable { - self.flags -= TypeFlags::STILL_FURTHER_SPECIALIZABLE; - } - - self.add_ty(args.sig_as_fn_ptr_ty()); - self.add_ty(args.kind_ty()); - self.add_ty(args.tupled_upvars_ty()); - } - - &ty::CoroutineClosure(_, args) => { - let args = args.as_coroutine_closure(); - let should_remove_further_specializable = - !self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE); - self.add_args(args.parent_args()); - if should_remove_further_specializable { - self.flags -= TypeFlags::STILL_FURTHER_SPECIALIZABLE; - } - - self.add_ty(args.kind_ty()); - self.add_ty(args.signature_parts_ty()); - self.add_ty(args.tupled_upvars_ty()); - self.add_ty(args.coroutine_captures_by_ref_ty()); - self.add_ty(args.coroutine_witness_ty()); - } - - &ty::Bound(debruijn, _) => { - self.add_bound_var(debruijn); - self.add_flags(TypeFlags::HAS_TY_BOUND); - } - - &ty::Placeholder(..) => { - self.add_flags(TypeFlags::HAS_TY_PLACEHOLDER); - self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); - } - - &ty::Infer(infer) => { - self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); - match infer { - ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => { - self.add_flags(TypeFlags::HAS_TY_FRESH) - } - - ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_) => { - self.add_flags(TypeFlags::HAS_TY_INFER) - } - } - } - - &ty::Adt(_, args) => { - self.add_args(args); - } - - &ty::Alias(kind, data) => { - self.add_flags(match kind { - ty::Projection => TypeFlags::HAS_TY_PROJECTION, - ty::Weak => TypeFlags::HAS_TY_WEAK, - ty::Opaque => TypeFlags::HAS_TY_OPAQUE, - ty::Inherent => TypeFlags::HAS_TY_INHERENT, - }); - - self.add_alias_ty(data); - } - - &ty::Dynamic(obj, r, _) => { - for predicate in obj.iter() { - self.bound_computation(predicate, |computation, predicate| match predicate { - ty::ExistentialPredicate::Trait(tr) => computation.add_args(tr.args), - ty::ExistentialPredicate::Projection(p) => { - computation.add_existential_projection(&p); - } - ty::ExistentialPredicate::AutoTrait(_) => {} - }); - } - - self.add_region(r); - } - - &ty::Array(tt, len) => { - self.add_ty(tt); - self.add_const(len); - } - - &ty::Pat(ty, pat) => { - self.add_ty(ty); - match *pat { - ty::PatternKind::Range { start, end, include_end: _ } => { - if let Some(start) = start { - self.add_const(start) - } - if let Some(end) = end { - self.add_const(end) - } - } - } - } - - &ty::Slice(tt) => self.add_ty(tt), - - &ty::RawPtr(ty, _) => { - self.add_ty(ty); - } - - &ty::Ref(r, ty, _) => { - self.add_region(r); - self.add_ty(ty); - } - - &ty::Tuple(types) => { - self.add_tys(types); - } - - &ty::FnDef(_, args) => { - self.add_args(args); - } - - &ty::FnPtr(sig_tys, _) => self.bound_computation(sig_tys, |computation, sig_tys| { - computation.add_tys(sig_tys.inputs_and_output); - }), - - &ty::UnsafeBinder(bound_ty) => { - self.bound_computation(bound_ty.into(), |computation, ty| { - computation.add_ty(ty); - }) - } - } - } - - fn add_predicate(&mut self, binder: ty::Binder<'_, ty::PredicateKind<'_>>) { - self.bound_computation(binder, |computation, atom| computation.add_predicate_atom(atom)); - } - - fn add_predicate_atom(&mut self, atom: ty::PredicateKind<'_>) { - match atom { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => { - self.add_args(trait_pred.trait_ref.args); - } - ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(ty::HostEffectPredicate { - trait_ref, - constness: _, - })) => { - self.add_args(trait_ref.args); - } - ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( - a, - b, - ))) => { - self.add_region(a); - self.add_region(b); - } - ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( - ty, - region, - ))) => { - self.add_ty(ty); - self.add_region(region); - } - ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { - self.add_const(ct); - self.add_ty(ty); - } - ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => { - self.add_ty(a); - self.add_ty(b); - } - ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => { - self.add_ty(a); - self.add_ty(b); - } - ty::PredicateKind::Clause(ty::ClauseKind::Projection(ty::ProjectionPredicate { - projection_term, - term, - })) => { - self.add_alias_term(projection_term); - self.add_term(term); - } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - self.add_args(slice::from_ref(&arg)); - } - ty::PredicateKind::DynCompatible(_def_id) => {} - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { - self.add_const(uv); - } - ty::PredicateKind::ConstEquate(expected, found) => { - self.add_const(expected); - self.add_const(found); - } - ty::PredicateKind::Ambiguous => {} - ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) => { - self.add_alias_term(alias); - self.add_term(term); - } - ty::PredicateKind::AliasRelate(t1, t2, _) => { - self.add_term(t1); - self.add_term(t2); - } - } - } - - fn add_ty(&mut self, ty: Ty<'_>) { - self.add_flags(ty.flags()); - self.add_exclusive_binder(ty.outer_exclusive_binder()); - } - - fn add_tys(&mut self, tys: &[Ty<'_>]) { - for &ty in tys { - self.add_ty(ty); - } - } - - fn add_region(&mut self, r: ty::Region<'_>) { - self.add_flags(r.type_flags()); - if let ty::ReBound(debruijn, _) = *r { - self.add_bound_var(debruijn); - } - } - - fn add_const(&mut self, c: ty::Const<'_>) { - self.add_flags(c.flags()); - self.add_exclusive_binder(c.outer_exclusive_binder()); - } - - fn add_const_kind(&mut self, c: &ty::ConstKind<'_>) { - match *c { - ty::ConstKind::Unevaluated(uv) => { - self.add_args(uv.args); - self.add_flags(TypeFlags::HAS_CT_PROJECTION); - } - ty::ConstKind::Infer(infer) => { - self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); - match infer { - InferConst::Fresh(_) => self.add_flags(TypeFlags::HAS_CT_FRESH), - InferConst::Var(_) => self.add_flags(TypeFlags::HAS_CT_INFER), - } - } - ty::ConstKind::Bound(debruijn, _) => { - self.add_bound_var(debruijn); - self.add_flags(TypeFlags::HAS_CT_BOUND); - } - ty::ConstKind::Param(_) => { - self.add_flags(TypeFlags::HAS_CT_PARAM); - self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); - } - ty::ConstKind::Placeholder(_) => { - self.add_flags(TypeFlags::HAS_CT_PLACEHOLDER); - self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); - } - ty::ConstKind::Value(cv) => self.add_ty(cv.ty), - ty::ConstKind::Expr(e) => self.add_args(e.args()), - ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), - } - } - - fn add_existential_projection(&mut self, projection: &ty::ExistentialProjection<'_>) { - self.add_args(projection.args); - match projection.term.unpack() { - ty::TermKind::Ty(ty) => self.add_ty(ty), - ty::TermKind::Const(ct) => self.add_const(ct), - } - } - - fn add_alias_ty(&mut self, alias_ty: ty::AliasTy<'_>) { - self.add_args(alias_ty.args); - } - - fn add_alias_term(&mut self, alias_term: ty::AliasTerm<'_>) { - self.add_args(alias_term.args); - } - - fn add_args(&mut self, args: &[GenericArg<'_>]) { - for kind in args { - match kind.unpack() { - GenericArgKind::Type(ty) => self.add_ty(ty), - GenericArgKind::Lifetime(lt) => self.add_region(lt), - GenericArgKind::Const(ct) => self.add_const(ct), - } - } - } - - fn add_term(&mut self, term: ty::Term<'_>) { - match term.unpack() { - ty::TermKind::Ty(ty) => self.add_ty(ty), - ty::TermKind::Const(ct) => self.add_const(ct), - } - } -} diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 4ea4050ed8b1d..8d6871d2f1fee 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -1,12 +1,11 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::DefId; use rustc_type_ir::data_structures::DelayedMap; -pub use rustc_type_ir::fold::{ - FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable, fold_regions, shift_region, - shift_vars, -}; -use crate::ty::{self, Binder, BoundTy, Ty, TyCtxt, TypeVisitableExt}; +use crate::ty::{ + self, Binder, BoundTy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, +}; /////////////////////////////////////////////////////////////////////////// // Some sample folders @@ -129,7 +128,7 @@ where ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => { let ty = self.delegate.replace_ty(bound_ty); debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST)); - ty::fold::shift_vars(self.tcx, ty, self.current_index.as_u32()) + ty::shift_vars(self.tcx, ty, self.current_index.as_u32()) } _ => { if !t.has_vars_bound_at_or_above(self.current_index) { @@ -146,10 +145,10 @@ where } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { + match r.kind() { ty::ReBound(debruijn, br) if debruijn == self.current_index => { let region = self.delegate.replace_region(br); - if let ty::ReBound(debruijn1, br) = *region { + if let ty::ReBound(debruijn1, br) = region.kind() { // If the callback returns a bound region, // that region should always use the INNERMOST // debruijn index. Then we adjust it to the @@ -169,7 +168,7 @@ where ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => { let ct = self.delegate.replace_const(bound_const); debug_assert!(!ct.has_vars_bound_above(ty::INNERMOST)); - ty::fold::shift_vars(self.tcx, ct, self.current_index.as_u32()) + ty::shift_vars(self.tcx, ct, self.current_index.as_u32()) } _ => ct.super_fold_with(self), } @@ -279,7 +278,7 @@ impl<'tcx> TyCtxt<'tcx> { where T: TypeFoldable>, { - let shift_bv = |bv: ty::BoundVar| ty::BoundVar::from_usize(bv.as_usize() + bound_vars); + let shift_bv = |bv: ty::BoundVar| bv + bound_vars; self.replace_escaping_bound_vars_uncached( value, FnMutDelegate { diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 27576a2ec4a28..542c0b3e6ebb4 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -2,23 +2,23 @@ use core::intrinsics; use std::marker::PhantomData; -use std::mem; use std::num::NonZero; use std::ptr::NonNull; use rustc_data_structures::intern::Interned; use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_hir::def_id::DefId; -use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, extension}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension}; use rustc_serialize::{Decodable, Encodable}; use rustc_type_ir::WithCachedTypeInfo; +use rustc_type_ir::walk::TypeWalker; use smallvec::SmallVec; use crate::ty::codec::{TyDecoder, TyEncoder}; -use crate::ty::fold::{FallibleTypeFolder, TypeFoldable}; -use crate::ty::visit::{TypeVisitable, TypeVisitor, VisitorResult, walk_visitable_list}; use crate::ty::{ - self, ClosureArgs, CoroutineArgs, CoroutineClosureArgs, InlineConstArgs, Lift, List, Ty, TyCtxt, + self, ClosureArgs, CoroutineArgs, CoroutineClosureArgs, FallibleTypeFolder, InlineConstArgs, + Lift, List, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, VisitorResult, + walk_visitable_list, }; pub type GenericArgKind<'tcx> = rustc_type_ir::GenericArgKind>; @@ -176,17 +176,17 @@ impl<'tcx> GenericArgKind<'tcx> { let (tag, ptr) = match self { GenericArgKind::Lifetime(lt) => { // Ensure we can use the tag bits. - assert_eq!(mem::align_of_val(&*lt.0.0) & TAG_MASK, 0); + assert_eq!(align_of_val(&*lt.0.0) & TAG_MASK, 0); (REGION_TAG, NonNull::from(lt.0.0).cast()) } GenericArgKind::Type(ty) => { // Ensure we can use the tag bits. - assert_eq!(mem::align_of_val(&*ty.0.0) & TAG_MASK, 0); + assert_eq!(align_of_val(&*ty.0.0) & TAG_MASK, 0); (TYPE_TAG, NonNull::from(ty.0.0).cast()) } GenericArgKind::Const(ct) => { // Ensure we can use the tag bits. - assert_eq!(mem::align_of_val(&*ct.0.0) & TAG_MASK, 0); + assert_eq!(align_of_val(&*ct.0.0) & TAG_MASK, 0); (CONST_TAG, NonNull::from(ct.0.0).cast()) } }; @@ -250,17 +250,17 @@ impl<'tcx> GenericArg<'tcx> { } #[inline] - pub fn as_type(self) -> Option> { + pub fn as_region(self) -> Option> { match self.unpack() { - GenericArgKind::Type(ty) => Some(ty), + GenericArgKind::Lifetime(re) => Some(re), _ => None, } } #[inline] - pub fn as_region(self) -> Option> { + pub fn as_type(self) -> Option> { match self.unpack() { - GenericArgKind::Lifetime(re) => Some(re), + GenericArgKind::Type(ty) => Some(ty), _ => None, } } @@ -273,6 +273,15 @@ impl<'tcx> GenericArg<'tcx> { } } + #[inline] + pub fn as_term(self) -> Option> { + match self.unpack() { + GenericArgKind::Lifetime(_) => None, + GenericArgKind::Type(ty) => Some(ty.into()), + GenericArgKind::Const(ct) => Some(ct.into()), + } + } + /// Unpack the `GenericArg` as a region when it is known certainly to be a region. pub fn expect_region(self) -> ty::Region<'tcx> { self.as_region().unwrap_or_else(|| bug!("expected a region, but found another kind")) @@ -298,6 +307,20 @@ impl<'tcx> GenericArg<'tcx> { GenericArgKind::Const(ct) => ct.is_ct_infer(), } } + + /// Iterator that walks `self` and any types reachable from + /// `self`, in depth-first order. Note that just walks the types + /// that appear in `self`, it does not descend into the fields of + /// structs or variants. For example: + /// + /// ```text + /// isize => { isize } + /// Foo> => { Foo>, Bar, isize } + /// [isize] => { [isize], isize } + /// ``` + pub fn walk(self) -> TypeWalker> { + TypeWalker::new(self) + } } impl<'a, 'tcx> Lift> for GenericArg<'a> { @@ -323,6 +346,14 @@ impl<'tcx> TypeFoldable> for GenericArg<'tcx> { GenericArgKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), } } + + fn fold_with>>(self, folder: &mut F) -> Self { + match self.unpack() { + GenericArgKind::Lifetime(lt) => lt.fold_with(folder).into(), + GenericArgKind::Type(ty) => ty.fold_with(folder).into(), + GenericArgKind::Const(ct) => ct.fold_with(folder).into(), + } + } } impl<'tcx> TypeVisitable> for GenericArg<'tcx> { @@ -335,13 +366,13 @@ impl<'tcx> TypeVisitable> for GenericArg<'tcx> { } } -impl<'tcx, E: TyEncoder>> Encodable for GenericArg<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for GenericArg<'tcx> { fn encode(&self, e: &mut E) { self.unpack().encode(e) } } -impl<'tcx, D: TyDecoder>> Decodable for GenericArg<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for GenericArg<'tcx> { fn decode(d: &mut D) -> GenericArg<'tcx> { GenericArgKind::decode(d).pack() } @@ -397,14 +428,14 @@ impl<'tcx> GenericArgs<'tcx> { InlineConstArgs { args: self } } - /// Creates an `GenericArgs` that maps each generic parameter to itself. + /// Creates a [`GenericArgs`] that maps each generic parameter to itself. pub fn identity_for_item(tcx: TyCtxt<'tcx>, def_id: impl Into) -> GenericArgsRef<'tcx> { Self::for_item(tcx, def_id.into(), |param, _| tcx.mk_param_from_def(param)) } - /// Creates an `GenericArgs` for generic parameter definitions, + /// Creates a [`GenericArgs`] for generic parameter definitions, /// by calling closures to obtain each kind. - /// The closures get to observe the `GenericArgs` as they're + /// The closures get to observe the [`GenericArgs`] as they're /// being built, which can be used to correctly /// replace defaults of generic parameters. pub fn for_item(tcx: TyCtxt<'tcx>, def_id: DefId, mut mk_kind: F) -> GenericArgsRef<'tcx> @@ -592,6 +623,27 @@ impl<'tcx> TypeFoldable> for GenericArgsRef<'tcx> { } } 0 => Ok(self), + _ => ty::util::try_fold_list(self, folder, |tcx, v| tcx.mk_args(v)), + } + } + + fn fold_with>>(self, folder: &mut F) -> Self { + // See justification for this behavior in `try_fold_with`. + match self.len() { + 1 => { + let param0 = self[0].fold_with(folder); + if param0 == self[0] { self } else { folder.cx().mk_args(&[param0]) } + } + 2 => { + let param0 = self[0].fold_with(folder); + let param1 = self[1].fold_with(folder); + if param0 == self[0] && param1 == self[1] { + self + } else { + folder.cx().mk_args(&[param0, param1]) + } + } + 0 => self, _ => ty::util::fold_list(self, folder, |tcx, v| tcx.mk_args(v)), } } @@ -627,6 +679,22 @@ impl<'tcx> TypeFoldable> for &'tcx ty::List> { Ok(folder.cx().mk_type_list(&[param0, param1])) } } + _ => ty::util::try_fold_list(self, folder, |tcx, v| tcx.mk_type_list(v)), + } + } + + fn fold_with>>(self, folder: &mut F) -> Self { + // See comment justifying behavior in `try_fold_with`. + match self.len() { + 2 => { + let param0 = self[0].fold_with(folder); + let param1 = self[1].fold_with(folder); + if param0 == self[0] && param1 == self[1] { + self + } else { + folder.cx().mk_type_list(&[param0, param1]) + } + } _ => ty::util::fold_list(self, folder, |tcx, v| tcx.mk_type_list(v)), } } diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 85d9db7ee7473..d4cc562e70cc7 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -73,9 +73,7 @@ impl GenericParamDef { pub fn is_anonymous_lifetime(&self) -> bool { match self.kind { - GenericParamDefKind::Lifetime => { - self.name == kw::UnderscoreLifetime || self.name == kw::Empty - } + GenericParamDefKind::Lifetime => self.name == kw::UnderscoreLifetime, _ => false, } } diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs index 505c7278176f3..953ad62be0a86 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs @@ -236,6 +236,11 @@ impl<'tcx> InhabitedPredicate<'tcx> { self.instantiate_opt(tcx, args).unwrap_or(self) } + /// Same as [`Self::instantiate`], but if there is no generics to + /// instantiate, returns `None`. This is useful because it lets us avoid + /// allocating a recursive copy of everything when the result is unchanged. + /// + /// Only used to implement `instantiate` itself. fn instantiate_opt(self, tcx: TyCtxt<'tcx>, args: ty::GenericArgsRef<'tcx>) -> Option { match self { Self::ConstIsZero(c) => { @@ -260,7 +265,10 @@ impl<'tcx> InhabitedPredicate<'tcx> { Some(InhabitedPredicate::True) => Some(InhabitedPredicate::True), Some(a) => Some(a.or(tcx, b.instantiate_opt(tcx, args).unwrap_or(b))), }, - _ => None, + Self::True | Self::False | Self::NotInModule(_) => None, + Self::OpaqueType(_) => { + bug!("unexpected OpaqueType in InhabitedPredicate"); + } } } } diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 4a5f6d80f24c4..d8bab58545fcc 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -43,6 +43,7 @@ //! This code should only compile in modules where the uninhabitedness of `Foo` //! is visible. +use rustc_span::sym; use rustc_type_ir::TyKind::*; use tracing::instrument; @@ -84,6 +85,21 @@ impl<'tcx> VariantDef { InhabitedPredicate::all( tcx, self.fields.iter().map(|field| { + // Unstable fields are always considered to be inhabited. In the future, + // this could be extended to be conditional on the field being unstable + // only within the module that's querying the inhabitedness, like: + // `let pred = pred.or(InhabitedPredicate::IsUnstable(field.did));` + // but this is unnecessary for now, since it would only affect nightly-only + // code or code within the standard library itself. + // HACK: We filter out `rustc_private` fields since with the flag + // `-Zforce-unstable-if-unmarked` we consider all unmarked fields to be + // unstable when building the compiler. + if tcx + .lookup_stability(field.did) + .is_some_and(|stab| stab.is_unstable() && stab.feature != sym::rustc_private) + { + return InhabitedPredicate::True; + } let pred = tcx.type_of(field.did).instantiate_identity().inhabited_predicate(tcx); if adt.is_enum() { return pred; @@ -107,11 +123,11 @@ impl<'tcx> Ty<'tcx> { // For now, unions are always considered inhabited Adt(adt, _) if adt.is_union() => InhabitedPredicate::True, // Non-exhaustive ADTs from other crates are always considered inhabited - Adt(adt, _) if adt.is_variant_list_non_exhaustive() && !adt.did().is_local() => { + Adt(adt, _) if adt.variant_list_has_applicable_non_exhaustive() => { InhabitedPredicate::True } Never => InhabitedPredicate::False, - Param(_) | Alias(ty::Projection | ty::Weak, _) => InhabitedPredicate::GenericType(self), + Param(_) | Alias(ty::Projection | ty::Free, _) => InhabitedPredicate::GenericType(self), Alias(ty::Opaque, alias_ty) => { match alias_ty.def_id.as_local() { // Foreign opaque is considered inhabited. diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 98ca71b86be07..0d99a1b51499a 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -71,7 +71,7 @@ pub enum InstanceKind<'tcx> { /// - coroutines Item(DefId), - /// An intrinsic `fn` item (with `"rust-intrinsic"` ABI). + /// An intrinsic `fn` item (with`#[rustc_instrinsic]`). /// /// Alongside `Virtual`, this is the only `InstanceKind` that does not have its own callable MIR. /// Instead, codegen and const eval "magically" evaluate calls to intrinsics purely in the @@ -147,6 +147,9 @@ pub enum InstanceKind<'tcx> { /// native support. ThreadLocalShim(DefId), + /// Proxy shim for async drop of future (def_id, proxy_cor_ty, impl_cor_ty) + FutureDropPollShim(DefId, Ty<'tcx>, Ty<'tcx>), + /// `core::ptr::drop_in_place::`. /// /// The `DefId` is for `core::ptr::drop_in_place`. @@ -173,7 +176,13 @@ pub enum InstanceKind<'tcx> { /// /// The `DefId` is for `core::future::async_drop::async_drop_in_place`, the `Ty` /// is the type `T`. - AsyncDropGlueCtorShim(DefId, Option>), + AsyncDropGlueCtorShim(DefId, Ty<'tcx>), + + /// `core::future::async_drop::async_drop_in_place::<'_, T>::{closure}`. + /// + /// async_drop_in_place poll function implementation (for generated coroutine). + /// `Ty` here is `async_drop_in_place::{closure}` coroutine type, not just `T` + AsyncDropGlue(DefId, Ty<'tcx>), } impl<'tcx> Instance<'tcx> { @@ -203,7 +212,7 @@ impl<'tcx> Instance<'tcx> { if !tcx.sess.opts.share_generics() // However, if the def_id is marked inline(never), then it's fine to just reuse the // upstream monomorphization. - && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_attr_parsing::InlineAttr::Never + && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_attr_data_structures::InlineAttr::Never { return None; } @@ -221,7 +230,9 @@ impl<'tcx> Instance<'tcx> { .upstream_monomorphizations_for(def) .and_then(|monos| monos.get(&self.args).cloned()), InstanceKind::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args), - InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => { + InstanceKind::AsyncDropGlue(_, _) => None, + InstanceKind::FutureDropPollShim(_, _, _) => None, + InstanceKind::AsyncDropGlueCtorShim(_, _) => { tcx.upstream_async_drop_glue_for(self.args) } _ => None, @@ -248,6 +259,8 @@ impl<'tcx> InstanceKind<'tcx> { | InstanceKind::DropGlue(def_id, _) | InstanceKind::CloneShim(def_id, _) | InstanceKind::FnPtrAddrShim(def_id, _) + | InstanceKind::FutureDropPollShim(def_id, _, _) + | InstanceKind::AsyncDropGlue(def_id, _) | InstanceKind::AsyncDropGlueCtorShim(def_id, _) => def_id, } } @@ -257,7 +270,9 @@ impl<'tcx> InstanceKind<'tcx> { match self { ty::InstanceKind::Item(def) => Some(def), ty::InstanceKind::DropGlue(def_id, Some(_)) - | InstanceKind::AsyncDropGlueCtorShim(def_id, Some(_)) + | InstanceKind::AsyncDropGlueCtorShim(def_id, _) + | InstanceKind::AsyncDropGlue(def_id, _) + | InstanceKind::FutureDropPollShim(def_id, ..) | InstanceKind::ThreadLocalShim(def_id) => Some(def_id), InstanceKind::VTableShim(..) | InstanceKind::ReifyShim(..) @@ -267,7 +282,6 @@ impl<'tcx> InstanceKind<'tcx> { | InstanceKind::ClosureOnceShim { .. } | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } | InstanceKind::DropGlue(..) - | InstanceKind::AsyncDropGlueCtorShim(..) | InstanceKind::CloneShim(..) | InstanceKind::FnPtrAddrShim(..) => None, } @@ -292,7 +306,9 @@ impl<'tcx> InstanceKind<'tcx> { let def_id = match *self { ty::InstanceKind::Item(def) => def, ty::InstanceKind::DropGlue(_, Some(_)) => return false, - ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => return false, + ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => return ty.is_coroutine(), + ty::InstanceKind::FutureDropPollShim(_, _, _) => return false, + ty::InstanceKind::AsyncDropGlue(_, _) => return false, ty::InstanceKind::ThreadLocalShim(_) => return false, _ => return true, }; @@ -302,50 +318,6 @@ impl<'tcx> InstanceKind<'tcx> { ) } - /// 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::InstanceKind::DropGlue(.., Some(ty)) - | ty::InstanceKind::AsyncDropGlueCtorShim(.., 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().is_none_or(|adt_def| { - match *self { - ty::InstanceKind::DropGlue(..) => adt_def.destructor(tcx).map(|dtor| dtor.did), - ty::InstanceKind::AsyncDropGlueCtorShim(..) => { - adt_def.async_destructor(tcx).map(|dtor| dtor.ctor) - } - _ => unreachable!(), - } - .map_or_else(|| adt_def.is_enum(), |did| tcx.cross_crate_inlinable(did)) - }); - } - if let ty::InstanceKind::ThreadLocalShim(..) = *self { - return false; - } - tcx.cross_crate_inlinable(self.def_id()) - } - pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool { match *self { InstanceKind::Item(def_id) | InstanceKind::Virtual(def_id, _) => { @@ -369,11 +341,12 @@ impl<'tcx> InstanceKind<'tcx> { | InstanceKind::FnPtrAddrShim(..) | InstanceKind::FnPtrShim(..) | InstanceKind::DropGlue(_, Some(_)) - | InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => false, + | InstanceKind::FutureDropPollShim(..) + | InstanceKind::AsyncDropGlue(_, _) => false, + InstanceKind::AsyncDropGlueCtorShim(_, _) => false, InstanceKind::ClosureOnceShim { .. } | InstanceKind::ConstructCoroutineInClosureShim { .. } | InstanceKind::DropGlue(..) - | InstanceKind::AsyncDropGlueCtorShim(..) | InstanceKind::Item(_) | InstanceKind::Intrinsic(..) | InstanceKind::ReifyShim(..) @@ -450,8 +423,11 @@ pub fn fmt_instance( InstanceKind::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), InstanceKind::CloneShim(_, ty) => write!(f, " - shim({ty})"), InstanceKind::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"), - InstanceKind::AsyncDropGlueCtorShim(_, None) => write!(f, " - shim(None)"), - InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), + InstanceKind::FutureDropPollShim(_, proxy_ty, impl_ty) => { + write!(f, " - dropshim({proxy_ty}-{impl_ty})") + } + InstanceKind::AsyncDropGlue(_, ty) => write!(f, " - shim({ty})"), + InstanceKind::AsyncDropGlueCtorShim(_, ty) => write!(f, " - shim(Some({ty}))"), } } @@ -469,8 +445,61 @@ impl<'tcx> fmt::Display for Instance<'tcx> { } } +// async_drop_in_place::coroutine.poll, when T is a standart coroutine, +// should be resolved to this coroutine's future_drop_poll (through FutureDropPollShim proxy). +// async_drop_in_place::coroutine>::coroutine.poll, +// when T is a standart coroutine, should be resolved to this coroutine's future_drop_poll. +// async_drop_in_place::coroutine>::coroutine.poll, +// when T is not a coroutine, should be resolved to the innermost +// async_drop_in_place::coroutine's poll function (through FutureDropPollShim proxy) +fn resolve_async_drop_poll<'tcx>(mut cor_ty: Ty<'tcx>) -> Instance<'tcx> { + let first_cor = cor_ty; + let ty::Coroutine(poll_def_id, proxy_args) = first_cor.kind() else { + bug!(); + }; + let poll_def_id = *poll_def_id; + let mut child_ty = cor_ty; + loop { + if let ty::Coroutine(child_def, child_args) = child_ty.kind() { + cor_ty = child_ty; + if *child_def == poll_def_id { + child_ty = child_args.first().unwrap().expect_ty(); + continue; + } else { + return Instance { + def: ty::InstanceKind::FutureDropPollShim(poll_def_id, first_cor, cor_ty), + args: proxy_args, + }; + } + } else { + let ty::Coroutine(_, child_args) = cor_ty.kind() else { + bug!(); + }; + if first_cor != cor_ty { + return Instance { + def: ty::InstanceKind::FutureDropPollShim(poll_def_id, first_cor, cor_ty), + args: proxy_args, + }; + } else { + return Instance { + def: ty::InstanceKind::AsyncDropGlue(poll_def_id, cor_ty), + args: child_args, + }; + } + } + } +} + impl<'tcx> Instance<'tcx> { - pub fn new(def_id: DefId, args: GenericArgsRef<'tcx>) -> Instance<'tcx> { + /// Creates a new [`InstanceKind::Item`] from the `def_id` and `args`. + /// + /// Note that this item corresponds to the body of `def_id` directly, which + /// likely does not make sense for trait items which need to be resolved to an + /// implementation, and which may not even have a body themselves. Usages of + /// this function should probably use [`Instance::expect_resolve`], or if run + /// in a polymorphic environment or within a lint (that may encounter ambiguity) + /// [`Instance::try_resolve`] instead. + pub fn new_raw(def_id: DefId, args: GenericArgsRef<'tcx>) -> Instance<'tcx> { assert!( !args.has_escaping_bound_vars(), "args of instance {def_id:?} has escaping bound vars: {args:?}" @@ -489,7 +518,7 @@ impl<'tcx> Instance<'tcx> { } }); - Instance::new(def_id, args) + Instance::new_raw(def_id, args) } #[inline] @@ -582,7 +611,7 @@ impl<'tcx> Instance<'tcx> { let type_length = type_length(args); if !tcx.type_length_limit().value_within_limit(type_length) { let (shrunk, written_to_path) = - shrunk_instance_name(tcx, Instance::new(def_id, args)); + shrunk_instance_name(tcx, Instance::new_raw(def_id, args)); let mut path = PathBuf::new(); let was_written = if let Some(path2) = written_to_path { path = path2; @@ -712,10 +741,7 @@ impl<'tcx> Instance<'tcx> { .. }) ); - // We also need to generate a shim if this is an AFIT. - let needs_rpitit_shim = - tcx.return_position_impl_trait_in_trait_shim_data(def).is_some(); - if needs_track_caller_shim || needs_rpitit_shim { + if needs_track_caller_shim { if tcx.is_closure_like(def) { debug!( " => vtable fn pointer created for closure with #[track_caller]: {:?} for method {:?} {:?}", @@ -755,7 +781,7 @@ impl<'tcx> Instance<'tcx> { match needs_fn_once_adapter_shim(actual_kind, requested_kind) { Ok(true) => Instance::fn_once_adapter_instance(tcx, def_id, args), - _ => Instance::new(def_id, args), + _ => Instance::new_raw(def_id, args), } } @@ -767,7 +793,7 @@ impl<'tcx> Instance<'tcx> { ty::TypingEnv::fully_monomorphized(), def_id, args, - ty.ty_adt_def().and_then(|adt| tcx.hir().span_if_local(adt.did())).unwrap_or(DUMMY_SP), + ty.ty_adt_def().and_then(|adt| tcx.hir_span_if_local(adt.did())).unwrap_or(DUMMY_SP), ) } @@ -779,10 +805,19 @@ impl<'tcx> Instance<'tcx> { ty::TypingEnv::fully_monomorphized(), def_id, args, - ty.ty_adt_def().and_then(|adt| tcx.hir().span_if_local(adt.did())).unwrap_or(DUMMY_SP), + ty.ty_adt_def().and_then(|adt| tcx.hir_span_if_local(adt.did())).unwrap_or(DUMMY_SP), ) } + pub fn resolve_async_drop_in_place_poll( + tcx: TyCtxt<'tcx>, + def_id: DefId, + ty: Ty<'tcx>, + ) -> ty::Instance<'tcx> { + let args = tcx.mk_args(&[ty.into()]); + Instance::expect_resolve(tcx, ty::TypingEnv::fully_monomorphized(), def_id, args, DUMMY_SP) + } + #[instrument(level = "debug", skip(tcx), ret)] pub fn fn_once_adapter_instance( tcx: TyCtxt<'tcx>, @@ -793,7 +828,7 @@ impl<'tcx> Instance<'tcx> { let call_once = tcx .associated_items(fn_once) .in_definition_order() - .find(|it| it.kind == ty::AssocKind::Fn) + .find(|it| it.is_fn()) .unwrap() .def_id; let track_caller = @@ -847,6 +882,9 @@ impl<'tcx> Instance<'tcx> { }; if tcx.is_lang_item(trait_item_id, coroutine_callable_item) { + if tcx.is_async_drop_in_place_coroutine(coroutine_def_id) { + return Some(resolve_async_drop_poll(rcvr_args.type_at(0))); + } let ty::Coroutine(_, id_args) = *tcx.type_of(coroutine_def_id).skip_binder().kind() else { bug!() @@ -869,7 +907,7 @@ impl<'tcx> Instance<'tcx> { // This is important for `Iterator`'s combinators, but also useful for // adding future default methods to `Future`, for instance. debug_assert!(tcx.defaultness(trait_item_id).has_value()); - Some(Instance::new(trait_item_id, rcvr_args)) + Some(Instance::new_raw(trait_item_id, rcvr_args)) } } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 272bb0cc915f9..7ebfebea44e56 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1,20 +1,17 @@ -use std::num::NonZero; use std::ops::Bound; use std::{cmp, fmt}; use rustc_abi::{ - AddressSpace, Align, BackendRepr, ExternAbi, FieldIdx, FieldsShape, HasDataLayout, LayoutData, - PointeeInfo, PointerKind, Primitive, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout, + AddressSpace, Align, ExternAbi, FieldIdx, FieldsShape, HasDataLayout, LayoutData, PointeeInfo, + PointerKind, Primitive, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout, TyAbiInterface, VariantIdx, Variants, }; use rustc_error_messages::DiagMessage; use rustc_errors::{ Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, }; -use rustc_hashes::Hash64; use rustc_hir::LangItem; use rustc_hir::def_id::DefId; -use rustc_index::IndexVec; use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension}; use rustc_session::config::OptLevel; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; @@ -185,12 +182,7 @@ pub const WIDE_PTR_ADDR: usize = 0; /// - For a slice, this is the length. pub const WIDE_PTR_EXTRA: usize = 1; -/// The maximum supported number of lanes in a SIMD vector. -/// -/// This value is selected based on backend support: -/// * LLVM does not appear to have a vector width limit. -/// * Cranelift stores the base-2 log of the lane count in a 4 bit integer. -pub const MAX_SIMD_LANES: u64 = 1 << 0xF; +pub const MAX_SIMD_LANES: u64 = rustc_abi::MAX_SIMD_LANES; /// Used in `check_validity_requirement` to indicate the kind of initialization /// that is checked to be valid @@ -762,11 +754,9 @@ where 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 => - { - this.layout + // If all variants but one are uninhabited, the variant layout is the enum layout. + Variants::Single { index } if index == variant_index => { + return this; } Variants::Single { .. } | Variants::Empty => { @@ -783,29 +773,18 @@ where } let fields = match this.ty.kind() { - ty::Adt(def, _) if def.variants().is_empty() => - bug!("for_variant called on zero-variant enum {}", this.ty), + ty::Adt(def, _) if def.variants().is_empty() => { + bug!("for_variant called on zero-variant enum {}", this.ty) + } ty::Adt(def, _) => def.variant(variant_index).fields.len(), _ => bug!("`ty_and_layout_for_variant` on unexpected type {}", this.ty), }; - tcx.mk_layout(LayoutData { - variants: Variants::Single { index: variant_index }, - fields: match NonZero::new(fields) { - Some(fields) => FieldsShape::Union(fields), - None => FieldsShape::Arbitrary { offsets: IndexVec::new(), memory_index: IndexVec::new() }, - }, - backend_repr: BackendRepr::Memory { sized: true }, - largest_niche: None, - uninhabited: true, - align: tcx.data_layout.i8_align, - size: Size::ZERO, - max_repr_align: None, - unadjusted_abi_align: tcx.data_layout.i8_align.abi, - randomization_seed: Hash64::ZERO, - }) + tcx.mk_layout(LayoutData::uninhabited_variant(cx, variant_index, fields)) } - Variants::Multiple { ref variants, .. } => cx.tcx().mk_layout(variants[variant_index].clone()), + Variants::Multiple { ref variants, .. } => { + cx.tcx().mk_layout(variants[variant_index].clone()) + } }; assert_eq!(*layout.variants(), Variants::Single { index: variant_index }); @@ -1286,9 +1265,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: ExternAbi) | CCmseNonSecureCall | CCmseNonSecureEntry | Unadjusted => false, - Rust | RustCall | RustCold | RustIntrinsic => { - tcx.sess.panic_strategy() == PanicStrategy::Unwind - } + Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind, } } diff --git a/compiler/rustc_middle/src/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs index 6718493f6b32a..0cf5820959ee5 100644 --- a/compiler/rustc_middle/src/ty/list.rs +++ b/compiler/rustc_middle/src/ty/list.rs @@ -7,9 +7,9 @@ use std::{fmt, iter, mem, ptr, slice}; use rustc_data_structures::aligned::{Aligned, align_of}; use rustc_data_structures::sync::DynSync; use rustc_serialize::{Encodable, Encoder}; +use rustc_type_ir::FlagComputation; -use super::flags::FlagComputation; -use super::{DebruijnIndex, TypeFlags}; +use super::{DebruijnIndex, TyCtxt, TypeFlags}; use crate::arena::Arena; /// `List` is a bit like `&[T]`, but with some critical differences. @@ -93,7 +93,7 @@ impl RawList { T: Copy, { assert!(!mem::needs_drop::()); - assert!(mem::size_of::() != 0); + assert!(size_of::() != 0); assert!(!slice.is_empty()); let (layout, _offset) = @@ -155,7 +155,7 @@ macro_rules! impl_list_empty { static EMPTY: ListSkeleton<$header_ty, MaxAlign> = ListSkeleton { header: $header_init, len: 0, data: [] }; - assert!(mem::align_of::() <= mem::align_of::()); + assert!(align_of::() <= align_of::()); // SAFETY: `EMPTY` is sufficiently aligned to be an empty list for all // types with `align_of(T) <= align_of(MaxAlign)`, which we checked above. @@ -299,8 +299,8 @@ impl TypeInfo { } } -impl From for TypeInfo { - fn from(computation: FlagComputation) -> TypeInfo { +impl<'tcx> From>> for TypeInfo { + fn from(computation: FlagComputation>) -> TypeInfo { TypeInfo { flags: computation.flags, outer_exclusive_binder: computation.outer_exclusive_binder, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 527509af05fc7..b2a58897c31bd 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -17,7 +17,7 @@ use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::num::NonZero; use std::ptr::NonNull; -use std::{fmt, mem, str}; +use std::{fmt, iter, str}; pub use adt::*; pub use assoc::*; @@ -27,16 +27,20 @@ pub use intrinsic::IntrinsicDef; use rustc_abi::{Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, VariantIdx}; use rustc_ast::expand::StrippedCfgItem; use rustc_ast::node_id::NodeMap; -use rustc_attr_parsing::AttributeKind; +pub use rustc_ast_ir::{Movability, Mutability, try_visit}; +use rustc_attr_data_structures::AttributeKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; +use rustc_data_structures::unord::UnordMap; use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::LangItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; +use rustc_hir::definitions::DisambiguatorState; use rustc_index::IndexVec; +use rustc_index::bit_set::BitMatrix; use rustc_macros::{ Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, extension, @@ -46,12 +50,22 @@ use rustc_serialize::{Decodable, Encodable}; use rustc_session::lint::LintBuffer; pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; -use rustc_span::{ExpnId, ExpnKind, Ident, Span, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol, kw, sym}; +pub use rustc_type_ir::data_structures::{DelayedMap, DelayedSet}; +pub use rustc_type_ir::fast_reject::DeepRejectCtxt; +#[allow( + hidden_glob_reexports, + rustc::usage_of_type_ir_inherent, + rustc::non_glob_import_of_type_ir_inherent +)] +use rustc_type_ir::inherent; pub use rustc_type_ir::relate::VarianceDiagInfo; pub use rustc_type_ir::*; +#[allow(hidden_glob_reexports, unused_imports)] +use rustc_type_ir::{InferCtxtLike, Interner}; use tracing::{debug, instrument}; pub use vtable::*; -use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_attr_data_structures as attr, rustc_hir as hir}; pub use self::closure::{ BorrowKind, CAPTURE_STRUCT_LOCAL, CaptureInfo, CapturedPlace, ClosureTypeInfo, @@ -67,7 +81,7 @@ pub use self::context::{ CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, tls, }; -pub use self::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; +pub use self::fold::*; pub use self::instance::{Instance, InstanceKind, ReifyReason, ShortInstance, UnusedGenericParams}; pub use self::list::{List, ListWithCachedTypeInfo}; pub use self::opaque_types::OpaqueTypeKey; @@ -97,16 +111,19 @@ pub use self::typeck_results::{ CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity, Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind, }; -pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; +pub use self::visit::*; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; use crate::metadata::ModChild; use crate::middle::privacy::EffectiveVisibilities; -use crate::mir::{Body, CoroutineLayout}; +use crate::mir::{Body, CoroutineLayout, CoroutineSavedLocal, SourceInfo}; use crate::query::{IntoQueryParam, Providers}; use crate::ty; +use crate::ty::codec::{TyDecoder, TyEncoder}; pub use crate::ty::diagnostics::*; use crate::ty::fast_reject::SimplifiedType; +use crate::ty::layout::LayoutError; use crate::ty::util::Discr; +use crate::ty::walk::TypeWalker; pub mod abstract_const; pub mod adjustment; @@ -114,8 +131,6 @@ pub mod cast; pub mod codec; pub mod error; pub mod fast_reject; -pub mod flags; -pub mod fold; pub mod inhabitedness; pub mod layout; pub mod normalize_erasing_regions; @@ -125,9 +140,7 @@ pub mod relate; pub mod significant_drop_order; pub mod trait_def; pub mod util; -pub mod visit; pub mod vtable; -pub mod walk; mod adt; mod assoc; @@ -137,6 +150,7 @@ mod context; mod diagnostics; mod elaborate_impl; mod erase_regions; +mod fold; mod generic_args; mod generics; mod impls_ty; @@ -147,12 +161,12 @@ mod opaque_types; mod parameterized; mod predicate; mod region; -mod return_position_impl_trait_in_trait; mod rvalue_scopes; mod structural_impls; #[allow(hidden_glob_reexports)] mod sty; mod typeck_results; +mod visit; // Data types @@ -167,10 +181,10 @@ pub struct ResolverGlobalCtxt { /// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`. pub expn_that_defined: FxHashMap, pub effective_visibilities: EffectiveVisibilities, - pub extern_crate_map: FxHashMap, + pub extern_crate_map: UnordMap, pub maybe_unused_trait_imports: FxIndexSet, pub module_children: LocalDefIdMap>, - pub glob_map: FxHashMap>, + pub glob_map: FxIndexMap>, pub main_def: Option, pub trait_impls: FxIndexMap>, /// A list of proc macro LocalDefIds, written out in the order in which @@ -206,6 +220,8 @@ pub struct ResolverAstLowering { pub node_id_to_def_id: NodeMap, + pub disambiguator: DisambiguatorState, + pub trait_map: NodeMap>, /// List functions and methods for which lifetime elision was successful. pub lifetime_elision_allowed: FxHashSet, @@ -295,7 +311,10 @@ impl Visibility { } else if restricted_id == tcx.parent_module_from_def_id(def_id).to_local_def_id() { "pub(self)".to_string() } else { - format!("pub({})", tcx.item_name(restricted_id.to_def_id())) + format!( + "pub(in crate{})", + tcx.def_path(restricted_id.to_def_id()).to_string_no_crate_verbose() + ) } } ty::Visibility::Public => "pub".to_string(), @@ -441,7 +460,7 @@ impl<'tcx> rustc_type_ir::inherent::IntoKind for Ty<'tcx> { } } -impl<'tcx> rustc_type_ir::visit::Flags for Ty<'tcx> { +impl<'tcx> rustc_type_ir::Flags for Ty<'tcx> { fn flags(&self) -> TypeFlags { self.0.flags } @@ -455,7 +474,7 @@ impl EarlyParamRegion { /// 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 && self.name != kw::Empty + self.name != kw::UnderscoreLifetime } } @@ -537,6 +556,13 @@ impl<'tcx> TypeFoldable> for Term<'tcx> { ty::TermKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), } } + + fn fold_with>>(self, folder: &mut F) -> Self { + match self.unpack() { + ty::TermKind::Ty(ty) => ty.fold_with(folder).into(), + ty::TermKind::Const(ct) => ct.fold_with(folder).into(), + } + } } impl<'tcx> TypeVisitable> for Term<'tcx> { @@ -548,13 +574,13 @@ impl<'tcx> TypeVisitable> for Term<'tcx> { } } -impl<'tcx, E: TyEncoder>> Encodable for Term<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for Term<'tcx> { fn encode(&self, e: &mut E) { self.unpack().encode(e) } } -impl<'tcx, D: TyDecoder>> Decodable for Term<'tcx> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for Term<'tcx> { fn decode(d: &mut D) -> Self { let res: TermKind<'tcx> = Decodable::decode(d); res.pack() @@ -624,6 +650,20 @@ impl<'tcx> Term<'tcx> { TermKind::Const(ct) => ct.is_ct_infer(), } } + + /// Iterator that walks `self` and any types reachable from + /// `self`, in depth-first order. Note that just walks the types + /// that appear in `self`, it does not descend into the fields of + /// structs or variants. For example: + /// + /// ```text + /// isize => { isize } + /// Foo> => { Foo>, Bar, isize } + /// [isize] => { [isize], isize } + /// ``` + pub fn walk(self) -> TypeWalker> { + TypeWalker::new(self.into()) + } } const TAG_MASK: usize = 0b11; @@ -637,12 +677,12 @@ impl<'tcx> TermKind<'tcx> { let (tag, ptr) = match self { TermKind::Ty(ty) => { // Ensure we can use the tag bits. - assert_eq!(mem::align_of_val(&*ty.0.0) & TAG_MASK, 0); + assert_eq!(align_of_val(&*ty.0.0) & TAG_MASK, 0); (TYPE_TAG, NonNull::from(ty.0.0).cast()) } TermKind::Const(ct) => { // Ensure we can use the tag bits. - assert_eq!(mem::align_of_val(&*ct.0.0) & TAG_MASK, 0); + assert_eq!(align_of_val(&*ct.0.0) & TAG_MASK, 0); (CONST_TAG, NonNull::from(ct.0.0).cast()) } }; @@ -780,7 +820,22 @@ pub struct OpaqueHiddenType<'tcx> { pub ty: Ty<'tcx>, } +/// Whether we're currently in HIR typeck or MIR borrowck. +#[derive(Debug, Clone, Copy)] +pub enum DefiningScopeKind { + /// During writeback in typeck, we don't care about regions and simply + /// erase them. This means we also don't check whether regions are + /// universal in the opaque type key. This will only be checked in + /// MIR borrowck. + HirTypeck, + MirBorrowck, +} + impl<'tcx> OpaqueHiddenType<'tcx> { + pub fn new_error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> OpaqueHiddenType<'tcx> { + OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(tcx, guar) } + } + pub fn build_mismatch_error( &self, other: &Self, @@ -806,8 +861,7 @@ impl<'tcx> OpaqueHiddenType<'tcx> { self, opaque_type_key: OpaqueTypeKey<'tcx>, tcx: TyCtxt<'tcx>, - // typeck errors have subpar spans for opaque types, so delay error reporting until borrowck. - ignore_errors: bool, + defining_scope_kind: DefiningScopeKind, ) -> Self { let OpaqueTypeKey { def_id, args } = opaque_type_key; @@ -826,10 +880,19 @@ impl<'tcx> OpaqueHiddenType<'tcx> { let map = args.iter().zip(id_args).collect(); debug!("map = {:#?}", map); - // Convert the type from the function into a type valid outside - // the function, by replacing invalid regions with 'static, - // after producing an error for each of them. - self.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span, ignore_errors)) + // Convert the type from the function into a type valid outside by mapping generic + // parameters to into the context of the opaque. + // + // We erase regions when doing this during HIR typeck. + let this = match defining_scope_kind { + DefiningScopeKind::HirTypeck => tcx.erase_regions(self), + DefiningScopeKind::MirBorrowck => self, + }; + let result = this.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span)); + if cfg!(debug_assertions) && matches!(defining_scope_kind, DefiningScopeKind::HirTypeck) { + assert_eq!(result.ty, tcx.erase_regions(result.ty)); + } + result } } @@ -937,7 +1000,7 @@ impl rustc_type_ir::inherent::PlaceholderLike for PlaceholderConst { pub type Clauses<'tcx> = &'tcx ListWithCachedTypeInfo>; -impl<'tcx> rustc_type_ir::visit::Flags for Clauses<'tcx> { +impl<'tcx> rustc_type_ir::Flags for Clauses<'tcx> { fn flags(&self) -> TypeFlags { (**self).flags() } @@ -951,7 +1014,7 @@ impl<'tcx> rustc_type_ir::visit::Flags for Clauses<'tcx> { /// environment. `ParamEnv` is the type that represents this information. See the /// [dev guide chapter][param_env_guide] for more information. /// -/// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html +/// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/typing_parameter_envs.html #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(HashStable, TypeVisitable, TypeFoldable)] pub struct ParamEnv<'tcx> { @@ -975,7 +1038,7 @@ impl<'tcx> ParamEnv<'tcx> { /// to use an empty environment. See the [dev guide section][param_env_guide] /// for information on what a `ParamEnv` is and how to acquire one. /// - /// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html + /// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/typing_parameter_envs.html #[inline] pub fn empty() -> Self { Self::new(ListWithCachedTypeInfo::empty()) @@ -1117,17 +1180,13 @@ pub struct PseudoCanonicalInput<'tcx, T> { pub struct Destructor { /// The `DefId` of the destructor method pub did: DefId, - /// The constness of the destructor method - pub constness: hir::Constness, } // FIXME: consider combining this definition with regular `Destructor` #[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)] pub struct AsyncDestructor { - /// The `DefId` of the async destructor future constructor - pub ctor: DefId, - /// The `DefId` of the async destructor future type - pub future: DefId, + /// The `DefId` of the `impl AsyncDrop` + pub impl_did: DefId, } #[derive(Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] @@ -1150,7 +1209,7 @@ pub struct VariantDef { /// `DefId` that identifies the variant's constructor. /// If this variant is a struct variant, then this is `None`. pub ctor: Option<(CtorKind, DefId)>, - /// Variant or struct name, maybe empty for anonymous adt (struct or union). + /// Variant or struct name. pub name: Symbol, /// Discriminant of this variant. pub discr: VariantDiscr, @@ -1206,12 +1265,23 @@ impl VariantDef { } } - /// Is this field list non-exhaustive? + /// Returns `true` if the field list of this variant is `#[non_exhaustive]`. + /// + /// Note that this function will return `true` even if the type has been + /// defined in the crate currently being compiled. If that's not what you + /// want, see [`Self::field_list_has_applicable_non_exhaustive`]. #[inline] pub fn is_field_list_non_exhaustive(&self) -> bool { self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE) } + /// Returns `true` if the field list of this variant is `#[non_exhaustive]` + /// and the type has been defined in another crate. + #[inline] + pub fn field_list_has_applicable_non_exhaustive(&self) -> bool { + self.is_field_list_non_exhaustive() && !self.def_id.is_local() + } + /// Computes the `Ident` of this variant by looking up the `Span` pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident { Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap()) @@ -1414,39 +1484,6 @@ pub enum ImplOverlapKind { /// 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 (tracked in #56484). - /// - /// 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 on - /// that difference, doing what reduces to the following set of impls: - /// - /// ```compile_fail,(E0119) - /// 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. - FutureCompatOrderDepTraitObjects, } /// Useful source information about where a desugared associated type for an @@ -1465,7 +1502,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn provided_trait_methods(self, id: DefId) -> impl 'tcx + Iterator { self.associated_items(id) .in_definition_order() - .filter(move |item| item.kind == AssocKind::Fn && item.defaultness(self).has_value()) + .filter(move |item| item.is_fn() && item.defaultness(self).has_value()) } pub fn repr_options_of_def(self, did: LocalDefId) -> ReprOptions { @@ -1611,8 +1648,11 @@ impl<'tcx> TyCtxt<'tcx> { /// return-position `impl Trait` from a trait, then provide the source info /// about where that RPITIT came from. pub fn opt_rpitit_info(self, def_id: DefId) -> Option { - if let DefKind::AssocTy = self.def_kind(def_id) { - self.associated_item(def_id).opt_rpitit_info + if let DefKind::AssocTy = self.def_kind(def_id) + && let AssocKind::Type { data: AssocTypeData::Rpitit(rpitit_info) } = + self.associated_item(def_id).kind + { + Some(rpitit_info) } else { None } @@ -1624,7 +1664,7 @@ impl<'tcx> TyCtxt<'tcx> { }) } - /// Returns `true` if the impls are the same polarity and the trait either + /// Returns `Some` if the impls are the same polarity and the trait either /// has no items or is annotated `#[marker]` and prevents item overrides. #[instrument(level = "debug", skip(self), ret)] pub fn impls_are_allowed_to_overlap( @@ -1665,18 +1705,6 @@ impl<'tcx> TyCtxt<'tcx> { return Some(ImplOverlapKind::Permitted { marker: true }); } - if let Some(self_ty1) = - self.self_ty_of_trait_impl_enabling_order_dep_trait_object_hack(def_id1) - && let Some(self_ty2) = - self.self_ty_of_trait_impl_enabling_order_dep_trait_object_hack(def_id2) - { - if self_ty1 == self_ty2 { - return Some(ImplOverlapKind::FutureCompatOrderDepTraitObjects); - } else { - debug!("found {self_ty1:?} != {self_ty2:?}"); - } - } - None } @@ -1729,18 +1757,20 @@ impl<'tcx> TyCtxt<'tcx> { | ty::InstanceKind::Virtual(..) | ty::InstanceKind::ClosureOnceShim { .. } | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } + | ty::InstanceKind::FutureDropPollShim(..) | ty::InstanceKind::DropGlue(..) | ty::InstanceKind::CloneShim(..) | ty::InstanceKind::ThreadLocalShim(..) | ty::InstanceKind::FnPtrAddrShim(..) - | ty::InstanceKind::AsyncDropGlueCtorShim(..) => self.mir_shims(instance), + | ty::InstanceKind::AsyncDropGlueCtorShim(..) + | ty::InstanceKind::AsyncDropGlue(..) => self.mir_shims(instance), } } // FIXME(@lcnr): Remove this function. pub fn get_attrs_unchecked(self, did: DefId) -> &'tcx [hir::Attribute] { if let Some(did) = did.as_local() { - self.hir().attrs(self.local_def_id_to_hir_id(did)) + self.hir_attrs(self.local_def_id_to_hir_id(did)) } else { self.attrs_for_def(did) } @@ -1757,14 +1787,14 @@ impl<'tcx> TyCtxt<'tcx> { /// Gets all attributes. /// - /// To see if an item has a specific attribute, you should use [`rustc_attr_parsing::find_attr!`] so you can use matching. + /// To see if an item has a specific attribute, you should use [`rustc_attr_data_structures::find_attr!`] so you can use matching. pub fn get_all_attrs( self, did: impl Into, ) -> impl Iterator { let did: DefId = did.into(); if let Some(did) = did.as_local() { - self.hir().attrs(self.local_def_id_to_hir_id(did)).iter() + self.hir_attrs(self.local_def_id_to_hir_id(did)).iter() } else { self.attrs_for_def(did).iter() } @@ -1808,7 +1838,7 @@ impl<'tcx> TyCtxt<'tcx> { ) -> impl Iterator { let filter_fn = move |a: &&hir::Attribute| a.path_matches(attr); if let Some(did) = did.as_local() { - self.hir().attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn) + self.hir_attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn) } else { self.attrs_for_def(did).iter().filter(filter_fn) } @@ -1849,20 +1879,27 @@ impl<'tcx> TyCtxt<'tcx> { self.def_kind(trait_def_id) == DefKind::TraitAlias } - /// Returns layout of a coroutine. Layout might be unavailable if the + /// Arena-alloc of LayoutError for coroutine layout + fn layout_error(self, err: LayoutError<'tcx>) -> &'tcx LayoutError<'tcx> { + self.arena.alloc(err) + } + + /// Returns layout of a non-async-drop coroutine. Layout might be unavailable if the /// coroutine is tainted by errors. /// /// Takes `coroutine_kind` which can be acquired from the `CoroutineArgs::kind_ty`, /// e.g. `args.as_coroutine().kind_ty()`. - pub fn coroutine_layout( + fn ordinary_coroutine_layout( self, def_id: DefId, - coroutine_kind_ty: Ty<'tcx>, - ) -> Option<&'tcx CoroutineLayout<'tcx>> { + args: GenericArgsRef<'tcx>, + ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> { + let coroutine_kind_ty = args.as_coroutine().kind_ty(); let mir = self.optimized_mir(def_id); + let ty = || Ty::new_coroutine(self, def_id, args); // Regular coroutine if coroutine_kind_ty.is_unit() { - mir.coroutine_layout_raw() + mir.coroutine_layout_raw().ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } else { // If we have a `Coroutine` that comes from an coroutine-closure, // then it may be a by-move or by-ref body. @@ -1876,6 +1913,7 @@ impl<'tcx> TyCtxt<'tcx> { // a by-ref coroutine. if identity_kind_ty == coroutine_kind_ty { mir.coroutine_layout_raw() + .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) } else { assert_matches!(coroutine_kind_ty.to_opt_closure_kind(), Some(ClosureKind::FnOnce)); assert_matches!( @@ -1884,7 +1922,63 @@ impl<'tcx> TyCtxt<'tcx> { ); self.optimized_mir(self.coroutine_by_move_body_def_id(def_id)) .coroutine_layout_raw() + .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) + } + } + } + + /// Returns layout of a `async_drop_in_place::{closure}` coroutine + /// (returned from `async fn async_drop_in_place(..)`). + /// Layout might be unavailable if the coroutine is tainted by errors. + fn async_drop_coroutine_layout( + self, + def_id: DefId, + args: GenericArgsRef<'tcx>, + ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> { + let ty = || Ty::new_coroutine(self, def_id, args); + if args[0].has_placeholders() || args[0].has_non_region_param() { + return Err(self.layout_error(LayoutError::TooGeneric(ty()))); + } + let instance = InstanceKind::AsyncDropGlue(def_id, Ty::new_coroutine(self, def_id, args)); + self.mir_shims(instance) + .coroutine_layout_raw() + .ok_or_else(|| self.layout_error(LayoutError::Unknown(ty()))) + } + + /// Returns layout of a coroutine. Layout might be unavailable if the + /// coroutine is tainted by errors. + pub fn coroutine_layout( + self, + def_id: DefId, + args: GenericArgsRef<'tcx>, + ) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> { + if self.is_async_drop_in_place_coroutine(def_id) { + // layout of `async_drop_in_place::{closure}` in case, + // when T is a coroutine, contains this internal coroutine's ptr in upvars + // and doesn't require any locals. Here is an `empty coroutine's layout` + let arg_cor_ty = args.first().unwrap().expect_ty(); + if arg_cor_ty.is_coroutine() { + let span = self.def_span(def_id); + let source_info = SourceInfo::outermost(span); + // Even minimal, empty coroutine has 3 states (RESERVED_VARIANTS), + // so variant_fields and variant_source_info should have 3 elements. + let variant_fields: IndexVec> = + iter::repeat(IndexVec::new()).take(CoroutineArgs::RESERVED_VARIANTS).collect(); + let variant_source_info: IndexVec = + iter::repeat(source_info).take(CoroutineArgs::RESERVED_VARIANTS).collect(); + let proxy_layout = CoroutineLayout { + field_tys: [].into(), + field_names: [].into(), + variant_fields, + variant_source_info, + storage_conflicts: BitMatrix::new(0, 0), + }; + return Ok(self.arena.alloc(proxy_layout)); + } else { + self.async_drop_coroutine_layout(def_id, args) } + } else { + self.ordinary_coroutine_layout(def_id, args) } } @@ -1919,6 +2013,10 @@ impl<'tcx> TyCtxt<'tcx> { None } + pub fn is_exportable(self, def_id: DefId) -> bool { + self.exportable_items(def_id.krate).contains(&def_id) + } + /// Check if the given `DefId` is `#\[automatically_derived\]`, *and* /// whether it was produced by expanding a builtin derive macro. pub fn is_builtin_derived(self, def_id: DefId) -> bool { @@ -1952,15 +2050,15 @@ impl<'tcx> TyCtxt<'tcx> { /// 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 + pub fn hygienic_eq(self, use_ident: Ident, def_ident: Ident, def_parent_def_id: DefId) -> bool { + // We could use `Ident::eq` here, but we deliberately don't. The identifier // 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 + use_ident.name == def_ident.name + && use_ident .span .ctxt() - .hygienic_eq(def_name.span.ctxt(), self.expn_that_defined(def_parent_def_id)) + .hygienic_eq(def_ident.span.ctxt(), self.expn_that_defined(def_parent_def_id)) } pub fn adjust_ident(self, mut ident: Ident, scope: DefId) -> Ident { diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index e86e01451fefb..f2a4a5a4ecf78 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -11,8 +11,10 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use tracing::{debug, instrument}; use crate::traits::query::NoSolution; -use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder}; -use crate::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; +use crate::ty::{ + self, EarlyBinder, FallibleTypeFolder, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeFolder, + TypeVisitableExt, +}; #[derive(Debug, Copy, Clone, HashStable, TyEncodable, TyDecodable)] pub enum NormalizationError<'tcx> { diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs index cf789807bb04a..9445a18ad76b1 100644 --- a/compiler/rustc_middle/src/ty/opaque_types.rs +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -4,8 +4,9 @@ use rustc_span::def_id::DefId; use tracing::{debug, instrument, trace}; use crate::error::ConstNotUsedTraitAlias; -use crate::ty::fold::{TypeFolder, TypeSuperFoldable}; -use crate::ty::{self, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{ + self, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, +}; pub type OpaqueTypeKey<'tcx> = rustc_type_ir::OpaqueTypeKey>; @@ -19,12 +20,6 @@ pub(super) struct ReverseMapper<'tcx> { /// for an explanation of this field. do_not_error: bool, - /// We do not want to emit any errors in typeck because - /// the spans in typeck are subpar at the moment. - /// Borrowck will do the same work again (this time with - /// lifetime information) and thus report better errors. - ignore_errors: bool, - /// Span of function being checked. span: Span, } @@ -34,9 +29,8 @@ impl<'tcx> ReverseMapper<'tcx> { tcx: TyCtxt<'tcx>, map: FxHashMap, GenericArg<'tcx>>, span: Span, - ignore_errors: bool, ) -> Self { - Self { tcx, map, do_not_error: false, ignore_errors, span } + Self { tcx, map, do_not_error: false, span } } fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { @@ -101,7 +95,7 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { #[instrument(skip(self), level = "debug")] fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { + match r.kind() { // Ignore bound regions and `'static` regions that appear in the // type, we only need to remap regions that reference lifetimes // from the function declaration. @@ -175,20 +169,18 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { Some(u) => panic!("type mapped to unexpected kind: {u:?}"), None => { debug!(?param, ?self.map); - if !self.ignore_errors { - self.tcx - .dcx() - .struct_span_err( - self.span, - format!( - "type parameter `{ty}` is part of concrete type but not \ + let guar = self + .tcx + .dcx() + .struct_span_err( + self.span, + format!( + "type parameter `{ty}` is part of concrete type but not \ used in parameter list for the `impl Trait` type alias" - ), - ) - .emit(); - } - - Ty::new_misc_error(self.tcx) + ), + ) + .emit(); + Ty::new_error(self.tcx, guar) } } } @@ -216,8 +208,7 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { ct: ct.to_string(), span: self.span, }) - .emit_unless(self.ignore_errors); - + .emit(); ty::Const::new_error(self.tcx, guar) } } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 8eaf0a58f7086..ecd6132b3ef35 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -3,6 +3,7 @@ use std::hash::Hash; use rustc_data_structures::unord::UnordMap; use rustc_hir::def_id::DefIndex; use rustc_index::{Idx, IndexVec}; +use rustc_span::Symbol; use crate::ty; @@ -64,9 +65,11 @@ trivially_parameterized_over_tcx! { crate::middle::lib_features::FeatureStability, crate::middle::resolve_bound_vars::ObjectLifetimeDefault, crate::mir::ConstQualifs, + ty::AsyncDestructor, ty::AssocItemContainer, ty::Asyncness, ty::DeducedParamAttrs, + ty::Destructor, ty::Generics, ty::ImplPolarity, ty::ImplTraitInTraitData, @@ -80,10 +83,10 @@ trivially_parameterized_over_tcx! { rustc_ast::Attribute, rustc_ast::DelimArgs, rustc_ast::expand::StrippedCfgItem, - rustc_attr_parsing::ConstStability, - rustc_attr_parsing::DefaultBodyStability, - rustc_attr_parsing::Deprecation, - rustc_attr_parsing::Stability, + rustc_attr_data_structures::ConstStability, + rustc_attr_data_structures::DefaultBodyStability, + rustc_attr_data_structures::Deprecation, + rustc_attr_data_structures::Stability, rustc_hir::Constness, rustc_hir::Defaultness, rustc_hir::Safety, @@ -96,6 +99,7 @@ trivially_parameterized_over_tcx! { rustc_hir::def_id::DefIndex, rustc_hir::definitions::DefKey, rustc_hir::OpaqueTyOrigin, + rustc_hir::PreciseCapturingArgKind, rustc_index::bit_set::DenseBitSet, rustc_index::bit_set::FiniteBitSet, rustc_session::cstore::ForeignModule, @@ -109,7 +113,7 @@ trivially_parameterized_over_tcx! { rustc_span::Span, rustc_span::Symbol, rustc_span::def_id::DefPathHash, - rustc_span::hygiene::SyntaxContextData, + rustc_span::hygiene::SyntaxContextKey, rustc_span::Ident, rustc_type_ir::Variance, rustc_hir::Attribute, diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs index e604aedd05e32..5af9b17dd7777 100644 --- a/compiler/rustc_middle/src/ty/pattern.rs +++ b/compiler/rustc_middle/src/ty/pattern.rs @@ -1,14 +1,54 @@ use std::fmt; use rustc_data_structures::intern::Interned; -use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; +use rustc_macros::HashStable; +use rustc_type_ir::ir_print::IrPrint; +use rustc_type_ir::{ + FlagComputation, Flags, {self as ir}, +}; +use super::TyCtxt; use crate::ty; +pub type PatternKind<'tcx> = ir::PatternKind>; + #[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)] #[rustc_pass_by_value] pub struct Pattern<'tcx>(pub Interned<'tcx, PatternKind<'tcx>>); +impl<'tcx> Flags for Pattern<'tcx> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + match &**self { + ty::PatternKind::Range { start, end } => { + FlagComputation::for_const_kind(&start.kind()).flags + | FlagComputation::for_const_kind(&end.kind()).flags + } + ty::PatternKind::Or(pats) => { + let mut flags = pats[0].flags(); + for pat in pats[1..].iter() { + flags |= pat.flags(); + } + flags + } + } + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + match &**self { + ty::PatternKind::Range { start, end } => { + start.outer_exclusive_binder().max(end.outer_exclusive_binder()) + } + ty::PatternKind::Or(pats) => { + let mut idx = pats[0].outer_exclusive_binder(); + for pat in pats[1..].iter() { + idx = idx.max(pat.outer_exclusive_binder()); + } + idx + } + } + } +} + impl<'tcx> std::ops::Deref for Pattern<'tcx> { type Target = PatternKind<'tcx>; @@ -23,28 +63,58 @@ impl<'tcx> fmt::Debug for Pattern<'tcx> { } } -impl<'tcx> fmt::Debug for PatternKind<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - PatternKind::Range { start, end, include_end } => { - if let Some(start) = start { - write!(f, "{start}")?; - } - write!(f, "..")?; - if include_end { - write!(f, "=")?; +impl<'tcx> IrPrint> for TyCtxt<'tcx> { + fn print(t: &PatternKind<'tcx>, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *t { + PatternKind::Range { start, end } => { + write!(f, "{start}")?; + + if let Some(c) = end.try_to_value() { + let end = c.valtree.unwrap_leaf(); + let size = end.size(); + let max = match c.ty.kind() { + ty::Int(_) => { + Some(ty::ScalarInt::truncate_from_int(size.signed_int_max(), size)) + } + ty::Uint(_) => { + Some(ty::ScalarInt::truncate_from_uint(size.unsigned_int_max(), size)) + } + ty::Char => Some(ty::ScalarInt::truncate_from_uint(char::MAX, size)), + _ => None, + }; + if let Some((max, _)) = max + && end == max + { + return write!(f, ".."); + } } - if let Some(end) = end { - write!(f, "{end}")?; + + write!(f, "..={end}") + } + PatternKind::Or(patterns) => { + write!(f, "(")?; + let mut first = true; + for pat in patterns { + if first { + first = false + } else { + write!(f, " | ")?; + } + write!(f, "{pat:?}")?; } - Ok(()) + write!(f, ")") } } } + + fn print_debug(t: &PatternKind<'tcx>, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + Self::print(t, fmt) + } } -#[derive(Clone, PartialEq, Eq, Hash)] -#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] -pub enum PatternKind<'tcx> { - Range { start: Option>, end: Option>, include_end: bool }, +impl<'tcx> rustc_type_ir::inherent::IntoKind for Pattern<'tcx> { + type Kind = PatternKind<'tcx>; + fn kind(self) -> Self::Kind { + *self + } } diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 1674ca4cfc567..551d816941b6e 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -63,7 +63,7 @@ impl<'tcx> rustc_type_ir::inherent::IntoKind for Predicate<'tcx> { } } -impl<'tcx> rustc_type_ir::visit::Flags for Predicate<'tcx> { +impl<'tcx> rustc_type_ir::Flags for Predicate<'tcx> { fn flags(&self) -> TypeFlags { self.0.flags } @@ -121,11 +121,10 @@ impl<'tcx> Predicate<'tcx> { /// unsoundly accept some programs. See #91068. #[inline] pub fn allow_normalization(self) -> bool { - // Keep this in sync with the one in `rustc_type_ir::inherent`! match self.kind().skip_binder() { - PredicateKind::Clause(ClauseKind::WellFormed(_)) - | PredicateKind::AliasRelate(..) - | PredicateKind::NormalizesTo(..) => false, + PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) => { + false + } PredicateKind::Clause(ClauseKind::Trait(_)) | PredicateKind::Clause(ClauseKind::HostEffect(..)) | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) @@ -137,6 +136,7 @@ impl<'tcx> Predicate<'tcx> { | PredicateKind::Coerce(_) | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) | PredicateKind::ConstEquate(_, _) + | PredicateKind::NormalizesTo(..) | PredicateKind::Ambiguous => true, } } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index dc2040aa5cf85..9172c5d3ab752 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -139,8 +139,7 @@ pub trait Printer<'tcx>: Sized { match key.disambiguated_data.data { DefPathData::Closure => { - // FIXME(async_closures): This is somewhat ugly. - // We need to additionally print the `kind` field of a closure if + // We need to additionally print the `kind` field of a coroutine if // it is desugared from a coroutine-closure. if let Some(hir::CoroutineKind::Desugared( _, @@ -156,6 +155,10 @@ pub trait Printer<'tcx>: Sized { // Closures' own generics are only captures, don't print them. } } + DefPathData::SyntheticCoroutineBody => { + // Synthetic coroutine bodies have no distinct generics, since like + // closures they're all just internal state of the coroutine. + } // This covers both `DefKind::AnonConst` and `DefKind::InlineConst`. // Anon consts doesn't have their own generics, and inline consts' own // generics are their inferred types, so don't print them. @@ -379,7 +382,7 @@ pub fn shrunk_instance_name<'tcx>( return (s, None); } - let path = tcx.output_filenames(()).temp_path_ext("long-type.txt", None); + let path = tcx.output_filenames(()).temp_path_for_diagnostic("long-type.txt"); let written_to_path = std::fs::write(&path, s).ok().map(|_| path); (shrunk, written_to_path) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c77b37a302bc9..6fd6aff0e2b23 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -6,7 +6,7 @@ use std::ops::{Deref, DerefMut}; use rustc_abi::{ExternAbi, Size}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_data_structures::unord::UnordMap; use rustc_hir as hir; use rustc_hir::LangItem; @@ -63,6 +63,19 @@ thread_local! { static FORCE_TRIMMED_PATH: Cell = const { Cell::new(false) }; static REDUCED_QUERIES: Cell = const { Cell::new(false) }; static NO_VISIBLE_PATH: Cell = const { Cell::new(false) }; + static NO_VISIBLE_PATH_IF_DOC_HIDDEN: Cell = const { Cell::new(false) }; + static RTN_MODE: Cell = const { Cell::new(RtnMode::ForDiagnostic) }; +} + +/// Rendering style for RTN types. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum RtnMode { + /// Print the RTN type as an impl trait with its path, i.e.e `impl Sized { T::method(..) }`. + ForDiagnostic, + /// Print the RTN type as an impl trait, i.e. `impl Sized`. + ForSignature, + /// Print the RTN type as a value path, i.e. `T::method(..): ...`. + ForSuggestion, } macro_rules! define_helper { @@ -122,8 +135,42 @@ define_helper!( /// Prevent selection of visible paths. `Display` impl of DefId will prefer /// visible (public) reexports of types as paths. fn with_no_visible_paths(NoVisibleGuard, NO_VISIBLE_PATH); + /// Prevent selection of visible paths if the paths are through a doc hidden path. + fn with_no_visible_paths_if_doc_hidden(NoVisibleIfDocHiddenGuard, NO_VISIBLE_PATH_IF_DOC_HIDDEN); ); +#[must_use] +pub struct RtnModeHelper(RtnMode); + +impl RtnModeHelper { + pub fn with(mode: RtnMode) -> RtnModeHelper { + RtnModeHelper(RTN_MODE.with(|c| c.replace(mode))) + } +} + +impl Drop for RtnModeHelper { + fn drop(&mut self) { + RTN_MODE.with(|c| c.set(self.0)) + } +} + +/// Print types for the purposes of a suggestion. +/// +/// Specifically, this will render RPITITs as `T::method(..)` which is suitable for +/// things like where-clauses. +pub macro with_types_for_suggestion($e:expr) {{ + let _guard = $crate::ty::print::pretty::RtnModeHelper::with(RtnMode::ForSuggestion); + $e +}} + +/// Print types for the purposes of a signature suggestion. +/// +/// Specifically, this will render RPITITs as `impl Trait` rather than `T::method(..)`. +pub macro with_types_for_signature($e:expr) {{ + let _guard = $crate::ty::print::pretty::RtnModeHelper::with(RtnMode::ForSignature); + $e +}} + /// Avoids running any queries during prints. pub macro with_no_queries($e:expr) {{ $crate::ty::print::with_reduced_queries!($crate::ty::print::with_forced_impl_filename_line!( @@ -133,6 +180,20 @@ pub macro with_no_queries($e:expr) {{ )) }} +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum WrapBinderMode { + ForAll, + Unsafe, +} +impl WrapBinderMode { + pub fn start_str(self) -> &'static str { + match self { + WrapBinderMode::ForAll => "for<", + WrapBinderMode::Unsafe => "unsafe<", + } + } +} + /// 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 @@ -219,7 +280,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.print_def_path(def_id, args) } - fn in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> + fn print_in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> where T: Print<'tcx, Self> + TypeFoldable>, { @@ -229,6 +290,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { fn wrap_binder Result<(), fmt::Error>>( &mut self, value: &ty::Binder<'tcx, T>, + _mode: WrapBinderMode, f: F, ) -> Result<(), PrintError> where @@ -510,6 +572,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { return Ok(false); }; + if self.tcx().is_doc_hidden(visible_parent) && with_no_visible_paths_if_doc_hidden() { + return Ok(false); + } + let actual_parent = self.tcx().opt_parent(def_id); debug!( "try_print_visible_def_path: visible_parent={:?} actual_parent={:?}", @@ -703,8 +769,9 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } ty::FnPtr(ref sig_tys, hdr) => p!(print(sig_tys.with(hdr))), ty::UnsafeBinder(ref bound_ty) => { - // FIXME(unsafe_binders): Make this print `unsafe<>` rather than `for<>`. - self.wrap_binder(bound_ty, |ty, cx| cx.pretty_print_type(*ty))?; + self.wrap_binder(bound_ty, WrapBinderMode::Unsafe, |ty, cx| { + cx.pretty_print_type(*ty) + })?; } ty::Infer(infer_ty) => { if self.should_print_verbose() { @@ -753,7 +820,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::Foreign(def_id) => { p!(print_def_path(def_id, &[])); } - ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ref data) => { + ty::Alias(ty::Projection | ty::Inherent | ty::Free, ref data) => { p!(print(data)) } ty::Placeholder(placeholder) => match placeholder.bound.kind { @@ -1067,29 +1134,33 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { }; if let Some(return_ty) = entry.return_ty { - self.wrap_binder(&bound_args_and_self_ty, |(args, _), cx| { - define_scoped_cx!(cx); - p!(write("{}", tcx.item_name(trait_def_id))); - p!("("); - - for (idx, ty) in args.iter().enumerate() { - if idx > 0 { - p!(", "); + self.wrap_binder( + &bound_args_and_self_ty, + WrapBinderMode::ForAll, + |(args, _), cx| { + define_scoped_cx!(cx); + p!(write("{}", tcx.item_name(trait_def_id))); + p!("("); + + for (idx, ty) in args.iter().enumerate() { + if idx > 0 { + p!(", "); + } + p!(print(ty)); } - p!(print(ty)); - } - p!(")"); - if let Some(ty) = return_ty.skip_binder().as_type() { - if !ty.is_unit() { - p!(" -> ", print(return_ty)); + p!(")"); + if let Some(ty) = return_ty.skip_binder().as_type() { + if !ty.is_unit() { + p!(" -> ", print(return_ty)); + } } - } - p!(write("{}", if paren_needed { ")" } else { "" })); + p!(write("{}", if paren_needed { ")" } else { "" })); - first = false; - Ok(()) - })?; + first = false; + Ok(()) + }, + )?; } else { // Otherwise, render this like a regular trait. traits.insert( @@ -1110,7 +1181,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { for (trait_pred, assoc_items) in traits { write!(self, "{}", if first { "" } else { " + " })?; - self.wrap_binder(&trait_pred, |trait_pred, cx| { + self.wrap_binder(&trait_pred, WrapBinderMode::ForAll, |trait_pred, cx| { define_scoped_cx!(cx); if trait_pred.polarity == ty::PredicatePolarity::Negative { @@ -1143,7 +1214,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { && assoc .trait_container(tcx) .is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Coroutine)) - && assoc.name == rustc_span::sym::Return + && assoc.opt_name() == Some(rustc_span::sym::Return) { if let ty::Coroutine(_, args) = args.type_at(0).kind() { let return_ty = args.as_coroutine().return_ty(); @@ -1166,7 +1237,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(", "); } - p!(write("{} = ", tcx.associated_item(assoc_item_def_id).name)); + p!(write("{} = ", tcx.associated_item(assoc_item_def_id).name())); match term.unpack() { TermKind::Ty(ty) => p!(print(ty)), @@ -1203,22 +1274,6 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } } - if self.tcx().features().return_type_notation() - && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) = - self.tcx().opt_rpitit_info(def_id) - && let ty::Alias(_, alias_ty) = - self.tcx().fn_sig(fn_def_id).skip_binder().output().skip_binder().kind() - && alias_ty.def_id == def_id - && let generics = self.tcx().generics_of(fn_def_id) - // FIXME(return_type_notation): We only support lifetime params for now. - && generics.own_params.iter().all(|param| matches!(param.kind, ty::GenericParamDefKind::Lifetime)) - { - let num_args = generics.count(); - write!(self, " {{ ")?; - self.print_def_path(fn_def_id, &args[..num_args])?; - write!(self, "(..) }}")?; - } - Ok(()) } @@ -1286,6 +1341,46 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ) } + fn pretty_print_rpitit( + &mut self, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + ) -> Result<(), PrintError> { + let fn_args = if self.tcx().features().return_type_notation() + && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) = + self.tcx().opt_rpitit_info(def_id) + && let ty::Alias(_, alias_ty) = + self.tcx().fn_sig(fn_def_id).skip_binder().output().skip_binder().kind() + && alias_ty.def_id == def_id + && let generics = self.tcx().generics_of(fn_def_id) + // FIXME(return_type_notation): We only support lifetime params for now. + && generics.own_params.iter().all(|param| matches!(param.kind, ty::GenericParamDefKind::Lifetime)) + { + let num_args = generics.count(); + Some((fn_def_id, &args[..num_args])) + } else { + None + }; + + match (fn_args, RTN_MODE.with(|c| c.get())) { + (Some((fn_def_id, fn_args)), RtnMode::ForDiagnostic) => { + self.pretty_print_opaque_impl_type(def_id, args)?; + write!(self, " {{ ")?; + self.print_def_path(fn_def_id, fn_args)?; + write!(self, "(..) }}")?; + } + (Some((fn_def_id, fn_args)), RtnMode::ForSuggestion) => { + self.print_def_path(fn_def_id, fn_args)?; + write!(self, "(..)")?; + } + _ => { + self.pretty_print_opaque_impl_type(def_id, args)?; + } + } + + Ok(()) + } + fn ty_infer_name(&self, _: ty::TyVid) -> Option { None } @@ -1302,7 +1397,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let mut first = true; if let Some(bound_principal) = predicates.principal() { - self.wrap_binder(&bound_principal, |principal, cx| { + self.wrap_binder(&bound_principal, WrapBinderMode::ForAll, |principal, cx| { define_scoped_cx!(cx); p!(print_def_path(principal.def_id, &[])); @@ -1358,9 +1453,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // contain named regions. So we erase and anonymize everything // here to compare the types modulo regions below. let proj = cx.tcx().erase_regions(proj); - let proj = cx.tcx().anonymize_bound_vars(proj); let super_proj = cx.tcx().erase_regions(super_proj); - let super_proj = cx.tcx().anonymize_bound_vars(super_proj); proj == super_proj }); @@ -1927,7 +2020,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let kind = closure.kind_ty().to_opt_closure_kind().unwrap_or(ty::ClosureKind::Fn); write!(self, "impl ")?; - self.wrap_binder(&sig, |sig, cx| { + self.wrap_binder(&sig, WrapBinderMode::ForAll, |sig, cx| { define_scoped_cx!(cx); p!(write("{kind}(")); @@ -2367,22 +2460,23 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> + fn print_in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> where T: Print<'tcx, Self> + TypeFoldable>, { - self.pretty_in_binder(value) + self.pretty_print_in_binder(value) } fn wrap_binder Result<(), PrintError>>( &mut self, value: &ty::Binder<'tcx, T>, + mode: WrapBinderMode, f: C, ) -> Result<(), PrintError> where T: TypeFoldable>, { - self.pretty_wrap_binder(value, f) + self.pretty_wrap_binder(value, mode, f) } fn typed_value( @@ -2431,7 +2525,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { let identify_regions = self.tcx.sess.opts.unstable_opts.identify_regions; - match *region { + match region.kind() { ty::ReEarlyParam(ref data) => data.has_name(), ty::ReLateParam(ty::LateParamRegion { kind, .. }) => kind.is_named(), @@ -2501,12 +2595,10 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { // 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::ReEarlyParam(ref data) => { - if data.name != kw::Empty { - p!(write("{}", data.name)); - return Ok(()); - } + match region.kind() { + ty::ReEarlyParam(data) => { + p!(write("{}", data.name)); + return Ok(()); } ty::ReLateParam(ty::LateParamRegion { kind, .. }) => { if let Some(name) = kind.get_name() { @@ -2593,7 +2685,7 @@ impl<'a, 'tcx> ty::TypeFolder> for RegionFolder<'a, 'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { let name = &mut self.name; - let region = match *r { + let region = match r.kind() { ty::ReBound(db, br) if db >= self.current_index => { *self.region_map.entry(br).or_insert_with(|| name(Some(db), self.current_index, br)) } @@ -2617,7 +2709,7 @@ impl<'a, 'tcx> ty::TypeFolder> for RegionFolder<'a, 'tcx> { } _ => return r, }; - if let ty::ReBound(debruijn1, br) = *region { + if let ty::ReBound(debruijn1, br) = region.kind() { assert_eq!(debruijn1, ty::INNERMOST); ty::Region::new_bound(self.tcx, self.current_index, br) } else { @@ -2632,6 +2724,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { pub fn name_all_regions( &mut self, value: &ty::Binder<'tcx, T>, + mode: WrapBinderMode, ) -> Result<(T, UnordMap>), fmt::Error> where T: TypeFoldable>, @@ -2705,9 +2798,13 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { // anyways. let (new_value, map) = if self.should_print_verbose() { for var in value.bound_vars().iter() { - start_or_continue(self, "for<", ", "); + start_or_continue(self, mode.start_str(), ", "); write!(self, "{var:?}")?; } + // Unconditionally render `unsafe<>`. + if value.bound_vars().is_empty() && mode == WrapBinderMode::Unsafe { + start_or_continue(self, mode.start_str(), ""); + } start_or_continue(self, "", "> "); (value.clone().skip_binder(), UnordMap::default()) } else { @@ -2740,7 +2837,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { (name, ty::BoundRegionKind::Named(CRATE_DEF_ID.to_def_id(), name)) } - ty::BoundRegionKind::Named(def_id, kw::UnderscoreLifetime | kw::Empty) => { + ty::BoundRegionKind::Named(def_id, kw::UnderscoreLifetime) => { let name = next_name(self); if let Some(lt_idx) = lifetime_idx { @@ -2772,8 +2869,9 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { } }; - if !trim_path { - start_or_continue(self, "for<", ", "); + // Unconditionally render `unsafe<>`. + if !trim_path || mode == WrapBinderMode::Unsafe { + start_or_continue(self, mode.start_str(), ", "); do_continue(self, name); } ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { var: br.var, kind }) @@ -2786,9 +2884,12 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { }; let new_value = value.clone().skip_binder().fold_with(&mut folder); let region_map = folder.region_map; - if !trim_path { - start_or_continue(self, "", "> "); + + if mode == WrapBinderMode::Unsafe && region_map.is_empty() { + start_or_continue(self, mode.start_str(), ""); } + start_or_continue(self, "", "> "); + (new_value, region_map) }; @@ -2797,12 +2898,15 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { Ok((new_value, map)) } - pub fn pretty_in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), fmt::Error> + pub fn pretty_print_in_binder( + &mut self, + value: &ty::Binder<'tcx, T>, + ) -> Result<(), fmt::Error> where T: Print<'tcx, Self> + TypeFoldable>, { let old_region_index = self.region_index; - let (new_value, _) = self.name_all_regions(value)?; + let (new_value, _) = self.name_all_regions(value, WrapBinderMode::ForAll)?; new_value.print(self)?; self.region_index = old_region_index; self.binder_depth -= 1; @@ -2812,13 +2916,14 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { pub fn pretty_wrap_binder Result<(), fmt::Error>>( &mut self, value: &ty::Binder<'tcx, T>, + mode: WrapBinderMode, f: C, ) -> Result<(), fmt::Error> where T: TypeFoldable>, { let old_region_index = self.region_index; - let (new_value, _) = self.name_all_regions(value)?; + let (new_value, _) = self.name_all_regions(value, mode)?; f(&new_value, self)?; self.region_index = old_region_index; self.binder_depth -= 1; @@ -2827,7 +2932,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { fn prepare_region_info(&mut self, value: &ty::Binder<'tcx, T>) where - T: TypeVisitable>, + T: TypeFoldable>, { struct RegionNameCollector<'tcx> { used_region_names: FxHashSet, @@ -2843,7 +2948,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { } } - impl<'tcx> ty::visit::TypeVisitor> for RegionNameCollector<'tcx> { + impl<'tcx> ty::TypeVisitor> for RegionNameCollector<'tcx> { fn visit_region(&mut self, r: ty::Region<'tcx>) { trace!("address: {:p}", r.0.0); @@ -2877,7 +2982,7 @@ where T: Print<'tcx, P> + TypeFoldable>, { fn print(&self, cx: &mut P) -> Result<(), PrintError> { - cx.in_binder(self) + cx.print_in_binder(self) } } @@ -3088,23 +3193,23 @@ define_print! { ty::AliasTerm<'tcx> { match self.kind(cx.tcx()) { - ty::AliasTermKind::InherentTy => p!(pretty_print_inherent_projection(*self)), - ty::AliasTermKind::ProjectionTy - | ty::AliasTermKind::WeakTy - | ty::AliasTermKind::OpaqueTy - | ty::AliasTermKind::UnevaluatedConst - | ty::AliasTermKind::ProjectionConst => { - // If we're printing verbosely, or don't want to invoke queries - // (`is_impl_trait_in_trait`), then fall back to printing the def path. - // This is likely what you want if you're debugging the compiler anyways. + ty::AliasTermKind::InherentTy | ty::AliasTermKind::InherentConst => p!(pretty_print_inherent_projection(*self)), + ty::AliasTermKind::ProjectionTy => { if !(cx.should_print_verbose() || with_reduced_queries()) && cx.tcx().is_impl_trait_in_trait(self.def_id) { - return cx.pretty_print_opaque_impl_type(self.def_id, self.args); + p!(pretty_print_rpitit(self.def_id, self.args)) } else { p!(print_def_path(self.def_id, self.args)); } } + ty::AliasTermKind::FreeTy + | ty::AliasTermKind::FreeConst + | ty::AliasTermKind::OpaqueTy + | ty::AliasTermKind::UnevaluatedConst + | ty::AliasTermKind::ProjectionConst => { + p!(print_def_path(self.def_id, self.args)); + } } } @@ -3141,7 +3246,7 @@ define_print! { ty::ClauseKind::ConstArgHasType(ct, ty) => { p!("the constant `", print(ct), "` has type `", print(ty), "`") }, - ty::ClauseKind::WellFormed(arg) => p!(print(arg), " well-formed"), + ty::ClauseKind::WellFormed(term) => p!(print(term), " well-formed"), ty::ClauseKind::ConstEvaluatable(ct) => { p!("the constant `", print(ct), "` can be evaluated") } @@ -3185,7 +3290,7 @@ define_print! { } ty::ExistentialProjection<'tcx> { - let name = cx.tcx().associated_item(self.def_id).name; + let name = cx.tcx().associated_item(self.def_id).name(); // The args don't contain the self ty (as it has been erased) but the corresp. // generics do as the trait always has a self ty param. We need to offset. let args = &self.args[cx.tcx().generics_of(self.def_id).parent_count - 1..]; @@ -3305,20 +3410,18 @@ define_print_and_forward_display! { } 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. + // Iterate all (non-anonymous) local crate items no matter where they are defined. for id in tcx.hir_free_items() { if matches!(tcx.def_kind(id.owner_id), DefKind::Use) { continue; } let item = tcx.hir_item(id); - if item.ident.name == kw::Empty { - continue; - } + let Some(ident) = item.kind.ident() else { continue }; let def_id = item.owner_id.to_def_id(); let ns = tcx.def_kind(def_id).ns().unwrap_or(Namespace::TypeNS); - collect_fn(&item.ident, ns, def_id); + collect_fn(&ident, ns, def_id); } // Now take care of extern crate items. @@ -3392,8 +3495,8 @@ pub fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> DefIdMap { // Once constructed, unique namespace+symbol pairs will have a `Some(_)` entry, while // non-unique pairs will have a `None` entry. - let unique_symbols_rev: &mut FxHashMap<(Namespace, Symbol), Option> = - &mut FxHashMap::default(); + let unique_symbols_rev: &mut FxIndexMap<(Namespace, Symbol), Option> = + &mut FxIndexMap::default(); for symbol_set in tcx.resolutions(()).glob_map.values() { for symbol in symbol_set { @@ -3403,27 +3506,23 @@ pub fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> DefIdMap { } } - 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); - } + for_each_def(tcx, |ident, ns, def_id| match unique_symbols_rev.entry((ns, ident.name)) { + IndexEntry::Occupied(mut v) => match v.get() { + None => {} + Some(existing) => { + if *existing != def_id { + v.insert(None); } - }, - Vacant(v) => { - v.insert(Some(def_id)); } + }, + IndexEntry::Vacant(v) => { + v.insert(Some(def_id)); } }); // Put the symbol from all the unique namespace+symbol pairs into `map`. let mut map: DefIdMap = Default::default(); - for ((_, symbol), opt_def_id) in unique_symbols_rev.drain() { + for ((_, symbol), opt_def_id) in unique_symbols_rev.drain(..) { use std::collections::hash_map::Entry::{Occupied, Vacant}; if let Some(def_id) = opt_def_id { diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index 14a2e5befe696..3e4f7a79d5394 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -1,5 +1,3 @@ -use std::ops::Deref; - use rustc_data_structures::intern::Interned; use rustc_errors::MultiSpan; use rustc_hir::def_id::DefId; @@ -22,17 +20,17 @@ impl<'tcx> rustc_type_ir::inherent::IntoKind for Region<'tcx> { type Kind = RegionKind<'tcx>; fn kind(self) -> RegionKind<'tcx> { - *self + *self.0.0 } } -impl<'tcx> rustc_type_ir::visit::Flags for Region<'tcx> { +impl<'tcx> rustc_type_ir::Flags for Region<'tcx> { fn flags(&self) -> TypeFlags { self.type_flags() } fn outer_exclusive_binder(&self) -> ty::DebruijnIndex { - match **self { + match self.kind() { ty::ReBound(debruijn, _) => debruijn.shifted_in(1), _ => ty::INNERMOST, } @@ -163,7 +161,7 @@ impl<'tcx> Region<'tcx> { pub fn get_name(self) -> Option { if self.has_name() { - match *self { + match self.kind() { ty::ReEarlyParam(ebr) => Some(ebr.name), ty::ReBound(_, br) => br.kind.get_name(), ty::ReLateParam(fr) => fr.kind.get_name(), @@ -185,7 +183,7 @@ impl<'tcx> Region<'tcx> { /// Is this region named by the user? pub fn has_name(self) -> bool { - match *self { + match self.kind() { ty::ReEarlyParam(ebr) => ebr.has_name(), ty::ReBound(_, br) => br.kind.is_named(), ty::ReLateParam(fr) => fr.kind.is_named(), @@ -199,32 +197,32 @@ impl<'tcx> Region<'tcx> { #[inline] pub fn is_error(self) -> bool { - matches!(*self, ty::ReError(_)) + matches!(self.kind(), ty::ReError(_)) } #[inline] pub fn is_static(self) -> bool { - matches!(*self, ty::ReStatic) + matches!(self.kind(), ty::ReStatic) } #[inline] pub fn is_erased(self) -> bool { - matches!(*self, ty::ReErased) + matches!(self.kind(), ty::ReErased) } #[inline] pub fn is_bound(self) -> bool { - matches!(*self, ty::ReBound(..)) + matches!(self.kind(), ty::ReBound(..)) } #[inline] pub fn is_placeholder(self) -> bool { - matches!(*self, ty::RePlaceholder(..)) + matches!(self.kind(), ty::RePlaceholder(..)) } #[inline] pub fn bound_at_or_above_binder(self, index: ty::DebruijnIndex) -> bool { - match *self { + match self.kind() { ty::ReBound(debruijn, _) => debruijn >= index, _ => false, } @@ -233,7 +231,7 @@ impl<'tcx> Region<'tcx> { pub fn type_flags(self) -> TypeFlags { let mut flags = TypeFlags::empty(); - match *self { + match self.kind() { ty::ReVar(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; @@ -275,14 +273,14 @@ impl<'tcx> Region<'tcx> { /// True for free regions other than `'static`. pub fn is_param(self) -> bool { - matches!(*self, ty::ReEarlyParam(_) | ty::ReLateParam(_)) + matches!(self.kind(), ty::ReEarlyParam(_) | ty::ReLateParam(_)) } /// True for free region in the current context. /// /// This is the case for `'static` and param regions. pub fn is_free(self) -> bool { - match *self { + match self.kind() { ty::ReStatic | ty::ReEarlyParam(..) | ty::ReLateParam(..) => true, ty::ReVar(..) | ty::RePlaceholder(..) @@ -319,15 +317,6 @@ impl<'tcx> Region<'tcx> { } } -impl<'tcx> Deref for Region<'tcx> { - type Target = RegionKind<'tcx>; - - #[inline] - fn deref(&self) -> &RegionKind<'tcx> { - self.0.0 - } -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] pub struct EarlyParamRegion { @@ -400,9 +389,7 @@ impl LateParamRegionKind { pub fn is_named(&self) -> bool { match *self { - LateParamRegionKind::Named(_, name) => { - name != kw::UnderscoreLifetime && name != kw::Empty - } + LateParamRegionKind::Named(_, name) => name != kw::UnderscoreLifetime, _ => false, } } @@ -475,7 +462,7 @@ impl core::fmt::Debug for BoundRegion { impl BoundRegionKind { pub fn is_named(&self) -> bool { match *self { - BoundRegionKind::Named(_, name) => name != kw::UnderscoreLifetime && name != kw::Empty, + BoundRegionKind::Named(_, name) => name != kw::UnderscoreLifetime, _ => false, } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 839c1c346a47b..6ad4e5276b253 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -49,25 +49,25 @@ impl<'tcx> Relate> for ty::Pattern<'tcx> { a: Self, b: Self, ) -> RelateResult<'tcx, Self> { + let tcx = relation.cx(); match (&*a, &*b) { ( - &ty::PatternKind::Range { start: start_a, end: end_a, include_end: inc_a }, - &ty::PatternKind::Range { start: start_b, end: end_b, include_end: inc_b }, + &ty::PatternKind::Range { start: start_a, end: end_a }, + &ty::PatternKind::Range { start: start_b, end: end_b }, ) => { - // FIXME(pattern_types): make equal patterns equal (`0..=` is the same as `..=`). - let mut relate_opt_const = |a, b| match (a, b) { - (None, None) => Ok(None), - (Some(a), Some(b)) => relation.relate(a, b).map(Some), - // FIXME(pattern_types): report a better error - _ => Err(TypeError::Mismatch), - }; - let start = relate_opt_const(start_a, start_b)?; - let end = relate_opt_const(end_a, end_b)?; - if inc_a != inc_b { - todo!() + let start = relation.relate(start_a, start_b)?; + let end = relation.relate(end_a, end_b)?; + Ok(tcx.mk_pat(ty::PatternKind::Range { start, end })) + } + (&ty::PatternKind::Or(a), &ty::PatternKind::Or(b)) => { + if a.len() != b.len() { + return Err(TypeError::Mismatch); } - Ok(relation.cx().mk_pat(ty::PatternKind::Range { start, end, include_end: inc_a })) + let v = iter::zip(a, b).map(|(a, b)| relation.relate(a, b)); + let patterns = tcx.mk_patterns_from_iter(v)?; + Ok(tcx.mk_pat(ty::PatternKind::Or(patterns))) } + (ty::PatternKind::Range { .. } | ty::PatternKind::Or(_), _) => Err(TypeError::Mismatch), } } } diff --git a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs deleted file mode 100644 index 568e504b940a6..0000000000000 --- a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs +++ /dev/null @@ -1,95 +0,0 @@ -use rustc_hir::def_id::DefId; - -use crate::ty::{self, ExistentialPredicateStableCmpExt, TyCtxt}; - -impl<'tcx> TyCtxt<'tcx> { - /// Given a `def_id` of a trait or impl method, compute whether that method needs to - /// have an RPITIT shim applied to it for it to be dyn compatible. If so, return the - /// `def_id` of the RPITIT, and also the args of trait method that returns the RPITIT. - /// - /// NOTE that these args are not, in general, the same as than the RPITIT's args. They - /// are a subset of those args, since they do not include the late-bound lifetimes of - /// the RPITIT. Depending on the context, these will need to be dealt with in different - /// ways -- in codegen, it's okay to fill them with ReErased. - pub fn return_position_impl_trait_in_trait_shim_data( - self, - def_id: DefId, - ) -> Option<(DefId, ty::EarlyBinder<'tcx, ty::GenericArgsRef<'tcx>>)> { - let assoc_item = self.opt_associated_item(def_id)?; - - let (trait_item_def_id, opt_impl_def_id) = match assoc_item.container { - ty::AssocItemContainer::Impl => { - (assoc_item.trait_item_def_id?, Some(self.parent(def_id))) - } - ty::AssocItemContainer::Trait => (def_id, None), - }; - - let sig = self.fn_sig(trait_item_def_id); - - // Check if the trait returns an RPITIT. - let ty::Alias(ty::Projection, ty::AliasTy { def_id, .. }) = - *sig.skip_binder().skip_binder().output().kind() - else { - return None; - }; - if !self.is_impl_trait_in_trait(def_id) { - return None; - } - - let args = if let Some(impl_def_id) = opt_impl_def_id { - // Rebase the args from the RPITIT onto the impl trait ref, so we can later - // substitute them with the method args of the *impl* method, since that's - // the instance we're building a vtable shim for. - ty::GenericArgs::identity_for_item(self, trait_item_def_id).rebase_onto( - self, - self.parent(trait_item_def_id), - self.impl_trait_ref(impl_def_id) - .expect("expected impl trait ref from parent of impl item") - .instantiate_identity() - .args, - ) - } else { - // This is when we have a default trait implementation. - ty::GenericArgs::identity_for_item(self, trait_item_def_id) - }; - - Some((def_id, ty::EarlyBinder::bind(args))) - } - - /// Given a `DefId` of an RPITIT and its args, return the existential predicates - /// that corresponds to the RPITIT's bounds with the self type erased. - pub fn item_bounds_to_existential_predicates( - self, - def_id: DefId, - args: ty::GenericArgsRef<'tcx>, - ) -> &'tcx ty::List> { - let mut bounds: Vec<_> = self - .item_self_bounds(def_id) - .iter_instantiated(self, args) - .filter_map(|clause| { - clause - .kind() - .map_bound(|clause| match clause { - ty::ClauseKind::Trait(trait_pred) => Some(ty::ExistentialPredicate::Trait( - ty::ExistentialTraitRef::erase_self_ty(self, trait_pred.trait_ref), - )), - ty::ClauseKind::Projection(projection_pred) => { - Some(ty::ExistentialPredicate::Projection( - ty::ExistentialProjection::erase_self_ty(self, projection_pred), - )) - } - ty::ClauseKind::TypeOutlives(_) => { - // Type outlives bounds don't really turn into anything, - // since we must use an intersection region for the `dyn*`'s - // region anyways. - None - } - _ => unreachable!("unexpected clause in item bounds: {clause:?}"), - }) - .transpose() - }) - .collect(); - bounds.sort_by(|a, b| a.skip_binder().stable_cmp(self, &b.skip_binder())); - self.mk_poly_existential_predicates(&bounds) - } -} diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs index b00c8169a36a7..9bf6e3a759004 100644 --- a/compiler/rustc_middle/src/ty/rvalue_scopes.rs +++ b/compiler/rustc_middle/src/ty/rvalue_scopes.rs @@ -38,7 +38,7 @@ impl RvalueScopes { let mut id = Scope { local_id: expr_id, data: ScopeData::Node }; let mut backwards_incompatible = None; - while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) { + while let Some(&p) = region_scope_tree.parent_map.get(&id) { match p.data { ScopeData::Destruction => { debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]"); diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs index 7f0d82d89fede..561f84192b42b 100644 --- a/compiler/rustc_middle/src/ty/significant_drop_order.rs +++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs @@ -23,9 +23,11 @@ fn true_significant_drop_ty<'tcx>( match key.disambiguated_data.data { rustc_hir::definitions::DefPathData::CrateRoot => { - name_rev.push(tcx.crate_name(did.krate)) + name_rev.push(tcx.crate_name(did.krate)); + } + rustc_hir::definitions::DefPathData::TypeNs(symbol) => { + name_rev.push(symbol); } - rustc_hir::definitions::DefPathData::TypeNs(symbol) => name_rev.push(symbol), _ => return None, } if let Some(parent) = key.parent { @@ -141,25 +143,11 @@ pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { | ty::UnsafeBinder(_) => None, ty::Adt(adt_def, _) => { - let did = adt_def.did(); - let try_local_did_span = |did: DefId| { - if let Some(local) = did.as_local() { - tcx.source_span(local) - } else { - tcx.def_span(did) - } - }; - let dtor = if let Some(dtor) = tcx.adt_destructor(did) { - dtor.did - } else if let Some(dtor) = tcx.adt_async_destructor(did) { - dtor.future + if let Some(dtor) = tcx.adt_destructor(adt_def.did()) { + Some(tcx.def_span(tcx.parent(dtor.did))) } else { - return Some(try_local_did_span(did)); - }; - let def_key = tcx.def_key(dtor); - let Some(parent_index) = def_key.parent else { return Some(try_local_did_span(dtor)) }; - let parent_did = DefId { index: parent_index, krate: dtor.krate }; - Some(try_local_did_span(parent_did)) + Some(tcx.def_span(adt_def.did())) + } } ty::Coroutine(did, _) | ty::CoroutineWitness(did, _) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index db9e9fbc643b2..58f7bc75054bb 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -6,21 +6,19 @@ use std::fmt::{self, Debug}; use rustc_abi::TyAndLayout; -use rustc_ast::InlineAsmTemplatePiece; use rustc_hir::def::Namespace; use rustc_hir::def_id::LocalDefId; -use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_type_ir::ConstKind; -use rustc_type_ir::visit::{VisitorResult, try_visit}; +use rustc_type_ir::{ConstKind, TypeFolder, VisitorResult, try_visit}; use super::print::PrettyPrinter; use super::{GenericArg, GenericArgKind, Pattern, Region}; use crate::mir::PlaceElem; -use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use crate::ty::print::{FmtPrinter, Printer, with_no_trimmed_paths}; -use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; -use crate::ty::{self, InferConst, Lift, Term, TermKind, Ty, TyCtxt}; +use crate::ty::{ + self, FallibleTypeFolder, Lift, Term, TermKind, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitor, +}; impl fmt::Debug for ty::TraitDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -50,7 +48,7 @@ impl<'tcx> fmt::Debug for ty::AdtDef<'tcx> { 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)); + 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) } } @@ -61,6 +59,12 @@ impl<'tcx> fmt::Debug for ty::adjustment::Adjustment<'tcx> { } } +impl<'tcx> fmt::Debug for ty::adjustment::PatAdjustment<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} -> {:?}", self.source, self.kind) + } +} + impl fmt::Debug for ty::BoundRegionKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { @@ -86,15 +90,15 @@ impl fmt::Debug for ty::LateParamRegion { impl fmt::Debug for ty::LateParamRegionKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - ty::LateParamRegionKind::Anon(idx) => write!(f, "BrAnon({idx})"), + ty::LateParamRegionKind::Anon(idx) => write!(f, "LateAnon({idx})"), ty::LateParamRegionKind::Named(did, name) => { if did.is_crate_root() { - write!(f, "BrNamed({name})") + write!(f, "LateNamed({name})") } else { - write!(f, "BrNamed({did:?}, {name})") + write!(f, "LateNamed({did:?}, {name})") } } - ty::LateParamRegionKind::ClosureEnv => write!(f, "BrEnv"), + ty::LateParamRegionKind::ClosureEnv => write!(f, "LateEnv"), } } } @@ -271,6 +275,7 @@ TrivialTypeTraversalImpls! { crate::ty::AssocKind, crate::ty::BoundRegion, crate::ty::BoundVar, + crate::ty::InferConst, crate::ty::Placeholder, crate::ty::Placeholder, crate::ty::Placeholder, @@ -284,6 +289,7 @@ TrivialTypeTraversalImpls! { rustc_hir::def_id::LocalDefId, rustc_hir::HirId, rustc_hir::MatchSource, + rustc_hir::RangeEnd, rustc_span::Ident, rustc_span::Span, rustc_span::Symbol, @@ -336,24 +342,6 @@ impl<'tcx> TypeVisitable> for ty::AdtDef<'tcx> { } } -impl<'tcx> TypeFoldable> for &'tcx ty::List> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - ty::util::fold_list(self, folder, |tcx, v| tcx.mk_poly_existential_predicates(v)) - } -} - -impl<'tcx> TypeFoldable> for &'tcx ty::List> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - ty::util::fold_list(self, folder, |tcx, v| tcx.mk_const_list(v)) - } -} - impl<'tcx> TypeFoldable> for Pattern<'tcx> { fn try_fold_with>>( self, @@ -362,6 +350,11 @@ impl<'tcx> TypeFoldable> for Pattern<'tcx> { let pat = (*self).clone().try_fold_with(folder)?; Ok(if pat == *self { self } else { folder.cx().mk_pat(pat) }) } + + fn fold_with>>(self, folder: &mut F) -> Self { + let pat = (*self).clone().fold_with(folder); + if pat == *self { self } else { folder.cx().mk_pat(pat) } + } } impl<'tcx> TypeVisitable> for Pattern<'tcx> { @@ -377,6 +370,10 @@ impl<'tcx> TypeFoldable> for Ty<'tcx> { ) -> Result { folder.try_fold_ty(self) } + + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_ty(self) + } } impl<'tcx> TypeVisitable> for Ty<'tcx> { @@ -435,6 +432,45 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { Ok(if *self.kind() == kind { self } else { folder.cx().mk_ty_from_kind(kind) }) } + + fn super_fold_with>>(self, folder: &mut F) -> Self { + let kind = match *self.kind() { + ty::RawPtr(ty, mutbl) => ty::RawPtr(ty.fold_with(folder), mutbl), + 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, args) => ty::Adt(tid, args.fold_with(folder)), + ty::Dynamic(trait_ty, region, representation) => { + ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder), representation) + } + ty::Tuple(ts) => ty::Tuple(ts.fold_with(folder)), + ty::FnDef(def_id, args) => ty::FnDef(def_id, args.fold_with(folder)), + ty::FnPtr(sig_tys, hdr) => ty::FnPtr(sig_tys.fold_with(folder), hdr), + ty::UnsafeBinder(f) => ty::UnsafeBinder(f.fold_with(folder)), + ty::Ref(r, ty, mutbl) => ty::Ref(r.fold_with(folder), ty.fold_with(folder), mutbl), + ty::Coroutine(did, args) => ty::Coroutine(did, args.fold_with(folder)), + ty::CoroutineWitness(did, args) => ty::CoroutineWitness(did, args.fold_with(folder)), + ty::Closure(did, args) => ty::Closure(did, args.fold_with(folder)), + ty::CoroutineClosure(did, args) => ty::CoroutineClosure(did, args.fold_with(folder)), + ty::Alias(kind, data) => ty::Alias(kind, data.fold_with(folder)), + ty::Pat(ty, pat) => ty::Pat(ty.fold_with(folder), pat.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.cx().mk_ty_from_kind(kind) } + } } impl<'tcx> TypeSuperVisitable> for Ty<'tcx> { @@ -495,6 +531,10 @@ impl<'tcx> TypeFoldable> for ty::Region<'tcx> { ) -> Result { folder.try_fold_region(self) } + + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_region(self) + } } impl<'tcx> TypeVisitable> for ty::Region<'tcx> { @@ -510,6 +550,10 @@ impl<'tcx> TypeFoldable> for ty::Predicate<'tcx> { ) -> Result { folder.try_fold_predicate(self) } + + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_predicate(self) + } } // FIXME(clause): This is wonky @@ -520,6 +564,10 @@ impl<'tcx> TypeFoldable> for ty::Clause<'tcx> { ) -> Result { Ok(folder.try_fold_predicate(self.as_predicate())?.expect_clause()) } + + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_predicate(self.as_predicate()).expect_clause() + } } impl<'tcx> TypeVisitable> for ty::Predicate<'tcx> { @@ -542,6 +590,11 @@ impl<'tcx> TypeSuperFoldable> for ty::Predicate<'tcx> { let new = self.kind().try_fold_with(folder)?; Ok(folder.cx().reuse_or_mk_predicate(self, new)) } + + fn super_fold_with>>(self, folder: &mut F) -> Self { + let new = self.kind().fold_with(folder); + folder.cx().reuse_or_mk_predicate(self, new) + } } impl<'tcx> TypeSuperVisitable> for ty::Predicate<'tcx> { @@ -562,15 +615,6 @@ impl<'tcx> TypeSuperVisitable> for ty::Clauses<'tcx> { } } -impl<'tcx> TypeFoldable> for ty::Clauses<'tcx> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - ty::util::fold_list(self, folder, |tcx, v| tcx.mk_clauses(v)) - } -} - impl<'tcx> TypeFoldable> for ty::Const<'tcx> { fn try_fold_with>>( self, @@ -578,6 +622,10 @@ impl<'tcx> TypeFoldable> for ty::Const<'tcx> { ) -> Result { folder.try_fold_const(self) } + + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_const(self) + } } impl<'tcx> TypeVisitable> for ty::Const<'tcx> { @@ -605,6 +653,20 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { }; if kind != self.kind() { Ok(folder.cx().mk_ct_from_kind(kind)) } else { Ok(self) } } + + fn super_fold_with>>(self, folder: &mut F) -> Self { + let kind = match self.kind() { + ConstKind::Param(p) => ConstKind::Param(p.fold_with(folder)), + ConstKind::Infer(i) => ConstKind::Infer(i.fold_with(folder)), + ConstKind::Bound(d, b) => ConstKind::Bound(d.fold_with(folder), b.fold_with(folder)), + ConstKind::Placeholder(p) => ConstKind::Placeholder(p.fold_with(folder)), + ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.fold_with(folder)), + ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)), + ConstKind::Error(e) => ConstKind::Error(e.fold_with(folder)), + ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)), + }; + if kind != self.kind() { folder.cx().mk_ct_from_kind(kind) } else { self } + } } impl<'tcx> TypeSuperVisitable> for ty::Const<'tcx> { @@ -638,20 +700,9 @@ impl<'tcx> TypeFoldable> for rustc_span::ErrorGuaranteed { ) -> Result { Ok(self) } -} - -impl<'tcx> TypeFoldable> for InferConst { - fn try_fold_with>>( - self, - _folder: &mut F, - ) -> Result { - Ok(self) - } -} -impl<'tcx> TypeVisitable> for InferConst { - fn visit_with>>(&self, _visitor: &mut V) -> V::Result { - V::Result::output() + fn fold_with>>(self, _folder: &mut F) -> Self { + self } } @@ -682,40 +733,50 @@ impl<'tcx, T: TypeFoldable> + Debug + Clone> TypeFoldable TypeFoldable> for &'tcx [InlineAsmTemplatePiece] { - fn try_fold_with>>( - self, - _folder: &mut F, - ) -> Result { - Ok(self) + fn fold_with>>(self, folder: &mut F) -> Self { + Spanned { node: self.node.fold_with(folder), span: self.span.fold_with(folder) } } } -impl<'tcx> TypeFoldable> for &'tcx [Span] { +impl<'tcx> TypeFoldable> for &'tcx ty::List { fn try_fold_with>>( self, _folder: &mut F, ) -> Result { Ok(self) } -} -impl<'tcx> TypeFoldable> for &'tcx ty::List { - fn try_fold_with>>( - self, - _folder: &mut F, - ) -> Result { - Ok(self) + fn fold_with>>(self, _folder: &mut F) -> Self { + self } } -impl<'tcx> TypeFoldable> for &'tcx ty::List> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - ty::util::fold_list(self, folder, |tcx, v| tcx.mk_place_elems(v)) +macro_rules! list_fold { + ($($ty:ty : $mk:ident),+ $(,)?) => { + $( + impl<'tcx> TypeFoldable> for $ty { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + ty::util::try_fold_list(self, folder, |tcx, v| tcx.$mk(v)) + } + + fn fold_with>>( + self, + folder: &mut F, + ) -> Self { + ty::util::fold_list(self, folder, |tcx, v| tcx.$mk(v)) + } + } + )* } } + +list_fold! { + ty::Clauses<'tcx> : mk_clauses, + &'tcx ty::List> : mk_poly_existential_predicates, + &'tcx ty::List> : mk_place_elems, + &'tcx ty::List> : mk_patterns, +} diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index ce563c5925169..ab1f3d6099fcf 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -4,11 +4,10 @@ use std::assert_matches::debug_assert_matches; use std::borrow::Cow; -use std::iter; use std::ops::{ControlFlow, Range}; use hir::def::{CtorKind, DefKind}; -use rustc_abi::{ExternAbi, FIRST_VARIANT, FieldIdx, VariantIdx}; +use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::LangItem; @@ -16,10 +15,10 @@ use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, extension}; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use rustc_type_ir::TyKind::*; -use rustc_type_ir::visit::TypeVisitableExt; -use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind, elaborate}; +use rustc_type_ir::walk::TypeWalker; +use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind, TypeVisitableExt, elaborate}; use tracing::instrument; -use ty::util::{AsyncDropGlueMorphology, IntTypeExt}; +use ty::util::IntTypeExt; use super::GenericParamDefKind; use crate::infer::canonical::Canonical; @@ -78,8 +77,7 @@ impl<'tcx> ty::CoroutineArgs> { #[inline] fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'tcx>) -> Range { // FIXME requires optimized MIR - FIRST_VARIANT - ..tcx.coroutine_layout(def_id, tcx.types.unit).unwrap().variant_fields.next_index() + FIRST_VARIANT..tcx.coroutine_layout(def_id, self.args).unwrap().variant_fields.next_index() } /// The discriminant for the given variant. Panics if the `variant_index` is @@ -139,10 +137,14 @@ impl<'tcx> ty::CoroutineArgs> { def_id: DefId, tcx: TyCtxt<'tcx>, ) -> impl Iterator>> { - let layout = tcx.coroutine_layout(def_id, self.kind_ty()).unwrap(); + let layout = tcx.coroutine_layout(def_id, self.args).unwrap(); layout.variant_fields.iter().map(move |variant| { variant.iter().map(move |field| { - ty::EarlyBinder::bind(layout.field_tys[*field].ty).instantiate(tcx, self.args) + if tcx.is_async_drop_in_place_coroutine(def_id) { + layout.field_tys[*field].ty + } else { + ty::EarlyBinder::bind(layout.field_tys[*field].ty).instantiate(tcx, self.args) + } }) }) } @@ -489,7 +491,7 @@ impl<'tcx> Ty<'tcx> { (kind, tcx.def_kind(alias_ty.def_id)), (ty::Opaque, DefKind::OpaqueTy) | (ty::Projection | ty::Inherent, DefKind::AssocTy) - | (ty::Weak, DefKind::TyAlias) + | (ty::Free, DefKind::TyAlias) ); Ty::new(tcx, Alias(kind, alias_ty)) } @@ -721,7 +723,10 @@ impl<'tcx> Ty<'tcx> { repr: DynKind, ) -> Ty<'tcx> { if cfg!(debug_assertions) { - let projection_count = obj.projection_bounds().count(); + let projection_count = obj + .projection_bounds() + .filter(|item| !tcx.generics_require_sized_self(item.item_def_id())) + .count(); let expected_count: usize = obj .principal_def_id() .into_iter() @@ -735,7 +740,7 @@ impl<'tcx> Ty<'tcx> { .map(|principal| { tcx.associated_items(principal.def_id()) .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Type) + .filter(|item| item.is_type()) .filter(|item| !item.is_impl_trait_in_trait()) .filter(|item| !tcx.generics_require_sized_self(item.def_id)) .count() @@ -1046,10 +1051,6 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { self.discriminant_ty(interner) } - fn async_destructor_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> { - self.async_destructor_ty(interner) - } - fn has_unsafe_fields(self) -> bool { Ty::has_unsafe_fields(self) } @@ -1259,8 +1260,7 @@ impl<'tcx> Ty<'tcx> { return true; }; alloc.expect_ty().ty_adt_def().is_some_and(|alloc_adt| { - let global_alloc = tcx.require_lang_item(LangItem::GlobalAlloc, None); - alloc_adt.did() == global_alloc + tcx.is_lang_item(alloc_adt.did(), LangItem::GlobalAlloc) }) } _ => false, @@ -1419,6 +1419,34 @@ impl<'tcx> Ty<'tcx> { cf.is_break() } + /// Returns the deepest `async_drop_in_place::{closure}` implementation. + /// + /// `async_drop_in_place::{closure}`, when T is a coroutine, is a proxy-impl + /// to call async drop poll from impl coroutine. + pub fn find_async_drop_impl_coroutine)>( + self, + tcx: TyCtxt<'tcx>, + mut f: F, + ) -> Ty<'tcx> { + assert!(self.is_coroutine()); + let mut cor_ty = self; + let mut ty = cor_ty; + loop { + if let ty::Coroutine(def_id, args) = ty.kind() { + cor_ty = ty; + f(ty); + if tcx.is_async_drop_in_place_coroutine(*def_id) { + ty = args.first().unwrap().expect_ty(); + continue; + } else { + return cor_ty; + } + } else { + return cor_ty; + } + } + } + /// Returns the type and mutability of `*ty`. /// /// The parameter `explicit` indicates if this is an *explicit* dereference. @@ -1442,23 +1470,7 @@ impl<'tcx> Ty<'tcx> { #[tracing::instrument(level = "trace", skip(tcx))] pub fn fn_sig(self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> { - match self.kind() { - FnDef(def_id, args) => tcx.fn_sig(*def_id).instantiate(tcx, args), - FnPtr(sig_tys, hdr) => sig_tys.with(*hdr), - Error(_) => { - // ignore errors (#54954) - Binder::dummy(ty::FnSig { - inputs_and_output: ty::List::empty(), - c_variadic: false, - safety: hir::Safety::Safe, - abi: ExternAbi::Rust, - }) - } - Closure(..) => bug!( - "to get the signature of a closure, use `args.as_closure().sig()` not `fn_sig()`", - ), - _ => bug!("Ty::fn_sig() called on non-fn type: {:?}", self), - } + self.kind().fn_sig(tcx) } #[inline] @@ -1576,125 +1588,6 @@ impl<'tcx> Ty<'tcx> { } } - /// Returns the type of the async destructor of this type. - pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self.async_drop_glue_morphology(tcx) { - AsyncDropGlueMorphology::Noop => { - return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop) - .instantiate_identity(); - } - AsyncDropGlueMorphology::DeferredDropInPlace => { - let drop_in_place = - Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDeferredDropInPlace) - .instantiate(tcx, &[self.into()]); - return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) - .instantiate(tcx, &[drop_in_place.into()]); - } - AsyncDropGlueMorphology::Custom => (), - } - - match *self.kind() { - ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => { - let assoc_items = tcx - .associated_item_def_ids(tcx.require_lang_item(LangItem::AsyncDestruct, None)); - Ty::new_projection(tcx, assoc_items[0], [self]) - } - - ty::Array(elem_ty, _) | ty::Slice(elem_ty) => { - let dtor = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropSlice) - .instantiate(tcx, &[elem_ty.into()]); - Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) - .instantiate(tcx, &[dtor.into()]) - } - - ty::Adt(adt_def, args) if adt_def.is_enum() || adt_def.is_struct() => self - .adt_async_destructor_ty( - tcx, - adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))), - ), - ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys)), - ty::Closure(_, args) => { - self.adt_async_destructor_ty(tcx, iter::once(args.as_closure().upvar_tys())) - } - ty::CoroutineClosure(_, args) => self - .adt_async_destructor_ty(tcx, iter::once(args.as_coroutine_closure().upvar_tys())), - - ty::Adt(adt_def, _) => { - assert!(adt_def.is_union()); - - let surface_drop = self.surface_async_dropper_ty(tcx).unwrap(); - - Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) - .instantiate(tcx, &[surface_drop.into()]) - } - - ty::Bound(..) - | ty::Foreign(_) - | ty::Placeholder(_) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("`async_destructor_ty` applied to unexpected type: {self:?}") - } - - _ => bug!("`async_destructor_ty` is not yet implemented for type: {self:?}"), - } - } - - fn adt_async_destructor_ty(self, tcx: TyCtxt<'tcx>, variants: I) -> Ty<'tcx> - where - I: Iterator + ExactSizeIterator, - I::Item: IntoIterator>, - { - debug_assert_eq!(self.async_drop_glue_morphology(tcx), AsyncDropGlueMorphology::Custom); - - let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer); - let chain = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain); - - let noop = - Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop).instantiate_identity(); - let either = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropEither); - - let variants_dtor = variants - .into_iter() - .map(|variant| { - variant - .into_iter() - .map(|ty| defer.instantiate(tcx, &[ty.into()])) - .reduce(|acc, next| chain.instantiate(tcx, &[acc.into(), next.into()])) - .unwrap_or(noop) - }) - .reduce(|other, matched| { - either.instantiate(tcx, &[other.into(), matched.into(), self.into()]) - }) - .unwrap(); - - let dtor = if let Some(dropper_ty) = self.surface_async_dropper_ty(tcx) { - Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain) - .instantiate(tcx, &[dropper_ty.into(), variants_dtor.into()]) - } else { - variants_dtor - }; - - Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) - .instantiate(tcx, &[dtor.into()]) - } - - fn surface_async_dropper_ty(self, tcx: TyCtxt<'tcx>) -> Option> { - let adt_def = self.ty_adt_def()?; - let dropper = adt_def - .async_destructor(tcx) - .map(|_| LangItem::SurfaceAsyncDropInPlace) - .or_else(|| adt_def.destructor(tcx).map(|_| LangItem::AsyncDropSurfaceDropInPlace))?; - Some(Ty::async_destructor_combinator(tcx, dropper).instantiate(tcx, &[self.into()])) - } - - fn async_destructor_combinator( - tcx: TyCtxt<'tcx>, - lang_item: LangItem, - ) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { - tcx.fn_sig(tcx.require_lang_item(lang_item, None)) - .map_bound(|fn_sig| fn_sig.output().no_bound_vars().unwrap()) - } - /// Returns the type of metadata for (potentially wide) pointers to this type, /// or the struct tail if the metadata type cannot be determined. pub fn ptr_metadata_ty_or_tail( @@ -1790,9 +1683,7 @@ impl<'tcx> Ty<'tcx> { match pointee_ty.ptr_metadata_ty_or_tail(tcx, |x| x) { Ok(metadata_ty) => metadata_ty, Err(tail_ty) => { - let Some(metadata_def_id) = tcx.lang_items().metadata_type() else { - bug!("No metadata_type lang item while looking at {self:?}") - }; + let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); Ty::new_projection(tcx, metadata_def_id, [tail_ty]) } } @@ -1889,14 +1780,14 @@ impl<'tcx> Ty<'tcx> { /// Fast path helper for testing if a type is `Sized`. /// - /// Returning true means the type is known to be sized. Returning - /// `false` means nothing -- could be sized, might not be. + /// Returning true means the type is known to implement `Sized`. Returning `false` means + /// nothing -- could be sized, might not be. /// - /// Note that we could never rely on the fact that a type such as `[_]` is - /// trivially `!Sized` because we could be in a type environment with a - /// bound such as `[_]: Copy`. A function with such a bound obviously never - /// can be called, but that doesn't mean it shouldn't typecheck. This is why - /// this method doesn't return `Option`. + /// Note that we could never rely on the fact that a type such as `[_]` is trivially `!Sized` + /// because we could be in a type environment with a bound such as `[_]: Copy`. A function with + /// such a bound obviously never can be called, but that doesn't mean it shouldn't typecheck. + /// This is why this method doesn't return `Option`. + #[instrument(skip(tcx), level = "debug")] pub fn is_trivially_sized(self, tcx: TyCtxt<'tcx>) -> bool { match self.kind() { ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) @@ -2038,38 +1929,34 @@ impl<'tcx> Ty<'tcx> { } } + pub fn is_async_drop_in_place_coroutine(self, tcx: TyCtxt<'_>) -> bool { + match self.kind() { + ty::Coroutine(def, ..) => tcx.is_async_drop_in_place_coroutine(*def), + _ => false, + } + } + /// Returns `true` when the outermost type cannot be further normalized, /// resolved, or instantiated. This includes all primitive types, but also /// things like ADTs and trait objects, since even if their arguments or /// nested types may be further simplified, the outermost [`TyKind`] or /// type constructor remains the same. pub fn is_known_rigid(self) -> bool { - match self.kind() { - Bool - | Char - | Int(_) - | Uint(_) - | Float(_) - | Adt(_, _) - | Foreign(_) - | Str - | Array(_, _) - | Pat(_, _) - | Slice(_) - | RawPtr(_, _) - | Ref(_, _, _) - | FnDef(_, _) - | FnPtr(..) - | Dynamic(_, _, _) - | Closure(_, _) - | CoroutineClosure(_, _) - | Coroutine(_, _) - | CoroutineWitness(..) - | Never - | Tuple(_) - | UnsafeBinder(_) => true, - Error(_) | Infer(_) | Alias(_, _) | Param(_) | Bound(_, _) | Placeholder(_) => false, - } + self.kind().is_known_rigid() + } + + /// Iterator that walks `self` and any types reachable from + /// `self`, in depth-first order. Note that just walks the types + /// that appear in `self`, it does not descend into the fields of + /// structs or variants. For example: + /// + /// ```text + /// isize => { isize } + /// Foo> => { Foo>, Bar, isize } + /// [isize] => { [isize], isize } + /// ``` + pub fn walk(self) -> TypeWalker> { + TypeWalker::new(self.into()) } } diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 8fa1c569737a0..ea25ce65f7727 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -129,21 +129,6 @@ impl<'tcx> TraitDef { } impl<'tcx> TyCtxt<'tcx> { - /// `trait_def_id` MUST BE the `DefId` of a trait. - pub fn for_each_impl(self, trait_def_id: DefId, mut f: F) { - let impls = self.trait_impls_of(trait_def_id); - - for &impl_def_id in impls.blanket_impls.iter() { - f(impl_def_id); - } - - for v in impls.non_blanket_impls.values() { - for &impl_def_id in v { - f(impl_def_id); - } - } - } - /// Iterate over every impl that could possibly match the self type `self_ty`. /// /// `trait_def_id` MUST BE the `DefId` of a trait. @@ -235,7 +220,7 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait } } - for &impl_def_id in tcx.hir_trait_impls(trait_id) { + for &impl_def_id in tcx.local_trait_impls(trait_id) { let impl_def_id = impl_def_id.to_def_id(); let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity(); diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 7d9c23c05f950..c6a45f8468690 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -77,8 +77,8 @@ pub struct TypeckResults<'tcx> { /// to a form valid in all Editions, either as a lint diagnostic or hard error. rust_2024_migration_desugared_pats: ItemLocalMap, - /// Stores the types which were implicitly dereferenced in pattern binding modes - /// for later usage in THIR lowering. For example, + /// Stores the types which were implicitly dereferenced in pattern binding modes or deref + /// patterns for later usage in THIR lowering. For example, /// /// ``` /// match &&Some(5i32) { @@ -86,11 +86,20 @@ pub struct TypeckResults<'tcx> { /// _ => {}, /// } /// ``` - /// leads to a `vec![&&Option, &Option]`. Empty vectors are not stored. + /// leads to a `vec![&&Option, &Option]` and + /// + /// ``` + /// #![feature(deref_patterns)] + /// match &Box::new(Some(5i32)) { + /// Some(n) => {}, + /// _ => {}, + /// } + /// ``` + /// leads to a `vec![&Box>, Box>]`. Empty vectors are not stored. /// /// See: /// - pat_adjustments: ItemLocalMap>>, + pat_adjustments: ItemLocalMap>>, /// Set of reference patterns that match against a match-ergonomics inserted reference /// (as opposed to against a reference in the scrutinee type). @@ -158,7 +167,7 @@ pub struct TypeckResults<'tcx> { /// We also store the type here, so that the compiler can use it as a hint /// for figuring out hidden types, even if they are only set in dead code /// (which doesn't show up in MIR). - pub concrete_opaque_types: FxIndexMap, ty::OpaqueHiddenType<'tcx>>, + pub concrete_opaque_types: FxIndexMap>, /// Tracks the minimum captures required for a closure; /// see `MinCaptureInformationMap` for more details. @@ -197,12 +206,6 @@ pub struct TypeckResults<'tcx> { /// formatting modified file tests/ui/coroutine/retain-resume-ref.rs pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>, - /// We sometimes treat byte string literals (which are of type `&[u8; N]`) - /// as `&[u8]`, depending on the pattern in which they are used. - /// This hashset records all instances where we behave - /// like this to allow `const_to_pat` to reliably handle this situation. - pub treat_byte_string_as_slice: ItemLocalSet, - /// Contains the data for evaluating the effect of feature `capture_disjoint_fields` /// on closure size. pub closure_size_eval: LocalDefIdMap>, @@ -237,7 +240,6 @@ impl<'tcx> TypeckResults<'tcx> { closure_fake_reads: Default::default(), rvalue_scopes: Default::default(), coroutine_stalled_predicates: Default::default(), - treat_byte_string_as_slice: Default::default(), closure_size_eval: Default::default(), offset_of_data: Default::default(), } @@ -310,7 +312,7 @@ impl<'tcx> TypeckResults<'tcx> { pub fn node_type(&self, id: 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))) + bug!("node_type: no type for node {}", tls::with(|tcx| tcx.hir_id_to_string(id))) }) } @@ -410,11 +412,15 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_binding_modes } } - pub fn pat_adjustments(&self) -> LocalTableInContext<'_, Vec>> { + 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>> { + pub fn pat_adjustments_mut( + &mut self, + ) -> LocalTableInContextMut<'_, Vec>> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } } @@ -469,6 +475,21 @@ impl<'tcx> TypeckResults<'tcx> { has_ref_mut } + /// How should a deref pattern find the place for its inner pattern to match on? + /// + /// In most cases, if the pattern recursively contains a `ref mut` binding, we find the inner + /// pattern's scrutinee by calling `DerefMut::deref_mut`, and otherwise we call `Deref::deref`. + /// However, for boxes we can use a built-in deref instead, which doesn't borrow the scrutinee; + /// in this case, we return `ByRef::No`. + pub fn deref_pat_borrow_mode(&self, pointer_ty: Ty<'_>, inner: &hir::Pat<'_>) -> ByRef { + if pointer_ty.is_box() { + ByRef::No + } else { + let mutable = self.pat_has_ref_mut_binding(inner); + ByRef::Yes(if mutable { Mutability::Mut } else { Mutability::Not }) + } + } + /// For a given closure, returns the iterator of `ty::CapturedPlace`s that are captured /// by the closure. pub fn closure_min_captures_flattened( @@ -554,7 +575,7 @@ fn invalid_hir_id_for_typeck_results(hir_owner: OwnerId, hir_id: HirId) { ty::tls::with(|tcx| { bug!( "node {} cannot be placed in TypeckResults with hir_owner {:?}", - tcx.hir().node_to_string(hir_id), + tcx.hir_id_to_string(hir_id), hir_owner ) }); @@ -695,6 +716,8 @@ pub type CanonicalUserTypeAnnotations<'tcx> = #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct CanonicalUserTypeAnnotation<'tcx> { + #[type_foldable(identity)] + #[type_visitable(ignore)] pub user_ty: Box>, pub span: Span, pub inferred_ty: Ty<'tcx>, @@ -765,7 +788,7 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> { _ => false, }, - GenericArgKind::Lifetime(r) => match *r { + GenericArgKind::Lifetime(r) => match r.kind() { ty::ReBound(debruijn, br) => { // We only allow a `ty::INNERMOST` index in generic parameters. assert_eq!(debruijn, ty::INNERMOST); diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 237aa66f486f8..9676aa4044823 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -2,7 +2,7 @@ use std::{fmt, iter}; -use rustc_abi::{ExternAbi, Float, Integer, IntegerType, Size}; +use rustc_abi::{Float, Integer, IntegerType, Size}; use rustc_apfloat::Float as _; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -23,11 +23,10 @@ use super::TypingEnv; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir; use crate::query::Providers; -use crate::ty::fold::fold_regions; use crate::ty::layout::{FloatExt, IntegerExt}; use crate::ty::{ self, Asyncness, FallibleTypeFolder, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeFoldable, - TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast, + TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast, fold_regions, }; #[derive(Copy, Clone, Debug)] @@ -192,6 +191,18 @@ impl<'tcx> TyCtxt<'tcx> { ty.is_trivially_pure_clone_copy() || self.is_copy_raw(typing_env.as_query_input(ty)) } + /// Checks whether `ty: UseCloned` holds while ignoring region constraints. + /// + /// This function should not be used if there is an `InferCtxt` available. + /// Use `InferCtxt::type_is_copy_modulo_regions` instead. + pub fn type_is_use_cloned_modulo_regions( + self, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + ty.is_trivially_pure_clone_copy() || self.is_use_cloned_raw(typing_env.as_query_input(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. @@ -378,96 +389,83 @@ impl<'tcx> TyCtxt<'tcx> { /// Calculate the destructor of a given type. pub fn calculate_dtor( self, - adt_did: DefId, - validate: impl Fn(Self, DefId) -> Result<(), ErrorGuaranteed>, + adt_did: LocalDefId, + validate: impl Fn(Self, LocalDefId) -> Result<(), ErrorGuaranteed>, ) -> Option { let drop_trait = self.lang_items().drop_trait()?; self.ensure_ok().coherent_trait(drop_trait).ok()?; - let ty = self.type_of(adt_did).instantiate_identity(); let mut dtor_candidate = None; - self.for_each_relevant_impl(drop_trait, ty, |impl_did| { + // `Drop` impls can only be written in the same crate as the adt, and cannot be blanket impls + for &impl_did in self.local_trait_impls(drop_trait) { + let Some(adt_def) = self.type_of(impl_did).skip_binder().ty_adt_def() else { continue }; + if adt_def.did() != adt_did.to_def_id() { + continue; + } + if validate(self, impl_did).is_err() { // Already `ErrorGuaranteed`, no need to delay a span bug here. - return; + continue; } let Some(item_id) = self.associated_item_def_ids(impl_did).first() else { self.dcx() .span_delayed_bug(self.def_span(impl_did), "Drop impl without drop function"); - return; + continue; }; - if let Some((old_item_id, _)) = dtor_candidate { + if self.def_kind(item_id) != DefKind::AssocFn { + self.dcx().span_delayed_bug(self.def_span(item_id), "drop is not a function"); + continue; + } + + if let Some(old_item_id) = dtor_candidate { self.dcx() .struct_span_err(self.def_span(item_id), "multiple drop impls found") .with_span_note(self.def_span(old_item_id), "other impl here") .delay_as_bug(); } - dtor_candidate = Some((*item_id, self.impl_trait_header(impl_did).unwrap().constness)); - }); + dtor_candidate = Some(*item_id); + } - let (did, constness) = dtor_candidate?; - Some(ty::Destructor { did, constness }) + let did = dtor_candidate?; + Some(ty::Destructor { did }) } /// Calculate the async destructor of a given type. pub fn calculate_async_dtor( self, - adt_did: DefId, - validate: impl Fn(Self, DefId) -> Result<(), ErrorGuaranteed>, + adt_did: LocalDefId, + validate: impl Fn(Self, LocalDefId) -> Result<(), ErrorGuaranteed>, ) -> Option { let async_drop_trait = self.lang_items().async_drop_trait()?; self.ensure_ok().coherent_trait(async_drop_trait).ok()?; - let ty = self.type_of(adt_did).instantiate_identity(); let mut dtor_candidate = None; - self.for_each_relevant_impl(async_drop_trait, ty, |impl_did| { + // `AsyncDrop` impls can only be written in the same crate as the adt, and cannot be blanket impls + for &impl_did in self.local_trait_impls(async_drop_trait) { + let Some(adt_def) = self.type_of(impl_did).skip_binder().ty_adt_def() else { continue }; + if adt_def.did() != adt_did.to_def_id() { + continue; + } + if validate(self, impl_did).is_err() { // Already `ErrorGuaranteed`, no need to delay a span bug here. - return; + continue; } - let [future, ctor] = self.associated_item_def_ids(impl_did) else { - self.dcx().span_delayed_bug( - self.def_span(impl_did), - "AsyncDrop impl without async_drop function or Dropper type", - ); - return; - }; - - if let Some((_, _, old_impl_did)) = dtor_candidate { + if let Some(old_impl_did) = dtor_candidate { self.dcx() .struct_span_err(self.def_span(impl_did), "multiple async drop impls found") .with_span_note(self.def_span(old_impl_did), "other impl here") .delay_as_bug(); } - dtor_candidate = Some((*future, *ctor, impl_did)); - }); - - let (future, ctor, _) = dtor_candidate?; - Some(ty::AsyncDestructor { future, ctor }) - } - - /// Returns async drop glue morphology for a definition. To get async drop - /// glue morphology for a type see [`Ty::async_drop_glue_morphology`]. - // - // FIXME: consider making this a query - pub fn async_drop_glue_morphology(self, did: DefId) -> AsyncDropGlueMorphology { - let ty: Ty<'tcx> = self.type_of(did).instantiate_identity(); - - // Async drop glue morphology is an internal detail, so - // using `TypingMode::PostAnalysis` probably should be fine. - let typing_env = ty::TypingEnv::fully_monomorphized(); - if ty.needs_async_drop(self, typing_env) { - AsyncDropGlueMorphology::Custom - } else if ty.needs_drop(self, typing_env) { - AsyncDropGlueMorphology::DeferredDropInPlace - } else { - AsyncDropGlueMorphology::Noop + dtor_candidate = Some(impl_did); } + + Some(ty::AsyncDestructor { impl_did: dtor_candidate?.into() }) } /// Returns the set of types that are required to be alive in @@ -802,7 +800,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Get an English description for the item's kind. pub fn def_kind_descr(self, def_kind: DefKind, def_id: DefId) -> &'static str { match def_kind { - DefKind::AssocFn if self.associated_item(def_id).fn_has_self_parameter => "method", + DefKind::AssocFn if self.associated_item(def_id).is_method() => "method", DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => { match coroutine_kind { hir::CoroutineKind::Desugared( @@ -856,7 +854,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Gets an English article for the [`TyCtxt::def_kind_descr`]. pub fn def_kind_descr_article(self, def_kind: DefKind, def_id: DefId) -> &'static str { match def_kind { - DefKind::AssocFn if self.associated_item(def_id).fn_has_self_parameter => "a", + DefKind::AssocFn if self.associated_item(def_id).is_method() => "a", DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => { match coroutine_kind { hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, ..) => "an", @@ -894,7 +892,7 @@ impl<'tcx> TyCtxt<'tcx> { || self.extern_crate(key).is_some_and(|e| e.is_direct()) } - /// Expand any [weak alias types][weak] contained within the given `value`. + /// Expand any [free alias types][free] contained within the given `value`. /// /// This should be used over other normalization routines in situations where /// it's important not to normalize other alias types and where the predicates @@ -909,19 +907,19 @@ impl<'tcx> TyCtxt<'tcx> { ///
/// This delays a bug on overflow! Therefore you need to be certain that the /// contained types get fully normalized at a later stage. Note that even on - /// overflow all well-behaved weak alias types get expanded correctly, so the + /// overflow all well-behaved free alias types get expanded correctly, so the /// result is still useful. ///
/// - /// [weak]: ty::Weak - pub fn expand_weak_alias_tys>>(self, value: T) -> T { - value.fold_with(&mut WeakAliasTypeExpander { tcx: self, depth: 0 }) + /// [free]: ty::Free + pub fn expand_free_alias_tys>>(self, value: T) -> T { + value.fold_with(&mut FreeAliasTypeExpander { tcx: self, depth: 0 }) } - /// Peel off all [weak alias types] in this type until there are none left. + /// Peel off all [free alias types] in this type until there are none left. /// - /// This only expands weak alias types in “head” / outermost positions. It can - /// be used over [expand_weak_alias_tys] as an optimization in situations where + /// This only expands free alias types in “head” / outermost positions. It can + /// be used over [expand_free_alias_tys] as an optimization in situations where /// one only really cares about the *kind* of the final aliased type but not /// the types the other constituent types alias. /// @@ -930,17 +928,17 @@ impl<'tcx> TyCtxt<'tcx> { /// type gets fully normalized at a later stage. /// /// - /// [weak]: ty::Weak - /// [expand_weak_alias_tys]: Self::expand_weak_alias_tys - pub fn peel_off_weak_alias_tys(self, mut ty: Ty<'tcx>) -> Ty<'tcx> { - let ty::Alias(ty::Weak, _) = ty.kind() else { return ty }; + /// [free]: ty::Free + /// [expand_free_alias_tys]: Self::expand_free_alias_tys + pub fn peel_off_free_alias_tys(self, mut ty: Ty<'tcx>) -> Ty<'tcx> { + let ty::Alias(ty::Free, _) = ty.kind() else { return ty }; let limit = self.recursion_limit(); let mut depth = 0; - while let ty::Alias(ty::Weak, alias) = ty.kind() { + while let ty::Alias(ty::Free, alias) = ty.kind() { if !limit.value_within_limit(depth) { - let guar = self.dcx().delayed_bug("overflow expanding weak alias type"); + let guar = self.dcx().delayed_bug("overflow expanding free alias type"); return Ty::new_error(self, guar); } @@ -968,7 +966,9 @@ impl<'tcx> TyCtxt<'tcx> { } ty::AliasTermKind::OpaqueTy => Some(self.variances_of(def_id)), ty::AliasTermKind::InherentTy - | ty::AliasTermKind::WeakTy + | ty::AliasTermKind::InherentConst + | ty::AliasTermKind::FreeTy + | ty::AliasTermKind::FreeConst | ty::AliasTermKind::UnevaluatedConst | ty::AliasTermKind::ProjectionConst => None, } @@ -1061,25 +1061,25 @@ impl<'tcx> TypeFolder> for OpaqueTypeExpander<'tcx> { } } -struct WeakAliasTypeExpander<'tcx> { +struct FreeAliasTypeExpander<'tcx> { tcx: TyCtxt<'tcx>, depth: usize, } -impl<'tcx> TypeFolder> for WeakAliasTypeExpander<'tcx> { +impl<'tcx> TypeFolder> for FreeAliasTypeExpander<'tcx> { fn cx(&self) -> TyCtxt<'tcx> { self.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if !ty.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) { + if !ty.has_type_flags(ty::TypeFlags::HAS_TY_FREE_ALIAS) { return ty; } - let ty::Alias(ty::Weak, alias) = ty.kind() else { + let ty::Alias(ty::Free, alias) = ty.kind() else { return ty.super_fold_with(self); }; if !self.tcx.recursion_limit().value_within_limit(self.depth) { - let guar = self.tcx.dcx().delayed_bug("overflow expanding weak alias type"); + let guar = self.tcx.dcx().delayed_bug("overflow expanding free alias type"); return Ty::new_error(self.tcx, guar); } @@ -1090,25 +1090,13 @@ impl<'tcx> TypeFolder> for WeakAliasTypeExpander<'tcx> { } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - if !ct.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) { + if !ct.has_type_flags(ty::TypeFlags::HAS_TY_FREE_ALIAS) { return ct; } ct.super_fold_with(self) } } -/// Indicates the form of `AsyncDestruct::Destructor`. Used to simplify async -/// drop glue for types not using async drop. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum AsyncDropGlueMorphology { - /// Async destructor simply does nothing - Noop, - /// Async destructor simply runs `drop_in_place` - DeferredDropInPlace, - /// Async destructor has custom logic - Custom, -} - impl<'tcx> Ty<'tcx> { /// Returns the `Size` for primitive types (bool, uint, int, char, float). pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size { @@ -1278,16 +1266,17 @@ impl<'tcx> Ty<'tcx> { } } - /// Get morphology of the async drop glue, needed for types which do not - /// use async drop. To get async drop glue morphology for a definition see - /// [`TyCtxt::async_drop_glue_morphology`]. Used for `AsyncDestruct::Destructor` - /// type construction. - // - // FIXME: implement optimization to not instantiate a certain morphology of - // async drop glue too soon to allow per type optimizations, see array case - // for more info. Perhaps then remove this method and use `needs_(async_)drop` - // instead. - pub fn async_drop_glue_morphology(self, tcx: TyCtxt<'tcx>) -> AsyncDropGlueMorphology { + /// Checks whether values of this type `T` implement the `AsyncDrop` trait. + pub fn is_async_drop(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool { + !self.is_trivially_not_async_drop() + && tcx.is_async_drop_raw(typing_env.as_query_input(self)) + } + + /// Fast path helper for testing if a type is `AsyncDrop`. + /// + /// Returning true means the type is known to be `!AsyncDrop`. Returning + /// `false` means nothing -- could be `AsyncDrop`, might not be. + fn is_trivially_not_async_drop(self) -> bool { match self.kind() { ty::Int(_) | ty::Uint(_) @@ -1299,46 +1288,26 @@ impl<'tcx> Ty<'tcx> { | ty::Ref(..) | ty::RawPtr(..) | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => AsyncDropGlueMorphology::Noop, - + | ty::Error(_) + | ty::FnPtr(..) => true, // FIXME(unsafe_binders): ty::UnsafeBinder(_) => todo!(), - - ty::Tuple(tys) if tys.is_empty() => AsyncDropGlueMorphology::Noop, - ty::Adt(adt_def, _) if adt_def.is_manually_drop() => AsyncDropGlueMorphology::Noop, - - // Foreign types can never have destructors. - ty::Foreign(_) => AsyncDropGlueMorphology::Noop, - - // FIXME: implement dynamic types async drops - ty::Error(_) | ty::Dynamic(..) => AsyncDropGlueMorphology::DeferredDropInPlace, - - ty::Tuple(_) | ty::Array(_, _) | ty::Slice(_) => { - // Assume worst-case scenario, because we can instantiate async - // destructors in different orders: - // - // 1. Instantiate [T; N] with T = String and N = 0 - // 2. Instantiate <[String; 0] as AsyncDestruct>::Destructor - // - // And viceversa, thus we cannot rely on String not using async - // drop or array having zero (0) elements - AsyncDropGlueMorphology::Custom - } - ty::Pat(ty, _) => ty.async_drop_glue_morphology(tcx), - - ty::Adt(adt_def, _) => tcx.async_drop_glue_morphology(adt_def.did()), - - ty::Closure(did, _) - | ty::CoroutineClosure(did, _) - | ty::Coroutine(did, _) - | ty::CoroutineWitness(did, _) => tcx.async_drop_glue_morphology(*did), - - ty::Alias(..) | ty::Param(_) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(_) => { - // No specifics, but would usually mean forwarding async drop glue - AsyncDropGlueMorphology::Custom + ty::Tuple(fields) => fields.iter().all(Self::is_trivially_not_async_drop), + ty::Pat(elem_ty, _) | ty::Slice(elem_ty) | ty::Array(elem_ty, _) => { + elem_ty.is_trivially_not_async_drop() } + ty::Adt(..) + | ty::Bound(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Dynamic(..) + | ty::Foreign(_) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Infer(_) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(_) => false, } } @@ -1384,9 +1353,6 @@ impl<'tcx> Ty<'tcx> { /// (Note that this implies that if `ty` has an async destructor attached, /// then `needs_async_drop` will definitely return `true` for `ty`.) /// - /// When constructing `AsyncDestruct::Destructor` type, use - /// [`Ty::async_drop_glue_morphology`] instead. - // // FIXME(zetanumbers): Note that this method is used to check eligible types // in unions. #[inline] @@ -1535,55 +1501,6 @@ impl<'tcx> Ty<'tcx> { } } -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` dyn-incompatible. - /// - /// Examples: - /// - /// ```ignore (illustrative) - /// 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
", "DIR"), + opt(Stable, Opt, "o", "", "Write output to FILENAME", ""), + opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in DIR", ""), opt( Stable, Opt, "", "explain", "Provide a detailed explanation of an error message", - "OPT", + "", ), opt(Stable, Flag, "", "test", "Build a test harness", ""), - opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", "TARGET"), - opt(Stable, Multi, "A", "allow", "Set lint allowed", "LINT"), - opt(Stable, Multi, "W", "warn", "Set lint warnings", "LINT"), - opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "LINT"), - opt(Stable, Multi, "D", "deny", "Set lint denied", "LINT"), - opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "LINT"), + opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", ""), + opt(Stable, Multi, "A", "allow", "Set lint allowed", ""), + opt(Stable, Multi, "W", "warn", "Set lint warnings", ""), + opt(Stable, Multi, "", "force-warn", "Set lint force-warn", ""), + opt(Stable, Multi, "D", "deny", "Set lint denied", ""), + opt(Stable, Multi, "F", "forbid", "Set lint forbidden", ""), opt( Stable, Multi, "", "cap-lints", "Set the most restrictive lint level. More restrictive lints are capped at this level", - "LEVEL", + "", ), - opt(Stable, Multi, "C", "codegen", "Set a codegen option", "OPT[=VALUE]"), + opt(Stable, Multi, "C", "codegen", "Set a codegen option", "[=]"), opt(Stable, Flag, "V", "version", "Print version info and exit", ""), opt(Stable, Flag, "v", "verbose", "Use verbose output", ""), ]; @@ -1614,29 +1782,29 @@ pub fn rustc_optgroups() -> Vec { "", "extern", "Specify where an external rust library is located", - "NAME[=PATH]", + "[=]", ), - opt(Stable, Opt, "", "sysroot", "Override the system root", "PATH"), - opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", "FLAG"), + opt(Stable, Opt, "", "sysroot", "Override the system root", ""), + opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", ""), opt( Stable, Opt, "", "error-format", "How errors and other messages are produced", - "human|json|short", + "", ), - opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", "CONFIG"), + opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", ""), opt( Stable, Opt, "", "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", + * auto = colorize, if output goes to a tty (default); + * always = always colorize output; + * never = never colorize output", + "", ), opt( Stable, @@ -1644,7 +1812,7 @@ pub fn rustc_optgroups() -> Vec { "", "diagnostic-width", "Inform rustc of the width of the output so that diagnostics can be truncated to fit", - "WIDTH", + "", ), opt( Stable, @@ -1652,9 +1820,9 @@ pub fn rustc_optgroups() -> Vec { "", "remap-path-prefix", "Remap source names in all output (compiler messages and output files)", - "FROM=TO", + "=", ), - opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "VAR=VALUE"), + opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "="), ]; options.extend(verbose_only.into_iter().map(|mut opt| { opt.is_verbose_help_only = true; @@ -1671,7 +1839,7 @@ pub fn get_cmd_lint_options( let mut lint_opts_with_position = vec![]; let mut describe_lints = false; - for level in [lint::Allow, lint::Warn, lint::ForceWarn(None), lint::Deny, lint::Forbid] { + for level in [lint::Allow, lint::Warn, lint::ForceWarn, lint::Deny, lint::Forbid] { for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) { if lint_name == "help" { describe_lints = true; @@ -1795,7 +1963,7 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json pub fn parse_error_format( early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches, - color: ColorConfig, + color_config: ColorConfig, json_color: ColorConfig, json_rendered: HumanReadableErrorType, ) -> ErrorOutputType { @@ -1805,27 +1973,26 @@ pub fn parse_error_format( // `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_deref() { - None | Some("human") => { - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default, color) - } - Some("human-annotate-rs") => { - ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet, color) - } + None | Some("human") => ErrorOutputType::HumanReadable { color_config, .. }, + Some("human-annotate-rs") => ErrorOutputType::HumanReadable { + kind: HumanReadableErrorType::AnnotateSnippet, + color_config, + }, Some("json") => { ErrorOutputType::Json { pretty: false, json_rendered, color_config: json_color } } Some("pretty-json") => { ErrorOutputType::Json { pretty: true, json_rendered, color_config: json_color } } - Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short, color), - Some("human-unicode") => { - ErrorOutputType::HumanReadable(HumanReadableErrorType::Unicode, color) + Some("short") => { + ErrorOutputType::HumanReadable { kind: HumanReadableErrorType::Short, color_config } } + Some("human-unicode") => ErrorOutputType::HumanReadable { + kind: HumanReadableErrorType::Unicode, + color_config, + }, Some(arg) => { - early_dcx.set_error_format(ErrorOutputType::HumanReadable( - HumanReadableErrorType::Default, - color, - )); + early_dcx.set_error_format(ErrorOutputType::HumanReadable { color_config, .. }); early_dcx.early_fatal(format!( "argument for `--error-format` must be `human`, `human-annotate-rs`, \ `human-unicode`, `json`, `pretty-json` or `short` (instead was `{arg}`)" @@ -1833,7 +2000,7 @@ pub fn parse_error_format( } } } else { - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default, color) + ErrorOutputType::HumanReadable { color_config, .. } }; match error_format { @@ -1888,7 +2055,7 @@ fn check_error_format_stability( } let format = match format { ErrorOutputType::Json { pretty: true, .. } => "pretty-json", - ErrorOutputType::HumanReadable(format, _) => match format { + ErrorOutputType::HumanReadable { kind, .. } => match kind { HumanReadableErrorType::AnnotateSnippet => "human-annotate-rs", HumanReadableErrorType::Unicode => "human-unicode", _ => return, @@ -1999,32 +2166,6 @@ fn collect_print_requests( cg.target_feature = String::new(); } - const PRINT_KINDS: &[(&str, PrintKind)] = &[ - // tidy-alphabetical-start - ("all-target-specs-json", PrintKind::AllTargetSpecs), - ("calling-conventions", PrintKind::CallingConventions), - ("cfg", PrintKind::Cfg), - ("check-cfg", PrintKind::CheckCfg), - ("code-models", PrintKind::CodeModels), - ("crate-name", PrintKind::CrateName), - ("deployment-target", PrintKind::DeploymentTarget), - ("file-names", PrintKind::FileNames), - ("host-tuple", PrintKind::HostTuple), - ("link-args", PrintKind::LinkArgs), - ("native-static-libs", PrintKind::NativeStaticLibs), - ("relocation-models", PrintKind::RelocationModels), - ("split-debuginfo", PrintKind::SplitDebuginfo), - ("stack-protector-strategies", PrintKind::StackProtectorStrategies), - ("sysroot", PrintKind::Sysroot), - ("target-cpus", PrintKind::TargetCPUs), - ("target-features", PrintKind::TargetFeatures), - ("target-libdir", PrintKind::TargetLibdir), - ("target-list", PrintKind::TargetList), - ("target-spec-json", PrintKind::TargetSpec), - ("tls-models", PrintKind::TlsModels), - // tidy-alphabetical-end - ]; - // We disallow reusing the same path in multiple prints, such as `--print // cfg=output.txt --print link-args=output.txt`, because outputs are printed // by disparate pieces of the compiler, and keeping track of which files @@ -2034,49 +2175,14 @@ fn collect_print_requests( prints.extend(matches.opt_strs("print").into_iter().map(|req| { let (req, out) = split_out_file_name(&req); - let kind = match PRINT_KINDS.iter().find(|&&(name, _)| name == req) { - Some((_, PrintKind::TargetSpec)) => { - if unstable_opts.unstable_options { - PrintKind::TargetSpec - } else { - early_dcx.early_fatal( - "the `-Z unstable-options` flag must also be passed to \ - enable the target-spec-json print option", - ); - } - } - Some((_, PrintKind::AllTargetSpecs)) => { - if unstable_opts.unstable_options { - PrintKind::AllTargetSpecs - } else { - early_dcx.early_fatal( - "the `-Z unstable-options` flag must also be passed to \ - enable the all-target-specs-json print option", - ); - } - } - Some((_, PrintKind::CheckCfg)) => { - if unstable_opts.unstable_options { - PrintKind::CheckCfg - } else { - early_dcx.early_fatal( - "the `-Z unstable-options` flag must also be passed to \ - enable the check-cfg print option", - ); - } - } - Some(&(_, print_kind)) => print_kind, - None => { - let prints = - PRINT_KINDS.iter().map(|(name, _)| format!("`{name}`")).collect::>(); - let prints = prints.join(", "); - - let mut diag = - early_dcx.early_struct_fatal(format!("unknown print request: `{req}`")); - #[allow(rustc::diagnostic_outside_of_impl)] - diag.help(format!("valid print requests are: {prints}")); - diag.emit() - } + let kind = if let Some((print_name, print_kind)) = + PRINT_KINDS.iter().find(|&&(name, _)| name == req) + { + check_print_request_stability(early_dcx, unstable_opts, (print_name, *print_kind)); + *print_kind + } else { + let is_nightly = nightly_options::match_is_nightly_build(matches); + emit_unknown_print_request_help(early_dcx, req, is_nightly) }; let out = out.unwrap_or(OutFileName::Stdout); @@ -2095,6 +2201,56 @@ fn collect_print_requests( prints } +fn check_print_request_stability( + early_dcx: &EarlyDiagCtxt, + unstable_opts: &UnstableOptions, + (print_name, print_kind): (&str, PrintKind), +) { + if !is_print_request_stable(print_kind) && !unstable_opts.unstable_options { + early_dcx.early_fatal(format!( + "the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \ + print option" + )); + } +} + +fn is_print_request_stable(print_kind: PrintKind) -> bool { + match print_kind { + PrintKind::AllTargetSpecsJson + | PrintKind::CheckCfg + | PrintKind::CrateRootLintLevels + | PrintKind::SupportedCrateTypes + | PrintKind::TargetSpecJson => false, + _ => true, + } +} + +fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! { + let prints = PRINT_KINDS + .iter() + .filter_map(|(name, kind)| { + // If we're not on nightly, we don't want to print unstable options + if !is_nightly && !is_print_request_stable(*kind) { + None + } else { + Some(format!("`{name}`")) + } + }) + .collect::>(); + let prints = prints.join(", "); + + let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`")); + #[allow(rustc::diagnostic_outside_of_impl)] + diag.help(format!("valid print requests are: {prints}")); + + if req == "lints" { + diag.help(format!("use `-Whelp` to print a list of lints")); + } + + diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information")); + diag.emit() +} + pub fn parse_target_triple(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> TargetTuple { match matches.opt_str("target") { Some(target) if target.ends_with(".json") => { @@ -2180,44 +2336,11 @@ pub fn parse_externs( matches: &getopts::Matches, unstable_opts: &UnstableOptions, ) -> Externs { - fn is_ascii_ident(string: &str) -> bool { - let mut chars = string.chars(); - if let Some(start) = chars.next() - && (start.is_ascii_alphabetic() || start == '_') - { - chars.all(|char| char.is_ascii_alphanumeric() || char == '_') - } else { - false - } - } - let is_unstable_enabled = unstable_opts.unstable_options; let mut externs: BTreeMap = BTreeMap::new(); for arg in matches.opt_strs("extern") { - let (name, path) = match arg.split_once('=') { - None => (arg, None), - Some((name, path)) => (name.to_string(), Some(Path::new(path))), - }; - let (options, name) = match name.split_once(':') { - None => (None, name), - Some((opts, name)) => (Some(opts), name.to_string()), - }; - - if !is_ascii_ident(&name) { - let mut error = early_dcx.early_struct_fatal(format!( - "crate name `{name}` passed to `--extern` is not a valid ASCII identifier" - )); - let adjusted_name = name.replace('-', "_"); - if is_ascii_ident(&adjusted_name) { - #[allow(rustc::diagnostic_outside_of_impl)] // FIXME - error.help(format!( - "consider replacing the dashes with underscores: `{adjusted_name}`" - )); - } - error.emit(); - } - - let path = path.map(|p| CanonicalizedPath::new(p)); + let ExternOpt { crate_name: name, path, options } = + split_extern_opt(early_dcx, unstable_opts, &arg).unwrap_or_else(|e| e.emit()); let entry = externs.entry(name.to_owned()); @@ -2225,6 +2348,7 @@ pub fn parse_externs( let entry = if let Some(path) = path { // --extern prelude_name=some_file.rlib + let path = CanonicalizedPath::new(path); match entry { Entry::Vacant(vacant) => { let files = BTreeSet::from_iter(iter::once(path)); @@ -2622,7 +2746,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M describe_lints, output_types, search_paths, - maybe_sysroot: Some(sysroot), + sysroot, target_triple, test, incremental, @@ -2697,7 +2821,7 @@ pub fn make_crate_type_option() -> RustcOptGroup { "crate-type", "Comma separated list of types of crates for the compiler to emit", - "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]", + "", ) } @@ -2713,7 +2837,13 @@ pub fn parse_crate_types_from_list(list_list: Vec) -> Result CrateType::Cdylib, "bin" => CrateType::Executable, "proc-macro" => CrateType::ProcMacro, - _ => return Err(format!("unknown crate type: `{part}`")), + "sdylib" => CrateType::Sdylib, + _ => { + return Err(format!( + "unknown crate type: `{part}`, expected one of: \ + `lib`, `rlib`, `staticlib`, `dylib`, `cdylib`, `bin`, `proc-macro`", + )); + } }; if !crate_types.contains(&new_part) { crate_types.push(new_part) @@ -2805,6 +2935,7 @@ impl fmt::Display for CrateType { CrateType::Staticlib => "staticlib".fmt(f), CrateType::Cdylib => "cdylib".fmt(f), CrateType::ProcMacro => "proc-macro".fmt(f), + CrateType::Sdylib => "sdylib".fmt(f), } } } @@ -2883,14 +3014,6 @@ impl PpMode { | StableMir => true, } } - pub fn needs_hir(&self) -> bool { - use PpMode::*; - match *self { - Source(_) | AstTree | AstTreeExpanded => false, - - Hir(_) | HirTree | ThirTree | ThirFlat | Mir | MirCFG | StableMir => true, - } - } pub fn needs_analysis(&self) -> bool { use PpMode::*; diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 231ca434962e6..cbfe9e0da6adf 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -142,6 +142,10 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { | (sym::target_has_atomic, Some(_)) | (sym::target_has_atomic_equal_alignment, Some(_)) | (sym::target_has_atomic_load_store, Some(_)) + | (sym::target_has_reliable_f16, None | Some(_)) + | (sym::target_has_reliable_f16_math, None | Some(_)) + | (sym::target_has_reliable_f128, None | Some(_)) + | (sym::target_has_reliable_f128_math, None | Some(_)) | (sym::target_thread_local, None) => disallow(cfg, "--target"), (sym::fmt_debug, None | Some(_)) => disallow(cfg, "-Z fmt-debug"), (sym::emscripten_wasm_eh, None | Some(_)) => disallow(cfg, "-Z emscripten_wasm_eh"), diff --git a/compiler/rustc_session/src/config/externs.rs b/compiler/rustc_session/src/config/externs.rs new file mode 100644 index 0000000000000..1420ee38bf214 --- /dev/null +++ b/compiler/rustc_session/src/config/externs.rs @@ -0,0 +1,79 @@ +//! This module contains code to help parse and manipulate `--extern` arguments. + +use std::path::PathBuf; + +use rustc_errors::{Diag, FatalAbort}; + +use super::UnstableOptions; +use crate::EarlyDiagCtxt; + +#[cfg(test)] +mod tests; + +/// Represents the pieces of an `--extern` argument. +pub(crate) struct ExternOpt { + pub(crate) crate_name: String, + pub(crate) path: Option, + pub(crate) options: Option, +} + +/// Breaks out the major components of an `--extern` argument. +/// +/// The options field will be a string containing comma-separated options that will need further +/// parsing and processing. +pub(crate) fn split_extern_opt<'a>( + early_dcx: &'a EarlyDiagCtxt, + unstable_opts: &UnstableOptions, + extern_opt: &str, +) -> Result> { + let (name, path) = match extern_opt.split_once('=') { + None => (extern_opt.to_string(), None), + Some((name, path)) => (name.to_string(), Some(PathBuf::from(path))), + }; + let (options, crate_name) = match name.split_once(':') { + None => (None, name), + Some((opts, crate_name)) => { + if unstable_opts.namespaced_crates && crate_name.starts_with(':') { + // If the name starts with `:`, we know this was actually something like `foo::bar` and + // not a set of options. We can just use the original name as the crate name. + (None, name) + } else { + (Some(opts.to_string()), crate_name.to_string()) + } + } + }; + + if !valid_crate_name(&crate_name, unstable_opts) { + let mut error = early_dcx.early_struct_fatal(format!( + "crate name `{crate_name}` passed to `--extern` is not a valid ASCII identifier" + )); + let adjusted_name = crate_name.replace('-', "_"); + if is_ascii_ident(&adjusted_name) { + #[allow(rustc::diagnostic_outside_of_impl)] // FIXME + error + .help(format!("consider replacing the dashes with underscores: `{adjusted_name}`")); + } + return Err(error); + } + + Ok(ExternOpt { crate_name, path, options }) +} + +fn valid_crate_name(name: &str, unstable_opts: &UnstableOptions) -> bool { + match name.split_once("::") { + Some((a, b)) if unstable_opts.namespaced_crates => is_ascii_ident(a) && is_ascii_ident(b), + Some(_) => false, + None => is_ascii_ident(name), + } +} + +fn is_ascii_ident(string: &str) -> bool { + let mut chars = string.chars(); + if let Some(start) = chars.next() + && (start.is_ascii_alphabetic() || start == '_') + { + chars.all(|char| char.is_ascii_alphanumeric() || char == '_') + } else { + false + } +} diff --git a/compiler/rustc_session/src/config/externs/tests.rs b/compiler/rustc_session/src/config/externs/tests.rs new file mode 100644 index 0000000000000..6544886951572 --- /dev/null +++ b/compiler/rustc_session/src/config/externs/tests.rs @@ -0,0 +1,92 @@ +use std::path::PathBuf; + +use super::split_extern_opt; +use crate::EarlyDiagCtxt; +use crate::config::UnstableOptions; + +/// Verifies split_extern_opt handles the supported cases. +#[test] +fn test_split_extern_opt() { + let early_dcx = EarlyDiagCtxt::new(<_>::default()); + let unstable_opts = &UnstableOptions::default(); + + let extern_opt = + split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo=libbar.rlib").unwrap(); + assert_eq!(extern_opt.crate_name, "foo"); + assert_eq!(extern_opt.path, Some(PathBuf::from("libbar.rlib"))); + assert_eq!(extern_opt.options, Some("priv,noprelude".to_string())); + + let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo").unwrap(); + assert_eq!(extern_opt.crate_name, "foo"); + assert_eq!(extern_opt.path, None); + assert_eq!(extern_opt.options, Some("priv,noprelude".to_string())); + + let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "foo=libbar.rlib").unwrap(); + assert_eq!(extern_opt.crate_name, "foo"); + assert_eq!(extern_opt.path, Some(PathBuf::from("libbar.rlib"))); + assert_eq!(extern_opt.options, None); + + let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "foo").unwrap(); + assert_eq!(extern_opt.crate_name, "foo"); + assert_eq!(extern_opt.path, None); + assert_eq!(extern_opt.options, None); +} + +/// Tests some invalid cases for split_extern_opt. +#[test] +fn test_split_extern_opt_invalid() { + let early_dcx = EarlyDiagCtxt::new(<_>::default()); + let unstable_opts = &UnstableOptions::default(); + + // too many `:`s + let result = split_extern_opt(&early_dcx, unstable_opts, "priv:noprelude:foo=libbar.rlib"); + assert!(result.is_err()); + let _ = result.map_err(|e| e.cancel()); + + // can't nest externs without the unstable flag + let result = split_extern_opt(&early_dcx, unstable_opts, "noprelude:foo::bar=libbar.rlib"); + assert!(result.is_err()); + let _ = result.map_err(|e| e.cancel()); +} + +/// Tests some cases for split_extern_opt with nested crates like `foo::bar`. +#[test] +fn test_split_extern_opt_nested() { + let early_dcx = EarlyDiagCtxt::new(<_>::default()); + let unstable_opts = &UnstableOptions { namespaced_crates: true, ..Default::default() }; + + let extern_opt = + split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo::bar=libbar.rlib").unwrap(); + assert_eq!(extern_opt.crate_name, "foo::bar"); + assert_eq!(extern_opt.path, Some(PathBuf::from("libbar.rlib"))); + assert_eq!(extern_opt.options, Some("priv,noprelude".to_string())); + + let extern_opt = + split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo::bar").unwrap(); + assert_eq!(extern_opt.crate_name, "foo::bar"); + assert_eq!(extern_opt.path, None); + assert_eq!(extern_opt.options, Some("priv,noprelude".to_string())); + + let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "foo::bar=libbar.rlib").unwrap(); + assert_eq!(extern_opt.crate_name, "foo::bar"); + assert_eq!(extern_opt.path, Some(PathBuf::from("libbar.rlib"))); + assert_eq!(extern_opt.options, None); + + let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "foo::bar").unwrap(); + assert_eq!(extern_opt.crate_name, "foo::bar"); + assert_eq!(extern_opt.path, None); + assert_eq!(extern_opt.options, None); +} + +/// Tests some invalid cases for split_extern_opt with nested crates like `foo::bar`. +#[test] +fn test_split_extern_opt_nested_invalid() { + let early_dcx = EarlyDiagCtxt::new(<_>::default()); + let unstable_opts = &UnstableOptions { namespaced_crates: true, ..Default::default() }; + + // crates can only be nested one deep. + let result = + split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo::bar::baz=libbar.rlib"); + assert!(result.is_err()); + let _ = result.map_err(|e| e.cancel()); +} diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index c8a5c22ad1230..4cfc745dec28a 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -27,6 +27,7 @@ pub struct CrateSource { pub dylib: Option<(PathBuf, PathKind)>, pub rlib: Option<(PathBuf, PathKind)>, pub rmeta: Option<(PathBuf, PathKind)>, + pub sdylib_interface: Option<(PathBuf, PathKind)>, } impl CrateSource { diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 71d8dbe44fed0..bf95014843d23 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -1,4 +1,4 @@ -use std::num::NonZero; +use std::num::{NonZero, ParseIntError}; use rustc_ast::token; use rustc_ast::util::literal::LitError; @@ -14,6 +14,14 @@ use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTuple}; use crate::config::CrateType; use crate::parse::ParseSess; +#[derive(Diagnostic)] +pub(crate) enum AppleDeploymentTarget { + #[diag(session_apple_deployment_target_invalid)] + Invalid { env_var: &'static str, error: ParseIntError }, + #[diag(session_apple_deployment_target_too_low)] + TooLow { env_var: &'static str, version: String, os_min: String }, +} + pub(crate) struct FeatureGateError { pub(crate) span: MultiSpan, pub(crate) explain: DiagMessage, @@ -147,6 +155,10 @@ pub(crate) struct SanitizerCfiGeneralizePointersRequiresCfi; #[diag(session_sanitizer_cfi_normalize_integers_requires_cfi)] pub(crate) struct SanitizerCfiNormalizeIntegersRequiresCfi; +#[derive(Diagnostic)] +#[diag(session_sanitizer_kcfi_arity_requires_kcfi)] +pub(crate) struct SanitizerKcfiArityRequiresKcfi; + #[derive(Diagnostic)] #[diag(session_sanitizer_kcfi_requires_panic_abort)] pub(crate) struct SanitizerKcfiRequiresPanicAbort; diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index ec83761da4a92..92f1bd8ab7367 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -60,66 +60,80 @@ pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf { #[cfg(unix)] fn current_dll_path() -> Result { - use std::ffi::{CStr, OsStr}; - use std::os::unix::prelude::*; - - #[cfg(not(target_os = "aix"))] - unsafe { - let addr = current_dll_path as usize as *mut _; - let mut info = std::mem::zeroed(); - if libc::dladdr(addr, &mut info) == 0 { - return Err("dladdr failed".into()); - } - if info.dli_fname.is_null() { - return Err("dladdr returned null pointer".into()); - } - let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); - let os = OsStr::from_bytes(bytes); - Ok(PathBuf::from(os)) - } - - #[cfg(target_os = "aix")] - unsafe { - // On AIX, the symbol `current_dll_path` references a function descriptor. - // A function descriptor is consisted of (See https://reviews.llvm.org/D62532) - // * The address of the entry point of the function. - // * The TOC base address for the function. - // * The environment pointer. - // The function descriptor is in the data section. - let addr = current_dll_path as u64; - let mut buffer = vec![std::mem::zeroed::(); 64]; - loop { - if libc::loadquery( - libc::L_GETINFO, - buffer.as_mut_ptr() as *mut u8, - (std::mem::size_of::() * buffer.len()) as u32, - ) >= 0 - { - break; - } else { - if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM { - return Err("loadquery failed".into()); + use std::sync::OnceLock; + + // This is somewhat expensive relative to other work when compiling `fn main() {}` as `dladdr` + // needs to iterate over the symbol table of librustc_driver.so until it finds a match. + // As such cache this to avoid recomputing if we try to get the sysroot in multiple places. + static CURRENT_DLL_PATH: OnceLock> = OnceLock::new(); + CURRENT_DLL_PATH + .get_or_init(|| { + use std::ffi::{CStr, OsStr}; + use std::os::unix::prelude::*; + + #[cfg(not(target_os = "aix"))] + unsafe { + let addr = current_dll_path as usize as *mut _; + let mut info = std::mem::zeroed(); + if libc::dladdr(addr, &mut info) == 0 { + return Err("dladdr failed".into()); } - buffer.resize(buffer.len() * 2, std::mem::zeroed::()); - } - } - let mut current = buffer.as_mut_ptr() as *mut libc::ld_info; - loop { - let data_base = (*current).ldinfo_dataorg as u64; - let data_end = data_base + (*current).ldinfo_datasize; - if (data_base..data_end).contains(&addr) { - let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes(); + #[cfg(target_os = "cygwin")] + let fname_ptr = info.dli_fname.as_ptr(); + #[cfg(not(target_os = "cygwin"))] + let fname_ptr = { + assert!(!info.dli_fname.is_null(), "dli_fname cannot be null"); + info.dli_fname + }; + let bytes = CStr::from_ptr(fname_ptr).to_bytes(); let os = OsStr::from_bytes(bytes); - return Ok(PathBuf::from(os)); + Ok(PathBuf::from(os)) } - if (*current).ldinfo_next == 0 { - break; + + #[cfg(target_os = "aix")] + unsafe { + // On AIX, the symbol `current_dll_path` references a function descriptor. + // A function descriptor is consisted of (See https://reviews.llvm.org/D62532) + // * The address of the entry point of the function. + // * The TOC base address for the function. + // * The environment pointer. + // The function descriptor is in the data section. + let addr = current_dll_path as u64; + let mut buffer = vec![std::mem::zeroed::(); 64]; + loop { + if libc::loadquery( + libc::L_GETINFO, + buffer.as_mut_ptr() as *mut u8, + (size_of::() * buffer.len()) as u32, + ) >= 0 + { + break; + } else { + if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM { + return Err("loadquery failed".into()); + } + buffer.resize(buffer.len() * 2, std::mem::zeroed::()); + } + } + let mut current = buffer.as_mut_ptr() as *mut libc::ld_info; + loop { + let data_base = (*current).ldinfo_dataorg as u64; + let data_end = data_base + (*current).ldinfo_datasize; + if (data_base..data_end).contains(&addr) { + let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes(); + let os = OsStr::from_bytes(bytes); + return Ok(PathBuf::from(os)); + } + if (*current).ldinfo_next == 0 { + break; + } + current = (current as *mut i8).offset((*current).ldinfo_next as isize) + as *mut libc::ld_info; + } + return Err(format!("current dll's address {} is not in the load map", addr)); } - current = - (current as *mut i8).offset((*current).ldinfo_next as isize) as *mut libc::ld_info; - } - return Err(format!("current dll's address {} is not in the load map", addr)); - } + }) + .clone() } #[cfg(windows)] @@ -160,8 +174,7 @@ fn current_dll_path() -> Result { pub fn sysroot_candidates() -> SmallVec<[PathBuf; 2]> { let target = crate::config::host_tuple(); - let mut sysroot_candidates: SmallVec<[PathBuf; 2]> = - smallvec![get_or_default_sysroot().expect("Failed finding sysroot")]; + let mut sysroot_candidates: SmallVec<[PathBuf; 2]> = smallvec![get_or_default_sysroot()]; let path = current_dll_path().and_then(|s| try_canonicalize(s).map_err(|e| e.to_string())); if let Ok(dll) = path { // use `parent` twice to chop off the file name and then also the @@ -195,12 +208,12 @@ pub fn sysroot_candidates() -> SmallVec<[PathBuf; 2]> { /// Returns the provided sysroot or calls [`get_or_default_sysroot`] if it's none. /// Panics if [`get_or_default_sysroot`] returns an error. pub fn materialize_sysroot(maybe_sysroot: Option) -> PathBuf { - maybe_sysroot.unwrap_or_else(|| get_or_default_sysroot().expect("Failed finding sysroot")) + maybe_sysroot.unwrap_or_else(|| get_or_default_sysroot()) } /// This function checks if sysroot is found using env::args().next(), and if it /// is not found, finds sysroot from current rustc_driver dll. -pub fn get_or_default_sysroot() -> Result { +pub fn get_or_default_sysroot() -> PathBuf { // Follow symlinks. If the resolved path is relative, make it absolute. fn canonicalize(path: PathBuf) -> PathBuf { let path = try_canonicalize(&path).unwrap_or(path); @@ -255,30 +268,25 @@ pub fn get_or_default_sysroot() -> Result { // binary able to locate Rust libraries in systems using content-addressable // storage (CAS). fn from_env_args_next() -> Option { - match env::args_os().next() { - Some(first_arg) => { - let mut p = PathBuf::from(first_arg); - - // Check if sysroot is found using env::args().next() only if the rustc in argv[0] - // is a symlink (see #79253). We might want to change/remove it to conform with - // https://www.gnu.org/prep/standards/standards.html#Finding-Program-Files in the - // future. - if fs::read_link(&p).is_err() { - // Path is not a symbolic link or does not exist. - return None; - } - - // Pop off `bin/rustc`, obtaining the suspected sysroot. - p.pop(); - p.pop(); - // Look for the target rustlib directory in the suspected sysroot. - let mut rustlib_path = rustc_target::relative_target_rustlib_path(&p, "dummy"); - rustlib_path.pop(); // pop off the dummy target. - rustlib_path.exists().then_some(p) - } - None => None, + let mut p = PathBuf::from(env::args_os().next()?); + + // Check if sysroot is found using env::args().next() only if the rustc in argv[0] + // is a symlink (see #79253). We might want to change/remove it to conform with + // https://www.gnu.org/prep/standards/standards.html#Finding-Program-Files in the + // future. + if fs::read_link(&p).is_err() { + // Path is not a symbolic link or does not exist. + return None; } + + // Pop off `bin/rustc`, obtaining the suspected sysroot. + p.pop(); + p.pop(); + // Look for the target rustlib directory in the suspected sysroot. + let mut rustlib_path = rustc_target::relative_target_rustlib_path(&p, "dummy"); + rustlib_path.pop(); // pop off the dummy target. + rustlib_path.exists().then_some(p) } - Ok(from_env_args_next().unwrap_or(default_from_rustc_driver_dll()?)) + from_env_args_next().unwrap_or(default_from_rustc_driver_dll().expect("Failed finding sysroot")) } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 112adde3740bc..5e5872ee06815 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,12 +1,11 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![feature(default_field_values)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(rustc_attrs)] // To generate CodegenOptionsTargetModifiers and UnstableOptionsTargetModifiers enums // with macro_rules, it is necessary to use recursive mechanic ("Incremental TT Munchers"). #![recursion_limit = "256"] -#![warn(unreachable_pub)] // tidy-alphabetical-end pub mod errors; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 8977365ee73d9..3d9fdcbc7b14c 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -94,45 +94,49 @@ fn tmod_push_impl( tmod_vals: &BTreeMap, tmods: &mut Vec, ) { - tmods.push(TargetModifier { opt, value_name: tmod_vals.get(&opt).cloned().unwrap_or_default() }) + if let Some(v) = tmod_vals.get(&opt) { + tmods.push(TargetModifier { opt, value_name: v.clone() }) + } } macro_rules! tmod_push { - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $mods:expr, $tmod_vals:expr) => { - tmod_push_impl( - OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt_name), - $tmod_vals, - $mods, - ); + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr) => { + if *$opt_expr != $init { + tmod_push_impl( + OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt_name), + $tmod_vals, + $mods, + ); + } }; } macro_rules! gather_tmods { - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [SUBSTRUCT], [TARGET_MODIFIER]) => { compile_error!("SUBSTRUCT can't be target modifier"); }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [UNTRACKED], [TARGET_MODIFIER]) => { - tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals) }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [TRACKED], [TARGET_MODIFIER]) => { - tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals) }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [TRACKED_NO_CRATE_HASH], [TARGET_MODIFIER]) => { - tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals) }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [SUBSTRUCT], []) => { $opt_expr.gather_target_modifiers($mods, $tmod_vals); }; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [UNTRACKED], []) => {{}}; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [TRACKED], []) => {{}}; - ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr, [TRACKED_NO_CRATE_HASH], []) => {{}}; } @@ -333,7 +337,7 @@ top_level_options!( output_types: OutputTypes [TRACKED], search_paths: Vec [UNTRACKED], libs: Vec [TRACKED], - maybe_sysroot: Option [UNTRACKED], + sysroot: PathBuf [UNTRACKED], target_triple: TargetTuple [TRACKED], @@ -389,7 +393,7 @@ top_level_options!( /// /// This directory is what the virtual `/rustc/$hash` is translated back to, /// if Rust was built with path remapping to `/rustc/$hash` enabled - /// (the `rust.remap-debuginfo` option in `config.toml`). + /// (the `rust.remap-debuginfo` option in `bootstrap.toml`). real_rust_source_base_dir: Option [TRACKED_NO_CRATE_HASH], edition: Edition [TRACKED], @@ -474,7 +478,8 @@ macro_rules! tmod_enum { $($pout)* Self::$opt => { let mut parsed : $t = Default::default(); - parse::$parse(&mut parsed, Some($puser_value)); + let val = if $puser_value.is_empty() { None } else { Some($puser_value) }; + parse::$parse(&mut parsed, val); ExtendedTargetModifierInfo { prefix: $prefix.to_string(), name: stringify!($opt).to_string().replace('_', "-"), @@ -569,7 +574,7 @@ macro_rules! options { _tmod_vals: &BTreeMap, ) { $({ - gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, _mods, _tmod_vals, + gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, $init, _mods, _tmod_vals, [$dep_tracking_marker], [$($tmod),*]); })* } @@ -681,10 +686,9 @@ fn build_options( ), } } - if let Some(tmod) = *tmod - && let Some(value) = value - { - target_modifiers.insert(tmod, value.to_string()); + if let Some(tmod) = *tmod { + let v = value.map_or(String::new(), ToOwned::to_owned); + target_modifiers.insert(tmod, v); } } None => early_dcx.early_fatal(format!("unknown {outputname} option: `{key}`")), @@ -707,7 +711,7 @@ mod desc { pub(crate) const parse_list: &str = "a space-separated list of strings"; pub(crate) const parse_list_with_polarity: &str = "a comma-separated list of strings, with elements beginning with + or -"; - pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `LooseTypes`, `Inline`"; + pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`"; pub(crate) const parse_comma_list: &str = "a comma-separated list of strings"; pub(crate) const parse_opt_comma_list: &str = parse_comma_list; pub(crate) const parse_number: &str = "a number"; @@ -1355,6 +1359,9 @@ pub mod parse { "PrintSteps" => AutoDiff::PrintSteps, "PrintModBefore" => AutoDiff::PrintModBefore, "PrintModAfter" => AutoDiff::PrintModAfter, + "PrintModFinal" => AutoDiff::PrintModFinal, + "NoPostopt" => AutoDiff::NoPostopt, + "PrintPasses" => AutoDiff::PrintPasses, "LooseTypes" => AutoDiff::LooseTypes, "Inline" => AutoDiff::Inline, _ => { @@ -1406,6 +1413,7 @@ pub mod parse { "mcdc" => slot.level = CoverageLevel::Mcdc, "no-mir-spans" => slot.no_mir_spans = true, "discard-all-spans-in-codegen" => slot.discard_all_spans_in_codegen = true, + "inject-unused-local-file" => slot.inject_unused_local_file = true, _ => return false, } } @@ -1886,7 +1894,8 @@ pub mod parse { pub(crate) fn parse_wasm_c_abi(slot: &mut WasmCAbi, v: Option<&str>) -> bool { match v { Some("spec") => *slot = WasmCAbi::Spec, - Some("legacy") => *slot = WasmCAbi::Legacy, + // Explicitly setting the `-Z` flag suppresses the lint. + Some("legacy") => *slot = WasmCAbi::Legacy { with_lint: false }, _ => return false, } true @@ -1950,6 +1959,9 @@ options! { "allow the linker to link its default libraries (default: no)"), dlltool: Option = (None, parse_opt_pathbuf, [UNTRACKED], "import library generation tool (ignored except when targeting windows-gnu)"), + #[rustc_lint_opt_deny_field_access("use `Session::dwarf_version` instead of this field")] + dwarf_version: Option = (None, parse_opt_number, [TRACKED], + "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), embed_bitcode: bool = (true, parse_bool, [TRACKED], "emit bitcode in rlibs (default: yes)"), extra_filename: String = (String::new(), parse_string, [UNTRACKED], @@ -2088,6 +2100,9 @@ options! { `=PrintSteps` `=PrintModBefore` `=PrintModAfter` + `=PrintModFinal` + `=PrintPasses`, + `=NoPostopt` `=LooseTypes` `=Inline` Multiple options can be combined with commas."), @@ -2099,6 +2114,8 @@ options! { "emit noalias metadata for box (default: yes)"), branch_protection: Option = (None, parse_branch_protection, [TRACKED], "set options for branch target identification and pointer authentication on AArch64"), + build_sdylib_interface: bool = (false, parse_bool, [UNTRACKED], + "whether the stable interface is being built"), cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED], "instrument control-flow architecture protection"), check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED], @@ -2170,6 +2187,8 @@ options! { them only if an error has not been emitted"), ehcont_guard: bool = (false, parse_bool, [TRACKED], "generate Windows EHCont Guard tables"), + embed_metadata: bool = (true, parse_bool, [TRACKED], + "embed metadata in rlibs and dylibs (default: yes)"), embed_source: bool = (false, parse_bool, [TRACKED], "embed source text in DWARF debug sections (default: no)"), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], @@ -2180,6 +2199,8 @@ options! { "Use WebAssembly error handling for wasm32-unknown-emscripten"), enforce_type_length_limit: bool = (false, parse_bool, [TRACKED], "enforce the type length limit when monomorphizing instances in codegen"), + experimental_default_bounds: bool = (false, parse_bool, [TRACKED], + "enable default bounds for experimental group of auto traits"), export_executable_symbols: bool = (false, parse_bool, [TRACKED], "export symbols from executables, as if they were dynamic libraries"), external_clangrt: bool = (false, parse_bool, [UNTRACKED], @@ -2190,7 +2211,7 @@ options! { fewer_names: Option = (None, parse_opt_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), - fixed_x18: bool = (false, parse_bool, [TRACKED], + fixed_x18: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], "make the x18 register reserved on AArch64 (default: no)"), flatten_format_args: bool = (true, parse_bool, [TRACKED], "flatten nested format_args!() and literals into a simplified format_args!() call \ @@ -2227,7 +2248,8 @@ options! { incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], "verify extended properties for incr. comp. (default: no): - hashes of green query instances - - hash collisions of query keys"), + - hash collisions of query keys + - hash collisions when creating dep-nodes"), inline_llvm: bool = (true, parse_bool, [TRACKED], "enable LLVM inlining (default: yes)"), inline_mir: Option = (None, parse_opt_bool, [TRACKED], @@ -2307,18 +2329,20 @@ options! { mir_include_spans: MirIncludeSpans = (MirIncludeSpans::default(), parse_mir_include_spans, [UNTRACKED], "include extra comments in mir pretty printing, like line numbers and statement indices, \ details about types, etc. (boolean for all passes, 'nll' to enable in NLL MIR only, default: 'nll')"), - mir_keep_place_mention: bool = (false, parse_bool, [TRACKED], - "keep place mention MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ - (default: no)"), #[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")] mir_opt_level: Option = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), + mir_preserve_ub: bool = (false, parse_bool, [TRACKED], + "keep place mention statements and reads in trivial SwitchInt terminators, which are interpreted \ + e.g., by miri; implies -Zmir-opt-level=0 (default: no)"), mir_strip_debuginfo: MirStripDebugInfo = (MirStripDebugInfo::None, parse_mir_strip_debuginfo, [TRACKED], "Whether to remove some of the MIR debug info from methods. Default: None"), move_size_limit: Option = (None, parse_opt_number, [TRACKED], "the size at which the `large_assignments` lint starts to be emitted"), mutable_noalias: bool = (true, parse_bool, [TRACKED], "emit noalias metadata for mutable references (default: yes)"), + namespaced_crates: bool = (false, parse_bool, [TRACKED], + "allow crates to be namespaced by other crates (default: no)"), next_solver: NextSolverConfig = (NextSolverConfig::default(), parse_next_solver_config, [TRACKED], "enable and configure the next generation trait solver used by rustc"), nll_facts: bool = (false, parse_bool, [UNTRACKED], @@ -2385,10 +2409,8 @@ options! { "print codegen statistics (default: no)"), print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "print the LLVM optimization passes being run (default: no)"), - print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], - "print the result of the monomorphization collection pass. \ - Value `lazy` means to use normal collection; `eager` means to collect all items. - Note that this overwrites the effect `-Clink-dead-code` has on collection!"), + print_mono_items: bool = (false, parse_bool, [UNTRACKED], + "print the result of the monomorphization collection pass (default: no)"), 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], @@ -2433,6 +2455,8 @@ written to standard error output)"), "enable normalizing integer types (default: no)"), sanitizer_dataflow_abilist: Vec = (Vec::new(), parse_comma_list, [TRACKED], "additional ABI list files that control how shadow parameters are passed (comma separated)"), + sanitizer_kcfi_arity: Option = (None, parse_opt_bool, [TRACKED], + "enable KCFI arity indicator (default: no)"), sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED], "enable origins tracking in MemorySanitizer"), sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], @@ -2525,7 +2549,7 @@ written to standard error output)"), "for every macro invocation, print its name and arguments (default: no)"), track_diagnostics: bool = (false, parse_bool, [UNTRACKED], "tracks where in rustc a diagnostic was emitted"), - // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved + // Diagnostics are considered side-effects of a query (see `QuerySideEffect`) and are saved // alongside query results and changes to translation options can affect diagnostics - so // translation options should be tracked. translate_additional_ftl: Option = (None, parse_opt_pathbuf, [TRACKED], @@ -2545,6 +2569,9 @@ written to standard error output)"), "in diagnostics, use heuristics to shorten paths referring to items"), tune_cpu: Option = (None, parse_opt_string, [TRACKED], "select processor to schedule for (`rustc --print target-cpus` for details)"), + #[rustc_lint_opt_deny_field_access("use `TyCtxt::use_typing_mode_borrowck` instead of this field")] + typing_mode_borrowck: bool = (false, parse_bool, [TRACKED], + "enable `TypingMode::Borrowck`, changing the way opaque types are handled during MIR borrowck"), #[rustc_lint_opt_deny_field_access("use `Session::ub_checks` instead of this field")] ub_checks: Option = (None, parse_opt_bool, [TRACKED], "emit runtime checks for Undefined Behavior (default: -Cdebug-assertions)"), @@ -2599,7 +2626,7 @@ written to standard error output)"), Requires `-Clto[=[fat,yes]]`"), wasi_exec_model: Option = (None, parse_wasi_exec_model, [TRACKED], "whether to build a wasi command or reactor"), - wasm_c_abi: WasmCAbi = (WasmCAbi::Legacy, parse_wasm_c_abi, [TRACKED], + wasm_c_abi: WasmCAbi = (WasmCAbi::Legacy { with_lint: true }, parse_wasm_c_abi, [TRACKED], "use spec-compliant C ABI for `wasm32-unknown-unknown` (default: legacy)"), write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED], "whether long type names should be written to files instead of being printed in errors"), diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index b37a80274c0cb..cba70b5bd5d17 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -98,12 +98,12 @@ pub fn filename_for_input( CrateType::Rlib => { OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rlib"))) } - CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib => { + CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib | CrateType::Sdylib => { let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix); OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))) } CrateType::Staticlib => { - let (prefix, suffix) = (&sess.target.staticlib_prefix, &sess.target.staticlib_suffix); + let (prefix, suffix) = sess.staticlib_components(false); OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))) } CrateType::Executable => { @@ -167,6 +167,7 @@ pub const CRATE_TYPES: &[(Symbol, CrateType)] = &[ (sym::staticlib, CrateType::Staticlib), (sym::proc_dash_macro, CrateType::ProcMacro), (sym::bin, CrateType::Executable), + (sym::sdylib, CrateType::Sdylib), ]; pub fn categorize_crate_type(s: Symbol) -> Option { @@ -177,9 +178,21 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec, pub target_tlib_path: Arc, pub psess: ParseSess, pub sysroot: PathBuf, @@ -204,6 +205,14 @@ pub struct Session { target_filesearch: FileSearch, host_filesearch: FileSearch, + + /// A random string generated per invocation of rustc. + /// + /// This is prepended to all temporary files so that they do not collide + /// during concurrent invocations of rustc, or past invocations that were + /// preserved with a flag like `-C save-temps`, since these files may be + /// hard linked. + pub invocation_temp: Option, } #[derive(PartialEq, Eq, PartialOrd, Ord)] @@ -362,6 +371,11 @@ impl Session { self.opts.unstable_opts.coverage_options.discard_all_spans_in_codegen } + /// True if testing flag `-Zcoverage-options=inject-unused-local-file` was passed. + pub fn coverage_inject_unused_local_file(&self) -> bool { + self.opts.unstable_opts.coverage_options.inject_unused_local_file + } + pub fn is_sanitizer_cfi_enabled(&self) -> bool { self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) } @@ -382,6 +396,10 @@ impl Session { self.opts.unstable_opts.sanitizer_cfi_normalize_integers == Some(true) } + pub fn is_sanitizer_kcfi_arity_enabled(&self) -> bool { + self.opts.unstable_opts.sanitizer_kcfi_arity == Some(true) + } + pub fn is_sanitizer_kcfi_enabled(&self) -> bool { self.opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI) } @@ -587,6 +605,14 @@ impl Session { .or(self.target.options.default_visibility) .unwrap_or(SymbolVisibility::Interposable) } + + pub fn staticlib_components(&self, verbatim: bool) -> (&str, &str) { + if verbatim { + ("", "") + } else { + (&*self.target.staticlib_prefix, &*self.target.staticlib_suffix) + } + } } // JUSTIFICATION: defn of the suggested wrapper fns @@ -743,7 +769,11 @@ impl Session { /// Returns the DWARF version passed on the CLI or the default for the target. pub fn dwarf_version(&self) -> u32 { - self.opts.unstable_opts.dwarf_version.unwrap_or(self.target.default_dwarf_version) + self.opts + .cg + .dwarf_version + .or(self.opts.unstable_opts.dwarf_version) + .unwrap_or(self.target.default_dwarf_version) } pub fn stack_protector(&self) -> StackProtector { @@ -884,6 +914,45 @@ impl Session { FileNameDisplayPreference::Local } } + + /// Get the deployment target on Apple platforms based on the standard environment variables, + /// or fall back to the minimum version supported by `rustc`. + /// + /// This should be guarded behind `if sess.target.is_like_darwin`. + pub fn apple_deployment_target(&self) -> apple::OSVersion { + let min = apple::OSVersion::minimum_deployment_target(&self.target); + let env_var = apple::deployment_target_env_var(&self.target.os); + + // FIXME(madsmtm): Track changes to this. + if let Ok(deployment_target) = env::var(env_var) { + match apple::OSVersion::from_str(&deployment_target) { + Ok(version) => { + let os_min = apple::OSVersion::os_minimum_deployment_target(&self.target.os); + // It is common that the deployment target is set a bit too low, for example on + // macOS Aarch64 to also target older x86_64. So we only want to warn when variable + // is lower than the minimum OS supported by rustc, not when the variable is lower + // than the minimum for a specific target. + if version < os_min { + self.dcx().emit_warn(errors::AppleDeploymentTarget::TooLow { + env_var, + version: version.fmt_pretty().to_string(), + os_min: os_min.fmt_pretty().to_string(), + }); + } + + // Raise the deployment target to the minimum supported. + version.max(min) + } + Err(error) => { + self.dcx().emit_err(errors::AppleDeploymentTarget::Invalid { env_var, error }); + min + } + } + } else { + // If no deployment target variable is set, default to the minimum found above. + min + } + } } // JUSTIFICATION: part of session construction @@ -913,7 +982,7 @@ fn default_emitter( let source_map = if sopts.unstable_opts.link_only { None } else { Some(source_map) }; match sopts.error_format { - config::ErrorOutputType::HumanReadable(kind, color_config) => { + config::ErrorOutputType::HumanReadable { kind, color_config } => { let short = kind.short(); if let HumanReadableErrorType::AnnotateSnippet = kind { @@ -930,7 +999,6 @@ fn default_emitter( .fluent_bundle(bundle) .sm(source_map) .short_message(short) - .teach(sopts.unstable_opts.teach) .diagnostic_width(sopts.diagnostic_width) .macro_backtrace(macro_backtrace) .track_diagnostics(track_diagnostics) @@ -1043,6 +1111,7 @@ pub fn build_session( let host_triple = config::host_tuple(); let target_triple = sopts.target_triple.tuple(); + // FIXME use host sysroot? let host_tlib_path = Arc::new(SearchPath::from_sysroot_and_triple(&sysroot, host_triple)); let target_tlib_path = if host_triple == target_triple { // Use the same `SearchPath` if host and target triple are identical to avoid unnecessary @@ -1067,11 +1136,16 @@ pub fn build_session( let target_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &target_tlib_path, &target); let host_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &host_tlib_path, &host); + + let invocation_temp = sopts + .incremental + .as_ref() + .map(|_| rng().next_u32().to_base_fixed_len(CASE_INSENSITIVE).to_string()); + let sess = Session { target, host, opts: sopts, - host_tlib_path, target_tlib_path, psess, sysroot, @@ -1091,6 +1165,7 @@ pub fn build_session( expanded_args, target_filesearch, host_filesearch, + invocation_temp, }; validate_commandline_args_with_session_available(&sess); @@ -1205,6 +1280,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } + // KCFI arity indicator requires KCFI. + if sess.is_sanitizer_kcfi_arity_enabled() && !sess.is_sanitizer_kcfi_enabled() { + sess.dcx().emit_err(errors::SanitizerKcfiArityRequiresKcfi); + } + // LLVM CFI pointer generalization requires CFI or KCFI. if sess.is_sanitizer_cfi_generalize_pointers_enabled() { if !(sess.is_sanitizer_cfi_enabled() || sess.is_sanitizer_kcfi_enabled()) { @@ -1256,7 +1336,9 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::BranchProtectionRequiresAArch64); } - if let Some(dwarf_version) = sess.opts.unstable_opts.dwarf_version { + if let Some(dwarf_version) = + sess.opts.cg.dwarf_version.or(sess.opts.unstable_opts.dwarf_version) + { // DWARF 1 is not supported by LLVM and DWARF 6 is not yet finalized. if dwarf_version < 2 || dwarf_version > 5 { sess.dcx().emit_err(errors::UnsupportedDwarfVersion { dwarf_version }); @@ -1430,7 +1512,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box { let fallback_bundle = fallback_fluent_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false); let emitter: Box = match output { - config::ErrorOutputType::HumanReadable(kind, color_config) => { + config::ErrorOutputType::HumanReadable { kind, color_config } => { let short = kind.short(); Box::new( HumanEmitter::new(stderr_destination(color_config), fallback_bundle) diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index 9182789cf0269..e9ddd66b5e8b3 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::OnceLock; use rustc_data_structures::profiling::VerboseTimingGuard; @@ -34,6 +34,7 @@ pub enum NativeLibKind { as_needed: Option, }, /// Dynamic library (e.g. `foo.dll` on Windows) without a corresponding import library. + /// On Linux, it refers to a generated shared library stub. RawDylib, /// A macOS-specific kind of dynamic libraries. Framework { @@ -103,8 +104,8 @@ pub struct CanonicalizedPath { } impl CanonicalizedPath { - pub fn new(path: &Path) -> Self { - Self { original: path.to_owned(), canonicalized: try_canonicalize(path).ok() } + pub fn new(path: PathBuf) -> Self { + Self { canonicalized: try_canonicalize(&path).ok(), original: path } } pub fn canonicalized(&self) -> &PathBuf { @@ -148,14 +149,15 @@ pub fn extra_compiler_flags() -> Option<(Vec, bool)> { arg[a.len()..].to_string() }; let option = content.split_once('=').map(|s| s.0).unwrap_or(&content); - if ICE_REPORT_COMPILER_FLAGS_EXCLUDE.iter().any(|exc| option == *exc) { + if ICE_REPORT_COMPILER_FLAGS_EXCLUDE.contains(&option) { excluded_cargo_defaults = true; } else { result.push(a.to_string()); - match ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.iter().find(|s| option == **s) { - Some(s) => result.push(format!("{s}=[REDACTED]")), - None => result.push(content), - } + result.push(if ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.contains(&option) { + format!("{option}=[REDACTED]") + } else { + content + }); } } } diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml index a11df9a9c9bc9..fc9f411ac3cd4 100644 --- a/compiler/rustc_smir/Cargo.toml +++ b/compiler/rustc_smir/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } -rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } @@ -15,6 +14,6 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } scoped-tls = "1.0" -stable_mir = {path = "../stable_mir" } +serde = { version = "1.0.125", features = [ "derive" ] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs index 2215e2f01ade0..771ff98d58d5a 100644 --- a/compiler/rustc_smir/src/lib.rs +++ b/compiler/rustc_smir/src/lib.rs @@ -15,10 +15,10 @@ )] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end pub mod rustc_internal; -// Make this module private for now since external users should not call these directly. -mod rustc_smir; +pub mod rustc_smir; + +pub mod stable_mir; diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 50cf605ba2a82..6e13b87c41d73 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -21,6 +21,7 @@ use stable_mir::{CrateItem, CrateNum, DefId}; use super::RustcInternal; use crate::rustc_smir::Tables; +use crate::stable_mir; impl RustcInternal for CrateItem { type T<'tcx> = rustc_span::def_id::DefId; @@ -88,10 +89,9 @@ impl RustcInternal for Pattern { type T<'tcx> = rustc_ty::Pattern<'tcx>; fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { tcx.mk_pat(match self { - Pattern::Range { start, end, include_end } => rustc_ty::PatternKind::Range { - start: start.as_ref().map(|c| c.internal(tables, tcx)), - end: end.as_ref().map(|c| c.internal(tables, tcx)), - include_end: *include_end, + Pattern::Range { start, end, include_end: _ } => rustc_ty::PatternKind::Range { + start: start.as_ref().unwrap().internal(tables, tcx), + end: end.as_ref().unwrap().internal(tables, tcx), }, }) } @@ -491,7 +491,6 @@ impl RustcInternal for Abi { Abi::CCmseNonSecureCall => rustc_abi::ExternAbi::CCmseNonSecureCall, Abi::CCmseNonSecureEntry => rustc_abi::ExternAbi::CCmseNonSecureEntry, Abi::System { unwind } => rustc_abi::ExternAbi::System { unwind }, - Abi::RustIntrinsic => rustc_abi::ExternAbi::RustIntrinsic, Abi::RustCall => rustc_abi::ExternAbi::RustCall, Abi::Unadjusted => rustc_abi::ExternAbi::Unadjusted, Abi::RustCold => rustc_abi::ExternAbi::RustCold, diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index ad38ea228bf53..2982a920b03d1 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -18,10 +18,12 @@ use rustc_span::def_id::{CrateNum, DefId}; use scoped_tls::scoped_thread_local; use stable_mir::Error; use stable_mir::abi::Layout; +use stable_mir::compiler_interface::SmirInterface; use stable_mir::ty::IndexedVal; -use crate::rustc_smir::context::TablesWrapper; +use crate::rustc_smir::context::SmirCtxt; use crate::rustc_smir::{Stable, Tables}; +use crate::stable_mir; mod internal; pub mod pretty; @@ -147,6 +149,14 @@ impl<'tcx> Tables<'tcx> { stable_mir::ty::CoroutineWitnessDef(self.create_def_id(did)) } + pub fn assoc_def(&mut self, did: DefId) -> stable_mir::ty::AssocDef { + stable_mir::ty::AssocDef(self.create_def_id(did)) + } + + pub fn opaque_def(&mut self, did: DefId) -> stable_mir::ty::OpaqueDef { + stable_mir::ty::OpaqueDef(self.create_def_id(did)) + } + pub fn prov(&mut self, aid: AllocId) -> stable_mir::ty::Prov { stable_mir::ty::Prov(self.create_alloc_id(aid)) } @@ -187,12 +197,12 @@ pub fn crate_num(item: &stable_mir::Crate) -> CrateNum { // datastructures and stable MIR datastructures scoped_thread_local! (static TLV: Cell<*const ()>); -pub(crate) fn init<'tcx, F, T>(tables: &TablesWrapper<'tcx>, f: F) -> T +pub(crate) fn init<'tcx, F, T>(cx: &SmirCtxt<'tcx>, f: F) -> T where F: FnOnce() -> T, { assert!(!TLV.is_set()); - let ptr = tables as *const _ as *const (); + let ptr = cx as *const _ as *const (); TLV.set(&Cell::new(ptr), || f()) } @@ -203,8 +213,8 @@ pub(crate) fn with_tables(f: impl for<'tcx> FnOnce(&mut Tables<'tcx>) -> R) - TLV.with(|tlv| { let ptr = tlv.get(); assert!(!ptr.is_null()); - let wrapper = ptr as *const TablesWrapper<'_>; - let mut tables = unsafe { (*wrapper).0.borrow_mut() }; + let context = ptr as *const SmirCtxt<'_>; + let mut tables = unsafe { (*context).0.borrow_mut() }; f(&mut *tables) }) } @@ -213,7 +223,7 @@ pub fn run(tcx: TyCtxt<'_>, f: F) -> Result where F: FnOnce() -> T, { - let tables = TablesWrapper(RefCell::new(Tables { + let tables = SmirCtxt(RefCell::new(Tables { tcx, def_ids: IndexMap::default(), alloc_ids: IndexMap::default(), @@ -224,7 +234,12 @@ where mir_consts: IndexMap::default(), layouts: IndexMap::default(), })); - stable_mir::compiler_interface::run(&tables, || init(&tables, f)) + + let interface = SmirInterface { cx: tables }; + + // Pass the `SmirInterface` to compiler_interface::run + // and initialize the rustc-specific TLS with tables. + stable_mir::compiler_interface::run(&interface, || init(&interface.cx, f)) } /// Instantiate and run the compiler with the provided arguments and callback. @@ -235,6 +250,7 @@ where /// ```ignore(needs-extern-crate) /// # extern crate rustc_driver; /// # extern crate rustc_interface; +/// # extern crate rustc_middle; /// # #[macro_use] /// # extern crate rustc_smir; /// # extern crate stable_mir; @@ -246,7 +262,7 @@ where /// // Your code goes in here. /// # ControlFlow::Continue(()) /// } -/// # let args = vec!["--verbose".to_string()]; +/// # let args = &["--verbose".to_string()]; /// let result = run!(args, analyze_code); /// # assert_eq!(result, Err(CompilerError::Skipped)) /// # } @@ -255,6 +271,7 @@ where /// ```ignore(needs-extern-crate) /// # extern crate rustc_driver; /// # extern crate rustc_interface; +/// # extern crate rustc_middle; /// # #[macro_use] /// # extern crate rustc_smir; /// # extern crate stable_mir; @@ -267,7 +284,7 @@ where /// // Your code goes in here. /// # ControlFlow::Continue(()) /// } -/// # let args = vec!["--verbose".to_string()]; +/// # let args = &["--verbose".to_string()]; /// # let extra_args = vec![]; /// let result = run!(args, || analyze_code(extra_args)); /// # assert_eq!(result, Err(CompilerError::Skipped)) @@ -319,6 +336,7 @@ macro_rules! run_driver { use rustc_driver::{Callbacks, Compilation, run_compiler}; use rustc_middle::ty::TyCtxt; use rustc_interface::interface; + use rustc_smir::rustc_internal; use stable_mir::CompilerError; use std::ops::ControlFlow; @@ -328,7 +346,6 @@ macro_rules! run_driver { C: Send, F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow + Send, { - args: Vec, callback: Option, result: Option>, } @@ -340,14 +357,14 @@ macro_rules! run_driver { F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow + Send, { /// Creates a new `StableMir` instance, with given test_function and arguments. - pub fn new(args: Vec, callback: F) -> Self { - StableMir { args, callback: Some(callback), result: None } + pub fn new(callback: F) -> Self { + StableMir { callback: Some(callback), result: None } } /// Runs the compiler against given target and tests it with `test_function` - pub fn run(&mut self) -> Result> { + pub fn run(&mut self, args: &[String]) -> Result> { let compiler_result = rustc_driver::catch_fatal_errors(|| -> interface::Result::<()> { - run_compiler(&self.args.clone(), self); + run_compiler(&args, self); Ok(()) }); match (compiler_result, self.result.take()) { @@ -397,7 +414,7 @@ macro_rules! run_driver { } } - StableMir::new($args, $callback).run() + StableMir::new($callback).run($args) }}; } diff --git a/compiler/rustc_smir/src/rustc_internal/pretty.rs b/compiler/rustc_smir/src/rustc_internal/pretty.rs index b752ad71ecc64..0710c18746afd 100644 --- a/compiler/rustc_smir/src/rustc_internal/pretty.rs +++ b/compiler/rustc_smir/src/rustc_internal/pretty.rs @@ -3,6 +3,7 @@ use std::io; use rustc_middle::ty::TyCtxt; use super::run; +use crate::stable_mir; pub fn write_smir_pretty<'tcx, W: io::Write>(tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()> { writeln!( diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs index 52c5b425c14f6..9cb89634c52e5 100644 --- a/compiler/rustc_smir/src/rustc_smir/alloc.rs +++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs @@ -6,6 +6,7 @@ use stable_mir::mir::Mutability; use stable_mir::ty::{Allocation, ProvenanceMap}; use crate::rustc_smir::{Stable, Tables}; +use crate::stable_mir; /// Creates new empty `Allocation` from given `Align`. fn new_empty_allocation(align: Align) -> Allocation { @@ -27,7 +28,7 @@ pub(crate) fn new_allocation<'tcx>( tables: &mut Tables<'tcx>, ) -> Allocation { try_new_allocation(ty, const_value, tables) - .expect(&format!("Failed to convert: {const_value:?} to {ty:?}")) + .unwrap_or_else(|_| panic!("Failed to convert: {const_value:?} to {ty:?}")) } #[allow(rustc::usage_of_qualified_ty)] @@ -36,39 +37,30 @@ pub(crate) fn try_new_allocation<'tcx>( const_value: ConstValue<'tcx>, tables: &mut Tables<'tcx>, ) -> Result { + let layout = tables + .tcx + .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) + .map_err(|e| e.stable(tables))?; Ok(match const_value { ConstValue::Scalar(scalar) => { let size = scalar.size(); - let align = tables - .tcx - .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) - .map_err(|e| e.stable(tables))? - .align; - let mut allocation = - rustc_middle::mir::interpret::Allocation::new(size, align.abi, AllocInit::Uninit); + let mut allocation = rustc_middle::mir::interpret::Allocation::new( + size, + layout.align.abi, + AllocInit::Uninit, + ); allocation .write_scalar(&tables.tcx, alloc_range(Size::ZERO, size), scalar) .map_err(|e| e.stable(tables))?; allocation.stable(tables) } - ConstValue::ZeroSized => { - let align = tables - .tcx - .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) - .map_err(|e| e.stable(tables))? - .align; - new_empty_allocation(align.abi) - } + ConstValue::ZeroSized => new_empty_allocation(layout.align.abi), ConstValue::Slice { data, meta } => { let alloc_id = tables.tcx.reserve_and_set_memory_alloc(data); let ptr = Pointer::new(alloc_id.into(), Size::ZERO); let scalar_ptr = rustc_middle::mir::interpret::Scalar::from_pointer(ptr, &tables.tcx); let scalar_meta = rustc_middle::mir::interpret::Scalar::from_target_usize(meta, &tables.tcx); - let layout = tables - .tcx - .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) - .map_err(|e| e.stable(tables))?; let mut allocation = rustc_middle::mir::interpret::Allocation::new( layout.size, layout.align.abi, @@ -92,12 +84,7 @@ pub(crate) fn try_new_allocation<'tcx>( } ConstValue::Indirect { alloc_id, offset } => { let alloc = tables.tcx.global_alloc(alloc_id).unwrap_memory(); - let ty_size = tables - .tcx - .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) - .map_err(|e| e.stable(tables))? - .size; - allocation_filter(&alloc.0, alloc_range(offset, ty_size), tables) + allocation_filter(&alloc.0, alloc_range(offset, layout.size), tables) } }) } diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index 2eb0cea0e8536..40e6d21c06378 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -10,6 +10,7 @@ use rustc_middle::mir::visit::MutVisitor; use rustc_middle::ty::{self, TyCtxt}; use crate::rustc_smir::{Stable, Tables}; +use crate::stable_mir; /// Builds a monomorphic body for a given instance. pub(crate) struct BodyBuilder<'tcx> { @@ -21,7 +22,7 @@ impl<'tcx> BodyBuilder<'tcx> { pub(crate) fn new(tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>) -> Self { let instance = match instance.def { // To get the fallback body of an intrinsic, we need to convert it to an item. - ty::InstanceKind::Intrinsic(def_id) => ty::Instance::new(def_id, instance.args), + ty::InstanceKind::Intrinsic(def_id) => ty::Instance::new_raw(def_id, instance.args), _ => instance, }; BodyBuilder { tcx, instance } diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index acd3b75835105..bac5c9066f1f6 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -1,7 +1,4 @@ -//! Implementation of `[stable_mir::compiler_interface::Context]` trait. -//! -//! This trait is currently the main interface between the Rust compiler, -//! and the `stable_mir` crate. +//! Implementation of StableMIR Context. #![allow(rustc::usage_of_qualified_ty)] @@ -20,7 +17,6 @@ use rustc_middle::ty::{ use rustc_middle::{mir, ty}; use rustc_span::def_id::LOCAL_CRATE; use stable_mir::abi::{FnAbi, Layout, LayoutShape}; -use stable_mir::compiler_interface::Context; use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::{InstanceDef, StaticDef}; use stable_mir::mir::{BinOp, Body, Place, UnOp}; @@ -35,9 +31,16 @@ use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, I use crate::rustc_internal::RustcInternal; use crate::rustc_smir::builder::BodyBuilder; use crate::rustc_smir::{Stable, Tables, alloc, filter_def_ids, new_item_kind, smir_crate}; +use crate::stable_mir; -impl<'tcx> Context for TablesWrapper<'tcx> { - fn target_info(&self) -> MachineInfo { +/// Provides direct access to rustc's internal queries. +/// +/// The [`crate::stable_mir::compiler_interface::SmirInterface`] must go through +/// this context to obtain rustc-level information. +pub struct SmirCtxt<'tcx>(pub RefCell>); + +impl<'tcx> SmirCtxt<'tcx> { + pub fn target_info(&self) -> MachineInfo { let mut tables = self.0.borrow_mut(); MachineInfo { endian: tables.tcx.data_layout.endian.stable(&mut *tables), @@ -47,31 +50,35 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn entry_fn(&self) -> Option { + pub fn entry_fn(&self) -> Option { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; Some(tables.crate_item(tcx.entry_fn(())?.0)) } - fn all_local_items(&self) -> stable_mir::CrateItems { + /// Retrieve all items of the local crate that have a MIR associated with them. + pub fn all_local_items(&self) -> stable_mir::CrateItems { let mut tables = self.0.borrow_mut(); tables.tcx.mir_keys(()).iter().map(|item| tables.crate_item(item.to_def_id())).collect() } - fn mir_body(&self, item: stable_mir::DefId) -> stable_mir::mir::Body { + /// Retrieve the body of a function. + /// This function will panic if the body is not available. + pub fn mir_body(&self, item: stable_mir::DefId) -> stable_mir::mir::Body { let mut tables = self.0.borrow_mut(); let def_id = tables[item]; tables.tcx.instance_mir(rustc_middle::ty::InstanceKind::Item(def_id)).stable(&mut tables) } - fn has_body(&self, def: DefId) -> bool { + /// Check whether the body of a function is available. + pub fn has_body(&self, def: DefId) -> bool { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.internal(&mut *tables, tcx); tables.item_has_body(def_id) } - fn foreign_modules(&self, crate_num: CrateNum) -> Vec { + pub fn foreign_modules(&self, crate_num: CrateNum) -> Vec { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; tcx.foreign_modules(crate_num.internal(&mut *tables, tcx)) @@ -80,21 +87,23 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn crate_functions(&self, crate_num: CrateNum) -> Vec { + /// Retrieve all functions defined in this crate. + pub fn crate_functions(&self, crate_num: CrateNum) -> Vec { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let krate = crate_num.internal(&mut *tables, tcx); filter_def_ids(tcx, krate, |def_id| tables.to_fn_def(def_id)) } - fn crate_statics(&self, crate_num: CrateNum) -> Vec { + /// Retrieve all static items defined in this crate. + pub fn crate_statics(&self, crate_num: CrateNum) -> Vec { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let krate = crate_num.internal(&mut *tables, tcx); filter_def_ids(tcx, krate, |def_id| tables.to_static(def_id)) } - fn foreign_module( + pub fn foreign_module( &self, mod_def: stable_mir::ty::ForeignModuleDef, ) -> stable_mir::ty::ForeignModule { @@ -104,7 +113,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { mod_def.stable(&mut *tables) } - fn foreign_items(&self, mod_def: stable_mir::ty::ForeignModuleDef) -> Vec { + pub fn foreign_items(&self, mod_def: stable_mir::ty::ForeignModuleDef) -> Vec { let mut tables = self.0.borrow_mut(); let def_id = tables[mod_def.def_id()]; tables @@ -118,12 +127,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn all_trait_decls(&self) -> stable_mir::TraitDecls { + pub fn all_trait_decls(&self) -> stable_mir::TraitDecls { let mut tables = self.0.borrow_mut(); tables.tcx.all_traits().map(|trait_def_id| tables.trait_def(trait_def_id)).collect() } - fn trait_decls(&self, crate_num: CrateNum) -> stable_mir::TraitDecls { + pub fn trait_decls(&self, crate_num: CrateNum) -> stable_mir::TraitDecls { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; tcx.traits(crate_num.internal(&mut *tables, tcx)) @@ -132,14 +141,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn trait_decl(&self, trait_def: &stable_mir::ty::TraitDef) -> stable_mir::ty::TraitDecl { + pub fn trait_decl(&self, trait_def: &stable_mir::ty::TraitDef) -> stable_mir::ty::TraitDecl { let mut tables = self.0.borrow_mut(); let def_id = tables[trait_def.0]; let trait_def = tables.tcx.trait_def(def_id); trait_def.stable(&mut *tables) } - fn all_trait_impls(&self) -> stable_mir::ImplTraitDecls { + pub fn all_trait_impls(&self) -> stable_mir::ImplTraitDecls { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; iter::once(LOCAL_CRATE) @@ -149,7 +158,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn trait_impls(&self, crate_num: CrateNum) -> stable_mir::ImplTraitDecls { + pub fn trait_impls(&self, crate_num: CrateNum) -> stable_mir::ImplTraitDecls { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; tcx.trait_impls_in_crate(crate_num.internal(&mut *tables, tcx)) @@ -158,21 +167,21 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn trait_impl(&self, impl_def: &stable_mir::ty::ImplDef) -> stable_mir::ty::ImplTrait { + pub fn trait_impl(&self, impl_def: &stable_mir::ty::ImplDef) -> stable_mir::ty::ImplTrait { let mut tables = self.0.borrow_mut(); let def_id = tables[impl_def.0]; let impl_trait = tables.tcx.impl_trait_ref(def_id).unwrap(); impl_trait.stable(&mut *tables) } - fn generics_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::Generics { + pub fn generics_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::Generics { let mut tables = self.0.borrow_mut(); let def_id = tables[def_id]; let generics = tables.tcx.generics_of(def_id); generics.stable(&mut *tables) } - fn predicates_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::GenericPredicates { + pub fn predicates_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::GenericPredicates { let mut tables = self.0.borrow_mut(); let def_id = tables[def_id]; let GenericPredicates { parent, predicates } = tables.tcx.predicates_of(def_id); @@ -190,7 +199,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn explicit_predicates_of( + pub fn explicit_predicates_of( &self, def_id: stable_mir::DefId, ) -> stable_mir::ty::GenericPredicates { @@ -211,17 +220,20 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn local_crate(&self) -> stable_mir::Crate { + /// Get information about the local crate. + pub fn local_crate(&self) -> stable_mir::Crate { let tables = self.0.borrow(); smir_crate(tables.tcx, LOCAL_CRATE) } - fn external_crates(&self) -> Vec { + /// Retrieve a list of all external crates. + pub fn external_crates(&self) -> Vec { let tables = self.0.borrow(); tables.tcx.crates(()).iter().map(|crate_num| smir_crate(tables.tcx, *crate_num)).collect() } - fn find_crates(&self, name: &str) -> Vec { + /// Find a crate with the given name. + pub fn find_crates(&self, name: &str) -> Vec { let tables = self.0.borrow(); let crates: Vec = [LOCAL_CRATE] .iter() @@ -234,7 +246,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { crates } - fn def_name(&self, def_id: stable_mir::DefId, trimmed: bool) -> Symbol { + /// Returns the name of given `DefId`. + pub fn def_name(&self, def_id: stable_mir::DefId, trimmed: bool) -> Symbol { let tables = self.0.borrow(); if trimmed { with_forced_trimmed_paths!(tables.tcx.def_path_str(tables[def_id])) @@ -243,7 +256,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn tool_attrs( + /// Return registered tool attributes with the given attribute name. + /// + /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool + /// attributes will simply return an empty list. + /// + /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`. + /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. + pub fn tool_attrs( &self, def_id: stable_mir::DefId, attr: &[stable_mir::Symbol], @@ -267,12 +287,16 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn all_tool_attrs(&self, def_id: stable_mir::DefId) -> Vec { + /// Get all tool attributes of a definition. + pub fn all_tool_attrs( + &self, + def_id: stable_mir::DefId, + ) -> Vec { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let did = tables[def_id]; let attrs_iter = if let Some(did) = did.as_local() { - tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)).iter() + tcx.hir_attrs(tcx.local_def_id_to_hir_id(did)).iter() } else { tcx.attrs_for_def(did).iter() }; @@ -291,12 +315,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .collect() } - fn span_to_string(&self, span: stable_mir::ty::Span) -> String { + /// Returns printable, human readable form of `Span`. + pub fn span_to_string(&self, span: stable_mir::ty::Span) -> String { let tables = self.0.borrow(); tables.tcx.sess.source_map().span_to_diagnostic_string(tables[span]) } - fn get_filename(&self, span: &Span) -> Filename { + /// Return filename from given `Span`, for diagnostic purposes. + pub fn get_filename(&self, span: &Span) -> Filename { let tables = self.0.borrow(); tables .tcx @@ -307,23 +333,27 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .to_string() } - fn get_lines(&self, span: &Span) -> LineInfo { + /// Return lines corresponding to this `Span`. + pub fn get_lines(&self, span: &Span) -> LineInfo { let tables = self.0.borrow(); let lines = &tables.tcx.sess.source_map().span_to_location_info(tables[*span]); LineInfo { start_line: lines.1, start_col: lines.2, end_line: lines.3, end_col: lines.4 } } - fn item_kind(&self, item: CrateItem) -> ItemKind { + /// Returns the `kind` of given `DefId`. + pub fn item_kind(&self, item: CrateItem) -> ItemKind { let tables = self.0.borrow(); new_item_kind(tables.tcx.def_kind(tables[item.0])) } - fn is_foreign_item(&self, item: DefId) -> bool { + /// Returns whether this is a foreign item. + pub fn is_foreign_item(&self, item: DefId) -> bool { let tables = self.0.borrow(); tables.tcx.is_foreign_item(tables[item]) } - fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind { + /// Returns the kind of a given foreign item. + pub fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind { let mut tables = self.0.borrow_mut(); let def_id = tables[def.def_id()]; let tcx = tables.tcx; @@ -338,32 +368,37 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn adt_kind(&self, def: AdtDef) -> AdtKind { + /// Returns the kind of a given algebraic data type. + pub fn adt_kind(&self, def: AdtDef) -> AdtKind { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; def.internal(&mut *tables, tcx).adt_kind().stable(&mut *tables) } - fn adt_is_box(&self, def: AdtDef) -> bool { + /// Returns if the ADT is a box. + pub fn adt_is_box(&self, def: AdtDef) -> bool { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; def.internal(&mut *tables, tcx).is_box() } - fn adt_is_simd(&self, def: AdtDef) -> bool { + /// Returns whether this ADT is simd. + pub fn adt_is_simd(&self, def: AdtDef) -> bool { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; def.internal(&mut *tables, tcx).repr().simd() } - fn adt_is_cstr(&self, def: AdtDef) -> bool { + /// Returns whether this definition is a C string. + pub fn adt_is_cstr(&self, def: AdtDef) -> bool { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.0.internal(&mut *tables, tcx); tables.tcx.is_lang_item(def_id, LangItem::CStr) } - fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig { + /// Retrieve the function signature for the given generic arguments. + pub fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.0.internal(&mut *tables, tcx); @@ -372,7 +407,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { sig.stable(&mut *tables) } - fn intrinsic(&self, def: DefId) -> Option { + /// Retrieve the intrinsic definition if the item corresponds one. + pub fn intrinsic(&self, def: DefId) -> Option { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.internal(&mut *tables, tcx); @@ -380,14 +416,16 @@ impl<'tcx> Context for TablesWrapper<'tcx> { intrinsic.map(|_| IntrinsicDef(def)) } - fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol { + /// Retrieve the plain function name of an intrinsic. + pub fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.0.internal(&mut *tables, tcx); tcx.intrinsic(def_id).unwrap().name.to_string() } - fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig { + /// Retrieve the closure signature for the given generic arguments. + pub fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let args_ref = args.internal(&mut *tables, tcx); @@ -395,25 +433,28 @@ impl<'tcx> Context for TablesWrapper<'tcx> { sig.stable(&mut *tables) } - fn adt_variants_len(&self, def: AdtDef) -> usize { + /// The number of variants in this ADT. + pub fn adt_variants_len(&self, def: AdtDef) -> usize { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; def.internal(&mut *tables, tcx).variants().len() } - fn variant_name(&self, def: VariantDef) -> Symbol { + /// The name of a variant. + pub fn variant_name(&self, def: VariantDef) -> Symbol { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; def.internal(&mut *tables, tcx).name.to_string() } - fn variant_fields(&self, def: VariantDef) -> Vec { + pub fn variant_fields(&self, def: VariantDef) -> Vec { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; def.internal(&mut *tables, tcx).fields.iter().map(|f| f.stable(&mut *tables)).collect() } - fn eval_target_usize(&self, cnst: &MirConst) -> Result { + /// Evaluate constant as a target usize. + pub fn eval_target_usize(&self, cnst: &MirConst) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let mir_const = cnst.internal(&mut *tables, tcx); @@ -421,7 +462,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .try_eval_target_usize(tables.tcx, ty::TypingEnv::fully_monomorphized()) .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64"))) } - fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result { + pub fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let mir_const = cnst.internal(&mut *tables, tcx); @@ -430,7 +471,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64"))) } - fn try_new_const_zst(&self, ty: Ty) -> Result { + /// Create a new zero-sized constant. + pub fn try_new_const_zst(&self, ty: Ty) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let ty_internal = ty.internal(&mut *tables, tcx); @@ -455,7 +497,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .stable(&mut *tables)) } - fn new_const_str(&self, value: &str) -> MirConst { + /// Create a new constant that represents the given string value. + pub fn new_const_str(&self, value: &str) -> MirConst { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let ty = ty::Ty::new_static_str(tcx); @@ -466,12 +509,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> { mir::Const::from_value(val, ty).stable(&mut tables) } - fn new_const_bool(&self, value: bool) -> MirConst { + /// Create a new constant that represents the given boolean value. + pub fn new_const_bool(&self, value: bool) -> MirConst { let mut tables = self.0.borrow_mut(); mir::Const::from_bool(tables.tcx, value).stable(&mut tables) } - fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result { + /// Create a new constant that represents the given value. + pub fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let ty = ty::Ty::new_uint(tcx, uint_ty.internal(&mut *tables, tcx)); @@ -486,7 +531,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { Ok(mir::Const::from_scalar(tcx, mir::interpret::Scalar::Int(scalar), ty) .stable(&mut tables)) } - fn try_new_ty_const_uint( + pub fn try_new_ty_const_uint( &self, value: u128, uint_ty: UintTy, @@ -508,27 +553,35 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .stable(&mut *tables)) } - fn new_rigid_ty(&self, kind: RigidTy) -> stable_mir::ty::Ty { + /// Create a new type from the given kind. + pub fn new_rigid_ty(&self, kind: RigidTy) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let internal_kind = kind.internal(&mut *tables, tcx); tables.tcx.mk_ty_from_kind(internal_kind).stable(&mut *tables) } - fn new_box_ty(&self, ty: stable_mir::ty::Ty) -> stable_mir::ty::Ty { + /// Create a new box type, `Box`, for the given inner type `T`. + pub fn new_box_ty(&self, ty: stable_mir::ty::Ty) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let inner = ty.internal(&mut *tables, tcx); ty::Ty::new_box(tables.tcx, inner).stable(&mut *tables) } - fn def_ty(&self, item: stable_mir::DefId) -> stable_mir::ty::Ty { + /// Returns the type of given crate item. + pub fn def_ty(&self, item: stable_mir::DefId) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; tcx.type_of(item.internal(&mut *tables, tcx)).instantiate_identity().stable(&mut *tables) } - fn def_ty_with_args(&self, item: stable_mir::DefId, args: &GenericArgs) -> stable_mir::ty::Ty { + /// Returns the type of given definition instantiated with the given arguments. + pub fn def_ty_with_args( + &self, + item: stable_mir::DefId, + args: &GenericArgs, + ) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let args = args.internal(&mut *tables, tcx); @@ -543,33 +596,38 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .stable(&mut *tables) } - fn mir_const_pretty(&self, cnst: &stable_mir::ty::MirConst) -> String { + /// Returns literal value of a const as a string. + pub fn mir_const_pretty(&self, cnst: &stable_mir::ty::MirConst) -> String { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; cnst.internal(&mut *tables, tcx).to_string() } - fn span_of_an_item(&self, def_id: stable_mir::DefId) -> Span { + /// `Span` of an item. + pub fn span_of_an_item(&self, def_id: stable_mir::DefId) -> Span { let mut tables = self.0.borrow_mut(); tables.tcx.def_span(tables[def_id]).stable(&mut *tables) } - fn ty_pretty(&self, ty: stable_mir::ty::Ty) -> String { + /// Obtain the representation of a type. + pub fn ty_pretty(&self, ty: stable_mir::ty::Ty) -> String { let tables = self.0.borrow_mut(); tables.types[ty].to_string() } - fn ty_kind(&self, ty: stable_mir::ty::Ty) -> TyKind { + /// Obtain the representation of a type. + pub fn ty_kind(&self, ty: stable_mir::ty::Ty) -> TyKind { let mut tables = self.0.borrow_mut(); tables.types[ty].kind().stable(&mut *tables) } - fn ty_const_pretty(&self, ct: stable_mir::ty::TyConstId) -> String { + pub fn ty_const_pretty(&self, ct: stable_mir::ty::TyConstId) -> String { let tables = self.0.borrow_mut(); tables.ty_consts[ct].to_string() } - fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> stable_mir::ty::Ty { + /// Get the discriminant Ty for this Ty if there's one. + pub fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let internal_kind = ty.internal(&mut *tables, tcx); @@ -577,7 +635,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { internal_ty.discriminant_ty(tables.tcx).stable(&mut *tables) } - fn instance_body(&self, def: InstanceDef) -> Option { + /// Get the body of an Instance which is already monomorphized. + pub fn instance_body(&self, def: InstanceDef) -> Option { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; tables @@ -585,63 +644,67 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .then(|| BodyBuilder::new(tables.tcx, instance).build(&mut *tables)) } - fn instance_ty(&self, def: InstanceDef) -> stable_mir::ty::Ty { + /// Get the instance type with generic instantiations applied and lifetimes erased. + pub fn instance_ty(&self, def: InstanceDef) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; assert!(!instance.has_non_region_param(), "{instance:?} needs further instantiation"); instance.ty(tables.tcx, ty::TypingEnv::fully_monomorphized()).stable(&mut *tables) } - fn instance_args(&self, def: InstanceDef) -> GenericArgs { + /// Get the instantiation types. + pub fn instance_args(&self, def: InstanceDef) -> GenericArgs { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; instance.args.stable(&mut *tables) } - fn instance_abi(&self, def: InstanceDef) -> Result { + /// Get an instance ABI. + pub fn instance_abi(&self, def: InstanceDef) -> Result { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; Ok(tables.fn_abi_of_instance(instance, List::empty())?.stable(&mut *tables)) } - fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result { + /// Get the ABI of a function pointer. + pub fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let sig = fn_ptr.internal(&mut *tables, tcx); Ok(tables.fn_abi_of_fn_ptr(sig, List::empty())?.stable(&mut *tables)) } - fn instance_def_id(&self, def: InstanceDef) -> stable_mir::DefId { + /// Get the instance. + pub fn instance_def_id(&self, def: InstanceDef) -> stable_mir::DefId { let mut tables = self.0.borrow_mut(); let def_id = tables.instances[def].def_id(); tables.create_def_id(def_id) } - fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol { + /// Get the instance mangled name. + pub fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol { let tables = self.0.borrow_mut(); let instance = tables.instances[instance]; tables.tcx.symbol_name(instance).name.to_string() } - fn is_empty_drop_shim(&self, def: InstanceDef) -> bool { + /// Check if this is an empty DropGlue shim. + pub fn is_empty_drop_shim(&self, def: InstanceDef) -> bool { let tables = self.0.borrow_mut(); let instance = tables.instances[def]; matches!(instance.def, ty::InstanceKind::DropGlue(_, None)) } - fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool { - let tables = self.0.borrow_mut(); - let instance = tables.instances[def]; - matches!(instance.def, ty::InstanceKind::AsyncDropGlueCtorShim(_, None)) - } - - fn mono_instance(&self, def_id: stable_mir::DefId) -> stable_mir::mir::mono::Instance { + /// Convert a non-generic crate item into an instance. + /// This function will panic if the item is generic. + pub fn mono_instance(&self, def_id: stable_mir::DefId) -> stable_mir::mir::mono::Instance { let mut tables = self.0.borrow_mut(); let def_id = tables[def_id]; Instance::mono(tables.tcx, def_id).stable(&mut *tables) } - fn requires_monomorphization(&self, def_id: stable_mir::DefId) -> bool { + /// Item requires monomorphization. + pub fn requires_monomorphization(&self, def_id: stable_mir::DefId) -> bool { let tables = self.0.borrow(); let def_id = tables[def_id]; let generics = tables.tcx.generics_of(def_id); @@ -649,7 +712,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { result } - fn resolve_instance( + /// Resolve an instance from the given function definition and generic arguments. + pub fn resolve_instance( &self, def: stable_mir::ty::FnDef, args: &stable_mir::ty::GenericArgs, @@ -669,7 +733,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn resolve_drop_in_place(&self, ty: stable_mir::ty::Ty) -> stable_mir::mir::mono::Instance { + /// Resolve an instance for drop_in_place for the given type. + pub fn resolve_drop_in_place(&self, ty: stable_mir::ty::Ty) -> stable_mir::mir::mono::Instance { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let internal_ty = ty.internal(&mut *tables, tcx); @@ -677,7 +742,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { instance.stable(&mut *tables) } - fn resolve_for_fn_ptr( + /// Resolve instance for a function pointer. + pub fn resolve_for_fn_ptr( &self, def: FnDef, args: &GenericArgs, @@ -695,7 +761,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .stable(&mut *tables) } - fn resolve_closure( + /// Resolve instance for a closure with the requested type. + pub fn resolve_closure( &self, def: ClosureDef, args: &GenericArgs, @@ -712,7 +779,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { ) } - fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result { + /// Try to evaluate an instance into a constant. + pub fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; let tcx = tables.tcx; @@ -732,21 +800,24 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .map_err(|e| e.stable(&mut *tables))? } - fn eval_static_initializer(&self, def: StaticDef) -> Result { + /// Evaluate a static's initializer. + pub fn eval_static_initializer(&self, def: StaticDef) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.0.internal(&mut *tables, tcx); tables.tcx.eval_static_initializer(def_id).stable(&mut *tables) } - fn global_alloc(&self, alloc: stable_mir::mir::alloc::AllocId) -> GlobalAlloc { + /// Retrieve global allocation for the given allocation ID. + pub fn global_alloc(&self, alloc: stable_mir::mir::alloc::AllocId) -> GlobalAlloc { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let alloc_id = alloc.internal(&mut *tables, tcx); tables.tcx.global_alloc(alloc_id).stable(&mut *tables) } - fn vtable_allocation( + /// Retrieve the id for the virtual table. + pub fn vtable_allocation( &self, global_alloc: &GlobalAlloc, ) -> Option { @@ -764,7 +835,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { Some(alloc_id.stable(&mut *tables)) } - fn krate(&self, def_id: stable_mir::DefId) -> Crate { + pub fn krate(&self, def_id: stable_mir::DefId) -> Crate { let tables = self.0.borrow(); smir_crate(tables.tcx, tables[def_id].krate) } @@ -772,7 +843,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { /// Retrieve the instance name for diagnostic messages. /// /// This will return the specialized name, e.g., `Vec::new`. - fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol { + pub fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol { let tables = self.0.borrow_mut(); let instance = tables.instances[def]; if trimmed { @@ -786,7 +857,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - fn ty_layout(&self, ty: Ty) -> Result { + /// Get the layout of a type. + pub fn ty_layout(&self, ty: Ty) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let ty = ty.internal(&mut *tables, tcx); @@ -794,19 +866,22 @@ impl<'tcx> Context for TablesWrapper<'tcx> { Ok(layout.stable(&mut *tables)) } - fn layout_shape(&self, id: Layout) -> LayoutShape { + /// Get the layout shape. + pub fn layout_shape(&self, id: Layout) -> LayoutShape { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; id.internal(&mut *tables, tcx).0.stable(&mut *tables) } - fn place_pretty(&self, place: &Place) -> String { + /// Get a debug string representation of a place. + pub fn place_pretty(&self, place: &Place) -> String { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; format!("{:?}", place.internal(&mut *tables, tcx)) } - fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty { + /// Get the resulting type of binary operation. + pub fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let rhs_internal = rhs.internal(&mut *tables, tcx); @@ -815,16 +890,31 @@ impl<'tcx> Context for TablesWrapper<'tcx> { ty.stable(&mut *tables) } - fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty { + /// Get the resulting type of unary operation. + pub fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let arg_internal = arg.internal(&mut *tables, tcx); let ty = un_op.internal(&mut *tables, tcx).ty(tcx, arg_internal); ty.stable(&mut *tables) } -} -pub(crate) struct TablesWrapper<'tcx>(pub RefCell>); + /// Get all associated items of a definition. + pub fn associated_items(&self, def_id: stable_mir::DefId) -> stable_mir::AssocItems { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let def_id = tables[def_id]; + let assoc_items = if tcx.is_trait_alias(def_id) { + Vec::new() + } else { + tcx.associated_item_def_ids(def_id) + .iter() + .map(|did| tcx.associated_item(*did).stable(&mut *tables)) + .collect() + }; + assoc_items + } +} /// Implement error handling for extracting function ABI information. impl<'tcx> FnAbiOfHelpers<'tcx> for Tables<'tcx> { diff --git a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs index 62cbab9b723cc..7ccc785a40026 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs @@ -14,6 +14,7 @@ use stable_mir::target::MachineSize as Size; use stable_mir::ty::{Align, IndexedVal, VariantIdx}; use crate::rustc_smir::{Stable, Tables}; +use crate::stable_mir; impl<'tcx> Stable<'tcx> for rustc_abi::VariantIdx { type T = VariantIdx; diff --git a/compiler/rustc_smir/src/rustc_smir/convert/error.rs b/compiler/rustc_smir/src/rustc_smir/convert/error.rs index 82ecfa630ddb0..2cde5542483db 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/error.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/error.rs @@ -6,6 +6,7 @@ use rustc_middle::mir::interpret::AllocError; use rustc_middle::ty::layout::LayoutError; use crate::rustc_smir::{Stable, Tables}; +use crate::stable_mir; impl<'tcx> Stable<'tcx> for LayoutError<'tcx> { type T = stable_mir::Error; diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index bdd6e16a7c171..42b3e59b73ab9 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -9,6 +9,7 @@ use stable_mir::ty::{Allocation, ConstantKind, MirConst}; use stable_mir::{Error, opaque}; use crate::rustc_smir::{Stable, Tables, alloc}; +use crate::stable_mir; impl<'tcx> Stable<'tcx> for mir::Body<'tcx> { type T = stable_mir::mir::Body; @@ -493,6 +494,9 @@ impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> { AssertKind::ResumedAfterPanic(coroutine) => { stable_mir::mir::AssertMessage::ResumedAfterPanic(coroutine.stable(tables)) } + AssertKind::ResumedAfterDrop(coroutine) => { + stable_mir::mir::AssertMessage::ResumedAfterDrop(coroutine.stable(tables)) + } AssertKind::MisalignedPointerDereference { required, found } => { stable_mir::mir::AssertMessage::MisalignedPointerDereference { required: required.stable(tables), @@ -647,13 +651,18 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> { mir::TerminatorKind::UnwindTerminate(_) => TerminatorKind::Abort, mir::TerminatorKind::Return => TerminatorKind::Return, mir::TerminatorKind::Unreachable => TerminatorKind::Unreachable, - mir::TerminatorKind::Drop { place, target, unwind, replace: _ } => { - TerminatorKind::Drop { - place: place.stable(tables), - target: target.as_usize(), - unwind: unwind.stable(tables), - } - } + mir::TerminatorKind::Drop { + place, + target, + unwind, + replace: _, + drop: _, + async_fut: _, + } => TerminatorKind::Drop { + place: place.stable(tables), + target: target.as_usize(), + unwind: unwind.stable(tables), + }, mir::TerminatorKind::Call { func, args, diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs index a3da563af50d4..3494de62d835b 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs @@ -3,6 +3,7 @@ use rustc_abi::FieldIdx; use crate::rustc_smir::{Stable, Tables}; +use crate::stable_mir; mod abi; mod error; diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index a0faf20c79a67..8bcac4c4678e4 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -7,6 +7,7 @@ use stable_mir::ty::{ }; use crate::rustc_smir::{Stable, Tables, alloc}; +use crate::stable_mir; impl<'tcx> Stable<'tcx> for ty::AliasTyKind { type T = stable_mir::ty::AliasKind; @@ -15,7 +16,7 @@ impl<'tcx> Stable<'tcx> for ty::AliasTyKind { ty::Projection => stable_mir::ty::AliasKind::Projection, ty::Inherent => stable_mir::ty::AliasKind::Inherent, ty::Opaque => stable_mir::ty::AliasKind::Opaque, - ty::Weak => stable_mir::ty::AliasKind::Weak, + ty::Free => stable_mir::ty::AliasKind::Free, } } } @@ -405,11 +406,13 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> { fn stable(&self, tables: &mut Tables<'_>) -> Self::T { match **self { - ty::PatternKind::Range { start, end, include_end } => stable_mir::ty::Pattern::Range { - start: start.stable(tables), - end: end.stable(tables), - include_end, + ty::PatternKind::Range { start, end } => stable_mir::ty::Pattern::Range { + // FIXME(SMIR): update data structures to not have an Option here anymore + start: Some(start.stable(tables)), + end: Some(end.stable(tables)), + include_end: true, }, + ty::PatternKind::Or(_) => todo!(), } } } @@ -636,8 +639,8 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> { const_.stable(tables), ty.stable(tables), ), - ClauseKind::WellFormed(generic_arg) => { - stable_mir::ty::ClauseKind::WellFormed(generic_arg.unpack().stable(tables)) + ClauseKind::WellFormed(term) => { + stable_mir::ty::ClauseKind::WellFormed(term.unpack().stable(tables)) } ClauseKind::ConstEvaluatable(const_) => { stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables)) @@ -811,6 +814,8 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> { | ty::InstanceKind::DropGlue(..) | ty::InstanceKind::CloneShim(..) | ty::InstanceKind::FnPtrShim(..) + | ty::InstanceKind::FutureDropPollShim(..) + | ty::InstanceKind::AsyncDropGlue(..) | ty::InstanceKind::AsyncDropGlueCtorShim(..) => { stable_mir::mir::mono::InstanceKind::Shim } @@ -869,7 +874,6 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi { ExternAbi::CCmseNonSecureCall => Abi::CCmseNonSecureCall, ExternAbi::CCmseNonSecureEntry => Abi::CCmseNonSecureEntry, ExternAbi::System { unwind } => Abi::System { unwind }, - ExternAbi::RustIntrinsic => Abi::RustIntrinsic, ExternAbi::RustCall => Abi::RustCall, ExternAbi::Unadjusted => Abi::Unadjusted, ExternAbi::RustCold => Abi::RustCold, @@ -889,3 +893,69 @@ impl<'tcx> Stable<'tcx> for rustc_session::cstore::ForeignModule { } } } + +impl<'tcx> Stable<'tcx> for ty::AssocKind { + type T = stable_mir::ty::AssocKind; + + fn stable(&self, tables: &mut Tables<'_>) -> Self::T { + use stable_mir::ty::{AssocKind, AssocTypeData}; + match *self { + ty::AssocKind::Const { name } => AssocKind::Const { name: name.to_string() }, + ty::AssocKind::Fn { name, has_self } => { + AssocKind::Fn { name: name.to_string(), has_self } + } + ty::AssocKind::Type { data } => AssocKind::Type { + data: match data { + ty::AssocTypeData::Normal(name) => AssocTypeData::Normal(name.to_string()), + ty::AssocTypeData::Rpitit(rpitit) => { + AssocTypeData::Rpitit(rpitit.stable(tables)) + } + }, + }, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::AssocItemContainer { + type T = stable_mir::ty::AssocItemContainer; + + fn stable(&self, _tables: &mut Tables<'_>) -> Self::T { + use stable_mir::ty::AssocItemContainer; + match self { + ty::AssocItemContainer::Trait => AssocItemContainer::Trait, + ty::AssocItemContainer::Impl => AssocItemContainer::Impl, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::AssocItem { + type T = stable_mir::ty::AssocItem; + + fn stable(&self, tables: &mut Tables<'_>) -> Self::T { + stable_mir::ty::AssocItem { + def_id: tables.assoc_def(self.def_id), + kind: self.kind.stable(tables), + container: self.container.stable(tables), + trait_item_def_id: self.trait_item_def_id.map(|did| tables.assoc_def(did)), + } + } +} + +impl<'tcx> Stable<'tcx> for ty::ImplTraitInTraitData { + type T = stable_mir::ty::ImplTraitInTraitData; + + fn stable(&self, tables: &mut Tables<'_>) -> Self::T { + use stable_mir::ty::ImplTraitInTraitData; + match self { + ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id } => { + ImplTraitInTraitData::Trait { + fn_def_id: tables.fn_def(*fn_def_id), + opaque_def_id: tables.opaque_def(*opaque_def_id), + } + } + ty::ImplTraitInTraitData::Impl { fn_def_id } => { + ImplTraitInTraitData::Impl { fn_def_id: tables.fn_def(*fn_def_id) } + } + } + } +} diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index c5d33f090a05b..b5003baaf633c 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -21,10 +21,11 @@ use stable_mir::{CtorKind, ItemKind}; use tracing::debug; use crate::rustc_internal::IndexMap; +use crate::stable_mir; mod alloc; mod builder; -pub(crate) mod context; +pub mod context; mod convert; pub struct Tables<'tcx> { diff --git a/compiler/stable_mir/src/abi.rs b/compiler/rustc_smir/src/stable_mir/abi.rs similarity index 97% rename from compiler/stable_mir/src/abi.rs rename to compiler/rustc_smir/src/stable_mir/abi.rs index 091f3e1a95e95..3842cb7e653e8 100644 --- a/compiler/stable_mir/src/abi.rs +++ b/compiler/rustc_smir/src/stable_mir/abi.rs @@ -3,12 +3,13 @@ use std::num::NonZero; use std::ops::RangeInclusive; use serde::Serialize; +use stable_mir::compiler_interface::with; +use stable_mir::mir::FieldIdx; +use stable_mir::target::{MachineInfo, MachineSize as Size}; +use stable_mir::ty::{Align, IndexedVal, Ty, VariantIdx}; +use stable_mir::{Error, Opaque, error}; -use crate::compiler_interface::with; -use crate::mir::FieldIdx; -use crate::target::{MachineInfo, MachineSize as Size}; -use crate::ty::{Align, IndexedVal, Ty, VariantIdx}; -use crate::{Error, Opaque, error}; +use crate::stable_mir; /// A function ABI definition. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] @@ -149,7 +150,7 @@ pub enum FieldsShape { Arbitrary { /// Offsets for the first byte of each field, /// ordered to match the source definition order. - /// I.e.: It follows the same order as [crate::ty::VariantDef::fields()]. + /// I.e.: It follows the same order as [super::ty::VariantDef::fields()]. /// This vector does not go in increasing order. offsets: Vec, }, diff --git a/compiler/rustc_smir/src/stable_mir/compiler_interface.rs b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs new file mode 100644 index 0000000000000..bb35e23a72884 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs @@ -0,0 +1,497 @@ +//! Define the interface with the Rust compiler. +//! +//! StableMIR users should not use any of the items in this module directly. +//! These APIs have no stability guarantee. + +use std::cell::Cell; + +use rustc_smir::context::SmirCtxt; +use stable_mir::abi::{FnAbi, Layout, LayoutShape}; +use stable_mir::crate_def::Attribute; +use stable_mir::mir::alloc::{AllocId, GlobalAlloc}; +use stable_mir::mir::mono::{Instance, InstanceDef, StaticDef}; +use stable_mir::mir::{BinOp, Body, Place, UnOp}; +use stable_mir::target::MachineInfo; +use stable_mir::ty::{ + AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef, + ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics, + ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, TraitDecl, + TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, +}; +use stable_mir::{ + AssocItems, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, + ItemKind, Symbol, TraitDecls, mir, +}; + +use crate::{rustc_smir, stable_mir}; + +/// Stable public API for querying compiler information. +/// +/// All queries are delegated to an internal [`SmirCtxt`] that provides +/// similar APIs but based on internal rustc constructs. +/// +/// Do not use this directly. This is currently used in the macro expansion. +pub(crate) struct SmirInterface<'tcx> { + pub(crate) cx: SmirCtxt<'tcx>, +} + +impl<'tcx> SmirInterface<'tcx> { + pub(crate) fn entry_fn(&self) -> Option { + self.cx.entry_fn() + } + + /// Retrieve all items of the local crate that have a MIR associated with them. + pub(crate) fn all_local_items(&self) -> CrateItems { + self.cx.all_local_items() + } + + /// Retrieve the body of a function. + /// This function will panic if the body is not available. + pub(crate) fn mir_body(&self, item: DefId) -> mir::Body { + self.cx.mir_body(item) + } + + /// Check whether the body of a function is available. + pub(crate) fn has_body(&self, item: DefId) -> bool { + self.cx.has_body(item) + } + + pub(crate) fn foreign_modules(&self, crate_num: CrateNum) -> Vec { + self.cx.foreign_modules(crate_num) + } + + /// Retrieve all functions defined in this crate. + pub(crate) fn crate_functions(&self, crate_num: CrateNum) -> Vec { + self.cx.crate_functions(crate_num) + } + + /// Retrieve all static items defined in this crate. + pub(crate) fn crate_statics(&self, crate_num: CrateNum) -> Vec { + self.cx.crate_statics(crate_num) + } + + pub(crate) fn foreign_module(&self, mod_def: ForeignModuleDef) -> ForeignModule { + self.cx.foreign_module(mod_def) + } + + pub(crate) fn foreign_items(&self, mod_def: ForeignModuleDef) -> Vec { + self.cx.foreign_items(mod_def) + } + + pub(crate) fn all_trait_decls(&self) -> TraitDecls { + self.cx.all_trait_decls() + } + + pub(crate) fn trait_decls(&self, crate_num: CrateNum) -> TraitDecls { + self.cx.trait_decls(crate_num) + } + + pub(crate) fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl { + self.cx.trait_decl(trait_def) + } + + pub(crate) fn all_trait_impls(&self) -> ImplTraitDecls { + self.cx.all_trait_impls() + } + + pub(crate) fn trait_impls(&self, crate_num: CrateNum) -> ImplTraitDecls { + self.cx.trait_impls(crate_num) + } + + pub(crate) fn trait_impl(&self, trait_impl: &ImplDef) -> ImplTrait { + self.cx.trait_impl(trait_impl) + } + + pub(crate) fn generics_of(&self, def_id: DefId) -> Generics { + self.cx.generics_of(def_id) + } + + pub(crate) fn predicates_of(&self, def_id: DefId) -> GenericPredicates { + self.cx.predicates_of(def_id) + } + + pub(crate) fn explicit_predicates_of(&self, def_id: DefId) -> GenericPredicates { + self.cx.explicit_predicates_of(def_id) + } + + /// Get information about the local crate. + pub(crate) fn local_crate(&self) -> Crate { + self.cx.local_crate() + } + + /// Retrieve a list of all external crates. + pub(crate) fn external_crates(&self) -> Vec { + self.cx.external_crates() + } + + /// Find a crate with the given name. + pub(crate) fn find_crates(&self, name: &str) -> Vec { + self.cx.find_crates(name) + } + + /// Returns the name of given `DefId`. + pub(crate) fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol { + self.cx.def_name(def_id, trimmed) + } + + /// Return registered tool attributes with the given attribute name. + /// + /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool + /// attributes will simply return an empty list. + /// + /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`. + /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. + pub(crate) fn tool_attrs(&self, def_id: DefId, attr: &[Symbol]) -> Vec { + self.cx.tool_attrs(def_id, attr) + } + + /// Get all tool attributes of a definition. + pub(crate) fn all_tool_attrs(&self, def_id: DefId) -> Vec { + self.cx.all_tool_attrs(def_id) + } + + /// Returns printable, human readable form of `Span`. + pub(crate) fn span_to_string(&self, span: Span) -> String { + self.cx.span_to_string(span) + } + + /// Return filename from given `Span`, for diagnostic purposes. + pub(crate) fn get_filename(&self, span: &Span) -> Filename { + self.cx.get_filename(span) + } + + /// Return lines corresponding to this `Span`. + pub(crate) fn get_lines(&self, span: &Span) -> LineInfo { + self.cx.get_lines(span) + } + + /// Returns the `kind` of given `DefId`. + pub(crate) fn item_kind(&self, item: CrateItem) -> ItemKind { + self.cx.item_kind(item) + } + + /// Returns whether this is a foreign item. + pub(crate) fn is_foreign_item(&self, item: DefId) -> bool { + self.cx.is_foreign_item(item) + } + + /// Returns the kind of a given foreign item. + pub(crate) fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind { + self.cx.foreign_item_kind(def) + } + + /// Returns the kind of a given algebraic data type. + pub(crate) fn adt_kind(&self, def: AdtDef) -> AdtKind { + self.cx.adt_kind(def) + } + + /// Returns if the ADT is a box. + pub(crate) fn adt_is_box(&self, def: AdtDef) -> bool { + self.cx.adt_is_box(def) + } + + /// Returns whether this ADT is simd. + pub(crate) fn adt_is_simd(&self, def: AdtDef) -> bool { + self.cx.adt_is_simd(def) + } + + /// Returns whether this definition is a C string. + pub(crate) fn adt_is_cstr(&self, def: AdtDef) -> bool { + self.cx.adt_is_cstr(def) + } + + /// Retrieve the function signature for the given generic arguments. + pub(crate) fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig { + self.cx.fn_sig(def, args) + } + + /// Retrieve the intrinsic definition if the item corresponds one. + pub(crate) fn intrinsic(&self, item: DefId) -> Option { + self.cx.intrinsic(item) + } + + /// Retrieve the plain function name of an intrinsic. + pub(crate) fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol { + self.cx.intrinsic_name(def) + } + + /// Retrieve the closure signature for the given generic arguments. + pub(crate) fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig { + self.cx.closure_sig(args) + } + + /// The number of variants in this ADT. + pub(crate) fn adt_variants_len(&self, def: AdtDef) -> usize { + self.cx.adt_variants_len(def) + } + + /// The name of a variant. + pub(crate) fn variant_name(&self, def: VariantDef) -> Symbol { + self.cx.variant_name(def) + } + + pub(crate) fn variant_fields(&self, def: VariantDef) -> Vec { + self.cx.variant_fields(def) + } + + /// Evaluate constant as a target usize. + pub(crate) fn eval_target_usize(&self, cnst: &MirConst) -> Result { + self.cx.eval_target_usize(cnst) + } + + pub(crate) fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result { + self.cx.eval_target_usize_ty(cnst) + } + + /// Create a new zero-sized constant. + pub(crate) fn try_new_const_zst(&self, ty: Ty) -> Result { + self.cx.try_new_const_zst(ty) + } + + /// Create a new constant that represents the given string value. + pub(crate) fn new_const_str(&self, value: &str) -> MirConst { + self.cx.new_const_str(value) + } + + /// Create a new constant that represents the given boolean value. + pub(crate) fn new_const_bool(&self, value: bool) -> MirConst { + self.cx.new_const_bool(value) + } + + /// Create a new constant that represents the given value. + pub(crate) fn try_new_const_uint( + &self, + value: u128, + uint_ty: UintTy, + ) -> Result { + self.cx.try_new_const_uint(value, uint_ty) + } + + pub(crate) fn try_new_ty_const_uint( + &self, + value: u128, + uint_ty: UintTy, + ) -> Result { + self.cx.try_new_ty_const_uint(value, uint_ty) + } + + /// Create a new type from the given kind. + pub(crate) fn new_rigid_ty(&self, kind: RigidTy) -> Ty { + self.cx.new_rigid_ty(kind) + } + + /// Create a new box type, `Box`, for the given inner type `T`. + pub(crate) fn new_box_ty(&self, ty: Ty) -> Ty { + self.cx.new_box_ty(ty) + } + + /// Returns the type of given crate item. + pub(crate) fn def_ty(&self, item: DefId) -> Ty { + self.cx.def_ty(item) + } + + /// Returns the type of given definition instantiated with the given arguments. + pub(crate) fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty { + self.cx.def_ty_with_args(item, args) + } + + /// Returns literal value of a const as a string. + pub(crate) fn mir_const_pretty(&self, cnst: &MirConst) -> String { + self.cx.mir_const_pretty(cnst) + } + + /// `Span` of an item. + pub(crate) fn span_of_an_item(&self, def_id: DefId) -> Span { + self.cx.span_of_an_item(def_id) + } + + pub(crate) fn ty_const_pretty(&self, ct: TyConstId) -> String { + self.cx.ty_const_pretty(ct) + } + + /// Obtain the representation of a type. + pub(crate) fn ty_pretty(&self, ty: Ty) -> String { + self.cx.ty_pretty(ty) + } + + /// Obtain the representation of a type. + pub(crate) fn ty_kind(&self, ty: Ty) -> TyKind { + self.cx.ty_kind(ty) + } + + /// Get the discriminant Ty for this Ty if there's one. + pub(crate) fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> Ty { + self.cx.rigid_ty_discriminant_ty(ty) + } + + /// Get the body of an Instance which is already monomorphized. + pub(crate) fn instance_body(&self, instance: InstanceDef) -> Option { + self.cx.instance_body(instance) + } + + /// Get the instance type with generic instantiations applied and lifetimes erased. + pub(crate) fn instance_ty(&self, instance: InstanceDef) -> Ty { + self.cx.instance_ty(instance) + } + + /// Get the instantiation types. + pub(crate) fn instance_args(&self, def: InstanceDef) -> GenericArgs { + self.cx.instance_args(def) + } + + /// Get the instance. + pub(crate) fn instance_def_id(&self, instance: InstanceDef) -> DefId { + self.cx.instance_def_id(instance) + } + + /// Get the instance mangled name. + pub(crate) fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol { + self.cx.instance_mangled_name(instance) + } + + /// Check if this is an empty DropGlue shim. + pub(crate) fn is_empty_drop_shim(&self, def: InstanceDef) -> bool { + self.cx.is_empty_drop_shim(def) + } + + /// Convert a non-generic crate item into an instance. + /// This function will panic if the item is generic. + pub(crate) fn mono_instance(&self, def_id: DefId) -> Instance { + self.cx.mono_instance(def_id) + } + + /// Item requires monomorphization. + pub(crate) fn requires_monomorphization(&self, def_id: DefId) -> bool { + self.cx.requires_monomorphization(def_id) + } + + /// Resolve an instance from the given function definition and generic arguments. + pub(crate) fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option { + self.cx.resolve_instance(def, args) + } + + /// Resolve an instance for drop_in_place for the given type. + pub(crate) fn resolve_drop_in_place(&self, ty: Ty) -> Instance { + self.cx.resolve_drop_in_place(ty) + } + + /// Resolve instance for a function pointer. + pub(crate) fn resolve_for_fn_ptr(&self, def: FnDef, args: &GenericArgs) -> Option { + self.cx.resolve_for_fn_ptr(def, args) + } + + /// Resolve instance for a closure with the requested type. + pub(crate) fn resolve_closure( + &self, + def: ClosureDef, + args: &GenericArgs, + kind: ClosureKind, + ) -> Option { + self.cx.resolve_closure(def, args, kind) + } + + /// Evaluate a static's initializer. + pub(crate) fn eval_static_initializer(&self, def: StaticDef) -> Result { + self.cx.eval_static_initializer(def) + } + + /// Try to evaluate an instance into a constant. + pub(crate) fn eval_instance( + &self, + def: InstanceDef, + const_ty: Ty, + ) -> Result { + self.cx.eval_instance(def, const_ty) + } + + /// Retrieve global allocation for the given allocation ID. + pub(crate) fn global_alloc(&self, id: AllocId) -> GlobalAlloc { + self.cx.global_alloc(id) + } + + /// Retrieve the id for the virtual table. + pub(crate) fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option { + self.cx.vtable_allocation(global_alloc) + } + + pub(crate) fn krate(&self, def_id: DefId) -> Crate { + self.cx.krate(def_id) + } + + pub(crate) fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol { + self.cx.instance_name(def, trimmed) + } + + /// Return information about the target machine. + pub(crate) fn target_info(&self) -> MachineInfo { + self.cx.target_info() + } + + /// Get an instance ABI. + pub(crate) fn instance_abi(&self, def: InstanceDef) -> Result { + self.cx.instance_abi(def) + } + + /// Get the ABI of a function pointer. + pub(crate) fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result { + self.cx.fn_ptr_abi(fn_ptr) + } + + /// Get the layout of a type. + pub(crate) fn ty_layout(&self, ty: Ty) -> Result { + self.cx.ty_layout(ty) + } + + /// Get the layout shape. + pub(crate) fn layout_shape(&self, id: Layout) -> LayoutShape { + self.cx.layout_shape(id) + } + + /// Get a debug string representation of a place. + pub(crate) fn place_pretty(&self, place: &Place) -> String { + self.cx.place_pretty(place) + } + + /// Get the resulting type of binary operation. + pub(crate) fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty { + self.cx.binop_ty(bin_op, rhs, lhs) + } + + /// Get the resulting type of unary operation. + pub(crate) fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty { + self.cx.unop_ty(un_op, arg) + } + + /// Get all associated items of a definition. + pub(crate) fn associated_items(&self, def_id: DefId) -> AssocItems { + self.cx.associated_items(def_id) + } +} + +// A thread local variable that stores a pointer to [`SmirInterface`]. +scoped_tls::scoped_thread_local!(static TLV: Cell<*const ()>); + +pub(crate) fn run<'tcx, T, F>(interface: &SmirInterface<'tcx>, f: F) -> Result +where + F: FnOnce() -> T, +{ + if TLV.is_set() { + Err(Error::from("StableMIR already running")) + } else { + let ptr: *const () = (interface as *const SmirInterface<'tcx>) as *const (); + TLV.set(&Cell::new(ptr), || Ok(f())) + } +} + +/// Execute the given function with access the [`SmirInterface`]. +/// +/// I.e., This function will load the current interface and calls a function with it. +/// Do not nest these, as that will ICE. +pub(crate) fn with(f: impl FnOnce(&SmirInterface<'_>) -> R) -> R { + assert!(TLV.is_set()); + TLV.with(|tlv| { + let ptr = tlv.get(); + assert!(!ptr.is_null()); + f(unsafe { &*(ptr as *const SmirInterface<'_>) }) + }) +} diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/rustc_smir/src/stable_mir/crate_def.rs similarity index 90% rename from compiler/stable_mir/src/crate_def.rs rename to compiler/rustc_smir/src/stable_mir/crate_def.rs index 2577c281ca4f2..64f7ef9b314ff 100644 --- a/compiler/stable_mir/src/crate_def.rs +++ b/compiler/rustc_smir/src/stable_mir/crate_def.rs @@ -2,9 +2,10 @@ //! such as, a function, a trait, an enum, and any other definitions. use serde::Serialize; +use stable_mir::ty::{GenericArgs, Span, Ty}; +use stable_mir::{AssocItems, Crate, Symbol, with}; -use crate::ty::{GenericArgs, Span, Ty}; -use crate::{Crate, Symbol, with}; +use crate::stable_mir; /// A unique identification number for each item accessible for the current compilation unit. #[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)] @@ -103,6 +104,14 @@ pub trait CrateDefType: CrateDef { } } +/// A trait for retrieving all items from a definition within a crate. +pub trait CrateDefItems: CrateDef { + /// Retrieve all associated items from a definition. + fn associated_items(&self) -> AssocItems { + with(|cx| cx.associated_items(self.def_id())) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Attribute { value: String, @@ -158,3 +167,9 @@ macro_rules! crate_def_with_ty { impl CrateDefType for $name {} }; } + +macro_rules! impl_crate_def_items { + ( $name:ident $(;)? ) => { + impl CrateDefItems for $name {} + }; +} diff --git a/compiler/stable_mir/src/error.rs b/compiler/rustc_smir/src/stable_mir/error.rs similarity index 100% rename from compiler/stable_mir/src/error.rs rename to compiler/rustc_smir/src/stable_mir/error.rs diff --git a/compiler/rustc_smir/src/stable_mir/mir.rs b/compiler/rustc_smir/src/stable_mir/mir.rs new file mode 100644 index 0000000000000..413b5152bb3b3 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/mir.rs @@ -0,0 +1,8 @@ +pub mod alloc; +mod body; +pub mod mono; +pub mod pretty; +pub mod visit; + +pub use body::*; +pub use visit::{MirVisitor, MutMirVisitor}; diff --git a/compiler/stable_mir/src/mir/alloc.rs b/compiler/rustc_smir/src/stable_mir/mir/alloc.rs similarity index 89% rename from compiler/stable_mir/src/mir/alloc.rs rename to compiler/rustc_smir/src/stable_mir/mir/alloc.rs index 7e0c4a479b8ef..782f52888b73d 100644 --- a/compiler/stable_mir/src/mir/alloc.rs +++ b/compiler/rustc_smir/src/stable_mir/mir/alloc.rs @@ -3,11 +3,12 @@ use std::io::Read; use serde::Serialize; +use stable_mir::mir::mono::{Instance, StaticDef}; +use stable_mir::target::{Endian, MachineInfo}; +use stable_mir::ty::{Allocation, Binder, ExistentialTraitRef, IndexedVal, Ty}; +use stable_mir::{Error, with}; -use crate::mir::mono::{Instance, StaticDef}; -use crate::target::{Endian, MachineInfo}; -use crate::ty::{Allocation, Binder, ExistentialTraitRef, IndexedVal, Ty}; -use crate::{Error, with}; +use crate::stable_mir; /// An allocation in the SMIR global memory can be either a function pointer, /// a static, or a "real" allocation with some data in it. @@ -58,7 +59,7 @@ impl IndexedVal for AllocId { /// Utility function used to read an allocation data into a unassigned integer. pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result { - let mut buf = [0u8; std::mem::size_of::()]; + let mut buf = [0u8; size_of::()]; match MachineInfo::target_endianness() { Endian::Little => { bytes.read_exact(&mut buf[..bytes.len()])?; @@ -73,7 +74,7 @@ pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result { /// Utility function used to read an allocation data into an assigned integer. pub(crate) fn read_target_int(mut bytes: &[u8]) -> Result { - let mut buf = [0u8; std::mem::size_of::()]; + let mut buf = [0u8; size_of::()]; match MachineInfo::target_endianness() { Endian::Little => { bytes.read_exact(&mut buf[..bytes.len()])?; diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs new file mode 100644 index 0000000000000..660cd7db0800d --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs @@ -0,0 +1,1126 @@ +use std::io; + +use serde::Serialize; +use stable_mir::compiler_interface::with; +use stable_mir::mir::pretty::function_body; +use stable_mir::ty::{ + AdtDef, ClosureDef, CoroutineClosureDef, CoroutineDef, GenericArgs, MirConst, Movability, + Region, RigidTy, Ty, TyConst, TyKind, VariantIdx, +}; +use stable_mir::{Error, Opaque, Span, Symbol}; + +use crate::stable_mir; + +/// The SMIR representation of a single function. +#[derive(Clone, Debug, Serialize)] +pub struct Body { + pub blocks: Vec, + + /// Declarations of locals within the function. + /// + /// 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(super) locals: LocalDecls, + + /// The number of arguments this function takes. + pub(super) arg_count: usize, + + /// Debug information pertaining to user variables, including captures. + pub var_debug_info: Vec, + + /// Mark an argument (which must be a tuple) as getting passed as its individual components. + /// + /// This is used for the "rust-call" ABI such as closures. + pub(super) spread_arg: Option, + + /// The span that covers the entire function body. + pub span: Span, +} + +pub type BasicBlockIdx = usize; + +impl Body { + /// Constructs a `Body`. + /// + /// A constructor is required to build a `Body` from outside the crate + /// because the `arg_count` and `locals` fields are private. + pub fn new( + blocks: Vec, + locals: LocalDecls, + arg_count: usize, + var_debug_info: Vec, + spread_arg: Option, + span: Span, + ) -> Self { + // If locals doesn't contain enough entries, it can lead to panics in + // `ret_local`, `arg_locals`, and `inner_locals`. + assert!( + locals.len() > arg_count, + "A Body must contain at least a local for the return value and each of the function's arguments" + ); + Self { blocks, locals, arg_count, var_debug_info, spread_arg, span } + } + + /// Return local that holds this function's return value. + pub fn ret_local(&self) -> &LocalDecl { + &self.locals[RETURN_LOCAL] + } + + /// Locals in `self` that correspond to this function's arguments. + pub fn arg_locals(&self) -> &[LocalDecl] { + &self.locals[1..][..self.arg_count] + } + + /// Inner locals for this function. These are the locals that are + /// neither the return local nor the argument locals. + pub fn inner_locals(&self) -> &[LocalDecl] { + &self.locals[self.arg_count + 1..] + } + + /// Returns a mutable reference to the local that holds this function's return value. + pub(crate) fn ret_local_mut(&mut self) -> &mut LocalDecl { + &mut self.locals[RETURN_LOCAL] + } + + /// Returns a mutable slice of locals corresponding to this function's arguments. + pub(crate) fn arg_locals_mut(&mut self) -> &mut [LocalDecl] { + &mut self.locals[1..][..self.arg_count] + } + + /// Returns a mutable slice of inner locals for this function. + /// Inner locals are those that are neither the return local nor the argument locals. + pub(crate) fn inner_locals_mut(&mut self) -> &mut [LocalDecl] { + &mut self.locals[self.arg_count + 1..] + } + + /// Convenience function to get all the locals in this function. + /// + /// Locals are typically accessed via the more specific methods `ret_local`, + /// `arg_locals`, and `inner_locals`. + pub fn locals(&self) -> &[LocalDecl] { + &self.locals + } + + /// Get the local declaration for this local. + pub fn local_decl(&self, local: Local) -> Option<&LocalDecl> { + self.locals.get(local) + } + + /// Get an iterator for all local declarations. + pub fn local_decls(&self) -> impl Iterator { + self.locals.iter().enumerate() + } + + /// Emit the body using the provided name for the signature. + pub fn dump(&self, w: &mut W, fn_name: &str) -> io::Result<()> { + function_body(w, self, fn_name) + } + + pub fn spread_arg(&self) -> Option { + self.spread_arg + } +} + +type LocalDecls = Vec; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct LocalDecl { + pub ty: Ty, + pub span: Span, + pub mutability: Mutability, +} + +#[derive(Clone, PartialEq, Eq, Debug, Serialize)] +pub struct BasicBlock { + pub statements: Vec, + pub terminator: Terminator, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct Terminator { + pub kind: TerminatorKind, + pub span: Span, +} + +impl Terminator { + pub fn successors(&self) -> Successors { + self.kind.successors() + } +} + +pub type Successors = Vec; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum TerminatorKind { + Goto { + target: BasicBlockIdx, + }, + SwitchInt { + discr: Operand, + targets: SwitchTargets, + }, + Resume, + Abort, + Return, + Unreachable, + Drop { + place: Place, + target: BasicBlockIdx, + unwind: UnwindAction, + }, + Call { + func: Operand, + args: Vec, + destination: Place, + target: Option, + unwind: UnwindAction, + }, + Assert { + cond: Operand, + expected: bool, + msg: AssertMessage, + target: BasicBlockIdx, + unwind: UnwindAction, + }, + InlineAsm { + template: String, + operands: Vec, + options: String, + line_spans: String, + destination: Option, + unwind: UnwindAction, + }, +} + +impl TerminatorKind { + pub fn successors(&self) -> Successors { + use self::TerminatorKind::*; + match *self { + Call { target: Some(t), unwind: UnwindAction::Cleanup(u), .. } + | Drop { target: t, unwind: UnwindAction::Cleanup(u), .. } + | Assert { target: t, unwind: UnwindAction::Cleanup(u), .. } + | InlineAsm { destination: Some(t), unwind: UnwindAction::Cleanup(u), .. } => { + vec![t, u] + } + Goto { target: t } + | Call { target: None, unwind: UnwindAction::Cleanup(t), .. } + | Call { target: Some(t), unwind: _, .. } + | Drop { target: t, unwind: _, .. } + | Assert { target: t, unwind: _, .. } + | InlineAsm { destination: None, unwind: UnwindAction::Cleanup(t), .. } + | InlineAsm { destination: Some(t), unwind: _, .. } => { + vec![t] + } + + Return + | Resume + | Abort + | Unreachable + | Call { target: None, unwind: _, .. } + | InlineAsm { destination: None, unwind: _, .. } => { + vec![] + } + SwitchInt { ref targets, .. } => targets.all_targets(), + } + } + + pub fn unwind(&self) -> Option<&UnwindAction> { + match *self { + TerminatorKind::Goto { .. } + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::SwitchInt { .. } => None, + TerminatorKind::Call { ref unwind, .. } + | TerminatorKind::Assert { ref unwind, .. } + | TerminatorKind::Drop { ref unwind, .. } + | TerminatorKind::InlineAsm { ref unwind, .. } => Some(unwind), + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct InlineAsmOperand { + pub in_value: Option, + pub out_place: Option, + // This field has a raw debug representation of MIR's InlineAsmOperand. + // For now we care about place/operand + the rest in a debug format. + pub raw_rpr: String, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum UnwindAction { + Continue, + Unreachable, + Terminate, + Cleanup(BasicBlockIdx), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum AssertMessage { + BoundsCheck { len: Operand, index: Operand }, + Overflow(BinOp, Operand, Operand), + OverflowNeg(Operand), + DivisionByZero(Operand), + RemainderByZero(Operand), + ResumedAfterReturn(CoroutineKind), + ResumedAfterPanic(CoroutineKind), + ResumedAfterDrop(CoroutineKind), + MisalignedPointerDereference { required: Operand, found: Operand }, + NullPointerDereference, +} + +impl AssertMessage { + pub fn description(&self) -> Result<&'static str, Error> { + match self { + AssertMessage::Overflow(BinOp::Add, _, _) => Ok("attempt to add with overflow"), + AssertMessage::Overflow(BinOp::Sub, _, _) => Ok("attempt to subtract with overflow"), + AssertMessage::Overflow(BinOp::Mul, _, _) => Ok("attempt to multiply with overflow"), + AssertMessage::Overflow(BinOp::Div, _, _) => Ok("attempt to divide with overflow"), + AssertMessage::Overflow(BinOp::Rem, _, _) => { + Ok("attempt to calculate the remainder with overflow") + } + AssertMessage::OverflowNeg(_) => Ok("attempt to negate with overflow"), + AssertMessage::Overflow(BinOp::Shr, _, _) => Ok("attempt to shift right with overflow"), + AssertMessage::Overflow(BinOp::Shl, _, _) => Ok("attempt to shift left with overflow"), + AssertMessage::Overflow(op, _, _) => Err(error!("`{:?}` cannot overflow", op)), + AssertMessage::DivisionByZero(_) => Ok("attempt to divide by zero"), + AssertMessage::RemainderByZero(_) => { + Ok("attempt to calculate the remainder with a divisor of zero") + } + AssertMessage::ResumedAfterReturn(CoroutineKind::Coroutine(_)) => { + Ok("coroutine resumed after completion") + } + AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( + CoroutineDesugaring::Async, + _, + )) => Ok("`async fn` resumed after completion"), + AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( + CoroutineDesugaring::Gen, + _, + )) => Ok("`async gen fn` resumed after completion"), + AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( + CoroutineDesugaring::AsyncGen, + _, + )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after completion"), + AssertMessage::ResumedAfterPanic(CoroutineKind::Coroutine(_)) => { + Ok("coroutine resumed after panicking") + } + AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( + CoroutineDesugaring::Async, + _, + )) => Ok("`async fn` resumed after panicking"), + AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( + CoroutineDesugaring::Gen, + _, + )) => Ok("`async gen fn` resumed after panicking"), + AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( + CoroutineDesugaring::AsyncGen, + _, + )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after panicking"), + + AssertMessage::ResumedAfterDrop(CoroutineKind::Coroutine(_)) => { + Ok("coroutine resumed after async drop") + } + AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared( + CoroutineDesugaring::Async, + _, + )) => Ok("`async fn` resumed after async drop"), + AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared( + CoroutineDesugaring::Gen, + _, + )) => Ok("`async gen fn` resumed after async drop"), + AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared( + CoroutineDesugaring::AsyncGen, + _, + )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after async drop"), + + AssertMessage::BoundsCheck { .. } => Ok("index out of bounds"), + AssertMessage::MisalignedPointerDereference { .. } => { + Ok("misaligned pointer dereference") + } + AssertMessage::NullPointerDereference => Ok("null pointer dereference occurred"), + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum BinOp { + Add, + AddUnchecked, + Sub, + SubUnchecked, + Mul, + MulUnchecked, + Div, + Rem, + BitXor, + BitAnd, + BitOr, + Shl, + ShlUnchecked, + Shr, + ShrUnchecked, + Eq, + Lt, + Le, + Ne, + Ge, + Gt, + Cmp, + Offset, +} + +impl BinOp { + /// Return the type of this operation for the given input Ty. + /// This function does not perform type checking, and it currently doesn't handle SIMD. + pub fn ty(&self, lhs_ty: Ty, rhs_ty: Ty) -> Ty { + with(|ctx| ctx.binop_ty(*self, lhs_ty, rhs_ty)) + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum UnOp { + Not, + Neg, + PtrMetadata, +} + +impl UnOp { + /// Return the type of this operation for the given input Ty. + /// This function does not perform type checking, and it currently doesn't handle SIMD. + pub fn ty(&self, arg_ty: Ty) -> Ty { + with(|ctx| ctx.unop_ty(*self, arg_ty)) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum CoroutineKind { + Desugared(CoroutineDesugaring, CoroutineSource), + Coroutine(Movability), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum CoroutineSource { + Block, + Closure, + Fn, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum CoroutineDesugaring { + Async, + + Gen, + + AsyncGen, +} + +pub(crate) type LocalDefId = Opaque; +/// The rustc coverage data structures are heavily tied to internal details of the +/// coverage implementation that are likely to change, and are unlikely to be +/// useful to third-party tools for the foreseeable future. +pub(crate) type Coverage = Opaque; + +/// The FakeReadCause describes the type of pattern why a FakeRead statement exists. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum FakeReadCause { + ForMatchGuard, + ForMatchedPlace(LocalDefId), + ForGuardBinding, + ForLet(LocalDefId), + ForIndex, +} + +/// Describes what kind of retag is to be performed +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] +pub enum RetagKind { + FnEntry, + TwoPhase, + Raw, + Default, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] +pub enum Variance { + Covariant, + Invariant, + Contravariant, + Bivariant, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct CopyNonOverlapping { + pub src: Operand, + pub dst: Operand, + pub count: Operand, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum NonDivergingIntrinsic { + Assume(Operand), + CopyNonOverlapping(CopyNonOverlapping), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct Statement { + pub kind: StatementKind, + pub span: Span, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum StatementKind { + Assign(Place, Rvalue), + FakeRead(FakeReadCause, Place), + SetDiscriminant { place: Place, variant_index: VariantIdx }, + Deinit(Place), + StorageLive(Local), + StorageDead(Local), + Retag(RetagKind, Place), + PlaceMention(Place), + AscribeUserType { place: Place, projections: UserTypeProjection, variance: Variance }, + Coverage(Coverage), + Intrinsic(NonDivergingIntrinsic), + ConstEvalCounter, + Nop, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum Rvalue { + /// Creates a pointer with the indicated mutability to the place. + /// + /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like + /// `&raw v` or `addr_of!(v)`. + AddressOf(RawPtrKind, Place), + + /// Creates an aggregate value, like a tuple or struct. + /// + /// This is needed because dataflow analysis needs to distinguish + /// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo` + /// has a destructor. + /// + /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Coroutine`. After + /// coroutine lowering, `Coroutine` aggregate kinds are disallowed too. + Aggregate(AggregateKind, Vec), + + /// * `Offset` has the same semantics as `<*const T>::offset`, except that the second + /// parameter may be a `usize` as well. + /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats, + /// raw pointers, or function pointers and return a `bool`. The types of the operands must be + /// matching, up to the usual caveat of the lifetimes in function pointers. + /// * Left and right shift operations accept signed or unsigned integers not necessarily of the + /// same type and return a value of the same type as their LHS. Like in Rust, the RHS is + /// truncated as needed. + /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching + /// types and return a value of that type. + /// * The remaining operations accept signed integers, unsigned integers, or floats with + /// matching types and return a value of that type. + BinaryOp(BinOp, Operand, Operand), + + /// Performs essentially all of the casts that can be performed via `as`. + /// + /// This allows for casts from/to a variety of types. + Cast(CastKind, Operand, Ty), + + /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition. + /// + /// For addition, subtraction, and multiplication on integers the error condition is set when + /// the infinite precision result would not be equal to the actual result. + CheckedBinaryOp(BinOp, Operand, Operand), + + /// A CopyForDeref is equivalent to a read from a place. + /// When such a read happens, it is guaranteed that the only use of the returned value is a + /// deref operation, immediately followed by one or more projections. + CopyForDeref(Place), + + /// Computes the discriminant of the place, returning it as an integer. + /// Returns zero for types without discriminant. + /// + /// The validity requirements for the underlying value are undecided for this rvalue, see + /// [#91095]. Note too that the value of the discriminant is not the same thing as the + /// variant index; + /// + /// [#91095]: https://github.com/rust-lang/rust/issues/91095 + Discriminant(Place), + + /// Yields the length of the place, as a `usize`. + /// + /// If the type of the place is an array, this is the array length. For slices (`[T]`, not + /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is + /// ill-formed for places of other types. + Len(Place), + + /// Creates a reference to the place. + Ref(Region, BorrowKind, Place), + + /// Creates an array where each element is the value of the operand. + /// + /// This is the cause of a bug in the case where the repetition count is zero because the value + /// is not dropped, see [#74836]. + /// + /// Corresponds to source code like `[x; 32]`. + /// + /// [#74836]: https://github.com/rust-lang/rust/issues/74836 + Repeat(Operand, TyConst), + + /// Transmutes a `*mut u8` into shallow-initialized `Box`. + /// + /// This is different from a normal transmute because dataflow analysis will treat the box as + /// initialized but its content as uninitialized. Like other pointer casts, this in general + /// affects alias analysis. + ShallowInitBox(Operand, Ty), + + /// Creates a pointer/reference to the given thread local. + /// + /// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a + /// `*const T`, and if neither of those apply a `&T`. + /// + /// **Note:** This is a runtime operation that actually executes code and is in this sense more + /// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to + /// SIGILL for some reason that I (JakobDegen) never got a chance to look into. + /// + /// **Needs clarification**: Are there weird additional semantics here related to the runtime + /// nature of this operation? + ThreadLocalRef(stable_mir::CrateItem), + + /// Computes a value as described by the operation. + NullaryOp(NullOp, Ty), + + /// Exactly like `BinaryOp`, but less operands. + /// + /// Also does two's-complement arithmetic. Negation requires a signed integer or a float; + /// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds + /// return a value with the same type as their operand. + UnaryOp(UnOp, Operand), + + /// Yields the operand unchanged + Use(Operand), +} + +impl Rvalue { + pub fn ty(&self, locals: &[LocalDecl]) -> Result { + match self { + Rvalue::Use(operand) => operand.ty(locals), + Rvalue::Repeat(operand, count) => { + Ok(Ty::new_array_with_const_len(operand.ty(locals)?, count.clone())) + } + Rvalue::ThreadLocalRef(did) => Ok(did.ty()), + Rvalue::Ref(reg, bk, place) => { + let place_ty = place.ty(locals)?; + Ok(Ty::new_ref(reg.clone(), place_ty, bk.to_mutable_lossy())) + } + Rvalue::AddressOf(mutability, place) => { + let place_ty = place.ty(locals)?; + Ok(Ty::new_ptr(place_ty, mutability.to_mutable_lossy())) + } + Rvalue::Len(..) => Ok(Ty::usize_ty()), + Rvalue::Cast(.., ty) => Ok(*ty), + Rvalue::BinaryOp(op, lhs, rhs) => { + let lhs_ty = lhs.ty(locals)?; + let rhs_ty = rhs.ty(locals)?; + Ok(op.ty(lhs_ty, rhs_ty)) + } + Rvalue::CheckedBinaryOp(op, lhs, rhs) => { + let lhs_ty = lhs.ty(locals)?; + let rhs_ty = rhs.ty(locals)?; + let ty = op.ty(lhs_ty, rhs_ty); + Ok(Ty::new_tuple(&[ty, Ty::bool_ty()])) + } + Rvalue::UnaryOp(op, operand) => { + let arg_ty = operand.ty(locals)?; + Ok(op.ty(arg_ty)) + } + Rvalue::Discriminant(place) => { + let place_ty = place.ty(locals)?; + place_ty + .kind() + .discriminant_ty() + .ok_or_else(|| error!("Expected a `RigidTy` but found: {place_ty:?}")) + } + Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { + Ok(Ty::usize_ty()) + } + Rvalue::NullaryOp(NullOp::ContractChecks, _) + | Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()), + Rvalue::Aggregate(ak, ops) => match *ak { + AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), + AggregateKind::Tuple => Ok(Ty::new_tuple( + &ops.iter().map(|op| op.ty(locals)).collect::, _>>()?, + )), + AggregateKind::Adt(def, _, ref args, _, _) => Ok(def.ty_with_args(args)), + AggregateKind::Closure(def, ref args) => Ok(Ty::new_closure(def, args.clone())), + AggregateKind::Coroutine(def, ref args, mov) => { + Ok(Ty::new_coroutine(def, args.clone(), mov)) + } + AggregateKind::CoroutineClosure(def, ref args) => { + Ok(Ty::new_coroutine_closure(def, args.clone())) + } + AggregateKind::RawPtr(ty, mutability) => Ok(Ty::new_ptr(ty, mutability)), + }, + Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)), + Rvalue::CopyForDeref(place) => place.ty(locals), + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum AggregateKind { + Array(Ty), + Tuple, + Adt(AdtDef, VariantIdx, GenericArgs, Option, Option), + Closure(ClosureDef, GenericArgs), + // FIXME(stable_mir): Movability here is redundant + Coroutine(CoroutineDef, GenericArgs, Movability), + CoroutineClosure(CoroutineClosureDef, GenericArgs), + RawPtr(Ty, Mutability), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum Operand { + Copy(Place), + Move(Place), + Constant(ConstOperand), +} + +#[derive(Clone, Eq, PartialEq, Serialize)] +pub struct Place { + pub local: Local, + /// projection out of a place (access a field, deref a pointer, etc) + pub projection: Vec, +} + +impl From for Place { + fn from(local: Local) -> Self { + Place { local, projection: vec![] } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct ConstOperand { + pub span: Span, + pub user_ty: Option, + pub const_: MirConst, +} + +/// Debug information pertaining to a user variable. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct VarDebugInfo { + /// The variable name. + pub name: Symbol, + + /// Source info of the user variable, including the scope + /// within which the variable is visible (to debuginfo). + pub source_info: SourceInfo, + + /// The user variable's data is split across several fragments, + /// each described by a `VarDebugInfoFragment`. + pub composite: Option, + + /// Where the data for this user variable is to be found. + pub value: VarDebugInfoContents, + + /// When present, indicates what argument number this variable is in the function that it + /// originated from (starting from 1). Note, if MIR inlining is enabled, then this is the + /// argument number in the original function before it was inlined. + pub argument_index: Option, +} + +impl VarDebugInfo { + /// Return a local variable if this info is related to one. + pub fn local(&self) -> Option { + match &self.value { + VarDebugInfoContents::Place(place) if place.projection.is_empty() => Some(place.local), + VarDebugInfoContents::Place(_) | VarDebugInfoContents::Const(_) => None, + } + } + + /// Return a constant if this info is related to one. + pub fn constant(&self) -> Option<&ConstOperand> { + match &self.value { + VarDebugInfoContents::Place(_) => None, + VarDebugInfoContents::Const(const_op) => Some(const_op), + } + } +} + +pub type SourceScope = u32; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct SourceInfo { + pub span: Span, + pub scope: SourceScope, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct VarDebugInfoFragment { + pub ty: Ty, + pub projection: Vec, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum VarDebugInfoContents { + Place(Place), + Const(ConstOperand), +} + +// In MIR ProjectionElem is parameterized on the second Field argument and the Index argument. This +// is so it can be used for both Places (for which the projection elements are of type +// ProjectionElem) and user-provided type annotations (for which the projection elements +// are of type ProjectionElem<(), ()>). In SMIR we don't need this generality, so we just use +// ProjectionElem for Places. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum ProjectionElem { + /// Dereference projections (e.g. `*_1`) project to the address referenced by the base place. + Deref, + + /// A field projection (e.g., `f` in `_1.f`) project to a field in the base place. The field is + /// referenced by source-order index rather than the name of the field. The fields type is also + /// given. + Field(FieldIdx, Ty), + + /// Index into a slice/array. The value of the index is computed at runtime using the `V` + /// argument. + /// + /// Note that this does not also dereference, and so it does not exactly correspond to slice + /// indexing in Rust. In other words, in the below Rust code: + /// + /// ```rust + /// let x = &[1, 2, 3, 4]; + /// let i = 2; + /// x[i]; + /// ``` + /// + /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same + /// thing is true of the `ConstantIndex` and `Subslice` projections below. + Index(Local), + + /// Index into a slice/array given by offsets. + /// + /// These indices are generated by slice patterns. Easiest to explain by example: + /// + /// ```ignore (illustrative) + /// [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 -- otherwise, the + /// projection is UB. + /// + /// 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, + }, + + /// Projects a slice from the base place. + /// + /// These indices are generated by slice patterns. If `from_end` is true, this represents + /// `slice[from..slice.len() - to]`. Otherwise it represents `array[from..to]`. + Subslice { + from: u64, + to: u64, + /// Whether `to` counts from the start or end of the array/slice. + from_end: bool, + }, + + /// "Downcast" to a variant of an enum or a coroutine. + Downcast(VariantIdx), + + /// Like an explicit cast from an opaque type to a concrete type, but without + /// requiring an intermediate variable. + OpaqueCast(Ty), + + /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where + /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping + /// explicit during optimizations and codegen. + /// + /// This projection doesn't impact the runtime behavior of the program except for potentially changing + /// some type metadata of the interpreter or codegen backend. + Subtype(Ty), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct UserTypeProjection { + pub base: UserTypeAnnotationIndex, + + pub projection: Opaque, +} + +pub type Local = usize; + +pub const RETURN_LOCAL: Local = 0; + +/// The source-order index of a field in a variant. +/// +/// For example, in the following types, +/// ```ignore(illustrative) +/// enum Demo1 { +/// Variant0 { a: bool, b: i32 }, +/// Variant1 { c: u8, d: u64 }, +/// } +/// struct Demo2 { e: u8, f: u16, g: u8 } +/// ``` +/// `a`'s `FieldIdx` is `0`, +/// `b`'s `FieldIdx` is `1`, +/// `c`'s `FieldIdx` is `0`, and +/// `g`'s `FieldIdx` is `2`. +pub type FieldIdx = usize; + +type UserTypeAnnotationIndex = usize; + +/// The possible branch sites of a [TerminatorKind::SwitchInt]. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct SwitchTargets { + /// The conditional branches where the first element represents the value that guards this + /// branch, and the second element is the branch target. + branches: Vec<(u128, BasicBlockIdx)>, + /// The `otherwise` branch which will be taken in case none of the conditional branches are + /// satisfied. + otherwise: BasicBlockIdx, +} + +impl SwitchTargets { + /// All possible targets including the `otherwise` target. + pub fn all_targets(&self) -> Successors { + self.branches.iter().map(|(_, target)| *target).chain(Some(self.otherwise)).collect() + } + + /// The `otherwise` branch target. + pub fn otherwise(&self) -> BasicBlockIdx { + self.otherwise + } + + /// The conditional targets which are only taken if the pattern matches the given value. + pub fn branches(&self) -> impl Iterator { + self.branches.iter().copied() + } + + /// The number of targets including `otherwise`. + pub fn len(&self) -> usize { + self.branches.len() + 1 + } + + /// Create a new SwitchTargets from the given branches and `otherwise` target. + pub fn new(branches: Vec<(u128, BasicBlockIdx)>, otherwise: BasicBlockIdx) -> SwitchTargets { + SwitchTargets { branches, otherwise } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum BorrowKind { + /// Data must be immutable and is aliasable. + Shared, + + /// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either + /// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]). + Fake(FakeBorrowKind), + + /// Data is mutable and not aliasable. + Mut { + /// `true` if this borrow arose from method-call auto-ref + kind: MutBorrowKind, + }, +} + +impl BorrowKind { + pub fn to_mutable_lossy(self) -> Mutability { + match self { + BorrowKind::Mut { .. } => Mutability::Mut, + BorrowKind::Shared => Mutability::Not, + // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. + BorrowKind::Fake(_) => Mutability::Not, + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum RawPtrKind { + Mut, + Const, + FakeForPtrMetadata, +} + +impl RawPtrKind { + pub fn to_mutable_lossy(self) -> Mutability { + match self { + RawPtrKind::Mut { .. } => Mutability::Mut, + RawPtrKind::Const => Mutability::Not, + // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. + RawPtrKind::FakeForPtrMetadata => Mutability::Not, + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum MutBorrowKind { + Default, + TwoPhaseBorrow, + ClosureCapture, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum FakeBorrowKind { + /// A shared (deep) borrow. Data must be immutable and is aliasable. + Deep, + /// The immediately borrowed place must be immutable, but projections from + /// it don't need to be. This is used to prevent match guards from replacing + /// the scrutinee. For example, a fake borrow of `a.b` doesn't + /// conflict with a mutable borrow of `a.b.c`. + Shallow, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub enum Mutability { + Not, + Mut, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum Safety { + Safe, + Unsafe, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum PointerCoercion { + /// Go from a fn-item type to a fn-pointer type. + ReifyFnPointer, + + /// Go from a safe fn pointer to an unsafe fn pointer. + UnsafeFnPointer, + + /// Go from a non-capturing closure to a fn pointer or an unsafe fn pointer. + /// It cannot convert a closure that requires unsafe. + ClosureFnPointer(Safety), + + /// Go from a mut raw pointer to a const raw pointer. + MutToConstPointer, + + /// Go from `*const [T; N]` to `*const T` + ArrayToPointer, + + /// Unsize a pointer/reference value, e.g., `&[T; n]` to + /// `&[T]`. Note that the source could be a thin or wide pointer. + /// This will do things like convert thin pointers to wide + /// pointers, or convert structs containing thin pointers to + /// structs containing wide pointers, or convert between wide + /// pointers. + Unsize, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum CastKind { + // FIXME(smir-rename): rename this to PointerExposeProvenance + PointerExposeAddress, + PointerWithExposedProvenance, + PointerCoercion(PointerCoercion), + // FIXME(smir-rename): change this to PointerCoercion(DynStar) + DynStar, + IntToInt, + FloatToInt, + FloatToFloat, + IntToFloat, + PtrToPtr, + FnPtrToPtr, + Transmute, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum NullOp { + /// Returns the size of a value of that type. + SizeOf, + /// Returns the minimum alignment of a type. + AlignOf, + /// Returns the offset of a field. + OffsetOf(Vec<(VariantIdx, FieldIdx)>), + /// cfg!(ub_checks), but at codegen time + UbChecks, + /// cfg!(contract_checks), but at codegen time + ContractChecks, +} + +impl Operand { + /// Get the type of an operand relative to the local declaration. + /// + /// In order to retrieve the correct type, the `locals` argument must match the list of all + /// locals from the function body where this operand originates from. + /// + /// Errors indicate a malformed operand or incompatible locals list. + pub fn ty(&self, locals: &[LocalDecl]) -> Result { + match self { + Operand::Copy(place) | Operand::Move(place) => place.ty(locals), + Operand::Constant(c) => Ok(c.ty()), + } + } +} + +impl ConstOperand { + pub fn ty(&self) -> Ty { + self.const_.ty() + } +} + +impl Place { + /// Resolve down the chain of projections to get the type referenced at the end of it. + /// E.g.: + /// Calling `ty()` on `var.field` should return the type of `field`. + /// + /// In order to retrieve the correct type, the `locals` argument must match the list of all + /// locals from the function body where this place originates from. + pub fn ty(&self, locals: &[LocalDecl]) -> Result { + self.projection.iter().try_fold(locals[self.local].ty, |place_ty, elem| elem.ty(place_ty)) + } +} + +impl ProjectionElem { + /// Get the expected type after applying this projection to a given place type. + pub fn ty(&self, place_ty: Ty) -> Result { + let ty = place_ty; + match &self { + ProjectionElem::Deref => Self::deref_ty(ty), + ProjectionElem::Field(_idx, fty) => Ok(*fty), + ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => Self::index_ty(ty), + ProjectionElem::Subslice { from, to, from_end } => { + Self::subslice_ty(ty, *from, *to, *from_end) + } + ProjectionElem::Downcast(_) => Ok(ty), + ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => Ok(*ty), + } + } + + fn index_ty(ty: Ty) -> Result { + ty.kind().builtin_index().ok_or_else(|| error!("Cannot index non-array type: {ty:?}")) + } + + fn subslice_ty(ty: Ty, from: u64, to: u64, from_end: bool) -> Result { + let ty_kind = ty.kind(); + match ty_kind { + TyKind::RigidTy(RigidTy::Slice(..)) => Ok(ty), + TyKind::RigidTy(RigidTy::Array(inner, _)) if !from_end => Ty::try_new_array( + inner, + to.checked_sub(from).ok_or_else(|| error!("Subslice overflow: {from}..{to}"))?, + ), + TyKind::RigidTy(RigidTy::Array(inner, size)) => { + let size = size.eval_target_usize()?; + let len = size - from - to; + Ty::try_new_array(inner, len) + } + _ => Err(Error(format!("Cannot subslice non-array type: `{ty_kind:?}`"))), + } + } + + fn deref_ty(ty: Ty) -> Result { + let deref_ty = ty + .kind() + .builtin_deref(true) + .ok_or_else(|| error!("Cannot dereference type: {ty:?}"))?; + Ok(deref_ty.ty) + } +} diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/rustc_smir/src/stable_mir/mir/mono.rs similarity index 88% rename from compiler/stable_mir/src/mir/mono.rs rename to compiler/rustc_smir/src/stable_mir/mir/mono.rs index 22507a49411f9..f5239cccae1eb 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/rustc_smir/src/stable_mir/mir/mono.rs @@ -2,12 +2,13 @@ use std::fmt::{Debug, Formatter}; use std::io; use serde::Serialize; +use stable_mir::abi::FnAbi; +use stable_mir::crate_def::CrateDef; +use stable_mir::mir::Body; +use stable_mir::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty}; +use stable_mir::{CrateItem, DefId, Error, ItemKind, Opaque, Symbol, with}; -use crate::abi::FnAbi; -use crate::crate_def::CrateDef; -use crate::mir::Body; -use crate::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty}; -use crate::{CrateItem, DefId, Error, ItemKind, Opaque, Symbol, with}; +use crate::stable_mir; #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum MonoItem { @@ -117,11 +118,11 @@ impl Instance { } /// Resolve an instance starting from a function definition and generic arguments. - pub fn resolve(def: FnDef, args: &GenericArgs) -> Result { + pub fn resolve(def: FnDef, args: &GenericArgs) -> Result { with(|context| { - context.resolve_instance(def, args).ok_or_else(|| { - crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) - }) + context + .resolve_instance(def, args) + .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`"))) }) } @@ -131,11 +132,11 @@ impl Instance { } /// Resolve an instance for a given function pointer. - pub fn resolve_for_fn_ptr(def: FnDef, args: &GenericArgs) -> Result { + pub fn resolve_for_fn_ptr(def: FnDef, args: &GenericArgs) -> Result { with(|context| { - context.resolve_for_fn_ptr(def, args).ok_or_else(|| { - crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) - }) + context + .resolve_for_fn_ptr(def, args) + .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`"))) }) } @@ -144,11 +145,11 @@ impl Instance { def: ClosureDef, args: &GenericArgs, kind: ClosureKind, - ) -> Result { + ) -> Result { with(|context| { - context.resolve_closure(def, args, kind).ok_or_else(|| { - crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) - }) + context + .resolve_closure(def, args, kind) + .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`"))) }) } @@ -162,10 +163,7 @@ impl Instance { /// When generating code for a Drop terminator, users can ignore an empty drop glue. /// These shims are only needed to generate a valid Drop call done via VTable. pub fn is_empty_shim(&self) -> bool { - self.kind == InstanceKind::Shim - && with(|cx| { - cx.is_empty_drop_shim(self.def) || cx.is_empty_async_drop_ctor_shim(self.def) - }) + self.kind == InstanceKind::Shim && with(|cx| cx.is_empty_drop_shim(self.def)) } /// Try to constant evaluate the instance into a constant with the given type. @@ -195,7 +193,7 @@ impl Debug for Instance { /// Try to convert a crate item into an instance. /// The item cannot be generic in order to be converted into an instance. impl TryFrom for Instance { - type Error = crate::Error; + type Error = stable_mir::Error; fn try_from(item: CrateItem) -> Result { with(|context| { @@ -212,7 +210,7 @@ impl TryFrom for Instance { /// Try to convert an instance into a crate item. /// Only user defined instances can be converted. impl TryFrom for CrateItem { - type Error = crate::Error; + type Error = stable_mir::Error; fn try_from(value: Instance) -> Result { with(|context| { @@ -259,7 +257,7 @@ crate_def! { } impl TryFrom for StaticDef { - type Error = crate::Error; + type Error = stable_mir::Error; fn try_from(value: CrateItem) -> Result { if matches!(value.kind(), ItemKind::Static) { @@ -271,7 +269,7 @@ impl TryFrom for StaticDef { } impl TryFrom for StaticDef { - type Error = crate::Error; + type Error = stable_mir::Error; fn try_from(value: Instance) -> Result { StaticDef::try_from(CrateItem::try_from(value)?) diff --git a/compiler/rustc_smir/src/stable_mir/mir/pretty.rs b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs new file mode 100644 index 0000000000000..ba20651f993d1 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs @@ -0,0 +1,466 @@ +//! Implement methods to pretty print stable MIR body. +use std::fmt::Debug; +use std::io::Write; +use std::{fmt, io, iter}; + +use fmt::{Display, Formatter}; +use stable_mir::mir::{ + Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents, +}; +use stable_mir::ty::{AdtKind, AssocKind, IndexedVal, MirConst, Ty, TyConst}; +use stable_mir::{Body, CrateDef, Mutability, with}; + +use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind}; +use crate::stable_mir; + +impl Display for Ty { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + with(|ctx| write!(f, "{}", ctx.ty_pretty(*self))) + } +} + +impl Display for AssocKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + AssocKind::Fn { has_self: true, .. } => write!(f, "method"), + AssocKind::Fn { has_self: false, .. } => write!(f, "associated function"), + AssocKind::Const { .. } => write!(f, "associated const"), + AssocKind::Type { .. } => write!(f, "associated type"), + } + } +} + +impl Debug for Place { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + with(|ctx| write!(f, "{}", ctx.place_pretty(self))) + } +} + +pub(crate) fn function_body(writer: &mut W, body: &Body, name: &str) -> io::Result<()> { + write!(writer, "fn {name}(")?; + let mut sep = ""; + for (index, local) in body.arg_locals().iter().enumerate() { + write!(writer, "{}_{}: {}", sep, index + 1, local.ty)?; + sep = ", "; + } + write!(writer, ")")?; + + let return_local = body.ret_local(); + writeln!(writer, " -> {} {{", return_local.ty)?; + + body.locals().iter().enumerate().try_for_each(|(index, local)| -> io::Result<()> { + if index == 0 || index > body.arg_count { + writeln!(writer, " let {}_{}: {};", pretty_mut(local.mutability), index, local.ty) + } else { + Ok(()) + } + })?; + + body.var_debug_info.iter().try_for_each(|info| { + let content = match &info.value { + VarDebugInfoContents::Place(place) => { + format!("{place:?}") + } + VarDebugInfoContents::Const(constant) => pretty_mir_const(&constant.const_), + }; + writeln!(writer, " debug {} => {};", info.name, content) + })?; + + body.blocks + .iter() + .enumerate() + .map(|(index, block)| -> io::Result<()> { + writeln!(writer, " bb{index}: {{")?; + let _ = block + .statements + .iter() + .map(|statement| -> io::Result<()> { + pretty_statement(writer, &statement.kind)?; + Ok(()) + }) + .collect::>(); + pretty_terminator(writer, &block.terminator.kind)?; + writeln!(writer, " }}").unwrap(); + Ok(()) + }) + .collect::, _>>()?; + writeln!(writer, "}}")?; + Ok(()) +} + +fn pretty_statement(writer: &mut W, statement: &StatementKind) -> io::Result<()> { + const INDENT: &str = " "; + match statement { + StatementKind::Assign(place, rval) => { + write!(writer, "{INDENT}{place:?} = ")?; + pretty_rvalue(writer, rval)?; + writeln!(writer, ";") + } + // FIXME: Add rest of the statements + StatementKind::FakeRead(cause, place) => { + writeln!(writer, "{INDENT}FakeRead({cause:?}, {place:?});") + } + StatementKind::SetDiscriminant { place, variant_index } => { + writeln!(writer, "{INDENT}discriminant({place:?} = {};", variant_index.to_index()) + } + StatementKind::Deinit(place) => writeln!(writer, "Deinit({place:?};"), + StatementKind::StorageLive(local) => { + writeln!(writer, "{INDENT}StorageLive(_{local});") + } + StatementKind::StorageDead(local) => { + writeln!(writer, "{INDENT}StorageDead(_{local});") + } + StatementKind::Retag(kind, place) => writeln!(writer, "Retag({kind:?}, {place:?});"), + StatementKind::PlaceMention(place) => { + writeln!(writer, "{INDENT}PlaceMention({place:?};") + } + StatementKind::ConstEvalCounter => { + writeln!(writer, "{INDENT}ConstEvalCounter;") + } + StatementKind::Nop => writeln!(writer, "{INDENT}nop;"), + StatementKind::AscribeUserType { .. } + | StatementKind::Coverage(_) + | StatementKind::Intrinsic(_) => { + // FIX-ME: Make them pretty. + writeln!(writer, "{INDENT}{statement:?};") + } + } +} + +fn pretty_terminator(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { + pretty_terminator_head(writer, terminator)?; + let successors = terminator.successors(); + let successor_count = successors.len(); + let labels = pretty_successor_labels(terminator); + + let show_unwind = !matches!(terminator.unwind(), None | Some(UnwindAction::Cleanup(_))); + let fmt_unwind = |w: &mut W| -> io::Result<()> { + write!(w, "unwind ")?; + match terminator.unwind() { + None | Some(UnwindAction::Cleanup(_)) => unreachable!(), + Some(UnwindAction::Continue) => write!(w, "continue"), + Some(UnwindAction::Unreachable) => write!(w, "unreachable"), + Some(UnwindAction::Terminate) => write!(w, "terminate"), + } + }; + + match (successor_count, show_unwind) { + (0, false) => {} + (0, true) => { + write!(writer, " -> ")?; + fmt_unwind(writer)?; + } + (1, false) => write!(writer, " -> bb{:?}", successors[0])?, + _ => { + write!(writer, " -> [")?; + for (i, target) in successors.iter().enumerate() { + if i > 0 { + write!(writer, ", ")?; + } + write!(writer, "{}: bb{:?}", labels[i], target)?; + } + if show_unwind { + write!(writer, ", ")?; + fmt_unwind(writer)?; + } + write!(writer, "]")?; + } + }; + + writeln!(writer, ";") +} + +fn pretty_terminator_head(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { + use self::TerminatorKind::*; + const INDENT: &str = " "; + match terminator { + Goto { .. } => write!(writer, "{INDENT}goto"), + SwitchInt { discr, .. } => { + write!(writer, "{INDENT}switchInt({})", pretty_operand(discr)) + } + Resume => write!(writer, "{INDENT}resume"), + Abort => write!(writer, "{INDENT}abort"), + Return => write!(writer, "{INDENT}return"), + Unreachable => write!(writer, "{INDENT}unreachable"), + Drop { place, .. } => write!(writer, "{INDENT}drop({place:?})"), + Call { func, args, destination, .. } => { + write!(writer, "{INDENT}{:?} = {}(", destination, pretty_operand(func))?; + let mut args_iter = args.iter(); + args_iter.next().map_or(Ok(()), |arg| write!(writer, "{}", pretty_operand(arg)))?; + args_iter.try_for_each(|arg| write!(writer, ", {}", pretty_operand(arg)))?; + write!(writer, ")") + } + Assert { cond, expected, msg, target: _, unwind: _ } => { + write!(writer, "{INDENT}assert(")?; + if !expected { + write!(writer, "!")?; + } + write!(writer, "{}, ", pretty_operand(cond))?; + pretty_assert_message(writer, msg)?; + write!(writer, ")") + } + InlineAsm { .. } => write!(writer, "{INDENT}InlineAsm"), + } +} + +fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec { + use self::TerminatorKind::*; + match terminator { + Call { target: None, unwind: UnwindAction::Cleanup(_), .. } + | InlineAsm { destination: None, .. } => vec!["unwind".into()], + Resume | Abort | Return | Unreachable | Call { target: None, unwind: _, .. } => vec![], + Goto { .. } => vec!["".to_string()], + SwitchInt { targets, .. } => targets + .branches() + .map(|(val, _target)| format!("{val}")) + .chain(iter::once("otherwise".into())) + .collect(), + Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()], + Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => { + vec!["return".into(), "unwind".into()] + } + Drop { unwind: _, .. } | Call { target: Some(_), unwind: _, .. } => vec!["return".into()], + Assert { unwind: UnwindAction::Cleanup(_), .. } => { + vec!["success".into(), "unwind".into()] + } + Assert { unwind: _, .. } => vec!["success".into()], + InlineAsm { destination: Some(_), .. } => vec!["goto".into(), "unwind".into()], + } +} + +fn pretty_assert_message(writer: &mut W, msg: &AssertMessage) -> io::Result<()> { + match msg { + AssertMessage::BoundsCheck { len, index } => { + let pretty_len = pretty_operand(len); + let pretty_index = pretty_operand(index); + write!( + writer, + "\"index out of bounds: the length is {{}} but the index is {{}}\", {pretty_len}, {pretty_index}" + ) + } + AssertMessage::Overflow(BinOp::Add, l, r) => { + let pretty_l = pretty_operand(l); + let pretty_r = pretty_operand(r); + write!( + writer, + "\"attempt to compute `{{}} + {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) + } + AssertMessage::Overflow(BinOp::Sub, l, r) => { + let pretty_l = pretty_operand(l); + let pretty_r = pretty_operand(r); + write!( + writer, + "\"attempt to compute `{{}} - {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) + } + AssertMessage::Overflow(BinOp::Mul, l, r) => { + let pretty_l = pretty_operand(l); + let pretty_r = pretty_operand(r); + write!( + writer, + "\"attempt to compute `{{}} * {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) + } + AssertMessage::Overflow(BinOp::Div, l, r) => { + let pretty_l = pretty_operand(l); + let pretty_r = pretty_operand(r); + write!( + writer, + "\"attempt to compute `{{}} / {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) + } + AssertMessage::Overflow(BinOp::Rem, l, r) => { + let pretty_l = pretty_operand(l); + let pretty_r = pretty_operand(r); + write!( + writer, + "\"attempt to compute `{{}} % {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) + } + AssertMessage::Overflow(BinOp::Shr, _, r) => { + let pretty_r = pretty_operand(r); + write!(writer, "\"attempt to shift right by `{{}}`, which would overflow\", {pretty_r}") + } + AssertMessage::Overflow(BinOp::Shl, _, r) => { + let pretty_r = pretty_operand(r); + write!(writer, "\"attempt to shift left by `{{}}`, which would overflow\", {pretty_r}") + } + AssertMessage::Overflow(op, _, _) => unreachable!("`{:?}` cannot overflow", op), + AssertMessage::OverflowNeg(op) => { + let pretty_op = pretty_operand(op); + write!(writer, "\"attempt to negate `{{}}`, which would overflow\", {pretty_op}") + } + AssertMessage::DivisionByZero(op) => { + let pretty_op = pretty_operand(op); + write!(writer, "\"attempt to divide `{{}}` by zero\", {pretty_op}") + } + AssertMessage::RemainderByZero(op) => { + let pretty_op = pretty_operand(op); + write!( + writer, + "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {pretty_op}" + ) + } + AssertMessage::MisalignedPointerDereference { required, found } => { + let pretty_required = pretty_operand(required); + let pretty_found = pretty_operand(found); + write!( + writer, + "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}" + ) + } + AssertMessage::NullPointerDereference => { + write!(writer, "\"null pointer dereference occurred\"") + } + AssertMessage::ResumedAfterReturn(_) + | AssertMessage::ResumedAfterPanic(_) + | AssertMessage::ResumedAfterDrop(_) => { + write!(writer, "{}", msg.description().unwrap()) + } + } +} + +fn pretty_operand(operand: &Operand) -> String { + match operand { + Operand::Copy(copy) => { + format!("{copy:?}") + } + Operand::Move(mv) => { + format!("move {mv:?}") + } + Operand::Constant(cnst) => pretty_mir_const(&cnst.const_), + } +} + +fn pretty_mir_const(literal: &MirConst) -> String { + with(|cx| cx.mir_const_pretty(literal)) +} + +fn pretty_ty_const(ct: &TyConst) -> String { + with(|cx| cx.ty_const_pretty(ct.id)) +} + +fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { + match rval { + Rvalue::AddressOf(mutability, place) => { + write!(writer, "&raw {} {:?}", pretty_raw_ptr_kind(*mutability), place) + } + Rvalue::Aggregate(aggregate_kind, operands) => { + // FIXME: Add pretty_aggregate function that returns a pretty string + pretty_aggregate(writer, aggregate_kind, operands) + } + Rvalue::BinaryOp(bin, op1, op2) => { + write!(writer, "{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2)) + } + Rvalue::Cast(_, op, ty) => { + write!(writer, "{} as {}", pretty_operand(op), ty) + } + Rvalue::CheckedBinaryOp(bin, op1, op2) => { + write!(writer, "Checked{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2)) + } + Rvalue::CopyForDeref(deref) => { + write!(writer, "CopyForDeref({deref:?})") + } + Rvalue::Discriminant(place) => { + write!(writer, "discriminant({place:?})") + } + Rvalue::Len(len) => { + write!(writer, "len({len:?})") + } + Rvalue::Ref(_, borrowkind, place) => { + let kind = match borrowkind { + BorrowKind::Shared => "&", + BorrowKind::Fake(FakeBorrowKind::Deep) => "&fake ", + BorrowKind::Fake(FakeBorrowKind::Shallow) => "&fake shallow ", + BorrowKind::Mut { .. } => "&mut ", + }; + write!(writer, "{kind}{place:?}") + } + Rvalue::Repeat(op, cnst) => { + write!(writer, "[{}; {}]", pretty_operand(op), pretty_ty_const(cnst)) + } + Rvalue::ShallowInitBox(_, _) => Ok(()), + Rvalue::ThreadLocalRef(item) => { + write!(writer, "thread_local_ref{item:?}") + } + Rvalue::NullaryOp(nul, ty) => { + write!(writer, "{nul:?}::<{ty}>() \" \"") + } + Rvalue::UnaryOp(un, op) => { + write!(writer, "{:?}({})", un, pretty_operand(op)) + } + Rvalue::Use(op) => write!(writer, "{}", pretty_operand(op)), + } +} + +fn pretty_aggregate( + writer: &mut W, + aggregate_kind: &AggregateKind, + operands: &Vec, +) -> io::Result<()> { + let suffix = match aggregate_kind { + AggregateKind::Array(_) => { + write!(writer, "[")?; + "]" + } + AggregateKind::Tuple => { + write!(writer, "(")?; + ")" + } + AggregateKind::Adt(def, var, _, _, _) => { + if def.kind() == AdtKind::Enum { + write!(writer, "{}::{}", def.name(), def.variant(*var).unwrap().name())?; + } else { + write!(writer, "{}", def.variant(*var).unwrap().name())?; + } + if operands.is_empty() { + return Ok(()); + } + // FIXME: Change this once we have CtorKind in StableMIR. + write!(writer, "(")?; + ")" + } + AggregateKind::Closure(def, _) => { + write!(writer, "{{closure@{}}}(", def.span().diagnostic())?; + ")" + } + AggregateKind::Coroutine(def, _, _) => { + write!(writer, "{{coroutine@{}}}(", def.span().diagnostic())?; + ")" + } + AggregateKind::CoroutineClosure(def, _) => { + write!(writer, "{{coroutine-closure@{}}}(", def.span().diagnostic())?; + ")" + } + AggregateKind::RawPtr(ty, mutability) => { + write!( + writer, + "*{} {ty} from (", + if *mutability == Mutability::Mut { "mut" } else { "const" } + )?; + ")" + } + }; + let mut separator = ""; + for op in operands { + write!(writer, "{}{}", separator, pretty_operand(op))?; + separator = ", "; + } + write!(writer, "{suffix}") +} + +fn pretty_mut(mutability: Mutability) -> &'static str { + match mutability { + Mutability::Not => " ", + Mutability::Mut => "mut ", + } +} + +fn pretty_raw_ptr_kind(kind: RawPtrKind) -> &'static str { + match kind { + RawPtrKind::Const => "const", + RawPtrKind::Mut => "mut", + RawPtrKind::FakeForPtrMetadata => "const (fake)", + } +} diff --git a/compiler/rustc_smir/src/stable_mir/mir/visit.rs b/compiler/rustc_smir/src/stable_mir/mir/visit.rs new file mode 100644 index 0000000000000..e21dc11eea9ca --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/mir/visit.rs @@ -0,0 +1,589 @@ +//! # The Stable MIR Visitor +//! +//! ## Overview +//! +//! We currently only support an immutable visitor. +//! The structure of this visitor is similar to the ones internal to `rustc`, +//! and it follows the following conventions: +//! +//! For every mir item, the trait has a `visit_` and a `super_` method. +//! - `visit_`, by default, calls `super_` +//! - `super_`, by default, destructures the `` and calls `visit_` for +//! all sub-items that compose the original item. +//! +//! In order to implement a visitor, override the `visit_*` methods for the types you are +//! interested in analyzing, and invoke (within that method call) +//! `self.super_*` to continue to the traverse. +//! Avoid calling `super` methods in other circumstances. +//! +//! 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. +//! +//! Use pattern matching to reference fields and ensure that all +//! matches are exhaustive. +//! +//! 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. + +use stable_mir::mir::*; +use stable_mir::ty::{GenericArgs, MirConst, Region, Ty, TyConst}; +use stable_mir::{Error, Opaque, Span}; + +use crate::stable_mir; + +macro_rules! make_mir_visitor { + ($visitor_trait_name:ident, $($mutability:ident)?) => { + pub trait $visitor_trait_name { + fn visit_body(&mut self, body: &$($mutability)? Body) { + self.super_body(body) + } + + fn visit_basic_block(&mut self, bb: &$($mutability)? BasicBlock) { + self.super_basic_block(bb) + } + + fn visit_ret_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { + self.super_ret_decl(local, decl) + } + + fn visit_arg_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { + self.super_arg_decl(local, decl) + } + + fn visit_local_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { + self.super_local_decl(local, decl) + } + + fn visit_statement(&mut self, stmt: &$($mutability)? Statement, location: Location) { + self.super_statement(stmt, location) + } + + fn visit_terminator(&mut self, term: &$($mutability)? Terminator, location: Location) { + self.super_terminator(term, location) + } + + fn visit_span(&mut self, span: &$($mutability)? Span) { + self.super_span(span) + } + + fn visit_place(&mut self, place: &$($mutability)? Place, ptx: PlaceContext, location: Location) { + self.super_place(place, ptx, location) + } + + visit_place_fns!($($mutability)?); + + fn visit_local(&mut self, local: &$($mutability)? Local, ptx: PlaceContext, location: Location) { + let _ = (local, ptx, location); + } + + fn visit_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) { + self.super_rvalue(rvalue, location) + } + + fn visit_operand(&mut self, operand: &$($mutability)? Operand, location: Location) { + self.super_operand(operand, location) + } + + fn visit_user_type_projection(&mut self, projection: &$($mutability)? UserTypeProjection) { + self.super_user_type_projection(projection) + } + + fn visit_ty(&mut self, ty: &$($mutability)? Ty, location: Location) { + let _ = location; + self.super_ty(ty) + } + + fn visit_const_operand(&mut self, constant: &$($mutability)? ConstOperand, location: Location) { + self.super_const_operand(constant, location) + } + + fn visit_mir_const(&mut self, constant: &$($mutability)? MirConst, location: Location) { + self.super_mir_const(constant, location) + } + + fn visit_ty_const(&mut self, constant: &$($mutability)? TyConst, location: Location) { + let _ = location; + self.super_ty_const(constant) + } + + fn visit_region(&mut self, region: &$($mutability)? Region, location: Location) { + let _ = location; + self.super_region(region) + } + + fn visit_args(&mut self, args: &$($mutability)? GenericArgs, location: Location) { + let _ = location; + self.super_args(args) + } + + fn visit_assert_msg(&mut self, msg: &$($mutability)? AssertMessage, location: Location) { + self.super_assert_msg(msg, location) + } + + fn visit_var_debug_info(&mut self, var_debug_info: &$($mutability)? VarDebugInfo) { + self.super_var_debug_info(var_debug_info); + } + + fn super_body(&mut self, body: &$($mutability)? Body) { + super_body!(self, body, $($mutability)?); + } + + fn super_basic_block(&mut self, bb: &$($mutability)? BasicBlock) { + let BasicBlock { statements, terminator } = bb; + for stmt in statements { + self.visit_statement(stmt, Location(stmt.span)); + } + self.visit_terminator(terminator, Location(terminator.span)); + } + + fn super_local_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { + let _ = local; + let LocalDecl { ty, span, .. } = decl; + self.visit_ty(ty, Location(*span)); + } + + fn super_ret_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { + self.super_local_decl(local, decl) + } + + fn super_arg_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { + self.super_local_decl(local, decl) + } + + fn super_statement(&mut self, stmt: &$($mutability)? Statement, location: Location) { + let Statement { kind, span } = stmt; + self.visit_span(span); + match kind { + StatementKind::Assign(place, rvalue) => { + self.visit_place(place, PlaceContext::MUTATING, location); + self.visit_rvalue(rvalue, location); + } + StatementKind::FakeRead(_, place) | StatementKind::PlaceMention(place) => { + self.visit_place(place, PlaceContext::NON_MUTATING, location); + } + StatementKind::SetDiscriminant { place, .. } + | StatementKind::Deinit(place) + | StatementKind::Retag(_, place) => { + self.visit_place(place, PlaceContext::MUTATING, location); + } + StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { + self.visit_local(local, PlaceContext::NON_USE, location); + } + StatementKind::AscribeUserType { place, projections, variance: _ } => { + self.visit_place(place, PlaceContext::NON_USE, location); + self.visit_user_type_projection(projections); + } + StatementKind::Coverage(coverage) => visit_opaque(coverage), + StatementKind::Intrinsic(intrisic) => match intrisic { + NonDivergingIntrinsic::Assume(operand) => { + self.visit_operand(operand, location); + } + NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + src, + dst, + count, + }) => { + self.visit_operand(src, location); + self.visit_operand(dst, location); + self.visit_operand(count, location); + } + }, + StatementKind::ConstEvalCounter | StatementKind::Nop => {} + } + } + + fn super_terminator(&mut self, term: &$($mutability)? Terminator, location: Location) { + let Terminator { kind, span } = term; + self.visit_span(span); + match kind { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Unreachable => {} + TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { + self.visit_operand(cond, location); + self.visit_assert_msg(msg, location); + } + TerminatorKind::Drop { place, target: _, unwind: _ } => { + self.visit_place(place, PlaceContext::MUTATING, location); + } + TerminatorKind::Call { func, args, destination, target: _, unwind: _ } => { + self.visit_operand(func, location); + for arg in args { + self.visit_operand(arg, location); + } + self.visit_place(destination, PlaceContext::MUTATING, location); + } + TerminatorKind::InlineAsm { operands, .. } => { + for op in operands { + let InlineAsmOperand { in_value, out_place, raw_rpr: _ } = op; + if let Some(input) = in_value { + self.visit_operand(input, location); + } + if let Some(output) = out_place { + self.visit_place(output, PlaceContext::MUTATING, location); + } + } + } + TerminatorKind::Return => { + let $($mutability)? local = RETURN_LOCAL; + self.visit_local(&$($mutability)? local, PlaceContext::NON_MUTATING, location); + } + TerminatorKind::SwitchInt { discr, targets: _ } => { + self.visit_operand(discr, location); + } + } + } + + fn super_span(&mut self, span: &$($mutability)? Span) { + let _ = span; + } + + fn super_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) { + match rvalue { + Rvalue::AddressOf(mutability, place) => { + let pcx = PlaceContext { is_mut: *mutability == RawPtrKind::Mut }; + self.visit_place(place, pcx, location); + } + Rvalue::Aggregate(_, operands) => { + for op in operands { + self.visit_operand(op, location); + } + } + Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { + self.visit_operand(lhs, location); + self.visit_operand(rhs, location); + } + Rvalue::Cast(_, op, ty) => { + self.visit_operand(op, location); + self.visit_ty(ty, location); + } + Rvalue::CopyForDeref(place) | Rvalue::Discriminant(place) | Rvalue::Len(place) => { + self.visit_place(place, PlaceContext::NON_MUTATING, location); + } + Rvalue::Ref(region, kind, place) => { + self.visit_region(region, location); + let pcx = PlaceContext { is_mut: matches!(kind, BorrowKind::Mut { .. }) }; + self.visit_place(place, pcx, location); + } + Rvalue::Repeat(op, constant) => { + self.visit_operand(op, location); + self.visit_ty_const(constant, location); + } + Rvalue::ShallowInitBox(op, ty) => { + self.visit_ty(ty, location); + self.visit_operand(op, location) + } + Rvalue::ThreadLocalRef(_) => {} + Rvalue::NullaryOp(_, ty) => { + self.visit_ty(ty, location); + } + Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => { + self.visit_operand(op, location); + } + } + } + + fn super_operand(&mut self, operand: &$($mutability)? Operand, location: Location) { + match operand { + Operand::Copy(place) | Operand::Move(place) => { + self.visit_place(place, PlaceContext::NON_MUTATING, location) + } + Operand::Constant(constant) => { + self.visit_const_operand(constant, location); + } + } + } + + fn super_user_type_projection(&mut self, projection: &$($mutability)? UserTypeProjection) { + // This is a no-op on mir::Visitor. + let _ = projection; + } + + fn super_ty(&mut self, ty: &$($mutability)? Ty) { + let _ = ty; + } + + fn super_const_operand(&mut self, constant: &$($mutability)? ConstOperand, location: Location) { + let ConstOperand { span, user_ty: _, const_ } = constant; + self.visit_span(span); + self.visit_mir_const(const_, location); + } + + fn super_mir_const(&mut self, constant: &$($mutability)? MirConst, location: Location) { + let MirConst { kind: _, ty, id: _ } = constant; + self.visit_ty(ty, location); + } + + fn super_ty_const(&mut self, constant: &$($mutability)? TyConst) { + let _ = constant; + } + + fn super_region(&mut self, region: &$($mutability)? Region) { + let _ = region; + } + + fn super_args(&mut self, args: &$($mutability)? GenericArgs) { + let _ = args; + } + + fn super_var_debug_info(&mut self, var_debug_info: &$($mutability)? VarDebugInfo) { + let VarDebugInfo { source_info, composite, value, name: _, argument_index: _ } = + var_debug_info; + self.visit_span(&$($mutability)? source_info.span); + let location = Location(source_info.span); + if let Some(composite) = composite { + self.visit_ty(&$($mutability)? composite.ty, location); + } + match value { + VarDebugInfoContents::Place(place) => { + self.visit_place(place, PlaceContext::NON_USE, location); + } + VarDebugInfoContents::Const(constant) => { + self.visit_mir_const(&$($mutability)? constant.const_, location); + } + } + } + + fn super_assert_msg(&mut self, msg: &$($mutability)? AssertMessage, location: Location) { + match msg { + AssertMessage::BoundsCheck { len, index } => { + self.visit_operand(len, location); + self.visit_operand(index, location); + } + AssertMessage::Overflow(_, left, right) => { + self.visit_operand(left, location); + self.visit_operand(right, location); + } + AssertMessage::OverflowNeg(op) + | AssertMessage::DivisionByZero(op) + | AssertMessage::RemainderByZero(op) => { + self.visit_operand(op, location); + } + AssertMessage::ResumedAfterReturn(_) + | AssertMessage::ResumedAfterPanic(_) + | AssertMessage::NullPointerDereference + | AssertMessage::ResumedAfterDrop(_) => { + //nothing to visit + } + AssertMessage::MisalignedPointerDereference { required, found } => { + self.visit_operand(required, location); + self.visit_operand(found, location); + } + } + } + } + }; +} + +macro_rules! super_body { + ($self:ident, $body:ident, mut) => { + for bb in $body.blocks.iter_mut() { + $self.visit_basic_block(bb); + } + + $self.visit_ret_decl(RETURN_LOCAL, $body.ret_local_mut()); + + for (idx, arg) in $body.arg_locals_mut().iter_mut().enumerate() { + $self.visit_arg_decl(idx + 1, arg) + } + + let local_start = $body.arg_count + 1; + for (idx, arg) in $body.inner_locals_mut().iter_mut().enumerate() { + $self.visit_local_decl(idx + local_start, arg) + } + + for info in $body.var_debug_info.iter_mut() { + $self.visit_var_debug_info(info); + } + + $self.visit_span(&mut $body.span) + }; + + ($self:ident, $body:ident, ) => { + let Body { blocks, locals: _, arg_count, var_debug_info, spread_arg: _, span } = $body; + + for bb in blocks { + $self.visit_basic_block(bb); + } + + $self.visit_ret_decl(RETURN_LOCAL, $body.ret_local()); + + for (idx, arg) in $body.arg_locals().iter().enumerate() { + $self.visit_arg_decl(idx + 1, arg) + } + + let local_start = arg_count + 1; + for (idx, arg) in $body.inner_locals().iter().enumerate() { + $self.visit_local_decl(idx + local_start, arg) + } + + for info in var_debug_info.iter() { + $self.visit_var_debug_info(info); + } + + $self.visit_span(span) + }; +} + +macro_rules! visit_place_fns { + (mut) => { + fn super_place(&mut self, place: &mut Place, ptx: PlaceContext, location: Location) { + self.visit_local(&mut place.local, ptx, location); + + for elem in place.projection.iter_mut() { + self.visit_projection_elem(elem, ptx, location); + } + } + + // We don't have to replicate the `process_projection()` like we did in + // `rustc_middle::mir::visit.rs` here because the `projection` field in `Place` + // of Stable-MIR is not an immutable borrow, unlike in `Place` of MIR. + fn visit_projection_elem( + &mut self, + elem: &mut ProjectionElem, + ptx: PlaceContext, + location: Location, + ) { + self.super_projection_elem(elem, ptx, location) + } + + fn super_projection_elem( + &mut self, + elem: &mut ProjectionElem, + ptx: PlaceContext, + location: Location, + ) { + match elem { + ProjectionElem::Deref => {} + ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location), + ProjectionElem::Index(local) => self.visit_local(local, ptx, location), + ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {} + ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {} + ProjectionElem::Downcast(_idx) => {} + ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location), + ProjectionElem::Subtype(ty) => self.visit_ty(ty, location), + } + } + }; + + () => { + fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) { + self.visit_local(&place.local, ptx, location); + + for (idx, elem) in place.projection.iter().enumerate() { + let place_ref = + PlaceRef { local: place.local, projection: &place.projection[..idx] }; + self.visit_projection_elem(place_ref, elem, ptx, location); + } + } + + fn visit_projection_elem<'a>( + &mut self, + place_ref: PlaceRef<'a>, + elem: &ProjectionElem, + ptx: PlaceContext, + location: Location, + ) { + let _ = place_ref; + self.super_projection_elem(elem, ptx, location); + } + + fn super_projection_elem( + &mut self, + elem: &ProjectionElem, + ptx: PlaceContext, + location: Location, + ) { + match elem { + ProjectionElem::Deref => {} + ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location), + ProjectionElem::Index(local) => self.visit_local(local, ptx, location), + ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {} + ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {} + ProjectionElem::Downcast(_idx) => {} + ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location), + ProjectionElem::Subtype(ty) => self.visit_ty(ty, location), + } + } + }; +} + +make_mir_visitor!(MirVisitor,); +make_mir_visitor!(MutMirVisitor, mut); + +/// This function is a no-op that gets used to ensure this visitor is kept up-to-date. +/// +/// The idea is that whenever we replace an Opaque type by a real type, the compiler will fail +/// when trying to invoke `visit_opaque`. +/// +/// If you are here because your compilation is broken, replace the failing call to `visit_opaque()` +/// by a `visit_` for your construct. +fn visit_opaque(_: &Opaque) {} + +/// The location of a statement / terminator in the code and the CFG. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct Location(Span); + +impl Location { + pub fn span(&self) -> Span { + self.0 + } +} + +/// Location of the statement at the given index for a given basic block. Assumes that `stmt_idx` +/// and `bb_idx` are valid for a given body. +pub fn statement_location(body: &Body, bb_idx: &BasicBlockIdx, stmt_idx: usize) -> Location { + let bb = &body.blocks[*bb_idx]; + let stmt = &bb.statements[stmt_idx]; + Location(stmt.span) +} + +/// Location of the terminator for a given basic block. Assumes that `bb_idx` is valid for a given +/// body. +pub fn terminator_location(body: &Body, bb_idx: &BasicBlockIdx) -> Location { + let bb = &body.blocks[*bb_idx]; + let terminator = &bb.terminator; + Location(terminator.span) +} + +/// Reference to a place used to represent a partial projection. +pub struct PlaceRef<'a> { + pub local: Local, + pub projection: &'a [ProjectionElem], +} + +impl PlaceRef<'_> { + /// Get the type of this place. + pub fn ty(&self, locals: &[LocalDecl]) -> Result { + self.projection.iter().try_fold(locals[self.local].ty, |place_ty, elem| elem.ty(place_ty)) + } +} + +/// Information about a place's usage. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct PlaceContext { + /// Whether the access is mutable or not. Keep this private so we can increment the type in a + /// backward compatible manner. + is_mut: bool, +} + +impl PlaceContext { + const MUTATING: Self = PlaceContext { is_mut: true }; + const NON_MUTATING: Self = PlaceContext { is_mut: false }; + const NON_USE: Self = PlaceContext { is_mut: false }; + + pub fn is_mutating(&self) -> bool { + self.is_mut + } +} diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs new file mode 100644 index 0000000000000..c59758d4ad3f2 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/mod.rs @@ -0,0 +1,239 @@ +//! Module that is temporarily parasitic on the `rustc_smir` crate, +//! +//! This module is designed to resolve circular dependency that would happen +//! if we gradually invert the dependency order between `rustc_smir` and `stable_mir`. +//! +//! Once refactoring is complete, we will migrate it back to the `stable_mir` crate. + +//! The WIP stable interface to rustc internals. +//! +//! For more information see +//! +//! # Note +//! +//! This API is still completely unstable and subject to change. + +// #![doc( +// html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", +// test(attr(allow(unused_variables), deny(warnings))) +// )] +//! +//! This crate shall contain all type definitions and APIs that we expect third-party tools to invoke to +//! interact with the compiler. +//! +//! The goal is to eventually be published on +//! [crates.io](https://crates.io). + +use std::fmt::Debug; +use std::{fmt, io}; + +use serde::Serialize; +use stable_mir::compiler_interface::with; +pub use stable_mir::crate_def::{CrateDef, CrateDefItems, CrateDefType, DefId}; +pub use stable_mir::error::*; +use stable_mir::mir::mono::StaticDef; +use stable_mir::mir::{Body, Mutability}; +use stable_mir::ty::{AssocItem, FnDef, ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty}; + +use crate::stable_mir; + +pub mod abi; +#[macro_use] +pub mod crate_def; +pub mod compiler_interface; +#[macro_use] +pub mod error; +pub mod mir; +pub mod target; +pub mod ty; +pub mod visitor; + +/// Use String for now but we should replace it. +pub type Symbol = String; + +/// The number that identifies a crate. +pub type CrateNum = usize; + +impl Debug for DefId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DefId").field("id", &self.0).field("name", &self.name()).finish() + } +} + +impl IndexedVal for DefId { + fn to_val(index: usize) -> Self { + DefId(index) + } + + fn to_index(&self) -> usize { + self.0 + } +} + +/// A list of crate items. +pub type CrateItems = Vec; + +/// A list of trait decls. +pub type TraitDecls = Vec; + +/// A list of impl trait decls. +pub type ImplTraitDecls = Vec; + +/// A list of associated items. +pub type AssocItems = Vec; + +/// Holds information about a crate. +#[derive(Clone, PartialEq, Eq, Debug, Serialize)] +pub struct Crate { + pub id: CrateNum, + pub name: Symbol, + pub is_local: bool, +} + +impl Crate { + /// The list of foreign modules in this crate. + pub fn foreign_modules(&self) -> Vec { + with(|cx| cx.foreign_modules(self.id)) + } + + /// The list of traits declared in this crate. + pub fn trait_decls(&self) -> TraitDecls { + with(|cx| cx.trait_decls(self.id)) + } + + /// The list of trait implementations in this crate. + pub fn trait_impls(&self) -> ImplTraitDecls { + with(|cx| cx.trait_impls(self.id)) + } + + /// Return a list of function definitions from this crate independent on their visibility. + pub fn fn_defs(&self) -> Vec { + with(|cx| cx.crate_functions(self.id)) + } + + /// Return a list of static items defined in this crate independent on their visibility. + pub fn statics(&self) -> Vec { + with(|cx| cx.crate_statics(self.id)) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)] +pub enum ItemKind { + Fn, + Static, + Const, + Ctor(CtorKind), +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)] +pub enum CtorKind { + Const, + Fn, +} + +pub type Filename = String; + +crate_def_with_ty! { + /// Holds information about an item in a crate. + #[derive(Serialize)] + pub CrateItem; +} + +impl CrateItem { + /// This will return the body of an item or panic if it's not available. + pub fn expect_body(&self) -> mir::Body { + with(|cx| cx.mir_body(self.0)) + } + + /// Return the body of an item if available. + pub fn body(&self) -> Option { + with(|cx| cx.has_body(self.0).then(|| cx.mir_body(self.0))) + } + + /// Check if a body is available for this item. + pub fn has_body(&self) -> bool { + with(|cx| cx.has_body(self.0)) + } + + pub fn span(&self) -> Span { + with(|cx| cx.span_of_an_item(self.0)) + } + + pub fn kind(&self) -> ItemKind { + with(|cx| cx.item_kind(*self)) + } + + pub fn requires_monomorphization(&self) -> bool { + with(|cx| cx.requires_monomorphization(self.0)) + } + + pub fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.0)) + } + + pub fn is_foreign_item(&self) -> bool { + with(|cx| cx.is_foreign_item(self.0)) + } + + /// Emit MIR for this item body. + pub fn emit_mir(&self, w: &mut W) -> io::Result<()> { + self.body() + .ok_or_else(|| io::Error::other(format!("No body found for `{}`", self.name())))? + .dump(w, &self.name()) + } +} + +/// Return the function where execution starts if the current +/// crate defines that. This is usually `main`, but could be +/// `start` if the crate is a no-std crate. +pub fn entry_fn() -> Option { + with(|cx| cx.entry_fn()) +} + +/// Access to the local crate. +pub fn local_crate() -> Crate { + with(|cx| cx.local_crate()) +} + +/// Try to find a crate or crates if multiple crates exist from given name. +pub fn find_crates(name: &str) -> Vec { + with(|cx| cx.find_crates(name)) +} + +/// Try to find a crate with the given name. +pub fn external_crates() -> Vec { + with(|cx| cx.external_crates()) +} + +/// Retrieve all items in the local crate that have a MIR associated with them. +pub fn all_local_items() -> CrateItems { + with(|cx| cx.all_local_items()) +} + +pub fn all_trait_decls() -> TraitDecls { + with(|cx| cx.all_trait_decls()) +} + +pub fn all_trait_impls() -> ImplTraitDecls { + with(|cx| cx.all_trait_impls()) +} + +/// A type that provides internal information but that can still be used for debug purpose. +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub struct Opaque(String); + +impl std::fmt::Display for Opaque { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::fmt::Debug for Opaque { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +pub fn opaque(value: &T) -> Opaque { + Opaque(format!("{value:?}")) +} diff --git a/compiler/stable_mir/src/target.rs b/compiler/rustc_smir/src/stable_mir/target.rs similarity index 95% rename from compiler/stable_mir/src/target.rs rename to compiler/rustc_smir/src/stable_mir/target.rs index 32c3a2a9122e3..6cf1e9feb015a 100644 --- a/compiler/stable_mir/src/target.rs +++ b/compiler/rustc_smir/src/stable_mir/target.rs @@ -1,8 +1,9 @@ //! Provide information about the machine that this is being compiled into. use serde::Serialize; +use stable_mir::compiler_interface::with; -use crate::compiler_interface::with; +use crate::stable_mir; /// The properties of the target machine being compiled into. #[derive(Clone, PartialEq, Eq, Serialize)] diff --git a/compiler/stable_mir/src/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs similarity index 95% rename from compiler/stable_mir/src/ty.rs rename to compiler/rustc_smir/src/stable_mir/ty.rs index b857a735b7259..e331e5934716a 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -2,15 +2,16 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::ops::Range; use serde::Serialize; +use stable_mir::abi::{FnAbi, Layout}; +use stable_mir::crate_def::{CrateDef, CrateDefItems, CrateDefType}; +use stable_mir::mir::alloc::{AllocId, read_target_int, read_target_uint}; +use stable_mir::mir::mono::StaticDef; +use stable_mir::target::MachineInfo; +use stable_mir::{Filename, Opaque}; use super::mir::{Body, Mutability, Safety}; use super::{DefId, Error, Symbol, with}; -use crate::abi::{FnAbi, Layout}; -use crate::crate_def::{CrateDef, CrateDefType}; -use crate::mir::alloc::{AllocId, read_target_int, read_target_uint}; -use crate::mir::mono::StaticDef; -use crate::target::MachineInfo; -use crate::{Filename, Opaque}; +use crate::stable_mir; #[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)] pub struct Ty(usize); @@ -588,7 +589,7 @@ pub enum IntTy { impl IntTy { pub fn num_bytes(self) -> usize { match self { - IntTy::Isize => crate::target::MachineInfo::target_pointer_width().bytes(), + IntTy::Isize => MachineInfo::target_pointer_width().bytes(), IntTy::I8 => 1, IntTy::I16 => 2, IntTy::I32 => 4, @@ -611,7 +612,7 @@ pub enum UintTy { impl UintTy { pub fn num_bytes(self) -> usize { match self { - UintTy::Usize => crate::target::MachineInfo::target_pointer_width().bytes(), + UintTy::Usize => MachineInfo::target_pointer_width().bytes(), UintTy::U8 => 1, UintTy::U16 => 2, UintTy::U32 => 4, @@ -910,6 +911,10 @@ crate_def! { pub TraitDef; } +impl_crate_def_items! { + TraitDef; +} + impl TraitDef { pub fn declaration(trait_def: &TraitDef) -> TraitDecl { with(|cx| cx.trait_decl(trait_def)) @@ -932,6 +937,10 @@ crate_def! { pub ImplDef; } +impl_crate_def_items! { + ImplDef; +} + impl ImplDef { /// Retrieve information about this implementation. pub fn trait_impl(&self) -> ImplTrait { @@ -1017,7 +1026,7 @@ pub enum AliasKind { Projection, Inherent, Opaque, - Weak, + Free, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] @@ -1084,7 +1093,6 @@ pub enum Abi { CCmseNonSecureCall, CCmseNonSecureEntry, System { unwind: bool }, - RustIntrinsic, RustCall, Unadjusted, RustCold, @@ -1454,7 +1462,7 @@ pub enum ClauseKind { TypeOutlives(TypeOutlivesPredicate), Projection(ProjectionPredicate), ConstArgHasType(TyConst, Ty), - WellFormed(GenericArgKind), + WellFormed(TermKind), ConstEvaluatable(TyConst), } @@ -1555,3 +1563,59 @@ index_impl!(Span); pub struct VariantIdx(usize); index_impl!(VariantIdx); + +crate_def! { + /// Hold infomation about an Opaque definition, particularly useful in `RPITIT`. + #[derive(Serialize)] + pub OpaqueDef; +} + +crate_def! { + #[derive(Serialize)] + pub AssocDef; +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct AssocItem { + pub def_id: AssocDef, + pub kind: AssocKind, + pub container: AssocItemContainer, + + /// If this is an item in an impl of a trait then this is the `DefId` of + /// the associated item on the trait that this implements. + pub trait_item_def_id: Option, +} + +#[derive(Clone, PartialEq, Debug, Eq, Serialize)] +pub enum AssocTypeData { + Normal(Symbol), + /// The associated type comes from an RPITIT. It has no name, and the + /// `ImplTraitInTraitData` provides additional information about its + /// source. + Rpitit(ImplTraitInTraitData), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum AssocKind { + Const { name: Symbol }, + Fn { name: Symbol, has_self: bool }, + Type { data: AssocTypeData }, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum AssocItemContainer { + Trait, + Impl, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] +pub enum ImplTraitInTraitData { + Trait { fn_def_id: FnDef, opaque_def_id: OpaqueDef }, + Impl { fn_def_id: FnDef }, +} + +impl AssocItem { + pub fn is_impl_trait_in_trait(&self) -> bool { + matches!(self.kind, AssocKind::Type { data: AssocTypeData::Rpitit(_) }) + } +} diff --git a/compiler/stable_mir/src/visitor.rs b/compiler/rustc_smir/src/stable_mir/visitor.rs similarity index 95% rename from compiler/stable_mir/src/visitor.rs rename to compiler/rustc_smir/src/stable_mir/visitor.rs index 8463174f9a468..31a53d1b19d96 100644 --- a/compiler/stable_mir/src/visitor.rs +++ b/compiler/rustc_smir/src/stable_mir/visitor.rs @@ -1,11 +1,13 @@ use std::ops::ControlFlow; +use stable_mir::Opaque; +use stable_mir::ty::TyConst; + use super::ty::{ Allocation, Binder, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs, MirConst, Promoted, Region, RigidTy, TermKind, Ty, UnevaluatedConst, }; -use crate::Opaque; -use crate::ty::TyConst; +use crate::stable_mir; pub trait Visitor: Sized { type Break; @@ -47,13 +49,13 @@ impl Visitable for TyConst { } fn super_visit(&self, visitor: &mut V) -> ControlFlow { match &self.kind { - crate::ty::TyConstKind::Param(_) | crate::ty::TyConstKind::Bound(_, _) => {} - crate::ty::TyConstKind::Unevaluated(_, args) => args.visit(visitor)?, - crate::ty::TyConstKind::Value(ty, alloc) => { + super::ty::TyConstKind::Param(_) | super::ty::TyConstKind::Bound(_, _) => {} + super::ty::TyConstKind::Unevaluated(_, args) => args.visit(visitor)?, + super::ty::TyConstKind::Value(ty, alloc) => { alloc.visit(visitor)?; ty.visit(visitor)?; } - crate::ty::TyConstKind::ZSTValue(ty) => ty.visit(visitor)?, + super::ty::TyConstKind::ZSTValue(ty) => ty.visit(visitor)?, } ControlFlow::Continue(()) } diff --git a/compiler/rustc_span/src/analyze_source_file.rs b/compiler/rustc_span/src/analyze_source_file.rs index 141d261b5f015..6384fa06c21b0 100644 --- a/compiler/rustc_span/src/analyze_source_file.rs +++ b/compiler/rustc_span/src/analyze_source_file.rs @@ -83,17 +83,17 @@ cfg_match! { // For character in the chunk, see if its byte value is < 0, which // indicates that it's part of a UTF-8 char. - let multibyte_test = unsafe { _mm_cmplt_epi8(chunk, _mm_set1_epi8(0)) }; + let multibyte_test = _mm_cmplt_epi8(chunk, _mm_set1_epi8(0)); // Create a bit mask from the comparison results. - let multibyte_mask = unsafe { _mm_movemask_epi8(multibyte_test) }; + let multibyte_mask = _mm_movemask_epi8(multibyte_test); // If the bit mask is all zero, we only have ASCII chars here: if multibyte_mask == 0 { assert!(intra_chunk_offset == 0); // Check for newlines in the chunk - let newlines_test = unsafe { _mm_cmpeq_epi8(chunk, _mm_set1_epi8(b'\n' as i8)) }; - let mut newlines_mask = unsafe { _mm_movemask_epi8(newlines_test) }; + let newlines_test = _mm_cmpeq_epi8(chunk, _mm_set1_epi8(b'\n' as i8)); + let mut newlines_mask = _mm_movemask_epi8(newlines_test); let output_offset = RelativeBytePos::from_usize(chunk_index * CHUNK_SIZE + 1); diff --git a/compiler/rustc_span/src/edit_distance.rs b/compiler/rustc_span/src/edit_distance.rs index 8a2baaa42e2a3..4f3202b694c1c 100644 --- a/compiler/rustc_span/src/edit_distance.rs +++ b/compiler/rustc_span/src/edit_distance.rs @@ -118,7 +118,7 @@ pub fn edit_distance_with_substrings(a: &str, b: &str, limit: usize) -> Option "2018", Edition::Edition2021 => "2021", Edition::Edition2024 => "2024", + Edition::EditionFuture => "future", }; write!(f, "{s}") } @@ -54,6 +71,7 @@ impl Edition { Edition::Edition2018 => "rust_2018_compatibility", Edition::Edition2021 => "rust_2021_compatibility", Edition::Edition2024 => "rust_2024_compatibility", + Edition::EditionFuture => "edition_future_compatibility", } } @@ -63,6 +81,7 @@ impl Edition { Edition::Edition2018 => true, Edition::Edition2021 => true, Edition::Edition2024 => true, + Edition::EditionFuture => false, } } @@ -85,6 +104,11 @@ impl Edition { pub fn at_least_rust_2024(self) -> bool { self >= Edition::Edition2024 } + + /// Are we allowed to use features from the future edition? + pub fn at_least_edition_future(self) -> bool { + self >= Edition::EditionFuture + } } impl FromStr for Edition { @@ -95,6 +119,7 @@ impl FromStr for Edition { "2018" => Ok(Edition::Edition2018), "2021" => Ok(Edition::Edition2021), "2024" => Ok(Edition::Edition2024), + "future" => Ok(Edition::EditionFuture), _ => Err(()), } } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 84e89ff4b7dab..b621920d62ba6 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -24,17 +24,14 @@ // because getting it wrong can lead to nested `HygieneData::with` calls that // trigger runtime aborts. (Fortunately these are obvious and easy to fix.) -use std::cell::RefCell; -use std::collections::hash_map::Entry; -use std::collections::hash_set::Entry as SetEntry; -use std::fmt; use std::hash::Hash; use std::sync::Arc; +use std::{fmt, iter, mem}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher}; -use rustc_data_structures::sync::{Lock, WorkerLocal}; +use rustc_data_structures::sync::Lock; use rustc_data_structures::unhash::UnhashMap; use rustc_hashes::Hash64; use rustc_index::IndexVec; @@ -44,6 +41,7 @@ use tracing::{debug, trace}; use crate::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, StableCrateId}; use crate::edition::Edition; +use crate::source_map::SourceMap; use crate::symbol::{Symbol, kw, sym}; use crate::{DUMMY_SP, HashStableContext, Span, SpanDecoder, SpanEncoder, with_session_globals}; @@ -57,19 +55,40 @@ pub struct SyntaxContext(u32); impl !Ord for SyntaxContext {} impl !PartialOrd for SyntaxContext {} -#[derive(Debug, Encodable, Decodable, Clone)] -pub struct SyntaxContextData { +/// If this part of two syntax contexts is equal, then the whole syntax contexts should be equal. +/// The other fields are only for caching. +pub type SyntaxContextKey = (SyntaxContext, ExpnId, Transparency); + +#[derive(Clone, Copy, Debug)] +struct SyntaxContextData { outer_expn: ExpnId, outer_transparency: Transparency, parent: SyntaxContext, - /// This context, but with all transparent and semi-transparent expansions filtered away. + /// This context, but with all transparent and semi-opaque expansions filtered away. opaque: SyntaxContext, /// This context, but with all transparent expansions filtered away. - opaque_and_semitransparent: SyntaxContext, + opaque_and_semiopaque: SyntaxContext, /// Name of the crate to which `$crate` with this context would resolve. dollar_crate_name: Symbol, } +impl SyntaxContextData { + fn root() -> SyntaxContextData { + SyntaxContextData { + outer_expn: ExpnId::root(), + outer_transparency: Transparency::Opaque, + parent: SyntaxContext::root(), + opaque: SyntaxContext::root(), + opaque_and_semiopaque: SyntaxContext::root(), + dollar_crate_name: kw::DollarCrate, + } + } + + fn key(&self) -> SyntaxContextKey { + (self.parent, self.outer_expn, self.outer_transparency) + } +} + rustc_index::newtype_index! { /// A unique ID associated with a macro invocation and expansion. #[orderable] @@ -107,7 +126,7 @@ impl !PartialOrd for LocalExpnId {} /// with a non-default mode. With this check in place, we can avoid the need /// to maintain separate versions of `ExpnData` hashes for each permutation /// of `HashingControls` settings. -fn assert_default_hashing_controls(ctx: &CTX, msg: &str) { +fn assert_default_hashing_controls(ctx: &impl HashStableContext, msg: &str) { match ctx.hashing_controls() { // Note that we require that `hash_spans` be set according to the global // `-Z incremental-ignore-spans` option. Normally, this option is disabled, @@ -163,13 +182,13 @@ 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 + /// Identifier produced by a semi-opaque 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, + SemiOpaque, /// 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, @@ -177,7 +196,7 @@ pub enum Transparency { impl Transparency { pub fn fallback(macro_rules: bool) -> Self { - if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque } + if macro_rules { Transparency::SemiOpaque } else { Transparency::Opaque } } } @@ -342,7 +361,7 @@ pub(crate) struct HygieneData { foreign_expn_hashes: FxHashMap, expn_hash_to_expn_id: UnhashMap, syntax_context_data: Vec, - syntax_context_map: FxHashMap<(SyntaxContext, ExpnId, Transparency), SyntaxContext>, + syntax_context_map: FxHashMap, /// Maps the `local_hash` of an `ExpnData` to the next disambiguator value. /// This is used by `update_disambiguator` to keep track of which `ExpnData`s /// would have collisions without a disambiguator. @@ -361,27 +380,21 @@ impl HygieneData { None, ); + let root_ctxt_data = SyntaxContextData::root(); HygieneData { local_expn_data: IndexVec::from_elem_n(Some(root_data), 1), local_expn_hashes: IndexVec::from_elem_n(ExpnHash(Fingerprint::ZERO), 1), foreign_expn_data: FxHashMap::default(), foreign_expn_hashes: FxHashMap::default(), - expn_hash_to_expn_id: std::iter::once((ExpnHash(Fingerprint::ZERO), ExpnId::root())) + expn_hash_to_expn_id: iter::once((ExpnHash(Fingerprint::ZERO), ExpnId::root())) .collect(), - 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(), + syntax_context_data: vec![root_ctxt_data], + syntax_context_map: iter::once((root_ctxt_data.key(), SyntaxContext(0))).collect(), expn_data_disambiguators: UnhashMap::default(), } } - fn with T>(f: F) -> T { + fn with(f: impl FnOnce(&mut HygieneData) -> R) -> R { with_session_globals(|session_globals| f(&mut session_globals.hygiene_data.borrow_mut())) } @@ -429,7 +442,7 @@ impl HygieneData { } fn normalize_to_macro_rules(&self, ctxt: SyntaxContext) -> SyntaxContext { - self.syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent + self.syntax_context_data[ctxt.0 as usize].opaque_and_semiopaque } fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId { @@ -515,18 +528,18 @@ impl HygieneData { ) -> SyntaxContext { assert_ne!(expn_id, ExpnId::root()); if transparency == Transparency::Opaque { - return self.apply_mark_internal(ctxt, expn_id, transparency); + return self.alloc_ctxt(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 { + let mut call_site_ctxt = if transparency == Transparency::SemiOpaque { self.normalize_to_macros_2_0(call_site_ctxt) } else { self.normalize_to_macro_rules(call_site_ctxt) }; if call_site_ctxt.is_root() { - return self.apply_mark_internal(ctxt, expn_id, transparency); + return self.alloc_ctxt(ctxt, expn_id, transparency); } // Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a @@ -539,75 +552,67 @@ impl HygieneData { // // 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); + call_site_ctxt = self.alloc_ctxt(call_site_ctxt, expn_id, transparency); } - self.apply_mark_internal(call_site_ctxt, expn_id, transparency) + self.alloc_ctxt(call_site_ctxt, expn_id, transparency) } - fn apply_mark_internal( + /// Allocate a new context with the given key, or retrieve it from cache if the given key + /// already exists. The auxiliary fields are calculated from the key. + fn alloc_ctxt( &mut self, - ctxt: SyntaxContext, + parent: 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 - }); + // Look into the cache first. + let key = (parent, expn_id, transparency); + if let Some(ctxt) = self.syntax_context_map.get(&key) { + return *ctxt; } - 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 - }); - } + // Reserve a new syntax context. + // The inserted dummy data can only be potentially accessed by nested `alloc_ctxt` calls, + // the assert below ensures that it doesn't happen. + let ctxt = SyntaxContext::from_usize(self.syntax_context_data.len()); + self.syntax_context_data + .push(SyntaxContextData { dollar_crate_name: sym::dummy, ..SyntaxContextData::root() }); + self.syntax_context_map.insert(key, ctxt); + + // Opaque and semi-opaque versions of the parent. Note that they may be equal to the + // parent itself. E.g. `parent_opaque` == `parent` if the expn chain contains only opaques, + // and `parent_opaque_and_semiopaque` == `parent` if the expn contains only (semi-)opaques. + let parent_data = &self.syntax_context_data[parent.0 as usize]; + assert_ne!(parent_data.dollar_crate_name, sym::dummy); + let parent_opaque = parent_data.opaque; + let parent_opaque_and_semiopaque = parent_data.opaque_and_semiopaque; + + // Evaluate opaque and semi-opaque versions of the new syntax context. + let (opaque, opaque_and_semiopaque) = match transparency { + Transparency::Transparent => (parent_opaque, parent_opaque_and_semiopaque), + Transparency::SemiOpaque => ( + parent_opaque, + // Will be the same as `ctxt` if the expn chain contains only (semi-)opaques. + self.alloc_ctxt(parent_opaque_and_semiopaque, expn_id, transparency), + ), + Transparency::Opaque => ( + // Will be the same as `ctxt` if the expn chain contains only opaques. + self.alloc_ctxt(parent_opaque, expn_id, transparency), + // Will be the same as `ctxt` if the expn chain contains only (semi-)opaques. + self.alloc_ctxt(parent_opaque_and_semiopaque, expn_id, transparency), + ), + }; - 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 - }) + // Fill the full data, now that we have it. + self.syntax_context_data[ctxt.as_u32() as usize] = SyntaxContextData { + outer_expn: expn_id, + outer_transparency: transparency, + parent, + opaque, + opaque_and_semiopaque, + dollar_crate_name: kw::DollarCrate, + }; + ctxt } } @@ -626,25 +631,25 @@ pub fn walk_chain_collapsed(span: Span, to: Span) -> Span { 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(), - ) + let mut to_update = vec![]; + HygieneData::with(|data| { + for (idx, scdata) in data.syntax_context_data.iter().enumerate().rev() { + if scdata.dollar_crate_name == kw::DollarCrate { + to_update.push((idx, kw::DollarCrate)); + } else { + break; + } + } }); // 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(); + for (idx, name) in &mut to_update { + *name = get_name(SyntaxContext::from_usize(*idx)); + } HygieneData::with(|data| { - range_to_update.zip(names).for_each(|(idx, name)| { + for (idx, name) in to_update { data.syntax_context_data[idx].dollar_crate_name = name; - }) + } }) } @@ -713,6 +718,10 @@ impl SyntaxContext { SyntaxContext(raw as u32) } + fn from_usize(raw: usize) -> SyntaxContext { + SyntaxContext(u32::try_from(raw).unwrap()) + } + /// Extend a syntax context with a given expansion and transparency. pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext { HygieneData::with(|data| data.apply_mark(self, expn_id, transparency)) @@ -899,6 +908,30 @@ impl SyntaxContext { pub fn edition(self) -> Edition { HygieneData::with(|data| data.expn_data(data.outer_expn(self)).edition) } + + /// Returns whether this context originates in a foreign crate's external macro. + /// + /// This is used to test whether a lint should not even begin to figure out whether it should + /// be reported on the current node. + pub fn in_external_macro(self, sm: &SourceMap) -> bool { + let expn_data = self.outer_expn_data(); + match expn_data.kind { + ExpnKind::Root + | ExpnKind::Desugaring( + DesugaringKind::ForLoop + | DesugaringKind::WhileLoop + | DesugaringKind::OpaqueTy + | DesugaringKind::Async + | DesugaringKind::Await, + ) => false, + ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external" + ExpnKind::Macro(MacroKind::Bang, _) => { + // Dummy span for the `def_site` means it's an external macro. + expn_data.def_site.is_dummy() || sm.is_imported(expn_data.def_site) + } + ExpnKind::Macro { .. } => true, // definitely a plugin + } + } } impl fmt::Debug for SyntaxContext { @@ -982,6 +1015,8 @@ pub struct ExpnData { /// Should debuginfo for the macro be collapsed to the outermost expansion site (in other /// words, was the macro definition annotated with `#[collapse_debuginfo]`)? pub(crate) collapse_debuginfo: bool, + /// When true, we do not display the note telling people to use the `-Zmacro-backtrace` flag. + pub hide_backtrace: bool, } impl !PartialEq for ExpnData {} @@ -1000,6 +1035,7 @@ impl ExpnData { allow_internal_unsafe: bool, local_inner_macros: bool, collapse_debuginfo: bool, + hide_backtrace: bool, ) -> ExpnData { ExpnData { kind, @@ -1014,6 +1050,7 @@ impl ExpnData { allow_internal_unsafe, local_inner_macros, collapse_debuginfo, + hide_backtrace, } } @@ -1038,6 +1075,7 @@ impl ExpnData { allow_internal_unsafe: false, local_inner_macros: false, collapse_debuginfo: false, + hide_backtrace: false, } } @@ -1173,6 +1211,8 @@ pub enum DesugaringKind { BoundModifier, /// Calls to contract checks (`#[requires]` to precond, `#[ensures]` to postcond) Contract, + /// A pattern type range start/end + PatTyRange, } impl DesugaringKind { @@ -1190,6 +1230,26 @@ impl DesugaringKind { DesugaringKind::WhileLoop => "`while` loop", DesugaringKind::BoundModifier => "trait bound modifier", DesugaringKind::Contract => "contract check", + DesugaringKind::PatTyRange => "pattern type", + } + } + + /// For use with `rustc_unimplemented` to support conditions + /// like `from_desugaring = "QuestionMark"` + pub fn matches(&self, value: &str) -> bool { + match self { + DesugaringKind::CondTemporary => value == "CondTemporary", + DesugaringKind::Async => value == "Async", + DesugaringKind::Await => value == "Await", + DesugaringKind::QuestionMark => value == "QuestionMark", + DesugaringKind::TryBlock => value == "TryBlock", + DesugaringKind::YeetExpr => value == "YeetExpr", + DesugaringKind::OpaqueTy => value == "OpaqueTy", + DesugaringKind::ForLoop => value == "ForLoop", + DesugaringKind::WhileLoop => value == "WhileLoop", + DesugaringKind::BoundModifier => value == "BoundModifier", + DesugaringKind::Contract => value == "Contract", + DesugaringKind::PatTyRange => value == "PatTyRange", } } } @@ -1222,7 +1282,7 @@ impl HygieneEncodeContext { pub fn encode( &self, encoder: &mut T, - mut encode_ctxt: impl FnMut(&mut T, u32, &SyntaxContextData), + mut encode_ctxt: impl FnMut(&mut T, u32, &SyntaxContextKey), mut encode_expn: impl FnMut(&mut T, ExpnId, &ExpnData, ExpnHash), ) { // When we serialize a `SyntaxContextData`, we may end up serializing @@ -1234,55 +1294,47 @@ impl HygieneEncodeContext { 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 + // Consume the current round of syntax contexts. + // Drop the lock() temporary early. + // 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. #[allow(rustc::potential_query_instability)] - for_all_ctxts_in(latest_ctxts.into_iter(), |index, ctxt, data| { + let latest_ctxts = { mem::take(&mut *self.latest_ctxts.lock()) }.into_iter(); + let all_ctxt_data: Vec<_> = HygieneData::with(|data| { + latest_ctxts + .map(|ctxt| (ctxt, data.syntax_context_data[ctxt.0 as usize].key())) + .collect() + }); + for (ctxt, ctxt_key) in all_ctxt_data { if self.serialized_ctxts.lock().insert(ctxt) { - encode_ctxt(encoder, index, data); + encode_ctxt(encoder, ctxt.0, &ctxt_key); } - }); - - let latest_expns = { std::mem::take(&mut *self.latest_expns.lock()) }; + } - // Same as above, this is fine as we are inserting into a order-independent hashset + // Same as above, but for expansions instead of syntax contexts. #[allow(rustc::potential_query_instability)] - for_all_expns_in(latest_expns.into_iter(), |expn, data, hash| { + let latest_expns = { mem::take(&mut *self.latest_expns.lock()) }.into_iter(); + let all_expn_data: Vec<_> = HygieneData::with(|data| { + latest_expns + .map(|expn| (expn, data.expn_data(expn).clone(), data.expn_hash(expn))) + .collect() + }); + for (expn, expn_data, expn_hash) in all_expn_data { if self.serialized_expns.lock().insert(expn) { - encode_expn(encoder, expn, data, hash); + encode_expn(encoder, expn, &expn_data, expn_hash); } - }); + } } debug!("encode_hygiene: Done serializing SyntaxContextData"); } } -#[derive(Default)] /// Additional information used to assist in decoding hygiene data -struct HygieneDecodeContextInner { - // 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`. This only stores `SyntaxContext`s which are completely decoded. - remapped_ctxts: Vec>, - - /// Maps serialized `SyntaxContext` ids that are currently being decoded to a `SyntaxContext`. - decoding: FxHashMap, -} - #[derive(Default)] -/// Additional information used to assist in decoding hygiene data pub struct HygieneDecodeContext { - inner: Lock, - - /// A set of serialized `SyntaxContext` ids that are currently being decoded on each thread. - local_in_progress: WorkerLocal>>, + // A cache mapping raw serialized per-crate syntax context ids to corresponding decoded + // `SyntaxContext`s in the current global `HygieneData`. + remapped_ctxts: Lock>>, } /// Register an expansion which has been decoded from the on-disk-cache for the local crate. @@ -1353,10 +1405,10 @@ pub fn decode_expn_id( // 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 SyntaxContextData>( +pub fn decode_syntax_context( d: &mut D, context: &HygieneDecodeContext, - decode_data: F, + decode_data: impl FnOnce(&mut D, u32) -> SyntaxContextKey, ) -> SyntaxContext { let raw_id: u32 = Decodable::decode(d); if raw_id == 0 { @@ -1365,117 +1417,24 @@ pub fn decode_syntax_context SyntaxContext return SyntaxContext::root(); } - let ctxt = { - let mut inner = context.inner.lock(); - - if let Some(ctxt) = inner.remapped_ctxts.get(raw_id as usize).copied().flatten() { - // This has already been decoded. - return ctxt; - } - - match inner.decoding.entry(raw_id) { - Entry::Occupied(ctxt_entry) => { - match context.local_in_progress.borrow_mut().entry(raw_id) { - SetEntry::Occupied(..) => { - // We're decoding this already on the current thread. Return here - // and let the function higher up the stack finish decoding to handle - // recursive cases. - return *ctxt_entry.get(); - } - SetEntry::Vacant(entry) => { - entry.insert(); - - // Some other thread is current decoding this. Race with it. - *ctxt_entry.get() - } - } - } - Entry::Vacant(entry) => { - // We are the first thread to start decoding. Mark the current thread as being progress. - context.local_in_progress.borrow_mut().insert(raw_id); - - // 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::Empty, - }); - new_ctxt - }); - entry.insert(new_ctxt); - new_ctxt - } - } - }; + // Look into the cache first. + // Reminder: `HygieneDecodeContext` is per-crate, so there are no collisions between + // raw ids from different crate metadatas. + if let Some(Some(ctxt)) = context.remapped_ctxts.lock().get(raw_id) { + return *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| { - if let Some(old) = hygiene_data.syntax_context_data.get(raw_id as usize) - && old.outer_expn == ctxt_data.outer_expn - && old.outer_transparency == ctxt_data.outer_transparency - && old.parent == ctxt_data.parent - { - ctxt_data = old.clone(); - } - - hygiene_data.syntax_context_data[ctxt.as_u32() as usize] = ctxt_data; - }); + let (parent, expn_id, transparency) = decode_data(d, raw_id); + let ctxt = + HygieneData::with(|hygiene_data| hygiene_data.alloc_ctxt(parent, expn_id, transparency)); - // Mark the context as completed - - context.local_in_progress.borrow_mut().remove(&raw_id); - - let mut inner = context.inner.lock(); - let new_len = raw_id as usize + 1; - if inner.remapped_ctxts.len() < new_len { - inner.remapped_ctxts.resize(new_len, None); - } - inner.remapped_ctxts[raw_id as usize] = Some(ctxt); - inner.decoding.remove(&raw_id); + context.remapped_ctxts.lock().insert(raw_id, ctxt); ctxt } -fn for_all_ctxts_in( - ctxts: impl Iterator, - mut f: F, -) { - 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); - } -} - -fn for_all_expns_in( - expns: impl Iterator, - mut f: impl FnMut(ExpnId, &ExpnData, ExpnHash), -) { - let all_data: Vec<_> = HygieneData::with(|data| { - expns.map(|expn| (expn, data.expn_data(expn).clone(), data.expn_hash(expn))).collect() - }); - for (expn, data, hash) in all_data.into_iter() { - f(expn, &data, hash); - } -} - impl Encodable for LocalExpnId { fn encode(&self, e: &mut E) { self.to_expn_id().encode(e); @@ -1488,10 +1447,10 @@ impl Decodable for LocalExpnId { } } -pub fn raw_encode_syntax_context( +pub fn raw_encode_syntax_context( ctxt: SyntaxContext, context: &HygieneEncodeContext, - e: &mut E, + e: &mut impl Encoder, ) { if !context.serialized_ctxts.lock().contains(&ctxt) { context.latest_ctxts.lock().insert(ctxt); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index bca9323a50d4c..22ca8accf9fde 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -24,15 +24,12 @@ #![feature(core_io_borrowed_buf)] #![feature(hash_set_entry)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(map_try_insert)] #![feature(negative_impls)] #![feature(read_buf)] #![feature(round_char_boundary)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] -#![feature(slice_as_chunks)] -#![warn(unreachable_pub)] // tidy-alphabetical-end // The code produced by the `Encodable`/`Decodable` derive macros refer to @@ -117,9 +114,13 @@ pub struct SessionGlobals { } impl SessionGlobals { - pub fn new(edition: Edition, sm_inputs: Option) -> SessionGlobals { + pub fn new( + edition: Edition, + extra_symbols: &[&'static str], + sm_inputs: Option, + ) -> SessionGlobals { SessionGlobals { - symbol_interner: symbol::Interner::fresh(), + symbol_interner: symbol::Interner::with_extra_symbols(extra_symbols), span_interner: Lock::new(span_encoding::SpanInterner::default()), metavar_spans: Default::default(), hygiene_data: Lock::new(hygiene::HygieneData::new(edition)), @@ -130,6 +131,7 @@ impl SessionGlobals { pub fn create_session_globals_then( edition: Edition, + extra_symbols: &[&'static str], sm_inputs: Option, f: impl FnOnce() -> R, ) -> R { @@ -138,7 +140,7 @@ pub fn create_session_globals_then( "SESSION_GLOBALS should never be overwritten! \ Use another thread if you need another SessionGlobals" ); - let session_globals = SessionGlobals::new(edition, sm_inputs); + let session_globals = SessionGlobals::new(edition, extra_symbols, sm_inputs); SESSION_GLOBALS.set(&session_globals, f) } @@ -157,7 +159,7 @@ where F: FnOnce(&SessionGlobals) -> R, { if !SESSION_GLOBALS.is_set() { - let session_globals = SessionGlobals::new(edition, None); + let session_globals = SessionGlobals::new(edition, &[], None); SESSION_GLOBALS.set(&session_globals, || SESSION_GLOBALS.with(f)) } else { SESSION_GLOBALS.with(f) @@ -173,7 +175,7 @@ where /// Default edition, no source map. pub fn create_default_session_globals_then(f: impl FnOnce() -> R) -> R { - create_session_globals_then(edition::DEFAULT_EDITION, None, f) + create_session_globals_then(edition::DEFAULT_EDITION, &[], None, f) } // If this ever becomes non thread-local, `decode_syntax_context` @@ -221,7 +223,7 @@ pub fn with_metavar_spans(f: impl FnOnce(&MetavarSpansMap) -> R) -> R { // 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, Decodable)] +#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Decodable, Encodable)] pub enum RealFileName { LocalPath(PathBuf), /// For remapped paths (namely paths into libstd that have been mapped @@ -247,28 +249,6 @@ impl Hash for RealFileName { } } -// This is functionally identical to #[derive(Encodable)], with the exception of -// an added assert statement -impl Encodable for RealFileName { - fn encode(&self, encoder: &mut S) { - match *self { - RealFileName::LocalPath(ref local_path) => { - encoder.emit_u8(0); - local_path.encode(encoder); - } - - RealFileName::Remapped { ref local_path, ref virtual_name } => { - encoder.emit_u8(1); - // For privacy and build reproducibility, we must not embed host-dependant path - // in artifacts if they have been remapped by --remap-path-prefix - assert!(local_path.is_none()); - local_path.encode(encoder); - virtual_name.encode(encoder); - } - } - } -} - impl RealFileName { /// Returns the path suitable for reading from the file system on the local host, /// if this information exists. @@ -365,6 +345,16 @@ impl From for FileName { } } +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] +pub enum FileNameEmbeddablePreference { + /// If a remapped path is available, only embed the `virtual_path` and omit the `local_path`. + /// + /// Otherwise embed the local-path into the `virtual_path`. + RemappedOnly, + /// Embed the original path as well as its remapped `virtual_path` component if available. + LocalAndRemapped, +} + #[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] pub enum FileNameDisplayPreference { /// Display the path after the application of rewrite rules provided via `--remap-path-prefix`. @@ -603,28 +593,13 @@ impl Span { !self.is_dummy() && sm.is_span_accessible(self) } - /// Returns whether `span` originates in a foreign crate's external macro. + /// Returns whether this span originates in a foreign crate's external macro. /// /// This is used to test whether a lint should not even begin to figure out whether it should /// be reported on the current node. + #[inline] pub fn in_external_macro(self, sm: &SourceMap) -> bool { - let expn_data = self.ctxt().outer_expn_data(); - match expn_data.kind { - ExpnKind::Root - | ExpnKind::Desugaring( - DesugaringKind::ForLoop - | DesugaringKind::WhileLoop - | DesugaringKind::OpaqueTy - | DesugaringKind::Async - | DesugaringKind::Await, - ) => false, - ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external" - ExpnKind::Macro(MacroKind::Bang, _) => { - // Dummy span for the `def_site` means it's an external macro. - expn_data.def_site.is_dummy() || sm.is_imported(expn_data.def_site) - } - ExpnKind::Macro { .. } => true, // definitely a plugin - } + self.ctxt().in_external_macro(sm) } /// Returns `true` if `span` originates in a derive-macro's expansion. @@ -877,7 +852,7 @@ impl Span { self.ctxt() .outer_expn_data() .allow_internal_unstable - .is_some_and(|features| features.iter().any(|&f| f == feature)) + .is_some_and(|features| features.contains(&feature)) } /// Checks if this span arises from a compiler desugaring of kind `kind`. @@ -1057,6 +1032,37 @@ impl Span { } } + /// Returns the `Span` within the syntax context of "within". This is useful when + /// "self" is an expansion from a macro variable, since this can be used for + /// providing extra macro expansion context for certain errors. + /// + /// ```text + /// macro_rules! m { + /// ($ident:ident) => { ($ident,) } + /// } + /// + /// m!(outer_ident); + /// ``` + /// + /// If "self" is the span of the outer_ident, and "within" is the span of the `($ident,)` + /// expr, then this will return the span of the `$ident` macro variable. + pub fn within_macro(self, within: Span, sm: &SourceMap) -> Option { + match Span::prepare_to_combine(self, within) { + // Only return something if it doesn't overlap with the original span, + // and the span isn't "imported" (i.e. from unavailable sources). + // FIXME: This does limit the usefulness of the error when the macro is + // from a foreign crate; we could also take into account `-Zmacro-backtrace`, + // which doesn't redact this span (but that would mean passing in even more + // args to this function, lol). + Ok((self_, _, parent)) + if self_.hi < self.lo() || self.hi() < self_.lo && !sm.is_imported(within) => + { + Some(Span::new(self_.lo, self_.hi, self_.ctxt, parent)) + } + _ => None, + } + } + pub fn from_inner(self, inner: InnerSpan) -> Span { let span = self.data(); Span::new( @@ -1082,7 +1088,7 @@ impl Span { /// 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) + self.with_ctxt_from_mark(expn_id, Transparency::SemiOpaque) } /// Produces a span with the same location as `self` and context produced by a macro with the diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 6fdf8e46fec65..8a3644163caf3 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -633,6 +633,24 @@ impl SourceMap { sp } + /// Extends the given `Span` to just before the previous occurrence of `c`. Return the same span + /// if an error occurred while retrieving the code snippet. + pub fn span_extend_to_prev_char_before( + &self, + sp: Span, + c: char, + accept_newlines: bool, + ) -> Span { + if let Ok(prev_source) = self.span_to_prev_source(sp) { + let prev_source = prev_source.rsplit(c).next().unwrap_or(""); + if accept_newlines || !prev_source.contains('\n') { + return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32 - 1_u32)); + } + } + + sp + } + /// Extends the given `Span` to just after the previous occurrence of `pat` when surrounded by /// whitespace. Returns None if the pattern could not be found or if an error occurred while /// retrieving the code snippet. @@ -1090,18 +1108,28 @@ pub fn get_source_map() -> Option> { pub struct FilePathMapping { mapping: Vec<(PathBuf, PathBuf)>, filename_display_for_diagnostics: FileNameDisplayPreference, + filename_embeddable_preference: FileNameEmbeddablePreference, } impl FilePathMapping { pub fn empty() -> FilePathMapping { - FilePathMapping::new(Vec::new(), FileNameDisplayPreference::Local) + FilePathMapping::new( + Vec::new(), + FileNameDisplayPreference::Local, + FileNameEmbeddablePreference::RemappedOnly, + ) } pub fn new( mapping: Vec<(PathBuf, PathBuf)>, filename_display_for_diagnostics: FileNameDisplayPreference, + filename_embeddable_preference: FileNameEmbeddablePreference, ) -> FilePathMapping { - FilePathMapping { mapping, filename_display_for_diagnostics } + FilePathMapping { + mapping, + filename_display_for_diagnostics, + filename_embeddable_preference, + } } /// Applies any path prefix substitution as defined by the mapping. @@ -1199,11 +1227,13 @@ impl FilePathMapping { ) -> RealFileName { match file_path { // Anything that's already remapped we don't modify, except for erasing - // the `local_path` portion. - RealFileName::Remapped { local_path: _, virtual_name } => { + // the `local_path` portion (if desired). + RealFileName::Remapped { local_path, virtual_name } => { RealFileName::Remapped { - // We do not want any local path to be exported into metadata - local_path: None, + local_path: match self.filename_embeddable_preference { + FileNameEmbeddablePreference::RemappedOnly => None, + FileNameEmbeddablePreference::LocalAndRemapped => local_path, + }, // We use the remapped name verbatim, even if it looks like a relative // path. The assumption is that the user doesn't want us to further // process paths that have gone through remapping. @@ -1213,12 +1243,18 @@ impl FilePathMapping { RealFileName::LocalPath(unmapped_file_path) => { // If no remapping has been applied yet, try to do so - let (new_path, was_remapped) = self.map_prefix(unmapped_file_path); + let (new_path, was_remapped) = self.map_prefix(&unmapped_file_path); if was_remapped { // It was remapped, so don't modify further return RealFileName::Remapped { - local_path: None, virtual_name: new_path.into_owned(), + // But still provide the local path if desired + local_path: match self.filename_embeddable_preference { + FileNameEmbeddablePreference::RemappedOnly => None, + FileNameEmbeddablePreference::LocalAndRemapped => { + Some(unmapped_file_path) + } + }, }; } @@ -1234,17 +1270,23 @@ impl FilePathMapping { match working_directory { RealFileName::LocalPath(unmapped_working_dir_abs) => { - let file_path_abs = unmapped_working_dir_abs.join(unmapped_file_path_rel); + let unmapped_file_path_abs = + unmapped_working_dir_abs.join(unmapped_file_path_rel); // Although neither `working_directory` nor the file name were subject // to path remapping, the concatenation between the two may be. Hence // we need to do a remapping here. - let (file_path_abs, was_remapped) = self.map_prefix(file_path_abs); + let (file_path_abs, was_remapped) = + self.map_prefix(&unmapped_file_path_abs); if was_remapped { RealFileName::Remapped { - // Erase the actual path - local_path: None, virtual_name: file_path_abs.into_owned(), + local_path: match self.filename_embeddable_preference { + FileNameEmbeddablePreference::RemappedOnly => None, + FileNameEmbeddablePreference::LocalAndRemapped => { + Some(unmapped_file_path_abs) + } + }, } } else { // No kind of remapping applied to this path, so @@ -1253,15 +1295,20 @@ impl FilePathMapping { } } RealFileName::Remapped { - local_path: _, + local_path, virtual_name: remapped_working_dir_abs, } => { // If working_directory has been remapped, then we emit // Remapped variant as the expanded path won't be valid RealFileName::Remapped { - local_path: None, virtual_name: Path::new(remapped_working_dir_abs) - .join(unmapped_file_path_rel), + .join(&unmapped_file_path_rel), + local_path: match self.filename_embeddable_preference { + FileNameEmbeddablePreference::RemappedOnly => None, + FileNameEmbeddablePreference::LocalAndRemapped => local_path + .as_ref() + .map(|local_path| local_path.join(unmapped_file_path_rel)), + }, } } } @@ -1269,27 +1316,6 @@ impl FilePathMapping { } } - /// Expand a relative path to an absolute path **without** remapping taken into account. - /// - /// The resulting `RealFileName` will have its `virtual_path` portion erased if - /// possible (i.e. if there's also a remapped path). - pub fn to_local_embeddable_absolute_path( - &self, - file_path: RealFileName, - working_directory: &RealFileName, - ) -> RealFileName { - let file_path = file_path.local_path_if_available(); - if file_path.is_absolute() { - // No remapping has applied to this path and it is absolute, - // so the working directory cannot influence it either, so - // we are done. - return RealFileName::LocalPath(file_path.to_path_buf()); - } - debug_assert!(file_path.is_relative()); - let working_directory = working_directory.local_path_if_available(); - RealFileName::LocalPath(Path::new(working_directory).join(file_path)) - } - /// Attempts to (heuristically) reverse a prefix mapping. /// /// Returns [`Some`] if there is exactly one mapping where the "to" part is diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 957f55e39138e..589c2a3635481 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -305,6 +305,7 @@ fn path_prefix_remapping() { let mapping = &FilePathMapping::new( vec![(path("abc/def"), path("foo"))], FileNameDisplayPreference::Remapped, + FileNameEmbeddablePreference::RemappedOnly, ); assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("foo/src/main.rs")); @@ -316,6 +317,7 @@ fn path_prefix_remapping() { let mapping = &FilePathMapping::new( vec![(path("abc/def"), path("/foo"))], FileNameDisplayPreference::Remapped, + FileNameEmbeddablePreference::RemappedOnly, ); assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("/foo/src/main.rs")); @@ -327,6 +329,7 @@ fn path_prefix_remapping() { let mapping = &FilePathMapping::new( vec![(path("/abc/def"), path("foo"))], FileNameDisplayPreference::Remapped, + FileNameEmbeddablePreference::RemappedOnly, ); assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("foo/src/main.rs")); @@ -338,6 +341,7 @@ fn path_prefix_remapping() { let mapping = &FilePathMapping::new( vec![(path("/abc/def"), path("/foo"))], FileNameDisplayPreference::Remapped, + FileNameEmbeddablePreference::RemappedOnly, ); assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("/foo/src/main.rs")); @@ -351,6 +355,7 @@ fn path_prefix_remapping_expand_to_absolute() { let mapping = &FilePathMapping::new( vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))], FileNameDisplayPreference::Remapped, + FileNameEmbeddablePreference::RemappedOnly, ); let working_directory = path("/foo"); let working_directory = RealFileName::Remapped { @@ -448,6 +453,71 @@ fn path_prefix_remapping_expand_to_absolute() { ); } +#[test] +fn path_prefix_remapping_expand_to_absolute_and_local() { + // "virtual" working directory is relative path + let mapping = &FilePathMapping::new( + vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))], + FileNameDisplayPreference::Remapped, + FileNameEmbeddablePreference::LocalAndRemapped, + ); + let working_directory = path("/foo"); + let working_directory = RealFileName::Remapped { + local_path: Some(working_directory.clone()), + virtual_name: mapping.map_prefix(working_directory).0.into_owned(), + }; + + assert_eq!(working_directory.remapped_path_if_available(), path("FOO")); + + // Unmapped absolute path + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::LocalPath(path("/foo/src/main.rs")), + &working_directory + ), + RealFileName::Remapped { + local_path: Some(path("/foo/src/main.rs")), + virtual_name: path("FOO/src/main.rs") + } + ); + + // Unmapped absolute path with unrelated working directory + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::LocalPath(path("/bar/src/main.rs")), + &working_directory + ), + RealFileName::Remapped { + local_path: Some(path("/bar/src/main.rs")), + virtual_name: path("BAR/src/main.rs") + } + ); + + // Already remapped absolute path, with unrelated working directory + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::Remapped { + local_path: Some(path("/bar/src/main.rs")), + virtual_name: path("BAR/src/main.rs"), + }, + &working_directory + ), + RealFileName::Remapped { + local_path: Some(path("/bar/src/main.rs")), + virtual_name: path("BAR/src/main.rs") + } + ); + + // Already remapped relative path + assert_eq!( + mapping.to_embeddable_absolute_path( + RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") }, + &working_directory + ), + RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") } + ); +} + #[test] fn path_prefix_remapping_reverse() { // Ignores options without alphanumeric chars. @@ -455,6 +525,7 @@ fn path_prefix_remapping_reverse() { let mapping = &FilePathMapping::new( vec![(path("abc"), path("/")), (path("def"), path("."))], FileNameDisplayPreference::Remapped, + FileNameEmbeddablePreference::RemappedOnly, ); assert_eq!(reverse_map_prefix(mapping, "/hello.rs"), None); @@ -466,6 +537,7 @@ fn path_prefix_remapping_reverse() { let mapping = &FilePathMapping::new( vec![(path("abc"), path("/redacted")), (path("def"), path("/redacted"))], FileNameDisplayPreference::Remapped, + FileNameEmbeddablePreference::RemappedOnly, ); assert_eq!(reverse_map_prefix(mapping, "/redacted/hello.rs"), None); @@ -476,6 +548,7 @@ fn path_prefix_remapping_reverse() { let mapping = &FilePathMapping::new( vec![(path("abc"), path("/redacted")), (path("def/ghi"), path("/fake/dir"))], FileNameDisplayPreference::Remapped, + FileNameEmbeddablePreference::RemappedOnly, ); assert_eq!( diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index 9d6c7d2a42a38..a4a47dc99b084 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -306,8 +306,21 @@ impl Span { /// Returns `true` if this span comes from any kind of macro, desugaring or inlining. #[inline] pub fn from_expansion(self) -> bool { - // If the span is fully inferred then ctxt > MAX_CTXT - self.inline_ctxt().map_or(true, |ctxt| !ctxt.is_root()) + let ctxt = match_span_kind! { + self, + // All branches here, except `InlineParent`, actually return `span.ctxt_or_parent_or_marker`. + // Since `Interned` is selected if the field contains `CTXT_INTERNED_MARKER` returning that value + // as the context allows the compiler to optimize out the branch that selects between either + // `Interned` and `PartiallyInterned`. + // + // Interned contexts can never be the root context and `CTXT_INTERNED_MARKER` has a different value + // than the root context so this works for checking is this is an expansion. + InlineCtxt(span) => SyntaxContext::from_u16(span.ctxt), + InlineParent(_span) => SyntaxContext::root(), + PartiallyInterned(span) => SyntaxContext::from_u16(span.ctxt), + Interned(_span) => SyntaxContext::from_u16(CTXT_INTERNED_MARKER), + }; + !ctxt.is_root() } /// Returns `true` if this is a dummy span with any hygienic context. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2a709b0725571..fbe3b4ca6f5f6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -26,20 +26,32 @@ symbols! { // documents (such as the Rust Reference) about whether it is a keyword // (e.g. `_`). // - // If you modify this list, adjust any relevant `Symbol::{is,can_be}_*` predicates and - // `used_keywords`. - // But this should rarely be necessary if the keywords are kept in alphabetic order. + // If you modify this list, adjust any relevant `Symbol::{is,can_be}_*` + // predicates and `used_keywords`. Also consider adding new keywords to the + // `ui/parser/raw/raw-idents.rs` test. Keywords { // Special reserved identifiers used internally for elided lifetimes, // unnamed method parameters, crate root module, error recovery etc. - // Matching predicates: `is_any_keyword`, `is_special`/`is_reserved` + // Matching predicates: `is_special`/`is_reserved` + // + // Notes about `kw::Empty`: + // - Its use can blur the lines between "empty symbol" and "no symbol". + // Using `Option` is preferable, where possible, because that + // is unambiguous. + // - For dummy symbols that are never used and absolutely must be + // present, it's better to use `sym::dummy` than `kw::Empty`, because + // it's clearer that it's intended as a dummy value, and more likely + // to be detected if it accidentally does get used. + // tidy-alphabetical-start + DollarCrate: "$crate", Empty: "", PathRoot: "{{root}}", - DollarCrate: "$crate", Underscore: "_", + // tidy-alphabetical-end // Keywords that are used in stable Rust. - // Matching predicates: `is_any_keyword`, `is_used_keyword_always`/`is_reserved` + // Matching predicates: `is_used_keyword_always`/`is_reserved` + // tidy-alphabetical-start As: "as", Break: "break", Const: "const", @@ -75,9 +87,11 @@ symbols! { Use: "use", Where: "where", While: "while", + // tidy-alphabetical-end // Keywords that are used in unstable Rust or reserved for future use. - // Matching predicates: `is_any_keyword`, `is_unused_keyword_always`/`is_reserved` + // Matching predicates: `is_unused_keyword_always`/`is_reserved` + // tidy-alphabetical-start Abstract: "abstract", Become: "become", Box: "box", @@ -90,41 +104,48 @@ symbols! { Unsized: "unsized", Virtual: "virtual", Yield: "yield", + // tidy-alphabetical-end // Edition-specific keywords that are used in stable Rust. - // Matching predicates: `is_any_keyword`, `is_used_keyword_conditional`/`is_reserved` (if + // Matching predicates: `is_used_keyword_conditional`/`is_reserved` (if // the edition suffices) + // tidy-alphabetical-start Async: "async", // >= 2018 Edition only Await: "await", // >= 2018 Edition only Dyn: "dyn", // >= 2018 Edition only + // tidy-alphabetical-end // Edition-specific keywords that are used in unstable Rust or reserved for future use. - // Matching predicates: `is_any_keyword`, `is_unused_keyword_conditional`/`is_reserved` (if + // Matching predicates: `is_unused_keyword_conditional`/`is_reserved` (if // the edition suffices) + // tidy-alphabetical-start Gen: "gen", // >= 2024 Edition only Try: "try", // >= 2018 Edition only - - // NOTE: When adding new keywords, consider adding them to the ui/parser/raw/raw-idents.rs test. + // tidy-alphabetical-end // "Lifetime keywords": regular keywords with a leading `'`. - // Matching predicates: `is_any_keyword` - UnderscoreLifetime: "'_", + // Matching predicates: none + // tidy-alphabetical-start StaticLifetime: "'static", + UnderscoreLifetime: "'_", + // tidy-alphabetical-end // Weak keywords, have special meaning only in specific contexts. - // Matching predicates: `is_any_keyword` + // Matching predicates: `is_weak` + // tidy-alphabetical-start Auto: "auto", Builtin: "builtin", Catch: "catch", + ContractEnsures: "contract_ensures", + ContractRequires: "contract_requires", Default: "default", MacroRules: "macro_rules", Raw: "raw", Reuse: "reuse", - ContractEnsures: "contract_ensures", - ContractRequires: "contract_requires", Safe: "safe", Union: "union", Yeet: "yeet", + // tidy-alphabetical-end } // Pre-interned symbols that can be referred to with `rustc_span::sym::*`. @@ -153,7 +174,6 @@ symbols! { Arc, ArcWeak, Argument, - ArgumentMethods, ArrayIntoIter, AsMut, AsRef, @@ -228,6 +248,7 @@ symbols! { Error, File, FileType, + FmtArgumentsNew, Fn, FnMut, FnOnce, @@ -259,6 +280,8 @@ symbols! { IoSeek, IoWrite, IpAddr, + Ipv4Addr, + Ipv6Addr, IrTyKind, Is, Item, @@ -308,6 +331,9 @@ symbols! { RangeFull, RangeInclusive, RangeInclusiveCopy, + RangeMax, + RangeMin, + RangeSub, RangeTo, RangeToInclusive, Rc, @@ -348,6 +374,7 @@ symbols! { SyncUnsafeCell, T, Target, + This, ToOwned, ToString, TokenStream, @@ -430,9 +457,11 @@ symbols! { and_then, anon, anon_adt, + anon_assoc, anonymous_lifetime_in_impl_trait, any, append_const_msg, + apx_target_feature, arbitrary_enum_discriminant, arbitrary_self_types, arbitrary_self_types_pointers, @@ -476,17 +505,8 @@ symbols! { async_call_mut, async_call_once, async_closure, - async_destruct, async_drop, - async_drop_chain, - async_drop_defer, - async_drop_deferred_drop_in_place, - async_drop_either, - async_drop_fuse, async_drop_in_place, - async_drop_noop, - async_drop_slice, - async_drop_surface_drop_in_place, async_fn, async_fn_in_dyn_trait, async_fn_in_trait, @@ -514,6 +534,7 @@ symbols! { autodiff, automatically_derived, avx, + avx10_target_feature, avx512_target_feature, avx512bw, avx512f, @@ -579,6 +600,7 @@ symbols! { cfg_accessible, cfg_attr, cfg_attr_multi, + cfg_attr_trace: "", // must not be a valid identifier cfg_boolean_literals, cfg_contract_checks, cfg_doctest, @@ -596,8 +618,10 @@ symbols! { cfg_target_feature, cfg_target_has_atomic, cfg_target_has_atomic_equal_alignment, + cfg_target_has_reliable_f16_f128, cfg_target_thread_local, cfg_target_vendor, + cfg_trace: "", // must not be a valid identifier cfg_ub_checks, cfg_version, cfi, @@ -774,8 +798,18 @@ symbols! { default_fn, default_lib_allocator, default_method_body_is_const, + // -------------------------- + // Lang items which are used only for experiments with auto traits with default bounds. + // These lang items are not actually defined in core/std. Experiment is a part of + // `MCP: Low level components for async drop`(https://github.com/rust-lang/compiler-team/issues/727) + default_trait1, + default_trait2, + default_trait3, + default_trait4, + // -------------------------- default_type_parameter_fallback, default_type_params, + define_opaque, delayed_bug_from_inside_query, deny, deprecated, @@ -830,6 +864,7 @@ symbols! { drop_types_in_const, dropck_eyepatch, dropck_parametricity, + dummy: "", // use this instead of `kw::Empty` for symbols that won't be used dummy_cgu_name, dylib, dyn_compatible_for_dispatch, @@ -859,6 +894,7 @@ symbols! { eprint_macro, eprintln_macro, eq, + ergonomic_clones, ermsb_target_feature, exact_div, except, @@ -877,9 +913,11 @@ symbols! { expf16, expf32, expf64, + explicit_extern_abis, explicit_generic_args_with_impl_trait, explicit_tail_calls, export_name, + export_stable, expr, expr_2021, expr_fragment_specifier_2024, @@ -942,7 +980,6 @@ symbols! { fadd_fast, fake_variadic, fallback, - fallback_surface_drop, fdiv_algebraic, fdiv_fast, feature, @@ -957,6 +994,7 @@ symbols! { field_init_shorthand, file, file_options, + flags, float, float_to_int_unchecked, floorf128, @@ -987,7 +1025,6 @@ symbols! { forbid, forget, format, - format_alignment, format_args, format_args_capture, format_args_macro, @@ -1012,8 +1049,10 @@ symbols! { from_residual, from_size_align_unchecked, from_str_method, + from_u16, from_usize, from_yeet, + frontmatter, fs_create_dir, fsub_algebraic, fsub_fast, @@ -1022,13 +1061,13 @@ symbols! { fundamental, fused_iterator, future, + future_drop_poll, future_output, future_trait, gdb_script_file, ge, gen_blocks, gen_future, - gen_kill, generator_clone, generators, generic_arg_infer, @@ -1146,6 +1185,7 @@ symbols! { instruction_set, integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below integral, + internal_features, into_async_iter_into_iter, into_future, into_iter, @@ -1263,6 +1303,10 @@ symbols! { match_beginning_vert, match_default_bindings, matches_macro, + maximumf128, + maximumf16, + maximumf32, + maximumf64, maxnumf128, maxnumf16, maxnumf32, @@ -1297,6 +1341,10 @@ symbols! { min_generic_const_args, min_specialization, min_type_alias_impl_trait, + minimumf128, + minimumf16, + minimumf32, + minimumf64, minnumf128, minnumf16, minnumf32, @@ -1348,6 +1396,7 @@ symbols! { movbe_target_feature, move_ref_pattern, move_size_limit, + movrs_target_feature, mul, mul_assign, mul_with_overflow, @@ -1359,6 +1408,8 @@ symbols! { naked, naked_asm, naked_functions, + naked_functions_rustic_abi, + naked_functions_target_feature, name, names, native_link_modifiers, @@ -1477,14 +1528,18 @@ symbols! { panic_cannot_unwind, panic_const_add_overflow, panic_const_async_fn_resumed, + panic_const_async_fn_resumed_drop, panic_const_async_fn_resumed_panic, panic_const_async_gen_fn_resumed, + panic_const_async_gen_fn_resumed_drop, panic_const_async_gen_fn_resumed_panic, panic_const_coroutine_resumed, + panic_const_coroutine_resumed_drop, panic_const_coroutine_resumed_panic, panic_const_div_by_zero, panic_const_div_overflow, panic_const_gen_fn_none, + panic_const_gen_fn_none_drop, panic_const_gen_fn_none_panic, panic_const_mul_overflow, panic_const_neg_overflow, @@ -1522,6 +1577,7 @@ symbols! { pattern_complexity_limit, pattern_parentheses, pattern_type, + pattern_type_range_trait, pattern_types, permissions_from_mode, phantom_data, @@ -1539,6 +1595,7 @@ symbols! { pointer_like, poll, poll_next, + position, post_dash_lto: "post-lto", postfix_match, powerpc_target_feature, @@ -1554,6 +1611,7 @@ symbols! { precise_capturing, precise_capturing_in_traits, precise_pointer_size_matching, + precision, pref_align_of, prefetch_read_data, prefetch_read_instruction, @@ -1625,6 +1683,7 @@ symbols! { quote, range_inclusive_new, raw_dylib, + raw_dylib_elf, raw_eq, raw_identifiers, raw_ref_op, @@ -1712,6 +1771,7 @@ symbols! { rust_cold_cc, rust_eh_catch_typeinfo, rust_eh_personality, + rust_future, rust_logo, rust_out, rustc, @@ -1738,6 +1798,7 @@ symbols! { rustc_deallocator, rustc_def_path, rustc_default_body_unstable, + rustc_delayed_bug_from_inside_query, rustc_deny_explicit_impl, rustc_deprecated_safe_2024, rustc_diagnostic_item, @@ -1754,7 +1815,6 @@ symbols! { rustc_dump_user_args, rustc_dump_vtable, rustc_effective_visibility, - rustc_error, rustc_evaluate_where_clauses, rustc_expected_cgu_reuse, rustc_force_inline, @@ -1780,6 +1840,7 @@ symbols! { rustc_must_implement_one_of, rustc_never_returns_null_ptr, rustc_never_type_options, + rustc_no_implicit_autorefs, rustc_no_mir_inline, rustc_nonnull_optimization_guaranteed, rustc_nounwind, @@ -1830,10 +1891,12 @@ symbols! { saturating_add, saturating_div, saturating_sub, + sdylib, search_unbox, select_unpredictable, self_in_typedefs, self_struct_ctor, + semiopaque, semitransparent, sha2, sha3, @@ -1866,6 +1929,7 @@ symbols! { simd_eq, simd_expose_provenance, simd_extract, + simd_extract_dyn, simd_fabs, simd_fcos, simd_fexp, @@ -1884,6 +1948,7 @@ symbols! { simd_ge, simd_gt, simd_insert, + simd_insert_dyn, simd_le, simd_lt, simd_masked_load, @@ -2002,8 +2067,8 @@ symbols! { sub_assign, sub_with_overflow, suggestion, + super_let, supertrait_item_shadowing, - surface_async_drop_in_place, sym, sync, synthetic, @@ -2019,6 +2084,10 @@ symbols! { target_has_atomic, target_has_atomic_equal_alignment, target_has_atomic_load_store, + target_has_reliable_f128, + target_has_reliable_f128_math, + target_has_reliable_f16, + target_has_reliable_f16_math, target_os, target_pointer_width, target_thread_local, @@ -2040,7 +2109,6 @@ symbols! { three_way_compare, thumb2, thumb_mode: "thumb-mode", - time, tmm_reg, to_owned_method, to_string, @@ -2083,8 +2151,11 @@ symbols! { type_ascribe, type_ascription, type_changing_struct_update, + type_const, type_id, + type_ir_infer_ctxt_like, type_ir_inherent, + type_ir_interner, type_length_limit, type_macros, type_name, @@ -2160,7 +2231,8 @@ symbols! { unsafe_extern_blocks, unsafe_fields, unsafe_no_drop_flag, - unsafe_pin_internals, + unsafe_pinned, + unsafe_unpin, unsize, unsized_const_param_ty, unsized_const_params, @@ -2179,6 +2251,7 @@ symbols! { unwrap, unwrap_binder, unwrap_or, + use_cloned, use_extern_macros, use_nested_groups, used, @@ -2234,7 +2307,9 @@ symbols! { wasm_abi, wasm_import_module, wasm_target_feature, + where_clause_attrs, while_let, + width, windows, windows_subsystem, with_negative_coherence, @@ -2276,6 +2351,9 @@ pub const STDLIB_STABLE_CRATES: &[Symbol] = &[sym::std, sym::core, sym::alloc, s #[derive(Copy, Clone, Eq, HashStable_Generic, Encodable, Decodable)] pub struct Ident { + // `name` should never be the empty symbol. If you are considering that, + // you are probably conflating "empty identifer with "no identifier" and + // you should use `Option` instead. pub name: Symbol, pub span: Span, } @@ -2283,19 +2361,24 @@ pub struct Ident { impl Ident { #[inline] /// Constructs a new identifier from a symbol and a span. - pub const fn new(name: Symbol, span: Span) -> Ident { + pub fn new(name: Symbol, span: Span) -> Ident { + debug_assert_ne!(name, kw::Empty); Ident { name, span } } /// Constructs a new identifier with a dummy span. #[inline] - pub const fn with_dummy_span(name: Symbol) -> Ident { + pub fn with_dummy_span(name: Symbol) -> Ident { Ident::new(name, DUMMY_SP) } + // For dummy identifiers that are never used and absolutely must be + // present. Note that this does *not* use the empty symbol; `sym::dummy` + // makes it clear that it's intended as a dummy value, and is more likely + // to be detected if it accidentally does get used. #[inline] - pub fn empty() -> Ident { - Ident::with_dummy_span(kw::Empty) + pub fn dummy() -> Ident { + Ident::with_dummy_span(sym::dummy) } /// Maps a string to an identifier with a dummy span. @@ -2472,15 +2555,10 @@ rustc_index::newtype_index! { } impl Symbol { - const fn new(n: u32) -> Self { + pub const fn new(n: u32) -> Self { Symbol(SymbolIndex::from_u32(n)) } - /// for use in Decoder only - pub fn new_from_decoded(n: u32) -> Self { - Self::new(n) - } - /// Maps a string to its interned representation. #[rustc_diagnostic_item = "SymbolIntern"] pub fn intern(string: &str) -> Self { @@ -2514,7 +2592,8 @@ impl Symbol { /// (`token_to_string`, `Ident::to_string`), except that symbols don't keep the rawness flag /// or edition, so we have to guess the rawness using the global edition. pub fn to_ident_string(self) -> String { - Ident::with_dummy_span(self).to_string() + // Avoid creating an empty identifier, because that asserts in debug builds. + if self == kw::Empty { String::new() } else { Ident::with_dummy_span(self).to_string() } } } @@ -2566,11 +2645,14 @@ struct InternerInner { } impl Interner { - fn prefill(init: &[&'static str]) -> Self { - Interner(Lock::new(InternerInner { - arena: Default::default(), - strings: init.iter().copied().collect(), - })) + fn prefill(init: &[&'static str], extra: &[&'static str]) -> Self { + let strings = FxIndexSet::from_iter(init.iter().copied().chain(extra.iter().copied())); + assert_eq!( + strings.len(), + init.len() + extra.len(), + "`init` or `extra` contain duplicate symbols", + ); + Interner(Lock::new(InternerInner { arena: Default::default(), strings })) } #[inline] @@ -2641,11 +2723,6 @@ pub mod sym { } impl Symbol { - /// Don't use this unless you're doing something very loose and heuristic-y. - pub fn is_any_keyword(self) -> bool { - self >= kw::As && self <= kw::Yeet - } - fn is_special(self) -> bool { self <= kw::Underscore } @@ -2654,14 +2731,14 @@ impl Symbol { self >= kw::As && self <= kw::While } - fn is_used_keyword_conditional(self, edition: impl FnOnce() -> Edition) -> bool { - (self >= kw::Async && self <= kw::Dyn) && edition() >= Edition::Edition2018 - } - fn is_unused_keyword_always(self) -> bool { self >= kw::Abstract && self <= kw::Yield } + fn is_used_keyword_conditional(self, edition: impl FnOnce() -> Edition) -> bool { + (self >= kw::Async && self <= kw::Dyn) && edition() >= Edition::Edition2018 + } + fn is_unused_keyword_conditional(self, edition: impl Copy + FnOnce() -> Edition) -> bool { self == kw::Gen && edition().at_least_rust_2024() || self == kw::Try && edition().at_least_rust_2018() @@ -2675,6 +2752,10 @@ impl Symbol { || self.is_unused_keyword_conditional(edition) } + pub fn is_weak(self) -> bool { + self >= kw::Auto && self <= kw::Yeet + } + /// A keyword or reserved identifier that can be used as a path segment. pub fn is_path_segment_keyword(self) -> bool { self == kw::Super @@ -2695,18 +2776,13 @@ impl Symbol { self != kw::Empty && self != kw::Underscore && !self.is_path_segment_keyword() } - /// Is this symbol was interned in compiler's `symbols!` macro - pub fn is_preinterned(self) -> bool { - self.as_u32() < PREINTERNED_SYMBOLS_COUNT + /// Was this symbol predefined in the compiler's `symbols!` macro + pub fn is_predefined(self) -> bool { + self.as_u32() < PREDEFINED_SYMBOLS_COUNT } } impl Ident { - /// Don't use this unless you're doing something very loose and heuristic-y. - pub fn is_any_keyword(self) -> bool { - self.name.is_any_keyword() - } - /// Returns `true` for reserved identifiers used internally for elided lifetimes, /// unnamed method parameters, crate root module, error recovery etc. pub fn is_special(self) -> bool { @@ -2756,7 +2832,7 @@ impl Ident { /// *Note:* Please update this if a new keyword is added beyond the current /// range. pub fn used_keywords(edition: impl Copy + FnOnce() -> Edition) -> Vec { - (kw::Empty.as_u32()..kw::Yeet.as_u32()) + (kw::DollarCrate.as_u32()..kw::Yeet.as_u32()) .filter_map(|kw| { let kw = Symbol::new(kw); if kw.is_used_keyword_always() || kw.is_used_keyword_conditional(edition) { diff --git a/compiler/rustc_span/src/symbol/tests.rs b/compiler/rustc_span/src/symbol/tests.rs index c6aa7627b2b54..660d0d7179afa 100644 --- a/compiler/rustc_span/src/symbol/tests.rs +++ b/compiler/rustc_span/src/symbol/tests.rs @@ -3,7 +3,7 @@ use crate::create_default_session_globals_then; #[test] fn interner_tests() { - let i = Interner::prefill(&[]); + let i = Interner::prefill(&[], &[]); // first one is zero: assert_eq!(i.intern("dog"), Symbol::new(0)); // re-use gets the same entry: diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index 90ddf4c8a0403..12fe6b719f9b8 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -9,7 +9,6 @@ punycode = "0.4.0" rustc-demangle = "0.1.21" rustc_abi = { path = "../rustc_abi" } -rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hashes = { path = "../rustc_hashes" } diff --git a/compiler/rustc_symbol_mangling/src/export.rs b/compiler/rustc_symbol_mangling/src/export.rs new file mode 100644 index 0000000000000..770401fc8cfea --- /dev/null +++ b/compiler/rustc_symbol_mangling/src/export.rs @@ -0,0 +1,181 @@ +use std::assert_matches::debug_assert_matches; + +use rustc_abi::IntegerType; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_hashes::Hash128; +use rustc_hir::def::DefKind; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_span::symbol::{Symbol, sym}; + +trait AbiHashStable<'tcx> { + fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher); +} +macro_rules! default_hash_impl { + ($($t:ty,)+) => { + $(impl<'tcx> AbiHashStable<'tcx> for $t { + #[inline] + fn abi_hash(&self, _tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { + ::std::hash::Hash::hash(self, hasher); + } + })* + }; +} + +default_hash_impl! { i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, } + +impl<'tcx> AbiHashStable<'tcx> for bool { + #[inline] + fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { + (if *self { 1u8 } else { 0u8 }).abi_hash(tcx, hasher); + } +} + +impl<'tcx> AbiHashStable<'tcx> for str { + #[inline] + fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { + self.as_bytes().abi_hash(tcx, hasher); + } +} + +impl<'tcx> AbiHashStable<'tcx> for String { + #[inline] + fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { + self[..].abi_hash(tcx, hasher); + } +} + +impl<'tcx> AbiHashStable<'tcx> for Symbol { + #[inline] + fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { + self.as_str().abi_hash(tcx, hasher); + } +} + +impl<'tcx, T: AbiHashStable<'tcx>> AbiHashStable<'tcx> for [T] { + fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { + self.len().abi_hash(tcx, hasher); + for item in self { + item.abi_hash(tcx, hasher); + } + } +} + +impl<'tcx> AbiHashStable<'tcx> for Ty<'tcx> { + fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { + match self.kind() { + ty::Bool => sym::bool.abi_hash(tcx, hasher), + ty::Char => sym::char.abi_hash(tcx, hasher), + ty::Int(int_ty) => int_ty.name_str().abi_hash(tcx, hasher), + ty::Uint(uint_ty) => uint_ty.name_str().abi_hash(tcx, hasher), + ty::Float(float_ty) => float_ty.name_str().abi_hash(tcx, hasher), + + ty::Adt(adt_def, args) => { + adt_def.is_struct().abi_hash(tcx, hasher); + adt_def.is_enum().abi_hash(tcx, hasher); + adt_def.is_union().abi_hash(tcx, hasher); + + if let Some(align) = adt_def.repr().align { + align.bits().abi_hash(tcx, hasher); + } + + if let Some(integer) = adt_def.repr().int { + match integer { + IntegerType::Pointer(sign) => sign.abi_hash(tcx, hasher), + IntegerType::Fixed(integer, sign) => { + integer.int_ty_str().abi_hash(tcx, hasher); + sign.abi_hash(tcx, hasher); + } + } + } + + if let Some(pack) = adt_def.repr().pack { + pack.bits().abi_hash(tcx, hasher); + } + + adt_def.repr().c().abi_hash(tcx, hasher); + + for variant in adt_def.variants() { + variant.name.abi_hash(tcx, hasher); + for field in &variant.fields { + field.name.abi_hash(tcx, hasher); + let field_ty = tcx.type_of(field.did).instantiate_identity(); + field_ty.abi_hash(tcx, hasher); + } + } + args.abi_hash(tcx, hasher); + } + + ty::Tuple(args) if args.len() == 0 => {} + + // FIXME: Not yet supported. + ty::Foreign(_) + | ty::Ref(_, _, _) + | ty::Str + | ty::Array(_, _) + | ty::Pat(_, _) + | ty::Slice(_) + | ty::RawPtr(_, _) + | ty::FnDef(_, _) + | ty::FnPtr(_, _) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(_, _) + | ty::Never + | ty::Tuple(_) + | ty::Alias(_, _) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Placeholder(_) + | ty::Infer(_) + | ty::UnsafeBinder(_) => unreachable!(), + + ty::Error(_) => {} + } + } +} + +impl<'tcx> AbiHashStable<'tcx> for ty::FnSig<'tcx> { + fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { + for ty in self.inputs_and_output { + ty.abi_hash(tcx, hasher); + } + self.safety.is_safe().abi_hash(tcx, hasher); + } +} + +impl<'tcx> AbiHashStable<'tcx> for ty::GenericArg<'tcx> { + fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { + self.unpack().abi_hash(tcx, hasher); + } +} + +impl<'tcx> AbiHashStable<'tcx> for ty::GenericArgKind<'tcx> { + fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { + match self { + ty::GenericArgKind::Type(t) => t.abi_hash(tcx, hasher), + ty::GenericArgKind::Lifetime(_) | ty::GenericArgKind::Const(_) => unimplemented!(), + } + } +} + +pub(crate) fn compute_hash_of_export_fn<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> String { + let def_id = instance.def_id(); + debug_assert_matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn); + + let args = instance.args; + let sig_ty = tcx.fn_sig(def_id).instantiate(tcx, args); + let sig_ty = tcx.instantiate_bound_regions_with_erased(sig_ty); + + let hash = { + let mut hasher = StableHasher::new(); + sig_ty.abi_hash(tcx, &mut hasher); + hasher.finish::() + }; + + hash.as_u128().to_string() +} diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 88754f1f15b46..db102abda7fa3 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -28,7 +28,10 @@ pub(super) fn mangle<'tcx>( loop { let key = tcx.def_key(ty_def_id); match key.disambiguated_data.data { - DefPathData::TypeNs(_) | DefPathData::ValueNs(_) | DefPathData::Closure => { + DefPathData::TypeNs(_) + | DefPathData::ValueNs(_) + | DefPathData::Closure + | DefPathData::SyntheticCoroutineBody => { instance_ty = tcx.type_of(ty_def_id).instantiate_identity(); debug!(?instance_ty); break; @@ -60,10 +63,17 @@ pub(super) fn mangle<'tcx>( .print_def_path( def_id, if let ty::InstanceKind::DropGlue(_, _) - | ty::InstanceKind::AsyncDropGlueCtorShim(_, _) = instance.def + | ty::InstanceKind::AsyncDropGlueCtorShim(_, _) + | ty::InstanceKind::FutureDropPollShim(_, _, _) = instance.def { // Add the name of the dropped type to the symbol name &*instance.args + } else if let ty::InstanceKind::AsyncDropGlue(_, ty) = instance.def { + let ty::Coroutine(_, cor_args) = ty.kind() else { + bug!(); + }; + let drop_ty = cor_args.first().unwrap().expect_ty(); + tcx.mk_args(&[GenericArg::from(drop_ty)]) } else { &[] }, @@ -96,6 +106,10 @@ pub(super) fn mangle<'tcx>( _ => {} } + if let ty::InstanceKind::FutureDropPollShim(..) = instance.def { + let _ = printer.write_str("{{drop-shim}}"); + } + printer.path.finish(hash) } diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 4312c82815c16..a9bf5eae445e4 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -91,9 +91,8 @@ #![allow(internal_features)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(let_chains)] +#![feature(assert_matches)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use rustc_hir::def::DefKind; @@ -105,6 +104,7 @@ use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_session::config::SymbolManglingVersion; use tracing::debug; +mod export; mod hashed; mod legacy; mod v0; @@ -112,6 +112,8 @@ mod v0; pub mod errors; pub mod test; +pub use v0::mangle_internal_symbol; + /// 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. @@ -183,6 +185,39 @@ fn compute_symbol_name<'tcx>( CodegenFnAttrs::EMPTY }; + if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { + // Items marked as #[rustc_std_internal_symbol] need to have a fixed + // symbol name because it is used to import items from another crate + // without a direct dependency. As such it is not possible to look up + // the mangled name for the `Instance` from the crate metadata of the + // defining crate. + // Weak lang items automatically get #[rustc_std_internal_symbol] + // applied by the code computing the CodegenFnAttrs. + // We are mangling all #[rustc_std_internal_symbol] items that don't + // also have #[no_mangle] as a combination of the rustc version and the + // unmangled linkage name. This is to ensure that if we link against a + // staticlib compiled by a different rustc version, we don't get symbol + // conflicts or even UB due to a different implementation/ABI. Rust + // staticlibs currently export all symbols, including those that are + // hidden in cdylibs. + // We are using the v0 symbol mangling scheme here as we need to be + // consistent across all crates and in some contexts the legacy symbol + // mangling scheme can't be used. For example both the GCC backend and + // Rust-for-Linux don't support some of the characters used by the + // legacy symbol mangling scheme. + let name = if tcx.is_foreign_item(def_id) { + if let Some(name) = attrs.link_name { name } else { tcx.item_name(def_id) } + } else { + if let Some(name) = attrs.export_name { name } else { tcx.item_name(def_id) } + }; + + if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { + return name.to_string(); + } else { + return v0::mangle_internal_symbol(tcx, name.as_str()); + } + } + // Foreign items by default use no mangling for their symbol name. There's a // few exceptions to this rule though: // @@ -198,6 +233,8 @@ fn compute_symbol_name<'tcx>( // is present we mangle everything on wasm because the demangled form will // show up in the `wasm-import-name` custom attribute in LLVM IR. // + // * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way + // both for exports and imports through foreign items. This is handled above. // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316 if tcx.is_foreign_item(def_id) && (!tcx.sess.target.is_like_wasm @@ -260,12 +297,21 @@ fn compute_symbol_name<'tcx>( tcx.symbol_mangling_version(mangling_version_crate) }; - let symbol = match mangling_version { - SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate), - SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate), - SymbolManglingVersion::Hashed => hashed::mangle(tcx, instance, instantiating_crate, || { - v0::mangle(tcx, instance, instantiating_crate) - }), + let symbol = match tcx.is_exportable(def_id) { + true => format!( + "{}.{}", + v0::mangle(tcx, instance, instantiating_crate, true), + export::compute_hash_of_export_fn(tcx, instance) + ), + false => match mangling_version { + SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate), + SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate, false), + SymbolManglingVersion::Hashed => { + hashed::mangle(tcx, instance, instantiating_crate, || { + v0::mangle(tcx, instance, instantiating_crate, false) + }) + } + }, }; debug_assert!( diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index ddeeadff13d17..0c6d1495e39cf 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -56,7 +56,7 @@ impl SymbolNamesTest<'_> { // some subset. for attr in tcx.get_attrs(def_id, SYMBOL_NAME) { let def_id = def_id.to_def_id(); - let instance = Instance::new( + let instance = Instance::new_raw( def_id, tcx.erase_regions(GenericArgs::identity_for_item(tcx, def_id)), ); diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 4fafd1ac3509a..4a99ce09b39ab 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -1,4 +1,5 @@ use std::fmt::Write; +use std::hash::Hasher; use std::iter; use std::ops::Range; @@ -6,6 +7,8 @@ use rustc_abi::{ExternAbi, Integer}; use rustc_data_structures::base_n::ToBaseN; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_hashes::Hash64; use rustc_hir as hir; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{CrateNum, DefId}; @@ -23,6 +26,7 @@ pub(super) fn mangle<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, instantiating_crate: Option, + is_exportable: bool, ) -> String { let def_id = instance.def_id(); // FIXME(eddyb) this should ideally not be needed. @@ -32,6 +36,7 @@ pub(super) fn mangle<'tcx>( let mut cx: SymbolMangler<'_> = SymbolMangler { tcx, start_offset: prefix.len(), + is_exportable, paths: FxHashMap::default(), types: FxHashMap::default(), consts: FxHashMap::default(), @@ -55,11 +60,17 @@ pub(super) fn mangle<'tcx>( ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref: false, .. } => { Some("by_ref") } - + ty::InstanceKind::FutureDropPollShim(_, _, _) => Some("drop"), _ => None, }; - if let Some(shim_kind) = shim_kind { + if let ty::InstanceKind::AsyncDropGlue(_, ty) = instance.def { + let ty::Coroutine(_, cor_args) = ty.kind() else { + bug!(); + }; + let drop_ty = cor_args.first().unwrap().expect_ty(); + cx.print_def_path(def_id, tcx.mk_args(&[GenericArg::from(drop_ty)])).unwrap() + } else if let Some(shim_kind) = shim_kind { cx.path_append_ns(|cx| cx.print_def_path(def_id, args), 'S', 0, shim_kind).unwrap() } else { cx.print_def_path(def_id, args).unwrap() @@ -70,6 +81,55 @@ pub(super) fn mangle<'tcx>( std::mem::take(&mut cx.out) } +pub fn mangle_internal_symbol<'tcx>(tcx: TyCtxt<'tcx>, item_name: &str) -> String { + if item_name == "rust_eh_personality" { + // rust_eh_personality must not be renamed as LLVM hard-codes the name + return "rust_eh_personality".to_owned(); + } else if item_name == "__rust_no_alloc_shim_is_unstable" { + // Temporary back compat hack to give people the chance to migrate to + // include #[rustc_std_internal_symbol]. + return "__rust_no_alloc_shim_is_unstable".to_owned(); + } + + let prefix = "_R"; + let mut cx: SymbolMangler<'_> = SymbolMangler { + tcx, + start_offset: prefix.len(), + is_exportable: false, + paths: FxHashMap::default(), + types: FxHashMap::default(), + consts: FxHashMap::default(), + binders: vec![], + out: String::from(prefix), + }; + + cx.path_append_ns( + |cx| { + cx.push("C"); + cx.push_disambiguator({ + let mut hasher = StableHasher::new(); + // Incorporate the rustc version to ensure #[rustc_std_internal_symbol] functions + // get a different symbol name depending on the rustc version. + // + // RUSTC_FORCE_RUSTC_VERSION is ignored here as otherwise different we would get an + // abi incompatibility with the standard library. + hasher.write(tcx.sess.cfg_version.as_bytes()); + + let hash: Hash64 = hasher.finish(); + hash.as_u64() + }); + cx.push_ident("__rustc"); + Ok(()) + }, + 'v', + 0, + item_name, + ) + .unwrap(); + + std::mem::take(&mut cx.out) +} + pub(super) fn mangle_typeid_for_trait_ref<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::ExistentialTraitRef<'tcx>, @@ -78,6 +138,7 @@ pub(super) fn mangle_typeid_for_trait_ref<'tcx>( let mut cx = SymbolMangler { tcx, start_offset: 0, + is_exportable: false, paths: FxHashMap::default(), types: FxHashMap::default(), consts: FxHashMap::default(), @@ -106,6 +167,7 @@ struct SymbolMangler<'tcx> { tcx: TyCtxt<'tcx>, binders: Vec, out: String, + is_exportable: bool, /// The length of the prefix in `out` (e.g. 2 for `_R`). start_offset: usize, @@ -169,7 +231,7 @@ impl<'tcx> SymbolMangler<'tcx> { Ok(()) } - fn in_binder( + fn wrap_binder( &mut self, value: &ty::Binder<'tcx, T>, print_value: impl FnOnce(&mut Self, &T) -> Result<(), PrintError>, @@ -196,6 +258,22 @@ impl<'tcx> SymbolMangler<'tcx> { Ok(()) } + + fn print_pat(&mut self, pat: ty::Pattern<'tcx>) -> Result<(), std::fmt::Error> { + Ok(match *pat { + ty::PatternKind::Range { start, end } => { + let consts = [start, end]; + for ct in consts { + Ty::new_array_with_const_len(self.tcx, self.tcx.types.unit, ct).print(self)?; + } + } + ty::PatternKind::Or(patterns) => { + for pat in patterns { + self.print_pat(pat)?; + } + } + }) + } } impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { @@ -303,7 +381,14 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { args, )?; } else { - self.push_disambiguator(key.disambiguated_data.disambiguator as u64); + let exported_impl_order = self.tcx.stable_order_of_exportable_impls(impl_def_id.krate); + let disambiguator = match self.is_exportable { + true => exported_impl_order[&impl_def_id] as u64, + false => { + exported_impl_order.len() as u64 + key.disambiguated_data.disambiguator as u64 + } + }; + self.push_disambiguator(disambiguator); self.print_def_path(parent_def_id, &[])?; } @@ -317,7 +402,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { } fn print_region(&mut self, region: ty::Region<'_>) -> Result<(), PrintError> { - let i = match *region { + let i = match region.kind() { // Erased lifetimes use the index 0, for a // shorter mangling of `L_`. ty::ReErased => 0, @@ -412,24 +497,14 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { ty.print(self)?; } - ty::Pat(ty, pat) => match *pat { - ty::PatternKind::Range { start, end, include_end } => { - let consts = [ - start.unwrap_or(self.tcx.consts.unit), - end.unwrap_or(self.tcx.consts.unit), - ty::Const::from_bool(self.tcx, include_end), - ]; - // HACK: Represent as tuple until we have something better. - // HACK: constants are used in arrays, even if the types don't match. - self.push("T"); - ty.print(self)?; - for ct in consts { - Ty::new_array_with_const_len(self.tcx, self.tcx.types.unit, ct) - .print(self)?; - } - self.push("E"); - } - }, + ty::Pat(ty, pat) => { + // HACK: Represent as tuple until we have something better. + // HACK: constants are used in arrays, even if the types don't match. + self.push("T"); + ty.print(self)?; + self.print_pat(pat)?; + self.push("E"); + } ty::Array(ty, len) => { self.push("A"); @@ -471,7 +546,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { ty::FnPtr(sig_tys, hdr) => { let sig = sig_tys.with(hdr); self.push("F"); - self.in_binder(&sig, |cx, sig| { + self.wrap_binder(&sig, |cx, sig| { if sig.safety.is_unsafe() { cx.push("U"); } @@ -554,7 +629,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { // [ [{}]] [{}] // Since any predicates after the first one shouldn't change the binders, // just put them all in the binders of the first. - self.in_binder(&predicates[0], |cx, _| { + self.wrap_binder(&predicates[0], |cx, _| { for predicate in predicates.iter() { // It would be nice to be able to validate bound vars here, but // projections can actually include bound vars from super traits @@ -568,7 +643,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { cx.print_def_path(trait_ref.def_id, trait_ref.args)?; } ty::ExistentialPredicate::Projection(projection) => { - let name = cx.tcx.associated_item(projection.def_id).name; + let name = cx.tcx.associated_item(projection.def_id).name(); cx.push("p"); cx.push_ident(name.as_str()); match projection.term.unpack() { @@ -729,7 +804,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { self.push_disambiguator( disambiguated_field.disambiguator as u64, ); - self.push_ident(field_name.unwrap_or(kw::Empty).as_str()); + self.push_ident(field_name.unwrap().as_str()); field.print(self)?; } @@ -755,8 +830,10 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.push("C"); - let stable_crate_id = self.tcx.def_path_hash(cnum.as_def_id()).stable_crate_id(); - self.push_disambiguator(stable_crate_id.as_u64()); + if !self.is_exportable { + let stable_crate_id = self.tcx.def_path_hash(cnum.as_def_id()).stable_crate_id(); + self.push_disambiguator(stable_crate_id.as_u64()); + } let name = self.tcx.crate_name(cnum); self.push_ident(name.as_str()); Ok(()) @@ -803,6 +880,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { DefPathData::Ctor => 'c', DefPathData::AnonConst => 'k', DefPathData::OpaqueTy => 'i', + DefPathData::SyntheticCoroutineBody => 's', + DefPathData::NestedStatic => 'n', // These should never show up as `path_append` arguments. DefPathData::CrateRoot @@ -810,7 +889,9 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { | DefPathData::GlobalAsm | DefPathData::Impl | DefPathData::MacroNs(_) - | DefPathData::LifetimeNs(_) => { + | DefPathData::LifetimeNs(_) + | DefPathData::OpaqueLifetime(_) + | DefPathData::AnonAssocTy(..) => { bug!("symbol_names: unexpected DefPathData: {:?}", disambiguated_data.data) } }; diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index cdccb3e5d728e..43a8d9ca119df 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -78,7 +78,7 @@ pub(crate) fn target_reserves_x18(target: &Target, target_features: &FxIndexSet< target.os == "android" || target.os == "fuchsia" || target.env == "ohos" - || target.is_like_osx + || target.is_like_darwin || target.is_like_windows || target_features.contains(&sym::reserve_x18) } diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index ff0cbddecf78b..7fea10ff067bf 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -68,7 +68,7 @@ impl ArmInlineAsmRegClass { // This uses the same logic as useR7AsFramePointer in LLVM fn frame_pointer_is_r7(target_features: &FxIndexSet, target: &Target) -> bool { - target.is_like_osx || (!target.is_like_windows && target_features.contains(&sym::thumb_mode)) + target.is_like_darwin || (!target.is_like_windows && target_features.contains(&sym::thumb_mode)) } fn frame_pointer_r11( diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index 209d7483e612a..c779720f97b9c 100644 --- a/compiler/rustc_target/src/callconv/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs @@ -1,6 +1,6 @@ use rustc_abi::{ - BackendRepr, ExternAbi, FieldsShape, HasDataLayout, Primitive, Reg, RegKind, Size, - TyAbiInterface, TyAndLayout, Variants, + BackendRepr, FieldsShape, HasDataLayout, Primitive, Reg, RegKind, Size, TyAbiInterface, + TyAndLayout, Variants, }; use crate::callconv::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Uniform}; @@ -364,15 +364,11 @@ where } } -pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: ExternAbi) +pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - if abi == ExternAbi::RustIntrinsic { - return; - } - let grlen = cx.data_layout().pointer_size.bits(); for arg in fn_abi.args.iter_mut() { diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 198d08864bec3..907614520a2cf 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; use std::str::FromStr; use std::{fmt, iter}; @@ -31,6 +32,7 @@ mod sparc64; mod wasm; mod x86; mod x86_64; +mod x86_win32; mod x86_win64; mod xtensa; @@ -143,6 +145,7 @@ pub struct ArgAttributes { /// (corresponding to LLVM's dereferenceable_or_null attributes, i.e., it is okay for this to be /// set on a null pointer, but all non-null pointers must be dereferenceable). pub pointee_size: Size, + /// The minimum alignment of the pointee, if any. pub pointee_align: Option, } @@ -649,7 +652,11 @@ impl<'a, Ty> FnAbi<'a, Ty> { }; let reg_struct_return = cx.x86_abi_opt().reg_struct_return; let opts = x86::X86Options { flavor, regparm, reg_struct_return }; - x86::compute_abi_info(cx, self, opts); + if spec.is_like_msvc { + x86_win32::compute_abi_info(cx, self, opts); + } else { + x86::compute_abi_info(cx, self, opts); + } } "x86_64" => match abi { ExternAbi::SysV64 { .. } => x86_64::compute_abi_info(cx, self), @@ -665,7 +672,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { } }, "aarch64" | "arm64ec" => { - let kind = if cx.target_spec().is_like_osx { + let kind = if cx.target_spec().is_like_darwin { aarch64::AbiKind::DarwinPCS } else if cx.target_spec().is_like_windows { aarch64::AbiKind::Win64 @@ -700,7 +707,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { "xtensa" => xtensa::compute_abi_info(cx, self), "riscv32" | "riscv64" => riscv::compute_abi_info(cx, self), "wasm32" => { - if spec.os == "unknown" && cx.wasm_c_abi_opt() == WasmCAbi::Legacy { + if spec.os == "unknown" && matches!(cx.wasm_c_abi_opt(), WasmCAbi::Legacy { .. }) { wasm::compute_wasm_abi_info(self) } else { wasm::compute_c_abi_info(cx, self) @@ -712,16 +719,16 @@ impl<'a, Ty> FnAbi<'a, Ty> { } } - pub fn adjust_for_rust_abi(&mut self, cx: &C, abi: ExternAbi) + pub fn adjust_for_rust_abi(&mut self, cx: &C) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { let spec = cx.target_spec(); match &*spec.arch { - "x86" => x86::compute_rust_abi_info(cx, self, abi), - "riscv32" | "riscv64" => riscv::compute_rust_abi_info(cx, self, abi), - "loongarch64" => loongarch::compute_rust_abi_info(cx, self, abi), + "x86" => x86::compute_rust_abi_info(cx, self), + "riscv32" | "riscv64" => riscv::compute_rust_abi_info(cx, self), + "loongarch64" => loongarch::compute_rust_abi_info(cx, self), "aarch64" => aarch64::compute_rust_abi_info(cx, self), _ => {} }; @@ -845,10 +852,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { // // Note that the intrinsic ABI is exempt here as those are not // real functions anyway, and the backend expects very specific types. - if abi != ExternAbi::RustIntrinsic - && spec.simd_types_indirect - && !can_pass_simd_directly(arg) - { + if spec.simd_types_indirect && !can_pass_simd_directly(arg) { arg.make_indirect(); } } @@ -892,6 +896,37 @@ impl FromStr for Conv { } } +fn conv_to_externabi(conv: &Conv) -> ExternAbi { + match conv { + Conv::C => ExternAbi::C { unwind: false }, + Conv::Rust => ExternAbi::Rust, + Conv::PreserveMost => ExternAbi::RustCold, + Conv::ArmAapcs => ExternAbi::Aapcs { unwind: false }, + Conv::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall, + Conv::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry, + Conv::Msp430Intr => ExternAbi::Msp430Interrupt, + Conv::GpuKernel => ExternAbi::GpuKernel, + Conv::X86Fastcall => ExternAbi::Fastcall { unwind: false }, + Conv::X86Intr => ExternAbi::X86Interrupt, + Conv::X86Stdcall => ExternAbi::Stdcall { unwind: false }, + Conv::X86ThisCall => ExternAbi::Thiscall { unwind: false }, + Conv::X86VectorCall => ExternAbi::Vectorcall { unwind: false }, + Conv::X86_64SysV => ExternAbi::SysV64 { unwind: false }, + Conv::X86_64Win64 => ExternAbi::Win64 { unwind: false }, + Conv::AvrInterrupt => ExternAbi::AvrInterrupt, + Conv::AvrNonBlockingInterrupt => ExternAbi::AvrNonBlockingInterrupt, + Conv::RiscvInterrupt { kind: RiscvInterruptKind::Machine } => ExternAbi::RiscvInterruptM, + Conv::RiscvInterrupt { kind: RiscvInterruptKind::Supervisor } => ExternAbi::RiscvInterruptS, + Conv::Cold | Conv::PreserveAll => unreachable!(), + } +} + +impl Display for Conv { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", conv_to_externabi(self)) + } +} + // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index 7368e225efa74..cd1d3cd1eee05 100644 --- a/compiler/rustc_target/src/callconv/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs @@ -5,8 +5,8 @@ // https://github.com/llvm/llvm-project/blob/8e780252a7284be45cf1ba224cabd884847e8e92/clang/lib/CodeGen/TargetInfo.cpp#L9311-L9773 use rustc_abi::{ - BackendRepr, ExternAbi, FieldsShape, HasDataLayout, Primitive, Reg, RegKind, Size, - TyAbiInterface, TyAndLayout, Variants, + BackendRepr, FieldsShape, HasDataLayout, Primitive, Reg, RegKind, Size, TyAbiInterface, + TyAndLayout, Variants, }; use crate::callconv::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Uniform}; @@ -370,15 +370,11 @@ where } } -pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: ExternAbi) +pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - if abi == ExternAbi::RustIntrinsic { - return; - } - let xlen = cx.data_layout().pointer_size.bits(); for arg in fn_abi.args.iter_mut() { diff --git a/compiler/rustc_target/src/callconv/wasm.rs b/compiler/rustc_target/src/callconv/wasm.rs index 364a655113114..881168c98c32e 100644 --- a/compiler/rustc_target/src/callconv/wasm.rs +++ b/compiler/rustc_target/src/callconv/wasm.rs @@ -10,6 +10,9 @@ where if val.layout.is_aggregate() { if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) { let size = val.layout.size; + // This size check also catches over-aligned scalars as `size` will be rounded up to a + // multiple of the alignment, and the default alignment of all scalar types on wasm + // equals their size. if unit.size == size { val.cast_to(unit); return true; diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index 73aff85a0ad88..8328f818f9b8f 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -1,6 +1,6 @@ use rustc_abi::{ - AddressSpace, Align, BackendRepr, ExternAbi, HasDataLayout, Primitive, Reg, RegKind, - TyAbiInterface, TyAndLayout, + AddressSpace, Align, BackendRepr, HasDataLayout, Primitive, Reg, RegKind, TyAbiInterface, + TyAndLayout, }; use crate::callconv::{ArgAttribute, FnAbi, PassMode}; @@ -36,7 +36,7 @@ where if t.abi_return_struct_as_int || opts.reg_struct_return { // According to Clang, everyone but MSVC returns single-element // float aggregates directly in a floating-point register. - if !t.is_like_msvc && fn_abi.ret.layout.is_single_fp_element(cx) { + if fn_abi.ret.layout.is_single_fp_element(cx) { match fn_abi.ret.layout.size.bytes() { 4 => fn_abi.ret.cast_to(Reg::f32()), 8 => fn_abi.ret.cast_to(Reg::f64()), @@ -64,31 +64,11 @@ where continue; } - // FIXME: MSVC 2015+ will pass the first 3 vector arguments in [XYZ]MM0-2 - // See https://reviews.llvm.org/D72114 for Clang behavior - let t = cx.target_spec(); let align_4 = Align::from_bytes(4).unwrap(); let align_16 = Align::from_bytes(16).unwrap(); - if t.is_like_msvc - && arg.layout.is_adt() - && let Some(max_repr_align) = arg.layout.max_repr_align - && max_repr_align > align_4 - { - // MSVC has special rules for overaligned arguments: https://reviews.llvm.org/D72114. - // Summarized here: - // - Arguments with _requested_ alignment > 4 are passed indirectly. - // - For backwards compatibility, arguments with natural alignment > 4 are still passed - // on stack (via `byval`). For example, this includes `double`, `int64_t`, - // and structs containing them, provided they lack an explicit alignment attribute. - assert!( - arg.layout.align.abi >= max_repr_align, - "abi alignment {:?} less than requested alignment {max_repr_align:?}", - arg.layout.align.abi, - ); - arg.make_indirect(); - } else if arg.layout.is_aggregate() { + if arg.layout.is_aggregate() { // We need to compute the alignment of the `byval` argument. The rules can be found in // `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized // here, they are: @@ -124,7 +104,7 @@ where let byval_align = if arg.layout.align.abi < align_4 { // (1.) align_4 - } else if t.is_like_osx && contains_vector(cx, arg.layout) { + } else if t.is_like_darwin && contains_vector(cx, arg.layout) { // (3.) align_16 } else { @@ -213,7 +193,7 @@ pub(crate) fn fill_inregs<'a, Ty, C>( } } -pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: ExternAbi) +pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, @@ -221,10 +201,7 @@ where // Avoid returning floats in x87 registers on x86 as loading and storing from x87 // registers will quiet signalling NaNs. Also avoid using SSE registers since they // are not always available (depending on target features). - if !fn_abi.ret.is_ignore() - // Intrinsics themselves are not "real" functions, so theres no need to change their ABIs. - && abi != ExternAbi::RustIntrinsic - { + if !fn_abi.ret.is_ignore() { let has_float = match fn_abi.ret.layout.backend_repr { BackendRepr::Scalar(s) => matches!(s.primitive(), Primitive::Float(_)), BackendRepr::ScalarPair(s1, s2) => { diff --git a/compiler/rustc_target/src/callconv/x86_win32.rs b/compiler/rustc_target/src/callconv/x86_win32.rs new file mode 100644 index 0000000000000..554a7368848b5 --- /dev/null +++ b/compiler/rustc_target/src/callconv/x86_win32.rs @@ -0,0 +1,81 @@ +use rustc_abi::{Align, HasDataLayout, Reg, TyAbiInterface}; + +use crate::callconv::FnAbi; +use crate::spec::HasTargetSpec; + +pub(crate) fn compute_abi_info<'a, Ty, C>( + cx: &C, + fn_abi: &mut FnAbi<'a, Ty>, + opts: super::x86::X86Options, +) where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + if !fn_abi.ret.is_ignore() { + if fn_abi.ret.layout.is_aggregate() && fn_abi.ret.layout.is_sized() { + // Returning a structure. Most often, this will use + // a hidden first argument. On some platforms, though, + // small structs are returned as integers. + // + // Some links: + // https://www.angelcode.com/dev/callconv/callconv.html + // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp + let t = cx.target_spec(); + // MSVC does not special-case 1-element float aggregates, unlike others. + // GCC used to apply the SysV rule here, breaking windows-gnu's ABI, but was fixed: + // - reported in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82028 + // - fixed in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85667 + if t.abi_return_struct_as_int || opts.reg_struct_return { + match fn_abi.ret.layout.size.bytes() { + 1 => fn_abi.ret.cast_to(Reg::i8()), + 2 => fn_abi.ret.cast_to(Reg::i16()), + 4 => fn_abi.ret.cast_to(Reg::i32()), + 8 => fn_abi.ret.cast_to(Reg::i64()), + _ => fn_abi.ret.make_indirect(), + } + } else { + fn_abi.ret.make_indirect(); + } + } else { + fn_abi.ret.extend_integer_width_to(32); + } + } + + for arg in fn_abi.args.iter_mut() { + if arg.is_ignore() || !arg.layout.is_sized() { + continue; + } + + // FIXME: MSVC 2015+ will pass the first 3 vector arguments in [XYZ]MM0-2 + // See https://reviews.llvm.org/D72114 for Clang behavior + + let align_4 = Align::from_bytes(4).unwrap(); + + if arg.layout.is_adt() + && let Some(max_repr_align) = arg.layout.max_repr_align + && max_repr_align > align_4 + { + // MSVC has special rules for overaligned arguments: https://reviews.llvm.org/D72114. + // Summarized here: + // - Arguments with _requested_ alignment > 4 are passed indirectly. + // - For backwards compatibility, arguments with natural alignment > 4 are still passed + // on stack (via `byval`). For example, this includes `double`, `int64_t`, + // and structs containing them, provided they lack an explicit alignment attribute. + assert!( + arg.layout.align.abi >= max_repr_align, + "abi alignment {:?} less than requested alignment {max_repr_align:?}", + arg.layout.align.abi, + ); + arg.make_indirect(); + } else if arg.layout.is_aggregate() { + // Alignment of the `byval` argument. + // The rules can be found in `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. + let byval_align = align_4; + arg.pass_by_stack_offset(Some(byval_align)); + } else { + arg.extend_integer_width_to(32); + } + } + + super::x86::fill_inregs(cx, fn_abi, opts, false); +} diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 7ebe96960ed0f..566bee75c7f3c 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -12,11 +12,10 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] +#![feature(debug_closure_helpers)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use std::path::{Path, PathBuf}; diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index 66c85146c2944..46fcd7d5c5198 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -1,9 +1,12 @@ use std::borrow::Cow; use std::env; +use std::fmt::{Display, from_fn}; +use std::num::ParseIntError; +use std::str::FromStr; use crate::spec::{ BinaryFormat, Cc, DebuginfoKind, FloatAbi, FramePointer, LinkerFlavor, Lld, RustcAbi, - SplitDebuginfo, StackProbeType, StaticCow, TargetOptions, cvs, + SplitDebuginfo, StackProbeType, StaticCow, Target, TargetOptions, cvs, }; #[cfg(test)] @@ -115,7 +118,7 @@ pub(crate) fn base( function_sections: false, dynamic_linking: true, families: cvs!["unix"], - is_like_osx: true, + is_like_darwin: true, binary_format: BinaryFormat::MachO, // LLVM notes that macOS 10.11+ and iOS 9+ default // to v4, so we do the same. @@ -222,3 +225,107 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow]> { cvs!["MACOSX_DEPLOYMENT_TARGET"] } } + +/// Deployment target or SDK version. +/// +/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct OSVersion { + pub major: u16, + pub minor: u8, + pub patch: u8, +} + +impl FromStr for OSVersion { + type Err = ParseIntError; + + /// Parse an OS version triple (SDK version or deployment target). + fn from_str(version: &str) -> Result { + if let Some((major, minor)) = version.split_once('.') { + let major = major.parse()?; + if let Some((minor, patch)) = minor.split_once('.') { + Ok(Self { major, minor: minor.parse()?, patch: patch.parse()? }) + } else { + Ok(Self { major, minor: minor.parse()?, patch: 0 }) + } + } else { + Ok(Self { major: version.parse()?, minor: 0, patch: 0 }) + } + } +} + +impl OSVersion { + pub fn new(major: u16, minor: u8, patch: u8) -> Self { + Self { major, minor, patch } + } + + pub fn fmt_pretty(self) -> impl Display { + let Self { major, minor, patch } = self; + from_fn(move |f| { + write!(f, "{major}.{minor}")?; + if patch != 0 { + write!(f, ".{patch}")?; + } + Ok(()) + }) + } + + pub fn fmt_full(self) -> impl Display { + let Self { major, minor, patch } = self; + from_fn(move |f| write!(f, "{major}.{minor}.{patch}")) + } + + /// Minimum operating system versions currently supported by `rustc`. + pub fn os_minimum_deployment_target(os: &str) -> Self { + // When bumping a version in here, remember to update the platform-support docs too. + // + // NOTE: The defaults may change in future `rustc` versions, so if you are looking for the + // default deployment target, prefer: + // ``` + // $ rustc --print deployment-target + // ``` + let (major, minor, patch) = match os { + "macos" => (10, 12, 0), + "ios" => (10, 0, 0), + "tvos" => (10, 0, 0), + "watchos" => (5, 0, 0), + "visionos" => (1, 0, 0), + _ => unreachable!("tried to get deployment target for non-Apple platform"), + }; + Self { major, minor, patch } + } + + /// The deployment target for the given target. + /// + /// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense + /// to raise the minimum OS version. + /// + /// This matches what LLVM does, see in part: + /// + pub fn minimum_deployment_target(target: &Target) -> Self { + let (major, minor, patch) = match (&*target.os, &*target.arch, &*target.abi) { + ("macos", "aarch64", _) => (11, 0, 0), + ("ios", "aarch64", "macabi") => (14, 0, 0), + ("ios", "aarch64", "sim") => (14, 0, 0), + ("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0), + // Mac Catalyst defaults to 13.1 in Clang. + ("ios", _, "macabi") => (13, 1, 0), + ("tvos", "aarch64", "sim") => (14, 0, 0), + ("watchos", "aarch64", "sim") => (7, 0, 0), + (os, _, _) => return Self::os_minimum_deployment_target(os), + }; + Self { major, minor, patch } + } +} + +/// Name of the environment variable used to fetch the deployment target on the given OS. +pub fn deployment_target_env_var(os: &str) -> &'static str { + match os { + "macos" => "MACOSX_DEPLOYMENT_TARGET", + "ios" => "IPHONEOS_DEPLOYMENT_TARGET", + "watchos" => "WATCHOS_DEPLOYMENT_TARGET", + "tvos" => "TVOS_DEPLOYMENT_TARGET", + "visionos" => "XROS_DEPLOYMENT_TARGET", + _ => unreachable!("tried to get deployment target env var for non-Apple platform"), + } +} diff --git a/compiler/rustc_target/src/spec/base/apple/tests.rs b/compiler/rustc_target/src/spec/base/apple/tests.rs index 7a985ad4dc056..391f347010436 100644 --- a/compiler/rustc_target/src/spec/base/apple/tests.rs +++ b/compiler/rustc_target/src/spec/base/apple/tests.rs @@ -1,3 +1,4 @@ +use super::OSVersion; use crate::spec::targets::{ aarch64_apple_darwin, aarch64_apple_ios_sim, aarch64_apple_visionos_sim, aarch64_apple_watchos_sim, i686_apple_darwin, x86_64_apple_darwin, x86_64_apple_ios, @@ -42,3 +43,11 @@ fn macos_link_environment_unmodified() { ); } } + +#[test] +fn test_parse_version() { + assert_eq!("10".parse(), Ok(OSVersion::new(10, 0, 0))); + assert_eq!("10.12".parse(), Ok(OSVersion::new(10, 12, 0))); + assert_eq!("10.12.6".parse(), Ok(OSVersion::new(10, 12, 6))); + assert_eq!("9999.99.99".parse(), Ok(OSVersion::new(9999, 99, 99))); +} diff --git a/compiler/rustc_target/src/spec/base/fuchsia.rs b/compiler/rustc_target/src/spec/base/fuchsia.rs index 9deafcac27f15..92cb0299ddb2f 100644 --- a/compiler/rustc_target/src/spec/base/fuchsia.rs +++ b/compiler/rustc_target/src/spec/base/fuchsia.rs @@ -7,7 +7,7 @@ pub(crate) fn opts() -> TargetOptions { // now. When using clang as the linker it will supply these options for us, // so we only list them for ld/lld. // - // https://github.com/llvm/llvm-project/blob/db9322b2066c55254e7691efeab863f43bfcc084/clang/lib/Driver/ToolChains/Fuchsia.cpp#L31 + // https://github.com/llvm/llvm-project/blob/0419db6b95e246fe9dc90b5795beb77c393eb2ce/clang/lib/Driver/ToolChains/Fuchsia.cpp#L32 let pre_link_args = TargetOptions::link_args( LinkerFlavor::Gnu(Cc::No, Lld::No), &[ @@ -18,9 +18,13 @@ pub(crate) fn opts() -> TargetOptions { "-z", "now", "-z", + "start-stop-visibility=hidden", + "-z", "rodynamic", "-z", "separate-loadable-segments", + "-z", + "rel", "--pack-dyn-relocs=relr", ], ); diff --git a/compiler/rustc_target/src/spec/base/linux_musl.rs b/compiler/rustc_target/src/spec/base/linux_musl.rs index 1a854fe362d50..1bef602404e56 100644 --- a/compiler/rustc_target/src/spec/base/linux_musl.rs +++ b/compiler/rustc_target/src/spec/base/linux_musl.rs @@ -1,12 +1,11 @@ use crate::spec::{LinkSelfContainedDefault, TargetOptions, base, crt_objects}; pub(crate) fn opts() -> TargetOptions { - let mut base = base::linux::opts(); - - base.env = "musl".into(); - base.pre_link_objects_self_contained = crt_objects::pre_musl_self_contained(); - base.post_link_objects_self_contained = crt_objects::post_musl_self_contained(); - base.link_self_contained = LinkSelfContainedDefault::InferredForMusl; - - base + TargetOptions { + env: "musl".into(), + pre_link_objects_self_contained: crt_objects::pre_musl_self_contained(), + post_link_objects_self_contained: crt_objects::post_musl_self_contained(), + link_self_contained: LinkSelfContainedDefault::InferredForMusl, + ..base::linux::opts() + } } diff --git a/compiler/rustc_target/src/spec/base/linux_ohos.rs b/compiler/rustc_target/src/spec/base/linux_ohos.rs index 6f4d69a996c34..1b7f1e196664f 100644 --- a/compiler/rustc_target/src/spec/base/linux_ohos.rs +++ b/compiler/rustc_target/src/spec/base/linux_ohos.rs @@ -1,12 +1,11 @@ use crate::spec::{TargetOptions, TlsModel, base}; pub(crate) fn opts() -> TargetOptions { - let mut base = base::linux::opts(); - - base.env = "ohos".into(); - base.crt_static_default = false; - base.tls_model = TlsModel::Emulated; - base.has_thread_local = false; - - base + TargetOptions { + env: "ohos".into(), + crt_static_default: false, + tls_model: TlsModel::Emulated, + has_thread_local: false, + ..base::linux::opts() + } } diff --git a/compiler/rustc_target/src/spec/base/linux_wasm.rs b/compiler/rustc_target/src/spec/base/linux_wasm.rs new file mode 100644 index 0000000000000..a8c137c22a97c --- /dev/null +++ b/compiler/rustc_target/src/spec/base/linux_wasm.rs @@ -0,0 +1,159 @@ +//! This target is a confluence of Linux and Wasm models, inheriting most +//! aspects from their respective base targets + +use crate::spec::{ + Cc, LinkSelfContainedDefault, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel, + add_link_args, crt_objects, cvs, +}; + +pub(crate) fn opts() -> TargetOptions { + macro_rules! args { + ($prefix:literal) => { + &[ + // 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. + concat!($prefix, "-z"), + concat!($prefix, "stack-size=1048576"), + // By default LLD's memory layout is: + // + // 1. First, a blank page + // 2. Next, all static data + // 3. Finally, the main stack (which grows down) + // + // This has the unfortunate consequence that on stack overflows you + // corrupt static data and can cause some exceedingly weird bugs. To + // help detect this a little sooner we instead request that the stack is + // placed before static data. + // + // This means that we'll generate slightly larger binaries as references + // to static data will take more bytes in the ULEB128 encoding, but + // stack overflow will be guaranteed to trap as it underflows instead of + // corrupting static data. + concat!($prefix, "--stack-first"), + // FIXME we probably shouldn't pass this but instead pass an explicit list + // of symbols we'll allow to be undefined. We don't currently have a + // mechanism of knowing, however, which symbols are intended to be imported + // from the environment and which are intended to be imported from other + // objects linked elsewhere. This is a coarse approximation but is sure to + // hide some bugs and frustrate someone at some point, so we should ideally + // work towards a world where we can explicitly list symbols that are + // supposed to be imported and have all other symbols generate errors if + // they remain undefined. + concat!($prefix, "--allow-undefined"), + // LLD only implements C++-like demangling, which doesn't match our own + // mangling scheme. Tell LLD to not demangle anything and leave it up to + // us to demangle these symbols later. Currently rustc does not perform + // further demangling, but tools like twiggy and wasm-bindgen are intended + // to do so. + concat!($prefix, "--no-demangle"), + ] + }; + } + + let mut pre_link_args = TargetOptions::link_args(LinkerFlavor::WasmLld(Cc::No), args!("")); + add_link_args(&mut pre_link_args, LinkerFlavor::WasmLld(Cc::Yes), args!("-Wl,")); + + TargetOptions { + is_like_wasm: true, + families: cvs!["wasm", "unix"], + os: "linux".into(), + env: "musl".into(), + + // we allow dynamic linking, but only cdylibs. Basically we allow a + // final library artifact that exports some symbols (a wasm module) but + // we don't allow intermediate `dylib` crate types + dynamic_linking: true, + only_cdylib: true, + + // relatively self-explanatory! + exe_suffix: ".wasm".into(), + dll_prefix: "".into(), + dll_suffix: ".wasm".into(), + eh_frame_header: false, + + max_atomic_width: Some(64), + + // Unwinding doesn't work right now, so the whole target unconditionally + // defaults to panic=abort. Note that this is guaranteed to change in + // the future once unwinding is implemented. Don't rely on this as we're + // basically guaranteed to change it once WebAssembly supports + // exceptions. + panic_strategy: PanicStrategy::Abort, + + // Symbol visibility takes care of this for the WebAssembly. + // Additionally the only known linker, LLD, doesn't support the script + // arguments just yet + limit_rdylib_exports: false, + + // we use the LLD shipped with the Rust toolchain by default + linker: Some("rust-lld".into()), + linker_flavor: LinkerFlavor::WasmLld(Cc::No), + + pre_link_args, + + // FIXME: Figure out cases in which WASM needs to link with a native toolchain. + // + // rust-lang/rust#104137: cannot blindly remove this without putting in + // some other way to compensate for lack of `-nostartfiles` in linker + // invocation. + link_self_contained: LinkSelfContainedDefault::True, + pre_link_objects_self_contained: crt_objects::pre_wasi_self_contained(), + post_link_objects_self_contained: crt_objects::post_wasi_self_contained(), + + // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when + // PIC code is implemented this has quite a drastic effect if it stays + // at the default, `pic`. In an effort to keep wasm binaries as minimal + // as possible we're defaulting to `static` for now, but the hope is + // that eventually we can ship a `pic`-compatible standard library which + // works with `static` as well (or works with some method of generating + // non-relative calls and such later on). + relocation_model: RelocModel::Static, + + // When the atomics feature is activated then these two keys matter, + // otherwise they're basically ignored by the standard library. In this + // mode, however, the `#[thread_local]` attribute works (i.e. + // `has_thread_local`) and we need to get it to work by specifying + // `local-exec` as that's all that's implemented in LLVM today for wasm. + has_thread_local: true, + tls_model: TlsModel::LocalExec, + + // Supporting Linux requires multithreading supported by Wasm's thread + // proposal + singlethread: false, + + // gdb scripts don't work on wasm blobs + emit_debug_gdb_scripts: false, + + // There's more discussion of this at + // https://bugs.llvm.org/show_bug.cgi?id=52442 but the general result is + // that this isn't useful for wasm and has tricky issues with + // representation, so this is disabled. + generate_arange_section: false, + + // Right now this is a bit of a workaround but we're currently saying that + // the target by default has a static crt which we're taking as a signal + // for "use the bundled crt". If that's turned off then the system's crt + // will be used, but this means that default usage of this target doesn't + // need an external compiler but it's still interoperable with an external + // compiler if configured correctly. + crt_static_default: true, + crt_static_respected: true, + + // Allow `+crt-static` to create a "cdylib" output which is just a wasm file + // without a main function. + crt_static_allows_dylibs: true, + + // Wasm start ignores arguments -- relies on API call from interface. + main_needs_argc_argv: false, + + // Wasm toolchains mangle the name of "main" to distinguish between different + // signatures. + entry_name: "__main_void".into(), + + // Wasm Feature flags for supporting Linux + features: "+atomics,+bulk-memory,+mutable-globals,+sign-ext".into(), + + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/base/lynxos178.rs b/compiler/rustc_target/src/spec/base/lynxos178.rs new file mode 100644 index 0000000000000..b9434ff5faaf6 --- /dev/null +++ b/compiler/rustc_target/src/spec/base/lynxos178.rs @@ -0,0 +1,31 @@ +use std::borrow::Cow; + +use crate::spec::{ + PanicStrategy, RelocModel, RelroLevel, SplitDebuginfo, StackProbeType, TargetOptions, cvs, +}; + +pub(crate) fn opts() -> TargetOptions { + TargetOptions { + os: "lynxos178".into(), + dynamic_linking: false, + families: cvs!["unix"], + position_independent_executables: false, + static_position_independent_executables: false, + relro_level: RelroLevel::Full, + has_thread_local: false, + crt_static_respected: true, + panic_strategy: PanicStrategy::Abort, + linker: Some(Cow::Borrowed("x86_64-lynx-lynxos178-gcc")), + no_default_libraries: false, + eh_frame_header: false, // GNU ld (GNU Binutils) 2.37.50 does not support --eh-frame-hdr + max_atomic_width: Some(64), + supported_split_debuginfo: Cow::Borrowed(&[ + SplitDebuginfo::Packed, + SplitDebuginfo::Unpacked, + SplitDebuginfo::Off, + ]), + relocation_model: RelocModel::Static, + stack_probes: StackProbeType::Inline, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/base/mod.rs b/compiler/rustc_target/src/spec/base/mod.rs index 6f88be5d37f16..b368d93f00726 100644 --- a/compiler/rustc_target/src/spec/base/mod.rs +++ b/compiler/rustc_target/src/spec/base/mod.rs @@ -1,6 +1,6 @@ pub(crate) mod aix; pub(crate) mod android; -pub(crate) mod apple; +pub mod apple; pub(crate) mod avr; pub(crate) mod bpf; pub(crate) mod cygwin; @@ -18,6 +18,8 @@ pub(crate) mod linux_gnu; pub(crate) mod linux_musl; pub(crate) mod linux_ohos; pub(crate) mod linux_uclibc; +pub(crate) mod linux_wasm; +pub(crate) mod lynxos178; pub(crate) mod msvc; pub(crate) mod netbsd; pub(crate) mod nto_qnx; diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 134405f3630e7..be71da76b4a35 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -103,6 +103,12 @@ impl Target { base.$key_name = Some(s); } } ); + ($key_name:ident, Option>) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.remove(&name).and_then(|b| Some(b.as_str()?.to_string())) { + base.$key_name = Some(s.into()); + } + } ); ($key_name:ident, BinaryFormat) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.remove(&name).and_then(|f| f.as_str().and_then(|s| { @@ -592,7 +598,7 @@ impl Target { key!(families, target_families); key!(abi_return_struct_as_int, bool); key!(is_like_aix, bool); - key!(is_like_osx, bool); + key!(is_like_darwin, bool); key!(is_like_solaris, bool); key!(is_like_windows, bool); key!(is_like_msvc, bool); @@ -623,6 +629,7 @@ impl Target { key!(stack_probes, StackProbeType)?; key!(min_global_align, Option); key!(default_codegen_units, Option); + key!(default_codegen_backend, Option>); key!(trap_unreachable, bool); key!(requires_lto, bool); key!(singlethread, bool); @@ -770,7 +777,7 @@ impl ToJson for Target { target_option_val!(families, "target-family"); target_option_val!(abi_return_struct_as_int); target_option_val!(is_like_aix); - target_option_val!(is_like_osx); + target_option_val!(is_like_darwin); target_option_val!(is_like_solaris); target_option_val!(is_like_windows); target_option_val!(is_like_msvc); @@ -801,6 +808,7 @@ impl ToJson for Target { target_option_val!(stack_probes); target_option_val!(min_global_align); target_option_val!(default_codegen_units); + target_option_val!(default_codegen_backend); target_option_val!(trap_unreachable); target_option_val!(requires_lto); target_option_val!(singlethread); diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index cc8162a2f27ea..303be54a6d786 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -42,8 +42,10 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{fmt, io}; -use rustc_abi::{Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors}; -use rustc_data_structures::fx::FxHashSet; +use rustc_abi::{ + Align, Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors, +}; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_fs_util::try_canonicalize; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -60,6 +62,7 @@ pub mod crt_objects; mod base; mod json; +pub use base::apple; pub use base::avr::ef_avr_arch; /// Linker is called through a C/C++ compiler. @@ -81,7 +84,7 @@ pub enum Lld { /// of classes that we call "linker flavors". /// /// Technically, it's not even necessary, we can nearly always infer the flavor from linker name -/// and target properties like `is_like_windows`/`is_like_osx`/etc. However, the PRs originally +/// and target properties like `is_like_windows`/`is_like_darwin`/etc. However, the PRs originally /// introducing `-Clinker-flavor` (#40018 and friends) were aiming to reduce this kind of inference /// and provide something certain and explicitly specified instead, and that design goal is still /// relevant now. @@ -1915,7 +1918,6 @@ supported_targets! { ("i686-pc-windows-msvc", i686_pc_windows_msvc), ("i686-uwp-windows-msvc", i686_uwp_windows_msvc), ("i686-win7-windows-msvc", i686_win7_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), @@ -1925,6 +1927,7 @@ supported_targets! { ("wasm32-wasip1", wasm32_wasip1), ("wasm32-wasip2", wasm32_wasip2), ("wasm32-wasip1-threads", wasm32_wasip1_threads), + ("wasm32-wali-linux-musl", wasm32_wali_linux_musl), ("wasm64-unknown-unknown", wasm64_unknown_unknown), ("thumbv6m-none-eabi", thumbv6m_none_eabi), @@ -2077,6 +2080,7 @@ supported_targets! { ("riscv32imafc-unknown-nuttx-elf", riscv32imafc_unknown_nuttx_elf), ("riscv64imac-unknown-nuttx-elf", riscv64imac_unknown_nuttx_elf), ("riscv64gc-unknown-nuttx-elf", riscv64gc_unknown_nuttx_elf), + ("x86_64-lynx-lynxos178", x86_64_lynx_lynxos178), ("x86_64-pc-cygwin", x86_64_pc_cygwin), } @@ -2234,7 +2238,10 @@ pub enum WasmCAbi { /// Spec-compliant C ABI. Spec, /// Legacy ABI. Which is non-spec-compliant. - Legacy, + Legacy { + /// Indicates whether the `wasm_c_abi` lint should be emitted. + with_lint: bool, + }, } pub trait HasWasmCAbiOpt { @@ -2403,7 +2410,7 @@ pub struct TargetOptions { /// in particular running dsymutil and some other stuff like `-dead_strip`. Defaults to false. /// Also indicates whether to use Apple-specific ABI changes, such as extending function /// parameters to 32-bits. - pub is_like_osx: bool, + pub is_like_darwin: 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. @@ -2697,7 +2704,7 @@ fn add_link_args(link_args: &mut LinkArgs, flavor: LinkerFlavor, args: &[&'stati impl TargetOptions { pub fn supports_comdat(&self) -> bool { // XCOFF and MachO don't support COMDAT. - !self.is_like_aix && !self.is_like_osx + !self.is_like_aix && !self.is_like_darwin } } @@ -2801,7 +2808,7 @@ impl Default for TargetOptions { families: cvs![], abi_return_struct_as_int: false, is_like_aix: false, - is_like_osx: false, + is_like_darwin: false, is_like_solaris: false, is_like_windows: false, is_like_msvc: false, @@ -2958,14 +2965,9 @@ impl Target { pub fn is_abi_supported(&self, abi: ExternAbi) -> bool { use ExternAbi::*; match abi { - Rust - | C { .. } - | System { .. } - | RustIntrinsic - | RustCall - | Unadjusted - | Cdecl { .. } - | RustCold => true, + Rust | C { .. } | System { .. } | RustCall | Unadjusted | Cdecl { .. } | RustCold => { + true + } EfiApi => { ["arm", "aarch64", "riscv32", "riscv64", "x86", "x86_64"].contains(&&self.arch[..]) } @@ -3067,9 +3069,9 @@ impl Target { } check_eq!( - self.is_like_osx, + self.is_like_darwin, self.vendor == "apple", - "`is_like_osx` must be set if and only if `vendor` is `apple`" + "`is_like_darwin` must be set if and only if `vendor` is `apple`" ); check_eq!( self.is_like_solaris, @@ -3095,9 +3097,9 @@ impl Target { // Check that default linker flavor is compatible with some other key properties. check_eq!( - self.is_like_osx, + self.is_like_darwin, matches!(self.linker_flavor, LinkerFlavor::Darwin(..)), - "`linker_flavor` must be `darwin` if and only if `is_like_osx` is set" + "`linker_flavor` must be `darwin` if and only if `is_like_darwin` is set" ); check_eq!( self.is_like_msvc, @@ -3342,7 +3344,10 @@ impl Target { ); } "arm" => { - check!(self.llvm_floatabi.is_some(), "ARM targets must specify their float ABI",) + check!( + self.llvm_floatabi.is_some(), + "ARM targets must set `llvm-floatabi` to `hard` or `soft`", + ) } _ => {} } @@ -3503,7 +3508,15 @@ impl Target { return load_file(&p); } - Err(format!("Could not find specification for target {target_tuple:?}")) + // Leave in a specialized error message for the removed target. + // FIXME: If you see this and it's been a few months after this has been released, + // you can probably remove it. + if target_tuple == "i586-pc-windows-msvc" { + Err("the `i586-pc-windows-msvc` target has been removed. Use the `i686-pc-windows-msvc` target instead.\n\ + Windows 10 (the minimum required OS version) requires a CPU baseline of at least i686 so you can safely switch".into()) + } else { + Err(format!("could not find specification for target {target_tuple:?}")) + } } TargetTuple::TargetJson { ref contents, .. } => { let obj = serde_json::from_str(contents).map_err(|e| e.to_string())?; @@ -3535,6 +3548,90 @@ impl Target { s => s.clone(), } } + + pub fn object_architecture( + &self, + unstable_target_features: &FxIndexSet, + ) -> Option<(object::Architecture, Option)> { + use object::Architecture; + Some(match self.arch.as_ref() { + "arm" => (Architecture::Arm, None), + "aarch64" => ( + if self.pointer_width == 32 { + Architecture::Aarch64_Ilp32 + } else { + Architecture::Aarch64 + }, + None, + ), + "x86" => (Architecture::I386, None), + "s390x" => (Architecture::S390x, None), + "mips" | "mips32r6" => (Architecture::Mips, None), + "mips64" | "mips64r6" => ( + // While there are currently no builtin targets + // using the N32 ABI, it is possible to specify + // it using a custom target specification. N32 + // is an ILP32 ABI like the Aarch64_Ilp32 + // and X86_64_X32 cases above and below this one. + if self.options.llvm_abiname.as_ref() == "n32" { + Architecture::Mips64_N32 + } else { + Architecture::Mips64 + }, + None, + ), + "x86_64" => ( + if self.pointer_width == 32 { + Architecture::X86_64_X32 + } else { + Architecture::X86_64 + }, + None, + ), + "powerpc" => (Architecture::PowerPc, None), + "powerpc64" => (Architecture::PowerPc64, None), + "riscv32" => (Architecture::Riscv32, None), + "riscv64" => (Architecture::Riscv64, None), + "sparc" => { + if unstable_target_features.contains(&sym::v8plus) { + // Target uses V8+, aka EM_SPARC32PLUS, aka 64-bit V9 but in 32-bit mode + (Architecture::Sparc32Plus, None) + } else { + // Target uses V7 or V8, aka EM_SPARC + (Architecture::Sparc, None) + } + } + "sparc64" => (Architecture::Sparc64, None), + "avr" => (Architecture::Avr, None), + "msp430" => (Architecture::Msp430, None), + "hexagon" => (Architecture::Hexagon, None), + "bpf" => (Architecture::Bpf, None), + "loongarch64" => (Architecture::LoongArch64, None), + "csky" => (Architecture::Csky, None), + "arm64ec" => (Architecture::Aarch64, Some(object::SubArchitecture::Arm64EC)), + // Unsupported architecture. + _ => return None, + }) + } + + /// Returns whether this target is known to have unreliable alignment: + /// native C code for the target fails to align some data to the degree + /// required by the C standard. We can't *really* do anything about that + /// since unsafe Rust code may assume alignment any time, but we can at least + /// inhibit some optimizations, and we suppress the alignment checks that + /// would detect this unsoundness. + /// + /// Every target that returns less than `Align::MAX` here is still has a soundness bug. + pub fn max_reliable_alignment(&self) -> Align { + // FIXME(#112480) MSVC on x86-32 is unsound and fails to properly align many types with + // more-than-4-byte-alignment on the stack. This makes alignments larger than 4 generally + // unreliable on 32bit Windows. + if self.is_like_windows && self.arch == "x86" { + Align::from_bytes(4).unwrap() + } else { + Align::MAX + } + } } /// Either a target tuple string or a path to a JSON file. diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs index 98d78520c9838..f1b6fa123deb4 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs @@ -1,10 +1,16 @@ -use crate::spec::{Target, TargetMetadata, base}; +use crate::spec::{FramePointer, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut base = base::windows_msvc::opts(); base.max_atomic_width = Some(128); base.features = "+v8a,+neon,+fp-armv8".into(); + // Microsoft recommends enabling frame pointers on Arm64 Windows. + // From https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170#integer-registers + // "The frame pointer (x29) is required for compatibility with fast stack walking used by ETW + // and other services. It must point to the previous {x29, x30} pair on the stack." + base.frame_pointer = FramePointer::NonLeaf; + Target { llvm_target: "aarch64-pc-windows-msvc".into(), metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs index 23ed92e62b8e9..8366b6d9bd824 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs @@ -1,6 +1,28 @@ -use crate::spec::{SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{ + Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target, TargetMetadata, base, +}; pub(crate) fn target() -> Target { + let mut base = base::fuchsia::opts(); + base.cpu = "generic".into(); + base.features = "+v8a,+crc,+aes,+sha2,+neon".into(); + base.max_atomic_width = Some(128); + base.stack_probes = StackProbeType::Inline; + base.supported_sanitizers = SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::LEAK + | SanitizerSet::SHADOWCALLSTACK; + base.supports_xray = true; + + base.add_pre_link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &[ + "--execute-only", + // Enable the Cortex-A53 errata 843419 mitigation by default + "--fix-cortex-a53-843419", + ], + ); + Target { llvm_target: "aarch64-unknown-fuchsia".into(), metadata: TargetMetadata { @@ -12,14 +34,6 @@ pub(crate) fn target() -> Target { pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: "aarch64".into(), - options: TargetOptions { - features: "+v8a".into(), - max_atomic_width: Some(128), - stack_probes: StackProbeType::Inline, - supported_sanitizers: SanitizerSet::ADDRESS - | SanitizerSet::CFI - | SanitizerSet::SHADOWCALLSTACK, - ..base::fuchsia::opts() - }, + options: base, } } diff --git a/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs index bb3e3e544cba4..8f93523909e37 100644 --- a/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Lld, Target, TargetMetadata, add_link_args, base}; +use crate::spec::{FramePointer, LinkerFlavor, Lld, Target, TargetMetadata, add_link_args, base}; pub(crate) fn target() -> Target { let mut base = base::windows_msvc::opts(); @@ -10,6 +10,12 @@ pub(crate) fn target() -> Target { &["/machine:arm64ec", "softintrin.lib"], ); + // Microsoft recommends enabling frame pointers on Arm64 Windows. + // From https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170#integer-registers + // "The frame pointer (x29) is required for compatibility with fast stack walking used by ETW + // and other services. It must point to the previous {x29, x30} pair on the stack." + base.frame_pointer = FramePointer::NonLeaf; + Target { llvm_target: "arm64ec-pc-windows-msvc".into(), metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_gnueabi.rs index 52e786de3ed98..7b93672dbe0fa 100644 --- a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_gnueabi.rs @@ -20,6 +20,7 @@ pub(crate) fn target() -> Target { max_atomic_width: Some(32), mcount: "\u{1}__gnu_mcount_nc".into(), has_thumb_interworking: true, + llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()), ..base::linux_gnu::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs index dbe1540364ad8..860629b08e19b 100644 --- a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs @@ -2,7 +2,7 @@ use crate::spec::{FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { - llvm_target: "armv5te-unknown-linux-uclibcgnueabi".into(), + llvm_target: "armv5te-unknown-linux-gnueabi".into(), metadata: TargetMetadata { description: Some("Armv5TE Linux with uClibc".into()), tier: Some(3), diff --git a/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs index 5d292bbf8adf0..6a83835059eee 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { ); Target { - llvm_target: "thumbv7a-vita-eabihf".into(), + llvm_target: "thumbv7a-sony-vita-eabihf".into(), metadata: TargetMetadata { description: Some( "Armv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain)".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabihf.rs index 3b5a337b4f139..a3b35d658e9da 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabihf.rs @@ -22,6 +22,7 @@ pub(crate) fn target() -> Target { features: "+v7,+vfp3,-d32,+thumb2,-neon".into(), max_atomic_width: Some(64), mcount: "\u{1}__gnu_mcount_nc".into(), + llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()), ..base::linux_gnu::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/i586_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i586_pc_windows_msvc.rs deleted file mode 100644 index 394e6f9e6bf65..0000000000000 --- a/compiler/rustc_target/src/spec/targets/i586_pc_windows_msvc.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::spec::Target; - -pub(crate) fn target() -> Target { - let mut base = super::i686_pc_windows_msvc::target(); - base.rustc_abi = None; // overwrite the SSE2 ABI set by the base target - base.cpu = "pentium".into(); - base.llvm_target = "i586-pc-windows-msvc".into(); - base -} diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs index 2a26323e5147b..e775c8fc524c4 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs @@ -20,7 +20,7 @@ pub(crate) fn target() -> Target { llvm_target: "i686-pc-windows-gnu".into(), metadata: TargetMetadata { description: Some("32-bit MinGW (Windows 10+)".into()), - tier: Some(1), + tier: Some(2), host_tools: Some(true), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs index 233a1c4fd7a54..91ab311109787 100644 --- a/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs @@ -7,6 +7,12 @@ pub(crate) fn target() -> Target { base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.supported_sanitizers = SanitizerSet::ADDRESS; + // On Windows 7 32-bit, the alignment characteristic of the TLS Directory + // don't appear to be respected by the PE Loader, leading to crashes. As + // a result, let's disable has_thread_local to make sure TLS goes through + // the emulation layer. + // See https://github.com/rust-lang/rust/issues/138903 + base.has_thread_local = false; base.add_pre_link_args( LinkerFlavor::Msvc(Lld::No), diff --git a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs index 1300280e35b0f..508abc0101841 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs @@ -23,9 +23,11 @@ pub(crate) fn target() -> Target { data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), arch: "mips64".into(), options: TargetOptions { + vendor: "openwrt".into(), abi: "abi64".into(), endian: Endian::Big, mcount: "_mcount".into(), + llvm_abiname: "n64".into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_gnuabi64.rs index b130ca29c7f03..a26350ff22509 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_gnuabi64.rs @@ -22,6 +22,7 @@ pub(crate) fn target() -> Target { features: "+mips64r2,+xgot".into(), max_atomic_width: Some(64), mcount: "_mcount".into(), + llvm_abiname: "n64".into(), ..base::linux_gnu::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs index 4ea7c7bff44a6..fd50950305300 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs @@ -25,6 +25,7 @@ pub(crate) fn target() -> Target { mcount: "_mcount".into(), // FIXME(compiler-team#422): musl targets should be dynamically linked by default. crt_static_default: true, + llvm_abiname: "n64".into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_gnuabi64.rs index a9afea27ef340..19bceadc62232 100644 --- a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_gnuabi64.rs @@ -19,6 +19,7 @@ pub(crate) fn target() -> Target { features: "+mips64r2,+xgot".into(), max_atomic_width: Some(64), mcount: "_mcount".into(), + llvm_abiname: "n64".into(), ..base::linux_gnu::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs index 7bdd9edda70cd..aa087b1a35af8 100644 --- a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs @@ -19,6 +19,11 @@ pub(crate) fn target() -> Target { pointer_width: 64, data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), arch: "mips64".into(), - options: TargetOptions { abi: "abi64".into(), mcount: "_mcount".into(), ..base }, + options: TargetOptions { + abi: "abi64".into(), + mcount: "_mcount".into(), + llvm_abiname: "n64".into(), + ..base + }, } } diff --git a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs index 0955b3debea00..c14bfbf46d21d 100644 --- a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs +++ b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs @@ -4,7 +4,7 @@ use crate::spec::{Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { - llvm_target: "mips-unknown-linux-uclibc".into(), + llvm_target: "mips-unknown-linux-gnu".into(), metadata: TargetMetadata { description: Some("MIPS Linux with uClibc".into()), tier: Some(3), diff --git a/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_uclibc.rs index 08c4347fe0145..f0056799d66f6 100644 --- a/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_uclibc.rs +++ b/compiler/rustc_target/src/spec/targets/mipsel_unknown_linux_uclibc.rs @@ -2,7 +2,7 @@ use crate::spec::{Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { - llvm_target: "mipsel-unknown-linux-uclibc".into(), + llvm_target: "mipsel-unknown-linux-gnu".into(), metadata: TargetMetadata { description: Some("MIPS (LE) Linux with uClibc".into()), tier: Some(3), diff --git a/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs index 3eefa27ea04b6..cdd5f6b84365a 100644 --- a/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs @@ -22,6 +22,7 @@ pub(crate) fn target() -> Target { features: "+mips64r6".into(), max_atomic_width: Some(64), mcount: "_mcount".into(), + llvm_abiname: "n64".into(), ..base::linux_gnu::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/mipsisa64r6el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/targets/mipsisa64r6el_unknown_linux_gnuabi64.rs index 0887180791c71..88879a25818b7 100644 --- a/compiler/rustc_target/src/spec/targets/mipsisa64r6el_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mipsisa64r6el_unknown_linux_gnuabi64.rs @@ -19,6 +19,7 @@ pub(crate) fn target() -> Target { features: "+mips64r6".into(), max_atomic_width: Some(64), mcount: "_mcount".into(), + llvm_abiname: "n64".into(), ..base::linux_gnu::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs index 55dc6a70627c9..efc17d8d083b7 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs @@ -2,7 +2,7 @@ use crate::spec::{StackProbeType, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { - llvm_target: "riscv32".into(), + llvm_target: "riscv32-unknown-linux-gnu".into(), metadata: TargetMetadata { description: None, tier: Some(3), @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { cpu: "generic-rv32".into(), llvm_abiname: "ilp32d".into(), max_atomic_width: Some(32), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(), stack_probes: StackProbeType::Inline, ..base::vxworks::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs index 6dda346aaaf56..5b7feef70d099 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { code_model: Some(CodeModel::Medium), cpu: "generic-rv32".into(), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(), llvm_abiname: "ilp32d".into(), max_atomic_width: Some(32), supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs index ba10e3c688184..938b39b10c64e 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { code_model: Some(CodeModel::Medium), cpu: "generic-rv32".into(), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(), llvm_abiname: "ilp32d".into(), max_atomic_width: Some(32), supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), diff --git a/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs b/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs index c8ef737b9e73b..b9176c939f805 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { code_model: Some(CodeModel::Medium), cpu: "generic-rv64".into(), - features: "+m,+a,+f,+d,+c,+zba,+zbb,+zbs,+v".into(), + features: "+m,+a,+f,+d,+c,+b,+v,+zicsr,+zifencei".into(), llvm_abiname: "lp64d".into(), supported_sanitizers: SanitizerSet::ADDRESS, max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs index 58ded24b9c599..8d8c21952de5f 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs @@ -2,7 +2,7 @@ use crate::spec::{StackProbeType, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { - llvm_target: "riscv64".into(), + llvm_target: "riscv64-unknown-linux-gnu".into(), metadata: TargetMetadata { description: None, tier: Some(3), @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { cpu: "generic-rv64".into(), llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(), stack_probes: StackProbeType::Inline, ..base::vxworks::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs index ecf6567753111..e628095b88a6d 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_freebsd.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { code_model: Some(CodeModel::Medium), cpu: "generic-rv64".into(), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(), llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), ..base::freebsd::opts() diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs index d673936f5f85d..c4466e13d1439 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs @@ -1,6 +1,16 @@ -use crate::spec::{CodeModel, SanitizerSet, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{CodeModel, SanitizerSet, StackProbeType, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { + let mut base = base::fuchsia::opts(); + base.code_model = Some(CodeModel::Medium); + base.cpu = "generic-rv64".into(); + base.features = "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(); + base.llvm_abiname = "lp64d".into(); + base.max_atomic_width = Some(64); + base.stack_probes = StackProbeType::Inline; + base.supported_sanitizers = SanitizerSet::SHADOWCALLSTACK; + base.supports_xray = true; + Target { llvm_target: "riscv64-unknown-fuchsia".into(), metadata: TargetMetadata { @@ -12,14 +22,6 @@ pub(crate) fn target() -> Target { pointer_width: 64, data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), arch: "riscv64".into(), - options: TargetOptions { - code_model: Some(CodeModel::Medium), - cpu: "generic-rv64".into(), - features: "+m,+a,+f,+d,+c".into(), - llvm_abiname: "lp64d".into(), - max_atomic_width: Some(64), - supported_sanitizers: SanitizerSet::SHADOWCALLSTACK, - ..base::fuchsia::opts() - }, + options: base, } } diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs index 88b5dca284ae9..5c15bdd9f6454 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_hermit.rs @@ -14,7 +14,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), options: TargetOptions { cpu: "generic-rv64".into(), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(), relocation_model: RelocModel::Pic, code_model: Some(CodeModel::Medium), tls_model: TlsModel::LocalExec, diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs index 8ffb622511db0..af2f42fa00a2f 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { code_model: Some(CodeModel::Medium), cpu: "generic-rv64".into(), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(), llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs index 33b08fdcb0510..70c19952af063 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { code_model: Some(CodeModel::Medium), cpu: "generic-rv64".into(), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(), llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs index 2b647e36f18a8..1f359d1e7fe6d 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_netbsd.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { code_model: Some(CodeModel::Medium), cpu: "generic-rv64".into(), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(), llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), mcount: "__mcount".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs index d6f0a5499b99c..5a5aad93efba1 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_none_elf.rs @@ -22,7 +22,7 @@ pub(crate) fn target() -> Target { llvm_abiname: "lp64d".into(), cpu: "generic-rv64".into(), max_atomic_width: Some(64), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, code_model: Some(CodeModel::Medium), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs index bc6829897a423..e8abc926dd0ed 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs @@ -24,7 +24,7 @@ pub(crate) fn target() -> Target { llvm_abiname: "lp64d".into(), cpu: "generic-rv64".into(), max_atomic_width: Some(64), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, code_model: Some(CodeModel::Medium), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs index 75f508d8e933b..85d7dfe7865ed 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_openbsd.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { code_model: Some(CodeModel::Medium), cpu: "generic-rv64".into(), - features: "+m,+a,+f,+d,+c".into(), + features: "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(), llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), ..base::openbsd::opts() diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs b/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs new file mode 100644 index 0000000000000..a0eb4a254fc0e --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/wasm32_wali_linux_musl.rs @@ -0,0 +1,29 @@ +//! The `wasm32-wali-linux-musl` target is a wasm32 target compliant with the +//! [WebAssembly Linux Interface](https://github.com/arjunr2/WALI). + +use crate::spec::{Cc, LinkerFlavor, Target, TargetMetadata, base}; + +pub(crate) fn target() -> Target { + let mut options = base::linux_wasm::opts(); + + options + .add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), &["--export-memory", "--shared-memory"]); + options.add_pre_link_args( + LinkerFlavor::WasmLld(Cc::Yes), + &["--target=wasm32-wasi-threads", "-Wl,--export-memory,", "-Wl,--shared-memory"], + ); + + Target { + llvm_target: "wasm32-wasi".into(), + metadata: TargetMetadata { + description: Some("WebAssembly Linux Interface with musl-libc".into()), + tier: Some(3), + host_tools: Some(false), + std: None, + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20".into(), + arch: "wasm32".into(), + options, + } +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_lynx_lynxos178.rs b/compiler/rustc_target/src/spec/targets/x86_64_lynx_lynxos178.rs new file mode 100644 index 0000000000000..654ae7c9c5bea --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_lynx_lynxos178.rs @@ -0,0 +1,34 @@ +use crate::spec::{SanitizerSet, StackProbeType, Target, base}; + +pub(crate) fn target() -> Target { + let mut base = base::lynxos178::opts(); + base.cpu = "x86-64".into(); + base.plt_by_default = false; + base.max_atomic_width = Some(64); + base.stack_probes = StackProbeType::Inline; + base.static_position_independent_executables = false; + base.supported_sanitizers = SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::KCFI + | SanitizerSet::DATAFLOW + | SanitizerSet::LEAK + | SanitizerSet::MEMORY + | SanitizerSet::SAFESTACK + | SanitizerSet::THREAD; + base.supports_xray = true; + + Target { + llvm_target: "x86_64-unknown-unknown-gnu".into(), + metadata: crate::spec::TargetMetadata { + description: Some("LynxOS-178".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), + arch: "x86_64".into(), + options: base, + } +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_cygwin.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_cygwin.rs index 8da4fe6b8b152..eac4caf41c8bc 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_cygwin.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_cygwin.rs @@ -18,7 +18,7 @@ pub(crate) fn target() -> Target { description: Some("64-bit x86 Cygwin".into()), tier: Some(3), host_tools: Some(false), - std: None, + std: Some(true), }, } } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs index d41c696ac23bf..d7253da7e8d66 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs @@ -4,7 +4,10 @@ pub(crate) fn target() -> Target { let mut base = base::fuchsia::opts(); base.cpu = "x86-64".into(); base.plt_by_default = false; - base.max_atomic_width = Some(64); + // See https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0073_x86_64_platform_requirement, + // which corresponds to x86-64-v2. + base.features = "+cx16,+sahf,+popcnt,+sse3,+sse4.1,+sse4.2,+ssse3".into(); + base.max_atomic_width = Some(128); base.stack_probes = StackProbeType::Inline; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK; base.supports_xray = true; diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs index a034a9fb24453..4107249dcf1e3 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { base.panic_strategy = PanicStrategy::Abort; Target { - llvm_target: "x86_64-unknown-l4re-uclibc".into(), + llvm_target: "x86_64-unknown-l4re-gnu".into(), metadata: TargetMetadata { description: None, tier: Some(3), diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs index dd98f34d32399..cffdaa9072738 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs @@ -20,7 +20,7 @@ pub(crate) fn target() -> Target { vendor: "espressif".into(), executables: true, - cpu: "esp32-s2".into(), + cpu: "esp32s2".into(), linker: Some("xtensa-esp32s2-elf-gcc".into()), // See https://github.com/espressif/rust-esp32-example/issues/3#issuecomment-861054477 diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs index 29bcf12cbaf6c..7bf5834ab0aa0 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { vendor: "espressif".into(), - cpu: "esp32-s2".into(), + cpu: "esp32s2".into(), linker: Some("xtensa-esp32s2-elf-gcc".into()), max_atomic_width: Some(32), features: "+forced-atomics".into(), diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs index dd6e7b6c3e8c1..2e4afc005414d 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs @@ -20,7 +20,7 @@ pub(crate) fn target() -> Target { vendor: "espressif".into(), executables: true, - cpu: "esp32-s3".into(), + cpu: "esp32s3".into(), linker: Some("xtensa-esp32s3-elf-gcc".into()), // The esp32s3 only supports native 32bit atomics. diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_none_elf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_none_elf.rs index ddc909f387e9e..d506888d8ee04 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_none_elf.rs @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { vendor: "espressif".into(), - cpu: "esp32-s3".into(), + cpu: "esp32s3".into(), linker: Some("xtensa-esp32s3-elf-gcc".into()), max_atomic_width: Some(32), atomic_cas: true, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index d05466bb48431..99b04ac272009 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -102,6 +102,9 @@ impl Stability { // check whether they're named already elsewhere in rust // e.g. in stdarch and whether the given name matches LLVM's // if it doesn't, to_llvm_feature in llvm_util in rustc_codegen_llvm needs to be adapted. +// Additionally, if the feature is not available in older version of LLVM supported by the current +// rust, the same function must be updated to filter out these features to avoid triggering +// warnings. // // Also note that all target features listed here must be purely additive: for target_feature 1.1 to // be sound, we can never allow features like `+soft-float` (on x86) to be controlled on a @@ -380,32 +383,58 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("adx", Stable, &[]), ("aes", Stable, &["sse2"]), + ("amx-avx512", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-fp8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-movrs", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-tf32", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), + ("amx-transpose", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("apxf", Unstable(sym::apx_target_feature), &[]), ("avx", Stable, &["sse4.2"]), + ( + "avx10.1", + Unstable(sym::avx10_target_feature), + &[ + "avx512bf16", + "avx512bitalg", + "avx512bw", + "avx512cd", + "avx512dq", + "avx512f", + "avx512fp16", + "avx512ifma", + "avx512vbmi", + "avx512vbmi2", + "avx512vl", + "avx512vnni", + "avx512vpopcntdq", + ], + ), + ("avx10.2", Unstable(sym::avx10_target_feature), &["avx10.1"]), ("avx2", Stable, &["avx"]), - ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), - ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), - ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]), - ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avx512bf16", Stable, &["avx512bw"]), + ("avx512bitalg", Stable, &["avx512bw"]), + ("avx512bw", Stable, &["avx512f"]), + ("avx512cd", Stable, &["avx512f"]), + ("avx512dq", Stable, &["avx512f"]), + ("avx512f", Stable, &["avx2", "fma", "f16c"]), + ("avx512fp16", Stable, &["avx512bw"]), + ("avx512ifma", Stable, &["avx512f"]), + ("avx512vbmi", Stable, &["avx512bw"]), + ("avx512vbmi2", Stable, &["avx512bw"]), + ("avx512vl", Stable, &["avx512f"]), + ("avx512vnni", Stable, &["avx512f"]), + ("avx512vp2intersect", Stable, &["avx512f"]), + ("avx512vpopcntdq", Stable, &["avx512f"]), + ("avxifma", Stable, &["avx2"]), + ("avxneconvert", Stable, &["avx2"]), + ("avxvnni", Stable, &["avx2"]), + ("avxvnniint16", Stable, &["avx2"]), + ("avxvnniint8", Stable, &["avx2"]), ("bmi1", Stable, &[]), ("bmi2", Stable, &[]), ("cmpxchg16b", Stable, &[]), @@ -413,11 +442,12 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("f16c", Stable, &["avx"]), ("fma", Stable, &["avx"]), ("fxsr", Stable, &[]), - ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), + ("gfni", Stable, &["sse2"]), ("kl", Unstable(sym::keylocker_x86), &["sse2"]), ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), ("lzcnt", Stable, &[]), ("movbe", Stable, &[]), + ("movrs", Unstable(sym::movrs_target_feature), &[]), ("pclmulqdq", Stable, &["sse2"]), ("popcnt", Stable, &[]), ("prfchw", Unstable(sym::prfchw_target_feature), &[]), @@ -439,8 +469,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]), ("ssse3", Stable, &["sse3"]), ("tbm", Unstable(sym::tbm_target_feature), &[]), - ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]), - ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), + ("vaes", Stable, &["avx2", "aes"]), + ("vpclmulqdq", Stable, &["avx", "pclmulqdq"]), ("widekl", Unstable(sym::keylocker_x86), &["kl"]), ("x87", Unstable(sym::x87_target_feature), &[]), ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), @@ -485,10 +515,11 @@ const MIPS_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("a", Stable, &["zaamo", "zalrsc"]), - ("c", Stable, &[]), + ("b", Unstable(sym::riscv_target_feature), &["zba", "zbb", "zbs"]), + ("c", Stable, &["zca"]), ("d", Unstable(sym::riscv_target_feature), &["f"]), ("e", Unstable(sym::riscv_target_feature), &[]), - ("f", Unstable(sym::riscv_target_feature), &[]), + ("f", Unstable(sym::riscv_target_feature), &["zicsr"]), ( "forced-atomics", Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, @@ -497,9 +528,10 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("m", Stable, &[]), ("relax", Unstable(sym::riscv_target_feature), &[]), ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]), - ("v", Unstable(sym::riscv_target_feature), &[]), + ("unaligned-vector-mem", Unstable(sym::riscv_target_feature), &[]), + ("v", Unstable(sym::riscv_target_feature), &["zvl128b", "zve64d"]), ("za128rs", Unstable(sym::riscv_target_feature), &[]), - ("za64rs", Unstable(sym::riscv_target_feature), &[]), + ("za64rs", Unstable(sym::riscv_target_feature), &["za128rs"]), // Za64rs ⊃ Za128rs ("zaamo", Unstable(sym::riscv_target_feature), &[]), ("zabha", Unstable(sym::riscv_target_feature), &["zaamo"]), ("zacas", Unstable(sym::riscv_target_feature), &["zaamo"]), @@ -508,17 +540,38 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("zawrs", Unstable(sym::riscv_target_feature), &[]), ("zba", Stable, &[]), ("zbb", Stable, &[]), - ("zbc", Stable, &[]), + ("zbc", Stable, &["zbkc"]), // Zbc ⊃ Zbkc ("zbkb", Stable, &[]), ("zbkc", Stable, &[]), ("zbkx", Stable, &[]), ("zbs", Stable, &[]), + ("zca", Unstable(sym::riscv_target_feature), &[]), + ("zcb", Unstable(sym::riscv_target_feature), &["zca"]), + ("zcmop", Unstable(sym::riscv_target_feature), &["zca"]), ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zfa", Unstable(sym::riscv_target_feature), &["f"]), + ("zfbfmin", Unstable(sym::riscv_target_feature), &["f"]), // and a subset of Zfhmin ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]), ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]), - ("zfinx", Unstable(sym::riscv_target_feature), &[]), + ("zfinx", Unstable(sym::riscv_target_feature), &["zicsr"]), ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]), ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zic64b", Unstable(sym::riscv_target_feature), &[]), + ("zicbom", Unstable(sym::riscv_target_feature), &[]), + ("zicbop", Unstable(sym::riscv_target_feature), &[]), + ("zicboz", Unstable(sym::riscv_target_feature), &[]), + ("ziccamoa", Unstable(sym::riscv_target_feature), &[]), + ("ziccif", Unstable(sym::riscv_target_feature), &[]), + ("zicclsm", Unstable(sym::riscv_target_feature), &[]), + ("ziccrse", Unstable(sym::riscv_target_feature), &[]), + ("zicntr", Unstable(sym::riscv_target_feature), &["zicsr"]), + ("zicond", Unstable(sym::riscv_target_feature), &[]), + ("zicsr", Unstable(sym::riscv_target_feature), &[]), + ("zifencei", Unstable(sym::riscv_target_feature), &[]), + ("zihintntl", Unstable(sym::riscv_target_feature), &[]), + ("zihintpause", Unstable(sym::riscv_target_feature), &[]), + ("zihpm", Unstable(sym::riscv_target_feature), &["zicsr"]), + ("zimop", Unstable(sym::riscv_target_feature), &[]), ("zk", Stable, &["zkn", "zkr", "zkt"]), ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), ("zknd", Stable, &[]), @@ -529,6 +582,44 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("zksed", Stable, &[]), ("zksh", Stable, &[]), ("zkt", Stable, &[]), + ("ztso", Unstable(sym::riscv_target_feature), &[]), + ("zvbb", Unstable(sym::riscv_target_feature), &["zvkb"]), // Zvbb ⊃ Zvkb + ("zvbc", Unstable(sym::riscv_target_feature), &["zve64x"]), + ("zve32f", Unstable(sym::riscv_target_feature), &["zve32x", "f"]), + ("zve32x", Unstable(sym::riscv_target_feature), &["zvl32b", "zicsr"]), + ("zve64d", Unstable(sym::riscv_target_feature), &["zve64f", "d"]), + ("zve64f", Unstable(sym::riscv_target_feature), &["zve32f", "zve64x"]), + ("zve64x", Unstable(sym::riscv_target_feature), &["zve32x", "zvl64b"]), + ("zvfbfmin", Unstable(sym::riscv_target_feature), &["zve32f"]), + ("zvfbfwma", Unstable(sym::riscv_target_feature), &["zfbfmin", "zvfbfmin"]), + ("zvfh", Unstable(sym::riscv_target_feature), &["zvfhmin", "zve32f", "zfhmin"]), // Zvfh ⊃ Zvfhmin + ("zvfhmin", Unstable(sym::riscv_target_feature), &["zve32f"]), + ("zvkb", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvkg", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvkn", Unstable(sym::riscv_target_feature), &["zvkned", "zvknhb", "zvkb", "zvkt"]), + ("zvknc", Unstable(sym::riscv_target_feature), &["zvkn", "zvbc"]), + ("zvkned", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvkng", Unstable(sym::riscv_target_feature), &["zvkn", "zvkg"]), + ("zvknha", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvknhb", Unstable(sym::riscv_target_feature), &["zvknha", "zve64x"]), // Zvknhb ⊃ Zvknha + ("zvks", Unstable(sym::riscv_target_feature), &["zvksed", "zvksh", "zvkb", "zvkt"]), + ("zvksc", Unstable(sym::riscv_target_feature), &["zvks", "zvbc"]), + ("zvksed", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvksg", Unstable(sym::riscv_target_feature), &["zvks", "zvkg"]), + ("zvksh", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvkt", Unstable(sym::riscv_target_feature), &[]), + ("zvl1024b", Unstable(sym::riscv_target_feature), &["zvl512b"]), + ("zvl128b", Unstable(sym::riscv_target_feature), &["zvl64b"]), + ("zvl16384b", Unstable(sym::riscv_target_feature), &["zvl8192b"]), + ("zvl2048b", Unstable(sym::riscv_target_feature), &["zvl1024b"]), + ("zvl256b", Unstable(sym::riscv_target_feature), &["zvl128b"]), + ("zvl32768b", Unstable(sym::riscv_target_feature), &["zvl16384b"]), + ("zvl32b", Unstable(sym::riscv_target_feature), &[]), + ("zvl4096b", Unstable(sym::riscv_target_feature), &["zvl2048b"]), + ("zvl512b", Unstable(sym::riscv_target_feature), &["zvl256b"]), + ("zvl64b", Unstable(sym::riscv_target_feature), &["zvl32b"]), + ("zvl65536b", Unstable(sym::riscv_target_feature), &["zvl32768b"]), + ("zvl8192b", Unstable(sym::riscv_target_feature), &["zvl4096b"]), // tidy-alphabetical-end ]; @@ -602,14 +693,19 @@ static CSKY_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ static LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("d", Unstable(sym::loongarch_target_feature), &["f"]), - ("f", Unstable(sym::loongarch_target_feature), &[]), - ("frecipe", Unstable(sym::loongarch_target_feature), &[]), - ("lasx", Unstable(sym::loongarch_target_feature), &["lsx"]), - ("lbt", Unstable(sym::loongarch_target_feature), &[]), - ("lsx", Unstable(sym::loongarch_target_feature), &["d"]), - ("lvz", Unstable(sym::loongarch_target_feature), &[]), + ("d", Stable, &["f"]), + ("div32", Unstable(sym::loongarch_target_feature), &[]), + ("f", Stable, &[]), + ("frecipe", Stable, &[]), + ("lam-bh", Unstable(sym::loongarch_target_feature), &[]), + ("lamcas", Unstable(sym::loongarch_target_feature), &[]), + ("lasx", Stable, &["lsx"]), + ("lbt", Stable, &[]), + ("ld-seq-sa", Unstable(sym::loongarch_target_feature), &[]), + ("lsx", Stable, &["d"]), + ("lvz", Stable, &[]), ("relax", Unstable(sym::loongarch_target_feature), &[]), + ("scq", Unstable(sym::loongarch_target_feature), &[]), ("ual", Unstable(sym::loongarch_target_feature), &[]), // tidy-alphabetical-end ]; @@ -699,9 +795,21 @@ const ARM_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(1 const POWERPC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "altivec")]; const WASM_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "simd128")]; const S390X_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "vector")]; -const RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = - &[/*(64, "zvl64b"), */ (128, "v")]; -// Always warn on SPARC, as the necessary target features cannot be enabled in Rust at the moment. +const RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[ + (32, "zvl32b"), + (64, "zvl64b"), + (128, "zvl128b"), + (256, "zvl256b"), + (512, "zvl512b"), + (1024, "zvl1024b"), + (2048, "zvl2048b"), + (4096, "zvl4096b"), + (8192, "zvl8192b"), + (16384, "zvl16384b"), + (32768, "zvl32768b"), + (65536, "zvl65536b"), +]; +// Always error on SPARC, as the necessary target features cannot be enabled in Rust at the moment. const SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[/*(64, "vis")*/]; const HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = @@ -768,17 +876,15 @@ impl Target { } } - pub fn implied_target_features<'a>( - &self, - base_features: impl Iterator, - ) -> FxHashSet<&'a str> { + // Note: the returned set includes `base_feature`. + pub fn implied_target_features<'a>(&self, base_feature: &'a str) -> FxHashSet<&'a str> { let implied_features = self.rust_target_features().iter().map(|(f, _, i)| (f, i)).collect::>(); - // implied target features have their own implied target features, so we traverse the - // map until there are no more features to add + // Implied target features have their own implied target features, so we traverse the + // map until there are no more features to add. let mut features = FxHashSet::default(); - let mut new_features = base_features.collect::>(); + let mut new_features = vec![base_feature]; while let Some(new_feature) = new_features.pop() { if features.insert(new_feature) { if let Some(implied_features) = implied_features.get(&new_feature) { @@ -891,12 +997,12 @@ impl Target { // about what the intended ABI is. match &*self.llvm_abiname { "ilp32d" | "lp64d" => { - // Requires d (which implies f), incompatible with e. - FeatureConstraints { required: &["d"], incompatible: &["e"] } + // Requires d (which implies f), incompatible with e and zfinx. + FeatureConstraints { required: &["d"], incompatible: &["e", "zfinx"] } } "ilp32f" | "lp64f" => { - // Requires f, incompatible with e. - FeatureConstraints { required: &["f"], incompatible: &["e"] } + // Requires f, incompatible with e and zfinx. + FeatureConstraints { required: &["f"], incompatible: &["e", "zfinx"] } } "ilp32" | "lp64" => { // Requires nothing, incompatible with e. @@ -920,6 +1026,28 @@ impl Target { _ => unreachable!(), } } + "loongarch64" => { + // LoongArch handles ABI in a very sane way, being fully explicit via `llvm_abiname` + // about what the intended ABI is. + match &*self.llvm_abiname { + "ilp32d" | "lp64d" => { + // Requires d (which implies f), incompatible with nothing. + FeatureConstraints { required: &["d"], incompatible: &[] } + } + "ilp32f" | "lp64f" => { + // Requires f, incompatible with nothing. + FeatureConstraints { required: &["f"], incompatible: &[] } + } + "ilp32s" | "lp64s" => { + // The soft-float ABI does not require any features and is also not + // incompatible with any features. Rust targets explicitly specify the + // LLVM ABI names, which allows for enabling hard-float support even on + // soft-float targets, and ensures that the ABI behavior is as expected. + NOTHING + } + _ => unreachable!(), + } + } _ => NOTHING, } } diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 1c61e23362a83..1071105522d11 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -8,7 +8,6 @@ edition = "2024" itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } @@ -21,7 +20,6 @@ rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] } -rustc_type_ir = { path = "../rustc_type_ir" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2" tracing = "0.1" diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 4db9d9915b139..00922c6038eee 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -148,9 +148,6 @@ trait_selection_dtcs_has_req_note = the used `impl` has a `'static` requirement trait_selection_dtcs_introduces_requirement = calling this method introduces the `impl`'s `'static` requirement trait_selection_dtcs_suggestion = consider relaxing the implicit `'static` requirement -trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]` - .label = empty on-clause here - trait_selection_explicit_lifetime_required_sugg_with_ident = add explicit lifetime `{$named}` to the type of `{$simple_ident}` trait_selection_explicit_lifetime_required_sugg_with_param_type = add explicit lifetime `{$named}` to type @@ -187,9 +184,6 @@ trait_selection_inherent_projection_normalization_overflow = overflow evaluating trait_selection_invalid_format_specifier = invalid format specifier .help = no format specifier are supported in this position -trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]` - .label = invalid on-clause here - trait_selection_label_bad = {$bad_kind -> *[other] cannot infer type [more_info] cannot infer {$prefix_kind -> @@ -237,10 +231,6 @@ trait_selection_negative_positive_conflict = found both positive and negative im .positive_implementation_here = positive implementation here .positive_implementation_in_crate = positive implementation in crate `{$positive_impl_cname}` -trait_selection_no_value_in_rustc_on_unimplemented = this attribute must have a valid value - .label = expected value here - .note = eg `#[rustc_on_unimplemented(message="foo")]` - trait_selection_nothing = {""} trait_selection_oc_cant_coerce_force_inline = @@ -264,8 +254,15 @@ trait_selection_oc_no_diverge = `else` clause of `let...else` does not diverge trait_selection_oc_no_else = `if` may be missing an `else` clause trait_selection_oc_try_compat = `?` operator has incompatible types trait_selection_oc_type_compat = type not compatible with trait + trait_selection_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds .label = opaque type defined here +trait_selection_opaque_type_non_generic_param = + expected generic {$kind} parameter, found `{$arg}` + .label = {STREQ($arg, "'static") -> + [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type + *[other] this generic parameter must be used with a generic {$kind} parameter + } trait_selection_outlives_bound = lifetime of the source pointer does not outlive lifetime bound of the object type trait_selection_outlives_content = lifetime of reference outlives lifetime of borrowed content... @@ -332,6 +329,22 @@ trait_selection_ril_introduced_by = requirement introduced by this return type trait_selection_ril_introduced_here = `'static` requirement introduced here trait_selection_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type +trait_selection_rustc_on_unimplemented_empty_on_clause = empty `on`-clause in `#[rustc_on_unimplemented]` + .label = empty `on`-clause here +trait_selection_rustc_on_unimplemented_expected_identifier = expected an identifier inside this `on`-clause + .label = expected an identifier here, not `{$path}` +trait_selection_rustc_on_unimplemented_expected_one_predicate_in_not = expected a single predicate in `not(..)` + .label = unexpected quantity of predicates here +trait_selection_rustc_on_unimplemented_invalid_flag = invalid flag in `on`-clause + .label = expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}` +trait_selection_rustc_on_unimplemented_invalid_predicate = this predicate is invalid + .label = expected one of `any`, `all` or `not` here, not `{$invalid_pred}` +trait_selection_rustc_on_unimplemented_missing_value = this attribute must have a value + .label = expected value here + .note = e.g. `#[rustc_on_unimplemented(message="foo")]` +trait_selection_rustc_on_unimplemented_unsupported_literal_in_on = literals inside `on`-clauses are not supported + .label = unexpected literal here + trait_selection_source_kind_closure_return = try giving this closure an explicit return type @@ -439,8 +452,6 @@ trait_selection_type_annotations_needed = {$source_kind -> } .label = type must be known at this point -trait_selection_type_annotations_needed_error_time = this is an inference error on crate `time` caused by an API change in Rust 1.80.0; update `time` to version `>=0.3.35` by calling `cargo update` - trait_selection_types_declared_different = these two types are declared with different lifetimes... trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated} diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 847bd06bb017a..fdd547448f004 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -65,7 +65,9 @@ use rustc_middle::bug; use rustc_middle::dep_graph::DepContext; use rustc_middle::traits::PatternOriginExpr; use rustc_middle::ty::error::{ExpectedFound, TypeError, TypeErrorToStringExt}; -use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, with_forced_trimmed_paths}; +use rustc_middle::ty::print::{ + PrintError, PrintTraitRefExt as _, WrapBinderMode, with_forced_trimmed_paths, +}; use rustc_middle::ty::{ self, List, ParamEnv, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, @@ -507,7 +509,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { hir::MatchSource::TryDesugar(scrut_hir_id), ) => { if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found { - let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id); + let scrut_expr = self.tcx.hir_expect_expr(scrut_hir_id); let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind { let arg_expr = args.first().expect("try desugaring call w/out arg"); self.typeck_results @@ -546,7 +548,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) => match source { hir::MatchSource::TryDesugar(scrut_hir_id) => { if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found { - let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id); + let scrut_expr = self.tcx.hir_expect_expr(scrut_hir_id); let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind { let arg_expr = args.first().expect("try desugaring call w/out arg"); self.typeck_results @@ -835,7 +837,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let get_lifetimes = |sig| { use rustc_hir::def::Namespace; let (sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS) - .name_all_regions(sig) + .name_all_regions(sig, WrapBinderMode::ForAll) .unwrap(); let lts: Vec = reg.into_items().map(|(_, kind)| kind.to_string()).into_sorted_stable_ord(); @@ -1464,7 +1466,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - impl<'tcx> ty::visit::TypeVisitor> for OpaqueTypesVisitor<'tcx> { + impl<'tcx> ty::TypeVisitor> for OpaqueTypesVisitor<'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) { if let Some((kind, def_id)) = TyCategory::from_ty(self.tcx, t) { let span = self.tcx.def_span(def_id); @@ -2024,7 +2026,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } LetVisitor { span }.visit_body(body).break_value() } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. }) => { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(_, ty, _, _), .. }) => { Some(&ty.peel_refs().kind) } _ => None, @@ -2332,13 +2334,13 @@ impl<'tcx> ObligationCause<'tcx> { subdiags: Vec, ) -> ObligationCauseFailureCode { match self.code() { - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Fn, .. } => { + ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Fn { .. }, .. } => { ObligationCauseFailureCode::MethodCompat { span, subdiags } } - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Type, .. } => { + ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Type { .. }, .. } => { ObligationCauseFailureCode::TypeCompat { span, subdiags } } - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Const, .. } => { + ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Const { .. }, .. } => { ObligationCauseFailureCode::ConstCompat { span, subdiags } } ObligationCauseCode::BlockTailExpression(.., hir::MatchSource::TryDesugar(_)) => { @@ -2396,13 +2398,13 @@ impl<'tcx> ObligationCause<'tcx> { fn as_requirement_str(&self) -> &'static str { match self.code() { - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Fn, .. } => { + ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Fn { .. }, .. } => { "method type is compatible with trait" } - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Type, .. } => { + ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Type { .. }, .. } => { "associated type is compatible with trait" } - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Const, .. } => { + ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Const { .. }, .. } => { "const is compatible with trait" } ObligationCauseCode::MainFunctionType => "`main` function has the correct type", @@ -2420,9 +2422,13 @@ pub struct ObligationCauseAsDiagArg<'tcx>(pub ObligationCause<'tcx>); impl IntoDiagArg for ObligationCauseAsDiagArg<'_> { fn into_diag_arg(self, _: &mut Option) -> rustc_errors::DiagArgValue { let kind = match self.0.code() { - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Fn, .. } => "method_compat", - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Type, .. } => "type_compat", - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Const, .. } => { + ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Fn { .. }, .. } => { + "method_compat" + } + ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Type { .. }, .. } => { + "type_compat" + } + ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Const { .. }, .. } => { "const_compat" } ObligationCauseCode::MainFunctionType => "fn_main_correct_type", diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index a6d8eb6add7d0..cb1c9c7536903 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -6,7 +6,7 @@ use rustc_errors::codes::*; use rustc_errors::{Diag, IntoDiagArg}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource}; use rustc_middle::bug; @@ -14,11 +14,10 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; use rustc_middle::ty::{ - self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Ty, TyCtxt, - TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults, + self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Term, TermKind, + Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, TypeckResults, }; -use rustc_span::{BytePos, DUMMY_SP, FileName, Ident, Span, sym}; -use rustc_type_ir::visit::TypeVisitableExt; +use rustc_span::{BytePos, DUMMY_SP, Ident, Span, sym}; use tracing::{debug, instrument, warn}; use super::nice_region_error::placeholder_error::Highlighted; @@ -344,12 +343,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// which were stuck during inference. pub fn extract_inference_diagnostics_data( &self, - arg: GenericArg<'tcx>, + term: Term<'tcx>, highlight: ty::print::RegionHighlightMode<'tcx>, ) -> InferenceDiagnosticsData { let tcx = self.tcx; - match arg.unpack() { - GenericArgKind::Type(ty) => { + match term.unpack() { + TermKind::Ty(ty) => { if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() { let var_origin = self.infcx.type_var_origin(ty_vid); if let Some(def_id) = var_origin.param_def_id @@ -375,7 +374,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { parent: None, } } - GenericArgKind::Const(ct) => { + TermKind::Const(ct) => { if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() { let origin = self.const_var_origin(vid).expect("expected unresolved const var"); if let Some(def_id) = origin.param_def_id { @@ -411,7 +410,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } } - GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), } } @@ -440,7 +438,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { bad_label, was_written: false, path: Default::default(), - time_version: false, }), TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl { span, @@ -472,13 +469,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, body_def_id: LocalDefId, failure_span: Span, - arg: GenericArg<'tcx>, + term: Term<'tcx>, error_code: TypeAnnotationNeeded, should_label_span: bool, ) -> Diag<'a> { - let arg = self.resolve_vars_if_possible(arg); - let arg_data = - self.extract_inference_diagnostics_data(arg, ty::print::RegionHighlightMode::default()); + let term = self.resolve_vars_if_possible(term); + let arg_data = self + .extract_inference_diagnostics_data(term, ty::print::RegionHighlightMode::default()); let Some(typeck_results) = &self.typeck_results else { // If we don't have any typeck results we're outside @@ -487,7 +484,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return self.bad_inference_failure_err(failure_span, arg_data, error_code); }; - let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, arg); + let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term); if let Some(body) = self.tcx.hir_maybe_body_owned_by( self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(), ) { @@ -542,7 +539,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { have_turbofish, } => { let generics = self.tcx.generics_of(generics_def_id); - let is_type = matches!(arg.unpack(), GenericArgKind::Type(_)); + let is_type = term.as_type().is_some(); let (parent_exists, parent_prefix, parent_name) = InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id) @@ -632,10 +629,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } } - - let time_version = - self.detect_old_time_crate_version(failure_span, &kind, &mut infer_subdiags); - match error_code { TypeAnnotationNeeded::E0282 => self.dcx().create_err(AnnotationRequired { span, @@ -647,7 +640,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { bad_label: None, was_written: path.is_some(), path: path.unwrap_or_default(), - time_version, }), TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl { span, @@ -673,42 +665,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }), } } - - /// Detect the inference regression on crate `time` <= 0.3.35 and emit a more targeted error. - /// - // FIXME: we should figure out a more generic version of doing this, ideally in cargo itself. - fn detect_old_time_crate_version( - &self, - span: Option, - kind: &InferSourceKind<'_>, - // We will clear the non-actionable suggestion from the error to reduce noise. - infer_subdiags: &mut Vec>, - ) -> bool { - // FIXME(#129461): We are time-boxing this code in the compiler. It'll start failing - // compilation once we promote 1.89 to beta, which will happen in 9 months from now. - #[cfg(not(version("1.89")))] - const fn version_check() {} - #[cfg(version("1.89"))] - const fn version_check() { - panic!("remove this check as presumably the ecosystem has moved from needing it"); - } - const { version_check() }; - // Only relevant when building the `time` crate. - if self.infcx.tcx.crate_name(LOCAL_CRATE) == sym::time - && let Some(span) = span - && let InferSourceKind::LetBinding { pattern_name, .. } = kind - && let Some(name) = pattern_name - && name.as_str() == "items" - && let FileName::Real(file) = self.infcx.tcx.sess.source_map().span_to_filename(span) - { - let path = file.local_path_if_available().to_string_lossy(); - if path.contains("format_description") && path.contains("parse") { - infer_subdiags.clear(); - return true; - } - } - false - } } #[derive(Debug)] @@ -811,7 +767,7 @@ struct FindInferSourceVisitor<'a, 'tcx> { tecx: &'a TypeErrCtxt<'a, 'tcx>, typeck_results: &'a TypeckResults<'tcx>, - target: GenericArg<'tcx>, + target: Term<'tcx>, attempt: usize, infer_source_cost: usize, @@ -822,7 +778,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { fn new( tecx: &'a TypeErrCtxt<'a, 'tcx>, typeck_results: &'a TypeckResults<'tcx>, - target: GenericArg<'tcx>, + target: Term<'tcx>, ) -> Self { FindInferSourceVisitor { tecx, @@ -938,12 +894,12 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // Check whether this generic argument is the inference variable we // are looking for. fn generic_arg_is_target(&self, arg: GenericArg<'tcx>) -> bool { - if arg == self.target { + if arg == self.target.into() { return true; } match (arg.unpack(), self.target.unpack()) { - (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => { + (GenericArgKind::Type(inner_ty), TermKind::Ty(target_ty)) => { use ty::{Infer, TyVar}; match (inner_ty.kind(), target_ty.kind()) { (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => { @@ -952,7 +908,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { _ => false, } } - (GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => { + (GenericArgKind::Const(inner_ct), TermKind::Const(target_ct)) => { use ty::InferConst::*; match (inner_ct.kind(), target_ct.kind()) { (ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => { @@ -1051,7 +1007,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { None? } let args = self.node_args_opt(expr.hir_id)?; - let span = tcx.hir().span(segment.hir_id); + let span = tcx.hir_span(segment.hir_id); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); InsertableGenericArgs { insert_span, @@ -1110,7 +1066,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { if generics.has_impl_trait() { return None; } - let span = tcx.hir().span(segment.hir_id); + let span = tcx.hir_span(segment.hir_id); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); Some(InsertableGenericArgs { insert_span, @@ -1144,7 +1100,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { if !segment.infer_args || generics.has_impl_trait() { do yeet (); } - let span = tcx.hir().span(segment.hir_id); + let span = tcx.hir_span(segment.hir_id); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); InsertableGenericArgs { insert_span, @@ -1351,7 +1307,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { && !has_impl_trait(def_id) // FIXME(fn_delegation): In delegation item argument spans are equal to last path // segment. This leads to ICE's when emitting `multipart_suggestion`. - && tcx.hir().opt_delegation_sig_id(expr.hir_id.owner.def_id).is_none() + && tcx.hir_opt_delegation_sig_id(expr.hir_id.owner.def_id).is_none() { let successor = method_args.get(0).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo())); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs index 5a303c3cd03c7..139b299713688 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -77,7 +77,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { match arg.kind { hir::TyKind::BareFn(_) => { self.current_index.shift_in(1); - intravisit::walk_ty(self, arg); + let _ = intravisit::walk_ty(self, arg); self.current_index.shift_out(1); return ControlFlow::Continue(()); } @@ -85,7 +85,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { hir::TyKind::TraitObject(bounds, ..) => { for bound in bounds { self.current_index.shift_in(1); - self.visit_poly_trait_ref(bound); + let _ = self.visit_poly_trait_ref(bound); self.current_index.shift_out(1); } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 083ce022238a0..eaa06d8e8b0ae 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -6,7 +6,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty}; use rustc_hir::{ self as hir, AmbigArg, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime, - LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind, + LifetimeKind, LifetimeParamKind, MissingLifetimeKind, Node, TyKind, }; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_span::def_id::LocalDefId; @@ -165,7 +165,7 @@ pub fn suggest_new_region_bound( if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg { GenericBound::Outlives(Lifetime { - res: LifetimeName::Static, ident, .. + kind: LifetimeKind::Static, ident, .. }) => Some(ident.span), _ => None, }) { @@ -253,7 +253,7 @@ pub fn suggest_new_region_bound( } } TyKind::TraitObject(_, lt) => { - if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res { + if let LifetimeKind::ImplicitObjectLifetimeDefault = lt.kind { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), format!("{declare} the trait object {captures}, {explicit}",), @@ -365,7 +365,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // obligation comes from the `impl`. Find that `impl` so that we can point // at it in the suggestion. let trait_did = trait_id.to_def_id(); - tcx.hir_trait_impls(trait_did).iter().find_map(|&impl_did| { + tcx.local_trait_impls(trait_did).iter().find_map(|&impl_did| { if let Node::Item(Item { kind: ItemKind::Impl(hir::Impl { self_ty, .. }), .. }) = tcx.hir_node_by_def_id(impl_did) @@ -414,7 +414,7 @@ pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec, pub DefId); impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) { if let TyKind::TraitObject(poly_trait_refs, lifetime_ptr) = t.kind - && let Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. } = + && let Lifetime { kind: LifetimeKind::ImplicitObjectLifetimeDefault, .. } = lifetime_ptr.pointer() { for ptr in poly_trait_refs { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs index cc2ab1c343289..b66bd2c6ab787 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs @@ -71,7 +71,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } } - impl<'tcx> ty::visit::TypeVisitor> for HighlightBuilder<'tcx> { + impl<'tcx> ty::TypeVisitor> for HighlightBuilder<'tcx> { fn visit_region(&mut self, r: ty::Region<'tcx>) { if !r.has_name() && self.counter <= 3 { self.highlight.highlighting_region(r, self.counter); @@ -98,7 +98,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let assoc_item = self.tcx().associated_item(trait_item_def_id); let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] }; match assoc_item.kind { - ty::AssocKind::Fn => { + ty::AssocKind::Fn { .. } => { if let Some(hir_id) = assoc_item.def_id.as_local().map(|id| self.tcx().local_def_id_to_hir_id(id)) { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs index 245764c94abf0..4a71ab4e06a35 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs @@ -3,8 +3,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::fold::fold_regions; -use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable, fold_regions}; use rustc_span::Span; use tracing::instrument; @@ -43,7 +42,7 @@ pub fn find_param_with_region<'tcx>( anon_region: Region<'tcx>, replace_region: Region<'tcx>, ) -> Option> { - let (id, kind) = match *anon_region { + let (id, kind) = match anon_region.kind() { ty::ReLateParam(late_param) => (late_param.scope, late_param.kind), ty::ReEarlyParam(ebr) => { let region_def = tcx.generics_of(generic_param_scope).region_param(ebr, tcx).def_id; @@ -52,7 +51,6 @@ pub fn find_param_with_region<'tcx>( _ => return None, // not a free region }; - let hir = &tcx.hir(); let def_id = id.as_local()?; // FIXME: use def_kind @@ -94,7 +92,7 @@ pub fn find_param_with_region<'tcx>( }); found_anon_region.then(|| { let ty_hir_id = fn_decl.inputs[index].hir_id; - let param_ty_span = hir.span(ty_hir_id); + let param_ty_span = tcx.hir_span(ty_hir_id); let is_first = index == 0; AnonymousParamInfo { param, param_ty: new_param_ty, param_ty_span, kind, is_first } }) @@ -160,6 +158,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { && self .tcx() .opt_associated_item(scope_def_id.to_def_id()) - .is_some_and(|i| i.fn_has_self_parameter) + .is_some_and(|i| i.is_method()) } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 514615735a507..be508c8cee13e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -395,11 +395,28 @@ impl Trait for X { let sp = tcx .def_ident_span(body_owner_def_id) .unwrap_or_else(|| tcx.def_span(body_owner_def_id)); - diag.span_note( - sp, - "this item must have the opaque type in its signature in order to \ - be able to register hidden types", - ); + let mut alias_def_id = opaque_ty.def_id; + while let DefKind::OpaqueTy = tcx.def_kind(alias_def_id) { + alias_def_id = tcx.parent(alias_def_id); + } + let opaque_path = tcx.def_path_str(alias_def_id); + // FIXME(type_alias_impl_trait): make this a structured suggestion + match tcx.opaque_ty_origin(opaque_ty.def_id) { + rustc_hir::OpaqueTyOrigin::FnReturn { .. } => {} + rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {} + rustc_hir::OpaqueTyOrigin::TyAlias { + in_assoc_ty: false, .. + } => { + diag.span_note( + sp, + format!("this item must have a `#[define_opaque({opaque_path})]` \ + attribute to be able to define hidden types"), + ); + } + rustc_hir::OpaqueTyOrigin::TyAlias { + in_assoc_ty: true, .. + } => {} + } } // If two if arms can be coerced to a trait object, provide a structured // suggestion. @@ -726,7 +743,7 @@ fn foo(&self) -> Self::T { String::new() } if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() { let opaque_local_def_id = def_id.as_local(); let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id { - tcx.hir().expect_opaque_ty(opaque_local_def_id) + tcx.hir_expect_opaque_ty(opaque_local_def_id) } else { return false; }; @@ -765,8 +782,8 @@ fn foo(&self) -> Self::T { String::new() } let methods: Vec<(Span, String)> = items .in_definition_order() .filter(|item| { - ty::AssocKind::Fn == item.kind - && Some(item.name) != current_method_ident + item.is_fn() + && Some(item.name()) != current_method_ident && !tcx.is_doc_hidden(item.def_id) }) .filter_map(|item| { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index fecb38ab597e4..b8207c4f81632 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -11,9 +11,10 @@ use rustc_hir::{self as hir, ParamName}; use rustc_middle::bug; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::{self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _}; +use rustc_middle::ty::{ + self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _, Upcast as _, +}; use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol, kw}; -use rustc_type_ir::Upcast as _; use tracing::{debug, instrument}; use super::ObligationCauseAsDiagArg; @@ -299,7 +300,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.tcx.param_env(generic_param_scope), terr, ); - match (*sub, *sup) { + match (sub.kind(), sup.kind()) { (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} (ty::RePlaceholder(_), _) => { note_and_explain_region( @@ -391,7 +392,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) } infer::RelateParamBound(span, ty, opt_span) => { - let prefix = match *sub { + let prefix = match sub.kind() { ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy, _ => note_and_explain::PrefixKind::TypeOutlive, }; @@ -707,7 +708,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::Projection | ty::Inherent => { format!("the associated type `{p}`") } - ty::Weak => format!("the type alias `{p}`"), + ty::Free => format!("the type alias `{p}`"), ty::Opaque => format!("the opaque type `{p}`"), }, }; @@ -850,14 +851,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { add_lt_suggs: &mut Vec<(Span, String)>, ) -> String { struct LifetimeReplaceVisitor<'a> { - needle: hir::LifetimeName, + needle: hir::LifetimeKind, new_lt: &'a str, add_lt_suggs: &'a mut Vec<(Span, String)>, } impl<'hir> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'_> { fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) { - if lt.res == self.needle { + if lt.kind == self.needle { self.add_lt_suggs.push(lt.suggestion(self.new_lt)); } } @@ -894,7 +895,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; let mut visitor = LifetimeReplaceVisitor { - needle: hir::LifetimeName::Param(lifetime_def_id), + needle: hir::LifetimeKind::Param(lifetime_def_id), add_lt_suggs, new_lt: &new_lt, }; @@ -967,7 +968,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { format!("...so that the {}", sup_trace.cause.as_requirement_str()), ); - err.note_expected_found(&"", sup_expected, &"", sup_found); + err.note_expected_found("", sup_expected, "", sup_found); return if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { @@ -1017,13 +1018,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { infer::BoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!( " for lifetime parameter {}in trait containing associated type `{}`", br_string(br), - self.tcx.associated_item(def_id).name + self.tcx.associated_item(def_id).name() ), infer::RegionParameterDefinition(_, name) => { format!(" for lifetime parameter `{name}`") } infer::UpvarRegion(ref upvar_id, _) => { - let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); + let var_name = self.tcx.hir_name(upvar_id.var_path.hir_id); format!(" for capture of `{var_name}` by closure") } infer::Nll(..) => bug!("NLL variable found in lexical phase"), @@ -1048,7 +1049,7 @@ pub(super) fn note_and_explain_region<'tcx>( suffix: &str, alt_span: Option, ) { - let (description, span) = match *region { + let (description, span) = match region.kind() { ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => { msg_span_from_named_region(tcx, generic_param_scope, region, alt_span) } @@ -1085,7 +1086,7 @@ fn msg_span_from_named_region<'tcx>( region: ty::Region<'tcx>, alt_span: Option, ) -> (String, Option) { - match *region { + match region.kind() { ty::ReEarlyParam(br) => { let param_def_id = tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id; let span = tcx.def_span(param_def_id); @@ -1185,7 +1186,7 @@ pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>( }); // Explain the region we are capturing. - match *hidden_region { + match hidden_region.kind() { ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { // Assuming regionck succeeded (*), we ought to always be // capturing *some* region from the fn header, and hence it diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index a7e68e6419d84..cdbb92f4c7baa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -778,8 +778,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let exp_local_id = exp_def_id.as_local()?; match ( - &self.tcx.hir().expect_opaque_ty(last_local_id), - &self.tcx.hir().expect_opaque_ty(exp_local_id), + &self.tcx.hir_expect_opaque_ty(last_local_id), + &self.tcx.hir_expect_opaque_ty(exp_local_id), ) { ( hir::OpaqueTy { bounds: last_bounds, .. }, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index f15f1b78b5282..275b580d79453 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -1,8 +1,6 @@ use std::ops::ControlFlow; -use rustc_errors::{ - Applicability, Diag, E0283, E0284, E0790, MultiSpan, StashKey, struct_span_code_err, -}; +use rustc_errors::{Applicability, Diag, E0283, E0284, E0790, MultiSpan, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::LangItem; use rustc_hir::def::{DefKind, Res}; @@ -197,7 +195,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // 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 + // inhabited. We used to check for `self.tainted_by_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. @@ -211,7 +209,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { TypeAnnotationNeeded::E0282, false, ); - return err.stash(span, StashKey::MaybeForgetReturn).unwrap(); + return err.emit(); } Some(e) => return e, } @@ -230,13 +228,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Pick the first generic parameter that still contains inference variables as the one // we're going to emit an error for. If there are none (see above), fall back to // a more general error. - let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer()); + let term = data + .trait_ref + .args + .iter() + .filter_map(ty::GenericArg::as_term) + .find(|s| s.has_non_region_infer()); - let mut err = if let Some(arg) = arg { + let mut err = if let Some(term) = term { self.emit_inference_failure_err( obligation.cause.body_id, span, - arg, + term, TypeAnnotationNeeded::E0283, true, ) @@ -278,7 +281,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { if let Some(e) = self.tainted_by_errors() - && arg.is_none() + && term.is_none() { // If `arg.is_none()`, then this is probably two param-env // candidates or impl candidates that are equal modulo lifetimes. @@ -315,7 +318,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_fully_qualified_path(&mut err, def_id, span, trait_pred.def_id()); } - if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack()) + if term.is_some_and(|term| term.as_type().is_some()) && let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id) { let mut expr_finder = FindExprBySpan::new(span, self.tcx); @@ -340,7 +343,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .. }, hir::PathSegment { - ident: assoc_item_name, + ident: assoc_item_ident, res: Res::Def(_, item_id), .. }, @@ -350,11 +353,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let None = self.tainted_by_errors() { let (verb, noun) = match self.tcx.associated_item(item_id).kind { - ty::AssocKind::Const => ("refer to the", "constant"), - ty::AssocKind::Fn => ("call", "function"), + ty::AssocKind::Const { .. } => ("refer to the", "constant"), + ty::AssocKind::Fn { .. } => ("call", "function"), // This is already covered by E0223, but this following single match // arm doesn't hurt here. - ty::AssocKind::Type => ("refer to the", "type"), + ty::AssocKind::Type { .. } => ("refer to the", "type"), }; // Replace the more general E0283 with a more specific error @@ -370,17 +373,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(local_def_id) = data.trait_ref.def_id.as_local() && let hir::Node::Item(hir::Item { - ident: trait_name, - kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), + kind: hir::ItemKind::Trait(_, _, trait_ident, _, _, trait_item_refs), .. }) = self.tcx.hir_node_by_def_id(local_def_id) && let Some(method_ref) = trait_item_refs .iter() - .find(|item_ref| item_ref.ident == *assoc_item_name) + .find(|item_ref| item_ref.ident == *assoc_item_ident) { err.span_label( method_ref.span, - format!("`{trait_name}::{assoc_item_name}` defined here"), + format!("`{trait_ident}::{assoc_item_ident}` defined here"), ); } @@ -467,11 +469,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { // Same hacky approach as above to avoid deluging user // with error messages. - if let Err(e) = arg.error_reported() { + if let Err(e) = term.error_reported() { return e; } if let Some(e) = self.tainted_by_errors() { @@ -481,7 +483,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.emit_inference_failure_err( obligation.cause.body_id, span, - arg, + term, TypeAnnotationNeeded::E0282, false, ) @@ -522,18 +524,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // other `Foo` impls are incoherent. return guar; } - let arg = data + let term = data .projection_term .args .iter() - .chain(Some(data.term.into_arg())) + .filter_map(ty::GenericArg::as_term) + .chain([data.term]) .find(|g| g.has_non_region_infer()); let predicate = self.tcx.short_string(predicate, &mut file); - if let Some(arg) = arg { + if let Some(term) = term { self.emit_inference_failure_err( obligation.cause.body_id, span, - arg, + term, TypeAnnotationNeeded::E0284, true, ) @@ -557,12 +560,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(e) = self.tainted_by_errors() { return e; } - let arg = data.walk().find(|g| g.is_non_region_infer()); - if let Some(arg) = arg { + let term = + data.walk().filter_map(ty::GenericArg::as_term).find(|term| term.is_infer()); + if let Some(term) = term { let err = self.emit_inference_failure_err( obligation.cause.body_id, span, - arg, + term, TypeAnnotationNeeded::E0284, true, ); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs index 1c3e570b67695..d8b405e904c06 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs @@ -81,9 +81,7 @@ pub fn call_kind<'tcx>( } }); - let fn_call = parent.and_then(|p| { - lang_items::FN_TRAITS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p) - }); + let fn_call = parent.filter(|&p| tcx.fn_trait_kind_from_def_id(p).is_some()); let operator = if !from_hir_call && let Some(p) = parent { lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d270b4cf96ba8..970160ba212af 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2,6 +2,7 @@ use core::ops::ControlFlow; use std::borrow::Cow; use std::path::PathBuf; +use rustc_abi::ExternAbi; use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; @@ -14,16 +15,20 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, LangItem, Node}; use rustc_infer::infer::{InferOk, TypeTrace}; +use rustc_infer::traits::ImplSource; +use rustc_infer::traits::solve::Goal; use rustc_middle::traits::SignatureMismatchData; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::print::{ PrintPolyTraitPredicateExt, PrintTraitPredicateExt as _, PrintTraitRefExt as _, with_forced_trimmed_paths, }; -use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast}; +use rustc_middle::ty::{ + self, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + Upcast, +}; use rustc_middle::{bug, span_bug}; use rustc_span::{BytePos, DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym}; use tracing::{debug, instrument}; @@ -44,8 +49,8 @@ use crate::infer::{self, InferCtxt, InferCtxtExt as _}; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::{ MismatchedProjectionTypes, NormalizeExt, Obligation, ObligationCause, ObligationCauseCode, - ObligationCtxt, Overflow, PredicateObligation, SelectionError, SignatureMismatch, - TraitDynIncompatible, elaborate, + ObligationCtxt, Overflow, PredicateObligation, SelectionContext, SelectionError, + SignatureMismatch, TraitDynIncompatible, elaborate, specialization_graph, }; impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { @@ -142,7 +147,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && leaf_trait_predicate.def_id() != root_pred.def_id() // The root trait is not `Unsize`, as to avoid talking about it in // `tests/ui/coercion/coerce-issue-49593-box-never.rs`. - && Some(root_pred.def_id()) != self.tcx.lang_items().unsize_trait() + && !self.tcx.is_lang_item(root_pred.def_id(), LangItem::Unsize) { ( self.resolve_vars_if_possible( @@ -809,98 +814,88 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, mut trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Option { - // If `AsyncFnKindHelper` is not implemented, that means that the closure kind - // doesn't extend the goal kind. This is worth reporting, but we can only do so - // if we actually know which closure this goal comes from, so look at the cause - // to see if we can extract that information. - if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::AsyncFnKindHelper) - && let Some(found_kind) = - trait_pred.skip_binder().trait_ref.args.type_at(0).to_opt_closure_kind() - && let Some(expected_kind) = - trait_pred.skip_binder().trait_ref.args.type_at(1).to_opt_closure_kind() - && !found_kind.extends(expected_kind) - { - if let Some((_, Some(parent))) = obligation.cause.code().parent_with_predicate() { - // If we have a derived obligation, then the parent will be a `AsyncFn*` goal. + // If we end up on an `AsyncFnKindHelper` goal, try to unwrap the parent + // `AsyncFn*` goal. + if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::AsyncFnKindHelper) { + let mut code = obligation.cause.code(); + // Unwrap a `FunctionArg` cause, which has been refined from a derived obligation. + if let ObligationCauseCode::FunctionArg { parent_code, .. } = code { + code = &**parent_code; + } + // If we have a derived obligation, then the parent will be a `AsyncFn*` goal. + if let Some((_, Some(parent))) = code.parent_with_predicate() { trait_pred = parent; - } else if let &ObligationCauseCode::FunctionArg { arg_hir_id, .. } = - obligation.cause.code() - && let Some(typeck_results) = &self.typeck_results - && let ty::Closure(closure_def_id, _) | ty::CoroutineClosure(closure_def_id, _) = - *typeck_results.node_type(arg_hir_id).kind() - { - // Otherwise, extract the closure kind from the obligation. - let mut err = self.report_closure_error( - &obligation, - closure_def_id, - found_kind, - expected_kind, - "Async", - ); - self.note_obligation_cause(&mut err, &obligation); - return Some(err.emit()); } } let self_ty = trait_pred.self_ty().skip_binder(); - if let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_pred.def_id()) { - let (closure_def_id, found_args, by_ref_captures) = match *self_ty.kind() { - ty::Closure(def_id, args) => { - (def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), None) - } - ty::CoroutineClosure(def_id, args) => ( - def_id, - args.as_coroutine_closure() - .coroutine_closure_sig() - .map_bound(|sig| sig.tupled_inputs_ty), - Some(args.as_coroutine_closure().coroutine_captures_by_ref_ty()), - ), - _ => return None, + let (expected_kind, trait_prefix) = + if let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_pred.def_id()) { + (expected_kind, "") + } else if let Some(expected_kind) = + self.tcx.async_fn_trait_kind_from_def_id(trait_pred.def_id()) + { + (expected_kind, "Async") + } else { + return None; }; - let expected_args = - trait_pred.map_bound(|trait_pred| trait_pred.trait_ref.args.type_at(1)); - - // Verify that the arguments are compatible. If the signature is - // mismatched, then we have a totally different error to report. - if self.enter_forall(found_args, |found_args| { - self.enter_forall(expected_args, |expected_args| { - !self.can_eq(obligation.param_env, expected_args, found_args) - }) - }) { - return None; + let (closure_def_id, found_args, by_ref_captures) = match *self_ty.kind() { + ty::Closure(def_id, args) => { + (def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), None) } + ty::CoroutineClosure(def_id, args) => ( + def_id, + args.as_coroutine_closure() + .coroutine_closure_sig() + .map_bound(|sig| sig.tupled_inputs_ty), + Some(args.as_coroutine_closure().coroutine_captures_by_ref_ty()), + ), + _ => return None, + }; - if let Some(found_kind) = self.closure_kind(self_ty) - && !found_kind.extends(expected_kind) - { - let mut err = self.report_closure_error( - &obligation, - closure_def_id, - found_kind, - expected_kind, - "", - ); - self.note_obligation_cause(&mut err, &obligation); - return Some(err.emit()); - } + let expected_args = trait_pred.map_bound(|trait_pred| trait_pred.trait_ref.args.type_at(1)); - // If the closure has captures, then perhaps the reason that the trait - // is unimplemented is because async closures don't implement `Fn`/`FnMut` - // if they have captures. - if let Some(by_ref_captures) = by_ref_captures - && let ty::FnPtr(sig_tys, _) = by_ref_captures.kind() - && !sig_tys.skip_binder().output().is_unit() - { - let mut err = self.dcx().create_err(AsyncClosureNotFn { - span: self.tcx.def_span(closure_def_id), - kind: expected_kind.as_str(), - }); - self.note_obligation_cause(&mut err, &obligation); - return Some(err.emit()); - } + // Verify that the arguments are compatible. If the signature is + // mismatched, then we have a totally different error to report. + if self.enter_forall(found_args, |found_args| { + self.enter_forall(expected_args, |expected_args| { + !self.can_eq(obligation.param_env, expected_args, found_args) + }) + }) { + return None; + } + + if let Some(found_kind) = self.closure_kind(self_ty) + && !found_kind.extends(expected_kind) + { + let mut err = self.report_closure_error( + &obligation, + closure_def_id, + found_kind, + expected_kind, + trait_prefix, + ); + self.note_obligation_cause(&mut err, &obligation); + return Some(err.emit()); + } + + // If the closure has captures, then perhaps the reason that the trait + // is unimplemented is because async closures don't implement `Fn`/`FnMut` + // if they have captures. + if let Some(by_ref_captures) = by_ref_captures + && let ty::FnPtr(sig_tys, _) = by_ref_captures.kind() + && !sig_tys.skip_binder().output().is_unit() + { + let mut err = self.dcx().create_err(AsyncClosureNotFn { + span: self.tcx.def_span(closure_def_id), + kind: expected_kind.as_str(), + }); + self.note_obligation_cause(&mut err, &obligation); + return Some(err.emit()); } + None } @@ -917,7 +912,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { )) = arg.kind && let Node::Pat(pat) = self.tcx.hir_node(*hir_id) && let Some((preds, guar)) = self.reported_trait_errors.borrow().get(&pat.span) - && preds.contains(&obligation.predicate) + && preds.contains(&obligation.as_goal()) { return Err(*guar); } @@ -1223,7 +1218,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); // Only suggest derive if this isn't a derived obligation, // and the struct is local. - if let Some(span) = self.tcx.hir().span_if_local(def.did()) + if let Some(span) = self.tcx.hir_span_if_local(def.did()) && obligation.cause.code().parent().is_none() { if ty.is_structural_eq_shallow(self.tcx) { @@ -1279,6 +1274,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn can_match_trait( &self, + param_env: ty::ParamEnv<'tcx>, goal: ty::TraitPredicate<'tcx>, assumption: ty::PolyTraitPredicate<'tcx>, ) -> bool { @@ -1293,11 +1289,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { assumption, ); - self.can_eq(ty::ParamEnv::empty(), goal.trait_ref, trait_assumption.trait_ref) + self.can_eq(param_env, goal.trait_ref, trait_assumption.trait_ref) } fn can_match_projection( &self, + param_env: ty::ParamEnv<'tcx>, goal: ty::ProjectionPredicate<'tcx>, assumption: ty::PolyProjectionPredicate<'tcx>, ) -> bool { @@ -1307,7 +1304,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { assumption, ); - let param_env = ty::ParamEnv::empty(); self.can_eq(param_env, goal.projection_term, assumption.projection_term) && self.can_eq(param_env, goal.term, assumption.term) } @@ -1317,24 +1313,32 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self), ret)] pub(super) fn error_implies( &self, - cond: ty::Predicate<'tcx>, - error: ty::Predicate<'tcx>, + cond: Goal<'tcx, ty::Predicate<'tcx>>, + error: Goal<'tcx, ty::Predicate<'tcx>>, ) -> bool { if cond == error { return true; } - if let Some(error) = error.as_trait_clause() { + // FIXME: We could be smarter about this, i.e. if cond's param-env is a + // subset of error's param-env. This only matters when binders will carry + // predicates though, and obviously only matters for error reporting. + if cond.param_env != error.param_env { + return false; + } + let param_env = error.param_env; + + if let Some(error) = error.predicate.as_trait_clause() { self.enter_forall(error, |error| { - elaborate(self.tcx, std::iter::once(cond)) + elaborate(self.tcx, std::iter::once(cond.predicate)) .filter_map(|implied| implied.as_trait_clause()) - .any(|implied| self.can_match_trait(error, implied)) + .any(|implied| self.can_match_trait(param_env, error, implied)) }) - } else if let Some(error) = error.as_projection_clause() { + } else if let Some(error) = error.predicate.as_projection_clause() { self.enter_forall(error, |error| { - elaborate(self.tcx, std::iter::once(cond)) + elaborate(self.tcx, std::iter::once(cond.predicate)) .filter_map(|implied| implied.as_projection_clause()) - .any(|implied| self.can_match_projection(error, implied)) + .any(|implied| self.can_match_projection(param_env, error, implied)) }) } else { false @@ -1492,34 +1496,33 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - let secondary_span = (|| { + let secondary_span = self.probe(|_| { let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) = predicate.kind().skip_binder() else { return None; }; - let trait_assoc_item = self.tcx.opt_associated_item(proj.projection_term.def_id)?; - let trait_assoc_ident = trait_assoc_item.ident(self.tcx); - - let mut associated_items = vec![]; - self.tcx.for_each_relevant_impl( - self.tcx.trait_of_item(proj.projection_term.def_id)?, - proj.projection_term.self_ty(), - |impl_def_id| { - associated_items.extend( - self.tcx - .associated_items(impl_def_id) - .in_definition_order() - .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident), - ); - }, - ); + let Ok(Some(ImplSource::UserDefined(impl_data))) = SelectionContext::new(self) + .poly_select(&obligation.with( + self.tcx, + predicate.kind().rebind(proj.projection_term.trait_ref(self.tcx)), + )) + else { + return None; + }; - let [associated_item]: &[ty::AssocItem] = &associated_items[..] else { + let Ok(node) = + specialization_graph::assoc_def(self.tcx, impl_data.impl_def_id, proj.def_id()) + else { return None; }; - match self.tcx.hir_get_if_local(associated_item.def_id) { + + if !node.is_final() { + return None; + } + + match self.tcx.hir_get_if_local(node.item.def_id) { Some( hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(_, Some(ty)), @@ -1542,7 +1545,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { )), _ => None, } - })(); + }); self.note_type_err( &mut diag, @@ -1658,7 +1661,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::Alias(ty::Projection, ..) => Some(12), ty::Alias(ty::Inherent, ..) => Some(13), ty::Alias(ty::Opaque, ..) => Some(14), - ty::Alias(ty::Weak, ..) => Some(15), + ty::Alias(ty::Free, ..) => Some(15), ty::Never => Some(16), ty::Adt(..) => Some(17), ty::Coroutine(..) => Some(18), @@ -2250,10 +2253,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // auto-traits or fundamental traits that might not be exactly what // the user might expect to be presented with. Instead this is // useful for less general traits. - if peeled - && !self.tcx.trait_is_auto(def_id) - && !self.tcx.lang_items().iter().any(|(_, id)| id == def_id) - { + if peeled && !self.tcx.trait_is_auto(def_id) && self.tcx.as_lang_item(def_id).is_none() { let impl_candidates = self.find_similar_impl_candidates(trait_pred); self.report_similar_impl_candidates( &impl_candidates, @@ -2776,32 +2776,57 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } // Note any argument mismatches - let given_ty = params.skip_binder(); + let ty::Tuple(given) = *params.skip_binder().kind() else { + return; + }; + let expected_ty = trait_pred.skip_binder().trait_ref.args.type_at(1); - if let ty::Tuple(given) = given_ty.kind() - && let ty::Tuple(expected) = expected_ty.kind() - { - if expected.len() != given.len() { - // Note number of types that were expected and given - err.note( - format!( - "expected a closure taking {} argument{}, but one taking {} argument{} was given", - given.len(), - pluralize!(given.len()), - expected.len(), - pluralize!(expected.len()), - ) - ); - } else if !self.same_type_modulo_infer(given_ty, expected_ty) { - // Print type mismatch - let (expected_args, given_args) = self.cmp(given_ty, expected_ty); - err.note_expected_found( - &"a closure with arguments", - expected_args, - &"a closure with arguments", - given_args, - ); - } + let ty::Tuple(expected) = *expected_ty.kind() else { + return; + }; + + if expected.len() != given.len() { + // Note number of types that were expected and given + err.note(format!( + "expected a closure taking {} argument{}, but one taking {} argument{} was given", + given.len(), + pluralize!(given.len()), + expected.len(), + pluralize!(expected.len()), + )); + return; + } + + let given_ty = Ty::new_fn_ptr( + self.tcx, + params.rebind(self.tcx.mk_fn_sig( + given, + self.tcx.types.unit, + false, + hir::Safety::Safe, + ExternAbi::Rust, + )), + ); + let expected_ty = Ty::new_fn_ptr( + self.tcx, + trait_pred.rebind(self.tcx.mk_fn_sig( + expected, + self.tcx.types.unit, + false, + hir::Safety::Safe, + ExternAbi::Rust, + )), + ); + + if !self.same_type_modulo_infer(given_ty, expected_ty) { + // Print type mismatch + let (expected_args, given_args) = self.cmp(expected_ty, given_ty); + err.note_expected_found( + "a closure with signature", + expected_args, + "a closure with signature", + given_args, + ); } } @@ -2930,7 +2955,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; let found_node = found_did.and_then(|did| self.tcx.hir_get_if_local(did)); - let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did)); + let found_span = found_did.and_then(|did| self.tcx.hir_span_if_local(did)); if !self.reported_signature_mismatch.borrow_mut().insert((span, found_span)) { // We check closures twice, with obligations flowing in different directions, @@ -2964,8 +2989,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // This shouldn't be common unless manually implementing one of the // traits manually, but don't make it more confusing when it does // happen. - if Some(expected_trait_ref.def_id) != self.tcx.lang_items().coroutine_trait() && not_tupled - { + if !self.tcx.is_lang_item(expected_trait_ref.def_id, LangItem::Coroutine) && not_tupled { return Ok(self.report_and_explain_type_error( TypeTrace::trait_refs(&obligation.cause, expected_trait_ref, found_trait_ref), obligation.param_env, @@ -3017,7 +3041,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { node: Node<'_>, ) -> Option<(Span, Option, Vec)> { let sm = self.tcx.sess.source_map(); - let hir = self.tcx.hir(); Some(match node { Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }), @@ -3073,7 +3096,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .collect::>(), ), Node::Ctor(variant_data) => { - let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); + let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| self.tcx.hir_span(id)); (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) } _ => panic!("non-FnLike node found: {node:?}"), @@ -3250,7 +3273,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, span: Span, ) -> Result, ErrorGuaranteed> { - if !self.tcx.features().generic_const_exprs() { + if !self.tcx.features().generic_const_exprs() + && !self.tcx.features().min_generic_const_args() + { let guar = self .dcx() .struct_span_err(span, "constant expression depends on a generic parameter") diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 4d87a93be0ca0..78f9287b407b3 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -2,6 +2,8 @@ pub mod ambiguity; pub mod call_kind; mod fulfillment_errors; pub mod on_unimplemented; +pub mod on_unimplemented_condition; +pub mod on_unimplemented_format; mod overflow; pub mod suggestions; @@ -12,6 +14,7 @@ use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_cod use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, AmbigArg, LangItem}; +use rustc_infer::traits::solve::Goal; use rustc_infer::traits::{ DynCompatibilityViolation, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError, @@ -144,7 +147,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { #[derive(Debug)] struct ErrorDescriptor<'tcx> { - predicate: ty::Predicate<'tcx>, + goal: Goal<'tcx, ty::Predicate<'tcx>>, index: Option, // None if this is an old error } @@ -152,15 +155,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .reported_trait_errors .borrow() .iter() - .map(|(&span, predicates)| { - ( - span, - predicates - .0 - .iter() - .map(|&predicate| ErrorDescriptor { predicate, index: None }) - .collect(), - ) + .map(|(&span, goals)| { + (span, goals.0.iter().map(|&goal| ErrorDescriptor { goal, index: None }).collect()) }) .collect(); @@ -186,10 +182,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span = expn_data.call_site; } - error_map.entry(span).or_default().push(ErrorDescriptor { - predicate: error.obligation.predicate, - index: Some(index), - }); + error_map + .entry(span) + .or_default() + .push(ErrorDescriptor { goal: error.obligation.as_goal(), index: Some(index) }); } // We do this in 2 passes because we want to display errors in order, though @@ -210,9 +206,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { continue; } - if self.error_implies(error2.predicate, error.predicate) + if self.error_implies(error2.goal, error.goal) && !(error2.index >= error.index - && self.error_implies(error.predicate, error2.predicate)) + && self.error_implies(error.goal, error2.goal)) { info!("skipping {:?} (implied by {:?})", error, error2); is_suppressed[index] = true; @@ -243,7 +239,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .entry(span) .or_insert_with(|| (vec![], guar)) .0 - .push(error.obligation.predicate); + .push(error.obligation.as_goal()); } } } @@ -398,7 +394,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) { - if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) { + if let Some(span) = self.tcx.hir_span_if_local(trait_item_def_id) { let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); err.span_label(span, format!("definition of `{item_name}` from trait")); } @@ -419,7 +415,12 @@ pub fn report_dyn_incompatibility<'tcx>( ) -> Diag<'tcx> { let trait_str = tcx.def_path_str(trait_def_id); let trait_span = tcx.hir_get_if_local(trait_def_id).and_then(|node| match node { - hir::Node::Item(item) => Some(item.ident.span), + hir::Node::Item(item) => match item.kind { + hir::ItemKind::Trait(_, _, ident, ..) | hir::ItemKind::TraitAlias(ident, _, _) => { + Some(ident.span) + } + _ => unreachable!(), + }, _ => None, }); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index f0c6e51f2a4c4..d5ee6e2123a19 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -1,44 +1,31 @@ use std::iter; use std::path::PathBuf; -use rustc_ast::MetaItemInner; -use rustc_data_structures::fx::FxHashMap; +use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit}; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; +use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{AttrArgs, Attribute}; use rustc_macros::LintDiagnostic; use rustc_middle::bug; -use rustc_middle::ty::print::PrintTraitRefExt as _; -use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt}; -use rustc_parse_format::{ParseMode, Parser, Piece, Position}; +use rustc_middle::ty::print::PrintTraitRefExt; +use rustc_middle::ty::{self, GenericArgsRef, GenericParamDef, GenericParamDefKind, TyCtxt}; use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; -use rustc_span::{Ident, Span, Symbol, kw, sym}; +use rustc_span::{Span, Symbol, sym}; use tracing::{debug, info}; -use {rustc_attr_parsing as attr, rustc_hir as hir}; use super::{ObligationCauseCode, PredicateObligation}; use crate::error_reporting::TypeErrCtxt; -use crate::errors::{ - EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, +use crate::error_reporting::traits::on_unimplemented_condition::{ + ConditionOptions, OnUnimplementedCondition, }; +use crate::error_reporting::traits::on_unimplemented_format::{ + Ctx, FormatArgs, FormatString, FormatWarning, +}; +use crate::errors::{InvalidOnClause, NoValueInOnUnimplemented}; use crate::infer::InferCtxtExt; -/// The symbols which are always allowed in a format string -static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[ - kw::SelfUpper, - sym::ItemContext, - sym::from_desugaring, - sym::direct, - sym::cause, - sym::integral, - sym::integer_, - sym::float, - sym::_Self, - sym::crate_local, - sym::Trait, -]; - impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn impl_similar_to( &self, @@ -121,86 +108,78 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args)); let trait_pred = trait_pred.skip_binder(); - let mut flags = vec![]; + let mut self_types = vec![]; + let mut generic_args: Vec<(Symbol, String)> = vec![]; + let mut crate_local = false; // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs, // but I guess we could synthesize one here. We don't see any errors that rely on // that yet, though. - let enclosure = self.describe_enclosure(obligation.cause.body_id).map(|t| t.to_owned()); - flags.push((sym::ItemContext, enclosure)); + let item_context = self.describe_enclosure(obligation.cause.body_id).unwrap_or(""); - match obligation.cause.code() { + let direct = match obligation.cause.code() { ObligationCauseCode::BuiltinDerived(..) | ObligationCauseCode::ImplDerived(..) - | ObligationCauseCode::WellFormedDerived(..) => {} + | ObligationCauseCode::WellFormedDerived(..) => false, _ => { // this is a "direct", user-specified, rather than derived, // obligation. - flags.push((sym::direct, None)); + true } - } - - if let Some(k) = obligation.cause.span.desugaring_kind() { - flags.push((sym::from_desugaring, None)); - flags.push((sym::from_desugaring, Some(format!("{k:?}")))); - } + }; - if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { - flags.push((sym::cause, Some("MainFunctionType".to_string()))); - } + let from_desugaring = obligation.cause.span.desugaring_kind(); - flags.push((sym::Trait, Some(trait_pred.trait_ref.print_trait_sugared().to_string()))); + let cause = if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { + Some("MainFunctionType".to_string()) + } else { + None + }; // Add all types without trimmed paths or visible paths, ensuring they end up with // their "canonical" def path. ty::print::with_no_trimmed_paths!(ty::print::with_no_visible_paths!({ let generics = self.tcx.generics_of(def_id); let self_ty = trait_pred.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()))); + self_types.push(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()).instantiate_identity().to_string()), - )); + self_types.push(self.tcx.type_of(def.did()).instantiate_identity().to_string()); } - for param in generics.own_params.iter() { - let value = match param.kind { + for GenericParamDef { name, kind, index, .. } in generics.own_params.iter() { + let value = match kind { GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { - args[param.index as usize].to_string() + args[*index as usize].to_string() } GenericParamDefKind::Lifetime => continue, }; - let name = param.name; - flags.push((name, Some(value))); + generic_args.push((*name, value)); - if let GenericParamDefKind::Type { .. } = param.kind { - let param_ty = args[param.index as usize].expect_ty(); + if let GenericParamDefKind::Type { .. } = kind { + let param_ty = args[*index as usize].expect_ty(); if let Some(def) = param_ty.ty_adt_def() { // We also want to be able to select the parameter's // original signature with no type arguments resolved - flags.push(( - name, - Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()), + generic_args.push(( + *name, + self.tcx.type_of(def.did()).instantiate_identity().to_string(), )); } } } if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) { - flags.push((sym::crate_local, None)); + crate_local = true; } // 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()))); + self_types.push("{integral}".to_owned()); } if self_ty.is_array_slice() { - flags.push((sym::_Self, Some("&[]".to_owned()))); + self_types.push("&[]".to_owned()); } if self_ty.is_fn() { @@ -215,53 +194,51 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { hir::Safety::Unsafe => "unsafe fn", } }; - flags.push((sym::_Self, Some(shortname.to_owned()))); + self_types.push(shortname.to_owned()); } // Slices give us `[]`, `[{ty}]` if let ty::Slice(aty) = self_ty.kind() { - flags.push((sym::_Self, Some("[]".to_string()))); + self_types.push("[]".to_owned()); if let Some(def) = aty.ty_adt_def() { // We also want to be able to select the slice's type's original // signature with no type arguments resolved - flags.push(( - sym::_Self, - Some(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity())), - )); + self_types + .push(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity())); } if aty.is_integral() { - flags.push((sym::_Self, Some("[{integral}]".to_string()))); + self_types.push("[{integral}]".to_string()); } } // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]` if let ty::Array(aty, len) = self_ty.kind() { - flags.push((sym::_Self, Some("[]".to_string()))); + self_types.push("[]".to_string()); let len = len.try_to_target_usize(self.tcx); - flags.push((sym::_Self, Some(format!("[{aty}; _]")))); + self_types.push(format!("[{aty}; _]")); if let Some(n) = len { - flags.push((sym::_Self, Some(format!("[{aty}; {n}]")))); + self_types.push(format!("[{aty}; {n}]")); } 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 let def_ty = self.tcx.type_of(def.did()).instantiate_identity(); - flags.push((sym::_Self, Some(format!("[{def_ty}; _]")))); + self_types.push(format!("[{def_ty}; _]")); if let Some(n) = len { - flags.push((sym::_Self, Some(format!("[{def_ty}; {n}]")))); + self_types.push(format!("[{def_ty}; {n}]")); } } if aty.is_integral() { - flags.push((sym::_Self, Some("[{integral}; _]".to_string()))); + self_types.push("[{integral}; _]".to_string()); if let Some(n) = len { - flags.push((sym::_Self, Some(format!("[{{integral}}; {n}]")))); + self_types.push(format!("[{{integral}}; {n}]")); } } } if let ty::Dynamic(traits, _, _) = self_ty.kind() { for t in traits.iter() { if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() { - flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) + self_types.push(self.tcx.def_path_str(trait_ref.def_id)); } } } @@ -271,34 +248,79 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && let ty::Slice(sty) = ref_ty.kind() && sty.is_integral() { - flags.push((sym::_Self, Some("&[{integral}]".to_owned()))); + self_types.push("&[{integral}]".to_owned()); } })); + let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id); + let trait_sugared = trait_pred.trait_ref.print_trait_sugared(); + + let condition_options = ConditionOptions { + self_types, + from_desugaring, + cause, + crate_local, + direct, + generic_args, + }; + + // Unlike the generic_args earlier, + // this one is *not* collected under `with_no_trimmed_paths!` + // for printing the type to the user + // + // This includes `Self`, as it is the first parameter in `own_params`. + let generic_args = self + .tcx + .generics_of(trait_pred.trait_ref.def_id) + .own_params + .iter() + .filter_map(|param| { + let value = match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { + if let Some(ty) = trait_pred.trait_ref.args[param.index as usize].as_type() + { + self.tcx.short_string(ty, long_ty_file) + } else { + trait_pred.trait_ref.args[param.index as usize].to_string() + } + } + GenericParamDefKind::Lifetime => return None, + }; + let name = param.name; + Some((name, value)) + }) + .collect(); + + let format_args = FormatArgs { this, trait_sugared, generic_args, item_context }; + if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) { - command.evaluate(self.tcx, trait_pred.trait_ref, &flags, long_ty_file) + command.evaluate(self.tcx, trait_pred.trait_ref, &condition_options, &format_args) } else { OnUnimplementedNote::default() } } } +/// Represents a format string in a on_unimplemented attribute, +/// like the "content" in `#[diagnostic::on_unimplemented(message = "content")]` #[derive(Clone, Debug)] pub struct OnUnimplementedFormatString { + /// Symbol of the format string, i.e. `"content"` symbol: Symbol, + ///The span of the format string, i.e. `"content"` span: Span, is_diagnostic_namespace_variant: bool, } #[derive(Debug)] pub struct OnUnimplementedDirective { - pub condition: Option, - pub subcommands: Vec, - pub message: Option, - pub label: Option, - pub notes: Vec, - pub parent_label: Option, - pub append_const_msg: Option, + condition: Option, + subcommands: Vec, + message: Option<(Span, OnUnimplementedFormatString)>, + label: Option<(Span, OnUnimplementedFormatString)>, + notes: Vec, + parent_label: Option, + append_const_msg: Option, } /// For the `#[rustc_on_unimplemented]` attribute @@ -329,7 +351,7 @@ pub struct MalformedOnUnimplementedAttrLint { } impl MalformedOnUnimplementedAttrLint { - fn new(span: Span) -> Self { + pub fn new(span: Span) -> Self { Self { span } } } @@ -350,7 +372,7 @@ pub struct IgnoredDiagnosticOption { } impl IgnoredDiagnosticOption { - fn maybe_emit_warning<'tcx>( + pub fn maybe_emit_warning<'tcx>( tcx: TyCtxt<'tcx>, item_def_id: DefId, new: Option, @@ -370,29 +392,11 @@ impl IgnoredDiagnosticOption { } } -#[derive(LintDiagnostic)] -#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)] -#[help] -pub struct UnknownFormatParameterForOnUnimplementedAttr { - argument_name: Symbol, - trait_name: Ident, -} - -#[derive(LintDiagnostic)] -#[diag(trait_selection_disallowed_positional_argument)] -#[help] -pub struct DisallowedPositionalArgument; - -#[derive(LintDiagnostic)] -#[diag(trait_selection_invalid_format_specifier)] -#[help] -pub struct InvalidFormatSpecifier; - #[derive(LintDiagnostic)] #[diag(trait_selection_wrapped_parser_error)] pub struct WrappedParserError { - description: String, - label: String, + pub description: String, + pub label: String, } impl<'tcx> OnUnimplementedDirective { @@ -407,12 +411,12 @@ impl<'tcx> OnUnimplementedDirective { let mut errored = None; let mut item_iter = items.iter(); - let parse_value = |value_str, value_span| { + let parse_value = |value_str, span| { OnUnimplementedFormatString::try_parse( tcx, item_def_id, value_str, - value_span, + span, is_diagnostic_namespace_variant, ) .map(Some) @@ -423,18 +427,12 @@ impl<'tcx> OnUnimplementedDirective { } else { let cond = item_iter .next() - .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))? - .meta_item_or_bool() - .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClauseInOnUnimplemented { span }))?; - attr::eval_condition(cond, &tcx.sess, Some(tcx.features()), &mut |cfg| { - if let Some(value) = cfg.value - && let Err(guar) = parse_value(value, cfg.span) - { - errored = Some(guar); - } - true - }); - Some(cond.clone()) + .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClause::Empty { span }))?; + + match OnUnimplementedCondition::parse(cond) { + Ok(condition) => Some(condition), + Err(e) => return Err(tcx.dcx().emit_err(e)), + } }; let mut message = None; @@ -444,24 +442,36 @@ impl<'tcx> OnUnimplementedDirective { let mut subcommands = vec![]; let mut append_const_msg = None; + let get_value_and_span = |item: &_, key| { + if let MetaItemInner::MetaItem(MetaItem { + path, + kind: MetaItemKind::NameValue(MetaItemLit { span, kind: LitKind::Str(s, _), .. }), + .. + }) = item + && *path == key + { + Some((*s, *span)) + } else { + None + } + }; + 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_, item.span())?; - continue; - } - } else if item.has_name(sym::label) && label.is_none() { - if let Some(label_) = item.value_str() { - label = parse_value(label_, item.span())?; + if let Some((message_, span)) = get_value_and_span(item, sym::message) + && message.is_none() + { + message = parse_value(message_, span)?.map(|l| (item.span(), l)); + continue; + } else if let Some((label_, span)) = get_value_and_span(item, sym::label) + && label.is_none() + { + label = parse_value(label_, span)?.map(|l| (item.span(), l)); + continue; + } else if let Some((note_, span)) = get_value_and_span(item, sym::note) { + if let Some(note) = parse_value(note_, span)? { + notes.push(note); continue; } - } else if item.has_name(sym::note) { - if let Some(note_) = item.value_str() { - if let Some(note) = parse_value(note_, item.span())? { - notes.push(note); - continue; - } - } } else if item.has_name(sym::parent_label) && parent_label.is_none() && !is_diagnostic_namespace_variant @@ -539,6 +549,13 @@ impl<'tcx> OnUnimplementedDirective { } pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result, ErrorGuaranteed> { + if !tcx.is_trait(item_def_id) { + // It could be a trait_alias (`trait MyTrait = SomeOtherTrait`) + // or an implementation (`impl MyTrait for Foo {}`) + // + // We don't support those. + return Ok(None); + } if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) { return Self::parse_attribute(attr, false, tcx, item_def_id); } else { @@ -554,15 +571,15 @@ impl<'tcx> OnUnimplementedDirective { IgnoredDiagnosticOption::maybe_emit_warning( tcx, item_def_id, - directive.message.as_ref().map(|f| f.span), - aggr.message.as_ref().map(|f| f.span), + directive.message.as_ref().map(|f| f.0), + aggr.message.as_ref().map(|f| f.0), "message", ); IgnoredDiagnosticOption::maybe_emit_warning( tcx, item_def_id, - directive.label.as_ref().map(|f| f.span), - aggr.label.as_ref().map(|f| f.span), + directive.label.as_ref().map(|f| f.0), + aggr.label.as_ref().map(|f| f.0), "label", ); IgnoredDiagnosticOption::maybe_emit_warning( @@ -636,13 +653,16 @@ impl<'tcx> OnUnimplementedDirective { condition: None, message: None, subcommands: vec![], - label: Some(OnUnimplementedFormatString::try_parse( - tcx, - item_def_id, - value, + label: Some(( attr.span(), - is_diagnostic_namespace_variant, - )?), + OnUnimplementedFormatString::try_parse( + tcx, + item_def_id, + value, + attr.value_span().unwrap_or(attr.span()), + is_diagnostic_namespace_variant, + )?, + )), notes: Vec::new(), parent_label: None, append_const_msg: None, @@ -698,47 +718,27 @@ impl<'tcx> OnUnimplementedDirective { result } - pub fn evaluate( + pub(crate) fn evaluate( &self, tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, - options: &[(Symbol, Option)], - long_ty_file: &mut Option, + condition_options: &ConditionOptions, + args: &FormatArgs<'tcx>, ) -> OnUnimplementedNote { let mut message = None; let mut label = None; let mut notes = Vec::new(); let mut parent_label = None; let mut append_const_msg = None; - info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); - - let options_map: FxHashMap = - options.iter().filter_map(|(k, v)| v.clone().map(|v| (*k, v))).collect(); + info!( + "evaluate({:?}, trait_ref={:?}, options={:?}, args ={:?})", + self, trait_ref, condition_options, args + ); for command in self.subcommands.iter().chain(Some(self)).rev() { debug!(?command); if let Some(ref condition) = command.condition - && !attr::eval_condition(condition, &tcx.sess, Some(tcx.features()), &mut |cfg| { - let value = cfg.value.map(|v| { - // `with_no_visible_paths` is also used when generating the options, - // so we need to match it here. - ty::print::with_no_visible_paths!( - OnUnimplementedFormatString { - symbol: v, - span: cfg.span, - is_diagnostic_namespace_variant: false - } - .format( - tcx, - trait_ref, - &options_map, - long_ty_file - ) - ) - }); - - options.contains(&(cfg.name, value)) - }) + && !condition.matches_predicate(condition_options) { debug!("evaluate: skipping {:?} due to condition", command); continue; @@ -762,14 +762,10 @@ impl<'tcx> OnUnimplementedDirective { } OnUnimplementedNote { - label: label.map(|l| l.format(tcx, trait_ref, &options_map, long_ty_file)), - message: message.map(|m| m.format(tcx, trait_ref, &options_map, long_ty_file)), - notes: notes - .into_iter() - .map(|n| n.format(tcx, trait_ref, &options_map, long_ty_file)) - .collect(), - parent_label: parent_label - .map(|e_s| e_s.format(tcx, trait_ref, &options_map, long_ty_file)), + label: label.map(|l| l.1.format(tcx, trait_ref, args)), + message: message.map(|m| m.1.format(tcx, trait_ref, args)), + notes: notes.into_iter().map(|n| n.format(tcx, trait_ref, args)).collect(), + parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, args)), append_const_msg, } } @@ -780,142 +776,95 @@ impl<'tcx> OnUnimplementedFormatString { tcx: TyCtxt<'tcx>, item_def_id: DefId, from: Symbol, - value_span: Span, + span: Span, is_diagnostic_namespace_variant: bool, ) -> Result { - let result = OnUnimplementedFormatString { - symbol: from, - span: value_span, - is_diagnostic_namespace_variant, - }; + let result = + OnUnimplementedFormatString { symbol: from, span, is_diagnostic_namespace_variant }; result.verify(tcx, item_def_id)?; Ok(result) } - fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> { - let trait_def_id = if tcx.is_trait(item_def_id) { - item_def_id + fn verify(&self, tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> { + if !tcx.is_trait(trait_def_id) { + return Ok(()); + }; + + let ctx = if self.is_diagnostic_namespace_variant { + Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id } } else { - tcx.trait_id_of_impl(item_def_id) - .expect("expected `on_unimplemented` to correspond to a trait") + Ctx::RustcOnUnimplemented { tcx, trait_def_id } }; - let trait_name = tcx.item_ident(trait_def_id); - let generics = tcx.generics_of(item_def_id); - let s = self.symbol.as_str(); - let mut parser = Parser::new(s, None, None, false, ParseMode::Format); + let mut result = Ok(()); - for token in &mut parser { - match token { - Piece::Lit(_) => (), // Normal string, no need to check it - Piece::NextArgument(a) => { - let format_spec = a.format; - if self.is_diagnostic_namespace_variant - && (format_spec.ty_span.is_some() - || format_spec.width_span.is_some() - || format_spec.precision_span.is_some() - || format_spec.fill_span.is_some()) - { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - self.span, - InvalidFormatSpecifier, - ); - } + + match FormatString::parse(self.symbol, self.span, &ctx) { + // Warnings about format specifiers, deprecated parameters, wrong parameters etc. + // In other words we'd like to let the author know, but we can still try to format the string later + Ok(FormatString { warnings, .. }) => { + if self.is_diagnostic_namespace_variant { + for w in warnings { + w.emit_warning(tcx, trait_def_id) } - match a.position { - Position::ArgumentNamed(s) => { - match Symbol::intern(s) { - // `{ThisTraitsName}` is allowed - s if s == trait_name.name - && !self.is_diagnostic_namespace_variant => - { - () - } - s if ALLOWED_FORMAT_SYMBOLS.contains(&s) - && !self.is_diagnostic_namespace_variant => - { - () - } - // So is `{A}` if A is a type parameter - s if generics.own_params.iter().any(|param| param.name == s) => (), - s => { - if self.is_diagnostic_namespace_variant { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - self.span, - UnknownFormatParameterForOnUnimplementedAttr { - argument_name: s, - trait_name, - }, - ); - } - } else { - result = Err(struct_span_code_err!( - tcx.dcx(), - self.span, - E0230, - "there is no parameter `{}` on {}", - s, - if trait_def_id == item_def_id { - format!("trait `{trait_name}`") - } else { - "impl".to_string() - } - ) - .emit()); - } - } + } else { + for w in warnings { + match w { + FormatWarning::UnknownParam { argument_name, span } => { + let reported = struct_span_code_err!( + tcx.dcx(), + span, + E0230, + "cannot find parameter {} on this trait", + argument_name, + ) + .emit(); + result = Err(reported); } - } - // `{:1}` and `{}` are not to be used - Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { - if self.is_diagnostic_namespace_variant { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - self.span, - DisallowedPositionalArgument, - ); - } - } else { + FormatWarning::PositionalArgument { span, .. } => { let reported = struct_span_code_err!( tcx.dcx(), - self.span, + span, E0231, - "only named generic parameters are allowed" + "positional format arguments are not allowed here" ) .emit(); result = Err(reported); } + FormatWarning::InvalidSpecifier { .. } + | FormatWarning::FutureIncompat { .. } => {} } } } } - } - // we cannot return errors from processing the format string as hard error here - // as the diagnostic namespace guarantees that malformed input cannot cause an error - // - // if we encounter any error while processing we nevertheless want to show it as warning - // so that users are aware that something is not correct - for e in parser.errors { - if self.is_diagnostic_namespace_variant { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - self.span, - WrappedParserError { description: e.description, label: e.label }, - ); + // Errors from the underlying `rustc_parse_format::Parser` + Err(errors) => { + // we cannot return errors from processing the format string as hard error here + // as the diagnostic namespace guarantees that malformed input cannot cause an error + // + // if we encounter any error while processing we nevertheless want to show it as warning + // so that users are aware that something is not correct + for e in errors { + if self.is_diagnostic_namespace_variant { + if let Some(trait_def_id) = trait_def_id.as_local() { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(trait_def_id), + self.span, + WrappedParserError { description: e.description, label: e.label }, + ); + } + } else { + let reported = struct_span_code_err!( + tcx.dcx(), + self.span, + E0231, + "{}", + e.description, + ) + .emit(); + result = Err(reported); + } } - } else { - let reported = - struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,).emit(); - result = Err(reported); } } @@ -926,98 +875,28 @@ impl<'tcx> OnUnimplementedFormatString { &self, tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, - options: &FxHashMap, - long_ty_file: &mut Option, + args: &FormatArgs<'tcx>, ) -> 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 - .own_params - .iter() - .filter_map(|param| { - let value = match param.kind { - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { - if let Some(ty) = trait_ref.args[param.index as usize].as_type() { - tcx.short_string(ty, long_ty_file) - } else { - trait_ref.args[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.symbol.as_str(); - let mut parser = Parser::new(s, None, None, false, ParseMode::Format); - let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); - let constructed_message = (&mut parser) - .map(|p| match p { - Piece::Lit(s) => s.to_owned(), - Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(arg) => { - let s = Symbol::intern(arg); - match generic_map.get(&s) { - Some(val) => val.to_string(), - None if self.is_diagnostic_namespace_variant => { - format!("{{{arg}}}") - } - None if s == name => trait_str.clone(), - None => { - if let Some(val) = options.get(&s) { - val.clone() - } else if s == sym::from_desugaring { - // don't break messages using these two arguments incorrectly - String::new() - } else if s == sym::ItemContext - && !self.is_diagnostic_namespace_variant - { - item_context.clone() - } else if s == sym::integral { - String::from("{integral}") - } else if s == sym::integer_ { - String::from("{integer}") - } else if s == sym::float { - String::from("{float}") - } else { - bug!( - "broken on_unimplemented {:?} for {:?}: \ - no argument matching {:?}", - self.symbol, - trait_ref, - s - ) - } - } - } - } - Position::ArgumentImplicitlyIs(_) if self.is_diagnostic_namespace_variant => { - String::from("{}") - } - Position::ArgumentIs(idx) if self.is_diagnostic_namespace_variant => { - format!("{{{idx}}}") - } - _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol), - }, - }) - .collect(); - // we cannot return errors from processing the format string as hard error here - // as the diagnostic namespace guarantees that malformed input cannot cause an error - // - // if we encounter any error while processing the format string - // we don't want to show the potentially half assembled formatted string, - // therefore we fall back to just showing the input string in this case - // - // The actual parser errors are emitted earlier - // as lint warnings in OnUnimplementedFormatString::verify - if self.is_diagnostic_namespace_variant && !parser.errors.is_empty() { - String::from(s) + let trait_def_id = trait_ref.def_id; + let ctx = if self.is_diagnostic_namespace_variant { + Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id } + } else { + Ctx::RustcOnUnimplemented { tcx, trait_def_id } + }; + + if let Ok(s) = FormatString::parse(self.symbol, self.span, &ctx) { + s.format(args) } else { - constructed_message + // we cannot return errors from processing the format string as hard error here + // as the diagnostic namespace guarantees that malformed input cannot cause an error + // + // if we encounter any error while processing the format string + // we don't want to show the potentially half assembled formatted string, + // therefore we fall back to just showing the input string in this case + // + // The actual parser errors are emitted earlier + // as lint warnings in OnUnimplementedFormatString::verify + self.symbol.as_str().into() } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs new file mode 100644 index 0000000000000..13753761f0923 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs @@ -0,0 +1,317 @@ +use rustc_ast::{MetaItemInner, MetaItemKind, MetaItemLit}; +use rustc_parse_format::{ParseMode, Parser, Piece, Position}; +use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym}; + +use crate::errors::InvalidOnClause; + +/// Represents the `on` filter in `#[rustc_on_unimplemented]`. +#[derive(Debug)] +pub(crate) struct OnUnimplementedCondition { + span: Span, + pred: Predicate, +} + +impl OnUnimplementedCondition { + pub(crate) fn span(&self) -> Span { + self.span + } + + pub(crate) fn matches_predicate(&self, options: &ConditionOptions) -> bool { + self.pred.eval(&mut |p| match p { + FlagOrNv::Flag(b) => options.has_flag(*b), + FlagOrNv::NameValue(NameValue { name, value }) => { + let value = value.format(&options.generic_args); + options.contains(*name, value) + } + }) + } + + pub(crate) fn parse(input: &MetaItemInner) -> Result { + let span = input.span(); + let pred = Predicate::parse(input)?; + Ok(OnUnimplementedCondition { span, pred }) + } +} + +/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`OnUnimplementedCondition`]. +/// +/// It is similar to the predicate in the `cfg` attribute, +/// and may contain nested predicates. +#[derive(Debug)] +enum Predicate { + /// A condition like `on(crate_local)`. + Flag(Flag), + /// A match, like `on(Rhs = "Whatever")`. + Match(NameValue), + /// Negation, like `on(not($pred))`. + Not(Box), + /// True if all predicates are true, like `on(all($a, $b, $c))`. + All(Vec), + /// True if any predicate is true, like `on(any($a, $b, $c))`. + Any(Vec), +} + +impl Predicate { + fn parse(input: &MetaItemInner) -> Result { + let meta_item = match input { + MetaItemInner::MetaItem(meta_item) => meta_item, + MetaItemInner::Lit(lit) => { + return Err(InvalidOnClause::UnsupportedLiteral { span: lit.span }); + } + }; + + let Some(predicate) = meta_item.ident() else { + return Err(InvalidOnClause::ExpectedIdentifier { + span: meta_item.path.span, + path: meta_item.path.clone(), + }); + }; + + match meta_item.kind { + MetaItemKind::List(ref mis) => match predicate.name { + sym::any => Ok(Predicate::Any(Predicate::parse_sequence(mis)?)), + sym::all => Ok(Predicate::All(Predicate::parse_sequence(mis)?)), + sym::not => match &**mis { + [one] => Ok(Predicate::Not(Box::new(Predicate::parse(one)?))), + [first, .., last] => Err(InvalidOnClause::ExpectedOnePredInNot { + span: first.span().to(last.span()), + }), + [] => Err(InvalidOnClause::ExpectedOnePredInNot { span: meta_item.span }), + }, + invalid_pred => { + Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred }) + } + }, + MetaItemKind::NameValue(MetaItemLit { symbol, .. }) => { + let name = Name::parse(predicate); + let value = FilterFormatString::parse(symbol); + let kv = NameValue { name, value }; + Ok(Predicate::Match(kv)) + } + MetaItemKind::Word => { + let flag = Flag::parse(predicate)?; + Ok(Predicate::Flag(flag)) + } + } + } + + fn parse_sequence(sequence: &[MetaItemInner]) -> Result, InvalidOnClause> { + sequence.iter().map(Predicate::parse).collect() + } + + fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool { + match self { + Predicate::Flag(flag) => eval(FlagOrNv::Flag(flag)), + Predicate::Match(nv) => eval(FlagOrNv::NameValue(nv)), + Predicate::Not(not) => !not.eval(eval), + Predicate::All(preds) => preds.into_iter().all(|pred| pred.eval(eval)), + Predicate::Any(preds) => preds.into_iter().any(|pred| pred.eval(eval)), + } + } +} + +/// Represents a `MetaWord` in an `on`-filter. +#[derive(Debug, Clone, Copy)] +enum Flag { + /// Whether the code causing the trait bound to not be fulfilled + /// is part of the user's crate. + CrateLocal, + /// Whether the obligation is user-specified rather than derived. + Direct, + /// Whether we are in some kind of desugaring like + /// `?` or `try { .. }`. + FromDesugaring, +} + +impl Flag { + fn parse(Ident { name, span }: Ident) -> Result { + match name { + sym::crate_local => Ok(Flag::CrateLocal), + sym::direct => Ok(Flag::Direct), + sym::from_desugaring => Ok(Flag::FromDesugaring), + invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }), + } + } +} + +/// A `MetaNameValueStr` in an `on`-filter. +/// +/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. +#[derive(Debug, Clone)] +struct NameValue { + name: Name, + /// Something like `"&str"` or `"alloc::string::String"`, + /// in which case it just contains a single string piece. + /// But if it is something like `"&[{A}]"` then it must be formatted later. + value: FilterFormatString, +} + +/// The valid names of the `on` filter. +#[derive(Debug, Clone, Copy)] +enum Name { + Cause, + FromDesugaring, + SelfUpper, + GenericArg(Symbol), +} + +impl Name { + fn parse(Ident { name, .. }: Ident) -> Self { + match name { + sym::_Self | kw::SelfUpper => Name::SelfUpper, + sym::from_desugaring => Name::FromDesugaring, + sym::cause => Name::Cause, + // FIXME(mejrs) Perhaps we should start checking that + // this actually is a valid generic parameter? + generic => Name::GenericArg(generic), + } + } +} + +#[derive(Debug, Clone)] +enum FlagOrNv<'p> { + Flag(&'p Flag), + NameValue(&'p NameValue), +} + +/// Represents a value inside an `on` filter. +/// +/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. +/// If it is a simple literal like this then `pieces` will be `[LitOrArg::Lit("value")]`. +/// The `Arg` variant is used when it contains formatting like +/// `#[rustc_on_unimplemented(on(Self = "&[{A}]", message = "hello"))]`. +#[derive(Debug, Clone)] +struct FilterFormatString { + pieces: Vec, +} + +#[derive(Debug, Clone)] +enum LitOrArg { + Lit(String), + Arg(String), +} + +impl FilterFormatString { + fn parse(input: Symbol) -> Self { + let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Format) + .map(|p| match p { + Piece::Lit(s) => LitOrArg::Lit(s.to_owned()), + // We just ignore formatspecs here + Piece::NextArgument(a) => match a.position { + // In `TypeErrCtxt::on_unimplemented_note` we substitute `"{integral}"` even + // if the integer type has been resolved, to allow targeting all integers. + // `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet, + // from the `Display` impl of `InferTy` to be precise. + // + // Don't try to format these later! + Position::ArgumentNamed(arg @ "integer" | arg @ "integral" | arg @ "float") => { + LitOrArg::Lit(format!("{{{arg}}}")) + } + + // FIXME(mejrs) We should check if these correspond to a generic of the trait. + Position::ArgumentNamed(arg) => LitOrArg::Arg(arg.to_owned()), + + // FIXME(mejrs) These should really be warnings/errors + Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(String::from("{}")), + Position::ArgumentIs(idx) => LitOrArg::Lit(format!("{{{idx}}}")), + }, + }) + .collect(); + Self { pieces } + } + + fn format(&self, generic_args: &[(Symbol, String)]) -> String { + let mut ret = String::new(); + + for piece in &self.pieces { + match piece { + LitOrArg::Lit(s) => ret.push_str(s), + LitOrArg::Arg(arg) => { + let s = Symbol::intern(arg); + match generic_args.iter().find(|(k, _)| *k == s) { + Some((_, val)) => ret.push_str(val), + None => { + // FIXME(mejrs) If we start checking as mentioned in + // FilterFormatString::parse then this shouldn't happen + let _ = std::fmt::write(&mut ret, format_args!("{{{s}}}")); + } + } + } + } + } + + ret + } +} + +/// Used with `OnUnimplementedCondition::matches_predicate` to evaluate the +/// [`OnUnimplementedCondition`]. +/// +/// For example, given a +/// ```rust,ignore (just an example) +/// #[rustc_on_unimplemented( +/// on(all(from_desugaring = "QuestionMark"), +/// message = "the `?` operator can only be used in {ItemContext} \ +/// that returns `Result` or `Option` \ +/// (or another type that implements `{FromResidual}`)", +/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`", +/// parent_label = "this function should return `Result` or `Option` to accept `?`" +/// ), +/// )] +/// pub trait FromResidual::Residual> { +/// ... +/// } +/// +/// async fn an_async_function() -> u32 { +/// let x: Option = None; +/// x?; //~ ERROR the `?` operator +/// 22 +/// } +/// ``` +/// it will look like this: +/// +/// ```rust,ignore (just an example) +/// ConditionOptions { +/// self_types: ["u32", "{integral}"], +/// from_desugaring: Some("QuestionMark"), +/// cause: None, +/// crate_local: false, +/// direct: true, +/// generic_args: [("Self","u32"), +/// ("R", "core::option::Option"), +/// ("R", "core::option::Option" ), +/// ], +/// } +/// ``` +#[derive(Debug)] +pub(crate) struct ConditionOptions { + /// All the self types that may apply. + pub(crate) self_types: Vec, + // The kind of compiler desugaring. + pub(crate) from_desugaring: Option, + /// Match on a variant of [rustc_infer::traits::ObligationCauseCode]. + pub(crate) cause: Option, + pub(crate) crate_local: bool, + /// Is the obligation "directly" user-specified, rather than derived? + pub(crate) direct: bool, + // A list of the generic arguments and their reified types. + pub(crate) generic_args: Vec<(Symbol, String)>, +} + +impl ConditionOptions { + fn has_flag(&self, name: Flag) -> bool { + match name { + Flag::CrateLocal => self.crate_local, + Flag::Direct => self.direct, + Flag::FromDesugaring => self.from_desugaring.is_some(), + } + } + fn contains(&self, name: Name, value: String) -> bool { + match name { + Name::SelfUpper => self.self_types.contains(&value), + Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)), + Name::Cause => self.cause == Some(value), + Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)), + } + } +} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs new file mode 100644 index 0000000000000..ce170f820e12f --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs @@ -0,0 +1,413 @@ +use std::fmt; +use std::ops::Range; + +use errors::*; +use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::print::TraitRefPrintSugared; +use rustc_parse_format::{ + Alignment, Argument, Count, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, + Position, +}; +use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; +use rustc_span::def_id::DefId; +use rustc_span::{BytePos, Pos, Span, Symbol, kw, sym}; + +/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces", +/// either as string pieces or dynamic arguments. +#[derive(Debug)] +pub struct FormatString { + #[allow(dead_code, reason = "Debug impl")] + input: Symbol, + span: Span, + pieces: Vec, + /// The formatting string was parsed successfully but with warnings + pub warnings: Vec, +} + +#[derive(Debug)] +enum Piece { + Lit(String), + Arg(FormatArg), +} + +#[derive(Debug)] +enum FormatArg { + // A generic parameter, like `{T}` if we're on the `From` trait. + GenericParam { + generic_param: Symbol, + }, + // `{Self}` + SelfUpper, + /// `{This}` or `{TraitName}` + This, + /// The sugared form of the trait + Trait, + /// what we're in, like a function, method, closure etc. + ItemContext, + /// What the user typed, if it doesn't match anything we can use. + AsIs(String), +} + +pub enum Ctx<'tcx> { + // `#[rustc_on_unimplemented]` + RustcOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId }, + // `#[diagnostic::...]` + DiagnosticOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId }, +} + +#[derive(Debug)] +pub enum FormatWarning { + UnknownParam { argument_name: Symbol, span: Span }, + PositionalArgument { span: Span, help: String }, + InvalidSpecifier { name: String, span: Span }, + FutureIncompat { span: Span, help: String }, +} + +impl FormatWarning { + pub fn emit_warning<'tcx>(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) { + match *self { + FormatWarning::UnknownParam { argument_name, span } => { + let this = tcx.item_ident(item_def_id); + if let Some(item_def_id) = item_def_id.as_local() { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id), + span, + UnknownFormatParameterForOnUnimplementedAttr { + argument_name, + trait_name: this, + }, + ); + } + } + FormatWarning::PositionalArgument { span, .. } => { + if let Some(item_def_id) = item_def_id.as_local() { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id), + span, + DisallowedPositionalArgument, + ); + } + } + FormatWarning::InvalidSpecifier { span, .. } => { + if let Some(item_def_id) = item_def_id.as_local() { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id), + span, + InvalidFormatSpecifier, + ); + } + } + FormatWarning::FutureIncompat { .. } => { + // We've never deprecated anything in diagnostic namespace format strings + // but if we do we will emit a warning here + + // FIXME(mejrs) in a couple releases, start emitting warnings for + // #[rustc_on_unimplemented] deprecated args + } + } + } +} + +/// Arguments to fill a [FormatString] with. +/// +/// For example, given a +/// ```rust,ignore (just an example) +/// +/// #[rustc_on_unimplemented( +/// on(all(from_desugaring = "QuestionMark"), +/// message = "the `?` operator can only be used in {ItemContext} \ +/// that returns `Result` or `Option` \ +/// (or another type that implements `{FromResidual}`)", +/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`", +/// parent_label = "this function should return `Result` or `Option` to accept `?`" +/// ), +/// )] +/// pub trait FromResidual::Residual> { +/// ... +/// } +/// +/// async fn an_async_function() -> u32 { +/// let x: Option = None; +/// x?; //~ ERROR the `?` operator +/// 22 +/// } +/// ``` +/// it will look like this: +/// +/// ```rust,ignore (just an example) +/// FormatArgs { +/// this: "FromResidual", +/// trait_sugared: "FromResidual>", +/// item_context: "an async function", +/// generic_args: [("Self", "u32"), ("R", "Option")], +/// } +/// ``` +#[derive(Debug)] +pub struct FormatArgs<'tcx> { + pub this: String, + pub trait_sugared: TraitRefPrintSugared<'tcx>, + pub item_context: &'static str, + pub generic_args: Vec<(Symbol, String)>, +} + +impl FormatString { + pub fn span(&self) -> Span { + self.span + } + + pub fn parse<'tcx>( + input: Symbol, + span: Span, + ctx: &Ctx<'tcx>, + ) -> Result> { + let s = input.as_str(); + let mut parser = Parser::new(s, None, None, false, ParseMode::Format); + let mut pieces = Vec::new(); + let mut warnings = Vec::new(); + + for piece in &mut parser { + match piece { + RpfPiece::Lit(lit) => { + pieces.push(Piece::Lit(lit.into())); + } + RpfPiece::NextArgument(arg) => { + warn_on_format_spec(arg.format.clone(), &mut warnings, span); + let arg = parse_arg(&arg, ctx, &mut warnings, span); + pieces.push(Piece::Arg(arg)); + } + } + } + + if parser.errors.is_empty() { + Ok(FormatString { input, pieces, span, warnings }) + } else { + Err(parser.errors) + } + } + + pub fn format(&self, args: &FormatArgs<'_>) -> String { + let mut ret = String::new(); + for piece in &self.pieces { + match piece { + Piece::Lit(s) | Piece::Arg(FormatArg::AsIs(s)) => ret.push_str(&s), + + // `A` if we have `trait Trait {}` and `note = "i'm the actual type of {A}"` + Piece::Arg(FormatArg::GenericParam { generic_param }) => { + // Should always be some but we can't raise errors here + let value = match args.generic_args.iter().find(|(p, _)| p == generic_param) { + Some((_, val)) => val.to_string(), + None => generic_param.to_string(), + }; + ret.push_str(&value); + } + // `{Self}` + Piece::Arg(FormatArg::SelfUpper) => { + let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) { + Some((_, val)) => val.to_string(), + None => "Self".to_string(), + }; + ret.push_str(&slf); + } + + // It's only `rustc_onunimplemented` from here + Piece::Arg(FormatArg::This) => ret.push_str(&args.this), + Piece::Arg(FormatArg::Trait) => { + let _ = fmt::write(&mut ret, format_args!("{}", &args.trait_sugared)); + } + Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context), + } + } + ret + } +} + +fn parse_arg<'tcx>( + arg: &Argument<'_>, + ctx: &Ctx<'tcx>, + warnings: &mut Vec, + input_span: Span, +) -> FormatArg { + let (Ctx::RustcOnUnimplemented { tcx, trait_def_id } + | Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = ctx; + let trait_name = tcx.item_ident(*trait_def_id); + let generics = tcx.generics_of(trait_def_id); + let span = slice_span(input_span, arg.position_span.clone()); + + match arg.position { + // Something like "hello {name}" + Position::ArgumentNamed(name) => match (ctx, Symbol::intern(name)) { + // accepted, but deprecated + (Ctx::RustcOnUnimplemented { .. }, sym::_Self) => { + warnings + .push(FormatWarning::FutureIncompat { span, help: String::from("use {Self}") }); + FormatArg::SelfUpper + } + ( + Ctx::RustcOnUnimplemented { .. }, + sym::from_desugaring + | sym::crate_local + | sym::direct + | sym::cause + | sym::float + | sym::integer_ + | sym::integral, + ) => { + warnings.push(FormatWarning::FutureIncompat { + span, + help: String::from("don't use this in a format string"), + }); + FormatArg::AsIs(String::new()) + } + + // Only `#[rustc_on_unimplemented]` can use these + (Ctx::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext, + (Ctx::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This, + (Ctx::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait, + // `{ThisTraitsName}`. Some attrs in std use this, but I'd like to change it to the more general `{This}` + // because that'll be simpler to parse and extend in the future + (Ctx::RustcOnUnimplemented { .. }, name) if name == trait_name.name => { + warnings + .push(FormatWarning::FutureIncompat { span, help: String::from("use {This}") }); + FormatArg::This + } + + // Any attribute can use these + ( + Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. }, + kw::SelfUpper, + ) => FormatArg::SelfUpper, + ( + Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. }, + generic_param, + ) if generics.own_params.iter().any(|param| param.name == generic_param) => { + FormatArg::GenericParam { generic_param } + } + + (_, argument_name) => { + warnings.push(FormatWarning::UnknownParam { argument_name, span }); + FormatArg::AsIs(format!("{{{}}}", argument_name.as_str())) + } + }, + + // `{:1}` and `{}` are ignored + Position::ArgumentIs(idx) => { + warnings.push(FormatWarning::PositionalArgument { + span, + help: format!("use `{{{idx}}}` to print a number in braces"), + }); + FormatArg::AsIs(format!("{{{idx}}}")) + } + Position::ArgumentImplicitlyIs(_) => { + warnings.push(FormatWarning::PositionalArgument { + span, + help: String::from("use `{{}}` to print empty braces"), + }); + FormatArg::AsIs(String::from("{}")) + } + } +} + +/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything +/// with specifiers, so emit a warning if they are used. +fn warn_on_format_spec(spec: FormatSpec<'_>, warnings: &mut Vec, input_span: Span) { + if !matches!( + spec, + FormatSpec { + fill: None, + fill_span: None, + align: Alignment::AlignUnknown, + sign: None, + alternate: false, + zero_pad: false, + debug_hex: None, + precision: Count::CountImplied, + precision_span: None, + width: Count::CountImplied, + width_span: None, + ty: _, + ty_span: _, + }, + ) { + let span = spec.ty_span.map(|inner| slice_span(input_span, inner)).unwrap_or(input_span); + warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() }) + } +} + +fn slice_span(input: Span, range: Range) -> Span { + let span = input.data(); + + Span::new( + span.lo + BytePos::from_usize(range.start), + span.lo + BytePos::from_usize(range.end), + span.ctxt, + span.parent, + ) +} + +pub mod errors { + use rustc_macros::LintDiagnostic; + use rustc_span::Ident; + + use super::*; + + #[derive(LintDiagnostic)] + #[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)] + #[help] + pub struct UnknownFormatParameterForOnUnimplementedAttr { + pub argument_name: Symbol, + pub trait_name: Ident, + } + + #[derive(LintDiagnostic)] + #[diag(trait_selection_disallowed_positional_argument)] + #[help] + pub struct DisallowedPositionalArgument; + + #[derive(LintDiagnostic)] + #[diag(trait_selection_invalid_format_specifier)] + #[help] + pub struct InvalidFormatSpecifier; + + #[derive(LintDiagnostic)] + #[diag(trait_selection_missing_options_for_on_unimplemented_attr)] + #[help] + pub struct MissingOptionsForOnUnimplementedAttr; + + #[derive(LintDiagnostic)] + #[diag(trait_selection_ignored_diagnostic_option)] + pub struct IgnoredDiagnosticOption { + pub option_name: &'static str, + #[label] + pub span: Span, + #[label(trait_selection_other_label)] + pub prev_span: Span, + } + + impl IgnoredDiagnosticOption { + pub fn maybe_emit_warning<'tcx>( + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + new: Option, + old: Option, + option_name: &'static str, + ) { + if let (Some(new_item), Some(old_item)) = (new, old) { + if let Some(item_def_id) = item_def_id.as_local() { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id), + new_item, + IgnoredDiagnosticOption { + span: new_item, + prev_span: old_item, + option_name, + }, + ); + } + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs index c5ed74420d4d2..d929ecf68bf34 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs @@ -5,10 +5,9 @@ use rustc_hir::def::Namespace; use rustc_hir::def_id::LOCAL_CRATE; use rustc_infer::traits::{Obligation, PredicateObligation}; use rustc_middle::ty::print::{FmtPrinter, Print}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt, Upcast}; use rustc_session::Limit; use rustc_span::Span; -use rustc_type_ir::Upcast; use tracing::debug; use crate::error_reporting::TypeErrCtxt; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index b85c18c53121f..6863857f9ecb8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -22,12 +22,11 @@ use rustc_hir::{ expr_needs_parens, is_range_literal, }; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk}; -use rustc_middle::hir::map; use rustc_middle::traits::IsConstable; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::{ PrintPolyTraitPredicateExt as _, PrintPolyTraitRefExt, PrintTraitPredicateExt as _, - with_forced_trimmed_paths, with_no_trimmed_paths, + with_forced_trimmed_paths, with_no_trimmed_paths, with_types_for_suggestion, }; use rustc_middle::ty::{ self, AdtKind, GenericArgs, InferTy, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeFolder, @@ -93,7 +92,7 @@ impl<'a, 'tcx> CoroutineData<'a, 'tcx> { fn get_from_await_ty( &self, visitor: AwaitsVisitor, - hir: map::Map<'tcx>, + tcx: TyCtxt<'tcx>, ty_matches: F, ) -> Option where @@ -102,7 +101,7 @@ impl<'a, 'tcx> CoroutineData<'a, 'tcx> { visitor .awaits .into_iter() - .map(|id| hir.expect_expr(id)) + .map(|id| tcx.hir_expect_expr(id)) .find(|await_expr| ty_matches(ty::Binder::dummy(self.0.expr_ty_adjusted(await_expr)))) .map(|expr| expr.span) } @@ -111,7 +110,7 @@ impl<'a, 'tcx> CoroutineData<'a, 'tcx> { fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) { ( generics.tail_span_for_predicate_suggestion(), - format!("{} {}", generics.add_where_or_trailing_comma(), pred), + with_types_for_suggestion!(format!("{} {}", generics.add_where_or_trailing_comma(), pred)), ) } @@ -137,7 +136,8 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( if hir_generics.where_clause_span.from_expansion() || hir_generics.where_clause_span.desugaring_kind().is_some() || projection.is_some_and(|projection| { - tcx.is_impl_trait_in_trait(projection.def_id) + (tcx.is_impl_trait_in_trait(projection.def_id) + && !tcx.features().return_type_notation()) || tcx.lookup_stability(projection.def_id).is_some_and(|stab| stab.is_unstable()) }) { @@ -267,8 +267,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let node = self.tcx.hir_node_by_def_id(body_id); match node { hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Trait(_, _, generics, bounds, _), + kind: hir::ItemKind::Trait(_, _, ident, generics, bounds, _), .. }) if self_ty == self.tcx.types.self_param => { assert!(param_ty); @@ -282,7 +281,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { None, projection, trait_pred, - Some((ident, bounds)), + Some((&ident, bounds)), ); return; } @@ -331,7 +330,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } hir::Node::Item(hir::Item { kind: - hir::ItemKind::Trait(_, _, generics, ..) + hir::ItemKind::Trait(_, _, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }), .. }) if projection.is_some() => { @@ -352,15 +351,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { hir::Node::Item(hir::Item { kind: - hir::ItemKind::Struct(_, generics) - | hir::ItemKind::Enum(_, generics) - | hir::ItemKind::Union(_, generics) - | hir::ItemKind::Trait(_, _, generics, ..) + hir::ItemKind::Struct(_, _, generics) + | hir::ItemKind::Enum(_, _, generics) + | hir::ItemKind::Union(_, _, generics) + | hir::ItemKind::Trait(_, _, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }) | hir::ItemKind::Fn { generics, .. } - | hir::ItemKind::TyAlias(_, generics) - | hir::ItemKind::Const(_, generics, _) - | hir::ItemKind::TraitAlias(generics, _), + | hir::ItemKind::TyAlias(_, _, generics) + | hir::ItemKind::Const(_, _, generics, _) + | hir::ItemKind::TraitAlias(_, generics, _), .. }) | hir::Node::TraitItem(hir::TraitItem { generics, .. }) @@ -390,13 +389,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some((name, term)) = associated_ty { // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err. // That should be extracted into a helper function. - if constraint.ends_with('>') { - constraint = format!( - "{}, {} = {}>", - &constraint[..constraint.len() - 1], - name, - term - ); + if let Some(stripped) = constraint.strip_suffix('>') { + constraint = format!("{stripped}, {name} = {term}>"); } else { constraint.push_str(&format!("<{name} = {term}>")); } @@ -417,15 +411,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { hir::Node::Item(hir::Item { kind: - hir::ItemKind::Struct(_, generics) - | hir::ItemKind::Enum(_, generics) - | hir::ItemKind::Union(_, generics) - | hir::ItemKind::Trait(_, _, generics, ..) + hir::ItemKind::Struct(_, _, generics) + | hir::ItemKind::Enum(_, _, generics) + | hir::ItemKind::Union(_, _, generics) + | hir::ItemKind::Trait(_, _, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }) | hir::ItemKind::Fn { generics, .. } - | hir::ItemKind::TyAlias(_, generics) - | hir::ItemKind::Const(_, generics, _) - | hir::ItemKind::TraitAlias(generics, _), + | hir::ItemKind::TyAlias(_, _, generics) + | hir::ItemKind::Const(_, _, generics, _) + | hir::ItemKind::TraitAlias(_, generics, _), .. }) if !param_ty => { // Missing generic type parameter bound. @@ -847,7 +841,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { name.to_string() } Some(hir::Node::Item(hir::Item { - ident, kind: hir::ItemKind::Fn { .. }, .. + kind: hir::ItemKind::Fn { ident, .. }, .. })) => { err.span_label(ident.span, "consider calling this function"); ident.to_string() @@ -1199,7 +1193,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // FIXME(compiler-errors): This is kind of a mess, but required for obligations // that come from a path expr to affect the *call* expr. c @ ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, _) - if self.tcx.hir().span(*hir_id).lo() == span.lo() => + if self.tcx.hir_span(*hir_id).lo() == span.lo() => { c } @@ -1522,6 +1516,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } else { expr.span.with_hi(expr.span.lo() + BytePos(1)) }; + + match self.tcx.sess.source_map().span_to_snippet(span) { + Ok(snippet) if snippet.starts_with("&") => {} + _ => break 'outer, + } + suggestions.push((span, String::new())); let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else { @@ -1593,20 +1593,20 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(typeck_results) = &self.typeck_results && let ty = typeck_results.expr_ty_adjusted(base) && let ty::FnDef(def_id, _args) = ty.kind() - && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) = - self.tcx.hir_get_if_local(*def_id) + && let Some(hir::Node::Item(item)) = self.tcx.hir_get_if_local(*def_id) { + let (ident, _, _, _) = item.expect_fn(); let msg = format!("alternatively, consider making `fn {ident}` asynchronous"); - if vis_span.is_empty() { + if item.vis_span.is_empty() { err.span_suggestion_verbose( - span.shrink_to_lo(), + item.span.shrink_to_lo(), msg, "async ", Applicability::MaybeIncorrect, ); } else { err.span_suggestion_verbose( - vis_span.shrink_to_hi(), + item.vis_span.shrink_to_hi(), msg, " async", Applicability::MaybeIncorrect, @@ -1994,14 +1994,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { let closure: Vec<_> = self .tcx - .fn_arg_names(fn_def_id) + .fn_arg_idents(fn_def_id) .iter() .enumerate() .map(|(i, ident)| { - if ident.name.is_empty() || ident.name == kw::SelfLower { - format!("arg{i}") - } else { + if let Some(ident) = ident + && !matches!(ident, Ident { name: kw::Underscore | kw::SelfLower, .. }) + { format!("{ident}") + } else { + format!("arg{i}") } }) .collect(); @@ -2116,18 +2118,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_ref: DefId, ) { if let Some(assoc_item) = self.tcx.opt_associated_item(item_def_id) { - if let ty::AssocKind::Const | ty::AssocKind::Type = assoc_item.kind { + if let ty::AssocKind::Const { .. } | ty::AssocKind::Type { .. } = assoc_item.kind { err.note(format!( "{}s cannot be accessed directly on a `trait`, they can only be \ accessed through a specific `impl`", - self.tcx.def_kind_descr(assoc_item.kind.as_def_kind(), item_def_id) + self.tcx.def_kind_descr(assoc_item.as_def_kind(), item_def_id) )); - err.span_suggestion( - span, - "use the fully qualified path to an implementation", - format!("::{}", self.tcx.def_path_str(trait_ref), assoc_item.name), - Applicability::HasPlaceholders, - ); + + if !assoc_item.is_impl_trait_in_trait() { + err.span_suggestion( + span, + "use the fully qualified path to an implementation", + format!( + "::{}", + self.tcx.def_path_str(trait_ref), + assoc_item.name() + ), + Applicability::HasPlaceholders, + ); + } } } } @@ -2180,8 +2189,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err: &mut Diag<'_, G>, obligation: &PredicateObligation<'tcx>, ) -> bool { - let hir = self.tcx.hir(); - // Attempt to detect an async-await error by looking at the obligation causes, looking // for a coroutine to be present. // @@ -2350,7 +2357,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut interior_or_upvar_span = None; - let from_awaited_ty = coroutine_data.get_from_await_ty(visitor, hir, ty_matches); + let from_awaited_ty = coroutine_data.get_from_await_ty(visitor, self.tcx, ty_matches); debug!(?from_awaited_ty); // Avoid disclosing internal information to downstream crates. @@ -2428,7 +2435,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Special case the primary error message when send or sync is the trait that was // not implemented. - let hir = self.tcx.hir(); let trait_explanation = if let Some(name @ (sym::Send | sym::Sync)) = self.tcx.get_diagnostic_name(trait_pred.def_id()) { @@ -2455,7 +2461,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .parent(coroutine_did) .as_local() .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) - .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) + .and_then(|parent_hir_id| self.tcx.hir_opt_name(parent_hir_id)) .map(|name| { format!("future returned by `{name}` is not {trait_name}") })?, @@ -2479,7 +2485,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .parent(coroutine_did) .as_local() .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) - .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) + .and_then(|parent_hir_id| self.tcx.hir_opt_name(parent_hir_id)) .map(|name| { format!("async iterator returned by `{name}` is not {trait_name}") })?, @@ -2502,7 +2508,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .parent(coroutine_did) .as_local() .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) - .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) + .and_then(|parent_hir_id| self.tcx.hir_opt_name(parent_hir_id)) .map(|name| { format!("iterator returned by `{name}` is not {trait_name}") })? @@ -2695,7 +2701,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | ObligationCauseCode::LetElse | ObligationCauseCode::BinOp { .. } | ObligationCauseCode::AscribeUserTypeProvePredicate(..) - | ObligationCauseCode::DropImpl + | ObligationCauseCode::AlwaysApplicableImpl | ObligationCauseCode::ConstParam(_) | ObligationCauseCode::ReferenceOutlivesReferent(..) | ObligationCauseCode::ObjectTypeBound(..) => {} @@ -3025,12 +3031,23 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"), }; let needs_parens = traits.len() != 1; - err.span_suggestion_verbose( - span, - "you can use `impl Trait` as the argument type", - "impl ", - Applicability::MaybeIncorrect, - ); + // Don't recommend impl Trait as a closure argument + if let Some(hir_id) = hir_id + && matches!( + self.tcx.parent_hir_node(hir_id), + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { .. }, + .. + }) + ) + { + err.span_suggestion_verbose( + span, + "you can use `impl Trait` as the argument type", + "impl ", + Applicability::MaybeIncorrect, + ); + } let sugg = if !needs_parens { vec![(span.shrink_to_lo(), format!("&{kw}"))] } else { @@ -3126,8 +3143,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } - ObligationCauseCode::ConstSized => { - err.note("constant expressions must have a statically known size"); + ObligationCauseCode::SizedConstOrStatic => { + err.note("statics and constants must have a statically known size"); } ObligationCauseCode::InlineAsmSized => { err.note("all inline asm arguments must have a statically known size"); @@ -3191,7 +3208,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { false }; - if !is_upvar_tys_infer_tuple { + let is_builtin_async_fn_trait = + tcx.async_fn_trait_kind_from_def_id(data.parent_trait_pred.def_id()).is_some(); + + if !is_upvar_tys_infer_tuple && !is_builtin_async_fn_trait { let ty_str = tcx.short_string(ty, err.long_ty_path()); let msg = format!("required because it appears within the type `{ty_str}`"); match ty.kind() { @@ -3305,8 +3325,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut is_auto_trait = false; match tcx.hir_get_if_local(data.impl_or_alias_def_id) { Some(Node::Item(hir::Item { - kind: hir::ItemKind::Trait(is_auto, ..), - ident, + kind: hir::ItemKind::Trait(is_auto, _, ident, ..), .. })) => { // FIXME: we should do something else so that it works even on crate foreign @@ -3566,7 +3585,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ObligationCauseCode::OpaqueReturnType(expr_info) => { let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info { let expr_ty = tcx.short_string(expr_ty, err.long_ty_path()); - let expr = tcx.hir().expect_expr(hir_id); + let expr = tcx.hir_expect_expr(hir_id); (expr_ty, expr) } else if let Some(body_id) = tcx.hir_node_by_def_id(body_id).body_id() && let body = tcx.hir_body(body_id) @@ -3834,12 +3853,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder() && let ty::ClauseKind::Trait(pred) = clause - && [ - tcx.lang_items().fn_once_trait(), - tcx.lang_items().fn_mut_trait(), - tcx.lang_items().fn_trait(), - ] - .contains(&Some(pred.def_id())) + && tcx.fn_trait_kind_from_def_id(pred.def_id()).is_some() { if let [stmt, ..] = block.stmts && let hir::StmtKind::Semi(value) = stmt.kind @@ -4486,7 +4500,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Obligation::new(self.tcx, obligation.cause.clone(), obligation.param_env, trait_ref); if self.predicate_must_hold_modulo_regions(&obligation) { - let arg_span = self.tcx.hir().span(*arg_hir_id); + let arg_span = self.tcx.hir_span(*arg_hir_id); err.multipart_suggestion_verbose( format!("use a unary tuple instead"), vec![(arg_span.shrink_to_lo(), "(".into()), (arg_span.shrink_to_hi(), ",)".into())], @@ -4526,7 +4540,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { parent_code: _, } = cause.code() { - let arg_span = self.tcx.hir().span(*arg_hir_id); + let arg_span = self.tcx.hir_span(*arg_hir_id); let mut sp: MultiSpan = arg_span.into(); sp.push_span_label( @@ -4535,7 +4549,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { generic types that should be inferred from this argument", ); sp.push_span_label( - self.tcx.hir().span(*call_hir_id), + self.tcx.hir_span(*call_hir_id), "add turbofish arguments to this call to \ specify the types manually, even if it's redundant", ); @@ -4944,7 +4958,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .type_implements_trait(pred.def_id(), [rhs_ty, lhs_ty], param_env) .must_apply_modulo_regions() { - let lhs_span = tcx.hir().span(lhs_hir_id); + let lhs_span = tcx.hir_span(lhs_hir_id); let sm = tcx.sess.source_map(); if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_span) && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_span) @@ -5402,10 +5416,10 @@ fn point_at_assoc_type_restriction( ); } if let Some(new) = - tcx.associated_items(data.impl_or_alias_def_id).find_by_name_and_kind( + tcx.associated_items(data.impl_or_alias_def_id).find_by_ident_and_kind( tcx, Ident::with_dummy_span(name), - ty::AssocKind::Type, + ty::AssocTag::Type, data.impl_or_alias_def_id, ) { diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index fe859eb53cd78..8ab4d795c4597 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,10 +1,11 @@ use std::path::PathBuf; +use rustc_ast::Path; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, - EmissionGuarantee, IntoDiagArg, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic, + EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -12,7 +13,7 @@ use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty}; use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath}; -use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, Region, Ty, TyCtxt}; +use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, GenericArg, Region, Ty, TyCtxt}; use rustc_span::{BytePos, Ident, Span, Symbol, kw}; use crate::error_reporting::infer::ObligationCauseAsDiagArg; @@ -31,23 +32,50 @@ pub struct UnableToConstructConstantValue<'a> { } #[derive(Diagnostic)] -#[diag(trait_selection_empty_on_clause_in_rustc_on_unimplemented, code = E0232)] -pub struct EmptyOnClauseInOnUnimplemented { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(trait_selection_invalid_on_clause_in_rustc_on_unimplemented, code = E0232)] -pub struct InvalidOnClauseInOnUnimplemented { - #[primary_span] - #[label] - pub span: Span, +pub enum InvalidOnClause { + #[diag(trait_selection_rustc_on_unimplemented_empty_on_clause, code = E0232)] + Empty { + #[primary_span] + #[label] + span: Span, + }, + #[diag(trait_selection_rustc_on_unimplemented_expected_one_predicate_in_not, code = E0232)] + ExpectedOnePredInNot { + #[primary_span] + #[label] + span: Span, + }, + #[diag(trait_selection_rustc_on_unimplemented_unsupported_literal_in_on, code = E0232)] + UnsupportedLiteral { + #[primary_span] + #[label] + span: Span, + }, + #[diag(trait_selection_rustc_on_unimplemented_expected_identifier, code = E0232)] + ExpectedIdentifier { + #[primary_span] + #[label] + span: Span, + path: Path, + }, + #[diag(trait_selection_rustc_on_unimplemented_invalid_predicate, code = E0232)] + InvalidPredicate { + #[primary_span] + #[label] + span: Span, + invalid_pred: Symbol, + }, + #[diag(trait_selection_rustc_on_unimplemented_invalid_flag, code = E0232)] + InvalidFlag { + #[primary_span] + #[label] + span: Span, + invalid_flag: Symbol, + }, } #[derive(Diagnostic)] -#[diag(trait_selection_no_value_in_rustc_on_unimplemented, code = E0232)] +#[diag(trait_selection_rustc_on_unimplemented_missing_value, code = E0232)] #[note] pub struct NoValueInOnUnimplemented { #[primary_span] @@ -107,11 +135,7 @@ pub enum AdjustSignatureBorrow { } impl Subdiagnostic for AdjustSignatureBorrow { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { AdjustSignatureBorrow::Borrow { to_borrow } => { diag.arg("len", to_borrow.len()); @@ -195,8 +219,6 @@ pub struct AnnotationRequired<'a> { #[note(trait_selection_full_type_written)] pub was_written: bool, pub path: PathBuf, - #[note(trait_selection_type_annotations_needed_error_time)] - pub time_version: bool, } // Copy of `AnnotationRequired` for E0283 @@ -381,11 +403,7 @@ pub enum RegionOriginNote<'a> { } impl Subdiagnostic for RegionOriginNote<'_> { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { let mut label_or_note = |span, msg: DiagMessage| { let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count(); @@ -415,7 +433,7 @@ impl Subdiagnostic for RegionOriginNote<'_> { label_or_note(span, fluent::trait_selection_subtype); diag.arg("requirement", requirement); - diag.note_expected_found(&"", expected, &"", found); + diag.note_expected_found("", expected, "", found); } RegionOriginNote::WithRequirement { span, requirement, expected_found: None } => { // FIXME: this really should be handled at some earlier stage. Our @@ -446,11 +464,7 @@ pub enum LifetimeMismatchLabels { } impl Subdiagnostic for LifetimeMismatchLabels { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { diag.span_label(param_span, fluent::trait_selection_declared_different); @@ -495,11 +509,7 @@ pub struct AddLifetimeParamsSuggestion<'a> { } impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { let mut mk_suggestion = || { let Some(anon_reg) = self.tcx.is_suitable_region(self.generic_param_scope, self.sub) else { @@ -516,7 +526,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.scope)) { hir::Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, generics, ..), + kind: hir::ItemKind::Trait(_, _, _, generics, ..), .. }) | hir::Node::Item(hir::Item { @@ -567,15 +577,6 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { impl<'v> Visitor<'v> for ImplicitLifetimeFinder { fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { - let make_suggestion = |ident: Ident| { - if ident.name == kw::Empty && ident.span.is_empty() { - format!("{}, ", self.suggestion_param_name) - } else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() { - format!("{} ", self.suggestion_param_name) - } else { - self.suggestion_param_name.clone() - } - }; match ty.kind { hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { for segment in path.segments { @@ -584,7 +585,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { matches!( arg, hir::GenericArg::Lifetime(lifetime) - if lifetime.ident.name == kw::Empty + if lifetime.is_syntactically_hidden() ) }) { self.suggestions.push(( @@ -603,10 +604,10 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { if let hir::GenericArg::Lifetime(lifetime) = arg && lifetime.is_anonymous() { - self.suggestions.push(( - lifetime.ident.span, - make_suggestion(lifetime.ident), - )); + self.suggestions.push( + lifetime + .suggestion(&self.suggestion_param_name), + ); } } } @@ -614,8 +615,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { } } hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => { - self.suggestions - .push((lifetime.ident.span, make_suggestion(lifetime.ident))); + self.suggestions.push(lifetime.suggestion(&self.suggestion_param_name)); } _ => {} } @@ -686,11 +686,7 @@ pub struct IntroducesStaticBecauseUnmetLifetimeReq { } impl Subdiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { - fn add_to_diag_with>( - mut self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(mut self, diag: &mut Diag<'_, G>) { self.unmet_requirements .push_span_label(self.binding_span, fluent::trait_selection_msl_introduces_static); diag.span_note(self.unmet_requirements, fluent::trait_selection_msl_unmet_req); @@ -1005,17 +1001,13 @@ pub struct ConsiderBorrowingParamHelp { } impl Subdiagnostic for ConsiderBorrowingParamHelp { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { let mut type_param_span: MultiSpan = self.spans.clone().into(); for &span in &self.spans { // Seems like we can't call f() here as Into is required type_param_span.push_span_label(span, fluent::trait_selection_tid_consider_borrowing); } - let msg = f(diag, fluent::trait_selection_tid_param_help.into()); + let msg = diag.eagerly_translate(fluent::trait_selection_tid_param_help); diag.span_help(type_param_span, msg); } } @@ -1050,18 +1042,14 @@ pub struct DynTraitConstraintSuggestion { } impl Subdiagnostic for DynTraitConstraintSuggestion { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { let mut multi_span: MultiSpan = vec![self.span].into(); multi_span.push_span_label(self.span, fluent::trait_selection_dtcs_has_lifetime_req_label); multi_span .push_span_label(self.ident.span, fluent::trait_selection_dtcs_introduces_requirement); - let msg = f(diag, fluent::trait_selection_dtcs_has_req_note.into()); + let msg = diag.eagerly_translate(fluent::trait_selection_dtcs_has_req_note); diag.span_note(multi_span, msg); - let msg = f(diag, fluent::trait_selection_dtcs_suggestion.into()); + let msg = diag.eagerly_translate(fluent::trait_selection_dtcs_suggestion); diag.span_suggestion_verbose( self.span.shrink_to_hi(), msg, @@ -1098,11 +1086,7 @@ pub struct ReqIntroducedLocations { } impl Subdiagnostic for ReqIntroducedLocations { - fn add_to_diag_with>( - mut self, - diag: &mut Diag<'_, G>, - f: &F, - ) { + fn add_to_diag(mut self, diag: &mut Diag<'_, G>) { for sp in self.spans { self.span.push_span_label(sp, fluent::trait_selection_ril_introduced_here); } @@ -1111,7 +1095,7 @@ impl Subdiagnostic for ReqIntroducedLocations { self.span.push_span_label(self.fn_decl_span, fluent::trait_selection_ril_introduced_by); } self.span.push_span_label(self.cause_span, fluent::trait_selection_ril_because_of); - let msg = f(diag, fluent::trait_selection_ril_static_introduced_by.into()); + let msg = diag.eagerly_translate(fluent::trait_selection_ril_static_introduced_by); diag.span_note(self.span, msg); } } @@ -1510,13 +1494,9 @@ pub struct SuggestTuplePatternMany { } impl Subdiagnostic for SuggestTuplePatternMany { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("path", self.path); - let message = f(diag, crate::fluent_generated::trait_selection_stp_wrap_many.into()); + let message = diag.eagerly_translate(fluent::trait_selection_stp_wrap_many); diag.multipart_suggestions( message, self.compatible_variants.into_iter().map(|variant| { @@ -1749,11 +1729,7 @@ pub struct AddPreciseCapturingAndParams { } impl Subdiagnostic for AddPreciseCapturingAndParams { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("new_lifetime", self.new_lifetime); diag.multipart_suggestion_verbose( fluent::trait_selection_precise_capturing_new_but_apit, @@ -1893,11 +1869,7 @@ pub struct AddPreciseCapturingForOvercapture { } impl Subdiagnostic for AddPreciseCapturingForOvercapture { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { let applicability = if self.apit_spans.is_empty() { Applicability::MachineApplicable } else { @@ -1919,3 +1891,14 @@ impl Subdiagnostic for AddPreciseCapturingForOvercapture { } } } + +#[derive(Diagnostic)] +#[diag(trait_selection_opaque_type_non_generic_param, code = E0792)] +pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> { + pub arg: GenericArg<'tcx>, + pub kind: &'a str, + #[primary_span] + pub span: Span, + #[label] + pub param_span: Span, +} diff --git a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs index 46622246a178d..84e7686fdd3fc 100644 --- a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs @@ -1,4 +1,4 @@ -use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, SubdiagMessageOp, Subdiagnostic}; +use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, Subdiagnostic}; use rustc_hir::def_id::LocalDefId; use rustc_middle::bug; use rustc_middle::ty::{self, TyCtxt}; @@ -20,7 +20,7 @@ impl<'a> DescriptionCtx<'a> { region: ty::Region<'tcx>, alt_span: Option, ) -> Option { - let (span, kind, arg) = match *region { + let (span, kind, arg) = match region.kind() { ty::ReEarlyParam(br) => { let scope = tcx .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id) @@ -162,17 +162,13 @@ impl RegionExplanation<'_> { } impl Subdiagnostic for RegionExplanation<'_> { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("pref_kind", self.prefix); diag.arg("suff_kind", self.suffix); diag.arg("desc_kind", self.desc.kind); diag.arg("desc_arg", self.desc.arg); - let msg = f(diag, fluent::trait_selection_region_explanation.into()); + let msg = diag.eagerly_translate(fluent::trait_selection_region_explanation); if let Some(span) = self.desc.span { diag.span_note(span, msg); } else { diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index f373706b2960c..0dab3adadb033 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -15,7 +15,7 @@ use tracing::instrument; use crate::infer::at::ToTrace; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::{self, Obligation, ObligationCause, ObligationCtxt, SelectionContext}; +use crate::traits::{self, Obligation, ObligationCause, ObligationCtxt}; #[extension(pub trait InferCtxtExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { @@ -34,7 +34,7 @@ impl<'tcx> InferCtxt<'tcx> { // FIXME(#132279): This should be removed as it causes us to incorrectly // handle opaques in their defining scope. - if !(param_env, ty).has_infer() { + if !self.next_trait_solver() && !(param_env, ty).has_infer() { return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty); } @@ -53,6 +53,16 @@ impl<'tcx> InferCtxt<'tcx> { traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, clone_def_id) } + fn type_is_use_cloned_modulo_regions( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + let ty = self.resolve_vars_if_possible(ty); + let use_cloned_def_id = self.tcx.require_lang_item(LangItem::UseCloned, None); + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, use_cloned_def_id) + } + fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item) @@ -112,9 +122,6 @@ impl<'tcx> InferCtxt<'tcx> { /// - If this returns `Some([errors..])`, then the trait has an impl for /// the self type, but some nested obligations do not hold. /// - If this returns `None`, no implementation that applies could be found. - /// - /// FIXME(-Znext-solver): Due to the recursive nature of the new solver, - /// this will probably only ever return `Some([])` or `None`. fn type_implements_trait_shallow( &self, trait_def_id: DefId, @@ -122,20 +129,29 @@ impl<'tcx> InferCtxt<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> Option>> { self.probe(|_snapshot| { - let mut selcx = SelectionContext::new(self); - match selcx.select(&Obligation::new( + let ocx = ObligationCtxt::new_with_diagnostics(self); + ocx.register_obligation(Obligation::new( self.tcx, ObligationCause::dummy(), param_env, ty::TraitRef::new(self.tcx, trait_def_id, [ty]), - )) { - Ok(Some(selection)) => { - let ocx = ObligationCtxt::new_with_diagnostics(self); - ocx.register_obligations(selection.nested_obligations()); - Some(ocx.select_all_or_error()) + )); + let errors = ocx.select_where_possible(); + // Find the original predicate in the list of predicates that could definitely not be fulfilled. + // If it is in that list, then we know this doesn't even shallowly implement the trait. + // If it is not in that list, it was fulfilled, but there may be nested obligations, which we don't care about here. + for error in &errors { + let Some(trait_clause) = error.obligation.predicate.as_trait_clause() else { + continue; + }; + let Some(bound_ty) = trait_clause.self_ty().no_bound_vars() else { continue }; + if trait_clause.def_id() == trait_def_id + && ocx.eq(&ObligationCause::dummy(), param_env, bound_ty, ty).is_ok() + { + return None; } - Ok(None) | Err(_) => None, } + Some(errors) }) } } diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index b235d0da83cca..67328defe36b5 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -23,7 +23,6 @@ #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iterator_try_reduce)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(try_blocks)] @@ -31,12 +30,12 @@ #![feature(unwrap_infallible)] #![feature(yeet_expr)] #![recursion_limit = "512"] // For rustdoc -#![warn(unreachable_pub)] // For rustdoc // tidy-alphabetical-end pub mod error_reporting; pub mod errors; pub mod infer; +pub mod opaque_types; pub mod regions; pub mod solve; pub mod traits; diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs new file mode 100644 index 0000000000000..332204a0c5f06 --- /dev/null +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -0,0 +1,207 @@ +use rustc_data_structures::fx::FxIndexMap; +use rustc_hir::OpaqueTyOrigin; +use rustc_hir::def_id::LocalDefId; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_middle::ty::{ + self, DefiningScopeKind, GenericArgKind, GenericArgs, OpaqueTypeKey, TyCtxt, TypeVisitableExt, + TypingMode, fold_regions, +}; +use rustc_span::{ErrorGuaranteed, Span}; + +use crate::errors::NonGenericOpaqueTypeParam; +use crate::regions::OutlivesEnvironmentBuildExt; +use crate::traits::ObligationCtxt; + +pub enum InvalidOpaqueTypeArgs<'tcx> { + AlreadyReported(ErrorGuaranteed), + NotAParam { opaque_type_key: OpaqueTypeKey<'tcx>, param_index: usize, span: Span }, + DuplicateParam { opaque_type_key: OpaqueTypeKey<'tcx>, param_indices: Vec, span: Span }, +} +impl From for InvalidOpaqueTypeArgs<'_> { + fn from(guar: ErrorGuaranteed) -> Self { + InvalidOpaqueTypeArgs::AlreadyReported(guar) + } +} +impl<'tcx> InvalidOpaqueTypeArgs<'tcx> { + pub fn report(self, infcx: &InferCtxt<'tcx>) -> ErrorGuaranteed { + let tcx = infcx.tcx; + match self { + InvalidOpaqueTypeArgs::AlreadyReported(guar) => guar, + InvalidOpaqueTypeArgs::NotAParam { opaque_type_key, param_index, span } => { + let opaque_generics = tcx.generics_of(opaque_type_key.def_id); + let opaque_param = opaque_generics.param_at(param_index, tcx); + let kind = opaque_param.kind.descr(); + infcx.dcx().emit_err(NonGenericOpaqueTypeParam { + arg: opaque_type_key.args[param_index], + kind, + span, + param_span: tcx.def_span(opaque_param.def_id), + }) + } + InvalidOpaqueTypeArgs::DuplicateParam { opaque_type_key, param_indices, span } => { + let opaque_generics = tcx.generics_of(opaque_type_key.def_id); + let descr = opaque_generics.param_at(param_indices[0], tcx).kind.descr(); + let spans: Vec<_> = param_indices + .into_iter() + .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) + .collect(); + infcx + .dcx() + .struct_span_err(span, "non-defining opaque type use in defining scope") + .with_span_note(spans, format!("{descr} used multiple times")) + .emit() + } + } + } +} + +/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter]. +/// +/// [rustc-dev-guide chapter]: +/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html +pub fn check_opaque_type_parameter_valid<'tcx>( + infcx: &InferCtxt<'tcx>, + opaque_type_key: OpaqueTypeKey<'tcx>, + span: Span, + defining_scope_kind: DefiningScopeKind, +) -> Result<(), InvalidOpaqueTypeArgs<'tcx>> { + let tcx = infcx.tcx; + let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id); + let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default(); + + // Avoid duplicate errors in case the opaque has already been malformed in + // HIR typeck. + if let DefiningScopeKind::MirBorrowck = defining_scope_kind { + infcx + .tcx + .type_of_opaque_hir_typeck(opaque_type_key.def_id) + .instantiate_identity() + .error_reported()?; + } + + for (i, arg) in opaque_type_key.iter_captured_args(tcx) { + let arg_is_param = match arg.unpack() { + GenericArgKind::Lifetime(lt) => match defining_scope_kind { + DefiningScopeKind::HirTypeck => continue, + DefiningScopeKind::MirBorrowck => { + matches!(lt.kind(), ty::ReEarlyParam(_) | ty::ReLateParam(_)) + || (lt.is_static() && opaque_env.param_equal_static(i)) + } + }, + GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), + GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)), + }; + + if arg_is_param { + // Register if the same lifetime appears multiple times in the generic args. + // There is an exception when the opaque type *requires* the lifetimes to be equal. + // See [rustc-dev-guide chapter] § "An exception to uniqueness rule". + let seen_where = seen_params.entry(arg).or_default(); + if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) { + seen_where.push(i); + } + } else { + // Prevent `fn foo() -> Foo` from being defining. + opaque_env.param_is_error(i)?; + return Err(InvalidOpaqueTypeArgs::NotAParam { opaque_type_key, param_index: i, span }); + } + } + + for (_, param_indices) in seen_params { + if param_indices.len() > 1 { + return Err(InvalidOpaqueTypeArgs::DuplicateParam { + opaque_type_key, + param_indices, + span, + }); + } + } + + Ok(()) +} + +/// Computes if an opaque type requires a lifetime parameter to be equal to +/// another one or to the `'static` lifetime. +/// These requirements are derived from the explicit and implied bounds. +struct LazyOpaqueTyEnv<'tcx> { + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + + /// Equal parameters will have the same name. Computed Lazily. + /// Example: + /// `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;` + /// Identity args: `['a, 'b, 'c]` + /// Canonical args: `['static, 'b, 'b]` + canonical_args: std::cell::OnceCell>, +} + +impl<'tcx> LazyOpaqueTyEnv<'tcx> { + fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { + Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() } + } + + fn param_equal_static(&self, param_index: usize) -> bool { + self.get_canonical_args()[param_index].expect_region().is_static() + } + + fn params_equal(&self, param1: usize, param2: usize) -> bool { + let canonical_args = self.get_canonical_args(); + canonical_args[param1] == canonical_args[param2] + } + + fn param_is_error(&self, param_index: usize) -> Result<(), ErrorGuaranteed> { + self.get_canonical_args()[param_index].error_reported() + } + + fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> { + if let Some(&canonical_args) = self.canonical_args.get() { + return canonical_args; + } + + let &Self { tcx, def_id, .. } = self; + let origin = tcx.local_opaque_ty_origin(def_id); + let parent = match origin { + OpaqueTyOrigin::FnReturn { parent, .. } + | OpaqueTyOrigin::AsyncFn { parent, .. } + | OpaqueTyOrigin::TyAlias { parent, .. } => parent, + }; + let param_env = tcx.param_env(parent); + let args = GenericArgs::identity_for_item(tcx, parent).extend_to( + tcx, + def_id.to_def_id(), + |param, _| { + tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into() + }, + ); + + // FIXME(#132279): It feels wrong to use `non_body_analysis` here given that we're + // in a body here. + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ocx = ObligationCtxt::new(&infcx); + + let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| { + tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds"); + Default::default() + }); + let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys); + + let mut seen = vec![tcx.lifetimes.re_static]; + let canonical_args = fold_regions(tcx, args, |r1, _| { + if r1.is_error() { + r1 + } else if let Some(&r2) = seen.iter().find(|&&r2| { + let free_regions = outlives_env.free_region_map(); + free_regions.sub_free_regions(tcx, r1, r2) + && free_regions.sub_free_regions(tcx, r2, r1) + }) { + r2 + } else { + seen.push(r1); + r1 + } + }); + self.canonical_args.set(canonical_args).unwrap(); + canonical_args + } +} diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 5517175461876..068e90b00b8d1 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -17,13 +17,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> { param_env: ty::ParamEnv<'tcx>, assumed_wf_tys: impl IntoIterator>, ) -> Self { - Self::new_with_implied_bounds_compat( - infcx, - body_id, - param_env, - assumed_wf_tys, - !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat, - ) + Self::new_with_implied_bounds_compat(infcx, body_id, param_env, assumed_wf_tys, false) } fn new_with_implied_bounds_compat( @@ -31,7 +25,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> { body_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, assumed_wf_tys: impl IntoIterator>, - implied_bounds_compat: bool, + disable_implied_bounds_hack: bool, ) -> Self { let mut bounds = vec![]; @@ -59,11 +53,11 @@ impl<'tcx> OutlivesEnvironment<'tcx> { OutlivesEnvironment::from_normalized_bounds( param_env, bounds, - infcx.implied_bounds_tys_with_compat( + infcx.implied_bounds_tys( body_id, param_env, assumed_wf_tys, - implied_bounds_compat, + disable_implied_bounds_hack, ), ) } diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs index d425ab50ae0fd..5a5d16167d28d 100644 --- a/compiler/rustc_trait_selection/src/solve.rs +++ b/compiler/rustc_trait_selection/src/solve.rs @@ -9,5 +9,8 @@ mod select; pub(crate) use delegate::SolverDelegate; pub use fulfill::{FulfillmentCtxt, NextSolverError}; pub(crate) use normalize::deeply_normalize_for_diagnostics; -pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes}; +pub use normalize::{ + deeply_normalize, deeply_normalize_with_skipped_universes, + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals, +}; pub use select::InferCtxtSelectExt; diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index e69bad095e336..3601c2cba9b55 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -8,11 +8,10 @@ use rustc_infer::infer::canonical::{ }; use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::solve::Goal; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt as _}; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::solve::Certainty; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, TypingMode}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; -use rustc_type_ir::TypingMode; -use rustc_type_ir::solve::{Certainty, NoSolution}; use crate::traits::{EvaluateConstErr, specialization_graph}; @@ -93,16 +92,16 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn well_formed_goals( &self, param_env: ty::ParamEnv<'tcx>, - arg: ty::GenericArg<'tcx>, + term: ty::Term<'tcx>, ) -> Option>>> { - crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID) - .map(|obligations| { - obligations.into_iter().map(|obligation| obligation.into()).collect() - }) - } - - fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { - self.0.clone_opaque_types_for_query_response() + crate::traits::wf::unnormalized_obligations( + &self.0, + param_env, + term, + DUMMY_SP, + CRATE_DEF_ID, + ) + .map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect()) } fn make_deduplicated_outlives_constraints( @@ -150,18 +149,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< self.0.instantiate_canonical_var(span, cv_info, universe_map) } - fn insert_hidden_type( - &self, - opaque_type_key: ty::OpaqueTypeKey<'tcx>, - param_env: ty::ParamEnv<'tcx>, - hidden_ty: Ty<'tcx>, - goals: &mut Vec>>, - ) -> Result<(), NoSolution> { - self.0 - .insert_hidden_type(opaque_type_key, DUMMY_SP, param_env, hidden_ty, goals) - .map_err(|_| NoSolution) - } - fn add_item_bounds_for_hidden_type( &self, def_id: DefId, @@ -173,19 +160,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals); } - fn inject_new_hidden_type_unchecked( - &self, - key: ty::OpaqueTypeKey<'tcx>, - hidden_ty: Ty<'tcx>, - span: Span, - ) { - self.0.inject_new_hidden_type_unchecked(key, ty::OpaqueHiddenType { ty: hidden_ty, span }) - } - - fn reset_opaque_types(&self) { - let _ = self.take_opaque_types(); - } - fn fetch_eligible_assoc_item( &self, goal_trait_ref: ty::TraitRef<'tcx>, @@ -205,6 +179,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< match self.typing_mode() { TypingMode::Coherence | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => false, TypingMode::PostAnalysis => { let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref); diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 704ba6e501d8c..3e1cdac84dfd1 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -1,18 +1,25 @@ use std::marker::PhantomData; use std::mem; +use std::ops::ControlFlow; use rustc_data_structures::thinvec::ExtractIf; +use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::{ FromSolverError, PredicateObligation, PredicateObligations, TraitEngine, }; +use rustc_middle::ty::{ + self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, TypingMode, +}; use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _}; +use rustc_span::Span; use tracing::instrument; use self::derive_errors::*; use super::Certainty; use super::delegate::SolverDelegate; +use super::inspect::{self, ProofTreeInferCtxtExt}; use crate::traits::{FulfillmentError, ScrubbedTraitError}; mod derive_errors; @@ -39,7 +46,7 @@ pub struct FulfillmentCtxt<'tcx, E: 'tcx> { _errors: PhantomData, } -#[derive(Default)] +#[derive(Default, Debug)] struct ObligationStorage<'tcx> { /// Obligations which resulted in an overflow in fulfillment itself. /// @@ -55,20 +62,23 @@ impl<'tcx> ObligationStorage<'tcx> { self.pending.push(obligation); } + fn has_pending_obligations(&self) -> bool { + !self.pending.is_empty() || !self.overflowed.is_empty() + } + fn clone_pending(&self) -> PredicateObligations<'tcx> { let mut obligations = self.pending.clone(); obligations.extend(self.overflowed.iter().cloned()); obligations } - fn take_pending(&mut self) -> PredicateObligations<'tcx> { - let mut obligations = mem::take(&mut self.pending); - obligations.append(&mut self.overflowed); - obligations - } - - fn unstalled_for_select(&mut self) -> impl Iterator> + 'tcx { - mem::take(&mut self.pending).into_iter() + fn drain_pending( + &mut self, + cond: impl Fn(&PredicateObligation<'tcx>) -> bool, + ) -> PredicateObligations<'tcx> { + let (unstalled, pending) = mem::take(&mut self.pending).into_iter().partition(cond); + self.pending = pending; + unstalled } fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) { @@ -80,7 +90,7 @@ impl<'tcx> ObligationStorage<'tcx> { // change. // FIXME: is merged, this can be removed. self.overflowed.extend(ExtractIf::new(&mut self.pending, |o| { - let goal = o.clone().into(); + let goal = o.as_goal(); let result = <&SolverDelegate<'tcx>>::from(infcx) .evaluate_root_goal(goal, GenerateProofTree::No, o.cause.span) .0; @@ -152,16 +162,16 @@ where fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); let mut errors = Vec::new(); - for i in 0.. { - if !infcx.tcx.recursion_limit().value_within_limit(i) { - self.obligations.on_fulfillment_overflow(infcx); - // Only return true errors that we have accumulated while processing. - return errors; - } - + loop { let mut has_changed = false; - for obligation in self.obligations.unstalled_for_select() { - let goal = obligation.clone().into(); + for mut obligation in self.obligations.drain_pending(|_| true) { + if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) { + self.obligations.on_fulfillment_overflow(infcx); + // Only return true errors that we have accumulated while processing. + return errors; + } + + let goal = obligation.as_goal(); let result = <&SolverDelegate<'tcx>>::from(infcx) .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span) .0; @@ -178,6 +188,13 @@ where }; if changed == HasChanged::Yes { + // We increment the recursion depth here to track the number of times + // this goal has resulted in inference progress. This doesn't precisely + // model the way that we track recursion depth in the old solver due + // to the fact that we only process root obligations, but it is a good + // approximation and should only result in fulfillment overflow in + // pathological cases. + obligation.recursion_depth += 1; has_changed = true; } @@ -196,15 +213,95 @@ where } fn has_pending_obligations(&self) -> bool { - !self.obligations.pending.is_empty() || !self.obligations.overflowed.is_empty() + self.obligations.has_pending_obligations() } fn pending_obligations(&self) -> PredicateObligations<'tcx> { self.obligations.clone_pending() } - fn drain_unstalled_obligations(&mut self, _: &InferCtxt<'tcx>) -> PredicateObligations<'tcx> { - self.obligations.take_pending() + fn drain_stalled_obligations_for_coroutines( + &mut self, + infcx: &InferCtxt<'tcx>, + ) -> PredicateObligations<'tcx> { + let stalled_generators = match infcx.typing_mode() { + TypingMode::Analysis { defining_opaque_types_and_generators } => { + defining_opaque_types_and_generators + } + TypingMode::Coherence + | TypingMode::Borrowck { defining_opaque_types: _ } + | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } + | TypingMode::PostAnalysis => return Default::default(), + }; + + if stalled_generators.is_empty() { + return Default::default(); + } + + self.obligations.drain_pending(|obl| { + infcx.probe(|_| { + infcx + .visit_proof_tree( + obl.as_goal(), + &mut StalledOnCoroutines { + stalled_generators, + span: obl.cause.span, + cache: Default::default(), + }, + ) + .is_break() + }) + }) + } +} + +/// Detect if a goal is stalled on a coroutine that is owned by the current typeck root. +/// +/// This function can (erroneously) fail to detect a predicate, i.e. it doesn't need to +/// be complete. However, this will lead to ambiguity errors, so we want to make it +/// accurate. +/// +/// This function can be also return false positives, which will lead to poor diagnostics +/// so we want to keep this visitor *precise* too. +struct StalledOnCoroutines<'tcx> { + stalled_generators: &'tcx ty::List, + span: Span, + cache: DelayedSet>, +} + +impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> { + type Result = ControlFlow<()>; + + fn span(&self) -> rustc_span::Span { + self.span + } + + fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'tcx>) -> Self::Result { + inspect_goal.goal().predicate.visit_with(self)?; + + if let Some(candidate) = inspect_goal.unique_applicable_candidate() { + candidate.visit_nested_no_probe(self) + } else { + ControlFlow::Continue(()) + } + } +} + +impl<'tcx> TypeVisitor> for StalledOnCoroutines<'tcx> { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if !self.cache.insert(ty) { + return ControlFlow::Continue(()); + } + + if let ty::CoroutineWitness(def_id, _) = *ty.kind() + && def_id.as_local().is_some_and(|def_id| self.stalled_generators.contains(&def_id)) + { + return ControlFlow::Break(()); + } + + ty.super_visit_with(self) } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 4f177df89e230..f64cd5ffebe3d 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -1,21 +1,22 @@ use std::ops::ControlFlow; +use rustc_hir::LangItem; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause}; use rustc_infer::traits::{ self, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError, }; +use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _}; -use rustc_type_ir::solve::{Goal, NoSolution}; use tracing::{instrument, trace}; -use crate::solve::Certainty; use crate::solve::delegate::SolverDelegate; use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor}; +use crate::solve::{Certainty, deeply_normalize_for_diagnostics}; use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf}; pub(super) fn fulfillment_error_for_no_solution<'tcx>( @@ -89,7 +90,7 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( let (code, refine_obligation) = infcx.probe(|_| { match <&SolverDelegate<'tcx>>::from(infcx) .evaluate_root_goal( - root_obligation.clone().into(), + root_obligation.as_goal(), GenerateProofTree::No, root_obligation.cause.span, ) @@ -98,7 +99,13 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => { (FulfillmentErrorCode::Ambiguity { overflow: None }, true) } - Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => ( + Ok(( + _, + Certainty::Maybe(MaybeCause::Overflow { + suggest_increasing_limit, + keep_constraints: _, + }), + )) => ( FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, // Don't look into overflows because we treat overflows weirdly anyways. // We discard the inference constraints from overflowing goals, so @@ -109,10 +116,16 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( false, ), Ok((_, Certainty::Yes)) => { - bug!("did not expect successful goal when collecting ambiguity errors") + bug!( + "did not expect successful goal when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) } Err(_) => { - bug!("did not expect selection error when collecting ambiguity errors") + bug!( + "did not expect selection error when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) } } }); @@ -151,17 +164,18 @@ fn find_best_leaf_obligation<'tcx>( // // We should probably fix the visitor to not do so instead, as this also // means the leaf obligation may be incorrect. - infcx + let obligation = infcx .fudge_inference_if_ok(|| { infcx .visit_proof_tree( - obligation.clone().into(), + obligation.as_goal(), &mut BestObligation { obligation: obligation.clone(), consider_ambiguities }, ) .break_value() .ok_or(()) }) - .unwrap_or(obligation) + .unwrap_or(obligation); + deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation) } struct BestObligation<'tcx> { @@ -233,19 +247,19 @@ impl<'tcx> BestObligation<'tcx> { fn visit_well_formed_goal( &mut self, candidate: &inspect::InspectCandidate<'_, 'tcx>, - arg: ty::GenericArg<'tcx>, + term: ty::Term<'tcx>, ) -> ControlFlow> { let infcx = candidate.goal().infcx(); let param_env = candidate.goal().goal().param_env; let body_id = self.obligation.cause.body_id; - for obligation in wf::unnormalized_obligations(infcx, param_env, arg, self.span(), body_id) + for obligation in wf::unnormalized_obligations(infcx, param_env, term, self.span(), body_id) .into_iter() .flatten() { let nested_goal = candidate.instantiate_proof_tree_for_nested_goal( GoalSource::Misc, - Goal::new(infcx.tcx, obligation.param_env, obligation.predicate), + obligation.as_goal(), self.span(), ); // Skip nested goals that aren't the *reason* for our goal's failure. @@ -291,6 +305,40 @@ impl<'tcx> BestObligation<'tcx> { } } + /// When a higher-ranked projection goal fails, check that the corresponding + /// higher-ranked trait goal holds or not. This is because the process of + /// instantiating and then re-canonicalizing the binder of the projection goal + /// forces us to be unable to see that the leak check failed in the nested + /// `NormalizesTo` goal, so we don't fall back to the rigid projection check + /// that should catch when a projection goal fails due to an unsatisfied trait + /// goal. + fn detect_trait_error_in_higher_ranked_projection( + &mut self, + goal: &inspect::InspectGoal<'_, 'tcx>, + ) -> ControlFlow> { + let tcx = goal.infcx().tcx; + if let Some(projection_clause) = goal.goal().predicate.as_projection_clause() + && !projection_clause.bound_vars().is_empty() + { + let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(tcx)); + let obligation = Obligation::new( + tcx, + self.obligation.cause.clone(), + goal.goal().param_env, + deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred), + ); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(tcx, pred), + goal.depth() + 1, + this, + ) + }) + } else { + ControlFlow::Continue(()) + } + } + /// It is likely that `NormalizesTo` failed without any applicable candidates /// because the alias is not well-formed. /// @@ -360,7 +408,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} _ => return ControlFlow::Continue(()), } - let pred_kind = goal.goal().predicate.kind(); + + let pred = goal.goal().predicate; let candidates = self.non_trivial_candidates(goal); let candidate = match candidates.as_slice() { @@ -374,7 +423,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { source: CandidateSource::Impl(impl_def_id), result: _, } = candidate.kind() - && goal.infcx().tcx.do_not_recommend_impl(impl_def_id) + && tcx.do_not_recommend_impl(impl_def_id) { trace!("#[do_not_recommend] -> exit"); return ControlFlow::Break(self.obligation.clone()); @@ -382,12 +431,12 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { // FIXME: Also, what about considering >1 layer up the stack? May be necessary // for normalizes-to. - let child_mode = match pred_kind.skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { - ChildMode::Trait(pred_kind.rebind(pred)) + let child_mode = match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => { + ChildMode::Trait(pred.kind().rebind(trait_pred)) } - ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => { - ChildMode::Host(pred_kind.rebind(pred)) + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(host_pred)) => { + ChildMode::Host(pred.kind().rebind(host_pred)) } ty::PredicateKind::NormalizesTo(normalizes_to) if matches!( @@ -395,13 +444,13 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst ) => { - ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate { + ChildMode::Trait(pred.kind().rebind(ty::TraitPredicate { trait_ref: normalizes_to.alias.trait_ref(tcx), polarity: ty::PredicatePolarity::Positive, })) } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - return self.visit_well_formed_goal(candidate, arg); + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + return self.visit_well_formed_goal(candidate, term); } _ => ChildMode::PassThrough, }; @@ -416,9 +465,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { // We do this as a separate loop so that we do not choose to tell the user about some nested // goal before we encounter a `T: FnPtr` nested goal. for nested_goal in &nested_goals { - if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait() - && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() - && poly_trait_pred.def_id() == fn_ptr_trait + if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() + && tcx.is_lang_item(poly_trait_pred.def_id(), LangItem::FnPtrTrait) && let Err(NoSolution) = nested_goal.result() { return ControlFlow::Break(self.obligation.clone()); @@ -429,10 +477,12 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { for nested_goal in nested_goals { trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result())); + let nested_pred = nested_goal.goal().predicate; + let make_obligation = |cause| Obligation { cause, param_env: nested_goal.goal().param_env, - predicate: nested_goal.goal().predicate, + predicate: nested_pred, recursion_depth: self.obligation.recursion_depth + 1, }; @@ -440,7 +490,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { match (child_mode, nested_goal.source()) { ( ChildMode::Trait(_) | ChildMode::Host(_), - GoalSource::Misc | GoalSource::NormalizeGoal(_), + GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_), ) => { continue; } @@ -482,30 +532,21 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { // alias-relate may fail because the lhs or rhs can't be normalized, // and therefore is treated as rigid. - if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() { - if let Some(obligation) = goal - .infcx() - .visit_proof_tree_at_depth( - goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())), - goal.depth() + 1, - self, - ) - .break_value() - { - return ControlFlow::Break(obligation); - } else if let Some(obligation) = goal - .infcx() - .visit_proof_tree_at_depth( - goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())), - goal.depth() + 1, - self, - ) - .break_value() - { - return ControlFlow::Break(obligation); - } + if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(tcx, ty::ClauseKind::WellFormed(lhs.into())), + goal.depth() + 1, + self, + )?; + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(tcx, ty::ClauseKind::WellFormed(rhs.into())), + goal.depth() + 1, + self, + )?; } + self.detect_trait_error_in_higher_ranked_projection(goal)?; + ControlFlow::Break(self.obligation.clone()) } } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 4b1bc316d5f1d..9795655e84222 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -15,8 +15,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_macros::extension; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult}; -use rustc_middle::ty::visit::{VisitorResult, try_visit}; -use rustc_middle::ty::{TyCtxt, TypeFoldable}; +use rustc_middle::ty::{TyCtxt, TypeFoldable, VisitorResult, try_visit}; use rustc_middle::{bug, ty}; use rustc_next_trait_solver::resolve::EagerResolver; use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state}; @@ -293,7 +292,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { inspect::ProbeStep::NestedProbe(ref probe) => { match probe.kind { // These never assemble candidates for the goal we're trying to solve. - inspect::ProbeKind::UpcastProjectionCompatibility + inspect::ProbeKind::ProjectionCompatibility | inspect::ProbeKind::ShadowedEnvProbing => continue, inspect::ProbeKind::NormalizedSelfTyAssembly @@ -315,8 +314,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { } match probe.kind { - inspect::ProbeKind::UpcastProjectionCompatibility - | inspect::ProbeKind::ShadowedEnvProbing => bug!(), + inspect::ProbeKind::ProjectionCompatibility + | inspect::ProbeKind::ShadowedEnvProbing => { + bug!() + } inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly => {} @@ -381,7 +382,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { if let Some(term_hack) = normalizes_to_term_hack { infcx .probe(|_| term_hack.constrain(infcx, DUMMY_SP, uncanonicalized_goal.param_env)) - .map(|certainty| ok.value.certainty.unify_with(certainty)) + .map(|certainty| ok.value.certainty.and(certainty)) } else { Ok(ok.value.certainty) } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 232357dc71a0d..d903f94b489d3 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -1,10 +1,10 @@ use std::assert_matches::assert_matches; use std::fmt::Debug; -use std::marker::PhantomData; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::InferCtxt; use rustc_infer::infer::at::At; +use rustc_infer::traits::solve::Goal; use rustc_infer::traits::{FromSolverError, Obligation, TraitEngine}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{ @@ -45,11 +45,44 @@ where T: TypeFoldable>, E: FromSolverError<'tcx, NextSolverError<'tcx>>, { - let fulfill_cx = FulfillmentCtxt::new(at.infcx); - let mut folder = - NormalizationFolder { at, fulfill_cx, depth: 0, universes, _errors: PhantomData }; + let (value, coroutine_goals) = + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + at, value, universes, + )?; + assert_eq!(coroutine_goals, vec![]); - value.try_fold_with(&mut folder) + Ok(value) +} + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// its input to be already fully resolved. +/// +/// Additionally takes a list of universes which represents the binders which have been +/// entered before passing `value` to the function. This is currently needed for +/// `normalize_erasing_regions`, which skips binders as it walks through a type. +/// +/// This returns a set of stalled obligations involving coroutines if the typing mode of +/// the underlying infcx has any stalled coroutine def ids. +pub fn deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals<'tcx, T, E>( + at: At<'_, 'tcx>, + value: T, + universes: Vec>, +) -> Result<(T, Vec>>), Vec> +where + T: TypeFoldable>, + E: FromSolverError<'tcx, NextSolverError<'tcx>>, +{ + let fulfill_cx = FulfillmentCtxt::new(at.infcx); + let mut folder = NormalizationFolder { + at, + fulfill_cx, + depth: 0, + universes, + stalled_coroutine_goals: vec![], + }; + let value = value.try_fold_with(&mut folder)?; + let errors = folder.fulfill_cx.select_all_or_error(at.infcx); + if errors.is_empty() { Ok((value, folder.stalled_coroutine_goals)) } else { Err(errors) } } struct NormalizationFolder<'me, 'tcx, E> { @@ -57,7 +90,7 @@ struct NormalizationFolder<'me, 'tcx, E> { fulfill_cx: FulfillmentCtxt<'tcx, E>, depth: usize, universes: Vec>, - _errors: PhantomData, + stalled_coroutine_goals: Vec>>, } impl<'tcx, E> NormalizationFolder<'_, 'tcx, E> @@ -98,10 +131,7 @@ where ); self.fulfill_cx.register_predicate_obligation(infcx, obligation); - let errors = self.fulfill_cx.select_all_or_error(infcx); - if !errors.is_empty() { - return Err(errors); - } + self.select_all_and_stall_coroutine_predicates()?; // Alias is guaranteed to be fully structurally resolved, // so we can super fold here. @@ -139,7 +169,7 @@ where let result = if infcx.predicate_may_hold(&obligation) { self.fulfill_cx.register_predicate_obligation(infcx, obligation); - let errors = self.fulfill_cx.select_all_or_error(infcx); + let errors = self.fulfill_cx.select_where_possible(infcx); if !errors.is_empty() { return Err(errors); } @@ -152,6 +182,27 @@ where self.depth -= 1; Ok(result) } + + fn select_all_and_stall_coroutine_predicates(&mut self) -> Result<(), Vec> { + let errors = self.fulfill_cx.select_where_possible(self.at.infcx); + if !errors.is_empty() { + return Err(errors); + } + + self.stalled_coroutine_goals.extend( + self.fulfill_cx + .drain_stalled_obligations_for_coroutines(self.at.infcx) + .into_iter() + .map(|obl| obl.as_goal()), + ); + + let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx); + if !errors.is_empty() { + return Err(errors); + } + + Ok(()) + } } impl<'tcx, E> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx, E> @@ -253,20 +304,32 @@ impl<'tcx> TypeFolder> for DeeplyNormalizeForDiagnosticsFolder<'_, } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - deeply_normalize_with_skipped_universes( - self.at, - ty, - vec![None; ty.outer_exclusive_binder().as_usize()], - ) - .unwrap_or_else(|_: Vec>| ty.super_fold_with(self)) + let infcx = self.at.infcx; + let result: Result<_, Vec>> = infcx.commit_if_ok(|_| { + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + self.at, + ty, + vec![None; ty.outer_exclusive_binder().as_usize()], + ) + }); + match result { + Ok((ty, _)) => ty, + Err(_) => ty.super_fold_with(self), + } } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - deeply_normalize_with_skipped_universes( - self.at, - ct, - vec![None; ct.outer_exclusive_binder().as_usize()], - ) - .unwrap_or_else(|_: Vec>| ct.super_fold_with(self)) + let infcx = self.at.infcx; + let result: Result<_, Vec>> = infcx.commit_if_ok(|_| { + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + self.at, + ct, + vec![None; ct.outer_exclusive_binder().as_usize()], + ) + }); + match result { + Ok((ct, _)) => ct, + Err(_) => ct.super_fold_with(self), + } } } diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs index 4437fc5b0295f..4fdaf740287ba 100644 --- a/compiler/rustc_trait_selection/src/solve/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -177,7 +177,7 @@ fn to_selection<'tcx>( }, ProbeKind::NormalizedSelfTyAssembly | ProbeKind::UnsizeAssembly - | ProbeKind::UpcastProjectionCompatibility + | ProbeKind::ProjectionCompatibility | ProbeKind::OpaqueTypeStorageLookup { result: _ } | ProbeKind::Root { result: _ } | ProbeKind::ShadowedEnvProbing diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 1fca2f4da7eee..02521c9453d98 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -382,7 +382,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { for (new_region, old_region) in iter::zip(new_args.regions(), old_args.regions()) { - match (*new_region, *old_region) { + match (new_region.kind(), old_region.kind()) { // If both predicates have an `ReBound` (a HRTB) in the // same spot, we do nothing. (ty::ReBound(_, _), ty::ReBound(_, _)) => {} diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 3b40882ef572d..93c7dae9c5be6 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -17,8 +17,9 @@ use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal}; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::DeepRejectCtxt; -use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode}; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, +}; pub use rustc_next_trait_solver::coherence::*; use rustc_next_trait_solver::solve::SolverDelegateEvalExt; use rustc_span::{DUMMY_SP, Span, sym}; @@ -536,7 +537,7 @@ fn plug_infer_with_placeholders<'tcx>( } fn visit_region(&mut self, r: ty::Region<'tcx>) { - if let ty::ReVar(vid) = *r { + if let ty::ReVar(vid) = r.kind() { let r = self .infcx .inner @@ -624,7 +625,7 @@ fn compute_intercrate_ambiguity_causes<'tcx>( let mut causes: FxIndexSet> = Default::default(); for obligation in obligations { - search_ambiguity_causes(infcx, obligation.clone().into(), &mut causes); + search_ambiguity_causes(infcx, obligation.as_goal(), &mut causes); } causes diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index bdee6ca2afe54..39333082acdff 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -2,7 +2,7 @@ //! //! For concrete constants, this is fairly simple as we can just try and evaluate it. //! -//! When dealing with polymorphic constants, for example `std::mem::size_of::() - 1`, +//! When dealing with polymorphic constants, for example `size_of::() - 1`, //! this is not as easy. //! //! In this case we try to build an abstract representation of this constant using @@ -85,6 +85,12 @@ pub fn is_const_evaluatable<'tcx>( } _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"), } + } else if tcx.features().min_generic_const_args() { + // This is a sanity check to make sure that non-generics consts are checked to + // be evaluatable in case they aren't cchecked elsewhere. This will NOT error + // if the const uses generics, as desired. + crate::traits::evaluate_const(infcx, unexpanded_ct, param_env); + Ok(()) } else { let uv = match unexpanded_ct.kind() { ty::ConstKind::Unevaluated(uv) => uv, diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index d4502be6ccfee..220a847cc230f 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -9,14 +9,13 @@ use std::ops::ControlFlow; use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{ self, EarlyBinder, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast, + elaborate, }; use rustc_span::Span; -use rustc_type_ir::elaborate; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -24,7 +23,9 @@ use super::elaborate; use crate::infer::TyCtxtInferExt; pub use crate::traits::DynCompatibilityViolation; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{MethodViolationCode, Obligation, ObligationCause, util}; +use crate::traits::{ + MethodViolationCode, Obligation, ObligationCause, normalize_param_env_or_error, util, +}; /// Returns the dyn-compatibility violations that affect HIR ty lowering. /// @@ -187,7 +188,7 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span tcx.associated_items(trait_def_id) .in_definition_order() // We're only looking at associated type bounds - .filter(|item| item.kind == ty::AssocKind::Type) + .filter(|item| item.is_type()) // Ignore GATs with `Self: Sized` .filter(|item| !tcx.generics_require_sized_self(item.def_id)) .flat_map(|item| tcx.explicit_item_bounds(item.def_id).iter_identity_copied()) @@ -297,31 +298,33 @@ pub fn dyn_compatibility_violations_for_assoc_item( match item.kind { // Associated consts are never dyn-compatible, as they can't have `where` bounds yet at all, // and associated const bounds in trait objects aren't a thing yet either. - ty::AssocKind::Const => { - vec![DynCompatibilityViolation::AssocConst(item.name, item.ident(tcx).span)] + ty::AssocKind::Const { name } => { + vec![DynCompatibilityViolation::AssocConst(name, item.ident(tcx).span)] } - ty::AssocKind::Fn => virtual_call_violations_for_method(tcx, trait_def_id, item) - .into_iter() - .map(|v| { - let node = tcx.hir_get_if_local(item.def_id); - // Get an accurate span depending on the violation. - let span = match (&v, node) { - (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span, - (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span, - (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span, - (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { - node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span()) - } - _ => item.ident(tcx).span, - }; + ty::AssocKind::Fn { name, .. } => { + virtual_call_violations_for_method(tcx, trait_def_id, item) + .into_iter() + .map(|v| { + let node = tcx.hir_get_if_local(item.def_id); + // Get an accurate span depending on the violation. + let span = match (&v, node) { + (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span, + (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span, + (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span, + (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { + node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span()) + } + _ => item.ident(tcx).span, + }; - DynCompatibilityViolation::Method(item.name, v, span) - }) - .collect(), + DynCompatibilityViolation::Method(name, v, span) + }) + .collect() + } // Associated types can only be dyn-compatible if they have `Self: Sized` bounds. - ty::AssocKind::Type => { + ty::AssocKind::Type { .. } => { if !tcx.generics_of(item.def_id).is_own_empty() && !item.is_impl_trait_in_trait() { - vec![DynCompatibilityViolation::GAT(item.name, item.ident(tcx).span)] + vec![DynCompatibilityViolation::GAT(item.name(), item.ident(tcx).span)] } else { // We will permit associated types if they are explicitly mentioned in the trait object. // We can't check this here, as here we only check if it is guaranteed to not be possible. @@ -343,7 +346,7 @@ fn virtual_call_violations_for_method<'tcx>( let sig = tcx.fn_sig(method.def_id).instantiate_identity(); // The method's first parameter must be named `self` - if !method.fn_has_self_parameter { + if !method.is_method() { let sugg = if let Some(hir::Node::TraitItem(hir::TraitItem { generics, kind: hir::TraitItemKind::Fn(sig, _), @@ -579,29 +582,41 @@ fn receiver_is_dispatchable<'tcx>( let unsized_receiver_ty = receiver_for_self_ty(tcx, receiver_ty, unsized_self_ty, method.def_id); - // create a modified param env, with `Self: Unsize` and `U: Trait` added to caller bounds - // `U: ?Sized` is already implied here + // create a modified param env, with `Self: Unsize` and `U: Trait` (and all of + // its supertraits) added to caller bounds. `U: ?Sized` is already implied here. let param_env = { - let param_env = tcx.param_env(method.def_id); + // N.B. We generally want to emulate the construction of the `unnormalized_param_env` + // in the param-env query here. The fact that we don't just start with the clauses + // in the param-env of the method is because those are already normalized, and mixing + // normalized and unnormalized copies of predicates in `normalize_param_env_or_error` + // will cause ambiguity that the user can't really avoid. + // + // We leave out certain complexities of the param-env query here. Specifically, we: + // 1. Do not add `~const` bounds since there are no `dyn const Trait`s. + // 2. Do not add RPITIT self projection bounds for defaulted methods, since we + // are not constructing a param-env for "inside" of the body of the defaulted + // method, so we don't really care about projecting to a specific RPIT type, + // and because RPITITs are not dyn compatible (yet). + let mut predicates = tcx.predicates_of(method.def_id).instantiate_identity(tcx).predicates; // Self: Unsize let unsize_predicate = - ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty]).upcast(tcx); + ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty]); + predicates.push(unsize_predicate.upcast(tcx)); // U: Trait - let trait_predicate = { - let trait_def_id = method.trait_container(tcx).unwrap(); - let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| { - if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) } - }); - - ty::TraitRef::new_from_args(tcx, trait_def_id, args).upcast(tcx) - }; - - let caller_bounds = - param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]); - - ty::ParamEnv::new(tcx.mk_clauses_from_iter(caller_bounds)) + let trait_def_id = method.trait_container(tcx).unwrap(); + let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| { + if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) } + }); + let trait_predicate = ty::TraitRef::new_from_args(tcx, trait_def_id, args); + predicates.push(trait_predicate.upcast(tcx)); + + normalize_param_env_or_error( + tcx, + ty::ParamEnv::new(tcx.mk_clauses(&predicates)), + ObligationCause::dummy_with_span(tcx.def_span(method.def_id)), + ) }; // Receiver: DispatchFromDyn U]> @@ -784,7 +799,7 @@ impl<'tcx> TypeFolder> for EraseEscapingBoundRegions<'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - if let ty::ReBound(debruijn, _) = *r + if let ty::ReBound(debruijn, _) = r.kind() && debruijn < self.binder { r @@ -802,31 +817,8 @@ fn contains_illegal_impl_trait_in_trait<'tcx>( let ty = tcx.liberate_late_bound_regions(fn_def_id, ty); if tcx.asyncness(fn_def_id).is_async() { - // FIXME(async_fn_in_dyn_trait): Think of a better way to unify these code paths - // to issue an appropriate feature suggestion when users try to use AFIDT. - // Obviously we must only do this once AFIDT is finished enough to actually be usable. - if tcx.features().async_fn_in_dyn_trait() { - let ty::Alias(ty::Projection, proj) = *ty.kind() else { - bug!("expected async fn in trait to return an RPITIT"); - }; - assert!(tcx.is_impl_trait_in_trait(proj.def_id)); - - // FIXME(async_fn_in_dyn_trait): We should check that this bound is legal too, - // and stop relying on `async fn` in the definition. - for bound in tcx.item_bounds(proj.def_id).instantiate(tcx, proj.args) { - if let Some(violation) = bound - .visit_with(&mut IllegalRpititVisitor { tcx, allowed: Some(proj) }) - .break_value() - { - return Some(violation); - } - } - - None - } else { - // Rendering the error as a separate `async-specific` message is better. - Some(MethodViolationCode::AsyncFn) - } + // Rendering the error as a separate `async-specific` message is better. + Some(MethodViolationCode::AsyncFn) } else { ty.visit_with(&mut IllegalRpititVisitor { tcx, allowed: None }).break_value() } diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 3c127416cbf7c..cc5861b5a1f59 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -4,10 +4,10 @@ use rustc_infer::traits::{ ImplDerivedHostCause, ImplSource, Obligation, ObligationCauseCode, PredicateObligation, }; use rustc_middle::span_bug; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::elaborate::elaborate; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{self, TypingMode}; -use rustc_type_ir::elaborate::elaborate; -use rustc_type_ir::solve::NoSolution; use thin_vec::{ThinVec, thin_vec}; use super::SelectionContext; @@ -106,10 +106,6 @@ fn match_candidate<'tcx>( more_nested(selcx, &mut nested); - for nested in &mut nested { - nested.set_depth_from_parent(obligation.recursion_depth); - } - Ok(nested) } @@ -256,6 +252,9 @@ fn evaluate_host_effect_for_destruct_goal<'tcx>( let self_ty = obligation.predicate.self_ty(); let const_conditions = match *self_ty.kind() { + // `ManuallyDrop` is trivially `~const Destruct` as we do not run any drop glue on it. + ty::Adt(adt_def, _) if adt_def.is_manually_drop() => thin_vec![], + // An ADT is `~const Destruct` only if all of the fields are, // *and* if there is a `Drop` impl, that `Drop` impl is also `~const`. ty::Adt(adt_def, args) => { @@ -263,7 +262,7 @@ fn evaluate_host_effect_for_destruct_goal<'tcx>( .all_fields() .map(|field| ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args)])) .collect(); - match adt_def.destructor(tcx).map(|dtor| dtor.constness) { + match adt_def.destructor(tcx).map(|dtor| tcx.constness(dtor.did)) { // `Drop` impl exists, but it's not const. Type cannot be `~const Destruct`. Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution), // `Drop` impl exists, and it's const. Require `Ty: ~const Drop` to hold. @@ -378,10 +377,6 @@ fn evaluate_host_effect_from_selection_candiate<'tcx>( }), ); - for nested in &mut nested { - nested.set_depth_from_parent(obligation.recursion_depth); - } - Ok(nested) } _ => Err(EvaluationFailure::NoSolution), diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 9f3178f887927..18f28d72f6f8f 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -14,8 +14,8 @@ use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::relate::Relate; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast, Variance}; -use rustc_type_ir::relate::Relate; use super::{FromSolverError, FulfillmentContext, ScrubbedTraitError, TraitEngine}; use crate::error_reporting::InferCtxtErrorExt; @@ -188,6 +188,20 @@ where .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) } + /// Computes the least-upper-bound, or mutual supertype, of two values. + pub fn lub>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + expected: T, + actual: T, + ) -> Result> { + self.infcx + .at(cause, param_env) + .lub(expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + #[must_use] pub fn select_where_possible(&self) -> Vec { self.engine.borrow_mut().select_where_possible(self.infcx) diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index e39f8e673dbac..34c3c905bd977 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -24,10 +24,10 @@ use super::{ }; use crate::error_reporting::InferCtxtErrorExt; use crate::infer::{InferCtxt, TyOrConstInferVar}; -use crate::traits::EvaluateConstErr; use crate::traits::normalize::normalize_with_depth_to; use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _}; use crate::traits::query::evaluate_obligation::InferCtxtExt; +use crate::traits::{EvaluateConstErr, sizedness_fast_path}; pub(crate) type PendingPredicateObligations<'tcx> = ThinVec>; @@ -162,7 +162,7 @@ where self.select(selcx) } - fn drain_unstalled_obligations( + fn drain_stalled_obligations_for_coroutines( &mut self, infcx: &InferCtxt<'tcx>, ) -> PredicateObligations<'tcx> { @@ -225,9 +225,15 @@ struct FulfillProcessor<'a, 'tcx> { selcx: SelectionContext<'a, 'tcx>, } -fn mk_pending<'tcx>(os: PredicateObligations<'tcx>) -> PendingPredicateObligations<'tcx> { +fn mk_pending<'tcx>( + parent: &PredicateObligation<'tcx>, + os: PredicateObligations<'tcx>, +) -> PendingPredicateObligations<'tcx> { os.into_iter() - .map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] }) + .map(|mut o| { + o.set_depth_from_parent(parent.recursion_depth); + PendingPredicateObligation { obligation: o, stalled_on: vec![] } + }) .collect() } @@ -329,6 +335,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let infcx = self.selcx.infcx; + if sizedness_fast_path(infcx.tcx, obligation.predicate) { + return ProcessResult::Changed(thin_vec::thin_vec![]); + } + if obligation.predicate.has_aliases() { let mut obligations = PredicateObligations::new(); let predicate = normalize_with_depth_to( @@ -341,7 +351,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ); if predicate != obligation.predicate { obligations.push(obligation.with(infcx.tcx, predicate)); - return ProcessResult::Changed(mk_pending(obligations)); + return ProcessResult::Changed(mk_pending(obligation, obligations)); } } let binder = obligation.predicate.kind(); @@ -385,7 +395,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let mut obligations = PredicateObligations::with_capacity(1); obligations.push(obligation.with(infcx.tcx, pred)); - ProcessResult::Changed(mk_pending(obligations)) + ProcessResult::Changed(mk_pending(obligation, obligations)) } ty::PredicateKind::Ambiguous => ProcessResult::Unchanged, ty::PredicateKind::NormalizesTo(..) => { @@ -410,6 +420,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let host_obligation = obligation.with(infcx.tcx, data); self.process_host_obligation( + obligation, host_obligation, &mut pending_obligation.stalled_on, ) @@ -486,7 +497,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { // `>::Output` when this is an `Expr` representing // `lhs + rhs`. ty::ConstKind::Expr(_) => { - return ProcessResult::Changed(mk_pending(PredicateObligations::new())); + return ProcessResult::Changed(mk_pending( + obligation, + PredicateObligations::new(), + )); } ty::ConstKind::Placeholder(_) => { bug!("placeholder const {:?} in old solver", ct) @@ -503,7 +517,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ct_ty, ty, ) { - Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())), + Ok(inf_ok) => ProcessResult::Changed(mk_pending( + obligation, + inf_ok.into_obligations(), + )), Err(_) => ProcessResult::Error(FulfillmentErrorCode::Select( SelectionError::ConstArgHasWrongType { ct, ct_ty, expected_ty: ty }, )), @@ -523,21 +540,21 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { self.selcx.infcx.err_ctxt().report_overflow_obligation(&obligation, false); } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { match wf::obligations( self.selcx.infcx, obligation.param_env, obligation.cause.body_id, obligation.recursion_depth + 1, - arg, + term, obligation.cause.span, ) { None => { pending_obligation.stalled_on = - vec![TyOrConstInferVar::maybe_from_generic_arg(arg).unwrap()]; + vec![TyOrConstInferVar::maybe_from_term(term).unwrap()]; ProcessResult::Unchanged } - Some(os) => ProcessResult::Changed(mk_pending(os)), + Some(os) => ProcessResult::Changed(mk_pending(obligation, os)), } } @@ -553,11 +570,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)]; ProcessResult::Unchanged } - Ok(Ok(mut ok)) => { - for subobligation in &mut ok.obligations { - subobligation.set_depth_from_parent(obligation.recursion_depth); - } - ProcessResult::Changed(mk_pending(ok.obligations)) + Ok(Ok(ok)) => { + ProcessResult::Changed(mk_pending(obligation, ok.obligations)) } Ok(Err(err)) => { let expected_found = if subtype.a_is_expected { @@ -582,7 +596,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)]; ProcessResult::Unchanged } - Ok(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), + Ok(Ok(ok)) => { + ProcessResult::Changed(mk_pending(obligation, ok.obligations)) + } Ok(Err(err)) => { let expected_found = ExpectedFound::new(coerce.b, coerce.a); ProcessResult::Error(FulfillmentErrorCode::Subtype(expected_found, err)) @@ -645,6 +661,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ) { return ProcessResult::Changed(mk_pending( + obligation, new_obligations.into_obligations(), )); } @@ -659,6 +676,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { .eq(DefineOpaqueTypes::Yes, c1, c2) { return ProcessResult::Changed(mk_pending( + obligation, new_obligations.into_obligations(), )); } @@ -704,9 +722,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { c1, c2, ) { - Ok(inf_ok) => { - ProcessResult::Changed(mk_pending(inf_ok.into_obligations())) - } + Ok(inf_ok) => ProcessResult::Changed(mk_pending( + obligation, + inf_ok.into_obligations(), + )), Err(err) => { ProcessResult::Error(FulfillmentErrorCode::ConstEquate( ExpectedFound::new(c1, c2), @@ -790,7 +809,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { match self.selcx.poly_select(&trait_obligation) { Ok(Some(impl_source)) => { debug!("selecting trait at depth {} yielded Ok(Some)", obligation.recursion_depth); - ProcessResult::Changed(mk_pending(impl_source.nested_obligations())) + ProcessResult::Changed(mk_pending(obligation, impl_source.nested_obligations())) } Ok(None) => { debug!("selecting trait at depth {} yielded Ok(None)", obligation.recursion_depth); @@ -854,7 +873,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { } match project::poly_project_and_unify_term(&mut self.selcx, &project_obligation) { - ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(os)), + ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(obligation, os)), ProjectAndUnifyResult::FailedNormalization => { stalled_on.clear(); stalled_on.extend(args_infer_vars( @@ -868,7 +887,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { let mut obligations = PredicateObligations::with_capacity(1); obligations.push(project_obligation.with(tcx, project_obligation.predicate)); - ProcessResult::Changed(mk_pending(obligations)) + ProcessResult::Changed(mk_pending(obligation, obligations)) } ProjectAndUnifyResult::MismatchedProjectionTypes(e) => { ProcessResult::Error(FulfillmentErrorCode::Project(e)) @@ -878,11 +897,12 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { fn process_host_obligation( &mut self, + obligation: &PredicateObligation<'tcx>, host_obligation: HostEffectObligation<'tcx>, stalled_on: &mut Vec, ) -> ProcessResult, FulfillmentErrorCode<'tcx>> { match effects::evaluate_host_effect_obligation(&mut self.selcx, &host_obligation) { - Ok(nested) => ProcessResult::Changed(mk_pending(nested)), + Ok(nested) => ProcessResult::Changed(mk_pending(obligation, nested)), Err(effects::EvaluationFailure::Ambiguous) => { stalled_on.clear(); stalled_on.extend(args_infer_vars( diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 105cb9175717d..f73603c00539d 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -32,11 +32,9 @@ pub use rustc_infer::traits::*; use rustc_middle::query::Providers; use rustc_middle::span_bug; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, - TypeSuperVisitable, TypingMode, Upcast, + self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypingMode, Upcast, }; use rustc_span::def_id::DefId; use rustc_span::{DUMMY_SP, Span}; @@ -53,7 +51,7 @@ pub use self::dyn_compatibility::{ pub use self::engine::{ObligationCtxt, TraitEngineExt}; pub use self::fulfill::{FulfillmentContext, OldSolverError, PendingPredicateObligation}; pub use self::normalize::NormalizeExt; -pub use self::project::{normalize_inherent_projection, normalize_projection_ty}; +pub use self::project::{normalize_inherent_projection, normalize_projection_term}; pub use self::select::{ EvaluationCache, EvaluationResult, IntercrateAmbiguityCause, OverflowError, SelectionCache, SelectionContext, @@ -67,8 +65,8 @@ pub use self::specialize::{ pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::{ BoundVarReplacer, PlaceholderReplacer, elaborate, expand_trait_aliases, impl_item_is_final, - supertrait_def_ids, supertraits, transitive_bounds_that_define_assoc_item, upcast_choices, - with_replaced_escaping_bound_vars, + sizedness_fast_path, supertrait_def_ids, supertraits, transitive_bounds_that_define_assoc_item, + upcast_choices, with_replaced_escaping_bound_vars, }; use crate::error_reporting::InferCtxtErrorExt; use crate::infer::outlives::env::OutlivesEnvironment; @@ -342,7 +340,7 @@ pub fn normalize_param_env_or_error<'tcx>( let mut predicates: Vec<_> = util::elaborate( tcx, unnormalized_env.caller_bounds().into_iter().map(|predicate| { - if tcx.features().generic_const_exprs() { + if tcx.features().generic_const_exprs() || tcx.next_trait_solver_globally() { return predicate; } @@ -407,8 +405,6 @@ pub fn normalize_param_env_or_error<'tcx>( // compatibility. Eventually when lazy norm is implemented this can just be removed. // We do not normalize types here as there is no backwards compatibility requirement // for us to do so. - // - // FIXME(-Znext-solver): remove this hack since we have deferred projection equality predicate.fold_with(&mut ConstNormalizer(tcx)) }), ) @@ -645,7 +641,7 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( ) -> GenericArgsRef<'tcx> { struct ReplaceParamAndInferWithPlaceholder<'tcx> { tcx: TyCtxt<'tcx>, - idx: u32, + idx: ty::BoundVar, } impl<'tcx> TypeFolder> for ReplaceParamAndInferWithPlaceholder<'tcx> { @@ -655,19 +651,13 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { if let ty::Infer(_) = t.kind() { - let idx = { - let idx = self.idx; - self.idx += 1; - idx - }; + let idx = self.idx; + self.idx += 1; Ty::new_placeholder( self.tcx, ty::PlaceholderType { universe: ty::UniverseIndex::ROOT, - bound: ty::BoundTy { - var: ty::BoundVar::from_u32(idx), - kind: ty::BoundTyKind::Anon, - }, + bound: ty::BoundTy { var: idx, kind: ty::BoundTyKind::Anon }, }, ) } else { @@ -677,16 +667,11 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { if let ty::ConstKind::Infer(_) = c.kind() { + let idx = self.idx; + self.idx += 1; ty::Const::new_placeholder( self.tcx, - ty::PlaceholderConst { - universe: ty::UniverseIndex::ROOT, - bound: ty::BoundVar::from_u32({ - let idx = self.idx; - self.idx += 1; - idx - }), - }, + ty::PlaceholderConst { universe: ty::UniverseIndex::ROOT, bound: idx }, ) } else { c.super_fold_with(self) @@ -694,7 +679,7 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( } } - args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 }) + args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: ty::BoundVar::ZERO }) } /// Normalizes the predicates and checks whether they hold in an empty environment. If this @@ -703,24 +688,26 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( /// used during analysis. pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec>) -> bool { debug!("impossible_predicates(predicates={:?})", predicates); - let (infcx, param_env) = - tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized()); + let (infcx, param_env) = tcx + .infer_ctxt() + .with_next_trait_solver(true) + .build_with_typing_env(ty::TypingEnv::fully_monomorphized()); + let ocx = ObligationCtxt::new(&infcx); let predicates = ocx.normalize(&ObligationCause::dummy(), param_env, predicates); for predicate in predicates { let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate); ocx.register_obligation(obligation); } - let errors = ocx.select_all_or_error(); - - if !errors.is_empty() { - return true; - } - // Leak check for any higher-ranked trait mismatches. - // We only need to do this in the old solver, since the new solver already - // leak-checks. - if !infcx.next_trait_solver() && infcx.leak_check(ty::UniverseIndex::ROOT, None).is_err() { + // Use `select_where_possible` to only return impossible for true errors, + // and not ambiguities or overflows. Since the new trait solver forces + // some currently undetected overlap between `dyn Trait: Trait` built-in + // vs user-written impls to AMBIGUOUS, this may return ambiguity even + // with no infer vars. There may also be ways to encounter ambiguity due + // to post-mono overflow. + let true_errors = ocx.select_where_possible(); + if !true_errors.is_empty() { return true; } diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index ad62b456ad461..eb6d5c8a60a25 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -1,6 +1,7 @@ //! Deeply normalize types using the old trait solver. use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_hir::def::DefKind; use rustc_infer::infer::at::At; use rustc_infer::infer::{InferCtxt, InferOk}; use rustc_infer::traits::{ @@ -10,15 +11,12 @@ use rustc_macros::extension; use rustc_middle::span_bug; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt, - TypingMode, + self, AliasTerm, Term, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, + TypeVisitableExt, TypingMode, }; use tracing::{debug, instrument}; -use super::{ - BoundVarReplacer, PlaceholderReplacer, SelectionContext, project, - with_replaced_escaping_bound_vars, -}; +use super::{BoundVarReplacer, PlaceholderReplacer, SelectionContext, project}; use crate::error_reporting::InferCtxtErrorExt; use crate::error_reporting::traits::OverflowCause; use crate::solve::NextSolverError; @@ -77,7 +75,15 @@ impl<'tcx> At<'_, 'tcx> { .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); let errors = fulfill_cx.select_all_or_error(self.infcx); let value = self.infcx.resolve_vars_if_possible(value); - if errors.is_empty() { Ok(value) } else { Err(errors) } + if errors.is_empty() { + Ok(value) + } else { + // Drop pending obligations, since deep normalization may happen + // in a loop and we don't want to trigger the assertion on the next + // iteration due to pending ambiguous obligations we've left over. + let _ = fulfill_cx.collect_remaining_errors(self.infcx); + Err(errors) + } } } } @@ -130,6 +136,7 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable>>( // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis TypingMode::Coherence | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(ty::TypeFlags::HAS_TY_OPAQUE), TypingMode::PostAnalysis => {} } @@ -169,6 +176,172 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { if !needs_normalization(self.selcx.infcx, &value) { value } else { value.fold_with(self) } } + + // FIXME(mgca): While this supports constants, it is only used for types by default right now + #[instrument(level = "debug", skip(self), ret)] + fn normalize_trait_projection(&mut self, proj: AliasTerm<'tcx>) -> Term<'tcx> { + if !proj.has_escaping_bound_vars() { + // When we don't have escaping bound vars we can normalize ambig aliases + // to inference variables (done in `normalize_projection_ty`). This would + // be wrong if there were escaping bound vars as even if we instantiated + // the bound vars with placeholders, we wouldn't be able to map them back + // after normalization succeeded. + // + // Also, as an optimization: when we don't have escaping bound vars, we don't + // need to replace them with placeholders (see branch below). + let proj = proj.fold_with(self); + project::normalize_projection_term( + self.selcx, + self.param_env, + proj, + self.cause.clone(), + self.depth, + self.obligations, + ) + } else { + // If there are escaping bound vars, we temporarily replace the + // bound vars with placeholders. Note though, that in the case + // that we still can't project for whatever reason (e.g. self + // type isn't known enough), we *can't* register an obligation + // and return an inference variable (since then that obligation + // would have bound vars and that's a can of worms). Instead, + // we just give up and fall back to pretending like we never tried! + // + // Note: this isn't necessarily the final approach here; we may + // want to figure out how to register obligations with escaping vars + // or handle this some other way. + let infcx = self.selcx.infcx; + let (proj, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, proj); + let proj = proj.fold_with(self); + let normalized_term = project::opt_normalize_projection_term( + self.selcx, + self.param_env, + proj, + self.cause.clone(), + self.depth, + self.obligations, + ) + .ok() + .flatten() + .unwrap_or(proj.to_term(infcx.tcx)); + + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + normalized_term, + ) + } + } + + // FIXME(mgca): While this supports constants, it is only used for types by default right now + #[instrument(level = "debug", skip(self), ret)] + fn normalize_inherent_projection(&mut self, inherent: AliasTerm<'tcx>) -> Term<'tcx> { + if !inherent.has_escaping_bound_vars() { + // When we don't have escaping bound vars we can normalize ambig aliases + // to inference variables (done in `normalize_projection_ty`). This would + // be wrong if there were escaping bound vars as even if we instantiated + // the bound vars with placeholders, we wouldn't be able to map them back + // after normalization succeeded. + // + // Also, as an optimization: when we don't have escaping bound vars, we don't + // need to replace them with placeholders (see branch below). + + let inherent = inherent.fold_with(self); + project::normalize_inherent_projection( + self.selcx, + self.param_env, + inherent, + self.cause.clone(), + self.depth, + self.obligations, + ) + } else { + let infcx = self.selcx.infcx; + let (inherent, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, inherent); + let inherent = inherent.fold_with(self); + let inherent = project::normalize_inherent_projection( + self.selcx, + self.param_env, + inherent, + self.cause.clone(), + self.depth, + self.obligations, + ); + + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + inherent, + ) + } + } + + // FIXME(mgca): While this supports constants, it is only used for types by default right now + #[instrument(level = "debug", skip(self), ret)] + fn normalize_free_alias(&mut self, free: AliasTerm<'tcx>) -> Term<'tcx> { + let recursion_limit = self.cx().recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.selcx.infcx.err_ctxt().report_overflow_error( + OverflowCause::DeeplyNormalize(free.into()), + self.cause.span, + false, + |diag| { + diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow); + }, + ); + } + + // We don't replace bound vars in the generic arguments of the free alias with + // placeholders. This doesn't cause any issues as instantiating parameters with + // bound variables is special-cased to rewrite the debruijn index to be higher + // whenever we fold through a binder. + // + // However, we do replace any escaping bound vars in the resulting goals with + // placeholders as the trait solver does not expect to encounter escaping bound + // vars in obligations. + // + // FIXME(lazy_type_alias): Check how much this actually matters for perf before + // stabilization. This is a bit weird and generally not how we handle binders in + // the compiler so ideally we'd do the same boundvar->placeholder->boundvar dance + // that other kinds of normalization do. + let infcx = self.selcx.infcx; + self.obligations.extend( + infcx.tcx.predicates_of(free.def_id).instantiate_own(infcx.tcx, free.args).map( + |(mut predicate, span)| { + if free.has_escaping_bound_vars() { + (predicate, ..) = BoundVarReplacer::replace_bound_vars( + infcx, + &mut self.universes, + predicate, + ); + } + let mut cause = self.cause.clone(); + cause.map_code(|code| ObligationCauseCode::TypeAlias(code, span, free.def_id)); + Obligation::new(infcx.tcx, cause, self.param_env, predicate) + }, + ), + ); + self.depth += 1; + let res = if free.kind(infcx.tcx).is_type() { + infcx.tcx.type_of(free.def_id).instantiate(infcx.tcx, free.args).fold_with(self).into() + } else { + // FIXME(mgca): once const items are actual aliases defined as equal to type system consts + // this should instead use that rather than evaluating. + super::evaluate_const(infcx, free.to_term(infcx.tcx).expect_const(), self.param_env) + .super_fold_with(self) + .into() + }; + self.depth -= 1; + res + } } impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx> { @@ -226,6 +399,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis TypingMode::Coherence | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => ty.super_fold_with(self), TypingMode::PostAnalysis => { let recursion_limit = self.cx().recursion_limit(); @@ -249,183 +423,63 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx } } - ty::Projection if !data.has_escaping_bound_vars() => { - // This branch is *mostly* just an optimization: when we don't - // have escaping bound vars, we don't need to replace them with - // placeholders (see branch below). *Also*, we know that we can - // register an obligation to *later* project, since we know - // there won't be bound vars there. - let data = data.fold_with(self); - let normalized_ty = project::normalize_projection_ty( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ); - debug!( - ?self.depth, - ?ty, - ?normalized_ty, - obligations.len = ?self.obligations.len(), - "AssocTypeNormalizer: normalized type" - ); - normalized_ty.expect_type() - } - - ty::Projection => { - // If there are escaping bound vars, we temporarily replace the - // bound vars with placeholders. Note though, that in the case - // that we still can't project for whatever reason (e.g. self - // type isn't known enough), we *can't* register an obligation - // and return an inference variable (since then that obligation - // would have bound vars and that's a can of worms). Instead, - // we just give up and fall back to pretending like we never tried! - // - // Note: this isn't necessarily the final approach here; we may - // want to figure out how to register obligations with escaping vars - // or handle this some other way. - - let infcx = self.selcx.infcx; - let (data, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); - let data = data.fold_with(self); - let normalized_ty = project::opt_normalize_projection_term( - self.selcx, - self.param_env, - data.into(), - self.cause.clone(), - self.depth, - self.obligations, - ) - .ok() - .flatten() - .map(|term| term.expect_type()) - .map(|normalized_ty| { - PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - &self.universes, - normalized_ty, - ) - }) - .unwrap_or_else(|| ty.super_fold_with(self)); - - debug!( - ?self.depth, - ?ty, - ?normalized_ty, - obligations.len = ?self.obligations.len(), - "AssocTypeNormalizer: normalized type" - ); - normalized_ty - } - ty::Weak => { - let recursion_limit = self.cx().recursion_limit(); - if !recursion_limit.value_within_limit(self.depth) { - self.selcx.infcx.err_ctxt().report_overflow_error( - OverflowCause::DeeplyNormalize(data.into()), - self.cause.span, - false, - |diag| { - diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow); - }, - ); - } - - let infcx = self.selcx.infcx; - self.obligations.extend( - infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map( - |(mut predicate, span)| { - if data.has_escaping_bound_vars() { - (predicate, ..) = BoundVarReplacer::replace_bound_vars( - infcx, - &mut self.universes, - predicate, - ); - } - let mut cause = self.cause.clone(); - cause.map_code(|code| { - ObligationCauseCode::TypeAlias(code, span, data.def_id) - }); - Obligation::new(infcx.tcx, cause, self.param_env, predicate) - }, - ), - ); - self.depth += 1; - let res = infcx - .tcx - .type_of(data.def_id) - .instantiate(infcx.tcx, data.args) - .fold_with(self); - self.depth -= 1; - res - } - - ty::Inherent if !data.has_escaping_bound_vars() => { - // This branch is *mostly* just an optimization: when we don't - // have escaping bound vars, we don't need to replace them with - // placeholders (see branch below). *Also*, we know that we can - // register an obligation to *later* project, since we know - // there won't be bound vars there. - - let data = data.fold_with(self); - - project::normalize_inherent_projection( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - } - - ty::Inherent => { - let infcx = self.selcx.infcx; - let (data, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); - let data = data.fold_with(self); - let ty = project::normalize_inherent_projection( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ); - - PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - &self.universes, - ty, - ) - } + ty::Projection => self.normalize_trait_projection(data.into()).expect_type(), + ty::Inherent => self.normalize_inherent_projection(data.into()).expect_type(), + ty::Free => self.normalize_free_alias(data.into()).expect_type(), } } #[instrument(skip(self), level = "debug")] - fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { let tcx = self.selcx.tcx(); - if tcx.features().generic_const_exprs() || !needs_normalization(self.selcx.infcx, &constant) - { - constant + if tcx.features().generic_const_exprs() || !needs_normalization(self.selcx.infcx, &ct) { + return ct; + } + + // Doing "proper" normalization of const aliases is inherently cyclic until const items + // are real aliases instead of having bodies. We gate proper const alias handling behind + // mgca to avoid breaking stable code, though this should become the "main" codepath long + // before mgca is stabilized. + // + // FIXME(BoxyUwU): Enabling this by default is blocked on a refactoring to how const items + // are represented. + if tcx.features().min_generic_const_args() { + let uv = match ct.kind() { + ty::ConstKind::Unevaluated(uv) => uv, + _ => return ct.super_fold_with(self), + }; + + let ct = match tcx.def_kind(uv.def) { + DefKind::AssocConst => match tcx.def_kind(tcx.parent(uv.def)) { + DefKind::Trait => self.normalize_trait_projection(uv.into()), + DefKind::Impl { of_trait: false } => { + self.normalize_inherent_projection(uv.into()) + } + kind => unreachable!( + "unexpected `DefKind` for const alias' resolution's parent def: {:?}", + kind + ), + }, + DefKind::Const | DefKind::AnonConst => self.normalize_free_alias(uv.into()), + kind => { + unreachable!("unexpected `DefKind` for const alias to resolve to: {:?}", kind) + } + }; + + // We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be + // unnormalized after const evaluation returns. + ct.expect_const().super_fold_with(self) } else { - let constant = constant.super_fold_with(self); - debug!(?constant, ?self.param_env); - with_replaced_escaping_bound_vars( + let ct = ct.super_fold_with(self); + return super::with_replaced_escaping_bound_vars( self.selcx.infcx, &mut self.universes, - constant, - |constant| super::evaluate_const(self.selcx.infcx, constant, self.param_env), + ct, + |ct| super::evaluate_const(self.selcx.infcx, ct, self.param_env), ) - .super_fold_with(self) + .super_fold_with(self); + // We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be + // unnormalized after const evaluation returns. } } diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 189326958078d..68983ef80fa48 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -36,7 +36,7 @@ fn implied_outlives_bounds<'a, 'tcx>( param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, ty: Ty<'tcx>, - compat: bool, + disable_implied_bounds_hack: bool, ) -> Vec> { let ty = infcx.resolve_vars_if_possible(ty); let ty = OpportunisticRegionResolver::new(infcx).fold_ty(ty); @@ -52,11 +52,8 @@ fn implied_outlives_bounds<'a, 'tcx>( let mut canonical_var_values = OriginalQueryValues::default(); let input = ImpliedOutlivesBounds { ty }; let canonical = infcx.canonicalize_query(param_env.and(input), &mut canonical_var_values); - let implied_bounds_result = if compat { - infcx.tcx.implied_outlives_bounds_compat(canonical) - } else { - infcx.tcx.implied_outlives_bounds(canonical) - }; + let implied_bounds_result = + infcx.tcx.implied_outlives_bounds((canonical, disable_implied_bounds_hack)); let Ok(canonical_result) = implied_bounds_result else { return vec![]; }; @@ -110,14 +107,15 @@ fn implied_outlives_bounds<'a, 'tcx>( impl<'tcx> InferCtxt<'tcx> { /// Do *NOT* call this directly. You probably want to construct a `OutlivesEnvironment` /// instead if you're interested in the implied bounds for a given signature. - fn implied_bounds_tys_with_compat>>( + fn implied_bounds_tys>>( &self, body_id: LocalDefId, param_env: ParamEnv<'tcx>, tys: Tys, - compat: bool, + disable_implied_bounds_hack: bool, ) -> impl Iterator> { - tys.into_iter() - .flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, ty, compat)) + tys.into_iter().flat_map(move |ty| { + implied_outlives_bounds(self, param_env, body_id, ty, disable_implied_bounds_hack) + }) } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index de5aaa5bd9729..ca58da5ca6d55 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -5,21 +5,18 @@ use std::ops::ControlFlow; use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; -use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::resolve::OpportunisticRegionResolver; -use rustc_infer::infer::{DefineOpaqueTypes, RegionVariableOrigin}; use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, Term, Ty, TyCtxt, TypingMode, Upcast}; +use rustc_middle::ty::{ + self, Term, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, Upcast, +}; use rustc_middle::{bug, span_bug}; use rustc_span::sym; -use rustc_type_ir::elaborate; -use thin_vec::thin_vec; use tracing::{debug, instrument}; use super::{ @@ -63,9 +60,6 @@ enum ProjectionCandidate<'tcx> { /// Bounds specified on an object type Object(ty::PolyProjectionPredicate<'tcx>), - /// Built-in bound for a dyn async fn in trait - ObjectRpitit, - /// From an "impl" (or a "pseudo-impl" returned by select) Select(Selection<'tcx>), } @@ -177,6 +171,7 @@ pub(super) enum ProjectAndUnifyResult<'tcx> { /// ``` /// If successful, this may result in additional obligations. Also returns /// the projection cache key used to track these additional obligations. +// FIXME(mgca): While this supports constants, it is only used for types by default right now #[instrument(level = "debug", skip(selcx))] pub(super) fn poly_project_and_unify_term<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, @@ -206,6 +201,7 @@ pub(super) fn poly_project_and_unify_term<'cx, 'tcx>( /// If successful, this may result in additional obligations. /// /// See [poly_project_and_unify_term] for an explanation of the return value. +// FIXME(mgca): While this supports constants, it is only used for types by default right now #[instrument(level = "debug", skip(selcx))] fn project_and_unify_term<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, @@ -263,34 +259,28 @@ fn project_and_unify_term<'cx, 'tcx>( /// there are unresolved type variables in the projection, we will /// instantiate it with a fresh type variable `$X` and generate a new /// obligation `::Item == $X` for later. -pub fn normalize_projection_ty<'a, 'b, 'tcx>( +// FIXME(mgca): While this supports constants, it is only used for types by default right now +pub fn normalize_projection_term<'a, 'b, 'tcx>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::AliasTy<'tcx>, + alias_term: ty::AliasTerm<'tcx>, cause: ObligationCause<'tcx>, depth: usize, obligations: &mut PredicateObligations<'tcx>, ) -> Term<'tcx> { - opt_normalize_projection_term( - selcx, - param_env, - projection_ty.into(), - cause.clone(), - 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 - // information is available. - - selcx - .infcx - .projection_ty_to_infer(param_env, projection_ty, cause, depth + 1, obligations) - .into() - }) + opt_normalize_projection_term(selcx, param_env, alias_term, cause.clone(), 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 + // information is available. + + selcx + .infcx + .projection_term_to_infer(param_env, alias_term, cause, depth + 1, obligations) + .into() + }) } /// The guts of `normalize`: normalize a specific projection like `( /// often immediately appended to another obligations vector. So now this /// function takes an obligations vector and appends to it directly, which is /// slightly uglier but avoids the need for an extra short-lived allocation. +// FIXME(mgca): While this supports constants, it is only used for types by default right now #[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>( selcx: &'a mut SelectionContext<'b, 'tcx>, @@ -461,6 +452,7 @@ pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>( /// an error for this obligation, but we legitimately should not, /// because it contains `[type error]`. Yuck! (See issue #29857 for /// one case where this arose.) +// FIXME(mgca): While this supports constants, it is only used for types by default right now fn normalize_to_error<'a, 'tcx>( selcx: &SelectionContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -473,10 +465,11 @@ fn normalize_to_error<'a, 'tcx>( ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::InherentTy | ty::AliasTermKind::OpaqueTy - | ty::AliasTermKind::WeakTy => selcx.infcx.next_ty_var(cause.span).into(), - ty::AliasTermKind::UnevaluatedConst | ty::AliasTermKind::ProjectionConst => { - selcx.infcx.next_const_var(cause.span).into() - } + | ty::AliasTermKind::FreeTy => selcx.infcx.next_ty_var(cause.span).into(), + ty::AliasTermKind::FreeConst + | ty::AliasTermKind::InherentConst + | ty::AliasTermKind::UnevaluatedConst + | ty::AliasTermKind::ProjectionConst => selcx.infcx.next_const_var(cause.span).into(), }; let mut obligations = PredicateObligations::new(); obligations.push(Obligation { @@ -489,36 +482,37 @@ fn normalize_to_error<'a, 'tcx>( } /// Confirm and normalize the given inherent projection. +// FIXME(mgca): While this supports constants, it is only used for types by default right now #[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] pub fn normalize_inherent_projection<'a, 'b, 'tcx>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, - alias_ty: ty::AliasTy<'tcx>, + alias_term: ty::AliasTerm<'tcx>, cause: ObligationCause<'tcx>, depth: usize, obligations: &mut PredicateObligations<'tcx>, -) -> Ty<'tcx> { +) -> ty::Term<'tcx> { let tcx = selcx.tcx(); if !tcx.recursion_limit().value_within_limit(depth) { // Halt compilation because it is important that overflows never be masked. tcx.dcx().emit_fatal(InherentProjectionNormalizationOverflow { span: cause.span, - ty: alias_ty.to_string(), + ty: alias_term.to_string(), }); } - let args = compute_inherent_assoc_ty_args( + let args = compute_inherent_assoc_term_args( selcx, param_env, - alias_ty, + alias_term, cause.clone(), depth, obligations, ); // Register the obligations arising from the impl and from the associated type itself. - let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, args); + let predicates = tcx.predicates_of(alias_term.def_id).instantiate(tcx, args); for (predicate, span) in predicates { let predicate = normalize_with_depth_to( selcx, @@ -536,7 +530,7 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>( // cause code, inherent projections will be printed with identity instantiation in // diagnostics which is not ideal. // Consider creating separate cause codes for this specific situation. - ObligationCauseCode::WhereClause(alias_ty.def_id, span), + ObligationCauseCode::WhereClause(alias_term.def_id, span), ); obligations.push(Obligation::with_depth( @@ -548,27 +542,33 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>( )); } - let ty = tcx.type_of(alias_ty.def_id).instantiate(tcx, args); + let term: Term<'tcx> = if alias_term.kind(tcx).is_type() { + tcx.type_of(alias_term.def_id).instantiate(tcx, args).into() + } else { + get_associated_const_value(selcx, alias_term.to_term(tcx).expect_const(), param_env).into() + }; - let mut ty = selcx.infcx.resolve_vars_if_possible(ty); - if ty.has_aliases() { - ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations); + let mut term = selcx.infcx.resolve_vars_if_possible(term); + if term.has_aliases() { + term = + normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, term, obligations); } - ty + term } -pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>( +// FIXME(mgca): While this supports constants, it is only used for types by default right now +pub fn compute_inherent_assoc_term_args<'a, 'b, 'tcx>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, - alias_ty: ty::AliasTy<'tcx>, + alias_term: ty::AliasTerm<'tcx>, cause: ObligationCause<'tcx>, depth: usize, obligations: &mut PredicateObligations<'tcx>, ) -> ty::GenericArgsRef<'tcx> { let tcx = selcx.tcx(); - let impl_def_id = tcx.parent(alias_ty.def_id); + let impl_def_id = tcx.parent(alias_term.def_id); let impl_args = selcx.infcx.fresh_args_for_item(cause.span, impl_def_id); let mut impl_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args); @@ -585,7 +585,7 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>( // Infer the generic parameters of the impl by unifying the // impl type with the self type of the projection. - let mut self_ty = alias_ty.self_ty(); + let mut self_ty = alias_term.self_ty(); if !selcx.infcx.next_trait_solver() { self_ty = normalize_with_depth_to( selcx, @@ -607,7 +607,7 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>( } } - alias_ty.rebase_inherent_args_onto_impl(impl_args, tcx) + alias_term.rebase_inherent_args_onto_impl(impl_args, tcx) } enum Projected<'tcx> { @@ -635,6 +635,7 @@ impl<'tcx> Progress<'tcx> { /// /// IMPORTANT: /// - `obligation` must be fully normalized +// FIXME(mgca): While this supports constants, it is only used for types by default right now #[instrument(level = "info", skip(selcx))] fn project<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, @@ -674,30 +675,11 @@ fn project<'cx, 'tcx>( match candidates { ProjectionCandidateSet::Single(candidate) => { - Ok(Projected::Progress(confirm_candidate(selcx, obligation, candidate))) + confirm_candidate(selcx, obligation, candidate) } ProjectionCandidateSet::None => { let tcx = selcx.tcx(); - let term = match tcx.def_kind(obligation.predicate.def_id) { - DefKind::AssocTy => Ty::new_projection_from_args( - tcx, - obligation.predicate.def_id, - obligation.predicate.args, - ) - .into(), - DefKind::AssocConst => ty::Const::new_unevaluated( - tcx, - ty::UnevaluatedConst::new( - obligation.predicate.def_id, - obligation.predicate.args, - ), - ) - .into(), - kind => { - bug!("unknown projection def-id: {}", kind.descr(obligation.predicate.def_id)) - } - }; - + let term = obligation.predicate.to_term(tcx); Ok(Projected::NoProgress(term)) } // Error occurred while trying to processing impls. @@ -743,7 +725,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( ) { debug!("assemble_candidates_from_trait_def(..)"); let mut ambiguous = false; - selcx.for_each_item_bound( + let _ = selcx.for_each_item_bound( obligation.predicate.self_ty(), |selcx, clause, _| { let Some(clause) = clause.as_projection_clause() else { @@ -832,16 +814,6 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( env_predicates, false, ); - - // `dyn Trait` automagically project their AFITs to `dyn* Future`. - if tcx.is_impl_trait_in_trait(obligation.predicate.def_id) - && let Some(out_trait_def_id) = data.principal_def_id() - && let rpitit_trait_def_id = tcx.parent(obligation.predicate.def_id) - && elaborate::supertrait_def_ids(tcx, out_trait_def_id) - .any(|trait_def_id| trait_def_id == rpitit_trait_def_id) - { - candidate_set.push_candidate(ProjectionCandidate::ObjectRpitit); - } } #[instrument( @@ -930,7 +902,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( ImplSource::UserDefined(impl_data) => { // We have to be careful when projecting out of an // impl because of specialization. If we are not in - // codegen (i.e., projection mode is not "any"), and the + // codegen (i.e., `TypingMode` is not `PostAnalysis`), and the // impl's type is declared as default, then we disable // projection (even if the trait ref is fully // monomorphic). In the case where trait ref is not @@ -967,6 +939,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( match selcx.infcx.typing_mode() { TypingMode::Coherence | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => { debug!( assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id), @@ -998,36 +971,38 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let tcx = selcx.tcx(); - let lang_items = selcx.tcx().lang_items(); - if [ - lang_items.coroutine_trait(), - lang_items.future_trait(), - lang_items.iterator_trait(), - lang_items.async_iterator_trait(), - lang_items.fn_trait(), - lang_items.fn_mut_trait(), - lang_items.fn_once_trait(), - lang_items.async_fn_trait(), - lang_items.async_fn_mut_trait(), - lang_items.async_fn_once_trait(), - ] - .contains(&Some(trait_ref.def_id)) - { - true - } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncFnKindHelper) { - // FIXME(async_closures): Validity constraints here could be cleaned up. - if obligation.predicate.args.type_at(0).is_ty_var() - || obligation.predicate.args.type_at(4).is_ty_var() - || obligation.predicate.args.type_at(5).is_ty_var() - { - candidate_set.mark_ambiguous(); - true - } else { - obligation.predicate.args.type_at(0).to_opt_closure_kind().is_some() - && obligation.predicate.args.type_at(1).to_opt_closure_kind().is_some() + match selcx.tcx().as_lang_item(trait_ref.def_id) { + Some( + LangItem::Coroutine + | LangItem::Future + | LangItem::Iterator + | LangItem::AsyncIterator + | LangItem::Fn + | LangItem::FnMut + | LangItem::FnOnce + | LangItem::AsyncFn + | LangItem::AsyncFnMut + | LangItem::AsyncFnOnce, + ) => true, + Some(LangItem::AsyncFnKindHelper) => { + // FIXME(async_closures): Validity constraints here could be cleaned up. + if obligation.predicate.args.type_at(0).is_ty_var() + || obligation.predicate.args.type_at(4).is_ty_var() + || obligation.predicate.args.type_at(5).is_ty_var() + { + candidate_set.mark_ambiguous(); + true + } else { + obligation.predicate.args.type_at(0).to_opt_closure_kind().is_some() + && obligation + .predicate + .args + .type_at(1) + .to_opt_closure_kind() + .is_some() + } } - } else if tcx.is_lang_item(trait_ref.def_id, LangItem::DiscriminantKind) { - match self_ty.kind() { + Some(LangItem::DiscriminantKind) => match self_ty.kind() { ty::Bool | ty::Char | ty::Int(_) @@ -1064,138 +1039,104 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Placeholder(..) | ty::Infer(..) | ty::Error(_) => false, - } - } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncDestruct) { - match self_ty.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Adt(..) - | ty::Str - | ty::Array(..) - | ty::Slice(_) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::UnsafeBinder(_) - | ty::Dynamic(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Pat(..) - | ty::Never - | ty::Tuple(..) - | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, - - // type parameters, opaques, and unnormalized projections don't have - // a known async destructor and may need to be normalized further or rely - // on param env for async destructor projections - ty::Param(_) - | ty::Foreign(_) - | ty::Alias(..) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Infer(_) - | ty::Error(_) => false, - } - } else if tcx.is_lang_item(trait_ref.def_id, LangItem::PointeeTrait) { - let tail = selcx.tcx().struct_tail_raw( - self_ty, - |ty| { - // We throw away any obligations we get from this, since we normalize - // and confirm these obligations once again during confirmation - normalize_with_depth( - selcx, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - ty, - ) - .value - }, - || {}, - ); + }, + Some(LangItem::PointeeTrait) => { + let tail = selcx.tcx().struct_tail_raw( + self_ty, + |ty| { + // We throw away any obligations we get from this, since we normalize + // and confirm these obligations once again during confirmation + normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + ty, + ) + .value + }, + || {}, + ); - match tail.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Array(..) - | ty::Pat(..) - | ty::Slice(_) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Dynamic(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Never - // Extern types have unit metadata, according to RFC 2850 - | ty::Foreign(_) - // If returned by `struct_tail` this is a unit struct - // without any fields, or not a struct, and therefore is Sized. - | ty::Adt(..) - // If returned by `struct_tail` this is the empty tuple. - | ty::Tuple(..) - // Integers and floats are always Sized, and so have unit type metadata. - | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) - // This happens if we reach the recursion limit when finding the struct tail. - | ty::Error(..) => true, - - // We normalize from `Wrapper::Metadata` to `Tail::Metadata` if able. - // Otherwise, type parameters, opaques, and unnormalized projections have - // unit metadata if they're known (e.g. by the param_env) to be sized. - ty::Param(_) | ty::Alias(..) - if self_ty != tail - || selcx.infcx.predicate_must_hold_modulo_regions( - &obligation.with( - selcx.tcx(), - ty::TraitRef::new( + match tail.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Array(..) + | ty::Pat(..) + | ty::Slice(_) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + // Extern types have unit metadata, according to RFC 2850 + | ty::Foreign(_) + // If returned by `struct_tail` this is a unit struct + // without any fields, or not a struct, and therefore is Sized. + | ty::Adt(..) + // If returned by `struct_tail` this is the empty tuple. + | ty::Tuple(..) + // Integers and floats are always Sized, and so have unit type metadata. + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) + // This happens if we reach the recursion limit when finding the struct tail. + | ty::Error(..) => true, + + // We normalize from `Wrapper::Metadata` to `Tail::Metadata` if able. + // Otherwise, type parameters, opaques, and unnormalized projections have + // unit metadata if they're known (e.g. by the param_env) to be sized. + ty::Param(_) | ty::Alias(..) + if self_ty != tail + || selcx.infcx.predicate_must_hold_modulo_regions( + &obligation.with( selcx.tcx(), - selcx.tcx().require_lang_item( - LangItem::Sized, - Some(obligation.cause.span), + ty::TraitRef::new( + selcx.tcx(), + selcx.tcx().require_lang_item( + LangItem::Sized, + Some(obligation.cause.span), + ), + [self_ty], ), - [self_ty], ), - ), - ) => - { - true - } + ) => + { + true + } - ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), + ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), - // FIXME(compiler-errors): are Bound and Placeholder types ever known sized? - ty::Param(_) - | ty::Alias(..) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Infer(..) => { - if tail.has_infer_types() { - candidate_set.mark_ambiguous(); + // FIXME(compiler-errors): are Bound and Placeholder types ever known sized? + ty::Param(_) + | ty::Alias(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) => { + if tail.has_infer_types() { + candidate_set.mark_ambiguous(); + } + false } - false } } - } else if tcx.trait_is_auto(trait_ref.def_id) { - tcx.dcx().span_delayed_bug( - tcx.def_span(obligation.predicate.def_id), - "associated types not allowed on auto traits", - ); - false - } else { - bug!("unexpected builtin trait with associated type: {trait_ref:?}") + _ if tcx.trait_is_auto(trait_ref.def_id) => { + tcx.dcx().span_delayed_bug( + tcx.def_span(obligation.predicate.def_id), + "associated types not allowed on auto traits", + ); + false + } + _ => { + bug!("unexpected builtin trait with associated type: {trait_ref:?}") + } } } ImplSource::Param(..) => { @@ -1232,8 +1173,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // why we special case object types. false } - ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) - | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { + ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) => { // These traits have no associated types. selcx.tcx().dcx().span_delayed_bug( obligation.cause.span, @@ -1255,27 +1195,24 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( }); } +// FIXME(mgca): While this supports constants, it is only used for types by default right now fn confirm_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTermObligation<'tcx>, candidate: ProjectionCandidate<'tcx>, -) -> Progress<'tcx> { +) -> Result, ProjectionError<'tcx>> { debug!(?obligation, ?candidate, "confirm_candidate"); - let mut progress = match candidate { + let mut result = match candidate { ProjectionCandidate::ParamEnv(poly_projection) - | ProjectionCandidate::Object(poly_projection) => { - confirm_param_env_candidate(selcx, obligation, poly_projection, false) - } - - ProjectionCandidate::TraitDef(poly_projection) => { - confirm_param_env_candidate(selcx, obligation, poly_projection, true) - } - + | ProjectionCandidate::Object(poly_projection) => Ok(Projected::Progress( + confirm_param_env_candidate(selcx, obligation, poly_projection, false), + )), + ProjectionCandidate::TraitDef(poly_projection) => Ok(Projected::Progress( + confirm_param_env_candidate(selcx, obligation, poly_projection, true), + )), ProjectionCandidate::Select(impl_source) => { confirm_select_candidate(selcx, obligation, impl_source) } - - ProjectionCandidate::ObjectRpitit => confirm_object_rpitit_candidate(selcx, obligation), }; // When checking for cycle during evaluation, we compare predicates with @@ -1283,23 +1220,27 @@ fn confirm_candidate<'cx, 'tcx>( // with new region variables, we need to resolve them to existing variables // when possible for this to work. See `auto-trait-projection-recursion.rs` // for a case where this matters. - if progress.term.has_infer_regions() { + if let Ok(Projected::Progress(progress)) = &mut result + && progress.term.has_infer_regions() + { progress.term = progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx)); } - progress + + result } +// FIXME(mgca): While this supports constants, it is only used for types by default right now fn confirm_select_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTermObligation<'tcx>, impl_source: Selection<'tcx>, -) -> Progress<'tcx> { +) -> Result, ProjectionError<'tcx>> { match impl_source { ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data), ImplSource::Builtin(BuiltinImplSource::Misc | BuiltinImplSource::Trivial, data) => { let tcx = selcx.tcx(); let trait_def_id = obligation.predicate.trait_def_id(tcx); - if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) { + let progress = if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) { confirm_coroutine_candidate(selcx, obligation, data) } else if tcx.is_lang_item(trait_def_id, LangItem::Future) { confirm_future_candidate(selcx, obligation, data) @@ -1321,12 +1262,12 @@ fn confirm_select_candidate<'cx, 'tcx>( confirm_async_fn_kind_helper_candidate(selcx, obligation, data) } else { confirm_builtin_candidate(selcx, obligation, data) - } + }; + Ok(Projected::Progress(progress)) } ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) | ImplSource::Param(..) - | ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) - | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { + | ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) => { // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, @@ -1428,7 +1369,7 @@ fn confirm_future_candidate<'cx, 'tcx>( coroutine_sig, ); - debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Output); + debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name(), sym::Output); let predicate = ty::ProjectionPredicate { projection_term: ty::AliasTerm::new_from_args( @@ -1474,7 +1415,7 @@ fn confirm_iterator_candidate<'cx, 'tcx>( gen_sig, ); - debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item); + debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name(), sym::Item); let predicate = ty::ProjectionPredicate { projection_term: ty::AliasTerm::new_from_args( @@ -1520,7 +1461,7 @@ fn confirm_async_iterator_candidate<'cx, 'tcx>( gen_sig, ); - debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item); + debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name(), sym::Item); let ty::Adt(_poll_adt, args) = *yield_ty.kind() else { bug!(); @@ -1559,11 +1500,6 @@ fn confirm_builtin_candidate<'cx, 'tcx>( assert_eq!(discriminant_def_id, item_def_id); (self_ty.discriminant_ty(tcx).into(), PredicateObligations::new()) - } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncDestruct) { - let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0]; - assert_eq!(destructor_def_id, item_def_id); - - (self_ty.async_destructor_ty(tcx).into(), PredicateObligations::new()) } else if tcx.is_lang_item(trait_def_id, LangItem::PointeeTrait) { let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); assert_eq!(metadata_def_id, item_def_id); @@ -1945,6 +1881,7 @@ fn confirm_async_fn_kind_helper_candidate<'cx, 'tcx>( .with_addl_obligations(nested) } +// FIXME(mgca): While this supports constants, it is only used for types by default right now fn confirm_param_env_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTermObligation<'tcx>, @@ -1998,9 +1935,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>( ) { Ok(InferOk { value: _, obligations }) => { nested_obligations.extend(obligations); - assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations); - // FIXME(associated_const_equality): Handle consts here as well? Maybe this progress type should just take - // a term instead. + assoc_term_own_obligations(selcx, obligation, &mut nested_obligations); Progress { term: cache_entry.term, obligations: nested_obligations } } Err(e) => { @@ -2014,11 +1949,12 @@ fn confirm_param_env_candidate<'cx, 'tcx>( } } +// FIXME(mgca): While this supports constants, it is only used for types by default right now fn confirm_impl_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTermObligation<'tcx>, impl_impl_source: ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>, -) -> Progress<'tcx> { +) -> Result, ProjectionError<'tcx>> { let tcx = selcx.tcx(); let ImplSourceUserDefinedData { impl_def_id, args, mut nested } = impl_impl_source; @@ -2027,21 +1963,40 @@ fn confirm_impl_candidate<'cx, 'tcx>( let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); let param_env = obligation.param_env; - let assoc_ty = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) { - Ok(assoc_ty) => assoc_ty, - Err(guar) => return Progress::error(tcx, guar), + let assoc_term = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) { + Ok(assoc_term) => assoc_term, + Err(guar) => return Ok(Projected::Progress(Progress::error(tcx, guar))), }; - if !assoc_ty.item.defaultness(tcx).has_value() { - // This means that the impl is missing a definition for the - // associated type. This error will be reported by the type - // checker method `check_impl_items_against_trait`, so here we - // just return Error. + + // This means that the impl is missing a definition for the + // associated type. This is either because the associate item + // has impossible-to-satisfy predicates (since those were + // allowed in ), + // or because the impl is literally missing the definition. + if !assoc_term.item.defaultness(tcx).has_value() { debug!( "confirm_impl_candidate: no associated type {:?} for {:?}", - assoc_ty.item.name, obligation.predicate + assoc_term.item.name(), + obligation.predicate ); - return Progress { term: Ty::new_misc_error(tcx).into(), obligations: nested }; + if tcx.impl_self_is_guaranteed_unsized(impl_def_id) { + // We treat this projection as rigid here, which is represented via + // `Projected::NoProgress`. This will ensure that the projection is + // checked for well-formedness, and it's either satisfied by a trivial + // where clause in its env or it results in an error. + return Ok(Projected::NoProgress(obligation.predicate.to_term(tcx))); + } else { + return Ok(Projected::Progress(Progress { + term: if obligation.predicate.kind(tcx).is_type() { + Ty::new_misc_error(tcx).into() + } else { + ty::Const::new_misc_error(tcx).into() + }, + obligations: nested, + })); + } } + // If we're trying to normalize ` as X>::A` using //`impl X for Vec { type A = Box; }`, then: // @@ -2049,71 +2004,41 @@ fn confirm_impl_candidate<'cx, 'tcx>( // * `args` is `[u32]` // * `args` ends up as `[u32, S]` let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args); - let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node); - let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst); - let term: ty::EarlyBinder<'tcx, ty::Term<'tcx>> = if is_const { - let did = assoc_ty.item.def_id; - let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did); - let uv = ty::UnevaluatedConst::new(did, identity_args); - ty::EarlyBinder::bind(ty::Const::new_unevaluated(tcx, uv).into()) + let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_term.defining_node); + + let term = if obligation.predicate.kind(tcx).is_type() { + tcx.type_of(assoc_term.item.def_id).map_bound(|ty| ty.into()) } else { - tcx.type_of(assoc_ty.item.def_id).map_bound(|ty| ty.into()) + ty::EarlyBinder::bind( + get_associated_const_value( + selcx, + obligation.predicate.to_term(tcx).expect_const(), + param_env, + ) + .into(), + ) }; - if !tcx.check_args_compatible(assoc_ty.item.def_id, args) { - let err = Ty::new_error_with_message( - tcx, - obligation.cause.span, - "impl item and trait item have different parameters", - ); - Progress { term: err.into(), obligations: nested } + + let progress = if !tcx.check_args_compatible(assoc_term.item.def_id, args) { + let msg = "impl item and trait item have different parameters"; + let span = obligation.cause.span; + let err = if obligation.predicate.kind(tcx).is_type() { + Ty::new_error_with_message(tcx, span, msg).into() + } else { + ty::Const::new_error_with_message(tcx, span, msg).into() + }; + Progress { term: err, obligations: nested } } else { - assoc_ty_own_obligations(selcx, obligation, &mut nested); + assoc_term_own_obligations(selcx, obligation, &mut nested); Progress { term: term.instantiate(tcx, args), obligations: nested } - } -} - -fn confirm_object_rpitit_candidate<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTermObligation<'tcx>, -) -> Progress<'tcx> { - let tcx = selcx.tcx(); - let mut obligations = thin_vec![]; - - // Compute an intersection lifetime for all the input components of this GAT. - let intersection = - selcx.infcx.next_region_var(RegionVariableOrigin::MiscVariable(obligation.cause.span)); - for component in obligation.predicate.args { - match component.unpack() { - ty::GenericArgKind::Lifetime(lt) => { - obligations.push(obligation.with(tcx, ty::OutlivesPredicate(lt, intersection))); - } - ty::GenericArgKind::Type(ty) => { - obligations.push(obligation.with(tcx, ty::OutlivesPredicate(ty, intersection))); - } - ty::GenericArgKind::Const(_ct) => { - // Consts have no outlives... - } - } - } - - Progress { - term: Ty::new_dynamic( - tcx, - tcx.item_bounds_to_existential_predicates( - obligation.predicate.def_id, - obligation.predicate.args, - ), - intersection, - ty::DynStar, - ) - .into(), - obligations, - } + }; + Ok(Projected::Progress(progress)) } // Get obligations corresponding to the predicates from the where-clause of the // associated type itself. -fn assoc_ty_own_obligations<'cx, 'tcx>( +// FIXME(mgca): While this supports constants, it is only used for types by default right now +fn assoc_term_own_obligations<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTermObligation<'tcx>, nested: &mut PredicateObligations<'tcx>, @@ -2183,3 +2108,15 @@ impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> { }) } } + +fn get_associated_const_value<'tcx>( + selcx: &mut SelectionContext<'_, 'tcx>, + alias_ct: ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> ty::Const<'tcx> { + // FIXME(mgca): We shouldn't be invoking ctfe here, instead const items should be aliases to type + // system consts that we can retrieve with some `query const_arg_of_alias` query. Evaluating the + // constant is "close enough" to getting the actual rhs of the const item for now even if it might + // lead to some cycles + super::evaluate_const(selcx.infcx, alias_ct, param_env) +} diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index f04a5feba3011..38cfdcdc22d33 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -196,11 +196,31 @@ where debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); ty } else { - ocx.deeply_normalize(&cause, param_env, ty)?; + // Flush errors b/c `deeply_normalize` doesn't expect pending + // obligations, and we may have pending obligations from the + // branch above (from other types). + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + return Err(errors); + } - let errors = ocx.select_where_possible(); - debug!("normalize errors: {ty} ~> {errors:#?}"); - return Err(errors); + // When query normalization fails, we don't get back an interesting + // reason that we could use to report an error in borrowck. In order to turn + // this into a reportable error, we deeply normalize again. We don't expect + // this to succeed, so delay a bug if it does. + match ocx.deeply_normalize(&cause, param_env, ty) { + Ok(_) => { + tcx.dcx().span_delayed_bug( + span, + format!( + "query normalize succeeded of {ty}, \ + but deep normalize failed", + ), + ); + ty + } + Err(errors) => return Err(errors), + } }; match ty.kind() { diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 6bf76eaee5c64..eb34cb10c68dd 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -7,9 +7,10 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::traits::PredicateObligations; use rustc_macros::extension; pub use rustc_middle::traits::query::NormalizationResult; -use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; -use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor, TypingMode}; +use rustc_middle::ty::{ + self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, + TypeVisitableExt, TypeVisitor, TypingMode, +}; use rustc_span::DUMMY_SP; use tracing::{debug, info, instrument}; @@ -126,7 +127,7 @@ struct MaxEscapingBoundVarVisitor { } impl<'tcx> TypeVisitor> for MaxEscapingBoundVarVisitor { - fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { + fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { self.outer_index.shift_in(1); t.super_visit_with(self); self.outer_index.shift_out(1); @@ -143,7 +144,7 @@ impl<'tcx> TypeVisitor> for MaxEscapingBoundVarVisitor { #[inline] fn visit_region(&mut self, r: ty::Region<'tcx>) { - match *r { + match r.kind() { ty::ReBound(debruijn, _) if debruijn > self.outer_index => { self.escaping = self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize()); @@ -215,6 +216,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { match self.infcx.typing_mode() { TypingMode::Coherence | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => ty.try_super_fold_with(self)?, TypingMode::PostAnalysis => { @@ -251,7 +253,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { } } - ty::Projection | ty::Inherent | ty::Weak => { + ty::Projection | ty::Inherent | ty::Free => { // See note in `rustc_trait_selection::traits::project` let infcx = self.infcx; @@ -273,7 +275,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { debug!("QueryNormalizer: orig_values = {:#?}", orig_values); let result = match kind { ty::Projection => tcx.normalize_canonicalized_projection_ty(c_data), - ty::Weak => tcx.normalize_canonicalized_weak_ty(c_data), + ty::Free => tcx.normalize_canonicalized_free_alias(c_data), ty::Inherent => tcx.normalize_canonicalized_inherent_projection_ty(c_data), kind => unreachable!("did not expect {kind:?} due to match arm above"), }?; @@ -311,10 +313,10 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { }; // `tcx.normalize_canonicalized_projection_ty` may normalize to a type that // still has unevaluated consts, so keep normalizing here if that's the case. - // Similarly, `tcx.normalize_canonicalized_weak_ty` will only unwrap one layer + // Similarly, `tcx.normalize_canonicalized_free_alias` will only unwrap one layer // of type and we need to continue folding it to reveal the TAIT behind it. if res != ty - && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak) + && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Free) { res.try_fold_with(self)? } else { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 4eecde00eaa1e..81b5a131a32e0 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -117,8 +117,7 @@ fn relate_mir_and_user_args<'tcx>( CRATE_DEF_ID, ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span), ); - let instantiated_predicate = - ocx.normalize(&cause.clone(), param_env, instantiated_predicate); + let instantiated_predicate = ocx.normalize(&cause, param_env, instantiated_predicate); ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate)); } @@ -132,12 +131,12 @@ fn relate_mir_and_user_args<'tcx>( // const CONST: () = { /* arbitrary code that depends on T being WF */ }; // } // ``` - for arg in args { + for term in args.iter().filter_map(ty::GenericArg::as_term) { ocx.register_obligation(Obligation::new( tcx, cause.clone(), param_env, - ty::ClauseKind::WellFormed(arg), + ty::ClauseKind::WellFormed(term), )); } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index ec0b790339691..d9b57f0c67d14 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -1,15 +1,16 @@ +use std::ops::ControlFlow; + +use rustc_infer::infer::RegionObligation; use rustc_infer::infer::canonical::CanonicalQueryInput; -use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::OutlivesBound; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt}; +use rustc_middle::ty::outlives::{Component, push_outlives_components}; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitable, TypeVisitor}; use rustc_span::def_id::CRATE_DEF_ID; -use rustc_span::{DUMMY_SP, Span}; -use rustc_type_ir::outlives::{Component, push_outlives_components}; +use rustc_span::{DUMMY_SP, Span, sym}; use smallvec::{SmallVec, smallvec}; -use tracing::debug; use crate::traits::query::NoSolution; use crate::traits::{ObligationCtxt, wf}; @@ -35,11 +36,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { tcx: TyCtxt<'tcx>, canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result, NoSolution> { - if tcx.sess.opts.unstable_opts.no_implied_bounds_compat { - tcx.implied_outlives_bounds(canonicalized) - } else { - tcx.implied_outlives_bounds_compat(canonicalized) - } + tcx.implied_outlives_bounds((canonicalized, false)) } fn perform_locally_with_next_solver( @@ -47,11 +44,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { key: ParamEnvAnd<'tcx, Self>, span: Span, ) -> Result { - if ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat { - compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty, span) - } else { - compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty, span) - } + compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty, span, false) } } @@ -60,18 +53,15 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, span: Span, + disable_implied_bounds_hack: bool, ) -> Result>, NoSolution> { - let normalize_op = |ty| -> Result<_, NoSolution> { + let normalize_ty = |ty| -> Result<_, NoSolution> { // We must normalize the type so we can compute the right outlives components. // for example, if we have some constrained param type like `T: Trait`, // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`. let ty = ocx .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty) .map_err(|_| NoSolution)?; - if !ocx.select_all_or_error().is_empty() { - return Err(NoSolution); - } - let ty = OpportunisticRegionResolver::new(&ocx.infcx).fold_ty(ty); Ok(ty) }; @@ -81,7 +71,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( // guaranteed to be a subset of the original type, so we need to store the // WF args we've computed in a set. let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default(); - let mut wf_args = vec![ty.into(), normalize_op(ty)?.into()]; + let mut wf_args = vec![ty.into(), normalize_ty(ty)?.into()]; let mut outlives_bounds: Vec> = vec![]; @@ -96,8 +86,14 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( .into_iter() .flatten() { - assert!(!obligation.has_escaping_bound_vars()); - let Some(pred) = obligation.predicate.kind().no_bound_vars() else { + let pred = ocx + .deeply_normalize( + &ObligationCause::dummy_with_span(span), + param_env, + obligation.predicate, + ) + .map_err(|_| NoSolution)?; + let Some(pred) = pred.kind().no_bound_vars() else { continue; }; match pred { @@ -117,8 +113,8 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( | ty::PredicateKind::AliasRelate(..) => {} // We need to search through *all* WellFormed predicates - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - wf_args.push(arg); + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + wf_args.push(term); } // We need to register region relationships @@ -130,7 +126,6 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( ty_a, r_b, ))) => { - let ty_a = normalize_op(ty_a)?; let mut components = smallvec![]; push_outlives_components(ocx.infcx.tcx, ty_a, &mut components); outlives_bounds.extend(implied_bounds_from_components(r_b, components)) @@ -139,141 +134,48 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( } } - Ok(outlives_bounds) -} - -pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( - ocx: &ObligationCtxt<'_, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - span: Span, -) -> Result>, NoSolution> { - let tcx = ocx.infcx.tcx; - - // Sometimes when we ask what it takes for T: WF, we get back that - // U: WF is required; in that case, we push U onto this stack and - // process it next. Because the resulting predicates aren't always - // guaranteed to be a subset of the original type, so we need to store the - // WF args we've computed in a set. - let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default(); - let mut wf_args = vec![ty.into()]; - - let mut outlives_bounds: Vec>> = vec![]; - - while let Some(arg) = wf_args.pop() { - if !checked_wf_args.insert(arg) { - continue; + // If we detect `bevy_ecs::*::ParamSet` in the WF args list (and `disable_implied_bounds_hack` + // or `-Zno-implied-bounds-compat` are not set), then use the registered outlives obligations + // as implied bounds. + if !disable_implied_bounds_hack + && !ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat + && ty.visit_with(&mut ContainsBevyParamSet { tcx: ocx.infcx.tcx }).is_break() + { + for RegionObligation { sup_type, sub_region, .. } in + ocx.infcx.take_registered_region_obligations() + { + let mut components = smallvec![]; + push_outlives_components(ocx.infcx.tcx, sup_type, &mut components); + outlives_bounds.extend(implied_bounds_from_components(sub_region, components)); } + } - // Compute the obligations for `arg` to be well-formed. If `arg` is - // an unresolved inference variable, just instantiated an empty set - // -- because the return type here is going to be things we *add* - // to the environment, it's always ok for this set to be smaller - // than the ultimate set. (Note: normally there won't be - // unresolved inference variables here anyway, but there might be - // during typeck under some circumstances.) - // - // FIXME(@lcnr): It's not really "always fine", having fewer implied - // bounds can be backward incompatible, e.g. #101951 was caused by - // us not dealing with inference vars in `TypeOutlives` predicates. - let obligations = - wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, span).unwrap_or_default(); + Ok(outlives_bounds) +} - for obligation in obligations { - debug!(?obligation); - assert!(!obligation.has_escaping_bound_vars()); +struct ContainsBevyParamSet<'tcx> { + tcx: TyCtxt<'tcx>, +} - // While these predicates should all be implied by other parts of - // the program, they are still relevant as they may constrain - // inference variables, which is necessary to add the correct - // implied bounds in some cases, mostly when dealing with projections. - // - // Another important point here: we only register `Projection` - // predicates, since otherwise we might register outlives - // predicates containing inference variables, and we don't - // learn anything new from those. - if obligation.predicate.has_non_region_infer() { - match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) - | ty::PredicateKind::AliasRelate(..) => { - ocx.register_obligation(obligation.clone()); - } - _ => {} - } - } +impl<'tcx> TypeVisitor> for ContainsBevyParamSet<'tcx> { + type Result = ControlFlow<()>; - let pred = match obligation.predicate.kind().no_bound_vars() { - None => continue, - Some(pred) => pred, - }; - match pred { - // FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound - // if we ever support that - ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) - | ty::PredicateKind::DynCompatible(..) - | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::Ambiguous - | ty::PredicateKind::NormalizesTo(..) - | ty::PredicateKind::AliasRelate(..) => {} - - // We need to search through *all* WellFormed predicates - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - wf_args.push(arg); + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + // We only care to match `ParamSet` or `&ParamSet`. + match t.kind() { + ty::Adt(def, _) => { + if self.tcx.item_name(def.did()) == sym::ParamSet + && self.tcx.crate_name(def.did().krate) == sym::bevy_ecs + { + return ControlFlow::Break(()); } - - // We need to register region relationships - ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives( - ty::OutlivesPredicate(r_a, r_b), - )) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)), - - ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( - ty_a, - r_b, - ))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)), } + ty::Ref(_, ty, _) => ty.visit_with(self)?, + _ => {} } - } - // This call to `select_all_or_error` is necessary to constrain inference variables, which we - // use further down when computing the implied bounds. - match ocx.select_all_or_error().as_slice() { - [] => (), - _ => return Err(NoSolution), + ControlFlow::Continue(()) } - - // We lazily compute the outlives components as - // `select_all_or_error` constrains inference variables. - let mut implied_bounds = Vec::new(); - for ty::OutlivesPredicate(a, r_b) in outlives_bounds { - match a.unpack() { - ty::GenericArgKind::Lifetime(r_a) => { - implied_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)) - } - ty::GenericArgKind::Type(ty_a) => { - let mut ty_a = ocx.infcx.resolve_vars_if_possible(ty_a); - // Need to manually normalize in the new solver as `wf::obligations` does not. - if ocx.infcx.next_trait_solver() { - ty_a = ocx - .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty_a) - .map_err(|_| NoSolution)?; - } - let mut components = smallvec![]; - push_outlives_components(tcx, ty_a, &mut components); - implied_bounds.extend(implied_bounds_from_components(r_b, components)) - } - ty::GenericArgKind::Const(_) => { - unreachable!("consts do not participate in outlives bounds") - } - } - } - - Ok(implied_bounds) } /// When we have an implied bound that `T: 'a`, we can further break diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 68feb19c55b89..4bdf04311a04f 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -3,8 +3,7 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; use rustc_infer::traits::PredicateObligations; use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; +use rustc_middle::ty::{ParamEnvAnd, TyCtxt, TypeFoldable}; use rustc_span::Span; use crate::infer::canonical::{ diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 2f6bbd7f4cf48..f2a6ce855b49d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -3,8 +3,7 @@ use std::fmt; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; pub use rustc_middle::traits::query::type_op::{DeeplyNormalize, Normalize}; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 4f9e2e79d624c..18971c47831a4 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -1,4 +1,3 @@ -use rustc_hir::LangItem; use rustc_infer::traits::Obligation; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; @@ -7,7 +6,7 @@ use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; -use crate::traits::ObligationCtxt; +use crate::traits::{ObligationCtxt, sizedness_fast_path}; impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { type QueryResponse = (); @@ -16,22 +15,14 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>, ) -> Option { - // Proving Sized, very often on "obviously sized" types like - // `&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::Clause(ty::ClauseKind::Trait(trait_ref)) = - key.value.predicate.kind().skip_binder() - && tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) - && trait_ref.self_ty().is_trivially_sized(tcx) - { + if sizedness_fast_path(tcx, key.value.predicate) { return Some(()); } - if let ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) = + if let ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) = key.value.predicate.kind().skip_binder() { - match arg.as_type()?.kind() { + match term.as_type()?.kind() { ty::Param(_) | ty::Bool | ty::Char diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 11b6b826efe54..7d869b27445cf 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -14,9 +14,8 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir as hir; use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; -use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode}; +use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode, elaborate}; use rustc_middle::{bug, span_bug}; -use rustc_type_ir::{Interner, elaborate}; use tracing::{debug, instrument, trace}; use super::SelectionCandidate::*; @@ -66,77 +65,92 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let def_id = obligation.predicate.def_id(); let tcx = self.tcx(); - if tcx.is_lang_item(def_id, LangItem::Copy) { - debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); + let lang_item = tcx.as_lang_item(def_id); + match lang_item { + Some(LangItem::Copy | LangItem::Clone) => { + debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); - // User-defined copy impls are permitted, but only for - // structs and enums. - self.assemble_candidates_from_impls(obligation, &mut candidates); + // User-defined copy impls are permitted, but only for + // structs and enums. + self.assemble_candidates_from_impls(obligation, &mut candidates); - // For other types, we'll use the builtin rules. - let copy_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::DiscriminantKind) { - // `DiscriminantKind` is automatically implemented for every type. - candidates.vec.push(BuiltinCandidate { has_nested: false }); - } else if tcx.is_lang_item(def_id, LangItem::AsyncDestruct) { - // `AsyncDestruct` is automatically implemented for every type. - candidates.vec.push(BuiltinCandidate { has_nested: false }); - } else if tcx.is_lang_item(def_id, LangItem::PointeeTrait) { - // `Pointee` is automatically implemented for every type. - candidates.vec.push(BuiltinCandidate { has_nested: false }); - } else if tcx.is_lang_item(def_id, LangItem::Sized) { - // Sized is never implementable by end-users, it is - // always automatically computed. - let sized_conditions = self.sized_conditions(obligation); - self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::Unsize) { - self.assemble_candidates_for_unsizing(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::Destruct) { - self.assemble_const_destruct_candidates(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::TransmuteTrait) { - // User-defined transmutability impls are permitted. - self.assemble_candidates_from_impls(obligation, &mut candidates); - self.assemble_candidates_for_transmutability(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::Tuple) { - self.assemble_candidate_for_tuple(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) { - self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::BikeshedGuaranteedNoDrop) { - self.assemble_candidates_for_bikeshed_guaranteed_no_drop_trait( - obligation, - &mut candidates, - ); - } else { - if tcx.is_lang_item(def_id, LangItem::Clone) { - // Same builtin conditions as `Copy`, i.e., every type which has builtin support - // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone` - // types have builtin support for `Clone`. - let clone_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates); + // For other types, we'll use the builtin rules. + let copy_conditions = self.copy_clone_conditions(obligation); + self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); } - - if tcx.is_lang_item(def_id, LangItem::Coroutine) { - self.assemble_coroutine_candidates(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::Future) { - self.assemble_future_candidates(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::Iterator) { - self.assemble_iterator_candidates(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::FusedIterator) { - self.assemble_fused_iterator_candidates(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::AsyncIterator) { - self.assemble_async_iterator_candidates(obligation, &mut candidates); - } else if tcx.is_lang_item(def_id, LangItem::AsyncFnKindHelper) { - self.assemble_async_fn_kind_helper_candidates(obligation, &mut candidates); + Some(LangItem::DiscriminantKind) => { + // `DiscriminantKind` is automatically implemented for every type. + candidates.vec.push(BuiltinCandidate { has_nested: false }); } + Some(LangItem::PointeeTrait) => { + // `Pointee` is automatically implemented for every type. + candidates.vec.push(BuiltinCandidate { has_nested: false }); + } + Some(LangItem::Sized) => { + self.assemble_builtin_sized_candidate(obligation, &mut candidates); + } + Some(LangItem::Unsize) => { + self.assemble_candidates_for_unsizing(obligation, &mut candidates); + } + Some(LangItem::Destruct) => { + self.assemble_const_destruct_candidates(obligation, &mut candidates); + } + Some(LangItem::TransmuteTrait) => { + // User-defined transmutability impls are permitted. + self.assemble_candidates_from_impls(obligation, &mut candidates); + self.assemble_candidates_for_transmutability(obligation, &mut candidates); + } + Some(LangItem::Tuple) => { + self.assemble_candidate_for_tuple(obligation, &mut candidates); + } + Some(LangItem::FnPtrTrait) => { + self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); + } + Some(LangItem::BikeshedGuaranteedNoDrop) => { + self.assemble_candidates_for_bikeshed_guaranteed_no_drop_trait( + obligation, + &mut candidates, + ); + } + _ => { + // We re-match here for traits that can have both builtin impls and user written impls. + // After the builtin impls we need to also add user written impls, which we do not want to + // do in general because just checking if there are any is expensive. + match lang_item { + Some(LangItem::Coroutine) => { + self.assemble_coroutine_candidates(obligation, &mut candidates); + } + Some(LangItem::Future) => { + self.assemble_future_candidates(obligation, &mut candidates); + } + Some(LangItem::Iterator) => { + self.assemble_iterator_candidates(obligation, &mut candidates); + } + Some(LangItem::FusedIterator) => { + self.assemble_fused_iterator_candidates(obligation, &mut candidates); + } + Some(LangItem::AsyncIterator) => { + self.assemble_async_iterator_candidates(obligation, &mut candidates); + } + Some(LangItem::AsyncFnKindHelper) => { + self.assemble_async_fn_kind_helper_candidates( + obligation, + &mut candidates, + ); + } + Some(LangItem::AsyncFn | LangItem::AsyncFnMut | LangItem::AsyncFnOnce) => { + self.assemble_async_closure_candidates(obligation, &mut candidates); + } + Some(LangItem::Fn | LangItem::FnMut | LangItem::FnOnce) => { + self.assemble_closure_candidates(obligation, &mut candidates); + self.assemble_fn_pointer_candidates(obligation, &mut candidates); + } + _ => {} + } - // FIXME: Put these into `else if` blocks above, since they're built-in. - self.assemble_closure_candidates(obligation, &mut candidates); - self.assemble_async_closure_candidates(obligation, &mut candidates); - self.assemble_fn_pointer_candidates(obligation, &mut candidates); - - self.assemble_candidates_from_impls(obligation, &mut candidates); - self.assemble_candidates_from_object_ty(obligation, &mut candidates); + self.assemble_candidates_from_impls(obligation, &mut candidates); + self.assemble_candidates_from_object_ty(obligation, &mut candidates); + } } self.assemble_candidates_from_projected_tys(obligation, &mut candidates); @@ -176,7 +190,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // normalization, so try to deduplicate when possible to avoid // unnecessary ambiguity. let mut distinct_normalized_bounds = FxHashSet::default(); - self.for_each_item_bound::( + let _ = self.for_each_item_bound::( placeholder_trait_predicate.self_ty(), |selcx, bound, idx| { let Some(bound) = bound.as_trait_clause() else { @@ -243,8 +257,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if !drcx.args_may_unify(obligation_args, bound_trait_ref.skip_binder().args) { continue; } - // FIXME(oli-obk): it is suspicious that we are dropping the constness and - // polarity here. let wc = self.where_clause_may_apply(stack, bound_trait_ref)?; if wc.may_apply() { candidates.vec.push(ParamCandidate(bound)); @@ -369,9 +381,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - let Some(kind) = self.tcx().fn_trait_kind_from_def_id(obligation.predicate.def_id()) else { - return; - }; + let kind = self.tcx().fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap(); // Okay to skip binder because the args on closure types never // touch bound regions, they just capture the in-scope @@ -433,11 +443,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - let Some(goal_kind) = - self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()) - else { - return; - }; + let goal_kind = + self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap(); match *obligation.self_ty().skip_binder().kind() { ty::CoroutineClosure(_, args) => { @@ -510,11 +517,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - // We provide impl of all fn traits for fn pointers. - if !self.tcx().is_fn_trait(obligation.predicate.def_id()) { - return; - } - // Keep this function in sync with extract_tupled_inputs_and_output_from_callable // until the old solver (and thus this function) is removed. @@ -695,6 +697,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let def_id = obligation.predicate.def_id(); + let mut check_impls = || { + // Only consider auto impls if there are no manual impls for the root of `self_ty`. + // + // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl + // for `&SomeType: Auto` exists. Due to E0321 the only crate where impls + // for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined. + // + // Generally, we have to guarantee that for all `SimplifiedType`s the only crate + // which may define impls for that type is either the crate defining the type + // or the trait. This should be guaranteed by the orphan check. + let mut has_impl = false; + self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true); + if !has_impl { + candidates.vec.push(AutoImplCandidate) + } + }; + if self.tcx().trait_is_auto(def_id) { match *self_ty.kind() { ty::Dynamic(..) => { @@ -708,9 +727,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // we don't add any `..` impl. Default traits could // still be provided by a manual implementation for // this trait and type. + + // Backward compatibility for default auto traits. + // Test: ui/traits/default_auto_traits/extern-types.rs + if self.tcx().is_default_trait(def_id) { + check_impls() + } } ty::Param(..) - | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) + | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) | ty::Placeholder(..) | ty::Bound(..) => { // In these cases, we don't know what the actual @@ -802,24 +827,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::UnsafeBinder(_) => { // Only consider auto impls of unsafe traits when there are // no unsafe fields. - if self.tcx().trait_is_unsafe(def_id) && self_ty.has_unsafe_fields() { + if self.tcx().trait_def(def_id).safety.is_unsafe() + && self_ty.has_unsafe_fields() + { return; } - // Only consider auto impls if there are no manual impls for the root of `self_ty`. - // - // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl - // for `&SomeType: Auto` exists. Due to E0321 the only crate where impls - // for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined. - // - // Generally, we have to guarantee that for all `SimplifiedType`s the only crate - // which may define impls for that type is either the crate defining the type - // or the trait. This should be guaranteed by the orphan check. - let mut has_impl = false; - self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true); - if !has_impl { - candidates.vec.push(AutoImplCandidate) - } + check_impls(); } ty::Error(_) => { candidates.vec.push(AutoImplCandidate); @@ -1017,13 +1031,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - // `(.., T)` -> `(.., U)` - (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { - if tys_a.len() == tys_b.len() { - candidates.vec.push(BuiltinUnsizeCandidate); - } - } - _ => {} }; } @@ -1066,6 +1073,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Assembles the trait which are built-in to the language itself: /// `Copy`, `Clone` and `Sized`. #[instrument(level = "debug", skip(self, candidates))] + fn assemble_builtin_sized_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + match self.sized_conditions(obligation) { + BuiltinImplConditions::Where(nested) => { + candidates + .vec + .push(SizedCandidate { has_nested: !nested.skip_binder().is_empty() }); + } + BuiltinImplConditions::None => {} + BuiltinImplConditions::Ambiguous => { + candidates.ambiguous = true; + } + } + } + + /// Assembles the trait which are built-in to the language itself: + /// e.g. `Copy` and `Clone`. + #[instrument(level = "debug", skip(self, candidates))] fn assemble_builtin_bound_candidates( &mut self, conditions: BuiltinImplConditions<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index a58317dd56d24..c9169127e0b90 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -7,7 +7,6 @@ //! [rustc dev guide]: //! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation -use std::iter; use std::ops::ControlFlow; use rustc_ast::Mutability; @@ -16,10 +15,9 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk}; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, Upcast}; +use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, Upcast, elaborate}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; -use rustc_type_ir::elaborate; use thin_vec::thin_vec; use tracing::{debug, instrument}; @@ -40,7 +38,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'tcx>, candidate: SelectionCandidate<'tcx>, ) -> Result, SelectionError<'tcx>> { - let mut impl_src = match candidate { + Ok(match candidate { + SizedCandidate { has_nested } => { + let data = self.confirm_builtin_candidate(obligation, has_nested); + ImplSource::Builtin(BuiltinImplSource::Misc, data) + } + BuiltinCandidate { has_nested } => { let data = self.confirm_builtin_candidate(obligation, has_nested); ImplSource::Builtin(BuiltinImplSource::Misc, data) @@ -135,15 +138,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { BikeshedGuaranteedNoDropCandidate => { self.confirm_bikeshed_guaranteed_no_drop_candidate(obligation) } - }; - - // The obligations returned by confirmation are recursively evaluated - // so we need to make sure they have the correct depth. - for subobligation in impl_src.borrow_nested_obligations_mut() { - subobligation.set_depth_from_parent(obligation.recursion_depth); - } - - Ok(impl_src) + }) } fn confirm_projection_candidate( @@ -256,20 +251,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); let obligations = if has_nested { let trait_def = obligation.predicate.def_id(); - let conditions = if tcx.is_lang_item(trait_def, LangItem::Sized) { - self.sized_conditions(obligation) - } else if tcx.is_lang_item(trait_def, LangItem::Copy) { - self.copy_clone_conditions(obligation) - } else if tcx.is_lang_item(trait_def, LangItem::Clone) { - self.copy_clone_conditions(obligation) - } else if tcx.is_lang_item(trait_def, LangItem::FusedIterator) { - self.fused_iterator_conditions(obligation) - } else { - bug!("unexpected builtin trait {:?}", trait_def) + let conditions = match tcx.as_lang_item(trait_def) { + Some(LangItem::Sized) => self.sized_conditions(obligation), + Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(obligation), + Some(LangItem::FusedIterator) => self.fused_iterator_conditions(obligation), + other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"), }; - let BuiltinImplConditions::Where(nested) = conditions else { + let BuiltinImplConditions::Where(types) = conditions else { bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation); }; + let types = self.infcx.enter_forall_and_leak_universe(types); let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived); self.collect_predicates_for_types( @@ -277,7 +268,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause, obligation.recursion_depth + 1, trait_def, - nested, + types, ) } else { PredicateObligations::new() @@ -320,7 +311,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.cause.clone(), obligation.recursion_depth + 1, obligation.param_env, - obligation.predicate.rebind(trait_ref), + trait_ref, ) }; @@ -346,7 +337,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.cause.clone(), obligation.recursion_depth + 1, obligation.param_env, - obligation.predicate.rebind(outlives), + outlives, ) }; @@ -407,10 +398,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - let predicate = obligation.predicate.skip_binder(); + let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); let mut assume = predicate.trait_ref.args.const_at(2); - // FIXME(min_generic_const_exprs): We should shallowly normalize this. if self.tcx().features().generic_const_exprs() { assume = crate::traits::evaluate_const(self.infcx, assume, obligation.param_env) } @@ -445,51 +435,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result, SelectionError<'tcx>> { - debug!(?obligation, "confirm_auto_impl_candidate"); - - let self_ty = obligation.predicate.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty)); - let types = self.constituent_types_for_ty(self_ty)?; - Ok(self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types)) - } - - /// See `confirm_auto_impl_candidate`. - fn vtable_auto_impl( - &mut self, - obligation: &PolyTraitObligation<'tcx>, - trait_def_id: DefId, - nested: ty::Binder<'tcx, Vec>>, - ) -> PredicateObligations<'tcx> { - debug!(?nested, "vtable_auto_impl"); ensure_sufficient_stack(|| { - let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived); - assert_eq!(obligation.predicate.polarity(), ty::PredicatePolarity::Positive); - let trait_ref = - self.infcx.enter_forall_and_leak_universe(obligation.predicate).trait_ref; - let trait_obligations = self.impl_or_trait_obligations( - &cause, - obligation.recursion_depth + 1, - obligation.param_env, - trait_def_id, - trait_ref.args, - obligation.predicate, - ); - let mut obligations = self.collect_predicates_for_types( + let self_ty = + obligation.predicate.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty)); + + let types = self.constituent_types_for_ty(self_ty)?; + let types = self.infcx.enter_forall_and_leak_universe(types); + + let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived); + let obligations = self.collect_predicates_for_types( obligation.param_env, cause, obligation.recursion_depth + 1, - trait_def_id, - nested, + obligation.predicate.def_id(), + types, ); - // Adds the predicates from the trait. Note that this contains a `Self: Trait` - // predicate as usual. It won't have any effect since auto traits are coinductive. - obligations.extend(trait_obligations); - - debug!(?obligations, "vtable_auto_impl"); - - obligations + Ok(obligations) }) } @@ -623,20 +587,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Associated types that require `Self: Sized` do not show up in the built-in // implementation of `Trait for dyn Trait`, and can be dropped here. .filter(|item| !tcx.generics_require_sized_self(item.def_id)) - .filter_map( - |item| if item.kind == ty::AssocKind::Type { Some(item.def_id) } else { None }, - ) + .filter_map(|item| if item.is_type() { Some(item.def_id) } else { None }) .collect(); for assoc_type in assoc_types { let defs: &ty::Generics = tcx.generics_of(assoc_type); - // When `async_fn_in_dyn_trait` is enabled, we don't need to check the - // RPITIT for compatibility, since it's not provided by the user. - if tcx.features().async_fn_in_dyn_trait() && tcx.is_impl_trait_in_trait(assoc_type) { - continue; - } - if !defs.own_params.is_empty() { tcx.dcx().span_delayed_bug( obligation.cause.span, @@ -984,8 +940,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(SelectionError::Unimplemented); } } else { - nested.push(obligation.with( + nested.push(Obligation::new( self.tcx(), + obligation.derived_cause(ObligationCauseCode::BuiltinDerived), + obligation.param_env, ty::TraitRef::new( self.tcx(), self.tcx().require_lang_item( @@ -1123,26 +1081,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { { // See `assemble_candidates_for_unsizing` for more info. // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. - let iter = data_a - .principal() - .filter(|_| { - // optionally drop the principal, if we're unsizing to no principal - data_b.principal().is_some() - }) - .map(|b| b.map_bound(ty::ExistentialPredicate::Trait)) - .into_iter() - .chain( + let existential_predicates = if data_b.principal().is_some() { + tcx.mk_poly_existential_predicates_from_iter( data_a - .projection_bounds() - .map(|b| b.map_bound(ty::ExistentialPredicate::Projection)), + .principal() + .map(|b| b.map_bound(ty::ExistentialPredicate::Trait)) + .into_iter() + .chain( + data_a + .projection_bounds() + .map(|b| b.map_bound(ty::ExistentialPredicate::Projection)), + ) + .chain( + data_b + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ), ) - .chain( + } else { + // If we're unsizing to a dyn type that has no principal, then drop + // the principal and projections from the type. We use the auto traits + // from the RHS type since as we noted that we've checked for auto + // trait compatibility during unsizing. + tcx.mk_poly_existential_predicates_from_iter( data_b .auto_traits() .map(ty::ExistentialPredicate::AutoTrait) .map(ty::Binder::dummy), - ); - let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter); + ) + }; let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, dyn_a); // Require that the traits involved in this upcast are **equal**; @@ -1315,34 +1283,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, nested) } - // `(.., T)` -> `(.., U)` - (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { - assert_eq!(tys_a.len(), tys_b.len()); - - // The last field of the tuple has to exist. - let (&a_last, a_mid) = tys_a.split_last().ok_or(Unimplemented)?; - let &b_last = tys_b.last().unwrap(); - - // Check that the source tuple with the target's - // last element is equal to the target. - let new_tuple = - Ty::new_tup_from_iter(tcx, a_mid.iter().copied().chain(iter::once(b_last))); - let InferOk { mut obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(DefineOpaqueTypes::Yes, target, new_tuple) - .map_err(|_| Unimplemented)?; - - // Add a nested `T: Unsize` predicate. - let last_unsize_obligation = obligation.with( - tcx, - ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]), - ); - obligations.push(last_unsize_obligation); - - ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, obligations) - } - _ => bug!("source: {source}, target: {target}"), }) } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 77dbb43465e9e..44a76f6e08327 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -27,11 +27,10 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths}; use rustc_middle::ty::{ - self, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, - TypingMode, Upcast, + self, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, + TypeVisitableExt, TypingMode, Upcast, elaborate, }; use rustc_span::{Symbol, sym}; -use rustc_type_ir::elaborate; use tracing::{debug, instrument, trace}; use self::EvaluationResult::*; @@ -49,7 +48,9 @@ use crate::infer::{InferCtxt, InferOk, TypeFreshener}; use crate::solve::InferCtxtSelectExt as _; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt}; -use crate::traits::{EvaluateConstErr, ProjectionCacheKey, Unimplemented, effects}; +use crate::traits::{ + EvaluateConstErr, ProjectionCacheKey, Unimplemented, effects, sizedness_fast_path, +}; mod _match; mod candidate_assembly; @@ -603,6 +604,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { None => self.check_recursion_limit(&obligation, &obligation)?, } + if sizedness_fast_path(self.tcx(), obligation.predicate) { + return Ok(EvaluatedToOk); + } + ensure_sufficient_stack(|| { let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { @@ -652,7 +657,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { // So, there is a bit going on here. First, `WellFormed` predicates // are coinductive, like trait predicates with auto traits. // This means that we need to detect if we have recursively @@ -676,11 +681,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let cache = previous_stack.cache; let dfn = cache.next_dfn(); - for stack_arg in previous_stack.cache.wf_args.borrow().iter().rev() { - if stack_arg.0 != arg { + for stack_term in previous_stack.cache.wf_args.borrow().iter().rev() { + if stack_term.0 != term { continue; } - debug!("WellFormed({:?}) on stack", arg); + debug!("WellFormed({:?}) on stack", term); if let Some(stack) = previous_stack.head { // Okay, let's imagine we have two different stacks: // `T: NonAutoTrait -> WF(T) -> T: NonAutoTrait` @@ -696,11 +701,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // would contain `(T, 1)`. We want to check all // trait predicates greater than `1`. The previous // stack would be `T: Auto`. - let cycle = stack.iter().take_while(|s| s.depth > stack_arg.1); + let cycle = stack.iter().take_while(|s| s.depth > stack_term.1); let tcx = self.tcx(); let cycle = cycle.map(|stack| stack.obligation.predicate.upcast(tcx)); if self.coinductive_match(cycle) { - stack.update_reached_depth(stack_arg.1); + stack.update_reached_depth(stack_term.1); return Ok(EvaluatedToOk); } else { return Ok(EvaluatedToAmbigStackDependent); @@ -714,11 +719,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.param_env, obligation.cause.body_id, obligation.recursion_depth + 1, - arg, + term, obligation.cause.span, ) { Some(obligations) => { - cache.wf_args.borrow_mut().push((arg, previous_stack.depth())); + cache.wf_args.borrow_mut().push((term, previous_stack.depth())); let result = self.evaluate_predicates_recursively(previous_stack, obligations); cache.wf_args.borrow_mut().pop(); @@ -1236,7 +1241,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { self.infcx.tcx.trait_is_coinductive(data.def_id()) } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => true, + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => { + // FIXME(generic_const_exprs): GCE needs well-formedness predicates to be + // coinductive, but GCE is on the way out anyways, so this should eventually + // be replaced with `false`. + self.infcx.tcx.features().generic_const_exprs() + } _ => false, }) } @@ -1446,6 +1456,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match self.infcx.typing_mode() { TypingMode::Coherence => {} TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Ok(()), } @@ -1491,7 +1502,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // However, if we disqualify *all* goals from being cached, perf suffers. // This is likely fixed by better caching in general in the new solver. // See: . - TypingMode::Analysis { defining_opaque_types } => { + TypingMode::Analysis { + defining_opaque_types_and_generators: defining_opaque_types, + } + | TypingMode::Borrowck { defining_opaque_types } => { defining_opaque_types.is_empty() || !pred.has_opaque_types() } // The hidden types of `defined_opaque_types` is not local to the current @@ -1660,6 +1674,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(()); } + let drcx = DeepRejectCtxt::relate_rigid_rigid(self.infcx.tcx); + let obligation_args = obligation.predicate.skip_binder().trait_ref.args; + if !drcx.args_may_unify(obligation_args, trait_bound.skip_binder().args) { + return Err(()); + } + let trait_bound = self.infcx.instantiate_binder_with_fresh_vars( obligation.cause.span, HigherRankedType, @@ -1751,12 +1771,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if is_match { let generics = self.tcx().generics_of(obligation.predicate.def_id); - // FIXME(generic-associated-types): Addresses aggressive inference in #92917. + // FIXME(generic_associated_types): Addresses aggressive inference in #92917. // If this type is a GAT, and of the GAT args resolve to something new, // that means that we must have newly inferred something about the GAT. // We should give up in that case. - // FIXME(generic-associated-types): This only detects one layer of inference, - // which is probably not what we actually want, but fixing it causes some ambiguity: + // + // This only detects one layer of inference, which is probably not what we actually + // want, but fixing it causes some ambiguity: // . if !generics.is_own_empty() && obligation.predicate.args[generics.parent_count..].iter().any(|&p| { @@ -1801,17 +1822,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> { return Some(candidates.pop().unwrap().candidate); } - // We prefer trivial builtin candidates, i.e. builtin impls without any nested - // requirements, over all others. This is a fix for #53123 and prevents winnowing - // from accidentally extending the lifetime of a variable. - let mut trivial_builtin = candidates - .iter() - .filter(|c| matches!(c.candidate, BuiltinCandidate { has_nested: false })); - if let Some(_trivial) = trivial_builtin.next() { - // There should only ever be a single trivial builtin candidate + // We prefer `Sized` candidates over everything. + let mut sized_candidates = + candidates.iter().filter(|c| matches!(c.candidate, SizedCandidate { has_nested: _ })); + if let Some(sized_candidate) = sized_candidates.next() { + // There should only ever be a single sized candidate // as they would otherwise overlap. - debug_assert_eq!(trivial_builtin.next(), None); - return Some(BuiltinCandidate { has_nested: false }); + debug_assert_eq!(sized_candidates.next(), None); + // Only prefer the built-in `Sized` candidate if its nested goals are certain. + // Otherwise, we may encounter failure later on if inference causes this candidate + // to not hold, but a where clause would've applied instead. + if sized_candidate.evaluation.must_apply_modulo_regions() { + return Some(sized_candidate.candidate.clone()); + } else { + return None; + } } // Before we consider where-bounds, we have to deduplicate them here and also @@ -1925,9 +1950,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let mut impl_candidate = None; for c in impls { if let Some(prev) = impl_candidate.replace(c) { - if self.prefer_lhs_over_victim(has_non_region_infer, c, prev) { + if self.prefer_lhs_over_victim(has_non_region_infer, c, prev.0) { // Ok, prefer `c` over the previous entry - } else if self.prefer_lhs_over_victim(has_non_region_infer, prev, c) { + } else if self.prefer_lhs_over_victim(has_non_region_infer, prev, c.0) { // Ok, keep `prev` instead of the new entry impl_candidate = Some(prev); } else { @@ -1940,7 +1965,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // Don't use impl candidates which overlap with other candidates. // This should pretty much only ever happen with malformed impls. if candidates.iter().all(|c| match c.candidate { - BuiltinCandidate { has_nested: _ } + SizedCandidate { has_nested: _ } + | BuiltinCandidate { has_nested: _ } | TransmutabilityCandidate | AutoImplCandidate | ClosureCandidate { .. } @@ -1986,7 +2012,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { &self, has_non_region_infer: bool, (lhs, lhs_evaluation): (DefId, EvaluationResult), - (victim, victim_evaluation): (DefId, EvaluationResult), + victim: DefId, ) -> bool { let tcx = self.tcx(); // See if we can toss out `victim` based on specialization. @@ -2002,14 +2028,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } match tcx.impls_are_allowed_to_overlap(lhs, victim) { - // For #33140 the impl headers must be exactly equal, the trait must not have - // any associated items and there are no where-clauses. - // - // We can just arbitrarily drop one of the impls. - Some(ty::ImplOverlapKind::FutureCompatOrderDepTraitObjects) => { - assert_eq!(lhs_evaluation, victim_evaluation); - true - } // For candidates which already reference errors it doesn't really // matter what we do 🤷 Some(ty::ImplOverlapKind::Permitted { marker: false }) => { @@ -2239,15 +2257,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } - // `Copy` and `Clone` are automatically implemented for an anonymous adt - // if all of its fields are `Copy` and `Clone` - ty::Adt(adt, args) if adt.is_anonymous() => { - // (*) binder moved here - Where(obligation.predicate.rebind( - adt.non_enum_variant().fields.iter().map(|f| f.ty(self.tcx(), args)).collect(), - )) - } - ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => { // Fallback to whatever user-defined impls exist in this case. None @@ -2311,6 +2320,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Never | ty::Char => ty::Binder::dummy(Vec::new()), + // This branch is only for `experimental_default_bounds`. + // Other foreign types were rejected earlier in + // `assemble_candidates_from_auto_impls`. + ty::Foreign(..) => ty::Binder::dummy(Vec::new()), + // FIXME(unsafe_binders): Squash the double binder for now, I guess. ty::UnsafeBinder(_) => return Err(SelectionError::Unimplemented), @@ -2320,8 +2334,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ty::Placeholder(..) | ty::Dynamic(..) | ty::Param(..) - | ty::Foreign(..) - | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) + | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) | ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble constituent types of unexpected type: {:?}", t); @@ -2387,7 +2400,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { cause: ObligationCause<'tcx>, recursion_depth: usize, trait_def_id: DefId, - types: ty::Binder<'tcx, Vec>>, + types: Vec>, ) -> PredicateObligations<'tcx> { // Because the types were potentially derived from // higher-ranked obligations they may reference late-bound @@ -2404,13 +2417,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // 3. Re-bind the regions back to `for<'a> &'a i32 : Copy` types - .as_ref() - .skip_binder() // binder moved -\ - .iter() - .flat_map(|ty| { - let ty: ty::Binder<'tcx, Ty<'tcx>> = types.rebind(*ty); // <----/ - - let placeholder_ty = self.infcx.enter_forall_and_leak_universe(ty); + .into_iter() + .flat_map(|placeholder_ty| { let Normalized { value: normalized_ty, mut obligations } = ensure_sufficient_stack(|| { normalize_with_depth( @@ -2972,14 +2980,14 @@ struct ProvisionalEvaluationCache<'tcx> { /// means the cached value for `F`. map: RefCell, ProvisionalEvaluation>>, - /// The stack of args that we assume to be true because a `WF(arg)` predicate + /// The stack of terms that we assume to be well-formed because a `WF(term)` predicate /// is on the stack above (and because of wellformedness is coinductive). /// In an "ideal" world, this would share a stack with trait predicates in /// `TraitObligationStack`. However, trait predicates are *much* hotter than /// `WellFormed` predicates, and it's very likely that the additional matches /// will have a perf effect. The value here is the well-formed `GenericArg` /// and the depth of the trait predicate *above* that well-formed predicate. - wf_args: RefCell, usize)>>, + wf_args: RefCell, usize)>>, } /// A cache value for the provisional cache: contains the depth-first diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index cb3e81f5477fc..b30fadd3e5b7d 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -18,11 +18,11 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::traits::Obligation; use rustc_middle::bug; use rustc_middle::query::LocalCrate; +use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; -use rustc_session::lint::builtin::{COHERENCE_LEAK_CHECK, ORDER_DEPENDENT_TRAIT_OBJECTS}; +use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, sym}; -use rustc_type_ir::solve::NoSolution; use specialization_graph::GraphExt; use tracing::{debug, instrument}; @@ -557,13 +557,9 @@ fn report_conflicting_impls<'tcx>( let msg = || { format!( - "conflicting implementations of trait `{}`{}{}", + "conflicting implementations of trait `{}`{}", overlap.trait_ref.print_trait_sugared(), overlap.self_ty.map_or_else(String::new, |ty| format!(" for type `{ty}`")), - match used_to_be_allowed { - Some(FutureCompatOverlapErrorKind::OrderDepTraitObjects) => ": (E0119)", - _ => "", - } ) }; @@ -588,7 +584,6 @@ fn report_conflicting_impls<'tcx>( } Some(kind) => { let lint = match kind { - FutureCompatOverlapErrorKind::OrderDepTraitObjects => ORDER_DEPENDENT_TRAIT_OBJECTS, FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK, }; tcx.node_span_lint(lint, tcx.local_def_id_to_hir_id(impl_def_id), impl_span, |err| { diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 19d3561dd595c..9452dca9a4f0f 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -12,7 +12,6 @@ use crate::traits; #[derive(Copy, Clone, Debug)] pub enum FutureCompatOverlapErrorKind { - OrderDepTraitObjects, LeakCheck, } @@ -151,12 +150,6 @@ impl<'tcx> Children { { match overlap_kind { ty::ImplOverlapKind::Permitted { marker: _ } => {} - ty::ImplOverlapKind::FutureCompatOrderDepTraitObjects => { - *last_lint_mut = Some(FutureCompatOverlapError { - error: create_overlap_error(overlap), - kind: FutureCompatOverlapErrorKind::OrderDepTraitObjects, - }); - } } return Ok((false, false)); diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 15f5cf916a48b..035fd38c48aad 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -1,6 +1,7 @@ use std::collections::{BTreeMap, VecDeque}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_hir::LangItem; use rustc_hir::def_id::DefId; use rustc_infer::infer::InferCtxt; pub use rustc_infer::traits::util::*; @@ -289,7 +290,7 @@ impl<'tcx> TypeFolder> for BoundVarReplacer<'_, 'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { + match r.kind() { ty::ReBound(debruijn, _) if debruijn.as_usize() >= self.current_index.as_usize() + self.universe_indices.len() => @@ -407,7 +408,7 @@ impl<'tcx> TypeFolder> for PlaceholderReplacer<'_, 'tcx> { } fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { - let r1 = match *r0 { + let r1 = match r0.kind() { ty::ReVar(vid) => self .infcx .inner @@ -417,7 +418,7 @@ impl<'tcx> TypeFolder> for PlaceholderReplacer<'_, 'tcx> { _ => r0, }; - let r2 = match *r1 { + let r2 = match r1.kind() { ty::RePlaceholder(p) => { let replace_var = self.mapped_regions.get(&p); match replace_var { @@ -504,3 +505,21 @@ impl<'tcx> TypeFolder> for PlaceholderReplacer<'_, 'tcx> { } } } + +pub fn sizedness_fast_path<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>) -> bool { + // Proving `Sized` very often on "obviously sized" types like `&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::Clause(ty::ClauseKind::Trait(trait_ref)) = + predicate.kind().skip_binder() + { + if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) + && trait_ref.self_ty().is_trivially_sized(tcx) + { + debug!("fast path -- trivial sizedness"); + return true; + } + } + + false +} diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 165174c0bcc15..3565c11249ad1 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -197,10 +197,8 @@ fn own_existential_vtable_entries_iter( tcx: TyCtxt<'_>, trait_def_id: DefId, ) -> impl Iterator { - let trait_methods = tcx - .associated_items(trait_def_id) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Fn); + let trait_methods = + tcx.associated_items(trait_def_id).in_definition_order().filter(|item| item.is_fn()); // Now list each method's DefId (for within its trait). let own_entries = trait_methods.filter_map(move |&trait_method| { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 18906a6a8ce0f..08d3b92e9b5ef 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1,12 +1,18 @@ +//! Core logic responsible for determining what it means for various type system +//! primitives to be "well formed". Actually checking whether these primitives are +//! well formed is performed elsewhere (e.g. during type checking or item well formedness +//! checking). + use std::iter; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; use rustc_middle::bug; use rustc_middle::ty::{ - self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, + self, GenericArgsRef, Term, TermKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, }; use rustc_session::parse::feature_err; use rustc_span::def_id::{DefId, LocalDefId}; @@ -15,28 +21,29 @@ use tracing::{debug, instrument, trace}; use crate::infer::InferCtxt; use crate::traits; + /// Returns the set of obligations needed to make `arg` well-formed. /// If `arg` contains unresolved inference variables, this may include /// further WF obligations. However, if `arg` IS an unresolved /// inference variable, returns `None`, because we are not able to -/// make any progress at all. This is to prevent "livelock" where we -/// say "$0 is WF if $0 is WF". +/// make any progress at all. This is to prevent cycles where we +/// say "?0 is WF if ?0 is WF". pub fn obligations<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, recursion_depth: usize, - arg: GenericArg<'tcx>, + term: Term<'tcx>, span: Span, ) -> Option> { - // Handle the "livelock" case (see comment above) by bailing out if necessary. - let arg = match arg.unpack() { - GenericArgKind::Type(ty) => { + // Handle the "cycle" case (see comment above) by bailing out if necessary. + let term = match term.unpack() { + TermKind::Ty(ty) => { match ty.kind() { ty::Infer(ty::TyVar(_)) => { let resolved_ty = infcx.shallow_resolve(ty); if resolved_ty == ty { - // No progress, bail out to prevent "livelock". + // No progress, bail out to prevent cycles. return None; } else { resolved_ty @@ -46,12 +53,12 @@ pub fn obligations<'tcx>( } .into() } - GenericArgKind::Const(ct) => { + TermKind::Const(ct) => { match ct.kind() { ty::ConstKind::Infer(_) => { let resolved = infcx.shallow_resolve_const(ct); if resolved == ct { - // No progress. + // No progress, bail out to prevent cycles. return None; } else { resolved @@ -61,8 +68,6 @@ pub fn obligations<'tcx>( } .into() } - // There is nothing we have to do for lifetimes. - GenericArgKind::Lifetime(..) => return Some(PredicateObligations::new()), }; let mut wf = WfPredicates { @@ -74,11 +79,11 @@ pub fn obligations<'tcx>( recursion_depth, item: None, }; - wf.compute(arg); - debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out); + wf.add_wf_preds_for_term(term); + debug!("wf::obligations({:?}, body_id={:?}) = {:?}", term, body_id, wf.out); let result = wf.normalize(infcx); - debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", arg, body_id, result); + debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", term, body_id, result); Some(result) } @@ -89,23 +94,19 @@ pub fn obligations<'tcx>( pub fn unnormalized_obligations<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - arg: GenericArg<'tcx>, + term: Term<'tcx>, span: Span, body_id: LocalDefId, ) -> Option> { - debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg)); + debug_assert_eq!(term, infcx.resolve_vars_if_possible(term)); // However, if `arg` IS an unresolved inference variable, returns `None`, // because we are not able to make any progress at all. This is to prevent - // "livelock" where we say "$0 is WF if $0 is WF". - if arg.is_non_region_infer() { + // cycles where we say "?0 is WF if ?0 is WF". + if term.is_infer() { return None; } - if let ty::GenericArgKind::Lifetime(..) = arg.unpack() { - return Some(PredicateObligations::new()); - } - let mut wf = WfPredicates { infcx, param_env, @@ -115,7 +116,7 @@ pub fn unnormalized_obligations<'tcx>( recursion_depth: 0, item: None, }; - wf.compute(arg); + wf.add_wf_preds_for_term(term); Some(wf.out) } @@ -140,7 +141,7 @@ pub fn trait_obligations<'tcx>( recursion_depth: 0, item: Some(item), }; - wf.compute_trait_pred(trait_pred, Elaborate::All); + wf.add_wf_preds_for_trait_pred(trait_pred, Elaborate::All); debug!(obligations = ?wf.out); wf.normalize(infcx) } @@ -171,7 +172,7 @@ pub fn clause_obligations<'tcx>( // It's ok to skip the binder here because wf code is prepared for it match clause.kind().skip_binder() { ty::ClauseKind::Trait(t) => { - wf.compute_trait_pred(t, Elaborate::None); + wf.add_wf_preds_for_trait_pred(t, Elaborate::None); } ty::ClauseKind::HostEffect(..) => { // Technically the well-formedness of this predicate is implied by @@ -179,22 +180,22 @@ pub fn clause_obligations<'tcx>( } ty::ClauseKind::RegionOutlives(..) => {} ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { - wf.compute(ty.into()); + wf.add_wf_preds_for_term(ty.into()); } ty::ClauseKind::Projection(t) => { - wf.compute_alias_term(t.projection_term); - wf.compute(t.term.into_arg()); + wf.add_wf_preds_for_alias_term(t.projection_term); + wf.add_wf_preds_for_term(t.term); } ty::ClauseKind::ConstArgHasType(ct, ty) => { - wf.compute(ct.into()); - wf.compute(ty.into()); + wf.add_wf_preds_for_term(ct.into()); + wf.add_wf_preds_for_term(ty.into()); } - ty::ClauseKind::WellFormed(arg) => { - wf.compute(arg); + ty::ClauseKind::WellFormed(term) => { + wf.add_wf_preds_for_term(term); } ty::ClauseKind::ConstEvaluatable(ct) => { - wf.compute(ct.into()); + wf.add_wf_preds_for_term(ct.into()); } } @@ -372,14 +373,18 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. - fn compute_trait_pred(&mut self, trait_pred: ty::TraitPredicate<'tcx>, elaborate: Elaborate) { + fn add_wf_preds_for_trait_pred( + &mut self, + trait_pred: ty::TraitPredicate<'tcx>, + elaborate: Elaborate, + ) { let tcx = self.tcx(); let trait_ref = trait_pred.trait_ref; // Negative trait predicates don't require supertraits to hold, just // that their args are WF. if trait_pred.polarity == ty::PredicatePolarity::Negative { - self.compute_negative_trait_pred(trait_ref); + self.add_wf_preds_for_negative_trait_pred(trait_ref); return; } @@ -416,11 +421,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { .args .iter() .enumerate() - .filter(|(_, arg)| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) - .filter(|(_, arg)| !arg.has_escaping_bound_vars()) - .map(|(i, arg)| { + .filter_map(|(i, arg)| arg.as_term().map(|t| (i, t))) + .filter(|(_, term)| !term.has_escaping_bound_vars()) + .map(|(i, term)| { let mut cause = traits::ObligationCause::misc(self.span, self.body_id); // The first arg is the self ty - use the correct span for it. if i == 0 { @@ -435,9 +438,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause, depth, param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - arg, - ))), + ty::ClauseKind::WellFormed(term), ) }), ); @@ -445,15 +446,17 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // Compute the obligations that are required for `trait_ref` to be WF, // given that it is a *negative* trait predicate. - fn compute_negative_trait_pred(&mut self, trait_ref: ty::TraitRef<'tcx>) { + fn add_wf_preds_for_negative_trait_pred(&mut self, trait_ref: ty::TraitRef<'tcx>) { for arg in trait_ref.args { - self.compute(arg); + if let Some(term) = arg.as_term() { + self.add_wf_preds_for_term(term); + } } } /// Pushes the obligations required for an alias (except inherent) to be WF /// into `self.out`. - fn compute_alias_term(&mut self, data: ty::AliasTerm<'tcx>) { + fn add_wf_preds_for_alias_term(&mut self, data: ty::AliasTerm<'tcx>) { // A projection is well-formed if // // (a) its predicates hold (*) @@ -478,13 +481,13 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let obligations = self.nominal_obligations(data.def_id, data.args); self.out.extend(obligations); - self.compute_projection_args(data.args); + self.add_wf_preds_for_projection_args(data.args); } /// Pushes the obligations required for an inherent alias to be WF /// into `self.out`. // FIXME(inherent_associated_types): Merge this function with `fn compute_alias`. - fn compute_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) { + fn add_wf_preds_for_inherent_projection(&mut self, data: ty::AliasTerm<'tcx>) { // An inherent projection is well-formed if // // (a) its predicates hold (*) @@ -496,7 +499,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { if !data.self_ty().has_escaping_bound_vars() { // FIXME(inherent_associated_types): Should this happen inside of a snapshot? // FIXME(inherent_associated_types): This is incompatible with the new solver and lazy norm! - let args = traits::project::compute_inherent_assoc_ty_args( + let args = traits::project::compute_inherent_assoc_term_args( &mut traits::SelectionContext::new(self.infcx), self.param_env, data, @@ -511,7 +514,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { data.args.visit_with(self); } - fn compute_projection_args(&mut self, args: GenericArgsRef<'tcx>) { + fn add_wf_preds_for_projection_args(&mut self, args: GenericArgsRef<'tcx>) { let tcx = self.tcx(); let cause = self.cause(ObligationCauseCode::WellFormed(None)); let param_env = self.param_env; @@ -519,19 +522,15 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.out.extend( args.iter() - .filter(|arg| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) - .filter(|arg| !arg.has_escaping_bound_vars()) - .map(|arg| { + .filter_map(|arg| arg.as_term()) + .filter(|term| !term.has_escaping_bound_vars()) + .map(|term| { traits::Obligation::with_depth( tcx, cause.clone(), depth, param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - arg, - ))), + ty::ClauseKind::WellFormed(term), ) }), ); @@ -555,10 +554,10 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } } - /// Pushes all the predicates needed to validate that `ty` is WF into `out`. + /// Pushes all the predicates needed to validate that `term` is WF into `out`. #[instrument(level = "debug", skip(self))] - fn compute(&mut self, arg: GenericArg<'tcx>) { - arg.visit_with(self); + fn add_wf_preds_for_term(&mut self, term: Term<'tcx>) { + term.visit_with(self); debug!(?self.out); } @@ -596,7 +595,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { .collect() } - fn from_object_ty( + fn add_wf_preds_for_dyn_ty( &mut self, ty: Ty<'tcx>, data: &'tcx ty::List>, @@ -651,6 +650,57 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { outlives, )); } + + // We don't add any wf predicates corresponding to the trait ref's generic arguments + // which allows code like this to compile: + // ```rust + // trait Trait {} + // fn foo(_: &dyn Trait<[u32]>) {} + // ``` + } + } + + fn add_wf_preds_for_pat_ty(&mut self, base_ty: Ty<'tcx>, pat: ty::Pattern<'tcx>) { + let tcx = self.tcx(); + match *pat { + ty::PatternKind::Range { start, end } => { + let mut check = |c| { + let cause = self.cause(ObligationCauseCode::Misc); + self.out.push(traits::Obligation::with_depth( + tcx, + cause.clone(), + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstArgHasType(c, base_ty), + )), + )); + if !tcx.features().generic_pattern_types() { + if c.has_param() { + if self.span.is_dummy() { + self.tcx() + .dcx() + .delayed_bug("feature error should be reported elsewhere, too"); + } else { + feature_err( + &self.tcx().sess, + sym::generic_pattern_types, + self.span, + "wraparound pattern type ranges cause monomorphization time errors", + ) + .emit(); + } + } + } + }; + check(start); + check(end); + } + ty::PatternKind::Or(patterns) => { + for pat in patterns { + self.add_wf_preds_for_pat_ty(base_ty, pat) + } + } } } } @@ -705,47 +755,9 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { )); } - ty::Pat(subty, pat) => { - self.require_sized(subty, ObligationCauseCode::Misc); - match *pat { - ty::PatternKind::Range { start, end, include_end: _ } => { - let mut check = |c| { - let cause = self.cause(ObligationCauseCode::Misc); - self.out.push(traits::Obligation::with_depth( - tcx, - cause.clone(), - self.recursion_depth, - self.param_env, - ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::ConstArgHasType(c, subty), - )), - )); - if !tcx.features().generic_pattern_types() { - if c.has_param() { - if self.span.is_dummy() { - self.tcx().dcx().delayed_bug( - "feature error should be reported elsewhere, too", - ); - } else { - feature_err( - &self.tcx().sess, - sym::generic_pattern_types, - self.span, - "wraparound pattern type ranges cause monomorphization time errors", - ) - .emit(); - } - } - } - }; - if let Some(start) = start { - check(start) - } - if let Some(end) = end { - check(end) - } - } - } + ty::Pat(base_ty, pat) => { + self.require_sized(base_ty, ObligationCauseCode::Misc); + self.add_wf_preds_for_pat_ty(base_ty, pat); } ty::Tuple(tys) => { @@ -760,12 +772,12 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // Simple cases that are WF if their type args are WF. } - ty::Alias(ty::Projection | ty::Opaque | ty::Weak, data) => { + ty::Alias(ty::Projection | ty::Opaque | ty::Free, data) => { let obligations = self.nominal_obligations(data.def_id, data.args); self.out.extend(obligations); } ty::Alias(ty::Inherent, data) => { - self.compute_inherent_projection(data); + self.add_wf_preds_for_inherent_projection(data.into()); return; // Subtree handled by compute_inherent_projection. } @@ -899,7 +911,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // // Here, we defer WF checking due to higher-ranked // regions. This is perhaps not ideal. - self.from_object_ty(t, data, r); + self.add_wf_preds_for_dyn_ty(t, data, r); // FIXME(#27579) RFC also considers adding trait // obligations that don't refer to Self and @@ -921,11 +933,11 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // 1. Check if they have been resolved, and if so proceed with // THAT type. // 2. If not, we've at least simplified things (e.g., we went - // from `Vec<$0>: WF` to `$0: WF`), so we can + // from `Vec?0>: WF` to `?0: WF`), so we can // register a pending obligation and keep // moving. (Goal is that an "inductive hypothesis" // is satisfied to ensure termination.) - // See also the comment on `fn obligations`, describing "livelock" + // See also the comment on `fn obligations`, describing cycle // prevention, which happens before this can be reached. ty::Infer(_) => { let cause = self.cause(ObligationCauseCode::WellFormed(None)); @@ -950,9 +962,6 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { match c.kind() { ty::ConstKind::Unevaluated(uv) => { if !c.has_escaping_bound_vars() { - let obligations = self.nominal_obligations(uv.def, uv.args); - self.out.extend(obligations); - let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( ty::ClauseKind::ConstEvaluatable(c), )); @@ -964,6 +973,16 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { self.param_env, predicate, )); + + if tcx.def_kind(uv.def) == DefKind::AssocConst + && tcx.def_kind(tcx.parent(uv.def)) == (DefKind::Impl { of_trait: false }) + { + self.add_wf_preds_for_inherent_projection(uv.into()); + return; // Subtree is handled by above function + } else { + let obligations = self.nominal_obligations(uv.def, uv.args); + self.out.extend(obligations); + } } } ty::ConstKind::Infer(_) => { diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index cc329ca33288c..88f02d16c7d5b 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -6,11 +6,11 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::bug; use rustc_middle::traits::CodegenObligationError; -use rustc_middle::ty::{self, PseudoCanonicalInput, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, PseudoCanonicalInput, TyCtxt, TypeVisitableExt, Upcast}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::{ ImplSource, Obligation, ObligationCause, ObligationCtxt, ScrubbedTraitError, SelectionContext, - Unimplemented, + Unimplemented, sizedness_fast_path, }; use tracing::debug; @@ -34,6 +34,13 @@ pub(crate) fn codegen_select_candidate<'tcx>( let (infcx, param_env) = tcx.infer_ctxt().ignoring_regions().build_with_typing_env(typing_env); let mut selcx = SelectionContext::new(&infcx); + if sizedness_fast_path(tcx, trait_ref.upcast(tcx)) { + return Ok(&*tcx.arena.alloc(ImplSource::Builtin( + ty::solve::BuiltinImplSource::Trivial, + Default::default(), + ))); + } + let obligation_cause = ObligationCause::dummy(); let obligation = Obligation::new(tcx, obligation_cause, param_env, trait_ref); @@ -70,7 +77,7 @@ pub(crate) fn codegen_select_candidate<'tcx>( infcx.err_ctxt().report_overflow_obligation_cycle(&cycle); } } - return Err(CodegenObligationError::FulfillmentError); + return Err(CodegenObligationError::Unimplemented); } let impl_source = infcx.resolve_vars_if_possible(impl_source); diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index c9ad096c6e9d0..7771db855d704 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -5,6 +5,7 @@ use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::query::CanonicalPredicateGoal; use rustc_trait_selection::traits::{ EvaluationResult, Obligation, ObligationCause, OverflowError, SelectionContext, TraitQueryMode, + sizedness_fast_path, }; use tracing::debug; @@ -23,6 +24,10 @@ fn evaluate_obligation<'tcx>( debug!("evaluate_obligation: goal={:#?}", goal); let ParamEnvAnd { param_env, value: predicate } = goal; + if sizedness_fast_path(tcx, predicate) { + return Ok(EvaluationResult::EvaluatedToOk); + } + let mut selcx = SelectionContext::with_query_mode(infcx, TraitQueryMode::Canonical); let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate); diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 5f75e242a50fb..6fb483e6dac93 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -10,38 +10,28 @@ use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; -use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{ - compute_implied_outlives_bounds_compat_inner, compute_implied_outlives_bounds_inner, -}; +use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::compute_implied_outlives_bounds_inner; use rustc_trait_selection::traits::query::{CanonicalImpliedOutlivesBoundsGoal, NoSolution}; pub(crate) fn provide(p: &mut Providers) { - *p = Providers { implied_outlives_bounds_compat, ..*p }; *p = Providers { implied_outlives_bounds, ..*p }; } -fn implied_outlives_bounds_compat<'tcx>( - tcx: TyCtxt<'tcx>, - goal: CanonicalImpliedOutlivesBoundsGoal<'tcx>, -) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, - NoSolution, -> { - tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { - let (param_env, ImpliedOutlivesBounds { ty }) = key.into_parts(); - compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty, DUMMY_SP) - }) -} - fn implied_outlives_bounds<'tcx>( tcx: TyCtxt<'tcx>, - goal: CanonicalImpliedOutlivesBoundsGoal<'tcx>, + (goal, disable_implied_bounds_hack): (CanonicalImpliedOutlivesBoundsGoal<'tcx>, bool), ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, NoSolution, > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { let (param_env, ImpliedOutlivesBounds { ty }) = key.into_parts(); - compute_implied_outlives_bounds_inner(ocx, param_env, ty, DUMMY_SP) + compute_implied_outlives_bounds_inner( + ocx, + param_env, + ty, + DUMMY_SP, + disable_implied_bounds_hack, + ) }) } diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs index d2f979bd6d9dc..697c839180312 100644 --- a/compiler/rustc_traits/src/lib.rs +++ b/compiler/rustc_traits/src/lib.rs @@ -2,7 +2,6 @@ // tidy-alphabetical-start #![recursion_limit = "256"] -#![warn(unreachable_pub)] // tidy-alphabetical-end mod codegen; diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index 4c2b7e4769ab0..e52898cc6e242 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -13,7 +13,7 @@ use tracing::debug; pub(crate) fn provide(p: &mut Providers) { *p = Providers { normalize_canonicalized_projection_ty, - normalize_canonicalized_weak_ty, + normalize_canonicalized_free_alias, normalize_canonicalized_inherent_projection_ty, ..*p }; @@ -32,8 +32,14 @@ fn normalize_canonicalized_projection_ty<'tcx>( let selcx = &mut SelectionContext::new(ocx.infcx); let cause = ObligationCause::dummy(); let mut obligations = PredicateObligations::new(); - let answer = - traits::normalize_projection_ty(selcx, param_env, goal, cause, 0, &mut obligations); + let answer = traits::normalize_projection_term( + selcx, + param_env, + goal.into(), + cause, + 0, + &mut obligations, + ); ocx.register_obligations(obligations); // #112047: With projections and opaques, we are able to create opaques that // are recursive (given some generic parameters of the opaque's type variables). @@ -63,11 +69,11 @@ fn normalize_canonicalized_projection_ty<'tcx>( ) } -fn normalize_canonicalized_weak_ty<'tcx>( +fn normalize_canonicalized_free_alias<'tcx>( tcx: TyCtxt<'tcx>, goal: CanonicalAliasGoal<'tcx>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> { - debug!("normalize_canonicalized_weak_ty(goal={:#?})", goal); + debug!("normalize_canonicalized_free_alias(goal={:#?})", goal); tcx.infer_ctxt().enter_canonical_trait_query( &goal, @@ -104,14 +110,14 @@ fn normalize_canonicalized_inherent_projection_ty<'tcx>( let answer = traits::normalize_inherent_projection( selcx, param_env, - goal, + goal.into(), cause, 0, &mut obligations, ); ocx.register_obligations(obligations); - Ok(NormalizationResult { normalized_ty: answer }) + Ok(NormalizationResult { normalized_ty: answer.expect_type() }) }, ) } diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml index f0c783b30020e..246b66d3d0307 100644 --- a/compiler/rustc_transmute/Cargo.toml +++ b/compiler/rustc_transmute/Cargo.toml @@ -10,9 +10,15 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir", optional = true } rustc_middle = { path = "../rustc_middle", optional = true } rustc_span = { path = "../rustc_span", optional = true } +smallvec = "1.8.1" tracing = "0.1" # tidy-alphabetical-end +[dev-dependencies] +# tidy-alphabetical-start +itertools = "0.12" +# tidy-alphabetical-end + [features] rustc = [ "dep:rustc_abi", @@ -20,8 +26,3 @@ rustc = [ "dep:rustc_middle", "dep:rustc_span", ] - -[dev-dependencies] -# tidy-alphabetical-start -itertools = "0.12" -# tidy-alphabetical-end diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs index a29baade42fb7..6d072c336af23 100644 --- a/compiler/rustc_transmute/src/layout/dfa.rs +++ b/compiler/rustc_transmute/src/layout/dfa.rs @@ -1,19 +1,19 @@ use std::fmt; +use std::iter::Peekable; use std::sync::atomic::{AtomicU32, Ordering}; -use tracing::instrument; +use super::{Byte, Ref, Tree, Uninhabited}; +use crate::{Map, Set}; -use super::{Byte, Nfa, Ref, nfa}; -use crate::Map; - -#[derive(PartialEq, Clone, Debug)] +#[derive(PartialEq)] +#[cfg_attr(test, derive(Clone))] pub(crate) struct Dfa where R: Ref, { pub(crate) transitions: Map>, pub(crate) start: State, - pub(crate) accepting: State, + pub(crate) accept: State, } #[derive(PartialEq, Clone, Debug)] @@ -21,7 +21,7 @@ pub(crate) struct Transitions where R: Ref, { - byte_transitions: Map, + byte_transitions: EdgeSet, ref_transitions: Map, } @@ -30,38 +30,19 @@ where R: Ref, { fn default() -> Self { - Self { byte_transitions: Map::default(), ref_transitions: Map::default() } - } -} - -impl Transitions -where - R: Ref, -{ - #[cfg(test)] - fn insert(&mut self, transition: Transition, state: State) { - match transition { - Transition::Byte(b) => { - self.byte_transitions.insert(b, state); - } - Transition::Ref(r) => { - self.ref_transitions.insert(r, state); - } - } + Self { byte_transitions: EdgeSet::empty(), ref_transitions: Map::default() } } } -/// The states in a `Nfa` represent byte offsets. +/// The states in a [`Dfa`] represent byte offsets. #[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Copy, Clone)] -pub(crate) struct State(u32); +pub(crate) struct State(pub(crate) u32); -#[derive(Hash, Eq, PartialEq, Clone, Copy)] -pub(crate) enum Transition -where - R: Ref, -{ - Byte(Byte), - Ref(R), +impl State { + pub(crate) fn new() -> Self { + static COUNTER: AtomicU32 = AtomicU32::new(0); + Self(COUNTER.fetch_add(1, Ordering::SeqCst)) + } } impl fmt::Debug for State { @@ -70,110 +51,449 @@ impl fmt::Debug for State { } } -impl fmt::Debug for Transition -where - R: Ref, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self { - Self::Byte(b) => b.fmt(f), - Self::Ref(r) => r.fmt(f), - } - } -} - impl Dfa where R: Ref, { #[cfg(test)] pub(crate) fn bool() -> Self { - let mut transitions: Map> = Map::default(); + Self::from_transitions(|accept| Transitions { + byte_transitions: EdgeSet::new(Byte::new(0x00..=0x01), accept), + ref_transitions: Map::default(), + }) + } + + pub(crate) fn unit() -> Self { + let transitions: Map> = Map::default(); + let start = State::new(); + let accept = start; + + Self { transitions, start, accept } + } + + pub(crate) fn from_byte(byte: Byte) -> Self { + Self::from_transitions(|accept| Transitions { + byte_transitions: EdgeSet::new(byte, accept), + ref_transitions: Map::default(), + }) + } + + pub(crate) fn from_ref(r: R) -> Self { + Self::from_transitions(|accept| Transitions { + byte_transitions: EdgeSet::empty(), + ref_transitions: [(r, accept)].into_iter().collect(), + }) + } + + fn from_transitions(f: impl FnOnce(State) -> Transitions) -> Self { let start = State::new(); - let accepting = State::new(); + let accept = State::new(); + + Self { transitions: [(start, f(accept))].into_iter().collect(), start, accept } + } + + pub(crate) fn from_tree(tree: Tree) -> Result { + Ok(match tree { + Tree::Byte(b) => Self::from_byte(b), + Tree::Ref(r) => Self::from_ref(r), + Tree::Alt(alts) => { + // Convert and filter the inhabited alternatives. + let mut alts = alts.into_iter().map(Self::from_tree).filter_map(Result::ok); + // If there are no alternatives, return `Uninhabited`. + let dfa = alts.next().ok_or(Uninhabited)?; + // Combine the remaining alternatives with `dfa`. + alts.fold(dfa, |dfa, alt| dfa.union(alt, State::new)) + } + Tree::Seq(elts) => { + let mut dfa = Self::unit(); + for elt in elts.into_iter().map(Self::from_tree) { + dfa = dfa.concat(elt?); + } + dfa + } + }) + } + + /// Concatenate two `Dfa`s. + pub(crate) fn concat(self, other: Self) -> Self { + if self.start == self.accept { + return other; + } else if other.start == other.accept { + return self; + } - transitions.entry(start).or_default().insert(Transition::Byte(Byte::Init(0x00)), accepting); + let start = self.start; + let accept = other.accept; - transitions.entry(start).or_default().insert(Transition::Byte(Byte::Init(0x01)), accepting); + let mut transitions: Map> = self.transitions; - Self { transitions, start, accepting } + for (source, transition) in other.transitions { + let fix_state = |state| if state == other.start { self.accept } else { state }; + let byte_transitions = transition.byte_transitions.map_states(&fix_state); + let ref_transitions = transition + .ref_transitions + .into_iter() + .map(|(r, state)| (r, fix_state(state))) + .collect(); + + let old = transitions + .insert(fix_state(source), Transitions { byte_transitions, ref_transitions }); + assert!(old.is_none()); + } + + Self { transitions, start, accept } } - #[instrument(level = "debug")] - pub(crate) fn from_nfa(nfa: Nfa) -> Self { - let Nfa { transitions: nfa_transitions, start: nfa_start, accepting: nfa_accepting } = nfa; + /// Compute the union of two `Dfa`s. + pub(crate) fn union(self, other: Self, mut new_state: impl FnMut() -> State) -> Self { + // We implement `union` by lazily initializing a set of states + // corresponding to the product of states in `self` and `other`, and + // then add transitions between these states that correspond to where + // they exist between `self` and `other`. - let mut dfa_transitions: Map> = Map::default(); - let mut nfa_to_dfa: Map = Map::default(); - let dfa_start = State::new(); - nfa_to_dfa.insert(nfa_start, dfa_start); + let a = self; + let b = other; - let mut queue = vec![(nfa_start, dfa_start)]; + let accept = new_state(); - while let Some((nfa_state, dfa_state)) = queue.pop() { - if nfa_state == nfa_accepting { - continue; + let mut mapping: Map<(Option, Option), State> = Map::default(); + + let mut mapped = |(a_state, b_state)| { + if Some(a.accept) == a_state || Some(b.accept) == b_state { + // If either `a_state` or `b_state` are accepting, map to a + // common `accept` state. + accept + } else { + *mapping.entry((a_state, b_state)).or_insert_with(&mut new_state) } + }; + + let start = mapped((Some(a.start), Some(b.start))); + let mut transitions: Map> = Map::default(); + let empty_transitions = Transitions::default(); - for (nfa_transition, next_nfa_states) in nfa_transitions[&nfa_state].iter() { - let dfa_transitions = - dfa_transitions.entry(dfa_state).or_insert_with(Default::default); - - let mapped_state = next_nfa_states.iter().find_map(|x| nfa_to_dfa.get(x).copied()); - - let next_dfa_state = match nfa_transition { - &nfa::Transition::Byte(b) => *dfa_transitions - .byte_transitions - .entry(b) - .or_insert_with(|| mapped_state.unwrap_or_else(State::new)), - &nfa::Transition::Ref(r) => *dfa_transitions - .ref_transitions - .entry(r) - .or_insert_with(|| mapped_state.unwrap_or_else(State::new)), - }; - - for &next_nfa_state in next_nfa_states { - nfa_to_dfa.entry(next_nfa_state).or_insert_with(|| { - queue.push((next_nfa_state, next_dfa_state)); - next_dfa_state - }); + struct WorkQueue { + queue: Vec<(Option, Option)>, + // Track all entries ever enqueued to avoid duplicating work. This + // gives us a guarantee that a given (a_state, b_state) pair will + // only ever be visited once. + enqueued: Set<(Option, Option)>, + } + impl WorkQueue { + fn enqueue(&mut self, a_state: Option, b_state: Option) { + if self.enqueued.insert((a_state, b_state)) { + self.queue.push((a_state, b_state)); } } } + let mut queue = WorkQueue { queue: Vec::new(), enqueued: Set::default() }; + queue.enqueue(Some(a.start), Some(b.start)); + + while let Some((a_src, b_src)) = queue.queue.pop() { + let src = mapped((a_src, b_src)); + if src == accept { + // While it's possible to have a DFA whose accept state has + // out-edges, these do not affect the semantics of the DFA, and + // so there's no point in processing them. Continuing here also + // has the advantage of guaranteeing that we only ever process a + // given node in the output DFA once. In particular, with the + // exception of the accept state, we ensure that we only push a + // given node to the `queue` once. This allows the following + // code to assume that we're processing a node we've never + // processed before, which means we never need to merge two edge + // sets - we only ever need to construct a new edge set from + // whole cloth. + continue; + } + + let a_transitions = + a_src.and_then(|a_src| a.transitions.get(&a_src)).unwrap_or(&empty_transitions); + let b_transitions = + b_src.and_then(|b_src| b.transitions.get(&b_src)).unwrap_or(&empty_transitions); + + let byte_transitions = a_transitions.byte_transitions.union( + &b_transitions.byte_transitions, + |a_dst, b_dst| { + assert!(a_dst.is_some() || b_dst.is_some()); + + queue.enqueue(a_dst, b_dst); + mapped((a_dst, b_dst)) + }, + ); + + let ref_transitions = + a_transitions.ref_transitions.keys().chain(b_transitions.ref_transitions.keys()); + + let ref_transitions = ref_transitions + .map(|ref_transition| { + let a_dst = a_transitions.ref_transitions.get(ref_transition).copied(); + let b_dst = b_transitions.ref_transitions.get(ref_transition).copied(); - let dfa_accepting = nfa_to_dfa[&nfa_accepting]; + assert!(a_dst.is_some() || b_dst.is_some()); - Self { transitions: dfa_transitions, start: dfa_start, accepting: dfa_accepting } + queue.enqueue(a_dst, b_dst); + (*ref_transition, mapped((a_dst, b_dst))) + }) + .collect(); + + let old = transitions.insert(src, Transitions { byte_transitions, ref_transitions }); + // See `if src == accept { ... }` above. The comment there explains + // why this assert is valid. + assert_eq!(old, None); + } + + Self { transitions, start, accept } } - pub(crate) fn bytes_from(&self, start: State) -> Option<&Map> { - Some(&self.transitions.get(&start)?.byte_transitions) + pub(crate) fn get_uninit_edge_dst(&self, state: State) -> Option { + let transitions = self.transitions.get(&state)?; + transitions.byte_transitions.get_uninit_edge_dst() } - pub(crate) fn byte_from(&self, start: State, byte: Byte) -> Option { - self.transitions.get(&start)?.byte_transitions.get(&byte).copied() + pub(crate) fn bytes_from(&self, start: State) -> impl Iterator { + self.transitions + .get(&start) + .into_iter() + .flat_map(|transitions| transitions.byte_transitions.iter()) } - pub(crate) fn refs_from(&self, start: State) -> Option<&Map> { - Some(&self.transitions.get(&start)?.ref_transitions) + pub(crate) fn refs_from(&self, start: State) -> impl Iterator { + self.transitions + .get(&start) + .into_iter() + .flat_map(|transitions| transitions.ref_transitions.iter()) + .map(|(r, s)| (*r, *s)) } -} -impl State { - pub(crate) fn new() -> Self { - static COUNTER: AtomicU32 = AtomicU32::new(0); - Self(COUNTER.fetch_add(1, Ordering::SeqCst)) + #[cfg(test)] + pub(crate) fn from_edges>( + start: u32, + accept: u32, + edges: &[(u32, B, u32)], + ) -> Self { + let start = State(start); + let accept = State(accept); + let mut transitions: Map> = Map::default(); + + for &(src, ref edge, dst) in edges.iter() { + transitions.entry(State(src)).or_default().push((edge.clone().into(), State(dst))); + } + + let transitions = transitions + .into_iter() + .map(|(src, edges)| { + ( + src, + Transitions { + byte_transitions: EdgeSet::from_edges(edges), + ref_transitions: Map::default(), + }, + ) + }) + .collect(); + + Self { start, accept, transitions } } } -impl From> for Transition +/// Serialize the DFA using the Graphviz DOT format. +impl fmt::Debug for Dfa where R: Ref, { - fn from(nfa_transition: nfa::Transition) -> Self { - match nfa_transition { - nfa::Transition::Byte(byte) => Transition::Byte(byte), - nfa::Transition::Ref(r) => Transition::Ref(r), + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "digraph {{")?; + writeln!(f, " {:?} [shape = doublecircle]", self.start)?; + writeln!(f, " {:?} [shape = doublecircle]", self.accept)?; + + for (src, transitions) in self.transitions.iter() { + for (t, dst) in transitions.byte_transitions.iter() { + writeln!(f, " {src:?} -> {dst:?} [label=\"{t:?}\"]")?; + } + + for (t, dst) in transitions.ref_transitions.iter() { + writeln!(f, " {src:?} -> {dst:?} [label=\"{t:?}\"]")?; + } + } + + writeln!(f, "}}") + } +} + +use edge_set::EdgeSet; +mod edge_set { + use smallvec::SmallVec; + + use super::*; + + /// The set of outbound byte edges associated with a DFA node. + #[derive(Eq, PartialEq, Clone, Debug)] + pub(super) struct EdgeSet { + // A sequence of byte edges with contiguous byte values and a common + // destination is stored as a single run. + // + // Runs are non-empty, non-overlapping, and stored in ascending order. + runs: SmallVec<[(Byte, S); 1]>, + } + + impl EdgeSet { + pub(crate) fn new(range: Byte, dst: S) -> Self { + let mut this = Self { runs: SmallVec::new() }; + if !range.is_empty() { + this.runs.push((range, dst)); + } + this + } + + pub(crate) fn empty() -> Self { + Self { runs: SmallVec::new() } + } + + #[cfg(test)] + pub(crate) fn from_edges(mut edges: Vec<(Byte, S)>) -> Self + where + S: Ord, + { + edges.sort(); + Self { runs: edges.into() } + } + + pub(crate) fn iter(&self) -> impl Iterator + where + S: Copy, + { + self.runs.iter().copied() + } + + pub(crate) fn get_uninit_edge_dst(&self) -> Option + where + S: Copy, + { + // Uninit is ordered last. + let &(range, dst) = self.runs.last()?; + if range.contains_uninit() { Some(dst) } else { None } + } + + pub(crate) fn map_states(self, mut f: impl FnMut(S) -> SS) -> EdgeSet { + EdgeSet { + // NOTE: It appears as through ` as + // IntoIterator>::IntoIter` and `std::iter::Map` both implement + // `TrustedLen`, which in turn means that this `.collect()` + // allocates the correct number of elements once up-front [1]. + // + // [1] https://doc.rust-lang.org/1.85.0/src/alloc/vec/spec_from_iter_nested.rs.html#47 + runs: self.runs.into_iter().map(|(b, s)| (b, f(s))).collect(), + } + } + + /// Unions two edge sets together. + /// + /// If `u = a.union(b)`, then for each byte value, `u` will have an edge + /// with that byte value and with the destination `join(Some(_), None)`, + /// `join(None, Some(_))`, or `join(Some(_), Some(_))` depending on whether `a`, + /// `b`, or both have an edge with that byte value. + /// + /// If neither `a` nor `b` have an edge with a particular byte value, + /// then no edge with that value will be present in `u`. + pub(crate) fn union( + &self, + other: &Self, + mut join: impl FnMut(Option, Option) -> S, + ) -> EdgeSet + where + S: Copy + Eq, + { + let mut runs: SmallVec<[(Byte, S); 1]> = SmallVec::new(); + let xs = self.runs.iter().copied(); + let ys = other.runs.iter().copied(); + for (range, (x, y)) in union(xs, ys) { + let state = join(x, y); + match runs.last_mut() { + // Merge contiguous runs with a common destination. + Some(&mut (ref mut last_range, ref mut last_state)) + if last_range.end == range.start && *last_state == state => + { + last_range.end = range.end + } + _ => runs.push((range, state)), + } + } + EdgeSet { runs } + } + } +} + +/// Merges two sorted sequences into one sorted sequence. +pub(crate) fn union, Y: Iterator>( + xs: X, + ys: Y, +) -> UnionIter { + UnionIter { xs: xs.peekable(), ys: ys.peekable() } +} + +pub(crate) struct UnionIter { + xs: Peekable, + ys: Peekable, +} + +// FIXME(jswrenn) we'd likely benefit from specializing try_fold here. +impl, Y: Iterator> Iterator + for UnionIter +{ + type Item = (Byte, (Option, Option)); + + fn next(&mut self) -> Option { + use std::cmp::{self, Ordering}; + + let ret; + match (self.xs.peek_mut(), self.ys.peek_mut()) { + (None, None) => { + ret = None; + } + (Some(x), None) => { + ret = Some((x.0, (Some(x.1), None))); + self.xs.next(); + } + (None, Some(y)) => { + ret = Some((y.0, (None, Some(y.1)))); + self.ys.next(); + } + (Some(x), Some(y)) => { + let start; + let end; + let dst; + match x.0.start.cmp(&y.0.start) { + Ordering::Less => { + start = x.0.start; + end = cmp::min(x.0.end, y.0.start); + dst = (Some(x.1), None); + } + Ordering::Greater => { + start = y.0.start; + end = cmp::min(x.0.start, y.0.end); + dst = (None, Some(y.1)); + } + Ordering::Equal => { + start = x.0.start; + end = cmp::min(x.0.end, y.0.end); + dst = (Some(x.1), Some(y.1)); + } + } + ret = Some((Byte { start, end }, dst)); + if start == x.0.start { + x.0.start = end; + } + if start == y.0.start { + y.0.start = end; + } + if x.0.is_empty() { + self.xs.next(); + } + if y.0.is_empty() { + self.ys.next(); + } + } } + ret } } diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs index c4c01a8fac31f..c08bf440734e2 100644 --- a/compiler/rustc_transmute/src/layout/mod.rs +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -1,34 +1,83 @@ use std::fmt::{self, Debug}; use std::hash::Hash; +use std::ops::RangeInclusive; pub(crate) mod tree; pub(crate) use tree::Tree; -pub(crate) mod nfa; -pub(crate) use nfa::Nfa; - pub(crate) mod dfa; -pub(crate) use dfa::Dfa; +pub(crate) use dfa::{Dfa, union}; #[derive(Debug)] pub(crate) struct Uninhabited; -/// An instance of a byte is either initialized to a particular value, or uninitialized. -#[derive(Hash, Eq, PartialEq, Clone, Copy)] -pub(crate) enum Byte { - Uninit, - Init(u8), +/// A range of byte values (including an uninit byte value). +#[derive(Hash, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] +pub(crate) struct Byte { + // An inclusive-exclusive range. We use this instead of `Range` because `Range: !Copy`. + // + // Uninit byte value is represented by 256. + pub(crate) start: u16, + pub(crate) end: u16, +} + +impl Byte { + const UNINIT: u16 = 256; + + #[inline] + fn new(range: RangeInclusive) -> Self { + let start: u16 = (*range.start()).into(); + let end: u16 = (*range.end()).into(); + Byte { start, end: end + 1 } + } + + #[inline] + fn from_val(val: u8) -> Self { + let val: u16 = val.into(); + Byte { start: val, end: val + 1 } + } + + #[inline] + fn uninit() -> Byte { + Byte { start: 0, end: Self::UNINIT + 1 } + } + + #[inline] + fn is_empty(&self) -> bool { + self.start == self.end + } + + #[inline] + fn contains_uninit(&self) -> bool { + self.start <= Self::UNINIT && Self::UNINIT < self.end + } } impl fmt::Debug for Byte { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self { - Self::Uninit => f.write_str("??u8"), - Self::Init(b) => write!(f, "{b:#04x}u8"), + if self.start == Self::UNINIT && self.end == Self::UNINIT + 1 { + write!(f, "uninit") + } else if self.start <= Self::UNINIT && self.end == Self::UNINIT + 1 { + write!(f, "{}..{}|uninit", self.start, self.end - 1) + } else { + write!(f, "{}..{}", self.start, self.end) } } } +impl From> for Byte { + fn from(src: RangeInclusive) -> Self { + Self::new(src) + } +} + +impl From for Byte { + #[inline] + fn from(src: u8) -> Self { + Self::from_val(src) + } +} + pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone { fn has_safety_invariants(&self) -> bool; } @@ -58,6 +107,21 @@ impl Ref for ! { } } +#[cfg(test)] +impl Ref for [(); N] { + fn min_align(&self) -> usize { + N + } + + fn size(&self) -> usize { + N + } + + fn is_mutable(&self) -> bool { + false + } +} + #[cfg(feature = "rustc")] pub mod rustc { use std::fmt::{self, Write}; diff --git a/compiler/rustc_transmute/src/layout/nfa.rs b/compiler/rustc_transmute/src/layout/nfa.rs deleted file mode 100644 index 9c21fd94f03ec..0000000000000 --- a/compiler/rustc_transmute/src/layout/nfa.rs +++ /dev/null @@ -1,169 +0,0 @@ -use std::fmt; -use std::sync::atomic::{AtomicU32, Ordering}; - -use super::{Byte, Ref, Tree, Uninhabited}; -use crate::{Map, Set}; - -/// A non-deterministic finite automaton (NFA) that represents the layout of a type. -/// The transmutability of two given types is computed by comparing their `Nfa`s. -#[derive(PartialEq, Debug)] -pub(crate) struct Nfa -where - R: Ref, -{ - pub(crate) transitions: Map, Set>>, - pub(crate) start: State, - pub(crate) accepting: State, -} - -/// The states in a `Nfa` represent byte offsets. -#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Copy, Clone)] -pub(crate) struct State(u32); - -/// The transitions between states in a `Nfa` reflect bit validity. -#[derive(Hash, Eq, PartialEq, Clone, Copy)] -pub(crate) enum Transition -where - R: Ref, -{ - Byte(Byte), - Ref(R), -} - -impl fmt::Debug for State { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "S_{}", self.0) - } -} - -impl fmt::Debug for Transition -where - R: Ref, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self { - Self::Byte(b) => b.fmt(f), - Self::Ref(r) => r.fmt(f), - } - } -} - -impl Nfa -where - R: Ref, -{ - pub(crate) fn unit() -> Self { - let transitions: Map, Set>> = Map::default(); - let start = State::new(); - let accepting = start; - - Nfa { transitions, start, accepting } - } - - pub(crate) fn from_byte(byte: Byte) -> Self { - let mut transitions: Map, Set>> = Map::default(); - let start = State::new(); - let accepting = State::new(); - - let source = transitions.entry(start).or_default(); - let edge = source.entry(Transition::Byte(byte)).or_default(); - edge.insert(accepting); - - Nfa { transitions, start, accepting } - } - - pub(crate) fn from_ref(r: R) -> Self { - let mut transitions: Map, Set>> = Map::default(); - let start = State::new(); - let accepting = State::new(); - - let source = transitions.entry(start).or_default(); - let edge = source.entry(Transition::Ref(r)).or_default(); - edge.insert(accepting); - - Nfa { transitions, start, accepting } - } - - pub(crate) fn from_tree(tree: Tree) -> Result { - Ok(match tree { - Tree::Byte(b) => Self::from_byte(b), - Tree::Ref(r) => Self::from_ref(r), - Tree::Alt(alts) => { - let mut alts = alts.into_iter().map(Self::from_tree); - let mut nfa = alts.next().ok_or(Uninhabited)??; - for alt in alts { - nfa = nfa.union(alt?); - } - nfa - } - Tree::Seq(elts) => { - let mut nfa = Self::unit(); - for elt in elts.into_iter().map(Self::from_tree) { - nfa = nfa.concat(elt?); - } - nfa - } - }) - } - - /// Concatenate two `Nfa`s. - pub(crate) fn concat(self, other: Self) -> Self { - if self.start == self.accepting { - return other; - } else if other.start == other.accepting { - return self; - } - - let start = self.start; - let accepting = other.accepting; - - let mut transitions: Map, Set>> = self.transitions; - - for (source, transition) in other.transitions { - let fix_state = |state| if state == other.start { self.accepting } else { state }; - let entry = transitions.entry(fix_state(source)).or_default(); - for (edge, destinations) in transition { - let entry = entry.entry(edge).or_default(); - for destination in destinations { - entry.insert(fix_state(destination)); - } - } - } - - Self { transitions, start, accepting } - } - - /// Compute the union of two `Nfa`s. - pub(crate) fn union(self, other: Self) -> Self { - let start = self.start; - let accepting = self.accepting; - - let mut transitions: Map, Set>> = self.transitions.clone(); - - for (&(mut source), transition) in other.transitions.iter() { - // if source is starting state of `other`, replace with starting state of `self` - if source == other.start { - source = self.start; - } - let entry = transitions.entry(source).or_default(); - for (edge, destinations) in transition { - let entry = entry.entry(*edge).or_default(); - for &(mut destination) in destinations { - // if dest is accepting state of `other`, replace with accepting state of `self` - if destination == other.accepting { - destination = self.accepting; - } - entry.insert(destination); - } - } - } - Self { transitions, start, accepting } - } -} - -impl State { - pub(crate) fn new() -> Self { - static COUNTER: AtomicU32 = AtomicU32::new(0); - Self(COUNTER.fetch_add(1, Ordering::SeqCst)) - } -} diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index a21be5dda4ee0..7cf712ce9e977 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -1,4 +1,4 @@ -use std::ops::ControlFlow; +use std::ops::{ControlFlow, RangeInclusive}; use super::{Byte, Def, Ref}; @@ -32,6 +32,22 @@ where Byte(Byte), } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(crate) enum Endian { + Little, + Big, +} + +#[cfg(feature = "rustc")] +impl From for Endian { + fn from(order: rustc_abi::Endian) -> Endian { + match order { + rustc_abi::Endian::Little => Endian::Little, + rustc_abi::Endian::Big => Endian::Big, + } + } +} + impl Tree where D: Def, @@ -54,27 +70,65 @@ where /// A `Tree` containing a single, uninitialized byte. pub(crate) fn uninit() -> Self { - Self::Byte(Byte::Uninit) + Self::Byte(Byte::uninit()) } /// A `Tree` representing the layout of `bool`. pub(crate) fn bool() -> Self { - Self::from_bits(0x00).or(Self::from_bits(0x01)) + Self::byte(0x00..=0x01) } /// A `Tree` whose layout matches that of a `u8`. pub(crate) fn u8() -> Self { - Self::Alt((0u8..=255).map(Self::from_bits).collect()) + Self::byte(0x00..=0xFF) + } + + /// A `Tree` whose layout matches that of a `char`. + pub(crate) fn char(order: Endian) -> Self { + // `char`s can be in the following ranges: + // - [0, 0xD7FF] + // - [0xE000, 10FFFF] + // + // All other `char` values are illegal. We can thus represent a `char` + // as a union of three possible layouts: + // - 00 00 [00, D7] XX + // - 00 00 [E0, FF] XX + // - 00 [01, 10] XX XX + + const _0: RangeInclusive = 0..=0; + const BYTE: RangeInclusive = 0x00..=0xFF; + let x = Self::from_big_endian(order, [_0, _0, 0x00..=0xD7, BYTE]); + let y = Self::from_big_endian(order, [_0, _0, 0xE0..=0xFF, BYTE]); + let z = Self::from_big_endian(order, [_0, 0x01..=0x10, BYTE, BYTE]); + Self::alt([x, y, z]) } - /// A `Tree` whose layout accepts exactly the given bit pattern. - pub(crate) fn from_bits(bits: u8) -> Self { - Self::Byte(Byte::Init(bits)) + /// A `Tree` whose layout matches `std::num::NonZeroXxx`. + #[allow(dead_code)] + pub(crate) fn nonzero(width_in_bytes: u64) -> Self { + const BYTE: RangeInclusive = 0x00..=0xFF; + const NONZERO: RangeInclusive = 0x01..=0xFF; + + (0..width_in_bytes) + .map(|nz_idx| { + (0..width_in_bytes) + .map(|pos| Self::byte(if pos == nz_idx { NONZERO } else { BYTE })) + .fold(Self::unit(), Self::then) + }) + .fold(Self::uninhabited(), Self::or) + } + + pub(crate) fn bytes>(bytes: [B; N]) -> Self { + Self::seq(bytes.map(B::into).map(Self::Byte)) + } + + pub(crate) fn byte(byte: impl Into) -> Self { + Self::Byte(byte.into()) } /// A `Tree` whose layout is a number of the given width. - pub(crate) fn number(width_in_bytes: usize) -> Self { - Self::Seq(vec![Self::u8(); width_in_bytes]) + pub(crate) fn number(width_in_bytes: u64) -> Self { + Self::Seq(vec![Self::u8(); width_in_bytes.try_into().unwrap()]) } /// A `Tree` whose layout is entirely padding of the given width. @@ -125,13 +179,35 @@ where Self::Byte(..) | Self::Ref(..) | Self::Def(..) => true, } } -} -impl Tree -where - D: Def, - R: Ref, -{ + /// Produces a `Tree` which represents a sequence of bytes stored in + /// `order`. + /// + /// `bytes` is taken to be in big-endian byte order, and its order will be + /// swapped if `order == Endian::Little`. + pub(crate) fn from_big_endian>( + order: Endian, + mut bytes: [B; N], + ) -> Self { + if order == Endian::Little { + (&mut bytes[..]).reverse(); + } + + Self::bytes(bytes) + } + + /// Produces a `Tree` where each of the trees in `trees` are sequenced one + /// after another. + pub(crate) fn seq(trees: [Tree; N]) -> Self { + trees.into_iter().fold(Tree::unit(), Self::then) + } + + /// Produces a `Tree` where each of the trees in `trees` are accepted as + /// alternative layouts. + pub(crate) fn alt(trees: [Tree; N]) -> Self { + trees.into_iter().fold(Tree::uninhabited(), Self::or) + } + /// Produces a new `Tree` where `other` is sequenced after `self`. pub(crate) fn then(self, other: Self) -> Self { match (self, other) { @@ -222,17 +298,17 @@ pub(crate) mod rustc { ty::Float(nty) => { let width = nty.bit_width() / 8; - Ok(Self::number(width as _)) + Ok(Self::number(width.try_into().unwrap())) } ty::Int(nty) => { let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; - Ok(Self::number(width as _)) + Ok(Self::number(width.try_into().unwrap())) } ty::Uint(nty) => { let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; - Ok(Self::number(width as _)) + Ok(Self::number(width.try_into().unwrap())) } ty::Tuple(members) => Self::from_tuple((ty, layout), members, cx), @@ -249,11 +325,33 @@ pub(crate) mod rustc { .fold(Tree::unit(), |tree, elt| tree.then(elt))) } - ty::Adt(adt_def, _args_ref) if !ty.is_box() => match adt_def.adt_kind() { - AdtKind::Struct => Self::from_struct((ty, layout), *adt_def, cx), - AdtKind::Enum => Self::from_enum((ty, layout), *adt_def, cx), - AdtKind::Union => Self::from_union((ty, layout), *adt_def, cx), - }, + ty::Adt(adt_def, _args_ref) if !ty.is_box() => { + let (lo, hi) = cx.tcx().layout_scalar_valid_range(adt_def.did()); + + use core::ops::Bound::*; + let is_transparent = adt_def.repr().transparent(); + match (adt_def.adt_kind(), lo, hi) { + (AdtKind::Struct, Unbounded, Unbounded) => { + Self::from_struct((ty, layout), *adt_def, cx) + } + (AdtKind::Struct, Included(1), Included(_hi)) if is_transparent => { + // FIXME(@joshlf): Support `NonZero` types: + // - Check to make sure that the first field is + // numerical + // - Check to make sure that the upper bound is the + // maximum value for the field's type + // - Construct `Self::nonzero` + Err(Err::NotYetSupported) + } + (AdtKind::Enum, Unbounded, Unbounded) => { + Self::from_enum((ty, layout), *adt_def, cx) + } + (AdtKind::Union, Unbounded, Unbounded) => { + Self::from_union((ty, layout), *adt_def, cx) + } + _ => Err(Err::NotYetSupported), + } + } ty::Ref(lifetime, ty, mutability) => { let layout = layout_of(cx, *ty)?; @@ -268,6 +366,8 @@ pub(crate) mod rustc { })) } + ty::Char => Ok(Self::char(cx.tcx().data_layout.endian.into())), + _ => Err(Err::NotYetSupported), } } @@ -450,7 +550,7 @@ pub(crate) mod rustc { &bytes[bytes.len() - size.bytes_usize()..] } }; - Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect()) + Self::Seq(bytes.iter().map(|&b| Self::byte(b)).collect()) } /// Constructs a `Tree` from a union. @@ -514,7 +614,7 @@ pub(crate) mod rustc { } } ty::Tuple(fields) => fields[i.as_usize()], - kind @ _ => unimplemented!( + kind => unimplemented!( "only a subset of `Ty::ty_and_layout_field`'s functionality is implemented. implementation needed for {:?}", kind ), diff --git a/compiler/rustc_transmute/src/layout/tree/tests.rs b/compiler/rustc_transmute/src/layout/tree/tests.rs index 44f50a25c939a..8c3dbbe37ab21 100644 --- a/compiler/rustc_transmute/src/layout/tree/tests.rs +++ b/compiler/rustc_transmute/src/layout/tree/tests.rs @@ -20,23 +20,18 @@ mod prune { #[test] fn seq_1() { - let layout: Tree = - Tree::def(Def::NoSafetyInvariants).then(Tree::from_bits(0x00)); - assert_eq!( - layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), - Tree::from_bits(0x00) - ); + let layout: Tree = Tree::def(Def::NoSafetyInvariants).then(Tree::byte(0x00)); + assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00)); } #[test] fn seq_2() { - let layout: Tree = Tree::from_bits(0x00) - .then(Tree::def(Def::NoSafetyInvariants)) - .then(Tree::from_bits(0x01)); + let layout: Tree = + Tree::byte(0x00).then(Tree::def(Def::NoSafetyInvariants)).then(Tree::byte(0x01)); assert_eq!( layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), - Tree::from_bits(0x00).then(Tree::from_bits(0x01)) + Tree::byte(0x00).then(Tree::byte(0x01)) ); } } @@ -66,7 +61,7 @@ mod prune { #[test] fn invisible_def_in_seq_len_3() { let layout: Tree = Tree::def(Def::NoSafetyInvariants) - .then(Tree::from_bits(0x00)) + .then(Tree::byte(0x00)) .then(Tree::def(Def::HasSafetyInvariants)); assert_eq!( layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), @@ -94,12 +89,9 @@ mod prune { #[test] fn visible_def_in_seq_len_3() { let layout: Tree = Tree::def(Def::NoSafetyInvariants) - .then(Tree::from_bits(0x00)) + .then(Tree::byte(0x00)) .then(Tree::def(Def::NoSafetyInvariants)); - assert_eq!( - layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), - Tree::from_bits(0x00) - ); + assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00)); } } } diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index 81a11f7cfb267..ce18dad55179c 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -1,6 +1,6 @@ // tidy-alphabetical-start +#![cfg_attr(test, feature(test))] #![feature(never_type)] -#![warn(unreachable_pub)] // tidy-alphabetical-end pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set}; diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index 63fabc9c83d93..f76abe50ed343 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -1,10 +1,11 @@ +use rustc_data_structures::stack::ensure_sufficient_stack; use tracing::{debug, instrument, trace}; pub(crate) mod query_context; #[cfg(test)] mod tests; -use crate::layout::{self, Byte, Def, Dfa, Nfa, Ref, Tree, Uninhabited, dfa}; +use crate::layout::{self, Def, Dfa, Ref, Tree, dfa, union}; use crate::maybe_transmutable::query_context::QueryContext; use crate::{Answer, Condition, Map, Reason}; @@ -73,7 +74,7 @@ where /// Answers whether a `Tree` is transmutable into another `Tree`. /// /// This method begins by de-def'ing `src` and `dst`, and prunes private paths from `dst`, - /// then converts `src` and `dst` to `Nfa`s, and computes an answer using those NFAs. + /// then converts `src` and `dst` to `Dfa`s, and computes an answer using those DFAs. #[inline(always)] #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))] pub(crate) fn answer(self) -> Answer<::Ref> { @@ -105,47 +106,30 @@ where trace!(?dst, "pruned dst"); - // Convert `src` from a tree-based representation to an NFA-based + // Convert `src` from a tree-based representation to an DFA-based // representation. If the conversion fails because `src` is uninhabited, // conclude that the transmutation is acceptable, because instances of // the `src` type do not exist. - let src = match Nfa::from_tree(src) { + let src = match Dfa::from_tree(src) { Ok(src) => src, - Err(Uninhabited) => return Answer::Yes, + Err(layout::Uninhabited) => return Answer::Yes, }; - // Convert `dst` from a tree-based representation to an NFA-based + // Convert `dst` from a tree-based representation to an DFA-based // representation. If the conversion fails because `src` is uninhabited, // conclude that the transmutation is unacceptable. Valid instances of // the `dst` type do not exist, either because it's genuinely // uninhabited, or because there are no branches of the tree that are // free of safety invariants. - let dst = match Nfa::from_tree(dst) { + let dst = match Dfa::from_tree(dst) { Ok(dst) => dst, - Err(Uninhabited) => return Answer::No(Reason::DstMayHaveSafetyInvariants), + Err(layout::Uninhabited) => return Answer::No(Reason::DstMayHaveSafetyInvariants), }; MaybeTransmutableQuery { src, dst, assume, context }.answer() } } -impl MaybeTransmutableQuery::Ref>, C> -where - C: QueryContext, -{ - /// Answers whether a `Nfa` is transmutable into another `Nfa`. - /// - /// This method converts `src` and `dst` to DFAs, then computes an answer using those DFAs. - #[inline(always)] - #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))] - pub(crate) fn answer(self) -> Answer<::Ref> { - let Self { src, dst, assume, context } = self; - let src = Dfa::from_nfa(src); - let dst = Dfa::from_nfa(dst); - MaybeTransmutableQuery { src, dst, assume, context }.answer() - } -} - impl MaybeTransmutableQuery::Ref>, C> where C: QueryContext, @@ -166,146 +150,135 @@ where if let Some(answer) = cache.get(&(src_state, dst_state)) { answer.clone() } else { - debug!(?src_state, ?dst_state); - debug!(src = ?self.src); - debug!(dst = ?self.dst); - debug!( - src_transitions_len = self.src.transitions.len(), - dst_transitions_len = self.dst.transitions.len() - ); - let answer = if dst_state == self.dst.accepting { - // truncation: `size_of(Src) >= size_of(Dst)` - // - // Why is truncation OK to do? Because even though the Src is bigger, all we care about - // is whether we have enough data for the Dst to be valid in accordance with what its - // type dictates. - // For example, in a u8 to `()` transmutation, we have enough data available from the u8 - // to transmute it to a `()` (though in this case does `()` really need any data to - // begin with? It doesn't). Same thing with u8 to fieldless struct. - // Now then, why is something like u8 to bool not allowed? That is not because the bool - // is smaller in size, but rather because those 2 bits that we are re-interpreting from - // the u8 could introduce invalid states for the bool type. - // - // So, if it's possible to transmute to a smaller Dst by truncating, and we can guarantee - // that none of the actually-used data can introduce an invalid state for Dst's type, we - // are able to safely transmute, even with truncation. - Answer::Yes - } else if src_state == self.src.accepting { - // extension: `size_of(Src) >= size_of(Dst)` - if let Some(dst_state_prime) = self.dst.byte_from(dst_state, Byte::Uninit) { - self.answer_memo(cache, src_state, dst_state_prime) - } else { - Answer::No(Reason::DstIsTooBig) - } + let answer = ensure_sufficient_stack(|| self.answer_impl(cache, src_state, dst_state)); + if let Some(..) = cache.insert((src_state, dst_state), answer.clone()) { + panic!("failed to correctly cache transmutability") + } + answer + } + } + + fn answer_impl( + &self, + cache: &mut Map<(dfa::State, dfa::State), Answer<::Ref>>, + src_state: dfa::State, + dst_state: dfa::State, + ) -> Answer<::Ref> { + debug!(?src_state, ?dst_state); + debug!(src = ?self.src); + debug!(dst = ?self.dst); + debug!( + src_transitions_len = self.src.transitions.len(), + dst_transitions_len = self.dst.transitions.len() + ); + if dst_state == self.dst.accept { + // truncation: `size_of(Src) >= size_of(Dst)` + // + // Why is truncation OK to do? Because even though the Src is bigger, all we care about + // is whether we have enough data for the Dst to be valid in accordance with what its + // type dictates. + // For example, in a u8 to `()` transmutation, we have enough data available from the u8 + // to transmute it to a `()` (though in this case does `()` really need any data to + // begin with? It doesn't). Same thing with u8 to fieldless struct. + // Now then, why is something like u8 to bool not allowed? That is not because the bool + // is smaller in size, but rather because those 2 bits that we are re-interpreting from + // the u8 could introduce invalid states for the bool type. + // + // So, if it's possible to transmute to a smaller Dst by truncating, and we can guarantee + // that none of the actually-used data can introduce an invalid state for Dst's type, we + // are able to safely transmute, even with truncation. + Answer::Yes + } else if src_state == self.src.accept { + // extension: `size_of(Src) <= size_of(Dst)` + if let Some(dst_state_prime) = self.dst.get_uninit_edge_dst(dst_state) { + self.answer_memo(cache, src_state, dst_state_prime) } else { - let src_quantifier = if self.assume.validity { - // if the compiler may assume that the programmer is doing additional validity checks, - // (e.g.: that `src != 3u8` when the destination type is `bool`) - // then there must exist at least one transition out of `src_state` such that the transmute is viable... - Quantifier::ThereExists - } else { - // if the compiler cannot assume that the programmer is doing additional validity checks, - // then for all transitions out of `src_state`, such that the transmute is viable... - // then there must exist at least one transition out of `dst_state` such that the transmute is viable... - Quantifier::ForAll - }; - - let bytes_answer = src_quantifier.apply( - // for each of the byte transitions out of the `src_state`... - self.src.bytes_from(src_state).unwrap_or(&Map::default()).into_iter().map( - |(&src_validity, &src_state_prime)| { - // ...try to find a matching transition out of `dst_state`. - if let Some(dst_state_prime) = - self.dst.byte_from(dst_state, src_validity) - { - self.answer_memo(cache, src_state_prime, dst_state_prime) - } else if let Some(dst_state_prime) = - // otherwise, see if `dst_state` has any outgoing `Uninit` transitions - // (any init byte is a valid uninit byte) - self.dst.byte_from(dst_state, Byte::Uninit) + Answer::No(Reason::DstIsTooBig) + } + } else { + let src_quantifier = if self.assume.validity { + // if the compiler may assume that the programmer is doing additional validity checks, + // (e.g.: that `src != 3u8` when the destination type is `bool`) + // then there must exist at least one transition out of `src_state` such that the transmute is viable... + Quantifier::ThereExists + } else { + // if the compiler cannot assume that the programmer is doing additional validity checks, + // then for all transitions out of `src_state`, such that the transmute is viable... + // then there must exist at least one transition out of `dst_state` such that the transmute is viable... + Quantifier::ForAll + }; + + let bytes_answer = src_quantifier.apply( + union(self.src.bytes_from(src_state), self.dst.bytes_from(dst_state)).filter_map( + |(_range, (src_state_prime, dst_state_prime))| { + match (src_state_prime, dst_state_prime) { + // No matching transitions in `src`. Skip. + (None, _) => None, + // No matching transitions in `dst`. Fail. + (Some(_), None) => Some(Answer::No(Reason::DstIsBitIncompatible)), + // Matching transitions. Continue with successor states. + (Some(src_state_prime), Some(dst_state_prime)) => { + Some(self.answer_memo(cache, src_state_prime, dst_state_prime)) + } + } + }, + ), + ); + + // The below early returns reflect how this code would behave: + // if self.assume.validity { + // or(bytes_answer, refs_answer) + // } else { + // and(bytes_answer, refs_answer) + // } + // ...if `refs_answer` was computed lazily. The below early + // returns can be deleted without impacting the correctness of + // the algorithm; only its performance. + debug!(?bytes_answer); + match bytes_answer { + Answer::No(_) if !self.assume.validity => return bytes_answer, + Answer::Yes if self.assume.validity => return bytes_answer, + _ => {} + }; + + let refs_answer = src_quantifier.apply( + // for each reference transition out of `src_state`... + self.src.refs_from(src_state).map(|(src_ref, src_state_prime)| { + // ...there exists a reference transition out of `dst_state`... + Quantifier::ThereExists.apply(self.dst.refs_from(dst_state).map( + |(dst_ref, dst_state_prime)| { + if !src_ref.is_mutable() && dst_ref.is_mutable() { + Answer::No(Reason::DstIsMoreUnique) + } else if !self.assume.alignment + && src_ref.min_align() < dst_ref.min_align() { - self.answer_memo(cache, src_state_prime, dst_state_prime) + Answer::No(Reason::DstHasStricterAlignment { + src_min_align: src_ref.min_align(), + dst_min_align: dst_ref.min_align(), + }) + } else if dst_ref.size() > src_ref.size() { + Answer::No(Reason::DstRefIsTooBig { src: src_ref, dst: dst_ref }) } else { - // otherwise, we've exhausted our options. - // the DFAs, from this point onwards, are bit-incompatible. - Answer::No(Reason::DstIsBitIncompatible) - } - }, - ), - ); - - // The below early returns reflect how this code would behave: - // if self.assume.validity { - // or(bytes_answer, refs_answer) - // } else { - // and(bytes_answer, refs_answer) - // } - // ...if `refs_answer` was computed lazily. The below early - // returns can be deleted without impacting the correctness of - // the algorithm; only its performance. - debug!(?bytes_answer); - match bytes_answer { - Answer::No(_) if !self.assume.validity => return bytes_answer, - Answer::Yes if self.assume.validity => return bytes_answer, - _ => {} - }; - - let refs_answer = src_quantifier.apply( - // for each reference transition out of `src_state`... - self.src.refs_from(src_state).unwrap_or(&Map::default()).into_iter().map( - |(&src_ref, &src_state_prime)| { - // ...there exists a reference transition out of `dst_state`... - Quantifier::ThereExists.apply( - self.dst - .refs_from(dst_state) - .unwrap_or(&Map::default()) - .into_iter() - .map(|(&dst_ref, &dst_state_prime)| { - if !src_ref.is_mutable() && dst_ref.is_mutable() { - Answer::No(Reason::DstIsMoreUnique) - } else if !self.assume.alignment - && src_ref.min_align() < dst_ref.min_align() - { - Answer::No(Reason::DstHasStricterAlignment { - src_min_align: src_ref.min_align(), - dst_min_align: dst_ref.min_align(), - }) - } else if dst_ref.size() > src_ref.size() { - Answer::No(Reason::DstRefIsTooBig { - src: src_ref, - dst: dst_ref, - }) - } else { - // ...such that `src` is transmutable into `dst`, if - // `src_ref` is transmutability into `dst_ref`. - and( - Answer::If(Condition::IfTransmutable { - src: src_ref, - dst: dst_ref, - }), - self.answer_memo( - cache, - src_state_prime, - dst_state_prime, - ), - ) - } + // ...such that `src` is transmutable into `dst`, if + // `src_ref` is transmutability into `dst_ref`. + and( + Answer::If(Condition::IfTransmutable { + src: src_ref, + dst: dst_ref, }), - ) + self.answer_memo(cache, src_state_prime, dst_state_prime), + ) + } }, - ), - ); + )) + }), + ); - if self.assume.validity { - or(bytes_answer, refs_answer) - } else { - and(bytes_answer, refs_answer) - } - }; - if let Some(..) = cache.insert((src_state, dst_state), answer.clone()) { - panic!("failed to correctly cache transmutability") + if self.assume.validity { + or(bytes_answer, refs_answer) + } else { + and(bytes_answer, refs_answer) } - answer } } } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs index f8b59bdf32684..214da101be375 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs @@ -8,9 +8,17 @@ pub(crate) trait QueryContext { #[cfg(test)] pub(crate) mod test { + use std::marker::PhantomData; + use super::QueryContext; - pub(crate) struct UltraMinimal; + pub(crate) struct UltraMinimal(PhantomData); + + impl Default for UltraMinimal { + fn default() -> Self { + Self(PhantomData) + } + } #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] pub(crate) enum Def { @@ -24,9 +32,9 @@ pub(crate) mod test { } } - impl QueryContext for UltraMinimal { + impl QueryContext for UltraMinimal { type Def = Def; - type Ref = !; + type Ref = R; } } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index 4d81382eba02c..0227ad71ae660 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -1,100 +1,185 @@ +extern crate test; + use itertools::Itertools; use super::query_context::test::{Def, UltraMinimal}; -use crate::maybe_transmutable::MaybeTransmutableQuery; -use crate::{Reason, layout}; +use crate::{Answer, Assume, Reason, layout}; + +type Tree = layout::Tree; +type Dfa = layout::Dfa; + +trait Representation { + fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer; +} + +impl Representation for Tree { + fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer { + crate::maybe_transmutable::MaybeTransmutableQuery::new( + src, + dst, + assume, + UltraMinimal::default(), + ) + .answer() + } +} + +impl Representation for Dfa { + fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer { + crate::maybe_transmutable::MaybeTransmutableQuery::new( + src, + dst, + assume, + UltraMinimal::default(), + ) + .answer() + } +} + +fn is_transmutable( + src: &R, + dst: &R, + assume: Assume, +) -> crate::Answer { + let src = src.clone(); + let dst = dst.clone(); + // The only dimension of the transmutability analysis we want to test + // here is the safety analysis. To ensure this, we disable all other + // toggleable aspects of the transmutability analysis. + R::is_transmutable(src, dst, assume) +} mod safety { use super::*; use crate::Answer; - type Tree = layout::Tree; - const DST_HAS_SAFETY_INVARIANTS: Answer = Answer::No(crate::Reason::DstMayHaveSafetyInvariants); - fn is_transmutable(src: &Tree, dst: &Tree, assume_safety: bool) -> crate::Answer { - let src = src.clone(); - let dst = dst.clone(); - // The only dimension of the transmutability analysis we want to test - // here is the safety analysis. To ensure this, we disable all other - // toggleable aspects of the transmutability analysis. - let assume = crate::Assume { - alignment: true, - lifetimes: true, - validity: true, - safety: assume_safety, - }; - crate::maybe_transmutable::MaybeTransmutableQuery::new(src, dst, assume, UltraMinimal) - .answer() - } - #[test] fn src_safe_dst_safe() { let src = Tree::Def(Def::NoSafetyInvariants).then(Tree::u8()); let dst = Tree::Def(Def::NoSafetyInvariants).then(Tree::u8()); - assert_eq!(is_transmutable(&src, &dst, false), Answer::Yes); - assert_eq!(is_transmutable(&src, &dst, true), Answer::Yes); + assert_eq!(is_transmutable(&src, &dst, Assume::default()), Answer::Yes); + assert_eq!( + is_transmutable(&src, &dst, Assume { safety: true, ..Assume::default() }), + Answer::Yes + ); } #[test] fn src_safe_dst_unsafe() { let src = Tree::Def(Def::NoSafetyInvariants).then(Tree::u8()); let dst = Tree::Def(Def::HasSafetyInvariants).then(Tree::u8()); - assert_eq!(is_transmutable(&src, &dst, false), DST_HAS_SAFETY_INVARIANTS); - assert_eq!(is_transmutable(&src, &dst, true), Answer::Yes); + assert_eq!(is_transmutable(&src, &dst, Assume::default()), DST_HAS_SAFETY_INVARIANTS); + assert_eq!( + is_transmutable(&src, &dst, Assume { safety: true, ..Assume::default() }), + Answer::Yes + ); } #[test] fn src_unsafe_dst_safe() { let src = Tree::Def(Def::HasSafetyInvariants).then(Tree::u8()); let dst = Tree::Def(Def::NoSafetyInvariants).then(Tree::u8()); - assert_eq!(is_transmutable(&src, &dst, false), Answer::Yes); - assert_eq!(is_transmutable(&src, &dst, true), Answer::Yes); + assert_eq!(is_transmutable(&src, &dst, Assume::default()), Answer::Yes); + assert_eq!( + is_transmutable(&src, &dst, Assume { safety: true, ..Assume::default() }), + Answer::Yes + ); } #[test] fn src_unsafe_dst_unsafe() { let src = Tree::Def(Def::HasSafetyInvariants).then(Tree::u8()); let dst = Tree::Def(Def::HasSafetyInvariants).then(Tree::u8()); - assert_eq!(is_transmutable(&src, &dst, false), DST_HAS_SAFETY_INVARIANTS); - assert_eq!(is_transmutable(&src, &dst, true), Answer::Yes); + assert_eq!(is_transmutable(&src, &dst, Assume::default()), DST_HAS_SAFETY_INVARIANTS); + assert_eq!( + is_transmutable(&src, &dst, Assume { safety: true, ..Assume::default() }), + Answer::Yes + ); + } +} + +mod size { + use super::*; + + #[test] + fn size() { + let small = Tree::number(1); + let large = Tree::number(2); + + for alignment in [false, true] { + for lifetimes in [false, true] { + for safety in [false, true] { + for validity in [false, true] { + let assume = Assume { alignment, lifetimes, safety, validity }; + assert_eq!( + is_transmutable(&small, &large, assume), + Answer::No(Reason::DstIsTooBig), + "assume: {assume:?}" + ); + assert_eq!( + is_transmutable(&large, &small, assume), + Answer::Yes, + "assume: {assume:?}" + ); + } + } + } + } } } mod bool { use super::*; - use crate::Answer; #[test] fn should_permit_identity_transmutation_tree() { - let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( - layout::Tree::::bool(), - layout::Tree::::bool(), - crate::Assume { alignment: false, lifetimes: false, validity: true, safety: false }, - UltraMinimal, - ) - .answer(); - assert_eq!(answer, Answer::Yes); + let src = Tree::bool(); + assert_eq!(is_transmutable(&src, &src, Assume::default()), Answer::Yes); + assert_eq!( + is_transmutable(&src, &src, Assume { validity: true, ..Assume::default() }), + Answer::Yes + ); } #[test] fn should_permit_identity_transmutation_dfa() { - let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( - layout::Dfa::::bool(), - layout::Dfa::::bool(), - crate::Assume { alignment: false, lifetimes: false, validity: true, safety: false }, - UltraMinimal, - ) - .answer(); - assert_eq!(answer, Answer::Yes); + let src = Dfa::bool(); + assert_eq!(is_transmutable(&src, &src, Assume::default()), Answer::Yes); + assert_eq!( + is_transmutable(&src, &src, Assume { validity: true, ..Assume::default() }), + Answer::Yes + ); + } + + #[test] + fn transmute_u8() { + let bool = &Tree::bool(); + let u8 = &Tree::u8(); + for (src, dst, assume_validity, answer) in [ + (bool, u8, false, Answer::Yes), + (bool, u8, true, Answer::Yes), + (u8, bool, false, Answer::No(Reason::DstIsBitIncompatible)), + (u8, bool, true, Answer::Yes), + ] { + assert_eq!( + is_transmutable( + src, + dst, + Assume { validity: assume_validity, ..Assume::default() } + ), + answer + ); + } } #[test] fn should_permit_validity_expansion_and_reject_contraction() { - let b0 = layout::Tree::::from_bits(0); - let b1 = layout::Tree::::from_bits(1); - let b2 = layout::Tree::::from_bits(2); + let b0 = layout::Tree::::byte(0); + let b1 = layout::Tree::::byte(1); + let b2 = layout::Tree::::byte(2); let alts = [b0, b1, b2]; @@ -104,7 +189,7 @@ mod bool { let into_set = |alts: Vec<_>| { #[cfg(feature = "rustc")] - let mut set = crate::Set::default(); + let mut set = rustc_data_structures::fx::FxIndexSet::default(); #[cfg(not(feature = "rustc"))] let mut set = std::collections::HashSet::new(); set.extend(alts); @@ -122,13 +207,7 @@ mod bool { if src_set.is_subset(&dst_set) { assert_eq!( Answer::Yes, - MaybeTransmutableQuery::new( - src_layout.clone(), - dst_layout.clone(), - crate::Assume { validity: false, ..crate::Assume::default() }, - UltraMinimal, - ) - .answer(), + is_transmutable(&src_layout, &dst_layout, Assume::default()), "{:?} SHOULD be transmutable into {:?}", src_layout, dst_layout @@ -136,13 +215,11 @@ mod bool { } else if !src_set.is_disjoint(&dst_set) { assert_eq!( Answer::Yes, - MaybeTransmutableQuery::new( - src_layout.clone(), - dst_layout.clone(), - crate::Assume { validity: true, ..crate::Assume::default() }, - UltraMinimal, - ) - .answer(), + is_transmutable( + &src_layout, + &dst_layout, + Assume { validity: true, ..Assume::default() } + ), "{:?} SHOULD be transmutable (assuming validity) into {:?}", src_layout, dst_layout @@ -150,13 +227,7 @@ mod bool { } else { assert_eq!( Answer::No(Reason::DstIsBitIncompatible), - MaybeTransmutableQuery::new( - src_layout.clone(), - dst_layout.clone(), - crate::Assume { validity: false, ..crate::Assume::default() }, - UltraMinimal, - ) - .answer(), + is_transmutable(&src_layout, &dst_layout, Assume::default()), "{:?} should NOT be transmutable into {:?}", src_layout, dst_layout @@ -166,3 +237,221 @@ mod bool { } } } + +mod uninit { + use super::*; + + #[test] + fn size() { + let mu = Tree::uninit(); + let u8 = Tree::u8(); + + for alignment in [false, true] { + for lifetimes in [false, true] { + for safety in [false, true] { + for validity in [false, true] { + let assume = Assume { alignment, lifetimes, safety, validity }; + + let want = if validity { + Answer::Yes + } else { + Answer::No(Reason::DstIsBitIncompatible) + }; + + assert_eq!(is_transmutable(&mu, &u8, assume), want, "assume: {assume:?}"); + assert_eq!( + is_transmutable(&u8, &mu, assume), + Answer::Yes, + "assume: {assume:?}" + ); + } + } + } + } + } +} + +mod alt { + use super::*; + use crate::Answer; + + #[test] + fn should_permit_identity_transmutation() { + type Tree = layout::Tree; + + let x = Tree::Seq(vec![Tree::byte(0), Tree::byte(0)]); + let y = Tree::Seq(vec![Tree::bool(), Tree::byte(1)]); + let layout = Tree::Alt(vec![x, y]); + + let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( + layout.clone(), + layout.clone(), + crate::Assume::default(), + UltraMinimal::default(), + ) + .answer(); + assert_eq!(answer, Answer::Yes, "layout:{:#?}", layout); + } +} + +mod union { + use super::*; + + #[test] + fn union() { + let [a, b, c, d] = [0, 1, 2, 3]; + let s = Dfa::from_edges(a, d, &[(a, 0, b), (b, 0, d), (a, 1, c), (c, 1, d)]); + + let t = Dfa::from_edges(a, c, &[(a, 1, b), (b, 0, c)]); + + let mut ctr = 0; + let new_state = || { + let state = crate::layout::dfa::State(ctr); + ctr += 1; + state + }; + + let u = s.clone().union(t.clone(), new_state); + + let expected_u = + Dfa::from_edges(b, a, &[(b, 0..=0, c), (b, 1..=1, d), (d, 0..=1, a), (c, 0..=0, a)]); + + assert_eq!(u, expected_u); + + assert_eq!(is_transmutable(&s, &u, Assume::default()), Answer::Yes); + assert_eq!(is_transmutable(&t, &u, Assume::default()), Answer::Yes); + } +} + +mod char { + use super::*; + use crate::layout::tree::Endian; + + #[test] + fn should_permit_valid_transmutation() { + for order in [Endian::Big, Endian::Little] { + use Answer::*; + let char_layout = layout::Tree::::char(order); + + // `char`s can be in the following ranges: + // - [0, 0xD7FF] + // - [0xE000, 10FFFF] + // + // This loop synthesizes a singleton-validity type for the extremes + // of each range, and for one past the end of the extremes of each + // range. + let no = No(Reason::DstIsBitIncompatible); + for (src, answer) in [ + (0u32, Yes), + (0xD7FF, Yes), + (0xD800, no.clone()), + (0xDFFF, no.clone()), + (0xE000, Yes), + (0x10FFFF, Yes), + (0x110000, no.clone()), + (0xFFFF0000, no.clone()), + (0xFFFFFFFF, no), + ] { + let src_layout = + layout::tree::Tree::::from_big_endian(order, src.to_be_bytes()); + + let a = is_transmutable(&src_layout, &char_layout, Assume::default()); + assert_eq!(a, answer, "endian:{order:?},\nsrc:{src:x}"); + } + } + } +} + +mod nonzero { + use super::*; + use crate::{Answer, Reason}; + + const NONZERO_BYTE_WIDTHS: [u64; 5] = [1, 2, 4, 8, 16]; + + #[test] + fn should_permit_identity_transmutation() { + for width in NONZERO_BYTE_WIDTHS { + let layout = layout::Tree::::nonzero(width); + assert_eq!(is_transmutable(&layout, &layout, Assume::default()), Answer::Yes); + } + } + + #[test] + fn should_permit_valid_transmutation() { + for width in NONZERO_BYTE_WIDTHS { + use Answer::*; + + let num = layout::Tree::::number(width); + let nz = layout::Tree::::nonzero(width); + + let a = is_transmutable(&num, &nz, Assume::default()); + assert_eq!(a, No(Reason::DstIsBitIncompatible), "width:{width}"); + + let a = is_transmutable(&nz, &num, Assume::default()); + assert_eq!(a, Yes, "width:{width}"); + } + } +} + +mod r#ref { + use super::*; + + #[test] + fn should_permit_identity_transmutation() { + type Tree = crate::layout::Tree; + + for validity in [false, true] { + let layout = Tree::Seq(vec![Tree::byte(0x00), Tree::Ref([()])]); + + let assume = Assume { validity, ..Assume::default() }; + + let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( + layout.clone(), + layout, + assume, + UltraMinimal::default(), + ) + .answer(); + assert_eq!( + answer, + Answer::If(crate::Condition::IfTransmutable { src: [()], dst: [()] }) + ); + } + } +} + +mod benches { + use std::hint::black_box; + + use test::Bencher; + + use super::*; + + #[bench] + fn bench_dfa_from_tree(b: &mut Bencher) { + let num = Tree::number(8).prune(&|_| false); + let num = black_box(num); + + b.iter(|| { + let _ = black_box(Dfa::from_tree(num.clone())); + }) + } + + #[bench] + fn bench_transmute(b: &mut Bencher) { + let num = Tree::number(8).prune(&|_| false); + let dfa = black_box(Dfa::from_tree(num).unwrap()); + + b.iter(|| { + let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( + dfa.clone(), + dfa.clone(), + Assume::default(), + UltraMinimal::default(), + ) + .answer(); + let answer = std::hint::black_box(answer); + assert_eq!(answer, Answer::Yes); + }) + } +} diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml index 4c7a57f2931bf..ce08b300cc800 100644 --- a/compiler/rustc_ty_utils/Cargo.toml +++ b/compiler/rustc_ty_utils/Cargo.toml @@ -20,6 +20,5 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -rustc_type_ir = { path = "../rustc_type_ir" } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_ty_utils/messages.ftl b/compiler/rustc_ty_utils/messages.ftl index ae795ef2214b8..8bc7bf10865f5 100644 --- a/compiler/rustc_ty_utils/messages.ftl +++ b/compiler/rustc_ty_utils/messages.ftl @@ -14,6 +14,8 @@ ty_utils_borrow_not_supported = borrowing is not supported in generic constants ty_utils_box_not_supported = allocations are not allowed in generic constants +ty_utils_by_use_not_supported = .use is not allowed in generic constants + ty_utils_closure_and_return_not_supported = closures and function keywords are not supported in generic constants ty_utils_const_block_not_supported = const blocks are not supported in generic constants @@ -42,8 +44,6 @@ ty_utils_logical_op_not_supported = unsupported operation in generic constants, ty_utils_loop_not_supported = loops and loop control flow are not supported in generic constants -ty_utils_multiple_array_fields_simd_type = monomorphising SIMD type `{$ty}` with more than one array field - ty_utils_needs_drop_overflow = overflow while checking whether `{$query_ty}` requires drop ty_utils_never_to_any_not_supported = coercing the `never` type is not supported in generic constants diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index e317768ff602c..2b49d7ac8b599 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -55,31 +55,6 @@ fn fn_sig_for_fn_abi<'tcx>( sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); } - // Modify `fn() -> impl Future` to `fn() -> dyn* Future`. - if let ty::InstanceKind::ReifyShim(def_id, _) = instance.def - && let Some((rpitit_def_id, fn_args)) = - tcx.return_position_impl_trait_in_trait_shim_data(def_id) - { - let fn_args = fn_args.instantiate(tcx, args); - let rpitit_args = - fn_args.extend_to(tcx, rpitit_def_id, |param, _| match param.kind { - ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - ty::GenericParamDefKind::Type { .. } - | ty::GenericParamDefKind::Const { .. } => { - unreachable!("rpitit should have no addition ty/ct") - } - }); - let dyn_star_ty = Ty::new_dynamic( - tcx, - tcx.item_bounds_to_existential_predicates(rpitit_def_id, rpitit_args), - tcx.lifetimes.re_erased, - ty::DynStar, - ); - let mut inputs_and_output = sig.inputs_and_output.to_vec(); - *inputs_and_output.last_mut().unwrap() = dyn_star_ty; - sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); - } - sig } ty::Closure(def_id, args) => { @@ -269,7 +244,7 @@ fn fn_sig_for_fn_abi<'tcx>( fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: ExternAbi, c_variadic: bool) -> Conv { use rustc_abi::ExternAbi::*; match tcx.sess.target.adjust_abi(abi, c_variadic) { - RustIntrinsic | Rust | RustCall => Conv::Rust, + Rust | RustCall => Conv::Rust, // This is intentionally not using `Conv::Cold`, as that has to preserve // even SIMD registers, which is generally not a good trade-off. @@ -372,7 +347,8 @@ fn adjust_for_rust_scalar<'tcx>( None }; if let Some(kind) = kind { - attrs.pointee_align = Some(pointee.align); + attrs.pointee_align = + Some(pointee.align.min(cx.tcx().sess.target.max_reliable_alignment())); // `Box` are not necessarily dereferenceable for the entire duration of the function as // they can be deallocated at any time. Same for non-frozen shared references (see @@ -436,10 +412,7 @@ fn fn_abi_sanity_check<'tcx>( ) { let tcx = cx.tcx(); - if spec_abi == ExternAbi::Rust - || spec_abi == ExternAbi::RustCall - || spec_abi == ExternAbi::RustCold - { + if spec_abi.is_rustic_abi() { if arg.layout.is_zst() { // Casting closures to function pointers depends on ZST closure types being // omitted entirely in the calling convention. @@ -578,8 +551,10 @@ fn fn_abi_new_uncached<'tcx>( extra_args }; - let is_drop_in_place = - determined_fn_def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::DropInPlace)); + let is_drop_in_place = determined_fn_def_id.is_some_and(|def_id| { + tcx.is_lang_item(def_id, LangItem::DropInPlace) + || tcx.is_lang_item(def_id, LangItem::AsyncDropInPlace) + }); let arg_of = |ty: Ty<'tcx>, arg_idx: Option| -> Result<_, &'tcx FnAbiError<'tcx>> { let span = tracing::debug_span!("arg_of"); @@ -687,8 +662,8 @@ fn fn_abi_adjust_for_abi<'tcx>( let tcx = cx.tcx(); - if abi == ExternAbi::Rust || abi == ExternAbi::RustCall || abi == ExternAbi::RustIntrinsic { - fn_abi.adjust_for_rust_abi(cx, abi); + if abi.is_rustic_abi() { + fn_abi.adjust_for_rust_abi(cx); // Look up the deduced parameter attributes for this function, if we have its def ID and // we're optimizing in non-incremental mode. We'll tag its parameters with those attributes diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index c8034f4e7b9f6..f14a45aa1e3b9 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -1,12 +1,12 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; +use rustc_hir::definitions::{DefPathData, DisambiguatorState}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_span::kw; pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { @@ -21,7 +21,7 @@ pub(crate) fn provide(providers: &mut Providers) { } fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] { - let item = tcx.hir().expect_item(def_id); + let item = tcx.hir_expect_item(def_id); match item.kind { hir::ItemKind::Trait(.., trait_item_refs) => { // We collect RPITITs for each trait method's return type and create a @@ -96,7 +96,7 @@ fn impl_item_implementor_ids(tcx: TyCtxt<'_>, impl_id: DefId) -> DefIdMap fn associated_item(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AssocItem { let id = tcx.local_def_id_to_hir_id(def_id); let parent_def_id = tcx.hir_get_parent_item(id); - let parent_item = tcx.hir().expect_item(parent_def_id.def_id); + let parent_item = tcx.hir_expect_item(parent_def_id.def_id); match parent_item.kind { hir::ItemKind::Impl(impl_) => { if let Some(impl_item_ref) = impl_.items.iter().find(|i| i.id.owner_id.def_id == def_id) @@ -129,39 +129,51 @@ fn associated_item(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AssocItem { fn associated_item_from_trait_item_ref(trait_item_ref: &hir::TraitItemRef) -> ty::AssocItem { let owner_id = trait_item_ref.id.owner_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), + let name = trait_item_ref.ident.name; + let kind = match trait_item_ref.kind { + hir::AssocItemKind::Const => ty::AssocKind::Const { name }, + hir::AssocItemKind::Fn { has_self } => ty::AssocKind::Fn { name, has_self }, + hir::AssocItemKind::Type => ty::AssocKind::Type { data: ty::AssocTypeData::Normal(name) }, }; ty::AssocItem { - name: trait_item_ref.ident.name, kind, def_id: owner_id.to_def_id(), trait_item_def_id: Some(owner_id.to_def_id()), container: ty::AssocItemContainer::Trait, - fn_has_self_parameter: has_self, - opt_rpitit_info: None, } } fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::AssocItem { let def_id = impl_item_ref.id.owner_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), + let name = impl_item_ref.ident.name; + let kind = match impl_item_ref.kind { + hir::AssocItemKind::Const => ty::AssocKind::Const { name }, + hir::AssocItemKind::Fn { has_self } => ty::AssocKind::Fn { name, has_self }, + hir::AssocItemKind::Type => ty::AssocKind::Type { data: ty::AssocTypeData::Normal(name) }, }; ty::AssocItem { - name: impl_item_ref.ident.name, kind, def_id: def_id.to_def_id(), trait_item_def_id: impl_item_ref.trait_item_def_id, container: ty::AssocItemContainer::Impl, - fn_has_self_parameter: has_self, - opt_rpitit_info: None, + } +} +struct RPITVisitor { + rpits: FxIndexSet, +} + +impl<'tcx> Visitor<'tcx> for RPITVisitor { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { + if let hir::TyKind::OpaqueDef(opaq) = ty.kind + && self.rpits.insert(opaq.def_id) + { + for bound in opaq.bounds { + intravisit::walk_param_bound(self, bound); + } + } + intravisit::walk_ty(self, ty) } } @@ -182,26 +194,9 @@ fn associated_types_for_impl_traits_in_associated_fn( match tcx.def_kind(parent_def_id) { DefKind::Trait => { - struct RPITVisitor { - rpits: FxIndexSet, - } - - impl<'tcx> Visitor<'tcx> for RPITVisitor { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { - if let hir::TyKind::OpaqueDef(opaq) = ty.kind - && self.rpits.insert(opaq.def_id) - { - for bound in opaq.bounds { - intravisit::walk_param_bound(self, bound); - } - } - intravisit::walk_ty(self, ty) - } - } - let mut visitor = RPITVisitor { rpits: FxIndexSet::default() }; - if let Some(output) = tcx.hir().get_fn_output(fn_def_id) { + if let Some(output) = tcx.hir_get_fn_output(fn_def_id) { visitor.visit_fn_ret_ty(output); tcx.arena.alloc_from_iter(visitor.rpits.iter().map(|opaque_ty_def_id| { @@ -251,8 +246,23 @@ fn associated_type_for_impl_trait_in_trait( let trait_def_id = tcx.local_parent(fn_def_id); assert_eq!(tcx.def_kind(trait_def_id), DefKind::Trait); + // Collect all opaque types in return position for the method and use + // the index as the disambiguator to make an unique def path. + let mut visitor = RPITVisitor { rpits: FxIndexSet::default() }; + visitor.visit_fn_ret_ty(tcx.hir_get_fn_output(fn_def_id).unwrap()); + let disambiguator = visitor.rpits.get_index_of(&opaque_ty_def_id).unwrap().try_into().unwrap(); + let span = tcx.def_span(opaque_ty_def_id); - let trait_assoc_ty = tcx.at(span).create_def(trait_def_id, kw::Empty, DefKind::AssocTy); + // Also use the method name to create an unique def path. + let data = DefPathData::AnonAssocTy(tcx.item_name(fn_def_id.to_def_id())); + let trait_assoc_ty = tcx.at(span).create_def( + trait_def_id, + // No name because this is an anonymous associated type. + None, + DefKind::AssocTy, + Some(data), + &mut DisambiguatorState::with(trait_def_id, data, disambiguator), + ); let local_def_id = trait_assoc_ty.def_id(); let def_id = local_def_id.to_def_id(); @@ -263,16 +273,15 @@ fn associated_type_for_impl_trait_in_trait( trait_assoc_ty.def_ident_span(Some(span)); trait_assoc_ty.associated_item(ty::AssocItem { - name: kw::Empty, - kind: ty::AssocKind::Type, + kind: ty::AssocKind::Type { + data: ty::AssocTypeData::Rpitit(ImplTraitInTraitData::Trait { + fn_def_id: fn_def_id.to_def_id(), + opaque_def_id: opaque_ty_def_id.to_def_id(), + }), + }, def_id, trait_item_def_id: None, container: ty::AssocItemContainer::Trait, - fn_has_self_parameter: false, - opt_rpitit_info: Some(ImplTraitInTraitData::Trait { - fn_def_id: fn_def_id.to_def_id(), - opaque_def_id: opaque_ty_def_id.to_def_id(), - }), }); // Copy visility of the containing function. @@ -304,7 +313,22 @@ fn associated_type_for_impl_trait_in_impl( hir::FnRetTy::DefaultReturn(_) => tcx.def_span(impl_fn_def_id), hir::FnRetTy::Return(ty) => ty.span, }; - let impl_assoc_ty = tcx.at(span).create_def(impl_local_def_id, kw::Empty, DefKind::AssocTy); + + // Use the same disambiguator and method name as the anon associated type in the trait. + let disambiguated_data = tcx.def_key(trait_assoc_def_id).disambiguated_data; + let DefPathData::AnonAssocTy(name) = disambiguated_data.data else { + bug!("expected anon associated type") + }; + let data = DefPathData::AnonAssocTy(name); + + let impl_assoc_ty = tcx.at(span).create_def( + impl_local_def_id, + // No name because this is an anonymous associated type. + None, + DefKind::AssocTy, + Some(data), + &mut DisambiguatorState::with(impl_local_def_id, data, disambiguated_data.disambiguator), + ); let local_def_id = impl_assoc_ty.def_id(); let def_id = local_def_id.to_def_id(); @@ -315,13 +339,14 @@ fn associated_type_for_impl_trait_in_impl( impl_assoc_ty.def_ident_span(Some(span)); impl_assoc_ty.associated_item(ty::AssocItem { - name: kw::Empty, - kind: ty::AssocKind::Type, + kind: ty::AssocKind::Type { + data: ty::AssocTypeData::Rpitit(ImplTraitInTraitData::Impl { + fn_def_id: impl_fn_def_id.to_def_id(), + }), + }, def_id, trait_item_def_id: Some(trait_assoc_def_id), container: ty::AssocItemContainer::Impl, - fn_has_self_parameter: false, - opt_rpitit_info: Some(ImplTraitInTraitData::Impl { fn_def_id: impl_fn_def_id.to_def_id() }), }); // Copy visility of the containing function. diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs index 2157ab3c4022c..bb2c4172b0877 100644 --- a/compiler/rustc_ty_utils/src/common_traits.rs +++ b/compiler/rustc_ty_utils/src/common_traits.rs @@ -10,6 +10,13 @@ fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty is_item_raw(tcx, query, LangItem::Copy) } +fn is_use_cloned_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, +) -> bool { + is_item_raw(tcx, query, LangItem::UseCloned) +} + fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { is_item_raw(tcx, query, LangItem::Sized) } @@ -22,6 +29,13 @@ fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, T is_item_raw(tcx, query, LangItem::Unpin) } +fn is_async_drop_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, +) -> bool { + is_item_raw(tcx, query, LangItem::AsyncDrop) +} + fn is_item_raw<'tcx>( tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, @@ -33,5 +47,13 @@ fn is_item_raw<'tcx>( } pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unpin_raw, ..*providers }; + *providers = Providers { + is_copy_raw, + is_use_cloned_raw, + is_sized_raw, + is_freeze_raw, + is_unpin_raw, + is_async_drop_raw, + ..*providers + }; } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index ece796b3c71ce..b275cd382ab83 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -230,7 +230,9 @@ fn recurse_build<'tcx>( error(GenericConstantTooComplexSub::LoopNotSupported(node.span))? } ExprKind::Box { .. } => error(GenericConstantTooComplexSub::BoxNotSupported(node.span))?, - + ExprKind::ByUse { .. } => { + error(GenericConstantTooComplexSub::ByUseNotSupported(node.span))? + } ExprKind::Unary { .. } => unreachable!(), // we handle valid unary/binary ops above ExprKind::Binary { .. } => { @@ -317,6 +319,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { | thir::ExprKind::Box { .. } | thir::ExprKind::If { .. } | thir::ExprKind::Call { .. } + | thir::ExprKind::ByUse { .. } | thir::ExprKind::Deref { .. } | thir::ExprKind::Binary { .. } | thir::ExprKind::LogicalOp { .. } diff --git a/compiler/rustc_ty_utils/src/errors.rs b/compiler/rustc_ty_utils/src/errors.rs index 5497d7d0bd2a0..0298e7e0e9511 100644 --- a/compiler/rustc_ty_utils/src/errors.rs +++ b/compiler/rustc_ty_utils/src/errors.rs @@ -55,6 +55,8 @@ pub(crate) enum GenericConstantTooComplexSub { BoxNotSupported(#[primary_span] Span), #[label(ty_utils_binary_not_supported)] BinaryNotSupported(#[primary_span] Span), + #[label(ty_utils_by_use_not_supported)] + ByUseNotSupported(#[primary_span] Span), #[label(ty_utils_logical_op_not_supported)] LogicalOpNotSupported(#[primary_span] Span), #[label(ty_utils_assign_not_supported)] @@ -82,12 +84,6 @@ pub(crate) struct ZeroLengthSimdType<'tcx> { pub ty: Ty<'tcx>, } -#[derive(Diagnostic)] -#[diag(ty_utils_multiple_array_fields_simd_type)] -pub(crate) struct MultipleArrayFieldsSimdType<'tcx> { - pub ty: Ty<'tcx>, -} - #[derive(Diagnostic)] #[diag(ty_utils_oversized_simd_type)] pub(crate) struct OversizedSimdType<'tcx> { diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 6205578bf7476..6fa763f18ef19 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -4,10 +4,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_middle::bug; use rustc_middle::query::Providers; -use rustc_middle::ty::fold::fold_regions; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, fold_regions}; +use rustc_middle::{bug, span_bug}; use rustc_span::Span; pub(crate) fn provide(providers: &mut Providers) { @@ -22,7 +21,8 @@ pub(crate) fn provide(providers: &mut Providers) { } fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'tcx>, Span)] { - match tcx.def_kind(def_id) { + let kind = tcx.def_kind(def_id); + match kind { DefKind::Fn => { let sig = tcx.fn_sig(def_id).instantiate_identity(); let liberated_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig); @@ -76,7 +76,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' { let orig_lt = tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()); - if matches!(*orig_lt, ty::ReLateParam(..)) { + if matches!(orig_lt.kind(), ty::ReLateParam(..)) { mapping.insert( orig_lt, ty::Region::new_early_param( @@ -122,32 +122,38 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' } } DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)), - DefKind::OpaqueTy => bug!("implied bounds are not defined for opaques"), - DefKind::Mod + DefKind::Static { .. } + | DefKind::Const + | DefKind::AnonConst + | DefKind::InlineConst | DefKind::Struct | DefKind::Union | DefKind::Enum - | DefKind::Variant | DefKind::Trait - | DefKind::TyAlias - | DefKind::ForeignTy | DefKind::TraitAlias + | DefKind::TyAlias => ty::List::empty(), + DefKind::OpaqueTy + | DefKind::Mod + | DefKind::Variant + | DefKind::ForeignTy | DefKind::TyParam - | DefKind::Const | DefKind::ConstParam - | DefKind::Static { .. } | DefKind::Ctor(_, _) | DefKind::Macro(_) | DefKind::ExternCrate | DefKind::Use | DefKind::ForeignMod - | DefKind::AnonConst - | DefKind::InlineConst | DefKind::Field | DefKind::LifetimeParam | DefKind::GlobalAsm | DefKind::Closure - | DefKind::SyntheticCoroutineBody => ty::List::empty(), + | DefKind::SyntheticCoroutineBody => { + span_bug!( + tcx.def_span(def_id), + "`assumed_wf_types` not defined for {} `{def_id:?}`", + kind.descr(def_id.to_def_id()) + ); + } } } @@ -161,7 +167,7 @@ fn fn_sig_spans(tcx: TyCtxt<'_>, def_id: LocalDefId) -> impl Iterator, def_id: LocalDefId) -> impl Iterator { - let item = tcx.hir().expect_item(def_id); + let item = tcx.hir_expect_item(def_id); if let hir::ItemKind::Impl(impl_) = item.kind { let trait_args = impl_ .of_trait diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index d059d6dcd139c..166e8f1934299 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -5,13 +5,11 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError}; -use rustc_middle::ty::util::AsyncDropGlueMorphology; use rustc_middle::ty::{ - self, GenericArgsRef, Instance, PseudoCanonicalInput, TyCtxt, TypeVisitableExt, + self, ClosureKind, GenericArgsRef, Instance, PseudoCanonicalInput, TyCtxt, TypeVisitableExt, }; use rustc_span::sym; use rustc_trait_selection::traits; -use rustc_type_ir::ClosureKind; use tracing::debug; use traits::translate_args; @@ -42,20 +40,26 @@ fn resolve_instance_raw<'tcx>( if ty.needs_drop(tcx, typing_env) { debug!(" => nontrivial drop glue"); match *ty.kind() { + ty::Coroutine(coroutine_def_id, ..) => { + // FIXME: sync drop of coroutine with async drop (generate both versions?) + // Currently just ignored + if tcx.optimized_mir(coroutine_def_id).coroutine_drop_async().is_some() { + ty::InstanceKind::DropGlue(def_id, None) + } else { + ty::InstanceKind::DropGlue(def_id, Some(ty)) + } + } ty::Closure(..) | ty::CoroutineClosure(..) - | ty::Coroutine(..) | ty::Tuple(..) | ty::Adt(..) | ty::Dynamic(..) | ty::Array(..) | ty::Slice(..) - | ty::UnsafeBinder(..) => {} + | ty::UnsafeBinder(..) => ty::InstanceKind::DropGlue(def_id, Some(ty)), // Drop shims can only be built from ADTs. _ => return Ok(None), } - - ty::InstanceKind::DropGlue(def_id, Some(ty)) } else { debug!(" => trivial drop glue"); ty::InstanceKind::DropGlue(def_id, None) @@ -63,7 +67,7 @@ fn resolve_instance_raw<'tcx>( } else if tcx.is_lang_item(def_id, LangItem::AsyncDropInPlace) { let ty = args.type_at(0); - if ty.async_drop_glue_morphology(tcx) != AsyncDropGlueMorphology::Noop { + if ty.needs_async_drop(tcx, typing_env) { match *ty.kind() { ty::Closure(..) | ty::CoroutineClosure(..) @@ -77,11 +81,14 @@ fn resolve_instance_raw<'tcx>( _ => return Ok(None), } debug!(" => nontrivial async drop glue ctor"); - ty::InstanceKind::AsyncDropGlueCtorShim(def_id, Some(ty)) + ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) } else { debug!(" => trivial async drop glue ctor"); - ty::InstanceKind::AsyncDropGlueCtorShim(def_id, None) + ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) } + } else if tcx.is_async_drop_in_place_coroutine(def_id) { + let ty = args.type_at(0); + ty::InstanceKind::AsyncDropGlue(def_id, ty) } else { debug!(" => free item"); ty::InstanceKind::Item(def_id) @@ -107,11 +114,9 @@ fn resolve_associated_item<'tcx>( let input = typing_env.as_query_input(trait_ref); let vtbl = match tcx.codegen_select_candidate(input) { Ok(vtbl) => vtbl, - Err( - CodegenObligationError::Ambiguity - | CodegenObligationError::Unimplemented - | CodegenObligationError::FulfillmentError, - ) => return Ok(None), + Err(CodegenObligationError::Ambiguity | CodegenObligationError::Unimplemented) => { + return Ok(None); + } Err(CodegenObligationError::UnconstrainedParam(guar)) => return Err(guar), }; @@ -152,6 +157,7 @@ fn resolve_associated_item<'tcx>( match typing_env.typing_mode { ty::TypingMode::Coherence | ty::TypingMode::Analysis { .. } + | ty::TypingMode::Borrowck { .. } | ty::TypingMode::PostBorrowckAnalysis { .. } => false, ty::TypingMode::PostAnalysis => !trait_ref.still_further_specializable(), } @@ -229,7 +235,7 @@ fn resolve_associated_item<'tcx>( tcx.ensure_ok().compare_impl_item(leaf_def_item)?; } - Some(ty::Instance::new(leaf_def.item.def_id, args)) + Some(ty::Instance::new_raw(leaf_def.item.def_id, args)) } traits::ImplSource::Builtin(BuiltinImplSource::Object(_), _) => { let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_args); @@ -274,7 +280,7 @@ fn resolve_associated_item<'tcx>( // Use the default `fn clone_from` from `trait Clone`. let args = tcx.erase_regions(rcvr_args); - Some(ty::Instance::new(trait_item_id, args)) + Some(ty::Instance::new_raw(trait_item_id, args)) } } else if tcx.is_lang_item(trait_ref.def_id, LangItem::FnPtrTrait) { if tcx.is_lang_item(trait_item_id, LangItem::FnPtrAddr) { @@ -323,7 +329,7 @@ fn resolve_associated_item<'tcx>( // sync with the built-in trait implementations (since all of the // implementations return `FnOnce::Output`). if ty::ClosureKind::FnOnce == args.as_coroutine_closure().kind() { - Some(Instance::new(coroutine_closure_def_id, args)) + Some(Instance::new_raw(coroutine_closure_def_id, args)) } else { Some(Instance { def: ty::InstanceKind::ConstructCoroutineInClosureShim { @@ -356,7 +362,7 @@ fn resolve_associated_item<'tcx>( args, }) } else { - Some(Instance::new(coroutine_closure_def_id, args)) + Some(Instance::new_raw(coroutine_closure_def_id, args)) } } ty::Closure(closure_def_id, args) => { @@ -375,14 +381,13 @@ fn resolve_associated_item<'tcx>( let name = tcx.item_name(trait_item_id); assert_eq!(name, sym::transmute); let args = tcx.erase_regions(rcvr_args); - Some(ty::Instance::new(trait_item_id, args)) + Some(ty::Instance::new_raw(trait_item_id, args)) } else { Instance::try_resolve_item_for_coroutine(tcx, trait_item_id, trait_id, rcvr_args) } } traits::ImplSource::Param(..) - | traits::ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) - | traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => None, + | traits::ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) => None, }) } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 1a82d17130755..ad57555bd24d3 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -1,36 +1,28 @@ -use std::fmt::Debug; -use std::iter; - use hir::def_id::DefId; use rustc_abi::Integer::{I8, I32}; use rustc_abi::Primitive::{self, Float, Int, Pointer}; use rustc_abi::{ - AbiAndPrefAlign, AddressSpace, Align, BackendRepr, FIRST_VARIANT, FieldIdx, FieldsShape, - HasDataLayout, Layout, LayoutCalculatorError, LayoutData, Niche, ReprOptions, Scalar, Size, - StructKind, TagEncoding, VariantIdx, Variants, WrappingRange, + AddressSpace, BackendRepr, FIRST_VARIANT, FieldIdx, FieldsShape, HasDataLayout, Layout, + LayoutCalculatorError, LayoutData, Niche, ReprOptions, Scalar, Size, StructKind, TagEncoding, + VariantIdx, Variants, WrappingRange, }; use rustc_hashes::Hash64; -use rustc_index::bit_set::DenseBitSet; -use rustc_index::{IndexSlice, IndexVec}; +use rustc_index::IndexVec; use rustc_middle::bug; -use rustc_middle::mir::{CoroutineLayout, CoroutineSavedLocal}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ - FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, MAX_SIMD_LANES, TyAndLayout, + FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, AdtDef, CoroutineArgsExt, EarlyBinder, GenericArgsRef, PseudoCanonicalInput, Ty, TyCtxt, - TypeVisitableExt, + self, AdtDef, CoroutineArgsExt, EarlyBinder, PseudoCanonicalInput, Ty, TyCtxt, TypeVisitableExt, }; use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use rustc_span::{Symbol, sym}; -use tracing::{debug, instrument, trace}; +use tracing::{debug, instrument}; use {rustc_abi as abi, rustc_hir as hir}; -use crate::errors::{ - MultipleArrayFieldsSimdType, NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType, -}; +use crate::errors::{NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType}; mod invariant; @@ -126,20 +118,23 @@ fn map_error<'tcx>( .delayed_bug(format!("computed impossible repr (packed enum?): {ty:?}")); LayoutError::ReferencesError(guar) } + LayoutCalculatorError::ZeroLengthSimdType => { + // Can't be caught in typeck if the array length is generic. + cx.tcx().dcx().emit_fatal(ZeroLengthSimdType { ty }) + } + LayoutCalculatorError::OversizedSimdType { max_lanes } => { + // Can't be caught in typeck if the array length is generic. + cx.tcx().dcx().emit_fatal(OversizedSimdType { ty, max_lanes }) + } + LayoutCalculatorError::NonPrimitiveSimdType(field) => { + // This error isn't caught in typeck, e.g., if + // the element type of the vector is generic. + cx.tcx().dcx().emit_fatal(NonPrimitiveSimdType { ty, e_ty: field.ty }) + } }; error(cx, err) } -fn univariant_uninterned<'tcx>( - cx: &LayoutCx<'tcx>, - ty: Ty<'tcx>, - fields: &IndexSlice>, - kind: StructKind, -) -> Result, &'tcx LayoutError<'tcx>> { - let repr = ReprOptions::default(); - cx.calc.univariant(fields, &repr, kind).map_err(|err| map_error(cx, ty, err)) -} - fn extract_const_value<'tcx>( cx: &LayoutCx<'tcx>, ty: Ty<'tcx>, @@ -189,7 +184,15 @@ fn layout_of_uncached<'tcx>( } let tcx = cx.tcx(); + + // layout of `async_drop_in_place::{closure}` in case, + // when T is a coroutine, contains this internal coroutine's ref + let dl = cx.data_layout(); + let map_layout = |result: Result<_, _>| match result { + Ok(layout) => Ok(tcx.mk_layout(layout)), + Err(err) => Err(map_error(cx, ty, err)), + }; let scalar_unit = |value: Primitive| { let size = value.size(dl); assert!(size.bits() <= 128); @@ -197,8 +200,10 @@ fn layout_of_uncached<'tcx>( }; let scalar = |value: Primitive| tcx.mk_layout(LayoutData::scalar(cx, scalar_unit(value))); - let univariant = |fields: &IndexSlice>, kind| { - Ok(tcx.mk_layout(univariant_uninterned(cx, ty, fields, kind)?)) + let univariant = |tys: &[Ty<'tcx>], kind| { + let fields = tys.iter().map(|ty| cx.layout_of(*ty)).try_collect::>()?; + let repr = ReprOptions::default(); + map_layout(cx.calc.univariant(&fields, &repr, kind)) }; debug_assert!(!ty.has_non_region_infer()); @@ -207,24 +212,45 @@ fn layout_of_uncached<'tcx>( let layout = cx.layout_of(ty)?.layout; let mut layout = LayoutData::clone(&layout.0); match *pat { - ty::PatternKind::Range { start, end, include_end } => { + ty::PatternKind::Range { start, end } => { if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = &mut layout.backend_repr { - if let Some(start) = start { - scalar.valid_range_mut().start = extract_const_value(cx, ty, start)? - .try_to_bits(tcx, cx.typing_env) - .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; - } - if let Some(end) = end { - let mut end = extract_const_value(cx, ty, end)? - .try_to_bits(tcx, cx.typing_env) - .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; - if !include_end { - end = end.wrapping_sub(1); + scalar.valid_range_mut().start = extract_const_value(cx, ty, start)? + .try_to_bits(tcx, cx.typing_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; + + scalar.valid_range_mut().end = extract_const_value(cx, ty, end)? + .try_to_bits(tcx, cx.typing_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; + + // FIXME(pattern_types): create implied bounds from pattern types in signatures + // that require that the range end is >= the range start so that we can't hit + // this error anymore without first having hit a trait solver error. + // Very fuzzy on the details here, but pattern types are an internal impl detail, + // so we can just go with this for now + if scalar.is_signed() { + let range = scalar.valid_range_mut(); + let start = layout.size.sign_extend(range.start); + let end = layout.size.sign_extend(range.end); + if end < start { + let guar = tcx.dcx().err(format!( + "pattern type ranges cannot wrap: {start}..={end}" + )); + + return Err(error(cx, LayoutError::ReferencesError(guar))); } - scalar.valid_range_mut().end = end; - } + } else { + let range = scalar.valid_range_mut(); + if range.end < range.start { + let guar = tcx.dcx().err(format!( + "pattern type ranges cannot wrap: {}..={}", + range.start, range.end + )); + + return Err(error(cx, LayoutError::ReferencesError(guar))); + } + }; let niche = Niche { offset: Size::ZERO, @@ -233,13 +259,95 @@ fn layout_of_uncached<'tcx>( }; layout.largest_niche = Some(niche); - - tcx.mk_layout(layout) } else { bug!("pattern type with range but not scalar layout: {ty:?}, {layout:?}") } } + ty::PatternKind::Or(variants) => match *variants[0] { + ty::PatternKind::Range { .. } => { + if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { + let variants: Result, _> = variants + .iter() + .map(|pat| match *pat { + ty::PatternKind::Range { start, end } => Ok(( + extract_const_value(cx, ty, start) + .unwrap() + .try_to_bits(tcx, cx.typing_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?, + extract_const_value(cx, ty, end) + .unwrap() + .try_to_bits(tcx, cx.typing_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?, + )), + ty::PatternKind::Or(_) => { + unreachable!("mixed or patterns are not allowed") + } + }) + .collect(); + let mut variants = variants?; + if !scalar.is_signed() { + let guar = tcx.dcx().err(format!( + "only signed integer base types are allowed for or-pattern pattern types at present" + )); + + return Err(error(cx, LayoutError::ReferencesError(guar))); + } + variants.sort(); + if variants.len() != 2 { + let guar = tcx + .dcx() + .err(format!("the only or-pattern types allowed are two range patterns that are directly connected at their overflow site")); + + return Err(error(cx, LayoutError::ReferencesError(guar))); + } + + // first is the one starting at the signed in range min + let mut first = variants[0]; + let mut second = variants[1]; + if second.0 + == layout.size.truncate(layout.size.signed_int_min() as u128) + { + (second, first) = (first, second); + } + + if layout.size.sign_extend(first.1) >= layout.size.sign_extend(second.0) + { + let guar = tcx.dcx().err(format!( + "only non-overlapping pattern type ranges are allowed at present" + )); + + return Err(error(cx, LayoutError::ReferencesError(guar))); + } + if layout.size.signed_int_max() as u128 != second.1 { + let guar = tcx.dcx().err(format!( + "one pattern needs to end at `{ty}::MAX`, but was {} instead", + second.1 + )); + + return Err(error(cx, LayoutError::ReferencesError(guar))); + } + + // Now generate a wrapping range (which aren't allowed in surface syntax). + scalar.valid_range_mut().start = second.0; + scalar.valid_range_mut().end = first.1; + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(cx), + }; + + layout.largest_niche = Some(niche); + } else { + bug!( + "pattern type with range but not scalar layout: {ty:?}, {layout:?}" + ) + } + } + ty::PatternKind::Or(..) => bug!("patterns cannot have nested or patterns"), + }, } + tcx.mk_layout(layout) } // Basic scalars. @@ -267,7 +375,7 @@ fn layout_of_uncached<'tcx>( } // The never type. - ty::Never => tcx.mk_layout(cx.calc.layout_of_never_type()), + ty::Never => tcx.mk_layout(LayoutData::never_type(cx)), // Potentially-wide pointers. ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => { @@ -338,7 +446,7 @@ fn layout_of_uncached<'tcx>( }; // Effectively a (ptr, meta) tuple. - tcx.mk_layout(cx.calc.scalar_pair(data_ptr, metadata)) + tcx.mk_layout(LayoutData::scalar_pair(cx, data_ptr, metadata)) } ty::Dynamic(_, _, ty::DynStar) => { @@ -346,7 +454,7 @@ fn layout_of_uncached<'tcx>( data.valid_range_mut().start = 0; let mut vtable = scalar_unit(Pointer(AddressSpace::DATA)); vtable.valid_range_mut().start = 1; - tcx.mk_layout(cx.calc.scalar_pair(data, vtable)) + tcx.mk_layout(LayoutData::scalar_pair(cx, data, vtable)) } // Arrays and slices. @@ -356,228 +464,112 @@ fn layout_of_uncached<'tcx>( .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; let element = cx.layout_of(element)?; - let size = element - .size - .checked_mul(count, dl) - .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?; - - let abi = BackendRepr::Memory { sized: true }; - - let largest_niche = if count != 0 { element.largest_niche } else { None }; - let uninhabited = if count != 0 { element.uninhabited } else { false }; - - tcx.mk_layout(LayoutData { - variants: Variants::Single { index: FIRST_VARIANT }, - fields: FieldsShape::Array { stride: element.size, count }, - backend_repr: abi, - largest_niche, - uninhabited, - align: element.align, - size, - max_repr_align: None, - unadjusted_abi_align: element.align.abi, - randomization_seed: element.randomization_seed.wrapping_add(Hash64::new(count)), - }) + map_layout(cx.calc.array_like(&element, Some(count)))? } ty::Slice(element) => { let element = cx.layout_of(element)?; - tcx.mk_layout(LayoutData { - variants: Variants::Single { index: FIRST_VARIANT }, - fields: FieldsShape::Array { stride: element.size, count: 0 }, - backend_repr: BackendRepr::Memory { sized: false }, - largest_niche: None, - uninhabited: false, - align: element.align, - size: Size::ZERO, - max_repr_align: None, - unadjusted_abi_align: element.align.abi, - // adding a randomly chosen value to distinguish slices - randomization_seed: element - .randomization_seed - .wrapping_add(Hash64::new(0x2dcba99c39784102)), - }) + map_layout(cx.calc.array_like(&element, None).map(|mut layout| { + // a randomly chosen value to distinguish slices + layout.randomization_seed = Hash64::new(0x2dcba99c39784102); + layout + }))? + } + ty::Str => { + let element = scalar(Int(I8, false)); + map_layout(cx.calc.array_like(&element, None).map(|mut layout| { + // another random value + layout.randomization_seed = Hash64::new(0xc1325f37d127be22); + layout + }))? } - ty::Str => tcx.mk_layout(LayoutData { - variants: Variants::Single { index: FIRST_VARIANT }, - fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 }, - backend_repr: BackendRepr::Memory { sized: false }, - largest_niche: None, - uninhabited: false, - align: dl.i8_align, - size: Size::ZERO, - max_repr_align: None, - unadjusted_abi_align: dl.i8_align.abi, - // another random value - randomization_seed: Hash64::new(0xc1325f37d127be22), - }), // Odd unit types. - ty::FnDef(..) => univariant(IndexSlice::empty(), StructKind::AlwaysSized)?, - ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => { - let mut unit = - univariant_uninterned(cx, ty, IndexSlice::empty(), StructKind::AlwaysSized)?; - match unit.backend_repr { - BackendRepr::Memory { ref mut sized } => *sized = false, - _ => bug!(), - } - tcx.mk_layout(unit) + ty::FnDef(..) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => { + let sized = matches!(ty.kind(), ty::FnDef(..)); + tcx.mk_layout(LayoutData::unit(cx, sized)) } - ty::Coroutine(def_id, args) => coroutine_layout(cx, ty, def_id, args)?, + ty::Coroutine(def_id, args) => { + use rustc_middle::ty::layout::PrimitiveExt as _; + + let info = tcx.coroutine_layout(def_id, args)?; + + let local_layouts = info + .field_tys + .iter() + .map(|local| { + let field_ty = EarlyBinder::bind(local.ty); + let uninit_ty = Ty::new_maybe_uninit(tcx, field_ty.instantiate(tcx, args)); + cx.spanned_layout_of(uninit_ty, local.source_info.span) + }) + .try_collect::>()?; + + let prefix_layouts = args + .as_coroutine() + .prefix_tys() + .iter() + .map(|ty| cx.layout_of(ty)) + .try_collect::>()?; - ty::Closure(_, args) => { - let tys = args.as_closure().upvar_tys(); - univariant( - &tys.iter().map(|ty| cx.layout_of(ty)).try_collect::>()?, - StructKind::AlwaysSized, - )? + let layout = cx + .calc + .coroutine( + &local_layouts, + prefix_layouts, + &info.variant_fields, + &info.storage_conflicts, + |tag| TyAndLayout { + ty: tag.primitive().to_ty(tcx), + layout: tcx.mk_layout(LayoutData::scalar(cx, tag)), + }, + ) + .map(|mut layout| { + // this is similar to how ReprOptions populates its field_shuffle_seed + layout.randomization_seed = tcx.def_path_hash(def_id).0.to_smaller_hash(); + debug!("coroutine layout ({:?}): {:#?}", ty, layout); + layout + }); + map_layout(layout)? } + ty::Closure(_, args) => univariant(args.as_closure().upvar_tys(), StructKind::AlwaysSized)?, + ty::CoroutineClosure(_, args) => { - let tys = args.as_coroutine_closure().upvar_tys(); - univariant( - &tys.iter().map(|ty| cx.layout_of(ty)).try_collect::>()?, - StructKind::AlwaysSized, - )? + univariant(args.as_coroutine_closure().upvar_tys(), StructKind::AlwaysSized)? } ty::Tuple(tys) => { let kind = if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; - univariant(&tys.iter().map(|k| cx.layout_of(k)).try_collect::>()?, kind)? + univariant(tys, kind)? } // SIMD vector types. ty::Adt(def, args) if def.repr().simd() => { - if !def.is_struct() { - // Should have yielded E0517 by now. - let guar = tcx - .dcx() - .delayed_bug("#[repr(simd)] was applied to an ADT that is not a struct"); - return Err(error(cx, LayoutError::ReferencesError(guar))); - } - - let fields = &def.non_enum_variant().fields; - - // Supported SIMD vectors are homogeneous ADTs with at least one field: + // Supported SIMD vectors are ADTs with a single array field: // - // * #[repr(simd)] struct S(T, T, T, T); - // * #[repr(simd)] struct S { x: T, y: T, z: T, w: T } // * #[repr(simd)] struct S([T; 4]) // // where T is a primitive scalar (integer/float/pointer). - - // SIMD vectors with zero fields are not supported. - // (should be caught by typeck) - if fields.is_empty() { - tcx.dcx().emit_fatal(ZeroLengthSimdType { ty }) - } - - // Type of the first ADT field: - let f0_ty = fields[FieldIdx::ZERO].ty(tcx, args); - - // Heterogeneous SIMD vectors are not supported: - // (should be caught by typeck) - for fi in fields { - if fi.ty(tcx, args) != f0_ty { - let guar = tcx.dcx().delayed_bug( - "#[repr(simd)] was applied to an ADT with heterogeneous field type", - ); - return Err(error(cx, LayoutError::ReferencesError(guar))); - } - } - - // The element type and number of elements of the SIMD vector - // are obtained from: - // - // * the element type and length of the single array field, if - // the first field is of array type, or - // - // * the homogeneous field type and the number of fields. - let (e_ty, e_len, is_array) = if let ty::Array(e_ty, _) = f0_ty.kind() { - // First ADT field is an array: - - // SIMD vectors with multiple array fields are not supported: - // Can't be caught by typeck with a generic simd type. - if def.non_enum_variant().fields.len() != 1 { - tcx.dcx().emit_fatal(MultipleArrayFieldsSimdType { ty }); - } - - // Extract the number of elements from the layout of the array field: - let FieldsShape::Array { count, .. } = cx.layout_of(f0_ty)?.layout.fields() else { - return Err(error(cx, LayoutError::Unknown(ty))); - }; - - (*e_ty, *count, true) - } else { - // First ADT field is not an array: - (f0_ty, def.non_enum_variant().fields.len() as _, false) + let Some(ty::Array(e_ty, e_len)) = def + .is_struct() + .then(|| &def.variant(FIRST_VARIANT).fields) + .filter(|fields| fields.len() == 1) + .map(|fields| *fields[FieldIdx::ZERO].ty(tcx, args).kind()) + else { + // Invalid SIMD types should have been caught by typeck by now. + let guar = tcx.dcx().delayed_bug("#[repr(simd)] was applied to an invalid ADT"); + return Err(error(cx, LayoutError::ReferencesError(guar))); }; - // SIMD vectors of zero length are not supported. - // Additionally, lengths are capped at 2^16 as a fixed maximum backends must - // support. - // - // Can't be caught in typeck if the array length is generic. - if e_len == 0 { - tcx.dcx().emit_fatal(ZeroLengthSimdType { ty }); - } else if e_len > MAX_SIMD_LANES { - tcx.dcx().emit_fatal(OversizedSimdType { ty, max_lanes: MAX_SIMD_LANES }); - } + let e_len = extract_const_value(cx, ty, e_len)? + .try_to_target_usize(tcx) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; - // Compute the ABI of the element type: let e_ly = cx.layout_of(e_ty)?; - let BackendRepr::Scalar(e_abi) = e_ly.backend_repr else { - // This error isn't caught in typeck, e.g., if - // the element type of the vector is generic. - tcx.dcx().emit_fatal(NonPrimitiveSimdType { ty, e_ty }); - }; - - // Compute the size and alignment of the vector: - let size = e_ly - .size - .checked_mul(e_len, dl) - .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?; - - let (abi, align) = if def.repr().packed() && !e_len.is_power_of_two() { - // Non-power-of-two vectors have padding up to the next power-of-two. - // If we're a packed repr, remove the padding while keeping the alignment as close - // to a vector as possible. - ( - BackendRepr::Memory { sized: true }, - AbiAndPrefAlign { - abi: Align::max_aligned_factor(size), - pref: dl.llvmlike_vector_align(size).pref, - }, - ) - } else { - ( - BackendRepr::SimdVector { element: e_abi, count: e_len }, - dl.llvmlike_vector_align(size), - ) - }; - let size = size.align_to(align.abi); - - // Compute the placement of the vector fields: - let fields = if is_array { - FieldsShape::Arbitrary { offsets: [Size::ZERO].into(), memory_index: [0].into() } - } else { - FieldsShape::Array { stride: e_ly.size, count: e_len } - }; - tcx.mk_layout(LayoutData { - variants: Variants::Single { index: FIRST_VARIANT }, - fields, - backend_repr: abi, - largest_niche: e_ly.largest_niche, - uninhabited: false, - size, - align, - max_repr_align: None, - unadjusted_abi_align: align.abi, - randomization_seed: e_ly.randomization_seed.wrapping_add(Hash64::new(e_len)), - }) + map_layout(cx.calc.simd_type(e_ly, e_len, def.repr().packed()))? } // ADTs. @@ -603,13 +595,12 @@ fn layout_of_uncached<'tcx>( return Err(error(cx, LayoutError::ReferencesError(guar))); } - return Ok(tcx.mk_layout( - cx.calc - .layout_of_union(&def.repr(), &variants) - .map_err(|err| map_error(cx, ty, err))?, - )); + return map_layout(cx.calc.layout_of_union(&def.repr(), &variants)); } + // UnsafeCell and UnsafePinned both disable niche optimizations + let is_special_no_niche = def.is_unsafe_cell() || def.is_unsafe_pinned(); + let get_discriminant_type = |min, max| abi::Integer::repr_discr(tcx, ty, &def.repr(), min, max); @@ -638,7 +629,7 @@ fn layout_of_uncached<'tcx>( &def.repr(), &variants, def.is_enum(), - def.is_unsafe_cell(), + is_special_no_niche, tcx.layout_scalar_valid_range(def.did()), get_discriminant_type, discriminants_iter(), @@ -664,7 +655,7 @@ fn layout_of_uncached<'tcx>( &def.repr(), &variants, def.is_enum(), - def.is_unsafe_cell(), + is_special_no_niche, tcx.layout_scalar_valid_range(def.did()), get_discriminant_type, discriminants_iter(), @@ -707,7 +698,7 @@ fn layout_of_uncached<'tcx>( } // Types with no meaningful known layout. - ty::Param(_) => { + ty::Param(_) | ty::Placeholder(..) => { return Err(error(cx, LayoutError::TooGeneric(ty))); } @@ -724,346 +715,13 @@ fn layout_of_uncached<'tcx>( return Err(error(cx, err)); } - ty::Placeholder(..) - | ty::Bound(..) - | ty::CoroutineWitness(..) - | ty::Infer(_) - | ty::Error(_) => { + ty::Bound(..) | ty::CoroutineWitness(..) | ty::Infer(_) | ty::Error(_) => { // `ty::Error` is handled at the top of this function. bug!("layout_of: unexpected type `{ty}`") } }) } -/// Overlap eligibility and variant assignment for each CoroutineSavedLocal. -#[derive(Clone, Debug, PartialEq)] -enum SavedLocalEligibility { - Unassigned, - Assigned(VariantIdx), - Ineligible(Option), -} - -// When laying out coroutines, 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. - -/// Compute the eligibility and assignment of each local. -fn coroutine_saved_local_eligibility( - info: &CoroutineLayout<'_>, -) -> (DenseBitSet, IndexVec) { - use SavedLocalEligibility::*; - - let mut assignments: IndexVec = - IndexVec::from_elem(Unassigned, &info.field_tys); - - // The saved locals not eligible for overlap. These will get - // "promoted" to the prefix of our coroutine. - let mut ineligible_locals = DenseBitSet::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 coroutine 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 = DenseBitSet::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(FieldIdx::from_usize(idx))); - } - } - debug!("coroutine saved local assignments: {:?}", assignments); - - (ineligible_locals, assignments) -} - -/// Compute the full coroutine layout. -fn coroutine_layout<'tcx>( - cx: &LayoutCx<'tcx>, - ty: Ty<'tcx>, - def_id: hir::def_id::DefId, - args: GenericArgsRef<'tcx>, -) -> Result, &'tcx LayoutError<'tcx>> { - use SavedLocalEligibility::*; - let tcx = cx.tcx(); - let instantiate_field = |ty: Ty<'tcx>| EarlyBinder::bind(ty).instantiate(tcx, args); - - let Some(info) = tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()) else { - return Err(error(cx, LayoutError::Unknown(ty))); - }; - let (ineligible_locals, assignments) = coroutine_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 = args.as_coroutine().prefix_tys().len(); - - // `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 = abi::Integer::fit_unsigned(max_discr); - let tag = Scalar::Initialized { - value: Primitive::Int(discr_int, /* signed = */ false), - valid_range: WrappingRange { start: 0, end: max_discr }, - }; - let tag_layout = TyAndLayout { - ty: discr_int.to_ty(tcx, /* signed = */ false), - layout: tcx.mk_layout(LayoutData::scalar(cx, tag)), - }; - - let promoted_layouts = ineligible_locals.iter().map(|local| { - let field_ty = instantiate_field(info.field_tys[local].ty); - let uninit_ty = Ty::new_maybe_uninit(tcx, field_ty); - cx.spanned_layout_of(uninit_ty, info.field_tys[local].source_info.span) - }); - let prefix_layouts = args - .as_coroutine() - .prefix_tys() - .iter() - .map(|ty| cx.layout_of(ty)) - .chain(iter::once(Ok(tag_layout))) - .chain(promoted_layouts) - .try_collect::>()?; - let prefix = univariant_uninterned(cx, ty, &prefix_layouts, 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 - // CoroutineLayout. - 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 = memory_index.invert_bijective_mapping(); - - // "a" (`0..b_start`) and "b" (`b_start..`) correspond to - // "outer" and "promoted" fields respectively. - let b_start = FieldIdx::from_usize(tag_index + 1); - let offsets_b = IndexVec::from_raw(offsets.raw.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: IndexVec = inverse_memory_index - .iter() - .filter_map(|&i| i.as_u32().checked_sub(b_start.as_u32()).map(FieldIdx::from_u32)) - .collect(); - inverse_memory_index.raw.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 = inverse_memory_index_a.invert_bijective_mapping(); - let memory_index_b = inverse_memory_index_b.invert_bijective_mapping(); - - 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| { - let field_ty = instantiate_field(info.field_tys[*local].ty); - Ty::new_maybe_uninit(tcx, field_ty) - }); - - let mut variant = univariant_uninterned( - cx, - ty, - &variant_only_tys.map(|ty| cx.layout_of(ty)).try_collect::>()?, - StructKind::Prefixed(prefix_size, prefix_align.abi), - )?; - variant.variants = Variants::Single { index }; - - let FieldsShape::Arbitrary { offsets, memory_index } = variant.fields else { - bug!(); - }; - - // Now, stitch the promoted and variant-only fields back together in - // the order they are mentioned by our CoroutineLayout. - // 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: FieldIdx = FieldIdx::MAX; - debug_assert!(variant_fields.next_index() <= INVALID_FIELD_IDX); - - let mut combined_inverse_memory_index = IndexVec::from_elem_n( - INVALID_FIELD_IDX, - promoted_memory_index.len() + memory_index.len(), - ); - let mut offsets_and_memory_index = iter::zip(offsets, memory_index); - let combined_offsets = variant_fields - .iter_enumerated() - .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(); - (promoted_offsets[field_idx], promoted_memory_index[field_idx]) - } - }; - combined_inverse_memory_index[memory_index] = i; - offset - }) - .collect(); - - // Remove the unused slots and invert the mapping to obtain the - // combined `memory_index` (also see previous comment). - combined_inverse_memory_index.raw.retain(|&i| i != INVALID_FIELD_IDX); - let combined_memory_index = combined_inverse_memory_index.invert_bijective_mapping(); - - variant.fields = FieldsShape::Arbitrary { - offsets: combined_offsets, - memory_index: combined_memory_index, - }; - - size = size.max(variant.size); - align = align.max(variant.align); - Ok(variant) - }) - .try_collect::>()?; - - size = size.align_to(align.abi); - - let uninhabited = prefix.uninhabited || variants.iter().all(|v| v.is_uninhabited()); - let abi = BackendRepr::Memory { sized: true }; - - // this is similar to how ReprOptions populates its field_shuffle_seed - let def_hash = tcx.def_path_hash(def_id).0.to_smaller_hash(); - - let layout = tcx.mk_layout(LayoutData { - variants: Variants::Multiple { - tag, - tag_encoding: TagEncoding::Direct, - tag_field: tag_index, - variants, - }, - fields: outer_fields, - backend_repr: abi, - // Suppress niches inside coroutines. If the niche is inside a field that is aliased (due to - // self-referentiality), getting the discriminant can cause aliasing violations. - // `UnsafeCell` blocks niches for the same reason, but we don't yet have `UnsafePinned` that - // would do the same for us here. - // See , . - // FIXME: Remove when is implemented and aliased coroutine fields are wrapped in `UnsafePinned`. - largest_niche: None, - uninhabited, - size, - align, - max_repr_align: None, - unadjusted_abi_align: align.abi, - randomization_seed: def_hash, - }); - debug!("coroutine layout ({:?}): {:#?}", ty, layout); - Ok(layout) -} - fn record_layout_for_printing<'tcx>(cx: &LayoutCx<'tcx>, 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 @@ -1196,7 +854,7 @@ fn variant_info_for_coroutine<'tcx>( return (vec![], None); }; - let coroutine = cx.tcx().coroutine_layout(def_id, args.as_coroutine().kind_ty()).unwrap(); + let coroutine = cx.tcx().coroutine_layout(def_id, args).unwrap(); let upvar_names = cx.tcx().closure_saved_names_of_captured_variables(def_id); let mut upvars_size = Size::ZERO; diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 7423a156a217a..c929de1162416 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -8,15 +8,6 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { let tcx = cx.tcx(); - // Type-level uninhabitedness should always imply ABI uninhabitedness. - if layout.ty.is_privately_uninhabited(tcx, cx.typing_env) { - assert!( - layout.is_uninhabited(), - "{:?} is type-level uninhabited but not ABI-uninhabited?", - layout.ty - ); - } - if layout.size.bytes() % layout.align.abi.bytes() != 0 { bug!("size is not a multiple of align, in the following layout:\n{layout:#?}"); } @@ -29,6 +20,19 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou return; } + // Type-level uninhabitedness should always imply ABI uninhabitedness. This can be expensive on + // big non-exhaustive types, and is [hard to + // fix](https://github.com/rust-lang/rust/issues/141006#issuecomment-2883415000) in general. + // Only doing this sanity check when debug assertions are turned on avoids the issue for the + // very specific case of #140944. + if layout.ty.is_privately_uninhabited(tcx, cx.typing_env) { + assert!( + layout.is_uninhabited(), + "{:?} is type-level uninhabited but not ABI-uninhabited?", + layout.ty + ); + } + /// Yields non-ZST fields of the type fn non_zst_fields<'tcx, 'a>( cx: &'a LayoutCx<'tcx>, diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 8be1611bb9ac2..929cc074bdac7 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -13,10 +13,8 @@ #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iterator_try_collect)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use rustc_middle::query::Providers; @@ -30,6 +28,7 @@ mod implied_bounds; mod instance; mod layout; mod needs_drop; +mod nested_bodies; mod opaque_types; mod representability; pub mod sig_types; @@ -51,4 +50,5 @@ pub fn provide(providers: &mut Providers) { ty::provide(providers); instance::provide(providers); structural_match::provide(providers); + nested_bodies::provide(providers); } diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 52955ec59a4a0..c3b04c20f4b67 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -42,11 +42,11 @@ fn needs_async_drop_raw<'tcx>( let adt_has_async_dtor = |adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant); let res = drop_tys_helper(tcx, query.value, query.typing_env, adt_has_async_dtor, false, false) - .filter(filter_array_elements(tcx, query.typing_env)) + .filter(filter_array_elements_async(tcx, query.typing_env)) .next() .is_some(); - debug!("needs_drop_raw({:?}) = {:?}", query, res); + debug!("needs_async_drop_raw({:?}) = {:?}", query, res); res } @@ -66,6 +66,18 @@ fn filter_array_elements<'tcx>( Err(AlwaysRequiresDrop) => true, } } +fn filter_array_elements_async<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, +) -> impl Fn(&Result, AlwaysRequiresDrop>) -> bool { + move |ty| match ty { + Ok(ty) => match *ty.kind() { + ty::Array(elem, _) => tcx.needs_async_drop_raw(typing_env.as_query_input(elem)), + _ => true, + }, + Err(AlwaysRequiresDrop) => true, + } +} fn has_significant_drop_raw<'tcx>( tcx: TyCtxt<'tcx>, @@ -414,6 +426,27 @@ fn adt_drop_tys<'tcx>( .collect::, _>>() .map(|components| tcx.mk_type_list(&components)) } + +fn adt_async_drop_tys<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { + // This is for the "adt_async_drop_tys" query, that considers all `AsyncDrop` impls. + let adt_has_dtor = + |adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant); + // `tcx.type_of(def_id)` identical to `tcx.make_adt(def, identity_args)` + drop_tys_helper( + tcx, + tcx.type_of(def_id).instantiate_identity(), + ty::TypingEnv::non_body_analysis(tcx, def_id), + adt_has_dtor, + false, + false, + ) + .collect::, _>>() + .map(|components| tcx.mk_type_list(&components)) +} + // If `def_id` refers to a generic ADT, the queries above and below act as if they had been handed // a `tcx.make_ty(def, identity_args)` and as such it is legal to instantiate the generic parameters // of the ADT into the outputted `ty`s. @@ -458,6 +491,7 @@ pub(crate) fn provide(providers: &mut Providers) { needs_async_drop_raw, has_significant_drop_raw, adt_drop_tys, + adt_async_drop_tys, adt_significant_drop_tys, list_significant_drop_tys, ..*providers diff --git a/compiler/rustc_ty_utils/src/nested_bodies.rs b/compiler/rustc_ty_utils/src/nested_bodies.rs new file mode 100644 index 0000000000000..7c74d8eb63518 --- /dev/null +++ b/compiler/rustc_ty_utils/src/nested_bodies.rs @@ -0,0 +1,34 @@ +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::Visitor; +use rustc_middle::query::Providers; +use rustc_middle::ty::{self, TyCtxt}; + +fn nested_bodies_within<'tcx>(tcx: TyCtxt<'tcx>, item: LocalDefId) -> &'tcx ty::List { + let body = tcx.hir_body_owned_by(item); + let mut collector = + NestedBodiesVisitor { tcx, root_def_id: item.to_def_id(), nested_bodies: vec![] }; + collector.visit_body(body); + tcx.mk_local_def_ids(&collector.nested_bodies) +} + +struct NestedBodiesVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + root_def_id: DefId, + nested_bodies: Vec, +} + +impl<'tcx> Visitor<'tcx> for NestedBodiesVisitor<'tcx> { + fn visit_nested_body(&mut self, id: hir::BodyId) { + let body_def_id = self.tcx.hir_body_owner_def_id(id); + if self.tcx.typeck_root_def_id(body_def_id.to_def_id()) == self.root_def_id { + self.nested_bodies.push(body_def_id); + let body = self.tcx.hir_body(id); + self.visit_body(body); + } + } +} + +pub(super) fn provide(providers: &mut Providers) { + *providers = Providers { nested_bodies_within, ..*providers }; +} diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 98881905bcf88..841f602d985cd 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -1,12 +1,12 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit; use rustc_hir::intravisit::Visitor; -use rustc_hir::{CRATE_HIR_ID, intravisit}; -use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::util::{CheckRegions, NotUniqueParam}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_middle::{bug, span_bug}; use rustc_span::Span; use tracing::{instrument, trace}; @@ -30,14 +30,21 @@ enum CollectionMode { /// For impl trait in assoc types we only permit collecting them from /// associated types of the same impl block. ImplTraitInAssocTypes, - TypeAliasImplTraitTransition, + /// When collecting for an explicit `#[define_opaque]` attribute, find all TAITs + Taits, + /// The default case, only collect RPITs and AsyncFn return types, as these are + /// always defined by the current item. + RpitAndAsyncFnOnly, } impl<'tcx> OpaqueTypeCollector<'tcx> { fn new(tcx: TyCtxt<'tcx>, item: LocalDefId) -> Self { - let mode = match tcx.def_kind(tcx.local_parent(item)) { - DefKind::Impl { of_trait: true } => CollectionMode::ImplTraitInAssocTypes, - _ => CollectionMode::TypeAliasImplTraitTransition, + let mode = match tcx.def_kind(item) { + DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy => { + CollectionMode::ImplTraitInAssocTypes + } + DefKind::TyAlias => CollectionMode::Taits, + _ => CollectionMode::RpitAndAsyncFnOnly, }; Self { tcx, opaques: Vec::new(), item, seen: Default::default(), span: None, mode } } @@ -73,40 +80,6 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { } } - /// Returns `true` if `opaque_hir_id` is a sibling or a child of a sibling of `self.item`. - /// - /// Example: - /// ```ignore UNSOLVED (is this a bug?) - /// # #![feature(type_alias_impl_trait)] - /// pub mod foo { - /// pub mod bar { - /// pub trait Bar { /* ... */ } - /// pub type Baz = impl Bar; - /// - /// # impl Bar for () {} - /// fn f1() -> Baz { /* ... */ } - /// } - /// fn f2() -> bar::Baz { /* ... */ } - /// } - /// ``` - /// - /// and `opaque_def_id` is the `DefId` of the definition of the opaque type `Baz`. - /// For the above example, this function returns `true` for `f1` and `false` for `f2`. - #[instrument(level = "trace", skip(self), ret)] - fn check_tait_defining_scope(&self, opaque_def_id: LocalDefId) -> bool { - let mut hir_id = self.tcx.local_def_id_to_hir_id(self.item); - let opaque_hir_id = self.tcx.local_def_id_to_hir_id(opaque_def_id); - - // Named opaque types can be defined by any siblings or children of siblings. - let scope = self.tcx.hir_get_defining_scope(opaque_hir_id); - // We walk up the node tree until we hit the root or the scope of the opaque type. - while hir_id != scope && hir_id != CRATE_HIR_ID { - hir_id = self.tcx.hir_get_parent_item(hir_id).into(); - } - // Syntactically, we are allowed to define the concrete type if: - hir_id == scope - } - #[instrument(level = "trace", skip(self))] fn collect_taits_declared_in_body(&mut self) { let body = self.tcx.hir_body_owned_by(self.item).value; @@ -139,18 +112,31 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { } // TAITs outside their defining scopes are ignored. - let origin = self.tcx.local_opaque_ty_origin(alias_ty.def_id.expect_local()); - trace!(?origin); - match origin { + match self.tcx.local_opaque_ty_origin(alias_ty.def_id.expect_local()) { rustc_hir::OpaqueTyOrigin::FnReturn { .. } | rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {} - rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => { - if !in_assoc_ty && !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) { - return; + rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => match self.mode { + // If we are collecting opaques in an assoc method, we are only looking at assoc types + // mentioned in the assoc method and only at opaques defined in there. We do not + // want to collect TAITs + CollectionMode::ImplTraitInAssocTypes => { + if !in_assoc_ty { + return; + } } - } + // If we are collecting opaques referenced from a `define_opaque` attribute, we + // do not want to look at opaques defined in associated types. Those can only be + // defined by methods on the same impl. + CollectionMode::Taits => { + if in_assoc_ty { + return; + } + } + CollectionMode::RpitAndAsyncFnOnly => return, + }, } + trace!(?alias_ty, "adding"); self.opaques.push(alias_ty.def_id.expect_local()); let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count; @@ -192,6 +178,32 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { } } } + + /// Checks the `#[define_opaque]` attributes on items and collectes opaques to define + /// from the referenced types. + #[instrument(level = "trace", skip(self))] + fn collect_taits_from_defines_attr(&mut self) { + let hir_id = self.tcx.local_def_id_to_hir_id(self.item); + if !hir_id.is_owner() { + return; + } + let Some(defines) = self.tcx.hir_attr_map(hir_id.owner).define_opaque else { + return; + }; + for &(span, define) in defines { + trace!(?define); + let mode = std::mem::replace(&mut self.mode, CollectionMode::Taits); + let n = self.opaques.len(); + super::sig_types::walk_types(self.tcx, define, self); + if n == self.opaques.len() { + self.tcx.dcx().span_err(span, "item does not contain any opaque types"); + } + self.mode = mode; + } + // Allow using `#[define_opaque]` on assoc methods and type aliases to override the default collection mode in + // case it was capturing too much. + self.mode = CollectionMode::RpitAndAsyncFnOnly; + } } impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> { @@ -210,7 +222,8 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { self.visit_opaque_ty(alias_ty); } // Skips type aliases, as they are meant to be transparent. - ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => { + // FIXME(type_alias_impl_trait): can we require mentioning nested type aliases explicitly? + ty::Alias(ty::Free, alias_ty) if alias_ty.def_id.is_local() => { self.tcx .type_of(alias_ty.def_id) .instantiate(self.tcx, alias_ty.args) @@ -283,28 +296,6 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { self.visit_opaque_ty(alias_ty); } } - ty::Adt(def, _) if def.did().is_local() => { - if let CollectionMode::ImplTraitInAssocTypes = self.mode { - return; - } - if !self.seen.insert(def.did().expect_local()) { - return; - } - for variant in def.variants().iter() { - for field in variant.fields.iter() { - // Don't use the `ty::Adt` args, we either - // * found the opaque in the args - // * will find the opaque in the uninstantiated fields - // The only other situation that can occur is that after instantiating, - // some projection resolves to an opaque that we would have otherwise - // not found. While we could instantiate and walk those, that would mean we - // would have to walk all generic parameters of an Adt, which can quickly - // degenerate into looking at an exponential number of types. - let ty = self.tcx.type_of(field.did).instantiate_identity(); - self.visit_spanned(self.tcx.def_span(field.did), ty); - } - } - } _ => trace!(kind=?t.kind()), } } @@ -317,7 +308,9 @@ fn opaque_types_defined_by<'tcx>( let kind = tcx.def_kind(item); trace!(?kind); let mut collector = OpaqueTypeCollector::new(tcx, item); + collector.collect_taits_from_defines_attr(); super::sig_types::walk_types(tcx, item, &mut collector); + match kind { DefKind::AssocFn | DefKind::Fn @@ -327,9 +320,12 @@ fn opaque_types_defined_by<'tcx>( | DefKind::AnonConst => { collector.collect_taits_declared_in_body(); } + // Closures and coroutines are type checked with their parent + DefKind::Closure | DefKind::InlineConst => { + collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item))); + } + DefKind::AssocTy | DefKind::TyAlias | DefKind::GlobalAsm => {} DefKind::OpaqueTy - | DefKind::TyAlias - | DefKind::AssocTy | DefKind::Mod | DefKind::Struct | DefKind::Union @@ -347,13 +343,13 @@ fn opaque_types_defined_by<'tcx>( | DefKind::ForeignMod | DefKind::Field | DefKind::LifetimeParam - | DefKind::GlobalAsm | DefKind::Impl { .. } - | DefKind::SyntheticCoroutineBody => {} - // Closures and coroutines are type checked with their parent, so we need to allow all - // opaques from the closure signature *and* from the parent body. - DefKind::Closure | DefKind::InlineConst => { - collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item))); + | DefKind::SyntheticCoroutineBody => { + span_bug!( + tcx.def_span(item), + "`opaque_types_defined_by` not defined for {} `{item:?}`", + kind.descr(item.to_def_id()) + ); } } tcx.mk_local_def_ids(&collector.opaques) diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index d6f9277813d12..dc6009116ac57 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -4,10 +4,8 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::span_bug; -use rustc_middle::ty::visit::{VisitorResult, try_visit}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable, VisitorResult, try_visit}; use rustc_span::Span; -use rustc_type_ir::visit::TypeVisitable; use tracing::{instrument, trace}; pub trait SpannedTypeVisitor<'tcx> { @@ -118,7 +116,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( "{kind:?} has not seen any uses of `walk_types` yet, ping oli-obk if you'd like any help" ) } - // These don't have any types. + // These don't have any types, but are visited during privacy checking. | DefKind::ExternCrate | DefKind::ForeignMod | DefKind::ForeignTy diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 8610c30ab703e..0c49ddff39bc2 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -3,62 +3,63 @@ use rustc_hir as hir; use rustc_hir::LangItem; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::bug; use rustc_middle::query::Providers; -use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{ - self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, + self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, + fold_regions, }; use rustc_span::DUMMY_SP; use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_trait_selection::traits; -use tracing::{debug, instrument}; +use tracing::instrument; #[instrument(level = "debug", skip(tcx), ret)] fn sized_constraint_for_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option> { - use rustc_type_ir::TyKind::*; - match ty.kind() { // these are always sized - Bool - | Char - | Int(..) - | Uint(..) - | Float(..) - | RawPtr(..) - | Ref(..) - | FnDef(..) - | FnPtr(..) - | Array(..) - | Closure(..) - | CoroutineClosure(..) - | Coroutine(..) - | CoroutineWitness(..) - | Never - | Dynamic(_, _, ty::DynStar) => None, + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Array(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Dynamic(_, _, ty::DynStar) => None, // these are never sized - Str | Slice(..) | Dynamic(_, _, ty::Dyn) | Foreign(..) => Some(ty), + ty::Str | ty::Slice(..) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => Some(ty), - Pat(ty, _) => sized_constraint_for_ty(tcx, *ty), + ty::Pat(ty, _) => sized_constraint_for_ty(tcx, *ty), - Tuple(tys) => tys.last().and_then(|&ty| sized_constraint_for_ty(tcx, ty)), + ty::Tuple(tys) => tys.last().and_then(|&ty| sized_constraint_for_ty(tcx, ty)), // recursive case - Adt(adt, args) => adt.sized_constraint(tcx).and_then(|intermediate| { + ty::Adt(adt, args) => adt.sized_constraint(tcx).and_then(|intermediate| { let ty = intermediate.instantiate(tcx, args); sized_constraint_for_ty(tcx, ty) }), // these can be sized or unsized. - Param(..) | Alias(..) | Error(_) => Some(ty), + ty::Param(..) | ty::Alias(..) | ty::Error(_) => Some(ty), // We cannot instantiate the binder, so just return the *original* type back, // but only if the inner type has a sized constraint. Thus we skip the binder, // but don't actually use the result from `sized_constraint_for_ty`. - UnsafeBinder(inner_ty) => sized_constraint_for_ty(tcx, inner_ty.skip_binder()).map(|_| ty), + ty::UnsafeBinder(inner_ty) => { + sized_constraint_for_ty(tcx, inner_ty.skip_binder()).map(|_| ty) + } - Placeholder(..) | Bound(..) | Infer(..) => { + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { bug!("unexpected type `{ty:?}` in sized_constraint_for_ty") } } @@ -185,7 +186,7 @@ struct ImplTraitInTraitFinder<'a, 'tcx> { } impl<'tcx> TypeVisitor> for ImplTraitInTraitFinder<'_, 'tcx> { - fn visit_binder>>(&mut self, binder: &ty::Binder<'tcx, T>) { + fn visit_binder>>(&mut self, binder: &ty::Binder<'tcx, T>) { self.depth.shift_in(1); binder.super_visit_with(self); self.depth.shift_out(1); @@ -260,57 +261,6 @@ fn param_env_normalized_for_post_analysis(tcx: TyCtxt<'_>, def_id: DefId) -> ty: typing_env.with_post_analysis_normalized(tcx).param_env } -/// If the given trait impl enables exploiting the former order dependence of trait objects, -/// returns its self type; otherwise, returns `None`. -/// -/// See [`ty::ImplOverlapKind::FutureCompatOrderDepTraitObjects`] for more details. -#[instrument(level = "debug", skip(tcx))] -fn self_ty_of_trait_impl_enabling_order_dep_trait_object_hack( - tcx: TyCtxt<'_>, - def_id: DefId, -) -> Option>> { - let impl_ = - tcx.impl_trait_header(def_id).unwrap_or_else(|| bug!("called on inherent impl {def_id:?}")); - - let trait_ref = impl_.trait_ref.skip_binder(); - debug!(?trait_ref); - - let is_marker_like = impl_.polarity == 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!("not marker-like!"); - return None; - } - - // impl must be `impl Trait for dyn Marker1 + Marker2 + ...` - if trait_ref.args.len() != 1 { - debug!("impl has args!"); - return None; - } - - let predicates = tcx.predicates_of(def_id); - if predicates.parent.is_some() || !predicates.predicates.is_empty() { - debug!(?predicates, "impl has predicates!"); - return None; - } - - let self_ty = trait_ref.self_ty(); - let self_ty_matches = match self_ty.kind() { - ty::Dynamic(data, re, _) if re.is_static() => data.principal().is_none(), - _ => false, - }; - - if self_ty_matches { - debug!("MATCHES!"); - Some(EarlyBinder::bind(self_ty)) - } else { - debug!("non-matching self type"); - None - } -} - /// Check if a function is async. fn asyncness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Asyncness { let node = tcx.hir_node_by_def_id(def_id); @@ -364,15 +314,70 @@ fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> DenseBitSe unsizing_params } +fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) -> bool { + debug_assert_eq!(tcx.def_kind(impl_def_id), DefKind::Impl { of_trait: true }); + + let infcx = tcx.infer_ctxt().ignoring_regions().build(ty::TypingMode::non_body_analysis()); + + let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx); + let cause = traits::ObligationCause::dummy(); + let param_env = tcx.param_env(impl_def_id); + + let tail = tcx.struct_tail_raw( + tcx.type_of(impl_def_id).instantiate_identity(), + |ty| { + ocx.structurally_normalize_ty(&cause, param_env, ty).unwrap_or_else(|_| { + Ty::new_error_with_message( + tcx, + tcx.def_span(impl_def_id), + "struct tail should be computable", + ) + }) + }, + || (), + ); + + match tail.kind() { + ty::Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true, + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Array(_, _) + | ty::Pat(_, _) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_, _) + | ty::UnsafeBinder(_) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(_, _) + | ty::Never + | ty::Tuple(_) + | ty::Alias(_, _) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Placeholder(_) + | ty::Infer(_) + | ty::Error(_) + | ty::Dynamic(_, _, ty::DynStar) => false, + } +} + pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { asyncness, adt_sized_constraint, param_env, param_env_normalized_for_post_analysis, - self_ty_of_trait_impl_enabling_order_dep_trait_object_hack, defaultness, unsizing_params_for_adt, + impl_self_is_guaranteed_unsized, ..*providers }; } diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index 7b2593b96e37f..83d3d78298e60 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" # tidy-alphabetical-start bitflags = "2.4.1" derive-where = "1.2.7" +ena = "0.14.3" indexmap = "2.0.0" rustc-hash = "1.1.0" rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } @@ -33,6 +34,3 @@ nightly = [ "rustc_index/nightly", "rustc_ast_ir/nightly", ] - -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)'] } diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 0d0092ea1aa08..000cf1e1fd8b1 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -5,7 +5,7 @@ use std::ops::{ControlFlow, Deref}; use derive_where::derive_where; #[cfg(feature = "nightly")] -use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use tracing::instrument; use crate::data_structures::SsoHashSet; @@ -57,7 +57,7 @@ where macro_rules! impl_binder_encode_decode { ($($t:ty),+ $(,)?) => { $( - impl> rustc_serialize::Encodable for ty::Binder + impl rustc_serialize::Encodable for ty::Binder where $t: rustc_serialize::Encodable, I::BoundVarKinds: rustc_serialize::Encodable, @@ -67,7 +67,7 @@ macro_rules! impl_binder_encode_decode { self.as_ref().skip_binder().encode(e); } } - impl> rustc_serialize::Decodable for ty::Binder + impl rustc_serialize::Decodable for ty::Binder where $t: TypeVisitable + rustc_serialize::Decodable, I::BoundVarKinds: rustc_serialize::Decodable, @@ -112,7 +112,7 @@ where pub fn bind_with_vars(value: T, bound_vars: I::BoundVarKinds) -> Binder { if cfg!(debug_assertions) { let mut validator = ValidateBoundVars::new(bound_vars); - value.visit_with(&mut validator); + let _ = value.visit_with(&mut validator); } Binder { value, bound_vars } } @@ -122,9 +122,13 @@ impl> TypeFoldable for Binder { fn try_fold_with>(self, folder: &mut F) -> Result { folder.try_fold_binder(self) } + + fn fold_with>(self, folder: &mut F) -> Self { + folder.fold_binder(self) + } } -impl> TypeVisitable for Binder { +impl> TypeVisitable for Binder { fn visit_with>(&self, visitor: &mut V) -> V::Result { visitor.visit_binder(self) } @@ -135,11 +139,15 @@ impl> TypeSuperFoldable for Binder { self, folder: &mut F, ) -> Result { - self.try_map_bound(|ty| ty.try_fold_with(folder)) + self.try_map_bound(|t| t.try_fold_with(folder)) + } + + fn super_fold_with>(self, folder: &mut F) -> Self { + self.map_bound(|t| t.fold_with(folder)) } } -impl> TypeSuperVisitable for Binder { +impl> TypeSuperVisitable for Binder { fn super_visit_with>(&self, visitor: &mut V) -> V::Result { self.as_ref().skip_binder().visit_with(visitor) } @@ -196,7 +204,7 @@ impl Binder { let value = f(value); if cfg!(debug_assertions) { let mut validator = ValidateBoundVars::new(bound_vars); - value.visit_with(&mut validator); + let _ = value.visit_with(&mut validator); } Binder { value, bound_vars } } @@ -209,7 +217,7 @@ impl Binder { let value = f(value)?; if cfg!(debug_assertions) { let mut validator = ValidateBoundVars::new(bound_vars); - value.visit_with(&mut validator); + let _ = value.visit_with(&mut validator); } Ok(Binder { value, bound_vars }) } @@ -284,7 +292,7 @@ impl ValidateBoundVars { impl TypeVisitor for ValidateBoundVars { type Result = ControlFlow<()>; - fn visit_binder>(&mut self, t: &Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &Binder) -> Self::Result { self.binder_index.shift_in(1); let result = t.super_visit_with(self); self.binder_index.shift_out(1); @@ -342,7 +350,10 @@ impl TypeVisitor for ValidateBoundVars { #[derive_where(PartialOrd; I: Interner, T: Ord)] #[derive_where(Hash; I: Interner, T: Hash)] #[derive_where(Debug; I: Interner, T: Debug)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub struct EarlyBinder { value: T, #[derive_where(skip(Debug))] @@ -859,7 +870,7 @@ impl<'a, I: Interner> ArgFolder<'a, I> { if self.binders_passed == 0 || !val.has_escaping_bound_vars() { val } else { - ty::fold::shift_vars(self.cx, val, self.binders_passed) + ty::shift_vars(self.cx, val, self.binders_passed) } } @@ -867,7 +878,7 @@ impl<'a, I: Interner> ArgFolder<'a, I> { if self.binders_passed == 0 || !region.has_escaping_bound_vars() { region } else { - ty::fold::shift_region(self.cx, region, self.binders_passed) + ty::shift_region(self.cx, region, self.binders_passed) } } } diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 24b2ebc1fbe79..67b67df4b2817 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -4,7 +4,7 @@ use std::ops::Index; use derive_where::derive_where; #[cfg(feature = "nightly")] -use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::inherent::*; @@ -16,7 +16,10 @@ use crate::{self as ty, Interner, TypingMode, UniverseIndex}; #[derive_where(Eq; I: Interner, V: Eq)] #[derive_where(Debug; I: Interner, V: fmt::Debug)] #[derive_where(Copy; I: Interner, V: Copy)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub struct CanonicalQueryInput { pub canonical: Canonical, pub typing_mode: TypingMode, @@ -31,8 +34,10 @@ pub struct CanonicalQueryInput { #[derive_where(Eq; I: Interner, V: Eq)] #[derive_where(Debug; I: Interner, V: fmt::Debug)] #[derive_where(Copy; I: Interner, V: Copy)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub struct Canonical { pub value: V, pub max_universe: UniverseIndex, @@ -85,7 +90,10 @@ impl fmt::Display for Canonical { /// with fresh inference variables replacing the canonical values. #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct CanonicalVarInfo { pub kind: CanonicalVarKind, } @@ -138,8 +146,10 @@ impl CanonicalVarInfo { /// in the type-theory sense of the term -- i.e., a "meta" type system /// that analyzes type-like values. #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub enum CanonicalVarKind { /// Some kind of type inference variable. Ty(CanonicalTyVarKind), @@ -213,7 +223,10 @@ impl CanonicalVarKind { /// know what set of types a given type variable can be unified with. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub enum CanonicalTyVarKind { /// General type variable `?T` that can be unified with arbitrary types. General(UniverseIndex), @@ -235,7 +248,10 @@ pub enum CanonicalTyVarKind { /// variables. You will need to supply it later to instantiate the /// canonicalized query response. #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] pub struct CanonicalVarValues { pub var_values: I::GenericArgs, diff --git a/compiler/rustc_type_ir/src/codec.rs b/compiler/rustc_type_ir/src/codec.rs deleted file mode 100644 index f443f596373fb..0000000000000 --- a/compiler/rustc_type_ir/src/codec.rs +++ /dev/null @@ -1,61 +0,0 @@ -use rustc_data_structures::fx::FxHashMap; -use rustc_span::{SpanDecoder, SpanEncoder}; - -use crate::{Interner, PredicateKind}; - -/// 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; - -/// 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> { - fn decode(d: &mut D) -> &'tcx Self; -} - -pub trait TyEncoder: SpanEncoder { - type I: Interner; - const CLEAR_CROSS_CRATE: bool; - - fn position(&self) -> usize; - - fn type_shorthands(&mut self) -> &mut FxHashMap<::Ty, usize>; - - fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize>; - - fn encode_alloc_id(&mut self, alloc_id: &::AllocId); -} - -pub trait TyDecoder: SpanDecoder { - type I: Interner; - const CLEAR_CROSS_CRATE: bool; - - fn interner(&self) -> Self::I; - - fn cached_ty_for_shorthand( - &mut self, - shorthand: usize, - or_insert_with: F, - ) -> ::Ty - where - F: FnOnce(&mut Self) -> ::Ty; - - fn with_position(&mut self, pos: usize, f: F) -> R - where - F: FnOnce(&mut Self) -> R; - - fn positioned_at_shorthand(&self) -> bool { - (self.peek_byte() & (SHORTHAND_OFFSET as u8)) != 0 - } - - fn decode_alloc_id(&mut self) -> ::AllocId; -} diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index aafc0907ae156..70a8509b51339 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -4,14 +4,17 @@ use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] -use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::{self as ty, DebruijnIndex, Interner}; /// Represents a constant in Rust. #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum ConstKind { /// A const generic parameter. Param(I::ParamConst), @@ -62,7 +65,10 @@ impl fmt::Debug for ConstKind { /// An unevaluated (potentially generic) constant used in the type-system. #[derive_where(Clone, Copy, Debug, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct UnevaluatedConst { pub def: I::DefId, pub args: I::GenericArgs, @@ -86,7 +92,7 @@ rustc_index::newtype_index! { /// An inference variable for a const, for use in const generics. #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable))] +#[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext))] pub enum InferConst { /// Infer the value of the const. Var(ConstVid), diff --git a/compiler/rustc_type_ir/src/data_structures/mod.rs b/compiler/rustc_type_ir/src/data_structures/mod.rs index 30c67d10d0e86..a72669cbd189b 100644 --- a/compiler/rustc_type_ir/src/data_structures/mod.rs +++ b/compiler/rustc_type_ir/src/data_structures/mod.rs @@ -1,5 +1,6 @@ use std::hash::BuildHasherDefault; +pub use ena::unify::{NoError, UnifyKey, UnifyValue}; use rustc_hash::FxHasher; pub use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 9955e92b55a5c..34502f4955003 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -9,7 +9,7 @@ use rustc_data_structures::fingerprint::Fingerprint; #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; #[cfg(feature = "nightly")] -use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use crate::inherent::*; use crate::visit::TypeVisitableExt as _; @@ -17,7 +17,10 @@ use crate::{self as ty, Interner}; /// See `simplify_type`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum SimplifiedType { Bool, Char, @@ -229,6 +232,9 @@ impl bool { + if lhs == rhs { + return true; + } self.types_may_unify_inner(lhs, rhs, Self::STARTING_DEPTH) } diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 81aa4a1f19ee0..7ed0f92b6398a 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -1,3 +1,7 @@ +use crate::inherent::*; +use crate::visit::Flags; +use crate::{self as ty, Interner}; + bitflags::bitflags! { /// Flags that we track on types. These flags are propagated upwards /// through the type during type construction, so that we can quickly check @@ -69,8 +73,8 @@ bitflags::bitflags! { /// Does this have `Projection`? const HAS_TY_PROJECTION = 1 << 10; - /// Does this have `Weak`? - const HAS_TY_WEAK = 1 << 11; + /// Does this have `Free` aliases? + const HAS_TY_FREE_ALIAS = 1 << 11; /// Does this have `Opaque`? const HAS_TY_OPAQUE = 1 << 12; /// Does this have `Inherent`? @@ -82,7 +86,7 @@ bitflags::bitflags! { /// /// Rephrased, could this term be normalized further? const HAS_ALIAS = TypeFlags::HAS_TY_PROJECTION.bits() - | TypeFlags::HAS_TY_WEAK.bits() + | TypeFlags::HAS_TY_FREE_ALIAS.bits() | TypeFlags::HAS_TY_OPAQUE.bits() | TypeFlags::HAS_TY_INHERENT.bits() | TypeFlags::HAS_CT_PROJECTION.bits(); @@ -111,18 +115,383 @@ bitflags::bitflags! { /// 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 << 21; + const STILL_FURTHER_SPECIALIZABLE = TypeFlags::HAS_TY_PARAM.bits() + | TypeFlags::HAS_TY_PLACEHOLDER.bits() + | TypeFlags::HAS_TY_INFER.bits() + | TypeFlags::HAS_CT_PARAM.bits() + | TypeFlags::HAS_CT_PLACEHOLDER.bits() + | TypeFlags::HAS_CT_INFER.bits(); /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`? - const HAS_TY_FRESH = 1 << 22; + const HAS_TY_FRESH = 1 << 21; /// Does this value have `InferConst::Fresh`? - const HAS_CT_FRESH = 1 << 23; - - /// Does this have `Coroutine` or `CoroutineWitness`? - const HAS_TY_COROUTINE = 1 << 24; + const HAS_CT_FRESH = 1 << 22; /// Does this have any binders with bound vars (e.g. that need to be anonymized)? - const HAS_BINDER_VARS = 1 << 25; + const HAS_BINDER_VARS = 1 << 23; + } +} + +#[derive(Debug)] +pub struct FlagComputation { + pub flags: TypeFlags, + + /// see `Ty::outer_exclusive_binder` for details + pub outer_exclusive_binder: ty::DebruijnIndex, + + interner: std::marker::PhantomData, +} + +impl FlagComputation { + fn new() -> FlagComputation { + FlagComputation { + flags: TypeFlags::empty(), + outer_exclusive_binder: ty::INNERMOST, + interner: std::marker::PhantomData, + } + } + + #[allow(rustc::usage_of_ty_tykind)] + pub fn for_kind(kind: &ty::TyKind) -> FlagComputation { + let mut result = FlagComputation::new(); + result.add_kind(kind); + result + } + + pub fn for_predicate(binder: ty::Binder>) -> FlagComputation { + let mut result = FlagComputation::new(); + result.add_predicate(binder); + result + } + + pub fn for_const_kind(kind: &ty::ConstKind) -> FlagComputation { + let mut result = FlagComputation::new(); + result.add_const_kind(kind); + result + } + + pub fn for_clauses(clauses: &[I::Clause]) -> FlagComputation { + let mut result = FlagComputation::new(); + for c in clauses { + result.add_flags(c.as_predicate().flags()); + result.add_exclusive_binder(c.as_predicate().outer_exclusive_binder()); + } + result + } + + fn add_flags(&mut self, flags: TypeFlags) { + self.flags = self.flags | flags; + } + + /// indicates that `self` refers to something at binding level `binder` + fn add_bound_var(&mut self, binder: ty::DebruijnIndex) { + let exclusive_binder = binder.shifted_in(1); + self.add_exclusive_binder(exclusive_binder); + } + + /// indicates that `self` refers to something *inside* binding + /// level `binder` -- not bound by `binder`, but bound by the next + /// binder internal to it + fn add_exclusive_binder(&mut self, exclusive_binder: ty::DebruijnIndex) { + self.outer_exclusive_binder = self.outer_exclusive_binder.max(exclusive_binder); + } + + /// Adds the flags/depth from a set of types that appear within the current type, but within a + /// region binder. + fn bound_computation(&mut self, value: ty::Binder, f: F) + where + F: FnOnce(&mut Self, T), + { + let mut computation = FlagComputation::new(); + + if !value.bound_vars().is_empty() { + computation.add_flags(TypeFlags::HAS_BINDER_VARS); + } + + f(&mut computation, value.skip_binder()); + + self.add_flags(computation.flags); + + // The types that contributed to `computation` occurred within + // a region binder, so subtract one from the region depth + // within when adding the depth to `self`. + let outer_exclusive_binder = computation.outer_exclusive_binder; + if outer_exclusive_binder > ty::INNERMOST { + self.add_exclusive_binder(outer_exclusive_binder.shifted_out(1)); + } // otherwise, this binder captures nothing + } + + #[allow(rustc::usage_of_ty_tykind)] + fn add_kind(&mut self, kind: &ty::TyKind) { + match *kind { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Float(_) + | ty::Uint(_) + | ty::Never + | ty::Str + | ty::Foreign(..) => {} + + ty::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), + + ty::Param(_) => { + self.add_flags(TypeFlags::HAS_TY_PARAM); + } + + ty::Closure(_, args) + | ty::Coroutine(_, args) + | ty::CoroutineClosure(_, args) + | ty::CoroutineWitness(_, args) => { + self.add_args(args.as_slice()); + } + + ty::Bound(debruijn, _) => { + self.add_bound_var(debruijn); + self.add_flags(TypeFlags::HAS_TY_BOUND); + } + + ty::Placeholder(..) => { + self.add_flags(TypeFlags::HAS_TY_PLACEHOLDER); + } + + ty::Infer(infer) => match infer { + ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => { + self.add_flags(TypeFlags::HAS_TY_FRESH) + } + + ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_) => { + self.add_flags(TypeFlags::HAS_TY_INFER) + } + }, + + ty::Adt(_, args) => { + self.add_args(args.as_slice()); + } + + ty::Alias(kind, data) => { + self.add_flags(match kind { + ty::Projection => TypeFlags::HAS_TY_PROJECTION, + ty::Free => TypeFlags::HAS_TY_FREE_ALIAS, + ty::Opaque => TypeFlags::HAS_TY_OPAQUE, + ty::Inherent => TypeFlags::HAS_TY_INHERENT, + }); + + self.add_alias_ty(data); + } + + ty::Dynamic(obj, r, _) => { + for predicate in obj.iter() { + self.bound_computation(predicate, |computation, predicate| match predicate { + ty::ExistentialPredicate::Trait(tr) => { + computation.add_args(tr.args.as_slice()) + } + ty::ExistentialPredicate::Projection(p) => { + computation.add_existential_projection(&p); + } + ty::ExistentialPredicate::AutoTrait(_) => {} + }); + } + + self.add_region(r); + } + + ty::Array(tt, len) => { + self.add_ty(tt); + self.add_const(len); + } + + ty::Pat(ty, pat) => { + self.add_ty(ty); + self.add_ty_pat(pat); + } + + ty::Slice(tt) => self.add_ty(tt), + + ty::RawPtr(ty, _) => { + self.add_ty(ty); + } + + ty::Ref(r, ty, _) => { + self.add_region(r); + self.add_ty(ty); + } + + ty::Tuple(types) => { + self.add_tys(types); + } + + ty::FnDef(_, args) => { + self.add_args(args.as_slice()); + } + + ty::FnPtr(sig_tys, _) => self.bound_computation(sig_tys, |computation, sig_tys| { + computation.add_tys(sig_tys.inputs_and_output); + }), + + ty::UnsafeBinder(bound_ty) => { + self.bound_computation(bound_ty.into(), |computation, ty| { + computation.add_ty(ty); + }) + } + } + } + + fn add_ty_pat(&mut self, pat: ::Pat) { + self.add_flags(pat.flags()); + } + + fn add_predicate(&mut self, binder: ty::Binder>) { + self.bound_computation(binder, |computation, atom| computation.add_predicate_atom(atom)); + } + + fn add_predicate_atom(&mut self, atom: ty::PredicateKind) { + match atom { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => { + self.add_args(trait_pred.trait_ref.args.as_slice()); + } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(ty::HostEffectPredicate { + trait_ref, + constness: _, + })) => { + self.add_args(trait_ref.args.as_slice()); + } + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( + a, + b, + ))) => { + self.add_region(a); + self.add_region(b); + } + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + ty, + region, + ))) => { + self.add_ty(ty); + self.add_region(region); + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { + self.add_const(ct); + self.add_ty(ty); + } + ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => { + self.add_ty(a); + self.add_ty(b); + } + ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => { + self.add_ty(a); + self.add_ty(b); + } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(ty::ProjectionPredicate { + projection_term, + term, + })) => { + self.add_alias_term(projection_term); + self.add_term(term); + } + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + self.add_term(term); + } + ty::PredicateKind::DynCompatible(_def_id) => {} + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { + self.add_const(uv); + } + ty::PredicateKind::ConstEquate(expected, found) => { + self.add_const(expected); + self.add_const(found); + } + ty::PredicateKind::Ambiguous => {} + ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) => { + self.add_alias_term(alias); + self.add_term(term); + } + ty::PredicateKind::AliasRelate(t1, t2, _) => { + self.add_term(t1); + self.add_term(t2); + } + } + } + + fn add_ty(&mut self, ty: I::Ty) { + self.add_flags(ty.flags()); + self.add_exclusive_binder(ty.outer_exclusive_binder()); + } + + fn add_tys(&mut self, tys: I::Tys) { + for ty in tys.iter() { + self.add_ty(ty); + } + } + + fn add_region(&mut self, r: I::Region) { + self.add_flags(r.flags()); + if let ty::ReBound(debruijn, _) = r.kind() { + self.add_bound_var(debruijn); + } + } + + fn add_const(&mut self, c: I::Const) { + self.add_flags(c.flags()); + self.add_exclusive_binder(c.outer_exclusive_binder()); + } + + fn add_const_kind(&mut self, c: &ty::ConstKind) { + match *c { + ty::ConstKind::Unevaluated(uv) => { + self.add_args(uv.args.as_slice()); + self.add_flags(TypeFlags::HAS_CT_PROJECTION); + } + ty::ConstKind::Infer(infer) => match infer { + ty::InferConst::Fresh(_) => self.add_flags(TypeFlags::HAS_CT_FRESH), + ty::InferConst::Var(_) => self.add_flags(TypeFlags::HAS_CT_INFER), + }, + ty::ConstKind::Bound(debruijn, _) => { + self.add_bound_var(debruijn); + self.add_flags(TypeFlags::HAS_CT_BOUND); + } + ty::ConstKind::Param(_) => { + self.add_flags(TypeFlags::HAS_CT_PARAM); + } + ty::ConstKind::Placeholder(_) => { + self.add_flags(TypeFlags::HAS_CT_PLACEHOLDER); + } + ty::ConstKind::Value(cv) => self.add_ty(cv.ty()), + ty::ConstKind::Expr(e) => self.add_args(e.args().as_slice()), + ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), + } + } + + fn add_existential_projection(&mut self, projection: &ty::ExistentialProjection) { + self.add_args(projection.args.as_slice()); + match projection.term.kind() { + ty::TermKind::Ty(ty) => self.add_ty(ty), + ty::TermKind::Const(ct) => self.add_const(ct), + } + } + + fn add_alias_ty(&mut self, alias_ty: ty::AliasTy) { + self.add_args(alias_ty.args.as_slice()); + } + + fn add_alias_term(&mut self, alias_term: ty::AliasTerm) { + self.add_args(alias_term.args.as_slice()); + } + + fn add_args(&mut self, args: &[I::GenericArg]) { + for kind in args { + match kind.kind() { + ty::GenericArgKind::Type(ty) => self.add_ty(ty), + ty::GenericArgKind::Lifetime(lt) => self.add_region(lt), + ty::GenericArgKind::Const(ct) => self.add_const(ct), + } + } + } + + fn add_term(&mut self, term: I::Term) { + match term.kind() { + ty::TermKind::Ty(ty) => self.add_ty(ty), + ty::TermKind::Const(ct) => self.add_const(ct), + } } } diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 0bc8b94bbf4cc..ce1188070ca7d 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -45,6 +45,7 @@ //! - u.fold_with(folder) //! ``` +use std::convert::Infallible; use std::mem; use std::sync::Arc; @@ -54,13 +55,7 @@ use tracing::{debug, instrument}; use crate::inherent::*; use crate::visit::{TypeVisitable, TypeVisitableExt as _}; -use crate::{self as ty, Interner}; - -#[cfg(feature = "nightly")] -type Never = !; - -#[cfg(not(feature = "nightly"))] -type Never = std::convert::Infallible; +use crate::{self as ty, Interner, TypeFlags}; /// This trait is implemented for every type that can be folded, /// providing the skeleton of the traversal. @@ -82,18 +77,24 @@ pub trait TypeFoldable: TypeVisitable + Clone { /// /// For types of interest (such as `Ty`), the implementation of this method /// calls a folder method specifically for that type (such as - /// `F::try_fold_ty`). This is where control transfers from `TypeFoldable` - /// to `TypeFolder`. + /// `F::try_fold_ty`). This is where control transfers from [`TypeFoldable`] + /// to [`FallibleTypeFolder`]. fn try_fold_with>(self, folder: &mut F) -> Result; - /// A convenient alternative to `try_fold_with` for use with infallible - /// folders. Do not override this method, to ensure coherence with - /// `try_fold_with`. - fn fold_with>(self, folder: &mut F) -> Self { - match self.try_fold_with(folder) { - Ok(t) => t, - } - } + /// The entry point for folding. To fold a value `t` with a folder `f` + /// call: `t.fold_with(f)`. + /// + /// For most types, this just traverses the value, calling `fold_with` + /// on each field/element. + /// + /// For types of interest (such as `Ty`), the implementation of this method + /// calls a folder method specifically for that type (such as + /// `F::fold_ty`). This is where control transfers from `TypeFoldable` + /// to `TypeFolder`. + /// + /// Same as [`TypeFoldable::try_fold_with`], but not fallible. Make sure to keep + /// the behavior in sync across functions. + fn fold_with>(self, folder: &mut F) -> Self; } // This trait is implemented for types of interest. @@ -112,11 +113,7 @@ pub trait TypeSuperFoldable: TypeFoldable { /// A convenient alternative to `try_super_fold_with` for use with /// infallible folders. Do not override this method, to ensure coherence /// with `try_super_fold_with`. - fn super_fold_with>(self, folder: &mut F) -> Self { - match self.try_super_fold_with(folder) { - Ok(t) => t, - } - } + fn super_fold_with>(self, folder: &mut F) -> Self; } /// This trait is implemented for every infallible folding traversal. There is @@ -128,7 +125,7 @@ pub trait TypeSuperFoldable: TypeFoldable { /// A blanket implementation of [`FallibleTypeFolder`] will defer to /// the infallible methods of this trait to ensure that the two APIs /// are coherent. -pub trait TypeFolder: FallibleTypeFolder { +pub trait TypeFolder: Sized { fn cx(&self) -> I; fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder @@ -195,42 +192,6 @@ pub trait FallibleTypeFolder: Sized { } } -// This blanket implementation of the fallible trait for infallible folders -// delegates to infallible methods to ensure coherence. -impl FallibleTypeFolder for F -where - F: TypeFolder, -{ - type Error = Never; - - fn cx(&self) -> I { - TypeFolder::cx(self) - } - - fn try_fold_binder(&mut self, t: ty::Binder) -> Result, Never> - where - T: TypeFoldable, - { - Ok(self.fold_binder(t)) - } - - fn try_fold_ty(&mut self, t: I::Ty) -> Result { - Ok(self.fold_ty(t)) - } - - fn try_fold_region(&mut self, r: I::Region) -> Result { - Ok(self.fold_region(r)) - } - - fn try_fold_const(&mut self, c: I::Const) -> Result { - Ok(self.fold_const(c)) - } - - fn try_fold_predicate(&mut self, p: I::Predicate) -> Result { - Ok(self.fold_predicate(p)) - } -} - /////////////////////////////////////////////////////////////////////////// // Traversal implementations. @@ -238,6 +199,10 @@ impl, U: TypeFoldable> TypeFoldable for (T fn try_fold_with>(self, folder: &mut F) -> Result<(T, U), F::Error> { Ok((self.0.try_fold_with(folder)?, self.1.try_fold_with(folder)?)) } + + fn fold_with>(self, folder: &mut F) -> Self { + (self.0.fold_with(folder), self.1.fold_with(folder)) + } } impl, B: TypeFoldable, C: TypeFoldable> TypeFoldable @@ -253,6 +218,10 @@ impl, B: TypeFoldable, C: TypeFoldable> Ty self.2.try_fold_with(folder)?, )) } + + fn fold_with>(self, folder: &mut F) -> Self { + (self.0.fold_with(folder), self.1.fold_with(folder), self.2.fold_with(folder)) + } } impl> TypeFoldable for Option { @@ -262,6 +231,10 @@ impl> TypeFoldable for Option { None => None, }) } + + fn fold_with>(self, folder: &mut F) -> Self { + Some(self?.fold_with(folder)) + } } impl, E: TypeFoldable> TypeFoldable for Result { @@ -271,41 +244,61 @@ impl, E: TypeFoldable> TypeFoldable for Re Err(e) => Err(e.try_fold_with(folder)?), }) } + + fn fold_with>(self, folder: &mut F) -> Self { + match self { + Ok(v) => Ok(v.fold_with(folder)), + Err(e) => Err(e.fold_with(folder)), + } + } +} + +fn fold_arc( + mut arc: Arc, + fold: impl FnOnce(T) -> Result, +) -> Result, E> { + // We merely want to replace the contained `T`, if at all possible, + // so that we don't needlessly allocate a new `Arc` or indeed clone + // the contained type. + unsafe { + // First step is to ensure that we have a unique reference to + // the contained type, which `Arc::make_mut` will accomplish (by + // allocating a new `Arc` and cloning the `T` only if required). + // This is done *before* casting to `Arc>` so that + // panicking during `make_mut` does not leak the `T`. + Arc::make_mut(&mut arc); + + // Casting to `Arc>` is safe because `ManuallyDrop` + // is `repr(transparent)`. + let ptr = Arc::into_raw(arc).cast::>(); + let mut unique = Arc::from_raw(ptr); + + // Call to `Arc::make_mut` above guarantees that `unique` is the + // sole reference to the contained value, so we can avoid doing + // a checked `get_mut` here. + let slot = Arc::get_mut(&mut unique).unwrap_unchecked(); + + // Semantically move the contained type out from `unique`, fold + // it, then move the folded value back into `unique`. Should + // folding fail, `ManuallyDrop` ensures that the "moved-out" + // value is not re-dropped. + let owned = mem::ManuallyDrop::take(slot); + let folded = fold(owned)?; + *slot = mem::ManuallyDrop::new(folded); + + // Cast back to `Arc`. + Ok(Arc::from_raw(Arc::into_raw(unique).cast())) + } } impl> TypeFoldable for Arc { - fn try_fold_with>(mut self, folder: &mut F) -> Result { - // We merely want to replace the contained `T`, if at all possible, - // so that we don't needlessly allocate a new `Arc` or indeed clone - // the contained type. - unsafe { - // First step is to ensure that we have a unique reference to - // the contained type, which `Arc::make_mut` will accomplish (by - // allocating a new `Arc` and cloning the `T` only if required). - // This is done *before* casting to `Arc>` so that - // panicking during `make_mut` does not leak the `T`. - Arc::make_mut(&mut self); - - // Casting to `Arc>` is safe because `ManuallyDrop` - // is `repr(transparent)`. - let ptr = Arc::into_raw(self).cast::>(); - let mut unique = Arc::from_raw(ptr); - - // Call to `Arc::make_mut` above guarantees that `unique` is the - // sole reference to the contained value, so we can avoid doing - // a checked `get_mut` here. - let slot = Arc::get_mut(&mut unique).unwrap_unchecked(); - - // Semantically move the contained type out from `unique`, fold - // it, then move the folded value back into `unique`. Should - // folding fail, `ManuallyDrop` ensures that the "moved-out" - // value is not re-dropped. - let owned = mem::ManuallyDrop::take(slot); - let folded = owned.try_fold_with(folder)?; - *slot = mem::ManuallyDrop::new(folded); - - // Cast back to `Arc`. - Ok(Arc::from_raw(Arc::into_raw(unique).cast())) + fn try_fold_with>(self, folder: &mut F) -> Result { + fold_arc(self, |t| t.try_fold_with(folder)) + } + + fn fold_with>(self, folder: &mut F) -> Self { + match fold_arc::(self, |t| Ok(t.fold_with(folder))) { + Ok(t) => t, } } } @@ -315,30 +308,51 @@ impl> TypeFoldable for Box { *self = (*self).try_fold_with(folder)?; Ok(self) } + + fn fold_with>(mut self, folder: &mut F) -> Self { + *self = (*self).fold_with(folder); + self + } } impl> TypeFoldable for Vec { fn try_fold_with>(self, folder: &mut F) -> Result { self.into_iter().map(|t| t.try_fold_with(folder)).collect() } + + fn fold_with>(self, folder: &mut F) -> Self { + self.into_iter().map(|t| t.fold_with(folder)).collect() + } } impl> TypeFoldable for ThinVec { fn try_fold_with>(self, folder: &mut F) -> Result { self.into_iter().map(|t| t.try_fold_with(folder)).collect() } + + fn fold_with>(self, folder: &mut F) -> Self { + self.into_iter().map(|t| t.fold_with(folder)).collect() + } } impl> TypeFoldable for Box<[T]> { fn try_fold_with>(self, folder: &mut F) -> Result { Vec::from(self).try_fold_with(folder).map(Vec::into_boxed_slice) } + + fn fold_with>(self, folder: &mut F) -> Self { + Vec::into_boxed_slice(Vec::from(self).fold_with(folder)) + } } impl, Ix: Idx> TypeFoldable for IndexVec { fn try_fold_with>(self, folder: &mut F) -> Result { self.raw.try_fold_with(folder).map(IndexVec::from_raw) } + + fn fold_with>(self, folder: &mut F) -> Self { + IndexVec::from_raw(self.raw.fold_with(folder)) + } } /////////////////////////////////////////////////////////////////////////// @@ -438,12 +452,12 @@ where pub fn fold_regions( cx: I, value: T, - mut f: impl FnMut(I::Region, ty::DebruijnIndex) -> I::Region, + f: impl FnMut(I::Region, ty::DebruijnIndex) -> I::Region, ) -> T where T: TypeFoldable, { - value.fold_with(&mut RegionFolder::new(cx, &mut f)) + value.fold_with(&mut RegionFolder::new(cx, f)) } /// Folds over the substructure of a type, visiting its component @@ -453,7 +467,7 @@ where /// new bound regions which are not visited by this visitors as /// they are not free; only regions that occur free will be /// visited by `fld_r`. -pub struct RegionFolder<'a, I: Interner> { +pub struct RegionFolder { cx: I, /// Stores the index of a binder *just outside* the stuff we have @@ -464,20 +478,21 @@ pub struct RegionFolder<'a, I: Interner> { /// Callback invokes for each free region. The `DebruijnIndex` /// points to the binder *just outside* the ones we have passed /// through. - fold_region_fn: &'a mut (dyn FnMut(I::Region, ty::DebruijnIndex) -> I::Region + 'a), + fold_region_fn: F, } -impl<'a, I: Interner> RegionFolder<'a, I> { +impl RegionFolder { #[inline] - pub fn new( - cx: I, - fold_region_fn: &'a mut dyn FnMut(I::Region, ty::DebruijnIndex) -> I::Region, - ) -> RegionFolder<'a, I> { + pub fn new(cx: I, fold_region_fn: F) -> RegionFolder { RegionFolder { cx, current_index: ty::INNERMOST, fold_region_fn } } } -impl<'a, I: Interner> TypeFolder for RegionFolder<'a, I> { +impl TypeFolder for RegionFolder +where + I: Interner, + F: FnMut(I::Region, ty::DebruijnIndex) -> I::Region, +{ fn cx(&self) -> I { self.cx } @@ -502,4 +517,34 @@ impl<'a, I: Interner> TypeFolder for RegionFolder<'a, I> { } } } + + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + if t.has_type_flags( + TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, + ) { + t.super_fold_with(self) + } else { + t + } + } + + fn fold_const(&mut self, ct: I::Const) -> I::Const { + if ct.has_type_flags( + TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, + ) { + ct.super_fold_with(self) + } else { + ct + } + } + + fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { + if p.has_type_flags( + TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_BOUND | TypeFlags::HAS_RE_ERASED, + ) { + p.super_fold_with(self) + } else { + p + } + } } diff --git a/compiler/rustc_type_ir/src/generic_arg.rs b/compiler/rustc_type_ir/src/generic_arg.rs index 008268c3bffad..c18cade0e39bc 100644 --- a/compiler/rustc_type_ir/src/generic_arg.rs +++ b/compiler/rustc_type_ir/src/generic_arg.rs @@ -1,11 +1,14 @@ use derive_where::derive_where; #[cfg(feature = "nightly")] -use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use crate::Interner; #[derive_where(Clone, Copy, PartialEq, Eq, Debug; I: Interner)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub enum GenericArgKind { Lifetime(I::Region), Type(I::Ty), @@ -13,7 +16,10 @@ pub enum GenericArgKind { } #[derive_where(Clone, Copy, PartialEq, Eq, Debug; I: Interner)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub enum TermKind { Ty(I::Ty), Const(I::Const), diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index d5edc9610a4af..c149076211739 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -1,6 +1,8 @@ +use std::fmt::Debug; + use derive_where::derive_where; #[cfg(feature = "nightly")] -use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use crate::fold::TypeFoldable; @@ -20,7 +22,10 @@ use crate::{self as ty, Interner}; /// t-types for help. #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum TypingMode { /// When checking whether impls overlap, we check whether any obligations /// are guaranteed to never hold when unifying the impls. This requires us @@ -62,13 +67,21 @@ pub enum TypingMode { /// let x: <() as Assoc>::Output = true; /// } /// ``` - Analysis { defining_opaque_types: I::DefiningOpaqueTypes }, + Analysis { defining_opaque_types_and_generators: I::LocalDefIds }, + /// The behavior during MIR borrowck is identical to `TypingMode::Analysis` + /// except that the initial value for opaque types is the type computed during + /// HIR typeck with unique unconstrained region inference variables. + /// + /// This is currently only used with by the new solver as it results in new + /// non-universal defining uses of opaque types, which is a breaking change. + /// See tests/ui/impl-trait/non-defining-use/as-projection-term.rs. + Borrowck { defining_opaque_types: I::LocalDefIds }, /// Any analysis after borrowck for a given body should be able to use all the /// hidden types defined by borrowck, without being able to define any new ones. /// /// This is currently only used by the new solver, but should be implemented in /// the old solver as well. - PostBorrowckAnalysis { defined_opaque_types: I::DefiningOpaqueTypes }, + PostBorrowckAnalysis { defined_opaque_types: I::LocalDefIds }, /// After analysis, mostly during codegen and MIR optimizations, we're able to /// reveal all opaque types. As the concrete type should *never* be observable /// directly by the user, this should not be used by checks which may expose @@ -83,13 +96,29 @@ pub enum TypingMode { impl TypingMode { /// Analysis outside of a body does not define any opaque types. pub fn non_body_analysis() -> TypingMode { - TypingMode::Analysis { defining_opaque_types: Default::default() } + TypingMode::Analysis { defining_opaque_types_and_generators: Default::default() } + } + + pub fn typeck_for_body(cx: I, body_def_id: I::LocalDefId) -> TypingMode { + TypingMode::Analysis { + defining_opaque_types_and_generators: cx + .opaque_types_and_coroutines_defined_by(body_def_id), + } } /// While typechecking a body, we need to be able to define the opaque /// types defined by that body. + /// + /// FIXME: This will be removed because it's generally not correct to define + /// opaques outside of HIR typeck. pub fn analysis_in_body(cx: I, body_def_id: I::LocalDefId) -> TypingMode { - TypingMode::Analysis { defining_opaque_types: cx.opaque_types_defined_by(body_def_id) } + TypingMode::Analysis { + defining_opaque_types_and_generators: cx.opaque_types_defined_by(body_def_id), + } + } + + pub fn borrowck(cx: I, body_def_id: I::LocalDefId) -> TypingMode { + TypingMode::Borrowck { defining_opaque_types: cx.opaque_types_defined_by(body_def_id) } } pub fn post_borrowck_analysis(cx: I, body_def_id: I::LocalDefId) -> TypingMode { @@ -99,6 +128,7 @@ impl TypingMode { } } +#[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_infer_ctxt_like")] pub trait InferCtxtLike: Sized { type Interner: Interner; fn cx(&self) -> Self::Interner; @@ -217,4 +247,32 @@ pub trait InferCtxtLike: Sized { r: ::Region, span: ::Span, ); + + type OpaqueTypeStorageEntries: Debug + Copy + Default; + fn opaque_types_storage_num_entries(&self) -> Self::OpaqueTypeStorageEntries; + fn clone_opaque_types_lookup_table( + &self, + ) -> Vec<(ty::OpaqueTypeKey, ::Ty)>; + fn clone_duplicate_opaque_types( + &self, + ) -> Vec<(ty::OpaqueTypeKey, ::Ty)>; + fn clone_opaque_types_added_since( + &self, + prev_entries: Self::OpaqueTypeStorageEntries, + ) -> Vec<(ty::OpaqueTypeKey, ::Ty)>; + + fn register_hidden_type_in_storage( + &self, + opaque_type_key: ty::OpaqueTypeKey, + hidden_ty: ::Ty, + span: ::Span, + ) -> Option<::Ty>; + fn add_duplicate_opaque_type( + &self, + opaque_type_key: ty::OpaqueTypeKey, + hidden_ty: ::Ty, + span: ::Span, + ); + + fn reset_opaque_types(&self); } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index d4134bdf3a782..ee4a8096462a0 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -146,36 +146,18 @@ pub trait Ty>: fn has_unsafe_fields(self) -> bool; fn fn_sig(self, interner: I) -> ty::Binder> { - match self.kind() { - ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr), - ty::FnDef(def_id, args) => interner.fn_sig(def_id).instantiate(interner, args), - ty::Error(_) => { - // ignore errors (#54954) - ty::Binder::dummy(ty::FnSig { - inputs_and_output: Default::default(), - c_variadic: false, - safety: I::Safety::safe(), - abi: I::Abi::rust(), - }) - } - ty::Closure(..) => panic!( - "to get the signature of a closure, use `args.as_closure().sig()` not `fn_sig()`", - ), - _ => panic!("Ty::fn_sig() called on non-fn type: {:?}", self), - } + self.kind().fn_sig(interner) } fn discriminant_ty(self, interner: I) -> I::Ty; - fn async_destructor_ty(self, interner: I) -> I::Ty; - - /// Returns `true` when the outermost type cannot be further normalized, - /// resolved, or instantiated. This includes all primitive types, but also - /// things like ADTs and trait objects, since even if their arguments or - /// nested types may be further simplified, the outermost [`ty::TyKind`] or - /// type constructor remains the same. fn is_known_rigid(self) -> bool { + self.kind().is_known_rigid() + } + + fn is_guaranteed_unsized_raw(self) -> bool { match self.kind() { + ty::Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true, ty::Bool | ty::Char | ty::Int(_) @@ -183,29 +165,26 @@ pub trait Ty>: | ty::Float(_) | ty::Adt(_, _) | ty::Foreign(_) - | ty::Str | ty::Array(_, _) | ty::Pat(_, _) - | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) - | ty::FnPtr(..) + | ty::FnPtr(_, _) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) - | ty::CoroutineWitness(..) + | ty::CoroutineWitness(_, _) | ty::Never - | ty::Tuple(_) => true, - - ty::Error(_) - | ty::Infer(_) + | ty::Tuple(_) | ty::Alias(_, _) | ty::Param(_) | ty::Bound(_, _) - | ty::Placeholder(_) => false, + | ty::Placeholder(_) + | ty::Infer(_) + | ty::Error(_) + | ty::Dynamic(_, _, ty::DynStar) => false, } } } @@ -319,6 +298,14 @@ pub trait GenericArg>: + From + From { + fn as_term(&self) -> Option { + match self.kind() { + ty::GenericArgKind::Lifetime(_) => None, + ty::GenericArgKind::Type(ty) => Some(ty.into()), + ty::GenericArgKind::Const(ct) => Some(ct.into()), + } + } + fn as_type(&self) -> Option { if let ty::GenericArgKind::Type(ty) = self.kind() { Some(ty) } else { None } } @@ -462,6 +449,14 @@ pub trait Predicate>: { fn as_clause(self) -> Option; + fn as_normalizes_to(self) -> Option>> { + let kind = self.kind(); + match kind.skip_binder() { + ty::PredicateKind::NormalizesTo(pred) => Some(kind.rebind(pred)), + _ => None, + } + } + // FIXME: Eventually uplift the impl out of rustc and make this defaulted. fn allow_normalization(self) -> bool; } @@ -603,7 +598,7 @@ pub trait Span: Copy + Debug + Hash + Eq + TypeFoldable { pub trait SliceLike: Sized + Copy { type Item: Copy; - type IntoIter: Iterator; + type IntoIter: Iterator + DoubleEndedIterator; fn iter(self) -> Self::IntoIter; diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index e765cb66d00a3..0fd2d9f3ad38b 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -15,6 +15,7 @@ use crate::solve::{CanonicalInput, ExternalConstraintsData, PredefinedOpaquesDat use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; use crate::{self as ty, search_graph}; +#[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_interner")] pub trait Interner: Sized + Copy @@ -30,6 +31,7 @@ pub trait Interner: + IrPrint> + IrPrint> + IrPrint> + + IrPrint> { type DefId: DefId; type LocalDefId: Copy + Debug + Hash + Eq + Into + TypeFoldable; @@ -54,7 +56,7 @@ pub trait Interner: data: PredefinedOpaquesData, ) -> Self::PredefinedOpaques; - type DefiningOpaqueTypes: Copy + type LocalDefIds: Copy + Debug + Hash + Default @@ -103,7 +105,21 @@ pub trait Interner: type ErrorGuaranteed: Copy + Debug + Hash + Eq; type BoundExistentialPredicates: BoundExistentialPredicates; type AllocId: Copy + Debug + Hash + Eq; - type Pat: Copy + Debug + Hash + Eq + Debug + Relate; + type Pat: Copy + + Debug + + Hash + + Eq + + Debug + + Relate + + Flags + + IntoKind>; + type PatList: Copy + + Debug + + Hash + + Default + + Eq + + TypeVisitable + + SliceLike; type Safety: Safety; type Abi: Abi; @@ -148,6 +164,8 @@ pub trait Interner: ) -> Option; fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder; + fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) + -> ty::EarlyBinder; type AdtDef: AdtDef; fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef; @@ -252,12 +270,16 @@ pub trait Interner: def_id: Self::DefId, ) -> ty::EarlyBinder>>>; + fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool; + fn has_target_features(self, def_id: Self::DefId) -> bool; fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId; fn is_lang_item(self, def_id: Self::DefId, lang_item: TraitSolverLangItem) -> bool; + fn is_default_trait(self, def_id: Self::DefId) -> bool; + fn as_lang_item(self, def_id: Self::DefId) -> Option; fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator; @@ -271,6 +293,8 @@ pub trait Interner: fn has_item_definition(self, def_id: Self::DefId) -> bool; + fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool; + fn impl_is_default(self, impl_def_id: Self::DefId) -> bool; fn impl_trait_ref(self, impl_def_id: Self::DefId) -> ty::EarlyBinder>; @@ -315,10 +339,12 @@ pub trait Interner: binder: ty::Binder, ) -> ty::Binder; - fn opaque_types_defined_by( + fn opaque_types_defined_by(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds; + + fn opaque_types_and_coroutines_defined_by( self, defining_anchor: Self::LocalDefId, - ) -> Self::DefiningOpaqueTypes; + ) -> Self::LocalDefIds; } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index 0c71f3a3df2a2..388ad09cb200c 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -2,8 +2,8 @@ use std::fmt; use crate::{ AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, - HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, ProjectionPredicate, - SubtypePredicate, TraitPredicate, TraitRef, + HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, PatternKind, + ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, }; pub trait IrPrint { @@ -57,9 +57,10 @@ define_display_via_print!( AliasTy, AliasTerm, FnSig, + PatternKind, ); -define_debug_via_print!(TraitRef, ExistentialTraitRef, ExistentialProjection); +define_debug_via_print!(TraitRef, ExistentialTraitRef, PatternKind); impl fmt::Display for OutlivesPredicate where diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 65f7cdf8f922b..699dd82fb22c9 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -2,7 +2,6 @@ /// representation of `LangItem`s used in the underlying compiler implementation. pub enum TraitSolverLangItem { // tidy-alphabetical-start - AsyncDestruct, AsyncFn, AsyncFnKindHelper, AsyncFnKindUpvars, diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 15ef4e7d6c1d2..792090effcff1 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -1,12 +1,12 @@ // tidy-alphabetical-start #![allow(rustc::usage_of_ty_tykind)] #![allow(rustc::usage_of_type_ir_inherent)] +#![allow(rustc::usage_of_type_ir_traits)] #![cfg_attr( feature = "nightly", feature(associated_type_defaults, never_type, rustc_attrs, negative_impls) )] #![cfg_attr(feature = "nightly", allow(internal_features))] -#![warn(unreachable_pub)] // tidy-alphabetical-end extern crate self as rustc_type_ir; @@ -18,15 +18,10 @@ use std::hash::Hash; use rustc_macros::{Decodable, Encodable, HashStable_NoContext}; // These modules are `pub` since they are not glob-imported. -#[macro_use] -pub mod visit; -#[cfg(feature = "nightly")] -pub mod codec; pub mod data_structures; pub mod elaborate; pub mod error; pub mod fast_reject; -pub mod fold; #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_inherent")] pub mod inherent; pub mod ir_print; @@ -36,6 +31,7 @@ pub mod outlives; pub mod relate; pub mod search_graph; pub mod solve; +pub mod walk; // These modules are not `pub` since they are glob-imported. #[macro_use] @@ -44,16 +40,19 @@ mod binder; mod canonical; mod const_kind; mod flags; +mod fold; mod generic_arg; mod infer_ctxt; mod interner; mod opaque_ty; +mod pattern; mod predicate; mod predicate_kind; mod region_kind; mod ty_info; mod ty_kind; mod upcast; +mod visit; pub use AliasTyKind::*; pub use DynKind::*; @@ -63,14 +62,14 @@ pub use TyKind::*; pub use Variance::*; pub use binder::*; pub use canonical::*; -#[cfg(feature = "nightly")] -pub use codec::*; pub use const_kind::*; pub use flags::*; +pub use fold::*; pub use generic_arg::*; pub use infer_ctxt::*; pub use interner::*; pub use opaque_ty::*; +pub use pattern::*; pub use predicate::*; pub use predicate_kind::*; pub use region_kind::*; @@ -78,6 +77,7 @@ pub use rustc_ast_ir::{Movability, Mutability, Pinnedness}; pub use ty_info::*; pub use ty_kind::*; pub use upcast::*; +pub use visit::*; rustc_index::newtype_index! { /// A [De Bruijn index][dbi] is a standard means of representing diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs index fab1a11304d57..c8c293121ca0a 100644 --- a/compiler/rustc_type_ir/src/macros.rs +++ b/compiler/rustc_type_ir/src/macros.rs @@ -1,10 +1,11 @@ /// Used for types that are `Copy` and which **do not care arena /// allocated data** (i.e., don't need to be folded). +#[macro_export] macro_rules! TrivialTypeTraversalImpls { ($($ty:ty,)+) => { $( - impl $crate::fold::TypeFoldable for $ty { - fn try_fold_with>( + impl $crate::TypeFoldable for $ty { + fn try_fold_with>( self, _: &mut F, ) -> ::std::result::Result { @@ -12,7 +13,7 @@ macro_rules! TrivialTypeTraversalImpls { } #[inline] - fn fold_with>( + fn fold_with>( self, _: &mut F, ) -> Self { @@ -20,14 +21,14 @@ macro_rules! TrivialTypeTraversalImpls { } } - impl $crate::visit::TypeVisitable for $ty { + impl $crate::TypeVisitable for $ty { #[inline] - fn visit_with>( + fn visit_with>( &self, _: &mut F) -> F::Result { - ::output() + ::output() } } )+ diff --git a/compiler/rustc_type_ir/src/opaque_ty.rs b/compiler/rustc_type_ir/src/opaque_ty.rs index 6d61a52723aeb..416d355fd03bd 100644 --- a/compiler/rustc_type_ir/src/opaque_ty.rs +++ b/compiler/rustc_type_ir/src/opaque_ty.rs @@ -1,6 +1,6 @@ use derive_where::derive_where; #[cfg(feature = "nightly")] -use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use crate::inherent::*; @@ -8,7 +8,10 @@ use crate::{self as ty, Interner}; #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub struct OpaqueTypeKey { pub def_id: I::LocalDefId, pub args: I::GenericArgs, diff --git a/compiler/rustc_type_ir/src/pattern.rs b/compiler/rustc_type_ir/src/pattern.rs new file mode 100644 index 0000000000000..7e56565917c67 --- /dev/null +++ b/compiler/rustc_type_ir/src/pattern.rs @@ -0,0 +1,17 @@ +use derive_where::derive_where; +#[cfg(feature = "nightly")] +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; +use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; + +use crate::Interner; + +#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] +pub enum PatternKind { + Range { start: I::Const, end: I::Const }, + Or(I::PatList), +} diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 46385ca3a6fdb..f02d9c988c8de 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -3,7 +3,9 @@ use std::hash::Hash; use derive_where::derive_where; #[cfg(feature = "nightly")] -use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::{ + Decodable, Decodable_NoContext, Encodable, Encodable_NoContext, HashStable_NoContext, +}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::inherent::*; @@ -20,7 +22,10 @@ use crate::{self as ty, Interner}; #[derive_where(Eq; I: Interner, A: Eq)] #[derive_where(Debug; I: Interner, A: fmt::Debug)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct OutlivesPredicate(pub A, pub I::Region); // FIXME: We manually derive `Lift` because the `derive(Lift_Generic)` doesn't @@ -50,7 +55,10 @@ where /// that case the `Self` parameter is absent from the generic parameters. #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct TraitRef { pub def_id: I::DefId, pub args: I::GenericArgs, @@ -122,7 +130,10 @@ impl ty::Binder> { #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct TraitPredicate { pub trait_ref: TraitRef, @@ -177,7 +188,10 @@ impl fmt::Debug for TraitPredicate { } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub enum ImplPolarity { /// `impl Trait for Type` Positive, @@ -215,7 +229,10 @@ impl ImplPolarity { /// Distinguished from [`ImplPolarity`] since we never compute goals with /// "reservation" level. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub enum PredicatePolarity { /// `Type: Trait` Positive, @@ -244,7 +261,10 @@ impl fmt::Display for PredicatePolarity { #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub enum ExistentialPredicate { /// E.g., `Iterator`. Trait(ExistentialTraitRef), @@ -289,7 +309,10 @@ impl ty::Binder> { /// type and lifetime parameters (`[X, Y]` and `['a, 'b]` above). #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct ExistentialTraitRef { pub def_id: I::DefId, pub args: I::GenericArgs, @@ -351,9 +374,12 @@ impl ty::Binder> { } /// A `ProjectionPredicate` for an `ExistentialTraitRef`. -#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] +#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct ExistentialProjection { pub def_id: I::DefId, pub args: I::GenericArgs, @@ -444,14 +470,19 @@ pub enum AliasTermKind { /// An opaque type (usually from `impl Trait` in type aliases or function return types) /// Can only be normalized away in PostAnalysis mode or its defining scope. OpaqueTy, - /// A type alias that actually checks its trait bounds. + /// A free type alias that actually checks its trait bounds. /// Currently only used if the type alias references opaque types. /// Can always be normalized away. - WeakTy, - /// An unevaluated const coming from a generic const expression. + FreeTy, + + /// An unevaluated anonymous constants. UnevaluatedConst, /// An unevaluated const coming from an associated const. ProjectionConst, + /// A top level const item not part of a trait or impl. + FreeConst, + /// An associated const in an inherent `impl` + InherentConst, } impl AliasTermKind { @@ -460,11 +491,27 @@ impl AliasTermKind { AliasTermKind::ProjectionTy => "associated type", AliasTermKind::ProjectionConst => "associated const", AliasTermKind::InherentTy => "inherent associated type", + AliasTermKind::InherentConst => "inherent associated const", AliasTermKind::OpaqueTy => "opaque type", - AliasTermKind::WeakTy => "type alias", + AliasTermKind::FreeTy => "type alias", + AliasTermKind::FreeConst => "unevaluated constant", AliasTermKind::UnevaluatedConst => "unevaluated constant", } } + + pub fn is_type(self) -> bool { + match self { + AliasTermKind::ProjectionTy + | AliasTermKind::InherentTy + | AliasTermKind::OpaqueTy + | AliasTermKind::FreeTy => true, + + AliasTermKind::UnevaluatedConst + | AliasTermKind::ProjectionConst + | AliasTermKind::InherentConst + | AliasTermKind::FreeConst => false, + } + } } impl From for AliasTermKind { @@ -472,7 +519,7 @@ impl From for AliasTermKind { match value { ty::Projection => AliasTermKind::ProjectionTy, ty::Opaque => AliasTermKind::OpaqueTy, - ty::Weak => AliasTermKind::WeakTy, + ty::Free => AliasTermKind::FreeTy, ty::Inherent => AliasTermKind::InherentTy, } } @@ -485,7 +532,10 @@ impl From for AliasTermKind { /// * For an opaque type, there is no explicit syntax. #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct AliasTerm { /// The parameters of the associated or opaque item. /// @@ -536,8 +586,11 @@ impl AliasTerm { AliasTermKind::ProjectionTy | AliasTermKind::InherentTy | AliasTermKind::OpaqueTy - | AliasTermKind::WeakTy => {} - AliasTermKind::UnevaluatedConst | AliasTermKind::ProjectionConst => { + | AliasTermKind::FreeTy => {} + AliasTermKind::InherentConst + | AliasTermKind::FreeConst + | AliasTermKind::UnevaluatedConst + | AliasTermKind::ProjectionConst => { panic!("Cannot turn `UnevaluatedConst` into `AliasTy`") } } @@ -568,24 +621,25 @@ impl AliasTerm { ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, ) .into(), - AliasTermKind::WeakTy => Ty::new_alias( + AliasTermKind::FreeTy => Ty::new_alias( interner, - ty::AliasTyKind::Weak, + ty::AliasTyKind::Free, ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, ) .into(), - AliasTermKind::UnevaluatedConst | AliasTermKind::ProjectionConst => { - I::Const::new_unevaluated( - interner, - ty::UnevaluatedConst::new(self.def_id, self.args), - ) - .into() - } + AliasTermKind::FreeConst + | AliasTermKind::InherentConst + | AliasTermKind::UnevaluatedConst + | AliasTermKind::ProjectionConst => I::Const::new_unevaluated( + interner, + ty::UnevaluatedConst::new(self.def_id, self.args), + ) + .into(), } } } -/// The following methods work only with (trait) associated type projections. +/// The following methods work only with (trait) associated term projections. impl AliasTerm { pub fn self_ty(self) -> I::Ty { self.args.type_at(0) @@ -628,6 +682,38 @@ impl AliasTerm { pub fn trait_ref(self, interner: I) -> TraitRef { self.trait_ref_and_own_args(interner).0 } + + /// Extract the own args from this projection. + /// For example, if this is a projection of `::Item<'a>`, + /// then this function would return the slice `['a]` as the own args. + pub fn own_args(self, interner: I) -> I::GenericArgsSlice { + self.trait_ref_and_own_args(interner).1 + } +} + +/// The following methods work only with inherent associated term projections. +impl AliasTerm { + /// Transform the generic parameters to have the given `impl` args as the base and the GAT args on top of that. + /// + /// Does the following transformation: + /// + /// ```text + /// [Self, P_0...P_m] -> [I_0...I_n, P_0...P_m] + /// + /// I_i impl args + /// P_j GAT args + /// ``` + pub fn rebase_inherent_args_onto_impl( + self, + impl_args: I::GenericArgs, + interner: I, + ) -> I::GenericArgs { + debug_assert!(matches!( + self.kind(interner), + AliasTermKind::InherentTy | AliasTermKind::InherentConst + )); + interner.mk_args_from_iter(impl_args.iter().chain(self.args.iter().skip(1))) + } } impl From> for AliasTerm { @@ -656,7 +742,10 @@ impl From> for AliasTerm { /// instances to normalize the LHS. #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct ProjectionPredicate { pub projection_term: AliasTerm, pub term: I::Term, @@ -711,7 +800,10 @@ impl fmt::Debug for ProjectionPredicate { /// be an unconstrained inference variable which is used as the output. #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct NormalizesTo { pub alias: AliasTerm, pub term: I::Term, @@ -743,7 +835,10 @@ impl fmt::Debug for NormalizesTo { #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub struct HostEffectPredicate { pub trait_ref: ty::TraitRef, pub constness: BoundConstness, @@ -784,7 +879,10 @@ impl ty::Binder> { /// presenting user diagnostics. #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct SubtypePredicate { pub a_is_expected: bool, pub a: I::Ty, @@ -794,14 +892,20 @@ pub struct SubtypePredicate { /// Encodes that we have to coerce *from* the `a` type to the `b` type. #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct CoercePredicate { pub a: I::Ty, pub b: I::Ty, } #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum BoundConstness { /// `Type: const Trait` /// diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index 21f4456abd114..4e41fd16ffd76 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -2,7 +2,7 @@ use std::fmt; use derive_where::derive_where; #[cfg(feature = "nightly")] -use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use crate::{self as ty, Interner}; @@ -11,7 +11,10 @@ use crate::{self as ty, Interner}; /// by implied bounds. #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum ClauseKind { /// Corresponds to `where Foo: Bar`. `Foo` here would be /// the `Self` type of the trait reference and `A`, `B`, and `C` @@ -33,7 +36,7 @@ pub enum ClauseKind { ConstArgHasType(I::Const, I::Ty), /// No syntax: `T` well-formed. - WellFormed(I::GenericArg), + WellFormed(I::Term), /// Constant initializer must evaluate successfully. ConstEvaluatable(I::Const), @@ -47,7 +50,10 @@ pub enum ClauseKind { #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum PredicateKind { /// Prove a clause Clause(ClauseKind), @@ -97,7 +103,10 @@ pub enum PredicateKind { } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)] -#[cfg_attr(feature = "nightly", derive(HashStable_NoContext, Encodable, Decodable))] +#[cfg_attr( + feature = "nightly", + derive(HashStable_NoContext, Encodable_NoContext, Decodable_NoContext) +)] pub enum AliasRelationDirection { Equate, Subtype, diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index e0b3973700783..1b5d04e6025fa 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -4,7 +4,7 @@ use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] -use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use self::RegionKind::*; use crate::{DebruijnIndex, Interner}; @@ -126,7 +126,7 @@ rustc_index::newtype_index! { /// [2]: https://smallcultfollowing.com/babysteps/blog/2013/11/04/intermingled-parameter-lists/ /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable))] +#[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext))] pub enum RegionKind { /// A region parameter; for example `'a` in `impl<'a> Trait for &'a ()`. /// @@ -193,7 +193,7 @@ impl fmt::Debug for RegionKind { ReVar(vid) => write!(f, "{vid:?}"), - RePlaceholder(placeholder) => write!(f, "{placeholder:?}"), + RePlaceholder(placeholder) => write!(f, "'{placeholder:?}"), // Use `'{erased}` as the output instead of `'erased` so that its more obviously distinct from // a `ReEarlyParam` named `'erased`. Technically that would print as `'erased/#IDX` so this is diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index d065384b58e23..e3c4a793b37f6 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -273,8 +273,10 @@ impl Relate for ty::AliasTerm { false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle )?, ty::AliasTermKind::ProjectionTy - | ty::AliasTermKind::WeakTy + | ty::AliasTermKind::FreeConst + | ty::AliasTermKind::FreeTy | ty::AliasTermKind::InherentTy + | ty::AliasTermKind::InherentConst | ty::AliasTermKind::UnevaluatedConst | ty::AliasTermKind::ProjectionConst => { relate_args_invariantly(relation, a.args, b.args)? diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index d49f8d3093db7..8dd7c4df24421 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -137,6 +137,7 @@ where Ok(a) } TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => structurally_relate_tys(relation, a, b), } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 18e84db5d686c..1acd5d5c2af4b 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -13,6 +13,7 @@ /// behavior as long as the resulting behavior is still correct. use std::cmp::Ordering; use std::collections::BTreeMap; +use std::collections::hash_map::Entry; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; @@ -20,7 +21,7 @@ use std::marker::PhantomData; use derive_where::derive_where; use rustc_index::{Idx, IndexVec}; #[cfg(feature = "nightly")] -use rustc_macros::HashStable_NoContext; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use tracing::debug; use crate::data_structures::HashMap; @@ -111,21 +112,38 @@ pub trait Delegate { /// In the initial iteration of a cycle, we do not yet have a provisional /// result. In the case we return an initial provisional result depending /// on the kind of cycle. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub enum PathKind { - Coinductive, + /// A path consisting of only inductive/unproductive steps. Their initial + /// provisional result is `Err(NoSolution)`. We currently treat them as + /// `PathKind::Unknown` during coherence until we're fully confident in + /// our approach. Inductive, + /// A path which is not be coinductive right now but we may want + /// to change of them to be so in the future. We return an ambiguous + /// result in this case to prevent people from relying on this. + Unknown, + /// A path with at least one coinductive step. Such cycles hold. + Coinductive, } + impl PathKind { /// Returns the path kind when merging `self` with `rest`. /// /// Given an inductive path `self` and a coinductive path `rest`, /// the path `self -> rest` would be coinductive. + /// + /// This operation represents an ordering and would be equivalent + /// to `max(self, rest)`. fn extend(self, rest: PathKind) -> PathKind { - match self { - PathKind::Coinductive => PathKind::Coinductive, - PathKind::Inductive => rest, + match (self, rest) { + (PathKind::Coinductive, _) | (_, PathKind::Coinductive) => PathKind::Coinductive, + (PathKind::Unknown, _) | (_, PathKind::Unknown) => PathKind::Unknown, + (PathKind::Inductive, PathKind::Inductive) => PathKind::Inductive, } } } @@ -159,9 +177,6 @@ impl UsageKind { } } } - fn and_merge(&mut self, other: impl Into) { - *self = self.merge(other); - } } /// For each goal we track whether the paths from this goal @@ -297,7 +312,7 @@ impl CycleHeads { let path_from_entry = match step_kind { PathKind::Coinductive => AllPathsToHeadCoinductive::Yes, - PathKind::Inductive => path_from_entry, + PathKind::Unknown | PathKind::Inductive => path_from_entry, }; self.insert(head, path_from_entry); @@ -305,6 +320,63 @@ impl CycleHeads { } } +bitflags::bitflags! { + /// Tracks how nested goals have been accessed. This is necessary to disable + /// global cache entries if computing them would otherwise result in a cycle or + /// access a provisional cache entry. + #[derive(Debug, Clone, Copy)] + pub struct PathsToNested: u8 { + /// The initial value when adding a goal to its own nested goals. + const EMPTY = 1 << 0; + const INDUCTIVE = 1 << 1; + const UNKNOWN = 1 << 2; + const COINDUCTIVE = 1 << 3; + } +} +impl From for PathsToNested { + fn from(path: PathKind) -> PathsToNested { + match path { + PathKind::Inductive => PathsToNested::INDUCTIVE, + PathKind::Unknown => PathsToNested::UNKNOWN, + PathKind::Coinductive => PathsToNested::COINDUCTIVE, + } + } +} +impl PathsToNested { + /// The implementation of this function is kind of ugly. We check whether + /// there currently exist 'weaker' paths in the set, if so we upgrade these + /// paths to at least `path`. + #[must_use] + fn extend_with(mut self, path: PathKind) -> Self { + match path { + PathKind::Inductive => { + if self.intersects(PathsToNested::EMPTY) { + self.remove(PathsToNested::EMPTY); + self.insert(PathsToNested::INDUCTIVE); + } + } + PathKind::Unknown => { + if self.intersects(PathsToNested::EMPTY | PathsToNested::INDUCTIVE) { + self.remove(PathsToNested::EMPTY | PathsToNested::INDUCTIVE); + self.insert(PathsToNested::UNKNOWN); + } + } + PathKind::Coinductive => { + if self.intersects( + PathsToNested::EMPTY | PathsToNested::INDUCTIVE | PathsToNested::UNKNOWN, + ) { + self.remove( + PathsToNested::EMPTY | PathsToNested::INDUCTIVE | PathsToNested::UNKNOWN, + ); + self.insert(PathsToNested::COINDUCTIVE); + } + } + } + + self + } +} + /// The nested goals of each stack entry and the path from the /// stack entry to that nested goal. /// @@ -322,15 +394,18 @@ impl CycleHeads { /// results from a the cycle BAB depending on the cycle root. #[derive_where(Debug, Default, Clone; X: Cx)] struct NestedGoals { - nested_goals: HashMap, + nested_goals: HashMap, } impl NestedGoals { fn is_empty(&self) -> bool { self.nested_goals.is_empty() } - fn insert(&mut self, input: X::Input, path_from_entry: UsageKind) { - self.nested_goals.entry(input).or_insert(path_from_entry).and_merge(path_from_entry); + fn insert(&mut self, input: X::Input, paths_to_nested: PathsToNested) { + match self.nested_goals.entry(input) { + Entry::Occupied(mut entry) => *entry.get_mut() |= paths_to_nested, + Entry::Vacant(entry) => drop(entry.insert(paths_to_nested)), + } } /// Adds the nested goals of a nested goal, given that the path `step_kind` from this goal @@ -341,18 +416,15 @@ impl NestedGoals { /// the same as for the child. fn extend_from_child(&mut self, step_kind: PathKind, nested_goals: &NestedGoals) { #[allow(rustc::potential_query_instability)] - for (input, path_from_entry) in nested_goals.iter() { - let path_from_entry = match step_kind { - PathKind::Coinductive => UsageKind::Single(PathKind::Coinductive), - PathKind::Inductive => path_from_entry, - }; - self.insert(input, path_from_entry); + for (input, paths_to_nested) in nested_goals.iter() { + let paths_to_nested = paths_to_nested.extend_with(step_kind); + self.insert(input, paths_to_nested); } } #[cfg_attr(feature = "nightly", rustc_lint_query_instability)] #[allow(rustc::potential_query_instability)] - fn iter(&self) -> impl Iterator { + fn iter(&self) -> impl Iterator + '_ { self.nested_goals.iter().map(|(i, p)| (*i, *p)) } @@ -490,7 +562,7 @@ impl, X: Cx> SearchGraph { // goals as this change may cause them to now depend on additional // goals, resulting in new cycles. See the dev-guide for examples. if parent_depends_on_cycle { - parent.nested_goals.insert(parent.input, UsageKind::Single(PathKind::Inductive)) + parent.nested_goals.insert(parent.input, PathsToNested::EMPTY); } } } @@ -666,7 +738,7 @@ impl, X: Cx> SearchGraph { // // We must therefore not use the global cache entry for `B` in that case. // See tests/ui/traits/next-solver/cycles/hidden-by-overflow.rs - last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Inductive)); + last.nested_goals.insert(last.input, PathsToNested::EMPTY); } debug!("encountered stack overflow"); @@ -749,16 +821,11 @@ impl, X: Cx> SearchGraph { // We now care about the path from the next highest cycle head to the // provisional cache entry. - match path_from_head { - PathKind::Coinductive => {} - PathKind::Inductive => { - *path_from_head = Self::cycle_path_kind( - &self.stack, - stack_entry.step_kind_from_parent, - head, - ) - } - } + *path_from_head = path_from_head.extend(Self::cycle_path_kind( + &self.stack, + stack_entry.step_kind_from_parent, + head, + )); // Mutate the result of the provisional cache entry in case we did // not reach a fixpoint. *result = mutate_result(input, *result); @@ -858,7 +925,7 @@ impl, X: Cx> SearchGraph { for &ProvisionalCacheEntry { encountered_overflow, ref heads, - path_from_head, + path_from_head: head_to_provisional, result: _, } in entries.iter() { @@ -870,24 +937,19 @@ impl, X: Cx> SearchGraph { // A provisional cache entry only applies if the path from its highest head // matches the path when encountering the goal. + // + // We check if any of the paths taken while computing the global goal + // would end up with an applicable provisional cache entry. let head = heads.highest_cycle_head(); - let full_path = match Self::cycle_path_kind(stack, step_kind_from_parent, head) { - PathKind::Coinductive => UsageKind::Single(PathKind::Coinductive), - PathKind::Inductive => path_from_global_entry, - }; - - match (full_path, path_from_head) { - (UsageKind::Mixed, _) - | (UsageKind::Single(PathKind::Coinductive), PathKind::Coinductive) - | (UsageKind::Single(PathKind::Inductive), PathKind::Inductive) => { - debug!( - ?full_path, - ?path_from_head, - "cache entry not applicable due to matching paths" - ); - return false; - } - _ => debug!(?full_path, ?path_from_head, "paths don't match"), + let head_to_curr = Self::cycle_path_kind(stack, step_kind_from_parent, head); + let full_paths = path_from_global_entry.extend_with(head_to_curr); + if full_paths.contains(head_to_provisional.into()) { + debug!( + ?full_paths, + ?head_to_provisional, + "cache entry not applicable due to matching paths" + ); + return false; } } } @@ -986,8 +1048,8 @@ impl, X: Cx> SearchGraph { let last = &mut self.stack[last_index]; last.reached_depth = last.reached_depth.max(next_index); - last.nested_goals.insert(input, UsageKind::Single(step_kind_from_parent)); - last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Inductive)); + last.nested_goals.insert(input, step_kind_from_parent.into()); + last.nested_goals.insert(last.input, PathsToNested::EMPTY); if last_index != head { last.heads.insert(head, step_kind_from_parent); } diff --git a/compiler/rustc_type_ir/src/solve/inspect.rs b/compiler/rustc_type_ir/src/solve/inspect.rs index 18fb71dd290e7..b10641b287d4a 100644 --- a/compiler/rustc_type_ir/src/solve/inspect.rs +++ b/compiler/rustc_type_ir/src/solve/inspect.rs @@ -118,10 +118,12 @@ pub enum ProbeKind { /// Used in the probe that wraps normalizing the non-self type for the unsize /// trait, which is also structurally matched on. UnsizeAssembly, - /// During upcasting from some source object to target object type, used to - /// do a probe to find out what projection type(s) may be used to prove that - /// the source type upholds all of the target type's object bounds. - UpcastProjectionCompatibility, + /// Used to do a probe to find out what projection type(s) match a given + /// alias bound or projection predicate. For trait upcasting, this is used + /// to prove that the source type upholds all of the target type's object + /// bounds. For object type bounds, this is used when eagerly replacing + /// supertrait aliases. + ProjectionCompatibility, /// Looking for param-env candidates that satisfy the trait ref for a projection. ShadowedEnvProbing, /// Try to unify an opaque type with an existing key in the storage. diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index b93668b5111ba..2e05c23a6458c 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -5,7 +5,7 @@ use std::hash::Hash; use derive_where::derive_where; #[cfg(feature = "nightly")] -use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::search_graph::PathKind; @@ -38,7 +38,10 @@ pub struct NoSolution; #[derive_where(Eq; I: Interner, P: Eq)] #[derive_where(Debug; I: Interner, P: fmt::Debug)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct Goal { pub param_env: I::ParamEnv, pub predicate: P, @@ -58,28 +61,34 @@ impl Goal { /// Why a specific goal has to be proven. /// /// This is necessary as we treat nested goals different depending on -/// their source. This is currently mostly used by proof tree visitors -/// but will be used by cycle handling in the future. +/// their source. This is used to decide whether a cycle is coinductive. +/// See the documentation of `EvalCtxt::step_kind_for_source` for more details +/// about this. +/// +/// It is also used by proof tree visitors, e.g. for diagnostics purposes. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum GoalSource { Misc, - /// We're proving a where-bound of an impl. + /// A nested goal required to prove that types are equal/subtypes. + /// This is always an unproductive step. /// - /// FIXME(-Znext-solver=coinductive): Explain how and why this - /// changes whether cycles are coinductive. + /// This is also used for all `NormalizesTo` goals as we they are used + /// to relate types in `AliasRelate`. + TypeRelating, + /// We're proving a where-bound of an impl. ImplWhereBound, /// Const conditions that need to hold for `~const` alias bounds to hold. - /// - /// FIXME(-Znext-solver=coinductive): Are these even coinductive? AliasBoundConstCondition, /// Instantiating a higher-ranked goal and re-proving it. InstantiateHigherRanked, /// Predicate required for an alias projection to be well-formed. - /// This is used in two places: projecting to an opaque whose hidden type - /// is already registered in the opaque type storage, and for rigid projections. + /// This is used in three places: + /// 1. projecting to an opaque whose hidden type is already registered in + /// the opaque type storage, + /// 2. for rigid projections's trait goal, + /// 3. for GAT where clauses. AliasWellFormed, - /// In case normalizing aliases in nested goals cycles, eagerly normalizing these /// aliases in the context of the parent may incorrectly change the cycle kind. /// Normalizing aliases in goals therefore tracks the original path kind for this @@ -95,7 +104,10 @@ pub enum GoalSource { #[derive_where(Eq; I: Interner, Goal: Eq)] #[derive_where(Debug; I: Interner, Goal: fmt::Debug)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct QueryInput { pub goal: Goal, pub predefined_opaques_in_body: I::PredefinedOpaques, @@ -104,7 +116,10 @@ pub struct QueryInput { /// Opaques that are defined in the inference context before a query is called. #[derive_where(Clone, Hash, PartialEq, Eq, Debug, Default; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct PredefinedOpaquesData { pub opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, } @@ -132,9 +147,8 @@ pub enum CandidateSource { /// For a list of all traits with builtin impls, check out the /// `EvalCtxt::assemble_builtin_impl_candidates` method. BuiltinImpl(BuiltinImplSource), - /// An assumption from the environment. - /// - /// More precisely we've used the `n-th` assumption in the `param_env`. + /// An assumption from the environment. Stores a [`ParamEnvSource`], since we + /// prefer non-global param-env candidates in candidate assembly. /// /// ## Examples /// @@ -145,7 +159,7 @@ pub enum CandidateSource { /// (x.clone(), x) /// } /// ``` - ParamEnv(usize), + ParamEnv(ParamEnvSource), /// If the self type is an alias type, e.g. an opaque type or a projection, /// we know the bounds on that alias to hold even without knowing its concrete /// underlying type. @@ -175,7 +189,18 @@ pub enum CandidateSource { } #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))] +pub enum ParamEnvSource { + /// Preferred eagerly. + NonGlobal, + // Not considered unless there are non-global param-env candidates too. + Global, +} + +#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] +#[cfg_attr( + feature = "nightly", + derive(HashStable_NoContext, Encodable_NoContext, Decodable_NoContext) +)] pub enum BuiltinImplSource { /// A built-in impl that is considered trivial, without any nested requirements. They /// are preferred over where-clauses, and we want to track them explicitly. @@ -189,11 +214,6 @@ pub enum BuiltinImplSource { /// /// The index is only used for winnowing. TraitUpcasting(usize), - /// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`. - /// - /// This can be removed when `feature(tuple_unsizing)` is stabilized, since we only - /// use it to detect when unsizing tuples in hir typeck. - TupleUnsizing, } #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] @@ -253,17 +273,17 @@ impl Certainty { /// however matter for diagnostics. If `T: Foo` resulted in overflow and `T: Bar` /// in ambiguity without changing the inference state, we still want to tell the /// user that `T: Baz` results in overflow. - pub fn unify_with(self, other: Certainty) -> Certainty { + pub fn and(self, other: Certainty) -> Certainty { match (self, other) { (Certainty::Yes, Certainty::Yes) => Certainty::Yes, (Certainty::Yes, Certainty::Maybe(_)) => other, (Certainty::Maybe(_), Certainty::Yes) => self, - (Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.unify_with(b)), + (Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.and(b)), } } pub const fn overflow(suggest_increasing_limit: bool) -> Certainty { - Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }) + Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false }) } } @@ -276,19 +296,58 @@ pub enum MaybeCause { /// or we hit a case where we just don't bother, e.g. `?x: Trait` goals. Ambiguity, /// We gave up due to an overflow, most often by hitting the recursion limit. - Overflow { suggest_increasing_limit: bool }, + Overflow { suggest_increasing_limit: bool, keep_constraints: bool }, } impl MaybeCause { - fn unify_with(self, other: MaybeCause) -> MaybeCause { + fn and(self, other: MaybeCause) -> MaybeCause { match (self, other) { (MaybeCause::Ambiguity, MaybeCause::Ambiguity) => MaybeCause::Ambiguity, (MaybeCause::Ambiguity, MaybeCause::Overflow { .. }) => other, (MaybeCause::Overflow { .. }, MaybeCause::Ambiguity) => self, ( - MaybeCause::Overflow { suggest_increasing_limit: a }, - MaybeCause::Overflow { suggest_increasing_limit: b }, - ) => MaybeCause::Overflow { suggest_increasing_limit: a || b }, + MaybeCause::Overflow { + suggest_increasing_limit: limit_a, + keep_constraints: keep_a, + }, + MaybeCause::Overflow { + suggest_increasing_limit: limit_b, + keep_constraints: keep_b, + }, + ) => MaybeCause::Overflow { + suggest_increasing_limit: limit_a && limit_b, + keep_constraints: keep_a && keep_b, + }, + } + } + + pub fn or(self, other: MaybeCause) -> MaybeCause { + match (self, other) { + (MaybeCause::Ambiguity, MaybeCause::Ambiguity) => MaybeCause::Ambiguity, + + // When combining ambiguity + overflow, we can keep constraints. + ( + MaybeCause::Ambiguity, + MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ }, + ) => MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: true }, + ( + MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ }, + MaybeCause::Ambiguity, + ) => MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: true }, + + ( + MaybeCause::Overflow { + suggest_increasing_limit: limit_a, + keep_constraints: keep_a, + }, + MaybeCause::Overflow { + suggest_increasing_limit: limit_b, + keep_constraints: keep_b, + }, + ) => MaybeCause::Overflow { + suggest_increasing_limit: limit_a || limit_b, + keep_constraints: keep_a || keep_b, + }, } } } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 83631f7fe343f..cf2e4284d10da 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -6,9 +6,8 @@ use rustc_ast_ir::Mutability; #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] -use rustc_data_structures::unify::{NoError, UnifyKey, UnifyValue}; -#[cfg(feature = "nightly")] -use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; +use rustc_type_ir::data_structures::{NoError, UnifyKey, UnifyValue}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use self::TyKind::*; @@ -22,7 +21,10 @@ mod closure; /// Specifies how a trait object is represented. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum DynKind { /// An unsized `dyn Trait` object Dyn, @@ -36,7 +38,10 @@ pub enum DynKind { } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum AliasTyKind { /// A projection `::AssocType`. /// Can get normalized away if monomorphic enough. @@ -49,7 +54,7 @@ pub enum AliasTyKind { /// A type alias that actually checks its trait bounds. /// Currently only used if the type alias references opaque types. /// Can always be normalized away. - Weak, + Free, } impl AliasTyKind { @@ -58,7 +63,7 @@ impl AliasTyKind { AliasTyKind::Projection => "associated type", AliasTyKind::Inherent => "inherent associated type", AliasTyKind::Opaque => "opaque type", - AliasTyKind::Weak => "type alias", + AliasTyKind::Free => "type alias", } } } @@ -69,7 +74,10 @@ impl AliasTyKind { /// converted to this representation using `::lower_ty`. #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "IrTyKind")] #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum TyKind { /// The primitive boolean type. Written as `bool`. Bool, @@ -215,7 +223,7 @@ pub enum TyKind { /// A tuple type. For example, `(i32, bool)`. Tuple(I::Tys), - /// A projection, opaque type, weak type alias, or inherent associated type. + /// A projection, opaque type, free type alias, or inherent associated type. /// All of these types are represented as pairs of def-id and args, and can /// be normalized, so they are grouped conceptually. Alias(AliasTyKind, AliasTy), @@ -264,6 +272,68 @@ pub enum TyKind { Error(I::ErrorGuaranteed), } +impl TyKind { + pub fn fn_sig(self, interner: I) -> ty::Binder> { + match self { + ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr), + ty::FnDef(def_id, args) => interner.fn_sig(def_id).instantiate(interner, args), + ty::Error(_) => { + // ignore errors (#54954) + ty::Binder::dummy(ty::FnSig { + inputs_and_output: Default::default(), + c_variadic: false, + safety: I::Safety::safe(), + abi: I::Abi::rust(), + }) + } + ty::Closure(..) => panic!( + "to get the signature of a closure, use `args.as_closure().sig()` not `fn_sig()`", + ), + _ => panic!("Ty::fn_sig() called on non-fn type: {:?}", self), + } + } + + /// Returns `true` when the outermost type cannot be further normalized, + /// resolved, or instantiated. This includes all primitive types, but also + /// things like ADTs and trait objects, since even if their arguments or + /// nested types may be further simplified, the outermost [`ty::TyKind`] or + /// type constructor remains the same. + pub fn is_known_rigid(self) -> bool { + match self { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Pat(_, _) + | ty::Slice(_) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(..) + | ty::UnsafeBinder(_) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(_) => true, + + ty::Error(_) + | ty::Infer(_) + | ty::Alias(_, _) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Placeholder(_) => false, + } + } +} + // This is manually implemented because a derive would require `I: Debug` impl fmt::Debug for TyKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -341,7 +411,10 @@ impl fmt::Debug for TyKind { /// * For an opaque type, there is no explicit syntax. #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) +)] pub struct AliasTy { /// The parameters of the associated or opaque type. /// @@ -441,30 +514,11 @@ impl AliasTy { } } -/// The following methods work only with inherent associated type projections. -impl AliasTy { - /// Transform the generic parameters to have the given `impl` args as the base and the GAT args on top of that. - /// - /// Does the following transformation: - /// - /// ```text - /// [Self, P_0...P_m] -> [I_0...I_n, P_0...P_m] - /// - /// I_i impl args - /// P_j GAT args - /// ``` - pub fn rebase_inherent_args_onto_impl( - self, - impl_args: I::GenericArgs, - interner: I, - ) -> I::GenericArgs { - debug_assert_eq!(self.kind(interner), AliasTyKind::Inherent); - interner.mk_args_from_iter(impl_args.iter().chain(self.args.iter().skip(1))) - } -} - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum IntTy { Isize, I8, @@ -522,7 +576,10 @@ impl IntTy { } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum UintTy { Usize, U8, @@ -580,7 +637,10 @@ impl UintTy { } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] pub enum FloatTy { F16, F32, @@ -680,7 +740,7 @@ rustc_index::newtype_index! { /// type variable for the element type since we won't know until it's /// used what the element type is supposed to be. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))] +#[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext))] pub enum InferTy { /// A type variable. TyVar(TyVid), @@ -713,7 +773,6 @@ pub enum InferTy { /// Raw `TyVid` are used as the unification key for `sub_relations`; /// they carry no values. -#[cfg(feature = "nightly")] impl UnifyKey for TyVid { type Value = (); #[inline] @@ -729,7 +788,6 @@ impl UnifyKey for TyVid { } } -#[cfg(feature = "nightly")] impl UnifyValue for IntVarValue { type Error = NoError; @@ -749,7 +807,6 @@ impl UnifyValue for IntVarValue { } } -#[cfg(feature = "nightly")] impl UnifyKey for IntVid { type Value = IntVarValue; #[inline] // make this function eligible for inlining - it is quite hot. @@ -765,7 +822,6 @@ impl UnifyKey for IntVid { } } -#[cfg(feature = "nightly")] impl UnifyValue for FloatVarValue { type Error = NoError; @@ -783,7 +839,6 @@ impl UnifyValue for FloatVarValue { } } -#[cfg(feature = "nightly")] impl UnifyKey for FloatVid { type Value = FloatVarValue; #[inline] @@ -860,7 +915,10 @@ impl fmt::Debug for InferTy { } #[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] pub struct TypeAndMut { pub ty: I::Ty, @@ -868,7 +926,10 @@ pub struct TypeAndMut { } #[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] pub struct FnSig { pub inputs_and_output: I::Tys, @@ -1010,7 +1071,8 @@ impl Deref for UnsafeBinderInner { } #[cfg(feature = "nightly")] -impl> rustc_serialize::Encodable for UnsafeBinderInner +impl rustc_serialize::Encodable + for UnsafeBinderInner where I::Ty: rustc_serialize::Encodable, I::BoundVarKinds: rustc_serialize::Encodable, @@ -1022,7 +1084,8 @@ where } #[cfg(feature = "nightly")] -impl> rustc_serialize::Decodable for UnsafeBinderInner +impl rustc_serialize::Decodable + for UnsafeBinderInner where I::Ty: TypeVisitable + rustc_serialize::Decodable, I::BoundVarKinds: rustc_serialize::Decodable, @@ -1038,7 +1101,10 @@ where // This is just a `FnSig` without the `FnHeader` fields. #[derive_where(Clone, Copy, Debug, PartialEq, Eq, Hash; I: Interner)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] pub struct FnSigTys { pub inputs_and_output: I::Tys, @@ -1087,7 +1153,10 @@ impl ty::Binder> { } #[derive_where(Clone, Copy, Debug, PartialEq, Eq, Hash; I: Interner)] -#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] pub struct FnHeader { pub c_variadic: bool, diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index d1ca9bdb7fbd1..8ba985d2d1931 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -342,7 +342,7 @@ struct HasRegionsBoundAt { // FIXME: Could be optimized to not walk into components with no escaping bound vars. impl TypeVisitor for HasRegionsBoundAt { type Result = ControlFlow<()>; - fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { self.binder.shift_in(1); t.super_visit_with(self)?; self.binder.shift_out(1); diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 119b658a2bf3f..ccb84e2591122 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -52,7 +52,7 @@ use smallvec::SmallVec; use thin_vec::ThinVec; use crate::inherent::*; -use crate::{self as ty, Interner, TypeFlags}; +use crate::{self as ty, Interner, TypeFlags, TypeFoldable}; /// This trait is implemented for every type that can be visited, /// providing the skeleton of the traversal. @@ -94,7 +94,7 @@ pub trait TypeVisitor: Sized { #[cfg(not(feature = "nightly"))] type Result: VisitorResult; - fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { t.super_visit_with(self) } @@ -269,10 +269,6 @@ pub trait TypeVisitableExt: TypeVisitable { self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) } - fn has_coroutines(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_COROUTINE) - } - fn references_error(&self) -> bool { self.has_type_flags(TypeFlags::HAS_ERROR) } @@ -405,7 +401,7 @@ impl std::fmt::Debug for HasTypeFlagsVisitor { impl TypeVisitor for HasTypeFlagsVisitor { type Result = ControlFlow; - fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { // If we're looking for the HAS_BINDER_VARS flag, check if the // binder has vars. This won't be present in the binder's bound // value, so we need to check here too. @@ -514,7 +510,7 @@ struct HasEscapingVarsVisitor { impl TypeVisitor for HasEscapingVarsVisitor { type Result = ControlFlow; - fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { self.outer_index.shift_in(1); let result = t.super_visit_with(self); self.outer_index.shift_out(1); diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs new file mode 100644 index 0000000000000..737550eb73e99 --- /dev/null +++ b/compiler/rustc_type_ir/src/walk.rs @@ -0,0 +1,182 @@ +//! An iterator over the type substructure. +//! WARNING: this does not keep track of the region depth. + +use smallvec::{SmallVec, smallvec}; +use tracing::debug; + +use crate::data_structures::SsoHashSet; +use crate::inherent::*; +use crate::{self as ty, Interner}; + +// The TypeWalker's stack is hot enough that it's worth going to some effort to +// avoid heap allocations. +type TypeWalkerStack = SmallVec<[::GenericArg; 8]>; + +pub struct TypeWalker { + stack: TypeWalkerStack, + last_subtree: usize, + pub visited: SsoHashSet, +} + +/// An iterator for walking the type tree. +/// +/// It's very easy to produce a deeply +/// nested type tree with a lot of +/// identical subtrees. In order to work efficiently +/// in this situation walker only visits each type once. +/// It maintains a set of visited types and +/// skips any types that are already there. +impl TypeWalker { + pub fn new(root: I::GenericArg) -> Self { + Self { stack: smallvec![root], last_subtree: 1, visited: SsoHashSet::new() } + } + + /// Skips the subtree corresponding to the last type + /// returned by `next()`. + /// + /// Example: Imagine you are walking `Foo, usize>`. + /// + /// ```ignore (illustrative) + /// let mut iter: TypeWalker = ...; + /// iter.next(); // yields Foo + /// iter.next(); // yields Bar + /// iter.skip_current_subtree(); // skips i32 + /// iter.next(); // yields usize + /// ``` + pub fn skip_current_subtree(&mut self) { + self.stack.truncate(self.last_subtree); + } +} + +impl Iterator for TypeWalker { + type Item = I::GenericArg; + + fn next(&mut self) -> Option { + debug!("next(): stack={:?}", self.stack); + loop { + let next = self.stack.pop()?; + self.last_subtree = self.stack.len(); + if self.visited.insert(next) { + push_inner::(&mut self.stack, next); + debug!("next: stack={:?}", self.stack); + return Some(next); + } + } + } +} + +/// We push `GenericArg`s on the stack in reverse order so as to +/// maintain a pre-order traversal. As of the time of this +/// writing, the fact that the traversal is pre-order is not +/// known to be significant to any code, but it seems like the +/// natural order one would expect (basically, the order of the +/// types as they are written). +fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg) { + match parent.kind() { + ty::GenericArgKind::Type(parent_ty) => match parent_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Infer(_) + | ty::Param(_) + | ty::Never + | ty::Error(_) + | ty::Placeholder(..) + | ty::Bound(..) + | ty::Foreign(..) => {} + + ty::Pat(ty, pat) => { + push_ty_pat::(stack, pat); + stack.push(ty.into()); + } + ty::Array(ty, len) => { + stack.push(len.into()); + stack.push(ty.into()); + } + ty::Slice(ty) => { + stack.push(ty.into()); + } + ty::RawPtr(ty, _) => { + stack.push(ty.into()); + } + ty::Ref(lt, ty, _) => { + stack.push(ty.into()); + stack.push(lt.into()); + } + ty::Alias(_, data) => { + stack.extend(data.args.iter().rev()); + } + ty::Dynamic(obj, lt, _) => { + stack.push(lt.into()); + stack.extend( + obj.iter() + .rev() + .filter_map(|predicate| { + let (args, opt_ty) = match predicate.skip_binder() { + ty::ExistentialPredicate::Trait(tr) => (tr.args, None), + ty::ExistentialPredicate::Projection(p) => (p.args, Some(p.term)), + ty::ExistentialPredicate::AutoTrait(_) => { + return None; + } + }; + + Some(args.iter().rev().chain(opt_ty.map(|term| match term.kind() { + ty::TermKind::Ty(ty) => ty.into(), + ty::TermKind::Const(ct) => ct.into(), + }))) + }) + .flatten(), + ); + } + ty::Adt(_, args) + | ty::Closure(_, args) + | ty::CoroutineClosure(_, args) + | ty::Coroutine(_, args) + | ty::CoroutineWitness(_, args) + | ty::FnDef(_, args) => { + stack.extend(args.iter().rev()); + } + ty::Tuple(ts) => stack.extend(ts.iter().rev().map(|ty| ty.into())), + ty::FnPtr(sig_tys, _hdr) => { + stack.extend( + sig_tys.skip_binder().inputs_and_output.iter().rev().map(|ty| ty.into()), + ); + } + ty::UnsafeBinder(bound_ty) => { + stack.push(bound_ty.skip_binder().into()); + } + }, + ty::GenericArgKind::Lifetime(_) => {} + ty::GenericArgKind::Const(parent_ct) => match parent_ct.kind() { + ty::ConstKind::Infer(_) + | ty::ConstKind::Param(_) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Error(_) => {} + + ty::ConstKind::Value(cv) => stack.push(cv.ty().into()), + + ty::ConstKind::Expr(expr) => stack.extend(expr.args().iter().rev()), + ty::ConstKind::Unevaluated(ct) => { + stack.extend(ct.args.iter().rev()); + } + }, + } +} + +fn push_ty_pat(stack: &mut TypeWalkerStack, pat: I::Pat) { + match pat.kind() { + ty::PatternKind::Range { start, end } => { + stack.push(end.into()); + stack.push(start.into()); + } + ty::PatternKind::Or(pats) => { + for pat in pats.iter() { + push_ty_pat::(stack, pat) + } + } + } +} diff --git a/compiler/rustc_type_ir_macros/src/lib.rs b/compiler/rustc_type_ir_macros/src/lib.rs index 640299c216796..3a10d0d41ef32 100644 --- a/compiler/rustc_type_ir_macros/src/lib.rs +++ b/compiler/rustc_type_ir_macros/src/lib.rs @@ -45,12 +45,12 @@ fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Tok s.add_bounds(synstructure::AddBounds::Fields); let body_visit = s.each(|bind| { quote! { - match ::rustc_type_ir::visit::VisitorResult::branch( - ::rustc_type_ir::visit::TypeVisitable::visit_with(#bind, __visitor) + match ::rustc_type_ir::VisitorResult::branch( + ::rustc_type_ir::TypeVisitable::visit_with(#bind, __visitor) ) { ::core::ops::ControlFlow::Continue(()) => {}, ::core::ops::ControlFlow::Break(r) => { - return ::rustc_type_ir::visit::VisitorResult::from_residual(r); + return ::rustc_type_ir::VisitorResult::from_residual(r); }, } } @@ -58,14 +58,14 @@ fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Tok s.bind_with(|_| synstructure::BindStyle::Move); s.bound_impl( - quote!(::rustc_type_ir::visit::TypeVisitable), + quote!(::rustc_type_ir::TypeVisitable), quote! { - fn visit_with<__V: ::rustc_type_ir::visit::TypeVisitor>( + fn visit_with<__V: ::rustc_type_ir::TypeVisitor>( &self, __visitor: &mut __V ) -> __V::Result { match *self { #body_visit } - <__V::Result as ::rustc_type_ir::visit::VisitorResult>::output() + <__V::Result as ::rustc_type_ir::VisitorResult>::output() } }, ) @@ -83,6 +83,22 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke s.add_where_predicate(parse_quote! { I: Interner }); s.add_bounds(synstructure::AddBounds::Fields); s.bind_with(|_| synstructure::BindStyle::Move); + let body_try_fold = s.each_variant(|vi| { + let bindings = vi.bindings(); + vi.construct(|_, index| { + let bind = &bindings[index]; + + // retain value of fields with #[type_foldable(identity)] + if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") { + bind.to_token_stream() + } else { + quote! { + ::rustc_type_ir::TypeFoldable::try_fold_with(#bind, __folder)? + } + } + }) + }); + let body_fold = s.each_variant(|vi| { let bindings = vi.bindings(); vi.construct(|_, index| { @@ -93,7 +109,7 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke bind.to_token_stream() } else { quote! { - ::rustc_type_ir::fold::TypeFoldable::try_fold_with(#bind, __folder)? + ::rustc_type_ir::TypeFoldable::fold_with(#bind, __folder) } } }) @@ -105,13 +121,20 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke s.filter(|bi| !has_ignore_attr(&bi.ast().attrs, "type_foldable", "identity")); s.add_bounds(synstructure::AddBounds::Fields); s.bound_impl( - quote!(::rustc_type_ir::fold::TypeFoldable), + quote!(::rustc_type_ir::TypeFoldable), quote! { - fn try_fold_with<__F: ::rustc_type_ir::fold::FallibleTypeFolder>( + fn try_fold_with<__F: ::rustc_type_ir::FallibleTypeFolder>( self, __folder: &mut __F ) -> Result { - Ok(match self { #body_fold }) + Ok(match self { #body_try_fold }) + } + + fn fold_with<__F: ::rustc_type_ir::TypeFolder>( + self, + __folder: &mut __F + ) -> Self { + match self { #body_fold } } }, ) diff --git a/compiler/stable_mir/Cargo.toml b/compiler/stable_mir/Cargo.toml index d691a0e4f22f5..516c8e9c718b4 100644 --- a/compiler/stable_mir/Cargo.toml +++ b/compiler/stable_mir/Cargo.toml @@ -4,5 +4,10 @@ version = "0.1.0-preview" edition = "2024" [dependencies] -scoped-tls = "1.0" -serde = { version = "1.0.125", features = [ "derive" ] } +rustc_smir = { path = "../rustc_smir" } + +[features] +# Provides access to APIs that expose internals of the rust compiler. +# APIs enabled by this feature are unstable. They can be removed or modified +# at any point and they are not included in the crate's semantic versioning. +rustc_internal = [] diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs deleted file mode 100644 index e82c957c34ea6..0000000000000 --- a/compiler/stable_mir/src/compiler_interface.rs +++ /dev/null @@ -1,283 +0,0 @@ -//! Define the interface with the Rust compiler. -//! -//! StableMIR users should not use any of the items in this module directly. -//! These APIs have no stability guarantee. - -use std::cell::Cell; - -use crate::abi::{FnAbi, Layout, LayoutShape}; -use crate::crate_def::Attribute; -use crate::mir::alloc::{AllocId, GlobalAlloc}; -use crate::mir::mono::{Instance, InstanceDef, StaticDef}; -use crate::mir::{BinOp, Body, Place, UnOp}; -use crate::target::MachineInfo; -use crate::ty::{ - AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef, - ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics, - ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, TraitDecl, - TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, -}; -use crate::{ - Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, ItemKind, - Symbol, TraitDecls, mir, -}; - -/// This trait defines the interface between stable_mir and the Rust compiler. -/// Do not use this directly. -pub trait Context { - fn entry_fn(&self) -> Option; - /// Retrieve all items of the local crate that have a MIR associated with them. - fn all_local_items(&self) -> CrateItems; - /// Retrieve the body of a function. - /// This function will panic if the body is not available. - fn mir_body(&self, item: DefId) -> mir::Body; - /// Check whether the body of a function is available. - fn has_body(&self, item: DefId) -> bool; - fn foreign_modules(&self, crate_num: CrateNum) -> Vec; - - /// Retrieve all functions defined in this crate. - fn crate_functions(&self, crate_num: CrateNum) -> Vec; - - /// Retrieve all static items defined in this crate. - fn crate_statics(&self, crate_num: CrateNum) -> Vec; - fn foreign_module(&self, mod_def: ForeignModuleDef) -> ForeignModule; - fn foreign_items(&self, mod_def: ForeignModuleDef) -> Vec; - fn all_trait_decls(&self) -> TraitDecls; - fn trait_decls(&self, crate_num: CrateNum) -> TraitDecls; - fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl; - fn all_trait_impls(&self) -> ImplTraitDecls; - fn trait_impls(&self, crate_num: CrateNum) -> ImplTraitDecls; - fn trait_impl(&self, trait_impl: &ImplDef) -> ImplTrait; - fn generics_of(&self, def_id: DefId) -> Generics; - fn predicates_of(&self, def_id: DefId) -> GenericPredicates; - fn explicit_predicates_of(&self, def_id: DefId) -> GenericPredicates; - /// Get information about the local crate. - fn local_crate(&self) -> Crate; - /// Retrieve a list of all external crates. - fn external_crates(&self) -> Vec; - - /// Find a crate with the given name. - fn find_crates(&self, name: &str) -> Vec; - - /// Returns the name of given `DefId` - fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol; - - /// Return registered tool attributes with the given attribute name. - /// - /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool - /// attributes will simply return an empty list. - /// - /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`. - /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. - fn tool_attrs(&self, def_id: DefId, attr: &[Symbol]) -> Vec; - - /// Get all tool attributes of a definition. - fn all_tool_attrs(&self, def_id: DefId) -> Vec; - - /// Returns printable, human readable form of `Span` - fn span_to_string(&self, span: Span) -> String; - - /// Return filename from given `Span`, for diagnostic purposes - fn get_filename(&self, span: &Span) -> Filename; - - /// Return lines corresponding to this `Span` - fn get_lines(&self, span: &Span) -> LineInfo; - - /// Returns the `kind` of given `DefId` - fn item_kind(&self, item: CrateItem) -> ItemKind; - - /// Returns whether this is a foreign item. - fn is_foreign_item(&self, item: DefId) -> bool; - - /// Returns the kind of a given foreign item. - fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind; - - /// Returns the kind of a given algebraic data type - fn adt_kind(&self, def: AdtDef) -> AdtKind; - - /// Returns if the ADT is a box. - fn adt_is_box(&self, def: AdtDef) -> bool; - - /// Returns whether this ADT is simd. - fn adt_is_simd(&self, def: AdtDef) -> bool; - - /// Returns whether this definition is a C string. - fn adt_is_cstr(&self, def: AdtDef) -> bool; - - /// Retrieve the function signature for the given generic arguments. - fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig; - - /// Retrieve the intrinsic definition if the item corresponds one. - fn intrinsic(&self, item: DefId) -> Option; - - /// Retrieve the plain function name of an intrinsic. - fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol; - - /// Retrieve the closure signature for the given generic arguments. - fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig; - - /// The number of variants in this ADT. - fn adt_variants_len(&self, def: AdtDef) -> usize; - - /// The name of a variant. - fn variant_name(&self, def: VariantDef) -> Symbol; - fn variant_fields(&self, def: VariantDef) -> Vec; - - /// Evaluate constant as a target usize. - fn eval_target_usize(&self, cnst: &MirConst) -> Result; - fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result; - - /// Create a new zero-sized constant. - fn try_new_const_zst(&self, ty: Ty) -> Result; - - /// Create a new constant that represents the given string value. - fn new_const_str(&self, value: &str) -> MirConst; - - /// Create a new constant that represents the given boolean value. - fn new_const_bool(&self, value: bool) -> MirConst; - - /// Create a new constant that represents the given value. - fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; - fn try_new_ty_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; - - /// Create a new type from the given kind. - fn new_rigid_ty(&self, kind: RigidTy) -> Ty; - - /// Create a new box type, `Box`, for the given inner type `T`. - fn new_box_ty(&self, ty: Ty) -> Ty; - - /// Returns the type of given crate item. - fn def_ty(&self, item: DefId) -> Ty; - - /// Returns the type of given definition instantiated with the given arguments. - fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty; - - /// Returns literal value of a const as a string. - fn mir_const_pretty(&self, cnst: &MirConst) -> String; - - /// `Span` of an item - fn span_of_an_item(&self, def_id: DefId) -> Span; - - fn ty_const_pretty(&self, ct: TyConstId) -> String; - - /// Obtain the representation of a type. - fn ty_pretty(&self, ty: Ty) -> String; - - /// Obtain the representation of a type. - fn ty_kind(&self, ty: Ty) -> TyKind; - - // Get the discriminant Ty for this Ty if there's one. - fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> Ty; - - /// Get the body of an Instance which is already monomorphized. - fn instance_body(&self, instance: InstanceDef) -> Option; - - /// Get the instance type with generic instantiations applied and lifetimes erased. - fn instance_ty(&self, instance: InstanceDef) -> Ty; - - /// Get the instantiation types. - fn instance_args(&self, def: InstanceDef) -> GenericArgs; - - /// Get the instance. - fn instance_def_id(&self, instance: InstanceDef) -> DefId; - - /// Get the instance mangled name. - fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol; - - /// Check if this is an empty DropGlue shim. - fn is_empty_drop_shim(&self, def: InstanceDef) -> bool; - - /// Check if this is an empty AsyncDropGlueCtor shim. - fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool; - - /// Convert a non-generic crate item into an instance. - /// This function will panic if the item is generic. - fn mono_instance(&self, def_id: DefId) -> Instance; - - /// Item requires monomorphization. - fn requires_monomorphization(&self, def_id: DefId) -> bool; - - /// Resolve an instance from the given function definition and generic arguments. - fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option; - - /// Resolve an instance for drop_in_place for the given type. - fn resolve_drop_in_place(&self, ty: Ty) -> Instance; - - /// Resolve instance for a function pointer. - fn resolve_for_fn_ptr(&self, def: FnDef, args: &GenericArgs) -> Option; - - /// Resolve instance for a closure with the requested type. - fn resolve_closure( - &self, - def: ClosureDef, - args: &GenericArgs, - kind: ClosureKind, - ) -> Option; - - /// Evaluate a static's initializer. - fn eval_static_initializer(&self, def: StaticDef) -> Result; - - /// Try to evaluate an instance into a constant. - fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result; - - /// Retrieve global allocation for the given allocation ID. - fn global_alloc(&self, id: AllocId) -> GlobalAlloc; - - /// Retrieve the id for the virtual table. - fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option; - fn krate(&self, def_id: DefId) -> Crate; - fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol; - - /// Return information about the target machine. - fn target_info(&self) -> MachineInfo; - - /// Get an instance ABI. - fn instance_abi(&self, def: InstanceDef) -> Result; - - /// Get the ABI of a function pointer. - fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result; - - /// Get the layout of a type. - fn ty_layout(&self, ty: Ty) -> Result; - - /// Get the layout shape. - fn layout_shape(&self, id: Layout) -> LayoutShape; - - /// Get a debug string representation of a place. - fn place_pretty(&self, place: &Place) -> String; - - /// Get the resulting type of binary operation. - fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty; - - /// Get the resulting type of unary operation. - fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty; -} - -// A thread local variable that stores a pointer to the tables mapping between TyCtxt -// datastructures and stable MIR datastructures -scoped_tls::scoped_thread_local!(static TLV: Cell<*const ()>); - -pub fn run(context: &dyn Context, f: F) -> Result -where - F: FnOnce() -> T, -{ - if TLV.is_set() { - Err(Error::from("StableMIR already running")) - } else { - let ptr: *const () = (&raw const context) as _; - TLV.set(&Cell::new(ptr), || Ok(f())) - } -} - -/// Execute the given function with access the compiler [Context]. -/// -/// I.e., This function will load the current context and calls a function with it. -/// Do not nest these, as that will ICE. -pub(crate) fn with(f: impl FnOnce(&dyn Context) -> R) -> R { - assert!(TLV.is_set()); - TLV.with(|tlv| { - let ptr = tlv.get(); - assert!(!ptr.is_null()); - f(unsafe { *(ptr as *const &dyn Context) }) - }) -} diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index 8df36e23c4a21..688f3936b26cc 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -1,217 +1,11 @@ -//! The WIP stable interface to rustc internals. +//! We've temporarily moved the `stable_mir` implementation to [`rustc_smir::stable_mir`], +//! during refactoring to break the circular dependency between `rustc_smir` and `stable_mir`, //! -//! For more information see -//! -//! # Note -//! -//! This API is still completely unstable and subject to change. - -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", - test(attr(allow(unused_variables), deny(warnings))) -)] -//! -//! This crate shall contain all type definitions and APIs that we expect third-party tools to invoke to -//! interact with the compiler. -//! -//! The goal is to eventually be published on -//! [crates.io](https://crates.io). - -use std::fmt::Debug; -use std::{fmt, io}; - -use serde::Serialize; - -use crate::compiler_interface::with; -pub use crate::crate_def::{CrateDef, CrateDefType, DefId}; -pub use crate::error::*; -use crate::mir::mono::StaticDef; -use crate::mir::{Body, Mutability}; -use crate::ty::{FnDef, ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty}; - -pub mod abi; -#[macro_use] -pub mod crate_def; -pub mod compiler_interface; -#[macro_use] -pub mod error; -pub mod mir; -pub mod target; -pub mod ty; -pub mod visitor; - -/// Use String for now but we should replace it. -pub type Symbol = String; - -/// The number that identifies a crate. -pub type CrateNum = usize; - -impl Debug for DefId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("DefId").field("id", &self.0).field("name", &self.name()).finish() - } -} - -impl IndexedVal for DefId { - fn to_val(index: usize) -> Self { - DefId(index) - } - - fn to_index(&self) -> usize { - self.0 - } -} - -/// A list of crate items. -pub type CrateItems = Vec; - -/// A list of trait decls. -pub type TraitDecls = Vec; - -/// A list of impl trait decls. -pub type ImplTraitDecls = Vec; - -/// Holds information about a crate. -#[derive(Clone, PartialEq, Eq, Debug, Serialize)] -pub struct Crate { - pub id: CrateNum, - pub name: Symbol, - pub is_local: bool, -} - -impl Crate { - /// The list of foreign modules in this crate. - pub fn foreign_modules(&self) -> Vec { - with(|cx| cx.foreign_modules(self.id)) - } - - /// The list of traits declared in this crate. - pub fn trait_decls(&self) -> TraitDecls { - with(|cx| cx.trait_decls(self.id)) - } - - /// The list of trait implementations in this crate. - pub fn trait_impls(&self) -> ImplTraitDecls { - with(|cx| cx.trait_impls(self.id)) - } - - /// Return a list of function definitions from this crate independent on their visibility. - pub fn fn_defs(&self) -> Vec { - with(|cx| cx.crate_functions(self.id)) - } - - /// Return a list of static items defined in this crate independent on their visibility. - pub fn statics(&self) -> Vec { - with(|cx| cx.crate_statics(self.id)) - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)] -pub enum ItemKind { - Fn, - Static, - Const, - Ctor(CtorKind), -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)] -pub enum CtorKind { - Const, - Fn, -} - -pub type Filename = String; - -crate_def_with_ty! { - /// Holds information about an item in a crate. - #[derive(Serialize)] - pub CrateItem; -} - -impl CrateItem { - /// This will return the body of an item. - /// - /// This will panic if no body is available. - pub fn body(&self) -> mir::Body { - with(|cx| cx.mir_body(self.0)) - } - - pub fn span(&self) -> Span { - with(|cx| cx.span_of_an_item(self.0)) - } - - pub fn kind(&self) -> ItemKind { - with(|cx| cx.item_kind(*self)) - } - - pub fn requires_monomorphization(&self) -> bool { - with(|cx| cx.requires_monomorphization(self.0)) - } - - pub fn ty(&self) -> Ty { - with(|cx| cx.def_ty(self.0)) - } - - pub fn is_foreign_item(&self) -> bool { - with(|cx| cx.is_foreign_item(self.0)) - } - - pub fn emit_mir(&self, w: &mut W) -> io::Result<()> { - self.body().dump(w, &self.name()) - } -} - -/// Return the function where execution starts if the current -/// crate defines that. This is usually `main`, but could be -/// `start` if the crate is a no-std crate. -pub fn entry_fn() -> Option { - with(|cx| cx.entry_fn()) -} - -/// Access to the local crate. -pub fn local_crate() -> Crate { - with(|cx| cx.local_crate()) -} - -/// Try to find a crate or crates if multiple crates exist from given name. -pub fn find_crates(name: &str) -> Vec { - with(|cx| cx.find_crates(name)) -} - -/// Try to find a crate with the given name. -pub fn external_crates() -> Vec { - with(|cx| cx.external_crates()) -} - -/// Retrieve all items in the local crate that have a MIR associated with them. -pub fn all_local_items() -> CrateItems { - with(|cx| cx.all_local_items()) -} - -pub fn all_trait_decls() -> TraitDecls { - with(|cx| cx.all_trait_decls()) -} - -pub fn all_trait_impls() -> ImplTraitDecls { - with(|cx| cx.all_trait_impls()) -} - -/// A type that provides internal information but that can still be used for debug purpose. -#[derive(Clone, PartialEq, Eq, Hash, Serialize)] -pub struct Opaque(String); - -impl std::fmt::Display for Opaque { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::fmt::Debug for Opaque { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} +//! This is a transitional measure as described in [PR #139319](https://github.com/rust-lang/rust/pull/139319). +//! Once the refactoring is complete, the `stable_mir` implementation will be moved back here. -pub fn opaque(value: &T) -> Opaque { - Opaque(format!("{value:?}")) -} +/// Export the rustc_internal APIs. Note that this module has no stability +/// guarantees and it is not taken into account for semver. +#[cfg(feature = "rustc_internal")] +pub use rustc_smir::rustc_internal; +pub use rustc_smir::stable_mir::*; diff --git a/compiler/stable_mir/src/mir.rs b/compiler/stable_mir/src/mir.rs deleted file mode 100644 index 82555461d644e..0000000000000 --- a/compiler/stable_mir/src/mir.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod alloc; -mod body; -pub mod mono; -pub mod pretty; -pub mod visit; - -pub use body::*; -pub use visit::MirVisitor; diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs deleted file mode 100644 index f8b46f50a5298..0000000000000 --- a/compiler/stable_mir/src/mir/body.rs +++ /dev/null @@ -1,1093 +0,0 @@ -use std::io; - -use serde::Serialize; - -use crate::compiler_interface::with; -use crate::mir::pretty::function_body; -use crate::ty::{ - AdtDef, ClosureDef, CoroutineClosureDef, CoroutineDef, GenericArgs, MirConst, Movability, - Region, RigidTy, Ty, TyConst, TyKind, VariantIdx, -}; -use crate::{Error, Opaque, Span, Symbol}; - -/// The SMIR representation of a single function. -#[derive(Clone, Debug, Serialize)] -pub struct Body { - pub blocks: Vec, - - /// Declarations of locals within the function. - /// - /// 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(super) locals: LocalDecls, - - /// The number of arguments this function takes. - pub(super) arg_count: usize, - - /// Debug information pertaining to user variables, including captures. - pub var_debug_info: Vec, - - /// Mark an argument (which must be a tuple) as getting passed as its individual components. - /// - /// This is used for the "rust-call" ABI such as closures. - pub(super) spread_arg: Option, - - /// The span that covers the entire function body. - pub span: Span, -} - -pub type BasicBlockIdx = usize; - -impl Body { - /// Constructs a `Body`. - /// - /// A constructor is required to build a `Body` from outside the crate - /// because the `arg_count` and `locals` fields are private. - pub fn new( - blocks: Vec, - locals: LocalDecls, - arg_count: usize, - var_debug_info: Vec, - spread_arg: Option, - span: Span, - ) -> Self { - // If locals doesn't contain enough entries, it can lead to panics in - // `ret_local`, `arg_locals`, and `inner_locals`. - assert!( - locals.len() > arg_count, - "A Body must contain at least a local for the return value and each of the function's arguments" - ); - Self { blocks, locals, arg_count, var_debug_info, spread_arg, span } - } - - /// Return local that holds this function's return value. - pub fn ret_local(&self) -> &LocalDecl { - &self.locals[RETURN_LOCAL] - } - - /// Locals in `self` that correspond to this function's arguments. - pub fn arg_locals(&self) -> &[LocalDecl] { - &self.locals[1..][..self.arg_count] - } - - /// Inner locals for this function. These are the locals that are - /// neither the return local nor the argument locals. - pub fn inner_locals(&self) -> &[LocalDecl] { - &self.locals[self.arg_count + 1..] - } - - /// Convenience function to get all the locals in this function. - /// - /// Locals are typically accessed via the more specific methods `ret_local`, - /// `arg_locals`, and `inner_locals`. - pub fn locals(&self) -> &[LocalDecl] { - &self.locals - } - - /// Get the local declaration for this local. - pub fn local_decl(&self, local: Local) -> Option<&LocalDecl> { - self.locals.get(local) - } - - /// Get an iterator for all local declarations. - pub fn local_decls(&self) -> impl Iterator { - self.locals.iter().enumerate() - } - - /// Emit the body using the provided name for the signature. - pub fn dump(&self, w: &mut W, fn_name: &str) -> io::Result<()> { - function_body(w, self, fn_name) - } - - pub fn spread_arg(&self) -> Option { - self.spread_arg - } -} - -type LocalDecls = Vec; - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct LocalDecl { - pub ty: Ty, - pub span: Span, - pub mutability: Mutability, -} - -#[derive(Clone, PartialEq, Eq, Debug, Serialize)] -pub struct BasicBlock { - pub statements: Vec, - pub terminator: Terminator, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct Terminator { - pub kind: TerminatorKind, - pub span: Span, -} - -impl Terminator { - pub fn successors(&self) -> Successors { - self.kind.successors() - } -} - -pub type Successors = Vec; - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum TerminatorKind { - Goto { - target: BasicBlockIdx, - }, - SwitchInt { - discr: Operand, - targets: SwitchTargets, - }, - Resume, - Abort, - Return, - Unreachable, - Drop { - place: Place, - target: BasicBlockIdx, - unwind: UnwindAction, - }, - Call { - func: Operand, - args: Vec, - destination: Place, - target: Option, - unwind: UnwindAction, - }, - Assert { - cond: Operand, - expected: bool, - msg: AssertMessage, - target: BasicBlockIdx, - unwind: UnwindAction, - }, - InlineAsm { - template: String, - operands: Vec, - options: String, - line_spans: String, - destination: Option, - unwind: UnwindAction, - }, -} - -impl TerminatorKind { - pub fn successors(&self) -> Successors { - use self::TerminatorKind::*; - match *self { - Call { target: Some(t), unwind: UnwindAction::Cleanup(u), .. } - | Drop { target: t, unwind: UnwindAction::Cleanup(u), .. } - | Assert { target: t, unwind: UnwindAction::Cleanup(u), .. } - | InlineAsm { destination: Some(t), unwind: UnwindAction::Cleanup(u), .. } => { - vec![t, u] - } - Goto { target: t } - | Call { target: None, unwind: UnwindAction::Cleanup(t), .. } - | Call { target: Some(t), unwind: _, .. } - | Drop { target: t, unwind: _, .. } - | Assert { target: t, unwind: _, .. } - | InlineAsm { destination: None, unwind: UnwindAction::Cleanup(t), .. } - | InlineAsm { destination: Some(t), unwind: _, .. } => { - vec![t] - } - - Return - | Resume - | Abort - | Unreachable - | Call { target: None, unwind: _, .. } - | InlineAsm { destination: None, unwind: _, .. } => { - vec![] - } - SwitchInt { ref targets, .. } => targets.all_targets(), - } - } - - pub fn unwind(&self) -> Option<&UnwindAction> { - match *self { - TerminatorKind::Goto { .. } - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::Resume - | TerminatorKind::Abort - | TerminatorKind::SwitchInt { .. } => None, - TerminatorKind::Call { ref unwind, .. } - | TerminatorKind::Assert { ref unwind, .. } - | TerminatorKind::Drop { ref unwind, .. } - | TerminatorKind::InlineAsm { ref unwind, .. } => Some(unwind), - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct InlineAsmOperand { - pub in_value: Option, - pub out_place: Option, - // This field has a raw debug representation of MIR's InlineAsmOperand. - // For now we care about place/operand + the rest in a debug format. - pub raw_rpr: String, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum UnwindAction { - Continue, - Unreachable, - Terminate, - Cleanup(BasicBlockIdx), -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum AssertMessage { - BoundsCheck { len: Operand, index: Operand }, - Overflow(BinOp, Operand, Operand), - OverflowNeg(Operand), - DivisionByZero(Operand), - RemainderByZero(Operand), - ResumedAfterReturn(CoroutineKind), - ResumedAfterPanic(CoroutineKind), - MisalignedPointerDereference { required: Operand, found: Operand }, - NullPointerDereference, -} - -impl AssertMessage { - pub fn description(&self) -> Result<&'static str, Error> { - match self { - AssertMessage::Overflow(BinOp::Add, _, _) => Ok("attempt to add with overflow"), - AssertMessage::Overflow(BinOp::Sub, _, _) => Ok("attempt to subtract with overflow"), - AssertMessage::Overflow(BinOp::Mul, _, _) => Ok("attempt to multiply with overflow"), - AssertMessage::Overflow(BinOp::Div, _, _) => Ok("attempt to divide with overflow"), - AssertMessage::Overflow(BinOp::Rem, _, _) => { - Ok("attempt to calculate the remainder with overflow") - } - AssertMessage::OverflowNeg(_) => Ok("attempt to negate with overflow"), - AssertMessage::Overflow(BinOp::Shr, _, _) => Ok("attempt to shift right with overflow"), - AssertMessage::Overflow(BinOp::Shl, _, _) => Ok("attempt to shift left with overflow"), - AssertMessage::Overflow(op, _, _) => Err(error!("`{:?}` cannot overflow", op)), - AssertMessage::DivisionByZero(_) => Ok("attempt to divide by zero"), - AssertMessage::RemainderByZero(_) => { - Ok("attempt to calculate the remainder with a divisor of zero") - } - AssertMessage::ResumedAfterReturn(CoroutineKind::Coroutine(_)) => { - Ok("coroutine resumed after completion") - } - AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( - CoroutineDesugaring::Async, - _, - )) => Ok("`async fn` resumed after completion"), - AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( - CoroutineDesugaring::Gen, - _, - )) => Ok("`async gen fn` resumed after completion"), - AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( - CoroutineDesugaring::AsyncGen, - _, - )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after completion"), - AssertMessage::ResumedAfterPanic(CoroutineKind::Coroutine(_)) => { - Ok("coroutine resumed after panicking") - } - AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( - CoroutineDesugaring::Async, - _, - )) => Ok("`async fn` resumed after panicking"), - AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( - CoroutineDesugaring::Gen, - _, - )) => Ok("`async gen fn` resumed after panicking"), - AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( - CoroutineDesugaring::AsyncGen, - _, - )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after panicking"), - - AssertMessage::BoundsCheck { .. } => Ok("index out of bounds"), - AssertMessage::MisalignedPointerDereference { .. } => { - Ok("misaligned pointer dereference") - } - AssertMessage::NullPointerDereference => Ok("null pointer dereference occurred"), - } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum BinOp { - Add, - AddUnchecked, - Sub, - SubUnchecked, - Mul, - MulUnchecked, - Div, - Rem, - BitXor, - BitAnd, - BitOr, - Shl, - ShlUnchecked, - Shr, - ShrUnchecked, - Eq, - Lt, - Le, - Ne, - Ge, - Gt, - Cmp, - Offset, -} - -impl BinOp { - /// Return the type of this operation for the given input Ty. - /// This function does not perform type checking, and it currently doesn't handle SIMD. - pub fn ty(&self, lhs_ty: Ty, rhs_ty: Ty) -> Ty { - with(|ctx| ctx.binop_ty(*self, lhs_ty, rhs_ty)) - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum UnOp { - Not, - Neg, - PtrMetadata, -} - -impl UnOp { - /// Return the type of this operation for the given input Ty. - /// This function does not perform type checking, and it currently doesn't handle SIMD. - pub fn ty(&self, arg_ty: Ty) -> Ty { - with(|ctx| ctx.unop_ty(*self, arg_ty)) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum CoroutineKind { - Desugared(CoroutineDesugaring, CoroutineSource), - Coroutine(Movability), -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum CoroutineSource { - Block, - Closure, - Fn, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum CoroutineDesugaring { - Async, - - Gen, - - AsyncGen, -} - -pub(crate) type LocalDefId = Opaque; -/// The rustc coverage data structures are heavily tied to internal details of the -/// coverage implementation that are likely to change, and are unlikely to be -/// useful to third-party tools for the foreseeable future. -pub(crate) type Coverage = Opaque; - -/// The FakeReadCause describes the type of pattern why a FakeRead statement exists. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum FakeReadCause { - ForMatchGuard, - ForMatchedPlace(LocalDefId), - ForGuardBinding, - ForLet(LocalDefId), - ForIndex, -} - -/// Describes what kind of retag is to be performed -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] -pub enum RetagKind { - FnEntry, - TwoPhase, - Raw, - Default, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] -pub enum Variance { - Covariant, - Invariant, - Contravariant, - Bivariant, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct CopyNonOverlapping { - pub src: Operand, - pub dst: Operand, - pub count: Operand, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum NonDivergingIntrinsic { - Assume(Operand), - CopyNonOverlapping(CopyNonOverlapping), -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct Statement { - pub kind: StatementKind, - pub span: Span, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum StatementKind { - Assign(Place, Rvalue), - FakeRead(FakeReadCause, Place), - SetDiscriminant { place: Place, variant_index: VariantIdx }, - Deinit(Place), - StorageLive(Local), - StorageDead(Local), - Retag(RetagKind, Place), - PlaceMention(Place), - AscribeUserType { place: Place, projections: UserTypeProjection, variance: Variance }, - Coverage(Coverage), - Intrinsic(NonDivergingIntrinsic), - ConstEvalCounter, - Nop, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum Rvalue { - /// Creates a pointer with the indicated mutability to the place. - /// - /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like - /// `&raw v` or `addr_of!(v)`. - AddressOf(RawPtrKind, Place), - - /// Creates an aggregate value, like a tuple or struct. - /// - /// This is needed because dataflow analysis needs to distinguish - /// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo` - /// has a destructor. - /// - /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Coroutine`. After - /// coroutine lowering, `Coroutine` aggregate kinds are disallowed too. - Aggregate(AggregateKind, Vec), - - /// * `Offset` has the same semantics as `<*const T>::offset`, except that the second - /// parameter may be a `usize` as well. - /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats, - /// raw pointers, or function pointers and return a `bool`. The types of the operands must be - /// matching, up to the usual caveat of the lifetimes in function pointers. - /// * Left and right shift operations accept signed or unsigned integers not necessarily of the - /// same type and return a value of the same type as their LHS. Like in Rust, the RHS is - /// truncated as needed. - /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching - /// types and return a value of that type. - /// * The remaining operations accept signed integers, unsigned integers, or floats with - /// matching types and return a value of that type. - BinaryOp(BinOp, Operand, Operand), - - /// Performs essentially all of the casts that can be performed via `as`. - /// - /// This allows for casts from/to a variety of types. - Cast(CastKind, Operand, Ty), - - /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition. - /// - /// For addition, subtraction, and multiplication on integers the error condition is set when - /// the infinite precision result would not be equal to the actual result. - CheckedBinaryOp(BinOp, Operand, Operand), - - /// A CopyForDeref is equivalent to a read from a place. - /// When such a read happens, it is guaranteed that the only use of the returned value is a - /// deref operation, immediately followed by one or more projections. - CopyForDeref(Place), - - /// Computes the discriminant of the place, returning it as an integer. - /// Returns zero for types without discriminant. - /// - /// The validity requirements for the underlying value are undecided for this rvalue, see - /// [#91095]. Note too that the value of the discriminant is not the same thing as the - /// variant index; - /// - /// [#91095]: https://github.com/rust-lang/rust/issues/91095 - Discriminant(Place), - - /// Yields the length of the place, as a `usize`. - /// - /// If the type of the place is an array, this is the array length. For slices (`[T]`, not - /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is - /// ill-formed for places of other types. - Len(Place), - - /// Creates a reference to the place. - Ref(Region, BorrowKind, Place), - - /// Creates an array where each element is the value of the operand. - /// - /// This is the cause of a bug in the case where the repetition count is zero because the value - /// is not dropped, see [#74836]. - /// - /// Corresponds to source code like `[x; 32]`. - /// - /// [#74836]: https://github.com/rust-lang/rust/issues/74836 - Repeat(Operand, TyConst), - - /// Transmutes a `*mut u8` into shallow-initialized `Box`. - /// - /// This is different from a normal transmute because dataflow analysis will treat the box as - /// initialized but its content as uninitialized. Like other pointer casts, this in general - /// affects alias analysis. - ShallowInitBox(Operand, Ty), - - /// Creates a pointer/reference to the given thread local. - /// - /// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a - /// `*const T`, and if neither of those apply a `&T`. - /// - /// **Note:** This is a runtime operation that actually executes code and is in this sense more - /// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to - /// SIGILL for some reason that I (JakobDegen) never got a chance to look into. - /// - /// **Needs clarification**: Are there weird additional semantics here related to the runtime - /// nature of this operation? - ThreadLocalRef(crate::CrateItem), - - /// Computes a value as described by the operation. - NullaryOp(NullOp, Ty), - - /// Exactly like `BinaryOp`, but less operands. - /// - /// Also does two's-complement arithmetic. Negation requires a signed integer or a float; - /// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds - /// return a value with the same type as their operand. - UnaryOp(UnOp, Operand), - - /// Yields the operand unchanged - Use(Operand), -} - -impl Rvalue { - pub fn ty(&self, locals: &[LocalDecl]) -> Result { - match self { - Rvalue::Use(operand) => operand.ty(locals), - Rvalue::Repeat(operand, count) => { - Ok(Ty::new_array_with_const_len(operand.ty(locals)?, count.clone())) - } - Rvalue::ThreadLocalRef(did) => Ok(did.ty()), - Rvalue::Ref(reg, bk, place) => { - let place_ty = place.ty(locals)?; - Ok(Ty::new_ref(reg.clone(), place_ty, bk.to_mutable_lossy())) - } - Rvalue::AddressOf(mutability, place) => { - let place_ty = place.ty(locals)?; - Ok(Ty::new_ptr(place_ty, mutability.to_mutable_lossy())) - } - Rvalue::Len(..) => Ok(Ty::usize_ty()), - Rvalue::Cast(.., ty) => Ok(*ty), - Rvalue::BinaryOp(op, lhs, rhs) => { - let lhs_ty = lhs.ty(locals)?; - let rhs_ty = rhs.ty(locals)?; - Ok(op.ty(lhs_ty, rhs_ty)) - } - Rvalue::CheckedBinaryOp(op, lhs, rhs) => { - let lhs_ty = lhs.ty(locals)?; - let rhs_ty = rhs.ty(locals)?; - let ty = op.ty(lhs_ty, rhs_ty); - Ok(Ty::new_tuple(&[ty, Ty::bool_ty()])) - } - Rvalue::UnaryOp(op, operand) => { - let arg_ty = operand.ty(locals)?; - Ok(op.ty(arg_ty)) - } - Rvalue::Discriminant(place) => { - let place_ty = place.ty(locals)?; - place_ty - .kind() - .discriminant_ty() - .ok_or_else(|| error!("Expected a `RigidTy` but found: {place_ty:?}")) - } - Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { - Ok(Ty::usize_ty()) - } - Rvalue::NullaryOp(NullOp::ContractChecks, _) - | Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()), - Rvalue::Aggregate(ak, ops) => match *ak { - AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), - AggregateKind::Tuple => Ok(Ty::new_tuple( - &ops.iter().map(|op| op.ty(locals)).collect::, _>>()?, - )), - AggregateKind::Adt(def, _, ref args, _, _) => Ok(def.ty_with_args(args)), - AggregateKind::Closure(def, ref args) => Ok(Ty::new_closure(def, args.clone())), - AggregateKind::Coroutine(def, ref args, mov) => { - Ok(Ty::new_coroutine(def, args.clone(), mov)) - } - AggregateKind::CoroutineClosure(def, ref args) => { - Ok(Ty::new_coroutine_closure(def, args.clone())) - } - AggregateKind::RawPtr(ty, mutability) => Ok(Ty::new_ptr(ty, mutability)), - }, - Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)), - Rvalue::CopyForDeref(place) => place.ty(locals), - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum AggregateKind { - Array(Ty), - Tuple, - Adt(AdtDef, VariantIdx, GenericArgs, Option, Option), - Closure(ClosureDef, GenericArgs), - // FIXME(stable_mir): Movability here is redundant - Coroutine(CoroutineDef, GenericArgs, Movability), - CoroutineClosure(CoroutineClosureDef, GenericArgs), - RawPtr(Ty, Mutability), -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum Operand { - Copy(Place), - Move(Place), - Constant(ConstOperand), -} - -#[derive(Clone, Eq, PartialEq, Serialize)] -pub struct Place { - pub local: Local, - /// projection out of a place (access a field, deref a pointer, etc) - pub projection: Vec, -} - -impl From for Place { - fn from(local: Local) -> Self { - Place { local, projection: vec![] } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ConstOperand { - pub span: Span, - pub user_ty: Option, - pub const_: MirConst, -} - -/// Debug information pertaining to a user variable. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct VarDebugInfo { - /// The variable name. - pub name: Symbol, - - /// Source info of the user variable, including the scope - /// within which the variable is visible (to debuginfo). - pub source_info: SourceInfo, - - /// The user variable's data is split across several fragments, - /// each described by a `VarDebugInfoFragment`. - pub composite: Option, - - /// Where the data for this user variable is to be found. - pub value: VarDebugInfoContents, - - /// When present, indicates what argument number this variable is in the function that it - /// originated from (starting from 1). Note, if MIR inlining is enabled, then this is the - /// argument number in the original function before it was inlined. - pub argument_index: Option, -} - -impl VarDebugInfo { - /// Return a local variable if this info is related to one. - pub fn local(&self) -> Option { - match &self.value { - VarDebugInfoContents::Place(place) if place.projection.is_empty() => Some(place.local), - VarDebugInfoContents::Place(_) | VarDebugInfoContents::Const(_) => None, - } - } - - /// Return a constant if this info is related to one. - pub fn constant(&self) -> Option<&ConstOperand> { - match &self.value { - VarDebugInfoContents::Place(_) => None, - VarDebugInfoContents::Const(const_op) => Some(const_op), - } - } -} - -pub type SourceScope = u32; - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct SourceInfo { - pub span: Span, - pub scope: SourceScope, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct VarDebugInfoFragment { - pub ty: Ty, - pub projection: Vec, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum VarDebugInfoContents { - Place(Place), - Const(ConstOperand), -} - -// In MIR ProjectionElem is parameterized on the second Field argument and the Index argument. This -// is so it can be used for both Places (for which the projection elements are of type -// ProjectionElem) and user-provided type annotations (for which the projection elements -// are of type ProjectionElem<(), ()>). In SMIR we don't need this generality, so we just use -// ProjectionElem for Places. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum ProjectionElem { - /// Dereference projections (e.g. `*_1`) project to the address referenced by the base place. - Deref, - - /// A field projection (e.g., `f` in `_1.f`) project to a field in the base place. The field is - /// referenced by source-order index rather than the name of the field. The fields type is also - /// given. - Field(FieldIdx, Ty), - - /// Index into a slice/array. The value of the index is computed at runtime using the `V` - /// argument. - /// - /// Note that this does not also dereference, and so it does not exactly correspond to slice - /// indexing in Rust. In other words, in the below Rust code: - /// - /// ```rust - /// let x = &[1, 2, 3, 4]; - /// let i = 2; - /// x[i]; - /// ``` - /// - /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same - /// thing is true of the `ConstantIndex` and `Subslice` projections below. - Index(Local), - - /// Index into a slice/array given by offsets. - /// - /// These indices are generated by slice patterns. Easiest to explain by example: - /// - /// ```ignore (illustrative) - /// [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 -- otherwise, the - /// projection is UB. - /// - /// 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, - }, - - /// Projects a slice from the base place. - /// - /// These indices are generated by slice patterns. If `from_end` is true, this represents - /// `slice[from..slice.len() - to]`. Otherwise it represents `array[from..to]`. - Subslice { - from: u64, - to: u64, - /// Whether `to` counts from the start or end of the array/slice. - from_end: bool, - }, - - /// "Downcast" to a variant of an enum or a coroutine. - Downcast(VariantIdx), - - /// Like an explicit cast from an opaque type to a concrete type, but without - /// requiring an intermediate variable. - OpaqueCast(Ty), - - /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where - /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping - /// explicit during optimizations and codegen. - /// - /// This projection doesn't impact the runtime behavior of the program except for potentially changing - /// some type metadata of the interpreter or codegen backend. - Subtype(Ty), -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct UserTypeProjection { - pub base: UserTypeAnnotationIndex, - - pub projection: Opaque, -} - -pub type Local = usize; - -pub const RETURN_LOCAL: Local = 0; - -/// The source-order index of a field in a variant. -/// -/// For example, in the following types, -/// ```ignore(illustrative) -/// enum Demo1 { -/// Variant0 { a: bool, b: i32 }, -/// Variant1 { c: u8, d: u64 }, -/// } -/// struct Demo2 { e: u8, f: u16, g: u8 } -/// ``` -/// `a`'s `FieldIdx` is `0`, -/// `b`'s `FieldIdx` is `1`, -/// `c`'s `FieldIdx` is `0`, and -/// `g`'s `FieldIdx` is `2`. -pub type FieldIdx = usize; - -type UserTypeAnnotationIndex = usize; - -/// The possible branch sites of a [TerminatorKind::SwitchInt]. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct SwitchTargets { - /// The conditional branches where the first element represents the value that guards this - /// branch, and the second element is the branch target. - branches: Vec<(u128, BasicBlockIdx)>, - /// The `otherwise` branch which will be taken in case none of the conditional branches are - /// satisfied. - otherwise: BasicBlockIdx, -} - -impl SwitchTargets { - /// All possible targets including the `otherwise` target. - pub fn all_targets(&self) -> Successors { - self.branches.iter().map(|(_, target)| *target).chain(Some(self.otherwise)).collect() - } - - /// The `otherwise` branch target. - pub fn otherwise(&self) -> BasicBlockIdx { - self.otherwise - } - - /// The conditional targets which are only taken if the pattern matches the given value. - pub fn branches(&self) -> impl Iterator { - self.branches.iter().copied() - } - - /// The number of targets including `otherwise`. - pub fn len(&self) -> usize { - self.branches.len() + 1 - } - - /// Create a new SwitchTargets from the given branches and `otherwise` target. - pub fn new(branches: Vec<(u128, BasicBlockIdx)>, otherwise: BasicBlockIdx) -> SwitchTargets { - SwitchTargets { branches, otherwise } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum BorrowKind { - /// Data must be immutable and is aliasable. - Shared, - - /// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either - /// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]). - Fake(FakeBorrowKind), - - /// Data is mutable and not aliasable. - Mut { - /// `true` if this borrow arose from method-call auto-ref - kind: MutBorrowKind, - }, -} - -impl BorrowKind { - pub fn to_mutable_lossy(self) -> Mutability { - match self { - BorrowKind::Mut { .. } => Mutability::Mut, - BorrowKind::Shared => Mutability::Not, - // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. - BorrowKind::Fake(_) => Mutability::Not, - } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum RawPtrKind { - Mut, - Const, - FakeForPtrMetadata, -} - -impl RawPtrKind { - pub fn to_mutable_lossy(self) -> Mutability { - match self { - RawPtrKind::Mut { .. } => Mutability::Mut, - RawPtrKind::Const => Mutability::Not, - // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. - RawPtrKind::FakeForPtrMetadata => Mutability::Not, - } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum MutBorrowKind { - Default, - TwoPhaseBorrow, - ClosureCapture, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum FakeBorrowKind { - /// A shared (deep) borrow. Data must be immutable and is aliasable. - Deep, - /// The immediately borrowed place must be immutable, but projections from - /// it don't need to be. This is used to prevent match guards from replacing - /// the scrutinee. For example, a fake borrow of `a.b` doesn't - /// conflict with a mutable borrow of `a.b.c`. - Shallow, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub enum Mutability { - Not, - Mut, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum Safety { - Safe, - Unsafe, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum PointerCoercion { - /// Go from a fn-item type to a fn-pointer type. - ReifyFnPointer, - - /// Go from a safe fn pointer to an unsafe fn pointer. - UnsafeFnPointer, - - /// Go from a non-capturing closure to a fn pointer or an unsafe fn pointer. - /// It cannot convert a closure that requires unsafe. - ClosureFnPointer(Safety), - - /// Go from a mut raw pointer to a const raw pointer. - MutToConstPointer, - - /// Go from `*const [T; N]` to `*const T` - ArrayToPointer, - - /// Unsize a pointer/reference value, e.g., `&[T; n]` to - /// `&[T]`. Note that the source could be a thin or wide pointer. - /// This will do things like convert thin pointers to wide - /// pointers, or convert structs containing thin pointers to - /// structs containing wide pointers, or convert between wide - /// pointers. - Unsize, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum CastKind { - // FIXME(smir-rename): rename this to PointerExposeProvenance - PointerExposeAddress, - PointerWithExposedProvenance, - PointerCoercion(PointerCoercion), - // FIXME(smir-rename): change this to PointerCoercion(DynStar) - DynStar, - IntToInt, - FloatToInt, - FloatToFloat, - IntToFloat, - PtrToPtr, - FnPtrToPtr, - Transmute, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum NullOp { - /// Returns the size of a value of that type. - SizeOf, - /// Returns the minimum alignment of a type. - AlignOf, - /// Returns the offset of a field. - OffsetOf(Vec<(VariantIdx, FieldIdx)>), - /// cfg!(ub_checks), but at codegen time - UbChecks, - /// cfg!(contract_checks), but at codegen time - ContractChecks, -} - -impl Operand { - /// Get the type of an operand relative to the local declaration. - /// - /// In order to retrieve the correct type, the `locals` argument must match the list of all - /// locals from the function body where this operand originates from. - /// - /// Errors indicate a malformed operand or incompatible locals list. - pub fn ty(&self, locals: &[LocalDecl]) -> Result { - match self { - Operand::Copy(place) | Operand::Move(place) => place.ty(locals), - Operand::Constant(c) => Ok(c.ty()), - } - } -} - -impl ConstOperand { - pub fn ty(&self) -> Ty { - self.const_.ty() - } -} - -impl Place { - /// Resolve down the chain of projections to get the type referenced at the end of it. - /// E.g.: - /// Calling `ty()` on `var.field` should return the type of `field`. - /// - /// In order to retrieve the correct type, the `locals` argument must match the list of all - /// locals from the function body where this place originates from. - pub fn ty(&self, locals: &[LocalDecl]) -> Result { - let start_ty = locals[self.local].ty; - self.projection.iter().fold(Ok(start_ty), |place_ty, elem| elem.ty(place_ty?)) - } -} - -impl ProjectionElem { - /// Get the expected type after applying this projection to a given place type. - pub fn ty(&self, place_ty: Ty) -> Result { - let ty = place_ty; - match &self { - ProjectionElem::Deref => Self::deref_ty(ty), - ProjectionElem::Field(_idx, fty) => Ok(*fty), - ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => Self::index_ty(ty), - ProjectionElem::Subslice { from, to, from_end } => { - Self::subslice_ty(ty, *from, *to, *from_end) - } - ProjectionElem::Downcast(_) => Ok(ty), - ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => Ok(*ty), - } - } - - fn index_ty(ty: Ty) -> Result { - ty.kind().builtin_index().ok_or_else(|| error!("Cannot index non-array type: {ty:?}")) - } - - fn subslice_ty(ty: Ty, from: u64, to: u64, from_end: bool) -> Result { - let ty_kind = ty.kind(); - match ty_kind { - TyKind::RigidTy(RigidTy::Slice(..)) => Ok(ty), - TyKind::RigidTy(RigidTy::Array(inner, _)) if !from_end => Ty::try_new_array( - inner, - to.checked_sub(from).ok_or_else(|| error!("Subslice overflow: {from}..{to}"))?, - ), - TyKind::RigidTy(RigidTy::Array(inner, size)) => { - let size = size.eval_target_usize()?; - let len = size - from - to; - Ty::try_new_array(inner, len) - } - _ => Err(Error(format!("Cannot subslice non-array type: `{ty_kind:?}`"))), - } - } - - fn deref_ty(ty: Ty) -> Result { - let deref_ty = ty - .kind() - .builtin_deref(true) - .ok_or_else(|| error!("Cannot dereference type: {ty:?}"))?; - Ok(deref_ty.ty) - } -} diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs deleted file mode 100644 index 8278afb7a2f17..0000000000000 --- a/compiler/stable_mir/src/mir/pretty.rs +++ /dev/null @@ -1,452 +0,0 @@ -//! Implement methods to pretty print stable MIR body. -use std::fmt::Debug; -use std::io::Write; -use std::{fmt, io, iter}; - -use fmt::{Display, Formatter}; - -use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind}; -use crate::mir::{ - Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents, -}; -use crate::ty::{AdtKind, IndexedVal, MirConst, Ty, TyConst}; -use crate::{Body, CrateDef, Mutability, with}; - -impl Display for Ty { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - with(|ctx| write!(f, "{}", ctx.ty_pretty(*self))) - } -} - -impl Debug for Place { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - with(|ctx| write!(f, "{}", ctx.place_pretty(self))) - } -} - -pub(crate) fn function_body(writer: &mut W, body: &Body, name: &str) -> io::Result<()> { - write!(writer, "fn {name}(")?; - let mut sep = ""; - for (index, local) in body.arg_locals().iter().enumerate() { - write!(writer, "{}_{}: {}", sep, index + 1, local.ty)?; - sep = ", "; - } - write!(writer, ")")?; - - let return_local = body.ret_local(); - writeln!(writer, " -> {} {{", return_local.ty)?; - - body.locals().iter().enumerate().try_for_each(|(index, local)| -> io::Result<()> { - if index == 0 || index > body.arg_count { - writeln!(writer, " let {}_{}: {};", pretty_mut(local.mutability), index, local.ty) - } else { - Ok(()) - } - })?; - - body.var_debug_info.iter().try_for_each(|info| { - let content = match &info.value { - VarDebugInfoContents::Place(place) => { - format!("{place:?}") - } - VarDebugInfoContents::Const(constant) => pretty_mir_const(&constant.const_), - }; - writeln!(writer, " debug {} => {};", info.name, content) - })?; - - body.blocks - .iter() - .enumerate() - .map(|(index, block)| -> io::Result<()> { - writeln!(writer, " bb{index}: {{")?; - let _ = block - .statements - .iter() - .map(|statement| -> io::Result<()> { - pretty_statement(writer, &statement.kind)?; - Ok(()) - }) - .collect::>(); - pretty_terminator(writer, &block.terminator.kind)?; - writeln!(writer, " }}").unwrap(); - Ok(()) - }) - .collect::, _>>()?; - writeln!(writer, "}}")?; - Ok(()) -} - -fn pretty_statement(writer: &mut W, statement: &StatementKind) -> io::Result<()> { - const INDENT: &str = " "; - match statement { - StatementKind::Assign(place, rval) => { - write!(writer, "{INDENT}{place:?} = ")?; - pretty_rvalue(writer, rval)?; - writeln!(writer, ";") - } - // FIXME: Add rest of the statements - StatementKind::FakeRead(cause, place) => { - writeln!(writer, "{INDENT}FakeRead({cause:?}, {place:?});") - } - StatementKind::SetDiscriminant { place, variant_index } => { - writeln!(writer, "{INDENT}discriminant({place:?} = {};", variant_index.to_index()) - } - StatementKind::Deinit(place) => writeln!(writer, "Deinit({place:?};"), - StatementKind::StorageLive(local) => { - writeln!(writer, "{INDENT}StorageLive(_{local});") - } - StatementKind::StorageDead(local) => { - writeln!(writer, "{INDENT}StorageDead(_{local});") - } - StatementKind::Retag(kind, place) => writeln!(writer, "Retag({kind:?}, {place:?});"), - StatementKind::PlaceMention(place) => { - writeln!(writer, "{INDENT}PlaceMention({place:?};") - } - StatementKind::ConstEvalCounter => { - writeln!(writer, "{INDENT}ConstEvalCounter;") - } - StatementKind::Nop => writeln!(writer, "{INDENT}nop;"), - StatementKind::AscribeUserType { .. } - | StatementKind::Coverage(_) - | StatementKind::Intrinsic(_) => { - // FIX-ME: Make them pretty. - writeln!(writer, "{INDENT}{statement:?};") - } - } -} - -fn pretty_terminator(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { - pretty_terminator_head(writer, terminator)?; - let successors = terminator.successors(); - let successor_count = successors.len(); - let labels = pretty_successor_labels(terminator); - - let show_unwind = !matches!(terminator.unwind(), None | Some(UnwindAction::Cleanup(_))); - let fmt_unwind = |w: &mut W| -> io::Result<()> { - write!(w, "unwind ")?; - match terminator.unwind() { - None | Some(UnwindAction::Cleanup(_)) => unreachable!(), - Some(UnwindAction::Continue) => write!(w, "continue"), - Some(UnwindAction::Unreachable) => write!(w, "unreachable"), - Some(UnwindAction::Terminate) => write!(w, "terminate"), - } - }; - - match (successor_count, show_unwind) { - (0, false) => {} - (0, true) => { - write!(writer, " -> ")?; - fmt_unwind(writer)?; - } - (1, false) => write!(writer, " -> bb{:?}", successors[0])?, - _ => { - write!(writer, " -> [")?; - for (i, target) in successors.iter().enumerate() { - if i > 0 { - write!(writer, ", ")?; - } - write!(writer, "{}: bb{:?}", labels[i], target)?; - } - if show_unwind { - write!(writer, ", ")?; - fmt_unwind(writer)?; - } - write!(writer, "]")?; - } - }; - - writeln!(writer, ";") -} - -fn pretty_terminator_head(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { - use self::TerminatorKind::*; - const INDENT: &str = " "; - match terminator { - Goto { .. } => write!(writer, "{INDENT}goto"), - SwitchInt { discr, .. } => { - write!(writer, "{INDENT}switchInt({})", pretty_operand(discr)) - } - Resume => write!(writer, "{INDENT}resume"), - Abort => write!(writer, "{INDENT}abort"), - Return => write!(writer, "{INDENT}return"), - Unreachable => write!(writer, "{INDENT}unreachable"), - Drop { place, .. } => write!(writer, "{INDENT}drop({place:?})"), - Call { func, args, destination, .. } => { - write!(writer, "{INDENT}{:?} = {}(", destination, pretty_operand(func))?; - let mut args_iter = args.iter(); - args_iter.next().map_or(Ok(()), |arg| write!(writer, "{}", pretty_operand(arg)))?; - args_iter.try_for_each(|arg| write!(writer, ", {}", pretty_operand(arg)))?; - write!(writer, ")") - } - Assert { cond, expected, msg, target: _, unwind: _ } => { - write!(writer, "{INDENT}assert(")?; - if !expected { - write!(writer, "!")?; - } - write!(writer, "{}, ", pretty_operand(cond))?; - pretty_assert_message(writer, msg)?; - write!(writer, ")") - } - InlineAsm { .. } => write!(writer, "{INDENT}InlineAsm"), - } -} - -fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec { - use self::TerminatorKind::*; - match terminator { - Call { target: None, unwind: UnwindAction::Cleanup(_), .. } - | InlineAsm { destination: None, .. } => vec!["unwind".into()], - Resume | Abort | Return | Unreachable | Call { target: None, unwind: _, .. } => vec![], - Goto { .. } => vec!["".to_string()], - SwitchInt { targets, .. } => targets - .branches() - .map(|(val, _target)| format!("{val}")) - .chain(iter::once("otherwise".into())) - .collect(), - Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()], - Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => { - vec!["return".into(), "unwind".into()] - } - Drop { unwind: _, .. } | Call { target: Some(_), unwind: _, .. } => vec!["return".into()], - Assert { unwind: UnwindAction::Cleanup(_), .. } => { - vec!["success".into(), "unwind".into()] - } - Assert { unwind: _, .. } => vec!["success".into()], - InlineAsm { destination: Some(_), .. } => vec!["goto".into(), "unwind".into()], - } -} - -fn pretty_assert_message(writer: &mut W, msg: &AssertMessage) -> io::Result<()> { - match msg { - AssertMessage::BoundsCheck { len, index } => { - let pretty_len = pretty_operand(len); - let pretty_index = pretty_operand(index); - write!( - writer, - "\"index out of bounds: the length is {{}} but the index is {{}}\", {pretty_len}, {pretty_index}" - ) - } - AssertMessage::Overflow(BinOp::Add, l, r) => { - let pretty_l = pretty_operand(l); - let pretty_r = pretty_operand(r); - write!( - writer, - "\"attempt to compute `{{}} + {{}}`, which would overflow\", {pretty_l}, {pretty_r}" - ) - } - AssertMessage::Overflow(BinOp::Sub, l, r) => { - let pretty_l = pretty_operand(l); - let pretty_r = pretty_operand(r); - write!( - writer, - "\"attempt to compute `{{}} - {{}}`, which would overflow\", {pretty_l}, {pretty_r}" - ) - } - AssertMessage::Overflow(BinOp::Mul, l, r) => { - let pretty_l = pretty_operand(l); - let pretty_r = pretty_operand(r); - write!( - writer, - "\"attempt to compute `{{}} * {{}}`, which would overflow\", {pretty_l}, {pretty_r}" - ) - } - AssertMessage::Overflow(BinOp::Div, l, r) => { - let pretty_l = pretty_operand(l); - let pretty_r = pretty_operand(r); - write!( - writer, - "\"attempt to compute `{{}} / {{}}`, which would overflow\", {pretty_l}, {pretty_r}" - ) - } - AssertMessage::Overflow(BinOp::Rem, l, r) => { - let pretty_l = pretty_operand(l); - let pretty_r = pretty_operand(r); - write!( - writer, - "\"attempt to compute `{{}} % {{}}`, which would overflow\", {pretty_l}, {pretty_r}" - ) - } - AssertMessage::Overflow(BinOp::Shr, _, r) => { - let pretty_r = pretty_operand(r); - write!(writer, "\"attempt to shift right by `{{}}`, which would overflow\", {pretty_r}") - } - AssertMessage::Overflow(BinOp::Shl, _, r) => { - let pretty_r = pretty_operand(r); - write!(writer, "\"attempt to shift left by `{{}}`, which would overflow\", {pretty_r}") - } - AssertMessage::Overflow(op, _, _) => unreachable!("`{:?}` cannot overflow", op), - AssertMessage::OverflowNeg(op) => { - let pretty_op = pretty_operand(op); - write!(writer, "\"attempt to negate `{{}}`, which would overflow\", {pretty_op}") - } - AssertMessage::DivisionByZero(op) => { - let pretty_op = pretty_operand(op); - write!(writer, "\"attempt to divide `{{}}` by zero\", {pretty_op}") - } - AssertMessage::RemainderByZero(op) => { - let pretty_op = pretty_operand(op); - write!( - writer, - "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {pretty_op}" - ) - } - AssertMessage::MisalignedPointerDereference { required, found } => { - let pretty_required = pretty_operand(required); - let pretty_found = pretty_operand(found); - write!( - writer, - "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}" - ) - } - AssertMessage::NullPointerDereference => { - write!(writer, "\"null pointer dereference occurred\"") - } - AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { - write!(writer, "{}", msg.description().unwrap()) - } - } -} - -fn pretty_operand(operand: &Operand) -> String { - match operand { - Operand::Copy(copy) => { - format!("{copy:?}") - } - Operand::Move(mv) => { - format!("move {mv:?}") - } - Operand::Constant(cnst) => pretty_mir_const(&cnst.const_), - } -} - -fn pretty_mir_const(literal: &MirConst) -> String { - with(|cx| cx.mir_const_pretty(literal)) -} - -fn pretty_ty_const(ct: &TyConst) -> String { - with(|cx| cx.ty_const_pretty(ct.id)) -} - -fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { - match rval { - Rvalue::AddressOf(mutability, place) => { - write!(writer, "&raw {} {:?}", pretty_raw_ptr_kind(*mutability), place) - } - Rvalue::Aggregate(aggregate_kind, operands) => { - // FIXME: Add pretty_aggregate function that returns a pretty string - pretty_aggregate(writer, aggregate_kind, operands) - } - Rvalue::BinaryOp(bin, op1, op2) => { - write!(writer, "{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2)) - } - Rvalue::Cast(_, op, ty) => { - write!(writer, "{} as {}", pretty_operand(op), ty) - } - Rvalue::CheckedBinaryOp(bin, op1, op2) => { - write!(writer, "Checked{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2)) - } - Rvalue::CopyForDeref(deref) => { - write!(writer, "CopyForDeref({deref:?})") - } - Rvalue::Discriminant(place) => { - write!(writer, "discriminant({place:?})") - } - Rvalue::Len(len) => { - write!(writer, "len({len:?})") - } - Rvalue::Ref(_, borrowkind, place) => { - let kind = match borrowkind { - BorrowKind::Shared => "&", - BorrowKind::Fake(FakeBorrowKind::Deep) => "&fake ", - BorrowKind::Fake(FakeBorrowKind::Shallow) => "&fake shallow ", - BorrowKind::Mut { .. } => "&mut ", - }; - write!(writer, "{kind}{place:?}") - } - Rvalue::Repeat(op, cnst) => { - write!(writer, "[{}; {}]", pretty_operand(op), pretty_ty_const(cnst)) - } - Rvalue::ShallowInitBox(_, _) => Ok(()), - Rvalue::ThreadLocalRef(item) => { - write!(writer, "thread_local_ref{item:?}") - } - Rvalue::NullaryOp(nul, ty) => { - write!(writer, "{nul:?}::<{ty}>() \" \"") - } - Rvalue::UnaryOp(un, op) => { - write!(writer, "{:?}({})", un, pretty_operand(op)) - } - Rvalue::Use(op) => write!(writer, "{}", pretty_operand(op)), - } -} - -fn pretty_aggregate( - writer: &mut W, - aggregate_kind: &AggregateKind, - operands: &Vec, -) -> io::Result<()> { - let suffix = match aggregate_kind { - AggregateKind::Array(_) => { - write!(writer, "[")?; - "]" - } - AggregateKind::Tuple => { - write!(writer, "(")?; - ")" - } - AggregateKind::Adt(def, var, _, _, _) => { - if def.kind() == AdtKind::Enum { - write!(writer, "{}::{}", def.name(), def.variant(*var).unwrap().name())?; - } else { - write!(writer, "{}", def.variant(*var).unwrap().name())?; - } - if operands.is_empty() { - return Ok(()); - } - // FIXME: Change this once we have CtorKind in StableMIR. - write!(writer, "(")?; - ")" - } - AggregateKind::Closure(def, _) => { - write!(writer, "{{closure@{}}}(", def.span().diagnostic())?; - ")" - } - AggregateKind::Coroutine(def, _, _) => { - write!(writer, "{{coroutine@{}}}(", def.span().diagnostic())?; - ")" - } - AggregateKind::CoroutineClosure(def, _) => { - write!(writer, "{{coroutine-closure@{}}}(", def.span().diagnostic())?; - ")" - } - AggregateKind::RawPtr(ty, mutability) => { - write!( - writer, - "*{} {ty} from (", - if *mutability == Mutability::Mut { "mut" } else { "const" } - )?; - ")" - } - }; - let mut separator = ""; - for op in operands { - write!(writer, "{}{}", separator, pretty_operand(op))?; - separator = ", "; - } - write!(writer, "{suffix}") -} - -fn pretty_mut(mutability: Mutability) -> &'static str { - match mutability { - Mutability::Not => " ", - Mutability::Mut => "mut ", - } -} - -fn pretty_raw_ptr_kind(kind: RawPtrKind) -> &'static str { - match kind { - RawPtrKind::Const => "const", - RawPtrKind::Mut => "mut", - RawPtrKind::FakeForPtrMetadata => "const (fake)", - } -} diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs deleted file mode 100644 index d985a98fcbf01..0000000000000 --- a/compiler/stable_mir/src/mir/visit.rs +++ /dev/null @@ -1,506 +0,0 @@ -//! # The Stable MIR Visitor -//! -//! ## Overview -//! -//! We currently only support an immutable visitor. -//! The structure of this visitor is similar to the ones internal to `rustc`, -//! and it follows the following conventions: -//! -//! For every mir item, the trait has a `visit_` and a `super_` method. -//! - `visit_`, by default, calls `super_` -//! - `super_`, by default, destructures the `` and calls `visit_` for -//! all sub-items that compose the original item. -//! -//! In order to implement a visitor, override the `visit_*` methods for the types you are -//! interested in analyzing, and invoke (within that method call) -//! `self.super_*` to continue to the traverse. -//! Avoid calling `super` methods in other circumstances. -//! -//! 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. -//! -//! Use pattern matching to reference fields and ensure that all -//! matches are exhaustive. -//! -//! 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. - -use crate::mir::*; -use crate::ty::{GenericArgs, MirConst, Region, Ty, TyConst}; -use crate::{Error, Opaque, Span}; - -pub trait MirVisitor { - fn visit_body(&mut self, body: &Body) { - self.super_body(body) - } - - fn visit_basic_block(&mut self, bb: &BasicBlock) { - self.super_basic_block(bb) - } - - fn visit_ret_decl(&mut self, local: Local, decl: &LocalDecl) { - self.super_ret_decl(local, decl) - } - - fn visit_arg_decl(&mut self, local: Local, decl: &LocalDecl) { - self.super_arg_decl(local, decl) - } - - fn visit_local_decl(&mut self, local: Local, decl: &LocalDecl) { - self.super_local_decl(local, decl) - } - - fn visit_statement(&mut self, stmt: &Statement, location: Location) { - self.super_statement(stmt, location) - } - - fn visit_terminator(&mut self, term: &Terminator, location: Location) { - self.super_terminator(term, location) - } - - fn visit_span(&mut self, span: &Span) { - self.super_span(span) - } - - fn visit_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) { - self.super_place(place, ptx, location) - } - - fn visit_projection_elem( - &mut self, - place_ref: PlaceRef<'_>, - elem: &ProjectionElem, - ptx: PlaceContext, - location: Location, - ) { - let _ = place_ref; - self.super_projection_elem(elem, ptx, location); - } - - fn visit_local(&mut self, local: &Local, ptx: PlaceContext, location: Location) { - let _ = (local, ptx, location); - } - - fn visit_rvalue(&mut self, rvalue: &Rvalue, location: Location) { - self.super_rvalue(rvalue, location) - } - - fn visit_operand(&mut self, operand: &Operand, location: Location) { - self.super_operand(operand, location) - } - - fn visit_user_type_projection(&mut self, projection: &UserTypeProjection) { - self.super_user_type_projection(projection) - } - - fn visit_ty(&mut self, ty: &Ty, location: Location) { - let _ = location; - self.super_ty(ty) - } - - fn visit_const_operand(&mut self, constant: &ConstOperand, location: Location) { - self.super_const_operand(constant, location) - } - - fn visit_mir_const(&mut self, constant: &MirConst, location: Location) { - self.super_mir_const(constant, location) - } - - fn visit_ty_const(&mut self, constant: &TyConst, location: Location) { - let _ = location; - self.super_ty_const(constant) - } - - fn visit_region(&mut self, region: &Region, location: Location) { - let _ = location; - self.super_region(region) - } - - fn visit_args(&mut self, args: &GenericArgs, location: Location) { - let _ = location; - self.super_args(args) - } - - fn visit_assert_msg(&mut self, msg: &AssertMessage, location: Location) { - self.super_assert_msg(msg, location) - } - - fn visit_var_debug_info(&mut self, var_debug_info: &VarDebugInfo) { - self.super_var_debug_info(var_debug_info); - } - - fn super_body(&mut self, body: &Body) { - let Body { blocks, locals: _, arg_count, var_debug_info, spread_arg: _, span } = body; - - for bb in blocks { - self.visit_basic_block(bb); - } - - self.visit_ret_decl(RETURN_LOCAL, body.ret_local()); - - for (idx, arg) in body.arg_locals().iter().enumerate() { - self.visit_arg_decl(idx + 1, arg) - } - - let local_start = arg_count + 1; - for (idx, arg) in body.inner_locals().iter().enumerate() { - self.visit_local_decl(idx + local_start, arg) - } - - for info in var_debug_info.iter() { - self.visit_var_debug_info(info); - } - - self.visit_span(span) - } - - fn super_basic_block(&mut self, bb: &BasicBlock) { - let BasicBlock { statements, terminator } = bb; - for stmt in statements { - self.visit_statement(stmt, Location(stmt.span)); - } - self.visit_terminator(terminator, Location(terminator.span)); - } - - fn super_local_decl(&mut self, local: Local, decl: &LocalDecl) { - let _ = local; - let LocalDecl { ty, span, .. } = decl; - self.visit_ty(ty, Location(*span)); - } - - fn super_ret_decl(&mut self, local: Local, decl: &LocalDecl) { - self.super_local_decl(local, decl) - } - - fn super_arg_decl(&mut self, local: Local, decl: &LocalDecl) { - self.super_local_decl(local, decl) - } - - fn super_statement(&mut self, stmt: &Statement, location: Location) { - let Statement { kind, span } = stmt; - self.visit_span(span); - match kind { - StatementKind::Assign(place, rvalue) => { - self.visit_place(place, PlaceContext::MUTATING, location); - self.visit_rvalue(rvalue, location); - } - StatementKind::FakeRead(_, place) | StatementKind::PlaceMention(place) => { - self.visit_place(place, PlaceContext::NON_MUTATING, location); - } - StatementKind::SetDiscriminant { place, .. } - | StatementKind::Deinit(place) - | StatementKind::Retag(_, place) => { - self.visit_place(place, PlaceContext::MUTATING, location); - } - StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { - self.visit_local(local, PlaceContext::NON_USE, location); - } - StatementKind::AscribeUserType { place, projections, variance: _ } => { - self.visit_place(place, PlaceContext::NON_USE, location); - self.visit_user_type_projection(projections); - } - StatementKind::Coverage(coverage) => visit_opaque(coverage), - StatementKind::Intrinsic(intrisic) => match intrisic { - NonDivergingIntrinsic::Assume(operand) => { - self.visit_operand(operand, location); - } - NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { - src, - dst, - count, - }) => { - self.visit_operand(src, location); - self.visit_operand(dst, location); - self.visit_operand(count, location); - } - }, - StatementKind::ConstEvalCounter | StatementKind::Nop => {} - } - } - - fn super_terminator(&mut self, term: &Terminator, location: Location) { - let Terminator { kind, span } = term; - self.visit_span(span); - match kind { - TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Abort - | TerminatorKind::Unreachable => {} - TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { - self.visit_operand(cond, location); - self.visit_assert_msg(msg, location); - } - TerminatorKind::Drop { place, target: _, unwind: _ } => { - self.visit_place(place, PlaceContext::MUTATING, location); - } - TerminatorKind::Call { func, args, destination, target: _, unwind: _ } => { - self.visit_operand(func, location); - for arg in args { - self.visit_operand(arg, location); - } - self.visit_place(destination, PlaceContext::MUTATING, location); - } - TerminatorKind::InlineAsm { operands, .. } => { - for op in operands { - let InlineAsmOperand { in_value, out_place, raw_rpr: _ } = op; - if let Some(input) = in_value { - self.visit_operand(input, location); - } - if let Some(output) = out_place { - self.visit_place(output, PlaceContext::MUTATING, location); - } - } - } - TerminatorKind::Return => { - let local = RETURN_LOCAL; - self.visit_local(&local, PlaceContext::NON_MUTATING, location); - } - TerminatorKind::SwitchInt { discr, targets: _ } => { - self.visit_operand(discr, location); - } - } - } - - fn super_span(&mut self, span: &Span) { - let _ = span; - } - - fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) { - let _ = location; - let _ = ptx; - self.visit_local(&place.local, ptx, location); - - for (idx, elem) in place.projection.iter().enumerate() { - let place_ref = PlaceRef { local: place.local, projection: &place.projection[..idx] }; - self.visit_projection_elem(place_ref, elem, ptx, location); - } - } - - fn super_projection_elem( - &mut self, - elem: &ProjectionElem, - ptx: PlaceContext, - location: Location, - ) { - match elem { - ProjectionElem::Downcast(_idx) => {} - ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } - | ProjectionElem::Deref - | ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {} - ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location), - ProjectionElem::Index(local) => self.visit_local(local, ptx, location), - ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => { - self.visit_ty(ty, location) - } - } - } - - fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) { - match rvalue { - Rvalue::AddressOf(mutability, place) => { - let pcx = PlaceContext { is_mut: *mutability == RawPtrKind::Mut }; - self.visit_place(place, pcx, location); - } - Rvalue::Aggregate(_, operands) => { - for op in operands { - self.visit_operand(op, location); - } - } - Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { - self.visit_operand(lhs, location); - self.visit_operand(rhs, location); - } - Rvalue::Cast(_, op, ty) => { - self.visit_operand(op, location); - self.visit_ty(ty, location); - } - Rvalue::CopyForDeref(place) | Rvalue::Discriminant(place) | Rvalue::Len(place) => { - self.visit_place(place, PlaceContext::NON_MUTATING, location); - } - Rvalue::Ref(region, kind, place) => { - self.visit_region(region, location); - let pcx = PlaceContext { is_mut: matches!(kind, BorrowKind::Mut { .. }) }; - self.visit_place(place, pcx, location); - } - Rvalue::Repeat(op, constant) => { - self.visit_operand(op, location); - self.visit_ty_const(constant, location); - } - Rvalue::ShallowInitBox(op, ty) => { - self.visit_ty(ty, location); - self.visit_operand(op, location) - } - Rvalue::ThreadLocalRef(_) => {} - Rvalue::NullaryOp(_, ty) => { - self.visit_ty(ty, location); - } - Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => { - self.visit_operand(op, location); - } - } - } - - fn super_operand(&mut self, operand: &Operand, location: Location) { - match operand { - Operand::Copy(place) | Operand::Move(place) => { - self.visit_place(place, PlaceContext::NON_MUTATING, location) - } - Operand::Constant(constant) => { - self.visit_const_operand(constant, location); - } - } - } - - fn super_user_type_projection(&mut self, projection: &UserTypeProjection) { - // This is a no-op on mir::Visitor. - let _ = projection; - } - - fn super_ty(&mut self, ty: &Ty) { - let _ = ty; - } - - fn super_const_operand(&mut self, constant: &ConstOperand, location: Location) { - let ConstOperand { span, user_ty: _, const_ } = constant; - self.visit_span(span); - self.visit_mir_const(const_, location); - } - - fn super_mir_const(&mut self, constant: &MirConst, location: Location) { - let MirConst { kind: _, ty, id: _ } = constant; - self.visit_ty(ty, location); - } - - fn super_ty_const(&mut self, constant: &TyConst) { - let _ = constant; - } - - fn super_region(&mut self, region: &Region) { - let _ = region; - } - - fn super_args(&mut self, args: &GenericArgs) { - let _ = args; - } - - fn super_var_debug_info(&mut self, var_debug_info: &VarDebugInfo) { - let VarDebugInfo { source_info, composite, value, name: _, argument_index: _ } = - var_debug_info; - self.visit_span(&source_info.span); - let location = Location(source_info.span); - if let Some(composite) = composite { - self.visit_ty(&composite.ty, location); - } - match value { - VarDebugInfoContents::Place(place) => { - self.visit_place(place, PlaceContext::NON_USE, location); - } - VarDebugInfoContents::Const(constant) => { - self.visit_mir_const(&constant.const_, location); - } - } - } - - fn super_assert_msg(&mut self, msg: &AssertMessage, location: Location) { - match msg { - AssertMessage::BoundsCheck { len, index } => { - self.visit_operand(len, location); - self.visit_operand(index, location); - } - AssertMessage::Overflow(_, left, right) => { - self.visit_operand(left, location); - self.visit_operand(right, location); - } - AssertMessage::OverflowNeg(op) - | AssertMessage::DivisionByZero(op) - | AssertMessage::RemainderByZero(op) => { - self.visit_operand(op, location); - } - AssertMessage::ResumedAfterReturn(_) - | AssertMessage::ResumedAfterPanic(_) - | AssertMessage::NullPointerDereference => { - //nothing to visit - } - AssertMessage::MisalignedPointerDereference { required, found } => { - self.visit_operand(required, location); - self.visit_operand(found, location); - } - } - } -} - -/// This function is a no-op that gets used to ensure this visitor is kept up-to-date. -/// -/// The idea is that whenever we replace an Opaque type by a real type, the compiler will fail -/// when trying to invoke `visit_opaque`. -/// -/// If you are here because your compilation is broken, replace the failing call to `visit_opaque()` -/// by a `visit_` for your construct. -fn visit_opaque(_: &Opaque) {} - -/// The location of a statement / terminator in the code and the CFG. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct Location(Span); - -impl Location { - pub fn span(&self) -> Span { - self.0 - } -} - -/// Location of the statement at the given index for a given basic block. Assumes that `stmt_idx` -/// and `bb_idx` are valid for a given body. -pub fn statement_location(body: &Body, bb_idx: &BasicBlockIdx, stmt_idx: usize) -> Location { - let bb = &body.blocks[*bb_idx]; - let stmt = &bb.statements[stmt_idx]; - Location(stmt.span) -} - -/// Location of the terminator for a given basic block. Assumes that `bb_idx` is valid for a given -/// body. -pub fn terminator_location(body: &Body, bb_idx: &BasicBlockIdx) -> Location { - let bb = &body.blocks[*bb_idx]; - let terminator = &bb.terminator; - Location(terminator.span) -} - -/// Reference to a place used to represent a partial projection. -pub struct PlaceRef<'a> { - pub local: Local, - pub projection: &'a [ProjectionElem], -} - -impl PlaceRef<'_> { - /// Get the type of this place. - pub fn ty(&self, locals: &[LocalDecl]) -> Result { - self.projection.iter().fold(Ok(locals[self.local].ty), |place_ty, elem| elem.ty(place_ty?)) - } -} - -/// Information about a place's usage. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct PlaceContext { - /// Whether the access is mutable or not. Keep this private so we can increment the type in a - /// backward compatible manner. - is_mut: bool, -} - -impl PlaceContext { - const MUTATING: Self = PlaceContext { is_mut: true }; - const NON_MUTATING: Self = PlaceContext { is_mut: false }; - const NON_USE: Self = PlaceContext { is_mut: false }; - - pub fn is_mutating(&self) -> bool { - self.is_mut - } -} diff --git a/config.example.toml b/config.example.toml deleted file mode 100644 index 2e26c024865db..0000000000000 --- a/config.example.toml +++ /dev/null @@ -1,984 +0,0 @@ -# Sample TOML configuration file for building Rust. -# -# To configure bootstrap, run `./configure` or `./x.py setup`. -# See https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html#create-a-configtoml for more information. -# -# All options are commented out by default in this file, and they're commented -# out with their default values. The build system by default looks for -# `config.toml` in the current directory of a build for build configuration, but -# a custom configuration file can also be specified with `--config` to the build -# system. - -# ============================================================================= -# Global Settings -# ============================================================================= - -# Use different pre-set defaults than the global defaults. -# -# See `src/bootstrap/defaults` for more information. -# Note that this has no default value (x.py uses the defaults in `config.example.toml`). -#profile = - -# Keeps track of major changes made to this configuration. -# -# This value also represents ID of the PR that caused major changes. Meaning, -# you can visit github.com/rust-lang/rust/pull/{change-id} to check for more details. -# -# A 'major change' includes any of the following -# - A new option -# - A change in the default values -# -# If `change-id` does not match the version that is currently running, -# `x.py` will inform you about the changes made on bootstrap. -#change-id = - -# ============================================================================= -# Tweaking how LLVM is compiled -# ============================================================================= -[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 leave this enabled. -# -# Set this to `true` to download if CI llvm available otherwise it builds -# from `src/llvm-project`. -# -# Set this to `"if-unchanged"` to download only if the llvm-project has not -# been modified. You can also use this if you are unsure whether you're on a -# tier 1 target. All tier 1 targets are currently supported. - -# Currently, we 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 = true - -# Indicates whether the LLVM build is a Release or Debug build -#optimize = true - -# Indicates whether LLVM should be built with ThinLTO. Note that this will -# only succeed if you use clang, lld, llvm-ar, and llvm-ranlib in your C/C++ -# toolchain (see the `cc`, `cxx`, `linker`, `ar`, and `ranlib` options below). -# More info at: https://clang.llvm.org/docs/ThinLTO.html#clang-bootstrap -#thin-lto = false - -# Indicates whether an LLVM Release build should include debug info -#release-debuginfo = false - -# Indicates whether the LLVM assertions are enabled or not -# NOTE: When assertions are disabled, bugs in the integration between rustc and LLVM can lead to -# unsoundness (segfaults, etc.) in the rustc process itself, not just in the generated code. -#assertions = false - -# Indicates whether the LLVM testsuite is enabled in the build or not. Does -# not execute the tests as part of the build as part of x.py build et al, -# just makes it possible to do `ninja check-llvm` in the staged LLVM build -# directory when doing LLVM development as part of Rust development. -#tests = false - -# Indicates whether the LLVM plugin is enabled or not -#plugins = false - -# Whether to build Enzyme as AutoDiff backend. -#enzyme = false - -# Whether to build LLVM with support for it's gpu offload runtime. -#offload = false - -# When true, link libstdc++ statically into the rustc_llvm. -# This is useful if you don't want to use the dynamic version of that -# library provided by LLVM. -#static-libstdcpp = false - -# Enable LLVM to use zstd for compression. -#libzstd = 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 -# dependent on LLVM for code generation, turning targets off here WILL lead to -# the resulting rustc being unable to compile for the disabled architectures. -# -# To add support for new targets, see https://rustc-dev-guide.rust-lang.org/building/new-target.html. -#targets = "AArch64;AMDGPU;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;Sparc;SystemZ;WebAssembly;X86" - -# LLVM experimental targets to build support for. These targets are specified in -# the same format as above, but since these targets are experimental, they are -# not built by default and the experimental Rust compilation targets that depend -# on them will not work unless the user opts in to building them. -#experimental-targets = "AVR;M68k;CSKY" - -# Cap the number of parallel linker invocations when compiling LLVM. -# This can be useful when building LLVM with debug info, which significantly -# increases the size of binaries and consequently the memory required by -# each linker process. -# If set to 0, linker invocations are treated like any other job and -# controlled by bootstrap's -j parameter. -#link-jobs = 0 - -# Whether to build LLVM as a dynamically linked library (as opposed to statically linked). -# Under the hood, this passes `--shared` to llvm-config. -# NOTE: To avoid performing LTO multiple times, we suggest setting this to `true` when `thin-lto` is enabled. -#link-shared = llvm.thin-lto - -# When building llvm, this configures what is being appended to the version. -# To use LLVM version as is, provide an empty string. -#version-suffix = if rust.channel == "dev" { "-rust-dev" } else { "-rust-$version-$channel" } - -# On MSVC you can compile LLVM with clang-cl, but the test suite doesn't pass -# with clang-cl, so this is special in that it only compiles LLVM with clang-cl. -# Note that this takes a /path/to/clang-cl, not a boolean. -#clang-cl = cc - -# Pass extra compiler and linker flags to the LLVM CMake build. -#cflags = "" -#cxxflags = "" -#ldflags = "" - -# Use libc++ when building LLVM instead of libstdc++. This is the default on -# platforms already use libc++ as the default C++ library, but this option -# allows you to use libc++ even on platforms when it's not. You need to ensure -# that your host compiler ships with libc++. -#use-libcxx = false - -# The value specified here will be passed as `-DLLVM_USE_LINKER` to CMake. -#use-linker = (path) - -# Whether or not to specify `-DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=YES` -#allow-old-toolchain = false - -# Whether to include the Polly optimizer. -#polly = false - -# Whether to build the clang compiler. -#clang = false - -# Whether to enable llvm compilation warnings. -#enable-warnings = false - -# Custom CMake defines to set when building LLVM. -#build-config = {} - -# ============================================================================= -# General build configuration options -# ============================================================================= -[build] - -# The default stage to use for the `check` subcommand -#check-stage = 0 - -# The default stage to use for the `doc` subcommand -#doc-stage = 0 - -# The default stage to use for the `build` subcommand -#build-stage = 1 - -# The default stage to use for the `test` subcommand -#test-stage = 1 - -# The default stage to use for the `dist` subcommand -#dist-stage = 2 - -# The default stage to use for the `install` subcommand -#install-stage = 2 - -# The default stage to use for the `bench` subcommand -#bench-stage = 2 - -# Build triple for the pre-compiled snapshot compiler. If `rustc` is set, this must match its host -# triple (see `rustc --version --verbose`; cross-compiling the rust build system itself is NOT -# supported). If `rustc` is unset, this must be a platform with pre-compiled host tools -# (https://doc.rust-lang.org/nightly/rustc/platform-support.html). The current platform must be -# able to run binaries of this build triple. -# -# If `rustc` is present in path, this defaults to the host it was compiled for. -# Otherwise, `x.py` will try to infer it from the output of `uname`. -# If `uname` is not found in PATH, we assume this is `x86_64-pc-windows-msvc`. -# This may be changed in the future. -#build = "x86_64-unknown-linux-gnu" (as an example) - -# Which triples to produce a compiler toolchain for. Each of these triples will be bootstrapped from -# the build triple themselves. In other words, this is the list of triples for which to build a -# compiler that can RUN on that triple. -# -# Defaults to just the `build` triple. -#host = [build.build] (list of triples) - -# Which triples to build libraries (core/alloc/std/test/proc_macro) for. Each of these triples will -# be bootstrapped from the build triple themselves. In other words, this is the list of triples for -# which to build a library that can CROSS-COMPILE to that 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 = build.host (list of triples) - -# Use this directory to store build artifacts. Paths are relative to the current directory, not to -# the root of the repository. -#build-dir = "build" - -# Instead of downloading the src/stage0 version of Cargo specified, use -# this Cargo binary instead to build all Rust code -# If you set this, you likely want to set `rustc` as well. -#cargo = "/path/to/cargo" - -# Instead of downloading the src/stage0 version of the compiler -# specified, use this rustc binary instead as the stage0 snapshot compiler. -# If you set this, you likely want to set `cargo` as well. -#rustc = "/path/to/rustc" - -# Instead of downloading the src/stage0 version of rustfmt specified, -# use this rustfmt binary instead as the stage0 snapshot rustfmt. -#rustfmt = "/path/to/rustfmt" - -# Instead of downloading the src/stage0 version of cargo-clippy specified, -# use this cargo-clippy binary instead as the stage0 snapshot cargo-clippy. -# -# Note that this option should be used with the same toolchain as the `rustc` option above. -# Otherwise, clippy is likely to fail due to a toolchain conflict. -#cargo-clippy = "/path/to/cargo-clippy" - -# Whether to build documentation by default. If false, rustdoc and -# friends will still be compiled but they will not be used to generate any -# documentation. -# -# You can still build documentation when this is disabled by explicitly passing paths, -# e.g. `x doc library`. -#docs = true - -# Flag to specify whether CSS, JavaScript, and HTML are minified when -# docs are generated. JSON is always minified, because it's enormous, -# and generated in already-minified form from the beginning. -#docs-minification = true - -# Flag to specify whether private items should be included in the library docs. -#library-docs-private-items = false - -# Indicate whether to build compiler documentation by default. -# You can still build documentation when this is disabled by explicitly passing a path: `x doc compiler`. -#compiler-docs = false - -# Indicate whether git submodules are managed and updated automatically. -#submodules = true - -# The path to (or name of) the GDB executable to use. This is only used for -# executing the debuginfo test suite. -#gdb = "gdb" - -# The path to (or name of) the LLDB executable to use. This is only used for -# executing the debuginfo test suite. -#lldb = "lldb" - -# The node.js executable to use. Note that this is only used for the emscripten -# target when running tests, otherwise this can be omitted. -#nodejs = "node" - -# The npm executable to use. Note that this is used for rustdoc-gui tests, -# otherwise this can be omitted. -# -# Under Windows this should be `npm.cmd` or path to it (verified on nodejs v18.06), or -# error will be emitted. -#npm = "npm" - -# Python interpreter to use for various tasks throughout the build, notably -# rustdoc tests, the lldb python interpreter, and some dist bits and pieces. -# -# Defaults to the Python interpreter used to execute x.py. -#python = "python" - -# The path to the REUSE executable to use. Note that REUSE is not required in -# most cases, as our tooling relies on a cached (and shrunk) copy of the -# REUSE output present in the git repository and in our source tarballs. -# -# REUSE is only needed if your changes caused the overall licensing of the -# repository to change, and the cached copy has to be regenerated. -# -# Defaults to the "reuse" command in the system path. -#reuse = "reuse" - -# Force Cargo to check that Cargo.lock describes the precise dependency -# set that all the Cargo.toml files create, instead of updating it. -#locked-deps = false - -# Indicate whether the vendored sources are used for Rust dependencies or not. -# -# Vendoring requires additional setup. We recommend using the pre-generated source tarballs if you -# want to use vendoring. See https://forge.rust-lang.org/infra/other-installation-methods.html#source-code. -#vendor = if "is a tarball source" && "vendor" dir exists && ".cargo/config.toml" file exists { true } else { false } - -# Typically the build system will build the Rust compiler twice. The second -# compiler, however, will simply use its own libraries to link against. If you -# would rather to perform a full bootstrap, compiling the compiler three times, -# then you can set this option to true. -# -# This is only useful for verifying that rustc generates reproducible builds. -#full-bootstrap = false - -# Set the bootstrap/download cache path. It is useful when building rust -# repeatedly in a CI environment. -#bootstrap-cache-path = /path/to/shared/cache - -# Enable a build of the extended Rust tool set which is not only the compiler -# but also tools such as Cargo. This will also produce "combined installers" -# which are used to install Rust and Cargo together. -# The `tools` (check `config.example.toml` to see its default value) option specifies -# which tools should be built if `extended = true`. -# -# This is disabled by default. -#extended = false - -# Set of tools to be included in the installation. -# -# If `extended = false`, the only one of these built by default is rustdoc. -# -# If `extended = true`, they are all included. -# -# If any enabled tool fails to build, the installation fails. -#tools = [ -# "cargo", -# "clippy", -# "rustdoc", -# "rustfmt", -# "rust-analyzer", -# "rust-analyzer-proc-macro-srv", -# "analysis", -# "src", -# "wasm-component-ld", -# "miri", "cargo-miri" # for dev/nightly channels -#] - -# Verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose, 3 == print environment variables on each rustc invocation -#verbose = 0 - -# Build the sanitizer runtimes -#sanitizers = false - -# Build the profiler runtime (required when compiling with options that depend -# on this runtime, such as `-C profile-generate` or `-C instrument-coverage`). -#profiler = false - -# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics. -# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt` -# sources are available. -# -# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in -# order to run `x check`. -#optimized-compiler-builtins = if rust.channel == "dev" { false } else { true } - -# Indicates whether the native libraries linked into Cargo will be statically -# linked or not. -#cargo-native-static = false - -# Run the build with low priority, by setting the process group's "nice" value -# to +10 on Unix platforms, and by using a "low priority" job object on Windows. -#low-priority = false - -# Arguments passed to the `./configure` script, used during distcheck. You -# probably won't fill this in but rather it's filled in by the `./configure` -# script. Useful for debugging. -#configure-args = [] - -# Indicates that a local rebuild is occurring instead of a full bootstrap, -# essentially skipping stage0 as the local compiler is recompiling itself again. -# Useful for modifying only the stage2 compiler without having to pass `--keep-stage 0` each time. -#local-rebuild = false - -# Print out how long each bootstrap step took (mostly intended for CI and -# tracking over time) -#print-step-timings = false - -# Print out resource usage data for each bootstrap step, as defined by the Unix -# struct rusage. (Note that this setting is completely unstable: the data it -# captures, what platforms it supports, the format of its associated output, and -# this setting's very existence, are all subject to change.) -#print-step-rusage = false - -# Always patch binaries for usage with Nix toolchains. If `true` then binaries -# will be patched unconditionally. If `false` or unset, binaries will be patched -# only if the current distribution is NixOS. This option is useful when using -# a Nix toolchain on non-NixOS distributions. -#patch-binaries-for-nix = false - -# Collect information and statistics about the current build, and write it to -# disk. Enabling this has no impact on the resulting build output. The -# schema of the file generated by the build metrics feature is unstable, and -# this is not intended to be used during local development. -#metrics = false - -# Specify the location of the Android NDK. Used when targeting Android. -#android-ndk = "/path/to/android-ndk-r26d" - -# Number of parallel jobs to be used for building and testing. If set to `0` or -# omitted, it will be automatically determined. This is the `-j`/`--jobs` flag -# passed to cargo invocations. -#jobs = 0 - -# What custom diff tool to use for displaying compiletest tests. -#compiletest-diff-tool = - -# Indicates whether ccache is used when building certain artifacts (e.g. LLVM). -# Set to `true` to use the first `ccache` in PATH, or set an absolute path to use -# a specific version. -#ccache = false - -# ============================================================================= -# General install configuration options -# ============================================================================= -[install] - -# Where to install the generated toolchain. Must be an absolute path. -#prefix = "/usr/local" - -# Where to install system configuration files. -# If this is a relative path, it will get installed in `prefix` above -#sysconfdir = "/etc" - -# Where to install documentation in `prefix` above -#docdir = "share/doc/rust" - -# Where to install binaries in `prefix` above -#bindir = "bin" - -# Where to install libraries in `prefix` above -#libdir = "lib" - -# Where to install man pages in `prefix` above -#mandir = "share/man" - -# Where to install data in `prefix` above -#datadir = "share" - -# ============================================================================= -# Options for compiling Rust code itself -# ============================================================================= -[rust] - -# Whether or not to optimize when compiling the compiler and standard library, -# and what level of optimization to use. -# WARNING: Building with optimize = false is NOT SUPPORTED. Due to bootstrapping, -# building without optimizations takes much longer than optimizing. Further, some platforms -# fail to build without this optimization (c.f. #65352). -# The valid options are: -# true - Enable optimizations. -# false - Disable optimizations. -# 0 - Disable optimizations. -# 1 - Basic optimizations. -# 2 - Some optimizations. -# 3 - All optimizations. -# "s" - Optimize for binary size. -# "z" - Optimize for binary size, but also turn off loop vectorization. -#optimize = true - -# Indicates that the build should be configured for debugging Rust. A -# `debug`-enabled compiler and standard library will be somewhat -# slower (due to e.g. checking of debug assertions) but should remain -# usable. -# -# Note: If this value is set to `true`, it will affect a number of -# configuration options below as well, if they have been left -# unconfigured in this file. -# -# Note: changes to the `debug` setting do *not* affect `optimize` -# above. In theory, a "maximally debuggable" environment would -# set `optimize` to `false` above to assist the introspection -# facilities of debuggers like lldb and gdb. To recreate such an -# environment, explicitly set `optimize` to `false` and `debug` -# to `true`. In practice, everyone leaves `optimize` set to -# `true`, because an unoptimized rustc with debugging -# enabled becomes *unusably slow* (e.g. rust-lang/rust#24840 -# reported a 25x slowdown) and bootstrapping the supposed -# "maximally debuggable" environment (notably libstd) takes -# hours to build. -# -#debug = false - -# Whether to download the stage 1 and 2 compilers from CI. This is useful if you -# are working on tools, doc-comments, or library (you will be able to build the -# standard library without needing to build the compiler). -# -# Set this to "if-unchanged" if you are working on `src/tools`, `tests` or -# `library` (on CI, `library` changes triggers in-tree compiler build) to speed -# up the build process if you don't need to build a compiler from the latest -# commit from `master`. -# -# Set this to `true` to always download or `false` to always use the in-tree -# compiler. -#download-rustc = false - -# 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. -# -# 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. -# NOTE: building with anything other than 1 is known to occasionally have bugs. -#codegen-units-std = codegen-units - -# Whether or not debug assertions are enabled for the compiler and standard library. -# These can help find bugs at the cost of a small runtime slowdown. -# -# Defaults to rust.debug value -#debug-assertions = rust.debug (boolean) - -# 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 = rust.debug-assertions (boolean) - -# Whether or not to leave debug! and trace! calls in the rust binary. -# -# Defaults to rust.debug-assertions value -# -# If you see a message from `tracing` saying "some trace filter directives would enable traces that -# are disabled statically" because `max_level_info` is enabled, set this value to `true`. -#debug-logging = rust.debug-assertions (boolean) - -# Whether or not to build rustc, tools and the libraries with randomized type layout -#randomize-layout = false - -# Whether or not overflow checks are enabled for the compiler and standard -# library. -# -# Defaults to rust.debug value -#overflow-checks = rust.debug (boolean) - -# Whether or not overflow checks are enabled for the standard library. -# Overrides the `overflow-checks` option, if defined. -# -# Defaults to rust.overflow-checks value -#overflow-checks-std = rust.overflow-checks (boolean) - -# Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`. -# See https://doc.rust-lang.org/rustc/codegen-options/index.html#debuginfo for available options. -# -# Can be overridden for specific subsets of Rust code (rustc, std or tools). -# Debuginfo for tests run with compiletest is not controlled by this option -# and needs to be enabled separately with `debuginfo-level-tests`. -# -# Note that debuginfo-level = 2 generates several gigabytes of debuginfo -# and will slow down the linking process significantly. -#debuginfo-level = if rust.debug { 1 } else { 0 } - -# Debuginfo level for the compiler. -#debuginfo-level-rustc = rust.debuginfo-level - -# Debuginfo level for the standard library. -#debuginfo-level-std = rust.debuginfo-level - -# Debuginfo level for the tools. -#debuginfo-level-tools = rust.debuginfo-level - -# Debuginfo level for the test suites run with compiletest. -# FIXME(#61117): Some tests fail when this option is enabled. -#debuginfo-level-tests = 0 - -# Should rustc and the standard library be built with split debuginfo? Default -# is platform dependent. -# -# This field is deprecated, use `target..split-debuginfo` instead. -# -# The value specified here is only used when targeting the `build.build` triple, -# and is overridden by `target..split-debuginfo` if specified. -# -#split-debuginfo = see target..split-debuginfo - -# Whether or not `panic!`s generate backtraces (RUST_BACKTRACE) -#backtrace = true - -# Whether to always use incremental compilation when building rustc -#incremental = false - -# The default linker that will be hard-coded into the generated -# compiler for targets that don't specify a default linker explicitly -# in their target specifications. Note that this is not the linker -# used to link said compiler. It can also be set per-target (via the -# `[target.]` block), which may be useful in a cross-compilation -# setting. -# -# See https://doc.rust-lang.org/rustc/codegen-options/index.html#linker for more information. -#default-linker = (path) - -# The "channel" for the Rust build to produce. The stable/beta channels only -# allow using stable features, whereas the nightly and dev channels allow using -# nightly features. -# -# You can set the channel to "auto-detect" to load the channel name from `src/ci/channel`. -# -# If using tarball sources, default value is "auto-detect", otherwise, it's "dev". -#channel = if "is a tarball source" { "auto-detect" } else { "dev" } - -# A descriptive string to be appended to `rustc --version` output, which is -# also used in places like debuginfo `DW_AT_producer`. This may be useful for -# supplementary build information, like distro-specific package versions. -# -# The Rust compiler will differentiate between versions of itself, including -# based on this string, which means that if you wish to be compatible with -# upstream Rust you need to set this to "". However, note that if you are not -# actually compatible -- for example if you've backported patches that change -# behavior -- this may lead to miscompilations or other bugs. -#description = "" - -# The root location of the musl installation directory. The library directory -# will also need to contain libunwind.a for an unwinding implementation. Note -# that this option only makes sense for musl targets that produce statically -# linked binaries. -# -# Defaults to /usr on musl hosts. Has no default otherwise. -#musl-root = (path) - -# By default the `rustc` executable is built with `-Wl,-rpath` flags on Unix -# platforms to ensure that the compiler is usable by default from the build -# directory (as it links to a number of dynamic libraries). This may not be -# desired in distributions, for example. -#rpath = true - -# Indicates whether symbols should be stripped using `-Cstrip=symbols`. -#strip = false - -# Forces frame pointers to be used with `-Cforce-frame-pointers`. -# This can be helpful for profiling at a small performance cost. -#frame-pointers = false - -# Indicates whether stack protectors should be used -# via the unstable option `-Zstack-protector`. -# -# Valid options are : `none`(default),`basic`,`strong`, or `all`. -# `strong` and `basic` options may be buggy and are not recommended, see rust-lang/rust#114903. -#stack-protector = "none" - -# Prints each test name as it is executed, to help debug issues in the test harness itself. -#verbose-tests = if is_verbose { true } else { false } - -# Flag indicating whether tests are compiled with optimizations (the -O flag). -#optimize-tests = true - -# Flag indicating whether codegen tests will be run or not. If you get an error -# saying that the FileCheck executable is missing, you may want to disable this. -# Also see the target's llvm-filecheck option. -#codegen-tests = true - -# Flag indicating whether git info will be retrieved from .git automatically. -# Having the git information can cause a lot of rebuilds during development. -#omit-git-hash = if rust.channel == "dev" { true } else { false } - -# Whether to create a source tarball by default when running `x dist`. -# -# You can still build a source tarball when this is disabled by explicitly passing `x dist rustc-src`. -#dist-src = true - -# After building or testing an optional component (e.g. the nomicon or reference), append the -# result (broken, compiling, testing) into this JSON file. -#save-toolstates = (path) - -# This is an array of the codegen backends that will be compiled for the rustc -# that's being compiled. The default is to only build the LLVM codegen backend, -# and currently the only standard options supported are `"llvm"`, `"cranelift"` -# and `"gcc"`. The first backend in this list will be used as default by rustc -# when no explicit backend is specified. -#codegen-backends = ["llvm"] - -# Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute, and -# whether to set it as rustc's default linker on `x86_64-unknown-linux-gnu`. This will also only be -# when *not* building an external LLVM (so only when using `download-ci-llvm` or building LLVM from -# the in-tree source): setting `llvm-config` in the `[target.x86_64-unknown-linux-gnu]` section will -# make this default to false. -#lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true - -# Indicates whether LLD will be used to link Rust crates during bootstrap on -# supported platforms. -# If set to `true` or `"external"`, a global `lld` binary that has to be in $PATH -# will be used. -# If set to `"self-contained"`, rust-lld from the snapshot compiler will be used. -# -# On MSVC, LLD will not be used if we're cross linking. -# -# 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 -# sysroot. -#llvm-tools = true - -# Indicates whether the `self-contained` llvm-bitcode-linker, will be made available -# in the sysroot. It is required for running nvptx tests. -#llvm-bitcode-linker = false - -# Whether to deny warnings in crates -#deny-warnings = true - -# Print backtrace on internal compiler errors during bootstrap -#backtrace-on-ice = false - -# Whether to verify generated LLVM IR -#verify-llvm-ir = false - -# Compile the compiler with a non-default ThinLTO import limit. This import -# limit controls the maximum size of functions imported by ThinLTO. Decreasing -# will make code compile faster at the expense of lower runtime performance. -#thin-lto-import-instr-limit = if incremental { 10 } else { LLVM default (currently 100) } - -# Map debuginfo paths to `/rust/$sha/...`. -# Useful for reproducible builds. Generally only set for releases -#remap-debuginfo = false - -# Link the compiler and LLVM against `jemalloc` instead of the default libc allocator. -# This option is only tested on Linux and OSX. It can also be configured per-target in the -# [target.] section. -#jemalloc = false - -# Run tests in various test suites with the "nll compare mode" in addition to -# running the tests in normal mode. Largely only used on CI and during local -# development of NLL -#test-compare-mode = false - -# Global default for llvm-libunwind for all targets. See the target-specific -# documentation for llvm-libunwind below. Note that the target-specific -# option will override this if set. -#llvm-libunwind = 'no' - -# Enable Windows Control Flow Guard checks in the standard library. -# This only applies from stage 1 onwards, and only for Windows targets. -#control-flow-guard = false - -# Enable Windows EHCont Guard checks in the standard library. -# This only applies from stage 1 onwards, and only for Windows targets. -#ehcont-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). -# When no setting is given, the new scheme will be used when compiling the -# compiler and its tools and the legacy scheme will be used when compiling the -# standard library. -# If an explicit setting is given, it will be used for all parts of the codebase. -#new-symbol-mangling = true|false (see comment) - -# Select LTO mode that will be used for compiling rustc. By default, thin local LTO -# (LTO within a single crate) is used (like for any Rust crate). You can also select -# "thin" or "fat" to apply Thin/Fat LTO to the `rustc_driver` dylib, or "off" to disable -# LTO entirely. -#lto = "thin-local" - -# Build compiler with the optimization enabled and -Zvalidate-mir, currently only for `std` -#validate-mir-opts = 3 - -# Configure `std` features used during bootstrap. -# -# Default features will be expanded in the following cases: -# - If `rust.llvm-libunwind` or `target.llvm-libunwind` is enabled: -# - "llvm-libunwind" will be added for in-tree LLVM builds. -# - "system-llvm-libunwind" will be added for system LLVM builds. -# - If `rust.backtrace` is enabled, "backtrace" will be added. -# - If `rust.profiler` or `target.profiler` is enabled, "profiler" will be added. -# - If building for a zkvm target, "compiler-builtins-mem" will be added. -# -# Since libstd also builds libcore and liballoc as dependencies and all their features are mirrored -# as libstd features, this option can also be used to configure features such as optimize_for_size. -#std-features = ["panic_unwind"] - -# ============================================================================= -# Options for specific targets -# -# Each of the following options is scoped to the specific target triple in -# question and is used for determining how to compile each target. -# ============================================================================= -[target.x86_64-unknown-linux-gnu] - -# C compiler to be used to compile C 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. -# See `src/bootstrap/src/utils/cc_detect.rs` for details. -#cc = "cc" (path) - -# C++ compiler to be used to compile C++ code (e.g. LLVM and our LLVM shims). -# This is only used for host targets. -# See `src/bootstrap/src/utils/cc_detect.rs` for details. -#cxx = "c++" (path) - -# Archiver to be used to assemble static libraries compiled from C/C++ code. -# Note: an absolute path should be used, otherwise LLVM build will break. -#ar = "ar" (path) - -# Ranlib to be used to assemble static libraries compiled from C/C++ code. -# Note: an absolute path should be used, otherwise LLVM build will break. -#ranlib = "ranlib" (path) - -# Linker to be used to bootstrap 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 when targeting MSVC. -#linker = "cc" (path) - -# Should rustc and the standard library be built with split debuginfo? Default -# is platform dependent. -# -# Valid values are the same as those accepted by `-C split-debuginfo` -# (`off`/`unpacked`/`packed`). -# -# On Linux, split debuginfo is disabled by default. -# -# On Apple platforms, unpacked split debuginfo is used by default. Unpacked -# debuginfo does not run `dsymutil`, which packages debuginfo from disparate -# object files into a single `.dSYM` file. `dsymutil` adds time to builds for -# no clear benefit, and also makes it more difficult for debuggers to find -# debug info. The compiler currently defaults to running `dsymutil` to preserve -# its historical default, but when compiling the compiler itself, we skip it by -# default since we know it's safe to do so in that case. -# -# On Windows platforms, packed debuginfo is the only supported option, -# producing a `.pdb` file. -#split-debuginfo = if linux { off } else if windows { packed } else if apple { unpacked } - -# Path to the `llvm-config` binary of the installation of a custom LLVM to link -# against. Note that if this is specified we don't compile LLVM at all for this -# target. -#llvm-config = (path) - -# Override detection of whether this is a Rust-patched LLVM. This would be used -# in conjunction with either an llvm-config or build.submodules = false. -#llvm-has-rust-patches = if llvm-config { false } else { true } - -# Normally the build system can find LLVM's FileCheck utility, but if -# not, you can specify an explicit file name for it. -#llvm-filecheck = "/path/to/llvm-version/bin/FileCheck" - -# Use LLVM libunwind as the implementation for Rust's unwinder. -# Accepted values are 'in-tree' (formerly true), 'system' or 'no' (formerly false). -# This option only applies for Linux and Fuchsia targets. -# On Linux target, if crt-static is not enabled, 'no' means dynamic link to -# `libgcc_s.so`, 'in-tree' means static link to the in-tree build of llvm libunwind -# and 'system' means dynamic link to `libunwind.so`. If crt-static is enabled, -# the behavior is depend on the libc. On musl target, 'no' and 'in-tree' both -# means static link to the in-tree build of llvm libunwind, and 'system' means -# static link to `libunwind.a` provided by system. Due to the limitation of glibc, -# it must link to `libgcc_eh.a` to get a working output, and this option have no effect. -#llvm-libunwind = 'no' if Linux, 'in-tree' if Fuchsia - -# Build the sanitizer runtimes for this target. -# This option will override the same option under [build] section. -#sanitizers = build.sanitizers (bool) - -# When true, build the profiler runtime for this target (required when compiling -# with options that depend on this runtime, such as `-C profile-generate` or -# `-C instrument-coverage`). This may also be given a path to an existing build -# of the profiling runtime library from LLVM's compiler-rt. -# This option will override the same option under [build] section. -#profiler = build.profiler (bool) - -# This option supports enable `rpath` in each target independently, -# and will override the same option under [rust] section. It only works on Unix platforms -#rpath = rust.rpath (bool) - -# Force static or dynamic linkage of the standard library for this target. If -# this target is a host for rustc, this will also affect the linkage of the -# compiler itself. This is useful for building rustc on targets that normally -# only use static libraries. If unset, the target's default linkage is used. -#crt-static = (bool) - -# The root location of the musl installation directory. The library directory -# will also need to contain libunwind.a for an unwinding implementation. Note -# that this option only makes sense for musl targets that produce statically -# linked binaries. -#musl-root = build.musl-root (path) - -# The full path to the musl libdir. -#musl-libdir = musl-root/lib - -# The root location of the `wasm32-wasip1` sysroot. Only used for WASI -# related targets. Make sure to create a `[target.wasm32-wasip1]` -# section and move this field there (or equivalent for the target being built). -#wasi-root = (path) - -# Used in testing for configuring where the QEMU images are located, you -# probably don't want to use this. -#qemu-rootfs = (path) - -# Skip building the `std` library for this target. Enabled by default for -# target triples containing `-none`, `nvptx`, `switch`, or `-uefi`. -#no-std = (bool) - -# This is an array of the codegen backends that will be -# compiled for this target, overriding the global rust.codegen-backends option. -# See that option for more info. -#codegen-backends = rust.codegen-backends (array) - -# This is a "runner" to pass to `compiletest` when executing tests. Tests will -# execute this tool where the binary-to-test is passed as an argument. Can -# be useful for situations such as when WebAssembly is being tested and a -# runtime needs to be configured. This value is similar to -# Cargo's `CARGO_$target_RUNNER` configuration. -# -# This configuration is a space-separated list of arguments so `foo bar` would -# execute the program `foo` with the first argument as `bar` and the second -# argument as the test binary. -#runner = (string) - -# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics -# on this target. -# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt` -# sources are available. -# -# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in -# order to run `x check`. -#optimized-compiler-builtins = build.optimized-compiler-builtins (bool) - -# Link the compiler and LLVM against `jemalloc` instead of the default libc allocator. -# This overrides the global `rust.jemalloc` option. See that option for more info. -#jemalloc = rust.jemalloc (bool) - -# ============================================================================= -# Distribution options -# -# These options are related to distribution, mostly for the Rust project itself. -# You probably won't need to concern yourself with any of these options -# ============================================================================= -[dist] - -# This is the folder of artifacts that the build system will sign. All files in -# this directory will be signed with the default gpg key using the system `gpg` -# binary. The `asc` and `sha256` files will all be output into the standard dist -# output folder (currently `build/dist`) -# -# This folder should be populated ahead of time before the build system is -# invoked. -#sign-folder = (path) - -# The remote address that all artifacts will eventually be uploaded to. The -# build system generates manifests which will point to these urls, and for the -# manifests to be correct they'll have to have the right URLs encoded. -# -# Note that this address should not contain a trailing slash as file names will -# be appended to it. -#upload-addr = (URL) - -# Whether to build a plain source tarball to upload -# We disable that on Windows not to override the one already uploaded on S3 -# as the one built on Windows will contain backslashes in paths causing problems -# on linux -#src-tarball = true - -# List of compression formats to use when generating dist tarballs. The list of -# formats is provided to rust-installer, which must support all of them. -# -# This list must be non-empty. -#compression-formats = ["gz", "xz"] - -# How much time should be spent compressing the tarballs. The better the -# compression profile, the longer compression will take. -# -# Available options: fast, balanced, best -#compression-profile = "fast" - -# Copy the linker, DLLs, and various libraries from MinGW into the Rust toolchain. -# Only applies when the host or target is pc-windows-gnu. -#include-mingw-linker = true - -# Whether to vendor dependencies for the dist tarball. -#vendor = if "is a tarball source" || "is a git repository" { true } else { false } diff --git a/library/Cargo.lock b/library/Cargo.lock index de9685742f59f..02018057ed53e 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -30,15 +30,15 @@ version = "0.0.0" dependencies = [ "compiler_builtins", "core", - "rand", - "rand_xorshift", ] [[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +name = "alloctests" +version = "0.0.0" +dependencies = [ + "rand", + "rand_xorshift", +] [[package]] name = "cc" @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.148" +version = "0.1.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26137996631d90d2727b905b480fdcf8c4479fdbce7afd7f8e3796d689b33cc2" +checksum = "448068da8f2326b2a0472353cb401dd8795a89c007ef30fff90f50706e862e72" dependencies = [ "cc", "rustc-std-workspace-core", @@ -83,9 +83,9 @@ dependencies = [ [[package]] name = "dlmalloc" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5e0d321d61de16390ed273b647ce51605b575916d3c25e6ddf27a1e140035" +checksum = "8cff88b751e7a276c4ab0e222c3f355190adc6dde9ce39c851db39da34990df7" dependencies = [ "cfg-if", "compiler_builtins", @@ -128,11 +128,10 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ - "allocator-api2", "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -140,9 +139,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -151,9 +150,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" dependencies = [ "rustc-std-workspace-core", ] @@ -170,9 +169,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "compiler_builtins", @@ -197,7 +196,6 @@ name = "panic_abort" version = "0.0.0" dependencies = [ "alloc", - "cfg-if", "compiler_builtins", "core", "libc", @@ -215,20 +213,12 @@ dependencies = [ "unwind", ] -[[package]] -name = "proc-macro2" -version = "1.0.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" -dependencies = [ - "unicode-ident", -] - [[package]] name = "proc_macro" version = "0.0.0" dependencies = [ "core", + "rustc-literal-escaper", "std", ] @@ -239,20 +229,11 @@ dependencies = [ "cc", ] -[[package]] -name = "quote" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" -dependencies = [ - "proc-macro2", -] - [[package]] name = "r-efi" -version = "4.5.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e935efc5854715dfc0a4c9ef18dc69dee0ec3bf9cc3ab740db831c0fdd86a3" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -260,9 +241,9 @@ dependencies = [ [[package]] name = "r-efi-alloc" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7" +checksum = "e43c53ff1a01d423d1cb762fd991de07d32965ff0ca2e4f80444ac7804198203" dependencies = [ "compiler_builtins", "r-efi", @@ -271,22 +252,18 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_core", - "zerocopy", ] [[package]] name = "rand_core" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" -dependencies = [ - "zerocopy", -] +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" [[package]] name = "rand_xorshift" @@ -307,6 +284,15 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "rustc-literal-escaper" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04" +dependencies = [ + "rustc-std-workspace-std", +] + [[package]] name = "rustc-std-workspace-alloc" version = "1.99.0" @@ -374,17 +360,6 @@ dependencies = [ "rustc-std-workspace-core", ] -[[package]] -name = "syn" -version = "2.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "sysroot" version = "0.0.0" @@ -405,12 +380,6 @@ dependencies = [ "std", ] -[[package]] -name = "unicode-ident" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" - [[package]] name = "unicode-width" version = "0.1.14" @@ -435,9 +404,9 @@ dependencies = [ [[package]] name = "unwinding" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f06a05848f650946acef3bf525fe96612226b61f74ae23ffa4e98bfbb8ab3c" +checksum = "8393f2782b6060a807337ff353780c1ca15206f9ba2424df18cb6e733bd7b345" dependencies = [ "compiler_builtins", "gimli", @@ -531,23 +500,3 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "zerocopy" -version = "0.8.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/library/Cargo.toml b/library/Cargo.toml index 1205f7c9ed6b5..026ba1470081d 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -4,6 +4,7 @@ members = [ "std", "sysroot", "coretests", + "alloctests", ] exclude = [ @@ -37,8 +38,11 @@ adler2.debug = 0 gimli.debug = 0 gimli.opt-level = "s" miniz_oxide.debug = 0 +miniz_oxide.opt-level = "s" +# `opt-level = "s"` for `object` led to a size regression when tried previously object.debug = 0 rustc-demangle.debug = 0 +rustc-demangle.opt-level = "s" [patch.crates-io] # See comments in `library/rustc-std-workspace-core/README.md` for what's going on diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 9e80f3579e808..9d0d957226d2e 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -8,33 +8,15 @@ repository = "https://github.com/rust-lang/rust.git" description = "The Rust core allocation and collections library" autotests = false autobenches = false -edition = "2021" +edition = "2024" + +[lib] +test = false +bench = false [dependencies] core = { path = "../core", public = true } -compiler_builtins = { version = "=0.1.148", features = ['rustc-dep-of-std'] } - -[dev-dependencies] -rand = { version = "0.9.0", default-features = false, features = ["alloc"] } -rand_xorshift = "0.4.0" - -[[test]] -name = "alloctests" -path = "tests/lib.rs" - -[[test]] -name = "vec_deque_alloc_error" -path = "tests/vec_deque_alloc_error.rs" - -[[bench]] -name = "allocbenches" -path = "benches/lib.rs" -test = true - -[[bench]] -name = "vec_deque_append_bench" -path = "benches/vec_deque_append.rs" -harness = false +compiler_builtins = { version = "=0.1.159", features = ['rustc-dep-of-std'] } [features] compiler-builtins-mem = ['compiler_builtins/mem'] @@ -54,5 +36,4 @@ check-cfg = [ 'cfg(no_global_oom_handling)', 'cfg(no_rc)', 'cfg(no_sync)', - 'cfg(randomized_layouts)', ] diff --git a/library/alloc/benches/slice.rs b/library/alloc/benches/slice.rs deleted file mode 100644 index c6b46e6a2a188..0000000000000 --- a/library/alloc/benches/slice.rs +++ /dev/null @@ -1,390 +0,0 @@ -use std::{mem, ptr}; - -use rand::Rng; -use rand::distr::{Alphanumeric, SampleString, StandardUniform}; -use test::{Bencher, black_box}; - -#[bench] -fn iterator(b: &mut Bencher) { - // peculiar numbers to stop LLVM from optimising the summation - // out. - let v: Vec<_> = (0..100).map(|i| i ^ (i << 1) ^ (i >> 1)).collect(); - - b.iter(|| { - let mut sum = 0; - for x in &v { - sum += *x; - } - // sum == 11806, to stop dead code elimination. - if sum == 0 { - panic!() - } - }) -} - -#[bench] -fn mut_iterator(b: &mut Bencher) { - let mut v = vec![0; 100]; - - b.iter(|| { - let mut i = 0; - for x in &mut v { - *x = i; - i += 1; - } - }) -} - -#[bench] -fn concat(b: &mut Bencher) { - let xss: Vec> = (0..100).map(|i| (0..i).collect()).collect(); - b.iter(|| { - xss.concat(); - }); -} - -#[bench] -fn join(b: &mut Bencher) { - let xss: Vec> = (0..100).map(|i| (0..i).collect()).collect(); - b.iter(|| xss.join(&0)); -} - -#[bench] -fn push(b: &mut Bencher) { - let mut vec = Vec::::new(); - b.iter(|| { - vec.push(0); - black_box(&vec); - }); -} - -#[bench] -fn starts_with_same_vector(b: &mut Bencher) { - let vec: Vec<_> = (0..100).collect(); - b.iter(|| vec.starts_with(&vec)) -} - -#[bench] -fn starts_with_single_element(b: &mut Bencher) { - let vec: Vec<_> = vec![0]; - b.iter(|| vec.starts_with(&vec)) -} - -#[bench] -fn starts_with_diff_one_element_at_end(b: &mut Bencher) { - let vec: Vec<_> = (0..100).collect(); - let mut match_vec: Vec<_> = (0..99).collect(); - match_vec.push(0); - b.iter(|| vec.starts_with(&match_vec)) -} - -#[bench] -fn ends_with_same_vector(b: &mut Bencher) { - let vec: Vec<_> = (0..100).collect(); - b.iter(|| vec.ends_with(&vec)) -} - -#[bench] -fn ends_with_single_element(b: &mut Bencher) { - let vec: Vec<_> = vec![0]; - b.iter(|| vec.ends_with(&vec)) -} - -#[bench] -fn ends_with_diff_one_element_at_beginning(b: &mut Bencher) { - let vec: Vec<_> = (0..100).collect(); - let mut match_vec: Vec<_> = (0..100).collect(); - match_vec[0] = 200; - b.iter(|| vec.starts_with(&match_vec)) -} - -#[bench] -fn contains_last_element(b: &mut Bencher) { - let vec: Vec<_> = (0..100).collect(); - b.iter(|| vec.contains(&99)) -} - -#[bench] -fn zero_1kb_from_elem(b: &mut Bencher) { - b.iter(|| vec![0u8; 1024]); -} - -#[bench] -fn zero_1kb_set_memory(b: &mut Bencher) { - b.iter(|| { - let mut v = Vec::::with_capacity(1024); - unsafe { - let vp = v.as_mut_ptr(); - ptr::write_bytes(vp, 0, 1024); - v.set_len(1024); - } - v - }); -} - -#[bench] -fn zero_1kb_loop_set(b: &mut Bencher) { - b.iter(|| { - let mut v = Vec::::with_capacity(1024); - unsafe { - v.set_len(1024); - } - for i in 0..1024 { - v[i] = 0; - } - }); -} - -#[bench] -fn zero_1kb_mut_iter(b: &mut Bencher) { - b.iter(|| { - let mut v = Vec::::with_capacity(1024); - unsafe { - v.set_len(1024); - } - for x in &mut v { - *x = 0; - } - v - }); -} - -#[bench] -fn random_inserts(b: &mut Bencher) { - let mut rng = crate::bench_rng(); - b.iter(|| { - let mut v = vec![(0, 0); 30]; - for _ in 0..100 { - let l = v.len(); - v.insert(rng.random::() as usize % (l + 1), (1, 1)); - } - }) -} - -#[bench] -fn random_removes(b: &mut Bencher) { - let mut rng = crate::bench_rng(); - b.iter(|| { - let mut v = vec![(0, 0); 130]; - for _ in 0..100 { - let l = v.len(); - v.remove(rng.random::() as usize % l); - } - }) -} - -fn gen_ascending(len: usize) -> Vec { - (0..len as u64).collect() -} - -fn gen_descending(len: usize) -> Vec { - (0..len as u64).rev().collect() -} - -fn gen_random(len: usize) -> Vec { - let mut rng = crate::bench_rng(); - (&mut rng).sample_iter(&StandardUniform).take(len).collect() -} - -fn gen_random_bytes(len: usize) -> Vec { - let mut rng = crate::bench_rng(); - (&mut rng).sample_iter(&StandardUniform).take(len).collect() -} - -fn gen_mostly_ascending(len: usize) -> Vec { - let mut rng = crate::bench_rng(); - let mut v = gen_ascending(len); - for _ in (0usize..).take_while(|x| x * x <= len) { - let x = rng.random::() as usize % len; - let y = rng.random::() as usize % len; - v.swap(x, y); - } - v -} - -fn gen_mostly_descending(len: usize) -> Vec { - let mut rng = crate::bench_rng(); - let mut v = gen_descending(len); - for _ in (0usize..).take_while(|x| x * x <= len) { - let x = rng.random::() as usize % len; - let y = rng.random::() as usize % len; - v.swap(x, y); - } - v -} - -fn gen_strings(len: usize) -> Vec { - let mut rng = crate::bench_rng(); - let mut v = vec![]; - for _ in 0..len { - let n = rng.random::() % 20 + 1; - v.push(Alphanumeric.sample_string(&mut rng, n as usize)); - } - v -} - -fn gen_big_random(len: usize) -> Vec<[u64; 16]> { - let mut rng = crate::bench_rng(); - (&mut rng).sample_iter(&StandardUniform).map(|x| [x; 16]).take(len).collect() -} - -macro_rules! sort { - ($f:ident, $name:ident, $gen:expr, $len:expr) => { - #[bench] - fn $name(b: &mut Bencher) { - let v = $gen($len); - b.iter(|| v.clone().$f()); - b.bytes = $len * mem::size_of_val(&$gen(1)[0]) as u64; - } - }; -} - -macro_rules! sort_strings { - ($f:ident, $name:ident, $gen:expr, $len:expr) => { - #[bench] - fn $name(b: &mut Bencher) { - let v = $gen($len); - let v = v.iter().map(|s| &**s).collect::>(); - b.iter(|| v.clone().$f()); - b.bytes = $len * mem::size_of::<&str>() as u64; - } - }; -} - -macro_rules! sort_expensive { - ($f:ident, $name:ident, $gen:expr, $len:expr) => { - #[bench] - fn $name(b: &mut Bencher) { - let v = $gen($len); - b.iter(|| { - let mut v = v.clone(); - let mut count = 0; - v.$f(|a: &u64, b: &u64| { - count += 1; - if count % 1_000_000_000 == 0 { - panic!("should not happen"); - } - (*a as f64).cos().partial_cmp(&(*b as f64).cos()).unwrap() - }); - black_box(count); - }); - b.bytes = $len * mem::size_of_val(&$gen(1)[0]) as u64; - } - }; -} - -macro_rules! sort_lexicographic { - ($f:ident, $name:ident, $gen:expr, $len:expr) => { - #[bench] - fn $name(b: &mut Bencher) { - let v = $gen($len); - b.iter(|| v.clone().$f(|x| x.to_string())); - b.bytes = $len * mem::size_of_val(&$gen(1)[0]) as u64; - } - }; -} - -sort!(sort, sort_small_ascending, gen_ascending, 10); -sort!(sort, sort_small_descending, gen_descending, 10); -sort!(sort, sort_small_random, gen_random, 10); -sort!(sort, sort_small_big, gen_big_random, 10); -sort!(sort, sort_medium_random, gen_random, 100); -sort!(sort, sort_large_ascending, gen_ascending, 10000); -sort!(sort, sort_large_descending, gen_descending, 10000); -sort!(sort, sort_large_mostly_ascending, gen_mostly_ascending, 10000); -sort!(sort, sort_large_mostly_descending, gen_mostly_descending, 10000); -sort!(sort, sort_large_random, gen_random, 10000); -sort!(sort, sort_large_big, gen_big_random, 10000); -sort_strings!(sort, sort_large_strings, gen_strings, 10000); -sort_expensive!(sort_by, sort_large_expensive, gen_random, 10000); - -sort!(sort_unstable, sort_unstable_small_ascending, gen_ascending, 10); -sort!(sort_unstable, sort_unstable_small_descending, gen_descending, 10); -sort!(sort_unstable, sort_unstable_small_random, gen_random, 10); -sort!(sort_unstable, sort_unstable_small_big, gen_big_random, 10); -sort!(sort_unstable, sort_unstable_medium_random, gen_random, 100); -sort!(sort_unstable, sort_unstable_large_ascending, gen_ascending, 10000); -sort!(sort_unstable, sort_unstable_large_descending, gen_descending, 10000); -sort!(sort_unstable, sort_unstable_large_mostly_ascending, gen_mostly_ascending, 10000); -sort!(sort_unstable, sort_unstable_large_mostly_descending, gen_mostly_descending, 10000); -sort!(sort_unstable, sort_unstable_large_random, gen_random, 10000); -sort!(sort_unstable, sort_unstable_large_big, gen_big_random, 10000); -sort_strings!(sort_unstable, sort_unstable_large_strings, gen_strings, 10000); -sort_expensive!(sort_unstable_by, sort_unstable_large_expensive, gen_random, 10000); - -sort_lexicographic!(sort_by_key, sort_by_key_lexicographic, gen_random, 10000); -sort_lexicographic!(sort_unstable_by_key, sort_unstable_by_key_lexicographic, gen_random, 10000); -sort_lexicographic!(sort_by_cached_key, sort_by_cached_key_lexicographic, gen_random, 10000); - -macro_rules! reverse { - ($name:ident, $ty:ty, $f:expr) => { - #[bench] - fn $name(b: &mut Bencher) { - // odd length and offset by 1 to be as unaligned as possible - let n = 0xFFFFF; - let mut v: Vec<_> = (0..1 + (n / mem::size_of::<$ty>() as u64)).map($f).collect(); - b.iter(|| black_box(&mut v[1..]).reverse()); - b.bytes = n; - } - }; -} - -reverse!(reverse_u8, u8, |x| x as u8); -reverse!(reverse_u16, u16, |x| x as u16); -reverse!(reverse_u8x3, [u8; 3], |x| [x as u8, (x >> 8) as u8, (x >> 16) as u8]); -reverse!(reverse_u32, u32, |x| x as u32); -reverse!(reverse_u64, u64, |x| x as u64); -reverse!(reverse_u128, u128, |x| x as u128); -#[repr(simd)] -struct F64x4([f64; 4]); -reverse!(reverse_simd_f64x4, F64x4, |x| { - let x = x as f64; - F64x4([x, x, x, x]) -}); - -macro_rules! rotate { - ($name:ident, $gen:expr, $len:expr, $mid:expr) => { - #[bench] - fn $name(b: &mut Bencher) { - let size = mem::size_of_val(&$gen(1)[0]); - let mut v = $gen($len * 8 / size); - b.iter(|| black_box(&mut v).rotate_left(($mid * 8 + size - 1) / size)); - b.bytes = (v.len() * size) as u64; - } - }; -} - -rotate!(rotate_tiny_by1, gen_random, 16, 1); -rotate!(rotate_tiny_half, gen_random, 16, 16 / 2); -rotate!(rotate_tiny_half_plus_one, gen_random, 16, 16 / 2 + 1); - -rotate!(rotate_medium_by1, gen_random, 9158, 1); -rotate!(rotate_medium_by727_u64, gen_random, 9158, 727); -rotate!(rotate_medium_by727_bytes, gen_random_bytes, 9158, 727); -rotate!(rotate_medium_by727_strings, gen_strings, 9158, 727); -rotate!(rotate_medium_half, gen_random, 9158, 9158 / 2); -rotate!(rotate_medium_half_plus_one, gen_random, 9158, 9158 / 2 + 1); - -// Intended to use more RAM than the machine has cache -#[cfg(not(target_os = "emscripten"))] // hits an OOM -rotate!(rotate_huge_by1, gen_random, 5 * 1024 * 1024, 1); -#[cfg(not(target_os = "emscripten"))] // hits an OOM -rotate!(rotate_huge_by9199_u64, gen_random, 5 * 1024 * 1024, 9199); -#[cfg(not(target_os = "emscripten"))] // hits an OOM -rotate!(rotate_huge_by9199_bytes, gen_random_bytes, 5 * 1024 * 1024, 9199); -#[cfg(not(target_os = "emscripten"))] // hits an OOM -rotate!(rotate_huge_by9199_strings, gen_strings, 5 * 1024 * 1024, 9199); -#[cfg(not(target_os = "emscripten"))] // hits an OOM -rotate!(rotate_huge_by9199_big, gen_big_random, 5 * 1024 * 1024, 9199); -#[cfg(not(target_os = "emscripten"))] // hits an OOM -rotate!(rotate_huge_by1234577_u64, gen_random, 5 * 1024 * 1024, 1234577); -#[cfg(not(target_os = "emscripten"))] // hits an OOM -rotate!(rotate_huge_by1234577_bytes, gen_random_bytes, 5 * 1024 * 1024, 1234577); -#[cfg(not(target_os = "emscripten"))] // hits an OOM -rotate!(rotate_huge_by1234577_strings, gen_strings, 5 * 1024 * 1024, 1234577); -#[cfg(not(target_os = "emscripten"))] // hits an OOM -rotate!(rotate_huge_by1234577_big, gen_big_random, 5 * 1024 * 1024, 1234577); -#[cfg(not(target_os = "emscripten"))] // hits an OOM -rotate!(rotate_huge_half, gen_random, 5 * 1024 * 1024, 5 * 1024 * 1024 / 2); -#[cfg(not(target_os = "emscripten"))] // hits an OOM -rotate!(rotate_huge_half_plus_one, gen_random, 5 * 1024 * 1024, 5 * 1024 * 1024 / 2 + 1); diff --git a/library/alloc/benches/vec.rs b/library/alloc/benches/vec.rs deleted file mode 100644 index a725ad6894b9c..0000000000000 --- a/library/alloc/benches/vec.rs +++ /dev/null @@ -1,878 +0,0 @@ -use std::iter::repeat; - -use rand::RngCore; -use test::{Bencher, black_box}; - -#[bench] -fn bench_new(b: &mut Bencher) { - b.iter(|| Vec::::new()) -} - -fn do_bench_with_capacity(b: &mut Bencher, src_len: usize) { - b.bytes = src_len as u64; - - b.iter(|| Vec::::with_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(|| (0..src_len).collect::>()) -} - -#[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(|| repeat(5).take(src_len).collect::>()) -} - -#[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(|| src.as_slice().to_vec()); -} - -#[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.iter().cloned()); - 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()); - 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); - dst - }); -} - -#[bench] -fn bench_extend_recycle(b: &mut Bencher) { - let mut data = vec![0; 1000]; - - b.iter(|| { - let tmp = std::mem::take(&mut data); - 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(|| src.clone()); -} - -#[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); - dst = black_box(dst); - } - 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]); - src.into_iter() - .enumerate() - .map(|(idx, e)| idx as $type ^ e) - .collect::>() - }); - } - )+ - }; -} - -bench_in_place![ - bench_in_place_xxu8_0010_i0, u8, 10, 0; - bench_in_place_xxu8_0100_i0, u8, 100, 0; - bench_in_place_xxu8_1000_i0, u8, 1000, 0; - bench_in_place_xxu8_0010_i1, u8, 10, 1; - bench_in_place_xxu8_0100_i1, u8, 100, 1; - bench_in_place_xxu8_1000_i1, u8, 1000, 1; - bench_in_place_xu32_0010_i0, u32, 10, 0; - bench_in_place_xu32_0100_i0, u32, 100, 0; - bench_in_place_xu32_1000_i0, u32, 1000, 0; - bench_in_place_xu32_0010_i1, u32, 10, 1; - bench_in_place_xu32_0100_i1, u32, 100, 1; - bench_in_place_xu32_1000_i1, u32, 1000, 1; - bench_in_place_u128_0010_i0, u128, 10, 0; - bench_in_place_u128_0100_i0, u128, 100, 0; - bench_in_place_u128_1000_i0, u128, 1000, 0; - bench_in_place_u128_0010_i1, u128, 10, 1; - bench_in_place_u128_0100_i1, u128, 100, 1; - bench_in_place_u128_1000_i1, u128, 1000, 1 -]; - -#[bench] -fn bench_in_place_recycle(b: &mut Bencher) { - let mut data = vec![0; 1000]; - - b.iter(|| { - let tmp = std::mem::take(&mut data); - data = black_box( - tmp.into_iter() - .enumerate() - .map(|(idx, e)| idx.wrapping_add(e)) - .fuse() - .collect::>(), - ); - }); -} - -#[bench] -fn bench_in_place_zip_recycle(b: &mut Bencher) { - let mut data = vec![0u8; 1000]; - let mut rng = crate::bench_rng(); - let mut subst = vec![0u8; 1000]; - rng.fill_bytes(&mut subst[..]); - - b.iter(|| { - let tmp = std::mem::take(&mut data); - let mangled = tmp - .into_iter() - .zip(subst.iter().copied()) - .enumerate() - .map(|(i, (d, s))| d.wrapping_add(i as u8) ^ s) - .collect::>(); - data = black_box(mangled); - }); -} - -#[bench] -fn bench_in_place_zip_iter_mut(b: &mut Bencher) { - let mut data = vec![0u8; 256]; - let mut rng = crate::bench_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); -} - -pub fn vec_cast(input: Vec) -> Vec { - input.into_iter().map(|e| unsafe { std::mem::transmute_copy(&e) }).collect() -} - -#[bench] -fn bench_transmute(b: &mut Bencher) { - let mut vec = vec![10u32; 100]; - b.bytes = 800; // 2 casts x 4 bytes x 100 - b.iter(|| { - let v = std::mem::take(&mut vec); - let v = black_box(vec_cast::(v)); - let v = black_box(vec_cast::(v)); - vec = v; - }); -} - -#[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 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::>() - }) -} - -// node.js gives out of memory error to use with length 1_100_000 -#[cfg(target_os = "emscripten")] -const LEN: usize = 4096; - -#[cfg(not(target_os = "emscripten"))] -const LEN: usize = 16384; - -#[bench] -fn bench_chain_collect(b: &mut Bencher) { - let data = black_box([0; LEN]); - b.iter(|| data.iter().cloned().chain([1]).collect::>()); -} - -#[bench] -fn bench_chain_chain_collect(b: &mut Bencher) { - let data = black_box([0; LEN]); - b.iter(|| data.iter().cloned().chain([1]).chain([2]).collect::>()); -} - -#[bench] -fn bench_nest_chain_chain_collect(b: &mut Bencher) { - let data = black_box([0; LEN]); - b.iter(|| { - data.iter().cloned().chain([1].iter().chain([2].iter()).cloned()).collect::>() - }); -} - -#[bench] -fn bench_range_map_collect(b: &mut Bencher) { - b.iter(|| (0..LEN).map(|_| u32::default()).collect::>()); -} - -#[bench] -fn bench_chain_extend_ref(b: &mut 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 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 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 Bencher) { - let data = black_box([0; LEN]); - b.iter(|| { - let mut v = Vec::::with_capacity(data.len()); - v.extend(data.iter().rev()); - v - }); -} - -#[bench] -fn bench_map_regular(b: &mut 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 Bencher) { - let data = black_box([(0, 0); LEN]); - b.iter(|| { - let mut result: Vec = Vec::with_capacity(data.len()); - for i in 0..data.len() { - unsafe { - *result.as_mut_ptr().add(i) = data[i].0; - result.set_len(i); - } - } - result - }); -} - -fn random_sorted_fill(mut seed: u32, buf: &mut [u32]) { - let mask = if buf.len() < 8192 { - 0xFF - } else if buf.len() < 200_000 { - 0xFFFF - } else { - 0xFFFF_FFFF - }; - - for item in buf.iter_mut() { - seed ^= seed << 13; - seed ^= seed >> 17; - seed ^= seed << 5; - - *item = seed & mask; - } - - buf.sort(); -} - -// Measures performance of slice dedup impl. -// This was used to justify separate implementation of dedup for Vec. -// This algorithm was used for Vecs prior to Rust 1.52. -fn bench_dedup_slice_truncate(b: &mut Bencher, sz: usize) { - let mut template = vec![0u32; sz]; - b.bytes = std::mem::size_of_val(template.as_slice()) as u64; - random_sorted_fill(0x43, &mut template); - - let mut vec = template.clone(); - b.iter(|| { - let vec = black_box(&mut vec); - let len = { - let (dedup, _) = vec.partition_dedup(); - dedup.len() - }; - vec.truncate(len); - - black_box(vec.first()); - let vec = black_box(vec); - vec.clear(); - vec.extend_from_slice(&template); - }); -} - -// Measures performance of Vec::dedup on random data. -fn bench_vec_dedup_random(b: &mut Bencher, sz: usize) { - let mut template = vec![0u32; sz]; - b.bytes = std::mem::size_of_val(template.as_slice()) as u64; - random_sorted_fill(0x43, &mut template); - - let mut vec = template.clone(); - b.iter(|| { - let vec = black_box(&mut vec); - vec.dedup(); - black_box(vec.first()); - let vec = black_box(vec); - vec.clear(); - vec.extend_from_slice(&template); - }); -} - -// Measures performance of Vec::dedup when there is no items removed -fn bench_vec_dedup_none(b: &mut Bencher, sz: usize) { - let mut template = vec![0u32; sz]; - b.bytes = std::mem::size_of_val(template.as_slice()) as u64; - template.chunks_exact_mut(2).for_each(|w| { - w[0] = black_box(0); - w[1] = black_box(5); - }); - - let mut vec = template.clone(); - b.iter(|| { - let vec = black_box(&mut vec); - vec.dedup(); - black_box(vec.first()); - // Unlike other benches of `dedup` - // this doesn't reinitialize vec - // because we measure how efficient dedup is - // when no memory written - }); -} - -// Measures performance of Vec::dedup when there is all items removed -fn bench_vec_dedup_all(b: &mut Bencher, sz: usize) { - let mut template = vec![0u32; sz]; - b.bytes = std::mem::size_of_val(template.as_slice()) as u64; - template.iter_mut().for_each(|w| { - *w = black_box(0); - }); - - let mut vec = template.clone(); - b.iter(|| { - let vec = black_box(&mut vec); - vec.dedup(); - black_box(vec.first()); - let vec = black_box(vec); - vec.clear(); - vec.extend_from_slice(&template); - }); -} - -#[bench] -fn bench_dedup_slice_truncate_100(b: &mut Bencher) { - bench_dedup_slice_truncate(b, 100); -} -#[bench] -fn bench_dedup_random_100(b: &mut Bencher) { - bench_vec_dedup_random(b, 100); -} - -#[bench] -fn bench_dedup_none_100(b: &mut Bencher) { - bench_vec_dedup_none(b, 100); -} - -#[bench] -fn bench_dedup_all_100(b: &mut Bencher) { - bench_vec_dedup_all(b, 100); -} - -#[bench] -fn bench_dedup_slice_truncate_1000(b: &mut Bencher) { - bench_dedup_slice_truncate(b, 1000); -} -#[bench] -fn bench_dedup_random_1000(b: &mut Bencher) { - bench_vec_dedup_random(b, 1000); -} - -#[bench] -fn bench_dedup_none_1000(b: &mut Bencher) { - bench_vec_dedup_none(b, 1000); -} - -#[bench] -fn bench_dedup_all_1000(b: &mut Bencher) { - bench_vec_dedup_all(b, 1000); -} - -#[bench] -fn bench_dedup_slice_truncate_10000(b: &mut Bencher) { - bench_dedup_slice_truncate(b, 10000); -} -#[bench] -fn bench_dedup_random_10000(b: &mut Bencher) { - bench_vec_dedup_random(b, 10000); -} - -#[bench] -fn bench_dedup_none_10000(b: &mut Bencher) { - bench_vec_dedup_none(b, 10000); -} - -#[bench] -fn bench_dedup_all_10000(b: &mut Bencher) { - bench_vec_dedup_all(b, 10000); -} - -#[bench] -fn bench_dedup_slice_truncate_100000(b: &mut Bencher) { - bench_dedup_slice_truncate(b, 100000); -} -#[bench] -fn bench_dedup_random_100000(b: &mut Bencher) { - bench_vec_dedup_random(b, 100000); -} - -#[bench] -fn bench_dedup_none_100000(b: &mut Bencher) { - bench_vec_dedup_none(b, 100000); -} - -#[bench] -fn bench_dedup_all_100000(b: &mut Bencher) { - bench_vec_dedup_all(b, 100000); -} - -#[bench] -fn bench_flat_map_collect(b: &mut Bencher) { - let v = vec![777u32; 500000]; - b.iter(|| v.iter().flat_map(|color| color.rotate_left(8).to_be_bytes()).collect::>()); -} - -/// Reference benchmark that `retain` has to compete with. -#[bench] -fn bench_retain_iter_100000(b: &mut Bencher) { - let mut v = Vec::with_capacity(100000); - - b.iter(|| { - let mut tmp = std::mem::take(&mut v); - tmp.clear(); - tmp.extend(black_box(1..=100000)); - v = tmp.into_iter().filter(|x| x & 1 == 0).collect(); - }); -} - -#[bench] -fn bench_retain_100000(b: &mut Bencher) { - let mut v = Vec::with_capacity(100000); - - b.iter(|| { - v.clear(); - v.extend(black_box(1..=100000)); - v.retain(|x| x & 1 == 0) - }); -} - -#[bench] -fn bench_retain_whole_100000(b: &mut Bencher) { - let mut v = black_box(vec![826u32; 100000]); - b.iter(|| v.retain(|x| *x == 826u32)); -} - -#[bench] -fn bench_next_chunk(b: &mut Bencher) { - let v = vec![13u8; 2048]; - - b.iter(|| { - const CHUNK: usize = 8; - - let mut sum = [0u32; CHUNK]; - let mut iter = black_box(v.clone()).into_iter(); - - while let Ok(chunk) = iter.next_chunk::() { - for i in 0..CHUNK { - sum[i] += chunk[i] as u32; - } - } - - sum - }) -} diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 3bfdc68dcdaf6..e1cc4ba25c4ea 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -5,32 +5,33 @@ #[stable(feature = "alloc_module", since = "1.28.0")] #[doc(inline)] pub use core::alloc::*; -#[cfg(not(test))] use core::hint; -#[cfg(not(test))] use core::ptr::{self, NonNull}; unsafe extern "Rust" { // These are the magic symbols to call the global allocator. rustc generates - // them to call `__rg_alloc` etc. if there is a `#[global_allocator]` attribute + // them to call the global allocator if there is a `#[global_allocator]` attribute // (the code expanding that attribute macro generates those functions), or to call // the default implementations in std (`__rdl_alloc` etc. in `library/std/src/alloc.rs`) // otherwise. - // The rustc fork of LLVM 14 and earlier also special-cases these function names to be able to optimize them - // like `malloc`, `realloc`, and `free`, respectively. #[rustc_allocator] #[rustc_nounwind] + #[rustc_std_internal_symbol] fn __rust_alloc(size: usize, align: usize) -> *mut u8; #[rustc_deallocator] #[rustc_nounwind] + #[rustc_std_internal_symbol] fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); #[rustc_reallocator] #[rustc_nounwind] + #[rustc_std_internal_symbol] fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8; #[rustc_allocator_zeroed] #[rustc_nounwind] + #[rustc_std_internal_symbol] fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; + #[rustc_std_internal_symbol] static __rust_no_alloc_shim_is_unstable: u8; } @@ -44,14 +45,10 @@ unsafe extern "Rust" { /// accessed through the [free functions in `alloc`](self#functions). #[unstable(feature = "allocator_api", issue = "32838")] #[derive(Copy, Clone, Default, Debug)] -#[cfg(not(test))] // the compiler needs to know when a Box uses the global allocator vs a custom one #[lang = "global_alloc_ty"] pub struct Global; -#[cfg(test)] -pub use std::alloc::Global; - /// Allocates memory with the global allocator. /// /// This function forwards calls to the [`GlobalAlloc::alloc`] method @@ -180,7 +177,6 @@ pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { } } -#[cfg(not(test))] impl Global { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -246,7 +242,6 @@ impl Global { } #[unstable(feature = "allocator_api", issue = "32838")] -#[cfg(not(test))] unsafe impl Allocator for Global { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -346,7 +341,7 @@ unsafe impl Allocator for Global { } /// The allocator for `Box`. -#[cfg(all(not(no_global_oom_handling), not(test)))] +#[cfg(not(no_global_oom_handling))] #[lang = "exchange_malloc"] #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -365,6 +360,7 @@ unsafe extern "Rust" { // This is the magic symbol to call the global alloc error handler. rustc generates // it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the // default implementations below (`__rdl_oom`) otherwise. + #[rustc_std_internal_symbol] fn __rust_alloc_error_handler(size: usize, align: usize) -> !; } @@ -395,7 +391,7 @@ unsafe extern "Rust" { /// [no_std]: https://doc.rust-lang.org/reference/names/preludes.html#the-no_std-attribute #[stable(feature = "global_alloc", since = "1.28.0")] #[rustc_const_unstable(feature = "const_alloc_error", issue = "92523")] -#[cfg(all(not(no_global_oom_handling), not(test)))] +#[cfg(not(no_global_oom_handling))] #[cold] #[optimize(size)] pub const fn handle_alloc_error(layout: Layout) -> ! { @@ -419,11 +415,7 @@ pub const fn handle_alloc_error(layout: Layout) -> ! { ct_error(layout) } -// For alloc test `std::alloc::handle_alloc_error` can be used directly. -#[cfg(all(not(no_global_oom_handling), test))] -pub use std::alloc::handle_alloc_error; - -#[cfg(all(not(no_global_oom_handling), not(test)))] +#[cfg(not(no_global_oom_handling))] #[doc(hidden)] #[allow(unused_attributes)] #[unstable(feature = "alloc_internals", issue = "none")] @@ -435,6 +427,7 @@ pub mod __alloc_error_handler { unsafe extern "Rust" { // This symbol is emitted by rustc next to __rust_alloc_error_handler. // Its value depends on the -Zoom={panic,abort} compiler option. + #[rustc_std_internal_symbol] static __rust_alloc_error_handler_should_panic: u8; } diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index 17dad3277b95d..07f51b7614ff8 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -32,7 +32,7 @@ where /// 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. -#[cfg_attr(not(test), rustc_diagnostic_item = "ToOwned")] +#[rustc_diagnostic_item = "ToOwned"] #[stable(feature = "rust1", since = "1.0.0")] pub trait ToOwned { /// The resulting type after obtaining ownership. @@ -54,7 +54,7 @@ pub trait ToOwned { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "cloning is often expensive and is not expected to have side effects"] - #[cfg_attr(not(test), rustc_diagnostic_item = "to_owned_method")] + #[rustc_diagnostic_item = "to_owned_method"] fn to_owned(&self) -> Self::Owned; /// Uses borrowed data to replace owned data, usually by cloning. @@ -175,7 +175,7 @@ where /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "Cow")] +#[rustc_diagnostic_item = "Cow"] pub enum Cow<'a, B: ?Sized + 'a> where B: ToOwned, diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index c3f5806e1aa90..4536f55544354 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -239,7 +239,7 @@ pub struct Box< /// This is the surface syntax for `box ` expressions. #[rustc_intrinsic] #[unstable(feature = "liballoc_internals", issue = "none")] -pub fn box_new(_x: T) -> Box; +pub fn box_new(x: T) -> Box; impl Box { /// Allocates memory on the heap and then places `x` into it. @@ -937,8 +937,6 @@ impl Box, A> { /// # Examples /// /// ``` - /// #![feature(box_uninit_write)] - /// /// let big_box = Box::<[usize; 1024]>::new_uninit(); /// /// let mut array = [0; 1024]; @@ -954,7 +952,7 @@ impl Box, A> { /// assert_eq!(*x, i); /// } /// ``` - #[unstable(feature = "box_uninit_write", issue = "129397")] + #[stable(feature = "box_uninit_write", since = "1.87.0")] #[inline] pub fn write(mut boxed: Self, value: T) -> Box { unsafe { @@ -1151,9 +1149,8 @@ impl Box { /// /// [memory layout]: self#memory-layout #[unstable(feature = "allocator_api", issue = "32838")] - #[rustc_const_unstable(feature = "const_box", issue = "92521")] #[inline] - pub const unsafe fn from_raw_in(raw: *mut T, alloc: A) -> Self { + pub unsafe fn from_raw_in(raw: *mut T, alloc: A) -> Self { Box(unsafe { Unique::new_unchecked(raw) }, alloc) } @@ -1205,9 +1202,8 @@ impl Box { /// [memory layout]: self#memory-layout #[unstable(feature = "allocator_api", issue = "32838")] // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] - #[rustc_const_unstable(feature = "const_box", issue = "92521")] #[inline] - pub const unsafe fn from_non_null_in(raw: NonNull, alloc: A) -> Self { + pub unsafe fn from_non_null_in(raw: NonNull, alloc: A) -> Self { // SAFETY: guaranteed by the caller. unsafe { Box::from_raw_in(raw.as_ptr(), alloc) } } @@ -1552,9 +1548,8 @@ impl Box { /// to call it as `Box::allocator(&b)` instead of `b.allocator()`. This /// is so that there is no conflict with a method on the inner type. #[unstable(feature = "allocator_api", issue = "32838")] - #[rustc_const_unstable(feature = "const_box", issue = "92521")] #[inline] - pub const fn allocator(b: &Self) -> &A { + pub fn allocator(b: &Self) -> &A { &b.1 } @@ -1641,8 +1636,7 @@ impl Box { /// let bar = Pin::from(foo); /// ``` #[stable(feature = "box_into_pin", since = "1.63.0")] - #[rustc_const_unstable(feature = "const_box", issue = "92521")] - pub const fn into_pin(boxed: Self) -> Pin + pub fn into_pin(boxed: Self) -> Pin where A: 'static, { diff --git a/library/alloc/src/boxed/convert.rs b/library/alloc/src/boxed/convert.rs index 255cefb1e78fb..8062658020239 100644 --- a/library/alloc/src/boxed/convert.rs +++ b/library/alloc/src/boxed/convert.rs @@ -529,7 +529,6 @@ impl<'a, E: Error + 'a> From for Box { /// ``` /// use std::error::Error; /// use std::fmt; - /// use std::mem; /// /// #[derive(Debug)] /// struct AnError; @@ -543,9 +542,9 @@ impl<'a, E: Error + 'a> From for Box { /// impl Error for AnError {} /// /// let an_error = AnError; - /// assert!(0 == mem::size_of_val(&an_error)); + /// assert!(0 == size_of_val(&an_error)); /// let a_boxed_error = Box::::from(an_error); - /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// assert!(size_of::>() == size_of_val(&a_boxed_error)) /// ``` fn from(err: E) -> Box { Box::new(err) @@ -563,7 +562,6 @@ impl<'a, E: Error + Send + Sync + 'a> From for Box From for Box::from(an_error); /// assert!( - /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) + /// size_of::>() == size_of_val(&a_boxed_error)) /// ``` fn from(err: E) -> Box { Box::new(err) @@ -600,12 +598,11 @@ impl<'a> From for Box { /// /// ``` /// 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)) + /// size_of::>() == size_of_val(&a_boxed_error)) /// ``` #[inline] fn from(err: String) -> Box { @@ -644,11 +641,10 @@ impl<'a> From for Box { /// /// ``` /// 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)) + /// assert!(size_of::>() == size_of_val(&a_boxed_error)) /// ``` fn from(str_err: String) -> Box { let err1: Box = From::from(str_err); @@ -668,12 +664,11 @@ impl<'a> From<&str> for Box { /// /// ``` /// 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)) + /// size_of::>() == size_of_val(&a_boxed_error)) /// ``` #[inline] fn from(err: &str) -> Box { @@ -692,11 +687,10 @@ impl<'a> From<&str> for Box { /// /// ``` /// 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)) + /// assert!(size_of::>() == size_of_val(&a_boxed_error)) /// ``` fn from(err: &str) -> Box { From::from(String::from(err)) @@ -712,13 +706,12 @@ impl<'a, 'b> From> for Box { /// /// ``` /// 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)) + /// size_of::>() == size_of_val(&a_boxed_error)) /// ``` fn from(err: Cow<'b, str>) -> Box { From::from(String::from(err)) @@ -734,12 +727,11 @@ impl<'a, 'b> From> for Box { /// /// ``` /// 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)) + /// assert!(size_of::>() == size_of_val(&a_boxed_error)) /// ``` fn from(err: Cow<'b, str>) -> Box { From::from(String::from(err)) diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 78e5aec09b18d..21425b9846e42 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -9,9 +9,8 @@ use core::intrinsics::const_allocate; use core::marker::PhantomData; #[cfg(not(no_global_oom_handling))] use core::marker::Unsize; -use core::mem; #[cfg(not(no_global_oom_handling))] -use core::mem::SizedTypeProperties; +use core::mem::{self, SizedTypeProperties}; use core::ops::{Deref, DerefMut}; use core::ptr::{self, NonNull, Pointee}; @@ -30,7 +29,6 @@ use crate::alloc::{self, Layout, LayoutError}; /// let five = ThinBox::new(5); /// let thin_slice = ThinBox::<[i32]>::new_unsize([1, 2, 3, 4]); /// -/// use std::mem::{size_of, size_of_val}; /// let size_of_ptr = size_of::<*const ()>(); /// assert_eq!(size_of_ptr, size_of_val(&five)); /// assert_eq!(size_of_ptr, size_of_val(&thin_slice)); @@ -114,7 +112,7 @@ impl ThinBox { where T: Unsize, { - if mem::size_of::() == 0 { + if size_of::() == 0 { let ptr = WithOpaqueHeader::new_unsize_zst::(value); ThinBox { ptr, _marker: PhantomData } } else { @@ -283,9 +281,7 @@ impl WithHeader { let ptr = if layout.size() == 0 { // Some paranoia checking, mostly so that the ThinBox tests are // more able to catch issues. - debug_assert!( - value_offset == 0 && mem::size_of::() == 0 && mem::size_of::() == 0 - ); + debug_assert!(value_offset == 0 && size_of::() == 0 && size_of::() == 0); layout.dangling() } else { let ptr = alloc::alloc(layout); @@ -315,7 +311,7 @@ impl WithHeader { Dyn: Pointee + ?Sized, T: Unsize, { - assert!(mem::size_of::() == 0); + assert!(size_of::() == 0); const fn max(a: usize, b: usize) -> usize { if a > b { a } else { b } @@ -329,18 +325,16 @@ impl WithHeader { // FIXME: just call `WithHeader::alloc_layout` with size reset to 0. // Currently that's blocked on `Layout::extend` not being `const fn`. - let alloc_align = - max(mem::align_of::(), mem::align_of::<::Metadata>()); + let alloc_align = max(align_of::(), align_of::<::Metadata>()); - let alloc_size = - max(mem::align_of::(), mem::size_of::<::Metadata>()); + let alloc_size = max(align_of::(), size_of::<::Metadata>()); unsafe { // SAFETY: align is power of two because it is the maximum of two alignments. let alloc: *mut u8 = const_allocate(alloc_size, alloc_align); let metadata_offset = - alloc_size.checked_sub(mem::size_of::<::Metadata>()).unwrap(); + alloc_size.checked_sub(size_of::<::Metadata>()).unwrap(); // SAFETY: adding offset within the allocation. let metadata_ptr: *mut ::Metadata = alloc.add(metadata_offset).cast(); @@ -421,7 +415,7 @@ impl WithHeader { } const fn header_size() -> usize { - mem::size_of::() + size_of::() } fn alloc_layout(value_layout: Layout) -> Result<(Layout, usize), LayoutError> { diff --git a/library/alloc/src/bstr.rs b/library/alloc/src/bstr.rs index 61e61019b508c..338c7ac7f8876 100644 --- a/library/alloc/src/bstr.rs +++ b/library/alloc/src/bstr.rs @@ -12,13 +12,10 @@ use core::ops::{ Deref, DerefMut, DerefPure, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, }; -#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 use core::str::FromStr; use core::{fmt, hash}; -#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 use crate::borrow::{Cow, ToOwned}; -#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 use crate::boxed::Box; #[cfg(not(no_rc))] use crate::rc::Rc; @@ -181,7 +178,6 @@ impl Default for ByteString { // Omitted due to inference failures // -// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 // #[unstable(feature = "bstr", issue = "134915")] // impl<'a, const N: usize> From<&'a [u8; N]> for ByteString { // #[inline] @@ -190,7 +186,6 @@ impl Default for ByteString { // } // } // -// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 // #[unstable(feature = "bstr", issue = "134915")] // impl From<[u8; N]> for ByteString { // #[inline] @@ -199,7 +194,6 @@ impl Default for ByteString { // } // } // -// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 // #[unstable(feature = "bstr", issue = "134915")] // impl<'a> From<&'a [u8]> for ByteString { // #[inline] @@ -226,7 +220,6 @@ impl From for Vec { // Omitted due to inference failures // -// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 // #[unstable(feature = "bstr", issue = "134915")] // impl<'a> From<&'a str> for ByteString { // #[inline] @@ -243,7 +236,6 @@ impl From for Vec { // } // } -#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[unstable(feature = "bstr", issue = "134915")] impl<'a> From<&'a ByteStr> for ByteString { #[inline] @@ -252,7 +244,6 @@ impl<'a> From<&'a ByteStr> for ByteString { } } -#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[unstable(feature = "bstr", issue = "134915")] impl<'a> From for Cow<'a, ByteStr> { #[inline] @@ -261,7 +252,6 @@ impl<'a> From for Cow<'a, ByteStr> { } } -#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[unstable(feature = "bstr", issue = "134915")] impl<'a> From<&'a ByteString> for Cow<'a, ByteStr> { #[inline] @@ -330,7 +320,6 @@ impl FromIterator for ByteString { } } -#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[unstable(feature = "bstr", issue = "134915")] impl FromStr for ByteString { type Err = core::convert::Infallible; @@ -488,7 +477,6 @@ impl PartialEq for ByteString { macro_rules! impl_partial_eq_ord_cow { ($lhs:ty, $rhs:ty) => { - #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[allow(unused_lifetimes)] #[unstable(feature = "bstr", issue = "134915")] impl<'a> PartialEq<$rhs> for $lhs { @@ -499,7 +487,6 @@ macro_rules! impl_partial_eq_ord_cow { } } - #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[allow(unused_lifetimes)] #[unstable(feature = "bstr", issue = "134915")] impl<'a> PartialEq<$lhs> for $rhs { @@ -510,7 +497,6 @@ macro_rules! impl_partial_eq_ord_cow { } } - #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[allow(unused_lifetimes)] #[unstable(feature = "bstr", issue = "134915")] impl<'a> PartialOrd<$rhs> for $lhs { @@ -521,7 +507,6 @@ macro_rules! impl_partial_eq_ord_cow { } } - #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[allow(unused_lifetimes)] #[unstable(feature = "bstr", issue = "134915")] impl<'a> PartialOrd<$lhs> for $rhs { @@ -572,7 +557,6 @@ impl PartialOrd for ByteString { } } -#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[unstable(feature = "bstr", issue = "134915")] impl ToOwned for ByteStr { type Owned = ByteString; @@ -605,7 +589,6 @@ impl<'a> TryFrom<&'a ByteString> for &'a str { // Additional impls for `ByteStr` that require types from `alloc`: -#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[unstable(feature = "bstr", issue = "134915")] impl Clone for Box { #[inline] @@ -614,7 +597,6 @@ impl Clone for Box { } } -#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[unstable(feature = "bstr", issue = "134915")] impl<'a> From<&'a ByteStr> for Cow<'a, ByteStr> { #[inline] @@ -623,7 +605,6 @@ impl<'a> From<&'a ByteStr> for Cow<'a, ByteStr> { } } -#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[unstable(feature = "bstr", issue = "134915")] impl From> for Box { #[inline] @@ -633,7 +614,6 @@ impl From> for Box { } } -#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 #[unstable(feature = "bstr", issue = "134915")] impl From> for Box<[u8]> { #[inline] diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 965fd63a52981..63828b482b9a9 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -153,7 +153,9 @@ use core::{fmt, ptr}; use crate::alloc::Global; use crate::collections::TryReserveError; use crate::slice; -use crate::vec::{self, AsVecIntoIter, Vec}; +#[cfg(not(test))] +use crate::vec::AsVecIntoIter; +use crate::vec::{self, Vec}; /// A priority queue implemented with a binary heap. /// @@ -359,6 +361,74 @@ impl DerefMut for PeekMut<'_, T, A> { } impl<'a, T: Ord, A: Allocator> PeekMut<'a, T, A> { + /// Sifts the current element to its new position. + /// + /// Afterwards refers to the new element. Returns if the element changed. + /// + /// ## Examples + /// + /// The condition can be used to upper bound all elements in the heap. When only few elements + /// are affected, the heap's sort ensures this is faster than a reconstruction from the raw + /// element list and requires no additional allocation. + /// + /// ``` + /// #![feature(binary_heap_peek_mut_refresh)] + /// use std::collections::BinaryHeap; + /// + /// let mut heap: BinaryHeap = (0..128).collect(); + /// let mut peek = heap.peek_mut().unwrap(); + /// + /// loop { + /// *peek = 99; + /// + /// if !peek.refresh() { + /// break; + /// } + /// } + /// + /// // Post condition, this is now an upper bound. + /// assert!(*peek < 100); + /// ``` + /// + /// When the element remains the maximum after modification, the peek remains unchanged: + /// + /// ``` + /// #![feature(binary_heap_peek_mut_refresh)] + /// use std::collections::BinaryHeap; + /// + /// let mut heap: BinaryHeap = [1, 2, 3].into(); + /// let mut peek = heap.peek_mut().unwrap(); + /// + /// assert_eq!(*peek, 3); + /// *peek = 42; + /// + /// // When we refresh, the peek is updated to the new maximum. + /// assert!(!peek.refresh(), "42 is even larger than 3"); + /// assert_eq!(*peek, 42); + /// ``` + #[unstable(feature = "binary_heap_peek_mut_refresh", issue = "138355")] + #[must_use = "is equivalent to dropping and getting a new PeekMut except for return information"] + pub fn refresh(&mut self) -> bool { + // The length of the underlying heap is unchanged by sifting down. The value stored for leak + // amplification thus remains accurate. We erase the leak amplification firstly because the + // operation is then equivalent to constructing a new PeekMut and secondly this avoids any + // future complication where original_len being non-empty would be interpreted as the heap + // having been leak amplified instead of checking the heap itself. + if let Some(original_len) = self.original_len.take() { + // SAFETY: This is how many elements were in the Vec at the time of + // the BinaryHeap::peek_mut call. + unsafe { self.heap.data.set_len(original_len.get()) }; + + // The length of the heap did not change by sifting, upholding our own invariants. + + // SAFETY: PeekMut is only instantiated for non-empty heaps. + (unsafe { self.heap.sift_down(0) }) != 0 + } else { + // The element was not modified. + false + } + } + /// 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, A>) -> T { @@ -670,6 +740,8 @@ impl BinaryHeap { /// # Safety /// /// The caller must guarantee that `pos < self.len()`. + /// + /// Returns the new position of the element. unsafe fn sift_up(&mut self, start: usize, pos: usize) -> usize { // Take out the value at `pos` and create a hole. // SAFETY: The caller guarantees that pos < self.len() @@ -696,10 +768,12 @@ impl BinaryHeap { /// Take an element at `pos` and move it down the heap, /// while its children are larger. /// + /// Returns the new position of the element. + /// /// # Safety /// /// The caller must guarantee that `pos < end <= self.len()`. - unsafe fn sift_down_range(&mut self, pos: usize, end: usize) { + unsafe fn sift_down_range(&mut self, pos: usize, end: usize) -> usize { // SAFETY: The caller guarantees that pos < end <= self.len(). let mut hole = unsafe { Hole::new(&mut self.data, pos) }; let mut child = 2 * hole.pos() + 1; @@ -719,7 +793,7 @@ impl BinaryHeap { // SAFETY: child is now either the old child or the old child+1 // We already proven that both are < self.len() and != hole.pos() if hole.element() >= unsafe { hole.get(child) } { - return; + return hole.pos(); } // SAFETY: same as above. @@ -734,16 +808,18 @@ impl BinaryHeap { // child == 2 * hole.pos() + 1 != hole.pos(). unsafe { hole.move_to(child) }; } + + hole.pos() } /// # Safety /// /// The caller must guarantee that `pos < self.len()`. - unsafe fn sift_down(&mut self, pos: usize) { + unsafe fn sift_down(&mut self, pos: usize) -> usize { let len = self.len(); // SAFETY: pos < len is guaranteed by the caller and // obviously len = self.len() <= self.len(). - unsafe { self.sift_down_range(pos, len) }; + unsafe { self.sift_down_range(pos, len) } } /// Take an element at `pos` and move it all the way down the heap, @@ -1600,6 +1676,7 @@ unsafe impl InPlaceIterable for IntoIter { const MERGE_BY: Option> = NonZero::new(1); } +#[cfg(not(test))] unsafe impl AsVecIntoIter for IntoIter { type Item = I; diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 78b7da9d6b3ee..5ca32ed741af8 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1917,14 +1917,13 @@ pub struct ExtractIf< V, F, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, -> where - F: 'a + FnMut(&K, &mut V) -> bool, -{ +> { pred: F, inner: ExtractIfInner<'a, K, V>, /// The BTreeMap will outlive this IntoIter so we don't care about drop order for `alloc`. alloc: A, } + /// Most of the implementation of ExtractIf are generic over the type /// of the predicate, thus also serving for BTreeSet::ExtractIf. pub(super) struct ExtractIfInner<'a, K, V> { @@ -1940,14 +1939,14 @@ pub(super) struct ExtractIfInner<'a, K, V> { } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl fmt::Debug for ExtractIf<'_, K, V, F> +impl fmt::Debug for ExtractIf<'_, K, V, F, A> where K: fmt::Debug, V: fmt::Debug, - F: FnMut(&K, &mut V) -> bool, + A: Allocator + Clone, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("ExtractIf").field(&self.inner.peek()).finish() + f.debug_struct("ExtractIf").field("peek", &self.inner.peek()).finish_non_exhaustive() } } diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index ecd009f11c71a..7d1a2ea480943 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -92,8 +92,8 @@ fn test_partial_eq() { #[cfg(target_arch = "x86_64")] #[cfg_attr(any(miri, randomized_layouts), ignore)] // We'd like to run Miri with layout randomization fn test_sizes() { - assert_eq!(core::mem::size_of::>(), 16); - assert_eq!(core::mem::size_of::>(), 16 + CAPACITY * 2 * 8); - assert_eq!(core::mem::size_of::>(), 16 + (CAPACITY + 1) * 8); - assert_eq!(core::mem::size_of::>(), 16 + (CAPACITY * 3 + 1) * 8); + assert_eq!(size_of::>(), 16); + assert_eq!(size_of::>(), 16 + CAPACITY * 2 * 8); + assert_eq!(size_of::>(), 16 + (CAPACITY + 1) * 8); + assert_eq!(size_of::>(), 16 + (CAPACITY * 3 + 1) * 8); } diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 041f80c1f2c52..343934680b87a 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -139,7 +139,7 @@ pub struct Iter<'a, T: 'a> { #[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() + f.debug_tuple("Iter").field(&self.iter).finish() } } @@ -1556,10 +1556,7 @@ pub struct ExtractIf< T, F, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, -> where - T: 'a, - F: 'a + FnMut(&T) -> bool, -{ +> { pred: F, inner: super::map::ExtractIfInner<'a, T, SetValZST>, /// The BTreeMap will outlive this IntoIter so we don't care about drop order for `alloc`. @@ -1567,13 +1564,15 @@ pub struct ExtractIf< } #[unstable(feature = "btree_extract_if", issue = "70530")] -impl fmt::Debug for ExtractIf<'_, T, F, A> +impl fmt::Debug for ExtractIf<'_, T, F, A> where T: fmt::Debug, - F: FnMut(&T) -> bool, + A: Allocator + Clone, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("ExtractIf").field(&self.inner.peek().map(|(k, _)| k)).finish() + f.debug_struct("ExtractIf") + .field("peek", &self.inner.peek().map(|(k, _)| k)) + .finish_non_exhaustive() } } diff --git a/library/alloc/src/collections/btree/set_val.rs b/library/alloc/src/collections/btree/set_val.rs index cf30160bfbbc2..5037b6578e80a 100644 --- a/library/alloc/src/collections/btree/set_val.rs +++ b/library/alloc/src/collections/btree/set_val.rs @@ -9,7 +9,7 @@ pub(super) struct SetValZST; /// Returns `true` only for type `SetValZST`, `false` for all other types (blanket implementation). /// `TypeId` requires a `'static` lifetime, use of this trait avoids that restriction. /// -/// [`TypeId`]: std::any::TypeId +/// [`TypeId`]: core::any::TypeId pub(super) trait IsSetVal { fn is_set_val() -> bool; } diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 3183268b4b32e..00e2805d11f61 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1151,7 +1151,7 @@ impl LinkedList { /// assert_eq!(evens.into_iter().collect::>(), vec![2, 4, 6, 8, 14]); /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 9, 11, 13, 15]); /// ``` - #[stable(feature = "extract_if", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "extract_if", since = "1.87.0")] pub fn extract_if(&mut self, filter: F) -> ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool, @@ -1931,7 +1931,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { } /// An iterator produced by calling `extract_if` on LinkedList. -#[stable(feature = "extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "extract_if", since = "1.87.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ExtractIf< 'a, @@ -1946,7 +1946,7 @@ pub struct ExtractIf< old_len: usize, } -#[stable(feature = "extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "extract_if", since = "1.87.0")] impl Iterator for ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool, @@ -1975,10 +1975,15 @@ where } } -#[stable(feature = "extract_if", since = "CURRENT_RUSTC_VERSION")] -impl fmt::Debug for ExtractIf<'_, T, F> { +#[stable(feature = "extract_if", since = "1.87.0")] +impl fmt::Debug for ExtractIf<'_, T, F, A> +where + T: fmt::Debug, + A: Allocator, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("ExtractIf").field(&self.list).finish() + let peek = self.it.map(|node| unsafe { &node.as_ref().element }); + f.debug_struct("ExtractIf").field("peek", &peek).finish_non_exhaustive() } } diff --git a/library/alloc/src/collections/mod.rs b/library/alloc/src/collections/mod.rs index 020cf4d736510..fac4d1a65abcb 100644 --- a/library/alloc/src/collections/mod.rs +++ b/library/alloc/src/collections/mod.rs @@ -1,5 +1,8 @@ //! Collection types. +// Note: This module is also included in the alloctests crate using #[path] to +// run the tests. See the comment there for an explanation why this is the case. + #![stable(feature = "rust1", since = "1.0.0")] #[cfg(not(no_global_oom_handling))] @@ -24,41 +27,54 @@ pub mod btree_map { pub mod btree_set { //! An ordered set based on a B-Tree. #[stable(feature = "rust1", since = "1.0.0")] + #[cfg(not(test))] pub use super::btree::set::*; } +#[cfg(not(test))] use core::fmt::Display; #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] +#[cfg(not(test))] pub use binary_heap::BinaryHeap; #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] +#[cfg(not(test))] pub use btree_map::BTreeMap; #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] +#[cfg(not(test))] pub use btree_set::BTreeSet; #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] +#[cfg(not(test))] pub use linked_list::LinkedList; #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] +#[cfg(not(test))] pub use vec_deque::VecDeque; +#[cfg(not(test))] use crate::alloc::{Layout, LayoutError}; /// The error type for `try_reserve` methods. #[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "try_reserve", since = "1.57.0")] +#[cfg(not(test))] pub struct TryReserveError { kind: TryReserveErrorKind, } +#[cfg(test)] +pub use realalloc::collections::TryReserveError; + +#[cfg(not(test))] impl TryReserveError { /// Details about the allocation that caused the error #[inline] @@ -80,6 +96,7 @@ impl TryReserveError { reason = "Uncertain how much info should be exposed", issue = "48043" )] +#[cfg(not(test))] pub enum TryReserveErrorKind { /// Error due to the computed capacity exceeding the collection's maximum /// (usually `isize::MAX` bytes). @@ -103,11 +120,15 @@ pub enum TryReserveErrorKind { }, } +#[cfg(test)] +pub use realalloc::collections::TryReserveErrorKind; + #[unstable( feature = "try_reserve_kind", reason = "Uncertain how much info should be exposed", issue = "48043" )] +#[cfg(not(test))] impl From for TryReserveError { #[inline] fn from(kind: TryReserveErrorKind) -> Self { @@ -116,6 +137,7 @@ impl From for TryReserveError { } #[unstable(feature = "try_reserve_kind", reason = "new API", issue = "48043")] +#[cfg(not(test))] impl From for TryReserveErrorKind { /// Always evaluates to [`TryReserveErrorKind::CapacityOverflow`]. #[inline] @@ -125,6 +147,7 @@ impl From for TryReserveErrorKind { } #[stable(feature = "try_reserve", since = "1.57.0")] +#[cfg(not(test))] impl Display for TryReserveError { fn fmt( &self, @@ -152,4 +175,5 @@ trait SpecExtend { } #[stable(feature = "try_reserve", since = "1.57.0")] +#[cfg(not(test))] impl core::error::Error for TryReserveError {} diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 299c8b8679e3d..08b1828ff000e 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -645,6 +645,7 @@ impl VecDeque { /// initialized rather than only supporting `0..len`. Requires that /// `initialized.start` ≤ `initialized.end` ≤ `capacity`. #[inline] + #[cfg(not(test))] pub(crate) unsafe fn from_contiguous_raw_parts_in( ptr: *mut T, initialized: Range, @@ -1187,6 +1188,73 @@ impl VecDeque { } } + /// Shortens the deque, keeping the last `len` elements and dropping + /// the rest. + /// + /// If `len` is greater or equal to the deque's current length, this has + /// no effect. + /// + /// # Examples + /// + /// ``` + /// # #![feature(vec_deque_truncate_front)] + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// buf.push_front(5); + /// buf.push_front(10); + /// buf.push_front(15); + /// assert_eq!(buf, [15, 10, 5]); + /// assert_eq!(buf.as_slices(), (&[15, 10, 5][..], &[][..])); + /// buf.truncate_front(1); + /// assert_eq!(buf.as_slices(), (&[5][..], &[][..])); + /// ``` + #[unstable(feature = "vec_deque_truncate_front", issue = "140667")] + pub fn truncate_front(&mut self, len: usize) { + /// Runs the destructor for all items in the slice when it gets dropped (normally or + /// during unwinding). + struct Dropper<'a, T>(&'a mut [T]); + + impl<'a, T> Drop for Dropper<'a, T> { + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(self.0); + } + } + } + + unsafe { + if len >= self.len { + // No action is taken + return; + } + + let (front, back) = self.as_mut_slices(); + if len > back.len() { + // The 'back' slice remains unchanged. + // front.len() + back.len() == self.len, so 'end' is non-negative + // and end < front.len() + let end = front.len() - (len - back.len()); + let drop_front = front.get_unchecked_mut(..end) as *mut _; + self.head += end; + self.len = len; + ptr::drop_in_place(drop_front); + } else { + let drop_front = front as *mut _; + // 'end' is non-negative by the condition above + let end = back.len() - len; + let drop_back = back.get_unchecked_mut(..end) as *mut _; + self.head = self.to_physical_idx(self.len - len); + self.len = len; + + // Make sure the second half is dropped even when a destructor + // in the first one panics. + let _back_dropper = Dropper(&mut *drop_back); + ptr::drop_in_place(drop_front); + } + } + } + /// Returns a reference to the underlying allocator. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] @@ -1244,6 +1312,8 @@ impl VecDeque { /// /// If [`make_contiguous`] was previously called, all elements of the /// deque will be in the first slice and the second slice will be empty. + /// Otherwise, the exact split point depends on implementation details + /// and is not guaranteed. /// /// [`make_contiguous`]: VecDeque::make_contiguous /// @@ -1258,12 +1328,18 @@ impl VecDeque { /// deque.push_back(1); /// deque.push_back(2); /// - /// assert_eq!(deque.as_slices(), (&[0, 1, 2][..], &[][..])); + /// let expected = [0, 1, 2]; + /// let (front, back) = deque.as_slices(); + /// assert_eq!(&expected[..front.len()], front); + /// assert_eq!(&expected[front.len()..], back); /// /// deque.push_front(10); /// deque.push_front(9); /// - /// assert_eq!(deque.as_slices(), (&[9, 10][..], &[0, 1, 2][..])); + /// let expected = [9, 10, 0, 1, 2]; + /// let (front, back) = deque.as_slices(); + /// assert_eq!(&expected[..front.len()], front); + /// assert_eq!(&expected[front.len()..], back); /// ``` #[inline] #[stable(feature = "deque_extras_15", since = "1.5.0")] @@ -1279,6 +1355,8 @@ impl VecDeque { /// /// If [`make_contiguous`] was previously called, all elements of the /// deque will be in the first slice and the second slice will be empty. + /// Otherwise, the exact split point depends on implementation details + /// and is not guaranteed. /// /// [`make_contiguous`]: VecDeque::make_contiguous /// @@ -1295,9 +1373,22 @@ impl VecDeque { /// deque.push_front(10); /// deque.push_front(9); /// - /// deque.as_mut_slices().0[0] = 42; - /// deque.as_mut_slices().1[0] = 24; - /// assert_eq!(deque.as_slices(), (&[42, 10][..], &[24, 1][..])); + /// // Since the split point is not guaranteed, we may need to update + /// // either slice. + /// let mut update_nth = |index: usize, val: u32| { + /// let (front, back) = deque.as_mut_slices(); + /// if index > front.len() - 1 { + /// back[index - front.len()] = val; + /// } else { + /// front[index] = val; + /// } + /// }; + /// + /// update_nth(0, 42); + /// update_nth(2, 24); + /// + /// let v: Vec<_> = deque.into(); + /// assert_eq!(v, [42, 10, 24, 1]); /// ``` #[inline] #[stable(feature = "deque_extras_15", since = "1.5.0")] diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs index d246385ca8419..7c7072c4c3a1b 100644 --- a/library/alloc/src/collections/vec_deque/spec_extend.rs +++ b/library/alloc/src/collections/vec_deque/spec_extend.rs @@ -3,6 +3,7 @@ use core::slice; use super::VecDeque; use crate::alloc::Allocator; +#[cfg(not(test))] use crate::vec; // Specialization trait used for VecDeque::extend @@ -78,6 +79,7 @@ where } } +#[cfg(not(test))] impl SpecExtend> for VecDeque { #[track_caller] fn spec_extend(&mut self, mut iterator: vec::IntoIter) { diff --git a/library/alloc/src/collections/vec_deque/spec_from_iter.rs b/library/alloc/src/collections/vec_deque/spec_from_iter.rs index 1efe84d6d7d7d..c80a30c2103dd 100644 --- a/library/alloc/src/collections/vec_deque/spec_from_iter.rs +++ b/library/alloc/src/collections/vec_deque/spec_from_iter.rs @@ -19,6 +19,7 @@ where } } +#[cfg(not(test))] impl SpecFromIter> for VecDeque { #[inline] fn spec_from_iter(iterator: crate::vec::IntoIter) -> Self { diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index fd93045a5ac4d..8b448a18402c3 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -10,7 +10,6 @@ use core::{fmt, mem, ops, ptr, slice}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::rc::Rc; -use crate::slice::hack::into_vec; use crate::string::String; #[cfg(target_has_atomic = "ptr")] use crate::sync::Arc; @@ -103,7 +102,7 @@ use crate::vec::Vec; /// of `CString` instances can lead to invalid memory accesses, memory leaks, /// and other memory errors. #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)] -#[cfg_attr(not(test), rustc_diagnostic_item = "cstring_type")] +#[rustc_diagnostic_item = "cstring_type"] #[stable(feature = "alloc_c_string", since = "1.64.0")] pub struct CString { // Invariant 1: the slice ends with a zero byte and has a length of at least one. @@ -352,9 +351,14 @@ impl CString { /// # Safety /// /// This should only ever be called with a pointer that was earlier - /// 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. + /// obtained by calling [`CString::into_raw`], and the memory it points to must not be accessed + /// through any other pointer during the lifetime of reconstructed `CString`. + /// 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. + /// + /// This function does not validate ownership of the raw pointer's memory. + /// A double-free may occur if the function is called twice on the same raw pointer. + /// Additionally, the caller must ensure the pointer is not dangling. /// /// It should be noted that the length isn't just "recomputed," but that /// the recomputed length must match the original length from the @@ -491,7 +495,7 @@ impl CString { #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "cstring_into", since = "1.7.0")] pub fn into_bytes(self) -> Vec { - let mut vec = into_vec(self.into_inner()); + let mut vec = self.into_inner().into_vec(); let _nul = vec.pop(); debug_assert_eq!(_nul, Some(0u8)); vec @@ -512,7 +516,7 @@ impl CString { #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "cstring_into", since = "1.7.0")] pub fn into_bytes_with_nul(self) -> Vec { - into_vec(self.into_inner()) + self.into_inner().into_vec() } /// Returns the contents of this `CString` as a slice of bytes. @@ -573,9 +577,9 @@ impl CString { #[inline] #[must_use] #[stable(feature = "as_c_str", since = "1.20.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "cstring_as_c_str")] + #[rustc_diagnostic_item = "cstring_as_c_str"] pub fn as_c_str(&self) -> &CStr { - &*self + unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) } } /// Converts this `CString` into a boxed [`CStr`]. @@ -706,14 +710,14 @@ impl ops::Deref for CString { #[inline] fn deref(&self) -> &CStr { - unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) } + self.as_c_str() } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for CString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) + fmt::Debug::fmt(self.as_c_str(), f) } } @@ -755,7 +759,6 @@ impl<'a> From> for CString { } } -#[cfg(not(test))] #[stable(feature = "box_from_c_str", since = "1.17.0")] impl From<&CStr> for Box { /// Converts a `&CStr` into a `Box`, @@ -766,7 +769,6 @@ impl From<&CStr> for Box { } } -#[cfg(not(test))] #[stable(feature = "box_from_mut_slice", since = "1.84.0")] impl From<&mut CStr> for Box { /// Converts a `&mut CStr` into a `Box`, @@ -821,6 +823,7 @@ impl From>> for CString { } } +#[stable(feature = "c_string_from_str", since = "1.85.0")] impl FromStr for CString { type Err = NulError; @@ -833,6 +836,7 @@ impl FromStr for CString { } } +#[stable(feature = "c_string_from_str", since = "1.85.0")] impl TryFrom for String { type Error = IntoStringError; @@ -845,7 +849,6 @@ impl TryFrom for String { } } -#[cfg(not(test))] #[stable(feature = "more_box_slice_clone", since = "1.29.0")] impl Clone for Box { #[inline] @@ -971,7 +974,6 @@ impl Default for Rc { } } -#[cfg(not(test))] #[stable(feature = "default_box_extra", since = "1.17.0")] impl Default for Box { fn default() -> Box { @@ -1080,7 +1082,7 @@ impl ToOwned for CStr { } fn clone_into(&self, target: &mut CString) { - let mut b = into_vec(mem::take(&mut target.inner)); + let mut b = mem::take(&mut target.inner).into_vec(); self.to_bytes_with_nul().clone_into(&mut b); target.inner = b.into_boxed_slice(); } @@ -1113,7 +1115,6 @@ impl AsRef for CString { } } -#[cfg(not(test))] impl CStr { /// Converts a `CStr` into a [Cow]<[str]>. /// @@ -1122,7 +1123,7 @@ impl CStr { /// with the corresponding &[str] slice. Otherwise, it will /// replace any invalid UTF-8 sequences with /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD] and return a - /// [Cow]::[Owned]\(&[str]) with the result. + /// [Cow]::[Owned]\([String]) with the result. /// /// [str]: prim@str "str" /// [Borrowed]: Cow::Borrowed diff --git a/library/alloc/src/ffi/mod.rs b/library/alloc/src/ffi/mod.rs index 695d7ad07cf76..1c408ace33613 100644 --- a/library/alloc/src/ffi/mod.rs +++ b/library/alloc/src/ffi/mod.rs @@ -87,5 +87,5 @@ pub use self::c_str::CString; #[stable(feature = "alloc_c_string", since = "1.64.0")] pub use self::c_str::{FromVecWithNulError, IntoStringError, NulError}; -#[unstable(feature = "c_str_module", issue = "112134")] +#[stable(feature = "c_str_module", since = "1.88.0")] pub mod c_str; diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index e40de13f3d4a9..30f42050ac8ac 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -109,7 +109,7 @@ //! parameters (corresponding to `format_spec` in [the syntax](#syntax)). These //! parameters affect the string representation of what's being formatted. //! -//! The colon `:` in format syntax divides indentifier of the input data and +//! The colon `:` in format syntax divides identifier of the input data and //! the formatting options, the colon itself does not change anything, only //! introduces the options. //! diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 2e9dd98571537..abda5aefab645 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -56,6 +56,7 @@ //! [`Rc`]: rc //! [`RefCell`]: core::cell +#![allow(incomplete_features)] #![allow(unused_attributes)] #![stable(feature = "alloc", since = "1.36.0")] #![doc( @@ -92,7 +93,6 @@ // // Library features: // tidy-alphabetical-start -#![cfg_attr(test, feature(str_as_str))] #![feature(alloc_layout_extra)] #![feature(allocator_api)] #![feature(array_chunks)] @@ -102,9 +102,9 @@ #![feature(assert_matches)] #![feature(async_fn_traits)] #![feature(async_iterator)] -#![feature(box_uninit_write)] #![feature(bstr)] #![feature(bstr_internals)] +#![feature(char_internals)] #![feature(char_max_len)] #![feature(clone_to_uninit)] #![feature(coerce_unsized)] @@ -114,6 +114,7 @@ #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] #![feature(dispatch_from_dyn)] +#![feature(ergonomic_clones)] #![feature(error_generic_member_access)] #![feature(exact_size_is_empty)] #![feature(extend_one)] @@ -121,6 +122,7 @@ #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(formatting_options)] +#![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] @@ -135,6 +137,7 @@ #![feature(pattern)] #![feature(pin_coerce_unsized_trait)] #![feature(pointer_like_trait)] +#![feature(ptr_alignment_type)] #![feature(ptr_internals)] #![feature(ptr_metadata)] #![feature(set_ptr_value)] @@ -160,13 +163,11 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(not(test), feature(coroutine_trait))] -#![cfg_attr(test, feature(panic_update_hook))] -#![cfg_attr(test, feature(test))] #![feature(allocator_internals)] #![feature(allow_internal_unstable)] #![feature(cfg_sanitize)] #![feature(const_precise_live_drops)] +#![feature(coroutine_trait)] #![feature(decl_macro)] #![feature(dropck_eyepatch)] #![feature(fundamental)] @@ -199,15 +200,6 @@ // from other crates, but since this can only appear for lang items, it doesn't seem worth fixing. #![feature(intra_doc_pointers)] -// Allow testing this library -#[cfg(test)] -#[macro_use] -extern crate std; -#[cfg(test)] -extern crate test; -#[cfg(test)] -mod testing; - // Module with internal macros used by other modules (needs to be included before other modules). #[macro_use] mod macros; @@ -215,7 +207,6 @@ mod macros; mod raw_vec; // Heaps provided for low-level allocation strategies - pub mod alloc; // Primitive types using the heaps above @@ -223,13 +214,8 @@ pub mod alloc; // 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(crate) use std::boxed::Box; -} pub mod borrow; +pub mod boxed; #[unstable(feature = "bstr", issue = "134915")] pub mod bstr; pub mod collections; @@ -253,20 +239,3 @@ pub mod __export { pub use core::format_args; pub use core::hint::must_use; } - -#[cfg(test)] -#[allow(dead_code)] // Not used in all configurations -pub(crate) mod test_helpers { - /// Copied from `std::test_helpers::test_rng`, since these tests rely on the - /// seed not being the same for every RNG invocation too. - pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { - use std::hash::{BuildHasher, Hash, Hasher}; - let mut hasher = std::hash::RandomState::new().build_hasher(); - std::panic::Location::caller().hash(&mut hasher); - let hc64 = hasher.finish(); - let seed_vec = - hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); - let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); - rand::SeedableRng::from_seed(seed) - } -} diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index c000fd6f4efa7..1e6e2ae8c3675 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -34,7 +34,7 @@ /// be mindful of side effects. /// /// [`Vec`]: crate::vec::Vec -#[cfg(all(not(no_global_oom_handling), not(test)))] +#[cfg(not(no_global_oom_handling))] #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "vec_macro"] @@ -55,25 +55,6 @@ macro_rules! vec { ); } -// 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(all(not(no_global_oom_handling), test))] -#[allow(unused_macro_rules)] -macro_rules! vec { - () => ( - $crate::vec::Vec::new() - ); - ($elem:expr; $n:expr) => ( - $crate::vec::from_elem($elem, $n) - ); - ($($x:expr),*) => ( - $crate::slice::into_vec($crate::boxed::Box::new([$($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 @@ -120,12 +101,11 @@ macro_rules! vec { #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(hint_must_use, liballoc_internals)] -#[cfg_attr(not(test), rustc_diagnostic_item = "format_macro")] +#[rustc_diagnostic_item = "format_macro"] macro_rules! format { ($($arg:tt)*) => { $crate::__export::must_use({ - let res = $crate::fmt::format($crate::__export::format_args!($($arg)*)); - res + $crate::fmt::format($crate::__export::format_args!($($arg)*)) }) } } diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs deleted file mode 100644 index b80d1fc788947..0000000000000 --- a/library/alloc/src/raw_vec.rs +++ /dev/null @@ -1,818 +0,0 @@ -#![unstable(feature = "raw_vec_internals", reason = "unstable const warnings", issue = "none")] - -use core::marker::PhantomData; -use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties}; -use core::ptr::{self, NonNull, Unique}; -use core::{cmp, hint}; - -#[cfg(not(no_global_oom_handling))] -use crate::alloc::handle_alloc_error; -use crate::alloc::{Allocator, Global, Layout}; -use crate::boxed::Box; -use crate::collections::TryReserveError; -use crate::collections::TryReserveErrorKind::*; - -#[cfg(test)] -mod tests; - -// One central function responsible for reporting capacity overflows. This'll -// ensure that the code generation related to these panics is minimal as there's -// only one location which panics rather than a bunch throughout the module. -#[cfg(not(no_global_oom_handling))] -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] -#[track_caller] -fn capacity_overflow() -> ! { - panic!("capacity overflow"); -} - -enum AllocInit { - /// The contents of the new memory are uninitialized. - Uninitialized, - #[cfg(not(no_global_oom_handling))] - /// The new memory is guaranteed to be zeroed. - Zeroed, -} - -type Cap = core::num::niche_types::UsizeNoHighBit; - -const ZERO_CAP: Cap = unsafe { Cap::new_unchecked(0) }; - -/// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. -/// -/// # Safety: cap must be <= `isize::MAX`. -unsafe fn new_cap(cap: usize) -> Cap { - if T::IS_ZST { ZERO_CAP } else { unsafe { Cap::new_unchecked(cap) } } -} - -/// 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. -/// In particular: -/// -/// * Produces `Unique::dangling()` on zero-sized types. -/// * Produces `Unique::dangling()` on zero-length allocations. -/// * Avoids freeing `Unique::dangling()`. -/// * Catches all overflows in capacity computations (promotes them to "capacity overflow" panics). -/// * Guards against 32-bit systems allocating more than `isize::MAX` bytes. -/// * Guards against overflowing your length. -/// * Calls `handle_alloc_error` for fallible allocations. -/// * Contains a `ptr::Unique` and thus endows the user with all related benefits. -/// * Uses the excess returned from the allocator to use the largest available capacity. -/// -/// This type does not in anyway inspect the memory that it manages. When dropped it *will* -/// free its memory, but it *won't* try to drop its contents. It is up to the user of `RawVec` -/// to handle the actual things *stored* inside of a `RawVec`. -/// -/// Note that the excess of a zero-sized types is always infinite, so `capacity()` always returns -/// `usize::MAX`. This means that you need to be careful when round-tripping this type with a -/// `Box<[T]>`, since `capacity()` won't yield the length. -#[allow(missing_debug_implementations)] -pub(crate) struct RawVec { - inner: RawVecInner, - _marker: PhantomData, -} - -/// Like a `RawVec`, but only generic over the allocator, not the type. -/// -/// As such, all the methods need the layout passed-in as a parameter. -/// -/// Having this separation reduces the amount of code we need to monomorphize, -/// as most operations don't need the actual type, just its layout. -#[allow(missing_debug_implementations)] -struct RawVecInner { - ptr: Unique, - /// Never used for ZSTs; it's `capacity()`'s responsibility to return usize::MAX in that case. - /// - /// # Safety - /// - /// `cap` must be in the `0..=isize::MAX` range. - cap: Cap, - alloc: A, -} - -impl RawVec { - /// Creates the biggest possible `RawVec` (on the system heap) - /// without allocating. If `T` has positive size, then this makes a - /// `RawVec` with capacity `0`. If `T` is zero-sized, then it makes a - /// `RawVec` with capacity `usize::MAX`. Useful for implementing - /// delayed allocation. - #[must_use] - pub(crate) const fn new() -> Self { - Self::new_in(Global) - } - - /// Creates a `RawVec` (on the system heap) with exactly the - /// capacity and alignment requirements for a `[T; capacity]`. This is - /// equivalent to calling `RawVec::new` when `capacity` is `0` or `T` is - /// zero-sized. Note that if `T` is zero-sized this means you will - /// *not* get a `RawVec` with the requested capacity. - /// - /// Non-fallible version of `try_with_capacity` - /// - /// # Panics - /// - /// Panics if the requested capacity exceeds `isize::MAX` bytes. - /// - /// # Aborts - /// - /// Aborts on OOM. - #[cfg(not(any(no_global_oom_handling, test)))] - #[must_use] - #[inline] - #[track_caller] - pub(crate) fn with_capacity(capacity: usize) -> Self { - Self { inner: RawVecInner::with_capacity(capacity, T::LAYOUT), _marker: PhantomData } - } - - /// Like `with_capacity`, but guarantees the buffer is zeroed. - #[cfg(not(any(no_global_oom_handling, test)))] - #[must_use] - #[inline] - #[track_caller] - pub(crate) fn with_capacity_zeroed(capacity: usize) -> Self { - Self { - inner: RawVecInner::with_capacity_zeroed_in(capacity, Global, T::LAYOUT), - _marker: PhantomData, - } - } -} - -impl RawVecInner { - #[cfg(not(any(no_global_oom_handling, test)))] - #[must_use] - #[inline] - #[track_caller] - fn with_capacity(capacity: usize, elem_layout: Layout) -> Self { - match Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global, elem_layout) { - Ok(res) => res, - Err(err) => handle_error(err), - } - } -} - -// Tiny Vecs are dumb. Skip to: -// - 8 if the element size is 1, because any heap allocators is likely -// to round up a request of less than 8 bytes to at least 8 bytes. -// - 4 if elements are moderate-sized (<= 1 KiB). -// - 1 otherwise, to avoid wasting too much space for very short Vecs. -const fn min_non_zero_cap(size: usize) -> usize { - if size == 1 { - 8 - } else if size <= 1024 { - 4 - } else { - 1 - } -} - -impl RawVec { - #[cfg(not(no_global_oom_handling))] - pub(crate) const MIN_NON_ZERO_CAP: usize = min_non_zero_cap(size_of::()); - - /// Like `new`, but parameterized over the choice of allocator for - /// the returned `RawVec`. - #[inline] - pub(crate) const fn new_in(alloc: A) -> Self { - Self { inner: RawVecInner::new_in(alloc, align_of::()), _marker: PhantomData } - } - - /// Like `with_capacity`, but parameterized over the choice of - /// allocator for the returned `RawVec`. - #[cfg(not(no_global_oom_handling))] - #[inline] - #[track_caller] - pub(crate) fn with_capacity_in(capacity: usize, alloc: A) -> Self { - Self { - inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), - _marker: PhantomData, - } - } - - /// Like `try_with_capacity`, but parameterized over the choice of - /// allocator for the returned `RawVec`. - #[inline] - pub(crate) fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { - match RawVecInner::try_with_capacity_in(capacity, alloc, T::LAYOUT) { - Ok(inner) => Ok(Self { inner, _marker: PhantomData }), - Err(e) => Err(e), - } - } - - /// Like `with_capacity_zeroed`, but parameterized over the choice - /// of allocator for the returned `RawVec`. - #[cfg(not(no_global_oom_handling))] - #[inline] - #[track_caller] - pub(crate) fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { - Self { - inner: RawVecInner::with_capacity_zeroed_in(capacity, alloc, T::LAYOUT), - _marker: PhantomData, - } - } - - /// Converts the entire buffer into `Box<[MaybeUninit]>` with the specified `len`. - /// - /// Note that this will correctly reconstitute any `cap` changes - /// that may have been performed. (See description of type for details.) - /// - /// # Safety - /// - /// * `len` must be greater than or equal to the most recently requested capacity, and - /// * `len` must be less than or equal to `self.capacity()`. - /// - /// Note, that the requested capacity and `self.capacity()` could differ, as - /// an allocator could overallocate and return a greater memory block than requested. - pub(crate) unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { - // Sanity-check one half of the safety requirement (we cannot check the other half). - debug_assert!( - len <= self.capacity(), - "`len` must be smaller than or equal to `self.capacity()`" - ); - - let me = ManuallyDrop::new(self); - unsafe { - let slice = ptr::slice_from_raw_parts_mut(me.ptr() as *mut MaybeUninit, len); - Box::from_raw_in(slice, ptr::read(&me.inner.alloc)) - } - } - - /// Reconstitutes a `RawVec` from a pointer, capacity, and allocator. - /// - /// # Safety - /// - /// 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). For ZSTs capacity is ignored. - /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is - /// guaranteed. - #[inline] - pub(crate) unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { - // SAFETY: Precondition passed to the caller - unsafe { - let ptr = ptr.cast(); - let capacity = new_cap::(capacity); - Self { - inner: RawVecInner::from_raw_parts_in(ptr, capacity, alloc), - _marker: PhantomData, - } - } - } - - /// A convenience method for hoisting the non-null precondition out of [`RawVec::from_raw_parts_in`]. - /// - /// # Safety - /// - /// See [`RawVec::from_raw_parts_in`]. - #[inline] - pub(crate) unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { - // SAFETY: Precondition passed to the caller - unsafe { - let ptr = ptr.cast(); - let capacity = new_cap::(capacity); - Self { inner: RawVecInner::from_nonnull_in(ptr, capacity, alloc), _marker: PhantomData } - } - } - - /// Gets a raw pointer to the start of the allocation. Note that this is - /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must - /// be careful. - #[inline] - pub(crate) const fn ptr(&self) -> *mut T { - self.inner.ptr() - } - - #[inline] - pub(crate) fn non_null(&self) -> NonNull { - self.inner.non_null() - } - - /// Gets the capacity of the allocation. - /// - /// This will always be `usize::MAX` if `T` is zero-sized. - #[inline] - pub(crate) const fn capacity(&self) -> usize { - self.inner.capacity(size_of::()) - } - - /// Returns a shared reference to the allocator backing this `RawVec`. - #[inline] - pub(crate) fn allocator(&self) -> &A { - self.inner.allocator() - } - - /// Ensures that the buffer contains at least enough space to hold `len + - /// additional` elements. If it doesn't already have enough capacity, will - /// reallocate enough space plus comfortable slack space to get amortized - /// *O*(1) behavior. Will limit this behavior if it would needlessly cause - /// itself to panic. - /// - /// If `len` exceeds `self.capacity()`, this may fail to actually allocate - /// the requested space. This is not really unsafe, but the unsafe - /// code *you* write that relies on the behavior of this function may break. - /// - /// This is ideal for implementing a bulk-push operation like `extend`. - /// - /// # Panics - /// - /// Panics if the new capacity exceeds `isize::MAX` _bytes_. - /// - /// # Aborts - /// - /// Aborts on OOM. - #[cfg(not(no_global_oom_handling))] - #[inline] - #[track_caller] - pub(crate) fn reserve(&mut self, len: usize, additional: usize) { - self.inner.reserve(len, additional, T::LAYOUT) - } - - /// A specialized version of `self.reserve(len, 1)` which requires the - /// caller to ensure `len == self.capacity()`. - #[cfg(not(no_global_oom_handling))] - #[inline(never)] - #[track_caller] - pub(crate) fn grow_one(&mut self) { - self.inner.grow_one(T::LAYOUT) - } - - /// The same as `reserve`, but returns on errors instead of panicking or aborting. - pub(crate) fn try_reserve( - &mut self, - len: usize, - additional: usize, - ) -> Result<(), TryReserveError> { - self.inner.try_reserve(len, additional, T::LAYOUT) - } - - /// Ensures that the buffer contains at least enough space to hold `len + - /// additional` elements. If it doesn't already, will reallocate the - /// minimum possible amount of memory necessary. Generally this will be - /// exactly the amount of memory necessary, but in principle the allocator - /// is free to give back more than we asked for. - /// - /// If `len` exceeds `self.capacity()`, this may fail to actually allocate - /// the requested space. This is not really unsafe, but the unsafe code - /// *you* write that relies on the behavior of this function may break. - /// - /// # Panics - /// - /// Panics if the new capacity exceeds `isize::MAX` _bytes_. - /// - /// # Aborts - /// - /// Aborts on OOM. - #[cfg(not(no_global_oom_handling))] - #[track_caller] - pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) { - self.inner.reserve_exact(len, additional, T::LAYOUT) - } - - /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. - pub(crate) fn try_reserve_exact( - &mut self, - len: usize, - additional: usize, - ) -> Result<(), TryReserveError> { - self.inner.try_reserve_exact(len, additional, T::LAYOUT) - } - - /// Shrinks the buffer down to the specified capacity. If the given amount - /// is 0, actually completely deallocates. - /// - /// # Panics - /// - /// Panics if the given amount is *larger* than the current capacity. - /// - /// # Aborts - /// - /// Aborts on OOM. - #[cfg(not(no_global_oom_handling))] - #[track_caller] - #[inline] - pub(crate) fn shrink_to_fit(&mut self, cap: usize) { - self.inner.shrink_to_fit(cap, T::LAYOUT) - } -} - -unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { - /// Frees the memory owned by the `RawVec` *without* trying to drop its contents. - fn drop(&mut self) { - // SAFETY: We are in a Drop impl, self.inner will not be used again. - unsafe { self.inner.deallocate(T::LAYOUT) } - } -} - -impl RawVecInner { - #[inline] - const fn new_in(alloc: A, align: usize) -> Self { - let ptr = unsafe { core::mem::transmute(align) }; - // `cap: 0` means "unallocated". zero-sized types are ignored. - Self { ptr, cap: ZERO_CAP, alloc } - } - - #[cfg(not(no_global_oom_handling))] - #[inline] - #[track_caller] - fn with_capacity_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { - match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) { - Ok(this) => { - unsafe { - // Make it more obvious that a subsequent Vec::reserve(capacity) will not allocate. - hint::assert_unchecked(!this.needs_to_grow(0, capacity, elem_layout)); - } - this - } - Err(err) => handle_error(err), - } - } - - #[inline] - fn try_with_capacity_in( - capacity: usize, - alloc: A, - elem_layout: Layout, - ) -> Result { - Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) - } - - #[cfg(not(no_global_oom_handling))] - #[inline] - #[track_caller] - fn with_capacity_zeroed_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { - match Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc, elem_layout) { - Ok(res) => res, - Err(err) => handle_error(err), - } - } - - fn try_allocate_in( - capacity: usize, - init: AllocInit, - alloc: A, - elem_layout: Layout, - ) -> Result { - // We avoid `unwrap_or_else` here because it bloats the amount of - // LLVM IR generated. - let layout = match layout_array(capacity, elem_layout) { - Ok(layout) => layout, - Err(_) => return Err(CapacityOverflow.into()), - }; - - // Don't allocate here because `Drop` will not deallocate when `capacity` is 0. - if layout.size() == 0 { - return Ok(Self::new_in(alloc, elem_layout.align())); - } - - if let Err(err) = alloc_guard(layout.size()) { - return Err(err); - } - - let result = match init { - AllocInit::Uninitialized => alloc.allocate(layout), - #[cfg(not(no_global_oom_handling))] - AllocInit::Zeroed => alloc.allocate_zeroed(layout), - }; - let ptr = match result { - Ok(ptr) => ptr, - Err(_) => return Err(AllocError { layout, non_exhaustive: () }.into()), - }; - - // Allocators currently return a `NonNull<[u8]>` whose length - // matches the size requested. If that ever changes, the capacity - // here should change to `ptr.len() / mem::size_of::()`. - Ok(Self { - ptr: Unique::from(ptr.cast()), - cap: unsafe { Cap::new_unchecked(capacity) }, - alloc, - }) - } - - #[inline] - unsafe fn from_raw_parts_in(ptr: *mut u8, cap: Cap, alloc: A) -> Self { - Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap, alloc } - } - - #[inline] - unsafe fn from_nonnull_in(ptr: NonNull, cap: Cap, alloc: A) -> Self { - Self { ptr: Unique::from(ptr), cap, alloc } - } - - #[inline] - const fn ptr(&self) -> *mut T { - self.non_null::().as_ptr() - } - - #[inline] - const fn non_null(&self) -> NonNull { - self.ptr.cast().as_non_null_ptr() - } - - #[inline] - const fn capacity(&self, elem_size: usize) -> usize { - if elem_size == 0 { usize::MAX } else { self.cap.as_inner() } - } - - #[inline] - fn allocator(&self) -> &A { - &self.alloc - } - - #[inline] - fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull, Layout)> { - if elem_layout.size() == 0 || self.cap.as_inner() == 0 { - None - } else { - // We could use Layout::array here which ensures the absence of isize and usize overflows - // and could hypothetically handle differences between stride and size, but this memory - // has already been allocated so we know it can't overflow and currently Rust does not - // support such types. So we can do better by skipping some checks and avoid an unwrap. - unsafe { - let alloc_size = elem_layout.size().unchecked_mul(self.cap.as_inner()); - let layout = Layout::from_size_align_unchecked(alloc_size, elem_layout.align()); - Some((self.ptr.into(), layout)) - } - } - } - - #[cfg(not(no_global_oom_handling))] - #[inline] - #[track_caller] - fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) { - // Callers expect this function to be very cheap when there is already sufficient capacity. - // Therefore, we move all the resizing and error-handling logic from grow_amortized and - // handle_reserve behind a call, while making sure that this function is likely to be - // inlined as just a comparison and a call if the comparison fails. - #[cold] - fn do_reserve_and_handle( - slf: &mut RawVecInner, - len: usize, - additional: usize, - elem_layout: Layout, - ) { - if let Err(err) = slf.grow_amortized(len, additional, elem_layout) { - handle_error(err); - } - } - - if self.needs_to_grow(len, additional, elem_layout) { - do_reserve_and_handle(self, len, additional, elem_layout); - } - } - - #[cfg(not(no_global_oom_handling))] - #[inline] - #[track_caller] - fn grow_one(&mut self, elem_layout: Layout) { - if let Err(err) = self.grow_amortized(self.cap.as_inner(), 1, elem_layout) { - handle_error(err); - } - } - - fn try_reserve( - &mut self, - len: usize, - additional: usize, - elem_layout: Layout, - ) -> Result<(), TryReserveError> { - if self.needs_to_grow(len, additional, elem_layout) { - self.grow_amortized(len, additional, elem_layout)?; - } - unsafe { - // Inform the optimizer that the reservation has succeeded or wasn't needed - hint::assert_unchecked(!self.needs_to_grow(len, additional, elem_layout)); - } - Ok(()) - } - - #[cfg(not(no_global_oom_handling))] - #[track_caller] - fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) { - if let Err(err) = self.try_reserve_exact(len, additional, elem_layout) { - handle_error(err); - } - } - - fn try_reserve_exact( - &mut self, - len: usize, - additional: usize, - elem_layout: Layout, - ) -> Result<(), TryReserveError> { - if self.needs_to_grow(len, additional, elem_layout) { - self.grow_exact(len, additional, elem_layout)?; - } - unsafe { - // Inform the optimizer that the reservation has succeeded or wasn't needed - hint::assert_unchecked(!self.needs_to_grow(len, additional, elem_layout)); - } - Ok(()) - } - - #[cfg(not(no_global_oom_handling))] - #[inline] - #[track_caller] - fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) { - if let Err(err) = self.shrink(cap, elem_layout) { - handle_error(err); - } - } - - #[inline] - fn needs_to_grow(&self, len: usize, additional: usize, elem_layout: Layout) -> bool { - additional > self.capacity(elem_layout.size()).wrapping_sub(len) - } - - #[inline] - unsafe fn set_ptr_and_cap(&mut self, ptr: NonNull<[u8]>, cap: usize) { - // Allocators currently return a `NonNull<[u8]>` whose length matches - // the size requested. If that ever changes, the capacity here should - // change to `ptr.len() / mem::size_of::()`. - self.ptr = Unique::from(ptr.cast()); - self.cap = unsafe { Cap::new_unchecked(cap) }; - } - - fn grow_amortized( - &mut self, - len: usize, - additional: usize, - elem_layout: Layout, - ) -> Result<(), TryReserveError> { - // This is ensured by the calling contexts. - debug_assert!(additional > 0); - - if elem_layout.size() == 0 { - // Since we return a capacity of `usize::MAX` when `elem_size` is - // 0, getting to here necessarily means the `RawVec` is overfull. - return Err(CapacityOverflow.into()); - } - - // Nothing we can really do about these checks, sadly. - let required_cap = len.checked_add(additional).ok_or(CapacityOverflow)?; - - // This guarantees exponential growth. The doubling cannot overflow - // because `cap <= isize::MAX` and the type of `cap` is `usize`. - let cap = cmp::max(self.cap.as_inner() * 2, required_cap); - let cap = cmp::max(min_non_zero_cap(elem_layout.size()), cap); - - let new_layout = layout_array(cap, elem_layout)?; - - let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?; - // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items - - unsafe { self.set_ptr_and_cap(ptr, cap) }; - Ok(()) - } - - fn grow_exact( - &mut self, - len: usize, - additional: usize, - elem_layout: Layout, - ) -> Result<(), TryReserveError> { - if elem_layout.size() == 0 { - // Since we return a capacity of `usize::MAX` when the type size is - // 0, getting to here necessarily means the `RawVec` is overfull. - return Err(CapacityOverflow.into()); - } - - let cap = len.checked_add(additional).ok_or(CapacityOverflow)?; - let new_layout = layout_array(cap, elem_layout)?; - - let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?; - // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items - unsafe { - self.set_ptr_and_cap(ptr, cap); - } - Ok(()) - } - - #[cfg(not(no_global_oom_handling))] - #[inline] - fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> { - assert!(cap <= self.capacity(elem_layout.size()), "Tried to shrink to a larger capacity"); - // SAFETY: Just checked this isn't trying to grow - unsafe { self.shrink_unchecked(cap, elem_layout) } - } - - /// `shrink`, but without the capacity check. - /// - /// This is split out so that `shrink` can inline the check, since it - /// optimizes out in things like `shrink_to_fit`, without needing to - /// also inline all this code, as doing that ends up failing the - /// `vec-shrink-panic` codegen test when `shrink_to_fit` ends up being too - /// big for LLVM to be willing to inline. - /// - /// # Safety - /// `cap <= self.capacity()` - #[cfg(not(no_global_oom_handling))] - unsafe fn shrink_unchecked( - &mut self, - cap: usize, - elem_layout: Layout, - ) -> Result<(), TryReserveError> { - let (ptr, layout) = - if let Some(mem) = self.current_memory(elem_layout) { mem } else { return Ok(()) }; - - // If shrinking to 0, deallocate the buffer. We don't reach this point - // for the T::IS_ZST case since current_memory() will have returned - // None. - if cap == 0 { - unsafe { self.alloc.deallocate(ptr, layout) }; - self.ptr = - unsafe { Unique::new_unchecked(ptr::without_provenance_mut(elem_layout.align())) }; - self.cap = ZERO_CAP; - } else { - let ptr = unsafe { - // Layout cannot overflow here because it would have - // overflowed earlier when capacity was larger. - let new_size = elem_layout.size().unchecked_mul(cap); - let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); - self.alloc - .shrink(ptr, layout, new_layout) - .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })? - }; - // SAFETY: if the allocation is valid, then the capacity is too - unsafe { - self.set_ptr_and_cap(ptr, cap); - } - } - Ok(()) - } - - /// # Safety - /// - /// This function deallocates the owned allocation, but does not update `ptr` or `cap` to - /// prevent double-free or use-after-free. Essentially, do not do anything with the caller - /// after this function returns. - /// Ideally this function would take `self` by move, but it cannot because it exists to be - /// called from a `Drop` impl. - unsafe fn deallocate(&mut self, elem_layout: Layout) { - if let Some((ptr, layout)) = self.current_memory(elem_layout) { - unsafe { - self.alloc.deallocate(ptr, layout); - } - } - } -} - -// not marked inline(never) since we want optimizers to be able to observe the specifics of this -// function, see tests/codegen/vec-reserve-extend.rs. -#[cold] -fn finish_grow( - new_layout: Layout, - current_memory: Option<(NonNull, Layout)>, - alloc: &mut A, -) -> Result, TryReserveError> -where - A: Allocator, -{ - alloc_guard(new_layout.size())?; - - let memory = if let Some((ptr, old_layout)) = current_memory { - debug_assert_eq!(old_layout.align(), new_layout.align()); - unsafe { - // The allocator checks for alignment equality - hint::assert_unchecked(old_layout.align() == new_layout.align()); - alloc.grow(ptr, old_layout, new_layout) - } - } else { - alloc.allocate(new_layout) - }; - - memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () }.into()) -} - -// Central function for reserve error handling. -#[cfg(not(no_global_oom_handling))] -#[cold] -#[optimize(size)] -#[track_caller] -fn handle_error(e: TryReserveError) -> ! { - match e.kind() { - CapacityOverflow => capacity_overflow(), - AllocError { layout, .. } => handle_alloc_error(layout), - } -} - -// We need to guarantee the following: -// * We don't ever allocate `> isize::MAX` byte-size objects. -// * We don't overflow `usize::MAX` and actually allocate too little. -// -// On 64-bit we just need to check for overflow since trying to allocate -// `> isize::MAX` bytes will surely fail. On 32-bit and 16-bit we need to add -// an extra guard for this in case we're running on a platform which can use -// all 4GB in user-space, e.g., PAE or x32. -#[inline] -fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> { - if usize::BITS < 64 && alloc_size > isize::MAX as usize { - Err(CapacityOverflow.into()) - } else { - Ok(()) - } -} - -#[inline] -fn layout_array(cap: usize, elem_layout: Layout) -> Result { - elem_layout.repeat(cap).map(|(layout, _pad)| layout).map_err(|_| CapacityOverflow.into()) -} diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs new file mode 100644 index 0000000000000..3e006a2d1bdf1 --- /dev/null +++ b/library/alloc/src/raw_vec/mod.rs @@ -0,0 +1,822 @@ +#![unstable(feature = "raw_vec_internals", reason = "unstable const warnings", issue = "none")] +#![cfg_attr(test, allow(dead_code))] + +// Note: This module is also included in the alloctests crate using #[path] to +// run the tests. See the comment there for an explanation why this is the case. + +use core::marker::PhantomData; +use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties}; +use core::ptr::{self, Alignment, NonNull, Unique}; +use core::{cmp, hint}; + +#[cfg(not(no_global_oom_handling))] +use crate::alloc::handle_alloc_error; +use crate::alloc::{Allocator, Global, Layout}; +use crate::boxed::Box; +use crate::collections::TryReserveError; +use crate::collections::TryReserveErrorKind::*; + +#[cfg(test)] +mod tests; + +// One central function responsible for reporting capacity overflows. This'll +// ensure that the code generation related to these panics is minimal as there's +// only one location which panics rather than a bunch throughout the module. +#[cfg(not(no_global_oom_handling))] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[track_caller] +fn capacity_overflow() -> ! { + panic!("capacity overflow"); +} + +enum AllocInit { + /// The contents of the new memory are uninitialized. + Uninitialized, + #[cfg(not(no_global_oom_handling))] + /// The new memory is guaranteed to be zeroed. + Zeroed, +} + +type Cap = core::num::niche_types::UsizeNoHighBit; + +const ZERO_CAP: Cap = unsafe { Cap::new_unchecked(0) }; + +/// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. +/// +/// # Safety: cap must be <= `isize::MAX`. +unsafe fn new_cap(cap: usize) -> Cap { + if T::IS_ZST { ZERO_CAP } else { unsafe { Cap::new_unchecked(cap) } } +} + +/// 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. +/// In particular: +/// +/// * Produces `Unique::dangling()` on zero-sized types. +/// * Produces `Unique::dangling()` on zero-length allocations. +/// * Avoids freeing `Unique::dangling()`. +/// * Catches all overflows in capacity computations (promotes them to "capacity overflow" panics). +/// * Guards against 32-bit systems allocating more than `isize::MAX` bytes. +/// * Guards against overflowing your length. +/// * Calls `handle_alloc_error` for fallible allocations. +/// * Contains a `ptr::Unique` and thus endows the user with all related benefits. +/// * Uses the excess returned from the allocator to use the largest available capacity. +/// +/// This type does not in anyway inspect the memory that it manages. When dropped it *will* +/// free its memory, but it *won't* try to drop its contents. It is up to the user of `RawVec` +/// to handle the actual things *stored* inside of a `RawVec`. +/// +/// Note that the excess of a zero-sized types is always infinite, so `capacity()` always returns +/// `usize::MAX`. This means that you need to be careful when round-tripping this type with a +/// `Box<[T]>`, since `capacity()` won't yield the length. +#[allow(missing_debug_implementations)] +pub(crate) struct RawVec { + inner: RawVecInner, + _marker: PhantomData, +} + +/// Like a `RawVec`, but only generic over the allocator, not the type. +/// +/// As such, all the methods need the layout passed-in as a parameter. +/// +/// Having this separation reduces the amount of code we need to monomorphize, +/// as most operations don't need the actual type, just its layout. +#[allow(missing_debug_implementations)] +struct RawVecInner { + ptr: Unique, + /// Never used for ZSTs; it's `capacity()`'s responsibility to return usize::MAX in that case. + /// + /// # Safety + /// + /// `cap` must be in the `0..=isize::MAX` range. + cap: Cap, + alloc: A, +} + +impl RawVec { + /// Creates the biggest possible `RawVec` (on the system heap) + /// without allocating. If `T` has positive size, then this makes a + /// `RawVec` with capacity `0`. If `T` is zero-sized, then it makes a + /// `RawVec` with capacity `usize::MAX`. Useful for implementing + /// delayed allocation. + #[must_use] + pub(crate) const fn new() -> Self { + Self::new_in(Global) + } + + /// Creates a `RawVec` (on the system heap) with exactly the + /// capacity and alignment requirements for a `[T; capacity]`. This is + /// equivalent to calling `RawVec::new` when `capacity` is `0` or `T` is + /// zero-sized. Note that if `T` is zero-sized this means you will + /// *not* get a `RawVec` with the requested capacity. + /// + /// Non-fallible version of `try_with_capacity` + /// + /// # Panics + /// + /// Panics if the requested capacity exceeds `isize::MAX` bytes. + /// + /// # Aborts + /// + /// Aborts on OOM. + #[cfg(not(any(no_global_oom_handling, test)))] + #[must_use] + #[inline] + #[track_caller] + pub(crate) fn with_capacity(capacity: usize) -> Self { + Self { inner: RawVecInner::with_capacity(capacity, T::LAYOUT), _marker: PhantomData } + } + + /// Like `with_capacity`, but guarantees the buffer is zeroed. + #[cfg(not(any(no_global_oom_handling, test)))] + #[must_use] + #[inline] + #[track_caller] + pub(crate) fn with_capacity_zeroed(capacity: usize) -> Self { + Self { + inner: RawVecInner::with_capacity_zeroed_in(capacity, Global, T::LAYOUT), + _marker: PhantomData, + } + } +} + +impl RawVecInner { + #[cfg(not(any(no_global_oom_handling, test)))] + #[must_use] + #[inline] + #[track_caller] + fn with_capacity(capacity: usize, elem_layout: Layout) -> Self { + match Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global, elem_layout) { + Ok(res) => res, + Err(err) => handle_error(err), + } + } +} + +// Tiny Vecs are dumb. Skip to: +// - 8 if the element size is 1, because any heap allocators is likely +// to round up a request of less than 8 bytes to at least 8 bytes. +// - 4 if elements are moderate-sized (<= 1 KiB). +// - 1 otherwise, to avoid wasting too much space for very short Vecs. +const fn min_non_zero_cap(size: usize) -> usize { + if size == 1 { + 8 + } else if size <= 1024 { + 4 + } else { + 1 + } +} + +impl RawVec { + #[cfg(not(no_global_oom_handling))] + pub(crate) const MIN_NON_ZERO_CAP: usize = min_non_zero_cap(size_of::()); + + /// Like `new`, but parameterized over the choice of allocator for + /// the returned `RawVec`. + #[inline] + pub(crate) const fn new_in(alloc: A) -> Self { + Self { inner: RawVecInner::new_in(alloc, Alignment::of::()), _marker: PhantomData } + } + + /// Like `with_capacity`, but parameterized over the choice of + /// allocator for the returned `RawVec`. + #[cfg(not(no_global_oom_handling))] + #[inline] + #[track_caller] + pub(crate) fn with_capacity_in(capacity: usize, alloc: A) -> Self { + Self { + inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), + _marker: PhantomData, + } + } + + /// Like `try_with_capacity`, but parameterized over the choice of + /// allocator for the returned `RawVec`. + #[inline] + pub(crate) fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { + match RawVecInner::try_with_capacity_in(capacity, alloc, T::LAYOUT) { + Ok(inner) => Ok(Self { inner, _marker: PhantomData }), + Err(e) => Err(e), + } + } + + /// Like `with_capacity_zeroed`, but parameterized over the choice + /// of allocator for the returned `RawVec`. + #[cfg(not(no_global_oom_handling))] + #[inline] + #[track_caller] + pub(crate) fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { + Self { + inner: RawVecInner::with_capacity_zeroed_in(capacity, alloc, T::LAYOUT), + _marker: PhantomData, + } + } + + /// Converts the entire buffer into `Box<[MaybeUninit]>` with the specified `len`. + /// + /// Note that this will correctly reconstitute any `cap` changes + /// that may have been performed. (See description of type for details.) + /// + /// # Safety + /// + /// * `len` must be greater than or equal to the most recently requested capacity, and + /// * `len` must be less than or equal to `self.capacity()`. + /// + /// Note, that the requested capacity and `self.capacity()` could differ, as + /// an allocator could overallocate and return a greater memory block than requested. + pub(crate) unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { + // Sanity-check one half of the safety requirement (we cannot check the other half). + debug_assert!( + len <= self.capacity(), + "`len` must be smaller than or equal to `self.capacity()`" + ); + + let me = ManuallyDrop::new(self); + unsafe { + let slice = ptr::slice_from_raw_parts_mut(me.ptr() as *mut MaybeUninit, len); + Box::from_raw_in(slice, ptr::read(&me.inner.alloc)) + } + } + + /// Reconstitutes a `RawVec` from a pointer, capacity, and allocator. + /// + /// # Safety + /// + /// 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). For ZSTs capacity is ignored. + /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is + /// guaranteed. + #[inline] + pub(crate) unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { + // SAFETY: Precondition passed to the caller + unsafe { + let ptr = ptr.cast(); + let capacity = new_cap::(capacity); + Self { + inner: RawVecInner::from_raw_parts_in(ptr, capacity, alloc), + _marker: PhantomData, + } + } + } + + /// A convenience method for hoisting the non-null precondition out of [`RawVec::from_raw_parts_in`]. + /// + /// # Safety + /// + /// See [`RawVec::from_raw_parts_in`]. + #[inline] + pub(crate) unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { + // SAFETY: Precondition passed to the caller + unsafe { + let ptr = ptr.cast(); + let capacity = new_cap::(capacity); + Self { inner: RawVecInner::from_nonnull_in(ptr, capacity, alloc), _marker: PhantomData } + } + } + + /// Gets a raw pointer to the start of the allocation. Note that this is + /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must + /// be careful. + #[inline] + pub(crate) const fn ptr(&self) -> *mut T { + self.inner.ptr() + } + + #[inline] + pub(crate) const fn non_null(&self) -> NonNull { + self.inner.non_null() + } + + /// Gets the capacity of the allocation. + /// + /// This will always be `usize::MAX` if `T` is zero-sized. + #[inline] + pub(crate) const fn capacity(&self) -> usize { + self.inner.capacity(size_of::()) + } + + /// Returns a shared reference to the allocator backing this `RawVec`. + #[inline] + pub(crate) fn allocator(&self) -> &A { + self.inner.allocator() + } + + /// Ensures that the buffer contains at least enough space to hold `len + + /// additional` elements. If it doesn't already have enough capacity, will + /// reallocate enough space plus comfortable slack space to get amortized + /// *O*(1) behavior. Will limit this behavior if it would needlessly cause + /// itself to panic. + /// + /// If `len` exceeds `self.capacity()`, this may fail to actually allocate + /// the requested space. This is not really unsafe, but the unsafe + /// code *you* write that relies on the behavior of this function may break. + /// + /// This is ideal for implementing a bulk-push operation like `extend`. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// + /// # Aborts + /// + /// Aborts on OOM. + #[cfg(not(no_global_oom_handling))] + #[inline] + #[track_caller] + pub(crate) fn reserve(&mut self, len: usize, additional: usize) { + self.inner.reserve(len, additional, T::LAYOUT) + } + + /// A specialized version of `self.reserve(len, 1)` which requires the + /// caller to ensure `len == self.capacity()`. + #[cfg(not(no_global_oom_handling))] + #[inline(never)] + #[track_caller] + pub(crate) fn grow_one(&mut self) { + self.inner.grow_one(T::LAYOUT) + } + + /// The same as `reserve`, but returns on errors instead of panicking or aborting. + pub(crate) fn try_reserve( + &mut self, + len: usize, + additional: usize, + ) -> Result<(), TryReserveError> { + self.inner.try_reserve(len, additional, T::LAYOUT) + } + + /// Ensures that the buffer contains at least enough space to hold `len + + /// additional` elements. If it doesn't already, will reallocate the + /// minimum possible amount of memory necessary. Generally this will be + /// exactly the amount of memory necessary, but in principle the allocator + /// is free to give back more than we asked for. + /// + /// If `len` exceeds `self.capacity()`, this may fail to actually allocate + /// the requested space. This is not really unsafe, but the unsafe code + /// *you* write that relies on the behavior of this function may break. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// + /// # Aborts + /// + /// Aborts on OOM. + #[cfg(not(no_global_oom_handling))] + #[track_caller] + pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) { + self.inner.reserve_exact(len, additional, T::LAYOUT) + } + + /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. + pub(crate) fn try_reserve_exact( + &mut self, + len: usize, + additional: usize, + ) -> Result<(), TryReserveError> { + self.inner.try_reserve_exact(len, additional, T::LAYOUT) + } + + /// Shrinks the buffer down to the specified capacity. If the given amount + /// is 0, actually completely deallocates. + /// + /// # Panics + /// + /// Panics if the given amount is *larger* than the current capacity. + /// + /// # Aborts + /// + /// Aborts on OOM. + #[cfg(not(no_global_oom_handling))] + #[track_caller] + #[inline] + pub(crate) fn shrink_to_fit(&mut self, cap: usize) { + self.inner.shrink_to_fit(cap, T::LAYOUT) + } +} + +unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { + /// Frees the memory owned by the `RawVec` *without* trying to drop its contents. + fn drop(&mut self) { + // SAFETY: We are in a Drop impl, self.inner will not be used again. + unsafe { self.inner.deallocate(T::LAYOUT) } + } +} + +impl RawVecInner { + #[inline] + const fn new_in(alloc: A, align: Alignment) -> Self { + let ptr = Unique::from_non_null(NonNull::without_provenance(align.as_nonzero())); + // `cap: 0` means "unallocated". zero-sized types are ignored. + Self { ptr, cap: ZERO_CAP, alloc } + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + #[track_caller] + fn with_capacity_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { + match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) { + Ok(this) => { + unsafe { + // Make it more obvious that a subsequent Vec::reserve(capacity) will not allocate. + hint::assert_unchecked(!this.needs_to_grow(0, capacity, elem_layout)); + } + this + } + Err(err) => handle_error(err), + } + } + + #[inline] + fn try_with_capacity_in( + capacity: usize, + alloc: A, + elem_layout: Layout, + ) -> Result { + Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + #[track_caller] + fn with_capacity_zeroed_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { + match Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc, elem_layout) { + Ok(res) => res, + Err(err) => handle_error(err), + } + } + + fn try_allocate_in( + capacity: usize, + init: AllocInit, + alloc: A, + elem_layout: Layout, + ) -> Result { + // We avoid `unwrap_or_else` here because it bloats the amount of + // LLVM IR generated. + let layout = match layout_array(capacity, elem_layout) { + Ok(layout) => layout, + Err(_) => return Err(CapacityOverflow.into()), + }; + + // Don't allocate here because `Drop` will not deallocate when `capacity` is 0. + if layout.size() == 0 { + return Ok(Self::new_in(alloc, elem_layout.alignment())); + } + + if let Err(err) = alloc_guard(layout.size()) { + return Err(err); + } + + let result = match init { + AllocInit::Uninitialized => alloc.allocate(layout), + #[cfg(not(no_global_oom_handling))] + AllocInit::Zeroed => alloc.allocate_zeroed(layout), + }; + let ptr = match result { + Ok(ptr) => ptr, + Err(_) => return Err(AllocError { layout, non_exhaustive: () }.into()), + }; + + // Allocators currently return a `NonNull<[u8]>` whose length + // matches the size requested. If that ever changes, the capacity + // here should change to `ptr.len() / size_of::()`. + Ok(Self { + ptr: Unique::from(ptr.cast()), + cap: unsafe { Cap::new_unchecked(capacity) }, + alloc, + }) + } + + #[inline] + unsafe fn from_raw_parts_in(ptr: *mut u8, cap: Cap, alloc: A) -> Self { + Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap, alloc } + } + + #[inline] + unsafe fn from_nonnull_in(ptr: NonNull, cap: Cap, alloc: A) -> Self { + Self { ptr: Unique::from(ptr), cap, alloc } + } + + #[inline] + const fn ptr(&self) -> *mut T { + self.non_null::().as_ptr() + } + + #[inline] + const fn non_null(&self) -> NonNull { + self.ptr.cast().as_non_null_ptr() + } + + #[inline] + const fn capacity(&self, elem_size: usize) -> usize { + if elem_size == 0 { usize::MAX } else { self.cap.as_inner() } + } + + #[inline] + fn allocator(&self) -> &A { + &self.alloc + } + + #[inline] + fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull, Layout)> { + if elem_layout.size() == 0 || self.cap.as_inner() == 0 { + None + } else { + // We could use Layout::array here which ensures the absence of isize and usize overflows + // and could hypothetically handle differences between stride and size, but this memory + // has already been allocated so we know it can't overflow and currently Rust does not + // support such types. So we can do better by skipping some checks and avoid an unwrap. + unsafe { + let alloc_size = elem_layout.size().unchecked_mul(self.cap.as_inner()); + let layout = Layout::from_size_align_unchecked(alloc_size, elem_layout.align()); + Some((self.ptr.into(), layout)) + } + } + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + #[track_caller] + fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) { + // Callers expect this function to be very cheap when there is already sufficient capacity. + // Therefore, we move all the resizing and error-handling logic from grow_amortized and + // handle_reserve behind a call, while making sure that this function is likely to be + // inlined as just a comparison and a call if the comparison fails. + #[cold] + fn do_reserve_and_handle( + slf: &mut RawVecInner, + len: usize, + additional: usize, + elem_layout: Layout, + ) { + if let Err(err) = slf.grow_amortized(len, additional, elem_layout) { + handle_error(err); + } + } + + if self.needs_to_grow(len, additional, elem_layout) { + do_reserve_and_handle(self, len, additional, elem_layout); + } + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + #[track_caller] + fn grow_one(&mut self, elem_layout: Layout) { + if let Err(err) = self.grow_amortized(self.cap.as_inner(), 1, elem_layout) { + handle_error(err); + } + } + + fn try_reserve( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + if self.needs_to_grow(len, additional, elem_layout) { + self.grow_amortized(len, additional, elem_layout)?; + } + unsafe { + // Inform the optimizer that the reservation has succeeded or wasn't needed + hint::assert_unchecked(!self.needs_to_grow(len, additional, elem_layout)); + } + Ok(()) + } + + #[cfg(not(no_global_oom_handling))] + #[track_caller] + fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) { + if let Err(err) = self.try_reserve_exact(len, additional, elem_layout) { + handle_error(err); + } + } + + fn try_reserve_exact( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + if self.needs_to_grow(len, additional, elem_layout) { + self.grow_exact(len, additional, elem_layout)?; + } + unsafe { + // Inform the optimizer that the reservation has succeeded or wasn't needed + hint::assert_unchecked(!self.needs_to_grow(len, additional, elem_layout)); + } + Ok(()) + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + #[track_caller] + fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) { + if let Err(err) = self.shrink(cap, elem_layout) { + handle_error(err); + } + } + + #[inline] + fn needs_to_grow(&self, len: usize, additional: usize, elem_layout: Layout) -> bool { + additional > self.capacity(elem_layout.size()).wrapping_sub(len) + } + + #[inline] + unsafe fn set_ptr_and_cap(&mut self, ptr: NonNull<[u8]>, cap: usize) { + // Allocators currently return a `NonNull<[u8]>` whose length matches + // the size requested. If that ever changes, the capacity here should + // change to `ptr.len() / size_of::()`. + self.ptr = Unique::from(ptr.cast()); + self.cap = unsafe { Cap::new_unchecked(cap) }; + } + + fn grow_amortized( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + // This is ensured by the calling contexts. + debug_assert!(additional > 0); + + if elem_layout.size() == 0 { + // Since we return a capacity of `usize::MAX` when `elem_size` is + // 0, getting to here necessarily means the `RawVec` is overfull. + return Err(CapacityOverflow.into()); + } + + // Nothing we can really do about these checks, sadly. + let required_cap = len.checked_add(additional).ok_or(CapacityOverflow)?; + + // This guarantees exponential growth. The doubling cannot overflow + // because `cap <= isize::MAX` and the type of `cap` is `usize`. + let cap = cmp::max(self.cap.as_inner() * 2, required_cap); + let cap = cmp::max(min_non_zero_cap(elem_layout.size()), cap); + + let new_layout = layout_array(cap, elem_layout)?; + + let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?; + // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items + + unsafe { self.set_ptr_and_cap(ptr, cap) }; + Ok(()) + } + + fn grow_exact( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + if elem_layout.size() == 0 { + // Since we return a capacity of `usize::MAX` when the type size is + // 0, getting to here necessarily means the `RawVec` is overfull. + return Err(CapacityOverflow.into()); + } + + let cap = len.checked_add(additional).ok_or(CapacityOverflow)?; + let new_layout = layout_array(cap, elem_layout)?; + + let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?; + // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items + unsafe { + self.set_ptr_and_cap(ptr, cap); + } + Ok(()) + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> { + assert!(cap <= self.capacity(elem_layout.size()), "Tried to shrink to a larger capacity"); + // SAFETY: Just checked this isn't trying to grow + unsafe { self.shrink_unchecked(cap, elem_layout) } + } + + /// `shrink`, but without the capacity check. + /// + /// This is split out so that `shrink` can inline the check, since it + /// optimizes out in things like `shrink_to_fit`, without needing to + /// also inline all this code, as doing that ends up failing the + /// `vec-shrink-panic` codegen test when `shrink_to_fit` ends up being too + /// big for LLVM to be willing to inline. + /// + /// # Safety + /// `cap <= self.capacity()` + #[cfg(not(no_global_oom_handling))] + unsafe fn shrink_unchecked( + &mut self, + cap: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + let (ptr, layout) = + if let Some(mem) = self.current_memory(elem_layout) { mem } else { return Ok(()) }; + + // If shrinking to 0, deallocate the buffer. We don't reach this point + // for the T::IS_ZST case since current_memory() will have returned + // None. + if cap == 0 { + unsafe { self.alloc.deallocate(ptr, layout) }; + self.ptr = + unsafe { Unique::new_unchecked(ptr::without_provenance_mut(elem_layout.align())) }; + self.cap = ZERO_CAP; + } else { + let ptr = unsafe { + // Layout cannot overflow here because it would have + // overflowed earlier when capacity was larger. + let new_size = elem_layout.size().unchecked_mul(cap); + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); + self.alloc + .shrink(ptr, layout, new_layout) + .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })? + }; + // SAFETY: if the allocation is valid, then the capacity is too + unsafe { + self.set_ptr_and_cap(ptr, cap); + } + } + Ok(()) + } + + /// # Safety + /// + /// This function deallocates the owned allocation, but does not update `ptr` or `cap` to + /// prevent double-free or use-after-free. Essentially, do not do anything with the caller + /// after this function returns. + /// Ideally this function would take `self` by move, but it cannot because it exists to be + /// called from a `Drop` impl. + unsafe fn deallocate(&mut self, elem_layout: Layout) { + if let Some((ptr, layout)) = self.current_memory(elem_layout) { + unsafe { + self.alloc.deallocate(ptr, layout); + } + } + } +} + +// not marked inline(never) since we want optimizers to be able to observe the specifics of this +// function, see tests/codegen/vec-reserve-extend.rs. +#[cold] +fn finish_grow( + new_layout: Layout, + current_memory: Option<(NonNull, Layout)>, + alloc: &mut A, +) -> Result, TryReserveError> +where + A: Allocator, +{ + alloc_guard(new_layout.size())?; + + let memory = if let Some((ptr, old_layout)) = current_memory { + debug_assert_eq!(old_layout.align(), new_layout.align()); + unsafe { + // The allocator checks for alignment equality + hint::assert_unchecked(old_layout.align() == new_layout.align()); + alloc.grow(ptr, old_layout, new_layout) + } + } else { + alloc.allocate(new_layout) + }; + + memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () }.into()) +} + +// Central function for reserve error handling. +#[cfg(not(no_global_oom_handling))] +#[cold] +#[optimize(size)] +#[track_caller] +fn handle_error(e: TryReserveError) -> ! { + match e.kind() { + CapacityOverflow => capacity_overflow(), + AllocError { layout, .. } => handle_alloc_error(layout), + } +} + +// We need to guarantee the following: +// * We don't ever allocate `> isize::MAX` byte-size objects. +// * We don't overflow `usize::MAX` and actually allocate too little. +// +// On 64-bit we just need to check for overflow since trying to allocate +// `> isize::MAX` bytes will surely fail. On 32-bit and 16-bit we need to add +// an extra guard for this in case we're running on a platform which can use +// all 4GB in user-space, e.g., PAE or x32. +#[inline] +fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> { + if usize::BITS < 64 && alloc_size > isize::MAX as usize { + Err(CapacityOverflow.into()) + } else { + Ok(()) + } +} + +#[inline] +fn layout_array(cap: usize, elem_layout: Layout) -> Result { + elem_layout.repeat(cap).map(|(layout, _pad)| layout).map_err(|_| CapacityOverflow.into()) +} diff --git a/library/alloc/src/raw_vec/tests.rs b/library/alloc/src/raw_vec/tests.rs index d78ded104fb09..700fa922739d6 100644 --- a/library/alloc/src/raw_vec/tests.rs +++ b/library/alloc/src/raw_vec/tests.rs @@ -1,4 +1,3 @@ -use core::mem::size_of; use std::cell::Cell; use super::*; @@ -93,7 +92,7 @@ fn zst_sanity(v: &RawVec) { fn zst() { let cap_err = Err(crate::collections::TryReserveErrorKind::CapacityOverflow.into()); - assert_eq!(std::mem::size_of::(), 0); + assert_eq!(size_of::(), 0); // All these different ways of creating the RawVec produce the same thing. diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 09206c2f8b290..4b8ea708e7e57 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -245,6 +245,7 @@ use core::any::Any; use core::cell::Cell; #[cfg(not(no_global_oom_handling))] use core::clone::CloneToUninit; +use core::clone::UseCloned; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::intrinsics::abort; @@ -262,23 +263,17 @@ use core::ptr::{self, NonNull, drop_in_place}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; use core::{borrow, fmt, hint}; -#[cfg(test)] -use std::boxed::Box; #[cfg(not(no_global_oom_handling))] use crate::alloc::handle_alloc_error; use crate::alloc::{AllocError, Allocator, Global, Layout}; use crate::borrow::{Cow, ToOwned}; -#[cfg(not(test))] use crate::boxed::Box; #[cfg(not(no_global_oom_handling))] use crate::string::String; #[cfg(not(no_global_oom_handling))] use crate::vec::Vec; -#[cfg(test)] -mod tests; - // This is repr(C) to future-proof against possible field-reordering, which // would interfere with otherwise safe [into|from]_raw() of transmutable // inner types. @@ -309,7 +304,7 @@ fn rc_inner_layout_for_value_layout(layout: Layout) -> Layout { /// /// [get_mut]: Rc::get_mut #[doc(search_unbox)] -#[cfg_attr(not(test), rustc_diagnostic_item = "Rc")] +#[rustc_diagnostic_item = "Rc"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] pub struct Rc< @@ -1332,11 +1327,14 @@ impl Rc { /// /// # Safety /// - /// The pointer must have been obtained through `Rc::into_raw`, the - /// associated `Rc` instance must be valid (i.e. the strong count must be at + /// The pointer must have been obtained through `Rc::into_raw` and must satisfy the + /// same layout requirements specified in [`Rc::from_raw_in`][from_raw_in]. + /// The associated `Rc` instance must be valid (i.e. the strong count must be at /// least 1) for the duration of this method, and `ptr` must point to a block of memory /// allocated by the global allocator. /// + /// [from_raw_in]: Rc::from_raw_in + /// /// # Examples /// /// ``` @@ -1365,12 +1363,15 @@ impl Rc { /// /// # Safety /// - /// The pointer must have been obtained through `Rc::into_raw`, the - /// associated `Rc` instance must be valid (i.e. the strong count must be at + /// The pointer must have been obtained through `Rc::into_raw`and must satisfy the + /// same layout requirements specified in [`Rc::from_raw_in`][from_raw_in]. + /// The associated `Rc` instance must be valid (i.e. the strong count must be at /// least 1) when invoking this method, and `ptr` must point to a block of memory /// allocated by the global allocator. This method can be used to release the final `Rc` and /// backing storage, but **should not** be called after the final `Rc` has been released. /// + /// [from_raw_in]: Rc::from_raw_in + /// /// # Examples /// /// ``` @@ -1628,10 +1629,13 @@ impl Rc { /// /// # Safety /// - /// The pointer must have been obtained through `Rc::into_raw`, the - /// associated `Rc` instance must be valid (i.e. the strong count must be at + /// The pointer must have been obtained through `Rc::into_raw` and must satisfy the + /// same layout requirements specified in [`Rc::from_raw_in`][from_raw_in]. + /// The associated `Rc` instance must be valid (i.e. the strong count must be at /// least 1) for the duration of this method, and `ptr` must point to a block of memory - /// allocated by `alloc` + /// allocated by `alloc`. + /// + /// [from_raw_in]: Rc::from_raw_in /// /// # Examples /// @@ -1670,11 +1674,14 @@ impl Rc { /// /// # Safety /// - /// The pointer must have been obtained through `Rc::into_raw`, the - /// associated `Rc` instance must be valid (i.e. the strong count must be at + /// The pointer must have been obtained through `Rc::into_raw`and must satisfy the + /// same layout requirements specified in [`Rc::from_raw_in`][from_raw_in]. + /// The associated `Rc` instance must be valid (i.e. the strong count must be at /// least 1) when invoking this method, and `ptr` must point to a block of memory - /// allocated by `alloc`. This method can be used to release the final `Rc` and backing storage, - /// but **should not** be called after the final `Rc` has been released. + /// allocated by `alloc`. This method can be used to release the final `Rc` and + /// backing storage, but **should not** be called after the final `Rc` has been released. + /// + /// [from_raw_in]: Rc::from_raw_in /// /// # Examples /// @@ -2333,6 +2340,9 @@ impl Clone for Rc { } } +#[unstable(feature = "ergonomic_clones", issue = "132290")] +impl UseCloned for Rc {} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for Rc { @@ -2984,7 +2994,7 @@ impl> ToRcSlice for I { /// /// [`upgrade`]: Weak::upgrade #[stable(feature = "rc_weak", since = "1.4.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "RcWeak")] +#[rustc_diagnostic_item = "RcWeak"] pub struct Weak< T: ?Sized, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, @@ -3496,6 +3506,9 @@ impl Clone for Weak { } } +#[unstable(feature = "ergonomic_clones", issue = "132290")] +impl UseCloned for Weak {} + #[stable(feature = "rc_weak", since = "1.4.0")] impl fmt::Debug for Weak { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -3523,11 +3536,11 @@ impl Default for Weak { } } -// NOTE: We checked_add here to deal with mem::forget safely. In particular -// if you mem::forget Rcs (or Weaks), the ref-count can overflow, and then -// you can free the allocation while outstanding Rcs (or Weaks) exist. -// We abort because this is such a degenerate scenario that we don't care about -// what happens -- no real program should ever experience this. +// NOTE: If you mem::forget Rcs (or Weaks), drop is skipped and the ref-count +// is not decremented, meaning the ref-count can overflow, and then you can +// free the allocation while outstanding Rcs (or Weaks) exist, which would be +// unsound. We abort because this is such a degenerate scenario that we don't +// care about what happens -- no real program should ever experience this. // // This should have negligible overhead since you don't actually need to // clone these much in Rust thanks to ownership and move-semantics. diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs deleted file mode 100644 index 2210a7c24c06a..0000000000000 --- a/library/alloc/src/rc/tests.rs +++ /dev/null @@ -1,664 +0,0 @@ -use std::cell::RefCell; -use std::clone::Clone; - -use super::*; - -#[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::new(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_inner() { - let x = Rc::new(3); - assert_eq!(Rc::into_inner(x), Some(3)); - - let x = Rc::new(4); - let y = Rc::clone(&x); - assert_eq!(Rc::into_inner(x), None); - assert_eq!(Rc::into_inner(y), Some(4)); - - let x = Rc::new(5); - let _w = Rc::downgrade(&x); - assert_eq!(Rc::into_inner(x), Some(5)); -} - -#[test] -fn into_from_raw() { - let x = Rc::new(Box::new("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 into_from_weak_raw() { - let x = Rc::new(Box::new("hello")); - let y = Rc::downgrade(&x); - - let y_ptr = Weak::into_raw(y); - unsafe { - assert_eq!(**y_ptr, "hello"); - - let y = Weak::from_raw(y_ptr); - let y_up = Weak::upgrade(&y).unwrap(); - assert_eq!(**y_up, "hello"); - drop(y_up); - - assert_eq!(Rc::try_unwrap(x).map(|x| *x), Ok("hello")); - } -} - -#[test] -fn test_into_from_weak_raw_unsized() { - use std::fmt::Display; - use std::string::ToString; - - let arc: Rc = Rc::from("foo"); - let weak: Weak = Rc::downgrade(&arc); - - let ptr = Weak::into_raw(weak.clone()); - let weak2 = unsafe { Weak::from_raw(ptr) }; - - assert_eq!(unsafe { &*ptr }, "foo"); - assert!(weak.ptr_eq(&weak2)); - - let arc: Rc = Rc::new(123); - let weak: Weak = Rc::downgrade(&arc); - - let ptr = Weak::into_raw(weak.clone()); - let weak2 = unsafe { Weak::from_raw(ptr) }; - - assert_eq!(unsafe { &*ptr }.to_string(), "123"); - assert!(weak.ptr_eq(&weak2)); -} - -#[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()); -} - -/// This is similar to the doc-test for `Rc::make_mut()`, but on an unsized type (slice). -#[test] -fn test_cowrc_unsized() { - use std::rc::Rc; - - let mut data: Rc<[i32]> = Rc::new([10, 20, 30]); - - Rc::make_mut(&mut data)[0] += 1; // Won't clone anything - let mut other_data = Rc::clone(&data); // Won't clone inner data - Rc::make_mut(&mut data)[1] += 1; // Clones inner data - Rc::make_mut(&mut data)[2] += 1; // Won't clone anything - Rc::make_mut(&mut other_data)[0] *= 10; // Won't clone anything - - // Now `data` and `other_data` point to different allocations. - assert_eq!(*data, [11, 21, 31]); - assert_eq!(*other_data, [110, 20, 30]); -} - -#[test] -fn test_show() { - let foo = Rc::new(75); - 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_maybe_thin_unsized() { - // If/when custom thin DSTs exist, this test should be updated to use one - use std::ffi::CStr; - - let x: Rc = Rc::from(c"swordfish"); - assert_eq!(format!("{x:?}"), "\"swordfish\""); - let y: Weak = Rc::downgrade(&x); - drop(x); - - // At this point, the weak points to a dropped DST - assert!(y.upgrade().is_none()); - // But we still need to be able to get the alloc layout to drop. - // CStr has no drop glue, but custom DSTs might, and need to work. - drop(y); -} - -#[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::new(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(); - assert_eq!((&&&s).as_str(), "foo"); - - let r: Rc = Rc::from(s); - assert_eq!((&r).as_str(), "foo"); - assert_eq!(r.as_str(), "foo"); - - 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::new(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::new(()); - 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); -} - -#[test] -fn test_unique_rc_weak() { - let rc = UniqueRc::new(42); - let weak = UniqueRc::downgrade(&rc); - assert!(weak.upgrade().is_none()); - - let _rc = UniqueRc::into_rc(rc); - assert_eq!(*weak.upgrade().unwrap(), 42); -} - -#[test] -fn test_unique_rc_drop_weak() { - let rc = UniqueRc::new(42); - let weak = UniqueRc::downgrade(&rc); - mem::drop(weak); - - let rc = UniqueRc::into_rc(rc); - assert_eq!(*rc, 42); -} - -#[test] -fn test_unique_rc_drops_contents() { - let mut dropped = false; - struct DropMe<'a>(&'a mut bool); - impl Drop for DropMe<'_> { - fn drop(&mut self) { - *self.0 = true; - } - } - { - let rc = UniqueRc::new(DropMe(&mut dropped)); - drop(rc); - } - assert!(dropped); -} - -/// Exercise the non-default allocator usage. -#[test] -fn test_unique_rc_with_alloc_drops_contents() { - let mut dropped = false; - struct DropMe<'a>(&'a mut bool); - impl Drop for DropMe<'_> { - fn drop(&mut self) { - *self.0 = true; - } - } - { - let rc = UniqueRc::new_in(DropMe(&mut dropped), std::alloc::System); - drop(rc); - } - assert!(dropped); -} - -#[test] -fn test_unique_rc_weak_clone_holding_ref() { - let mut v = UniqueRc::new(0u8); - let w = UniqueRc::downgrade(&v); - let r = &mut *v; - let _ = w.clone(); // touch weak count - *r = 123; -} - -#[test] -fn test_unique_rc_unsizing_coercion() { - let mut rc: UniqueRc<[u8]> = UniqueRc::new([0u8; 3]); - assert_eq!(rc.len(), 3); - rc[0] = 123; - let rc: Rc<[u8]> = UniqueRc::into_rc(rc); - assert_eq!(*rc, [123, 0, 0]); -} diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index dcd95ddf00ff5..b49b3f41a7676 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -8,15 +8,12 @@ //! A few functions are provided to create a slice from a value reference //! or from a raw pointer. #![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}; #[cfg(not(no_global_oom_handling))] use core::cmp::Ordering::{self, Less}; #[cfg(not(no_global_oom_handling))] -use core::mem::{self, MaybeUninit}; +use core::mem::MaybeUninit; #[cfg(not(no_global_oom_handling))] use core::ptr; #[unstable(feature = "array_chunks", issue = "74985")] @@ -63,16 +60,6 @@ pub use core::slice::{range, try_range}; //////////////////////////////////////////////////////////////////////////////// // 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; - use crate::alloc::Allocator; #[cfg(not(no_global_oom_handling))] use crate::alloc::Global; @@ -81,100 +68,8 @@ use crate::borrow::ToOwned; use crate::boxed::Box; use crate::vec::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 -#[allow(unreachable_pub)] // cfg(test) pub above -pub(crate) mod hack { - use core::alloc::Allocator; - - 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. - #[allow(missing_docs)] - pub fn into_vec(b: Box<[T], A>) -> Vec { - unsafe { - let len = b.len(); - let (b, alloc) = Box::into_raw_with_allocator(b); - Vec::from_raw_parts_in(b as *mut T, len, len, alloc) - } - } - - #[cfg(not(no_global_oom_handling))] - #[allow(missing_docs)] - #[inline] - pub fn to_vec(s: &[T], alloc: A) -> Vec { - T::to_vec(s, alloc) - } - - #[cfg(not(no_global_oom_handling))] - pub trait ConvertVec { - fn to_vec(s: &[Self], alloc: A) -> Vec - where - Self: Sized; - } - - #[cfg(not(no_global_oom_handling))] - impl ConvertVec for T { - #[inline] - default fn to_vec(s: &[Self], alloc: A) -> Vec { - struct DropGuard<'a, T, A: Allocator> { - vec: &'a mut Vec, - num_init: usize, - } - impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { - #[inline] - fn drop(&mut self) { - // SAFETY: - // items were marked initialized in the loop below - unsafe { - self.vec.set_len(self.num_init); - } - } - } - let mut vec = Vec::with_capacity_in(s.len(), alloc); - let mut guard = DropGuard { vec: &mut vec, num_init: 0 }; - let slots = guard.vec.spare_capacity_mut(); - // .take(slots.len()) is necessary for LLVM to remove bounds checks - // and has better codegen than zip. - for (i, b) in s.iter().enumerate().take(slots.len()) { - guard.num_init = i; - slots[i].write(b.clone()); - } - core::mem::forget(guard); - // SAFETY: - // the vec was allocated and initialized above to at least this length. - unsafe { - vec.set_len(s.len()); - } - vec - } - } - - #[cfg(not(no_global_oom_handling))] - impl ConvertVec for T { - #[inline] - fn to_vec(s: &[Self], alloc: A) -> Vec { - let mut v = Vec::with_capacity_in(s.len(), alloc); - // SAFETY: - // allocated above with the capacity of `s`, and initialize to `s.len()` in - // ptr::copy_to_non_overlapping below. - unsafe { - s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), s.len()); - v.set_len(s.len()); - } - v - } - } -} - -#[cfg(not(test))] impl [T] { - /// Sorts the slice, preserving initial order of equal elements. + /// Sorts the slice in ascending order, preserving initial order of equal elements. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) /// worst-case. @@ -242,7 +137,8 @@ impl [T] { stable_sort(self, T::lt); } - /// Sorts the slice with a comparison function, preserving initial order of equal elements. + /// Sorts the slice in ascending order with a comparison function, preserving initial order of + /// equal elements. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) /// worst-case. @@ -302,7 +198,8 @@ impl [T] { stable_sort(self, |a, b| compare(a, b) == Less); } - /// Sorts the slice with a key extraction function, preserving initial order of equal elements. + /// Sorts the slice in ascending order with a key extraction function, preserving initial order + /// of equal elements. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* \* log(*n*)) /// worst-case, where the key function is *O*(*m*). @@ -357,7 +254,8 @@ impl [T] { stable_sort(self, |a, b| f(a).lt(&f(b))); } - /// Sorts the slice with a key extraction function, preserving initial order of equal elements. + /// Sorts the slice in ascending order with a key extraction function, preserving initial order + /// of equal elements. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* + *n* \* /// log(*n*)) worst-case, where the key function is *O*(*m*). @@ -446,7 +344,7 @@ impl [T] { // Avoids binary-size usage in cases where the alignment doesn't work out to make this // beneficial or on 32-bit platforms. let is_using_u32_as_idx_type_helpful = - const { mem::size_of::<(K, u32)>() < mem::size_of::<(K, usize)>() }; + const { size_of::<(K, u32)>() < size_of::<(K, usize)>() }; // It's possible to instantiate this for u8 and u16 but, doing so is very wasteful in terms // of compile-times and binary-size, the peak saved heap memory for u16 is (u8 + u16) -> 4 @@ -501,8 +399,64 @@ impl [T] { where T: Clone, { - // N.B., see the `hack` module in this file for more details. - hack::to_vec(self, alloc) + return T::to_vec(self, alloc); + + trait ConvertVec { + fn to_vec(s: &[Self], alloc: A) -> Vec + where + Self: Sized; + } + + impl ConvertVec for T { + #[inline] + default fn to_vec(s: &[Self], alloc: A) -> Vec { + struct DropGuard<'a, T, A: Allocator> { + vec: &'a mut Vec, + num_init: usize, + } + impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { + #[inline] + fn drop(&mut self) { + // SAFETY: + // items were marked initialized in the loop below + unsafe { + self.vec.set_len(self.num_init); + } + } + } + let mut vec = Vec::with_capacity_in(s.len(), alloc); + let mut guard = DropGuard { vec: &mut vec, num_init: 0 }; + let slots = guard.vec.spare_capacity_mut(); + // .take(slots.len()) is necessary for LLVM to remove bounds checks + // and has better codegen than zip. + for (i, b) in s.iter().enumerate().take(slots.len()) { + guard.num_init = i; + slots[i].write(b.clone()); + } + core::mem::forget(guard); + // SAFETY: + // the vec was allocated and initialized above to at least this length. + unsafe { + vec.set_len(s.len()); + } + vec + } + } + + impl ConvertVec for T { + #[inline] + fn to_vec(s: &[Self], alloc: A) -> Vec { + let mut v = Vec::with_capacity_in(s.len(), alloc); + // SAFETY: + // allocated above with the capacity of `s`, and initialize to `s.len()` in + // ptr::copy_to_non_overlapping below. + unsafe { + s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), s.len()); + v.set_len(s.len()); + } + v + } + } } /// Converts `self` into a vector without clones or allocation. @@ -522,10 +476,13 @@ impl [T] { #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[inline] - #[cfg_attr(not(test), rustc_diagnostic_item = "slice_into_vec")] + #[rustc_diagnostic_item = "slice_into_vec"] pub fn into_vec(self: Box) -> Vec { - // N.B., see the `hack` module in this file for more details. - hack::into_vec(self) + unsafe { + let len = self.len(); + let (b, alloc) = Box::into_raw_with_allocator(self); + Vec::from_raw_parts_in(b as *mut T, len, len, alloc) + } } /// Creates a vector by copying a slice `n` times. @@ -666,7 +623,6 @@ impl [T] { } } -#[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. @@ -883,14 +839,9 @@ impl SpecCloneIntoVec for [T] { #[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, Global) + self.to_vec() } fn clone_into(&self, target: &mut Vec) { diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 6fee8d3fe3346..8766fd904b074 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -219,7 +219,6 @@ impl ToOwned for str { } /// Methods for string slices. -#[cfg(not(test))] impl str { /// Converts a `Box` into a `Box<[u8]>` without copying or allocating. /// @@ -235,7 +234,7 @@ impl str { #[stable(feature = "str_box_extras", since = "1.20.0")] #[must_use = "`self` will be dropped if the result is not used"] #[inline] - pub fn into_boxed_bytes(self: Box) -> Box<[u8]> { + pub fn into_boxed_bytes(self: Box) -> Box<[u8]> { self.into() } @@ -502,7 +501,7 @@ impl str { #[rustc_allow_incoherent_impl] #[must_use = "`self` will be dropped if the result is not used"] #[inline] - pub fn into_string(self: Box) -> String { + pub fn into_string(self: Box) -> String { let slice = Box::<[u8]>::from(self); unsafe { String::from_utf8_unchecked(slice.into_vec()) } } @@ -604,6 +603,10 @@ impl str { /// Converts a boxed slice of bytes to a boxed string slice without checking /// that the string contains valid UTF-8. /// +/// # Safety +/// +/// * The provided bytes must contain a valid UTF-8 sequence. +/// /// # Examples /// /// ``` @@ -631,7 +634,6 @@ pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box { #[unstable(feature = "str_internals", issue = "none")] #[doc(hidden)] #[inline] -#[cfg(not(test))] #[cfg(not(no_global_oom_handling))] pub fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, &str) { // Process the input in chunks of 16 bytes to enable auto-vectorization. @@ -704,7 +706,6 @@ pub fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, &str) { } } #[inline] -#[cfg(not(test))] #[cfg(not(no_global_oom_handling))] #[allow(dead_code)] /// Faster implementation of string replacement for ASCII to ASCII cases. diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index f10ef1fca1377..37614a7ca4571 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -119,8 +119,6 @@ use crate::vec::{self, Vec}; /// the same `char`s: /// /// ``` -/// use std::mem; -/// /// // `s` is ASCII which represents each `char` as one byte /// let s = "hello"; /// assert_eq!(s.len(), 5); @@ -128,7 +126,7 @@ use crate::vec::{self, Vec}; /// // A `char` array with the same contents would be longer because /// // every `char` is four bytes /// let s = ['h', 'e', 'l', 'l', 'o']; -/// let size: usize = s.into_iter().map(|c| mem::size_of_val(&c)).sum(); +/// let size: usize = s.into_iter().map(|c| size_of_val(&c)).sum(); /// assert_eq!(size, 20); /// /// // However, for non-ASCII strings, the difference will be smaller @@ -137,7 +135,7 @@ use crate::vec::{self, Vec}; /// assert_eq!(s.len(), 20); /// /// let s = ['💖', '💖', '💖', '💖', '💖']; -/// let size: usize = s.into_iter().map(|c| mem::size_of_val(&c)).sum(); +/// let size: usize = s.into_iter().map(|c| size_of_val(&c)).sum(); /// assert_eq!(size, 20); /// ``` /// @@ -358,7 +356,7 @@ use crate::vec::{self, Vec}; /// [`as_str()`]: String::as_str #[derive(PartialEq, PartialOrd, Eq, Ord)] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), lang = "String")] +#[lang = "String"] pub struct String { vec: Vec, } @@ -440,7 +438,7 @@ impl String { /// ``` #[inline] #[rustc_const_stable(feature = "const_string_new", since = "1.39.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "string_new")] + #[rustc_diagnostic_item = "string_new"] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub const fn new() -> String { @@ -503,17 +501,6 @@ impl String { Ok(String { vec: Vec::try_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)] - #[allow(missing_docs)] - 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 @@ -572,7 +559,7 @@ impl String { /// [`into_bytes`]: String::into_bytes #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "string_from_utf8")] + #[rustc_diagnostic_item = "string_from_utf8"] pub fn from_utf8(vec: Vec) -> Result { match str::from_utf8(&vec) { Ok(..) => Ok(String { vec }), @@ -1056,7 +1043,8 @@ impl String { #[inline] #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] pub const fn into_bytes(self) -> Vec { self.vec } @@ -1073,8 +1061,8 @@ impl String { #[inline] #[must_use] #[stable(feature = "string_as_str", since = "1.7.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "string_as_str")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_diagnostic_item = "string_as_str"] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] pub const fn as_str(&self) -> &str { // SAFETY: String contents are stipulated to be valid UTF-8, invalid contents are an error // at construction. @@ -1096,8 +1084,8 @@ impl String { #[inline] #[must_use] #[stable(feature = "string_as_str", since = "1.7.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "string_as_mut_str")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_diagnostic_item = "string_as_mut_str"] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] pub const fn as_mut_str(&mut self) -> &mut str { // SAFETY: String contents are stipulated to be valid UTF-8, invalid contents are an error // at construction. @@ -1119,7 +1107,7 @@ impl String { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("append", "push")] - #[cfg_attr(not(test), rustc_diagnostic_item = "string_push_str")] + #[rustc_diagnostic_item = "string_push_str"] pub fn push_str(&mut self, string: &str) { self.vec.extend_from_slice(string.as_bytes()) } @@ -1134,7 +1122,6 @@ impl String { /// # Examples /// /// ``` - /// #![feature(string_extend_from_within)] /// let mut string = String::from("abcde"); /// /// string.extend_from_within(2..); @@ -1147,7 +1134,7 @@ impl String { /// assert_eq!(string, "abcdecdeabecde"); /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "string_extend_from_within", issue = "103806")] + #[stable(feature = "string_extend_from_within", since = "1.87.0")] pub fn extend_from_within(&mut self, src: R) where R: RangeBounds, @@ -1172,7 +1159,7 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] pub const fn capacity(&self) -> usize { self.vec.capacity() } @@ -1414,11 +1401,14 @@ impl String { #[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; char::MAX_LEN_UTF8]).as_bytes()) - } + let len = self.len(); + let ch_len = ch.len_utf8(); + self.reserve(ch_len); + + // SAFETY: Just reserved capacity for at least the length needed to encode `ch`. + unsafe { + core::char::encode_utf8_raw_unchecked(ch as u32, self.vec.as_mut_ptr().add(self.len())); + self.vec.set_len(len + ch_len); } } @@ -1438,7 +1428,7 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] pub const fn as_bytes(&self) -> &[u8] { self.vec.as_slice() } @@ -1715,24 +1705,31 @@ impl String { #[rustc_confusables("set")] pub fn insert(&mut self, idx: usize, ch: char) { assert!(self.is_char_boundary(idx)); - let mut bits = [0; char::MAX_LEN_UTF8]; - let bits = ch.encode_utf8(&mut bits).as_bytes(); + let len = self.len(); + let ch_len = ch.len_utf8(); + self.reserve(ch_len); + + // SAFETY: Move the bytes starting from `idx` to their new location `ch_len` + // bytes ahead. This is safe because sufficient capacity was reserved, and `idx` + // is a char boundary. unsafe { - self.insert_bytes(idx, bits); + ptr::copy( + self.vec.as_ptr().add(idx), + self.vec.as_mut_ptr().add(idx + ch_len), + len - idx, + ); } - } - #[cfg(not(no_global_oom_handling))] - unsafe fn insert_bytes(&mut self, idx: usize, bytes: &[u8]) { - let len = self.len(); - let amt = bytes.len(); - self.vec.reserve(amt); + // SAFETY: Encode the character into the vacated region if `idx != len`, + // or into the uninitialized spare capacity otherwise. + unsafe { + core::char::encode_utf8_raw_unchecked(ch as u32, self.vec.as_mut_ptr().add(idx)); + } + // SAFETY: Update the length to include the newly added bytes. unsafe { - ptr::copy(self.vec.as_ptr().add(idx), self.vec.as_mut_ptr().add(idx + amt), len - idx); - ptr::copy_nonoverlapping(bytes.as_ptr(), self.vec.as_mut_ptr().add(idx), amt); - self.vec.set_len(len + amt); + self.vec.set_len(len + ch_len); } } @@ -1758,12 +1755,31 @@ impl String { #[cfg(not(no_global_oom_handling))] #[inline] #[stable(feature = "insert_str", since = "1.16.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "string_insert_str")] + #[rustc_diagnostic_item = "string_insert_str"] pub fn insert_str(&mut self, idx: usize, string: &str) { assert!(self.is_char_boundary(idx)); + let len = self.len(); + let amt = string.len(); + self.reserve(amt); + + // SAFETY: Move the bytes starting from `idx` to their new location `amt` bytes + // ahead. This is safe because sufficient capacity was just reserved, and `idx` + // is a char boundary. + unsafe { + ptr::copy(self.vec.as_ptr().add(idx), self.vec.as_mut_ptr().add(idx + amt), len - idx); + } + + // SAFETY: Copy the new string slice into the vacated region if `idx != len`, + // or into the uninitialized spare capacity otherwise. The borrow checker + // ensures that the source and destination do not overlap. unsafe { - self.insert_bytes(idx, string.as_bytes()); + ptr::copy_nonoverlapping(string.as_ptr(), self.vec.as_mut_ptr().add(idx), amt); + } + + // SAFETY: Update the length to include the newly added bytes. + unsafe { + self.vec.set_len(len + amt); } } @@ -1792,7 +1808,7 @@ impl String { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] pub const unsafe fn as_mut_vec(&mut self) -> &mut Vec { &mut self.vec } @@ -1814,8 +1830,9 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] #[rustc_confusables("length", "size")] + #[rustc_no_implicit_autorefs] pub const fn len(&self) -> usize { self.vec.len() } @@ -1834,7 +1851,8 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] + #[rustc_no_implicit_autorefs] pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -2727,7 +2745,7 @@ impl FromStr for String { /// implementation for free. /// /// [`Display`]: fmt::Display -#[cfg_attr(not(test), rustc_diagnostic_item = "ToString")] +#[rustc_diagnostic_item = "ToString"] #[stable(feature = "rust1", since = "1.0.0")] pub trait ToString { /// Converts the given value to a `String`. @@ -2742,7 +2760,7 @@ pub trait ToString { /// ``` #[rustc_conversion_suggestion] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "to_string_method")] + #[rustc_diagnostic_item = "to_string_method"] fn to_string(&self) -> String; } @@ -2808,7 +2826,54 @@ impl SpecToString for bool { } } +macro_rules! impl_to_string { + ($($signed:ident, $unsigned:ident,)*) => { + $( + #[cfg(not(no_global_oom_handling))] + #[cfg(not(feature = "optimize_for_size"))] + impl SpecToString for $signed { + #[inline] + fn spec_to_string(&self) -> String { + const SIZE: usize = $signed::MAX.ilog(10) as usize + 1; + let mut buf = [core::mem::MaybeUninit::::uninit(); SIZE]; + // Only difference between signed and unsigned are these 8 lines. + let mut out; + if *self < 0 { + out = String::with_capacity(SIZE + 1); + out.push('-'); + } else { + out = String::with_capacity(SIZE); + } + + out.push_str(self.unsigned_abs()._fmt(&mut buf)); + out + } + } + #[cfg(not(no_global_oom_handling))] + #[cfg(not(feature = "optimize_for_size"))] + impl SpecToString for $unsigned { + #[inline] + fn spec_to_string(&self) -> String { + const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1; + let mut buf = [core::mem::MaybeUninit::::uninit(); SIZE]; + + self._fmt(&mut buf).to_string() + } + } + )* + } +} + +impl_to_string! { + i8, u8, + i16, u16, + i32, u32, + i64, u64, + isize, usize, +} + #[cfg(not(no_global_oom_handling))] +#[cfg(feature = "optimize_for_size")] impl SpecToString for u8 { #[inline] fn spec_to_string(&self) -> String { @@ -2828,6 +2893,7 @@ impl SpecToString for u8 { } #[cfg(not(no_global_oom_handling))] +#[cfg(feature = "optimize_for_size")] impl SpecToString for i8 { #[inline] fn spec_to_string(&self) -> String { @@ -2982,7 +3048,6 @@ impl From<&String> for String { } // note: test pulls in std, 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`]. @@ -3154,7 +3219,7 @@ impl From for Vec { } } -#[stable(feature = "try_from_vec_u8_for_string", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "try_from_vec_u8_for_string", since = "1.87.0")] impl TryFrom> for String { type Error = FromUtf8Error; /// Converts the given [`Vec`] into a [`String`] if it contains valid UTF-8 data. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index dba1449347ac0..17090925cfa0c 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -11,6 +11,7 @@ use core::any::Any; #[cfg(not(no_global_oom_handling))] use core::clone::CloneToUninit; +use core::clone::UseCloned; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::intrinsics::abort; @@ -19,14 +20,14 @@ use core::iter; use core::marker::{PhantomData, Unsize}; use core::mem::{self, ManuallyDrop, align_of_val_raw}; use core::num::NonZeroUsize; -use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, LegacyReceiver}; +use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::pin::{Pin, PinCoerceUnsized}; use core::ptr::{self, NonNull}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; -use core::sync::atomic; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use core::sync::atomic::{self, Atomic}; use core::{borrow, fmt, hint}; #[cfg(not(no_global_oom_handling))] @@ -83,9 +84,29 @@ macro_rules! acquire { /// /// 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. +/// inside an `Arc`. If you do need to mutate through an `Arc`, you have several options: +/// +/// 1. Use interior mutability with synchronization primitives like [`Mutex`][mutex], +/// [`RwLock`][rwlock], or one of the [`Atomic`][atomic] types. +/// +/// 2. Use clone-on-write semantics with [`Arc::make_mut`] which provides efficient mutation +/// without requiring interior mutability. This approach clones the data only when +/// needed (when there are multiple references) and can be more efficient when mutations +/// are infrequent. +/// +/// 3. Use [`Arc::get_mut`] when you know your `Arc` is not shared (has a reference count of 1), +/// which provides direct mutable access to the inner value without any cloning. +/// +/// ``` +/// use std::sync::Arc; +/// +/// let mut data = Arc::new(vec![1, 2, 3]); +/// +/// // This will clone the vector only if there are other references to it +/// Arc::make_mut(&mut data).push(4); +/// +/// assert_eq!(*data, vec![1, 2, 3, 4]); +/// ``` /// /// **Note**: This type is only available on platforms that support atomic /// loads and stores of pointers, which includes all platforms that support @@ -234,7 +255,7 @@ macro_rules! acquire { /// /// [rc_examples]: crate::rc#examples #[doc(search_unbox)] -#[cfg_attr(not(test), rustc_diagnostic_item = "Arc")] +#[rustc_diagnostic_item = "Arc"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] pub struct Arc< @@ -311,7 +332,7 @@ impl Arc { /// /// [`upgrade`]: Weak::upgrade #[stable(feature = "arc_weak", since = "1.4.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "ArcWeak")] +#[rustc_diagnostic_item = "ArcWeak"] pub struct Weak< T: ?Sized, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, @@ -348,12 +369,12 @@ impl fmt::Debug for Weak { // inner types. #[repr(C)] struct ArcInner { - strong: atomic::AtomicUsize, + strong: Atomic, // 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, + weak: Atomic, data: T, } @@ -1452,11 +1473,14 @@ impl Arc { /// /// # 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 + /// The pointer must have been obtained through `Arc::into_raw` and must satisfy the + /// same layout requirements specified in [`Arc::from_raw_in`][from_raw_in]. + /// The associated `Arc` instance must be valid (i.e. the strong count must be at /// least 1) for the duration of this method, and `ptr` must point to a block of memory /// allocated by the global allocator. /// + /// [from_raw_in]: Arc::from_raw_in + /// /// # Examples /// /// ``` @@ -1487,13 +1511,16 @@ impl Arc { /// /// # 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 + /// The pointer must have been obtained through `Arc::into_raw` and must satisfy the + /// same layout requirements specified in [`Arc::from_raw_in`][from_raw_in]. + /// The associated `Arc` instance must be valid (i.e. the strong count must be at /// least 1) when invoking this method, and `ptr` must point to a block of memory /// allocated by the global allocator. 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. /// + /// [from_raw_in]: Arc::from_raw_in + /// /// # Examples /// /// ``` @@ -1805,11 +1832,14 @@ impl Arc { /// /// # 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,, and `ptr` must point to a block of memory + /// The pointer must have been obtained through `Arc::into_raw` and must satisfy the + /// same layout requirements specified in [`Arc::from_raw_in`][from_raw_in]. + /// The associated `Arc` instance must be valid (i.e. the strong count must be at + /// least 1) for the duration of this method, and `ptr` must point to a block of memory /// allocated by `alloc`. /// + /// [from_raw_in]: Arc::from_raw_in + /// /// # Examples /// /// ``` @@ -1849,13 +1879,16 @@ impl Arc { /// /// # Safety /// - /// The pointer must have been obtained through `Arc::into_raw`, the - /// associated `Arc` instance must be valid (i.e. the strong count must be at + /// The pointer must have been obtained through `Arc::into_raw` and must satisfy the + /// same layout requirements specified in [`Arc::from_raw_in`][from_raw_in]. + /// The associated `Arc` instance must be valid (i.e. the strong count must be at /// least 1) when invoking this method, and `ptr` must point to a block of memory /// allocated by `alloc`. 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. /// + /// [from_raw_in]: Arc::from_raw_in + /// /// # Examples /// /// ``` @@ -2197,6 +2230,9 @@ impl Clone for Arc { } } +#[unstable(feature = "ergonomic_clones", issue = "132290")] +impl UseCloned for Arc {} + #[stable(feature = "rust1", since = "1.0.0")] impl Deref for Arc { type Target = T; @@ -2274,7 +2310,7 @@ impl Arc { #[inline] #[stable(feature = "arc_unique", since = "1.4.0")] pub fn make_mut(this: &mut Self) -> &mut T { - let size_of_val = mem::size_of_val::(&**this); + let size_of_val = size_of_val::(&**this); // Note that we hold both a strong reference and a weak reference. // Thus, releasing our strong reference only will not, by itself, cause @@ -2410,7 +2446,7 @@ impl Arc { #[inline] #[stable(feature = "arc_unique", since = "1.4.0")] pub fn get_mut(this: &mut Self) -> Option<&mut T> { - if this.is_unique() { + if Self::is_unique(this) { // 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 @@ -2490,11 +2526,64 @@ impl Arc { unsafe { &mut (*this.ptr.as_ptr()).data } } - /// Determine whether this is the unique reference (including weak refs) to - /// the underlying data. + /// Determine whether this is the unique reference to the underlying data. + /// + /// Returns `true` if there are no other `Arc` or [`Weak`] pointers to the same allocation; + /// returns `false` otherwise. + /// + /// If this function returns `true`, then is guaranteed to be safe to call [`get_mut_unchecked`] + /// on this `Arc`, so long as no clones occur in between. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_is_unique)] + /// + /// use std::sync::Arc; + /// + /// let x = Arc::new(3); + /// assert!(Arc::is_unique(&x)); + /// + /// let y = Arc::clone(&x); + /// assert!(!Arc::is_unique(&x)); + /// drop(y); + /// + /// // Weak references also count, because they could be upgraded at any time. + /// let z = Arc::downgrade(&x); + /// assert!(!Arc::is_unique(&x)); + /// ``` + /// + /// # Pointer invalidation + /// + /// This function will always return the same value as `Arc::get_mut(arc).is_some()`. However, + /// unlike that operation it does not produce any mutable references to the underlying data, + /// meaning no pointers to the data inside the `Arc` are invalidated by the call. Thus, the + /// following code is valid, even though it would be UB if it used `Arc::get_mut`: /// - /// Note that this requires locking the weak ref count. - fn is_unique(&mut self) -> bool { + /// ``` + /// #![feature(arc_is_unique)] + /// + /// use std::sync::Arc; + /// + /// let arc = Arc::new(5); + /// let pointer: *const i32 = &*arc; + /// assert!(Arc::is_unique(&arc)); + /// assert_eq!(unsafe { *pointer }, 5); + /// ``` + /// + /// # Atomic orderings + /// + /// Concurrent drops to other `Arc` pointers to the same allocation will synchronize with this + /// call - that is, this call performs an `Acquire` operation on the underlying strong and weak + /// ref counts. This ensures that calling `get_mut_unchecked` is safe. + /// + /// Note that this operation requires locking the weak ref count, so concurrent calls to + /// `downgrade` may spin-loop for a short period of time. + /// + /// [`get_mut_unchecked`]: Self::get_mut_unchecked + #[inline] + #[unstable(feature = "arc_is_unique", issue = "138938")] + pub fn is_unique(this: &Self) -> bool { // lock the weak pointer count if we appear to be the sole weak pointer // holder. // @@ -2502,16 +2591,16 @@ impl Arc { // 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() { + if this.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; + let unique = this.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 + this.inner().weak.store(1, Release); // release the lock unique } else { false @@ -2724,8 +2813,8 @@ impl Weak { /// 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, + weak: &'a Atomic, + strong: &'a Atomic, } impl Weak { @@ -3158,6 +3247,9 @@ impl Clone for Weak { } } +#[unstable(feature = "ergonomic_clones", issue = "132290")] +impl UseCloned for Weak {} + #[stable(feature = "downgraded_weak", since = "1.10.0")] impl Default for Weak { /// Constructs a new `Weak`, without allocating memory. @@ -3544,7 +3636,7 @@ impl Default for Arc<[T]> { /// This may or may not share an allocation with other Arcs. #[inline] fn default() -> Self { - if mem::align_of::() <= MAX_STATIC_INNER_SLICE_ALIGNMENT { + if align_of::() <= MAX_STATIC_INNER_SLICE_ALIGNMENT { // We take a reference to the whole struct instead of the ArcInner<[u8; 1]> inside it so // we don't shrink the range of bytes the ptr is allowed to access under Stacked Borrows. // (Miri complains on 32-bit targets with Arc<[Align16]> otherwise.) @@ -4027,3 +4119,413 @@ impl core::error::Error for Arc { core::error::Error::provide(&**self, req); } } + +/// A uniquely owned [`Arc`]. +/// +/// This represents an `Arc` that is known to be uniquely owned -- that is, have exactly one strong +/// reference. Multiple weak pointers can be created, but attempts to upgrade those to strong +/// references will fail unless the `UniqueArc` they point to has been converted into a regular `Arc`. +/// +/// Because it is uniquely owned, the contents of a `UniqueArc` can be freely mutated. A common +/// use case is to have an object be mutable during its initialization phase but then have it become +/// immutable and converted to a normal `Arc`. +/// +/// This can be used as a flexible way to create cyclic data structures, as in the example below. +/// +/// ``` +/// #![feature(unique_rc_arc)] +/// use std::sync::{Arc, Weak, UniqueArc}; +/// +/// struct Gadget { +/// me: Weak, +/// } +/// +/// fn create_gadget() -> Option> { +/// let mut rc = UniqueArc::new(Gadget { +/// me: Weak::new(), +/// }); +/// rc.me = UniqueArc::downgrade(&rc); +/// Some(UniqueArc::into_arc(rc)) +/// } +/// +/// create_gadget().unwrap(); +/// ``` +/// +/// An advantage of using `UniqueArc` over [`Arc::new_cyclic`] to build cyclic data structures is that +/// [`Arc::new_cyclic`]'s `data_fn` parameter cannot be async or return a [`Result`]. As shown in the +/// previous example, `UniqueArc` allows for more flexibility in the construction of cyclic data, +/// including fallible or async constructors. +#[unstable(feature = "unique_rc_arc", issue = "112566")] +pub struct UniqueArc< + T: ?Sized, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + ptr: NonNull>, + // Define the ownership of `ArcInner` for drop-check + _marker: PhantomData>, + // Invariance is necessary for soundness: once other `Weak` + // references exist, we already have a form of shared mutability! + _marker2: PhantomData<*mut T>, + alloc: A, +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +unsafe impl Send for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +unsafe impl Sync for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +// #[unstable(feature = "coerce_unsized", issue = "18598")] +impl, U: ?Sized, A: Allocator> CoerceUnsized> + for UniqueArc +{ +} + +//#[unstable(feature = "unique_rc_arc", issue = "112566")] +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +impl, U: ?Sized> DispatchFromDyn> for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl fmt::Display for UniqueArc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl fmt::Debug for UniqueArc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl fmt::Pointer for UniqueArc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&(&raw const **self), f) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl borrow::Borrow for UniqueArc { + fn borrow(&self) -> &T { + &**self + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl borrow::BorrowMut for UniqueArc { + fn borrow_mut(&mut self) -> &mut T { + &mut **self + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl AsRef for UniqueArc { + fn as_ref(&self) -> &T { + &**self + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl AsMut for UniqueArc { + fn as_mut(&mut self) -> &mut T { + &mut **self + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Unpin for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl PartialEq for UniqueArc { + /// Equality for two `UniqueArc`s. + /// + /// Two `UniqueArc`s are equal if their inner values are equal. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// + /// let five = UniqueArc::new(5); + /// + /// assert!(five == UniqueArc::new(5)); + /// ``` + #[inline] + fn eq(&self, other: &Self) -> bool { + PartialEq::eq(&**self, &**other) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl PartialOrd for UniqueArc { + /// Partial comparison for two `UniqueArc`s. + /// + /// The two are compared by calling `partial_cmp()` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// use std::cmp::Ordering; + /// + /// let five = UniqueArc::new(5); + /// + /// assert_eq!(Some(Ordering::Less), five.partial_cmp(&UniqueArc::new(6))); + /// ``` + #[inline(always)] + fn partial_cmp(&self, other: &UniqueArc) -> Option { + (**self).partial_cmp(&**other) + } + + /// Less-than comparison for two `UniqueArc`s. + /// + /// The two are compared by calling `<` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// + /// let five = UniqueArc::new(5); + /// + /// assert!(five < UniqueArc::new(6)); + /// ``` + #[inline(always)] + fn lt(&self, other: &UniqueArc) -> bool { + **self < **other + } + + /// 'Less than or equal to' comparison for two `UniqueArc`s. + /// + /// The two are compared by calling `<=` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// + /// let five = UniqueArc::new(5); + /// + /// assert!(five <= UniqueArc::new(5)); + /// ``` + #[inline(always)] + fn le(&self, other: &UniqueArc) -> bool { + **self <= **other + } + + /// Greater-than comparison for two `UniqueArc`s. + /// + /// The two are compared by calling `>` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// + /// let five = UniqueArc::new(5); + /// + /// assert!(five > UniqueArc::new(4)); + /// ``` + #[inline(always)] + fn gt(&self, other: &UniqueArc) -> bool { + **self > **other + } + + /// 'Greater than or equal to' comparison for two `UniqueArc`s. + /// + /// The two are compared by calling `>=` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// + /// let five = UniqueArc::new(5); + /// + /// assert!(five >= UniqueArc::new(5)); + /// ``` + #[inline(always)] + fn ge(&self, other: &UniqueArc) -> bool { + **self >= **other + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Ord for UniqueArc { + /// Comparison for two `UniqueArc`s. + /// + /// The two are compared by calling `cmp()` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::sync::UniqueArc; + /// use std::cmp::Ordering; + /// + /// let five = UniqueArc::new(5); + /// + /// assert_eq!(Ordering::Less, five.cmp(&UniqueArc::new(6))); + /// ``` + #[inline] + fn cmp(&self, other: &UniqueArc) -> Ordering { + (**self).cmp(&**other) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Eq for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Hash for UniqueArc { + fn hash(&self, state: &mut H) { + (**self).hash(state); + } +} + +impl UniqueArc { + /// Creates a new `UniqueArc`. + /// + /// Weak references to this `UniqueArc` can be created with [`UniqueArc::downgrade`]. Upgrading + /// these weak references will fail before the `UniqueArc` has been converted into an [`Arc`]. + /// After converting the `UniqueArc` into an [`Arc`], any weak references created beforehand will + /// point to the new [`Arc`]. + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + pub fn new(value: T) -> Self { + Self::new_in(value, Global) + } +} + +impl UniqueArc { + /// Creates a new `UniqueArc` in the provided allocator. + /// + /// Weak references to this `UniqueArc` can be created with [`UniqueArc::downgrade`]. Upgrading + /// these weak references will fail before the `UniqueArc` has been converted into an [`Arc`]. + /// After converting the `UniqueArc` into an [`Arc`], any weak references created beforehand will + /// point to the new [`Arc`]. + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + // #[unstable(feature = "allocator_api", issue = "32838")] + pub fn new_in(data: T, alloc: A) -> Self { + let (ptr, alloc) = Box::into_unique(Box::new_in( + ArcInner { + strong: atomic::AtomicUsize::new(0), + // keep one weak reference so if all the weak pointers that are created are dropped + // the UniqueArc still stays valid. + weak: atomic::AtomicUsize::new(1), + data, + }, + alloc, + )); + Self { ptr: ptr.into(), _marker: PhantomData, _marker2: PhantomData, alloc } + } +} + +impl UniqueArc { + /// Converts the `UniqueArc` into a regular [`Arc`]. + /// + /// This consumes the `UniqueArc` and returns a regular [`Arc`] that contains the `value` that + /// is passed to `into_arc`. + /// + /// Any weak references created before this method is called can now be upgraded to strong + /// references. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + pub fn into_arc(this: Self) -> Arc { + let this = ManuallyDrop::new(this); + + // Move the allocator out. + // SAFETY: `this.alloc` will not be accessed again, nor dropped because it is in + // a `ManuallyDrop`. + let alloc: A = unsafe { ptr::read(&this.alloc) }; + + // SAFETY: This pointer was allocated at creation time so we know it is valid. + unsafe { + // Convert our weak reference into a strong reference + (*this.ptr.as_ptr()).strong.store(1, Release); + Arc::from_inner_in(this.ptr, alloc) + } + } +} + +impl UniqueArc { + /// Creates a new weak reference to the `UniqueArc`. + /// + /// Attempting to upgrade this weak reference will fail before the `UniqueArc` has been converted + /// to a [`Arc`] using [`UniqueArc::into_arc`]. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + #[must_use] + pub fn downgrade(this: &Self) -> Weak { + // Using a relaxed ordering is alright here, as knowledge of the + // original reference prevents other threads from erroneously deleting + // the object or converting the object to a normal `Arc`. + // + // Note that we don't need to test if the weak counter is locked because there + // are no such operations like `Arc::get_mut` or `Arc::make_mut` that will lock + // the weak counter. + // + // SAFETY: This pointer was allocated at creation time so we know it is valid. + let old_size = unsafe { (*this.ptr.as_ptr()).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: this.ptr, alloc: this.alloc.clone() } + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Deref for UniqueArc { + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: This pointer was allocated at creation time so we know it is valid. + unsafe { &self.ptr.as_ref().data } + } +} + +// #[unstable(feature = "unique_rc_arc", issue = "112566")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl DerefMut for UniqueArc { + fn deref_mut(&mut self) -> &mut T { + // SAFETY: This pointer was allocated at creation time so we know it is valid. We know we + // have unique ownership and therefore it's safe to make a mutable reference because + // `UniqueArc` owns the only strong reference to itself. + // We also need to be careful to only create a mutable reference to the `data` field, + // as a mutable reference to the entire `ArcInner` would assert uniqueness over the + // ref count fields too, invalidating any attempt by `Weak`s to access the ref count. + unsafe { &mut (*self.ptr.as_ptr()).data } + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +// #[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for UniqueArc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for UniqueArc { + fn drop(&mut self) { + // See `Arc::drop_slow` which drops an `Arc` with a strong count of 0. + // SAFETY: This pointer was allocated at creation time so we know it is valid. + let _weak = Weak { ptr: self.ptr, alloc: &self.alloc }; + + unsafe { ptr::drop_in_place(&mut (*self.ptr.as_ptr()).data) }; + } +} diff --git a/library/alloc/src/vec/extract_if.rs b/library/alloc/src/vec/extract_if.rs index be869553ef4e1..a456d3d9e602d 100644 --- a/library/alloc/src/vec/extract_if.rs +++ b/library/alloc/src/vec/extract_if.rs @@ -1,5 +1,5 @@ use core::ops::{Range, RangeBounds}; -use core::{ptr, slice}; +use core::{fmt, ptr, slice}; use super::Vec; use crate::alloc::{Allocator, Global}; @@ -15,8 +15,7 @@ use crate::alloc::{Allocator, Global}; /// let mut v = vec![0, 1, 2]; /// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(.., |x| *x % 2 == 0); /// ``` -#[stable(feature = "extract_if", since = "CURRENT_RUSTC_VERSION")] -#[derive(Debug)] +#[stable(feature = "extract_if", since = "1.87.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ExtractIf< 'a, @@ -57,7 +56,7 @@ impl<'a, T, F, A: Allocator> ExtractIf<'a, T, F, A> { } } -#[stable(feature = "extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "extract_if", since = "1.87.0")] impl Iterator for ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool, @@ -93,7 +92,7 @@ where } } -#[stable(feature = "extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "extract_if", since = "1.87.0")] impl Drop for ExtractIf<'_, T, F, A> { fn drop(&mut self) { unsafe { @@ -108,3 +107,15 @@ impl Drop for ExtractIf<'_, T, F, A> { } } } + +#[stable(feature = "extract_if", since = "1.87.0")] +impl fmt::Debug for ExtractIf<'_, T, F, A> +where + T: fmt::Debug, + A: Allocator, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let peek = if self.idx < self.end { self.vec.get(self.idx) } else { None }; + f.debug_struct("ExtractIf").field("peek", &peek).finish_non_exhaustive() + } +} diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index dffd85f13aa50..b98a118048f2d 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -171,7 +171,7 @@ const fn in_place_collectible( ) -> bool { // Require matching alignments because an alignment-changing realloc is inefficient on many // system allocators and better implementations would require the unstable Allocator trait. - if const { SRC::IS_ZST || DEST::IS_ZST || mem::align_of::() != mem::align_of::() } { + if const { SRC::IS_ZST || DEST::IS_ZST || align_of::() != align_of::() } { return false; } @@ -181,7 +181,7 @@ const fn in_place_collectible( // e.g. // - 1 x [u8; 4] -> 4x u8, via flatten // - 4 x u8 -> 1x [u8; 4], via array_chunks - mem::size_of::() * step_merge.get() >= mem::size_of::() * step_expand.get() + size_of::() * step_merge.get() >= size_of::() * step_expand.get() } // Fall back to other from_iter impls if an overflow occurred in the step merge/expansion // tracking. @@ -190,7 +190,7 @@ const fn in_place_collectible( } const fn needs_realloc(src_cap: usize, dst_cap: usize) -> bool { - if const { mem::align_of::() != mem::align_of::() } { + if const { align_of::() != align_of::() } { // FIXME(const-hack): use unreachable! once that works in const panic!("in_place_collectible() prevents this"); } @@ -199,8 +199,8 @@ const fn needs_realloc(src_cap: usize, dst_cap: usize) -> bool { // the caller will have calculated a `dst_cap` that is an integer multiple of // `src_cap` without remainder. if const { - let src_sz = mem::size_of::(); - let dest_sz = mem::size_of::(); + let src_sz = size_of::(); + let dest_sz = size_of::(); dest_sz != 0 && src_sz % dest_sz == 0 } { return false; @@ -208,7 +208,7 @@ const fn needs_realloc(src_cap: usize, dst_cap: usize) -> bool { // type layouts don't guarantee a fit, so do a runtime check to see if // the allocations happen to match - src_cap > 0 && src_cap * mem::size_of::() != dst_cap * mem::size_of::() + src_cap > 0 && src_cap * size_of::() != dst_cap * size_of::() } /// This provides a shorthand for the source type since local type aliases aren't a thing. @@ -262,7 +262,7 @@ where inner.buf.cast::(), inner.end as *const T, // SAFETY: the multiplication can not overflow, since `inner.cap * size_of::()` is the size of the allocation. - inner.cap.unchecked_mul(mem::size_of::()) / mem::size_of::(), + inner.cap.unchecked_mul(size_of::()) / size_of::(), ) }; @@ -310,14 +310,14 @@ where debug_assert_ne!(dst_cap, 0); unsafe { // The old allocation exists, therefore it must have a valid layout. - let src_align = mem::align_of::(); - let src_size = mem::size_of::().unchecked_mul(src_cap); + let src_align = align_of::(); + let src_size = size_of::().unchecked_mul(src_cap); let old_layout = Layout::from_size_align_unchecked(src_size, src_align); // The allocation must be equal or smaller for in-place iteration to be possible // therefore the new layout must be ≤ the old one and therefore valid. - let dst_align = mem::align_of::(); - let dst_size = mem::size_of::().unchecked_mul(dst_cap); + let dst_align = align_of::(); + let dst_size = size_of::().unchecked_mul(dst_cap); let new_layout = Layout::from_size_align_unchecked(dst_size, dst_align); let result = alloc.shrink(dst_buf.cast(), old_layout, new_layout); @@ -325,7 +325,7 @@ where dst_buf = reallocated.cast::(); } } else { - debug_assert_eq!(src_cap * mem::size_of::(), dst_cap * mem::size_of::()); + debug_assert_eq!(src_cap * size_of::(), dst_cap * size_of::()); } mem::forget(dst_guard); diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 52597e41c1cf8..37df928228d9c 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -168,7 +168,7 @@ impl IntoIter { // SAFETY: This allocation originally came from a `Vec`, so it passes // all those checks. We have `this.buf` ≤ `this.ptr` ≤ `this.end`, - // so the `sub_ptr`s below cannot wrap, and will produce a well-formed + // so the `offset_from_unsigned`s below cannot wrap, and will produce a well-formed // range. `end` ≤ `buf + cap`, so the range will be in-bounds. // Taking `alloc` is ok because nothing else is going to look at it, // since our `Drop` impl isn't going to run so there's no more code. @@ -258,6 +258,11 @@ impl Iterator for IntoIter { self.len() } + #[inline] + fn last(mut self) -> Option { + self.next_back() + } + #[inline] fn next_chunk(&mut self) -> Result<[T; N], core::array::IntoIter> { let mut raw_ary = [const { MaybeUninit::uninit() }; N]; @@ -472,14 +477,9 @@ where #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_into_iter_clone", since = "1.8.0")] impl Clone for IntoIter { - #[cfg(not(test))] fn clone(&self) -> Self { self.as_slice().to_vec_in(self.alloc.deref().clone()).into_iter() } - #[cfg(test)] - fn clone(&self) -> Self { - crate::slice::to_vec(self.as_slice(), self.alloc.deref().clone()).into_iter() - } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 701144cc3af22..59879f23d7850 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -66,7 +66,7 @@ use core::ptr::{self, NonNull}; use core::slice::{self, SliceIndex}; use core::{fmt, intrinsics}; -#[stable(feature = "extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "extract_if", since = "1.87.0")] pub use self::extract_if::ExtractIf; use crate::alloc::{Allocator, Global}; use crate::borrow::{Cow, ToOwned}; @@ -293,7 +293,7 @@ mod spec_extend; /// 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` might not report a [`capacity`] of 0*. `Vec` will allocate if and only -/// if [mem::size_of::\]\() * [capacity]\() > 0. In general, `Vec`'s allocation +/// if [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 @@ -393,7 +393,7 @@ mod spec_extend; /// [capacity]: Vec::capacity /// [`capacity`]: Vec::capacity /// [`Vec::capacity`]: Vec::capacity -/// [mem::size_of::\]: core::mem::size_of +/// [size_of::\]: size_of /// [len]: Vec::len /// [`len`]: Vec::len /// [`push`]: Vec::push @@ -404,7 +404,7 @@ mod spec_extend; /// [owned slice]: Box /// [`into_boxed_slice`]: Vec::into_boxed_slice #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "Vec")] +#[rustc_diagnostic_item = "Vec"] #[rustc_insignificant_dtor] pub struct Vec { buf: RawVec, @@ -428,7 +428,7 @@ impl Vec { /// ``` #[inline] #[rustc_const_stable(feature = "const_vec_new", since = "1.39.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_new")] + #[rustc_diagnostic_item = "vec_new"] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub const fn new() -> Self { @@ -489,7 +489,7 @@ impl Vec { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_with_capacity")] + #[rustc_diagnostic_item = "vec_with_capacity"] #[track_caller] pub fn with_capacity(capacity: usize) -> Self { Self::with_capacity_in(capacity, Global) @@ -1252,9 +1252,22 @@ impl Vec { /// vec.push(42); /// assert!(vec.capacity() >= 10); /// ``` + /// + /// A vector with zero-sized elements will always have a capacity of usize::MAX: + /// + /// ``` + /// #[derive(Clone)] + /// struct ZeroSized; + /// + /// fn main() { + /// assert_eq!(std::mem::size_of::(), 0); + /// let v = vec![ZeroSized; 0]; + /// assert_eq!(v.capacity(), usize::MAX); + /// } + /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] pub const fn capacity(&self) -> usize { self.buf.capacity() } @@ -1279,7 +1292,7 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_reserve")] + #[rustc_diagnostic_item = "vec_reserve"] pub fn reserve(&mut self, additional: usize) { self.buf.reserve(self.len, additional); } @@ -1568,12 +1581,12 @@ impl Vec { /// ``` #[inline] #[stable(feature = "vec_as_slice", since = "1.7.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_as_slice")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_diagnostic_item = "vec_as_slice"] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] pub const fn as_slice(&self) -> &[T] { // SAFETY: `slice::from_raw_parts` requires pointee is a contiguous, aligned buffer of size // `len` containing properly-initialized `T`s. Data must not be mutated for the returned - // lifetime. Further, `len * mem::size_of::` <= `ISIZE::MAX`, and allocation does not + // lifetime. Further, `len * size_of::` <= `isize::MAX`, and allocation does not // "wrap" through overflowing memory addresses. // // * Vec API guarantees that self.buf: @@ -1600,12 +1613,12 @@ impl Vec { /// ``` #[inline] #[stable(feature = "vec_as_slice", since = "1.7.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_as_mut_slice")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_diagnostic_item = "vec_as_mut_slice"] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] pub const fn as_mut_slice(&mut self) -> &mut [T] { // SAFETY: `slice::from_raw_parts_mut` requires pointee is a contiguous, aligned buffer of // size `len` containing properly-initialized `T`s. Data must not be accessed through any - // other pointer for the returned lifetime. Further, `len * mem::size_of::` <= + // other pointer for the returned lifetime. Further, `len * size_of::` <= // `ISIZE::MAX` and allocation does not "wrap" through overflowing memory addresses. // // * Vec API guarantees that self.buf: @@ -1673,7 +1686,7 @@ impl Vec { /// [`as_ptr`]: Vec::as_ptr /// [`as_non_null`]: Vec::as_non_null #[stable(feature = "vec_as_ptr", since = "1.37.0")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] #[rustc_never_returns_null_ptr] #[rustc_as_ptr] #[inline] @@ -1736,7 +1749,7 @@ impl Vec { /// [`as_ptr`]: Vec::as_ptr /// [`as_non_null`]: Vec::as_non_null #[stable(feature = "vec_as_ptr", since = "1.37.0")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] #[rustc_never_returns_null_ptr] #[rustc_as_ptr] #[inline] @@ -1803,10 +1816,10 @@ impl Vec { /// [`as_ptr`]: Vec::as_ptr /// [`as_non_null`]: Vec::as_non_null #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + #[rustc_const_unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] #[inline] - pub fn as_non_null(&mut self) -> NonNull { - // SAFETY: A `Vec` always has a non-null pointer. - unsafe { NonNull::new_unchecked(self.as_mut_ptr()) } + pub const fn as_non_null(&mut self) -> NonNull { + self.buf.non_null() } /// Returns a reference to the underlying allocator. @@ -2511,7 +2524,7 @@ impl Vec { /// Takes *O*(1) time. #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_pop")] + #[rustc_diagnostic_item = "vec_pop"] pub fn pop(&mut self) -> Option { if self.len == 0 { None @@ -2575,7 +2588,7 @@ impl Vec { #[inline] #[track_caller] unsafe fn append_elements(&mut self, other: *const [T]) { - let count = unsafe { (*other).len() }; + let count = other.len(); self.reserve(count); let len = self.len(); unsafe { ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) }; @@ -2687,13 +2700,13 @@ impl Vec { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] #[rustc_confusables("length", "size")] pub const fn len(&self) -> usize { let len = self.len; // SAFETY: The maximum capacity of `Vec` is `isize::MAX` bytes, so the maximum value can - // be returned is `usize::checked_div(mem::size_of::()).unwrap_or(usize::MAX)`, which + // be returned is `usize::checked_div(size_of::()).unwrap_or(usize::MAX)`, which // matches the definition of `T::MAX_SLICE_LEN`. unsafe { intrinsics::assume(len <= T::MAX_SLICE_LEN) }; @@ -2712,8 +2725,8 @@ impl Vec { /// assert!(!v.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_is_empty")] - #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + #[rustc_diagnostic_item = "vec_is_empty"] + #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -2790,6 +2803,10 @@ impl Vec { /// want to use the [`Default`] trait to generate values, you can /// pass [`Default::default`] as the second argument. /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` @@ -2997,6 +3014,10 @@ impl Vec { /// [`Clone`]), use [`Vec::resize_with`]. /// If you only need to resize to a smaller size, use [`Vec::truncate`]. /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` @@ -3193,7 +3214,7 @@ impl Vec { #[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "vec_from_elem")] +#[rustc_diagnostic_item = "vec_from_elem"] #[track_caller] pub fn from_elem(elem: T, n: usize) -> Vec { ::from_elem(elem, n, Global) @@ -3293,23 +3314,12 @@ unsafe impl ops::DerefPure for Vec {} #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Vec { - #[cfg(not(test))] #[track_caller] fn clone(&self) -> Self { let alloc = self.allocator().clone(); <[T]>::to_vec_in(&**self, alloc) } - // 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) -> Self { - let alloc = self.allocator().clone(); - crate::slice::to_vec(&**self, alloc) - } - /// Overwrites the contents of `self` with a clone of the contents of `source`. /// /// This method is preferred over simply assigning `source.clone()` to `self`, @@ -3358,10 +3368,6 @@ impl Hash for Vec { } #[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, A: Allocator> Index for Vec { type Output = I::Output; @@ -3372,10 +3378,6 @@ impl, A: Allocator> Index for Vec { } #[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, A: Allocator> IndexMut for Vec { #[inline] fn index_mut(&mut self, index: I) -> &mut Self::Output { @@ -3657,28 +3659,34 @@ impl Vec { /// /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating /// or the iteration short-circuits, then the remaining elements will be retained. - /// Use [`retain`] with a negated predicate if you do not need the returned iterator. + /// Use [`retain_mut`] with a negated predicate if you do not need the returned iterator. /// - /// [`retain`]: Vec::retain + /// [`retain_mut`]: Vec::retain_mut /// /// Using this method is equivalent to the following code: /// /// ``` - /// # use std::cmp::min; - /// # let some_predicate = |x: &mut i32| { *x == 2 || *x == 3 || *x == 6 }; - /// # let mut vec = vec![1, 2, 3, 4, 5, 6]; - /// # let range = 1..4; + /// # let some_predicate = |x: &mut i32| { *x % 2 == 1 }; + /// # let mut vec = vec![0, 1, 2, 3, 4, 5, 6]; + /// # let mut vec2 = vec.clone(); + /// # let range = 1..5; /// let mut i = range.start; - /// while i < min(vec.len(), range.end) { + /// let end_items = vec.len() - range.end; + /// # let mut extracted = vec![]; + /// + /// while i < vec.len() - end_items { /// if some_predicate(&mut vec[i]) { /// let val = vec.remove(i); + /// # extracted.push(val); /// // your code here /// } else { /// i += 1; /// } /// } /// - /// # assert_eq!(vec, vec![1, 4, 5]); + /// # let extracted2: Vec<_> = vec2.extract_if(range, some_predicate).collect(); + /// # assert_eq!(vec, vec2); + /// # assert_eq!(extracted, extracted2); /// ``` /// /// But `extract_if` is easier to use. `extract_if` is also more efficient, @@ -3713,7 +3721,7 @@ impl Vec { /// assert_eq!(items, vec![0, 0, 0, 0, 0, 0, 0, 2, 2, 2]); /// assert_eq!(ones.len(), 3); /// ``` - #[stable(feature = "extract_if", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "extract_if", since = "1.87.0")] pub fn extract_if(&mut self, range: R, filter: F) -> ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool, @@ -3854,15 +3862,10 @@ impl From<&[T]> for Vec { /// ``` /// assert_eq!(Vec::from(&[1, 2, 3][..]), vec![1, 2, 3]); /// ``` - #[cfg(not(test))] #[track_caller] fn from(s: &[T]) -> Vec { s.to_vec() } - #[cfg(test)] - fn from(s: &[T]) -> Vec { - crate::slice::to_vec(s, Global) - } } #[cfg(not(no_global_oom_handling))] @@ -3875,15 +3878,10 @@ impl From<&mut [T]> for Vec { /// ``` /// assert_eq!(Vec::from(&mut [1, 2, 3][..]), vec![1, 2, 3]); /// ``` - #[cfg(not(test))] #[track_caller] fn from(s: &mut [T]) -> Vec { s.to_vec() } - #[cfg(test)] - fn from(s: &mut [T]) -> Vec { - crate::slice::to_vec(s, Global) - } } #[cfg(not(no_global_oom_handling))] @@ -3928,16 +3926,10 @@ impl From<[T; N]> for Vec { /// ``` /// assert_eq!(Vec::from([1, 2, 3]), vec![1, 2, 3]); /// ``` - #[cfg(not(test))] #[track_caller] fn from(s: [T; N]) -> Vec { <[T]>::into_vec(Box::new(s)) } - - #[cfg(test)] - fn from(s: [T; N]) -> Vec { - crate::slice::into_vec(Box::new(s)) - } } #[stable(feature = "vec_from_cow_slice", since = "1.14.0")] @@ -3966,7 +3958,6 @@ where } // note: test pulls in std, which causes errors here -#[cfg(not(test))] #[stable(feature = "vec_from_box", since = "1.18.0")] impl From> for Vec { /// Converts a boxed slice into a vector by transferring ownership of @@ -3985,7 +3976,6 @@ impl From> for Vec { // note: test pulls in std, which causes errors here #[cfg(not(no_global_oom_handling))] -#[cfg(not(test))] #[stable(feature = "box_from_vec", since = "1.20.0")] impl From> for Box<[T], A> { /// Converts a vector into a boxed slice. diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs index ca5cb17f8bfda..ed1a0dda76d29 100644 --- a/library/alloc/src/vec/splice.rs +++ b/library/alloc/src/vec/splice.rs @@ -59,7 +59,7 @@ impl Drop for Splice<'_, I, A> { // and moving things into the final place. // Which means we can replace the slice::Iter with pointers that won't point to deallocated // memory, so that Drain::drop is still allowed to call iter.len(), otherwise it would break - // the ptr.sub_ptr contract. + // the ptr.offset_from_unsigned contract. self.drain.iter = (&[]).iter(); unsafe { diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs deleted file mode 100644 index 785070fb2bbcb..0000000000000 --- a/library/alloc/tests/lib.rs +++ /dev/null @@ -1,106 +0,0 @@ -#![feature(allocator_api)] -#![feature(alloc_layout_extra)] -#![feature(iter_array_chunks)] -#![feature(assert_matches)] -#![feature(btree_extract_if)] -#![feature(char_max_len)] -#![feature(cow_is_borrowed)] -#![feature(core_intrinsics)] -#![feature(downcast_unchecked)] -#![feature(exact_size_is_empty)] -#![feature(hashmap_internals)] -#![feature(linked_list_cursors)] -#![feature(map_try_insert)] -#![feature(pattern)] -#![feature(trusted_len)] -#![feature(try_reserve_kind)] -#![feature(try_with_capacity)] -#![feature(unboxed_closures)] -#![feature(binary_heap_into_iter_sorted)] -#![feature(binary_heap_drain_sorted)] -#![feature(slice_ptr_get)] -#![feature(inplace_iteration)] -#![feature(iter_advance_by)] -#![feature(iter_next_chunk)] -#![feature(round_char_boundary)] -#![feature(slice_partition_dedup)] -#![feature(string_from_utf8_lossy_owned)] -#![feature(string_remove_matches)] -#![feature(const_btree_len)] -#![feature(const_trait_impl)] -#![feature(panic_update_hook)] -#![feature(pointer_is_aligned_to)] -#![feature(test)] -#![feature(thin_box)] -#![feature(drain_keep_rest)] -#![feature(local_waker)] -#![feature(str_as_str)] -#![feature(strict_provenance_lints)] -#![feature(vec_deque_pop_if)] -#![feature(unique_rc_arc)] -#![feature(macro_metavar_expr_concat)] -#![allow(internal_features)] -#![deny(fuzzy_provenance_casts)] -#![deny(unsafe_op_in_unsafe_fn)] - -extern crate test; - -use std::hash::{DefaultHasher, Hash, Hasher}; - -mod alloc; -mod arc; -mod autotraits; -mod borrow; -mod boxed; -mod btree_set_hash; -mod c_str; -mod c_str2; -mod collections; -mod const_fns; -mod cow_str; -mod fmt; -mod heap; -mod linked_list; -mod misc_tests; -mod rc; -mod slice; -mod sort; -mod str; -mod string; -mod sync; -mod task; -mod testing; -mod thin_box; -mod vec; -mod vec_deque; - -fn hash(t: &T) -> u64 { - let mut s = DefaultHasher::new(); - t.hash(&mut s); - s.finish() -} - -/// Copied from `std::test_helpers::test_rng`, since these tests rely on the -/// seed not being the same for every RNG invocation too. -fn test_rng() -> rand_xorshift::XorShiftRng { - use std::hash::{BuildHasher, Hash, Hasher}; - let mut hasher = std::hash::RandomState::new().build_hasher(); - std::panic::Location::caller().hash(&mut hasher); - let hc64 = hasher.finish(); - let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); - let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); - rand::SeedableRng::from_seed(seed) -} - -#[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/library/alloc/tests/rc.rs b/library/alloc/tests/rc.rs deleted file mode 100644 index 451765d724283..0000000000000 --- a/library/alloc/tests/rc.rs +++ /dev/null @@ -1,260 +0,0 @@ -use std::any::Any; -use std::cell::{Cell, RefCell}; -use std::iter::TrustedLen; -use std::mem; -use std::rc::{Rc, Weak}; - -#[test] -fn uninhabited() { - enum Void {} - let mut a = Weak::::new(); - a = a.clone(); - assert!(a.upgrade().is_none()); - - let mut a: Weak = a; // Unsizing - a = a.clone(); - assert!(a.upgrade().is_none()); -} - -#[test] -fn slice() { - let a: Rc<[u32; 3]> = Rc::new([3, 2, 1]); - let a: Rc<[u32]> = a; // Unsizing - let b: Rc<[u32]> = Rc::from(&[3, 2, 1][..]); // Conversion - assert_eq!(a, b); - - // Exercise is_dangling() with a DST - let mut a = Rc::downgrade(&a); - a = a.clone(); - assert!(a.upgrade().is_some()); -} - -#[test] -fn trait_object() { - let a: Rc = Rc::new(4); - let a: Rc = a; // Unsizing - - // Exercise is_dangling() with a DST - let mut a = Rc::downgrade(&a); - a = a.clone(); - assert!(a.upgrade().is_some()); - - let mut b = Weak::::new(); - b = b.clone(); - assert!(b.upgrade().is_none()); - let mut b: Weak = b; // Unsizing - b = b.clone(); - assert!(b.upgrade().is_none()); -} - -#[test] -fn float_nan_ne() { - let x = Rc::new(f32::NAN); - assert!(x != x); - assert!(!(x == x)); -} - -#[test] -fn partial_eq() { - struct TestPEq(RefCell); - impl PartialEq for TestPEq { - fn eq(&self, other: &TestPEq) -> bool { - *self.0.borrow_mut() += 1; - *other.0.borrow_mut() += 1; - true - } - } - let x = Rc::new(TestPEq(RefCell::new(0))); - assert!(x == x); - assert!(!(x != x)); - assert_eq!(*x.0.borrow(), 4); -} - -#[test] -fn eq() { - #[derive(Eq)] - struct TestEq(RefCell); - impl PartialEq for TestEq { - fn eq(&self, other: &TestEq) -> bool { - *self.0.borrow_mut() += 1; - *other.0.borrow_mut() += 1; - true - } - } - let x = Rc::new(TestEq(RefCell::new(0))); - assert!(x == x); - assert!(!(x != x)); - assert_eq!(*x.0.borrow(), 0); -} - -const SHARED_ITER_MAX: u16 = 100; - -fn assert_trusted_len(_: &I) {} - -#[test] -fn shared_from_iter_normal() { - // Exercise the base implementation for non-`TrustedLen` iterators. - { - // `Filter` is never `TrustedLen` since we don't - // know statically how many elements will be kept: - let iter = (0..SHARED_ITER_MAX).filter(|x| x % 2 == 0).map(Box::new); - - // Collecting into a `Vec` or `Rc<[T]>` should make no difference: - let vec = iter.clone().collect::>(); - let rc = iter.collect::>(); - assert_eq!(&*vec, &*rc); - - // Clone a bit and let these get dropped. - { - let _rc_2 = rc.clone(); - let _rc_3 = rc.clone(); - let _rc_4 = Rc::downgrade(&_rc_3); - } - } // Drop what hasn't been here. -} - -#[test] -fn shared_from_iter_trustedlen_normal() { - // Exercise the `TrustedLen` implementation under normal circumstances - // where `size_hint()` matches `(_, Some(exact_len))`. - { - let iter = (0..SHARED_ITER_MAX).map(Box::new); - assert_trusted_len(&iter); - - // Collecting into a `Vec` or `Rc<[T]>` should make no difference: - let vec = iter.clone().collect::>(); - let rc = iter.collect::>(); - assert_eq!(&*vec, &*rc); - assert_eq!(mem::size_of::>() * SHARED_ITER_MAX as usize, mem::size_of_val(&*rc)); - - // Clone a bit and let these get dropped. - { - let _rc_2 = rc.clone(); - let _rc_3 = rc.clone(); - let _rc_4 = Rc::downgrade(&_rc_3); - } - } // Drop what hasn't been here. - - // Try a ZST to make sure it is handled well. - { - let iter = (0..SHARED_ITER_MAX).map(drop); - let vec = iter.clone().collect::>(); - let rc = iter.collect::>(); - assert_eq!(&*vec, &*rc); - assert_eq!(0, mem::size_of_val(&*rc)); - { - let _rc_2 = rc.clone(); - let _rc_3 = rc.clone(); - let _rc_4 = Rc::downgrade(&_rc_3); - } - } -} - -#[test] -#[should_panic = "I've almost got 99 problems."] -fn shared_from_iter_trustedlen_panic() { - // Exercise the `TrustedLen` implementation when `size_hint()` matches - // `(_, Some(exact_len))` but where `.next()` drops before the last iteration. - let iter = (0..SHARED_ITER_MAX).map(|val| match val { - 98 => panic!("I've almost got 99 problems."), - _ => Box::new(val), - }); - assert_trusted_len(&iter); - let _ = iter.collect::>(); - - panic!("I am unreachable."); -} - -#[test] -fn shared_from_iter_trustedlen_no_fuse() { - // Exercise the `TrustedLen` implementation when `size_hint()` matches - // `(_, Some(exact_len))` but where the iterator does not behave in a fused manner. - struct Iter(std::vec::IntoIter>>); - - unsafe impl TrustedLen for Iter {} - - impl Iterator for Iter { - fn size_hint(&self) -> (usize, Option) { - (2, Some(2)) - } - - type Item = Box; - - fn next(&mut self) -> Option { - self.0.next().flatten() - } - } - - let vec = vec![Some(Box::new(42)), Some(Box::new(24)), None, Some(Box::new(12))]; - let iter = Iter(vec.into_iter()); - assert_trusted_len(&iter); - assert_eq!(&[Box::new(42), Box::new(24)], &*iter.collect::>()); -} - -#[test] -fn weak_may_dangle() { - fn hmm<'a>(val: &'a mut Weak<&'a str>) -> Weak<&'a str> { - val.clone() - } - - // Without #[may_dangle] we get: - let mut val = Weak::new(); - hmm(&mut val); - // ~~~~~~~~ borrowed value does not live long enough - // - // `val` dropped here while still borrowed - // borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::rc::Weak` -} - -/// Test that a panic from a destructor does not leak the allocation. -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn panic_no_leak() { - use std::alloc::{AllocError, Allocator, Global, Layout}; - use std::panic::{AssertUnwindSafe, catch_unwind}; - use std::ptr::NonNull; - - struct AllocCount(Cell); - unsafe impl Allocator for AllocCount { - fn allocate(&self, layout: Layout) -> Result, AllocError> { - self.0.set(self.0.get() + 1); - Global.allocate(layout) - } - unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { - self.0.set(self.0.get() - 1); - unsafe { Global.deallocate(ptr, layout) } - } - } - - struct PanicOnDrop; - impl Drop for PanicOnDrop { - fn drop(&mut self) { - panic!("PanicOnDrop"); - } - } - - let alloc = AllocCount(Cell::new(0)); - let rc = Rc::new_in(PanicOnDrop, &alloc); - assert_eq!(alloc.0.get(), 1); - - let panic_message = catch_unwind(AssertUnwindSafe(|| drop(rc))).unwrap_err(); - assert_eq!(*panic_message.downcast_ref::<&'static str>().unwrap(), "PanicOnDrop"); - assert_eq!(alloc.0.get(), 0); -} - -#[allow(unused)] -mod pin_coerce_unsized { - use alloc::rc::{Rc, UniqueRc}; - use core::pin::Pin; - - pub trait MyTrait {} - impl MyTrait for String {} - - // Pin coercion should work for Rc - pub fn pin_rc(arg: Pin>) -> Pin> { - arg - } - pub fn pin_unique_rc(arg: Pin>) -> Pin> { - arg - } -} diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs deleted file mode 100644 index f990a41b679fa..0000000000000 --- a/library/alloc/tests/slice.rs +++ /dev/null @@ -1,1668 +0,0 @@ -use std::cmp::Ordering::{Equal, Greater, Less}; -use std::convert::identity; -use std::rc::Rc; -use std::{fmt, mem, panic}; - -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::new(0)); - v.push(Box::new(0)); - v.push(Box::new(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::new(6), Box::new(5), Box::new(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::new(6), Box::new(5), Box::new(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] -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_split_iterator() { - 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_split_iterator_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_split_iterator_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_split_iterator_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_split_iterator_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_splitn_iterator() { - 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_splitn_iterator_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_rsplit_iterator() { - 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_rsplitn_iterator() { - 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_split_iterators_size_hint() { - #[derive(Copy, Clone)] - enum Bounds { - Lower, - Upper, - } - fn assert_tight_size_hints(mut it: impl Iterator, which: Bounds, ctx: impl fmt::Display) { - match which { - Bounds::Lower => { - let mut lower_bounds = vec![it.size_hint().0]; - while let Some(_) = it.next() { - lower_bounds.push(it.size_hint().0); - } - let target: Vec<_> = (0..lower_bounds.len()).rev().collect(); - assert_eq!(lower_bounds, target, "lower bounds incorrect or not tight: {}", ctx); - } - Bounds::Upper => { - let mut upper_bounds = vec![it.size_hint().1]; - while let Some(_) = it.next() { - upper_bounds.push(it.size_hint().1); - } - let target: Vec<_> = (0..upper_bounds.len()).map(Some).rev().collect(); - assert_eq!(upper_bounds, target, "upper bounds incorrect or not tight: {}", ctx); - } - } - } - - for len in 0..=2 { - let mut v: Vec = (0..len).collect(); - - // p: predicate, b: bound selection - for (p, b) in [ - // with a predicate always returning false, the split*-iterators - // become maximally short, so the size_hint lower bounds are tight - ((|_| false) as fn(&_) -> _, Bounds::Lower), - // with a predicate always returning true, the split*-iterators - // become maximally long, so the size_hint upper bounds are tight - ((|_| true) as fn(&_) -> _, Bounds::Upper), - ] { - use {assert_tight_size_hints as a, format_args as f}; - - a(v.split(p), b, "split"); - a(v.split_mut(p), b, "split_mut"); - a(v.split_inclusive(p), b, "split_inclusive"); - a(v.split_inclusive_mut(p), b, "split_inclusive_mut"); - a(v.rsplit(p), b, "rsplit"); - a(v.rsplit_mut(p), b, "rsplit_mut"); - - for n in 0..=3 { - a(v.splitn(n, p), b, f!("splitn, n = {n}")); - a(v.splitn_mut(n, p), b, f!("splitn_mut, n = {n}")); - a(v.rsplitn(n, p), b, f!("rsplitn, n = {n}")); - a(v.rsplitn_mut(n, p), b, f!("rsplitn_mut, n = {n}")); - } - } - } -} - -#[test] -fn test_windows_iterator() { - 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_windows_iterator_0() { - let v = &[1, 2, 3, 4]; - let _it = v.windows(0); -} - -#[test] -fn test_chunks_iterator() { - 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_chunks_iterator_0() { - let v = &[1, 2, 3, 4]; - let _it = v.chunks(0); -} - -#[test] -fn test_chunks_exact_iterator() { - 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_exact_iterator_0() { - let v = &[1, 2, 3, 4]; - let _it = v.chunks_exact(0); -} - -#[test] -fn test_rchunks_iterator() { - 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_rchunks_iterator_0() { - let v = &[1, 2, 3, 4]; - let _it = v.rchunks(0); -} - -#[test] -fn test_rchunks_exact_iterator() { - 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_exact_iterator_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_split_iterator() { - 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_split_iterator_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::new([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::new([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(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_box_slice_clone_panics() { - use std::sync::Arc; - use std::sync::atomic::{AtomicUsize, Ordering}; - - 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); -} - -#[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]); -} - -#[test] -#[allow(unreachable_patterns)] -fn subslice_patterns() { - // This test comprehensively checks the passing static and dynamic semantics - // of subslice patterns `..`, `x @ ..`, `ref x @ ..`, and `ref mut @ ..` - // in slice patterns `[$($pat), $(,)?]` . - - #[derive(PartialEq, Debug, Clone)] - struct N(u8); - - macro_rules! n { - ($($e:expr),* $(,)?) => { - [$(N($e)),*] - } - } - - macro_rules! c { - ($inp:expr, $typ:ty, $out:expr $(,)?) => { - assert_eq!($out, identity::<$typ>($inp)) - }; - } - - macro_rules! m { - ($e:expr, $p:pat => $b:expr) => { - match $e { - $p => $b, - _ => panic!(), - } - }; - } - - // == Slices == - - // Matching slices using `ref` patterns: - let mut v = vec![N(0), N(1), N(2), N(3), N(4)]; - let mut vc = (0..=4).collect::>(); - - let [..] = v[..]; // Always matches. - m!(v[..], [N(0), ref sub @ .., N(4)] => c!(sub, &[N], n![1, 2, 3])); - m!(v[..], [N(0), ref sub @ ..] => c!(sub, &[N], n![1, 2, 3, 4])); - m!(v[..], [ref sub @ .., N(4)] => c!(sub, &[N], n![0, 1, 2, 3])); - m!(v[..], [ref sub @ .., _, _, _, _, _] => c!(sub, &[N], &n![] as &[N])); - m!(v[..], [_, _, _, _, _, ref sub @ ..] => c!(sub, &[N], &n![] as &[N])); - m!(vc[..], [x, .., y] => c!((x, y), (u8, u8), (0, 4))); - - // Matching slices using `ref mut` patterns: - let [..] = v[..]; // Always matches. - m!(v[..], [N(0), ref mut sub @ .., N(4)] => c!(sub, &mut [N], n![1, 2, 3])); - m!(v[..], [N(0), ref mut sub @ ..] => c!(sub, &mut [N], n![1, 2, 3, 4])); - m!(v[..], [ref mut sub @ .., N(4)] => c!(sub, &mut [N], n![0, 1, 2, 3])); - m!(v[..], [ref mut sub @ .., _, _, _, _, _] => c!(sub, &mut [N], &mut n![] as &mut [N])); - m!(v[..], [_, _, _, _, _, ref mut sub @ ..] => c!(sub, &mut [N], &mut n![] as &mut [N])); - m!(vc[..], [x, .., y] => c!((x, y), (u8, u8), (0, 4))); - - // Matching slices using default binding modes (&): - let [..] = &v[..]; // Always matches. - m!(&v[..], [N(0), sub @ .., N(4)] => c!(sub, &[N], n![1, 2, 3])); - m!(&v[..], [N(0), sub @ ..] => c!(sub, &[N], n![1, 2, 3, 4])); - m!(&v[..], [sub @ .., N(4)] => c!(sub, &[N], n![0, 1, 2, 3])); - m!(&v[..], [sub @ .., _, _, _, _, _] => c!(sub, &[N], &n![] as &[N])); - m!(&v[..], [_, _, _, _, _, sub @ ..] => c!(sub, &[N], &n![] as &[N])); - m!(&vc[..], [x, .., y] => c!((x, y), (&u8, &u8), (&0, &4))); - - // Matching slices using default binding modes (&mut): - let [..] = &mut v[..]; // Always matches. - m!(&mut v[..], [N(0), sub @ .., N(4)] => c!(sub, &mut [N], n![1, 2, 3])); - m!(&mut v[..], [N(0), sub @ ..] => c!(sub, &mut [N], n![1, 2, 3, 4])); - m!(&mut v[..], [sub @ .., N(4)] => c!(sub, &mut [N], n![0, 1, 2, 3])); - m!(&mut v[..], [sub @ .., _, _, _, _, _] => c!(sub, &mut [N], &mut n![] as &mut [N])); - m!(&mut v[..], [_, _, _, _, _, sub @ ..] => c!(sub, &mut [N], &mut n![] as &mut [N])); - m!(&mut vc[..], [x, .., y] => c!((x, y), (&mut u8, &mut u8), (&mut 0, &mut 4))); - - // == Arrays == - let mut v = n![0, 1, 2, 3, 4]; - let vc = [0, 1, 2, 3, 4]; - - // Matching arrays by value: - m!(v.clone(), [N(0), sub @ .., N(4)] => c!(sub, [N; 3], n![1, 2, 3])); - m!(v.clone(), [N(0), sub @ ..] => c!(sub, [N; 4], n![1, 2, 3, 4])); - m!(v.clone(), [sub @ .., N(4)] => c!(sub, [N; 4], n![0, 1, 2, 3])); - m!(v.clone(), [sub @ .., _, _, _, _, _] => c!(sub, [N; 0], n![] as [N; 0])); - m!(v.clone(), [_, _, _, _, _, sub @ ..] => c!(sub, [N; 0], n![] as [N; 0])); - m!(v.clone(), [x, .., y] => c!((x, y), (N, N), (N(0), N(4)))); - m!(v.clone(), [..] => ()); - - // Matching arrays by ref patterns: - m!(v, [N(0), ref sub @ .., N(4)] => c!(sub, &[N; 3], &n![1, 2, 3])); - m!(v, [N(0), ref sub @ ..] => c!(sub, &[N; 4], &n![1, 2, 3, 4])); - m!(v, [ref sub @ .., N(4)] => c!(sub, &[N; 4], &n![0, 1, 2, 3])); - m!(v, [ref sub @ .., _, _, _, _, _] => c!(sub, &[N; 0], &n![] as &[N; 0])); - m!(v, [_, _, _, _, _, ref sub @ ..] => c!(sub, &[N; 0], &n![] as &[N; 0])); - m!(vc, [x, .., y] => c!((x, y), (u8, u8), (0, 4))); - - // Matching arrays by ref mut patterns: - m!(v, [N(0), ref mut sub @ .., N(4)] => c!(sub, &mut [N; 3], &mut n![1, 2, 3])); - m!(v, [N(0), ref mut sub @ ..] => c!(sub, &mut [N; 4], &mut n![1, 2, 3, 4])); - m!(v, [ref mut sub @ .., N(4)] => c!(sub, &mut [N; 4], &mut n![0, 1, 2, 3])); - m!(v, [ref mut sub @ .., _, _, _, _, _] => c!(sub, &mut [N; 0], &mut n![] as &mut [N; 0])); - m!(v, [_, _, _, _, _, ref mut sub @ ..] => c!(sub, &mut [N; 0], &mut n![] as &mut [N; 0])); - - // Matching arrays by default binding modes (&): - m!(&v, [N(0), sub @ .., N(4)] => c!(sub, &[N; 3], &n![1, 2, 3])); - m!(&v, [N(0), sub @ ..] => c!(sub, &[N; 4], &n![1, 2, 3, 4])); - m!(&v, [sub @ .., N(4)] => c!(sub, &[N; 4], &n![0, 1, 2, 3])); - m!(&v, [sub @ .., _, _, _, _, _] => c!(sub, &[N; 0], &n![] as &[N; 0])); - m!(&v, [_, _, _, _, _, sub @ ..] => c!(sub, &[N; 0], &n![] as &[N; 0])); - m!(&v, [..] => ()); - m!(&v, [x, .., y] => c!((x, y), (&N, &N), (&N(0), &N(4)))); - - // Matching arrays by default binding modes (&mut): - m!(&mut v, [N(0), sub @ .., N(4)] => c!(sub, &mut [N; 3], &mut n![1, 2, 3])); - m!(&mut v, [N(0), sub @ ..] => c!(sub, &mut [N; 4], &mut n![1, 2, 3, 4])); - m!(&mut v, [sub @ .., N(4)] => c!(sub, &mut [N; 4], &mut n![0, 1, 2, 3])); - m!(&mut v, [sub @ .., _, _, _, _, _] => c!(sub, &mut [N; 0], &mut n![] as &[N; 0])); - m!(&mut v, [_, _, _, _, _, sub @ ..] => c!(sub, &mut [N; 0], &mut n![] as &[N; 0])); - m!(&mut v, [..] => ()); - m!(&mut v, [x, .., y] => c!((x, y), (&mut N, &mut N), (&mut N(0), &mut N(4)))); -} - -#[test] -fn test_chunk_by() { - let slice = &[1, 1, 1, 3, 3, 2, 2, 2, 1, 0]; - - let mut iter = slice.chunk_by(|a, b| a == b); - assert_eq!(iter.next(), Some(&[1, 1, 1][..])); - assert_eq!(iter.next(), Some(&[3, 3][..])); - assert_eq!(iter.next(), Some(&[2, 2, 2][..])); - assert_eq!(iter.next(), Some(&[1][..])); - assert_eq!(iter.next(), Some(&[0][..])); - assert_eq!(iter.next(), None); - - let mut iter = slice.chunk_by(|a, b| a == b); - assert_eq!(iter.next_back(), Some(&[0][..])); - assert_eq!(iter.next_back(), Some(&[1][..])); - assert_eq!(iter.next_back(), Some(&[2, 2, 2][..])); - assert_eq!(iter.next_back(), Some(&[3, 3][..])); - assert_eq!(iter.next_back(), Some(&[1, 1, 1][..])); - assert_eq!(iter.next_back(), None); - - let mut iter = slice.chunk_by(|a, b| a == b); - assert_eq!(iter.next(), Some(&[1, 1, 1][..])); - assert_eq!(iter.next_back(), Some(&[0][..])); - assert_eq!(iter.next(), Some(&[3, 3][..])); - assert_eq!(iter.next_back(), Some(&[1][..])); - assert_eq!(iter.next(), Some(&[2, 2, 2][..])); - assert_eq!(iter.next_back(), None); -} - -#[test] -fn test_chunk_by_mut() { - let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2, 1, 0]; - - let mut iter = slice.chunk_by_mut(|a, b| a == b); - assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); - assert_eq!(iter.next(), Some(&mut [3, 3][..])); - assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); - assert_eq!(iter.next(), Some(&mut [1][..])); - assert_eq!(iter.next(), Some(&mut [0][..])); - assert_eq!(iter.next(), None); - - let mut iter = slice.chunk_by_mut(|a, b| a == b); - assert_eq!(iter.next_back(), Some(&mut [0][..])); - assert_eq!(iter.next_back(), Some(&mut [1][..])); - assert_eq!(iter.next_back(), Some(&mut [2, 2, 2][..])); - assert_eq!(iter.next_back(), Some(&mut [3, 3][..])); - assert_eq!(iter.next_back(), Some(&mut [1, 1, 1][..])); - assert_eq!(iter.next_back(), None); - - let mut iter = slice.chunk_by_mut(|a, b| a == b); - assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); - assert_eq!(iter.next_back(), Some(&mut [0][..])); - assert_eq!(iter.next(), Some(&mut [3, 3][..])); - assert_eq!(iter.next_back(), Some(&mut [1][..])); - assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); - assert_eq!(iter.next_back(), None); -} diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs deleted file mode 100644 index fe1db56414e0c..0000000000000 --- a/library/alloc/tests/vec.rs +++ /dev/null @@ -1,2750 +0,0 @@ -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#![allow(static_mut_refs)] - -use core::alloc::{Allocator, Layout}; -use core::num::NonZero; -use core::ptr::NonNull; -use core::{assert_eq, assert_ne}; -use std::alloc::System; -use std::assert_matches::assert_matches; -use std::borrow::Cow; -use std::cell::Cell; -use std::collections::TryReserveErrorKind::*; -use std::fmt::Debug; -use std::iter::InPlaceIterable; -use std::mem::{size_of, swap}; -use std::ops::Bound::*; -use std::panic::{AssertUnwindSafe, catch_unwind}; -use std::rc::Rc; -use std::sync::atomic::{AtomicU32, Ordering}; -use std::vec::{Drain, IntoIter}; -use std::{hint, mem}; - -struct DropCounter<'a> { - count: &'a mut u32, -} - -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::new(1), Box::new(2), Box::new(3)]; - let two: Vec> = vec![Box::new(4), Box::new(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_retain_predicate_order() { - for to_keep in [true, false] { - let mut number_of_executions = 0; - let mut vec = vec![1, 2, 3, 4]; - let mut next_expected = 1; - vec.retain(|&x| { - assert_eq!(next_expected, x); - next_expected += 1; - number_of_executions += 1; - to_keep - }); - assert_eq!(number_of_executions, 4); - } -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_retain_pred_panic_with_hole() { - let v = (0..5).map(Rc::new).collect::>(); - catch_unwind(AssertUnwindSafe(|| { - let mut v = v.clone(); - v.retain(|r| match **r { - 0 => true, - 1 => false, - 2 => true, - _ => panic!(), - }); - })) - .unwrap_err(); - // Everything is dropped when predicate panicked. - assert!(v.iter().all(|r| Rc::strong_count(r) == 1)); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_retain_pred_panic_no_hole() { - let v = (0..5).map(Rc::new).collect::>(); - catch_unwind(AssertUnwindSafe(|| { - let mut v = v.clone(); - v.retain(|r| match **r { - 0 | 1 | 2 => true, - _ => panic!(), - }); - })) - .unwrap_err(); - // Everything is dropped when predicate panicked. - assert!(v.iter().all(|r| Rc::strong_count(r) == 1)); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_retain_drop_panic() { - struct Wrap(Rc); - - impl Drop for Wrap { - fn drop(&mut self) { - if *self.0 == 3 { - panic!(); - } - } - } - - let v = (0..5).map(|x| Rc::new(x)).collect::>(); - catch_unwind(AssertUnwindSafe(|| { - let mut v = v.iter().map(|r| Wrap(r.clone())).collect::>(); - v.retain(|w| match *w.0 { - 0 => true, - 1 => false, - 2 => true, - 3 => false, // Drop panic. - _ => true, - }); - })) - .unwrap_err(); - // Other elements are dropped when `drop` of one element panicked. - // The panicked wrapper also has its Rc dropped. - assert!(v.iter().all(|r| Rc::strong_count(r) == 1)); -} - -#[test] -fn test_retain_maybeuninits() { - // This test aimed to be run under miri. - use core::mem::MaybeUninit; - let mut vec: Vec<_> = [1i32, 2, 3, 4].map(|v| MaybeUninit::new(vec![v])).into(); - vec.retain(|x| { - // SAFETY: Retain must visit every element of Vec in original order and exactly once. - // Our values is initialized at creation of Vec. - let v = unsafe { x.assume_init_ref()[0] }; - if v & 1 == 0 { - return true; - } - // SAFETY: Value is initialized. - // Value wouldn't be dropped by `Vec::retain` - // because `MaybeUninit` doesn't drop content. - drop(unsafe { x.assume_init_read() }); - false - }); - let vec: Vec = vec - .into_iter() - .map(|x| unsafe { - // SAFETY: All values dropped in retain predicate must be removed by `Vec::retain`. - // Remaining values are initialized. - x.assume_init()[0] - }) - .collect(); - 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::new(1), Box::new(1), Box::new(2), Box::new(3)]; - v0.dedup(); - let mut v1: Vec> = vec![Box::new(1), Box::new(2), Box::new(2), Box::new(3)]; - v1.dedup(); - let mut v2: Vec> = vec![Box::new(1), Box::new(2), Box::new(3), Box::new(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!([].into_iter().partition(|x: &i32| *x < 3), (vec![], vec![])); - assert_eq!([1, 2, 3].into_iter().partition(|x| *x < 4), (vec![1, 2, 3], vec![])); - assert_eq!([1, 2, 3].into_iter().partition(|x| *x < 2), (vec![1], vec![2, 3])); - assert_eq!([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(#[allow(dead_code)] 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]; - let _ = &x[!0..]; -} - -#[test] -#[should_panic] -fn test_slice_out_of_bounds_2() { - let x = vec![1, 2, 3, 4, 5]; - let _ = &x[..6]; -} - -#[test] -#[should_panic] -fn test_slice_out_of_bounds_3() { - let x = vec![1, 2, 3, 4, 5]; - let _ = &x[!0..4]; -} - -#[test] -#[should_panic] -fn test_slice_out_of_bounds_4() { - let x = vec![1, 2, 3, 4, 5]; - let _ = &x[1..6]; -} - -#[test] -#[should_panic] -fn test_slice_out_of_bounds_5() { - let x = vec![1, 2, 3, 4, 5]; - let _ = &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_empty_vec() { - let mut vec: Vec = vec![]; - let mut vec2: Vec = vec![]; - for i in vec.drain(..) { - vec2.push(i); - } - assert!(vec.is_empty()); - assert!(vec2.is_empty()); -} - -#[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_index_overflow() { - let mut v = Vec::<()>::with_capacity(usize::MAX); - unsafe { - v.set_len(usize::MAX); - } - v.drain(0..=usize::MAX); -} - -#[test] -#[should_panic] -fn test_drain_inclusive_out_of_bounds() { - let mut v = vec![1, 2, 3, 4, 5]; - v.drain(5..=5); -} - -#[test] -#[should_panic] -fn test_drain_start_overflow() { - let mut v = vec![1, 2, 3]; - v.drain((Excluded(usize::MAX), Included(0))); -} - -#[test] -#[should_panic] -fn test_drain_end_overflow() { - let mut v = vec![1, 2, 3]; - v.drain((Included(0), Included(usize::MAX))); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -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_drain_keep_rest() { - let mut v = vec![0, 1, 2, 3, 4, 5, 6]; - let mut drain = v.drain(1..6); - assert_eq!(drain.next(), Some(1)); - assert_eq!(drain.next_back(), Some(5)); - assert_eq!(drain.next(), Some(2)); - - drain.keep_rest(); - assert_eq!(v, &[0, 3, 4, 6]); -} - -#[test] -fn test_drain_keep_rest_all() { - let mut v = vec![0, 1, 2, 3, 4, 5, 6]; - v.drain(1..6).keep_rest(); - assert_eq!(v, &[0, 1, 2, 3, 4, 5, 6]); -} - -#[test] -fn test_drain_keep_rest_none() { - let mut v = vec![0, 1, 2, 3, 4, 5, 6]; - let mut drain = v.drain(1..6); - - drain.by_ref().for_each(drop); - - drain.keep_rest(); - assert_eq!(v, &[0, 6]); -} - -#[test] -fn test_splice() { - let mut v = vec![1, 2, 3, 4, 5]; - let a = [10, 11, 12]; - v.splice(2..4, a); - 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).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); -} - -#[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); -} - -#[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)); - 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 orig_ptr = vec.as_ptr(); - let orig_capacity = vec.capacity(); - - let split_off = vec.split_off(4); - assert_eq!(vec, [1, 2, 3, 4]); - assert_eq!(split_off, [5, 6]); - assert_eq!(vec.capacity(), orig_capacity); - assert_eq!(vec.as_ptr(), orig_ptr); -} - -#[test] -fn test_split_off_take_all() { - // Allocate enough capacity that we can tell whether the split-off vector's - // capacity is based on its size, or (incorrectly) on the original capacity. - let mut vec = Vec::with_capacity(1000); - vec.extend([1, 2, 3, 4, 5, 6]); - let orig_ptr = vec.as_ptr(); - let orig_capacity = vec.capacity(); - - let split_off = vec.split_off(0); - assert_eq!(vec, []); - assert_eq!(split_off, [1, 2, 3, 4, 5, 6]); - assert_eq!(vec.capacity(), orig_capacity); - assert_eq!(vec.as_ptr(), orig_ptr); - - // The split-off vector should be newly-allocated, and should not have - // stolen the original vector's allocation. - assert!(split_off.capacity() < orig_capacity); - assert_ne!(split_off.as_ptr(), orig_ptr); -} - -#[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!([1, 2, 3].into_iter().count(), 3); -} - -#[test] -fn test_into_iter_next_chunk() { - let mut iter = b"lorem".to_vec().into_iter(); - - assert_eq!(iter.next_chunk().unwrap(), [b'l', b'o']); // N is inferred as 2 - assert_eq!(iter.next_chunk().unwrap(), [b'r', b'e', b'm']); // N is inferred as 3 - assert_eq!(iter.next_chunk::<4>().unwrap_err().as_slice(), &[]); // N is explicitly 4 -} - -#[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 = [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] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -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_into_iter_advance_by() { - let mut i = vec![1, 2, 3, 4, 5].into_iter(); - assert_eq!(i.advance_by(0), Ok(())); - assert_eq!(i.advance_back_by(0), Ok(())); - assert_eq!(i.as_slice(), [1, 2, 3, 4, 5]); - - assert_eq!(i.advance_by(1), Ok(())); - assert_eq!(i.advance_back_by(1), Ok(())); - assert_eq!(i.as_slice(), [2, 3, 4]); - - assert_eq!(i.advance_back_by(usize::MAX), Err(NonZero::new(usize::MAX - 3).unwrap())); - - assert_eq!(i.advance_by(usize::MAX), Err(NonZero::new(usize::MAX).unwrap())); - - assert_eq!(i.advance_by(0), Ok(())); - assert_eq!(i.advance_back_by(0), Ok(())); - - assert_eq!(i.len(), 0); -} - -#[test] -fn test_into_iter_drop_allocator() { - struct ReferenceCountedAllocator<'a>(#[allow(dead_code)] DropCounter<'a>); - - unsafe impl Allocator for ReferenceCountedAllocator<'_> { - fn allocate(&self, layout: Layout) -> Result, core::alloc::AllocError> { - System.allocate(layout) - } - - unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { - // Safety: Invariants passed to caller. - unsafe { System.deallocate(ptr, layout) } - } - } - - let mut drop_count = 0; - - let allocator = ReferenceCountedAllocator(DropCounter { count: &mut drop_count }); - let _ = Vec::::new_in(allocator); - assert_eq!(drop_count, 1); - - let allocator = ReferenceCountedAllocator(DropCounter { count: &mut drop_count }); - let _ = Vec::::new_in(allocator).into_iter(); - assert_eq!(drop_count, 2); -} - -#[test] -fn test_into_iter_zst() { - #[derive(Debug, Clone)] - struct AlignedZstWithDrop([u64; 0]); - impl Drop for AlignedZstWithDrop { - fn drop(&mut self) { - let addr = self as *mut _ as usize; - assert!(hint::black_box(addr) % mem::align_of::() == 0); - } - } - - const C: AlignedZstWithDrop = AlignedZstWithDrop([0u64; 0]); - - for _ in vec![C].into_iter() {} - for _ in vec![C; 5].into_iter().rev() {} - - let mut it = vec![C, C].into_iter(); - assert_eq!(it.advance_by(1), Ok(())); - drop(it); - - let mut it = vec![C, C].into_iter(); - it.next_chunk::<1>().unwrap(); - drop(it); - - let mut it = vec![C, C].into_iter(); - it.next_chunk::<4>().unwrap_err(); - drop(it); -} - -#[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 owned: Vec = vec![0usize; 256]; - let refd: Vec<&usize> = owned.iter().collect(); - let src: Vec<&&usize> = refd.iter().collect(); - let srcptr = src.as_ptr(); - let iter = src - .into_iter() - .copied() - .cloned() - .enumerate() - .map(|i| i.0 + i.1) - .zip(std::iter::repeat(1usize)) - .map(|(a, b)| a + b) - .map_while(Option::Some) - .skip(1) - .map(|e| if e != usize::MAX { Ok(NonZero::new(e)) } else { Err(()) }); - assert_in_place_trait(&iter); - let sink = iter.collect::, _>>().unwrap(); - let sinkptr = sink.as_ptr(); - assert_eq!(srcptr as *const usize, sinkptr as *const usize); -} - -#[test] -fn test_in_place_specialization_step_up_down() { - fn assert_in_place_trait(_: &T) {} - - let src = vec![0u8; 1024]; - let srcptr = src.as_ptr(); - let src_bytes = src.capacity(); - let iter = src.into_iter().array_chunks::<4>(); - assert_in_place_trait(&iter); - let sink = iter.collect::>(); - let sinkptr = sink.as_ptr(); - assert_eq!(srcptr.addr(), sinkptr.addr()); - assert_eq!(src_bytes, sink.capacity() * 4); - - let mut src: Vec = Vec::with_capacity(17); - let src_bytes = src.capacity(); - src.resize(8, 0u8); - let sink: Vec<[u8; 4]> = src.into_iter().array_chunks::<4>().collect(); - let sink_bytes = sink.capacity() * 4; - assert_ne!(src_bytes, sink_bytes); - assert_eq!(sink.len(), 2); - - let mut src: Vec<[u8; 3]> = Vec::with_capacity(17); - src.resize(8, [0; 3]); - let iter = src.into_iter().map(|[a, b, _]| [a, b]); - assert_in_place_trait(&iter); - let sink: Vec<[u8; 2]> = iter.collect(); - assert_eq!(sink.len(), 8); - assert!(sink.capacity() <= 25); -} - -#[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] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_from_iter_specialization_panic_during_iteration_drops() { - 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] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#[allow(static_mut_refs)] -fn test_from_iter_specialization_panic_during_drop_doesnt_leak() { - static mut DROP_COUNTER_OLD: [usize; 5] = [0; 5]; - static mut DROP_COUNTER_NEW: [usize; 2] = [0; 2]; - - #[derive(Debug)] - struct Old(usize); - - impl Drop for Old { - fn drop(&mut self) { - unsafe { - DROP_COUNTER_OLD[self.0] += 1; - } - - if self.0 == 3 { - panic!(); - } - - println!("Dropped Old: {}", self.0); - } - } - - #[derive(Debug)] - struct New(usize); - - impl Drop for New { - fn drop(&mut self) { - unsafe { - DROP_COUNTER_NEW[self.0] += 1; - } - - println!("Dropped New: {}", self.0); - } - } - - let _ = std::panic::catch_unwind(AssertUnwindSafe(|| { - let v = vec![Old(0), Old(1), Old(2), Old(3), Old(4)]; - let _ = v.into_iter().map(|x| New(x.0)).take(2).collect::>(); - })); - - assert_eq!(unsafe { DROP_COUNTER_OLD[0] }, 1); - assert_eq!(unsafe { DROP_COUNTER_OLD[1] }, 1); - assert_eq!(unsafe { DROP_COUNTER_OLD[2] }, 1); - assert_eq!(unsafe { DROP_COUNTER_OLD[3] }, 1); - assert_eq!(unsafe { DROP_COUNTER_OLD[4] }, 1); - - assert_eq!(unsafe { DROP_COUNTER_NEW[0] }, 1); - assert_eq!(unsafe { DROP_COUNTER_NEW[1] }, 1); -} - -// regression test for issue #85322. Peekable previously implemented InPlaceIterable, -// but due to an interaction with IntoIter's current Clone implementation it failed to uphold -// the contract. -#[test] -fn test_collect_after_iterator_clone() { - let v = vec![0; 5]; - let mut i = v.into_iter().map(|i| i + 1).peekable(); - i.peek(); - let v = i.clone().collect::>(); - assert_eq!(v, [1, 1, 1, 1, 1]); - assert!(v.len() <= v.capacity()); -} - -// regression test for #135103, similar to the one above Flatten/FlatMap had an unsound InPlaceIterable -// implementation. -#[test] -fn test_flatten_clone() { - const S: String = String::new(); - - let v = vec![[S, "Hello World!".into()], [S, S]]; - let mut i = v.into_iter().flatten(); - let _ = i.next(); - let result: Vec = i.clone().collect(); - assert_eq!(result, ["Hello World!", "", ""]); -} - -#[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 extract_if_empty() { - let mut vec: Vec = vec![]; - - { - let mut iter = vec.extract_if(.., |_| 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 extract_if_zst() { - let mut vec = vec![(), (), (), (), ()]; - let initial_len = vec.len(); - let mut count = 0; - { - let mut iter = vec.extract_if(.., |_| 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 extract_if_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.extract_if(.., |_| 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 extract_if_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.extract_if(.., |_| 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 extract_if_ranges() { - let mut vec = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - - let mut count = 0; - let it = vec.extract_if(1..=3, |_| { - count += 1; - true - }); - assert_eq!(it.collect::>(), vec![1, 2, 3]); - assert_eq!(vec, vec![0, 4, 5, 6, 7, 8, 9, 10]); - assert_eq!(count, 3); - - let it = vec.extract_if(1..=3, |_| false); - assert_eq!(it.collect::>(), vec![]); - assert_eq!(vec, vec![0, 4, 5, 6, 7, 8, 9, 10]); -} - -#[test] -#[should_panic] -fn extract_if_out_of_bounds() { - let mut vec = vec![0, 1]; - let _ = vec.extract_if(5.., |_| true).for_each(drop); -} - -#[test] -fn extract_if_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.extract_if(.., |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.extract_if(.., |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.extract_if(.., |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.extract_if(.., |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.extract_if(.., |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]); - } -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn extract_if_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.extract_if(.., filter); - - // NOTE: The ExtractIf 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); - } -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn extract_if_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.extract_if(.., filter); - - // NOTE: The ExtractIf 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 extract_if_unconsumed() { - let mut vec = vec![1, 2, 3, 4]; - let drain = vec.extract_if(.., |&mut x| x % 2 != 0); - drop(drain); - assert_eq!(vec, [1, 2, 3, 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 -fn test_try_with_capacity() { - let mut vec: Vec = Vec::try_with_capacity(5).unwrap(); - assert_eq!(0, vec.len()); - assert!(vec.capacity() >= 5 && vec.capacity() <= isize::MAX as usize / 4); - assert!(vec.spare_capacity_mut().len() >= 5); - - assert!(Vec::::try_with_capacity(isize::MAX as usize + 1).is_err()); -} - -#[test] -#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -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; - - { - // 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).map_err(|e| e.kind()) { - 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).map_err(|e| e.kind()) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - - // Check isize::MAX + 1 does count as overflow - assert_matches!( - empty_bytes.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - - // Check usize::MAX does count as overflow - assert_matches!( - empty_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), - Err(CapacityOverflow), - "usize::MAX should trigger an overflow!" - ); - } - - { - // 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).map_err(|e| e.kind()) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - - assert_matches!( - ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - - // Should always overflow in the add-to-len - assert_matches!( - ten_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), - Err(CapacityOverflow), - "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).map_err(|e| e.kind()) - { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP / 4 - 10).map_err(|e| e.kind()) - { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - - assert_matches!( - ten_u32s.try_reserve(MAX_CAP / 4 - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - - // Should fail in the mul-by-size - assert_matches!( - ten_u32s.try_reserve(MAX_USIZE - 20).map_err(|e| e.kind()), - Err(CapacityOverflow), - "usize::MAX should trigger an overflow!" - ); - } -} - -#[test] -#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -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 mut empty_bytes: Vec = Vec::new(); - - if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP).map_err(|e| e.kind()) - { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP).map_err(|e| e.kind()) - { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - - assert_matches!( - empty_bytes.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - - assert_matches!( - empty_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), - Err(CapacityOverflow), - "usize::MAX should trigger an overflow!" - ); - } - - { - 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).map_err(|e| e.kind()) - { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = - ten_bytes.try_reserve_exact(MAX_CAP - 10).map_err(|e| e.kind()) - { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - - assert_matches!( - ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - - assert_matches!( - ten_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), - Err(CapacityOverflow), - "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).map_err(|e| e.kind()) - { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - if let Err(CapacityOverflow) = - ten_u32s.try_reserve_exact(MAX_CAP / 4 - 10).map_err(|e| e.kind()) - { - panic!("isize::MAX shouldn't trigger an overflow!"); - } - - assert_matches!( - ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9).map_err(|e| e.kind()), - Err(CapacityOverflow), - "isize::MAX + 1 should trigger an overflow!" - ); - - assert_matches!( - ten_u32s.try_reserve_exact(MAX_USIZE - 20).map_err(|e| e.kind()), - Err(CapacityOverflow), - "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. - // Note that this test does *not* constitute a stable guarantee that all these functions do not - // reallocate! Only what is explicitly documented at - // is stably guaranteed. - 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, [1; 10].into_iter().filter(|_| true))); // lower bound not exact - assert_eq!(*v0, 13); - - // spare_capacity_mut - v.spare_capacity_mut(); - assert_eq!(*v0, 13); - - // Smoke test that would fire even outside Miri if an actual relocation happened. - // Also ensures the pointer is still writeable after all this. - *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!(std::ptr::eq(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:expr, $a3:expr; $b2:expr, $b3: expr) => { - 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); - assert_partial_eq_valid!(vec2,vec3; arrayref2[..],arrayref3[..]); -} - -#[test] -fn test_vec_cycle() { - #[derive(Debug)] - struct C<'a> { - v: Vec>>>, - } - - impl<'a> C<'a> { - fn new() -> C<'a> { - C { v: Vec::new() } - } - } - - let mut c1 = C::new(); - let mut c2 = C::new(); - let mut c3 = C::new(); - - // Push - c1.v.push(Cell::new(None)); - c1.v.push(Cell::new(None)); - - c2.v.push(Cell::new(None)); - c2.v.push(Cell::new(None)); - - c3.v.push(Cell::new(None)); - c3.v.push(Cell::new(None)); - - // Set - c1.v[0].set(Some(&c2)); - c1.v[1].set(Some(&c3)); - - c2.v[0].set(Some(&c2)); - c2.v[1].set(Some(&c3)); - - c3.v[0].set(Some(&c1)); - c3.v[1].set(Some(&c2)); -} - -#[test] -fn test_vec_cycle_wrapped() { - struct Refs<'a> { - v: Vec>>>, - } - - struct C<'a> { - refs: Refs<'a>, - } - - impl<'a> Refs<'a> { - fn new() -> Refs<'a> { - Refs { v: Vec::new() } - } - } - - impl<'a> C<'a> { - fn new() -> C<'a> { - C { refs: Refs::new() } - } - } - - let mut c1 = C::new(); - let mut c2 = C::new(); - let mut c3 = C::new(); - - c1.refs.v.push(Cell::new(None)); - c1.refs.v.push(Cell::new(None)); - c2.refs.v.push(Cell::new(None)); - c2.refs.v.push(Cell::new(None)); - c3.refs.v.push(Cell::new(None)); - c3.refs.v.push(Cell::new(None)); - - c1.refs.v[0].set(Some(&c2)); - c1.refs.v[1].set(Some(&c3)); - c2.refs.v[0].set(Some(&c2)); - c2.refs.v[1].set(Some(&c3)); - c3.refs.v[0].set(Some(&c1)); - c3.refs.v[1].set(Some(&c2)); -} - -#[test] -fn test_zero_sized_capacity() { - for len in [0, 1, 2, 4, 8, 16, 32, 64, 128, 256] { - let v = Vec::<()>::with_capacity(len); - assert_eq!(v.len(), 0); - assert_eq!(v.capacity(), usize::MAX); - } -} - -#[test] -fn test_zero_sized_vec_push() { - const N: usize = 8; - - for len in 0..N { - let mut tester = Vec::with_capacity(len); - assert_eq!(tester.len(), 0); - assert!(tester.capacity() >= len); - for _ in 0..len { - tester.push(()); - } - assert_eq!(tester.len(), len); - assert_eq!(tester.iter().count(), len); - tester.clear(); - } -} - -#[test] -fn test_vec_macro_repeat() { - assert_eq!(vec![1; 3], vec![1, 1, 1]); - assert_eq!(vec![1; 2], vec![1, 1]); - assert_eq!(vec![1; 1], vec![1]); - assert_eq!(vec![1; 0], vec![]); - - // from_elem syntax (see RFC 832) - let el = Box::new(1); - let n = 3; - assert_eq!(vec![el; n], vec![Box::new(1), Box::new(1), Box::new(1)]); -} - -#[test] -fn test_vec_swap() { - let mut a: Vec = vec![0, 1, 2, 3, 4, 5, 6]; - a.swap(2, 4); - assert_eq!(a[2], 4); - assert_eq!(a[4], 2); - let mut n = 42; - swap(&mut n, &mut a[0]); - assert_eq!(a[0], 42); - assert_eq!(n, 0); -} - -#[test] -fn test_extend_from_within_spec() { - #[derive(Copy)] - struct CopyOnly; - - impl Clone for CopyOnly { - fn clone(&self) -> Self { - panic!("extend_from_within must use specialization on copy"); - } - } - - vec![CopyOnly, CopyOnly].extend_from_within(..); -} - -#[test] -fn test_extend_from_within_clone() { - let mut v = vec![String::from("sssss"), String::from("12334567890"), String::from("c")]; - v.extend_from_within(1..); - - assert_eq!(v, ["sssss", "12334567890", "c", "12334567890", "c"]); -} - -#[test] -fn test_extend_from_within_complete_rande() { - let mut v = vec![0, 1, 2, 3]; - v.extend_from_within(..); - - assert_eq!(v, [0, 1, 2, 3, 0, 1, 2, 3]); -} - -#[test] -fn test_extend_from_within_empty_rande() { - let mut v = vec![0, 1, 2, 3]; - v.extend_from_within(1..1); - - assert_eq!(v, [0, 1, 2, 3]); -} - -#[test] -#[should_panic] -fn test_extend_from_within_out_of_rande() { - let mut v = vec![0, 1]; - v.extend_from_within(..3); -} - -#[test] -fn test_extend_from_within_zst() { - let mut v = vec![(); 8]; - v.extend_from_within(3..7); - - assert_eq!(v, [(); 12]); -} - -#[test] -fn test_extend_from_within_empty_vec() { - let mut v = Vec::::new(); - v.extend_from_within(..); - - assert_eq!(v, []); -} - -#[test] -fn test_extend_from_within() { - let mut v = vec![String::from("a"), String::from("b"), String::from("c")]; - v.extend_from_within(1..=2); - v.extend_from_within(..=1); - - assert_eq!(v, ["a", "b", "c", "b", "c", "a", "b"]); -} - -#[test] -fn test_vec_dedup_by() { - let mut vec: Vec = vec![1, -1, 2, 3, 1, -5, 5, -2, 2]; - - vec.dedup_by(|a, b| a.abs() == b.abs()); - - assert_eq!(vec, [1, 2, 3, 1, -5, -2]); -} - -#[test] -fn test_vec_dedup_empty() { - let mut vec: Vec = Vec::new(); - - vec.dedup(); - - assert_eq!(vec, []); -} - -#[test] -fn test_vec_dedup_one() { - let mut vec = vec![12i32]; - - vec.dedup(); - - assert_eq!(vec, [12]); -} - -#[test] -fn test_vec_dedup_multiple_ident() { - let mut vec = vec![12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11]; - - vec.dedup(); - - assert_eq!(vec, [12, 11]); -} - -#[test] -fn test_vec_dedup_partialeq() { - #[derive(Debug)] - struct Foo(i32, #[allow(dead_code)] i32); - - impl PartialEq for Foo { - fn eq(&self, other: &Foo) -> bool { - self.0 == other.0 - } - } - - let mut vec = vec![Foo(0, 1), Foo(0, 5), Foo(1, 7), Foo(1, 9)]; - - vec.dedup(); - assert_eq!(vec, [Foo(0, 1), Foo(1, 7)]); -} - -#[test] -fn test_vec_dedup() { - let mut vec: Vec = Vec::with_capacity(8); - let mut template = vec.clone(); - - for x in 0u8..255u8 { - vec.clear(); - template.clear(); - - let iter = (0..8).map(move |bit| (x >> bit) & 1 == 1); - vec.extend(iter); - template.extend_from_slice(&vec); - - let (dedup, _) = template.partition_dedup(); - vec.dedup(); - - assert_eq!(vec, dedup); - } -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_vec_dedup_panicking() { - #[derive(Debug)] - struct Panic<'a> { - drop_counter: &'a Cell, - value: bool, - index: usize, - } - - impl<'a> PartialEq for Panic<'a> { - fn eq(&self, other: &Self) -> bool { - self.value == other.value - } - } - - impl<'a> Drop for Panic<'a> { - fn drop(&mut self) { - self.drop_counter.set(self.drop_counter.get() + 1); - if !std::thread::panicking() { - assert!(self.index != 4); - } - } - } - - let drop_counter = &Cell::new(0); - let expected = [ - Panic { drop_counter, value: false, index: 0 }, - Panic { drop_counter, value: false, index: 5 }, - Panic { drop_counter, value: true, index: 6 }, - Panic { drop_counter, value: true, index: 7 }, - ]; - let mut vec = vec![ - Panic { drop_counter, value: false, index: 0 }, - // these elements get deduplicated - Panic { drop_counter, value: false, index: 1 }, - Panic { drop_counter, value: false, index: 2 }, - Panic { drop_counter, value: false, index: 3 }, - Panic { drop_counter, value: false, index: 4 }, - // here it panics while dropping the item with index==4 - Panic { drop_counter, value: false, index: 5 }, - Panic { drop_counter, value: true, index: 6 }, - Panic { drop_counter, value: true, index: 7 }, - ]; - - let _ = catch_unwind(AssertUnwindSafe(|| vec.dedup())).unwrap_err(); - - assert_eq!(drop_counter.get(), 4); - - let ok = vec.iter().zip(expected.iter()).all(|(x, y)| x.index == y.index); - - if !ok { - panic!("expected: {expected:?}\ngot: {vec:?}\n"); - } -} - -// Regression test for issue #82533 -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_extend_from_within_panicking_clone() { - struct Panic<'dc> { - drop_count: &'dc AtomicU32, - aaaaa: bool, - } - - impl Clone for Panic<'_> { - fn clone(&self) -> Self { - if self.aaaaa { - panic!("panic! at the clone"); - } - - Self { ..*self } - } - } - - impl Drop for Panic<'_> { - fn drop(&mut self) { - self.drop_count.fetch_add(1, Ordering::SeqCst); - } - } - - let count = core::sync::atomic::AtomicU32::new(0); - let mut vec = vec![ - Panic { drop_count: &count, aaaaa: false }, - Panic { drop_count: &count, aaaaa: true }, - Panic { drop_count: &count, aaaaa: false }, - ]; - - // This should clone&append one Panic{..} at the end, and then panic while - // cloning second Panic{..}. This means that `Panic::drop` should be called - // 4 times (3 for items already in vector, 1 for just appended). - // - // Previously just appended item was leaked, making drop_count = 3, instead of 4. - std::panic::catch_unwind(move || vec.extend_from_within(..)).unwrap_err(); - - assert_eq!(count.load(Ordering::SeqCst), 4); -} - -#[test] -#[should_panic = "vec len overflow"] -fn test_into_flattened_size_overflow() { - let v = vec![[(); usize::MAX]; 2]; - let _ = v.into_flattened(); -} - -#[test] -fn test_box_zero_allocator() { - use core::alloc::AllocError; - use core::cell::RefCell; - use std::collections::HashSet; - - // Track ZST allocations and ensure that they all have a matching free. - struct ZstTracker { - state: RefCell<(HashSet, usize)>, - } - unsafe impl Allocator for ZstTracker { - fn allocate(&self, layout: Layout) -> Result, AllocError> { - let ptr = if layout.size() == 0 { - let mut state = self.state.borrow_mut(); - let addr = state.1; - assert!(state.0.insert(addr)); - state.1 += 1; - std::println!("allocating {addr}"); - std::ptr::without_provenance_mut(addr) - } else { - unsafe { std::alloc::alloc(layout) } - }; - Ok(NonNull::slice_from_raw_parts(NonNull::new(ptr).ok_or(AllocError)?, layout.size())) - } - - unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { - if layout.size() == 0 { - let addr = ptr.as_ptr() as usize; - let mut state = self.state.borrow_mut(); - std::println!("freeing {addr}"); - assert!(state.0.remove(&addr), "ZST free that wasn't allocated"); - } else { - unsafe { std::alloc::dealloc(ptr.as_ptr(), layout) } - } - } - } - - // Start the state at 100 to avoid returning null pointers. - let alloc = ZstTracker { state: RefCell::new((HashSet::new(), 100)) }; - - // Ensure that unsizing retains the same behavior. - { - let b1: Box<[u8; 0], &ZstTracker> = Box::new_in([], &alloc); - let b2: Box<[u8], &ZstTracker> = b1.clone(); - let _b3: Box<[u8], &ZstTracker> = b2.clone(); - } - - // Ensure that shrinking doesn't leak a ZST allocation. - { - let mut v1: Vec = Vec::with_capacity_in(100, &alloc); - v1.shrink_to_fit(); - } - - // Ensure that conversion to/from vec works. - { - let v1: Vec<(), &ZstTracker> = Vec::with_capacity_in(100, &alloc); - let _b1: Box<[()], &ZstTracker> = v1.into_boxed_slice(); - let b2: Box<[()], &ZstTracker> = Box::new_in([(), (), ()], &alloc); - let _v2: Vec<(), &ZstTracker> = b2.into(); - } - - // Ensure all ZSTs have been freed. - assert!(alloc.state.borrow().0.is_empty()); -} - -#[test] -fn test_vec_from_array_ref() { - assert_eq!(Vec::from(&[1, 2, 3]), vec![1, 2, 3]); -} - -#[test] -fn test_vec_from_array_mut_ref() { - assert_eq!(Vec::from(&mut [1, 2, 3]), vec![1, 2, 3]); -} - -#[test] -fn test_pop_if() { - let mut v = vec![1, 2, 3, 4]; - let pred = |x: &mut i32| *x % 2 == 0; - - assert_eq!(v.pop_if(pred), Some(4)); - assert_eq!(v, [1, 2, 3]); - - assert_eq!(v.pop_if(pred), None); - assert_eq!(v, [1, 2, 3]); -} - -#[test] -fn test_pop_if_empty() { - let mut v = Vec::::new(); - assert_eq!(v.pop_if(|_| true), None); - assert!(v.is_empty()); -} - -#[test] -fn test_pop_if_mutates() { - let mut v = vec![1]; - let pred = |x: &mut i32| { - *x += 1; - false - }; - assert_eq!(v.pop_if(pred), None); - assert_eq!(v, [2]); -} - -/// This assortment of tests, in combination with miri, verifies we handle UB on fishy arguments -/// in the stdlib. Draining and extending the allocation are fairly well-tested earlier, but -/// `vec.insert(usize::MAX, val)` once slipped by! -/// -/// All code that manipulates the collection types should be tested with "trivially wrong" args. -#[test] -fn max_dont_panic() { - let mut v = vec![0]; - let _ = v.get(usize::MAX); - v.shrink_to(usize::MAX); - v.truncate(usize::MAX); -} - -#[test] -#[should_panic] -fn max_insert() { - let mut v = vec![0]; - v.insert(usize::MAX, 1); -} - -#[test] -#[should_panic] -fn max_remove() { - let mut v = vec![0]; - v.remove(usize::MAX); -} - -#[test] -#[should_panic] -fn max_splice() { - let mut v = vec![0]; - v.splice(usize::MAX.., core::iter::once(1)); -} - -#[test] -#[should_panic] -fn max_swap_remove() { - let mut v = vec![0]; - v.swap_remove(usize::MAX); -} - -// Regression test for #135338 -#[test] -fn vec_null_ptr_roundtrip() { - let ptr = std::ptr::from_ref(&42); - let zero = ptr.with_addr(0); - let roundtripped = vec![zero; 1].pop().unwrap(); - let new = roundtripped.with_addr(ptr.addr()); - unsafe { new.read() }; -} diff --git a/library/alloctests/Cargo.toml b/library/alloctests/Cargo.toml new file mode 100644 index 0000000000000..306375f5f01cc --- /dev/null +++ b/library/alloctests/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "alloctests" +version = "0.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" +description = "Tests for the Rust Allocation Library" +autotests = false +autobenches = false +edition = "2021" + +[lib] +path = "lib.rs" +test = true +bench = true +doc = false + +[dev-dependencies] +rand = { version = "0.9.0", default-features = false, features = ["alloc"] } +rand_xorshift = "0.4.0" + +[[test]] +name = "alloctests" +path = "tests/lib.rs" + +[[test]] +name = "vec_deque_alloc_error" +path = "tests/vec_deque_alloc_error.rs" + +[[bench]] +name = "allocbenches" +path = "benches/lib.rs" +test = true + +[[bench]] +name = "vec_deque_append_bench" +path = "benches/vec_deque_append.rs" +harness = false + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + 'cfg(bootstrap)', + 'cfg(no_global_oom_handling)', + 'cfg(no_rc)', + 'cfg(no_sync)', + 'cfg(randomized_layouts)', +] diff --git a/library/alloc/benches/binary_heap.rs b/library/alloctests/benches/binary_heap.rs similarity index 100% rename from library/alloc/benches/binary_heap.rs rename to library/alloctests/benches/binary_heap.rs diff --git a/library/alloc/benches/btree/map.rs b/library/alloctests/benches/btree/map.rs similarity index 100% rename from library/alloc/benches/btree/map.rs rename to library/alloctests/benches/btree/map.rs diff --git a/library/alloc/benches/btree/mod.rs b/library/alloctests/benches/btree/mod.rs similarity index 100% rename from library/alloc/benches/btree/mod.rs rename to library/alloctests/benches/btree/mod.rs diff --git a/library/alloc/benches/btree/set.rs b/library/alloctests/benches/btree/set.rs similarity index 100% rename from library/alloc/benches/btree/set.rs rename to library/alloctests/benches/btree/set.rs diff --git a/library/alloc/benches/lib.rs b/library/alloctests/benches/lib.rs similarity index 100% rename from library/alloc/benches/lib.rs rename to library/alloctests/benches/lib.rs diff --git a/library/alloc/benches/linked_list.rs b/library/alloctests/benches/linked_list.rs similarity index 100% rename from library/alloc/benches/linked_list.rs rename to library/alloctests/benches/linked_list.rs diff --git a/library/alloctests/benches/slice.rs b/library/alloctests/benches/slice.rs new file mode 100644 index 0000000000000..27b0e6fac0adb --- /dev/null +++ b/library/alloctests/benches/slice.rs @@ -0,0 +1,390 @@ +use std::ptr; + +use rand::Rng; +use rand::distr::{Alphanumeric, SampleString, StandardUniform}; +use test::{Bencher, black_box}; + +#[bench] +fn iterator(b: &mut Bencher) { + // peculiar numbers to stop LLVM from optimising the summation + // out. + let v: Vec<_> = (0..100).map(|i| i ^ (i << 1) ^ (i >> 1)).collect(); + + b.iter(|| { + let mut sum = 0; + for x in &v { + sum += *x; + } + // sum == 11806, to stop dead code elimination. + if sum == 0 { + panic!() + } + }) +} + +#[bench] +fn mut_iterator(b: &mut Bencher) { + let mut v = vec![0; 100]; + + b.iter(|| { + let mut i = 0; + for x in &mut v { + *x = i; + i += 1; + } + }) +} + +#[bench] +fn concat(b: &mut Bencher) { + let xss: Vec> = (0..100).map(|i| (0..i).collect()).collect(); + b.iter(|| { + xss.concat(); + }); +} + +#[bench] +fn join(b: &mut Bencher) { + let xss: Vec> = (0..100).map(|i| (0..i).collect()).collect(); + b.iter(|| xss.join(&0)); +} + +#[bench] +fn push(b: &mut Bencher) { + let mut vec = Vec::::new(); + b.iter(|| { + vec.push(0); + black_box(&vec); + }); +} + +#[bench] +fn starts_with_same_vector(b: &mut Bencher) { + let vec: Vec<_> = (0..100).collect(); + b.iter(|| vec.starts_with(&vec)) +} + +#[bench] +fn starts_with_single_element(b: &mut Bencher) { + let vec: Vec<_> = vec![0]; + b.iter(|| vec.starts_with(&vec)) +} + +#[bench] +fn starts_with_diff_one_element_at_end(b: &mut Bencher) { + let vec: Vec<_> = (0..100).collect(); + let mut match_vec: Vec<_> = (0..99).collect(); + match_vec.push(0); + b.iter(|| vec.starts_with(&match_vec)) +} + +#[bench] +fn ends_with_same_vector(b: &mut Bencher) { + let vec: Vec<_> = (0..100).collect(); + b.iter(|| vec.ends_with(&vec)) +} + +#[bench] +fn ends_with_single_element(b: &mut Bencher) { + let vec: Vec<_> = vec![0]; + b.iter(|| vec.ends_with(&vec)) +} + +#[bench] +fn ends_with_diff_one_element_at_beginning(b: &mut Bencher) { + let vec: Vec<_> = (0..100).collect(); + let mut match_vec: Vec<_> = (0..100).collect(); + match_vec[0] = 200; + b.iter(|| vec.starts_with(&match_vec)) +} + +#[bench] +fn contains_last_element(b: &mut Bencher) { + let vec: Vec<_> = (0..100).collect(); + b.iter(|| vec.contains(&99)) +} + +#[bench] +fn zero_1kb_from_elem(b: &mut Bencher) { + b.iter(|| vec![0u8; 1024]); +} + +#[bench] +fn zero_1kb_set_memory(b: &mut Bencher) { + b.iter(|| { + let mut v = Vec::::with_capacity(1024); + unsafe { + let vp = v.as_mut_ptr(); + ptr::write_bytes(vp, 0, 1024); + v.set_len(1024); + } + v + }); +} + +#[bench] +fn zero_1kb_loop_set(b: &mut Bencher) { + b.iter(|| { + let mut v = Vec::::with_capacity(1024); + unsafe { + v.set_len(1024); + } + for i in 0..1024 { + v[i] = 0; + } + }); +} + +#[bench] +fn zero_1kb_mut_iter(b: &mut Bencher) { + b.iter(|| { + let mut v = Vec::::with_capacity(1024); + unsafe { + v.set_len(1024); + } + for x in &mut v { + *x = 0; + } + v + }); +} + +#[bench] +fn random_inserts(b: &mut Bencher) { + let mut rng = crate::bench_rng(); + b.iter(|| { + let mut v = vec![(0, 0); 30]; + for _ in 0..100 { + let l = v.len(); + v.insert(rng.random::() as usize % (l + 1), (1, 1)); + } + }) +} + +#[bench] +fn random_removes(b: &mut Bencher) { + let mut rng = crate::bench_rng(); + b.iter(|| { + let mut v = vec![(0, 0); 130]; + for _ in 0..100 { + let l = v.len(); + v.remove(rng.random::() as usize % l); + } + }) +} + +fn gen_ascending(len: usize) -> Vec { + (0..len as u64).collect() +} + +fn gen_descending(len: usize) -> Vec { + (0..len as u64).rev().collect() +} + +fn gen_random(len: usize) -> Vec { + let mut rng = crate::bench_rng(); + (&mut rng).sample_iter(&StandardUniform).take(len).collect() +} + +fn gen_random_bytes(len: usize) -> Vec { + let mut rng = crate::bench_rng(); + (&mut rng).sample_iter(&StandardUniform).take(len).collect() +} + +fn gen_mostly_ascending(len: usize) -> Vec { + let mut rng = crate::bench_rng(); + let mut v = gen_ascending(len); + for _ in (0usize..).take_while(|x| x * x <= len) { + let x = rng.random::() as usize % len; + let y = rng.random::() as usize % len; + v.swap(x, y); + } + v +} + +fn gen_mostly_descending(len: usize) -> Vec { + let mut rng = crate::bench_rng(); + let mut v = gen_descending(len); + for _ in (0usize..).take_while(|x| x * x <= len) { + let x = rng.random::() as usize % len; + let y = rng.random::() as usize % len; + v.swap(x, y); + } + v +} + +fn gen_strings(len: usize) -> Vec { + let mut rng = crate::bench_rng(); + let mut v = vec![]; + for _ in 0..len { + let n = rng.random::() % 20 + 1; + v.push(Alphanumeric.sample_string(&mut rng, n as usize)); + } + v +} + +fn gen_big_random(len: usize) -> Vec<[u64; 16]> { + let mut rng = crate::bench_rng(); + (&mut rng).sample_iter(&StandardUniform).map(|x| [x; 16]).take(len).collect() +} + +macro_rules! sort { + ($f:ident, $name:ident, $gen:expr, $len:expr) => { + #[bench] + fn $name(b: &mut Bencher) { + let v = $gen($len); + b.iter(|| v.clone().$f()); + b.bytes = $len * size_of_val(&$gen(1)[0]) as u64; + } + }; +} + +macro_rules! sort_strings { + ($f:ident, $name:ident, $gen:expr, $len:expr) => { + #[bench] + fn $name(b: &mut Bencher) { + let v = $gen($len); + let v = v.iter().map(|s| &**s).collect::>(); + b.iter(|| v.clone().$f()); + b.bytes = $len * size_of::<&str>() as u64; + } + }; +} + +macro_rules! sort_expensive { + ($f:ident, $name:ident, $gen:expr, $len:expr) => { + #[bench] + fn $name(b: &mut Bencher) { + let v = $gen($len); + b.iter(|| { + let mut v = v.clone(); + let mut count = 0; + v.$f(|a: &u64, b: &u64| { + count += 1; + if count % 1_000_000_000 == 0 { + panic!("should not happen"); + } + (*a as f64).cos().partial_cmp(&(*b as f64).cos()).unwrap() + }); + black_box(count); + }); + b.bytes = $len * size_of_val(&$gen(1)[0]) as u64; + } + }; +} + +macro_rules! sort_lexicographic { + ($f:ident, $name:ident, $gen:expr, $len:expr) => { + #[bench] + fn $name(b: &mut Bencher) { + let v = $gen($len); + b.iter(|| v.clone().$f(|x| x.to_string())); + b.bytes = $len * size_of_val(&$gen(1)[0]) as u64; + } + }; +} + +sort!(sort, sort_small_ascending, gen_ascending, 10); +sort!(sort, sort_small_descending, gen_descending, 10); +sort!(sort, sort_small_random, gen_random, 10); +sort!(sort, sort_small_big, gen_big_random, 10); +sort!(sort, sort_medium_random, gen_random, 100); +sort!(sort, sort_large_ascending, gen_ascending, 10000); +sort!(sort, sort_large_descending, gen_descending, 10000); +sort!(sort, sort_large_mostly_ascending, gen_mostly_ascending, 10000); +sort!(sort, sort_large_mostly_descending, gen_mostly_descending, 10000); +sort!(sort, sort_large_random, gen_random, 10000); +sort!(sort, sort_large_big, gen_big_random, 10000); +sort_strings!(sort, sort_large_strings, gen_strings, 10000); +sort_expensive!(sort_by, sort_large_expensive, gen_random, 10000); + +sort!(sort_unstable, sort_unstable_small_ascending, gen_ascending, 10); +sort!(sort_unstable, sort_unstable_small_descending, gen_descending, 10); +sort!(sort_unstable, sort_unstable_small_random, gen_random, 10); +sort!(sort_unstable, sort_unstable_small_big, gen_big_random, 10); +sort!(sort_unstable, sort_unstable_medium_random, gen_random, 100); +sort!(sort_unstable, sort_unstable_large_ascending, gen_ascending, 10000); +sort!(sort_unstable, sort_unstable_large_descending, gen_descending, 10000); +sort!(sort_unstable, sort_unstable_large_mostly_ascending, gen_mostly_ascending, 10000); +sort!(sort_unstable, sort_unstable_large_mostly_descending, gen_mostly_descending, 10000); +sort!(sort_unstable, sort_unstable_large_random, gen_random, 10000); +sort!(sort_unstable, sort_unstable_large_big, gen_big_random, 10000); +sort_strings!(sort_unstable, sort_unstable_large_strings, gen_strings, 10000); +sort_expensive!(sort_unstable_by, sort_unstable_large_expensive, gen_random, 10000); + +sort_lexicographic!(sort_by_key, sort_by_key_lexicographic, gen_random, 10000); +sort_lexicographic!(sort_unstable_by_key, sort_unstable_by_key_lexicographic, gen_random, 10000); +sort_lexicographic!(sort_by_cached_key, sort_by_cached_key_lexicographic, gen_random, 10000); + +macro_rules! reverse { + ($name:ident, $ty:ty, $f:expr) => { + #[bench] + fn $name(b: &mut Bencher) { + // odd length and offset by 1 to be as unaligned as possible + let n = 0xFFFFF; + let mut v: Vec<_> = (0..1 + (n / size_of::<$ty>() as u64)).map($f).collect(); + b.iter(|| black_box(&mut v[1..]).reverse()); + b.bytes = n; + } + }; +} + +reverse!(reverse_u8, u8, |x| x as u8); +reverse!(reverse_u16, u16, |x| x as u16); +reverse!(reverse_u8x3, [u8; 3], |x| [x as u8, (x >> 8) as u8, (x >> 16) as u8]); +reverse!(reverse_u32, u32, |x| x as u32); +reverse!(reverse_u64, u64, |x| x as u64); +reverse!(reverse_u128, u128, |x| x as u128); +#[repr(simd)] +struct F64x4([f64; 4]); +reverse!(reverse_simd_f64x4, F64x4, |x| { + let x = x as f64; + F64x4([x, x, x, x]) +}); + +macro_rules! rotate { + ($name:ident, $gen:expr, $len:expr, $mid:expr) => { + #[bench] + fn $name(b: &mut Bencher) { + let size = size_of_val(&$gen(1)[0]); + let mut v = $gen($len * 8 / size); + b.iter(|| black_box(&mut v).rotate_left(($mid * 8 + size - 1) / size)); + b.bytes = (v.len() * size) as u64; + } + }; +} + +rotate!(rotate_tiny_by1, gen_random, 16, 1); +rotate!(rotate_tiny_half, gen_random, 16, 16 / 2); +rotate!(rotate_tiny_half_plus_one, gen_random, 16, 16 / 2 + 1); + +rotate!(rotate_medium_by1, gen_random, 9158, 1); +rotate!(rotate_medium_by727_u64, gen_random, 9158, 727); +rotate!(rotate_medium_by727_bytes, gen_random_bytes, 9158, 727); +rotate!(rotate_medium_by727_strings, gen_strings, 9158, 727); +rotate!(rotate_medium_half, gen_random, 9158, 9158 / 2); +rotate!(rotate_medium_half_plus_one, gen_random, 9158, 9158 / 2 + 1); + +// Intended to use more RAM than the machine has cache +#[cfg(not(target_os = "emscripten"))] // hits an OOM +rotate!(rotate_huge_by1, gen_random, 5 * 1024 * 1024, 1); +#[cfg(not(target_os = "emscripten"))] // hits an OOM +rotate!(rotate_huge_by9199_u64, gen_random, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM +rotate!(rotate_huge_by9199_bytes, gen_random_bytes, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM +rotate!(rotate_huge_by9199_strings, gen_strings, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM +rotate!(rotate_huge_by9199_big, gen_big_random, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM +rotate!(rotate_huge_by1234577_u64, gen_random, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM +rotate!(rotate_huge_by1234577_bytes, gen_random_bytes, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM +rotate!(rotate_huge_by1234577_strings, gen_strings, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM +rotate!(rotate_huge_by1234577_big, gen_big_random, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM +rotate!(rotate_huge_half, gen_random, 5 * 1024 * 1024, 5 * 1024 * 1024 / 2); +#[cfg(not(target_os = "emscripten"))] // hits an OOM +rotate!(rotate_huge_half_plus_one, gen_random, 5 * 1024 * 1024, 5 * 1024 * 1024 / 2 + 1); diff --git a/library/alloc/benches/str.rs b/library/alloctests/benches/str.rs similarity index 100% rename from library/alloc/benches/str.rs rename to library/alloctests/benches/str.rs diff --git a/library/alloc/benches/string.rs b/library/alloctests/benches/string.rs similarity index 75% rename from library/alloc/benches/string.rs rename to library/alloctests/benches/string.rs index 3d79ab78c6950..0bbec12e4fdc6 100644 --- a/library/alloc/benches/string.rs +++ b/library/alloctests/benches/string.rs @@ -4,7 +4,7 @@ use test::{Bencher, black_box}; #[bench] fn bench_with_capacity(b: &mut Bencher) { - b.iter(|| String::with_capacity(100)); + b.iter(|| String::with_capacity(black_box(100))); } #[bench] @@ -12,7 +12,8 @@ fn bench_push_str(b: &mut Bencher) { let s = "ศไทย中华Việt Nam; Mary had a little lamb, Little lamb"; b.iter(|| { let mut r = String::new(); - r.push_str(s); + black_box(&mut r).push_str(black_box(s)); + r }); } @@ -24,8 +25,9 @@ fn bench_push_str_one_byte(b: &mut Bencher) { b.iter(|| { let mut r = String::new(); for _ in 0..REPETITIONS { - r.push_str("a") + black_box(&mut r).push_str(black_box("a")); } + r }); } @@ -35,8 +37,9 @@ fn bench_push_char_one_byte(b: &mut Bencher) { b.iter(|| { let mut r = String::new(); for _ in 0..REPETITIONS { - r.push('a') + black_box(&mut r).push(black_box('a')); } + r }); } @@ -46,8 +49,9 @@ fn bench_push_char_two_bytes(b: &mut Bencher) { b.iter(|| { let mut r = String::new(); for _ in 0..REPETITIONS { - r.push('â') + black_box(&mut r).push(black_box('â')); } + r }); } @@ -57,34 +61,26 @@ fn from_utf8_lossy_100_ascii(b: &mut Bencher) { Lorem ipsum dolor sit amet, consectetur. "; assert_eq!(100, s.len()); - b.iter(|| { - let _ = String::from_utf8_lossy(s); - }); + b.iter(|| String::from_utf8_lossy(black_box(s))); } #[bench] fn from_utf8_lossy_100_multibyte(b: &mut Bencher) { let s = "𐌀𐌖𐌋𐌄𐌑𐌉ปรدولة الكويتทศไทย中华𐍅𐌿𐌻𐍆𐌹𐌻𐌰".as_bytes(); assert_eq!(100, s.len()); - b.iter(|| { - let _ = String::from_utf8_lossy(s); - }); + b.iter(|| String::from_utf8_lossy(black_box(s))); } #[bench] fn from_utf8_lossy_invalid(b: &mut Bencher) { let s = b"Hello\xC0\x80 There\xE6\x83 Goodbye"; - b.iter(|| { - let _ = String::from_utf8_lossy(s); - }); + b.iter(|| String::from_utf8_lossy(black_box(s))); } #[bench] fn from_utf8_lossy_100_invalid(b: &mut Bencher) { let s = repeat(0xf5).take(100).collect::>(); - b.iter(|| { - let _ = String::from_utf8_lossy(&s); - }); + b.iter(|| String::from_utf8_lossy(black_box(&s))); } #[bench] @@ -96,8 +92,8 @@ fn bench_exact_size_shrink_to_fit(b: &mut Bencher) { r.push_str(s); assert_eq!(r.len(), r.capacity()); b.iter(|| { - let mut r = String::with_capacity(s.len()); - r.push_str(s); + let mut r = String::with_capacity(black_box(s.len())); + r.push_str(black_box(s)); r.shrink_to_fit(); r }); @@ -107,21 +103,21 @@ fn bench_exact_size_shrink_to_fit(b: &mut Bencher) { fn bench_from_str(b: &mut Bencher) { let s = "Hello there, the quick brown fox jumped over the lazy dog! \ Lorem ipsum dolor sit amet, consectetur. "; - b.iter(|| String::from(s)) + b.iter(|| String::from(black_box(s))) } #[bench] fn bench_from(b: &mut Bencher) { let s = "Hello there, the quick brown fox jumped over the lazy dog! \ Lorem ipsum dolor sit amet, consectetur. "; - b.iter(|| String::from(s)) + b.iter(|| String::from(black_box(s))) } #[bench] fn bench_to_string(b: &mut Bencher) { let s = "Hello there, the quick brown fox jumped over the lazy dog! \ Lorem ipsum dolor sit amet, consectetur. "; - b.iter(|| s.to_string()) + b.iter(|| black_box(s).to_string()) } #[bench] @@ -129,7 +125,7 @@ fn bench_insert_char_short(b: &mut Bencher) { let s = "Hello, World!"; b.iter(|| { let mut x = String::from(s); - black_box(&mut x).insert(6, black_box(' ')); + black_box(&mut x).insert(black_box(6), black_box(' ')); x }) } @@ -139,7 +135,7 @@ fn bench_insert_char_long(b: &mut Bencher) { let s = "Hello, World!"; b.iter(|| { let mut x = String::from(s); - black_box(&mut x).insert(6, black_box('❤')); + black_box(&mut x).insert(black_box(6), black_box('❤')); x }) } @@ -149,7 +145,7 @@ fn bench_insert_str_short(b: &mut Bencher) { let s = "Hello, World!"; b.iter(|| { let mut x = String::from(s); - black_box(&mut x).insert_str(6, black_box(" ")); + black_box(&mut x).insert_str(black_box(6), black_box(" ")); x }) } @@ -159,7 +155,7 @@ fn bench_insert_str_long(b: &mut Bencher) { let s = "Hello, World!"; b.iter(|| { let mut x = String::from(s); - black_box(&mut x).insert_str(6, black_box(" rustic ")); + black_box(&mut x).insert_str(black_box(6), black_box(" rustic ")); x }) } diff --git a/library/alloctests/benches/vec.rs b/library/alloctests/benches/vec.rs new file mode 100644 index 0000000000000..1dab71fa1f4f4 --- /dev/null +++ b/library/alloctests/benches/vec.rs @@ -0,0 +1,878 @@ +use std::iter::repeat; + +use rand::RngCore; +use test::{Bencher, black_box}; + +#[bench] +fn bench_new(b: &mut Bencher) { + b.iter(|| Vec::::new()) +} + +fn do_bench_with_capacity(b: &mut Bencher, src_len: usize) { + b.bytes = src_len as u64; + + b.iter(|| Vec::::with_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(|| (0..src_len).collect::>()) +} + +#[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(|| repeat(5).take(src_len).collect::>()) +} + +#[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(|| src.as_slice().to_vec()); +} + +#[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.iter().cloned()); + 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()); + 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); + dst + }); +} + +#[bench] +fn bench_extend_recycle(b: &mut Bencher) { + let mut data = vec![0; 1000]; + + b.iter(|| { + let tmp = std::mem::take(&mut data); + 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(|| src.clone()); +} + +#[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); + dst = black_box(dst); + } + 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]); + src.into_iter() + .enumerate() + .map(|(idx, e)| idx as $type ^ e) + .collect::>() + }); + } + )+ + }; +} + +bench_in_place![ + bench_in_place_xxu8_0010_i0, u8, 10, 0; + bench_in_place_xxu8_0100_i0, u8, 100, 0; + bench_in_place_xxu8_1000_i0, u8, 1000, 0; + bench_in_place_xxu8_0010_i1, u8, 10, 1; + bench_in_place_xxu8_0100_i1, u8, 100, 1; + bench_in_place_xxu8_1000_i1, u8, 1000, 1; + bench_in_place_xu32_0010_i0, u32, 10, 0; + bench_in_place_xu32_0100_i0, u32, 100, 0; + bench_in_place_xu32_1000_i0, u32, 1000, 0; + bench_in_place_xu32_0010_i1, u32, 10, 1; + bench_in_place_xu32_0100_i1, u32, 100, 1; + bench_in_place_xu32_1000_i1, u32, 1000, 1; + bench_in_place_u128_0010_i0, u128, 10, 0; + bench_in_place_u128_0100_i0, u128, 100, 0; + bench_in_place_u128_1000_i0, u128, 1000, 0; + bench_in_place_u128_0010_i1, u128, 10, 1; + bench_in_place_u128_0100_i1, u128, 100, 1; + bench_in_place_u128_1000_i1, u128, 1000, 1 +]; + +#[bench] +fn bench_in_place_recycle(b: &mut Bencher) { + let mut data = vec![0; 1000]; + + b.iter(|| { + let tmp = std::mem::take(&mut data); + data = black_box( + tmp.into_iter() + .enumerate() + .map(|(idx, e)| idx.wrapping_add(e)) + .fuse() + .collect::>(), + ); + }); +} + +#[bench] +fn bench_in_place_zip_recycle(b: &mut Bencher) { + let mut data = vec![0u8; 1000]; + let mut rng = crate::bench_rng(); + let mut subst = vec![0u8; 1000]; + rng.fill_bytes(&mut subst[..]); + + b.iter(|| { + let tmp = std::mem::take(&mut data); + let mangled = tmp + .into_iter() + .zip(subst.iter().copied()) + .enumerate() + .map(|(i, (d, s))| d.wrapping_add(i as u8) ^ s) + .collect::>(); + data = black_box(mangled); + }); +} + +#[bench] +fn bench_in_place_zip_iter_mut(b: &mut Bencher) { + let mut data = vec![0u8; 256]; + let mut rng = crate::bench_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); +} + +pub fn vec_cast(input: Vec) -> Vec { + input.into_iter().map(|e| unsafe { std::mem::transmute_copy(&e) }).collect() +} + +#[bench] +fn bench_transmute(b: &mut Bencher) { + let mut vec = vec![10u32; 100]; + b.bytes = 800; // 2 casts x 4 bytes x 100 + b.iter(|| { + let v = std::mem::take(&mut vec); + let v = black_box(vec_cast::(v)); + let v = black_box(vec_cast::(v)); + vec = v; + }); +} + +#[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 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::>() + }) +} + +// node.js gives out of memory error to use with length 1_100_000 +#[cfg(target_os = "emscripten")] +const LEN: usize = 4096; + +#[cfg(not(target_os = "emscripten"))] +const LEN: usize = 16384; + +#[bench] +fn bench_chain_collect(b: &mut Bencher) { + let data = black_box([0; LEN]); + b.iter(|| data.iter().cloned().chain([1]).collect::>()); +} + +#[bench] +fn bench_chain_chain_collect(b: &mut Bencher) { + let data = black_box([0; LEN]); + b.iter(|| data.iter().cloned().chain([1]).chain([2]).collect::>()); +} + +#[bench] +fn bench_nest_chain_chain_collect(b: &mut Bencher) { + let data = black_box([0; LEN]); + b.iter(|| { + data.iter().cloned().chain([1].iter().chain([2].iter()).cloned()).collect::>() + }); +} + +#[bench] +fn bench_range_map_collect(b: &mut Bencher) { + b.iter(|| (0..LEN).map(|_| u32::default()).collect::>()); +} + +#[bench] +fn bench_chain_extend_ref(b: &mut 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 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 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 Bencher) { + let data = black_box([0; LEN]); + b.iter(|| { + let mut v = Vec::::with_capacity(data.len()); + v.extend(data.iter().rev()); + v + }); +} + +#[bench] +fn bench_map_regular(b: &mut 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 Bencher) { + let data = black_box([(0, 0); LEN]); + b.iter(|| { + let mut result: Vec = Vec::with_capacity(data.len()); + for i in 0..data.len() { + unsafe { + *result.as_mut_ptr().add(i) = data[i].0; + result.set_len(i); + } + } + result + }); +} + +fn random_sorted_fill(mut seed: u32, buf: &mut [u32]) { + let mask = if buf.len() < 8192 { + 0xFF + } else if buf.len() < 200_000 { + 0xFFFF + } else { + 0xFFFF_FFFF + }; + + for item in buf.iter_mut() { + seed ^= seed << 13; + seed ^= seed >> 17; + seed ^= seed << 5; + + *item = seed & mask; + } + + buf.sort(); +} + +// Measures performance of slice dedup impl. +// This was used to justify separate implementation of dedup for Vec. +// This algorithm was used for Vecs prior to Rust 1.52. +fn bench_dedup_slice_truncate(b: &mut Bencher, sz: usize) { + let mut template = vec![0u32; sz]; + b.bytes = size_of_val(template.as_slice()) as u64; + random_sorted_fill(0x43, &mut template); + + let mut vec = template.clone(); + b.iter(|| { + let vec = black_box(&mut vec); + let len = { + let (dedup, _) = vec.partition_dedup(); + dedup.len() + }; + vec.truncate(len); + + black_box(vec.first()); + let vec = black_box(vec); + vec.clear(); + vec.extend_from_slice(&template); + }); +} + +// Measures performance of Vec::dedup on random data. +fn bench_vec_dedup_random(b: &mut Bencher, sz: usize) { + let mut template = vec![0u32; sz]; + b.bytes = size_of_val(template.as_slice()) as u64; + random_sorted_fill(0x43, &mut template); + + let mut vec = template.clone(); + b.iter(|| { + let vec = black_box(&mut vec); + vec.dedup(); + black_box(vec.first()); + let vec = black_box(vec); + vec.clear(); + vec.extend_from_slice(&template); + }); +} + +// Measures performance of Vec::dedup when there is no items removed +fn bench_vec_dedup_none(b: &mut Bencher, sz: usize) { + let mut template = vec![0u32; sz]; + b.bytes = size_of_val(template.as_slice()) as u64; + template.chunks_exact_mut(2).for_each(|w| { + w[0] = black_box(0); + w[1] = black_box(5); + }); + + let mut vec = template.clone(); + b.iter(|| { + let vec = black_box(&mut vec); + vec.dedup(); + black_box(vec.first()); + // Unlike other benches of `dedup` + // this doesn't reinitialize vec + // because we measure how efficient dedup is + // when no memory written + }); +} + +// Measures performance of Vec::dedup when there is all items removed +fn bench_vec_dedup_all(b: &mut Bencher, sz: usize) { + let mut template = vec![0u32; sz]; + b.bytes = size_of_val(template.as_slice()) as u64; + template.iter_mut().for_each(|w| { + *w = black_box(0); + }); + + let mut vec = template.clone(); + b.iter(|| { + let vec = black_box(&mut vec); + vec.dedup(); + black_box(vec.first()); + let vec = black_box(vec); + vec.clear(); + vec.extend_from_slice(&template); + }); +} + +#[bench] +fn bench_dedup_slice_truncate_100(b: &mut Bencher) { + bench_dedup_slice_truncate(b, 100); +} +#[bench] +fn bench_dedup_random_100(b: &mut Bencher) { + bench_vec_dedup_random(b, 100); +} + +#[bench] +fn bench_dedup_none_100(b: &mut Bencher) { + bench_vec_dedup_none(b, 100); +} + +#[bench] +fn bench_dedup_all_100(b: &mut Bencher) { + bench_vec_dedup_all(b, 100); +} + +#[bench] +fn bench_dedup_slice_truncate_1000(b: &mut Bencher) { + bench_dedup_slice_truncate(b, 1000); +} +#[bench] +fn bench_dedup_random_1000(b: &mut Bencher) { + bench_vec_dedup_random(b, 1000); +} + +#[bench] +fn bench_dedup_none_1000(b: &mut Bencher) { + bench_vec_dedup_none(b, 1000); +} + +#[bench] +fn bench_dedup_all_1000(b: &mut Bencher) { + bench_vec_dedup_all(b, 1000); +} + +#[bench] +fn bench_dedup_slice_truncate_10000(b: &mut Bencher) { + bench_dedup_slice_truncate(b, 10000); +} +#[bench] +fn bench_dedup_random_10000(b: &mut Bencher) { + bench_vec_dedup_random(b, 10000); +} + +#[bench] +fn bench_dedup_none_10000(b: &mut Bencher) { + bench_vec_dedup_none(b, 10000); +} + +#[bench] +fn bench_dedup_all_10000(b: &mut Bencher) { + bench_vec_dedup_all(b, 10000); +} + +#[bench] +fn bench_dedup_slice_truncate_100000(b: &mut Bencher) { + bench_dedup_slice_truncate(b, 100000); +} +#[bench] +fn bench_dedup_random_100000(b: &mut Bencher) { + bench_vec_dedup_random(b, 100000); +} + +#[bench] +fn bench_dedup_none_100000(b: &mut Bencher) { + bench_vec_dedup_none(b, 100000); +} + +#[bench] +fn bench_dedup_all_100000(b: &mut Bencher) { + bench_vec_dedup_all(b, 100000); +} + +#[bench] +fn bench_flat_map_collect(b: &mut Bencher) { + let v = vec![777u32; 500000]; + b.iter(|| v.iter().flat_map(|color| color.rotate_left(8).to_be_bytes()).collect::>()); +} + +/// Reference benchmark that `retain` has to compete with. +#[bench] +fn bench_retain_iter_100000(b: &mut Bencher) { + let mut v = Vec::with_capacity(100000); + + b.iter(|| { + let mut tmp = std::mem::take(&mut v); + tmp.clear(); + tmp.extend(black_box(1..=100000)); + v = tmp.into_iter().filter(|x| x & 1 == 0).collect(); + }); +} + +#[bench] +fn bench_retain_100000(b: &mut Bencher) { + let mut v = Vec::with_capacity(100000); + + b.iter(|| { + v.clear(); + v.extend(black_box(1..=100000)); + v.retain(|x| x & 1 == 0) + }); +} + +#[bench] +fn bench_retain_whole_100000(b: &mut Bencher) { + let mut v = black_box(vec![826u32; 100000]); + b.iter(|| v.retain(|x| *x == 826u32)); +} + +#[bench] +fn bench_next_chunk(b: &mut Bencher) { + let v = vec![13u8; 2048]; + + b.iter(|| { + const CHUNK: usize = 8; + + let mut sum = [0u32; CHUNK]; + let mut iter = black_box(v.clone()).into_iter(); + + while let Ok(chunk) = iter.next_chunk::() { + for i in 0..CHUNK { + sum[i] += chunk[i] as u32; + } + } + + sum + }) +} diff --git a/library/alloc/benches/vec_deque.rs b/library/alloctests/benches/vec_deque.rs similarity index 100% rename from library/alloc/benches/vec_deque.rs rename to library/alloctests/benches/vec_deque.rs diff --git a/library/alloc/benches/vec_deque_append.rs b/library/alloctests/benches/vec_deque_append.rs similarity index 100% rename from library/alloc/benches/vec_deque_append.rs rename to library/alloctests/benches/vec_deque_append.rs diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs new file mode 100644 index 0000000000000..56e60ed4c8448 --- /dev/null +++ b/library/alloctests/lib.rs @@ -0,0 +1,95 @@ +#![cfg(test)] +#![allow(unused_attributes)] +#![unstable(feature = "alloctests", issue = "none")] +#![no_std] +// Lints: +#![deny(unsafe_op_in_unsafe_fn)] +#![warn(deprecated_in_future)] +#![warn(missing_debug_implementations)] +#![allow(explicit_outlives_requirements)] +#![allow(internal_features)] +#![allow(rustdoc::redundant_explicit_links)] +#![warn(rustdoc::unescaped_backticks)] +#![deny(ffi_unwind_calls)] +// +// Library features: +// tidy-alphabetical-start +#![feature(alloc_layout_extra)] +#![feature(allocator_api)] +#![feature(array_into_iter_constructors)] +#![feature(assert_matches)] +#![feature(core_intrinsics)] +#![feature(exact_size_is_empty)] +#![feature(extend_one)] +#![feature(extend_one_unchecked)] +#![feature(hasher_prefixfree_extras)] +#![feature(inplace_iteration)] +#![feature(iter_advance_by)] +#![feature(iter_next_chunk)] +#![feature(maybe_uninit_slice)] +#![feature(maybe_uninit_uninit_array_transpose)] +#![feature(nonnull_provenance)] +#![feature(ptr_alignment_type)] +#![feature(ptr_internals)] +#![feature(sized_type_properties)] +#![feature(slice_iter_mut_as_mut_slice)] +#![feature(slice_ptr_get)] +#![feature(slice_range)] +#![feature(std_internals)] +#![feature(temporary_niche_types)] +#![feature(trusted_fused)] +#![feature(trusted_len)] +#![feature(trusted_random_access)] +#![feature(try_reserve_kind)] +#![feature(try_trait_v2)] +// tidy-alphabetical-end +// +// Language features: +// tidy-alphabetical-start +#![feature(cfg_sanitize)] +#![feature(dropck_eyepatch)] +#![feature(lang_items)] +#![feature(min_specialization)] +#![feature(negative_impls)] +#![feature(never_type)] +#![feature(optimize_attribute)] +#![feature(rustc_allow_const_fn_unstable)] +#![feature(rustc_attrs)] +#![feature(staged_api)] +#![feature(test)] +#![rustc_preserve_ub_checks] +// tidy-alphabetical-end + +// Allow testing this library +extern crate alloc as realalloc; +#[macro_use] +extern crate std; +#[cfg(test)] +extern crate test; +mod testing; +use realalloc::*; + +// We are directly including collections and raw_vec here as both use non-public +// methods and fields in tests and as such need to have the types to test in the +// same crate as the tests themself. +#[path = "../alloc/src/collections/mod.rs"] +mod collections; + +#[path = "../alloc/src/raw_vec/mod.rs"] +mod raw_vec; + +#[allow(dead_code)] // Not used in all configurations +pub(crate) mod test_helpers { + /// Copied from `std::test_helpers::test_rng`, since these tests rely on the + /// seed not being the same for every RNG invocation too. + pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { + use std::hash::{BuildHasher, Hash, Hasher}; + let mut hasher = std::hash::RandomState::new().build_hasher(); + std::panic::Location::caller().hash(&mut hasher); + let hc64 = hasher.finish(); + let seed_vec = + hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); + let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); + rand::SeedableRng::from_seed(seed) + } +} diff --git a/library/alloc/src/testing/crash_test.rs b/library/alloctests/testing/crash_test.rs similarity index 100% rename from library/alloc/src/testing/crash_test.rs rename to library/alloctests/testing/crash_test.rs diff --git a/library/alloc/src/testing/mod.rs b/library/alloctests/testing/mod.rs similarity index 100% rename from library/alloc/src/testing/mod.rs rename to library/alloctests/testing/mod.rs diff --git a/library/alloc/src/testing/ord_chaos.rs b/library/alloctests/testing/ord_chaos.rs similarity index 100% rename from library/alloc/src/testing/ord_chaos.rs rename to library/alloctests/testing/ord_chaos.rs diff --git a/library/alloc/src/testing/rng.rs b/library/alloctests/testing/rng.rs similarity index 100% rename from library/alloc/src/testing/rng.rs rename to library/alloctests/testing/rng.rs diff --git a/library/alloc/tests/alloc.rs b/library/alloctests/tests/alloc_test.rs similarity index 100% rename from library/alloc/tests/alloc.rs rename to library/alloctests/tests/alloc_test.rs diff --git a/library/alloc/tests/arc.rs b/library/alloctests/tests/arc.rs similarity index 88% rename from library/alloc/tests/arc.rs rename to library/alloctests/tests/arc.rs index a259c0131ecdf..00bdf527133f7 100644 --- a/library/alloc/tests/arc.rs +++ b/library/alloctests/tests/arc.rs @@ -1,8 +1,7 @@ use std::any::Any; use std::cell::{Cell, RefCell}; use std::iter::TrustedLen; -use std::mem; -use std::sync::{Arc, Weak}; +use std::sync::{Arc, UniqueArc, Weak}; #[test] fn uninhabited() { @@ -129,7 +128,7 @@ fn shared_from_iter_trustedlen_normal() { let vec = iter.clone().collect::>(); let rc = iter.collect::>(); assert_eq!(&*vec, &*rc); - assert_eq!(mem::size_of::>() * SHARED_ITER_MAX as usize, mem::size_of_val(&*rc)); + assert_eq!(size_of::>() * SHARED_ITER_MAX as usize, size_of_val(&*rc)); // Clone a bit and let these get dropped. { @@ -145,7 +144,7 @@ fn shared_from_iter_trustedlen_normal() { let vec = iter.clone().collect::>(); let rc = iter.collect::>(); assert_eq!(&*vec, &*rc); - assert_eq!(0, mem::size_of_val(&*rc)); + assert_eq!(0, size_of_val(&*rc)); { let _rc_2 = rc.clone(); let _rc_3 = rc.clone(); @@ -264,9 +263,30 @@ fn make_mut_unsized() { assert_eq!(*other_data, [110, 20, 30]); } +#[test] +fn test_unique_arc_weak() { + let data = UniqueArc::new(32); + + // Test that `Weak` downgraded from `UniqueArc` cannot be upgraded. + let weak = UniqueArc::downgrade(&data); + assert_eq!(weak.strong_count(), 0); + assert_eq!(weak.weak_count(), 0); + assert!(weak.upgrade().is_none()); + + // Test that `Weak` can now be upgraded after the `UniqueArc` being converted to `Arc`. + let strong = UniqueArc::into_arc(data); + assert_eq!(*strong, 32); + assert_eq!(weak.strong_count(), 1); + assert_eq!(weak.weak_count(), 1); + let upgraded = weak.upgrade().unwrap(); + assert_eq!(*upgraded, 32); + assert_eq!(weak.strong_count(), 2); + assert_eq!(weak.weak_count(), 1); +} + #[allow(unused)] mod pin_coerce_unsized { - use alloc::sync::Arc; + use alloc::sync::{Arc, UniqueArc}; use core::pin::Pin; pub trait MyTrait {} @@ -276,4 +296,7 @@ mod pin_coerce_unsized { pub fn pin_arc(arg: Pin>) -> Pin> { arg } + pub fn pin_unique_arc(arg: Pin>) -> Pin> { + arg + } } diff --git a/library/alloc/tests/autotraits.rs b/library/alloctests/tests/autotraits.rs similarity index 100% rename from library/alloc/tests/autotraits.rs rename to library/alloctests/tests/autotraits.rs diff --git a/library/alloc/tests/borrow.rs b/library/alloctests/tests/borrow.rs similarity index 100% rename from library/alloc/tests/borrow.rs rename to library/alloctests/tests/borrow.rs diff --git a/library/alloc/tests/boxed.rs b/library/alloctests/tests/boxed.rs similarity index 100% rename from library/alloc/tests/boxed.rs rename to library/alloctests/tests/boxed.rs diff --git a/library/alloc/tests/btree_set_hash.rs b/library/alloctests/tests/btree_set_hash.rs similarity index 100% rename from library/alloc/tests/btree_set_hash.rs rename to library/alloctests/tests/btree_set_hash.rs diff --git a/library/alloc/tests/c_str.rs b/library/alloctests/tests/c_str.rs similarity index 100% rename from library/alloc/tests/c_str.rs rename to library/alloctests/tests/c_str.rs diff --git a/library/alloc/tests/c_str2.rs b/library/alloctests/tests/c_str2.rs similarity index 97% rename from library/alloc/tests/c_str2.rs rename to library/alloctests/tests/c_str2.rs index 0f4c27fa12322..fe7686bd1c592 100644 --- a/library/alloc/tests/c_str2.rs +++ b/library/alloctests/tests/c_str2.rs @@ -33,12 +33,6 @@ fn build_with_zero2() { assert!(CString::new(vec![0]).is_err()); } -#[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 { diff --git a/library/alloc/tests/collections/binary_heap.rs b/library/alloctests/tests/collections/binary_heap.rs similarity index 100% rename from library/alloc/tests/collections/binary_heap.rs rename to library/alloctests/tests/collections/binary_heap.rs diff --git a/library/alloc/tests/collections/mod.rs b/library/alloctests/tests/collections/mod.rs similarity index 100% rename from library/alloc/tests/collections/mod.rs rename to library/alloctests/tests/collections/mod.rs diff --git a/library/alloc/tests/const_fns.rs b/library/alloctests/tests/const_fns.rs similarity index 100% rename from library/alloc/tests/const_fns.rs rename to library/alloctests/tests/const_fns.rs diff --git a/library/alloc/tests/cow_str.rs b/library/alloctests/tests/cow_str.rs similarity index 100% rename from library/alloc/tests/cow_str.rs rename to library/alloctests/tests/cow_str.rs diff --git a/library/alloc/tests/fmt.rs b/library/alloctests/tests/fmt.rs similarity index 99% rename from library/alloc/tests/fmt.rs rename to library/alloctests/tests/fmt.rs index c13074c53b73d..dbcf0c3a11410 100644 --- a/library/alloc/tests/fmt.rs +++ b/library/alloctests/tests/fmt.rs @@ -1,6 +1,7 @@ #![deny(warnings)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] +#![allow(unnecessary_transmutes)] use std::cell::RefCell; use std::fmt::{self, Write}; diff --git a/library/alloc/tests/heap.rs b/library/alloctests/tests/heap.rs similarity index 100% rename from library/alloc/tests/heap.rs rename to library/alloctests/tests/heap.rs diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs new file mode 100644 index 0000000000000..38309585fad61 --- /dev/null +++ b/library/alloctests/tests/lib.rs @@ -0,0 +1,109 @@ +#![feature(allocator_api)] +#![feature(alloc_layout_extra)] +#![feature(iter_array_chunks)] +#![feature(assert_matches)] +#![feature(btree_extract_if)] +#![feature(char_max_len)] +#![feature(cow_is_borrowed)] +#![feature(core_intrinsics)] +#![feature(downcast_unchecked)] +#![feature(exact_size_is_empty)] +#![feature(hashmap_internals)] +#![feature(linked_list_cursors)] +#![feature(map_try_insert)] +#![feature(pattern)] +#![feature(trusted_len)] +#![feature(try_reserve_kind)] +#![feature(try_with_capacity)] +#![feature(unboxed_closures)] +#![feature(binary_heap_into_iter_sorted)] +#![feature(binary_heap_drain_sorted)] +#![feature(slice_ptr_get)] +#![feature(inplace_iteration)] +#![feature(iter_advance_by)] +#![feature(iter_next_chunk)] +#![feature(round_char_boundary)] +#![feature(slice_partition_dedup)] +#![feature(string_from_utf8_lossy_owned)] +#![feature(string_remove_matches)] +#![feature(const_btree_len)] +#![feature(const_trait_impl)] +#![feature(panic_update_hook)] +#![feature(pointer_is_aligned_to)] +#![feature(test)] +#![feature(thin_box)] +#![feature(drain_keep_rest)] +#![feature(local_waker)] +#![feature(str_as_str)] +#![feature(strict_provenance_lints)] +#![feature(vec_deque_pop_if)] +#![feature(vec_deque_truncate_front)] +#![feature(unique_rc_arc)] +#![feature(macro_metavar_expr_concat)] +#![allow(internal_features)] +#![deny(fuzzy_provenance_casts)] +#![deny(unsafe_op_in_unsafe_fn)] + +extern crate alloc; +extern crate test; + +use std::hash::{DefaultHasher, Hash, Hasher}; + +mod alloc_test; +mod arc; +mod autotraits; +mod borrow; +mod boxed; +mod btree_set_hash; +mod c_str; +mod c_str2; +mod collections; +mod const_fns; +mod cow_str; +mod fmt; +mod heap; +mod linked_list; +mod misc_tests; +mod num; +mod rc; +mod slice; +mod sort; +mod str; +mod string; +mod sync; +mod task; +mod testing; +mod thin_box; +mod vec; +mod vec_deque; + +fn hash(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} + +/// Copied from `std::test_helpers::test_rng`, since these tests rely on the +/// seed not being the same for every RNG invocation too. +fn test_rng() -> rand_xorshift::XorShiftRng { + use std::hash::{BuildHasher, Hash, Hasher}; + let mut hasher = std::hash::RandomState::new().build_hasher(); + std::panic::Location::caller().hash(&mut hasher); + let hc64 = hasher.finish(); + let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); + let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); + rand::SeedableRng::from_seed(seed) +} + +#[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/library/alloc/tests/linked_list.rs b/library/alloctests/tests/linked_list.rs similarity index 100% rename from library/alloc/tests/linked_list.rs rename to library/alloctests/tests/linked_list.rs diff --git a/library/alloc/tests/misc_tests.rs b/library/alloctests/tests/misc_tests.rs similarity index 100% rename from library/alloc/tests/misc_tests.rs rename to library/alloctests/tests/misc_tests.rs diff --git a/library/alloctests/tests/num.rs b/library/alloctests/tests/num.rs new file mode 100644 index 0000000000000..3c76e68c60640 --- /dev/null +++ b/library/alloctests/tests/num.rs @@ -0,0 +1,69 @@ +use std::fmt::{Debug, Display}; +use std::str::FromStr; + +fn assert_nb(value: Int) { + let s = value.to_string(); + let s2 = format!("s: {}.", value); + + assert_eq!(format!("s: {s}."), s2); + let Ok(ret) = Int::from_str(&s) else { + panic!("failed to convert into to string"); + }; + assert_eq!(ret, value); +} + +macro_rules! uint_to_s { + ($($fn_name:ident, $int:ident,)+) => { + $( + #[test] + fn $fn_name() { + assert_nb::<$int>($int::MIN); + assert_nb::<$int>($int::MAX); + assert_nb::<$int>(1); + assert_nb::<$int>($int::MIN / 2); + assert_nb::<$int>($int::MAX / 2); + } + )+ + } +} +macro_rules! int_to_s { + ($($fn_name:ident, $int:ident,)+) => { + $( + #[test] + fn $fn_name() { + assert_nb::<$int>($int::MIN); + assert_nb::<$int>($int::MAX); + assert_nb::<$int>(1); + assert_nb::<$int>(0); + assert_nb::<$int>(-1); + assert_nb::<$int>($int::MIN / 2); + assert_nb::<$int>($int::MAX / 2); + } + )+ + } +} + +int_to_s!( + test_i8_to_string, + i8, + test_i16_to_string, + i16, + test_i32_to_string, + i32, + test_i64_to_string, + i64, + test_i128_to_string, + i128, +); +uint_to_s!( + test_u8_to_string, + u8, + test_u16_to_string, + u16, + test_u32_to_string, + u32, + test_u64_to_string, + u64, + test_u128_to_string, + u128, +); diff --git a/library/alloctests/tests/rc.rs b/library/alloctests/tests/rc.rs new file mode 100644 index 0000000000000..bb68eb4ac9e3d --- /dev/null +++ b/library/alloctests/tests/rc.rs @@ -0,0 +1,924 @@ +use std::any::Any; +use std::cell::{Cell, RefCell}; +use std::iter::TrustedLen; +use std::mem; +use std::rc::{Rc, UniqueRc, Weak}; + +#[test] +fn uninhabited() { + enum Void {} + let mut a = Weak::::new(); + a = a.clone(); + assert!(a.upgrade().is_none()); + + let mut a: Weak = a; // Unsizing + a = a.clone(); + assert!(a.upgrade().is_none()); +} + +#[test] +fn slice() { + let a: Rc<[u32; 3]> = Rc::new([3, 2, 1]); + let a: Rc<[u32]> = a; // Unsizing + let b: Rc<[u32]> = Rc::from(&[3, 2, 1][..]); // Conversion + assert_eq!(a, b); + + // Exercise is_dangling() with a DST + let mut a = Rc::downgrade(&a); + a = a.clone(); + assert!(a.upgrade().is_some()); +} + +#[test] +fn trait_object() { + let a: Rc = Rc::new(4); + let a: Rc = a; // Unsizing + + // Exercise is_dangling() with a DST + let mut a = Rc::downgrade(&a); + a = a.clone(); + assert!(a.upgrade().is_some()); + + let mut b = Weak::::new(); + b = b.clone(); + assert!(b.upgrade().is_none()); + let mut b: Weak = b; // Unsizing + b = b.clone(); + assert!(b.upgrade().is_none()); +} + +#[test] +fn float_nan_ne() { + let x = Rc::new(f32::NAN); + assert!(x != x); + assert!(!(x == x)); +} + +#[test] +fn partial_eq() { + struct TestPEq(RefCell); + impl PartialEq for TestPEq { + fn eq(&self, other: &TestPEq) -> bool { + *self.0.borrow_mut() += 1; + *other.0.borrow_mut() += 1; + true + } + } + let x = Rc::new(TestPEq(RefCell::new(0))); + assert!(x == x); + assert!(!(x != x)); + assert_eq!(*x.0.borrow(), 4); +} + +#[test] +fn eq() { + #[derive(Eq)] + struct TestEq(RefCell); + impl PartialEq for TestEq { + fn eq(&self, other: &TestEq) -> bool { + *self.0.borrow_mut() += 1; + *other.0.borrow_mut() += 1; + true + } + } + let x = Rc::new(TestEq(RefCell::new(0))); + assert!(x == x); + assert!(!(x != x)); + assert_eq!(*x.0.borrow(), 0); +} + +const SHARED_ITER_MAX: u16 = 100; + +fn assert_trusted_len(_: &I) {} + +#[test] +fn shared_from_iter_normal() { + // Exercise the base implementation for non-`TrustedLen` iterators. + { + // `Filter` is never `TrustedLen` since we don't + // know statically how many elements will be kept: + let iter = (0..SHARED_ITER_MAX).filter(|x| x % 2 == 0).map(Box::new); + + // Collecting into a `Vec` or `Rc<[T]>` should make no difference: + let vec = iter.clone().collect::>(); + let rc = iter.collect::>(); + assert_eq!(&*vec, &*rc); + + // Clone a bit and let these get dropped. + { + let _rc_2 = rc.clone(); + let _rc_3 = rc.clone(); + let _rc_4 = Rc::downgrade(&_rc_3); + } + } // Drop what hasn't been here. +} + +#[test] +fn shared_from_iter_trustedlen_normal() { + // Exercise the `TrustedLen` implementation under normal circumstances + // where `size_hint()` matches `(_, Some(exact_len))`. + { + let iter = (0..SHARED_ITER_MAX).map(Box::new); + assert_trusted_len(&iter); + + // Collecting into a `Vec` or `Rc<[T]>` should make no difference: + let vec = iter.clone().collect::>(); + let rc = iter.collect::>(); + assert_eq!(&*vec, &*rc); + assert_eq!(size_of::>() * SHARED_ITER_MAX as usize, size_of_val(&*rc)); + + // Clone a bit and let these get dropped. + { + let _rc_2 = rc.clone(); + let _rc_3 = rc.clone(); + let _rc_4 = Rc::downgrade(&_rc_3); + } + } // Drop what hasn't been here. + + // Try a ZST to make sure it is handled well. + { + let iter = (0..SHARED_ITER_MAX).map(drop); + let vec = iter.clone().collect::>(); + let rc = iter.collect::>(); + assert_eq!(&*vec, &*rc); + assert_eq!(0, size_of_val(&*rc)); + { + let _rc_2 = rc.clone(); + let _rc_3 = rc.clone(); + let _rc_4 = Rc::downgrade(&_rc_3); + } + } +} + +#[test] +#[should_panic = "I've almost got 99 problems."] +fn shared_from_iter_trustedlen_panic() { + // Exercise the `TrustedLen` implementation when `size_hint()` matches + // `(_, Some(exact_len))` but where `.next()` drops before the last iteration. + let iter = (0..SHARED_ITER_MAX).map(|val| match val { + 98 => panic!("I've almost got 99 problems."), + _ => Box::new(val), + }); + assert_trusted_len(&iter); + let _ = iter.collect::>(); + + panic!("I am unreachable."); +} + +#[test] +fn shared_from_iter_trustedlen_no_fuse() { + // Exercise the `TrustedLen` implementation when `size_hint()` matches + // `(_, Some(exact_len))` but where the iterator does not behave in a fused manner. + struct Iter(std::vec::IntoIter>>); + + unsafe impl TrustedLen for Iter {} + + impl Iterator for Iter { + fn size_hint(&self) -> (usize, Option) { + (2, Some(2)) + } + + type Item = Box; + + fn next(&mut self) -> Option { + self.0.next().flatten() + } + } + + let vec = vec![Some(Box::new(42)), Some(Box::new(24)), None, Some(Box::new(12))]; + let iter = Iter(vec.into_iter()); + assert_trusted_len(&iter); + assert_eq!(&[Box::new(42), Box::new(24)], &*iter.collect::>()); +} + +#[test] +fn weak_may_dangle() { + fn hmm<'a>(val: &'a mut Weak<&'a str>) -> Weak<&'a str> { + val.clone() + } + + // Without #[may_dangle] we get: + let mut val = Weak::new(); + hmm(&mut val); + // ~~~~~~~~ borrowed value does not live long enough + // + // `val` dropped here while still borrowed + // borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::rc::Weak` +} + +/// Test that a panic from a destructor does not leak the allocation. +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn panic_no_leak() { + use std::alloc::{AllocError, Allocator, Global, Layout}; + use std::panic::{AssertUnwindSafe, catch_unwind}; + use std::ptr::NonNull; + + struct AllocCount(Cell); + unsafe impl Allocator for AllocCount { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + self.0.set(self.0.get() + 1); + Global.allocate(layout) + } + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + self.0.set(self.0.get() - 1); + unsafe { Global.deallocate(ptr, layout) } + } + } + + struct PanicOnDrop; + impl Drop for PanicOnDrop { + fn drop(&mut self) { + panic!("PanicOnDrop"); + } + } + + let alloc = AllocCount(Cell::new(0)); + let rc = Rc::new_in(PanicOnDrop, &alloc); + assert_eq!(alloc.0.get(), 1); + + let panic_message = catch_unwind(AssertUnwindSafe(|| drop(rc))).unwrap_err(); + assert_eq!(*panic_message.downcast_ref::<&'static str>().unwrap(), "PanicOnDrop"); + assert_eq!(alloc.0.get(), 0); +} + +#[allow(unused)] +mod pin_coerce_unsized { + use alloc::rc::{Rc, UniqueRc}; + use core::pin::Pin; + + pub trait MyTrait {} + impl MyTrait for String {} + + // Pin coercion should work for Rc + pub fn pin_rc(arg: Pin>) -> Pin> { + arg + } + pub fn pin_unique_rc(arg: Pin>) -> Pin> { + arg + } +} + +#[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::new(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() { + fn is_unique(this: &Rc) -> bool { + Rc::weak_count(this) == 0 && Rc::strong_count(this) == 1 + } + + let x = Rc::new(3); + assert!(is_unique(&x)); + let y = x.clone(); + assert!(!is_unique(&x)); + drop(y); + assert!(is_unique(&x)); + let w = Rc::downgrade(&x); + assert!(!is_unique(&x)); + drop(w); + assert!(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_inner() { + let x = Rc::new(3); + assert_eq!(Rc::into_inner(x), Some(3)); + + let x = Rc::new(4); + let y = Rc::clone(&x); + assert_eq!(Rc::into_inner(x), None); + assert_eq!(Rc::into_inner(y), Some(4)); + + let x = Rc::new(5); + let _w = Rc::downgrade(&x); + assert_eq!(Rc::into_inner(x), Some(5)); +} + +#[test] +fn into_from_raw() { + let x = Rc::new(Box::new("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 into_from_weak_raw() { + let x = Rc::new(Box::new("hello")); + let y = Rc::downgrade(&x); + + let y_ptr = Weak::into_raw(y); + unsafe { + assert_eq!(**y_ptr, "hello"); + + let y = Weak::from_raw(y_ptr); + let y_up = Weak::upgrade(&y).unwrap(); + assert_eq!(**y_up, "hello"); + drop(y_up); + + assert_eq!(Rc::try_unwrap(x).map(|x| *x), Ok("hello")); + } +} + +#[test] +fn test_into_from_weak_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let arc: Rc = Rc::from("foo"); + let weak: Weak = Rc::downgrade(&arc); + + let ptr = Weak::into_raw(weak.clone()); + let weak2 = unsafe { Weak::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert!(weak.ptr_eq(&weak2)); + + let arc: Rc = Rc::new(123); + let weak: Weak = Rc::downgrade(&arc); + + let ptr = Weak::into_raw(weak.clone()); + let weak2 = unsafe { Weak::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert!(weak.ptr_eq(&weak2)); +} + +#[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()); +} + +/// This is similar to the doc-test for `Rc::make_mut()`, but on an unsized type (slice). +#[test] +fn test_cowrc_unsized() { + use std::rc::Rc; + + let mut data: Rc<[i32]> = Rc::new([10, 20, 30]); + + Rc::make_mut(&mut data)[0] += 1; // Won't clone anything + let mut other_data = Rc::clone(&data); // Won't clone inner data + Rc::make_mut(&mut data)[1] += 1; // Clones inner data + Rc::make_mut(&mut data)[2] += 1; // Won't clone anything + Rc::make_mut(&mut other_data)[0] *= 10; // Won't clone anything + + // Now `data` and `other_data` point to different allocations. + assert_eq!(*data, [11, 21, 31]); + assert_eq!(*other_data, [110, 20, 30]); +} + +#[test] +fn test_show() { + let foo = Rc::new(75); + 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_maybe_thin_unsized() { + // If/when custom thin DSTs exist, this test should be updated to use one + use std::ffi::CStr; + + let x: Rc = Rc::from(c"swordfish"); + assert_eq!(format!("{x:?}"), "\"swordfish\""); + let y: Weak = Rc::downgrade(&x); + drop(x); + + // At this point, the weak points to a dropped DST + assert!(y.upgrade().is_none()); + // But we still need to be able to get the alloc layout to drop. + // CStr has no drop glue, but custom DSTs might, and need to work. + drop(y); +} + +#[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::new(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(); + assert_eq!((&&&s).as_str(), "foo"); + + let r: Rc = Rc::from(s); + assert_eq!((&r).as_str(), "foo"); + assert_eq!(r.as_str(), "foo"); + + 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::new(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::new(()); + 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); +} + +#[test] +fn test_unique_rc_weak() { + let rc = UniqueRc::new(42); + let weak = UniqueRc::downgrade(&rc); + assert!(weak.upgrade().is_none()); + + let _rc = UniqueRc::into_rc(rc); + assert_eq!(*weak.upgrade().unwrap(), 42); +} + +#[test] +fn test_unique_rc_drop_weak() { + let rc = UniqueRc::new(42); + let weak = UniqueRc::downgrade(&rc); + mem::drop(weak); + + let rc = UniqueRc::into_rc(rc); + assert_eq!(*rc, 42); +} + +#[test] +fn test_unique_rc_drops_contents() { + let mut dropped = false; + struct DropMe<'a>(&'a mut bool); + impl Drop for DropMe<'_> { + fn drop(&mut self) { + *self.0 = true; + } + } + { + let rc = UniqueRc::new(DropMe(&mut dropped)); + drop(rc); + } + assert!(dropped); +} + +/// Exercise the non-default allocator usage. +#[test] +fn test_unique_rc_with_alloc_drops_contents() { + let mut dropped = false; + struct DropMe<'a>(&'a mut bool); + impl Drop for DropMe<'_> { + fn drop(&mut self) { + *self.0 = true; + } + } + { + let rc = UniqueRc::new_in(DropMe(&mut dropped), std::alloc::System); + drop(rc); + } + assert!(dropped); +} + +#[test] +fn test_unique_rc_weak_clone_holding_ref() { + let mut v = UniqueRc::new(0u8); + let w = UniqueRc::downgrade(&v); + let r = &mut *v; + let _ = w.clone(); // touch weak count + *r = 123; +} + +#[test] +fn test_unique_rc_unsizing_coercion() { + let mut rc: UniqueRc<[u8]> = UniqueRc::new([0u8; 3]); + assert_eq!(rc.len(), 3); + rc[0] = 123; + let rc: Rc<[u8]> = UniqueRc::into_rc(rc); + assert_eq!(*rc, [123, 0, 0]); +} diff --git a/library/alloctests/tests/slice.rs b/library/alloctests/tests/slice.rs new file mode 100644 index 0000000000000..2516563187f2d --- /dev/null +++ b/library/alloctests/tests/slice.rs @@ -0,0 +1,1668 @@ +use std::cmp::Ordering::{Equal, Greater, Less}; +use std::convert::identity; +use std::rc::Rc; +use std::{fmt, panic}; + +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!(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::new(0)); + v.push(Box::new(0)); + v.push(Box::new(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::new(6), Box::new(5), Box::new(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::new(6), Box::new(5), Box::new(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] +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_split_iterator() { + 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_split_iterator_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_split_iterator_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_split_iterator_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_split_iterator_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_splitn_iterator() { + 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_splitn_iterator_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_rsplit_iterator() { + 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_rsplitn_iterator() { + 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_split_iterators_size_hint() { + #[derive(Copy, Clone)] + enum Bounds { + Lower, + Upper, + } + fn assert_tight_size_hints(mut it: impl Iterator, which: Bounds, ctx: impl fmt::Display) { + match which { + Bounds::Lower => { + let mut lower_bounds = vec![it.size_hint().0]; + while let Some(_) = it.next() { + lower_bounds.push(it.size_hint().0); + } + let target: Vec<_> = (0..lower_bounds.len()).rev().collect(); + assert_eq!(lower_bounds, target, "lower bounds incorrect or not tight: {}", ctx); + } + Bounds::Upper => { + let mut upper_bounds = vec![it.size_hint().1]; + while let Some(_) = it.next() { + upper_bounds.push(it.size_hint().1); + } + let target: Vec<_> = (0..upper_bounds.len()).map(Some).rev().collect(); + assert_eq!(upper_bounds, target, "upper bounds incorrect or not tight: {}", ctx); + } + } + } + + for len in 0..=2 { + let mut v: Vec = (0..len).collect(); + + // p: predicate, b: bound selection + for (p, b) in [ + // with a predicate always returning false, the split*-iterators + // become maximally short, so the size_hint lower bounds are tight + ((|_| false) as fn(&_) -> _, Bounds::Lower), + // with a predicate always returning true, the split*-iterators + // become maximally long, so the size_hint upper bounds are tight + ((|_| true) as fn(&_) -> _, Bounds::Upper), + ] { + use {assert_tight_size_hints as a, format_args as f}; + + a(v.split(p), b, "split"); + a(v.split_mut(p), b, "split_mut"); + a(v.split_inclusive(p), b, "split_inclusive"); + a(v.split_inclusive_mut(p), b, "split_inclusive_mut"); + a(v.rsplit(p), b, "rsplit"); + a(v.rsplit_mut(p), b, "rsplit_mut"); + + for n in 0..=3 { + a(v.splitn(n, p), b, f!("splitn, n = {n}")); + a(v.splitn_mut(n, p), b, f!("splitn_mut, n = {n}")); + a(v.rsplitn(n, p), b, f!("rsplitn, n = {n}")); + a(v.rsplitn_mut(n, p), b, f!("rsplitn_mut, n = {n}")); + } + } + } +} + +#[test] +fn test_windows_iterator() { + 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_windows_iterator_0() { + let v = &[1, 2, 3, 4]; + let _it = v.windows(0); +} + +#[test] +fn test_chunks_iterator() { + 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_chunks_iterator_0() { + let v = &[1, 2, 3, 4]; + let _it = v.chunks(0); +} + +#[test] +fn test_chunks_exact_iterator() { + 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_exact_iterator_0() { + let v = &[1, 2, 3, 4]; + let _it = v.chunks_exact(0); +} + +#[test] +fn test_rchunks_iterator() { + 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_rchunks_iterator_0() { + let v = &[1, 2, 3, 4]; + let _it = v.rchunks(0); +} + +#[test] +fn test_rchunks_exact_iterator() { + 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_exact_iterator_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_split_iterator() { + 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_split_iterator_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::new([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::new([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(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_box_slice_clone_panics() { + use std::sync::Arc; + use std::sync::atomic::{AtomicUsize, Ordering}; + + 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); +} + +#[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]); +} + +#[test] +#[allow(unreachable_patterns)] +fn subslice_patterns() { + // This test comprehensively checks the passing static and dynamic semantics + // of subslice patterns `..`, `x @ ..`, `ref x @ ..`, and `ref mut @ ..` + // in slice patterns `[$($pat), $(,)?]` . + + #[derive(PartialEq, Debug, Clone)] + struct N(u8); + + macro_rules! n { + ($($e:expr),* $(,)?) => { + [$(N($e)),*] + } + } + + macro_rules! c { + ($inp:expr, $typ:ty, $out:expr $(,)?) => { + assert_eq!($out, identity::<$typ>($inp)) + }; + } + + macro_rules! m { + ($e:expr, $p:pat => $b:expr) => { + match $e { + $p => $b, + _ => panic!(), + } + }; + } + + // == Slices == + + // Matching slices using `ref` patterns: + let mut v = vec![N(0), N(1), N(2), N(3), N(4)]; + let mut vc = (0..=4).collect::>(); + + let [..] = v[..]; // Always matches. + m!(v[..], [N(0), ref sub @ .., N(4)] => c!(sub, &[N], n![1, 2, 3])); + m!(v[..], [N(0), ref sub @ ..] => c!(sub, &[N], n![1, 2, 3, 4])); + m!(v[..], [ref sub @ .., N(4)] => c!(sub, &[N], n![0, 1, 2, 3])); + m!(v[..], [ref sub @ .., _, _, _, _, _] => c!(sub, &[N], &n![] as &[N])); + m!(v[..], [_, _, _, _, _, ref sub @ ..] => c!(sub, &[N], &n![] as &[N])); + m!(vc[..], [x, .., y] => c!((x, y), (u8, u8), (0, 4))); + + // Matching slices using `ref mut` patterns: + let [..] = v[..]; // Always matches. + m!(v[..], [N(0), ref mut sub @ .., N(4)] => c!(sub, &mut [N], n![1, 2, 3])); + m!(v[..], [N(0), ref mut sub @ ..] => c!(sub, &mut [N], n![1, 2, 3, 4])); + m!(v[..], [ref mut sub @ .., N(4)] => c!(sub, &mut [N], n![0, 1, 2, 3])); + m!(v[..], [ref mut sub @ .., _, _, _, _, _] => c!(sub, &mut [N], &mut n![] as &mut [N])); + m!(v[..], [_, _, _, _, _, ref mut sub @ ..] => c!(sub, &mut [N], &mut n![] as &mut [N])); + m!(vc[..], [x, .., y] => c!((x, y), (u8, u8), (0, 4))); + + // Matching slices using default binding modes (&): + let [..] = &v[..]; // Always matches. + m!(&v[..], [N(0), sub @ .., N(4)] => c!(sub, &[N], n![1, 2, 3])); + m!(&v[..], [N(0), sub @ ..] => c!(sub, &[N], n![1, 2, 3, 4])); + m!(&v[..], [sub @ .., N(4)] => c!(sub, &[N], n![0, 1, 2, 3])); + m!(&v[..], [sub @ .., _, _, _, _, _] => c!(sub, &[N], &n![] as &[N])); + m!(&v[..], [_, _, _, _, _, sub @ ..] => c!(sub, &[N], &n![] as &[N])); + m!(&vc[..], [x, .., y] => c!((x, y), (&u8, &u8), (&0, &4))); + + // Matching slices using default binding modes (&mut): + let [..] = &mut v[..]; // Always matches. + m!(&mut v[..], [N(0), sub @ .., N(4)] => c!(sub, &mut [N], n![1, 2, 3])); + m!(&mut v[..], [N(0), sub @ ..] => c!(sub, &mut [N], n![1, 2, 3, 4])); + m!(&mut v[..], [sub @ .., N(4)] => c!(sub, &mut [N], n![0, 1, 2, 3])); + m!(&mut v[..], [sub @ .., _, _, _, _, _] => c!(sub, &mut [N], &mut n![] as &mut [N])); + m!(&mut v[..], [_, _, _, _, _, sub @ ..] => c!(sub, &mut [N], &mut n![] as &mut [N])); + m!(&mut vc[..], [x, .., y] => c!((x, y), (&mut u8, &mut u8), (&mut 0, &mut 4))); + + // == Arrays == + let mut v = n![0, 1, 2, 3, 4]; + let vc = [0, 1, 2, 3, 4]; + + // Matching arrays by value: + m!(v.clone(), [N(0), sub @ .., N(4)] => c!(sub, [N; 3], n![1, 2, 3])); + m!(v.clone(), [N(0), sub @ ..] => c!(sub, [N; 4], n![1, 2, 3, 4])); + m!(v.clone(), [sub @ .., N(4)] => c!(sub, [N; 4], n![0, 1, 2, 3])); + m!(v.clone(), [sub @ .., _, _, _, _, _] => c!(sub, [N; 0], n![] as [N; 0])); + m!(v.clone(), [_, _, _, _, _, sub @ ..] => c!(sub, [N; 0], n![] as [N; 0])); + m!(v.clone(), [x, .., y] => c!((x, y), (N, N), (N(0), N(4)))); + m!(v.clone(), [..] => ()); + + // Matching arrays by ref patterns: + m!(v, [N(0), ref sub @ .., N(4)] => c!(sub, &[N; 3], &n![1, 2, 3])); + m!(v, [N(0), ref sub @ ..] => c!(sub, &[N; 4], &n![1, 2, 3, 4])); + m!(v, [ref sub @ .., N(4)] => c!(sub, &[N; 4], &n![0, 1, 2, 3])); + m!(v, [ref sub @ .., _, _, _, _, _] => c!(sub, &[N; 0], &n![] as &[N; 0])); + m!(v, [_, _, _, _, _, ref sub @ ..] => c!(sub, &[N; 0], &n![] as &[N; 0])); + m!(vc, [x, .., y] => c!((x, y), (u8, u8), (0, 4))); + + // Matching arrays by ref mut patterns: + m!(v, [N(0), ref mut sub @ .., N(4)] => c!(sub, &mut [N; 3], &mut n![1, 2, 3])); + m!(v, [N(0), ref mut sub @ ..] => c!(sub, &mut [N; 4], &mut n![1, 2, 3, 4])); + m!(v, [ref mut sub @ .., N(4)] => c!(sub, &mut [N; 4], &mut n![0, 1, 2, 3])); + m!(v, [ref mut sub @ .., _, _, _, _, _] => c!(sub, &mut [N; 0], &mut n![] as &mut [N; 0])); + m!(v, [_, _, _, _, _, ref mut sub @ ..] => c!(sub, &mut [N; 0], &mut n![] as &mut [N; 0])); + + // Matching arrays by default binding modes (&): + m!(&v, [N(0), sub @ .., N(4)] => c!(sub, &[N; 3], &n![1, 2, 3])); + m!(&v, [N(0), sub @ ..] => c!(sub, &[N; 4], &n![1, 2, 3, 4])); + m!(&v, [sub @ .., N(4)] => c!(sub, &[N; 4], &n![0, 1, 2, 3])); + m!(&v, [sub @ .., _, _, _, _, _] => c!(sub, &[N; 0], &n![] as &[N; 0])); + m!(&v, [_, _, _, _, _, sub @ ..] => c!(sub, &[N; 0], &n![] as &[N; 0])); + m!(&v, [..] => ()); + m!(&v, [x, .., y] => c!((x, y), (&N, &N), (&N(0), &N(4)))); + + // Matching arrays by default binding modes (&mut): + m!(&mut v, [N(0), sub @ .., N(4)] => c!(sub, &mut [N; 3], &mut n![1, 2, 3])); + m!(&mut v, [N(0), sub @ ..] => c!(sub, &mut [N; 4], &mut n![1, 2, 3, 4])); + m!(&mut v, [sub @ .., N(4)] => c!(sub, &mut [N; 4], &mut n![0, 1, 2, 3])); + m!(&mut v, [sub @ .., _, _, _, _, _] => c!(sub, &mut [N; 0], &mut n![] as &[N; 0])); + m!(&mut v, [_, _, _, _, _, sub @ ..] => c!(sub, &mut [N; 0], &mut n![] as &[N; 0])); + m!(&mut v, [..] => ()); + m!(&mut v, [x, .., y] => c!((x, y), (&mut N, &mut N), (&mut N(0), &mut N(4)))); +} + +#[test] +fn test_chunk_by() { + let slice = &[1, 1, 1, 3, 3, 2, 2, 2, 1, 0]; + + let mut iter = slice.chunk_by(|a, b| a == b); + assert_eq!(iter.next(), Some(&[1, 1, 1][..])); + assert_eq!(iter.next(), Some(&[3, 3][..])); + assert_eq!(iter.next(), Some(&[2, 2, 2][..])); + assert_eq!(iter.next(), Some(&[1][..])); + assert_eq!(iter.next(), Some(&[0][..])); + assert_eq!(iter.next(), None); + + let mut iter = slice.chunk_by(|a, b| a == b); + assert_eq!(iter.next_back(), Some(&[0][..])); + assert_eq!(iter.next_back(), Some(&[1][..])); + assert_eq!(iter.next_back(), Some(&[2, 2, 2][..])); + assert_eq!(iter.next_back(), Some(&[3, 3][..])); + assert_eq!(iter.next_back(), Some(&[1, 1, 1][..])); + assert_eq!(iter.next_back(), None); + + let mut iter = slice.chunk_by(|a, b| a == b); + assert_eq!(iter.next(), Some(&[1, 1, 1][..])); + assert_eq!(iter.next_back(), Some(&[0][..])); + assert_eq!(iter.next(), Some(&[3, 3][..])); + assert_eq!(iter.next_back(), Some(&[1][..])); + assert_eq!(iter.next(), Some(&[2, 2, 2][..])); + assert_eq!(iter.next_back(), None); +} + +#[test] +fn test_chunk_by_mut() { + let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2, 1, 0]; + + let mut iter = slice.chunk_by_mut(|a, b| a == b); + assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); + assert_eq!(iter.next(), Some(&mut [3, 3][..])); + assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); + assert_eq!(iter.next(), Some(&mut [1][..])); + assert_eq!(iter.next(), Some(&mut [0][..])); + assert_eq!(iter.next(), None); + + let mut iter = slice.chunk_by_mut(|a, b| a == b); + assert_eq!(iter.next_back(), Some(&mut [0][..])); + assert_eq!(iter.next_back(), Some(&mut [1][..])); + assert_eq!(iter.next_back(), Some(&mut [2, 2, 2][..])); + assert_eq!(iter.next_back(), Some(&mut [3, 3][..])); + assert_eq!(iter.next_back(), Some(&mut [1, 1, 1][..])); + assert_eq!(iter.next_back(), None); + + let mut iter = slice.chunk_by_mut(|a, b| a == b); + assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); + assert_eq!(iter.next_back(), Some(&mut [0][..])); + assert_eq!(iter.next(), Some(&mut [3, 3][..])); + assert_eq!(iter.next_back(), Some(&mut [1][..])); + assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); + assert_eq!(iter.next_back(), None); +} diff --git a/library/alloc/tests/sort/ffi_types.rs b/library/alloctests/tests/sort/ffi_types.rs similarity index 100% rename from library/alloc/tests/sort/ffi_types.rs rename to library/alloctests/tests/sort/ffi_types.rs diff --git a/library/alloc/tests/sort/known_good_stable_sort.rs b/library/alloctests/tests/sort/known_good_stable_sort.rs similarity index 98% rename from library/alloc/tests/sort/known_good_stable_sort.rs rename to library/alloctests/tests/sort/known_good_stable_sort.rs index f8615435fc2a7..2df891462538d 100644 --- a/library/alloc/tests/sort/known_good_stable_sort.rs +++ b/library/alloctests/tests/sort/known_good_stable_sort.rs @@ -5,7 +5,7 @@ // Based on https://github.com/voultapher/tiny-sort-rs. use alloc::alloc::{Layout, alloc, dealloc}; -use std::{mem, ptr}; +use std::ptr; /// Sort `v` preserving initial order of equal elements. /// @@ -26,7 +26,7 @@ pub fn sort(v: &mut [T]) { #[inline(always)] fn stable_sort bool>(v: &mut [T], mut is_less: F) { - if mem::size_of::() == 0 { + if size_of::() == 0 { return; } @@ -166,7 +166,7 @@ struct BufGuard { impl BufGuard { // SAFETY: The caller has to ensure that len is not 0 and that T is not a ZST. unsafe fn new(len: usize) -> Self { - debug_assert!(len > 0 && mem::size_of::() > 0); + debug_assert!(len > 0 && size_of::() > 0); // SAFETY: See function safety description. let layout = unsafe { unwrap_unchecked(Layout::array::(len).ok()) }; diff --git a/library/alloc/tests/sort/mod.rs b/library/alloctests/tests/sort/mod.rs similarity index 100% rename from library/alloc/tests/sort/mod.rs rename to library/alloctests/tests/sort/mod.rs diff --git a/library/alloc/tests/sort/patterns.rs b/library/alloctests/tests/sort/patterns.rs similarity index 100% rename from library/alloc/tests/sort/patterns.rs rename to library/alloctests/tests/sort/patterns.rs diff --git a/library/alloc/tests/sort/tests.rs b/library/alloctests/tests/sort/tests.rs similarity index 100% rename from library/alloc/tests/sort/tests.rs rename to library/alloctests/tests/sort/tests.rs diff --git a/library/alloc/tests/sort/zipf.rs b/library/alloctests/tests/sort/zipf.rs similarity index 100% rename from library/alloc/tests/sort/zipf.rs rename to library/alloctests/tests/sort/zipf.rs diff --git a/library/alloc/tests/str.rs b/library/alloctests/tests/str.rs similarity index 100% rename from library/alloc/tests/str.rs rename to library/alloctests/tests/str.rs diff --git a/library/alloc/tests/string.rs b/library/alloctests/tests/string.rs similarity index 100% rename from library/alloc/tests/string.rs rename to library/alloctests/tests/string.rs diff --git a/library/alloc/tests/sync.rs b/library/alloctests/tests/sync.rs similarity index 100% rename from library/alloc/tests/sync.rs rename to library/alloctests/tests/sync.rs diff --git a/library/alloc/tests/task.rs b/library/alloctests/tests/task.rs similarity index 100% rename from library/alloc/tests/task.rs rename to library/alloctests/tests/task.rs diff --git a/library/alloc/tests/testing/crash_test.rs b/library/alloctests/tests/testing/crash_test.rs similarity index 100% rename from library/alloc/tests/testing/crash_test.rs rename to library/alloctests/tests/testing/crash_test.rs diff --git a/library/alloc/tests/testing/mod.rs b/library/alloctests/tests/testing/mod.rs similarity index 100% rename from library/alloc/tests/testing/mod.rs rename to library/alloctests/tests/testing/mod.rs diff --git a/library/alloc/tests/thin_box.rs b/library/alloctests/tests/thin_box.rs similarity index 99% rename from library/alloc/tests/thin_box.rs rename to library/alloctests/tests/thin_box.rs index e008b0cc35718..4c46b61412796 100644 --- a/library/alloc/tests/thin_box.rs +++ b/library/alloctests/tests/thin_box.rs @@ -1,5 +1,4 @@ use core::fmt::Debug; -use core::mem::size_of; use std::boxed::ThinBox; #[test] @@ -52,7 +51,7 @@ fn verify_aligned(ptr: *const T) { ptr.is_aligned() && !ptr.is_null(), "misaligned ThinBox data; valid pointers to `{ty}` should be aligned to {align}: {ptr:p}", ty = core::any::type_name::(), - align = core::mem::align_of::(), + align = align_of::(), ); } diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs new file mode 100644 index 0000000000000..f430d979fa848 --- /dev/null +++ b/library/alloctests/tests/vec.rs @@ -0,0 +1,2750 @@ +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + +use core::alloc::{Allocator, Layout}; +use core::num::NonZero; +use core::ptr::NonNull; +use core::{assert_eq, assert_ne}; +use std::alloc::System; +use std::assert_matches::assert_matches; +use std::borrow::Cow; +use std::cell::Cell; +use std::collections::TryReserveErrorKind::*; +use std::fmt::Debug; +use std::hint; +use std::iter::InPlaceIterable; +use std::mem::swap; +use std::ops::Bound::*; +use std::panic::{AssertUnwindSafe, catch_unwind}; +use std::rc::Rc; +use std::sync::atomic::{AtomicU32, Ordering}; +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::new(1), Box::new(2), Box::new(3)]; + let two: Vec> = vec![Box::new(4), Box::new(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_retain_predicate_order() { + for to_keep in [true, false] { + let mut number_of_executions = 0; + let mut vec = vec![1, 2, 3, 4]; + let mut next_expected = 1; + vec.retain(|&x| { + assert_eq!(next_expected, x); + next_expected += 1; + number_of_executions += 1; + to_keep + }); + assert_eq!(number_of_executions, 4); + } +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_retain_pred_panic_with_hole() { + let v = (0..5).map(Rc::new).collect::>(); + catch_unwind(AssertUnwindSafe(|| { + let mut v = v.clone(); + v.retain(|r| match **r { + 0 => true, + 1 => false, + 2 => true, + _ => panic!(), + }); + })) + .unwrap_err(); + // Everything is dropped when predicate panicked. + assert!(v.iter().all(|r| Rc::strong_count(r) == 1)); +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_retain_pred_panic_no_hole() { + let v = (0..5).map(Rc::new).collect::>(); + catch_unwind(AssertUnwindSafe(|| { + let mut v = v.clone(); + v.retain(|r| match **r { + 0 | 1 | 2 => true, + _ => panic!(), + }); + })) + .unwrap_err(); + // Everything is dropped when predicate panicked. + assert!(v.iter().all(|r| Rc::strong_count(r) == 1)); +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_retain_drop_panic() { + struct Wrap(Rc); + + impl Drop for Wrap { + fn drop(&mut self) { + if *self.0 == 3 { + panic!(); + } + } + } + + let v = (0..5).map(|x| Rc::new(x)).collect::>(); + catch_unwind(AssertUnwindSafe(|| { + let mut v = v.iter().map(|r| Wrap(r.clone())).collect::>(); + v.retain(|w| match *w.0 { + 0 => true, + 1 => false, + 2 => true, + 3 => false, // Drop panic. + _ => true, + }); + })) + .unwrap_err(); + // Other elements are dropped when `drop` of one element panicked. + // The panicked wrapper also has its Rc dropped. + assert!(v.iter().all(|r| Rc::strong_count(r) == 1)); +} + +#[test] +fn test_retain_maybeuninits() { + // This test aimed to be run under miri. + use core::mem::MaybeUninit; + let mut vec: Vec<_> = [1i32, 2, 3, 4].map(|v| MaybeUninit::new(vec![v])).into(); + vec.retain(|x| { + // SAFETY: Retain must visit every element of Vec in original order and exactly once. + // Our values is initialized at creation of Vec. + let v = unsafe { x.assume_init_ref()[0] }; + if v & 1 == 0 { + return true; + } + // SAFETY: Value is initialized. + // Value wouldn't be dropped by `Vec::retain` + // because `MaybeUninit` doesn't drop content. + drop(unsafe { x.assume_init_read() }); + false + }); + let vec: Vec = vec + .into_iter() + .map(|x| unsafe { + // SAFETY: All values dropped in retain predicate must be removed by `Vec::retain`. + // Remaining values are initialized. + x.assume_init()[0] + }) + .collect(); + 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::new(1), Box::new(1), Box::new(2), Box::new(3)]; + v0.dedup(); + let mut v1: Vec> = vec![Box::new(1), Box::new(2), Box::new(2), Box::new(3)]; + v1.dedup(); + let mut v2: Vec> = vec![Box::new(1), Box::new(2), Box::new(3), Box::new(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!([].into_iter().partition(|x: &i32| *x < 3), (vec![], vec![])); + assert_eq!([1, 2, 3].into_iter().partition(|x| *x < 4), (vec![1, 2, 3], vec![])); + assert_eq!([1, 2, 3].into_iter().partition(|x| *x < 2), (vec![1], vec![2, 3])); + assert_eq!([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(#[allow(dead_code)] 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]; + let _ = &x[!0..]; +} + +#[test] +#[should_panic] +fn test_slice_out_of_bounds_2() { + let x = vec![1, 2, 3, 4, 5]; + let _ = &x[..6]; +} + +#[test] +#[should_panic] +fn test_slice_out_of_bounds_3() { + let x = vec![1, 2, 3, 4, 5]; + let _ = &x[!0..4]; +} + +#[test] +#[should_panic] +fn test_slice_out_of_bounds_4() { + let x = vec![1, 2, 3, 4, 5]; + let _ = &x[1..6]; +} + +#[test] +#[should_panic] +fn test_slice_out_of_bounds_5() { + let x = vec![1, 2, 3, 4, 5]; + let _ = &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_empty_vec() { + let mut vec: Vec = vec![]; + let mut vec2: Vec = vec![]; + for i in vec.drain(..) { + vec2.push(i); + } + assert!(vec.is_empty()); + assert!(vec2.is_empty()); +} + +#[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_index_overflow() { + let mut v = Vec::<()>::with_capacity(usize::MAX); + unsafe { + v.set_len(usize::MAX); + } + v.drain(0..=usize::MAX); +} + +#[test] +#[should_panic] +fn test_drain_inclusive_out_of_bounds() { + let mut v = vec![1, 2, 3, 4, 5]; + v.drain(5..=5); +} + +#[test] +#[should_panic] +fn test_drain_start_overflow() { + let mut v = vec![1, 2, 3]; + v.drain((Excluded(usize::MAX), Included(0))); +} + +#[test] +#[should_panic] +fn test_drain_end_overflow() { + let mut v = vec![1, 2, 3]; + v.drain((Included(0), Included(usize::MAX))); +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +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_drain_keep_rest() { + let mut v = vec![0, 1, 2, 3, 4, 5, 6]; + let mut drain = v.drain(1..6); + assert_eq!(drain.next(), Some(1)); + assert_eq!(drain.next_back(), Some(5)); + assert_eq!(drain.next(), Some(2)); + + drain.keep_rest(); + assert_eq!(v, &[0, 3, 4, 6]); +} + +#[test] +fn test_drain_keep_rest_all() { + let mut v = vec![0, 1, 2, 3, 4, 5, 6]; + v.drain(1..6).keep_rest(); + assert_eq!(v, &[0, 1, 2, 3, 4, 5, 6]); +} + +#[test] +fn test_drain_keep_rest_none() { + let mut v = vec![0, 1, 2, 3, 4, 5, 6]; + let mut drain = v.drain(1..6); + + drain.by_ref().for_each(drop); + + drain.keep_rest(); + assert_eq!(v, &[0, 6]); +} + +#[test] +fn test_splice() { + let mut v = vec![1, 2, 3, 4, 5]; + let a = [10, 11, 12]; + v.splice(2..4, a); + 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).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); +} + +#[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); +} + +#[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)); + 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 orig_ptr = vec.as_ptr(); + let orig_capacity = vec.capacity(); + + let split_off = vec.split_off(4); + assert_eq!(vec, [1, 2, 3, 4]); + assert_eq!(split_off, [5, 6]); + assert_eq!(vec.capacity(), orig_capacity); + assert_eq!(vec.as_ptr(), orig_ptr); +} + +#[test] +fn test_split_off_take_all() { + // Allocate enough capacity that we can tell whether the split-off vector's + // capacity is based on its size, or (incorrectly) on the original capacity. + let mut vec = Vec::with_capacity(1000); + vec.extend([1, 2, 3, 4, 5, 6]); + let orig_ptr = vec.as_ptr(); + let orig_capacity = vec.capacity(); + + let split_off = vec.split_off(0); + assert_eq!(vec, []); + assert_eq!(split_off, [1, 2, 3, 4, 5, 6]); + assert_eq!(vec.capacity(), orig_capacity); + assert_eq!(vec.as_ptr(), orig_ptr); + + // The split-off vector should be newly-allocated, and should not have + // stolen the original vector's allocation. + assert!(split_off.capacity() < orig_capacity); + assert_ne!(split_off.as_ptr(), orig_ptr); +} + +#[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!([1, 2, 3].into_iter().count(), 3); +} + +#[test] +fn test_into_iter_next_chunk() { + let mut iter = b"lorem".to_vec().into_iter(); + + assert_eq!(iter.next_chunk().unwrap(), [b'l', b'o']); // N is inferred as 2 + assert_eq!(iter.next_chunk().unwrap(), [b'r', b'e', b'm']); // N is inferred as 3 + assert_eq!(iter.next_chunk::<4>().unwrap_err().as_slice(), &[]); // N is explicitly 4 +} + +#[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 = [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] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +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_into_iter_advance_by() { + let mut i = vec![1, 2, 3, 4, 5].into_iter(); + assert_eq!(i.advance_by(0), Ok(())); + assert_eq!(i.advance_back_by(0), Ok(())); + assert_eq!(i.as_slice(), [1, 2, 3, 4, 5]); + + assert_eq!(i.advance_by(1), Ok(())); + assert_eq!(i.advance_back_by(1), Ok(())); + assert_eq!(i.as_slice(), [2, 3, 4]); + + assert_eq!(i.advance_back_by(usize::MAX), Err(NonZero::new(usize::MAX - 3).unwrap())); + + assert_eq!(i.advance_by(usize::MAX), Err(NonZero::new(usize::MAX).unwrap())); + + assert_eq!(i.advance_by(0), Ok(())); + assert_eq!(i.advance_back_by(0), Ok(())); + + assert_eq!(i.len(), 0); +} + +#[test] +fn test_into_iter_drop_allocator() { + struct ReferenceCountedAllocator<'a>(#[allow(dead_code)] DropCounter<'a>); + + unsafe impl Allocator for ReferenceCountedAllocator<'_> { + fn allocate(&self, layout: Layout) -> Result, core::alloc::AllocError> { + System.allocate(layout) + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + // Safety: Invariants passed to caller. + unsafe { System.deallocate(ptr, layout) } + } + } + + let mut drop_count = 0; + + let allocator = ReferenceCountedAllocator(DropCounter { count: &mut drop_count }); + let _ = Vec::::new_in(allocator); + assert_eq!(drop_count, 1); + + let allocator = ReferenceCountedAllocator(DropCounter { count: &mut drop_count }); + let _ = Vec::::new_in(allocator).into_iter(); + assert_eq!(drop_count, 2); +} + +#[test] +fn test_into_iter_zst() { + #[derive(Debug, Clone)] + struct AlignedZstWithDrop([u64; 0]); + impl Drop for AlignedZstWithDrop { + fn drop(&mut self) { + let addr = self as *mut _ as usize; + assert!(hint::black_box(addr) % align_of::() == 0); + } + } + + const C: AlignedZstWithDrop = AlignedZstWithDrop([0u64; 0]); + + for _ in vec![C].into_iter() {} + for _ in vec![C; 5].into_iter().rev() {} + + let mut it = vec![C, C].into_iter(); + assert_eq!(it.advance_by(1), Ok(())); + drop(it); + + let mut it = vec![C, C].into_iter(); + it.next_chunk::<1>().unwrap(); + drop(it); + + let mut it = vec![C, C].into_iter(); + it.next_chunk::<4>().unwrap_err(); + drop(it); +} + +#[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 owned: Vec = vec![0usize; 256]; + let refd: Vec<&usize> = owned.iter().collect(); + let src: Vec<&&usize> = refd.iter().collect(); + let srcptr = src.as_ptr(); + let iter = src + .into_iter() + .copied() + .cloned() + .enumerate() + .map(|i| i.0 + i.1) + .zip(std::iter::repeat(1usize)) + .map(|(a, b)| a + b) + .map_while(Option::Some) + .skip(1) + .map(|e| if e != usize::MAX { Ok(NonZero::new(e)) } else { Err(()) }); + assert_in_place_trait(&iter); + let sink = iter.collect::, _>>().unwrap(); + let sinkptr = sink.as_ptr(); + assert_eq!(srcptr as *const usize, sinkptr as *const usize); +} + +#[test] +fn test_in_place_specialization_step_up_down() { + fn assert_in_place_trait(_: &T) {} + + let src = vec![0u8; 1024]; + let srcptr = src.as_ptr(); + let src_bytes = src.capacity(); + let iter = src.into_iter().array_chunks::<4>(); + assert_in_place_trait(&iter); + let sink = iter.collect::>(); + let sinkptr = sink.as_ptr(); + assert_eq!(srcptr.addr(), sinkptr.addr()); + assert_eq!(src_bytes, sink.capacity() * 4); + + let mut src: Vec = Vec::with_capacity(17); + let src_bytes = src.capacity(); + src.resize(8, 0u8); + let sink: Vec<[u8; 4]> = src.into_iter().array_chunks::<4>().collect(); + let sink_bytes = sink.capacity() * 4; + assert_ne!(src_bytes, sink_bytes); + assert_eq!(sink.len(), 2); + + let mut src: Vec<[u8; 3]> = Vec::with_capacity(17); + src.resize(8, [0; 3]); + let iter = src.into_iter().map(|[a, b, _]| [a, b]); + assert_in_place_trait(&iter); + let sink: Vec<[u8; 2]> = iter.collect(); + assert_eq!(sink.len(), 8); + assert!(sink.capacity() <= 25); +} + +#[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] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_from_iter_specialization_panic_during_iteration_drops() { + 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] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#[allow(static_mut_refs)] +fn test_from_iter_specialization_panic_during_drop_doesnt_leak() { + static mut DROP_COUNTER_OLD: [usize; 5] = [0; 5]; + static mut DROP_COUNTER_NEW: [usize; 2] = [0; 2]; + + #[derive(Debug)] + struct Old(usize); + + impl Drop for Old { + fn drop(&mut self) { + unsafe { + DROP_COUNTER_OLD[self.0] += 1; + } + + if self.0 == 3 { + panic!(); + } + + println!("Dropped Old: {}", self.0); + } + } + + #[derive(Debug)] + struct New(usize); + + impl Drop for New { + fn drop(&mut self) { + unsafe { + DROP_COUNTER_NEW[self.0] += 1; + } + + println!("Dropped New: {}", self.0); + } + } + + let _ = std::panic::catch_unwind(AssertUnwindSafe(|| { + let v = vec![Old(0), Old(1), Old(2), Old(3), Old(4)]; + let _ = v.into_iter().map(|x| New(x.0)).take(2).collect::>(); + })); + + assert_eq!(unsafe { DROP_COUNTER_OLD[0] }, 1); + assert_eq!(unsafe { DROP_COUNTER_OLD[1] }, 1); + assert_eq!(unsafe { DROP_COUNTER_OLD[2] }, 1); + assert_eq!(unsafe { DROP_COUNTER_OLD[3] }, 1); + assert_eq!(unsafe { DROP_COUNTER_OLD[4] }, 1); + + assert_eq!(unsafe { DROP_COUNTER_NEW[0] }, 1); + assert_eq!(unsafe { DROP_COUNTER_NEW[1] }, 1); +} + +// regression test for issue #85322. Peekable previously implemented InPlaceIterable, +// but due to an interaction with IntoIter's current Clone implementation it failed to uphold +// the contract. +#[test] +fn test_collect_after_iterator_clone() { + let v = vec![0; 5]; + let mut i = v.into_iter().map(|i| i + 1).peekable(); + i.peek(); + let v = i.clone().collect::>(); + assert_eq!(v, [1, 1, 1, 1, 1]); + assert!(v.len() <= v.capacity()); +} + +// regression test for #135103, similar to the one above Flatten/FlatMap had an unsound InPlaceIterable +// implementation. +#[test] +fn test_flatten_clone() { + const S: String = String::new(); + + let v = vec![[S, "Hello World!".into()], [S, S]]; + let mut i = v.into_iter().flatten(); + let _ = i.next(); + let result: Vec = i.clone().collect(); + assert_eq!(result, ["Hello World!", "", ""]); +} + +#[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 extract_if_empty() { + let mut vec: Vec = vec![]; + + { + let mut iter = vec.extract_if(.., |_| 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 extract_if_zst() { + let mut vec = vec![(), (), (), (), ()]; + let initial_len = vec.len(); + let mut count = 0; + { + let mut iter = vec.extract_if(.., |_| 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 extract_if_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.extract_if(.., |_| 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 extract_if_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.extract_if(.., |_| 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 extract_if_ranges() { + let mut vec = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + let mut count = 0; + let it = vec.extract_if(1..=3, |_| { + count += 1; + true + }); + assert_eq!(it.collect::>(), vec![1, 2, 3]); + assert_eq!(vec, vec![0, 4, 5, 6, 7, 8, 9, 10]); + assert_eq!(count, 3); + + let it = vec.extract_if(1..=3, |_| false); + assert_eq!(it.collect::>(), vec![]); + assert_eq!(vec, vec![0, 4, 5, 6, 7, 8, 9, 10]); +} + +#[test] +#[should_panic] +fn extract_if_out_of_bounds() { + let mut vec = vec![0, 1]; + let _ = vec.extract_if(5.., |_| true).for_each(drop); +} + +#[test] +fn extract_if_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.extract_if(.., |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.extract_if(.., |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.extract_if(.., |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.extract_if(.., |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.extract_if(.., |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]); + } +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn extract_if_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.extract_if(.., filter); + + // NOTE: The ExtractIf 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); + } +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn extract_if_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.extract_if(.., filter); + + // NOTE: The ExtractIf 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 extract_if_unconsumed() { + let mut vec = vec![1, 2, 3, 4]; + let drain = vec.extract_if(.., |&mut x| x % 2 != 0); + drop(drain); + assert_eq!(vec, [1, 2, 3, 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 +fn test_try_with_capacity() { + let mut vec: Vec = Vec::try_with_capacity(5).unwrap(); + assert_eq!(0, vec.len()); + assert!(vec.capacity() >= 5 && vec.capacity() <= isize::MAX as usize / 4); + assert!(vec.spare_capacity_mut().len() >= 5); + + assert!(Vec::::try_with_capacity(isize::MAX as usize + 1).is_err()); +} + +#[test] +#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +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; + + { + // 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).map_err(|e| e.kind()) { + 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).map_err(|e| e.kind()) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + // Check isize::MAX + 1 does count as overflow + assert_matches!( + empty_bytes.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + + // Check usize::MAX does count as overflow + assert_matches!( + empty_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), + Err(CapacityOverflow), + "usize::MAX should trigger an overflow!" + ); + } + + { + // 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).map_err(|e| e.kind()) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + assert_matches!( + ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + + // Should always overflow in the add-to-len + assert_matches!( + ten_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), + Err(CapacityOverflow), + "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).map_err(|e| e.kind()) + { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP / 4 - 10).map_err(|e| e.kind()) + { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + assert_matches!( + ten_u32s.try_reserve(MAX_CAP / 4 - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + + // Should fail in the mul-by-size + assert_matches!( + ten_u32s.try_reserve(MAX_USIZE - 20).map_err(|e| e.kind()), + Err(CapacityOverflow), + "usize::MAX should trigger an overflow!" + ); + } +} + +#[test] +#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +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 mut empty_bytes: Vec = Vec::new(); + + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP).map_err(|e| e.kind()) + { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP).map_err(|e| e.kind()) + { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + assert_matches!( + empty_bytes.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + + assert_matches!( + empty_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), + Err(CapacityOverflow), + "usize::MAX should trigger an overflow!" + ); + } + + { + 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).map_err(|e| e.kind()) + { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = + ten_bytes.try_reserve_exact(MAX_CAP - 10).map_err(|e| e.kind()) + { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + assert_matches!( + ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + + assert_matches!( + ten_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()), + Err(CapacityOverflow), + "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).map_err(|e| e.kind()) + { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = + ten_u32s.try_reserve_exact(MAX_CAP / 4 - 10).map_err(|e| e.kind()) + { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + assert_matches!( + ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9).map_err(|e| e.kind()), + Err(CapacityOverflow), + "isize::MAX + 1 should trigger an overflow!" + ); + + assert_matches!( + ten_u32s.try_reserve_exact(MAX_USIZE - 20).map_err(|e| e.kind()), + Err(CapacityOverflow), + "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. + // Note that this test does *not* constitute a stable guarantee that all these functions do not + // reallocate! Only what is explicitly documented at + // is stably guaranteed. + 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, [1; 10].into_iter().filter(|_| true))); // lower bound not exact + assert_eq!(*v0, 13); + + // spare_capacity_mut + v.spare_capacity_mut(); + assert_eq!(*v0, 13); + + // Smoke test that would fire even outside Miri if an actual relocation happened. + // Also ensures the pointer is still writeable after all this. + *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!(std::ptr::eq(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:expr, $a3:expr; $b2:expr, $b3: expr) => { + 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); + assert_partial_eq_valid!(vec2,vec3; arrayref2[..],arrayref3[..]); +} + +#[test] +fn test_vec_cycle() { + #[derive(Debug)] + struct C<'a> { + v: Vec>>>, + } + + impl<'a> C<'a> { + fn new() -> C<'a> { + C { v: Vec::new() } + } + } + + let mut c1 = C::new(); + let mut c2 = C::new(); + let mut c3 = C::new(); + + // Push + c1.v.push(Cell::new(None)); + c1.v.push(Cell::new(None)); + + c2.v.push(Cell::new(None)); + c2.v.push(Cell::new(None)); + + c3.v.push(Cell::new(None)); + c3.v.push(Cell::new(None)); + + // Set + c1.v[0].set(Some(&c2)); + c1.v[1].set(Some(&c3)); + + c2.v[0].set(Some(&c2)); + c2.v[1].set(Some(&c3)); + + c3.v[0].set(Some(&c1)); + c3.v[1].set(Some(&c2)); +} + +#[test] +fn test_vec_cycle_wrapped() { + struct Refs<'a> { + v: Vec>>>, + } + + struct C<'a> { + refs: Refs<'a>, + } + + impl<'a> Refs<'a> { + fn new() -> Refs<'a> { + Refs { v: Vec::new() } + } + } + + impl<'a> C<'a> { + fn new() -> C<'a> { + C { refs: Refs::new() } + } + } + + let mut c1 = C::new(); + let mut c2 = C::new(); + let mut c3 = C::new(); + + c1.refs.v.push(Cell::new(None)); + c1.refs.v.push(Cell::new(None)); + c2.refs.v.push(Cell::new(None)); + c2.refs.v.push(Cell::new(None)); + c3.refs.v.push(Cell::new(None)); + c3.refs.v.push(Cell::new(None)); + + c1.refs.v[0].set(Some(&c2)); + c1.refs.v[1].set(Some(&c3)); + c2.refs.v[0].set(Some(&c2)); + c2.refs.v[1].set(Some(&c3)); + c3.refs.v[0].set(Some(&c1)); + c3.refs.v[1].set(Some(&c2)); +} + +#[test] +fn test_zero_sized_capacity() { + for len in [0, 1, 2, 4, 8, 16, 32, 64, 128, 256] { + let v = Vec::<()>::with_capacity(len); + assert_eq!(v.len(), 0); + assert_eq!(v.capacity(), usize::MAX); + } +} + +#[test] +fn test_zero_sized_vec_push() { + const N: usize = 8; + + for len in 0..N { + let mut tester = Vec::with_capacity(len); + assert_eq!(tester.len(), 0); + assert!(tester.capacity() >= len); + for _ in 0..len { + tester.push(()); + } + assert_eq!(tester.len(), len); + assert_eq!(tester.iter().count(), len); + tester.clear(); + } +} + +#[test] +fn test_vec_macro_repeat() { + assert_eq!(vec![1; 3], vec![1, 1, 1]); + assert_eq!(vec![1; 2], vec![1, 1]); + assert_eq!(vec![1; 1], vec![1]); + assert_eq!(vec![1; 0], vec![]); + + // from_elem syntax (see RFC 832) + let el = Box::new(1); + let n = 3; + assert_eq!(vec![el; n], vec![Box::new(1), Box::new(1), Box::new(1)]); +} + +#[test] +fn test_vec_swap() { + let mut a: Vec = vec![0, 1, 2, 3, 4, 5, 6]; + a.swap(2, 4); + assert_eq!(a[2], 4); + assert_eq!(a[4], 2); + let mut n = 42; + swap(&mut n, &mut a[0]); + assert_eq!(a[0], 42); + assert_eq!(n, 0); +} + +#[test] +fn test_extend_from_within_spec() { + #[derive(Copy)] + struct CopyOnly; + + impl Clone for CopyOnly { + fn clone(&self) -> Self { + panic!("extend_from_within must use specialization on copy"); + } + } + + vec![CopyOnly, CopyOnly].extend_from_within(..); +} + +#[test] +fn test_extend_from_within_clone() { + let mut v = vec![String::from("sssss"), String::from("12334567890"), String::from("c")]; + v.extend_from_within(1..); + + assert_eq!(v, ["sssss", "12334567890", "c", "12334567890", "c"]); +} + +#[test] +fn test_extend_from_within_complete_rande() { + let mut v = vec![0, 1, 2, 3]; + v.extend_from_within(..); + + assert_eq!(v, [0, 1, 2, 3, 0, 1, 2, 3]); +} + +#[test] +fn test_extend_from_within_empty_rande() { + let mut v = vec![0, 1, 2, 3]; + v.extend_from_within(1..1); + + assert_eq!(v, [0, 1, 2, 3]); +} + +#[test] +#[should_panic] +fn test_extend_from_within_out_of_rande() { + let mut v = vec![0, 1]; + v.extend_from_within(..3); +} + +#[test] +fn test_extend_from_within_zst() { + let mut v = vec![(); 8]; + v.extend_from_within(3..7); + + assert_eq!(v, [(); 12]); +} + +#[test] +fn test_extend_from_within_empty_vec() { + let mut v = Vec::::new(); + v.extend_from_within(..); + + assert_eq!(v, []); +} + +#[test] +fn test_extend_from_within() { + let mut v = vec![String::from("a"), String::from("b"), String::from("c")]; + v.extend_from_within(1..=2); + v.extend_from_within(..=1); + + assert_eq!(v, ["a", "b", "c", "b", "c", "a", "b"]); +} + +#[test] +fn test_vec_dedup_by() { + let mut vec: Vec = vec![1, -1, 2, 3, 1, -5, 5, -2, 2]; + + vec.dedup_by(|a, b| a.abs() == b.abs()); + + assert_eq!(vec, [1, 2, 3, 1, -5, -2]); +} + +#[test] +fn test_vec_dedup_empty() { + let mut vec: Vec = Vec::new(); + + vec.dedup(); + + assert_eq!(vec, []); +} + +#[test] +fn test_vec_dedup_one() { + let mut vec = vec![12i32]; + + vec.dedup(); + + assert_eq!(vec, [12]); +} + +#[test] +fn test_vec_dedup_multiple_ident() { + let mut vec = vec![12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11]; + + vec.dedup(); + + assert_eq!(vec, [12, 11]); +} + +#[test] +fn test_vec_dedup_partialeq() { + #[derive(Debug)] + struct Foo(i32, #[allow(dead_code)] i32); + + impl PartialEq for Foo { + fn eq(&self, other: &Foo) -> bool { + self.0 == other.0 + } + } + + let mut vec = vec![Foo(0, 1), Foo(0, 5), Foo(1, 7), Foo(1, 9)]; + + vec.dedup(); + assert_eq!(vec, [Foo(0, 1), Foo(1, 7)]); +} + +#[test] +fn test_vec_dedup() { + let mut vec: Vec = Vec::with_capacity(8); + let mut template = vec.clone(); + + for x in 0u8..255u8 { + vec.clear(); + template.clear(); + + let iter = (0..8).map(move |bit| (x >> bit) & 1 == 1); + vec.extend(iter); + template.extend_from_slice(&vec); + + let (dedup, _) = template.partition_dedup(); + vec.dedup(); + + assert_eq!(vec, dedup); + } +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_vec_dedup_panicking() { + #[derive(Debug)] + struct Panic<'a> { + drop_counter: &'a Cell, + value: bool, + index: usize, + } + + impl<'a> PartialEq for Panic<'a> { + fn eq(&self, other: &Self) -> bool { + self.value == other.value + } + } + + impl<'a> Drop for Panic<'a> { + fn drop(&mut self) { + self.drop_counter.set(self.drop_counter.get() + 1); + if !std::thread::panicking() { + assert!(self.index != 4); + } + } + } + + let drop_counter = &Cell::new(0); + let expected = [ + Panic { drop_counter, value: false, index: 0 }, + Panic { drop_counter, value: false, index: 5 }, + Panic { drop_counter, value: true, index: 6 }, + Panic { drop_counter, value: true, index: 7 }, + ]; + let mut vec = vec![ + Panic { drop_counter, value: false, index: 0 }, + // these elements get deduplicated + Panic { drop_counter, value: false, index: 1 }, + Panic { drop_counter, value: false, index: 2 }, + Panic { drop_counter, value: false, index: 3 }, + Panic { drop_counter, value: false, index: 4 }, + // here it panics while dropping the item with index==4 + Panic { drop_counter, value: false, index: 5 }, + Panic { drop_counter, value: true, index: 6 }, + Panic { drop_counter, value: true, index: 7 }, + ]; + + let _ = catch_unwind(AssertUnwindSafe(|| vec.dedup())).unwrap_err(); + + assert_eq!(drop_counter.get(), 4); + + let ok = vec.iter().zip(expected.iter()).all(|(x, y)| x.index == y.index); + + if !ok { + panic!("expected: {expected:?}\ngot: {vec:?}\n"); + } +} + +// Regression test for issue #82533 +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_extend_from_within_panicking_clone() { + struct Panic<'dc> { + drop_count: &'dc AtomicU32, + aaaaa: bool, + } + + impl Clone for Panic<'_> { + fn clone(&self) -> Self { + if self.aaaaa { + panic!("panic! at the clone"); + } + + Self { ..*self } + } + } + + impl Drop for Panic<'_> { + fn drop(&mut self) { + self.drop_count.fetch_add(1, Ordering::SeqCst); + } + } + + let count = core::sync::atomic::AtomicU32::new(0); + let mut vec = vec![ + Panic { drop_count: &count, aaaaa: false }, + Panic { drop_count: &count, aaaaa: true }, + Panic { drop_count: &count, aaaaa: false }, + ]; + + // This should clone&append one Panic{..} at the end, and then panic while + // cloning second Panic{..}. This means that `Panic::drop` should be called + // 4 times (3 for items already in vector, 1 for just appended). + // + // Previously just appended item was leaked, making drop_count = 3, instead of 4. + std::panic::catch_unwind(move || vec.extend_from_within(..)).unwrap_err(); + + assert_eq!(count.load(Ordering::SeqCst), 4); +} + +#[test] +#[should_panic = "vec len overflow"] +fn test_into_flattened_size_overflow() { + let v = vec![[(); usize::MAX]; 2]; + let _ = v.into_flattened(); +} + +#[test] +fn test_box_zero_allocator() { + use core::alloc::AllocError; + use core::cell::RefCell; + use std::collections::HashSet; + + // Track ZST allocations and ensure that they all have a matching free. + struct ZstTracker { + state: RefCell<(HashSet, usize)>, + } + unsafe impl Allocator for ZstTracker { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + let ptr = if layout.size() == 0 { + let mut state = self.state.borrow_mut(); + let addr = state.1; + assert!(state.0.insert(addr)); + state.1 += 1; + std::println!("allocating {addr}"); + std::ptr::without_provenance_mut(addr) + } else { + unsafe { std::alloc::alloc(layout) } + }; + Ok(NonNull::slice_from_raw_parts(NonNull::new(ptr).ok_or(AllocError)?, layout.size())) + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + if layout.size() == 0 { + let addr = ptr.as_ptr() as usize; + let mut state = self.state.borrow_mut(); + std::println!("freeing {addr}"); + assert!(state.0.remove(&addr), "ZST free that wasn't allocated"); + } else { + unsafe { std::alloc::dealloc(ptr.as_ptr(), layout) } + } + } + } + + // Start the state at 100 to avoid returning null pointers. + let alloc = ZstTracker { state: RefCell::new((HashSet::new(), 100)) }; + + // Ensure that unsizing retains the same behavior. + { + let b1: Box<[u8; 0], &ZstTracker> = Box::new_in([], &alloc); + let b2: Box<[u8], &ZstTracker> = b1.clone(); + let _b3: Box<[u8], &ZstTracker> = b2.clone(); + } + + // Ensure that shrinking doesn't leak a ZST allocation. + { + let mut v1: Vec = Vec::with_capacity_in(100, &alloc); + v1.shrink_to_fit(); + } + + // Ensure that conversion to/from vec works. + { + let v1: Vec<(), &ZstTracker> = Vec::with_capacity_in(100, &alloc); + let _b1: Box<[()], &ZstTracker> = v1.into_boxed_slice(); + let b2: Box<[()], &ZstTracker> = Box::new_in([(), (), ()], &alloc); + let _v2: Vec<(), &ZstTracker> = b2.into(); + } + + // Ensure all ZSTs have been freed. + assert!(alloc.state.borrow().0.is_empty()); +} + +#[test] +fn test_vec_from_array_ref() { + assert_eq!(Vec::from(&[1, 2, 3]), vec![1, 2, 3]); +} + +#[test] +fn test_vec_from_array_mut_ref() { + assert_eq!(Vec::from(&mut [1, 2, 3]), vec![1, 2, 3]); +} + +#[test] +fn test_pop_if() { + let mut v = vec![1, 2, 3, 4]; + let pred = |x: &mut i32| *x % 2 == 0; + + assert_eq!(v.pop_if(pred), Some(4)); + assert_eq!(v, [1, 2, 3]); + + assert_eq!(v.pop_if(pred), None); + assert_eq!(v, [1, 2, 3]); +} + +#[test] +fn test_pop_if_empty() { + let mut v = Vec::::new(); + assert_eq!(v.pop_if(|_| true), None); + assert!(v.is_empty()); +} + +#[test] +fn test_pop_if_mutates() { + let mut v = vec![1]; + let pred = |x: &mut i32| { + *x += 1; + false + }; + assert_eq!(v.pop_if(pred), None); + assert_eq!(v, [2]); +} + +/// This assortment of tests, in combination with miri, verifies we handle UB on fishy arguments +/// in the stdlib. Draining and extending the allocation are fairly well-tested earlier, but +/// `vec.insert(usize::MAX, val)` once slipped by! +/// +/// All code that manipulates the collection types should be tested with "trivially wrong" args. +#[test] +fn max_dont_panic() { + let mut v = vec![0]; + let _ = v.get(usize::MAX); + v.shrink_to(usize::MAX); + v.truncate(usize::MAX); +} + +#[test] +#[should_panic] +fn max_insert() { + let mut v = vec![0]; + v.insert(usize::MAX, 1); +} + +#[test] +#[should_panic] +fn max_remove() { + let mut v = vec![0]; + v.remove(usize::MAX); +} + +#[test] +#[should_panic] +fn max_splice() { + let mut v = vec![0]; + v.splice(usize::MAX.., core::iter::once(1)); +} + +#[test] +#[should_panic] +fn max_swap_remove() { + let mut v = vec![0]; + v.swap_remove(usize::MAX); +} + +// Regression test for #135338 +#[test] +fn vec_null_ptr_roundtrip() { + let ptr = std::ptr::from_ref(&42); + let zero = ptr.with_addr(0); + let roundtripped = vec![zero; 1].pop().unwrap(); + let new = roundtripped.with_addr(ptr.addr()); + unsafe { new.read() }; +} diff --git a/library/alloc/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs similarity index 95% rename from library/alloc/tests/vec_deque.rs rename to library/alloctests/tests/vec_deque.rs index 1b03c29e5bda1..b77ea3a312bef 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -1686,6 +1686,40 @@ fn truncate_leak() { assert_eq!(unsafe { DROPS }, 7); } +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn truncate_front_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 mut q = VecDeque::new(); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_front(D(true)); + q.push_front(D(false)); + q.push_front(D(false)); + + catch_unwind(AssertUnwindSafe(|| q.truncate_front(1))).ok(); + + assert_eq!(unsafe { DROPS }, 7); +} + #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_drain_leak() { @@ -1863,3 +1897,38 @@ fn test_collect_from_into_iter_keeps_allocation() { assert_eq!(v.capacity(), 13); } } + +#[test] +fn test_truncate_front() { + let mut v = VecDeque::with_capacity(13); + v.extend(0..7); + assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); + v.truncate_front(10); + assert_eq!(v.len(), 7); + assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); + v.truncate_front(7); + assert_eq!(v.len(), 7); + assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); + v.truncate_front(3); + assert_eq!(v.as_slices(), ([4, 5, 6].as_slice(), [].as_slice())); + assert_eq!(v.len(), 3); + v.truncate_front(0); + assert_eq!(v.as_slices(), ([].as_slice(), [].as_slice())); + assert_eq!(v.len(), 0); + + v.clear(); + v.extend(0..7); + assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); + v.push_front(9); + v.push_front(8); + v.push_front(7); + assert_eq!(v.as_slices(), ([7, 8, 9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); + v.truncate_front(12); + assert_eq!(v.as_slices(), ([7, 8, 9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); + v.truncate_front(10); + assert_eq!(v.as_slices(), ([7, 8, 9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); + v.truncate_front(8); + assert_eq!(v.as_slices(), ([9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); + v.truncate_front(5); + assert_eq!(v.as_slices(), ([2, 3, 4, 5, 6].as_slice(), [].as_slice())); +} diff --git a/library/alloc/tests/vec_deque_alloc_error.rs b/library/alloctests/tests/vec_deque_alloc_error.rs similarity index 100% rename from library/alloc/tests/vec_deque_alloc_error.rs rename to library/alloctests/tests/vec_deque_alloc_error.rs diff --git a/library/backtrace b/library/backtrace index 9d2c34e7e63af..6c882eb11984d 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit 9d2c34e7e63afe1e71c333b247065e3b7ba4d883 +Subproject commit 6c882eb11984d737f62e85f36703effaf34c2453 diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 1538ecc6b9297..83ba17b93f519 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -9,7 +9,7 @@ autobenches = false # If you update this, be sure to update it in a bunch of other places too! # As of 2024, it was src/tools/opt-dist, the core-no-fp-fmt-parse test and # the version of the prelude imported in core/lib.rs. -edition = "2021" +edition = "2024" [lib] test = false @@ -31,9 +31,14 @@ level = "warn" check-cfg = [ 'cfg(bootstrap)', 'cfg(no_fp_fmt_parse)', - 'cfg(stdarch_intel_sde)', # core use #[path] imports to portable-simd `core_simd` crate # and to stdarch `core_arch` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg 'cfg(feature, values(any()))', + # Internal features aren't marked known config by default, we use these to + # gate tests. + 'cfg(target_has_reliable_f16)', + 'cfg(target_has_reliable_f16_math)', + 'cfg(target_has_reliable_f128)', + 'cfg(target_has_reliable_f128_math)', ] diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 17f4d68867e1e..e8a03aadc3390 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -17,7 +17,7 @@ use crate::{assert_unsafe_precondition, fmt, mem}; // * https://github.com/rust-lang/rust/pull/72189 // * https://github.com/rust-lang/rust/pull/79827 const fn size_align() -> (usize, usize) { - (mem::size_of::(), mem::align_of::()) + (size_of::(), align_of::()) } /// Layout of a block of memory. @@ -182,7 +182,7 @@ impl Layout { #[must_use] #[inline] pub const fn for_value(t: &T) -> Self { - let (size, align) = (mem::size_of_val(t), mem::align_of_val(t)); + let (size, align) = (size_of_val(t), align_of_val(t)); // SAFETY: see rationale in `new` for why this is using the unsafe variant unsafe { Layout::from_size_align_unchecked(size, align) } } @@ -520,6 +520,14 @@ impl Layout { unsafe { Ok(Layout::from_size_align_unchecked(array_size, align.as_usize())) } } } + + /// Perma-unstable access to `align` as `Alignment` type. + #[unstable(issue = "none", feature = "std_internals")] + #[doc(hidden)] + #[inline] + pub const fn alignment(&self) -> Alignment { + self.align + } } #[stable(feature = "alloc_layout", since = "1.28.0")] diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index 9805cee1c331e..9d608d5e83c40 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -90,7 +90,7 @@ impl fmt::Display for AllocError { /// # Safety /// /// Memory blocks that are [*currently allocated*] by an allocator, -/// must point to valid memory, and retain their validity while until either: +/// must point to valid memory, and retain their validity until either: /// - the memory block is deallocated, or /// - the allocator is dropped. /// @@ -112,7 +112,9 @@ pub unsafe trait Allocator { /// /// The returned block of memory remains valid as long as it is [*currently allocated*] and the shorter of: /// - the borrow-checker lifetime of the allocator type itself. - /// - as long as at the allocator and all its clones has not been dropped. + /// - as long as the allocator and all its clones have not been dropped. + /// + /// [*currently allocated*]: #currently-allocated-memory /// /// # Errors /// diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 9ed2c8e9f3ad1..7aa3f3c6d7434 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -109,7 +109,7 @@ use crate::{fmt, hash, intrinsics}; // unsafe traits and unsafe methods (i.e., `type_id` would still be safe to call, // but we would likely want to indicate as such in documentation). #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "Any")] +#[rustc_diagnostic_item = "Any"] pub trait Any: 'static { /// Gets the `TypeId` of `self`. /// @@ -772,8 +772,8 @@ impl hash::Hash for TypeId { // (especially given the previous point about the lower 64 bits being // high quality on their own). // - It is correct to do so -- only hashing a subset of `self` is still - // with an `Eq` implementation that considers the entire value, as - // ours does. + // compatible with an `Eq` implementation that considers the entire + // value, as ours does. self.t.1.hash(state); } } diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 81d828a971c80..e5078a45c6d9c 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -32,7 +32,7 @@ pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) { /// /// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html /// [reference]: https://doc.rust-lang.org/nightly/reference/inline-assembly.html -#[unstable(feature = "naked_functions", issue = "90957")] +#[stable(feature = "naked_functions", since = "1.88.0")] #[rustc_builtin_macro] pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) { /* compiler built-in */ diff --git a/library/core/src/array/ascii.rs b/library/core/src/array/ascii.rs index e2faef855bc2c..792a57e3fa25c 100644 --- a/library/core/src/array/ascii.rs +++ b/library/core/src/array/ascii.rs @@ -1,6 +1,5 @@ use crate::ascii; -#[cfg(not(test))] impl [u8; N] { /// Converts this array of bytes into an array of ASCII characters, /// or returns `None` if any of the characters is non-ASCII. diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 1edade41597f7..90f76d6d4c7be 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -1,38 +1,35 @@ //! Defines the `IntoIter` owned iterator for arrays. use crate::intrinsics::transmute_unchecked; -use crate::iter::{self, FusedIterator, TrustedLen, TrustedRandomAccessNoCoerce}; +use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccessNoCoerce}; use crate::mem::MaybeUninit; use crate::num::NonZero; -use crate::ops::{IndexRange, Range}; +use crate::ops::{IndexRange, Range, Try}; use crate::{fmt, ptr}; +mod iter_inner; + +type InnerSized = iter_inner::PolymorphicIter<[MaybeUninit; N]>; +type InnerUnsized = iter_inner::PolymorphicIter<[MaybeUninit]>; + /// A by-value [array] iterator. #[stable(feature = "array_value_iter", since = "1.51.0")] #[rustc_insignificant_dtor] #[rustc_diagnostic_item = "ArrayIntoIter"] +#[derive(Clone)] 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], + inner: InnerSized, +} - /// The elements in `data` that have not been yielded yet. - /// - /// Invariants: - /// - `alive.end <= N` - /// - /// (And the `IndexRange` type requires `alive.start <= alive.end`.) - alive: IndexRange, +impl IntoIter { + #[inline] + fn unsize(&self) -> &InnerUnsized { + &self.inner + } + #[inline] + fn unsize_mut(&mut self) -> &mut InnerUnsized { + &mut self.inner + } } // Note: the `#[rustc_skip_during_method_dispatch(array)]` on `trait IntoIterator` @@ -53,6 +50,7 @@ impl IntoIterator for [T; N] { /// 2021 edition -- see the [array] Editions section for more information. /// /// [array]: prim@array + #[inline] fn into_iter(self) -> Self::IntoIter { // SAFETY: The transmute here is actually safe. The docs of `MaybeUninit` // promise: @@ -68,7 +66,10 @@ impl IntoIterator for [T; N] { // FIXME: If normal `transmute` ever gets smart enough to allow this // directly, use it instead of `transmute_unchecked`. let data: [MaybeUninit; N] = unsafe { transmute_unchecked(self) }; - IntoIter { data, alive: IndexRange::zero_to(N) } + // SAFETY: The original array was entirely initialized and the the alive + // range we're passing here represents that fact. + let inner = unsafe { InnerSized::new_unchecked(IndexRange::zero_to(N), data) }; + IntoIter { inner } } } @@ -136,13 +137,16 @@ impl IntoIter { /// assert_eq!(r.collect::>(), vec![10, 11, 12, 13, 14, 15]); /// ``` #[unstable(feature = "array_into_iter_constructors", issue = "91583")] + #[inline] pub const unsafe fn new_unchecked( buffer: [MaybeUninit; N], initialized: Range, ) -> Self { // SAFETY: one of our safety conditions is that the range is canonical. let alive = unsafe { IndexRange::new_unchecked(initialized.start, initialized.end) }; - Self { data: buffer, alive } + // SAFETY: one of our safety condition is that these items are initialized. + let inner = unsafe { InnerSized::new_unchecked(alive, buffer) }; + IntoIter { inner } } /// Creates an iterator over `T` which returns no elements. @@ -198,172 +202,134 @@ impl IntoIter { /// assert_eq!(get_bytes(false).collect::>(), vec![]); /// ``` #[unstable(feature = "array_into_iter_constructors", issue = "91583")] + #[inline] pub const fn empty() -> Self { - let buffer = [const { MaybeUninit::uninit() }; N]; - let initialized = 0..0; - - // SAFETY: We're telling it that none of the elements are initialized, - // which is trivially true. And ∀N: usize, 0 <= N. - unsafe { Self::new_unchecked(buffer, initialized) } + let inner = InnerSized::empty(); + IntoIter { inner } } /// Returns an immutable slice of all elements that have not been yielded /// yet. #[stable(feature = "array_value_iter", since = "1.51.0")] + #[inline] pub 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()); - slice.assume_init_ref() - } + self.unsize().as_slice() } /// Returns a mutable slice of all elements that have not been yielded yet. #[stable(feature = "array_value_iter", since = "1.51.0")] + #[inline] pub 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()); - slice.assume_init_mut() - } + self.unsize_mut().as_mut_slice() } } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] impl Iterator for IntoIter { type Item = T; + + #[inline] 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() } - }) + self.unsize_mut().next() } + #[inline] fn size_hint(&self) -> (usize, Option) { - let len = self.len(); - (len, Some(len)) + self.unsize().size_hint() } #[inline] - fn fold(mut self, init: Acc, mut fold: Fold) -> Acc + fn fold(mut self, init: Acc, fold: Fold) -> Acc where Fold: FnMut(Acc, Self::Item) -> Acc, { - let data = &mut self.data; - iter::ByRefSized(&mut self.alive).fold(init, |acc, idx| { - // SAFETY: idx is obtained by folding over the `alive` range, which implies the - // value is currently considered alive but as the range is being consumed each value - // we read here will only be read once and then considered dead. - fold(acc, unsafe { data.get_unchecked(idx).assume_init_read() }) - }) + self.unsize_mut().fold(init, fold) } + #[inline] + fn try_fold(&mut self, init: B, f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + self.unsize_mut().try_fold(init, f) + } + + #[inline] fn count(self) -> usize { self.len() } + #[inline] fn last(mut self) -> Option { self.next_back() } + #[inline] fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { - // This also moves the start, which marks them as conceptually "dropped", - // so if anything goes bad then our drop impl won't double-free them. - let range_to_drop = self.alive.take_prefix(n); - let remaining = n - range_to_drop.len(); - - // SAFETY: These elements are currently initialized, so it's fine to drop them. - unsafe { - let slice = self.data.get_unchecked_mut(range_to_drop); - slice.assume_init_drop(); - } - - NonZero::new(remaining).map_or(Ok(()), Err) + self.unsize_mut().advance_by(n) } #[inline] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { // SAFETY: The caller must provide an idx that is in bound of the remainder. - unsafe { self.data.as_ptr().add(self.alive.start()).add(idx).cast::().read() } + let elem_ref = unsafe { self.as_mut_slice().get_unchecked_mut(idx) }; + // SAFETY: We only implement `TrustedRandomAccessNoCoerce` for types + // which are actually `Copy`, so cannot have multiple-drop issues. + unsafe { ptr::read(elem_ref) } } } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] impl DoubleEndedIterator for IntoIter { + #[inline] 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() } - }) + self.unsize_mut().next_back() } #[inline] - fn rfold(mut self, init: Acc, mut rfold: Fold) -> Acc + fn rfold(mut self, init: Acc, rfold: Fold) -> Acc where Fold: FnMut(Acc, Self::Item) -> Acc, { - let data = &mut self.data; - iter::ByRefSized(&mut self.alive).rfold(init, |acc, idx| { - // SAFETY: idx is obtained by folding over the `alive` range, which implies the - // value is currently considered alive but as the range is being consumed each value - // we read here will only be read once and then considered dead. - rfold(acc, unsafe { data.get_unchecked(idx).assume_init_read() }) - }) + self.unsize_mut().rfold(init, rfold) + } + + #[inline] + fn try_rfold(&mut self, init: B, f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + self.unsize_mut().try_rfold(init, f) } + #[inline] fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { - // This also moves the end, which marks them as conceptually "dropped", - // so if anything goes bad then our drop impl won't double-free them. - let range_to_drop = self.alive.take_suffix(n); - let remaining = n - range_to_drop.len(); - - // SAFETY: These elements are currently initialized, so it's fine to drop them. - unsafe { - let slice = self.data.get_unchecked_mut(range_to_drop); - slice.assume_init_drop(); - } - - NonZero::new(remaining).map_or(Ok(()), Err) + self.unsize_mut().advance_back_by(n) } } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] impl Drop for IntoIter { + #[inline] 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()) } + // `inner` now handles this, but it'd technically be a breaking change + // to remove this `impl`, even though it's useless. } } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] impl ExactSizeIterator for IntoIter { + #[inline] fn len(&self) -> usize { - self.alive.len() + self.inner.len() } + #[inline] fn is_empty(&self) -> bool { - self.alive.is_empty() + self.inner.len() == 0 } } @@ -396,32 +362,9 @@ where const MAY_HAVE_SIDE_EFFECT: bool = false; } -#[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: [const { MaybeUninit::uninit() }; N], alive: IndexRange::zero_to(0) }; - - // Clone all alive elements. - for (src, dst) in iter::zip(self.as_slice(), &mut new.data) { - // 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()); - // This addition cannot overflow as we're iterating a slice - new.alive = IndexRange::zero_to(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() + self.unsize().fmt(f) } } diff --git a/library/core/src/array/iter/iter_inner.rs b/library/core/src/array/iter/iter_inner.rs new file mode 100644 index 0000000000000..3c2343591f8cf --- /dev/null +++ b/library/core/src/array/iter/iter_inner.rs @@ -0,0 +1,281 @@ +//! Defines the `IntoIter` owned iterator for arrays. + +use crate::mem::MaybeUninit; +use crate::num::NonZero; +use crate::ops::{IndexRange, NeverShortCircuit, Try}; +use crate::{fmt, iter}; + +#[allow(private_bounds)] +trait PartialDrop { + /// # Safety + /// `self[alive]` are all initialized before the call, + /// then are never used (without reinitializing them) after it. + unsafe fn partial_drop(&mut self, alive: IndexRange); +} +impl PartialDrop for [MaybeUninit] { + unsafe fn partial_drop(&mut self, alive: IndexRange) { + // SAFETY: We know that all elements within `alive` are properly initialized. + unsafe { self.get_unchecked_mut(alive).assume_init_drop() } + } +} +impl PartialDrop for [MaybeUninit; N] { + unsafe fn partial_drop(&mut self, alive: IndexRange) { + let slice: &mut [MaybeUninit] = self; + // SAFETY: Initialized elements in the array are also initialized in the slice. + unsafe { slice.partial_drop(alive) } + } +} + +/// The internals of a by-value array iterator. +/// +/// The real `array::IntoIter` stores a `PolymorphicIter<[MaybeUninit, N]>` +/// which it unsizes to `PolymorphicIter<[MaybeUninit]>` to iterate. +#[allow(private_bounds)] +pub(super) struct PolymorphicIter +where + DATA: PartialDrop, +{ + /// The elements in `data` that have not been yielded yet. + /// + /// Invariants: + /// - `alive.end <= N` + /// + /// (And the `IndexRange` type requires `alive.start <= alive.end`.) + alive: IndexRange, + + /// 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: DATA, +} + +#[allow(private_bounds)] +impl PolymorphicIter +where + DATA: PartialDrop, +{ + #[inline] + pub(super) const fn len(&self) -> usize { + self.alive.len() + } +} + +#[allow(private_bounds)] +impl Drop for PolymorphicIter +where + DATA: PartialDrop, +{ + #[inline] + fn drop(&mut self) { + // SAFETY: by our type invariant `self.alive` is exactly the initialized + // items, and this is drop so nothing can use the items afterwards. + unsafe { self.data.partial_drop(self.alive.clone()) } + } +} + +impl PolymorphicIter<[MaybeUninit; N]> { + #[inline] + pub(super) const fn empty() -> Self { + Self { alive: IndexRange::zero_to(0), data: [const { MaybeUninit::uninit() }; N] } + } + + /// # Safety + /// `data[alive]` are all initialized. + #[inline] + pub(super) const unsafe fn new_unchecked(alive: IndexRange, data: [MaybeUninit; N]) -> Self { + Self { alive, data } + } +} + +impl Clone for PolymorphicIter<[MaybeUninit; N]> { + #[inline] + 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::empty(); + + fn clone_into_new( + source: &PolymorphicIter<[MaybeUninit]>, + target: &mut PolymorphicIter<[MaybeUninit]>, + ) { + // Clone all alive elements. + for (src, dst) in iter::zip(source.as_slice(), &mut target.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()); + // This addition cannot overflow as we're iterating a slice, + // the length of which always fits in usize. + target.alive = IndexRange::zero_to(target.alive.end() + 1); + } + } + + clone_into_new(self, &mut new); + new + } +} + +impl PolymorphicIter<[MaybeUninit]> { + #[inline] + pub(super) 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()); + slice.assume_init_ref() + } + } + + #[inline] + pub(super) 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()); + slice.assume_init_mut() + } + } +} + +impl fmt::Debug for PolymorphicIter<[MaybeUninit]> { + #[inline] + 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() + } +} + +/// Iterator-equivalent methods. +/// +/// We don't implement the actual iterator traits because we want to implement +/// things like `try_fold` that require `Self: Sized` (which we're not). +impl PolymorphicIter<[MaybeUninit]> { + #[inline] + pub(super) 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() } + }) + } + + #[inline] + pub(super) fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } + + #[inline] + pub(super) fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + // This also moves the start, which marks them as conceptually "dropped", + // so if anything goes bad then our drop impl won't double-free them. + let range_to_drop = self.alive.take_prefix(n); + let remaining = n - range_to_drop.len(); + + // SAFETY: These elements are currently initialized, so it's fine to drop them. + unsafe { + let slice = self.data.get_unchecked_mut(range_to_drop); + slice.assume_init_drop(); + } + + NonZero::new(remaining).map_or(Ok(()), Err) + } + + #[inline] + pub(super) fn fold(&mut self, init: B, f: impl FnMut(B, T) -> B) -> B { + self.try_fold(init, NeverShortCircuit::wrap_mut_2(f)).0 + } + + #[inline] + pub(super) fn try_fold(&mut self, init: B, mut f: F) -> R + where + F: FnMut(B, T) -> R, + R: Try, + { + // `alive` is an `IndexRange`, not an arbitrary iterator, so we can + // trust that its `try_fold` isn't going to do something weird like + // call the fold-er multiple times for the same index. + let data = &mut self.data; + self.alive.try_fold(init, move |accum, idx| { + // SAFETY: `idx` has been removed from the alive range, so we're not + // going to drop it (even if `f` panics) and thus its ok to give + // out ownership of that item to `f` to handle. + let elem = unsafe { data.get_unchecked(idx).assume_init_read() }; + f(accum, elem) + }) + } + + #[inline] + pub(super) 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() } + }) + } + + #[inline] + pub(super) fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + // This also moves the end, which marks them as conceptually "dropped", + // so if anything goes bad then our drop impl won't double-free them. + let range_to_drop = self.alive.take_suffix(n); + let remaining = n - range_to_drop.len(); + + // SAFETY: These elements are currently initialized, so it's fine to drop them. + unsafe { + let slice = self.data.get_unchecked_mut(range_to_drop); + slice.assume_init_drop(); + } + + NonZero::new(remaining).map_or(Ok(()), Err) + } + + #[inline] + pub(super) fn rfold(&mut self, init: B, f: impl FnMut(B, T) -> B) -> B { + self.try_rfold(init, NeverShortCircuit::wrap_mut_2(f)).0 + } + + #[inline] + pub(super) fn try_rfold(&mut self, init: B, mut f: F) -> R + where + F: FnMut(B, T) -> R, + R: Try, + { + // `alive` is an `IndexRange`, not an arbitrary iterator, so we can + // trust that its `try_rfold` isn't going to do something weird like + // call the fold-er multiple times for the same index. + let data = &mut self.data; + self.alive.try_rfold(init, move |accum, idx| { + // SAFETY: `idx` has been removed from the alive range, so we're not + // going to drop it (even if `f` panics) and thus its ok to give + // out ownership of that item to `f` to handle. + let elem = unsafe { data.get_unchecked(idx).assume_init_read() }; + f(accum, elem) + }) + } +} diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 28329bb090845..4476e3f79238d 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -55,12 +55,16 @@ pub fn repeat(val: T) -> [T; N] { from_trusted_iterator(repeat_n(val, N)) } -/// Creates an array of type [T; N], where each element `T` is the returned value from `cb` -/// using that element's index. +/// Creates an array where each element is produced by calling `f` with +/// that element's index while walking forward through the array. /// -/// # Arguments +/// This is essentially the same as writing +/// ```text +/// [f(0), f(1), f(2), …, f(N - 2), f(N - 1)] +/// ``` +/// and is similar to `(0..i).map(f)`, just for arrays not iterators. /// -/// * `cb`: Callback where the passed argument is the current array index. +/// If `N == 0`, this produces an empty array without ever calling `f`. /// /// # Example /// @@ -82,13 +86,30 @@ pub fn repeat(val: T) -> [T; N] { /// // indexes are: 0 1 2 3 4 /// assert_eq!(bool_arr, [true, false, true, false, true]); /// ``` +/// +/// You can also capture things, for example to create an array full of clones +/// where you can't just use `[item; N]` because it's not `Copy`: +/// ``` +/// # // TBH `array::repeat` would be better for this, but it's not stable yet. +/// let my_string = String::from("Hello"); +/// let clones: [String; 42] = std::array::from_fn(|_| my_string.clone()); +/// assert!(clones.iter().all(|x| *x == my_string)); +/// ``` +/// +/// The array is generated in ascending index order, starting from the front +/// and going towards the back, so you can use closures with mutable state: +/// ``` +/// let mut state = 1; +/// let a = std::array::from_fn(|_| { let x = state; state *= 2; x }); +/// assert_eq!(a, [1, 2, 4, 8, 16, 32]); +/// ``` #[inline] #[stable(feature = "array_from_fn", since = "1.63.0")] -pub fn from_fn(cb: F) -> [T; N] +pub fn from_fn(f: F) -> [T; N] where F: FnMut(usize) -> T, { - try_from_fn(NeverShortCircuit::wrap_mut_1(cb)).0 + try_from_fn(NeverShortCircuit::wrap_mut_1(f)).0 } /// Creates an array `[T; N]` where each fallible array element `T` is returned by the `cb` call. @@ -510,6 +531,7 @@ impl [T; N] { /// let y = x.map(|v| v.len()); /// assert_eq!(y, [6, 9, 3, 3]); /// ``` + #[must_use] #[stable(feature = "array_map", since = "1.55.0")] pub fn map(self, f: F) -> [U; N] where diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs index 3c589ca5dfa7e..2016ece007eba 100644 --- a/library/core/src/bool.rs +++ b/library/core/src/bool.rs @@ -56,57 +56,9 @@ impl bool { /// ``` #[doc(alias = "then_with")] #[stable(feature = "lazy_bool_to_option", since = "1.50.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "bool_then")] + #[rustc_diagnostic_item = "bool_then"] #[inline] pub fn then T>(self, f: F) -> Option { if self { Some(f()) } else { None } } - - /// Returns either `true_val` or `false_val` depending on the value of - /// `self`, with a hint to the compiler that `self` is unlikely - /// to be correctly predicted by a CPU’s branch predictor. - /// - /// This method is functionally equivalent to - /// ```ignore (this is just for illustrative purposes) - /// fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { - /// if b { true_val } else { false_val } - /// } - /// ``` - /// but might generate different assembly. In particular, on platforms with - /// a conditional move or select instruction (like `cmov` on x86 or `csel` - /// on ARM) the optimizer might use these instructions to avoid branches, - /// which can benefit performance if the branch predictor is struggling - /// with predicting `condition`, such as in an implementation of binary - /// search. - /// - /// Note however that this lowering is not guaranteed (on any platform) and - /// should not be relied upon when trying to write constant-time code. Also - /// be aware that this lowering might *decrease* performance if `condition` - /// is well-predictable. It is advisable to perform benchmarks to tell if - /// this function is useful. - /// - /// # Examples - /// - /// Distribute values evenly between two buckets: - /// ``` - /// #![feature(select_unpredictable)] - /// - /// use std::hash::BuildHasher; - /// - /// fn append(hasher: &H, v: i32, bucket_one: &mut Vec, bucket_two: &mut Vec) { - /// let hash = hasher.hash_one(&v); - /// let bucket = (hash % 2 == 0).select_unpredictable(bucket_one, bucket_two); - /// bucket.push(v); - /// } - /// # let hasher = std::collections::hash_map::RandomState::new(); - /// # let mut bucket_one = Vec::new(); - /// # let mut bucket_two = Vec::new(); - /// # append(&hasher, 42, &mut bucket_one, &mut bucket_two); - /// # assert_eq!(bucket_one.len() + bucket_two.len(), 1); - /// ``` - #[inline(always)] - #[unstable(feature = "select_unpredictable", issue = "133962")] - pub fn select_unpredictable(self, true_val: T, false_val: T) -> T { - crate::intrinsics::select_unpredictable(self, true_val, false_val) - } } diff --git a/library/core/src/bstr.rs b/library/core/src/bstr.rs deleted file mode 100644 index 74e07f3d242cd..0000000000000 --- a/library/core/src/bstr.rs +++ /dev/null @@ -1,581 +0,0 @@ -//! The `ByteStr` type and trait implementations. - -use crate::borrow::{Borrow, BorrowMut}; -use crate::cmp::Ordering; -use crate::ops::{ - Deref, DerefMut, DerefPure, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, - RangeTo, RangeToInclusive, -}; -use crate::{fmt, hash}; - -/// A wrapper for `&[u8]` representing a human-readable string that's conventionally, but not -/// always, UTF-8. -/// -/// Unlike `&str`, this type permits non-UTF-8 contents, making it suitable for user input, -/// non-native filenames (as `Path` only supports native filenames), and other applications that -/// need to round-trip whatever data the user provides. -/// -/// For an owned, growable byte string buffer, use -/// [`ByteString`](../../std/bstr/struct.ByteString.html). -/// -/// `ByteStr` implements `Deref` to `[u8]`, so all methods available on `[u8]` are available on -/// `ByteStr`. -/// -/// # Representation -/// -/// A `&ByteStr` has the same representation as a `&str`. That is, a `&ByteStr` is a wide pointer -/// which includes a pointer to some bytes and a length. -/// -/// # Trait implementations -/// -/// The `ByteStr` type has a number of trait implementations, and in particular, defines equality -/// and comparisons between `&ByteStr`, `&str`, and `&[u8]`, for convenience. -/// -/// The `Debug` implementation for `ByteStr` shows its bytes as a normal string, with invalid UTF-8 -/// presented as hex escape sequences. -/// -/// The `Display` implementation behaves as if the `ByteStr` were first lossily converted to a -/// `str`, with invalid UTF-8 presented as the Unicode replacement character: � -/// -#[unstable(feature = "bstr", issue = "134915")] -#[repr(transparent)] -#[doc(alias = "BStr")] -pub struct ByteStr(pub [u8]); - -impl ByteStr { - /// Creates a `ByteStr` slice from anything that can be converted to a byte slice. - /// - /// This is a zero-cost conversion. - /// - /// # Example - /// - /// You can create a `ByteStr` from a byte array, a byte slice or a string slice: - /// - /// ``` - /// # #![feature(bstr)] - /// # use std::bstr::ByteStr; - /// let a = ByteStr::new(b"abc"); - /// let b = ByteStr::new(&b"abc"[..]); - /// let c = ByteStr::new("abc"); - /// - /// assert_eq!(a, b); - /// assert_eq!(a, c); - /// ``` - #[inline] - #[unstable(feature = "bstr", issue = "134915")] - pub fn new>(bytes: &B) -> &Self { - ByteStr::from_bytes(bytes.as_ref()) - } - - #[doc(hidden)] - #[unstable(feature = "bstr_internals", issue = "none")] - #[inline] - pub fn from_bytes(slice: &[u8]) -> &Self { - // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to - // the wrapped type into a reference to the wrapper type. - unsafe { &*(slice as *const [u8] as *const Self) } - } - - #[doc(hidden)] - #[unstable(feature = "bstr_internals", issue = "none")] - #[inline] - pub fn from_bytes_mut(slice: &mut [u8]) -> &mut Self { - // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to - // the wrapped type into a reference to the wrapper type. - unsafe { &mut *(slice as *mut [u8] as *mut Self) } - } - - #[doc(hidden)] - #[unstable(feature = "bstr_internals", issue = "none")] - #[inline] - pub fn as_bytes(&self) -> &[u8] { - &self.0 - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Deref for ByteStr { - type Target = [u8]; - - #[inline] - fn deref(&self) -> &[u8] { - &self.0 - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl DerefMut for ByteStr { - #[inline] - fn deref_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -#[unstable(feature = "deref_pure_trait", issue = "87121")] -unsafe impl DerefPure for ByteStr {} - -#[unstable(feature = "bstr", issue = "134915")] -impl fmt::Debug for ByteStr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "\"")?; - for chunk in self.utf8_chunks() { - for c in chunk.valid().chars() { - match c { - '\0' => write!(f, "\\0")?, - '\x01'..='\x7f' => write!(f, "{}", (c as u8).escape_ascii())?, - _ => write!(f, "{}", c.escape_debug())?, - } - } - write!(f, "{}", chunk.invalid().escape_ascii())?; - } - write!(f, "\"")?; - Ok(()) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl fmt::Display for ByteStr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fn fmt_nopad(this: &ByteStr, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for chunk in this.utf8_chunks() { - f.write_str(chunk.valid())?; - if !chunk.invalid().is_empty() { - f.write_str("\u{FFFD}")?; - } - } - Ok(()) - } - - let Some(align) = f.align() else { - return fmt_nopad(self, f); - }; - let nchars: usize = self - .utf8_chunks() - .map(|chunk| chunk.valid().len() + if chunk.invalid().is_empty() { 0 } else { 1 }) - .sum(); - let padding = f.width().unwrap_or(0).saturating_sub(nchars); - let fill = f.fill(); - let (lpad, rpad) = match align { - fmt::Alignment::Left => (0, padding), - fmt::Alignment::Right => (padding, 0), - fmt::Alignment::Center => { - let half = padding / 2; - (half, half + padding % 2) - } - }; - for _ in 0..lpad { - write!(f, "{fill}")?; - } - fmt_nopad(self, f)?; - for _ in 0..rpad { - write!(f, "{fill}")?; - } - - Ok(()) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl AsRef<[u8]> for ByteStr { - #[inline] - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl AsRef for ByteStr { - #[inline] - fn as_ref(&self) -> &ByteStr { - self - } -} - -// `impl AsRef for [u8]` omitted to avoid widespread inference failures - -#[unstable(feature = "bstr", issue = "134915")] -impl AsRef for str { - #[inline] - fn as_ref(&self) -> &ByteStr { - ByteStr::new(self) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl AsMut<[u8]> for ByteStr { - #[inline] - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -// `impl AsMut for [u8]` omitted to avoid widespread inference failures - -// `impl Borrow for [u8]` omitted to avoid widespread inference failures - -// `impl Borrow for str` omitted to avoid widespread inference failures - -#[unstable(feature = "bstr", issue = "134915")] -impl Borrow<[u8]> for ByteStr { - #[inline] - fn borrow(&self) -> &[u8] { - &self.0 - } -} - -// `impl BorrowMut for [u8]` omitted to avoid widespread inference failures - -#[unstable(feature = "bstr", issue = "134915")] -impl BorrowMut<[u8]> for ByteStr { - #[inline] - fn borrow_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl<'a> Default for &'a ByteStr { - fn default() -> Self { - ByteStr::from_bytes(b"") - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl<'a> Default for &'a mut ByteStr { - fn default() -> Self { - ByteStr::from_bytes_mut(&mut []) - } -} - -// Omitted due to inference failures -// -// #[unstable(feature = "bstr", issue = "134915")] -// impl<'a, const N: usize> From<&'a [u8; N]> for &'a ByteStr { -// #[inline] -// fn from(s: &'a [u8; N]) -> Self { -// ByteStr::from_bytes(s) -// } -// } -// -// #[unstable(feature = "bstr", issue = "134915")] -// impl<'a> From<&'a [u8]> for &'a ByteStr { -// #[inline] -// fn from(s: &'a [u8]) -> Self { -// ByteStr::from_bytes(s) -// } -// } - -// Omitted due to slice-from-array-issue-113238: -// -// #[unstable(feature = "bstr", issue = "134915")] -// impl<'a> From<&'a ByteStr> for &'a [u8] { -// #[inline] -// fn from(s: &'a ByteStr) -> Self { -// &s.0 -// } -// } -// -// #[unstable(feature = "bstr", issue = "134915")] -// impl<'a> From<&'a mut ByteStr> for &'a mut [u8] { -// #[inline] -// fn from(s: &'a mut ByteStr) -> Self { -// &mut s.0 -// } -// } - -// Omitted due to inference failures -// -// #[unstable(feature = "bstr", issue = "134915")] -// impl<'a> From<&'a str> for &'a ByteStr { -// #[inline] -// fn from(s: &'a str) -> Self { -// ByteStr::from_bytes(s.as_bytes()) -// } -// } - -#[unstable(feature = "bstr", issue = "134915")] -impl hash::Hash for ByteStr { - #[inline] - fn hash(&self, state: &mut H) { - self.0.hash(state); - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index for ByteStr { - type Output = u8; - - #[inline] - fn index(&self, idx: usize) -> &u8 { - &self.0[idx] - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index for ByteStr { - type Output = ByteStr; - - #[inline] - fn index(&self, _: RangeFull) -> &ByteStr { - self - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteStr { - type Output = ByteStr; - - #[inline] - fn index(&self, r: Range) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteStr { - type Output = ByteStr; - - #[inline] - fn index(&self, r: RangeInclusive) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteStr { - type Output = ByteStr; - - #[inline] - fn index(&self, r: RangeFrom) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteStr { - type Output = ByteStr; - - #[inline] - fn index(&self, r: RangeTo) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteStr { - type Output = ByteStr; - - #[inline] - fn index(&self, r: RangeToInclusive) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut for ByteStr { - #[inline] - fn index_mut(&mut self, idx: usize) -> &mut u8 { - &mut self.0[idx] - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut for ByteStr { - #[inline] - fn index_mut(&mut self, _: RangeFull) -> &mut ByteStr { - self - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteStr { - #[inline] - fn index_mut(&mut self, r: Range) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteStr { - #[inline] - fn index_mut(&mut self, r: RangeInclusive) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteStr { - #[inline] - fn index_mut(&mut self, r: RangeFrom) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteStr { - #[inline] - fn index_mut(&mut self, r: RangeTo) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteStr { - #[inline] - fn index_mut(&mut self, r: RangeToInclusive) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Eq for ByteStr {} - -#[unstable(feature = "bstr", issue = "134915")] -impl PartialEq for ByteStr { - #[inline] - fn eq(&self, other: &ByteStr) -> bool { - &self.0 == &other.0 - } -} - -#[doc(hidden)] -#[macro_export] -#[unstable(feature = "bstr_internals", issue = "none")] -macro_rules! impl_partial_eq { - ($lhs:ty, $rhs:ty) => { - #[allow(unused_lifetimes)] - impl<'a> PartialEq<$rhs> for $lhs { - #[inline] - fn eq(&self, other: &$rhs) -> bool { - let other: &[u8] = other.as_ref(); - PartialEq::eq(self.as_bytes(), other) - } - } - - #[allow(unused_lifetimes)] - impl<'a> PartialEq<$lhs> for $rhs { - #[inline] - fn eq(&self, other: &$lhs) -> bool { - let this: &[u8] = self.as_ref(); - PartialEq::eq(this, other.as_bytes()) - } - } - }; -} - -#[doc(hidden)] -#[unstable(feature = "bstr_internals", issue = "none")] -pub use impl_partial_eq; - -#[doc(hidden)] -#[macro_export] -#[unstable(feature = "bstr_internals", issue = "none")] -macro_rules! impl_partial_eq_ord { - ($lhs:ty, $rhs:ty) => { - $crate::bstr::impl_partial_eq!($lhs, $rhs); - - #[allow(unused_lifetimes)] - #[unstable(feature = "bstr", issue = "134915")] - impl<'a> PartialOrd<$rhs> for $lhs { - #[inline] - fn partial_cmp(&self, other: &$rhs) -> Option { - let other: &[u8] = other.as_ref(); - PartialOrd::partial_cmp(self.as_bytes(), other) - } - } - - #[allow(unused_lifetimes)] - #[unstable(feature = "bstr", issue = "134915")] - impl<'a> PartialOrd<$lhs> for $rhs { - #[inline] - fn partial_cmp(&self, other: &$lhs) -> Option { - let this: &[u8] = self.as_ref(); - PartialOrd::partial_cmp(this, other.as_bytes()) - } - } - }; -} - -#[doc(hidden)] -#[unstable(feature = "bstr_internals", issue = "none")] -pub use impl_partial_eq_ord; - -#[doc(hidden)] -#[macro_export] -#[unstable(feature = "bstr_internals", issue = "none")] -macro_rules! impl_partial_eq_n { - ($lhs:ty, $rhs:ty) => { - #[allow(unused_lifetimes)] - #[unstable(feature = "bstr", issue = "134915")] - impl PartialEq<$rhs> for $lhs { - #[inline] - fn eq(&self, other: &$rhs) -> bool { - let other: &[u8] = other.as_ref(); - PartialEq::eq(self.as_bytes(), other) - } - } - - #[allow(unused_lifetimes)] - #[unstable(feature = "bstr", issue = "134915")] - impl PartialEq<$lhs> for $rhs { - #[inline] - fn eq(&self, other: &$lhs) -> bool { - let this: &[u8] = self.as_ref(); - PartialEq::eq(this, other.as_bytes()) - } - } - }; -} - -#[doc(hidden)] -#[unstable(feature = "bstr_internals", issue = "none")] -pub use impl_partial_eq_n; - -// PartialOrd with `[u8]` omitted to avoid inference failures -impl_partial_eq!(ByteStr, [u8]); -// PartialOrd with `&[u8]` omitted to avoid inference failures -impl_partial_eq!(ByteStr, &[u8]); -// PartialOrd with `str` omitted to avoid inference failures -impl_partial_eq!(ByteStr, str); -// PartialOrd with `&str` omitted to avoid inference failures -impl_partial_eq!(ByteStr, &str); -// PartialOrd with `[u8; N]` omitted to avoid inference failures -impl_partial_eq_n!(ByteStr, [u8; N]); -// PartialOrd with `[u8; N]` omitted to avoid inference failures -impl_partial_eq_n!(ByteStr, &[u8; N]); - -#[unstable(feature = "bstr", issue = "134915")] -impl Ord for ByteStr { - #[inline] - fn cmp(&self, other: &ByteStr) -> Ordering { - Ord::cmp(&self.0, &other.0) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl PartialOrd for ByteStr { - #[inline] - fn partial_cmp(&self, other: &ByteStr) -> Option { - PartialOrd::partial_cmp(&self.0, &other.0) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl<'a> TryFrom<&'a ByteStr> for &'a str { - type Error = crate::str::Utf8Error; - - #[inline] - fn try_from(s: &'a ByteStr) -> Result { - crate::str::from_utf8(&s.0) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl<'a> TryFrom<&'a mut ByteStr> for &'a mut str { - type Error = crate::str::Utf8Error; - - #[inline] - fn try_from(s: &'a mut ByteStr) -> Result { - crate::str::from_utf8_mut(&mut s.0) - } -} diff --git a/library/core/src/bstr/mod.rs b/library/core/src/bstr/mod.rs new file mode 100644 index 0000000000000..13127d645a257 --- /dev/null +++ b/library/core/src/bstr/mod.rs @@ -0,0 +1,323 @@ +//! The `ByteStr` type and trait implementations. + +mod traits; + +#[unstable(feature = "bstr_internals", issue = "none")] +pub use traits::{impl_partial_eq, impl_partial_eq_n, impl_partial_eq_ord}; + +use crate::borrow::{Borrow, BorrowMut}; +use crate::fmt; +use crate::ops::{Deref, DerefMut, DerefPure}; + +/// A wrapper for `&[u8]` representing a human-readable string that's conventionally, but not +/// always, UTF-8. +/// +/// Unlike `&str`, this type permits non-UTF-8 contents, making it suitable for user input, +/// non-native filenames (as `Path` only supports native filenames), and other applications that +/// need to round-trip whatever data the user provides. +/// +/// For an owned, growable byte string buffer, use +/// [`ByteString`](../../std/bstr/struct.ByteString.html). +/// +/// `ByteStr` implements `Deref` to `[u8]`, so all methods available on `[u8]` are available on +/// `ByteStr`. +/// +/// # Representation +/// +/// A `&ByteStr` has the same representation as a `&str`. That is, a `&ByteStr` is a wide pointer +/// which includes a pointer to some bytes and a length. +/// +/// # Trait implementations +/// +/// The `ByteStr` type has a number of trait implementations, and in particular, defines equality +/// and comparisons between `&ByteStr`, `&str`, and `&[u8]`, for convenience. +/// +/// The `Debug` implementation for `ByteStr` shows its bytes as a normal string, with invalid UTF-8 +/// presented as hex escape sequences. +/// +/// The `Display` implementation behaves as if the `ByteStr` were first lossily converted to a +/// `str`, with invalid UTF-8 presented as the Unicode replacement character (�). +#[unstable(feature = "bstr", issue = "134915")] +#[repr(transparent)] +#[doc(alias = "BStr")] +pub struct ByteStr(pub [u8]); + +impl ByteStr { + /// Creates a `ByteStr` slice from anything that can be converted to a byte slice. + /// + /// This is a zero-cost conversion. + /// + /// # Example + /// + /// You can create a `ByteStr` from a byte array, a byte slice or a string slice: + /// + /// ``` + /// # #![feature(bstr)] + /// # use std::bstr::ByteStr; + /// let a = ByteStr::new(b"abc"); + /// let b = ByteStr::new(&b"abc"[..]); + /// let c = ByteStr::new("abc"); + /// + /// assert_eq!(a, b); + /// assert_eq!(a, c); + /// ``` + #[inline] + #[unstable(feature = "bstr", issue = "134915")] + pub fn new>(bytes: &B) -> &Self { + ByteStr::from_bytes(bytes.as_ref()) + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn from_bytes(slice: &[u8]) -> &Self { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to + // the wrapped type into a reference to the wrapper type. + unsafe { &*(slice as *const [u8] as *const Self) } + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn from_bytes_mut(slice: &mut [u8]) -> &mut Self { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to + // the wrapped type into a reference to the wrapper type. + unsafe { &mut *(slice as *mut [u8] as *mut Self) } + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Deref for ByteStr { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl DerefMut for ByteStr { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for ByteStr {} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Debug for ByteStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\"")?; + for chunk in self.utf8_chunks() { + for c in chunk.valid().chars() { + match c { + '\0' => write!(f, "\\0")?, + '\x01'..='\x7f' => write!(f, "{}", (c as u8).escape_ascii())?, + _ => write!(f, "{}", c.escape_debug())?, + } + } + write!(f, "{}", chunk.invalid().escape_ascii())?; + } + write!(f, "\"")?; + Ok(()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Display for ByteStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt_nopad(this: &ByteStr, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for chunk in this.utf8_chunks() { + f.write_str(chunk.valid())?; + if !chunk.invalid().is_empty() { + f.write_str("\u{FFFD}")?; + } + } + Ok(()) + } + + let Some(align) = f.align() else { + return fmt_nopad(self, f); + }; + let nchars: usize = self + .utf8_chunks() + .map(|chunk| { + chunk.valid().chars().count() + if chunk.invalid().is_empty() { 0 } else { 1 } + }) + .sum(); + let padding = f.width().unwrap_or(0).saturating_sub(nchars); + let fill = f.fill(); + let (lpad, rpad) = match align { + fmt::Alignment::Left => (0, padding), + fmt::Alignment::Right => (padding, 0), + fmt::Alignment::Center => { + let half = padding / 2; + (half, half + padding % 2) + } + }; + for _ in 0..lpad { + write!(f, "{fill}")?; + } + fmt_nopad(self, f)?; + for _ in 0..rpad { + write!(f, "{fill}")?; + } + + Ok(()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef<[u8]> for ByteStr { + #[inline] + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef for ByteStr { + #[inline] + fn as_ref(&self) -> &ByteStr { + self + } +} + +// `impl AsRef for [u8]` omitted to avoid widespread inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef for str { + #[inline] + fn as_ref(&self) -> &ByteStr { + ByteStr::new(self) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsMut<[u8]> for ByteStr { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +// `impl AsMut for [u8]` omitted to avoid widespread inference failures + +// `impl Borrow for [u8]` omitted to avoid widespread inference failures + +// `impl Borrow for str` omitted to avoid widespread inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl Borrow<[u8]> for ByteStr { + #[inline] + fn borrow(&self) -> &[u8] { + &self.0 + } +} + +// `impl BorrowMut for [u8]` omitted to avoid widespread inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl BorrowMut<[u8]> for ByteStr { + #[inline] + fn borrow_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> Default for &'a ByteStr { + fn default() -> Self { + ByteStr::from_bytes(b"") + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> Default for &'a mut ByteStr { + fn default() -> Self { + ByteStr::from_bytes_mut(&mut []) + } +} + +// Omitted due to inference failures +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a, const N: usize> From<&'a [u8; N]> for &'a ByteStr { +// #[inline] +// fn from(s: &'a [u8; N]) -> Self { +// ByteStr::from_bytes(s) +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a [u8]> for &'a ByteStr { +// #[inline] +// fn from(s: &'a [u8]) -> Self { +// ByteStr::from_bytes(s) +// } +// } + +// Omitted due to slice-from-array-issue-113238: +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a ByteStr> for &'a [u8] { +// #[inline] +// fn from(s: &'a ByteStr) -> Self { +// &s.0 +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a mut ByteStr> for &'a mut [u8] { +// #[inline] +// fn from(s: &'a mut ByteStr) -> Self { +// &mut s.0 +// } +// } + +// Omitted due to inference failures +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a str> for &'a ByteStr { +// #[inline] +// fn from(s: &'a str) -> Self { +// ByteStr::from_bytes(s.as_bytes()) +// } +// } + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a ByteStr> for &'a str { + type Error = crate::str::Utf8Error; + + #[inline] + fn try_from(s: &'a ByteStr) -> Result { + crate::str::from_utf8(&s.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a mut ByteStr> for &'a mut str { + type Error = crate::str::Utf8Error; + + #[inline] + fn try_from(s: &'a mut ByteStr) -> Result { + crate::str::from_utf8_mut(&mut s.0) + } +} diff --git a/library/core/src/bstr/traits.rs b/library/core/src/bstr/traits.rs new file mode 100644 index 0000000000000..ff46bb13ba4eb --- /dev/null +++ b/library/core/src/bstr/traits.rs @@ -0,0 +1,277 @@ +//! Trait implementations for `ByteStr`. + +use crate::bstr::ByteStr; +use crate::cmp::Ordering; +use crate::slice::SliceIndex; +use crate::{hash, ops, range}; + +#[unstable(feature = "bstr", issue = "134915")] +impl Ord for ByteStr { + #[inline] + fn cmp(&self, other: &ByteStr) -> Ordering { + Ord::cmp(&self.0, &other.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialOrd for ByteStr { + #[inline] + fn partial_cmp(&self, other: &ByteStr) -> Option { + PartialOrd::partial_cmp(&self.0, &other.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialEq for ByteStr { + #[inline] + fn eq(&self, other: &ByteStr) -> bool { + &self.0 == &other.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Eq for ByteStr {} + +#[unstable(feature = "bstr", issue = "134915")] +impl hash::Hash for ByteStr { + #[inline] + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "bstr_internals", issue = "none")] +macro_rules! impl_partial_eq { + ($lhs:ty, $rhs:ty) => { + #[allow(unused_lifetimes)] + impl<'a> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let other: &[u8] = other.as_ref(); + PartialEq::eq(self.as_bytes(), other) + } + } + + #[allow(unused_lifetimes)] + impl<'a> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let this: &[u8] = self.as_ref(); + PartialEq::eq(this, other.as_bytes()) + } + } + }; +} + +#[doc(hidden)] +#[unstable(feature = "bstr_internals", issue = "none")] +pub use impl_partial_eq; + +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "bstr_internals", issue = "none")] +macro_rules! impl_partial_eq_ord { + ($lhs:ty, $rhs:ty) => { + $crate::bstr::impl_partial_eq!($lhs, $rhs); + + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + let other: &[u8] = other.as_ref(); + PartialOrd::partial_cmp(self.as_bytes(), other) + } + } + + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + let this: &[u8] = self.as_ref(); + PartialOrd::partial_cmp(this, other.as_bytes()) + } + } + }; +} + +#[doc(hidden)] +#[unstable(feature = "bstr_internals", issue = "none")] +pub use impl_partial_eq_ord; + +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "bstr_internals", issue = "none")] +macro_rules! impl_partial_eq_n { + ($lhs:ty, $rhs:ty) => { + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let other: &[u8] = other.as_ref(); + PartialEq::eq(self.as_bytes(), other) + } + } + + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let this: &[u8] = self.as_ref(); + PartialEq::eq(this, other.as_bytes()) + } + } + }; +} + +#[doc(hidden)] +#[unstable(feature = "bstr_internals", issue = "none")] +pub use impl_partial_eq_n; + +// PartialOrd with `[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteStr, [u8]); +// PartialOrd with `&[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteStr, &[u8]); +// PartialOrd with `str` omitted to avoid inference failures +impl_partial_eq!(ByteStr, str); +// PartialOrd with `&str` omitted to avoid inference failures +impl_partial_eq!(ByteStr, &str); +// PartialOrd with `[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteStr, [u8; N]); +// PartialOrd with `[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteStr, &[u8; N]); + +#[unstable(feature = "bstr", issue = "134915")] +impl ops::Index for ByteStr +where + I: SliceIndex, +{ + type Output = I::Output; + + #[inline] + fn index(&self, index: I) -> &I::Output { + index.index(self) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl ops::IndexMut for ByteStr +where + I: SliceIndex, +{ + #[inline] + fn index_mut(&mut self, index: I) -> &mut I::Output { + index.index_mut(self) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +unsafe impl SliceIndex for ops::RangeFull { + type Output = ByteStr; + #[inline] + fn get(self, slice: &ByteStr) -> Option<&Self::Output> { + Some(slice) + } + #[inline] + fn get_mut(self, slice: &mut ByteStr) -> Option<&mut Self::Output> { + Some(slice) + } + #[inline] + unsafe fn get_unchecked(self, slice: *const ByteStr) -> *const Self::Output { + slice + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut ByteStr) -> *mut Self::Output { + slice + } + #[inline] + fn index(self, slice: &ByteStr) -> &Self::Output { + slice + } + #[inline] + fn index_mut(self, slice: &mut ByteStr) -> &mut Self::Output { + slice + } +} + +#[unstable(feature = "bstr", issue = "134915")] +unsafe impl SliceIndex for usize { + type Output = u8; + #[inline] + fn get(self, slice: &ByteStr) -> Option<&Self::Output> { + self.get(slice.as_bytes()) + } + #[inline] + fn get_mut(self, slice: &mut ByteStr) -> Option<&mut Self::Output> { + self.get_mut(slice.as_bytes_mut()) + } + #[inline] + unsafe fn get_unchecked(self, slice: *const ByteStr) -> *const Self::Output { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { self.get_unchecked(slice as *const [u8]) } + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut ByteStr) -> *mut Self::Output { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { self.get_unchecked_mut(slice as *mut [u8]) } + } + #[inline] + fn index(self, slice: &ByteStr) -> &Self::Output { + self.index(slice.as_bytes()) + } + #[inline] + fn index_mut(self, slice: &mut ByteStr) -> &mut Self::Output { + self.index_mut(slice.as_bytes_mut()) + } +} + +macro_rules! impl_slice_index { + ($index:ty) => { + #[unstable(feature = "bstr", issue = "134915")] + unsafe impl SliceIndex for $index { + type Output = ByteStr; + #[inline] + fn get(self, slice: &ByteStr) -> Option<&Self::Output> { + self.get(slice.as_bytes()).map(ByteStr::from_bytes) + } + #[inline] + fn get_mut(self, slice: &mut ByteStr) -> Option<&mut Self::Output> { + self.get_mut(slice.as_bytes_mut()).map(ByteStr::from_bytes_mut) + } + #[inline] + unsafe fn get_unchecked(self, slice: *const ByteStr) -> *const Self::Output { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { self.get_unchecked(slice as *const [u8]) as *const ByteStr } + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut ByteStr) -> *mut Self::Output { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { self.get_unchecked_mut(slice as *mut [u8]) as *mut ByteStr } + } + #[inline] + fn index(self, slice: &ByteStr) -> &Self::Output { + ByteStr::from_bytes(self.index(slice.as_bytes())) + } + #[inline] + fn index_mut(self, slice: &mut ByteStr) -> &mut Self::Output { + ByteStr::from_bytes_mut(self.index_mut(slice.as_bytes_mut())) + } + } + }; +} + +impl_slice_index!(ops::IndexRange); +impl_slice_index!(ops::Range); +impl_slice_index!(range::Range); +impl_slice_index!(ops::RangeTo); +impl_slice_index!(ops::RangeFrom); +impl_slice_index!(range::RangeFrom); +impl_slice_index!(ops::RangeInclusive); +impl_slice_index!(range::RangeInclusive); +impl_slice_index!(ops::RangeToInclusive); +impl_slice_index!((ops::Bound, ops::Bound)); diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index cbf00106c5173..ed523920e42b5 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -304,7 +304,7 @@ pub use once::OnceCell; /// ``` /// /// See the [module-level documentation](self) for more. -#[cfg_attr(not(test), rustc_diagnostic_item = "Cell")] +#[rustc_diagnostic_item = "Cell"] #[stable(feature = "rust1", since = "1.0.0")] #[repr(transparent)] #[rustc_pub_transparent] @@ -495,7 +495,7 @@ impl Cell { /// ``` #[inline] #[stable(feature = "move_cell", since = "1.17.0")] - #[rustc_const_unstable(feature = "const_cell", issue = "131283")] + #[rustc_const_stable(feature = "const_cell", since = "1.88.0")] #[rustc_confusables("swap")] pub const fn replace(&self, val: T) -> T { // SAFETY: This can cause data races if called from a separate thread, @@ -537,38 +537,29 @@ impl Cell { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_cell", issue = "131283")] + #[rustc_const_stable(feature = "const_cell", since = "1.88.0")] pub const fn get(&self) -> T { // SAFETY: This can cause data races if called from a separate thread, // but `Cell` is `!Sync` so this won't happen. unsafe { *self.value.get() } } - /// Updates the contained value using a function and returns the new value. + /// Updates the contained value using a function. /// /// # Examples /// /// ``` - /// #![feature(cell_update)] - /// /// use std::cell::Cell; /// /// let c = Cell::new(5); - /// let new = c.update(|x| x + 1); - /// - /// assert_eq!(new, 6); + /// c.update(|x| x + 1); /// assert_eq!(c.get(), 6); /// ``` #[inline] - #[unstable(feature = "cell_update", issue = "50186")] - pub fn update(&self, f: F) -> T - where - F: FnOnce(T) -> T, - { + #[stable(feature = "cell_update", since = "1.88.0")] + pub fn update(&self, f: impl FnOnce(T) -> T) { let old = self.get(); - let new = f(old); - self.set(new); - new + self.set(f(old)); } } @@ -617,7 +608,7 @@ impl Cell { /// ``` #[inline] #[stable(feature = "cell_get_mut", since = "1.11.0")] - #[rustc_const_unstable(feature = "const_cell", issue = "131283")] + #[rustc_const_stable(feature = "const_cell", since = "1.88.0")] pub const fn get_mut(&mut self) -> &mut T { self.value.get_mut() } @@ -637,7 +628,7 @@ impl Cell { /// ``` #[inline] #[stable(feature = "as_cell", since = "1.37.0")] - #[rustc_const_unstable(feature = "const_cell", issue = "131283")] + #[rustc_const_stable(feature = "const_cell", since = "1.88.0")] pub const fn from_mut(t: &mut T) -> &Cell { // SAFETY: `&mut` ensures unique access. unsafe { &*(t as *mut T as *const Cell) } @@ -695,7 +686,7 @@ impl Cell<[T]> { /// assert_eq!(slice_cell.len(), 3); /// ``` #[stable(feature = "as_cell", since = "1.37.0")] - #[rustc_const_unstable(feature = "const_cell", issue = "131283")] + #[rustc_const_stable(feature = "const_cell", since = "1.88.0")] pub const fn as_slice_of_cells(&self) -> &[Cell] { // SAFETY: `Cell` has the same memory layout as `T`. unsafe { &*(self as *const Cell<[T]> as *const [Cell]) } @@ -725,7 +716,7 @@ impl Cell<[T; N]> { /// A mutable memory location with dynamically checked borrow rules /// /// See the [module-level documentation](self) for more. -#[cfg_attr(not(test), rustc_diagnostic_item = "RefCell")] +#[rustc_diagnostic_item = "RefCell"] #[stable(feature = "rust1", since = "1.0.0")] pub struct RefCell { borrow: Cell, @@ -1163,7 +1154,9 @@ impl RefCell { /// Since this method borrows `RefCell` mutably, it is statically guaranteed /// that no borrows to the underlying data exist. The dynamic checks inherent /// in [`borrow_mut`] and most other methods of `RefCell` are therefore - /// unnecessary. + /// unnecessary. Note that this method does not reset the borrowing state if borrows were previously leaked + /// (e.g., via [`forget()`] on a [`Ref`] or [`RefMut`]). For that purpose, + /// consider using the unstable [`undo_leak`] method. /// /// This method can only be called if `RefCell` can be mutably borrowed, /// which in general is only the case directly after the `RefCell` has @@ -1174,6 +1167,8 @@ impl RefCell { /// Use [`borrow_mut`] to get mutable access to the underlying data then. /// /// [`borrow_mut`]: RefCell::borrow_mut() + /// [`forget()`]: mem::forget + /// [`undo_leak`]: RefCell::undo_leak() /// /// # Examples /// diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs index 84cbbc71f40ae..0b2a2ce7ded04 100644 --- a/library/core/src/cell/lazy.rs +++ b/library/core/src/cell/lazy.rs @@ -1,6 +1,6 @@ use super::UnsafeCell; use crate::hint::unreachable_unchecked; -use crate::ops::Deref; +use crate::ops::{Deref, DerefMut}; use crate::{fmt, mem}; enum State { @@ -284,6 +284,14 @@ impl T> Deref for LazyCell { } } +#[stable(feature = "lazy_deref_mut", since = "CURRENT_RUSTC_VERSION")] +impl T> DerefMut for LazyCell { + #[inline] + fn deref_mut(&mut self) -> &mut T { + LazyCell::force_mut(self) + } +} + #[stable(feature = "lazy_cell", since = "1.80.0")] impl Default for LazyCell { /// Creates a new lazy value using `Default` as the initializing function. diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 73ab4f1e52ade..fd17f92f7be08 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -21,6 +21,7 @@ pub(super) const fn from_u32(i: u32) -> Option { /// Converts a `u32` to a `char`, ignoring validity. See [`char::from_u32_unchecked`]. #[inline] #[must_use] +#[allow(unnecessary_transmutes)] pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { // SAFETY: the caller must guarantee that `i` is a valid char value. unsafe { @@ -40,11 +41,9 @@ impl From for u32 { /// # Examples /// /// ``` - /// use std::mem; - /// /// let c = 'c'; /// let u = u32::from(c); - /// assert!(4 == mem::size_of_val(&u)) + /// assert!(4 == size_of_val(&u)) /// ``` #[inline] fn from(c: char) -> Self { @@ -59,11 +58,9 @@ impl From for u64 { /// # Examples /// /// ``` - /// use std::mem; - /// /// let c = '👤'; /// let u = u64::from(c); - /// assert!(8 == mem::size_of_val(&u)) + /// assert!(8 == size_of_val(&u)) /// ``` #[inline] fn from(c: char) -> Self { @@ -80,11 +77,9 @@ impl From for u128 { /// # Examples /// /// ``` - /// use std::mem; - /// /// let c = '⚙'; /// let u = u128::from(c); - /// assert!(16 == mem::size_of_val(&u)) + /// assert!(16 == size_of_val(&u)) /// ``` #[inline] fn from(c: char) -> Self { @@ -167,11 +162,9 @@ impl From for char { /// # Examples /// /// ``` - /// use std::mem; - /// /// let u = 32 as u8; /// let c = char::from(u); - /// assert!(4 == mem::size_of_val(&c)) + /// assert!(4 == size_of_val(&c)) /// ``` #[inline] fn from(i: u8) -> Self { @@ -229,6 +222,7 @@ impl FromStr for char { } #[inline] +#[allow(unnecessary_transmutes)] const fn char_try_from_u32(i: u32) -> Result { // This is an optimized version of the check // (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF), diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 85cc315626d4b..af2edf141b22f 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -4,6 +4,7 @@ use super::*; use crate::panic::const_panic; use crate::slice; use crate::str::from_utf8_unchecked_mut; +use crate::ub_checks::assert_unsafe_precondition; use crate::unicode::printable::is_printable; use crate::unicode::{self, conversions}; @@ -337,7 +338,7 @@ impl char { /// '1'.is_digit(1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_char_classify", issue = "132241")] + #[rustc_const_stable(feature = "const_char_classify", since = "1.87.0")] #[inline] pub const fn is_digit(self, radix: u32) -> bool { self.to_digit(radix).is_some() @@ -886,7 +887,7 @@ impl char { /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_char_classify", issue = "132241")] + #[rustc_const_stable(feature = "const_char_classify", since = "1.87.0")] #[inline] pub const fn is_whitespace(self) -> bool { match self { @@ -1178,7 +1179,7 @@ impl char { #[must_use] #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[rustc_const_stable(feature = "const_char_is_ascii", since = "1.32.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "char_is_ascii")] + #[rustc_diagnostic_item = "char_is_ascii"] #[inline] pub const fn is_ascii(&self) -> bool { *self as u32 <= 0x7F @@ -1202,6 +1203,26 @@ impl char { } } + /// Converts this char into an [ASCII character](`ascii::Char`), without + /// checking whether it is valid. + /// + /// # Safety + /// + /// This char must be within the ASCII range, or else this is UB. + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const unsafe fn as_ascii_unchecked(&self) -> ascii::Char { + assert_unsafe_precondition!( + check_library_ub, + "as_ascii_unchecked requires that the char is valid ASCII", + (it: &char = self) => it.is_ascii() + ); + + // SAFETY: the caller promised that this char is ASCII. + unsafe { ascii::Char::from_u8_unchecked(*self as u8) } + } + /// Makes a copy of the value in its ASCII upper case equivalent. /// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', @@ -1806,39 +1827,71 @@ const fn len_utf16(code: u32) -> usize { #[inline] pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { let len = len_utf8(code); - match (len, &mut *dst) { - (1, [a, ..]) => { - *a = code as u8; - } - (2, [a, b, ..]) => { - *a = (code >> 6 & 0x1F) as u8 | TAG_TWO_B; - *b = (code & 0x3F) as u8 | TAG_CONT; - } - (3, [a, b, c, ..]) => { - *a = (code >> 12 & 0x0F) as u8 | TAG_THREE_B; - *b = (code >> 6 & 0x3F) as u8 | TAG_CONT; - *c = (code & 0x3F) as u8 | TAG_CONT; - } - (4, [a, b, c, d, ..]) => { - *a = (code >> 18 & 0x07) as u8 | TAG_FOUR_B; - *b = (code >> 12 & 0x3F) as u8 | TAG_CONT; - *c = (code >> 6 & 0x3F) as u8 | TAG_CONT; - *d = (code & 0x3F) as u8 | TAG_CONT; - } - _ => { - const_panic!( - "encode_utf8: buffer does not have enough bytes to encode code point", - "encode_utf8: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}", - code: u32 = code, - len: usize = len, - dst_len: usize = dst.len(), - ) - } - }; + if dst.len() < len { + const_panic!( + "encode_utf8: buffer does not have enough bytes to encode code point", + "encode_utf8: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}", + code: u32 = code, + len: usize = len, + dst_len: usize = dst.len(), + ); + } + + // SAFETY: `dst` is checked to be at least the length needed to encode the codepoint. + unsafe { encode_utf8_raw_unchecked(code, dst.as_mut_ptr()) }; + // SAFETY: `<&mut [u8]>::as_mut_ptr` is guaranteed to return a valid pointer and `len` has been tested to be within bounds. unsafe { slice::from_raw_parts_mut(dst.as_mut_ptr(), len) } } +/// Encodes a raw `u32` value as UTF-8 into the byte buffer pointed to by `dst`. +/// +/// Unlike `char::encode_utf8`, this method also handles codepoints in the surrogate range. +/// (Creating a `char` in the surrogate range is UB.) +/// The result is valid [generalized UTF-8] but not valid UTF-8. +/// +/// [generalized UTF-8]: https://simonsapin.github.io/wtf-8/#generalized-utf8 +/// +/// # Safety +/// +/// The behavior is undefined if the buffer pointed to by `dst` is not +/// large enough to hold the encoded codepoint. A buffer of length four +/// is large enough to encode any `char`. +/// +/// For a safe version of this function, see the [`encode_utf8_raw`] function. +#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] +#[doc(hidden)] +#[inline] +pub const unsafe fn encode_utf8_raw_unchecked(code: u32, dst: *mut u8) { + let len = len_utf8(code); + // SAFETY: The caller must guarantee that the buffer pointed to by `dst` + // is at least `len` bytes long. + unsafe { + match len { + 1 => { + *dst = code as u8; + } + 2 => { + *dst = (code >> 6 & 0x1F) as u8 | TAG_TWO_B; + *dst.add(1) = (code & 0x3F) as u8 | TAG_CONT; + } + 3 => { + *dst = (code >> 12 & 0x0F) as u8 | TAG_THREE_B; + *dst.add(1) = (code >> 6 & 0x3F) as u8 | TAG_CONT; + *dst.add(2) = (code & 0x3F) as u8 | TAG_CONT; + } + 4 => { + *dst = (code >> 18 & 0x07) as u8 | TAG_FOUR_B; + *dst.add(1) = (code >> 12 & 0x3F) as u8 | TAG_CONT; + *dst.add(2) = (code >> 6 & 0x3F) as u8 | TAG_CONT; + *dst.add(3) = (code & 0x3F) as u8 | TAG_CONT; + } + // SAFETY: `char` always takes between 1 and 4 bytes to encode in UTF-8. + _ => crate::hint::unreachable_unchecked(), + } + } +} + /// Encodes a raw `u32` value as native endian UTF-16 into the provided `u16` buffer, /// and then returns the subslice of the buffer that contains the encoded character. /// diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 088c709f1a2af..5b9f0e2143f5d 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -38,7 +38,7 @@ pub use self::decode::{DecodeUtf16, DecodeUtf16Error}; #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] pub use self::methods::encode_utf16_raw; // perma-unstable #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::methods::encode_utf8_raw; // perma-unstable +pub use self::methods::{encode_utf8_raw, encode_utf8_raw_unchecked}; // perma-unstable #[rustfmt::skip] use crate::ascii; diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 00300328b64c1..c237ac84cf407 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -184,6 +184,59 @@ pub macro Clone($item:item) { /* compiler built-in */ } +/// Trait for objects whose [`Clone`] impl is lightweight (e.g. reference-counted) +/// +/// Cloning an object implementing this trait should in general: +/// - be O(1) (constant) time regardless of the amount of data managed by the object, +/// - not require a memory allocation, +/// - not require copying more than roughly 64 bytes (a typical cache line size), +/// - not block the current thread, +/// - not have any semantic side effects (e.g. allocating a file descriptor), and +/// - not have overhead larger than a couple of atomic operations. +/// +/// The `UseCloned` trait does not provide a method; instead, it indicates that +/// `Clone::clone` is lightweight, and allows the use of the `.use` syntax. +/// +/// ## .use postfix syntax +/// +/// Values can be `.use`d by adding `.use` postfix to the value you want to use. +/// +/// ```ignore (this won't work until we land use) +/// fn foo(f: Foo) { +/// // if `Foo` implements `Copy` f would be copied into x. +/// // if `Foo` implements `UseCloned` f would be cloned into x. +/// // otherwise f would be moved into x. +/// let x = f.use; +/// // ... +/// } +/// ``` +/// +/// ## use closures +/// +/// Use closures allow captured values to be automatically used. +/// This is similar to have a closure that you would call `.use` over each captured value. +#[unstable(feature = "ergonomic_clones", issue = "132290")] +#[lang = "use_cloned"] +pub trait UseCloned: Clone { + // Empty. +} + +macro_rules! impl_use_cloned { + ($($t:ty)*) => { + $( + #[unstable(feature = "ergonomic_clones", issue = "132290")] + impl UseCloned for $t {} + )* + } +} + +impl_use_cloned! { + usize u8 u16 u32 u64 u128 + isize i8 i16 i32 i64 i128 + f16 f32 f64 f128 + bool char +} + // FIXME(aburka): these structs are used solely by #[derive] to // assert that every component of a type implements Clone or Copy. // @@ -209,34 +262,150 @@ pub struct AssertParamIsCopy { _field: crate::marker::PhantomData, } -/// A generalization of [`Clone`] to dynamically-sized types stored in arbitrary containers. +/// A generalization of [`Clone`] to [dynamically-sized types][DST] stored in arbitrary containers. +/// +/// This trait is implemented for all types implementing [`Clone`], [slices](slice) of all +/// such types, and other dynamically-sized types in the standard library. +/// You may also implement this trait to enable cloning custom DSTs +/// (structures containing dynamically-sized fields), or use it as a supertrait to enable +/// cloning a [trait object]. /// -/// This trait is implemented for all types implementing [`Clone`], and also [slices](slice) of all -/// such types. You may also implement this trait to enable cloning trait objects and custom DSTs -/// (structures containing dynamically-sized fields). +/// This trait is normally used via operations on container types which support DSTs, +/// so you should not typically need to call `.clone_to_uninit()` explicitly except when +/// implementing such a container or otherwise performing explicit management of an allocation, +/// or when implementing `CloneToUninit` itself. /// /// # Safety /// -/// Implementations must ensure that when `.clone_to_uninit(dst)` returns normally rather than -/// panicking, it always leaves `*dst` initialized as a valid value of type `Self`. +/// Implementations must ensure that when `.clone_to_uninit(dest)` returns normally rather than +/// panicking, it always leaves `*dest` initialized as a valid value of type `Self`. /// -/// # See also +/// # Examples +/// +// FIXME(#126799): when `Box::clone` allows use of `CloneToUninit`, rewrite these examples with it +// since `Rc` is a distraction. +/// +/// If you are defining a trait, you can add `CloneToUninit` as a supertrait to enable cloning of +/// `dyn` values of your trait: +/// +/// ``` +/// #![feature(clone_to_uninit)] +/// use std::rc::Rc; +/// +/// trait Foo: std::fmt::Debug + std::clone::CloneToUninit { +/// fn modify(&mut self); +/// fn value(&self) -> i32; +/// } +/// +/// impl Foo for i32 { +/// fn modify(&mut self) { +/// *self *= 10; +/// } +/// fn value(&self) -> i32 { +/// *self +/// } +/// } /// -/// * [`Clone::clone_from`] is a safe function which may be used instead when `Self` is a [`Sized`] +/// let first: Rc = Rc::new(1234); +/// +/// let mut second = first.clone(); +/// Rc::make_mut(&mut second).modify(); // make_mut() will call clone_to_uninit() +/// +/// assert_eq!(first.value(), 1234); +/// assert_eq!(second.value(), 12340); +/// ``` +/// +/// The following is an example of implementing `CloneToUninit` for a custom DST. +/// (It is essentially a limited form of what `derive(CloneToUninit)` would do, +/// if such a derive macro existed.) +/// +/// ``` +/// #![feature(clone_to_uninit)] +/// use std::clone::CloneToUninit; +/// use std::mem::offset_of; +/// use std::rc::Rc; +/// +/// #[derive(PartialEq)] +/// struct MyDst { +/// label: String, +/// contents: T, +/// } +/// +/// unsafe impl CloneToUninit for MyDst { +/// unsafe fn clone_to_uninit(&self, dest: *mut u8) { +/// // The offset of `self.contents` is dynamic because it depends on the alignment of T +/// // which can be dynamic (if `T = dyn SomeTrait`). Therefore, we have to obtain it +/// // dynamically by examining `self`, rather than using `offset_of!`. +/// // +/// // SAFETY: `self` by definition points somewhere before `&self.contents` in the same +/// // allocation. +/// let offset_of_contents = unsafe { +/// (&raw const self.contents).byte_offset_from_unsigned(self) +/// }; +/// +/// // Clone the *sized* fields of `self` (just one, in this example). +/// // (By cloning this first and storing it temporarily in a local variable, we avoid +/// // leaking it in case of any panic, using the ordinary automatic cleanup of local +/// // variables. Such a leak would be sound, but undesirable.) +/// let label = self.label.clone(); +/// +/// // SAFETY: The caller must provide a `dest` such that these field offsets are valid +/// // to write to. +/// unsafe { +/// // Clone the unsized field directly from `self` to `dest`. +/// self.contents.clone_to_uninit(dest.add(offset_of_contents)); +/// +/// // Now write all the sized fields. +/// // +/// // Note that we only do this once all of the clone() and clone_to_uninit() calls +/// // have completed, and therefore we know that there are no more possible panics; +/// // this ensures no memory leaks in case of panic. +/// dest.add(offset_of!(Self, label)).cast::().write(label); +/// } +/// // All fields of the struct have been initialized; therefore, the struct is initialized, +/// // and we have satisfied our `unsafe impl CloneToUninit` obligations. +/// } +/// } +/// +/// fn main() { +/// // Construct MyDst<[u8; 4]>, then coerce to MyDst<[u8]>. +/// let first: Rc> = Rc::new(MyDst { +/// label: String::from("hello"), +/// contents: [1, 2, 3, 4], +/// }); +/// +/// let mut second = first.clone(); +/// // make_mut() will call clone_to_uninit(). +/// for elem in Rc::make_mut(&mut second).contents.iter_mut() { +/// *elem *= 10; +/// } +/// +/// assert_eq!(first.contents, [1, 2, 3, 4]); +/// assert_eq!(second.contents, [10, 20, 30, 40]); +/// assert_eq!(second.label, "hello"); +/// } +/// ``` +/// +/// # See Also +/// +/// * [`Clone::clone_from`] is a safe function which may be used instead when [`Self: Sized`](Sized) /// and the destination is already initialized; it may be able to reuse allocations owned by -/// the destination. +/// the destination, whereas `clone_to_uninit` cannot, since its destination is assumed to be +/// uninitialized. /// * [`ToOwned`], which allocates a new destination container. /// /// [`ToOwned`]: ../../std/borrow/trait.ToOwned.html +/// [DST]: https://doc.rust-lang.org/reference/dynamically-sized-types.html +/// [trait object]: https://doc.rust-lang.org/reference/types/trait-object.html #[unstable(feature = "clone_to_uninit", issue = "126799")] pub unsafe trait CloneToUninit { - /// Performs copy-assignment from `self` to `dst`. + /// Performs copy-assignment from `self` to `dest`. /// - /// This is analogous to `std::ptr::write(dst.cast(), self.clone())`, - /// except that `self` may be a dynamically-sized type ([`!Sized`](Sized)). + /// This is analogous to `std::ptr::write(dest.cast(), self.clone())`, + /// except that `Self` may be a dynamically-sized type ([`!Sized`](Sized)). /// - /// Before this function is called, `dst` may point to uninitialized memory. - /// After this function is called, `dst` will point to initialized memory; it will be + /// Before this function is called, `dest` may point to uninitialized memory. + /// After this function is called, `dest` will point to initialized memory; it will be /// sound to create a `&Self` reference from the pointer with the [pointer metadata] /// from `self`. /// @@ -244,8 +413,8 @@ pub unsafe trait CloneToUninit { /// /// Behavior is undefined if any of the following conditions are violated: /// - /// * `dst` must be [valid] for writes for `std::mem::size_of_val(self)` bytes. - /// * `dst` must be properly aligned to `std::mem::align_of_val(self)`. + /// * `dest` must be [valid] for writes for `size_of_val(self)` bytes. + /// * `dest` must be properly aligned to `align_of_val(self)`. /// /// [valid]: crate::ptr#safety /// [pointer metadata]: crate::ptr::metadata() @@ -254,27 +423,26 @@ pub unsafe trait CloneToUninit { /// /// This function may panic. (For example, it might panic if memory allocation for a clone /// of a value owned by `self` fails.) - /// If the call panics, then `*dst` should be treated as uninitialized memory; it must not be + /// If the call panics, then `*dest` should be treated as uninitialized memory; it must not be /// read or dropped, because even if it was previously valid, it may have been partially /// overwritten. /// - /// The caller may also need to take care to deallocate the allocation pointed to by `dst`, - /// if applicable, to avoid a memory leak, and may need to take other precautions to ensure - /// soundness in the presence of unwinding. + /// The caller may wish to take care to deallocate the allocation pointed to by `dest`, + /// if applicable, to avoid a memory leak (but this is not a requirement). /// /// Implementors should avoid leaking values by, upon unwinding, dropping all component values /// that might have already been created. (For example, if a `[Foo]` of length 3 is being /// cloned, and the second of the three calls to `Foo::clone()` unwinds, then the first `Foo` /// cloned should be dropped.) - unsafe fn clone_to_uninit(&self, dst: *mut u8); + unsafe fn clone_to_uninit(&self, dest: *mut u8); } #[unstable(feature = "clone_to_uninit", issue = "126799")] unsafe impl CloneToUninit for T { #[inline] - unsafe fn clone_to_uninit(&self, dst: *mut u8) { + unsafe fn clone_to_uninit(&self, dest: *mut u8) { // SAFETY: we're calling a specialization with the same contract - unsafe { ::clone_one(self, dst.cast::()) } + unsafe { ::clone_one(self, dest.cast::()) } } } @@ -282,10 +450,10 @@ unsafe impl CloneToUninit for T { unsafe impl CloneToUninit for [T] { #[inline] #[cfg_attr(debug_assertions, track_caller)] - unsafe fn clone_to_uninit(&self, dst: *mut u8) { - let dst: *mut [T] = dst.with_metadata_of(self); + unsafe fn clone_to_uninit(&self, dest: *mut u8) { + let dest: *mut [T] = dest.with_metadata_of(self); // SAFETY: we're calling a specialization with the same contract - unsafe { ::clone_slice(self, dst) } + unsafe { ::clone_slice(self, dest) } } } @@ -293,21 +461,21 @@ unsafe impl CloneToUninit for [T] { unsafe impl CloneToUninit for str { #[inline] #[cfg_attr(debug_assertions, track_caller)] - unsafe fn clone_to_uninit(&self, dst: *mut u8) { + unsafe fn clone_to_uninit(&self, dest: *mut u8) { // SAFETY: str is just a [u8] with UTF-8 invariant - unsafe { self.as_bytes().clone_to_uninit(dst) } + unsafe { self.as_bytes().clone_to_uninit(dest) } } } #[unstable(feature = "clone_to_uninit", issue = "126799")] unsafe impl CloneToUninit for crate::ffi::CStr { #[cfg_attr(debug_assertions, track_caller)] - unsafe fn clone_to_uninit(&self, dst: *mut u8) { + unsafe fn clone_to_uninit(&self, dest: *mut u8) { // SAFETY: For now, CStr is just a #[repr(trasnsparent)] [c_char] with some invariants. // And we can cast [c_char] to [u8] on all supported platforms (see: to_bytes_with_nul). // The pointer metadata properly preserves the length (so NUL is also copied). // See: `cstr_metadata_is_length_with_nul` in tests. - unsafe { self.to_bytes_with_nul().clone_to_uninit(dst) } + unsafe { self.to_bytes_with_nul().clone_to_uninit(dest) } } } diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index c8ced78c4d791..c315131f4136c 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -29,6 +29,7 @@ mod bytewise; pub(crate) use bytewise::BytewiseEq; use self::Ordering::*; +use crate::ops::ControlFlow; /// Trait for comparisons using the equality operator. /// @@ -397,6 +398,12 @@ pub enum Ordering { } impl Ordering { + #[inline] + const fn as_raw(self) -> i8 { + // FIXME(const-hack): just use `PartialOrd` against `Equal` once that's const + crate::intrinsics::discriminant_value(&self) + } + /// Returns `true` if the ordering is the `Equal` variant. /// /// # Examples @@ -413,7 +420,11 @@ impl Ordering { #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")] #[stable(feature = "ordering_helpers", since = "1.53.0")] pub const fn is_eq(self) -> bool { - matches!(self, Equal) + // All the `is_*` methods are implemented as comparisons against zero + // to follow how clang's libcxx implements their equivalents in + // + + self.as_raw() == 0 } /// Returns `true` if the ordering is not the `Equal` variant. @@ -432,7 +443,7 @@ impl Ordering { #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")] #[stable(feature = "ordering_helpers", since = "1.53.0")] pub const fn is_ne(self) -> bool { - !matches!(self, Equal) + self.as_raw() != 0 } /// Returns `true` if the ordering is the `Less` variant. @@ -451,7 +462,7 @@ impl Ordering { #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")] #[stable(feature = "ordering_helpers", since = "1.53.0")] pub const fn is_lt(self) -> bool { - matches!(self, Less) + self.as_raw() < 0 } /// Returns `true` if the ordering is the `Greater` variant. @@ -470,7 +481,7 @@ impl Ordering { #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")] #[stable(feature = "ordering_helpers", since = "1.53.0")] pub const fn is_gt(self) -> bool { - matches!(self, Greater) + self.as_raw() > 0 } /// Returns `true` if the ordering is either the `Less` or `Equal` variant. @@ -489,7 +500,7 @@ impl Ordering { #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")] #[stable(feature = "ordering_helpers", since = "1.53.0")] pub const fn is_le(self) -> bool { - !matches!(self, Greater) + self.as_raw() <= 0 } /// Returns `true` if the ordering is either the `Greater` or `Equal` variant. @@ -508,7 +519,7 @@ impl Ordering { #[rustc_const_stable(feature = "ordering_helpers", since = "1.53.0")] #[stable(feature = "ordering_helpers", since = "1.53.0")] pub const fn is_ge(self) -> bool { - !matches!(self, Less) + self.as_raw() >= 0 } /// Reverses the `Ordering`. @@ -1425,6 +1436,67 @@ pub trait PartialOrd: PartialEq { fn ge(&self, other: &Rhs) -> bool { self.partial_cmp(other).is_some_and(Ordering::is_ge) } + + /// If `self == other`, returns `ControlFlow::Continue(())`. + /// Otherwise, returns `ControlFlow::Break(self < other)`. + /// + /// This is useful for chaining together calls when implementing a lexical + /// `PartialOrd::lt`, as it allows types (like primitives) which can cheaply + /// check `==` and `<` separately to do rather than needing to calculate + /// (then optimize out) the three-way `Ordering` result. + #[inline] + #[must_use] + // Added to improve the behaviour of tuples; not necessarily stabilization-track. + #[unstable(feature = "partial_ord_chaining_methods", issue = "none")] + #[doc(hidden)] + fn __chaining_lt(&self, other: &Rhs) -> ControlFlow { + default_chaining_impl(self, other, Ordering::is_lt) + } + + /// Same as `__chaining_lt`, but for `<=` instead of `<`. + #[inline] + #[must_use] + #[unstable(feature = "partial_ord_chaining_methods", issue = "none")] + #[doc(hidden)] + fn __chaining_le(&self, other: &Rhs) -> ControlFlow { + default_chaining_impl(self, other, Ordering::is_le) + } + + /// Same as `__chaining_lt`, but for `>` instead of `<`. + #[inline] + #[must_use] + #[unstable(feature = "partial_ord_chaining_methods", issue = "none")] + #[doc(hidden)] + fn __chaining_gt(&self, other: &Rhs) -> ControlFlow { + default_chaining_impl(self, other, Ordering::is_gt) + } + + /// Same as `__chaining_lt`, but for `>=` instead of `<`. + #[inline] + #[must_use] + #[unstable(feature = "partial_ord_chaining_methods", issue = "none")] + #[doc(hidden)] + fn __chaining_ge(&self, other: &Rhs) -> ControlFlow { + default_chaining_impl(self, other, Ordering::is_ge) + } +} + +fn default_chaining_impl( + lhs: &T, + rhs: &U, + p: impl FnOnce(Ordering) -> bool, +) -> ControlFlow +where + T: PartialOrd, +{ + // It's important that this only call `partial_cmp` once, not call `eq` then + // one of the relational operators. We don't want to `bcmp`-then-`memcp` a + // `String`, for example, or similarly for other data structures (#108157). + match >::partial_cmp(lhs, rhs) { + Some(Equal) => ControlFlow::Continue(()), + Some(c) => ControlFlow::Break(p(c)), + None => ControlFlow::Break(false), + } } /// Derive macro generating an impl of the trait [`PartialOrd`]. @@ -1471,7 +1543,7 @@ pub macro PartialOrd($item:item) { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "cmp_min")] +#[rustc_diagnostic_item = "cmp_min"] pub fn min(v1: T, v2: T) -> T { v1.min(v2) } @@ -1563,7 +1635,7 @@ pub fn min_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "cmp_max")] +#[rustc_diagnostic_item = "cmp_max"] pub fn max(v1: T, v2: T) -> T { v1.max(v2) } @@ -1731,15 +1803,16 @@ where mod impls { use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::hint::unreachable_unchecked; + use crate::ops::ControlFlow::{self, Break, Continue}; macro_rules! partial_eq_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for $t { #[inline] - fn eq(&self, other: &$t) -> bool { (*self) == (*other) } + fn eq(&self, other: &Self) -> bool { *self == *other } #[inline] - fn ne(&self, other: &$t) -> bool { (*self) != (*other) } + fn ne(&self, other: &Self) -> bool { *self != *other } } )*) } @@ -1769,12 +1842,51 @@ mod impls { eq_impl! { () bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } + #[rustfmt::skip] + macro_rules! partial_ord_methods_primitive_impl { + () => { + #[inline(always)] + fn lt(&self, other: &Self) -> bool { *self < *other } + #[inline(always)] + fn le(&self, other: &Self) -> bool { *self <= *other } + #[inline(always)] + fn gt(&self, other: &Self) -> bool { *self > *other } + #[inline(always)] + fn ge(&self, other: &Self) -> bool { *self >= *other } + + // These implementations are the same for `Ord` or `PartialOrd` types + // because if either is NAN the `==` test will fail so we end up in + // the `Break` case and the comparison will correctly return `false`. + + #[inline] + fn __chaining_lt(&self, other: &Self) -> ControlFlow { + let (lhs, rhs) = (*self, *other); + if lhs == rhs { Continue(()) } else { Break(lhs < rhs) } + } + #[inline] + fn __chaining_le(&self, other: &Self) -> ControlFlow { + let (lhs, rhs) = (*self, *other); + if lhs == rhs { Continue(()) } else { Break(lhs <= rhs) } + } + #[inline] + fn __chaining_gt(&self, other: &Self) -> ControlFlow { + let (lhs, rhs) = (*self, *other); + if lhs == rhs { Continue(()) } else { Break(lhs > rhs) } + } + #[inline] + fn __chaining_ge(&self, other: &Self) -> ControlFlow { + let (lhs, rhs) = (*self, *other); + if lhs == rhs { Continue(()) } else { Break(lhs >= rhs) } + } + }; + } + macro_rules! partial_ord_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for $t { #[inline] - fn partial_cmp(&self, other: &$t) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { match (*self <= *other, *self >= *other) { (false, false) => None, (false, true) => Some(Greater), @@ -1782,14 +1894,8 @@ mod impls { (true, true) => Some(Equal), } } - #[inline(always)] - fn lt(&self, other: &$t) -> bool { (*self) < (*other) } - #[inline(always)] - fn le(&self, other: &$t) -> bool { (*self) <= (*other) } - #[inline(always)] - fn ge(&self, other: &$t) -> bool { (*self) >= (*other) } - #[inline(always)] - fn gt(&self, other: &$t) -> bool { (*self) > (*other) } + + partial_ord_methods_primitive_impl!(); } )*) } @@ -1808,6 +1914,8 @@ mod impls { fn partial_cmp(&self, other: &bool) -> Option { Some(self.cmp(other)) } + + partial_ord_methods_primitive_impl!(); } partial_ord_impl! { f16 f32 f64 f128 } @@ -1817,23 +1925,17 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for $t { #[inline] - fn partial_cmp(&self, other: &$t) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { Some(crate::intrinsics::three_way_compare(*self, *other)) } - #[inline(always)] - fn lt(&self, other: &$t) -> bool { (*self) < (*other) } - #[inline(always)] - fn le(&self, other: &$t) -> bool { (*self) <= (*other) } - #[inline(always)] - fn ge(&self, other: &$t) -> bool { (*self) >= (*other) } - #[inline(always)] - fn gt(&self, other: &$t) -> bool { (*self) > (*other) } + + partial_ord_methods_primitive_impl!(); } #[stable(feature = "rust1", since = "1.0.0")] impl Ord for $t { #[inline] - fn cmp(&self, other: &$t) -> Ordering { + fn cmp(&self, other: &Self) -> Ordering { crate::intrinsics::three_way_compare(*self, *other) } } @@ -1951,6 +2053,22 @@ mod impls { fn ge(&self, other: &&B) -> bool { PartialOrd::ge(*self, *other) } + #[inline] + fn __chaining_lt(&self, other: &&B) -> ControlFlow { + PartialOrd::__chaining_lt(*self, *other) + } + #[inline] + fn __chaining_le(&self, other: &&B) -> ControlFlow { + PartialOrd::__chaining_le(*self, *other) + } + #[inline] + fn __chaining_gt(&self, other: &&B) -> ControlFlow { + PartialOrd::__chaining_gt(*self, *other) + } + #[inline] + fn __chaining_ge(&self, other: &&B) -> ControlFlow { + PartialOrd::__chaining_ge(*self, *other) + } } #[stable(feature = "rust1", since = "1.0.0")] impl Ord for &A @@ -2006,6 +2124,22 @@ mod impls { fn ge(&self, other: &&mut B) -> bool { PartialOrd::ge(*self, *other) } + #[inline] + fn __chaining_lt(&self, other: &&mut B) -> ControlFlow { + PartialOrd::__chaining_lt(*self, *other) + } + #[inline] + fn __chaining_le(&self, other: &&mut B) -> ControlFlow { + PartialOrd::__chaining_le(*self, *other) + } + #[inline] + fn __chaining_gt(&self, other: &&mut B) -> ControlFlow { + PartialOrd::__chaining_gt(*self, *other) + } + #[inline] + fn __chaining_ge(&self, other: &&mut B) -> ControlFlow { + PartialOrd::__chaining_ge(*self, *other) + } } #[stable(feature = "rust1", since = "1.0.0")] impl Ord for &mut A diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs index 8b79a3a7eba86..495f84bce4bf2 100644 --- a/library/core/src/contracts.rs +++ b/library/core/src/contracts.rs @@ -2,19 +2,23 @@ pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires}; -/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }` -/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` -/// (including the implicit return of the tail expression, if any). +/// This is an identity function used as part of the desugaring of the `#[ensures]` attribute. +/// +/// This is an existing hack to allow users to omit the type of the return value in their ensures +/// attribute. +/// +/// Ideally, rustc should be able to generate the type annotation. +/// The existing lowering logic makes it rather hard to add the explicit type annotation, +/// while the function call is fairly straight forward. #[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +// Similar to `contract_check_requires`, we need to use the user-facing +// `contracts` feature rather than the perma-unstable `contracts_internals`. +// Const-checking doesn't honor allow_internal_unstable logic used by contract expansion. +#[rustc_const_unstable(feature = "contracts", issue = "128044")] #[lang = "contract_build_check_ensures"] -#[track_caller] -pub fn build_check_ensures(cond: C) -> impl (Fn(Ret) -> Ret) + Copy +pub const fn build_check_ensures(cond: C) -> C where - C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static, + C: Fn(&Ret) -> bool + Copy + 'static, { - #[track_caller] - move |ret| { - crate::intrinsics::contract_check_ensures(&ret, cond); - ret - } + cond } diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index e468f4f0f7e66..c542a28beb87c 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -214,7 +214,7 @@ pub const fn identity(x: T) -> T { /// is_hello(s); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "AsRef")] +#[rustc_diagnostic_item = "AsRef"] pub trait AsRef { /// Converts this type into a shared reference of the (usually inferred) input type. #[stable(feature = "rust1", since = "1.0.0")] @@ -365,7 +365,7 @@ pub trait AsRef { /// Note, however, that APIs don't need to be generic. In many cases taking a `&mut [u8]` or /// `&mut Vec`, for example, is the better choice (callers need to pass the correct type then). #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "AsMut")] +#[rustc_diagnostic_item = "AsMut"] pub trait AsMut { /// Converts this type into a mutable reference of the (usually inferred) input type. #[stable(feature = "rust1", since = "1.0.0")] @@ -464,8 +464,8 @@ pub trait Into: Sized { /// 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. +/// 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. /// /// The `From` trait 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`. @@ -597,6 +597,9 @@ pub trait From: Sized { /// standard library. For more information on this, see the /// documentation for [`Into`]. /// +/// Prefer using [`TryInto`] over [`TryFrom`] when specifying trait bounds on a generic function +/// to ensure that types that only implement [`TryInto`] can be used as well. +/// /// # Implementing `TryInto` /// /// This suffers the same restrictions and reasoning as implementing @@ -636,6 +639,9 @@ pub trait TryInto: Sized { /// When the [`!`] type is stabilized [`Infallible`] and [`!`] will be /// equivalent. /// +/// Prefer using [`TryInto`] over [`TryFrom`] when specifying trait bounds on a generic function +/// to ensure that types that only implement [`TryInto`] can be used as well. +/// /// `TryFrom` can be implemented as follows: /// /// ``` @@ -778,7 +784,6 @@ impl From for T { /// /// [#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 { diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 0246d0627cafe..d5cb10a5d1c8b 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -147,22 +147,42 @@ impl_from!(i16 => isize, #[stable(feature = "lossless_iusize_conv", since = "1.2 // https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-951.pdf // Note: integers can only be represented with full precision in a float if -// they fit in the significand, which is 24 bits in f32 and 53 bits in f64. +// they fit in the significand, which is: +// * 11 bits in f16 +// * 24 bits in f32 +// * 53 bits in f64 +// * 113 bits in f128 // Lossy float conversions are not implemented at this time. +// FIXME(f16_f128): The `f16`/`f128` impls `#[stable]` attributes should be changed to reference +// `f16`/`f128` when they are stabilised (trait impls have to have a `#[stable]` attribute, but none +// of the `f16`/`f128` impls can be used on stable as the `f16` and `f128` types are unstable). // signed integer -> float +impl_from!(i8 => f16, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(i8 => f32, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(i8 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); +impl_from!(i8 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(i16 => f32, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(i16 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); +impl_from!(i16 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(i32 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); +impl_from!(i32 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); +// FIXME(f16_f128): This impl would allow using `f128` on stable before it is stabilised. +// impl_from!(i64 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); // unsigned integer -> float +impl_from!(u8 => f16, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(u8 => f32, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(u8 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); +impl_from!(u8 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); +impl_from!(u16 => f16, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(u16 => f32, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(u16 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); +impl_from!(u16 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(u32 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); +impl_from!(u32 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); +// FIXME(f16_f128): This impl would allow using `f128` on stable before it is stabilised. +// impl_from!(u64 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); // float -> float // FIXME(f16_f128): adding additional `From<{float}>` impls to `f32` breaks inference. See @@ -174,7 +194,12 @@ impl_from!(f32 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0 impl_from!(f64 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); macro_rules! impl_float_from_bool { - ($float:ty) => { + ( + $float:ty $(; + doctest_prefix: $(#[doc = $doctest_prefix:literal])* + doctest_suffix: $(#[doc = $doctest_suffix:literal])* + )? + ) => { #[stable(feature = "float_from_bool", since = "1.68.0")] impl From for $float { #[doc = concat!("Converts a [`bool`] to [`", stringify!($float),"`] losslessly.")] @@ -182,12 +207,14 @@ macro_rules! impl_float_from_bool { /// /// # Examples /// ``` + $($(#[doc = $doctest_prefix])*)? #[doc = concat!("let x: ", stringify!($float)," = false.into();")] /// assert_eq!(x, 0.0); /// assert!(x.is_sign_positive()); /// #[doc = concat!("let y: ", stringify!($float)," = true.into();")] /// assert_eq!(y, 1.0); + $($(#[doc = $doctest_suffix])*)? /// ``` #[inline] fn from(small: bool) -> Self { @@ -198,8 +225,27 @@ macro_rules! impl_float_from_bool { } // boolean -> float +impl_float_from_bool!( + f16; + doctest_prefix: + // rustdoc doesn't remove the conventional space after the `///` + ///#![feature(f16)] + ///# #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + doctest_suffix: + ///# } +); impl_float_from_bool!(f32); impl_float_from_bool!(f64); +impl_float_from_bool!( + f128; + doctest_prefix: + ///#![feature(f128)] + ///# #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + doctest_suffix: + ///# } +); // no possible bounds violation macro_rules! impl_try_from_unbounded { diff --git a/library/core/src/default.rs b/library/core/src/default.rs index 4c30290ff263b..044997a81a9a2 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -101,7 +101,7 @@ use crate::ascii::Char as AsciiChar; /// bar: f32, /// } /// ``` -#[cfg_attr(not(test), rustc_diagnostic_item = "Default")] +#[rustc_diagnostic_item = "Default"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_trivial_field_reads] pub trait Default: Sized { diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 94847685ec965..bfa392003b91b 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -47,7 +47,7 @@ use crate::fmt::{self, Debug, Display, Formatter}; /// impl Error for ReadConfigError {} /// ``` #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "Error")] +#[rustc_diagnostic_item = "Error"] #[rustc_has_incoherent_inherent_impls] #[allow(multiple_supertrait_upcastable)] pub trait Error: Debug + Display { diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 080c0cef53304..ac07c645c0195 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -79,8 +79,9 @@ use crate::{fmt, ops, slice, str}; /// /// fn my_string_safe() -> String { /// let cstr = unsafe { CStr::from_ptr(my_string()) }; -/// // Get copy-on-write Cow<'_, str>, then guarantee a freshly-owned String allocation -/// String::from_utf8_lossy(cstr.to_bytes()).to_string() +/// // Get a copy-on-write Cow<'_, str>, then extract the +/// // allocated String (or allocate a fresh one if needed). +/// cstr.to_string_lossy().into_owned() /// } /// /// println!("string: {}", my_string_safe()); @@ -150,7 +151,6 @@ impl Error for FromBytesWithNulError { /// within the slice. /// /// This error is created by the [`CStr::from_bytes_until_nul`] method. -/// #[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] pub struct FromBytesUntilNulError(()); diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 9bae5fd466a18..0bc98e2ea8645 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -20,7 +20,7 @@ pub use self::c_str::FromBytesUntilNulError; pub use self::c_str::FromBytesWithNulError; use crate::fmt; -#[unstable(feature = "c_str_module", issue = "112134")] +#[stable(feature = "c_str_module", since = "1.88.0")] pub mod c_str; #[unstable( @@ -28,7 +28,7 @@ pub mod c_str; issue = "44930", reason = "the `c_variadic` feature has not been properly tested on all supported platforms" )] -pub use self::va_list::{VaList, VaListImpl}; +pub use self::va_list::{VaArgSafe, VaList, VaListImpl}; #[unstable( feature = "c_variadic", diff --git a/library/core/src/ffi/primitives.rs b/library/core/src/ffi/primitives.rs index ece3c7538dabb..351bf9f831476 100644 --- a/library/core/src/ffi/primitives.rs +++ b/library/core/src/ffi/primitives.rs @@ -35,11 +35,10 @@ type_alias! { "c_float.md", c_float = f32; } type_alias! { "c_double.md", c_double = f64; } mod c_char_definition { - cfg_if! { + crate::cfg_match! { // These are the targets on which c_char is unsigned. Usually the // signedness is the same for all target_os values on a given architecture // but there are some exceptions (see isSignedCharDefault() in clang). - // // aarch64: // Section 10 "Arm C and C++ language mappings" in Procedure Call Standard for the Arm® // 64-bit Architecture (AArch64) says C/C++ char is unsigned byte. @@ -97,14 +96,19 @@ mod c_char_definition { // are promoted to int as if from type signed char by default, unless the /J compilation // option is used." // https://learn.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170#character-types + // Vita: + // Chars are signed by default on the Vita, and VITASDK follows that convention. + // https://github.com/vitasdk/buildscripts/blob/09c533b771591ecde88864b6acad28ffb688dbd4/patches/gcc/0001-gcc-10.patch#L33-L34 + // // L4Re: - // The kernel builds with -funsigned-char on all targets (but useserspace follows the + // The kernel builds with -funsigned-char on all targets (but userspace follows the // architecture defaults). As we only have a target for userspace apps so there are no // special cases for L4Re below. // https://github.com/rust-lang/rust/pull/132975#issuecomment-2484645240 - if #[cfg(all( + all( not(windows), not(target_vendor = "apple"), + not(target_os = "vita"), any( target_arch = "aarch64", target_arch = "arm", @@ -118,21 +122,27 @@ mod c_char_definition { target_arch = "s390x", target_arch = "xtensa", ) - ))] { + ) => { pub(super) type c_char = u8; - } else { - // On every other target, c_char is signed. + } + // On every other target, c_char is signed. + _ => { pub(super) type c_char = i8; } } } mod c_long_definition { - cfg_if! { - if #[cfg(all(target_pointer_width = "64", not(windows)))] { + crate::cfg_match! { + any( + all(target_pointer_width = "64", not(windows)), + // wasm32 Linux ABI uses 64-bit long + all(target_arch = "wasm32", target_os = "linux") + ) => { pub(super) type c_long = i64; pub(super) type c_ulong = u64; - } else { + } + _ => { // The minimal size of `long` in the C standard is 32 bits pub(super) type c_long = i32; pub(super) type c_ulong = u32; @@ -162,11 +172,12 @@ pub type c_ptrdiff_t = isize; pub type c_ssize_t = isize; mod c_int_definition { - cfg_if! { - if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] { + crate::cfg_match! { + any(target_arch = "avr", target_arch = "msp430") => { pub(super) type c_int = i16; pub(super) type c_uint = u16; - } else { + } + _ => { pub(super) type c_int = i32; pub(super) type c_uint = u32; } diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index cefa0e3950cad..f12bd289f27aa 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -223,39 +223,57 @@ impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { } } -// The VaArgSafe trait needs to be used in public interfaces, however, the trait -// itself must not be allowed to be used outside this module. Allowing users to -// implement the trait for a new type (thereby allowing the va_arg intrinsic to -// be used on a new type) is likely to cause undefined behavior. -// -// FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface -// but also ensure it cannot be used elsewhere, the trait needs to be public -// within a private module. Once RFC 2145 has been implemented look into -// improving this. -mod sealed_trait { - /// Trait which permits the allowed types to be used with [super::VaListImpl::arg]. - pub unsafe trait VaArgSafe {} -} +mod sealed { + pub trait Sealed {} -macro_rules! impl_va_arg_safe { - ($($t:ty),+) => { - $( - unsafe impl sealed_trait::VaArgSafe for $t {} - )+ - } + impl Sealed for i32 {} + impl Sealed for i64 {} + impl Sealed for isize {} + + impl Sealed for u32 {} + impl Sealed for u64 {} + impl Sealed for usize {} + + impl Sealed for f64 {} + + impl Sealed for *mut T {} + impl Sealed for *const T {} } -impl_va_arg_safe! {i8, i16, i32, i64, usize} -impl_va_arg_safe! {u8, u16, u32, u64, isize} -impl_va_arg_safe! {f64} +/// Trait which permits the allowed types to be used with [`VaListImpl::arg`]. +/// +/// # Safety +/// +/// This trait must only be implemented for types that C passes as varargs without implicit promotion. +/// +/// In C varargs, integers smaller than [`c_int`] and floats smaller than [`c_double`] +/// are implicitly promoted to [`c_int`] and [`c_double`] respectively. Implementing this trait for +/// types that are subject to this promotion rule is invalid. +/// +/// [`c_int`]: core::ffi::c_int +/// [`c_double`]: core::ffi::c_double +pub unsafe trait VaArgSafe: sealed::Sealed {} + +// i8 and i16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`. +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for isize {} + +// u8 and u16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`. +unsafe impl VaArgSafe for u32 {} +unsafe impl VaArgSafe for u64 {} +unsafe impl VaArgSafe for usize {} + +// f32 is implicitly promoted to c_double in C, and cannot implement `VaArgSafe`. +unsafe impl VaArgSafe for f64 {} -unsafe impl sealed_trait::VaArgSafe for *mut T {} -unsafe impl sealed_trait::VaArgSafe for *const T {} +unsafe impl VaArgSafe for *mut T {} +unsafe impl VaArgSafe for *const T {} impl<'f> VaListImpl<'f> { /// Advance to the next arg. #[inline] - pub unsafe fn arg(&mut self) -> T { + pub unsafe fn arg(&mut self) -> T { // SAFETY: the caller must uphold the safety contract for `va_arg`. unsafe { va_arg(self) } } @@ -306,15 +324,15 @@ impl<'f> Drop for VaListImpl<'f> { /// `va_copy`. #[rustc_intrinsic] #[rustc_nounwind] -unsafe fn va_end(_ap: &mut VaListImpl<'_>); +unsafe fn va_end(ap: &mut VaListImpl<'_>); /// Copies the current location of arglist `src` to the arglist `dst`. #[rustc_intrinsic] #[rustc_nounwind] -unsafe fn va_copy<'f>(_dest: *mut VaListImpl<'f>, _src: &VaListImpl<'f>); +unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); /// Loads an argument of type `T` from the `va_list` `ap` and increment the /// argument `ap` points to. #[rustc_intrinsic] #[rustc_nounwind] -unsafe fn va_arg(_ap: &mut VaListImpl<'_>) -> T; +unsafe fn va_arg(ap: &mut VaListImpl<'_>) -> T; diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 3f10158193d76..556db239f2499 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -20,6 +20,8 @@ macro_rules! impl_general_format { } } +#[cfg(target_has_reliable_f16)] +impl_general_format! { f16 } impl_general_format! { f32 f64 } // Don't inline this so callers don't use the stack space this function @@ -29,7 +31,7 @@ fn float_to_decimal_common_exact( fmt: &mut Formatter<'_>, num: &T, sign: flt2dec::Sign, - precision: usize, + precision: u16, ) -> Result where T: flt2dec::DecodableFloat, @@ -40,7 +42,7 @@ where flt2dec::strategy::grisu::format_exact, *num, sign, - precision, + precision.into(), &mut buf, &mut parts, ); @@ -55,7 +57,7 @@ fn float_to_decimal_common_shortest( fmt: &mut Formatter<'_>, num: &T, sign: flt2dec::Sign, - precision: usize, + precision: u16, ) -> Result where T: flt2dec::DecodableFloat, @@ -68,7 +70,7 @@ where flt2dec::strategy::grisu::format_shortest, *num, sign, - precision, + precision.into(), &mut buf, &mut parts, ); @@ -86,7 +88,7 @@ where true => flt2dec::Sign::MinusPlus, }; - if let Some(precision) = fmt.options.precision { + if let Some(precision) = fmt.options.get_precision() { float_to_decimal_common_exact(fmt, num, sign, precision) } else { let min_precision = 0; @@ -101,7 +103,7 @@ fn float_to_exponential_common_exact( fmt: &mut Formatter<'_>, num: &T, sign: flt2dec::Sign, - precision: usize, + precision: u16, upper: bool, ) -> Result where @@ -113,7 +115,7 @@ where flt2dec::strategy::grisu::format_exact, *num, sign, - precision, + precision.into(), upper, &mut buf, &mut parts, @@ -162,7 +164,7 @@ where true => flt2dec::Sign::MinusPlus, }; - if let Some(precision) = fmt.options.precision { + if let Some(precision) = fmt.options.get_precision() { // 1 integral digit + `precision` fractional digits = `precision + 1` total digits float_to_exponential_common_exact(fmt, num, sign, precision + 1, upper) } else { @@ -180,7 +182,7 @@ where true => flt2dec::Sign::MinusPlus, }; - if let Some(precision) = fmt.options.precision { + if let Some(precision) = fmt.options.get_precision() { // this behavior of {:.PREC?} predates exponential formatting for {:?} float_to_decimal_common_exact(fmt, num, sign, precision) } else { @@ -231,6 +233,13 @@ macro_rules! floating { floating! { f32 f64 } +#[cfg(target_has_reliable_f16)] +floating! { f16 } + +// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order +// to avoid ICEs. + +#[cfg(not(target_has_reliable_f16))] #[stable(feature = "rust1", since = "1.0.0")] impl Debug for f16 { #[inline] @@ -239,6 +248,33 @@ impl Debug for f16 { } } +#[cfg(not(target_has_reliable_f16))] +#[stable(feature = "rust1", since = "1.0.0")] +impl Display for f16 { + #[inline] + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Debug::fmt(self, fmt) + } +} + +#[cfg(not(target_has_reliable_f16))] +#[stable(feature = "rust1", since = "1.0.0")] +impl LowerExp for f16 { + #[inline] + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Debug::fmt(self, fmt) + } +} + +#[cfg(not(target_has_reliable_f16))] +#[stable(feature = "rust1", since = "1.0.0")] +impl UpperExp for f16 { + #[inline] + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { + Debug::fmt(self, fmt) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Debug for f128 { #[inline] diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 764e7fff33ec7..4f7f8a5b84dd5 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -7,7 +7,7 @@ use crate::char::{EscapeDebugExtArgs, MAX_LEN_UTF8}; use crate::marker::PhantomData; use crate::num::fmt as numfmt; use crate::ops::Deref; -use crate::{iter, mem, result, str}; +use crate::{iter, result, str}; mod builders; #[cfg(not(no_fp_fmt_parse))] @@ -18,7 +18,7 @@ mod num; mod rt; #[stable(feature = "fmt_flags_align", since = "1.28.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "Alignment")] +#[rustc_diagnostic_item = "Alignment"] /// Possible alignments returned by `Formatter::align` #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Alignment { @@ -33,19 +33,6 @@ pub enum Alignment { Center, } -#[doc(hidden)] -#[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")] -impl From for Option { - fn from(value: rt::Alignment) -> Self { - match value { - rt::Alignment::Left => Some(Alignment::Left), - rt::Alignment::Right => Some(Alignment::Right), - rt::Alignment::Center => Some(Alignment::Center), - rt::Alignment::Unknown => None, - } - } -} - #[stable(feature = "debug_builders", since = "1.2.0")] pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; #[unstable(feature = "debug_closure_helpers", issue = "117729")] @@ -291,11 +278,52 @@ pub enum DebugAsHex { #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[unstable(feature = "formatting_options", issue = "118117")] pub struct FormattingOptions { + /// Flags, with the following bit fields: + /// + /// ```text + /// 31 30 29 28 27 26 25 24 23 22 21 20 0 + /// ┌───┬───────┬───┬───┬───┬───┬───┬───┬───┬───┬──────────────────────────────────┐ + /// │ 1 │ align │ p │ w │ X?│ x?│'0'│ # │ - │ + │ fill │ + /// └───┴───────┴───┴───┴───┴───┴───┴───┴───┴───┴──────────────────────────────────┘ + /// │ │ │ │ └─┬───────────────────┘ └─┬──────────────────────────────┘ + /// │ │ │ │ │ └─ The fill character (21 bits char). + /// │ │ │ │ └─ The debug upper/lower hex, zero pad, alternate, and plus/minus flags. + /// │ │ │ └─ Whether a width is set. (The value is stored separately.) + /// │ │ └─ Whether a precision is set. (The value is stored separately.) + /// │ ├─ 0: Align left. (<) + /// │ ├─ 1: Align right. (>) + /// │ ├─ 2: Align center. (^) + /// │ └─ 3: Alignment not set. (default) + /// └─ Always set. + /// This makes it possible to distinguish formatting flags from + /// a &str size when stored in (the upper bits of) the same field. + /// (fmt::Arguments will make use of this property in the future.) + /// ``` + // Note: This could use a special niche type with range 0x8000_0000..=0xfdd0ffff. + // It's unclear if that's useful, though. flags: u32, - fill: char, - align: Option, - width: Option, - precision: Option, + /// Width if width flag (bit 27) above is set. Otherwise, always 0. + width: u16, + /// Precision if precision flag (bit 28) above is set. Otherwise, always 0. + precision: u16, +} + +// This needs to match with compiler/rustc_ast_lowering/src/format.rs. +mod flags { + pub(super) const SIGN_PLUS_FLAG: u32 = 1 << 21; + pub(super) const SIGN_MINUS_FLAG: u32 = 1 << 22; + pub(super) const ALTERNATE_FLAG: u32 = 1 << 23; + pub(super) const SIGN_AWARE_ZERO_PAD_FLAG: u32 = 1 << 24; + pub(super) const DEBUG_LOWER_HEX_FLAG: u32 = 1 << 25; + pub(super) const DEBUG_UPPER_HEX_FLAG: u32 = 1 << 26; + pub(super) const WIDTH_FLAG: u32 = 1 << 27; + pub(super) const PRECISION_FLAG: u32 = 1 << 28; + pub(super) const ALIGN_BITS: u32 = 0b11 << 29; + pub(super) const ALIGN_LEFT: u32 = 0 << 29; + pub(super) const ALIGN_RIGHT: u32 = 1 << 29; + pub(super) const ALIGN_CENTER: u32 = 2 << 29; + pub(super) const ALIGN_UNKNOWN: u32 = 3 << 29; + pub(super) const ALWAYS_SET: u32 = 1 << 31; } impl FormattingOptions { @@ -311,7 +339,11 @@ impl FormattingOptions { /// - no [`DebugAsHex`] output mode. #[unstable(feature = "formatting_options", issue = "118117")] pub const fn new() -> Self { - Self { flags: 0, fill: ' ', align: None, width: None, precision: None } + Self { + flags: ' ' as u32 | flags::ALIGN_UNKNOWN | flags::ALWAYS_SET, + width: 0, + precision: 0, + } } /// Sets or removes the sign (the `+` or the `-` flag). @@ -324,13 +356,12 @@ impl FormattingOptions { /// - `-`: Currently not used #[unstable(feature = "formatting_options", issue = "118117")] pub fn sign(&mut self, sign: Option) -> &mut Self { - self.flags = - self.flags & !(1 << rt::Flag::SignMinus as u32 | 1 << rt::Flag::SignPlus as u32); - match sign { - None => {} - Some(Sign::Plus) => self.flags |= 1 << rt::Flag::SignPlus as u32, - Some(Sign::Minus) => self.flags |= 1 << rt::Flag::SignMinus as u32, - } + let sign = match sign { + None => 0, + Some(Sign::Plus) => flags::SIGN_PLUS_FLAG, + Some(Sign::Minus) => flags::SIGN_MINUS_FLAG, + }; + self.flags = self.flags & !(flags::SIGN_PLUS_FLAG | flags::SIGN_MINUS_FLAG) | sign; self } /// Sets or unsets the `0` flag. @@ -339,9 +370,9 @@ impl FormattingOptions { #[unstable(feature = "formatting_options", issue = "118117")] pub fn sign_aware_zero_pad(&mut self, sign_aware_zero_pad: bool) -> &mut Self { if sign_aware_zero_pad { - self.flags |= 1 << rt::Flag::SignAwareZeroPad as u32 + self.flags |= flags::SIGN_AWARE_ZERO_PAD_FLAG; } else { - self.flags &= !(1 << rt::Flag::SignAwareZeroPad as u32) + self.flags &= !flags::SIGN_AWARE_ZERO_PAD_FLAG; } self } @@ -356,9 +387,9 @@ impl FormattingOptions { #[unstable(feature = "formatting_options", issue = "118117")] pub fn alternate(&mut self, alternate: bool) -> &mut Self { if alternate { - self.flags |= 1 << rt::Flag::Alternate as u32 + self.flags |= flags::ALTERNATE_FLAG; } else { - self.flags &= !(1 << rt::Flag::Alternate as u32) + self.flags &= !flags::ALTERNATE_FLAG; } self } @@ -370,7 +401,7 @@ impl FormattingOptions { /// printed around it. #[unstable(feature = "formatting_options", issue = "118117")] pub fn fill(&mut self, fill: char) -> &mut Self { - self.fill = fill; + self.flags = self.flags & (u32::MAX << 21) | fill as u32; self } /// Sets or removes the alignment. @@ -379,7 +410,13 @@ impl FormattingOptions { /// positioned if it is smaller than the width of the formatter. #[unstable(feature = "formatting_options", issue = "118117")] pub fn align(&mut self, align: Option) -> &mut Self { - self.align = align; + let align: u32 = match align { + Some(Alignment::Left) => flags::ALIGN_LEFT, + Some(Alignment::Right) => flags::ALIGN_RIGHT, + Some(Alignment::Center) => flags::ALIGN_CENTER, + None => flags::ALIGN_UNKNOWN, + }; + self.flags = self.flags & !flags::ALIGN_BITS | align; self } /// Sets or removes the width. @@ -389,8 +426,14 @@ impl FormattingOptions { /// the padding specified by [`FormattingOptions::fill`]/[`FormattingOptions::align`] /// will be used to take up the required space. #[unstable(feature = "formatting_options", issue = "118117")] - pub fn width(&mut self, width: Option) -> &mut Self { - self.width = width; + pub fn width(&mut self, width: Option) -> &mut Self { + if let Some(width) = width { + self.flags |= flags::WIDTH_FLAG; + self.width = width; + } else { + self.flags &= !flags::WIDTH_FLAG; + self.width = 0; + } self } /// Sets or removes the precision. @@ -403,78 +446,86 @@ impl FormattingOptions { /// - For floating-point types, this indicates how many digits after the /// decimal point should be printed. #[unstable(feature = "formatting_options", issue = "118117")] - pub fn precision(&mut self, precision: Option) -> &mut Self { - self.precision = precision; + pub fn precision(&mut self, precision: Option) -> &mut Self { + if let Some(precision) = precision { + self.flags |= flags::PRECISION_FLAG; + self.precision = precision; + } else { + self.flags &= !flags::PRECISION_FLAG; + self.precision = 0; + } self } /// Specifies whether the [`Debug`] trait should use lower-/upper-case /// hexadecimal or normal integers #[unstable(feature = "formatting_options", issue = "118117")] pub fn debug_as_hex(&mut self, debug_as_hex: Option) -> &mut Self { - self.flags = self.flags - & !(1 << rt::Flag::DebugUpperHex as u32 | 1 << rt::Flag::DebugLowerHex as u32); - match debug_as_hex { - None => {} - Some(DebugAsHex::Upper) => self.flags |= 1 << rt::Flag::DebugUpperHex as u32, - Some(DebugAsHex::Lower) => self.flags |= 1 << rt::Flag::DebugLowerHex as u32, - } + let debug_as_hex = match debug_as_hex { + None => 0, + Some(DebugAsHex::Lower) => flags::DEBUG_LOWER_HEX_FLAG, + Some(DebugAsHex::Upper) => flags::DEBUG_UPPER_HEX_FLAG, + }; + self.flags = self.flags & !(flags::DEBUG_LOWER_HEX_FLAG | flags::DEBUG_UPPER_HEX_FLAG) + | debug_as_hex; self } /// Returns the current sign (the `+` or the `-` flag). #[unstable(feature = "formatting_options", issue = "118117")] pub const fn get_sign(&self) -> Option { - const SIGN_PLUS_BITFIELD: u32 = 1 << rt::Flag::SignPlus as u32; - const SIGN_MINUS_BITFIELD: u32 = 1 << rt::Flag::SignMinus as u32; - match self.flags & ((1 << rt::Flag::SignPlus as u32) | (1 << rt::Flag::SignMinus as u32)) { - SIGN_PLUS_BITFIELD => Some(Sign::Plus), - SIGN_MINUS_BITFIELD => Some(Sign::Minus), - 0 => None, - _ => panic!("Invalid sign bits set in flags"), + if self.flags & flags::SIGN_PLUS_FLAG != 0 { + Some(Sign::Plus) + } else if self.flags & flags::SIGN_MINUS_FLAG != 0 { + Some(Sign::Minus) + } else { + None } } /// Returns the current `0` flag. #[unstable(feature = "formatting_options", issue = "118117")] pub const fn get_sign_aware_zero_pad(&self) -> bool { - self.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 0 + self.flags & flags::SIGN_AWARE_ZERO_PAD_FLAG != 0 } /// Returns the current `#` flag. #[unstable(feature = "formatting_options", issue = "118117")] pub const fn get_alternate(&self) -> bool { - self.flags & (1 << rt::Flag::Alternate as u32) != 0 + self.flags & flags::ALTERNATE_FLAG != 0 } /// Returns the current fill character. #[unstable(feature = "formatting_options", issue = "118117")] pub const fn get_fill(&self) -> char { - self.fill + // SAFETY: We only ever put a valid `char` in the lower 21 bits of the flags field. + unsafe { char::from_u32_unchecked(self.flags & 0x1FFFFF) } } /// Returns the current alignment. #[unstable(feature = "formatting_options", issue = "118117")] pub const fn get_align(&self) -> Option { - self.align + match self.flags & flags::ALIGN_BITS { + flags::ALIGN_LEFT => Some(Alignment::Left), + flags::ALIGN_RIGHT => Some(Alignment::Right), + flags::ALIGN_CENTER => Some(Alignment::Center), + _ => None, + } } /// Returns the current width. #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn get_width(&self) -> Option { - self.width + pub const fn get_width(&self) -> Option { + if self.flags & flags::WIDTH_FLAG != 0 { Some(self.width) } else { None } } /// Returns the current precision. #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn get_precision(&self) -> Option { - self.precision + pub const fn get_precision(&self) -> Option { + if self.flags & flags::PRECISION_FLAG != 0 { Some(self.precision) } else { None } } /// Returns the current precision. #[unstable(feature = "formatting_options", issue = "118117")] pub const fn get_debug_as_hex(&self) -> Option { - const DEBUG_UPPER_BITFIELD: u32 = 1 << rt::Flag::DebugUpperHex as u32; - const DEBUG_LOWER_BITFIELD: u32 = 1 << rt::Flag::DebugLowerHex as u32; - match self.flags - & ((1 << rt::Flag::DebugUpperHex as u32) | (1 << rt::Flag::DebugLowerHex as u32)) - { - DEBUG_UPPER_BITFIELD => Some(DebugAsHex::Upper), - DEBUG_LOWER_BITFIELD => Some(DebugAsHex::Lower), - 0 => None, - _ => panic!("Invalid hex debug bits set in flags"), + if self.flags & flags::DEBUG_LOWER_HEX_FLAG != 0 { + Some(DebugAsHex::Lower) + } else if self.flags & flags::DEBUG_UPPER_HEX_FLAG != 0 { + Some(DebugAsHex::Upper) + } else { + None } } @@ -485,27 +536,6 @@ impl FormattingOptions { pub fn create_formatter<'a>(self, write: &'a mut (dyn Write + 'a)) -> Formatter<'a> { Formatter { options: self, buf: write } } - - #[doc(hidden)] - #[unstable( - feature = "fmt_internals", - reason = "internal routines only exposed for testing", - issue = "none" - )] - /// Flags for formatting - pub fn flags(&mut self, flags: u32) { - self.flags = flags - } - #[doc(hidden)] - #[unstable( - feature = "fmt_internals", - reason = "internal routines only exposed for testing", - issue = "none" - )] - /// Flags for formatting - pub fn get_flags(&self) -> u32 { - self.flags - } } #[unstable(feature = "formatting_options", issue = "118117")] @@ -592,44 +622,9 @@ pub struct Arguments<'a> { args: &'a [rt::Argument<'a>], } -/// Used by the format_args!() macro to create a fmt::Arguments object. #[doc(hidden)] #[unstable(feature = "fmt_internals", issue = "none")] impl<'a> Arguments<'a> { - #[inline] - pub const fn new_const(pieces: &'a [&'static str; N]) -> Self { - const { assert!(N <= 1) }; - Arguments { pieces, fmt: None, args: &[] } - } - - /// When using the format_args!() macro, this function is used to generate the - /// Arguments structure. - #[inline] - pub const fn new_v1( - pieces: &'a [&'static str; P], - args: &'a [rt::Argument<'a>; A], - ) -> Arguments<'a> { - const { assert!(P >= A && P <= A + 1, "invalid args") } - Arguments { pieces, fmt: None, args } - } - - /// Specifies nonstandard formatting parameters. - /// - /// An `rt::UnsafeArg` is required because the following invariants must be held - /// in order for this function to be safe: - /// 1. The `pieces` slice must be at least as long as `fmt`. - /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`. - /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`. - #[inline] - pub const fn new_v1_formatted( - pieces: &'a [&'static str], - args: &'a [rt::Argument<'a>], - fmt: &'a [rt::Placeholder], - _unsafe_arg: rt::UnsafeArg, - ) -> 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 @@ -710,9 +705,11 @@ impl<'a> Arguments<'a> { } /// Same as [`Arguments::as_str`], but will only return `Some(s)` if it can be determined at compile time. + #[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")] #[must_use] #[inline] - fn as_statically_known_str(&self) -> Option<&'static str> { + #[doc(hidden)] + pub fn as_statically_known_str(&self) -> Option<&'static str> { let s = self.as_str(); if core::intrinsics::is_val_statically_known(s.is_some()) { s } else { None } } @@ -1478,15 +1475,12 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { } unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argument<'_>]) -> Result { - fmt.options.fill = arg.fill; - fmt.options.align = arg.align.into(); - fmt.options.flags = arg.flags; - // SAFETY: arg and args come from the same Arguments, - // which guarantees the indexes are always within bounds. - unsafe { - fmt.options.width = getcount(args, &arg.width); - fmt.options.precision = getcount(args, &arg.precision); - } + let (width, precision) = + // SAFETY: arg and args come from the same Arguments, + // which guarantees the indexes are always within bounds. + unsafe { (getcount(args, &arg.width), getcount(args, &arg.precision)) }; + + let options = FormattingOptions { flags: arg.flags, width, precision }; // Extract the correct argument debug_assert!(arg.position < args.len()); @@ -1494,20 +1488,23 @@ unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argume // which guarantees its index is always within bounds. let value = unsafe { args.get_unchecked(arg.position) }; + // Set all the formatting options. + fmt.options = options; + // Then actually do some printing // SAFETY: this is a placeholder argument. unsafe { value.fmt(fmt) } } -unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> Option { +unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> u16 { match *cnt { - rt::Count::Is(n) => Some(n), - rt::Count::Implied => None, + rt::Count::Is(n) => n, + rt::Count::Implied => 0, rt::Count::Param(i) => { debug_assert!(i < args.len()); // SAFETY: cnt and args come from the same Arguments, // which guarantees this index is always within bounds. - unsafe { args.get_unchecked(i).as_usize() } + unsafe { args.get_unchecked(i).as_u16().unwrap_unchecked() } } } } @@ -1516,11 +1513,11 @@ unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> Option #[must_use = "don't forget to write the post padding"] pub(crate) struct PostPadding { fill: char, - padding: usize, + padding: u16, } impl PostPadding { - fn new(fill: char, padding: usize) -> PostPadding { + fn new(fill: char, padding: u16) -> PostPadding { PostPadding { fill, padding } } @@ -1625,40 +1622,28 @@ impl<'a> Formatter<'a> { } // The `width` field is more of a `min-width` parameter at this point. - match self.options.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) - } + let min = self.options.width; + if width >= usize::from(min) { + // We're over the minimum width, so then we can just write the bytes. + write_prefix(self, sign, prefix)?; + self.buf.write_str(buf) + } else if self.sign_aware_zero_pad() { // 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.options.fill, '0'); - let old_align = - crate::mem::replace(&mut self.options.align, Some(Alignment::Right)); - write_prefix(self, sign, prefix)?; - let post_padding = self.padding(min - width, Alignment::Right)?; - self.buf.write_str(buf)?; - post_padding.write(self)?; - self.options.fill = old_fill; - self.options.align = old_align; - Ok(()) - } + let old_options = self.options; + self.options.fill('0').align(Some(Alignment::Right)); + write_prefix(self, sign, prefix)?; + let post_padding = self.padding(min - width as u16, Alignment::Right)?; + self.buf.write_str(buf)?; + post_padding.write(self)?; + self.options = old_options; + Ok(()) + } else { // Otherwise, the sign and prefix goes after the padding - Some(min) => { - let post_padding = self.padding(min - width, Alignment::Right)?; - write_prefix(self, sign, prefix)?; - self.buf.write_str(buf)?; - post_padding.write(self) - } + let post_padding = self.padding(min - width as u16, Alignment::Right)?; + write_prefix(self, sign, prefix)?; + self.buf.write_str(buf)?; + post_padding.write(self) } } @@ -1693,49 +1678,40 @@ impl<'a> Formatter<'a> { /// ``` #[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.options.width.is_none() && self.options.precision.is_none() { + // Make sure there's a fast path up front. + if self.options.flags & (flags::WIDTH_FLAG | flags::PRECISION_FLAG) == 0 { return self.buf.write_str(s); } - // The `precision` field can be interpreted as a `max-width` for the + + // The `precision` field can be interpreted as a maximum width for the // string being formatted. - let s = if let Some(max) = self.options.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 - } + let (s, char_count) = if let Some(max_char_count) = self.options.get_precision() { + let mut iter = s.char_indices(); + let remaining = match iter.advance_by(usize::from(max_char_count)) { + Ok(()) => 0, + Err(remaining) => remaining.get(), + }; + // SAFETY: The offset of `.char_indices()` is guaranteed to be + // in-bounds and between character boundaries. + let truncated = unsafe { s.get_unchecked(..iter.offset()) }; + (truncated, usize::from(max_char_count) - remaining) } else { - &s + // Use the optimized char counting algorithm for the full string. + (s, s.chars().count()) }; - // The `width` field is more of a `min-width` parameter at this point. - match self.options.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), - Some(width) => { - let chars_count = s.chars().count(); - // 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. - if 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. - else { - let align = Alignment::Left; - let post_padding = self.padding(width - chars_count, align)?; - self.buf.write_str(s)?; - post_padding.write(self) - } - } + + // The `width` field is more of a minimum width parameter at this point. + if char_count < usize::from(self.options.width) { + // If we're under the minimum width, then fill up the minimum width + // with the specified string + some alignment. + let post_padding = + self.padding(self.options.width - char_count as u16, Alignment::Left)?; + self.buf.write_str(s)?; + post_padding.write(self) + } else { + // If we're over the minimum width or there is no minimum width, we + // can just emit the string. + self.buf.write_str(s) } } @@ -1745,22 +1721,23 @@ impl<'a> Formatter<'a> { /// thing that is being padded. pub(crate) fn padding( &mut self, - padding: usize, + padding: u16, default: Alignment, ) -> result::Result { - let align = self.align().unwrap_or(default); + let align = self.options.get_align().unwrap_or(default); + let fill = self.options.get_fill(); - let (pre_pad, post_pad) = match align { - Alignment::Left => (0, padding), - Alignment::Right => (padding, 0), - Alignment::Center => (padding / 2, (padding + 1) / 2), + let padding_left = match align { + Alignment::Left => 0, + Alignment::Right => padding, + Alignment::Center => padding / 2, }; - for _ in 0..pre_pad { - self.buf.write_char(self.options.fill)?; + for _ in 0..padding_left { + self.buf.write_char(fill)?; } - Ok(PostPadding::new(self.options.fill, post_pad)) + Ok(PostPadding::new(fill, padding - padding_left)) } /// Takes the formatted parts and applies the padding. @@ -1772,12 +1749,16 @@ impl<'a> Formatter<'a> { /// /// Any `numfmt::Part::Copy` parts in `formatted` must contain valid UTF-8. unsafe fn pad_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result { - if let Some(mut width) = self.options.width { + if self.options.width == 0 { + // this is the common case and we take a shortcut + // SAFETY: Per the precondition. + unsafe { self.write_formatted_parts(formatted) } + } else { // 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.options.fill; - let old_align = self.options.align; + let mut width = self.options.width; + let old_options = self.options; if self.sign_aware_zero_pad() { // a sign always goes first let sign = formatted.sign; @@ -1785,32 +1766,26 @@ impl<'a> Formatter<'a> { // remove the sign from the formatted parts formatted.sign = ""; - width = width.saturating_sub(sign.len()); - self.options.fill = '0'; - self.options.align = Some(Alignment::Right); + width = width.saturating_sub(sign.len() as u16); + self.options.fill('0').align(Some(Alignment::Right)); } // remaining parts go through the ordinary padding process. let len = formatted.len(); - let ret = if width <= len { + let ret = if usize::from(width) <= len { // no padding // SAFETY: Per the precondition. unsafe { self.write_formatted_parts(&formatted) } } else { - let post_padding = self.padding(width - len, Alignment::Right)?; + let post_padding = self.padding(width - len as u16, Alignment::Right)?; // SAFETY: Per the precondition. unsafe { self.write_formatted_parts(&formatted)?; } post_padding.write(self) }; - self.options.fill = old_fill; - self.options.align = old_align; + self.options = old_options; ret - } else { - // this is the common case and we take a shortcut - // SAFETY: Per the precondition. - unsafe { self.write_formatted_parts(formatted) } } } @@ -1931,7 +1906,9 @@ impl<'a> Formatter<'a> { or `sign_aware_zero_pad` methods instead" )] pub fn flags(&self) -> u32 { - self.options.flags + // Extract the debug upper/lower hex, zero pad, alternate, and plus/minus flags + // to stay compatible with older versions of Rust. + self.options.flags >> 21 & 0x3F } /// Returns the character used as 'fill' whenever there is alignment. @@ -1964,7 +1941,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn fill(&self) -> char { - self.options.fill + self.options.get_fill() } /// Returns a flag indicating what form of alignment was requested. @@ -1999,7 +1976,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags_align", since = "1.28.0")] pub fn align(&self) -> Option { - self.options.align + self.options.get_align() } /// Returns the optionally specified integer width that the output should be. @@ -2029,7 +2006,11 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn width(&self) -> Option { - self.options.width + if self.options.flags & flags::WIDTH_FLAG == 0 { + None + } else { + Some(self.options.width as usize) + } } /// Returns the optionally specified precision for numeric types. @@ -2060,7 +2041,11 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn precision(&self) -> Option { - self.options.precision + if self.options.flags & flags::PRECISION_FLAG == 0 { + None + } else { + Some(self.options.precision as usize) + } } /// Determines if the `+` flag was specified. @@ -2092,7 +2077,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_plus(&self) -> bool { - self.options.flags & (1 << rt::Flag::SignPlus as u32) != 0 + self.options.flags & flags::SIGN_PLUS_FLAG != 0 } /// Determines if the `-` flag was specified. @@ -2121,7 +2106,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_minus(&self) -> bool { - self.options.flags & (1 << rt::Flag::SignMinus as u32) != 0 + self.options.flags & flags::SIGN_MINUS_FLAG != 0 } /// Determines if the `#` flag was specified. @@ -2149,7 +2134,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn alternate(&self) -> bool { - self.options.flags & (1 << rt::Flag::Alternate as u32) != 0 + self.options.flags & flags::ALTERNATE_FLAG != 0 } /// Determines if the `0` flag was specified. @@ -2175,17 +2160,16 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_aware_zero_pad(&self) -> bool { - self.options.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 0 + self.options.flags & flags::SIGN_AWARE_ZERO_PAD_FLAG != 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.options.flags & (1 << rt::Flag::DebugLowerHex as u32) != 0 + self.options.flags & flags::DEBUG_LOWER_HEX_FLAG != 0 } - fn debug_upper_hex(&self) -> bool { - self.options.flags & (1 << rt::Flag::DebugUpperHex as u32) != 0 + self.options.flags & flags::DEBUG_UPPER_HEX_FLAG != 0 } /// Creates a [`DebugStruct`] builder designed to assist with creation of @@ -2765,7 +2749,7 @@ impl Debug for char { #[stable(feature = "rust1", since = "1.0.0")] impl Display for char { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - if f.options.width.is_none() && f.options.precision.is_none() { + if f.options.flags & (flags::WIDTH_FLAG | flags::PRECISION_FLAG) == 0 { f.write_char(*self) } else { f.pad(self.encode_utf8(&mut [0; MAX_LEN_UTF8])) @@ -2776,7 +2760,14 @@ impl Display for char { #[stable(feature = "rust1", since = "1.0.0")] impl Pointer for *const T { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - pointer_fmt_inner(self.expose_provenance(), f) + if <::Metadata as core::unit::IsUnit>::is_unit() { + pointer_fmt_inner(self.expose_provenance(), f) + } else { + f.debug_struct("Pointer") + .field_with("addr", |f| pointer_fmt_inner(self.expose_provenance(), f)) + .field("metadata", &core::ptr::metadata(*self)) + .finish() + } } } @@ -2789,26 +2780,24 @@ impl Pointer for *const T { /// /// [problematic]: https://github.com/rust-lang/rust/issues/95489 pub(crate) fn pointer_fmt_inner(ptr_addr: usize, f: &mut Formatter<'_>) -> Result { - let old_width = f.options.width; - let old_flags = f.options.flags; + let old_options = f.options; // 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.options.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32); + if f.options.get_alternate() { + f.options.sign_aware_zero_pad(true); - if f.options.width.is_none() { - f.options.width = Some((usize::BITS / 4) as usize + 2); + if f.options.get_width().is_none() { + f.options.width(Some((usize::BITS / 4) as u16 + 2)); } } - f.options.flags |= 1 << (rt::Flag::Alternate as u32); + f.options.alternate(true); let ret = LowerHex::fmt(&ptr_addr, f); - f.options.width = old_width; - f.options.flags = old_flags; + f.options = old_options; ret } @@ -2965,6 +2954,6 @@ impl Debug for SyncUnsafeCell { } } -// If you expected tests to be here, look instead at the core/tests/fmt.rs file, +// If you expected tests to be here, look instead at coretests/tests/fmt/; // it's a lot easier than creating all of the rt::Piece structures here. -// There are also tests in the alloc crate, for those that need allocations. +// There are also tests in alloctests/tests/fmt.rs, for those that need allocations. diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 4467b37bd4510..ba30518d70bc2 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -208,7 +208,11 @@ macro_rules! impl_Display { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "optimize_for_size"))] { - self._fmt(true, f) + const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; + // Buffer decimals for $unsigned with right alignment. + let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + + f.pad_integral(true, "", self._fmt(&mut buf)) } #[cfg(feature = "optimize_for_size")] { @@ -222,7 +226,11 @@ macro_rules! impl_Display { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "optimize_for_size"))] { - return self.unsigned_abs()._fmt(*self >= 0, f); + const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; + // Buffer decimals for $unsigned with right alignment. + let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + + f.pad_integral(*self >= 0, "", self.unsigned_abs()._fmt(&mut buf)) } #[cfg(feature = "optimize_for_size")] { @@ -233,10 +241,13 @@ macro_rules! impl_Display { #[cfg(not(feature = "optimize_for_size"))] impl $unsigned { - fn _fmt(self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; - // Buffer decimals for $unsigned with right alignment. - let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + #[doc(hidden)] + #[unstable( + feature = "fmt_internals", + reason = "specialized method meant to only be used by `SpecToString` implementation", + issue = "none" + )] + pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit::]) -> &'a str { // Count the number of bytes in buf that are not initialized. let mut offset = buf.len(); // Consume the least-significant decimals from a working copy. @@ -301,13 +312,12 @@ macro_rules! impl_Display { // SAFETY: All buf content since offset is set. let written = unsafe { buf.get_unchecked(offset..) }; // SAFETY: Writes use ASCII from the lookup table exclusively. - let as_str = unsafe { + unsafe { str::from_utf8_unchecked(slice::from_raw_parts( MaybeUninit::slice_as_ptr(written), written.len(), )) - }; - f.pad_integral(is_nonnegative, "", as_str) + } } })* diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs index 85d089a079082..7fd8d3e5b5319 100644 --- a/library/core/src/fmt/rt.rs +++ b/library/core/src/fmt/rt.rs @@ -1,7 +1,10 @@ #![allow(missing_debug_implementations)] #![unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] -//! These are the lang items used by format_args!(). +//! All types and methods in this file are used by the compiler in +//! the expansion/lowering of format_args!(). +//! +//! Do not modify them without understanding the consequences for the format_args!() macro. use super::*; use crate::hint::unreachable_unchecked; @@ -11,60 +14,24 @@ use crate::ptr::NonNull; #[derive(Copy, Clone)] pub struct Placeholder { pub position: usize, - pub fill: char, - pub align: Alignment, pub flags: u32, pub precision: Count, pub width: Count, } -impl Placeholder { - #[inline] - pub const fn new( - position: usize, - fill: char, - align: Alignment, - flags: u32, - precision: Count, - width: Count, - ) -> Self { - Self { position, fill, align, flags, precision, width } - } -} - -#[lang = "format_alignment"] -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum Alignment { - Left, - Right, - Center, - Unknown, -} - /// Used by [width](https://doc.rust-lang.org/std/fmt/#width) /// and [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers. #[lang = "format_count"] #[derive(Copy, Clone)] pub enum Count { /// Specified with a literal number, stores the value - Is(usize), + Is(u16), /// Specified using `$` and `*` syntaxes, stores the index into `args` Param(usize), /// Not specified Implied, } -// This needs to match the order of flags in compiler/rustc_ast_lowering/src/format.rs. -#[derive(Copy, Clone)] -pub(super) enum Flag { - SignPlus, - SignMinus, - Alternate, - SignAwareZeroPad, - DebugLowerHex, - DebugUpperHex, -} - #[derive(Copy, Clone)] enum ArgumentType<'a> { Placeholder { @@ -74,7 +41,7 @@ enum ArgumentType<'a> { formatter: unsafe fn(NonNull<()>, &mut Formatter<'_>) -> Result, _lifetime: PhantomData<&'a ()>, }, - Count(usize), + Count(u16), } /// This struct represents a generic "argument" which is taken by format_args!(). @@ -93,65 +60,99 @@ pub struct Argument<'a> { ty: ArgumentType<'a>, } -#[rustc_diagnostic_item = "ArgumentMethods"] -impl Argument<'_> { - #[inline] - const fn new<'a, T>(x: &'a T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'a> { +macro_rules! argument_new { + ($t:ty, $x:expr, $f:expr) => { Argument { // INVARIANT: this creates an `ArgumentType<'a>` from a `&'a T` and // a `fn(&T, ...)`, so the invariant is maintained. ty: ArgumentType::Placeholder { - value: NonNull::from_ref(x).cast(), - // SAFETY: function pointers always have the same layout. - formatter: unsafe { mem::transmute(f) }, + value: NonNull::<$t>::from_ref($x).cast(), + // The Rust ABI considers all pointers to be equivalent, so transmuting a fn(&T) to + // fn(NonNull<()>) and calling it with a NonNull<()> that points at a T is allowed. + // However, the CFI sanitizer does not allow this, and triggers a crash when it + // happens. + // + // To avoid this crash, we use a helper function when CFI is enabled. To avoid the + // cost of this helper function (mainly code-size) when it is not needed, we + // transmute the function pointer otherwise. + // + // This is similar to what the Rust compiler does internally with vtables when KCFI + // is enabled, where it generates trampoline functions that only serve to adjust the + // expected type of the argument. `ArgumentType::Placeholder` is a bit like a + // manually constructed trait object, so it is not surprising that the same approach + // has to be applied here as well. + // + // It is still considered problematic (from the Rust side) that CFI rejects entirely + // legal Rust programs, so we do not consider anything done here a stable guarantee, + // but meanwhile we carry this work-around to keep Rust compatible with CFI and + // KCFI. + #[cfg(not(any(sanitize = "cfi", sanitize = "kcfi")))] + formatter: { + let f: fn(&$t, &mut Formatter<'_>) -> Result = $f; + // SAFETY: This is only called with `value`, which has the right type. + unsafe { core::mem::transmute(f) } + }, + #[cfg(any(sanitize = "cfi", sanitize = "kcfi"))] + formatter: |ptr: NonNull<()>, fmt: &mut Formatter<'_>| { + let func = $f; + // SAFETY: This is the same type as the `value` field. + let r = unsafe { ptr.cast::<$t>().as_ref() }; + (func)(r, fmt) + }, _lifetime: PhantomData, }, } - } + }; +} +impl Argument<'_> { #[inline] - pub fn new_display(x: &T) -> Argument<'_> { - Self::new(x, Display::fmt) + pub const fn new_display(x: &T) -> Argument<'_> { + argument_new!(T, x, ::fmt) } #[inline] - pub fn new_debug(x: &T) -> Argument<'_> { - Self::new(x, Debug::fmt) + pub const fn new_debug(x: &T) -> Argument<'_> { + argument_new!(T, x, ::fmt) } #[inline] - pub fn new_debug_noop(x: &T) -> Argument<'_> { - Self::new(x, |_, _| Ok(())) + pub const fn new_debug_noop(x: &T) -> Argument<'_> { + argument_new!(T, x, |_: &T, _| Ok(())) } #[inline] - pub fn new_octal(x: &T) -> Argument<'_> { - Self::new(x, Octal::fmt) + pub const fn new_octal(x: &T) -> Argument<'_> { + argument_new!(T, x, ::fmt) } #[inline] - pub fn new_lower_hex(x: &T) -> Argument<'_> { - Self::new(x, LowerHex::fmt) + pub const fn new_lower_hex(x: &T) -> Argument<'_> { + argument_new!(T, x, ::fmt) } #[inline] - pub fn new_upper_hex(x: &T) -> Argument<'_> { - Self::new(x, UpperHex::fmt) + pub const fn new_upper_hex(x: &T) -> Argument<'_> { + argument_new!(T, x, ::fmt) } #[inline] - pub fn new_pointer(x: &T) -> Argument<'_> { - Self::new(x, Pointer::fmt) + pub const fn new_pointer(x: &T) -> Argument<'_> { + argument_new!(T, x, ::fmt) } #[inline] - pub fn new_binary(x: &T) -> Argument<'_> { - Self::new(x, Binary::fmt) + pub const fn new_binary(x: &T) -> Argument<'_> { + argument_new!(T, x, ::fmt) } #[inline] - pub fn new_lower_exp(x: &T) -> Argument<'_> { - Self::new(x, LowerExp::fmt) + pub const fn new_lower_exp(x: &T) -> Argument<'_> { + argument_new!(T, x, ::fmt) } #[inline] - pub fn new_upper_exp(x: &T) -> Argument<'_> { - Self::new(x, UpperExp::fmt) + pub const fn new_upper_exp(x: &T) -> Argument<'_> { + argument_new!(T, x, ::fmt) } #[inline] + #[track_caller] pub const fn from_usize(x: &usize) -> Argument<'_> { - Argument { ty: ArgumentType::Count(*x) } + if *x > u16::MAX as usize { + panic!("Formatting argument out of range"); + } + Argument { ty: ArgumentType::Count(*x as u16) } } /// Format this placeholder argument. @@ -159,11 +160,6 @@ impl Argument<'_> { /// # Safety /// /// This argument must actually be a placeholder argument. - /// - // FIXME: Transmuting formatter in new and indirectly branching to/calling - // it here is an explicit CFI violation. - #[allow(inline_no_sanitize)] - #[no_sanitize(cfi, kcfi)] #[inline] pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result { match self.ty { @@ -181,7 +177,7 @@ impl Argument<'_> { } #[inline] - pub(super) const fn as_usize(&self) -> Option { + pub(super) const fn as_u16(&self) -> Option { match self.ty { ArgumentType::Count(count) => Some(count), ArgumentType::Placeholder { .. } => None, @@ -220,3 +216,57 @@ impl UnsafeArg { Self { _private: () } } } + +/// Used by the format_args!() macro to create a fmt::Arguments object. +#[doc(hidden)] +#[unstable(feature = "fmt_internals", issue = "none")] +#[rustc_diagnostic_item = "FmtArgumentsNew"] +impl<'a> Arguments<'a> { + #[inline] + pub const fn new_const(pieces: &'a [&'static str; N]) -> Self { + const { assert!(N <= 1) }; + Arguments { pieces, fmt: None, args: &[] } + } + + /// When using the format_args!() macro, this function is used to generate the + /// Arguments structure. + /// + /// This function should _not_ be const, to make sure we don't accept + /// format_args!() and panic!() with arguments in const, even when not evaluated: + /// + /// ```compile_fail,E0015 + /// const _: () = if false { panic!("a {}", "a") }; + /// ``` + #[inline] + pub fn new_v1( + pieces: &'a [&'static str; P], + args: &'a [rt::Argument<'a>; A], + ) -> Arguments<'a> { + const { assert!(P >= A && P <= A + 1, "invalid args") } + Arguments { pieces, fmt: None, args } + } + + /// Specifies nonstandard formatting parameters. + /// + /// An `rt::UnsafeArg` is required because the following invariants must be held + /// in order for this function to be safe: + /// 1. The `pieces` slice must be at least as long as `fmt`. + /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`. + /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`. + /// + /// This function should _not_ be const, to make sure we don't accept + /// format_args!() and panic!() with arguments in const, even when not evaluated: + /// + /// ```compile_fail,E0015 + /// const _: () = if false { panic!("a {:1}", "a") }; + /// ``` + #[inline] + pub fn new_v1_formatted( + pieces: &'a [&'static str], + args: &'a [rt::Argument<'a>], + fmt: &'a [rt::Placeholder], + _unsafe_arg: rt::UnsafeArg, + ) -> Arguments<'a> { + Arguments { pieces, fmt: Some(fmt), args } + } +} diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs index f1778a4d782af..c48c3f2ba281e 100644 --- a/library/core/src/future/async_drop.rs +++ b/library/core/src/future/async_drop.rs @@ -1,284 +1,49 @@ #![unstable(feature = "async_drop", issue = "126482")] -use crate::fmt; -use crate::future::{Future, IntoFuture}; -use crate::intrinsics::discriminant_value; -use crate::marker::{DiscriminantKind, PhantomPinned}; -use crate::mem::MaybeUninit; -use crate::pin::Pin; -use crate::task::{Context, Poll, ready}; - -/// Asynchronously drops a value by running `AsyncDrop::async_drop` -/// on a value and its fields recursively. -#[unstable(feature = "async_drop", issue = "126482")] -pub fn async_drop(value: T) -> AsyncDropOwning { - AsyncDropOwning { value: MaybeUninit::new(value), dtor: None, _pinned: PhantomPinned } -} - -/// A future returned by the [`async_drop`]. -#[unstable(feature = "async_drop", issue = "126482")] -pub struct AsyncDropOwning { - value: MaybeUninit, - dtor: Option>, - _pinned: PhantomPinned, -} - -#[unstable(feature = "async_drop", issue = "126482")] -impl fmt::Debug for AsyncDropOwning { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AsyncDropOwning").finish_non_exhaustive() - } -} - -#[unstable(feature = "async_drop", issue = "126482")] -impl Future for AsyncDropOwning { - type Output = (); - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // SAFETY: Self is pinned thus it is ok to store references to self - unsafe { - let this = self.get_unchecked_mut(); - let dtor = Pin::new_unchecked( - this.dtor.get_or_insert_with(|| async_drop_in_place(this.value.as_mut_ptr())), - ); - // AsyncDestuctors are idempotent so Self gets idempotency as well - dtor.poll(cx) - } - } -} +#[allow(unused_imports)] +use core::future::Future; -#[lang = "async_drop_in_place"] -#[allow(unconditional_recursion)] -// FIXME: Consider if `#[rustc_diagnostic_item = "ptr_drop_in_place"]` is needed? -unsafe fn async_drop_in_place_raw( - to_drop: *mut T, -) -> ::AsyncDestructor { - // Code here does not matter - this is replaced by the - // real async drop glue constructor by the compiler. - - // SAFETY: see comment above - unsafe { async_drop_in_place_raw(to_drop) } -} +#[allow(unused_imports)] +use crate::pin::Pin; +#[allow(unused_imports)] +use crate::task::{Context, Poll}; -/// Creates the asynchronous destructor of the pointed-to value. -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `to_drop` must be [valid](crate::ptr#safety) for both reads and writes. -/// -/// * `to_drop` must be properly aligned, even if `T` has size 0. +/// Async version of Drop trait. /// -/// * `to_drop` must be nonnull, even if `T` has size 0. +/// 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 value `to_drop` points to must be valid for async dropping, -/// which may mean it must uphold additional invariants. These -/// invariants depend on the type of the value being dropped. For -/// instance, when dropping a Box, the box's pointer to the heap must -/// be valid. +/// [the reference]: https://doc.rust-lang.org/reference/destructors.html /// -/// * While `async_drop_in_place` is executing or the returned async -/// destructor is alive, the only way to access parts of `to_drop` -/// is through the `self: Pin<&mut Self>` references supplied to -/// the `AsyncDrop::async_drop` methods that `async_drop_in_place` -/// or `AsyncDropInPlace::poll` invokes. This usually means the -/// returned future stores the `to_drop` pointer and user is required -/// to guarantee that dropped value doesn't move. +/// ## `Copy` and ([`Drop`]|`AsyncDrop`) are exclusive /// -#[unstable(feature = "async_drop", issue = "126482")] -pub unsafe fn async_drop_in_place(to_drop: *mut T) -> AsyncDropInPlace { - // SAFETY: `async_drop_in_place_raw` has the same safety requirements - unsafe { AsyncDropInPlace(async_drop_in_place_raw(to_drop)) } -} - -/// A future returned by the [`async_drop_in_place`]. -#[unstable(feature = "async_drop", issue = "126482")] -pub struct AsyncDropInPlace(::AsyncDestructor); - -#[unstable(feature = "async_drop", issue = "126482")] -impl fmt::Debug for AsyncDropInPlace { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AsyncDropInPlace").finish_non_exhaustive() - } -} - -#[unstable(feature = "async_drop", issue = "126482")] -impl Future for AsyncDropInPlace { - type Output = (); - - #[inline(always)] - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // SAFETY: This code simply forwards poll call to the inner future - unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }.poll(cx) - } -} - -// FIXME(zetanumbers): Add same restrictions on AsyncDrop impls as -// with Drop impls -/// Custom code within the asynchronous destructor. +/// You cannot implement both [`Copy`] and ([`Drop`]|`AsyncDrop`) 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. #[unstable(feature = "async_drop", issue = "126482")] #[lang = "async_drop"] pub trait AsyncDrop { - /// A future returned by the [`AsyncDrop::async_drop`] to be part - /// of the async destructor. - #[unstable(feature = "async_drop", issue = "126482")] - type Dropper<'a>: Future - where - Self: 'a; - - /// Constructs the asynchronous destructor for this type. - #[unstable(feature = "async_drop", issue = "126482")] - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>; -} - -#[lang = "async_destruct"] -#[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] -trait AsyncDestruct { - type AsyncDestructor: Future; -} - -/// Basically calls `AsyncDrop::async_drop` with pointer. Used to simplify -/// generation of the code for `async_drop_in_place_raw` -#[lang = "surface_async_drop_in_place"] -async unsafe fn surface_async_drop_in_place(ptr: *mut T) { - // SAFETY: We call this from async drop `async_drop_in_place_raw` - // which has the same safety requirements - unsafe { ::async_drop(Pin::new_unchecked(&mut *ptr)).await } -} - -/// Basically calls `Drop::drop` with pointer. Used to simplify generation -/// of the code for `async_drop_in_place_raw` -#[allow(drop_bounds)] -#[lang = "async_drop_surface_drop_in_place"] -async unsafe fn surface_drop_in_place(ptr: *mut T) { - // SAFETY: We call this from async drop `async_drop_in_place_raw` - // which has the same safety requirements - unsafe { crate::ops::fallback_surface_drop(&mut *ptr) } -} - -/// Wraps a future to continue outputting `Poll::Ready(())` once after -/// wrapped future completes by returning `Poll::Ready(())` on poll. This -/// is useful for constructing async destructors to guarantee this -/// "fuse" property -// -// FIXME: Consider optimizing combinators to not have to use fuse in majority -// of cases, perhaps by adding `#[(rustc_)idempotent(_future)]` attribute for -// async functions and blocks with the unit return type. However current layout -// optimizations currently encode `None` case into the async block's discriminant. -struct Fuse { - inner: Option, -} - -#[lang = "async_drop_fuse"] -fn fuse(inner: T) -> Fuse { - Fuse { inner: Some(inner) } -} - -impl Future for Fuse -where - T: Future, -{ - type Output = (); - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // SAFETY: pin projection into `self.inner` - unsafe { - let this = self.get_unchecked_mut(); - if let Some(inner) = &mut this.inner { - ready!(Pin::new_unchecked(inner).poll(cx)); - this.inner = None; - } - } - Poll::Ready(()) - } -} - -/// Async destructor for arrays and slices. -#[lang = "async_drop_slice"] -async unsafe fn slice(s: *mut [T]) { - let len = s.len(); - let ptr = s.as_mut_ptr(); - for i in 0..len { - // SAFETY: we iterate over elements of `s` slice - unsafe { async_drop_in_place_raw(ptr.add(i)).await } - } -} - -/// Constructs a chain of two futures, which awaits them sequentially as -/// a future. -#[lang = "async_drop_chain"] -async fn chain(first: F, last: G) -where - F: IntoFuture, - G: IntoFuture, -{ - first.await; - last.await; + /// Executes the async destructor for this type. + /// + /// This method is called implicitly when the value goes out of scope, + /// and cannot be called explicitly. + /// + /// When this method has been called, `self` has not yet been deallocated. + /// That only happens after the method is over. + /// + /// # Panics + #[allow(async_fn_in_trait)] + async fn drop(self: Pin<&mut Self>); } -/// Basically a lazy version of `async_drop_in_place`. Returns a future -/// that would call `AsyncDrop::async_drop` on a first poll. -/// -/// # Safety -/// -/// Same as `async_drop_in_place` except is lazy to avoid creating -/// multiple mutable references. -#[lang = "async_drop_defer"] -async unsafe fn defer(to_drop: *mut T) { - // SAFETY: same safety requirements as `async_drop_in_place` - unsafe { async_drop_in_place(to_drop) }.await -} - -/// If `T`'s discriminant is equal to the stored one then awaits `M` -/// otherwise awaits the `O`. -/// -/// # Safety -/// -/// Users should carefully manage the returned future, since it would -/// try creating an immutable reference from `this` and get pointee's -/// discriminant. -// FIXME(zetanumbers): Send and Sync impls -#[lang = "async_drop_either"] -async unsafe fn either, M: IntoFuture, T>( - other: O, - matched: M, - this: *mut T, - discr: ::Discriminant, -) { - // SAFETY: Guaranteed by the safety section of this funtion's documentation - if unsafe { discriminant_value(&*this) } == discr { - drop(other); - matched.await - } else { - drop(matched); - other.await - } -} - -#[lang = "async_drop_deferred_drop_in_place"] -async unsafe fn deferred_drop_in_place(to_drop: *mut T) { - // SAFETY: same safety requirements as with drop_in_place (implied by - // function's name) - unsafe { crate::ptr::drop_in_place(to_drop) } -} - -/// Used for noop async destructors. We don't use [`core::future::Ready`] -/// because it panics after its second poll, which could be potentially -/// bad if that would happen during the cleanup. -#[derive(Clone, Copy)] -struct Noop; - -#[lang = "async_drop_noop"] -fn noop() -> Noop { - Noop -} - -impl Future for Noop { - type Output = (); - - fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { - Poll::Ready(()) - } +/// Async drop. +#[unstable(feature = "async_drop", issue = "126482")] +#[lang = "async_drop_in_place"] +pub async unsafe fn async_drop_in_place(_to_drop: *mut T) { + // Code here does not matter - this is replaced by the + // real implementation by the compiler. } diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs index 65c0171c88d5b..2b16a568b4031 100644 --- a/library/core/src/future/mod.rs +++ b/library/core/src/future/mod.rs @@ -21,7 +21,7 @@ mod poll_fn; mod ready; #[unstable(feature = "async_drop", issue = "126482")] -pub use async_drop::{AsyncDrop, AsyncDropInPlace, async_drop, async_drop_in_place}; +pub use async_drop::{AsyncDrop, async_drop_in_place}; #[stable(feature = "into_future", since = "1.64.0")] pub use into_future::IntoFuture; #[stable(feature = "future_readiness_fns", since = "1.48.0")] diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index 7a6630c82d0da..f7b874b26bb74 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -801,7 +801,7 @@ impl Eq for BuildHasherDefault {} mod impls { use super::*; - use crate::{mem, slice}; + use crate::slice; macro_rules! impl_write { ($(($ty:ident, $meth:ident),)*) => {$( @@ -814,7 +814,7 @@ mod impls { #[inline] fn hash_slice(data: &[$ty], state: &mut H) { - let newlen = mem::size_of_val(data); + let newlen = size_of_val(data); 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 diff --git a/library/core/src/hash/sip.rs b/library/core/src/hash/sip.rs index 6ea3241c59354..780e522c48ebf 100644 --- a/library/core/src/hash/sip.rs +++ b/library/core/src/hash/sip.rs @@ -3,7 +3,7 @@ #![allow(deprecated)] // the types in this module are deprecated use crate::marker::PhantomData; -use crate::{cmp, mem, ptr}; +use crate::{cmp, ptr}; /// An implementation of SipHash 1-3. /// @@ -99,12 +99,12 @@ macro_rules! compress { /// `$i..$i+size_of::<$int_ty>()`, so that must be in-bounds. macro_rules! load_int_le { ($buf:expr, $i:expr, $int_ty:ident) => {{ - debug_assert!($i + mem::size_of::<$int_ty>() <= $buf.len()); + debug_assert!($i + size_of::<$int_ty>() <= $buf.len()); let mut data = 0 as $int_ty; ptr::copy_nonoverlapping( $buf.as_ptr().add($i), &mut data as *mut _ as *mut u8, - mem::size_of::<$int_ty>(), + size_of::<$int_ty>(), ); data.to_le() }}; diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 5ce282b05de73..6eefb30468931 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -4,6 +4,7 @@ //! //! Hints may be compile time or runtime. +use crate::mem::MaybeUninit; use crate::{intrinsics, ub_checks}; /// Informs the compiler that the site which is calling this function is not @@ -319,6 +320,10 @@ pub fn spin_loop() { /// This also means that this function does not offer any guarantees for cryptographic or security /// purposes. /// +/// This limitation is not specific to `black_box`; there is no mechanism in the entire Rust +/// language that can provide the guarantees required for constant-time cryptography. +/// (There is also no such mechanism in LLVM, so the same is true for every other LLVM-based compiler.) +/// /// /// /// [`std::convert::identity`]: crate::convert::identity @@ -734,3 +739,61 @@ pub const fn unlikely(b: bool) -> bool { pub const fn cold_path() { crate::intrinsics::cold_path() } + +/// Returns either `true_val` or `false_val` depending on the value of +/// `condition`, with a hint to the compiler that `condition` is unlikely to be +/// correctly predicted by a CPU’s branch predictor. +/// +/// This method is functionally equivalent to +/// ```ignore (this is just for illustrative purposes) +/// fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { +/// if b { true_val } else { false_val } +/// } +/// ``` +/// but might generate different assembly. In particular, on platforms with +/// a conditional move or select instruction (like `cmov` on x86 or `csel` +/// on ARM) the optimizer might use these instructions to avoid branches, +/// which can benefit performance if the branch predictor is struggling +/// with predicting `condition`, such as in an implementation of binary +/// search. +/// +/// Note however that this lowering is not guaranteed (on any platform) and +/// should not be relied upon when trying to write cryptographic constant-time +/// code. Also be aware that this lowering might *decrease* performance if +/// `condition` is well-predictable. It is advisable to perform benchmarks to +/// tell if this function is useful. +/// +/// # Examples +/// +/// Distribute values evenly between two buckets: +/// ``` +/// use std::hash::BuildHasher; +/// use std::hint; +/// +/// fn append(hasher: &H, v: i32, bucket_one: &mut Vec, bucket_two: &mut Vec) { +/// let hash = hasher.hash_one(&v); +/// let bucket = hint::select_unpredictable(hash % 2 == 0, bucket_one, bucket_two); +/// bucket.push(v); +/// } +/// # let hasher = std::collections::hash_map::RandomState::new(); +/// # let mut bucket_one = Vec::new(); +/// # let mut bucket_two = Vec::new(); +/// # append(&hasher, 42, &mut bucket_one, &mut bucket_two); +/// # assert_eq!(bucket_one.len() + bucket_two.len(), 1); +/// ``` +#[inline(always)] +#[stable(feature = "select_unpredictable", since = "1.88.0")] +pub fn select_unpredictable(condition: bool, true_val: T, false_val: T) -> T { + // FIXME(https://github.com/rust-lang/unsafe-code-guidelines/issues/245): + // Change this to use ManuallyDrop instead. + let mut true_val = MaybeUninit::new(true_val); + let mut false_val = MaybeUninit::new(false_val); + // SAFETY: The value that is not selected is dropped, and the selected one + // is returned. This is necessary because the intrinsic doesn't drop the + // value that is not selected. + unsafe { + crate::intrinsics::select_unpredictable(!condition, &mut true_val, &mut false_val) + .assume_init_drop(); + crate::intrinsics::select_unpredictable(condition, true_val, false_val).assume_init() + } +} diff --git a/library/core/src/internal_macros.rs b/library/core/src/internal_macros.rs index fe4fa80263c28..2aaefba2468bb 100644 --- a/library/core/src/internal_macros.rs +++ b/library/core/src/internal_macros.rs @@ -120,80 +120,3 @@ macro_rules! impl_fn_for_zst { )+ } } - -/// A macro for defining `#[cfg]` if-else statements. -/// -/// `cfg_if` is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade -/// of `#[cfg]` cases, emitting the implementation which matches first. -/// -/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code without having to -/// rewrite each clause multiple times. -/// -/// # Example -/// -/// ```ignore(cannot-test-this-because-non-exported-macro) -/// cfg_if! { -/// if #[cfg(unix)] { -/// fn foo() { /* unix specific functionality */ } -/// } else if #[cfg(target_pointer_width = "32")] { -/// fn foo() { /* non-unix, 32-bit functionality */ } -/// } else { -/// fn foo() { /* fallback implementation */ } -/// } -/// } -/// -/// # fn main() {} -/// ``` -// This is a copy of `cfg_if!` from the `cfg_if` crate. -// The recursive invocations should use $crate if this is ever exported. -macro_rules! cfg_if { - // match if/else chains with a final `else` - ( - $( - if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* } - ) else+ - else { $( $e_tokens:tt )* } - ) => { - cfg_if! { - @__items () ; - $( - (( $i_meta ) ( $( $i_tokens )* )) , - )+ - (() ( $( $e_tokens )* )) , - } - }; - - // Internal and recursive macro to emit all the items - // - // Collects all the previous cfgs in a list at the beginning, so they can be - // negated. After the semicolon is all the remaining items. - (@__items ( $( $_:meta , )* ) ; ) => {}; - ( - @__items ( $( $no:meta , )* ) ; - (( $( $yes:meta )? ) ( $( $tokens:tt )* )) , - $( $rest:tt , )* - ) => { - // Emit all items within one block, applying an appropriate #[cfg]. The - // #[cfg] will require all `$yes` matchers specified and must also negate - // all previous matchers. - #[cfg(all( - $( $yes , )? - not(any( $( $no ),* )) - ))] - cfg_if! { @__identity $( $tokens )* } - - // Recurse to emit all other items in `$rest`, and when we do so add all - // our `$yes` matchers to the list of `$no` matchers as future emissions - // will have to negate everything we just matched as well. - cfg_if! { - @__items ( $( $no , )* $( $yes , )? ) ; - $( $rest , )* - } - }; - - // Internal macro to make __apply work out right for different match types, - // because of how macros match/expand stuff. - (@__identity $( $tokens:tt )* ) => { - $( $tokens )* - }; -} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 38a60338e74ed..23bafa778bc6b 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -5,14 +5,11 @@ //! //! # 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, it needs to be declared in the "new" -//! style, i.e. as a `#[rustc_intrinsic]` function, not inside an `extern` block. Then copy the -//! implementation from to +//! In order to make an intrinsic unstable usable at compile-time, copy the implementation from +//! to //! -//! and make the intrinsic declaration a `const fn`. +//! and make the intrinsic declaration below a `const fn`. This should be done in coordination with +//! wg-const-eval. //! //! If an intrinsic is supposed to be used from a `const fn` with a `rustc_const_stable` attribute, //! `#[rustc_intrinsic_const_stable_indirect]` needs to be added to the intrinsic. Such a change requires @@ -65,8 +62,7 @@ #![allow(missing_docs)] use crate::marker::{DiscriminantKind, Tuple}; -use crate::mem::SizedTypeProperties; -use crate::{ptr, ub_checks}; +use crate::ptr; pub mod fallback; pub mod mir; @@ -77,19 +73,11 @@ pub mod simd; #[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_allowed_through_unstable_modules = "import this function via `std::ptr` instead"] -#[deprecated(note = "no longer an intrinsic - use `ptr::drop_in_place` directly", since = "1.52.0")] -#[inline] -pub unsafe fn drop_in_place(to_drop: *mut T) { - // SAFETY: see `ptr::drop_in_place` - unsafe { crate::ptr::drop_in_place(to_drop) } -} - // 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. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -97,8 +85,9 @@ pub unsafe fn drop_in_place(to_drop: *mut T) { /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_relaxed_relaxed(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_relaxed_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -106,8 +95,9 @@ pub unsafe fn atomic_cxchg_relaxed_relaxed(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_relaxed_acquire(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_relaxed_acquire(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -115,8 +105,9 @@ pub unsafe fn atomic_cxchg_relaxed_acquire(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_relaxed_seqcst(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_relaxed_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -124,8 +115,9 @@ pub unsafe fn atomic_cxchg_relaxed_seqcst(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_acquire_relaxed(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_acquire_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -133,8 +125,9 @@ pub unsafe fn atomic_cxchg_acquire_relaxed(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_acquire_acquire(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_acquire_acquire(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -142,8 +135,9 @@ pub unsafe fn atomic_cxchg_acquire_acquire(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_acquire_seqcst(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_acquire_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -151,8 +145,9 @@ pub unsafe fn atomic_cxchg_acquire_seqcst(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_release_relaxed(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_release_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -160,8 +155,9 @@ pub unsafe fn atomic_cxchg_release_relaxed(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_release_acquire(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_release_acquire(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -169,8 +165,9 @@ pub unsafe fn atomic_cxchg_release_acquire(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_release_seqcst(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_release_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -178,8 +175,9 @@ pub unsafe fn atomic_cxchg_release_seqcst(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_acqrel_relaxed(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_acqrel_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -187,8 +185,9 @@ pub unsafe fn atomic_cxchg_acqrel_relaxed(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_acqrel_acquire(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_acqrel_acquire(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -196,8 +195,9 @@ pub unsafe fn atomic_cxchg_acqrel_acquire(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_acqrel_seqcst(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_acqrel_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -205,8 +205,9 @@ pub unsafe fn atomic_cxchg_acqrel_seqcst(_dst: *mut T, _old: T, _src: T /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_seqcst_relaxed(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_seqcst_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -214,8 +215,9 @@ pub unsafe fn atomic_cxchg_seqcst_relaxed(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_seqcst_acquire(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_seqcst_acquire(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange` method by passing @@ -223,9 +225,10 @@ pub unsafe fn atomic_cxchg_seqcst_acquire(_dst: *mut T, _old: T, _src: /// For example, [`AtomicBool::compare_exchange`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchg_seqcst_seqcst(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchg_seqcst_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -239,6 +242,7 @@ pub unsafe fn atomic_cxchgweak_relaxed_relaxed( _src: T, ) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -252,6 +256,7 @@ pub unsafe fn atomic_cxchgweak_relaxed_acquire( _src: T, ) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -259,9 +264,9 @@ pub unsafe fn atomic_cxchgweak_relaxed_acquire( /// For example, [`AtomicBool::compare_exchange_weak`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchgweak_relaxed_seqcst(_dst: *mut T, _old: T, _src: T) --> (T, bool); +pub unsafe fn atomic_cxchgweak_relaxed_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -275,6 +280,7 @@ pub unsafe fn atomic_cxchgweak_acquire_relaxed( _src: T, ) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -288,6 +294,7 @@ pub unsafe fn atomic_cxchgweak_acquire_acquire( _src: T, ) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -295,9 +302,9 @@ pub unsafe fn atomic_cxchgweak_acquire_acquire( /// For example, [`AtomicBool::compare_exchange_weak`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchgweak_acquire_seqcst(_dst: *mut T, _old: T, _src: T) --> (T, bool); +pub unsafe fn atomic_cxchgweak_acquire_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -311,6 +318,7 @@ pub unsafe fn atomic_cxchgweak_release_relaxed( _src: T, ) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -324,6 +332,7 @@ pub unsafe fn atomic_cxchgweak_release_acquire( _src: T, ) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -331,9 +340,9 @@ pub unsafe fn atomic_cxchgweak_release_acquire( /// For example, [`AtomicBool::compare_exchange_weak`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchgweak_release_seqcst(_dst: *mut T, _old: T, _src: T) --> (T, bool); +pub unsafe fn atomic_cxchgweak_release_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -341,9 +350,9 @@ pub unsafe fn atomic_cxchgweak_release_seqcst(_dst: *mut T, _old: T, _s /// For example, [`AtomicBool::compare_exchange_weak`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchgweak_acqrel_relaxed(_dst: *mut T, _old: T, _src: T) --> (T, bool); +pub unsafe fn atomic_cxchgweak_acqrel_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -351,9 +360,9 @@ pub unsafe fn atomic_cxchgweak_acqrel_relaxed(_dst: *mut T, _old: T, _s /// For example, [`AtomicBool::compare_exchange_weak`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchgweak_acqrel_acquire(_dst: *mut T, _old: T, _src: T) --> (T, bool); +pub unsafe fn atomic_cxchgweak_acqrel_acquire(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -361,8 +370,9 @@ pub unsafe fn atomic_cxchgweak_acqrel_acquire(_dst: *mut T, _old: T, _s /// For example, [`AtomicBool::compare_exchange_weak`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchgweak_acqrel_seqcst(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchgweak_acqrel_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -370,9 +380,9 @@ pub unsafe fn atomic_cxchgweak_acqrel_seqcst(_dst: *mut T, _old: T, _sr /// For example, [`AtomicBool::compare_exchange_weak`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchgweak_seqcst_relaxed(_dst: *mut T, _old: T, _src: T) --> (T, bool); +pub unsafe fn atomic_cxchgweak_seqcst_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -380,9 +390,9 @@ pub unsafe fn atomic_cxchgweak_seqcst_relaxed(_dst: *mut T, _old: T, _s /// For example, [`AtomicBool::compare_exchange_weak`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchgweak_seqcst_acquire(_dst: *mut T, _old: T, _src: T) --> (T, bool); +pub unsafe fn atomic_cxchgweak_seqcst_acquire(dst: *mut T, old: T, src: T) -> (T, bool); /// Stores a value if the current value is the same as the `old` value. +/// `T` must be an integer or pointer type. /// /// The stabilized version of this intrinsic is available on the /// [`atomic`] types via the `compare_exchange_weak` method by passing @@ -390,520 +400,629 @@ pub unsafe fn atomic_cxchgweak_seqcst_acquire(_dst: *mut T, _old: T, _s /// For example, [`AtomicBool::compare_exchange_weak`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_cxchgweak_seqcst_seqcst(_dst: *mut T, _old: T, _src: T) -> (T, bool); +pub unsafe fn atomic_cxchgweak_seqcst_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); /// Loads the current value of the pointer. +/// `T` must be an integer or pointer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_load_seqcst(_src: *const T) -> T; +pub unsafe fn atomic_load_seqcst(src: *const T) -> T; /// Loads the current value of the pointer. +/// `T` must be an integer or pointer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_load_acquire(_src: *const T) -> T; +pub unsafe fn atomic_load_acquire(src: *const T) -> T; /// Loads the current value of the pointer. +/// `T` must be an integer or pointer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_load_relaxed(_src: *const T) -> T; -/// Do NOT use this intrinsic; "unordered" operations do not exist in our memory model! -/// In terms of the Rust Abstract Machine, this operation is equivalent to `src.read()`, -/// i.e., it performs a non-atomic read. -#[rustc_intrinsic] -#[rustc_nounwind] -pub unsafe fn atomic_load_unordered(_src: *const T) -> T; +pub unsafe fn atomic_load_relaxed(src: *const T) -> T; /// Stores the value at the specified memory location. +/// `T` must be an integer or pointer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_store_seqcst(_dst: *mut T, _val: T); +pub unsafe fn atomic_store_seqcst(dst: *mut T, val: T); /// Stores the value at the specified memory location. +/// `T` must be an integer or pointer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_store_release(_dst: *mut T, _val: T); +pub unsafe fn atomic_store_release(dst: *mut T, val: T); /// Stores the value at the specified memory location. +/// `T` must be an integer or pointer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_store_relaxed(_dst: *mut T, _val: T); -/// Do NOT use this intrinsic; "unordered" operations do not exist in our memory model! -/// In terms of the Rust Abstract Machine, this operation is equivalent to `dst.write(val)`, -/// i.e., it performs a non-atomic write. -#[rustc_intrinsic] -#[rustc_nounwind] -pub unsafe fn atomic_store_unordered(_dst: *mut T, _val: T); +pub unsafe fn atomic_store_relaxed(dst: *mut T, val: T); /// Stores the value at the specified memory location, returning the old value. +/// `T` must be an integer or pointer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xchg_seqcst(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xchg_seqcst(dst: *mut T, src: T) -> T; /// Stores the value at the specified memory location, returning the old value. +/// `T` must be an integer or pointer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xchg_acquire(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xchg_acquire(dst: *mut T, src: T) -> T; /// Stores the value at the specified memory location, returning the old value. +/// `T` must be an integer or pointer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xchg_release(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xchg_release(dst: *mut T, src: T) -> T; /// Stores the value at the specified memory location, returning the old value. +/// `T` must be an integer or pointer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xchg_acqrel(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xchg_acqrel(dst: *mut T, src: T) -> T; /// Stores the value at the specified memory location, returning the old value. +/// `T` must be an integer or pointer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xchg_relaxed(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xchg_relaxed(dst: *mut T, src: T) -> T; /// Adds to the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xadd_seqcst(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xadd_seqcst(dst: *mut T, src: T) -> T; /// Adds to the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xadd_acquire(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xadd_acquire(dst: *mut T, src: T) -> T; /// Adds to the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xadd_release(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xadd_release(dst: *mut T, src: T) -> T; /// Adds to the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xadd_acqrel(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xadd_acqrel(dst: *mut T, src: T) -> T; /// Adds to the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xadd_relaxed(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xadd_relaxed(dst: *mut T, src: T) -> T; /// Subtract from the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xsub_seqcst(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xsub_seqcst(dst: *mut T, src: T) -> T; /// Subtract from the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xsub_acquire(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xsub_acquire(dst: *mut T, src: T) -> T; /// Subtract from the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xsub_release(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xsub_release(dst: *mut T, src: T) -> T; /// Subtract from the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xsub_acqrel(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xsub_acqrel(dst: *mut T, src: T) -> T; /// Subtract from the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xsub_relaxed(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xsub_relaxed(dst: *mut T, src: T) -> T; /// Bitwise and with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_and_seqcst(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_and_seqcst(dst: *mut T, src: T) -> T; /// Bitwise and with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_and_acquire(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_and_acquire(dst: *mut T, src: T) -> T; /// Bitwise and with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_and_release(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_and_release(dst: *mut T, src: T) -> T; /// Bitwise and with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_and_acqrel(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_and_acqrel(dst: *mut T, src: T) -> T; /// Bitwise and with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_and_relaxed(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_and_relaxed(dst: *mut T, src: T) -> T; /// Bitwise nand with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_nand_seqcst(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_nand_seqcst(dst: *mut T, src: T) -> T; /// Bitwise nand with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_nand_acquire(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_nand_acquire(dst: *mut T, src: T) -> T; /// Bitwise nand with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_nand_release(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_nand_release(dst: *mut T, src: T) -> T; /// Bitwise nand with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_nand_acqrel(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_nand_acqrel(dst: *mut T, src: T) -> T; /// Bitwise nand with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_nand_relaxed(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_nand_relaxed(dst: *mut T, src: T) -> T; /// Bitwise or with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_or_seqcst(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_or_seqcst(dst: *mut T, src: T) -> T; /// Bitwise or with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_or_acquire(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_or_acquire(dst: *mut T, src: T) -> T; /// Bitwise or with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_or_release(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_or_release(dst: *mut T, src: T) -> T; /// Bitwise or with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_or_acqrel(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_or_acqrel(dst: *mut T, src: T) -> T; /// Bitwise or with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_or_relaxed(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_or_relaxed(dst: *mut T, src: T) -> T; /// Bitwise xor with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xor_seqcst(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xor_seqcst(dst: *mut T, src: T) -> T; /// Bitwise xor with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xor_acquire(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xor_acquire(dst: *mut T, src: T) -> T; /// Bitwise xor with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xor_release(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xor_release(dst: *mut T, src: T) -> T; /// Bitwise xor with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xor_acqrel(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xor_acqrel(dst: *mut T, src: T) -> T; /// Bitwise xor with the current value, returning the previous value. +/// `T` must be an integer or pointer type. +/// If `T` is a pointer type, the provenance of `src` is ignored: both the return value and the new +/// value stored at `*dst` will have the provenance of the old value stored there. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_xor_relaxed(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_xor_relaxed(dst: *mut T, src: T) -> T; /// Maximum with the current value using a signed comparison. +/// `T` must be a signed integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_max_seqcst(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_max_seqcst(dst: *mut T, src: T) -> T; /// Maximum with the current value using a signed comparison. +/// `T` must be a signed integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_max_acquire(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_max_acquire(dst: *mut T, src: T) -> T; /// Maximum with the current value using a signed comparison. +/// `T` must be a signed integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_max_release(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_max_release(dst: *mut T, src: T) -> T; /// Maximum with the current value using a signed comparison. +/// `T` must be a signed integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_max_acqrel(_dst: *mut T, _src: T) -> T; -/// Maximum with the current value. +pub unsafe fn atomic_max_acqrel(dst: *mut T, src: T) -> T; +/// Maximum with the current value using a signed comparison. +/// `T` must be a signed integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_max_relaxed(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_max_relaxed(dst: *mut T, src: T) -> T; /// Minimum with the current value using a signed comparison. +/// `T` must be a signed integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_min_seqcst(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_min_seqcst(dst: *mut T, src: T) -> T; /// Minimum with the current value using a signed comparison. +/// `T` must be a signed integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_min_acquire(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_min_acquire(dst: *mut T, src: T) -> T; /// Minimum with the current value using a signed comparison. +/// `T` must be a signed integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_min_release(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_min_release(dst: *mut T, src: T) -> T; /// Minimum with the current value using a signed comparison. +/// `T` must be a signed integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_min_acqrel(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_min_acqrel(dst: *mut T, src: T) -> T; /// Minimum with the current value using a signed comparison. +/// `T` must be a signed integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_min_relaxed(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_min_relaxed(dst: *mut T, src: T) -> T; /// Minimum with the current value using an unsigned comparison. +/// `T` must be an unsigned integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_umin_seqcst(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_umin_seqcst(dst: *mut T, src: T) -> T; /// Minimum with the current value using an unsigned comparison. +/// `T` must be an unsigned integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_umin_acquire(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_umin_acquire(dst: *mut T, src: T) -> T; /// Minimum with the current value using an unsigned comparison. +/// `T` must be an unsigned integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_umin_release(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_umin_release(dst: *mut T, src: T) -> T; /// Minimum with the current value using an unsigned comparison. +/// `T` must be an unsigned integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_umin_acqrel(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_umin_acqrel(dst: *mut T, src: T) -> T; /// Minimum with the current value using an unsigned comparison. +/// `T` must be an unsigned integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_umin_relaxed(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_umin_relaxed(dst: *mut T, src: T) -> T; /// Maximum with the current value using an unsigned comparison. +/// `T` must be an unsigned integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_umax_seqcst(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_umax_seqcst(dst: *mut T, src: T) -> T; /// Maximum with the current value using an unsigned comparison. +/// `T` must be an unsigned integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_umax_acquire(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_umax_acquire(dst: *mut T, src: T) -> T; /// Maximum with the current value using an unsigned comparison. +/// `T` must be an unsigned integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_umax_release(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_umax_release(dst: *mut T, src: T) -> T; /// Maximum with the current value using an unsigned comparison. +/// `T` must be an unsigned integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_umax_acqrel(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_umax_acqrel(dst: *mut T, src: T) -> T; /// Maximum with the current value using an unsigned comparison. +/// `T` must be an unsigned integer type. /// /// 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`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn atomic_umax_relaxed(_dst: *mut T, _src: T) -> T; +pub unsafe fn atomic_umax_relaxed(dst: *mut T, src: T) -> T; /// An atomic fence. /// @@ -1002,7 +1121,7 @@ pub unsafe fn atomic_singlethreadfence_acqrel(); /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn prefetch_read_data(_data: *const T, _locality: i32); +pub unsafe 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 @@ -1014,7 +1133,7 @@ pub unsafe fn prefetch_read_data(_data: *const T, _locality: i32); /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn prefetch_write_data(_data: *const T, _locality: i32); +pub unsafe 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 @@ -1026,7 +1145,7 @@ pub unsafe fn prefetch_write_data(_data: *const T, _locality: i32); /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn prefetch_read_instruction(_data: *const T, _locality: i32); +pub unsafe 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 @@ -1038,7 +1157,7 @@ pub unsafe fn prefetch_read_instruction(_data: *const T, _locality: i32); /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn prefetch_write_instruction(_data: *const T, _locality: i32); +pub unsafe fn prefetch_write_instruction(data: *const T, locality: i32); /// Executes a breakpoint trap, for inspection by a debugger. /// @@ -1185,7 +1304,9 @@ pub const fn unlikely(b: bool) -> bool { /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The public form of this instrinsic is [`bool::select_unpredictable`]. +/// The public form of this instrinsic is [`core::hint::select_unpredictable`]. +/// However unlike the public form, the intrinsic will not drop the value that +/// is not selected. #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] @@ -1354,6 +1475,7 @@ pub const fn forget(_: T); /// Turning raw bytes (`[u8; SZ]`) into `u32`, `f64`, etc.: /// /// ``` +/// # #![allow(unnecessary_transmutes)] /// let raw_bytes = [0x78, 0x56, 0x34, 0x12]; /// /// let num = unsafe { @@ -1539,7 +1661,7 @@ pub const fn forget(_: T); #[rustc_diagnostic_item = "transmute"] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn transmute(_src: Src) -> Dst; +pub const unsafe fn transmute(src: Src) -> Dst; /// Like [`transmute`], but even less checked at compile-time: rather than /// giving an error for `size_of::() != size_of::()`, it's @@ -1553,7 +1675,7 @@ pub const unsafe fn transmute(_src: Src) -> Dst; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn transmute_unchecked(_src: Src) -> Dst; +pub const unsafe fn transmute_unchecked(src: Src) -> Dst; /// Returns `true` if the actual type given as `T` requires drop /// glue; returns `false` if the actual type provided for `T` @@ -1593,7 +1715,7 @@ pub const fn needs_drop() -> bool; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn offset(_dst: Ptr, _offset: Delta) -> Ptr; +pub const unsafe fn offset(dst: Ptr, offset: Delta) -> Ptr; /// Calculates the offset from a pointer, potentially wrapping. /// @@ -1612,7 +1734,7 @@ pub const unsafe fn offset(_dst: Ptr, _offset: Delta) -> Ptr; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn arith_offset(_dst: *const T, _offset: isize) -> *const T; +pub const unsafe fn arith_offset(dst: *const T, offset: isize) -> *const T; /// Masks out bits of the pointer according to a mask. /// @@ -1624,19 +1746,23 @@ pub const unsafe fn arith_offset(_dst: *const T, _offset: isize) -> *const T; /// Consider using [`pointer::mask`] instead. #[rustc_nounwind] #[rustc_intrinsic] -pub fn ptr_mask(_ptr: *const T, _mask: usize) -> *const T; +pub fn ptr_mask(ptr: *const T, mask: usize) -> *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. +/// # Safety +/// +/// The safety requirements are consistent with [`copy_nonoverlapping`] +/// while the read and write behaviors are volatile, +/// which means it will not be optimized out unless `_count` or `size_of::()` is equal to zero. +/// +/// [`copy_nonoverlapping`]: ptr::copy_nonoverlapping #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn volatile_copy_nonoverlapping_memory(_dst: *mut T, _src: *const T, _count: usize); +pub unsafe 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::()` @@ -1647,31 +1773,34 @@ pub unsafe fn volatile_copy_nonoverlapping_memory(_dst: *mut T, _src: *const /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn volatile_copy_memory(_dst: *mut T, _src: *const T, _count: usize); +pub unsafe 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. +/// # Safety +/// +/// The safety requirements are consistent with [`write_bytes`] while the write behavior is volatile, +/// which means it will not be optimized out unless `_count` or `size_of::()` is equal to zero. +/// +/// [`write_bytes`]: ptr::write_bytes #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn volatile_set_memory(_dst: *mut T, _val: u8, _count: usize); +pub unsafe 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 [`core::ptr::read_volatile`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn volatile_load(_src: *const T) -> T; +pub unsafe fn volatile_load(src: *const T) -> T; /// Performs a volatile store to the `dst` pointer. /// /// The stabilized version of this intrinsic is [`core::ptr::write_volatile`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn volatile_store(_dst: *mut T, _val: T); +pub unsafe fn volatile_store(dst: *mut T, val: T); /// Performs a volatile load from the `src` pointer /// The pointer is not required to be aligned. @@ -1680,7 +1809,7 @@ pub unsafe fn volatile_store(_dst: *mut T, _val: T); #[rustc_intrinsic] #[rustc_nounwind] #[rustc_diagnostic_item = "intrinsics_unaligned_volatile_load"] -pub unsafe fn unaligned_volatile_load(_src: *const T) -> T; +pub unsafe fn unaligned_volatile_load(src: *const T) -> T; /// Performs a volatile store to the `dst` pointer. /// The pointer is not required to be aligned. /// @@ -1688,7 +1817,7 @@ pub unsafe fn unaligned_volatile_load(_src: *const T) -> T; #[rustc_intrinsic] #[rustc_nounwind] #[rustc_diagnostic_item = "intrinsics_unaligned_volatile_store"] -pub unsafe fn unaligned_volatile_store(_dst: *mut T, _val: T); +pub unsafe fn unaligned_volatile_store(dst: *mut T, val: T); /// Returns the square root of an `f16` /// @@ -1696,28 +1825,28 @@ pub unsafe fn unaligned_volatile_store(_dst: *mut T, _val: T); /// [`f16::sqrt`](../../std/primitive.f16.html#method.sqrt) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sqrtf16(_x: f16) -> f16; +pub unsafe fn sqrtf16(x: f16) -> f16; /// Returns the square root of an `f32` /// /// The stabilized version of this intrinsic is /// [`f32::sqrt`](../../std/primitive.f32.html#method.sqrt) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sqrtf32(_x: f32) -> f32; +pub unsafe fn sqrtf32(x: f32) -> f32; /// Returns the square root of an `f64` /// /// The stabilized version of this intrinsic is /// [`f64::sqrt`](../../std/primitive.f64.html#method.sqrt) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sqrtf64(_x: f64) -> f64; +pub unsafe fn sqrtf64(x: f64) -> f64; /// Returns the square root of an `f128` /// /// The stabilized version of this intrinsic is /// [`f128::sqrt`](../../std/primitive.f128.html#method.sqrt) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sqrtf128(_x: f128) -> f128; +pub unsafe fn sqrtf128(x: f128) -> f128; /// Raises an `f16` to an integer power. /// @@ -1725,28 +1854,28 @@ pub unsafe fn sqrtf128(_x: f128) -> f128; /// [`f16::powi`](../../std/primitive.f16.html#method.powi) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powif16(_a: f16, _x: i32) -> f16; +pub unsafe fn powif16(a: f16, x: i32) -> f16; /// Raises an `f32` to an integer power. /// /// The stabilized version of this intrinsic is /// [`f32::powi`](../../std/primitive.f32.html#method.powi) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powif32(_a: f32, _x: i32) -> f32; +pub unsafe fn powif32(a: f32, x: i32) -> f32; /// Raises an `f64` to an integer power. /// /// The stabilized version of this intrinsic is /// [`f64::powi`](../../std/primitive.f64.html#method.powi) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powif64(_a: f64, _x: i32) -> f64; +pub unsafe fn powif64(a: f64, x: i32) -> f64; /// Raises an `f128` to an integer power. /// /// The stabilized version of this intrinsic is /// [`f128::powi`](../../std/primitive.f128.html#method.powi) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powif128(_a: f128, _x: i32) -> f128; +pub unsafe fn powif128(a: f128, x: i32) -> f128; /// Returns the sine of an `f16`. /// @@ -1754,28 +1883,28 @@ pub unsafe fn powif128(_a: f128, _x: i32) -> f128; /// [`f16::sin`](../../std/primitive.f16.html#method.sin) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sinf16(_x: f16) -> f16; +pub unsafe fn sinf16(x: f16) -> f16; /// Returns the sine of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::sin`](../../std/primitive.f32.html#method.sin) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sinf32(_x: f32) -> f32; +pub unsafe fn sinf32(x: f32) -> f32; /// Returns the sine of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::sin`](../../std/primitive.f64.html#method.sin) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sinf64(_x: f64) -> f64; +pub unsafe fn sinf64(x: f64) -> f64; /// Returns the sine of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::sin`](../../std/primitive.f128.html#method.sin) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn sinf128(_x: f128) -> f128; +pub unsafe fn sinf128(x: f128) -> f128; /// Returns the cosine of an `f16`. /// @@ -1783,28 +1912,28 @@ pub unsafe fn sinf128(_x: f128) -> f128; /// [`f16::cos`](../../std/primitive.f16.html#method.cos) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn cosf16(_x: f16) -> f16; +pub unsafe fn cosf16(x: f16) -> f16; /// Returns the cosine of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::cos`](../../std/primitive.f32.html#method.cos) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn cosf32(_x: f32) -> f32; +pub unsafe fn cosf32(x: f32) -> f32; /// Returns the cosine of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::cos`](../../std/primitive.f64.html#method.cos) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn cosf64(_x: f64) -> f64; +pub unsafe fn cosf64(x: f64) -> f64; /// Returns the cosine of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::cos`](../../std/primitive.f128.html#method.cos) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn cosf128(_x: f128) -> f128; +pub unsafe fn cosf128(x: f128) -> f128; /// Raises an `f16` to an `f16` power. /// @@ -1812,28 +1941,28 @@ pub unsafe fn cosf128(_x: f128) -> f128; /// [`f16::powf`](../../std/primitive.f16.html#method.powf) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powf16(_a: f16, _x: f16) -> f16; +pub unsafe fn powf16(a: f16, x: f16) -> f16; /// Raises an `f32` to an `f32` power. /// /// The stabilized version of this intrinsic is /// [`f32::powf`](../../std/primitive.f32.html#method.powf) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powf32(_a: f32, _x: f32) -> f32; +pub unsafe fn powf32(a: f32, x: f32) -> f32; /// Raises an `f64` to an `f64` power. /// /// The stabilized version of this intrinsic is /// [`f64::powf`](../../std/primitive.f64.html#method.powf) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powf64(_a: f64, _x: f64) -> f64; +pub unsafe fn powf64(a: f64, x: f64) -> f64; /// Raises an `f128` to an `f128` power. /// /// The stabilized version of this intrinsic is /// [`f128::powf`](../../std/primitive.f128.html#method.powf) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn powf128(_a: f128, _x: f128) -> f128; +pub unsafe fn powf128(a: f128, x: f128) -> f128; /// Returns the exponential of an `f16`. /// @@ -1841,28 +1970,28 @@ pub unsafe fn powf128(_a: f128, _x: f128) -> f128; /// [`f16::exp`](../../std/primitive.f16.html#method.exp) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn expf16(_x: f16) -> f16; +pub unsafe fn expf16(x: f16) -> f16; /// Returns the exponential of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::exp`](../../std/primitive.f32.html#method.exp) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn expf32(_x: f32) -> f32; +pub unsafe fn expf32(x: f32) -> f32; /// Returns the exponential of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::exp`](../../std/primitive.f64.html#method.exp) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn expf64(_x: f64) -> f64; +pub unsafe fn expf64(x: f64) -> f64; /// Returns the exponential of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::exp`](../../std/primitive.f128.html#method.exp) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn expf128(_x: f128) -> f128; +pub unsafe fn expf128(x: f128) -> f128; /// Returns 2 raised to the power of an `f16`. /// @@ -1870,28 +1999,28 @@ pub unsafe fn expf128(_x: f128) -> f128; /// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn exp2f16(_x: f16) -> f16; +pub unsafe fn exp2f16(x: f16) -> f16; /// Returns 2 raised to the power of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::exp2`](../../std/primitive.f32.html#method.exp2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn exp2f32(_x: f32) -> f32; +pub unsafe fn exp2f32(x: f32) -> f32; /// Returns 2 raised to the power of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn exp2f64(_x: f64) -> f64; +pub unsafe fn exp2f64(x: f64) -> f64; /// Returns 2 raised to the power of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn exp2f128(_x: f128) -> f128; +pub unsafe fn exp2f128(x: f128) -> f128; /// Returns the natural logarithm of an `f16`. /// @@ -1899,28 +2028,28 @@ pub unsafe fn exp2f128(_x: f128) -> f128; /// [`f16::ln`](../../std/primitive.f16.html#method.ln) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn logf16(_x: f16) -> f16; +pub unsafe fn logf16(x: f16) -> f16; /// Returns the natural logarithm of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::ln`](../../std/primitive.f32.html#method.ln) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn logf32(_x: f32) -> f32; +pub unsafe fn logf32(x: f32) -> f32; /// Returns the natural logarithm of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::ln`](../../std/primitive.f64.html#method.ln) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn logf64(_x: f64) -> f64; +pub unsafe fn logf64(x: f64) -> f64; /// Returns the natural logarithm of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::ln`](../../std/primitive.f128.html#method.ln) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn logf128(_x: f128) -> f128; +pub unsafe fn logf128(x: f128) -> f128; /// Returns the base 10 logarithm of an `f16`. /// @@ -1928,28 +2057,28 @@ pub unsafe fn logf128(_x: f128) -> f128; /// [`f16::log10`](../../std/primitive.f16.html#method.log10) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log10f16(_x: f16) -> f16; +pub unsafe fn log10f16(x: f16) -> f16; /// Returns the base 10 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::log10`](../../std/primitive.f32.html#method.log10) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log10f32(_x: f32) -> f32; +pub unsafe fn log10f32(x: f32) -> f32; /// Returns the base 10 logarithm of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::log10`](../../std/primitive.f64.html#method.log10) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log10f64(_x: f64) -> f64; +pub unsafe fn log10f64(x: f64) -> f64; /// Returns the base 10 logarithm of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::log10`](../../std/primitive.f128.html#method.log10) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log10f128(_x: f128) -> f128; +pub unsafe fn log10f128(x: f128) -> f128; /// Returns the base 2 logarithm of an `f16`. /// @@ -1957,28 +2086,28 @@ pub unsafe fn log10f128(_x: f128) -> f128; /// [`f16::log2`](../../std/primitive.f16.html#method.log2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log2f16(_x: f16) -> f16; +pub unsafe fn log2f16(x: f16) -> f16; /// Returns the base 2 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::log2`](../../std/primitive.f32.html#method.log2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log2f32(_x: f32) -> f32; +pub unsafe fn log2f32(x: f32) -> f32; /// Returns the base 2 logarithm of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::log2`](../../std/primitive.f64.html#method.log2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log2f64(_x: f64) -> f64; +pub unsafe fn log2f64(x: f64) -> f64; /// Returns the base 2 logarithm of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::log2`](../../std/primitive.f128.html#method.log2) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn log2f128(_x: f128) -> f128; +pub unsafe fn log2f128(x: f128) -> f128; /// Returns `a * b + c` for `f16` values. /// @@ -1986,28 +2115,28 @@ pub unsafe fn log2f128(_x: f128) -> f128; /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmaf16(_a: f16, _b: f16, _c: f16) -> f16; +pub unsafe fn fmaf16(a: f16, b: f16, c: f16) -> f16; /// Returns `a * b + c` for `f32` values. /// /// The stabilized version of this intrinsic is /// [`f32::mul_add`](../../std/primitive.f32.html#method.mul_add) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmaf32(_a: f32, _b: f32, _c: f32) -> f32; +pub unsafe fn fmaf32(a: f32, b: f32, c: f32) -> f32; /// Returns `a * b + c` for `f64` values. /// /// The stabilized version of this intrinsic is /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmaf64(_a: f64, _b: f64, _c: f64) -> f64; +pub unsafe fn fmaf64(a: f64, b: f64, c: f64) -> f64; /// Returns `a * b + c` for `f128` values. /// /// The stabilized version of this intrinsic is /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmaf128(_a: f128, _b: f128, _c: f128) -> f128; +pub unsafe fn fmaf128(a: f128, b: f128, c: f128) -> f128; /// Returns `a * b + c` for `f16` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the @@ -2021,7 +2150,7 @@ pub unsafe fn fmaf128(_a: f128, _b: f128, _c: f128) -> f128; /// example. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmuladdf16(_a: f16, _b: f16, _c: f16) -> f16; +pub unsafe fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; /// Returns `a * b + c` for `f32` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -2034,7 +2163,7 @@ pub unsafe fn fmuladdf16(_a: f16, _b: f16, _c: f16) -> f16; /// example. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmuladdf32(_a: f32, _b: f32, _c: f32) -> f32; +pub unsafe fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; /// Returns `a * b + c` for `f64` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -2047,7 +2176,7 @@ pub unsafe fn fmuladdf32(_a: f32, _b: f32, _c: f32) -> f32; /// example. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmuladdf64(_a: f64, _b: f64, _c: f64) -> f64; +pub unsafe fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; /// Returns `a * b + c` for `f128` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -2060,7 +2189,7 @@ pub unsafe fn fmuladdf64(_a: f64, _b: f64, _c: f64) -> f64; /// example. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmuladdf128(_a: f128, _b: f128, _c: f128) -> f128; +pub unsafe fn fmuladdf128(a: f128, b: f128, c: f128) -> f128; /// Returns the largest integer less than or equal to an `f16`. /// @@ -2068,28 +2197,28 @@ pub unsafe fn fmuladdf128(_a: f128, _b: f128, _c: f128) -> f128; /// [`f16::floor`](../../std/primitive.f16.html#method.floor) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn floorf16(_x: f16) -> f16; +pub unsafe fn floorf16(x: f16) -> f16; /// Returns the largest integer less than or equal to an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::floor`](../../std/primitive.f32.html#method.floor) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn floorf32(_x: f32) -> f32; +pub unsafe fn floorf32(x: f32) -> f32; /// Returns the largest integer less than or equal to an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::floor`](../../std/primitive.f64.html#method.floor) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn floorf64(_x: f64) -> f64; +pub unsafe fn floorf64(x: f64) -> f64; /// Returns the largest integer less than or equal to an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::floor`](../../std/primitive.f128.html#method.floor) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn floorf128(_x: f128) -> f128; +pub unsafe fn floorf128(x: f128) -> f128; /// Returns the smallest integer greater than or equal to an `f16`. /// @@ -2097,28 +2226,28 @@ pub unsafe fn floorf128(_x: f128) -> f128; /// [`f16::ceil`](../../std/primitive.f16.html#method.ceil) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn ceilf16(_x: f16) -> f16; +pub unsafe fn ceilf16(x: f16) -> f16; /// Returns the smallest integer greater than or equal to an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::ceil`](../../std/primitive.f32.html#method.ceil) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn ceilf32(_x: f32) -> f32; +pub unsafe fn ceilf32(x: f32) -> f32; /// Returns the smallest integer greater than or equal to an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::ceil`](../../std/primitive.f64.html#method.ceil) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn ceilf64(_x: f64) -> f64; +pub unsafe fn ceilf64(x: f64) -> f64; /// Returns the smallest integer greater than or equal to an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::ceil`](../../std/primitive.f128.html#method.ceil) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn ceilf128(_x: f128) -> f128; +pub unsafe fn ceilf128(x: f128) -> f128; /// Returns the integer part of an `f16`. /// @@ -2126,28 +2255,28 @@ pub unsafe fn ceilf128(_x: f128) -> f128; /// [`f16::trunc`](../../std/primitive.f16.html#method.trunc) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn truncf16(_x: f16) -> f16; +pub unsafe fn truncf16(x: f16) -> f16; /// Returns the integer part of an `f32`. /// /// The stabilized version of this intrinsic is /// [`f32::trunc`](../../std/primitive.f32.html#method.trunc) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn truncf32(_x: f32) -> f32; +pub unsafe fn truncf32(x: f32) -> f32; /// Returns the integer part of an `f64`. /// /// The stabilized version of this intrinsic is /// [`f64::trunc`](../../std/primitive.f64.html#method.trunc) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn truncf64(_x: f64) -> f64; +pub unsafe fn truncf64(x: f64) -> f64; /// Returns the integer part of an `f128`. /// /// The stabilized version of this intrinsic is /// [`f128::trunc`](../../std/primitive.f128.html#method.trunc) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn truncf128(_x: f128) -> f128; +pub unsafe fn truncf128(x: f128) -> f128; /// Returns the nearest integer to an `f16`. Rounds half-way cases to the number with an even /// least significant digit. @@ -2156,19 +2285,7 @@ pub unsafe fn truncf128(_x: f128) -> f128; /// [`f16::round_ties_even`](../../std/primitive.f16.html#method.round_ties_even) #[rustc_intrinsic] #[rustc_nounwind] -#[cfg(not(bootstrap))] -pub fn round_ties_even_f16(_x: f16) -> f16; - -/// To be removed on next bootstrap bump. -#[cfg(bootstrap)] -pub fn round_ties_even_f16(x: f16) -> f16 { - #[rustc_intrinsic] - #[rustc_nounwind] - unsafe fn rintf16(_x: f16) -> f16; - - // SAFETY: this intrinsic isn't actually unsafe - unsafe { rintf16(x) } -} +pub fn round_ties_even_f16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases to the number with an even /// least significant digit. @@ -2177,19 +2294,7 @@ pub fn round_ties_even_f16(x: f16) -> f16 { /// [`f32::round_ties_even`](../../std/primitive.f32.html#method.round_ties_even) #[rustc_intrinsic] #[rustc_nounwind] -#[cfg(not(bootstrap))] -pub fn round_ties_even_f32(_x: f32) -> f32; - -/// To be removed on next bootstrap bump. -#[cfg(bootstrap)] -pub fn round_ties_even_f32(x: f32) -> f32 { - #[rustc_intrinsic] - #[rustc_nounwind] - unsafe fn rintf32(_x: f32) -> f32; - - // SAFETY: this intrinsic isn't actually unsafe - unsafe { rintf32(x) } -} +pub fn round_ties_even_f32(x: f32) -> f32; /// Provided for compatibility with stdarch. DO NOT USE. #[inline(always)] @@ -2204,19 +2309,7 @@ pub unsafe fn rintf32(x: f32) -> f32 { /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) #[rustc_intrinsic] #[rustc_nounwind] -#[cfg(not(bootstrap))] -pub fn round_ties_even_f64(_x: f64) -> f64; - -/// To be removed on next bootstrap bump. -#[cfg(bootstrap)] -pub fn round_ties_even_f64(x: f64) -> f64 { - #[rustc_intrinsic] - #[rustc_nounwind] - unsafe fn rintf64(_x: f64) -> f64; - - // SAFETY: this intrinsic isn't actually unsafe - unsafe { rintf64(x) } -} +pub fn round_ties_even_f64(x: f64) -> f64; /// Provided for compatibility with stdarch. DO NOT USE. #[inline(always)] @@ -2231,19 +2324,7 @@ pub unsafe fn rintf64(x: f64) -> f64 { /// [`f128::round_ties_even`](../../std/primitive.f128.html#method.round_ties_even) #[rustc_intrinsic] #[rustc_nounwind] -#[cfg(not(bootstrap))] -pub fn round_ties_even_f128(_x: f128) -> f128; - -/// To be removed on next bootstrap bump. -#[cfg(bootstrap)] -pub fn round_ties_even_f128(x: f128) -> f128 { - #[rustc_intrinsic] - #[rustc_nounwind] - unsafe fn rintf128(_x: f128) -> f128; - - // SAFETY: this intrinsic isn't actually unsafe - unsafe { rintf128(x) } -} +pub fn round_ties_even_f128(x: f128) -> f128; /// Returns the nearest integer to an `f16`. Rounds half-way cases away from zero. /// @@ -2251,28 +2332,28 @@ pub fn round_ties_even_f128(x: f128) -> f128 { /// [`f16::round`](../../std/primitive.f16.html#method.round) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn roundf16(_x: f16) -> f16; +pub unsafe fn roundf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is /// [`f32::round`](../../std/primitive.f32.html#method.round) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn roundf32(_x: f32) -> f32; +pub unsafe 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 /// [`f64::round`](../../std/primitive.f64.html#method.round) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn roundf64(_x: f64) -> f64; +pub unsafe fn roundf64(x: f64) -> f64; /// Returns the nearest integer to an `f128`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is /// [`f128::round`](../../std/primitive.f128.html#method.round) #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn roundf128(_x: f128) -> f128; +pub unsafe fn roundf128(x: f128) -> f128; /// Float addition that allows optimizations based on algebraic rules. /// May assume inputs are finite. @@ -2280,7 +2361,7 @@ pub unsafe fn roundf128(_x: f128) -> f128; /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fadd_fast(_a: T, _b: T) -> T; +pub unsafe fn fadd_fast(a: T, b: T) -> T; /// Float subtraction that allows optimizations based on algebraic rules. /// May assume inputs are finite. @@ -2288,7 +2369,7 @@ pub unsafe fn fadd_fast(_a: T, _b: T) -> T; /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fsub_fast(_a: T, _b: T) -> T; +pub unsafe fn fsub_fast(a: T, b: T) -> T; /// Float multiplication that allows optimizations based on algebraic rules. /// May assume inputs are finite. @@ -2296,7 +2377,7 @@ pub unsafe fn fsub_fast(_a: T, _b: T) -> T; /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fmul_fast(_a: T, _b: T) -> T; +pub unsafe fn fmul_fast(a: T, b: T) -> T; /// Float division that allows optimizations based on algebraic rules. /// May assume inputs are finite. @@ -2304,7 +2385,7 @@ pub unsafe fn fmul_fast(_a: T, _b: T) -> T; /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn fdiv_fast(_a: T, _b: T) -> T; +pub unsafe fn fdiv_fast(a: T, b: T) -> T; /// Float remainder that allows optimizations based on algebraic rules. /// May assume inputs are finite. @@ -2312,7 +2393,7 @@ pub unsafe fn fdiv_fast(_a: T, _b: T) -> T; /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn frem_fast(_a: T, _b: T) -> T; +pub unsafe fn frem_fast(a: T, b: T) -> T; /// Converts with LLVM’s fptoui/fptosi, which may return undef for values out of range /// () @@ -2320,42 +2401,42 @@ pub unsafe fn frem_fast(_a: T, _b: T) -> T; /// Stabilized as [`f32::to_int_unchecked`] and [`f64::to_int_unchecked`]. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn float_to_int_unchecked(_value: Float) -> Int; +pub unsafe fn float_to_int_unchecked(value: Float) -> Int; /// Float addition that allows optimizations based on algebraic rules. /// -/// This intrinsic does not have a stable counterpart. +/// Stabilized as [`f16::algebraic_add`], [`f32::algebraic_add`], [`f64::algebraic_add`] and [`f128::algebraic_add`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn fadd_algebraic(_a: T, _b: T) -> T; +pub const fn fadd_algebraic(a: T, b: T) -> T; /// Float subtraction that allows optimizations based on algebraic rules. /// -/// This intrinsic does not have a stable counterpart. +/// Stabilized as [`f16::algebraic_sub`], [`f32::algebraic_sub`], [`f64::algebraic_sub`] and [`f128::algebraic_sub`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn fsub_algebraic(_a: T, _b: T) -> T; +pub const fn fsub_algebraic(a: T, b: T) -> T; /// Float multiplication that allows optimizations based on algebraic rules. /// -/// This intrinsic does not have a stable counterpart. +/// Stabilized as [`f16::algebraic_mul`], [`f32::algebraic_mul`], [`f64::algebraic_mul`] and [`f128::algebraic_mul`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn fmul_algebraic(_a: T, _b: T) -> T; +pub const fn fmul_algebraic(a: T, b: T) -> T; /// Float division that allows optimizations based on algebraic rules. /// -/// This intrinsic does not have a stable counterpart. +/// Stabilized as [`f16::algebraic_div`], [`f32::algebraic_div`], [`f64::algebraic_div`] and [`f128::algebraic_div`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn fdiv_algebraic(_a: T, _b: T) -> T; +pub const fn fdiv_algebraic(a: T, b: T) -> T; /// Float remainder that allows optimizations based on algebraic rules. /// -/// This intrinsic does not have a stable counterpart. +/// Stabilized as [`f16::algebraic_rem`], [`f32::algebraic_rem`], [`f64::algebraic_rem`] and [`f128::algebraic_rem`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn frem_algebraic(_a: T, _b: T) -> T; +pub const fn frem_algebraic(a: T, b: T) -> T; /// Returns the number of bits set in an integer type `T` /// @@ -2370,7 +2451,7 @@ pub fn frem_algebraic(_a: T, _b: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn ctpop(_x: T) -> u32; +pub const fn ctpop(x: T) -> u32; /// Returns the number of leading unset bits (zeroes) in an integer type `T`. /// @@ -2411,7 +2492,7 @@ pub const fn ctpop(_x: T) -> u32; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn ctlz(_x: T) -> u32; +pub const fn ctlz(x: T) -> u32; /// Like `ctlz`, but extra-unsafe as it returns `undef` when /// given an `x` with value `0`. @@ -2433,7 +2514,7 @@ pub const fn ctlz(_x: T) -> u32; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn ctlz_nonzero(_x: T) -> u32; +pub const unsafe fn ctlz_nonzero(x: T) -> u32; /// Returns the number of trailing unset bits (zeroes) in an integer type `T`. /// @@ -2474,7 +2555,7 @@ pub const unsafe fn ctlz_nonzero(_x: T) -> u32; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn cttz(_x: T) -> u32; +pub const fn cttz(x: T) -> u32; /// Like `cttz`, but extra-unsafe as it returns `undef` when /// given an `x` with value `0`. @@ -2496,7 +2577,7 @@ pub const fn cttz(_x: T) -> u32; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn cttz_nonzero(_x: T) -> u32; +pub const unsafe fn cttz_nonzero(x: T) -> u32; /// Reverses the bytes in an integer type `T`. /// @@ -2511,7 +2592,7 @@ pub const unsafe fn cttz_nonzero(_x: T) -> u32; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn bswap(_x: T) -> T; +pub const fn bswap(x: T) -> T; /// Reverses the bits in an integer type `T`. /// @@ -2526,17 +2607,19 @@ pub const fn bswap(_x: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn bitreverse(_x: T) -> T; +pub const fn bitreverse(x: T) -> T; -/// Does a three-way comparison between the two integer arguments. +/// Does a three-way comparison between the two arguments, +/// which must be of character or integer (signed or unsigned) type. /// -/// This is included as an intrinsic as it's useful to let it be one thing -/// in MIR, rather than the multiple checks and switches that make its IR -/// large and difficult to optimize. +/// This was originally added because it greatly simplified the MIR in `cmp` +/// implementations, and then LLVM 20 added a backend intrinsic for it too. /// /// The stabilized version of this intrinsic is [`Ord::cmp`]. +#[rustc_intrinsic_const_stable_indirect] +#[rustc_nounwind] #[rustc_intrinsic] -pub const fn three_way_compare(_lhs: T, _rhss: T) -> crate::cmp::Ordering; +pub const fn three_way_compare(lhs: T, rhss: T) -> crate::cmp::Ordering; /// Combine two values which have no bits in common. /// @@ -2571,7 +2654,7 @@ pub const unsafe fn disjoint_bitor(a: T, b: T #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn add_with_overflow(_x: T, _y: T) -> (T, bool); +pub const fn add_with_overflow(x: T, y: T) -> (T, bool); /// Performs checked integer subtraction /// @@ -2586,7 +2669,7 @@ pub const fn add_with_overflow(_x: T, _y: T) -> (T, bool); #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn sub_with_overflow(_x: T, _y: T) -> (T, bool); +pub const fn sub_with_overflow(x: T, y: T) -> (T, bool); /// Performs checked integer multiplication /// @@ -2601,7 +2684,7 @@ pub const fn sub_with_overflow(_x: T, _y: T) -> (T, bool); #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn mul_with_overflow(_x: T, _y: T) -> (T, bool); +pub const fn mul_with_overflow(x: T, y: T) -> (T, bool); /// Performs full-width multiplication and addition with a carry: /// `multiplier * multiplicand + addend + carry`. @@ -2635,9 +2718,10 @@ pub const fn carrying_mul_add, /// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1` /// /// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn exact_div(_x: T, _y: T) -> T; +pub const unsafe fn exact_div(x: T, y: T) -> T; /// Performs an unchecked division, resulting in undefined behavior /// where `y == 0` or `x == T::MIN && y == -1` @@ -2648,7 +2732,7 @@ pub const unsafe fn exact_div(_x: T, _y: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn unchecked_div(_x: T, _y: T) -> T; +pub const unsafe fn unchecked_div(x: T, y: T) -> T; /// Returns the remainder of an unchecked division, resulting in /// undefined behavior when `y == 0` or `x == T::MIN && y == -1` /// @@ -2658,7 +2742,7 @@ pub const unsafe fn unchecked_div(_x: T, _y: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn unchecked_rem(_x: T, _y: T) -> T; +pub const unsafe 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. @@ -2669,7 +2753,7 @@ pub const unsafe fn unchecked_rem(_x: T, _y: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn unchecked_shl(_x: T, _y: U) -> T; +pub const unsafe fn unchecked_shl(x: T, y: U) -> 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. /// @@ -2679,7 +2763,7 @@ pub const unsafe fn unchecked_shl(_x: T, _y: U) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn unchecked_shr(_x: T, _y: U) -> T; +pub const unsafe fn unchecked_shr(x: T, y: U) -> T; /// Returns the result of an unchecked addition, resulting in /// undefined behavior when `x + y > T::MAX` or `x + y < T::MIN`. @@ -2689,7 +2773,7 @@ pub const unsafe fn unchecked_shr(_x: T, _y: U) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn unchecked_add(_x: T, _y: T) -> T; +pub const unsafe 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`. @@ -2699,7 +2783,7 @@ pub const unsafe fn unchecked_add(_x: T, _y: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn unchecked_sub(_x: T, _y: T) -> T; +pub const unsafe 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`. @@ -2709,7 +2793,7 @@ pub const unsafe fn unchecked_sub(_x: T, _y: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn unchecked_mul(_x: T, _y: T) -> T; +pub const unsafe fn unchecked_mul(x: T, y: T) -> T; /// Performs rotate left. /// @@ -2724,7 +2808,7 @@ pub const unsafe fn unchecked_mul(_x: T, _y: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn rotate_left(_x: T, _shift: u32) -> T; +pub const fn rotate_left(x: T, shift: u32) -> T; /// Performs rotate right. /// @@ -2739,7 +2823,7 @@ pub const fn rotate_left(_x: T, _shift: u32) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn rotate_right(_x: T, _shift: u32) -> T; +pub const fn rotate_right(x: T, shift: u32) -> T; /// Returns (a + b) mod 2N, where N is the width of T in bits. /// @@ -2754,7 +2838,7 @@ pub const fn rotate_right(_x: T, _shift: u32) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn wrapping_add(_a: T, _b: T) -> T; +pub const fn wrapping_add(a: T, b: T) -> T; /// Returns (a - b) mod 2N, where N is the width of T in bits. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -2768,7 +2852,7 @@ pub const fn wrapping_add(_a: T, _b: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn wrapping_sub(_a: T, _b: T) -> T; +pub const fn wrapping_sub(a: T, b: T) -> T; /// Returns (a * b) mod 2N, where N is the width of T in bits. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -2782,7 +2866,7 @@ pub const fn wrapping_sub(_a: T, _b: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn wrapping_mul(_a: T, _b: T) -> T; +pub const fn wrapping_mul(a: T, b: T) -> T; /// Computes `a + b`, saturating at numeric bounds. /// @@ -2797,7 +2881,7 @@ pub const fn wrapping_mul(_a: T, _b: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn saturating_add(_a: T, _b: T) -> T; +pub const fn saturating_add(a: T, b: T) -> T; /// Computes `a - b`, saturating at numeric bounds. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -2811,7 +2895,7 @@ pub const fn saturating_add(_a: T, _b: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn saturating_sub(_a: T, _b: T) -> T; +pub const fn saturating_sub(a: T, b: T) -> T; /// This is an implementation detail of [`crate::ptr::read`] and should /// not be used anywhere else. See its comments for why this exists. @@ -2822,7 +2906,7 @@ pub const fn saturating_sub(_a: T, _b: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn read_via_copy(_ptr: *const T) -> T; +pub const unsafe fn read_via_copy(ptr: *const T) -> T; /// This is an implementation detail of [`crate::ptr::write`] and should /// not be used anywhere else. See its comments for why this exists. @@ -2833,7 +2917,7 @@ pub const unsafe fn read_via_copy(_ptr: *const T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn write_via_move(_ptr: *mut T, _value: T); +pub const unsafe fn write_via_move(ptr: *mut T, value: T); /// Returns the value of the discriminant for the variant in 'v'; /// if `T` has no discriminant, returns `0`. @@ -2847,10 +2931,11 @@ pub const unsafe fn write_via_move(_ptr: *mut T, _value: T); #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn discriminant_value(_v: &T) -> ::Discriminant; +pub const fn discriminant_value(v: &T) -> ::Discriminant; /// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the /// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs. +/// Returns `1` if unwinding occurred and `catch_fn` was called; returns `0` otherwise. /// /// `catch_fn` must not unwind. /// @@ -2881,19 +2966,19 @@ pub unsafe fn catch_unwind( /// in ways that are not allowed for regular writes). #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn nontemporal_store(_ptr: *mut T, _val: T); +pub unsafe fn nontemporal_store(ptr: *mut T, val: T); /// See documentation of `<*const T>::offset_from` for details. #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn ptr_offset_from(_ptr: *const T, _base: *const T) -> isize; +pub const unsafe fn ptr_offset_from(ptr: *const T, base: *const T) -> isize; -/// See documentation of `<*const T>::sub_ptr` for details. +/// See documentation of `<*const T>::offset_from_unsigned` for details. #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_const_stable_indirect] -pub const unsafe fn ptr_offset_from_unsigned(_ptr: *const T, _base: *const T) -> usize; +pub const unsafe fn ptr_offset_from_unsigned(ptr: *const T, base: *const T) -> usize; /// See documentation of `<*const T>::guaranteed_eq` for details. /// Returns `2` if the result is unknown. @@ -2933,7 +3018,7 @@ pub const fn ptr_guaranteed_cmp(ptr: *const T, other: *const T) -> u8 { /// which is UB if any of their inputs are `undef`.) #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn raw_eq(_a: &T, _b: &T) -> bool; +pub const unsafe fn raw_eq(a: &T, b: &T) -> bool; /// Lexicographically compare `[left, left + bytes)` and `[right, right + bytes)` /// as unsigned bytes, returning negative if `left` is less, zero if all the @@ -2951,7 +3036,7 @@ pub const unsafe fn raw_eq(_a: &T, _b: &T) -> bool; /// [valid]: crate::ptr#safety #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn compare_bytes(_left: *const u8, _right: *const u8, _bytes: usize) -> i32; +pub const unsafe fn compare_bytes(left: *const u8, right: *const u8, bytes: usize) -> i32; /// See documentation of [`std::hint::black_box`] for details. /// @@ -2959,7 +3044,7 @@ pub const unsafe fn compare_bytes(_left: *const u8, _right: *const u8, _bytes: u #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_const_stable_indirect] -pub const fn black_box(_dummy: T) -> T; +pub const fn black_box(dummy: T) -> T; /// Selects which function to call depending on the context. /// @@ -3197,13 +3282,22 @@ pub const fn is_val_statically_known(_arg: T) -> bool { /// The stabilized form of this intrinsic is [`crate::mem::swap`]. /// /// # Safety +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * Both `x` and `y` must be [valid] for both reads and writes. /// -/// `x` and `y` are readable and writable as `T`, and non-overlapping. +/// * Both `x` and `y` must be properly aligned. +/// +/// * The region of memory beginning at `x` must *not* overlap with the region of memory +/// beginning at `y`. +/// +/// * The memory pointed by `x` and `y` must both contain values of type `T`. +/// +/// [valid]: crate::ptr#safety #[rustc_nounwind] #[inline] #[rustc_intrinsic] #[rustc_intrinsic_const_stable_indirect] -#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic pub const unsafe fn typed_swap_nonoverlapping(x: *mut T, y: *mut T) { // SAFETY: The caller provided single non-overlapping items behind // pointers, so swapping them with `count: 1` is fine. @@ -3222,7 +3316,7 @@ pub const unsafe fn typed_swap_nonoverlapping(x: *mut T, y: *mut T) { /// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(ub_checks)` means that /// assertions are enabled whenever the *user crate* has UB checks enabled. However, if the /// user has UB checks disabled, the checks will still get optimized out. This intrinsic is -/// primarily used by [`ub_checks::assert_unsafe_precondition`]. +/// primarily used by [`crate::ub_checks::assert_unsafe_precondition`]. #[rustc_intrinsic_const_stable_indirect] // just for UB checks #[inline(always)] #[rustc_intrinsic] @@ -3288,26 +3382,57 @@ pub const fn contract_checks() -> bool { /// /// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition /// returns false. -#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +/// +/// Note that this function is a no-op during constant evaluation. +#[unstable(feature = "contracts_internals", issue = "128044")] +// Calls to this function get inserted by an AST expansion pass, which uses the equivalent of +// `#[allow_internal_unstable]` to allow using `contracts_internals` functions. Const-checking +// doesn't honor `#[allow_internal_unstable]`, so for the const feature gate we use the user-facing +// `contracts` feature rather than the perma-unstable `contracts_internals` +#[rustc_const_unstable(feature = "contracts", issue = "128044")] #[lang = "contract_check_requires"] #[rustc_intrinsic] -pub fn contract_check_requires bool>(cond: C) { - if contract_checks() && !cond() { - // Emit no unwind panic in case this was a safety requirement. - crate::panicking::panic_nounwind("failed requires check"); - } +pub const fn contract_check_requires bool + Copy>(cond: C) { + const_eval_select!( + @capture[C: Fn() -> bool + Copy] { cond: C } : + if const { + // Do nothing + } else { + if contract_checks() && !cond() { + // Emit no unwind panic in case this was a safety requirement. + crate::panicking::panic_nounwind("failed requires check"); + } + } + ) } /// Check if the post-condition `cond` has been met. /// /// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition /// returns false. -#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +/// +/// Note that this function is a no-op during constant evaluation. +#[unstable(feature = "contracts_internals", issue = "128044")] +// Similar to `contract_check_requires`, we need to use the user-facing +// `contracts` feature rather than the perma-unstable `contracts_internals`. +// Const-checking doesn't honor allow_internal_unstable logic used by contract expansion. +#[rustc_const_unstable(feature = "contracts", issue = "128044")] +#[lang = "contract_check_ensures"] #[rustc_intrinsic] -pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) { - if contract_checks() && !cond(ret) { - crate::panicking::panic_nounwind("failed ensures check"); - } +pub const fn contract_check_ensures bool + Copy, Ret>(cond: C, ret: Ret) -> Ret { + const_eval_select!( + @capture[C: Fn(&Ret) -> bool + Copy, Ret] { cond: C, ret: Ret } -> Ret : + if const { + // Do nothing + ret + } else { + if contract_checks() && !cond(&ret) { + // Emit no unwind panic in case this was a safety requirement. + crate::panicking::panic_nounwind("failed ensures check"); + } + ret + } + ) } /// The intrinsic will return the size stored in that vtable. @@ -3318,7 +3443,7 @@ pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, con #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] -pub unsafe fn vtable_size(_ptr: *const ()) -> usize; +pub unsafe fn vtable_size(ptr: *const ()) -> usize; /// The intrinsic will return the alignment stored in that vtable. /// @@ -3328,7 +3453,7 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize; #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] -pub unsafe fn vtable_align(_ptr: *const ()) -> usize; +pub unsafe fn vtable_align(ptr: *const ()) -> usize; /// The size of a type in bytes. /// @@ -3340,7 +3465,7 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize; /// More specifically, this is the offset in bytes between successive /// items of the same type, including alignment padding. /// -/// The stabilized version of this intrinsic is [`core::mem::size_of`]. +/// The stabilized version of this intrinsic is [`size_of`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic_const_stable_indirect] @@ -3354,7 +3479,7 @@ pub const fn size_of() -> usize; /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is [`core::mem::align_of`]. +/// The stabilized version of this intrinsic is [`align_of`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic_const_stable_indirect] @@ -3386,7 +3511,7 @@ pub const fn variant_count() -> usize; /// The size of the referenced value in bytes. /// -/// The stabilized version of this intrinsic is [`crate::mem::size_of_val`]. +/// The stabilized version of this intrinsic is [`size_of_val`]. /// /// # Safety /// @@ -3395,11 +3520,11 @@ pub const fn variant_count() -> usize; #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_intrinsic_const_stable_indirect] -pub const unsafe fn size_of_val(_ptr: *const T) -> usize; +pub const unsafe fn size_of_val(ptr: *const T) -> usize; /// The required alignment of the referenced value. /// -/// The stabilized version of this intrinsic is [`core::mem::align_of_val`]. +/// The stabilized version of this intrinsic is [`align_of_val`]. /// /// # Safety /// @@ -3408,7 +3533,7 @@ pub const unsafe fn size_of_val(_ptr: *const T) -> usize; #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_intrinsic_const_stable_indirect] -pub const unsafe fn min_align_of_val(_ptr: *const T) -> usize; +pub const unsafe fn min_align_of_val(ptr: *const T) -> usize; /// Gets a static string slice containing the name of a type. /// @@ -3447,7 +3572,7 @@ pub const fn type_id() -> u128; #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const fn aggregate_raw_ptr, D, M>(_data: D, _meta: M) -> P; +pub const fn aggregate_raw_ptr, D, M>(data: D, meta: M) -> P; #[unstable(feature = "core_intrinsics", issue = "none")] pub trait AggregateRawPtr { @@ -3467,307 +3592,42 @@ impl AggregateRawPtr<*mut T> for *mut P { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const fn ptr_metadata + ?Sized, M>(_ptr: *const P) -> M; - -// 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.) +pub const fn ptr_metadata + ?Sized, M>(ptr: *const P) -> M; -/// 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. -/// -/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the -/// requirements of `T`. The initialization state is preserved exactly. -/// -/// [`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 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 add is always safe because `Vec` will never -/// // allocate more than `isize::MAX` bytes. -/// let dst_ptr = dst.as_mut_ptr().add(dst_len); -/// 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")] +/// This is an accidentally-stable alias to [`ptr::copy_nonoverlapping`]; use that instead. +// Note (intentionally not in the doc comment): `ptr::copy_nonoverlapping` adds some extra +// debug assertions; if you are writing compiler tests or code inside the standard library +// that wants to avoid those debug assertions, directly call this intrinsic instead. #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead"] +#[rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead"] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] -#[inline(always)] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[rustc_diagnostic_item = "ptr_copy_nonoverlapping"] -pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { - #[rustc_intrinsic_const_stable_indirect] - #[rustc_nounwind] - #[rustc_intrinsic] - const unsafe fn copy_nonoverlapping(_src: *const T, _dst: *mut T, _count: usize); - - ub_checks::assert_unsafe_precondition!( - check_language_ub, - "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ - and the specified memory ranges do not overlap", - ( - src: *const () = src as *const (), - dst: *mut () = dst as *mut (), - size: usize = size_of::(), - align: usize = align_of::(), - count: usize = count, - ) => { - let zero_size = count == 0 || size == 0; - ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size) - && ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size) - && ub_checks::maybe_is_nonoverlapping(src, dst, size, count) - } - ); - - // SAFETY: the safety contract for `copy_nonoverlapping` must be - // upheld by the caller. - unsafe { copy_nonoverlapping(src, dst, count) } -} +#[rustc_nounwind] +#[rustc_intrinsic] +pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); -/// 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`. -/// -/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the -/// requirements of `T`. The initialization state is preserved exactly. -/// -/// [`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, and must remain valid even -/// when `src` is read for `count * size_of::()` bytes. (This means if the memory ranges -/// overlap, the `dst` pointer must not be invalidated by `src` reads.) -/// -/// * 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 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; -/// -/// /// # Safety -/// /// -/// /// * `ptr` must be correctly aligned for its type and non-zero. -/// /// * `ptr` must be valid for reads of `elts` contiguous elements of type `T`. -/// /// * Those elements must not be used after calling this function unless `T: Copy`. -/// # #[allow(dead_code)] -/// unsafe fn from_buf_raw(ptr: *const T, elts: usize) -> Vec { -/// let mut dst = Vec::with_capacity(elts); -/// -/// // SAFETY: Our precondition ensures the source is aligned and valid, -/// // and `Vec::with_capacity` ensures that we have usable space to write them. -/// unsafe { ptr::copy(ptr, dst.as_mut_ptr(), elts); } -/// -/// // SAFETY: We created it with this much capacity earlier, -/// // and the previous `copy` has initialized these elements. -/// unsafe { dst.set_len(elts); } -/// dst -/// } -/// ``` -#[doc(alias = "memmove")] +/// This is an accidentally-stable alias to [`ptr::copy`]; use that instead. +// Note (intentionally not in the doc comment): `ptr::copy` adds some extra +// debug assertions; if you are writing compiler tests or code inside the standard library +// that wants to avoid those debug assertions, directly call this intrinsic instead. #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead"] +#[rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead"] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] -#[inline(always)] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[rustc_diagnostic_item = "ptr_copy"] -pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { - #[rustc_intrinsic_const_stable_indirect] - #[rustc_nounwind] - #[rustc_intrinsic] - const unsafe fn copy(_src: *const T, _dst: *mut T, _count: usize); - - // SAFETY: the safety contract for `copy` must be upheld by the caller. - unsafe { - ub_checks::assert_unsafe_precondition!( - check_language_ub, - "ptr::copy requires that both pointer arguments are aligned and non-null", - ( - src: *const () = src as *const (), - dst: *mut () = dst as *mut (), - align: usize = align_of::(), - zero_size: bool = T::IS_ZST || count == 0, - ) => - ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size) - && ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size) - ); - copy(src, dst, count) - } -} +#[rustc_nounwind] +#[rustc_intrinsic] +pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize); -/// 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. -/// -/// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointer must be properly aligned. -/// -/// Additionally, note that changing `*dst` in this way can easily lead to undefined behavior (UB) -/// later if the written bytes are not a valid representation of some `T`. For instance, the -/// following is an **incorrect** use of this function: -/// -/// ```rust,no_run -/// unsafe { -/// let mut value: u8 = 0; -/// let ptr: *mut bool = &mut value as *mut u8 as *mut bool; -/// let _bool = ptr.read(); // This is fine, `ptr` points to a valid `bool`. -/// ptr.write_bytes(42u8, 1); // This function itself does not cause UB... -/// let _bool = ptr.read(); // ...but it makes this operation UB! ⚠️ -/// } -/// ``` -/// -/// [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]); -/// ``` -#[doc(alias = "memset")] +/// This is an accidentally-stable alias to [`ptr::write_bytes`]; use that instead. +// Note (intentionally not in the doc comment): `ptr::write_bytes` adds some extra +// debug assertions; if you are writing compiler tests or code inside the standard library +// that wants to avoid those debug assertions, directly call this intrinsic instead. #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead"] -#[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")] -#[inline(always)] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[rustc_diagnostic_item = "ptr_write_bytes"] -pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { - #[rustc_intrinsic_const_stable_indirect] - #[rustc_nounwind] - #[rustc_intrinsic] - const unsafe fn write_bytes(_dst: *mut T, _val: u8, _count: usize); - - // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. - unsafe { - ub_checks::assert_unsafe_precondition!( - check_language_ub, - "ptr::write_bytes requires that the destination pointer is aligned and non-null", - ( - addr: *const () = dst as *const (), - align: usize = align_of::(), - zero_size: bool = T::IS_ZST || count == 0, - ) => ub_checks::maybe_is_aligned_and_not_null(addr, align, zero_size) - ); - write_bytes(dst, val, count) - } -} +#[rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead"] +#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] +#[rustc_nounwind] +#[rustc_intrinsic] +pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize); -/// Returns the minimum of two `f16` values. +/// Returns the minimum (IEEE 754-2008 minNum) of two `f16` values. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3778,9 +3638,9 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { /// [`f16::min`] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn minnumf16(_x: f16, _y: f16) -> f16; +pub const fn minnumf16(x: f16, y: f16) -> f16; -/// Returns the minimum of two `f32` values. +/// Returns the minimum (IEEE 754-2008 minNum) of two `f32` values. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3792,9 +3652,9 @@ pub const fn minnumf16(_x: f16, _y: f16) -> f16; #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const fn minnumf32(_x: f32, _y: f32) -> f32; +pub const fn minnumf32(x: f32, y: f32) -> f32; -/// Returns the minimum of two `f64` values. +/// Returns the minimum (IEEE 754-2008 minNum) of two `f64` values. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3806,9 +3666,9 @@ pub const fn minnumf32(_x: f32, _y: f32) -> f32; #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const fn minnumf64(_x: f64, _y: f64) -> f64; +pub const fn minnumf64(x: f64, y: f64) -> f64; -/// Returns the minimum of two `f128` values. +/// Returns the minimum (IEEE 754-2008 minNum) of two `f128` values. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3819,9 +3679,93 @@ pub const fn minnumf64(_x: f64, _y: f64) -> f64; /// [`f128::min`] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn minnumf128(_x: f128, _y: f128) -> f128; +pub const fn minnumf128(x: f128, y: f128) -> f128; -/// Returns the maximum of two `f16` values. +/// Returns the minimum (IEEE 754-2019 minimum) of two `f16` values. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +#[rustc_nounwind] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +pub const fn minimumf16(x: f16, y: f16) -> f16 { + if x < y { + x + } else if y < x { + y + } else if x == y { + if x.is_sign_negative() && y.is_sign_positive() { x } else { y } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + x + y + } +} + +/// Returns the minimum (IEEE 754-2019 minimum) of two `f32` values. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +#[rustc_nounwind] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +pub const fn minimumf32(x: f32, y: f32) -> f32 { + if x < y { + x + } else if y < x { + y + } else if x == y { + if x.is_sign_negative() && y.is_sign_positive() { x } else { y } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + x + y + } +} + +/// Returns the minimum (IEEE 754-2019 minimum) of two `f64` values. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +#[rustc_nounwind] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +pub const fn minimumf64(x: f64, y: f64) -> f64 { + if x < y { + x + } else if y < x { + y + } else if x == y { + if x.is_sign_negative() && y.is_sign_positive() { x } else { y } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + x + y + } +} + +/// Returns the minimum (IEEE 754-2019 minimum) of two `f128` values. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +#[rustc_nounwind] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +pub const fn minimumf128(x: f128, y: f128) -> f128 { + if x < y { + x + } else if y < x { + y + } else if x == y { + if x.is_sign_negative() && y.is_sign_positive() { x } else { y } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + x + y + } +} + +/// Returns the maximum (IEEE 754-2008 maxNum) of two `f16` values. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3832,9 +3776,9 @@ pub const fn minnumf128(_x: f128, _y: f128) -> f128; /// [`f16::max`] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn maxnumf16(_x: f16, _y: f16) -> f16; +pub const fn maxnumf16(x: f16, y: f16) -> f16; -/// Returns the maximum of two `f32` values. +/// Returns the maximum (IEEE 754-2008 maxNum) of two `f32` values. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3846,9 +3790,9 @@ pub const fn maxnumf16(_x: f16, _y: f16) -> f16; #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const fn maxnumf32(_x: f32, _y: f32) -> f32; +pub const fn maxnumf32(x: f32, y: f32) -> f32; -/// Returns the maximum of two `f64` values. +/// Returns the maximum (IEEE 754-2008 maxNum) of two `f64` values. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3860,9 +3804,9 @@ pub const fn maxnumf32(_x: f32, _y: f32) -> f32; #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const fn maxnumf64(_x: f64, _y: f64) -> f64; +pub const fn maxnumf64(x: f64, y: f64) -> f64; -/// Returns the maximum of two `f128` values. +/// Returns the maximum (IEEE 754-2008 maxNum) of two `f128` values. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3873,7 +3817,87 @@ pub const fn maxnumf64(_x: f64, _y: f64) -> f64; /// [`f128::max`] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn maxnumf128(_x: f128, _y: f128) -> f128; +pub const fn maxnumf128(x: f128, y: f128) -> f128; + +/// Returns the maximum (IEEE 754-2019 maximum) of two `f16` values. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +#[rustc_nounwind] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +pub const fn maximumf16(x: f16, y: f16) -> f16 { + if x > y { + x + } else if y > x { + y + } else if x == y { + if x.is_sign_positive() && y.is_sign_negative() { x } else { y } + } else { + x + y + } +} + +/// Returns the maximum (IEEE 754-2019 maximum) of two `f32` values. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +#[rustc_nounwind] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +pub const fn maximumf32(x: f32, y: f32) -> f32 { + if x > y { + x + } else if y > x { + y + } else if x == y { + if x.is_sign_positive() && y.is_sign_negative() { x } else { y } + } else { + x + y + } +} + +/// Returns the maximum (IEEE 754-2019 maximum) of two `f64` values. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +#[rustc_nounwind] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +pub const fn maximumf64(x: f64, y: f64) -> f64 { + if x > y { + x + } else if y > x { + y + } else if x == y { + if x.is_sign_positive() && y.is_sign_negative() { x } else { y } + } else { + x + y + } +} + +/// Returns the maximum (IEEE 754-2019 maximum) of two `f128` values. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +#[rustc_nounwind] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +pub const fn maximumf128(x: f128, y: f128) -> f128 { + if x > y { + x + } else if y > x { + y + } else if x == y { + if x.is_sign_positive() && y.is_sign_negative() { x } else { y } + } else { + x + y + } +} /// Returns the absolute value of an `f16`. /// @@ -3881,7 +3905,7 @@ pub const fn maxnumf128(_x: f128, _y: f128) -> f128; /// [`f16::abs`](../../std/primitive.f16.html#method.abs) #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn fabsf16(_x: f16) -> f16; +pub const unsafe fn fabsf16(x: f16) -> f16; /// Returns the absolute value of an `f32`. /// @@ -3890,7 +3914,7 @@ pub const unsafe fn fabsf16(_x: f16) -> f16; #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const unsafe fn fabsf32(_x: f32) -> f32; +pub const unsafe fn fabsf32(x: f32) -> f32; /// Returns the absolute value of an `f64`. /// @@ -3899,7 +3923,7 @@ pub const unsafe fn fabsf32(_x: f32) -> f32; #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const unsafe fn fabsf64(_x: f64) -> f64; +pub const unsafe fn fabsf64(x: f64) -> f64; /// Returns the absolute value of an `f128`. /// @@ -3907,7 +3931,7 @@ pub const unsafe fn fabsf64(_x: f64) -> f64; /// [`f128::abs`](../../std/primitive.f128.html#method.abs) #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn fabsf128(_x: f128) -> f128; +pub const unsafe fn fabsf128(x: f128) -> f128; /// Copies the sign from `y` to `x` for `f16` values. /// @@ -3915,7 +3939,7 @@ pub const unsafe fn fabsf128(_x: f128) -> f128; /// [`f16::copysign`](../../std/primitive.f16.html#method.copysign) #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn copysignf16(_x: f16, _y: f16) -> f16; +pub const unsafe fn copysignf16(x: f16, y: f16) -> f16; /// Copies the sign from `y` to `x` for `f32` values. /// @@ -3924,7 +3948,7 @@ pub const unsafe fn copysignf16(_x: f16, _y: f16) -> f16; #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const unsafe fn copysignf32(_x: f32, _y: f32) -> f32; +pub const unsafe fn copysignf32(x: f32, y: f32) -> f32; /// Copies the sign from `y` to `x` for `f64` values. /// /// The stabilized version of this intrinsic is @@ -3932,7 +3956,7 @@ pub const unsafe fn copysignf32(_x: f32, _y: f32) -> f32; #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] -pub const unsafe fn copysignf64(_x: f64, _y: f64) -> f64; +pub const unsafe fn copysignf64(x: f64, y: f64) -> f64; /// Copies the sign from `y` to `x` for `f128` values. /// @@ -3940,7 +3964,7 @@ pub const unsafe fn copysignf64(_x: f64, _y: f64) -> f64; /// [`f128::copysign`](../../std/primitive.f128.html#method.copysign) #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn copysignf128(_x: f128, _y: f128) -> f128; +pub const unsafe fn copysignf128(x: f128, y: f128) -> f128; /// Inform Miri that a given pointer definitely has a certain alignment. #[cfg(miri)] diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index 935dd2e567318..40efc2630689b 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -4,46 +4,79 @@ /// Inserts an element into a vector, returning the updated vector. /// -/// `T` must be a vector with element type `U`. +/// `T` must be a vector with element type `U`, and `idx` must be `const`. /// /// # Safety /// /// `idx` must be in-bounds of the vector. #[rustc_intrinsic] #[rustc_nounwind] -pub const unsafe fn simd_insert(_x: T, _idx: u32, _val: U) -> T; +pub const unsafe fn simd_insert(x: T, idx: u32, val: U) -> T; /// Extracts an element from a vector. /// +/// `T` must be a vector with element type `U`, and `idx` must be `const`. +/// +/// # Safety +/// +/// `idx` must be const and in-bounds of the vector. +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn simd_extract(x: T, idx: u32) -> U; + +/// Inserts an element into a vector, returning the updated vector. +/// /// `T` must be a vector with element type `U`. /// +/// If the index is `const`, [`simd_insert`] may emit better assembly. +/// /// # Safety /// /// `idx` must be in-bounds of the vector. +#[rustc_nounwind] #[rustc_intrinsic] +pub unsafe fn simd_insert_dyn(mut x: T, idx: u32, val: U) -> T { + // SAFETY: `idx` must be in-bounds + unsafe { (&raw mut x).cast::().add(idx as usize).write(val) } + x +} + +/// Extracts an element from a vector. +/// +/// `T` must be a vector with element type `U`. +/// +/// If the index is `const`, [`simd_extract`] may emit better assembly. +/// +/// # Safety +/// +/// `idx` must be in-bounds of the vector. #[rustc_nounwind] -pub const unsafe fn simd_extract(_x: T, _idx: u32) -> U; +#[rustc_intrinsic] +pub unsafe fn simd_extract_dyn(x: T, idx: u32) -> U { + // SAFETY: `idx` must be in-bounds + unsafe { (&raw const x).cast::().add(idx as usize).read() } +} /// Adds two simd vectors elementwise. /// /// `T` must be a vector of integers or floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_add(_x: T, _y: T) -> T; +pub unsafe fn simd_add(x: T, y: T) -> T; /// Subtracts `rhs` from `lhs` elementwise. /// /// `T` must be a vector of integers or floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_sub(_lhs: T, _rhs: T) -> T; +pub unsafe fn simd_sub(lhs: T, rhs: T) -> T; /// Multiplies two simd vectors elementwise. /// /// `T` must be a vector of integers or floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_mul(_x: T, _y: T) -> T; +pub unsafe fn simd_mul(x: T, y: T) -> T; /// Divides `lhs` by `rhs` elementwise. /// @@ -54,7 +87,7 @@ pub unsafe fn simd_mul(_x: T, _y: T) -> T; /// Additionally for signed integers, `::MIN / -1` is undefined behavior. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_div(_lhs: T, _rhs: T) -> T; +pub unsafe fn simd_div(lhs: T, rhs: T) -> T; /// Returns remainder of two vectors elementwise. /// @@ -65,7 +98,7 @@ pub unsafe fn simd_div(_lhs: T, _rhs: T) -> T; /// Additionally for signed integers, `::MIN / -1` is undefined behavior. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_rem(_lhs: T, _rhs: T) -> T; +pub unsafe fn simd_rem(lhs: T, rhs: T) -> T; /// Shifts vector left elementwise, with UB on overflow. /// @@ -78,7 +111,7 @@ pub unsafe fn simd_rem(_lhs: T, _rhs: T) -> T; /// Each element of `rhs` must be less than `::BITS`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_shl(_lhs: T, _rhs: T) -> T; +pub unsafe fn simd_shl(lhs: T, rhs: T) -> T; /// Shifts vector right elementwise, with UB on overflow. /// @@ -91,28 +124,28 @@ pub unsafe fn simd_shl(_lhs: T, _rhs: T) -> T; /// Each element of `rhs` must be less than `::BITS`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_shr(_lhs: T, _rhs: T) -> T; +pub unsafe fn simd_shr(lhs: T, rhs: T) -> T; /// "Ands" vectors elementwise. /// /// `T` must be a vector of integers. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_and(_x: T, _y: T) -> T; +pub unsafe fn simd_and(x: T, y: T) -> T; /// "Ors" vectors elementwise. /// /// `T` must be a vector of integers. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_or(_x: T, _y: T) -> T; +pub unsafe fn simd_or(x: T, y: T) -> T; /// "Exclusive ors" vectors elementwise. /// /// `T` must be a vector of integers. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_xor(_x: T, _y: T) -> T; +pub unsafe fn simd_xor(x: T, y: T) -> T; /// Numerically casts a vector, elementwise. /// @@ -133,7 +166,7 @@ pub unsafe fn simd_xor(_x: T, _y: T) -> T; /// * Be representable in the return type, after truncating off its fractional part #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_cast(_x: T) -> U; +pub unsafe fn simd_cast(x: T) -> U; /// Numerically casts a vector, elementwise. /// @@ -147,7 +180,7 @@ pub unsafe fn simd_cast(_x: T) -> U; /// Otherwise, truncates or extends the value, maintaining the sign for signed integers. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_as(_x: T) -> U; +pub unsafe fn simd_as(x: T) -> U; /// Negates a vector elementwise. /// @@ -156,14 +189,14 @@ pub unsafe fn simd_as(_x: T) -> U; /// Rust panics for `-::Min` due to overflow, but it is not UB with this intrinsic. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_neg(_x: T) -> T; +pub unsafe fn simd_neg(x: T) -> T; /// Returns absolute value of a vector, elementwise. /// /// `T` must be a vector of floating-point primitive types. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_fabs(_x: T) -> T; +pub unsafe fn simd_fabs(x: T) -> T; /// Returns the minimum of two vectors, elementwise. /// @@ -172,7 +205,7 @@ pub unsafe fn simd_fabs(_x: T) -> T; /// Follows IEEE-754 `minNum` semantics. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_fmin(_x: T, _y: T) -> T; +pub unsafe fn simd_fmin(x: T, y: T) -> T; /// Returns the maximum of two vectors, elementwise. /// @@ -181,7 +214,7 @@ pub unsafe fn simd_fmin(_x: T, _y: T) -> T; /// Follows IEEE-754 `maxNum` semantics. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_fmax(_x: T, _y: T) -> T; +pub unsafe fn simd_fmax(x: T, y: T) -> T; /// Tests elementwise equality of two vectors. /// @@ -192,7 +225,7 @@ pub unsafe fn simd_fmax(_x: T, _y: T) -> T; /// Returns `0` for false and `!0` for true. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_eq(_x: T, _y: T) -> U; +pub unsafe fn simd_eq(x: T, y: T) -> U; /// Tests elementwise inequality equality of two vectors. /// @@ -203,7 +236,7 @@ pub unsafe fn simd_eq(_x: T, _y: T) -> U; /// Returns `0` for false and `!0` for true. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_ne(_x: T, _y: T) -> U; +pub unsafe fn simd_ne(x: T, y: T) -> U; /// Tests if `x` is less than `y`, elementwise. /// @@ -214,7 +247,7 @@ pub unsafe fn simd_ne(_x: T, _y: T) -> U; /// Returns `0` for false and `!0` for true. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_lt(_x: T, _y: T) -> U; +pub unsafe fn simd_lt(x: T, y: T) -> U; /// Tests if `x` is less than or equal to `y`, elementwise. /// @@ -225,7 +258,7 @@ pub unsafe fn simd_lt(_x: T, _y: T) -> U; /// Returns `0` for false and `!0` for true. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_le(_x: T, _y: T) -> U; +pub unsafe fn simd_le(x: T, y: T) -> U; /// Tests if `x` is greater than `y`, elementwise. /// @@ -236,7 +269,7 @@ pub unsafe fn simd_le(_x: T, _y: T) -> U; /// Returns `0` for false and `!0` for true. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_gt(_x: T, _y: T) -> U; +pub unsafe fn simd_gt(x: T, y: T) -> U; /// Tests if `x` is greater than or equal to `y`, elementwise. /// @@ -247,7 +280,7 @@ pub unsafe fn simd_gt(_x: T, _y: T) -> U; /// Returns `0` for false and `!0` for true. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_ge(_x: T, _y: T) -> U; +pub unsafe fn simd_ge(x: T, y: T) -> U; /// Shuffles two vectors by const indices. /// @@ -263,7 +296,7 @@ pub unsafe fn simd_ge(_x: T, _y: T) -> U; /// of `xy`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_shuffle(_x: T, _y: T, _idx: U) -> V; +pub unsafe fn simd_shuffle(x: T, y: T, idx: U) -> V; /// Reads a vector of pointers. /// @@ -271,7 +304,7 @@ pub unsafe fn simd_shuffle(_x: T, _y: T, _idx: U) -> V; /// /// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. /// -/// `V` must be a vector of signed integers with the same length as `T` (but any element size). +/// `V` must be a vector of integers with the same length as `T` (but any element size). /// /// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, read the pointer. /// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from @@ -284,7 +317,7 @@ pub unsafe fn simd_shuffle(_x: T, _y: T, _idx: U) -> V; /// `mask` must only contain `0` or `!0` values. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_gather(_val: T, _ptr: U, _mask: V) -> T; +pub unsafe fn simd_gather(val: T, ptr: U, mask: V) -> T; /// Writes to a vector of pointers. /// @@ -292,7 +325,7 @@ pub unsafe fn simd_gather(_val: T, _ptr: U, _mask: V) -> T; /// /// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. /// -/// `V` must be a vector of signed integers with the same length as `T` (but any element size). +/// `V` must be a vector of integers with the same length as `T` (but any element size). /// /// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, write the /// corresponding value in `val` to the pointer. @@ -308,7 +341,7 @@ pub unsafe fn simd_gather(_val: T, _ptr: U, _mask: V) -> T; /// `mask` must only contain `0` or `!0` values. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_scatter(_val: T, _ptr: U, _mask: V); +pub unsafe fn simd_scatter(val: T, ptr: U, mask: V); /// Reads a vector of pointers. /// @@ -316,7 +349,7 @@ pub unsafe fn simd_scatter(_val: T, _ptr: U, _mask: V); /// /// `U` must be a pointer to the element type of `T` /// -/// `V` must be a vector of signed integers with the same length as `T` (but any element size). +/// `V` must be a vector of integers with the same length as `T` (but any element size). /// /// For each element, if the corresponding value in `mask` is `!0`, read the corresponding /// pointer offset from `ptr`. @@ -331,7 +364,7 @@ pub unsafe fn simd_scatter(_val: T, _ptr: U, _mask: V); /// `mask` must only contain `0` or `!0` values. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_masked_load(_mask: V, _ptr: U, _val: T) -> T; +pub unsafe fn simd_masked_load(mask: V, ptr: U, val: T) -> T; /// Writes to a vector of pointers. /// @@ -339,7 +372,7 @@ pub unsafe fn simd_masked_load(_mask: V, _ptr: U, _val: T) -> T; /// /// `U` must be a pointer to the element type of `T` /// -/// `V` must be a vector of signed integers with the same length as `T` (but any element size). +/// `V` must be a vector of integers with the same length as `T` (but any element size). /// /// For each element, if the corresponding value in `mask` is `!0`, write the corresponding /// value in `val` to the pointer offset from `ptr`. @@ -353,14 +386,14 @@ pub unsafe fn simd_masked_load(_mask: V, _ptr: U, _val: T) -> T; /// `mask` must only contain `0` or `!0` values. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_masked_store(_mask: V, _ptr: U, _val: T); +pub unsafe fn simd_masked_store(mask: V, ptr: U, val: T); /// Adds two simd vectors elementwise, with saturation. /// /// `T` must be a vector of integer primitive types. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_saturating_add(_x: T, _y: T) -> T; +pub unsafe fn simd_saturating_add(x: T, y: T) -> T; /// Subtracts two simd vectors elementwise, with saturation. /// @@ -369,7 +402,7 @@ pub unsafe fn simd_saturating_add(_x: T, _y: T) -> T; /// Subtract `rhs` from `lhs`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_saturating_sub(_lhs: T, _rhs: T) -> T; +pub unsafe fn simd_saturating_sub(lhs: T, rhs: T) -> T; /// Adds elements within a vector from left to right. /// @@ -380,7 +413,7 @@ pub unsafe fn simd_saturating_sub(_lhs: T, _rhs: T) -> T; /// Starting with the value `y`, add the elements of `x` and accumulate. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_reduce_add_ordered(_x: T, _y: U) -> U; +pub unsafe fn simd_reduce_add_ordered(x: T, y: U) -> U; /// Adds elements within a vector in arbitrary order. May also be re-associated with /// unordered additions on the inputs/outputs. @@ -390,7 +423,7 @@ pub unsafe fn simd_reduce_add_ordered(_x: T, _y: U) -> U; /// `U` must be the element type of `T`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_reduce_add_unordered(_x: T) -> U; +pub unsafe fn simd_reduce_add_unordered(x: T) -> U; /// Multiplies elements within a vector from left to right. /// @@ -401,7 +434,7 @@ pub unsafe fn simd_reduce_add_unordered(_x: T) -> U; /// Starting with the value `y`, multiply the elements of `x` and accumulate. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_reduce_mul_ordered(_x: T, _y: U) -> U; +pub unsafe fn simd_reduce_mul_ordered(x: T, y: U) -> U; /// Multiplies elements within a vector in arbitrary order. May also be re-associated with /// unordered additions on the inputs/outputs. @@ -411,7 +444,7 @@ pub unsafe fn simd_reduce_mul_ordered(_x: T, _y: U) -> U; /// `U` must be the element type of `T`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_reduce_mul_unordered(_x: T) -> U; +pub unsafe fn simd_reduce_mul_unordered(x: T) -> U; /// Checks if all mask values are true. /// @@ -421,7 +454,7 @@ pub unsafe fn simd_reduce_mul_unordered(_x: T) -> U; /// `x` must contain only `0` or `!0`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_reduce_all(_x: T) -> bool; +pub unsafe fn simd_reduce_all(x: T) -> bool; /// Checks if any mask value is true. /// @@ -431,7 +464,7 @@ pub unsafe fn simd_reduce_all(_x: T) -> bool; /// `x` must contain only `0` or `!0`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_reduce_any(_x: T) -> bool; +pub unsafe fn simd_reduce_any(x: T) -> bool; /// Returns the maximum element of a vector. /// @@ -442,7 +475,7 @@ pub unsafe fn simd_reduce_any(_x: T) -> bool; /// For floating-point values, uses IEEE-754 `maxNum`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_reduce_max(_x: T) -> U; +pub unsafe fn simd_reduce_max(x: T) -> U; /// Returns the minimum element of a vector. /// @@ -453,7 +486,7 @@ pub unsafe fn simd_reduce_max(_x: T) -> U; /// For floating-point values, uses IEEE-754 `minNum`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_reduce_min(_x: T) -> U; +pub unsafe fn simd_reduce_min(x: T) -> U; /// Logical "ands" all elements together. /// @@ -462,7 +495,7 @@ pub unsafe fn simd_reduce_min(_x: T) -> U; /// `U` must be the element type of `T`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_reduce_and(_x: T) -> U; +pub unsafe fn simd_reduce_and(x: T) -> U; /// Logical "ors" all elements together. /// @@ -471,7 +504,7 @@ pub unsafe fn simd_reduce_and(_x: T) -> U; /// `U` must be the element type of `T`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_reduce_or(_x: T) -> U; +pub unsafe fn simd_reduce_or(x: T) -> U; /// Logical "exclusive ors" all elements together. /// @@ -480,7 +513,7 @@ pub unsafe fn simd_reduce_or(_x: T) -> U; /// `U` must be the element type of `T`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_reduce_xor(_x: T) -> U; +pub unsafe fn simd_reduce_xor(x: T) -> U; /// Truncates an integer vector to a bitmask. /// @@ -517,13 +550,13 @@ pub unsafe fn simd_reduce_xor(_x: T) -> U; /// `x` must contain only `0` and `!0`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_bitmask(_x: T) -> U; +pub unsafe fn simd_bitmask(x: T) -> U; /// Selects elements from a mask. /// /// `T` must be a vector. /// -/// `M` must be a signed integer vector with the same length as `T` (but any element size). +/// `M` must be an integer vector with the same length as `T` (but any element size). /// /// For each element, if the corresponding value in `mask` is `!0`, select the element from /// `if_true`. If the corresponding value in `mask` is `0`, select the element from @@ -533,7 +566,7 @@ pub unsafe fn simd_bitmask(_x: T) -> U; /// `mask` must only contain `0` and `!0`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_select(_mask: M, _if_true: T, _if_false: T) -> T; +pub unsafe fn simd_select(mask: M, if_true: T, if_false: T) -> T; /// Selects elements from a bitmask. /// @@ -544,14 +577,12 @@ pub unsafe fn simd_select(_mask: M, _if_true: T, _if_false: T) -> T; /// For each element, if the bit in `mask` is `1`, select the element from /// `if_true`. If the corresponding bit in `mask` is `0`, select the element from /// `if_false`. +/// The remaining bits of the mask are ignored. /// /// The bitmask bit order matches `simd_bitmask`. -/// -/// # Safety -/// Padding bits must be all zero. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_select_bitmask(_m: M, _yes: T, _no: T) -> T; +pub unsafe fn simd_select_bitmask(m: M, yes: T, no: T) -> T; /// Calculates the offset from a pointer vector elementwise, potentially /// wrapping. @@ -563,14 +594,14 @@ pub unsafe fn simd_select_bitmask(_m: M, _yes: T, _no: T) -> T; /// Operates as if by `::wrapping_offset`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_arith_offset(_ptr: T, _offset: U) -> T; +pub unsafe fn simd_arith_offset(ptr: T, offset: U) -> T; /// Casts a vector of pointers. /// /// `T` and `U` must be vectors of pointers with the same number of elements. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_cast_ptr(_ptr: T) -> U; +pub unsafe fn simd_cast_ptr(ptr: T) -> U; /// Exposes a vector of pointers as a vector of addresses. /// @@ -579,7 +610,7 @@ pub unsafe fn simd_cast_ptr(_ptr: T) -> U; /// `U` must be a vector of `usize` with the same length as `T`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_expose_provenance(_ptr: T) -> U; +pub unsafe fn simd_expose_provenance(ptr: T) -> U; /// Creates a vector of pointers from a vector of addresses. /// @@ -588,56 +619,56 @@ pub unsafe fn simd_expose_provenance(_ptr: T) -> U; /// `U` must be a vector of pointers, with the same length as `T`. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_with_exposed_provenance(_addr: T) -> U; +pub unsafe fn simd_with_exposed_provenance(addr: T) -> U; /// Swaps bytes of each element. /// /// `T` must be a vector of integers. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_bswap(_x: T) -> T; +pub unsafe fn simd_bswap(x: T) -> T; /// Reverses bits of each element. /// /// `T` must be a vector of integers. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_bitreverse(_x: T) -> T; +pub unsafe fn simd_bitreverse(x: T) -> T; /// Counts the leading zeros of each element. /// /// `T` must be a vector of integers. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_ctlz(_x: T) -> T; +pub unsafe fn simd_ctlz(x: T) -> T; /// Counts the number of ones in each element. /// /// `T` must be a vector of integers. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_ctpop(_x: T) -> T; +pub unsafe fn simd_ctpop(x: T) -> T; /// Counts the trailing zeros of each element. /// /// `T` must be a vector of integers. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_cttz(_x: T) -> T; +pub unsafe fn simd_cttz(x: T) -> T; /// Rounds up each element to the next highest integer-valued float. /// /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_ceil(_x: T) -> T; +pub unsafe fn simd_ceil(x: T) -> T; /// Rounds down each element to the next lowest integer-valued float. /// /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_floor(_x: T) -> T; +pub unsafe fn simd_floor(x: T) -> T; /// Rounds each element to the closest integer-valued float. /// Ties are resolved by rounding away from 0. @@ -645,7 +676,7 @@ pub unsafe fn simd_floor(_x: T) -> T; /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_round(_x: T) -> T; +pub unsafe fn simd_round(x: T) -> T; /// Returns the integer part of each element as an integer-valued float. /// In other words, non-integer values are truncated towards zero. @@ -653,21 +684,21 @@ pub unsafe fn simd_round(_x: T) -> T; /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_trunc(_x: T) -> T; +pub unsafe fn simd_trunc(x: T) -> T; /// Takes the square root of each element. /// /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_fsqrt(_x: T) -> T; +pub unsafe fn simd_fsqrt(x: T) -> T; /// Computes `(x*y) + z` for each element, but without any intermediate rounding. /// /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_fma(_x: T, _y: T, _z: T) -> T; +pub unsafe fn simd_fma(x: T, y: T, z: T) -> T; /// Computes `(x*y) + z` for each element, non-deterministically executing either /// a fused multiply-add or two operations with rounding of the intermediate result. @@ -682,53 +713,53 @@ pub unsafe fn simd_fma(_x: T, _y: T, _z: T) -> T; /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_relaxed_fma(_x: T, _y: T, _z: T) -> T; +pub unsafe fn simd_relaxed_fma(x: T, y: T, z: T) -> T; // Computes the sine of each element. /// /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_fsin(_a: T) -> T; +pub unsafe fn simd_fsin(a: T) -> T; // Computes the cosine of each element. /// /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_fcos(_a: T) -> T; +pub unsafe fn simd_fcos(a: T) -> T; // Computes the exponential function of each element. /// /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_fexp(_a: T) -> T; +pub unsafe fn simd_fexp(a: T) -> T; // Computes 2 raised to the power of each element. /// /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_fexp2(_a: T) -> T; +pub unsafe fn simd_fexp2(a: T) -> T; // Computes the base 10 logarithm of each element. /// /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_flog10(_a: T) -> T; +pub unsafe fn simd_flog10(a: T) -> T; // Computes the base 2 logarithm of each element. /// /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_flog2(_a: T) -> T; +pub unsafe fn simd_flog2(a: T) -> T; // Computes the natural logarithm of each element. /// /// `T` must be a vector of floats. #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn simd_flog(_a: T) -> T; +pub unsafe fn simd_flog(a: T) -> T; diff --git a/library/core/src/iter/adapters/enumerate.rs b/library/core/src/iter/adapters/enumerate.rs index ac15e3767fc09..f7b9f0b7a5e9d 100644 --- a/library/core/src/iter/adapters/enumerate.rs +++ b/library/core/src/iter/adapters/enumerate.rs @@ -14,7 +14,7 @@ use crate::ops::Try; #[derive(Clone, Debug)] #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "Enumerate")] +#[rustc_diagnostic_item = "Enumerate"] pub struct Enumerate { iter: I, count: usize, @@ -23,6 +23,39 @@ impl Enumerate { pub(in crate::iter) fn new(iter: I) -> Enumerate { Enumerate { iter, count: 0 } } + + /// Retrieve the current position of the iterator. + /// + /// If the iterator has not advanced, the position returned will be 0. + /// + /// The position may also exceed the bounds of the iterator to allow for calculating + /// the displacement of the iterator from following calls to [`Iterator::next`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(next_index)] + /// + /// let arr = ['a', 'b']; + /// + /// let mut iter = arr.iter().enumerate(); + /// + /// assert_eq!(iter.next_index(), 0); + /// assert_eq!(iter.next(), Some((0, &'a'))); + /// + /// assert_eq!(iter.next_index(), 1); + /// assert_eq!(iter.next_index(), 1); + /// assert_eq!(iter.next(), Some((1, &'b'))); + /// + /// assert_eq!(iter.next_index(), 2); + /// assert_eq!(iter.next(), None); + /// assert_eq!(iter.next_index(), 2); + /// ``` + #[inline] + #[unstable(feature = "next_index", issue = "130711")] + pub fn next_index(&self) -> usize { + self.count + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -36,7 +69,7 @@ where /// /// 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. + /// overflow checks are enabled, a panic is guaranteed. /// /// # Panics /// diff --git a/library/core/src/iter/adapters/flatten.rs b/library/core/src/iter/adapters/flatten.rs index 9b9353b800a98..a820045521b9f 100644 --- a/library/core/src/iter/adapters/flatten.rs +++ b/library/core/src/iter/adapters/flatten.rs @@ -172,61 +172,6 @@ where } } -/// Marker trait for iterators/iterables which have a statically known upper -/// bound of the number of items they can produce. -/// -/// # Safety -/// -/// Implementations must not yield more elements than indicated by UPPER_BOUND if it is `Some`. -/// Used in specializations. Implementations must not be conditional on lifetimes or -/// user-implementable traits. -#[rustc_specialization_trait] -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe trait BoundedSize { - const UPPER_BOUND: Option> = NonZero::new(1); -} - -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for Option {} -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for option::IntoIter {} -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for Result {} -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for result::IntoIter {} -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for Once {} -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for OnceWith {} -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for [T; N] { - const UPPER_BOUND: Option> = NonZero::new(N); -} -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for array::IntoIter { - const UPPER_BOUND: Option> = NonZero::new(N); -} -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for Filter { - const UPPER_BOUND: Option> = I::UPPER_BOUND; -} -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for FilterMap { - const UPPER_BOUND: Option> = I::UPPER_BOUND; -} -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for Map { - const UPPER_BOUND: Option> = I::UPPER_BOUND; -} -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for Copied { - const UPPER_BOUND: Option> = I::UPPER_BOUND; -} -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl BoundedSize for Cloned { - const UPPER_BOUND: Option> = I::UPPER_BOUND; -} - /// An iterator that flattens one level of nesting in an iterator of things /// that can be turned into iterators. /// diff --git a/library/core/src/iter/adapters/map_windows.rs b/library/core/src/iter/adapters/map_windows.rs index cb13023c85c41..a9c07fee2a91e 100644 --- a/library/core/src/iter/adapters/map_windows.rs +++ b/library/core/src/iter/adapters/map_windows.rs @@ -1,5 +1,5 @@ use crate::iter::FusedIterator; -use crate::mem::{self, MaybeUninit}; +use crate::mem::MaybeUninit; use crate::{fmt, ptr}; /// An iterator over the mapped windows of another iterator. @@ -50,7 +50,7 @@ impl MapWindows { assert!(N != 0, "array in `Iterator::map_windows` must contain more than 0 elements"); // Only ZST arrays' length can be so large. - if mem::size_of::() == 0 { + if size_of::() == 0 { assert!( N.checked_mul(2).is_some(), "array size of `Iterator::map_windows` is too large" diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index cc12cd9c35601..a6522659620a0 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -271,7 +271,7 @@ impl Peekable { /// assert_eq!(iter.next_if(|&x| x == 0), Some(0)); /// // The next item returned is now 1, so `next_if` will return `None`. /// 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`. + /// // `next_if` retains the next item if the predicate evaluates to `false` for it. /// assert_eq!(iter.next(), Some(1)); /// ``` /// @@ -304,9 +304,9 @@ impl Peekable { /// 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 `next_if` will return `None`. + /// // The next item returned is now 1, so `next_if_eq` will return `None`. /// 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`. + /// // `next_if_eq` retains the next item if it was not equal to `expected`. /// assert_eq!(iter.next(), Some(1)); /// ``` #[stable(feature = "peekable_next_if", since = "1.51.0")] diff --git a/library/core/src/iter/sources/repeat.rs b/library/core/src/iter/sources/repeat.rs index 243f938bce2af..c4f5a483e5c2a 100644 --- a/library/core/src/iter/sources/repeat.rs +++ b/library/core/src/iter/sources/repeat.rs @@ -56,7 +56,7 @@ use crate::num::NonZero; /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "iter_repeat")] +#[rustc_diagnostic_item = "iter_repeat"] pub fn repeat(elt: T) -> Repeat { Repeat { element: elt } } diff --git a/library/core/src/iter/sources/repeat_n.rs b/library/core/src/iter/sources/repeat_n.rs index cc089c617c0e3..ada37b9af4c8a 100644 --- a/library/core/src/iter/sources/repeat_n.rs +++ b/library/core/src/iter/sources/repeat_n.rs @@ -1,7 +1,8 @@ use crate::fmt; use crate::iter::{FusedIterator, TrustedLen, UncheckedIterator}; -use crate::mem::{self, MaybeUninit}; +use crate::mem::MaybeUninit; use crate::num::NonZero; +use crate::ops::{NeverShortCircuit, Try}; /// Creates a new iterator that repeats a single element a given number of times. /// @@ -95,10 +96,10 @@ impl RepeatN { fn take_element(&mut self) -> Option { if self.count > 0 { self.count = 0; - let element = mem::replace(&mut self.element, MaybeUninit::uninit()); // SAFETY: We just set count to zero so it won't be dropped again, // and it used to be non-zero so it hasn't already been dropped. - unsafe { Some(element.assume_init()) } + let element = unsafe { self.element.assume_init_read() }; + Some(element) } else { None } @@ -169,6 +170,39 @@ impl Iterator for RepeatN { } } + fn try_fold(&mut self, mut acc: B, mut f: F) -> R + where + F: FnMut(B, A) -> R, + R: Try, + { + if self.count > 0 { + while self.count > 1 { + self.count -= 1; + // SAFETY: the count was larger than 1, so the element is + // initialized and hasn't been dropped. + acc = f(acc, unsafe { self.element.assume_init_ref().clone() })?; + } + + // We could just set the count to zero directly, but doing it this + // way should make it easier for the optimizer to fold this tail + // into the loop when `clone()` is equivalent to copying. + self.count -= 1; + // SAFETY: we just set the count to zero from one, so the element + // is still initialized, has not been dropped yet and will not be + // accessed by future calls. + f(acc, unsafe { self.element.assume_init_read() }) + } else { + try { acc } + } + } + + fn fold(mut self, init: B, f: F) -> B + where + F: FnMut(B, A) -> B, + { + self.try_fold(init, NeverShortCircuit::wrap_mut_2(f)).0 + } + #[inline] fn last(mut self) -> Option { self.take_element() @@ -203,6 +237,23 @@ impl DoubleEndedIterator for RepeatN { fn nth_back(&mut self, n: usize) -> Option { self.nth(n) } + + #[inline] + fn try_rfold(&mut self, init: B, f: F) -> R + where + F: FnMut(B, A) -> R, + R: Try, + { + self.try_fold(init, f) + } + + #[inline] + fn rfold(self, init: B, f: F) -> B + where + F: FnMut(B, A) -> B, + { + self.fold(init, f) + } } #[stable(feature = "iter_repeat_n", since = "1.82.0")] @@ -220,7 +271,7 @@ impl UncheckedIterator for RepeatN { // SAFETY: the check above ensured that the count used to be non-zero, // so element hasn't been dropped yet, and we just lowered the count to // zero so it won't be dropped later, and thus it's okay to take it here. - unsafe { mem::replace(&mut self.element, MaybeUninit::uninit()).assume_init() } + unsafe { self.element.assume_init_read() } } else { // SAFETY: the count is non-zero, so it must have not been dropped yet. let element = unsafe { self.element.assume_init_ref() }; diff --git a/library/core/src/iter/sources/successors.rs b/library/core/src/iter/sources/successors.rs index e14c9235e5562..5466131903f85 100644 --- a/library/core/src/iter/sources/successors.rs +++ b/library/core/src/iter/sources/successors.rs @@ -1,11 +1,16 @@ use crate::fmt; use crate::iter::FusedIterator; -/// Creates a new iterator where each successive item is computed based on the preceding one. +/// Creates an iterator which, starting from an initial item, +/// computes each successive item from the preceding one. /// -/// The iterator starts with the given first item (if any) -/// and calls the given `FnMut(&T) -> Option` closure to compute each item’s successor. -/// The iterator will yield the `T`s returned from the closure. +/// This iterator stores an optional item (`Option`) and a successor closure (`impl FnMut(&T) -> Option`). +/// Its `next` method returns the stored optional item and +/// if it is `Some(val)` calls the stored closure on `&val` to compute and store its successor. +/// The iterator will apply the closure successively to the stored option's value until the option is `None`. +/// This also means that once the stored option is `None` it will remain `None`, +/// as the closure will not be called again, so the created iterator is a [`FusedIterator`]. +/// The iterator's items will be the initial item and all of its successors as calculated by the successor closure. /// /// ``` /// use std::iter::successors; @@ -24,7 +29,8 @@ where Successors { next: first, succ } } -/// A new iterator where each successive item is computed based on the preceding one. +/// An iterator which, starting from an initial item, +/// computes each successive item from the preceding one. /// /// This `struct` is created by the [`iter::successors()`] function. /// See its documentation for more. diff --git a/library/core/src/iter/traits/accum.rs b/library/core/src/iter/traits/accum.rs index 5b7d95c2f65e6..12e2b8b393a88 100644 --- a/library/core/src/iter/traits/accum.rs +++ b/library/core/src/iter/traits/accum.rs @@ -10,7 +10,7 @@ use crate::num::Wrapping; /// [`sum()`]: Iterator::sum /// [`FromIterator`]: iter::FromIterator #[stable(feature = "iter_arith_traits", since = "1.12.0")] -#[rustc_on_unimplemented( +#[diagnostic::on_unimplemented( message = "a value of type `{Self}` cannot be made by summing an iterator over elements of type `{A}`", label = "value of type `{Self}` cannot be made by summing a `std::iter::Iterator`" )] @@ -31,7 +31,7 @@ pub trait Sum: Sized { /// [`product()`]: Iterator::product /// [`FromIterator`]: iter::FromIterator #[stable(feature = "iter_arith_traits", since = "1.12.0")] -#[rustc_on_unimplemented( +#[diagnostic::on_unimplemented( message = "a value of type `{Self}` cannot be made by multiplying all elements of type `{A}` from an iterator", label = "value of type `{Self}` cannot be made by multiplying all elements from a `std::iter::Iterator`" )] diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index 3b12678572841..7dabaece95561 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -37,7 +37,7 @@ use crate::ops::{ControlFlow, Try}; /// assert_eq!(None, iter.next_back()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "DoubleEndedIterator")] +#[rustc_diagnostic_item = "DoubleEndedIterator"] pub trait DoubleEndedIterator: Iterator { /// Removes and returns an element from the end of the iterator. /// diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 42886e90f997d..d1e71f0e60f2a 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -56,12 +56,12 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// let mut iter = a.iter(); + /// let mut iter = a.into_iter(); /// /// // A call to next() returns the next value... - /// assert_eq!(Some(&1), iter.next()); - /// assert_eq!(Some(&2), iter.next()); - /// assert_eq!(Some(&3), iter.next()); + /// assert_eq!(Some(1), iter.next()); + /// assert_eq!(Some(2), iter.next()); + /// assert_eq!(Some(3), iter.next()); /// /// // ... and then None once it's over. /// assert_eq!(None, iter.next()); @@ -199,7 +199,7 @@ pub trait Iterator { /// /// The method does no guarding against overflows, so counting elements of /// an iterator with more than [`usize::MAX`] elements either produces the - /// wrong result or panics. If debug assertions are enabled, a panic is + /// wrong result or panics. If overflow checks are enabled, a panic is /// guaranteed. /// /// # Panics @@ -239,10 +239,10 @@ pub trait Iterator { /// /// ``` /// let a = [1, 2, 3]; - /// assert_eq!(a.iter().last(), Some(&3)); + /// assert_eq!(a.into_iter().last(), Some(3)); /// /// let a = [1, 2, 3, 4, 5]; - /// assert_eq!(a.iter().last(), Some(&5)); + /// assert_eq!(a.into_iter().last(), Some(5)); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -284,12 +284,12 @@ pub trait Iterator { /// use std::num::NonZero; /// /// let a = [1, 2, 3, 4]; - /// let mut iter = a.iter(); + /// let mut iter = a.into_iter(); /// /// assert_eq!(iter.advance_by(2), Ok(())); - /// assert_eq!(iter.next(), Some(&3)); + /// assert_eq!(iter.next(), Some(3)); /// assert_eq!(iter.advance_by(0), Ok(())); - /// assert_eq!(iter.advance_by(100), Err(NonZero::new(99).unwrap())); // only `&4` was skipped + /// assert_eq!(iter.advance_by(100), Err(NonZero::new(99).unwrap())); // only `4` was skipped /// ``` #[inline] #[unstable(feature = "iter_advance_by", reason = "recently added", issue = "77404")] @@ -322,7 +322,7 @@ pub trait Iterator { /// /// ``` /// let a = [1, 2, 3]; - /// assert_eq!(a.iter().nth(1), Some(&2)); + /// assert_eq!(a.into_iter().nth(1), Some(2)); /// ``` /// /// Calling `nth()` multiple times doesn't rewind the iterator: @@ -330,9 +330,9 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// let mut iter = a.iter(); + /// let mut iter = a.into_iter(); /// - /// assert_eq!(iter.nth(1), Some(&2)); + /// assert_eq!(iter.nth(1), Some(2)); /// assert_eq!(iter.nth(1), None); /// ``` /// @@ -340,7 +340,7 @@ pub trait Iterator { /// /// ``` /// let a = [1, 2, 3]; - /// assert_eq!(a.iter().nth(10), None); + /// assert_eq!(a.into_iter().nth(10), None); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -385,11 +385,11 @@ pub trait Iterator { /// /// ``` /// let a = [0, 1, 2, 3, 4, 5]; - /// let mut iter = a.iter().step_by(2); + /// let mut iter = a.into_iter().step_by(2); /// - /// assert_eq!(iter.next(), Some(&0)); - /// assert_eq!(iter.next(), Some(&2)); - /// assert_eq!(iter.next(), Some(&4)); + /// assert_eq!(iter.next(), Some(0)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(4)); /// assert_eq!(iter.next(), None); /// ``` #[inline] @@ -417,37 +417,37 @@ pub trait Iterator { /// Basic usage: /// /// ``` - /// let a1 = [1, 2, 3]; - /// let a2 = [4, 5, 6]; + /// let s1 = "abc".chars(); + /// let s2 = "def".chars(); /// - /// let mut iter = a1.iter().chain(a2.iter()); + /// let mut iter = s1.chain(s2); /// - /// assert_eq!(iter.next(), Some(&1)); - /// assert_eq!(iter.next(), Some(&2)); - /// assert_eq!(iter.next(), Some(&3)); - /// assert_eq!(iter.next(), Some(&4)); - /// assert_eq!(iter.next(), Some(&5)); - /// assert_eq!(iter.next(), Some(&6)); + /// assert_eq!(iter.next(), Some('a')); + /// assert_eq!(iter.next(), Some('b')); + /// assert_eq!(iter.next(), Some('c')); + /// assert_eq!(iter.next(), Some('d')); + /// assert_eq!(iter.next(), Some('e')); + /// assert_eq!(iter.next(), Some('f')); /// assert_eq!(iter.next(), None); /// ``` /// /// Since the argument to `chain()` uses [`IntoIterator`], we can pass /// anything that can be converted into an [`Iterator`], not just an - /// [`Iterator`] itself. For example, slices (`&[T]`) implement + /// [`Iterator`] itself. For example, arrays (`[T]`) implement /// [`IntoIterator`], and so can be passed to `chain()` directly: /// /// ``` - /// let s1 = &[1, 2, 3]; - /// let s2 = &[4, 5, 6]; + /// let a1 = [1, 2, 3]; + /// let a2 = [4, 5, 6]; /// - /// let mut iter = s1.iter().chain(s2); + /// let mut iter = a1.into_iter().chain(a2); /// - /// assert_eq!(iter.next(), Some(&1)); - /// assert_eq!(iter.next(), Some(&2)); - /// assert_eq!(iter.next(), Some(&3)); - /// assert_eq!(iter.next(), Some(&4)); - /// assert_eq!(iter.next(), Some(&5)); - /// assert_eq!(iter.next(), Some(&6)); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), Some(4)); + /// assert_eq!(iter.next(), Some(5)); + /// assert_eq!(iter.next(), Some(6)); /// assert_eq!(iter.next(), None); /// ``` /// @@ -496,31 +496,31 @@ pub trait Iterator { /// Basic usage: /// /// ``` - /// let a1 = [1, 2, 3]; - /// let a2 = [4, 5, 6]; + /// let s1 = "abc".chars(); + /// let s2 = "def".chars(); /// - /// let mut iter = a1.iter().zip(a2.iter()); + /// let mut iter = s1.zip(s2); /// - /// assert_eq!(iter.next(), Some((&1, &4))); - /// assert_eq!(iter.next(), Some((&2, &5))); - /// assert_eq!(iter.next(), Some((&3, &6))); + /// assert_eq!(iter.next(), Some(('a', 'd'))); + /// assert_eq!(iter.next(), Some(('b', 'e'))); + /// assert_eq!(iter.next(), Some(('c', 'f'))); /// assert_eq!(iter.next(), None); /// ``` /// /// Since the argument to `zip()` uses [`IntoIterator`], we can pass /// anything that can be converted into an [`Iterator`], not just an - /// [`Iterator`] itself. For example, slices (`&[T]`) implement + /// [`Iterator`] itself. For example, arrays (`[T]`) implement /// [`IntoIterator`], and so can be passed to `zip()` directly: /// /// ``` - /// let s1 = &[1, 2, 3]; - /// let s2 = &[4, 5, 6]; + /// let a1 = [1, 2, 3]; + /// let a2 = [4, 5, 6]; /// - /// let mut iter = s1.iter().zip(s2); + /// let mut iter = a1.into_iter().zip(a2); /// - /// assert_eq!(iter.next(), Some((&1, &4))); - /// assert_eq!(iter.next(), Some((&2, &5))); - /// assert_eq!(iter.next(), Some((&3, &6))); + /// assert_eq!(iter.next(), Some((1, 4))); + /// assert_eq!(iter.next(), Some((2, 5))); + /// assert_eq!(iter.next(), Some((3, 6))); /// assert_eq!(iter.next(), None); /// ``` /// @@ -604,12 +604,12 @@ pub trait Iterator { /// ``` /// #![feature(iter_intersperse)] /// - /// let mut a = [0, 1, 2].iter().intersperse(&100); - /// assert_eq!(a.next(), Some(&0)); // The first element from `a`. - /// assert_eq!(a.next(), Some(&100)); // The separator. - /// assert_eq!(a.next(), Some(&1)); // The next element from `a`. - /// assert_eq!(a.next(), Some(&100)); // The separator. - /// assert_eq!(a.next(), Some(&2)); // The last element from `a`. + /// let mut a = [0, 1, 2].into_iter().intersperse(100); + /// assert_eq!(a.next(), Some(0)); // The first element from `a`. + /// assert_eq!(a.next(), Some(100)); // The separator. + /// assert_eq!(a.next(), Some(1)); // The next element from `a`. + /// assert_eq!(a.next(), Some(100)); // The separator. + /// assert_eq!(a.next(), Some(2)); // The last element from `a`. /// assert_eq!(a.next(), None); // The iterator is finished. /// ``` /// @@ -617,7 +617,8 @@ pub trait Iterator { /// ``` /// #![feature(iter_intersperse)] /// - /// let hello = ["Hello", "World", "!"].iter().copied().intersperse(" ").collect::(); + /// let words = ["Hello", "World", "!"]; + /// let hello: String = words.into_iter().intersperse(" ").collect(); /// assert_eq!(hello, "Hello World !"); /// ``` /// @@ -673,7 +674,7 @@ pub trait Iterator { /// let src = ["Hello", "to", "all", "people", "!!"].iter().copied(); /// /// // The closure mutably borrows its context to generate an item. - /// let mut happy_emojis = [" ❤️ ", " 😀 "].iter().copied(); + /// let mut happy_emojis = [" ❤️ ", " 😀 "].into_iter(); /// let separator = || happy_emojis.next().unwrap_or(" 🦀 "); /// /// let result = src.intersperse_with(separator).collect::(); @@ -734,7 +735,7 @@ pub trait Iterator { /// /// // it won't even execute, as it is lazy. Rust will warn you about this. /// - /// // Instead, use for: + /// // Instead, use a for-loop: /// for x in 0..5 { /// println!("{x}"); /// } @@ -814,10 +815,10 @@ pub trait Iterator { /// ``` /// let a = [0i32, 1, 2]; /// - /// let mut iter = a.iter().filter(|x| x.is_positive()); + /// let mut iter = a.into_iter().filter(|x| x.is_positive()); /// - /// assert_eq!(iter.next(), Some(&1)); - /// assert_eq!(iter.next(), Some(&2)); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); /// assert_eq!(iter.next(), None); /// ``` /// @@ -826,21 +827,20 @@ pub trait Iterator { /// situation, where the type of the closure is a double reference: /// /// ``` - /// let a = [0, 1, 2]; + /// let s = &[0, 1, 2]; /// - /// let mut iter = a.iter().filter(|x| **x > 1); // need two *s! + /// let mut iter = s.iter().filter(|x| **x > 1); // needs two *s! /// /// assert_eq!(iter.next(), Some(&2)); /// assert_eq!(iter.next(), None); /// ``` /// - /// It's common to instead use destructuring on the argument to strip away - /// one: + /// It's common to instead use destructuring on the argument to strip away one: /// /// ``` - /// let a = [0, 1, 2]; + /// let s = &[0, 1, 2]; /// - /// let mut iter = a.iter().filter(|&x| *x > 1); // both & and * + /// let mut iter = s.iter().filter(|&x| *x > 1); // both & and * /// /// assert_eq!(iter.next(), Some(&2)); /// assert_eq!(iter.next(), None); @@ -849,9 +849,9 @@ pub trait Iterator { /// or both: /// /// ``` - /// let a = [0, 1, 2]; + /// let s = &[0, 1, 2]; /// - /// let mut iter = a.iter().filter(|&&x| x > 1); // two &s + /// let mut iter = s.iter().filter(|&&x| x > 1); // two &s /// /// assert_eq!(iter.next(), Some(&2)); /// assert_eq!(iter.next(), None); @@ -862,7 +862,7 @@ pub trait Iterator { /// Note that `iter.filter(f).next()` is equivalent to `iter.find(f)`. #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "iter_filter")] + #[rustc_diagnostic_item = "iter_filter"] fn filter

(self, predicate: P) -> Filter where Self: Sized, @@ -931,7 +931,7 @@ pub trait Iterator { /// /// 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. + /// overflow checks are enabled, a panic is guaranteed. /// /// # Panics /// @@ -945,16 +945,16 @@ pub trait Iterator { /// ``` /// let a = ['a', 'b', 'c']; /// - /// let mut iter = a.iter().enumerate(); + /// let mut iter = a.into_iter().enumerate(); /// - /// assert_eq!(iter.next(), Some((0, &'a'))); - /// assert_eq!(iter.next(), Some((1, &'b'))); - /// assert_eq!(iter.next(), Some((2, &'c'))); + /// assert_eq!(iter.next(), Some((0, 'a'))); + /// assert_eq!(iter.next(), Some((1, 'b'))); + /// assert_eq!(iter.next(), Some((2, 'c'))); /// assert_eq!(iter.next(), None); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "enumerate_method")] + #[rustc_diagnostic_item = "enumerate_method"] fn enumerate(self) -> Enumerate where Self: Sized, @@ -980,19 +980,19 @@ pub trait Iterator { /// ``` /// let xs = [1, 2, 3]; /// - /// let mut iter = xs.iter().peekable(); + /// let mut iter = xs.into_iter().peekable(); /// /// // peek() lets us see into the future - /// assert_eq!(iter.peek(), Some(&&1)); - /// assert_eq!(iter.next(), Some(&1)); + /// assert_eq!(iter.peek(), Some(&1)); + /// assert_eq!(iter.next(), Some(1)); /// - /// assert_eq!(iter.next(), Some(&2)); + /// assert_eq!(iter.next(), Some(2)); /// /// // we can peek() multiple times, the iterator won't advance - /// assert_eq!(iter.peek(), Some(&&3)); - /// assert_eq!(iter.peek(), Some(&&3)); + /// assert_eq!(iter.peek(), Some(&3)); + /// assert_eq!(iter.peek(), Some(&3)); /// - /// assert_eq!(iter.next(), Some(&3)); + /// assert_eq!(iter.next(), Some(3)); /// /// // after the iterator is finished, so is peek() /// assert_eq!(iter.peek(), None); @@ -1005,21 +1005,21 @@ pub trait Iterator { /// ``` /// let xs = [1, 2, 3]; /// - /// let mut iter = xs.iter().peekable(); + /// let mut iter = xs.into_iter().peekable(); /// /// // `peek_mut()` lets us see into the future - /// assert_eq!(iter.peek_mut(), Some(&mut &1)); - /// assert_eq!(iter.peek_mut(), Some(&mut &1)); - /// assert_eq!(iter.next(), Some(&1)); + /// assert_eq!(iter.peek_mut(), Some(&mut 1)); + /// assert_eq!(iter.peek_mut(), Some(&mut 1)); + /// assert_eq!(iter.next(), Some(1)); /// - /// if let Some(mut p) = iter.peek_mut() { - /// assert_eq!(*p, &2); + /// if let Some(p) = iter.peek_mut() { + /// assert_eq!(*p, 2); /// // put a value into the iterator - /// *p = &1000; + /// *p = 1000; /// } /// /// // The value reappears as the iterator continues - /// assert_eq!(iter.collect::>(), vec![&1000, &3]); + /// assert_eq!(iter.collect::>(), vec![1000, 3]); /// ``` /// [`peek`]: Peekable::peek /// [`peek_mut`]: Peekable::peek_mut @@ -1051,10 +1051,10 @@ pub trait Iterator { /// ``` /// let a = [-1i32, 0, 1]; /// - /// let mut iter = a.iter().skip_while(|x| x.is_negative()); + /// let mut iter = a.into_iter().skip_while(|x| x.is_negative()); /// - /// assert_eq!(iter.next(), Some(&0)); - /// assert_eq!(iter.next(), Some(&1)); + /// assert_eq!(iter.next(), Some(0)); + /// assert_eq!(iter.next(), Some(1)); /// assert_eq!(iter.next(), None); /// ``` /// @@ -1063,9 +1063,9 @@ pub trait Iterator { /// situation, where the type of the closure argument is a double reference: /// /// ``` - /// let a = [-1, 0, 1]; + /// let s = &[-1, 0, 1]; /// - /// let mut iter = a.iter().skip_while(|x| **x < 0); // need two *s! + /// let mut iter = s.iter().skip_while(|x| **x < 0); // need two *s! /// /// assert_eq!(iter.next(), Some(&0)); /// assert_eq!(iter.next(), Some(&1)); @@ -1077,14 +1077,14 @@ pub trait Iterator { /// ``` /// let a = [-1, 0, 1, -2]; /// - /// let mut iter = a.iter().skip_while(|x| **x < 0); + /// let mut iter = a.into_iter().skip_while(|&x| x < 0); /// - /// assert_eq!(iter.next(), Some(&0)); - /// assert_eq!(iter.next(), Some(&1)); + /// assert_eq!(iter.next(), Some(0)); + /// assert_eq!(iter.next(), Some(1)); /// /// // while this would have been false, since we already got a false, /// // skip_while() isn't used any more - /// assert_eq!(iter.next(), Some(&-2)); + /// assert_eq!(iter.next(), Some(-2)); /// /// assert_eq!(iter.next(), None); /// ``` @@ -1115,9 +1115,9 @@ pub trait Iterator { /// ``` /// let a = [-1i32, 0, 1]; /// - /// let mut iter = a.iter().take_while(|x| x.is_negative()); + /// let mut iter = a.into_iter().take_while(|x| x.is_negative()); /// - /// assert_eq!(iter.next(), Some(&-1)); + /// assert_eq!(iter.next(), Some(-1)); /// assert_eq!(iter.next(), None); /// ``` /// @@ -1126,9 +1126,9 @@ pub trait Iterator { /// situation, where the type of the closure is a double reference: /// /// ``` - /// let a = [-1, 0, 1]; + /// let s = &[-1, 0, 1]; /// - /// let mut iter = a.iter().take_while(|x| **x < 0); // need two *s! + /// let mut iter = s.iter().take_while(|x| **x < 0); // need two *s! /// /// assert_eq!(iter.next(), Some(&-1)); /// assert_eq!(iter.next(), None); @@ -1139,12 +1139,12 @@ pub trait Iterator { /// ``` /// let a = [-1, 0, 1, -2]; /// - /// let mut iter = a.iter().take_while(|x| **x < 0); + /// let mut iter = a.into_iter().take_while(|&x| x < 0); /// - /// assert_eq!(iter.next(), Some(&-1)); + /// assert_eq!(iter.next(), Some(-1)); /// /// // We have more elements that are less than zero, but since we already - /// // got a false, take_while() isn't used any more + /// // got a false, take_while() ignores the remaining elements. /// assert_eq!(iter.next(), None); /// ``` /// @@ -1154,18 +1154,15 @@ pub trait Iterator { /// /// ``` /// let a = [1, 2, 3, 4]; - /// let mut iter = a.iter(); + /// let mut iter = a.into_iter(); /// - /// let result: Vec = iter.by_ref() - /// .take_while(|n| **n != 3) - /// .cloned() - /// .collect(); + /// let result: Vec = iter.by_ref().take_while(|&n| n != 3).collect(); /// - /// assert_eq!(result, &[1, 2]); + /// assert_eq!(result, [1, 2]); /// - /// let result: Vec = iter.cloned().collect(); + /// let result: Vec = iter.collect(); /// - /// assert_eq!(result, &[4]); + /// assert_eq!(result, [4]); /// ``` /// /// The `3` is no longer there, because it was consumed in order to see if @@ -1193,7 +1190,7 @@ pub trait Iterator { /// ``` /// let a = [-1i32, 4, 0, 1]; /// - /// let mut iter = a.iter().map_while(|x| 16i32.checked_div(*x)); + /// let mut iter = a.into_iter().map_while(|x| 16i32.checked_div(x)); /// /// assert_eq!(iter.next(), Some(-16)); /// assert_eq!(iter.next(), Some(4)); @@ -1208,8 +1205,8 @@ pub trait Iterator { /// ``` /// let a = [-1i32, 4, 0, 1]; /// - /// let mut iter = a.iter() - /// .map(|x| 16i32.checked_div(*x)) + /// let mut iter = a.into_iter() + /// .map(|x| 16i32.checked_div(x)) /// .take_while(|x| x.is_some()) /// .map(|x| x.unwrap()); /// @@ -1223,12 +1220,12 @@ pub trait Iterator { /// ``` /// let a = [0, 1, 2, -3, 4, 5, -6]; /// - /// let iter = a.iter().map_while(|x| u32::try_from(*x).ok()); - /// let vec = iter.collect::>(); + /// let iter = a.into_iter().map_while(|x| u32::try_from(x).ok()); + /// let vec: Vec<_> = iter.collect(); /// - /// // We have more elements which could fit in u32 (4, 5), but `map_while` returned `None` for `-3` + /// // We have more elements that could fit in u32 (such as 4, 5), but `map_while` returned `None` for `-3` /// // (as the `predicate` returned `None`) and `collect` stops at the first `None` encountered. - /// assert_eq!(vec, vec![0, 1, 2]); + /// assert_eq!(vec, [0, 1, 2]); /// ``` /// /// Because `map_while()` needs to look at the value in order to see if it @@ -1237,17 +1234,17 @@ pub trait Iterator { /// /// ``` /// let a = [1, 2, -3, 4]; - /// let mut iter = a.iter(); + /// let mut iter = a.into_iter(); /// /// let result: Vec = iter.by_ref() - /// .map_while(|n| u32::try_from(*n).ok()) + /// .map_while(|n| u32::try_from(n).ok()) /// .collect(); /// - /// assert_eq!(result, &[1, 2]); + /// assert_eq!(result, [1, 2]); /// - /// let result: Vec = iter.cloned().collect(); + /// let result: Vec = iter.collect(); /// - /// assert_eq!(result, &[4]); + /// assert_eq!(result, [4]); /// ``` /// /// The `-3` is no longer there, because it was consumed in order to see if @@ -1255,7 +1252,7 @@ pub trait Iterator { /// /// Note that unlike [`take_while`] this iterator is **not** fused. /// It is also not specified what this iterator returns after the first [`None`] is returned. - /// If you need fused iterator, use [`fuse`]. + /// If you need a fused iterator, use [`fuse`]. /// /// [`fuse`]: Iterator::fuse #[inline] @@ -1282,9 +1279,9 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// let mut iter = a.iter().skip(2); + /// let mut iter = a.into_iter().skip(2); /// - /// assert_eq!(iter.next(), Some(&3)); + /// assert_eq!(iter.next(), Some(3)); /// assert_eq!(iter.next(), None); /// ``` #[inline] @@ -1312,10 +1309,10 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// let mut iter = a.iter().take(2); + /// let mut iter = a.into_iter().take(2); /// - /// assert_eq!(iter.next(), Some(&1)); - /// assert_eq!(iter.next(), Some(&2)); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); /// assert_eq!(iter.next(), None); /// ``` /// @@ -1340,6 +1337,25 @@ pub trait Iterator { /// assert_eq!(iter.next(), Some(2)); /// assert_eq!(iter.next(), None); /// ``` + /// + /// Use [`by_ref`] to take from the iterator without consuming it, and then + /// continue using the original iterator: + /// + /// ``` + /// let mut words = ["hello", "world", "of", "Rust"].into_iter(); + /// + /// // Take the first two words. + /// let hello_world: Vec<_> = words.by_ref().take(2).collect(); + /// assert_eq!(hello_world, vec!["hello", "world"]); + /// + /// // Collect the rest of the words. + /// // We can only do this because we used `by_ref` earlier. + /// let of_rust: Vec<_> = words.collect(); + /// assert_eq!(of_rust, vec!["of", "Rust"]); + /// ``` + /// + /// [`by_ref`]: Iterator::by_ref + #[doc(alias = "limit")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn take(self, n: usize) -> Take @@ -1370,7 +1386,7 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3, 4]; /// - /// let mut iter = a.iter().scan(1, |state, &x| { + /// let mut iter = a.into_iter().scan(1, |state, x| { /// // each iteration, we'll multiply the state by the element ... /// *state = *state * x; /// @@ -1448,8 +1464,8 @@ pub trait Iterator { /// /// ``` /// let data = vec![vec![1, 2, 3, 4], vec![5, 6]]; - /// let flattened = data.into_iter().flatten().collect::>(); - /// assert_eq!(flattened, &[1, 2, 3, 4, 5, 6]); + /// let flattened: Vec<_> = data.into_iter().flatten().collect(); + /// assert_eq!(flattened, [1, 2, 3, 4, 5, 6]); /// ``` /// /// Mapping and then flattening: @@ -1483,11 +1499,11 @@ pub trait Iterator { /// ``` /// let options = vec![Some(123), Some(321), None, Some(231)]; /// let flattened_options: Vec<_> = options.into_iter().flatten().collect(); - /// assert_eq!(flattened_options, vec![123, 321, 231]); + /// assert_eq!(flattened_options, [123, 321, 231]); /// /// let results = vec![Ok(123), Ok(321), Err(456), Ok(231)]; /// let flattened_results: Vec<_> = results.into_iter().flatten().collect(); - /// assert_eq!(flattened_results, vec![123, 321, 231]); + /// assert_eq!(flattened_results, [123, 321, 231]); /// ``` /// /// Flattening only removes one level of nesting at a time: @@ -1495,11 +1511,11 @@ pub trait Iterator { /// ``` /// let d3 = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]; /// - /// let d2 = d3.iter().flatten().collect::>(); - /// assert_eq!(d2, [&[1, 2], &[3, 4], &[5, 6], &[7, 8]]); + /// let d2: Vec<_> = d3.into_iter().flatten().collect(); + /// assert_eq!(d2, [[1, 2], [3, 4], [5, 6], [7, 8]]); /// - /// let d1 = d3.iter().flatten().flatten().collect::>(); - /// assert_eq!(d1, [&1, &2, &3, &4, &5, &6, &7, &8]); + /// let d1: Vec<_> = d3.into_iter().flatten().flatten().collect(); + /// assert_eq!(d1, [1, 2, 3, 4, 5, 6, 7, 8]); /// ``` /// /// Here we see that `flatten()` does not perform a "deep" flatten. @@ -1704,11 +1720,7 @@ pub trait Iterator { /// self.state = self.state + 1; /// /// // if it's even, Some(i32), else None - /// if val % 2 == 0 { - /// Some(val) - /// } else { - /// None - /// } + /// (val % 2 == 0).then_some(val) /// } /// } /// @@ -1825,10 +1837,19 @@ pub trait Iterator { Inspect::new(self, f) } - /// Borrows an iterator, rather than consuming it. + /// Creates a "by reference" adapter for this instance of `Iterator`. + /// + /// Consuming method calls (direct or indirect calls to `next`) + /// on the "by reference" adapter will consume the original iterator, + /// but ownership-taking methods (those with a `self` parameter) + /// only take ownership of the "by reference" iterator. /// - /// This is useful to allow applying iterator adapters while still - /// retaining ownership of the original iterator. + /// This is useful for applying ownership-taking methods + /// (such as `take` in the example below) + /// without giving up ownership of the original iterator, + /// so you can use the original iterator afterwards. + /// + /// Uses [impl Iterator for &mut I { type Item = I::Item; ...}](https://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#impl-Iterator-for-%26mut+I). /// /// # Examples /// @@ -1881,7 +1902,7 @@ pub trait Iterator { /// let a = [1, 2, 3]; /// /// let doubled: Vec = a.iter() - /// .map(|&x| x * 2) + /// .map(|x| x * 2) /// .collect(); /// /// assert_eq!(vec![2, 4, 6], doubled); @@ -1897,7 +1918,7 @@ pub trait Iterator { /// /// let a = [1, 2, 3]; /// - /// let doubled: VecDeque = a.iter().map(|&x| x * 2).collect(); + /// let doubled: VecDeque = a.iter().map(|x| x * 2).collect(); /// /// assert_eq!(2, doubled[0]); /// assert_eq!(4, doubled[1]); @@ -1930,8 +1951,8 @@ pub trait Iterator { /// ``` /// let chars = ['g', 'd', 'k', 'k', 'n']; /// - /// let hello: String = chars.iter() - /// .map(|&x| x as u8) + /// let hello: String = chars.into_iter() + /// .map(|x| x as u8) /// .map(|x| (x + 1) as char) /// .collect(); /// @@ -1944,14 +1965,14 @@ pub trait Iterator { /// ``` /// let results = [Ok(1), Err("nope"), Ok(3), Err("bad")]; /// - /// let result: Result, &str> = results.iter().cloned().collect(); + /// let result: Result, &str> = results.into_iter().collect(); /// /// // gives us the first error /// assert_eq!(Err("nope"), result); /// /// let results = [Ok(1), Ok(3)]; /// - /// let result: Result, &str> = results.iter().cloned().collect(); + /// let result: Result, &str> = results.into_iter().collect(); /// /// // gives us the list of answers /// assert_eq!(Ok(vec![1, 3]), result); @@ -1963,11 +1984,20 @@ pub trait Iterator { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead"] - #[cfg_attr(not(test), rustc_diagnostic_item = "iterator_collect_fn")] + #[rustc_diagnostic_item = "iterator_collect_fn"] fn collect>(self) -> B where Self: Sized, { + // This is too aggressive to turn on for everything all the time, but PR#137908 + // accidentally noticed that some rustc iterators had malformed `size_hint`s, + // so this will help catch such things in debug-assertions-std runners, + // even if users won't actually ever see it. + if cfg!(debug_assertions) { + let hint = self.size_hint(); + assert!(hint.1.is_none_or(|high| high >= hint.0), "Malformed size_hint {hint:?}"); + } + FromIterator::from_iter(self) } @@ -2073,8 +2103,8 @@ pub trait Iterator { /// let a = [1, 2, 3]; /// let mut vec: Vec:: = vec![0, 1]; /// - /// a.iter().map(|&x| x * 2).collect_into(&mut vec); - /// a.iter().map(|&x| x * 10).collect_into(&mut vec); + /// a.iter().map(|x| x * 2).collect_into(&mut vec); + /// a.iter().map(|x| x * 10).collect_into(&mut vec); /// /// assert_eq!(vec, vec![0, 1, 2, 4, 6, 10, 20, 30]); /// ``` @@ -2087,8 +2117,8 @@ pub trait Iterator { /// let a = [1, 2, 3]; /// let mut vec: Vec:: = Vec::with_capacity(6); /// - /// a.iter().map(|&x| x * 2).collect_into(&mut vec); - /// a.iter().map(|&x| x * 10).collect_into(&mut vec); + /// a.iter().map(|x| x * 2).collect_into(&mut vec); + /// a.iter().map(|x| x * 10).collect_into(&mut vec); /// /// assert_eq!(6, vec.capacity()); /// assert_eq!(vec, vec![2, 4, 6, 10, 20, 30]); @@ -2142,8 +2172,8 @@ pub trait Iterator { /// .into_iter() /// .partition(|n| n % 2 == 0); /// - /// assert_eq!(even, vec![2]); - /// assert_eq!(odd, vec![1, 3]); + /// assert_eq!(even, [2]); + /// assert_eq!(odd, [1, 3]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn partition(self, f: F) -> (B, B) @@ -2201,11 +2231,11 @@ pub trait Iterator { /// let mut a = [1, 2, 3, 4, 5, 6, 7]; /// /// // Partition in-place between evens and odds - /// let i = a.iter_mut().partition_in_place(|&n| n % 2 == 0); + /// let i = a.iter_mut().partition_in_place(|n| n % 2 == 0); /// /// assert_eq!(i, 3); - /// assert!(a[..i].iter().all(|&n| n % 2 == 0)); // evens - /// assert!(a[i..].iter().all(|&n| n % 2 == 1)); // odds + /// assert!(a[..i].iter().all(|n| n % 2 == 0)); // evens + /// assert!(a[i..].iter().all(|n| n % 2 == 1)); // odds /// ``` #[unstable(feature = "iter_partition_in_place", reason = "new API", issue = "62543")] fn partition_in_place<'a, T: 'a, P>(mut self, ref mut predicate: P) -> usize @@ -2312,7 +2342,7 @@ pub trait Iterator { /// let a = [1, 2, 3]; /// /// // the checked sum of all of the elements of the array - /// let sum = a.iter().try_fold(0i8, |acc, &x| acc.checked_add(x)); + /// let sum = a.into_iter().try_fold(0i8, |acc, x| acc.checked_add(x)); /// /// assert_eq!(sum, Some(6)); /// ``` @@ -2321,16 +2351,16 @@ pub trait Iterator { /// /// ``` /// let a = [10, 20, 30, 100, 40, 50]; - /// let mut it = a.iter(); + /// let mut iter = a.into_iter(); /// /// // This sum overflows when adding the 100 element - /// let sum = it.try_fold(0i8, |acc, &x| acc.checked_add(x)); + /// let sum = iter.try_fold(0i8, |acc, x| acc.checked_add(x)); /// assert_eq!(sum, None); /// /// // Because it short-circuited, the remaining elements are still /// // available through the iterator. - /// assert_eq!(it.len(), 2); - /// assert_eq!(it.next(), Some(&40)); + /// assert_eq!(iter.len(), 2); + /// assert_eq!(iter.next(), Some(40)); /// ``` /// /// While you cannot `break` from a closure, the [`ControlFlow`] type allows @@ -2683,9 +2713,9 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// assert!(a.iter().all(|&x| x > 0)); + /// assert!(a.into_iter().all(|x| x > 0)); /// - /// assert!(!a.iter().all(|&x| x > 2)); + /// assert!(!a.into_iter().all(|x| x > 2)); /// ``` /// /// Stopping at the first `false`: @@ -2693,12 +2723,12 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// let mut iter = a.iter(); + /// let mut iter = a.into_iter(); /// - /// assert!(!iter.all(|&x| x != 2)); + /// assert!(!iter.all(|x| x != 2)); /// /// // we can still use `iter`, as there are more elements. - /// assert_eq!(iter.next(), Some(&3)); + /// assert_eq!(iter.next(), Some(3)); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -2736,9 +2766,9 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// assert!(a.iter().any(|&x| x > 0)); + /// assert!(a.into_iter().any(|x| x > 0)); /// - /// assert!(!a.iter().any(|&x| x > 5)); + /// assert!(!a.into_iter().any(|x| x > 5)); /// ``` /// /// Stopping at the first `true`: @@ -2746,12 +2776,12 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// let mut iter = a.iter(); + /// let mut iter = a.into_iter(); /// - /// assert!(iter.any(|&x| x != 2)); + /// assert!(iter.any(|x| x != 2)); /// /// // we can still use `iter`, as there are more elements. - /// assert_eq!(iter.next(), Some(&2)); + /// assert_eq!(iter.next(), Some(2)); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -2797,9 +2827,8 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// assert_eq!(a.iter().find(|&&x| x == 2), Some(&2)); - /// - /// assert_eq!(a.iter().find(|&&x| x == 5), None); + /// assert_eq!(a.into_iter().find(|&x| x == 2), Some(2)); + /// assert_eq!(a.into_iter().find(|&x| x == 5), None); /// ``` /// /// Stopping at the first `true`: @@ -2807,12 +2836,12 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// let mut iter = a.iter(); + /// let mut iter = a.into_iter(); /// - /// assert_eq!(iter.find(|&&x| x == 2), Some(&2)); + /// assert_eq!(iter.find(|&x| x == 2), Some(2)); /// /// // we can still use `iter`, as there are more elements. - /// assert_eq!(iter.next(), Some(&3)); + /// assert_eq!(iter.next(), Some(3)); /// ``` /// /// Note that `iter.find(f)` is equivalent to `iter.filter(f).next()`. @@ -2880,13 +2909,13 @@ pub trait Iterator { /// let a = ["1", "2", "lol", "NaN", "5"]; /// /// let is_my_num = |s: &str, search: i32| -> Result { - /// Ok(s.parse::()? == search) + /// Ok(s.parse::()? == search) /// }; /// - /// let result = a.iter().try_find(|&&s| is_my_num(s, 2)); - /// assert_eq!(result, Ok(Some(&"2"))); + /// let result = a.into_iter().try_find(|&s| is_my_num(s, 2)); + /// assert_eq!(result, Ok(Some("2"))); /// - /// let result = a.iter().try_find(|&&s| is_my_num(s, 5)); + /// let result = a.into_iter().try_find(|&s| is_my_num(s, 5)); /// assert!(result.is_err()); /// ``` /// @@ -2898,11 +2927,11 @@ pub trait Iterator { /// use std::num::NonZero; /// /// let a = [3, 5, 7, 4, 9, 0, 11u32]; - /// let result = a.iter().try_find(|&&x| NonZero::new(x).map(|y| y.is_power_of_two())); - /// assert_eq!(result, Some(Some(&4))); - /// let result = a.iter().take(3).try_find(|&&x| NonZero::new(x).map(|y| y.is_power_of_two())); + /// let result = a.into_iter().try_find(|&x| NonZero::new(x).map(|y| y.is_power_of_two())); + /// assert_eq!(result, Some(Some(4))); + /// let result = a.into_iter().take(3).try_find(|&x| NonZero::new(x).map(|y| y.is_power_of_two())); /// assert_eq!(result, Some(None)); - /// let result = a.iter().rev().try_find(|&&x| NonZero::new(x).map(|y| y.is_power_of_two())); + /// let result = a.into_iter().rev().try_find(|&x| NonZero::new(x).map(|y| y.is_power_of_two())); /// assert_eq!(result, None); /// ``` #[inline] @@ -2950,7 +2979,7 @@ pub trait Iterator { /// /// The method does no guarding against overflows, so if there are more /// than [`usize::MAX`] non-matching elements, it either produces the wrong - /// result or panics. If debug assertions are enabled, a panic is + /// result or panics. If overflow checks are enabled, a panic is /// guaranteed. /// /// # Panics @@ -2967,9 +2996,9 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// assert_eq!(a.iter().position(|&x| x == 2), Some(1)); + /// assert_eq!(a.into_iter().position(|x| x == 2), Some(1)); /// - /// assert_eq!(a.iter().position(|&x| x == 5), None); + /// assert_eq!(a.into_iter().position(|x| x == 5), None); /// ``` /// /// Stopping at the first `true`: @@ -2977,15 +3006,15 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3, 4]; /// - /// let mut iter = a.iter(); + /// let mut iter = a.into_iter(); /// - /// assert_eq!(iter.position(|&x| x >= 2), Some(1)); + /// assert_eq!(iter.position(|x| x >= 2), Some(1)); /// /// // we can still use `iter`, as there are more elements. - /// assert_eq!(iter.next(), Some(&3)); + /// assert_eq!(iter.next(), Some(3)); /// /// // The returned index depends on iterator state - /// assert_eq!(iter.position(|&x| x == 4), Some(0)); + /// assert_eq!(iter.position(|x| x == 4), Some(0)); /// /// ``` #[inline] @@ -3035,9 +3064,9 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// assert_eq!(a.iter().rposition(|&x| x == 3), Some(2)); + /// assert_eq!(a.into_iter().rposition(|x| x == 3), Some(2)); /// - /// assert_eq!(a.iter().rposition(|&x| x == 5), None); + /// assert_eq!(a.into_iter().rposition(|x| x == 5), None); /// ``` /// /// Stopping at the first `true`: @@ -3045,13 +3074,13 @@ pub trait Iterator { /// ``` /// let a = [-1, 2, 3, 4]; /// - /// let mut iter = a.iter(); + /// let mut iter = a.into_iter(); /// - /// assert_eq!(iter.rposition(|&x| x >= 2), Some(3)); + /// assert_eq!(iter.rposition(|x| x >= 2), Some(3)); /// /// // we can still use `iter`, as there are more elements. - /// assert_eq!(iter.next(), Some(&-1)); - /// assert_eq!(iter.next_back(), Some(&3)); + /// assert_eq!(iter.next(), Some(-1)); + /// assert_eq!(iter.next_back(), Some(3)); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -3097,10 +3126,10 @@ pub trait Iterator { /// /// ``` /// let a = [1, 2, 3]; - /// let b: Vec = Vec::new(); + /// let b: [u32; 0] = []; /// - /// assert_eq!(a.iter().max(), Some(&3)); - /// assert_eq!(b.iter().max(), None); + /// assert_eq!(a.into_iter().max(), Some(3)); + /// assert_eq!(b.into_iter().max(), None); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -3133,10 +3162,10 @@ pub trait Iterator { /// /// ``` /// let a = [1, 2, 3]; - /// let b: Vec = Vec::new(); + /// let b: [u32; 0] = []; /// - /// assert_eq!(a.iter().min(), Some(&1)); - /// assert_eq!(b.iter().min(), None); + /// assert_eq!(a.into_iter().min(), Some(1)); + /// assert_eq!(b.into_iter().min(), None); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -3158,7 +3187,7 @@ pub trait Iterator { /// /// ``` /// let a = [-3_i32, 0, 1, 5, -10]; - /// assert_eq!(*a.iter().max_by_key(|x| x.abs()).unwrap(), -10); + /// assert_eq!(a.into_iter().max_by_key(|x| x.abs()).unwrap(), -10); /// ``` #[inline] #[stable(feature = "iter_cmp_by_key", since = "1.6.0")] @@ -3191,7 +3220,7 @@ pub trait Iterator { /// /// ``` /// let a = [-3_i32, 0, 1, 5, -10]; - /// assert_eq!(*a.iter().max_by(|x, y| x.cmp(y)).unwrap(), 5); + /// assert_eq!(a.into_iter().max_by(|x, y| x.cmp(y)).unwrap(), 5); /// ``` #[inline] #[stable(feature = "iter_max_by", since = "1.15.0")] @@ -3218,7 +3247,7 @@ pub trait Iterator { /// /// ``` /// let a = [-3_i32, 0, 1, 5, -10]; - /// assert_eq!(*a.iter().min_by_key(|x| x.abs()).unwrap(), 0); + /// assert_eq!(a.into_iter().min_by_key(|x| x.abs()).unwrap(), 0); /// ``` #[inline] #[stable(feature = "iter_cmp_by_key", since = "1.6.0")] @@ -3251,7 +3280,7 @@ pub trait Iterator { /// /// ``` /// let a = [-3_i32, 0, 1, 5, -10]; - /// assert_eq!(*a.iter().min_by(|x, y| x.cmp(y)).unwrap(), -10); + /// assert_eq!(a.into_iter().min_by(|x, y| x.cmp(y)).unwrap(), -10); /// ``` #[inline] #[stable(feature = "iter_min_by", since = "1.15.0")] @@ -3281,11 +3310,11 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// let mut iter = a.iter().rev(); + /// let mut iter = a.into_iter().rev(); /// - /// assert_eq!(iter.next(), Some(&3)); - /// assert_eq!(iter.next(), Some(&2)); - /// assert_eq!(iter.next(), Some(&1)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(1)); /// /// assert_eq!(iter.next(), None); /// ``` @@ -3314,7 +3343,7 @@ pub trait Iterator { /// ``` /// let a = [(1, 2), (3, 4), (5, 6)]; /// - /// let (left, right): (Vec<_>, Vec<_>) = a.iter().cloned().unzip(); + /// let (left, right): (Vec<_>, Vec<_>) = a.into_iter().unzip(); /// /// assert_eq!(left, [1, 3, 5]); /// assert_eq!(right, [2, 4, 6]); @@ -3322,7 +3351,7 @@ pub trait Iterator { /// // you can also unzip multiple nested tuples at once /// let a = [(1, (2, 3)), (4, (5, 6))]; /// - /// let (x, (y, z)): (Vec<_>, (Vec<_>, Vec<_>)) = a.iter().cloned().unzip(); + /// let (x, (y, z)): (Vec<_>, (Vec<_>, Vec<_>)) = a.into_iter().unzip(); /// assert_eq!(x, [1, 4]); /// assert_eq!(y, [2, 5]); /// assert_eq!(z, [3, 6]); @@ -3354,11 +3383,11 @@ pub trait Iterator { /// // copied is the same as .map(|&x| x) /// let v_map: Vec<_> = a.iter().map(|&x| x).collect(); /// - /// assert_eq!(v_copied, vec![1, 2, 3]); - /// assert_eq!(v_map, vec![1, 2, 3]); + /// assert_eq!(v_copied, [1, 2, 3]); + /// assert_eq!(v_map, [1, 2, 3]); /// ``` #[stable(feature = "iter_copied", since = "1.36.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "iter_copied")] + #[rustc_diagnostic_item = "iter_copied"] fn copied<'a, T: 'a>(self) -> Copied where Self: Sized + Iterator, @@ -3390,8 +3419,8 @@ pub trait Iterator { /// // cloned is the same as .map(|&x| x), for integers /// let v_map: Vec<_> = a.iter().map(|&x| x).collect(); /// - /// assert_eq!(v_cloned, vec![1, 2, 3]); - /// assert_eq!(v_map, vec![1, 2, 3]); + /// assert_eq!(v_cloned, [1, 2, 3]); + /// assert_eq!(v_map, [1, 2, 3]); /// ``` /// /// To get the best performance, try to clone late: @@ -3406,7 +3435,7 @@ pub trait Iterator { /// assert_eq!(&[vec![23]], &faster[..]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "iter_cloned")] + #[rustc_diagnostic_item = "iter_cloned"] fn cloned<'a, T: 'a>(self) -> Cloned where Self: Sized + Iterator, @@ -3427,15 +3456,14 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// let mut it = a.iter().cycle(); + /// let mut iter = a.into_iter().cycle(); /// - /// assert_eq!(it.next(), Some(&1)); - /// assert_eq!(it.next(), Some(&2)); - /// assert_eq!(it.next(), Some(&3)); - /// assert_eq!(it.next(), Some(&1)); - /// assert_eq!(it.next(), Some(&2)); - /// assert_eq!(it.next(), Some(&3)); - /// assert_eq!(it.next(), Some(&1)); + /// loop { + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// # break; + /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -3502,7 +3530,7 @@ pub trait Iterator { /// # Panics /// /// When calling `sum()` and a primitive integer type is being returned, this - /// method will panic if the computation overflows and debug assertions are + /// method will panic if the computation overflows and overflow checks are /// enabled. /// /// # Examples @@ -3536,7 +3564,7 @@ pub trait Iterator { /// # Panics /// /// When calling `product()` and a primitive integer type is being returned, - /// method will panic if the computation overflows and debug assertions are + /// method will panic if the computation overflows and overflow checks are /// enabled. /// /// # Examples @@ -3593,9 +3621,9 @@ pub trait Iterator { /// let xs = [1, 2, 3, 4]; /// let ys = [1, 4, 9, 16]; /// - /// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| x.cmp(&y)), Ordering::Less); - /// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| (x * x).cmp(&y)), Ordering::Equal); - /// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| (2 * x).cmp(&y)), Ordering::Greater); + /// assert_eq!(xs.into_iter().cmp_by(ys, |x, y| x.cmp(&y)), Ordering::Less); + /// assert_eq!(xs.into_iter().cmp_by(ys, |x, y| (x * x).cmp(&y)), Ordering::Equal); + /// assert_eq!(xs.into_iter().cmp_by(ys, |x, y| (2 * x).cmp(&y)), Ordering::Greater); /// ``` #[unstable(feature = "iter_order_by", issue = "64295")] fn cmp_by(self, other: I, cmp: F) -> Ordering @@ -3677,15 +3705,15 @@ pub trait Iterator { /// let ys = [1.0, 4.0, 9.0, 16.0]; /// /// assert_eq!( - /// xs.iter().partial_cmp_by(&ys, |&x, &y| x.partial_cmp(&y)), + /// xs.iter().partial_cmp_by(ys, |x, y| x.partial_cmp(&y)), /// Some(Ordering::Less) /// ); /// assert_eq!( - /// xs.iter().partial_cmp_by(&ys, |&x, &y| (x * x).partial_cmp(&y)), + /// xs.iter().partial_cmp_by(ys, |x, y| (x * x).partial_cmp(&y)), /// Some(Ordering::Equal) /// ); /// assert_eq!( - /// xs.iter().partial_cmp_by(&ys, |&x, &y| (2.0 * x).partial_cmp(&y)), + /// xs.iter().partial_cmp_by(ys, |x, y| (2.0 * x).partial_cmp(&y)), /// Some(Ordering::Greater) /// ); /// ``` @@ -3743,7 +3771,7 @@ pub trait Iterator { /// let xs = [1, 2, 3, 4]; /// let ys = [1, 4, 9, 16]; /// - /// assert!(xs.iter().eq_by(&ys, |&x, &y| x * x == y)); + /// assert!(xs.iter().eq_by(ys, |x, y| x * x == y)); /// ``` #[unstable(feature = "iter_order_by", issue = "64295")] fn eq_by(self, other: I, eq: F) -> bool @@ -4024,6 +4052,9 @@ where } } +/// Implements `Iterator` for mutable references to iterators, such as those produced by [`Iterator::by_ref`]. +/// +/// This implementation passes all method calls on to the original iterator. #[stable(feature = "rust1", since = "1.0.0")] impl Iterator for &mut I { type Item = I::Item; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index db68f472c42f6..e605d7e0d7844 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -43,18 +43,6 @@ //! which do not trigger a panic can be assured that this function is never //! called. The `lang` attribute is called `eh_personality`. -// Since core defines many fundamental lang items, all tests live in a -// separate crate, coretests (library/coretests), 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 core) 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_playground_url = "https://play.rust-lang.org/", @@ -64,7 +52,6 @@ )] #![doc(rust_logo)] #![doc(cfg_hide( - not(test), no_fp_fmt_parse, target_pointer_width = "16", target_pointer_width = "32", @@ -113,7 +100,8 @@ #![feature(bigint_helper_methods)] #![feature(bstr)] #![feature(bstr_internals)] -#![feature(closure_track_caller)] +#![feature(cfg_match)] +#![feature(cfg_target_has_reliable_f16_f128)] #![feature(const_carrying_mul_add)] #![feature(const_eval_select)] #![feature(core_intrinsics)] @@ -124,14 +112,12 @@ #![feature(is_ascii_octdigit)] #![feature(lazy_get)] #![feature(link_cfg)] -#![feature(non_null_from_ref)] #![feature(offset_of_enum)] #![feature(panic_internals)] #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] #![feature(set_ptr_value)] #![feature(slice_as_array)] -#![feature(slice_as_chunks)] #![feature(slice_ptr_get)] #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] @@ -139,6 +125,7 @@ #![feature(ub_checks)] #![feature(unchecked_neg)] #![feature(unchecked_shifts)] +#![feature(unsafe_pinned)] #![feature(utf16_extra)] #![feature(variant_count)] // tidy-alphabetical-end @@ -181,7 +168,6 @@ #![feature(negative_impls)] #![feature(never_type)] #![feature(no_core)] -#![feature(no_sanitize)] #![feature(optimize_attribute)] #![feature(prelude_import)] #![feature(repr_simd)] @@ -202,14 +188,17 @@ // // Target features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(avx512_target_feature))] +#![feature(aarch64_unstable_target_feature)] #![feature(arm_target_feature)] -#![feature(avx512_target_feature)] #![feature(hexagon_target_feature)] +#![feature(keylocker_x86)] #![feature(loongarch_target_feature)] #![feature(mips_target_feature)] #![feature(powerpc_target_feature)] #![feature(riscv_target_feature)] #![feature(rtm_target_feature)] +#![feature(s390x_target_feature)] #![feature(sha512_sm_x86)] #![feature(sse4a_target_feature)] #![feature(tbm_target_feature)] @@ -223,15 +212,11 @@ extern crate self as core; #[prelude_import] #[allow(unused)] -use prelude::rust_2021::*; +use prelude::rust_2024::*; -#[cfg(not(test))] // See #65860 #[macro_use] mod macros; -// We don't export this through #[macro_export] for now, to avoid breakage. -// See https://github.com/rust-lang/rust/issues/82913 -#[cfg(not(test))] #[unstable(feature = "assert_matches", issue = "82775")] /// Unstable module containing the unstable `assert_matches` macro. pub mod assert_matches { diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 16200184422b9..7dc8c060cd5bc 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -37,7 +37,7 @@ macro_rules! panic { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "assert_eq_macro")] +#[rustc_diagnostic_item = "assert_eq_macro"] #[allow_internal_unstable(panic_internals)] macro_rules! assert_eq { ($left:expr, $right:expr $(,)?) => { @@ -93,7 +93,7 @@ macro_rules! assert_eq { /// ``` #[macro_export] #[stable(feature = "assert_ne", since = "1.13.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "assert_ne_macro")] +#[rustc_diagnostic_item = "assert_ne_macro"] #[allow_internal_unstable(panic_internals)] macro_rules! assert_ne { ($left:expr, $right:expr $(,)?) => { @@ -237,9 +237,10 @@ pub macro assert_matches { /// ``` #[unstable(feature = "cfg_match", issue = "115585")] #[rustc_diagnostic_item = "cfg_match"] +#[rustc_macro_transparency = "semitransparent"] pub macro cfg_match { ({ $($tt:tt)* }) => {{ - cfg_match! { $($tt)* } + $crate::cfg_match! { $($tt)* } }}, (_ => { $($output:tt)* }) => { $($output)* @@ -249,10 +250,10 @@ pub macro cfg_match { $($( $rest:tt )+)? ) => { #[cfg($cfg)] - cfg_match! { _ => $output } + $crate::cfg_match! { _ => $output } $( #[cfg(not($cfg))] - cfg_match! { $($rest)+ } + $crate::cfg_match! { $($rest)+ } )? }, } @@ -331,7 +332,7 @@ macro_rules! debug_assert { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "debug_assert_eq_macro")] +#[rustc_diagnostic_item = "debug_assert_eq_macro"] macro_rules! debug_assert_eq { ($($arg:tt)*) => { if $crate::cfg!(debug_assertions) { @@ -361,7 +362,7 @@ macro_rules! debug_assert_eq { /// ``` #[macro_export] #[stable(feature = "assert_ne", since = "1.13.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "debug_assert_ne_macro")] +#[rustc_diagnostic_item = "debug_assert_ne_macro"] macro_rules! debug_assert_ne { ($($arg:tt)*) => { if $crate::cfg!(debug_assertions) { @@ -442,7 +443,7 @@ pub macro debug_assert_matches($($arg:tt)*) { /// ``` #[macro_export] #[stable(feature = "matches_macro", since = "1.42.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "matches_macro")] +#[rustc_diagnostic_item = "matches_macro"] macro_rules! matches { ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { match $expression { @@ -617,7 +618,7 @@ macro_rules! r#try { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "write_macro")] +#[rustc_diagnostic_item = "write_macro"] macro_rules! write { ($dst:expr, $($arg:tt)*) => { $dst.write_fmt($crate::format_args!($($arg)*)) @@ -651,7 +652,7 @@ macro_rules! write { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "writeln_macro")] +#[rustc_diagnostic_item = "writeln_macro"] #[allow_internal_unstable(format_args_nl)] macro_rules! writeln { ($dst:expr $(,)?) => { @@ -718,7 +719,7 @@ macro_rules! writeln { #[rustc_builtin_macro(unreachable)] #[allow_internal_unstable(edition_panic)] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "unreachable_macro")] +#[rustc_diagnostic_item = "unreachable_macro"] macro_rules! unreachable { // Expands to either `$crate::panic::unreachable_2015` or `$crate::panic::unreachable_2021` // depending on the edition of the caller. @@ -803,7 +804,7 @@ macro_rules! unreachable { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "unimplemented_macro")] +#[rustc_diagnostic_item = "unimplemented_macro"] #[allow_internal_unstable(panic_internals)] macro_rules! unimplemented { () => { @@ -883,7 +884,7 @@ macro_rules! unimplemented { /// ``` #[macro_export] #[stable(feature = "todo_macro", since = "1.40.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "todo_macro")] +#[rustc_diagnostic_item = "todo_macro"] #[allow_internal_unstable(panic_internals)] macro_rules! todo { () => { @@ -995,7 +996,7 @@ pub(crate) mod builtin { /// and cannot be stored for later use. /// This is a known limitation, see [#92698](https://github.com/rust-lang/rust/issues/92698). #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "format_args_macro")] + #[rustc_diagnostic_item = "format_args_macro"] #[allow_internal_unsafe] #[allow_internal_unstable(fmt_internals)] #[rustc_builtin_macro] @@ -1137,6 +1138,10 @@ pub(crate) mod builtin { issue = "29599", reason = "`concat_idents` is not stable enough for use and is subject to change" )] + #[deprecated( + since = "1.88.0", + note = "use `${concat(...)}` with the `macro_metavar_expr_concat` feature instead" + )] #[rustc_builtin_macro] #[macro_export] macro_rules! concat_idents { @@ -1342,7 +1347,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] - #[cfg_attr(not(test), rustc_diagnostic_item = "include_str_macro")] + #[rustc_diagnostic_item = "include_str_macro"] macro_rules! include_str { ($file:expr $(,)?) => {{ /* compiler built-in */ }}; } @@ -1382,7 +1387,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] - #[cfg_attr(not(test), rustc_diagnostic_item = "include_bytes_macro")] + #[rustc_diagnostic_item = "include_bytes_macro"] macro_rules! include_bytes { ($file:expr $(,)?) => {{ /* compiler built-in */ }}; } @@ -1656,7 +1661,6 @@ pub(crate) mod builtin { #[unstable( feature = "test", issue = "50297", - soft, reason = "`bench` is a part of custom test frameworks which are unstable" )] #[allow_internal_unstable(test, rustc_attrs, coverage_attribute)] @@ -1743,6 +1747,20 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Provide a list of type aliases and other opaque-type-containing type definitions + /// to an item with a body. This list will be used in that body to define opaque + /// types' hidden types. + /// Can only be applied to things that have bodies. + #[unstable( + feature = "type_alias_impl_trait", + issue = "63063", + reason = "`type_alias_impl_trait` has open design concerns" + )] + #[rustc_builtin_macro] + pub macro define_opaque($($tt:tt)*) { + /* compiler built-in */ + } + /// Unstable placeholder for type ascription. #[allow_internal_unstable(builtin_syntax)] #[unstable( diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index b0571bf7247af..f33b8d188d86c 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -17,6 +17,7 @@ use crate::cell::UnsafeCell; use crate::cmp; use crate::fmt::Debug; use crate::hash::{Hash, Hasher}; +use crate::pin::UnsafePinned; /// Implements a given marker trait for multiple types at the same time. /// @@ -82,7 +83,7 @@ macro marker_impls { /// [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")] +#[rustc_diagnostic_item = "Send"] #[diagnostic::on_unimplemented( message = "`{Self}` cannot be sent between threads safely", label = "`{Self}` cannot be sent between threads safely" @@ -405,7 +406,7 @@ marker_impls! { /// /// [`Vec`]: ../../std/vec/struct.Vec.html /// [`String`]: ../../std/string/struct.String.html -/// [`size_of::`]: crate::mem::size_of +/// [`size_of::`]: size_of /// [impls]: #implementors #[stable(feature = "rust1", since = "1.0.0")] #[lang = "copy"] @@ -453,8 +454,8 @@ impl Copy for ! {} #[stable(feature = "rust1", since = "1.0.0")] impl Copy for &T {} -/// Marker trait for the types that are allowed in union fields, unsafe fields, -/// and unsafe binder types. +/// Marker trait for the types that are allowed in union fields and unsafe +/// binder types. /// /// Implemented for: /// * `&T`, `&mut T` for all `T`, @@ -465,9 +466,13 @@ impl Copy for &T {} /// Notably, this doesn't include all trivially-destructible types for semver /// reasons. /// -/// Bikeshed name for now. +/// Bikeshed name for now. This trait does not do anything other than reflect the +/// set of types that are allowed within unions for field validity. #[unstable(feature = "bikeshed_guaranteed_no_drop", issue = "none")] #[lang = "bikeshed_guaranteed_no_drop"] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] +#[doc(hidden)] pub trait BikeshedGuaranteedNoDrop {} /// Types for which it is safe to share references between threads. @@ -541,7 +546,7 @@ pub trait BikeshedGuaranteedNoDrop {} /// [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")] +#[rustc_diagnostic_item = "Sync"] #[lang = "sync"] #[rustc_on_unimplemented( on( @@ -731,7 +736,6 @@ impl !Sync for *mut T {} /// # } /// # fn convert_params(_: ParamType) -> usize { 42 } /// use std::marker::PhantomData; -/// use std::mem; /// /// struct ExternalResource { /// resource_handle: *mut (), @@ -740,7 +744,7 @@ impl !Sync for *mut T {} /// /// impl ExternalResource { /// fn new() -> Self { -/// let size_of_res = mem::size_of::(); +/// let size_of_res = size_of::(); /// Self { /// resource_handle: foreign_lib::new(size_of_res), /// resource_type: PhantomData, @@ -875,6 +879,22 @@ marker_impls! { {T: ?Sized} &mut T, } +/// Used to determine whether a type contains any `UnsafePinned` (or `PhantomPinned`) internally, +/// but not through an indirection. This affects, for example, whether we emit `noalias` metadata +/// for `&mut T` or not. +/// +/// This is part of [RFC 3467](https://rust-lang.github.io/rfcs/3467-unsafe-pinned.html), and is +/// tracked by [#125735](https://github.com/rust-lang/rust/issues/125735). +#[lang = "unsafe_unpin"] +pub(crate) unsafe auto trait UnsafeUnpin {} + +impl !UnsafeUnpin for UnsafePinned {} +unsafe impl UnsafeUnpin for PhantomData {} +unsafe impl UnsafeUnpin for *const T {} +unsafe impl UnsafeUnpin for *mut T {} +unsafe impl UnsafeUnpin for &T {} +unsafe impl UnsafeUnpin for &mut T {} + /// Types that do not require any pinning guarantees. /// /// For information on what "pinning" is, see the [`pin` module] documentation. @@ -950,6 +970,11 @@ 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. +// +// FIXME(unsafe_pinned): This is *not* a stable guarantee we want to make, at least not yet. +// Note that for backwards compatibility with the new [`UnsafePinned`] wrapper type, placing this +// marker in your struct acts as if you wrapped the entire struct in an `UnsafePinned`. This type +// will likely eventually be deprecated, and all new code should be using `UnsafePinned` instead. #[stable(feature = "pin", since = "1.33.0")] #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct PhantomPinned; @@ -957,6 +982,12 @@ pub struct PhantomPinned; #[stable(feature = "pin", since = "1.33.0")] impl !Unpin for PhantomPinned {} +// This is a small hack to allow existing code which uses PhantomPinned to opt-out of noalias to +// continue working. Ideally PhantomPinned could just wrap an `UnsafePinned<()>` to get the same +// effect, but we can't add a new field to an already stable unit struct -- that would be a breaking +// change. +impl !UnsafeUnpin for PhantomPinned {} + marker_impls! { #[stable(feature = "pin", since = "1.33.0")] Unpin for @@ -1302,7 +1333,7 @@ pub trait FnPtr: Copy + Clone { /// ``` #[rustc_builtin_macro(CoercePointee, attributes(pointee))] #[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize, coerce_pointee_validated)] -#[cfg_attr(not(test), rustc_diagnostic_item = "CoercePointee")] +#[rustc_diagnostic_item = "CoercePointee"] #[unstable(feature = "derive_coerce_pointee", issue = "123430")] pub macro CoercePointee($item:item) { /* compiler built-in */ diff --git a/library/core/src/marker/variance.rs b/library/core/src/marker/variance.rs index 23334e6575ddf..235f8a3bb79f2 100644 --- a/library/core/src/marker/variance.rs +++ b/library/core/src/marker/variance.rs @@ -136,6 +136,8 @@ phantom_lifetime! { /// For all `'a`, the following are guaranteed: /// * `size_of::>() == 0` /// * `align_of::>() == 1` + #[rustc_pub_transparent] + #[repr(transparent)] pub struct PhantomCovariantLifetime<'a>(PhantomCovariant<&'a ()>); /// Zero-sized type used to mark a lifetime as contravariant. /// @@ -149,6 +151,8 @@ phantom_lifetime! { /// For all `'a`, the following are guaranteed: /// * `size_of::>() == 0` /// * `align_of::>() == 1` + #[rustc_pub_transparent] + #[repr(transparent)] pub struct PhantomContravariantLifetime<'a>(PhantomContravariant<&'a ()>); /// Zero-sized type used to mark a lifetime as invariant. /// @@ -162,6 +166,8 @@ phantom_lifetime! { /// For all `'a`, the following are guaranteed: /// * `size_of::>() == 0` /// * `align_of::>() == 1` + #[rustc_pub_transparent] + #[repr(transparent)] pub struct PhantomInvariantLifetime<'a>(PhantomInvariant<&'a ()>); } @@ -179,6 +185,8 @@ phantom_type! { /// For all `T`, the following are guaranteed: /// * `size_of::>() == 0` /// * `align_of::>() == 1` + #[rustc_pub_transparent] + #[repr(transparent)] pub struct PhantomCovariant(PhantomData T>); /// Zero-sized type used to mark a type parameter as contravariant. /// @@ -193,6 +201,8 @@ phantom_type! { /// For all `T`, the following are guaranteed: /// * `size_of::>() == 0` /// * `align_of::>() == 1` + #[rustc_pub_transparent] + #[repr(transparent)] pub struct PhantomContravariant(PhantomData); /// Zero-sized type used to mark a type parameter as invariant. /// @@ -206,6 +216,8 @@ phantom_type! { /// For all `T`, the following are guaranteed: /// * `size_of::>() == 0` /// * `align_of::>() == 1` + #[rustc_pub_transparent] + #[repr(transparent)] pub struct PhantomInvariant(PhantomData T>); } diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 067371c1b58ab..63a479ed8dd4e 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -203,7 +203,7 @@ use crate::{fmt, intrinsics, ptr, slice}; /// `MaybeUninit` is guaranteed to have the same size, alignment, and ABI as `T`: /// /// ```rust -/// use std::mem::{MaybeUninit, size_of, align_of}; +/// use std::mem::MaybeUninit; /// assert_eq!(size_of::>(), size_of::()); /// assert_eq!(align_of::>(), align_of::()); /// ``` @@ -215,7 +215,7 @@ use crate::{fmt, intrinsics, ptr, slice}; /// optimizations, potentially resulting in a larger size: /// /// ```rust -/// # use std::mem::{MaybeUninit, size_of}; +/// # use std::mem::MaybeUninit; /// assert_eq!(size_of::>(), 1); /// assert_eq!(size_of::>>(), 2); /// ``` @@ -391,7 +391,7 @@ impl MaybeUninit { /// For your convenience, this also returns a mutable reference to the /// (now safely initialized) contents of `self`. /// - /// As the content is stored inside a `MaybeUninit`, the destructor is not + /// As the content is stored inside a `ManuallyDrop`, the destructor is not /// run for the inner data if the MaybeUninit leaves scope without a call to /// [`assume_init`], [`assume_init_drop`], or similar. Code that receives /// the mutable reference returned by this function needs to keep this in @@ -1065,161 +1065,46 @@ impl MaybeUninit { this.write_clone_of_slice(src) } - /// Fills a slice with elements by cloning `value`, returning a mutable reference to the now - /// initialized contents of the slice. - /// Any previously initialized elements will not be dropped. - /// - /// This is similar to [`slice::fill`]. - /// - /// # Panics - /// - /// This function will panic if any call to `Clone` panics. - /// - /// If such a panic occurs, any elements previously initialized during this operation will be - /// dropped. - /// - /// # Examples - /// - /// ``` - /// #![feature(maybe_uninit_fill)] - /// use std::mem::MaybeUninit; - /// - /// let mut buf = [const { MaybeUninit::uninit() }; 10]; - /// let initialized = MaybeUninit::fill(&mut buf, 1); - /// assert_eq!(initialized, &mut [1; 10]); - /// ``` - #[doc(alias = "memset")] + /// Deprecated version of [`slice::write_filled`]. #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill(this: &mut [MaybeUninit], value: T) -> &mut [T] + #[deprecated( + note = "replaced by inherent write_filled method; will eventually be removed", + since = "1.83.0" + )] + pub fn fill<'a>(this: &'a mut [MaybeUninit], value: T) -> &'a mut [T] where T: Clone, { - SpecFill::spec_fill(this, value); - // SAFETY: Valid elements have just been filled into `this` so it is initialized - unsafe { this.assume_init_mut() } + this.write_filled(value) } - /// Fills a slice with elements returned by calling a closure repeatedly. - /// - /// This method uses a closure to create new values. If you'd rather `Clone` a given value, use - /// [`MaybeUninit::fill`]. If you want to use the `Default` trait to generate values, you can - /// pass [`Default::default`] as the argument. - /// - /// # Panics - /// - /// This function will panic if any call to the provided closure panics. - /// - /// If such a panic occurs, any elements previously initialized during this operation will be - /// dropped. - /// - /// # Examples - /// - /// ``` - /// #![feature(maybe_uninit_fill)] - /// use std::mem::MaybeUninit; - /// - /// let mut buf = [const { MaybeUninit::::uninit() }; 10]; - /// let initialized = MaybeUninit::fill_with(&mut buf, Default::default); - /// assert_eq!(initialized, &mut [0; 10]); - /// ``` + /// Deprecated version of [`slice::write_with`]. #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill_with(this: &mut [MaybeUninit], mut f: F) -> &mut [T] + #[deprecated( + note = "replaced by inherent write_with method; will eventually be removed", + since = "1.83.0" + )] + pub fn fill_with<'a, F>(this: &'a mut [MaybeUninit], mut f: F) -> &'a mut [T] where F: FnMut() -> T, { - let mut guard = Guard { slice: this, initialized: 0 }; - - for element in guard.slice.iter_mut() { - element.write(f()); - guard.initialized += 1; - } - - super::forget(guard); - - // SAFETY: Valid elements have just been written into `this` so it is initialized - unsafe { this.assume_init_mut() } + this.write_with(|_| f()) } - /// Fills a slice with elements yielded by an iterator until either all elements have been - /// initialized or the iterator is empty. - /// - /// Returns two slices. The first slice contains the initialized portion of the original slice. - /// The second slice is the still-uninitialized remainder of the original slice. - /// - /// # Panics - /// - /// This function panics if the iterator's `next` function panics. - /// - /// If such a panic occurs, any elements previously initialized during this operation will be - /// dropped. - /// - /// # Examples - /// - /// Completely filling the slice: - /// - /// ``` - /// #![feature(maybe_uninit_fill)] - /// use std::mem::MaybeUninit; - /// - /// let mut buf = [const { MaybeUninit::uninit() }; 5]; - /// - /// let iter = [1, 2, 3].into_iter().cycle(); - /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); - /// - /// assert_eq!(initialized, &mut [1, 2, 3, 1, 2]); - /// assert_eq!(remainder.len(), 0); - /// ``` - /// - /// Partially filling the slice: - /// - /// ``` - /// #![feature(maybe_uninit_fill)] - /// use std::mem::MaybeUninit; - /// - /// let mut buf = [const { MaybeUninit::uninit() }; 5]; - /// let iter = [1, 2]; - /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); - /// - /// assert_eq!(initialized, &mut [1, 2]); - /// assert_eq!(remainder.len(), 3); - /// ``` - /// - /// Checking an iterator after filling a slice: - /// - /// ``` - /// #![feature(maybe_uninit_fill)] - /// use std::mem::MaybeUninit; - /// - /// let mut buf = [const { MaybeUninit::uninit() }; 3]; - /// let mut iter = [1, 2, 3, 4, 5].into_iter(); - /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter.by_ref()); - /// - /// assert_eq!(initialized, &mut [1, 2, 3]); - /// assert_eq!(remainder.len(), 0); - /// assert_eq!(iter.as_slice(), &[4, 5]); - /// ``` + /// Deprecated version of [`slice::write_iter`]. #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill_from(this: &mut [MaybeUninit], it: I) -> (&mut [T], &mut [MaybeUninit]) + #[deprecated( + note = "replaced by inherent write_iter method; will eventually be removed", + since = "1.83.0" + )] + pub fn fill_from<'a, I>( + this: &'a mut [MaybeUninit], + it: I, + ) -> (&'a mut [T], &'a mut [MaybeUninit]) where I: IntoIterator, { - let iter = it.into_iter(); - let mut guard = Guard { slice: this, initialized: 0 }; - - for (element, val) in guard.slice.iter_mut().zip(iter) { - element.write(val); - guard.initialized += 1; - } - - let initialized_len = guard.initialized; - super::forget(guard); - - // SAFETY: guard.initialized <= this.len() - let (initted, remainder) = unsafe { this.split_at_mut_unchecked(initialized_len) }; - - // SAFETY: Valid elements have just been written into `init`, so that portion - // of `this` is initialized. - (unsafe { initted.assume_init_mut() }, remainder) + this.write_iter(it) } /// Deprecated version of [`slice::as_bytes`]. @@ -1380,6 +1265,163 @@ impl [MaybeUninit] { unsafe { self.assume_init_mut() } } + /// Fills a slice with elements by cloning `value`, returning a mutable reference to the now + /// initialized contents of the slice. + /// Any previously initialized elements will not be dropped. + /// + /// This is similar to [`slice::fill`]. + /// + /// # Panics + /// + /// This function will panic if any call to `Clone` panics. + /// + /// If such a panic occurs, any elements previously initialized during this operation will be + /// dropped. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = [const { MaybeUninit::uninit() }; 10]; + /// let initialized = buf.write_filled(1); + /// assert_eq!(initialized, &mut [1; 10]); + /// ``` + #[doc(alias = "memset")] + #[unstable(feature = "maybe_uninit_fill", issue = "117428")] + pub fn write_filled(&mut self, value: T) -> &mut [T] + where + T: Clone, + { + SpecFill::spec_fill(self, value); + // SAFETY: Valid elements have just been filled into `self` so it is initialized + unsafe { self.assume_init_mut() } + } + + /// Fills a slice with elements returned by calling a closure for each index. + /// + /// This method uses a closure to create new values. If you'd rather `Clone` a given value, use + /// [`MaybeUninit::fill`]. If you want to use the `Default` trait to generate values, you can + /// pass [`|_| Default::default()`][Default::default] as the argument. + /// + /// # Panics + /// + /// This function will panic if any call to the provided closure panics. + /// + /// If such a panic occurs, any elements previously initialized during this operation will be + /// dropped. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = [const { MaybeUninit::::uninit() }; 5]; + /// let initialized = buf.write_with(|idx| idx + 1); + /// assert_eq!(initialized, &mut [1, 2, 3, 4, 5]); + /// ``` + #[unstable(feature = "maybe_uninit_fill", issue = "117428")] + pub fn write_with(&mut self, mut f: F) -> &mut [T] + where + F: FnMut(usize) -> T, + { + let mut guard = Guard { slice: self, initialized: 0 }; + + for (idx, element) in guard.slice.iter_mut().enumerate() { + element.write(f(idx)); + guard.initialized += 1; + } + + super::forget(guard); + + // SAFETY: Valid elements have just been written into `this` so it is initialized + unsafe { self.assume_init_mut() } + } + + /// Fills a slice with elements yielded by an iterator until either all elements have been + /// initialized or the iterator is empty. + /// + /// Returns two slices. The first slice contains the initialized portion of the original slice. + /// The second slice is the still-uninitialized remainder of the original slice. + /// + /// # Panics + /// + /// This function panics if the iterator's `next` function panics. + /// + /// If such a panic occurs, any elements previously initialized during this operation will be + /// dropped. + /// + /// # Examples + /// + /// Completely filling the slice: + /// + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = [const { MaybeUninit::uninit() }; 5]; + /// + /// let iter = [1, 2, 3].into_iter().cycle(); + /// let (initialized, remainder) = buf.write_iter(iter); + /// + /// assert_eq!(initialized, &mut [1, 2, 3, 1, 2]); + /// assert_eq!(remainder.len(), 0); + /// ``` + /// + /// Partially filling the slice: + /// + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = [const { MaybeUninit::uninit() }; 5]; + /// let iter = [1, 2]; + /// let (initialized, remainder) = buf.write_iter(iter); + /// + /// assert_eq!(initialized, &mut [1, 2]); + /// assert_eq!(remainder.len(), 3); + /// ``` + /// + /// Checking an iterator after filling a slice: + /// + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = [const { MaybeUninit::uninit() }; 3]; + /// let mut iter = [1, 2, 3, 4, 5].into_iter(); + /// let (initialized, remainder) = buf.write_iter(iter.by_ref()); + /// + /// assert_eq!(initialized, &mut [1, 2, 3]); + /// assert_eq!(remainder.len(), 0); + /// assert_eq!(iter.as_slice(), &[4, 5]); + /// ``` + #[unstable(feature = "maybe_uninit_fill", issue = "117428")] + pub fn write_iter(&mut self, it: I) -> (&mut [T], &mut [MaybeUninit]) + where + I: IntoIterator, + { + let iter = it.into_iter(); + let mut guard = Guard { slice: self, initialized: 0 }; + + for (element, val) in guard.slice.iter_mut().zip(iter) { + element.write(val); + guard.initialized += 1; + } + + let initialized_len = guard.initialized; + super::forget(guard); + + // SAFETY: guard.initialized <= self.len() + let (initted, remainder) = unsafe { self.split_at_mut_unchecked(initialized_len) }; + + // SAFETY: Valid elements have just been written into `init`, so that portion + // of `this` is initialized. + (unsafe { initted.assume_init_mut() }, remainder) + } + /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. /// /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index b9bb6d6a13f7f..0a5f3ee35b105 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -21,6 +21,8 @@ mod transmutability; #[unstable(feature = "transmutability", issue = "99571")] pub use transmutability::{Assume, TransmuteFrom}; +// This one has to be a re-export (rather than wrapping the underlying intrinsic) so that we can do +// the special magic "types have equal size" check at the call site. #[stable(feature = "rust1", since = "1.0.0")] #[doc(inline)] pub use crate::intrinsics::transmute; @@ -140,7 +142,7 @@ pub use crate::intrinsics::transmute; #[inline] #[rustc_const_stable(feature = "const_forget", since = "1.46.0")] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "mem_forget")] +#[rustc_diagnostic_item = "mem_forget"] pub const fn forget(t: T) { let _ = ManuallyDrop::new(t); } @@ -226,31 +228,27 @@ pub fn forget_unsized(t: T) { /// # Examples /// /// ``` -/// use std::mem; -/// /// // Some primitives -/// assert_eq!(4, mem::size_of::()); -/// assert_eq!(8, mem::size_of::()); -/// assert_eq!(0, mem::size_of::<()>()); +/// assert_eq!(4, size_of::()); +/// assert_eq!(8, size_of::()); +/// assert_eq!(0, 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]>()); +/// assert_eq!(8, size_of::<[i32; 2]>()); +/// assert_eq!(12, size_of::<[i32; 3]>()); +/// assert_eq!(0, 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::>>()); +/// assert_eq!(size_of::<&i32>(), size_of::<*const i32>()); +/// assert_eq!(size_of::<&i32>(), size_of::>()); +/// assert_eq!(size_of::<&i32>(), size_of::>()); +/// assert_eq!(size_of::>(), size_of::>>()); /// ``` /// /// Using `#[repr(C)]`. /// /// ``` -/// use std::mem; -/// /// #[repr(C)] /// struct FieldStruct { /// first: u8, @@ -265,13 +263,13 @@ pub fn forget_unsized(t: T) { /// // 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::()); +/// assert_eq!(6, size_of::()); /// /// #[repr(C)] /// struct TupleStruct(u8, u16, u8); /// /// // Tuple structs follow the same rules. -/// assert_eq!(6, mem::size_of::()); +/// assert_eq!(6, size_of::()); /// /// // Note that reordering the fields can lower the size. We can remove both padding bytes /// // by putting `third` before `second`. @@ -282,7 +280,7 @@ pub fn forget_unsized(t: T) { /// second: u16 /// } /// -/// assert_eq!(4, mem::size_of::()); +/// assert_eq!(4, size_of::()); /// /// // Union size is the size of the largest field. /// #[repr(C)] @@ -291,7 +289,7 @@ pub fn forget_unsized(t: T) { /// larger: u16 /// } /// -/// assert_eq!(2, mem::size_of::()); +/// assert_eq!(2, size_of::()); /// ``` /// /// [alignment]: align_of @@ -304,7 +302,7 @@ pub fn forget_unsized(t: T) { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] #[rustc_const_stable(feature = "const_mem_size_of", since = "1.24.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "mem_size_of")] +#[rustc_diagnostic_item = "mem_size_of"] pub const fn size_of() -> usize { intrinsics::size_of::() } @@ -320,13 +318,11 @@ pub const fn size_of() -> usize { /// # Examples /// /// ``` -/// use std::mem; -/// -/// assert_eq!(4, mem::size_of_val(&5i32)); +/// assert_eq!(4, size_of_val(&5i32)); /// /// let x: [u8; 13] = [0; 13]; /// let y: &[u8] = &x; -/// assert_eq!(13, mem::size_of_val(y)); +/// assert_eq!(13, size_of_val(y)); /// ``` /// /// [`size_of::()`]: size_of @@ -334,7 +330,7 @@ pub const fn size_of() -> usize { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_size_of_val", since = "1.85.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "mem_size_of_val")] +#[rustc_diagnostic_item = "mem_size_of_val"] pub const fn size_of_val(val: &T) -> usize { // SAFETY: `val` is a reference, so it's a valid raw pointer unsafe { intrinsics::size_of_val(val) } @@ -381,7 +377,7 @@ pub const fn size_of_val(val: &T) -> usize { /// #![feature(layout_for_ptr)] /// use std::mem; /// -/// assert_eq!(4, mem::size_of_val(&5i32)); +/// assert_eq!(4, size_of_val(&5i32)); /// /// let x: [u8; 13] = [0; 13]; /// let y: &[u8] = &x; @@ -454,9 +450,7 @@ pub fn min_align_of_val(val: &T) -> usize { /// # Examples /// /// ``` -/// use std::mem; -/// -/// assert_eq!(4, mem::align_of::()); +/// assert_eq!(4, align_of::()); /// ``` #[inline(always)] #[must_use] @@ -477,9 +471,7 @@ pub const fn align_of() -> usize { /// # Examples /// /// ``` -/// use std::mem; -/// -/// assert_eq!(4, mem::align_of_val(&5i32)); +/// assert_eq!(4, align_of_val(&5i32)); /// ``` #[inline] #[must_use] @@ -856,7 +848,7 @@ pub fn take(dest: &mut T) -> T { #[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"] #[rustc_const_stable(feature = "const_replace", since = "1.83.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "mem_replace")] +#[rustc_diagnostic_item = "mem_replace"] pub const fn replace(dest: &mut T, src: T) -> T { // It may be tempting to use `swap` to avoid `unsafe` here. Don't! // The compiler optimizes the implementation below to two `memcpy`s @@ -866,8 +858,13 @@ pub const fn replace(dest: &mut T, src: T) -> T { // such that the old value is not duplicated. Nothing is dropped and // nothing here can panic. unsafe { - let result = ptr::read(dest); - ptr::write(dest, src); + // Ideally we wouldn't use the intrinsics here, but going through the + // `ptr` methods introduces two unnecessary UbChecks, so until we can + // remove those for pointers that come from references, this uses the + // intrinsics instead so this stays very cheap in MIR (and debug). + + let result = crate::intrinsics::read_via_copy(dest); + crate::intrinsics::write_via_move(dest, src); result } } @@ -936,7 +933,7 @@ pub const fn replace(dest: &mut T, src: T) -> T { /// [`RefCell`]: crate::cell::RefCell #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "mem_drop")] +#[rustc_diagnostic_item = "mem_drop"] pub fn drop(_x: T) {} /// Bitwise-copies a value. @@ -1159,7 +1156,7 @@ impl fmt::Debug for Discriminant { /// ``` #[stable(feature = "discriminant_value", since = "1.21.0")] #[rustc_const_stable(feature = "const_discriminant", since = "1.75.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "mem_discriminant")] +#[rustc_diagnostic_item = "mem_discriminant"] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const fn discriminant(v: &T) -> Discriminant { Discriminant(intrinsics::discriminant_value(v)) @@ -1259,42 +1256,56 @@ impl SizedTypeProperties for T {} /// Expands to the offset in bytes of a field from the beginning of the given type. /// -/// Structs, enums, unions and tuples are supported. -/// -/// Nested field accesses may be used, but not array indexes. +/// The type may be a `struct`, `enum`, `union`, or tuple. /// -/// If the nightly-only feature `offset_of_enum` is enabled, -/// variants may be traversed as if they were fields. -/// Variants themselves do not have an offset. +/// The field may be a nested field (`field1.field2`), but not an array index. +/// The field must be visible to the call site. /// -/// Visibility is respected - all types and fields must be visible to the call site: +/// The offset is returned as a [`usize`]. /// -/// ``` -/// mod nested { -/// #[repr(C)] -/// pub struct Struct { -/// private: u8, -/// } -/// } +/// # Offsets of, and in, dynamically sized types /// -/// // assert_eq!(mem::offset_of!(nested::Struct, private), 0); -/// // ^^^ error[E0616]: field `private` of struct `Struct` is private -/// ``` +/// The field’s type must be [`Sized`], but it may be located in a [dynamically sized] container. +/// If the field type is dynamically sized, then you cannot use `offset_of!` (since the field's +/// alignment, and therefore its offset, may also be dynamic) and must take the offset from an +/// actual pointer to the container instead. /// -/// Only [`Sized`] fields are supported, but the container may be unsized: /// ``` /// # use core::mem; +/// # use core::fmt::Debug; /// #[repr(C)] -/// pub struct Struct { +/// pub struct Struct { /// a: u8, -/// b: [u8], +/// b: T, /// } /// -/// assert_eq!(mem::offset_of!(Struct, a), 0); // OK -/// // assert_eq!(mem::offset_of!(Struct, b), 1); -/// // ^^^ error[E0277]: doesn't have a size known at compile-time +/// #[derive(Debug)] +/// #[repr(C, align(4))] +/// struct Align4(u32); +/// +/// assert_eq!(mem::offset_of!(Struct, a), 0); // OK — Sized field +/// assert_eq!(mem::offset_of!(Struct, b), 4); // OK — not DST +/// +/// // assert_eq!(mem::offset_of!(Struct, b), 1); +/// // ^^^ error[E0277]: ... cannot be known at compilation time +/// +/// // To obtain the offset of a !Sized field, examine a concrete value +/// // instead of using offset_of!. +/// let value: Struct = Struct { a: 1, b: Align4(2) }; +/// let ref_unsized: &Struct = &value; +/// let offset_of_b = unsafe { +/// (&raw const ref_unsized.b).byte_offset_from_unsigned(ref_unsized) +/// }; +/// assert_eq!(offset_of_b, 4); /// ``` /// +/// If you need to obtain the offset of a field of a `!Sized` type, then, since the offset may +/// depend on the particular value being stored (in particular, `dyn Trait` values have a +/// dynamically-determined alignment), you must retrieve the offset from a specific reference +/// or pointer, and so you cannot use `offset_of!` to work without one. +/// +/// # Layout is subject to change +/// /// Note that type layout is, in general, [subject to change and /// platform-specific](https://doc.rust-lang.org/reference/type-layout.html). If /// layout stability is required, consider using an [explicit `repr` attribute]. @@ -1330,11 +1341,16 @@ impl SizedTypeProperties for T {} /// /// [explicit `repr` attribute]: https://doc.rust-lang.org/reference/type-layout.html#representations /// +/// # Unstable features +/// +/// The following unstable features expand the functionality of `offset_of!`: +/// +/// * [`offset_of_enum`] — allows `enum` variants to be traversed as if they were fields. +/// * [`offset_of_slice`] — allows getting the offset of a field of type `[T]`. +/// /// # Examples /// /// ``` -/// #![feature(offset_of_enum)] -/// /// use std::mem; /// #[repr(C)] /// struct FieldStruct { @@ -1356,18 +1372,11 @@ impl SizedTypeProperties for T {} /// struct NestedB(u8); /// /// assert_eq!(mem::offset_of!(NestedA, b.0), 0); -/// -/// #[repr(u8)] -/// enum Enum { -/// A(u8, u16), -/// B { one: u8, two: u16 }, -/// } -/// -/// assert_eq!(mem::offset_of!(Enum, A.0), 1); -/// assert_eq!(mem::offset_of!(Enum, B.two), 2); -/// -/// assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0); /// ``` +/// +/// [dynamically sized]: https://doc.rust-lang.org/reference/dynamically-sized-types.html +/// [`offset_of_enum`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/offset-of-enum.html +/// [`offset_of_slice`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/offset-of-slice.html #[stable(feature = "offset_of", since = "1.77.0")] #[allow_internal_unstable(builtin_syntax)] pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index 7b920d7a777ca..782b826448a32 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -153,7 +153,7 @@ pub struct Assume { /// /// ```compile_fail,E0277 /// #![feature(transmutability)] - /// use core::mem::{align_of, TransmuteFrom}; + /// use core::mem::TransmuteFrom; /// /// assert_eq!(align_of::<[u8; 2]>(), 1); /// assert_eq!(align_of::(), 2); @@ -172,7 +172,7 @@ pub struct Assume { /// /// ```rust /// #![feature(pointer_is_aligned_to, transmutability)] - /// use core::mem::{align_of, Assume, TransmuteFrom}; + /// use core::mem::{Assume, TransmuteFrom}; /// /// let src: &[u8; 2] = &[0xFF, 0xFF]; /// @@ -337,7 +337,7 @@ impl Assume { /// transmutability, /// )] /// #![allow(incomplete_features)] - /// use core::mem::{align_of, Assume, TransmuteFrom}; + /// use core::mem::{Assume, TransmuteFrom}; /// /// /// Attempts to transmute `src` to `&Dst`. /// /// diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index 8e4417ec461b8..aaa68e8d7d1ad 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -25,7 +25,7 @@ use crate::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not}; /// assert_eq!(localhost_v4.is_ipv6(), false); /// assert_eq!(localhost_v4.is_ipv4(), true); /// ``` -#[cfg_attr(not(test), rustc_diagnostic_item = "IpAddr")] +#[rustc_diagnostic_item = "IpAddr"] #[stable(feature = "ip_addr", since = "1.7.0")] #[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] pub enum IpAddr { @@ -68,6 +68,7 @@ pub enum IpAddr { /// assert!("0000000.0.0.0".parse::().is_err()); // first octet is a zero in octal /// assert!("0xcb.0x0.0x71.0x00".parse::().is_err()); // all octets are in hex /// ``` +#[rustc_diagnostic_item = "Ipv4Addr"] #[derive(Copy, Clone, PartialEq, Eq)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Ipv4Addr { @@ -160,6 +161,7 @@ impl Hash for Ipv4Addr { /// assert_eq!("::1".parse(), Ok(localhost)); /// assert_eq!(localhost.is_loopback(), true); /// ``` +#[rustc_diagnostic_item = "Ipv6Addr"] #[derive(Copy, Clone, PartialEq, Eq)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Ipv6Addr { @@ -1623,6 +1625,8 @@ impl Ipv6Addr { // IANA says N/A. || matches!(self.segments(), [0x2002, _, _, _, _, _, _, _]) || self.is_documentation() + // Segment Routing (SRv6) SIDs (`5f00::/16`) + || matches!(self.segments(), [0x5f00, ..]) || self.is_unique_local() || self.is_unicast_link_local()) } diff --git a/library/core/src/net/socket_addr.rs b/library/core/src/net/socket_addr.rs index 9204797e6e157..936f9f64930d5 100644 --- a/library/core/src/net/socket_addr.rs +++ b/library/core/src/net/socket_addr.rs @@ -8,11 +8,15 @@ use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// 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 /// +/// # Portability +/// +/// `SocketAddr` is intended to be a portable representation of socket addresses and is likely not +/// the same as the internal socket address type used by the target operating system's API. Like all +/// `repr(Rust)` structs, however, its exact layout remains undefined and should not be relied upon +/// between builds. +/// /// # Examples /// /// ``` @@ -42,13 +46,16 @@ pub enum SocketAddr { /// /// 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. Do not assume that this type has the same memory layout as the underlying -/// system representation. -/// /// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 /// [`IPv4` address]: Ipv4Addr /// +/// # Portability +/// +/// `SocketAddrV4` is intended to be a portable representation of socket addresses and is likely not +/// the same as the internal socket address type used by the target operating system's API. Like all +/// `repr(Rust)` structs, however, its exact layout remains undefined and should not be relied upon +/// between builds. +/// /// # Textual representation /// /// `SocketAddrV4` provides a [`FromStr`](crate::str::FromStr) implementation. @@ -84,13 +91,16 @@ pub struct SocketAddrV4 { /// /// 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. Do not assume that this type has the same memory layout as the underlying -/// system representation. -/// /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 /// [`IPv6` address]: Ipv6Addr /// +/// # Portability +/// +/// `SocketAddrV6` is intended to be a portable representation of socket addresses and is likely not +/// the same as the internal socket address type used by the target operating system's API. Like all +/// `repr(Rust)` structs, however, its exact layout remains undefined and should not be relied upon +/// between builds. +/// /// # Textual representation /// /// `SocketAddrV6` provides a [`FromStr`](crate::str::FromStr) implementation, @@ -200,7 +210,7 @@ impl SocketAddr { /// ``` #[inline] #[stable(feature = "sockaddr_setters", since = "1.9.0")] - #[rustc_const_unstable(feature = "const_sockaddr_setters", issue = "131714")] + #[rustc_const_stable(feature = "const_sockaddr_setters", since = "1.87.0")] pub const 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) { @@ -244,7 +254,7 @@ impl SocketAddr { /// ``` #[inline] #[stable(feature = "sockaddr_setters", since = "1.9.0")] - #[rustc_const_unstable(feature = "const_sockaddr_setters", issue = "131714")] + #[rustc_const_stable(feature = "const_sockaddr_setters", since = "1.87.0")] pub const fn set_port(&mut self, new_port: u16) { match *self { SocketAddr::V4(ref mut a) => a.set_port(new_port), @@ -350,7 +360,7 @@ impl SocketAddrV4 { /// ``` #[inline] #[stable(feature = "sockaddr_setters", since = "1.9.0")] - #[rustc_const_unstable(feature = "const_sockaddr_setters", issue = "131714")] + #[rustc_const_stable(feature = "const_sockaddr_setters", since = "1.87.0")] pub const fn set_ip(&mut self, new_ip: Ipv4Addr) { self.ip = new_ip; } @@ -386,7 +396,7 @@ impl SocketAddrV4 { /// ``` #[inline] #[stable(feature = "sockaddr_setters", since = "1.9.0")] - #[rustc_const_unstable(feature = "const_sockaddr_setters", issue = "131714")] + #[rustc_const_stable(feature = "const_sockaddr_setters", since = "1.87.0")] pub const fn set_port(&mut self, new_port: u16) { self.port = new_port; } @@ -448,7 +458,7 @@ impl SocketAddrV6 { /// ``` #[inline] #[stable(feature = "sockaddr_setters", since = "1.9.0")] - #[rustc_const_unstable(feature = "const_sockaddr_setters", issue = "131714")] + #[rustc_const_stable(feature = "const_sockaddr_setters", since = "1.87.0")] pub const fn set_ip(&mut self, new_ip: Ipv6Addr) { self.ip = new_ip; } @@ -484,7 +494,7 @@ impl SocketAddrV6 { /// ``` #[inline] #[stable(feature = "sockaddr_setters", since = "1.9.0")] - #[rustc_const_unstable(feature = "const_sockaddr_setters", issue = "131714")] + #[rustc_const_stable(feature = "const_sockaddr_setters", since = "1.87.0")] pub const fn set_port(&mut self, new_port: u16) { self.port = new_port; } @@ -532,7 +542,7 @@ impl SocketAddrV6 { /// ``` #[inline] #[stable(feature = "sockaddr_setters", since = "1.9.0")] - #[rustc_const_unstable(feature = "const_sockaddr_setters", issue = "131714")] + #[rustc_const_stable(feature = "const_sockaddr_setters", since = "1.87.0")] pub const fn set_flowinfo(&mut self, new_flowinfo: u32) { self.flowinfo = new_flowinfo; } @@ -575,7 +585,7 @@ impl SocketAddrV6 { /// ``` #[inline] #[stable(feature = "sockaddr_setters", since = "1.9.0")] - #[rustc_const_unstable(feature = "const_sockaddr_setters", issue = "131714")] + #[rustc_const_stable(feature = "const_sockaddr_setters", since = "1.87.0")] pub const fn set_scope_id(&mut self, new_scope_id: u32) { self.scope_id = new_scope_id; } diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/bignum.rs index 2a47c89e2aee2..e33f58197bba5 100644 --- a/library/core/src/num/bignum.rs +++ b/library/core/src/num/bignum.rs @@ -253,12 +253,11 @@ macro_rules! define_bignum { /// Multiplies itself by `5^e` and returns its own mutable reference. pub fn mul_pow5(&mut self, mut e: usize) -> &mut $name { - use crate::mem; use crate::num::bignum::SMALL_POW5; // There are exactly n trailing zeros on 2^n, and the only relevant digit sizes // are consecutive powers of two, so this is well suited index for the table. - let table_index = mem::size_of::<$ty>().trailing_zeros() as usize; + let table_index = size_of::<$ty>().trailing_zeros() as usize; let (small_power, small_e) = SMALL_POW5[table_index]; let small_power = small_power as $ty; @@ -405,6 +404,8 @@ macro_rules! define_bignum { } } + impl crate::clone::UseCloned for $name {} + impl crate::fmt::Debug for $name { fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { let sz = if self.size < 1 { 1 } else { self.size }; diff --git a/library/core/src/num/dec2flt/common.rs b/library/core/src/num/dec2flt/common.rs index 4dadf406ae8c7..a140a311c452f 100644 --- a/library/core/src/num/dec2flt/common.rs +++ b/library/core/src/num/dec2flt/common.rs @@ -8,12 +8,12 @@ pub(crate) trait ByteSlice { /// Writes a 64-bit integer as 8 bytes in little-endian order. fn write_u64(&mut self, value: u64); - /// Calculate the offset of a slice from another. + /// Calculate the difference in length between two slices. fn offset_from(&self, other: &Self) -> isize; /// Iteratively parse and consume digits from bytes. - /// Returns the same bytes with consumed digits being - /// elided. + /// + /// Returns the same bytes with consumed digits being elided. Breaks on invalid digits. fn parse_digits(&self, func: impl FnMut(u8)) -> &Self; } @@ -39,11 +39,11 @@ impl ByteSlice for [u8] { fn parse_digits(&self, mut func: impl FnMut(u8)) -> &Self { let mut s = self; - while let Some((c, s_next)) = s.split_first() { + while let Some((c, rest)) = s.split_first() { let c = c.wrapping_sub(b'0'); if c < 10 { func(c); - s = s_next; + s = rest; } else { break; } @@ -53,7 +53,9 @@ impl ByteSlice for [u8] { } } -/// Determine if 8 bytes are all decimal digits. +/// Determine if all characters in an 8-byte byte string (represented as a `u64`) are all decimal +/// digits. +/// /// This does not care about the order in which the bytes were loaded. pub(crate) fn is_8digits(v: u64) -> bool { let a = v.wrapping_add(0x4646_4646_4646_4646); @@ -61,19 +63,20 @@ pub(crate) fn is_8digits(v: u64) -> bool { (a | b) & 0x8080_8080_8080_8080 == 0 } -/// A custom 64-bit floating point type, representing `f * 2^e`. -/// e is biased, so it be directly shifted into the exponent bits. +/// A custom 64-bit floating point type, representing `m * 2^p`. +/// p is biased, so it be directly shifted into the exponent bits. #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub struct BiasedFp { /// The significant digits. - pub f: u64, + pub m: u64, /// The biased, binary exponent. - pub e: i32, + pub p_biased: i32, } impl BiasedFp { + /// Represent `0 ^ p` #[inline] - pub const fn zero_pow2(e: i32) -> Self { - Self { f: 0, e } + pub const fn zero_pow2(p_biased: i32) -> Self { + Self { m: 0, p_biased } } } diff --git a/library/core/src/num/dec2flt/decimal.rs b/library/core/src/num/dec2flt/decimal.rs index b37724ba62d5e..db7176c124318 100644 --- a/library/core/src/num/dec2flt/decimal.rs +++ b/library/core/src/num/dec2flt/decimal.rs @@ -1,358 +1,87 @@ -//! Arbitrary-precision decimal class for fallback algorithms. -//! -//! This is only used if the fast-path (native floats) and -//! the Eisel-Lemire algorithm are unable to unambiguously -//! determine the float. -//! -//! The technique used is "Simple Decimal Conversion", developed -//! by Nigel Tao and Ken Thompson. A detailed description of the -//! algorithm can be found in "ParseNumberF64 by Simple Decimal Conversion", -//! available online: . - -use crate::num::dec2flt::common::{ByteSlice, is_8digits}; - -#[derive(Clone)] -pub(super) struct Decimal { - /// The number of significant digits in the decimal. - pub num_digits: usize, - /// The offset of the decimal point in the significant digits. - pub decimal_point: i32, - /// If the number of significant digits stored in the decimal is truncated. - pub truncated: bool, - /// Buffer of the raw digits, in the range [0, 9]. - pub digits: [u8; Self::MAX_DIGITS], +//! Representation of a float as the significant digits and exponent. + +use crate::num::dec2flt::float::RawFloat; +use crate::num::dec2flt::fpu::set_precision; + +const INT_POW10: [u64; 16] = [ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, +]; + +/// A floating point number with up to 64 bits of mantissa and an `i64` exponent. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct Decimal { + pub exponent: i64, + pub mantissa: u64, + pub negative: bool, + pub many_digits: bool, } -impl Default for Decimal { - fn default() -> Self { - Self { num_digits: 0, decimal_point: 0, truncated: false, digits: [0; Self::MAX_DIGITS] } +impl Decimal { + /// Detect if the float can be accurately reconstructed from native floats. + #[inline] + fn can_use_fast_path(&self) -> bool { + F::MIN_EXPONENT_FAST_PATH <= self.exponent + && self.exponent <= F::MAX_EXPONENT_DISGUISED_FAST_PATH + && self.mantissa <= F::MAX_MANTISSA_FAST_PATH + && !self.many_digits } -} -impl Decimal { - /// The maximum number of digits required to unambiguously round a float. - /// - /// For a double-precision IEEE 754 float, this required 767 digits, - /// so we store the max digits + 1. - /// - /// We can exactly represent a float in radix `b` from radix 2 if - /// `b` is divisible by 2. This function calculates the exact number of - /// digits required to exactly represent that float. - /// - /// According to the "Handbook of Floating Point Arithmetic", - /// for IEEE754, with emin being the min exponent, p2 being the - /// precision, and b being the radix, the number of digits follows as: + /// Try turning the decimal into an exact float representation, using machine-sized integers + /// and floats. /// - /// `−emin + p2 + ⌊(emin + 1) log(2, b) − log(1 − 2^(−p2), b)⌋` + /// This is extracted into a separate function so that it can be attempted before constructing + /// a Decimal. This only works if both the mantissa and the exponent + /// can be exactly represented as a machine float, since IEE-754 guarantees + /// no rounding will occur. /// - /// For f32, this follows as: - /// emin = -126 - /// p2 = 24 - /// - /// For f64, this follows as: - /// emin = -1022 - /// p2 = 53 - /// - /// In Python: - /// `-emin + p2 + math.floor((emin+ 1)*math.log(2, b)-math.log(1-2**(-p2), b))` - pub(super) const MAX_DIGITS: usize = 768; - /// The max digits that can be exactly represented in a 64-bit integer. - pub(super) const MAX_DIGITS_WITHOUT_OVERFLOW: usize = 19; - pub(super) const DECIMAL_POINT_RANGE: i32 = 2047; - - /// Append a digit to the buffer. - pub(super) fn try_add_digit(&mut self, digit: u8) { - if self.num_digits < Self::MAX_DIGITS { - self.digits[self.num_digits] = digit; - } - self.num_digits += 1; - } - - /// Trim trailing zeros from the buffer. - pub(super) fn trim(&mut self) { - // All of the following calls to `Decimal::trim` can't panic because: - // - // 1. `parse_decimal` sets `num_digits` to a max of `Decimal::MAX_DIGITS`. - // 2. `right_shift` sets `num_digits` to `write_index`, which is bounded by `num_digits`. - // 3. `left_shift` `num_digits` to a max of `Decimal::MAX_DIGITS`. - // - // Trim is only called in `right_shift` and `left_shift`. - debug_assert!(self.num_digits <= Self::MAX_DIGITS); - while self.num_digits != 0 && self.digits[self.num_digits - 1] == 0 { - self.num_digits -= 1; - } - } - - pub(super) fn round(&self) -> u64 { - if self.num_digits == 0 || self.decimal_point < 0 { - return 0; - } else if self.decimal_point > 18 { - return 0xFFFF_FFFF_FFFF_FFFF_u64; - } - let dp = self.decimal_point as usize; - let mut n = 0_u64; - for i in 0..dp { - n *= 10; - if i < self.num_digits { - n += self.digits[i] as u64; - } - } - let mut round_up = false; - if dp < self.num_digits { - round_up = self.digits[dp] >= 5; - if self.digits[dp] == 5 && dp + 1 == self.num_digits { - round_up = self.truncated || ((dp != 0) && (1 & self.digits[dp - 1] != 0)) - } - } - if round_up { - n += 1; - } - n - } - - /// Computes decimal * 2^shift. - pub(super) fn left_shift(&mut self, shift: usize) { - if self.num_digits == 0 { - return; - } - let num_new_digits = number_of_digits_decimal_left_shift(self, shift); - let mut read_index = self.num_digits; - let mut write_index = self.num_digits + num_new_digits; - let mut n = 0_u64; - while read_index != 0 { - read_index -= 1; - write_index -= 1; - n += (self.digits[read_index] as u64) << shift; - let quotient = n / 10; - let remainder = n - (10 * quotient); - if write_index < Self::MAX_DIGITS { - self.digits[write_index] = remainder as u8; - } else if remainder > 0 { - self.truncated = true; - } - n = quotient; - } - while n > 0 { - write_index -= 1; - let quotient = n / 10; - let remainder = n - (10 * quotient); - if write_index < Self::MAX_DIGITS { - self.digits[write_index] = remainder as u8; - } else if remainder > 0 { - self.truncated = true; - } - n = quotient; - } - self.num_digits += num_new_digits; - if self.num_digits > Self::MAX_DIGITS { - self.num_digits = Self::MAX_DIGITS; - } - self.decimal_point += num_new_digits as i32; - self.trim(); - } - - /// Computes decimal * 2^-shift. - pub(super) fn right_shift(&mut self, shift: usize) { - let mut read_index = 0; - let mut write_index = 0; - let mut n = 0_u64; - while (n >> shift) == 0 { - if read_index < self.num_digits { - n = (10 * n) + self.digits[read_index] as u64; - read_index += 1; - } else if n == 0 { - return; + /// There is an exception: disguised fast-path cases, where we can shift + /// powers-of-10 from the exponent to the significant digits. + pub fn try_fast_path(&self) -> Option { + // Here we need to work around . + // The fast path crucially depends on arithmetic being rounded to the correct number of bits + // without any intermediate rounding. On x86 (without SSE or SSE2) this requires the precision + // of the x87 FPU stack to be changed so that it directly rounds to 64/32 bit. + // The `set_precision` function takes care of setting the precision on architectures which + // require setting it by changing the global state (like the control word of the x87 FPU). + let _cw = set_precision::(); + + if !self.can_use_fast_path::() { + return None; + } + + let value = if self.exponent <= F::MAX_EXPONENT_FAST_PATH { + // normal fast path + let value = F::from_u64(self.mantissa); + if self.exponent < 0 { + value / F::pow10_fast_path((-self.exponent) as _) } else { - while (n >> shift) == 0 { - n *= 10; - read_index += 1; - } - break; - } - } - self.decimal_point -= read_index as i32 - 1; - if self.decimal_point < -Self::DECIMAL_POINT_RANGE { - // `self = Self::Default()`, but without the overhead of clearing `digits`. - self.num_digits = 0; - self.decimal_point = 0; - self.truncated = false; - return; - } - let mask = (1_u64 << shift) - 1; - while read_index < self.num_digits { - let new_digit = (n >> shift) as u8; - n = (10 * (n & mask)) + self.digits[read_index] as u64; - read_index += 1; - self.digits[write_index] = new_digit; - write_index += 1; - } - while n > 0 { - let new_digit = (n >> shift) as u8; - n = 10 * (n & mask); - if write_index < Self::MAX_DIGITS { - self.digits[write_index] = new_digit; - write_index += 1; - } else if new_digit > 0 { - self.truncated = true; - } - } - self.num_digits = write_index; - self.trim(); - } -} - -/// Parse a big integer representation of the float as a decimal. -pub(super) fn parse_decimal(mut s: &[u8]) -> Decimal { - let mut d = Decimal::default(); - let start = s; - - while let Some((&b'0', s_next)) = s.split_first() { - s = s_next; - } - - s = s.parse_digits(|digit| d.try_add_digit(digit)); - - if let Some((b'.', s_next)) = s.split_first() { - s = s_next; - let first = s; - // Skip leading zeros. - if d.num_digits == 0 { - while let Some((&b'0', s_next)) = s.split_first() { - s = s_next; - } - } - while s.len() >= 8 && d.num_digits + 8 < Decimal::MAX_DIGITS { - let v = s.read_u64(); - if !is_8digits(v) { - break; + value * F::pow10_fast_path(self.exponent as _) } - d.digits[d.num_digits..].write_u64(v - 0x3030_3030_3030_3030); - d.num_digits += 8; - s = &s[8..]; - } - s = s.parse_digits(|digit| d.try_add_digit(digit)); - d.decimal_point = s.len() as i32 - first.len() as i32; - } - if d.num_digits != 0 { - // Ignore the trailing zeros if there are any - let mut n_trailing_zeros = 0; - for &c in start[..(start.len() - s.len())].iter().rev() { - if c == b'0' { - n_trailing_zeros += 1; - } else if c != b'.' { - break; - } - } - d.decimal_point += n_trailing_zeros as i32; - d.num_digits -= n_trailing_zeros; - d.decimal_point += d.num_digits as i32; - if d.num_digits > Decimal::MAX_DIGITS { - d.truncated = true; - d.num_digits = Decimal::MAX_DIGITS; - } - } - if let Some((&ch, s_next)) = s.split_first() { - if ch == b'e' || ch == b'E' { - s = s_next; - let mut neg_exp = false; - if let Some((&ch, s_next)) = s.split_first() { - neg_exp = ch == b'-'; - if ch == b'-' || ch == b'+' { - s = s_next; - } + } else { + // disguised fast path + let shift = self.exponent - F::MAX_EXPONENT_FAST_PATH; + let mantissa = self.mantissa.checked_mul(INT_POW10[shift as usize])?; + if mantissa > F::MAX_MANTISSA_FAST_PATH { + return None; } - let mut exp_num = 0_i32; - - s.parse_digits(|digit| { - if exp_num < 0x10000 { - exp_num = 10 * exp_num + digit as i32; - } - }); + F::from_u64(mantissa) * F::pow10_fast_path(F::MAX_EXPONENT_FAST_PATH as _) + }; - d.decimal_point += if neg_exp { -exp_num } else { exp_num }; - } - } - for i in d.num_digits..Decimal::MAX_DIGITS_WITHOUT_OVERFLOW { - d.digits[i] = 0; - } - d -} - -fn number_of_digits_decimal_left_shift(d: &Decimal, mut shift: usize) -> usize { - #[rustfmt::skip] - const TABLE: [u16; 65] = [ - 0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817, 0x181D, 0x2024, - 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067, 0x3073, 0x3080, 0x388E, 0x389C, - 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF, 0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, - 0x5180, 0x5998, 0x59B0, 0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, - 0x72AA, 0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC, 0x8C02, - 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C, 0x051C, 0x051C, - ]; - #[rustfmt::skip] - const TABLE_POW5: [u8; 0x051C] = [ - 5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3, 9, 0, 6, 2, 5, 1, - 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8, 1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5, - 1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1, 0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, - 5, 1, 5, 2, 5, 8, 7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6, - 9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5, 3, 6, 7, 4, 3, 1, - 6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3, 1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9, - 1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0, 9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, 0, 4, 6, - 4, 4, 7, 7, 5, 3, 9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1, - 4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, 0, 5, 9, 6, 9, 2, - 3, 8, 2, 8, 1, 2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6, - 2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5, 7, 4, 6, 1, 5, - 4, 7, 8, 5, 1, 5, 6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2, - 5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5, 1, 1, 6, 4, 1, 5, - 3, 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5, 5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4, - 6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5, 2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6, - 1, 3, 2, 8, 1, 2, 5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0, - 6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2, 0, 3, 1, 2, 5, 3, - 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1, 6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, - 9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6, 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, - 7, 0, 1, 7, 7, 2, 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5, - 0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7, 3, 7, 3, 6, 7, 5, - 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5, 6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7, - 7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9, 3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, - 8, 6, 0, 8, 0, 8, 0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0, - 9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2, 5, 1, 4, 2, 1, 0, - 8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2, 4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1, - 0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, 0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, 5, 7, 8, 1, 2, - 5, 3, 5, 5, 2, 7, 1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8, - 9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, 6, 7, 7, 8, 1, 0, - 6, 6, 8, 9, 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, 9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3, - 8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, 6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8, 5, 0, 0, 6, 2, - 6, 1, 6, 1, 6, 9, 4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4, - 9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4, 0, 6, 2, 5, 1, 1, - 1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4, 2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8, - 2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1, 5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5, - 8, 3, 4, 0, 4, 5, 4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1, - 3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1, 3, 8, 7, 7, 7, 8, - 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, 9, 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, - 6, 2, 5, 6, 9, 3, 8, 8, 9, 3, 9, 0, 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, - 5, 5, 6, 7, 6, 2, 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1, - 8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5, 1, 7, 3, 4, 7, 2, - 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2, 4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3, - 8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1, 7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2, - 2, 4, 0, 6, 9, 5, 9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5, - ]; - - shift &= 63; - let x_a = TABLE[shift]; - let x_b = TABLE[shift + 1]; - let num_new_digits = (x_a >> 11) as _; - let pow5_a = (0x7FF & x_a) as usize; - let pow5_b = (0x7FF & x_b) as usize; - let pow5 = &TABLE_POW5[pow5_a..]; - for (i, &p5) in pow5.iter().enumerate().take(pow5_b - pow5_a) { - if i >= d.num_digits { - return num_new_digits - 1; - } else if d.digits[i] == p5 { - continue; - } else if d.digits[i] < p5 { - return num_new_digits - 1; - } else { - return num_new_digits; - } + if self.negative { Some(-value) } else { Some(value) } } - num_new_digits } diff --git a/library/core/src/num/dec2flt/decimal_seq.rs b/library/core/src/num/dec2flt/decimal_seq.rs new file mode 100644 index 0000000000000..de22280c001c9 --- /dev/null +++ b/library/core/src/num/dec2flt/decimal_seq.rs @@ -0,0 +1,379 @@ +//! Arbitrary-precision decimal type used by fallback algorithms. +//! +//! This is only used if the fast-path (native floats) and +//! the Eisel-Lemire algorithm are unable to unambiguously +//! determine the float. +//! +//! The technique used is "Simple Decimal Conversion", developed +//! by Nigel Tao and Ken Thompson. A detailed description of the +//! algorithm can be found in "ParseNumberF64 by Simple Decimal Conversion", +//! available online: . + +use crate::num::dec2flt::common::{ByteSlice, is_8digits}; + +/// A decimal floating-point number, represented as a sequence of decimal digits. +#[derive(Clone, Debug, PartialEq)] +pub struct DecimalSeq { + /// The number of significant digits in the decimal. + pub num_digits: usize, + /// The offset of the decimal point in the significant digits. + pub decimal_point: i32, + /// If the number of significant digits stored in the decimal is truncated. + pub truncated: bool, + /// Buffer of the raw digits, in the range [0, 9]. + pub digits: [u8; Self::MAX_DIGITS], +} + +impl Default for DecimalSeq { + fn default() -> Self { + Self { num_digits: 0, decimal_point: 0, truncated: false, digits: [0; Self::MAX_DIGITS] } + } +} + +impl DecimalSeq { + /// The maximum number of digits required to unambiguously round up to a 64-bit float. + /// + /// For an IEEE 754 binary64 float, this required 767 digits. So we store the max digits + 1. + /// + /// We can exactly represent a float in radix `b` from radix 2 if + /// `b` is divisible by 2. This function calculates the exact number of + /// digits required to exactly represent that float. + /// + /// According to the "Handbook of Floating Point Arithmetic", + /// for IEEE754, with `emin` being the min exponent, `p2` being the + /// precision, and `b` being the radix, the number of digits follows as: + /// + /// `−emin + p2 + ⌊(emin + 1) log(2, b) − log(1 − 2^(−p2), b)⌋` + /// + /// For f32, this follows as: + /// emin = -126 + /// p2 = 24 + /// + /// For f64, this follows as: + /// emin = -1022 + /// p2 = 53 + /// + /// In Python: + /// `-emin + p2 + math.floor((emin+ 1)*math.log(2, b)-math.log(1-2**(-p2), b))` + pub const MAX_DIGITS: usize = 768; + + /// The max decimal digits that can be exactly represented in a 64-bit integer. + pub(super) const MAX_DIGITS_WITHOUT_OVERFLOW: usize = 19; + pub(super) const DECIMAL_POINT_RANGE: i32 = 2047; + + /// Append a digit to the buffer if it fits. + // FIXME(tgross35): it may be better for this to return an option + // FIXME(tgross35): incrementing the digit counter even if we don't push anything + // seems incorrect. + pub(super) fn try_add_digit(&mut self, digit: u8) { + if self.num_digits < Self::MAX_DIGITS { + self.digits[self.num_digits] = digit; + } + self.num_digits += 1; + } + + /// Trim trailing zeros from the buffer. + // FIXME(tgross35): this could be `.rev().position()` if perf is okay + pub fn trim(&mut self) { + // All of the following calls to `DecimalSeq::trim` can't panic because: + // + // 1. `parse_decimal` sets `num_digits` to a max of `DecimalSeq::MAX_DIGITS`. + // 2. `right_shift` sets `num_digits` to `write_index`, which is bounded by `num_digits`. + // 3. `left_shift` `num_digits` to a max of `DecimalSeq::MAX_DIGITS`. + // + // Trim is only called in `right_shift` and `left_shift`. + debug_assert!(self.num_digits <= Self::MAX_DIGITS); + while self.num_digits != 0 && self.digits[self.num_digits - 1] == 0 { + self.num_digits -= 1; + } + } + + pub(super) fn round(&self) -> u64 { + if self.num_digits == 0 || self.decimal_point < 0 { + return 0; + } else if self.decimal_point >= Self::MAX_DIGITS_WITHOUT_OVERFLOW as i32 { + return 0xFFFF_FFFF_FFFF_FFFF_u64; + } + + let dp = self.decimal_point as usize; + let mut n = 0_u64; + + for i in 0..dp { + n *= 10; + if i < self.num_digits { + n += self.digits[i] as u64; + } + } + + let mut round_up = false; + + if dp < self.num_digits { + round_up = self.digits[dp] >= 5; + if self.digits[dp] == 5 && dp + 1 == self.num_digits { + round_up = self.truncated || ((dp != 0) && (1 & self.digits[dp - 1] != 0)) + } + } + + if round_up { + n += 1; + } + n + } + + /// Computes decimal * 2^shift. + pub(super) fn left_shift(&mut self, shift: usize) { + if self.num_digits == 0 { + return; + } + let num_new_digits = number_of_digits_decimal_left_shift(self, shift); + let mut read_index = self.num_digits; + let mut write_index = self.num_digits + num_new_digits; + let mut n = 0_u64; + + while read_index != 0 { + read_index -= 1; + write_index -= 1; + n += (self.digits[read_index] as u64) << shift; + let quotient = n / 10; + let remainder = n - (10 * quotient); + if write_index < Self::MAX_DIGITS { + self.digits[write_index] = remainder as u8; + } else if remainder > 0 { + self.truncated = true; + } + n = quotient; + } + + while n > 0 { + write_index -= 1; + let quotient = n / 10; + let remainder = n - (10 * quotient); + if write_index < Self::MAX_DIGITS { + self.digits[write_index] = remainder as u8; + } else if remainder > 0 { + self.truncated = true; + } + n = quotient; + } + + self.num_digits += num_new_digits; + + if self.num_digits > Self::MAX_DIGITS { + self.num_digits = Self::MAX_DIGITS; + } + + self.decimal_point += num_new_digits as i32; + self.trim(); + } + + /// Computes decimal * 2^-shift. + pub(super) fn right_shift(&mut self, shift: usize) { + let mut read_index = 0; + let mut write_index = 0; + let mut n = 0_u64; + while (n >> shift) == 0 { + if read_index < self.num_digits { + n = (10 * n) + self.digits[read_index] as u64; + read_index += 1; + } else if n == 0 { + return; + } else { + while (n >> shift) == 0 { + n *= 10; + read_index += 1; + } + break; + } + } + self.decimal_point -= read_index as i32 - 1; + if self.decimal_point < -Self::DECIMAL_POINT_RANGE { + // `self = Self::Default()`, but without the overhead of clearing `digits`. + self.num_digits = 0; + self.decimal_point = 0; + self.truncated = false; + return; + } + let mask = (1_u64 << shift) - 1; + while read_index < self.num_digits { + let new_digit = (n >> shift) as u8; + n = (10 * (n & mask)) + self.digits[read_index] as u64; + read_index += 1; + self.digits[write_index] = new_digit; + write_index += 1; + } + while n > 0 { + let new_digit = (n >> shift) as u8; + n = 10 * (n & mask); + if write_index < Self::MAX_DIGITS { + self.digits[write_index] = new_digit; + write_index += 1; + } else if new_digit > 0 { + self.truncated = true; + } + } + self.num_digits = write_index; + self.trim(); + } +} + +/// Parse a big integer representation of the float as a decimal. +pub fn parse_decimal_seq(mut s: &[u8]) -> DecimalSeq { + let mut d = DecimalSeq::default(); + let start = s; + + while let Some((&b'0', s_next)) = s.split_first() { + s = s_next; + } + + s = s.parse_digits(|digit| d.try_add_digit(digit)); + + if let Some((b'.', s_next)) = s.split_first() { + s = s_next; + let first = s; + // Skip leading zeros. + if d.num_digits == 0 { + while let Some((&b'0', s_next)) = s.split_first() { + s = s_next; + } + } + while s.len() >= 8 && d.num_digits + 8 < DecimalSeq::MAX_DIGITS { + let v = s.read_u64(); + if !is_8digits(v) { + break; + } + d.digits[d.num_digits..].write_u64(v - 0x3030_3030_3030_3030); + d.num_digits += 8; + s = &s[8..]; + } + s = s.parse_digits(|digit| d.try_add_digit(digit)); + d.decimal_point = s.len() as i32 - first.len() as i32; + } + + if d.num_digits != 0 { + // Ignore the trailing zeros if there are any + let mut n_trailing_zeros = 0; + for &c in start[..(start.len() - s.len())].iter().rev() { + if c == b'0' { + n_trailing_zeros += 1; + } else if c != b'.' { + break; + } + } + d.decimal_point += n_trailing_zeros as i32; + d.num_digits -= n_trailing_zeros; + d.decimal_point += d.num_digits as i32; + if d.num_digits > DecimalSeq::MAX_DIGITS { + d.truncated = true; + d.num_digits = DecimalSeq::MAX_DIGITS; + } + } + + if let Some((&ch, s_next)) = s.split_first() { + if ch == b'e' || ch == b'E' { + s = s_next; + let mut neg_exp = false; + if let Some((&ch, s_next)) = s.split_first() { + neg_exp = ch == b'-'; + if ch == b'-' || ch == b'+' { + s = s_next; + } + } + let mut exp_num = 0_i32; + + s.parse_digits(|digit| { + if exp_num < 0x10000 { + exp_num = 10 * exp_num + digit as i32; + } + }); + + d.decimal_point += if neg_exp { -exp_num } else { exp_num }; + } + } + + for i in d.num_digits..DecimalSeq::MAX_DIGITS_WITHOUT_OVERFLOW { + d.digits[i] = 0; + } + + d +} + +fn number_of_digits_decimal_left_shift(d: &DecimalSeq, mut shift: usize) -> usize { + #[rustfmt::skip] + const TABLE: [u16; 65] = [ + 0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817, 0x181D, 0x2024, + 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067, 0x3073, 0x3080, 0x388E, 0x389C, + 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF, 0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, + 0x5180, 0x5998, 0x59B0, 0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, + 0x72AA, 0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC, 0x8C02, + 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C, 0x051C, 0x051C, + ]; + #[rustfmt::skip] + const TABLE_POW5: [u8; 0x051C] = [ + 5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3, 9, 0, 6, 2, 5, 1, + 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8, 1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5, + 1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1, 0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, + 5, 1, 5, 2, 5, 8, 7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6, + 9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5, 3, 6, 7, 4, 3, 1, + 6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3, 1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9, + 1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0, 9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, 0, 4, 6, + 4, 4, 7, 7, 5, 3, 9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1, + 4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, 0, 5, 9, 6, 9, 2, + 3, 8, 2, 8, 1, 2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6, + 2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5, 7, 4, 6, 1, 5, + 4, 7, 8, 5, 1, 5, 6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2, + 5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5, 1, 1, 6, 4, 1, 5, + 3, 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5, 5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4, + 6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5, 2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6, + 1, 3, 2, 8, 1, 2, 5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0, + 6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2, 0, 3, 1, 2, 5, 3, + 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1, 6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, + 9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6, 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, + 7, 0, 1, 7, 7, 2, 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5, + 0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7, 3, 7, 3, 6, 7, 5, + 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5, 6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7, + 7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9, 3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, + 8, 6, 0, 8, 0, 8, 0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0, + 9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2, 5, 1, 4, 2, 1, 0, + 8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2, 4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1, + 0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, 0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, 5, 7, 8, 1, 2, + 5, 3, 5, 5, 2, 7, 1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8, + 9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, 6, 7, 7, 8, 1, 0, + 6, 6, 8, 9, 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, 9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3, + 8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, 6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8, 5, 0, 0, 6, 2, + 6, 1, 6, 1, 6, 9, 4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4, + 9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4, 0, 6, 2, 5, 1, 1, + 1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4, 2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8, + 2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1, 5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5, + 8, 3, 4, 0, 4, 5, 4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1, + 3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1, 3, 8, 7, 7, 7, 8, + 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, 9, 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, + 6, 2, 5, 6, 9, 3, 8, 8, 9, 3, 9, 0, 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, + 5, 5, 6, 7, 6, 2, 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1, + 8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5, 1, 7, 3, 4, 7, 2, + 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2, 4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3, + 8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1, 7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2, + 2, 4, 0, 6, 9, 5, 9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5, + ]; + + shift &= 63; + let x_a = TABLE[shift]; + let x_b = TABLE[shift + 1]; + let num_new_digits = (x_a >> 11) as _; + let pow5_a = (0x7FF & x_a) as usize; + let pow5_b = (0x7FF & x_b) as usize; + let pow5 = &TABLE_POW5[pow5_a..]; + + for (i, &p5) in pow5.iter().enumerate().take(pow5_b - pow5_a) { + if i >= d.num_digits { + return num_new_digits - 1; + } else if d.digits[i] == p5 { + continue; + } else if d.digits[i] < p5 { + return num_new_digits - 1; + } else { + return num_new_digits; + } + } + + num_new_digits +} diff --git a/library/core/src/num/dec2flt/float.rs b/library/core/src/num/dec2flt/float.rs index da57aa9a546af..5bf0faf0bc910 100644 --- a/library/core/src/num/dec2flt/float.rs +++ b/library/core/src/num/dec2flt/float.rs @@ -1,14 +1,57 @@ //! Helper trait for generic float types. +use core::f64; + use crate::fmt::{Debug, LowerExp}; use crate::num::FpCategory; -use crate::ops::{Add, Div, Mul, Neg}; +use crate::ops::{self, Add, Div, Mul, Neg}; + +/// Lossy `as` casting between two types. +pub trait CastInto: Copy { + fn cast(self) -> T; +} -/// A helper trait to avoid duplicating basically all the conversion code for `f32` and `f64`. +/// Collection of traits that allow us to be generic over integer size. +pub trait Integer: + Sized + + Clone + + Copy + + Debug + + ops::Shr + + ops::Shl + + ops::BitAnd + + ops::BitOr + + PartialEq + + CastInto +{ + const ZERO: Self; + const ONE: Self; +} + +macro_rules! int { + ($($ty:ty),+) => { + $( + impl CastInto for $ty { + fn cast(self) -> i16 { + self as i16 + } + } + + impl Integer for $ty { + const ZERO: Self = 0; + const ONE: Self = 1; + } + )+ + } +} + +int!(u16, u32, u64); + +/// A helper trait to avoid duplicating basically all the conversion code for IEEE floats. /// /// See the parent module's doc comment for why this is necessary. /// -/// Should **never ever** be implemented for other types or be used outside the dec2flt module. +/// Should **never ever** be implemented for other types or be used outside the `dec2flt` module. #[doc(hidden)] pub trait RawFloat: Sized @@ -24,62 +67,107 @@ pub trait RawFloat: + Copy + Debug { + /// The unsigned integer with the same size as the float + type Int: Integer + Into; + + /* general constants */ + const INFINITY: Self; const NEG_INFINITY: Self; const NAN: Self; const NEG_NAN: Self; - /// The number of bits in the significand, *excluding* the hidden bit. - const MANTISSA_EXPLICIT_BITS: usize; - - // Round-to-even only happens for negative values of q - // when q ≥ −4 in the 64-bit case and when q ≥ −17 in - // the 32-bitcase. - // - // When q ≥ 0,we have that 5^q ≤ 2m+1. In the 64-bit case,we - // have 5^q ≤ 2m+1 ≤ 2^54 or q ≤ 23. In the 32-bit case,we have - // 5^q ≤ 2m+1 ≤ 2^25 or q ≤ 10. - // - // When q < 0, we have w ≥ (2m+1)×5^−q. We must have that w < 2^64 - // so (2m+1)×5^−q < 2^64. We have that 2m+1 > 2^53 (64-bit case) - // or 2m+1 > 2^24 (32-bit case). Hence,we must have 2^53×5^−q < 2^64 - // (64-bit) and 2^24×5^−q < 2^64 (32-bit). Hence we have 5^−q < 2^11 - // or q ≥ −4 (64-bit case) and 5^−q < 2^40 or q ≥ −17 (32-bitcase). - // - // Thus we have that we only need to round ties to even when - // we have that q ∈ [−4,23](in the 64-bit case) or q∈[−17,10] - // (in the 32-bit case). In both cases,the power of five(5^|q|) - // fits in a 64-bit word. - const MIN_EXPONENT_ROUND_TO_EVEN: i32; - const MAX_EXPONENT_ROUND_TO_EVEN: i32; - - // Minimum exponent that for a fast path case, or `-⌊(MANTISSA_EXPLICIT_BITS+1)/log2(5)⌋` - const MIN_EXPONENT_FAST_PATH: i64; + /// Bit width of the float + const BITS: u32; - // Maximum exponent that for a fast path case, or `⌊(MANTISSA_EXPLICIT_BITS+1)/log2(5)⌋` - const MAX_EXPONENT_FAST_PATH: i64; + /// The number of bits in the significand, *including* the hidden bit. + const SIG_TOTAL_BITS: u32; - // Maximum exponent that can be represented for a disguised-fast path case. - // This is `MAX_EXPONENT_FAST_PATH + ⌊(MANTISSA_EXPLICIT_BITS+1)/log2(10)⌋` - const MAX_EXPONENT_DISGUISED_FAST_PATH: i64; + const EXP_MASK: Self::Int; + const SIG_MASK: Self::Int; - // Minimum exponent value `-(1 << (EXP_BITS - 1)) + 1`. - const MINIMUM_EXPONENT: i32; + /// The number of bits in the significand, *excluding* the hidden bit. + const SIG_BITS: u32 = Self::SIG_TOTAL_BITS - 1; + + /// Number of bits in the exponent. + const EXP_BITS: u32 = Self::BITS - Self::SIG_BITS - 1; + + /// The saturated (maximum bitpattern) value of the exponent, i.e. the infinite + /// representation. + /// + /// This shifted fully right, use `EXP_MASK` for the shifted value. + const EXP_SAT: u32 = (1 << Self::EXP_BITS) - 1; + + /// Signed version of `EXP_SAT` since we convert a lot. + const INFINITE_POWER: i32 = Self::EXP_SAT as i32; + + /// The exponent bias value. This is also the maximum value of the exponent. + const EXP_BIAS: u32 = Self::EXP_SAT >> 1; + + /// Minimum exponent value of normal values. + const EXP_MIN: i32 = -(Self::EXP_BIAS as i32 - 1); + + /// Round-to-even only happens for negative values of q + /// when q ≥ −4 in the 64-bit case and when q ≥ −17 in + /// the 32-bitcase. + /// + /// When q ≥ 0,we have that 5^q ≤ 2m+1. In the 64-bit case,we + /// have 5^q ≤ 2m+1 ≤ 2^54 or q ≤ 23. In the 32-bit case,we have + /// 5^q ≤ 2m+1 ≤ 2^25 or q ≤ 10. + /// + /// When q < 0, we have w ≥ (2m+1)×5^−q. We must have that w < 2^64 + /// so (2m+1)×5^−q < 2^64. We have that 2m+1 > 2^53 (64-bit case) + /// or 2m+1 > 2^24 (32-bit case). Hence,we must have 2^53×5^−q < 2^64 + /// (64-bit) and 2^24×5^−q < 2^64 (32-bit). Hence we have 5^−q < 2^11 + /// or q ≥ −4 (64-bit case) and 5^−q < 2^40 or q ≥ −17 (32-bitcase). + /// + /// Thus we have that we only need to round ties to even when + /// we have that q ∈ [−4,23](in the 64-bit case) or q∈[−17,10] + /// (in the 32-bit case). In both cases,the power of five(5^|q|) + /// fits in a 64-bit word. + const MIN_EXPONENT_ROUND_TO_EVEN: i32; + const MAX_EXPONENT_ROUND_TO_EVEN: i32; - // Largest exponent value `(1 << EXP_BITS) - 1`. - const INFINITE_POWER: i32; + /* limits related to Fast pathing */ + + /// Largest decimal exponent for a non-infinite value. + /// + /// This is the max exponent in binary converted to the max exponent in decimal. Allows fast + /// pathing anything larger than `10^LARGEST_POWER_OF_TEN`, which will round to infinity. + const LARGEST_POWER_OF_TEN: i32 = { + let largest_pow2 = Self::EXP_BIAS + 1; + pow2_to_pow10(largest_pow2 as i64) as i32 + }; + + /// Smallest decimal exponent for a non-zero value. This allows for fast pathing anything + /// smaller than `10^SMALLEST_POWER_OF_TEN`, which will round to zero. + /// + /// The smallest power of ten is represented by `⌊log10(2^-n / (2^64 - 1))⌋`, where `n` is + /// the smallest power of two. The `2^64 - 1)` denomenator comes from the number of values + /// that are representable by the intermediate storage format. I don't actually know _why_ + /// the storage format is relevant here. + /// + /// The values may be calculated using the formula. Unfortunately we cannot calculate them at + /// compile time since intermediates exceed the range of an `f64`. + const SMALLEST_POWER_OF_TEN: i32; - // Index (in bits) of the sign. - const SIGN_INDEX: usize; + /// Maximum exponent for a fast path case, or `⌊(SIG_BITS+1)/log2(5)⌋` + // assuming FLT_EVAL_METHOD = 0 + const MAX_EXPONENT_FAST_PATH: i64 = { + let log2_5 = f64::consts::LOG2_10 - 1.0; + (Self::SIG_TOTAL_BITS as f64 / log2_5) as i64 + }; - // Smallest decimal exponent for a non-zero value. - const SMALLEST_POWER_OF_TEN: i32; + /// Minimum exponent for a fast path case, or `-⌊(SIG_BITS+1)/log2(5)⌋` + const MIN_EXPONENT_FAST_PATH: i64 = -Self::MAX_EXPONENT_FAST_PATH; - // Largest decimal exponent for a non-infinite value. - const LARGEST_POWER_OF_TEN: i32; + /// Maximum exponent that can be represented for a disguised-fast path case. + /// This is `MAX_EXPONENT_FAST_PATH + ⌊(SIG_BITS+1)/log2(10)⌋` + const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 = + Self::MAX_EXPONENT_FAST_PATH + (Self::SIG_TOTAL_BITS as f64 / f64::consts::LOG2_10) as i64; - // Maximum mantissa for the fast-path (`1 << 53` for f64). - const MAX_MANTISSA_FAST_PATH: u64 = 2_u64 << Self::MANTISSA_EXPLICIT_BITS; + /// Maximum mantissa for the fast-path (`1 << 53` for f64). + const MAX_MANTISSA_FAST_PATH: u64 = 1 << Self::SIG_TOTAL_BITS; /// Converts integer into float through an as cast. /// This is only called in the fast-path algorithm, and therefore @@ -96,27 +184,100 @@ pub trait RawFloat: /// Returns the category that this number falls into. fn classify(self) -> FpCategory; + /// Transmute to the integer representation + fn to_bits(self) -> Self::Int; + /// Returns the mantissa, exponent and sign as integers. - fn integer_decode(self) -> (u64, i16, i8); + /// + /// This returns `(m, p, s)` such that `s * m * 2^p` represents the original float. For 0, the + /// exponent will be `-(EXP_BIAS + SIG_BITS)`, which is the minimum subnormal power. For + /// infinity or NaN, the exponent will be `EXP_SAT - EXP_BIAS - SIG_BITS`. + /// + /// If subnormal, the mantissa will be shifted one bit to the left. Otherwise, it is returned + /// with the explicit bit set but otherwise unshifted + /// + /// `s` is only ever +/-1. + fn integer_decode(self) -> (u64, i16, i8) { + let bits = self.to_bits(); + let sign: i8 = if bits >> (Self::BITS - 1) == Self::Int::ZERO { 1 } else { -1 }; + let mut exponent: i16 = ((bits & Self::EXP_MASK) >> Self::SIG_BITS).cast(); + let mantissa = if exponent == 0 { + (bits & Self::SIG_MASK) << 1 + } else { + (bits & Self::SIG_MASK) | (Self::Int::ONE << Self::SIG_BITS) + }; + // Exponent bias + mantissa shift + exponent -= (Self::EXP_BIAS + Self::SIG_BITS) as i16; + (mantissa.into(), exponent, sign) + } +} + +/// Solve for `b` in `10^b = 2^a` +const fn pow2_to_pow10(a: i64) -> i64 { + let res = (a as f64) / f64::consts::LOG2_10; + res as i64 +} + +#[cfg(target_has_reliable_f16)] +impl RawFloat for f16 { + type Int = u16; + + const INFINITY: Self = Self::INFINITY; + const NEG_INFINITY: Self = Self::NEG_INFINITY; + const NAN: Self = Self::NAN; + const NEG_NAN: Self = -Self::NAN; + + const BITS: u32 = 16; + const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS; + const EXP_MASK: Self::Int = Self::EXP_MASK; + const SIG_MASK: Self::Int = Self::MAN_MASK; + + const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -22; + const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 5; + const SMALLEST_POWER_OF_TEN: i32 = -27; + + #[inline] + fn from_u64(v: u64) -> Self { + debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); + v as _ + } + + #[inline] + fn from_u64_bits(v: u64) -> Self { + Self::from_bits((v & 0xFFFF) as u16) + } + + fn pow10_fast_path(exponent: usize) -> Self { + #[allow(clippy::use_self)] + const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.]; + TABLE[exponent & 7] + } + + fn to_bits(self) -> Self::Int { + self.to_bits() + } + + fn classify(self) -> FpCategory { + self.classify() + } } impl RawFloat for f32 { + type Int = u32; + const INFINITY: Self = f32::INFINITY; const NEG_INFINITY: Self = f32::NEG_INFINITY; const NAN: Self = f32::NAN; const NEG_NAN: Self = -f32::NAN; - const MANTISSA_EXPLICIT_BITS: usize = 23; + const BITS: u32 = 32; + const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS; + const EXP_MASK: Self::Int = Self::EXP_MASK; + const SIG_MASK: Self::Int = Self::MAN_MASK; + const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -17; const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 10; - const MIN_EXPONENT_FAST_PATH: i64 = -10; // assuming FLT_EVAL_METHOD = 0 - const MAX_EXPONENT_FAST_PATH: i64 = 10; - const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 = 17; - const MINIMUM_EXPONENT: i32 = -127; - const INFINITE_POWER: i32 = 0xFF; - const SIGN_INDEX: usize = 31; const SMALLEST_POWER_OF_TEN: i32 = -65; - const LARGEST_POWER_OF_TEN: i32 = 38; #[inline] fn from_u64(v: u64) -> Self { @@ -136,16 +297,8 @@ impl RawFloat for f32 { TABLE[exponent & 15] } - /// Returns the mantissa, exponent and sign as integers. - fn integer_decode(self) -> (u64, i16, i8) { - let bits = self.to_bits(); - let sign: i8 = if bits >> 31 == 0 { 1 } else { -1 }; - let mut exponent: i16 = ((bits >> 23) & 0xff) as i16; - let mantissa = - if exponent == 0 { (bits & 0x7fffff) << 1 } else { (bits & 0x7fffff) | 0x800000 }; - // Exponent bias + mantissa shift - exponent -= 127 + 23; - (mantissa as u64, exponent, sign) + fn to_bits(self) -> Self::Int { + self.to_bits() } fn classify(self) -> FpCategory { @@ -154,22 +307,21 @@ impl RawFloat for f32 { } impl RawFloat for f64 { - const INFINITY: Self = f64::INFINITY; - const NEG_INFINITY: Self = f64::NEG_INFINITY; - const NAN: Self = f64::NAN; - const NEG_NAN: Self = -f64::NAN; + type Int = u64; + + const INFINITY: Self = Self::INFINITY; + const NEG_INFINITY: Self = Self::NEG_INFINITY; + const NAN: Self = Self::NAN; + const NEG_NAN: Self = -Self::NAN; + + const BITS: u32 = 64; + const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS; + const EXP_MASK: Self::Int = Self::EXP_MASK; + const SIG_MASK: Self::Int = Self::MAN_MASK; - const MANTISSA_EXPLICIT_BITS: usize = 52; const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -4; const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 23; - const MIN_EXPONENT_FAST_PATH: i64 = -22; // assuming FLT_EVAL_METHOD = 0 - const MAX_EXPONENT_FAST_PATH: i64 = 22; - const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 = 37; - const MINIMUM_EXPONENT: i32 = -1023; - const INFINITE_POWER: i32 = 0x7FF; - const SIGN_INDEX: usize = 63; const SMALLEST_POWER_OF_TEN: i32 = -342; - const LARGEST_POWER_OF_TEN: i32 = 308; #[inline] fn from_u64(v: u64) -> Self { @@ -190,19 +342,8 @@ impl RawFloat for f64 { TABLE[exponent & 31] } - /// Returns the mantissa, exponent and sign as integers. - fn integer_decode(self) -> (u64, i16, i8) { - let bits = self.to_bits(); - let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 }; - let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16; - let mantissa = if exponent == 0 { - (bits & 0xfffffffffffff) << 1 - } else { - (bits & 0xfffffffffffff) | 0x10000000000000 - }; - // Exponent bias + mantissa shift - exponent -= 1023 + 52; - (mantissa, exponent, sign) + fn to_bits(self) -> Self::Int { + self.to_bits() } fn classify(self) -> FpCategory { diff --git a/library/core/src/num/dec2flt/fpu.rs b/library/core/src/num/dec2flt/fpu.rs index daeee1755b0b5..8aad087ec1bc4 100644 --- a/library/core/src/num/dec2flt/fpu.rs +++ b/library/core/src/num/dec2flt/fpu.rs @@ -22,7 +22,6 @@ pub(super) use fpu_precision::set_precision; #[cfg(all(target_arch = "x86", not(target_feature = "sse2")))] mod fpu_precision { use core::arch::asm; - use core::mem::size_of; /// A structure used to preserve the original value of the FPU control word, so that it can be /// restored when the structure is dropped. diff --git a/library/core/src/num/dec2flt/lemire.rs b/library/core/src/num/dec2flt/lemire.rs index 01642e1b1112a..f84929a03c172 100644 --- a/library/core/src/num/dec2flt/lemire.rs +++ b/library/core/src/num/dec2flt/lemire.rs @@ -38,7 +38,7 @@ pub fn compute_float(q: i64, mut w: u64) -> BiasedFp { // Normalize our significant digits, so the most-significant bit is set. let lz = w.leading_zeros(); w <<= lz; - let (lo, hi) = compute_product_approx(q, w, F::MANTISSA_EXPLICIT_BITS + 3); + let (lo, hi) = compute_product_approx(q, w, F::SIG_BITS as usize + 3); if lo == 0xFFFF_FFFF_FFFF_FFFF { // If we have failed to approximate w x 5^-q with our 128-bit value. // Since the addition of 1 could lead to an overflow which could then @@ -61,8 +61,8 @@ pub fn compute_float(q: i64, mut w: u64) -> BiasedFp { } } let upperbit = (hi >> 63) as i32; - let mut mantissa = hi >> (upperbit + 64 - F::MANTISSA_EXPLICIT_BITS as i32 - 3); - let mut power2 = power(q as i32) + upperbit - lz as i32 - F::MINIMUM_EXPONENT; + let mut mantissa = hi >> (upperbit + 64 - F::SIG_BITS as i32 - 3); + let mut power2 = power(q as i32) + upperbit - lz as i32 - F::EXP_MIN + 1; if power2 <= 0 { if -power2 + 1 >= 64 { // Have more than 64 bits below the minimum exponent, must be 0. @@ -72,8 +72,8 @@ pub fn compute_float(q: i64, mut w: u64) -> BiasedFp { mantissa >>= -power2 + 1; mantissa += mantissa & 1; mantissa >>= 1; - power2 = (mantissa >= (1_u64 << F::MANTISSA_EXPLICIT_BITS)) as i32; - return BiasedFp { f: mantissa, e: power2 }; + power2 = (mantissa >= (1_u64 << F::SIG_BITS)) as i32; + return BiasedFp { m: mantissa, p_biased: power2 }; } // Need to handle rounding ties. Normally, we need to round up, // but if we fall right in between and we have an even basis, we @@ -89,8 +89,8 @@ pub fn compute_float(q: i64, mut w: u64) -> BiasedFp { if lo <= 1 && q >= F::MIN_EXPONENT_ROUND_TO_EVEN as i64 && q <= F::MAX_EXPONENT_ROUND_TO_EVEN as i64 - && mantissa & 3 == 1 - && (mantissa << (upperbit + 64 - F::MANTISSA_EXPLICIT_BITS as i32 - 3)) == hi + && mantissa & 0b11 == 0b01 + && (mantissa << (upperbit + 64 - F::SIG_BITS as i32 - 3)) == hi { // Zero the lowest bit, so we don't round up. mantissa &= !1_u64; @@ -98,20 +98,20 @@ pub fn compute_float(q: i64, mut w: u64) -> BiasedFp { // Round-to-even, then shift the significant digits into place. mantissa += mantissa & 1; mantissa >>= 1; - if mantissa >= (2_u64 << F::MANTISSA_EXPLICIT_BITS) { + if mantissa >= (2_u64 << F::SIG_BITS) { // Rounding up overflowed, so the carry bit is set. Set the // mantissa to 1 (only the implicit, hidden bit is set) and // increase the exponent. - mantissa = 1_u64 << F::MANTISSA_EXPLICIT_BITS; + mantissa = 1_u64 << F::SIG_BITS; power2 += 1; } // Zero out the hidden bit. - mantissa &= !(1_u64 << F::MANTISSA_EXPLICIT_BITS); + mantissa &= !(1_u64 << F::SIG_BITS); if power2 >= F::INFINITE_POWER { // Exponent is above largest normal value, must be infinite. return fp_inf; } - BiasedFp { f: mantissa, e: power2 } + BiasedFp { m: mantissa, p_biased: power2 } } /// Calculate a base 2 exponent from a decimal exponent. diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs index 6dca740684537..abad7acb1046a 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/dec2flt/mod.rs @@ -3,8 +3,8 @@ //! # Problem statement //! //! We are given a decimal string such as `12.34e56`. This string consists of integral (`12`), -//! fractional (`34`), and exponent (`56`) parts. All parts are optional and interpreted as zero -//! when missing. +//! fractional (`34`), and exponent (`56`) parts. All parts are optional and interpreted as a +//! default value (1 or 0) 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 @@ -67,6 +67,18 @@ //! "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}. +//! +//! # Notation +//! +//! This module uses the same notation as the Lemire paper: +//! +//! - `m`: binary mantissa; always nonnegative +//! - `p`: binary exponent; a signed integer +//! - `w`: decimal significand; always nonnegative +//! - `q`: decimal exponent; a signed integer +//! +//! This gives `m * 2^p` for the binary floating-point number, with `w * 10^q` as the decimal +//! equivalent. #![doc(hidden)] #![unstable( @@ -85,14 +97,14 @@ use crate::fmt; use crate::str::FromStr; mod common; -mod decimal; +pub mod decimal; +pub mod decimal_seq; mod fpu; mod slow; mod table; // float is used in flt2dec, and all are used in unit tests. pub mod float; pub mod lemire; -pub mod number; pub mod parse; macro_rules! from_str_float_impl { @@ -159,9 +171,25 @@ macro_rules! from_str_float_impl { } }; } + +#[cfg(target_has_reliable_f16)] +from_str_float_impl!(f16); from_str_float_impl!(f32); from_str_float_impl!(f64); +// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order +// to avoid ICEs. + +#[cfg(not(target_has_reliable_f16))] +impl FromStr for f16 { + type Err = ParseFloatError; + + #[inline] + fn from_str(_src: &str) -> Result { + unimplemented!("requires target_has_reliable_f16") + } +} + /// An error which can be returned when parsing a float. /// /// This error is used as the error type for the [`FromStr`] implementation @@ -220,10 +248,10 @@ pub fn pfe_invalid() -> ParseFloatError { } /// Converts a `BiasedFp` to the closest machine float type. -fn biased_fp_to_float(x: BiasedFp) -> T { - let mut word = x.f; - word |= (x.e as u64) << T::MANTISSA_EXPLICIT_BITS; - T::from_u64_bits(word) +fn biased_fp_to_float(x: BiasedFp) -> F { + let mut word = x.m; + word |= (x.p_biased as u64) << F::SIG_BITS; + F::from_u64_bits(word) } /// Converts a decimal string into a floating point number. @@ -260,12 +288,15 @@ pub fn dec2flt(s: &str) -> Result { // redundantly using the Eisel-Lemire algorithm if it was unable to // correctly round on the first pass. let mut fp = compute_float::(num.exponent, num.mantissa); - if num.many_digits && fp.e >= 0 && fp != compute_float::(num.exponent, num.mantissa + 1) { - fp.e = -1; + if num.many_digits + && fp.p_biased >= 0 + && fp != compute_float::(num.exponent, num.mantissa + 1) + { + fp.p_biased = -1; } // Unable to correctly round the float using the Eisel-Lemire algorithm. // Fallback to a slower, but always correct algorithm. - if fp.e < 0 { + if fp.p_biased < 0 { fp = parse_long_mantissa::(s); } diff --git a/library/core/src/num/dec2flt/number.rs b/library/core/src/num/dec2flt/number.rs deleted file mode 100644 index 2538991564ae4..0000000000000 --- a/library/core/src/num/dec2flt/number.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Representation of a float as the significant digits and exponent. - -use crate::num::dec2flt::float::RawFloat; -use crate::num::dec2flt::fpu::set_precision; - -#[rustfmt::skip] -const INT_POW10: [u64; 16] = [ - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000, - 100000000000, - 1000000000000, - 10000000000000, - 100000000000000, - 1000000000000000, -]; - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub struct Number { - pub exponent: i64, - pub mantissa: u64, - pub negative: bool, - pub many_digits: bool, -} - -impl Number { - /// Detect if the float can be accurately reconstructed from native floats. - #[inline] - fn is_fast_path(&self) -> bool { - F::MIN_EXPONENT_FAST_PATH <= self.exponent - && self.exponent <= F::MAX_EXPONENT_DISGUISED_FAST_PATH - && self.mantissa <= F::MAX_MANTISSA_FAST_PATH - && !self.many_digits - } - - /// The fast path algorithm using machine-sized integers and floats. - /// - /// This is extracted into a separate function so that it can be attempted before constructing - /// a Decimal. This only works if both the mantissa and the exponent - /// can be exactly represented as a machine float, since IEE-754 guarantees - /// no rounding will occur. - /// - /// There is an exception: disguised fast-path cases, where we can shift - /// powers-of-10 from the exponent to the significant digits. - pub fn try_fast_path(&self) -> Option { - // Here we need to work around . - // The fast path crucially depends on arithmetic being rounded to the correct number of bits - // without any intermediate rounding. On x86 (without SSE or SSE2) this requires the precision - // of the x87 FPU stack to be changed so that it directly rounds to 64/32 bit. - // The `set_precision` function takes care of setting the precision on architectures which - // require setting it by changing the global state (like the control word of the x87 FPU). - let _cw = set_precision::(); - - if self.is_fast_path::() { - let mut value = if self.exponent <= F::MAX_EXPONENT_FAST_PATH { - // normal fast path - let value = F::from_u64(self.mantissa); - if self.exponent < 0 { - value / F::pow10_fast_path((-self.exponent) as _) - } else { - value * F::pow10_fast_path(self.exponent as _) - } - } else { - // disguised fast path - let shift = self.exponent - F::MAX_EXPONENT_FAST_PATH; - let mantissa = self.mantissa.checked_mul(INT_POW10[shift as usize])?; - if mantissa > F::MAX_MANTISSA_FAST_PATH { - return None; - } - F::from_u64(mantissa) * F::pow10_fast_path(F::MAX_EXPONENT_FAST_PATH as _) - }; - if self.negative { - value = -value; - } - Some(value) - } else { - None - } - } -} diff --git a/library/core/src/num/dec2flt/parse.rs b/library/core/src/num/dec2flt/parse.rs index 06ee8e95fbc47..e38fedc58bec0 100644 --- a/library/core/src/num/dec2flt/parse.rs +++ b/library/core/src/num/dec2flt/parse.rs @@ -1,8 +1,8 @@ //! Functions to parse floating-point numbers. use crate::num::dec2flt::common::{ByteSlice, is_8digits}; +use crate::num::dec2flt::decimal::Decimal; use crate::num::dec2flt::float::RawFloat; -use crate::num::dec2flt::number::Number; const MIN_19DIGIT_INT: u64 = 100_0000_0000_0000_0000; @@ -100,7 +100,7 @@ fn parse_scientific(s_ref: &mut &[u8]) -> Option { /// /// This creates a representation of the float as the /// significant digits and the decimal exponent. -fn parse_partial_number(mut s: &[u8]) -> Option<(Number, usize)> { +fn parse_partial_number(mut s: &[u8]) -> Option<(Decimal, usize)> { debug_assert!(!s.is_empty()); // parse initial digits before dot @@ -146,7 +146,7 @@ fn parse_partial_number(mut s: &[u8]) -> Option<(Number, usize)> { // handle uncommon case with many digits if n_digits <= 19 { - return Some((Number { exponent, mantissa, negative: false, many_digits: false }, len)); + return Some((Decimal { exponent, mantissa, negative: false, many_digits: false }, len)); } n_digits -= 19; @@ -179,13 +179,13 @@ fn parse_partial_number(mut s: &[u8]) -> Option<(Number, usize)> { exponent += exp_number; } - Some((Number { exponent, mantissa, negative: false, many_digits }, len)) + Some((Decimal { exponent, mantissa, negative: false, many_digits }, len)) } /// Try to parse a non-special floating point number, /// as well as two slices with integer and fractional parts /// and the parsed exponent. -pub fn parse_number(s: &[u8]) -> Option { +pub fn parse_number(s: &[u8]) -> Option { if let Some((float, rest)) = parse_partial_number(s) { if rest == s.len() { return Some(float); diff --git a/library/core/src/num/dec2flt/slow.rs b/library/core/src/num/dec2flt/slow.rs index 85d4b13284b7d..3baed42652393 100644 --- a/library/core/src/num/dec2flt/slow.rs +++ b/library/core/src/num/dec2flt/slow.rs @@ -1,7 +1,7 @@ //! Slow, fallback algorithm for cases the Eisel-Lemire algorithm cannot round. use crate::num::dec2flt::common::BiasedFp; -use crate::num::dec2flt::decimal::{Decimal, parse_decimal}; +use crate::num::dec2flt::decimal_seq::{DecimalSeq, parse_decimal_seq}; use crate::num::dec2flt::float::RawFloat; /// Parse the significant digits and biased, binary exponent of a float. @@ -36,7 +36,7 @@ pub(crate) fn parse_long_mantissa(s: &[u8]) -> BiasedFp { let fp_zero = BiasedFp::zero_pow2(0); let fp_inf = BiasedFp::zero_pow2(F::INFINITE_POWER); - let mut d = parse_decimal(s); + let mut d = parse_decimal_seq(s); // Short-circuit if the value can only be a literal 0 or infinity. if d.num_digits == 0 || d.decimal_point < -324 { @@ -50,7 +50,7 @@ pub(crate) fn parse_long_mantissa(s: &[u8]) -> BiasedFp { let n = d.decimal_point as usize; let shift = get_shift(n); d.right_shift(shift); - if d.decimal_point < -Decimal::DECIMAL_POINT_RANGE { + if d.decimal_point < -DecimalSeq::DECIMAL_POINT_RANGE { return fp_zero; } exp2 += shift as i32; @@ -67,43 +67,43 @@ pub(crate) fn parse_long_mantissa(s: &[u8]) -> BiasedFp { get_shift((-d.decimal_point) as _) }; d.left_shift(shift); - if d.decimal_point > Decimal::DECIMAL_POINT_RANGE { + if d.decimal_point > DecimalSeq::DECIMAL_POINT_RANGE { return fp_inf; } exp2 -= shift as i32; } // We are now in the range [1/2 ... 1] but the binary format uses [1 ... 2]. exp2 -= 1; - while (F::MINIMUM_EXPONENT + 1) > exp2 { - let mut n = ((F::MINIMUM_EXPONENT + 1) - exp2) as usize; + while F::EXP_MIN > exp2 { + let mut n = (F::EXP_MIN - exp2) as usize; if n > MAX_SHIFT { n = MAX_SHIFT; } d.right_shift(n); exp2 += n as i32; } - if (exp2 - F::MINIMUM_EXPONENT) >= F::INFINITE_POWER { + if (exp2 - F::EXP_MIN + 1) >= F::INFINITE_POWER { return fp_inf; } // Shift the decimal to the hidden bit, and then round the value // to get the high mantissa+1 bits. - d.left_shift(F::MANTISSA_EXPLICIT_BITS + 1); + d.left_shift(F::SIG_BITS as usize + 1); let mut mantissa = d.round(); - if mantissa >= (1_u64 << (F::MANTISSA_EXPLICIT_BITS + 1)) { + if mantissa >= (1_u64 << (F::SIG_BITS + 1)) { // Rounding up overflowed to the carry bit, need to // shift back to the hidden bit. d.right_shift(1); exp2 += 1; mantissa = d.round(); - if (exp2 - F::MINIMUM_EXPONENT) >= F::INFINITE_POWER { + if (exp2 - F::EXP_MIN + 1) >= F::INFINITE_POWER { return fp_inf; } } - let mut power2 = exp2 - F::MINIMUM_EXPONENT; - if mantissa < (1_u64 << F::MANTISSA_EXPLICIT_BITS) { + let mut power2 = exp2 - F::EXP_MIN + 1; + if mantissa < (1_u64 << F::SIG_BITS) { power2 -= 1; } // Zero out all the bits above the explicit mantissa bits. - mantissa &= (1_u64 << F::MANTISSA_EXPLICIT_BITS) - 1; - BiasedFp { f: mantissa, e: power2 } + mantissa &= (1_u64 << F::SIG_BITS) - 1; + BiasedFp { m: mantissa, p_biased: power2 } } diff --git a/library/core/src/num/diy_float.rs b/library/core/src/num/diy_float.rs index ce7f6475d0599..e054e7f3f10a7 100644 --- a/library/core/src/num/diy_float.rs +++ b/library/core/src/num/diy_float.rs @@ -21,61 +21,29 @@ pub struct Fp { impl Fp { /// Returns a correctly rounded product of itself and `other`. - pub fn mul(&self, other: &Fp) -> Fp { - const MASK: u64 = 0xffffffff; - let a = self.f >> 32; - let b = self.f & MASK; - let c = other.f >> 32; - let d = other.f & MASK; - let ac = a * c; - let bc = b * c; - let ad = a * d; - let bd = b * d; - let tmp = (bd >> 32) + (ad & MASK) + (bc & MASK) + (1 << 31) /* round */; - let f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + pub fn mul(self, other: Self) -> Self { + let (lo, hi) = self.f.widening_mul(other.f); + let f = hi + (lo >> 63) /* round */; let e = self.e + other.e + 64; - Fp { f, e } + Self { f, e } } /// Normalizes itself so that the resulting mantissa is at least `2^63`. - pub fn normalize(&self) -> Fp { - let mut f = self.f; - let mut e = self.e; - if f >> (64 - 32) == 0 { - f <<= 32; - e -= 32; - } - if f >> (64 - 16) == 0 { - f <<= 16; - e -= 16; - } - if f >> (64 - 8) == 0 { - f <<= 8; - e -= 8; - } - if f >> (64 - 4) == 0 { - f <<= 4; - e -= 4; - } - if f >> (64 - 2) == 0 { - f <<= 2; - e -= 2; - } - if f >> (64 - 1) == 0 { - f <<= 1; - e -= 1; - } + pub fn normalize(self) -> Self { + let lz = self.f.leading_zeros(); + let f = self.f << lz; + let e = self.e - lz as i16; debug_assert!(f >= (1 << 63)); - Fp { f, e } + Self { f, e } } /// Normalizes itself to have the shared exponent. /// It can only decrease the exponent (and thus increase the mantissa). - pub fn normalize_to(&self, e: i16) -> Fp { + pub fn normalize_to(self, e: i16) -> Self { let edelta = self.e - e; assert!(edelta >= 0); let edelta = edelta as usize; assert_eq!(self.f << edelta >> edelta, self.f); - Fp { f: self.f << edelta, e } + Self { f: self.f << edelta, e } } } diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 5e45974b3d422..0c2c4155d66ce 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -12,11 +12,9 @@ #![unstable(feature = "f128", issue = "116909")] use crate::convert::FloatToInt; -#[cfg(not(test))] -use crate::intrinsics; -use crate::mem; use crate::num::FpCategory; use crate::panic::const_assert; +use crate::{intrinsics, mem}; /// Basic mathematical constants. #[unstable(feature = "f128", issue = "116909")] @@ -138,7 +136,6 @@ pub mod consts { pub const LN_10: f128 = 2.30258509299404568401799145468436420760110148862877297603333_f128; } -#[cfg(not(test))] impl f128 { // FIXME(f16_f128): almost all methods in this `impl` are missing examples and a const // implementation. Add these once we can run code on all platforms and have f16/f128 in CTFE. @@ -148,6 +145,9 @@ impl f128 { pub const RADIX: u32 = 2; /// Number of significant digits in base 2. + /// + /// Note that the size of the mantissa in the bitwise representation is one + /// smaller than this since the leading 1 is not stored explicitly. #[unstable(feature = "f128", issue = "116909")] pub const MANTISSA_DIGITS: u32 = 113; @@ -197,16 +197,22 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] pub const MAX: f128 = 1.18973149535723176508575932662800702e+4932_f128; - /// One greater than the minimum possible normal power of 2 exponent. + /// One greater than the minimum possible *normal* power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MIN_EXP`, then normal numbers - /// ≥ 0.5 × 2x. + /// This corresponds to the exact minimum possible *normal* power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all normal numbers representable by this type are + /// greater than or equal to 0.5 × 2MIN_EXP. #[unstable(feature = "f128", issue = "116909")] pub const MIN_EXP: i32 = -16_381; - /// Maximum possible power of 2 exponent. + /// One greater than the maximum possible power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MAX_EXP`, then normal numbers - /// < 1 × 2x. + /// This corresponds to the exact maximum possible power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all numbers representable by this type are + /// strictly less than 2MAX_EXP. #[unstable(feature = "f128", issue = "116909")] pub const MAX_EXP: i32 = 16_384; @@ -227,14 +233,16 @@ impl f128 { /// Not a Number (NaN). /// - /// Note that IEEE 754 doesn't define just a single NaN value; - /// a plethora of bit patterns are considered to be NaN. - /// Furthermore, the standard makes a difference - /// between a "signaling" and a "quiet" NaN, - /// and allows inspecting its "payload" (the unspecified bits in the bit pattern). - /// This constant isn't guaranteed to equal to any specific NaN bitpattern, - /// and the stability of its representation over Rust versions - /// and target platforms isn't guaranteed. + /// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are + /// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and + /// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern) + /// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more + /// info. + /// + /// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions + /// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is + /// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary. + /// The concrete bit pattern may change across Rust versions and target platforms. #[allow(clippy::eq_op)] #[rustc_diagnostic_item = "f128_nan"] #[unstable(feature = "f128", issue = "116909")] @@ -749,15 +757,7 @@ impl f128 { // #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[must_use = "this returns the result of the comparison, without modifying either input"] pub const fn maximum(self, other: f128) -> f128 { - if self > other { - self - } else if other > self { - other - } else if self == other { - if self.is_sign_positive() && other.is_sign_negative() { self } else { other } - } else { - self + other - } + intrinsics::maximumf128(self, other) } /// Returns the minimum of the two numbers, propagating NaN. @@ -790,19 +790,10 @@ impl f128 { // #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[must_use = "this returns the result of the comparison, without modifying either input"] pub const fn minimum(self, other: f128) -> f128 { - if self < other { - self - } else if other < self { - other - } else if self == other { - if self.is_sign_negative() && other.is_sign_positive() { self } else { other } - } else { - // At least one input is NaN. Use `+` to perform NaN propagation and quieting. - self + other - } + intrinsics::minimumf128(self, other) } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// This returns NaN when *either* argument is NaN or if a combination of /// +inf and -inf is provided as arguments. @@ -819,6 +810,7 @@ impl f128 { /// # } /// ``` #[inline] + #[doc(alias = "average")] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "f128", issue = "116909")] pub const fn midpoint(self, other: f128) -> f128 { @@ -901,6 +893,7 @@ impl f128 { #[inline] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] + #[allow(unnecessary_transmutes)] pub const fn to_bits(self) -> u128 { // SAFETY: `u128` is a plain old datatype so we can always transmute to it. unsafe { mem::transmute(self) } @@ -948,6 +941,7 @@ impl f128 { #[inline] #[must_use] #[unstable(feature = "f128", issue = "116909")] + #[allow(unnecessary_transmutes)] pub const fn from_bits(v: u128) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u128` is a plain old datatype so we can always transmute from it. @@ -1365,4 +1359,469 @@ impl f128 { // SAFETY: this is actually a safe intrinsic unsafe { intrinsics::copysignf128(self, sign) } } + + /// Float addition that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_add(self, rhs: f128) -> f128 { + intrinsics::fadd_algebraic(self, rhs) + } + + /// Float subtraction that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_sub(self, rhs: f128) -> f128 { + intrinsics::fsub_algebraic(self, rhs) + } + + /// Float multiplication that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_mul(self, rhs: f128) -> f128 { + intrinsics::fmul_algebraic(self, rhs) + } + + /// Float division that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_div(self, rhs: f128) -> f128 { + intrinsics::fdiv_algebraic(self, rhs) + } + + /// Float remainder that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_rem(self, rhs: f128) -> f128 { + intrinsics::frem_algebraic(self, rhs) + } +} + +// Functions in this module fall into `core_float_math` +// FIXME(f16_f128): all doctests must be gated to platforms that have `long double` === `_Float128` +// due to https://github.com/llvm/llvm-project/issues/44744. aarch64 linux matches this. +// #[unstable(feature = "core_float_math", issue = "137578")] +#[cfg(not(test))] +impl f128 { + /// Returns the largest integer less than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn floor(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf128(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.01_f128; + /// let g = 4.0_f128; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf128(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = -3.7_f128; + /// let i = 3.5_f128; + /// let j = 4.5_f128; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf128(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = 3.5_f128; + /// let i = 4.5_f128; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f128 { + intrinsics::round_ties_even_f128(self) + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf128(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let x = 3.6_f128; + /// let y = -3.6_f128; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f128::EPSILON); + /// assert!(abs_difference_y <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f128 { + self - self.trunc() + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let m = 10.0_f128; + /// let x = 4.0_f128; + /// let b = 60.0_f128; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f128 + f128::EPSILON; + /// let one_minus_eps = 1.0_f128 - f128::EPSILON; + /// let minus_one = -1.0_f128; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f128::EPSILON * f128::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f128, b: f128) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf128(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f128) -> f128 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f128::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f128) -> f128 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif128(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// let positive = 4.0_f128; + /// let negative = -4.0_f128; + /// let negative_zero = -0.0_f128; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[doc(alias = "squareRoot")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f128 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf128(self) } + } } diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index e3176cd168852..1a859f2277ff3 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -12,11 +12,11 @@ #![unstable(feature = "f16", issue = "116909")] use crate::convert::FloatToInt; -#[cfg(not(test))] -use crate::intrinsics; -use crate::mem; use crate::num::FpCategory; +#[cfg(not(test))] +use crate::num::libm; use crate::panic::const_assert; +use crate::{intrinsics, mem}; /// Basic mathematical constants. #[unstable(feature = "f16", issue = "116909")] @@ -133,7 +133,6 @@ pub mod consts { pub const LN_10: f16 = 2.30258509299404568401799145468436421_f16; } -#[cfg(not(test))] impl f16 { // FIXME(f16_f128): almost all methods in this `impl` are missing examples and a const // implementation. Add these once we can run code on all platforms and have f16/f128 in CTFE. @@ -143,6 +142,9 @@ impl f16 { pub const RADIX: u32 = 2; /// Number of significant digits in base 2. + /// + /// Note that the size of the mantissa in the bitwise representation is one + /// smaller than this since the leading 1 is not stored explicitly. #[unstable(feature = "f16", issue = "116909")] pub const MANTISSA_DIGITS: u32 = 11; @@ -192,16 +194,22 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] pub const MAX: f16 = 6.5504e+4_f16; - /// One greater than the minimum possible normal power of 2 exponent. + /// One greater than the minimum possible *normal* power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MIN_EXP`, then normal numbers - /// ≥ 0.5 × 2x. + /// This corresponds to the exact minimum possible *normal* power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all normal numbers representable by this type are + /// greater than or equal to 0.5 × 2MIN_EXP. #[unstable(feature = "f16", issue = "116909")] pub const MIN_EXP: i32 = -13; - /// Maximum possible power of 2 exponent. + /// One greater than the maximum possible power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MAX_EXP`, then normal numbers - /// < 1 × 2x. + /// This corresponds to the exact maximum possible power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all numbers representable by this type are + /// strictly less than 2MAX_EXP. #[unstable(feature = "f16", issue = "116909")] pub const MAX_EXP: i32 = 16; @@ -222,14 +230,16 @@ impl f16 { /// Not a Number (NaN). /// - /// Note that IEEE 754 doesn't define just a single NaN value; - /// a plethora of bit patterns are considered to be NaN. - /// Furthermore, the standard makes a difference - /// between a "signaling" and a "quiet" NaN, - /// and allows inspecting its "payload" (the unspecified bits in the bit pattern). - /// This constant isn't guaranteed to equal to any specific NaN bitpattern, - /// and the stability of its representation over Rust versions - /// and target platforms isn't guaranteed. + /// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are + /// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and + /// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern) + /// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more + /// info. + /// + /// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions + /// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is + /// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary. + /// The concrete bit pattern may change across Rust versions and target platforms. #[allow(clippy::eq_op)] #[rustc_diagnostic_item = "f16_nan"] #[unstable(feature = "f16", issue = "116909")] @@ -738,15 +748,7 @@ impl f16 { // #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[must_use = "this returns the result of the comparison, without modifying either input"] pub const fn maximum(self, other: f16) -> f16 { - if self > other { - self - } else if other > self { - other - } else if self == other { - if self.is_sign_positive() && other.is_sign_negative() { self } else { other } - } else { - self + other - } + intrinsics::maximumf16(self, other) } /// Returns the minimum of the two numbers, propagating NaN. @@ -778,19 +780,10 @@ impl f16 { // #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[must_use = "this returns the result of the comparison, without modifying either input"] pub const fn minimum(self, other: f16) -> f16 { - if self < other { - self - } else if other < self { - other - } else if self == other { - if self.is_sign_negative() && other.is_sign_positive() { self } else { other } - } else { - // At least one input is NaN. Use `+` to perform NaN propagation and quieting. - self + other - } + intrinsics::minimumf16(self, other) } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// This returns NaN when *either* argument is NaN or if a combination of /// +inf and -inf is provided as arguments. @@ -806,6 +799,7 @@ impl f16 { /// # } /// ``` #[inline] + #[doc(alias = "average")] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "f16", issue = "116909")] pub const fn midpoint(self, other: f16) -> f16 { @@ -889,6 +883,7 @@ impl f16 { #[inline] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] + #[allow(unnecessary_transmutes)] pub const fn to_bits(self) -> u16 { // SAFETY: `u16` is a plain old datatype so we can always transmute to it. unsafe { mem::transmute(self) } @@ -935,6 +930,7 @@ impl f16 { #[inline] #[must_use] #[unstable(feature = "f16", issue = "116909")] + #[allow(unnecessary_transmutes)] pub const fn from_bits(v: u16) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u16` is a plain old datatype so we can always transmute from it. @@ -1341,4 +1337,502 @@ impl f16 { // SAFETY: this is actually a safe intrinsic unsafe { intrinsics::copysignf16(self, sign) } } + + /// Float addition that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_add(self, rhs: f16) -> f16 { + intrinsics::fadd_algebraic(self, rhs) + } + + /// Float subtraction that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_sub(self, rhs: f16) -> f16 { + intrinsics::fsub_algebraic(self, rhs) + } + + /// Float multiplication that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_mul(self, rhs: f16) -> f16 { + intrinsics::fmul_algebraic(self, rhs) + } + + /// Float division that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_div(self, rhs: f16) -> f16 { + intrinsics::fdiv_algebraic(self, rhs) + } + + /// Float remainder that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_rem(self, rhs: f16) -> f16 { + intrinsics::frem_algebraic(self, rhs) + } +} + +// Functions in this module fall into `core_float_math` +// #[unstable(feature = "core_float_math", issue = "137578")] +#[cfg(not(test))] +impl f16 { + /// Returns the largest integer less than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn floor(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf16(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.01_f16; + /// let g = 4.0_f16; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf16(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = -3.7_f16; + /// let i = 3.5_f16; + /// let j = 4.5_f16; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf16(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = 3.5_f16; + /// let i = 4.5_f16; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f16 { + intrinsics::round_ties_even_f16(self) + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf16(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let x = 3.6_f16; + /// let y = -3.6_f16; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f16::EPSILON); + /// assert!(abs_difference_y <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f16 { + self - self.trunc() + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let m = 10.0_f16; + /// let x = 4.0_f16; + /// let b = 60.0_f16; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f16 + f16::EPSILON; + /// let one_minus_eps = 1.0_f16 - f16::EPSILON; + /// let minus_one = -1.0_f16; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f16::EPSILON * f16::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f16, b: f16) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf16(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f16) -> f16 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f16::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f16) -> f16 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif16(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let positive = 4.0_f16; + /// let negative = -4.0_f16; + /// let negative_zero = -0.0_f16; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[doc(alias = "squareRoot")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f16 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf16(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `cbrtf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// let x = 8.0f16; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(self) -> f16 { + libm::cbrtf(self as f32) as f16 + } } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index a200fd5318669..43bf414d6be32 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -12,11 +12,9 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::convert::FloatToInt; -#[cfg(not(test))] -use crate::intrinsics; -use crate::mem; use crate::num::FpCategory; use crate::panic::const_assert; +use crate::{cfg_match, intrinsics, mem}; /// The radix or base of the internal representation of `f32`. /// Use [`f32::RADIX`] instead. @@ -386,13 +384,15 @@ pub mod consts { pub const LN_10: f32 = 2.30258509299404568401799145468436421_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. + /// + /// Note that the size of the mantissa in the bitwise representation is one + /// smaller than this since the leading 1 is not stored explicitly. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MANTISSA_DIGITS: u32 = 24; @@ -416,7 +416,7 @@ impl f32 { /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon /// [`MANTISSA_DIGITS`]: f32::MANTISSA_DIGITS #[stable(feature = "assoc_int_consts", since = "1.43.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "f32_epsilon")] + #[rustc_diagnostic_item = "f32_epsilon"] pub const EPSILON: f32 = 1.19209290e-07_f32; /// Smallest finite `f32` value. @@ -443,16 +443,22 @@ impl f32 { #[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. + /// One greater than the minimum possible *normal* power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MIN_EXP`, then normal numbers - /// ≥ 0.5 × 2x. + /// This corresponds to the exact minimum possible *normal* power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all normal numbers representable by this type are + /// greater than or equal to 0.5 × 2MIN_EXP. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MIN_EXP: i32 = -125; - /// Maximum possible power of 2 exponent. + /// One greater than the maximum possible power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MAX_EXP`, then normal numbers - /// < 1 × 2x. + /// This corresponds to the exact maximum possible power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all numbers representable by this type are + /// strictly less than 2MAX_EXP. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MAX_EXP: i32 = 128; @@ -473,14 +479,16 @@ impl f32 { /// Not a Number (NaN). /// - /// Note that IEEE 754 doesn't define just a single NaN value; - /// a plethora of bit patterns are considered to be NaN. - /// Furthermore, the standard makes a difference - /// between a "signaling" and a "quiet" NaN, - /// and allows inspecting its "payload" (the unspecified bits in the bit pattern). - /// This constant isn't guaranteed to equal to any specific NaN bitpattern, - /// and the stability of its representation over Rust versions - /// and target platforms isn't guaranteed. + /// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are + /// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and + /// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern) + /// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more + /// info. + /// + /// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions + /// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is + /// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary. + /// The concrete bit pattern may change across Rust versions and target platforms. #[stable(feature = "assoc_int_consts", since = "1.43.0")] #[rustc_diagnostic_item = "f32_nan"] #[allow(clippy::eq_op)] @@ -493,13 +501,13 @@ impl f32 { pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32; /// Sign bit - const SIGN_MASK: u32 = 0x8000_0000; + pub(crate) const SIGN_MASK: u32 = 0x8000_0000; /// Exponent mask - const EXP_MASK: u32 = 0x7f80_0000; + pub(crate) const EXP_MASK: u32 = 0x7f80_0000; /// Mantissa mask - const MAN_MASK: u32 = 0x007f_ffff; + pub(crate) const MAN_MASK: u32 = 0x007f_ffff; /// Minimum representable positive value (min subnormal) const TINY_BITS: u32 = 0x1; @@ -708,8 +716,7 @@ impl f32 { 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. - // SAFETY: This is just transmuting to get the sign bit, it's fine. - unsafe { mem::transmute::(self) & 0x8000_0000 != 0 } + self.to_bits() & 0x8000_0000 != 0 } /// Returns the least number greater than `self`. @@ -937,15 +944,7 @@ impl f32 { #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] pub const fn maximum(self, other: f32) -> f32 { - if self > other { - self - } else if other > self { - other - } else if self == other { - if self.is_sign_positive() && other.is_sign_negative() { self } else { other } - } else { - self + other - } + intrinsics::maximumf32(self, other) } /// Returns the minimum of the two numbers, propagating NaN. @@ -972,19 +971,10 @@ impl f32 { #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] pub const fn minimum(self, other: f32) -> f32 { - if self < other { - self - } else if other < self { - other - } else if self == other { - if self.is_sign_negative() && other.is_sign_positive() { self } else { other } - } else { - // At least one input is NaN. Use `+` to perform NaN propagation and quieting. - self + other - } + intrinsics::minimumf32(self, other) } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// This returns NaN when *either* argument is NaN or if a combination of /// +inf and -inf is provided as arguments. @@ -996,24 +986,26 @@ impl f32 { /// assert_eq!((-5.5f32).midpoint(8.0), 1.25); /// ``` #[inline] + #[doc(alias = "average")] #[stable(feature = "num_midpoint", since = "1.85.0")] #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] pub const fn midpoint(self, other: f32) -> f32 { - cfg_if! { + cfg_match! { // Allow faster implementation that have known good 64-bit float // implementations. Falling back to the branchy code on targets that don't // have 64-bit hardware floats or buggy implementations. // https://github.com/rust-lang/rust/pull/121062#issuecomment-2123408114 - if #[cfg(any( - target_arch = "x86_64", - target_arch = "aarch64", - all(any(target_arch = "riscv32", target_arch = "riscv64"), target_feature = "d"), - all(target_arch = "arm", target_feature = "vfp2"), - target_arch = "wasm32", - target_arch = "wasm64", - ))] { + any( + target_arch = "x86_64", + target_arch = "aarch64", + all(any(target_arch = "riscv32", target_arch = "riscv64"), target_feature = "d"), + all(target_arch = "arm", target_feature = "vfp2"), + target_arch = "wasm32", + target_arch = "wasm64", + ) => { ((self as f64 + other as f64) / 2.0) as f32 - } else { + } + _ => { const LO: f32 = f32::MIN_POSITIVE * 2.; const HI: f32 = f32::MAX / 2.; @@ -1093,6 +1085,7 @@ impl f32 { #[stable(feature = "float_bits_conv", since = "1.20.0")] #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] #[inline] + #[allow(unnecessary_transmutes)] 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) } @@ -1138,6 +1131,7 @@ impl f32 { #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] #[must_use] #[inline] + #[allow(unnecessary_transmutes)] pub const fn from_bits(v: u32) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u32` is a plain old datatype so we can always transmute from it. @@ -1506,4 +1500,498 @@ impl f32 { // SAFETY: this is actually a safe intrinsic unsafe { intrinsics::copysignf32(self, sign) } } + + /// Float addition that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_add(self, rhs: f32) -> f32 { + intrinsics::fadd_algebraic(self, rhs) + } + + /// Float subtraction that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_sub(self, rhs: f32) -> f32 { + intrinsics::fsub_algebraic(self, rhs) + } + + /// Float multiplication that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_mul(self, rhs: f32) -> f32 { + intrinsics::fmul_algebraic(self, rhs) + } + + /// Float division that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_div(self, rhs: f32) -> f32 { + intrinsics::fdiv_algebraic(self, rhs) + } + + /// Float remainder that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_rem(self, rhs: f32) -> f32 { + intrinsics::frem_algebraic(self, rhs) + } +} + +/// Experimental implementations of floating point functions in `core`. +/// +/// _The standalone functions in this module are for testing only. +/// They will be stabilized as inherent methods._ +#[unstable(feature = "core_float_math", issue = "137578")] +pub mod math { + use crate::intrinsics; + use crate::num::libm; + + /// Experimental version of `floor` in `core`. See [`f32::floor`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let f = 3.7_f32; + /// let g = 3.0_f32; + /// let h = -3.7_f32; + /// + /// assert_eq!(f32::math::floor(f), 3.0); + /// assert_eq!(f32::math::floor(g), 3.0); + /// assert_eq!(f32::math::floor(h), -4.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::floor`]: ../../../std/primitive.f32.html#method.floor + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn floor(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf32(x) } + } + + /// Experimental version of `ceil` in `core`. See [`f32::ceil`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let f = 3.01_f32; + /// let g = 4.0_f32; + /// + /// assert_eq!(f32::math::ceil(f), 4.0); + /// assert_eq!(f32::math::ceil(g), 4.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::ceil`]: ../../../std/primitive.f32.html#method.ceil + #[inline] + #[doc(alias = "ceiling")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "core_float_math", issue = "137578")] + pub fn ceil(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf32(x) } + } + + /// Experimental version of `round` in `core`. See [`f32::round`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let f = 3.3_f32; + /// let g = -3.3_f32; + /// let h = -3.7_f32; + /// let i = 3.5_f32; + /// let j = 4.5_f32; + /// + /// assert_eq!(f32::math::round(f), 3.0); + /// assert_eq!(f32::math::round(g), -3.0); + /// assert_eq!(f32::math::round(h), -4.0); + /// assert_eq!(f32::math::round(i), 4.0); + /// assert_eq!(f32::math::round(j), 5.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::round`]: ../../../std/primitive.f32.html#method.round + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf32(x) } + } + + /// Experimental version of `round_ties_even` in `core`. See [`f32::round_ties_even`] for + /// details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let f = 3.3_f32; + /// let g = -3.3_f32; + /// let h = 3.5_f32; + /// let i = 4.5_f32; + /// + /// assert_eq!(f32::math::round_ties_even(f), 3.0); + /// assert_eq!(f32::math::round_ties_even(g), -3.0); + /// assert_eq!(f32::math::round_ties_even(h), 4.0); + /// assert_eq!(f32::math::round_ties_even(i), 4.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::round_ties_even`]: ../../../std/primitive.f32.html#method.round_ties_even + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(x: f32) -> f32 { + intrinsics::round_ties_even_f32(x) + } + + /// Experimental version of `trunc` in `core`. See [`f32::trunc`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let f = 3.7_f32; + /// let g = 3.0_f32; + /// let h = -3.7_f32; + /// + /// assert_eq!(f32::math::trunc(f), 3.0); + /// assert_eq!(f32::math::trunc(g), 3.0); + /// assert_eq!(f32::math::trunc(h), -3.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::trunc`]: ../../../std/primitive.f32.html#method.trunc + #[inline] + #[doc(alias = "truncate")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "core_float_math", issue = "137578")] + pub fn trunc(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf32(x) } + } + + /// Experimental version of `fract` in `core`. See [`f32::fract`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let x = 3.6_f32; + /// let y = -3.6_f32; + /// let abs_difference_x = (f32::math::fract(x) - 0.6).abs(); + /// let abs_difference_y = (f32::math::fract(y) - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f32::EPSILON); + /// assert!(abs_difference_y <= f32::EPSILON); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::fract`]: ../../../std/primitive.f32.html#method.fract + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(x: f32) -> f32 { + x - trunc(x) + } + + /// Experimental version of `mul_add` in `core`. See [`f32::mul_add`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// # // FIXME(#140515): mingw has an incorrect fma + /// # // https://sourceforge.net/p/mingw-w64/bugs/848/ + /// # #[cfg(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")))] { + /// use core::f32; + /// + /// let m = 10.0_f32; + /// let x = 4.0_f32; + /// let b = 60.0_f32; + /// + /// assert_eq!(f32::math::mul_add(m, x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f32 + f32::EPSILON; + /// let one_minus_eps = 1.0_f32 - f32::EPSILON; + /// let minus_one = -1.0_f32; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!( + /// f32::math::mul_add(one_plus_eps, one_minus_eps, minus_one), + /// -f32::EPSILON * f32::EPSILON + /// ); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::mul_add`]: ../../../std/primitive.f32.html#method.mul_add + #[inline] + #[doc(alias = "fmaf", alias = "fusedMultiplyAdd")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "core_float_math", issue = "137578")] + pub fn mul_add(x: f32, y: f32, z: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf32(x, y, z) } + } + + /// Experimental version of `div_euclid` in `core`. See [`f32::div_euclid`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let a: f32 = 7.0; + /// let b = 4.0; + /// assert_eq!(f32::math::div_euclid(a, b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!(f32::math::div_euclid(-a, b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(f32::math::div_euclid(a, -b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!(f32::math::div_euclid(-a, -b), 2.0); // -7.0 >= -4.0 * 2.0 + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::div_euclid`]: ../../../std/primitive.f32.html#method.div_euclid + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(x: f32, rhs: f32) -> f32 { + let q = trunc(x / rhs); + if x % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Experimental version of `rem_euclid` in `core`. See [`f32::rem_euclid`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let a: f32 = 7.0; + /// let b = 4.0; + /// assert_eq!(f32::math::rem_euclid(a, b), 3.0); + /// assert_eq!(f32::math::rem_euclid(-a, b), 1.0); + /// assert_eq!(f32::math::rem_euclid(a, -b), 3.0); + /// assert_eq!(f32::math::rem_euclid(-a, -b), 1.0); + /// // limitation due to round-off error + /// assert!(f32::math::rem_euclid(-f32::EPSILON, 3.0) != 0.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::rem_euclid`]: ../../../std/primitive.f32.html#method.rem_euclid + #[inline] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(x: f32, rhs: f32) -> f32 { + let r = x % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Experimental version of `powi` in `core`. See [`f32::powi`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let x = 2.0_f32; + /// let abs_difference = (f32::math::powi(x, 2) - (x * x)).abs(); + /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::math::powi(f32::NAN, 0), 1.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::powi`]: ../../../std/primitive.f32.html#method.powi + #[inline] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "core_float_math", issue = "137578")] + pub fn powi(x: f32, n: i32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif32(x, n) } + } + + /// Experimental version of `sqrt` in `core`. See [`f32::sqrt`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let positive = 4.0_f32; + /// let negative = -4.0_f32; + /// let negative_zero = -0.0_f32; + /// + /// assert_eq!(f32::math::sqrt(positive), 2.0); + /// assert!(f32::math::sqrt(negative).is_nan()); + /// assert_eq!(f32::math::sqrt(negative_zero), negative_zero); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::sqrt`]: ../../../std/primitive.f32.html#method.sqrt + #[inline] + #[doc(alias = "squareRoot")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(x: f32) -> f32 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf32(x) } + } + + /// Experimental version of `abs_sub` in `core`. See [`f32::abs_sub`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let x = 3.0f32; + /// let y = -3.0f32; + /// + /// let abs_difference_x = (f32::math::abs_sub(x, 1.0) - 2.0).abs(); + /// let abs_difference_y = (f32::math::abs_sub(y, 1.0) - 0.0).abs(); + /// + /// assert!(abs_difference_x <= f32::EPSILON); + /// assert!(abs_difference_y <= f32::EPSILON); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::abs_sub`]: ../../../std/primitive.f32.html#method.abs_sub + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + #[deprecated( + since = "1.10.0", + note = "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)." + )] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn abs_sub(x: f32, other: f32) -> f32 { + libm::fdimf(x, other) + } + + /// Experimental version of `cbrt` in `core`. See [`f32::cbrt`] for details. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and + /// can even differ within the same execution from one invocation to the next. + /// This function currently corresponds to the `cbrtf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f32; + /// + /// let x = 8.0f32; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (f32::math::cbrt(x) - 2.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f32::cbrt`]: ../../../std/primitive.f32.html#method.cbrt + #[inline] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "core_float_math", issue = "137578")] + pub fn cbrt(x: f32) -> f32 { + libm::cbrtf(x) + } } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index de63a462b61ac..8fbf2cffbaf4e 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -12,11 +12,9 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::convert::FloatToInt; -#[cfg(not(test))] -use crate::intrinsics; -use crate::mem; use crate::num::FpCategory; use crate::panic::const_assert; +use crate::{intrinsics, mem}; /// The radix or base of the internal representation of `f64`. /// Use [`f64::RADIX`] instead. @@ -386,13 +384,15 @@ pub mod consts { pub const LN_10: f64 = 2.30258509299404568401799145468436421_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. + /// + /// Note that the size of the mantissa in the bitwise representation is one + /// smaller than this since the leading 1 is not stored explicitly. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MANTISSA_DIGITS: u32 = 53; /// Approximate number of significant digits in base 10. @@ -415,7 +415,7 @@ impl f64 { /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon /// [`MANTISSA_DIGITS`]: f64::MANTISSA_DIGITS #[stable(feature = "assoc_int_consts", since = "1.43.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "f64_epsilon")] + #[rustc_diagnostic_item = "f64_epsilon"] pub const EPSILON: f64 = 2.2204460492503131e-16_f64; /// Smallest finite `f64` value. @@ -442,16 +442,22 @@ impl f64 { #[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. + /// One greater than the minimum possible *normal* power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MIN_EXP`, then normal numbers - /// ≥ 0.5 × 2x. + /// This corresponds to the exact minimum possible *normal* power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all normal numbers representable by this type are + /// greater than or equal to 0.5 × 2MIN_EXP. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MIN_EXP: i32 = -1021; - /// Maximum possible power of 2 exponent. + /// One greater than the maximum possible power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If x = `MAX_EXP`, then normal numbers - /// < 1 × 2x. + /// This corresponds to the exact maximum possible power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all numbers representable by this type are + /// strictly less than 2MAX_EXP. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MAX_EXP: i32 = 1024; @@ -472,14 +478,16 @@ impl f64 { /// Not a Number (NaN). /// - /// Note that IEEE 754 doesn't define just a single NaN value; - /// a plethora of bit patterns are considered to be NaN. - /// Furthermore, the standard makes a difference - /// between a "signaling" and a "quiet" NaN, - /// and allows inspecting its "payload" (the unspecified bits in the bit pattern). - /// This constant isn't guaranteed to equal to any specific NaN bitpattern, - /// and the stability of its representation over Rust versions - /// and target platforms isn't guaranteed. + /// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are + /// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and + /// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern) + /// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more + /// info. + /// + /// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions + /// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is + /// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary. + /// The concrete bit pattern may change across Rust versions and target platforms. #[rustc_diagnostic_item = "f64_nan"] #[stable(feature = "assoc_int_consts", since = "1.43.0")] #[allow(clippy::eq_op)] @@ -492,13 +500,13 @@ impl f64 { pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64; /// Sign bit - const SIGN_MASK: u64 = 0x8000_0000_0000_0000; + pub(crate) const SIGN_MASK: u64 = 0x8000_0000_0000_0000; /// Exponent mask - const EXP_MASK: u64 = 0x7ff0_0000_0000_0000; + pub(crate) const EXP_MASK: u64 = 0x7ff0_0000_0000_0000; /// Mantissa mask - const MAN_MASK: u64 = 0x000f_ffff_ffff_ffff; + pub(crate) const MAN_MASK: u64 = 0x000f_ffff_ffff_ffff; /// Minimum representable positive value (min subnormal) const TINY_BITS: u64 = 0x1; @@ -716,8 +724,7 @@ impl f64 { 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. - // SAFETY: This is just transmuting to get the sign bit, it's fine. - unsafe { mem::transmute::(self) & Self::SIGN_MASK != 0 } + self.to_bits() & Self::SIGN_MASK != 0 } #[must_use] @@ -955,15 +962,7 @@ impl f64 { #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] pub const fn maximum(self, other: f64) -> f64 { - if self > other { - self - } else if other > self { - other - } else if self == other { - if self.is_sign_positive() && other.is_sign_negative() { self } else { other } - } else { - self + other - } + intrinsics::maximumf64(self, other) } /// Returns the minimum of the two numbers, propagating NaN. @@ -990,19 +989,10 @@ impl f64 { #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] pub const fn minimum(self, other: f64) -> f64 { - if self < other { - self - } else if other < self { - other - } else if self == other { - if self.is_sign_negative() && other.is_sign_positive() { self } else { other } - } else { - // At least one input is NaN. Use `+` to perform NaN propagation and quieting. - self + other - } + intrinsics::minimumf64(self, other) } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// This returns NaN when *either* argument is NaN or if a combination of /// +inf and -inf is provided as arguments. @@ -1014,6 +1004,7 @@ impl f64 { /// assert_eq!((-5.5f64).midpoint(8.0), 1.25); /// ``` #[inline] + #[doc(alias = "average")] #[stable(feature = "num_midpoint", since = "1.85.0")] #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] pub const fn midpoint(self, other: f64) -> f64 { @@ -1092,6 +1083,7 @@ impl f64 { without modifying the original"] #[stable(feature = "float_bits_conv", since = "1.20.0")] #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] + #[allow(unnecessary_transmutes)] #[inline] pub const fn to_bits(self) -> u64 { // SAFETY: `u64` is a plain old datatype so we can always transmute to it. @@ -1138,6 +1130,7 @@ impl f64 { #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] #[must_use] #[inline] + #[allow(unnecessary_transmutes)] pub const fn from_bits(v: u64) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u64` is a plain old datatype so we can always transmute from it. @@ -1506,4 +1499,491 @@ impl f64 { // SAFETY: this is actually a safe intrinsic unsafe { intrinsics::copysignf64(self, sign) } } + + /// Float addition that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_add(self, rhs: f64) -> f64 { + intrinsics::fadd_algebraic(self, rhs) + } + + /// Float subtraction that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_sub(self, rhs: f64) -> f64 { + intrinsics::fsub_algebraic(self, rhs) + } + + /// Float multiplication that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_mul(self, rhs: f64) -> f64 { + intrinsics::fmul_algebraic(self, rhs) + } + + /// Float division that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_div(self, rhs: f64) -> f64 { + intrinsics::fdiv_algebraic(self, rhs) + } + + /// Float remainder that allows optimizations based on algebraic rules. + /// + /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] + #[inline] + pub const fn algebraic_rem(self, rhs: f64) -> f64 { + intrinsics::frem_algebraic(self, rhs) + } +} + +#[unstable(feature = "core_float_math", issue = "137578")] +/// Experimental implementations of floating point functions in `core`. +/// +/// _The standalone functions in this module are for testing only. +/// They will be stabilized as inherent methods._ +pub mod math { + use crate::intrinsics; + use crate::num::libm; + + /// Experimental version of `floor` in `core`. See [`f64::floor`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let f = 3.7_f64; + /// let g = 3.0_f64; + /// let h = -3.7_f64; + /// + /// assert_eq!(f64::math::floor(f), 3.0); + /// assert_eq!(f64::math::floor(g), 3.0); + /// assert_eq!(f64::math::floor(h), -4.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::floor`]: ../../../std/primitive.f64.html#method.floor + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn floor(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::floorf64(x) } + } + + /// Experimental version of `ceil` in `core`. See [`f64::ceil`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let f = 3.01_f64; + /// let g = 4.0_f64; + /// + /// assert_eq!(f64::math::ceil(f), 4.0); + /// assert_eq!(f64::math::ceil(g), 4.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::ceil`]: ../../../std/primitive.f64.html#method.ceil + #[inline] + #[doc(alias = "ceiling")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::ceilf64(x) } + } + + /// Experimental version of `round` in `core`. See [`f64::round`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let f = 3.3_f64; + /// let g = -3.3_f64; + /// let h = -3.7_f64; + /// let i = 3.5_f64; + /// let j = 4.5_f64; + /// + /// assert_eq!(f64::math::round(f), 3.0); + /// assert_eq!(f64::math::round(g), -3.0); + /// assert_eq!(f64::math::round(h), -4.0); + /// assert_eq!(f64::math::round(i), 4.0); + /// assert_eq!(f64::math::round(j), 5.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::round`]: ../../../std/primitive.f64.html#method.round + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::roundf64(x) } + } + + /// Experimental version of `round_ties_even` in `core`. See [`f64::round_ties_even`] for + /// details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let f = 3.3_f64; + /// let g = -3.3_f64; + /// let h = 3.5_f64; + /// let i = 4.5_f64; + /// + /// assert_eq!(f64::math::round_ties_even(f), 3.0); + /// assert_eq!(f64::math::round_ties_even(g), -3.0); + /// assert_eq!(f64::math::round_ties_even(h), 4.0); + /// assert_eq!(f64::math::round_ties_even(i), 4.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::round_ties_even`]: ../../../std/primitive.f64.html#method.round_ties_even + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(x: f64) -> f64 { + intrinsics::round_ties_even_f64(x) + } + + /// Experimental version of `trunc` in `core`. See [`f64::trunc`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let f = 3.7_f64; + /// let g = 3.0_f64; + /// let h = -3.7_f64; + /// + /// assert_eq!(f64::math::trunc(f), 3.0); + /// assert_eq!(f64::math::trunc(g), 3.0); + /// assert_eq!(f64::math::trunc(h), -3.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::trunc`]: ../../../std/primitive.f64.html#method.trunc + #[inline] + #[doc(alias = "truncate")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::truncf64(x) } + } + + /// Experimental version of `fract` in `core`. See [`f64::fract`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let x = 3.6_f64; + /// let y = -3.6_f64; + /// let abs_difference_x = (f64::math::fract(x) - 0.6).abs(); + /// let abs_difference_y = (f64::math::fract(y) - (-0.6)).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::fract`]: ../../../std/primitive.f64.html#method.fract + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(x: f64) -> f64 { + x - trunc(x) + } + + /// Experimental version of `mul_add` in `core`. See [`f64::mul_add`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// # // FIXME(#140515): mingw has an incorrect fma + /// # // https://sourceforge.net/p/mingw-w64/bugs/848/ + /// # #[cfg(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")))] { + /// use core::f64; + /// + /// let m = 10.0_f64; + /// let x = 4.0_f64; + /// let b = 60.0_f64; + /// + /// assert_eq!(f64::math::mul_add(m, x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f64 + f64::EPSILON; + /// let one_minus_eps = 1.0_f64 - f64::EPSILON; + /// let minus_one = -1.0_f64; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!( + /// f64::math::mul_add(one_plus_eps, one_minus_eps, minus_one), + /// -f64::EPSILON * f64::EPSILON + /// ); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::mul_add`]: ../../../std/primitive.f64.html#method.mul_add + #[inline] + #[doc(alias = "fma", alias = "fusedMultiplyAdd")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(x: f64, a: f64, b: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::fmaf64(x, a, b) } + } + + /// Experimental version of `div_euclid` in `core`. See [`f64::div_euclid`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let a: f64 = 7.0; + /// let b = 4.0; + /// assert_eq!(f64::math::div_euclid(a, b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!(f64::math::div_euclid(-a, b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(f64::math::div_euclid(a, -b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!(f64::math::div_euclid(-a, -b), 2.0); // -7.0 >= -4.0 * 2.0 + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::div_euclid`]: ../../../std/primitive.f64.html#method.div_euclid + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(x: f64, rhs: f64) -> f64 { + let q = trunc(x / rhs); + if x % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Experimental version of `rem_euclid` in `core`. See [`f64::rem_euclid`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let a: f64 = 7.0; + /// let b = 4.0; + /// assert_eq!(f64::math::rem_euclid(a, b), 3.0); + /// assert_eq!(f64::math::rem_euclid(-a, b), 1.0); + /// assert_eq!(f64::math::rem_euclid(a, -b), 3.0); + /// assert_eq!(f64::math::rem_euclid(-a, -b), 1.0); + /// // limitation due to round-off error + /// assert!(f64::math::rem_euclid(-f64::EPSILON, 3.0) != 0.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::rem_euclid`]: ../../../std/primitive.f64.html#method.rem_euclid + #[inline] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(x: f64, rhs: f64) -> f64 { + let r = x % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Experimental version of `powi` in `core`. See [`f64::powi`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let x = 2.0_f64; + /// let abs_difference = (f64::math::powi(x, 2) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); + /// + /// assert_eq!(f64::math::powi(f64::NAN, 0), 1.0); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::powi`]: ../../../std/primitive.f64.html#method.powi + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(x: f64, n: i32) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::powif64(x, n) } + } + + /// Experimental version of `sqrt` in `core`. See [`f64::sqrt`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let positive = 4.0_f64; + /// let negative = -4.0_f64; + /// let negative_zero = -0.0_f64; + /// + /// assert_eq!(f64::math::sqrt(positive), 2.0); + /// assert!(f64::math::sqrt(negative).is_nan()); + /// assert_eq!(f64::math::sqrt(negative_zero), negative_zero); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::sqrt`]: ../../../std/primitive.f64.html#method.sqrt + #[inline] + #[doc(alias = "squareRoot")] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(x: f64) -> f64 { + // SAFETY: intrinsic with no preconditions + unsafe { intrinsics::sqrtf64(x) } + } + + /// Experimental version of `abs_sub` in `core`. See [`f64::abs_sub`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let x = 3.0_f64; + /// let y = -3.0_f64; + /// + /// let abs_difference_x = (f64::math::abs_sub(x, 1.0) - 2.0).abs(); + /// let abs_difference_y = (f64::math::abs_sub(y, 1.0) - 0.0).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::abs_sub`]: ../../../std/primitive.f64.html#method.abs_sub + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[deprecated( + since = "1.10.0", + note = "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)." + )] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn abs_sub(x: f64, other: f64) -> f64 { + libm::fdim(x, other) + } + + /// Experimental version of `cbrt` in `core`. See [`f64::cbrt`] for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_float_math)] + /// + /// use core::f64; + /// + /// let x = 8.0_f64; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (f64::math::cbrt(x) - 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + /// + /// _This standalone function is for testing only. + /// It will be stabilized as an inherent method._ + /// + /// [`f64::cbrt`]: ../../../std/primitive.f64.html#method.cbrt + #[inline] + #[unstable(feature = "core_float_math", issue = "137578")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(x: f64) -> f64 { + libm::cbrt(x) + } } diff --git a/library/core/src/num/flt2dec/decoder.rs b/library/core/src/num/flt2dec/decoder.rs index 40b3aae24a536..bd6e2cdbafec8 100644 --- a/library/core/src/num/flt2dec/decoder.rs +++ b/library/core/src/num/flt2dec/decoder.rs @@ -45,6 +45,13 @@ pub trait DecodableFloat: RawFloat + Copy { fn min_pos_norm_value() -> Self; } +#[cfg(target_has_reliable_f16)] +impl DecodableFloat for f16 { + fn min_pos_norm_value() -> Self { + f16::MIN_POSITIVE + } +} + impl DecodableFloat for f32 { fn min_pos_norm_value() -> Self { f32::MIN_POSITIVE diff --git a/library/core/src/num/flt2dec/strategy/grisu.rs b/library/core/src/num/flt2dec/strategy/grisu.rs index 2816de4c63339..d3bbb0934e0ff 100644 --- a/library/core/src/num/flt2dec/strategy/grisu.rs +++ b/library/core/src/num/flt2dec/strategy/grisu.rs @@ -196,9 +196,9 @@ pub fn format_shortest_opt<'a>( 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); + 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); @@ -480,7 +480,7 @@ pub fn format_exact_opt<'a>( // 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); + let v = v.mul(cached); // divide `v` into integral and fractional parts. let e = -v.e as usize; diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 7d99aaa173143..84e1482ed31db 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -244,8 +244,8 @@ macro_rules! int_impl { /// #[doc = concat!("assert_eq!(n.cast_unsigned(), ", stringify!($UnsignedT), "::MAX);")] /// ``` - #[stable(feature = "integer_sign_cast", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "integer_sign_cast", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "integer_sign_cast", since = "1.87.0")] + #[rustc_const_stable(feature = "integer_sign_cast", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1355,8 +1355,8 @@ macro_rules! int_impl { #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(4), 0x10);")] #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(129), 0);")] /// ``` - #[stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unbounded_shifts", since = "1.87.0")] + #[rustc_const_stable(feature = "unbounded_shifts", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1478,8 +1478,8 @@ macro_rules! int_impl { #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(129), 0);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.unbounded_shr(129), -1);")] /// ``` - #[stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unbounded_shifts", since = "1.87.0")] + #[rustc_const_stable(feature = "unbounded_shifts", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3571,10 +3571,7 @@ macro_rules! int_impl { // so delegate it to `Ord` which is already producing -1/0/+1 // exactly like we need and can be the place to deal with the complexity. - // FIXME(const-hack): replace with cmp - if self < 0 { -1 } - else if self == 0 { 0 } - else { 1 } + crate::intrinsics::three_way_compare(self, 0) as Self } /// Returns `true` if `self` is positive and `false` if the number is zero or @@ -3627,7 +3624,7 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { + pub const fn to_be_bytes(self) -> [u8; size_of::()] { self.to_be().to_ne_bytes() } @@ -3647,7 +3644,7 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { + pub const fn to_le_bytes(self) -> [u8; size_of::()] { self.to_le().to_ne_bytes() } @@ -3678,12 +3675,13 @@ macro_rules! int_impl { /// ``` #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[allow(unnecessary_transmutes)] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute them to arrays of bytes #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + pub const fn to_ne_bytes(self) -> [u8; size_of::()] { // SAFETY: integers are plain old datatypes so we can always transmute them to // arrays of bytes unsafe { mem::transmute(self) } @@ -3705,7 +3703,7 @@ macro_rules! int_impl { /// /// ``` #[doc = concat!("fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] - #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(size_of::<", stringify!($SelfT), ">());")] /// *input = rest; #[doc = concat!(" ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap())")] /// } @@ -3714,7 +3712,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] #[must_use] #[inline] - pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { + pub const fn from_be_bytes(bytes: [u8; size_of::()]) -> Self { Self::from_be(Self::from_ne_bytes(bytes)) } @@ -3734,7 +3732,7 @@ macro_rules! int_impl { /// /// ``` #[doc = concat!("fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] - #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(size_of::<", stringify!($SelfT), ">());")] /// *input = rest; #[doc = concat!(" ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap())")] /// } @@ -3743,7 +3741,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] #[must_use] #[inline] - pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { + pub const fn from_le_bytes(bytes: [u8; size_of::()]) -> Self { Self::from_le(Self::from_ne_bytes(bytes)) } @@ -3774,18 +3772,19 @@ macro_rules! int_impl { /// /// ``` #[doc = concat!("fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] - #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(size_of::<", stringify!($SelfT), ">());")] /// *input = rest; #[doc = concat!(" ", 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")] + #[allow(unnecessary_transmutes)] #[must_use] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute to them #[inline] - pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + pub const fn from_ne_bytes(bytes: [u8; size_of::()]) -> Self { // SAFETY: integers are plain old datatypes so we can always transmute to them unsafe { mem::transmute(bytes) } } diff --git a/library/core/src/num/libm.rs b/library/core/src/num/libm.rs new file mode 100644 index 0000000000000..aeabb08723095 --- /dev/null +++ b/library/core/src/num/libm.rs @@ -0,0 +1,11 @@ +//! Bindings to math functions provided by the system `libm` or by the `libm` crate, exposed +//! via `compiler-builtins`. + +// SAFETY: These symbols have standard interfaces in C and are defined by `libm`, or are +// provided by `compiler-builtins` on unsupported platforms. +unsafe extern "C" { + pub(crate) safe fn cbrt(n: f64) -> f64; + pub(crate) safe fn cbrtf(n: f32) -> f32; + pub(crate) safe fn fdim(a: f64, b: f64) -> f64; + pub(crate) safe fn fdimf(a: f32, b: f32) -> f32; +} diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 80a38a6013dd0..5c73bddbef2d1 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -46,6 +46,7 @@ mod uint_macros; // import uint_impl! mod error; mod int_log10; mod int_sqrt; +pub(crate) mod libm; mod nonzero; mod overflow_panic; mod saturating; @@ -99,8 +100,8 @@ macro_rules! i8_xe_bytes_doc { **Note**: This function is meaningless on `i8`. Byte order does not exist as a concept for byte-sized integers. This function is only provided in symmetry -with larger integer types. You can cast from and to `u8` using `as i8` and `as -u8`. +with larger integer types. You can cast from and to `u8` using +[`cast_signed`](u8::cast_signed) and [`cast_unsigned`](Self::cast_unsigned). " }; @@ -130,7 +131,7 @@ depending on the target pointer size. macro_rules! midpoint_impl { ($SelfT:ty, unsigned) => { - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a /// sufficiently-large unsigned integral type. This implies that the result is @@ -146,6 +147,8 @@ macro_rules! midpoint_impl { #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: $SelfT) -> $SelfT { // Use the well known branchless algorithm from Hacker's Delight to compute @@ -154,7 +157,7 @@ macro_rules! midpoint_impl { } }; ($SelfT:ty, signed) => { - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a /// sufficiently-large signed integral type. This implies that the result is @@ -169,10 +172,13 @@ macro_rules! midpoint_impl { #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(-7), -3);")] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(7), 3);")] /// ``` - #[stable(feature = "num_midpoint_signed", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "num_midpoint_signed", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "num_midpoint_signed", since = "1.87.0")] + #[rustc_const_stable(feature = "num_midpoint_signed", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average_ceil")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: Self) -> Self { // Use the well known branchless algorithm from Hacker's Delight to compute @@ -184,7 +190,7 @@ macro_rules! midpoint_impl { } }; ($SelfT:ty, $WideT:ty, unsigned) => { - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a /// sufficiently-large unsigned integral type. This implies that the result is @@ -200,13 +206,15 @@ macro_rules! midpoint_impl { #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: $SelfT) -> $SelfT { ((self as $WideT + rhs as $WideT) / 2) as $SelfT } }; ($SelfT:ty, $WideT:ty, signed) => { - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a /// sufficiently-large signed integral type. This implies that the result is @@ -221,10 +229,13 @@ macro_rules! midpoint_impl { #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(-7), -3);")] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(7), 3);")] /// ``` - #[stable(feature = "num_midpoint_signed", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "num_midpoint_signed", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "num_midpoint_signed", since = "1.87.0")] + #[rustc_const_stable(feature = "num_midpoint_signed", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average_ceil")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: $SelfT) -> $SelfT { ((self as $WideT + rhs as $WideT) / 2) as $SelfT @@ -482,6 +493,26 @@ impl u8 { ascii::Char::from_u8(*self) } + /// Converts this byte to an [ASCII character](ascii::Char), without + /// checking whether or not it's valid. + /// + /// # Safety + /// + /// This byte must be valid ASCII, or else this is UB. + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const unsafe fn as_ascii_unchecked(&self) -> ascii::Char { + assert_unsafe_precondition!( + check_library_ub, + "as_ascii_unchecked requires that the byte is valid ASCII", + (it: &u8 = self) => it.is_ascii() + ); + + // SAFETY: the caller promised that this byte is ASCII. + unsafe { ascii::Char::from_u8_unchecked(*self) } + } + /// Makes a copy of the value in its ASCII upper case equivalent. /// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', @@ -1241,7 +1272,7 @@ impl usize { /// Returns an `usize` where every byte is equal to `x`. #[inline] pub(crate) const fn repeat_u8(x: u8) -> usize { - usize::from_ne_bytes([x; mem::size_of::()]) + usize::from_ne_bytes([x; size_of::()]) } /// Returns an `usize` where every byte pair is equal to `x`. @@ -1249,7 +1280,7 @@ impl usize { pub(crate) const fn repeat_u16(x: u16) -> usize { let mut r = 0usize; let mut i = 0; - while i < mem::size_of::() { + while i < size_of::() { // Use `wrapping_shl` to make it work on targets with 16-bit `usize` r = r.wrapping_shl(16) | (x as usize); i += 2; @@ -1330,7 +1361,7 @@ pub enum FpCategory { #[inline(always)] #[unstable(issue = "none", feature = "std_internals")] pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { - radix <= 16 && digits.len() <= mem::size_of::() * 2 - is_signed_ty as usize + radix <= 16 && digits.len() <= size_of::() * 2 - is_signed_ty as usize } #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index a967b72c4fa9b..8a8b2733d5e88 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1,6 +1,7 @@ //! Definitions of integer that is known not to equal zero. use super::{IntErrorKind, ParseIntError}; +use crate::clone::UseCloned; use crate::cmp::Ordering; use crate::hash::{Hash, Hasher}; use crate::marker::{Freeze, StructuralPartialEq}; @@ -86,7 +87,7 @@ impl_zeroable_primitive!( /// For example, `Option>` is the same size as `u32`: /// /// ``` -/// use core::{mem::size_of, num::NonZero}; +/// use core::{num::NonZero}; /// /// assert_eq!(size_of::>>(), size_of::()); /// ``` @@ -102,7 +103,6 @@ impl_zeroable_primitive!( /// `Option>` are guaranteed to have the same size and alignment: /// /// ``` -/// # use std::mem::{size_of, align_of}; /// use std::num::NonZero; /// /// assert_eq!(size_of::>(), size_of::>>()); @@ -183,6 +183,9 @@ where } } +#[unstable(feature = "ergonomic_clones", issue = "132290")] +impl UseCloned for NonZero where T: ZeroablePrimitive {} + #[stable(feature = "nonzero", since = "1.28.0")] impl Copy for NonZero where T: ZeroablePrimitive {} @@ -500,7 +503,6 @@ macro_rules! nonzero_integer { #[doc = concat!("For example, `Option<", stringify!($Ty), ">` is the same size as `", stringify!($Int), "`:")] /// /// ```rust - /// use std::mem::size_of; #[doc = concat!("assert_eq!(size_of::>(), size_of::<", stringify!($Int), ">());")] /// ``` /// @@ -516,7 +518,6 @@ macro_rules! nonzero_integer { /// are guaranteed to have the same size and alignment: /// /// ``` - /// # use std::mem::{size_of, align_of}; #[doc = concat!("use std::num::", stringify!($Ty), ";")] /// #[doc = concat!("assert_eq!(size_of::<", stringify!($Ty), ">(), size_of::>());")] @@ -1588,7 +1589,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { super::int_log10::$Int(self.get()) } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) >> 1` as if it were performed in a /// sufficiently-large signed integral type. This implies that the result is @@ -1614,6 +1615,8 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: Self) -> Self { // SAFETY: The only way to get `0` with midpoint is to have two opposite or @@ -1703,8 +1706,8 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// #[doc = concat!("assert_eq!(n.cast_signed(), NonZero::new(-1", stringify!($Sint), ").unwrap());")] /// ``` - #[stable(feature = "integer_sign_cast", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "integer_sign_cast", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "integer_sign_cast", since = "1.87.0")] + #[rustc_const_stable(feature = "integer_sign_cast", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -2142,8 +2145,8 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// #[doc = concat!("assert_eq!(n.cast_unsigned(), NonZero::<", stringify!($Uint), ">::MAX);")] /// ``` - #[stable(feature = "integer_sign_cast", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "integer_sign_cast", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "integer_sign_cast", since = "1.87.0")] + #[rustc_const_stable(feature = "integer_sign_cast", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 405c71121caad..f38d809c1544b 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -273,8 +273,8 @@ macro_rules! uint_impl { /// #[doc = concat!("assert_eq!(n.cast_signed(), -1", stringify!($SignedT), ");")] /// ``` - #[stable(feature = "integer_sign_cast", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "integer_sign_cast", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "integer_sign_cast", since = "1.87.0")] + #[rustc_const_stable(feature = "integer_sign_cast", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1616,8 +1616,8 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(4), 0x10);")] #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(129), 0);")] /// ``` - #[stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unbounded_shifts", since = "1.87.0")] + #[rustc_const_stable(feature = "unbounded_shifts", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1737,8 +1737,8 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(4), 0x1);")] #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(129), 0);")] /// ``` - #[stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unbounded_shifts", since = "1.87.0")] + #[rustc_const_stable(feature = "unbounded_shifts", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2533,15 +2533,20 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!((diff1, diff0), (3, ", stringify!($SelfT), "::MAX));")] /// ``` #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn borrowing_sub(self, rhs: Self, borrow: bool) -> (Self, bool) { // note: longer-term this should be done via an intrinsic, but this has been shown // to generate optimal code for now, and LLVM doesn't have an equivalent intrinsic - let (a, b) = self.overflowing_sub(rhs); - let (c, d) = a.overflowing_sub(borrow as $SelfT); - (c, b | d) + let (a, c1) = self.overflowing_sub(rhs); + let (b, c2) = a.overflowing_sub(borrow as $SelfT); + // SAFETY: Only one of `c1` and `c2` can be set. + // For c1 to be set we need to have underflowed, but if we did then + // `a` is nonzero, which means that `c2` cannot possibly + // underflow because it's subtracting at most `1` (since it came from `bool`) + (b, unsafe { intrinsics::disjoint_bitor(c1, c2) }) } /// Calculates `self` - `rhs` with a signed `rhs` @@ -2586,7 +2591,7 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn abs_diff(self, other: Self) -> Self { - if mem::size_of::() == 1 { + if size_of::() == 1 { // Trick LLVM into generating the psadbw instruction when SSE2 // is available and this function is autovectorized for u8's. (self as i32).wrapping_sub(other as i32).abs() as Self @@ -3326,8 +3331,8 @@ macro_rules! uint_impl { #[doc = concat!("assert!(0_", stringify!($SelfT), ".is_multiple_of(0));")] #[doc = concat!("assert!(!6_", stringify!($SelfT), ".is_multiple_of(0));")] /// ``` - #[stable(feature = "unsigned_is_multiple_of", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unsigned_is_multiple_of", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unsigned_is_multiple_of", since = "1.87.0")] + #[rustc_const_stable(feature = "unsigned_is_multiple_of", since = "1.87.0")] #[must_use] #[inline] #[rustc_inherit_overflow_checks] @@ -3338,7 +3343,7 @@ macro_rules! uint_impl { } } - /// Returns `true` if and only if `self == 2^k` for some `k`. + /// Returns `true` if and only if `self == 2^k` for some unsigned integer `k`. /// /// # Examples /// @@ -3465,7 +3470,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { + pub const fn to_be_bytes(self) -> [u8; size_of::()] { self.to_be().to_ne_bytes() } @@ -3485,7 +3490,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { + pub const fn to_le_bytes(self) -> [u8; size_of::()] { self.to_le().to_ne_bytes() } @@ -3518,10 +3523,11 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[allow(unnecessary_transmutes)] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute them to arrays of bytes #[inline] - pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + pub const fn to_ne_bytes(self) -> [u8; size_of::()] { // SAFETY: integers are plain old datatypes so we can always transmute them to // arrays of bytes unsafe { mem::transmute(self) } @@ -3543,7 +3549,7 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] - #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(size_of::<", stringify!($SelfT), ">());")] /// *input = rest; #[doc = concat!(" ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap())")] /// } @@ -3552,7 +3558,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] #[must_use] #[inline] - pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { + pub const fn from_be_bytes(bytes: [u8; size_of::()]) -> Self { Self::from_be(Self::from_ne_bytes(bytes)) } @@ -3572,7 +3578,7 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] - #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(size_of::<", stringify!($SelfT), ">());")] /// *input = rest; #[doc = concat!(" ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap())")] /// } @@ -3581,7 +3587,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] #[must_use] #[inline] - pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { + pub const fn from_le_bytes(bytes: [u8; size_of::()]) -> Self { Self::from_le(Self::from_ne_bytes(bytes)) } @@ -3612,18 +3618,19 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] - #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(size_of::<", stringify!($SelfT), ">());")] /// *input = rest; #[doc = concat!(" ", 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")] + #[allow(unnecessary_transmutes)] #[must_use] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute to them #[inline] - pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + pub const fn from_ne_bytes(bytes: [u8; size_of::()]) -> Self { // SAFETY: integers are plain old datatypes so we can always transmute to them unsafe { mem::transmute(bytes) } } diff --git a/library/core/src/ops/bit.rs b/library/core/src/ops/bit.rs index 6984100e498e8..deb54c8ba348e 100644 --- a/library/core/src/ops/bit.rs +++ b/library/core/src/ops/bit.rs @@ -493,7 +493,7 @@ macro_rules! shl_impl_all { )*) } -shl_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 isize i128 } +shl_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } /// The right shift operator `>>`. Note that because this trait is implemented /// for all integer types with multiple right-hand-side types, Rust's type diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index 8993e14fcd379..ef7e6f9c2f491 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -79,7 +79,8 @@ use crate::{convert, ops}; /// [`Break`]: ControlFlow::Break /// [`Continue`]: ControlFlow::Continue #[stable(feature = "control_flow_enum_type", since = "1.55.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "ControlFlow")] +#[rustc_diagnostic_item = "ControlFlow"] +#[must_use] // ControlFlow should not implement PartialOrd or Ord, per RFC 3058: // https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#traits-for-controlflow #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index e024b7fb4d301..5d040804a8d1c 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -240,10 +240,3 @@ pub trait Drop { #[stable(feature = "rust1", since = "1.0.0")] fn drop(&mut self); } - -/// Fallback function to call surface level `Drop::drop` function -#[allow(drop_bounds)] -#[lang = "fallback_surface_drop"] -pub(crate) fn fallback_surface_drop(x: &mut T) { - ::drop(x) -} diff --git a/library/core/src/ops/index.rs b/library/core/src/ops/index.rs index 37d9a28fb99c0..46e19bed43aba 100644 --- a/library/core/src/ops/index.rs +++ b/library/core/src/ops/index.rs @@ -67,6 +67,7 @@ pub trait Index { /// /// May panic if the index is out of bounds. #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_no_implicit_autorefs] #[track_caller] fn index(&self, index: Idx) -> &Self::Output; } @@ -171,6 +172,7 @@ pub trait IndexMut: Index { /// /// May panic if the index is out of bounds. #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_no_implicit_autorefs] #[track_caller] fn index_mut(&mut self, index: Idx) -> &mut Self::Output; } diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index b82184b15b2f5..c645c996eb707 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -1,5 +1,6 @@ use crate::iter::{FusedIterator, TrustedLen}; use crate::num::NonZero; +use crate::ops::{NeverShortCircuit, Try}; use crate::ub_checks; /// Like a `Range`, but with a safety invariant that `start <= end`. @@ -112,6 +113,12 @@ impl IndexRange { self.end = mid; suffix } + + #[inline] + fn assume_range(&self) { + // SAFETY: This is the type invariant + unsafe { crate::hint::assert_unchecked(self.start <= self.end) } + } } impl Iterator for IndexRange { @@ -138,6 +145,30 @@ impl Iterator for IndexRange { let taken = self.take_prefix(n); NonZero::new(n - taken.len()).map_or(Ok(()), Err) } + + #[inline] + fn fold B>(mut self, init: B, f: F) -> B { + self.try_fold(init, NeverShortCircuit::wrap_mut_2(f)).0 + } + + #[inline] + fn try_fold(&mut self, mut accum: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + // `Range` needs to check `start < end`, but thanks to our type invariant + // we can loop on the stricter `start != end`. + + self.assume_range(); + while self.start != self.end { + // SAFETY: We just checked that the range is non-empty + let i = unsafe { self.next_unchecked() }; + accum = f(accum, i)?; + } + try { accum } + } } impl DoubleEndedIterator for IndexRange { @@ -156,6 +187,30 @@ impl DoubleEndedIterator for IndexRange { let taken = self.take_suffix(n); NonZero::new(n - taken.len()).map_or(Ok(()), Err) } + + #[inline] + fn rfold B>(mut self, init: B, f: F) -> B { + self.try_rfold(init, NeverShortCircuit::wrap_mut_2(f)).0 + } + + #[inline] + fn try_rfold(&mut self, mut accum: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + // `Range` needs to check `start < end`, but thanks to our type invariant + // we can loop on the stricter `start != end`. + + self.assume_range(); + while self.start != self.end { + // SAFETY: We just checked that the range is non-empty + let i = unsafe { self.next_back_unchecked() }; + accum = f(accum, i)?; + } + try { accum } + } } impl ExactSizeIterator for IndexRange { diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 627a875d9f724..1658f0e5a3692 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -176,7 +176,6 @@ pub use self::deref::Receiver; pub use self::deref::{Deref, DerefMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::drop::Drop; -pub(crate) use self::drop::fallback_surface_drop; #[stable(feature = "rust1", since = "1.0.0")] pub use self::function::{Fn, FnMut, FnOnce}; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/option.rs b/library/core/src/option.rs index a9f06b92ad5dd..aed5a043c11a3 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -162,8 +162,14 @@ //! The [`is_some`] and [`is_none`] methods return [`true`] if the [`Option`] //! is [`Some`] or [`None`], respectively. //! +//! The [`is_some_and`] and [`is_none_or`] methods apply the provided function +//! to the contents of the [`Option`] to produce a boolean value. +//! If this is [`None`] then a default result is returned instead without executing the function. +//! //! [`is_none`]: Option::is_none //! [`is_some`]: Option::is_some +//! [`is_some_and`]: Option::is_some_and +//! [`is_none_or`]: Option::is_none_or //! //! ## Adapters for working with references //! @@ -177,6 +183,10 @@ //! [Option]<[Pin]<[&]T>> //! * [`as_pin_mut`] converts from [Pin]<[&mut] [Option]\> to //! [Option]<[Pin]<[&mut] T>> +//! * [`as_slice`] returns a one-element slice of the contained value, if any. +//! If this is [`None`], an empty slice is returned. +//! * [`as_mut_slice`] returns a mutable one-element slice of the contained value, if any. +//! If this is [`None`], an empty slice is returned. //! //! [&]: reference "shared reference" //! [&mut]: reference "mutable reference" @@ -187,6 +197,8 @@ //! [`as_pin_mut`]: Option::as_pin_mut //! [`as_pin_ref`]: Option::as_pin_ref //! [`as_ref`]: Option::as_ref +//! [`as_slice`]: Option::as_slice +//! [`as_mut_slice`]: Option::as_mut_slice //! //! ## Extracting the contained value //! @@ -200,12 +212,15 @@ //! (which must implement the [`Default`] trait) //! * [`unwrap_or_else`] returns the result of evaluating the provided //! function +//! * [`unwrap_unchecked`] produces *[undefined behavior]* //! //! [`expect`]: Option::expect //! [`unwrap`]: Option::unwrap //! [`unwrap_or`]: Option::unwrap_or //! [`unwrap_or_default`]: Option::unwrap_or_default //! [`unwrap_or_else`]: Option::unwrap_or_else +//! [`unwrap_unchecked`]: Option::unwrap_unchecked +//! [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html //! //! ## Transforming contained values //! @@ -230,8 +245,9 @@ //! * [`filter`] calls the provided predicate function on the contained //! value `t` if the [`Option`] is [`Some(t)`], and returns [`Some(t)`] //! if the function returns `true`; otherwise, returns [`None`] -//! * [`flatten`] removes one level of nesting from an -//! [`Option>`] +//! * [`flatten`] removes one level of nesting from an [`Option>`] +//! * [`inspect`] method takes ownership of the [`Option`] and applies +//! the provided function to the contained value by reference if [`Some`] //! * [`map`] transforms [`Option`] to [`Option`] by applying the //! provided function to the contained value of [`Some`] and leaving //! [`None`] values unchanged @@ -239,6 +255,7 @@ //! [`Some(t)`]: Some //! [`filter`]: Option::filter //! [`flatten`]: Option::flatten +//! [`inspect`]: Option::inspect //! [`map`]: Option::map //! //! These methods transform [`Option`] to a value of a possibly @@ -621,6 +638,10 @@ impl Option { /// /// let x: Option = None; /// assert_eq!(x.is_some_and(|x| x > 1), false); + /// + /// let x: Option = Some("ownership".to_string()); + /// assert_eq!(x.as_ref().is_some_and(|x| x.len() > 1), true); + /// println!("still alive {:?}", x); /// ``` #[must_use] #[inline] @@ -665,6 +686,10 @@ impl Option { /// /// let x: Option = None; /// assert_eq!(x.is_none_or(|x| x > 1), true); + /// + /// let x: Option = Some("ownership".to_string()); + /// assert_eq!(x.as_ref().is_none_or(|x| x.len() > 1), true); + /// println!("still alive {:?}", x); /// ``` #[must_use] #[inline] @@ -924,7 +949,7 @@ impl Option { #[inline] #[track_caller] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "option_expect")] + #[rustc_diagnostic_item = "option_expect"] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] #[rustc_const_stable(feature = "const_option", since = "1.83.0")] pub const fn expect(self, msg: &str) -> T { @@ -969,7 +994,7 @@ impl Option { #[inline(always)] #[track_caller] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "option_unwrap")] + #[rustc_diagnostic_item = "option_unwrap"] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] #[rustc_const_stable(feature = "const_option", since = "1.83.0")] pub const fn unwrap(self) -> T { @@ -2050,6 +2075,9 @@ where } } +#[unstable(feature = "ergonomic_clones", issue = "132290")] +impl crate::clone::UseCloned for Option where T: crate::clone::UseCloned {} + #[stable(feature = "rust1", since = "1.0.0")] impl Default for Option { /// Returns [`None`][Option::None]. diff --git a/library/core/src/panic/unwind_safe.rs b/library/core/src/panic/unwind_safe.rs index 37859212c0ee3..a60f0799c0eae 100644 --- a/library/core/src/panic/unwind_safe.rs +++ b/library/core/src/panic/unwind_safe.rs @@ -82,7 +82,7 @@ use crate::task::{Context, Poll}; /// [`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")] -#[cfg_attr(not(test), rustc_diagnostic_item = "unwind_safe_trait")] +#[rustc_diagnostic_item = "unwind_safe_trait"] #[diagnostic::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" @@ -98,7 +98,7 @@ pub auto trait UnwindSafe {} /// 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")] -#[cfg_attr(not(test), rustc_diagnostic_item = "ref_unwind_safe_trait")] +#[rustc_diagnostic_item = "ref_unwind_safe_trait"] #[diagnostic::on_unimplemented( message = "the type `{Self}` may contain interior mutability and a reference may not be safely \ transferrable across a catch_unwind boundary", diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 33ad59916e391..d87f4814f0218 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -155,30 +155,26 @@ pub const fn panic(expr: &'static str) -> ! { // reducing binary size impact. macro_rules! panic_const { ($($lang:ident = $message:expr,)+) => { - pub mod panic_const { - use super::*; - - $( - /// This is a panic called with a message that's a result of a MIR-produced Assert. - // - // never inline unless panic_immediate_abort to avoid code - // bloat at the call sites as much as possible - #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] - #[cfg_attr(feature = "panic_immediate_abort", inline)] - #[track_caller] - #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable - #[lang = stringify!($lang)] - pub const fn $lang() -> ! { - // Use Arguments::new_const instead of format_args!("{expr}") to potentially - // reduce size overhead. The format_args! macro uses str's Display trait to - // write expr, which calls Formatter::pad, which must accommodate string - // truncation and padding (even though none is used here). Using - // Arguments::new_const may allow the compiler to omit Formatter::pad from the - // output binary, saving up to a few kilobytes. - panic_fmt(fmt::Arguments::new_const(&[$message])); - } - )+ - } + $( + /// This is a panic called with a message that's a result of a MIR-produced Assert. + // + // never inline unless panic_immediate_abort to avoid code + // bloat at the call sites as much as possible + #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] + #[cfg_attr(feature = "panic_immediate_abort", inline)] + #[track_caller] + #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable + #[lang = stringify!($lang)] + pub const fn $lang() -> ! { + // Use Arguments::new_const instead of format_args!("{expr}") to potentially + // reduce size overhead. The format_args! macro uses str's Display trait to + // write expr, which calls Formatter::pad, which must accommodate string + // truncation and padding (even though none is used here). Using + // Arguments::new_const may allow the compiler to omit Formatter::pad from the + // output binary, saving up to a few kilobytes. + panic_fmt(fmt::Arguments::new_const(&[$message])); + } + )+ } } @@ -186,25 +182,36 @@ macro_rules! panic_const { // slightly different forms. It's not clear if there's a good way to deduplicate without adding // special cases to the compiler (e.g., a const generic function wouldn't have a single definition // shared across crates, which is exactly what we want here). -panic_const! { - panic_const_add_overflow = "attempt to add with overflow", - panic_const_sub_overflow = "attempt to subtract with overflow", - panic_const_mul_overflow = "attempt to multiply with overflow", - panic_const_div_overflow = "attempt to divide with overflow", - panic_const_rem_overflow = "attempt to calculate the remainder with overflow", - panic_const_neg_overflow = "attempt to negate with overflow", - panic_const_shr_overflow = "attempt to shift right with overflow", - panic_const_shl_overflow = "attempt to shift left with overflow", - panic_const_div_by_zero = "attempt to divide by zero", - panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero", - panic_const_coroutine_resumed = "coroutine resumed after completion", - panic_const_async_fn_resumed = "`async fn` resumed after completion", - panic_const_async_gen_fn_resumed = "`async gen fn` resumed after completion", - panic_const_gen_fn_none = "`gen fn` should just keep returning `None` after completion", - panic_const_coroutine_resumed_panic = "coroutine resumed after panicking", - panic_const_async_fn_resumed_panic = "`async fn` resumed after panicking", - panic_const_async_gen_fn_resumed_panic = "`async gen fn` resumed after panicking", - panic_const_gen_fn_none_panic = "`gen fn` should just keep returning `None` after panicking", +pub mod panic_const { + use super::*; + panic_const! { + panic_const_add_overflow = "attempt to add with overflow", + panic_const_sub_overflow = "attempt to subtract with overflow", + panic_const_mul_overflow = "attempt to multiply with overflow", + panic_const_div_overflow = "attempt to divide with overflow", + panic_const_rem_overflow = "attempt to calculate the remainder with overflow", + panic_const_neg_overflow = "attempt to negate with overflow", + panic_const_shr_overflow = "attempt to shift right with overflow", + panic_const_shl_overflow = "attempt to shift left with overflow", + panic_const_div_by_zero = "attempt to divide by zero", + panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero", + panic_const_coroutine_resumed = "coroutine resumed after completion", + panic_const_async_fn_resumed = "`async fn` resumed after completion", + panic_const_async_gen_fn_resumed = "`async gen fn` resumed after completion", + panic_const_gen_fn_none = "`gen fn` should just keep returning `None` after completion", + panic_const_coroutine_resumed_panic = "coroutine resumed after panicking", + panic_const_async_fn_resumed_panic = "`async fn` resumed after panicking", + panic_const_async_gen_fn_resumed_panic = "`async gen fn` resumed after panicking", + panic_const_gen_fn_none_panic = "`gen fn` should just keep returning `None` after panicking", + } + // Separated panic constants list for async drop feature + // (May be joined when the corresponding lang items will be in the bootstrap) + panic_const! { + panic_const_coroutine_resumed_drop = "coroutine resumed after async drop", + panic_const_async_fn_resumed_drop = "`async fn` resumed after async drop", + panic_const_async_gen_fn_resumed_drop = "`async gen fn` resumed after async drop", + panic_const_gen_fn_none_drop = "`gen fn` resumed after async drop", + } } /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize on the caller. diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs index 752e79c2dacee..91d015b1bc53f 100644 --- a/library/core/src/pat.rs +++ b/library/core/src/pat.rs @@ -12,3 +12,65 @@ macro_rules! pattern_type { /* compiler built-in */ }; } + +/// A trait implemented for integer types and `char`. +/// Useful in the future for generic pattern types, but +/// used right now to simplify ast lowering of pattern type ranges. +#[unstable(feature = "pattern_type_range_trait", issue = "123646")] +#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")] +#[const_trait] +#[diagnostic::on_unimplemented( + message = "`{Self}` is not a valid base type for range patterns", + label = "only integer types and `char` are supported" +)] +pub trait RangePattern { + /// Trait version of the inherent `MIN` assoc const. + #[lang = "RangeMin"] + const MIN: Self; + + /// Trait version of the inherent `MIN` assoc const. + #[lang = "RangeMax"] + const MAX: Self; + + /// A compile-time helper to subtract 1 for exclusive ranges. + #[lang = "RangeSub"] + #[track_caller] + fn sub_one(self) -> Self; +} + +macro_rules! impl_range_pat { + ($($ty:ty,)*) => { + $( + #[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")] + impl const RangePattern for $ty { + const MIN: $ty = <$ty>::MIN; + const MAX: $ty = <$ty>::MAX; + fn sub_one(self) -> Self { + match self.checked_sub(1) { + Some(val) => val, + None => panic!("exclusive range end at minimum value of type") + } + } + } + )* + } +} + +impl_range_pat! { + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize, +} + +#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")] +impl const RangePattern for char { + const MIN: Self = char::MIN; + + const MAX: Self = char::MAX; + + fn sub_one(self) -> Self { + match char::from_u32(self as u32 - 1) { + None => panic!("exclusive range to start of valid chars"), + Some(val) => val, + } + } +} diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 7fcd19f67ee2d..aad073cc8cdd0 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -12,11 +12,11 @@ //! "pinned," in that it has been permanently (until the end of its lifespan) attached to its //! location in memory, as though pinned to a pinboard. Pinning a value is an incredibly useful //! building block for [`unsafe`] code to be able to reason about whether a raw pointer to the -//! pinned value is still valid. [As we'll see later][drop-guarantee], this is necessarily from the -//! time the value is first pinned until the end of its lifespan. This concept of "pinning" is -//! necessary to implement safe interfaces on top of things like self-referential types and -//! intrusive data structures which cannot currently be modeled in fully safe Rust using only -//! borrow-checked [references][reference]. +//! pinned value is still valid. [As we'll see later][drop-guarantee], once a value is pinned, +//! it is necessarily valid at its memory location until the end of its lifespan. This concept +//! of "pinning" is necessary to implement safe interfaces on top of things like self-referential +//! types and intrusive data structures which cannot currently be modeled in fully safe Rust using +//! only borrow-checked [references][reference]. //! //! "Pinning" allows us to put a *value* which exists at some location in memory into a state where //! safe code cannot *move* that value to a different location in memory or otherwise invalidate it @@ -676,7 +676,7 @@ //! let data_ptr = unpinned_src.data.as_ptr() as *const u8; //! let slice_ptr = unpinned_src.slice.as_ptr() as *const u8; //! let offset = slice_ptr.offset_from(data_ptr) as usize; -//! let len = (*unpinned_src.slice.as_ptr()).len(); +//! let len = unpinned_src.slice.as_ptr().len(); //! //! unpinned_self.slice = NonNull::from(&mut unpinned_self.data[offset..offset+len]); //! } @@ -931,6 +931,11 @@ use crate::{ }; use crate::{cmp, fmt}; +mod unsafe_pinned; + +#[unstable(feature = "unsafe_pinned", issue = "125735")] +pub use self::unsafe_pinned::UnsafePinned; + /// A pointer which pins its pointee in place. /// /// [`Pin`] is a wrapper around some kind of pointer `Ptr` which makes that pointer "pin" its @@ -1087,24 +1092,12 @@ use crate::{cmp, fmt}; #[rustc_pub_transparent] #[derive(Copy, Clone)] pub struct Pin { - // FIXME(#93176): this field is made `#[unstable] #[doc(hidden)] pub` to: - // - deter downstream users from accessing it (which would be unsound!), - // - let the `pin!` macro access it (such a macro requires using struct - // literal syntax in order to benefit from lifetime extension). - // - // However, if the `Deref` impl exposes a field with the same name as this - // field, then the two will collide, resulting in a confusing error when the - // user attempts to access the field through a `Pin`. Therefore, the - // name `__pointer` is designed to be unlikely to collide with any other - // field. Long-term, macro hygiene is expected to offer a more robust - // alternative, alongside `unsafe` fields. - #[unstable(feature = "unsafe_pin_internals", issue = "none")] - #[doc(hidden)] - pub __pointer: Ptr, + /// Only public for bootstrap. + pointer: Ptr, } // The following implementations aren't derived in order to avoid soundness -// issues. `&self.__pointer` should not be accessible to untrusted trait +// issues. `&self.pointer` should not be accessible to untrusted trait // implementations. // // See for more details. @@ -1218,7 +1211,7 @@ impl> Pin { #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin_into_inner", since = "1.39.0")] pub const fn into_inner(pin: Pin) -> Ptr { - pin.__pointer + pin.pointer } } @@ -1355,7 +1348,7 @@ impl Pin { #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin", since = "1.33.0")] pub const unsafe fn new_unchecked(pointer: Ptr) -> Pin { - Pin { __pointer: pointer } + Pin { pointer } } /// Gets a shared reference to the pinned value this [`Pin`] points to. @@ -1369,7 +1362,7 @@ impl Pin { #[inline(always)] pub fn as_ref(&self) -> Pin<&Ptr::Target> { // SAFETY: see documentation on this function - unsafe { Pin::new_unchecked(&*self.__pointer) } + unsafe { Pin::new_unchecked(&*self.pointer) } } } @@ -1413,7 +1406,7 @@ impl Pin { #[inline(always)] pub fn as_mut(&mut self) -> Pin<&mut Ptr::Target> { // SAFETY: see documentation on this function - unsafe { Pin::new_unchecked(&mut *self.__pointer) } + unsafe { Pin::new_unchecked(&mut *self.pointer) } } /// Gets `Pin<&mut T>` to the underlying pinned value from this nested `Pin`-pointer. @@ -1426,7 +1419,7 @@ impl Pin { #[stable(feature = "pin_deref_mut", since = "1.84.0")] #[must_use = "`self` will be dropped if the result is not used"] #[inline(always)] - pub fn as_deref_mut(self: Pin<&mut Pin>) -> Pin<&mut Ptr::Target> { + pub fn as_deref_mut(self: Pin<&mut Self>) -> Pin<&mut Ptr::Target> { // SAFETY: What we're asserting here is that going from // // Pin<&mut Pin> @@ -1480,7 +1473,7 @@ impl Pin { where Ptr::Target: Sized, { - *(self.__pointer) = value; + *(self.pointer) = value; } } @@ -1508,7 +1501,7 @@ impl Pin { #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin_into_inner", since = "1.39.0")] pub const unsafe fn into_inner_unchecked(pin: Pin) -> Ptr { - pin.__pointer + pin.pointer } } @@ -1534,7 +1527,7 @@ impl<'a, T: ?Sized> Pin<&'a T> { U: ?Sized, F: FnOnce(&T) -> &U, { - let pointer = &*self.__pointer; + let pointer = &*self.pointer; let new_pointer = func(pointer); // SAFETY: the safety contract for `new_unchecked` must be @@ -1564,7 +1557,7 @@ impl<'a, T: ?Sized> Pin<&'a T> { #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin", since = "1.33.0")] pub const fn get_ref(self) -> &'a T { - self.__pointer + self.pointer } } @@ -1575,7 +1568,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin", since = "1.33.0")] pub const fn into_ref(self) -> Pin<&'a T> { - Pin { __pointer: self.__pointer } + Pin { pointer: self.pointer } } /// Gets a mutable reference to the data inside of this `Pin`. @@ -1595,7 +1588,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { where T: Unpin, { - self.__pointer + self.pointer } /// Gets a mutable reference to the data inside of this `Pin`. @@ -1613,7 +1606,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[stable(feature = "pin", since = "1.33.0")] #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] pub const unsafe fn get_unchecked_mut(self) -> &'a mut T { - self.__pointer + self.pointer } /// Constructs a new pin by mapping the interior value. @@ -1700,21 +1693,21 @@ impl LegacyReceiver for Pin {} #[stable(feature = "pin", since = "1.33.0")] impl fmt::Debug for Pin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.__pointer, f) + fmt::Debug::fmt(&self.pointer, f) } } #[stable(feature = "pin", since = "1.33.0")] impl fmt::Display for Pin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.__pointer, f) + fmt::Display::fmt(&self.pointer, f) } } #[stable(feature = "pin", since = "1.33.0")] impl fmt::Pointer for Pin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&self.__pointer, f) + fmt::Pointer::fmt(&self.pointer, f) } } @@ -1942,77 +1935,13 @@ unsafe impl PinCoerceUnsized for *mut T {} /// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin #[stable(feature = "pin_macro", since = "1.68.0")] #[rustc_macro_transparency = "semitransparent"] -#[allow_internal_unstable(unsafe_pin_internals)] +#[allow_internal_unstable(super_let)] +// `super` gets removed by rustfmt +#[rustfmt::skip] pub macro pin($value:expr $(,)?) { - // This is `Pin::new_unchecked(&mut { $value })`, so, for starters, let's - // review such a hypothetical macro (that any user-code could define): - // - // ```rust - // macro_rules! pin {( $value:expr ) => ( - // match &mut { $value } { at_value => unsafe { // Do not wrap `$value` in an `unsafe` block. - // $crate::pin::Pin::<&mut _>::new_unchecked(at_value) - // }} - // )} - // ``` - // - // Safety: - // - `type P = &mut _`. There are thus no pathological `Deref{,Mut}` impls - // that would break `Pin`'s invariants. - // - `{ $value }` is braced, making it a _block expression_, thus **moving** - // the given `$value`, and making it _become an **anonymous** temporary_. - // By virtue of being anonymous, it can no longer be accessed, thus - // preventing any attempts to `mem::replace` it or `mem::forget` it, _etc._ - // - // This gives us a `pin!` definition that is sound, and which works, but only - // in certain scenarios: - // - If the `pin!(value)` expression is _directly_ fed to a function call: - // `let poll = pin!(fut).poll(cx);` - // - If the `pin!(value)` expression is part of a scrutinee: - // ```rust - // match pin!(fut) { pinned_fut => { - // pinned_fut.as_mut().poll(...); - // pinned_fut.as_mut().poll(...); - // }} // <- `fut` is dropped here. - // ``` - // Alas, it doesn't work for the more straight-forward use-case: `let` bindings. - // ```rust - // let pinned_fut = pin!(fut); // <- temporary value is freed at the end of this statement - // pinned_fut.poll(...) // error[E0716]: temporary value dropped while borrowed - // // note: consider using a `let` binding to create a longer lived value - // ``` - // - Issues such as this one are the ones motivating https://github.com/rust-lang/rfcs/pull/66 - // - // This makes such a macro incredibly unergonomic in practice, and the reason most macros - // out there had to take the path of being a statement/binding macro (_e.g._, `pin!(future);`) - // instead of featuring the more intuitive ergonomics of an expression macro. - // - // Luckily, there is a way to avoid the problem. Indeed, the problem stems from the fact that a - // temporary is dropped at the end of its enclosing statement when it is part of the parameters - // given to function call, which has precisely been the case with our `Pin::new_unchecked()`! - // For instance, - // ```rust - // let p = Pin::new_unchecked(&mut ); - // ``` - // becomes: - // ```rust - // let p = { let mut anon = ; &mut anon }; - // ``` - // - // However, when using a literal braced struct to construct the value, references to temporaries - // can then be taken. This makes Rust change the lifespan of such temporaries so that they are, - // instead, dropped _at the end of the enscoping block_. - // For instance, - // ```rust - // let p = Pin { __pointer: &mut }; - // ``` - // becomes: - // ```rust - // let mut anon = ; - // let p = Pin { __pointer: &mut anon }; - // ``` - // which is *exactly* what we want. - // - // See https://doc.rust-lang.org/1.58.1/reference/destructors.html#temporary-lifetime-extension - // for more info. - $crate::pin::Pin::<&mut _> { __pointer: &mut { $value } } + { + super let mut pinned = $value; + // SAFETY: The value is pinned: it is the local above which cannot be named outside this macro. + unsafe { $crate::pin::Pin::new_unchecked(&mut pinned) } + } } diff --git a/library/core/src/pin/unsafe_pinned.rs b/library/core/src/pin/unsafe_pinned.rs new file mode 100644 index 0000000000000..f65e83662fef8 --- /dev/null +++ b/library/core/src/pin/unsafe_pinned.rs @@ -0,0 +1,197 @@ +use crate::marker::{PointerLike, Unpin}; +use crate::ops::{CoerceUnsized, DispatchFromDyn}; +use crate::pin::Pin; +use crate::{fmt, ptr}; + +/// This type provides a way to opt-out of typical aliasing rules; +/// specifically, `&mut UnsafePinned` is not guaranteed to be a unique pointer. +/// +/// However, even if you define your type like `pub struct Wrapper(UnsafePinned<...>)`, it is still +/// very risky to have an `&mut Wrapper` that aliases anything else. Many functions that work +/// generically on `&mut T` assume that the memory that stores `T` is uniquely owned (such as +/// `mem::swap`). In other words, while having aliasing with `&mut Wrapper` is not immediate +/// Undefined Behavior, it is still unsound to expose such a mutable reference to code you do not +/// control! Techniques such as pinning via [`Pin`] are needed to ensure soundness. +/// +/// Similar to [`UnsafeCell`](crate::cell::UnsafeCell), `UnsafePinned` will not usually show up in +/// the public API of a library. It is an internal implementation detail of libraries that need to +/// support aliasing mutable references. +/// +/// Further note that this does *not* lift the requirement that shared references must be read-only! +/// Use `UnsafeCell` for that. +/// +/// This type blocks niches the same way `UnsafeCell` does. +#[lang = "unsafe_pinned"] +#[repr(transparent)] +#[unstable(feature = "unsafe_pinned", issue = "125735")] +pub struct UnsafePinned { + value: T, +} + +/// When this type is used, that almost certainly means safe APIs need to use pinning to avoid the +/// aliases from becoming invalidated. Therefore let's mark this as `!Unpin`. You can always opt +/// back in to `Unpin` with an `impl` block, provided your API is still sound while unpinned. +#[unstable(feature = "unsafe_pinned", issue = "125735")] +impl !Unpin for UnsafePinned {} + +/// The type is `Copy` when `T` is to avoid people assuming that `Copy` implies there is no +/// `UnsafePinned` anywhere. (This is an issue with `UnsafeCell`: people use `Copy` bounds to mean +/// `Freeze`.) Given that there is no `unsafe impl Copy for ...`, this is also the option that +/// leaves the user more choices (as they can always wrap this in a `!Copy` type). +// FIXME(unsafe_pinned): this may be unsound or a footgun? +#[unstable(feature = "unsafe_pinned", issue = "125735")] +impl Copy for UnsafePinned {} + +#[unstable(feature = "unsafe_pinned", issue = "125735")] +impl Clone for UnsafePinned { + fn clone(&self) -> Self { + *self + } +} + +// `Send` and `Sync` are inherited from `T`. This is similar to `SyncUnsafeCell`, since +// we eventually concluded that `UnsafeCell` implicitly making things `!Sync` is sometimes +// unergonomic. A type that needs to be `!Send`/`!Sync` should really have an explicit +// opt-out itself, e.g. via an `PhantomData<*mut T>` or (one day) via `impl !Send`/`impl !Sync`. + +impl UnsafePinned { + /// Constructs a new instance of `UnsafePinned` which will wrap the specified value. + /// + /// All access to the inner value through `&UnsafePinned` or `&mut UnsafePinned` or + /// `Pin<&mut UnsafePinned>` requires `unsafe` code. + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + pub const fn new(value: T) -> Self { + UnsafePinned { value } + } + + /// Unwraps the value, consuming this `UnsafePinned`. + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] + pub const fn into_inner(self) -> T { + self.value + } +} + +impl UnsafePinned { + /// Get read-write access to the contents of a pinned `UnsafePinned`. + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + pub const fn get_mut_pinned(self: Pin<&mut Self>) -> *mut T { + // SAFETY: we're not using `get_unchecked_mut` to unpin anything + unsafe { self.get_unchecked_mut() }.get_mut_unchecked() + } + + /// Get read-write access to the contents of an `UnsafePinned`. + /// + /// You should usually be using `get_mut_pinned` instead to explicitly track the fact that this + /// memory is "pinned" due to there being aliases. + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + pub const fn get_mut_unchecked(&mut self) -> *mut T { + ptr::from_mut(self) as *mut T + } + + /// Get read-only access to the contents of a shared `UnsafePinned`. + /// + /// Note that `&UnsafePinned` is read-only if `&T` is read-only. This means that if there is + /// mutation of the `T`, future reads from the `*const T` returned here are UB! Use + /// [`UnsafeCell`] if you also need interior mutability. + /// + /// [`UnsafeCell`]: crate::cell::UnsafeCell + /// + /// ```rust,no_run + /// #![feature(unsafe_pinned)] + /// use std::pin::UnsafePinned; + /// + /// unsafe { + /// let mut x = UnsafePinned::new(0); + /// let ptr = x.get(); // read-only pointer, assumes immutability + /// x.get_mut_unchecked().write(1); + /// ptr.read(); // UB! + /// } + /// ``` + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + pub const fn get(&self) -> *const T { + ptr::from_ref(self) as *const T + } + + /// Gets an immutable pointer to the wrapped value. + /// + /// The difference from [`get`] is that this function accepts a raw pointer, which is useful to + /// avoid the creation of temporary references. + /// + /// [`get`]: UnsafePinned::get + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + pub const fn raw_get(this: *const Self) -> *const T { + this as *const T + } + + /// Gets a mutable pointer to the wrapped value. + /// + /// The difference from [`get_mut_pinned`] and [`get_mut_unchecked`] is that this function + /// accepts a raw pointer, which is useful to avoid the creation of temporary references. + /// + /// [`get_mut_pinned`]: UnsafePinned::get_mut_pinned + /// [`get_mut_unchecked`]: UnsafePinned::get_mut_unchecked + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + pub const fn raw_get_mut(this: *mut Self) -> *mut T { + this as *mut T + } +} + +#[unstable(feature = "unsafe_pinned", issue = "125735")] +impl Default for UnsafePinned { + /// Creates an `UnsafePinned`, with the `Default` value for T. + fn default() -> Self { + UnsafePinned::new(T::default()) + } +} + +#[unstable(feature = "unsafe_pinned", issue = "125735")] +impl From for UnsafePinned { + /// Creates a new `UnsafePinned` containing the given value. + fn from(value: T) -> Self { + UnsafePinned::new(value) + } +} + +#[unstable(feature = "unsafe_pinned", issue = "125735")] +impl fmt::Debug for UnsafePinned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("UnsafePinned").finish_non_exhaustive() + } +} + +#[unstable(feature = "coerce_unsized", issue = "18598")] +// #[unstable(feature = "unsafe_pinned", issue = "125735")] +impl, U> CoerceUnsized> for UnsafePinned {} + +// Allow types that wrap `UnsafePinned` to also implement `DispatchFromDyn` +// and become dyn-compatible method receivers. +// Note that currently `UnsafePinned` itself cannot be a method receiver +// because it does not implement Deref. +// In other words: +// `self: UnsafePinned<&Self>` won't work +// `self: UnsafePinned` becomes possible +// FIXME(unsafe_pinned) this logic is copied from UnsafeCell, is it still sound? +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +// #[unstable(feature = "unsafe_pinned", issue = "125735")] +impl, U> DispatchFromDyn> for UnsafePinned {} + +#[unstable(feature = "pointer_like_trait", issue = "none")] +// #[unstable(feature = "unsafe_pinned", issue = "125735")] +impl PointerLike for UnsafePinned {} + +// FIXME(unsafe_pinned): impl PinCoerceUnsized for UnsafePinned? diff --git a/library/core/src/prelude/mod.rs b/library/core/src/prelude/mod.rs index 590ffd64b5bff..8d867a269a21a 100644 --- a/library/core/src/prelude/mod.rs +++ b/library/core/src/prelude/mod.rs @@ -70,3 +70,26 @@ pub mod rust_2024 { #[doc(no_inline)] pub use crate::future::{Future, IntoFuture}; } + +/// The Future version of the core prelude. +/// +/// See the [module-level documentation](self) for more. +#[doc(hidden)] +#[unstable(feature = "prelude_future", issue = "none")] +pub mod rust_future { + #[stable(feature = "rust1", since = "1.0.0")] + #[doc(no_inline)] + pub use super::v1::*; + + #[stable(feature = "prelude_2021", since = "1.55.0")] + #[doc(no_inline)] + pub use crate::iter::FromIterator; + + #[stable(feature = "prelude_2021", since = "1.55.0")] + #[doc(no_inline)] + pub use crate::convert::{TryFrom, TryInto}; + + #[stable(feature = "prelude_2024", since = "1.85.0")] + #[doc(no_inline)] + pub use crate::future::{Future, IntoFuture}; +} diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index 50fd67e839557..8f1b5275871e6 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -111,3 +111,10 @@ pub use crate::macros::builtin::type_ascribe; reason = "placeholder syntax for deref patterns" )] pub use crate::macros::builtin::deref; + +#[unstable( + feature = "type_alias_impl_trait", + issue = "63063", + reason = "`type_alias_impl_trait` has open design concerns" +)] +pub use crate::macros::builtin::define_opaque; diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index bbf5939fe1b05..17c4b48836134 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -127,15 +127,13 @@ mod prim_bool {} /// [`Result`] which we can unpack like this: /// /// ``` -/// #![feature(exhaustive_patterns)] /// use std::str::FromStr; /// let Ok(s) = String::from_str("hello"); /// ``` /// -/// Since the [`Err`] variant contains a `!`, it can never occur. If the `exhaustive_patterns` -/// feature is present this means we can exhaustively match on [`Result`] by just taking the -/// [`Ok`] variant. This illustrates another behavior of `!` - it can be used to "delete" certain -/// enum variants from generic types like `Result`. +/// Since the [`Err`] variant contains a `!`, it can never occur. This means we can exhaustively +/// match on [`Result`] by just taking the [`Ok`] variant. This illustrates another behavior +/// of `!` - it can be used to "delete" certain enum variants from generic types like `Result`. /// /// ## Infinite loops /// @@ -398,12 +396,12 @@ mod prim_never {} /// let v = vec!['h', 'e', 'l', 'l', 'o']; /// /// // five elements times four bytes for each element -/// assert_eq!(20, v.len() * std::mem::size_of::()); +/// assert_eq!(20, v.len() * size_of::()); /// /// let s = String::from("hello"); /// /// // five elements times one byte per element -/// assert_eq!(5, s.len() * std::mem::size_of::()); +/// assert_eq!(5, s.len() * size_of::()); /// ``` /// /// [`String`]: ../std/string/struct.String.html @@ -443,8 +441,8 @@ mod prim_never {} /// let s = String::from("love: ❤️"); /// let v: Vec = s.chars().collect(); /// -/// assert_eq!(12, std::mem::size_of_val(&s[..])); -/// assert_eq!(32, std::mem::size_of_val(&v[..])); +/// assert_eq!(12, size_of_val(&s[..])); +/// assert_eq!(32, size_of_val(&v[..])); /// ``` #[stable(feature = "rust1", since = "1.0.0")] mod prim_char {} @@ -594,10 +592,8 @@ impl () {} /// #[allow(unused_extern_crates)] /// extern crate libc; /// -/// use std::mem; -/// /// unsafe { -/// let my_num: *mut i32 = libc::malloc(mem::size_of::()) as *mut i32; +/// let my_num: *mut i32 = libc::malloc(size_of::()) as *mut i32; /// if my_num.is_null() { /// panic!("failed to allocate memory"); /// } @@ -893,11 +889,11 @@ mod prim_array {} /// /// ``` /// # 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::>()); +/// let pointer_size = size_of::<&u8>(); +/// assert_eq!(2 * pointer_size, size_of::<&[u8]>()); +/// assert_eq!(2 * pointer_size, size_of::<*const [u8]>()); +/// assert_eq!(2 * pointer_size, size_of::>()); +/// assert_eq!(2 * pointer_size, size_of::>()); /// ``` /// /// ## Trait Implementations @@ -1311,12 +1307,59 @@ mod prim_f16 {} // FIXME: Is there a better place to put this? /// /// | `target_arch` | Extra payloads possible on this platform | -/// |---------------|---------| -/// | `x86`, `x86_64`, `arm`, `aarch64`, `riscv32`, `riscv64` | None | +/// |---------------|------------------------------------------| +// Sorted alphabetically +/// | `aarch64`, `arm`, `arm64ec`, `loongarch64`, `powerpc` (except when `target_abi = "spe"`), `powerpc64`, `riscv32`, `riscv64`, `s390x`, `x86`, `x86_64` | None | +/// | `nvptx64` | All payloads | /// | `sparc`, `sparc64` | The all-one payload | -/// | `wasm32`, `wasm64` | If all input NaNs are quiet with all-zero payload: None.
Otherwise: all possible payloads. | +/// | `wasm32`, `wasm64` | If all input NaNs are quiet with all-zero payload: None.
Otherwise: all payloads. | /// /// For targets not in this table, all payloads are possible. +/// +/// # Algebraic operators +/// +/// Algebraic operators of the form `a.algebraic_*(b)` allow the compiler to optimize +/// floating point operations using all the usual algebraic properties of real numbers -- +/// despite the fact that those properties do *not* hold on floating point numbers. +/// This can give a great performance boost since it may unlock vectorization. +/// +/// The exact set of optimizations is unspecified but typically allows combining operations, +/// rearranging series of operations based on mathematical properties, converting between division +/// and reciprocal multiplication, and disregarding the sign of zero. This means that the results of +/// elementary operations may have undefined precision, and "non-mathematical" values +/// such as NaN, +/-Inf, or -0.0 may behave in unexpected ways, but these operations +/// will never cause undefined behavior. +/// +/// Because of the unpredictable nature of compiler optimizations, the same inputs may produce +/// different results even within a single program run. **Unsafe code must not rely on any property +/// of the return value for soundness.** However, implementations will generally do their best to +/// pick a reasonable tradeoff between performance and accuracy of the result. +/// +/// For example: +/// +/// ``` +/// # #![feature(float_algebraic)] +/// # #![allow(unused_assignments)] +/// # let mut x: f32 = 0.0; +/// # let a: f32 = 1.0; +/// # let b: f32 = 2.0; +/// # let c: f32 = 3.0; +/// # let d: f32 = 4.0; +/// x = a.algebraic_add(b).algebraic_add(c).algebraic_add(d); +/// ``` +/// +/// May be rewritten as: +/// +/// ``` +/// # #![allow(unused_assignments)] +/// # let mut x: f32 = 0.0; +/// # let a: f32 = 1.0; +/// # let b: f32 = 2.0; +/// # let c: f32 = 3.0; +/// # let d: f32 = 4.0; +/// x = a + b + c + d; // As written +/// x = (a + c) + (b + d); // Reordered to shorten critical path and enable vectorization +/// ``` #[stable(feature = "rust1", since = "1.0.0")] mod prim_f32 {} @@ -1692,15 +1735,13 @@ mod prim_ref {} /// 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); +/// assert_eq!(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::()); +/// assert_eq!(size_of_val(&bar_ptr), size_of::()); /// /// let footgun = &bar; // this is a shared reference to the zero-sized type identifying `bar` /// ``` diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 2da94e72566e9..19311e39b454e 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -13,8 +13,8 @@ use crate::{cmp, fmt, hash, mem, num}; pub struct Alignment(AlignmentEnum); // Alignment is `repr(usize)`, but via extra steps. -const _: () = assert!(mem::size_of::() == mem::size_of::()); -const _: () = assert!(mem::align_of::() == mem::align_of::()); +const _: () = assert!(size_of::() == size_of::()); +const _: () = assert!(align_of::() == align_of::()); fn _alignment_can_be_structurally_matched(a: Alignment) -> bool { matches!(a, Alignment::MIN) @@ -38,14 +38,14 @@ impl Alignment { /// Returns the alignment for a type. /// - /// This provides the same numerical value as [`mem::align_of`], + /// This provides the same numerical value as [`align_of`], /// but in an `Alignment` instead of a `usize`. #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] #[must_use] pub const fn of() -> Self { // This can't actually panic since type alignment is always a power of two. - const { Alignment::new(mem::align_of::()).unwrap() } + const { Alignment::new(align_of::()).unwrap() } } /// Creates an `Alignment` from a `usize`, or returns `None` if it's diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 8db620596dde7..f6109cafe86b1 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1,7 +1,7 @@ use super::*; use crate::cmp::Ordering::{Equal, Greater, Less}; use crate::intrinsics::const_eval_select; -use crate::mem::SizedTypeProperties; +use crate::mem::{self, SizedTypeProperties}; use crate::slice::{self, SliceIndex}; impl *const T { @@ -66,6 +66,34 @@ impl *const T { self as _ } + /// Try to cast to a pointer of another type by checking aligment. + /// + /// If the pointer is properly aligned to the target type, it will be + /// cast to the target type. Otherwise, `None` is returned. + /// + /// # Examples + /// + /// ```rust + /// #![feature(pointer_try_cast_aligned)] + /// + /// let aligned: *const u8 = 0x1000 as _; + /// + /// // i32 has at most 4-byte alignment, so this will succeed + /// assert!(aligned.try_cast_aligned::().is_some()); + /// + /// let unaligned: *const u8 = 0x1001 as _; + /// + /// // i32 has at least 2-byte alignment, so this will fail + /// assert!(unaligned.try_cast_aligned::().is_none()); + /// ``` + #[unstable(feature = "pointer_try_cast_aligned", issue = "141221")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn try_cast_aligned(self) -> Option<*const U> { + if self.is_aligned_to(align_of::()) { Some(self.cast()) } else { None } + } + /// Uses the address value in a new pointer of another type. /// /// This operation will ignore the address part of its `meta` operand and discard existing @@ -193,7 +221,6 @@ impl *const T { /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. /// /// [`with_exposed_provenance`]: with_exposed_provenance - #[must_use] #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] pub fn expose_provenance(self) -> usize { @@ -387,7 +414,8 @@ impl *const T { /// * If the computed offset is non-zero, then `self` must be [derived from][crate::ptr#provenance] a pointer to some /// [allocated object], and the entire memory range between `self` and the result must be in /// bounds of that allocated object. In particular, this range must not "wrap around" the edge - /// of the address space. + /// of the address space. Note that "range" here refers to a half-open range as usual in Rust, + /// i.e., `self..result` for non-negative offsets and `result..self` for negative offsets. /// /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. @@ -484,8 +512,9 @@ impl *const T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer "remembers" the [allocated object] that `self` points to; it must not - /// be used to read or write other allocated objects. + /// The resulting pointer "remembers" the [allocated object] that `self` points to + /// (this is called "[Provenance](ptr/index.html#provenance)"). + /// The pointer must not be used to read or write other allocated objects. /// /// In other words, `let z = x.wrapping_offset((y as isize) - (x as isize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still @@ -595,9 +624,9 @@ impl *const T { } /// Calculates the distance between two pointers within the same allocation. The returned value is in - /// units of T: the distance in bytes divided by `mem::size_of::()`. + /// units of T: the distance in bytes divided by `size_of::()`. /// - /// This is equivalent to `(self as isize - origin as isize) / (mem::size_of::() as isize)`, + /// This is equivalent to `(self as isize - origin as isize) / (size_of::() as isize)`, /// except that it has a lot more opportunities for UB, in exchange for the compiler /// better understanding what you are doing. /// @@ -633,7 +662,7 @@ impl *const T { /// objects is not known at compile-time. However, the requirement also exists at /// runtime and may be exploited by optimizations. If you wish to compute the difference between /// pointers that are not guaranteed to be from the same allocation, use `(self as isize - - /// origin as isize) / mem::size_of::()`. + /// origin as isize) / size_of::()`. // FIXME: recommend `addr()` instead of `as usize` once that is stable. /// /// [`add`]: #method.add @@ -683,7 +712,7 @@ impl *const T { where T: Sized, { - let pointee_size = mem::size_of::(); + let pointee_size = size_of::(); assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); // SAFETY: the caller must uphold the safety contract for `ptr_offset_from`. unsafe { intrinsics::ptr_offset_from(self, origin) } @@ -709,7 +738,7 @@ impl *const T { /// Calculates the distance between two pointers within the same allocation, *where it's known that /// `self` is equal to or greater than `origin`*. The returned value is in - /// units of T: the distance in bytes is divided by `mem::size_of::()`. + /// units of T: the distance in bytes is divided by `size_of::()`. /// /// This computes the same value that [`offset_from`](#method.offset_from) /// would compute, but with the added precondition that the offset is @@ -764,8 +793,8 @@ impl *const T { /// // This would be incorrect, as the pointers are not correctly ordered: /// // ptr1.offset_from_unsigned(ptr2) /// ``` - #[stable(feature = "ptr_sub_ptr", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ptr_sub_ptr", since = "1.87.0")] + #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "1.87.0")] #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn offset_from_unsigned(self, origin: *const T) -> usize @@ -793,7 +822,7 @@ impl *const T { ) => runtime_ptr_ge(this, origin) ); - let pointee_size = mem::size_of::(); + let pointee_size = size_of::(); assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); // SAFETY: the caller must uphold the safety contract for `ptr_offset_from_unsigned`. unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) } @@ -804,17 +833,17 @@ impl *const T { /// units of **bytes**. /// /// This is purely a convenience for casting to a `u8` pointer and - /// using [`sub_ptr`][pointer::offset_from_unsigned] on it. See that method for - /// documentation and safety requirements. + /// using [`offset_from_unsigned`][pointer::offset_from_unsigned] on it. + /// See that method for documentation and safety requirements. /// /// For non-`Sized` pointees this operation considers only the data pointers, /// ignoring the metadata. - #[stable(feature = "ptr_sub_ptr", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ptr_sub_ptr", since = "1.87.0")] + #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "1.87.0")] #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_offset_from_unsigned(self, origin: *const U) -> usize { - // SAFETY: the caller must uphold the safety contract for `sub_ptr`. + // SAFETY: the caller must uphold the safety contract for `offset_from_unsigned`. unsafe { self.cast::().offset_from_unsigned(origin.cast::()) } } @@ -1313,7 +1342,7 @@ impl *const T { unsafe { read_unaligned(self) } } - /// Copies `count * size_of` bytes from `self` to `dest`. The source + /// 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`]. @@ -1333,7 +1362,7 @@ impl *const T { unsafe { copy(self, dest, count) } } - /// Copies `count * size_of` bytes from `self` to `dest`. The source + /// 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`]. @@ -1375,8 +1404,6 @@ impl *const T { /// Accessing adjacent `u8` as `u16` /// /// ``` - /// use std::mem::align_of; - /// /// # unsafe { /// let x = [5_u8, 6, 7, 8, 9]; /// let ptr = x.as_ptr(); @@ -1436,7 +1463,7 @@ impl *const T { where T: Sized, { - self.is_aligned_to(mem::align_of::()) + self.is_aligned_to(align_of::()) } /// Returns whether the pointer is aligned to `align`. @@ -1595,7 +1622,7 @@ impl *const [T] { /// 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, + /// * The pointer must be [valid] for reads for `ptr.len() * 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]! @@ -1607,7 +1634,7 @@ impl *const [T] { /// 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`. + /// * The total size `ptr.len() * 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 @@ -1741,3 +1768,11 @@ impl PartialOrd for *const T { *self >= *other } } + +#[stable(feature = "raw_ptr_default", since = "1.88.0")] +impl Default for *const T { + /// Returns the default value of [`null()`][crate::ptr::null]. + fn default() -> Self { + crate::ptr::null() + } +} diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 9eee29d485f41..9c5da306e27a7 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -61,6 +61,8 @@ pub trait Pointee { // NOTE: Keep trait bounds in `static_assert_expected_bounds_for_metadata` // in `library/core/src/ptr/metadata.rs` // in sync with those here: + // NOTE: The metadata of `dyn Trait + 'a` is `DynMetadata` + // so a `'static` bound must not be added. type Metadata: fmt::Debug + Copy + Send + Sync + Ord + Hash + Unpin + Freeze; } @@ -74,7 +76,7 @@ pub trait Pointee { /// #![feature(ptr_metadata)] /// /// fn this_never_panics() { -/// assert_eq!(std::mem::size_of::<&T>(), std::mem::size_of::()) +/// assert_eq!(size_of::<&T>(), size_of::()) /// } /// ``` #[unstable(feature = "ptr_metadata", issue = "81513")] diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index eb99be817a2ca..35a909f6904cd 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -48,7 +48,7 @@ //! //! 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 +//! aligned to `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`]. @@ -278,7 +278,7 @@ //! ### Using Strict Provenance //! //! Most code needs no changes to conform to strict provenance, as the only really concerning -//! operation is casts from usize to a pointer. For code which *does* cast a `usize` to a pointer, +//! operation is casts from `usize` to a pointer. For code which *does* cast a `usize` to a pointer, //! the scope of the change depends on exactly what you're doing. //! //! In general, you just need to make sure that if you want to convert a `usize` address to a @@ -297,7 +297,7 @@ //! //! // Our value, which must have enough alignment to have spare least-significant-bits. //! let my_precious_data: u32 = 17; -//! assert!(core::mem::align_of::() > 1); +//! assert!(align_of::() > 1); //! //! // Create a tagged pointer //! let ptr = &my_precious_data as *const u32; @@ -398,22 +398,13 @@ use crate::cmp::Ordering; use crate::intrinsics::const_eval_select; use crate::marker::FnPtr; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; +use crate::num::NonZero; use crate::{fmt, hash, intrinsics, ub_checks}; mod alignment; #[unstable(feature = "ptr_alignment_type", issue = "102070")] pub use alignment::Alignment; -#[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::copy_nonoverlapping; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use crate::intrinsics::write_bytes; - mod metadata; #[unstable(feature = "ptr_metadata", issue = "81513")] pub use metadata::{DynMetadata, Pointee, Thin, from_raw_parts, from_raw_parts_mut, metadata}; @@ -429,6 +420,289 @@ pub use unique::Unique; mod const_ptr; mod mut_ptr; +// 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.) + +/// 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 source and destination arguments swapped, +/// and `count` counting the number of `T`s instead of bytes. +/// +/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the +/// requirements of `T`. The initialization state is preserved exactly. +/// +/// [`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 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 add is always safe because `Vec` will never +/// // allocate more than `isize::MAX` bytes. +/// let dst_ptr = dst.as_mut_ptr().add(dst_len); +/// 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")] +#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] +#[inline(always)] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[rustc_diagnostic_item = "ptr_copy_nonoverlapping"] +pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { + ub_checks::assert_unsafe_precondition!( + check_language_ub, + "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ + and the specified memory ranges do not overlap", + ( + src: *const () = src as *const (), + dst: *mut () = dst as *mut (), + size: usize = size_of::(), + align: usize = align_of::(), + count: usize = count, + ) => { + let zero_size = count == 0 || size == 0; + ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size) + && ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size) + && ub_checks::maybe_is_nonoverlapping(src, dst, size, count) + } + ); + + // SAFETY: the safety contract for `copy_nonoverlapping` must be + // upheld by the caller. + unsafe { crate::intrinsics::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 source and destination arguments swapped, +/// and `count` counting the number of `T`s instead of bytes. +/// Copying takes place as if the bytes were copied from `src` +/// to a temporary array and then copied from the array to `dst`. +/// +/// The copy is "untyped" in the sense that data may be uninitialized or otherwise violate the +/// requirements of `T`. The initialization state is preserved exactly. +/// +/// [`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, and must remain valid even +/// when `src` is read for `count * size_of::()` bytes. (This means if the memory ranges +/// overlap, the `dst` pointer must not be invalidated by `src` reads.) +/// +/// * 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 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; +/// +/// /// # Safety +/// /// +/// /// * `ptr` must be correctly aligned for its type and non-zero. +/// /// * `ptr` must be valid for reads of `elts` contiguous elements of type `T`. +/// /// * Those elements must not be used after calling this function unless `T: Copy`. +/// # #[allow(dead_code)] +/// unsafe fn from_buf_raw(ptr: *const T, elts: usize) -> Vec { +/// let mut dst = Vec::with_capacity(elts); +/// +/// // SAFETY: Our precondition ensures the source is aligned and valid, +/// // and `Vec::with_capacity` ensures that we have usable space to write them. +/// unsafe { ptr::copy(ptr, dst.as_mut_ptr(), elts); } +/// +/// // SAFETY: We created it with this much capacity earlier, +/// // and the previous `copy` has initialized these elements. +/// unsafe { dst.set_len(elts); } +/// dst +/// } +/// ``` +#[doc(alias = "memmove")] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] +#[inline(always)] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[rustc_diagnostic_item = "ptr_copy"] +pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { + // SAFETY: the safety contract for `copy` must be upheld by the caller. + unsafe { + ub_checks::assert_unsafe_precondition!( + check_language_ub, + "ptr::copy requires that both pointer arguments are aligned and non-null", + ( + src: *const () = src as *const (), + dst: *mut () = dst as *mut (), + align: usize = align_of::(), + zero_size: bool = T::IS_ZST || count == 0, + ) => + ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size) + && ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size) + ); + crate::intrinsics::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. +/// +/// Note that even if the effectively copied size (`count * size_of::()`) is +/// `0`, the pointer must be properly aligned. +/// +/// Additionally, note that changing `*dst` in this way can easily lead to undefined behavior (UB) +/// later if the written bytes are not a valid representation of some `T`. For instance, the +/// following is an **incorrect** use of this function: +/// +/// ```rust,no_run +/// unsafe { +/// let mut value: u8 = 0; +/// let ptr: *mut bool = &mut value as *mut u8 as *mut bool; +/// let _bool = ptr.read(); // This is fine, `ptr` points to a valid `bool`. +/// ptr.write_bytes(42u8, 1); // This function itself does not cause UB... +/// let _bool = ptr.read(); // ...but it makes this operation UB! ⚠️ +/// } +/// ``` +/// +/// [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]); +/// ``` +#[doc(alias = "memset")] +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")] +#[inline(always)] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[rustc_diagnostic_item = "ptr_write_bytes"] +pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { + // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. + unsafe { + ub_checks::assert_unsafe_precondition!( + check_language_ub, + "ptr::write_bytes requires that the destination pointer is aligned and non-null", + ( + addr: *const () = dst as *const (), + align: usize = align_of::(), + zero_size: bool = T::IS_ZST || count == 0, + ) => ub_checks::maybe_is_aligned_and_not_null(addr, align, zero_size) + ); + crate::intrinsics::write_bytes(dst, val, count) + } +} + /// Executes the destructor (if any) of the pointed-to value. /// /// This is almost the same as calling [`ptr::read`] and discarding @@ -1064,10 +1338,45 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { /// assert_eq!(x, [7, 8, 3, 4]); /// assert_eq!(y, [1, 2, 9]); /// ``` +/// +/// # Const evaluation limitations +/// +/// If this function is invoked during const-evaluation, the current implementation has a small (and +/// rarely relevant) limitation: if `count` is at least 2 and the data pointed to by `x` or `y` +/// contains a pointer that crosses the boundary of two `T`-sized chunks of memory, the function may +/// fail to evaluate (similar to a panic during const-evaluation). This behavior may change in the +/// future. +/// +/// The limitation is illustrated by the following example: +/// +/// ``` +/// use std::mem::size_of; +/// use std::ptr; +/// +/// const { unsafe { +/// const PTR_SIZE: usize = size_of::<*const i32>(); +/// let mut data1 = [0u8; PTR_SIZE]; +/// let mut data2 = [0u8; PTR_SIZE]; +/// // Store a pointer in `data1`. +/// data1.as_mut_ptr().cast::<*const i32>().write_unaligned(&42); +/// // Swap the contents of `data1` and `data2` by swapping `PTR_SIZE` many `u8`-sized chunks. +/// // This call will fail, because the pointer in `data1` crosses the boundary +/// // between several of the 1-byte chunks that are being swapped here. +/// //ptr::swap_nonoverlapping(data1.as_mut_ptr(), data2.as_mut_ptr(), PTR_SIZE); +/// // Swap the contents of `data1` and `data2` by swapping a single chunk of size +/// // `[u8; PTR_SIZE]`. That works, as there is no pointer crossing the boundary between +/// // two chunks. +/// ptr::swap_nonoverlapping(&mut data1, &mut data2, 1); +/// // Read the pointer from `data2` and dereference it. +/// let ptr = data2.as_ptr().cast::<*const i32>().read_unaligned(); +/// assert!(*ptr == 42); +/// } } +/// ``` #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] -#[rustc_const_unstable(feature = "const_swap_nonoverlapping", issue = "133668")] +#[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "1.88.0")] #[rustc_diagnostic_item = "ptr_swap_nonoverlapping"] +#[rustc_allow_const_fn_unstable(const_eval_select)] // both implementations behave the same pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { ub_checks::assert_unsafe_precondition!( check_library_ub, @@ -1094,51 +1403,25 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { // are pointers inside `T` we will copy them in one go rather than trying to copy a part // of a pointer (which would not work). // SAFETY: Same preconditions as this function - unsafe { swap_nonoverlapping_simple_untyped(x, y, count) } + unsafe { swap_nonoverlapping_const(x, y, count) } } else { - macro_rules! attempt_swap_as_chunks { - ($ChunkTy:ty) => { - if mem::align_of::() >= mem::align_of::<$ChunkTy>() - && mem::size_of::() % mem::size_of::<$ChunkTy>() == 0 - { - let x: *mut $ChunkTy = x.cast(); - let y: *mut $ChunkTy = y.cast(); - let count = count * (mem::size_of::() / mem::size_of::<$ChunkTy>()); - // SAFETY: these are the same bytes that the caller promised were - // ok, just typed as `MaybeUninit`s instead of as `T`s. - // The `if` condition above ensures that we're not violating - // alignment requirements, and that the division is exact so - // that we don't lose any bytes off the end. - return unsafe { swap_nonoverlapping_simple_untyped(x, y, count) }; - } - }; - } - - // Split up the slice into small power-of-two-sized chunks that LLVM is able - // to vectorize (unless it's a special type with more-than-pointer alignment, - // because we don't want to pessimize things like slices of SIMD vectors.) - if mem::align_of::() <= mem::size_of::() - && (!mem::size_of::().is_power_of_two() - || mem::size_of::() > mem::size_of::() * 2) - { - attempt_swap_as_chunks!(usize); - attempt_swap_as_chunks!(u8); + // Going though a slice here helps codegen know the size fits in `isize` + let slice = slice_from_raw_parts_mut(x, count); + // SAFETY: This is all readable from the pointer, meaning it's one + // allocated object, and thus cannot be more than isize::MAX bytes. + let bytes = unsafe { mem::size_of_val_raw::<[T]>(slice) }; + if let Some(bytes) = NonZero::new(bytes) { + // SAFETY: These are the same ranges, just expressed in a different + // type, so they're still non-overlapping. + unsafe { swap_nonoverlapping_bytes(x.cast(), y.cast(), bytes) }; } - - // SAFETY: Same preconditions as this function - unsafe { swap_nonoverlapping_simple_untyped(x, y, count) } } ) } /// Same behavior and safety conditions as [`swap_nonoverlapping`] -/// -/// LLVM can vectorize this (at least it can for the power-of-two-sized types -/// `swap_nonoverlapping` tries to use) so no need to manually SIMD it. #[inline] -const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, count: usize) { - let x = x.cast::>(); - let y = y.cast::>(); +const unsafe fn swap_nonoverlapping_const(x: *mut T, y: *mut T, count: usize) { let mut i = 0; while i < count { // SAFETY: By precondition, `i` is in-bounds because it's below `n` @@ -1147,26 +1430,91 @@ const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, coun // and it's distinct from `x` since the ranges are non-overlapping let y = unsafe { y.add(i) }; - // If we end up here, it's because we're using a simple type -- like - // a small power-of-two-sized thing -- or a special type with particularly - // large alignment, particularly SIMD types. - // Thus, we're fine just reading-and-writing it, as either it's small - // and that works well anyway or it's special and the type's author - // presumably wanted things to be done in the larger chunk. - // SAFETY: we're only ever given pointers that are valid to read/write, // including being aligned, and nothing here panics so it's drop-safe. unsafe { - let a: MaybeUninit = read(x); - let b: MaybeUninit = read(y); - write(x, b); - write(y, a); + // Note that it's critical that these use `copy_nonoverlapping`, + // rather than `read`/`write`, to avoid #134713 if T has padding. + let mut temp = MaybeUninit::::uninit(); + copy_nonoverlapping(x, temp.as_mut_ptr(), 1); + copy_nonoverlapping(y, x, 1); + copy_nonoverlapping(temp.as_ptr(), y, 1); } i += 1; } } +// Don't let MIR inline this, because we really want it to keep its noalias metadata +#[rustc_no_mir_inline] +#[inline] +fn swap_chunk(x: &mut MaybeUninit<[u8; N]>, y: &mut MaybeUninit<[u8; N]>) { + let a = *x; + let b = *y; + *x = b; + *y = a; +} + +#[inline] +unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, bytes: NonZero) { + // Same as `swap_nonoverlapping::<[u8; N]>`. + unsafe fn swap_nonoverlapping_chunks( + x: *mut MaybeUninit<[u8; N]>, + y: *mut MaybeUninit<[u8; N]>, + chunks: NonZero, + ) { + let chunks = chunks.get(); + for i in 0..chunks { + // SAFETY: i is in [0, chunks) so the adds and dereferences are in-bounds. + unsafe { swap_chunk(&mut *x.add(i), &mut *y.add(i)) }; + } + } + + // Same as `swap_nonoverlapping_bytes`, but accepts at most 1+2+4=7 bytes + #[inline] + unsafe fn swap_nonoverlapping_short(x: *mut u8, y: *mut u8, bytes: NonZero) { + // Tail handling for auto-vectorized code sometimes has element-at-a-time behaviour, + // see . + // By swapping as different sizes, rather than as a loop over bytes, + // we make sure not to end up with, say, seven byte-at-a-time copies. + + let bytes = bytes.get(); + let mut i = 0; + macro_rules! swap_prefix { + ($($n:literal)+) => {$( + if (bytes & $n) != 0 { + // SAFETY: `i` can only have the same bits set as those in bytes, + // so these `add`s are in-bounds of `bytes`. But the bit for + // `$n` hasn't been set yet, so the `$n` bytes that `swap_chunk` + // will read and write are within the usable range. + unsafe { swap_chunk::<$n>(&mut*x.add(i).cast(), &mut*y.add(i).cast()) }; + i |= $n; + } + )+}; + } + swap_prefix!(4 2 1); + debug_assert_eq!(i, bytes); + } + + const CHUNK_SIZE: usize = size_of::<*const ()>(); + let bytes = bytes.get(); + + let chunks = bytes / CHUNK_SIZE; + let tail = bytes % CHUNK_SIZE; + if let Some(chunks) = NonZero::new(chunks) { + // SAFETY: this is bytes/CHUNK_SIZE*CHUNK_SIZE bytes, which is <= bytes, + // so it's within the range of our non-overlapping bytes. + unsafe { swap_nonoverlapping_chunks::(x.cast(), y.cast(), chunks) }; + } + if let Some(tail) = NonZero::new(tail) { + const { assert!(CHUNK_SIZE <= 8) }; + let delta = chunks * CHUNK_SIZE; + // SAFETY: the tail length is below CHUNK SIZE because of the remainder, + // and CHUNK_SIZE is at most 8 by the const assert, so tail <= 7 + unsafe { swap_nonoverlapping_short(x.add(delta), y.add(delta), tail) }; + } +} + /// Moves `src` into the pointed `dst`, returning the previous `dst` value. /// /// Neither value is dropped. @@ -1443,10 +1791,8 @@ pub const unsafe fn read(src: *const T) -> T { /// Read a `usize` value from a byte buffer: /// /// ``` -/// use std::mem; -/// /// fn read_usize(x: &[u8]) -> usize { -/// assert!(x.len() >= mem::size_of::()); +/// assert!(x.len() >= size_of::()); /// /// let ptr = x.as_ptr() as *const usize; /// @@ -1467,7 +1813,7 @@ pub const unsafe fn read_unaligned(src: *const T) -> T { // 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::()); + copy_nonoverlapping(src as *const u8, tmp.as_mut_ptr() as *mut u8, size_of::()); tmp.assume_init() } } @@ -1647,10 +1993,8 @@ pub const unsafe fn write(dst: *mut T, src: T) { /// Write a `usize` value to a byte buffer: /// /// ``` -/// use std::mem; -/// /// fn write_usize(x: &mut [u8], val: usize) { -/// assert!(x.len() >= mem::size_of::()); +/// assert!(x.len() >= size_of::()); /// /// let ptr = x.as_mut_ptr() as *mut usize; /// @@ -1667,7 +2011,7 @@ pub const unsafe fn write_unaligned(dst: *mut T, src: T) { // `dst` cannot overlap `src` because the caller has mutable access // to `dst` while `src` is owned by this function. unsafe { - copy_nonoverlapping((&raw const src) as *const u8, dst as *mut u8, mem::size_of::()); + copy_nonoverlapping((&raw const src) as *const u8, dst as *mut u8, size_of::()); // We are calling the intrinsic directly to avoid function calls in the generated code. intrinsics::forget(src); } @@ -1911,7 +2255,7 @@ pub(crate) unsafe fn align_offset(p: *const T, a: usize) -> usize { inverse & m_minus_one } - let stride = mem::size_of::(); + let stride = size_of::(); let addr: usize = p.addr(); diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 5a64f12ca99ff..2662a4fdc3138 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1,7 +1,7 @@ use super::*; use crate::cmp::Ordering::{Equal, Greater, Less}; use crate::intrinsics::const_eval_select; -use crate::mem::SizedTypeProperties; +use crate::mem::{self, SizedTypeProperties}; use crate::slice::{self, SliceIndex}; impl *mut T { @@ -48,6 +48,34 @@ impl *mut T { self as _ } + /// Try to cast to a pointer of another type by checking aligment. + /// + /// If the pointer is properly aligned to the target type, it will be + /// cast to the target type. Otherwise, `None` is returned. + /// + /// # Examples + /// + /// ```rust + /// #![feature(pointer_try_cast_aligned)] + /// + /// let aligned: *mut u8 = 0x1000 as _; + /// + /// // i32 has at most 4-byte alignment, so this will succeed + /// assert!(aligned.try_cast_aligned::().is_some()); + /// + /// let unaligned: *mut u8 = 0x1001 as _; + /// + /// // i32 has at least 2-byte alignment, so this will fail + /// assert!(unaligned.try_cast_aligned::().is_none()); + /// ``` + #[unstable(feature = "pointer_try_cast_aligned", issue = "141221")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn try_cast_aligned(self) -> Option<*mut U> { + if self.is_aligned_to(align_of::()) { Some(self.cast()) } else { None } + } + /// Uses the address value in a new pointer of another type. /// /// This operation will ignore the address part of its `meta` operand and discard existing @@ -482,8 +510,9 @@ impl *mut T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer "remembers" the [allocated object] that `self` points to; it must not - /// be used to read or write other allocated objects. + /// The resulting pointer "remembers" the [allocated object] that `self` points to + /// (this is called "[Provenance](ptr/index.html#provenance)"). + /// The pointer must not be used to read or write other allocated objects. /// /// In other words, `let z = x.wrapping_offset((y as isize) - (x as isize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still @@ -769,9 +798,9 @@ impl *mut T { } /// Calculates the distance between two pointers within the same allocation. The returned value is in - /// units of T: the distance in bytes divided by `mem::size_of::()`. + /// units of T: the distance in bytes divided by `size_of::()`. /// - /// This is equivalent to `(self as isize - origin as isize) / (mem::size_of::() as isize)`, + /// This is equivalent to `(self as isize - origin as isize) / (size_of::() as isize)`, /// except that it has a lot more opportunities for UB, in exchange for the compiler /// better understanding what you are doing. /// @@ -807,7 +836,7 @@ impl *mut T { /// objects is not known at compile-time. However, the requirement also exists at /// runtime and may be exploited by optimizations. If you wish to compute the difference between /// pointers that are not guaranteed to be from the same allocation, use `(self as isize - - /// origin as isize) / mem::size_of::()`. + /// origin as isize) / size_of::()`. // FIXME: recommend `addr()` instead of `as usize` once that is stable. /// /// [`add`]: #method.add @@ -881,7 +910,7 @@ impl *mut T { /// Calculates the distance between two pointers within the same allocation, *where it's known that /// `self` is equal to or greater than `origin`*. The returned value is in - /// units of T: the distance in bytes is divided by `mem::size_of::()`. + /// units of T: the distance in bytes is divided by `size_of::()`. /// /// This computes the same value that [`offset_from`](#method.offset_from) /// would compute, but with the added precondition that the offset is @@ -937,15 +966,16 @@ impl *mut T { /// /// // This would be incorrect, as the pointers are not correctly ordered: /// // ptr1.offset_from(ptr2) - #[stable(feature = "ptr_sub_ptr", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "CURRENT_RUSTC_VERSION")] + /// ``` + #[stable(feature = "ptr_sub_ptr", since = "1.87.0")] + #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "1.87.0")] #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn offset_from_unsigned(self, origin: *const T) -> usize where T: Sized, { - // SAFETY: the caller must uphold the safety contract for `sub_ptr`. + // SAFETY: the caller must uphold the safety contract for `offset_from_unsigned`. unsafe { (self as *const T).offset_from_unsigned(origin) } } @@ -954,17 +984,17 @@ impl *mut T { /// units of **bytes**. /// /// This is purely a convenience for casting to a `u8` pointer and - /// using [`sub_ptr`][pointer::offset_from_unsigned] on it. See that method for - /// documentation and safety requirements. + /// using [`offset_from_unsigned`][pointer::offset_from_unsigned] on it. + /// See that method for documentation and safety requirements. /// /// For non-`Sized` pointees this operation considers only the data pointers, /// ignoring the metadata. - #[stable(feature = "ptr_sub_ptr", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ptr_sub_ptr", since = "1.87.0")] + #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "1.87.0")] #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_offset_from_unsigned(self, origin: *mut U) -> usize { - // SAFETY: the caller must uphold the safety contract for `byte_sub_ptr`. + // SAFETY: the caller must uphold the safety contract for `byte_offset_from_unsigned`. unsafe { (self as *const T).byte_offset_from_unsigned(origin) } } @@ -1397,7 +1427,7 @@ impl *mut T { unsafe { read_unaligned(self) } } - /// Copies `count * size_of` bytes from `self` to `dest`. The source + /// 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`]. @@ -1417,7 +1447,7 @@ impl *mut T { unsafe { copy(self, dest, count) } } - /// Copies `count * size_of` bytes from `self` to `dest`. The source + /// 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`]. @@ -1437,7 +1467,7 @@ impl *mut T { unsafe { copy_nonoverlapping(self, dest, count) } } - /// Copies `count * size_of` bytes from `src` to `self`. The source + /// 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`]. @@ -1457,7 +1487,7 @@ impl *mut T { unsafe { copy(src, self, count) } } - /// Copies `count * size_of` bytes from `src` to `self`. The source + /// 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`]. @@ -1574,8 +1604,9 @@ impl *mut T { /// /// [`ptr::replace`]: crate::ptr::replace() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_stable(feature = "const_inherent_ptr_replace", since = "1.88.0")] #[inline(always)] - pub unsafe fn replace(self, src: T) -> T + pub const unsafe fn replace(self, src: T) -> T where T: Sized, { @@ -1623,8 +1654,6 @@ impl *mut T { /// Accessing adjacent `u8` as `u16` /// /// ``` - /// use std::mem::align_of; - /// /// # unsafe { /// let mut x = [5_u8, 6, 7, 8, 9]; /// let ptr = x.as_mut_ptr(); @@ -1689,7 +1718,7 @@ impl *mut T { where T: Sized, { - self.is_aligned_to(mem::align_of::()) + self.is_aligned_to(align_of::()) } /// Returns whether the pointer is aligned to `align`. @@ -1950,7 +1979,7 @@ impl *mut [T] { /// 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, + /// * The pointer must be [valid] for reads for `ptr.len() * 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]! @@ -1962,7 +1991,7 @@ impl *mut [T] { /// 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`. + /// * The total size `ptr.len() * 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 @@ -2008,7 +2037,7 @@ impl *mut [T] { /// 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::()` + /// * The pointer must be [valid] for reads and writes for `ptr.len() * 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]! @@ -2020,7 +2049,7 @@ impl *mut [T] { /// 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`. + /// * The total size `ptr.len() * 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 @@ -2157,3 +2186,11 @@ impl PartialOrd for *mut T { *self >= *other } } + +#[stable(feature = "raw_ptr_default", since = "1.88.0")] +impl Default for *mut T { + /// Returns the default value of [`null_mut()`][crate::ptr::null_mut]. + fn default() -> Self { + crate::ptr::null_mut() + } +} diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 7abd3ddaa9efc..bb344c6a0d316 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -49,7 +49,6 @@ use crate::{fmt, hash, intrinsics, mem, ptr}; /// are guaranteed to have the same size and alignment: /// /// ``` -/// # use std::mem::{size_of, align_of}; /// use std::ptr::NonNull; /// /// assert_eq!(size_of::>(), size_of::>>()); @@ -263,7 +262,8 @@ impl NonNull { } /// Converts a reference to a `NonNull` pointer. - #[unstable(feature = "non_null_from_ref", issue = "130823")] + #[stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn from_ref(r: &T) -> Self { // SAFETY: A reference cannot be null. @@ -271,7 +271,8 @@ impl NonNull { } /// Converts a mutable reference to a `NonNull` pointer. - #[unstable(feature = "non_null_from_ref", issue = "130823")] + #[stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn from_mut(r: &mut T) -> Self { // SAFETY: A mutable reference cannot be null. @@ -489,6 +490,35 @@ impl NonNull { unsafe { NonNull { pointer: self.as_ptr() as *mut U } } } + /// Try to cast to a pointer of another type by checking aligment. + /// + /// If the pointer is properly aligned to the target type, it will be + /// cast to the target type. Otherwise, `None` is returned. + /// + /// # Examples + /// + /// ```rust + /// #![feature(pointer_try_cast_aligned)] + /// use std::ptr::NonNull; + /// + /// let aligned: NonNull = NonNull::new(0x1000 as _).unwrap(); + /// + /// // i32 has at most 4-byte alignment, so this will succeed + /// assert!(aligned.try_cast_aligned::().is_some()); + /// + /// let unaligned: NonNull = NonNull::new(0x1001 as _).unwrap(); + /// + /// // i32 has at least 2-byte alignment, so this will fail + /// assert!(unaligned.try_cast_aligned::().is_none()); + /// ``` + #[unstable(feature = "pointer_try_cast_aligned", issue = "141221")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn try_cast_aligned(self) -> Option> { + if self.is_aligned_to(align_of::()) { Some(self.cast()) } else { None } + } + /// Adds an offset to a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer @@ -724,9 +754,9 @@ impl NonNull { } /// Calculates the distance between two pointers within the same allocation. The returned value is in - /// units of T: the distance in bytes divided by `mem::size_of::()`. + /// units of T: the distance in bytes divided by `size_of::()`. /// - /// This is equivalent to `(self as isize - origin as isize) / (mem::size_of::() as isize)`, + /// This is equivalent to `(self as isize - origin as isize) / (size_of::() as isize)`, /// except that it has a lot more opportunities for UB, in exchange for the compiler /// better understanding what you are doing. /// @@ -762,7 +792,7 @@ impl NonNull { /// objects is not known at compile-time. However, the requirement also exists at /// runtime and may be exploited by optimizations. If you wish to compute the difference between /// pointers that are not guaranteed to be from the same allocation, use `(self as isize - - /// origin as isize) / mem::size_of::()`. + /// origin as isize) / size_of::()`. // FIXME: recommend `addr()` instead of `as usize` once that is stable. /// /// [`add`]: #method.add @@ -842,7 +872,7 @@ impl NonNull { /// Calculates the distance between two pointers within the same allocation, *where it's known that /// `self` is equal to or greater than `origin`*. The returned value is in - /// units of T: the distance in bytes is divided by `mem::size_of::()`. + /// units of T: the distance in bytes is divided by `size_of::()`. /// /// This computes the same value that [`offset_from`](#method.offset_from) /// would compute, but with the added precondition that the offset is @@ -901,13 +931,13 @@ impl NonNull { /// ``` #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "ptr_sub_ptr", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ptr_sub_ptr", since = "1.87.0")] + #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "1.87.0")] pub const unsafe fn offset_from_unsigned(self, subtracted: NonNull) -> usize where T: Sized, { - // SAFETY: the caller must uphold the safety contract for `sub_ptr`. + // SAFETY: the caller must uphold the safety contract for `offset_from_unsigned`. unsafe { self.as_ptr().offset_from_unsigned(subtracted.as_ptr()) } } @@ -916,17 +946,17 @@ impl NonNull { /// units of **bytes**. /// /// This is purely a convenience for casting to a `u8` pointer and - /// using [`sub_ptr`][NonNull::offset_from_unsigned] on it. See that method for - /// documentation and safety requirements. + /// using [`offset_from_unsigned`][NonNull::offset_from_unsigned] on it. + /// See that method for documentation and safety requirements. /// /// For non-`Sized` pointees this operation considers only the data pointers, /// ignoring the metadata. #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "ptr_sub_ptr", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ptr_sub_ptr", since = "1.87.0")] + #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "1.87.0")] pub const unsafe fn byte_offset_from_unsigned(self, origin: NonNull) -> usize { - // SAFETY: the caller must uphold the safety contract for `byte_sub_ptr`. + // SAFETY: the caller must uphold the safety contract for `byte_offset_from_unsigned`. unsafe { self.as_ptr().byte_offset_from_unsigned(origin.as_ptr()) } } @@ -989,7 +1019,7 @@ impl NonNull { unsafe { ptr::read_unaligned(self.as_ptr()) } } - /// Copies `count * size_of` bytes from `self` to `dest`. The source + /// 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`]. @@ -1009,7 +1039,7 @@ impl NonNull { unsafe { ptr::copy(self.as_ptr(), dest.as_ptr(), count) } } - /// Copies `count * size_of` bytes from `self` to `dest`. The source + /// 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`]. @@ -1029,7 +1059,7 @@ impl NonNull { unsafe { ptr::copy_nonoverlapping(self.as_ptr(), dest.as_ptr(), count) } } - /// Copies `count * size_of` bytes from `src` to `self`. The source + /// 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`]. @@ -1049,7 +1079,7 @@ impl NonNull { unsafe { ptr::copy(src.as_ptr(), self.as_ptr(), count) } } - /// Copies `count * size_of` bytes from `src` to `self`. The source + /// 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`]. @@ -1167,7 +1197,8 @@ impl NonNull { /// [`ptr::replace`]: crate::ptr::replace() #[inline(always)] #[stable(feature = "non_null_convenience", since = "1.80.0")] - pub unsafe fn replace(self, src: T) -> T + #[rustc_const_stable(feature = "const_inherent_ptr_replace", since = "1.88.0")] + pub const unsafe fn replace(self, src: T) -> T where T: Sized, { @@ -1223,7 +1254,6 @@ impl NonNull { /// Accessing adjacent `u8` as `u16` /// /// ``` - /// use std::mem::align_of; /// use std::ptr::NonNull; /// /// # unsafe { @@ -1443,7 +1473,7 @@ impl NonNull<[T]> { /// /// 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, + /// * The pointer must be [valid] for reads for `ptr.len() * 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! @@ -1455,7 +1485,7 @@ impl NonNull<[T]> { /// 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`. + /// * The total size `ptr.len() * 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 @@ -1488,7 +1518,7 @@ impl NonNull<[T]> { /// /// 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::()` + /// * The pointer must be [valid] for reads and writes for `ptr.len() * 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! @@ -1500,7 +1530,7 @@ impl NonNull<[T]> { /// 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`. + /// * The total size `ptr.len() * 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 diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index 4810ebe01f9bb..d688ce2a07a6a 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -100,6 +100,12 @@ impl Unique { } } + /// Create a new `Unique` from a `NonNull` in const context. + #[inline] + pub const fn from_non_null(pointer: NonNull) -> Self { + Unique { pointer, _marker: PhantomData } + } + /// Acquires the underlying `*mut` pointer. #[must_use = "`self` will be dropped if the result is not used"] #[inline] @@ -202,6 +208,6 @@ impl From> for Unique { /// This conversion is infallible since `NonNull` cannot be null. #[inline] fn from(pointer: NonNull) -> Self { - Unique { pointer, _marker: PhantomData } + Unique::from_non_null(pointer) } } diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 92b5cba153166..736ffb7d0caf3 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -259,8 +259,14 @@ //! The [`is_ok`] and [`is_err`] methods return [`true`] if the [`Result`] //! is [`Ok`] or [`Err`], respectively. //! +//! The [`is_ok_and`] and [`is_err_and`] methods apply the provided function +//! to the contents of the [`Result`] to produce a boolean value. If the [`Result`] does not have the expected variant +//! then [`false`] is returned instead without executing the function. +//! //! [`is_err`]: Result::is_err //! [`is_ok`]: Result::is_ok +//! [`is_ok_and`]: Result::is_ok_and +//! [`is_err_and`]: Result::is_err_and //! //! ## Adapters for working with references //! @@ -287,6 +293,7 @@ //! (which must implement the [`Default`] trait) //! * [`unwrap_or_else`] returns the result of evaluating the provided //! function +//! * [`unwrap_unchecked`] produces *[undefined behavior]* //! //! The panicking methods [`expect`] and [`unwrap`] require `E` to //! implement the [`Debug`] trait. @@ -297,6 +304,8 @@ //! [`unwrap_or`]: Result::unwrap_or //! [`unwrap_or_default`]: Result::unwrap_or_default //! [`unwrap_or_else`]: Result::unwrap_or_else +//! [`unwrap_unchecked`]: Result::unwrap_unchecked +//! [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html //! //! These methods extract the contained value in a [`Result`] when it //! is the [`Err`] variant. They require `T` to implement the [`Debug`] @@ -304,10 +313,13 @@ //! //! * [`expect_err`] panics with a provided custom message //! * [`unwrap_err`] panics with a generic message +//! * [`unwrap_err_unchecked`] produces *[undefined behavior]* //! //! [`Debug`]: crate::fmt::Debug //! [`expect_err`]: Result::expect_err //! [`unwrap_err`]: Result::unwrap_err +//! [`unwrap_err_unchecked`]: Result::unwrap_err_unchecked +//! [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html //! //! ## Transforming contained values //! @@ -330,21 +342,29 @@ //! [`Some(v)`]: Option::Some //! [`transpose`]: Result::transpose //! -//! This method transforms the contained value of the [`Ok`] variant: +//! These methods transform the contained value of the [`Ok`] variant: //! //! * [`map`] transforms [`Result`] into [`Result`] by applying //! the provided function to the contained value of [`Ok`] and leaving //! [`Err`] values unchanged +//! * [`inspect`] takes ownership of the [`Result`], applies the +//! provided function to the contained value by reference, +//! and then returns the [`Result`] //! //! [`map`]: Result::map +//! [`inspect`]: Result::inspect //! -//! This method transforms the contained value of the [`Err`] variant: +//! These methods transform the contained value of the [`Err`] variant: //! //! * [`map_err`] transforms [`Result`] into [`Result`] by //! applying the provided function to the contained value of [`Err`] and //! leaving [`Ok`] values unchanged +//! * [`inspect_err`] takes ownership of the [`Result`], applies the +//! provided function to the contained value of [`Err`] by reference, +//! and then returns the [`Result`] //! //! [`map_err`]: Result::map_err +//! [`inspect_err`]: Result::inspect_err //! //! These methods transform a [`Result`] into a value of a possibly //! different type `U`: @@ -578,6 +598,10 @@ impl Result { /// /// let x: Result = Err("hey"); /// assert_eq!(x.is_ok_and(|x| x > 1), false); + /// + /// let x: Result = Ok("ownership".to_string()); + /// assert_eq!(x.as_ref().is_ok_and(|x| x.len() > 1), true); + /// println!("still alive {:?}", x); /// ``` #[must_use] #[inline] @@ -623,6 +647,10 @@ impl Result { /// /// let x: Result = Ok(123); /// assert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false); + /// + /// let x: Result = Err("ownership".to_string()); + /// assert_eq!(x.as_ref().is_err_and(|x| x.len() > 1), true); + /// println!("still alive {:?}", x); /// ``` #[must_use] #[inline] @@ -654,7 +682,7 @@ impl Result { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "result_ok_method")] + #[rustc_diagnostic_item = "result_ok_method"] pub fn ok(self) -> Option { match self { Ok(x) => Some(x), @@ -1744,6 +1772,14 @@ where } } +#[unstable(feature = "ergonomic_clones", issue = "132290")] +impl crate::clone::UseCloned for Result +where + T: crate::clone::UseCloned, + E: crate::clone::UseCloned, +{ +} + #[stable(feature = "rust1", since = "1.0.0")] impl IntoIterator for Result { type Item = T; diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 51b25fa40e3d9..91befdb8c78d9 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -7,7 +7,6 @@ use crate::fmt::{self, Write}; use crate::intrinsics::const_eval_select; use crate::{ascii, iter, ops}; -#[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")] diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 9cb00644e6442..5ce72b46eee36 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -1,10 +1,11 @@ //! Comparison traits for `[T]`. use super::{from_raw_parts, memchr}; +use crate::ascii; use crate::cmp::{self, BytewiseEq, Ordering}; use crate::intrinsics::compare_bytes; use crate::num::NonZero; -use crate::{ascii, mem}; +use crate::ops::ControlFlow; #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq<[U]> for [T] @@ -31,12 +32,64 @@ impl Ord for [T] { } } +#[inline] +fn as_underlying(x: ControlFlow) -> u8 { + // SAFETY: This will only compile if `bool` and `ControlFlow` have the same + // size (which isn't guaranteed but this is libcore). Because they have the same + // size, it's a niched implementation, which in one byte means there can't be + // any uninitialized memory. The callers then only check for `0` or `1` from this, + // which must necessarily match the `Break` variant, and we're fine no matter + // what ends up getting picked as the value representing `Continue(())`. + unsafe { crate::mem::transmute(x) } +} + /// Implements comparison of slices [lexicographically](Ord#lexicographical-comparison). #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for [T] { + #[inline] fn partial_cmp(&self, other: &[T]) -> Option { SlicePartialOrd::partial_compare(self, other) } + #[inline] + fn lt(&self, other: &Self) -> bool { + // This is certainly not the obvious way to implement these methods. + // Unfortunately, using anything that looks at the discriminant means that + // LLVM sees a check for `2` (aka `ControlFlow::Continue(())`) and + // gets very distracted by that, ending up generating extraneous code. + // This should be changed to something simpler once either LLVM is smarter, + // see , or we generate + // niche discriminant checks in a way that doesn't trigger it. + + as_underlying(self.__chaining_lt(other)) == 1 + } + #[inline] + fn le(&self, other: &Self) -> bool { + as_underlying(self.__chaining_le(other)) != 0 + } + #[inline] + fn gt(&self, other: &Self) -> bool { + as_underlying(self.__chaining_gt(other)) == 1 + } + #[inline] + fn ge(&self, other: &Self) -> bool { + as_underlying(self.__chaining_ge(other)) != 0 + } + #[inline] + fn __chaining_lt(&self, other: &Self) -> ControlFlow { + SliceChain::chaining_lt(self, other) + } + #[inline] + fn __chaining_le(&self, other: &Self) -> ControlFlow { + SliceChain::chaining_le(self, other) + } + #[inline] + fn __chaining_gt(&self, other: &Self) -> ControlFlow { + SliceChain::chaining_gt(self, other) + } + #[inline] + fn __chaining_ge(&self, other: &Self) -> ControlFlow { + SliceChain::chaining_ge(self, other) + } } #[doc(hidden)] @@ -87,7 +140,7 @@ where // 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); + let size = size_of_val(self); compare_bytes(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0 } } @@ -99,24 +152,63 @@ trait SlicePartialOrd: Sized { fn partial_compare(left: &[Self], right: &[Self]) -> Option; } +#[doc(hidden)] +// intermediate trait for specialization of slice's PartialOrd chaining methods +trait SliceChain: Sized { + fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow; + fn chaining_le(left: &[Self], right: &[Self]) -> ControlFlow; + fn chaining_gt(left: &[Self], right: &[Self]) -> ControlFlow; + fn chaining_ge(left: &[Self], right: &[Self]) -> ControlFlow; +} + +type AlwaysBreak = ControlFlow; + 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]; + let elem_chain = |a, b| match PartialOrd::partial_cmp(a, b) { + Some(Ordering::Equal) => ControlFlow::Continue(()), + non_eq => ControlFlow::Break(non_eq), + }; + let len_chain = |a: &_, b: &_| ControlFlow::Break(usize::partial_cmp(a, b)); + let AlwaysBreak::Break(b) = chaining_impl(left, right, elem_chain, len_chain); + b + } +} - for i in 0..l { - match lhs[i].partial_cmp(&rhs[i]) { - Some(Ordering::Equal) => (), - non_eq => return non_eq, - } - } +impl SliceChain for A { + default fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow { + chaining_impl(left, right, PartialOrd::__chaining_lt, usize::__chaining_lt) + } + default fn chaining_le(left: &[Self], right: &[Self]) -> ControlFlow { + chaining_impl(left, right, PartialOrd::__chaining_le, usize::__chaining_le) + } + default fn chaining_gt(left: &[Self], right: &[Self]) -> ControlFlow { + chaining_impl(left, right, PartialOrd::__chaining_gt, usize::__chaining_gt) + } + default fn chaining_ge(left: &[Self], right: &[Self]) -> ControlFlow { + chaining_impl(left, right, PartialOrd::__chaining_ge, usize::__chaining_ge) + } +} - left.len().partial_cmp(&right.len()) +#[inline] +fn chaining_impl<'l, 'r, A: PartialOrd, B, C>( + left: &'l [A], + right: &'r [A], + elem_chain: impl Fn(&'l A, &'r A) -> ControlFlow, + len_chain: impl for<'a> FnOnce(&'a usize, &'a usize) -> ControlFlow, +) -> ControlFlow { + 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 { + elem_chain(&lhs[i], &rhs[i])?; } + + len_chain(&left.len(), &right.len()) } // This is the impl that we would like to have. Unfortunately it's not sound. @@ -165,21 +257,13 @@ trait SliceOrd: Sized { 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()) + let elem_chain = |a, b| match Ord::cmp(a, b) { + Ordering::Equal => ControlFlow::Continue(()), + non_eq => ControlFlow::Break(non_eq), + }; + let len_chain = |a: &_, b: &_| ControlFlow::Break(usize::cmp(a, b)); + let AlwaysBreak::Break(b) = chaining_impl(left, right, elem_chain, len_chain); + b } } @@ -191,7 +275,7 @@ impl SliceOrd for A { /// * For every `x` and `y` of this type, `Ord(x, y)` must return the same /// value as `Ord::cmp(transmute::<_, u8>(x), transmute::<_, u8>(y))`. #[rustc_specialization_trait] -unsafe trait UnsignedBytewiseOrd {} +unsafe trait UnsignedBytewiseOrd: Ord {} unsafe impl UnsignedBytewiseOrd for bool {} unsafe impl UnsignedBytewiseOrd for u8 {} @@ -225,6 +309,38 @@ impl SliceOrd for A { } } +// Don't generate our own chaining loops for `memcmp`-able things either. +impl SliceChain for A { + #[inline] + fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow { + match SliceOrd::compare(left, right) { + Ordering::Equal => ControlFlow::Continue(()), + ne => ControlFlow::Break(ne.is_lt()), + } + } + #[inline] + fn chaining_le(left: &[Self], right: &[Self]) -> ControlFlow { + match SliceOrd::compare(left, right) { + Ordering::Equal => ControlFlow::Continue(()), + ne => ControlFlow::Break(ne.is_le()), + } + } + #[inline] + fn chaining_gt(left: &[Self], right: &[Self]) -> ControlFlow { + match SliceOrd::compare(left, right) { + Ordering::Equal => ControlFlow::Continue(()), + ne => ControlFlow::Break(ne.is_gt()), + } + } + #[inline] + fn chaining_ge(left: &[Self], right: &[Self]) -> ControlFlow { + match SliceOrd::compare(left, right) { + Ordering::Equal => ControlFlow::Continue(()), + ne => ControlFlow::Break(ne.is_ge()), + } + } +} + pub(super) trait SliceContains: Sized { fn slice_contains(&self, x: &[Self]) -> bool; } @@ -266,7 +382,7 @@ macro_rules! impl_slice_contains { fn slice_contains(&self, arr: &[$t]) -> bool { // Make our LANE_COUNT 4x the normal lane count (aiming for 128 bit vectors). // The compiler will nicely unroll it. - const LANE_COUNT: usize = 4 * (128 / (mem::size_of::<$t>() * 8)); + const LANE_COUNT: usize = 4 * (128 / (size_of::<$t>() * 8)); // SIMD let mut chunks = arr.chunks_exact(LANE_COUNT); for chunk in &mut chunks { @@ -282,4 +398,4 @@ macro_rules! impl_slice_contains { }; } -impl_slice_contains!(u16, u32, u64, i16, i32, i64, f32, f64, usize, isize); +impl_slice_contains!(u16, u32, u64, i16, i32, i64, f32, f64, usize, isize, char); diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index a687ed7129dc8..85a5e89a49eb3 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -93,9 +93,9 @@ unsafe impl Send for Iter<'_, T> {} impl<'a, T> Iter<'a, T> { #[inline] - pub(super) fn new(slice: &'a [T]) -> Self { + pub(super) const fn new(slice: &'a [T]) -> Self { let len = slice.len(); - let ptr: NonNull = NonNull::from(slice).cast(); + let ptr: NonNull = NonNull::from_ref(slice).cast(); // SAFETY: Similar to `IterMut::new`. unsafe { let end_or_len = @@ -218,9 +218,9 @@ unsafe impl Send for IterMut<'_, T> {} impl<'a, T> IterMut<'a, T> { #[inline] - pub(super) fn new(slice: &'a mut [T]) -> Self { + pub(super) const fn new(slice: &'a mut [T]) -> Self { let len = slice.len(); - let ptr: NonNull = NonNull::from(slice).cast(); + let ptr: NonNull = NonNull::from_mut(slice).cast(); // SAFETY: There are several things here: // // `ptr` has been obtained by `slice.as_ptr()` where `slice` is a valid @@ -1335,7 +1335,7 @@ pub struct Windows<'a, T: 'a> { impl<'a, T: 'a> Windows<'a, T> { #[inline] - pub(super) fn new(slice: &'a [T], size: NonZero) -> Self { + pub(super) const fn new(slice: &'a [T], size: NonZero) -> Self { Self { v: slice, size } } } @@ -1380,14 +1380,16 @@ impl<'a, T> Iterator for Windows<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option { - let (end, overflow) = self.size.get().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..]; + let size = self.size.get(); + if let Some(rest) = self.v.get(n..) + && let Some(nth) = rest.get(..size) + { + self.v = &rest[1..]; Some(nth) + } else { + // setting length to 0 is cheaper than overwriting the pointer when assigning &[] + self.v = &self.v[..0]; // cheaper than &[] + None } } @@ -1427,7 +1429,7 @@ impl<'a, T> DoubleEndedIterator for Windows<'a, T> { fn nth_back(&mut self, n: usize) -> Option { let (end, overflow) = self.v.len().overflowing_sub(n); if end < self.size.get() || overflow { - self.v = &[]; + self.v = &self.v[..0]; // cheaper than &[] None } else { let ret = &self.v[end - self.size.get()..end]; @@ -1487,7 +1489,7 @@ pub struct Chunks<'a, T: 'a> { impl<'a, T: 'a> Chunks<'a, T> { #[inline] - pub(super) fn new(slice: &'a [T], size: usize) -> Self { + pub(super) const fn new(slice: &'a [T], size: usize) -> Self { Self { v: slice, chunk_size: size } } } @@ -1536,17 +1538,15 @@ impl<'a, T> Iterator for Chunks<'a, T> { #[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..]; + // min(len) makes a wrong start harmless, but enables optimizing this to brachless code + let chunk_start = &self.v[start.min(self.v.len())..]; + let (nth, remainder) = chunk_start.split_at(self.chunk_size.min(chunk_start.len())); + if !overflow && start < self.v.len() { + self.v = remainder; Some(nth) + } else { + self.v = &self.v[..0]; // cheaper than &[] + None } } @@ -1609,7 +1609,7 @@ impl<'a, T> DoubleEndedIterator for Chunks<'a, T> { fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); if n >= len { - self.v = &[]; + self.v = &self.v[..0]; // cheaper than &[] None } else { let start = (len - 1 - n) * self.chunk_size; @@ -1677,7 +1677,7 @@ pub struct ChunksMut<'a, T: 'a> { impl<'a, T: 'a> ChunksMut<'a, T> { #[inline] - pub(super) fn new(slice: &'a mut [T], size: usize) -> Self { + pub(super) const fn new(slice: &'a mut [T], size: usize) -> Self { Self { v: slice, chunk_size: size, _marker: PhantomData } } } @@ -1863,7 +1863,7 @@ pub struct ChunksExact<'a, T: 'a> { impl<'a, T> ChunksExact<'a, T> { #[inline] - pub(super) fn new(slice: &'a [T], chunk_size: usize) -> Self { + pub(super) const fn new(slice: &'a [T], chunk_size: usize) -> Self { let rem = slice.len() % chunk_size; let fst_len = slice.len() - rem; // SAFETY: 0 <= fst_len <= slice.len() by construction above @@ -1933,7 +1933,7 @@ impl<'a, T> Iterator for ChunksExact<'a, T> { fn nth(&mut self, n: usize) -> Option { let (start, overflow) = n.overflowing_mul(self.chunk_size); if start >= self.v.len() || overflow { - self.v = &[]; + self.v = &self.v[..0]; // cheaper than &[] None } else { let (_, snd) = self.v.split_at(start); @@ -1971,7 +1971,7 @@ impl<'a, T> DoubleEndedIterator for ChunksExact<'a, T> { fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); if n >= len { - self.v = &[]; + self.v = &self.v[..0]; // cheaper than &[] None } else { let start = (len - 1 - n) * self.chunk_size; @@ -2043,7 +2043,7 @@ pub struct ChunksExactMut<'a, T: 'a> { impl<'a, T> ChunksExactMut<'a, T> { #[inline] - pub(super) fn new(slice: &'a mut [T], chunk_size: usize) -> Self { + pub(super) const fn new(slice: &'a mut [T], chunk_size: usize) -> Self { let rem = slice.len() % chunk_size; let fst_len = slice.len() - rem; // SAFETY: 0 <= fst_len <= slice.len() by construction above @@ -2210,7 +2210,7 @@ pub struct ArrayWindows<'a, T: 'a, const N: usize> { impl<'a, T: 'a, const N: usize> ArrayWindows<'a, T, N> { #[inline] - pub(super) fn new(slice: &'a [T]) -> Self { + pub(super) const fn new(slice: &'a [T]) -> Self { let num_windows = slice.len().saturating_sub(N - 1); Self { slice_head: slice.as_ptr(), num: num_windows, marker: PhantomData } } @@ -2334,8 +2334,9 @@ pub struct ArrayChunks<'a, T: 'a, const N: usize> { } impl<'a, T, const N: usize> ArrayChunks<'a, T, N> { + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] - pub(super) fn new(slice: &'a [T]) -> Self { + pub(super) const fn new(slice: &'a [T]) -> Self { let (array_slice, rem) = slice.as_chunks(); Self { iter: array_slice.iter(), rem } } @@ -2460,8 +2461,9 @@ pub struct ArrayChunksMut<'a, T: 'a, const N: usize> { } impl<'a, T, const N: usize> ArrayChunksMut<'a, T, N> { + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] - pub(super) fn new(slice: &'a mut [T]) -> Self { + pub(super) const fn new(slice: &'a mut [T]) -> Self { let (array_slice, rem) = slice.as_chunks_mut(); Self { iter: array_slice.iter_mut(), rem } } @@ -2579,7 +2581,7 @@ pub struct RChunks<'a, T: 'a> { impl<'a, T: 'a> RChunks<'a, T> { #[inline] - pub(super) fn new(slice: &'a [T], size: usize) -> Self { + pub(super) const fn new(slice: &'a [T], size: usize) -> Self { Self { v: slice, chunk_size: size } } } @@ -2635,7 +2637,7 @@ impl<'a, T> Iterator for RChunks<'a, T> { fn nth(&mut self, n: usize) -> Option { let (end, overflow) = n.overflowing_mul(self.chunk_size); if end >= self.v.len() || overflow { - self.v = &[]; + self.v = &self.v[..0]; // cheaper than &[] None } else { // Can't underflow because of the check above @@ -2692,7 +2694,7 @@ impl<'a, T> DoubleEndedIterator for RChunks<'a, T> { fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); if n >= len { - self.v = &[]; + self.v = &self.v[..0]; // cheaper than &[] None } else { // can't underflow because `n < len` @@ -2759,7 +2761,7 @@ pub struct RChunksMut<'a, T: 'a> { impl<'a, T: 'a> RChunksMut<'a, T> { #[inline] - pub(super) fn new(slice: &'a mut [T], size: usize) -> Self { + pub(super) const fn new(slice: &'a mut [T], size: usize) -> Self { Self { v: slice, chunk_size: size, _marker: PhantomData } } } @@ -2950,7 +2952,7 @@ pub struct RChunksExact<'a, T: 'a> { impl<'a, T> RChunksExact<'a, T> { #[inline] - pub(super) fn new(slice: &'a [T], chunk_size: usize) -> Self { + pub(super) const fn new(slice: &'a [T], chunk_size: usize) -> Self { let rem = slice.len() % chunk_size; // SAFETY: 0 <= rem <= slice.len() by construction above let (fst, snd) = unsafe { slice.split_at_unchecked(rem) }; @@ -2976,7 +2978,8 @@ impl<'a, T> RChunksExact<'a, T> { /// ``` #[must_use] #[stable(feature = "rchunks", since = "1.31.0")] - pub fn remainder(&self) -> &'a [T] { + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] + pub const fn remainder(&self) -> &'a [T] { self.rem } } @@ -3019,7 +3022,7 @@ impl<'a, T> Iterator for RChunksExact<'a, T> { fn nth(&mut self, n: usize) -> Option { let (end, overflow) = n.overflowing_mul(self.chunk_size); if end >= self.v.len() || overflow { - self.v = &[]; + self.v = &self.v[..0]; // cheaper than &[] None } else { let (fst, _) = self.v.split_at(self.v.len() - end); @@ -3058,7 +3061,7 @@ impl<'a, T> DoubleEndedIterator for RChunksExact<'a, T> { fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); if n >= len { - self.v = &[]; + self.v = &self.v[..0]; // cheaper than &[] None } else { // now that we know that `n` corresponds to a chunk, @@ -3132,7 +3135,7 @@ pub struct RChunksExactMut<'a, T: 'a> { impl<'a, T> RChunksExactMut<'a, T> { #[inline] - pub(super) fn new(slice: &'a mut [T], chunk_size: usize) -> Self { + pub(super) const fn new(slice: &'a mut [T], chunk_size: usize) -> Self { let rem = slice.len() % chunk_size; // SAFETY: 0 <= rem <= slice.len() by construction above let (fst, snd) = unsafe { slice.split_at_mut_unchecked(rem) }; @@ -3144,7 +3147,8 @@ impl<'a, T> RChunksExactMut<'a, T> { /// elements. #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "rchunks", since = "1.31.0")] - pub fn into_remainder(self) -> &'a mut [T] { + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] + pub const fn into_remainder(self) -> &'a mut [T] { self.rem } } @@ -3308,7 +3312,7 @@ pub struct ChunkBy<'a, T: 'a, P> { #[stable(feature = "slice_group_by", since = "1.77.0")] impl<'a, T: 'a, P> ChunkBy<'a, T, P> { - pub(super) fn new(slice: &'a [T], predicate: P) -> Self { + pub(super) const fn new(slice: &'a [T], predicate: P) -> Self { ChunkBy { slice, predicate } } } @@ -3395,7 +3399,7 @@ pub struct ChunkByMut<'a, T: 'a, P> { #[stable(feature = "slice_group_by", since = "1.77.0")] impl<'a, T: 'a, P> ChunkByMut<'a, T, P> { - pub(super) fn new(slice: &'a mut [T], predicate: P) -> Self { + pub(super) const fn new(slice: &'a mut [T], predicate: P) -> Self { ChunkByMut { slice, predicate } } } diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index 98db7aaf53321..1e1053583a617 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -2,11 +2,10 @@ // Copyright 2015 Andrew Gallant, bluss and Nicolas Koch use crate::intrinsics::const_eval_select; -use crate::mem; const LO_USIZE: usize = usize::repeat_u8(0x01); const HI_USIZE: usize = usize::repeat_u8(0x80); -const USIZE_BYTES: usize = mem::size_of::(); +const USIZE_BYTES: usize = size_of::(); /// Returns `true` if `x` contains any zero byte. /// @@ -138,7 +137,7 @@ pub fn memrchr(x: u8, text: &[u8]) -> Option { // offset is always aligned, so just testing `>` is sufficient and avoids possible // overflow. let repeated_x = usize::repeat_u8(x); - let chunk_bytes = mem::size_of::(); + let chunk_bytes = size_of::(); while offset > min_aligned_offset { // SAFETY: offset starts at len - suffix.len(), as long as it is greater than diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 7a2764206e8db..26520123a8d5f 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -8,7 +8,7 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::intrinsics::{exact_div, unchecked_sub}; -use crate::mem::{self, SizedTypeProperties}; +use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeInclusive}; use crate::panic::const_panic; @@ -78,7 +78,7 @@ pub use raw::{from_raw_parts, from_raw_parts_mut}; /// Calculates the direction and split point of a one-sided range. /// -/// This is a helper function for `take` and `take_mut` that returns +/// This is a helper function for `split_off` and `split_off_mut` that returns /// the direction of the split (front or back) as well as the index at /// which to split. Returns `None` if the split index would overflow. #[inline] @@ -97,7 +97,6 @@ enum Direction { Back, } -#[cfg(not(test))] impl [T] { /// Returns the number of elements in the slice. /// @@ -110,6 +109,7 @@ impl [T] { #[lang = "slice_len_fn"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_len", since = "1.39.0")] + #[rustc_no_implicit_autorefs] #[inline] #[must_use] pub const fn len(&self) -> usize { @@ -129,6 +129,7 @@ impl [T] { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_is_empty", since = "1.39.0")] + #[rustc_no_implicit_autorefs] #[inline] #[must_use] pub const fn is_empty(&self) -> bool { @@ -383,16 +384,11 @@ impl [T] { #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] #[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")] pub const fn split_first_chunk(&self) -> Option<(&[T; N], &[T])> { - if self.len() < N { - None - } else { - // SAFETY: We manually verified the bounds of the split. - let (first, tail) = unsafe { self.split_at_unchecked(N) }; + let Some((first, tail)) = self.split_at_checked(N) else { return None }; - // SAFETY: We explicitly check for the correct number of elements, - // and do not let the references outlive the slice. - Some((unsafe { &*(first.as_ptr().cast::<[T; N]>()) }, tail)) - } + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the references outlive the slice. + Some((unsafe { &*(first.as_ptr().cast::<[T; N]>()) }, tail)) } /// Returns a mutable array reference to the first `N` items in the slice and the remaining @@ -420,17 +416,12 @@ impl [T] { pub const fn split_first_chunk_mut( &mut self, ) -> Option<(&mut [T; N], &mut [T])> { - if self.len() < N { - None - } else { - // SAFETY: We manually verified the bounds of the split. - let (first, tail) = unsafe { self.split_at_mut_unchecked(N) }; + let Some((first, tail)) = self.split_at_mut_checked(N) else { return None }; - // SAFETY: We explicitly check for the correct number of elements, - // do not let the reference outlive the slice, - // and enforce exclusive mutability of the chunk by the split. - Some((unsafe { &mut *(first.as_mut_ptr().cast::<[T; N]>()) }, tail)) - } + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and enforce exclusive mutability of the chunk by the split. + Some((unsafe { &mut *(first.as_mut_ptr().cast::<[T; N]>()) }, tail)) } /// Returns an array reference to the last `N` items in the slice and the remaining slice. @@ -453,16 +444,12 @@ impl [T] { #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] #[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")] pub const fn split_last_chunk(&self) -> Option<(&[T], &[T; N])> { - if self.len() < N { - None - } else { - // SAFETY: We manually verified the bounds of the split. - let (init, last) = unsafe { self.split_at_unchecked(self.len() - N) }; + let Some(index) = self.len().checked_sub(N) else { return None }; + let (init, last) = self.split_at(index); - // SAFETY: We explicitly check for the correct number of elements, - // and do not let the references outlive the slice. - Some((init, unsafe { &*(last.as_ptr().cast::<[T; N]>()) })) - } + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the references outlive the slice. + Some((init, unsafe { &*(last.as_ptr().cast::<[T; N]>()) })) } /// Returns a mutable array reference to the last `N` items in the slice and the remaining @@ -490,17 +477,13 @@ impl [T] { pub const fn split_last_chunk_mut( &mut self, ) -> Option<(&mut [T], &mut [T; N])> { - if self.len() < N { - None - } else { - // SAFETY: We manually verified the bounds of the split. - let (init, last) = unsafe { self.split_at_mut_unchecked(self.len() - N) }; + let Some(index) = self.len().checked_sub(N) else { return None }; + let (init, last) = self.split_at_mut(index); - // SAFETY: We explicitly check for the correct number of elements, - // do not let the reference outlive the slice, - // and enforce exclusive mutability of the chunk by the split. - Some((init, unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) })) - } + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and enforce exclusive mutability of the chunk by the split. + Some((init, unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) })) } /// Returns an array reference to the last `N` items in the slice. @@ -523,17 +506,13 @@ impl [T] { #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] #[rustc_const_stable(feature = "const_slice_last_chunk", since = "1.80.0")] pub const fn last_chunk(&self) -> Option<&[T; N]> { - if self.len() < N { - None - } else { - // SAFETY: We manually verified the bounds of the slice. - // FIXME(const-hack): Without const traits, we need this instead of `get_unchecked`. - let last = unsafe { self.split_at_unchecked(self.len() - N).1 }; + // FIXME(const-hack): Without const traits, we need this instead of `get`. + let Some(index) = self.len().checked_sub(N) else { return None }; + let (_, last) = self.split_at(index); - // SAFETY: We explicitly check for the correct number of elements, - // and do not let the references outlive the slice. - Some(unsafe { &*(last.as_ptr().cast::<[T; N]>()) }) - } + // SAFETY: We explicitly check for the correct number of elements, + // and do not let the references outlive the slice. + Some(unsafe { &*(last.as_ptr().cast::<[T; N]>()) }) } /// Returns a mutable array reference to the last `N` items in the slice. @@ -557,18 +536,14 @@ impl [T] { #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] #[rustc_const_stable(feature = "const_slice_first_last_chunk", since = "1.83.0")] pub const fn last_chunk_mut(&mut self) -> Option<&mut [T; N]> { - if self.len() < N { - None - } else { - // SAFETY: We manually verified the bounds of the slice. - // FIXME(const-hack): Without const traits, we need this instead of `get_unchecked`. - let last = unsafe { self.split_at_mut_unchecked(self.len() - N).1 }; + // FIXME(const-hack): Without const traits, we need this instead of `get`. + let Some(index) = self.len().checked_sub(N) else { return None }; + let (_, last) = self.split_at_mut(index); - // SAFETY: We explicitly check for the correct number of elements, - // do not let the reference outlive the slice, - // and require exclusive access to the entire slice to mutate the chunk. - Some(unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) }) - } + // SAFETY: We explicitly check for the correct number of elements, + // do not let the reference outlive the slice, + // and require exclusive access to the entire slice to mutate the chunk. + Some(unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) }) } /// Returns a reference to an element or subslice depending on the type of @@ -589,6 +564,7 @@ impl [T] { /// assert_eq!(None, v.get(0..4)); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_no_implicit_autorefs] #[inline] #[must_use] pub fn get(&self, index: I) -> Option<&I::Output> @@ -614,6 +590,7 @@ impl [T] { /// assert_eq!(x, &[0, 42, 2]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_no_implicit_autorefs] #[inline] #[must_use] pub fn get_mut(&mut self, index: I) -> Option<&mut I::Output> @@ -651,6 +628,7 @@ impl [T] { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_no_implicit_autorefs] #[inline] #[must_use] pub unsafe fn get_unchecked(&self, index: I) -> &I::Output @@ -693,6 +671,7 @@ impl [T] { /// assert_eq!(x, &[1, 13, 4]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_no_implicit_autorefs] #[inline] #[must_use] pub unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output @@ -1044,9 +1023,10 @@ impl [T] { /// assert_eq!(iterator.next(), None); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] - #[cfg_attr(not(test), rustc_diagnostic_item = "slice_iter")] - pub fn iter(&self) -> Iter<'_, T> { + #[rustc_diagnostic_item = "slice_iter"] + pub const fn iter(&self) -> Iter<'_, T> { Iter::new(self) } @@ -1063,9 +1043,10 @@ impl [T] { /// } /// assert_eq!(x, &[3, 4, 6]); /// ``` + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn iter_mut(&mut self) -> IterMut<'_, T> { + pub const fn iter_mut(&mut self) -> IterMut<'_, T> { IterMut::new(self) } @@ -1117,9 +1098,10 @@ impl [T] { /// assert_eq!(array, ['s', 't', ' ', '2', '0', '1', '5', 'u', 'R']); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] - pub fn windows(&self, size: usize) -> Windows<'_, T> { + pub const fn windows(&self, size: usize) -> Windows<'_, T> { let size = NonZero::new(size).expect("window size must be non-zero"); Windows::new(self, size) } @@ -1152,9 +1134,10 @@ impl [T] { /// [`chunks_exact`]: slice::chunks_exact /// [`rchunks`]: slice::rchunks #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] - pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T> { + pub const fn chunks(&self, chunk_size: usize) -> Chunks<'_, T> { assert!(chunk_size != 0, "chunk size must be non-zero"); Chunks::new(self, chunk_size) } @@ -1191,9 +1174,10 @@ impl [T] { /// [`chunks_exact_mut`]: slice::chunks_exact_mut /// [`rchunks_mut`]: slice::rchunks_mut #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] - pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T> { + pub const fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T> { assert!(chunk_size != 0, "chunk size must be non-zero"); ChunksMut::new(self, chunk_size) } @@ -1229,9 +1213,10 @@ impl [T] { /// [`chunks`]: slice::chunks /// [`rchunks_exact`]: slice::rchunks_exact #[stable(feature = "chunks_exact", since = "1.31.0")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] - pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> { + pub const fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> { assert!(chunk_size != 0, "chunk size must be non-zero"); ChunksExact::new(self, chunk_size) } @@ -1272,9 +1257,10 @@ impl [T] { /// [`chunks_mut`]: slice::chunks_mut /// [`rchunks_exact_mut`]: slice::rchunks_exact_mut #[stable(feature = "chunks_exact", since = "1.31.0")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] - pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> { + pub const fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> { assert!(chunk_size != 0, "chunk size must be non-zero"); ChunksExactMut::new(self, chunk_size) } @@ -1282,6 +1268,18 @@ impl [T] { /// Splits the slice into a slice of `N`-element arrays, /// assuming that there's no remainder. /// + /// This is the inverse operation to [`as_flattened`]. + /// + /// [`as_flattened`]: slice::as_flattened + /// + /// As this is `unsafe`, consider whether you could use [`as_chunks`] or + /// [`as_rchunks`] instead, perhaps via something like + /// `if let (chunks, []) = slice.as_chunks()` or + /// `let (chunks, []) = slice.as_chunks() else { unreachable!() };`. + /// + /// [`as_chunks`]: slice::as_chunks + /// [`as_rchunks`]: slice::as_rchunks + /// /// # Safety /// /// This may only be called when @@ -1291,7 +1289,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_as_chunks)] /// let slice: &[char] = &['l', 'o', 'r', 'e', 'm', '!']; /// let chunks: &[[char; 1]] = /// // SAFETY: 1-element chunks never have remainder @@ -1306,8 +1303,8 @@ impl [T] { /// // let chunks: &[[_; 5]] = slice.as_chunks_unchecked() // The slice length is not a multiple of 5 /// // let chunks: &[[_; 0]] = slice.as_chunks_unchecked() // Zero-length chunks are never allowed /// ``` - #[unstable(feature = "slice_as_chunks", issue = "74985")] - #[rustc_const_unstable(feature = "slice_as_chunks", issue = "74985")] + #[stable(feature = "slice_as_chunks", since = "1.88.0")] + #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[must_use] pub const unsafe fn as_chunks_unchecked(&self) -> &[[T; N]] { @@ -1327,15 +1324,27 @@ impl [T] { /// starting at the beginning of the slice, /// and a remainder slice with length strictly less than `N`. /// + /// The remainder is meaningful in the division sense. Given + /// `let (chunks, remainder) = slice.as_chunks()`, then: + /// - `chunks.len()` equals `slice.len() / N`, + /// - `remainder.len()` equals `slice.len() % N`, and + /// - `slice.len()` equals `chunks.len() * N + remainder.len()`. + /// + /// You can flatten the chunks back into a slice-of-`T` with [`as_flattened`]. + /// + /// [`as_flattened`]: slice::as_flattened + /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. + /// Panics if `N` is zero. + /// + /// Note that this check is against a const generic parameter, not a runtime + /// value, and thus a particular monomorphization will either always panic + /// or it will never panic. /// /// # Examples /// /// ``` - /// #![feature(slice_as_chunks)] /// let slice = ['l', 'o', 'r', 'e', 'm']; /// let (chunks, remainder) = slice.as_chunks(); /// assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]); @@ -1345,15 +1354,14 @@ impl [T] { /// If you expect the slice to be an exact multiple, you can combine /// `let`-`else` with an empty slice pattern: /// ``` - /// #![feature(slice_as_chunks)] /// let slice = ['R', 'u', 's', 't']; /// let (chunks, []) = slice.as_chunks::<2>() else { /// panic!("slice didn't have even length") /// }; /// assert_eq!(chunks, &[['R', 'u'], ['s', 't']]); /// ``` - #[unstable(feature = "slice_as_chunks", issue = "74985")] - #[rustc_const_unstable(feature = "slice_as_chunks", issue = "74985")] + #[stable(feature = "slice_as_chunks", since = "1.88.0")] + #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[track_caller] #[must_use] @@ -1373,22 +1381,34 @@ impl [T] { /// starting at the end of the slice, /// and a remainder slice with length strictly less than `N`. /// + /// The remainder is meaningful in the division sense. Given + /// `let (remainder, chunks) = slice.as_rchunks()`, then: + /// - `remainder.len()` equals `slice.len() % N`, + /// - `chunks.len()` equals `slice.len() / N`, and + /// - `slice.len()` equals `chunks.len() * N + remainder.len()`. + /// + /// You can flatten the chunks back into a slice-of-`T` with [`as_flattened`]. + /// + /// [`as_flattened`]: slice::as_flattened + /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. + /// Panics if `N` is zero. + /// + /// Note that this check is against a const generic parameter, not a runtime + /// value, and thus a particular monomorphization will either always panic + /// or it will never panic. /// /// # Examples /// /// ``` - /// #![feature(slice_as_chunks)] /// let slice = ['l', 'o', 'r', 'e', 'm']; /// let (remainder, chunks) = slice.as_rchunks(); /// assert_eq!(remainder, &['l']); /// assert_eq!(chunks, &[['o', 'r'], ['e', 'm']]); /// ``` - #[unstable(feature = "slice_as_chunks", issue = "74985")] - #[rustc_const_unstable(feature = "slice_as_chunks", issue = "74985")] + #[stable(feature = "slice_as_chunks", since = "1.88.0")] + #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[track_caller] #[must_use] @@ -1430,9 +1450,10 @@ impl [T] { /// /// [`chunks_exact`]: slice::chunks_exact #[unstable(feature = "array_chunks", issue = "74985")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] - pub fn array_chunks(&self) -> ArrayChunks<'_, T, N> { + pub const fn array_chunks(&self) -> ArrayChunks<'_, T, N> { assert!(N != 0, "chunk size must be non-zero"); ArrayChunks::new(self) } @@ -1440,6 +1461,18 @@ impl [T] { /// Splits the slice into a slice of `N`-element arrays, /// assuming that there's no remainder. /// + /// This is the inverse operation to [`as_flattened_mut`]. + /// + /// [`as_flattened_mut`]: slice::as_flattened_mut + /// + /// As this is `unsafe`, consider whether you could use [`as_chunks_mut`] or + /// [`as_rchunks_mut`] instead, perhaps via something like + /// `if let (chunks, []) = slice.as_chunks_mut()` or + /// `let (chunks, []) = slice.as_chunks_mut() else { unreachable!() };`. + /// + /// [`as_chunks_mut`]: slice::as_chunks_mut + /// [`as_rchunks_mut`]: slice::as_rchunks_mut + /// /// # Safety /// /// This may only be called when @@ -1449,7 +1482,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_as_chunks)] /// let slice: &mut [char] = &mut ['l', 'o', 'r', 'e', 'm', '!']; /// let chunks: &mut [[char; 1]] = /// // SAFETY: 1-element chunks never have remainder @@ -1466,8 +1498,8 @@ impl [T] { /// // let chunks: &[[_; 5]] = slice.as_chunks_unchecked_mut() // The slice length is not a multiple of 5 /// // let chunks: &[[_; 0]] = slice.as_chunks_unchecked_mut() // Zero-length chunks are never allowed /// ``` - #[unstable(feature = "slice_as_chunks", issue = "74985")] - #[rustc_const_unstable(feature = "slice_as_chunks", issue = "74985")] + #[stable(feature = "slice_as_chunks", since = "1.88.0")] + #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[must_use] pub const unsafe fn as_chunks_unchecked_mut(&mut self) -> &mut [[T; N]] { @@ -1487,15 +1519,27 @@ impl [T] { /// starting at the beginning of the slice, /// and a remainder slice with length strictly less than `N`. /// + /// The remainder is meaningful in the division sense. Given + /// `let (chunks, remainder) = slice.as_chunks_mut()`, then: + /// - `chunks.len()` equals `slice.len() / N`, + /// - `remainder.len()` equals `slice.len() % N`, and + /// - `slice.len()` equals `chunks.len() * N + remainder.len()`. + /// + /// You can flatten the chunks back into a slice-of-`T` with [`as_flattened_mut`]. + /// + /// [`as_flattened_mut`]: slice::as_flattened_mut + /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. + /// Panics if `N` is zero. + /// + /// Note that this check is against a const generic parameter, not a runtime + /// value, and thus a particular monomorphization will either always panic + /// or it will never panic. /// /// # Examples /// /// ``` - /// #![feature(slice_as_chunks)] /// let v = &mut [0, 0, 0, 0, 0]; /// let mut count = 1; /// @@ -1507,8 +1551,8 @@ impl [T] { /// } /// assert_eq!(v, &[1, 1, 2, 2, 9]); /// ``` - #[unstable(feature = "slice_as_chunks", issue = "74985")] - #[rustc_const_unstable(feature = "slice_as_chunks", issue = "74985")] + #[stable(feature = "slice_as_chunks", since = "1.88.0")] + #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[track_caller] #[must_use] @@ -1528,15 +1572,27 @@ impl [T] { /// starting at the end of the slice, /// and a remainder slice with length strictly less than `N`. /// + /// The remainder is meaningful in the division sense. Given + /// `let (remainder, chunks) = slice.as_rchunks_mut()`, then: + /// - `remainder.len()` equals `slice.len() % N`, + /// - `chunks.len()` equals `slice.len() / N`, and + /// - `slice.len()` equals `chunks.len() * N + remainder.len()`. + /// + /// You can flatten the chunks back into a slice-of-`T` with [`as_flattened_mut`]. + /// + /// [`as_flattened_mut`]: slice::as_flattened_mut + /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. + /// Panics if `N` is zero. + /// + /// Note that this check is against a const generic parameter, not a runtime + /// value, and thus a particular monomorphization will either always panic + /// or it will never panic. /// /// # Examples /// /// ``` - /// #![feature(slice_as_chunks)] /// let v = &mut [0, 0, 0, 0, 0]; /// let mut count = 1; /// @@ -1548,8 +1604,8 @@ impl [T] { /// } /// assert_eq!(v, &[9, 1, 1, 2, 2]); /// ``` - #[unstable(feature = "slice_as_chunks", issue = "74985")] - #[rustc_const_unstable(feature = "slice_as_chunks", issue = "74985")] + #[stable(feature = "slice_as_chunks", since = "1.88.0")] + #[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")] #[inline] #[track_caller] #[must_use] @@ -1593,9 +1649,10 @@ impl [T] { /// /// [`chunks_exact_mut`]: slice::chunks_exact_mut #[unstable(feature = "array_chunks", issue = "74985")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] - pub fn array_chunks_mut(&mut self) -> ArrayChunksMut<'_, T, N> { + pub const fn array_chunks_mut(&mut self) -> ArrayChunksMut<'_, T, N> { assert!(N != 0, "chunk size must be non-zero"); ArrayChunksMut::new(self) } @@ -1626,9 +1683,10 @@ impl [T] { /// /// [`windows`]: slice::windows #[unstable(feature = "array_windows", issue = "75027")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] - pub fn array_windows(&self) -> ArrayWindows<'_, T, N> { + pub const fn array_windows(&self) -> ArrayWindows<'_, T, N> { assert!(N != 0, "window size must be non-zero"); ArrayWindows::new(self) } @@ -1661,9 +1719,10 @@ impl [T] { /// [`rchunks_exact`]: slice::rchunks_exact /// [`chunks`]: slice::chunks #[stable(feature = "rchunks", since = "1.31.0")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] - pub fn rchunks(&self, chunk_size: usize) -> RChunks<'_, T> { + pub const fn rchunks(&self, chunk_size: usize) -> RChunks<'_, T> { assert!(chunk_size != 0, "chunk size must be non-zero"); RChunks::new(self, chunk_size) } @@ -1700,9 +1759,10 @@ impl [T] { /// [`rchunks_exact_mut`]: slice::rchunks_exact_mut /// [`chunks_mut`]: slice::chunks_mut #[stable(feature = "rchunks", since = "1.31.0")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] - pub fn rchunks_mut(&mut self, chunk_size: usize) -> RChunksMut<'_, T> { + pub const fn rchunks_mut(&mut self, chunk_size: usize) -> RChunksMut<'_, T> { assert!(chunk_size != 0, "chunk size must be non-zero"); RChunksMut::new(self, chunk_size) } @@ -1740,9 +1800,10 @@ impl [T] { /// [`rchunks`]: slice::rchunks /// [`chunks_exact`]: slice::chunks_exact #[stable(feature = "rchunks", since = "1.31.0")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] - pub fn rchunks_exact(&self, chunk_size: usize) -> RChunksExact<'_, T> { + pub const fn rchunks_exact(&self, chunk_size: usize) -> RChunksExact<'_, T> { assert!(chunk_size != 0, "chunk size must be non-zero"); RChunksExact::new(self, chunk_size) } @@ -1784,9 +1845,10 @@ impl [T] { /// [`rchunks_mut`]: slice::rchunks_mut /// [`chunks_exact_mut`]: slice::chunks_exact_mut #[stable(feature = "rchunks", since = "1.31.0")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] - pub fn rchunks_exact_mut(&mut self, chunk_size: usize) -> RChunksExactMut<'_, T> { + pub const fn rchunks_exact_mut(&mut self, chunk_size: usize) -> RChunksExactMut<'_, T> { assert!(chunk_size != 0, "chunk size must be non-zero"); RChunksExactMut::new(self, chunk_size) } @@ -1824,8 +1886,9 @@ impl [T] { /// assert_eq!(iter.next(), None); /// ``` #[stable(feature = "slice_group_by", since = "1.77.0")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] - pub fn chunk_by(&self, pred: F) -> ChunkBy<'_, T, F> + pub const fn chunk_by(&self, pred: F) -> ChunkBy<'_, T, F> where F: FnMut(&T, &T) -> bool, { @@ -1865,8 +1928,9 @@ impl [T] { /// assert_eq!(iter.next(), None); /// ``` #[stable(feature = "slice_group_by", since = "1.77.0")] + #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] - pub fn chunk_by_mut(&mut self, pred: F) -> ChunkByMut<'_, T, F> + pub const fn chunk_by_mut(&mut self, pred: F) -> ChunkByMut<'_, T, F> where F: FnMut(&T, &T) -> bool, { @@ -2831,7 +2895,7 @@ impl [T] { let half = size / 2; let mid = base + half; - // SAFETY: the call is made safe by the following inconstants: + // SAFETY: the call is made safe by the following invariants: // - `mid >= 0`: by definition // - `mid < size`: `mid = size / 2 + size / 4 + size / 8 ...` let cmp = f(unsafe { self.get_unchecked(mid) }); @@ -2839,7 +2903,7 @@ impl [T] { // Binary search interacts poorly with branch prediction, so force // the compiler to use conditional moves if supported by the target // architecture. - base = (cmp == Greater).select_unpredictable(base, mid); + base = hint::select_unpredictable(cmp == Greater, base, mid); // This is imprecise in the case where `size` is odd and the // comparison returns Greater: the mid element still gets included @@ -2922,7 +2986,7 @@ impl [T] { self.binary_search_by(|k| f(k).cmp(b)) } - /// Sorts the slice **without** preserving the initial order of equal elements. + /// Sorts the slice in ascending order **without** preserving the initial order of equal elements. /// /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not /// allocate), and *O*(*n* \* log(*n*)) worst-case. @@ -2983,8 +3047,8 @@ impl [T] { sort::unstable::sort(self, &mut T::lt); } - /// Sorts the slice with a comparison function, **without** preserving the initial order of - /// equal elements. + /// Sorts the slice in ascending order with a comparison function, **without** preserving the + /// initial order of equal elements. /// /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not /// allocate), and *O*(*n* \* log(*n*)) worst-case. @@ -3038,8 +3102,8 @@ impl [T] { sort::unstable::sort(self, &mut |a, b| compare(a, b) == Ordering::Less); } - /// Sorts the slice with a key extraction function, **without** preserving the initial order of - /// equal elements. + /// Sorts the slice in ascending order with a key extraction function, **without** preserving + /// the initial order of equal elements. /// /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not /// allocate), and *O*(*n* \* log(*n*)) worst-case. @@ -3732,8 +3796,7 @@ impl [T] { #[doc(alias = "memcpy")] #[inline] #[stable(feature = "copy_from_slice", since = "1.9.0")] - #[rustc_const_unstable(feature = "const_copy_from_slice", issue = "131415")] - #[rustc_const_stable_indirect] + #[rustc_const_stable(feature = "const_copy_from_slice", since = "1.87.0")] #[track_caller] pub const fn copy_from_slice(&mut self, src: &[T]) where @@ -3894,9 +3957,9 @@ impl [T] { // Explicitly wrap the function call in a const block so it gets // constant-evaluated even in debug mode. - let gcd: usize = const { gcd(mem::size_of::(), mem::size_of::()) }; - let ts: usize = mem::size_of::() / gcd; - let us: usize = mem::size_of::() / gcd; + let gcd: usize = const { gcd(size_of::(), size_of::()) }; + let ts: usize = size_of::() / gcd; + let us: usize = size_of::() / gcd; // Armed with this knowledge, we can find how many `U`s we can fit! let us_len = self.len() / ts * us; @@ -3946,7 +4009,7 @@ impl [T] { // 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::()) }; + let offset = unsafe { crate::ptr::align_offset(ptr, align_of::()) }; if offset > self.len() { (self, &[], &[]) } else { @@ -3956,7 +4019,7 @@ impl [T] { #[cfg(miri)] crate::intrinsics::miri_promise_symbolic_alignment( rest.as_ptr().cast(), - mem::align_of::(), + align_of::(), ); // 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. @@ -4017,7 +4080,7 @@ impl [T] { // 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 alignment for U), // satisfying its safety constraints. - let offset = unsafe { crate::ptr::align_offset(ptr, mem::align_of::()) }; + let offset = unsafe { crate::ptr::align_offset(ptr, align_of::()) }; if offset > self.len() { (self, &mut [], &mut []) } else { @@ -4029,7 +4092,7 @@ impl [T] { #[cfg(miri)] crate::intrinsics::miri_promise_symbolic_alignment( mut_ptr.cast() as *const (), - mem::align_of::(), + align_of::(), ); // We can't use `rest` again after this, that would invalidate its alias `mut_ptr`! // SAFETY: see comments for `align_to`. @@ -4100,7 +4163,7 @@ impl [T] { // These are expected to always match, as vector types are laid out like // arrays per , but we // might as well double-check since it'll optimize away anyhow. - assert_eq!(mem::size_of::>(), mem::size_of::<[T; LANES]>()); + assert_eq!(size_of::>(), size_of::<[T; LANES]>()); // SAFETY: The simd types have the same layout as arrays, just with // potentially-higher alignment, so the de-facto transmutes are sound. @@ -4136,7 +4199,7 @@ impl [T] { // These are expected to always match, as vector types are laid out like // arrays per , but we // might as well double-check since it'll optimize away anyhow. - assert_eq!(mem::size_of::>(), mem::size_of::<[T; LANES]>()); + assert_eq!(size_of::>(), size_of::<[T; LANES]>()); // SAFETY: The simd types have the same layout as arrays, just with // potentially-higher alignment, so the de-facto transmutes are sound. @@ -4313,8 +4376,6 @@ impl [T] { /// Splitting off the first three elements of a slice: /// /// ``` - /// #![feature(slice_take)] - /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; /// let mut first_three = slice.split_off(..3).unwrap(); /// @@ -4322,11 +4383,9 @@ impl [T] { /// assert_eq!(first_three, &['a', 'b', 'c']); /// ``` /// - /// Splitting off the last two elements of a slice: + /// Splitting off a slice starting with the third element: /// /// ``` - /// #![feature(slice_take)] - /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; /// let mut tail = slice.split_off(2..).unwrap(); /// @@ -4337,8 +4396,6 @@ impl [T] { /// Getting `None` when `range` is out of bounds: /// /// ``` - /// #![feature(slice_take)] - /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; /// /// assert_eq!(None, slice.split_off(5..)); @@ -4349,7 +4406,7 @@ impl [T] { /// ``` #[inline] #[must_use = "method does not modify the slice if the range is out of bounds"] - #[unstable(feature = "slice_take", issue = "62280")] + #[stable(feature = "slice_take", since = "1.87.0")] pub fn split_off<'a, R: OneSidedRange>( self: &mut &'a Self, range: R, @@ -4385,8 +4442,6 @@ impl [T] { /// Splitting off the first three elements of a slice: /// /// ``` - /// #![feature(slice_take)] - /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; /// let mut first_three = slice.split_off_mut(..3).unwrap(); /// @@ -4394,11 +4449,9 @@ impl [T] { /// assert_eq!(first_three, &mut ['a', 'b', 'c']); /// ``` /// - /// Taking the last two elements of a slice: + /// Splitting off a slice starting with the third element: /// /// ``` - /// #![feature(slice_take)] - /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; /// let mut tail = slice.split_off_mut(2..).unwrap(); /// @@ -4409,8 +4462,6 @@ impl [T] { /// Getting `None` when `range` is out of bounds: /// /// ``` - /// #![feature(slice_take)] - /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; /// /// assert_eq!(None, slice.split_off_mut(5..)); @@ -4421,7 +4472,7 @@ impl [T] { /// ``` #[inline] #[must_use = "method does not modify the slice if the range is out of bounds"] - #[unstable(feature = "slice_take", issue = "62280")] + #[stable(feature = "slice_take", since = "1.87.0")] pub fn split_off_mut<'a, R: OneSidedRange>( self: &mut &'a mut Self, range: R, @@ -4451,8 +4502,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_take)] - /// /// let mut slice: &[_] = &['a', 'b', 'c']; /// let first = slice.split_off_first().unwrap(); /// @@ -4460,9 +4509,11 @@ impl [T] { /// assert_eq!(first, &'a'); /// ``` #[inline] - #[unstable(feature = "slice_take", issue = "62280")] - pub fn split_off_first<'a>(self: &mut &'a Self) -> Option<&'a T> { - let (first, rem) = self.split_first()?; + #[stable(feature = "slice_take", since = "1.87.0")] + #[rustc_const_unstable(feature = "const_split_off_first_last", issue = "138539")] + pub const fn split_off_first<'a>(self: &mut &'a Self) -> Option<&'a T> { + // FIXME(const-hack): Use `?` when available in const instead of `let-else`. + let Some((first, rem)) = self.split_first() else { return None }; *self = rem; Some(first) } @@ -4475,8 +4526,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_take)] - /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; /// let first = slice.split_off_first_mut().unwrap(); /// *first = 'd'; @@ -4485,9 +4534,12 @@ impl [T] { /// assert_eq!(first, &'d'); /// ``` #[inline] - #[unstable(feature = "slice_take", issue = "62280")] - pub fn split_off_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { - let (first, rem) = mem::take(self).split_first_mut()?; + #[stable(feature = "slice_take", since = "1.87.0")] + #[rustc_const_unstable(feature = "const_split_off_first_last", issue = "138539")] + pub const fn split_off_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + // FIXME(const-hack): Use `mem::take` and `?` when available in const. + // Original: `mem::take(self).split_first_mut()?` + let Some((first, rem)) = mem::replace(self, &mut []).split_first_mut() else { return None }; *self = rem; Some(first) } @@ -4500,8 +4552,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_take)] - /// /// let mut slice: &[_] = &['a', 'b', 'c']; /// let last = slice.split_off_last().unwrap(); /// @@ -4509,9 +4559,11 @@ impl [T] { /// assert_eq!(last, &'c'); /// ``` #[inline] - #[unstable(feature = "slice_take", issue = "62280")] - pub fn split_off_last<'a>(self: &mut &'a Self) -> Option<&'a T> { - let (last, rem) = self.split_last()?; + #[stable(feature = "slice_take", since = "1.87.0")] + #[rustc_const_unstable(feature = "const_split_off_first_last", issue = "138539")] + pub const fn split_off_last<'a>(self: &mut &'a Self) -> Option<&'a T> { + // FIXME(const-hack): Use `?` when available in const instead of `let-else`. + let Some((last, rem)) = self.split_last() else { return None }; *self = rem; Some(last) } @@ -4524,8 +4576,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_take)] - /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; /// let last = slice.split_off_last_mut().unwrap(); /// *last = 'd'; @@ -4534,9 +4584,12 @@ impl [T] { /// assert_eq!(last, &'d'); /// ``` #[inline] - #[unstable(feature = "slice_take", issue = "62280")] - pub fn split_off_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { - let (last, rem) = mem::take(self).split_last_mut()?; + #[stable(feature = "slice_take", since = "1.87.0")] + #[rustc_const_unstable(feature = "const_split_off_first_last", issue = "138539")] + pub const fn split_off_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + // FIXME(const-hack): Use `mem::take` and `?` when available in const. + // Original: `mem::take(self).split_last_mut()?` + let Some((last, rem)) = mem::replace(self, &mut []).split_last_mut() else { return None }; *self = rem; Some(last) } @@ -4601,7 +4654,7 @@ impl [T] { // or generate worse code otherwise. This is also why we need to go // through a raw pointer here. let slice: *mut [T] = self; - let mut arr: mem::MaybeUninit<[&mut I::Output; N]> = mem::MaybeUninit::uninit(); + let mut arr: MaybeUninit<[&mut I::Output; N]> = MaybeUninit::uninit(); let arr_ptr = arr.as_mut_ptr(); // SAFETY: We expect `indices` to contain disjunct values that are @@ -4721,11 +4774,11 @@ impl [T] { let byte_offset = elem_start.wrapping_sub(self_start); - if byte_offset % mem::size_of::() != 0 { + if byte_offset % size_of::() != 0 { return None; } - let offset = byte_offset / mem::size_of::(); + let offset = byte_offset / size_of::(); if offset < self.len() { Some(offset) } else { None } } @@ -4775,20 +4828,74 @@ impl [T] { let byte_start = subslice_start.wrapping_sub(self_start); - if byte_start % core::mem::size_of::() != 0 { + if byte_start % size_of::() != 0 { return None; } - let start = byte_start / core::mem::size_of::(); + let start = byte_start / size_of::(); let end = start.wrapping_add(subslice.len()); if start <= self.len() && end <= self.len() { Some(start..end) } else { None } } } +impl [MaybeUninit] { + /// Transmutes the mutable uninitialized slice to a mutable uninitialized slice of + /// another type, ensuring alignment of the types is maintained. + /// + /// This is a safe wrapper around [`slice::align_to_mut`], so inherits the same + /// guarantees as that method. + /// + /// # Examples + /// + /// ``` + /// #![feature(align_to_uninit_mut)] + /// use std::mem::MaybeUninit; + /// + /// pub struct BumpAllocator<'scope> { + /// memory: &'scope mut [MaybeUninit], + /// } + /// + /// impl<'scope> BumpAllocator<'scope> { + /// pub fn new(memory: &'scope mut [MaybeUninit]) -> Self { + /// Self { memory } + /// } + /// pub fn try_alloc_uninit(&mut self) -> Option<&'scope mut MaybeUninit> { + /// let first_end = self.memory.as_ptr().align_offset(align_of::()) + size_of::(); + /// let prefix = self.memory.split_off_mut(..first_end)?; + /// Some(&mut prefix.align_to_uninit_mut::().1[0]) + /// } + /// pub fn try_alloc_u32(&mut self, value: u32) -> Option<&'scope mut u32> { + /// let uninit = self.try_alloc_uninit()?; + /// Some(uninit.write(value)) + /// } + /// } + /// + /// let mut memory = [MaybeUninit::::uninit(); 10]; + /// let mut allocator = BumpAllocator::new(&mut memory); + /// let v = allocator.try_alloc_u32(42); + /// assert_eq!(v, Some(&mut 42)); + /// ``` + #[unstable(feature = "align_to_uninit_mut", issue = "139062")] + #[inline] + #[must_use] + pub fn align_to_uninit_mut(&mut self) -> (&mut Self, &mut [MaybeUninit], &mut Self) { + // SAFETY: `MaybeUninit` is transparent. Correct size and alignment are guaranteed by + // `align_to_mut` itself. Therefore the only thing that we have to ensure for a safe + // `transmute` is that the values are valid for the types involved. But for `MaybeUninit` + // any values are valid, so this operation is safe. + unsafe { self.align_to_mut() } + } +} + impl [[T; N]] { /// Takes a `&[[T; N]]`, and flattens it to a `&[T]`. /// + /// For the opposite operation, see [`as_chunks`] and [`as_rchunks`]. + /// + /// [`as_chunks`]: slice::as_chunks + /// [`as_rchunks`]: slice::as_rchunks + /// /// # Panics /// /// This panics if the length of the resulting slice would overflow a `usize`. @@ -4814,7 +4921,7 @@ impl [[T; N]] { /// assert!(empty_slice_of_arrays.as_flattened().is_empty()); /// ``` #[stable(feature = "slice_flatten", since = "1.80.0")] - #[rustc_const_stable(feature = "const_slice_flatten", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_slice_flatten", since = "1.87.0")] pub const fn as_flattened(&self) -> &[T] { let len = if T::IS_ZST { self.len().checked_mul(N).expect("slice len overflow") @@ -4829,6 +4936,11 @@ impl [[T; N]] { /// Takes a `&mut [[T; N]]`, and flattens it to a `&mut [T]`. /// + /// For the opposite operation, see [`as_chunks_mut`] and [`as_rchunks_mut`]. + /// + /// [`as_chunks_mut`]: slice::as_chunks_mut + /// [`as_rchunks_mut`]: slice::as_rchunks_mut + /// /// # Panics /// /// This panics if the length of the resulting slice would overflow a `usize`. @@ -4851,7 +4963,7 @@ impl [[T; N]] { /// assert_eq!(array, [[6, 7, 8], [9, 10, 11], [12, 13, 14]]); /// ``` #[stable(feature = "slice_flatten", since = "1.80.0")] - #[rustc_const_stable(feature = "const_slice_flatten", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_slice_flatten", since = "1.87.0")] pub const fn as_flattened_mut(&mut self) -> &mut [T] { let len = if T::IS_ZST { self.len().checked_mul(N).expect("slice len overflow") @@ -4865,7 +4977,6 @@ impl [[T; N]] { } } -#[cfg(not(test))] impl [f32] { /// Sorts the slice of floats. /// @@ -4894,7 +5005,6 @@ impl [f32] { } } -#[cfg(not(test))] impl [f64] { /// Sorts the slice of floats. /// diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index e24b52cff82e1..3582c7e8b3f38 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -11,7 +11,7 @@ use crate::{array, ptr, ub_checks}; /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `data` must be non-null, [valid] for reads for `len * mem::size_of::()` many bytes, +/// * `data` must be non-null, [valid] for reads for `len * 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! @@ -28,7 +28,7 @@ use crate::{array, ptr, ub_checks}; /// * 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`, +/// * The total size `len * size_of::()` of the slice must be no larger than `isize::MAX`, /// and adding that size to `data` must not "wrap around" the address space. /// See the safety documentation of [`pointer::offset`]. /// @@ -146,7 +146,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `data` must be non-null, [valid] for both reads and writes for `len * mem::size_of::()` many bytes, +/// * `data` must be non-null, [valid] for both reads and writes for `len * 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! @@ -163,7 +163,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] /// (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`, +/// * The total size `len * size_of::()` of the slice must be no larger than `isize::MAX`, /// and adding that size to `data` must not "wrap around" the address space. /// See the safety documentation of [`pointer::offset`]. /// diff --git a/library/core/src/slice/rotate.rs b/library/core/src/slice/rotate.rs index 5d5ee4c7b6240..80178f297eaae 100644 --- a/library/core/src/slice/rotate.rs +++ b/library/core/src/slice/rotate.rs @@ -1,4 +1,4 @@ -use crate::mem::{self, MaybeUninit, SizedTypeProperties}; +use crate::mem::{MaybeUninit, SizedTypeProperties}; use crate::{cmp, ptr}; type BufType = [usize; 32]; @@ -21,12 +21,12 @@ pub(super) unsafe fn ptr_rotate(left: usize, mid: *mut T, right: usize) { } // `T` is not a zero-sized type, so it's okay to divide by its size. if !cfg!(feature = "optimize_for_size") - && cmp::min(left, right) <= mem::size_of::() / mem::size_of::() + && cmp::min(left, right) <= size_of::() / size_of::() { // SAFETY: guaranteed by the caller unsafe { ptr_rotate_memmove(left, mid, right) }; } else if !cfg!(feature = "optimize_for_size") - && ((left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>())) + && ((left + right < 24) || (size_of::() > size_of::<[usize; 4]>())) { // SAFETY: guaranteed by the caller unsafe { ptr_rotate_gcd(left, mid, right) } diff --git a/library/core/src/slice/sort/select.rs b/library/core/src/slice/sort/select.rs index 3358c03d30a9b..c4808b1065d07 100644 --- a/library/core/src/slice/sort/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -6,6 +6,7 @@ //! for pivot selection. Using this as a fallback ensures O(n) worst case running time with //! better performance than one would get using heapsort as fallback. +use crate::cfg_match; use crate::mem::{self, SizedTypeProperties}; #[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::pivot::choose_pivot; @@ -41,10 +42,11 @@ where let min_idx = min_index(v, &mut is_less).unwrap(); v.swap(min_idx, index); } else { - cfg_if! { - if #[cfg(feature = "optimize_for_size")] { + cfg_match! { + feature = "optimize_for_size" => { median_of_medians(v, &mut is_less, index); - } else { + } + _ => { partition_at_index_loop(v, index, None, &mut is_less); } } diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index f6dcf42ba6037..4280f7570db4c 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -2,7 +2,7 @@ use crate::mem::{self, ManuallyDrop, MaybeUninit}; use crate::slice::sort::shared::FreezeMarker; -use crate::{intrinsics, ptr, slice}; +use crate::{hint, intrinsics, ptr, slice}; // It's important to differentiate between SMALL_SORT_THRESHOLD performance for // small slices and small-sort performance sorting small sub-slices as part of @@ -113,7 +113,7 @@ pub(crate) trait UnstableSmallSortFreezeTypeImpl: Sized + FreezeMarker { impl UnstableSmallSortFreezeTypeImpl for T { #[inline(always)] default fn small_sort_threshold() -> usize { - if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + if (size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { SMALL_SORT_GENERAL_THRESHOLD } else { SMALL_SORT_FALLBACK_THRESHOLD @@ -125,7 +125,7 @@ impl UnstableSmallSortFreezeTypeImpl for T { where F: FnMut(&T, &T) -> bool, { - if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + if (size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { small_sort_general(v, is_less); } else { small_sort_fallback(v, is_less); @@ -143,10 +143,10 @@ impl UnstableSmallSortFreezeTypeImpl for T { #[inline(always)] fn small_sort_threshold() -> usize { if has_efficient_in_place_swap::() - && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE + && (size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { SMALL_SORT_NETWORK_THRESHOLD - } else if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + } else if (size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { SMALL_SORT_GENERAL_THRESHOLD } else { SMALL_SORT_FALLBACK_THRESHOLD @@ -159,10 +159,10 @@ impl UnstableSmallSortFreezeTypeImpl for T { F: FnMut(&T, &T) -> bool, { if has_efficient_in_place_swap::() - && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE + && (size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { small_sort_network(v, is_less); - } else if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + } else if (size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { small_sort_general(v, is_less); } else { small_sort_fallback(v, is_less); @@ -238,7 +238,7 @@ fn small_sort_general_with_scratch bool>( unsafe { let scratch_base = scratch.as_mut_ptr() as *mut T; - let presorted_len = if const { mem::size_of::() <= 16 } && len >= 16 { + let presorted_len = if const { size_of::() <= 16 } && len >= 16 { // SAFETY: scratch_base is valid and has enough space. sort8_stable(v_base, scratch_base, scratch_base.add(len), is_less); sort8_stable( @@ -408,8 +408,8 @@ where // } // The goal is to generate cmov instructions here. - let v_a_swap = should_swap.select_unpredictable(v_b, v_a); - let v_b_swap = should_swap.select_unpredictable(v_a, v_b); + let v_a_swap = hint::select_unpredictable(should_swap, v_b, v_a); + let v_b_swap = hint::select_unpredictable(should_swap, v_a, v_b); let v_b_swap_tmp = ManuallyDrop::new(ptr::read(v_b_swap)); ptr::copy(v_a_swap, v_a, 1); @@ -640,15 +640,15 @@ pub unsafe fn sort4_stable bool>( // 1, 1 | c b a d let c3 = is_less(&*c, &*a); let c4 = is_less(&*d, &*b); - let min = c3.select_unpredictable(c, a); - let max = c4.select_unpredictable(b, d); - let unknown_left = c3.select_unpredictable(a, c4.select_unpredictable(c, b)); - let unknown_right = c4.select_unpredictable(d, c3.select_unpredictable(b, c)); + let min = hint::select_unpredictable(c3, c, a); + let max = hint::select_unpredictable(c4, b, d); + let unknown_left = hint::select_unpredictable(c3, a, hint::select_unpredictable(c4, c, b)); + let unknown_right = hint::select_unpredictable(c4, d, hint::select_unpredictable(c3, b, c)); // Sort the last two unknown elements. let c5 = is_less(&*unknown_right, &*unknown_left); - let lo = c5.select_unpredictable(unknown_right, unknown_left); - let hi = c5.select_unpredictable(unknown_left, unknown_right); + let lo = hint::select_unpredictable(c5, unknown_right, unknown_left); + let hi = hint::select_unpredictable(c5, unknown_left, unknown_right); ptr::copy_nonoverlapping(min, dst, 1); ptr::copy_nonoverlapping(lo, dst.add(1), 1); @@ -863,5 +863,5 @@ fn panic_on_ord_violation() -> ! { #[must_use] pub(crate) const fn has_efficient_in_place_swap() -> bool { // Heuristic that holds true on all tested 64-bit capable architectures. - mem::size_of::() <= 8 // mem::size_of::() + size_of::() <= 8 // size_of::() } diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index 3ff2e71fd05bc..a36e5f7801d49 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -2,12 +2,12 @@ #[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::cmp; -use crate::intrinsics; -use crate::mem::{self, MaybeUninit, SizedTypeProperties}; +use crate::mem::{MaybeUninit, SizedTypeProperties}; #[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::smallsort::{ SMALL_SORT_GENERAL_SCRATCH_LEN, StableSmallSortTypeImpl, insertion_sort_shift_left, }; +use crate::{cfg_match, intrinsics}; pub(crate) mod merge; @@ -39,17 +39,18 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less return; } - cfg_if! { - if #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] { + cfg_match! { + any(feature = "optimize_for_size", target_pointer_width = "16") => { // Unlike driftsort, mergesort only requires len / 2, // not len - len / 2. let alloc_len = len / 2; - cfg_if! { - if #[cfg(target_pointer_width = "16")] { + cfg_match! { + target_pointer_width = "16" => { let mut heap_buf = BufT::with_capacity(alloc_len); let scratch = heap_buf.as_uninit_slice_mut(); - } else { + } + _ => { // For small inputs 4KiB of stack storage suffices, which allows us to avoid // calling the (de-)allocator. Benchmarks showed this was quite beneficial. let mut stack_buf = AlignedStorage::::new(); @@ -65,7 +66,8 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less } tiny::mergesort(v, scratch, is_less); - } else { + } + _ => { // More advanced sorting methods than insertion sort are faster if called in // a hot loop for small inputs, but for general-purpose code the small // binary size of insertion sort is more important. The instruction cache in @@ -107,7 +109,7 @@ fn driftsort_main bool, BufT: BufGuard>(v: &mut [T], i // If min_good_run_len is ever modified, this code must be updated to allocate // the correct scratch size for it. const MAX_FULL_ALLOC_BYTES: usize = 8_000_000; // 8MB - let max_full_alloc = MAX_FULL_ALLOC_BYTES / mem::size_of::(); + let max_full_alloc = MAX_FULL_ALLOC_BYTES / size_of::(); let len = v.len(); let alloc_len = cmp::max( cmp::max(len - len / 2, cmp::min(len, max_full_alloc)), @@ -155,7 +157,7 @@ impl AlignedStorage { } fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { - let len = N / mem::size_of::(); + let len = N / size_of::(); // SAFETY: `_align` ensures we are correctly aligned. unsafe { core::slice::from_raw_parts_mut(self.storage.as_mut_ptr().cast(), len) } diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs index 630c6ff907703..3c9688790c40b 100644 --- a/library/core/src/slice/sort/stable/quicksort.rs +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -1,6 +1,6 @@ //! This module contains a stable quicksort and partition implementation. -use crate::mem::{self, ManuallyDrop, MaybeUninit}; +use crate::mem::{ManuallyDrop, MaybeUninit}; use crate::slice::sort::shared::FreezeMarker; use crate::slice::sort::shared::pivot::choose_pivot; use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl; @@ -126,7 +126,7 @@ fn stable_partition bool>( // this gave significant performance boosts in benchmarks. Unrolling // through for _ in 0..UNROLL_LEN { .. } instead of manually improves // compile times but has a ~10-20% performance penalty on opt-level=s. - if const { mem::size_of::() <= 16 } { + if const { size_of::() <= 16 } { const UNROLL_LEN: usize = 4; let unroll_end = v_base.add(loop_end_pos.saturating_sub(UNROLL_LEN - 1)); while state.scan < unroll_end { diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index 2eb653c4601a7..b6c2e05a06a0e 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -1,11 +1,11 @@ //! This module contains the entry points for `slice::sort_unstable`. -use crate::intrinsics; use crate::mem::SizedTypeProperties; #[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::find_existing_run; #[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; +use crate::{cfg_match, intrinsics}; pub(crate) mod heapsort; pub(crate) mod quicksort; @@ -30,10 +30,11 @@ pub fn sort bool>(v: &mut [T], is_less: &mut F) { return; } - cfg_if! { - if #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] { + cfg_match! { + any(feature = "optimize_for_size", target_pointer_width = "16") => { heapsort::heapsort(v, is_less); - } else { + } + _ => { // More advanced sorting methods than insertion sort are faster if called in // a hot loop for small inputs, but for general-purpose code the small // binary size of insertion sort is more important. The instruction cache in diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs index bb9f90fc881a0..7e6cfb5599050 100644 --- a/library/core/src/slice/sort/unstable/quicksort.rs +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -1,13 +1,15 @@ //! This module contains an unstable quicksort and two partition implementations. -use crate::mem::{self, ManuallyDrop}; +#[cfg(not(feature = "optimize_for_size"))] +use crate::mem; +use crate::mem::ManuallyDrop; #[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::pivot::choose_pivot; #[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::smallsort::UnstableSmallSortTypeImpl; #[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::unstable::heapsort; -use crate::{intrinsics, ptr}; +use crate::{cfg_match, intrinsics, ptr}; /// Sorts `v` recursively. /// @@ -137,13 +139,14 @@ where const fn inst_partition bool>() -> fn(&mut [T], &T, &mut F) -> usize { const MAX_BRANCHLESS_PARTITION_SIZE: usize = 96; - if mem::size_of::() <= MAX_BRANCHLESS_PARTITION_SIZE { + if size_of::() <= MAX_BRANCHLESS_PARTITION_SIZE { // Specialize for types that are relatively cheap to copy, where branchless optimizations // have large leverage e.g. `u64` and `String`. - cfg_if! { - if #[cfg(feature = "optimize_for_size")] { + cfg_match! { + feature = "optimize_for_size" => { partition_lomuto_branchless_simple:: - } else { + } + _ => { partition_lomuto_branchless_cyclic:: } } @@ -304,7 +307,7 @@ where // Manual unrolling that works well on x86, Arm and with opt-level=s without murdering // compile-times. Leaving this to the compiler yields ok to bad results. - let unroll_len = const { if mem::size_of::() <= 16 { 2 } else { 1 } }; + let unroll_len = const { if size_of::() <= 16 { 2 } else { 1 } }; let unroll_end = v_base.add(len - (unroll_len - 1)); while state.right < unroll_end { diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs index 1276d9014f0ef..058628797ea85 100644 --- a/library/core/src/str/converts.rs +++ b/library/core/src/str/converts.rs @@ -126,7 +126,7 @@ pub const fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { /// See the docs for [`Utf8Error`] for more details on the kinds of /// errors that can be returned. #[stable(feature = "str_mut_extras", since = "1.20.0")] -#[rustc_const_stable(feature = "const_str_from_utf8", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_str_from_utf8", since = "1.87.0")] #[rustc_diagnostic_item = "str_from_utf8_mut"] pub const fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { // FIXME(const-hack): This should use `?` again, once it's `const` @@ -178,7 +178,7 @@ pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &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. +/// See the immutable version, [`from_utf8_unchecked()`] for documentation and safety requirements. /// /// # Examples /// diff --git a/library/core/src/str/count.rs b/library/core/src/str/count.rs index b5d7aaf05d4bd..452403b23dee1 100644 --- a/library/core/src/str/count.rs +++ b/library/core/src/str/count.rs @@ -20,7 +20,7 @@ use core::intrinsics::unlikely; -const USIZE_SIZE: usize = core::mem::size_of::(); +const USIZE_SIZE: usize = size_of::(); const UNROLL_INNER: usize = 4; #[inline] diff --git a/library/core/src/str/lossy.rs b/library/core/src/str/lossy.rs index ed2cefc59a51c..8d4210c80827d 100644 --- a/library/core/src/str/lossy.rs +++ b/library/core/src/str/lossy.rs @@ -147,12 +147,14 @@ impl fmt::Debug for Debug<'_> { /// An iterator used to decode a slice of mostly UTF-8 bytes to string slices /// ([`&str`]) and byte slices ([`&[u8]`][byteslice]). /// +/// This struct is created by the [`utf8_chunks`] method on bytes slices. /// If you want a simple conversion from UTF-8 byte slices to string slices, /// [`from_utf8`] is easier to use. /// /// See the [`Utf8Chunk`] type for documentation of the items yielded by this iterator. /// /// [byteslice]: slice +/// [`utf8_chunks`]: slice::utf8_chunks /// [`from_utf8`]: super::from_utf8 /// /// # Examples diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 83ad10db2da45..e505e2280953e 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -17,6 +17,7 @@ use self::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; use crate::char::{self, EscapeDebugExtArgs}; use crate::ops::Range; use crate::slice::{self, SliceIndex}; +use crate::ub_checks::assert_unsafe_precondition; use crate::{ascii, mem}; pub mod pattern; @@ -114,7 +115,6 @@ fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! { ); } -#[cfg(not(test))] impl str { /// Returns the length of `self`. /// @@ -134,7 +134,8 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_len", since = "1.39.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "str_len")] + #[rustc_diagnostic_item = "str_len"] + #[rustc_no_implicit_autorefs] #[must_use] #[inline] pub const fn len(&self) -> usize { @@ -154,6 +155,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_is_empty", since = "1.39.0")] + #[rustc_no_implicit_autorefs] #[must_use] #[inline] pub const fn is_empty(&self) -> bool { @@ -231,8 +233,8 @@ impl str { /// /// assert_eq!("💖", sparkle_heart); /// ``` - #[stable(feature = "inherent_str_constructors", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "inherent_str_constructors", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "inherent_str_constructors", since = "1.87.0")] + #[rustc_const_stable(feature = "inherent_str_constructors", since = "1.87.0")] #[rustc_diagnostic_item = "str_inherent_from_utf8"] pub const fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { converts::from_utf8(v) @@ -264,8 +266,8 @@ impl str { /// ``` /// See the docs for [`Utf8Error`] for more details on the kinds of /// errors that can be returned. - #[stable(feature = "inherent_str_constructors", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "const_str_from_utf8", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "inherent_str_constructors", since = "1.87.0")] + #[rustc_const_stable(feature = "const_str_from_utf8", since = "1.87.0")] #[rustc_diagnostic_item = "str_inherent_from_utf8_mut"] pub const fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { converts::from_utf8_mut(v) @@ -296,8 +298,8 @@ impl str { /// ``` #[inline] #[must_use] - #[stable(feature = "inherent_str_constructors", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "inherent_str_constructors", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "inherent_str_constructors", since = "1.87.0")] + #[rustc_const_stable(feature = "inherent_str_constructors", since = "1.87.0")] #[rustc_diagnostic_item = "str_inherent_from_utf8_unchecked"] pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { // SAFETY: converts::from_utf8_unchecked has the same safety requirements as this function. @@ -307,7 +309,7 @@ impl 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. + /// See the immutable version, [`from_utf8_unchecked()`] for documentation and safety requirements. /// /// # Examples /// @@ -321,8 +323,8 @@ impl str { /// ``` #[inline] #[must_use] - #[stable(feature = "inherent_str_constructors", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "inherent_str_constructors", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "inherent_str_constructors", since = "1.87.0")] + #[rustc_const_stable(feature = "inherent_str_constructors", since = "1.87.0")] #[rustc_diagnostic_item = "str_inherent_from_utf8_unchecked_mut"] pub const unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { // SAFETY: converts::from_utf8_unchecked_mut has the same safety requirements as this function. @@ -1029,7 +1031,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - #[cfg_attr(not(test), rustc_diagnostic_item = "str_chars")] + #[rustc_diagnostic_item = "str_chars"] pub fn chars(&self) -> Chars<'_> { Chars { iter: self.as_bytes().iter() } } @@ -1160,7 +1162,7 @@ impl str { #[must_use = "this returns the split string as an iterator, \ without modifying the original"] #[stable(feature = "split_whitespace", since = "1.1.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "str_split_whitespace")] + #[rustc_diagnostic_item = "str_split_whitespace"] #[inline] pub fn split_whitespace(&self) -> SplitWhitespace<'_> { SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } @@ -1355,7 +1357,7 @@ impl str { /// assert!(bananas.starts_with(&['a', 'b', 'c', 'd'])); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "str_starts_with")] + #[rustc_diagnostic_item = "str_starts_with"] pub fn starts_with(&self, pat: P) -> bool { pat.is_prefix_of(self) } @@ -1380,7 +1382,7 @@ impl str { /// assert!(!bananas.ends_with("nana")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "str_ends_with")] + #[rustc_diagnostic_item = "str_ends_with"] pub fn ends_with(&self, pat: P) -> bool where for<'a> P::Searcher<'a>: ReverseSearcher<'a>, @@ -2114,9 +2116,9 @@ impl str { #[must_use = "this returns the trimmed string as a slice, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim")] + #[rustc_diagnostic_item = "str_trim"] pub fn trim(&self) -> &str { - self.trim_matches(|c: char| c.is_whitespace()) + self.trim_matches(char::is_whitespace) } /// Returns a string slice with leading whitespace removed. @@ -2153,9 +2155,9 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim_start")] + #[rustc_diagnostic_item = "str_trim_start"] pub fn trim_start(&self) -> &str { - self.trim_start_matches(|c: char| c.is_whitespace()) + self.trim_start_matches(char::is_whitespace) } /// Returns a string slice with trailing whitespace removed. @@ -2192,9 +2194,9 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim_end")] + #[rustc_diagnostic_item = "str_trim_end"] pub fn trim_end(&self) -> &str { - self.trim_end_matches(|c: char| c.is_whitespace()) + self.trim_end_matches(char::is_whitespace) } /// Returns a string slice with leading whitespace removed. @@ -2633,6 +2635,27 @@ impl str { self.as_bytes().as_ascii() } + /// Converts this string slice into a slice of [ASCII characters](ascii::Char), + /// without checking whether they are valid. + /// + /// # Safety + /// + /// Every character in this string must be ASCII, or else this is UB. + #[unstable(feature = "ascii_char", issue = "110998")] + #[must_use] + #[inline] + pub const unsafe fn as_ascii_unchecked(&self) -> &[ascii::Char] { + assert_unsafe_precondition!( + check_library_ub, + "as_ascii_unchecked requires that the string is valid ASCII", + (it: &str = self) => it.is_ascii() + ); + + // SAFETY: the caller promised that every byte of this string slice + // is ASCII. + unsafe { self.as_bytes().as_ascii_unchecked() } + } + /// Checks that two strings are an ASCII case-insensitive match. /// /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index 2d941adfd859c..bcbbb11c83b2f 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -644,21 +644,21 @@ where impl MultiCharEq for [char; N] { #[inline] fn matches(&mut self, c: char) -> bool { - self.iter().any(|&m| m == c) + self.contains(&c) } } impl MultiCharEq for &[char; N] { #[inline] fn matches(&mut self, c: char) -> bool { - self.iter().any(|&m| m == c) + self.contains(&c) } } impl MultiCharEq for &[char] { #[inline] fn matches(&mut self, c: char) -> bool { - self.iter().any(|&m| m == c) + self.contains(&c) } } diff --git a/library/core/src/str/validations.rs b/library/core/src/str/validations.rs index 0f724dd961329..8174e4ff97dfc 100644 --- a/library/core/src/str/validations.rs +++ b/library/core/src/str/validations.rs @@ -2,7 +2,6 @@ use super::Utf8Error; use crate::intrinsics::const_eval_select; -use crate::mem; /// Returns the initial codepoint accumulator for the first byte. /// The first byte is special, only want bottom 5 bits for width 2, 4 bits @@ -128,7 +127,7 @@ pub(super) const fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { let mut index = 0; let len = v.len(); - const USIZE_BYTES: usize = mem::size_of::(); + const USIZE_BYTES: usize = size_of::(); let ascii_block_size = 2 * USIZE_BYTES; let blocks_end = if len >= ascii_block_size { len - ascii_block_size + 1 } else { 0 }; diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 73180bde54aa9..84c7f1aafe1b0 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -44,8 +44,9 @@ //! The most important aspect of this model is that *data races* are undefined behavior. A data race //! is defined as conflicting non-synchronized accesses where at least one of the accesses is //! non-atomic. Here, accesses are *conflicting* if they affect overlapping regions of memory and at -//! least one of them is a write. They are *non-synchronized* if neither of them *happens-before* -//! the other, according to the happens-before order of the memory model. +//! least one of them is a write. (A `compare_exchange` or `compare_exchange_weak` that does not +//! succeed is not considered a write.) They are *non-synchronized* if neither of them +//! *happens-before* the other, according to the happens-before order of the memory model. //! //! The other possible cause of undefined behavior in the memory model are mixed-size accesses: Rust //! inherits the C++ limitation that non-synchronized conflicting atomic accesses may not partially @@ -246,6 +247,100 @@ use crate::cell::UnsafeCell; use crate::hint::spin_loop; use crate::{fmt, intrinsics}; +trait Sealed {} + +/// A marker trait for primitive types which can be modified atomically. +/// +/// This is an implementation detail for [Atomic]\ which may disappear or be replaced at any time. +/// +/// # Safety +/// +/// Types implementing this trait must be primitives that can be modified atomically. +/// +/// The associated `Self::AtomicInner` type must have the same size and bit validity as `Self`, +/// but may have a higher alignment requirement, so the following `transmute`s are sound: +/// +/// - `&mut Self::AtomicInner` as `&mut Self` +/// - `Self` as `Self::AtomicInner` or the reverse +#[unstable( + feature = "atomic_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" +)] +#[expect(private_bounds)] +pub unsafe trait AtomicPrimitive: Sized + Copy + Sealed { + /// Temporary implementation detail. + type AtomicInner: Sized; +} + +macro impl_atomic_primitive( + $Atom:ident $(<$T:ident>)? ($Primitive:ty), + size($size:literal), + align($align:literal) $(,)? +) { + impl $(<$T>)? Sealed for $Primitive {} + + #[unstable( + feature = "atomic_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" + )] + #[cfg(target_has_atomic_load_store = $size)] + unsafe impl $(<$T>)? AtomicPrimitive for $Primitive { + type AtomicInner = $Atom $(<$T>)?; + } +} + +impl_atomic_primitive!(AtomicBool(bool), size("8"), align(1)); +impl_atomic_primitive!(AtomicI8(i8), size("8"), align(1)); +impl_atomic_primitive!(AtomicU8(u8), size("8"), align(1)); +impl_atomic_primitive!(AtomicI16(i16), size("16"), align(2)); +impl_atomic_primitive!(AtomicU16(u16), size("16"), align(2)); +impl_atomic_primitive!(AtomicI32(i32), size("32"), align(4)); +impl_atomic_primitive!(AtomicU32(u32), size("32"), align(4)); +impl_atomic_primitive!(AtomicI64(i64), size("64"), align(8)); +impl_atomic_primitive!(AtomicU64(u64), size("64"), align(8)); +impl_atomic_primitive!(AtomicI128(i128), size("128"), align(16)); +impl_atomic_primitive!(AtomicU128(u128), size("128"), align(16)); + +#[cfg(target_pointer_width = "16")] +impl_atomic_primitive!(AtomicIsize(isize), size("ptr"), align(2)); +#[cfg(target_pointer_width = "32")] +impl_atomic_primitive!(AtomicIsize(isize), size("ptr"), align(4)); +#[cfg(target_pointer_width = "64")] +impl_atomic_primitive!(AtomicIsize(isize), size("ptr"), align(8)); + +#[cfg(target_pointer_width = "16")] +impl_atomic_primitive!(AtomicUsize(usize), size("ptr"), align(2)); +#[cfg(target_pointer_width = "32")] +impl_atomic_primitive!(AtomicUsize(usize), size("ptr"), align(4)); +#[cfg(target_pointer_width = "64")] +impl_atomic_primitive!(AtomicUsize(usize), size("ptr"), align(8)); + +#[cfg(target_pointer_width = "16")] +impl_atomic_primitive!(AtomicPtr(*mut T), size("ptr"), align(2)); +#[cfg(target_pointer_width = "32")] +impl_atomic_primitive!(AtomicPtr(*mut T), size("ptr"), align(4)); +#[cfg(target_pointer_width = "64")] +impl_atomic_primitive!(AtomicPtr(*mut T), size("ptr"), align(8)); + +/// A memory location which can be safely modified from multiple threads. +/// +/// This has the same size and bit validity as the underlying type `T`. However, +/// the alignment of this type is always equal to its size, even on targets where +/// `T` has alignment less than its size. +/// +/// For more about the differences between atomic types and non-atomic types as +/// well as information about the portability of this type, please see the +/// [module-level documentation]. +/// +/// **Note:** This type is only available on platforms that support atomic loads +/// and stores of `T`. +/// +/// [module-level documentation]: crate::sync::atomic +#[unstable(feature = "generic_atomic", issue = "130539")] +pub type Atomic = ::AtomicInner; + // Some architectures don't have byte-sized atomics, which results in LLVM // emulating them using a LL/SC loop. However for AtomicBool we can take // advantage of the fact that it only ever contains 0 or 1 and use atomic OR/AND @@ -294,7 +389,7 @@ unsafe impl Sync for AtomicBool {} /// loads and stores of pointers. Its size depends on the target pointer's size. #[cfg(target_has_atomic_load_store = "ptr")] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "AtomicPtr")] +#[rustc_diagnostic_item = "AtomicPtr"] #[cfg_attr(target_pointer_width = "16", repr(C, align(2)))] #[cfg_attr(target_pointer_width = "32", repr(C, align(4)))] #[cfg_attr(target_pointer_width = "64", repr(C, align(8)))] @@ -468,6 +563,7 @@ impl AtomicBool { /// /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses + #[inline] #[stable(feature = "atomic_from_ptr", since = "1.75.0")] #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "1.84.0")] pub const unsafe fn from_ptr<'a>(ptr: *mut bool) -> &'a AtomicBool { @@ -1388,6 +1484,7 @@ impl AtomicPtr { /// /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses + #[inline] #[stable(feature = "atomic_from_ptr", since = "1.75.0")] #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "1.84.0")] pub const unsafe fn from_ptr<'a>(ptr: *mut *mut T) -> &'a AtomicPtr { @@ -2033,7 +2130,7 @@ impl AtomicPtr { #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_ptr_add(&self, val: usize, order: Ordering) -> *mut T { - self.fetch_byte_add(val.wrapping_mul(core::mem::size_of::()), order) + self.fetch_byte_add(val.wrapping_mul(size_of::()), order) } /// Offsets the pointer's address by subtracting `val` (in units of `T`), @@ -2078,7 +2175,7 @@ impl AtomicPtr { #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_ptr_sub(&self, val: usize, order: Ordering) -> *mut T { - self.fetch_byte_sub(val.wrapping_mul(core::mem::size_of::()), order) + self.fetch_byte_sub(val.wrapping_mul(size_of::()), order) } /// Offsets the pointer's address by adding `val` *bytes*, returning the @@ -2524,6 +2621,7 @@ macro_rules! atomic_int { /// /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses + #[inline] #[stable(feature = "atomic_from_ptr", since = "1.75.0")] #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "1.84.0")] pub const unsafe fn from_ptr<'a>(ptr: *mut $int_type) -> &'a $atomic_type { @@ -3445,7 +3543,7 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicI8"), + rustc_diagnostic_item = "AtomicI8", "i8", "", atomic_min, atomic_max, @@ -3464,7 +3562,7 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicU8"), + rustc_diagnostic_item = "AtomicU8", "u8", "", atomic_umin, atomic_umax, @@ -3483,7 +3581,7 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicI16"), + rustc_diagnostic_item = "AtomicI16", "i16", "", atomic_min, atomic_max, @@ -3502,7 +3600,7 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicU16"), + rustc_diagnostic_item = "AtomicU16", "u16", "", atomic_umin, atomic_umax, @@ -3521,7 +3619,7 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicI32"), + rustc_diagnostic_item = "AtomicI32", "i32", "", atomic_min, atomic_max, @@ -3540,7 +3638,7 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicU32"), + rustc_diagnostic_item = "AtomicU32", "u32", "", atomic_umin, atomic_umax, @@ -3559,7 +3657,7 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicI64"), + rustc_diagnostic_item = "AtomicI64", "i64", "", atomic_min, atomic_max, @@ -3578,7 +3676,7 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicU64"), + rustc_diagnostic_item = "AtomicU64", "u64", "", atomic_umin, atomic_umax, @@ -3597,7 +3695,7 @@ atomic_int! { unstable(feature = "integer_atomics", issue = "99069"), rustc_const_unstable(feature = "integer_atomics", issue = "99069"), rustc_const_unstable(feature = "integer_atomics", issue = "99069"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicI128"), + rustc_diagnostic_item = "AtomicI128", "i128", "#![feature(integer_atomics)]\n\n", atomic_min, atomic_max, @@ -3616,7 +3714,7 @@ atomic_int! { unstable(feature = "integer_atomics", issue = "99069"), rustc_const_unstable(feature = "integer_atomics", issue = "99069"), rustc_const_unstable(feature = "integer_atomics", issue = "99069"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicU128"), + rustc_diagnostic_item = "AtomicU128", "u128", "#![feature(integer_atomics)]\n\n", atomic_umin, atomic_umax, @@ -3639,7 +3737,7 @@ macro_rules! atomic_int_ptr_sized { stable(feature = "atomic_nand", since = "1.27.0"), rustc_const_stable(feature = "const_ptr_sized_atomics", since = "1.24.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicIsize"), + rustc_diagnostic_item = "AtomicIsize", "isize", "", atomic_min, atomic_max, @@ -3658,7 +3756,7 @@ macro_rules! atomic_int_ptr_sized { stable(feature = "atomic_nand", since = "1.27.0"), rustc_const_stable(feature = "const_ptr_sized_atomics", since = "1.24.0"), rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicUsize"), + rustc_diagnostic_item = "AtomicUsize", "usize", "", atomic_umin, atomic_umax, @@ -3997,24 +4095,24 @@ unsafe fn atomic_umin(dst: *mut T, val: T, order: Ordering) -> T { /// /// A fence 'A' which has (at least) [`Release`] ordering semantics, synchronizes /// with a fence 'B' with (at least) [`Acquire`] semantics, if and only if there -/// exist operations X and Y, both operating on some atomic object 'M' such +/// exist operations X and Y, both operating on some atomic object 'm' such /// that A is sequenced before X, Y is sequenced before B and Y observes -/// the change to M. This provides a happens-before dependence between A and B. +/// the change to m. This provides a happens-before dependence between A and B. /// /// ```text /// Thread 1 Thread 2 /// /// fence(Release); A -------------- -/// x.store(3, Relaxed); X --------- | +/// m.store(3, Relaxed); X --------- | /// | | /// | | -/// -------------> Y if x.load(Relaxed) == 3 { +/// -------------> Y if m.load(Relaxed) == 3 { /// |-------> B fence(Acquire); /// ... /// } /// ``` /// -/// Note that in the example above, it is crucial that the accesses to `x` are atomic. Fences cannot +/// Note that in the example above, it is crucial that the accesses to `m` are atomic. Fences cannot /// be used to establish synchronization among non-atomic accesses in different threads. However, /// thanks to the happens-before relationship between A and B, any non-atomic accesses that /// happen-before A are now also properly synchronized with any non-atomic accesses that diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 3f57b04753a6b..9b8fefe42af60 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -402,7 +402,7 @@ impl<'a> ContextBuilder<'a> { /// [`Wake`]: ../../alloc/task/trait.Wake.html #[repr(transparent)] #[stable(feature = "futures_api", since = "1.36.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "Waker")] +#[rustc_diagnostic_item = "Waker"] pub struct Waker { waker: RawWaker, } diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 8b211b442eab6..0fb5c0bac7562 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -77,7 +77,7 @@ const DAYS_PER_WEEK: u64 = 7; /// crate to do so. #[stable(feature = "duration", since = "1.3.0")] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -#[cfg_attr(not(test), rustc_diagnostic_item = "Duration")] +#[rustc_diagnostic_item = "Duration"] pub struct Duration { secs: u64, nanos: Nanoseconds, // Always 0 <= nanos < NANOS_PER_SEC @@ -373,7 +373,7 @@ impl Duration { /// # Examples /// /// ``` - /// #![feature(duration_constructors)] + /// #![feature(duration_constructors_lite)] /// use std::time::Duration; /// /// let duration = Duration::from_hours(6); @@ -381,7 +381,7 @@ impl Duration { /// assert_eq!(6 * 60 * 60, duration.as_secs()); /// assert_eq!(0, duration.subsec_nanos()); /// ``` - #[unstable(feature = "duration_constructors", issue = "120301")] + #[unstable(feature = "duration_constructors_lite", issue = "140881")] #[must_use] #[inline] pub const fn from_hours(hours: u64) -> Duration { @@ -401,7 +401,7 @@ impl Duration { /// # Examples /// /// ``` - /// #![feature(duration_constructors)] + /// #![feature(duration_constructors_lite)] /// use std::time::Duration; /// /// let duration = Duration::from_mins(10); @@ -409,7 +409,7 @@ impl Duration { /// assert_eq!(10 * 60, duration.as_secs()); /// assert_eq!(0, duration.subsec_nanos()); /// ``` - #[unstable(feature = "duration_constructors", issue = "120301")] + #[unstable(feature = "duration_constructors_lite", issue = "140881")] #[must_use] #[inline] pub const fn from_mins(mins: u64) -> Duration { @@ -1377,7 +1377,8 @@ impl fmt::Debug for Duration { } else { // We need to add padding. Use the `Formatter::padding` helper function. let default_align = fmt::Alignment::Left; - let post_padding = f.padding(requested_w - actual_w, default_align)?; + let post_padding = + f.padding((requested_w - actual_w) as u16, default_align)?; emit_without_padding(f)?; post_padding.write(f) } diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 206b5b9e2c24f..02eb805ece121 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -2,6 +2,7 @@ use crate::cmp::Ordering::{self, *}; use crate::marker::{ConstParamTy_, StructuralPartialEq, UnsizedConstParamTy}; +use crate::ops::ControlFlow::{self, Break, Continue}; // Recursive macro for implementing n-ary tuple functions and operations // @@ -80,19 +81,35 @@ macro_rules! tuple_impls { } #[inline] fn lt(&self, other: &($($T,)+)) -> bool { - lexical_ord!(lt, Less, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + lexical_ord!(lt, __chaining_lt, $( ${ignore($T)} self.${index()}, other.${index()} ),+) } #[inline] fn le(&self, other: &($($T,)+)) -> bool { - lexical_ord!(le, Less, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + lexical_ord!(le, __chaining_le, $( ${ignore($T)} self.${index()}, other.${index()} ),+) } #[inline] fn ge(&self, other: &($($T,)+)) -> bool { - lexical_ord!(ge, Greater, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + lexical_ord!(ge, __chaining_ge, $( ${ignore($T)} self.${index()}, other.${index()} ),+) } #[inline] fn gt(&self, other: &($($T,)+)) -> bool { - lexical_ord!(gt, Greater, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + lexical_ord!(gt, __chaining_gt, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + } + #[inline] + fn __chaining_lt(&self, other: &($($T,)+)) -> ControlFlow { + lexical_chain!(__chaining_lt, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + } + #[inline] + fn __chaining_le(&self, other: &($($T,)+)) -> ControlFlow { + lexical_chain!(__chaining_le, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + } + #[inline] + fn __chaining_gt(&self, other: &($($T,)+)) -> ControlFlow { + lexical_chain!(__chaining_gt, $( ${ignore($T)} self.${index()}, other.${index()} ),+) + } + #[inline] + fn __chaining_ge(&self, other: &($($T,)+)) -> ControlFlow { + lexical_chain!(__chaining_ge, $( ${ignore($T)} self.${index()}, other.${index()} ),+) } } } @@ -171,20 +188,32 @@ macro_rules! maybe_tuple_doc { // `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_ord!(lt, opt_is_lt, a1, b1, // a2, b2, a3, b3)` (and similarly for `lexical_cmp`) // -// `$ne_rel` is only used to determine the result after checking that they're -// not equal, so `lt` and `le` can both just use `Less`. +// `$chain_rel` is the chaining method from `PartialOrd` to use for all but the +// final value, to produce better results for simple primitives. macro_rules! lexical_ord { - ($rel: ident, $ne_rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {{ - let c = PartialOrd::partial_cmp(&$a, &$b); - if c != Some(Equal) { c == Some($ne_rel) } - else { lexical_ord!($rel, $ne_rel, $($rest_a, $rest_b),+) } + ($rel: ident, $chain_rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {{ + match PartialOrd::$chain_rel(&$a, &$b) { + Break(val) => val, + Continue(()) => lexical_ord!($rel, $chain_rel, $($rest_a, $rest_b),+), + } }}; - ($rel: ident, $ne_rel: ident, $a:expr, $b:expr) => { + ($rel: ident, $chain_rel: ident, $a:expr, $b:expr) => { // Use the specific method for the last element PartialOrd::$rel(&$a, &$b) }; } +// Same parameter interleaving as `lexical_ord` above +macro_rules! lexical_chain { + ($chain_rel: ident, $a:expr, $b:expr $(,$rest_a:expr, $rest_b:expr)*) => {{ + PartialOrd::$chain_rel(&$a, &$b)?; + lexical_chain!($chain_rel $(,$rest_a, $rest_b)*) + }}; + ($chain_rel: ident) => { + Continue(()) + }; +} + macro_rules! lexical_partial_cmp { ($a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => { match ($a).partial_cmp(&$b) { diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index 4655d35e9c437..25b9c6e0e0e94 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -47,45 +47,78 @@ const fn bitset_search< (word & (1 << (needle % 64) as u64)) != 0 } -fn decode_prefix_sum(short_offset_run_header: u32) -> u32 { - short_offset_run_header & ((1 << 21) - 1) -} +#[repr(transparent)] +struct ShortOffsetRunHeader(u32); + +impl ShortOffsetRunHeader { + const fn new(start_index: usize, prefix_sum: u32) -> Self { + assert!(start_index < (1 << 11)); + assert!(prefix_sum < (1 << 21)); + + Self((start_index as u32) << 21 | prefix_sum) + } -fn decode_length(short_offset_run_header: u32) -> usize { - (short_offset_run_header >> 21) as usize + #[inline] + const fn start_index(&self) -> usize { + (self.0 >> 21) as usize + } + + #[inline] + const fn prefix_sum(&self) -> u32 { + self.0 & ((1 << 21) - 1) + } } +/// # Safety +/// +/// - The last element of `short_offset_runs` must be greater than `std::char::MAX`. +/// - The start indices of all elements in `short_offset_runs` must be less than `OFFSETS`. #[inline(always)] -fn skip_search( - needle: u32, - short_offset_runs: &[u32; SOR], +unsafe fn skip_search( + needle: char, + short_offset_runs: &[ShortOffsetRunHeader; SOR], offsets: &[u8; OFFSETS], ) -> bool { - // Note that this *cannot* be past the end of the array, as the last - // element is greater than std::char::MAX (the largest possible needle). - // - // So, we cannot have found it (i.e. Ok(idx) + 1 != length) and the correct - // location cannot be past it, so Err(idx) != length either. - // - // This means that we can avoid bounds checking for the accesses below, too. + let needle = needle as u32; + let last_idx = - match short_offset_runs.binary_search_by_key(&(needle << 11), |header| header << 11) { + match short_offset_runs.binary_search_by_key(&(needle << 11), |header| (header.0 << 11)) { Ok(idx) => idx + 1, Err(idx) => idx, }; + // SAFETY: `last_idx` *cannot* be past the end of the array, as the last + // element is greater than `std::char::MAX` (the largest possible needle) + // as guaranteed by the caller. + // + // So, we cannot have found it (i.e. `Ok(idx) => idx + 1 != length`) and the + // correct location cannot be past it, so `Err(idx) => idx != length` either. + // + // This means that we can avoid bounds checking for the accesses below, too. + // + // We need to use `intrinsics::assume` since the `panic_nounwind` contained + // in `hint::assert_unchecked` may not be optimized out. + unsafe { crate::intrinsics::assume(last_idx < SOR) }; - let mut offset_idx = decode_length(short_offset_runs[last_idx]); + let mut offset_idx = short_offset_runs[last_idx].start_index(); let length = if let Some(next) = short_offset_runs.get(last_idx + 1) { - decode_length(*next) - offset_idx + (*next).start_index() - offset_idx } else { offsets.len() - offset_idx }; + let prev = - last_idx.checked_sub(1).map(|prev| decode_prefix_sum(short_offset_runs[prev])).unwrap_or(0); + last_idx.checked_sub(1).map(|prev| short_offset_runs[prev].prefix_sum()).unwrap_or(0); let total = needle - prev; let mut prefix_sum = 0; for _ in 0..(length - 1) { + // SAFETY: It is guaranteed that `length <= OFFSETS - offset_idx`, + // so it follows that `length - 1 + offset_idx < OFFSETS`, therefore + // `offset_idx < OFFSETS` is always true in this loop. + // + // We need to use `intrinsics::assume` since the `panic_nounwind` contained + // in `hint::assert_unchecked` may not be optimized out. + unsafe { crate::intrinsics::assume(offset_idx < OFFSETS) }; let offset = offsets[offset_idx]; prefix_sum += offset as u32; if prefix_sum > total { @@ -100,15 +133,36 @@ pub const UNICODE_VERSION: (u8, u8, u8) = (16, 0, 0); #[rustfmt::skip] pub mod alphabetic { - static SHORT_OFFSET_RUNS: [u32; 53] = [ - 706, 33559113, 876615277, 956309270, 1166025910, 1314925568, 1319120901, 1398813696, - 1449151936, 1451271309, 1455465997, 1463867300, 1652619520, 1663105646, 1665203518, - 1711342208, 1797326647, 1895898848, 2560697242, 2583768976, 2594255920, 2600551419, - 2608940615, 2613141760, 2615240704, 2619435577, 2621533504, 2652997624, 2688650454, - 2692853744, 2699145507, 2713826044, 2734799872, 2736903168, 2757875366, 2835472128, - 2883707536, 2934039760, 2942429152, 2955013632, 2988568880, 3126984704, 3139610336, - 3141711674, 3145911970, 3154308065, 3158503006, 3162699776, 3164797470, 3166896128, - 3168998219, 3171099568, 3176407984, + use super::ShortOffsetRunHeader; + + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 53] = [ + ShortOffsetRunHeader::new(0, 706), ShortOffsetRunHeader::new(16, 4681), + ShortOffsetRunHeader::new(418, 5741), ShortOffsetRunHeader::new(456, 7958), + ShortOffsetRunHeader::new(556, 9398), ShortOffsetRunHeader::new(627, 11264), + ShortOffsetRunHeader::new(629, 12293), ShortOffsetRunHeader::new(667, 13312), + ShortOffsetRunHeader::new(691, 19904), ShortOffsetRunHeader::new(692, 42125), + ShortOffsetRunHeader::new(694, 42509), ShortOffsetRunHeader::new(698, 55204), + ShortOffsetRunHeader::new(788, 63744), ShortOffsetRunHeader::new(793, 64110), + ShortOffsetRunHeader::new(794, 64830), ShortOffsetRunHeader::new(816, 66176), + ShortOffsetRunHeader::new(857, 67383), ShortOffsetRunHeader::new(904, 73440), + ShortOffsetRunHeader::new(1221, 74650), ShortOffsetRunHeader::new(1232, 77712), + ShortOffsetRunHeader::new(1237, 78896), ShortOffsetRunHeader::new(1240, 82939), + ShortOffsetRunHeader::new(1244, 83527), ShortOffsetRunHeader::new(1246, 90368), + ShortOffsetRunHeader::new(1247, 92160), ShortOffsetRunHeader::new(1249, 92729), + ShortOffsetRunHeader::new(1250, 93504), ShortOffsetRunHeader::new(1265, 100344), + ShortOffsetRunHeader::new(1282, 101590), ShortOffsetRunHeader::new(1284, 110576), + ShortOffsetRunHeader::new(1287, 110883), ShortOffsetRunHeader::new(1294, 111356), + ShortOffsetRunHeader::new(1304, 113664), ShortOffsetRunHeader::new(1305, 119808), + ShortOffsetRunHeader::new(1315, 120486), ShortOffsetRunHeader::new(1352, 122624), + ShortOffsetRunHeader::new(1375, 123536), ShortOffsetRunHeader::new(1399, 124112), + ShortOffsetRunHeader::new(1403, 124896), ShortOffsetRunHeader::new(1409, 126464), + ShortOffsetRunHeader::new(1425, 127280), ShortOffsetRunHeader::new(1491, 131072), + ShortOffsetRunHeader::new(1497, 173792), ShortOffsetRunHeader::new(1498, 177978), + ShortOffsetRunHeader::new(1500, 183970), ShortOffsetRunHeader::new(1504, 191457), + ShortOffsetRunHeader::new(1506, 192094), ShortOffsetRunHeader::new(1508, 194560), + ShortOffsetRunHeader::new(1509, 195102), ShortOffsetRunHeader::new(1510, 196608), + ShortOffsetRunHeader::new(1511, 201547), ShortOffsetRunHeader::new(1512, 205744), + ShortOffsetRunHeader::new(1514, 1319856), ]; static OFFSETS: [u8; 1515] = [ 65, 26, 6, 26, 47, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 0, 4, 12, 14, 5, 7, 1, 1, 1, 86, 1, 29, @@ -169,22 +223,44 @@ pub mod alphabetic { 0, 0, 0, 0, 5, 0, 0, ]; pub fn lookup(c: char) -> bool { - super::skip_search( - c as u32, - &SHORT_OFFSET_RUNS, - &OFFSETS, - ) + const { + assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); + let mut i = 0; + while i < SHORT_OFFSET_RUNS.len() { + assert!(SHORT_OFFSET_RUNS[i].start_index() < OFFSETS.len()); + i += 1; + } + } + // SAFETY: We just ensured the last element of `SHORT_OFFSET_RUNS` is greater than `std::char::MAX` + // and the start indices of all elements in `SHORT_OFFSET_RUNS` are smaller than `OFFSETS.len()`. + unsafe { super::skip_search(c, &SHORT_OFFSET_RUNS, &OFFSETS) } } } #[rustfmt::skip] pub mod case_ignorable { - static SHORT_OFFSET_RUNS: [u32; 37] = [ - 688, 44045149, 572528402, 576724925, 807414908, 878718981, 903913493, 929080568, 933275148, - 937491230, 1138818560, 1147208189, 1210124160, 1222707713, 1235291428, 1260457643, - 1277237295, 1537284411, 1545673776, 1604394739, 1667314736, 1692492062, 1700883184, - 1709272384, 1721855823, 1730260976, 1747041437, 1759629056, 1768018279, 1776409088, - 1797382144, 1822548654, 1856103659, 1864493264, 1872884731, 1882062849, 1887371760, + use super::ShortOffsetRunHeader; + + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 37] = [ + ShortOffsetRunHeader::new(0, 688), ShortOffsetRunHeader::new(21, 4957), + ShortOffsetRunHeader::new(273, 5906), ShortOffsetRunHeader::new(275, 8125), + ShortOffsetRunHeader::new(385, 11388), ShortOffsetRunHeader::new(419, 12293), + ShortOffsetRunHeader::new(431, 40981), ShortOffsetRunHeader::new(443, 42232), + ShortOffsetRunHeader::new(445, 42508), ShortOffsetRunHeader::new(447, 64286), + ShortOffsetRunHeader::new(543, 65024), ShortOffsetRunHeader::new(547, 66045), + ShortOffsetRunHeader::new(577, 67456), ShortOffsetRunHeader::new(583, 68097), + ShortOffsetRunHeader::new(589, 68900), ShortOffsetRunHeader::new(601, 69291), + ShortOffsetRunHeader::new(609, 71727), ShortOffsetRunHeader::new(733, 71995), + ShortOffsetRunHeader::new(737, 72752), ShortOffsetRunHeader::new(765, 73459), + ShortOffsetRunHeader::new(795, 78896), ShortOffsetRunHeader::new(807, 90398), + ShortOffsetRunHeader::new(811, 92912), ShortOffsetRunHeader::new(815, 93504), + ShortOffsetRunHeader::new(821, 94031), ShortOffsetRunHeader::new(825, 110576), + ShortOffsetRunHeader::new(833, 113821), ShortOffsetRunHeader::new(839, 118528), + ShortOffsetRunHeader::new(843, 119143), ShortOffsetRunHeader::new(847, 121344), + ShortOffsetRunHeader::new(857, 122880), ShortOffsetRunHeader::new(869, 123566), + ShortOffsetRunHeader::new(885, 124139), ShortOffsetRunHeader::new(889, 125136), + ShortOffsetRunHeader::new(893, 127995), ShortOffsetRunHeader::new(897, 917505), + ShortOffsetRunHeader::new(899, 2032112), ]; static OFFSETS: [u8; 905] = [ 39, 1, 6, 1, 11, 1, 35, 1, 1, 1, 71, 1, 4, 1, 1, 1, 4, 1, 2, 2, 0, 192, 4, 2, 4, 1, 9, 2, @@ -222,20 +298,36 @@ pub mod case_ignorable { 1, 61, 4, 0, 5, 254, 2, 0, 7, 109, 8, 0, 5, 0, 1, 30, 96, 128, 240, 0, ]; pub fn lookup(c: char) -> bool { - super::skip_search( - c as u32, - &SHORT_OFFSET_RUNS, - &OFFSETS, - ) + const { + assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); + let mut i = 0; + while i < SHORT_OFFSET_RUNS.len() { + assert!(SHORT_OFFSET_RUNS[i].start_index() < OFFSETS.len()); + i += 1; + } + } + // SAFETY: We just ensured the last element of `SHORT_OFFSET_RUNS` is greater than `std::char::MAX` + // and the start indices of all elements in `SHORT_OFFSET_RUNS` are smaller than `OFFSETS.len()`. + unsafe { super::skip_search(c, &SHORT_OFFSET_RUNS, &OFFSETS) } } } #[rustfmt::skip] pub mod cased { - static SHORT_OFFSET_RUNS: [u32; 22] = [ - 4256, 115348384, 136322176, 144711446, 163587254, 320875520, 325101120, 350268208, - 392231680, 404815649, 413205504, 421595008, 467733632, 484513952, 501313088, 505533440, - 509728422, 587325184, 635559984, 648145152, 652341552, 657650058, + use super::ShortOffsetRunHeader; + + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 22] = [ + ShortOffsetRunHeader::new(0, 4256), ShortOffsetRunHeader::new(55, 5024), + ShortOffsetRunHeader::new(65, 7296), ShortOffsetRunHeader::new(69, 7958), + ShortOffsetRunHeader::new(78, 9398), ShortOffsetRunHeader::new(153, 11264), + ShortOffsetRunHeader::new(155, 42560), ShortOffsetRunHeader::new(167, 43824), + ShortOffsetRunHeader::new(187, 64256), ShortOffsetRunHeader::new(193, 65313), + ShortOffsetRunHeader::new(197, 66560), ShortOffsetRunHeader::new(201, 67456), + ShortOffsetRunHeader::new(223, 68736), ShortOffsetRunHeader::new(231, 71840), + ShortOffsetRunHeader::new(239, 93760), ShortOffsetRunHeader::new(241, 119808), + ShortOffsetRunHeader::new(243, 120486), ShortOffsetRunHeader::new(280, 122624), + ShortOffsetRunHeader::new(303, 122928), ShortOffsetRunHeader::new(309, 125184), + ShortOffsetRunHeader::new(311, 127280), ShortOffsetRunHeader::new(313, 1241482), ]; static OFFSETS: [u8; 319] = [ 65, 26, 6, 26, 47, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 195, 1, 4, 4, 208, 1, 36, 7, 2, 30, 5, @@ -252,39 +344,67 @@ pub mod cased { 8, 0, 10, 1, 20, 6, 6, 0, 62, 0, 68, 0, 26, 6, 26, 6, 26, 0, ]; pub fn lookup(c: char) -> bool { - super::skip_search( - c as u32, - &SHORT_OFFSET_RUNS, - &OFFSETS, - ) + const { + assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); + let mut i = 0; + while i < SHORT_OFFSET_RUNS.len() { + assert!(SHORT_OFFSET_RUNS[i].start_index() < OFFSETS.len()); + i += 1; + } + } + // SAFETY: We just ensured the last element of `SHORT_OFFSET_RUNS` is greater than `std::char::MAX` + // and the start indices of all elements in `SHORT_OFFSET_RUNS` are smaller than `OFFSETS.len()`. + unsafe { super::skip_search(c, &SHORT_OFFSET_RUNS, &OFFSETS) } } } #[rustfmt::skip] pub mod cc { - static SHORT_OFFSET_RUNS: [u32; 1] = [ - 1114272, + use super::ShortOffsetRunHeader; + + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 1] = [ + ShortOffsetRunHeader::new(0, 1114272), ]; static OFFSETS: [u8; 5] = [ 0, 32, 95, 33, 0, ]; pub fn lookup(c: char) -> bool { - super::skip_search( - c as u32, - &SHORT_OFFSET_RUNS, - &OFFSETS, - ) + const { + assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); + let mut i = 0; + while i < SHORT_OFFSET_RUNS.len() { + assert!(SHORT_OFFSET_RUNS[i].start_index() < OFFSETS.len()); + i += 1; + } + } + // SAFETY: We just ensured the last element of `SHORT_OFFSET_RUNS` is greater than `std::char::MAX` + // and the start indices of all elements in `SHORT_OFFSET_RUNS` are smaller than `OFFSETS.len()`. + unsafe { super::skip_search(c, &SHORT_OFFSET_RUNS, &OFFSETS) } } } #[rustfmt::skip] pub mod grapheme_extend { - static SHORT_OFFSET_RUNS: [u32; 34] = [ - 768, 2098307, 6292881, 10490717, 522196754, 526393356, 723528943, 731918378, 744531567, - 752920578, 769719070, 908131840, 912326558, 920715773, 924912129, 937495844, 962662059, - 971053103, 1256266800, 1323376371, 1386296384, 1407279390, 1415670512, 1424060239, - 1432468637, 1449250560, 1453445477, 1461836288, 1487003648, 1512170158, 1541530860, - 1549920464, 1559101472, 1568604656, + use super::ShortOffsetRunHeader; + + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 34] = [ + ShortOffsetRunHeader::new(0, 768), ShortOffsetRunHeader::new(1, 1155), + ShortOffsetRunHeader::new(3, 1425), ShortOffsetRunHeader::new(5, 4957), + ShortOffsetRunHeader::new(249, 5906), ShortOffsetRunHeader::new(251, 8204), + ShortOffsetRunHeader::new(345, 11503), ShortOffsetRunHeader::new(349, 12330), + ShortOffsetRunHeader::new(355, 42607), ShortOffsetRunHeader::new(359, 43010), + ShortOffsetRunHeader::new(367, 64286), ShortOffsetRunHeader::new(433, 65024), + ShortOffsetRunHeader::new(435, 65438), ShortOffsetRunHeader::new(439, 66045), + ShortOffsetRunHeader::new(441, 68097), ShortOffsetRunHeader::new(447, 68900), + ShortOffsetRunHeader::new(459, 69291), ShortOffsetRunHeader::new(463, 71727), + ShortOffsetRunHeader::new(599, 72752), ShortOffsetRunHeader::new(631, 73459), + ShortOffsetRunHeader::new(661, 78912), ShortOffsetRunHeader::new(671, 90398), + ShortOffsetRunHeader::new(675, 92912), ShortOffsetRunHeader::new(679, 94031), + ShortOffsetRunHeader::new(683, 113821), ShortOffsetRunHeader::new(691, 118528), + ShortOffsetRunHeader::new(693, 119141), ShortOffsetRunHeader::new(697, 121344), + ShortOffsetRunHeader::new(709, 122880), ShortOffsetRunHeader::new(721, 123566), + ShortOffsetRunHeader::new(735, 124140), ShortOffsetRunHeader::new(739, 125136), + ShortOffsetRunHeader::new(743, 917536), ShortOffsetRunHeader::new(747, 2032112), ]; static OFFSETS: [u8; 751] = [ 0, 112, 0, 7, 0, 45, 1, 1, 1, 2, 1, 2, 1, 1, 72, 11, 48, 21, 16, 1, 101, 7, 2, 6, 2, 2, 1, @@ -319,12 +439,20 @@ pub mod grapheme_extend { pub fn lookup(c: char) -> bool { (c as u32) >= 0x300 && lookup_slow(c) } + + #[inline(never)] fn lookup_slow(c: char) -> bool { - super::skip_search( - c as u32, - &SHORT_OFFSET_RUNS, - &OFFSETS, - ) + const { + assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); + let mut i = 0; + while i < SHORT_OFFSET_RUNS.len() { + assert!(SHORT_OFFSET_RUNS[i].start_index() < OFFSETS.len()); + i += 1; + } + } + // SAFETY: We just ensured the last element of `SHORT_OFFSET_RUNS` is greater than `std::char::MAX` + // and the start indices of all elements in `SHORT_OFFSET_RUNS` are smaller than `OFFSETS.len()`. + unsafe { super::skip_search(c, &SHORT_OFFSET_RUNS, &OFFSETS) } } } @@ -436,13 +564,30 @@ pub mod lowercase { #[rustfmt::skip] pub mod n { - static SHORT_OFFSET_RUNS: [u32; 42] = [ - 1632, 18876774, 31461440, 102765417, 111154926, 115349830, 132128880, 165684320, 186656630, - 195046653, 199241735, 203436434, 216049184, 241215536, 249605104, 274792208, 278987015, - 283181793, 295766104, 320933114, 383848032, 396432464, 438376016, 446765280, 463543280, - 471932752, 488711168, 497115440, 501312096, 505507184, 522284672, 526503152, 530698944, - 534894542, 547479872, 551674608, 555869424, 560064711, 568454257, 576844032, 597818352, - 603126778, + use super::ShortOffsetRunHeader; + + static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 42] = [ + ShortOffsetRunHeader::new(0, 1632), ShortOffsetRunHeader::new(9, 2406), + ShortOffsetRunHeader::new(15, 4160), ShortOffsetRunHeader::new(49, 4969), + ShortOffsetRunHeader::new(53, 5870), ShortOffsetRunHeader::new(55, 6470), + ShortOffsetRunHeader::new(63, 8304), ShortOffsetRunHeader::new(79, 9312), + ShortOffsetRunHeader::new(89, 10102), ShortOffsetRunHeader::new(93, 11517), + ShortOffsetRunHeader::new(95, 12295), ShortOffsetRunHeader::new(97, 12690), + ShortOffsetRunHeader::new(103, 42528), ShortOffsetRunHeader::new(115, 43056), + ShortOffsetRunHeader::new(119, 44016), ShortOffsetRunHeader::new(131, 65296), + ShortOffsetRunHeader::new(133, 65799), ShortOffsetRunHeader::new(135, 66273), + ShortOffsetRunHeader::new(141, 67672), ShortOffsetRunHeader::new(153, 68858), + ShortOffsetRunHeader::new(183, 69216), ShortOffsetRunHeader::new(189, 70736), + ShortOffsetRunHeader::new(209, 71248), ShortOffsetRunHeader::new(213, 71904), + ShortOffsetRunHeader::new(221, 72688), ShortOffsetRunHeader::new(225, 73552), + ShortOffsetRunHeader::new(233, 74752), ShortOffsetRunHeader::new(237, 90416), + ShortOffsetRunHeader::new(239, 92768), ShortOffsetRunHeader::new(241, 93552), + ShortOffsetRunHeader::new(249, 93824), ShortOffsetRunHeader::new(251, 118000), + ShortOffsetRunHeader::new(253, 119488), ShortOffsetRunHeader::new(255, 120782), + ShortOffsetRunHeader::new(261, 123200), ShortOffsetRunHeader::new(263, 123632), + ShortOffsetRunHeader::new(265, 124144), ShortOffsetRunHeader::new(267, 125127), + ShortOffsetRunHeader::new(271, 126065), ShortOffsetRunHeader::new(275, 127232), + ShortOffsetRunHeader::new(285, 130032), ShortOffsetRunHeader::new(287, 1244154), ]; static OFFSETS: [u8; 289] = [ 48, 10, 120, 2, 5, 1, 2, 3, 0, 10, 134, 10, 198, 10, 0, 10, 118, 10, 4, 6, 108, 10, 118, @@ -459,11 +604,17 @@ pub mod n { 10, 247, 10, 0, 9, 128, 10, 0, 59, 1, 3, 1, 4, 76, 45, 1, 15, 0, 13, 0, 10, 0, ]; pub fn lookup(c: char) -> bool { - super::skip_search( - c as u32, - &SHORT_OFFSET_RUNS, - &OFFSETS, - ) + const { + assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); + let mut i = 0; + while i < SHORT_OFFSET_RUNS.len() { + assert!(SHORT_OFFSET_RUNS[i].start_index() < OFFSETS.len()); + i += 1; + } + } + // SAFETY: We just ensured the last element of `SHORT_OFFSET_RUNS` is greater than `std::char::MAX` + // and the start indices of all elements in `SHORT_OFFSET_RUNS` are smaller than `OFFSETS.len()`. + unsafe { super::skip_search(c, &SHORT_OFFSET_RUNS, &OFFSETS) } } } diff --git a/library/core/src/unit.rs b/library/core/src/unit.rs index d656005f3d42d..d54816c444bc4 100644 --- a/library/core/src/unit.rs +++ b/library/core/src/unit.rs @@ -17,3 +17,19 @@ impl FromIterator<()> for () { iter.into_iter().for_each(|()| {}) } } + +pub(crate) trait IsUnit { + fn is_unit() -> bool; +} + +impl IsUnit for T { + default fn is_unit() -> bool { + false + } +} + +impl IsUnit for () { + fn is_unit() -> bool { + true + } +} diff --git a/library/coretests/Cargo.toml b/library/coretests/Cargo.toml index e44f01d347b3d..e0ddcd466aea5 100644 --- a/library/coretests/Cargo.toml +++ b/library/coretests/Cargo.toml @@ -12,6 +12,7 @@ edition = "2024" path = "lib.rs" test = false bench = false +doc = false [[test]] name = "coretests" @@ -25,3 +26,14 @@ test = true [dev-dependencies] rand = { version = "0.9.0", default-features = false } rand_xorshift = { version = "0.4.0", default-features = false } + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + # Internal features aren't marked known config by default, we use these to + # gate tests. + 'cfg(target_has_reliable_f16)', + 'cfg(target_has_reliable_f16_math)', + 'cfg(target_has_reliable_f128)', + 'cfg(target_has_reliable_f128_math)', +] diff --git a/library/coretests/benches/ascii.rs b/library/coretests/benches/ascii.rs index 3fe45aa360bf0..64bdc7fed118f 100644 --- a/library/coretests/benches/ascii.rs +++ b/library/coretests/benches/ascii.rs @@ -354,7 +354,7 @@ static ASCII_CHARACTER_CLASS: [AsciiCharacterClass; 256] = [ ]; const ASCII_PATH: &[u8] = b"home/kyubey/rust/build/x86_64-unknown-linux-gnu/stage0/lib:/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-tools/release/deps"; -const RUST_INCANTATION: &[u8] = br#"AR_x86_64_unknown_linux_gnu="ar" CARGO_INCREMENTAL="0" CARGO_PROFILE_RELEASE_DEBUG="1" CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS="false" CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS="false" CARGO_TARGET_DIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-std" CC_x86_64_unknown_linux_gnu="cc" CFG_COMPILER_HOST_TRIPLE="x86_64-unknown-linux-gnu" CFG_RELEASE_CHANNEL="dev" CFLAGS_x86_64_unknown_linux_gnu="-ffunction-sections -fdata-sections -fPIC -m64" CXXFLAGS_x86_64_unknown_linux_gnu="-ffunction-sections -fdata-sections -fPIC -m64" CXX_x86_64_unknown_linux_gnu="c++" LD_LIBRARY_PATH="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-sysroot/lib/rustlib/x86_64-unknown-linux-gnu/lib" LIBC_CHECK_CFG="1" RANLIB_x86_64_unknown_linux_gnu="ar s" REAL_LIBRARY_PATH_VAR="LD_LIBRARY_PATH" RUSTBUILD_NATIVE_DIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/native" RUSTC="/home/kyubey/workspace/rust/build/bootstrap/debug/rustc" RUSTC_BOOTSTRAP="1" RUSTC_BREAK_ON_ICE="1" RUSTC_ERROR_METADATA_DST="/home/kyubey/workspace/rust/build/tmp/extended-error-metadata" RUSTC_FORCE_UNSTABLE="1" RUSTC_HOST_FUSE_LD_LLD="1" RUSTC_INSTALL_BINDIR="bin" RUSTC_LIBDIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/lib" RUSTC_LINT_FLAGS="-Wrust_2018_idioms -Wunused_lifetimes -Wsemicolon_in_expressions_from_macros" RUSTC_REAL="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/bin/rustc" RUSTC_SNAPSHOT="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/bin/rustc" RUSTC_SNAPSHOT_LIBDIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/lib" RUSTC_STAGE="0" RUSTC_SYSROOT="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-sysroot" RUSTC_VERBOSE="0" RUSTDOC="/home/kyubey/workspace/rust/build/bootstrap/debug/rustdoc" RUSTDOCFLAGS="-C target-cpu=native --cfg=bootstrap -Csymbol-mangling-version=legacy -Zunstable-options -Zunstable-options --check-cfg=values(bootstrap) --check-cfg=values(stdarch_intel_sde) --check-cfg=values(no_fp_fmt_parse) --check-cfg=values(no_global_oom_handling) --check-cfg=values(no_rc) --check-cfg=values(no_sync) --check-cfg=values(freebsd12) --check-cfg=values(freebsd13) --check-cfg=values(backtrace_in_libstd) --check-cfg=values(target_env,\"libnx\") --check-cfg=values(target_arch,\"asmjs\",\"spirv\",\"nvptx\",\"xtensa\") -Clink-arg=-fuse-ld=lld -Clink-arg=-Wl,--threads=1 -Wrustdoc::invalid_codeblock_attributes --crate-version 1.72.0-dev -Zcrate-attr=doc(html_root_url=\"https://doc.rust-lang.org/nightly/\") -Zcrate-attr=warn(rust_2018_idioms)" RUSTDOC_FUSE_LD_LLD="1" RUSTDOC_LIBDIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/lib" RUSTDOC_REAL="/path/to/nowhere/rustdoc/not/required" RUSTFLAGS="-C target-cpu=native --cfg=bootstrap -Csymbol-mangling-version=legacy -Zunstable-options -Zunstable-options --check-cfg=values(bootstrap) --check-cfg=values(stdarch_intel_sde) --check-cfg=values(no_fp_fmt_parse) --check-cfg=values(no_global_oom_handling) --check-cfg=values(no_rc) --check-cfg=values(no_sync) --check-cfg=values(freebsd12) --check-cfg=values(freebsd13) --check-cfg=values(backtrace_in_libstd) --check-cfg=values(target_env,\"libnx\") --check-cfg=values(target_arch,\"asmjs\",\"spirv\",\"nvptx\",\"xtensa\") -Zmacro-backtrace -Clink-args=-Wl,-z,origin -Clink-args=-Wl,-rpath,$ORIGIN/../lib -Clink-args=-fuse-ld=lld -Csplit-debuginfo=off -Cprefer-dynamic -Zinline-mir -Clto=off -Zcrate-attr=doc(html_root_url=\"https://doc.rust-lang.org/nightly/\")" RUST_COMPILER_RT_ROOT="/home/kyubey/workspace/rust/src/llvm-project/compiler-rt" RUST_TEST_THREADS="48" WINAPI_NO_BUNDLED_LIBRARIES="1" __CARGO_DEFAULT_LIB_METADATA="bootstrapstd" "/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "bench" "--target" "x86_64-unknown-linux-gnu" "-Zcheck-cfg=names,values,output" "-Zbinary-dep-depinfo" "-j" "48" "--features" " panic-unwind backtrace compiler-builtins-c" "--manifest-path" "/home/kyubey/workspace/rust/library/sysroot/Cargo.toml" "-p" "core" "--" "bench_ascii_escape_display" "--quiet" "-Z" "unstable-options" "--format" "json""#; +const RUST_INCANTATION: &[u8] = br#"AR_x86_64_unknown_linux_gnu="ar" CARGO_INCREMENTAL="0" CARGO_PROFILE_RELEASE_DEBUG="1" CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS="false" CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS="false" CARGO_TARGET_DIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-std" CC_x86_64_unknown_linux_gnu="cc" CFG_COMPILER_HOST_TRIPLE="x86_64-unknown-linux-gnu" CFG_RELEASE_CHANNEL="dev" CFLAGS_x86_64_unknown_linux_gnu="-ffunction-sections -fdata-sections -fPIC -m64" CXXFLAGS_x86_64_unknown_linux_gnu="-ffunction-sections -fdata-sections -fPIC -m64" CXX_x86_64_unknown_linux_gnu="c++" LD_LIBRARY_PATH="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-sysroot/lib/rustlib/x86_64-unknown-linux-gnu/lib" LIBC_CHECK_CFG="1" RANLIB_x86_64_unknown_linux_gnu="ar s" REAL_LIBRARY_PATH_VAR="LD_LIBRARY_PATH" RUSTBUILD_NATIVE_DIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/native" RUSTC="/home/kyubey/workspace/rust/build/bootstrap/debug/rustc" RUSTC_BOOTSTRAP="1" RUSTC_BREAK_ON_ICE="1" RUSTC_ERROR_METADATA_DST="/home/kyubey/workspace/rust/build/tmp/extended-error-metadata" RUSTC_FORCE_UNSTABLE="1" RUSTC_HOST_FUSE_LD_LLD="1" RUSTC_INSTALL_BINDIR="bin" RUSTC_LIBDIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/lib" RUSTC_LINT_FLAGS="-Wrust_2018_idioms -Wunused_lifetimes -Wsemicolon_in_expressions_from_macros" RUSTC_REAL="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/bin/rustc" RUSTC_SNAPSHOT="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/bin/rustc" RUSTC_SNAPSHOT_LIBDIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/lib" RUSTC_STAGE="0" RUSTC_SYSROOT="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0-sysroot" RUSTC_VERBOSE="0" RUSTDOC="/home/kyubey/workspace/rust/build/bootstrap/debug/rustdoc" RUSTDOCFLAGS="-C target-cpu=native --cfg=bootstrap -Csymbol-mangling-version=legacy -Zunstable-options -Zunstable-options --check-cfg=values(bootstrap) --check-cfg=values(no_fp_fmt_parse) --check-cfg=values(no_global_oom_handling) --check-cfg=values(no_rc) --check-cfg=values(no_sync) --check-cfg=values(freebsd12) --check-cfg=values(freebsd13) --check-cfg=values(backtrace_in_libstd) --check-cfg=values(target_env,\"libnx\") --check-cfg=values(target_arch,\"asmjs\",\"spirv\",\"nvptx\",\"xtensa\") -Clink-arg=-fuse-ld=lld -Clink-arg=-Wl,--threads=1 -Wrustdoc::invalid_codeblock_attributes --crate-version 1.72.0-dev -Zcrate-attr=doc(html_root_url=\"https://doc.rust-lang.org/nightly/\") -Zcrate-attr=warn(rust_2018_idioms)" RUSTDOC_FUSE_LD_LLD="1" RUSTDOC_LIBDIR="/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/lib" RUSTDOC_REAL="/path/to/nowhere/rustdoc/not/required" RUSTFLAGS="-C target-cpu=native --cfg=bootstrap -Csymbol-mangling-version=legacy -Zunstable-options -Zunstable-options --check-cfg=values(bootstrap) --check-cfg=values(no_fp_fmt_parse) --check-cfg=values(no_global_oom_handling) --check-cfg=values(no_rc) --check-cfg=values(no_sync) --check-cfg=values(freebsd12) --check-cfg=values(freebsd13) --check-cfg=values(backtrace_in_libstd) --check-cfg=values(target_env,\"libnx\") --check-cfg=values(target_arch,\"asmjs\",\"spirv\",\"nvptx\",\"xtensa\") -Zmacro-backtrace -Clink-args=-Wl,-z,origin -Clink-args=-Wl,-rpath,$ORIGIN/../lib -Clink-args=-fuse-ld=lld -Csplit-debuginfo=off -Cprefer-dynamic -Zinline-mir -Clto=off -Zcrate-attr=doc(html_root_url=\"https://doc.rust-lang.org/nightly/\")" RUST_COMPILER_RT_ROOT="/home/kyubey/workspace/rust/src/llvm-project/compiler-rt" RUST_TEST_THREADS="48" WINAPI_NO_BUNDLED_LIBRARIES="1" __CARGO_DEFAULT_LIB_METADATA="bootstrapstd" "/home/kyubey/workspace/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "bench" "--target" "x86_64-unknown-linux-gnu" "-Zcheck-cfg=names,values,output" "-Zbinary-dep-depinfo" "-j" "48" "--features" " panic-unwind backtrace compiler-builtins-c" "--manifest-path" "/home/kyubey/workspace/rust/library/sysroot/Cargo.toml" "-p" "core" "--" "bench_ascii_escape_display" "--quiet" "-Z" "unstable-options" "--format" "json""#; #[bench] fn bench_ascii_escape_display_no_escape(b: &mut Bencher) { diff --git a/library/coretests/benches/ascii/is_ascii.rs b/library/coretests/benches/ascii/is_ascii.rs index ced7084fb0e48..a6c718409ee85 100644 --- a/library/coretests/benches/ascii/is_ascii.rs +++ b/library/coretests/benches/ascii/is_ascii.rs @@ -95,7 +95,7 @@ benches! { // These are separate since it's easier to debug errors if they don't go through // macro expansion first. fn is_ascii_align_to(bytes: &[u8]) -> bool { - if bytes.len() < core::mem::size_of::() { + if bytes.len() < size_of::() { return bytes.iter().all(|b| b.is_ascii()); } // SAFETY: transmuting a sequence of `u8` to `usize` is always fine @@ -106,7 +106,7 @@ fn is_ascii_align_to(bytes: &[u8]) -> bool { } fn is_ascii_align_to_unrolled(bytes: &[u8]) -> bool { - if bytes.len() < core::mem::size_of::() { + if bytes.len() < size_of::() { return bytes.iter().all(|b| b.is_ascii()); } // SAFETY: transmuting a sequence of `u8` to `[usize; 2]` is always fine @@ -118,6 +118,6 @@ fn is_ascii_align_to_unrolled(bytes: &[u8]) -> bool { #[inline] fn contains_nonascii(v: usize) -> bool { - const NONASCII_MASK: usize = usize::from_ne_bytes([0x80; core::mem::size_of::()]); + const NONASCII_MASK: usize = usize::from_ne_bytes([0x80; size_of::()]); (NONASCII_MASK & v) != 0 } diff --git a/library/coretests/benches/iter.rs b/library/coretests/benches/iter.rs index e14f26b729032..e49d152eb539f 100644 --- a/library/coretests/benches/iter.rs +++ b/library/coretests/benches/iter.rs @@ -1,6 +1,5 @@ use core::borrow::Borrow; use core::iter::*; -use core::mem; use core::num::Wrapping; use core::ops::Range; @@ -477,7 +476,7 @@ fn bench_next_chunk_copied(b: &mut Bencher) { let mut iter = black_box(&v).iter().copied(); let mut acc = Wrapping(0); // This uses a while-let loop to side-step the TRA specialization in ArrayChunks - while let Ok(chunk) = iter.next_chunk::<{ mem::size_of::() }>() { + while let Ok(chunk) = iter.next_chunk::<{ size_of::() }>() { let d = u64::from_ne_bytes(chunk); acc += Wrapping(d.rotate_left(7).wrapping_add(1)); } @@ -496,7 +495,7 @@ fn bench_next_chunk_trusted_random_access(b: &mut Bencher) { .iter() // this shows that we're not relying on the slice::Iter specialization in Copied .map(|b| *b.borrow()) - .array_chunks::<{ mem::size_of::() }>() + .array_chunks::<{ size_of::() }>() .map(|ary| { let d = u64::from_ne_bytes(ary); Wrapping(d.rotate_left(7).wrapping_add(1)) diff --git a/library/coretests/tests/alloc.rs b/library/coretests/tests/alloc.rs index b88f1821cd77c..72fdf82c1f8cf 100644 --- a/library/coretests/tests/alloc.rs +++ b/library/coretests/tests/alloc.rs @@ -1,5 +1,4 @@ use core::alloc::Layout; -use core::mem::size_of; use core::ptr::{self, NonNull}; #[test] diff --git a/library/coretests/tests/array.rs b/library/coretests/tests/array.rs index b6d18f1ec3822..30ccbbc320318 100644 --- a/library/coretests/tests/array.rs +++ b/library/coretests/tests/array.rs @@ -325,7 +325,7 @@ fn array_map_drop_safety() { let success = std::panic::catch_unwind(|| { let items = [0; 10]; let mut nth = 0; - items.map(|_| { + let _ = items.map(|_| { assert!(nth < num_to_create); nth += 1; DropCounter diff --git a/library/coretests/tests/atomic.rs b/library/coretests/tests/atomic.rs index 0ffba538b2074..e0c0fe4790c04 100644 --- a/library/coretests/tests/atomic.rs +++ b/library/coretests/tests/atomic.rs @@ -250,8 +250,6 @@ fn atomic_access_bool() { #[test] fn atomic_alignment() { - use std::mem::{align_of, size_of}; - #[cfg(target_has_atomic = "8")] assert_eq!(align_of::(), size_of::()); #[cfg(target_has_atomic = "ptr")] diff --git a/library/coretests/tests/cell.rs b/library/coretests/tests/cell.rs index d6a401c2b4d98..781a46c3744f5 100644 --- a/library/coretests/tests/cell.rs +++ b/library/coretests/tests/cell.rs @@ -50,10 +50,10 @@ fn smoketest_cell() { fn cell_update() { let x = Cell::new(10); - assert_eq!(x.update(|x| x + 5), 15); + x.update(|x| x + 5); assert_eq!(x.get(), 15); - assert_eq!(x.update(|x| x / 3), 5); + x.update(|x| x / 3); assert_eq!(x.get(), 5); } diff --git a/library/coretests/tests/ffi/cstr.rs b/library/coretests/tests/ffi/cstr.rs index 9bf4c21a9ab97..0d85b22c585a1 100644 --- a/library/coretests/tests/ffi/cstr.rs +++ b/library/coretests/tests/ffi/cstr.rs @@ -13,3 +13,9 @@ fn compares_as_u8s() { assert_eq!(Ord::cmp(a, b), Ord::cmp(a_bytes, b_bytes)); assert_eq!(PartialOrd::partial_cmp(a, b), PartialOrd::partial_cmp(a_bytes, b_bytes)); } + +#[test] +fn debug() { + let s = c"abc\x01\x02\n\xE2\x80\xA6\xFF"; + assert_eq!(format!("{s:?}"), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#); +} diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs new file mode 100644 index 0000000000000..12cf651f03f46 --- /dev/null +++ b/library/coretests/tests/floats/f128.rs @@ -0,0 +1,790 @@ +// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy +#![cfg(target_has_reliable_f128)] + +use std::f128::consts; +use std::num::FpCategory as Fp; +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +use std::ops::Rem; +use std::ops::{Add, Div, Mul, Sub}; + +// Note these tolerances make sense around zero, but not for more extreme exponents. + +/// Default tolerances. Works for values that should be near precise but not exact. Roughly +/// the precision carried by `100 * 100`. +const TOL: f128 = 1e-12; + +/// For operations that are near exact, usually not involving math of different +/// signs. +const TOL_PRECISE: f128 = 1e-28; + +/// Smallest number +const TINY_BITS: u128 = 0x1; + +/// Next smallest number +const TINY_UP_BITS: u128 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u128 = 0x7ffefffffffffffffffffffffffffffe; + +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u128 = 0x0000ffffffffffffffffffffffffffff; + +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u128 = 0x00010000000000000000000000000000; + +/// First pattern over the mantissa +const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; + +/// Second pattern over the mantissa +const NAN_MASK2: u128 = 0x00005555555555555555555555555555; + +/// Compare by representation +#[allow(unused_macros)] +macro_rules! assert_f128_biteq { + ($a:expr, $b:expr) => { + let (l, r): (&f128, &f128) = (&$a, &$b); + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}"); + }; +} + +#[test] +fn test_num_f128() { + // FIXME(f16_f128): replace with a `test_num` call once the required `fmodl`/`fmodf128` + // function is available on all platforms. + let ten = 10f128; + let two = 2f128; + 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); +} + +// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support +// the intrinsics. + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_num_f128_rem() { + let ten = 10f128; + let two = 2f128; + assert_eq!(ten.rem(two), ten % two); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_min_nan() { + assert_eq!(f128::NAN.min(2.0), 2.0); + assert_eq!(2.0f128.min(f128::NAN), 2.0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_max_nan() { + assert_eq!(f128::NAN.max(2.0), 2.0); + assert_eq!(2.0f128.max(f128::NAN), 2.0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_minimum() { + assert!(f128::NAN.minimum(2.0).is_nan()); + assert!(2.0f128.minimum(f128::NAN).is_nan()); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_maximum() { + assert!(f128::NAN.maximum(2.0).is_nan()); + assert!(2.0f128.maximum(f128::NAN).is_nan()); +} + +#[test] +fn test_nan() { + let nan: f128 = f128::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert!(!nan.is_normal()); + assert_eq!(Fp::Nan, nan.classify()); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (f128::MANTISSA_DIGITS - 2)) != 0); +} + +#[test] +fn test_infinity() { + let inf: f128 = f128::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f128 = 0.0f128; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f128 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f128 = 1.0f128; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f128.is_nan()); + assert!(!5.3f128.is_nan()); + assert!(!(-10.732f128).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f128.is_infinite()); + assert!(!42.8f128.is_infinite()); + assert!(!(-109.2f128).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f128.is_finite()); + assert!(42.8f128.is_finite()); + assert!((-109.2f128).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let zero: f128 = 0.0f128; + let neg_zero: f128 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f128.is_normal()); + assert!(1e-4931f128.is_normal()); + assert!(!1e-4932f128.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let zero: f128 = 0.0f128; + let neg_zero: f128 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1f128.classify(), Fp::Normal); + assert_eq!(1e-4931f128.classify(), Fp::Normal); + assert_eq!(1e-4932f128.classify(), Fp::Subnormal); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_floor() { + assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_ceil() { + assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_round() { + assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_trunc() { + assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_fract() { + assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_abs() { + assert_eq!(f128::INFINITY.abs(), f128::INFINITY); + assert_eq!(1f128.abs(), 1f128); + assert_eq!(0f128.abs(), 0f128); + assert_eq!((-0f128).abs(), 0f128); + assert_eq!((-1f128).abs(), 1f128); + assert_eq!(f128::NEG_INFINITY.abs(), f128::INFINITY); + assert_eq!((1f128 / f128::NEG_INFINITY).abs(), 0f128); + assert!(f128::NAN.abs().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f128::INFINITY.is_sign_positive()); + assert!(1f128.is_sign_positive()); + assert!(0f128.is_sign_positive()); + assert!(!(-0f128).is_sign_positive()); + assert!(!(-1f128).is_sign_positive()); + assert!(!f128::NEG_INFINITY.is_sign_positive()); + assert!(!(1f128 / f128::NEG_INFINITY).is_sign_positive()); + assert!(f128::NAN.is_sign_positive()); + assert!(!(-f128::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f128::INFINITY.is_sign_negative()); + assert!(!1f128.is_sign_negative()); + assert!(!0f128.is_sign_negative()); + assert!((-0f128).is_sign_negative()); + assert!((-1f128).is_sign_negative()); + assert!(f128::NEG_INFINITY.is_sign_negative()); + assert!((1f128 / f128::NEG_INFINITY).is_sign_negative()); + assert!(!f128::NAN.is_sign_negative()); + assert!((-f128::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f128::from_bits(TINY_BITS); + let tiny_up = f128::from_bits(TINY_UP_BITS); + let max_down = f128::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); + assert_f128_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN); + assert_f128_biteq!(f128::MIN.next_up(), -max_down); + assert_f128_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0); + assert_f128_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f128_biteq!((-tiny_up).next_up(), -tiny); + assert_f128_biteq!((-tiny).next_up(), -0.0f128); + assert_f128_biteq!((-0.0f128).next_up(), tiny); + assert_f128_biteq!(0.0f128.next_up(), tiny); + assert_f128_biteq!(tiny.next_up(), tiny_up); + assert_f128_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f128_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON); + assert_f128_biteq!(f128::MAX.next_up(), f128::INFINITY); + assert_f128_biteq!(f128::INFINITY.next_up(), f128::INFINITY); + + // Check that NaNs roundtrip. + let nan0 = f128::NAN; + let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); + assert_f128_biteq!(nan0.next_up(), nan0); + assert_f128_biteq!(nan1.next_up(), nan1); + assert_f128_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f128::from_bits(TINY_BITS); + let tiny_up = f128::from_bits(TINY_UP_BITS); + let max_down = f128::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); + assert_f128_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY); + assert_f128_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY); + assert_f128_biteq!((-max_down).next_down(), f128::MIN); + assert_f128_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON); + assert_f128_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f128_biteq!((-tiny).next_down(), -tiny_up); + assert_f128_biteq!((-0.0f128).next_down(), -tiny); + assert_f128_biteq!((0.0f128).next_down(), -tiny); + assert_f128_biteq!(tiny.next_down(), 0.0f128); + assert_f128_biteq!(tiny_up.next_down(), tiny); + assert_f128_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f128_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128); + assert_f128_biteq!(f128::MAX.next_down(), max_down); + assert_f128_biteq!(f128::INFINITY.next_down(), f128::MAX); + + // Check that NaNs roundtrip. + let nan0 = f128::NAN; + let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); + assert_f128_biteq!(nan0.next_down(), nan0); + assert_f128_biteq!(nan1.next_down(), nan1); + assert_f128_biteq!(nan2.next_down(), nan2); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_mul_add() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE); + assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE); + assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE); + assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f128.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_recip() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.recip(), 1.0); + assert_eq!(2.0f128.recip(), 0.5); + assert_eq!((-0.4f128).recip(), -2.5); + assert_eq!(0.0f128.recip(), inf); + assert_approx_eq!( + f128::MAX.recip(), + 8.40525785778023376565669454330438228902076605e-4933, + 1e-4900 + ); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_powi() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powi(1), 1.0); + assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL); + assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL); + assert_eq!(8.3f128.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] +fn test_sqrt_domain() { + assert!(f128::NAN.sqrt().is_nan()); + assert!(f128::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f128).sqrt().is_nan()); + assert_eq!((-0.0f128).sqrt(), -0.0); + assert_eq!(0.0f128.sqrt(), 0.0); + assert_eq!(1.0f128.sqrt(), 1.0); + assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); +} + +#[test] +fn test_to_degrees() { + let pi: f128 = consts::PI; + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(0.0f128.to_degrees(), 0.0); + assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_eq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703); +} + +#[test] +fn test_to_radians() { + let pi: f128 = consts::PI; + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(0.0f128.to_radians(), 0.0); + assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL); + assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL); + // check approx rather than exact because round trip for pi doesn't fall on an exactly + // representable value (unlike `f32` and `f64`). + assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000); + assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); + assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); + assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); + assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + let masked_nan1 = f128::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f128::NAN.to_bits() ^ NAN_MASK2; + assert!(f128::from_bits(masked_nan1).is_nan()); + assert!(f128::from_bits(masked_nan2).is_nan()); + + assert_eq!(f128::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f128.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f128.clamp(f128::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f128.clamp(3.0, f128::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u128 { + 1 << (f128::MANTISSA_DIGITS - 2) + } + + // FIXME(f16_f128): test subnormals when powf is available + // fn min_subnorm() -> f128 { + // f128::MIN_POSITIVE / f128::powf(2.0, f128::MANTISSA_DIGITS as f128 - 1.0) + // } + + // fn max_subnorm() -> f128 { + // f128::MIN_POSITIVE - min_subnorm() + // } + + fn q_nan() -> f128 { + f128::from_bits(f128::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f128 { + f128::from_bits((f128::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f128::INFINITY).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Equal, (-f128::MAX).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f128).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f128).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f128::MIN_POSITIVE).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f128).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f128.total_cmp(&0.0)); + // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f128::MIN_POSITIVE.total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f128.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f128.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f128::MAX.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Equal, f128::INFINITY.total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-f128::INFINITY).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-f128::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f128).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f128).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-f128::MIN_POSITIVE).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f128).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, 0.0_f128.total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f128::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f128.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f128.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, f128::MAX.total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, f128::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f128::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f128::MAX).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f128).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f128).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f128::MIN_POSITIVE).total_cmp(&-0.5)); + // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Greater, (-0.0_f128).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f128.total_cmp(&-0.0)); + // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Greater, f128::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f128.total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f128.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f128::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f128::INFINITY.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} + +#[test] +fn test_algebraic() { + let a: f128 = 123.0; + let b: f128 = 456.0; + + // Check that individual operations match their primitive counterparts. + // + // This is a check of current implementations and does NOT imply any form of + // guarantee about future behavior. The compiler reserves the right to make + // these operations inexact matches in the future. + let eps = if cfg!(miri) { 1e-6 } else { 0.0 }; + + assert_approx_eq!(a.algebraic_add(b), a + b, eps); + assert_approx_eq!(a.algebraic_sub(b), a - b, eps); + assert_approx_eq!(a.algebraic_mul(b), a * b, eps); + assert_approx_eq!(a.algebraic_div(b), a / b, eps); + assert_approx_eq!(a.algebraic_rem(b), a % b, eps); +} + +#[test] +fn test_from() { + assert_eq!(f128::from(false), 0.0); + assert_eq!(f128::from(true), 1.0); + assert_eq!(f128::from(u8::MIN), 0.0); + assert_eq!(f128::from(42_u8), 42.0); + assert_eq!(f128::from(u8::MAX), 255.0); + assert_eq!(f128::from(i8::MIN), -128.0); + assert_eq!(f128::from(42_i8), 42.0); + assert_eq!(f128::from(i8::MAX), 127.0); + assert_eq!(f128::from(u16::MIN), 0.0); + assert_eq!(f128::from(42_u16), 42.0); + assert_eq!(f128::from(u16::MAX), 65535.0); + assert_eq!(f128::from(i16::MIN), -32768.0); + assert_eq!(f128::from(42_i16), 42.0); + assert_eq!(f128::from(i16::MAX), 32767.0); + assert_eq!(f128::from(u32::MIN), 0.0); + assert_eq!(f128::from(42_u32), 42.0); + assert_eq!(f128::from(u32::MAX), 4294967295.0); + assert_eq!(f128::from(i32::MIN), -2147483648.0); + assert_eq!(f128::from(42_i32), 42.0); + assert_eq!(f128::from(i32::MAX), 2147483647.0); + // FIXME(f16_f128): Uncomment these tests once the From<{u64,i64}> impls are added. + // assert_eq!(f128::from(u64::MIN), 0.0); + // assert_eq!(f128::from(42_u64), 42.0); + // assert_eq!(f128::from(u64::MAX), 18446744073709551615.0); + // assert_eq!(f128::from(i64::MIN), -9223372036854775808.0); + // assert_eq!(f128::from(42_i64), 42.0); + // assert_eq!(f128::from(i64::MAX), 9223372036854775807.0); +} diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs new file mode 100644 index 0000000000000..db98181226c85 --- /dev/null +++ b/library/coretests/tests/floats/f16.rs @@ -0,0 +1,753 @@ +// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy +#![cfg(target_has_reliable_f16)] + +use std::f16::consts; +use std::num::FpCategory as Fp; + +/// Tolerance for results on the order of 10.0e-2 +#[allow(unused)] +const TOL_N2: f16 = 0.0001; + +/// Tolerance for results on the order of 10.0e+0 +#[allow(unused)] +const TOL_0: f16 = 0.01; + +/// Tolerance for results on the order of 10.0e+2 +#[allow(unused)] +const TOL_P2: f16 = 0.5; + +/// Tolerance for results on the order of 10.0e+4 +#[allow(unused)] +const TOL_P4: f16 = 10.0; + +/// Smallest number +const TINY_BITS: u16 = 0x1; + +/// Next smallest number +const TINY_UP_BITS: u16 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u16 = 0x7bfe; + +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u16 = 0x03ff; + +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u16 = 0x0400; + +/// First pattern over the mantissa +const NAN_MASK1: u16 = 0x02aa; + +/// Second pattern over the mantissa +const NAN_MASK2: u16 = 0x0155; + +/// Compare by representation +#[allow(unused_macros)] +macro_rules! assert_f16_biteq { + ($a:expr, $b:expr) => { + let (l, r): (&f16, &f16) = (&$a, &$b); + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})"); + }; +} + +#[test] +fn test_num_f16() { + super::test_num(10f16, 2f16); +} + +// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support +// the intrinsics. + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_min_nan() { + assert_eq!(f16::NAN.min(2.0), 2.0); + assert_eq!(2.0f16.min(f16::NAN), 2.0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_max_nan() { + assert_eq!(f16::NAN.max(2.0), 2.0); + assert_eq!(2.0f16.max(f16::NAN), 2.0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_minimum() { + assert!(f16::NAN.minimum(2.0).is_nan()); + assert!(2.0f16.minimum(f16::NAN).is_nan()); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_maximum() { + assert!(f16::NAN.maximum(2.0).is_nan()); + assert!(2.0f16.maximum(f16::NAN).is_nan()); +} + +#[test] +fn test_nan() { + let nan: f16 = f16::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert!(!nan.is_normal()); + assert_eq!(Fp::Nan, nan.classify()); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (f16::MANTISSA_DIGITS - 2)) != 0); +} + +#[test] +fn test_infinity() { + let inf: f16 = f16::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f16 = 0.0f16; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f16 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f16 = 1.0f16; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f16.is_nan()); + assert!(!5.3f16.is_nan()); + assert!(!(-10.732f16).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f16.is_infinite()); + assert!(!42.8f16.is_infinite()); + assert!(!(-109.2f16).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f16.is_finite()); + assert!(42.8f16.is_finite()); + assert!((-109.2f16).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let zero: f16 = 0.0f16; + let neg_zero: f16 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f16.is_normal()); + assert!(1e-4f16.is_normal()); + assert!(!1e-5f16.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let zero: f16 = 0.0f16; + let neg_zero: f16 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1f16.classify(), Fp::Normal); + assert_eq!(1e-4f16.classify(), Fp::Normal); + assert_eq!(1e-5f16.classify(), Fp::Subnormal); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_floor() { + assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_ceil() { + assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_round() { + assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0); + assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_trunc() { + assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_fract() { + assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0); + assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0); + assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0); + assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0); + assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0); + assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_abs() { + assert_eq!(f16::INFINITY.abs(), f16::INFINITY); + assert_eq!(1f16.abs(), 1f16); + assert_eq!(0f16.abs(), 0f16); + assert_eq!((-0f16).abs(), 0f16); + assert_eq!((-1f16).abs(), 1f16); + assert_eq!(f16::NEG_INFINITY.abs(), f16::INFINITY); + assert_eq!((1f16 / f16::NEG_INFINITY).abs(), 0f16); + assert!(f16::NAN.abs().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f16::INFINITY.is_sign_positive()); + assert!(1f16.is_sign_positive()); + assert!(0f16.is_sign_positive()); + assert!(!(-0f16).is_sign_positive()); + assert!(!(-1f16).is_sign_positive()); + assert!(!f16::NEG_INFINITY.is_sign_positive()); + assert!(!(1f16 / f16::NEG_INFINITY).is_sign_positive()); + assert!(f16::NAN.is_sign_positive()); + assert!(!(-f16::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f16::INFINITY.is_sign_negative()); + assert!(!1f16.is_sign_negative()); + assert!(!0f16.is_sign_negative()); + assert!((-0f16).is_sign_negative()); + assert!((-1f16).is_sign_negative()); + assert!(f16::NEG_INFINITY.is_sign_negative()); + assert!((1f16 / f16::NEG_INFINITY).is_sign_negative()); + assert!(!f16::NAN.is_sign_negative()); + assert!((-f16::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f16::from_bits(TINY_BITS); + let tiny_up = f16::from_bits(TINY_UP_BITS); + let max_down = f16::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); + assert_f16_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN); + assert_f16_biteq!(f16::MIN.next_up(), -max_down); + assert_f16_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0); + assert_f16_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f16_biteq!((-tiny_up).next_up(), -tiny); + assert_f16_biteq!((-tiny).next_up(), -0.0f16); + assert_f16_biteq!((-0.0f16).next_up(), tiny); + assert_f16_biteq!(0.0f16.next_up(), tiny); + assert_f16_biteq!(tiny.next_up(), tiny_up); + assert_f16_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f16_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON); + assert_f16_biteq!(f16::MAX.next_up(), f16::INFINITY); + assert_f16_biteq!(f16::INFINITY.next_up(), f16::INFINITY); + + // Check that NaNs roundtrip. + let nan0 = f16::NAN; + let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); + assert_f16_biteq!(nan0.next_up(), nan0); + assert_f16_biteq!(nan1.next_up(), nan1); + assert_f16_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f16::from_bits(TINY_BITS); + let tiny_up = f16::from_bits(TINY_UP_BITS); + let max_down = f16::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); + assert_f16_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY); + assert_f16_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY); + assert_f16_biteq!((-max_down).next_down(), f16::MIN); + assert_f16_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON); + assert_f16_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f16_biteq!((-tiny).next_down(), -tiny_up); + assert_f16_biteq!((-0.0f16).next_down(), -tiny); + assert_f16_biteq!((0.0f16).next_down(), -tiny); + assert_f16_biteq!(tiny.next_down(), 0.0f16); + assert_f16_biteq!(tiny_up.next_down(), tiny); + assert_f16_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f16_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16); + assert_f16_biteq!(f16::MAX.next_down(), max_down); + assert_f16_biteq!(f16::INFINITY.next_down(), f16::MAX); + + // Check that NaNs roundtrip. + let nan0 = f16::NAN; + let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); + assert_f16_biteq!(nan0.next_down(), nan0); + assert_f16_biteq!(nan1.next_down(), nan1); + assert_f16_biteq!(nan2.next_down(), nan2); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_mul_add() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2); + assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2); + assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0); + assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f16.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_recip() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.recip(), 1.0); + assert_eq!(2.0f16.recip(), 0.5); + assert_eq!((-0.4f16).recip(), -2.5); + assert_eq!(0.0f16.recip(), inf); + assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_powi() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.powi(1), 1.0); + assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0); + assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2); + assert_eq!(8.3f16.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_sqrt_domain() { + assert!(f16::NAN.sqrt().is_nan()); + assert!(f16::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f16).sqrt().is_nan()); + assert_eq!((-0.0f16).sqrt(), -0.0); + assert_eq!(0.0f16.sqrt(), 0.0); + assert_eq!(1.0f16.sqrt(), 1.0); + assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); +} + +#[test] +fn test_to_degrees() { + let pi: f16 = consts::PI; + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(0.0f16.to_degrees(), 0.0); + assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_eq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703); +} + +#[test] +fn test_to_radians() { + let pi: f16 = consts::PI; + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(0.0f16.to_radians(), 0.0); + assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0); + assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0); + assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f16).to_bits(), 0x3c00); + assert_eq!((12.5f16).to_bits(), 0x4a40); + assert_eq!((1337f16).to_bits(), 0x6539); + assert_eq!((-14.25f16).to_bits(), 0xcb20); + assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0); + assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0); + assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4); + assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f16::NAN.to_bits() ^ NAN_MASK2; + assert!(f16::from_bits(masked_nan1).is_nan()); + assert!(f16::from_bits(masked_nan2).is_nan()); + + assert_eq!(f16::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f16.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f16.clamp(f16::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f16.clamp(3.0, f16::NAN); +} + +#[test] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u16 { + 1 << (f16::MANTISSA_DIGITS - 2) + } + + fn min_subnorm() -> f16 { + f16::MIN_POSITIVE / f16::powf(2.0, f16::MANTISSA_DIGITS as f16 - 1.0) + } + + fn max_subnorm() -> f16 { + f16::MIN_POSITIVE - min_subnorm() + } + + fn q_nan() -> f16 { + f16::from_bits(f16::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f16 { + f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f16::INFINITY).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Equal, (-f16::MAX).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f16).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f16).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f16::MIN_POSITIVE).total_cmp(&-f16::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f16).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f16.total_cmp(&0.0)); + assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f16::MIN_POSITIVE.total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f16.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f16.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f16::MAX.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Equal, f16::INFINITY.total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-f16::INFINITY).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-f16::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f16).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f16).total_cmp(&-f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-f16::MIN_POSITIVE).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f16).total_cmp(&0.0)); + assert_eq!(Ordering::Less, 0.0_f16.total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f16::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f16.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f16.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, f16::MAX.total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f16::MAX).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f16).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f16).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f16::MIN_POSITIVE).total_cmp(&-0.5)); + assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f16::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Greater, (-0.0_f16).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f16.total_cmp(&-0.0)); + assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Greater, f16::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f16.total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f16.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f16::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f16::INFINITY.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} + +#[test] +fn test_algebraic() { + let a: f16 = 123.0; + let b: f16 = 456.0; + + // Check that individual operations match their primitive counterparts. + // + // This is a check of current implementations and does NOT imply any form of + // guarantee about future behavior. The compiler reserves the right to make + // these operations inexact matches in the future. + let eps_add = if cfg!(miri) { 1e1 } else { 0.0 }; + let eps_mul = if cfg!(miri) { 1e3 } else { 0.0 }; + let eps_div = if cfg!(miri) { 1e0 } else { 0.0 }; + + assert_approx_eq!(a.algebraic_add(b), a + b, eps_add); + assert_approx_eq!(a.algebraic_sub(b), a - b, eps_add); + assert_approx_eq!(a.algebraic_mul(b), a * b, eps_mul); + assert_approx_eq!(a.algebraic_div(b), a / b, eps_div); + assert_approx_eq!(a.algebraic_rem(b), a % b, eps_div); +} + +#[test] +fn test_from() { + assert_eq!(f16::from(false), 0.0); + assert_eq!(f16::from(true), 1.0); + assert_eq!(f16::from(u8::MIN), 0.0); + assert_eq!(f16::from(42_u8), 42.0); + assert_eq!(f16::from(u8::MAX), 255.0); + assert_eq!(f16::from(i8::MIN), -128.0); + assert_eq!(f16::from(42_i8), 42.0); + assert_eq!(f16::from(i8::MAX), 127.0); +} diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs new file mode 100644 index 0000000000000..36f1937bedfeb --- /dev/null +++ b/library/coretests/tests/floats/f32.rs @@ -0,0 +1,702 @@ +use core::f32; +use core::f32::consts; +use core::num::FpCategory as Fp; + +/// Smallest number +const TINY_BITS: u32 = 0x1; + +/// Next smallest number +const TINY_UP_BITS: u32 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u32 = 0x7f7f_fffe; + +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff; + +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000; + +/// First pattern over the mantissa +const NAN_MASK1: u32 = 0x002a_aaaa; + +/// Second pattern over the mantissa +const NAN_MASK2: u32 = 0x0055_5555; + +#[allow(unused_macros)] +macro_rules! assert_f32_biteq { + ($left : expr, $right : expr) => { + let l: &f32 = &$left; + let r: &f32 = &$right; + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})"); + }; +} + +#[test] +fn test_num_f32() { + super::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_minimum() { + assert!(f32::NAN.minimum(2.0).is_nan()); + assert!(2.0f32.minimum(f32::NAN).is_nan()); +} + +#[test] +fn test_maximum() { + assert!(f32::NAN.maximum(2.0).is_nan()); + assert!(2.0f32.maximum(f32::NAN).is_nan()); +} + +#[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()); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (f32::MANTISSA_DIGITS - 2)) != 0); +} + +#[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!(f32::math::floor(1.0f32), 1.0f32); + assert_approx_eq!(f32::math::floor(1.3f32), 1.0f32); + assert_approx_eq!(f32::math::floor(1.5f32), 1.0f32); + assert_approx_eq!(f32::math::floor(1.7f32), 1.0f32); + assert_approx_eq!(f32::math::floor(0.0f32), 0.0f32); + assert_approx_eq!(f32::math::floor(-0.0f32), -0.0f32); + assert_approx_eq!(f32::math::floor(-1.0f32), -1.0f32); + assert_approx_eq!(f32::math::floor(-1.3f32), -2.0f32); + assert_approx_eq!(f32::math::floor(-1.5f32), -2.0f32); + assert_approx_eq!(f32::math::floor(-1.7f32), -2.0f32); +} + +#[test] +fn test_ceil() { + assert_approx_eq!(f32::math::ceil(1.0f32), 1.0f32); + assert_approx_eq!(f32::math::ceil(1.3f32), 2.0f32); + assert_approx_eq!(f32::math::ceil(1.5f32), 2.0f32); + assert_approx_eq!(f32::math::ceil(1.7f32), 2.0f32); + assert_approx_eq!(f32::math::ceil(0.0f32), 0.0f32); + assert_approx_eq!(f32::math::ceil(-0.0f32), -0.0f32); + assert_approx_eq!(f32::math::ceil(-1.0f32), -1.0f32); + assert_approx_eq!(f32::math::ceil(-1.3f32), -1.0f32); + assert_approx_eq!(f32::math::ceil(-1.5f32), -1.0f32); + assert_approx_eq!(f32::math::ceil(-1.7f32), -1.0f32); +} + +#[test] +fn test_round() { + assert_approx_eq!(f32::math::round(2.5f32), 3.0f32); + assert_approx_eq!(f32::math::round(1.0f32), 1.0f32); + assert_approx_eq!(f32::math::round(1.3f32), 1.0f32); + assert_approx_eq!(f32::math::round(1.5f32), 2.0f32); + assert_approx_eq!(f32::math::round(1.7f32), 2.0f32); + assert_approx_eq!(f32::math::round(0.0f32), 0.0f32); + assert_approx_eq!(f32::math::round(-0.0f32), -0.0f32); + assert_approx_eq!(f32::math::round(-1.0f32), -1.0f32); + assert_approx_eq!(f32::math::round(-1.3f32), -1.0f32); + assert_approx_eq!(f32::math::round(-1.5f32), -2.0f32); + assert_approx_eq!(f32::math::round(-1.7f32), -2.0f32); +} + +#[test] +fn test_round_ties_even() { + assert_approx_eq!(f32::math::round_ties_even(2.5f32), 2.0f32); + assert_approx_eq!(f32::math::round_ties_even(1.0f32), 1.0f32); + assert_approx_eq!(f32::math::round_ties_even(1.3f32), 1.0f32); + assert_approx_eq!(f32::math::round_ties_even(1.5f32), 2.0f32); + assert_approx_eq!(f32::math::round_ties_even(1.7f32), 2.0f32); + assert_approx_eq!(f32::math::round_ties_even(0.0f32), 0.0f32); + assert_approx_eq!(f32::math::round_ties_even(-0.0f32), -0.0f32); + assert_approx_eq!(f32::math::round_ties_even(-1.0f32), -1.0f32); + assert_approx_eq!(f32::math::round_ties_even(-1.3f32), -1.0f32); + assert_approx_eq!(f32::math::round_ties_even(-1.5f32), -2.0f32); + assert_approx_eq!(f32::math::round_ties_even(-1.7f32), -2.0f32); +} + +#[test] +fn test_trunc() { + assert_approx_eq!(f32::math::trunc(1.0f32), 1.0f32); + assert_approx_eq!(f32::math::trunc(1.3f32), 1.0f32); + assert_approx_eq!(f32::math::trunc(1.5f32), 1.0f32); + assert_approx_eq!(f32::math::trunc(1.7f32), 1.0f32); + assert_approx_eq!(f32::math::trunc(0.0f32), 0.0f32); + assert_approx_eq!(f32::math::trunc(-0.0f32), -0.0f32); + assert_approx_eq!(f32::math::trunc(-1.0f32), -1.0f32); + assert_approx_eq!(f32::math::trunc(-1.3f32), -1.0f32); + assert_approx_eq!(f32::math::trunc(-1.5f32), -1.0f32); + assert_approx_eq!(f32::math::trunc(-1.7f32), -1.0f32); +} + +#[test] +fn test_fract() { + assert_approx_eq!(f32::math::fract(1.0f32), 0.0f32); + assert_approx_eq!(f32::math::fract(1.3f32), 0.3f32); + assert_approx_eq!(f32::math::fract(1.5f32), 0.5f32); + assert_approx_eq!(f32::math::fract(1.7f32), 0.7f32); + assert_approx_eq!(f32::math::fract(0.0f32), 0.0f32); + assert_approx_eq!(f32::math::fract(-0.0f32), -0.0f32); + assert_approx_eq!(f32::math::fract(-1.0f32), -0.0f32); + assert_approx_eq!(f32::math::fract(-1.3f32), -0.3f32); + assert_approx_eq!(f32::math::fract(-1.5f32), -0.5f32); + assert_approx_eq!(f32::math::fract(-1.7f32), -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_next_up() { + let tiny = f32::from_bits(TINY_BITS); + let tiny_up = f32::from_bits(TINY_UP_BITS); + let max_down = f32::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); + assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); + assert_f32_biteq!(f32::MIN.next_up(), -max_down); + assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0); + assert_f32_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f32_biteq!((-tiny_up).next_up(), -tiny); + assert_f32_biteq!((-tiny).next_up(), -0.0f32); + assert_f32_biteq!((-0.0f32).next_up(), tiny); + assert_f32_biteq!(0.0f32.next_up(), tiny); + assert_f32_biteq!(tiny.next_up(), tiny_up); + assert_f32_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f32_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON); + assert_f32_biteq!(f32::MAX.next_up(), f32::INFINITY); + assert_f32_biteq!(f32::INFINITY.next_up(), f32::INFINITY); + + // Check that NaNs roundtrip. + let nan0 = f32::NAN; + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); + assert_f32_biteq!(nan0.next_up(), nan0); + assert_f32_biteq!(nan1.next_up(), nan1); + assert_f32_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f32::from_bits(TINY_BITS); + let tiny_up = f32::from_bits(TINY_UP_BITS); + let max_down = f32::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); + assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); + assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); + assert_f32_biteq!((-max_down).next_down(), f32::MIN); + assert_f32_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); + assert_f32_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f32_biteq!((-tiny).next_down(), -tiny_up); + assert_f32_biteq!((-0.0f32).next_down(), -tiny); + assert_f32_biteq!((0.0f32).next_down(), -tiny); + assert_f32_biteq!(tiny.next_down(), 0.0f32); + assert_f32_biteq!(tiny_up.next_down(), tiny); + assert_f32_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f32_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32); + assert_f32_biteq!(f32::MAX.next_down(), max_down); + assert_f32_biteq!(f32::INFINITY.next_down(), f32::MAX); + + // Check that NaNs roundtrip. + let nan0 = f32::NAN; + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); + assert_f32_biteq!(nan0.next_down(), nan0); + assert_f32_biteq!(nan1.next_down(), nan1); + assert_f32_biteq!(nan2.next_down(), nan2); +} + +// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ +#[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] +#[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!(f32::math::mul_add(12.3f32, 4.5, 6.7), 62.05); + assert_approx_eq!(f32::math::mul_add(-12.3f32, -4.5, -6.7), 48.65); + assert_approx_eq!(f32::math::mul_add(0.0f32, 8.9, 1.2), 1.2); + assert_approx_eq!(f32::math::mul_add(3.4f32, -0.0, 5.6), 5.6); + assert!(f32::math::mul_add(nan, 7.8, 9.0).is_nan()); + assert_eq!(f32::math::mul_add(inf, 7.8, 9.0), inf); + assert_eq!(f32::math::mul_add(neg_inf, 7.8, 9.0), neg_inf); + assert_eq!(f32::math::mul_add(8.9f32, inf, 3.2), inf); + assert_eq!(f32::math::mul_add(-3.2f32, 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_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_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_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() ^ NAN_MASK1; + let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2; + assert!(f32::from_bits(masked_nan1).is_nan()); + assert!(f32::from_bits(masked_nan2).is_nan()); + + 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())); +} + +#[test] +fn test_algebraic() { + let a: f32 = 123.0; + let b: f32 = 456.0; + + // Check that individual operations match their primitive counterparts. + // + // This is a check of current implementations and does NOT imply any form of + // guarantee about future behavior. The compiler reserves the right to make + // these operations inexact matches in the future. + let eps_add = if cfg!(miri) { 1e-3 } else { 0.0 }; + let eps_mul = if cfg!(miri) { 1e-1 } else { 0.0 }; + let eps_div = if cfg!(miri) { 1e-4 } else { 0.0 }; + + assert_approx_eq!(a.algebraic_add(b), a + b, eps_add); + assert_approx_eq!(a.algebraic_sub(b), a - b, eps_add); + assert_approx_eq!(a.algebraic_mul(b), a * b, eps_mul); + assert_approx_eq!(a.algebraic_div(b), a / b, eps_div); + assert_approx_eq!(a.algebraic_rem(b), a % b, eps_div); +} diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs new file mode 100644 index 0000000000000..970519983538d --- /dev/null +++ b/library/coretests/tests/floats/f64.rs @@ -0,0 +1,683 @@ +use core::f64; +use core::f64::consts; +use core::num::FpCategory as Fp; + +/// Smallest number +const TINY_BITS: u64 = 0x1; + +/// Next smallest number +const TINY_UP_BITS: u64 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe; + +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff; + +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000; + +/// First pattern over the mantissa +const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; + +/// Second pattern over the mantissa +const NAN_MASK2: u64 = 0x0005_5555_5555_5555; + +#[allow(unused_macros)] +macro_rules! assert_f64_biteq { + ($left : expr, $right : expr) => { + let l: &f64 = &$left; + let r: &f64 = &$right; + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l} ({lb:#018x}) is not bitequal to {r} ({rb:#018x})"); + }; +} + +#[test] +fn test_num_f64() { + super::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()); + // Ensure the quiet bit is set. + assert!(nan.to_bits() & (1 << (f64::MANTISSA_DIGITS - 2)) != 0); +} + +#[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()); +} + +#[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()); +} + +#[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()); +} + +#[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!(f64::math::floor(1.0f64), 1.0f64); + assert_approx_eq!(f64::math::floor(1.3f64), 1.0f64); + assert_approx_eq!(f64::math::floor(1.5f64), 1.0f64); + assert_approx_eq!(f64::math::floor(1.7f64), 1.0f64); + assert_approx_eq!(f64::math::floor(0.0f64), 0.0f64); + assert_approx_eq!(f64::math::floor(-0.0f64), -0.0f64); + assert_approx_eq!(f64::math::floor(-1.0f64), -1.0f64); + assert_approx_eq!(f64::math::floor(-1.3f64), -2.0f64); + assert_approx_eq!(f64::math::floor(-1.5f64), -2.0f64); + assert_approx_eq!(f64::math::floor(-1.7f64), -2.0f64); +} + +#[test] +fn test_ceil() { + assert_approx_eq!(f64::math::ceil(1.0f64), 1.0f64); + assert_approx_eq!(f64::math::ceil(1.3f64), 2.0f64); + assert_approx_eq!(f64::math::ceil(1.5f64), 2.0f64); + assert_approx_eq!(f64::math::ceil(1.7f64), 2.0f64); + assert_approx_eq!(f64::math::ceil(0.0f64), 0.0f64); + assert_approx_eq!(f64::math::ceil(-0.0f64), -0.0f64); + assert_approx_eq!(f64::math::ceil(-1.0f64), -1.0f64); + assert_approx_eq!(f64::math::ceil(-1.3f64), -1.0f64); + assert_approx_eq!(f64::math::ceil(-1.5f64), -1.0f64); + assert_approx_eq!(f64::math::ceil(-1.7f64), -1.0f64); +} + +#[test] +fn test_round() { + assert_approx_eq!(f64::math::round(2.5f64), 3.0f64); + assert_approx_eq!(f64::math::round(1.0f64), 1.0f64); + assert_approx_eq!(f64::math::round(1.3f64), 1.0f64); + assert_approx_eq!(f64::math::round(1.5f64), 2.0f64); + assert_approx_eq!(f64::math::round(1.7f64), 2.0f64); + assert_approx_eq!(f64::math::round(0.0f64), 0.0f64); + assert_approx_eq!(f64::math::round(-0.0f64), -0.0f64); + assert_approx_eq!(f64::math::round(-1.0f64), -1.0f64); + assert_approx_eq!(f64::math::round(-1.3f64), -1.0f64); + assert_approx_eq!(f64::math::round(-1.5f64), -2.0f64); + assert_approx_eq!(f64::math::round(-1.7f64), -2.0f64); +} + +#[test] +fn test_round_ties_even() { + assert_approx_eq!(f64::math::round_ties_even(2.5f64), 2.0f64); + assert_approx_eq!(f64::math::round_ties_even(1.0f64), 1.0f64); + assert_approx_eq!(f64::math::round_ties_even(1.3f64), 1.0f64); + assert_approx_eq!(f64::math::round_ties_even(1.5f64), 2.0f64); + assert_approx_eq!(f64::math::round_ties_even(1.7f64), 2.0f64); + assert_approx_eq!(f64::math::round_ties_even(0.0f64), 0.0f64); + assert_approx_eq!(f64::math::round_ties_even(-0.0f64), -0.0f64); + assert_approx_eq!(f64::math::round_ties_even(-1.0f64), -1.0f64); + assert_approx_eq!(f64::math::round_ties_even(-1.3f64), -1.0f64); + assert_approx_eq!(f64::math::round_ties_even(-1.5f64), -2.0f64); + assert_approx_eq!(f64::math::round_ties_even(-1.7f64), -2.0f64); +} + +#[test] +fn test_trunc() { + assert_approx_eq!(f64::math::trunc(1.0f64), 1.0f64); + assert_approx_eq!(f64::math::trunc(1.3f64), 1.0f64); + assert_approx_eq!(f64::math::trunc(1.5f64), 1.0f64); + assert_approx_eq!(f64::math::trunc(1.7f64), 1.0f64); + assert_approx_eq!(f64::math::trunc(0.0f64), 0.0f64); + assert_approx_eq!(f64::math::trunc(-0.0f64), -0.0f64); + assert_approx_eq!(f64::math::trunc(-1.0f64), -1.0f64); + assert_approx_eq!(f64::math::trunc(-1.3f64), -1.0f64); + assert_approx_eq!(f64::math::trunc(-1.5f64), -1.0f64); + assert_approx_eq!(f64::math::trunc(-1.7f64), -1.0f64); +} + +#[test] +fn test_fract() { + assert_approx_eq!(f64::math::fract(1.0f64), 0.0f64); + assert_approx_eq!(f64::math::fract(1.3f64), 0.3f64); + assert_approx_eq!(f64::math::fract(1.5f64), 0.5f64); + assert_approx_eq!(f64::math::fract(1.7f64), 0.7f64); + assert_approx_eq!(f64::math::fract(0.0f64), 0.0f64); + assert_approx_eq!(f64::math::fract(-0.0f64), -0.0f64); + assert_approx_eq!(f64::math::fract(-1.0f64), -0.0f64); + assert_approx_eq!(f64::math::fract(-1.3f64), -0.3f64); + assert_approx_eq!(f64::math::fract(-1.5f64), -0.5f64); + assert_approx_eq!(f64::math::fract(-1.7f64), -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_next_up() { + let tiny = f64::from_bits(TINY_BITS); + let tiny_up = f64::from_bits(TINY_UP_BITS); + let max_down = f64::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); + assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); + assert_f64_biteq!(f64::MIN.next_up(), -max_down); + assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0); + assert_f64_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f64_biteq!((-tiny_up).next_up(), -tiny); + assert_f64_biteq!((-tiny).next_up(), -0.0f64); + assert_f64_biteq!((-0.0f64).next_up(), tiny); + assert_f64_biteq!(0.0f64.next_up(), tiny); + assert_f64_biteq!(tiny.next_up(), tiny_up); + assert_f64_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f64_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON); + assert_f64_biteq!(f64::MAX.next_up(), f64::INFINITY); + assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY); + + let nan0 = f64::NAN; + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); + assert_f64_biteq!(nan0.next_up(), nan0); + assert_f64_biteq!(nan1.next_up(), nan1); + assert_f64_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f64::from_bits(TINY_BITS); + let tiny_up = f64::from_bits(TINY_UP_BITS); + let max_down = f64::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); + assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); + assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); + assert_f64_biteq!((-max_down).next_down(), f64::MIN); + assert_f64_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); + assert_f64_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f64_biteq!((-tiny).next_down(), -tiny_up); + assert_f64_biteq!((-0.0f64).next_down(), -tiny); + assert_f64_biteq!((0.0f64).next_down(), -tiny); + assert_f64_biteq!(tiny.next_down(), 0.0f64); + assert_f64_biteq!(tiny_up.next_down(), tiny); + assert_f64_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f64_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64); + assert_f64_biteq!(f64::MAX.next_down(), max_down); + assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX); + + let nan0 = f64::NAN; + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); + assert_f64_biteq!(nan0.next_down(), nan0); + assert_f64_biteq!(nan1.next_down(), nan1); + assert_f64_biteq!(nan2.next_down(), nan2); +} + +// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ +#[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] +#[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_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_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_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 + let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f64::NAN.to_bits() ^ NAN_MASK2; + assert!(f64::from_bits(masked_nan1).is_nan()); + assert!(f64::from_bits(masked_nan2).is_nan()); + + 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())); +} + +#[test] +fn test_algebraic() { + let a: f64 = 123.0; + let b: f64 = 456.0; + + // Check that individual operations match their primitive counterparts. + // + // This is a check of current implementations and does NOT imply any form of + // guarantee about future behavior. The compiler reserves the right to make + // these operations inexact matches in the future. + let eps = if cfg!(miri) { 1e-6 } else { 0.0 }; + + assert_approx_eq!(a.algebraic_add(b), a + b, eps); + assert_approx_eq!(a.algebraic_sub(b), a - b, eps); + assert_approx_eq!(a.algebraic_mul(b), a * b, eps); + assert_approx_eq!(a.algebraic_div(b), a / b, eps); + assert_approx_eq!(a.algebraic_rem(b), a % b, eps); +} diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs new file mode 100644 index 0000000000000..7de34271ad05e --- /dev/null +++ b/library/coretests/tests/floats/mod.rs @@ -0,0 +1,40 @@ +use std::fmt; +use std::ops::{Add, Div, Mul, Rem, Sub}; + +/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. +macro_rules! assert_approx_eq { + ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; + ($a:expr, $b:expr, $lim:expr) => {{ + let (a, b) = (&$a, &$b); + let diff = (*a - *b).abs(); + assert!( + diff <= $lim, + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", + lim = $lim + ); + }}; +} + +/// Helper function for testing numeric operations +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); +} + +mod f128; +mod f16; +mod f32; +mod f64; diff --git a/library/coretests/tests/fmt/mod.rs b/library/coretests/tests/fmt/mod.rs index 025c69c4f6236..d9060fe903d24 100644 --- a/library/coretests/tests/fmt/mod.rs +++ b/library/coretests/tests/fmt/mod.rs @@ -15,8 +15,31 @@ fn test_format_flags() { fn test_pointer_formats_data_pointer() { let b: &[u8] = b""; let s: &str = ""; - assert_eq!(format!("{s:p}"), format!("{:p}", s.as_ptr())); - assert_eq!(format!("{b:p}"), format!("{:p}", b.as_ptr())); + assert_eq!(format!("{s:p}"), format!("{:p}", s as *const _)); + assert_eq!(format!("{b:p}"), format!("{:p}", b as *const _)); +} + +#[test] +fn test_fmt_debug_of_raw_pointers() { + use core::fmt::Debug; + use core::ptr; + + fn check_fmt(t: T, start: &str, contains: &str) { + let formatted = format!("{:?}", t); + assert!(formatted.starts_with(start), "{formatted:?} doesn't start with {start:?}"); + assert!(formatted.contains(contains), "{formatted:?} doesn't contain {contains:?}"); + } + + assert_eq!(format!("{:?}", ptr::without_provenance_mut::(0x100)), "0x100"); + assert_eq!(format!("{:?}", ptr::without_provenance::(0x100)), "0x100"); + + let slice = ptr::slice_from_raw_parts(ptr::without_provenance::(0x100), 3); + assert_eq!(format!("{:?}", slice as *mut [i32]), "Pointer { addr: 0x100, metadata: 3 }"); + assert_eq!(format!("{:?}", slice as *const [i32]), "Pointer { addr: 0x100, metadata: 3 }"); + + let vtable = &mut 500 as &mut dyn Debug; + check_fmt(vtable as *mut dyn Debug, "Pointer { addr: ", ", metadata: DynMetadata("); + check_fmt(vtable as *const dyn Debug, "Pointer { addr: ", ", metadata: DynMetadata("); } #[test] @@ -58,6 +81,7 @@ fn formatting_options_ctor() { } #[test] +#[allow(deprecated)] fn formatting_options_flags() { use core::fmt::*; for sign in [None, Some(Sign::Plus), Some(Sign::Minus)] { @@ -75,6 +99,25 @@ fn formatting_options_flags() { assert_eq!(formatting_options.get_alternate(), alternate); assert_eq!(formatting_options.get_sign_aware_zero_pad(), sign_aware_zero_pad); assert_eq!(formatting_options.get_debug_as_hex(), debug_as_hex); + + let mut output = String::new(); + let fmt = Formatter::new(&mut output, formatting_options); + assert_eq!(fmt.options(), formatting_options); + + assert_eq!(fmt.sign_minus(), sign == Some(Sign::Minus)); + assert_eq!(fmt.sign_plus(), sign == Some(Sign::Plus)); + assert_eq!(fmt.alternate(), alternate); + assert_eq!(fmt.sign_aware_zero_pad(), sign_aware_zero_pad); + + // The flags method is deprecated. + // This checks compatibility with older versions of Rust. + assert_eq!(fmt.flags() & 1 != 0, sign == Some(Sign::Plus)); + assert_eq!(fmt.flags() & 2 != 0, sign == Some(Sign::Minus)); + assert_eq!(fmt.flags() & 4 != 0, alternate); + assert_eq!(fmt.flags() & 8 != 0, sign_aware_zero_pad); + assert_eq!(fmt.flags() & 16 != 0, debug_as_hex == Some(DebugAsHex::Lower)); + assert_eq!(fmt.flags() & 32 != 0, debug_as_hex == Some(DebugAsHex::Upper)); + assert_eq!(fmt.flags() & 0xFFFF_FFC0, 0); } } } diff --git a/library/coretests/tests/hash/sip.rs b/library/coretests/tests/hash/sip.rs index f79954f916b77..6add1a33cb931 100644 --- a/library/coretests/tests/hash/sip.rs +++ b/library/coretests/tests/hash/sip.rs @@ -1,7 +1,7 @@ #![allow(deprecated)] use core::hash::{Hash, Hasher, SipHasher, SipHasher13}; -use core::{mem, slice}; +use core::slice; // Hash just the bytes of the slice, without length prefix struct Bytes<'a>(&'a [u8]); @@ -314,7 +314,7 @@ fn test_write_short_works() { h1.write_u8(0x01u8); let mut h2 = SipHasher::new(); h2.write(unsafe { - slice::from_raw_parts(&test_usize as *const _ as *const u8, mem::size_of::()) + slice::from_raw_parts(&test_usize as *const _ as *const u8, size_of::()) }); h2.write(b"bytes"); h2.write(b"string"); diff --git a/library/coretests/tests/hint.rs b/library/coretests/tests/hint.rs new file mode 100644 index 0000000000000..032bbc1dcc80f --- /dev/null +++ b/library/coretests/tests/hint.rs @@ -0,0 +1,23 @@ +#[test] +fn select_unpredictable_drop() { + use core::cell::Cell; + struct X<'a>(&'a Cell); + impl Drop for X<'_> { + fn drop(&mut self) { + self.0.set(true); + } + } + + let a_dropped = Cell::new(false); + let b_dropped = Cell::new(false); + let a = X(&a_dropped); + let b = X(&b_dropped); + assert!(!a_dropped.get()); + assert!(!b_dropped.get()); + let selected = core::hint::select_unpredictable(core::hint::black_box(true), a, b); + assert!(!a_dropped.get()); + assert!(b_dropped.get()); + drop(selected); + assert!(a_dropped.get()); + assert!(b_dropped.get()); +} diff --git a/library/coretests/tests/iter/adapters/enumerate.rs b/library/coretests/tests/iter/adapters/enumerate.rs index b57d51c077e9b..2294f856b58d6 100644 --- a/library/coretests/tests/iter/adapters/enumerate.rs +++ b/library/coretests/tests/iter/adapters/enumerate.rs @@ -120,3 +120,13 @@ fn test_double_ended_enumerate() { assert_eq!(it.next_back(), Some((2, 3))); assert_eq!(it.next(), None); } + +#[test] +fn test_empty_iterator_enumerate_next_index() { + let mut it = empty::().enumerate(); + assert_eq!(it.next_index(), 0); + assert_eq!(it.next_index(), 0); + assert_eq!(it.next(), None); + assert_eq!(it.next_index(), 0); + assert_eq!(it.next_index(), 0); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 4f21ae5013b66..b98e52718f60b 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -12,12 +12,12 @@ #![feature(async_iterator)] #![feature(bigint_helper_methods)] #![feature(bstr)] -#![feature(cell_update)] +#![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_max_len)] #![feature(clone_to_uninit)] #![feature(const_eval_select)] -#![feature(const_swap_nonoverlapping)] #![feature(const_trait_impl)] +#![feature(core_float_math)] #![feature(core_intrinsics)] #![feature(core_intrinsics_fallbacks)] #![feature(core_io_borrowed_buf)] @@ -26,10 +26,15 @@ #![feature(dec2flt)] #![feature(duration_constants)] #![feature(duration_constructors)] +#![feature(duration_constructors_lite)] #![feature(error_generic_member_access)] #![feature(exact_size_is_empty)] #![feature(extend_one)] #![feature(extern_types)] +#![feature(f128)] +#![feature(f16)] +#![feature(float_algebraic)] +#![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(flt2dec)] #![feature(fmt_internals)] @@ -39,7 +44,6 @@ #![feature(generic_assert_internals)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] -#![feature(inline_const_pat)] #![feature(int_roundings)] #![feature(ip)] #![feature(ip_from)] @@ -64,6 +68,7 @@ #![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(never_type)] +#![feature(next_index)] #![feature(numfmt)] #![feature(pattern)] #![feature(pointer_is_aligned_to)] @@ -73,7 +78,6 @@ #![feature(slice_internals)] #![feature(slice_partition_dedup)] #![feature(slice_split_once)] -#![feature(slice_take)] #![feature(split_array)] #![feature(split_as_slice)] #![feature(std_internals)] @@ -88,7 +92,6 @@ #![feature(try_find)] #![feature(try_trait_v2)] #![feature(unsize)] -#![feature(unsized_tuple_coercion)] #![feature(unwrap_infallible)] // tidy-alphabetical-end #![allow(internal_features)] @@ -97,16 +100,17 @@ /// Version of `assert_matches` that ignores fancy runtime printing in const context and uses structural equality. macro_rules! assert_eq_const_safe { - ($left:expr, $right:expr) => { - assert_eq_const_safe!($left, $right, concat!(stringify!($left), " == ", stringify!($right))); + ($t:ty: $left:expr, $right:expr) => { + assert_eq_const_safe!($t: $left, $right, concat!(stringify!($left), " == ", stringify!($right))); }; - ($left:expr, $right:expr$(, $($arg:tt)+)?) => { + ($t:ty: $left:expr, $right:expr$(, $($arg:tt)+)?) => { { fn runtime() { assert_eq!($left, $right, $($($arg)*),*); } const fn compiletime() { - assert!(matches!($left, const { $right })); + const PAT: $t = $right; + assert!(matches!($left, PAT), $($($arg)*),*); } core::intrinsics::const_eval_select((), compiletime, runtime) } @@ -146,9 +150,11 @@ mod cmp; mod const_ptr; mod convert; mod ffi; +mod floats; mod fmt; mod future; mod hash; +mod hint; mod intrinsics; mod io; mod iter; diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs index 9cb94ca3b0ff0..9c15be4a8c4bd 100644 --- a/library/coretests/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -1,5 +1,5 @@ use core::mem::*; -use core::ptr; +use core::{array, ptr}; #[cfg(panic = "unwind")] use std::rc::Rc; @@ -327,11 +327,11 @@ fn uninit_write_clone_of_slice_no_drop() { } #[test] -fn uninit_fill() { +fn uninit_write_filled() { let mut dst = [MaybeUninit::new(255); 64]; let expect = [0; 64]; - assert_eq!(MaybeUninit::fill(&mut dst, 0), &expect); + assert_eq!(dst.write_filled(0), &expect); } #[cfg(panic = "unwind")] @@ -352,7 +352,7 @@ impl Clone for CloneUntilPanic { #[test] #[cfg(panic = "unwind")] -fn uninit_fill_clone_panic_drop() { +fn uninit_write_filled_panic_drop() { use std::panic; let rc = Rc::new(()); @@ -361,7 +361,7 @@ fn uninit_fill_clone_panic_drop() { let src = CloneUntilPanic { limit: 3, rc: rc.clone() }; let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { - MaybeUninit::fill(&mut dst, src); + dst.write_filled(src); })); match err { @@ -378,23 +378,23 @@ fn uninit_fill_clone_panic_drop() { #[test] #[cfg(panic = "unwind")] -fn uninit_fill_clone_no_drop_clones() { +fn uninit_write_filled_no_drop_clones() { let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()]; - MaybeUninit::fill(&mut dst, Bomb); + dst.write_filled(Bomb); } #[test] -fn uninit_fill_with() { - let mut dst = [MaybeUninit::new(255); 64]; - let expect = [0; 64]; +fn uninit_write_with() { + let mut dst = [MaybeUninit::new(255usize); 64]; + let expect = array::from_fn::(|idx| idx); - assert_eq!(MaybeUninit::fill_with(&mut dst, || 0), &expect); + assert_eq!(dst.write_with(|idx| idx), &expect); } #[test] #[cfg(panic = "unwind")] -fn uninit_fill_with_mid_panic() { +fn uninit_write_with_mid_panic() { use std::panic; let rc = Rc::new(()); @@ -403,7 +403,7 @@ fn uninit_fill_with_mid_panic() { let src = CloneUntilPanic { limit: 3, rc: rc.clone() }; let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { - MaybeUninit::fill_with(&mut dst, || src.clone()); + dst.write_with(|_| src.clone()); })); drop(src); @@ -423,58 +423,58 @@ fn uninit_fill_with_mid_panic() { #[test] #[cfg(panic = "unwind")] -fn uninit_fill_with_no_drop() { +fn uninit_write_with_no_drop() { let mut dst = [MaybeUninit::uninit()]; let src = Bomb; - MaybeUninit::fill_with(&mut dst, || src.clone()); + dst.write_with(|_| src.clone()); forget(src); } #[test] -fn uninit_fill_from() { +fn uninit_write_iter() { let mut dst = [MaybeUninit::new(255); 64]; let src = [0; 64]; - let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter()); + let (initted, remainder) = dst.write_iter(src.into_iter()); assert_eq!(initted, &src); assert_eq!(remainder.len(), 0); } #[test] -fn uninit_fill_from_partial() { +fn uninit_write_iter_partial() { let mut dst = [MaybeUninit::new(255); 64]; let src = [0; 48]; - let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter()); + let (initted, remainder) = dst.write_iter(src.into_iter()); assert_eq!(initted, &src); assert_eq!(remainder.len(), 16); } #[test] -fn uninit_over_fill() { +fn uninit_write_iter_overfill() { let mut dst = [MaybeUninit::new(255); 64]; let src = [0; 72]; - let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter()); + let (initted, remainder) = dst.write_iter(src.into_iter()); assert_eq!(initted, &src[0..64]); assert_eq!(remainder.len(), 0); } #[test] -fn uninit_empty_fill() { +fn uninit_write_iter_empty() { let mut dst = [MaybeUninit::new(255); 64]; let src = [0; 0]; - let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter()); + let (initted, remainder) = dst.write_iter(src.into_iter()); assert_eq!(initted, &src[0..0]); assert_eq!(remainder.len(), 64); } #[test] #[cfg(panic = "unwind")] -fn uninit_fill_from_mid_panic() { +fn uninit_write_iter_mid_panic() { use std::panic; struct IterUntilPanic { @@ -504,7 +504,7 @@ fn uninit_fill_from_mid_panic() { let src = IterUntilPanic { limit: 3, rc: rc.clone() }; let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { - MaybeUninit::fill_from(&mut dst, src); + dst.write_iter(src); })); match err { @@ -522,11 +522,11 @@ fn uninit_fill_from_mid_panic() { #[test] #[cfg(panic = "unwind")] -fn uninit_fill_from_no_drop() { +fn uninit_write_iter_no_drop() { let mut dst = [MaybeUninit::uninit()]; let src = [Bomb]; - MaybeUninit::fill_from(&mut dst, src.iter()); + dst.write_iter(src.iter()); forget(src); } diff --git a/library/coretests/tests/net/ip_addr.rs b/library/coretests/tests/net/ip_addr.rs index f01b43282ec42..3fec59d67b748 100644 --- a/library/coretests/tests/net/ip_addr.rs +++ b/library/coretests/tests/net/ip_addr.rs @@ -689,6 +689,8 @@ fn ipv6_properties() { check!("2002::", &[0x20, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); + check!("5f00::", &[0x5f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); + check!("fc00::", &[0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unique_local); check!( diff --git a/library/coretests/tests/nonzero.rs b/library/coretests/tests/nonzero.rs index bdc5701d9fd23..00232c9b7061a 100644 --- a/library/coretests/tests/nonzero.rs +++ b/library/coretests/tests/nonzero.rs @@ -1,6 +1,5 @@ use core::num::{IntErrorKind, NonZero}; use core::option::Option::None; -use std::mem::size_of; #[test] fn test_create_nonzero_instance() { diff --git a/library/coretests/tests/num/dec2flt/decimal.rs b/library/coretests/tests/num/dec2flt/decimal.rs new file mode 100644 index 0000000000000..f759e1dbde6cb --- /dev/null +++ b/library/coretests/tests/num/dec2flt/decimal.rs @@ -0,0 +1,42 @@ +use core::num::dec2flt::decimal::Decimal; + +type FPath = ((i64, u64, bool, bool), Option); + +const FPATHS_F32: &[FPath] = + &[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))]; +const FPATHS_F64: &[FPath] = + &[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))]; + +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn check_fast_path_f16() { + const FPATHS_F16: &[FPath] = + &[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))]; + for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F16.iter().copied() { + let dec = Decimal { exponent, mantissa, negative, many_digits }; + let actual = dec.try_fast_path::(); + + assert_eq!(actual, expected); + } +} + +#[test] +fn check_fast_path_f32() { + for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F32.iter().copied() { + let dec = Decimal { exponent, mantissa, negative, many_digits }; + let actual = dec.try_fast_path::(); + + assert_eq!(actual, expected); + } +} + +#[test] +fn check_fast_path_f64() { + for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F64.iter().copied() { + let dec = Decimal { exponent, mantissa, negative, many_digits }; + let actual = dec.try_fast_path::(); + + assert_eq!(actual, expected); + } +} diff --git a/library/coretests/tests/num/dec2flt/decimal_seq.rs b/library/coretests/tests/num/dec2flt/decimal_seq.rs new file mode 100644 index 0000000000000..f46ba7c465a6e --- /dev/null +++ b/library/coretests/tests/num/dec2flt/decimal_seq.rs @@ -0,0 +1,30 @@ +use core::num::dec2flt::decimal_seq::{DecimalSeq, parse_decimal_seq}; + +#[test] +fn test_trim() { + let mut dec = DecimalSeq::default(); + let digits = [1, 2, 3, 4]; + + dec.digits[0..4].copy_from_slice(&digits); + dec.num_digits = 8; + dec.trim(); + + assert_eq!(dec.digits[0..4], digits); + assert_eq!(dec.num_digits, 4); +} + +#[test] +fn test_parse() { + let tests = [("1.234", [1, 2, 3, 4], 1)]; + + for (s, exp_digits, decimal_point) in tests { + let actual = parse_decimal_seq(s.as_bytes()); + let mut digits = [0; DecimalSeq::MAX_DIGITS]; + digits[..exp_digits.len()].copy_from_slice(&exp_digits); + + let expected = + DecimalSeq { num_digits: exp_digits.len(), decimal_point, truncated: false, digits }; + + assert_eq!(actual, expected); + } +} diff --git a/library/coretests/tests/num/dec2flt/float.rs b/library/coretests/tests/num/dec2flt/float.rs index 7a9587a18d030..264de061be98c 100644 --- a/library/coretests/tests/num/dec2flt/float.rs +++ b/library/coretests/tests/num/dec2flt/float.rs @@ -1,5 +1,24 @@ use core::num::dec2flt::float::RawFloat; +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn test_f16_integer_decode() { + assert_eq!(3.14159265359f16.integer_decode(), (1608, -9, 1)); + assert_eq!((-8573.5918555f16).integer_decode(), (1072, 3, -1)); + #[cfg(not(miri))] // miri doesn't have powf16 + assert_eq!(2f16.powf(14.0).integer_decode(), (1 << 10, 4, 1)); + assert_eq!(0f16.integer_decode(), (0, -25, 1)); + assert_eq!((-0f16).integer_decode(), (0, -25, -1)); + assert_eq!(f16::INFINITY.integer_decode(), (1 << 10, 6, 1)); + assert_eq!(f16::NEG_INFINITY.integer_decode(), (1 << 10, 6, -1)); + + // Ignore the "sign" (quiet / signalling flag) of NAN. + // It can vary between runtime operations and LLVM folding. + let (nan_m, nan_p, _nan_s) = f16::NAN.integer_decode(); + assert_eq!((nan_m, nan_p), (1536, 6)); +} + #[test] fn test_f32_integer_decode() { assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1)); @@ -12,8 +31,8 @@ fn test_f32_integer_decode() { // Ignore the "sign" (quiet / signalling flag) of NAN. // It can vary between runtime operations and LLVM folding. - let (nan_m, nan_e, _nan_s) = f32::NAN.integer_decode(); - assert_eq!((nan_m, nan_e), (12582912, 105)); + let (nan_m, nan_p, _nan_s) = f32::NAN.integer_decode(); + assert_eq!((nan_m, nan_p), (12582912, 105)); } #[test] @@ -28,6 +47,67 @@ fn test_f64_integer_decode() { // Ignore the "sign" (quiet / signalling flag) of NAN. // It can vary between runtime operations and LLVM folding. - let (nan_m, nan_e, _nan_s) = f64::NAN.integer_decode(); - assert_eq!((nan_m, nan_e), (6755399441055744, 972)); + let (nan_m, nan_p, _nan_s) = f64::NAN.integer_decode(); + assert_eq!((nan_m, nan_p), (6755399441055744, 972)); +} + +/* Sanity checks of computed magic numbers */ + +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn test_f16_consts() { + assert_eq!(::INFINITY, f16::INFINITY); + assert_eq!(::NEG_INFINITY, -f16::INFINITY); + assert_eq!(::NAN.to_bits(), f16::NAN.to_bits()); + assert_eq!(::NEG_NAN.to_bits(), (-f16::NAN).to_bits()); + assert_eq!(::SIG_BITS, 10); + assert_eq!(::MIN_EXPONENT_ROUND_TO_EVEN, -22); + assert_eq!(::MAX_EXPONENT_ROUND_TO_EVEN, 5); + assert_eq!(::MIN_EXPONENT_FAST_PATH, -4); + assert_eq!(::MAX_EXPONENT_FAST_PATH, 4); + assert_eq!(::MAX_EXPONENT_DISGUISED_FAST_PATH, 7); + assert_eq!(::EXP_MIN, -14); + assert_eq!(::EXP_SAT, 0x1f); + assert_eq!(::SMALLEST_POWER_OF_TEN, -27); + assert_eq!(::LARGEST_POWER_OF_TEN, 4); + assert_eq!(::MAX_MANTISSA_FAST_PATH, 2048); +} + +#[test] +fn test_f32_consts() { + assert_eq!(::INFINITY, f32::INFINITY); + assert_eq!(::NEG_INFINITY, -f32::INFINITY); + assert_eq!(::NAN.to_bits(), f32::NAN.to_bits()); + assert_eq!(::NEG_NAN.to_bits(), (-f32::NAN).to_bits()); + assert_eq!(::SIG_BITS, 23); + assert_eq!(::MIN_EXPONENT_ROUND_TO_EVEN, -17); + assert_eq!(::MAX_EXPONENT_ROUND_TO_EVEN, 10); + assert_eq!(::MIN_EXPONENT_FAST_PATH, -10); + assert_eq!(::MAX_EXPONENT_FAST_PATH, 10); + assert_eq!(::MAX_EXPONENT_DISGUISED_FAST_PATH, 17); + assert_eq!(::EXP_MIN, -126); + assert_eq!(::EXP_SAT, 0xff); + assert_eq!(::SMALLEST_POWER_OF_TEN, -65); + assert_eq!(::LARGEST_POWER_OF_TEN, 38); + assert_eq!(::MAX_MANTISSA_FAST_PATH, 16777216); +} + +#[test] +fn test_f64_consts() { + assert_eq!(::INFINITY, f64::INFINITY); + assert_eq!(::NEG_INFINITY, -f64::INFINITY); + assert_eq!(::NAN.to_bits(), f64::NAN.to_bits()); + assert_eq!(::NEG_NAN.to_bits(), (-f64::NAN).to_bits()); + assert_eq!(::SIG_BITS, 52); + assert_eq!(::MIN_EXPONENT_ROUND_TO_EVEN, -4); + assert_eq!(::MAX_EXPONENT_ROUND_TO_EVEN, 23); + assert_eq!(::MIN_EXPONENT_FAST_PATH, -22); + assert_eq!(::MAX_EXPONENT_FAST_PATH, 22); + assert_eq!(::MAX_EXPONENT_DISGUISED_FAST_PATH, 37); + assert_eq!(::EXP_MIN, -1022); + assert_eq!(::EXP_SAT, 0x7ff); + assert_eq!(::SMALLEST_POWER_OF_TEN, -342); + assert_eq!(::LARGEST_POWER_OF_TEN, 308); + assert_eq!(::MAX_MANTISSA_FAST_PATH, 9007199254740992); } diff --git a/library/coretests/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs index f71bbb7c7a318..6d49d85170e2d 100644 --- a/library/coretests/tests/num/dec2flt/lemire.rs +++ b/library/coretests/tests/num/dec2flt/lemire.rs @@ -1,53 +1,130 @@ +use core::num::dec2flt::float::RawFloat; use core::num::dec2flt::lemire::compute_float; +#[cfg(target_has_reliable_f16)] +fn compute_float16(q: i64, w: u64) -> (i32, u64) { + let fp = compute_float::(q, w); + (fp.p_biased, fp.m) +} + fn compute_float32(q: i64, w: u64) -> (i32, u64) { let fp = compute_float::(q, w); - (fp.e, fp.f) + (fp.p_biased, fp.m) } fn compute_float64(q: i64, w: u64) -> (i32, u64) { let fp = compute_float::(q, w); - (fp.e, fp.f) + (fp.p_biased, fp.m) +} + +// FIXME(f16_f128): enable on all targets once possible. +#[test] +#[cfg(target_has_reliable_f16)] +fn compute_float_f16_rounding() { + // The maximum integer that cna be converted to a `f16` without lost precision. + let val = 1 << 11; + let scale = 10_u64.pow(10); + + // These test near-halfway cases for half-precision floats. + assert_eq!(compute_float16(0, val), (26, 0)); + assert_eq!(compute_float16(0, val + 1), (26, 0)); + assert_eq!(compute_float16(0, val + 2), (26, 1)); + assert_eq!(compute_float16(0, val + 3), (26, 2)); + assert_eq!(compute_float16(0, val + 4), (26, 2)); + + // For the next power up, the two nearest representable numbers are twice as far apart. + let val2 = 1 << 12; + assert_eq!(compute_float16(0, val2), (27, 0)); + assert_eq!(compute_float16(0, val2 + 2), (27, 0)); + assert_eq!(compute_float16(0, val2 + 4), (27, 1)); + assert_eq!(compute_float16(0, val2 + 6), (27, 2)); + assert_eq!(compute_float16(0, val2 + 8), (27, 2)); + + // These are examples of the above tests, with digits from the exponent shifted + // to the mantissa. + assert_eq!(compute_float16(-10, val * scale), (26, 0)); + assert_eq!(compute_float16(-10, (val + 1) * scale), (26, 0)); + assert_eq!(compute_float16(-10, (val + 2) * scale), (26, 1)); + // Let's check the lines to see if anything is different in table... + assert_eq!(compute_float16(-10, (val + 3) * scale), (26, 2)); + assert_eq!(compute_float16(-10, (val + 4) * scale), (26, 2)); + + // Check the rounding point between infinity and the next representable number down + assert_eq!(compute_float16(4, 6), (f16::INFINITE_POWER - 1, 851)); + assert_eq!(compute_float16(4, 7), (f16::INFINITE_POWER, 0)); // infinity + assert_eq!(compute_float16(2, 655), (f16::INFINITE_POWER - 1, 1023)); } #[test] fn compute_float_f32_rounding() { + // the maximum integer that cna be converted to a `f32` without lost precision. + let val = 1 << 24; + let scale = 10_u64.pow(10); + // These test near-halfway cases for single-precision floats. - assert_eq!(compute_float32(0, 16777216), (151, 0)); - assert_eq!(compute_float32(0, 16777217), (151, 0)); - assert_eq!(compute_float32(0, 16777218), (151, 1)); - assert_eq!(compute_float32(0, 16777219), (151, 2)); - assert_eq!(compute_float32(0, 16777220), (151, 2)); - - // These are examples of the above tests, with - // digits from the exponent shifted to the mantissa. - assert_eq!(compute_float32(-10, 167772160000000000), (151, 0)); - assert_eq!(compute_float32(-10, 167772170000000000), (151, 0)); - assert_eq!(compute_float32(-10, 167772180000000000), (151, 1)); + assert_eq!(compute_float32(0, val), (151, 0)); + assert_eq!(compute_float32(0, val + 1), (151, 0)); + assert_eq!(compute_float32(0, val + 2), (151, 1)); + assert_eq!(compute_float32(0, val + 3), (151, 2)); + assert_eq!(compute_float32(0, val + 4), (151, 2)); + + // For the next power up, the two nearest representable numbers are twice as far apart. + let val2 = 1 << 25; + assert_eq!(compute_float32(0, val2), (152, 0)); + assert_eq!(compute_float32(0, val2 + 2), (152, 0)); + assert_eq!(compute_float32(0, val2 + 4), (152, 1)); + assert_eq!(compute_float32(0, val2 + 6), (152, 2)); + assert_eq!(compute_float32(0, val2 + 8), (152, 2)); + + // These are examples of the above tests, with digits from the exponent shifted + // to the mantissa. + assert_eq!(compute_float32(-10, val * scale), (151, 0)); + assert_eq!(compute_float32(-10, (val + 1) * scale), (151, 0)); + assert_eq!(compute_float32(-10, (val + 2) * scale), (151, 1)); // Let's check the lines to see if anything is different in table... - assert_eq!(compute_float32(-10, 167772190000000000), (151, 2)); - assert_eq!(compute_float32(-10, 167772200000000000), (151, 2)); + assert_eq!(compute_float32(-10, (val + 3) * scale), (151, 2)); + assert_eq!(compute_float32(-10, (val + 4) * scale), (151, 2)); + + // Check the rounding point between infinity and the next representable number down + assert_eq!(compute_float32(38, 3), (f32::INFINITE_POWER - 1, 6402534)); + assert_eq!(compute_float32(38, 4), (f32::INFINITE_POWER, 0)); // infinity + assert_eq!(compute_float32(20, 3402823470000000000), (f32::INFINITE_POWER - 1, 8388607)); } #[test] fn compute_float_f64_rounding() { + // The maximum integer that cna be converted to a `f64` without lost precision. + let val = 1 << 53; + let scale = 1000; + // These test near-halfway cases for double-precision floats. - assert_eq!(compute_float64(0, 9007199254740992), (1076, 0)); - assert_eq!(compute_float64(0, 9007199254740993), (1076, 0)); - assert_eq!(compute_float64(0, 9007199254740994), (1076, 1)); - assert_eq!(compute_float64(0, 9007199254740995), (1076, 2)); - assert_eq!(compute_float64(0, 9007199254740996), (1076, 2)); - assert_eq!(compute_float64(0, 18014398509481984), (1077, 0)); - assert_eq!(compute_float64(0, 18014398509481986), (1077, 0)); - assert_eq!(compute_float64(0, 18014398509481988), (1077, 1)); - assert_eq!(compute_float64(0, 18014398509481990), (1077, 2)); - assert_eq!(compute_float64(0, 18014398509481992), (1077, 2)); - - // These are examples of the above tests, with - // digits from the exponent shifted to the mantissa. - assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0)); - assert_eq!(compute_float64(-3, 9007199254740993000), (1076, 0)); - assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1)); - assert_eq!(compute_float64(-3, 9007199254740995000), (1076, 2)); - assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2)); + assert_eq!(compute_float64(0, val), (1076, 0)); + assert_eq!(compute_float64(0, val + 1), (1076, 0)); + assert_eq!(compute_float64(0, val + 2), (1076, 1)); + assert_eq!(compute_float64(0, val + 3), (1076, 2)); + assert_eq!(compute_float64(0, val + 4), (1076, 2)); + + // For the next power up, the two nearest representable numbers are twice as far apart. + let val2 = 1 << 54; + assert_eq!(compute_float64(0, val2), (1077, 0)); + assert_eq!(compute_float64(0, val2 + 2), (1077, 0)); + assert_eq!(compute_float64(0, val2 + 4), (1077, 1)); + assert_eq!(compute_float64(0, val2 + 6), (1077, 2)); + assert_eq!(compute_float64(0, val2 + 8), (1077, 2)); + + // These are examples of the above tests, with digits from the exponent shifted + // to the mantissa. + assert_eq!(compute_float64(-3, val * scale), (1076, 0)); + assert_eq!(compute_float64(-3, (val + 1) * scale), (1076, 0)); + assert_eq!(compute_float64(-3, (val + 2) * scale), (1076, 1)); + assert_eq!(compute_float64(-3, (val + 3) * scale), (1076, 2)); + assert_eq!(compute_float64(-3, (val + 4) * scale), (1076, 2)); + + // Check the rounding point between infinity and the next representable number down + assert_eq!(compute_float64(308, 1), (f64::INFINITE_POWER - 1, 506821272651936)); + assert_eq!(compute_float64(308, 2), (f64::INFINITE_POWER, 0)); // infinity + assert_eq!( + compute_float64(292, 17976931348623157), + (f64::INFINITE_POWER - 1, 4503599627370495) + ); } diff --git a/library/coretests/tests/num/dec2flt/mod.rs b/library/coretests/tests/num/dec2flt/mod.rs index 874e0ec7093c7..b8ca220847cfa 100644 --- a/library/coretests/tests/num/dec2flt/mod.rs +++ b/library/coretests/tests/num/dec2flt/mod.rs @@ -1,5 +1,7 @@ #![allow(overflowing_literals)] +mod decimal; +mod decimal_seq; mod float; mod lemire; mod parse; @@ -9,15 +11,23 @@ mod parse; // Requires a *polymorphic literal*, i.e., one that can serve as f64 as well as f32. macro_rules! test_literal { ($x: expr) => {{ + #[cfg(target_has_reliable_f16)] + let x16: f16 = $x; let x32: f32 = $x; let x64: f64 = $x; let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)]; + for input in inputs { - assert_eq!(input.parse(), Ok(x64)); - assert_eq!(input.parse(), Ok(x32)); + assert_eq!(input.parse(), Ok(x64), "failed f64 {input}"); + assert_eq!(input.parse(), Ok(x32), "failed f32 {input}"); + #[cfg(target_has_reliable_f16)] + assert_eq!(input.parse(), Ok(x16), "failed f16 {input}"); + let neg_input = format!("-{input}"); - assert_eq!(neg_input.parse(), Ok(-x64)); - assert_eq!(neg_input.parse(), Ok(-x32)); + assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}"); + assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}"); + #[cfg(target_has_reliable_f16)] + assert_eq!(neg_input.parse(), Ok(-x16), "failed f16 {neg_input}"); } }}; } @@ -82,48 +92,87 @@ fn fast_path_correct() { test_literal!(1.448997445238699); } +// FIXME(f16_f128): remove gates once tests work on all targets + #[test] fn lonely_dot() { + #[cfg(target_has_reliable_f16)] + assert!(".".parse::().is_err()); assert!(".".parse::().is_err()); assert!(".".parse::().is_err()); } #[test] fn exponentiated_dot() { + #[cfg(target_has_reliable_f16)] + assert!(".e0".parse::().is_err()); assert!(".e0".parse::().is_err()); assert!(".e0".parse::().is_err()); } #[test] fn lonely_sign() { - assert!("+".parse::().is_err()); - assert!("-".parse::().is_err()); + #[cfg(target_has_reliable_f16)] + assert!("+".parse::().is_err()); + assert!("-".parse::().is_err()); + assert!("+".parse::().is_err()); } #[test] fn whitespace() { + #[cfg(target_has_reliable_f16)] + assert!("1.0 ".parse::().is_err()); assert!(" 1.0".parse::().is_err()); assert!("1.0 ".parse::().is_err()); } #[test] fn nan() { + #[cfg(target_has_reliable_f16)] + { + assert!("NaN".parse::().unwrap().is_nan()); + assert!("-NaN".parse::().unwrap().is_nan()); + } + assert!("NaN".parse::().unwrap().is_nan()); + assert!("-NaN".parse::().unwrap().is_nan()); + assert!("NaN".parse::().unwrap().is_nan()); + assert!("-NaN".parse::().unwrap().is_nan()); } #[test] fn inf() { - assert_eq!("inf".parse(), Ok(f64::INFINITY)); - assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY)); + #[cfg(target_has_reliable_f16)] + { + assert_eq!("inf".parse(), Ok(f16::INFINITY)); + assert_eq!("-inf".parse(), Ok(f16::NEG_INFINITY)); + } + assert_eq!("inf".parse(), Ok(f32::INFINITY)); assert_eq!("-inf".parse(), Ok(f32::NEG_INFINITY)); + + assert_eq!("inf".parse(), Ok(f64::INFINITY)); + assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY)); } #[test] fn massive_exponent() { + #[cfg(target_has_reliable_f16)] + { + let max = i16::MAX; + assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY)); + assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f16)); + assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY)); + } + + let max = i32::MAX; + assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY)); + assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f32)); + assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY)); + let max = i64::MAX; assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY)); - assert_eq!(format!("1e-{max}000").parse(), Ok(0.0)); + assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f64)); assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY)); } diff --git a/library/coretests/tests/num/dec2flt/parse.rs b/library/coretests/tests/num/dec2flt/parse.rs index 4a5d24ba7d5fa..dccb6b5528d4c 100644 --- a/library/coretests/tests/num/dec2flt/parse.rs +++ b/library/coretests/tests/num/dec2flt/parse.rs @@ -1,15 +1,18 @@ -use core::num::dec2flt::number::Number; +use core::num::dec2flt::decimal::Decimal; use core::num::dec2flt::parse::parse_number; use core::num::dec2flt::{dec2flt, pfe_invalid}; -fn new_number(e: i64, m: u64) -> Number { - Number { exponent: e, mantissa: m, negative: false, many_digits: false } +fn new_dec(e: i64, m: u64) -> Decimal { + Decimal { exponent: e, mantissa: m, negative: false, many_digits: false } } #[test] fn missing_pieces() { let permutations = &[".e", "1e", "e4", "e", ".12e", "321.e", "32.12e+", "12.32e-"]; for &s in permutations { + #[cfg(target_has_reliable_f16)] + assert_eq!(dec2flt::(s), Err(pfe_invalid())); + assert_eq!(dec2flt::(s), Err(pfe_invalid())); assert_eq!(dec2flt::(s), Err(pfe_invalid())); } } @@ -17,37 +20,53 @@ fn missing_pieces() { #[test] fn invalid_chars() { let invalid = "r,?(&input) == error, "did not reject invalid {:?}", input); + + #[cfg(target_has_reliable_f16)] + assert_eq!( + dec2flt::(&input), + Err(pfe_invalid()), + "f16 did not reject invalid {input:?}", + ); + assert_eq!( + dec2flt::(&input), + Err(pfe_invalid()), + "f32 did not reject invalid {input:?}", + ); + assert_eq!( + dec2flt::(&input), + Err(pfe_invalid()), + "f64 did not reject invalid {input:?}", + ); } } } } -fn parse_positive(s: &[u8]) -> Option { +fn parse_positive(s: &[u8]) -> Option { parse_number(s) } #[test] fn valid() { - assert_eq!(parse_positive(b"123.456e789"), Some(new_number(786, 123456))); - assert_eq!(parse_positive(b"123.456e+789"), Some(new_number(786, 123456))); - assert_eq!(parse_positive(b"123.456e-789"), Some(new_number(-792, 123456))); - assert_eq!(parse_positive(b".050"), Some(new_number(-3, 50))); - assert_eq!(parse_positive(b"999"), Some(new_number(0, 999))); - assert_eq!(parse_positive(b"1.e300"), Some(new_number(300, 1))); - assert_eq!(parse_positive(b".1e300"), Some(new_number(299, 1))); - assert_eq!(parse_positive(b"101e-33"), Some(new_number(-33, 101))); + assert_eq!(parse_positive(b"123.456e789"), Some(new_dec(786, 123456))); + assert_eq!(parse_positive(b"123.456e+789"), Some(new_dec(786, 123456))); + assert_eq!(parse_positive(b"123.456e-789"), Some(new_dec(-792, 123456))); + assert_eq!(parse_positive(b".050"), Some(new_dec(-3, 50))); + assert_eq!(parse_positive(b"999"), Some(new_dec(0, 999))); + assert_eq!(parse_positive(b"1.e300"), Some(new_dec(300, 1))); + assert_eq!(parse_positive(b".1e300"), Some(new_dec(299, 1))); + assert_eq!(parse_positive(b"101e-33"), Some(new_dec(-33, 101))); let zeros = "0".repeat(25); let s = format!("1.5e{zeros}"); - assert_eq!(parse_positive(s.as_bytes()), Some(new_number(-1, 15))); + assert_eq!(parse_positive(s.as_bytes()), Some(new_dec(-1, 15))); } macro_rules! assert_float_result_bits_eq { @@ -57,6 +76,21 @@ macro_rules! assert_float_result_bits_eq { }}; } +#[test] +fn regression() { + // These showed up in fuzz tests when the minimum exponent was incorrect. + assert_float_result_bits_eq!( + 0x0, + f64, + "3313756768023998018398807867233977556112078681253148176737587500333136120852692315608454494981109839693784033457129423181787087843504060087613228932431e-475" + ); + assert_float_result_bits_eq!( + 0x0, + f64, + "5298127456259331337220.92759278003098321644501973966679724599271041396379712108366679824365568578569680024083293475291869842408884554511641179110778276695274832779269225510492006696321279587846006535230380114430977056662212751544508159333199129106162019382177820713609e-346" + ); +} + #[test] fn issue31109() { // Regression test for #31109. diff --git a/library/coretests/tests/num/flt2dec/mod.rs b/library/coretests/tests/num/flt2dec/mod.rs index 6041923117c2a..ce36db33d05f3 100644 --- a/library/coretests/tests/num/flt2dec/mod.rs +++ b/library/coretests/tests/num/flt2dec/mod.rs @@ -16,7 +16,7 @@ mod random; pub fn decode_finite(v: T) -> Decoded { match decode(v).1 { FullDecoded::Finite(decoded) => decoded, - full_decoded => panic!("expected finite, got {full_decoded:?} instead"), + full_decoded => panic!("expected finite, got {full_decoded:?} instead for {v:?}"), } } @@ -75,6 +75,11 @@ macro_rules! try_fixed { }) } +#[cfg(target_has_reliable_f16)] +fn ldexp_f16(a: f16, b: i32) -> f16 { + ldexp_f64(a as f64, b) as f16 +} + fn ldexp_f32(a: f32, b: i32) -> f32 { ldexp_f64(a as f64, b) as f32 } @@ -176,6 +181,13 @@ trait TestableFloat: DecodableFloat + fmt::Display { fn ldexpi(f: i64, exp: isize) -> Self; } +#[cfg(target_has_reliable_f16)] +impl TestableFloat for f16 { + fn ldexpi(f: i64, exp: isize) -> Self { + f as Self * (exp as Self).exp2() + } +} + impl TestableFloat for f32 { fn ldexpi(f: i64, exp: isize) -> Self { f as Self * (exp as Self).exp2() @@ -225,6 +237,76 @@ macro_rules! check_exact_one { // // [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion // ftp://ftp.ee.lbl.gov/testbase-report.ps.Z +// or https://www.icir.org/vern/papers/testbase-report.pdf + +#[cfg(target_has_reliable_f16)] +pub fn f16_shortest_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + // 0.0999145507813 + // 0.0999755859375 + // 0.100036621094 + check_shortest!(f(0.1f16) => b"1", 0); + + // 0.3330078125 + // 0.333251953125 (1/3 in the default rounding) + // 0.33349609375 + check_shortest!(f(1.0f16/3.0) => b"3333", 0); + + // 10^1 * 0.3138671875 + // 10^1 * 0.3140625 + // 10^1 * 0.3142578125 + check_shortest!(f(3.14f16) => b"314", 1); + + // 10^18 * 0.31415916243714048 + // 10^18 * 0.314159196796878848 + // 10^18 * 0.314159231156617216 + check_shortest!(f(3.1415e4f16) => b"3141", 5); + + // regression test for decoders + // 10^2 * 0.31984375 + // 10^2 * 0.32 + // 10^2 * 0.3203125 + check_shortest!(f(ldexp_f16(1.0, 5)) => b"32", 2); + + // 10^5 * 0.65472 + // 10^5 * 0.65504 + // 10^5 * 0.65536 + check_shortest!(f(f16::MAX) => b"655", 5); + + // 10^-4 * 0.60975551605224609375 + // 10^-4 * 0.6103515625 + // 10^-4 * 0.61094760894775390625 + check_shortest!(f(f16::MIN_POSITIVE) => b"6104", -4); + + // 10^-9 * 0 + // 10^-9 * 0.59604644775390625 + // 10^-8 * 0.11920928955078125 + let minf16 = ldexp_f16(1.0, -24); + check_shortest!(f(minf16) => b"6", -7); +} + +#[cfg(target_has_reliable_f16)] +pub fn f16_exact_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + let minf16 = ldexp_f16(1.0, -24); + + check_exact!(f(0.1f16) => b"999755859375 ", -1); + check_exact!(f(0.5f16) => b"5 ", 0); + check_exact!(f(1.0f16/3.0) => b"333251953125 ", 0); + check_exact!(f(3.141f16) => b"3140625 ", 1); + check_exact!(f(3.141e4f16) => b"31408 ", 5); + check_exact!(f(f16::MAX) => b"65504 ", 5); + check_exact!(f(f16::MIN_POSITIVE) => b"6103515625 ", -4); + check_exact!(f(minf16) => b"59604644775390625", -7); + + // FIXME(f16_f128): these should gain the check_exact_one tests like `f32` and `f64` have, + // but these values are not easy to generate. The algorithm from the Paxon paper [1] needs + // to be adapted to binary16. +} pub fn f32_shortest_sanity_test(mut f: F) where @@ -553,23 +635,45 @@ where 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", "")); + #[cfg(target_has_reliable_f16)] + { + // f16 + assert_eq!(to_string(f, f16::MAX, Minus, 0), "65500"); + assert_eq!(to_string(f, f16::MAX, Minus, 1), "65500.0"); + assert_eq!(to_string(f, f16::MAX, Minus, 8), "65500.00000000"); + + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, 0), "0.00000006"); + assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006"); + assert_eq!(to_string(f, minf16, Minus, 9), "0.000000060"); + } - 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", "")); + { + // f32 + 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", "")); + } - 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", "")); + { + // f64 + 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 @@ -577,7 +681,7 @@ where } // very large output - assert_eq!(to_string(f, 1.1, Minus, 80000), format!("1.1{:0>79999}", "")); + assert_eq!(to_string(f, 1.1, Minus, 50000), format!("1.1{:0>49999}", "")); } pub fn to_shortest_exp_str_test(mut f_: F) @@ -655,27 +759,45 @@ where 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"); + #[cfg(target_has_reliable_f16)] + { + // f16 + assert_eq!(to_string(f, f16::MAX, Minus, (-2, 2), false), "6.55e4"); + assert_eq!(to_string(f, f16::MAX, Minus, (-4, 4), false), "6.55e4"); + assert_eq!(to_string(f, f16::MAX, Minus, (-5, 5), false), "65500"); + + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, (-2, 2), false), "6e-8"); + assert_eq!(to_string(f, minf16, Minus, (-7, 7), false), "6e-8"); + assert_eq!(to_string(f, minf16, Minus, (-8, 8), false), "0.00000006"); + } - 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"); + { + // f32 + 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", "")); + } + { + // f64 + 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"); } @@ -791,6 +913,26 @@ where "9.999999999999999547481118258862586856139387236908078193664550781250000e-7" ); + #[cfg(target_has_reliable_f16)] + { + assert_eq!(to_string(f, f16::MAX, Minus, 1, false), "7e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 2, false), "6.6e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 4, false), "6.550e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 5, false), "6.5504e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 6, false), "6.55040e4"); + assert_eq!(to_string(f, f16::MAX, Minus, 16, false), "6.550400000000000e4"); + + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, 1, false), "6e-8"); + assert_eq!(to_string(f, minf16, Minus, 2, false), "6.0e-8"); + assert_eq!(to_string(f, minf16, Minus, 4, false), "5.960e-8"); + assert_eq!(to_string(f, minf16, Minus, 8, false), "5.9604645e-8"); + assert_eq!(to_string(f, minf16, Minus, 16, false), "5.960464477539062e-8"); + assert_eq!(to_string(f, minf16, Minus, 17, false), "5.9604644775390625e-8"); + assert_eq!(to_string(f, minf16, Minus, 18, false), "5.96046447753906250e-8"); + assert_eq!(to_string(f, minf16, Minus, 24, false), "5.96046447753906250000000e-8"); + } + 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"); @@ -914,22 +1056,22 @@ where ); // 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, 0.0, Minus, 50000, false), format!("0.{:0>49999}e0", "")); + assert_eq!(to_string(f, 1.0e1, Minus, 50000, false), format!("1.{:0>49999}e1", "")); + assert_eq!(to_string(f, 1.0e0, Minus, 50000, false), format!("1.{:0>49999}e0", "")); assert_eq!( - to_string(f, 1.0e-1, Minus, 80000, false), + to_string(f, 1.0e-1, Minus, 50000, false), format!( - "1.000000000000000055511151231257827021181583404541015625{:0>79945}\ + "1.000000000000000055511151231257827021181583404541015625{:0>49945}\ e-1", "" ) ); assert_eq!( - to_string(f, 1.0e-20, Minus, 80000, false), + to_string(f, 1.0e-20, Minus, 50000, false), format!( "9.999999999999999451532714542095716517295037027873924471077157760\ - 66783064379706047475337982177734375{:0>79901}e-21", + 66783064379706047475337982177734375{:0>49901}e-21", "" ) ); @@ -1069,6 +1211,13 @@ where "0.000000999999999999999954748111825886258685613938723690807819366455078125000" ); + #[cfg(target_has_reliable_f16)] + { + assert_eq!(to_string(f, f16::MAX, Minus, 0), "65504"); + assert_eq!(to_string(f, f16::MAX, Minus, 1), "65504.0"); + assert_eq!(to_string(f, f16::MAX, Minus, 2), "65504.00"); + } + 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"); @@ -1078,6 +1227,21 @@ where return; } + #[cfg(target_has_reliable_f16)] + { + let minf16 = ldexp_f16(1.0, -24); + assert_eq!(to_string(f, minf16, Minus, 0), "0"); + assert_eq!(to_string(f, minf16, Minus, 1), "0.0"); + assert_eq!(to_string(f, minf16, Minus, 2), "0.00"); + assert_eq!(to_string(f, minf16, Minus, 4), "0.0000"); + assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006"); + assert_eq!(to_string(f, minf16, Minus, 10), "0.0000000596"); + assert_eq!(to_string(f, minf16, Minus, 15), "0.000000059604645"); + assert_eq!(to_string(f, minf16, Minus, 20), "0.00000005960464477539"); + assert_eq!(to_string(f, minf16, Minus, 24), "0.000000059604644775390625"); + assert_eq!(to_string(f, minf16, Minus, 32), "0.00000005960464477539062500000000"); + } + 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"); @@ -1150,18 +1314,18 @@ where ); // 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, 0.0, Minus, 50000), format!("0.{:0>50000}", "")); + assert_eq!(to_string(f, 1.0e1, Minus, 50000), format!("10.{:0>50000}", "")); + assert_eq!(to_string(f, 1.0e0, Minus, 50000), format!("1.{:0>50000}", "")); assert_eq!( - to_string(f, 1.0e-1, Minus, 80000), - format!("0.1000000000000000055511151231257827021181583404541015625{:0>79945}", "") + to_string(f, 1.0e-1, Minus, 50000), + format!("0.1000000000000000055511151231257827021181583404541015625{:0>49945}", "") ); assert_eq!( - to_string(f, 1.0e-20, Minus, 80000), + to_string(f, 1.0e-20, Minus, 50000), format!( "0.0000000000000000000099999999999999994515327145420957165172950370\ - 2787392447107715776066783064379706047475337982177734375{:0>79881}", + 2787392447107715776066783064379706047475337982177734375{:0>49881}", "" ) ); diff --git a/library/coretests/tests/num/flt2dec/random.rs b/library/coretests/tests/num/flt2dec/random.rs index 586b49df7d9b2..7386139aaced5 100644 --- a/library/coretests/tests/num/flt2dec/random.rs +++ b/library/coretests/tests/num/flt2dec/random.rs @@ -79,6 +79,20 @@ where (npassed, nignored) } +#[cfg(target_has_reliable_f16)] +pub fn f16_random_equivalence_test(f: F, g: G, k: usize, n: usize) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + let mut rng = crate::test_rng(); + let f16_range = Uniform::new(0x0001u16, 0x7c00).unwrap(); + iterate("f16_random_equivalence_test", k, n, f, g, |_| { + let x = f16::from_bits(f16_range.sample(&mut rng)); + decode_finite(x) + }); +} + pub fn f32_random_equivalence_test(f: F, g: G, k: usize, n: usize) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, @@ -105,6 +119,24 @@ where }); } +#[cfg(target_has_reliable_f16)] +pub fn f16_exhaustive_equivalence_test(f: F, g: G, k: usize) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + // Unlike the other float types, `f16` is small enough that these exhaustive tests + // can run in less than a second so we don't need to ignore it. + + // iterate from 0x0001 to 0x7bff, i.e., all finite ranges + let (npassed, nignored) = + iterate("f16_exhaustive_equivalence_test", k, 0x7bff, f, g, |i: usize| { + let x = f16::from_bits(i as u16 + 1); + decode_finite(x) + }); + assert_eq!((npassed, nignored), (29735, 2008)); +} + pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, @@ -133,6 +165,17 @@ fn shortest_random_equivalence_test() { f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); + #[cfg(target_has_reliable_f16)] + f16_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n); +} + +#[test] +#[cfg_attr(miri, ignore)] // Miri is to slow +#[cfg(target_has_reliable_f16)] +fn shortest_f16_exhaustive_equivalence_test() { + // see the f32 version + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f16_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); } #[test] @@ -158,6 +201,23 @@ fn shortest_f64_hard_random_equivalence_test() { f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 100_000_000); } +#[test] +#[cfg(target_has_reliable_f16)] +fn exact_f16_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 }; + + for k in 1..21 { + f16_random_equivalence_test( + |d, buf| format_exact_opt(d, buf, i16::MIN), + |d, buf| fallback(d, buf, i16::MIN), + k, + n, + ); + } +} + #[test] fn exact_f32_random_equivalence_test() { use core::num::flt2dec::strategy::dragon::format_exact as fallback; diff --git a/library/coretests/tests/num/flt2dec/strategy/dragon.rs b/library/coretests/tests/num/flt2dec/strategy/dragon.rs index be25fee3f6c71..43bb6024f9cee 100644 --- a/library/coretests/tests/num/flt2dec/strategy/dragon.rs +++ b/library/coretests/tests/num/flt2dec/strategy/dragon.rs @@ -18,6 +18,8 @@ fn test_mul_pow10() { fn shortest_sanity_test() { f64_shortest_sanity_test(format_shortest); f32_shortest_sanity_test(format_shortest); + #[cfg(target_has_reliable_f16)] + f16_shortest_sanity_test(format_shortest); more_shortest_sanity_test(format_shortest); } @@ -41,6 +43,9 @@ fn exact_sanity_test() { f64_exact_sanity_test(format_exact); } f32_exact_sanity_test(format_exact); + + #[cfg(target_has_reliable_f16)] + f16_exact_sanity_test(format_exact); } #[test] diff --git a/library/coretests/tests/num/flt2dec/strategy/grisu.rs b/library/coretests/tests/num/flt2dec/strategy/grisu.rs index 9b2f0453de73e..117191e0c8fdb 100644 --- a/library/coretests/tests/num/flt2dec/strategy/grisu.rs +++ b/library/coretests/tests/num/flt2dec/strategy/grisu.rs @@ -38,6 +38,8 @@ fn test_max_pow10_no_more_than() { fn shortest_sanity_test() { f64_shortest_sanity_test(format_shortest); f32_shortest_sanity_test(format_shortest); + #[cfg(target_has_reliable_f16)] + f16_shortest_sanity_test(format_shortest); more_shortest_sanity_test(format_shortest); } @@ -50,6 +52,8 @@ fn exact_sanity_test() { f64_exact_sanity_test(format_exact); } f32_exact_sanity_test(format_exact); + #[cfg(target_has_reliable_f16)] + f16_exact_sanity_test(format_exact); } #[test] diff --git a/library/coretests/tests/num/int_macros.rs b/library/coretests/tests/num/int_macros.rs index bbf19d2b444f9..0d9fb9e797e1f 100644 --- a/library/coretests/tests/num/int_macros.rs +++ b/library/coretests/tests/num/int_macros.rs @@ -1,5 +1,6 @@ macro_rules! int_module { ($T:ident, $U:ident) => { + use core::num::ParseIntError; use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; use core::$T::*; @@ -32,20 +33,20 @@ macro_rules! int_module { test_runtime_and_compiletime! { fn test_rem_euclid() { - assert_eq_const_safe!((-1 as $T).rem_euclid(MIN), MAX); + assert_eq_const_safe!($T: (-1 as $T).rem_euclid(MIN), MAX); } fn test_abs() { - assert_eq_const_safe!((1 as $T).abs(), 1 as $T); - assert_eq_const_safe!((0 as $T).abs(), 0 as $T); - assert_eq_const_safe!((-1 as $T).abs(), 1 as $T); + assert_eq_const_safe!($T: (1 as $T).abs(), 1 as $T); + assert_eq_const_safe!($T: (0 as $T).abs(), 0 as $T); + assert_eq_const_safe!($T: (-1 as $T).abs(), 1 as $T); } fn test_signum() { - assert_eq_const_safe!((1 as $T).signum(), 1 as $T); - assert_eq_const_safe!((0 as $T).signum(), 0 as $T); - assert_eq_const_safe!((-0 as $T).signum(), 0 as $T); - assert_eq_const_safe!((-1 as $T).signum(), -1 as $T); + assert_eq_const_safe!($T: (1 as $T).signum(), 1 as $T); + assert_eq_const_safe!($T: (0 as $T).signum(), 0 as $T); + assert_eq_const_safe!($T: (-0 as $T).signum(), 0 as $T); + assert_eq_const_safe!($T: (-1 as $T).signum(), -1 as $T); } fn test_is_positive() { @@ -72,123 +73,123 @@ macro_rules! int_module { test_runtime_and_compiletime! { fn test_count_ones() { - assert_eq_const_safe!(A.count_ones(), 3); - assert_eq_const_safe!(B.count_ones(), 2); - assert_eq_const_safe!(C.count_ones(), 5); + assert_eq_const_safe!(u32: A.count_ones(), 3); + assert_eq_const_safe!(u32: B.count_ones(), 2); + assert_eq_const_safe!(u32: C.count_ones(), 5); } fn test_count_zeros() { - assert_eq_const_safe!(A.count_zeros(), $T::BITS - 3); - assert_eq_const_safe!(B.count_zeros(), $T::BITS - 2); - assert_eq_const_safe!(C.count_zeros(), $T::BITS - 5); + assert_eq_const_safe!(u32: A.count_zeros(), $T::BITS - 3); + assert_eq_const_safe!(u32: B.count_zeros(), $T::BITS - 2); + assert_eq_const_safe!(u32: C.count_zeros(), $T::BITS - 5); } fn test_leading_trailing_ones() { const A: $T = 0b0101_1111; - assert_eq_const_safe!(A.trailing_ones(), 5); - assert_eq_const_safe!((!A).leading_ones(), $T::BITS - 7); + assert_eq_const_safe!(u32: A.trailing_ones(), 5); + assert_eq_const_safe!(u32: (!A).leading_ones(), $T::BITS - 7); - assert_eq_const_safe!(A.reverse_bits().leading_ones(), 5); + assert_eq_const_safe!(u32: A.reverse_bits().leading_ones(), 5); - assert_eq_const_safe!(_1.leading_ones(), $T::BITS); - assert_eq_const_safe!(_1.trailing_ones(), $T::BITS); + assert_eq_const_safe!(u32: _1.leading_ones(), $T::BITS); + assert_eq_const_safe!(u32: _1.trailing_ones(), $T::BITS); - assert_eq_const_safe!((_1 << 1).trailing_ones(), 0); - assert_eq_const_safe!(MAX.leading_ones(), 0); + assert_eq_const_safe!(u32: (_1 << 1).trailing_ones(), 0); + assert_eq_const_safe!(u32: MAX.leading_ones(), 0); - assert_eq_const_safe!((_1 << 1).leading_ones(), $T::BITS - 1); - assert_eq_const_safe!(MAX.trailing_ones(), $T::BITS - 1); + assert_eq_const_safe!(u32: (_1 << 1).leading_ones(), $T::BITS - 1); + assert_eq_const_safe!(u32: MAX.trailing_ones(), $T::BITS - 1); - assert_eq_const_safe!(_0.leading_ones(), 0); - assert_eq_const_safe!(_0.trailing_ones(), 0); + assert_eq_const_safe!(u32: _0.leading_ones(), 0); + assert_eq_const_safe!(u32: _0.trailing_ones(), 0); const X: $T = 0b0010_1100; - assert_eq_const_safe!(X.leading_ones(), 0); - assert_eq_const_safe!(X.trailing_ones(), 0); + assert_eq_const_safe!(u32: X.leading_ones(), 0); + assert_eq_const_safe!(u32: X.trailing_ones(), 0); } fn test_rotate() { - assert_eq_const_safe!(A.rotate_left(6).rotate_right(2).rotate_right(4), A); - assert_eq_const_safe!(B.rotate_left(3).rotate_left(2).rotate_right(5), B); - assert_eq_const_safe!(C.rotate_left(6).rotate_right(2).rotate_right(4), C); + assert_eq_const_safe!($T: A.rotate_left(6).rotate_right(2).rotate_right(4), A); + assert_eq_const_safe!($T: B.rotate_left(3).rotate_left(2).rotate_right(5), B); + assert_eq_const_safe!($T: C.rotate_left(6).rotate_right(2).rotate_right(4), C); // Rotating these should make no difference // // We test using 124 bits because to ensure that overlong bit shifts do // not cause undefined behavior. See #10183. - assert_eq_const_safe!(_0.rotate_left(124), _0); - assert_eq_const_safe!(_1.rotate_left(124), _1); - assert_eq_const_safe!(_0.rotate_right(124), _0); - assert_eq_const_safe!(_1.rotate_right(124), _1); + assert_eq_const_safe!($T: _0.rotate_left(124), _0); + assert_eq_const_safe!($T: _1.rotate_left(124), _1); + assert_eq_const_safe!($T: _0.rotate_right(124), _0); + assert_eq_const_safe!($T: _1.rotate_right(124), _1); // Rotating by 0 should have no effect - assert_eq_const_safe!(A.rotate_left(0), A); - assert_eq_const_safe!(B.rotate_left(0), B); - assert_eq_const_safe!(C.rotate_left(0), C); + assert_eq_const_safe!($T: A.rotate_left(0), A); + assert_eq_const_safe!($T: B.rotate_left(0), B); + assert_eq_const_safe!($T: C.rotate_left(0), C); // Rotating by a multiple of word size should also have no effect - assert_eq_const_safe!(A.rotate_left(128), A); - assert_eq_const_safe!(B.rotate_left(128), B); - assert_eq_const_safe!(C.rotate_left(128), C); + assert_eq_const_safe!($T: A.rotate_left(128), A); + assert_eq_const_safe!($T: B.rotate_left(128), B); + assert_eq_const_safe!($T: C.rotate_left(128), C); } fn test_swap_bytes() { - assert_eq_const_safe!(A.swap_bytes().swap_bytes(), A); - assert_eq_const_safe!(B.swap_bytes().swap_bytes(), B); - assert_eq_const_safe!(C.swap_bytes().swap_bytes(), C); + assert_eq_const_safe!($T: A.swap_bytes().swap_bytes(), A); + assert_eq_const_safe!($T: B.swap_bytes().swap_bytes(), B); + assert_eq_const_safe!($T: C.swap_bytes().swap_bytes(), C); // Swapping these should make no difference - assert_eq_const_safe!(_0.swap_bytes(), _0); - assert_eq_const_safe!(_1.swap_bytes(), _1); + assert_eq_const_safe!($T: _0.swap_bytes(), _0); + assert_eq_const_safe!($T: _1.swap_bytes(), _1); } fn test_le() { - assert_eq_const_safe!($T::from_le(A.to_le()), A); - assert_eq_const_safe!($T::from_le(B.to_le()), B); - assert_eq_const_safe!($T::from_le(C.to_le()), C); - assert_eq_const_safe!($T::from_le(_0), _0); - assert_eq_const_safe!($T::from_le(_1), _1); - assert_eq_const_safe!(_0.to_le(), _0); - assert_eq_const_safe!(_1.to_le(), _1); + assert_eq_const_safe!($T: $T::from_le(A.to_le()), A); + assert_eq_const_safe!($T: $T::from_le(B.to_le()), B); + assert_eq_const_safe!($T: $T::from_le(C.to_le()), C); + assert_eq_const_safe!($T: $T::from_le(_0), _0); + assert_eq_const_safe!($T: $T::from_le(_1), _1); + assert_eq_const_safe!($T: _0.to_le(), _0); + assert_eq_const_safe!($T: _1.to_le(), _1); } fn test_be() { - assert_eq_const_safe!($T::from_be(A.to_be()), A); - assert_eq_const_safe!($T::from_be(B.to_be()), B); - assert_eq_const_safe!($T::from_be(C.to_be()), C); - assert_eq_const_safe!($T::from_be(_0), _0); - assert_eq_const_safe!($T::from_be(_1), _1); - assert_eq_const_safe!(_0.to_be(), _0); - assert_eq_const_safe!(_1.to_be(), _1); + assert_eq_const_safe!($T: $T::from_be(A.to_be()), A); + assert_eq_const_safe!($T: $T::from_be(B.to_be()), B); + assert_eq_const_safe!($T: $T::from_be(C.to_be()), C); + assert_eq_const_safe!($T: $T::from_be(_0), _0); + assert_eq_const_safe!($T: $T::from_be(_1), _1); + assert_eq_const_safe!($T: _0.to_be(), _0); + assert_eq_const_safe!($T: _1.to_be(), _1); } fn test_signed_checked_div() { - assert_eq_const_safe!((10 as $T).checked_div(2), Some(5)); - assert_eq_const_safe!((5 as $T).checked_div(0), None); - assert_eq_const_safe!(isize::MIN.checked_div(-1), None); + assert_eq_const_safe!(Option<$T>: (10 as $T).checked_div(2), Some(5)); + assert_eq_const_safe!(Option<$T>: (5 as $T).checked_div(0), None); + assert_eq_const_safe!(Option<$T>: $T::MIN.checked_div(-1), None); } fn test_saturating_abs() { - assert_eq_const_safe!((0 as $T).saturating_abs(), 0); - assert_eq_const_safe!((123 as $T).saturating_abs(), 123); - assert_eq_const_safe!((-123 as $T).saturating_abs(), 123); - assert_eq_const_safe!((MAX - 2).saturating_abs(), MAX - 2); - assert_eq_const_safe!((MAX - 1).saturating_abs(), MAX - 1); - assert_eq_const_safe!(MAX.saturating_abs(), MAX); - assert_eq_const_safe!((MIN + 2).saturating_abs(), MAX - 1); - assert_eq_const_safe!((MIN + 1).saturating_abs(), MAX); - assert_eq_const_safe!(MIN.saturating_abs(), MAX); + assert_eq_const_safe!($T: (0 as $T).saturating_abs(), 0); + assert_eq_const_safe!($T: (123 as $T).saturating_abs(), 123); + assert_eq_const_safe!($T: (-123 as $T).saturating_abs(), 123); + assert_eq_const_safe!($T: (MAX - 2).saturating_abs(), MAX - 2); + assert_eq_const_safe!($T: (MAX - 1).saturating_abs(), MAX - 1); + assert_eq_const_safe!($T: MAX.saturating_abs(), MAX); + assert_eq_const_safe!($T: (MIN + 2).saturating_abs(), MAX - 1); + assert_eq_const_safe!($T: (MIN + 1).saturating_abs(), MAX); + assert_eq_const_safe!($T: MIN.saturating_abs(), MAX); } fn test_saturating_neg() { - assert_eq_const_safe!((0 as $T).saturating_neg(), 0); - assert_eq_const_safe!((123 as $T).saturating_neg(), -123); - assert_eq_const_safe!((-123 as $T).saturating_neg(), 123); - assert_eq_const_safe!((MAX - 2).saturating_neg(), MIN + 3); - assert_eq_const_safe!((MAX - 1).saturating_neg(), MIN + 2); - assert_eq_const_safe!(MAX.saturating_neg(), MIN + 1); - assert_eq_const_safe!((MIN + 2).saturating_neg(), MAX - 1); - assert_eq_const_safe!((MIN + 1).saturating_neg(), MAX); - assert_eq_const_safe!(MIN.saturating_neg(), MAX); + assert_eq_const_safe!($T: (0 as $T).saturating_neg(), 0); + assert_eq_const_safe!($T: (123 as $T).saturating_neg(), -123); + assert_eq_const_safe!($T: (-123 as $T).saturating_neg(), 123); + assert_eq_const_safe!($T: (MAX - 2).saturating_neg(), MIN + 3); + assert_eq_const_safe!($T: (MAX - 1).saturating_neg(), MIN + 2); + assert_eq_const_safe!($T: MAX.saturating_neg(), MIN + 1); + assert_eq_const_safe!($T: (MIN + 2).saturating_neg(), MAX - 1); + assert_eq_const_safe!($T: (MIN + 1).saturating_neg(), MAX); + assert_eq_const_safe!($T: MIN.saturating_neg(), MAX); } } @@ -250,23 +251,23 @@ macro_rules! int_module { test_runtime_and_compiletime! { fn test_from_str_radix() { - assert_eq_const_safe!($T::from_str_radix("123", 10), Ok(123 as $T)); - assert_eq_const_safe!($T::from_str_radix("1001", 2), Ok(9 as $T)); - assert_eq_const_safe!($T::from_str_radix("123", 8), Ok(83 as $T)); - assert_eq_const_safe!(i32::from_str_radix("123", 16), Ok(291 as i32)); - assert_eq_const_safe!(i32::from_str_radix("ffff", 16), Ok(65535 as i32)); - assert_eq_const_safe!(i32::from_str_radix("FFFF", 16), Ok(65535 as i32)); - assert_eq_const_safe!($T::from_str_radix("z", 36), Ok(35 as $T)); - assert_eq_const_safe!($T::from_str_radix("Z", 36), Ok(35 as $T)); - - assert_eq_const_safe!($T::from_str_radix("-123", 10), Ok(-123 as $T)); - assert_eq_const_safe!($T::from_str_radix("-1001", 2), Ok(-9 as $T)); - assert_eq_const_safe!($T::from_str_radix("-123", 8), Ok(-83 as $T)); - assert_eq_const_safe!(i32::from_str_radix("-123", 16), Ok(-291 as i32)); - assert_eq_const_safe!(i32::from_str_radix("-ffff", 16), Ok(-65535 as i32)); - assert_eq_const_safe!(i32::from_str_radix("-FFFF", 16), Ok(-65535 as i32)); - assert_eq_const_safe!($T::from_str_radix("-z", 36), Ok(-35 as $T)); - assert_eq_const_safe!($T::from_str_radix("-Z", 36), Ok(-35 as $T)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("123", 10), Ok(123 as $T)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("1001", 2), Ok(9 as $T)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("123", 8), Ok(83 as $T)); + assert_eq_const_safe!(Result: i32::from_str_radix("123", 16), Ok(291 as i32)); + assert_eq_const_safe!(Result: i32::from_str_radix("ffff", 16), Ok(65535 as i32)); + assert_eq_const_safe!(Result: i32::from_str_radix("FFFF", 16), Ok(65535 as i32)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("z", 36), Ok(35 as $T)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("Z", 36), Ok(35 as $T)); + + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("-123", 10), Ok(-123 as $T)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("-1001", 2), Ok(-9 as $T)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("-123", 8), Ok(-83 as $T)); + assert_eq_const_safe!(Result: i32::from_str_radix("-123", 16), Ok(-291 as i32)); + assert_eq_const_safe!(Result: i32::from_str_radix("-ffff", 16), Ok(-65535 as i32)); + assert_eq_const_safe!(Result: i32::from_str_radix("-FFFF", 16), Ok(-65535 as i32)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("-z", 36), Ok(-35 as $T)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("-Z", 36), Ok(-35 as $T)); assert!($T::from_str_radix("Z", 35).is_err()); assert!($T::from_str_radix("-9", 2).is_err()); @@ -277,16 +278,16 @@ macro_rules! int_module { fn test_pow() { { const R: $T = 2; - assert_eq_const_safe!(R.pow(2), 4 as $T); - assert_eq_const_safe!(R.pow(0), 1 as $T); - assert_eq_const_safe!(R.wrapping_pow(2), 4 as $T); - assert_eq_const_safe!(R.wrapping_pow(0), 1 as $T); - assert_eq_const_safe!(R.checked_pow(2), Some(4 as $T)); - assert_eq_const_safe!(R.checked_pow(0), Some(1 as $T)); - assert_eq_const_safe!(R.overflowing_pow(2), (4 as $T, false)); - assert_eq_const_safe!(R.overflowing_pow(0), (1 as $T, false)); - assert_eq_const_safe!(R.saturating_pow(2), 4 as $T); - assert_eq_const_safe!(R.saturating_pow(0), 1 as $T); + assert_eq_const_safe!($T: R.pow(2), 4 as $T); + assert_eq_const_safe!($T: R.pow(0), 1 as $T); + assert_eq_const_safe!($T: R.wrapping_pow(2), 4 as $T); + assert_eq_const_safe!($T: R.wrapping_pow(0), 1 as $T); + assert_eq_const_safe!(Option<$T>: R.checked_pow(2), Some(4 as $T)); + assert_eq_const_safe!(Option<$T>: R.checked_pow(0), Some(1 as $T)); + assert_eq_const_safe!(($T, bool): R.overflowing_pow(2), (4 as $T, false)); + assert_eq_const_safe!(($T, bool): R.overflowing_pow(0), (1 as $T, false)); + assert_eq_const_safe!($T: R.saturating_pow(2), 4 as $T); + assert_eq_const_safe!($T: R.saturating_pow(0), 1 as $T); } { @@ -295,221 +296,227 @@ macro_rules! int_module { // 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_const_safe!(R.wrapping_pow(2), 1 as $T); - assert_eq_const_safe!(R.checked_pow(2), None); - assert_eq_const_safe!(R.overflowing_pow(2), (1 as $T, true)); - assert_eq_const_safe!(R.saturating_pow(2), MAX); + assert_eq_const_safe!($T: R.wrapping_pow(2), 1 as $T); + assert_eq_const_safe!(Option<$T>: R.checked_pow(2), None); + assert_eq_const_safe!(($T, bool): R.overflowing_pow(2), (1 as $T, true)); + assert_eq_const_safe!($T: R.saturating_pow(2), MAX); } { // test for negative exponent. const R: $T = -2; - assert_eq_const_safe!(R.pow(2), 4 as $T); - assert_eq_const_safe!(R.pow(3), -8 as $T); - assert_eq_const_safe!(R.pow(0), 1 as $T); - assert_eq_const_safe!(R.wrapping_pow(2), 4 as $T); - assert_eq_const_safe!(R.wrapping_pow(3), -8 as $T); - assert_eq_const_safe!(R.wrapping_pow(0), 1 as $T); - assert_eq_const_safe!(R.checked_pow(2), Some(4 as $T)); - assert_eq_const_safe!(R.checked_pow(3), Some(-8 as $T)); - assert_eq_const_safe!(R.checked_pow(0), Some(1 as $T)); - assert_eq_const_safe!(R.overflowing_pow(2), (4 as $T, false)); - assert_eq_const_safe!(R.overflowing_pow(3), (-8 as $T, false)); - assert_eq_const_safe!(R.overflowing_pow(0), (1 as $T, false)); - assert_eq_const_safe!(R.saturating_pow(2), 4 as $T); - assert_eq_const_safe!(R.saturating_pow(3), -8 as $T); - assert_eq_const_safe!(R.saturating_pow(0), 1 as $T); + assert_eq_const_safe!($T: R.pow(2), 4 as $T); + assert_eq_const_safe!($T: R.pow(3), -8 as $T); + assert_eq_const_safe!($T: R.pow(0), 1 as $T); + assert_eq_const_safe!($T: R.wrapping_pow(2), 4 as $T); + assert_eq_const_safe!($T: R.wrapping_pow(3), -8 as $T); + assert_eq_const_safe!($T: R.wrapping_pow(0), 1 as $T); + assert_eq_const_safe!(Option<$T>: R.checked_pow(2), Some(4 as $T)); + assert_eq_const_safe!(Option<$T>: R.checked_pow(3), Some(-8 as $T)); + assert_eq_const_safe!(Option<$T>: R.checked_pow(0), Some(1 as $T)); + assert_eq_const_safe!(($T, bool): R.overflowing_pow(2), (4 as $T, false)); + assert_eq_const_safe!(($T, bool): R.overflowing_pow(3), (-8 as $T, false)); + assert_eq_const_safe!(($T, bool): R.overflowing_pow(0), (1 as $T, false)); + assert_eq_const_safe!($T: R.saturating_pow(2), 4 as $T); + assert_eq_const_safe!($T: R.saturating_pow(3), -8 as $T); + assert_eq_const_safe!($T: R.saturating_pow(0), 1 as $T); } } fn test_div_floor() { const A: $T = 8; const B: $T = 3; - assert_eq_const_safe!(A.div_floor(B), 2); - assert_eq_const_safe!(A.div_floor(-B), -3); - assert_eq_const_safe!((-A).div_floor(B), -3); - assert_eq_const_safe!((-A).div_floor(-B), 2); + assert_eq_const_safe!($T: A.div_floor(B), 2); + assert_eq_const_safe!($T: A.div_floor(-B), -3); + assert_eq_const_safe!($T: (-A).div_floor(B), -3); + assert_eq_const_safe!($T: (-A).div_floor(-B), 2); } fn test_div_ceil() { const A: $T = 8; const B: $T = 3; - assert_eq_const_safe!(A.div_ceil(B), 3); - assert_eq_const_safe!(A.div_ceil(-B), -2); - assert_eq_const_safe!((-A).div_ceil(B), -2); - assert_eq_const_safe!((-A).div_ceil(-B), 3); + assert_eq_const_safe!($T: A.div_ceil(B), 3); + assert_eq_const_safe!($T: A.div_ceil(-B), -2); + assert_eq_const_safe!($T: (-A).div_ceil(B), -2); + assert_eq_const_safe!($T: (-A).div_ceil(-B), 3); } fn test_next_multiple_of() { - assert_eq_const_safe!((16 as $T).next_multiple_of(8), 16); - assert_eq_const_safe!((23 as $T).next_multiple_of(8), 24); - assert_eq_const_safe!((16 as $T).next_multiple_of(-8), 16); - assert_eq_const_safe!((23 as $T).next_multiple_of(-8), 16); - assert_eq_const_safe!((-16 as $T).next_multiple_of(8), -16); - assert_eq_const_safe!((-23 as $T).next_multiple_of(8), -16); - assert_eq_const_safe!((-16 as $T).next_multiple_of(-8), -16); - assert_eq_const_safe!((-23 as $T).next_multiple_of(-8), -24); - assert_eq_const_safe!(MIN.next_multiple_of(-1), MIN); + assert_eq_const_safe!($T: (16 as $T).next_multiple_of(8), 16); + assert_eq_const_safe!($T: (23 as $T).next_multiple_of(8), 24); + assert_eq_const_safe!($T: (16 as $T).next_multiple_of(-8), 16); + assert_eq_const_safe!($T: (23 as $T).next_multiple_of(-8), 16); + assert_eq_const_safe!($T: (-16 as $T).next_multiple_of(8), -16); + assert_eq_const_safe!($T: (-23 as $T).next_multiple_of(8), -16); + assert_eq_const_safe!($T: (-16 as $T).next_multiple_of(-8), -16); + assert_eq_const_safe!($T: (-23 as $T).next_multiple_of(-8), -24); + assert_eq_const_safe!($T: MIN.next_multiple_of(-1), MIN); } fn test_checked_next_multiple_of() { - assert_eq_const_safe!((16 as $T).checked_next_multiple_of(8), Some(16)); - assert_eq_const_safe!((23 as $T).checked_next_multiple_of(8), Some(24)); - assert_eq_const_safe!((16 as $T).checked_next_multiple_of(-8), Some(16)); - assert_eq_const_safe!((23 as $T).checked_next_multiple_of(-8), Some(16)); - assert_eq_const_safe!((-16 as $T).checked_next_multiple_of(8), Some(-16)); - assert_eq_const_safe!((-23 as $T).checked_next_multiple_of(8), Some(-16)); - assert_eq_const_safe!((-16 as $T).checked_next_multiple_of(-8), Some(-16)); - assert_eq_const_safe!((-23 as $T).checked_next_multiple_of(-8), Some(-24)); - assert_eq_const_safe!((1 as $T).checked_next_multiple_of(0), None); - assert_eq_const_safe!(MAX.checked_next_multiple_of(2), None); - assert_eq_const_safe!(MIN.checked_next_multiple_of(-3), None); - assert_eq_const_safe!(MIN.checked_next_multiple_of(-1), Some(MIN)); + assert_eq_const_safe!(Option<$T>: (16 as $T).checked_next_multiple_of(8), Some(16)); + assert_eq_const_safe!(Option<$T>: (23 as $T).checked_next_multiple_of(8), Some(24)); + assert_eq_const_safe!(Option<$T>: (16 as $T).checked_next_multiple_of(-8), Some(16)); + assert_eq_const_safe!(Option<$T>: (23 as $T).checked_next_multiple_of(-8), Some(16)); + assert_eq_const_safe!(Option<$T>: (-16 as $T).checked_next_multiple_of(8), Some(-16)); + assert_eq_const_safe!(Option<$T>: (-23 as $T).checked_next_multiple_of(8), Some(-16)); + assert_eq_const_safe!(Option<$T>: (-16 as $T).checked_next_multiple_of(-8), Some(-16)); + assert_eq_const_safe!(Option<$T>: (-23 as $T).checked_next_multiple_of(-8), Some(-24)); + assert_eq_const_safe!(Option<$T>: (1 as $T).checked_next_multiple_of(0), None); + assert_eq_const_safe!(Option<$T>: MAX.checked_next_multiple_of(2), None); + assert_eq_const_safe!(Option<$T>: MIN.checked_next_multiple_of(-3), None); + assert_eq_const_safe!(Option<$T>: MIN.checked_next_multiple_of(-1), Some(MIN)); } fn test_carrying_add() { - assert_eq_const_safe!(MAX.carrying_add(1, false), (MIN, true)); - assert_eq_const_safe!(MAX.carrying_add(0, true), (MIN, true)); - assert_eq_const_safe!(MAX.carrying_add(1, true), (MIN + 1, true)); - assert_eq_const_safe!(MAX.carrying_add(-1, false), (MAX - 1, false)); - assert_eq_const_safe!(MAX.carrying_add(-1, true), (MAX, false)); // no intermediate overflow - assert_eq_const_safe!(MIN.carrying_add(-1, false), (MAX, true)); - assert_eq_const_safe!(MIN.carrying_add(-1, true), (MIN, false)); // no intermediate overflow - assert_eq_const_safe!((0 as $T).carrying_add(MAX, true), (MIN, true)); - assert_eq_const_safe!((0 as $T).carrying_add(MIN, true), (MIN + 1, false)); + assert_eq_const_safe!(($T, bool): MAX.carrying_add(1, false), (MIN, true)); + assert_eq_const_safe!(($T, bool): MAX.carrying_add(0, true), (MIN, true)); + assert_eq_const_safe!(($T, bool): MAX.carrying_add(1, true), (MIN + 1, true)); + assert_eq_const_safe!(($T, bool): MAX.carrying_add(-1, false), (MAX - 1, false)); + assert_eq_const_safe!(($T, bool): MAX.carrying_add(-1, true), (MAX, false)); // no intermediate overflow + assert_eq_const_safe!(($T, bool): MIN.carrying_add(-1, false), (MAX, true)); + assert_eq_const_safe!(($T, bool): MIN.carrying_add(-1, true), (MIN, false)); // no intermediate overflow + assert_eq_const_safe!(($T, bool): (0 as $T).carrying_add(MAX, true), (MIN, true)); + assert_eq_const_safe!(($T, bool): (0 as $T).carrying_add(MIN, true), (MIN + 1, false)); } fn test_borrowing_sub() { - assert_eq_const_safe!(MIN.borrowing_sub(1, false), (MAX, true)); - assert_eq_const_safe!(MIN.borrowing_sub(0, true), (MAX, true)); - assert_eq_const_safe!(MIN.borrowing_sub(1, true), (MAX - 1, true)); - assert_eq_const_safe!(MIN.borrowing_sub(-1, false), (MIN + 1, false)); - assert_eq_const_safe!(MIN.borrowing_sub(-1, true), (MIN, false)); // no intermediate overflow - assert_eq_const_safe!(MAX.borrowing_sub(-1, false), (MIN, true)); - assert_eq_const_safe!(MAX.borrowing_sub(-1, true), (MAX, false)); // no intermediate overflow - assert_eq_const_safe!((0 as $T).borrowing_sub(MIN, false), (MIN, true)); - assert_eq_const_safe!((0 as $T).borrowing_sub(MIN, true), (MAX, false)); + assert_eq_const_safe!(($T, bool): MIN.borrowing_sub(1, false), (MAX, true)); + assert_eq_const_safe!(($T, bool): MIN.borrowing_sub(0, true), (MAX, true)); + assert_eq_const_safe!(($T, bool): MIN.borrowing_sub(1, true), (MAX - 1, true)); + assert_eq_const_safe!(($T, bool): MIN.borrowing_sub(-1, false), (MIN + 1, false)); + assert_eq_const_safe!(($T, bool): MIN.borrowing_sub(-1, true), (MIN, false)); // no intermediate overflow + assert_eq_const_safe!(($T, bool): MAX.borrowing_sub(-1, false), (MIN, true)); + assert_eq_const_safe!(($T, bool): MAX.borrowing_sub(-1, true), (MAX, false)); // no intermediate overflow + assert_eq_const_safe!(($T, bool): (0 as $T).borrowing_sub(MIN, false), (MIN, true)); + assert_eq_const_safe!(($T, bool): (0 as $T).borrowing_sub(MIN, true), (MAX, false)); } fn test_widening_mul() { - assert_eq_const_safe!(MAX.widening_mul(MAX), (1, MAX / 2)); - assert_eq_const_safe!(MIN.widening_mul(MAX), (MIN as $U, MIN / 2)); - assert_eq_const_safe!(MIN.widening_mul(MIN), (0, MAX / 2 + 1)); + assert_eq_const_safe!(($U, $T): MAX.widening_mul(MAX), (1, MAX / 2)); + assert_eq_const_safe!(($U, $T): MIN.widening_mul(MAX), (MIN as $U, MIN / 2)); + assert_eq_const_safe!(($U, $T): MIN.widening_mul(MIN), (0, MAX / 2 + 1)); } fn test_carrying_mul() { - assert_eq_const_safe!(MAX.carrying_mul(MAX, 0), (1, MAX / 2)); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MAX.carrying_mul(MAX, 0), (1, MAX / 2)); + assert_eq_const_safe!(($U, $T): MAX.carrying_mul(MAX, MAX), (UMAX / 2 + 1, MAX / 2) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MAX.carrying_mul(MAX, MIN), (UMAX / 2 + 2, MAX / 2 - 1) ); - assert_eq_const_safe!(MIN.carrying_mul(MAX, 0), (MIN as $U, MIN / 2)); - assert_eq_const_safe!(MIN.carrying_mul(MAX, MAX), (UMAX, MIN / 2)); - assert_eq_const_safe!(MIN.carrying_mul(MAX, MIN), (0, MIN / 2)); - assert_eq_const_safe!(MIN.carrying_mul(MIN, 0), (0, MAX / 2 + 1)); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MIN.carrying_mul(MAX, 0), (MIN as $U, MIN / 2)); + assert_eq_const_safe!(($U, $T): MIN.carrying_mul(MAX, MAX), (UMAX, MIN / 2)); + assert_eq_const_safe!(($U, $T): MIN.carrying_mul(MAX, MIN), (0, MIN / 2)); + assert_eq_const_safe!(($U, $T): MIN.carrying_mul(MIN, 0), (0, MAX / 2 + 1)); + assert_eq_const_safe!(($U, $T): MIN.carrying_mul(MIN, MAX), (UMAX / 2, MAX / 2 + 1) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MIN.carrying_mul(MIN, MIN), (UMAX / 2 + 1, MAX / 2) ); } fn test_carrying_mul_add() { - assert_eq_const_safe!(MAX.carrying_mul_add(MAX, 0, 0), (1, MAX / 2)); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MAX.carrying_mul_add(MAX, 0, 0), (1, MAX / 2)); + assert_eq_const_safe!(($U, $T): MAX.carrying_mul_add(MAX, MAX, 0), (UMAX / 2 + 1, MAX / 2) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MAX.carrying_mul_add(MAX, MIN, 0), (UMAX / 2 + 2, MAX / 2 - 1) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MAX.carrying_mul_add(MAX, MAX, MAX), (UMAX, MAX / 2) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MAX.carrying_mul_add(MAX, MAX, MIN), (0, MAX / 2) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MAX.carrying_mul_add(MAX, MIN, MIN), (1, MAX / 2 - 1) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MIN.carrying_mul_add(MAX, 0, 0), (MIN as $U, MIN / 2) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MIN.carrying_mul_add(MAX, MAX, 0), (UMAX, MIN / 2) ); - assert_eq_const_safe!(MIN.carrying_mul_add(MAX, MIN, 0), (0, MIN / 2)); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): + MIN.carrying_mul_add(MAX, MIN, 0), + (0, MIN / 2) + ); + assert_eq_const_safe!(($U, $T): MIN.carrying_mul_add(MAX, MAX, MAX), (UMAX / 2 - 1, MIN / 2 + 1) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MIN.carrying_mul_add(MAX, MAX, MIN), (UMAX / 2, MIN / 2) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MIN.carrying_mul_add(MAX, MIN, MIN), (UMAX / 2 + 1, MIN / 2 - 1) ); - assert_eq_const_safe!(MIN.carrying_mul_add(MIN, 0, 0), (0, MAX / 2 + 1)); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): + MIN.carrying_mul_add(MIN, 0, 0), + (0, MAX / 2 + 1) + ); + assert_eq_const_safe!(($U, $T): MIN.carrying_mul_add(MIN, MAX, 0), (UMAX / 2, MAX / 2 + 1) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MIN.carrying_mul_add(MIN, MIN, 0), (UMAX / 2 + 1, MAX / 2) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MIN.carrying_mul_add(MIN, MAX, MAX), (UMAX - 1, MAX / 2 + 1) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MIN.carrying_mul_add(MIN, MAX, MIN), (UMAX, MAX / 2) ); - assert_eq_const_safe!( + assert_eq_const_safe!(($U, $T): MIN.carrying_mul_add(MIN, MIN, MIN), (0, MAX / 2) ); } fn test_midpoint() { - assert_eq_const_safe!(<$T>::midpoint(1, 3), 2); - assert_eq_const_safe!(<$T>::midpoint(3, 1), 2); + assert_eq_const_safe!($T: <$T>::midpoint(1, 3), 2); + assert_eq_const_safe!($T: <$T>::midpoint(3, 1), 2); - assert_eq_const_safe!(<$T>::midpoint(0, 0), 0); - assert_eq_const_safe!(<$T>::midpoint(0, 2), 1); - assert_eq_const_safe!(<$T>::midpoint(2, 0), 1); - assert_eq_const_safe!(<$T>::midpoint(2, 2), 2); + assert_eq_const_safe!($T: <$T>::midpoint(0, 0), 0); + assert_eq_const_safe!($T: <$T>::midpoint(0, 2), 1); + assert_eq_const_safe!($T: <$T>::midpoint(2, 0), 1); + assert_eq_const_safe!($T: <$T>::midpoint(2, 2), 2); - assert_eq_const_safe!(<$T>::midpoint(1, 4), 2); - assert_eq_const_safe!(<$T>::midpoint(4, 1), 2); - assert_eq_const_safe!(<$T>::midpoint(3, 4), 3); - assert_eq_const_safe!(<$T>::midpoint(4, 3), 3); + assert_eq_const_safe!($T: <$T>::midpoint(1, 4), 2); + assert_eq_const_safe!($T: <$T>::midpoint(4, 1), 2); + assert_eq_const_safe!($T: <$T>::midpoint(3, 4), 3); + assert_eq_const_safe!($T: <$T>::midpoint(4, 3), 3); - assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), 0); - assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), 0); - assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN); - assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX); + assert_eq_const_safe!($T: <$T>::midpoint(<$T>::MIN, <$T>::MAX), 0); + assert_eq_const_safe!($T: <$T>::midpoint(<$T>::MAX, <$T>::MIN), 0); + assert_eq_const_safe!($T: <$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN); + assert_eq_const_safe!($T: <$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX); - assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3); - assert_eq_const_safe!(<$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3); - assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, 6), <$T>::MAX / 2 + 3); - assert_eq_const_safe!(<$T>::midpoint(6, <$T>::MAX), <$T>::MAX / 2 + 3); + assert_eq_const_safe!($T: <$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3); + assert_eq_const_safe!($T: <$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3); + assert_eq_const_safe!($T: <$T>::midpoint(<$T>::MAX, 6), <$T>::MAX / 2 + 3); + assert_eq_const_safe!($T: <$T>::midpoint(6, <$T>::MAX), <$T>::MAX / 2 + 3); } } @@ -526,154 +533,154 @@ macro_rules! int_module { test_runtime_and_compiletime! { fn test_unbounded_shl() { // <$T>::MIN - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN << SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN << SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN << SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MIN << SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 1), (<$T>::MIN << 1)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 3), (<$T>::MIN << 3)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 5), (<$T>::MIN << 5)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN << SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN << SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN << SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MIN << SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, 1), (<$T>::MIN << 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, 3), (<$T>::MIN << 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, 5), (<$T>::MIN << 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW3), 0); // <$T>::MAX - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_ONE), (<$T>::MAX << SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_TWO), (<$T>::MAX << SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_THREE), (<$T>::MAX << SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MAX << SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, 1), (<$T>::MAX << 1)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, 3), (<$T>::MAX << 3)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, 5), (<$T>::MAX << 5)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_ONE), (<$T>::MAX << SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_TWO), (<$T>::MAX << SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_THREE), (<$T>::MAX << SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MAX << SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, 1), (<$T>::MAX << 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, 3), (<$T>::MAX << 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, 5), (<$T>::MAX << 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW3), 0); // 1 - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_ONE), (1 << SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_TWO), (1 << SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_THREE), (1 << SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_FOUR), (1 << SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, 1), (1 << 1)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, 3), (1 << 3)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, 5), (1 << 5)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_ONE), (1 << SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_TWO), (1 << SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_THREE), (1 << SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_FOUR), (1 << SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, 1), (1 << 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, 3), (1 << 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, 5), (1 << 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW3), 0); // -1 - assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_TEST_ONE), (-1 << SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_TEST_TWO), (-1 << SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_TEST_THREE), (-1 << SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_TEST_FOUR), (-1 << SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shl(-1, 1), (-1 << 1)); - assert_eq_const_safe!(<$T>::unbounded_shl(-1, 3), (-1 << 3)); - assert_eq_const_safe!(<$T>::unbounded_shl(-1, 5), (-1 << 5)); - assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(-1, SHIFT_AMOUNT_TEST_ONE), (-1 << SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(-1, SHIFT_AMOUNT_TEST_TWO), (-1 << SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(-1, SHIFT_AMOUNT_TEST_THREE), (-1 << SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(-1, SHIFT_AMOUNT_TEST_FOUR), (-1 << SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(-1, 1), (-1 << 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(-1, 3), (-1 << 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(-1, 5), (-1 << 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(-1, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(-1, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(-1, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(-1, SHIFT_AMOUNT_OVERFLOW3), 0); // 8 - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_ONE), (8 << SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_TWO), (8 << SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_THREE), (8 << SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_FOUR), (8 << SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, 1), (8 << 1)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, 3), (8 << 3)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, 5), (8 << 5)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_ONE), (8 << SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_TWO), (8 << SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_THREE), (8 << SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_FOUR), (8 << SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, 1), (8 << 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, 3), (8 << 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, 5), (8 << 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW3), 0); // 17 - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_ONE), (17 << SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_TWO), (17 << SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_THREE), (17 << SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_FOUR), (17 << SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, 1), (17 << 1)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, 3), (17 << 3)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, 5), (17 << 5)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_ONE), (17 << SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_TWO), (17 << SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_THREE), (17 << SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_FOUR), (17 << SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, 1), (17 << 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, 3), (17 << 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, 5), (17 << 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW3), 0); } fn test_unbounded_shr() { // <$T>::MIN - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN >> SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MIN >> SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 1), (<$T>::MIN >> 1)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 3), (<$T>::MIN >> 3)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 5), (<$T>::MIN >> 5)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW), -1); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW2), -1); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW3), -1); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN >> SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MIN >> SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, 1), (<$T>::MIN >> 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, 3), (<$T>::MIN >> 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, 5), (<$T>::MIN >> 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW), -1); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW2), -1); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW3), -1); // <$T>::MAX - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_ONE), (<$T>::MAX >> SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_TWO), (<$T>::MAX >> SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_THREE), (<$T>::MAX >> SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MAX >> SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, 1), (<$T>::MAX >> 1)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, 3), (<$T>::MAX >> 3)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, 5), (<$T>::MAX >> 5)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_ONE), (<$T>::MAX >> SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_TWO), (<$T>::MAX >> SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_THREE), (<$T>::MAX >> SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MAX >> SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, 1), (<$T>::MAX >> 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, 3), (<$T>::MAX >> 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, 5), (<$T>::MAX >> 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW3), 0); // 1 - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_ONE), (1 >> SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_TWO), (1 >> SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_THREE), (1 >> SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_FOUR), (1 >> SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, 1), (1 >> 1)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, 3), (1 >> 3)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, 5), (1 >> 5)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_ONE), (1 >> SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_TWO), (1 >> SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_THREE), (1 >> SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_FOUR), (1 >> SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, 1), (1 >> 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, 3), (1 >> 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, 5), (1 >> 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW3), 0); // -1 - assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_TEST_ONE), (-1 >> SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_TEST_TWO), (-1 >> SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_TEST_THREE), (-1 >> SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_TEST_FOUR), (-1 >> SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shr(-1, 1), (-1 >> 1)); - assert_eq_const_safe!(<$T>::unbounded_shr(-1, 3), (-1 >> 3)); - assert_eq_const_safe!(<$T>::unbounded_shr(-1, 5), (-1 >> 5)); - assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_OVERFLOW), -1); - assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_OVERFLOW), -1); - assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_OVERFLOW2), -1); - assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_OVERFLOW3), -1); + assert_eq_const_safe!($T: <$T>::unbounded_shr(-1, SHIFT_AMOUNT_TEST_ONE), (-1 >> SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(-1, SHIFT_AMOUNT_TEST_TWO), (-1 >> SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(-1, SHIFT_AMOUNT_TEST_THREE), (-1 >> SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(-1, SHIFT_AMOUNT_TEST_FOUR), (-1 >> SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(-1, 1), (-1 >> 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(-1, 3), (-1 >> 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(-1, 5), (-1 >> 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(-1, SHIFT_AMOUNT_OVERFLOW), -1); + assert_eq_const_safe!($T: <$T>::unbounded_shr(-1, SHIFT_AMOUNT_OVERFLOW), -1); + assert_eq_const_safe!($T: <$T>::unbounded_shr(-1, SHIFT_AMOUNT_OVERFLOW2), -1); + assert_eq_const_safe!($T: <$T>::unbounded_shr(-1, SHIFT_AMOUNT_OVERFLOW3), -1); // 8 - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_ONE), (8 >> SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_TWO), (8 >> SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_THREE), (8 >> SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_FOUR), (8 >> SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, 1), (8 >> 1)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, 3), (8 >> 3)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, 5), (8 >> 5)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_ONE), (8 >> SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_TWO), (8 >> SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_THREE), (8 >> SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_FOUR), (8 >> SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, 1), (8 >> 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, 3), (8 >> 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, 5), (8 >> 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW3), 0); // 17 - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_ONE), (17 >> SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_TWO), (17 >> SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_THREE), (17 >> SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_FOUR), (17 >> SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, 1), (17 >> 1)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, 3), (17 >> 3)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, 5), (17 >> 5)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_ONE), (17 >> SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_TWO), (17 >> SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_THREE), (17 >> SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_FOUR), (17 >> SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, 1), (17 >> 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, 3), (17 >> 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, 5), (17 >> 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW3), 0); } } }; diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 0add9a01e682d..a6b75f7026604 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -732,157 +732,157 @@ assume_usize_width! { } macro_rules! test_float { - ($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => { + ($modname: ident, $fassert: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => { mod $modname { #[test] fn min() { - assert_eq!((0.0 as $fty).min(0.0), 0.0); - assert!((0.0 as $fty).min(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).min(-0.0), -0.0); - assert!((-0.0 as $fty).min(-0.0).is_sign_negative()); - assert_eq!((9.0 as $fty).min(9.0), 9.0); - assert_eq!((-9.0 as $fty).min(0.0), -9.0); - assert_eq!((0.0 as $fty).min(9.0), 0.0); - assert!((0.0 as $fty).min(9.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).min(9.0), -0.0); - assert!((-0.0 as $fty).min(9.0).is_sign_negative()); - assert_eq!((-0.0 as $fty).min(-9.0), -9.0); - assert_eq!(($inf as $fty).min(9.0), 9.0); - assert_eq!((9.0 as $fty).min($inf), 9.0); - assert_eq!(($inf as $fty).min(-9.0), -9.0); - assert_eq!((-9.0 as $fty).min($inf), -9.0); - assert_eq!(($neginf as $fty).min(9.0), $neginf); - assert_eq!((9.0 as $fty).min($neginf), $neginf); - assert_eq!(($neginf as $fty).min(-9.0), $neginf); - assert_eq!((-9.0 as $fty).min($neginf), $neginf); - assert_eq!(($nan as $fty).min(9.0), 9.0); - assert_eq!(($nan as $fty).min(-9.0), -9.0); - assert_eq!((9.0 as $fty).min($nan), 9.0); - assert_eq!((-9.0 as $fty).min($nan), -9.0); - assert!(($nan as $fty).min($nan).is_nan()); + $fassert!((0.0 as $fty).min(0.0), 0.0); + $fassert!((0.0 as $fty).min(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).min(-0.0), -0.0); + $fassert!((-0.0 as $fty).min(-0.0).is_sign_negative()); + $fassert!((9.0 as $fty).min(9.0), 9.0); + $fassert!((-9.0 as $fty).min(0.0), -9.0); + $fassert!((0.0 as $fty).min(9.0), 0.0); + $fassert!((0.0 as $fty).min(9.0).is_sign_positive()); + $fassert!((-0.0 as $fty).min(9.0), -0.0); + $fassert!((-0.0 as $fty).min(9.0).is_sign_negative()); + $fassert!((-0.0 as $fty).min(-9.0), -9.0); + $fassert!(($inf as $fty).min(9.0), 9.0); + $fassert!((9.0 as $fty).min($inf), 9.0); + $fassert!(($inf as $fty).min(-9.0), -9.0); + $fassert!((-9.0 as $fty).min($inf), -9.0); + $fassert!(($neginf as $fty).min(9.0), $neginf); + $fassert!((9.0 as $fty).min($neginf), $neginf); + $fassert!(($neginf as $fty).min(-9.0), $neginf); + $fassert!((-9.0 as $fty).min($neginf), $neginf); + $fassert!(($nan as $fty).min(9.0), 9.0); + $fassert!(($nan as $fty).min(-9.0), -9.0); + $fassert!((9.0 as $fty).min($nan), 9.0); + $fassert!((-9.0 as $fty).min($nan), -9.0); + $fassert!(($nan as $fty).min($nan).is_nan()); } #[test] fn max() { - assert_eq!((0.0 as $fty).max(0.0), 0.0); - assert!((0.0 as $fty).max(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).max(-0.0), -0.0); - assert!((-0.0 as $fty).max(-0.0).is_sign_negative()); - assert_eq!((9.0 as $fty).max(9.0), 9.0); - assert_eq!((-9.0 as $fty).max(0.0), 0.0); - assert!((-9.0 as $fty).max(0.0).is_sign_positive()); - assert_eq!((-9.0 as $fty).max(-0.0), -0.0); - assert!((-9.0 as $fty).max(-0.0).is_sign_negative()); - assert_eq!((0.0 as $fty).max(9.0), 9.0); - assert_eq!((0.0 as $fty).max(-9.0), 0.0); - assert!((0.0 as $fty).max(-9.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).max(-9.0), -0.0); - assert!((-0.0 as $fty).max(-9.0).is_sign_negative()); - assert_eq!(($inf as $fty).max(9.0), $inf); - assert_eq!((9.0 as $fty).max($inf), $inf); - assert_eq!(($inf as $fty).max(-9.0), $inf); - assert_eq!((-9.0 as $fty).max($inf), $inf); - assert_eq!(($neginf as $fty).max(9.0), 9.0); - assert_eq!((9.0 as $fty).max($neginf), 9.0); - assert_eq!(($neginf as $fty).max(-9.0), -9.0); - assert_eq!((-9.0 as $fty).max($neginf), -9.0); - assert_eq!(($nan as $fty).max(9.0), 9.0); - assert_eq!(($nan as $fty).max(-9.0), -9.0); - assert_eq!((9.0 as $fty).max($nan), 9.0); - assert_eq!((-9.0 as $fty).max($nan), -9.0); - assert!(($nan as $fty).max($nan).is_nan()); + $fassert!((0.0 as $fty).max(0.0), 0.0); + $fassert!((0.0 as $fty).max(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).max(-0.0), -0.0); + $fassert!((-0.0 as $fty).max(-0.0).is_sign_negative()); + $fassert!((9.0 as $fty).max(9.0), 9.0); + $fassert!((-9.0 as $fty).max(0.0), 0.0); + $fassert!((-9.0 as $fty).max(0.0).is_sign_positive()); + $fassert!((-9.0 as $fty).max(-0.0), -0.0); + $fassert!((-9.0 as $fty).max(-0.0).is_sign_negative()); + $fassert!((0.0 as $fty).max(9.0), 9.0); + $fassert!((0.0 as $fty).max(-9.0), 0.0); + $fassert!((0.0 as $fty).max(-9.0).is_sign_positive()); + $fassert!((-0.0 as $fty).max(-9.0), -0.0); + $fassert!((-0.0 as $fty).max(-9.0).is_sign_negative()); + $fassert!(($inf as $fty).max(9.0), $inf); + $fassert!((9.0 as $fty).max($inf), $inf); + $fassert!(($inf as $fty).max(-9.0), $inf); + $fassert!((-9.0 as $fty).max($inf), $inf); + $fassert!(($neginf as $fty).max(9.0), 9.0); + $fassert!((9.0 as $fty).max($neginf), 9.0); + $fassert!(($neginf as $fty).max(-9.0), -9.0); + $fassert!((-9.0 as $fty).max($neginf), -9.0); + $fassert!(($nan as $fty).max(9.0), 9.0); + $fassert!(($nan as $fty).max(-9.0), -9.0); + $fassert!((9.0 as $fty).max($nan), 9.0); + $fassert!((-9.0 as $fty).max($nan), -9.0); + $fassert!(($nan as $fty).max($nan).is_nan()); } #[test] fn minimum() { - assert_eq!((0.0 as $fty).minimum(0.0), 0.0); - assert!((0.0 as $fty).minimum(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).minimum(0.0), -0.0); - assert!((-0.0 as $fty).minimum(0.0).is_sign_negative()); - assert_eq!((-0.0 as $fty).minimum(-0.0), -0.0); - assert!((-0.0 as $fty).minimum(-0.0).is_sign_negative()); - assert_eq!((9.0 as $fty).minimum(9.0), 9.0); - assert_eq!((-9.0 as $fty).minimum(0.0), -9.0); - assert_eq!((0.0 as $fty).minimum(9.0), 0.0); - assert!((0.0 as $fty).minimum(9.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).minimum(9.0), -0.0); - assert!((-0.0 as $fty).minimum(9.0).is_sign_negative()); - assert_eq!((-0.0 as $fty).minimum(-9.0), -9.0); - assert_eq!(($inf as $fty).minimum(9.0), 9.0); - assert_eq!((9.0 as $fty).minimum($inf), 9.0); - assert_eq!(($inf as $fty).minimum(-9.0), -9.0); - assert_eq!((-9.0 as $fty).minimum($inf), -9.0); - assert_eq!(($neginf as $fty).minimum(9.0), $neginf); - assert_eq!((9.0 as $fty).minimum($neginf), $neginf); - assert_eq!(($neginf as $fty).minimum(-9.0), $neginf); - assert_eq!((-9.0 as $fty).minimum($neginf), $neginf); - assert!(($nan as $fty).minimum(9.0).is_nan()); - assert!(($nan as $fty).minimum(-9.0).is_nan()); - assert!((9.0 as $fty).minimum($nan).is_nan()); - assert!((-9.0 as $fty).minimum($nan).is_nan()); - assert!(($nan as $fty).minimum($nan).is_nan()); + $fassert!((0.0 as $fty).minimum(0.0), 0.0); + $fassert!((0.0 as $fty).minimum(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).minimum(0.0), -0.0); + $fassert!((-0.0 as $fty).minimum(0.0).is_sign_negative()); + $fassert!((-0.0 as $fty).minimum(-0.0), -0.0); + $fassert!((-0.0 as $fty).minimum(-0.0).is_sign_negative()); + $fassert!((9.0 as $fty).minimum(9.0), 9.0); + $fassert!((-9.0 as $fty).minimum(0.0), -9.0); + $fassert!((0.0 as $fty).minimum(9.0), 0.0); + $fassert!((0.0 as $fty).minimum(9.0).is_sign_positive()); + $fassert!((-0.0 as $fty).minimum(9.0), -0.0); + $fassert!((-0.0 as $fty).minimum(9.0).is_sign_negative()); + $fassert!((-0.0 as $fty).minimum(-9.0), -9.0); + $fassert!(($inf as $fty).minimum(9.0), 9.0); + $fassert!((9.0 as $fty).minimum($inf), 9.0); + $fassert!(($inf as $fty).minimum(-9.0), -9.0); + $fassert!((-9.0 as $fty).minimum($inf), -9.0); + $fassert!(($neginf as $fty).minimum(9.0), $neginf); + $fassert!((9.0 as $fty).minimum($neginf), $neginf); + $fassert!(($neginf as $fty).minimum(-9.0), $neginf); + $fassert!((-9.0 as $fty).minimum($neginf), $neginf); + $fassert!(($nan as $fty).minimum(9.0).is_nan()); + $fassert!(($nan as $fty).minimum(-9.0).is_nan()); + $fassert!((9.0 as $fty).minimum($nan).is_nan()); + $fassert!((-9.0 as $fty).minimum($nan).is_nan()); + $fassert!(($nan as $fty).minimum($nan).is_nan()); } #[test] fn maximum() { - assert_eq!((0.0 as $fty).maximum(0.0), 0.0); - assert!((0.0 as $fty).maximum(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).maximum(0.0), 0.0); - assert!((-0.0 as $fty).maximum(0.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).maximum(-0.0), -0.0); - assert!((-0.0 as $fty).maximum(-0.0).is_sign_negative()); - assert_eq!((9.0 as $fty).maximum(9.0), 9.0); - assert_eq!((-9.0 as $fty).maximum(0.0), 0.0); - assert!((-9.0 as $fty).maximum(0.0).is_sign_positive()); - assert_eq!((-9.0 as $fty).maximum(-0.0), -0.0); - assert!((-9.0 as $fty).maximum(-0.0).is_sign_negative()); - assert_eq!((0.0 as $fty).maximum(9.0), 9.0); - assert_eq!((0.0 as $fty).maximum(-9.0), 0.0); - assert!((0.0 as $fty).maximum(-9.0).is_sign_positive()); - assert_eq!((-0.0 as $fty).maximum(-9.0), -0.0); - assert!((-0.0 as $fty).maximum(-9.0).is_sign_negative()); - assert_eq!(($inf as $fty).maximum(9.0), $inf); - assert_eq!((9.0 as $fty).maximum($inf), $inf); - assert_eq!(($inf as $fty).maximum(-9.0), $inf); - assert_eq!((-9.0 as $fty).maximum($inf), $inf); - assert_eq!(($neginf as $fty).maximum(9.0), 9.0); - assert_eq!((9.0 as $fty).maximum($neginf), 9.0); - assert_eq!(($neginf as $fty).maximum(-9.0), -9.0); - assert_eq!((-9.0 as $fty).maximum($neginf), -9.0); - assert!(($nan as $fty).maximum(9.0).is_nan()); - assert!(($nan as $fty).maximum(-9.0).is_nan()); - assert!((9.0 as $fty).maximum($nan).is_nan()); - assert!((-9.0 as $fty).maximum($nan).is_nan()); - assert!(($nan as $fty).maximum($nan).is_nan()); + $fassert!((0.0 as $fty).maximum(0.0), 0.0); + $fassert!((0.0 as $fty).maximum(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).maximum(0.0), 0.0); + $fassert!((-0.0 as $fty).maximum(0.0).is_sign_positive()); + $fassert!((-0.0 as $fty).maximum(-0.0), -0.0); + $fassert!((-0.0 as $fty).maximum(-0.0).is_sign_negative()); + $fassert!((9.0 as $fty).maximum(9.0), 9.0); + $fassert!((-9.0 as $fty).maximum(0.0), 0.0); + $fassert!((-9.0 as $fty).maximum(0.0).is_sign_positive()); + $fassert!((-9.0 as $fty).maximum(-0.0), -0.0); + $fassert!((-9.0 as $fty).maximum(-0.0).is_sign_negative()); + $fassert!((0.0 as $fty).maximum(9.0), 9.0); + $fassert!((0.0 as $fty).maximum(-9.0), 0.0); + $fassert!((0.0 as $fty).maximum(-9.0).is_sign_positive()); + $fassert!((-0.0 as $fty).maximum(-9.0), -0.0); + $fassert!((-0.0 as $fty).maximum(-9.0).is_sign_negative()); + $fassert!(($inf as $fty).maximum(9.0), $inf); + $fassert!((9.0 as $fty).maximum($inf), $inf); + $fassert!(($inf as $fty).maximum(-9.0), $inf); + $fassert!((-9.0 as $fty).maximum($inf), $inf); + $fassert!(($neginf as $fty).maximum(9.0), 9.0); + $fassert!((9.0 as $fty).maximum($neginf), 9.0); + $fassert!(($neginf as $fty).maximum(-9.0), -9.0); + $fassert!((-9.0 as $fty).maximum($neginf), -9.0); + $fassert!(($nan as $fty).maximum(9.0).is_nan()); + $fassert!(($nan as $fty).maximum(-9.0).is_nan()); + $fassert!((9.0 as $fty).maximum($nan).is_nan()); + $fassert!((-9.0 as $fty).maximum($nan).is_nan()); + $fassert!(($nan as $fty).maximum($nan).is_nan()); } #[test] fn midpoint() { - assert_eq!((0.5 as $fty).midpoint(0.5), 0.5); - assert_eq!((0.5 as $fty).midpoint(2.5), 1.5); - assert_eq!((3.0 as $fty).midpoint(4.0), 3.5); - assert_eq!((-3.0 as $fty).midpoint(4.0), 0.5); - assert_eq!((3.0 as $fty).midpoint(-4.0), -0.5); - assert_eq!((-3.0 as $fty).midpoint(-4.0), -3.5); - assert_eq!((0.0 as $fty).midpoint(0.0), 0.0); - assert_eq!((-0.0 as $fty).midpoint(-0.0), -0.0); - assert_eq!((-5.0 as $fty).midpoint(5.0), 0.0); - assert_eq!(($max as $fty).midpoint($min), 0.0); - assert_eq!(($min as $fty).midpoint($max), -0.0); - assert_eq!(($max as $fty).midpoint($min_pos), $max / 2.); - assert_eq!((-$max as $fty).midpoint($min_pos), -$max / 2.); - assert_eq!(($max as $fty).midpoint(-$min_pos), $max / 2.); - assert_eq!((-$max as $fty).midpoint(-$min_pos), -$max / 2.); - assert_eq!(($min_pos as $fty).midpoint($max), $max / 2.); - assert_eq!(($min_pos as $fty).midpoint(-$max), -$max / 2.); - assert_eq!((-$min_pos as $fty).midpoint($max), $max / 2.); - assert_eq!((-$min_pos as $fty).midpoint(-$max), -$max / 2.); - assert_eq!(($max as $fty).midpoint($max), $max); - assert_eq!(($min_pos as $fty).midpoint($min_pos), $min_pos); - assert_eq!((-$min_pos as $fty).midpoint(-$min_pos), -$min_pos); - assert_eq!(($max as $fty).midpoint(5.0), $max / 2.0 + 2.5); - assert_eq!(($max as $fty).midpoint(-5.0), $max / 2.0 - 2.5); - assert_eq!(($inf as $fty).midpoint($inf), $inf); - assert_eq!(($neginf as $fty).midpoint($neginf), $neginf); - assert!(($nan as $fty).midpoint(1.0).is_nan()); - assert!((1.0 as $fty).midpoint($nan).is_nan()); - assert!(($nan as $fty).midpoint($nan).is_nan()); + $fassert!((0.5 as $fty).midpoint(0.5), 0.5); + $fassert!((0.5 as $fty).midpoint(2.5), 1.5); + $fassert!((3.0 as $fty).midpoint(4.0), 3.5); + $fassert!((-3.0 as $fty).midpoint(4.0), 0.5); + $fassert!((3.0 as $fty).midpoint(-4.0), -0.5); + $fassert!((-3.0 as $fty).midpoint(-4.0), -3.5); + $fassert!((0.0 as $fty).midpoint(0.0), 0.0); + $fassert!((-0.0 as $fty).midpoint(-0.0), -0.0); + $fassert!((-5.0 as $fty).midpoint(5.0), 0.0); + $fassert!(($max as $fty).midpoint($min), 0.0); + $fassert!(($min as $fty).midpoint($max), -0.0); + $fassert!(($max as $fty).midpoint($min_pos), $max / 2.); + $fassert!((-$max as $fty).midpoint($min_pos), -$max / 2.); + $fassert!(($max as $fty).midpoint(-$min_pos), $max / 2.); + $fassert!((-$max as $fty).midpoint(-$min_pos), -$max / 2.); + $fassert!(($min_pos as $fty).midpoint($max), $max / 2.); + $fassert!(($min_pos as $fty).midpoint(-$max), -$max / 2.); + $fassert!((-$min_pos as $fty).midpoint($max), $max / 2.); + $fassert!((-$min_pos as $fty).midpoint(-$max), -$max / 2.); + $fassert!(($max as $fty).midpoint($max), $max); + $fassert!(($min_pos as $fty).midpoint($min_pos), $min_pos); + $fassert!((-$min_pos as $fty).midpoint(-$min_pos), -$min_pos); + $fassert!(($max as $fty).midpoint(5.0), $max / 2.0 + 2.5); + $fassert!(($max as $fty).midpoint(-5.0), $max / 2.0 - 2.5); + $fassert!(($inf as $fty).midpoint($inf), $inf); + $fassert!(($neginf as $fty).midpoint($neginf), $neginf); + $fassert!(($nan as $fty).midpoint(1.0).is_nan()); + $fassert!((1.0 as $fty).midpoint($nan).is_nan()); + $fassert!(($nan as $fty).midpoint($nan).is_nan()); // test if large differences in magnitude are still correctly computed. // NOTE: that because of how small x and y are, x + y can never overflow @@ -907,19 +907,19 @@ macro_rules! test_float { } #[test] fn rem_euclid() { - let a: $fty = 42.0; - assert!($inf.rem_euclid(a).is_nan()); - assert_eq!(a.rem_euclid($inf), a); - assert!(a.rem_euclid($nan).is_nan()); + // FIXME: Use $fassert when rem_euclid becomes const + assert!($inf.rem_euclid((42.0 as $fty)).is_nan()); + assert_eq!((42.0 as $fty).rem_euclid($inf), (42.0 as $fty)); + assert!((42.0 as $fty).rem_euclid($nan).is_nan()); assert!($inf.rem_euclid($inf).is_nan()); assert!($inf.rem_euclid($nan).is_nan()); assert!($nan.rem_euclid($inf).is_nan()); } #[test] fn div_euclid() { - let a: $fty = 42.0; - assert_eq!(a.div_euclid($inf), 0.0); - assert!(a.div_euclid($nan).is_nan()); + // FIXME: Use $fassert when div_euclid becomes const + assert_eq!((42.0 as $fty).div_euclid($inf), 0.0); + assert!((42.0 as $fty).div_euclid($nan).is_nan()); assert!($inf.div_euclid($inf).is_nan()); assert!($inf.div_euclid($nan).is_nan()); assert!($nan.div_euclid($inf).is_nan()); @@ -928,8 +928,41 @@ macro_rules! test_float { }; } +// Custom assert macro that distribute between assert! and assert_eq! in a non-const context +macro_rules! float_assert { + ($b:expr) => { + assert!($b); + }; + ($left:expr, $right:expr) => { + assert_eq!($left, $right); + }; +} + +// Custom assert macro that only uses assert! in a const context +macro_rules! float_const_assert { + ($b:expr) => { + assert!(const { $b }); + }; + ($left:expr, $right:expr) => { + assert!(const { $left == $right }); + }; +} + test_float!( f32, + float_assert, + f32, + f32::INFINITY, + f32::NEG_INFINITY, + f32::NAN, + f32::MIN, + f32::MAX, + f32::MIN_POSITIVE, + f32::MAX_EXP +); +test_float!( + f32_const, + float_const_assert, f32, f32::INFINITY, f32::NEG_INFINITY, @@ -941,6 +974,19 @@ test_float!( ); test_float!( f64, + float_assert, + f64, + f64::INFINITY, + f64::NEG_INFINITY, + f64::NAN, + f64::MIN, + f64::MAX, + f64::MIN_POSITIVE, + f64::MAX_EXP +); +test_float!( + f64_const, + float_const_assert, f64, f64::INFINITY, f64::NEG_INFINITY, diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index d09eb97b17e06..2e35e8bf5342a 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -1,5 +1,6 @@ macro_rules! uint_module { ($T:ident) => { + use core::num::ParseIntError; use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; use core::$T::*; @@ -49,95 +50,95 @@ macro_rules! uint_module { fn test_leading_trailing_ones() { const A: $T = 0b0101_1111; - assert_eq_const_safe!(A.trailing_ones(), 5); - assert_eq_const_safe!((!A).leading_ones(), $T::BITS - 7); + assert_eq_const_safe!(u32: A.trailing_ones(), 5); + assert_eq_const_safe!(u32: (!A).leading_ones(), $T::BITS - 7); - assert_eq_const_safe!(A.reverse_bits().leading_ones(), 5); + assert_eq_const_safe!(u32: A.reverse_bits().leading_ones(), 5); - assert_eq_const_safe!(_1.leading_ones(), $T::BITS); - assert_eq_const_safe!(_1.trailing_ones(), $T::BITS); + assert_eq_const_safe!(u32: _1.leading_ones(), $T::BITS); + assert_eq_const_safe!(u32: _1.trailing_ones(), $T::BITS); - assert_eq_const_safe!((_1 << 1).trailing_ones(), 0); - assert_eq_const_safe!((_1 >> 1).leading_ones(), 0); + assert_eq_const_safe!(u32: (_1 << 1).trailing_ones(), 0); + assert_eq_const_safe!(u32: (_1 >> 1).leading_ones(), 0); - assert_eq_const_safe!((_1 << 1).leading_ones(), $T::BITS - 1); - assert_eq_const_safe!((_1 >> 1).trailing_ones(), $T::BITS - 1); + assert_eq_const_safe!(u32: (_1 << 1).leading_ones(), $T::BITS - 1); + assert_eq_const_safe!(u32: (_1 >> 1).trailing_ones(), $T::BITS - 1); - assert_eq_const_safe!(_0.leading_ones(), 0); - assert_eq_const_safe!(_0.trailing_ones(), 0); + assert_eq_const_safe!(u32: _0.leading_ones(), 0); + assert_eq_const_safe!(u32: _0.trailing_ones(), 0); const X: $T = 0b0010_1100; - assert_eq_const_safe!(X.leading_ones(), 0); - assert_eq_const_safe!(X.trailing_ones(), 0); + assert_eq_const_safe!(u32: X.leading_ones(), 0); + assert_eq_const_safe!(u32: X.trailing_ones(), 0); } fn test_rotate() { - assert_eq_const_safe!(A.rotate_left(6).rotate_right(2).rotate_right(4), A); - assert_eq_const_safe!(B.rotate_left(3).rotate_left(2).rotate_right(5), B); - assert_eq_const_safe!(C.rotate_left(6).rotate_right(2).rotate_right(4), C); + assert_eq_const_safe!($T: A.rotate_left(6).rotate_right(2).rotate_right(4), A); + assert_eq_const_safe!($T: B.rotate_left(3).rotate_left(2).rotate_right(5), B); + assert_eq_const_safe!($T: C.rotate_left(6).rotate_right(2).rotate_right(4), C); // Rotating these should make no difference // // We test using 124 bits because to ensure that overlong bit shifts do // not cause undefined behavior. See #10183. - assert_eq_const_safe!(_0.rotate_left(124), _0); - assert_eq_const_safe!(_1.rotate_left(124), _1); - assert_eq_const_safe!(_0.rotate_right(124), _0); - assert_eq_const_safe!(_1.rotate_right(124), _1); + assert_eq_const_safe!($T: _0.rotate_left(124), _0); + assert_eq_const_safe!($T: _1.rotate_left(124), _1); + assert_eq_const_safe!($T: _0.rotate_right(124), _0); + assert_eq_const_safe!($T: _1.rotate_right(124), _1); // Rotating by 0 should have no effect - assert_eq_const_safe!(A.rotate_left(0), A); - assert_eq_const_safe!(B.rotate_left(0), B); - assert_eq_const_safe!(C.rotate_left(0), C); + assert_eq_const_safe!($T: A.rotate_left(0), A); + assert_eq_const_safe!($T: B.rotate_left(0), B); + assert_eq_const_safe!($T: C.rotate_left(0), C); // Rotating by a multiple of word size should also have no effect - assert_eq_const_safe!(A.rotate_left(128), A); - assert_eq_const_safe!(B.rotate_left(128), B); - assert_eq_const_safe!(C.rotate_left(128), C); + assert_eq_const_safe!($T: A.rotate_left(128), A); + assert_eq_const_safe!($T: B.rotate_left(128), B); + assert_eq_const_safe!($T: C.rotate_left(128), C); } fn test_swap_bytes() { - assert_eq_const_safe!(A.swap_bytes().swap_bytes(), A); - assert_eq_const_safe!(B.swap_bytes().swap_bytes(), B); - assert_eq_const_safe!(C.swap_bytes().swap_bytes(), C); + assert_eq_const_safe!($T: A.swap_bytes().swap_bytes(), A); + assert_eq_const_safe!($T: B.swap_bytes().swap_bytes(), B); + assert_eq_const_safe!($T: C.swap_bytes().swap_bytes(), C); // Swapping these should make no difference - assert_eq_const_safe!(_0.swap_bytes(), _0); - assert_eq_const_safe!(_1.swap_bytes(), _1); + assert_eq_const_safe!($T: _0.swap_bytes(), _0); + assert_eq_const_safe!($T: _1.swap_bytes(), _1); } fn test_reverse_bits() { - assert_eq_const_safe!(A.reverse_bits().reverse_bits(), A); - assert_eq_const_safe!(B.reverse_bits().reverse_bits(), B); - assert_eq_const_safe!(C.reverse_bits().reverse_bits(), C); + assert_eq_const_safe!($T: A.reverse_bits().reverse_bits(), A); + assert_eq_const_safe!($T: B.reverse_bits().reverse_bits(), B); + assert_eq_const_safe!($T: C.reverse_bits().reverse_bits(), C); // Swapping these should make no difference - assert_eq_const_safe!(_0.reverse_bits(), _0); - assert_eq_const_safe!(_1.reverse_bits(), _1); + assert_eq_const_safe!($T: _0.reverse_bits(), _0); + assert_eq_const_safe!($T: _1.reverse_bits(), _1); } fn test_le() { - assert_eq_const_safe!($T::from_le(A.to_le()), A); - assert_eq_const_safe!($T::from_le(B.to_le()), B); - assert_eq_const_safe!($T::from_le(C.to_le()), C); - assert_eq_const_safe!($T::from_le(_0), _0); - assert_eq_const_safe!($T::from_le(_1), _1); - assert_eq_const_safe!(_0.to_le(), _0); - assert_eq_const_safe!(_1.to_le(), _1); + assert_eq_const_safe!($T: $T::from_le(A.to_le()), A); + assert_eq_const_safe!($T: $T::from_le(B.to_le()), B); + assert_eq_const_safe!($T: $T::from_le(C.to_le()), C); + assert_eq_const_safe!($T: $T::from_le(_0), _0); + assert_eq_const_safe!($T: $T::from_le(_1), _1); + assert_eq_const_safe!($T: _0.to_le(), _0); + assert_eq_const_safe!($T: _1.to_le(), _1); } fn test_be() { - assert_eq_const_safe!($T::from_be(A.to_be()), A); - assert_eq_const_safe!($T::from_be(B.to_be()), B); - assert_eq_const_safe!($T::from_be(C.to_be()), C); - assert_eq_const_safe!($T::from_be(_0), _0); - assert_eq_const_safe!($T::from_be(_1), _1); - assert_eq_const_safe!(_0.to_be(), _0); - assert_eq_const_safe!(_1.to_be(), _1); + assert_eq_const_safe!($T: $T::from_be(A.to_be()), A); + assert_eq_const_safe!($T: $T::from_be(B.to_be()), B); + assert_eq_const_safe!($T: $T::from_be(C.to_be()), C); + assert_eq_const_safe!($T: $T::from_be(_0), _0); + assert_eq_const_safe!($T: $T::from_be(_1), _1); + assert_eq_const_safe!($T: _0.to_be(), _0); + assert_eq_const_safe!($T: _1.to_be(), _1); } fn test_unsigned_checked_div() { - assert_eq_const_safe!((10 as $T).checked_div(2), Some(5)); - assert_eq_const_safe!((5 as $T).checked_div(0), None); + assert_eq_const_safe!(Option<$T>: (10 as $T).checked_div(2), Some(5)); + assert_eq_const_safe!(Option<$T>: (5 as $T).checked_div(0), None); } } @@ -194,12 +195,12 @@ macro_rules! uint_module { test_runtime_and_compiletime! { fn test_parse_bytes() { - assert_eq_const_safe!($T::from_str_radix("123", 10), Ok(123 as $T)); - assert_eq_const_safe!($T::from_str_radix("1001", 2), Ok(9 as $T)); - assert_eq_const_safe!($T::from_str_radix("123", 8), Ok(83 as $T)); - assert_eq_const_safe!(u16::from_str_radix("123", 16), Ok(291 as u16)); - assert_eq_const_safe!(u16::from_str_radix("ffff", 16), Ok(65535 as u16)); - assert_eq_const_safe!($T::from_str_radix("z", 36), Ok(35 as $T)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("123", 10), Ok(123 as $T)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("1001", 2), Ok(9 as $T)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("123", 8), Ok(83 as $T)); + assert_eq_const_safe!(Result: u16::from_str_radix("123", 16), Ok(291 as u16)); + assert_eq_const_safe!(Result: u16::from_str_radix("ffff", 16), Ok(65535 as u16)); + assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("z", 36), Ok(35 as $T)); assert!($T::from_str_radix("Z", 10).is_err()); assert!($T::from_str_radix("_", 2).is_err()); @@ -208,16 +209,16 @@ macro_rules! uint_module { fn test_pow() { { const R: $T = 2; - assert_eq_const_safe!(R.pow(2), 4 as $T); - assert_eq_const_safe!(R.pow(0), 1 as $T); - assert_eq_const_safe!(R.wrapping_pow(2), 4 as $T); - assert_eq_const_safe!(R.wrapping_pow(0), 1 as $T); - assert_eq_const_safe!(R.checked_pow(2), Some(4 as $T)); - assert_eq_const_safe!(R.checked_pow(0), Some(1 as $T)); - assert_eq_const_safe!(R.overflowing_pow(2), (4 as $T, false)); - assert_eq_const_safe!(R.overflowing_pow(0), (1 as $T, false)); - assert_eq_const_safe!(R.saturating_pow(2), 4 as $T); - assert_eq_const_safe!(R.saturating_pow(0), 1 as $T); + assert_eq_const_safe!($T: R.pow(2), 4 as $T); + assert_eq_const_safe!($T: R.pow(0), 1 as $T); + assert_eq_const_safe!($T: R.wrapping_pow(2), 4 as $T); + assert_eq_const_safe!($T: R.wrapping_pow(0), 1 as $T); + assert_eq_const_safe!(Option<$T>: R.checked_pow(2), Some(4 as $T)); + assert_eq_const_safe!(Option<$T>: R.checked_pow(0), Some(1 as $T)); + assert_eq_const_safe!(($T, bool): R.overflowing_pow(2), (4 as $T, false)); + assert_eq_const_safe!(($T, bool): R.overflowing_pow(0), (1 as $T, false)); + assert_eq_const_safe!($T: R.saturating_pow(2), 4 as $T); + assert_eq_const_safe!($T: R.saturating_pow(0), 1 as $T); } { @@ -226,20 +227,20 @@ macro_rules! uint_module { // 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_const_safe!(R.wrapping_pow(2), 1 as $T); - assert_eq_const_safe!(R.checked_pow(2), None); - assert_eq_const_safe!(R.overflowing_pow(2), (1 as $T, true)); - assert_eq_const_safe!(R.saturating_pow(2), MAX); + assert_eq_const_safe!($T: R.wrapping_pow(2), 1 as $T); + assert_eq_const_safe!(Option<$T>: R.checked_pow(2), None); + assert_eq_const_safe!(($T, bool): R.overflowing_pow(2), (1 as $T, true)); + assert_eq_const_safe!($T: R.saturating_pow(2), MAX); } } fn test_isqrt() { - assert_eq_const_safe!((0 as $T).isqrt(), 0 as $T); - assert_eq_const_safe!((1 as $T).isqrt(), 1 as $T); - assert_eq_const_safe!((2 as $T).isqrt(), 1 as $T); - assert_eq_const_safe!((99 as $T).isqrt(), 9 as $T); - assert_eq_const_safe!((100 as $T).isqrt(), 10 as $T); - assert_eq_const_safe!($T::MAX.isqrt(), (1 << ($T::BITS / 2)) - 1); + assert_eq_const_safe!($T: (0 as $T).isqrt(), 0 as $T); + assert_eq_const_safe!($T: (1 as $T).isqrt(), 1 as $T); + assert_eq_const_safe!($T: (2 as $T).isqrt(), 1 as $T); + assert_eq_const_safe!($T: (99 as $T).isqrt(), 9 as $T); + assert_eq_const_safe!($T: (100 as $T).isqrt(), 10 as $T); + assert_eq_const_safe!($T: $T::MAX.isqrt(), (1 << ($T::BITS / 2)) - 1); } } @@ -264,24 +265,24 @@ macro_rules! uint_module { test_runtime_and_compiletime! { fn test_div_floor() { - assert_eq_const_safe!((8 as $T).div_floor(3), 2); + assert_eq_const_safe!($T: (8 as $T).div_floor(3), 2); } fn test_div_ceil() { - assert_eq_const_safe!((8 as $T).div_ceil(3), 3); + assert_eq_const_safe!($T: (8 as $T).div_ceil(3), 3); } fn test_next_multiple_of() { - assert_eq_const_safe!((16 as $T).next_multiple_of(8), 16); - assert_eq_const_safe!((23 as $T).next_multiple_of(8), 24); - assert_eq_const_safe!(MAX.next_multiple_of(1), MAX); + assert_eq_const_safe!($T: (16 as $T).next_multiple_of(8), 16); + assert_eq_const_safe!($T: (23 as $T).next_multiple_of(8), 24); + assert_eq_const_safe!($T: MAX.next_multiple_of(1), MAX); } fn test_checked_next_multiple_of() { - assert_eq_const_safe!((16 as $T).checked_next_multiple_of(8), Some(16)); - assert_eq_const_safe!((23 as $T).checked_next_multiple_of(8), Some(24)); - assert_eq_const_safe!((1 as $T).checked_next_multiple_of(0), None); - assert_eq_const_safe!(MAX.checked_next_multiple_of(2), None); + assert_eq_const_safe!(Option<$T>: (16 as $T).checked_next_multiple_of(8), Some(16)); + assert_eq_const_safe!(Option<$T>: (23 as $T).checked_next_multiple_of(8), Some(24)); + assert_eq_const_safe!(Option<$T>: (1 as $T).checked_next_multiple_of(0), None); + assert_eq_const_safe!(Option<$T>: MAX.checked_next_multiple_of(2), None); } fn test_is_next_multiple_of() { @@ -292,63 +293,63 @@ macro_rules! uint_module { } fn test_carrying_add() { - assert_eq_const_safe!($T::MAX.carrying_add(1, false), (0, true)); - assert_eq_const_safe!($T::MAX.carrying_add(0, true), (0, true)); - assert_eq_const_safe!($T::MAX.carrying_add(1, true), (1, true)); + assert_eq_const_safe!(($T, bool): $T::MAX.carrying_add(1, false), (0, true)); + assert_eq_const_safe!(($T, bool): $T::MAX.carrying_add(0, true), (0, true)); + assert_eq_const_safe!(($T, bool): $T::MAX.carrying_add(1, true), (1, true)); - assert_eq_const_safe!($T::MIN.carrying_add($T::MAX, false), ($T::MAX, false)); - assert_eq_const_safe!($T::MIN.carrying_add(0, true), (1, false)); - assert_eq_const_safe!($T::MIN.carrying_add($T::MAX, true), (0, true)); + assert_eq_const_safe!(($T, bool): $T::MIN.carrying_add($T::MAX, false), ($T::MAX, false)); + assert_eq_const_safe!(($T, bool): $T::MIN.carrying_add(0, true), (1, false)); + assert_eq_const_safe!(($T, bool): $T::MIN.carrying_add($T::MAX, true), (0, true)); } fn test_borrowing_sub() { - assert_eq_const_safe!($T::MIN.borrowing_sub(1, false), ($T::MAX, true)); - assert_eq_const_safe!($T::MIN.borrowing_sub(0, true), ($T::MAX, true)); - assert_eq_const_safe!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true)); + assert_eq_const_safe!(($T, bool): $T::MIN.borrowing_sub(1, false), ($T::MAX, true)); + assert_eq_const_safe!(($T, bool): $T::MIN.borrowing_sub(0, true), ($T::MAX, true)); + assert_eq_const_safe!(($T, bool): $T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true)); - assert_eq_const_safe!($T::MAX.borrowing_sub($T::MAX, false), (0, false)); - assert_eq_const_safe!($T::MAX.borrowing_sub(0, true), ($T::MAX - 1, false)); - assert_eq_const_safe!($T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true)); + assert_eq_const_safe!(($T, bool): $T::MAX.borrowing_sub($T::MAX, false), (0, false)); + assert_eq_const_safe!(($T, bool): $T::MAX.borrowing_sub(0, true), ($T::MAX - 1, false)); + assert_eq_const_safe!(($T, bool): $T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true)); } fn test_widening_mul() { - assert_eq_const_safe!($T::MAX.widening_mul($T::MAX), (1, $T::MAX - 1)); + assert_eq_const_safe!(($T, $T): $T::MAX.widening_mul($T::MAX), (1, $T::MAX - 1)); } fn test_carrying_mul() { - assert_eq_const_safe!($T::MAX.carrying_mul($T::MAX, 0), (1, $T::MAX - 1)); - assert_eq_const_safe!($T::MAX.carrying_mul($T::MAX, $T::MAX), (0, $T::MAX)); + assert_eq_const_safe!(($T, $T): $T::MAX.carrying_mul($T::MAX, 0), (1, $T::MAX - 1)); + assert_eq_const_safe!(($T, $T): $T::MAX.carrying_mul($T::MAX, $T::MAX), (0, $T::MAX)); } fn test_carrying_mul_add() { - assert_eq_const_safe!($T::MAX.carrying_mul_add($T::MAX, 0, 0), (1, $T::MAX - 1)); - assert_eq_const_safe!($T::MAX.carrying_mul_add($T::MAX, $T::MAX, 0), (0, $T::MAX)); - assert_eq_const_safe!($T::MAX.carrying_mul_add($T::MAX, $T::MAX, $T::MAX), ($T::MAX, $T::MAX)); + assert_eq_const_safe!(($T, $T): $T::MAX.carrying_mul_add($T::MAX, 0, 0), (1, $T::MAX - 1)); + assert_eq_const_safe!(($T, $T): $T::MAX.carrying_mul_add($T::MAX, $T::MAX, 0), (0, $T::MAX)); + assert_eq_const_safe!(($T, $T): $T::MAX.carrying_mul_add($T::MAX, $T::MAX, $T::MAX), ($T::MAX, $T::MAX)); } fn test_midpoint() { - assert_eq_const_safe!(<$T>::midpoint(1, 3), 2); - assert_eq_const_safe!(<$T>::midpoint(3, 1), 2); - - assert_eq_const_safe!(<$T>::midpoint(0, 0), 0); - assert_eq_const_safe!(<$T>::midpoint(0, 2), 1); - assert_eq_const_safe!(<$T>::midpoint(2, 0), 1); - assert_eq_const_safe!(<$T>::midpoint(2, 2), 2); - - assert_eq_const_safe!(<$T>::midpoint(1, 4), 2); - assert_eq_const_safe!(<$T>::midpoint(4, 1), 2); - assert_eq_const_safe!(<$T>::midpoint(3, 4), 3); - assert_eq_const_safe!(<$T>::midpoint(4, 3), 3); - - assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2); - assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), (<$T>::MAX - <$T>::MIN) / 2); - assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN); - assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX); - - assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3); - assert_eq_const_safe!(<$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3); - assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, 6), (<$T>::MAX - <$T>::MIN) / 2 + 3); - assert_eq_const_safe!(<$T>::midpoint(6, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2 + 3); + assert_eq_const_safe!($T: <$T>::midpoint(1, 3), 2); + assert_eq_const_safe!($T: <$T>::midpoint(3, 1), 2); + + assert_eq_const_safe!($T: <$T>::midpoint(0, 0), 0); + assert_eq_const_safe!($T: <$T>::midpoint(0, 2), 1); + assert_eq_const_safe!($T: <$T>::midpoint(2, 0), 1); + assert_eq_const_safe!($T: <$T>::midpoint(2, 2), 2); + + assert_eq_const_safe!($T: <$T>::midpoint(1, 4), 2); + assert_eq_const_safe!($T: <$T>::midpoint(4, 1), 2); + assert_eq_const_safe!($T: <$T>::midpoint(3, 4), 3); + assert_eq_const_safe!($T: <$T>::midpoint(4, 3), 3); + + assert_eq_const_safe!($T: <$T>::midpoint(<$T>::MIN, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2); + assert_eq_const_safe!($T: <$T>::midpoint(<$T>::MAX, <$T>::MIN), (<$T>::MAX - <$T>::MIN) / 2); + assert_eq_const_safe!($T: <$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN); + assert_eq_const_safe!($T: <$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX); + + assert_eq_const_safe!($T: <$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3); + assert_eq_const_safe!($T: <$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3); + assert_eq_const_safe!($T: <$T>::midpoint(<$T>::MAX, 6), (<$T>::MAX - <$T>::MIN) / 2 + 3); + assert_eq_const_safe!($T: <$T>::midpoint(6, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2 + 3); } } @@ -365,154 +366,154 @@ macro_rules! uint_module { test_runtime_and_compiletime! { fn test_unbounded_shl() { // <$T>::MIN - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN << SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN << SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN << SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MIN << SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 1), (<$T>::MIN << 1)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 3), (<$T>::MIN << 3)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 5), (<$T>::MIN << 5)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN << SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN << SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN << SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MIN << SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, 1), (<$T>::MIN << 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, 3), (<$T>::MIN << 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, 5), (<$T>::MIN << 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW3), 0); // <$T>::MAX - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_ONE), (<$T>::MAX << SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_TWO), (<$T>::MAX << SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_THREE), (<$T>::MAX << SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MAX << SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, 1), (<$T>::MAX << 1)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, 3), (<$T>::MAX << 3)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, 5), (<$T>::MAX << 5)); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_ONE), (<$T>::MAX << SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_TWO), (<$T>::MAX << SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_THREE), (<$T>::MAX << SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MAX << SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, 1), (<$T>::MAX << 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, 3), (<$T>::MAX << 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, 5), (<$T>::MAX << 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW3), 0); // 1 - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_ONE), (1 << SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_TWO), (1 << SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_THREE), (1 << SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_FOUR), (1 << SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, 1), (1 << 1)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, 3), (1 << 3)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, 5), (1 << 5)); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_ONE), (1 << SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_TWO), (1 << SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_THREE), (1 << SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_FOUR), (1 << SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, 1), (1 << 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, 3), (1 << 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, 5), (1 << 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW3), 0); // !0 - assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_TEST_ONE), (!0 << SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_TEST_TWO), (!0 << SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_TEST_THREE), (!0 << SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_TEST_FOUR), (!0 << SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shl(!0, 1), (!0 << 1)); - assert_eq_const_safe!(<$T>::unbounded_shl(!0, 3), (!0 << 3)); - assert_eq_const_safe!(<$T>::unbounded_shl(!0, 5), (!0 << 5)); - assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(!0, SHIFT_AMOUNT_TEST_ONE), (!0 << SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(!0, SHIFT_AMOUNT_TEST_TWO), (!0 << SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(!0, SHIFT_AMOUNT_TEST_THREE), (!0 << SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(!0, SHIFT_AMOUNT_TEST_FOUR), (!0 << SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(!0, 1), (!0 << 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(!0, 3), (!0 << 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(!0, 5), (!0 << 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(!0, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(!0, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(!0, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(!0, SHIFT_AMOUNT_OVERFLOW3), 0); // 8 - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_ONE), (8 << SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_TWO), (8 << SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_THREE), (8 << SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_FOUR), (8 << SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, 1), (8 << 1)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, 3), (8 << 3)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, 5), (8 << 5)); - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_ONE), (8 << SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_TWO), (8 << SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_THREE), (8 << SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_FOUR), (8 << SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, 1), (8 << 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, 3), (8 << 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, 5), (8 << 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW3), 0); // 17 - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_ONE), (17 << SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_TWO), (17 << SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_THREE), (17 << SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_FOUR), (17 << SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, 1), (17 << 1)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, 3), (17 << 3)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, 5), (17 << 5)); - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_ONE), (17 << SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_TWO), (17 << SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_THREE), (17 << SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_FOUR), (17 << SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, 1), (17 << 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, 3), (17 << 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, 5), (17 << 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW3), 0); } fn test_unbounded_shr() { // <$T>::MIN - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN >> SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MIN >> SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 1), (<$T>::MIN >> 1)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 3), (<$T>::MIN >> 3)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 5), (<$T>::MIN >> 5)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN >> SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MIN >> SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, 1), (<$T>::MIN >> 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, 3), (<$T>::MIN >> 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, 5), (<$T>::MIN >> 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW3), 0); // <$T>::MAX - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_ONE), (<$T>::MAX >> SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_TWO), (<$T>::MAX >> SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_THREE), (<$T>::MAX >> SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MAX >> SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, 1), (<$T>::MAX >> 1)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, 3), (<$T>::MAX >> 3)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, 5), (<$T>::MAX >> 5)); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_ONE), (<$T>::MAX >> SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_TWO), (<$T>::MAX >> SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_THREE), (<$T>::MAX >> SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MAX >> SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, 1), (<$T>::MAX >> 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, 3), (<$T>::MAX >> 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, 5), (<$T>::MAX >> 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW3), 0); // 1 - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_ONE), (1 >> SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_TWO), (1 >> SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_THREE), (1 >> SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_FOUR), (1 >> SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, 1), (1 >> 1)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, 3), (1 >> 3)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, 5), (1 >> 5)); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_ONE), (1 >> SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_TWO), (1 >> SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_THREE), (1 >> SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_FOUR), (1 >> SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, 1), (1 >> 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, 3), (1 >> 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, 5), (1 >> 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW3), 0); // !0 - assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_TEST_ONE), (!0 >> SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_TEST_TWO), (!0 >> SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_TEST_THREE), (!0 >> SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_TEST_FOUR), (!0 >> SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shr(!0, 1), (!0 >> 1)); - assert_eq_const_safe!(<$T>::unbounded_shr(!0, 3), (!0 >> 3)); - assert_eq_const_safe!(<$T>::unbounded_shr(!0, 5), (!0 >> 5)); - assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(!0, SHIFT_AMOUNT_TEST_ONE), (!0 >> SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(!0, SHIFT_AMOUNT_TEST_TWO), (!0 >> SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(!0, SHIFT_AMOUNT_TEST_THREE), (!0 >> SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(!0, SHIFT_AMOUNT_TEST_FOUR), (!0 >> SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(!0, 1), (!0 >> 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(!0, 3), (!0 >> 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(!0, 5), (!0 >> 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(!0, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(!0, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(!0, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(!0, SHIFT_AMOUNT_OVERFLOW3), 0); // 8 - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_ONE), (8 >> SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_TWO), (8 >> SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_THREE), (8 >> SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_FOUR), (8 >> SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, 1), (8 >> 1)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, 3), (8 >> 3)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, 5), (8 >> 5)); - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_ONE), (8 >> SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_TWO), (8 >> SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_THREE), (8 >> SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_FOUR), (8 >> SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, 1), (8 >> 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, 3), (8 >> 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, 5), (8 >> 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW3), 0); // 17 - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_ONE), (17 >> SHIFT_AMOUNT_TEST_ONE)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_TWO), (17 >> SHIFT_AMOUNT_TEST_TWO)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_THREE), (17 >> SHIFT_AMOUNT_TEST_THREE)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_FOUR), (17 >> SHIFT_AMOUNT_TEST_FOUR)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, 1), (17 >> 1)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, 3), (17 >> 3)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, 5), (17 >> 5)); - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW2), 0); - assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW3), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_ONE), (17 >> SHIFT_AMOUNT_TEST_ONE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_TWO), (17 >> SHIFT_AMOUNT_TEST_TWO)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_THREE), (17 >> SHIFT_AMOUNT_TEST_THREE)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_FOUR), (17 >> SHIFT_AMOUNT_TEST_FOUR)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, 1), (17 >> 1)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, 3), (17 >> 3)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, 5), (17 >> 5)); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW2), 0); + assert_eq_const_safe!($T: <$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW3), 0); } } }; diff --git a/library/coretests/tests/pin_macro.rs b/library/coretests/tests/pin_macro.rs index 43542397a6136..bfbfa8d280fa1 100644 --- a/library/coretests/tests/pin_macro.rs +++ b/library/coretests/tests/pin_macro.rs @@ -30,3 +30,30 @@ fn unsize_coercion() { let dyn_obj: Pin<&mut dyn Send> = pin!([PhantomPinned; 2]); stuff(dyn_obj); } + +#[test] +fn rust_2024_expr() { + // Check that we accept a Rust 2024 $expr. + std::pin::pin!(const { 1 }); +} + +#[test] +fn temp_lifetime() { + // Check that temporary lifetimes work as in Rust 2021. + // Regression test for https://github.com/rust-lang/rust/issues/138596 + match std::pin::pin!(foo(&mut 0)) { + _ => {} + } + async fn foo(_: &mut usize) {} +} + +#[test] +fn transitive_extension() { + async fn temporary() {} + + // `pin!` witnessed in the wild being used like this, even if it yields + // a `Pin<&mut &mut impl Unpin>`; it does work because `pin!` + // happens to transitively extend the lifespan of `temporary()`. + let p = pin!(&mut temporary()); + let _use = p; +} diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index 0c9f9b338b0c1..bb60fb07468f9 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -1,6 +1,6 @@ use core::cell::RefCell; use core::marker::Freeze; -use core::mem::{self, MaybeUninit}; +use core::mem::MaybeUninit; use core::num::NonZero; use core::ptr; use core::ptr::*; @@ -388,7 +388,7 @@ fn align_offset_various_strides() { 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 { + if (numptr + el * size_of::()) % align == 0 { expected = el; break; } @@ -398,7 +398,7 @@ fn align_offset_various_strides() { eprintln!( "aligning {:p} (with stride of {}) to {}, expected {}, got {}", ptr, - ::std::mem::size_of::(), + size_of::(), align, expected, got @@ -525,31 +525,24 @@ fn ptr_metadata() { assert_eq!(metadata("foo"), 3_usize); assert_eq!(metadata(&[4, 7][..]), 2_usize); - let dst_tuple: &(bool, [u8]) = &(true, [0x66, 0x6F, 0x6F]); let dst_struct: &Pair = &Pair(true, [0x66, 0x6F, 0x6F]); - assert_eq!(metadata(dst_tuple), 3_usize); assert_eq!(metadata(dst_struct), 3_usize); unsafe { - let dst_tuple: &(bool, str) = std::mem::transmute(dst_tuple); let dst_struct: &Pair = std::mem::transmute(dst_struct); - assert_eq!(&dst_tuple.1, "foo"); assert_eq!(&dst_struct.1, "foo"); - assert_eq!(metadata(dst_tuple), 3_usize); assert_eq!(metadata(dst_struct), 3_usize); } let vtable_1: DynMetadata = metadata(&4_u16 as &dyn Debug); let vtable_2: DynMetadata = metadata(&4_u16 as &dyn Display); let vtable_3: DynMetadata = metadata(&4_u32 as &dyn Display); - let vtable_4: DynMetadata = metadata(&(true, 7_u32) as &(bool, dyn Display)); - let vtable_5: DynMetadata = + let vtable_4: DynMetadata = metadata(&Pair(true, 7_u32) as &Pair); unsafe { let address_1: *const () = std::mem::transmute(vtable_1); let address_2: *const () = std::mem::transmute(vtable_2); let address_3: *const () = std::mem::transmute(vtable_3); let address_4: *const () = std::mem::transmute(vtable_4); - let address_5: *const () = std::mem::transmute(vtable_5); // Different trait => different vtable pointer assert_ne!(address_1, address_2); // Different erased type => different vtable pointer @@ -558,7 +551,6 @@ fn ptr_metadata() { // This is *not guaranteed*, so we skip it in Miri. if !cfg!(miri) { assert_eq!(address_3, address_4); - assert_eq!(address_3, address_5); } } } @@ -613,9 +605,9 @@ fn dyn_metadata() { let meta = metadata(trait_object); assert_eq!(meta.size_of(), 64); - assert_eq!(meta.size_of(), std::mem::size_of::()); + assert_eq!(meta.size_of(), size_of::()); assert_eq!(meta.align_of(), 32); - assert_eq!(meta.align_of(), std::mem::align_of::()); + assert_eq!(meta.align_of(), align_of::()); assert_eq!(meta.layout(), std::alloc::Layout::new::()); assert!(format!("{meta:?}").starts_with("DynMetadata(0x")); @@ -789,7 +781,7 @@ fn nonnull_tagged_pointer_with_provenance() { impl TaggedPointer { /// The ABI-required minimum alignment of the `P` type. - pub const ALIGNMENT: usize = core::mem::align_of::(); + pub const ALIGNMENT: usize = align_of::(); /// A mask for data-carrying bits of the address. pub const DATA_MASK: usize = !Self::ADDRESS_MASK; /// Number of available bits of storage in the address. @@ -873,7 +865,7 @@ fn test_const_copy_ptr() { ptr::copy( &ptr1 as *const _ as *const MaybeUninit, &mut ptr2 as *mut _ as *mut MaybeUninit, - mem::size_of::<&i32>(), + size_of::<&i32>(), ); } @@ -891,7 +883,7 @@ fn test_const_copy_ptr() { ptr::copy_nonoverlapping( &ptr1 as *const _ as *const MaybeUninit, &mut ptr2 as *mut _ as *mut MaybeUninit, - mem::size_of::<&i32>(), + size_of::<&i32>(), ); } @@ -936,7 +928,7 @@ fn test_const_swap_ptr() { let mut s2 = A(S { ptr: &666, f1: 0, f2: [0; 3] }); // Swap ptr1 and ptr2, as an array. - type T = [u8; mem::size_of::
()]; + type T = [u8; size_of::()]; unsafe { ptr::swap(ptr::from_mut(&mut s1).cast::(), ptr::from_mut(&mut s2).cast::()); } @@ -957,6 +949,10 @@ fn test_const_swap_ptr() { // Make sure they still work. assert!(*s1.0.ptr == 1); assert!(*s2.0.ptr == 666); + + // This is where we'd swap again using a `u8` type and a `count` of `size_of::()` if it + // were not for the limitation of `swap_nonoverlapping` around pointers crossing multiple + // elements. }; } @@ -992,3 +988,56 @@ fn test_ptr_metadata_in_const() { assert_eq!(SLICE_META, 3); assert_eq!(DYN_META.size_of(), 42); } + +// See +const fn ptr_swap_nonoverlapping_is_untyped_inner() { + #[repr(C)] + struct HasPadding(usize, u8); + + let buf1: [usize; 2] = [1000, 2000]; + let buf2: [usize; 2] = [3000, 4000]; + + // HasPadding and [usize; 2] have the same size and alignment, + // so swap_nonoverlapping should treat them the same + assert!(size_of::() == size_of::<[usize; 2]>()); + assert!(align_of::() == align_of::<[usize; 2]>()); + + let mut b1 = buf1; + let mut b2 = buf2; + // Safety: b1 and b2 are distinct local variables, + // with the same size and alignment as HasPadding. + unsafe { + std::ptr::swap_nonoverlapping( + b1.as_mut_ptr().cast::(), + b2.as_mut_ptr().cast::(), + 1, + ); + } + assert!(b1[0] == buf2[0]); + assert!(b1[1] == buf2[1]); + assert!(b2[0] == buf1[0]); + assert!(b2[1] == buf1[1]); +} + +#[test] +fn test_ptr_swap_nonoverlapping_is_untyped() { + ptr_swap_nonoverlapping_is_untyped_inner(); + const { ptr_swap_nonoverlapping_is_untyped_inner() }; +} + +#[test] +fn test_ptr_default() { + #[derive(Default)] + struct PtrDefaultTest { + ptr: *const u64, + } + let default = PtrDefaultTest::default(); + assert!(default.ptr.is_null()); + + #[derive(Default)] + struct PtrMutDefaultTest { + ptr: *mut u64, + } + let default = PtrMutDefaultTest::default(); + assert!(default.ptr.is_null()); +} diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index 1c5c8a9ebf258..d17e681480c70 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -5,8 +5,6 @@ use core::num::NonZero; use core::ops::{Range, RangeInclusive}; use core::slice; -use rand::seq::IndexedRandom; - #[test] fn test_position() { let b = [1, 2, 3, 5, 5]; @@ -1810,6 +1808,7 @@ fn select_nth_unstable() { use core::cmp::Ordering::{Equal, Greater, Less}; use rand::Rng; + use rand::seq::IndexedRandom; let mut rng = crate::test_rng(); @@ -2058,15 +2057,13 @@ fn test_align_to_non_trivial() { #[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); + assert_eq!(mid.as_ptr() as usize % align_of::(), 0); } } diff --git a/library/coretests/tests/str.rs b/library/coretests/tests/str.rs index f5066343af20a..5e23e910f0aeb 100644 --- a/library/coretests/tests/str.rs +++ b/library/coretests/tests/str.rs @@ -1 +1 @@ -// All `str` tests live in library/alloc/tests/str.rs +// All `str` tests live in library/alloctests/tests/str.rs diff --git a/library/coretests/tests/time.rs b/library/coretests/tests/time.rs index fe7bb11c67589..bb98e59bf5a2b 100644 --- a/library/coretests/tests/time.rs +++ b/library/coretests/tests/time.rs @@ -46,16 +46,25 @@ fn from_weeks_overflow() { } #[test] -fn constructors() { +fn constructor_weeks() { assert_eq!(Duration::from_weeks(1), Duration::from_secs(7 * 24 * 60 * 60)); assert_eq!(Duration::from_weeks(0), Duration::ZERO); +} +#[test] +fn constructor_days() { assert_eq!(Duration::from_days(1), Duration::from_secs(86_400)); assert_eq!(Duration::from_days(0), Duration::ZERO); +} +#[test] +fn constructor_hours() { assert_eq!(Duration::from_hours(1), Duration::from_secs(3_600)); assert_eq!(Duration::from_hours(0), Duration::ZERO); +} +#[test] +fn constructor_minutes() { assert_eq!(Duration::from_mins(1), Duration::from_secs(60)); assert_eq!(Duration::from_mins(0), Duration::ZERO); } diff --git a/library/panic_abort/Cargo.toml b/library/panic_abort/Cargo.toml index a9d1f53761cbf..d7d169671f010 100644 --- a/library/panic_abort/Cargo.toml +++ b/library/panic_abort/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" description = "Implementation of Rust panics via process aborts" -edition = "2021" +edition = "2024" [lib] test = false @@ -12,10 +12,11 @@ bench = false doc = false [dependencies] -alloc = { path = "../alloc" } -cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } core = { path = "../core" } compiler_builtins = "0.1.0" -[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] +[target.'cfg(target_os = "android")'.dependencies] libc = { version = "0.2", default-features = false } + +[target.'cfg(any(target_os = "android", target_os = "zkvm"))'.dependencies] +alloc = { path = "../alloc" } diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index b2ad0f4ac3d04..d1706b6525295 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -7,15 +7,11 @@ #![unstable(feature = "panic_abort", issue = "32837")] #![doc(issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] #![panic_runtime] -#![allow(unused_features)] -#![feature(asm_experimental_arch)] -#![feature(core_intrinsics)] #![feature(panic_runtime)] #![feature(std_internals)] #![feature(staged_api)] #![feature(rustc_attrs)] #![allow(internal_features)] -#![deny(unsafe_op_in_unsafe_fn)] #[cfg(target_os = "android")] mod android; @@ -45,75 +41,13 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { zkvm::zkvm_set_abort_message(_payload); } - unsafe { - abort(); + unsafe extern "Rust" { + // This is defined in std::rt. + #[rustc_std_internal_symbol] + safe fn __rust_abort() -> !; } - cfg_if::cfg_if! { - if #[cfg(any(unix, target_os = "solid_asp3"))] { - unsafe fn abort() -> ! { - unsafe { libc::abort(); } - } - } else if #[cfg(any(target_os = "hermit", - all(target_vendor = "fortanix", target_env = "sgx"), - target_os = "xous", - target_os = "uefi", - ))] { - unsafe fn abort() -> ! { - // call std::sys::abort_internal - unsafe extern "C" { - pub fn __rust_abort() -> !; - } - unsafe { __rust_abort(); } - } - } else if #[cfg(all(windows, not(miri)))] { - // 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 std's `abort_internal` - unsafe fn abort() -> ! { - #[allow(unused)] - const FAST_FAIL_FATAL_APP_EXIT: usize = 7; - cfg_if::cfg_if! { - if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { - unsafe { - core::arch::asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } - } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] { - unsafe { - core::arch::asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } - } else if #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] { - unsafe { - core::arch::asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } - } else { - core::intrinsics::abort(); - } - } - } - } else if #[cfg(target_os = "teeos")] { - mod teeos { - unsafe extern "C" { - pub fn TEE_Panic(code: u32) -> !; - } - } - - unsafe fn abort() -> ! { - unsafe { teeos::TEE_Panic(1); } - } - } else { - unsafe fn abort() -> ! { - core::intrinsics::abort(); - } - } - } + __rust_abort() } // This... is a bit of an oddity. The tl;dr; is that this is required to link diff --git a/library/panic_unwind/Cargo.toml b/library/panic_unwind/Cargo.toml index c2abb79192e9f..d176434e06ba1 100644 --- a/library/panic_unwind/Cargo.toml +++ b/library/panic_unwind/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" description = "Implementation of Rust panics via stack unwinding" -edition = "2021" +edition = "2024" [lib] test = false diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs index 1569c26c9de47..bad795a019c9a 100644 --- a/library/panic_unwind/src/emcc.rs +++ b/library/panic_unwind/src/emcc.rs @@ -9,7 +9,7 @@ use alloc::boxed::Box; use core::any::Any; use core::sync::atomic::{AtomicBool, Ordering}; -use core::{intrinsics, mem, ptr}; +use core::{intrinsics, ptr}; use unwind as uw; @@ -97,7 +97,7 @@ pub(crate) unsafe fn cleanup(ptr: *mut u8) -> Box { pub(crate) unsafe fn panic(data: Box) -> u32 { unsafe { - let exception = __cxa_allocate_exception(mem::size_of::()) as *mut Exception; + let exception = __cxa_allocate_exception(size_of::()) as *mut Exception; if exception.is_null() { return uw::_URC_FATAL_PHASE1_ERROR as u32; } diff --git a/library/panic_unwind/src/hermit.rs b/library/panic_unwind/src/hermit.rs index 8f4562d07fc4e..b36d1a019fddb 100644 --- a/library/panic_unwind/src/hermit.rs +++ b/library/panic_unwind/src/hermit.rs @@ -5,20 +5,16 @@ use alloc::boxed::Box; use core::any::Any; +unsafe extern "Rust" { + // This is defined in std::rt + #[rustc_std_internal_symbol] + safe fn __rust_abort() -> !; +} + pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box { - unsafe extern "C" { - fn __rust_abort() -> !; - } - unsafe { - __rust_abort(); - } + __rust_abort() } pub(crate) unsafe fn panic(_data: Box) -> u32 { - unsafe extern "C" { - fn __rust_abort() -> !; - } - unsafe { - __rust_abort(); - } + __rust_abort() } diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index a284633ea2fc7..50bd933aca204 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -79,9 +79,11 @@ cfg_if::cfg_if! { unsafe extern "C" { /// Handler in std called when a panic object is dropped outside of /// `catch_unwind`. + #[rustc_std_internal_symbol] fn __rust_drop_panic() -> !; /// Handler in std called when a foreign exception is caught. + #[rustc_std_internal_symbol] fn __rust_foreign_exception() -> !; } diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index 3a95b940221c2..3794b56c0898f 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -49,7 +49,7 @@ use alloc::boxed::Box; use core::any::Any; use core::ffi::{c_int, c_uint, c_void}; -use core::mem::{self, ManuallyDrop}; +use core::mem::ManuallyDrop; // NOTE(nbdd0121): The `canary` field is part of stable ABI. #[repr(C)] @@ -225,7 +225,7 @@ static mut CATCHABLE_TYPE: _CatchableType = _CatchableType { properties: 0, pType: ptr_t::null(), thisDisplacement: _PMD { mdisp: 0, pdisp: -1, vdisp: 0 }, - sizeOrOffset: mem::size_of::() as c_int, + sizeOrOffset: size_of::() as c_int, copyFunction: ptr_t::null(), }; diff --git a/library/portable-simd/beginners-guide.md b/library/portable-simd/beginners-guide.md index 17ade06ae80f9..dc08d847ced50 100644 --- a/library/portable-simd/beginners-guide.md +++ b/library/portable-simd/beginners-guide.md @@ -80,12 +80,12 @@ Most of the portable SIMD API is designed to allow the user to gloss over the de Fortunately, most SIMD types have a fairly predictable size. `i32x4` is bit-equivalent to `[i32; 4]` and so can be bitcast to it, e.g. using [`mem::transmute`], though the API usually offers a safe cast you can use instead. -However, this is not the same as alignment. Computer architectures generally prefer aligned accesses, especially when moving data between memory and vector registers, and while some support specialized operations that can bend the rules to help with this, unaligned access is still typically slow, or even undefined behavior. In addition, different architectures can require different alignments when interacting with their native SIMD types. For this reason, any `#[repr(simd)]` type has a non-portable alignment. If it is necessary to directly interact with the alignment of these types, it should be via [`mem::align_of`]. +However, this is not the same as alignment. Computer architectures generally prefer aligned accesses, especially when moving data between memory and vector registers, and while some support specialized operations that can bend the rules to help with this, unaligned access is still typically slow, or even undefined behavior. In addition, different architectures can require different alignments when interacting with their native SIMD types. For this reason, any `#[repr(simd)]` type has a non-portable alignment. If it is necessary to directly interact with the alignment of these types, it should be via [`align_of`]. When working with slices, data correctly aligned for SIMD can be acquired using the [`as_simd`] and [`as_simd_mut`] methods of the slice primitive. [`mem::transmute`]: https://doc.rust-lang.org/core/mem/fn.transmute.html -[`mem::align_of`]: https://doc.rust-lang.org/core/mem/fn.align_of.html +[`align_of`]: https://doc.rust-lang.org/core/mem/fn.align_of.html [`as_simd`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.as_simd [`as_simd_mut`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.as_simd_mut diff --git a/library/portable-simd/crates/core_simd/Cargo.toml b/library/portable-simd/crates/core_simd/Cargo.toml index a7a6d43b11d3c..537ce459c07cd 100644 --- a/library/portable-simd/crates/core_simd/Cargo.toml +++ b/library/portable-simd/crates/core_simd/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "core_simd" version = "0.1.0" -edition = "2021" +edition = "2024" homepage = "https://github.com/rust-lang/portable-simd" repository = "https://github.com/rust-lang/portable-simd" keywords = ["core", "simd", "intrinsics"] diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs index 7f57847c9c234..717b882b64ba1 100644 --- a/library/portable-simd/crates/core_simd/src/lib.rs +++ b/library/portable-simd/crates/core_simd/src/lib.rs @@ -35,7 +35,11 @@ feature(stdarch_x86_avx512) )] #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really -#![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)] +#![deny( + unsafe_op_in_unsafe_fn, + unreachable_pub, + clippy::undocumented_unsafe_blocks +)] #![doc(test(attr(deny(warnings))))] #![allow(internal_features)] #![unstable(feature = "portable_simd", issue = "86656")] diff --git a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs b/library/portable-simd/crates/core_simd/src/masks/bitmask.rs index db4312d5bf88a..8221d8f17e90e 100644 --- a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs +++ b/library/portable-simd/crates/core_simd/src/masks/bitmask.rs @@ -5,7 +5,7 @@ use core::marker::PhantomData; /// A mask where each lane is represented by a single bit. #[repr(transparent)] -pub struct Mask( +pub(crate) struct Mask( as SupportedLaneCount>::BitMask, PhantomData, ) @@ -78,7 +78,7 @@ where { #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn splat(value: bool) -> Self { + pub(crate) fn splat(value: bool) -> Self { let mut mask = as SupportedLaneCount>::BitMask::default(); if value { mask.as_mut().fill(u8::MAX) @@ -93,12 +93,12 @@ where #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub unsafe fn test_unchecked(&self, lane: usize) -> bool { + pub(crate) unsafe fn test_unchecked(&self, lane: usize) -> bool { (self.0.as_ref()[lane / 8] >> (lane % 8)) & 0x1 > 0 } #[inline] - pub unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { + pub(crate) unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { unsafe { self.0.as_mut()[lane / 8] ^= ((value ^ self.test_unchecked(lane)) as u8) << (lane % 8) } @@ -106,7 +106,7 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_int(self) -> Simd { + pub(crate) fn to_int(self) -> Simd { unsafe { core::intrinsics::simd::simd_select_bitmask( self.0, @@ -118,19 +118,19 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub unsafe fn from_int_unchecked(value: Simd) -> Self { + pub(crate) unsafe fn from_int_unchecked(value: Simd) -> Self { unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) } } #[inline] - pub fn to_bitmask_integer(self) -> u64 { + pub(crate) fn to_bitmask_integer(self) -> u64 { let mut bitmask = [0u8; 8]; bitmask[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref()); u64::from_ne_bytes(bitmask) } #[inline] - pub fn from_bitmask_integer(bitmask: u64) -> Self { + pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self { let mut bytes = as SupportedLaneCount>::BitMask::default(); let len = bytes.as_mut().len(); bytes @@ -141,7 +141,7 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn convert(self) -> Mask + pub(crate) fn convert(self) -> Mask where U: MaskElement, { @@ -151,13 +151,13 @@ where #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub fn any(self) -> bool { + pub(crate) fn any(self) -> bool { self != Self::splat(false) } #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub fn all(self) -> bool { + pub(crate) fn all(self) -> bool { self == Self::splat(true) } } diff --git a/library/portable-simd/crates/core_simd/src/masks/full_masks.rs b/library/portable-simd/crates/core_simd/src/masks/full_masks.rs index 387b508c4b4ef..4e98db4070a9d 100644 --- a/library/portable-simd/crates/core_simd/src/masks/full_masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks/full_masks.rs @@ -3,7 +3,7 @@ use crate::simd::{LaneCount, MaskElement, Simd, SupportedLaneCount}; #[repr(transparent)] -pub struct Mask(Simd) +pub(crate) struct Mask(Simd) where T: MaskElement, LaneCount: SupportedLaneCount; @@ -80,7 +80,7 @@ macro_rules! impl_reverse_bits { #[inline(always)] fn reverse_bits(self, n: usize) -> Self { let rev = <$int>::reverse_bits(self); - let bitsize = core::mem::size_of::<$int>() * 8; + let bitsize = size_of::<$int>() * 8; if n < bitsize { // Shift things back to the right rev >> (bitsize - n) @@ -102,36 +102,36 @@ where { #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn splat(value: bool) -> Self { + pub(crate) fn splat(value: bool) -> Self { Self(Simd::splat(if value { T::TRUE } else { T::FALSE })) } #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub unsafe fn test_unchecked(&self, lane: usize) -> bool { + pub(crate) unsafe fn test_unchecked(&self, lane: usize) -> bool { T::eq(self.0[lane], T::TRUE) } #[inline] - pub unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { + pub(crate) unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { self.0[lane] = if value { T::TRUE } else { T::FALSE } } #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_int(self) -> Simd { + pub(crate) fn to_int(self) -> Simd { self.0 } #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub unsafe fn from_int_unchecked(value: Simd) -> Self { + pub(crate) unsafe fn from_int_unchecked(value: Simd) -> Self { Self(value) } #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn convert(self) -> Mask + pub(crate) fn convert(self) -> Mask where U: MaskElement, { @@ -220,14 +220,14 @@ where #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub fn any(self) -> bool { + pub(crate) fn any(self) -> bool { // Safety: use `self` as an integer vector unsafe { core::intrinsics::simd::simd_reduce_any(self.to_int()) } } #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub fn all(self) -> bool { + pub(crate) fn all(self) -> bool { // Safety: use `self` as an integer vector unsafe { core::intrinsics::simd::simd_reduce_all(self.to_int()) } } diff --git a/library/portable-simd/crates/core_simd/src/ops.rs b/library/portable-simd/crates/core_simd/src/ops.rs index 4ac64a253a3bd..f36e8d01a73bb 100644 --- a/library/portable-simd/crates/core_simd/src/ops.rs +++ b/library/portable-simd/crates/core_simd/src/ops.rs @@ -1,4 +1,4 @@ -use crate::simd::{cmp::SimdPartialEq, LaneCount, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount, cmp::SimdPartialEq}; use core::ops::{Add, Mul}; use core::ops::{BitAnd, BitOr, BitXor}; use core::ops::{Div, Rem, Sub}; diff --git a/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs b/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs index 93989ce91b89d..2312ba401fa78 100644 --- a/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs +++ b/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs @@ -1,6 +1,6 @@ use crate::simd::{ - ptr::{SimdConstPtr, SimdMutPtr}, LaneCount, Mask, Simd, SimdElement, SupportedLaneCount, + ptr::{SimdConstPtr, SimdMutPtr}, }; /// Parallel `PartialEq`. diff --git a/library/portable-simd/crates/core_simd/src/simd/cmp/ord.rs b/library/portable-simd/crates/core_simd/src/simd/cmp/ord.rs index 899f00a831641..e813e7613032c 100644 --- a/library/portable-simd/crates/core_simd/src/simd/cmp/ord.rs +++ b/library/portable-simd/crates/core_simd/src/simd/cmp/ord.rs @@ -1,7 +1,7 @@ use crate::simd::{ + LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, ptr::{SimdConstPtr, SimdMutPtr}, - LaneCount, Mask, Simd, SupportedLaneCount, }; /// Parallel `PartialOrd`. diff --git a/library/portable-simd/crates/core_simd/src/simd/num/float.rs b/library/portable-simd/crates/core_simd/src/simd/num/float.rs index db705dfe20221..b5972c47373bb 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/float.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/float.rs @@ -1,7 +1,7 @@ use super::sealed::Sealed; use crate::simd::{ - cmp::{SimdPartialEq, SimdPartialOrd}, LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, + cmp::{SimdPartialEq, SimdPartialOrd}, }; /// Operations on SIMD vectors of floats. @@ -263,7 +263,8 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_as(self) } } - // https://github.com/llvm/llvm-project/issues/94694 + // workaround for https://github.com/llvm/llvm-project/issues/94694 (fixed in LLVM 20) + // tracked in: https://github.com/rust-lang/rust/issues/135982 #[cfg(target_arch = "aarch64")] #[inline] fn cast(self) -> Self::Cast @@ -302,14 +303,14 @@ macro_rules! impl_trait { #[inline] fn to_bits(self) -> Simd<$bits_ty, N> { - assert_eq!(core::mem::size_of::(), core::mem::size_of::()); + assert_eq!(size_of::(), size_of::()); // Safety: transmuting between vector types is safe unsafe { core::mem::transmute_copy(&self) } } #[inline] fn from_bits(bits: Simd<$bits_ty, N>) -> Self { - assert_eq!(core::mem::size_of::(), core::mem::size_of::()); + assert_eq!(size_of::(), size_of::()); // Safety: transmuting between vector types is safe unsafe { core::mem::transmute_copy(&bits) } } diff --git a/library/portable-simd/crates/core_simd/src/simd/num/int.rs b/library/portable-simd/crates/core_simd/src/simd/num/int.rs index 3a51235ff954e..d25050c3e4b47 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/int.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/int.rs @@ -1,7 +1,7 @@ use super::sealed::Sealed; use crate::simd::{ - cmp::SimdOrd, cmp::SimdPartialOrd, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, - SupportedLaneCount, + LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd, + cmp::SimdPartialOrd, num::SimdUint, }; /// Operations on SIMD vectors of signed integers. diff --git a/library/portable-simd/crates/core_simd/src/simd/num/uint.rs b/library/portable-simd/crates/core_simd/src/simd/num/uint.rs index 1ab2d8c7b7316..45d978068b664 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/uint.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/uint.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{cmp::SimdOrd, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; +use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd}; /// Operations on SIMD vectors of unsigned integers. pub trait SimdUint: Copy + Sealed { diff --git a/library/portable-simd/crates/core_simd/src/simd/prelude.rs b/library/portable-simd/crates/core_simd/src/simd/prelude.rs index 4b7c744c01326..e5d7a2aeb73df 100644 --- a/library/portable-simd/crates/core_simd/src/simd/prelude.rs +++ b/library/portable-simd/crates/core_simd/src/simd/prelude.rs @@ -7,10 +7,11 @@ #[doc(no_inline)] pub use super::{ + Mask, Simd, cmp::{SimdOrd, SimdPartialEq, SimdPartialOrd}, num::{SimdFloat, SimdInt, SimdUint}, ptr::{SimdConstPtr, SimdMutPtr}, - simd_swizzle, Mask, Simd, + simd_swizzle, }; #[rustfmt::skip] diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs index 47383809ffbae..36452e7ae920d 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{cmp::SimdPartialEq, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount}; +use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, num::SimdUint}; /// Operations on SIMD vectors of constant pointers. pub trait SimdConstPtr: Copy + Sealed { diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs index 3f20eef21a312..c644f390c20a5 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{cmp::SimdPartialEq, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount}; +use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, num::SimdUint}; /// Operations on SIMD vectors of mutable pointers. pub trait SimdMutPtr: Copy + Sealed { diff --git a/library/portable-simd/crates/core_simd/src/swizzle.rs b/library/portable-simd/crates/core_simd/src/swizzle.rs index 42425ef37e50b..dbdd6ef40eba7 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle.rs @@ -214,6 +214,17 @@ where /// Rotates the vector such that the first `OFFSET` elements of the slice move to the end /// while the last `self.len() - OFFSET` elements move to the front. After calling `rotate_elements_left`, /// the element previously at index `OFFSET` will become the first element in the slice. + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; + /// let a = Simd::from_array([0, 1, 2, 3]); + /// let x = a.rotate_elements_left::<3>(); + /// assert_eq!(x.to_array(), [3, 0, 1, 2]); + /// + /// let y = a.rotate_elements_left::<7>(); + /// assert_eq!(y.to_array(), [3, 0, 1, 2]); + /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn rotate_elements_left(self) -> Self { @@ -238,6 +249,17 @@ where /// Rotates the vector such that the first `self.len() - OFFSET` elements of the vector move to /// the end while the last `OFFSET` elements move to the front. After calling `rotate_elements_right`, /// the element previously at index `self.len() - OFFSET` will become the first element in the slice. + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; + /// let a = Simd::from_array([0, 1, 2, 3]); + /// let x = a.rotate_elements_right::<3>(); + /// assert_eq!(x.to_array(), [1, 2, 3, 0]); + /// + /// let y = a.rotate_elements_right::<7>(); + /// assert_eq!(y.to_array(), [1, 2, 3, 0]); + /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn rotate_elements_right(self) -> Self { @@ -261,6 +283,17 @@ where /// Shifts the vector elements to the left by `OFFSET`, filling in with /// `padding` from the right. + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; + /// let a = Simd::from_array([0, 1, 2, 3]); + /// let x = a.shift_elements_left::<3>(255); + /// assert_eq!(x.to_array(), [3, 255, 255, 255]); + /// + /// let y = a.shift_elements_left::<7>(255); + /// assert_eq!(y.to_array(), [255, 255, 255, 255]); + /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn shift_elements_left(self, padding: T) -> Self { @@ -283,6 +316,17 @@ where /// Shifts the vector elements to the right by `OFFSET`, filling in with /// `padding` from the left. + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; + /// let a = Simd::from_array([0, 1, 2, 3]); + /// let x = a.shift_elements_right::<3>(255); + /// assert_eq!(x.to_array(), [255, 255, 255, 0]); + /// + /// let y = a.shift_elements_right::<7>(255); + /// assert_eq!(y.to_array(), [255, 255, 255, 255]); + /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn shift_elements_right(self, padding: T) -> Self { diff --git a/library/portable-simd/crates/core_simd/src/to_bytes.rs b/library/portable-simd/crates/core_simd/src/to_bytes.rs index 4833ea9e11362..fee2cc06c5b09 100644 --- a/library/portable-simd/crates/core_simd/src/to_bytes.rs +++ b/library/portable-simd/crates/core_simd/src/to_bytes.rs @@ -1,6 +1,6 @@ use crate::simd::{ - num::{SimdFloat, SimdInt, SimdUint}, LaneCount, Simd, SimdElement, SupportedLaneCount, + num::{SimdFloat, SimdInt, SimdUint}, }; mod sealed { diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index 9c4dd36c24fe8..d76a6cd52bfc5 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -1,8 +1,8 @@ use crate::simd::{ + LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, cmp::SimdPartialOrd, num::SimdUint, ptr::{SimdConstPtr, SimdMutPtr}, - LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, }; /// A SIMD vector with the shape of `[T; N]` but the operations of `T`. @@ -83,7 +83,7 @@ use crate::simd::{ /// converting `[T]` to `[Simd]`, and allows soundly operating on an aligned SIMD body, /// but it may cost more time when handling the scalar head and tail. /// If these are not enough, it is most ideal to design data structures to be already aligned -/// to `mem::align_of::>()` before using `unsafe` Rust to read or write. +/// to `align_of::>()` before using `unsafe` Rust to read or write. /// Other ways to compensate for these facts, like materializing `Simd` to or from an array first, /// are handled by safe methods like [`Simd::from_array`] and [`Simd::from_slice`]. /// diff --git a/library/portable-simd/crates/core_simd/tests/layout.rs b/library/portable-simd/crates/core_simd/tests/layout.rs index 24114c2d261e7..3b4666249b0d7 100644 --- a/library/portable-simd/crates/core_simd/tests/layout.rs +++ b/library/portable-simd/crates/core_simd/tests/layout.rs @@ -7,8 +7,8 @@ macro_rules! layout_tests { test_helpers::test_lanes! { fn no_padding() { assert_eq!( - core::mem::size_of::>(), - core::mem::size_of::<[$ty; LANES]>(), + size_of::>(), + size_of::<[$ty; LANES]>(), ); } } diff --git a/library/portable-simd/crates/core_simd/tests/pointers.rs b/library/portable-simd/crates/core_simd/tests/pointers.rs index d7db4e82b3ca2..6e74c2d18b1ed 100644 --- a/library/portable-simd/crates/core_simd/tests/pointers.rs +++ b/library/portable-simd/crates/core_simd/tests/pointers.rs @@ -1,8 +1,8 @@ #![feature(portable_simd)] use core_simd::simd::{ - ptr::{SimdConstPtr, SimdMutPtr}, Simd, + ptr::{SimdConstPtr, SimdMutPtr}, }; macro_rules! common_tests { diff --git a/library/portable-simd/crates/core_simd/tests/round.rs b/library/portable-simd/crates/core_simd/tests/round.rs index 847766ec41ed2..4c1ac3c36f894 100644 --- a/library/portable-simd/crates/core_simd/tests/round.rs +++ b/library/portable-simd/crates/core_simd/tests/round.rs @@ -58,7 +58,7 @@ macro_rules! float_rounding_test { // all of the mantissa digits set to 1, pushed up to the MSB. const ALL_MANTISSA_BITS: IntScalar = ((1 << ::MANTISSA_DIGITS) - 1); const MAX_REPRESENTABLE_VALUE: Scalar = - (ALL_MANTISSA_BITS << (core::mem::size_of::() * 8 - ::MANTISSA_DIGITS as usize - 1)) as Scalar; + (ALL_MANTISSA_BITS << (size_of::() * 8 - ::MANTISSA_DIGITS as usize - 1)) as Scalar; let mut runner = test_helpers::make_runner(); runner.run( diff --git a/library/portable-simd/crates/test_helpers/src/subnormals.rs b/library/portable-simd/crates/test_helpers/src/subnormals.rs index ec0f1fb24b936..b5f19ba47b819 100644 --- a/library/portable-simd/crates/test_helpers/src/subnormals.rs +++ b/library/portable-simd/crates/test_helpers/src/subnormals.rs @@ -12,7 +12,7 @@ macro_rules! impl_float { $( impl FlushSubnormals for $ty { fn flush(self) -> Self { - let is_f32 = core::mem::size_of::() == 4; + let is_f32 = size_of::() == 4; let ppc_flush = is_f32 && cfg!(all( any(target_arch = "powerpc", all(target_arch = "powerpc64", target_endian = "big")), target_feature = "altivec", diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml index e54a50aa15c61..b8bc2a3af4cd4 100644 --- a/library/proc_macro/Cargo.toml +++ b/library/proc_macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "proc_macro" version = "0.0.0" -edition = "2021" +edition = "2024" [dependencies] std = { path = "../std" } @@ -9,3 +9,4 @@ std = { path = "../std" } # `core` when resolving doc links. Without this line a different `core` will be # loaded from sysroot causing duplicate lang items and other similar errors. core = { path = "../core" } +rustc-literal-escaper = { version = "0.0.2", features = ["rustc-dep-of-std"] } diff --git a/library/proc_macro/src/bridge/arena.rs b/library/proc_macro/src/bridge/arena.rs index 29636e793f614..bf5a5b5a81821 100644 --- a/library/proc_macro/src/bridge/arena.rs +++ b/library/proc_macro/src/bridge/arena.rs @@ -68,6 +68,7 @@ impl Arena { /// Allocates a byte slice with specified size from the current memory /// chunk. Returns `None` if there is no free space left to satisfy the /// request. + #[allow(clippy::mut_from_ref)] fn alloc_raw_without_grow(&self, bytes: usize) -> Option<&mut [MaybeUninit]> { let start = self.start.get().addr(); let old_end = self.end.get(); diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index f6d4825c67b24..e7d547966a5d5 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -111,12 +111,6 @@ impl Clone for TokenStream { } } -impl Clone for SourceFile { - fn clone(&self) -> Self { - self.clone() - } -} - impl Span { pub(crate) fn def_site() -> Span { Bridge::with(|bridge| bridge.globals.def_site) diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 03c3e697cfe2b..75d82d7465404 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -7,6 +7,9 @@ //! Rust ABIs (e.g., stage0/bin/rustc vs stage1/bin/rustc during bootstrap). #![deny(unsafe_code)] +// proc_macros anyway don't work on wasm hosts so while both sides of this bridge can +// be built with different versions of rustc, the wasm ABI changes don't really matter. +#![allow(wasm_c_abi)] use std::hash::Hash; use std::ops::{Bound, Range}; @@ -78,16 +81,8 @@ macro_rules! with_api { $self: $S::TokenStream ) -> Vec>; }, - 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; - }, Span { fn debug($self: $S::Span) -> String; - fn source_file($self: $S::Span) -> $S::SourceFile; fn parent($self: $S::Span) -> Option<$S::Span>; fn source($self: $S::Span) -> $S::Span; fn byte_range($self: $S::Span) -> Range; @@ -95,6 +90,8 @@ macro_rules! with_api { fn end($self: $S::Span) -> $S::Span; fn line($self: $S::Span) -> usize; fn column($self: $S::Span) -> usize; + fn file($self: $S::Span) -> String; + fn local_file($self: $S::Span) -> Option; fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>; fn subspan($self: $S::Span, start: Bound, end: Bound) -> Option<$S::Span>; fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span; @@ -117,7 +114,6 @@ macro_rules! with_api_handle_types { 'owned: FreeFunctions, TokenStream, - SourceFile, 'interned: Span, diff --git a/library/proc_macro/src/bridge/selfless_reify.rs b/library/proc_macro/src/bridge/selfless_reify.rs index 312a79152e23b..b06434a5ffee2 100644 --- a/library/proc_macro/src/bridge/selfless_reify.rs +++ b/library/proc_macro/src/bridge/selfless_reify.rs @@ -50,7 +50,7 @@ macro_rules! define_reify_functions { >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { // FIXME(eddyb) describe the `F` type (e.g. via `type_name::`) once panic // formatting becomes possible in `const fn`. - assert!(mem::size_of::() == 0, "selfless_reify: closure must be zero-sized"); + assert!(size_of::() == 0, "selfless_reify: closure must be zero-sized"); $(extern $abi)? fn wrapper< $($($param,)*)? diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 97e5a603c3ac9..5beda7c3c96e5 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -82,7 +82,6 @@ with_api_handle_types!(define_server_handles); pub trait Types { type FreeFunctions: 'static; type TokenStream: 'static + Clone; - type SourceFile: 'static + Clone; type Span: 'static + Copy + Eq + Hash; type Symbol: 'static; } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index d9141eab5919f..b4fd20c0c176a 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -27,6 +27,7 @@ #![feature(panic_can_unwind)] #![feature(restricted_std)] #![feature(rustc_attrs)] +#![feature(stmt_expr_attributes)] #![feature(extend_one)] #![recursion_limit = "256"] #![allow(internal_features)] @@ -51,11 +52,24 @@ use std::{error, fmt}; #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] pub use diagnostic::{Diagnostic, Level, MultiSpan}; +#[unstable(feature = "proc_macro_value", issue = "136652")] +pub use rustc_literal_escaper::EscapeError; +use rustc_literal_escaper::{MixedUnit, Mode, byte_from_char, unescape_mixed, unescape_unicode}; #[unstable(feature = "proc_macro_totokens", issue = "130977")] pub use to_tokens::ToTokens; use crate::escape::{EscapeOptions, escape_bytes}; +/// Errors returned when trying to retrieve a literal unescaped value. +#[unstable(feature = "proc_macro_value", issue = "136652")] +#[derive(Debug, PartialEq, Eq)] +pub enum ConversionErrorKind { + /// The literal failed to be escaped, take a look at [`EscapeError`] for more information. + FailedToUnescape(EscapeError), + /// Trying to convert a literal with the wrong type. + InvalidLiteralKind, +} + /// Determines whether proc_macro has been made accessible to the currently /// running program. /// @@ -477,12 +491,6 @@ impl 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")] @@ -505,13 +513,13 @@ impl Span { } /// Creates an empty span pointing to directly before this span. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn start(&self) -> Span { Span(self.0.start()) } /// Creates an empty span pointing to directly after this span. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn end(&self) -> Span { Span(self.0.end()) } @@ -519,7 +527,7 @@ impl Span { /// The one-indexed line of the source file where the span starts. /// /// To obtain the line of the span's end, use `span.end().line()`. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn line(&self) -> usize { self.0.line() } @@ -527,11 +535,30 @@ impl Span { /// The one-indexed column of the source file where the span starts. /// /// To obtain the column of the span's end, use `span.end().column()`. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn column(&self) -> usize { self.0.column() } + /// The path to the source file in which this span occurs, for display purposes. + /// + /// This might not correspond to a valid file system path. + /// It might be remapped (e.g. `"/src/lib.rs"`) or an artificial path (e.g. `""`). + #[stable(feature = "proc_macro_span_file", since = "1.88.0")] + pub fn file(&self) -> String { + self.0.file() + } + + /// The path to the source file in which this span occurs on the local file system. + /// + /// This is the actual path on disk. It is unaffected by path remapping. + /// + /// This path should not be embedded in the output of the macro; prefer `file()` instead. + #[stable(feature = "proc_macro_span_file", since = "1.88.0")] + pub fn local_file(&self) -> Option { + self.0.local_file().map(|s| PathBuf::from(s)) + } + /// Creates a new span encompassing `self` and `other`. /// /// Returns `None` if `self` and `other` are from different files. @@ -600,58 +627,6 @@ impl fmt::Debug for Span { } } -/// 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 might 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 might 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)] @@ -1451,6 +1426,107 @@ impl Literal { } }) } + + /// Returns the unescaped string value if the current literal is a string or a string literal. + #[unstable(feature = "proc_macro_value", issue = "136652")] + pub fn str_value(&self) -> Result { + self.0.symbol.with(|symbol| match self.0.kind { + bridge::LitKind::Str => { + if symbol.contains('\\') { + let mut buf = String::with_capacity(symbol.len()); + let mut error = None; + // Force-inlining here is aggressive but the closure is + // called on every char in the string, so it can be hot in + // programs with many long strings containing escapes. + unescape_unicode( + symbol, + Mode::Str, + &mut #[inline(always)] + |_, c| match c { + Ok(c) => buf.push(c), + Err(err) => { + if err.is_fatal() { + error = Some(ConversionErrorKind::FailedToUnescape(err)); + } + } + }, + ); + if let Some(error) = error { Err(error) } else { Ok(buf) } + } else { + Ok(symbol.to_string()) + } + } + bridge::LitKind::StrRaw(_) => Ok(symbol.to_string()), + _ => Err(ConversionErrorKind::InvalidLiteralKind), + }) + } + + /// Returns the unescaped string value if the current literal is a c-string or a c-string + /// literal. + #[unstable(feature = "proc_macro_value", issue = "136652")] + pub fn cstr_value(&self) -> Result, ConversionErrorKind> { + self.0.symbol.with(|symbol| match self.0.kind { + bridge::LitKind::CStr => { + let mut error = None; + let mut buf = Vec::with_capacity(symbol.len()); + + unescape_mixed(symbol, Mode::CStr, &mut |_span, c| match c { + Ok(MixedUnit::Char(c)) => { + buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes()) + } + Ok(MixedUnit::HighByte(b)) => buf.push(b), + Err(err) => { + if err.is_fatal() { + error = Some(ConversionErrorKind::FailedToUnescape(err)); + } + } + }); + if let Some(error) = error { + Err(error) + } else { + buf.push(0); + Ok(buf) + } + } + bridge::LitKind::CStrRaw(_) => { + // Raw strings have no escapes so we can convert the symbol + // directly to a `Lrc` after appending the terminating NUL + // char. + let mut buf = symbol.to_owned().into_bytes(); + buf.push(0); + Ok(buf) + } + _ => Err(ConversionErrorKind::InvalidLiteralKind), + }) + } + + /// Returns the unescaped string value if the current literal is a byte string or a byte string + /// literal. + #[unstable(feature = "proc_macro_value", issue = "136652")] + pub fn byte_str_value(&self) -> Result, ConversionErrorKind> { + self.0.symbol.with(|symbol| match self.0.kind { + bridge::LitKind::ByteStr => { + let mut buf = Vec::with_capacity(symbol.len()); + let mut error = None; + + unescape_unicode(symbol, Mode::ByteStr, &mut |_, c| match c { + Ok(c) => buf.push(byte_from_char(c)), + Err(err) => { + if err.is_fatal() { + error = Some(ConversionErrorKind::FailedToUnescape(err)); + } + } + }); + if let Some(error) = error { Err(error) } else { Ok(buf) } + } + bridge::LitKind::ByteStrRaw(_) => { + // Raw strings have no escapes so we can convert the symbol + // directly to a `Lrc`. + Ok(symbol.to_owned().into_bytes()) + } + _ => Err(ConversionErrorKind::InvalidLiteralKind), + }) + } } /// Parse a single literal from its stringified representation. diff --git a/library/profiler_builtins/Cargo.toml b/library/profiler_builtins/Cargo.toml index 230e8051602e4..e075a38daea11 100644 --- a/library/profiler_builtins/Cargo.toml +++ b/library/profiler_builtins/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "profiler_builtins" version = "0.0.0" -edition = "2021" +edition = "2024" [lib] test = false diff --git a/library/profiler_builtins/build.rs b/library/profiler_builtins/build.rs index dd85239fa8cfd..fc1a9ecc1ec32 100644 --- a/library/profiler_builtins/build.rs +++ b/library/profiler_builtins/build.rs @@ -9,8 +9,14 @@ use std::path::PathBuf; fn main() { if let Ok(rt) = tracked_env_var("LLVM_PROFILER_RT_LIB") { - println!("cargo::rustc-link-lib=static:+verbatim={rt}"); - return; + let rt = PathBuf::from(rt); + if let Some(lib) = rt.file_name() { + if let Some(dir) = rt.parent() { + println!("cargo::rustc-link-search=native={}", dir.display()); + } + println!("cargo::rustc-link-lib=static:+verbatim={}", lib.to_str().unwrap()); + return; + } } let target_os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS was not set"); diff --git a/library/rustc-std-workspace-alloc/Cargo.toml b/library/rustc-std-workspace-alloc/Cargo.toml index 049ca3e46b57d..5a177808d1bd8 100644 --- a/library/rustc-std-workspace-alloc/Cargo.toml +++ b/library/rustc-std-workspace-alloc/Cargo.toml @@ -5,7 +5,7 @@ license = 'MIT OR Apache-2.0' description = """ Hack for the compiler's own build system """ -edition = "2021" +edition = "2024" [lib] path = "lib.rs" diff --git a/library/rustc-std-workspace-core/Cargo.toml b/library/rustc-std-workspace-core/Cargo.toml index ff5cfcbd64144..9315c08a4d199 100644 --- a/library/rustc-std-workspace-core/Cargo.toml +++ b/library/rustc-std-workspace-core/Cargo.toml @@ -5,7 +5,7 @@ license = 'MIT OR Apache-2.0' description = """ Hack for the compiler's own build system """ -edition = "2021" +edition = "2024" [lib] path = "lib.rs" diff --git a/library/rustc-std-workspace-std/Cargo.toml b/library/rustc-std-workspace-std/Cargo.toml index 3a1dc2a02b557..f70994e1f8868 100644 --- a/library/rustc-std-workspace-std/Cargo.toml +++ b/library/rustc-std-workspace-std/Cargo.toml @@ -5,7 +5,7 @@ license = 'MIT OR Apache-2.0' description = """ Hack for the compiler's own build system """ -edition = "2021" +edition = "2024" [lib] path = "lib.rs" diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index f4d4894c1bbdf..4ff4895ecde7a 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -6,7 +6,7 @@ version = "0.0.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" description = "The Rust Standard Library" -edition = "2021" +edition = "2024" autobenches = false [lib] @@ -18,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "=0.1.148" } +compiler_builtins = { version = "=0.1.159" } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', @@ -35,7 +35,7 @@ miniz_oxide = { version = "0.8.0", optional = true, default-features = false } addr2line = { version = "0.24.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.169", default-features = false, features = [ +libc = { version = "0.2.172", default-features = false, features = [ 'rustc-dep-of-std', ], public = true } @@ -57,7 +57,7 @@ object = { version = "0.36.0", default-features = false, optional = true, featur 'archive', ] } -[target.'cfg(windows)'.dependencies.windows-targets] +[target.'cfg(any(windows, target_os = "cygwin"))'.dependencies.windows-targets] path = "../windows_targets" [dev-dependencies] @@ -73,7 +73,7 @@ fortanix-sgx-abi = { version = "0.5.0", features = [ ], public = true } [target.'cfg(target_os = "hermit")'.dependencies] -hermit-abi = { version = "0.4.0", features = [ +hermit-abi = { version = "0.5.0", features = [ 'rustc-dep-of-std', ], public = true } @@ -83,8 +83,8 @@ wasi = { version = "0.11.0", features = [ ], default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] -r-efi = { version = "4.5.0", features = ['rustc-dep-of-std'] } -r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std'] } +r-efi = { version = "5.2.0", features = ['rustc-dep-of-std'] } +r-efi-alloc = { version = "2.0.0", features = ['rustc-dep-of-std'] } [features] backtrace = [ @@ -121,7 +121,6 @@ debug_typeid = ["core/debug_typeid"] # https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/Cargo.toml std_detect_file_io = ["std_detect/std_detect_file_io"] std_detect_dlsym_getauxval = ["std_detect/std_detect_dlsym_getauxval"] -std_detect_env_override = ["std_detect/std_detect_env_override"] # Enable using raw-dylib for Windows imports. # This will eventually be the default. @@ -163,4 +162,10 @@ check-cfg = [ # and to the `backtrace` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg 'cfg(feature, values(any()))', + # Internal features aren't marked known config by default, we use these to + # gate tests. + 'cfg(target_has_reliable_f16)', + 'cfg(target_has_reliable_f16_math)', + 'cfg(target_has_reliable_f128)', + 'cfg(target_has_reliable_f128_math)', ] diff --git a/library/std/build.rs b/library/std/build.rs index 9df35ce3cc852..ef695601a448a 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -7,17 +7,6 @@ fn main() { let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").expect("CARGO_CFG_TARGET_VENDOR was not set"); let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("CARGO_CFG_TARGET_ENV was not set"); - let target_abi = env::var("CARGO_CFG_TARGET_ABI").expect("CARGO_CFG_TARGET_ABI was not set"); - let target_pointer_width: u32 = env::var("CARGO_CFG_TARGET_POINTER_WIDTH") - .expect("CARGO_CFG_TARGET_POINTER_WIDTH was not set") - .parse() - .unwrap(); - let target_features: Vec<_> = env::var("CARGO_CFG_TARGET_FEATURE") - .unwrap_or_default() - .split(",") - .map(ToOwned::to_owned) - .collect(); - let is_miri = env::var_os("CARGO_CFG_MIRI").is_some(); println!("cargo:rustc-check-cfg=cfg(netbsd10)"); if target_os == "netbsd" && env::var("RUSTC_STD_NETBSD10").is_ok() { @@ -42,6 +31,7 @@ fn main() { || target_os == "fuchsia" || (target_vendor == "fortanix" && target_env == "sgx") || target_os == "hermit" + || target_os == "trusty" || target_os == "l4re" || target_os == "redox" || target_os == "haiku" @@ -61,6 +51,7 @@ fn main() { || target_os == "zkvm" || target_os == "rtems" || target_os == "nuttx" + || target_os == "cygwin" // See src/bootstrap/src/core/build_steps/synthetic_targets.rs || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok() @@ -83,111 +74,4 @@ fn main() { println!("cargo:rustc-cfg=backtrace_in_libstd"); println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap()); - - // Emit these on platforms that have no known ABI bugs, LLVM selection bugs, lowering bugs, - // missing symbols, or other problems, to determine when tests get run. - // If more broken platforms are found, please update the tracking issue at - // - // - // Some of these match arms are redundant; the goal is to separate reasons that the type is - // unreliable, even when multiple reasons might fail the same platform. - println!("cargo:rustc-check-cfg=cfg(reliable_f16)"); - println!("cargo:rustc-check-cfg=cfg(reliable_f128)"); - - // This is a step beyond only having the types and basic functions available. Math functions - // aren't consistently available or correct. - println!("cargo:rustc-check-cfg=cfg(reliable_f16_math)"); - println!("cargo:rustc-check-cfg=cfg(reliable_f128_math)"); - - let has_reliable_f16 = match (target_arch.as_str(), target_os.as_str()) { - // We can always enable these in Miri as that is not affected by codegen bugs. - _ if is_miri => true, - // Selection failure - ("s390x", _) => false, - // Unsupported - ("arm64ec", _) => false, - // LLVM crash - ("aarch64", _) if !target_features.iter().any(|f| f == "neon") => false, - // MinGW ABI bugs - ("x86_64", "windows") if target_env == "gnu" && target_abi != "llvm" => false, - // Infinite recursion - ("csky", _) => false, - ("hexagon", _) => false, - ("loongarch64", _) => false, - ("powerpc" | "powerpc64", _) => false, - ("sparc" | "sparc64", _) => false, - ("wasm32" | "wasm64", _) => false, - // `f16` support only requires that symbols converting to and from `f32` are available. We - // provide these in `compiler-builtins`, so `f16` should be available on all platforms that - // do not have other ABI issues or LLVM crashes. - _ => true, - }; - - let has_reliable_f128 = match (target_arch.as_str(), target_os.as_str()) { - // We can always enable these in Miri as that is not affected by codegen bugs. - _ if is_miri => true, - // Unsupported - ("arm64ec", _) => false, - // Selection bug - ("mips64" | "mips64r6", _) => false, - // Selection bug - ("nvptx64", _) => false, - // ABI bugs et al. (full - // list at ) - ("powerpc" | "powerpc64", _) => false, - // ABI unsupported - ("sparc", _) => false, - // Stack alignment bug . NB: tests may - // not fail if our compiler-builtins is linked. - ("x86", _) => false, - // MinGW ABI bugs - ("x86_64", "windows") if target_env == "gnu" && target_abi != "llvm" => false, - // There are no known problems on other platforms, so the only requirement is that symbols - // are available. `compiler-builtins` provides all symbols required for core `f128` - // support, so this should work for everything else. - _ => true, - }; - - // Configure platforms that have reliable basics but may have unreliable math. - - // LLVM is currently adding missing routines, - let has_reliable_f16_math = has_reliable_f16 - && match (target_arch.as_str(), target_os.as_str()) { - // FIXME: Disabled on Miri as the intrinsics are not implemented yet. - _ if is_miri => false, - // x86 has a crash for `powi`: - ("x86" | "x86_64", _) => false, - // Assume that working `f16` means working `f16` math for most platforms, since - // operations just go through `f32`. - _ => true, - }; - - let has_reliable_f128_math = has_reliable_f128 - && match (target_arch.as_str(), target_os.as_str()) { - // FIXME: Disabled on Miri as the intrinsics are not implemented yet. - _ if is_miri => false, - // LLVM lowers `fp128` math to `long double` symbols even on platforms where - // `long double` is not IEEE binary128. See - // . - // - // This rules out anything that doesn't have `long double` = `binary128`; <= 32 bits - // (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86` - // (ld is 80-bit extended precision). - ("x86_64", _) => false, - (_, "linux") if target_pointer_width == 64 => true, - _ => false, - }; - - if has_reliable_f16 { - println!("cargo:rustc-cfg=reliable_f16"); - } - if has_reliable_f128 { - println!("cargo:rustc-cfg=reliable_f128"); - } - if has_reliable_f16_math { - println!("cargo:rustc-cfg=reliable_f16_math"); - } - if has_reliable_f128_math { - println!("cargo:rustc-cfg=reliable_f128_math"); - } } diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 99d105a2454a5..b574e9f3a25e3 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -57,7 +57,7 @@ #![stable(feature = "alloc_module", since = "1.28.0")] use core::ptr::NonNull; -use core::sync::atomic::{AtomicPtr, Ordering}; +use core::sync::atomic::{Atomic, AtomicPtr, Ordering}; use core::{hint, mem, ptr}; #[stable(feature = "alloc_module", since = "1.28.0")] @@ -287,7 +287,7 @@ unsafe impl Allocator for System { } } -static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); +static HOOK: Atomic<*mut ()> = AtomicPtr::new(ptr::null_mut()); /// Registers a custom allocation error hook, replacing any that was previously registered. /// @@ -348,6 +348,7 @@ fn default_alloc_error_hook(layout: Layout) { unsafe extern "Rust" { // This symbol is emitted by rustc next to __rust_alloc_error_handler. // Its value depends on the -Zoom={panic,abort} compiler option. + #[rustc_std_internal_symbol] static __rust_alloc_error_handler_should_panic: u8; } diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index fc333d7ff3f95..c3fcb0e2e42b0 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -92,8 +92,8 @@ use crate::backtrace_rs::{self, BytesOrWideString}; use crate::ffi::c_void; use crate::panic::UnwindSafe; use crate::sync::LazyLock; -use crate::sync::atomic::AtomicU8; use crate::sync::atomic::Ordering::Relaxed; +use crate::sync::atomic::{Atomic, AtomicU8}; use crate::sys::backtrace::{lock, output_filename, set_image_base}; use crate::{env, fmt}; @@ -254,7 +254,7 @@ impl Backtrace { // 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: AtomicU8 = AtomicU8::new(0); + static ENABLED: Atomic = AtomicU8::new(0); match ENABLED.load(Relaxed) { 0 => {} 1 => return false, @@ -432,6 +432,7 @@ mod helper { use super::*; pub(super) type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync + UnwindSafe; + #[define_opaque(LazyResolve)] pub(super) fn lazy_resolve(mut capture: Capture) -> LazyResolve { move || { // Use the global backtrace lock to synchronize this as it's a diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index ff4a4b35ce450..3530f890f5294 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -208,20 +208,32 @@ use crate::ops::Index; /// # Usage in `const` and `static` /// /// As explained above, `HashMap` is randomly seeded: each `HashMap` instance uses a different seed, -/// which means that `HashMap::new` cannot be used in const context. To construct a `HashMap` in the -/// initializer of a `const` or `static` item, you will have to use a different hasher that does not -/// involve a random seed, as demonstrated in the following example. **A `HashMap` constructed this -/// way is not resistant against HashDoS!** +/// which means that `HashMap::new` normally cannot be used in a `const` or `static` initializer. /// +/// However, if you need to use a `HashMap` in a `const` or `static` initializer while retaining +/// random seed generation, you can wrap the `HashMap` in [`LazyLock`]. +/// +/// Alternatively, you can construct a `HashMap` in a `const` or `static` initializer using a different +/// hasher that does not rely on a random seed. **Be aware that a `HashMap` created this way is not +/// resistant to HashDoS attacks!** +/// +/// [`LazyLock`]: crate::sync::LazyLock /// ```rust /// use std::collections::HashMap; /// use std::hash::{BuildHasherDefault, DefaultHasher}; -/// use std::sync::Mutex; +/// use std::sync::{LazyLock, Mutex}; /// -/// const EMPTY_MAP: HashMap, BuildHasherDefault> = +/// // HashMaps with a fixed, non-random hasher +/// const NONRANDOM_EMPTY_MAP: HashMap, BuildHasherDefault> = /// HashMap::with_hasher(BuildHasherDefault::new()); -/// static MAP: Mutex, BuildHasherDefault>> = +/// static NONRANDOM_MAP: Mutex, BuildHasherDefault>> = /// Mutex::new(HashMap::with_hasher(BuildHasherDefault::new())); +/// +/// // HashMaps using LazyLock to retain random seeding +/// const RANDOM_EMPTY_MAP: LazyLock>> = +/// LazyLock::new(HashMap::new); +/// static RANDOM_MAP: LazyLock>>> = +/// LazyLock::new(|| Mutex::new(HashMap::new())); /// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "HashMap")] @@ -671,7 +683,7 @@ impl HashMap { /// ``` #[inline] #[rustc_lint_query_instability] - #[stable(feature = "hash_extract_if", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "hash_extract_if", since = "1.88.0")] pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool, @@ -961,6 +973,9 @@ where /// Returns an array of length `N` with the results of each query. For soundness, at most one /// mutable reference will be returned to any value. `None` will be used if the key is missing. /// + /// This method performs a check to ensure there are no duplicate keys, which currently has a time-complexity of O(n^2), + /// so be careful when passing many keys. + /// /// # Panics /// /// Panics if any keys are overlapping. @@ -1278,69 +1293,6 @@ where } } -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 initialize the raw entry must still be - /// consistent with the hash of the key that is ultimately stored in the entry. - /// This is because implementations of `HashMap` may need to recompute hashes - /// when resizing, at which point only the keys are available. - /// - /// 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 Clone for HashMap where @@ -1728,12 +1680,9 @@ impl<'a, K, V> Drain<'a, K, V> { /// ]); /// let iter = map.extract_if(|_k, v| *v % 2 == 0); /// ``` -#[stable(feature = "hash_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "hash_extract_if", since = "1.88.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct ExtractIf<'a, K, V, F> -where - F: FnMut(&K, &mut V) -> bool, -{ +pub struct ExtractIf<'a, K, V, F> { base: base::ExtractIf<'a, K, V, F>, } @@ -1828,404 +1777,6 @@ impl Default for IntoValues { } } -/// 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. -#[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`]. -/// -/// [`raw_entry_mut`]: HashMap::raw_entry_mut -#[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. -#[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] - #[must_use] - #[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] - #[must_use] - #[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] - #[must_use = "`self` will be dropped if the result is not used"] - #[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] - #[must_use] - #[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] - #[must_use = "`self` will be dropped if the result is not used"] - #[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] - #[must_use] - #[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] - #[must_use] - #[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] - #[must_use = "`self` will be dropped if the result is not used"] - #[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_non_exhaustive() - } -} - -#[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_non_exhaustive() - } -} - -#[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_non_exhaustive() - } -} - -#[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_non_exhaustive() - } -} - /// 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`]. @@ -2743,7 +2294,7 @@ where } } -#[stable(feature = "hash_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "hash_extract_if", since = "1.88.0")] impl Iterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool, @@ -2760,13 +2311,14 @@ where } } -#[stable(feature = "hash_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "hash_extract_if", since = "1.88.0")] impl FusedIterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} -#[stable(feature = "hash_extract_if", since = "CURRENT_RUSTC_VERSION")] -impl<'a, K, V, F> fmt::Debug for ExtractIf<'a, K, V, F> +#[stable(feature = "hash_extract_if", since = "1.88.0")] +impl fmt::Debug for ExtractIf<'_, K, V, F> where - F: FnMut(&K, &mut V) -> bool, + K: fmt::Debug, + V: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ExtractIf").finish_non_exhaustive() @@ -3298,16 +2850,6 @@ pub(super) fn map_try_reserve_error(err: hashbrown::TryReserveError) -> TryReser } } -#[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> { diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index a275488a55602..9f7df20a1d7e4 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -852,99 +852,6 @@ fn test_try_reserve() { } } -#[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_extract_if { use super::*; use crate::panic::{AssertUnwindSafe, catch_unwind}; diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index a547a9943c1a0..8514dfd9a984d 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -308,7 +308,7 @@ impl HashSet { /// ``` #[inline] #[rustc_lint_query_instability] - #[stable(feature = "hash_extract_if", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "hash_extract_if", since = "1.88.0")] pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, T, F> where F: FnMut(&T) -> bool, @@ -1390,11 +1390,8 @@ pub struct Drain<'a, K: 'a> { /// /// let mut extract_ifed = a.extract_if(|v| v % 2 == 0); /// ``` -#[stable(feature = "hash_extract_if", since = "CURRENT_RUSTC_VERSION")] -pub struct ExtractIf<'a, K, F> -where - F: FnMut(&K) -> bool, -{ +#[stable(feature = "hash_extract_if", since = "1.88.0")] +pub struct ExtractIf<'a, K, F> { base: base::ExtractIf<'a, K, F>, } @@ -1673,7 +1670,7 @@ impl fmt::Debug for Drain<'_, K> { } } -#[stable(feature = "hash_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "hash_extract_if", since = "1.88.0")] impl Iterator for ExtractIf<'_, K, F> where F: FnMut(&K) -> bool, @@ -1690,13 +1687,13 @@ where } } -#[stable(feature = "hash_extract_if", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "hash_extract_if", since = "1.88.0")] impl FusedIterator for ExtractIf<'_, K, F> where F: FnMut(&K) -> bool {} -#[stable(feature = "hash_extract_if", since = "CURRENT_RUSTC_VERSION")] -impl<'a, K, F> fmt::Debug for ExtractIf<'a, K, F> +#[stable(feature = "hash_extract_if", since = "1.88.0")] +impl fmt::Debug for ExtractIf<'_, K, F> where - F: FnMut(&K) -> bool, + K: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ExtractIf").finish_non_exhaustive() diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 4a071b4e1faec..ce2dc79522076 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -12,9 +12,11 @@ use crate::error::Error; use crate::ffi::{OsStr, OsString}; +use crate::num::NonZero; +use crate::ops::Try; use crate::path::{Path, PathBuf}; -use crate::sys::os as os_imp; -use crate::{fmt, io, sys}; +use crate::sys::{env as env_imp, os as os_imp}; +use crate::{array, fmt, io, sys}; /// Returns the current working directory as a [`PathBuf`]. /// @@ -96,7 +98,7 @@ pub struct Vars { /// [`env::vars_os()`]: vars_os #[stable(feature = "env", since = "1.0.0")] pub struct VarsOs { - inner: os_imp::Env, + inner: env_imp::Env, } /// Returns an iterator of (variable, value) pairs of strings, for all the @@ -150,7 +152,7 @@ pub fn vars() -> Vars { #[must_use] #[stable(feature = "env", since = "1.0.0")] pub fn vars_os() -> VarsOs { - VarsOs { inner: os_imp::env() } + VarsOs { inner: env_imp::env() } } #[stable(feature = "env", since = "1.0.0")] @@ -202,6 +204,9 @@ impl fmt::Debug for VarsOs { /// Returns [`VarError::NotUnicode`] if the variable's value is not valid /// Unicode. If this is not desired, consider using [`var_os`]. /// +/// Use [`env!`] or [`option_env!`] instead if you want to check environment +/// variables at compile time. +/// /// # Examples /// /// ``` @@ -256,7 +261,7 @@ pub fn var_os>(key: K) -> Option { } fn _var_os(key: &OsStr) -> Option { - os_imp::getenv(key) + env_imp::getenv(key) } /// The error type for operations interacting with environment variables. @@ -330,7 +335,7 @@ impl Error for VarError { /// /// Discussion of this unsafety on Unix may be found in: /// -/// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188) +/// - [Austin Group Bugzilla (for POSIX)](https://austingroupbugs.net/view.php?id=188) /// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) /// /// To pass an environment variable to a child process, you can instead use [`Command::env`]. @@ -360,7 +365,7 @@ impl Error for VarError { #[stable(feature = "env", since = "1.0.0")] pub unsafe fn set_var, V: AsRef>(key: K, value: V) { let (key, value) = (key.as_ref(), value.as_ref()); - unsafe { os_imp::setenv(key, value) }.unwrap_or_else(|e| { + unsafe { env_imp::setenv(key, value) }.unwrap_or_else(|e| { panic!("failed to set environment variable `{key:?}` to `{value:?}`: {e}") }) } @@ -431,7 +436,7 @@ pub unsafe fn set_var, V: AsRef>(key: K, value: V) { #[stable(feature = "env", since = "1.0.0")] pub unsafe fn remove_var>(key: K) { let key = key.as_ref(); - unsafe { os_imp::unsetenv(key) } + unsafe { env_imp::unsetenv(key) } .unwrap_or_else(|e| panic!("failed to remove environment variable `{key:?}`: {e}")) } @@ -641,11 +646,6 @@ impl Error for JoinPathsError { /// None => println!("Impossible to get your home dir!"), /// } /// ``` -#[deprecated( - since = "1.29.0", - note = "This function's behavior may be unexpected on Windows. \ - Consider using a crate from crates.io instead." -)] #[must_use] #[stable(feature = "env", since = "1.0.0")] pub fn home_dir() -> Option { @@ -874,19 +874,36 @@ 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()) } + + #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } + + // Methods which skip args cannot simply delegate to the inner iterator, + // because `env::args` states that we will "panic during iteration if any + // argument to the process is not valid Unicode". + // + // This offers two possible interpretations: + // - a skipped argument is never encountered "during iteration" + // - even a skipped argument is encountered "during iteration" + // + // As a panic can be observed, we err towards validating even skipped + // arguments for now, though this is not explicitly promised by the API. } #[stable(feature = "env", since = "1.0.0")] impl ExactSizeIterator for Args { + #[inline] fn len(&self) -> usize { self.inner.len() } + + #[inline] fn is_empty(&self) -> bool { self.inner.is_empty() } @@ -916,19 +933,65 @@ impl !Sync for ArgsOs {} #[stable(feature = "env", since = "1.0.0")] impl Iterator for ArgsOs { type Item = OsString; + + #[inline] fn next(&mut self) -> Option { self.inner.next() } + + #[inline] + fn next_chunk( + &mut self, + ) -> Result<[OsString; N], array::IntoIter> { + self.inner.next_chunk() + } + + #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } + + #[inline] + fn count(self) -> usize { + self.inner.len() + } + + #[inline] + fn last(self) -> Option { + self.inner.last() + } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + self.inner.advance_by(n) + } + + #[inline] + fn try_fold(&mut self, init: B, f: F) -> R + where + F: FnMut(B, Self::Item) -> R, + R: Try, + { + self.inner.try_fold(init, f) + } + + #[inline] + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.inner.fold(init, f) + } } #[stable(feature = "env", since = "1.0.0")] impl ExactSizeIterator for ArgsOs { + #[inline] fn len(&self) -> usize { self.inner.len() } + + #[inline] fn is_empty(&self) -> bool { self.inner.is_empty() } @@ -936,9 +999,15 @@ impl ExactSizeIterator for ArgsOs { #[stable(feature = "env_iterators", since = "1.12.0")] impl DoubleEndedIterator for ArgsOs { + #[inline] fn next_back(&mut self) -> Option { self.inner.next_back() } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + self.inner.advance_back_by(n) + } } #[stable(feature = "std_debug", since = "1.16.0")] @@ -952,7 +1021,7 @@ impl fmt::Debug for ArgsOs { /// Constants associated with the current target #[stable(feature = "env", since = "1.0.0")] pub mod consts { - use crate::sys::env::os; + use crate::sys::env_consts::os; /// A string describing the architecture of the CPU that is currently in use. /// An example value may be: `"x86"`, `"arm"` or `"riscv64"`. diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index 974514c9c4556..bb4acde48224c 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -14,335 +14,6 @@ use crate::sys::cmath; #[cfg(not(test))] impl f128 { - /// Returns the largest integer less than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let f = 3.7_f128; - /// let g = 3.0_f128; - /// let h = -3.7_f128; - /// - /// assert_eq!(f.floor(), 3.0); - /// assert_eq!(g.floor(), 3.0); - /// assert_eq!(h.floor(), -4.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn floor(self) -> f128 { - unsafe { intrinsics::floorf128(self) } - } - - /// Returns the smallest integer greater than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let f = 3.01_f128; - /// let g = 4.0_f128; - /// - /// assert_eq!(f.ceil(), 4.0); - /// assert_eq!(g.ceil(), 4.0); - /// # } - /// ``` - #[inline] - #[doc(alias = "ceiling")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ceil(self) -> f128 { - unsafe { intrinsics::ceilf128(self) } - } - - /// Returns the nearest integer to `self`. If a value is half-way between two - /// integers, round away from `0.0`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let f = 3.3_f128; - /// let g = -3.3_f128; - /// let h = -3.7_f128; - /// let i = 3.5_f128; - /// let j = 4.5_f128; - /// - /// assert_eq!(f.round(), 3.0); - /// assert_eq!(g.round(), -3.0); - /// assert_eq!(h.round(), -4.0); - /// assert_eq!(i.round(), 4.0); - /// assert_eq!(j.round(), 5.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round(self) -> f128 { - unsafe { intrinsics::roundf128(self) } - } - - /// Returns the nearest integer to a number. Rounds half-way cases to the number - /// with an even least significant digit. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let f = 3.3_f128; - /// let g = -3.3_f128; - /// let h = 3.5_f128; - /// let i = 4.5_f128; - /// - /// assert_eq!(f.round_ties_even(), 3.0); - /// assert_eq!(g.round_ties_even(), -3.0); - /// assert_eq!(h.round_ties_even(), 4.0); - /// assert_eq!(i.round_ties_even(), 4.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round_ties_even(self) -> f128 { - intrinsics::round_ties_even_f128(self) - } - - /// Returns the integer part of `self`. - /// This means that non-integer numbers are always truncated towards zero. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let f = 3.7_f128; - /// let g = 3.0_f128; - /// let h = -3.7_f128; - /// - /// assert_eq!(f.trunc(), 3.0); - /// assert_eq!(g.trunc(), 3.0); - /// assert_eq!(h.trunc(), -3.0); - /// # } - /// ``` - #[inline] - #[doc(alias = "truncate")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn trunc(self) -> f128 { - unsafe { intrinsics::truncf128(self) } - } - - /// Returns the fractional part of `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let x = 3.6_f128; - /// let y = -3.6_f128; - /// let abs_difference_x = (x.fract() - 0.6).abs(); - /// let abs_difference_y = (y.fract() - (-0.6)).abs(); - /// - /// assert!(abs_difference_x <= f128::EPSILON); - /// assert!(abs_difference_y <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn fract(self) -> f128 { - self - self.trunc() - } - - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding - /// error, yielding a more accurate result than an unfused multiply-add. - /// - /// Using `mul_add` *may* be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. However, - /// this is not always true, and will be heavily dependant on designing - /// algorithms with specific target hardware in mind. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as - /// `fusedMultiplyAdd` and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let m = 10.0_f128; - /// let x = 4.0_f128; - /// let b = 60.0_f128; - /// - /// assert_eq!(m.mul_add(x, b), 100.0); - /// assert_eq!(m * x + b, 100.0); - /// - /// let one_plus_eps = 1.0_f128 + f128::EPSILON; - /// let one_minus_eps = 1.0_f128 - f128::EPSILON; - /// let minus_one = -1.0_f128; - /// - /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. - /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f128::EPSILON * f128::EPSILON); - /// // Different rounding with the non-fused multiply and add. - /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn mul_add(self, a: f128, b: f128) -> f128 { - unsafe { intrinsics::fmaf128(self, a, b) } - } - - /// Calculates Euclidean division, the matching method for `rem_euclid`. - /// - /// This computes the integer `n` such that - /// `self = n * rhs + self.rem_euclid(rhs)`. - /// In other words, the result is `self / rhs` rounded to the integer `n` - /// such that `self >= n * rhs`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let a: f128 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 - /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn div_euclid(self, rhs: f128) -> f128 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; - } - q - } - - /// Calculates the least nonnegative remainder of `self (mod rhs)`. - /// - /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in - /// most cases. However, due to a floating point round-off error it can - /// result in `r == rhs.abs()`, violating the mathematical definition, if - /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. - /// This result is not an element of the function's codomain, but it is the - /// closest floating point number in the real numbers and thus fulfills the - /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` - /// approximately. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let a: f128 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.rem_euclid(b), 3.0); - /// assert_eq!((-a).rem_euclid(b), 1.0); - /// assert_eq!(a.rem_euclid(-b), 3.0); - /// assert_eq!((-a).rem_euclid(-b), 1.0); - /// // limitation due to round-off error - /// assert!((-f128::EPSILON).rem_euclid(3.0) != 0.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[doc(alias = "modulo", alias = "mod")] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn rem_euclid(self, rhs: f128) -> f128 { - let r = self % rhs; - if r < 0.0 { r + rhs.abs() } else { r } - } - - /// Raises a number to an integer power. - /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let x = 2.0_f128; - /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// assert!(abs_difference <= f128::EPSILON); - /// - /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f128 { - unsafe { intrinsics::powif128(self, n) } - } - /// Raises a number to a floating point power. /// /// # Unspecified precision @@ -354,7 +25,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 2.0_f128; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); @@ -372,40 +46,6 @@ impl f128 { unsafe { intrinsics::powf128(self, n) } } - /// Returns the square root of a number. - /// - /// Returns NaN if `self` is a negative number other than `-0.0`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` - /// and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { - /// - /// let positive = 4.0_f128; - /// let negative = -4.0_f128; - /// let negative_zero = -0.0_f128; - /// - /// assert_eq!(positive.sqrt(), 2.0); - /// assert!(negative.sqrt().is_nan()); - /// assert!(negative_zero.sqrt() == negative_zero); - /// # } - /// ``` - #[inline] - #[doc(alias = "squareRoot")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn sqrt(self) -> f128 { - unsafe { intrinsics::sqrtf128(self) } - } - /// Returns `e^(self)`, (the exponential function). /// /// # Unspecified precision @@ -417,7 +57,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let one = 1.0f128; /// // e^1 @@ -448,7 +91,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let f = 2.0f128; /// @@ -468,6 +114,8 @@ impl f128 { /// Returns the natural logarithm of the number. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, @@ -477,7 +125,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let one = 1.0f128; /// // e^1 @@ -489,6 +140,19 @@ impl f128 { /// assert!(abs_difference <= f128::EPSILON); /// # } /// ``` + /// + /// Non-positive values: + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// assert_eq!(0_f128.ln(), f128::NEG_INFINITY); + /// assert!((-42_f128).ln().is_nan()); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] @@ -499,6 +163,8 @@ impl f128 { /// Returns the logarithm of the number with respect to an arbitrary base. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// The result might not be correctly rounded owing to implementation details; /// `self.log2()` can produce more accurate results for base 2, and /// `self.log10()` can produce more accurate results for base 10. @@ -512,7 +178,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let five = 5.0f128; /// @@ -522,6 +191,19 @@ impl f128 { /// assert!(abs_difference <= f128::EPSILON); /// # } /// ``` + /// + /// Non-positive values: + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// assert_eq!(0_f128.log(10.0), f128::NEG_INFINITY); + /// assert!((-42_f128).log(10.0).is_nan()); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] @@ -532,6 +214,8 @@ impl f128 { /// Returns the base 2 logarithm of the number. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, @@ -541,7 +225,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let two = 2.0f128; /// @@ -551,6 +238,19 @@ impl f128 { /// assert!(abs_difference <= f128::EPSILON); /// # } /// ``` + /// + /// Non-positive values: + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// assert_eq!(0_f128.log2(), f128::NEG_INFINITY); + /// assert!((-42_f128).log2().is_nan()); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] @@ -561,6 +261,8 @@ impl f128 { /// Returns the base 10 logarithm of the number. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, @@ -570,7 +272,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let ten = 10.0f128; /// @@ -580,6 +285,19 @@ impl f128 { /// assert!(abs_difference <= f128::EPSILON); /// # } /// ``` + /// + /// Non-positive values: + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// assert_eq!(0_f128.log10(), f128::NEG_INFINITY); + /// assert!((-42_f128).log10().is_nan()); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] @@ -603,7 +321,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 8.0f128; /// @@ -618,7 +339,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn cbrt(self) -> f128 { - unsafe { cmath::cbrtf128(self) } + cmath::cbrtf128(self) } /// Compute the distance between the origin and a point (`x`, `y`) on the @@ -639,7 +360,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 2.0f128; /// let y = 3.0f128; @@ -655,7 +379,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn hypot(self, other: f128) -> f128 { - unsafe { cmath::hypotf128(self, other) } + cmath::hypotf128(self, other) } /// Computes the sine of a number (in radians). @@ -669,7 +393,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = std::f128::consts::FRAC_PI_2; /// @@ -697,7 +424,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 2.0 * std::f128::consts::PI; /// @@ -728,7 +458,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = std::f128::consts::FRAC_PI_4; /// let abs_difference = (x.tan() - 1.0).abs(); @@ -741,7 +474,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn tan(self) -> f128 { - unsafe { cmath::tanf128(self) } + cmath::tanf128(self) } /// Computes the arcsine of a number. Return value is in radians in @@ -760,7 +493,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let f = std::f128::consts::FRAC_PI_2; /// @@ -776,7 +512,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn asin(self) -> f128 { - unsafe { cmath::asinf128(self) } + cmath::asinf128(self) } /// Computes the arccosine of a number. Return value is in radians in @@ -795,7 +531,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let f = std::f128::consts::FRAC_PI_4; /// @@ -811,7 +550,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn acos(self) -> f128 { - unsafe { cmath::acosf128(self) } + cmath::acosf128(self) } /// Computes the arctangent of a number. Return value is in radians in the @@ -829,7 +568,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let f = 1.0f128; /// @@ -845,7 +587,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn atan(self) -> f128 { - unsafe { cmath::atanf128(self) } + cmath::atanf128(self) } /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. @@ -867,7 +609,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// // Positive angles measured counter-clockwise /// // from positive x axis @@ -891,7 +636,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn atan2(self, other: f128) -> f128 { - unsafe { cmath::atan2f128(self, other) } + cmath::atan2f128(self, other) } /// Simultaneously computes the sine and cosine of the number, `x`. Returns @@ -909,7 +654,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = std::f128::consts::FRAC_PI_4; /// let f = x.sin_cos(); @@ -944,7 +692,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 1e-8_f128; /// @@ -960,12 +711,14 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn exp_m1(self) -> f128 { - unsafe { cmath::expm1f128(self) } + cmath::expm1f128(self) } /// Returns `ln(1+n)` (natural logarithm) more accurately than if /// the operations were performed separately. /// + /// This returns NaN when `n < -1.0`, and negative infinity when `n == -1.0`. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, @@ -978,7 +731,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 1e-8_f128; /// @@ -989,13 +745,26 @@ impl f128 { /// assert!(abs_difference < 1e-10); /// # } /// ``` + /// + /// Out-of-range values: + /// ``` + /// #![feature(f128)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { + /// + /// assert_eq!((-1.0_f128).ln_1p(), f128::NEG_INFINITY); + /// assert!((-2.0_f128).ln_1p().is_nan()); + /// # } + /// ``` #[inline] #[doc(alias = "log1p")] #[must_use = "method returns a new number and does not mutate the original value"] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] pub fn ln_1p(self) -> f128 { - unsafe { cmath::log1pf128(self) } + cmath::log1pf128(self) } /// Hyperbolic sine function. @@ -1012,7 +781,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let e = std::f128::consts::E; /// let x = 1.0f128; @@ -1030,7 +802,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn sinh(self) -> f128 { - unsafe { cmath::sinhf128(self) } + cmath::sinhf128(self) } /// Hyperbolic cosine function. @@ -1047,7 +819,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let e = std::f128::consts::E; /// let x = 1.0f128; @@ -1065,7 +840,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn cosh(self) -> f128 { - unsafe { cmath::coshf128(self) } + cmath::coshf128(self) } /// Hyperbolic tangent function. @@ -1082,7 +857,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let e = std::f128::consts::E; /// let x = 1.0f128; @@ -1100,7 +878,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn tanh(self) -> f128 { - unsafe { cmath::tanhf128(self) } + cmath::tanhf128(self) } /// Inverse hyperbolic sine function. @@ -1114,7 +892,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 1.0f128; /// let f = x.sinh().asinh(); @@ -1146,7 +927,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 1.0f128; /// let f = x.cosh().acosh(); @@ -1180,7 +964,10 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let e = std::f128::consts::E; /// let f = e.tanh().atanh(); @@ -1214,7 +1001,10 @@ impl f128 { /// ``` /// #![feature(f128)] /// #![feature(float_gamma)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 5.0f128; /// @@ -1229,7 +1019,7 @@ impl f128 { // #[unstable(feature = "float_gamma", issue = "99842")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn gamma(self) -> f128 { - unsafe { cmath::tgammaf128(self) } + cmath::tgammaf128(self) } /// Natural logarithm of the absolute value of the gamma function @@ -1249,7 +1039,10 @@ impl f128 { /// ``` /// #![feature(f128)] /// #![feature(float_gamma)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 2.0f128; /// @@ -1265,7 +1058,7 @@ impl f128 { #[must_use = "method returns a new number and does not mutate the original value"] pub fn ln_gamma(self) -> (f128, i32) { let mut signgamp: i32 = 0; - let x = unsafe { cmath::lgammaf128_r(self, &mut signgamp) }; + let x = cmath::lgammaf128_r(self, &mut signgamp); (x, signgamp) } @@ -1284,7 +1077,10 @@ impl f128 { /// ``` /// #![feature(f128)] /// #![feature(float_erf)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// /// The error function relates what percent of a normal distribution lies /// /// within `x` standard deviations (scaled by `1/sqrt(2)`). /// fn within_standard_deviations(x: f128) -> f128 { @@ -1305,7 +1101,7 @@ impl f128 { // #[unstable(feature = "float_erf", issue = "136321")] #[inline] pub fn erf(self) -> f128 { - unsafe { cmath::erff128(self) } + cmath::erff128(self) } /// Complementary error function. @@ -1323,7 +1119,10 @@ impl f128 { /// ``` /// #![feature(f128)] /// #![feature(float_erf)] - /// # #[cfg(reliable_f128_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f128_math)] { /// let x: f128 = 0.123; /// /// let one = x.erf() + x.erfc(); @@ -1338,6 +1137,6 @@ impl f128 { // #[unstable(feature = "float_erf", issue = "136321")] #[inline] pub fn erfc(self) -> f128 { - unsafe { cmath::erfcf128(self) } + cmath::erfcf128(self) } } diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index c3b51bf31de70..4792eac1f9e28 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -14,335 +14,6 @@ use crate::sys::cmath; #[cfg(not(test))] impl f16 { - /// Returns the largest integer less than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let f = 3.7_f16; - /// let g = 3.0_f16; - /// let h = -3.7_f16; - /// - /// assert_eq!(f.floor(), 3.0); - /// assert_eq!(g.floor(), 3.0); - /// assert_eq!(h.floor(), -4.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn floor(self) -> f16 { - unsafe { intrinsics::floorf16(self) } - } - - /// Returns the smallest integer greater than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let f = 3.01_f16; - /// let g = 4.0_f16; - /// - /// assert_eq!(f.ceil(), 4.0); - /// assert_eq!(g.ceil(), 4.0); - /// # } - /// ``` - #[inline] - #[doc(alias = "ceiling")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ceil(self) -> f16 { - unsafe { intrinsics::ceilf16(self) } - } - - /// Returns the nearest integer to `self`. If a value is half-way between two - /// integers, round away from `0.0`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let f = 3.3_f16; - /// let g = -3.3_f16; - /// let h = -3.7_f16; - /// let i = 3.5_f16; - /// let j = 4.5_f16; - /// - /// assert_eq!(f.round(), 3.0); - /// assert_eq!(g.round(), -3.0); - /// assert_eq!(h.round(), -4.0); - /// assert_eq!(i.round(), 4.0); - /// assert_eq!(j.round(), 5.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round(self) -> f16 { - unsafe { intrinsics::roundf16(self) } - } - - /// Returns the nearest integer to a number. Rounds half-way cases to the number - /// with an even least significant digit. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let f = 3.3_f16; - /// let g = -3.3_f16; - /// let h = 3.5_f16; - /// let i = 4.5_f16; - /// - /// assert_eq!(f.round_ties_even(), 3.0); - /// assert_eq!(g.round_ties_even(), -3.0); - /// assert_eq!(h.round_ties_even(), 4.0); - /// assert_eq!(i.round_ties_even(), 4.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn round_ties_even(self) -> f16 { - intrinsics::round_ties_even_f16(self) - } - - /// Returns the integer part of `self`. - /// This means that non-integer numbers are always truncated towards zero. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let f = 3.7_f16; - /// let g = 3.0_f16; - /// let h = -3.7_f16; - /// - /// assert_eq!(f.trunc(), 3.0); - /// assert_eq!(g.trunc(), 3.0); - /// assert_eq!(h.trunc(), -3.0); - /// # } - /// ``` - #[inline] - #[doc(alias = "truncate")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn trunc(self) -> f16 { - unsafe { intrinsics::truncf16(self) } - } - - /// Returns the fractional part of `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let x = 3.6_f16; - /// let y = -3.6_f16; - /// let abs_difference_x = (x.fract() - 0.6).abs(); - /// let abs_difference_y = (y.fract() - (-0.6)).abs(); - /// - /// assert!(abs_difference_x <= f16::EPSILON); - /// assert!(abs_difference_y <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn fract(self) -> f16 { - self - self.trunc() - } - - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding - /// error, yielding a more accurate result than an unfused multiply-add. - /// - /// Using `mul_add` *may* be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. However, - /// this is not always true, and will be heavily dependant on designing - /// algorithms with specific target hardware in mind. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as - /// `fusedMultiplyAdd` and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let m = 10.0_f16; - /// let x = 4.0_f16; - /// let b = 60.0_f16; - /// - /// assert_eq!(m.mul_add(x, b), 100.0); - /// assert_eq!(m * x + b, 100.0); - /// - /// let one_plus_eps = 1.0_f16 + f16::EPSILON; - /// let one_minus_eps = 1.0_f16 - f16::EPSILON; - /// let minus_one = -1.0_f16; - /// - /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. - /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f16::EPSILON * f16::EPSILON); - /// // Different rounding with the non-fused multiply and add. - /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn mul_add(self, a: f16, b: f16) -> f16 { - unsafe { intrinsics::fmaf16(self, a, b) } - } - - /// Calculates Euclidean division, the matching method for `rem_euclid`. - /// - /// This computes the integer `n` such that - /// `self = n * rhs + self.rem_euclid(rhs)`. - /// In other words, the result is `self / rhs` rounded to the integer `n` - /// such that `self >= n * rhs`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let a: f16 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 - /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn div_euclid(self, rhs: f16) -> f16 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; - } - q - } - - /// Calculates the least nonnegative remainder of `self (mod rhs)`. - /// - /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in - /// most cases. However, due to a floating point round-off error it can - /// result in `r == rhs.abs()`, violating the mathematical definition, if - /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. - /// This result is not an element of the function's codomain, but it is the - /// closest floating point number in the real numbers and thus fulfills the - /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` - /// approximately. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let a: f16 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.rem_euclid(b), 3.0); - /// assert_eq!((-a).rem_euclid(b), 1.0); - /// assert_eq!(a.rem_euclid(-b), 3.0); - /// assert_eq!((-a).rem_euclid(-b), 1.0); - /// // limitation due to round-off error - /// assert!((-f16::EPSILON).rem_euclid(3.0) != 0.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[doc(alias = "modulo", alias = "mod")] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn rem_euclid(self, rhs: f16) -> f16 { - let r = self % rhs; - if r < 0.0 { r + rhs.abs() } else { r } - } - - /// Raises a number to an integer power. - /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let x = 2.0_f16; - /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// assert!(abs_difference <= f16::EPSILON); - /// - /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f16 { - unsafe { intrinsics::powif16(self, n) } - } - /// Raises a number to a floating point power. /// /// # Unspecified precision @@ -354,7 +25,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = 2.0_f16; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); @@ -372,40 +46,6 @@ impl f16 { unsafe { intrinsics::powf16(self, n) } } - /// Returns the square root of a number. - /// - /// Returns NaN if `self` is a negative number other than `-0.0`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` - /// and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let positive = 4.0_f16; - /// let negative = -4.0_f16; - /// let negative_zero = -0.0_f16; - /// - /// assert_eq!(positive.sqrt(), 2.0); - /// assert!(negative.sqrt().is_nan()); - /// assert!(negative_zero.sqrt() == negative_zero); - /// # } - /// ``` - #[inline] - #[doc(alias = "squareRoot")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn sqrt(self) -> f16 { - unsafe { intrinsics::sqrtf16(self) } - } - /// Returns `e^(self)`, (the exponential function). /// /// # Unspecified precision @@ -417,7 +57,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let one = 1.0f16; /// // e^1 @@ -448,7 +91,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let f = 2.0f16; /// @@ -468,6 +114,8 @@ impl f16 { /// Returns the natural logarithm of the number. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, @@ -477,7 +125,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let one = 1.0f16; /// // e^1 @@ -489,6 +140,19 @@ impl f16 { /// assert!(abs_difference <= f16::EPSILON); /// # } /// ``` + /// + /// Non-positive values: + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// assert_eq!(0_f16.ln(), f16::NEG_INFINITY); + /// assert!((-42_f16).ln().is_nan()); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] @@ -499,6 +163,8 @@ impl f16 { /// Returns the logarithm of the number with respect to an arbitrary base. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// The result might not be correctly rounded owing to implementation details; /// `self.log2()` can produce more accurate results for base 2, and /// `self.log10()` can produce more accurate results for base 10. @@ -512,7 +178,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let five = 5.0f16; /// @@ -522,6 +191,19 @@ impl f16 { /// assert!(abs_difference <= f16::EPSILON); /// # } /// ``` + /// + /// Non-positive values: + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// assert_eq!(0_f16.log(10.0), f16::NEG_INFINITY); + /// assert!((-42_f16).log(10.0).is_nan()); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] @@ -532,6 +214,8 @@ impl f16 { /// Returns the base 2 logarithm of the number. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, @@ -541,7 +225,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let two = 2.0f16; /// @@ -551,6 +238,19 @@ impl f16 { /// assert!(abs_difference <= f16::EPSILON); /// # } /// ``` + /// + /// Non-positive values: + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// assert_eq!(0_f16.log2(), f16::NEG_INFINITY); + /// assert!((-42_f16).log2().is_nan()); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] @@ -561,6 +261,8 @@ impl f16 { /// Returns the base 10 logarithm of the number. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, @@ -570,7 +272,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let ten = 10.0f16; /// @@ -580,44 +285,25 @@ impl f16 { /// assert!(abs_difference <= f16::EPSILON); /// # } /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn log10(self) -> f16 { - unsafe { intrinsics::log10f16(self) } - } - - /// Returns the cube root of a number. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `cbrtf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples /// + /// Non-positive values: /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { - /// - /// let x = 8.0f16; - /// - /// // x^(1/3) - 2 == 0 - /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// - /// assert!(abs_difference <= f16::EPSILON); + /// assert_eq!(0_f16.log10(), f16::NEG_INFINITY); + /// assert!((-42_f16).log10().is_nan()); /// # } /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn cbrt(self) -> f16 { - (unsafe { cmath::cbrtf(self as f32) }) as f16 + pub fn log10(self) -> f16 { + unsafe { intrinsics::log10f16(self) } } /// Compute the distance between the origin and a point (`x`, `y`) on the @@ -637,7 +323,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = 2.0f16; /// let y = 3.0f16; @@ -653,7 +342,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn hypot(self, other: f16) -> f16 { - (unsafe { cmath::hypotf(self as f32, other as f32) }) as f16 + cmath::hypotf(self as f32, other as f32) as f16 } /// Computes the sine of a number (in radians). @@ -667,7 +356,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = std::f16::consts::FRAC_PI_2; /// @@ -695,7 +387,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = 2.0 * std::f16::consts::PI; /// @@ -726,7 +421,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = std::f16::consts::FRAC_PI_4; /// let abs_difference = (x.tan() - 1.0).abs(); @@ -739,7 +437,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn tan(self) -> f16 { - (unsafe { cmath::tanf(self as f32) }) as f16 + cmath::tanf(self as f32) as f16 } /// Computes the arcsine of a number. Return value is in radians in @@ -758,7 +456,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let f = std::f16::consts::FRAC_PI_2; /// @@ -774,7 +475,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn asin(self) -> f16 { - (unsafe { cmath::asinf(self as f32) }) as f16 + cmath::asinf(self as f32) as f16 } /// Computes the arccosine of a number. Return value is in radians in @@ -793,7 +494,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let f = std::f16::consts::FRAC_PI_4; /// @@ -809,7 +513,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn acos(self) -> f16 { - (unsafe { cmath::acosf(self as f32) }) as f16 + cmath::acosf(self as f32) as f16 } /// Computes the arctangent of a number. Return value is in radians in the @@ -827,7 +531,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let f = 1.0f16; /// @@ -843,7 +550,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn atan(self) -> f16 { - (unsafe { cmath::atanf(self as f32) }) as f16 + cmath::atanf(self as f32) as f16 } /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. @@ -865,7 +572,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// // Positive angles measured counter-clockwise /// // from positive x axis @@ -889,7 +599,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn atan2(self, other: f16) -> f16 { - (unsafe { cmath::atan2f(self as f32, other as f32) }) as f16 + cmath::atan2f(self as f32, other as f32) as f16 } /// Simultaneously computes the sine and cosine of the number, `x`. Returns @@ -907,7 +617,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = std::f16::consts::FRAC_PI_4; /// let f = x.sin_cos(); @@ -942,7 +655,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = 1e-4_f16; /// @@ -958,12 +674,14 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn exp_m1(self) -> f16 { - (unsafe { cmath::expm1f(self as f32) }) as f16 + cmath::expm1f(self as f32) as f16 } /// Returns `ln(1+n)` (natural logarithm) more accurately than if /// the operations were performed separately. /// + /// This returns NaN when `n < -1.0`, and negative infinity when `n == -1.0`. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, @@ -976,7 +694,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = 1e-4_f16; /// @@ -987,13 +708,26 @@ impl f16 { /// assert!(abs_difference < 1e-4); /// # } /// ``` + /// + /// Out-of-range values: + /// ``` + /// #![feature(f16)] + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { + /// + /// assert_eq!((-1.0_f16).ln_1p(), f16::NEG_INFINITY); + /// assert!((-2.0_f16).ln_1p().is_nan()); + /// # } + /// ``` #[inline] #[doc(alias = "log1p")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn ln_1p(self) -> f16 { - (unsafe { cmath::log1pf(self as f32) }) as f16 + cmath::log1pf(self as f32) as f16 } /// Hyperbolic sine function. @@ -1010,7 +744,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let e = std::f16::consts::E; /// let x = 1.0f16; @@ -1028,7 +765,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn sinh(self) -> f16 { - (unsafe { cmath::sinhf(self as f32) }) as f16 + cmath::sinhf(self as f32) as f16 } /// Hyperbolic cosine function. @@ -1045,7 +782,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let e = std::f16::consts::E; /// let x = 1.0f16; @@ -1063,7 +803,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn cosh(self) -> f16 { - (unsafe { cmath::coshf(self as f32) }) as f16 + cmath::coshf(self as f32) as f16 } /// Hyperbolic tangent function. @@ -1080,7 +820,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let e = std::f16::consts::E; /// let x = 1.0f16; @@ -1098,7 +841,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn tanh(self) -> f16 { - (unsafe { cmath::tanhf(self as f32) }) as f16 + cmath::tanhf(self as f32) as f16 } /// Inverse hyperbolic sine function. @@ -1112,7 +855,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = 1.0f16; /// let f = x.sinh().asinh(); @@ -1144,7 +890,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = 1.0f16; /// let f = x.cosh().acosh(); @@ -1178,7 +927,10 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let e = std::f16::consts::E; /// let f = e.tanh().atanh(); @@ -1212,7 +964,10 @@ impl f16 { /// ``` /// #![feature(f16)] /// #![feature(float_gamma)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = 5.0f16; /// @@ -1227,7 +982,7 @@ impl f16 { // #[unstable(feature = "float_gamma", issue = "99842")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn gamma(self) -> f16 { - (unsafe { cmath::tgammaf(self as f32) }) as f16 + cmath::tgammaf(self as f32) as f16 } /// Natural logarithm of the absolute value of the gamma function @@ -1247,7 +1002,10 @@ impl f16 { /// ``` /// #![feature(f16)] /// #![feature(float_gamma)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = 2.0f16; /// @@ -1263,7 +1021,7 @@ impl f16 { #[must_use = "method returns a new number and does not mutate the original value"] pub fn ln_gamma(self) -> (f16, i32) { let mut signgamp: i32 = 0; - let x = (unsafe { cmath::lgammaf_r(self as f32, &mut signgamp) }) as f16; + let x = cmath::lgammaf_r(self as f32, &mut signgamp) as f16; (x, signgamp) } @@ -1282,7 +1040,10 @@ impl f16 { /// ``` /// #![feature(f16)] /// #![feature(float_erf)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// /// The error function relates what percent of a normal distribution lies /// /// within `x` standard deviations (scaled by `1/sqrt(2)`). /// fn within_standard_deviations(x: f16) -> f16 { @@ -1303,7 +1064,7 @@ impl f16 { // #[unstable(feature = "float_erf", issue = "136321")] #[inline] pub fn erf(self) -> f16 { - (unsafe { cmath::erff(self as f32) }) as f16 + cmath::erff(self as f32) as f16 } /// Complementary error function. @@ -1321,7 +1082,10 @@ impl f16 { /// ``` /// #![feature(f16)] /// #![feature(float_erf)] - /// # #[cfg(reliable_f16_math)] { + /// # #![feature(cfg_target_has_reliable_f16_f128)] + /// # #![expect(internal_features)] + /// # #[cfg(not(miri))] + /// # #[cfg(target_has_reliable_f16_math)] { /// let x: f16 = 0.123; /// /// let one = x.erf() + x.erfc(); @@ -1336,6 +1100,6 @@ impl f16 { // #[unstable(feature = "float_erf", issue = "136321")] #[inline] pub fn erfc(self) -> f16 { - (unsafe { cmath::erfcf(self as f32) }) as f16 + cmath::erfcf(self as f32) as f16 } } diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 19fb24c8ee26c..5210e75ec4531 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -46,7 +46,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn floor(self) -> f32 { - unsafe { intrinsics::floorf32(self) } + core::f32::math::floor(self) } /// Returns the smallest integer greater than or equal to `self`. @@ -68,7 +68,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn ceil(self) -> f32 { - unsafe { intrinsics::ceilf32(self) } + core::f32::math::ceil(self) } /// Returns the nearest integer to `self`. If a value is half-way between two @@ -96,7 +96,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn round(self) -> f32 { - unsafe { intrinsics::roundf32(self) } + core::f32::math::round(self) } /// Returns the nearest integer to a number. Rounds half-way cases to the number @@ -122,7 +122,7 @@ impl f32 { #[stable(feature = "round_ties_even", since = "1.77.0")] #[inline] pub fn round_ties_even(self) -> f32 { - intrinsics::round_ties_even_f32(self) + core::f32::math::round_ties_even(self) } /// Returns the integer part of `self`. @@ -147,7 +147,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn trunc(self) -> f32 { - unsafe { intrinsics::truncf32(self) } + core::f32::math::trunc(self) } /// Returns the fractional part of `self`. @@ -170,7 +170,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn fract(self) -> f32 { - self - self.trunc() + core::f32::math::fract(self) } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding @@ -212,7 +212,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn mul_add(self, a: f32, b: f32) -> f32 { - unsafe { intrinsics::fmaf32(self, a, b) } + core::f32::math::mul_add(self, a, b) } /// Calculates Euclidean division, the matching method for `rem_euclid`. @@ -242,11 +242,7 @@ impl f32 { #[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 + core::f32::math::div_euclid(self, rhs) } /// Calculates the least nonnegative remainder of `self (mod rhs)`. @@ -283,8 +279,7 @@ impl f32 { #[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 } + core::f32::math::rem_euclid(self, rhs) } /// Raises a number to an integer power. @@ -312,7 +307,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn powi(self, n: i32) -> f32 { - unsafe { intrinsics::powif32(self, n) } + core::f32::math::powi(self, n) } /// Raises a number to a floating point power. @@ -367,7 +362,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sqrt(self) -> f32 { - unsafe { intrinsics::sqrtf32(self) } + core::f32::math::sqrt(self) } /// Returns `e^(self)`, (the exponential function). @@ -424,6 +419,8 @@ impl f32 { /// Returns the natural logarithm of the number. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and @@ -441,6 +438,12 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + /// + /// Non-positive values: + /// ``` + /// assert_eq!(0_f32.ln(), f32::NEG_INFINITY); + /// assert!((-42_f32).ln().is_nan()); + /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -451,6 +454,8 @@ impl f32 { /// Returns the logarithm of the number with respect to an arbitrary base. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// The result might not be correctly rounded owing to implementation details; /// `self.log2()` can produce more accurate results for base 2, and /// `self.log10()` can produce more accurate results for base 10. @@ -470,6 +475,12 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + /// + /// Non-positive values: + /// ``` + /// assert_eq!(0_f32.log(10.0), f32::NEG_INFINITY); + /// assert!((-42_f32).log(10.0).is_nan()); + /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -480,6 +491,8 @@ impl f32 { /// Returns the base 2 logarithm of the number. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and @@ -495,6 +508,12 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + /// + /// Non-positive values: + /// ``` + /// assert_eq!(0_f32.log2(), f32::NEG_INFINITY); + /// assert!((-42_f32).log2().is_nan()); + /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -505,6 +524,8 @@ impl f32 { /// Returns the base 10 logarithm of the number. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and @@ -520,6 +541,12 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + /// + /// Non-positive values: + /// ``` + /// assert_eq!(0_f32.log10(), f32::NEG_INFINITY); + /// assert!((-42_f32).log10().is_nan()); + /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -567,7 +594,8 @@ impl f32 { filing an issue describing your use-case too)." )] pub fn abs_sub(self, other: f32) -> f32 { - unsafe { cmath::fdimf(self, other) } + #[allow(deprecated)] + core::f32::math::abs_sub(self, other) } /// Returns the cube root of a number. @@ -594,7 +622,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cbrt(self) -> f32 { - unsafe { cmath::cbrtf(self) } + core::f32::math::cbrt(self) } /// Compute the distance between the origin and a point (`x`, `y`) on the @@ -625,7 +653,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn hypot(self, other: f32) -> f32 { - unsafe { cmath::hypotf(self, other) } + cmath::hypotf(self, other) } /// Computes the sine of a number (in radians). @@ -698,7 +726,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn tan(self) -> f32 { - unsafe { cmath::tanf(self) } + cmath::tanf(self) } /// Computes the arcsine of a number. Return value is in radians in @@ -728,7 +756,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asin(self) -> f32 { - unsafe { cmath::asinf(self) } + cmath::asinf(self) } /// Computes the arccosine of a number. Return value is in radians in @@ -758,7 +786,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn acos(self) -> f32 { - unsafe { cmath::acosf(self) } + cmath::acosf(self) } /// Computes the arctangent of a number. Return value is in radians in the @@ -787,7 +815,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn atan(self) -> f32 { - unsafe { cmath::atanf(self) } + cmath::atanf(self) } /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. @@ -828,7 +856,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn atan2(self, other: f32) -> f32 { - unsafe { cmath::atan2f(self, other) } + cmath::atan2f(self, other) } /// Simultaneously computes the sine and cosine of the number, `x`. Returns @@ -887,12 +915,14 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn exp_m1(self) -> f32 { - unsafe { cmath::expm1f(self) } + cmath::expm1f(self) } /// Returns `ln(1+n)` (natural logarithm) more accurately than if /// the operations were performed separately. /// + /// This returns NaN when `n < -1.0`, and negative infinity when `n == -1.0`. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and @@ -911,13 +941,19 @@ impl f32 { /// /// assert!(abs_difference < 1e-10); /// ``` + /// + /// Out-of-range values: + /// ``` + /// assert_eq!((-1.0_f32).ln_1p(), f32::NEG_INFINITY); + /// assert!((-2.0_f32).ln_1p().is_nan()); + /// ``` #[doc(alias = "log1p")] #[rustc_allow_incoherent_impl] #[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) } + cmath::log1pf(self) } /// Hyperbolic sine function. @@ -947,7 +983,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sinh(self) -> f32 { - unsafe { cmath::sinhf(self) } + cmath::sinhf(self) } /// Hyperbolic cosine function. @@ -977,7 +1013,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cosh(self) -> f32 { - unsafe { cmath::coshf(self) } + cmath::coshf(self) } /// Hyperbolic tangent function. @@ -1007,7 +1043,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn tanh(self) -> f32 { - unsafe { cmath::tanhf(self) } + cmath::tanhf(self) } /// Inverse hyperbolic sine function. @@ -1118,7 +1154,7 @@ impl f32 { #[unstable(feature = "float_gamma", issue = "99842")] #[inline] pub fn gamma(self) -> f32 { - unsafe { cmath::tgammaf(self) } + cmath::tgammaf(self) } /// Natural logarithm of the absolute value of the gamma function @@ -1148,7 +1184,7 @@ impl f32 { #[inline] pub fn ln_gamma(self) -> (f32, i32) { let mut signgamp: i32 = 0; - let x = unsafe { cmath::lgammaf_r(self, &mut signgamp) }; + let x = cmath::lgammaf_r(self, &mut signgamp); (x, signgamp) } @@ -1184,7 +1220,7 @@ impl f32 { #[unstable(feature = "float_erf", issue = "136321")] #[inline] pub fn erf(self) -> f32 { - unsafe { cmath::erff(self) } + cmath::erff(self) } /// Complementary error function. @@ -1213,6 +1249,6 @@ impl f32 { #[unstable(feature = "float_erf", issue = "136321")] #[inline] pub fn erfc(self) -> f32 { - unsafe { cmath::erfcf(self) } + cmath::erfcf(self) } } diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index f1c3cb561271a..f837800d66342 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -46,7 +46,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn floor(self) -> f64 { - unsafe { intrinsics::floorf64(self) } + core::f64::math::floor(self) } /// Returns the smallest integer greater than or equal to `self`. @@ -68,7 +68,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn ceil(self) -> f64 { - unsafe { intrinsics::ceilf64(self) } + core::f64::math::ceil(self) } /// Returns the nearest integer to `self`. If a value is half-way between two @@ -96,7 +96,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn round(self) -> f64 { - unsafe { intrinsics::roundf64(self) } + core::f64::math::round(self) } /// Returns the nearest integer to a number. Rounds half-way cases to the number @@ -122,7 +122,7 @@ impl f64 { #[stable(feature = "round_ties_even", since = "1.77.0")] #[inline] pub fn round_ties_even(self) -> f64 { - intrinsics::round_ties_even_f64(self) + core::f64::math::round_ties_even(self) } /// Returns the integer part of `self`. @@ -147,7 +147,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn trunc(self) -> f64 { - unsafe { intrinsics::truncf64(self) } + core::f64::math::trunc(self) } /// Returns the fractional part of `self`. @@ -170,7 +170,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn fract(self) -> f64 { - self - self.trunc() + core::f64::math::fract(self) } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding @@ -212,7 +212,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn mul_add(self, a: f64, b: f64) -> f64 { - unsafe { intrinsics::fmaf64(self, a, b) } + core::f64::math::mul_add(self, a, b) } /// Calculates Euclidean division, the matching method for `rem_euclid`. @@ -242,11 +242,7 @@ impl f64 { #[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 + core::f64::math::div_euclid(self, rhs) } /// Calculates the least nonnegative remainder of `self (mod rhs)`. @@ -283,8 +279,7 @@ impl f64 { #[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 } + core::f64::math::rem_euclid(self, rhs) } /// Raises a number to an integer power. @@ -312,7 +307,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn powi(self, n: i32) -> f64 { - unsafe { intrinsics::powif64(self, n) } + core::f64::math::powi(self, n) } /// Raises a number to a floating point power. @@ -367,7 +362,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sqrt(self) -> f64 { - unsafe { intrinsics::sqrtf64(self) } + core::f64::math::sqrt(self) } /// Returns `e^(self)`, (the exponential function). @@ -424,6 +419,8 @@ impl f64 { /// Returns the natural logarithm of the number. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and @@ -441,6 +438,12 @@ impl f64 { /// /// assert!(abs_difference < 1e-10); /// ``` + /// + /// Non-positive values: + /// ``` + /// assert_eq!(0_f64.ln(), f64::NEG_INFINITY); + /// assert!((-42_f64).ln().is_nan()); + /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -451,6 +454,8 @@ impl f64 { /// Returns the logarithm of the number with respect to an arbitrary base. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// The result might not be correctly rounded owing to implementation details; /// `self.log2()` can produce more accurate results for base 2, and /// `self.log10()` can produce more accurate results for base 10. @@ -470,6 +475,12 @@ impl f64 { /// /// assert!(abs_difference < 1e-10); /// ``` + /// + /// Non-positive values: + /// ``` + /// assert_eq!(0_f64.log(10.0), f64::NEG_INFINITY); + /// assert!((-42_f64).log(10.0).is_nan()); + /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -480,6 +491,8 @@ impl f64 { /// Returns the base 2 logarithm of the number. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and @@ -495,6 +508,12 @@ impl f64 { /// /// assert!(abs_difference < 1e-10); /// ``` + /// + /// Non-positive values: + /// ``` + /// assert_eq!(0_f64.log2(), f64::NEG_INFINITY); + /// assert!((-42_f64).log2().is_nan()); + /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -505,6 +524,8 @@ impl f64 { /// Returns the base 10 logarithm of the number. /// + /// This returns NaN when the number is negative, and negative infinity when number is zero. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and @@ -520,6 +541,12 @@ impl f64 { /// /// assert!(abs_difference < 1e-10); /// ``` + /// + /// Non-positive values: + /// ``` + /// assert_eq!(0_f64.log10(), f64::NEG_INFINITY); + /// assert!((-42_f64).log10().is_nan()); + /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -567,7 +594,8 @@ impl f64 { filing an issue describing your use-case too)." )] pub fn abs_sub(self, other: f64) -> f64 { - unsafe { cmath::fdim(self, other) } + #[allow(deprecated)] + core::f64::math::abs_sub(self, other) } /// Returns the cube root of a number. @@ -594,7 +622,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cbrt(self) -> f64 { - unsafe { cmath::cbrt(self) } + core::f64::math::cbrt(self) } /// Compute the distance between the origin and a point (`x`, `y`) on the @@ -625,7 +653,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn hypot(self, other: f64) -> f64 { - unsafe { cmath::hypot(self, other) } + cmath::hypot(self, other) } /// Computes the sine of a number (in radians). @@ -698,7 +726,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn tan(self) -> f64 { - unsafe { cmath::tan(self) } + cmath::tan(self) } /// Computes the arcsine of a number. Return value is in radians in @@ -728,7 +756,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asin(self) -> f64 { - unsafe { cmath::asin(self) } + cmath::asin(self) } /// Computes the arccosine of a number. Return value is in radians in @@ -758,7 +786,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn acos(self) -> f64 { - unsafe { cmath::acos(self) } + cmath::acos(self) } /// Computes the arctangent of a number. Return value is in radians in the @@ -787,7 +815,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn atan(self) -> f64 { - unsafe { cmath::atan(self) } + cmath::atan(self) } /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. @@ -828,7 +856,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn atan2(self, other: f64) -> f64 { - unsafe { cmath::atan2(self, other) } + cmath::atan2(self, other) } /// Simultaneously computes the sine and cosine of the number, `x`. Returns @@ -887,12 +915,14 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn exp_m1(self) -> f64 { - unsafe { cmath::expm1(self) } + cmath::expm1(self) } /// Returns `ln(1+n)` (natural logarithm) more accurately than if /// the operations were performed separately. /// + /// This returns NaN when `n < -1.0`, and negative infinity when `n == -1.0`. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and @@ -911,13 +941,19 @@ impl f64 { /// /// assert!(abs_difference < 1e-20); /// ``` + /// + /// Out-of-range values: + /// ``` + /// assert_eq!((-1.0_f64).ln_1p(), f64::NEG_INFINITY); + /// assert!((-2.0_f64).ln_1p().is_nan()); + /// ``` #[doc(alias = "log1p")] #[rustc_allow_incoherent_impl] #[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) } + cmath::log1p(self) } /// Hyperbolic sine function. @@ -947,7 +983,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sinh(self) -> f64 { - unsafe { cmath::sinh(self) } + cmath::sinh(self) } /// Hyperbolic cosine function. @@ -977,7 +1013,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cosh(self) -> f64 { - unsafe { cmath::cosh(self) } + cmath::cosh(self) } /// Hyperbolic tangent function. @@ -1007,7 +1043,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn tanh(self) -> f64 { - unsafe { cmath::tanh(self) } + cmath::tanh(self) } /// Inverse hyperbolic sine function. @@ -1118,7 +1154,7 @@ impl f64 { #[unstable(feature = "float_gamma", issue = "99842")] #[inline] pub fn gamma(self) -> f64 { - unsafe { cmath::tgamma(self) } + cmath::tgamma(self) } /// Natural logarithm of the absolute value of the gamma function @@ -1148,7 +1184,7 @@ impl f64 { #[inline] pub fn ln_gamma(self) -> (f64, i32) { let mut signgamp: i32 = 0; - let x = unsafe { cmath::lgamma_r(self, &mut signgamp) }; + let x = cmath::lgamma_r(self, &mut signgamp); (x, signgamp) } @@ -1184,7 +1220,7 @@ impl f64 { #[unstable(feature = "float_erf", issue = "136321")] #[inline] pub fn erf(self) -> f64 { - unsafe { cmath::erf(self) } + cmath::erf(self) } /// Complementary error function. @@ -1213,6 +1249,6 @@ impl f64 { #[unstable(feature = "float_erf", issue = "136321")] #[inline] pub fn erfc(self) -> f64 { - unsafe { cmath::erfc(self) } + cmath::erfc(self) } } diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index 860ec3a6be16e..024cb71b915d7 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -161,7 +161,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[unstable(feature = "c_str_module", issue = "112134")] +#[stable(feature = "c_str_module", since = "1.88.0")] pub mod c_str; #[stable(feature = "core_c_void", since = "1.30.0")] @@ -172,7 +172,7 @@ pub use core::ffi::c_void; all supported platforms", issue = "44930" )] -pub use core::ffi::{VaList, VaListImpl}; +pub use core::ffi::{VaArgSafe, VaList, VaListImpl}; #[stable(feature = "core_ffi_c", since = "1.64.0")] pub use core::ffi::{ c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, @@ -201,5 +201,5 @@ pub use self::c_str::{CStr, CString}; #[doc(inline)] pub use self::os_str::{OsStr, OsString}; -#[stable(feature = "os_str_display", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "os_str_display", since = "1.87.0")] pub mod os_str; diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index f4a02802336d5..ead4877512727 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -257,7 +257,30 @@ impl OsString { #[inline] #[rustc_confusables("append", "put")] pub fn push>(&mut self, s: T) { - self.inner.push_slice(&s.as_ref().inner) + trait SpecPushTo { + fn spec_push_to(&self, buf: &mut OsString); + } + + impl> SpecPushTo for T { + #[inline] + default fn spec_push_to(&self, buf: &mut OsString) { + buf.inner.push_slice(&self.as_ref().inner); + } + } + + // Use a more efficient implementation when the string is UTF-8. + macro spec_str($T:ty) { + impl SpecPushTo for $T { + #[inline] + fn spec_push_to(&self, buf: &mut OsString) { + buf.inner.push_str(self); + } + } + } + spec_str!(str); + spec_str!(String); + + s.spec_push_to(self) } /// Creates a new `OsString` with at least the given capacity. @@ -559,15 +582,25 @@ impl OsString { #[unstable(feature = "os_string_truncate", issue = "133262")] pub fn truncate(&mut self, len: usize) { self.as_os_str().inner.check_public_boundary(len); - self.inner.truncate(len); + // SAFETY: The length was just checked to be at a valid boundary. + unsafe { self.inner.truncate_unchecked(len) }; } - /// Provides plumbing to core `Vec::extend_from_slice`. - /// More well behaving alternative to allowing outer types - /// full mutable access to the core `Vec`. + /// Provides plumbing to `Vec::extend_from_slice` without giving full + /// mutable access to the `Vec`. + /// + /// # Safety + /// + /// The slice must be valid for the platform encoding (as described in + /// [`OsStr::from_encoded_bytes_unchecked`]). + /// + /// This bypasses the encoding-dependent surrogate joining, so `self` must + /// not end with a leading surrogate half and `other` must not start with + /// with a trailing surrogate half. #[inline] - pub(crate) fn extend_from_slice(&mut self, other: &[u8]) { - self.inner.extend_from_slice(other); + pub(crate) unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { + // SAFETY: Guaranteed by caller. + unsafe { self.inner.extend_from_slice_unchecked(other) }; } } @@ -587,7 +620,30 @@ impl> From<&T> for OsString { /// Copies any value implementing [AsRef]<[OsStr]> /// into a newly allocated [`OsString`]. fn from(s: &T) -> OsString { - s.as_ref().to_os_string() + trait SpecToOsString { + fn spec_to_os_string(&self) -> OsString; + } + + impl> SpecToOsString for T { + #[inline] + default fn spec_to_os_string(&self) -> OsString { + self.as_ref().to_os_string() + } + } + + // Preserve the known-UTF-8 property for strings. + macro spec_str($T:ty) { + impl SpecToOsString for $T { + #[inline] + fn spec_to_os_string(&self) -> OsString { + OsString::from(String::from(self)) + } + } + } + spec_str!(str); + spec_str!(String); + + s.spec_to_os_string() } } @@ -984,7 +1040,7 @@ impl OsStr { /// Converts a [Box]<[OsStr]> into an [`OsString`] without copying or allocating. #[stable(feature = "into_boxed_os_str", since = "1.20.0")] #[must_use = "`self` will be dropped if the result is not used"] - pub fn into_os_string(self: Box) -> OsString { + 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) } } @@ -1209,7 +1265,7 @@ impl OsStr { /// let s = OsStr::new("Hello, world!"); /// println!("{}", s.display()); /// ``` - #[stable(feature = "os_str_display", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "os_str_display", since = "1.87.0")] #[must_use = "this does not display the `OsStr`; \ it returns an object that can be displayed"] #[inline] @@ -1566,19 +1622,19 @@ impl fmt::Debug for OsStr { /// /// [`Display`]: fmt::Display /// [`format!`]: crate::format -#[stable(feature = "os_str_display", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "os_str_display", since = "1.87.0")] pub struct Display<'a> { os_str: &'a OsStr, } -#[stable(feature = "os_str_display", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "os_str_display", since = "1.87.0")] impl fmt::Debug for Display<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.os_str, f) } } -#[stable(feature = "os_str_display", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "os_str_display", since = "1.87.0")] impl fmt::Display for Display<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.os_str.inner, f) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 57e235c3efe1d..509e673bdb8b9 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -14,13 +14,13 @@ target_os = "emscripten", target_os = "wasi", target_env = "sgx", - target_os = "xous" + target_os = "xous", + target_os = "trusty", )) ))] mod tests; use crate::ffi::OsString; -use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; use crate::path::{Path, PathBuf}; use crate::sealed::Sealed; @@ -28,6 +28,7 @@ use crate::sync::Arc; use crate::sys::fs as fs_imp; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::time::SystemTime; +use crate::{error, fmt}; /// An object providing access to an open file on the filesystem. /// @@ -115,6 +116,22 @@ pub struct File { inner: fs_imp::File, } +/// An enumeration of possible errors which can occur while trying to acquire a lock +/// from the [`try_lock`] method and [`try_lock_shared`] method on a [`File`]. +/// +/// [`try_lock`]: File::try_lock +/// [`try_lock_shared`]: File::try_lock_shared +#[unstable(feature = "file_lock", issue = "130994")] +pub enum TryLockError { + /// The lock could not be acquired due to an I/O error on the file. The standard library will + /// not return an [`ErrorKind::WouldBlock`] error inside [`TryLockError::Error`] + /// + /// [`ErrorKind::WouldBlock`]: io::ErrorKind::WouldBlock + Error(io::Error), + /// The lock could not be acquired at this time because it is held by another handle/process. + WouldBlock, +} + /// Metadata information about a file. /// /// This structure is returned from the [`metadata`] or @@ -268,8 +285,7 @@ pub fn read>(path: P) -> io::Result> { fn inner(path: &Path) -> io::Result> { let mut file = File::open(path)?; let size = file.metadata().map(|m| m.len() as usize).ok(); - let mut bytes = Vec::new(); - bytes.try_reserve_exact(size.unwrap_or(0))?; + let mut bytes = Vec::try_with_capacity(size.unwrap_or(0))?; io::default_read_to_end(&mut file, &mut bytes, size)?; Ok(bytes) } @@ -351,6 +367,30 @@ pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result inner(path.as_ref(), contents.as_ref()) } +#[unstable(feature = "file_lock", issue = "130994")] +impl error::Error for TryLockError {} + +#[unstable(feature = "file_lock", issue = "130994")] +impl fmt::Debug for TryLockError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TryLockError::Error(err) => err.fmt(f), + TryLockError::WouldBlock => "WouldBlock".fmt(f), + } + } +} + +#[unstable(feature = "file_lock", issue = "130994")] +impl fmt::Display for TryLockError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TryLockError::Error(_) => "lock acquisition failed due to I/O error", + TryLockError::WouldBlock => "lock acquisition failed because the operation would block", + } + .fmt(f) + } +} + impl File { /// Attempts to open a file in read-only mode. /// @@ -664,6 +704,7 @@ impl File { /// # Examples /// /// ```no_run + /// #![feature(file_lock)] /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { @@ -672,7 +713,7 @@ impl File { /// Ok(()) /// } /// ``` - #[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")] + #[unstable(feature = "file_lock", issue = "130994")] pub fn lock(&self) -> io::Result<()> { self.inner.lock() } @@ -716,6 +757,7 @@ impl File { /// # Examples /// /// ```no_run + /// #![feature(file_lock)] /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { @@ -724,15 +766,15 @@ impl File { /// Ok(()) /// } /// ``` - #[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")] + #[unstable(feature = "file_lock", issue = "130994")] pub fn lock_shared(&self) -> io::Result<()> { self.inner.lock_shared() } /// Try to acquire an exclusive lock on the file. /// - /// Returns `Ok(false)` if a different lock is already held on this file (via another - /// handle/descriptor). + /// Returns `Err(TryLockError::WouldBlock)` if a different lock is already held on this file + /// (via another handle/descriptor). /// /// This acquires an exclusive lock; no other file handle to this file may acquire another lock. /// @@ -773,23 +815,28 @@ impl File { /// # Examples /// /// ```no_run - /// use std::fs::File; + /// #![feature(file_lock)] + /// use std::fs::{File, TryLockError}; /// /// fn main() -> std::io::Result<()> { /// let f = File::create("foo.txt")?; - /// f.try_lock()?; + /// match f.try_lock() { + /// Ok(_) => (), + /// Err(TryLockError::WouldBlock) => (), // Lock not acquired + /// Err(TryLockError::Error(err)) => return Err(err), + /// } /// Ok(()) /// } /// ``` - #[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")] - pub fn try_lock(&self) -> io::Result { + #[unstable(feature = "file_lock", issue = "130994")] + pub fn try_lock(&self) -> Result<(), TryLockError> { self.inner.try_lock() } /// Try to acquire a shared (non-exclusive) lock on the file. /// - /// Returns `Ok(false)` if an exclusive lock is already held on this file (via another - /// handle/descriptor). + /// Returns `Err(TryLockError::WouldBlock)` if a different lock is already held on this file + /// (via another handle/descriptor). /// /// This acquires a shared lock; more than one file handle may hold a shared lock, but none may /// hold an exclusive lock at the same time. @@ -829,16 +876,22 @@ impl File { /// # Examples /// /// ```no_run - /// use std::fs::File; + /// #![feature(file_lock)] + /// use std::fs::{File, TryLockError}; /// /// fn main() -> std::io::Result<()> { /// let f = File::open("foo.txt")?; - /// f.try_lock_shared()?; + /// match f.try_lock_shared() { + /// Ok(_) => (), + /// Err(TryLockError::WouldBlock) => (), // Lock not acquired + /// Err(TryLockError::Error(err)) => return Err(err), + /// } + /// /// Ok(()) /// } /// ``` - #[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")] - pub fn try_lock_shared(&self) -> io::Result { + #[unstable(feature = "file_lock", issue = "130994")] + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { self.inner.try_lock_shared() } @@ -865,6 +918,7 @@ impl File { /// # Examples /// /// ```no_run + /// #![feature(file_lock)] /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { @@ -874,7 +928,7 @@ impl File { /// Ok(()) /// } /// ``` - #[stable(feature = "file_lock", since = "CURRENT_RUSTC_VERSION")] + #[unstable(feature = "file_lock", issue = "130994")] pub fn unlock(&self) -> io::Result<()> { self.inner.unlock() } @@ -1342,6 +1396,9 @@ impl Seek for Arc { fn seek(&mut self, pos: SeekFrom) -> io::Result { (&**self).seek(pos) } + fn stream_position(&mut self) -> io::Result { + (&**self).stream_position() + } } impl OpenOptions { @@ -2361,7 +2418,7 @@ impl AsInner for DirEntry { #[doc(alias = "rm", alias = "unlink", alias = "DeleteFile")] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove_file>(path: P) -> io::Result<()> { - fs_imp::unlink(path.as_ref()) + fs_imp::remove_file(path.as_ref()) } /// Given a path, queries the file system to get information about a file, @@ -2400,7 +2457,7 @@ pub fn remove_file>(path: P) -> io::Result<()> { #[doc(alias = "stat")] #[stable(feature = "rust1", since = "1.0.0")] pub fn metadata>(path: P) -> io::Result { - fs_imp::stat(path.as_ref()).map(Metadata) + fs_imp::metadata(path.as_ref()).map(Metadata) } /// Queries the metadata about a file without following symlinks. @@ -2435,7 +2492,7 @@ pub fn metadata>(path: P) -> io::Result { #[doc(alias = "lstat")] #[stable(feature = "symlink_metadata", since = "1.1.0")] pub fn symlink_metadata>(path: P) -> io::Result { - fs_imp::lstat(path.as_ref()).map(Metadata) + fs_imp::symlink_metadata(path.as_ref()).map(Metadata) } /// Renames a file or directory to a new name, replacing the original file if @@ -2446,7 +2503,7 @@ pub fn symlink_metadata>(path: P) -> io::Result { /// # Platform-specific behavior /// /// This function currently corresponds to the `rename` function on Unix -/// and the `SetFileInformationByHandle` function on Windows. +/// and the `MoveFileExW` or `SetFileInformationByHandle` function 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 @@ -2589,7 +2646,7 @@ pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { #[doc(alias = "CreateHardLink", alias = "linkat")] #[stable(feature = "rust1", since = "1.0.0")] pub fn hard_link, Q: AsRef>(original: P, link: Q) -> io::Result<()> { - fs_imp::link(original.as_ref(), link.as_ref()) + fs_imp::hard_link(original.as_ref(), link.as_ref()) } /// Creates a new symbolic link on the filesystem. @@ -2655,7 +2712,7 @@ pub fn soft_link, Q: AsRef>(original: P, link: Q) -> io::Re /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn read_link>(path: P) -> io::Result { - fs_imp::readlink(path.as_ref()) + fs_imp::read_link(path.as_ref()) } /// Returns the canonical, absolute form of a path with all intermediate @@ -2831,7 +2888,7 @@ pub fn create_dir_all>(path: P) -> io::Result<()> { #[doc(alias = "rmdir", alias = "RemoveDirectory")] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove_dir>(path: P) -> io::Result<()> { - fs_imp::rmdir(path.as_ref()) + fs_imp::remove_dir(path.as_ref()) } /// Removes a directory at this path, after removing all its contents. Use @@ -2857,12 +2914,16 @@ pub fn remove_dir>(path: P) -> io::Result<()> { /// /// See [`fs::remove_file`] and [`fs::remove_dir`]. /// -/// `remove_dir_all` will fail if `remove_dir` or `remove_file` fail on any constituent paths, including the root `path`. -/// As a result, the directory you are deleting must exist, meaning that this function is not idempotent. -/// Additionally, `remove_dir_all` will also fail if the `path` is not a directory. +/// [`remove_dir_all`] will fail if [`remove_dir`] or [`remove_file`] fail on *any* constituent +/// paths, *including* the root `path`. Consequently, +/// +/// - The directory you are deleting *must* exist, meaning that this function is *not idempotent*. +/// - [`remove_dir_all`] will fail if the `path` is *not* a directory. /// /// Consider ignoring the error if validating the removal is not required for your use case. /// +/// This function may return [`io::ErrorKind::DirectoryNotEmpty`] if the directory is concurrently +/// written into, which typically indicates some contents were removed but not all. /// [`io::ErrorKind::NotFound`] is only returned if no removal occurs. /// /// [`fs::remove_file`]: remove_file @@ -2956,7 +3017,7 @@ pub fn remove_dir_all>(path: P) -> io::Result<()> { #[doc(alias = "ls", alias = "opendir", alias = "FindFirstFile", alias = "FindNextFile")] #[stable(feature = "rust1", since = "1.0.0")] pub fn read_dir>(path: P) -> io::Result { - fs_imp::readdir(path.as_ref()).map(ReadDir) + fs_imp::read_dir(path.as_ref()).map(ReadDir) } /// Changes the permissions found on a file or a directory. @@ -2969,6 +3030,21 @@ pub fn read_dir>(path: P) -> io::Result { /// /// [changes]: io#platform-specific-behavior /// +/// ## Symlinks +/// On UNIX-like systems, this function will update the permission bits +/// of the file pointed to by the symlink. +/// +/// Note that this behavior can lead to privalage escalation vulnerabilites, +/// where the ability to create a symlink in one directory allows you to +/// cause the permissions of another file or directory to be modified. +/// +/// For this reason, using this function with symlinks should be avoided. +/// When possible, permissions should be set at creation time instead. +/// +/// # Rationale +/// POSIX does not specify an `lchmod` function, +/// and symlinks can be followed regardless of what permission bits are set. +/// /// # Errors /// /// This function will return an error in the following situations, but is not @@ -2992,7 +3068,7 @@ pub fn read_dir>(path: P) -> io::Result { #[doc(alias = "chmod", alias = "SetFileAttributes")] #[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) + fs_imp::set_permissions(path.as_ref(), perm.0) } impl DirBuilder { diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 38dcd816d267d..c81a5ff4d96e6 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1,6 +1,22 @@ use rand::RngCore; +#[cfg(any( + windows, + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", +))] +use crate::assert_matches::assert_matches; use crate::char::MAX_LEN_UTF8; +#[cfg(any( + windows, + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", +))] +use crate::fs::TryLockError; use crate::fs::{self, File, FileTimes, OpenOptions}; use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind, SeekFrom}; @@ -223,8 +239,8 @@ fn file_lock_multiple_shared() { check!(f2.lock_shared()); check!(f1.unlock()); check!(f2.unlock()); - assert!(check!(f1.try_lock_shared())); - assert!(check!(f2.try_lock_shared())); + check!(f1.try_lock_shared()); + check!(f2.try_lock_shared()); } #[test] @@ -243,12 +259,12 @@ fn file_lock_blocking() { // Check that shared locks block exclusive locks check!(f1.lock_shared()); - assert!(!check!(f2.try_lock())); + assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); check!(f1.unlock()); // Check that exclusive locks block shared locks check!(f1.lock()); - assert!(!check!(f2.try_lock_shared())); + assert_matches!(f2.try_lock_shared(), Err(TryLockError::WouldBlock)); } #[test] @@ -267,9 +283,9 @@ fn file_lock_drop() { // Check that locks are released when the File is dropped check!(f1.lock_shared()); - assert!(!check!(f2.try_lock())); + assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); drop(f1); - assert!(check!(f2.try_lock())); + check!(f2.try_lock()); } #[test] @@ -288,10 +304,10 @@ fn file_lock_dup() { // Check that locks are not dropped if the File has been cloned check!(f1.lock_shared()); - assert!(!check!(f2.try_lock())); + assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); let cloned = check!(f1.try_clone()); drop(f1); - assert!(!check!(f2.try_lock())); + assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); drop(cloned) } @@ -307,9 +323,9 @@ fn file_lock_double_unlock() { // Check that both are released by unlock() check!(f1.lock()); check!(f1.lock_shared()); - assert!(!check!(f2.try_lock())); + assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); check!(f1.unlock()); - assert!(check!(f2.try_lock())); + check!(f2.try_lock()); } #[test] @@ -714,6 +730,10 @@ fn recursive_mkdir_empty() { } #[test] +#[cfg_attr( + all(windows, target_arch = "aarch64"), + ignore = "SymLinks not enabled on Arm64 Windows runners https://github.com/actions/partner-runner-images/issues/94" +)] fn recursive_rmdir() { let tmpdir = tmpdir(); let d1 = tmpdir.join("d1"); @@ -733,6 +753,10 @@ fn recursive_rmdir() { } #[test] +#[cfg_attr( + all(windows, target_arch = "aarch64"), + ignore = "SymLinks not enabled on Arm64 Windows runners https://github.com/actions/partner-runner-images/issues/94" +)] fn recursive_rmdir_of_symlink() { // test we do not recursively delete a symlink but only dirs. let tmpdir = tmpdir(); @@ -1517,6 +1541,10 @@ fn file_open_not_found() { } #[test] +#[cfg_attr( + all(windows, target_arch = "aarch64"), + ignore = "SymLinks not enabled on Arm64 Windows runners https://github.com/actions/partner-runner-images/issues/94" +)] fn create_dir_all_with_junctions() { let tmpdir = tmpdir(); let target = tmpdir.join("target"); @@ -1719,6 +1747,23 @@ fn test_eq_direntry_metadata() { } } +/// Test that windows file type equality is not affected by attributes unrelated +/// to the file type. +#[test] +#[cfg(target_os = "windows")] +fn test_eq_windows_file_type() { + let tmpdir = tmpdir(); + let file1 = File::create(tmpdir.join("file1")).unwrap(); + let file2 = File::create(tmpdir.join("file2")).unwrap(); + assert_eq!(file1.metadata().unwrap().file_type(), file2.metadata().unwrap().file_type()); + + // Change the readonly attribute of one file. + let mut perms = file1.metadata().unwrap().permissions(); + perms.set_readonly(true); + file1.set_permissions(perms).unwrap(); + assert_eq!(file1.metadata().unwrap().file_type(), file2.metadata().unwrap().file_type()); +} + /// Regression test for https://github.com/rust-lang/rust/issues/50619. #[test] #[cfg(target_os = "linux")] @@ -1878,7 +1923,7 @@ fn windows_unix_socket_exists() { let bytes = socket_path.as_os_str().as_encoded_bytes(); let bytes = core::slice::from_raw_parts(bytes.as_ptr().cast::(), bytes.len()); addr.sun_path[..bytes.len()].copy_from_slice(bytes); - let len = mem::size_of_val(&addr) as i32; + let len = size_of_val(&addr) as i32; let result = c::bind(socket, (&raw const addr).cast::(), len); c::closesocket(socket); assert_eq!(result, 0); @@ -1978,6 +2023,10 @@ fn test_rename_symlink() { #[test] #[cfg(windows)] +#[cfg_attr( + all(windows, target_arch = "aarch64"), + ignore = "SymLinks not enabled on Arm64 Windows runners https://github.com/actions/partner-runner-images/issues/94" +)] fn test_rename_junction() { let tmpdir = tmpdir(); let original = tmpdir.join("original"); diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 8b46738ab8aee..40441dc057d0d 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -109,25 +109,29 @@ impl BufReader { /// /// `n` must be less than or equal to `capacity`. /// - /// the returned slice may be less than `n` bytes long if + /// The returned slice may be less than `n` bytes long if /// end of file is reached. /// + /// After calling this method, you may call [`consume`](BufRead::consume) + /// with a value less than or equal to `n` to advance over some or all of + /// the returned bytes. + /// /// ## Examples /// /// ```rust /// #![feature(bufreader_peek)] /// use std::io::{Read, BufReader}; /// - /// let mut bytes = &b"oh, hello"[..]; + /// let mut bytes = &b"oh, hello there"[..]; /// let mut rdr = BufReader::with_capacity(6, &mut bytes); /// assert_eq!(rdr.peek(2).unwrap(), b"oh"); /// let mut buf = [0; 4]; /// rdr.read(&mut buf[..]).unwrap(); /// assert_eq!(&buf, b"oh, "); - /// assert_eq!(rdr.peek(2).unwrap(), b"he"); + /// assert_eq!(rdr.peek(5).unwrap(), b"hello"); /// let mut s = String::new(); /// rdr.read_to_string(&mut s).unwrap(); - /// assert_eq!(&s, "hello"); + /// assert_eq!(&s, "hello there"); /// assert_eq!(rdr.peek(1).unwrap().len(), 0); /// ``` #[unstable(feature = "bufreader_peek", issue = "128405")] diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 5251cc302cb49..574288e579e0b 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -109,8 +109,8 @@ impl Buffer { /// Read more bytes into the buffer without discarding any of its contents pub fn read_more(&mut self, mut reader: impl Read) -> io::Result { - let mut buf = BorrowedBuf::from(&mut self.buf[self.pos..]); - let old_init = self.initialized - self.pos; + let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]); + let old_init = self.initialized - self.filled; unsafe { buf.set_init(old_init); } @@ -123,7 +123,6 @@ impl Buffer { /// Remove bytes that have already been read from the buffer. pub fn backshift(&mut self) { self.buf.copy_within(self.pos.., 0); - self.initialized -= self.pos; self.filled -= self.pos; self.pos = 0; } diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index 8d733325b3be7..15e962924ac71 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -248,8 +248,11 @@ impl BufferedWriterSpec for BufWriter { Err(e) => return Err(e), } } else { + // All the bytes that were already in the buffer are initialized, + // treat them as such when the buffer is flushed. + init += buf.len(); + self.flush_buf()?; - init = 0; } } } diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 08832bbc1e3d1..d7131e2fe92fd 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -439,6 +439,27 @@ fn slice_write_vectored( Ok(nwritten) } +#[inline] +fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<()> { + let n = slice_write(pos_mut, slice, buf)?; + if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } +} + +#[inline] +fn slice_write_all_vectored( + pos_mut: &mut u64, + slice: &mut [u8], + bufs: &[IoSlice<'_>], +) -> io::Result<()> { + for buf in bufs { + let n = slice_write(pos_mut, slice, buf)?; + if n < buf.len() { + return Err(io::Error::WRITE_ALL_EOF); + } + } + Ok(()) +} + /// Reserves the required space, and pads the vec with 0s if necessary. fn reserve_and_pad( pos_mut: &mut u64, @@ -481,9 +502,12 @@ fn reserve_and_pad( Ok(pos) } -/// Writes the slice to the vec without allocating -/// # Safety: vec must have buf.len() spare capacity -unsafe fn vec_write_unchecked(pos: usize, vec: &mut Vec, buf: &[u8]) -> usize +/// Writes the slice to the vec without allocating. +/// +/// # Safety +/// +/// `vec` must have `buf.len()` spare capacity. +unsafe fn vec_write_all_unchecked(pos: usize, vec: &mut Vec, buf: &[u8]) -> usize where A: Allocator, { @@ -492,7 +516,7 @@ where pos + buf.len() } -/// Resizing write implementation for [`Cursor`] +/// Resizing `write_all` implementation for [`Cursor`]. /// /// Cursor is allowed to have a pre-allocated and initialised /// vector body, but with a position of 0. This means the [`Write`] @@ -501,7 +525,7 @@ where /// This also allows for the vec body to be empty, but with a position of N. /// This means that [`Write`] will pad the vec with 0 initially, /// before writing anything from that point -fn vec_write(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result +fn vec_write_all(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result where A: Allocator, { @@ -512,7 +536,7 @@ where // Safety: we have ensured that the capacity is available // and that all bytes get written up to pos unsafe { - pos = vec_write_unchecked(pos, vec, buf); + pos = vec_write_all_unchecked(pos, vec, buf); if pos > vec.len() { vec.set_len(pos); } @@ -523,7 +547,7 @@ where Ok(buf_len) } -/// Resizing write_vectored implementation for [`Cursor`] +/// Resizing `write_all_vectored` implementation for [`Cursor`]. /// /// Cursor is allowed to have a pre-allocated and initialised /// vector body, but with a position of 0. This means the [`Write`] @@ -532,7 +556,7 @@ where /// This also allows for the vec body to be empty, but with a position of N. /// This means that [`Write`] will pad the vec with 0 initially, /// before writing anything from that point -fn vec_write_vectored( +fn vec_write_all_vectored( pos_mut: &mut u64, vec: &mut Vec, bufs: &[IoSlice<'_>], @@ -550,7 +574,7 @@ where // and that all bytes get written up to the last pos unsafe { for buf in bufs { - pos = vec_write_unchecked(pos, vec, buf); + pos = vec_write_all_unchecked(pos, vec, buf); } if pos > vec.len() { vec.set_len(pos); @@ -579,6 +603,16 @@ impl Write for Cursor<&mut [u8]> { true } + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + slice_write_all(&mut self.pos, self.inner, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + slice_write_all_vectored(&mut self.pos, self.inner, bufs) + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -591,11 +625,11 @@ where A: Allocator, { fn write(&mut self, buf: &[u8]) -> io::Result { - vec_write(&mut self.pos, self.inner, buf) + vec_write_all(&mut self.pos, self.inner, buf) } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - vec_write_vectored(&mut self.pos, self.inner, bufs) + vec_write_all_vectored(&mut self.pos, self.inner, bufs) } #[inline] @@ -603,6 +637,16 @@ where true } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + vec_write_all(&mut self.pos, self.inner, buf)?; + Ok(()) + } + + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + vec_write_all_vectored(&mut self.pos, self.inner, bufs)?; + Ok(()) + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -615,11 +659,11 @@ where A: Allocator, { fn write(&mut self, buf: &[u8]) -> io::Result { - vec_write(&mut self.pos, &mut self.inner, buf) + vec_write_all(&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) + vec_write_all_vectored(&mut self.pos, &mut self.inner, bufs) } #[inline] @@ -627,6 +671,16 @@ where true } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + vec_write_all(&mut self.pos, &mut self.inner, buf)?; + Ok(()) + } + + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + vec_write_all_vectored(&mut self.pos, &mut self.inner, bufs)?; + Ok(()) + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -653,6 +707,16 @@ where true } + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + slice_write_all(&mut self.pos, &mut self.inner, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + slice_write_all_vectored(&mut self.pos, &mut self.inner, bufs) + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -676,6 +740,16 @@ impl Write for Cursor<[u8; N]> { true } + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + slice_write_all(&mut self.pos, &mut self.inner, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + slice_write_all_vectored(&mut self.pos, &mut self.inner, bufs) + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 5aa048c4cbc85..ba765a6203f2f 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -48,6 +48,7 @@ use crate::{error, fmt, result, sys}; /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[doc(search_unbox)] pub type Result = result::Result; /// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and @@ -373,8 +374,8 @@ pub enum ErrorKind { TooManyLinks, /// A filename was invalid. /// - /// This error can also cause if it exceeded the filename length limit. - #[unstable(feature = "io_error_more", issue = "86442")] + /// This error can also occur if a length limit for a name was exceeded. + #[stable(feature = "io_error_invalid_filename", since = "1.87.0")] InvalidFilename, /// Program argument list too long. /// diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index edac6563478cd..3e4029768eb85 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,6 +1,5 @@ use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error}; use crate::assert_matches::assert_matches; -use crate::mem::size_of; use crate::sys::decode_error_kind; use crate::sys::os::error_string; use crate::{error, fmt}; diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 8239b29884e8e..d0245f3d4984c 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -455,7 +455,17 @@ impl Write for &mut [u8] { #[inline] fn write_all(&mut self, data: &[u8]) -> io::Result<()> { - if self.write(data)? == data.len() { Ok(()) } else { Err(io::Error::WRITE_ALL_EOF) } + if self.write(data)? < data.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + for buf in bufs { + if self.write(buf)? < buf.len() { + return Err(io::Error::WRITE_ALL_EOF); + } + } + Ok(()) } #[inline] @@ -495,6 +505,12 @@ impl Write for Vec { Ok(()) } + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.write_vectored(bufs)?; + Ok(()) + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -515,6 +531,7 @@ impl Read for VecDeque { Ok(n) } + #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { let (front, back) = self.as_slices(); @@ -547,6 +564,7 @@ impl Read for VecDeque { Ok(()) } + #[inline] fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { let len = cursor.capacity(); let (front, back) = self.as_slices(); @@ -638,6 +656,12 @@ impl Write for VecDeque { Ok(()) } + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.write_vectored(bufs)?; + Ok(()) + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -646,12 +670,46 @@ impl Write for VecDeque { #[unstable(feature = "read_buf", issue = "78485")] impl<'a> io::Write for core::io::BorrowedCursor<'a> { + #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { let amt = cmp::min(buf.len(), self.capacity()); self.append(&buf[..amt]); Ok(amt) } + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + let n = self.write(buf)?; + nwritten += n; + if n < buf.len() { + break; + } + } + Ok(nwritten) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + if self.write(buf)? < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + for buf in bufs { + if self.write(buf)? < buf.len() { + return Err(io::Error::WRITE_ALL_EOF); + } + } + Ok(()) + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 7f610bc88bfd7..03f5f838311a9 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -310,7 +310,7 @@ pub use self::error::RawOsError; pub use self::error::SimpleMessage; #[unstable(feature = "io_const_error", issue = "133448")] pub use self::error::const_error; -#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[stable(feature = "anonymous_pipe", since = "1.87.0")] pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] pub use self::stdio::IsTerminal; @@ -612,6 +612,47 @@ pub(crate) fn default_read_buf_exact( Ok(()) } +pub(crate) fn default_write_fmt( + this: &mut W, + args: 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 Adapter<'a, T: ?Sized + 'a> { + inner: &'a mut T, + error: Result<()>, + } + + impl fmt::Write for Adapter<'_, 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 = Adapter { inner: this, error: Ok(()) }; + match fmt::write(&mut output, args) { + Ok(()) => Ok(()), + Err(..) => { + // Check whether the error came from the underlying `Write`. + if output.error.is_err() { + output.error + } else { + // This shouldn't happen: the underlying stream did not error, + // but somehow the formatter still errored? + panic!( + "a formatting trait implementation returned an error when the underlying stream did not" + ); + } + } + } +} + /// The `Read` trait allows for reading bytes from a source. /// /// Implementors of the `Read` trait are called 'readers'. @@ -1173,7 +1214,7 @@ pub trait Read { where Self: Sized, { - Take { inner: self, limit } + Take { inner: self, len: limit, limit } } } @@ -1866,41 +1907,11 @@ pub trait Write { /// } /// ``` #[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 Adapter<'a, T: ?Sized + 'a> { - inner: &'a mut T, - error: Result<()>, - } - - impl fmt::Write for Adapter<'_, 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 = Adapter { 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 { - // This shouldn't happen: the underlying stream did not error, but somehow - // the formatter still errored? - panic!( - "a formatting trait implementation returned an error when the underlying stream did not" - ); - } - } + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> { + if let Some(s) = args.as_statically_known_str() { + self.write_all(s.as_bytes()) + } else { + default_write_fmt(self, args) } } @@ -2251,24 +2262,18 @@ fn skip_until(r: &mut R, delim: u8) -> Result { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")] pub trait BufRead: Read { - /// Returns the contents of the internal buffer, filling it with more data - /// from the inner reader if it is empty. + /// Returns the contents of the internal buffer, filling it with more data, via `Read` methods, if 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. + /// This is a lower-level method and is meant to be used together with [`consume`], + /// which can be used to mark bytes that should not be returned by subsequent calls to `read`. /// /// [`consume`]: BufRead::consume /// - /// An empty buffer returned indicates that the stream has reached EOF. + /// Returns an empty buffer when the stream has reached EOF. /// /// # Errors /// - /// This function will return an I/O error if the underlying reader was - /// read, but returned an error. + /// This function will return an I/O error if a `Read` method was called, but returned an error. /// /// # Examples /// @@ -2286,7 +2291,7 @@ pub trait BufRead: Read { /// // work with buffer /// println!("{buffer:?}"); /// - /// // ensure the bytes we worked with aren't returned again later + /// // mark the bytes we worked with as read /// let length = buffer.len(); /// stdin.consume(length); /// # std::io::Result::Ok(()) @@ -2294,18 +2299,13 @@ pub trait BufRead: Read { #[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`. + /// Marks the given `amount` of additional bytes from the internal buffer as having been read. + /// Subsequent calls to `read` only return bytes that have not been marked as 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. + /// This is a lower-level method and is meant to be used together with [`fill_buf`], + /// which can be used to fill the internal buffer via `Read` methods. /// - /// The `amt` must be `<=` the number of bytes in the buffer returned by - /// [`fill_buf`]. + /// It is a logic error if `amount` exceeds the number of unread bytes in the internal buffer, which is returned by [`fill_buf`]. /// /// # Examples /// @@ -2314,17 +2314,21 @@ pub trait BufRead: Read { /// /// [`fill_buf`]: BufRead::fill_buf #[stable(feature = "rust1", since = "1.0.0")] - fn consume(&mut self, amt: usize); + fn consume(&mut self, amount: usize); - /// Checks if the underlying `Read` has any data left to be read. + /// Checks if there is any data left to be `read`. /// /// This function may fill the buffer to check for data, - /// so this functions returns `Result`, not `bool`. + /// so this function returns `Result`, not `bool`. /// - /// Default implementation calls `fill_buf` and checks that + /// The default implementation calls `fill_buf` and checks that the /// returned slice is empty (which means that there is no data left, /// since EOF is reached). /// + /// # Errors + /// + /// This function will return an I/O error if a `Read` method was called, but returned an error. + /// /// Examples /// /// ``` @@ -2654,6 +2658,10 @@ impl Chain { /// Gets 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 @@ -2822,6 +2830,7 @@ impl SizeHint for Chain { #[derive(Debug)] pub struct Take { inner: T, + len: u64, limit: u64, } @@ -2856,6 +2865,12 @@ impl Take { self.limit } + /// Returns the number of bytes read so far. + #[unstable(feature = "seek_io_take_position", issue = "97227")] + pub fn position(&self) -> u64 { + self.len - 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 @@ -2881,6 +2896,7 @@ impl Take { /// ``` #[stable(feature = "take_set_limit", since = "1.27.0")] pub fn set_limit(&mut self, limit: u64) { + self.len = limit; self.limit = limit; } @@ -2911,6 +2927,10 @@ impl Take { /// Gets a 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 @@ -2985,11 +3005,11 @@ impl Read for Take { return Ok(()); } - if self.limit <= buf.capacity() as u64 { - // if we just use an as cast to convert, limit may wrap around on a 32 bit target - let limit = cmp::min(self.limit, usize::MAX as u64) as usize; + if self.limit < buf.capacity() as u64 { + // The condition above guarantees that `self.limit` fits in `usize`. + let limit = self.limit as usize; - let extra_init = cmp::min(limit as usize, buf.init_ref().len()); + let extra_init = cmp::min(limit, buf.init_ref().len()); // SAFETY: no uninit data is written to ibuf let ibuf = unsafe { &mut buf.as_mut()[..limit] }; @@ -3064,6 +3084,49 @@ impl SizeHint for Take { } } +#[stable(feature = "seek_io_take", since = "CURRENT_RUSTC_VERSION")] +impl Seek for Take { + fn seek(&mut self, pos: SeekFrom) -> Result { + let new_position = match pos { + SeekFrom::Start(v) => Some(v), + SeekFrom::Current(v) => self.position().checked_add_signed(v), + SeekFrom::End(v) => self.len.checked_add_signed(v), + }; + let new_position = match new_position { + Some(v) if v <= self.len => v, + _ => return Err(ErrorKind::InvalidInput.into()), + }; + while new_position != self.position() { + if let Some(offset) = new_position.checked_signed_diff(self.position()) { + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + break; + } + let offset = if new_position > self.position() { i64::MAX } else { i64::MIN }; + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + } + Ok(new_position) + } + + fn stream_len(&mut self) -> Result { + Ok(self.len) + } + + fn stream_position(&mut self) -> Result { + Ok(self.position()) + } + + fn seek_relative(&mut self, offset: i64) -> Result<()> { + if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) { + return Err(ErrorKind::InvalidInput.into()); + } + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + Ok(()) + } +} + /// An iterator over `u8` values of a reader. /// /// This struct is generally created by calling [`bytes`] on a reader. diff --git a/library/std/src/io/pipe.rs b/library/std/src/io/pipe.rs index 266c7bc96389b..47243806cd2d9 100644 --- a/library/std/src/io/pipe.rs +++ b/library/std/src/io/pipe.rs @@ -1,7 +1,8 @@ use crate::io; use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; +use crate::sys_common::{FromInner, IntoInner}; -/// Create an anonymous pipe. +/// Creates an anonymous pipe. /// /// # Behavior /// @@ -40,7 +41,6 @@ use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; /// # Examples /// /// ```no_run -/// #![feature(anonymous_pipe)] /// # #[cfg(miri)] fn main() {} /// # #[cfg(not(miri))] /// # fn main() -> std::io::Result<()> { @@ -67,29 +67,52 @@ use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; /// ``` /// [changes]: io#platform-specific-behavior /// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html -#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[stable(feature = "anonymous_pipe", since = "1.87.0")] #[inline] pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) } /// Read end of an anonymous pipe. -#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[stable(feature = "anonymous_pipe", since = "1.87.0")] #[derive(Debug)] pub struct PipeReader(pub(crate) AnonPipe); /// Write end of an anonymous pipe. -#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[stable(feature = "anonymous_pipe", since = "1.87.0")] #[derive(Debug)] pub struct PipeWriter(pub(crate) AnonPipe); +impl FromInner for PipeReader { + fn from_inner(inner: AnonPipe) -> Self { + Self(inner) + } +} + +impl IntoInner for PipeReader { + fn into_inner(self) -> AnonPipe { + self.0 + } +} + +impl FromInner for PipeWriter { + fn from_inner(inner: AnonPipe) -> Self { + Self(inner) + } +} + +impl IntoInner for PipeWriter { + fn into_inner(self) -> AnonPipe { + self.0 + } +} + impl PipeReader { - /// Create a new [`PipeReader`] instance that shares the same underlying file description. + /// Creates a new [`PipeReader`] instance that shares the same underlying file description. /// /// # Examples /// /// ```no_run - /// #![feature(anonymous_pipe)] /// # #[cfg(miri)] fn main() {} /// # #[cfg(not(miri))] /// # fn main() -> std::io::Result<()> { @@ -137,19 +160,18 @@ impl PipeReader { /// # Ok(()) /// # } /// ``` - #[unstable(feature = "anonymous_pipe", issue = "127154")] + #[stable(feature = "anonymous_pipe", since = "1.87.0")] pub fn try_clone(&self) -> io::Result { self.0.try_clone().map(Self) } } impl PipeWriter { - /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + /// Creates a new [`PipeWriter`] instance that shares the same underlying file description. /// /// # Examples /// /// ```no_run - /// #![feature(anonymous_pipe)] /// # #[cfg(miri)] fn main() {} /// # #[cfg(not(miri))] /// # fn main() -> std::io::Result<()> { @@ -177,13 +199,13 @@ impl PipeWriter { /// # Ok(()) /// # } /// ``` - #[unstable(feature = "anonymous_pipe", issue = "127154")] + #[stable(feature = "anonymous_pipe", since = "1.87.0")] pub fn try_clone(&self) -> io::Result { self.0.try_clone().map(Self) } } -#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[stable(feature = "anonymous_pipe", since = "1.87.0")] impl io::Read for &PipeReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) @@ -203,7 +225,7 @@ impl io::Read for &PipeReader { } } -#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[stable(feature = "anonymous_pipe", since = "1.87.0")] impl io::Read for PipeReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) @@ -223,7 +245,7 @@ impl io::Read for PipeReader { } } -#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[stable(feature = "anonymous_pipe", since = "1.87.0")] impl io::Write for &PipeWriter { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) @@ -241,7 +263,7 @@ impl io::Write for &PipeWriter { } } -#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[stable(feature = "anonymous_pipe", since = "1.87.0")] impl io::Write for PipeWriter { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 017862c7f3aac..2d80fe49e80a7 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -11,7 +11,7 @@ use crate::io::{ self, BorrowedCursor, BufReader, IoSlice, IoSliceMut, LineWriter, Lines, SpecReadByte, }; use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantLock, ReentrantLockGuard}; use crate::sys::stdio; use crate::thread::AccessError; @@ -37,7 +37,7 @@ thread_local! { /// have a consistent order between set_output_capture and print_to *within /// the same thread*. Within the same thread, things always have a perfectly /// consistent order. So Ordering::Relaxed is fine. -static OUTPUT_CAPTURE_USED: AtomicBool = AtomicBool::new(false); +static OUTPUT_CAPTURE_USED: Atomic = AtomicBool::new(false); /// A handle to a raw instance of the standard input stream of this process. /// @@ -97,15 +97,15 @@ const fn stderr_raw() -> StderrRaw { impl Read for StdinRaw { fn read(&mut self, buf: &mut [u8]) -> io::Result { - handle_ebadf(self.0.read(buf), 0) + handle_ebadf(self.0.read(buf), || Ok(0)) } fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - handle_ebadf(self.0.read_buf(buf), ()) + handle_ebadf(self.0.read_buf(buf), || Ok(())) } fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - handle_ebadf(self.0.read_vectored(bufs), 0) + handle_ebadf(self.0.read_vectored(bufs), || Ok(0)) } #[inline] @@ -113,23 +113,37 @@ impl Read for StdinRaw { self.0.is_read_vectored() } + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + if buf.is_empty() { + return Ok(()); + } + handle_ebadf(self.0.read_exact(buf), || Err(io::Error::READ_EXACT_EOF)) + } + + fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + if buf.capacity() == 0 { + return Ok(()); + } + handle_ebadf(self.0.read_buf_exact(buf), || Err(io::Error::READ_EXACT_EOF)) + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - handle_ebadf(self.0.read_to_end(buf), 0) + handle_ebadf(self.0.read_to_end(buf), || Ok(0)) } fn read_to_string(&mut self, buf: &mut String) -> io::Result { - handle_ebadf(self.0.read_to_string(buf), 0) + handle_ebadf(self.0.read_to_string(buf), || Ok(0)) } } impl Write for StdoutRaw { fn write(&mut self, buf: &[u8]) -> io::Result { - handle_ebadf(self.0.write(buf), buf.len()) + handle_ebadf(self.0.write(buf), || Ok(buf.len())) } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total = || bufs.iter().map(|b| b.len()).sum(); - handle_ebadf_lazy(self.0.write_vectored(bufs), total) + let total = || Ok(bufs.iter().map(|b| b.len()).sum()); + handle_ebadf(self.0.write_vectored(bufs), total) } #[inline] @@ -138,30 +152,30 @@ impl Write for StdoutRaw { } fn flush(&mut self) -> io::Result<()> { - handle_ebadf(self.0.flush(), ()) + handle_ebadf(self.0.flush(), || Ok(())) } fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - handle_ebadf(self.0.write_all(buf), ()) + handle_ebadf(self.0.write_all(buf), || Ok(())) } fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - handle_ebadf(self.0.write_all_vectored(bufs), ()) + handle_ebadf(self.0.write_all_vectored(bufs), || Ok(())) } fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - handle_ebadf(self.0.write_fmt(fmt), ()) + handle_ebadf(self.0.write_fmt(fmt), || Ok(())) } } impl Write for StderrRaw { fn write(&mut self, buf: &[u8]) -> io::Result { - handle_ebadf(self.0.write(buf), buf.len()) + handle_ebadf(self.0.write(buf), || Ok(buf.len())) } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total = || bufs.iter().map(|b| b.len()).sum(); - handle_ebadf_lazy(self.0.write_vectored(bufs), total) + let total = || Ok(bufs.iter().map(|b| b.len()).sum()); + handle_ebadf(self.0.write_vectored(bufs), total) } #[inline] @@ -170,32 +184,25 @@ impl Write for StderrRaw { } fn flush(&mut self) -> io::Result<()> { - handle_ebadf(self.0.flush(), ()) + handle_ebadf(self.0.flush(), || Ok(())) } fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - handle_ebadf(self.0.write_all(buf), ()) + handle_ebadf(self.0.write_all(buf), || Ok(())) } fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - handle_ebadf(self.0.write_all_vectored(bufs), ()) + handle_ebadf(self.0.write_all_vectored(bufs), || Ok(())) } fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - handle_ebadf(self.0.write_fmt(fmt), ()) + handle_ebadf(self.0.write_fmt(fmt), || Ok(())) } } -fn handle_ebadf(r: io::Result, default: T) -> io::Result { +fn handle_ebadf(r: io::Result, default: impl FnOnce() -> io::Result) -> io::Result { match r { - Err(ref e) if stdio::is_ebadf(e) => Ok(default), - r => r, - } -} - -fn handle_ebadf_lazy(r: io::Result, default: impl FnOnce() -> T) -> io::Result { - match r { - Err(ref e) if stdio::is_ebadf(e) => Ok(default()), + Err(ref e) if stdio::is_ebadf(e) => default(), r => r, } } @@ -575,6 +582,11 @@ impl fmt::Debug for StdinLock<'_> { /// output stream. Access is also synchronized via a lock and explicit control /// over locking is available via the [`lock`] method. /// +/// By default, the handle is line-buffered when connected to a terminal, meaning +/// it flushes automatically when a newline (`\n`) is encountered. For immediate +/// output, you can manually call the [`flush`] method. When the handle goes out +/// of scope, the buffer is automatically flushed. +/// /// Created by the [`io::stdout`] method. /// /// ### Note: Windows Portability Considerations @@ -590,6 +602,7 @@ impl fmt::Debug for StdinLock<'_> { /// standard library or via raw Windows API calls, will fail. /// /// [`lock`]: Stdout::lock +/// [`flush`]: Write::flush /// [`io::stdout`]: stdout #[stable(feature = "rust1", since = "1.0.0")] pub struct Stdout { @@ -604,6 +617,11 @@ pub struct Stdout { /// This handle implements the [`Write`] trait, and is constructed via /// the [`Stdout::lock`] method. See its documentation for more. /// +/// By default, the handle is line-buffered when connected to a terminal, meaning +/// it flushes automatically when a newline (`\n`) is encountered. For immediate +/// output, you can manually call the [`flush`] method. When the handle goes out +/// of scope, the buffer is automatically flushed. +/// /// ### Note: Windows Portability Considerations /// /// When operating in a console, the Windows implementation of this stream does not support @@ -615,6 +633,8 @@ pub struct Stdout { /// the contained handle will be null. In such cases, the standard library's `Read` and /// `Write` will do nothing and silently succeed. All other I/O operations, via the /// standard library or via raw Windows API calls, will fail. +/// +/// [`flush`]: Write::flush #[must_use = "if unused stdout will immediately unlock"] #[stable(feature = "rust1", since = "1.0.0")] pub struct StdoutLock<'a> { @@ -629,6 +649,11 @@ static STDOUT: OnceLock>>> = OnceLoc /// is synchronized via a mutex. If you need more explicit control over /// locking, see the [`Stdout::lock`] method. /// +/// By default, the handle is line-buffered when connected to a terminal, meaning +/// it flushes automatically when a newline (`\n`) is encountered. For immediate +/// output, you can manually call the [`flush`] method. When the handle goes out +/// of scope, the buffer is automatically flushed. +/// /// ### Note: Windows Portability Considerations /// /// When operating in a console, the Windows implementation of this stream does not support @@ -669,6 +694,22 @@ static STDOUT: OnceLock>>> = OnceLoc /// Ok(()) /// } /// ``` +/// +/// Ensuring output is flushed immediately: +/// +/// ```no_run +/// use std::io::{self, Write}; +/// +/// fn main() -> io::Result<()> { +/// let mut stdout = io::stdout(); +/// stdout.write_all(b"hello, ")?; +/// stdout.flush()?; // Manual flush +/// stdout.write_all(b"world!\n")?; // Automatically flushed +/// Ok(()) +/// } +/// ``` +/// +/// [`flush`]: Write::flush #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "io_stdout")] diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index f64f034cce779..b22988d4a8a9d 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -416,6 +416,126 @@ fn seek_position() -> io::Result<()> { Ok(()) } +#[test] +fn take_seek() -> io::Result<()> { + let mut buf = Cursor::new(b"0123456789"); + buf.set_position(2); + let mut take = buf.by_ref().take(4); + let mut buf1 = [0u8; 1]; + let mut buf2 = [0u8; 2]; + assert_eq!(take.position(), 0); + + assert_eq!(take.seek(SeekFrom::Start(0))?, 0); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'2', b'3']); + assert_eq!(take.seek(SeekFrom::Start(1))?, 1); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'3', b'4']); + assert_eq!(take.seek(SeekFrom::Start(2))?, 2); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'4', b'5']); + assert_eq!(take.seek(SeekFrom::Start(3))?, 3); + take.read_exact(&mut buf1)?; + assert_eq!(buf1, [b'5']); + assert_eq!(take.seek(SeekFrom::Start(4))?, 4); + assert_eq!(take.read(&mut buf1)?, 0); + + assert_eq!(take.seek(SeekFrom::End(0))?, 4); + assert_eq!(take.seek(SeekFrom::End(-1))?, 3); + take.read_exact(&mut buf1)?; + assert_eq!(buf1, [b'5']); + assert_eq!(take.seek(SeekFrom::End(-2))?, 2); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'4', b'5']); + assert_eq!(take.seek(SeekFrom::End(-3))?, 1); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'3', b'4']); + assert_eq!(take.seek(SeekFrom::End(-4))?, 0); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'2', b'3']); + + assert_eq!(take.seek(SeekFrom::Current(0))?, 2); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'4', b'5']); + + assert_eq!(take.seek(SeekFrom::Current(-3))?, 1); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'3', b'4']); + + assert_eq!(take.seek(SeekFrom::Current(-1))?, 2); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'4', b'5']); + + assert_eq!(take.seek(SeekFrom::Current(-4))?, 0); + take.read_exact(&mut buf2)?; + assert_eq!(buf2, [b'2', b'3']); + + assert_eq!(take.seek(SeekFrom::Current(2))?, 4); + assert_eq!(take.read(&mut buf1)?, 0); + + Ok(()) +} + +#[test] +fn take_seek_error() { + let buf = Cursor::new(b"0123456789"); + let mut take = buf.take(2); + assert!(take.seek(SeekFrom::Start(3)).is_err()); + assert!(take.seek(SeekFrom::End(1)).is_err()); + assert!(take.seek(SeekFrom::End(-3)).is_err()); + assert!(take.seek(SeekFrom::Current(-1)).is_err()); + assert!(take.seek(SeekFrom::Current(3)).is_err()); +} + +struct ExampleHugeRangeOfZeroes { + position: u64, +} + +impl Read for ExampleHugeRangeOfZeroes { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let max = buf.len().min(usize::MAX); + for i in 0..max { + if self.position == u64::MAX { + return Ok(i); + } + self.position += 1; + buf[i] = 0; + } + Ok(max) + } +} + +impl Seek for ExampleHugeRangeOfZeroes { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + match pos { + io::SeekFrom::Start(i) => self.position = i, + io::SeekFrom::End(i) if i >= 0 => self.position = u64::MAX, + io::SeekFrom::End(i) => self.position = self.position - i.unsigned_abs(), + io::SeekFrom::Current(i) => { + self.position = if i >= 0 { + self.position.saturating_add(i.unsigned_abs()) + } else { + self.position.saturating_sub(i.unsigned_abs()) + }; + } + } + Ok(self.position) + } +} + +#[test] +fn take_seek_big_offsets() -> io::Result<()> { + let inner = ExampleHugeRangeOfZeroes { position: 1 }; + let mut take = inner.take(u64::MAX - 2); + assert_eq!(take.seek(io::SeekFrom::Start(u64::MAX - 2))?, u64::MAX - 2); + assert_eq!(take.inner.position, u64::MAX - 1); + assert_eq!(take.seek(io::SeekFrom::Start(0))?, 0); + assert_eq!(take.inner.position, 1); + assert_eq!(take.seek(io::SeekFrom::End(-1))?, u64::MAX - 3); + assert_eq!(take.inner.position, u64::MAX - 2); + Ok(()) +} + // A simple example reader which uses the default implementation of // read_to_end. struct ExampleSliceReader<'a> { @@ -811,13 +931,17 @@ fn read_to_end_error() { } #[test] -// Miri does not support signalling OOM -#[cfg_attr(miri, ignore)] -// 64-bit only to be sure the allocator will fail fast on an impossible to satisfy size -#[cfg(target_pointer_width = "64")] fn try_oom_error() { - let mut v = Vec::::new(); - let reserve_err = v.try_reserve(isize::MAX as usize - 1).unwrap_err(); + use alloc::alloc::Layout; + use alloc::collections::{TryReserveError, TryReserveErrorKind}; + + // We simulate a `Vec::try_reserve` error rather than attempting a huge size for real. This way + // we're not subject to the whims of optimization that might skip the actual allocation, and it + // also works for 32-bit targets and miri that might not OOM at all. + let layout = Layout::new::(); + let kind = TryReserveErrorKind::AllocError { layout, non_exhaustive: () }; + let reserve_err = TryReserveError::from(kind); + let io_err = io::Error::from(reserve_err); assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind()); } diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index cb3f864fd4e1e..0410df3ef1a3e 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -7,7 +7,6 @@ use crate::fmt; use crate::io::{ self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write, }; -use crate::mem::MaybeUninit; /// `Empty` ignores any data written via [`Write`], and will always be empty /// (returning zero bytes) when read via [`Read`]. @@ -68,6 +67,38 @@ impl Read for Empty { fn read_buf(&mut self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { Ok(()) } + + #[inline] + fn read_vectored(&mut self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + Ok(0) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + // Do not force `Chain` or `Chain` to use vectored + // reads, unless the other reader is vectored. + false + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } + } + + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + if cursor.capacity() != 0 { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } + } + + #[inline] + fn read_to_end(&mut self, _buf: &mut Vec) -> io::Result { + Ok(0) + } + + #[inline] + fn read_to_string(&mut self, _buf: &mut String) -> io::Result { + Ok(0) + } } #[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Empty { @@ -75,20 +106,44 @@ impl BufRead for Empty { fn fill_buf(&mut self) -> io::Result<&[u8]> { Ok(&[]) } + #[inline] fn consume(&mut self, _n: usize) {} + + #[inline] + fn has_data_left(&mut self) -> io::Result { + Ok(false) + } + + #[inline] + fn read_until(&mut self, _byte: u8, _buf: &mut Vec) -> io::Result { + Ok(0) + } + + #[inline] + fn skip_until(&mut self, _byte: u8) -> io::Result { + Ok(0) + } + + #[inline] + fn read_line(&mut self, _buf: &mut String) -> io::Result { + Ok(0) + } } #[stable(feature = "empty_seek", since = "1.51.0")] impl Seek for Empty { + #[inline] fn seek(&mut self, _pos: SeekFrom) -> io::Result { Ok(0) } + #[inline] fn stream_len(&mut self) -> io::Result { Ok(0) } + #[inline] fn stream_position(&mut self) -> io::Result { Ok(0) } @@ -119,6 +174,21 @@ impl Write for Empty { true } + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -143,6 +213,21 @@ impl Write for &Empty { true } + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -196,7 +281,7 @@ impl Read for Repeat { #[inline] fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { // SAFETY: No uninit bytes are being written. - MaybeUninit::fill(unsafe { buf.as_mut() }, self.byte); + unsafe { buf.as_mut() }.write_filled(self.byte); // SAFETY: the entire unfilled portion of buf has been initialized. unsafe { buf.advance_unchecked(buf.capacity()) }; Ok(()) @@ -302,6 +387,21 @@ impl Write for Sink { true } + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -326,6 +426,21 @@ impl Write for &Sink { true } + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index 0599a881af179..d0f106d7af416 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -1,14 +1,51 @@ +use crate::fmt; use crate::io::prelude::*; -use crate::io::{BorrowedBuf, Empty, Repeat, SeekFrom, Sink, empty, repeat, sink}; +use crate::io::{ + BorrowedBuf, Empty, ErrorKind, IoSlice, IoSliceMut, Repeat, SeekFrom, Sink, empty, repeat, sink, +}; use crate::mem::MaybeUninit; +struct ErrorDisplay; + +impl fmt::Display for ErrorDisplay { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + Err(fmt::Error) + } +} + +struct PanicDisplay; + +impl fmt::Display for PanicDisplay { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + panic!() + } +} + +#[track_caller] +fn test_sinking(mut w: W) { + assert_eq!(w.write(&[]).unwrap(), 0); + assert_eq!(w.write(&[0]).unwrap(), 1); + assert_eq!(w.write(&[0; 1024]).unwrap(), 1024); + w.write_all(&[]).unwrap(); + w.write_all(&[0]).unwrap(); + w.write_all(&[0; 1024]).unwrap(); + let mut bufs = + [IoSlice::new(&[]), IoSlice::new(&[0]), IoSlice::new(&[0; 1024]), IoSlice::new(&[])]; + assert!(w.is_write_vectored()); + assert_eq!(w.write_vectored(&[]).unwrap(), 0); + assert_eq!(w.write_vectored(&bufs).unwrap(), 1025); + w.write_all_vectored(&mut []).unwrap(); + w.write_all_vectored(&mut bufs).unwrap(); + assert!(w.flush().is_ok()); + assert_eq!(w.by_ref().write(&[0; 1024]).unwrap(), 1024); + // Ignores fmt arguments + w.write_fmt(format_args!("{}", ErrorDisplay)).unwrap(); + w.write_fmt(format_args!("{}", PanicDisplay)).unwrap(); +} + #[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_sinking(sink()); } #[test] @@ -19,6 +56,21 @@ fn empty_reads() { assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0); assert_eq!(Read::by_ref(&mut e).read(&mut [0; 1024]).unwrap(), 0); + e.read_exact(&mut []).unwrap(); + assert_eq!(e.read_exact(&mut [0]).unwrap_err().kind(), ErrorKind::UnexpectedEof); + assert_eq!(e.read_exact(&mut [0; 1024]).unwrap_err().kind(), ErrorKind::UnexpectedEof); + + assert!(!e.is_read_vectored()); + assert_eq!(e.read_vectored(&mut []).unwrap(), 0); + let (mut buf1, mut buf1024) = ([0], [0; 1024]); + let bufs = &mut [ + IoSliceMut::new(&mut []), + IoSliceMut::new(&mut buf1), + IoSliceMut::new(&mut buf1024), + IoSliceMut::new(&mut []), + ]; + assert_eq!(e.read_vectored(bufs).unwrap(), 0); + let buf: &mut [MaybeUninit<_>] = &mut []; let mut buf: BorrowedBuf<'_> = buf.into(); e.read_buf(buf.unfilled()).unwrap(); @@ -42,6 +94,47 @@ fn empty_reads() { Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); assert_eq!(buf.init_len(), 0); + + let buf: &mut [MaybeUninit<_>] = &mut []; + let mut buf: BorrowedBuf<'_> = buf.into(); + e.read_buf_exact(buf.unfilled()).unwrap(); + assert_eq!(buf.len(), 0); + assert_eq!(buf.init_len(), 0); + + let buf: &mut [_] = &mut [MaybeUninit::uninit()]; + let mut buf: BorrowedBuf<'_> = buf.into(); + assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof); + assert_eq!(buf.len(), 0); + assert_eq!(buf.init_len(), 0); + + let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; + let mut buf: BorrowedBuf<'_> = buf.into(); + assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof); + assert_eq!(buf.len(), 0); + assert_eq!(buf.init_len(), 0); + + let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; + let mut buf: BorrowedBuf<'_> = buf.into(); + assert_eq!( + Read::by_ref(&mut e).read_buf_exact(buf.unfilled()).unwrap_err().kind(), + ErrorKind::UnexpectedEof, + ); + assert_eq!(buf.len(), 0); + assert_eq!(buf.init_len(), 0); + + let mut buf = Vec::new(); + assert_eq!(e.read_to_end(&mut buf).unwrap(), 0); + assert_eq!(buf, vec![]); + let mut buf = vec![1, 2, 3]; + assert_eq!(e.read_to_end(&mut buf).unwrap(), 0); + assert_eq!(buf, vec![1, 2, 3]); + + let mut buf = String::new(); + assert_eq!(e.read_to_string(&mut buf).unwrap(), 0); + assert_eq!(buf, ""); + let mut buf = "hello".to_owned(); + assert_eq!(e.read_to_string(&mut buf).unwrap(), 0); + assert_eq!(buf, "hello"); } #[test] @@ -66,11 +159,7 @@ fn empty_seeks() { #[test] fn empty_sinks() { - let mut e = empty(); - assert_eq!(e.write(&[]).unwrap(), 0); - assert_eq!(e.write(&[0]).unwrap(), 1); - assert_eq!(e.write(&[0; 1024]).unwrap(), 1024); - assert_eq!(Write::by_ref(&mut e).write(&[0; 1024]).unwrap(), 1024); + test_sinking(empty()); } #[test] diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index bdd330611de3d..79b25040ef607 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -91,7 +91,7 @@ mod as_keyword {} /// /// When associated with `loop`, a break expression may be used to return a value from that loop. /// This is only valid with `loop` and not with any other type of loop. -/// If no value is specified, `break;` returns `()`. +/// If no value is specified for `break;` it returns `()`. /// Every `break` within a loop must return the same type. /// /// ```rust @@ -109,6 +109,33 @@ mod as_keyword {} /// println!("{result}"); /// ``` /// +/// It is also possible to exit from any *labelled* block returning the value early. +/// If no value is specified for `break;` it returns `()`. +/// +/// ```rust +/// let inputs = vec!["Cow", "Cat", "Dog", "Snake", "Cod"]; +/// +/// let mut results = vec![]; +/// for input in inputs { +/// let result = 'filter: { +/// if input.len() > 3 { +/// break 'filter Err("Too long"); +/// }; +/// +/// if !input.contains("C") { +/// break 'filter Err("No Cs"); +/// }; +/// +/// Ok(input.to_uppercase()) +/// }; +/// +/// results.push(result); +/// } +/// +/// // [Ok("COW"), Ok("CAT"), Err("No Cs"), Err("Too long"), Ok("COD")] +/// println!("{:?}", results) +/// ``` +/// /// For more details consult the [Reference on "break expression"] and the [Reference on "break and /// loop values"]. /// @@ -119,7 +146,7 @@ mod break_keyword {} #[doc(keyword = "const")] // -/// Compile-time constants, compile-time evaluable functions, and raw pointers. +/// Compile-time constants, compile-time blocks, compile-time evaluable functions, and raw pointers. /// /// ## Compile-time constants /// @@ -166,6 +193,12 @@ mod break_keyword {} /// /// For more detail on `const`, see the [Rust Book] or the [Reference]. /// +/// ## Compile-time blocks +/// +/// The `const` keyword can also be used to define a block of code that is evaluated at compile time. +/// This is useful for ensuring certain computations are completed before optimizations happen, as well as +/// before runtime. For more details, see the [Reference][const-blocks]. +/// /// ## Compile-time evaluable functions /// /// The other main use of the `const` keyword is in `const fn`. This marks a function as being @@ -184,6 +217,7 @@ mod break_keyword {} /// [pointer primitive]: pointer /// [Rust Book]: ../book/ch03-01-variables-and-mutability.html#constants /// [Reference]: ../reference/items/constant-items.html +/// [const-blocks]: ../reference/expressions/block-expr.html#const-blocks /// [const-eval]: ../reference/const_eval.html mod const_keyword {} @@ -381,11 +415,15 @@ mod enum_keyword {} /// lazy_static;`. The other use is in foreign function interfaces (FFI). /// /// `extern` is used in two different contexts within FFI. The first is in the form of external -/// blocks, for declaring function interfaces that Rust code can call foreign code by. +/// blocks, for declaring function interfaces that Rust code can call foreign code by. This use +/// of `extern` is unsafe, since we are asserting to the compiler that all function declarations +/// are correct. If they are not, using these items may lead to undefined behavior. /// /// ```rust ignore +/// // SAFETY: The function declarations given below are in +/// // line with the header files of `my_c_library`. /// #[link(name = "my_c_library")] -/// extern "C" { +/// unsafe extern "C" { /// fn my_c_function(x: i32) -> bool; /// } /// ``` @@ -1064,7 +1102,7 @@ mod move_keyword {} /// ```rust,compile_fail,E0502 /// let mut v = vec![0, 1]; /// let mut_ref_v = &mut v; -/// ##[allow(unused)] +/// # #[allow(unused)] /// let ref_v = &v; /// mut_ref_v.push(2); /// ``` @@ -1195,6 +1233,28 @@ mod ref_keyword {} /// Ok(()) /// } /// ``` +/// +/// Within [closures] and [`async`] blocks, `return` returns a value from within the closure or +/// `async` block, not from the parent function: +/// +/// ```rust +/// fn foo() -> i32 { +/// let closure = || { +/// return 5; +/// }; +/// +/// let future = async { +/// return 10; +/// }; +/// +/// return 15; +/// } +/// +/// assert_eq!(foo(), 15); +/// ``` +/// +/// [closures]: ../book/ch13-01-closures.html +/// [`async`]: ../std/keyword.async.html mod return_keyword {} #[doc(keyword = "self")] @@ -2121,8 +2181,8 @@ mod unsafe_keyword {} #[doc(keyword = "use")] // -/// Import or rename items from other crates or modules, or specify precise capturing -/// with `use<..>`. +/// Import or rename items from other crates or modules, use values under ergonomic clones +/// semantic, or specify precise capturing with `use<..>`. /// /// ## Importing items /// @@ -2205,6 +2265,11 @@ mod unsafe_keyword {} /// /// For more details about precise capturing, see the [Reference][ref-impl-trait]. /// +/// ## Ergonomic clones +/// +/// Use a values, copying its content if the value implements `Copy`, cloning the contents if the +/// value implements `UseCloned` or moving it otherwise. +/// /// [`crate`]: keyword.crate.html /// [`self`]: keyword.self.html /// [`super`]: keyword.super.html @@ -2383,6 +2448,39 @@ mod while_keyword {} /// /// We have written an [async book] detailing `async`/`await` and trade-offs compared to using threads. /// +/// ## Control Flow +/// [`return`] statements and [`?`][try operator] operators within `async` blocks do not cause +/// a return from the parent function; rather, they cause the `Future` returned by the block to +/// return with that value. +/// +/// For example, the following Rust function will return `5`, causing `x` to take the [`!` type][never type]: +/// ```rust +/// #[expect(unused_variables)] +/// fn example() -> i32 { +/// let x = { +/// return 5; +/// }; +/// } +/// ``` +/// In contrast, the following asynchronous function assigns a `Future` to `x`, and +/// only returns `5` when `x` is `.await`ed: +/// ```rust +/// async fn example() -> i32 { +/// let x = async { +/// return 5; +/// }; +/// +/// x.await +/// } +/// ``` +/// Code using `?` behaves similarly - it causes the `async` block to return a [`Result`] without +/// affecting the parent function. +/// +/// Note that you cannot use `break` or `continue` from within an `async` block to affect the +/// control flow of a loop in the parent function. +/// +/// Control flow in `async` blocks is documented further in the [async book][async book blocks]. +/// /// ## Editions /// /// `async` is a keyword from the 2018 edition onwards. @@ -2392,6 +2490,11 @@ mod while_keyword {} /// [`Future`]: future::Future /// [`.await`]: ../std/keyword.await.html /// [async book]: https://rust-lang.github.io/async-book/ +/// [`return`]: ../std/keyword.return.html +/// [try operator]: ../reference/expressions/operator-expr.html#r-expr.try +/// [never type]: ../reference/types/never.html +/// [`Result`]: result::Result +/// [async book blocks]: https://rust-lang.github.io/async-book/part-guide/more-async-await.html#async-blocks mod async_keyword {} #[doc(keyword = "await")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 938b8c6e4f41b..ef41b47384d61 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -174,7 +174,9 @@ //! //! - after-main use of thread-locals, which also affects additional features: //! - [`thread::current()`] -//! - before-main stdio file descriptors are not guaranteed to be open on unix platforms +//! - under UNIX, before main, file descriptors 0, 1, and 2 may be unchanged +//! (they are guaranteed to be open during main, +//! and are opened to /dev/null O_RDWR if they weren't open on program start) //! //! //! [I/O]: io @@ -285,6 +287,7 @@ #![feature(cfi_encoding)] #![feature(char_max_len)] #![feature(concat_idents)] +#![feature(core_float_math)] #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(doc_cfg)] @@ -295,11 +298,13 @@ #![feature(extended_varargs_abi_support)] #![feature(f128)] #![feature(f16)] +#![feature(ffi_const)] #![feature(formatting_options)] #![feature(if_let_guard)] #![feature(intra_doc_pointers)] +#![feature(iter_advance_by)] +#![feature(iter_next_chunk)] #![feature(lang_items)] -#![feature(let_chains)] #![feature(link_cfg)] #![feature(linkage)] #![feature(macro_metavar_expr_concat)] @@ -309,7 +314,6 @@ #![feature(needs_panic_runtime)] #![feature(negative_impls)] #![feature(never_type)] -#![feature(no_sanitize)] #![feature(optimize_attribute)] #![feature(prelude_import)] #![feature(rustc_attrs)] @@ -319,7 +323,9 @@ #![feature(strict_provenance_lints)] #![feature(thread_local)] #![feature(try_blocks)] +#![feature(try_trait_v2)] #![feature(type_alias_impl_trait)] +#![feature(unsigned_signed_diff)] // tidy-alphabetical-end // // Library features (core): @@ -327,7 +333,6 @@ #![feature(array_chunks)] #![feature(bstr)] #![feature(bstr_internals)] -#![feature(c_str_module)] #![feature(char_internals)] #![feature(clone_to_uninit)] #![feature(core_intrinsics)] @@ -338,9 +343,11 @@ #![feature(exact_size_is_empty)] #![feature(exclusive_wrapper)] #![feature(extend_one)] +#![feature(float_algebraic)] #![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(fmt_internals)] +#![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(hint_must_use)] @@ -666,6 +673,8 @@ pub mod arch { pub use std_detect::is_loongarch_feature_detected; #[unstable(feature = "is_riscv_feature_detected", issue = "111192")] pub use std_detect::is_riscv_feature_detected; + #[unstable(feature = "stdarch_s390x_feature_detection", issue = "135413")] + pub use std_detect::is_s390x_feature_detected; #[stable(feature = "simd_x86", since = "1.27.0")] pub use std_detect::is_x86_feature_detected; #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] @@ -699,8 +708,14 @@ pub use core::cfg_match; reason = "`concat_bytes` is not stable enough for use and is subject to change" )] pub use core::concat_bytes; +#[stable(feature = "matches_macro", since = "1.42.0")] +#[allow(deprecated, deprecated_in_future)] +pub use core::matches; #[stable(feature = "core_primitive", since = "1.43.0")] pub use core::primitive; +#[stable(feature = "todo_macro", since = "1.40.0")] +#[allow(deprecated, deprecated_in_future)] +pub use core::todo; // Re-export built-in macros defined through core. #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow(deprecated)] @@ -713,8 +728,8 @@ pub use core::{ #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::{ - assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches, todo, r#try, - unimplemented, unreachable, write, writeln, + assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, r#try, unimplemented, + unreachable, write, writeln, }; // Include a number of private modules that exist solely to provide diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index e0f9f0bb5cee4..f008d42804c08 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -41,7 +41,7 @@ macro_rules! panic { /// Use `print!` only for the primary output of your program. Use /// [`eprint!`] instead to print error and progress messages. /// -/// See [the formatting documentation in `std::fmt`](../std/fmt/index.html) +/// See the formatting documentation in [`std::fmt`](crate::fmt) /// for details of the macro argument syntax. /// /// [flush]: crate::io::Write::flush @@ -106,7 +106,7 @@ macro_rules! print { /// Use `println!` only for the primary output of your program. Use /// [`eprintln!`] instead to print error and progress messages. /// -/// See [the formatting documentation in `std::fmt`](../std/fmt/index.html) +/// See the formatting documentation in [`std::fmt`](crate::fmt) /// for details of the macro argument syntax. /// /// [`std::fmt`]: crate::fmt @@ -156,7 +156,7 @@ macro_rules! println { /// [`io::stderr`]: crate::io::stderr /// [`io::stdout`]: crate::io::stdout /// -/// See [the formatting documentation in `std::fmt`](../std/fmt/index.html) +/// See the formatting documentation in [`std::fmt`](crate::fmt) /// for details of the macro argument syntax. /// /// # Panics @@ -190,7 +190,7 @@ macro_rules! eprint { /// Use `eprintln!` only for error and progress messages. Use `println!` /// instead for the primary output of your program. /// -/// See [the formatting documentation in `std::fmt`](../std/fmt/index.html) +/// See the formatting documentation in [`std::fmt`](crate::fmt) /// for details of the macro argument syntax. /// /// [`io::stderr`]: crate::io::stderr diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index 4c8905c0d4609..41e623e79ce27 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -101,7 +101,7 @@ use crate::{io, iter, option, slice, vec}; /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); /// ``` /// -/// [`TcpStream::connect`] is an example of an function that utilizes +/// [`TcpStream::connect`] is an example of a function that utilizes /// `ToSocketAddrs` as a trait bound on its parameter in order to accept /// different types: /// diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 9b68f872955c0..6a95142640726 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -5,7 +5,8 @@ not(any( target_os = "emscripten", all(target_os = "wasi", target_env = "p1"), - target_os = "xous" + target_os = "xous", + target_os = "trusty", )) ))] mod tests; diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index a7b5cdf4ec061..03003037b295c 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -315,12 +315,8 @@ fn read_buf() { let mut buf = BorrowedBuf::from(buf.as_mut_slice()); t!(s.read_buf(buf.unfilled())); assert_eq!(buf.filled(), &[1, 2, 3, 4]); - - // FIXME: sgx uses default_read_buf that initializes the buffer. - if cfg!(not(target_env = "sgx")) { - // TcpStream::read_buf should omit buffer initialization. - assert_eq!(buf.init_len(), 4); - } + // TcpStream::read_buf should omit buffer initialization. + assert_eq!(buf.init_len(), 4); t.join().ok().expect("thread panicked"); }) diff --git a/library/std/src/net/test.rs b/library/std/src/net/test.rs index a5c3983cd89ec..df48b2f2420c3 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/test.rs @@ -31,3 +31,14 @@ pub fn tsa(a: A) -> Result, String> { Err(e) => Err(e.to_string()), } } + +pub fn compare_ignore_zoneid(a: &SocketAddr, b: &SocketAddr) -> bool { + match (a, b) { + (SocketAddr::V6(a), SocketAddr::V6(b)) => { + a.ip().segments() == b.ip().segments() + && a.flowinfo() == b.flowinfo() + && a.port() == b.port() + } + _ => a == b, + } +} diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 3eb798ad34aaa..a97b3299774bb 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -4,7 +4,8 @@ target_os = "emscripten", all(target_os = "wasi", target_env = "p1"), target_env = "sgx", - target_os = "xous" + target_os = "xous", + target_os = "trusty", )) ))] mod tests; diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index 1c8c58d187957..91da3135f97c6 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -1,4 +1,4 @@ -use crate::net::test::{next_test_ip4, next_test_ip6}; +use crate::net::test::{compare_ignore_zoneid, next_test_ip4, next_test_ip6}; use crate::net::*; use crate::sync::mpsc::channel; use crate::thread; @@ -46,7 +46,7 @@ fn socket_smoke_test_ip4() { let (nread, src) = t!(server.recv_from(&mut buf)); assert_eq!(nread, 1); assert_eq!(buf[0], 99); - assert_eq!(src, client_ip); + assert_eq!(compare_ignore_zoneid(&src, &client_ip), true); rx2.recv().unwrap(); }) } @@ -78,7 +78,9 @@ fn udp_clone_smoke() { let _t = thread::spawn(move || { let mut buf = [0, 0]; - assert_eq!(sock2.recv_from(&mut buf).unwrap(), (1, addr1)); + let res = sock2.recv_from(&mut buf).unwrap(); + assert_eq!(res.0, 1); + assert_eq!(compare_ignore_zoneid(&res.1, &addr1), true); assert_eq!(buf[0], 1); t!(sock2.send_to(&[2], &addr1)); }); @@ -94,7 +96,9 @@ fn udp_clone_smoke() { }); tx1.send(()).unwrap(); let mut buf = [0, 0]; - assert_eq!(sock1.recv_from(&mut buf).unwrap(), (1, addr2)); + let res = sock1.recv_from(&mut buf).unwrap(); + assert_eq!(res.0, 1); + assert_eq!(compare_ignore_zoneid(&res.1, &addr2), true); rx2.recv().unwrap(); }) } diff --git a/library/std/src/os/cygwin/fs.rs b/library/std/src/os/cygwin/fs.rs new file mode 100644 index 0000000000000..5533264fd515b --- /dev/null +++ b/library/std/src/os/cygwin/fs.rs @@ -0,0 +1,102 @@ +#![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_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_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 + } + 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 + } +} diff --git a/library/std/src/os/cygwin/mod.rs b/library/std/src/os/cygwin/mod.rs new file mode 100644 index 0000000000000..7f6d6a645c855 --- /dev/null +++ b/library/std/src/os/cygwin/mod.rs @@ -0,0 +1,4 @@ +//! Cygwin-specific definitions +#![stable(feature = "raw_ext", since = "1.1.0")] +pub mod fs; +pub(crate) mod raw; diff --git a/library/std/src/os/cygwin/raw.rs b/library/std/src/os/cygwin/raw.rs new file mode 100644 index 0000000000000..2bae1477fcfe1 --- /dev/null +++ b/library/std/src/os/cygwin/raw.rs @@ -0,0 +1,4 @@ +//! Cygwin-specific raw type definitions. + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use libc::{blkcnt_t, blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t, pthread_t, time_t}; diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs index 35de4860fe249..95cf4932e6e2c 100644 --- a/library/std/src/os/fd/mod.rs +++ b/library/std/src/os/fd/mod.rs @@ -13,6 +13,7 @@ mod raw; mod owned; // Implementations for `AsRawFd` etc. for network types. +#[cfg(not(target_os = "trusty"))] mod net; #[cfg(test)] diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 5cec11ecccf1c..10e1e73a115bd 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -4,12 +4,20 @@ #![deny(unsafe_op_in_unsafe_fn)] use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +#[cfg(not(target_os = "trusty"))] +use crate::fs; use crate::marker::PhantomData; use crate::mem::ManuallyDrop; -#[cfg(not(any(target_arch = "wasm32", target_env = "sgx", target_os = "hermit")))] +#[cfg(not(any( + target_arch = "wasm32", + target_env = "sgx", + target_os = "hermit", + target_os = "trusty" +)))] use crate::sys::cvt; +#[cfg(not(target_os = "trusty"))] use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::{fmt, fs, io}; +use crate::{fmt, io}; type ValidRawFd = core::num::niche_types::NotAllOnes; @@ -87,7 +95,7 @@ impl OwnedFd { impl BorrowedFd<'_> { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `BorrowedFd` instance. - #[cfg(not(any(target_arch = "wasm32", target_os = "hermit")))] + #[cfg(not(any(target_arch = "wasm32", target_os = "hermit", target_os = "trusty")))] #[stable(feature = "io_safety", since = "1.63.0")] pub fn try_clone_to_owned(&self) -> crate::io::Result { // We want to atomically duplicate this file descriptor and set the @@ -110,7 +118,7 @@ impl BorrowedFd<'_> { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `BorrowedFd` instance. - #[cfg(any(target_arch = "wasm32", target_os = "hermit"))] + #[cfg(any(target_arch = "wasm32", target_os = "hermit", target_os = "trusty"))] #[stable(feature = "io_safety", since = "1.63.0")] pub fn try_clone_to_owned(&self) -> crate::io::Result { Err(crate::io::Error::UNSUPPORTED_PLATFORM) @@ -280,6 +288,7 @@ impl AsFd for OwnedFd { } #[stable(feature = "io_safety", since = "1.63.0")] +#[cfg(not(target_os = "trusty"))] impl AsFd for fs::File { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { @@ -288,6 +297,7 @@ impl AsFd for fs::File { } #[stable(feature = "io_safety", since = "1.63.0")] +#[cfg(not(target_os = "trusty"))] impl From for OwnedFd { /// Takes ownership of a [`File`](fs::File)'s underlying file descriptor. #[inline] @@ -297,6 +307,7 @@ impl From for OwnedFd { } #[stable(feature = "io_safety", since = "1.63.0")] +#[cfg(not(target_os = "trusty"))] impl From for fs::File { /// Returns a [`File`](fs::File) that takes ownership of the given /// file descriptor. @@ -307,6 +318,7 @@ impl From for fs::File { } #[stable(feature = "io_safety", since = "1.63.0")] +#[cfg(not(target_os = "trusty"))] impl AsFd for crate::net::TcpStream { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { @@ -315,6 +327,7 @@ impl AsFd for crate::net::TcpStream { } #[stable(feature = "io_safety", since = "1.63.0")] +#[cfg(not(target_os = "trusty"))] impl From for OwnedFd { /// Takes ownership of a [`TcpStream`](crate::net::TcpStream)'s socket file descriptor. #[inline] @@ -324,6 +337,7 @@ impl From for OwnedFd { } #[stable(feature = "io_safety", since = "1.63.0")] +#[cfg(not(target_os = "trusty"))] impl From for crate::net::TcpStream { #[inline] fn from(owned_fd: OwnedFd) -> Self { @@ -334,6 +348,7 @@ impl From for crate::net::TcpStream { } #[stable(feature = "io_safety", since = "1.63.0")] +#[cfg(not(target_os = "trusty"))] impl AsFd for crate::net::TcpListener { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { @@ -342,6 +357,7 @@ impl AsFd for crate::net::TcpListener { } #[stable(feature = "io_safety", since = "1.63.0")] +#[cfg(not(target_os = "trusty"))] impl From for OwnedFd { /// Takes ownership of a [`TcpListener`](crate::net::TcpListener)'s socket file descriptor. #[inline] @@ -351,6 +367,7 @@ impl From for OwnedFd { } #[stable(feature = "io_safety", since = "1.63.0")] +#[cfg(not(target_os = "trusty"))] impl From for crate::net::TcpListener { #[inline] fn from(owned_fd: OwnedFd) -> Self { @@ -361,6 +378,7 @@ impl From for crate::net::TcpListener { } #[stable(feature = "io_safety", since = "1.63.0")] +#[cfg(not(target_os = "trusty"))] impl AsFd for crate::net::UdpSocket { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { @@ -369,6 +387,7 @@ impl AsFd for crate::net::UdpSocket { } #[stable(feature = "io_safety", since = "1.63.0")] +#[cfg(not(target_os = "trusty"))] impl From for OwnedFd { /// Takes ownership of a [`UdpSocket`](crate::net::UdpSocket)'s file descriptor. #[inline] @@ -378,6 +397,7 @@ impl From for OwnedFd { } #[stable(feature = "io_safety", since = "1.63.0")] +#[cfg(not(target_os = "trusty"))] impl From for crate::net::UdpSocket { #[inline] fn from(owned_fd: OwnedFd) -> Self { @@ -484,3 +504,51 @@ impl<'a> AsFd for io::StderrLock<'a> { unsafe { BorrowedFd::borrow_raw(2) } } } + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +#[cfg(not(target_os = "trusty"))] +impl AsFd for io::PipeReader { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +#[cfg(not(target_os = "trusty"))] +impl From for OwnedFd { + fn from(pipe: io::PipeReader) -> Self { + pipe.0.into_inner() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +#[cfg(not(target_os = "trusty"))] +impl AsFd for io::PipeWriter { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +#[cfg(not(target_os = "trusty"))] +impl From for OwnedFd { + fn from(pipe: io::PipeWriter) -> Self { + pipe.0.into_inner() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +#[cfg(not(target_os = "trusty"))] +impl From for io::PipeReader { + fn from(owned_fd: OwnedFd) -> Self { + Self(FromInner::from_inner(owned_fd)) + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +#[cfg(not(target_os = "trusty"))] +impl From for io::PipeWriter { + fn from(owned_fd: OwnedFd) -> Self { + Self(FromInner::from_inner(owned_fd)) + } +} diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 03dff94350dad..34a6cf1a8b84d 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -5,6 +5,9 @@ #[cfg(target_os = "hermit")] use hermit_abi as libc; +#[cfg(not(target_os = "trusty"))] +use crate::fs; +use crate::io; #[cfg(target_os = "hermit")] use crate::os::hermit::io::OwnedFd; #[cfg(not(target_os = "hermit"))] @@ -15,8 +18,8 @@ use crate::os::unix::io::AsFd; use crate::os::unix::io::OwnedFd; #[cfg(target_os = "wasi")] use crate::os::wasi::io::OwnedFd; -use crate::sys_common::{AsInner, IntoInner}; -use crate::{fs, io}; +#[cfg(not(target_os = "trusty"))] +use crate::sys_common::{AsInner, FromInner, IntoInner}; /// Raw file descriptors. #[stable(feature = "rust1", since = "1.0.0")] @@ -161,6 +164,7 @@ impl FromRawFd for RawFd { } #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(target_os = "trusty"))] impl AsRawFd for fs::File { #[inline] fn as_raw_fd(&self) -> RawFd { @@ -168,6 +172,7 @@ impl AsRawFd for fs::File { } } #[stable(feature = "from_raw_os", since = "1.1.0")] +#[cfg(not(target_os = "trusty"))] impl FromRawFd for fs::File { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> fs::File { @@ -175,6 +180,7 @@ impl FromRawFd for fs::File { } } #[stable(feature = "into_raw_os", since = "1.4.0")] +#[cfg(not(target_os = "trusty"))] impl IntoRawFd for fs::File { #[inline] fn into_raw_fd(self) -> RawFd { @@ -183,6 +189,7 @@ impl IntoRawFd for fs::File { } #[stable(feature = "asraw_stdio", since = "1.21.0")] +#[cfg(not(target_os = "trusty"))] impl AsRawFd for io::Stdin { #[inline] fn as_raw_fd(&self) -> RawFd { @@ -207,6 +214,7 @@ impl AsRawFd for io::Stderr { } #[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +#[cfg(not(target_os = "trusty"))] impl<'a> AsRawFd for io::StdinLock<'a> { #[inline] fn as_raw_fd(&self) -> RawFd { @@ -276,3 +284,51 @@ impl AsRawFd for Box { (**self).as_raw_fd() } } + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +#[cfg(not(target_os = "trusty"))] +impl AsRawFd for io::PipeReader { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +#[cfg(not(target_os = "trusty"))] +impl FromRawFd for io::PipeReader { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self::from_inner(unsafe { FromRawFd::from_raw_fd(raw_fd) }) + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +#[cfg(not(target_os = "trusty"))] +impl IntoRawFd for io::PipeReader { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +#[cfg(not(target_os = "trusty"))] +impl AsRawFd for io::PipeWriter { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +#[cfg(not(target_os = "trusty"))] +impl FromRawFd for io::PipeWriter { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self::from_inner(unsafe { FromRawFd::from_raw_fd(raw_fd) }) + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +#[cfg(not(target_os = "trusty"))] +impl IntoRawFd for io::PipeWriter { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} diff --git a/library/std/src/os/fd/tests.rs b/library/std/src/os/fd/tests.rs index b39863644f116..7e9cf038e9a75 100644 --- a/library/std/src/os/fd/tests.rs +++ b/library/std/src/os/fd/tests.rs @@ -36,7 +36,6 @@ fn test_fd() { #[cfg(any(unix, target_os = "wasi"))] #[test] fn test_niche_optimizations() { - use crate::mem::size_of; #[cfg(unix)] use crate::os::unix::io::{BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; #[cfg(target_os = "wasi")] diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index e28a1c3e6d5f4..ab7734a795268 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -125,6 +125,8 @@ pub mod windows; pub mod aix; #[cfg(target_os = "android")] pub mod android; +#[cfg(target_os = "cygwin")] +pub mod cygwin; #[cfg(target_os = "dragonfly")] pub mod dragonfly; #[cfg(target_os = "emscripten")] @@ -169,6 +171,8 @@ pub mod rtems; pub mod solaris; #[cfg(target_os = "solid_asp3")] pub mod solid; +#[cfg(target_os = "trusty")] +pub mod trusty; #[cfg(target_os = "uefi")] pub mod uefi; #[cfg(target_os = "vita")] @@ -178,7 +182,7 @@ pub mod vxworks; #[cfg(target_os = "xous")] pub mod xous; -#[cfg(any(unix, target_os = "hermit", target_os = "wasi", doc))] +#[cfg(any(unix, target_os = "hermit", target_os = "trusty", target_os = "wasi", doc))] pub mod fd; #[cfg(any(target_os = "linux", target_os = "android", doc))] diff --git a/library/std/src/os/trusty/io/mod.rs b/library/std/src/os/trusty/io/mod.rs new file mode 100644 index 0000000000000..4cfd448305b65 --- /dev/null +++ b/library/std/src/os/trusty/io/mod.rs @@ -0,0 +1,4 @@ +#![stable(feature = "os_fd", since = "1.66.0")] + +#[stable(feature = "os_fd", since = "1.66.0")] +pub use crate::os::fd::*; diff --git a/library/std/src/os/trusty/mod.rs b/library/std/src/os/trusty/mod.rs new file mode 100644 index 0000000000000..cc67c92d7ff47 --- /dev/null +++ b/library/std/src/os/trusty/mod.rs @@ -0,0 +1,3 @@ +#![stable(feature = "rust1", since = "1.0.0")] + +pub mod io; diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index cf8ae697e389d..ab5406e605c6b 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -4,13 +4,13 @@ use crate::ffi::c_void; use crate::ptr::NonNull; -use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, Ordering}; -static SYSTEM_TABLE: AtomicPtr = AtomicPtr::new(crate::ptr::null_mut()); -static IMAGE_HANDLE: AtomicPtr = AtomicPtr::new(crate::ptr::null_mut()); +static SYSTEM_TABLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); +static IMAGE_HANDLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); // Flag to check if BootServices are still valid. // Start with assuming that they are not available -static BOOT_SERVICES_FLAG: AtomicBool = AtomicBool::new(false); +static BOOT_SERVICES_FLAG: Atomic = AtomicBool::new(false); /// Initializes the global System Table and Image Handle pointers. /// diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 04a45fd035a55..4f9259f39c1ab 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -276,63 +276,89 @@ impl FileExt for fs::File { } /// Unix-specific extensions to [`fs::Permissions`]. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs::{File, Permissions}; +/// use std::io::{ErrorKind, Result as IoResult}; +/// use std::os::unix::fs::PermissionsExt; +/// +/// fn main() -> IoResult<()> { +/// let name = "test_file_for_permissions"; +/// +/// // make sure file does not exist +/// let _ = std::fs::remove_file(name); +/// assert_eq!( +/// File::open(name).unwrap_err().kind(), +/// ErrorKind::NotFound, +/// "file already exists" +/// ); +/// +/// // full read/write/execute mode bits for owner of file +/// // that we want to add to existing mode bits +/// let my_mode = 0o700; +/// +/// // create new file with specified permissions +/// { +/// let file = File::create(name)?; +/// let mut permissions = file.metadata()?.permissions(); +/// eprintln!("Current permissions: {:o}", permissions.mode()); +/// +/// // make sure new permissions are not already set +/// assert!( +/// permissions.mode() & my_mode != my_mode, +/// "permissions already set" +/// ); +/// +/// // either use `set_mode` to change an existing Permissions struct +/// permissions.set_mode(permissions.mode() | my_mode); +/// +/// // or use `from_mode` to construct a new Permissions struct +/// permissions = Permissions::from_mode(permissions.mode() | my_mode); +/// +/// // write new permissions to file +/// file.set_permissions(permissions)?; +/// } +/// +/// let permissions = File::open(name)?.metadata()?.permissions(); +/// eprintln!("New permissions: {:o}", permissions.mode()); +/// +/// // assert new permissions were set +/// assert_eq!( +/// permissions.mode() & my_mode, +/// my_mode, +/// "new permissions not set" +/// ); +/// Ok(()) +/// } +/// ``` +/// +/// ```no_run +/// use std::fs::Permissions; +/// use std::os::unix::fs::PermissionsExt; +/// +/// // read/write for owner and read for others +/// let my_mode = 0o644; +/// let mut permissions = Permissions::from_mode(my_mode); +/// assert_eq!(permissions.mode(), my_mode); +/// +/// // read/write/execute for owner +/// let other_mode = 0o700; +/// permissions.set_mode(other_mode); +/// assert_eq!(permissions.mode(), other_mode); +/// ``` #[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(()) - /// } - /// ``` + /// Returns the mode permission bits #[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(()) - /// } - /// ``` + /// Sets the mode permission bits. #[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); - /// ``` + /// Creates a new instance from the given mode permission bits. #[stable(feature = "fs_ext", since = "1.1.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "permissions_from_mode")] fn from_mode(mode: u32) -> Self; @@ -1074,3 +1100,39 @@ pub fn lchown>(dir: P, uid: Option, gid: Option) -> io: pub fn chroot>(dir: P) -> io::Result<()> { sys::fs::chroot(dir.as_ref()) } + +/// Create a FIFO special file at the specified path with the specified mode. +/// +/// # Examples +/// +/// ```no_run +/// # #![feature(unix_mkfifo)] +/// # #[cfg(not(unix))] +/// # fn main() {} +/// # #[cfg(unix)] +/// # fn main() -> std::io::Result<()> { +/// # use std::{ +/// # os::unix::fs::{mkfifo, PermissionsExt}, +/// # fs::{File, Permissions, remove_file}, +/// # io::{Write, Read}, +/// # }; +/// # let _ = remove_file("/tmp/fifo"); +/// mkfifo("/tmp/fifo", Permissions::from_mode(0o774))?; +/// +/// let mut wx = File::options().read(true).write(true).open("/tmp/fifo")?; +/// let mut rx = File::open("/tmp/fifo")?; +/// +/// wx.write_all(b"hello, world!")?; +/// drop(wx); +/// +/// let mut s = String::new(); +/// rx.read_to_string(&mut s)?; +/// +/// assert_eq!(s, "hello, world!"); +/// # Ok(()) +/// # } +/// ``` +#[unstable(feature = "unix_mkfifo", issue = "139324")] +pub fn mkfifo>(path: P, permissions: Permissions) -> io::Result<()> { + sys::fs::mkfifo(path.as_ref(), permissions.mode()) +} diff --git a/library/std/src/os/unix/fs/tests.rs b/library/std/src/os/unix/fs/tests.rs index db9621c8c205c..1840bb38c17c8 100644 --- a/library/std/src/os/unix/fs/tests.rs +++ b/library/std/src/os/unix/fs/tests.rs @@ -55,3 +55,23 @@ fn write_vectored_at() { let content = fs::read(&filename).unwrap(); assert_eq!(&content, expected); } + +#[test] +fn test_mkfifo() { + let tmp_dir = crate::test_helpers::tmpdir(); + + let fifo = tmp_dir.path().join("fifo"); + + mkfifo(&fifo, Permissions::from_mode(0o774)).unwrap(); + + let mut wx = fs::File::options().read(true).write(true).open(&fifo).unwrap(); + let mut rx = fs::File::open(fifo).unwrap(); + + wx.write_all(b"hello, world!").unwrap(); + drop(wx); + + let mut s = String::new(); + rx.read_to_string(&mut s).unwrap(); + + assert_eq!(s, "hello, world!"); +} diff --git a/library/std/src/os/unix/io/tests.rs b/library/std/src/os/unix/io/tests.rs index 84d2a7a1a91b4..fc147730578ac 100644 --- a/library/std/src/os/unix/io/tests.rs +++ b/library/std/src/os/unix/io/tests.rs @@ -1,4 +1,3 @@ -use crate::mem::size_of; use crate::os::unix::io::RawFd; #[test] diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 2f9dffe8c6561..5802b6539651c 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -41,6 +41,8 @@ mod platform { pub use crate::os::aix::*; #[cfg(target_os = "android")] pub use crate::os::android::*; + #[cfg(target_os = "cygwin")] + pub use crate::os::cygwin::*; #[cfg(target_vendor = "apple")] pub use crate::os::darwin::*; #[cfg(target_os = "dragonfly")] diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 56789f235fdab..cb1246db3109e 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -94,7 +94,7 @@ impl SocketAddr { { unsafe { let mut addr: libc::sockaddr_un = mem::zeroed(); - let mut len = mem::size_of::() as libc::socklen_t; + let mut len = size_of::() as libc::socklen_t; cvt(f((&raw mut addr) as *mut _, &mut len))?; SocketAddr::from_parts(addr, len) } diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 82446ea107fe5..7735637c84059 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -9,6 +9,7 @@ target_os = "illumos", target_os = "haiku", target_os = "nto", + target_os = "cygwin" ))] use libc::MSG_NOSIGNAL; @@ -37,6 +38,7 @@ use crate::{fmt, io}; target_os = "illumos", target_os = "haiku", target_os = "nto", + target_os = "cygwin" )))] const MSG_NOSIGNAL: core::ffi::c_int = 0x0; diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index be236317d047d..27428c9eb2855 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -177,7 +177,7 @@ impl UnixListener { #[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 mut len = size_of_val(&storage) as libc::socklen_t; let sock = self.0.accept((&raw mut storage) as *mut _, &mut len)?; let addr = SocketAddr::from_parts(storage, len)?; Ok((UnixStream(sock), addr)) diff --git a/library/std/src/os/unix/net/mod.rs b/library/std/src/os/unix/net/mod.rs index 3e45e3533ed28..6cd62303a5325 100644 --- a/library/std/src/os/unix/net/mod.rs +++ b/library/std/src/os/unix/net/mod.rs @@ -21,6 +21,7 @@ mod tests; target_os = "openbsd", target_os = "nto", target_vendor = "apple", + target_os = "cygwin" ))] mod ucred; @@ -44,6 +45,7 @@ pub use self::stream::*; target_os = "openbsd", target_os = "nto", target_vendor = "apple", + target_os = "cygwin", ))] #[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")] pub use self::ucred::*; diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index cb210b41eae19..1bd3bab5e3738 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -10,6 +10,7 @@ use super::{SocketAncillary, recv_vectored_with_ancillary_from, send_vectored_wi target_os = "openbsd", target_os = "nto", target_vendor = "apple", + target_os = "cygwin" ))] use super::{UCred, peer_cred}; use crate::fmt; @@ -231,6 +232,7 @@ impl UnixStream { target_os = "openbsd", target_os = "nto", target_vendor = "apple", + target_os = "cygwin" ))] pub fn peer_cred(&self) -> io::Result { peer_cred(self) @@ -305,11 +307,11 @@ impl UnixStream { /// /// ```no_run /// use std::io; - /// use std::net::UdpSocket; + /// use std::os::unix::net::UnixStream; /// use std::time::Duration; /// /// fn main() -> std::io::Result<()> { - /// let socket = UdpSocket::bind("127.0.0.1:34254")?; + /// let socket = UnixStream::connect("/tmp/sock")?; /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); /// let err = result.unwrap_err(); /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); diff --git a/library/std/src/os/unix/net/ucred.rs b/library/std/src/os/unix/net/ucred.rs index e1014a4f296dd..36fb9c46b4aba 100644 --- a/library/std/src/os/unix/net/ucred.rs +++ b/library/std/src/os/unix/net/ucred.rs @@ -33,23 +33,23 @@ pub(super) use self::impl_apple::peer_cred; target_os = "nto" ))] pub(super) use self::impl_bsd::peer_cred; -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] pub(super) use self::impl_linux::peer_cred; -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin"))] mod impl_linux { use libc::{SO_PEERCRED, SOL_SOCKET, c_void, getsockopt, socklen_t, ucred}; use super::UCred; + use crate::io; use crate::os::unix::io::AsRawFd; use crate::os::unix::net::UnixStream; - use crate::{io, mem}; pub fn peer_cred(socket: &UnixStream) -> io::Result { - let ucred_size = mem::size_of::(); + let ucred_size = size_of::(); // Trivial sanity checks. - assert!(mem::size_of::() <= mem::size_of::()); + assert!(size_of::() <= size_of::()); assert!(ucred_size <= u32::MAX as usize); let mut ucred_size = ucred_size as socklen_t; @@ -64,7 +64,7 @@ mod impl_linux { &mut ucred_size, ); - if ret == 0 && ucred_size as usize == mem::size_of::() { + if ret == 0 && ucred_size as usize == size_of::() { Ok(UCred { uid: ucred.uid, gid: ucred.gid, pid: Some(ucred.pid) }) } else { Err(io::Error::last_os_error()) @@ -101,9 +101,9 @@ mod impl_apple { use libc::{LOCAL_PEERPID, SOL_LOCAL, c_void, getpeereid, getsockopt, pid_t, socklen_t}; use super::UCred; + use crate::io; use crate::os::unix::io::AsRawFd; use crate::os::unix::net::UnixStream; - use crate::{io, mem}; pub fn peer_cred(socket: &UnixStream) -> io::Result { let mut cred = UCred { uid: 1, gid: 1, pid: None }; @@ -115,7 +115,7 @@ mod impl_apple { } let mut pid: pid_t = 1; - let mut pid_size = mem::size_of::() as socklen_t; + let mut pid_size = size_of::() as socklen_t; let ret = getsockopt( socket.as_raw_fd(), @@ -125,7 +125,7 @@ mod impl_apple { &mut pid_size, ); - if ret == 0 && pid_size as usize == mem::size_of::() { + if ret == 0 && pid_size as usize == size_of::() { cred.pid = Some(pid); Ok(cred) } else { diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 7c3fa7d6507e7..57ce3c5a4bf4a 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -8,6 +8,7 @@ use cfg_if::cfg_if; use crate::ffi::OsStr; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::path::Path; use crate::sealed::Sealed; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{io, process, sys}; @@ -197,6 +198,18 @@ pub trait CommandExt: Sealed { /// ``` #[stable(feature = "process_set_process_group", since = "1.64.0")] fn process_group(&mut self, pgroup: i32) -> &mut process::Command; + + /// Set the root of the child process. This calls `chroot` in the child process before executing + /// the command. + /// + /// This happens before changing to the directory specified with + /// [`process::Command::current_dir`], and that directory will be relative to the new root. + /// + /// If no directory has been specified with [`process::Command::current_dir`], this will set the + /// directory to `/`, to avoid leaving the current directory outside the chroot. (This is an + /// intentional difference from the underlying `chroot` system call.) + #[unstable(feature = "process_chroot", issue = "141298")] + fn chroot>(&mut self, dir: P) -> &mut process::Command; } #[stable(feature = "rust1", since = "1.0.0")] @@ -242,6 +255,11 @@ impl CommandExt for process::Command { self.as_inner_mut().pgroup(pgroup); self } + + fn chroot>(&mut self, dir: P) -> &mut process::Command { + self.as_inner_mut().chroot(dir.as_ref()); + self + } } /// Unix-specific extensions to [`process::ExitStatus`] and diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 34f0e89f2f1ee..5ea91dd6521ad 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -72,7 +72,6 @@ pub trait FileExt { /// 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) { @@ -144,7 +143,6 @@ pub trait FileExt { /// non-[`io::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) { diff --git a/library/std/src/os/wasi/io/tests.rs b/library/std/src/os/wasi/io/tests.rs index 418274752b0ad..c5c6a19a6c885 100644 --- a/library/std/src/os/wasi/io/tests.rs +++ b/library/std/src/os/wasi/io/tests.rs @@ -1,4 +1,3 @@ -use crate::mem::size_of; use crate::os::wasi::io::RawFd; #[test] diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index 76f5f549dd244..4fc04b79315f9 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -660,3 +660,45 @@ impl From> for OwnedHandle { join_handle.into_inner().into_handle().into_inner() } } + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl AsHandle for io::PipeReader { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.as_handle() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl From for OwnedHandle { + fn from(pipe: io::PipeReader) -> Self { + pipe.into_inner().into_inner() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl AsHandle for io::PipeWriter { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.as_handle() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl From for OwnedHandle { + fn from(pipe: io::PipeWriter) -> Self { + pipe.into_inner().into_inner() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl From for io::PipeReader { + fn from(owned_handle: OwnedHandle) -> Self { + Self::from_inner(FromInner::from_inner(owned_handle)) + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl From for io::PipeWriter { + fn from(owned_handle: OwnedHandle) -> Self { + Self::from_inner(FromInner::from_inner(owned_handle)) + } +} diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index c0517fab95068..a3ec7440338d2 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -310,3 +310,45 @@ impl IntoRawSocket for net::UdpSocket { self.into_inner().into_socket().into_inner().into_raw_socket() } } + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl AsRawHandle for io::PipeReader { + fn as_raw_handle(&self) -> RawHandle { + self.0.as_raw_handle() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl FromRawHandle for io::PipeReader { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + unsafe { Self::from_inner(FromRawHandle::from_raw_handle(raw_handle)) } + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl IntoRawHandle for io::PipeReader { + fn into_raw_handle(self) -> RawHandle { + self.0.into_raw_handle() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl AsRawHandle for io::PipeWriter { + fn as_raw_handle(&self) -> RawHandle { + self.0.as_raw_handle() + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl FromRawHandle for io::PipeWriter { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + unsafe { Self::from_inner(FromRawHandle::from_raw_handle(raw_handle)) } + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl IntoRawHandle for io::PipeWriter { + fn into_raw_handle(self) -> RawHandle { + self.0.into_raw_handle() + } +} diff --git a/library/std/src/os/windows/io/tests.rs b/library/std/src/os/windows/io/tests.rs index 41734e52e8cce..029b6f5cd3d9e 100644 --- a/library/std/src/os/windows/io/tests.rs +++ b/library/std/src/os/windows/io/tests.rs @@ -1,6 +1,5 @@ #[test] fn test_niche_optimizations_socket() { - use crate::mem::size_of; use crate::os::windows::io::{ BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, }; diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 201274cf03aec..c223eee95b5f5 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -344,6 +344,27 @@ pub trait CommandExt: Sealed { &mut self, attribute_list: &ProcThreadAttributeList<'_>, ) -> io::Result; + + /// When true, sets the `STARTF_RUNFULLSCREEN` flag on the [STARTUPINFO][1] struct before passing it to `CreateProcess`. + /// + /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")] + fn startupinfo_fullscreen(&mut self, enabled: bool) -> &mut process::Command; + + /// When true, sets the `STARTF_UNTRUSTEDSOURCE` flag on the [STARTUPINFO][1] struct before passing it to `CreateProcess`. + /// + /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")] + fn startupinfo_untrusted_source(&mut self, enabled: bool) -> &mut process::Command; + + /// When specified, sets the following flags on the [STARTUPINFO][1] struct before passing it to `CreateProcess`: + /// - If `Some(true)`, sets `STARTF_FORCEONFEEDBACK` + /// - If `Some(false)`, sets `STARTF_FORCEOFFFEEDBACK` + /// - If `None`, does not set any flags + /// + /// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + #[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")] + fn startupinfo_force_feedback(&mut self, enabled: Option) -> &mut process::Command; } #[stable(feature = "windows_process_extensions", since = "1.16.0")] @@ -385,6 +406,21 @@ impl CommandExt for process::Command { .spawn_with_attributes(sys::process::Stdio::Inherit, true, Some(attribute_list)) .map(process::Child::from_inner) } + + fn startupinfo_fullscreen(&mut self, enabled: bool) -> &mut process::Command { + self.as_inner_mut().startupinfo_fullscreen(enabled); + self + } + + fn startupinfo_untrusted_source(&mut self, enabled: bool) -> &mut process::Command { + self.as_inner_mut().startupinfo_untrusted_source(enabled); + self + } + + fn startupinfo_force_feedback(&mut self, enabled: Option) -> &mut process::Command { + self.as_inner_mut().startupinfo_force_feedback(enabled); + self + } } #[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")] @@ -500,11 +536,7 @@ impl<'a> ProcThreadAttributeListBuilder<'a> { /// [1]: pub fn attribute(self, attribute: usize, value: &'a T) -> Self { unsafe { - self.raw_attribute( - attribute, - ptr::addr_of!(*value).cast::(), - crate::mem::size_of::(), - ) + self.raw_attribute(attribute, ptr::addr_of!(*value).cast::(), size_of::()) } } @@ -535,7 +567,7 @@ impl<'a> ProcThreadAttributeListBuilder<'a> { /// pub Y: i16, /// } /// - /// extern "system" { + /// unsafe extern "system" { /// fn CreatePipe( /// hreadpipe: *mut HANDLE, /// hwritepipe: *mut HANDLE, @@ -574,7 +606,7 @@ impl<'a> ProcThreadAttributeListBuilder<'a> { /// .raw_attribute( /// PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, /// h_pc as *const c_void, - /// std::mem::size_of::(), + /// size_of::(), /// ) /// .finish()? /// }; diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs index 1db314e9ddad7..9394f0a0496b2 100644 --- a/library/std/src/os/xous/ffi.rs +++ b/library/std/src/os/xous/ffi.rs @@ -368,7 +368,7 @@ pub(crate) unsafe fn map_memory( let mut a0 = Syscall::MapMemory as usize; let mut a1 = phys.map(|p| p.as_ptr() as usize).unwrap_or_default(); let mut a2 = virt.map(|p| p.as_ptr() as usize).unwrap_or_default(); - let a3 = count * core::mem::size_of::(); + let a3 = count * size_of::(); let a4 = flags.bits(); let a5 = 0; let a6 = 0; @@ -392,7 +392,7 @@ pub(crate) unsafe fn map_memory( if result == SyscallResult::MemoryRange as usize { let start = core::ptr::with_exposed_provenance_mut::(a1); - let len = a2 / core::mem::size_of::(); + let len = a2 / size_of::(); let end = unsafe { start.add(len) }; Ok(unsafe { core::slice::from_raw_parts_mut(start, len) }) } else if result == SyscallResult::Error as usize { @@ -409,7 +409,7 @@ pub(crate) unsafe fn map_memory( pub(crate) unsafe fn unmap_memory(range: *mut [T]) -> Result<(), Error> { let mut a0 = Syscall::UnmapMemory as usize; let mut a1 = range.as_mut_ptr() as usize; - let a2 = range.len() * core::mem::size_of::(); + let a2 = range.len() * size_of::(); let a3 = 0; let a4 = 0; let a5 = 0; @@ -455,7 +455,7 @@ pub(crate) unsafe fn update_memory_flags( ) -> Result<(), Error> { let mut a0 = Syscall::UpdateMemoryFlags as usize; let mut a1 = range.as_mut_ptr() as usize; - let a2 = range.len() * core::mem::size_of::(); + let a2 = range.len() * size_of::(); let a3 = new_flags.bits(); let a4 = 0; // Process ID is currently None let a5 = 0; diff --git a/library/std/src/os/xous/services.rs b/library/std/src/os/xous/services.rs index 93916750c0547..0681485ea0610 100644 --- a/library/std/src/os/xous/services.rs +++ b/library/std/src/os/xous/services.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use crate::os::xous::ffi::Connection; @@ -106,7 +106,7 @@ pub fn try_connect(name: &str) -> Option { ns::try_connect_with_name(name) } -static NAME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); +static NAME_SERVER_CONNECTION: Atomic = AtomicU32::new(0); /// Returns a `Connection` to the name server. If the name server has not been started, /// then this call will block until the name server has been started. The `Connection` diff --git a/library/std/src/os/xous/services/dns.rs b/library/std/src/os/xous/services/dns.rs index 0288164839360..7641d1f15e444 100644 --- a/library/std/src/os/xous/services/dns.rs +++ b/library/std/src/os/xous/services/dns.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use crate::os::xous::ffi::Connection; use crate::os::xous::services::connect; @@ -17,7 +17,7 @@ impl Into for DnsLendMut { /// Returns a `Connection` to the DNS lookup server. This server is used for /// querying domain name values. pub(crate) fn dns_server() -> Connection { - static DNS_CONNECTION: AtomicU32 = AtomicU32::new(0); + static DNS_CONNECTION: Atomic = AtomicU32::new(0); let cid = DNS_CONNECTION.load(Ordering::Relaxed); if cid != 0 { return cid.into(); diff --git a/library/std/src/os/xous/services/log.rs b/library/std/src/os/xous/services/log.rs index 1661011ca64b1..e7717c8515d06 100644 --- a/library/std/src/os/xous/services/log.rs +++ b/library/std/src/os/xous/services/log.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use crate::os::xous::ffi::Connection; @@ -7,8 +7,8 @@ use crate::os::xous::ffi::Connection; /// `group_or_null([1,2,3,4,5,6,7,8], 1)` on a 32-bit system will return a /// `usize` with 5678 packed into it. fn group_or_null(data: &[u8], offset: usize) -> usize { - let start = offset * core::mem::size_of::(); - let mut out_array = [0u8; core::mem::size_of::()]; + let start = offset * size_of::(); + let mut out_array = [0u8; size_of::()]; if start < data.len() { for (dest, src) in out_array.iter_mut().zip(&data[start..]) { *dest = *src; @@ -64,7 +64,7 @@ impl Into for LogLend { /// running. It is safe to call this multiple times, because the address is /// shared among all threads in a process. pub(crate) fn log_server() -> Connection { - static LOG_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); + static LOG_SERVER_CONNECTION: Atomic = AtomicU32::new(0); let cid = LOG_SERVER_CONNECTION.load(Ordering::Relaxed); if cid != 0 { diff --git a/library/std/src/os/xous/services/net.rs b/library/std/src/os/xous/services/net.rs index 83acc7961b377..c20bf1a7ad596 100644 --- a/library/std/src/os/xous/services/net.rs +++ b/library/std/src/os/xous/services/net.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use crate::os::xous::ffi::Connection; use crate::os::xous::services::connect; @@ -84,7 +84,7 @@ impl<'a> Into<[usize; 5]> for NetBlockingScalar { /// Returns a `Connection` to the Network server. This server provides all /// OS-level networking functions. pub(crate) fn net_server() -> Connection { - static NET_CONNECTION: AtomicU32 = AtomicU32::new(0); + static NET_CONNECTION: Atomic = AtomicU32::new(0); let cid = NET_CONNECTION.load(Ordering::Relaxed); if cid != 0 { return cid.into(); diff --git a/library/std/src/os/xous/services/systime.rs b/library/std/src/os/xous/services/systime.rs index de87694b4cdca..e54cffdc4c018 100644 --- a/library/std/src/os/xous/services/systime.rs +++ b/library/std/src/os/xous/services/systime.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use crate::os::xous::ffi::{Connection, connect}; @@ -17,7 +17,7 @@ impl Into<[usize; 5]> for SystimeScalar { /// Returns a `Connection` to the systime server. This server is used for reporting the /// realtime clock. pub(crate) fn systime_server() -> Connection { - static SYSTIME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); + static SYSTIME_SERVER_CONNECTION: Atomic = AtomicU32::new(0); let cid = SYSTIME_SERVER_CONNECTION.load(Ordering::Relaxed); if cid != 0 { return cid.into(); diff --git a/library/std/src/os/xous/services/ticktimer.rs b/library/std/src/os/xous/services/ticktimer.rs index 66ade6da65cd3..bf51ecde8e5bc 100644 --- a/library/std/src/os/xous/services/ticktimer.rs +++ b/library/std/src/os/xous/services/ticktimer.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use crate::os::xous::ffi::Connection; @@ -31,7 +31,7 @@ impl Into<[usize; 5]> for TicktimerScalar { /// Returns a `Connection` to the ticktimer server. This server is used for synchronization /// primitives such as sleep, Mutex, and Condvar. pub(crate) fn ticktimer_server() -> Connection { - static TICKTIMER_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); + static TICKTIMER_SERVER_CONNECTION: Atomic = AtomicU32::new(0); let cid = TICKTIMER_SERVER_CONNECTION.load(Ordering::Relaxed); if cid != 0 { return cid.into(); diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 22776ae2bc4a7..f3b26ac64dfa3 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -3,7 +3,7 @@ #![stable(feature = "std_panic", since = "1.9.0")] use crate::any::Any; -use crate::sync::atomic::{AtomicU8, Ordering}; +use crate::sync::atomic::{Atomic, AtomicU8, Ordering}; use crate::sync::{Condvar, Mutex, RwLock}; use crate::thread::Result; use crate::{collections, fmt, panicking}; @@ -469,7 +469,7 @@ impl BacktraceStyle { // that backtrace. // // Internally stores equivalent of an Option. -static SHOULD_CAPTURE: AtomicU8 = AtomicU8::new(0); +static SHOULD_CAPTURE: Atomic = AtomicU8::new(0); /// Configures whether the default panic hook will capture and display a /// backtrace. diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index b47b41d4bc5b7..4bfedf78366e7 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -21,7 +21,7 @@ use crate::any::Any; use crate::io::try_set_output_capture; use crate::mem::{self, ManuallyDrop}; use crate::panic::{BacktraceStyle, PanicHookInfo}; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; use crate::sync::{PoisonError, RwLock}; use crate::sys::backtrace; use crate::sys::stdio::panic_output; @@ -55,12 +55,14 @@ pub static EMPTY_PANIC: fn(&'static str) -> ! = // hook up these functions, but it is not this day! #[allow(improper_ctypes)] unsafe extern "C" { + #[rustc_std_internal_symbol] fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static); } unsafe extern "Rust" { /// `PanicPayload` lazily performs allocation only when needed (this avoids /// allocations when using the "abort" panic runtime). + #[rustc_std_internal_symbol] fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32; } @@ -287,7 +289,7 @@ fn default_hook(info: &PanicHookInfo<'_>) { }; }); - static FIRST_PANIC: AtomicBool = AtomicBool::new(true); + static FIRST_PANIC: Atomic = AtomicBool::new(true); match backtrace { // SAFETY: we took out a lock just a second ago. @@ -372,7 +374,7 @@ pub mod panic_count { #[unstable(feature = "update_panic_count", issue = "none")] pub mod panic_count { use crate::cell::Cell; - use crate::sync::atomic::{AtomicUsize, Ordering}; + use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; const ALWAYS_ABORT_FLAG: usize = 1 << (usize::BITS - 1); @@ -414,7 +416,7 @@ pub mod panic_count { // // Stealing a bit is fine because it just amounts to assuming that each // panicking thread consumes at least 2 bytes of address space. - static GLOBAL_PANIC_COUNT: AtomicUsize = AtomicUsize::new(0); + static GLOBAL_PANIC_COUNT: Atomic = AtomicUsize::new(0); // Increases the global and local panic count, and returns whether an // immediate abort is required. diff --git a/library/std/src/path.rs b/library/std/src/path.rs index f9f3b488f0d03..7959c63385816 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -294,11 +294,6 @@ where } } -// Detect scheme on Redox -pub(crate) fn has_redox_scheme(s: &[u8]) -> bool { - cfg!(target_os = "redox") && s.contains(&b':') -} - //////////////////////////////////////////////////////////////////////////////// // Cross-platform, iterator-independent parsing //////////////////////////////////////////////////////////////////////////////// @@ -358,6 +353,15 @@ fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) { } } +/// Checks whether the string is valid as a file extension, or panics otherwise. +fn validate_extension(extension: &OsStr) { + for &b in extension.as_encoded_bytes() { + if is_sep_byte(b) { + panic!("extension cannot contain path separators: {extension:?}"); + } + } +} + //////////////////////////////////////////////////////////////////////////////// // The core iterators //////////////////////////////////////////////////////////////////////////////// @@ -1512,13 +1516,7 @@ impl PathBuf { } fn _set_extension(&mut self, extension: &OsStr) -> bool { - for &b in extension.as_encoded_bytes() { - if b < 128 { - if is_separator(b as char) { - panic!("extension cannot contain path separators: {:?}", extension); - } - } - } + validate_extension(extension); let file_stem = match self.file_stem() { None => return false, @@ -1531,11 +1529,13 @@ impl PathBuf { self.inner.truncate(end_file_stem.wrapping_sub(start)); // add the new extension, if any - let new = extension; + let new = extension.as_encoded_bytes(); if !new.is_empty() { self.inner.reserve_exact(new.len() + 1); - self.inner.push(OsStr::new(".")); - self.inner.push(new); + self.inner.push("."); + // SAFETY: Since a UTF-8 string was just pushed, it is not possible + // for the buffer to end with a surrogate half. + unsafe { self.inner.extend_from_slice_unchecked(new) }; } true @@ -1546,6 +1546,11 @@ impl PathBuf { /// Returns `false` and does nothing if [`self.file_name`] is [`None`], /// returns `true` and updates the extension otherwise. /// + /// # Panics + /// + /// Panics if the passed extension contains a path separator (see + /// [`is_separator`]). + /// /// # Caveats /// /// The appended `extension` may contain dots and will be used in its entirety, @@ -1587,12 +1592,14 @@ impl PathBuf { } fn _add_extension(&mut self, extension: &OsStr) -> bool { + validate_extension(extension); + let file_name = match self.file_name() { None => return false, Some(f) => f.as_encoded_bytes(), }; - let new = extension; + let new = extension.as_encoded_bytes(); if !new.is_empty() { // truncate until right after the file name // this is necessary for trimming the trailing slash @@ -1602,8 +1609,10 @@ impl PathBuf { // append the new extension self.inner.reserve_exact(new.len() + 1); - self.inner.push(OsStr::new(".")); - self.inner.push(new); + self.inner.push("."); + // SAFETY: Since a UTF-8 string was just pushed, it is not possible + // for the buffer to end with a surrogate half. + unsafe { self.inner.extend_from_slice_unchecked(new) }; } true @@ -2764,7 +2773,8 @@ impl Path { }; let mut new_path = PathBuf::with_capacity(new_capacity); - new_path.inner.extend_from_slice(slice_to_copy); + // SAFETY: The path is empty, so cannot have surrogate halves. + unsafe { new_path.inner.extend_from_slice_unchecked(slice_to_copy) }; new_path.set_extension(extension); new_path } @@ -2834,8 +2844,7 @@ impl Path { 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()), + has_physical_root: has_physical_root(self.as_u8_slice(), prefix), front: State::Prefix, back: State::Body, } @@ -3154,7 +3163,7 @@ impl Path { /// allocating. #[stable(feature = "into_boxed_path", since = "1.20.0")] #[must_use = "`self` will be dropped if the result is not used"] - pub fn into_path_buf(self: Box) -> PathBuf { + 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) } @@ -3271,7 +3280,7 @@ impl Hash for Path { if !verbatim { component_start += match tail { [b'.'] => 1, - [b'.', sep @ _, ..] if is_sep_byte(*sep) => 1, + [b'.', sep, ..] if is_sep_byte(*sep) => 1, _ => 0, }; } diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 992a9207a7206..5f7097c26e228 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -160,3 +160,18 @@ pub mod rust_2024 { #[doc(no_inline)] pub use core::prelude::rust_2024::*; } + +/// The Future version of the prelude of The Rust Standard Library. +/// +/// See the [module-level documentation](self) for more. +#[doc(hidden)] +#[unstable(feature = "prelude_future", issue = "none")] +pub mod rust_future { + #[stable(feature = "rust1", since = "1.0.0")] + #[doc(no_inline)] + pub use super::v1::*; + + #[unstable(feature = "prelude_next", issue = "none")] + #[doc(no_inline)] + pub use core::prelude::rust_future::*; +} diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index 5b324b2e91671..c15d8c40085a5 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -103,6 +103,14 @@ pub use core::prelude::v1::type_ascribe; )] pub use core::prelude::v1::deref; +// Do not `doc(no_inline)` either. +#[unstable( + feature = "type_alias_impl_trait", + issue = "63063", + reason = "`type_alias_impl_trait` has open design concerns" +)] +pub use core::prelude::v1::define_opaque; + // The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated // rather than glob imported because we want docs to show these re-exports as // pointing to within `std`. diff --git a/library/std/src/process.rs b/library/std/src/process.rs index bdd4844b6511a..df6b9a6e563ce 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -154,7 +154,8 @@ target_os = "emscripten", target_os = "wasi", target_env = "sgx", - target_os = "xous" + target_os = "xous", + target_os = "trusty", )) ))] mod tests; @@ -167,8 +168,6 @@ use crate::num::NonZero; use crate::path::Path; use crate::sys::pipe::{AnonPipe, read2}; use crate::sys::process as imp; -#[stable(feature = "command_access", since = "1.57.0")] -pub use crate::sys_common::process::CommandEnvs; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{fmt, fs, str}; @@ -1072,7 +1071,7 @@ impl Command { /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn output(&mut self) -> io::Result { - let (status, stdout, stderr) = self.inner.output()?; + let (status, stdout, stderr) = imp::output(&mut self.inner)?; Ok(Output { status: ExitStatus(status), stdout, stderr }) } @@ -1173,7 +1172,7 @@ impl Command { /// ``` #[stable(feature = "command_access", since = "1.57.0")] pub fn get_envs(&self) -> CommandEnvs<'_> { - self.inner.get_envs() + CommandEnvs { iter: self.inner.get_envs() } } /// Returns the working directory for the child process. @@ -1263,6 +1262,48 @@ impl<'a> ExactSizeIterator for CommandArgs<'a> { } } +/// An iterator over the command environment variables. +/// +/// This struct is created by +/// [`Command::get_envs`][crate::process::Command::get_envs]. See its +/// documentation for more. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "command_access", since = "1.57.0")] +pub struct CommandEnvs<'a> { + iter: imp::CommandEnvs<'a>, +} + +#[stable(feature = "command_access", since = "1.57.0")] +impl<'a> Iterator for CommandEnvs<'a> { + type Item = (&'a OsStr, Option<&'a OsStr>); + + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[stable(feature = "command_access", since = "1.57.0")] +impl<'a> ExactSizeIterator for CommandEnvs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[stable(feature = "command_access", since = "1.57.0")] +impl<'a> fmt::Debug for CommandEnvs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.iter.fmt(f) + } +} + /// The output of a finished process. /// /// This is returned in a Result by either the [`output`] method of a @@ -1285,6 +1326,40 @@ pub struct Output { pub stderr: Vec, } +impl Output { + /// Returns an error if a nonzero exit status was received. + /// + /// If the [`Command`] exited successfully, + /// `self` is returned. + /// + /// This is equivalent to calling [`exit_ok`](ExitStatus::exit_ok) + /// on [`Output.status`](Output::status). + /// + /// Note that this will throw away the [`Output::stderr`] field in the error case. + /// If the child process outputs useful informantion to stderr, you can: + /// * Use `cmd.stderr(Stdio::inherit())` to forward the + /// stderr child process to the parent's stderr, + /// usually printing it to console where the user can see it. + /// This is usually correct for command-line applications. + /// * Capture `stderr` using a custom error type. + /// This is usually correct for libraries. + /// + /// # Examples + /// + /// ``` + /// #![feature(exit_status_error)] + /// # #[cfg(unix)] { + /// use std::process::Command; + /// assert!(Command::new("false").output().unwrap().exit_ok().is_err()); + /// # } + /// ``` + #[unstable(feature = "exit_status_error", issue = "84908")] + pub fn exit_ok(self) -> Result { + self.status.exit_ok()?; + Ok(self) + } +} + // 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")] @@ -1658,6 +1733,20 @@ impl From for Stdio { } } +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl From for Stdio { + fn from(pipe: io::PipeWriter) -> Self { + Stdio::from_inner(pipe.into_inner().into()) + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl From for Stdio { + fn from(pipe: io::PipeReader) -> Self { + Stdio::from_inner(pipe.into_inner().into()) + } +} + /// Describes the result of a process after it has terminated. /// /// This `struct` is used to represent the exit status or other termination of a child process. @@ -1821,7 +1910,7 @@ impl crate::sealed::Sealed for ExitStatusError {} /// # if cfg!(unix) { /// use std::process::{Command, ExitStatusError}; /// -/// fn run(cmd: &str) -> Result<(),ExitStatusError> { +/// fn run(cmd: &str) -> Result<(), ExitStatusError> { /// Command::new(cmd).status().unwrap().exit_ok()?; /// Ok(()) /// } @@ -2003,9 +2092,9 @@ impl ExitCode { /// /// Note that this has the same caveats as [`process::exit()`][exit], namely that this function /// terminates the process immediately, so 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 simply - /// return this ExitCode from the `main` function, as demonstrated in the [type - /// documentation](#examples). + /// thread's stack will be run. Also see those docs for some important notes on interop with C + /// code. If a clean shutdown is needed, it is recommended to simply return this ExitCode from + /// the `main` function, as demonstrated in the [type documentation](#examples). /// /// # Differences from `process::exit()` /// @@ -2311,6 +2400,33 @@ impl Child { /// /// process::exit(0x0100); /// ``` +/// +/// ### Safe interop with C code +/// +/// On Unix, this function is currently implemented using the `exit` C function [`exit`][C-exit]. As +/// of C23, the C standard does not permit multiple threads to call `exit` concurrently. Rust +/// mitigates this with a lock, but if C code calls `exit`, that can still cause undefined behavior. +/// Note that returning from `main` is equivalent to calling `exit`. +/// +/// Therefore, it is undefined behavior to have two concurrent threads perform the following +/// without synchronization: +/// - One thread calls Rust's `exit` function or returns from Rust's `main` function +/// - Another thread calls the C function `exit` or `quick_exit`, or returns from C's `main` function +/// +/// Note that if a binary contains multiple copies of the Rust runtime (e.g., when combining +/// multiple `cdylib` or `staticlib`), they each have their own separate lock, so from the +/// perspective of code running in one of the Rust runtimes, the "outside" Rust code is basically C +/// code, and concurrent `exit` again causes undefined behavior. +/// +/// Individual C implementations might provide more guarantees than the standard and permit concurrent +/// calls to `exit`; consult the documentation of your C implementation for details. +/// +/// For some of the on-going discussion to make `exit` thread-safe in C, see: +/// - [Rust issue #126600](https://github.com/rust-lang/rust/issues/126600) +/// - [Austin Group Bugzilla (for POSIX)](https://austingroupbugs.net/view.php?id=1845) +/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=31997) +/// +/// [C-exit]: https://en.cppreference.com/w/c/program/exit #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "process_exit")] pub fn exit(code: i32) -> ! { diff --git a/library/std/src/random.rs b/library/std/src/random.rs index 45f51dd37b041..e7d4ab81df0ac 100644 --- a/library/std/src/random.rs +++ b/library/std/src/random.rs @@ -37,7 +37,7 @@ use crate::sys::random as sys; /// Solaris | [`arc4random_buf`](https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html) /// Vita | `arc4random_buf` /// Hermit | `read_entropy` -/// Horizon | `getrandom` shim +/// Horizon, Cygwin | `getrandom` /// AIX, Hurd, L4Re, QNX | `/dev/urandom` /// Redox | `/scheme/rand` /// RTEMS | [`arc4random_buf`](https://docs.rtems.org/branches/master/bsp-howto/getentropy.html) diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 3a22a16cb165e..b3f3b301e3db6 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -26,6 +26,13 @@ use crate::sync::Once; use crate::thread::{self, main_thread}; use crate::{mem, panic, sys}; +// This function is needed by the panic runtime. +#[cfg(not(test))] +#[rustc_std_internal_symbol] +fn __rust_abort() { + crate::process::abort(); +} + // Prints to the "panic output", depending on the platform this may be: // - the standard error output // - some dedicated platform specific output @@ -46,8 +53,8 @@ macro_rules! rtprintpanic { macro_rules! rtabort { ($($t:tt)*) => { { - rtprintpanic!("fatal runtime error: {}\n", format_args!($($t)*)); - crate::sys::abort_internal(); + rtprintpanic!("fatal runtime error: {}, aborting\n", format_args!($($t)*)); + crate::process::abort(); } } } diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 78cf8841efefb..82e5fe05db5e3 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -1,7 +1,7 @@ use super::poison::once::ExclusiveState; use crate::cell::UnsafeCell; use crate::mem::ManuallyDrop; -use crate::ops::Deref; +use crate::ops::{Deref, DerefMut}; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sync::Once; use crate::{fmt, ptr}; @@ -313,6 +313,14 @@ impl T> Deref for LazyLock { } } +#[stable(feature = "lazy_deref_mut", since = "CURRENT_RUSTC_VERSION")] +impl T> DerefMut for LazyLock { + #[inline] + fn deref_mut(&mut self) -> &mut T { + LazyLock::force_mut(self) + } +} + #[stable(feature = "lazy_cell", since = "1.80.0")] impl Default for LazyLock { /// Creates a new lazy value using `Default` as the initializing function. diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 5b50a3c6ccf90..e67b4f6f22f5a 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -176,6 +176,8 @@ pub use core::sync::Exclusive; #[stable(feature = "rust1", since = "1.0.0")] pub use core::sync::atomic; +#[unstable(feature = "unique_rc_arc", issue = "112566")] +pub use alloc_crate::sync::UniqueArc; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::sync::{Arc, Weak}; diff --git a/library/std/src/sync/mpmc/array.rs b/library/std/src/sync/mpmc/array.rs index a467237fef152..880d8b5f57cf4 100644 --- a/library/std/src/sync/mpmc/array.rs +++ b/library/std/src/sync/mpmc/array.rs @@ -16,13 +16,13 @@ use super::waker::SyncWaker; use crate::cell::UnsafeCell; use crate::mem::MaybeUninit; use crate::ptr; -use crate::sync::atomic::{self, AtomicUsize, Ordering}; +use crate::sync::atomic::{self, Atomic, AtomicUsize, Ordering}; use crate::time::Instant; /// A slot in a channel. struct Slot { /// The current stamp. - stamp: AtomicUsize, + stamp: Atomic, /// The message in this slot. Either read out in `read` or dropped through /// `discard_all_messages`. @@ -55,7 +55,7 @@ pub(crate) struct Channel { /// represent the lap. The mark bit in the head is always zero. /// /// Messages are popped from the head of the channel. - head: CachePadded, + head: CachePadded>, /// The tail of the channel. /// @@ -64,7 +64,7 @@ pub(crate) struct Channel { /// represent the lap. The mark bit indicates that the channel is disconnected. /// /// Messages are pushed into the tail of the channel. - tail: CachePadded, + tail: CachePadded>, /// The buffer holding slots. buffer: Box<[Slot]>, diff --git a/library/std/src/sync/mpmc/context.rs b/library/std/src/sync/mpmc/context.rs index 51aa7e82e7890..6b2f4cb6ffd29 100644 --- a/library/std/src/sync/mpmc/context.rs +++ b/library/std/src/sync/mpmc/context.rs @@ -5,7 +5,7 @@ use super::waker::current_thread_id; use crate::cell::Cell; use crate::ptr; use crate::sync::Arc; -use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicPtr, AtomicUsize, Ordering}; use crate::thread::{self, Thread}; use crate::time::Instant; @@ -19,10 +19,10 @@ pub struct Context { #[derive(Debug)] struct Inner { /// Selected operation. - select: AtomicUsize, + select: Atomic, /// A slot into which another thread may store a pointer to its `Packet`. - packet: AtomicPtr<()>, + packet: Atomic<*mut ()>, /// Thread handle. thread: Thread, diff --git a/library/std/src/sync/mpmc/counter.rs b/library/std/src/sync/mpmc/counter.rs index d1bfe612f536f..efa6af1148334 100644 --- a/library/std/src/sync/mpmc/counter.rs +++ b/library/std/src/sync/mpmc/counter.rs @@ -1,16 +1,16 @@ -use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering}; use crate::{ops, process}; /// Reference counter internals. struct Counter { /// The number of senders associated with the channel. - senders: AtomicUsize, + senders: Atomic, /// The number of receivers associated with the channel. - receivers: AtomicUsize, + receivers: Atomic, /// Set to `true` if the last sender or the last receiver reference deallocates the channel. - destroy: AtomicBool, + destroy: Atomic, /// The internal channel. chan: C, diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs index d88914f529142..3fcfb85cf2aab 100644 --- a/library/std/src/sync/mpmc/list.rs +++ b/library/std/src/sync/mpmc/list.rs @@ -9,7 +9,7 @@ use crate::cell::UnsafeCell; use crate::marker::PhantomData; use crate::mem::MaybeUninit; use crate::ptr; -use crate::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; +use crate::sync::atomic::{self, Atomic, AtomicPtr, AtomicUsize, Ordering}; use crate::time::Instant; // Bits indicating the state of a slot: @@ -37,7 +37,7 @@ struct Slot { msg: UnsafeCell>, /// The state of the slot. - state: AtomicUsize, + state: Atomic, } impl Slot { @@ -55,7 +55,7 @@ impl Slot { /// Each block in the list can hold up to `BLOCK_CAP` messages. struct Block { /// The next block in the linked list. - next: AtomicPtr>, + next: Atomic<*mut Block>, /// Slots for messages. slots: [Slot; BLOCK_CAP], @@ -65,11 +65,11 @@ impl Block { /// Creates an empty block. fn new() -> Box> { // SAFETY: This is safe because: - // [1] `Block::next` (AtomicPtr) may be safely zero initialized. + // [1] `Block::next` (Atomic<*mut _>) may be safely zero initialized. // [2] `Block::slots` (Array) may be safely zero initialized because of [3, 4]. // [3] `Slot::msg` (UnsafeCell) may be safely zero initialized because it // holds a MaybeUninit. - // [4] `Slot::state` (AtomicUsize) may be safely zero initialized. + // [4] `Slot::state` (Atomic) may be safely zero initialized. unsafe { Box::new_zeroed().assume_init() } } @@ -110,10 +110,10 @@ impl Block { #[derive(Debug)] struct Position { /// The index in the channel. - index: AtomicUsize, + index: Atomic, /// The block in the linked list. - block: AtomicPtr>, + block: Atomic<*mut Block>, } /// The token type for the list flavor. @@ -213,6 +213,11 @@ impl Channel { .compare_exchange(block, new, Ordering::Release, Ordering::Relaxed) .is_ok() { + // This yield point leaves the channel in a half-initialized state where the + // tail.block pointer is set but the head.block is not. This is used to + // facilitate the test in src/tools/miri/tests/pass/issues/issue-139553.rs + #[cfg(miri)] + crate::thread::yield_now(); self.head.block.store(new, Ordering::Release); block = new; } else { @@ -564,9 +569,15 @@ impl Channel { // In that case, just wait until it gets initialized. while block.is_null() { backoff.spin_heavy(); - block = self.head.block.load(Ordering::Acquire); + block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel); } } + // After this point `head.block` is not modified again and it will be deallocated if it's + // non-null. The `Drop` code of the channel, which runs after this function, also attempts + // to deallocate `head.block` if it's non-null. Therefore this function must maintain the + // invariant that if a deallocation of head.block is attemped then it must also be set to + // NULL. Failing to do so will lead to the Drop code attempting a double free. For this + // reason both reads above do an atomic swap instead of a simple atomic load. unsafe { // Drop all messages between head and tail and deallocate the heap-allocated blocks. diff --git a/library/std/src/sync/mpmc/waker.rs b/library/std/src/sync/mpmc/waker.rs index f5e764e69bd6e..4216fb7ac5902 100644 --- a/library/std/src/sync/mpmc/waker.rs +++ b/library/std/src/sync/mpmc/waker.rs @@ -4,7 +4,7 @@ use super::context::Context; use super::select::{Operation, Selected}; use crate::ptr; use crate::sync::Mutex; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; /// Represents a thread blocked on a specific channel operation. pub(crate) struct Entry { @@ -137,7 +137,7 @@ pub(crate) struct SyncWaker { inner: Mutex, /// `true` if the waker is empty. - is_empty: AtomicBool, + is_empty: Atomic, } impl SyncWaker { diff --git a/library/std/src/sync/mpmc/zero.rs b/library/std/src/sync/mpmc/zero.rs index 577997c07a636..f1ecf80fcb9f6 100644 --- a/library/std/src/sync/mpmc/zero.rs +++ b/library/std/src/sync/mpmc/zero.rs @@ -10,7 +10,7 @@ use super::waker::Waker; use crate::cell::UnsafeCell; use crate::marker::PhantomData; use crate::sync::Mutex; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; use crate::time::Instant; use crate::{fmt, ptr}; @@ -35,7 +35,7 @@ struct Packet { on_stack: bool, /// Equals `true` once the packet is ready for reading or writing. - ready: AtomicBool, + ready: Atomic, /// The message. msg: UnsafeCell>, diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index ffb90b1469584..324b5451873bf 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -279,7 +279,7 @@ impl OnceLock { /// /// Many threads may call `get_or_init` concurrently with different /// initializing functions, but it is guaranteed that only one function - /// will be executed. + /// will be executed if the function doesn't panic. /// /// # Panics /// diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 1b8809734b8a8..cc1d0b30152a1 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -76,7 +76,7 @@ pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::error::Error; use crate::fmt; #[cfg(panic = "unwind")] -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; #[cfg(panic = "unwind")] use crate::thread; @@ -88,7 +88,7 @@ mod rwlock; pub(crate) struct Flag { #[cfg(panic = "unwind")] - failed: AtomicBool, + failed: Atomic, } // Note that the Ordering uses to access the `failed` field of `Flag` below is diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 9362c764173a8..1c29c619edc3a 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -253,11 +253,11 @@ unsafe impl Sync for MutexGuard<'_, T> {} /// The data protected by the mutex can be accessed through this guard via its /// [`Deref`] and [`DerefMut`] implementations. /// -/// This structure is created by the [`map`] and [`try_map`] methods on +/// This structure is created by the [`map`] and [`filter_map`] methods on /// [`MutexGuard`]. /// /// [`map`]: MutexGuard::map -/// [`try_map`]: MutexGuard::try_map +/// [`filter_map`]: MutexGuard::filter_map /// [`Condvar`]: crate::sync::Condvar #[must_use = "if unused the Mutex will immediately unlock"] #[must_not_suspend = "holding a MappedMutexGuard across suspend \ @@ -582,7 +582,9 @@ impl Mutex { /// 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. + /// take place -- the mutable borrow statically guarantees no new locks can be acquired + /// while this reference exists. Note that this method does not clear any previous abandoned locks + /// (e.g., via [`forget()`] on a [`MutexGuard`]). /// /// # Errors /// @@ -599,6 +601,8 @@ impl Mutex { /// *mutex.get_mut().unwrap() = 10; /// assert_eq!(*mutex.lock().unwrap(), 10); /// ``` + /// + /// [`forget()`]: mem::forget #[stable(feature = "mutex_get_mut", since = "1.6.0")] pub fn get_mut(&mut self) -> LockResult<&mut T> { let data = self.data.get_mut(); @@ -714,7 +718,7 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> { U: ?Sized, { // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); @@ -735,17 +739,16 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> { /// The `Mutex` is already locked, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `MutexGuard::try_map(...)`. A method would interfere with methods of the + /// `MutexGuard::filter_map(...)`. A method would interfere with methods of the /// same name on the contents of the `MutexGuard` used through `Deref`. - #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map(orig: Self, f: F) -> Result, Self> + pub fn filter_map(orig: Self, f: F) -> Result, Self> where F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. match f(unsafe { &mut *orig.lock.data.get() }) { @@ -822,7 +825,7 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> { U: ?Sized, { // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_mut() })); @@ -843,17 +846,16 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> { /// The `Mutex` is already locked, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `MappedMutexGuard::try_map(...)`. A method would interfere with methods of the + /// `MappedMutexGuard::filter_map(...)`. A method would interfere with methods of the /// same name on the contents of the `MutexGuard` used through `Deref`. - #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map(mut orig: Self, f: F) -> Result, Self> + pub fn filter_map(mut orig: Self, f: F) -> Result, Self> where F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. match f(unsafe { orig.data.as_mut() }) { diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index f9d9321f5f2d8..6976c0a64e23f 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -147,11 +147,11 @@ unsafe impl Sync for RwLockWriteGuard<'_, T> {} /// RAII structure used to release the shared read access of a lock when /// dropped, which can point to a subfield of the protected data. /// -/// This structure is created by the [`map`] and [`try_map`] methods +/// This structure is created by the [`map`] and [`filter_map`] methods /// on [`RwLockReadGuard`]. /// /// [`map`]: RwLockReadGuard::map -/// [`try_map`]: RwLockReadGuard::try_map +/// [`filter_map`]: RwLockReadGuard::filter_map #[must_use = "if unused the RwLock will immediately unlock"] #[must_not_suspend = "holding a MappedRwLockReadGuard across suspend \ points can cause deadlocks, delays, \ @@ -176,11 +176,11 @@ unsafe impl Sync for MappedRwLockReadGuard<'_, T> {} /// RAII structure used to release the exclusive write access of a lock when /// dropped, which can point to a subfield of the protected data. /// -/// This structure is created by the [`map`] and [`try_map`] methods +/// This structure is created by the [`map`] and [`filter_map`] methods /// on [`RwLockWriteGuard`]. /// /// [`map`]: RwLockWriteGuard::map -/// [`try_map`]: RwLockWriteGuard::try_map +/// [`filter_map`]: RwLockWriteGuard::filter_map #[must_use = "if unused the RwLock will immediately unlock"] #[must_not_suspend = "holding a MappedRwLockWriteGuard across suspend \ points can cause deadlocks, delays, \ @@ -608,7 +608,9 @@ impl RwLock { /// 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. + /// take place -- the mutable borrow statically guarantees no new locks can be acquired + /// while this reference exists. Note that this method does not clear any previously abandoned locks + /// (e.g., via [`forget()`] on a [`RwLockReadGuard`] or [`RwLockWriteGuard`]). /// /// # Errors /// @@ -786,7 +788,7 @@ impl Deref for MappedRwLockReadGuard<'_, T> { fn deref(&self) -> &T { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. unsafe { self.data.as_ref() } } } @@ -797,7 +799,7 @@ impl Deref for MappedRwLockWriteGuard<'_, T> { fn deref(&self) -> &T { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. unsafe { self.data.as_ref() } } } @@ -806,7 +808,7 @@ impl Deref for MappedRwLockWriteGuard<'_, T> { impl DerefMut for MappedRwLockWriteGuard<'_, T> { fn deref_mut(&mut self) -> &mut T { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. unsafe { self.data.as_mut() } } } @@ -836,7 +838,7 @@ impl Drop for RwLockWriteGuard<'_, T> { impl Drop for MappedRwLockReadGuard<'_, T> { fn drop(&mut self) { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. unsafe { self.inner_lock.read_unlock(); } @@ -848,7 +850,7 @@ impl Drop for MappedRwLockWriteGuard<'_, T> { fn drop(&mut self) { self.poison_flag.done(&self.poison); // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. unsafe { self.inner_lock.write_unlock(); } @@ -876,7 +878,7 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> { U: ?Sized, { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_ref() })); @@ -891,22 +893,21 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> { /// The `RwLock` is already locked for reading, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `RwLockReadGuard::try_map(...)`. A method would interfere with methods + /// `RwLockReadGuard::filter_map(...)`. A method would interfere with methods /// of the same name on the contents of the `RwLockReadGuard` used through /// `Deref`. /// /// # Panics /// /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be poisoned. - #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map(orig: Self, f: F) -> Result, Self> + pub fn filter_map(orig: Self, f: F) -> Result, Self> where F: FnOnce(&T) -> Option<&U>, U: ?Sized, { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. match f(unsafe { orig.data.as_ref() }) { @@ -941,7 +942,7 @@ impl<'a, T: ?Sized> MappedRwLockReadGuard<'a, T> { U: ?Sized, { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_ref() })); @@ -956,22 +957,21 @@ impl<'a, T: ?Sized> MappedRwLockReadGuard<'a, T> { /// The `RwLock` is already locked for reading, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `MappedRwLockReadGuard::try_map(...)`. A method would interfere with + /// `MappedRwLockReadGuard::filter_map(...)`. A method would interfere with /// methods of the same name on the contents of the `MappedRwLockReadGuard` /// used through `Deref`. /// /// # Panics /// /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be poisoned. - #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map(orig: Self, f: F) -> Result, Self> + pub fn filter_map(orig: Self, f: F) -> Result, Self> where F: FnOnce(&T) -> Option<&U>, U: ?Sized, { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. match f(unsafe { orig.data.as_ref() }) { @@ -1006,7 +1006,7 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> { U: ?Sized, { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); @@ -1027,22 +1027,21 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> { /// The `RwLock` is already locked for writing, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `RwLockWriteGuard::try_map(...)`. A method would interfere with methods + /// `RwLockWriteGuard::filter_map(...)`. A method would interfere with methods /// of the same name on the contents of the `RwLockWriteGuard` used through /// `Deref`. /// /// # Panics /// /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. - #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map(orig: Self, f: F) -> Result, Self> + pub fn filter_map(orig: Self, f: F) -> Result, Self> where F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. match f(unsafe { &mut *orig.lock.data.get() }) { @@ -1145,7 +1144,7 @@ impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> { U: ?Sized, { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_mut() })); @@ -1166,22 +1165,21 @@ impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> { /// The `RwLock` is already locked for writing, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `MappedRwLockWriteGuard::try_map(...)`. A method would interfere with + /// `MappedRwLockWriteGuard::filter_map(...)`. A method would interfere with /// methods of the same name on the contents of the `MappedRwLockWriteGuard` /// used through `Deref`. /// /// # Panics /// /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. - #[doc(alias = "filter_map")] #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn try_map(mut orig: Self, f: F) -> Result, Self> + pub fn filter_map(mut orig: Self, f: F) -> Result, Self> where F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `try_map`. + // was created, and have been upheld throughout `map` and/or `filter_map`. // The signature of the closure guarantees that it will not "leak" the lifetime of the reference // passed to it. If the closure panics, the guard will be dropped. match f(unsafe { orig.data.as_mut() }) { diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index e009eb410efc0..96a4cf12659cc 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -89,9 +89,9 @@ pub struct ReentrantLock { cfg_if!( if #[cfg(target_has_atomic = "64")] { - use crate::sync::atomic::{AtomicU64, Ordering::Relaxed}; + use crate::sync::atomic::{Atomic, AtomicU64, Ordering::Relaxed}; - struct Tid(AtomicU64); + struct Tid(Atomic); impl Tid { const fn new() -> Self { @@ -120,6 +120,7 @@ cfg_if!( } use crate::sync::atomic::{ + Atomic, AtomicUsize, Ordering, }; @@ -135,9 +136,9 @@ cfg_if!( // we only ever read from the tid if `tls_addr` matches the current // TLS address. In that case, either the tid has been set by // the current thread, or by a thread that has terminated before - // the current thread was created. In either case, no further + // the current thread's `tls_addr` was allocated. In either case, no further // synchronization is needed (as per ) - tls_addr: AtomicUsize, + tls_addr: Atomic, tid: UnsafeCell, } @@ -153,8 +154,12 @@ cfg_if!( // NOTE: This assumes that `owner` is the ID of the current // thread, and may spuriously return `false` if that's not the case. fn contains(&self, owner: ThreadId) -> bool { + // We must call `tls_addr()` *before* doing the load to ensure that if we reuse an + // earlier thread's address, the `tls_addr.load()` below happens-after everything + // that thread did. + let tls_addr = tls_addr(); // SAFETY: See the comments in the struct definition. - self.tls_addr.load(Ordering::Relaxed) == tls_addr() + self.tls_addr.load(Ordering::Relaxed) == tls_addr && unsafe { *self.tid.get() } == owner.as_u64().get() } diff --git a/library/std/src/sys/alloc/mod.rs b/library/std/src/sys/alloc/mod.rs index 2c0b533a5703f..8489e17c971d9 100644 --- a/library/std/src/sys/alloc/mod.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -72,6 +72,7 @@ cfg_if::cfg_if! { target_family = "unix", target_os = "wasi", target_os = "teeos", + target_os = "trusty", ))] { mod unix; } else if #[cfg(target_os = "windows")] { diff --git a/library/std/src/sys/alloc/sgx.rs b/library/std/src/sys/alloc/sgx.rs index f5c27688fbc8f..afdef7a5cb647 100644 --- a/library/std/src/sys/alloc/sgx.rs +++ b/library/std/src/sys/alloc/sgx.rs @@ -1,6 +1,6 @@ use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ptr; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; use crate::sys::pal::abi::mem as sgx_mem; use crate::sys::pal::waitqueue::SpinMutex; @@ -10,8 +10,10 @@ use crate::sys::pal::waitqueue::SpinMutex; // The current allocator here is the `dlmalloc` crate which we've got included // in the rust-lang/rust repository as a submodule. The crate is a port of // dlmalloc.c from C to Rust. +// +// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx5alloc8DLMALLOCE")] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys5alloc3sgx8DLMALLOCE")] static DLMALLOC: SpinMutex> = SpinMutex::new(dlmalloc::Dlmalloc::new_with_allocator(Sgx {})); @@ -20,7 +22,7 @@ struct Sgx; unsafe impl dlmalloc::Allocator for Sgx { /// Allocs system resources fn alloc(&self, _size: usize) -> (*mut u8, usize, u32) { - static INIT: AtomicBool = AtomicBool::new(false); + static INIT: Atomic = AtomicBool::new(false); // No ordering requirement since this function is protected by the global lock. if !INIT.swap(true, Ordering::Relaxed) { diff --git a/library/std/src/sys/alloc/unix.rs b/library/std/src/sys/alloc/unix.rs index 1af9d76629014..a7ac4117ec902 100644 --- a/library/std/src/sys/alloc/unix.rs +++ b/library/std/src/sys/alloc/unix.rs @@ -81,7 +81,7 @@ cfg_if::cfg_if! { // while others require the alignment to be at least the pointer size (Illumos, macOS). // posix_memalign only has one, clear requirement: that the alignment be a multiple of // `sizeof(void*)`. Since these are all powers of 2, we can just use max. - let align = layout.align().max(crate::mem::size_of::()); + let align = layout.align().max(size_of::()); let ret = unsafe { libc::posix_memalign(&mut out, align, layout.size()) }; if ret != 0 { ptr::null_mut() } else { out as *mut u8 } } diff --git a/library/std/src/sys/alloc/wasm.rs b/library/std/src/sys/alloc/wasm.rs index 53fbc9529e590..c8fab992a88a7 100644 --- a/library/std/src/sys/alloc/wasm.rs +++ b/library/std/src/sys/alloc/wasm.rs @@ -60,10 +60,10 @@ unsafe impl GlobalAlloc for System { #[cfg(target_feature = "atomics")] mod lock { - use crate::sync::atomic::AtomicI32; use crate::sync::atomic::Ordering::{Acquire, Release}; + use crate::sync::atomic::{Atomic, AtomicI32}; - static LOCKED: AtomicI32 = AtomicI32::new(0); + static LOCKED: Atomic = AtomicI32::new(0); pub struct DropLock; diff --git a/library/std/src/sys/alloc/windows/tests.rs b/library/std/src/sys/alloc/windows/tests.rs index 674a3e1d92d17..1d5614528b12a 100644 --- a/library/std/src/sys/alloc/windows/tests.rs +++ b/library/std/src/sys/alloc/windows/tests.rs @@ -1,9 +1,8 @@ use super::{Header, MIN_ALIGN}; -use crate::mem; #[test] fn alloc_header() { // Header must fit in the padding before an aligned pointer - assert!(mem::size_of::

() <= MIN_ALIGN); - assert!(mem::align_of::
() <= MIN_ALIGN); + assert!(size_of::
() <= MIN_ALIGN); + assert!(align_of::
() <= MIN_ALIGN); } diff --git a/library/std/src/sys/alloc/xous.rs b/library/std/src/sys/alloc/xous.rs index ccaa972c22de3..c7f973b802791 100644 --- a/library/std/src/sys/alloc/xous.rs +++ b/library/std/src/sys/alloc/xous.rs @@ -49,10 +49,10 @@ unsafe impl GlobalAlloc for System { } mod lock { - use crate::sync::atomic::AtomicI32; use crate::sync::atomic::Ordering::{Acquire, Release}; + use crate::sync::atomic::{Atomic, AtomicI32}; - static LOCKED: AtomicI32 = AtomicI32::new(0); + static LOCKED: Atomic = AtomicI32::new(0); pub struct DropLock; diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs index 9e398765634b7..dfe10f7fafe49 100644 --- a/library/std/src/sys/anonymous_pipe/unix.rs +++ b/library/std/src/sys/anonymous_pipe/unix.rs @@ -1,9 +1,7 @@ -use crate::io::{self, PipeReader, PipeWriter}; -use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::process::Stdio; +use crate::io; use crate::sys::fd::FileDesc; use crate::sys::pipe::anon_pipe; -use crate::sys_common::{FromInner, IntoInner}; +use crate::sys_common::IntoInner; pub type AnonPipe = FileDesc; @@ -11,91 +9,3 @@ pub type AnonPipe = FileDesc; pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) } - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl AsFd for PipeReader { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl AsRawFd for PipeReader { - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for OwnedFd { - fn from(pipe: PipeReader) -> Self { - FileDesc::into_inner(pipe.0) - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl FromRawFd for PipeReader { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self(FileDesc::from_raw_fd(raw_fd)) } - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl IntoRawFd for PipeReader { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for Stdio { - fn from(pipe: PipeReader) -> Self { - Self::from(OwnedFd::from(pipe)) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl AsFd for PipeWriter { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl AsRawFd for PipeWriter { - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for OwnedFd { - fn from(pipe: PipeWriter) -> Self { - FileDesc::into_inner(pipe.0) - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl FromRawFd for PipeWriter { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self(FileDesc::from_raw_fd(raw_fd)) } - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl IntoRawFd for PipeWriter { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for Stdio { - fn from(pipe: PipeWriter) -> Self { - Self::from(OwnedFd::from(pipe)) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for PipeReader { - fn from(owned_fd: OwnedFd) -> Self { - Self(FileDesc::from_inner(owned_fd)) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for PipeWriter { - fn from(owned_fd: OwnedFd) -> Self { - Self(FileDesc::from_inner(owned_fd)) - } -} diff --git a/library/std/src/sys/anonymous_pipe/unsupported.rs b/library/std/src/sys/anonymous_pipe/unsupported.rs index 4e79ac9c21aad..a0805ba9540e0 100644 --- a/library/std/src/sys/anonymous_pipe/unsupported.rs +++ b/library/std/src/sys/anonymous_pipe/unsupported.rs @@ -1,22 +1,7 @@ -use crate::io::{self, PipeReader, PipeWriter}; -use crate::process::Stdio; +use crate::io; pub use crate::sys::pipe::AnonPipe; #[inline] pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { Err(io::Error::UNSUPPORTED_PLATFORM) } - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for Stdio { - fn from(pipe: PipeReader) -> Self { - pipe.0.diverge() - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for Stdio { - fn from(pipe: PipeWriter) -> Self { - pipe.0.diverge() - } -} diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/anonymous_pipe/windows.rs index eb7fa8ec1c9a1..bdda7ffc5d251 100644 --- a/library/std/src/sys/anonymous_pipe/windows.rs +++ b/library/std/src/sys/anonymous_pipe/windows.rs @@ -1,12 +1,7 @@ -use crate::io::{self, PipeReader, PipeWriter}; -use crate::os::windows::io::{ - AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, -}; -use crate::process::Stdio; -use crate::ptr; +use crate::os::windows::io::FromRawHandle; use crate::sys::c; use crate::sys::handle::Handle; -use crate::sys_common::{FromInner, IntoInner}; +use crate::{io, ptr}; pub type AnonPipe = Handle; @@ -22,95 +17,3 @@ pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { unsafe { Ok((Handle::from_raw_handle(read_pipe), Handle::from_raw_handle(write_pipe))) } } } - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl AsHandle for PipeReader { - fn as_handle(&self) -> BorrowedHandle<'_> { - self.0.as_handle() - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl AsRawHandle for PipeReader { - fn as_raw_handle(&self) -> RawHandle { - self.0.as_raw_handle() - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl FromRawHandle for PipeReader { - unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { - unsafe { Self(Handle::from_raw_handle(raw_handle)) } - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl IntoRawHandle for PipeReader { - fn into_raw_handle(self) -> RawHandle { - self.0.into_raw_handle() - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for OwnedHandle { - fn from(pipe: PipeReader) -> Self { - Handle::into_inner(pipe.0) - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for Stdio { - fn from(pipe: PipeReader) -> Self { - Self::from(OwnedHandle::from(pipe)) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl AsHandle for PipeWriter { - fn as_handle(&self) -> BorrowedHandle<'_> { - self.0.as_handle() - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl AsRawHandle for PipeWriter { - fn as_raw_handle(&self) -> RawHandle { - self.0.as_raw_handle() - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl FromRawHandle for PipeWriter { - unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { - unsafe { Self(Handle::from_raw_handle(raw_handle)) } - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl IntoRawHandle for PipeWriter { - fn into_raw_handle(self) -> RawHandle { - self.0.into_raw_handle() - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for OwnedHandle { - fn from(pipe: PipeWriter) -> Self { - Handle::into_inner(pipe.0) - } -} -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for Stdio { - fn from(pipe: PipeWriter) -> Self { - Self::from(OwnedHandle::from(pipe)) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for PipeReader { - fn from(owned_handle: OwnedHandle) -> Self { - Self(Handle::from_inner(owned_handle)) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl From for PipeWriter { - fn from(owned_handle: OwnedHandle) -> Self { - Self(Handle::from_inner(owned_handle)) - } -} diff --git a/library/std/src/sys/args/common.rs b/library/std/src/sys/args/common.rs new file mode 100644 index 0000000000000..e787105a05a73 --- /dev/null +++ b/library/std/src/sys/args/common.rs @@ -0,0 +1,101 @@ +use crate::ffi::OsString; +use crate::num::NonZero; +use crate::ops::Try; +use crate::{array, fmt, vec}; + +pub struct Args { + iter: vec::IntoIter, +} + +impl !Send for Args {} +impl !Sync for Args {} + +impl Args { + #[inline] + pub(super) fn new(args: Vec) -> Self { + Args { iter: args.into_iter() } + } +} + +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.iter.as_slice().fmt(f) + } +} + +impl Iterator for Args { + type Item = OsString; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next() + } + + #[inline] + fn next_chunk( + &mut self, + ) -> Result<[OsString; N], array::IntoIter> { + self.iter.next_chunk() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn count(self) -> usize { + self.iter.len() + } + + #[inline] + fn last(self) -> Option { + self.iter.last() + } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + self.iter.advance_by(n) + } + + #[inline] + fn try_fold(&mut self, init: B, f: F) -> R + where + F: FnMut(B, Self::Item) -> R, + R: Try, + { + self.iter.try_fold(init, f) + } + + #[inline] + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.fold(init, f) + } +} + +impl DoubleEndedIterator for Args { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back() + } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + self.iter.advance_back_by(n) + } +} + +impl ExactSizeIterator for Args { + #[inline] + fn len(&self) -> usize { + self.iter.len() + } + + #[inline] + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs new file mode 100644 index 0000000000000..0011f55dc14ee --- /dev/null +++ b/library/std/src/sys/args/mod.rs @@ -0,0 +1,44 @@ +//! Platform-dependent command line arguments abstraction. + +#![forbid(unsafe_op_in_unsafe_fn)] + +#[cfg(any( + all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))), + target_family = "windows", + target_os = "hermit", + target_os = "uefi", + target_os = "wasi", + target_os = "xous", +))] +mod common; + +cfg_if::cfg_if! { + if #[cfg(any( + all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))), + target_os = "hermit", + ))] { + mod unix; + pub use unix::*; + } else if #[cfg(target_family = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + pub use sgx::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::*; + } else if #[cfg(target_os = "xous")] { + mod xous; + pub use xous::*; + } else if #[cfg(target_os = "zkvm")] { + mod zkvm; + pub use zkvm::*; + } else { + mod unsupported; + pub use unsupported::*; + } +} diff --git a/library/std/src/sys/args/sgx.rs b/library/std/src/sys/args/sgx.rs new file mode 100644 index 0000000000000..f800500c22a8a --- /dev/null +++ b/library/std/src/sys/args/sgx.rs @@ -0,0 +1,110 @@ +#![allow(fuzzy_provenance_casts)] // FIXME: this module systematically confuses pointers and integers + +use crate::ffi::OsString; +use crate::num::NonZero; +use crate::ops::Try; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; +use crate::sys::os_str::Buf; +use crate::sys::pal::abi::usercalls::alloc; +use crate::sys::pal::abi::usercalls::raw::ByteBuffer; +use crate::sys_common::FromInner; +use crate::{fmt, slice}; + +// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests +#[cfg_attr(test, linkage = "available_externally")] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx4args4ARGSE")] +static ARGS: Atomic = AtomicUsize::new(0); +type ArgsStore = Vec; + +#[cfg_attr(test, allow(dead_code))] +pub unsafe fn init(argc: isize, argv: *const *const u8) { + if argc != 0 { + let args = unsafe { alloc::User::<[ByteBuffer]>::from_raw_parts(argv as _, argc as _) }; + let args = args + .iter() + .map(|a| OsString::from_inner(Buf { inner: a.copy_user_buffer() })) + .collect::(); + ARGS.store(Box::into_raw(Box::new(args)) as _, Ordering::Relaxed); + } +} + +pub fn args() -> Args { + let args = unsafe { (ARGS.load(Ordering::Relaxed) as *const ArgsStore).as_ref() }; + let slice = args.map(|args| args.as_slice()).unwrap_or(&[]); + Args { iter: slice.iter() } +} + +pub struct Args { + iter: slice::Iter<'static, OsString>, +} + +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.iter.as_slice().fmt(f) + } +} + +impl Iterator for Args { + type Item = OsString; + + fn next(&mut self) -> Option { + self.iter.next().cloned() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn count(self) -> usize { + self.iter.len() + } + + fn last(self) -> Option { + self.iter.last().cloned() + } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + self.iter.advance_by(n) + } + + fn try_fold(&mut self, init: B, f: F) -> R + where + F: FnMut(B, Self::Item) -> R, + R: Try, + { + self.iter.by_ref().cloned().try_fold(init, f) + } + + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter.cloned().fold(init, f) + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + self.iter.next_back().cloned() + } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + self.iter.advance_back_by(n) + } +} + +impl ExactSizeIterator for Args { + #[inline] + fn len(&self) -> usize { + self.iter.len() + } + + #[inline] + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} diff --git a/library/std/src/sys/args/uefi.rs b/library/std/src/sys/args/uefi.rs new file mode 100644 index 0000000000000..02dada382eff0 --- /dev/null +++ b/library/std/src/sys/args/uefi.rs @@ -0,0 +1,118 @@ +use r_efi::protocols::loaded_image; + +pub use super::common::Args; +use crate::env::current_exe; +use crate::ffi::OsString; +use crate::iter::Iterator; +use crate::sys::pal::helpers; + +pub fn args() -> Args { + let lazy_current_exe = || Vec::from([current_exe().map(Into::into).unwrap_or_default()]); + + // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this + // will never fail. + let protocol = + helpers::image_handle_protocol::(loaded_image::PROTOCOL_GUID) + .unwrap(); + + let lp_size = unsafe { (*protocol.as_ptr()).load_options_size } as usize; + // Break if we are sure that it cannot be UTF-16 + if lp_size < size_of::() || lp_size % size_of::() != 0 { + return Args::new(lazy_current_exe()); + } + let lp_size = lp_size / size_of::(); + + let lp_cmd_line = unsafe { (*protocol.as_ptr()).load_options as *const u16 }; + if !lp_cmd_line.is_aligned() { + return Args::new(lazy_current_exe()); + } + let lp_cmd_line = unsafe { crate::slice::from_raw_parts(lp_cmd_line, lp_size) }; + + Args::new(parse_lp_cmd_line(lp_cmd_line).unwrap_or_else(lazy_current_exe)) +} + +/// Implements the UEFI command-line argument parsing algorithm. +/// +/// This implementation is based on what is defined in Section 3.4 of +/// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_Spec_2_0.pdf) +/// +/// Returns None in the following cases: +/// - Invalid UTF-16 (unpaired surrogate) +/// - Empty/improper arguments +fn parse_lp_cmd_line(code_units: &[u16]) -> Option> { + const QUOTE: char = '"'; + const SPACE: char = ' '; + const CARET: char = '^'; + const NULL: char = '\0'; + + let mut ret_val = Vec::new(); + let mut code_units_iter = char::decode_utf16(code_units.iter().cloned()).peekable(); + + // The executable name at the beginning is special. + let mut in_quotes = false; + let mut cur = String::new(); + while let Some(w) = code_units_iter.next() { + let w = w.ok()?; + match w { + // break on NULL + NULL => break, + // A quote mark always toggles `in_quotes` no matter what because + // there are no escape characters when parsing the executable name. + QUOTE => in_quotes = !in_quotes, + // If not `in_quotes` then whitespace ends argv[0]. + SPACE if !in_quotes => break, + // In all other cases the code unit is taken literally. + _ => cur.push(w), + } + } + + // If exe name is missing, the cli args are invalid + if cur.is_empty() { + return None; + } + + ret_val.push(OsString::from(cur)); + // Skip whitespace. + while code_units_iter.next_if_eq(&Ok(SPACE)).is_some() {} + + // Parse the arguments according to these rules: + // * All code units are taken literally except space, quote and caret. + // * When not `in_quotes`, space separate arguments. Consecutive spaces are + // treated as a single separator. + // * A space `in_quotes` is taken literally. + // * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally. + // * A quote can be escaped if preceded by caret. + // * A caret can be escaped if preceded by caret. + let mut cur = String::new(); + let mut in_quotes = false; + while let Some(w) = code_units_iter.next() { + let w = w.ok()?; + match w { + // break on NULL + NULL => break, + // If not `in_quotes`, a space or tab ends the argument. + SPACE if !in_quotes => { + ret_val.push(OsString::from(&cur[..])); + cur.truncate(0); + + // Skip whitespace. + while code_units_iter.next_if_eq(&Ok(SPACE)).is_some() {} + } + // Caret can escape quotes or carets + CARET if in_quotes => { + if let Some(x) = code_units_iter.next() { + cur.push(x.ok()?); + } + } + // If quote then flip `in_quotes` + QUOTE => in_quotes = !in_quotes, + // Everything else is always taken literally. + _ => cur.push(w), + } + } + // Push the final argument, if any. + if !cur.is_empty() || in_quotes { + ret_val.push(OsString::from(cur)); + } + Some(ret_val) +} diff --git a/library/std/src/sys/args/unix.rs b/library/std/src/sys/args/unix.rs new file mode 100644 index 0000000000000..0dfbd5f03eba9 --- /dev/null +++ b/library/std/src/sys/args/unix.rs @@ -0,0 +1,197 @@ +//! Global initialization and retrieval of command line arguments. +//! +//! On some platforms these are stored during runtime startup, +//! and on some they are retrieved from the system on demand. + +#![allow(dead_code)] // runtime init functions not used during testing + +pub use super::common::Args; +use crate::ffi::CStr; +#[cfg(target_os = "hermit")] +use crate::os::hermit::ffi::OsStringExt; +#[cfg(not(target_os = "hermit"))] +use crate::os::unix::ffi::OsStringExt; + +/// One-time global initialization. +pub unsafe fn init(argc: isize, argv: *const *const u8) { + unsafe { imp::init(argc, argv) } +} + +/// Returns the command line arguments +pub fn args() -> Args { + let (argc, argv) = imp::argc_argv(); + + let mut vec = Vec::with_capacity(argc as usize); + + for i in 0..argc { + // SAFETY: `argv` is non-null if `argc` is positive, and it is + // guaranteed to be at least as long as `argc`, so reading from it + // should be safe. + let ptr = unsafe { argv.offset(i).read() }; + + // Some C commandline parsers (e.g. GLib and Qt) are replacing already + // handled arguments in `argv` with `NULL` and move them to the end. + // + // Since they can't directly ensure updates to `argc` as well, this + // means that `argc` might be bigger than the actual number of + // non-`NULL` pointers in `argv` at this point. + // + // To handle this we simply stop iterating at the first `NULL` + // argument. `argv` is also guaranteed to be `NULL`-terminated so any + // non-`NULL` arguments after the first `NULL` can safely be ignored. + if ptr.is_null() { + // NOTE: On Apple platforms, `-[NSProcessInfo arguments]` does not + // stop iterating here, but instead `continue`, always iterating + // up until it reached `argc`. + // + // This difference will only matter in very specific circumstances + // where `argc`/`argv` have been modified, but in unexpected ways, + // so it likely doesn't really matter which option we choose. + // See the following PR for further discussion: + // + break; + } + + // SAFETY: Just checked that the pointer is not NULL, and arguments + // are otherwise guaranteed to be valid C strings. + let cstr = unsafe { CStr::from_ptr(ptr) }; + vec.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); + } + + Args::new(vec) +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "solaris", + target_os = "illumos", + target_os = "emscripten", + target_os = "haiku", + target_os = "hermit", + target_os = "l4re", + target_os = "fuchsia", + target_os = "redox", + target_os = "vxworks", + target_os = "horizon", + target_os = "aix", + target_os = "nto", + target_os = "hurd", + target_os = "rtems", + target_os = "nuttx", +))] +mod imp { + use crate::ffi::c_char; + use crate::ptr; + use crate::sync::atomic::{Atomic, AtomicIsize, AtomicPtr, Ordering}; + + // The system-provided argc and argv, which we store in static memory + // here so that we can defer the work of parsing them until its actually + // needed. + // + // Note that we never mutate argv/argc, the argv array, or the argv + // strings, which allows the code in this file to be very simple. + static ARGC: Atomic = AtomicIsize::new(0); + static ARGV: Atomic<*mut *const u8> = AtomicPtr::new(ptr::null_mut()); + + unsafe fn really_init(argc: isize, argv: *const *const u8) { + // These don't need to be ordered with each other or other stores, + // because they only hold the unmodified system-provided argv/argc. + ARGC.store(argc, Ordering::Relaxed); + ARGV.store(argv as *mut _, Ordering::Relaxed); + } + + #[inline(always)] + pub unsafe fn init(argc: isize, argv: *const *const u8) { + // on GNU/Linux if we are main then we will init argv and argc twice, it "duplicates work" + // BUT edge-cases are real: only using .init_array can break most emulators, dlopen, etc. + unsafe { really_init(argc, argv) }; + } + + /// 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`, as it does on macOS and Windows. + #[cfg(all(target_os = "linux", target_env = "gnu"))] + #[used] + #[unsafe(link_section = ".init_array.00099")] + static ARGV_INIT_ARRAY: extern "C" fn( + crate::os::raw::c_int, + *const *const u8, + *const *const u8, + ) = { + extern "C" fn init_wrapper( + argc: crate::os::raw::c_int, + argv: *const *const u8, + _envp: *const *const u8, + ) { + unsafe { really_init(argc as isize, argv) }; + } + init_wrapper + }; + + pub fn argc_argv() -> (isize, *const *const c_char) { + // Load ARGC and ARGV, which hold the unmodified system-provided + // argc/argv, so we can read the pointed-to memory without atomics or + // synchronization. + // + // If either ARGC or ARGV is still zero or null, then either there + // really are no arguments, or someone is asking for `args()` before + // initialization has completed, and we return an empty list. + let argv = ARGV.load(Ordering::Relaxed); + let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) }; + + // Cast from `*mut *const u8` to `*const *const c_char` + (argc, argv.cast()) + } +} + +// Use `_NSGetArgc` and `_NSGetArgv` on Apple platforms. +// +// Even though these have underscores in their names, they've been available +// since the first versions of both macOS and iOS, and are declared in +// the header `crt_externs.h`. +// +// NOTE: This header was added to the iOS 13.0 SDK, which has been the source +// of a great deal of confusion in the past about the availability of these +// APIs. +// +// NOTE(madsmtm): This has not strictly been verified to not cause App Store +// rejections; if this is found to be the case, the previous implementation +// of this used `[[NSProcessInfo processInfo] arguments]`. +#[cfg(target_vendor = "apple")] +mod imp { + use crate::ffi::{c_char, c_int}; + + pub unsafe fn init(_argc: isize, _argv: *const *const u8) { + // No need to initialize anything in here, `libdyld.dylib` has already + // done the work for us. + } + + pub fn argc_argv() -> (isize, *const *const c_char) { + unsafe extern "C" { + // These functions are in crt_externs.h. + fn _NSGetArgc() -> *mut c_int; + fn _NSGetArgv() -> *mut *mut *mut c_char; + } + + // SAFETY: The returned pointer points to a static initialized early + // in the program lifetime by `libdyld.dylib`, and as such is always + // valid. + // + // NOTE: Similar to `_NSGetEnviron`, there technically isn't anything + // protecting us against concurrent modifications to this, and there + // doesn't exist a lock that we can take. Instead, it is generally + // expected that it's only modified in `main` / before other code + // runs, so reading this here should be fine. + let argc = unsafe { _NSGetArgc().read() }; + // SAFETY: Same as above. + let argv = unsafe { _NSGetArgv().read() }; + + // Cast from `*mut *mut c_char` to `*const *const c_char` + (argc as isize, argv.cast()) + } +} diff --git a/library/std/src/sys/args/unsupported.rs b/library/std/src/sys/args/unsupported.rs new file mode 100644 index 0000000000000..ecffc6d26414b --- /dev/null +++ b/library/std/src/sys/args/unsupported.rs @@ -0,0 +1,42 @@ +use crate::ffi::OsString; +use crate::fmt; + +pub struct Args {} + +pub fn args() -> Args { + Args {} +} + +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().finish() + } +} + +impl Iterator for Args { + type Item = OsString; + + #[inline] + fn next(&mut self) -> Option { + None + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (0, Some(0)) + } +} + +impl DoubleEndedIterator for Args { + #[inline] + fn next_back(&mut self) -> Option { + None + } +} + +impl ExactSizeIterator for Args { + #[inline] + fn len(&self) -> usize { + 0 + } +} diff --git a/library/std/src/sys/args/wasi.rs b/library/std/src/sys/args/wasi.rs new file mode 100644 index 0000000000000..72063a87dc9f5 --- /dev/null +++ b/library/std/src/sys/args/wasi.rs @@ -0,0 +1,26 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +pub use super::common::Args; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::os::wasi::ffi::OsStrExt; + +/// Returns the command line arguments +pub fn args() -> Args { + Args::new(maybe_args().unwrap_or(Vec::new())) +} + +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) + } +} diff --git a/library/std/src/sys/args/windows.rs b/library/std/src/sys/args/windows.rs new file mode 100644 index 0000000000000..81c44fabdcc67 --- /dev/null +++ b/library/std/src/sys/args/windows.rs @@ -0,0 +1,411 @@ +//! The Windows command line is just a string +//! +//! +//! This module implements the parsing necessary to turn that string into a list of arguments. + +#[cfg(test)] +mod tests; + +pub use super::common::Args; +use crate::ffi::{OsStr, OsString}; +use crate::num::NonZero; +use crate::os::windows::prelude::*; +use crate::path::{Path, PathBuf}; +use crate::sys::pal::os::current_exe; +use crate::sys::pal::{ensure_no_nuls, fill_utf16_buf}; +use crate::sys::path::get_long_path; +use crate::sys::{c, to_u16s}; +use crate::sys_common::AsInner; +use crate::sys_common::wstr::WStrUnits; +use crate::{io, iter, ptr}; + +pub fn args() -> Args { + // SAFETY: `GetCommandLineW` returns a pointer to a null terminated UTF-16 + // string so it's safe for `WStrUnits` to use. + unsafe { + let lp_cmd_line = c::GetCommandLineW(); + let parsed_args_list = parse_lp_cmd_line(WStrUnits::new(lp_cmd_line), || { + current_exe().map(PathBuf::into_os_string).unwrap_or_else(|_| OsString::new()) + }); + + Args::new(parsed_args_list) + } +} + +/// Implements the Windows command-line argument parsing algorithm. +/// +/// Microsoft's documentation for the Windows CLI argument format can be found at +/// +/// +/// A more in-depth explanation is here: +/// +/// +/// Windows includes a function to do command line parsing in shell32.dll. +/// However, this is not used for two reasons: +/// +/// 1. 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 +/// . +/// +/// 2. It does not follow the modern C/C++ argv rules outlined in the first two links above. +/// +/// This function was tested for equivalence to the C/C++ parsing rules using an +/// extensive test suite available at +/// . +fn parse_lp_cmd_line<'a, F: Fn() -> OsString>( + lp_cmd_line: Option>, + exe_name: F, +) -> Vec { + const BACKSLASH: NonZero = NonZero::new(b'\\' as u16).unwrap(); + const QUOTE: NonZero = NonZero::new(b'"' as u16).unwrap(); + const TAB: NonZero = NonZero::new(b'\t' as u16).unwrap(); + const SPACE: NonZero = NonZero::new(b' ' as u16).unwrap(); + + let mut ret_val = Vec::new(); + // If the cmd line pointer is null or it points to an empty string then + // return the name of the executable as argv[0]. + if lp_cmd_line.as_ref().and_then(|cmd| cmd.peek()).is_none() { + ret_val.push(exe_name()); + return ret_val; + } + let mut code_units = lp_cmd_line.unwrap(); + + // The executable name at the beginning is special. + let mut in_quotes = false; + let mut cur = Vec::new(); + for w in &mut code_units { + match w { + // A quote mark always toggles `in_quotes` no matter what because + // there are no escape characters when parsing the executable name. + QUOTE => in_quotes = !in_quotes, + // If not `in_quotes` then whitespace ends argv[0]. + SPACE | TAB if !in_quotes => break, + // In all other cases the code unit is taken literally. + _ => cur.push(w.get()), + } + } + // Skip whitespace. + code_units.advance_while(|w| w == SPACE || w == TAB); + ret_val.push(OsString::from_wide(&cur)); + + // Parse the arguments according to these rules: + // * All code units are taken literally except space, tab, quote and backslash. + // * When not `in_quotes`, space and tab separate arguments. Consecutive spaces and tabs are + // treated as a single separator. + // * A space or tab `in_quotes` is taken literally. + // * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally. + // * A quote can be escaped if preceded by an odd number of backslashes. + // * If any number of backslashes is immediately followed by a quote then the number of + // backslashes is halved (rounding down). + // * Backslashes not followed by a quote are all taken literally. + // * If `in_quotes` then a quote can also be escaped using another quote + // (i.e. two consecutive quotes become one literal quote). + let mut cur = Vec::new(); + let mut in_quotes = false; + while let Some(w) = code_units.next() { + match w { + // If not `in_quotes`, a space or tab ends the argument. + SPACE | TAB if !in_quotes => { + ret_val.push(OsString::from_wide(&cur[..])); + cur.truncate(0); + + // Skip whitespace. + code_units.advance_while(|w| w == SPACE || w == TAB); + } + // Backslashes can escape quotes or backslashes but only if consecutive backslashes are followed by a quote. + BACKSLASH => { + let backslash_count = code_units.advance_while(|w| w == BACKSLASH) + 1; + if code_units.peek() == Some(QUOTE) { + cur.extend(iter::repeat(BACKSLASH.get()).take(backslash_count / 2)); + // The quote is escaped if there are an odd number of backslashes. + if backslash_count % 2 == 1 { + code_units.next(); + cur.push(QUOTE.get()); + } + } else { + // If there is no quote on the end then there is no escaping. + cur.extend(iter::repeat(BACKSLASH.get()).take(backslash_count)); + } + } + // If `in_quotes` and not backslash escaped (see above) then a quote either + // unsets `in_quote` or is escaped by another quote. + QUOTE if in_quotes => match code_units.peek() { + // Two consecutive quotes when `in_quotes` produces one literal quote. + Some(QUOTE) => { + cur.push(QUOTE.get()); + code_units.next(); + } + // Otherwise set `in_quotes`. + Some(_) => in_quotes = false, + // The end of the command line. + // Push `cur` even if empty, which we do by breaking while `in_quotes` is still set. + None => break, + }, + // If not `in_quotes` and not BACKSLASH escaped (see above) then a quote sets `in_quote`. + QUOTE => in_quotes = true, + // Everything else is always taken literally. + _ => cur.push(w.get()), + } + } + // Push the final argument, if any. + if !cur.is_empty() || in_quotes { + ret_val.push(OsString::from_wide(&cur[..])); + } + ret_val +} + +#[derive(Debug)] +pub(crate) enum Arg { + /// Add quotes (if needed) + Regular(OsString), + /// Append raw string without quoting + Raw(OsString), +} + +enum Quote { + // Every arg is quoted + Always, + // Whitespace and empty args are quoted + Auto, + // Arg appended without any changes (#29494) + Never, +} + +pub(crate) fn append_arg(cmd: &mut Vec, arg: &Arg, force_quotes: bool) -> io::Result<()> { + let (arg, quote) = match arg { + Arg::Regular(arg) => (arg, if force_quotes { Quote::Always } else { Quote::Auto }), + Arg::Raw(arg) => (arg, Quote::Never), + }; + + // 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_encoded_bytes(); + let (quote, escape) = match quote { + Quote::Always => (true, true), + Quote::Auto => { + (arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') || arg_bytes.is_empty(), true) + } + Quote::Never => (false, false), + }; + if quote { + cmd.push('"' as u16); + } + + let mut backslashes: usize = 0; + for x in arg.encode_wide() { + if escape { + 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 append_bat_arg(cmd: &mut Vec, arg: &OsStr, mut quote: bool) -> io::Result<()> { + ensure_no_nuls(arg)?; + // 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. + // + // We also need to quote the argument if it ends with `\` to guard against + // bat usage such as `"%~2"` (i.e. force quote arguments) otherwise a + // trailing slash will escape the closing quote. + if arg.is_empty() || arg.as_encoded_bytes().last() == Some(&b'\\') { + quote = true; + } + for cp in arg.as_inner().inner.code_points() { + if let Some(cp) = cp.to_char() { + // Rather than trying to find every ascii symbol that must be quoted, + // we assume that all ascii symbols must be quoted unless they're known to be good. + // We also quote Unicode control blocks for good measure. + // Note an unquoted `\` is fine so long as the argument isn't otherwise quoted. + static UNQUOTED: &str = r"#$*+-./:?@\_"; + let ascii_needs_quotes = + cp.is_ascii() && !(cp.is_ascii_alphanumeric() || UNQUOTED.contains(cp)); + if ascii_needs_quotes || cp.is_control() { + quote = true; + } + } + } + + if quote { + cmd.push('"' as u16); + } + // Loop through the string, escaping `\` only if followed by `"`. + // And escaping `"` by doubling them. + let mut backslashes: usize = 0; + for x in arg.encode_wide() { + if x == '\\' as u16 { + backslashes += 1; + } else { + if x == '"' as u16 { + // Add n backslashes to total 2n before internal `"`. + cmd.extend((0..backslashes).map(|_| '\\' as u16)); + // Appending an additional double-quote acts as an escape. + cmd.push(b'"' as u16) + } else if x == '%' as u16 || x == '\r' as u16 { + // yt-dlp hack: replaces `%` with `%%cd:~,%` to stop %VAR% being expanded as an environment variable. + // + // # Explanation + // + // cmd supports extracting a substring from a variable using the following syntax: + // %variable:~start_index,end_index% + // + // In the above command `cd` is used as the variable and the start_index and end_index are left blank. + // `cd` is a built-in variable that dynamically expands to the current directory so it's always available. + // Explicitly omitting both the start and end index creates a zero-length substring. + // + // Therefore it all resolves to nothing. However, by doing this no-op we distract cmd.exe + // from potentially expanding %variables% in the argument. + cmd.extend_from_slice(&[ + '%' as u16, '%' as u16, 'c' as u16, 'd' as u16, ':' as u16, '~' as u16, + ',' 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(()) +} + +pub(crate) fn make_bat_command_line( + script: &[u16], + args: &[Arg], + force_quotes: bool, +) -> io::Result> { + const INVALID_ARGUMENT_ERROR: io::Error = + io::const_error!(io::ErrorKind::InvalidInput, r#"batch file arguments are invalid"#); + // Set the start of the command line to `cmd.exe /c "` + // It is necessary to surround the command in an extra pair of quotes, + // hence the trailing quote here. It will be closed after all arguments + // have been added. + // Using /e:ON enables "command extensions" which is essential for the `%` hack to work. + let mut cmd: Vec = "cmd.exe /e:ON /v:OFF /d /c \"".encode_utf16().collect(); + + // Push the script name surrounded by its quote pair. + cmd.push(b'"' as u16); + // Windows file names cannot contain a `"` character or end with `\\`. + // If the script name does then return an error. + if script.contains(&(b'"' as u16)) || script.last() == Some(&(b'\\' as u16)) { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + "Windows file names may not contain `\"` or end with `\\`" + )); + } + cmd.extend_from_slice(script.strip_suffix(&[0]).unwrap_or(script)); + cmd.push(b'"' as u16); + + // Append the arguments. + // FIXME: This needs tests to ensure that the arguments are properly + // reconstructed by the batch script by default. + for arg in args { + cmd.push(' ' as u16); + match arg { + Arg::Regular(arg_os) => { + let arg_bytes = arg_os.as_encoded_bytes(); + // Disallow \r and \n as they may truncate the arguments. + const DISALLOWED: &[u8] = b"\r\n"; + if arg_bytes.iter().any(|c| DISALLOWED.contains(c)) { + return Err(INVALID_ARGUMENT_ERROR); + } + append_bat_arg(&mut cmd, arg_os, force_quotes)?; + } + _ => { + // Raw arguments are passed on as-is. + // It's the user's responsibility to properly handle arguments in this case. + append_arg(&mut cmd, arg, force_quotes)?; + } + }; + } + + // Close the quote we left opened earlier. + cmd.push(b'"' as u16); + + Ok(cmd) +} + +/// Takes a path and tries to return a non-verbatim path. +/// +/// This is necessary because cmd.exe does not support verbatim paths. +pub(crate) fn to_user_path(path: &Path) -> io::Result> { + from_wide_to_user_path(to_u16s(path)?) +} +pub(crate) fn from_wide_to_user_path(mut path: Vec) -> io::Result> { + // UTF-16 encoded code points, used in parsing and building UTF-16 paths. + // All of these are in the ASCII range so they can be cast directly to `u16`. + const SEP: u16 = b'\\' as _; + const QUERY: u16 = b'?' as _; + const COLON: u16 = b':' as _; + const U: u16 = b'U' as _; + const N: u16 = b'N' as _; + const C: u16 = b'C' as _; + + // Early return if the path is too long to remove the verbatim prefix. + const LEGACY_MAX_PATH: usize = 260; + if path.len() > LEGACY_MAX_PATH { + return Ok(path); + } + + match &path[..] { + // `\\?\C:\...` => `C:\...` + [SEP, SEP, QUERY, SEP, _, COLON, SEP, ..] => unsafe { + let lpfilename = path[4..].as_ptr(); + fill_utf16_buf( + |buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()), + |full_path: &[u16]| { + if full_path == &path[4..path.len() - 1] { + let mut path: Vec = full_path.into(); + path.push(0); + path + } else { + path + } + }, + ) + }, + // `\\?\UNC\...` => `\\...` + [SEP, SEP, QUERY, SEP, U, N, C, SEP, ..] => unsafe { + // Change the `C` in `UNC\` to `\` so we can get a slice that starts with `\\`. + path[6] = b'\\' as u16; + let lpfilename = path[6..].as_ptr(); + fill_utf16_buf( + |buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()), + |full_path: &[u16]| { + if full_path == &path[6..path.len() - 1] { + let mut path: Vec = full_path.into(); + path.push(0); + path + } else { + // Restore the 'C' in "UNC". + path[6] = b'C' as u16; + path + } + }, + ) + }, + // For everything else, leave the path unchanged. + _ => get_long_path(path, false), + } +} diff --git a/library/std/src/sys/pal/windows/args/tests.rs b/library/std/src/sys/args/windows/tests.rs similarity index 100% rename from library/std/src/sys/pal/windows/args/tests.rs rename to library/std/src/sys/args/windows/tests.rs diff --git a/library/std/src/sys/args/xous.rs b/library/std/src/sys/args/xous.rs new file mode 100644 index 0000000000000..2010bad14d1fb --- /dev/null +++ b/library/std/src/sys/args/xous.rs @@ -0,0 +1,20 @@ +pub use super::common::Args; +use crate::sys::pal::os::get_application_parameters; +use crate::sys::pal::os::params::ArgumentList; + +pub fn args() -> Args { + let Some(params) = get_application_parameters() else { + return Args::new(vec![]); + }; + + for param in params { + if let Ok(args) = ArgumentList::try_from(¶m) { + let mut parsed_args = vec![]; + for arg in args { + parsed_args.push(arg.into()); + } + return Args::new(parsed_args); + } + } + Args::new(vec![]) +} diff --git a/library/std/src/sys/args/zkvm.rs b/library/std/src/sys/args/zkvm.rs new file mode 100644 index 0000000000000..194ba7159d459 --- /dev/null +++ b/library/std/src/sys/args/zkvm.rs @@ -0,0 +1,81 @@ +use crate::ffi::OsString; +use crate::fmt; +use crate::sys::os_str; +use crate::sys::pal::{WORD_SIZE, abi}; +use crate::sys_common::FromInner; + +pub struct Args { + i_forward: usize, + i_back: usize, + count: usize, +} + +pub fn args() -> Args { + let count = unsafe { abi::sys_argc() }; + Args { i_forward: 0, i_back: 0, count } +} + +impl Args { + /// Use sys_argv to get the arg at the requested index. Does not check that i is less than argc + /// and will not return if the index is out of bounds. + fn argv(i: usize) -> OsString { + let arg_len = unsafe { abi::sys_argv(crate::ptr::null_mut(), 0, i) }; + + let arg_len_words = (arg_len + WORD_SIZE - 1) / WORD_SIZE; + let words = unsafe { abi::sys_alloc_words(arg_len_words) }; + + let arg_len2 = unsafe { abi::sys_argv(words, arg_len_words, i) }; + debug_assert_eq!(arg_len, arg_len2); + + // Convert to OsString. + // + // FIXME: We can probably get rid of the extra copy here if we + // reimplement "os_str" instead of just using the generic unix + // "os_str". + let arg_bytes: &[u8] = + unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, arg_len) }; + OsString::from_inner(os_str::Buf { inner: arg_bytes.to_vec() }) + } +} + +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().finish() + } +} + +impl Iterator for Args { + type Item = OsString; + + fn next(&mut self) -> Option { + if self.i_forward >= self.count - self.i_back { + None + } else { + let arg = Self::argv(self.i_forward); + self.i_forward += 1; + Some(arg) + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.count, Some(self.count)) + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.count + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + if self.i_back >= self.count - self.i_forward { + None + } else { + let arg = Self::argv(self.count - 1 - self.i_back); + self.i_back += 1; + Some(arg) + } + } +} diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index c9969b4e376ea..299ce1a6ff063 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -3,70 +3,66 @@ // These symbols are all defined by `libm`, // or by `compiler-builtins` on unsupported platforms. unsafe extern "C" { - pub fn acos(n: f64) -> f64; - pub fn asin(n: f64) -> f64; - pub fn atan(n: f64) -> f64; - pub fn atan2(a: f64, b: f64) -> f64; - pub fn cbrt(n: f64) -> f64; - pub fn cbrtf(n: f32) -> f32; - pub fn cosh(n: f64) -> f64; - pub fn expm1(n: f64) -> f64; - pub fn expm1f(n: f32) -> f32; - pub fn fdim(a: f64, b: f64) -> f64; - pub fn fdimf(a: f32, b: f32) -> f32; + pub safe fn acos(n: f64) -> f64; + pub safe fn asin(n: f64) -> f64; + pub safe fn atan(n: f64) -> f64; + pub safe fn atan2(a: f64, b: f64) -> f64; + pub safe fn cosh(n: f64) -> f64; + pub safe fn expm1(n: f64) -> f64; + pub safe fn expm1f(n: f32) -> f32; #[cfg_attr(target_env = "msvc", link_name = "_hypot")] - pub fn hypot(x: f64, y: f64) -> f64; + pub safe fn hypot(x: f64, y: f64) -> f64; #[cfg_attr(target_env = "msvc", link_name = "_hypotf")] - pub fn hypotf(x: f32, y: f32) -> f32; - pub fn log1p(n: f64) -> f64; - pub fn log1pf(n: f32) -> f32; - pub fn sinh(n: f64) -> f64; - pub fn tan(n: f64) -> f64; - pub fn tanh(n: f64) -> f64; - pub fn tgamma(n: f64) -> f64; - pub fn tgammaf(n: f32) -> f32; - pub fn lgamma_r(n: f64, s: &mut i32) -> f64; + pub safe fn hypotf(x: f32, y: f32) -> f32; + pub safe fn log1p(n: f64) -> f64; + pub safe fn log1pf(n: f32) -> f32; + pub safe fn sinh(n: f64) -> f64; + pub safe fn tan(n: f64) -> f64; + pub safe fn tanh(n: f64) -> f64; + pub safe fn tgamma(n: f64) -> f64; + pub safe fn tgammaf(n: f32) -> f32; + pub safe fn lgamma_r(n: f64, s: &mut i32) -> f64; #[cfg(not(target_os = "aix"))] - pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; - pub fn erf(n: f64) -> f64; - pub fn erff(n: f32) -> f32; - pub fn erfc(n: f64) -> f64; - pub fn erfcf(n: f32) -> f32; + pub safe fn lgammaf_r(n: f32, s: &mut i32) -> f32; + pub safe fn erf(n: f64) -> f64; + pub safe fn erff(n: f32) -> f32; + pub safe fn erfc(n: f64) -> f64; + pub safe fn erfcf(n: f32) -> f32; - pub fn acosf128(n: f128) -> f128; - pub fn asinf128(n: f128) -> f128; - pub fn atanf128(n: f128) -> f128; - pub fn atan2f128(a: f128, b: f128) -> f128; - pub fn cbrtf128(n: f128) -> f128; - pub fn coshf128(n: f128) -> f128; - pub fn expm1f128(n: f128) -> f128; - pub fn hypotf128(x: f128, y: f128) -> f128; - pub fn log1pf128(n: f128) -> f128; - pub fn sinhf128(n: f128) -> f128; - pub fn tanf128(n: f128) -> f128; - pub fn tanhf128(n: f128) -> f128; - pub fn tgammaf128(n: f128) -> f128; - pub fn lgammaf128_r(n: f128, s: &mut i32) -> f128; - pub fn erff128(n: f128) -> f128; - pub fn erfcf128(n: f128) -> f128; + pub safe fn acosf128(n: f128) -> f128; + pub safe fn asinf128(n: f128) -> f128; + pub safe fn atanf128(n: f128) -> f128; + pub safe fn atan2f128(a: f128, b: f128) -> f128; + pub safe fn cbrtf128(n: f128) -> f128; + pub safe fn coshf128(n: f128) -> f128; + pub safe fn expm1f128(n: f128) -> f128; + pub safe fn hypotf128(x: f128, y: f128) -> f128; + pub safe fn log1pf128(n: f128) -> f128; + pub safe fn sinhf128(n: f128) -> f128; + pub safe fn tanf128(n: f128) -> f128; + pub safe fn tanhf128(n: f128) -> f128; + pub safe fn tgammaf128(n: f128) -> f128; + pub safe fn lgammaf128_r(n: f128, s: &mut i32) -> f128; + pub safe fn erff128(n: f128) -> f128; + pub safe fn erfcf128(n: f128) -> f128; cfg_if::cfg_if! { if #[cfg(not(all(target_os = "windows", target_env = "msvc", target_arch = "x86")))] { - pub fn acosf(n: f32) -> f32; - pub fn asinf(n: f32) -> f32; - pub fn atan2f(a: f32, b: f32) -> f32; - pub fn atanf(n: f32) -> f32; - pub fn coshf(n: f32) -> f32; - pub fn sinhf(n: f32) -> f32; - pub fn tanf(n: f32) -> f32; - pub fn tanhf(n: f32) -> f32; + pub safe fn acosf(n: f32) -> f32; + pub safe fn asinf(n: f32) -> f32; + pub safe fn atan2f(a: f32, b: f32) -> f32; + pub safe fn atanf(n: f32) -> f32; + pub safe fn coshf(n: f32) -> f32; + pub safe fn sinhf(n: f32) -> f32; + pub safe fn tanf(n: f32) -> f32; + pub safe fn tanhf(n: f32) -> f32; }} } // On AIX, we don't have lgammaf_r only the f64 version, so we can // use the f64 version lgamma_r #[cfg(target_os = "aix")] -pub unsafe fn lgammaf_r(n: f32, s: &mut i32) -> f32 { +pub fn lgammaf_r(n: f32, s: &mut i32) -> f32 { lgamma_r(n.into(), s) as f32 } @@ -76,42 +72,42 @@ pub unsafe fn lgammaf_r(n: f32, s: &mut i32) -> f32 { cfg_if::cfg_if! { if #[cfg(all(target_os = "windows", target_env = "msvc", target_arch = "x86"))] { #[inline] - pub unsafe fn acosf(n: f32) -> f32 { + pub fn acosf(n: f32) -> f32 { f64::acos(n as f64) as f32 } #[inline] - pub unsafe fn asinf(n: f32) -> f32 { + pub fn asinf(n: f32) -> f32 { f64::asin(n as f64) as f32 } #[inline] - pub unsafe fn atan2f(n: f32, b: f32) -> f32 { + pub fn atan2f(n: f32, b: f32) -> f32 { f64::atan2(n as f64, b as f64) as f32 } #[inline] - pub unsafe fn atanf(n: f32) -> f32 { + pub fn atanf(n: f32) -> f32 { f64::atan(n as f64) as f32 } #[inline] - pub unsafe fn coshf(n: f32) -> f32 { + pub fn coshf(n: f32) -> f32 { f64::cosh(n as f64) as f32 } #[inline] - pub unsafe fn sinhf(n: f32) -> f32 { + pub fn sinhf(n: f32) -> f32 { f64::sinh(n as f64) as f32 } #[inline] - pub unsafe fn tanf(n: f32) -> f32 { + pub fn tanf(n: f32) -> f32 { f64::tan(n as f64) as f32 } #[inline] - pub unsafe fn tanhf(n: f32) -> f32 { + pub fn tanhf(n: f32) -> f32 { f64::tanh(n as f64) as f32 } }} diff --git a/library/std/src/sys/env/common.rs b/library/std/src/sys/env/common.rs new file mode 100644 index 0000000000000..f161ff073f3d5 --- /dev/null +++ b/library/std/src/sys/env/common.rs @@ -0,0 +1,48 @@ +use crate::ffi::OsString; +use crate::{fmt, vec}; + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list() + .entries(self.slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub(super) fn new(env: Vec<(OsString, OsString)>) -> Self { + Env { iter: env.into_iter() } + } + + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + EnvStrDebug { slice: self.iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.as_slice()).finish() + } +} + +impl !Send for Env {} +impl !Sync for Env {} + +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() + } +} diff --git a/library/std/src/sys/env/hermit.rs b/library/std/src/sys/env/hermit.rs new file mode 100644 index 0000000000000..445ecdeb6a39f --- /dev/null +++ b/library/std/src/sys/env/hermit.rs @@ -0,0 +1,72 @@ +use core::slice::memchr; + +pub use super::common::Env; +use crate::collections::HashMap; +use crate::ffi::{CStr, OsStr, OsString, c_char}; +use crate::io; +use crate::os::hermit::ffi::OsStringExt; +use crate::sync::Mutex; + +static ENV: Mutex>> = Mutex::new(None); + +pub fn init(env: *const *const c_char) { + let mut guard = ENV.lock().unwrap(); + let map = guard.insert(HashMap::new()); + + if env.is_null() { + return; + } + + unsafe { + let mut environ = env; + while !(*environ).is_null() { + if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) { + map.insert(key, value); + } + environ = environ.add(1); + } + } + + 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()), + ) + }) + } +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + let guard = ENV.lock().unwrap(); + let env = guard.as_ref().unwrap(); + + let result = env.iter().map(|(key, value)| (key.clone(), value.clone())).collect(); + + Env::new(result) +} + +pub fn getenv(k: &OsStr) -> Option { + ENV.lock().unwrap().as_ref().unwrap().get(k).cloned() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + ENV.lock().unwrap().as_mut().unwrap().insert(k, v); + Ok(()) +} + +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + ENV.lock().unwrap().as_mut().unwrap().remove(k); + Ok(()) +} diff --git a/library/std/src/sys/env/mod.rs b/library/std/src/sys/env/mod.rs new file mode 100644 index 0000000000000..d81ff875c830f --- /dev/null +++ b/library/std/src/sys/env/mod.rs @@ -0,0 +1,48 @@ +//! Platform-dependent environment variables abstraction. + +#![forbid(unsafe_op_in_unsafe_fn)] + +#[cfg(any( + target_family = "unix", + target_os = "hermit", + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "solid_asp3", + target_os = "uefi", + target_os = "wasi", + target_os = "xous", +))] +mod common; + +cfg_if::cfg_if! { + if #[cfg(target_family = "unix")] { + mod unix; + pub use unix::*; + } else if #[cfg(target_family = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + pub use sgx::*; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::*; + } else if #[cfg(target_os = "xous")] { + mod xous; + pub use xous::*; + } else if #[cfg(target_os = "zkvm")] { + mod zkvm; + pub use zkvm::*; + } else { + mod unsupported; + pub use unsupported::*; + } +} diff --git a/library/std/src/sys/env/sgx.rs b/library/std/src/sys/env/sgx.rs new file mode 100644 index 0000000000000..09090ec7cf0dd --- /dev/null +++ b/library/std/src/sys/env/sgx.rs @@ -0,0 +1,55 @@ +#![allow(fuzzy_provenance_casts)] // FIXME: this module systematically confuses pointers and integers + +pub use super::common::Env; +use crate::collections::HashMap; +use crate::ffi::{OsStr, OsString}; +use crate::io; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; +use crate::sync::{Mutex, Once}; + +// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests +#[cfg_attr(test, linkage = "available_externally")] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os3ENVE")] +static ENV: Atomic = AtomicUsize::new(0); +// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests +#[cfg_attr(test, linkage = "available_externally")] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os8ENV_INITE")] +static ENV_INIT: Once = Once::new(); +type EnvStore = Mutex>; + +fn get_env_store() -> Option<&'static EnvStore> { + unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() } +} + +fn create_env_store() -> &'static EnvStore { + ENV_INIT.call_once(|| { + ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed) + }); + unsafe { &*(ENV.load(Ordering::Relaxed) as *const EnvStore) } +} + +pub fn env() -> Env { + let clone_to_vec = |map: &HashMap| -> Vec<_> { + map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() + }; + + let env = get_env_store().map(|env| clone_to_vec(&env.lock().unwrap())).unwrap_or_default(); + Env::new(env) +} + +pub fn getenv(k: &OsStr) -> Option { + get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned()) +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + create_env_store().lock().unwrap().insert(k, v); + Ok(()) +} + +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + if let Some(env) = get_env_store() { + env.lock().unwrap().remove(k); + } + Ok(()) +} diff --git a/library/std/src/sys/env/solid.rs b/library/std/src/sys/env/solid.rs new file mode 100644 index 0000000000000..ea77fc3c11930 --- /dev/null +++ b/library/std/src/sys/env/solid.rs @@ -0,0 +1,96 @@ +use core::slice::memchr; + +pub use super::common::Env; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::io; +use crate::os::raw::{c_char, c_int}; +use crate::os::solid::ffi::{OsStrExt, OsStringExt}; +use crate::sync::{PoisonError, RwLock}; +use crate::sys::common::small_c_string::run_with_cstr; + +static ENV_LOCK: RwLock<()> = RwLock::new(()); + +pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + unsafe extern "C" { + static mut environ: *const *const c_char; + } + + unsafe { + let _guard = env_read_lock(); + 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::new(result); + } + + 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) -> Option { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), &|k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + run_with_cstr(k.as_bytes(), &|k| { + run_with_cstr(v.as_bytes(), &|v| { + let _guard = ENV_LOCK.write(); + cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) + }) + }) +} + +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + run_with_cstr(n.as_bytes(), &|nbuf| { + let _guard = ENV_LOCK.write(); + cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) + }) +} + +/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this +/// function just returns a generic error. +fn cvt_env(t: c_int) -> io::Result { + if t == -1 { Err(io::const_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) } +} diff --git a/library/std/src/sys/env/uefi.rs b/library/std/src/sys/env/uefi.rs new file mode 100644 index 0000000000000..1561df41cac3f --- /dev/null +++ b/library/std/src/sys/env/uefi.rs @@ -0,0 +1,102 @@ +pub use super::common::Env; +use crate::ffi::{OsStr, OsString}; +use crate::io; + +pub fn env() -> Env { + let env = uefi_env::get_all().expect("not supported on this platform"); + Env::new(env) +} + +pub fn getenv(key: &OsStr) -> Option { + uefi_env::get(key) +} + +pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> { + uefi_env::set(key, val) +} + +pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> { + uefi_env::unset(key) +} + +mod uefi_env { + use crate::ffi::{OsStr, OsString}; + use crate::io; + use crate::os::uefi::ffi::OsStringExt; + use crate::ptr::NonNull; + use crate::sys::{helpers, unsupported_err}; + + pub(crate) fn get(key: &OsStr) -> Option { + let shell = helpers::open_shell()?; + let mut key_ptr = helpers::os_string_to_raw(key)?; + unsafe { get_raw(shell, key_ptr.as_mut_ptr()) } + } + + pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> { + let mut key_ptr = helpers::os_string_to_raw(key) + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; + let mut val_ptr = helpers::os_string_to_raw(val) + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?; + unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) } + } + + pub(crate) fn unset(key: &OsStr) -> io::Result<()> { + let mut key_ptr = helpers::os_string_to_raw(key) + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; + unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) } + } + + pub(crate) fn get_all() -> io::Result> { + let shell = helpers::open_shell().ok_or(unsupported_err())?; + + let mut vars = Vec::new(); + let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) }; + + if val.is_null() { + return Ok(vars); + } + + let mut start = 0; + + // UEFI Shell returns all keys separated by NULL. + // End of string is denoted by two NULLs + for i in 0.. { + if unsafe { *val.add(i) } == 0 { + // Two NULL signal end of string + if i == start { + break; + } + + let key = OsString::from_wide(unsafe { + crate::slice::from_raw_parts(val.add(start), i - start) + }); + // SAFETY: val.add(start) is always NULL terminated + let val = unsafe { get_raw(shell, val.add(start)) } + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?; + + vars.push((key, val)); + start = i + 1; + } + } + + Ok(vars) + } + + unsafe fn get_raw( + shell: NonNull, + key_ptr: *mut r_efi::efi::Char16, + ) -> Option { + let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) }; + helpers::os_string_from_raw(val) + } + + unsafe fn set_raw( + key_ptr: *mut r_efi::efi::Char16, + val_ptr: *mut r_efi::efi::Char16, + ) -> io::Result<()> { + let shell = helpers::open_shell().ok_or(unsupported_err())?; + let r = + unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } +} diff --git a/library/std/src/sys/env/unix.rs b/library/std/src/sys/env/unix.rs new file mode 100644 index 0000000000000..78c7af65f9e38 --- /dev/null +++ b/library/std/src/sys/env/unix.rs @@ -0,0 +1,126 @@ +use core::slice::memchr; + +use libc::c_char; + +pub use super::common::Env; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::io; +use crate::os::unix::prelude::*; +use crate::sync::{PoisonError, RwLock}; +use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys::cvt; + +// Use `_NSGetEnviron` on Apple platforms. +// +// `_NSGetEnviron` is the documented alternative (see `man environ`), and has +// been available since the first versions of both macOS and iOS. +// +// Nowadays, specifically since macOS 10.8, `environ` has been exposed through +// `libdyld.dylib`, which is linked via. `libSystem.dylib`: +// +// +// So in the end, it likely doesn't really matter which option we use, but the +// performance cost of using `_NSGetEnviron` is extremely miniscule, and it +// might be ever so slightly more supported, so let's just use that. +// +// NOTE: The header where this is defined (`crt_externs.h`) was added to the +// iOS 13.0 SDK, which has been the source of a great deal of confusion in the +// past about the availability of this API. +// +// NOTE(madsmtm): Neither this nor using `environ` has been verified to not +// cause App Store rejections; if this is found to be the case, an alternative +// implementation of this is possible using `[NSProcessInfo environment]` +// - which internally uses `_NSGetEnviron` and a system-wide lock on the +// environment variables to protect against `setenv`, so using that might be +// desirable anyhow? Though it also means that we have to link to Foundation. +#[cfg(target_vendor = "apple")] +pub unsafe fn environ() -> *mut *const *const c_char { + unsafe { libc::_NSGetEnviron() as *mut *const *const c_char } +} + +// Use the `environ` static which is part of POSIX. +#[cfg(not(target_vendor = "apple"))] +pub unsafe fn environ() -> *mut *const *const c_char { + unsafe extern "C" { + static mut environ: *const *const c_char; + } + &raw mut environ +} + +static ENV_LOCK: RwLock<()> = RwLock::new(()); + +pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) +} + +/// 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_read_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::new(result); + } + + 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) -> Option { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), &|k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + run_with_cstr(k.as_bytes(), &|k| { + run_with_cstr(v.as_bytes(), &|v| { + let _guard = ENV_LOCK.write(); + cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) + }) + }) +} + +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + run_with_cstr(n.as_bytes(), &|nbuf| { + let _guard = ENV_LOCK.write(); + cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) + }) +} diff --git a/library/std/src/sys/env/unsupported.rs b/library/std/src/sys/env/unsupported.rs new file mode 100644 index 0000000000000..98905e6482747 --- /dev/null +++ b/library/std/src/sys/env/unsupported.rs @@ -0,0 +1,40 @@ +use crate::ffi::{OsStr, OsString}; +use crate::{fmt, io}; + +pub struct Env(!); + +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + self.0 + } +} + +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.0 + } +} + +pub fn env() -> Env { + panic!("not supported on this platform") +} + +pub fn getenv(_: &OsStr) -> Option { + None +} + +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +} + +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +} diff --git a/library/std/src/sys/env/wasi.rs b/library/std/src/sys/env/wasi.rs new file mode 100644 index 0000000000000..3719f9db51eb3 --- /dev/null +++ b/library/std/src/sys/env/wasi.rs @@ -0,0 +1,102 @@ +use core::slice::memchr; + +pub use super::common::Env; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::io; +use crate::os::wasi::prelude::*; +use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys::pal::os::{cvt, libc}; + +cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + // Access to the environment must be protected by a lock in multi-threaded scenarios. + use crate::sync::{PoisonError, RwLock}; + static ENV_LOCK: RwLock<()> = RwLock::new(()); + pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) + } + pub fn env_write_lock() -> impl Drop { + ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner) + } + } else { + // No need for a lock if we are single-threaded. + pub fn env_read_lock() -> impl Drop { + Box::new(()) + } + pub fn env_write_lock() -> impl Drop { + Box::new(()) + } + } +} + +pub fn env() -> Env { + unsafe { + let _guard = env_read_lock(); + + // Use `__wasilibc_get_environ` instead of `environ` here so that we + // don't require wasi-libc to eagerly initialize the environment + // variables. + let mut environ = libc::__wasilibc_get_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::new(result); + } + + // See src/libstd/sys/pal/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) -> Option { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), &|k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + run_with_cstr(k.as_bytes(), &|k| { + run_with_cstr(v.as_bytes(), &|v| unsafe { + let _guard = env_write_lock(); + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) + }) + }) +} + +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + run_with_cstr(n.as_bytes(), &|nbuf| unsafe { + let _guard = env_write_lock(); + cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) + }) +} diff --git a/library/std/src/sys/env/windows.rs b/library/std/src/sys/env/windows.rs new file mode 100644 index 0000000000000..3c4d4a84cfd6b --- /dev/null +++ b/library/std/src/sys/env/windows.rs @@ -0,0 +1,133 @@ +use crate::ffi::{OsStr, OsString}; +use crate::os::windows::prelude::*; +use crate::sys::pal::{c, cvt, fill_utf16_buf, to_u16s}; +use crate::{fmt, io, ptr, slice}; + +pub struct Env { + base: *mut c::WCHAR, + iter: EnvIterator, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. +pub struct EnvStrDebug<'a> { + iter: &'a EnvIterator, +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + let iter: EnvIterator = (*iter).clone(); + let mut list = f.debug_list(); + for (a, b) in iter { + list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); + } + list.finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { base: _, iter } = self; + EnvStrDebug { iter } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { base: _, iter } = self; + f.debug_list().entries(iter.clone()).finish() + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + let Self { base: _, iter } = self; + iter.next() + } +} + +#[derive(Clone)] +struct EnvIterator(*mut c::WCHAR); + +impl Iterator for EnvIterator { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + let Self(cur) = self; + loop { + unsafe { + if **cur == 0 { + return None; + } + let p = *cur as *const u16; + let mut len = 0; + while *p.add(len) != 0 { + len += 1; + } + let s = slice::from_raw_parts(p, len); + *cur = cur.add(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.is_null() { + panic!("failure getting env string from OS: {}", io::Error::last_os_error()); + } + Env { base: ch, iter: EnvIterator(ch) } + } +} + +pub fn getenv(k: &OsStr) -> Option { + let k = to_u16s(k).ok()?; + fill_utf16_buf( + |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) }, + OsStringExt::from_wide, + ) + .ok() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + // SAFETY: We ensure that k and v are null-terminated wide strings. + unsafe { + let k = to_u16s(k)?; + let v = to_u16s(v)?; + + cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop) + } +} + +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + // SAFETY: We ensure that v is a null-terminated wide strings. + unsafe { + let v = to_u16s(n)?; + cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop) + } +} diff --git a/library/std/src/sys/env/xous.rs b/library/std/src/sys/env/xous.rs new file mode 100644 index 0000000000000..8f65f30d35fcc --- /dev/null +++ b/library/std/src/sys/env/xous.rs @@ -0,0 +1,54 @@ +pub use super::common::Env; +use crate::collections::HashMap; +use crate::ffi::{OsStr, OsString}; +use crate::io; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; +use crate::sync::{Mutex, Once}; +use crate::sys::pal::os::{get_application_parameters, params}; + +static ENV: Atomic = AtomicUsize::new(0); +static ENV_INIT: Once = Once::new(); +type EnvStore = Mutex>; + +fn get_env_store() -> &'static EnvStore { + ENV_INIT.call_once(|| { + let env_store = EnvStore::default(); + if let Some(params) = get_application_parameters() { + for param in params { + if let Ok(envs) = params::EnvironmentBlock::try_from(¶m) { + let mut env_store = env_store.lock().unwrap(); + for env in envs { + env_store.insert(env.key.into(), env.value.into()); + } + break; + } + } + } + ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed) + }); + unsafe { &*core::ptr::with_exposed_provenance::(ENV.load(Ordering::Relaxed)) } +} + +pub fn env() -> Env { + let clone_to_vec = |map: &HashMap| -> Vec<_> { + map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() + }; + + let env = clone_to_vec(&*get_env_store().lock().unwrap()); + Env::new(env) +} + +pub fn getenv(k: &OsStr) -> Option { + get_env_store().lock().unwrap().get(k).cloned() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + get_env_store().lock().unwrap().insert(k, v); + Ok(()) +} + +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + get_env_store().lock().unwrap().remove(k); + Ok(()) +} diff --git a/library/std/src/sys/env/zkvm.rs b/library/std/src/sys/env/zkvm.rs new file mode 100644 index 0000000000000..2eb7005ba1289 --- /dev/null +++ b/library/std/src/sys/env/zkvm.rs @@ -0,0 +1,32 @@ +#[expect(dead_code)] +#[path = "unsupported.rs"] +mod unsupported_env; +pub use unsupported_env::{Env, env, setenv, unsetenv}; + +use crate::ffi::{OsStr, OsString}; +use crate::sys::os_str; +use crate::sys::pal::{WORD_SIZE, abi}; +use crate::sys_common::FromInner; + +pub fn getenv(varname: &OsStr) -> Option { + let varname = varname.as_encoded_bytes(); + let nbytes = + unsafe { abi::sys_getenv(crate::ptr::null_mut(), 0, varname.as_ptr(), varname.len()) }; + if nbytes == usize::MAX { + return None; + } + + let nwords = (nbytes + WORD_SIZE - 1) / WORD_SIZE; + let words = unsafe { abi::sys_alloc_words(nwords) }; + + let nbytes2 = unsafe { abi::sys_getenv(words, nwords, varname.as_ptr(), varname.len()) }; + debug_assert_eq!(nbytes, nbytes2); + + // Convert to OsString. + // + // FIXME: We can probably get rid of the extra copy here if we + // reimplement "os_str" instead of just using the generic unix + // "os_str". + let u8s: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, nbytes) }; + Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() })) +} diff --git a/library/std/src/sys/env_consts.rs b/library/std/src/sys/env_consts.rs new file mode 100644 index 0000000000000..9683fd47cf96b --- /dev/null +++ b/library/std/src/sys/env_consts.rs @@ -0,0 +1,415 @@ +//! Constants associated with each target. + +// Replaces the #[else] gate with #[cfg(not(any(…)))] of all the other gates. +// This ensures that they must be mutually exclusive and do not have precedence +// like cfg_if!. +macro cfg_unordered( + $(#[cfg($cfg:meta)] $os:item)* + #[else] $fallback:item +) { + $(#[cfg($cfg)] $os)* + #[cfg(not(any($($cfg),*)))] $fallback +} + +// Keep entries sorted alphabetically and mutually exclusive. + +cfg_unordered! { + +#[cfg(target_os = "aix")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "aix"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".a"; + pub const DLL_EXTENSION: &str = "a"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "android")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "android"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "cygwin")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "cygwin"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".dll"; + pub const DLL_EXTENSION: &str = "dll"; + pub const EXE_SUFFIX: &str = ".exe"; + pub const EXE_EXTENSION: &str = "exe"; +} + +#[cfg(target_os = "dragonfly")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "dragonfly"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "emscripten")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "emscripten"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ".js"; + pub const EXE_EXTENSION: &str = "js"; +} + +#[cfg(target_os = "espidf")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "espidf"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "freebsd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "freebsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "fuchsia")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "fuchsia"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "haiku")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "haiku"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "hermit")] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = "hermit"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ""; + pub const DLL_EXTENSION: &str = ""; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "horizon")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "horizon"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ".elf"; + pub const EXE_EXTENSION: &str = "elf"; +} + +#[cfg(target_os = "hurd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "hurd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "illumos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "illumos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "ios")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "ios"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "l4re")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "l4re"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "linux")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "linux"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "macos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "macos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "netbsd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "netbsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "nto")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "nto"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "nuttx")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "nuttx"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "openbsd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "openbsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "redox")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "redox"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "rtems")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "rtems"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".sgxs"; + pub const DLL_EXTENSION: &str = "sgxs"; + pub const EXE_SUFFIX: &str = ".sgxs"; + pub const EXE_EXTENSION: &str = "sgxs"; +} + +#[cfg(target_os = "solaris")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "solaris"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "solid_asp3")] +pub mod os { + pub const FAMILY: &str = "itron"; + pub const OS: &str = "solid"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "tvos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "tvos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "uefi")] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = "uefi"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ""; + pub const DLL_EXTENSION: &str = ""; + pub const EXE_SUFFIX: &str = ".efi"; + pub const EXE_EXTENSION: &str = "efi"; +} + +#[cfg(target_os = "visionos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "visionos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "vita")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "vita"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ".elf"; + pub const EXE_EXTENSION: &str = "elf"; +} + +#[cfg(target_os = "vxworks")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "vxworks"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(all(target_family = "wasm", not(any(target_os = "emscripten", target_os = "linux"))))] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".wasm"; + pub const DLL_EXTENSION: &str = "wasm"; + pub const EXE_SUFFIX: &str = ".wasm"; + pub const EXE_EXTENSION: &str = "wasm"; +} + +#[cfg(target_os = "watchos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "watchos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "windows")] +pub mod os { + pub const FAMILY: &str = "windows"; + pub const OS: &str = "windows"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".dll"; + pub const DLL_EXTENSION: &str = "dll"; + pub const EXE_SUFFIX: &str = ".exe"; + pub const EXE_EXTENSION: &str = "exe"; +} + +#[cfg(target_os = "zkvm")] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".elf"; + pub const DLL_EXTENSION: &str = "elf"; + pub const EXE_SUFFIX: &str = ".elf"; + pub const EXE_EXTENSION: &str = "elf"; +} + +// The fallback when none of the other gates match. +#[else] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ""; + pub const DLL_EXTENSION: &str = ""; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +} diff --git a/library/std/src/sys/exit_guard.rs b/library/std/src/sys/exit_guard.rs index 5a090f506661d..bd70d1782440f 100644 --- a/library/std/src/sys/exit_guard.rs +++ b/library/std/src/sys/exit_guard.rs @@ -1,14 +1,5 @@ cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { - /// pthread_t is a pointer on some platforms, - /// so we wrap it in this to impl Send + Sync. - #[derive(Clone, Copy)] - #[repr(transparent)] - struct PThread(libc::pthread_t); - // Safety: pthread_t is safe to send between threads - unsafe impl Send for PThread {} - // Safety: pthread_t is safe to share between threads - unsafe impl Sync for PThread {} /// Mitigation for /// /// On glibc, `libc::exit` has been observed to not always be thread-safe. @@ -30,28 +21,34 @@ cfg_if::cfg_if! { /// (waiting for the process to exit). #[cfg_attr(any(test, doctest), allow(dead_code))] pub(crate) fn unique_thread_exit() { - let this_thread_id = unsafe { libc::pthread_self() }; - use crate::sync::{Mutex, PoisonError}; - static EXITING_THREAD_ID: Mutex> = Mutex::new(None); - let mut exiting_thread_id = - EXITING_THREAD_ID.lock().unwrap_or_else(PoisonError::into_inner); - match *exiting_thread_id { - None => { + use crate::ffi::c_int; + use crate::ptr; + use crate::sync::atomic::AtomicPtr; + use crate::sync::atomic::Ordering::{Acquire, Relaxed}; + + static EXITING_THREAD_ID: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + + // We use the address of `errno` as a cheap and safe way to identify + // threads. As the C standard mandates that `errno` must have thread + // storage duration, we can rely on its address not changing over the + // lifetime of the thread. Additionally, accesses to `errno` are + // async-signal-safe, so this function is available in all imaginable + // circumstances. + let this_thread_id = crate::sys::os::errno_location(); + match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { + Ok(_) => { // This is the first thread to call `unique_thread_exit`, - // and this is the first time it is called. - // Set EXITING_THREAD_ID to this thread's ID and return. - *exiting_thread_id = Some(PThread(this_thread_id)); - }, - Some(exiting_thread_id) if exiting_thread_id.0 == this_thread_id => { + // and this is the first time it is called. Continue exiting. + } + Err(exiting_thread_id) if exiting_thread_id == this_thread_id => { // This is the first thread to call `unique_thread_exit`, // but this is the second time it is called. // Abort the process. core::panicking::panic_nounwind("std::process::exit called re-entrantly") } - Some(_) => { + Err(_) => { // This is not the first thread to call `unique_thread_exit`. // Pause until the process exits. - drop(exiting_thread_id); loop { // Safety: libc::pause is safe to call. unsafe { libc::pause(); } diff --git a/library/std/src/sys/fd/hermit.rs b/library/std/src/sys/fd/hermit.rs new file mode 100644 index 0000000000000..7e8ba065f1b96 --- /dev/null +++ b/library/std/src/sys/fd/hermit.rs @@ -0,0 +1,175 @@ +#![unstable(reason = "not public", issue = "none", feature = "fd")] + +use crate::cmp; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, SeekFrom}; +use crate::os::hermit::hermit_abi; +use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::{cvt, unsupported}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +const fn max_iov() -> usize { + hermit_abi::IOV_MAX +} + +#[derive(Debug)] +pub struct FileDesc { + fd: OwnedFd, +} + +impl FileDesc { + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let result = + cvt(unsafe { hermit_abi::read(self.fd.as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?; + Ok(result as usize) + } + + pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { + // SAFETY: The `read` syscall does not read from the buffer, so it is + // safe to use `&mut [MaybeUninit]`. + let result = cvt(unsafe { + hermit_abi::read( + self.fd.as_raw_fd(), + buf.as_mut().as_mut_ptr() as *mut u8, + buf.capacity(), + ) + })?; + // SAFETY: Exactly `result` bytes have been filled. + unsafe { buf.advance_unchecked(result as usize) }; + Ok(()) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let ret = cvt(unsafe { + hermit_abi::readv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut hermit_abi::iovec as *const hermit_abi::iovec, + cmp::min(bufs.len(), max_iov()), + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut me = self; + (&mut me).read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let result = + cvt(unsafe { hermit_abi::write(self.fd.as_raw_fd(), buf.as_ptr(), buf.len()) })?; + Ok(result as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let ret = cvt(unsafe { + hermit_abi::writev( + self.as_raw_fd(), + bufs.as_ptr() as *const hermit_abi::iovec, + cmp::min(bufs.len(), max_iov()), + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn 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 `lseek`. + SeekFrom::Start(off) => (hermit_abi::SEEK_SET, off as i64), + SeekFrom::End(off) => (hermit_abi::SEEK_END, off), + SeekFrom::Current(off) => (hermit_abi::SEEK_CUR, off), + }; + let n = cvt(unsafe { hermit_abi::lseek(self.as_raw_fd(), pos as isize, whence) })?; + Ok(n as u64) + } + + pub fn tell(&self) -> io::Result { + self.seek(SeekFrom::Current(0)) + } + + pub fn duplicate(&self) -> io::Result { + self.duplicate_path(&[]) + } + + pub fn duplicate_path(&self, _path: &[u8]) -> io::Result { + unsupported() + } + + pub fn nonblocking(&self) -> io::Result { + Ok(false) + } + + pub fn set_cloexec(&self) -> io::Result<()> { + unsupported() + } + + pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> { + unsupported() + } + + pub fn fstat(&self, stat: *mut hermit_abi::stat) -> io::Result<()> { + cvt(unsafe { hermit_abi::fstat(self.fd.as_raw_fd(), stat) })?; + Ok(()) + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } +} + +impl IntoInner for FileDesc { + fn into_inner(self) -> OwnedFd { + self.fd + } +} + +impl FromInner for FileDesc { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self { fd: owned_fd } + } +} + +impl FromRawFd for FileDesc { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) }; + Self { fd } + } +} + +impl AsInner for FileDesc { + #[inline] + fn as_inner(&self) -> &OwnedFd { + &self.fd + } +} + +impl AsFd for FileDesc { + fn as_fd(&self) -> BorrowedFd<'_> { + self.fd.as_fd() + } +} + +impl AsRawFd for FileDesc { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} + +impl IntoRawFd for FileDesc { + fn into_raw_fd(self) -> RawFd { + self.fd.into_raw_fd() + } +} diff --git a/library/std/src/sys/fd/mod.rs b/library/std/src/sys/fd/mod.rs new file mode 100644 index 0000000000000..e0f5eab69514e --- /dev/null +++ b/library/std/src/sys/fd/mod.rs @@ -0,0 +1,19 @@ +//! Platform-dependent file descriptor abstraction. + +#![forbid(unsafe_op_in_unsafe_fn)] + +cfg_if::cfg_if! { + if #[cfg(target_family = "unix")] { + mod unix; + pub use unix::*; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + pub use sgx::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::*; + } +} diff --git a/library/std/src/sys/fd/sgx.rs b/library/std/src/sys/fd/sgx.rs new file mode 100644 index 0000000000000..1ef768db64c7f --- /dev/null +++ b/library/std/src/sys/fd/sgx.rs @@ -0,0 +1,85 @@ +use fortanix_sgx_abi::Fd; + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::mem::ManuallyDrop; +use crate::sys::pal::abi::usercalls; +use crate::sys::{AsInner, FromInner, IntoInner}; + +#[derive(Debug)] +pub struct FileDesc { + fd: Fd, +} + +impl FileDesc { + pub fn new(fd: Fd) -> FileDesc { + FileDesc { fd } + } + + pub fn raw(&self) -> Fd { + self.fd + } + + /// Extracts the actual file descriptor without closing it. + pub fn into_raw(self) -> Fd { + ManuallyDrop::new(self).fd + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + usercalls::read(self.fd, &mut [IoSliceMut::new(buf)]) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + usercalls::read_buf(self.fd, 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 { + #[inline] + fn as_inner(&self) -> &Fd { + &self.fd + } +} + +impl IntoInner for FileDesc { + fn into_inner(self) -> Fd { + ManuallyDrop::new(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/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs new file mode 100644 index 0000000000000..cdca73cdca11e --- /dev/null +++ b/library/std/src/sys/fd/unix.rs @@ -0,0 +1,677 @@ +#![unstable(reason = "not public", issue = "none", feature = "fd")] + +#[cfg(test)] +mod tests; + +#[cfg(not(any( + target_os = "linux", + target_os = "l4re", + target_os = "android", + target_os = "hurd", +)))] +use libc::off_t as off64_t; +#[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "l4re", + target_os = "hurd", +))] +use libc::off64_t; + +use crate::cmp; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::cvt; +#[cfg(all(target_os = "android", target_pointer_width = "64"))] +use crate::sys::pal::weak::syscall; +#[cfg(any(all(target_os = "android", target_pointer_width = "32"), target_vendor = "apple"))] +use crate::sys::pal::weak::weak; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +#[derive(Debug)] +pub struct FileDesc(OwnedFd); + +// 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 Apple targets 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. +const READ_LIMIT: usize = if cfg!(target_vendor = "apple") { + libc::c_int::MAX as usize - 1 +} else { + libc::ssize_t::MAX as usize +}; + +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_vendor = "apple", + target_os = "cygwin", +))] +const fn max_iov() -> usize { + libc::IOV_MAX as usize +} + +#[cfg(any( + target_os = "android", + target_os = "emscripten", + target_os = "linux", + target_os = "nto", +))] +const fn max_iov() -> usize { + libc::UIO_MAXIOV as usize +} + +#[cfg(not(any( + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "espidf", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "nuttx", + target_os = "nto", + target_os = "openbsd", + target_os = "horizon", + target_os = "vita", + target_vendor = "apple", + target_os = "cygwin", +)))] +const fn max_iov() -> usize { + 16 // The minimum value required by POSIX. +} + +impl FileDesc { + #[inline] + pub fn try_clone(&self) -> io::Result { + self.duplicate() + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let ret = cvt(unsafe { + libc::read( + self.as_raw_fd(), + buf.as_mut_ptr() as *mut libc::c_void, + cmp::min(buf.len(), READ_LIMIT), + ) + })?; + Ok(ret as usize) + } + + #[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + )))] + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let ret = cvt(unsafe { + libc::readv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + ) + })?; + Ok(ret as usize) + } + + #[cfg(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))] + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + io::default_read_vectored(|b| self.read(b), bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + cfg!(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))) + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut me = self; + (&mut me).read_to_end(buf) + } + + #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + #[cfg(not(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd" + )))] + use libc::pread as pread64; + #[cfg(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd" + ))] + use libc::pread64; + + unsafe { + cvt(pread64( + self.as_raw_fd(), + buf.as_mut_ptr() as *mut libc::c_void, + cmp::min(buf.len(), READ_LIMIT), + offset as off64_t, + )) + .map(|n| n as usize) + } + } + + pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + let ret = cvt(unsafe { + libc::read( + self.as_raw_fd(), + cursor.as_mut().as_mut_ptr() as *mut libc::c_void, + cmp::min(cursor.capacity(), READ_LIMIT), + ) + })?; + + // Safety: `ret` bytes were written to the initialized portion of the buffer + unsafe { + cursor.advance_unchecked(ret as usize); + } + Ok(()) + } + + #[cfg(any( + target_os = "aix", + target_os = "dragonfly", // DragonFly 1.5 + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", // OpenBSD 2.7 + ))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + let ret = cvt(unsafe { + libc::preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + #[cfg(not(any( + target_os = "aix", + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_vendor = "apple", + )))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + io::default_read_vectored(|b| self.read_at(b, offset), bufs) + } + + // We support some old Android versions that do not have `preadv` in libc, + // so we use weak linkage and fallback to a direct syscall if not available. + // + // On 32-bit targets, we don't want to deal with weird ABI issues around + // passing 64-bits parameters to syscalls, so we fallback to the default + // implementation if `preadv` is not available. + #[cfg(all(target_os = "android", target_pointer_width = "64"))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + syscall!( + fn preadv( + fd: libc::c_int, + iovec: *const libc::iovec, + n_iovec: libc::c_int, + offset: off64_t, + ) -> isize; + ); + + let ret = cvt(unsafe { + preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + #[cfg(all(target_os = "android", target_pointer_width = "32"))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + weak!( + fn preadv64( + fd: libc::c_int, + iovec: *const libc::iovec, + n_iovec: libc::c_int, + offset: off64_t, + ) -> isize; + ); + + match preadv64.get() { + Some(preadv) => { + let ret = cvt(unsafe { + preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_read_vectored(|b| self.read_at(b, offset), bufs), + } + } + + // We support old MacOS, iOS, watchOS, tvOS and visionOS. `preadv` was added in the following + // Apple OS versions: + // ios 14.0 + // tvos 14.0 + // macos 11.0 + // watchos 7.0 + // + // These versions may be newer than the minimum supported versions of OS's we support so we must + // use "weak" linking. + #[cfg(target_vendor = "apple")] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + weak!( + fn preadv( + fd: libc::c_int, + iovec: *const libc::iovec, + n_iovec: libc::c_int, + offset: off64_t, + ) -> isize; + ); + + match preadv.get() { + Some(preadv) => { + let ret = cvt(unsafe { + preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_read_vectored(|b| self.read_at(b, offset), bufs), + } + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let ret = cvt(unsafe { + libc::write( + self.as_raw_fd(), + buf.as_ptr() as *const libc::c_void, + cmp::min(buf.len(), READ_LIMIT), + ) + })?; + Ok(ret as usize) + } + + #[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + )))] + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let ret = cvt(unsafe { + libc::writev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + ) + })?; + Ok(ret as usize) + } + + #[cfg(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))] + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + io::default_write_vectored(|b| self.write(b), bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + cfg!(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))) + } + + #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + #[cfg(not(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd" + )))] + use libc::pwrite as pwrite64; + #[cfg(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd" + ))] + use libc::pwrite64; + + unsafe { + cvt(pwrite64( + self.as_raw_fd(), + buf.as_ptr() as *const libc::c_void, + cmp::min(buf.len(), READ_LIMIT), + offset as off64_t, + )) + .map(|n| n as usize) + } + } + + #[cfg(any( + target_os = "aix", + target_os = "dragonfly", // DragonFly 1.5 + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", // OpenBSD 2.7 + ))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + let ret = cvt(unsafe { + libc::pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + #[cfg(not(any( + target_os = "aix", + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_vendor = "apple", + )))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + io::default_write_vectored(|b| self.write_at(b, offset), bufs) + } + + // We support some old Android versions that do not have `pwritev` in libc, + // so we use weak linkage and fallback to a direct syscall if not available. + // + // On 32-bit targets, we don't want to deal with weird ABI issues around + // passing 64-bits parameters to syscalls, so we fallback to the default + // implementation if `pwritev` is not available. + #[cfg(all(target_os = "android", target_pointer_width = "64"))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + syscall!( + fn pwritev( + fd: libc::c_int, + iovec: *const libc::iovec, + n_iovec: libc::c_int, + offset: off64_t, + ) -> isize; + ); + + let ret = cvt(unsafe { + pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + #[cfg(all(target_os = "android", target_pointer_width = "32"))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + weak!( + fn pwritev64( + fd: libc::c_int, + iovec: *const libc::iovec, + n_iovec: libc::c_int, + offset: off64_t, + ) -> isize; + ); + + match pwritev64.get() { + Some(pwritev) => { + let ret = cvt(unsafe { + pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_write_vectored(|b| self.write_at(b, offset), bufs), + } + } + + // We support old MacOS, iOS, watchOS, tvOS and visionOS. `pwritev` was added in the following + // Apple OS versions: + // ios 14.0 + // tvos 14.0 + // macos 11.0 + // watchos 7.0 + // + // These versions may be newer than the minimum supported versions of OS's we support so we must + // use "weak" linking. + #[cfg(target_vendor = "apple")] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + weak!( + fn pwritev( + fd: libc::c_int, + iovec: *const libc::iovec, + n_iovec: libc::c_int, + offset: off64_t, + ) -> isize; + ); + + match pwritev.get() { + Some(pwritev) => { + let ret = cvt(unsafe { + pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_write_vectored(|b| self.write_at(b, offset), bufs), + } + } + + #[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 = "cygwin", + target_os = "haiku", + target_os = "redox", + target_os = "vxworks", + target_os = "nto", + )))] + pub fn set_cloexec(&self) -> io::Result<()> { + unsafe { + cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?; + Ok(()) + } + } + #[cfg(any( + all( + target_env = "newlib", + not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")) + ), + target_os = "solaris", + target_os = "illumos", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re", + target_os = "linux", + target_os = "cygwin", + target_os = "haiku", + target_os = "redox", + target_os = "vxworks", + target_os = "nto", + ))] + pub fn set_cloexec(&self) -> io::Result<()> { + unsafe { + let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?; + let new = previous | libc::FD_CLOEXEC; + if new != previous { + cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?; + } + Ok(()) + } + } + #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] + pub fn set_cloexec(&self) -> io::Result<()> { + // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS and Vita but there's no need to, + // because none of them supports spawning processes. + Ok(()) + } + + #[cfg(target_os = "linux")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + unsafe { + let v = nonblocking as libc::c_int; + cvt(libc::ioctl(self.as_raw_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.as_raw_fd(), libc::F_GETFL))?; + let new = if nonblocking { + previous | libc::O_NONBLOCK + } else { + previous & !libc::O_NONBLOCK + }; + if new != previous { + cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?; + } + Ok(()) + } + } + + #[inline] + pub fn duplicate(&self) -> io::Result { + Ok(Self(self.0.try_clone()?)) + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf(cursor) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + (**self).read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + (**self).is_read_vectored() + } +} + +impl AsInner for FileDesc { + #[inline] + fn as_inner(&self) -> &OwnedFd { + &self.0 + } +} + +impl IntoInner for FileDesc { + fn into_inner(self) -> OwnedFd { + self.0 + } +} + +impl FromInner for FileDesc { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self(owned_fd) + } +} + +impl AsFd for FileDesc { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for FileDesc { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for FileDesc { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for FileDesc { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(unsafe { FromRawFd::from_raw_fd(raw_fd) }) + } +} diff --git a/library/std/src/sys/fd/unix/tests.rs b/library/std/src/sys/fd/unix/tests.rs new file mode 100644 index 0000000000000..fcd66c71707d9 --- /dev/null +++ b/library/std/src/sys/fd/unix/tests.rs @@ -0,0 +1,12 @@ +use core::mem::ManuallyDrop; + +use super::FileDesc; +use crate::io::IoSlice; +use crate::os::unix::io::FromRawFd; + +#[test] +fn limit_vector_count() { + let stdout = ManuallyDrop::new(unsafe { FileDesc::from_raw_fd(1) }); + let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::>(); + assert!(stdout.write_vectored(&bufs).is_ok()); +} diff --git a/library/std/src/sys/fd/wasi.rs b/library/std/src/sys/fd/wasi.rs new file mode 100644 index 0000000000000..80a5143ff0b00 --- /dev/null +++ b/library/std/src/sys/fd/wasi.rs @@ -0,0 +1,331 @@ +#![expect(dead_code)] + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem; +use crate::net::Shutdown; +use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::pal::err2io; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; + +#[derive(Debug)] +pub struct WasiFd { + fd: OwnedFd, +} + +fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] { + assert_eq!(size_of::>(), size_of::()); + assert_eq!(align_of::>(), align_of::()); + // SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout. + // We decorate our `IoSliceMut` with `repr(transparent)` (see `io.rs`), and + // `crate::io::IoSliceMut` is a `repr(transparent)` wrapper around our type, so this is + // guaranteed. + unsafe { mem::transmute(a) } +} + +fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] { + assert_eq!(size_of::>(), size_of::()); + assert_eq!(align_of::>(), align_of::()); + // SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout. + // We decorate our `IoSlice` with `repr(transparent)` (see `io.rs`), and + // `crate::io::IoSlice` is a `repr(transparent)` wrapper around our type, so this is + // guaranteed. + unsafe { mem::transmute(a) } +} + +impl WasiFd { + pub fn datasync(&self) -> io::Result<()> { + unsafe { wasi::fd_datasync(self.as_raw_fd() as wasi::Fd).map_err(err2io) } + } + + pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + unsafe { wasi::fd_pread(self.as_raw_fd() as wasi::Fd, iovec(bufs), offset).map_err(err2io) } + } + + pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + unsafe { + wasi::fd_pwrite(self.as_raw_fd() as wasi::Fd, ciovec(bufs), offset).map_err(err2io) + } + } + + pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) } + } + + pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { + unsafe { + let bufs = [wasi::Iovec { + buf: buf.as_mut().as_mut_ptr() as *mut u8, + buf_len: buf.capacity(), + }]; + match wasi::fd_read(self.as_raw_fd() as wasi::Fd, &bufs) { + Ok(n) => { + buf.advance_unchecked(n); + Ok(()) + } + Err(e) => Err(err2io(e)), + } + } + } + + pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { wasi::fd_write(self.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, offset, whence).map_err(err2io) } + } + + pub fn tell(&self) -> io::Result { + unsafe { wasi::fd_tell(self.as_raw_fd() as wasi::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.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, base, inheriting) + .map_err(err2io) + } + } + + pub fn sync(&self) -> io::Result<()> { + unsafe { wasi::fd_sync(self.as_raw_fd() as wasi::Fd).map_err(err2io) } + } + + pub(crate) fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> { + unsafe { + wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io) + } + } + + pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { + unsafe { wasi::fd_allocate(self.as_raw_fd() as wasi::Fd, offset, len).map_err(err2io) } + } + + pub fn create_directory(&self, path: &str) -> io::Result<()> { + unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, + old_flags, + old_path, + new_fd.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, + dirflags, + path, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + ) + .map(|fd| WasiFd::from_raw_fd(fd as RawFd)) + .map_err(err2io) + } + } + + pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result { + unsafe { + wasi::fd_readdir(self.as_raw_fd() as wasi::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.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, + old_path, + new_fd.as_raw_fd() as wasi::Fd, + new_path, + ) + .map_err(err2io) + } + } + + pub(crate) fn filestat_get(&self) -> io::Result { + unsafe { wasi::fd_filestat_get(self.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, atim, mtim, fstflags) + .map_err(err2io) + } + } + + pub fn filestat_set_size(&self, size: u64) -> io::Result<()> { + unsafe { wasi::fd_filestat_set_size(self.as_raw_fd() as wasi::Fd, size).map_err(err2io) } + } + + pub(crate) fn path_filestat_get( + &self, + flags: wasi::Lookupflags, + path: &str, + ) -> io::Result { + unsafe { + wasi::path_filestat_get(self.as_raw_fd() as wasi::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.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, new_path).map_err(err2io) + } + } + + pub fn unlink_file(&self, path: &str) -> io::Result<()> { + unsafe { wasi::path_unlink_file(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } + } + + pub fn remove_directory(&self, path: &str) -> io::Result<()> { + unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } + } + + pub fn sock_accept(&self, flags: wasi::Fdflags) -> io::Result { + unsafe { wasi::sock_accept(self.as_raw_fd() as wasi::Fd, flags).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.as_raw_fd() as wasi::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.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, how).map_err(err2io) } + } +} + +impl AsInner for WasiFd { + #[inline] + fn as_inner(&self) -> &OwnedFd { + &self.fd + } +} + +impl AsInnerMut for WasiFd { + #[inline] + fn as_inner_mut(&mut self) -> &mut OwnedFd { + &mut self.fd + } +} + +impl IntoInner for WasiFd { + fn into_inner(self) -> OwnedFd { + self.fd + } +} + +impl FromInner for WasiFd { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self { fd: owned_fd } + } +} + +impl AsFd for WasiFd { + fn as_fd(&self) -> BorrowedFd<'_> { + self.fd.as_fd() + } +} + +impl AsRawFd for WasiFd { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} + +impl IntoRawFd for WasiFd { + fn into_raw_fd(self) -> RawFd { + self.fd.into_raw_fd() + } +} + +impl FromRawFd for WasiFd { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } } + } +} diff --git a/library/std/src/sys_common/fs.rs b/library/std/src/sys/fs/common.rs similarity index 100% rename from library/std/src/sys_common/fs.rs rename to library/std/src/sys/fs/common.rs diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs new file mode 100644 index 0000000000000..a9774bef9e338 --- /dev/null +++ b/library/std/src/sys/fs/hermit.rs @@ -0,0 +1,603 @@ +use crate::ffi::{CStr, OsStr, OsString, c_char}; +use crate::fs::TryLockError; +use crate::io::{self, BorrowedCursor, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; +use crate::os::hermit::ffi::OsStringExt; +use crate::os::hermit::hermit_abi::{ + self, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, O_DIRECTORY, O_EXCL, O_RDONLY, + O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, dirent64, stat as stat_struct, +}; +use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::path::{Path, PathBuf}; +use crate::sync::Arc; +use crate::sys::common::small_c_string::run_path_with_cstr; +use crate::sys::fd::FileDesc; +pub use crate::sys::fs::common::{copy, exists}; +use crate::sys::time::SystemTime; +use crate::sys::{cvt, unsupported, unsupported_err}; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::{fmt, mem}; + +#[derive(Debug)] +pub struct File(FileDesc); +#[derive(Clone)] +pub struct FileAttr { + stat_val: stat_struct, +} + +impl FileAttr { + fn from_stat(stat_val: stat_struct) -> Self { + Self { stat_val } + } +} + +// all DirEntry's will have a reference to this struct +struct InnerReadDir { + root: PathBuf, + dir: Vec, +} + +impl InnerReadDir { + pub fn new(root: PathBuf, dir: Vec) -> Self { + Self { root, dir } + } +} + +pub struct ReadDir { + inner: Arc, + pos: usize, +} + +impl ReadDir { + fn new(inner: InnerReadDir) -> Self { + Self { inner: Arc::new(inner), pos: 0 } + } +} + +pub struct DirEntry { + /// path to the entry + root: PathBuf, + /// 64-bit inode number + ino: u64, + /// File type + type_: u8, + /// name of the entry + name: OsString, +} + +#[derive(Clone, Debug)] +pub struct OpenOptions { + // generic + read: bool, + write: bool, + append: bool, + truncate: bool, + create: bool, + create_new: bool, + // system-specific + mode: i32, +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes {} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + mode: u32, +} + +#[derive(Copy, Clone, Eq, Debug)] +pub struct FileType { + mode: u8, +} + +impl PartialEq for FileType { + fn eq(&self, other: &Self) -> bool { + self.mode == other.mode + } +} + +impl core::hash::Hash for FileType { + fn hash(&self, state: &mut H) { + self.mode.hash(state); + } +} + +#[derive(Debug)] +pub struct DirBuilder { + mode: u32, +} + +impl FileAttr { + pub fn modified(&self) -> io::Result { + Ok(SystemTime::new(self.stat_val.st_mtim.tv_sec, self.stat_val.st_mtim.tv_nsec)) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::new(self.stat_val.st_atim.tv_sec, self.stat_val.st_atim.tv_nsec)) + } + + pub fn created(&self) -> io::Result { + Ok(SystemTime::new(self.stat_val.st_ctim.tv_sec, self.stat_val.st_ctim.tv_nsec)) + } + + pub fn size(&self) -> u64 { + self.stat_val.st_size as u64 + } + + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: self.stat_val.st_mode } + } + + pub fn file_type(&self) -> FileType { + let masked_mode = self.stat_val.st_mode & S_IFMT; + let mode = match masked_mode { + S_IFDIR => DT_DIR, + S_IFLNK => DT_LNK, + S_IFREG => DT_REG, + _ => DT_UNKNOWN, + }; + FileType { mode } + } +} + +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) { + unimplemented!() + } + + #[allow(dead_code)] + pub fn mode(&self) -> u32 { + self.mode as u32 + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, _t: SystemTime) {} + pub fn set_modified(&mut self, _t: SystemTime) {} +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.mode == DT_DIR + } + pub fn is_file(&self) -> bool { + self.mode == DT_REG + } + pub fn is_symlink(&self) -> bool { + self.mode == DT_LNK + } +} + +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; + + fn next(&mut self) -> Option> { + let mut counter: usize = 0; + let mut offset: usize = 0; + + // loop over all directory entries and search the entry for the current position + loop { + // leave function, if the loop reaches the of the buffer (with all entries) + if offset >= self.inner.dir.len() { + return None; + } + + let dir = unsafe { &*(self.inner.dir.as_ptr().add(offset) as *const dirent64) }; + + if counter == self.pos { + self.pos += 1; + + // After dirent64, the file name is stored. d_reclen represents the length of the dirent64 + // plus the length of the file name. Consequently, file name has a size of d_reclen minus + // the size of dirent64. The file name is always a C string and terminated by `\0`. + // Consequently, we are able to ignore the last byte. + let name_bytes = + unsafe { CStr::from_ptr(&dir.d_name as *const _ as *const c_char).to_bytes() }; + let entry = DirEntry { + root: self.inner.root.clone(), + ino: dir.d_ino, + type_: dir.d_type, + name: OsString::from_vec(name_bytes.to_vec()), + }; + + return Some(Ok(entry)); + } + + counter += 1; + + // move to the next dirent64, which is directly stored after the previous one + offset = offset + usize::from(dir.d_reclen); + } + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.root.join(self.file_name_os_str()) + } + + pub fn file_name(&self) -> OsString { + self.file_name_os_str().to_os_string() + } + + pub fn metadata(&self) -> io::Result { + let mut path = self.path(); + path.set_file_name(self.file_name_os_str()); + lstat(&path) + } + + pub fn file_type(&self) -> io::Result { + Ok(FileType { mode: self.type_ }) + } + + #[allow(dead_code)] + pub fn ino(&self) -> u64 { + self.ino + } + + pub fn file_name_os_str(&self) -> &OsStr { + self.name.as_os_str() + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { + // generic + read: false, + write: false, + append: false, + truncate: false, + create: false, + create_new: false, + // system-specific + mode: 0o777, + } + } + + 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; + } + + fn get_access_mode(&self) -> io::Result { + match (self.read, self.write, self.append) { + (true, false, false) => Ok(O_RDONLY), + (false, true, false) => Ok(O_WRONLY), + (true, true, false) => Ok(O_RDWR), + (false, _, true) => Ok(O_WRONLY | O_APPEND), + (true, _, true) => Ok(O_RDWR | O_APPEND), + (false, false, false) => { + Err(io::const_error!(ErrorKind::InvalidInput, "invalid access mode")) + } + } + } + + 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(io::const_error!(ErrorKind::InvalidInput, "invalid creation mode")); + } + } + (_, true) => { + if self.truncate && !self.create_new { + return Err(io::const_error!(ErrorKind::InvalidInput, "invalid creation mode")); + } + } + } + + Ok(match (self.create, self.truncate, self.create_new) { + (false, false, false) => 0, + (true, false, false) => O_CREAT, + (false, true, false) => O_TRUNC, + (true, true, false) => O_CREAT | O_TRUNC, + (_, _, true) => O_CREAT | O_EXCL, + }) + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + run_path_with_cstr(path, &|path| File::open_c(&path, opts)) + } + + pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { + let mut flags = opts.get_access_mode()?; + flags = flags | opts.get_creation_mode()?; + + let mode; + if flags & O_CREAT == O_CREAT { + mode = opts.mode; + } else { + mode = 0; + } + + let fd = unsafe { cvt(hermit_abi::open(path.as_ptr(), flags, mode))? }; + Ok(File(unsafe { FileDesc::from_raw_fd(fd as i32) })) + } + + pub fn file_attr(&self) -> io::Result { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + self.0.fstat(&mut stat_val)?; + Ok(FileAttr::from_stat(stat_val)) + } + + pub fn fsync(&self) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) + } + + pub fn datasync(&self) -> io::Result<()> { + self.fsync() + } + + pub fn lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) + } + + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) + } + + 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_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(cursor) + } + + 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() + } + + #[inline] + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + self.0.seek(pos) + } + + pub fn tell(&self) -> io::Result { + self.0.tell() + } + + pub fn duplicate(&self) -> io::Result { + Err(Error::from_raw_os_error(22)) + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { mode: 0o777 } + } + + pub fn mkdir(&self, path: &Path) -> io::Result<()> { + run_path_with_cstr(path, &|path| { + cvt(unsafe { hermit_abi::mkdir(path.as_ptr().cast(), self.mode.into()) }).map(|_| ()) + }) + } + + #[allow(dead_code)] + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode; + } +} + +impl AsInner for File { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl AsInnerMut for File { + #[inline] + fn as_inner_mut(&mut self) -> &mut FileDesc { + &mut self.0 + } +} + +impl IntoInner for File { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for File { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for File { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for File { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for File { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for File { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + let file_desc = unsafe { FileDesc::from_raw_fd(raw_fd) }; + Self(file_desc) + } +} + +pub fn readdir(path: &Path) -> io::Result { + let fd_raw = run_path_with_cstr(path, &|path| { + cvt(unsafe { hermit_abi::open(path.as_ptr(), O_RDONLY | O_DIRECTORY, 0) }) + })?; + let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; + let root = path.to_path_buf(); + + // read all director entries + let mut vec: Vec = Vec::new(); + let mut sz = 512; + loop { + // reserve memory to receive all directory entries + vec.resize(sz, 0); + + let readlen = unsafe { + hermit_abi::getdents64(fd.as_raw_fd(), vec.as_mut_ptr() as *mut dirent64, sz) + }; + if readlen > 0 { + // shrink down to the minimal size + vec.resize(readlen.try_into().unwrap(), 0); + break; + } + + // if the buffer is too small, getdents64 returns EINVAL + // otherwise, getdents64 returns an error number + if readlen != (-hermit_abi::errno::EINVAL).into() { + return Err(Error::from_raw_os_error(readlen.try_into().unwrap())); + } + + // we don't have enough memory => try to increase the vector size + sz = sz * 2; + + // 1 MB for directory entries should be enough + // stop here to avoid an endless loop + if sz > 0x100000 { + return Err(Error::from(ErrorKind::Uncategorized)); + } + } + + Ok(ReadDir::new(InnerReadDir::new(root, vec))) +} + +pub fn unlink(path: &Path) -> io::Result<()> { + run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::unlink(path.as_ptr()) }).map(|_| ())) +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + unsupported() +} + +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) +} + +pub fn rmdir(path: &Path) -> io::Result<()> { + run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::rmdir(path.as_ptr()) }).map(|_| ())) +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(path: &Path) -> io::Result { + run_path_with_cstr(path, &|path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { hermit_abi::stat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) +} + +pub fn lstat(path: &Path) -> io::Result { + run_path_with_cstr(path, &|path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { hermit_abi::lstat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs new file mode 100644 index 0000000000000..d55e28074fe8c --- /dev/null +++ b/library/std/src/sys/fs/mod.rs @@ -0,0 +1,129 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::io; +use crate::path::{Path, PathBuf}; + +pub mod common; + +cfg_if::cfg_if! { + if #[cfg(target_family = "unix")] { + mod unix; + use unix as imp; + pub use unix::{chown, fchown, lchown, mkfifo}; + #[cfg(not(target_os = "fuchsia"))] + pub use unix::chroot; + pub(crate) use unix::debug_assert_fd_is_open; + #[cfg(any(target_os = "linux", target_os = "android"))] + pub(crate) use unix::CachedFileMetadata; + use crate::sys::common::small_c_string::run_path_with_cstr as with_native_path; + } else if #[cfg(target_os = "windows")] { + mod windows; + use windows as imp; + pub use windows::{symlink_inner, junction_point}; + use crate::sys::path::with_native_path; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + use hermit as imp; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + use solid as imp; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + use uefi as imp; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + use wasi as imp; + } else { + mod unsupported; + use unsupported as imp; + } +} + +// FIXME: Replace this with platform-specific path conversion functions. +#[cfg(not(any(target_family = "unix", target_os = "windows")))] +#[inline] +pub fn with_native_path(path: &Path, f: &dyn Fn(&Path) -> io::Result) -> io::Result { + f(path) +} + +pub use imp::{ + DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions, + ReadDir, +}; + +pub fn read_dir(path: &Path) -> io::Result { + // FIXME: use with_native_path on all platforms + imp::readdir(path) +} + +pub fn remove_file(path: &Path) -> io::Result<()> { + with_native_path(path, &imp::unlink) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + with_native_path(old, &|old| with_native_path(new, &|new| imp::rename(old, new))) +} + +pub fn remove_dir(path: &Path) -> io::Result<()> { + with_native_path(path, &imp::rmdir) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + // FIXME: use with_native_path on all platforms + #[cfg(not(windows))] + return imp::remove_dir_all(path); + #[cfg(windows)] + with_native_path(path, &imp::remove_dir_all) +} + +pub fn read_link(path: &Path) -> io::Result { + with_native_path(path, &imp::readlink) +} + +pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { + // FIXME: use with_native_path on all platforms + #[cfg(windows)] + return imp::symlink(original, link); + #[cfg(not(windows))] + with_native_path(original, &|original| { + with_native_path(link, &|link| imp::symlink(original, link)) + }) +} + +pub fn hard_link(original: &Path, link: &Path) -> io::Result<()> { + with_native_path(original, &|original| { + with_native_path(link, &|link| imp::link(original, link)) + }) +} + +pub fn metadata(path: &Path) -> io::Result { + with_native_path(path, &imp::stat) +} + +pub fn symlink_metadata(path: &Path) -> io::Result { + with_native_path(path, &imp::lstat) +} + +pub fn set_permissions(path: &Path, perm: FilePermissions) -> io::Result<()> { + with_native_path(path, &|path| imp::set_perm(path, perm.clone())) +} + +pub fn canonicalize(path: &Path) -> io::Result { + with_native_path(path, &imp::canonicalize) +} + +pub fn copy(from: &Path, to: &Path) -> io::Result { + // FIXME: use with_native_path on all platforms + #[cfg(not(windows))] + return imp::copy(from, to); + #[cfg(windows)] + with_native_path(from, &|from| with_native_path(to, &|to| imp::copy(from, to))) +} + +pub fn exists(path: &Path) -> io::Result { + // FIXME: use with_native_path on all platforms + #[cfg(not(windows))] + return imp::exists(path); + #[cfg(windows)] + with_native_path(path, &imp::exists) +} diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs new file mode 100644 index 0000000000000..3bfb39bac95bc --- /dev/null +++ b/library/std/src/sys/fs/solid.rs @@ -0,0 +1,612 @@ +#![allow(dead_code)] + +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::fs::TryLockError; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem::MaybeUninit; +use crate::os::raw::{c_int, c_short}; +use crate::os::solid::ffi::OsStrExt; +use crate::path::{Path, PathBuf}; +use crate::sync::Arc; +pub use crate::sys::fs::common::exists; +use crate::sys::pal::{abi, error}; +use crate::sys::time::SystemTime; +use crate::sys::{unsupported, unsupported_err}; +use crate::sys_common::ignore_notfound; + +type CIntNotMinusOne = core::num::niche_types::NotAllOnes; + +/// A file descriptor. +#[derive(Clone, Copy)] +struct FileDesc { + fd: CIntNotMinusOne, +} + +impl FileDesc { + #[inline] + #[track_caller] + fn new(fd: c_int) -> FileDesc { + FileDesc { fd: CIntNotMinusOne::new(fd).expect("fd != -1") } + } + + #[inline] + fn raw(&self) -> c_int { + self.fd.as_inner() + } +} + +pub struct File { + fd: FileDesc, +} + +#[derive(Clone)] +pub struct FileAttr { + stat: abi::stat, +} + +// all DirEntry's will have a reference to this struct +struct InnerReadDir { + dirp: abi::S_DIR, + root: PathBuf, +} + +pub struct ReadDir { + inner: Arc, +} + +pub struct DirEntry { + entry: abi::dirent, + inner: Arc, +} + +#[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, +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes {} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions(c_short); + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct FileType(c_short); + +#[derive(Debug)] +pub struct DirBuilder {} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.stat.st_size as u64 + } + + pub fn perm(&self) -> FilePermissions { + FilePermissions(self.stat.st_mode) + } + + pub fn file_type(&self) -> FileType { + FileType(self.stat.st_mode) + } + + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from_time_t(self.stat.st_mtime)) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from_time_t(self.stat.st_atime)) + } + + pub fn created(&self) -> io::Result { + Ok(SystemTime::from_time_t(self.stat.st_ctime)) + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + (self.0 & abi::S_IWRITE) == 0 + } + + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + self.0 &= !abi::S_IWRITE; + } else { + self.0 |= abi::S_IWRITE; + } + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, _t: SystemTime) {} + pub fn set_modified(&mut self, _t: SystemTime) {} +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.is(abi::S_IFDIR) + } + pub fn is_file(&self) -> bool { + self.is(abi::S_IFREG) + } + pub fn is_symlink(&self) -> bool { + false + } + + pub fn is(&self, mode: c_short) -> bool { + self.0 & abi::S_IFMT == mode + } +} + +pub fn readdir(p: &Path) -> io::Result { + unsafe { + let mut dir = MaybeUninit::uninit(); + error::SolidError::err_if_negative(abi::SOLID_FS_OpenDir( + cstr(p)?.as_ptr(), + dir.as_mut_ptr(), + )) + .map_err(|e| e.as_io_error())?; + let inner = Arc::new(InnerReadDir { dirp: dir.assume_init(), root: p.to_owned() }); + Ok(ReadDir { inner }) + } +} + +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; + + fn next(&mut self) -> Option> { + let entry = unsafe { + let mut out_entry = MaybeUninit::uninit(); + match error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir( + self.inner.dirp, + out_entry.as_mut_ptr(), + )) { + Ok(_) => out_entry.assume_init(), + Err(e) if e.as_raw() == abi::SOLID_ERR_NOTFOUND => return None, + Err(e) => return Some(Err(e.as_io_error())), + } + }; + + (entry.d_name[0] != 0).then(|| Ok(DirEntry { entry, inner: Arc::clone(&self.inner) })) + } +} + +impl Drop for InnerReadDir { + fn drop(&mut self) { + unsafe { abi::SOLID_FS_CloseDir(self.dirp) }; + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.inner.root.join(OsStr::from_bytes( + unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes(), + )) + } + + pub fn file_name(&self) -> OsString { + OsStr::from_bytes(unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes()) + .to_os_string() + } + + pub fn metadata(&self) -> io::Result { + lstat(&self.path()) + } + + pub fn file_type(&self) -> io::Result { + match self.entry.d_type { + abi::DT_CHR => Ok(FileType(abi::S_IFCHR)), + abi::DT_FIFO => Ok(FileType(abi::S_IFIFO)), + abi::DT_REG => Ok(FileType(abi::S_IFREG)), + abi::DT_DIR => Ok(FileType(abi::S_IFDIR)), + abi::DT_BLK => Ok(FileType(abi::S_IFBLK)), + _ => lstat(&self.path()).map(|m| m.file_type()), + } + } +} + +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, + } + } + + 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) {} + + fn get_access_mode(&self) -> io::Result { + match (self.read, self.write, self.append) { + (true, false, false) => Ok(abi::O_RDONLY), + (false, true, false) => Ok(abi::O_WRONLY), + (true, true, false) => Ok(abi::O_RDWR), + (false, _, true) => Ok(abi::O_WRONLY | abi::O_APPEND), + (true, _, true) => Ok(abi::O_RDWR | abi::O_APPEND), + (false, false, false) => Err(io::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(io::Error::from_raw_os_error(libc::EINVAL)); + } + } + (_, true) => { + if self.truncate && !self.create_new { + return Err(io::Error::from_raw_os_error(libc::EINVAL)); + } + } + } + + Ok(match (self.create, self.truncate, self.create_new) { + (false, false, false) => 0, + (true, false, false) => abi::O_CREAT, + (false, true, false) => abi::O_TRUNC, + (true, true, false) => abi::O_CREAT | abi::O_TRUNC, + (_, _, true) => abi::O_CREAT | abi::O_EXCL, + }) + } +} + +fn cstr(path: &Path) -> io::Result { + let path = path.as_os_str().as_bytes(); + + if !path.starts_with(br"\") { + // Relative paths aren't supported + return Err(crate::io::const_error!( + crate::io::ErrorKind::Unsupported, + "relative path is not supported on this platform", + )); + } + + // Apply the thread-safety wrapper + const SAFE_PREFIX: &[u8] = br"\TS"; + let wrapped_path = [SAFE_PREFIX, &path, &[0]].concat(); + + CString::from_vec_with_nul(wrapped_path).map_err(|_| { + crate::io::const_error!(io::ErrorKind::InvalidInput, "path provided contains a nul byte") + }) +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + let flags = opts.get_access_mode()? + | opts.get_creation_mode()? + | (opts.custom_flags as c_int & !abi::O_ACCMODE); + unsafe { + let mut fd = MaybeUninit::uninit(); + error::SolidError::err_if_negative(abi::SOLID_FS_Open( + fd.as_mut_ptr(), + cstr(path)?.as_ptr(), + flags, + )) + .map_err(|e| e.as_io_error())?; + Ok(File { fd: FileDesc::new(fd.assume_init()) }) + } + } + + pub fn file_attr(&self) -> io::Result { + unsupported() + } + + pub fn fsync(&self) -> io::Result<()> { + self.flush() + } + + pub fn datasync(&self) -> io::Result<()> { + self.flush() + } + + pub fn lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) + } + + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + unsupported() + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + unsafe { + let mut out_num_bytes = MaybeUninit::uninit(); + error::SolidError::err_if_negative(abi::SOLID_FS_Read( + self.fd.raw(), + buf.as_mut_ptr(), + buf.len(), + out_num_bytes.as_mut_ptr(), + )) + .map_err(|e| e.as_io_error())?; + Ok(out_num_bytes.assume_init()) + } + } + + pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + unsafe { + let len = cursor.capacity(); + let mut out_num_bytes = MaybeUninit::uninit(); + error::SolidError::err_if_negative(abi::SOLID_FS_Read( + self.fd.raw(), + cursor.as_mut().as_mut_ptr() as *mut u8, + len, + out_num_bytes.as_mut_ptr(), + )) + .map_err(|e| e.as_io_error())?; + + // Safety: `out_num_bytes` is filled by the successful call to + // `SOLID_FS_Read` + let num_bytes_read = out_num_bytes.assume_init(); + + // Safety: `num_bytes_read` bytes were written to the unfilled + // portion of the buffer + cursor.advance_unchecked(num_bytes_read); + + Ok(()) + } + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + crate::io::default_read_vectored(|buf| self.read(buf), bufs) + } + + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + unsafe { + let mut out_num_bytes = MaybeUninit::uninit(); + error::SolidError::err_if_negative(abi::SOLID_FS_Write( + self.fd.raw(), + buf.as_ptr(), + buf.len(), + out_num_bytes.as_mut_ptr(), + )) + .map_err(|e| e.as_io_error())?; + Ok(out_num_bytes.assume_init()) + } + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|buf| self.write(buf), bufs) + } + + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn flush(&self) -> io::Result<()> { + error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Sync(self.fd.raw()) }) + .map_err(|e| e.as_io_error())?; + 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 `SOLID_FS_Lseek`. + SeekFrom::Start(off) => (abi::SEEK_SET, off as i64), + SeekFrom::End(off) => (abi::SEEK_END, off), + SeekFrom::Current(off) => (abi::SEEK_CUR, off), + }; + error::SolidError::err_if_negative(unsafe { + abi::SOLID_FS_Lseek(self.fd.raw(), pos, whence) + }) + .map_err(|e| e.as_io_error())?; + // Get the new offset + self.tell() + } + + pub fn tell(&self) -> io::Result { + unsafe { + let mut out_offset = MaybeUninit::uninit(); + error::SolidError::err_if_negative(abi::SOLID_FS_Ftell( + self.fd.raw(), + out_offset.as_mut_ptr(), + )) + .map_err(|e| e.as_io_error())?; + Ok(out_offset.assume_init() as u64) + } + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + unsupported() + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + unsupported() + } +} + +impl Drop for File { + fn drop(&mut self) { + unsafe { abi::SOLID_FS_Close(self.fd.raw()) }; + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder {} + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Mkdir(cstr(p)?.as_ptr()) }) + .map_err(|e| e.as_io_error())?; + Ok(()) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("File").field("fd", &self.fd.raw()).finish() + } +} + +pub fn unlink(p: &Path) -> io::Result<()> { + if stat(p)?.file_type().is_dir() { + Err(io::const_error!(io::ErrorKind::IsADirectory, "is a directory")) + } else { + error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) }) + .map_err(|e| e.as_io_error())?; + Ok(()) + } +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + error::SolidError::err_if_negative(unsafe { + abi::SOLID_FS_Rename(cstr(old)?.as_ptr(), cstr(new)?.as_ptr()) + }) + .map_err(|e| e.as_io_error())?; + Ok(()) +} + +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + error::SolidError::err_if_negative(unsafe { + abi::SOLID_FS_Chmod(cstr(p)?.as_ptr(), perm.0.into()) + }) + .map_err(|e| e.as_io_error())?; + Ok(()) +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + if stat(p)?.file_type().is_dir() { + error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) }) + .map_err(|e| e.as_io_error())?; + Ok(()) + } else { + Err(io::const_error!(io::ErrorKind::NotADirectory, "not a directory")) + } +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + for child in readdir(path)? { + let result: io::Result<()> = try { + let child = child?; + let child_type = child.file_type()?; + if child_type.is_dir() { + remove_dir_all(&child.path())?; + } else { + unlink(&child.path())?; + } + }; + // ignore internal NotFound errors + if let Err(err) = &result + && err.kind() != io::ErrorKind::NotFound + { + return result; + } + } + ignore_notfound(rmdir(path)) +} + +pub fn readlink(p: &Path) -> io::Result { + // This target doesn't support symlinks + stat(p)?; + Err(io::const_error!(io::ErrorKind::InvalidInput, "not a symbolic link")) +} + +pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { + // This target doesn't support symlinks + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + // This target doesn't support symlinks + unsupported() +} + +pub fn stat(p: &Path) -> io::Result { + // This target doesn't support symlinks + lstat(p) +} + +pub fn lstat(p: &Path) -> io::Result { + unsafe { + let mut out_stat = MaybeUninit::uninit(); + error::SolidError::err_if_negative(abi::SOLID_FS_Stat( + cstr(p)?.as_ptr(), + out_stat.as_mut_ptr(), + )) + .map_err(|e| e.as_io_error())?; + Ok(FileAttr { stat: out_stat.assume_init() }) + } +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} + +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/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs new file mode 100644 index 0000000000000..416c90b98b6d3 --- /dev/null +++ b/library/std/src/sys/fs/uefi.rs @@ -0,0 +1,532 @@ +use r_efi::protocols::file; + +use crate::ffi::OsString; +use crate::fmt; +use crate::fs::TryLockError; +use crate::hash::Hash; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::path::{Path, PathBuf}; +use crate::sys::time::SystemTime; +use crate::sys::unsupported; + +#[expect(dead_code)] +const FILE_PERMISSIONS_MASK: u64 = r_efi::protocols::file::READ_ONLY; + +pub struct File(!); + +#[derive(Clone)] +pub struct FileAttr { + attr: u64, + size: u64, +} + +pub struct ReadDir(!); + +pub struct DirEntry(!); + +#[derive(Clone, Debug)] +pub struct OpenOptions { + mode: u64, + append: bool, + truncate: bool, + create_new: bool, +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes {} + +#[derive(Clone, PartialEq, Eq, Debug)] +// Bool indicates if file is readonly +pub struct FilePermissions(bool); + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +// Bool indicates if directory +pub struct FileType(bool); + +#[derive(Debug)] +pub struct DirBuilder; + +impl FileAttr { + pub fn size(&self) -> u64 { + self.size + } + + pub fn perm(&self) -> FilePermissions { + FilePermissions::from_attr(self.attr) + } + + pub fn file_type(&self) -> FileType { + FileType::from_attr(self.attr) + } + + pub fn modified(&self) -> io::Result { + unsupported() + } + + pub fn accessed(&self) -> io::Result { + unsupported() + } + + pub fn created(&self) -> io::Result { + unsupported() + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + self.0 + } + + pub fn set_readonly(&mut self, readonly: bool) { + self.0 = readonly + } + + const fn from_attr(attr: u64) -> Self { + Self(attr & r_efi::protocols::file::READ_ONLY != 0) + } + + #[expect(dead_code)] + const fn to_attr(&self) -> u64 { + if self.0 { r_efi::protocols::file::READ_ONLY } else { 0 } + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, _t: SystemTime) {} + pub fn set_modified(&mut self, _t: SystemTime) {} +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.0 + } + + pub fn is_file(&self) -> bool { + !self.is_dir() + } + + // Symlinks are not supported in UEFI + pub fn is_symlink(&self) -> bool { + false + } + + const fn from_attr(attr: u64) -> Self { + Self(attr & r_efi::protocols::file::DIRECTORY != 0) + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + self.0 + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.0 + } + + pub fn file_name(&self) -> OsString { + self.0 + } + + pub fn metadata(&self) -> io::Result { + self.0 + } + + pub fn file_type(&self) -> io::Result { + self.0 + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { mode: 0, append: false, create_new: false, truncate: false } + } + + pub fn read(&mut self, read: bool) { + if read { + self.mode |= file::MODE_READ; + } else { + self.mode &= !file::MODE_READ; + } + } + + pub fn write(&mut self, write: bool) { + if write { + // Valid Combinations: Read, Read/Write, Read/Write/Create + self.read(true); + self.mode |= file::MODE_WRITE; + } else { + self.mode &= !file::MODE_WRITE; + } + } + + pub fn append(&mut self, append: bool) { + // Docs state that `.write(true).append(true)` has the same effect as `.append(true)` + if append { + self.write(true); + } + self.append = append; + } + + pub fn truncate(&mut self, truncate: bool) { + self.truncate = truncate; + } + + pub fn create(&mut self, create: bool) { + if create { + self.mode |= file::MODE_CREATE; + } else { + self.mode &= !file::MODE_CREATE; + } + } + + pub fn create_new(&mut self, create_new: bool) { + self.create_new = create_new; + } + + #[expect(dead_code)] + const fn is_mode_valid(&self) -> bool { + // Valid Combinations: Read, Read/Write, Read/Write/Create + self.mode == file::MODE_READ + || self.mode == (file::MODE_READ | file::MODE_WRITE) + || self.mode == (file::MODE_READ | file::MODE_WRITE | file::MODE_CREATE) + } +} + +impl File { + pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { + unsupported() + } + + pub fn file_attr(&self) -> io::Result { + self.0 + } + + pub fn fsync(&self) -> io::Result<()> { + self.0 + } + + pub fn datasync(&self) -> io::Result<()> { + self.0 + } + + pub fn lock(&self) -> io::Result<()> { + self.0 + } + + pub fn lock_shared(&self) -> io::Result<()> { + self.0 + } + + pub fn try_lock(&self) -> Result<(), TryLockError> { + self.0 + } + + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + self.0 + } + + pub fn unlock(&self) -> io::Result<()> { + self.0 + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + self.0 + } + + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn flush(&self) -> io::Result<()> { + self.0 + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result { + self.0 + } + + pub fn tell(&self) -> io::Result { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + self.0 + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + self.0 + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + uefi_fs::mkdir(p) + } +} + +impl fmt::Debug for File { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub fn readdir(_p: &Path) -> io::Result { + unsupported() +} + +pub fn unlink(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + unsupported() +} + +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + unsupported() +} + +pub fn rmdir(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn exists(path: &Path) -> io::Result { + let f = uefi_fs::File::from_path(path, r_efi::protocols::file::MODE_READ, 0); + match f { + Ok(_) => Ok(true), + Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(false), + Err(e) => Err(e), + } +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn lstat(p: &Path) -> io::Result { + stat(p) +} + +pub fn canonicalize(p: &Path) -> io::Result { + crate::path::absolute(p) +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result { + unsupported() +} + +mod uefi_fs { + use r_efi::protocols::{device_path, file, simple_file_system}; + + use crate::boxed::Box; + use crate::io; + use crate::path::Path; + use crate::ptr::NonNull; + use crate::sys::helpers; + + pub(crate) struct File(NonNull); + + impl File { + pub(crate) fn from_path(path: &Path, open_mode: u64, attr: u64) -> io::Result { + let absolute = crate::path::absolute(path)?; + + let p = helpers::OwnedDevicePath::from_text(absolute.as_os_str())?; + let (vol, mut path_remaining) = Self::open_volume_from_device_path(p.borrow())?; + + vol.open(&mut path_remaining, open_mode, attr) + } + + /// Open Filesystem volume given a devicepath to the volume, or a file/directory in the + /// volume. The path provided should be absolute UEFI device path, without any UEFI shell + /// mappings. + /// + /// Returns + /// 1. The volume as a UEFI File + /// 2. Path relative to the volume. + /// + /// For example, given "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi", + /// this will open the volume "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)" + /// and return the remaining file path "\abc\run.efi". + fn open_volume_from_device_path( + path: helpers::BorrowedDevicePath<'_>, + ) -> io::Result<(Self, Box<[u16]>)> { + let handles = match helpers::locate_handles(simple_file_system::PROTOCOL_GUID) { + Ok(x) => x, + Err(e) => return Err(e), + }; + for handle in handles { + let volume_device_path: NonNull = + match helpers::open_protocol(handle, device_path::PROTOCOL_GUID) { + Ok(x) => x, + Err(_) => continue, + }; + let volume_device_path = helpers::BorrowedDevicePath::new(volume_device_path); + + if let Some(left_path) = path_best_match(&volume_device_path, &path) { + return Ok((Self::open_volume(handle)?, left_path)); + } + } + + Err(io::const_error!(io::ErrorKind::NotFound, "Volume Not Found")) + } + + // Open volume on device_handle using SIMPLE_FILE_SYSTEM_PROTOCOL + fn open_volume(device_handle: NonNull) -> io::Result { + let simple_file_system_protocol = helpers::open_protocol::( + device_handle, + simple_file_system::PROTOCOL_GUID, + )?; + + let mut file_protocol = crate::ptr::null_mut(); + let r = unsafe { + ((*simple_file_system_protocol.as_ptr()).open_volume)( + simple_file_system_protocol.as_ptr(), + &mut file_protocol, + ) + }; + if r.is_error() { + return Err(io::Error::from_raw_os_error(r.as_usize())); + } + + // Since no error was returned, file protocol should be non-NULL. + let p = NonNull::new(file_protocol).unwrap(); + Ok(Self(p)) + } + + fn open(&self, path: &mut [u16], open_mode: u64, attr: u64) -> io::Result { + let file_ptr = self.0.as_ptr(); + let mut file_opened = crate::ptr::null_mut(); + + let r = unsafe { + ((*file_ptr).open)(file_ptr, &mut file_opened, path.as_mut_ptr(), open_mode, attr) + }; + + if r.is_error() { + return Err(io::Error::from_raw_os_error(r.as_usize())); + } + + // Since no error was returned, file protocol should be non-NULL. + let p = NonNull::new(file_opened).unwrap(); + Ok(File(p)) + } + } + + impl Drop for File { + fn drop(&mut self) { + let file_ptr = self.0.as_ptr(); + let _ = unsafe { ((*self.0.as_ptr()).close)(file_ptr) }; + } + } + + /// A helper to check that target path is a descendent of source. It is expected to be used with + /// absolute UEFI device paths without any UEFI shell mappings. + /// + /// Returns the path relative to source + /// + /// For example, given "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/" and + /// "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi", this will return + /// "\abc\run.efi" + fn path_best_match( + source: &helpers::BorrowedDevicePath<'_>, + target: &helpers::BorrowedDevicePath<'_>, + ) -> Option> { + let mut source_iter = source.iter().take_while(|x| !x.is_end_instance()); + let mut target_iter = target.iter().take_while(|x| !x.is_end_instance()); + + loop { + match (source_iter.next(), target_iter.next()) { + (Some(x), Some(y)) if x == y => continue, + (None, Some(y)) => { + let p = y.to_path().to_text().ok()?; + return helpers::os_string_to_raw(&p); + } + _ => return None, + } + } + } + + /// An implementation of mkdir to allow creating new directory without having to open the + /// volume twice (once for checking and once for creating) + pub(crate) fn mkdir(path: &Path) -> io::Result<()> { + let absolute = crate::path::absolute(path)?; + + let p = helpers::OwnedDevicePath::from_text(absolute.as_os_str())?; + let (vol, mut path_remaining) = File::open_volume_from_device_path(p.borrow())?; + + // Check if file exists + match vol.open(&mut path_remaining, file::MODE_READ, 0) { + Ok(_) => { + return Err(io::Error::new(io::ErrorKind::AlreadyExists, "Path already exists")); + } + Err(e) if e.kind() == io::ErrorKind::NotFound => {} + Err(e) => return Err(e), + } + + let _ = vol.open( + &mut path_remaining, + file::MODE_READ | file::MODE_WRITE | file::MODE_CREATE, + file::DIRECTORY, + )?; + + Ok(()) + } +} diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs new file mode 100644 index 0000000000000..a3e520fdeef43 --- /dev/null +++ b/library/std/src/sys/fs/unix.rs @@ -0,0 +1,2330 @@ +#![allow(nonstandard_style)] +#![allow(unsafe_op_in_unsafe_fn)] +// miri has some special hacks here that make things unused. +#![cfg_attr(miri, allow(unused))] + +#[cfg(test)] +mod tests; + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +use libc::c_char; +#[cfg(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", +))] +use libc::dirfd; +#[cfg(any(target_os = "fuchsia", target_os = "illumos"))] +use libc::fstatat as fstatat64; +#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] +use libc::fstatat64; +#[cfg(any( + target_os = "android", + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos", + target_os = "aix", + target_os = "nto", + target_os = "vita", + all(target_os = "linux", target_env = "musl"), +))] +use libc::readdir as readdir64; +#[cfg(not(any( + target_os = "android", + target_os = "linux", + target_os = "solaris", + target_os = "illumos", + target_os = "l4re", + target_os = "fuchsia", + target_os = "redox", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", +)))] +use libc::readdir_r as readdir64_r; +#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] +use libc::readdir64; +#[cfg(target_os = "l4re")] +use libc::readdir64_r; +use libc::{c_int, mode_t}; +#[cfg(target_os = "android")] +use libc::{ + dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64, + lstat as lstat64, off64_t, open as open64, stat as stat64, +}; +#[cfg(not(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "l4re", + target_os = "android", + target_os = "hurd", +)))] +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( + all(target_os = "linux", not(target_env = "musl")), + target_os = "l4re", + target_os = "hurd" +))] +use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64}; + +use crate::ffi::{CStr, OsStr, OsString}; +use crate::fmt::{self, Write as _}; +use crate::fs::TryLockError; +use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; +use crate::os::unix::prelude::*; +use crate::path::{Path, PathBuf}; +use crate::sync::Arc; +use crate::sys::common::small_c_string::run_path_with_cstr; +use crate::sys::fd::FileDesc; +pub use crate::sys::fs::common::exists; +use crate::sys::time::SystemTime; +#[cfg(all(target_os = "linux", target_env = "gnu"))] +use crate::sys::weak::syscall; +#[cfg(target_os = "android")] +use crate::sys::weak::weak; +use crate::sys::{cvt, cvt_r}; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::{mem, ptr}; + +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, + // With statx, we can overcome 32-bit `time_t` too. + #[cfg(target_pointer_width = "32")] + stx_atime: libc::statx_timestamp, + #[cfg(target_pointer_width = "32")] + stx_ctime: libc::statx_timestamp, + #[cfg(target_pointer_width = "32")] + stx_mtime: libc::statx_timestamp, + + } + + // We prefer `statx` on Linux if available, which contains file creation time, + // as well as 64-bit timestamps of all kinds. + // Default `stat64` contains no creation time and may have 32-bit `time_t`. + unsafe fn try_statx( + fd: c_int, + path: *const c_char, + flags: i32, + mask: u32, + ) -> Option> { + use crate::sync::atomic::{Atomic, AtomicU8, Ordering}; + + // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`. + // We check for it on first failure and remember availability to avoid having to + // do it again. + #[repr(u8)] + enum STATX_STATE{ Unknown = 0, Present, Unavailable } + static STATX_SAVED_STATE: Atomic = AtomicU8::new(STATX_STATE::Unknown as u8); + + syscall!( + fn statx( + fd: c_int, + pathname: *const c_char, + flags: c_int, + mask: libc::c_uint, + statxbuf: *mut libc::statx, + ) -> c_int; + ); + + let statx_availability = STATX_SAVED_STATE.load(Ordering::Relaxed); + if statx_availability == STATX_STATE::Unavailable as u8 { + return None; + } + + let mut buf: libc::statx = mem::zeroed(); + if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) { + if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 { + return Some(Err(err)); + } + + // We're not yet entirely sure whether `statx` is usable on this kernel + // or not. Syscalls can return errors from things other than the kernel + // per se, e.g. `EPERM` can be returned if seccomp is used to block the + // syscall, or `ENOSYS` might be returned from a faulty FUSE driver. + // + // Availability is checked by performing a call which expects `EFAULT` + // if the syscall is usable. + // + // See: https://github.com/rust-lang/rust/issues/65662 + // + // FIXME what about transient conditions like `ENOMEM`? + let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_BASIC_STATS | libc::STATX_BTIME, ptr::null_mut())) + .err() + .and_then(|e| e.raw_os_error()); + if err2 == Some(libc::EFAULT) { + STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed); + return Some(Err(err)); + } else { + STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed); + return None; + } + } + if statx_availability == STATX_STATE::Unknown as u8 { + STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed); + } + + // 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, + // Store full times to avoid 32-bit `time_t` truncation. + #[cfg(target_pointer_width = "32")] + stx_atime: buf.stx_atime, + #[cfg(target_pointer_width = "32")] + stx_ctime: buf.stx_ctime, + #[cfg(target_pointer_width = "32")] + stx_mtime: buf.stx_mtime, + }; + + 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, +} + +pub struct ReadDir { + inner: Arc, + end_of_stream: bool, +} + +impl ReadDir { + fn new(inner: InnerReadDir) -> Self { + Self { inner: Arc::new(inner), end_of_stream: false } + } +} + +struct Dir(*mut libc::DIR); + +unsafe impl Send for Dir {} +unsafe impl Sync for Dir {} + +#[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", +))] +pub struct DirEntry { + dir: Arc, + entry: dirent64_min, + // We need to store an owned copy of the entry name on platforms that use + // readdir() (not readdir_r()), because a) struct dirent may use a flexible + // array to store the name, b) it lives only until the next readdir() call. + name: crate::ffi::CString, +} + +// Define a minimal subset of fields we need from `dirent64`, especially since +// we're not using the immediate `d_name` on these targets. Keeping this as an +// `entry` field in `DirEntry` helps reduce the `cfg` boilerplate elsewhere. +#[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", +))] +struct dirent64_min { + d_ino: u64, + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "aix", + target_os = "nto", + target_os = "vita", + )))] + d_type: u8, +} + +#[cfg(not(any( + target_os = "android", + target_os = "linux", + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", +)))] +pub struct DirEntry { + dir: Arc, + // The full entry includes a fixed-length `d_name`. + entry: dirent64, +} + +#[derive(Clone)] +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)] +pub struct FilePermissions { + mode: mode_t, +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes { + accessed: Option, + modified: Option, + #[cfg(target_vendor = "apple")] + created: Option, +} + +#[derive(Copy, Clone, Eq)] +pub struct FileType { + mode: mode_t, +} + +impl PartialEq for FileType { + fn eq(&self, other: &Self) -> bool { + self.masked() == other.masked() + } +} + +impl core::hash::Hash for FileType { + fn hash(&self, state: &mut H) { + self.masked().hash(state); + } +} + +pub struct DirBuilder { + mode: mode_t, +} + +#[derive(Copy, Clone)] +struct Mode(mode_t); + +cfg_has_statx! {{ + impl FileAttr { + fn from_stat64(stat: stat64) -> Self { + Self { stat, statx_extra_fields: None } + } + + #[cfg(target_pointer_width = "32")] + pub fn stx_mtime(&self) -> Option<&libc::statx_timestamp> { + if let Some(ext) = &self.statx_extra_fields { + if (ext.stx_mask & libc::STATX_MTIME) != 0 { + return Some(&ext.stx_mtime); + } + } + None + } + + #[cfg(target_pointer_width = "32")] + pub fn stx_atime(&self) -> Option<&libc::statx_timestamp> { + if let Some(ext) = &self.statx_extra_fields { + if (ext.stx_mask & libc::STATX_ATIME) != 0 { + return Some(&ext.stx_atime); + } + } + None + } + + #[cfg(target_pointer_width = "32")] + pub fn stx_ctime(&self) -> Option<&libc::statx_timestamp> { + if let Some(ext) = &self.statx_extra_fields { + if (ext.stx_mask & libc::STATX_CTIME) != 0 { + return Some(&ext.stx_ctime); + } + } + 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 { + SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64) + } + + pub fn accessed(&self) -> io::Result { + SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64) + } + + pub fn created(&self) -> io::Result { + SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64) + } +} + +#[cfg(target_os = "aix")] +impl FileAttr { + pub fn modified(&self) -> io::Result { + SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64) + } + + pub fn accessed(&self) -> io::Result { + SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64) + } + + pub fn created(&self) -> io::Result { + SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64) + } +} + +#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix")))] +impl FileAttr { + #[cfg(not(any( + target_os = "vxworks", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "hurd", + target_os = "rtems", + target_os = "nuttx", + )))] + pub fn modified(&self) -> io::Result { + #[cfg(target_pointer_width = "32")] + cfg_has_statx! { + if let Some(mtime) = self.stx_mtime() { + return SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64); + } + } + + SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64) + } + + #[cfg(any( + target_os = "vxworks", + target_os = "espidf", + target_os = "vita", + target_os = "rtems", + ))] + pub fn modified(&self) -> io::Result { + SystemTime::new(self.stat.st_mtime as i64, 0) + } + + #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))] + pub fn modified(&self) -> io::Result { + SystemTime::new(self.stat.st_mtim.tv_sec as i64, self.stat.st_mtim.tv_nsec as i64) + } + + #[cfg(not(any( + target_os = "vxworks", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "hurd", + target_os = "rtems", + target_os = "nuttx", + )))] + pub fn accessed(&self) -> io::Result { + #[cfg(target_pointer_width = "32")] + cfg_has_statx! { + if let Some(atime) = self.stx_atime() { + return SystemTime::new(atime.tv_sec, atime.tv_nsec as i64); + } + } + + SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64) + } + + #[cfg(any( + target_os = "vxworks", + target_os = "espidf", + target_os = "vita", + target_os = "rtems" + ))] + pub fn accessed(&self) -> io::Result { + SystemTime::new(self.stat.st_atime as i64, 0) + } + + #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))] + pub fn accessed(&self) -> io::Result { + SystemTime::new(self.stat.st_atim.tv_sec as i64, self.stat.st_atim.tv_nsec as i64) + } + + #[cfg(any( + target_os = "freebsd", + target_os = "openbsd", + target_vendor = "apple", + target_os = "cygwin", + ))] + pub fn created(&self) -> io::Result { + SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64) + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "openbsd", + target_os = "vita", + target_vendor = "apple", + target_os = "cygwin", + )))] + 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 { + SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64) + } else { + Err(io::const_error!( + io::ErrorKind::Unsupported, + "creation time is not available for the filesystem", + )) + }; + } + } + + Err(io::const_error!( + io::ErrorKind::Unsupported, + "creation time is not available on this platform currently", + )) + } + + #[cfg(target_os = "vita")] + pub fn created(&self) -> io::Result { + SystemTime::new(self.stat.st_ctime as i64, 0) + } +} + +#[cfg(target_os = "nto")] +impl FileAttr { + pub fn modified(&self) -> io::Result { + SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec) + } + + pub fn accessed(&self) -> io::Result { + SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec) + } + + pub fn created(&self) -> io::Result { + SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec) + } +} + +impl AsInner for FileAttr { + #[inline] + 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 FileTimes { + pub fn set_accessed(&mut self, t: SystemTime) { + self.accessed = Some(t); + } + + pub fn set_modified(&mut self, t: SystemTime) { + self.modified = Some(t); + } + + #[cfg(target_vendor = "apple")] + pub fn set_created(&mut self, t: SystemTime) { + self.created = Some(t); + } +} + +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.masked() == mode + } + + fn masked(&self) -> mode_t { + self.mode & libc::S_IFMT + } +} + +impl fmt::Debug for FileType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let FileType { mode } = self; + f.debug_struct("FileType").field("mode", &Mode(*mode)).finish() + } +} + +impl FromInner for FilePermissions { + fn from_inner(mode: u32) -> FilePermissions { + FilePermissions { mode: mode as mode_t } + } +} + +impl fmt::Debug for FilePermissions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let FilePermissions { mode } = self; + f.debug_struct("FilePermissions").field("mode", &Mode(*mode)).finish() + } +} + +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 = "android", + target_os = "linux", + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", + ))] + fn next(&mut self) -> Option> { + use crate::sys::os::{errno, set_errno}; + + if self.end_of_stream { + return None; + } + + unsafe { + loop { + // As of POSIX.1-2017, readdir() is not required to be thread safe; only + // readdir_r() is. However, readdir_r() cannot correctly handle platforms + // with unlimited or variable NAME_MAX. Many modern platforms guarantee + // thread safety for readdir() as long an individual DIR* is not accessed + // concurrently, which is sufficient for Rust. + set_errno(0); + let entry_ptr: *const dirent64 = readdir64(self.inner.dirp.0); + if entry_ptr.is_null() { + // We either encountered an error, or reached the end. Either way, + // the next call to next() should return None. + self.end_of_stream = true; + + // To distinguish between errors and end-of-directory, we had to clear + // errno beforehand to check for an error now. + return match errno() { + 0 => None, + e => Some(Err(Error::from_raw_os_error(e))), + }; + } + + // The dirent64 struct is a weird imaginary thing that isn't ever supposed + // to be worked with by value. Its trailing d_name field is declared + // variously as [c_char; 256] or [c_char; 1] on different systems but + // either way that size is meaningless; only the offset of d_name is + // meaningful. The dirent64 pointers that libc returns from readdir64 are + // allowed to point to allocations smaller _or_ LARGER than implied by the + // definition of the struct. + // + // As such, we need to be even more careful with dirent64 than if its + // contents were "simply" partially initialized data. + // + // Like for uninitialized contents, converting entry_ptr to `&dirent64` + // would not be legal. However, we can use `&raw const (*entry_ptr).d_name` + // to refer the fields individually, because that operation is equivalent + // to `byte_offset` and thus does not require the full extent of `*entry_ptr` + // to be in bounds of the same allocation, only the offset of the field + // being referenced. + + // d_name is guaranteed to be null-terminated. + let name = CStr::from_ptr((&raw const (*entry_ptr).d_name).cast()); + let name_bytes = name.to_bytes(); + if name_bytes == b"." || name_bytes == b".." { + continue; + } + + // When loading from a field, we can skip the `&raw const`; `(*entry_ptr).d_ino` as + // a value expression will do the right thing: `byte_offset` to the field and then + // only access those bytes. + #[cfg(not(target_os = "vita"))] + let entry = dirent64_min { + d_ino: (*entry_ptr).d_ino as u64, + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "aix", + target_os = "nto", + )))] + d_type: (*entry_ptr).d_type as u8, + }; + + #[cfg(target_os = "vita")] + let entry = dirent64_min { d_ino: 0u64 }; + + return Some(Ok(DirEntry { + entry, + name: name.to_owned(), + dir: Arc::clone(&self.inner), + })); + } + } + } + + #[cfg(not(any( + target_os = "android", + target_os = "linux", + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", + )))] + fn next(&mut self) -> Option> { + if self.end_of_stream { + return None; + } + + unsafe { + let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) }; + let mut entry_ptr = ptr::null_mut(); + loop { + let err = readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr); + if err != 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::from_raw_os_error(err))); + } + if entry_ptr.is_null() { + return None; + } + if ret.name_bytes() != b"." && ret.name_bytes() != b".." { + return Some(Ok(ret)); + } + } + } + } +} + +/// Aborts the process if a file desceriptor is not open, if debug asserts are enabled +/// +/// Many IO syscalls can't be fully trusted about EBADF error codes because those +/// might get bubbled up from a remote FUSE server rather than the file descriptor +/// in the current process being invalid. +/// +/// So we check file flags instead which live on the file descriptor and not the underlying file. +/// The downside is that it costs an extra syscall, so we only do it for debug. +#[inline] +pub(crate) fn debug_assert_fd_is_open(fd: RawFd) { + use crate::sys::os::errno; + + // this is similar to assert_unsafe_precondition!() but it doesn't require const + if core::ub_checks::check_library_ub() { + if unsafe { libc::fcntl(fd, libc::F_GETFD) } == -1 && errno() == libc::EBADF { + rtabort!("IO Safety violation: owned file descriptor already closed"); + } + } +} + +impl Drop for Dir { + fn drop(&mut self) { + // dirfd isn't supported everywhere + #[cfg(not(any( + miri, + target_os = "redox", + target_os = "nto", + target_os = "vita", + target_os = "hurd", + target_os = "espidf", + target_os = "horizon", + target_os = "vxworks", + target_os = "rtems", + target_os = "nuttx", + )))] + { + let fd = unsafe { libc::dirfd(self.0) }; + debug_assert_fd_is_open(fd); + } + let r = unsafe { libc::closedir(self.0) }; + assert!( + r == 0 || crate::io::Error::last_os_error().is_interrupted(), + "unexpected error during closedir: {:?}", + crate::io::Error::last_os_error() + ); + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.dir.root.join(self.file_name_os_str()) + } + + pub fn file_name(&self) -> OsString { + self.file_name_os_str().to_os_string() + } + + #[cfg(all( + any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + ), + not(miri) // no dirfd on Miri + ))] + pub fn metadata(&self) -> io::Result { + let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?; + let name = self.name_cstr().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_BASIC_STATS | libc::STATX_BTIME, + ) } { + 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(any( + not(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + )), + miri + ))] + pub fn metadata(&self) -> io::Result { + run_path_with_cstr(&self.path(), &lstat) + } + + #[cfg(any( + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "vxworks", + target_os = "aix", + target_os = "nto", + target_os = "vita", + ))] + pub fn file_type(&self) -> io::Result { + self.metadata().map(|m| m.file_type()) + } + + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "vxworks", + target_os = "aix", + target_os = "nto", + target_os = "vita", + )))] + 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 }), + _ => self.metadata().map(|m| m.file_type()), + } + } + + #[cfg(any( + target_os = "linux", + target_os = "cygwin", + target_os = "emscripten", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "l4re", + target_os = "fuchsia", + target_os = "redox", + target_os = "vxworks", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "aix", + target_os = "nto", + target_os = "hurd", + target_os = "rtems", + target_vendor = "apple", + ))] + 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(target_os = "nuttx")] + pub fn ino(&self) -> u64 { + // Leave this 0 for now, as NuttX does not provide an inode number + // in its directory entries. + 0 + } + + #[cfg(any( + target_os = "netbsd", + target_os = "openbsd", + target_os = "freebsd", + target_os = "dragonfly", + target_vendor = "apple", + ))] + 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(not(any( + target_os = "netbsd", + target_os = "openbsd", + target_os = "freebsd", + target_os = "dragonfly", + target_vendor = "apple", + )))] + fn name_bytes(&self) -> &[u8] { + self.name_cstr().to_bytes() + } + + #[cfg(not(any( + target_os = "android", + target_os = "linux", + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", + )))] + fn name_cstr(&self) -> &CStr { + unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) } + } + #[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", + ))] + fn name_cstr(&self) -> &CStr { + &self.name + } + + pub fn file_name_os_str(&self) -> &OsStr { + OsStr::from_bytes(self.name_bytes()) + } +} + +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 fmt::Debug for OpenOptions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let OpenOptions { read, write, append, truncate, create, create_new, custom_flags, mode } = + self; + f.debug_struct("OpenOptions") + .field("read", read) + .field("write", write) + .field("append", append) + .field("truncate", truncate) + .field("create", create) + .field("create_new", create_new) + .field("custom_flags", custom_flags) + .field("mode", &Mode(*mode)) + .finish() + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + run_path_with_cstr(path, &|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(unsafe { FileDesc::from_raw_fd(fd) })) + } + + pub fn file_attr(&self) -> io::Result { + let fd = self.as_raw_fd(); + + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + fd, + c"".as_ptr() as *const c_char, + libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + ) } { + 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.as_raw_fd()) })?; + return Ok(()); + + #[cfg(target_vendor = "apple")] + unsafe fn os_fsync(fd: c_int) -> c_int { + libc::fcntl(fd, libc::F_FULLFSYNC) + } + #[cfg(not(target_vendor = "apple"))] + unsafe fn os_fsync(fd: c_int) -> c_int { + libc::fsync(fd) + } + } + + pub fn datasync(&self) -> io::Result<()> { + cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?; + return Ok(()); + + #[cfg(target_vendor = "apple")] + unsafe fn os_datasync(fd: c_int) -> c_int { + libc::fcntl(fd, libc::F_FULLFSYNC) + } + #[cfg(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "cygwin", + target_os = "android", + target_os = "netbsd", + target_os = "openbsd", + target_os = "nto", + target_os = "hurd", + ))] + unsafe fn os_datasync(fd: c_int) -> c_int { + libc::fdatasync(fd) + } + #[cfg(not(any( + target_os = "android", + target_os = "fuchsia", + target_os = "freebsd", + target_os = "linux", + target_os = "cygwin", + target_os = "netbsd", + target_os = "openbsd", + target_os = "nto", + target_os = "hurd", + target_vendor = "apple", + )))] + unsafe fn os_datasync(fd: c_int) -> c_int { + libc::fsync(fd) + } + } + + #[cfg(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + ))] + pub fn lock(&self) -> io::Result<()> { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?; + return Ok(()); + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + )))] + pub fn lock(&self) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported")) + } + + #[cfg(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + ))] + pub fn lock_shared(&self) -> io::Result<()> { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?; + return Ok(()); + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + )))] + pub fn lock_shared(&self) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) + } + + #[cfg(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + ))] + pub fn try_lock(&self) -> Result<(), TryLockError> { + let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }); + if let Err(err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + Err(TryLockError::WouldBlock) + } else { + Err(TryLockError::Error(err)) + } + } else { + Ok(()) + } + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + )))] + pub fn try_lock(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(io::const_error!( + io::ErrorKind::Unsupported, + "try_lock() not supported" + ))) + } + + #[cfg(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + ))] + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) }); + if let Err(err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + Err(TryLockError::WouldBlock) + } else { + Err(TryLockError::Error(err)) + } + } else { + Ok(()) + } + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + )))] + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(io::const_error!( + io::ErrorKind::Unsupported, + "try_lock_shared() not supported" + ))) + } + + #[cfg(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + ))] + pub fn unlock(&self) -> io::Result<()> { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?; + return Ok(()); + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_vendor = "apple", + )))] + pub fn unlock(&self) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported")) + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + let size: off64_t = + size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; + cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), 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 read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(cursor) + } + + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + self.0.read_vectored_at(bufs, 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 write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + self.0.write_vectored_at(bufs, offset) + } + + #[inline] + 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.as_raw_fd(), pos as off64_t, whence) })?; + Ok(n as u64) + } + + pub fn tell(&self) -> io::Result { + self.seek(SeekFrom::Current(0)) + } + + pub fn duplicate(&self) -> io::Result { + self.0.duplicate().map(File) + } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?; + Ok(()) + } + + pub fn set_times(&self, times: FileTimes) -> io::Result<()> { + #[cfg(not(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "vxworks", + target_os = "nuttx", + )))] + let to_timespec = |time: Option| match time { + Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), + Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time", + )), + Some(_) => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too small to set as a file time", + )), + None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), + }; + cfg_if::cfg_if! { + if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx"))] { + // Redox doesn't appear to support `UTIME_OMIT`. + // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore + // the same as for Redox. + let _ = times; + Err(io::const_error!( + io::ErrorKind::Unsupported, + "setting file times not supported", + )) + } else if #[cfg(target_vendor = "apple")] { + let mut buf = [mem::MaybeUninit::::uninit(); 3]; + let mut num_times = 0; + let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; + attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; + if times.created.is_some() { + buf[num_times].write(to_timespec(times.created)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_CRTIME; + } + if times.modified.is_some() { + buf[num_times].write(to_timespec(times.modified)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_MODTIME; + } + if times.accessed.is_some() { + buf[num_times].write(to_timespec(times.accessed)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; + } + cvt(unsafe { libc::fsetattrlist( + self.as_raw_fd(), + (&raw const attrlist).cast::().cast_mut(), + buf.as_ptr().cast::().cast_mut(), + num_times * size_of::(), + 0 + ) })?; + Ok(()) + } else if #[cfg(target_os = "android")] { + let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + // futimens requires Android API level 19 + cvt(unsafe { + weak!( + fn futimens(fd: c_int, times: *const libc::timespec) -> c_int; + ); + match futimens.get() { + Some(futimens) => futimens(self.as_raw_fd(), times.as_ptr()), + None => return Err(io::const_error!( + io::ErrorKind::Unsupported, + "setting file times requires Android API level >= 19", + )), + } + })?; + Ok(()) + } else { + #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))] + { + use crate::sys::{time::__timespec64, weak::weak}; + + // Added in glibc 2.34 + weak!( + fn __futimens64(fd: c_int, times: *const __timespec64) -> c_int; + ); + + if let Some(futimens64) = __futimens64.get() { + let to_timespec = |time: Option| time.map(|time| time.t.to_timespec64()) + .unwrap_or(__timespec64::new(0, libc::UTIME_OMIT as _)); + let times = [to_timespec(times.accessed), to_timespec(times.modified)]; + cvt(unsafe { futimens64(self.as_raw_fd(), times.as_ptr()) })?; + return Ok(()); + } + } + let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?; + Ok(()) + } + } + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { mode: 0o777 } + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + run_path_with_cstr(p, &|p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ())) + } + + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as mode_t; + } +} + +impl fmt::Debug for DirBuilder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let DirBuilder { mode } = self; + f.debug_struct("DirBuilder").field("mode", &Mode(*mode)).finish() + } +} + +impl AsInner for File { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl AsInnerMut for File { + #[inline] + fn as_inner_mut(&mut self) -> &mut FileDesc { + &mut self.0 + } +} + +impl IntoInner for File { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for File { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for File { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for File { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for File { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for File { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))] + fn get_path(fd: c_int) -> Option { + let mut p = PathBuf::from("/proc/self/fd"); + p.push(&fd.to_string()); + run_path_with_cstr(&p, &readlink).ok() + } + + #[cfg(any(target_vendor = "apple", target_os = "netbsd"))] + fn get_path(fd: c_int) -> Option { + // FIXME: The use of PATH_MAX is generally not encouraged, but it + // is inevitable in this case because Apple targets and NetBSD define `fcntl` + // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no + // alternatives. If a better method is invented, it should be used + // instead. + let mut buf = vec![0; libc::PATH_MAX as usize]; + let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; + if n == -1 { + cfg_if::cfg_if! { + if #[cfg(target_os = "netbsd")] { + // fallback to procfs as last resort + let mut p = PathBuf::from("/proc/self/fd"); + p.push(&fd.to_string()); + return run_path_with_cstr(&p, &readlink).ok() + } else { + 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(target_os = "freebsd")] + fn get_path(fd: c_int) -> Option { + let info = Box::::new_zeroed(); + let mut info = unsafe { info.assume_init() }; + info.kf_structsize = size_of::() as libc::c_int; + let n = unsafe { libc::fcntl(fd, libc::F_KINFO, &mut *info) }; + if n == -1 { + return None; + } + let buf = unsafe { CStr::from_ptr(info.kf_path.as_mut_ptr()).to_bytes().to_vec() }; + Some(PathBuf::from(OsString::from_vec(buf))) + } + + #[cfg(target_os = "vxworks")] + fn get_path(fd: c_int) -> Option { + let mut buf = vec![0; libc::PATH_MAX as usize]; + let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) }; + if n == -1 { + return None; + } + let l = buf.iter().position(|&c| c == 0).unwrap(); + buf.truncate(l as usize); + Some(PathBuf::from(OsString::from_vec(buf))) + } + + #[cfg(not(any( + target_os = "linux", + target_os = "vxworks", + target_os = "freebsd", + target_os = "netbsd", + target_os = "illumos", + target_os = "solaris", + target_vendor = "apple", + )))] + fn get_path(_fd: c_int) -> Option { + // FIXME(#24570): implement this for other Unix platforms + None + } + + 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, + } + } + + let fd = self.as_raw_fd(); + 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() + } +} + +// Format in octal, followed by the mode format used in `ls -l`. +// +// References: +// https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html +// https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html +// https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html +// +// Example: +// 0o100664 (-rw-rw-r--) +impl fmt::Debug for Mode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(mode) = *self; + write!(f, "0o{mode:06o}")?; + + let entry_type = match mode & libc::S_IFMT { + libc::S_IFDIR => 'd', + libc::S_IFBLK => 'b', + libc::S_IFCHR => 'c', + libc::S_IFLNK => 'l', + libc::S_IFIFO => 'p', + libc::S_IFREG => '-', + _ => return Ok(()), + }; + + f.write_str(" (")?; + f.write_char(entry_type)?; + + // Owner permissions + f.write_char(if mode & libc::S_IRUSR != 0 { 'r' } else { '-' })?; + f.write_char(if mode & libc::S_IWUSR != 0 { 'w' } else { '-' })?; + let owner_executable = mode & libc::S_IXUSR != 0; + let setuid = mode as c_int & libc::S_ISUID as c_int != 0; + f.write_char(match (owner_executable, setuid) { + (true, true) => 's', // executable and setuid + (false, true) => 'S', // setuid + (true, false) => 'x', // executable + (false, false) => '-', + })?; + + // Group permissions + f.write_char(if mode & libc::S_IRGRP != 0 { 'r' } else { '-' })?; + f.write_char(if mode & libc::S_IWGRP != 0 { 'w' } else { '-' })?; + let group_executable = mode & libc::S_IXGRP != 0; + let setgid = mode as c_int & libc::S_ISGID as c_int != 0; + f.write_char(match (group_executable, setgid) { + (true, true) => 's', // executable and setgid + (false, true) => 'S', // setgid + (true, false) => 'x', // executable + (false, false) => '-', + })?; + + // Other permissions + f.write_char(if mode & libc::S_IROTH != 0 { 'r' } else { '-' })?; + f.write_char(if mode & libc::S_IWOTH != 0 { 'w' } else { '-' })?; + let other_executable = mode & libc::S_IXOTH != 0; + let sticky = mode as c_int & libc::S_ISVTX as c_int != 0; + f.write_char(match (entry_type, other_executable, sticky) { + ('d', true, true) => 't', // searchable and restricted deletion + ('d', false, true) => 'T', // restricted deletion + (_, true, _) => 'x', // executable + (_, false, _) => '-', + })?; + + f.write_char(')') + } +} + +pub fn readdir(path: &Path) -> io::Result { + let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?; + if ptr.is_null() { + Err(Error::last_os_error()) + } else { + let root = path.to_path_buf(); + let inner = InnerReadDir { dirp: Dir(ptr), root }; + Ok(ReadDir::new(inner)) + } +} + +pub fn unlink(p: &CStr) -> io::Result<()> { + cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ()) +} + +pub fn rename(old: &CStr, new: &CStr) -> io::Result<()> { + cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ()) +} + +pub fn set_perm(p: &CStr, perm: FilePermissions) -> io::Result<()> { + cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ()) +} + +pub fn rmdir(p: &CStr) -> io::Result<()> { + cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ()) +} + +pub fn readlink(c_path: &CStr) -> io::Result { + 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(original: &CStr, link: &CStr) -> io::Result<()> { + cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ()) +} + +pub fn link(original: &CStr, link: &CStr) -> io::Result<()> { + cfg_if::cfg_if! { + if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_env = "nto70"))] { + // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves + // it implementation-defined whether `link` follows symlinks, so rely on the + // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. + // Android has `linkat` on newer versions, but we happen to know `link` + // always has the correct behavior, so it's here as well. + cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; + } else { + // Where we can, use `linkat` instead of `link`; see the comment above + // this one for details on why. + cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; + } + } + Ok(()) +} + +pub fn stat(p: &CStr) -> io::Result { + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + libc::AT_FDCWD, + p.as_ptr(), + libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + ) } { + 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: &CStr) -> io::Result { + 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_BASIC_STATS | libc::STATX_BTIME, + ) } { + 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(path: &CStr) -> io::Result { + let r = unsafe { libc::realpath(path.as_ptr(), ptr::null_mut()) }; + if r.is_null() { + return Err(io::Error::last_os_error()); + } + Ok(PathBuf::from(OsString::from_vec(unsafe { + let buf = CStr::from_ptr(r).to_bytes().to_vec(); + libc::free(r as *mut _); + buf + }))) +} + +fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { + use crate::fs::File; + use crate::sys::fs::common::NOT_FILE_ERROR; + + let reader = File::open(from)?; + let metadata = reader.metadata()?; + if !metadata.is_file() { + return Err(NOT_FILE_ERROR); + } + Ok((reader, metadata)) +} + +#[cfg(target_os = "espidf")] +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; + let writer = OpenOptions::new().open(to)?; + let writer_metadata = writer.metadata()?; + Ok((writer, writer_metadata)) +} + +#[cfg(not(target_os = "espidf"))] +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()?; + // fchmod is broken on vita + #[cfg(not(target_os = "vita"))] + 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)) +} + +mod cfm { + use crate::fs::{File, Metadata}; + use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, Read, Result, Write}; + + #[allow(dead_code)] + pub struct CachedFileMetadata(pub File, pub Metadata); + + impl Read for CachedFileMetadata { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { + self.0.read_vectored(bufs) + } + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { + self.0.read_buf(cursor) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + self.0.read_to_end(buf) + } + fn read_to_string(&mut self, buf: &mut String) -> Result { + self.0.read_to_string(buf) + } + } + impl Write for CachedFileMetadata { + fn write(&mut self, buf: &[u8]) -> Result { + self.0.write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + #[inline] + fn flush(&mut self) -> Result<()> { + self.0.flush() + } + } +} +#[cfg(any(target_os = "linux", target_os = "android"))] +pub(crate) use cfm::CachedFileMetadata; + +#[cfg(not(target_vendor = "apple"))] +pub fn copy(from: &Path, to: &Path) -> io::Result { + let (reader, reader_metadata) = open_from(from)?; + let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?; + + io::copy( + &mut cfm::CachedFileMetadata(reader, reader_metadata), + &mut cfm::CachedFileMetadata(writer, writer_metadata), + ) +} + +#[cfg(target_vendor = "apple")] +pub fn copy(from: &Path, to: &Path) -> io::Result { + const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA; + + struct FreeOnDrop(libc::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 an error. + libc::copyfile_state_free(self.0); + } + } + } + + let (reader, reader_metadata) = open_from(from)?; + + let clonefile_result = run_path_with_cstr(to, &|to| { + cvt(unsafe { libc::fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) }) + }); + match clonefile_result { + Ok(_) => return Ok(reader_metadata.len()), + Err(e) => match e.raw_os_error() { + // `fclonefileat` will fail on non-APFS volumes, if the + // destination already exists, or if the source and destination + // are on different devices. In all these cases `fcopyfile` + // should succeed. + Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (), + _ => return Err(e), + }, + } + + // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. + 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 = libc::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 { libc::COPYFILE_DATA }; + + cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; + + let mut bytes_copied: libc::off_t = 0; + cvt(unsafe { + libc::copyfile_state_get( + state.0, + libc::COPYFILE_STATE_COPIED as u32, + (&raw mut bytes_copied) as *mut libc::c_void, + ) + })?; + Ok(bytes_copied as u64) +} + +pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { + run_path_with_cstr(path, &|path| { + cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) + .map(|_| ()) + }) +} + +pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> { + cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?; + Ok(()) +} + +#[cfg(not(target_os = "vxworks"))] +pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { + run_path_with_cstr(path, &|path| { + cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) + .map(|_| ()) + }) +} + +#[cfg(target_os = "vxworks")] +pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { + let (_, _, _) = (path, uid, gid); + Err(io::const_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks")) +} + +#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] +pub fn chroot(dir: &Path) -> io::Result<()> { + run_path_with_cstr(dir, &|dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ())) +} + +#[cfg(target_os = "vxworks")] +pub fn chroot(dir: &Path) -> io::Result<()> { + let _ = dir; + Err(io::const_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks")) +} + +pub fn mkfifo(path: &Path, mode: u32) -> io::Result<()> { + run_path_with_cstr(path, &|path| { + cvt(unsafe { libc::mkfifo(path.as_ptr(), mode.try_into().unwrap()) }).map(|_| ()) + }) +} + +pub use remove_dir_impl::remove_dir_all; + +// Fallback for REDOX, ESP-ID, Horizon, Vita, Vxworks and Miri +#[cfg(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nto", + target_os = "vxworks", + miri +))] +mod remove_dir_impl { + pub use crate::sys::fs::common::remove_dir_all; +} + +// Modern implementation using openat(), unlinkat() and fdopendir() +#[cfg(not(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nto", + target_os = "vxworks", + miri +)))] +mod remove_dir_impl { + #[cfg(not(all(target_os = "linux", target_env = "gnu")))] + use libc::{fdopendir, openat, unlinkat}; + #[cfg(all(target_os = "linux", target_env = "gnu"))] + use libc::{fdopendir, openat64 as openat, unlinkat}; + + use super::{Dir, DirEntry, InnerReadDir, ReadDir, lstat}; + use crate::ffi::CStr; + use crate::io; + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; + use crate::os::unix::prelude::{OwnedFd, RawFd}; + use crate::path::{Path, PathBuf}; + use crate::sys::common::small_c_string::run_path_with_cstr; + use crate::sys::{cvt, cvt_r}; + use crate::sys_common::ignore_notfound; + + pub fn openat_nofollow_dironly(parent_fd: Option, p: &CStr) -> io::Result { + let fd = cvt_r(|| unsafe { + openat( + parent_fd.unwrap_or(libc::AT_FDCWD), + p.as_ptr(), + libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY, + ) + })?; + Ok(unsafe { OwnedFd::from_raw_fd(fd) }) + } + + fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> { + let ptr = unsafe { fdopendir(dir_fd.as_raw_fd()) }; + if ptr.is_null() { + return Err(io::Error::last_os_error()); + } + let dirp = Dir(ptr); + // file descriptor is automatically closed by libc::closedir() now, so give up ownership + let new_parent_fd = dir_fd.into_raw_fd(); + // a valid root is not needed because we do not call any functions involving the full path + // of the `DirEntry`s. + let dummy_root = PathBuf::new(); + let inner = InnerReadDir { dirp, root: dummy_root }; + Ok((ReadDir::new(inner), new_parent_fd)) + } + + #[cfg(any( + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "vxworks", + target_os = "aix", + ))] + fn is_dir(_ent: &DirEntry) -> Option { + None + } + + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "vxworks", + target_os = "aix", + )))] + fn is_dir(ent: &DirEntry) -> Option { + match ent.entry.d_type { + libc::DT_UNKNOWN => None, + libc::DT_DIR => Some(true), + _ => Some(false), + } + } + + fn is_enoent(result: &io::Result<()>) -> bool { + if let Err(err) = result + && matches!(err.raw_os_error(), Some(libc::ENOENT)) + { + true + } else { + false + } + } + + fn remove_dir_all_recursive(parent_fd: Option, path: &CStr) -> io::Result<()> { + // try opening as directory + let fd = match openat_nofollow_dironly(parent_fd, &path) { + Err(err) if matches!(err.raw_os_error(), Some(libc::ENOTDIR | libc::ELOOP)) => { + // not a directory - don't traverse further + // (for symlinks, older Linux kernels may return ELOOP instead of ENOTDIR) + return match parent_fd { + // unlink... + Some(parent_fd) => { + cvt(unsafe { unlinkat(parent_fd, path.as_ptr(), 0) }).map(drop) + } + // ...unless this was supposed to be the deletion root directory + None => Err(err), + }; + } + result => result?, + }; + + // open the directory passing ownership of the fd + let (dir, fd) = fdreaddir(fd)?; + for child in dir { + let child = child?; + let child_name = child.name_cstr(); + // we need an inner try block, because if one of these + // directories has already been deleted, then we need to + // continue the loop, not return ok. + let result: io::Result<()> = try { + match is_dir(&child) { + Some(true) => { + remove_dir_all_recursive(Some(fd), child_name)?; + } + Some(false) => { + cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?; + } + None => { + // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed + // if the process has the appropriate privileges. This however can causing orphaned + // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing + // into it first instead of trying to unlink() it. + remove_dir_all_recursive(Some(fd), child_name)?; + } + } + }; + if result.is_err() && !is_enoent(&result) { + return result; + } + } + + // unlink the directory after removing its contents + ignore_notfound(cvt(unsafe { + unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), path.as_ptr(), libc::AT_REMOVEDIR) + }))?; + Ok(()) + } + + fn remove_dir_all_modern(p: &CStr) -> io::Result<()> { + // We cannot just call remove_dir_all_recursive() here because that would not delete a passed + // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse + // into symlinks. + let attr = lstat(p)?; + if attr.file_type().is_symlink() { + super::unlink(p) + } else { + remove_dir_all_recursive(None, &p) + } + } + + pub fn remove_dir_all(p: &Path) -> io::Result<()> { + run_path_with_cstr(p, &remove_dir_all_modern) + } +} diff --git a/library/std/src/sys/fs/unix/tests.rs b/library/std/src/sys/fs/unix/tests.rs new file mode 100644 index 0000000000000..8875a318db7d2 --- /dev/null +++ b/library/std/src/sys/fs/unix/tests.rs @@ -0,0 +1,71 @@ +use crate::sys::fs::FilePermissions; + +#[test] +fn test_debug_permissions() { + for (expected, mode) in [ + // typical directory + ("FilePermissions { mode: 0o040775 (drwxrwxr-x) }", 0o04_0775), + // typical text file + ("FilePermissions { mode: 0o100664 (-rw-rw-r--) }", 0o10_0664), + // setuid executable (/usr/bin/doas) + ("FilePermissions { mode: 0o104755 (-rwsr-xr-x) }", 0o10_4755), + // char device (/dev/zero) + ("FilePermissions { mode: 0o020666 (crw-rw-rw-) }", 0o02_0666), + // block device (/dev/vda) + ("FilePermissions { mode: 0o060660 (brw-rw----) }", 0o06_0660), + // symbolic link + ("FilePermissions { mode: 0o120777 (lrwxrwxrwx) }", 0o12_0777), + // fifo + ("FilePermissions { mode: 0o010664 (prw-rw-r--) }", 0o01_0664), + // none + ("FilePermissions { mode: 0o100000 (----------) }", 0o10_0000), + // unrecognized + ("FilePermissions { mode: 0o000001 }", 1), + ] { + assert_eq!(format!("{:?}", FilePermissions { mode }), expected); + } + + for (expected, mode) in [ + // owner readable + ("FilePermissions { mode: 0o100400 (-r--------) }", libc::S_IRUSR), + // owner writable + ("FilePermissions { mode: 0o100200 (--w-------) }", libc::S_IWUSR), + // owner executable + ("FilePermissions { mode: 0o100100 (---x------) }", libc::S_IXUSR), + // setuid + ("FilePermissions { mode: 0o104000 (---S------) }", libc::S_ISUID), + // owner executable and setuid + ("FilePermissions { mode: 0o104100 (---s------) }", libc::S_IXUSR | libc::S_ISUID), + // group readable + ("FilePermissions { mode: 0o100040 (----r-----) }", libc::S_IRGRP), + // group writable + ("FilePermissions { mode: 0o100020 (-----w----) }", libc::S_IWGRP), + // group executable + ("FilePermissions { mode: 0o100010 (------x---) }", libc::S_IXGRP), + // setgid + ("FilePermissions { mode: 0o102000 (------S---) }", libc::S_ISGID), + // group executable and setgid + ("FilePermissions { mode: 0o102010 (------s---) }", libc::S_IXGRP | libc::S_ISGID), + // other readable + ("FilePermissions { mode: 0o100004 (-------r--) }", libc::S_IROTH), + // other writeable + ("FilePermissions { mode: 0o100002 (--------w-) }", libc::S_IWOTH), + // other executable + ("FilePermissions { mode: 0o100001 (---------x) }", libc::S_IXOTH), + // sticky + ("FilePermissions { mode: 0o101000 (----------) }", libc::S_ISVTX), + // other executable and sticky + ("FilePermissions { mode: 0o101001 (---------x) }", libc::S_IXOTH | libc::S_ISVTX), + ] { + assert_eq!(format!("{:?}", FilePermissions { mode: libc::S_IFREG | mode }), expected); + } + + for (expected, mode) in [ + // restricted deletion ("sticky") flag is set, and search permission is not granted to others + ("FilePermissions { mode: 0o041000 (d--------T) }", libc::S_ISVTX), + // sticky and searchable + ("FilePermissions { mode: 0o041001 (d--------t) }", libc::S_ISVTX | libc::S_IXOTH), + ] { + assert_eq!(format!("{:?}", FilePermissions { mode: libc::S_IFDIR | mode }), expected); + } +} diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs new file mode 100644 index 0000000000000..0ff9533c04734 --- /dev/null +++ b/library/std/src/sys/fs/unsupported.rs @@ -0,0 +1,349 @@ +use crate::ffi::OsString; +use crate::fmt; +use crate::fs::TryLockError; +use crate::hash::{Hash, Hasher}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::path::{Path, PathBuf}; +use crate::sys::time::SystemTime; +use crate::sys::unsupported; + +pub struct File(!); + +pub struct FileAttr(!); + +pub struct ReadDir(!); + +pub struct DirEntry(!); + +#[derive(Clone, Debug)] +pub struct OpenOptions {} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes {} + +pub struct FilePermissions(!); + +pub struct FileType(!); + +#[derive(Debug)] +pub struct DirBuilder {} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.0 + } + + pub fn perm(&self) -> FilePermissions { + self.0 + } + + pub fn file_type(&self) -> FileType { + self.0 + } + + pub fn modified(&self) -> io::Result { + self.0 + } + + pub fn accessed(&self) -> io::Result { + self.0 + } + + pub fn created(&self) -> io::Result { + self.0 + } +} + +impl Clone for FileAttr { + fn clone(&self) -> FileAttr { + self.0 + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + self.0 + } + + pub fn set_readonly(&mut self, _readonly: bool) { + self.0 + } +} + +impl Clone for FilePermissions { + fn clone(&self) -> FilePermissions { + self.0 + } +} + +impl PartialEq for FilePermissions { + fn eq(&self, _other: &FilePermissions) -> bool { + self.0 + } +} + +impl Eq for FilePermissions {} + +impl fmt::Debug for FilePermissions { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, _t: SystemTime) {} + pub fn set_modified(&mut self, _t: SystemTime) {} +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.0 + } + + pub fn is_file(&self) -> bool { + self.0 + } + + pub fn is_symlink(&self) -> bool { + self.0 + } +} + +impl Clone for FileType { + fn clone(&self) -> FileType { + self.0 + } +} + +impl Copy for FileType {} + +impl PartialEq for FileType { + fn eq(&self, _other: &FileType) -> bool { + self.0 + } +} + +impl Eq for FileType {} + +impl Hash for FileType { + fn hash(&self, _h: &mut H) { + self.0 + } +} + +impl fmt::Debug for FileType { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + self.0 + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.0 + } + + pub fn file_name(&self) -> OsString { + self.0 + } + + pub fn metadata(&self) -> io::Result { + self.0 + } + + pub fn file_type(&self) -> io::Result { + self.0 + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions {} + } + + pub fn read(&mut self, _read: bool) {} + pub fn write(&mut self, _write: bool) {} + pub fn append(&mut self, _append: bool) {} + pub fn truncate(&mut self, _truncate: bool) {} + pub fn create(&mut self, _create: bool) {} + pub fn create_new(&mut self, _create_new: bool) {} +} + +impl File { + pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { + unsupported() + } + + pub fn file_attr(&self) -> io::Result { + self.0 + } + + pub fn fsync(&self) -> io::Result<()> { + self.0 + } + + pub fn datasync(&self) -> io::Result<()> { + self.0 + } + + pub fn lock(&self) -> io::Result<()> { + self.0 + } + + pub fn lock_shared(&self) -> io::Result<()> { + self.0 + } + + pub fn try_lock(&self) -> Result<(), TryLockError> { + self.0 + } + + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + self.0 + } + + pub fn unlock(&self) -> io::Result<()> { + self.0 + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + self.0 + } + + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn flush(&self) -> io::Result<()> { + self.0 + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result { + self.0 + } + + pub fn tell(&self) -> io::Result { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + self.0 + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + self.0 + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder {} + } + + pub fn mkdir(&self, _p: &Path) -> io::Result<()> { + unsupported() + } +} + +impl fmt::Debug for File { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub fn readdir(_p: &Path) -> io::Result { + unsupported() +} + +pub fn unlink(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + unsupported() +} + +pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { + match perm.0 {} +} + +pub fn rmdir(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn exists(_path: &Path) -> io::Result { + unsupported() +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs new file mode 100644 index 0000000000000..ebfc7377a2ead --- /dev/null +++ b/library/std/src/sys/fs/wasi.rs @@ -0,0 +1,871 @@ +use crate::ffi::{CStr, OsStr, OsString}; +use crate::fs::TryLockError; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem::{self, ManuallyDrop}; +use crate::os::raw::c_int; +use crate::os::wasi::ffi::{OsStrExt, OsStringExt}; +use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::path::{Path, PathBuf}; +use crate::sync::Arc; +use crate::sys::common::small_c_string::run_path_with_cstr; +use crate::sys::fd::WasiFd; +pub use crate::sys::fs::common::exists; +use crate::sys::time::SystemTime; +use crate::sys::{unsupported, unsupported_err}; +use crate::sys_common::{AsInner, FromInner, IntoInner, ignore_notfound}; +use crate::{fmt, iter, ptr}; + +pub struct File { + fd: WasiFd, +} + +#[derive(Clone)] +pub struct FileAttr { + meta: wasi::Filestat, +} + +pub struct ReadDir { + inner: Arc, + state: ReadDirState, +} + +enum ReadDirState { + /// Fill `buf` with `buf.len()` bytes starting from `next_read_offset`. + FillBuffer { + next_read_offset: wasi::Dircookie, + buf: Vec, + }, + ProcessEntry { + buf: Vec, + next_read_offset: Option, + offset: usize, + }, + /// There is no more data to get in [`Self::FillBuffer`]; keep returning + /// entries via ProcessEntry until `buf` is exhausted. + RunUntilExhaustion { + buf: Vec, + offset: usize, + }, + Done, +} + +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(Copy, Clone, Debug, Default)] +pub struct FileTimes { + accessed: Option, + modified: Option, +} + +#[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(crate) 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 FileTimes { + pub fn set_accessed(&mut self, t: SystemTime) { + self.accessed = Some(t); + } + + pub fn set_modified(&mut self, t: SystemTime) { + self.modified = Some(t); + } +} + +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(crate) fn bits(&self) -> wasi::Filetype { + self.bits + } +} + +impl ReadDir { + fn new(dir: File, root: PathBuf) -> ReadDir { + ReadDir { + inner: Arc::new(ReadDirInner { dir, root }), + state: ReadDirState::FillBuffer { next_read_offset: 0, buf: vec![0; 128] }, + } + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ReadDir").finish_non_exhaustive() + } +} + +impl core::iter::FusedIterator for ReadDir {} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + match &mut self.state { + ReadDirState::FillBuffer { next_read_offset, buf } => { + let result = self.inner.dir.fd.readdir(buf, *next_read_offset); + match result { + Ok(read_bytes) => { + if read_bytes < buf.len() { + buf.truncate(read_bytes); + self.state = + ReadDirState::RunUntilExhaustion { buf: mem::take(buf), offset: 0 }; + } else { + debug_assert_eq!(read_bytes, buf.len()); + self.state = ReadDirState::ProcessEntry { + buf: mem::take(buf), + offset: 0, + next_read_offset: Some(*next_read_offset), + }; + } + self.next() + } + Err(e) => { + self.state = ReadDirState::Done; + return Some(Err(e)); + } + } + } + ReadDirState::ProcessEntry { buf, next_read_offset, offset } => { + let contents = &buf[*offset..]; + const DIRENT_SIZE: usize = size_of::(); + if contents.len() >= DIRENT_SIZE { + let (dirent, data) = contents.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. + if data.len() < dirent.d_namlen as usize { + if buf.len() < dirent.d_namlen as usize + DIRENT_SIZE { + buf.resize(dirent.d_namlen as usize + DIRENT_SIZE, 0); + } + if let Some(next_read_offset) = *next_read_offset { + self.state = + ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; + } else { + self.state = ReadDirState::Done; + } + + return self.next(); + } + next_read_offset.as_mut().map(|cookie| { + *cookie = dirent.d_next; + }); + *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".." { + return self.next(); + } + + return Some(Ok(DirEntry { + meta: dirent, + name: name.to_vec(), + inner: self.inner.clone(), + })); + } else if let Some(next_read_offset) = *next_read_offset { + self.state = ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; + } else { + self.state = ReadDirState::Done; + } + self.next() + } + ReadDirState::RunUntilExhaustion { buf, offset } => { + if *offset >= buf.len() { + self.state = ReadDirState::Done; + } else { + self.state = ReadDirState::ProcessEntry { + buf: mem::take(buf), + offset: *offset, + next_read_offset: None, + }; + } + + self.next() + } + ReadDirState::Done => None, + } + } +} + +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; + 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; + + 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 lock(&self) -> io::Result<()> { + unsupported() + } + + pub fn lock_shared(&self) -> io::Result<()> { + unsupported() + } + + pub fn try_lock(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) + } + + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + Err(TryLockError::Error(unsupported_err())) + } + + pub fn unlock(&self) -> io::Result<()> { + unsupported() + } + + 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 read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.fd.read_buf(cursor) + } + + 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 tell(&self) -> io::Result { + self.fd.tell() + } + + 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 set_times(&self, times: FileTimes) -> io::Result<()> { + let to_timestamp = |time: Option| match time { + Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), + Some(_) => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time", + )), + None => Ok(0), + }; + self.fd.filestat_set_times( + to_timestamp(times.accessed)?, + to_timestamp(times.modified)?, + times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM) + | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM), + ) + } + + pub fn read_link(&self, file: &Path) -> io::Result { + read_link(&self.fd, file) + } +} + +impl AsInner for File { + #[inline] + fn as_inner(&self) -> &WasiFd { + &self.fd + } +} + +impl IntoInner for File { + fn into_inner(self) -> WasiFd { + self.fd + } +} + +impl FromInner for File { + fn from_inner(fd: WasiFd) -> File { + File { fd } + } +} + +impl AsFd for File { + fn as_fd(&self) -> BorrowedFd<'_> { + self.fd.as_fd() + } +} + +impl AsRawFd for File { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} + +impl IntoRawFd for File { + fn into_raw_fd(self) -> RawFd { + self.fd.into_raw_fd() + } +} + +impl FromRawFd for File { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self { fd: FromRawFd::from_raw_fd(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.as_raw_fd()).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::new(dir, 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(original: &Path, link: &Path) -> io::Result<()> { + let (link, link_file) = open_parent(link)?; + link.symlink(osstr2str(original.as_ref())?, osstr2str(link_file.as_ref())?) +} + +pub fn link(original: &Path, link: &Path) -> io::Result<()> { + let (original, original_file) = open_parent(original)?; + let (link, link_file) = open_parent(link)?; + // Pass 0 as the flags argument, meaning don't follow symlinks. + original.link(0, osstr2str(original_file.as_ref())?, &link, osstr2str(link_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)> { + run_path_with_cstr(p, &|p| { + let mut buf = Vec::::with_capacity(512); + loop { + unsafe { + let mut relative_path = buf.as_ptr().cast(); + let mut abs_prefix = ptr::null(); + let fd = __wasilibc_find_relpath( + p.as_ptr(), + &mut abs_prefix, + &mut relative_path, + buf.capacity(), + ); + if fd == -1 { + if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) { + // 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); + continue; + } + let msg = format!( + "failed to find a pre-opened file descriptor \ + through which {p:?} could be opened", + ); + return Err(io::Error::new(io::ErrorKind::Uncategorized, msg)); + } + let relative = CStr::from_ptr(relative_path).to_bytes().to_vec(); + + return Ok(( + ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)), + PathBuf::from(OsString::from_vec(relative)), + )); + } + } + + unsafe extern "C" { + pub fn __wasilibc_find_relpath( + path: *const libc::c_char, + abs_prefix: *mut *const libc::c_char, + relative_path: *mut *const libc::c_char, + relative_path_len: libc::size_t, + ) -> libc::c_int; + } + }) +} + +pub fn osstr2str(f: &OsStr) -> io::Result<&str> { + f.to_str().ok_or_else(|| io::const_error!(io::ErrorKind::Uncategorized, "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) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + let (parent, path) = open_parent(path)?; + remove_dir_all_recursive(&parent, &path) +} + +fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { + // Open up a file descriptor for the directory itself. Note that we don't + // follow symlinks here and we specifically open directories. + // + // At the root invocation of this function this will correctly handle + // symlinks passed to the top-level `remove_dir_all`. At the recursive + // level this will double-check that after the `readdir` call deduced this + // was a directory it's still a directory by the time we open it up. + // + // If the opened file was actually a symlink then the symlink is deleted, + // not the directory recursively. + let mut opts = OpenOptions::new(); + opts.lookup_flags(0); + opts.directory(true); + opts.read(true); + let fd = open_at(parent, path, &opts)?; + if fd.file_attr()?.file_type().is_symlink() { + return parent.unlink_file(osstr2str(path.as_ref())?); + } + + // this "root" is only used by `DirEntry::path` which we don't use below so + // it's ok for this to be a bogus value + let dummy_root = PathBuf::new(); + + // Iterate over all the entries in this directory, and travel recursively if + // necessary + for entry in ReadDir::new(fd, dummy_root) { + let entry = entry?; + let path = crate::str::from_utf8(&entry.name).map_err(|_| { + io::const_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found") + })?; + + let result: io::Result<()> = try { + if entry.file_type()?.is_dir() { + remove_dir_all_recursive(&entry.inner.dir.fd, path.as_ref())?; + } else { + entry.inner.dir.fd.unlink_file(path)?; + } + }; + // ignore internal NotFound errors + if let Err(err) = &result + && err.kind() != io::ErrorKind::NotFound + { + return result; + } + } + + // Once all this directory's contents are deleted it should be safe to + // delete the directory tiself. + ignore_notfound(parent.remove_directory(osstr2str(path.as_ref())?)) +} diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs new file mode 100644 index 0000000000000..9039fd00f5d62 --- /dev/null +++ b/library/std/src/sys/fs/windows.rs @@ -0,0 +1,1651 @@ +#![allow(nonstandard_style)] + +use crate::alloc::{Layout, alloc, dealloc}; +use crate::borrow::Cow; +use crate::ffi::{OsStr, OsString, c_void}; +use crate::fs::TryLockError; +use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem::{self, MaybeUninit, offset_of}; +use crate::os::windows::io::{AsHandle, BorrowedHandle}; +use crate::os::windows::prelude::*; +use crate::path::{Path, PathBuf}; +use crate::sync::Arc; +use crate::sys::handle::Handle; +use crate::sys::pal::api::{self, WinError, set_file_information_by_handle}; +use crate::sys::pal::{IoResult, fill_utf16_buf, to_u16s, truncate_utf16_at_nul}; +use crate::sys::path::{WCStr, maybe_verbatim}; +use crate::sys::time::SystemTime; +use crate::sys::{Align8, c, cvt}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::{fmt, ptr, slice}; + +mod remove_dir_all; +use remove_dir_all::remove_dir_all_iterative; + +pub struct File { + handle: Handle, +} + +#[derive(Clone)] +pub struct FileAttr { + attributes: u32, + creation_time: c::FILETIME, + last_access_time: c::FILETIME, + last_write_time: c::FILETIME, + change_time: Option, + file_size: u64, + reparse_tag: u32, + volume_serial_number: Option, + number_of_links: Option, + file_index: Option, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct FileType { + is_directory: bool, + is_symlink: bool, +} + +pub struct ReadDir { + handle: Option, + root: Arc, + first: Option, +} + +struct FindNextFileHandle(c::HANDLE); + +unsafe impl Send for FindNextFileHandle {} +unsafe impl Sync for FindNextFileHandle {} + +pub struct DirEntry { + root: Arc, + data: c::WIN32_FIND_DATAW, +} + +unsafe impl Send for OpenOptions {} +unsafe impl Sync for OpenOptions {} + +#[derive(Clone, Debug)] +pub struct OpenOptions { + // generic + read: bool, + write: bool, + append: bool, + truncate: bool, + create: bool, + create_new: bool, + // system-specific + custom_flags: u32, + access_mode: Option, + attributes: u32, + share_mode: u32, + security_qos_flags: u32, + security_attributes: *mut c::SECURITY_ATTRIBUTES, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + attrs: u32, +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes { + accessed: Option, + modified: Option, + created: Option, +} + +impl fmt::Debug for c::FILETIME { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let time = ((self.dwHighDateTime as u64) << 32) | self.dwLowDateTime as u64; + f.debug_tuple("FILETIME").field(&time).finish() + } +} + +#[derive(Debug)] +pub struct DirBuilder; + +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("C:\")' + fmt::Debug::fmt(&*self.root, f) + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + fn next(&mut self) -> Option> { + let Some(handle) = self.handle.as_ref() else { + // This iterator was initialized with an `INVALID_HANDLE_VALUE` as its handle. + // Simply return `None` because this is only the case when `FindFirstFileExW` in + // the construction of this iterator returns `ERROR_FILE_NOT_FOUND` which means + // no matchhing files can be found. + return None; + }; + if let Some(first) = self.first.take() { + if let Some(e) = DirEntry::new(&self.root, &first) { + return Some(Ok(e)); + } + } + unsafe { + let mut wfd = mem::zeroed(); + loop { + if c::FindNextFileW(handle.0, &mut wfd) == 0 { + match api::get_last_error() { + WinError::NO_MORE_FILES => return None, + WinError { code } => { + return Some(Err(Error::from_raw_os_error(code as i32))); + } + } + } + if let Some(e) = DirEntry::new(&self.root, &wfd) { + return Some(Ok(e)); + } + } + } + } +} + +impl Drop for FindNextFileHandle { + fn drop(&mut self) { + let r = unsafe { c::FindClose(self.0) }; + debug_assert!(r != 0); + } +} + +impl DirEntry { + fn new(root: &Arc, wfd: &c::WIN32_FIND_DATAW) -> Option { + match &wfd.cFileName[0..3] { + // check for '.' and '..' + &[46, 0, ..] | &[46, 46, 0, ..] => return None, + _ => {} + } + + Some(DirEntry { root: root.clone(), data: *wfd }) + } + + pub fn path(&self) -> PathBuf { + self.root.join(self.file_name()) + } + + pub fn file_name(&self) -> OsString { + let filename = truncate_utf16_at_nul(&self.data.cFileName); + OsString::from_wide(filename) + } + + pub fn file_type(&self) -> io::Result { + Ok(FileType::new( + self.data.dwFileAttributes, + /* reparse_tag = */ self.data.dwReserved0, + )) + } + + pub fn metadata(&self) -> io::Result { + Ok(self.data.into()) + } +} + +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, + access_mode: None, + share_mode: c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE, + attributes: 0, + security_qos_flags: 0, + security_attributes: ptr::null_mut(), + } + } + + 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: u32) { + self.custom_flags = flags; + } + pub fn access_mode(&mut self, access_mode: u32) { + self.access_mode = Some(access_mode); + } + pub fn share_mode(&mut self, share_mode: u32) { + self.share_mode = share_mode; + } + pub fn attributes(&mut self, attrs: u32) { + self.attributes = attrs; + } + pub fn security_qos_flags(&mut self, flags: u32) { + // We have to set `SECURITY_SQOS_PRESENT` here, because one of the valid flags we can + // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on. + self.security_qos_flags = flags | c::SECURITY_SQOS_PRESENT; + } + pub fn security_attributes(&mut self, attrs: *mut c::SECURITY_ATTRIBUTES) { + self.security_attributes = attrs; + } + + fn get_access_mode(&self) -> io::Result { + match (self.read, self.write, self.append, self.access_mode) { + (.., Some(mode)) => Ok(mode), + (true, false, false, None) => Ok(c::GENERIC_READ), + (false, true, false, None) => Ok(c::GENERIC_WRITE), + (true, true, false, None) => Ok(c::GENERIC_READ | c::GENERIC_WRITE), + (false, _, true, None) => Ok(c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA), + (true, _, true, None) => { + Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA)) + } + (false, false, false, None) => { + Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)) + } + } + } + + 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(c::ERROR_INVALID_PARAMETER as i32)); + } + } + (_, true) => { + if self.truncate && !self.create_new { + return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)); + } + } + } + + Ok(match (self.create, self.truncate, self.create_new) { + (false, false, false) => c::OPEN_EXISTING, + (true, false, false) => c::OPEN_ALWAYS, + (false, true, false) => c::TRUNCATE_EXISTING, + // `CREATE_ALWAYS` has weird semantics so we emulate it using + // `OPEN_ALWAYS` and a manual truncation step. See #115745. + (true, true, false) => c::OPEN_ALWAYS, + (_, _, true) => c::CREATE_NEW, + }) + } + + fn get_flags_and_attributes(&self) -> u32 { + self.custom_flags + | self.attributes + | self.security_qos_flags + | if self.create_new { c::FILE_FLAG_OPEN_REPARSE_POINT } else { 0 } + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + let path = maybe_verbatim(path)?; + // SAFETY: maybe_verbatim returns null-terminated strings + let path = unsafe { WCStr::from_wchars_with_null_unchecked(&path) }; + Self::open_native(&path, opts) + } + + fn open_native(path: &WCStr, opts: &OpenOptions) -> io::Result { + let creation = opts.get_creation_mode()?; + let handle = unsafe { + c::CreateFileW( + path.as_ptr(), + opts.get_access_mode()?, + opts.share_mode, + opts.security_attributes, + creation, + opts.get_flags_and_attributes(), + ptr::null_mut(), + ) + }; + let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) }; + if let Ok(handle) = OwnedHandle::try_from(handle) { + // Manual truncation. See #115745. + if opts.truncate + && creation == c::OPEN_ALWAYS + && api::get_last_error() == WinError::ALREADY_EXISTS + { + // This first tries `FileAllocationInfo` but falls back to + // `FileEndOfFileInfo` in order to support WINE. + // If WINE gains support for FileAllocationInfo, we should + // remove the fallback. + let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 }; + set_file_information_by_handle(handle.as_raw_handle(), &alloc) + .or_else(|_| { + let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 }; + set_file_information_by_handle(handle.as_raw_handle(), &eof) + }) + .io_result()?; + } + Ok(File { handle: Handle::from_inner(handle) }) + } else { + Err(Error::last_os_error()) + } + } + + pub fn fsync(&self) -> io::Result<()> { + cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?; + Ok(()) + } + + pub fn datasync(&self) -> io::Result<()> { + self.fsync() + } + + fn acquire_lock(&self, flags: c::LOCK_FILE_FLAGS) -> io::Result<()> { + unsafe { + let mut overlapped: c::OVERLAPPED = mem::zeroed(); + let event = c::CreateEventW(ptr::null_mut(), c::FALSE, c::FALSE, ptr::null()); + if event.is_null() { + return Err(io::Error::last_os_error()); + } + overlapped.hEvent = event; + let lock_result = cvt(c::LockFileEx( + self.handle.as_raw_handle(), + flags, + 0, + u32::MAX, + u32::MAX, + &mut overlapped, + )); + + let final_result = match lock_result { + Ok(_) => Ok(()), + Err(err) => { + if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) { + // Wait for the lock to be acquired, and get the lock operation status. + // This can happen asynchronously, if the file handle was opened for async IO + let mut bytes_transferred = 0; + cvt(c::GetOverlappedResult( + self.handle.as_raw_handle(), + &mut overlapped, + &mut bytes_transferred, + c::TRUE, + )) + .map(|_| ()) + } else { + Err(err) + } + } + }; + c::CloseHandle(overlapped.hEvent); + final_result + } + } + + pub fn lock(&self) -> io::Result<()> { + self.acquire_lock(c::LOCKFILE_EXCLUSIVE_LOCK) + } + + pub fn lock_shared(&self) -> io::Result<()> { + self.acquire_lock(0) + } + + pub fn try_lock(&self) -> Result<(), TryLockError> { + let result = cvt(unsafe { + let mut overlapped = mem::zeroed(); + c::LockFileEx( + self.handle.as_raw_handle(), + c::LOCKFILE_EXCLUSIVE_LOCK | c::LOCKFILE_FAIL_IMMEDIATELY, + 0, + u32::MAX, + u32::MAX, + &mut overlapped, + ) + }); + + match result { + Ok(_) => Ok(()), + Err(err) + if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) + || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => + { + Err(TryLockError::WouldBlock) + } + Err(err) => Err(TryLockError::Error(err)), + } + } + + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + let result = cvt(unsafe { + let mut overlapped = mem::zeroed(); + c::LockFileEx( + self.handle.as_raw_handle(), + c::LOCKFILE_FAIL_IMMEDIATELY, + 0, + u32::MAX, + u32::MAX, + &mut overlapped, + ) + }); + + match result { + Ok(_) => Ok(()), + Err(err) + if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) + || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => + { + Err(TryLockError::WouldBlock) + } + Err(err) => Err(TryLockError::Error(err)), + } + } + + pub fn unlock(&self) -> io::Result<()> { + // Unlock the handle twice because LockFileEx() allows a file handle to acquire + // both an exclusive and shared lock, in which case the documentation states that: + // "...two unlock operations are necessary to unlock the region; the first unlock operation + // unlocks the exclusive lock, the second unlock operation unlocks the shared lock" + cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) })?; + let result = + cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) }); + match result { + Ok(_) => Ok(()), + Err(err) if err.raw_os_error() == Some(c::ERROR_NOT_LOCKED as i32) => Ok(()), + Err(err) => Err(err), + } + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 }; + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() + } + + #[cfg(not(target_vendor = "uwp"))] + pub fn file_attr(&self) -> io::Result { + unsafe { + let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed(); + cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?; + let mut reparse_tag = 0; + if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed(); + cvt(c::GetFileInformationByHandleEx( + self.handle.as_raw_handle(), + c::FileAttributeTagInfo, + (&raw mut attr_tag).cast(), + size_of::().try_into().unwrap(), + ))?; + if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + reparse_tag = attr_tag.ReparseTag; + } + } + Ok(FileAttr { + attributes: info.dwFileAttributes, + creation_time: info.ftCreationTime, + last_access_time: info.ftLastAccessTime, + last_write_time: info.ftLastWriteTime, + change_time: None, // Only available in FILE_BASIC_INFO + file_size: (info.nFileSizeLow as u64) | ((info.nFileSizeHigh as u64) << 32), + reparse_tag, + volume_serial_number: Some(info.dwVolumeSerialNumber), + number_of_links: Some(info.nNumberOfLinks), + file_index: Some( + (info.nFileIndexLow as u64) | ((info.nFileIndexHigh as u64) << 32), + ), + }) + } + } + + #[cfg(target_vendor = "uwp")] + pub fn file_attr(&self) -> io::Result { + unsafe { + let mut info: c::FILE_BASIC_INFO = mem::zeroed(); + let size = size_of_val(&info); + cvt(c::GetFileInformationByHandleEx( + self.handle.as_raw_handle(), + c::FileBasicInfo, + (&raw mut info) as *mut c_void, + size as u32, + ))?; + let mut attr = FileAttr { + attributes: info.FileAttributes, + creation_time: c::FILETIME { + dwLowDateTime: info.CreationTime as u32, + dwHighDateTime: (info.CreationTime >> 32) as u32, + }, + last_access_time: c::FILETIME { + dwLowDateTime: info.LastAccessTime as u32, + dwHighDateTime: (info.LastAccessTime >> 32) as u32, + }, + last_write_time: c::FILETIME { + dwLowDateTime: info.LastWriteTime as u32, + dwHighDateTime: (info.LastWriteTime >> 32) as u32, + }, + change_time: Some(c::FILETIME { + dwLowDateTime: info.ChangeTime as u32, + dwHighDateTime: (info.ChangeTime >> 32) as u32, + }), + file_size: 0, + reparse_tag: 0, + volume_serial_number: None, + number_of_links: None, + file_index: None, + }; + let mut info: c::FILE_STANDARD_INFO = mem::zeroed(); + let size = size_of_val(&info); + cvt(c::GetFileInformationByHandleEx( + self.handle.as_raw_handle(), + c::FileStandardInfo, + (&raw mut info) as *mut c_void, + size as u32, + ))?; + attr.file_size = info.AllocationSize as u64; + attr.number_of_links = Some(info.NumberOfLinks); + if attr.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed(); + cvt(c::GetFileInformationByHandleEx( + self.handle.as_raw_handle(), + c::FileAttributeTagInfo, + (&raw mut attr_tag).cast(), + size_of::().try_into().unwrap(), + ))?; + if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + attr.reparse_tag = attr_tag.ReparseTag; + } + } + Ok(attr) + } + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.handle.read(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.handle.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.handle.is_read_vectored() + } + + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.handle.read_at(buf, offset) + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.handle.read_buf(cursor) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.handle.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.handle.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.handle.is_write_vectored() + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + self.handle.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, `SetFilePointerEx` reinterprets this + // integer as `u64`. + SeekFrom::Start(n) => (c::FILE_BEGIN, n as i64), + SeekFrom::End(n) => (c::FILE_END, n), + SeekFrom::Current(n) => (c::FILE_CURRENT, n), + }; + let pos = pos as i64; + let mut newpos = 0; + cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?; + Ok(newpos as u64) + } + + pub fn tell(&self) -> io::Result { + self.seek(SeekFrom::Current(0)) + } + + pub fn duplicate(&self) -> io::Result { + Ok(Self { handle: self.handle.try_clone()? }) + } + + // NB: returned pointer is derived from `space`, and has provenance to + // match. A raw pointer is returned rather than a reference in order to + // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`. + fn reparse_point( + &self, + space: &mut Align8<[MaybeUninit]>, + ) -> io::Result<(u32, *mut c::REPARSE_DATA_BUFFER)> { + unsafe { + let mut bytes = 0; + cvt({ + // Grab this in advance to avoid it invalidating the pointer + // we get from `space.0.as_mut_ptr()`. + let len = space.0.len(); + c::DeviceIoControl( + self.handle.as_raw_handle(), + c::FSCTL_GET_REPARSE_POINT, + ptr::null_mut(), + 0, + space.0.as_mut_ptr().cast(), + len as u32, + &mut bytes, + ptr::null_mut(), + ) + })?; + const _: () = assert!(align_of::() <= 8); + Ok((bytes, space.0.as_mut_ptr().cast::())) + } + } + + fn readlink(&self) -> io::Result { + let mut space = + Align8([MaybeUninit::::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]); + let (_bytes, buf) = self.reparse_point(&mut space)?; + unsafe { + let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag { + c::IO_REPARSE_TAG_SYMLINK => { + let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = (&raw mut (*buf).rest).cast(); + assert!(info.is_aligned()); + ( + (&raw mut (*info).PathBuffer).cast::(), + (*info).SubstituteNameOffset / 2, + (*info).SubstituteNameLength / 2, + (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0, + ) + } + c::IO_REPARSE_TAG_MOUNT_POINT => { + let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = (&raw mut (*buf).rest).cast(); + assert!(info.is_aligned()); + ( + (&raw mut (*info).PathBuffer).cast::(), + (*info).SubstituteNameOffset / 2, + (*info).SubstituteNameLength / 2, + false, + ) + } + _ => { + return Err(io::const_error!( + io::ErrorKind::Uncategorized, + "Unsupported reparse point type", + )); + } + }; + let subst_ptr = path_buffer.add(subst_off.into()); + let subst = slice::from_raw_parts_mut(subst_ptr, subst_len as usize); + // Absolute paths start with an NT internal namespace prefix `\??\` + // We should not let it leak through. + if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) { + // Turn `\??\` into `\\?\` (a verbatim path). + subst[1] = b'\\' as u16; + // Attempt to convert to a more user-friendly path. + let user = crate::sys::args::from_wide_to_user_path( + subst.iter().copied().chain([0]).collect(), + )?; + Ok(PathBuf::from(OsString::from_wide(user.strip_suffix(&[0]).unwrap_or(&user)))) + } else { + Ok(PathBuf::from(OsString::from_wide(subst))) + } + } + } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + let info = c::FILE_BASIC_INFO { + CreationTime: 0, + LastAccessTime: 0, + LastWriteTime: 0, + ChangeTime: 0, + FileAttributes: perm.attrs, + }; + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() + } + + pub fn set_times(&self, times: FileTimes) -> io::Result<()> { + let is_zero = |t: c::FILETIME| t.dwLowDateTime == 0 && t.dwHighDateTime == 0; + if times.accessed.map_or(false, is_zero) + || times.modified.map_or(false, is_zero) + || times.created.map_or(false, is_zero) + { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + "cannot set file timestamp to 0", + )); + } + let is_max = |t: c::FILETIME| t.dwLowDateTime == u32::MAX && t.dwHighDateTime == u32::MAX; + if times.accessed.map_or(false, is_max) + || times.modified.map_or(false, is_max) + || times.created.map_or(false, is_max) + { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + "cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF", + )); + } + cvt(unsafe { + let created = + times.created.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); + let accessed = + times.accessed.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); + let modified = + times.modified.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); + c::SetFileTime(self.as_raw_handle(), created, accessed, modified) + })?; + Ok(()) + } + + /// Gets only basic file information such as attributes and file times. + fn basic_info(&self) -> io::Result { + unsafe { + let mut info: c::FILE_BASIC_INFO = mem::zeroed(); + let size = size_of_val(&info); + cvt(c::GetFileInformationByHandleEx( + self.handle.as_raw_handle(), + c::FileBasicInfo, + (&raw mut info) as *mut c_void, + size as u32, + ))?; + Ok(info) + } + } + + /// Deletes the file, consuming the file handle to ensure the delete occurs + /// as immediately as possible. + /// This attempts to use `posix_delete` but falls back to `win32_delete` + /// if that is not supported by the filesystem. + #[allow(unused)] + fn delete(self) -> Result<(), WinError> { + // If POSIX delete is not supported for this filesystem then fallback to win32 delete. + match self.posix_delete() { + Err(WinError::INVALID_PARAMETER) + | Err(WinError::NOT_SUPPORTED) + | Err(WinError::INVALID_FUNCTION) => self.win32_delete(), + result => result, + } + } + + /// Delete using POSIX semantics. + /// + /// Files will be deleted as soon as the handle is closed. This is supported + /// for Windows 10 1607 (aka RS1) and later. However some filesystem + /// drivers will not support it even then, e.g. FAT32. + /// + /// If the operation is not supported for this filesystem or OS version + /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`. + #[allow(unused)] + fn posix_delete(&self) -> Result<(), WinError> { + let info = c::FILE_DISPOSITION_INFO_EX { + Flags: c::FILE_DISPOSITION_FLAG_DELETE + | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS + | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE, + }; + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info) + } + + /// Delete a file using win32 semantics. The file won't actually be deleted + /// until all file handles are closed. However, marking a file for deletion + /// will prevent anyone from opening a new handle to the file. + #[allow(unused)] + fn win32_delete(&self) -> Result<(), WinError> { + let info = c::FILE_DISPOSITION_INFO { DeleteFile: true }; + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info) + } + + /// Fill the given buffer with as many directory entries as will fit. + /// This will remember its position and continue from the last call unless + /// `restart` is set to `true`. + /// + /// The returned bool indicates if there are more entries or not. + /// It is an error if `self` is not a directory. + /// + /// # Symlinks and other reparse points + /// + /// On Windows a file is either a directory or a non-directory. + /// A symlink directory is simply an empty directory with some "reparse" metadata attached. + /// So if you open a link (not its target) and iterate the directory, + /// you will always iterate an empty directory regardless of the target. + #[allow(unused)] + fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> Result { + let class = + if restart { c::FileIdBothDirectoryRestartInfo } else { c::FileIdBothDirectoryInfo }; + + unsafe { + let result = c::GetFileInformationByHandleEx( + self.as_raw_handle(), + class, + buffer.as_mut_ptr().cast(), + buffer.capacity() as _, + ); + if result == 0 { + let err = api::get_last_error(); + if err.code == c::ERROR_NO_MORE_FILES { Ok(false) } else { Err(err) } + } else { + Ok(true) + } + } + } +} + +/// A buffer for holding directory entries. +struct DirBuff { + buffer: Box; Self::BUFFER_SIZE]>>, +} +impl DirBuff { + const BUFFER_SIZE: usize = 1024; + fn new() -> Self { + Self { + // Safety: `Align8<[MaybeUninit; N]>` does not need + // initialization. + buffer: unsafe { Box::new_uninit().assume_init() }, + } + } + fn capacity(&self) -> usize { + self.buffer.0.len() + } + fn as_mut_ptr(&mut self) -> *mut u8 { + self.buffer.0.as_mut_ptr().cast() + } + /// Returns a `DirBuffIter`. + fn iter(&self) -> DirBuffIter<'_> { + DirBuffIter::new(self) + } +} +impl AsRef<[MaybeUninit]> for DirBuff { + fn as_ref(&self) -> &[MaybeUninit] { + &self.buffer.0 + } +} + +/// An iterator over entries stored in a `DirBuff`. +/// +/// Currently only returns file names (UTF-16 encoded). +struct DirBuffIter<'a> { + buffer: Option<&'a [MaybeUninit]>, + cursor: usize, +} +impl<'a> DirBuffIter<'a> { + fn new(buffer: &'a DirBuff) -> Self { + Self { buffer: Some(buffer.as_ref()), cursor: 0 } + } +} +impl<'a> Iterator for DirBuffIter<'a> { + type Item = (Cow<'a, [u16]>, bool); + fn next(&mut self) -> Option { + let buffer = &self.buffer?[self.cursor..]; + + // Get the name and next entry from the buffer. + // SAFETY: + // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last + // field (the file name) is unsized. So an offset has to be used to + // get the file name slice. + // - The OS has guaranteed initialization of the fields of + // `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least + // `FileNameLength` bytes) + let (name, is_directory, next_entry) = unsafe { + let info = buffer.as_ptr().cast::(); + // While this is guaranteed to be aligned in documentation for + // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info + // it does not seem that reality is so kind, and assuming this + // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530) + // presumably, this can be blamed on buggy filesystem drivers, but who knows. + let next_entry = (&raw const (*info).NextEntryOffset).read_unaligned() as usize; + let length = (&raw const (*info).FileNameLength).read_unaligned() as usize; + let attrs = (&raw const (*info).FileAttributes).read_unaligned(); + let name = from_maybe_unaligned( + (&raw const (*info).FileName).cast::(), + length / size_of::(), + ); + let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0; + + (name, is_directory, next_entry) + }; + + if next_entry == 0 { + self.buffer = None + } else { + self.cursor += next_entry + } + + // Skip `.` and `..` pseudo entries. + const DOT: u16 = b'.' as u16; + match &name[..] { + [DOT] | [DOT, DOT] => self.next(), + _ => Some((name, is_directory)), + } + } +} + +unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> { + unsafe { + if p.is_aligned() { + Cow::Borrowed(crate::slice::from_raw_parts(p, len)) + } else { + Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect()) + } + } +} + +impl AsInner for File { + #[inline] + fn as_inner(&self) -> &Handle { + &self.handle + } +} + +impl IntoInner for File { + fn into_inner(self) -> Handle { + self.handle + } +} + +impl FromInner for File { + fn from_inner(handle: Handle) -> File { + File { handle } + } +} + +impl AsHandle for File { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.as_inner().as_handle() + } +} + +impl AsRawHandle for File { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().as_raw_handle() + } +} + +impl IntoRawHandle for File { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_raw_handle() + } +} + +impl FromRawHandle for File { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + unsafe { + Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) } + } + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // FIXME(#24570): add more info here (e.g., mode) + let mut b = f.debug_struct("File"); + b.field("handle", &self.handle.as_raw_handle()); + if let Ok(path) = get_path(self) { + b.field("path", &path); + } + b.finish() + } +} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.file_size + } + + pub fn perm(&self) -> FilePermissions { + FilePermissions { attrs: self.attributes } + } + + pub fn attrs(&self) -> u32 { + self.attributes + } + + pub fn file_type(&self) -> FileType { + FileType::new(self.attributes, self.reparse_tag) + } + + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from(self.last_write_time)) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from(self.last_access_time)) + } + + pub fn created(&self) -> io::Result { + Ok(SystemTime::from(self.creation_time)) + } + + pub fn modified_u64(&self) -> u64 { + to_u64(&self.last_write_time) + } + + pub fn accessed_u64(&self) -> u64 { + to_u64(&self.last_access_time) + } + + pub fn created_u64(&self) -> u64 { + to_u64(&self.creation_time) + } + + pub fn changed_u64(&self) -> Option { + self.change_time.as_ref().map(|c| to_u64(c)) + } + + pub fn volume_serial_number(&self) -> Option { + self.volume_serial_number + } + + pub fn number_of_links(&self) -> Option { + self.number_of_links + } + + pub fn file_index(&self) -> Option { + self.file_index + } +} +impl From for FileAttr { + fn from(wfd: c::WIN32_FIND_DATAW) -> Self { + FileAttr { + attributes: wfd.dwFileAttributes, + creation_time: wfd.ftCreationTime, + last_access_time: wfd.ftLastAccessTime, + last_write_time: wfd.ftLastWriteTime, + change_time: None, + file_size: ((wfd.nFileSizeHigh as u64) << 32) | (wfd.nFileSizeLow as u64), + reparse_tag: if wfd.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + // reserved unless this is a reparse point + wfd.dwReserved0 + } else { + 0 + }, + volume_serial_number: None, + number_of_links: None, + file_index: None, + } + } +} + +fn to_u64(ft: &c::FILETIME) -> u64 { + (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + self.attrs & c::FILE_ATTRIBUTE_READONLY != 0 + } + + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + self.attrs |= c::FILE_ATTRIBUTE_READONLY; + } else { + self.attrs &= !c::FILE_ATTRIBUTE_READONLY; + } + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, t: SystemTime) { + self.accessed = Some(t.into_inner()); + } + + pub fn set_modified(&mut self, t: SystemTime) { + self.modified = Some(t.into_inner()); + } + + pub fn set_created(&mut self, t: SystemTime) { + self.created = Some(t.into_inner()); + } +} + +impl FileType { + fn new(attributes: u32, reparse_tag: u32) -> FileType { + let is_directory = attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0; + let is_symlink = { + let is_reparse_point = attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0; + let is_reparse_tag_name_surrogate = reparse_tag & 0x20000000 != 0; + is_reparse_point && is_reparse_tag_name_surrogate + }; + FileType { is_directory, is_symlink } + } + pub fn is_dir(&self) -> bool { + !self.is_symlink && self.is_directory + } + pub fn is_file(&self) -> bool { + !self.is_symlink && !self.is_directory + } + pub fn is_symlink(&self) -> bool { + self.is_symlink + } + pub fn is_symlink_dir(&self) -> bool { + self.is_symlink && self.is_directory + } + pub fn is_symlink_file(&self) -> bool { + self.is_symlink && !self.is_directory + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let p = maybe_verbatim(p)?; + cvt(unsafe { c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) })?; + Ok(()) + } +} + +pub fn readdir(p: &Path) -> io::Result { + // We push a `*` to the end of the path which cause the empty path to be + // treated as the current directory. So, for consistency with other platforms, + // we explicitly error on the empty path. + if p.as_os_str().is_empty() { + // Return an error code consistent with other ways of opening files. + // E.g. fs::metadata or File::open. + return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32)); + } + let root = p.to_path_buf(); + let star = p.join("*"); + let path = maybe_verbatim(&star)?; + + unsafe { + let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed(); + // this is like FindFirstFileW (see https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfileexw), + // but with FindExInfoBasic it should skip filling WIN32_FIND_DATAW.cAlternateFileName + // (see https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-win32_find_dataw) + // (which will be always null string value and currently unused) and should be faster. + // + // We can pass FIND_FIRST_EX_LARGE_FETCH to dwAdditionalFlags to speed up things more, + // but as we don't know user's use profile of this function, lets be conservative. + let find_handle = c::FindFirstFileExW( + path.as_ptr(), + c::FindExInfoBasic, + &mut wfd as *mut _ as _, + c::FindExSearchNameMatch, + ptr::null(), + 0, + ); + + if find_handle != c::INVALID_HANDLE_VALUE { + Ok(ReadDir { + handle: Some(FindNextFileHandle(find_handle)), + root: Arc::new(root), + first: Some(wfd), + }) + } else { + // The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileExW` function + // if no matching files can be found, but not necessarily that the path to find the + // files in does not exist. + // + // Hence, a check for whether the path to search in exists is added when the last + // os error returned by Windows is `ERROR_FILE_NOT_FOUND` to handle this scenario. + // If that is the case, an empty `ReadDir` iterator is returned as it returns `None` + // in the initial `.next()` invocation because `ERROR_NO_MORE_FILES` would have been + // returned by the `FindNextFileW` function. + // + // See issue #120040: https://github.com/rust-lang/rust/issues/120040. + let last_error = api::get_last_error(); + if last_error == WinError::FILE_NOT_FOUND { + return Ok(ReadDir { handle: None, root: Arc::new(root), first: None }); + } + + // Just return the error constructed from the raw OS error if the above is not the case. + // + // Note: `ERROR_PATH_NOT_FOUND` would have been returned by the `FindFirstFileExW` function + // when the path to search in does not exist in the first place. + Err(Error::from_raw_os_error(last_error.code as i32)) + } + } +} + +pub fn unlink(path: &WCStr) -> io::Result<()> { + if unsafe { c::DeleteFileW(path.as_ptr()) } == 0 { + let err = api::get_last_error(); + // if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove + // the file while ignoring the readonly attribute. + // This is accomplished by calling the `posix_delete` function on an open file handle. + if err == WinError::ACCESS_DENIED { + let mut opts = OpenOptions::new(); + opts.access_mode(c::DELETE); + opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT); + if let Ok(f) = File::open_native(&path, &opts) { + if f.posix_delete().is_ok() { + return Ok(()); + } + } + } + // return the original error if any of the above fails. + Err(io::Error::from_raw_os_error(err.code as i32)) + } else { + Ok(()) + } +} + +pub fn rename(old: &WCStr, new: &WCStr) -> io::Result<()> { + if unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) } == 0 { + let err = api::get_last_error(); + // if `MoveFileExW` fails with ERROR_ACCESS_DENIED then try to move + // the file while ignoring the readonly attribute. + // This is accomplished by calling `SetFileInformationByHandle` with `FileRenameInfoEx`. + if err == WinError::ACCESS_DENIED { + let mut opts = OpenOptions::new(); + opts.access_mode(c::DELETE); + opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); + let Ok(f) = File::open_native(&old, &opts) else { return Err(err).io_result() }; + + // Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation` + // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size. + let Ok(new_len_without_nul_in_bytes): Result = + ((new.count_bytes() - 1) * 2).try_into() + else { + return Err(err).io_result(); + }; + let offset: u32 = offset_of!(c::FILE_RENAME_INFO, FileName).try_into().unwrap(); + let struct_size = offset + new_len_without_nul_in_bytes + 2; + let layout = + Layout::from_size_align(struct_size as usize, align_of::()) + .unwrap(); + + // SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename. + let file_rename_info; + unsafe { + file_rename_info = alloc(layout).cast::(); + if file_rename_info.is_null() { + return Err(io::ErrorKind::OutOfMemory.into()); + } + + (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 { + Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS + | c::FILE_RENAME_FLAG_POSIX_SEMANTICS, + }); + + (&raw mut (*file_rename_info).RootDirectory).write(ptr::null_mut()); + // Don't include the NULL in the size + (&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes); + + new.as_ptr().copy_to_nonoverlapping( + (&raw mut (*file_rename_info).FileName).cast::(), + new.count_bytes(), + ); + } + + let result = unsafe { + c::SetFileInformationByHandle( + f.as_raw_handle(), + c::FileRenameInfoEx, + file_rename_info.cast::(), + struct_size, + ) + }; + unsafe { dealloc(file_rename_info.cast::(), layout) }; + if result == 0 { + if api::get_last_error() == WinError::DIR_NOT_EMPTY { + return Err(WinError::DIR_NOT_EMPTY).io_result(); + } else { + return Err(err).io_result(); + } + } + } else { + return Err(err).io_result(); + } + } + Ok(()) +} + +pub fn rmdir(p: &WCStr) -> io::Result<()> { + cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?; + Ok(()) +} + +pub fn remove_dir_all(path: &WCStr) -> io::Result<()> { + // Open a file or directory without following symlinks. + let mut opts = OpenOptions::new(); + opts.access_mode(c::FILE_LIST_DIRECTORY); + // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories. + // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target. + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); + let file = File::open_native(path, &opts)?; + + // Test if the file is not a directory or a symlink to a directory. + if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 { + return Err(io::Error::from_raw_os_error(c::ERROR_DIRECTORY as _)); + } + + // Remove the directory and all its contents. + remove_dir_all_iterative(file).io_result() +} + +pub fn readlink(path: &WCStr) -> io::Result { + // Open the link with no access mode, instead of generic read. + // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so + // this is needed for a common case. + let mut opts = OpenOptions::new(); + opts.access_mode(0); + opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); + let file = File::open_native(&path, &opts)?; + file.readlink() +} + +pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { + symlink_inner(original, link, false) +} + +pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()> { + let original = to_u16s(original)?; + let link = maybe_verbatim(link)?; + let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 }; + // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10 + // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the + // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be + // added to dwFlags to opt into this behavior. + let result = cvt(unsafe { + c::CreateSymbolicLinkW( + link.as_ptr(), + original.as_ptr(), + flags | c::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, + ) as c::BOOL + }); + if let Err(err) = result { + if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as i32) { + // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, + // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag. + cvt(unsafe { + c::CreateSymbolicLinkW(link.as_ptr(), original.as_ptr(), flags) as c::BOOL + })?; + } else { + return Err(err); + } + } + Ok(()) +} + +#[cfg(not(target_vendor = "uwp"))] +pub fn link(original: &WCStr, link: &WCStr) -> io::Result<()> { + cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?; + Ok(()) +} + +#[cfg(target_vendor = "uwp")] +pub fn link(_original: &WCStr, _link: &WCStr) -> io::Result<()> { + return Err(io::const_error!(io::ErrorKind::Unsupported, "hard link are not supported on UWP")); +} + +pub fn stat(path: &WCStr) -> io::Result { + match metadata(path, ReparsePoint::Follow) { + Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => { + if let Ok(attrs) = lstat(path) { + if !attrs.file_type().is_symlink() { + return Ok(attrs); + } + } + Err(err) + } + result => result, + } +} + +pub fn lstat(path: &WCStr) -> io::Result { + metadata(path, ReparsePoint::Open) +} + +#[repr(u32)] +#[derive(Clone, Copy, PartialEq, Eq)] +enum ReparsePoint { + Follow = 0, + Open = c::FILE_FLAG_OPEN_REPARSE_POINT, +} +impl ReparsePoint { + fn as_flag(self) -> u32 { + self as u32 + } +} + +fn metadata(path: &WCStr, reparse: ReparsePoint) -> io::Result { + let mut opts = OpenOptions::new(); + // No read or write permissions are necessary + opts.access_mode(0); + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag()); + + // Attempt to open the file normally. + // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileExW`. + // If the fallback fails for any reason we return the original error. + match File::open_native(&path, &opts) { + Ok(file) => file.file_attr(), + Err(e) + if [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)] + .contains(&e.raw_os_error()) => + { + // `ERROR_ACCESS_DENIED` is returned when the user doesn't have permission for the resource. + // One such example is `System Volume Information` as default but can be created as well + // `ERROR_SHARING_VIOLATION` will almost never be returned. + // Usually if a file is locked you can still read some metadata. + // However, there are special system files, such as + // `C:\hiberfil.sys`, that are locked in a way that denies even that. + unsafe { + // `FindFirstFileExW` accepts wildcard file names. + // Fortunately wildcards are not valid file names and + // `ERROR_SHARING_VIOLATION` means the file exists (but is locked) + // therefore it's safe to assume the file name given does not + // include wildcards. + let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed(); + let handle = c::FindFirstFileExW( + path.as_ptr(), + c::FindExInfoBasic, + &mut wfd as *mut _ as _, + c::FindExSearchNameMatch, + ptr::null(), + 0, + ); + + if handle == c::INVALID_HANDLE_VALUE { + // This can fail if the user does not have read access to the + // directory. + Err(e) + } else { + // We no longer need the find handle. + c::FindClose(handle); + + // `FindFirstFileExW` reads the cached file information from the + // directory. The downside is that this metadata may be outdated. + let attrs = FileAttr::from(wfd); + if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() { + Err(e) + } else { + Ok(attrs) + } + } + } + } + Err(e) => Err(e), + } +} + +pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> { + unsafe { + cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?; + Ok(()) + } +} + +fn get_path(f: &File) -> io::Result { + fill_utf16_buf( + |buf, sz| unsafe { + c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS) + }, + |buf| PathBuf::from(OsString::from_wide(buf)), + ) +} + +pub fn canonicalize(p: &WCStr) -> io::Result { + let mut opts = OpenOptions::new(); + // No read or write permissions are necessary + opts.access_mode(0); + // This flag is so we can open directories too + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); + let f = File::open_native(p, &opts)?; + get_path(&f) +} + +pub fn copy(from: &WCStr, to: &WCStr) -> io::Result { + unsafe extern "system" fn callback( + _TotalFileSize: i64, + _TotalBytesTransferred: i64, + _StreamSize: i64, + StreamBytesTransferred: i64, + dwStreamNumber: u32, + _dwCallbackReason: u32, + _hSourceFile: c::HANDLE, + _hDestinationFile: c::HANDLE, + lpData: *const c_void, + ) -> u32 { + unsafe { + if dwStreamNumber == 1 { + *(lpData as *mut i64) = StreamBytesTransferred; + } + c::PROGRESS_CONTINUE + } + } + let mut size = 0i64; + cvt(unsafe { + c::CopyFileExW( + from.as_ptr(), + to.as_ptr(), + Some(callback), + (&raw mut size) as *mut _, + ptr::null_mut(), + 0, + ) + })?; + Ok(size as u64) +} + +pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { + // Create and open a new directory in one go. + let mut opts = OpenOptions::new(); + opts.create_new(true); + opts.write(true); + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_POSIX_SEMANTICS); + opts.attributes(c::FILE_ATTRIBUTE_DIRECTORY); + + let d = File::open(link, &opts)?; + + // We need to get an absolute, NT-style path. + let path_bytes = original.as_os_str().as_encoded_bytes(); + let abs_path: Vec = if path_bytes.starts_with(br"\\?\") || path_bytes.starts_with(br"\??\") + { + // It's already an absolute path, we just need to convert the prefix to `\??\` + let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&path_bytes[4..]) }; + r"\??\".encode_utf16().chain(bytes.encode_wide()).collect() + } else { + // Get an absolute path and then convert the prefix to `\??\` + let abs_path = crate::path::absolute(original)?.into_os_string().into_encoded_bytes(); + if abs_path.len() > 0 && abs_path[1..].starts_with(br":\") { + let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path) }; + r"\??\".encode_utf16().chain(bytes.encode_wide()).collect() + } else if abs_path.starts_with(br"\\.\") { + let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[4..]) }; + r"\??\".encode_utf16().chain(bytes.encode_wide()).collect() + } else if abs_path.starts_with(br"\\") { + let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[2..]) }; + r"\??\UNC\".encode_utf16().chain(bytes.encode_wide()).collect() + } else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "path is not valid")); + } + }; + // Defined inline so we don't have to mess about with variable length buffer. + #[repr(C)] + pub struct MountPointBuffer { + ReparseTag: u32, + ReparseDataLength: u16, + Reserved: u16, + SubstituteNameOffset: u16, + SubstituteNameLength: u16, + PrintNameOffset: u16, + PrintNameLength: u16, + PathBuffer: [MaybeUninit; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize], + } + let data_len = 12 + (abs_path.len() * 2); + if data_len > u16::MAX as usize { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "`original` path is too long")); + } + let data_len = data_len as u16; + let mut header = MountPointBuffer { + ReparseTag: c::IO_REPARSE_TAG_MOUNT_POINT, + ReparseDataLength: data_len, + Reserved: 0, + SubstituteNameOffset: 0, + SubstituteNameLength: (abs_path.len() * 2) as u16, + PrintNameOffset: ((abs_path.len() + 1) * 2) as u16, + PrintNameLength: 0, + PathBuffer: [MaybeUninit::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize], + }; + unsafe { + let ptr = header.PathBuffer.as_mut_ptr(); + ptr.copy_from(abs_path.as_ptr().cast::>(), abs_path.len()); + + let mut ret = 0; + cvt(c::DeviceIoControl( + d.as_raw_handle(), + c::FSCTL_SET_REPARSE_POINT, + (&raw const header).cast::(), + data_len as u32 + 8, + ptr::null_mut(), + 0, + &mut ret, + ptr::null_mut(), + )) + .map(drop) + } +} + +// Try to see if a file exists but, unlike `exists`, report I/O errors. +pub fn exists(path: &WCStr) -> io::Result { + // Open the file to ensure any symlinks are followed to their target. + let mut opts = OpenOptions::new(); + // No read, write, etc access rights are needed. + opts.access_mode(0); + // Backup semantics enables opening directories as well as files. + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); + match File::open_native(path, &opts) { + Err(e) => match e.kind() { + // The file definitely does not exist + io::ErrorKind::NotFound => Ok(false), + + // `ERROR_SHARING_VIOLATION` means that the file has been locked by + // another process. This is often temporary so we simply report it + // as the file existing. + _ if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => Ok(true), + + // `ERROR_CANT_ACCESS_FILE` means that a file exists but that the + // reparse point could not be handled by `CreateFile`. + // This can happen for special files such as: + // * Unix domain sockets which you need to `connect` to + // * App exec links which require using `CreateProcess` + _ if e.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => Ok(true), + + // Other errors such as `ERROR_ACCESS_DENIED` may indicate that the + // file exists. However, these types of errors are usually more + // permanent so we report them here. + _ => Err(e), + }, + // The file was opened successfully therefore it must exist, + Ok(_) => Ok(true), + } +} diff --git a/library/std/src/sys/pal/windows/fs/remove_dir_all.rs b/library/std/src/sys/fs/windows/remove_dir_all.rs similarity index 97% rename from library/std/src/sys/pal/windows/fs/remove_dir_all.rs rename to library/std/src/sys/fs/windows/remove_dir_all.rs index 9416049da78f8..06734f9e3097b 100644 --- a/library/std/src/sys/pal/windows/fs/remove_dir_all.rs +++ b/library/std/src/sys/fs/windows/remove_dir_all.rs @@ -29,11 +29,11 @@ //! race but we do make a best effort such that it *should* do so. use core::ptr; -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use super::{AsRawHandle, DirBuff, File, FromRawHandle}; use crate::sys::c; -use crate::sys::pal::windows::api::WinError; +use crate::sys::pal::api::WinError; use crate::thread; // The maximum number of times to spin when waiting for deletes to complete. @@ -87,7 +87,7 @@ fn open_link_no_reparse( // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been // tricked into following a symlink. However, it may not be available in // earlier versions of Windows. - static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE); + static ATTRIBUTES: Atomic = AtomicU32::new(c::OBJ_DONT_REPARSE); let result = unsafe { let mut path_str = c::UNICODE_STRING::from_ref(path); @@ -95,7 +95,7 @@ fn open_link_no_reparse( ObjectName: &mut path_str, RootDirectory: parent.as_raw_handle(), Attributes: ATTRIBUTES.load(Ordering::Relaxed), - ..c::OBJECT_ATTRIBUTES::default() + ..c::OBJECT_ATTRIBUTES::with_length() }; let share = c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE; let options = c::FILE_OPEN_REPARSE_POINT | options; diff --git a/library/std/src/sys/io/io_slice/iovec.rs b/library/std/src/sys/io/io_slice/iovec.rs index 072191315f7c5..df56358969a39 100644 --- a/library/std/src/sys/io/io_slice/iovec.rs +++ b/library/std/src/sys/io/io_slice/iovec.rs @@ -1,6 +1,6 @@ #[cfg(target_os = "hermit")] use hermit_abi::iovec; -#[cfg(target_family = "unix")] +#[cfg(any(target_family = "unix", target_os = "trusty"))] use libc::iovec; use crate::ffi::c_void; diff --git a/library/std/src/sys/io/is_terminal/windows.rs b/library/std/src/sys/io/is_terminal/windows.rs index 3ec18fb47b9de..b0c718d71f9f3 100644 --- a/library/std/src/sys/io/is_terminal/windows.rs +++ b/library/std/src/sys/io/is_terminal/windows.rs @@ -1,5 +1,4 @@ use crate::ffi::c_void; -use crate::mem::size_of; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; use crate::sys::c; diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index e00b479109f39..4d0365d42fd9b 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -2,7 +2,7 @@ mod io_slice { cfg_if::cfg_if! { - if #[cfg(any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3"))] { + if #[cfg(any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty"))] { mod iovec; pub use iovec::*; } else if #[cfg(target_os = "windows")] { diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 1032fcba5e2e1..f9a02b522e5e1 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -9,14 +9,21 @@ mod alloc; mod personality; pub mod anonymous_pipe; +pub mod args; pub mod backtrace; pub mod cmath; +pub mod env; +pub mod env_consts; pub mod exit_guard; +pub mod fd; +pub mod fs; pub mod io; pub mod net; pub mod os_str; pub mod path; +pub mod process; pub mod random; +pub mod stdio; pub mod sync; pub mod thread_local; diff --git a/library/std/src/sys/net/connection/socket.rs b/library/std/src/sys/net/connection/socket.rs index ddd74b426158d..7301bde6881a3 100644 --- a/library/std/src/sys/net/connection/socket.rs +++ b/library/std/src/sys/net/connection/socket.rs @@ -59,7 +59,8 @@ cfg_if::cfg_if! { target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "solaris", target_os = "illumos", - target_os = "haiku", target_os = "nto"))] { + target_os = "haiku", target_os = "nto", + target_os = "cygwin"))] { use libc::MSG_NOSIGNAL; } else { const MSG_NOSIGNAL: c_int = 0x0; @@ -154,11 +155,11 @@ fn socket_addr_to_c(addr: &SocketAddr) -> (SocketAddrCRepr, c::socklen_t) { match addr { SocketAddr::V4(a) => { let sockaddr = SocketAddrCRepr { v4: socket_addr_v4_to_c(a) }; - (sockaddr, mem::size_of::() as c::socklen_t) + (sockaddr, size_of::() as c::socklen_t) } SocketAddr::V6(a) => { let sockaddr = SocketAddrCRepr { v6: socket_addr_v6_to_c(a) }; - (sockaddr, mem::size_of::() as c::socklen_t) + (sockaddr, size_of::() as c::socklen_t) } } } @@ -169,13 +170,13 @@ unsafe fn socket_addr_from_c( ) -> io::Result { match (*storage).ss_family as c_int { c::AF_INET => { - assert!(len >= mem::size_of::()); + assert!(len >= size_of::()); Ok(SocketAddr::V4(socket_addr_v4_from_c(unsafe { *(storage as *const _ as *const c::sockaddr_in) }))) } c::AF_INET6 => { - assert!(len >= mem::size_of::()); + assert!(len >= size_of::()); Ok(SocketAddr::V6(socket_addr_v6_from_c(unsafe { *(storage as *const _ as *const c::sockaddr_in6) }))) @@ -200,7 +201,7 @@ pub fn setsockopt( level, option_name, (&raw const option_value) as *const _, - mem::size_of::() as c::socklen_t, + size_of::() as c::socklen_t, ))?; Ok(()) } @@ -209,7 +210,7 @@ pub fn setsockopt( pub fn getsockopt(sock: &Socket, level: c_int, option_name: c_int) -> io::Result { unsafe { let mut option_value: T = mem::zeroed(); - let mut option_len = mem::size_of::() as c::socklen_t; + let mut option_len = size_of::() as c::socklen_t; cvt(c::getsockopt( sock.as_raw(), level, @@ -227,7 +228,7 @@ where { unsafe { let mut storage: c::sockaddr_storage = mem::zeroed(); - let mut len = mem::size_of_val(&storage) as c::socklen_t; + let mut len = size_of_val(&storage) as c::socklen_t; cvt(f((&raw mut storage) as *mut _, &mut len))?; socket_addr_from_c(&storage, len as usize) } @@ -561,7 +562,7 @@ impl TcpListener { // so we don't need to zero it here. // reference: https://linux.die.net/man/2/accept4 let mut storage: mem::MaybeUninit = mem::MaybeUninit::uninit(); - let mut len = mem::size_of_val(&storage) as c::socklen_t; + let mut len = size_of_val(&storage) as c::socklen_t; let sock = self.inner.accept(storage.as_mut_ptr() as *mut _, &mut len)?; let addr = unsafe { socket_addr_from_c(storage.as_ptr(), len as usize)? }; Ok((TcpStream { inner: sock }, addr)) diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index e393342ced9da..f49821657d940 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -183,7 +183,7 @@ impl Socket { fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> { let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + let mut addrlen = size_of_val(&storage) as netc::socklen_t; let n = cvt(unsafe { netc::recvfrom( diff --git a/library/std/src/sys/net/connection/socket/solid.rs b/library/std/src/sys/net/connection/socket/solid.rs index 906bef267b6f0..94bb605c1007c 100644 --- a/library/std/src/sys/net/connection/socket/solid.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -244,7 +244,7 @@ impl Socket { flags: c_int, ) -> io::Result<(usize, SocketAddr)> { let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + let mut addrlen = size_of_val(&storage) as netc::socklen_t; let n = cvt(unsafe { netc::recvfrom( diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 29fb47ddca3b9..b35d5d2aa8418 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -1,5 +1,6 @@ use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t}; +#[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; @@ -81,6 +82,7 @@ impl Socket { target_os = "linux", target_os = "netbsd", target_os = "openbsd", + target_os = "cygwin", target_os = "nto", target_os = "solaris", ))] { @@ -128,6 +130,7 @@ impl Socket { target_os = "hurd", target_os = "netbsd", target_os = "openbsd", + target_os = "cygwin", target_os = "nto", ))] { // Like above, set cloexec atomically @@ -257,6 +260,7 @@ impl Socket { target_os = "hurd", target_os = "netbsd", target_os = "openbsd", + target_os = "cygwin", ))] { unsafe { let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?; @@ -326,7 +330,7 @@ impl Socket { // so we don't need to zero it here. // reference: https://linux.die.net/man/2/recvfrom let mut storage: mem::MaybeUninit = mem::MaybeUninit::uninit(); - let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; + let mut addrlen = size_of_val(&storage) as libc::socklen_t; let n = cvt(unsafe { libc::recvfrom( @@ -421,6 +425,7 @@ impl Socket { Ok(()) } + #[cfg(not(target_os = "cygwin"))] pub fn set_linger(&self, linger: Option) -> io::Result<()> { let linger = libc::linger { l_onoff: linger.is_some() as libc::c_int, @@ -430,6 +435,16 @@ impl Socket { setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) } + #[cfg(target_os = "cygwin")] + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + let linger = libc::linger { + l_onoff: linger.is_some() as libc::c_ushort, + l_linger: linger.unwrap_or_default().as_secs() as libc::c_ushort, + }; + + setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) + } + pub fn linger(&self) -> io::Result> { let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?; diff --git a/library/std/src/sys/net/connection/socket/wasip2.rs b/library/std/src/sys/net/connection/socket/wasip2.rs index c5034e73dd704..73c2583187207 100644 --- a/library/std/src/sys/net/connection/socket/wasip2.rs +++ b/library/std/src/sys/net/connection/socket/wasip2.rs @@ -211,7 +211,7 @@ impl Socket { flags: c_int, ) -> io::Result<(usize, SocketAddr)> { let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + let mut addrlen = size_of_val(&storage) as netc::socklen_t; let n = cvt(unsafe { netc::recvfrom( diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index 428f142dabe20..ce975bb2289c2 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -381,7 +381,7 @@ impl Socket { flags: c_int, ) -> io::Result<(usize, SocketAddr)> { let mut storage = unsafe { mem::zeroed::() }; - let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + let mut addrlen = size_of_val(&storage) as netc::socklen_t; let length = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; // On unix when a socket is shut down all further reads return 0, so we @@ -514,13 +514,13 @@ impl Socket { // This is used by sys_common code to abstract over Windows and Unix. pub fn as_raw(&self) -> c::SOCKET { - debug_assert_eq!(mem::size_of::(), mem::size_of::()); - debug_assert_eq!(mem::align_of::(), mem::align_of::()); + debug_assert_eq!(size_of::(), size_of::()); + debug_assert_eq!(align_of::(), align_of::()); self.as_inner().as_raw_socket() as c::SOCKET } pub unsafe fn from_raw(raw: c::SOCKET) -> Self { - debug_assert_eq!(mem::size_of::(), mem::size_of::()); - debug_assert_eq!(mem::align_of::(), mem::align_of::()); + debug_assert_eq!(size_of::(), size_of::()); + debug_assert_eq!(align_of::(), align_of::()); unsafe { Self::from_raw_socket(raw as RawSocket) } } } diff --git a/library/std/src/sys/net/connection/xous/tcplistener.rs b/library/std/src/sys/net/connection/xous/tcplistener.rs index 7f13ca5592040..bdf1fcd9302b7 100644 --- a/library/std/src/sys/net/connection/xous/tcplistener.rs +++ b/library/std/src/sys/net/connection/xous/tcplistener.rs @@ -1,5 +1,5 @@ use core::convert::TryInto; -use core::sync::atomic::{AtomicBool, AtomicU16, AtomicUsize, Ordering}; +use core::sync::atomic::{Atomic, AtomicBool, AtomicU16, AtomicUsize, Ordering}; use super::*; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; @@ -18,10 +18,10 @@ macro_rules! unimpl { #[derive(Clone)] pub struct TcpListener { - fd: Arc, + fd: Arc>, local: SocketAddr, - handle_count: Arc, - nonblocking: Arc, + handle_count: Arc>, + nonblocking: Arc>, } impl TcpListener { diff --git a/library/std/src/sys/net/connection/xous/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs index 283b1fe9a33b9..5452476745265 100644 --- a/library/std/src/sys/net/connection/xous/tcpstream.rs +++ b/library/std/src/sys/net/connection/xous/tcpstream.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; +use core::sync::atomic::{Atomic, AtomicBool, AtomicU32, AtomicUsize, Ordering}; use super::*; use crate::fmt; @@ -29,11 +29,11 @@ pub struct TcpStream { remote_port: u16, peer_addr: SocketAddr, // milliseconds - read_timeout: Arc, + read_timeout: Arc>, // milliseconds - write_timeout: Arc, - handle_count: Arc, - nonblocking: Arc, + write_timeout: Arc>, + handle_count: Arc>, + nonblocking: Arc>, } fn sockaddr_to_buf(duration: Duration, addr: &SocketAddr, buf: &mut [u8]) { @@ -324,7 +324,7 @@ impl TcpStream { } Ok(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.local_port, 0, 0))) } - _ => Err(io::const_error!(io::ErrorKind::InvalidInput, "tnternal error")), + _ => Err(io::const_error!(io::ErrorKind::InvalidInput, "internal error")), } } diff --git a/library/std/src/sys/net/connection/xous/udp.rs b/library/std/src/sys/net/connection/xous/udp.rs index f35970bc32152..2127d3267ed47 100644 --- a/library/std/src/sys/net/connection/xous/udp.rs +++ b/library/std/src/sys/net/connection/xous/udp.rs @@ -1,5 +1,5 @@ use core::convert::TryInto; -use core::sync::atomic::{AtomicUsize, Ordering}; +use core::sync::atomic::{Atomic, AtomicUsize, Ordering}; use super::*; use crate::cell::Cell; @@ -27,7 +27,7 @@ pub struct UdpSocket { read_timeout: Cell, // in milliseconds. The setting applies only to `send` calls after the timeout is set. write_timeout: Cell, - handle_count: Arc, + handle_count: Arc>, nonblocking: Cell, } @@ -244,7 +244,7 @@ impl UdpSocket { // let buf = unsafe { // xous::MemoryRange::new( // &mut tx_req as *mut SendData as usize, - // core::mem::size_of::(), + // size_of::(), // ) // .unwrap() // }; diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 1d337694944bc..4a8808c923045 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -139,6 +139,11 @@ impl Buf { self.inner.extend_from_slice(&s.inner) } + #[inline] + pub fn push_str(&mut self, s: &str) { + self.inner.extend_from_slice(s.as_bytes()); + } + #[inline] pub fn reserve(&mut self, additional: usize) { self.inner.reserve(additional) @@ -211,19 +216,26 @@ impl Buf { self.as_slice().into_rc() } - /// Provides plumbing to core `Vec::truncate`. - /// More well behaving alternative to allowing outer types - /// full mutable access to the core `Vec`. + /// Provides plumbing to `Vec::truncate` without giving full mutable access + /// to the `Vec`. + /// + /// # Safety + /// + /// The length must be at an `OsStr` boundary, according to + /// `Slice::check_public_boundary`. #[inline] - pub(crate) fn truncate(&mut self, len: usize) { + pub unsafe fn truncate_unchecked(&mut self, len: usize) { self.inner.truncate(len); } - /// Provides plumbing to core `Vec::extend_from_slice`. - /// More well behaving alternative to allowing outer types - /// full mutable access to the core `Vec`. + /// Provides plumbing to `Vec::extend_from_slice` without giving full + /// mutable access to the `Vec`. + /// + /// # Safety + /// + /// This encoding has no safety requirements. #[inline] - pub(crate) fn extend_from_slice(&mut self, other: &[u8]) { + pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { self.inner.extend_from_slice(other); } } diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 8acec6f949fc5..5174ea65d0cd9 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -116,6 +116,11 @@ impl Buf { self.inner.push_wtf8(&s.inner) } + #[inline] + pub fn push_str(&mut self, s: &str) { + self.inner.push_str(s); + } + #[inline] pub fn reserve(&mut self, additional: usize) { self.inner.reserve(additional) @@ -190,19 +195,31 @@ impl Buf { self.as_slice().into_rc() } - /// Provides plumbing to core `Vec::truncate`. - /// More well behaving alternative to allowing outer types - /// full mutable access to the core `Vec`. + /// Provides plumbing to `Vec::truncate` without giving full mutable access + /// to the `Vec`. + /// + /// # Safety + /// + /// The length must be at an `OsStr` boundary, according to + /// `Slice::check_public_boundary`. #[inline] - pub(crate) fn truncate(&mut self, len: usize) { + pub unsafe fn truncate_unchecked(&mut self, len: usize) { self.inner.truncate(len); } - /// Provides plumbing to core `Vec::extend_from_slice`. - /// More well behaving alternative to allowing outer types - /// full mutable access to the core `Vec`. + /// Provides plumbing to `Vec::extend_from_slice` without giving full + /// mutable access to the `Vec`. + /// + /// # Safety + /// + /// The slice must be valid for the platform encoding (as described in + /// [`Slice::from_encoded_bytes_unchecked`]). + /// + /// This bypasses the WTF-8 surrogate joining, so `self` must not end with a + /// leading surrogate half and `other` must not start with with a trailing + /// surrogate half. #[inline] - pub(crate) fn extend_from_slice(&mut self, other: &[u8]) { + pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { self.inner.extend_from_slice(other); } } diff --git a/library/std/src/sys/pal/hermit/args.rs b/library/std/src/sys/pal/hermit/args.rs deleted file mode 100644 index 4402426027730..0000000000000 --- a/library/std/src/sys/pal/hermit/args.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::ffi::{CStr, OsString, c_char}; -use crate::os::hermit::ffi::OsStringExt; -use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::atomic::{AtomicIsize, AtomicPtr}; -use crate::{fmt, ptr, vec}; - -static ARGC: AtomicIsize = AtomicIsize::new(0); -static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut()); - -/// One-time global initialization. -pub unsafe fn init(argc: isize, argv: *const *const u8) { - ARGC.store(argc, Relaxed); - // Use release ordering here to broadcast writes by the OS. - ARGV.store(argv as *mut *const u8, Release); -} - -/// Returns the command line arguments -pub fn args() -> Args { - // Synchronize with the store above. - let argv = ARGV.load(Acquire); - // If argv has not been initialized yet, do not return any arguments. - let argc = if argv.is_null() { 0 } else { ARGC.load(Relaxed) }; - let args: Vec = (0..argc) - .map(|i| unsafe { - let cstr = CStr::from_ptr(*argv.offset(i) as *const c_char); - OsStringExt::from_vec(cstr.to_bytes().to_vec()) - }) - .collect(); - - Args { iter: args.into_iter() } -} - -pub struct Args { - iter: vec::IntoIter, -} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.iter.as_slice().fmt(f) - } -} - -impl !Send for Args {} -impl !Sync for Args {} - -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/library/std/src/sys/pal/hermit/env.rs b/library/std/src/sys/pal/hermit/env.rs deleted file mode 100644 index 7a0fcb31ef2e8..0000000000000 --- a/library/std/src/sys/pal/hermit/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = ""; - pub const OS: &str = "hermit"; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ""; - pub const DLL_EXTENSION: &str = ""; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} diff --git a/library/std/src/sys/pal/hermit/fd.rs b/library/std/src/sys/pal/hermit/fd.rs deleted file mode 100644 index 79fc13bd4a87f..0000000000000 --- a/library/std/src/sys/pal/hermit/fd.rs +++ /dev/null @@ -1,143 +0,0 @@ -#![unstable(reason = "not public", issue = "none", feature = "fd")] - -use super::hermit_abi; -use crate::cmp; -use crate::io::{self, IoSlice, IoSliceMut, Read}; -use crate::os::hermit::io::{FromRawFd, OwnedFd, RawFd, *}; -use crate::sys::{cvt, unsupported}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; - -const fn max_iov() -> usize { - hermit_abi::IOV_MAX -} - -#[derive(Debug)] -pub struct FileDesc { - fd: OwnedFd, -} - -impl FileDesc { - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let result = - cvt(unsafe { hermit_abi::read(self.fd.as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?; - Ok(result as usize) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let ret = cvt(unsafe { - hermit_abi::readv( - self.as_raw_fd(), - bufs.as_mut_ptr() as *mut hermit_abi::iovec as *const hermit_abi::iovec, - cmp::min(bufs.len(), max_iov()), - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - let mut me = self; - (&mut me).read_to_end(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let result = - cvt(unsafe { hermit_abi::write(self.fd.as_raw_fd(), buf.as_ptr(), buf.len()) })?; - Ok(result as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let ret = cvt(unsafe { - hermit_abi::writev( - self.as_raw_fd(), - bufs.as_ptr() as *const hermit_abi::iovec, - cmp::min(bufs.len(), max_iov()), - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn duplicate(&self) -> io::Result { - self.duplicate_path(&[]) - } - pub fn duplicate_path(&self, _path: &[u8]) -> io::Result { - unsupported() - } - - pub fn nonblocking(&self) -> io::Result { - Ok(false) - } - - pub fn set_cloexec(&self) -> io::Result<()> { - unsupported() - } - - pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> { - unsupported() - } - - pub fn fstat(&self, stat: *mut hermit_abi::stat) -> io::Result<()> { - cvt(unsafe { hermit_abi::fstat(self.fd.as_raw_fd(), stat) })?; - Ok(()) - } -} - -impl<'a> Read for &'a FileDesc { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } -} - -impl IntoInner for FileDesc { - fn into_inner(self) -> OwnedFd { - self.fd - } -} - -impl FromInner for FileDesc { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self { fd: owned_fd } - } -} - -impl FromRawFd for FileDesc { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) }; - Self { fd } - } -} - -impl AsInner for FileDesc { - #[inline] - fn as_inner(&self) -> &OwnedFd { - &self.fd - } -} - -impl AsFd for FileDesc { - fn as_fd(&self) -> BorrowedFd<'_> { - self.fd.as_fd() - } -} - -impl AsRawFd for FileDesc { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.fd.as_raw_fd() - } -} - -impl IntoRawFd for FileDesc { - fn into_raw_fd(self) -> RawFd { - self.fd.into_raw_fd() - } -} diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs deleted file mode 100644 index d4bf84dc1854c..0000000000000 --- a/library/std/src/sys/pal/hermit/fs.rs +++ /dev/null @@ -1,602 +0,0 @@ -use super::fd::FileDesc; -use super::hermit_abi::{ - self, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, O_DIRECTORY, O_EXCL, O_RDONLY, - O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, dirent64, stat as stat_struct, -}; -use crate::ffi::{CStr, OsStr, OsString, c_char}; -use crate::io::{self, BorrowedCursor, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; -use crate::os::hermit::ffi::OsStringExt; -use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::path::{Path, PathBuf}; -use crate::sync::Arc; -use crate::sys::common::small_c_string::run_path_with_cstr; -use crate::sys::time::SystemTime; -use crate::sys::{cvt, unsupported}; -pub use crate::sys_common::fs::{copy, exists}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; -use crate::{fmt, mem}; - -#[derive(Debug)] -pub struct File(FileDesc); -#[derive(Clone)] -pub struct FileAttr { - stat_val: stat_struct, -} - -impl FileAttr { - fn from_stat(stat_val: stat_struct) -> Self { - Self { stat_val } - } -} - -// all DirEntry's will have a reference to this struct -struct InnerReadDir { - root: PathBuf, - dir: Vec, -} - -impl InnerReadDir { - pub fn new(root: PathBuf, dir: Vec) -> Self { - Self { root, dir } - } -} - -pub struct ReadDir { - inner: Arc, - pos: usize, -} - -impl ReadDir { - fn new(inner: InnerReadDir) -> Self { - Self { inner: Arc::new(inner), pos: 0 } - } -} - -pub struct DirEntry { - /// path to the entry - root: PathBuf, - /// 64-bit inode number - ino: u64, - /// File type - type_: u8, - /// name of the entry - name: OsString, -} - -#[derive(Clone, Debug)] -pub struct OpenOptions { - // generic - read: bool, - write: bool, - append: bool, - truncate: bool, - create: bool, - create_new: bool, - // system-specific - mode: i32, -} - -#[derive(Copy, Clone, Debug, Default)] -pub struct FileTimes {} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FilePermissions { - mode: u32, -} - -#[derive(Copy, Clone, Eq, Debug)] -pub struct FileType { - mode: u8, -} - -impl PartialEq for FileType { - fn eq(&self, other: &Self) -> bool { - self.mode == other.mode - } -} - -impl core::hash::Hash for FileType { - fn hash(&self, state: &mut H) { - self.mode.hash(state); - } -} - -#[derive(Debug)] -pub struct DirBuilder { - mode: u32, -} - -impl FileAttr { - pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat_val.st_mtim.tv_sec, self.stat_val.st_mtim.tv_nsec)) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat_val.st_atim.tv_sec, self.stat_val.st_atim.tv_nsec)) - } - - pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat_val.st_ctim.tv_sec, self.stat_val.st_ctim.tv_nsec)) - } - - pub fn size(&self) -> u64 { - self.stat_val.st_size as u64 - } - - pub fn perm(&self) -> FilePermissions { - FilePermissions { mode: self.stat_val.st_mode } - } - - pub fn file_type(&self) -> FileType { - let masked_mode = self.stat_val.st_mode & S_IFMT; - let mode = match masked_mode { - S_IFDIR => DT_DIR, - S_IFLNK => DT_LNK, - S_IFREG => DT_REG, - _ => DT_UNKNOWN, - }; - FileType { mode } - } -} - -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) { - unimplemented!() - } - - #[allow(dead_code)] - pub fn mode(&self) -> u32 { - self.mode as u32 - } -} - -impl FileTimes { - pub fn set_accessed(&mut self, _t: SystemTime) {} - pub fn set_modified(&mut self, _t: SystemTime) {} -} - -impl FileType { - pub fn is_dir(&self) -> bool { - self.mode == DT_DIR - } - pub fn is_file(&self) -> bool { - self.mode == DT_REG - } - pub fn is_symlink(&self) -> bool { - self.mode == DT_LNK - } -} - -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; - - fn next(&mut self) -> Option> { - let mut counter: usize = 0; - let mut offset: usize = 0; - - // loop over all directory entries and search the entry for the current position - loop { - // leave function, if the loop reaches the of the buffer (with all entries) - if offset >= self.inner.dir.len() { - return None; - } - - let dir = unsafe { &*(self.inner.dir.as_ptr().add(offset) as *const dirent64) }; - - if counter == self.pos { - self.pos += 1; - - // After dirent64, the file name is stored. d_reclen represents the length of the dirent64 - // plus the length of the file name. Consequently, file name has a size of d_reclen minus - // the size of dirent64. The file name is always a C string and terminated by `\0`. - // Consequently, we are able to ignore the last byte. - let name_bytes = - unsafe { CStr::from_ptr(&dir.d_name as *const _ as *const c_char).to_bytes() }; - let entry = DirEntry { - root: self.inner.root.clone(), - ino: dir.d_ino, - type_: dir.d_type, - name: OsString::from_vec(name_bytes.to_vec()), - }; - - return Some(Ok(entry)); - } - - counter += 1; - - // move to the next dirent64, which is directly stored after the previous one - offset = offset + usize::from(dir.d_reclen); - } - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - self.root.join(self.file_name_os_str()) - } - - pub fn file_name(&self) -> OsString { - self.file_name_os_str().to_os_string() - } - - pub fn metadata(&self) -> io::Result { - let mut path = self.path(); - path.set_file_name(self.file_name_os_str()); - lstat(&path) - } - - pub fn file_type(&self) -> io::Result { - Ok(FileType { mode: self.type_ }) - } - - #[allow(dead_code)] - pub fn ino(&self) -> u64 { - self.ino - } - - pub fn file_name_os_str(&self) -> &OsStr { - self.name.as_os_str() - } -} - -impl OpenOptions { - pub fn new() -> OpenOptions { - OpenOptions { - // generic - read: false, - write: false, - append: false, - truncate: false, - create: false, - create_new: false, - // system-specific - mode: 0o777, - } - } - - 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; - } - - fn get_access_mode(&self) -> io::Result { - match (self.read, self.write, self.append) { - (true, false, false) => Ok(O_RDONLY), - (false, true, false) => Ok(O_WRONLY), - (true, true, false) => Ok(O_RDWR), - (false, _, true) => Ok(O_WRONLY | O_APPEND), - (true, _, true) => Ok(O_RDWR | O_APPEND), - (false, false, false) => { - Err(io::const_error!(ErrorKind::InvalidInput, "invalid access mode")) - } - } - } - - 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(io::const_error!(ErrorKind::InvalidInput, "invalid creation mode")); - } - } - (_, true) => { - if self.truncate && !self.create_new { - return Err(io::const_error!(ErrorKind::InvalidInput, "invalid creation mode")); - } - } - } - - Ok(match (self.create, self.truncate, self.create_new) { - (false, false, false) => 0, - (true, false, false) => O_CREAT, - (false, true, false) => O_TRUNC, - (true, true, false) => O_CREAT | O_TRUNC, - (_, _, true) => O_CREAT | O_EXCL, - }) - } -} - -impl File { - pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - run_path_with_cstr(path, &|path| File::open_c(&path, opts)) - } - - pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { - let mut flags = opts.get_access_mode()?; - flags = flags | opts.get_creation_mode()?; - - let mode; - if flags & O_CREAT == O_CREAT { - mode = opts.mode; - } else { - mode = 0; - } - - let fd = unsafe { cvt(hermit_abi::open(path.as_ptr(), flags, mode))? }; - Ok(File(unsafe { FileDesc::from_raw_fd(fd as i32) })) - } - - pub fn file_attr(&self) -> io::Result { - let mut stat_val: stat_struct = unsafe { mem::zeroed() }; - self.0.fstat(&mut stat_val)?; - Ok(FileAttr::from_stat(stat_val)) - } - - pub fn fsync(&self) -> io::Result<()> { - Err(Error::from_raw_os_error(22)) - } - - pub fn datasync(&self) -> io::Result<()> { - self.fsync() - } - - pub fn lock(&self) -> io::Result<()> { - unsupported() - } - - pub fn lock_shared(&self) -> io::Result<()> { - unsupported() - } - - pub fn try_lock(&self) -> io::Result { - unsupported() - } - - pub fn try_lock_shared(&self) -> io::Result { - unsupported() - } - - pub fn unlock(&self) -> io::Result<()> { - unsupported() - } - - pub fn truncate(&self, _size: u64) -> io::Result<()> { - Err(Error::from_raw_os_error(22)) - } - - 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_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - crate::io::default_read_buf(|buf| self.read(buf), cursor) - } - - 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() - } - - #[inline] - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } - - pub fn seek(&self, _pos: SeekFrom) -> io::Result { - Err(Error::from_raw_os_error(22)) - } - - pub fn tell(&self) -> io::Result { - self.seek(SeekFrom::Current(0)) - } - - pub fn duplicate(&self) -> io::Result { - Err(Error::from_raw_os_error(22)) - } - - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - Err(Error::from_raw_os_error(22)) - } - - pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { - Err(Error::from_raw_os_error(22)) - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder { mode: 0o777 } - } - - pub fn mkdir(&self, path: &Path) -> io::Result<()> { - run_path_with_cstr(path, &|path| { - cvt(unsafe { hermit_abi::mkdir(path.as_ptr().cast(), self.mode.into()) }).map(|_| ()) - }) - } - - #[allow(dead_code)] - pub fn set_mode(&mut self, mode: u32) { - self.mode = mode; - } -} - -impl AsInner for File { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } -} - -impl AsInnerMut for File { - #[inline] - fn as_inner_mut(&mut self) -> &mut FileDesc { - &mut self.0 - } -} - -impl IntoInner for File { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner for File { - fn from_inner(file_desc: FileDesc) -> Self { - Self(file_desc) - } -} - -impl AsFd for File { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for File { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for File { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for File { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - let file_desc = unsafe { FileDesc::from_raw_fd(raw_fd) }; - Self(file_desc) - } -} - -pub fn readdir(path: &Path) -> io::Result { - let fd_raw = run_path_with_cstr(path, &|path| { - cvt(unsafe { hermit_abi::open(path.as_ptr(), O_RDONLY | O_DIRECTORY, 0) }) - })?; - let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; - let root = path.to_path_buf(); - - // read all director entries - let mut vec: Vec = Vec::new(); - let mut sz = 512; - loop { - // reserve memory to receive all directory entries - vec.resize(sz, 0); - - let readlen = unsafe { - hermit_abi::getdents64(fd.as_raw_fd(), vec.as_mut_ptr() as *mut dirent64, sz) - }; - if readlen > 0 { - // shrink down to the minimal size - vec.resize(readlen.try_into().unwrap(), 0); - break; - } - - // if the buffer is too small, getdents64 returns EINVAL - // otherwise, getdents64 returns an error number - if readlen != (-hermit_abi::errno::EINVAL).into() { - return Err(Error::from_raw_os_error(readlen.try_into().unwrap())); - } - - // we don't have enough memory => try to increase the vector size - sz = sz * 2; - - // 1 MB for directory entries should be enough - // stop here to avoid an endless loop - if sz > 0x100000 { - return Err(Error::from(ErrorKind::Uncategorized)); - } - } - - Ok(ReadDir::new(InnerReadDir::new(root, vec))) -} - -pub fn unlink(path: &Path) -> io::Result<()> { - run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::unlink(path.as_ptr()) }).map(|_| ())) -} - -pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { - unsupported() -} - -pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { - Err(Error::from_raw_os_error(22)) -} - -pub fn rmdir(path: &Path) -> io::Result<()> { - run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::rmdir(path.as_ptr()) }).map(|_| ())) -} - -pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - unsupported() -} - -pub fn readlink(_p: &Path) -> io::Result { - unsupported() -} - -pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { - unsupported() -} - -pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { - unsupported() -} - -pub fn stat(path: &Path) -> io::Result { - run_path_with_cstr(path, &|path| { - let mut stat_val: stat_struct = unsafe { mem::zeroed() }; - cvt(unsafe { hermit_abi::stat(path.as_ptr(), &mut stat_val) })?; - Ok(FileAttr::from_stat(stat_val)) - }) -} - -pub fn lstat(path: &Path) -> io::Result { - run_path_with_cstr(path, &|path| { - let mut stat_val: stat_struct = unsafe { mem::zeroed() }; - cvt(unsafe { hermit_abi::lstat(path.as_ptr(), &mut stat_val) })?; - Ok(FileAttr::from_stat(stat_val)) - }) -} - -pub fn canonicalize(_p: &Path) -> io::Result { - unsupported() -} diff --git a/library/std/src/sys/pal/hermit/futex.rs b/library/std/src/sys/pal/hermit/futex.rs index 670383b45aca9..78c86071fdd53 100644 --- a/library/std/src/sys/pal/hermit/futex.rs +++ b/library/std/src/sys/pal/hermit/futex.rs @@ -1,19 +1,19 @@ use super::hermit_abi; use crate::ptr::null; -use crate::sync::atomic::AtomicU32; +use crate::sync::atomic::Atomic; use crate::time::Duration; /// An atomic for use as a futex that is at least 32-bits but may be larger -pub type Futex = AtomicU32; +pub type Futex = Atomic; /// Must be the underlying type of Futex pub type Primitive = u32; /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallFutex = AtomicU32; +pub type SmallFutex = Atomic; /// Must be the underlying type of SmallFutex pub type SmallPrimitive = u32; -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { +pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) -> bool { // Calculate the timeout as a relative timespec. // // Overflows are rounded up to an infinite timeout (None). @@ -37,12 +37,12 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - } #[inline] -pub fn futex_wake(futex: &AtomicU32) -> bool { +pub fn futex_wake(futex: &Atomic) -> bool { unsafe { hermit_abi::futex_wake(futex.as_ptr(), 1) > 0 } } #[inline] -pub fn futex_wake_all(futex: &AtomicU32) { +pub fn futex_wake_all(futex: &Atomic) { unsafe { hermit_abi::futex_wake(futex.as_ptr(), i32::MAX); } diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 21cbac643bbec..fb8d69b73759d 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -16,25 +16,18 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(missing_docs, nonstandard_style)] +use crate::io::ErrorKind; +use crate::os::hermit::hermit_abi; use crate::os::raw::c_char; +use crate::sys::env; -pub mod args; -pub mod env; -pub mod fd; -pub mod fs; pub mod futex; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/process.rs"] -pub mod process; -pub mod stdio; pub mod thread; pub mod time; -use crate::io::ErrorKind; -use crate::os::hermit::hermit_abi; - pub fn unsupported() -> crate::io::Result { Err(unsupported_err()) } @@ -50,20 +43,11 @@ pub fn abort_internal() -> ! { unsafe { hermit_abi::abort() } } -// 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))] -#[unsafe(no_mangle)] -// NB. used by both libunwind and libpanic_abort -pub extern "C" fn __rust_abort() { - abort_internal(); -} - // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { unsafe { - args::init(argc, argv); + crate::sys::args::init(argc, argv); } } @@ -83,7 +67,7 @@ pub unsafe extern "C" fn runtime_entry( } // initialize environment - os::init_environment(env); + env::init(env); let result = unsafe { main(argc as isize, argv) }; diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 791cdb1e57e7d..a998c3165e52f 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -1,15 +1,10 @@ -use core::slice::memchr; - use super::hermit_abi; -use crate::collections::HashMap; use crate::error::Error as StdError; -use crate::ffi::{CStr, OsStr, OsString, c_char}; +use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; -use crate::os::hermit::ffi::OsStringExt; use crate::path::{self, PathBuf}; -use crate::sync::Mutex; use crate::sys::unsupported; -use crate::{fmt, io, str, vec}; +use crate::{fmt, io, str}; pub fn errno() -> i32 { unsafe { hermit_abi::get_errno() } @@ -68,115 +63,6 @@ pub fn current_exe() -> io::Result { unsupported() } -static ENV: Mutex>> = Mutex::new(None); - -pub fn init_environment(env: *const *const c_char) { - let mut guard = ENV.lock().unwrap(); - let map = guard.insert(HashMap::new()); - - if env.is_null() { - return; - } - - unsafe { - let mut environ = env; - while !(*environ).is_null() { - if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) { - map.insert(key, value); - } - environ = environ.add(1); - } - } - - 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 struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -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() - } -} - -/// Returns a vector of (variable, value) byte-vector pairs for all the -/// environment variables of the current process. -pub fn env() -> Env { - let guard = ENV.lock().unwrap(); - let env = guard.as_ref().unwrap(); - - let result = env.iter().map(|(key, value)| (key.clone(), value.clone())).collect::>(); - - Env { iter: result.into_iter() } -} - -pub fn getenv(k: &OsStr) -> Option { - ENV.lock().unwrap().as_ref().unwrap().get(k).cloned() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let (k, v) = (k.to_owned(), v.to_owned()); - ENV.lock().unwrap().as_mut().unwrap().insert(k, v); - Ok(()) -} - -pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - ENV.lock().unwrap().as_mut().unwrap().remove(k); - Ok(()) -} - pub fn temp_dir() -> PathBuf { PathBuf::from("/tmp") } diff --git a/library/std/src/sys/pal/hermit/stdio.rs b/library/std/src/sys/pal/hermit/stdio.rs deleted file mode 100644 index 3ea00f5cc5ec9..0000000000000 --- a/library/std/src/sys/pal/hermit/stdio.rs +++ /dev/null @@ -1,97 +0,0 @@ -use super::hermit_abi; -use crate::io; -use crate::io::{IoSlice, IoSliceMut}; -use crate::mem::ManuallyDrop; -use crate::os::hermit::io::FromRawFd; -use crate::sys::fd::FileDesc; - -pub struct Stdin; -pub struct Stdout; -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 { - unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDIN_FILENO)).read(buf) } - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - unsafe { - ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDIN_FILENO)).read_vectored(bufs) - } - } - - #[inline] - 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 { - unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDOUT_FILENO)).write(buf) } - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - unsafe { - ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::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 { - unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDERR_FILENO)).write(buf) } - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - unsafe { - ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDERR_FILENO)).write_vectored(bufs) - } - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub const STDIN_BUF_SIZE: usize = 128; - -pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(hermit_abi::EBADF) -} - -pub fn panic_output() -> Option { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/pal/itron/spin.rs b/library/std/src/sys/pal/itron/spin.rs index 6a9a7c72deb7d..bc4f83260bbd0 100644 --- a/library/std/src/sys/pal/itron/spin.rs +++ b/library/std/src/sys/pal/itron/spin.rs @@ -1,12 +1,12 @@ use super::abi; use crate::cell::UnsafeCell; use crate::mem::MaybeUninit; -use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering}; /// A mutex implemented by `dis_dsp` (for intra-core synchronization) and a /// spinlock (for inter-core synchronization). pub struct SpinMutex { - locked: AtomicBool, + locked: Atomic, data: UnsafeCell, } @@ -19,7 +19,7 @@ impl SpinMutex { /// Acquire a lock. #[inline] pub fn with_locked(&self, f: impl FnOnce(&mut T) -> R) -> R { - struct SpinMutexGuard<'a>(&'a AtomicBool); + struct SpinMutexGuard<'a>(&'a Atomic); impl Drop for SpinMutexGuard<'_> { #[inline] @@ -50,7 +50,7 @@ impl SpinMutex { /// It's assumed that `0` is not a valid ID, and all kernel /// object IDs fall into range `1..=usize::MAX`. pub struct SpinIdOnceCell { - id: AtomicUsize, + id: Atomic, spin: SpinMutex<()>, extra: UnsafeCell>, } diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index 04095e1a7cf99..a974f4f17ae67 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -9,7 +9,7 @@ use crate::ffi::CStr; use crate::mem::ManuallyDrop; use crate::num::NonZero; use crate::ptr::NonNull; -use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::time::Duration; use crate::{hint, io}; @@ -64,7 +64,7 @@ struct ThreadInner { /// '--> JOIN_FINALIZE ---' /// (-1) /// - lifecycle: AtomicUsize, + lifecycle: Atomic, } // Safety: The only `!Sync` field, `ThreadInner::start`, is only touched by @@ -80,7 +80,7 @@ const LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE: usize = usize::MAX; // there's no single value for `JOINING` // 64KiB for 32-bit ISAs, 128KiB for 64-bit ISAs. -pub const DEFAULT_MIN_STACK_SIZE: usize = 0x4000 * crate::mem::size_of::(); +pub const DEFAULT_MIN_STACK_SIZE: usize = 0x4000 * size_of::(); impl Thread { /// # Safety diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 9be018c8a5312..fbefc62ac88eb 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -37,6 +37,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "hermit")] { mod hermit; pub use self::hermit::*; + } else if #[cfg(target_os = "trusty")] { + mod trusty; + pub use self::trusty::*; } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { mod wasip2; pub use self::wasip2::*; diff --git a/library/std/src/sys/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs index 90981bd6a6a04..57247cffad3f2 100644 --- a/library/std/src/sys/pal/sgx/abi/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/mod.rs @@ -1,12 +1,12 @@ #![cfg_attr(test, allow(unused))] // RT initialization logic is not compiled for test use core::arch::global_asm; -use core::sync::atomic::{AtomicUsize, Ordering}; +use core::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::io::Write; // runtime features -pub(super) mod panic; +pub mod panic; mod reloc; // library features @@ -31,7 +31,7 @@ unsafe extern "C" fn tcs_init(secondary: bool) { const BUSY: usize = 1; const DONE: usize = 2; // Three-state spin-lock - static RELOC_STATE: AtomicUsize = AtomicUsize::new(UNINIT); + static RELOC_STATE: Atomic = AtomicUsize::new(UNINIT); if secondary && RELOC_STATE.load(Ordering::Relaxed) != DONE { rtabort!("Entered secondary TCS before main TCS!") diff --git a/library/std/src/sys/pal/sgx/abi/tls/mod.rs b/library/std/src/sys/pal/sgx/abi/tls/mod.rs index 8e2b271f1c970..41e38b6961680 100644 --- a/library/std/src/sys/pal/sgx/abi/tls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/tls/mod.rs @@ -3,7 +3,7 @@ mod sync_bitset; use self::sync_bitset::*; use crate::cell::Cell; use crate::num::NonZero; -use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::{mem, ptr}; #[cfg(target_pointer_width = "64")] @@ -11,16 +11,14 @@ 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; +// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_KEY_IN_USEE")] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx3abi3tls14TLS_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),*]) -} +// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_DESTRUCTORE")] -static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0))); +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx3abi3tls14TLS_DESTRUCTORE")] +static TLS_DESTRUCTOR: [Atomic; TLS_KEYS] = [const { AtomicUsize::new(0) }; TLS_KEYS]; unsafe extern "C" { fn get_tls_ptr() -> *const u8; @@ -82,7 +80,7 @@ impl<'a> Drop for ActiveTls<'a> { impl Tls { pub fn new() -> Tls { - Tls { data: dup!((* * * * * * *) (Cell::new(ptr::null_mut()))) } + Tls { data: [const { Cell::new(ptr::null_mut()) }; TLS_KEYS] } } pub unsafe fn activate(&self) -> ActiveTls<'_> { diff --git a/library/std/src/sys/pal/sgx/abi/tls/sync_bitset.rs b/library/std/src/sys/pal/sgx/abi/tls/sync_bitset.rs index 4eeff8f6ef773..9087168589aa5 100644 --- a/library/std/src/sys/pal/sgx/abi/tls/sync_bitset.rs +++ b/library/std/src/sys/pal/sgx/abi/tls/sync_bitset.rs @@ -4,10 +4,10 @@ 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}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; /// A bitset that can be used synchronously. -pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]); +pub(super) struct SyncBitset([Atomic; TLS_KEYS_BITSET_SIZE]); pub(super) const SYNC_BITSET_INIT: SyncBitset = SyncBitset([AtomicUsize::new(0), AtomicUsize::new(0)]); @@ -58,7 +58,7 @@ impl SyncBitset { } pub(super) struct SyncBitsetIter<'a> { - iter: Peekable>>, + iter: Peekable>>>, elem_idx: usize, } diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs index 5069ab82ccc90..a60b83213fd96 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs @@ -6,7 +6,7 @@ use super::super::mem::{is_enclave_range, is_user_range}; use crate::arch::asm; use crate::cell::UnsafeCell; use crate::convert::TryInto; -use crate::mem::{self, ManuallyDrop}; +use crate::mem::{self, ManuallyDrop, MaybeUninit}; use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut}; use crate::pin::PinCoerceUnsized; use crate::ptr::{self, NonNull}; @@ -63,7 +63,7 @@ unsafe impl UserSafeSized for [T; 2] {} /// A type that can be represented in memory as one or more `UserSafeSized`s. #[unstable(feature = "sgx_platform", issue = "56975")] pub unsafe trait UserSafe { - /// Equivalent to `mem::align_of::`. + /// Equivalent to `align_of::`. fn align_of() -> usize; /// Constructs a pointer to `Self` given a memory range in user space. @@ -120,7 +120,7 @@ pub unsafe trait UserSafe { let is_aligned = |p: *const u8| -> bool { p.is_aligned_to(Self::align_of()) }; assert!(is_aligned(ptr as *const u8)); - assert!(is_user_range(ptr as _, mem::size_of_val(unsafe { &*ptr }))); + assert!(is_user_range(ptr as _, size_of_val(unsafe { &*ptr }))); assert!(!ptr.is_null()); } } @@ -128,11 +128,11 @@ pub unsafe trait UserSafe { #[unstable(feature = "sgx_platform", issue = "56975")] unsafe impl UserSafe for T { fn align_of() -> usize { - mem::align_of::() + align_of::() } unsafe fn from_raw_sized_unchecked(ptr: *mut u8, size: usize) -> *mut Self { - assert_eq!(size, mem::size_of::()); + assert_eq!(size, size_of::()); ptr as _ } } @@ -140,7 +140,7 @@ unsafe impl UserSafe for T { #[unstable(feature = "sgx_platform", issue = "56975")] unsafe impl UserSafe for [T] { fn align_of() -> usize { - mem::align_of::() + align_of::() } /// # Safety @@ -155,7 +155,7 @@ unsafe impl UserSafe for [T] { /// /// * the element size is not a factor of the size unsafe fn from_raw_sized_unchecked(ptr: *mut u8, size: usize) -> *mut Self { - let elem_size = mem::size_of::(); + let elem_size = size_of::(); assert_eq!(size % elem_size, 0); let len = size / elem_size; // SAFETY: The caller must uphold the safety contract for `from_raw_sized_unchecked` @@ -209,6 +209,45 @@ impl NewUserRef> for NonNull> { } } +/// A type which can a destination for safely copying from userspace. +/// +/// # Safety +/// +/// Requires that `T` and `Self` have identical layouts. +#[unstable(feature = "sgx_platform", issue = "56975")] +pub unsafe trait UserSafeCopyDestination { + /// Returns a pointer for writing to the value. + fn as_mut_ptr(&mut self) -> *mut T; +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +unsafe impl UserSafeCopyDestination for T { + fn as_mut_ptr(&mut self) -> *mut T { + self as _ + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +unsafe impl UserSafeCopyDestination<[T]> for [T] { + fn as_mut_ptr(&mut self) -> *mut [T] { + self as _ + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +unsafe impl UserSafeCopyDestination for MaybeUninit { + fn as_mut_ptr(&mut self) -> *mut T { + self as *mut Self as _ + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +unsafe impl UserSafeCopyDestination<[T]> for [MaybeUninit] { + fn as_mut_ptr(&mut self) -> *mut [T] { + self as *mut Self as _ + } +} + #[unstable(feature = "sgx_platform", issue = "56975")] impl User where @@ -239,7 +278,7 @@ where /// Copies `val` into freshly allocated space in user memory. pub fn new_from_enclave(val: &T) -> Self { unsafe { - let mut user = Self::new_uninit_bytes(mem::size_of_val(val)); + let mut user = Self::new_uninit_bytes(size_of_val(val)); user.copy_from_enclave(val); user } @@ -277,7 +316,7 @@ where { /// Allocates space for `T` in user memory. pub fn uninitialized() -> Self { - Self::new_uninit_bytes(mem::size_of::()) + Self::new_uninit_bytes(size_of::()) } } @@ -288,7 +327,7 @@ where { /// Allocates space for a `[T]` of `n` elements in user memory. pub fn uninitialized(n: usize) -> Self { - Self::new_uninit_bytes(n * mem::size_of::()) + Self::new_uninit_bytes(n * size_of::()) } /// Creates an owned `User<[T]>` from a raw thin pointer and a slice length. @@ -306,9 +345,7 @@ where /// * The pointed-to range does not fit in the address space /// * The pointed-to range is not in user memory pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self { - User(unsafe { - NonNull::new_userref(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::())) - }) + User(unsafe { NonNull::new_userref(<[T]>::from_raw_sized(ptr as _, len * size_of::())) }) } } @@ -326,7 +363,7 @@ where // `<*const u8>::align_offset` aren't _guaranteed_ to compute the largest // possible middle region, and as such can't be used. fn u64_align_to_guaranteed(ptr: *const u8, mut len: usize) -> (usize, usize, usize) { - const QWORD_SIZE: usize = mem::size_of::(); + const QWORD_SIZE: usize = size_of::(); let offset = ptr as usize % QWORD_SIZE; @@ -532,11 +569,11 @@ where /// the source. This can happen for dynamically-sized types such as slices. pub fn copy_from_enclave(&mut self, val: &T) { unsafe { - assert_eq!(mem::size_of_val(val), mem::size_of_val(&*self.0.get())); + assert_eq!(size_of_val(val), size_of_val(&*self.0.get())); copy_to_userspace( val as *const T as *const u8, self.0.get() as *mut T as *mut u8, - mem::size_of_val(val), + size_of_val(val), ); } } @@ -546,13 +583,13 @@ where /// # Panics /// This function panics if the destination doesn't have the same size as /// the source. This can happen for dynamically-sized types such as slices. - pub fn copy_to_enclave(&self, dest: &mut T) { + pub fn copy_to_enclave>(&self, dest: &mut U) { unsafe { - assert_eq!(mem::size_of_val(dest), mem::size_of_val(&*self.0.get())); + assert_eq!(size_of_val(dest), size_of_val(&*self.0.get())); copy_from_userspace( self.0.get() as *const T as *const u8, - dest as *mut T as *mut u8, - mem::size_of_val(dest), + dest.as_mut_ptr() as *mut u8, + size_of_val(dest), ); } } @@ -577,7 +614,7 @@ where pub fn to_enclave(&self) -> T { unsafe { let mut data = mem::MaybeUninit::uninit(); - copy_from_userspace(self.0.get() as _, data.as_mut_ptr() as _, mem::size_of::()); + copy_from_userspace(self.0.get() as _, data.as_mut_ptr() as _, size_of::()); data.assume_init() } } @@ -602,9 +639,7 @@ where /// * The pointed-to range is not in user memory pub unsafe fn from_raw_parts<'a>(ptr: *const T, len: usize) -> &'a Self { // SAFETY: The caller must uphold the safety contract for `from_raw_parts`. - unsafe { - &*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::()).as_ptr() as *const Self) - } + unsafe { &*(<[T]>::from_raw_sized(ptr as _, len * size_of::()).as_ptr() as *const Self) } } /// Creates a `&mut UserRef<[T]>` from a raw thin pointer and a slice length. @@ -624,7 +659,7 @@ where pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut T, len: usize) -> &'a mut Self { // SAFETY: The caller must uphold the safety contract for `from_raw_parts_mut`. unsafe { - &mut *(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::()).as_ptr() as *mut Self) + &mut *(<[T]>::from_raw_sized(ptr as _, len * size_of::()).as_ptr() as *mut Self) } } @@ -640,28 +675,21 @@ where /// Obtain the number of elements in this user slice. pub fn len(&self) -> usize { - unsafe { (*self.0.get()).len() } + unsafe { self.0.get().len() } } - /// Copies the value from user memory and place it into `dest`. Afterwards, - /// `dest` will contain exactly `self.len()` elements. - /// - /// # Panics - /// This function panics if the destination doesn't have the same size as - /// the source. This can happen for dynamically-sized types such as slices. - pub fn copy_to_enclave_vec(&self, dest: &mut Vec) { - if let Some(missing) = self.len().checked_sub(dest.capacity()) { - dest.reserve(missing) - } + /// Copies the value from user memory and appends it to `dest`. + pub fn append_to_enclave_vec(&self, dest: &mut Vec) { + dest.reserve(self.len()); + self.copy_to_enclave(&mut dest.spare_capacity_mut()[..self.len()]); // SAFETY: We reserve enough space above. - unsafe { dest.set_len(self.len()) }; - self.copy_to_enclave(&mut dest[..]); + unsafe { dest.set_len(dest.len() + self.len()) }; } /// Copies the value from user memory into a vector in enclave memory. pub fn to_enclave(&self) -> Vec { let mut ret = Vec::with_capacity(self.len()); - self.copy_to_enclave_vec(&mut ret); + self.append_to_enclave_vec(&mut ret); ret } @@ -744,7 +772,7 @@ where fn drop(&mut self) { unsafe { let ptr = (*self.0.as_ptr()).0.get(); - super::free(ptr as _, mem::size_of_val(&mut *ptr), T::align_of()); + super::free(ptr as _, size_of_val(&mut *ptr), T::align_of()); } } } diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs index 90b9b59471a52..cbdaf439b2847 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs @@ -1,5 +1,7 @@ use crate::cmp; -use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult}; +use crate::io::{ + BorrowedCursor, Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult, +}; use crate::random::{DefaultRandomSource, Random}; use crate::time::{Duration, Instant}; @@ -36,6 +38,19 @@ pub fn read(fd: Fd, bufs: &mut [IoSliceMut<'_>]) -> IoResult { } } +/// Usercall `read` with an uninitialized buffer. See the ABI documentation for +/// more information. +#[unstable(feature = "sgx_platform", issue = "56975")] +pub fn read_buf(fd: Fd, mut buf: BorrowedCursor<'_>) -> IoResult<()> { + unsafe { + let mut userbuf = alloc::User::<[u8]>::uninitialized(buf.capacity()); + let len = raw::read(fd, userbuf.as_mut_ptr().cast(), userbuf.len()).from_sgx_result()?; + userbuf[..len].copy_to_enclave(&mut buf.as_mut()[..len]); + buf.advance_unchecked(len); + Ok(()) + } +} + /// Usercall `read_alloc`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] pub fn read_alloc(fd: Fd) -> IoResult> { diff --git a/library/std/src/sys/pal/sgx/args.rs b/library/std/src/sys/pal/sgx/args.rs deleted file mode 100644 index e62bf383954eb..0000000000000 --- a/library/std/src/sys/pal/sgx/args.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::abi::usercalls::alloc; -use super::abi::usercalls::raw::ByteBuffer; -use crate::ffi::OsString; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sys::os_str::Buf; -use crate::sys_common::FromInner; -use crate::{fmt, slice}; - -#[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx4args4ARGSE")] -static ARGS: AtomicUsize = AtomicUsize::new(0); -type ArgsStore = Vec; - -#[cfg_attr(test, allow(dead_code))] -pub unsafe fn init(argc: isize, argv: *const *const u8) { - if argc != 0 { - let args = unsafe { alloc::User::<[ByteBuffer]>::from_raw_parts(argv as _, argc as _) }; - let args = args - .iter() - .map(|a| OsString::from_inner(Buf { inner: a.copy_user_buffer() })) - .collect::(); - ARGS.store(Box::into_raw(Box::new(args)) as _, Ordering::Relaxed); - } -} - -pub fn args() -> Args { - let args = unsafe { (ARGS.load(Ordering::Relaxed) as *const ArgsStore).as_ref() }; - if let Some(args) = args { Args(args.iter()) } else { Args([].iter()) } -} - -pub struct Args(slice::Iter<'static, OsString>); - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.as_slice().fmt(f) - } -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option { - self.0.next().cloned() - } - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.0.len() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.0.next_back().cloned() - } -} diff --git a/library/std/src/sys/pal/sgx/env.rs b/library/std/src/sys/pal/sgx/env.rs deleted file mode 100644 index 8043b7c5213a1..0000000000000 --- a/library/std/src/sys/pal/sgx/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = ""; - pub const OS: &str = ""; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ".sgxs"; - pub const DLL_EXTENSION: &str = "sgxs"; - pub const EXE_SUFFIX: &str = ".sgxs"; - pub const EXE_EXTENSION: &str = "sgxs"; -} diff --git a/library/std/src/sys/pal/sgx/fd.rs b/library/std/src/sys/pal/sgx/fd.rs deleted file mode 100644 index 3bb3189a1d127..0000000000000 --- a/library/std/src/sys/pal/sgx/fd.rs +++ /dev/null @@ -1,85 +0,0 @@ -use fortanix_sgx_abi::Fd; - -use super::abi::usercalls; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::mem::ManuallyDrop; -use crate::sys::{AsInner, FromInner, IntoInner}; - -#[derive(Debug)] -pub struct FileDesc { - fd: Fd, -} - -impl FileDesc { - pub fn new(fd: Fd) -> FileDesc { - FileDesc { fd } - } - - pub fn raw(&self) -> Fd { - self.fd - } - - /// Extracts the actual file descriptor without closing it. - pub fn into_raw(self) -> Fd { - ManuallyDrop::new(self).fd - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - usercalls::read(self.fd, &mut [IoSliceMut::new(buf)]) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - crate::io::default_read_buf(|b| self.read(b), 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 { - #[inline] - fn as_inner(&self) -> &Fd { - &self.fd - } -} - -impl IntoInner for FileDesc { - fn into_inner(self) -> Fd { - ManuallyDrop::new(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/library/std/src/sys/pal/sgx/libunwind_integration.rs b/library/std/src/sys/pal/sgx/libunwind_integration.rs index 6d0d78d1eb9b5..b5419ad05decd 100644 --- a/library/std/src/sys/pal/sgx/libunwind_integration.rs +++ b/library/std/src/sys/pal/sgx/libunwind_integration.rs @@ -4,6 +4,7 @@ #![cfg(not(test))] use crate::sys::sync::RwLock; +use crate::{slice, str}; // Verify that the byte pattern libunwind uses to initialize an RwLock is // equivalent to the value of RwLock::new(). If the value changes, @@ -44,3 +45,14 @@ pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RwLock) -> i32 { unsafe { (*p).write_unlock() }; return 0; } + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) { + if s < 0 { + return; + } + let buf = unsafe { 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/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 37ca6b08c950b..6e43a79ddec2c 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -6,21 +6,13 @@ #![allow(fuzzy_provenance_casts)] // FIXME: this entire module systematically confuses pointers and integers use crate::io::ErrorKind; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; pub mod abi; -pub mod args; -pub mod env; -pub mod fd; -#[path = "../unsupported/fs.rs"] -pub mod fs; mod libunwind_integration; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/process.rs"] -pub mod process; -pub mod stdio; pub mod thread; pub mod thread_parking; pub mod time; @@ -30,7 +22,7 @@ pub mod waitqueue; // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { unsafe { - args::init(argc, argv); + crate::sys::args::init(argc, argv); } } @@ -54,7 +46,7 @@ pub fn unsupported_err() -> crate::io::Error { /// what happens when `SGX_INEFFECTIVE_ERROR` is set to `true`. If it is /// `false`, the behavior is the same as `unsupported`. pub fn sgx_ineffective(v: T) -> crate::io::Result { - static SGX_INEFFECTIVE_ERROR: AtomicBool = AtomicBool::new(false); + static SGX_INEFFECTIVE_ERROR: Atomic = AtomicBool::new(false); if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) { Err(crate::io::const_error!( ErrorKind::Uncategorized, @@ -120,11 +112,14 @@ pub fn abort_internal() -> ! { abi::usercalls::exit(true) } -// This function is needed by the panic runtime. The symbol is named in +// This function is needed by libunwind. The symbol is named in // pre-link args for the target specification, so keep that in sync. +// Note: contrary to the `__rust_abort` in `crate::rt`, this uses `no_mangle` +// because it is actually used from C code. Because symbols annotated with +// #[rustc_std_internal_symbol] get mangled, this will not lead to linker +// conflicts. #[cfg(not(test))] #[unsafe(no_mangle)] -// NB. used by both libunwind and libpanic_abort pub extern "C" fn __rust_abort() { abort_internal(); } diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs index b1ec2afd764e6..70f838679c9ca 100644 --- a/library/std/src/sys/pal/sgx/os.rs +++ b/library/std/src/sys/pal/sgx/os.rs @@ -1,14 +1,11 @@ use fortanix_sgx_abi::{Error, RESULT_SUCCESS}; -use crate::collections::HashMap; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::{Mutex, Once}; use crate::sys::{decode_error_kind, sgx_ineffective, unsupported}; -use crate::{fmt, io, str, vec}; +use crate::{fmt, io, str}; pub fn errno() -> i32 { RESULT_SUCCESS @@ -73,99 +70,6 @@ pub fn current_exe() -> io::Result { unsupported() } -#[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx2os3ENVE")] -static ENV: AtomicUsize = AtomicUsize::new(0); -#[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx2os8ENV_INITE")] -static ENV_INIT: Once = Once::new(); -type EnvStore = Mutex>; - -fn get_env_store() -> Option<&'static EnvStore> { - unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() } -} - -fn create_env_store() -> &'static EnvStore { - ENV_INIT.call_once(|| { - ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed) - }); - unsafe { &*(ENV.load(Ordering::Relaxed) as *const EnvStore) } -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -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 { - let clone_to_vec = |map: &HashMap| -> Vec<_> { - map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() - }; - - let iter = get_env_store() - .map(|env| clone_to_vec(&env.lock().unwrap())) - .unwrap_or_default() - .into_iter(); - Env { iter } -} - -pub fn getenv(k: &OsStr) -> Option { - get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned()) -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let (k, v) = (k.to_owned(), v.to_owned()); - create_env_store().lock().unwrap().insert(k, v); - Ok(()) -} - -pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - if let Some(env) = get_env_store() { - env.lock().unwrap().remove(k); - } - Ok(()) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem in SGX") } diff --git a/library/std/src/sys/pal/sgx/stdio.rs b/library/std/src/sys/pal/sgx/stdio.rs deleted file mode 100644 index 726a93acae44b..0000000000000 --- a/library/std/src/sys/pal/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 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::io::DEFAULT_BUF_SIZE; - -pub fn is_ebadf(err: &io::Error) -> bool { - // FIXME: Rust normally maps Unix EBADF to `Uncategorized` - 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))] -#[unsafe(no_mangle)] -pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) { - if s < 0 { - return; - } - let buf = unsafe { 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/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index b6932df431f42..219ef1b7a9897 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -45,8 +45,9 @@ mod task_queue { } } + // Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] - #[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx6thread10TASK_QUEUEE")] + #[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx6thread10TASK_QUEUEE")] static TASK_QUEUE: Mutex> = Mutex::new(Vec::new()); pub(super) fn lock() -> MutexGuard<'static, Vec> { diff --git a/library/std/src/sys/pal/sgx/waitqueue/spin_mutex.rs b/library/std/src/sys/pal/sgx/waitqueue/spin_mutex.rs index f6e851ccaddfa..73c7a101d601d 100644 --- a/library/std/src/sys/pal/sgx/waitqueue/spin_mutex.rs +++ b/library/std/src/sys/pal/sgx/waitqueue/spin_mutex.rs @@ -7,12 +7,12 @@ mod tests; use crate::cell::UnsafeCell; use crate::hint; use crate::ops::{Deref, DerefMut}; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; #[derive(Default)] pub struct SpinMutex { value: UnsafeCell, - lock: AtomicBool, + lock: Atomic, } unsafe impl Send for SpinMutex {} diff --git a/library/std/src/sys/pal/solid/env.rs b/library/std/src/sys/pal/solid/env.rs deleted file mode 100644 index 6855c113b2893..0000000000000 --- a/library/std/src/sys/pal/solid/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = "itron"; - pub const OS: &str = "solid"; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} diff --git a/library/std/src/sys/pal/solid/fs.rs b/library/std/src/sys/pal/solid/fs.rs deleted file mode 100644 index 4e741943283d0..0000000000000 --- a/library/std/src/sys/pal/solid/fs.rs +++ /dev/null @@ -1,609 +0,0 @@ -use super::{abi, error}; -use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem::MaybeUninit; -use crate::os::raw::{c_int, c_short}; -use crate::os::solid::ffi::OsStrExt; -use crate::path::{Path, PathBuf}; -use crate::sync::Arc; -use crate::sys::time::SystemTime; -use crate::sys::unsupported; -pub use crate::sys_common::fs::exists; -use crate::sys_common::ignore_notfound; - -type CIntNotMinusOne = core::num::niche_types::NotAllOnes; - -/// A file descriptor. -#[derive(Clone, Copy)] -struct FileDesc { - fd: CIntNotMinusOne, -} - -impl FileDesc { - #[inline] - #[track_caller] - fn new(fd: c_int) -> FileDesc { - FileDesc { fd: CIntNotMinusOne::new(fd).expect("fd != -1") } - } - - #[inline] - fn raw(&self) -> c_int { - self.fd.as_inner() - } -} - -pub struct File { - fd: FileDesc, -} - -#[derive(Clone)] -pub struct FileAttr { - stat: abi::stat, -} - -// all DirEntry's will have a reference to this struct -struct InnerReadDir { - dirp: abi::S_DIR, - root: PathBuf, -} - -pub struct ReadDir { - inner: Arc, -} - -pub struct DirEntry { - entry: abi::dirent, - inner: Arc, -} - -#[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, -} - -#[derive(Copy, Clone, Debug, Default)] -pub struct FileTimes {} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FilePermissions(c_short); - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct FileType(c_short); - -#[derive(Debug)] -pub struct DirBuilder {} - -impl FileAttr { - pub fn size(&self) -> u64 { - self.stat.st_size as u64 - } - - pub fn perm(&self) -> FilePermissions { - FilePermissions(self.stat.st_mode) - } - - pub fn file_type(&self) -> FileType { - FileType(self.stat.st_mode) - } - - pub fn modified(&self) -> io::Result { - Ok(SystemTime::from_time_t(self.stat.st_mtime)) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from_time_t(self.stat.st_atime)) - } - - pub fn created(&self) -> io::Result { - Ok(SystemTime::from_time_t(self.stat.st_ctime)) - } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - (self.0 & abi::S_IWRITE) == 0 - } - - pub fn set_readonly(&mut self, readonly: bool) { - if readonly { - self.0 &= !abi::S_IWRITE; - } else { - self.0 |= abi::S_IWRITE; - } - } -} - -impl FileTimes { - pub fn set_accessed(&mut self, _t: SystemTime) {} - pub fn set_modified(&mut self, _t: SystemTime) {} -} - -impl FileType { - pub fn is_dir(&self) -> bool { - self.is(abi::S_IFDIR) - } - pub fn is_file(&self) -> bool { - self.is(abi::S_IFREG) - } - pub fn is_symlink(&self) -> bool { - false - } - - pub fn is(&self, mode: c_short) -> bool { - self.0 & abi::S_IFMT == mode - } -} - -pub fn readdir(p: &Path) -> io::Result { - unsafe { - let mut dir = MaybeUninit::uninit(); - error::SolidError::err_if_negative(abi::SOLID_FS_OpenDir( - cstr(p)?.as_ptr(), - dir.as_mut_ptr(), - )) - .map_err(|e| e.as_io_error())?; - let inner = Arc::new(InnerReadDir { dirp: dir.assume_init(), root: p.to_owned() }); - Ok(ReadDir { inner }) - } -} - -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; - - fn next(&mut self) -> Option> { - let entry = unsafe { - let mut out_entry = MaybeUninit::uninit(); - match error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir( - self.inner.dirp, - out_entry.as_mut_ptr(), - )) { - Ok(_) => out_entry.assume_init(), - Err(e) if e.as_raw() == abi::SOLID_ERR_NOTFOUND => return None, - Err(e) => return Some(Err(e.as_io_error())), - } - }; - - (entry.d_name[0] != 0).then(|| Ok(DirEntry { entry, inner: Arc::clone(&self.inner) })) - } -} - -impl Drop for InnerReadDir { - fn drop(&mut self) { - unsafe { abi::SOLID_FS_CloseDir(self.dirp) }; - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - self.inner.root.join(OsStr::from_bytes( - unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes(), - )) - } - - pub fn file_name(&self) -> OsString { - OsStr::from_bytes(unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes()) - .to_os_string() - } - - pub fn metadata(&self) -> io::Result { - lstat(&self.path()) - } - - pub fn file_type(&self) -> io::Result { - match self.entry.d_type { - abi::DT_CHR => Ok(FileType(abi::S_IFCHR)), - abi::DT_FIFO => Ok(FileType(abi::S_IFIFO)), - abi::DT_REG => Ok(FileType(abi::S_IFREG)), - abi::DT_DIR => Ok(FileType(abi::S_IFDIR)), - abi::DT_BLK => Ok(FileType(abi::S_IFBLK)), - _ => lstat(&self.path()).map(|m| m.file_type()), - } - } -} - -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, - } - } - - 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) {} - - fn get_access_mode(&self) -> io::Result { - match (self.read, self.write, self.append) { - (true, false, false) => Ok(abi::O_RDONLY), - (false, true, false) => Ok(abi::O_WRONLY), - (true, true, false) => Ok(abi::O_RDWR), - (false, _, true) => Ok(abi::O_WRONLY | abi::O_APPEND), - (true, _, true) => Ok(abi::O_RDWR | abi::O_APPEND), - (false, false, false) => Err(io::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(io::Error::from_raw_os_error(libc::EINVAL)); - } - } - (_, true) => { - if self.truncate && !self.create_new { - return Err(io::Error::from_raw_os_error(libc::EINVAL)); - } - } - } - - Ok(match (self.create, self.truncate, self.create_new) { - (false, false, false) => 0, - (true, false, false) => abi::O_CREAT, - (false, true, false) => abi::O_TRUNC, - (true, true, false) => abi::O_CREAT | abi::O_TRUNC, - (_, _, true) => abi::O_CREAT | abi::O_EXCL, - }) - } -} - -fn cstr(path: &Path) -> io::Result { - let path = path.as_os_str().as_bytes(); - - if !path.starts_with(br"\") { - // Relative paths aren't supported - return Err(crate::io::const_error!( - crate::io::ErrorKind::Unsupported, - "relative path is not supported on this platform", - )); - } - - // Apply the thread-safety wrapper - const SAFE_PREFIX: &[u8] = br"\TS"; - let wrapped_path = [SAFE_PREFIX, &path, &[0]].concat(); - - CString::from_vec_with_nul(wrapped_path).map_err(|_| { - crate::io::const_error!(io::ErrorKind::InvalidInput, "path provided contains a nul byte") - }) -} - -impl File { - pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let flags = opts.get_access_mode()? - | opts.get_creation_mode()? - | (opts.custom_flags as c_int & !abi::O_ACCMODE); - unsafe { - let mut fd = MaybeUninit::uninit(); - error::SolidError::err_if_negative(abi::SOLID_FS_Open( - fd.as_mut_ptr(), - cstr(path)?.as_ptr(), - flags, - )) - .map_err(|e| e.as_io_error())?; - Ok(File { fd: FileDesc::new(fd.assume_init()) }) - } - } - - pub fn file_attr(&self) -> io::Result { - unsupported() - } - - pub fn fsync(&self) -> io::Result<()> { - self.flush() - } - - pub fn datasync(&self) -> io::Result<()> { - self.flush() - } - - pub fn lock(&self) -> io::Result<()> { - unsupported() - } - - pub fn lock_shared(&self) -> io::Result<()> { - unsupported() - } - - pub fn try_lock(&self) -> io::Result { - unsupported() - } - - pub fn try_lock_shared(&self) -> io::Result { - unsupported() - } - - pub fn unlock(&self) -> io::Result<()> { - unsupported() - } - - pub fn truncate(&self, _size: u64) -> io::Result<()> { - unsupported() - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - unsafe { - let mut out_num_bytes = MaybeUninit::uninit(); - error::SolidError::err_if_negative(abi::SOLID_FS_Read( - self.fd.raw(), - buf.as_mut_ptr(), - buf.len(), - out_num_bytes.as_mut_ptr(), - )) - .map_err(|e| e.as_io_error())?; - Ok(out_num_bytes.assume_init()) - } - } - - pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - unsafe { - let len = cursor.capacity(); - let mut out_num_bytes = MaybeUninit::uninit(); - error::SolidError::err_if_negative(abi::SOLID_FS_Read( - self.fd.raw(), - cursor.as_mut().as_mut_ptr() as *mut u8, - len, - out_num_bytes.as_mut_ptr(), - )) - .map_err(|e| e.as_io_error())?; - - // Safety: `out_num_bytes` is filled by the successful call to - // `SOLID_FS_Read` - let num_bytes_read = out_num_bytes.assume_init(); - - // Safety: `num_bytes_read` bytes were written to the unfilled - // portion of the buffer - cursor.advance_unchecked(num_bytes_read); - - Ok(()) - } - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - crate::io::default_read_vectored(|buf| self.read(buf), bufs) - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - unsafe { - let mut out_num_bytes = MaybeUninit::uninit(); - error::SolidError::err_if_negative(abi::SOLID_FS_Write( - self.fd.raw(), - buf.as_ptr(), - buf.len(), - out_num_bytes.as_mut_ptr(), - )) - .map_err(|e| e.as_io_error())?; - Ok(out_num_bytes.assume_init()) - } - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - crate::io::default_write_vectored(|buf| self.write(buf), bufs) - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn flush(&self) -> io::Result<()> { - error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Sync(self.fd.raw()) }) - .map_err(|e| e.as_io_error())?; - 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 `SOLID_FS_Lseek`. - SeekFrom::Start(off) => (abi::SEEK_SET, off as i64), - SeekFrom::End(off) => (abi::SEEK_END, off), - SeekFrom::Current(off) => (abi::SEEK_CUR, off), - }; - error::SolidError::err_if_negative(unsafe { - abi::SOLID_FS_Lseek(self.fd.raw(), pos, whence) - }) - .map_err(|e| e.as_io_error())?; - // Get the new offset - self.tell() - } - - pub fn tell(&self) -> io::Result { - unsafe { - let mut out_offset = MaybeUninit::uninit(); - error::SolidError::err_if_negative(abi::SOLID_FS_Ftell( - self.fd.raw(), - out_offset.as_mut_ptr(), - )) - .map_err(|e| e.as_io_error())?; - Ok(out_offset.assume_init() as u64) - } - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - unsupported() - } - - pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { - unsupported() - } -} - -impl Drop for File { - fn drop(&mut self) { - unsafe { abi::SOLID_FS_Close(self.fd.raw()) }; - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder {} - } - - pub fn mkdir(&self, p: &Path) -> io::Result<()> { - error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Mkdir(cstr(p)?.as_ptr()) }) - .map_err(|e| e.as_io_error())?; - Ok(()) - } -} - -impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("File").field("fd", &self.fd.raw()).finish() - } -} - -pub fn unlink(p: &Path) -> io::Result<()> { - if stat(p)?.file_type().is_dir() { - Err(io::const_error!(io::ErrorKind::IsADirectory, "is a directory")) - } else { - error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) }) - .map_err(|e| e.as_io_error())?; - Ok(()) - } -} - -pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - error::SolidError::err_if_negative(unsafe { - abi::SOLID_FS_Rename(cstr(old)?.as_ptr(), cstr(new)?.as_ptr()) - }) - .map_err(|e| e.as_io_error())?; - Ok(()) -} - -pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - error::SolidError::err_if_negative(unsafe { - abi::SOLID_FS_Chmod(cstr(p)?.as_ptr(), perm.0.into()) - }) - .map_err(|e| e.as_io_error())?; - Ok(()) -} - -pub fn rmdir(p: &Path) -> io::Result<()> { - if stat(p)?.file_type().is_dir() { - error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) }) - .map_err(|e| e.as_io_error())?; - Ok(()) - } else { - Err(io::const_error!(io::ErrorKind::NotADirectory, "not a directory")) - } -} - -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - for child in readdir(path)? { - let result: io::Result<()> = try { - let child = child?; - let child_type = child.file_type()?; - if child_type.is_dir() { - remove_dir_all(&child.path())?; - } else { - unlink(&child.path())?; - } - }; - // ignore internal NotFound errors - if let Err(err) = &result - && err.kind() != io::ErrorKind::NotFound - { - return result; - } - } - ignore_notfound(rmdir(path)) -} - -pub fn readlink(p: &Path) -> io::Result { - // This target doesn't support symlinks - stat(p)?; - Err(io::const_error!(io::ErrorKind::InvalidInput, "not a symbolic link")) -} - -pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { - // This target doesn't support symlinks - unsupported() -} - -pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { - // This target doesn't support symlinks - unsupported() -} - -pub fn stat(p: &Path) -> io::Result { - // This target doesn't support symlinks - lstat(p) -} - -pub fn lstat(p: &Path) -> io::Result { - unsafe { - let mut out_stat = MaybeUninit::uninit(); - error::SolidError::err_if_negative(abi::SOLID_FS_Stat( - cstr(p)?.as_ptr(), - out_stat.as_mut_ptr(), - )) - .map_err(|e| e.as_io_error())?; - Ok(FileAttr { stat: out_stat.assume_init() }) - } -} - -pub fn canonicalize(_p: &Path) -> io::Result { - unsupported() -} - -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/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 06af7bfade059..0011cf256df74 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -16,19 +16,12 @@ pub mod itron { use super::unsupported; } -#[path = "../unsupported/args.rs"] -pub mod args; -pub mod env; // `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as // `crate::sys::error` pub(crate) mod error; -pub mod fs; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/process.rs"] -pub mod process; -pub mod stdio; pub use self::itron::{thread, thread_parking}; pub mod time; diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index e3b2e0aa50f4a..8f5976b0592ec 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -1,14 +1,8 @@ -use core::slice::memchr; - use super::{error, itron, unsupported}; use crate::error::Error as StdError; -use crate::ffi::{CStr, OsStr, OsString}; -use crate::os::raw::{c_char, c_int}; -use crate::os::solid::ffi::{OsStrExt, OsStringExt}; +use crate::ffi::{OsStr, OsString}; use crate::path::{self, PathBuf}; -use crate::sync::{PoisonError, RwLock}; -use crate::sys::common::small_c_string::run_with_cstr; -use crate::{fmt, io, vec}; +use crate::{fmt, io}; // `solid` directly maps `errno`s to μITRON error codes. impl itron::error::ItronError { @@ -75,138 +69,6 @@ pub fn current_exe() -> io::Result { unsupported() } -static ENV_LOCK: RwLock<()> = RwLock::new(()); - -pub fn env_read_lock() -> impl Drop { - ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -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() - } -} - -/// Returns a vector of (variable, value) byte-vector pairs for all the -/// environment variables of the current process. -pub fn env() -> Env { - unsafe extern "C" { - static mut environ: *const *const c_char; - } - - unsafe { - let _guard = env_read_lock(); - 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() }; - } - - 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) -> Option { - // environment variables with a nul byte can't be set, so their value is - // always None as well - run_with_cstr(k.as_bytes(), &|k| { - let _guard = env_read_lock(); - let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - - if v.is_null() { - Ok(None) - } else { - // SAFETY: `v` cannot be mutated while executing this line since we've a read lock - let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); - - Ok(Some(OsStringExt::from_vec(bytes))) - } - }) - .ok() - .flatten() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), &|k| { - run_with_cstr(v.as_bytes(), &|v| { - let _guard = ENV_LOCK.write(); - cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) - }) - }) -} - -pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), &|nbuf| { - let _guard = ENV_LOCK.write(); - cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) - }) -} - -/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this -/// function just returns a generic error. -fn cvt_env(t: c_int) -> io::Result { - if t == -1 { Err(io::const_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) } -} - pub fn temp_dir() -> PathBuf { panic!("no standard temporary directory on this platform") } diff --git a/library/std/src/sys/pal/solid/stdio.rs b/library/std/src/sys/pal/solid/stdio.rs deleted file mode 100644 index 50f0176967b2d..0000000000000 --- a/library/std/src/sys/pal/solid/stdio.rs +++ /dev/null @@ -1,80 +0,0 @@ -use super::abi; -use crate::io; - -pub struct Stdin; -pub struct Stdout; -pub struct Stderr; -struct PanicOutput; - -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 { - unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) }; - 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 { - unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) }; - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl PanicOutput { - pub const fn new() -> PanicOutput { - PanicOutput - } -} - -impl io::Write for PanicOutput { - fn write(&mut self, buf: &[u8]) -> io::Result { - unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) }; - 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 { - Some(PanicOutput::new()) -} diff --git a/library/std/src/sys/pal/solid/time.rs b/library/std/src/sys/pal/solid/time.rs index 3f9bbb0b63cdb..c39d715c6a6f6 100644 --- a/library/std/src/sys/pal/solid/time.rs +++ b/library/std/src/sys/pal/solid/time.rs @@ -35,7 +35,7 @@ impl SystemTime { SystemTime(t) } - pub(super) fn from_time_t(t: abi::time_t) -> Self { + pub fn from_time_t(t: abi::time_t) -> Self { Self(t) } diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index 3632524157db9..c7b1777725858 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -6,19 +6,9 @@ #![allow(unused_variables)] #![allow(dead_code)] -#[path = "../unsupported/args.rs"] -pub mod args; -#[path = "../unsupported/env.rs"] -pub mod env; -//pub mod fd; -#[path = "../unsupported/fs.rs"] -pub mod fs; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/process.rs"] -pub mod process; -pub mod stdio; pub mod thread; #[allow(non_upper_case_globals)] #[path = "../unix/time.rs"] diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index bf6945811ab0e..03f3c72b0229a 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -73,47 +73,6 @@ pub fn current_exe() -> io::Result { unsupported() } -pub struct Env(!); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self(inner) = self; - match *inner {} - } -} - -pub fn env() -> Env { - panic!("not supported on this platform") -} - -pub fn getenv(_: &OsStr) -> Option { - None -} - -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) -} - -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } diff --git a/library/std/src/sys/pal/teeos/stdio.rs b/library/std/src/sys/pal/teeos/stdio.rs deleted file mode 100644 index 67e251812da7f..0000000000000 --- a/library/std/src/sys/pal/teeos/stdio.rs +++ /dev/null @@ -1,89 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -use core::arch::asm; - -use crate::io; - -pub struct Stdin; -pub struct Stdout; -pub struct Stderr; - -const KCALL_DEBUG_CMD_PUT_BYTES: i64 = 2; - -unsafe fn debug_call(cap_ref: u64, call_no: i64, arg1: u64, arg2: u64) -> i32 { - let ret: u64; - unsafe { - asm!( - "svc #99", - inout("x0") cap_ref => ret, - in("x1") call_no, - in("x2") arg1, - in("x3") arg2, - ); - } - - ret as i32 -} - -fn print_buf(s: &[u8]) -> io::Result { - // Corresponds to `HM_DEBUG_PUT_BYTES_LIMIT`. - const MAX_LEN: usize = 512; - let len = if s.len() > MAX_LEN { MAX_LEN } else { s.len() }; - let result = unsafe { debug_call(0, KCALL_DEBUG_CMD_PUT_BYTES, s.as_ptr() as u64, len as u64) }; - - if result == 0 { Ok(len) } else { Err(io::Error::from(io::ErrorKind::InvalidInput)) } -} - -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 { - print_buf(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 { - print_buf(buf) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub const STDIN_BUF_SIZE: usize = 0; - -pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(libc::EBADF as i32) -} - -pub fn panic_output() -> Option { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/pal/trusty/mod.rs b/library/std/src/sys/pal/trusty/mod.rs new file mode 100644 index 0000000000000..275f606246336 --- /dev/null +++ b/library/std/src/sys/pal/trusty/mod.rs @@ -0,0 +1,15 @@ +//! System bindings for the Trusty OS. + +#[path = "../unsupported/common.rs"] +#[deny(unsafe_op_in_unsafe_fn)] +mod common; +#[path = "../unsupported/os.rs"] +pub mod os; +#[path = "../unsupported/pipe.rs"] +pub mod pipe; +#[path = "../unsupported/thread.rs"] +pub mod thread; +#[path = "../unsupported/time.rs"] +pub mod time; + +pub use common::*; diff --git a/library/std/src/sys/pal/uefi/args.rs b/library/std/src/sys/pal/uefi/args.rs deleted file mode 100644 index bdf6f5a0c1c34..0000000000000 --- a/library/std/src/sys/pal/uefi/args.rs +++ /dev/null @@ -1,157 +0,0 @@ -use r_efi::protocols::loaded_image; - -use super::helpers; -use crate::env::current_exe; -use crate::ffi::OsString; -use crate::iter::Iterator; -use crate::mem::size_of; -use crate::{fmt, vec}; - -pub struct Args { - parsed_args_list: vec::IntoIter, -} - -pub fn args() -> Args { - let lazy_current_exe = || Vec::from([current_exe().map(Into::into).unwrap_or_default()]); - - // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this - // will never fail. - let protocol = - helpers::image_handle_protocol::(loaded_image::PROTOCOL_GUID) - .unwrap(); - - let lp_size = unsafe { (*protocol.as_ptr()).load_options_size } as usize; - // Break if we are sure that it cannot be UTF-16 - if lp_size < size_of::() || lp_size % size_of::() != 0 { - return Args { parsed_args_list: lazy_current_exe().into_iter() }; - } - let lp_size = lp_size / size_of::(); - - let lp_cmd_line = unsafe { (*protocol.as_ptr()).load_options as *const u16 }; - if !lp_cmd_line.is_aligned() { - return Args { parsed_args_list: lazy_current_exe().into_iter() }; - } - let lp_cmd_line = unsafe { crate::slice::from_raw_parts(lp_cmd_line, lp_size) }; - - Args { - parsed_args_list: parse_lp_cmd_line(lp_cmd_line) - .unwrap_or_else(lazy_current_exe) - .into_iter(), - } -} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.parsed_args_list.as_slice().fmt(f) - } -} - -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 ExactSizeIterator for Args { - fn len(&self) -> usize { - self.parsed_args_list.len() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.parsed_args_list.next_back() - } -} - -/// Implements the UEFI command-line argument parsing algorithm. -/// -/// This implementation is based on what is defined in Section 3.4 of -/// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_Spec_2_0.pdf) -/// -/// Returns None in the following cases: -/// - Invalid UTF-16 (unpaired surrogate) -/// - Empty/improper arguments -fn parse_lp_cmd_line(code_units: &[u16]) -> Option> { - const QUOTE: char = '"'; - const SPACE: char = ' '; - const CARET: char = '^'; - const NULL: char = '\0'; - - let mut ret_val = Vec::new(); - let mut code_units_iter = char::decode_utf16(code_units.iter().cloned()).peekable(); - - // The executable name at the beginning is special. - let mut in_quotes = false; - let mut cur = String::new(); - while let Some(w) = code_units_iter.next() { - let w = w.ok()?; - match w { - // break on NULL - NULL => break, - // A quote mark always toggles `in_quotes` no matter what because - // there are no escape characters when parsing the executable name. - QUOTE => in_quotes = !in_quotes, - // If not `in_quotes` then whitespace ends argv[0]. - SPACE if !in_quotes => break, - // In all other cases the code unit is taken literally. - _ => cur.push(w), - } - } - - // If exe name is missing, the cli args are invalid - if cur.is_empty() { - return None; - } - - ret_val.push(OsString::from(cur)); - // Skip whitespace. - while code_units_iter.next_if_eq(&Ok(SPACE)).is_some() {} - - // Parse the arguments according to these rules: - // * All code units are taken literally except space, quote and caret. - // * When not `in_quotes`, space separate arguments. Consecutive spaces are - // treated as a single separator. - // * A space `in_quotes` is taken literally. - // * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally. - // * A quote can be escaped if preceded by caret. - // * A caret can be escaped if preceded by caret. - let mut cur = String::new(); - let mut in_quotes = false; - while let Some(w) = code_units_iter.next() { - let w = w.ok()?; - match w { - // break on NULL - NULL => break, - // If not `in_quotes`, a space or tab ends the argument. - SPACE if !in_quotes => { - ret_val.push(OsString::from(&cur[..])); - cur.truncate(0); - - // Skip whitespace. - while code_units_iter.next_if_eq(&Ok(SPACE)).is_some() {} - } - // Caret can escape quotes or carets - CARET if in_quotes => { - if let Some(x) = code_units_iter.next() { - cur.push(x.ok()?); - } - } - // If quote then flip `in_quotes` - QUOTE => in_quotes = !in_quotes, - // Everything else is always taken literally. - _ => cur.push(w), - } - } - // Push the final argument, if any. - if !cur.is_empty() || in_quotes { - ret_val.push(OsString::from(cur)); - } - Some(ret_val) -} diff --git a/library/std/src/sys/pal/uefi/env.rs b/library/std/src/sys/pal/uefi/env.rs deleted file mode 100644 index c106d5fed3e1d..0000000000000 --- a/library/std/src/sys/pal/uefi/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = ""; - pub const OS: &str = "uefi"; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ""; - pub const DLL_EXTENSION: &str = ""; - pub const EXE_SUFFIX: &str = ".efi"; - pub const EXE_EXTENSION: &str = "efi"; -} diff --git a/library/std/src/sys/pal/uefi/fs.rs b/library/std/src/sys/pal/uefi/fs.rs deleted file mode 100644 index 45e93deffa3a4..0000000000000 --- a/library/std/src/sys/pal/uefi/fs.rs +++ /dev/null @@ -1,348 +0,0 @@ -use crate::ffi::OsString; -use crate::fmt; -use crate::hash::{Hash, Hasher}; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::path::{Path, PathBuf}; -use crate::sys::time::SystemTime; -use crate::sys::unsupported; - -pub struct File(!); - -pub struct FileAttr(!); - -pub struct ReadDir(!); - -pub struct DirEntry(!); - -#[derive(Clone, Debug)] -pub struct OpenOptions {} - -#[derive(Copy, Clone, Debug, Default)] -pub struct FileTimes {} - -pub struct FilePermissions(!); - -pub struct FileType(!); - -#[derive(Debug)] -pub struct DirBuilder {} - -impl FileAttr { - pub fn size(&self) -> u64 { - self.0 - } - - pub fn perm(&self) -> FilePermissions { - self.0 - } - - pub fn file_type(&self) -> FileType { - self.0 - } - - pub fn modified(&self) -> io::Result { - self.0 - } - - pub fn accessed(&self) -> io::Result { - self.0 - } - - pub fn created(&self) -> io::Result { - self.0 - } -} - -impl Clone for FileAttr { - fn clone(&self) -> FileAttr { - self.0 - } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.0 - } - - pub fn set_readonly(&mut self, _readonly: bool) { - self.0 - } -} - -impl Clone for FilePermissions { - fn clone(&self) -> FilePermissions { - self.0 - } -} - -impl PartialEq for FilePermissions { - fn eq(&self, _other: &FilePermissions) -> bool { - self.0 - } -} - -impl Eq for FilePermissions {} - -impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -impl FileTimes { - pub fn set_accessed(&mut self, _t: SystemTime) {} - pub fn set_modified(&mut self, _t: SystemTime) {} -} - -impl FileType { - pub fn is_dir(&self) -> bool { - self.0 - } - - pub fn is_file(&self) -> bool { - self.0 - } - - pub fn is_symlink(&self) -> bool { - self.0 - } -} - -impl Clone for FileType { - fn clone(&self) -> FileType { - self.0 - } -} - -impl Copy for FileType {} - -impl PartialEq for FileType { - fn eq(&self, _other: &FileType) -> bool { - self.0 - } -} - -impl Eq for FileType {} - -impl Hash for FileType { - fn hash(&self, _h: &mut H) { - self.0 - } -} - -impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -impl Iterator for ReadDir { - type Item = io::Result; - - fn next(&mut self) -> Option> { - self.0 - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - self.0 - } - - pub fn file_name(&self) -> OsString { - self.0 - } - - pub fn metadata(&self) -> io::Result { - self.0 - } - - pub fn file_type(&self) -> io::Result { - self.0 - } -} - -impl OpenOptions { - pub fn new() -> OpenOptions { - OpenOptions {} - } - - pub fn read(&mut self, _read: bool) {} - pub fn write(&mut self, _write: bool) {} - pub fn append(&mut self, _append: bool) {} - pub fn truncate(&mut self, _truncate: bool) {} - pub fn create(&mut self, _create: bool) {} - pub fn create_new(&mut self, _create_new: bool) {} -} - -impl File { - pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { - unsupported() - } - - pub fn file_attr(&self) -> io::Result { - self.0 - } - - pub fn fsync(&self) -> io::Result<()> { - self.0 - } - - pub fn datasync(&self) -> io::Result<()> { - self.0 - } - - pub fn lock(&self) -> io::Result<()> { - self.0 - } - - pub fn lock_shared(&self) -> io::Result<()> { - self.0 - } - - pub fn try_lock(&self) -> io::Result { - self.0 - } - - pub fn try_lock_shared(&self) -> io::Result { - self.0 - } - - pub fn unlock(&self) -> io::Result<()> { - self.0 - } - - pub fn truncate(&self, _size: u64) -> io::Result<()> { - self.0 - } - - pub fn read(&self, _buf: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn write(&self, _buf: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { - self.0 - } - - pub fn is_write_vectored(&self) -> bool { - self.0 - } - - pub fn flush(&self) -> io::Result<()> { - self.0 - } - - pub fn seek(&self, _pos: SeekFrom) -> io::Result { - self.0 - } - - pub fn tell(&self) -> io::Result { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - self.0 - } - - pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { - self.0 - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder {} - } - - pub fn mkdir(&self, _p: &Path) -> io::Result<()> { - unsupported() - } -} - -impl fmt::Debug for File { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub fn readdir(_p: &Path) -> io::Result { - unsupported() -} - -pub fn unlink(_p: &Path) -> io::Result<()> { - unsupported() -} - -pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { - unsupported() -} - -pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { - match perm.0 {} -} - -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() -} - -pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - unsupported() -} - -pub fn exists(_path: &Path) -> io::Result { - unsupported() -} - -pub fn readlink(_p: &Path) -> io::Result { - unsupported() -} - -pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { - unsupported() -} - -pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { - unsupported() -} - -pub fn stat(_p: &Path) -> io::Result { - unsupported() -} - -pub fn lstat(_p: &Path) -> io::Result { - unsupported() -} - -pub fn canonicalize(_p: &Path) -> io::Result { - unsupported() -} - -pub fn copy(_from: &Path, _to: &Path) -> io::Result { - unsupported() -} diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index cb6aacd0063d4..6ee3e0a8b6625 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -10,19 +10,19 @@ //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) use r_efi::efi::{self, Guid}; -use r_efi::protocols::{device_path, device_path_to_text, shell}; +use r_efi::protocols::{device_path, device_path_to_text, service_binding, shell}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_error}; use crate::marker::PhantomData; -use crate::mem::{MaybeUninit, size_of}; +use crate::mem::MaybeUninit; use crate::os::uefi::env::boot_services; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::os::uefi::{self}; use crate::path::Path; use crate::ptr::NonNull; use crate::slice; -use crate::sync::atomic::{AtomicPtr, Ordering}; +use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::sys_common::wstr::WStrUnits; type BootInstallMultipleProtocolInterfaces = @@ -120,39 +120,6 @@ pub(crate) fn open_protocol( } } -pub(crate) fn create_event( - signal: u32, - tpl: efi::Tpl, - handler: Option, - context: *mut crate::ffi::c_void, -) -> io::Result> { - let boot_services: NonNull = - boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); - let mut event: r_efi::efi::Event = crate::ptr::null_mut(); - let r = unsafe { - let create_event = (*boot_services.as_ptr()).create_event; - (create_event)(signal, tpl, handler, context, &mut event) - }; - if r.is_error() { - Err(crate::io::Error::from_raw_os_error(r.as_usize())) - } else { - NonNull::new(event).ok_or(const_error!(io::ErrorKind::Other, "null protocol")) - } -} - -/// # SAFETY -/// - The supplied event must be valid -pub(crate) unsafe fn close_event(evt: NonNull) -> io::Result<()> { - let boot_services: NonNull = - boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); - let r = unsafe { - let close_event = (*boot_services.as_ptr()).close_event; - (close_event)(evt.as_ptr()) - }; - - if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } -} - /// Gets the Protocol for current system handle. /// /// Note: Some protocols need to be manually freed. It is the caller's responsibility to do so. @@ -190,7 +157,7 @@ pub(crate) fn device_path_to_text(path: NonNull) -> io::R Ok(path) } - static LAST_VALID_HANDLE: AtomicPtr = + static LAST_VALID_HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { @@ -216,6 +183,60 @@ pub(crate) fn device_path_to_text(path: NonNull) -> io::R Err(io::const_error!(io::ErrorKind::NotFound, "no device path to text protocol found")) } +fn device_node_to_text(path: NonNull) -> io::Result { + fn node_to_text( + protocol: NonNull, + path: NonNull, + ) -> io::Result { + let path_ptr: *mut r_efi::efi::Char16 = unsafe { + ((*protocol.as_ptr()).convert_device_node_to_text)( + path.as_ptr(), + // DisplayOnly + r_efi::efi::Boolean::FALSE, + // AllowShortcuts + r_efi::efi::Boolean::FALSE, + ) + }; + + let path = os_string_from_raw(path_ptr) + .ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path"))?; + + if let Some(boot_services) = crate::os::uefi::env::boot_services() { + let boot_services: NonNull = boot_services.cast(); + unsafe { + ((*boot_services.as_ptr()).free_pool)(path_ptr.cast()); + } + } + + Ok(path) + } + + static LAST_VALID_HANDLE: AtomicPtr = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = open_protocol::( + handle, + device_path_to_text::PROTOCOL_GUID, + ) { + return node_to_text(protocol, path); + } + } + + let device_path_to_text_handles = locate_handles(device_path_to_text::PROTOCOL_GUID)?; + for handle in device_path_to_text_handles { + if let Ok(protocol) = open_protocol::( + handle, + device_path_to_text::PROTOCOL_GUID, + ) { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return node_to_text(protocol, path); + } + } + + Err(io::const_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) +} + /// Gets RuntimeServices. pub(crate) fn runtime_services() -> Option> { let system_table: NonNull = @@ -248,7 +269,7 @@ impl OwnedDevicePath { .ok_or_else(|| const_error!(io::ErrorKind::InvalidFilename, "invalid Device Path")) } - static LAST_VALID_HANDLE: AtomicPtr = + static LAST_VALID_HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { @@ -319,6 +340,10 @@ impl<'a> BorrowedDevicePath<'a> { pub(crate) fn to_text(&self) -> io::Result { device_path_to_text(self.protocol) } + + pub(crate) const fn iter(&'a self) -> DevicePathIterator<'a> { + DevicePathIterator::new(DevicePathNode::new(self.protocol)) + } } impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> { @@ -330,6 +355,124 @@ impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> { } } +pub(crate) struct DevicePathIterator<'a>(Option>); + +impl<'a> DevicePathIterator<'a> { + const fn new(node: DevicePathNode<'a>) -> Self { + if node.is_end() { Self(None) } else { Self(Some(node)) } + } +} + +impl<'a> Iterator for DevicePathIterator<'a> { + type Item = DevicePathNode<'a>; + + fn next(&mut self) -> Option { + let cur_node = self.0?; + + let next_node = unsafe { cur_node.next_node() }; + self.0 = if next_node.is_end() { None } else { Some(next_node) }; + + Some(cur_node) + } +} + +#[derive(Copy, Clone)] +pub(crate) struct DevicePathNode<'a> { + protocol: NonNull, + phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>, +} + +impl<'a> DevicePathNode<'a> { + pub(crate) const fn new(protocol: NonNull) -> Self { + Self { protocol, phantom: PhantomData } + } + + pub(crate) const fn length(&self) -> u16 { + let len = unsafe { (*self.protocol.as_ptr()).length }; + u16::from_le_bytes(len) + } + + pub(crate) const fn node_type(&self) -> u8 { + unsafe { (*self.protocol.as_ptr()).r#type } + } + + pub(crate) const fn sub_type(&self) -> u8 { + unsafe { (*self.protocol.as_ptr()).sub_type } + } + + pub(crate) fn data(&self) -> &[u8] { + let length: usize = self.length().into(); + + // Some nodes do not have any special data + if length > 4 { + let raw_ptr: *const u8 = self.protocol.as_ptr().cast(); + let data = unsafe { raw_ptr.add(4) }; + unsafe { crate::slice::from_raw_parts(data, length - 4) } + } else { + &[] + } + } + + pub(crate) const fn is_end(&self) -> bool { + self.node_type() == r_efi::protocols::device_path::TYPE_END + && self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_ENTIRE + } + + pub(crate) const fn is_end_instance(&self) -> bool { + self.node_type() == r_efi::protocols::device_path::TYPE_END + && self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_INSTANCE + } + + pub(crate) unsafe fn next_node(&self) -> Self { + let node = unsafe { + self.protocol + .cast::() + .add(self.length().into()) + .cast::() + }; + Self::new(node) + } + + pub(crate) fn to_path(&'a self) -> BorrowedDevicePath<'a> { + BorrowedDevicePath::new(self.protocol) + } + + pub(crate) fn to_text(&self) -> io::Result { + device_node_to_text(self.protocol) + } +} + +impl<'a> PartialEq for DevicePathNode<'a> { + fn eq(&self, other: &Self) -> bool { + let self_len = self.length(); + let other_len = other.length(); + + self_len == other_len + && unsafe { + compiler_builtins::mem::memcmp( + self.protocol.as_ptr().cast(), + other.protocol.as_ptr().cast(), + usize::from(self_len), + ) == 0 + } + } +} + +impl<'a> crate::fmt::Debug for DevicePathNode<'a> { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + match self.to_text() { + Ok(p) => p.fmt(f), + Err(_) => f + .debug_struct("DevicePathNode") + .field("type", &self.node_type()) + .field("sub_type", &self.sub_type()) + .field("length", &self.length()) + .field("specific_device_path_data", &self.data()) + .finish(), + } + } +} + pub(crate) struct OwnedProtocol { guid: r_efi::efi::Guid, handle: NonNull, @@ -463,7 +606,7 @@ pub(crate) fn os_string_to_raw(s: &OsStr) -> Option> { } pub(crate) fn open_shell() -> Option> { - static LAST_VALID_HANDLE: AtomicPtr = + static LAST_VALID_HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { @@ -500,3 +643,115 @@ pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result, + child_handle: NonNull, +} + +impl ServiceProtocol { + #[expect(dead_code)] + pub(crate) fn open(service_guid: r_efi::efi::Guid) -> io::Result { + let handles = locate_handles(service_guid)?; + + for handle in handles { + if let Ok(protocol) = open_protocol::(handle, service_guid) { + let Ok(child_handle) = Self::create_child(protocol) else { + continue; + }; + + return Ok(Self { service_guid, handle, child_handle }); + } + } + + Err(io::const_error!(io::ErrorKind::NotFound, "no service binding protocol found")) + } + + #[expect(dead_code)] + pub(crate) fn child_handle(&self) -> NonNull { + self.child_handle + } + + fn create_child( + sbp: NonNull, + ) -> io::Result> { + let mut child_handle: r_efi::efi::Handle = crate::ptr::null_mut(); + // SAFETY: A new handle is allocated if a pointer to NULL is passed. + let r = unsafe { ((*sbp.as_ptr()).create_child)(sbp.as_ptr(), &mut child_handle) }; + + if r.is_error() { + Err(crate::io::Error::from_raw_os_error(r.as_usize())) + } else { + NonNull::new(child_handle) + .ok_or(const_error!(io::ErrorKind::Other, "null child handle")) + } + } +} + +impl Drop for ServiceProtocol { + fn drop(&mut self) { + if let Ok(sbp) = open_protocol::(self.handle, self.service_guid) + { + // SAFETY: Child handle must be allocated by the current service binding protocol. + let _ = unsafe { + ((*sbp.as_ptr()).destroy_child)(sbp.as_ptr(), self.child_handle.as_ptr()) + }; + } + } +} + +#[repr(transparent)] +pub(crate) struct OwnedEvent(NonNull); + +impl OwnedEvent { + pub(crate) fn new( + signal: u32, + tpl: efi::Tpl, + handler: Option, + context: Option>, + ) -> io::Result { + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let mut event: r_efi::efi::Event = crate::ptr::null_mut(); + let context = context.map(NonNull::as_ptr).unwrap_or(crate::ptr::null_mut()); + + let r = unsafe { + let create_event = (*boot_services.as_ptr()).create_event; + (create_event)(signal, tpl, handler, context, &mut event) + }; + + if r.is_error() { + Err(crate::io::Error::from_raw_os_error(r.as_usize())) + } else { + NonNull::new(event) + .ok_or(const_error!(io::ErrorKind::Other, "failed to create event")) + .map(Self) + } + } + + pub(crate) fn into_raw(self) -> *mut crate::ffi::c_void { + let r = self.0.as_ptr(); + crate::mem::forget(self); + r + } + + /// SAFETY: Assumes that ptr is a non-null valid UEFI event + pub(crate) unsafe fn from_raw(ptr: *mut crate::ffi::c_void) -> Self { + Self(unsafe { NonNull::new_unchecked(ptr) }) + } +} + +impl Drop for OwnedEvent { + fn drop(&mut self) { + if let Some(boot_services) = boot_services() { + let bt: NonNull = boot_services.cast(); + unsafe { + let close_event = (*bt.as_ptr()).close_event; + (close_event)(self.0.as_ptr()) + }; + } + } +} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 6a03e240c6bd4..8911a2ee5194d 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -13,15 +13,10 @@ //! [`OsString`]: crate::ffi::OsString #![forbid(unsafe_op_in_unsafe_fn)] -pub mod args; -pub mod env; -pub mod fs; pub mod helpers; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod process; -pub mod stdio; pub mod thread; pub mod time; @@ -33,9 +28,9 @@ pub type RawOsError = usize; use crate::io as std_io; use crate::os::uefi; use crate::ptr::NonNull; -use crate::sync::atomic::{AtomicPtr, Ordering}; +use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; -static EXIT_BOOT_SERVICE_EVENT: AtomicPtr = +static EXIT_BOOT_SERVICE_EVENT: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); /// # SAFETY @@ -49,17 +44,17 @@ pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { unsafe { uefi::env::init_globals(image_handle, system_table) }; // Register exit boot services handler - match helpers::create_event( + match helpers::OwnedEvent::new( r_efi::efi::EVT_SIGNAL_EXIT_BOOT_SERVICES, r_efi::efi::TPL_NOTIFY, Some(exit_boot_service_handler), - crate::ptr::null_mut(), + None, ) { Ok(x) => { if EXIT_BOOT_SERVICE_EVENT .compare_exchange( crate::ptr::null_mut(), - x.as_ptr(), + x.into_raw(), Ordering::Release, Ordering::Acquire, ) @@ -79,7 +74,7 @@ pub unsafe fn cleanup() { if let Some(exit_boot_service_event) = NonNull::new(EXIT_BOOT_SERVICE_EVENT.swap(crate::ptr::null_mut(), Ordering::Acquire)) { - let _ = unsafe { helpers::close_event(exit_boot_service_event) }; + let _ = unsafe { helpers::OwnedEvent::from_raw(exit_boot_service_event.as_ptr()) }; } } @@ -145,7 +140,7 @@ pub fn abort_internal() -> ! { if let Some(exit_boot_service_event) = NonNull::new(EXIT_BOOT_SERVICE_EVENT.load(Ordering::Acquire)) { - let _ = unsafe { helpers::close_event(exit_boot_service_event) }; + let _ = unsafe { helpers::OwnedEvent::from_raw(exit_boot_service_event.as_ptr()) }; } if let (Some(boot_services), Some(handle)) = @@ -166,14 +161,6 @@ pub fn abort_internal() -> ! { core::intrinsics::abort(); } -// 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))] -#[unsafe(no_mangle)] -pub extern "C" fn __rust_abort() { - abort_internal(); -} - /// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { uefi::env::disable_boot_services(); diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index d26d61890c19e..bfd4dc81cb44f 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -131,60 +131,6 @@ pub fn current_exe() -> io::Result { helpers::device_path_to_text(protocol).map(PathBuf::from) } -pub struct EnvStrDebug<'a> { - iter: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut list = f.debug_list(); - for (a, b) in self.iter { - list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); - } - list.finish() - } -} - -pub struct Env(crate::vec::IntoIter<(OsString, OsString)>); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - EnvStrDebug { iter: self.0.as_slice() } - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option<(OsString, OsString)> { - self.0.next() - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -pub fn env() -> Env { - let env = uefi_env::get_all().expect("not supported on this platform"); - Env(env.into_iter()) -} - -pub fn getenv(key: &OsStr) -> Option { - uefi_env::get(key) -} - -pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> { - uefi_env::set(key, val) -} - -pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> { - uefi_env::unset(key) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } @@ -213,85 +159,3 @@ pub fn exit(code: i32) -> ! { pub fn getpid() -> u32 { panic!("no pids on this platform") } - -mod uefi_env { - use crate::ffi::{OsStr, OsString}; - use crate::io; - use crate::os::uefi::ffi::OsStringExt; - use crate::ptr::NonNull; - use crate::sys::{helpers, unsupported_err}; - - pub(crate) fn get(key: &OsStr) -> Option { - let shell = helpers::open_shell()?; - let mut key_ptr = helpers::os_string_to_raw(key)?; - unsafe { get_raw(shell, key_ptr.as_mut_ptr()) } - } - - pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> { - let mut key_ptr = helpers::os_string_to_raw(key) - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; - let mut val_ptr = helpers::os_string_to_raw(val) - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?; - unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) } - } - - pub(crate) fn unset(key: &OsStr) -> io::Result<()> { - let mut key_ptr = helpers::os_string_to_raw(key) - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; - unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) } - } - - pub(crate) fn get_all() -> io::Result> { - let shell = helpers::open_shell().ok_or(unsupported_err())?; - - let mut vars = Vec::new(); - let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) }; - - if val.is_null() { - return Ok(vars); - } - - let mut start = 0; - - // UEFI Shell returns all keys separated by NULL. - // End of string is denoted by two NULLs - for i in 0.. { - if unsafe { *val.add(i) } == 0 { - // Two NULL signal end of string - if i == start { - break; - } - - let key = OsString::from_wide(unsafe { - crate::slice::from_raw_parts(val.add(start), i - start) - }); - // SAFETY: val.add(start) is always NULL terminated - let val = unsafe { get_raw(shell, val.add(start)) } - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?; - - vars.push((key, val)); - start = i + 1; - } - } - - Ok(vars) - } - - unsafe fn get_raw( - shell: NonNull, - key_ptr: *mut r_efi::efi::Char16, - ) -> Option { - let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) }; - helpers::os_string_from_raw(val) - } - - unsafe fn set_raw( - key_ptr: *mut r_efi::efi::Char16, - val_ptr: *mut r_efi::efi::Char16, - ) -> io::Result<()> { - let shell = helpers::open_shell().ok_or(unsupported_err())?; - let r = - unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) }; - if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } - } -} diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs deleted file mode 100644 index a47c8dbcaaafc..0000000000000 --- a/library/std/src/sys/pal/uefi/process.rs +++ /dev/null @@ -1,786 +0,0 @@ -use r_efi::protocols::simple_text_output; - -use super::helpers; -use crate::collections::BTreeMap; -pub use crate::ffi::OsString as EnvKey; -use crate::ffi::{OsStr, OsString}; -use crate::num::{NonZero, NonZeroI32}; -use crate::path::Path; -use crate::sys::fs::File; -use crate::sys::pipe::AnonPipe; -use crate::sys::unsupported; -use crate::sys_common::process::{CommandEnv, CommandEnvs}; -use crate::{fmt, io}; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Debug)] -pub struct Command { - prog: OsString, - args: Vec, - stdout: Option, - stderr: Option, - 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, -} - -#[derive(Copy, Clone, Debug)] -pub enum Stdio { - Inherit, - Null, - MakePipe, -} - -impl Command { - pub fn new(program: &OsStr) -> Command { - Command { - prog: program.to_os_string(), - args: Vec::new(), - stdout: None, - stderr: None, - env: Default::default(), - } - } - - 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) { - panic!("unsupported") - } - - pub fn stdin(&mut self, _stdin: Stdio) { - panic!("unsupported") - } - - pub fn stdout(&mut self, stdout: Stdio) { - self.stdout = Some(stdout); - } - - pub fn stderr(&mut self, stderr: Stdio) { - self.stderr = Some(stderr); - } - - pub fn get_program(&self) -> &OsStr { - self.prog.as_ref() - } - - pub fn get_args(&self) -> CommandArgs<'_> { - CommandArgs { iter: self.args.iter() } - } - - pub fn get_envs(&self) -> CommandEnvs<'_> { - self.env.iter() - } - - pub fn get_current_dir(&self) -> Option<&Path> { - None - } - - pub fn spawn( - &mut self, - _default: Stdio, - _needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - unsupported() - } - - fn create_pipe( - s: Stdio, - ) -> io::Result>> { - match s { - Stdio::MakePipe => unsafe { - helpers::OwnedProtocol::create( - uefi_command_internal::PipeProtocol::new(), - simple_text_output::PROTOCOL_GUID, - ) - } - .map(Some), - Stdio::Null => unsafe { - helpers::OwnedProtocol::create( - uefi_command_internal::PipeProtocol::null(), - simple_text_output::PROTOCOL_GUID, - ) - } - .map(Some), - Stdio::Inherit => Ok(None), - } - } - - pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - let mut cmd = uefi_command_internal::Image::load_image(&self.prog)?; - - // UEFI adds the bin name by default - if !self.args.is_empty() { - let args = uefi_command_internal::create_args(&self.prog, &self.args); - cmd.set_args(args); - } - - // Setup Stdout - let stdout = self.stdout.unwrap_or(Stdio::MakePipe); - let stdout = Self::create_pipe(stdout)?; - if let Some(con) = stdout { - cmd.stdout_init(con) - } else { - cmd.stdout_inherit() - }; - - // Setup Stderr - let stderr = self.stderr.unwrap_or(Stdio::MakePipe); - let stderr = Self::create_pipe(stderr)?; - if let Some(con) = stderr { - cmd.stderr_init(con) - } else { - cmd.stderr_inherit() - }; - - let env = env_changes(&self.env); - - // Set any new vars - if let Some(e) = &env { - for (k, (_, v)) in e { - match v { - Some(v) => unsafe { crate::env::set_var(k, v) }, - None => unsafe { crate::env::remove_var(k) }, - } - } - } - - let stat = cmd.start_image()?; - - // Rollback any env changes - if let Some(e) = env { - for (k, (v, _)) in e { - match v { - Some(v) => unsafe { crate::env::set_var(k, v) }, - None => unsafe { crate::env::remove_var(k) }, - } - } - } - - let stdout = cmd.stdout()?; - let stderr = cmd.stderr()?; - - Ok((ExitStatus(stat), stdout, stderr)) - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - pipe.diverge() - } -} - -impl From for Stdio { - fn from(_: io::Stdout) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") - } -} - -impl From for Stdio { - fn from(_: io::Stderr) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") - } -} - -impl From for Stdio { - fn from(_file: File) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[non_exhaustive] -pub struct ExitStatus(r_efi::efi::Status); - -impl ExitStatus { - pub fn exit_ok(&self) -> Result<(), ExitStatusError> { - if self.0 == r_efi::efi::Status::SUCCESS { Ok(()) } else { Err(ExitStatusError(self.0)) } - } - - pub fn code(&self) -> Option { - Some(self.0.as_usize() as i32) - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let err_str = super::os::error_string(self.0.as_usize()); - write!(f, "{}", err_str) - } -} - -impl Default for ExitStatus { - fn default() -> Self { - ExitStatus(r_efi::efi::Status::SUCCESS) - } -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct ExitStatusError(r_efi::efi::Status); - -impl fmt::Debug for ExitStatusError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let err_str = super::os::error_string(self.0.as_usize()); - write!(f, "{}", err_str) - } -} - -impl Into for ExitStatusError { - fn into(self) -> ExitStatus { - ExitStatus(self.0) - } -} - -impl ExitStatusError { - pub fn code(self) -> Option> { - NonZeroI32::new(self.0.as_usize() as i32) - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(bool); - -impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(false); - pub const FAILURE: ExitCode = ExitCode(true); - - pub fn as_i32(&self) -> i32 { - self.0 as i32 - } -} - -impl From for ExitCode { - fn from(code: u8) -> Self { - match code { - 0 => Self::SUCCESS, - 1..=255 => Self::FAILURE, - } - } -} - -pub struct Process(!); - -impl Process { - pub fn id(&self) -> u32 { - self.0 - } - - pub fn kill(&mut self) -> io::Result<()> { - self.0 - } - - pub fn wait(&mut self) -> io::Result { - self.0 - } - - pub fn try_wait(&mut self) -> io::Result> { - self.0 - } -} - -pub struct CommandArgs<'a> { - iter: crate::slice::Iter<'a, OsString>, -} - -impl<'a> Iterator for CommandArgs<'a> { - type Item = &'a OsStr; - - fn next(&mut self) -> Option<&'a OsStr> { - self.iter.next().map(|x| x.as_ref()) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl<'a> ExactSizeIterator for CommandArgs<'a> { - fn len(&self) -> usize { - self.iter.len() - } - - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -impl<'a> fmt::Debug for CommandArgs<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter.clone()).finish() - } -} - -#[allow(dead_code)] -mod uefi_command_internal { - use r_efi::protocols::{loaded_image, simple_text_output}; - - use super::super::helpers; - use crate::ffi::{OsStr, OsString}; - use crate::io::{self, const_error}; - use crate::mem::MaybeUninit; - use crate::os::uefi::env::{boot_services, image_handle, system_table}; - use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; - use crate::ptr::NonNull; - use crate::slice; - use crate::sys::pal::uefi::helpers::OwnedTable; - use crate::sys_common::wstr::WStrUnits; - - pub struct Image { - handle: NonNull, - stdout: Option>, - stderr: Option>, - st: OwnedTable, - args: Option<(*mut u16, usize)>, - } - - impl Image { - pub fn load_image(p: &OsStr) -> io::Result { - let path = helpers::OwnedDevicePath::from_text(p)?; - let boot_services: NonNull = boot_services() - .ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))? - .cast(); - let mut child_handle: MaybeUninit = MaybeUninit::uninit(); - let image_handle = image_handle(); - - let r = unsafe { - ((*boot_services.as_ptr()).load_image)( - r_efi::efi::Boolean::FALSE, - image_handle.as_ptr(), - path.as_ptr(), - crate::ptr::null_mut(), - 0, - child_handle.as_mut_ptr(), - ) - }; - - if r.is_error() { - Err(io::Error::from_raw_os_error(r.as_usize())) - } else { - let child_handle = unsafe { child_handle.assume_init() }; - let child_handle = NonNull::new(child_handle).unwrap(); - - let loaded_image: NonNull = - helpers::open_protocol(child_handle, loaded_image::PROTOCOL_GUID).unwrap(); - let st = OwnedTable::from_table(unsafe { (*loaded_image.as_ptr()).system_table }); - - Ok(Self { handle: child_handle, stdout: None, stderr: None, st, args: None }) - } - } - - pub(crate) fn start_image(&mut self) -> io::Result { - self.update_st_crc32()?; - - // Use our system table instead of the default one - let loaded_image: NonNull = - helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); - unsafe { - (*loaded_image.as_ptr()).system_table = self.st.as_mut_ptr(); - } - - let boot_services: NonNull = boot_services() - .ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))? - .cast(); - let mut exit_data_size: usize = 0; - let mut exit_data: MaybeUninit<*mut u16> = MaybeUninit::uninit(); - - let r = unsafe { - ((*boot_services.as_ptr()).start_image)( - self.handle.as_ptr(), - &mut exit_data_size, - exit_data.as_mut_ptr(), - ) - }; - - // Drop exitdata - if exit_data_size != 0 { - unsafe { - let exit_data = exit_data.assume_init(); - ((*boot_services.as_ptr()).free_pool)(exit_data as *mut crate::ffi::c_void); - } - } - - Ok(r) - } - - fn set_stdout( - &mut self, - handle: r_efi::efi::Handle, - protocol: *mut simple_text_output::Protocol, - ) { - unsafe { - (*self.st.as_mut_ptr()).console_out_handle = handle; - (*self.st.as_mut_ptr()).con_out = protocol; - } - } - - fn set_stderr( - &mut self, - handle: r_efi::efi::Handle, - protocol: *mut simple_text_output::Protocol, - ) { - unsafe { - (*self.st.as_mut_ptr()).standard_error_handle = handle; - (*self.st.as_mut_ptr()).std_err = protocol; - } - } - - pub fn stdout_init(&mut self, protocol: helpers::OwnedProtocol) { - self.set_stdout( - protocol.handle().as_ptr(), - protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol, - ); - self.stdout = Some(protocol); - } - - pub fn stdout_inherit(&mut self) { - let st: NonNull = system_table().cast(); - unsafe { self.set_stdout((*st.as_ptr()).console_out_handle, (*st.as_ptr()).con_out) } - } - - pub fn stderr_init(&mut self, protocol: helpers::OwnedProtocol) { - self.set_stderr( - protocol.handle().as_ptr(), - protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol, - ); - self.stderr = Some(protocol); - } - - pub fn stderr_inherit(&mut self) { - let st: NonNull = system_table().cast(); - unsafe { self.set_stderr((*st.as_ptr()).standard_error_handle, (*st.as_ptr()).std_err) } - } - - pub fn stderr(&self) -> io::Result> { - match &self.stderr { - Some(stderr) => stderr.as_ref().utf8(), - None => Ok(Vec::new()), - } - } - - pub fn stdout(&self) -> io::Result> { - match &self.stdout { - Some(stdout) => stdout.as_ref().utf8(), - None => Ok(Vec::new()), - } - } - - pub fn set_args(&mut self, args: Box<[u16]>) { - let loaded_image: NonNull = - helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); - - let len = args.len(); - let args_size: u32 = (len * crate::mem::size_of::()).try_into().unwrap(); - let ptr = Box::into_raw(args).as_mut_ptr(); - - unsafe { - (*loaded_image.as_ptr()).load_options = ptr as *mut crate::ffi::c_void; - (*loaded_image.as_ptr()).load_options_size = args_size; - } - - self.args = Some((ptr, len)); - } - - fn update_st_crc32(&mut self) -> io::Result<()> { - let bt: NonNull = boot_services().unwrap().cast(); - let st_size = unsafe { (*self.st.as_ptr()).hdr.header_size as usize }; - let mut crc32: u32 = 0; - - // Set crc to 0 before calculation - unsafe { - (*self.st.as_mut_ptr()).hdr.crc32 = 0; - } - - let r = unsafe { - ((*bt.as_ptr()).calculate_crc32)( - self.st.as_mut_ptr() as *mut crate::ffi::c_void, - st_size, - &mut crc32, - ) - }; - - if r.is_error() { - Err(io::Error::from_raw_os_error(r.as_usize())) - } else { - unsafe { - (*self.st.as_mut_ptr()).hdr.crc32 = crc32; - } - Ok(()) - } - } - } - - impl Drop for Image { - fn drop(&mut self) { - if let Some(bt) = boot_services() { - let bt: NonNull = bt.cast(); - unsafe { - ((*bt.as_ptr()).unload_image)(self.handle.as_ptr()); - } - } - - if let Some((ptr, len)) = self.args { - let _ = unsafe { Box::from_raw(crate::ptr::slice_from_raw_parts_mut(ptr, len)) }; - } - } - } - - #[repr(C)] - pub struct PipeProtocol { - reset: simple_text_output::ProtocolReset, - output_string: simple_text_output::ProtocolOutputString, - test_string: simple_text_output::ProtocolTestString, - query_mode: simple_text_output::ProtocolQueryMode, - set_mode: simple_text_output::ProtocolSetMode, - set_attribute: simple_text_output::ProtocolSetAttribute, - clear_screen: simple_text_output::ProtocolClearScreen, - set_cursor_position: simple_text_output::ProtocolSetCursorPosition, - enable_cursor: simple_text_output::ProtocolEnableCursor, - mode: *mut simple_text_output::Mode, - _buffer: Vec, - } - - impl PipeProtocol { - pub fn new() -> Self { - let mode = Box::new(simple_text_output::Mode { - max_mode: 0, - mode: 0, - attribute: 0, - cursor_column: 0, - cursor_row: 0, - cursor_visible: r_efi::efi::Boolean::FALSE, - }); - Self { - reset: Self::reset, - output_string: Self::output_string, - test_string: Self::test_string, - query_mode: Self::query_mode, - set_mode: Self::set_mode, - set_attribute: Self::set_attribute, - clear_screen: Self::clear_screen, - set_cursor_position: Self::set_cursor_position, - enable_cursor: Self::enable_cursor, - mode: Box::into_raw(mode), - _buffer: Vec::new(), - } - } - - pub fn null() -> Self { - let mode = Box::new(simple_text_output::Mode { - max_mode: 0, - mode: 0, - attribute: 0, - cursor_column: 0, - cursor_row: 0, - cursor_visible: r_efi::efi::Boolean::FALSE, - }); - Self { - reset: Self::reset_null, - output_string: Self::output_string_null, - test_string: Self::test_string, - query_mode: Self::query_mode, - set_mode: Self::set_mode, - set_attribute: Self::set_attribute, - clear_screen: Self::clear_screen, - set_cursor_position: Self::set_cursor_position, - enable_cursor: Self::enable_cursor, - mode: Box::into_raw(mode), - _buffer: Vec::new(), - } - } - - pub fn utf8(&self) -> io::Result> { - OsString::from_wide(&self._buffer) - .into_string() - .map(Into::into) - .map_err(|_| const_error!(io::ErrorKind::Other, "UTF-8 conversion failed")) - } - - extern "efiapi" fn reset( - proto: *mut simple_text_output::Protocol, - _: r_efi::efi::Boolean, - ) -> r_efi::efi::Status { - let proto: *mut PipeProtocol = proto.cast(); - unsafe { - (*proto)._buffer.clear(); - } - r_efi::efi::Status::SUCCESS - } - - extern "efiapi" fn reset_null( - _: *mut simple_text_output::Protocol, - _: r_efi::efi::Boolean, - ) -> r_efi::efi::Status { - r_efi::efi::Status::SUCCESS - } - - extern "efiapi" fn output_string( - proto: *mut simple_text_output::Protocol, - buf: *mut r_efi::efi::Char16, - ) -> r_efi::efi::Status { - let proto: *mut PipeProtocol = proto.cast(); - let buf_len = unsafe { - if let Some(x) = WStrUnits::new(buf) { - x.count() - } else { - return r_efi::efi::Status::INVALID_PARAMETER; - } - }; - let buf_slice = unsafe { slice::from_raw_parts(buf, buf_len) }; - - unsafe { - (*proto)._buffer.extend_from_slice(buf_slice); - }; - - r_efi::efi::Status::SUCCESS - } - - extern "efiapi" fn output_string_null( - _: *mut simple_text_output::Protocol, - _: *mut r_efi::efi::Char16, - ) -> r_efi::efi::Status { - r_efi::efi::Status::SUCCESS - } - - extern "efiapi" fn test_string( - _: *mut simple_text_output::Protocol, - _: *mut r_efi::efi::Char16, - ) -> r_efi::efi::Status { - r_efi::efi::Status::SUCCESS - } - - extern "efiapi" fn query_mode( - _: *mut simple_text_output::Protocol, - _: usize, - _: *mut usize, - _: *mut usize, - ) -> r_efi::efi::Status { - r_efi::efi::Status::UNSUPPORTED - } - - extern "efiapi" fn set_mode( - _: *mut simple_text_output::Protocol, - _: usize, - ) -> r_efi::efi::Status { - r_efi::efi::Status::UNSUPPORTED - } - - extern "efiapi" fn set_attribute( - _: *mut simple_text_output::Protocol, - _: usize, - ) -> r_efi::efi::Status { - r_efi::efi::Status::UNSUPPORTED - } - - extern "efiapi" fn clear_screen( - _: *mut simple_text_output::Protocol, - ) -> r_efi::efi::Status { - r_efi::efi::Status::UNSUPPORTED - } - - extern "efiapi" fn set_cursor_position( - _: *mut simple_text_output::Protocol, - _: usize, - _: usize, - ) -> r_efi::efi::Status { - r_efi::efi::Status::UNSUPPORTED - } - - extern "efiapi" fn enable_cursor( - _: *mut simple_text_output::Protocol, - _: r_efi::efi::Boolean, - ) -> r_efi::efi::Status { - r_efi::efi::Status::UNSUPPORTED - } - } - - impl Drop for PipeProtocol { - fn drop(&mut self) { - unsafe { - let _ = Box::from_raw(self.mode); - } - } - } - - pub fn create_args(prog: &OsStr, args: &[OsString]) -> Box<[u16]> { - const QUOTE: u16 = 0x0022; - const SPACE: u16 = 0x0020; - const CARET: u16 = 0x005e; - const NULL: u16 = 0; - - // This is the lower bound on the final length under the assumption that - // the arguments only contain ASCII characters. - let mut res = Vec::with_capacity(args.iter().map(|arg| arg.len() + 3).sum()); - - // Wrap program name in quotes to avoid any problems - res.push(QUOTE); - res.extend(prog.encode_wide()); - res.push(QUOTE); - - for arg in args { - res.push(SPACE); - - // Wrap the argument in quotes to be treat as single arg - res.push(QUOTE); - for c in arg.encode_wide() { - // CARET in quotes is used to escape CARET or QUOTE - if c == QUOTE || c == CARET { - res.push(CARET); - } - res.push(c); - } - res.push(QUOTE); - } - - res.into_boxed_slice() - } -} - -/// Create a map of environment variable changes. Allows efficient setting and rolling back of -/// enviroment variable changes. -/// -/// Entry: (Old Value, New Value) -fn env_changes(env: &CommandEnv) -> Option, Option)>> { - if env.is_unchanged() { - return None; - } - - let mut result = BTreeMap::, Option)>::new(); - - // Check if we want to clear all prior variables - if env.does_clear() { - for (k, v) in crate::env::vars_os() { - result.insert(k.into(), (Some(v), None)); - } - } - - for (k, v) in env.iter() { - let v: Option = v.map(Into::into); - result - .entry(k.into()) - .and_modify(|cur| *cur = (cur.0.clone(), v.clone())) - .or_insert((crate::env::var_os(k), v)); - } - - Some(result) -} diff --git a/library/std/src/sys/pal/uefi/stdio.rs b/library/std/src/sys/pal/uefi/stdio.rs deleted file mode 100644 index 257e321dd03d7..0000000000000 --- a/library/std/src/sys/pal/uefi/stdio.rs +++ /dev/null @@ -1,219 +0,0 @@ -use crate::io; -use crate::iter::Iterator; -use crate::mem::MaybeUninit; -use crate::os::uefi; -use crate::ptr::NonNull; - -pub struct Stdin { - surrogate: Option, - incomplete_utf8: IncompleteUtf8, -} - -struct IncompleteUtf8 { - bytes: [u8; 4], - len: u8, -} - -impl IncompleteUtf8 { - pub const fn new() -> IncompleteUtf8 { - IncompleteUtf8 { bytes: [0; 4], len: 0 } - } - - // Implemented for use in Stdin::read. - fn read(&mut self, buf: &mut [u8]) -> usize { - // Write to buffer until the buffer is full or we run out of bytes. - let to_write = crate::cmp::min(buf.len(), self.len as usize); - buf[..to_write].copy_from_slice(&self.bytes[..to_write]); - - // Rotate the remaining bytes if not enough remaining space in buffer. - if usize::from(self.len) > buf.len() { - self.bytes.copy_within(to_write.., 0); - self.len -= to_write as u8; - } else { - self.len = 0; - } - - to_write - } -} - -pub struct Stdout; -pub struct Stderr; - -impl Stdin { - pub const fn new() -> Stdin { - Stdin { surrogate: None, incomplete_utf8: IncompleteUtf8::new() } - } -} - -impl io::Read for Stdin { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - // If there are bytes in the incomplete utf-8, start with those. - // (No-op if there is nothing in the buffer.) - let mut bytes_copied = self.incomplete_utf8.read(buf); - - let stdin: *mut r_efi::protocols::simple_text_input::Protocol = unsafe { - let st: NonNull = uefi::env::system_table().cast(); - (*st.as_ptr()).con_in - }; - - if bytes_copied == buf.len() { - return Ok(bytes_copied); - } - - let ch = simple_text_input_read(stdin)?; - // Only 1 character should be returned. - let mut ch: Vec> = - if let Some(x) = self.surrogate.take() { - char::decode_utf16([x, ch]).collect() - } else { - char::decode_utf16([ch]).collect() - }; - - if ch.len() > 1 { - return Err(io::const_error!(io::ErrorKind::InvalidData, "invalid UTF-16 sequence")); - } - - match ch.pop().unwrap() { - Err(e) => { - self.surrogate = Some(e.unpaired_surrogate()); - } - Ok(x) => { - // This will always be > 0 - let buf_free_count = buf.len() - bytes_copied; - assert!(buf_free_count > 0); - - if buf_free_count >= x.len_utf8() { - // There is enough space in the buffer for the character. - bytes_copied += x.encode_utf8(&mut buf[bytes_copied..]).len(); - } else { - // There is not enough space in the buffer for the character. - // Store the character in the incomplete buffer. - self.incomplete_utf8.len = - x.encode_utf8(&mut self.incomplete_utf8.bytes).len() as u8; - // write partial character to buffer. - bytes_copied += self.incomplete_utf8.read(buf); - } - } - } - - Ok(bytes_copied) - } -} - -impl Stdout { - pub const fn new() -> Stdout { - Stdout - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - let st: NonNull = uefi::env::system_table().cast(); - let stdout = unsafe { (*st.as_ptr()).con_out }; - - write(stdout, 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 { - let st: NonNull = uefi::env::system_table().cast(); - let stderr = unsafe { (*st.as_ptr()).std_err }; - - write(stderr, buf) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -// UTF-16 character should occupy 4 bytes at most in UTF-8 -pub const STDIN_BUF_SIZE: usize = 4; - -pub fn is_ebadf(_err: &io::Error) -> bool { - false -} - -pub fn panic_output() -> Option { - uefi::env::try_system_table().map(|_| Stderr::new()) -} - -fn write( - protocol: *mut r_efi::protocols::simple_text_output::Protocol, - buf: &[u8], -) -> io::Result { - // Get valid UTF-8 buffer - let utf8 = match crate::str::from_utf8(buf) { - Ok(x) => x, - Err(e) => unsafe { crate::str::from_utf8_unchecked(&buf[..e.valid_up_to()]) }, - }; - - let mut utf16: Vec = utf8.encode_utf16().collect(); - // NULL terminate the string - utf16.push(0); - - unsafe { simple_text_output(protocol, &mut utf16) }?; - - Ok(utf8.len()) -} - -unsafe fn simple_text_output( - protocol: *mut r_efi::protocols::simple_text_output::Protocol, - buf: &mut [u16], -) -> io::Result<()> { - let res = unsafe { ((*protocol).output_string)(protocol, buf.as_mut_ptr()) }; - if res.is_error() { Err(io::Error::from_raw_os_error(res.as_usize())) } else { Ok(()) } -} - -fn simple_text_input_read( - stdin: *mut r_efi::protocols::simple_text_input::Protocol, -) -> io::Result { - loop { - match read_key_stroke(stdin) { - Ok(x) => return Ok(x.unicode_char), - Err(e) if e == r_efi::efi::Status::NOT_READY => wait_stdin(stdin)?, - Err(e) => return Err(io::Error::from_raw_os_error(e.as_usize())), - } - } -} - -fn wait_stdin(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> { - let boot_services: NonNull = - uefi::env::boot_services().unwrap().cast(); - let wait_for_event = unsafe { (*boot_services.as_ptr()).wait_for_event }; - let wait_for_key_event = unsafe { (*stdin).wait_for_key }; - - let r = { - let mut x: usize = 0; - (wait_for_event)(1, [wait_for_key_event].as_mut_ptr(), &mut x) - }; - if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } -} - -fn read_key_stroke( - stdin: *mut r_efi::protocols::simple_text_input::Protocol, -) -> Result { - let mut input_key: MaybeUninit = - MaybeUninit::uninit(); - - let r = unsafe { ((*stdin).read_key_stroke)(stdin, input_key.as_mut_ptr()) }; - - if r.is_error() { - Err(r) - } else { - let input_key = unsafe { input_key.assume_init() }; - Ok(input_key) - } -} diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs index 5eb36da922b54..38658cc4e9ac4 100644 --- a/library/std/src/sys/pal/uefi/tests.rs +++ b/library/std/src/sys/pal/uefi/tests.rs @@ -16,7 +16,7 @@ fn align() { if *j <= 8 { assert_eq!(align_size(i, *j), i); } else { - assert!(align_size(i, *j) > i + std::mem::size_of::<*mut ()>()); + assert!(align_size(i, *j) > i + size_of::<*mut ()>()); } } } diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs index c4ff3015ac60d..eeb2c35ffbbc9 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -121,7 +121,7 @@ pub(crate) mod instant_internal { use super::*; use crate::mem::MaybeUninit; use crate::ptr::NonNull; - use crate::sync::atomic::{AtomicPtr, Ordering}; + use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::sys_common::mul_div_u64; const NS_PER_SEC: u64 = 1_000_000_000; @@ -142,7 +142,7 @@ pub(crate) mod instant_internal { Some(mul_div_u64(ts, NS_PER_SEC, freq)) } - static LAST_VALID_HANDLE: AtomicPtr = + static LAST_VALID_HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs deleted file mode 100644 index 1c87a79803c0d..0000000000000 --- a/library/std/src/sys/pal/unix/args.rs +++ /dev/null @@ -1,242 +0,0 @@ -//! Global initialization and retrieval of command line arguments. -//! -//! On some platforms these are stored during runtime startup, -//! and on some they are retrieved from the system on demand. - -#![allow(dead_code)] // runtime init functions not used during testing - -use crate::ffi::{CStr, OsString}; -use crate::os::unix::ffi::OsStringExt; -use crate::{fmt, vec}; - -/// One-time global initialization. -pub unsafe fn init(argc: isize, argv: *const *const u8) { - imp::init(argc, argv) -} - -/// Returns the command line arguments -pub fn args() -> Args { - let (argc, argv) = imp::argc_argv(); - - let mut vec = Vec::with_capacity(argc as usize); - - for i in 0..argc { - // SAFETY: `argv` is non-null if `argc` is positive, and it is - // guaranteed to be at least as long as `argc`, so reading from it - // should be safe. - let ptr = unsafe { argv.offset(i).read() }; - - // Some C commandline parsers (e.g. GLib and Qt) are replacing already - // handled arguments in `argv` with `NULL` and move them to the end. - // - // Since they can't directly ensure updates to `argc` as well, this - // means that `argc` might be bigger than the actual number of - // non-`NULL` pointers in `argv` at this point. - // - // To handle this we simply stop iterating at the first `NULL` - // argument. `argv` is also guaranteed to be `NULL`-terminated so any - // non-`NULL` arguments after the first `NULL` can safely be ignored. - if ptr.is_null() { - // NOTE: On Apple platforms, `-[NSProcessInfo arguments]` does not - // stop iterating here, but instead `continue`, always iterating - // up until it reached `argc`. - // - // This difference will only matter in very specific circumstances - // where `argc`/`argv` have been modified, but in unexpected ways, - // so it likely doesn't really matter which option we choose. - // See the following PR for further discussion: - // - break; - } - - // SAFETY: Just checked that the pointer is not NULL, and arguments - // are otherwise guaranteed to be valid C strings. - let cstr = unsafe { CStr::from_ptr(ptr) }; - vec.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); - } - - Args { iter: vec.into_iter() } -} - -pub struct Args { - iter: vec::IntoIter, -} - -impl !Send for Args {} -impl !Sync for Args {} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.iter.as_slice().fmt(f) - } -} - -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() - } -} - -#[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", - target_os = "emscripten", - target_os = "haiku", - target_os = "l4re", - target_os = "fuchsia", - target_os = "redox", - target_os = "vxworks", - target_os = "horizon", - target_os = "aix", - target_os = "nto", - target_os = "hurd", - target_os = "rtems", - target_os = "nuttx", -))] -mod imp { - use crate::ffi::c_char; - use crate::ptr; - use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering}; - - // The system-provided argc and argv, which we store in static memory - // here so that we can defer the work of parsing them until its actually - // needed. - // - // Note that we never mutate argv/argc, the argv array, or the argv - // strings, which allows the code in this file to be very simple. - static ARGC: AtomicIsize = AtomicIsize::new(0); - static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut()); - - unsafe fn really_init(argc: isize, argv: *const *const u8) { - // These don't need to be ordered with each other or other stores, - // because they only hold the unmodified system-provide argv/argc. - ARGC.store(argc, Ordering::Relaxed); - ARGV.store(argv as *mut _, Ordering::Relaxed); - } - - #[inline(always)] - pub unsafe fn init(argc: isize, argv: *const *const u8) { - // on GNU/Linux if we are main then we will init argv and argc twice, it "duplicates work" - // BUT edge-cases are real: only using .init_array can break most emulators, dlopen, etc. - really_init(argc, argv); - } - - /// 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`, as it does on macOS and Windows. - #[cfg(all(target_os = "linux", target_env = "gnu"))] - #[used] - #[unsafe(link_section = ".init_array.00099")] - static ARGV_INIT_ARRAY: extern "C" fn( - crate::os::raw::c_int, - *const *const u8, - *const *const u8, - ) = { - extern "C" fn init_wrapper( - argc: crate::os::raw::c_int, - argv: *const *const u8, - _envp: *const *const u8, - ) { - unsafe { - really_init(argc as isize, argv); - } - } - init_wrapper - }; - - pub fn argc_argv() -> (isize, *const *const c_char) { - // Load ARGC and ARGV, which hold the unmodified system-provided - // argc/argv, so we can read the pointed-to memory without atomics or - // synchronization. - // - // If either ARGC or ARGV is still zero or null, then either there - // really are no arguments, or someone is asking for `args()` before - // initialization has completed, and we return an empty list. - let argv = ARGV.load(Ordering::Relaxed); - let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) }; - - // Cast from `*mut *const u8` to `*const *const c_char` - (argc, argv.cast()) - } -} - -// Use `_NSGetArgc` and `_NSGetArgv` on Apple platforms. -// -// Even though these have underscores in their names, they've been available -// since the first versions of both macOS and iOS, and are declared in -// the header `crt_externs.h`. -// -// NOTE: This header was added to the iOS 13.0 SDK, which has been the source -// of a great deal of confusion in the past about the availability of these -// APIs. -// -// NOTE(madsmtm): This has not strictly been verified to not cause App Store -// rejections; if this is found to be the case, the previous implementation -// of this used `[[NSProcessInfo processInfo] arguments]`. -#[cfg(target_vendor = "apple")] -mod imp { - use crate::ffi::{c_char, c_int}; - - pub unsafe fn init(_argc: isize, _argv: *const *const u8) { - // No need to initialize anything in here, `libdyld.dylib` has already - // done the work for us. - } - - pub fn argc_argv() -> (isize, *const *const c_char) { - unsafe extern "C" { - // These functions are in crt_externs.h. - fn _NSGetArgc() -> *mut c_int; - fn _NSGetArgv() -> *mut *mut *mut c_char; - } - - // SAFETY: The returned pointer points to a static initialized early - // in the program lifetime by `libdyld.dylib`, and as such is always - // valid. - // - // NOTE: Similar to `_NSGetEnviron`, there technically isn't anything - // protecting us against concurrent modifications to this, and there - // doesn't exist a lock that we can take. Instead, it is generally - // expected that it's only modified in `main` / before other code - // runs, so reading this here should be fine. - let argc = unsafe { _NSGetArgc().read() }; - // SAFETY: Same as above. - let argv = unsafe { _NSGetArgv().read() }; - - // Cast from `*mut *mut c_char` to `*const *const c_char` - (argc as isize, argv.cast()) - } -} - -#[cfg(any(target_os = "espidf", target_os = "vita"))] -mod imp { - use crate::ffi::c_char; - use crate::ptr; - - #[inline(always)] - pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} - - pub fn argc_argv() -> (isize, *const *const c_char) { - (0, ptr::null()) - } -} diff --git a/library/std/src/sys/pal/unix/env.rs b/library/std/src/sys/pal/unix/env.rs deleted file mode 100644 index 2aee0b5d46056..0000000000000 --- a/library/std/src/sys/pal/unix/env.rs +++ /dev/null @@ -1,296 +0,0 @@ -#[cfg(target_os = "linux")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "linux"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "macos")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "macos"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".dylib"; - pub const DLL_EXTENSION: &str = "dylib"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "ios")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "ios"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".dylib"; - pub const DLL_EXTENSION: &str = "dylib"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "tvos")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "tvos"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".dylib"; - pub const DLL_EXTENSION: &str = "dylib"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "watchos")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "watchos"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".dylib"; - pub const DLL_EXTENSION: &str = "dylib"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "visionos")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "visionos"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".dylib"; - pub const DLL_EXTENSION: &str = "dylib"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "freebsd")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "freebsd"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "dragonfly")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "dragonfly"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "netbsd")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "netbsd"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "openbsd")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "openbsd"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "android")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "android"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "solaris")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "solaris"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "illumos")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "illumos"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "haiku")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "haiku"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "horizon")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "horizon"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ".elf"; - pub const EXE_EXTENSION: &str = "elf"; -} - -#[cfg(target_os = "hurd")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "hurd"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "vita")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "vita"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ".elf"; - pub const EXE_EXTENSION: &str = "elf"; -} - -#[cfg(all(target_os = "emscripten", target_arch = "wasm32"))] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "emscripten"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ".js"; - pub const EXE_EXTENSION: &str = "js"; -} - -#[cfg(target_os = "fuchsia")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "fuchsia"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "l4re")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "l4re"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "nto")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "nto"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "redox")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "redox"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "rtems")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "rtems"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "vxworks")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "vxworks"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "espidf")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "espidf"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "aix")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "aix"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".a"; - pub const DLL_EXTENSION: &str = "a"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - -#[cfg(target_os = "nuttx")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "nuttx"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs deleted file mode 100644 index 2fc33bdfefbf5..0000000000000 --- a/library/std/src/sys/pal/unix/fd.rs +++ /dev/null @@ -1,639 +0,0 @@ -#![unstable(reason = "not public", issue = "none", feature = "fd")] - -#[cfg(test)] -mod tests; - -#[cfg(not(any( - target_os = "linux", - target_os = "l4re", - target_os = "android", - target_os = "hurd", -)))] -use libc::off_t as off64_t; -#[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "l4re", - target_os = "hurd", -))] -use libc::off64_t; - -use crate::cmp; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::cvt; -use crate::sys_common::{AsInner, FromInner, IntoInner}; - -#[derive(Debug)] -pub struct FileDesc(OwnedFd); - -// 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 Apple targets 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. -const READ_LIMIT: usize = if cfg!(target_vendor = "apple") { - libc::c_int::MAX as usize - 1 -} else { - libc::ssize_t::MAX as usize -}; - -#[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_vendor = "apple", -))] -const fn max_iov() -> usize { - libc::IOV_MAX as usize -} - -#[cfg(any( - target_os = "android", - target_os = "emscripten", - target_os = "linux", - target_os = "nto", -))] -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 = "linux", - target_os = "netbsd", - target_os = "nto", - target_os = "openbsd", - target_os = "horizon", - target_os = "vita", - target_vendor = "apple", -)))] -const fn max_iov() -> usize { - 16 // The minimum value required by POSIX. -} - -impl FileDesc { - #[inline] - pub fn try_clone(&self) -> io::Result { - self.duplicate() - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let ret = cvt(unsafe { - libc::read( - self.as_raw_fd(), - buf.as_mut_ptr() as *mut libc::c_void, - cmp::min(buf.len(), READ_LIMIT), - ) - })?; - Ok(ret as usize) - } - - #[cfg(not(any( - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "nuttx" - )))] - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let ret = cvt(unsafe { - libc::readv( - self.as_raw_fd(), - bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - ) - })?; - Ok(ret as usize) - } - - #[cfg(any( - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "nuttx" - ))] - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - io::default_read_vectored(|b| self.read(b), bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - cfg!(not(any( - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "nuttx" - ))) - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - let mut me = self; - (&mut me).read_to_end(buf) - } - - #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] - pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - #[cfg(not(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "hurd" - )))] - use libc::pread as pread64; - #[cfg(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "hurd" - ))] - use libc::pread64; - - unsafe { - cvt(pread64( - self.as_raw_fd(), - buf.as_mut_ptr() as *mut libc::c_void, - cmp::min(buf.len(), READ_LIMIT), - offset as off64_t, - )) - .map(|n| n as usize) - } - } - - pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - let ret = cvt(unsafe { - libc::read( - self.as_raw_fd(), - cursor.as_mut().as_mut_ptr() as *mut libc::c_void, - cmp::min(cursor.capacity(), READ_LIMIT), - ) - })?; - - // Safety: `ret` bytes were written to the initialized portion of the buffer - unsafe { - cursor.advance_unchecked(ret as usize); - } - Ok(()) - } - - #[cfg(any( - target_os = "aix", - target_os = "dragonfly", // DragonFly 1.5 - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", // OpenBSD 2.7 - ))] - pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - let ret = cvt(unsafe { - libc::preadv( - self.as_raw_fd(), - bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - - #[cfg(not(any( - target_os = "aix", - target_os = "android", - target_os = "dragonfly", - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_vendor = "apple", - )))] - pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - io::default_read_vectored(|b| self.read_at(b, offset), bufs) - } - - // We support some old Android versions that do not have `preadv` in libc, - // so we use weak linkage and fallback to a direct syscall if not available. - // - // On 32-bit targets, we don't want to deal with weird ABI issues around - // passing 64-bits parameters to syscalls, so we fallback to the default - // implementation if `preadv` is not available. - #[cfg(all(target_os = "android", target_pointer_width = "64"))] - pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - super::weak::syscall! { - fn preadv( - fd: libc::c_int, - iovec: *const libc::iovec, - n_iovec: libc::c_int, - offset: off64_t - ) -> isize - } - - let ret = cvt(unsafe { - preadv( - self.as_raw_fd(), - bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - - #[cfg(all(target_os = "android", target_pointer_width = "32"))] - pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); - - match preadv64.get() { - Some(preadv) => { - let ret = cvt(unsafe { - preadv( - self.as_raw_fd(), - bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - None => io::default_read_vectored(|b| self.read_at(b, offset), bufs), - } - } - - // We support old MacOS, iOS, watchOS, tvOS and visionOS. `preadv` was added in the following - // Apple OS versions: - // ios 14.0 - // tvos 14.0 - // macos 11.0 - // watchos 7.0 - // - // These versions may be newer than the minimum supported versions of OS's we support so we must - // use "weak" linking. - #[cfg(target_vendor = "apple")] - pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - super::weak::weak!(fn preadv(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); - - match preadv.get() { - Some(preadv) => { - let ret = cvt(unsafe { - preadv( - self.as_raw_fd(), - bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - None => io::default_read_vectored(|b| self.read_at(b, offset), bufs), - } - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let ret = cvt(unsafe { - libc::write( - self.as_raw_fd(), - buf.as_ptr() as *const libc::c_void, - cmp::min(buf.len(), READ_LIMIT), - ) - })?; - Ok(ret as usize) - } - - #[cfg(not(any( - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "nuttx" - )))] - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let ret = cvt(unsafe { - libc::writev( - self.as_raw_fd(), - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - ) - })?; - Ok(ret as usize) - } - - #[cfg(any( - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "nuttx" - ))] - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - io::default_write_vectored(|b| self.write(b), bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - cfg!(not(any( - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "nuttx" - ))) - } - - #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] - pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - #[cfg(not(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "hurd" - )))] - use libc::pwrite as pwrite64; - #[cfg(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "hurd" - ))] - use libc::pwrite64; - - unsafe { - cvt(pwrite64( - self.as_raw_fd(), - buf.as_ptr() as *const libc::c_void, - cmp::min(buf.len(), READ_LIMIT), - offset as off64_t, - )) - .map(|n| n as usize) - } - } - - #[cfg(any( - target_os = "aix", - target_os = "dragonfly", // DragonFly 1.5 - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", // OpenBSD 2.7 - ))] - pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - let ret = cvt(unsafe { - libc::pwritev( - self.as_raw_fd(), - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - - #[cfg(not(any( - target_os = "aix", - target_os = "android", - target_os = "dragonfly", - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_vendor = "apple", - )))] - pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - io::default_write_vectored(|b| self.write_at(b, offset), bufs) - } - - // We support some old Android versions that do not have `pwritev` in libc, - // so we use weak linkage and fallback to a direct syscall if not available. - // - // On 32-bit targets, we don't want to deal with weird ABI issues around - // passing 64-bits parameters to syscalls, so we fallback to the default - // implementation if `pwritev` is not available. - #[cfg(all(target_os = "android", target_pointer_width = "64"))] - pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - super::weak::syscall! { - fn pwritev( - fd: libc::c_int, - iovec: *const libc::iovec, - n_iovec: libc::c_int, - offset: off64_t - ) -> isize - } - - let ret = cvt(unsafe { - pwritev( - self.as_raw_fd(), - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - - #[cfg(all(target_os = "android", target_pointer_width = "32"))] - pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); - - match pwritev64.get() { - Some(pwritev) => { - let ret = cvt(unsafe { - pwritev( - self.as_raw_fd(), - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - None => io::default_write_vectored(|b| self.write_at(b, offset), bufs), - } - } - - // We support old MacOS, iOS, watchOS, tvOS and visionOS. `pwritev` was added in the following - // Apple OS versions: - // ios 14.0 - // tvos 14.0 - // macos 11.0 - // watchos 7.0 - // - // These versions may be newer than the minimum supported versions of OS's we support so we must - // use "weak" linking. - #[cfg(target_vendor = "apple")] - pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - super::weak::weak!(fn pwritev(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); - - match pwritev.get() { - Some(pwritev) => { - let ret = cvt(unsafe { - pwritev( - self.as_raw_fd(), - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - None => io::default_write_vectored(|b| self.write_at(b, offset), bufs), - } - } - - #[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", - target_os = "vxworks", - target_os = "nto", - )))] - pub fn set_cloexec(&self) -> io::Result<()> { - unsafe { - cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?; - Ok(()) - } - } - #[cfg(any( - all( - target_env = "newlib", - not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")) - ), - target_os = "solaris", - target_os = "illumos", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "l4re", - target_os = "linux", - target_os = "haiku", - target_os = "redox", - target_os = "vxworks", - target_os = "nto", - ))] - pub fn set_cloexec(&self) -> io::Result<()> { - unsafe { - let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?; - let new = previous | libc::FD_CLOEXEC; - if new != previous { - cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?; - } - Ok(()) - } - } - #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] - pub fn set_cloexec(&self) -> io::Result<()> { - // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS and Vita but there's no need to, - // because none of them supports spawning processes. - Ok(()) - } - - #[cfg(target_os = "linux")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - unsafe { - let v = nonblocking as libc::c_int; - cvt(libc::ioctl(self.as_raw_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.as_raw_fd(), libc::F_GETFL))?; - let new = if nonblocking { - previous | libc::O_NONBLOCK - } else { - previous & !libc::O_NONBLOCK - }; - if new != previous { - cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?; - } - Ok(()) - } - } - - #[inline] - pub fn duplicate(&self) -> io::Result { - Ok(Self(self.0.try_clone()?)) - } -} - -impl<'a> Read for &'a FileDesc { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } - - fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - (**self).read_buf(cursor) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - (**self).read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - (**self).is_read_vectored() - } -} - -impl AsInner for FileDesc { - #[inline] - fn as_inner(&self) -> &OwnedFd { - &self.0 - } -} - -impl IntoInner for FileDesc { - fn into_inner(self) -> OwnedFd { - self.0 - } -} - -impl FromInner for FileDesc { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self(owned_fd) - } -} - -impl AsFd for FileDesc { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for FileDesc { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for FileDesc { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for FileDesc { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } -} diff --git a/library/std/src/sys/pal/unix/fd/tests.rs b/library/std/src/sys/pal/unix/fd/tests.rs deleted file mode 100644 index c5301ce655787..0000000000000 --- a/library/std/src/sys/pal/unix/fd/tests.rs +++ /dev/null @@ -1,11 +0,0 @@ -use core::mem::ManuallyDrop; - -use super::{FileDesc, IoSlice}; -use crate::os::unix::io::FromRawFd; - -#[test] -fn limit_vector_count() { - let stdout = ManuallyDrop::new(unsafe { FileDesc::from_raw_fd(1) }); - let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::>(); - assert!(stdout.write_vectored(&bufs).is_ok()); -} diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs deleted file mode 100644 index 3df460e38b72e..0000000000000 --- a/library/std/src/sys/pal/unix/fs.rs +++ /dev/null @@ -1,2315 +0,0 @@ -// miri has some special hacks here that make things unused. -#![cfg_attr(miri, allow(unused))] - -#[cfg(test)] -mod tests; - -#[cfg(all(target_os = "linux", target_env = "gnu"))] -use libc::c_char; -#[cfg(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "fuchsia", - target_os = "hurd" -))] -use libc::dirfd; -#[cfg(target_os = "fuchsia")] -use libc::fstatat as fstatat64; -#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] -use libc::fstatat64; -#[cfg(any( - target_os = "android", - target_os = "solaris", - target_os = "fuchsia", - target_os = "redox", - target_os = "illumos", - target_os = "aix", - target_os = "nto", - target_os = "vita", - all(target_os = "linux", target_env = "musl"), -))] -use libc::readdir as readdir64; -#[cfg(not(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", - target_os = "l4re", - target_os = "fuchsia", - target_os = "redox", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", -)))] -use libc::readdir_r as readdir64_r; -#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] -use libc::readdir64; -#[cfg(target_os = "l4re")] -use libc::readdir64_r; -use libc::{c_int, mode_t}; -#[cfg(target_os = "android")] -use libc::{ - dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64, - lstat as lstat64, off64_t, open as open64, stat as stat64, -}; -#[cfg(not(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "l4re", - target_os = "android", - target_os = "hurd", -)))] -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( - all(target_os = "linux", not(target_env = "musl")), - target_os = "l4re", - target_os = "hurd" -))] -use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64}; - -use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt::{self, Write as _}; -use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; -use crate::os::unix::prelude::*; -use crate::path::{Path, PathBuf}; -use crate::sync::Arc; -use crate::sys::common::small_c_string::run_path_with_cstr; -use crate::sys::fd::FileDesc; -use crate::sys::time::SystemTime; -#[cfg(all(target_os = "linux", target_env = "gnu"))] -use crate::sys::weak::syscall; -#[cfg(target_os = "android")] -use crate::sys::weak::weak; -use crate::sys::{cvt, cvt_r}; -pub use crate::sys_common::fs::exists; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; -use crate::{mem, ptr}; - -pub struct File(FileDesc); - -// 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, - // With statx, we can overcome 32-bit `time_t` too. - #[cfg(target_pointer_width = "32")] - stx_atime: libc::statx_timestamp, - #[cfg(target_pointer_width = "32")] - stx_ctime: libc::statx_timestamp, - #[cfg(target_pointer_width = "32")] - stx_mtime: libc::statx_timestamp, - - } - - // We prefer `statx` on Linux if available, which contains file creation time, - // as well as 64-bit timestamps of all kinds. - // Default `stat64` contains no creation time and may have 32-bit `time_t`. - unsafe fn try_statx( - fd: c_int, - path: *const 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 check for it on first failure and remember availability to avoid having to - // do it again. - #[repr(u8)] - enum STATX_STATE{ Unknown = 0, Present, Unavailable } - static STATX_SAVED_STATE: AtomicU8 = AtomicU8::new(STATX_STATE::Unknown as u8); - - syscall! { - fn statx( - fd: c_int, - pathname: *const c_char, - flags: c_int, - mask: libc::c_uint, - statxbuf: *mut libc::statx - ) -> c_int - } - - let statx_availability = STATX_SAVED_STATE.load(Ordering::Relaxed); - if statx_availability == STATX_STATE::Unavailable as u8 { - return None; - } - - let mut buf: libc::statx = mem::zeroed(); - if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) { - if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 { - return Some(Err(err)); - } - - // We're not yet entirely sure whether `statx` is usable on this kernel - // or not. Syscalls can return errors from things other than the kernel - // per se, e.g. `EPERM` can be returned if seccomp is used to block the - // syscall, or `ENOSYS` might be returned from a faulty FUSE driver. - // - // Availability is checked by performing a call which expects `EFAULT` - // if the syscall is usable. - // - // See: https://github.com/rust-lang/rust/issues/65662 - // - // FIXME what about transient conditions like `ENOMEM`? - let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_BASIC_STATS | libc::STATX_BTIME, ptr::null_mut())) - .err() - .and_then(|e| e.raw_os_error()); - if err2 == Some(libc::EFAULT) { - STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed); - return Some(Err(err)); - } else { - STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed); - return None; - } - } - if statx_availability == STATX_STATE::Unknown as u8 { - STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed); - } - - // 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, - // Store full times to avoid 32-bit `time_t` truncation. - #[cfg(target_pointer_width = "32")] - stx_atime: buf.stx_atime, - #[cfg(target_pointer_width = "32")] - stx_ctime: buf.stx_ctime, - #[cfg(target_pointer_width = "32")] - stx_mtime: buf.stx_mtime, - }; - - 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, -} - -pub struct ReadDir { - inner: Arc, - end_of_stream: bool, -} - -impl ReadDir { - fn new(inner: InnerReadDir) -> Self { - Self { inner: Arc::new(inner), end_of_stream: false } - } -} - -struct Dir(*mut libc::DIR); - -unsafe impl Send for Dir {} -unsafe impl Sync for Dir {} - -#[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", -))] -pub struct DirEntry { - dir: Arc, - entry: dirent64_min, - // We need to store an owned copy of the entry name on platforms that use - // readdir() (not readdir_r()), because a) struct dirent may use a flexible - // array to store the name, b) it lives only until the next readdir() call. - name: crate::ffi::CString, -} - -// Define a minimal subset of fields we need from `dirent64`, especially since -// we're not using the immediate `d_name` on these targets. Keeping this as an -// `entry` field in `DirEntry` helps reduce the `cfg` boilerplate elsewhere. -#[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", -))] -struct dirent64_min { - d_ino: u64, - #[cfg(not(any( - target_os = "solaris", - target_os = "illumos", - target_os = "aix", - target_os = "nto", - target_os = "vita", - )))] - d_type: u8, -} - -#[cfg(not(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", -)))] -pub struct DirEntry { - dir: Arc, - // The full entry includes a fixed-length `d_name`. - entry: dirent64, -} - -#[derive(Clone)] -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)] -pub struct FilePermissions { - mode: mode_t, -} - -#[derive(Copy, Clone, Debug, Default)] -pub struct FileTimes { - accessed: Option, - modified: Option, - #[cfg(target_vendor = "apple")] - created: Option, -} - -#[derive(Copy, Clone, Eq)] -pub struct FileType { - mode: mode_t, -} - -impl PartialEq for FileType { - fn eq(&self, other: &Self) -> bool { - self.masked() == other.masked() - } -} - -impl core::hash::Hash for FileType { - fn hash(&self, state: &mut H) { - self.masked().hash(state); - } -} - -pub struct DirBuilder { - mode: mode_t, -} - -#[derive(Copy, Clone)] -struct Mode(mode_t); - -cfg_has_statx! {{ - impl FileAttr { - fn from_stat64(stat: stat64) -> Self { - Self { stat, statx_extra_fields: None } - } - - #[cfg(target_pointer_width = "32")] - pub fn stx_mtime(&self) -> Option<&libc::statx_timestamp> { - if let Some(ext) = &self.statx_extra_fields { - if (ext.stx_mask & libc::STATX_MTIME) != 0 { - return Some(&ext.stx_mtime); - } - } - None - } - - #[cfg(target_pointer_width = "32")] - pub fn stx_atime(&self) -> Option<&libc::statx_timestamp> { - if let Some(ext) = &self.statx_extra_fields { - if (ext.stx_mask & libc::STATX_ATIME) != 0 { - return Some(&ext.stx_atime); - } - } - None - } - - #[cfg(target_pointer_width = "32")] - pub fn stx_ctime(&self) -> Option<&libc::statx_timestamp> { - if let Some(ext) = &self.statx_extra_fields { - if (ext.stx_mask & libc::STATX_CTIME) != 0 { - return Some(&ext.stx_ctime); - } - } - 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 { - SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64) - } - - pub fn accessed(&self) -> io::Result { - SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64) - } - - pub fn created(&self) -> io::Result { - SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64) - } -} - -#[cfg(target_os = "aix")] -impl FileAttr { - pub fn modified(&self) -> io::Result { - SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64) - } - - pub fn accessed(&self) -> io::Result { - SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64) - } - - pub fn created(&self) -> io::Result { - SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64) - } -} - -#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix")))] -impl FileAttr { - #[cfg(not(any( - target_os = "vxworks", - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "hurd", - target_os = "rtems", - target_os = "nuttx", - )))] - pub fn modified(&self) -> io::Result { - #[cfg(target_pointer_width = "32")] - cfg_has_statx! { - if let Some(mtime) = self.stx_mtime() { - return SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64); - } - } - - SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64) - } - - #[cfg(any( - target_os = "vxworks", - target_os = "espidf", - target_os = "vita", - target_os = "rtems", - ))] - pub fn modified(&self) -> io::Result { - SystemTime::new(self.stat.st_mtime as i64, 0) - } - - #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))] - pub fn modified(&self) -> io::Result { - SystemTime::new(self.stat.st_mtim.tv_sec as i64, self.stat.st_mtim.tv_nsec as i64) - } - - #[cfg(not(any( - target_os = "vxworks", - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "hurd", - target_os = "rtems", - target_os = "nuttx", - )))] - pub fn accessed(&self) -> io::Result { - #[cfg(target_pointer_width = "32")] - cfg_has_statx! { - if let Some(atime) = self.stx_atime() { - return SystemTime::new(atime.tv_sec, atime.tv_nsec as i64); - } - } - - SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64) - } - - #[cfg(any( - target_os = "vxworks", - target_os = "espidf", - target_os = "vita", - target_os = "rtems" - ))] - pub fn accessed(&self) -> io::Result { - SystemTime::new(self.stat.st_atime as i64, 0) - } - - #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))] - pub fn accessed(&self) -> io::Result { - SystemTime::new(self.stat.st_atim.tv_sec as i64, self.stat.st_atim.tv_nsec as i64) - } - - #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_vendor = "apple"))] - pub fn created(&self) -> io::Result { - SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64) - } - - #[cfg(not(any( - target_os = "freebsd", - target_os = "openbsd", - target_os = "vita", - target_vendor = "apple", - )))] - 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 { - SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64) - } else { - Err(io::const_error!( - io::ErrorKind::Unsupported, - "creation time is not available for the filesystem", - )) - }; - } - } - - Err(io::const_error!( - io::ErrorKind::Unsupported, - "creation time is not available on this platform currently", - )) - } - - #[cfg(target_os = "vita")] - pub fn created(&self) -> io::Result { - SystemTime::new(self.stat.st_ctime as i64, 0) - } -} - -#[cfg(target_os = "nto")] -impl FileAttr { - pub fn modified(&self) -> io::Result { - SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec) - } - - pub fn accessed(&self) -> io::Result { - SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec) - } - - pub fn created(&self) -> io::Result { - SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec) - } -} - -impl AsInner for FileAttr { - #[inline] - 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 FileTimes { - pub fn set_accessed(&mut self, t: SystemTime) { - self.accessed = Some(t); - } - - pub fn set_modified(&mut self, t: SystemTime) { - self.modified = Some(t); - } - - #[cfg(target_vendor = "apple")] - pub fn set_created(&mut self, t: SystemTime) { - self.created = Some(t); - } -} - -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.masked() == mode - } - - fn masked(&self) -> mode_t { - self.mode & libc::S_IFMT - } -} - -impl fmt::Debug for FileType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let FileType { mode } = self; - f.debug_struct("FileType").field("mode", &Mode(*mode)).finish() - } -} - -impl FromInner for FilePermissions { - fn from_inner(mode: u32) -> FilePermissions { - FilePermissions { mode: mode as mode_t } - } -} - -impl fmt::Debug for FilePermissions { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let FilePermissions { mode } = self; - f.debug_struct("FilePermissions").field("mode", &Mode(*mode)).finish() - } -} - -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 = "android", - target_os = "linux", - target_os = "solaris", - target_os = "fuchsia", - target_os = "redox", - target_os = "illumos", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", - ))] - fn next(&mut self) -> Option> { - if self.end_of_stream { - return None; - } - - unsafe { - loop { - // As of POSIX.1-2017, readdir() is not required to be thread safe; only - // readdir_r() is. However, readdir_r() cannot correctly handle platforms - // with unlimited or variable NAME_MAX. Many modern platforms guarantee - // thread safety for readdir() as long an individual DIR* is not accessed - // concurrently, which is sufficient for Rust. - super::os::set_errno(0); - let entry_ptr: *const dirent64 = readdir64(self.inner.dirp.0); - if entry_ptr.is_null() { - // We either encountered an error, or reached the end. Either way, - // the next call to next() should return None. - self.end_of_stream = true; - - // To distinguish between errors and end-of-directory, 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))), - }; - } - - // The dirent64 struct is a weird imaginary thing that isn't ever supposed - // to be worked with by value. Its trailing d_name field is declared - // variously as [c_char; 256] or [c_char; 1] on different systems but - // either way that size is meaningless; only the offset of d_name is - // meaningful. The dirent64 pointers that libc returns from readdir64 are - // allowed to point to allocations smaller _or_ LARGER than implied by the - // definition of the struct. - // - // As such, we need to be even more careful with dirent64 than if its - // contents were "simply" partially initialized data. - // - // Like for uninitialized contents, converting entry_ptr to `&dirent64` - // would not be legal. However, we can use `&raw const (*entry_ptr).d_name` - // to refer the fields individually, because that operation is equivalent - // to `byte_offset` and thus does not require the full extent of `*entry_ptr` - // to be in bounds of the same allocation, only the offset of the field - // being referenced. - - // d_name is guaranteed to be null-terminated. - let name = CStr::from_ptr((&raw const (*entry_ptr).d_name).cast()); - let name_bytes = name.to_bytes(); - if name_bytes == b"." || name_bytes == b".." { - continue; - } - - // When loading from a field, we can skip the `&raw const`; `(*entry_ptr).d_ino` as - // a value expression will do the right thing: `byte_offset` to the field and then - // only access those bytes. - #[cfg(not(target_os = "vita"))] - let entry = dirent64_min { - d_ino: (*entry_ptr).d_ino as u64, - #[cfg(not(any( - target_os = "solaris", - target_os = "illumos", - target_os = "aix", - target_os = "nto", - )))] - d_type: (*entry_ptr).d_type as u8, - }; - - #[cfg(target_os = "vita")] - let entry = dirent64_min { d_ino: 0u64 }; - - return Some(Ok(DirEntry { - entry, - name: name.to_owned(), - dir: Arc::clone(&self.inner), - })); - } - } - } - - #[cfg(not(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "fuchsia", - target_os = "redox", - target_os = "illumos", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", - )))] - fn next(&mut self) -> Option> { - if self.end_of_stream { - return None; - } - - unsafe { - let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) }; - let mut entry_ptr = ptr::null_mut(); - loop { - let err = readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr); - if err != 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::from_raw_os_error(err))); - } - if entry_ptr.is_null() { - return None; - } - if ret.name_bytes() != b"." && ret.name_bytes() != b".." { - return Some(Ok(ret)); - } - } - } - } -} - -/// Aborts the process if a file desceriptor is not open, if debug asserts are enabled -/// -/// Many IO syscalls can't be fully trusted about EBADF error codes because those -/// might get bubbled up from a remote FUSE server rather than the file descriptor -/// in the current process being invalid. -/// -/// So we check file flags instead which live on the file descriptor and not the underlying file. -/// The downside is that it costs an extra syscall, so we only do it for debug. -#[inline] -pub(crate) fn debug_assert_fd_is_open(fd: RawFd) { - use crate::sys::os::errno; - - // this is similar to assert_unsafe_precondition!() but it doesn't require const - if core::ub_checks::check_library_ub() { - if unsafe { libc::fcntl(fd, libc::F_GETFD) } == -1 && errno() == libc::EBADF { - rtabort!("IO Safety violation: owned file descriptor already closed"); - } - } -} - -impl Drop for Dir { - fn drop(&mut self) { - // dirfd isn't supported everywhere - #[cfg(not(any( - miri, - target_os = "redox", - target_os = "nto", - target_os = "vita", - target_os = "hurd", - target_os = "espidf", - target_os = "horizon", - target_os = "vxworks", - target_os = "rtems", - target_os = "nuttx", - )))] - { - let fd = unsafe { libc::dirfd(self.0) }; - debug_assert_fd_is_open(fd); - } - let r = unsafe { libc::closedir(self.0) }; - assert!( - r == 0 || crate::io::Error::last_os_error().is_interrupted(), - "unexpected error during closedir: {:?}", - crate::io::Error::last_os_error() - ); - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - self.dir.root.join(self.file_name_os_str()) - } - - pub fn file_name(&self) -> OsString { - self.file_name_os_str().to_os_string() - } - - #[cfg(all( - any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "fuchsia", - target_os = "hurd" - ), - not(miri) // no dirfd on Miri - ))] - pub fn metadata(&self) -> io::Result { - let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?; - let name = self.name_cstr().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_BASIC_STATS | libc::STATX_BTIME, - ) } { - 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(any( - not(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "fuchsia", - target_os = "hurd", - )), - miri - ))] - pub fn metadata(&self) -> io::Result { - lstat(&self.path()) - } - - #[cfg(any( - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "vxworks", - target_os = "aix", - target_os = "nto", - target_os = "vita", - ))] - pub fn file_type(&self) -> io::Result { - self.metadata().map(|m| m.file_type()) - } - - #[cfg(not(any( - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "vxworks", - target_os = "aix", - target_os = "nto", - target_os = "vita", - )))] - 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 }), - _ => self.metadata().map(|m| m.file_type()), - } - } - - #[cfg(any( - 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", - target_os = "vxworks", - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "aix", - target_os = "nto", - target_os = "hurd", - target_os = "rtems", - target_vendor = "apple", - ))] - 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(target_os = "nuttx")] - pub fn ino(&self) -> u64 { - // Leave this 0 for now, as NuttX does not provide an inode number - // in its directory entries. - 0 - } - - #[cfg(any( - target_os = "netbsd", - target_os = "openbsd", - target_os = "freebsd", - target_os = "dragonfly", - target_vendor = "apple", - ))] - 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(not(any( - target_os = "netbsd", - target_os = "openbsd", - target_os = "freebsd", - target_os = "dragonfly", - target_vendor = "apple", - )))] - fn name_bytes(&self) -> &[u8] { - self.name_cstr().to_bytes() - } - - #[cfg(not(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", - )))] - fn name_cstr(&self) -> &CStr { - unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) } - } - #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", - ))] - fn name_cstr(&self) -> &CStr { - &self.name - } - - pub fn file_name_os_str(&self) -> &OsStr { - OsStr::from_bytes(self.name_bytes()) - } -} - -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 fmt::Debug for OpenOptions { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let OpenOptions { read, write, append, truncate, create, create_new, custom_flags, mode } = - self; - f.debug_struct("OpenOptions") - .field("read", read) - .field("write", write) - .field("append", append) - .field("truncate", truncate) - .field("create", create) - .field("create_new", create_new) - .field("custom_flags", custom_flags) - .field("mode", &Mode(*mode)) - .finish() - } -} - -impl File { - pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - run_path_with_cstr(path, &|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(unsafe { FileDesc::from_raw_fd(fd) })) - } - - pub fn file_attr(&self) -> io::Result { - let fd = self.as_raw_fd(); - - cfg_has_statx! { - if let Some(ret) = unsafe { try_statx( - fd, - c"".as_ptr() as *const c_char, - libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_BASIC_STATS | libc::STATX_BTIME, - ) } { - 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.as_raw_fd()) })?; - return Ok(()); - - #[cfg(target_vendor = "apple")] - unsafe fn os_fsync(fd: c_int) -> c_int { - libc::fcntl(fd, libc::F_FULLFSYNC) - } - #[cfg(not(target_vendor = "apple"))] - unsafe fn os_fsync(fd: c_int) -> c_int { - libc::fsync(fd) - } - } - - pub fn datasync(&self) -> io::Result<()> { - cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?; - return Ok(()); - - #[cfg(target_vendor = "apple")] - unsafe fn os_datasync(fd: c_int) -> c_int { - libc::fcntl(fd, libc::F_FULLFSYNC) - } - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "android", - target_os = "netbsd", - target_os = "openbsd", - target_os = "nto", - target_os = "hurd", - ))] - unsafe fn os_datasync(fd: c_int) -> c_int { - libc::fdatasync(fd) - } - #[cfg(not(any( - target_os = "android", - target_os = "fuchsia", - target_os = "freebsd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "nto", - target_os = "hurd", - target_vendor = "apple", - )))] - unsafe fn os_datasync(fd: c_int) -> c_int { - libc::fsync(fd) - } - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "netbsd", - target_vendor = "apple", - ))] - pub fn lock(&self) -> io::Result<()> { - cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?; - return Ok(()); - } - - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "netbsd", - target_vendor = "apple", - )))] - pub fn lock(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported")) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "netbsd", - target_vendor = "apple", - ))] - pub fn lock_shared(&self) -> io::Result<()> { - cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?; - return Ok(()); - } - - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "netbsd", - target_vendor = "apple", - )))] - pub fn lock_shared(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "netbsd", - target_vendor = "apple", - ))] - pub fn try_lock(&self) -> io::Result { - let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }); - if let Err(ref err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - return Ok(false); - } - } - result?; - return Ok(true); - } - - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "netbsd", - target_vendor = "apple", - )))] - pub fn try_lock(&self) -> io::Result { - Err(io::const_error!(io::ErrorKind::Unsupported, "try_lock() not supported")) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "netbsd", - target_vendor = "apple", - ))] - pub fn try_lock_shared(&self) -> io::Result { - let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) }); - if let Err(ref err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - return Ok(false); - } - } - result?; - return Ok(true); - } - - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "netbsd", - target_vendor = "apple", - )))] - pub fn try_lock_shared(&self) -> io::Result { - Err(io::const_error!(io::ErrorKind::Unsupported, "try_lock_shared() not supported")) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "netbsd", - target_vendor = "apple", - ))] - pub fn unlock(&self) -> io::Result<()> { - cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?; - return Ok(()); - } - - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "netbsd", - target_vendor = "apple", - )))] - pub fn unlock(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported")) - } - - pub fn truncate(&self, size: u64) -> io::Result<()> { - let size: off64_t = - size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; - cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), 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 read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(cursor) - } - - pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - self.0.read_vectored_at(bufs, 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 write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - self.0.write_vectored_at(bufs, offset) - } - - #[inline] - 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.as_raw_fd(), pos as off64_t, whence) })?; - Ok(n as u64) - } - - pub fn tell(&self) -> io::Result { - self.seek(SeekFrom::Current(0)) - } - - pub fn duplicate(&self) -> io::Result { - self.0.duplicate().map(File) - } - - pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { - cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?; - Ok(()) - } - - pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - #[cfg(not(any( - target_os = "redox", - target_os = "espidf", - target_os = "horizon", - target_os = "vxworks", - target_os = "nuttx", - )))] - let to_timespec = |time: Option| match time { - Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), - Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time", - )), - Some(_) => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too small to set as a file time", - )), - None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), - }; - cfg_if::cfg_if! { - if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks", target_os = "nuttx"))] { - // Redox doesn't appear to support `UTIME_OMIT`. - // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore - // the same as for Redox. - // `futimens` and `UTIME_OMIT` are a work in progress for vxworks. - let _ = times; - Err(io::const_error!( - io::ErrorKind::Unsupported, - "setting file times not supported", - )) - } else if #[cfg(target_vendor = "apple")] { - let mut buf = [mem::MaybeUninit::::uninit(); 3]; - let mut num_times = 0; - let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; - attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; - if times.created.is_some() { - buf[num_times].write(to_timespec(times.created)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_CRTIME; - } - if times.modified.is_some() { - buf[num_times].write(to_timespec(times.modified)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_MODTIME; - } - if times.accessed.is_some() { - buf[num_times].write(to_timespec(times.accessed)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; - } - cvt(unsafe { libc::fsetattrlist( - self.as_raw_fd(), - (&raw const attrlist).cast::().cast_mut(), - buf.as_ptr().cast::().cast_mut(), - num_times * mem::size_of::(), - 0 - ) })?; - Ok(()) - } else if #[cfg(target_os = "android")] { - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; - // futimens requires Android API level 19 - cvt(unsafe { - weak!(fn futimens(c_int, *const libc::timespec) -> c_int); - match futimens.get() { - Some(futimens) => futimens(self.as_raw_fd(), times.as_ptr()), - None => return Err(io::const_error!( - io::ErrorKind::Unsupported, - "setting file times requires Android API level >= 19", - )), - } - })?; - Ok(()) - } else { - #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))] - { - use crate::sys::{time::__timespec64, weak::weak}; - - // Added in glibc 2.34 - weak!(fn __futimens64(libc::c_int, *const __timespec64) -> libc::c_int); - - if let Some(futimens64) = __futimens64.get() { - let to_timespec = |time: Option| time.map(|time| time.t.to_timespec64()) - .unwrap_or(__timespec64::new(0, libc::UTIME_OMIT as _)); - let times = [to_timespec(times.accessed), to_timespec(times.modified)]; - cvt(unsafe { futimens64(self.as_raw_fd(), times.as_ptr()) })?; - return Ok(()); - } - } - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; - cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?; - Ok(()) - } - } - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder { mode: 0o777 } - } - - pub fn mkdir(&self, p: &Path) -> io::Result<()> { - run_path_with_cstr(p, &|p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ())) - } - - pub fn set_mode(&mut self, mode: u32) { - self.mode = mode as mode_t; - } -} - -impl fmt::Debug for DirBuilder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let DirBuilder { mode } = self; - f.debug_struct("DirBuilder").field("mode", &Mode(*mode)).finish() - } -} - -impl AsInner for File { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } -} - -impl AsInnerMut for File { - #[inline] - fn as_inner_mut(&mut self) -> &mut FileDesc { - &mut self.0 - } -} - -impl IntoInner for File { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner for File { - fn from_inner(file_desc: FileDesc) -> Self { - Self(file_desc) - } -} - -impl AsFd for File { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for File { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for File { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for File { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } -} - -impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))] - fn get_path(fd: c_int) -> Option { - let mut p = PathBuf::from("/proc/self/fd"); - p.push(&fd.to_string()); - readlink(&p).ok() - } - - #[cfg(any(target_vendor = "apple", target_os = "netbsd"))] - fn get_path(fd: c_int) -> Option { - // FIXME: The use of PATH_MAX is generally not encouraged, but it - // is inevitable in this case because Apple targets and NetBSD define `fcntl` - // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no - // alternatives. If a better method is invented, it should be used - // instead. - let mut buf = vec![0; libc::PATH_MAX as usize]; - let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; - if n == -1 { - cfg_if::cfg_if! { - if #[cfg(target_os = "netbsd")] { - // fallback to procfs as last resort - let mut p = PathBuf::from("/proc/self/fd"); - p.push(&fd.to_string()); - return readlink(&p).ok(); - } else { - return None; - } - } - } - let l = buf.iter().position(|&c| c == 0).unwrap(); - buf.truncate(l as usize); - buf.shrink_to_fit(); - Some(PathBuf::from(OsString::from_vec(buf))) - } - - #[cfg(target_os = "freebsd")] - fn get_path(fd: c_int) -> Option { - let info = Box::::new_zeroed(); - let mut info = unsafe { info.assume_init() }; - info.kf_structsize = mem::size_of::() as libc::c_int; - let n = unsafe { libc::fcntl(fd, libc::F_KINFO, &mut *info) }; - if n == -1 { - return None; - } - let buf = unsafe { CStr::from_ptr(info.kf_path.as_mut_ptr()).to_bytes().to_vec() }; - Some(PathBuf::from(OsString::from_vec(buf))) - } - - #[cfg(target_os = "vxworks")] - fn get_path(fd: c_int) -> Option { - let mut buf = vec![0; libc::PATH_MAX as usize]; - let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) }; - if n == -1 { - return None; - } - let l = buf.iter().position(|&c| c == 0).unwrap(); - buf.truncate(l as usize); - Some(PathBuf::from(OsString::from_vec(buf))) - } - - #[cfg(not(any( - target_os = "linux", - target_os = "vxworks", - target_os = "freebsd", - target_os = "netbsd", - target_os = "illumos", - target_os = "solaris", - target_vendor = "apple", - )))] - fn get_path(_fd: c_int) -> Option { - // FIXME(#24570): implement this for other Unix platforms - None - } - - 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, - } - } - - let fd = self.as_raw_fd(); - 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() - } -} - -// Format in octal, followed by the mode format used in `ls -l`. -// -// References: -// https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html -// https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html -// https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html -// -// Example: -// 0o100664 (-rw-rw-r--) -impl fmt::Debug for Mode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(mode) = *self; - write!(f, "0o{mode:06o}")?; - - let entry_type = match mode & libc::S_IFMT { - libc::S_IFDIR => 'd', - libc::S_IFBLK => 'b', - libc::S_IFCHR => 'c', - libc::S_IFLNK => 'l', - libc::S_IFIFO => 'p', - libc::S_IFREG => '-', - _ => return Ok(()), - }; - - f.write_str(" (")?; - f.write_char(entry_type)?; - - // Owner permissions - f.write_char(if mode & libc::S_IRUSR != 0 { 'r' } else { '-' })?; - f.write_char(if mode & libc::S_IWUSR != 0 { 'w' } else { '-' })?; - let owner_executable = mode & libc::S_IXUSR != 0; - let setuid = mode as c_int & libc::S_ISUID as c_int != 0; - f.write_char(match (owner_executable, setuid) { - (true, true) => 's', // executable and setuid - (false, true) => 'S', // setuid - (true, false) => 'x', // executable - (false, false) => '-', - })?; - - // Group permissions - f.write_char(if mode & libc::S_IRGRP != 0 { 'r' } else { '-' })?; - f.write_char(if mode & libc::S_IWGRP != 0 { 'w' } else { '-' })?; - let group_executable = mode & libc::S_IXGRP != 0; - let setgid = mode as c_int & libc::S_ISGID as c_int != 0; - f.write_char(match (group_executable, setgid) { - (true, true) => 's', // executable and setgid - (false, true) => 'S', // setgid - (true, false) => 'x', // executable - (false, false) => '-', - })?; - - // Other permissions - f.write_char(if mode & libc::S_IROTH != 0 { 'r' } else { '-' })?; - f.write_char(if mode & libc::S_IWOTH != 0 { 'w' } else { '-' })?; - let other_executable = mode & libc::S_IXOTH != 0; - let sticky = mode as c_int & libc::S_ISVTX as c_int != 0; - f.write_char(match (entry_type, other_executable, sticky) { - ('d', true, true) => 't', // searchable and restricted deletion - ('d', false, true) => 'T', // restricted deletion - (_, true, _) => 'x', // executable - (_, false, _) => '-', - })?; - - f.write_char(')') - } -} - -pub fn readdir(path: &Path) -> io::Result { - let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?; - if ptr.is_null() { - Err(Error::last_os_error()) - } else { - let root = path.to_path_buf(); - let inner = InnerReadDir { dirp: Dir(ptr), root }; - Ok(ReadDir::new(inner)) - } -} - -pub fn unlink(p: &Path) -> io::Result<()> { - run_path_with_cstr(p, &|p| cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ())) -} - -pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - run_path_with_cstr(old, &|old| { - run_path_with_cstr(new, &|new| { - cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ()) - }) - }) -} - -pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - run_path_with_cstr(p, &|p| cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ())) -} - -pub fn rmdir(p: &Path) -> io::Result<()> { - run_path_with_cstr(p, &|p| cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ())) -} - -pub fn readlink(p: &Path) -> io::Result { - run_path_with_cstr(p, &|c_path| { - 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(original: &Path, link: &Path) -> io::Result<()> { - run_path_with_cstr(original, &|original| { - run_path_with_cstr(link, &|link| { - cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ()) - }) - }) -} - -pub fn link(original: &Path, link: &Path) -> io::Result<()> { - run_path_with_cstr(original, &|original| { - run_path_with_cstr(link, &|link| { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_env = "nto70"))] { - // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves - // it implementation-defined whether `link` follows symlinks, so rely on the - // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. - // Android has `linkat` on newer versions, but we happen to know `link` - // always has the correct behavior, so it's here as well. - cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; - } else { - // Where we can, use `linkat` instead of `link`; see the comment above - // this one for details on why. - cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; - } - } - Ok(()) - }) - }) -} - -pub fn stat(p: &Path) -> io::Result { - run_path_with_cstr(p, &|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_BASIC_STATS | libc::STATX_BTIME, - ) } { - 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 { - run_path_with_cstr(p, &|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_BASIC_STATS | libc::STATX_BTIME, - ) } { - 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 r = run_path_with_cstr(p, &|path| unsafe { - Ok(libc::realpath(path.as_ptr(), ptr::null_mut())) - })?; - if r.is_null() { - return Err(io::Error::last_os_error()); - } - Ok(PathBuf::from(OsString::from_vec(unsafe { - let buf = CStr::from_ptr(r).to_bytes().to_vec(); - libc::free(r as *mut _); - buf - }))) -} - -fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { - use crate::fs::File; - use crate::sys_common::fs::NOT_FILE_ERROR; - - let reader = File::open(from)?; - let metadata = reader.metadata()?; - if !metadata.is_file() { - return Err(NOT_FILE_ERROR); - } - Ok((reader, metadata)) -} - -#[cfg(target_os = "espidf")] -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; - let writer = OpenOptions::new().open(to)?; - let writer_metadata = writer.metadata()?; - Ok((writer, writer_metadata)) -} - -#[cfg(not(target_os = "espidf"))] -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()?; - // fchmod is broken on vita - #[cfg(not(target_os = "vita"))] - 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)) -} - -mod cfm { - use crate::fs::{File, Metadata}; - use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, Read, Result, Write}; - - #[allow(dead_code)] - pub struct CachedFileMetadata(pub File, pub Metadata); - - impl Read for CachedFileMetadata { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { - self.0.read_vectored(bufs) - } - fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { - self.0.read_buf(cursor) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec) -> Result { - self.0.read_to_end(buf) - } - fn read_to_string(&mut self, buf: &mut String) -> Result { - self.0.read_to_string(buf) - } - } - impl Write for CachedFileMetadata { - fn write(&mut self, buf: &[u8]) -> Result { - self.0.write(buf) - } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { - self.0.write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - #[inline] - fn flush(&mut self) -> Result<()> { - self.0.flush() - } - } -} -#[cfg(any(target_os = "linux", target_os = "android"))] -pub(crate) use cfm::CachedFileMetadata; - -#[cfg(not(target_vendor = "apple"))] -pub fn copy(from: &Path, to: &Path) -> io::Result { - let (reader, reader_metadata) = open_from(from)?; - let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?; - - io::copy( - &mut cfm::CachedFileMetadata(reader, reader_metadata), - &mut cfm::CachedFileMetadata(writer, writer_metadata), - ) -} - -#[cfg(target_vendor = "apple")] -pub fn copy(from: &Path, to: &Path) -> io::Result { - const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA; - - struct FreeOnDrop(libc::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 an error. - libc::copyfile_state_free(self.0); - } - } - } - - let (reader, reader_metadata) = open_from(from)?; - - let clonefile_result = run_path_with_cstr(to, &|to| { - cvt(unsafe { libc::fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) }) - }); - match clonefile_result { - Ok(_) => return Ok(reader_metadata.len()), - Err(e) => match e.raw_os_error() { - // `fclonefileat` will fail on non-APFS volumes, if the - // destination already exists, or if the source and destination - // are on different devices. In all these cases `fcopyfile` - // should succeed. - Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (), - _ => return Err(e), - }, - } - - // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. - 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 = libc::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 { libc::COPYFILE_DATA }; - - cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; - - let mut bytes_copied: libc::off_t = 0; - cvt(unsafe { - libc::copyfile_state_get( - state.0, - libc::COPYFILE_STATE_COPIED as u32, - (&raw mut bytes_copied) as *mut libc::c_void, - ) - })?; - Ok(bytes_copied as u64) -} - -pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { - run_path_with_cstr(path, &|path| { - cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) - .map(|_| ()) - }) -} - -pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> { - cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?; - Ok(()) -} - -#[cfg(not(target_os = "vxworks"))] -pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { - run_path_with_cstr(path, &|path| { - cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) - .map(|_| ()) - }) -} - -#[cfg(target_os = "vxworks")] -pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { - let (_, _, _) = (path, uid, gid); - Err(io::const_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks")) -} - -#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] -pub fn chroot(dir: &Path) -> io::Result<()> { - run_path_with_cstr(dir, &|dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ())) -} - -#[cfg(target_os = "vxworks")] -pub fn chroot(dir: &Path) -> io::Result<()> { - let _ = dir; - Err(io::const_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks")) -} - -pub use remove_dir_impl::remove_dir_all; - -// Fallback for REDOX, ESP-ID, Horizon, Vita, Vxworks and Miri -#[cfg(any( - target_os = "redox", - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "nto", - target_os = "vxworks", - miri -))] -mod remove_dir_impl { - pub use crate::sys_common::fs::remove_dir_all; -} - -// Modern implementation using openat(), unlinkat() and fdopendir() -#[cfg(not(any( - target_os = "redox", - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "nto", - target_os = "vxworks", - miri -)))] -mod remove_dir_impl { - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{fdopendir, openat, unlinkat}; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{fdopendir, openat64 as openat, unlinkat}; - - use super::{Dir, DirEntry, InnerReadDir, ReadDir, lstat}; - use crate::ffi::CStr; - use crate::io; - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; - use crate::os::unix::prelude::{OwnedFd, RawFd}; - use crate::path::{Path, PathBuf}; - use crate::sys::common::small_c_string::run_path_with_cstr; - use crate::sys::{cvt, cvt_r}; - use crate::sys_common::ignore_notfound; - - pub fn openat_nofollow_dironly(parent_fd: Option, p: &CStr) -> io::Result { - let fd = cvt_r(|| unsafe { - openat( - parent_fd.unwrap_or(libc::AT_FDCWD), - p.as_ptr(), - libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY, - ) - })?; - Ok(unsafe { OwnedFd::from_raw_fd(fd) }) - } - - fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> { - let ptr = unsafe { fdopendir(dir_fd.as_raw_fd()) }; - if ptr.is_null() { - return Err(io::Error::last_os_error()); - } - let dirp = Dir(ptr); - // file descriptor is automatically closed by libc::closedir() now, so give up ownership - let new_parent_fd = dir_fd.into_raw_fd(); - // a valid root is not needed because we do not call any functions involving the full path - // of the `DirEntry`s. - let dummy_root = PathBuf::new(); - let inner = InnerReadDir { dirp, root: dummy_root }; - Ok((ReadDir::new(inner), new_parent_fd)) - } - - #[cfg(any( - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "vxworks", - target_os = "aix", - ))] - fn is_dir(_ent: &DirEntry) -> Option { - None - } - - #[cfg(not(any( - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "vxworks", - target_os = "aix", - )))] - fn is_dir(ent: &DirEntry) -> Option { - match ent.entry.d_type { - libc::DT_UNKNOWN => None, - libc::DT_DIR => Some(true), - _ => Some(false), - } - } - - fn is_enoent(result: &io::Result<()>) -> bool { - if let Err(err) = result - && matches!(err.raw_os_error(), Some(libc::ENOENT)) - { - true - } else { - false - } - } - - fn remove_dir_all_recursive(parent_fd: Option, path: &CStr) -> io::Result<()> { - // try opening as directory - let fd = match openat_nofollow_dironly(parent_fd, &path) { - Err(err) if matches!(err.raw_os_error(), Some(libc::ENOTDIR | libc::ELOOP)) => { - // not a directory - don't traverse further - // (for symlinks, older Linux kernels may return ELOOP instead of ENOTDIR) - return match parent_fd { - // unlink... - Some(parent_fd) => { - cvt(unsafe { unlinkat(parent_fd, path.as_ptr(), 0) }).map(drop) - } - // ...unless this was supposed to be the deletion root directory - None => Err(err), - }; - } - result => result?, - }; - - // open the directory passing ownership of the fd - let (dir, fd) = fdreaddir(fd)?; - for child in dir { - let child = child?; - let child_name = child.name_cstr(); - // we need an inner try block, because if one of these - // directories has already been deleted, then we need to - // continue the loop, not return ok. - let result: io::Result<()> = try { - match is_dir(&child) { - Some(true) => { - remove_dir_all_recursive(Some(fd), child_name)?; - } - Some(false) => { - cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?; - } - None => { - // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed - // if the process has the appropriate privileges. This however can causing orphaned - // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing - // into it first instead of trying to unlink() it. - remove_dir_all_recursive(Some(fd), child_name)?; - } - } - }; - if result.is_err() && !is_enoent(&result) { - return result; - } - } - - // unlink the directory after removing its contents - ignore_notfound(cvt(unsafe { - unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), path.as_ptr(), libc::AT_REMOVEDIR) - }))?; - Ok(()) - } - - fn remove_dir_all_modern(p: &Path) -> io::Result<()> { - // We cannot just call remove_dir_all_recursive() here because that would not delete a passed - // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse - // into symlinks. - let attr = lstat(p)?; - if attr.file_type().is_symlink() { - crate::fs::remove_file(p) - } else { - run_path_with_cstr(p, &|p| remove_dir_all_recursive(None, &p)) - } - } - - pub fn remove_dir_all(p: &Path) -> io::Result<()> { - remove_dir_all_modern(p) - } -} diff --git a/library/std/src/sys/pal/unix/fs/tests.rs b/library/std/src/sys/pal/unix/fs/tests.rs deleted file mode 100644 index 71be3472148b0..0000000000000 --- a/library/std/src/sys/pal/unix/fs/tests.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::sys::pal::unix::fs::FilePermissions; - -#[test] -fn test_debug_permissions() { - for (expected, mode) in [ - // typical directory - ("FilePermissions { mode: 0o040775 (drwxrwxr-x) }", 0o04_0775), - // typical text file - ("FilePermissions { mode: 0o100664 (-rw-rw-r--) }", 0o10_0664), - // setuid executable (/usr/bin/doas) - ("FilePermissions { mode: 0o104755 (-rwsr-xr-x) }", 0o10_4755), - // char device (/dev/zero) - ("FilePermissions { mode: 0o020666 (crw-rw-rw-) }", 0o02_0666), - // block device (/dev/vda) - ("FilePermissions { mode: 0o060660 (brw-rw----) }", 0o06_0660), - // symbolic link - ("FilePermissions { mode: 0o120777 (lrwxrwxrwx) }", 0o12_0777), - // fifo - ("FilePermissions { mode: 0o010664 (prw-rw-r--) }", 0o01_0664), - // none - ("FilePermissions { mode: 0o100000 (----------) }", 0o10_0000), - // unrecognized - ("FilePermissions { mode: 0o000001 }", 1), - ] { - assert_eq!(format!("{:?}", FilePermissions { mode }), expected); - } - - for (expected, mode) in [ - // owner readable - ("FilePermissions { mode: 0o100400 (-r--------) }", libc::S_IRUSR), - // owner writable - ("FilePermissions { mode: 0o100200 (--w-------) }", libc::S_IWUSR), - // owner executable - ("FilePermissions { mode: 0o100100 (---x------) }", libc::S_IXUSR), - // setuid - ("FilePermissions { mode: 0o104000 (---S------) }", libc::S_ISUID), - // owner executable and setuid - ("FilePermissions { mode: 0o104100 (---s------) }", libc::S_IXUSR | libc::S_ISUID), - // group readable - ("FilePermissions { mode: 0o100040 (----r-----) }", libc::S_IRGRP), - // group writable - ("FilePermissions { mode: 0o100020 (-----w----) }", libc::S_IWGRP), - // group executable - ("FilePermissions { mode: 0o100010 (------x---) }", libc::S_IXGRP), - // setgid - ("FilePermissions { mode: 0o102000 (------S---) }", libc::S_ISGID), - // group executable and setgid - ("FilePermissions { mode: 0o102010 (------s---) }", libc::S_IXGRP | libc::S_ISGID), - // other readable - ("FilePermissions { mode: 0o100004 (-------r--) }", libc::S_IROTH), - // other writeable - ("FilePermissions { mode: 0o100002 (--------w-) }", libc::S_IWOTH), - // other executable - ("FilePermissions { mode: 0o100001 (---------x) }", libc::S_IXOTH), - // sticky - ("FilePermissions { mode: 0o101000 (----------) }", libc::S_ISVTX), - // other executable and sticky - ("FilePermissions { mode: 0o101001 (---------x) }", libc::S_IXOTH | libc::S_ISVTX), - ] { - assert_eq!(format!("{:?}", FilePermissions { mode: libc::S_IFREG | mode }), expected); - } - - for (expected, mode) in [ - // restricted deletion ("sticky") flag is set, and search permission is not granted to others - ("FilePermissions { mode: 0o041000 (d--------T) }", libc::S_ISVTX), - // sticky and searchable - ("FilePermissions { mode: 0o041001 (d--------t) }", libc::S_ISVTX | libc::S_IXOTH), - ] { - assert_eq!(format!("{:?}", FilePermissions { mode: libc::S_IFDIR | mode }), expected); - } -} diff --git a/library/std/src/sys/pal/unix/fuchsia.rs b/library/std/src/sys/pal/unix/fuchsia.rs new file mode 100644 index 0000000000000..c118dee624764 --- /dev/null +++ b/library/std/src/sys/pal/unix/fuchsia.rs @@ -0,0 +1,204 @@ +#![expect(non_camel_case_types)] + +use libc::size_t; + +use crate::ffi::{c_char, c_int, c_void}; +use crate::io; + +////////// +// Time // +////////// + +pub type zx_time_t = i64; + +pub const ZX_TIME_INFINITE: zx_time_t = i64::MAX; + +unsafe extern "C" { + pub safe fn zx_clock_get_monotonic() -> zx_time_t; +} + +///////////// +// Handles // +///////////// + +pub type zx_handle_t = u32; + +pub const ZX_HANDLE_INVALID: zx_handle_t = 0; + +unsafe extern "C" { + pub fn zx_handle_close(handle: zx_handle_t) -> zx_status_t; +} + +/// A safe wrapper around `zx_handle_t`. +pub struct Handle { + raw: zx_handle_t, +} + +impl Handle { + pub fn new(raw: zx_handle_t) -> Handle { + Handle { raw } + } + + pub fn raw(&self) -> zx_handle_t { + self.raw + } +} + +impl Drop for Handle { + fn drop(&mut self) { + unsafe { + zx_cvt(zx_handle_close(self.raw)).expect("Failed to close zx_handle_t"); + } + } +} + +/////////// +// Futex // +/////////// + +pub type zx_futex_t = crate::sync::atomic::Atomic; + +unsafe extern "C" { + pub fn zx_object_wait_one( + handle: zx_handle_t, + signals: zx_signals_t, + timeout: zx_time_t, + pending: *mut zx_signals_t, + ) -> zx_status_t; + + pub fn zx_futex_wait( + value_ptr: *const zx_futex_t, + current_value: zx_futex_t, + new_futex_owner: zx_handle_t, + deadline: zx_time_t, + ) -> zx_status_t; + pub fn zx_futex_wake(value_ptr: *const zx_futex_t, wake_count: u32) -> zx_status_t; + pub fn zx_futex_wake_single_owner(value_ptr: *const zx_futex_t) -> zx_status_t; + pub safe fn zx_thread_self() -> zx_handle_t; +} + +//////////////// +// Properties // +//////////////// + +pub const ZX_PROP_NAME: u32 = 3; + +unsafe extern "C" { + pub fn zx_object_set_property( + handle: zx_handle_t, + property: u32, + value: *const libc::c_void, + value_size: libc::size_t, + ) -> zx_status_t; +} + +///////////// +// Signals // +///////////// + +pub type zx_signals_t = u32; + +pub const ZX_OBJECT_SIGNAL_3: zx_signals_t = 1 << 3; +pub const ZX_TASK_TERMINATED: zx_signals_t = ZX_OBJECT_SIGNAL_3; + +///////////////// +// Object info // +///////////////// + +// The upper four bits gives the minor version. +pub type zx_object_info_topic_t = u32; + +pub const ZX_INFO_PROCESS: zx_object_info_topic_t = 3 | (1 << 28); + +pub type zx_info_process_flags_t = u32; + +// Returned for topic ZX_INFO_PROCESS +#[derive(Default)] +#[repr(C)] +pub struct zx_info_process_t { + pub return_code: i64, + pub start_time: zx_time_t, + pub flags: zx_info_process_flags_t, + pub reserved1: u32, +} + +unsafe extern "C" { + pub fn zx_object_get_info( + handle: zx_handle_t, + topic: u32, + buffer: *mut c_void, + buffer_size: size_t, + actual_size: *mut size_t, + avail: *mut size_t, + ) -> zx_status_t; +} + +/////////////// +// Processes // +/////////////// + +#[derive(Default)] +#[repr(C)] +pub struct fdio_spawn_action_t { + pub action: u32, + pub reserved0: u32, + pub local_fd: i32, + pub target_fd: i32, + pub reserved1: u64, +} + +unsafe extern "C" { + pub fn fdio_spawn_etc( + job: zx_handle_t, + flags: u32, + path: *const c_char, + argv: *const *const c_char, + envp: *const *const c_char, + action_count: size_t, + actions: *const fdio_spawn_action_t, + process: *mut zx_handle_t, + err_msg: *mut c_char, + ) -> zx_status_t; + + pub fn fdio_fd_clone(fd: c_int, out_handle: *mut zx_handle_t) -> zx_status_t; + pub fn fdio_fd_create(handle: zx_handle_t, fd: *mut c_int) -> zx_status_t; + + pub fn zx_task_kill(handle: zx_handle_t) -> zx_status_t; +} + +// fdio_spawn_etc flags + +pub const FDIO_SPAWN_CLONE_JOB: u32 = 0x0001; +pub const FDIO_SPAWN_CLONE_LDSVC: u32 = 0x0002; +pub const FDIO_SPAWN_CLONE_NAMESPACE: u32 = 0x0004; +pub const FDIO_SPAWN_CLONE_ENVIRON: u32 = 0x0010; +pub const FDIO_SPAWN_CLONE_UTC_CLOCK: u32 = 0x0020; + +// fdio_spawn_etc actions + +pub const FDIO_SPAWN_ACTION_TRANSFER_FD: u32 = 0x0002; + +//////////// +// Errors // +//////////// + +pub type zx_status_t = i32; + +pub const ZX_OK: zx_status_t = 0; +pub const ZX_ERR_NOT_SUPPORTED: zx_status_t = -2; +pub const ZX_ERR_INVALID_ARGS: zx_status_t = -10; +pub const ZX_ERR_BAD_HANDLE: zx_status_t = -11; +pub const ZX_ERR_WRONG_TYPE: zx_status_t = -12; +pub const ZX_ERR_BAD_STATE: zx_status_t = -20; +pub const ZX_ERR_TIMED_OUT: zx_status_t = -21; + +pub fn zx_cvt(t: T) -> io::Result +where + T: TryInto + Copy, +{ + if let Ok(status) = TryInto::try_into(t) { + if status < 0 { Err(io::Error::from_raw_os_error(status)) } else { Ok(t) } + } else { + Err(io::Error::last_os_error()) + } +} diff --git a/library/std/src/sys/pal/unix/futex.rs b/library/std/src/sys/pal/unix/futex.rs index d4551dd6a38bb..c23278bdf5e5d 100644 --- a/library/std/src/sys/pal/unix/futex.rs +++ b/library/std/src/sys/pal/unix/futex.rs @@ -8,16 +8,16 @@ target_os = "fuchsia", ))] -use crate::sync::atomic::AtomicU32; +use crate::sync::atomic::Atomic; use crate::time::Duration; /// An atomic for use as a futex that is at least 32-bits but may be larger -pub type Futex = AtomicU32; +pub type Futex = Atomic; /// Must be the underlying type of Futex pub type Primitive = u32; /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallFutex = AtomicU32; +pub type SmallFutex = Atomic; /// Must be the underlying type of SmallFutex pub type SmallPrimitive = u32; @@ -27,7 +27,7 @@ pub type SmallPrimitive = u32; /// /// Returns false on timeout, and true in all other cases. #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { +pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) -> bool { use super::time::Timespec; use crate::ptr::null; use crate::sync::atomic::Ordering::Relaxed; @@ -58,9 +58,9 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - _clockid: libc::CLOCK_MONOTONIC as u32, }); let umtx_timeout_ptr = umtx_timeout.as_ref().map_or(null(), |t| t as *const _); - let umtx_timeout_size = umtx_timeout.as_ref().map_or(0, |t| crate::mem::size_of_val(t)); + let umtx_timeout_size = umtx_timeout.as_ref().map_or(0, |t| size_of_val(t)); libc::_umtx_op( - futex as *const AtomicU32 as *mut _, + futex as *const Atomic as *mut _, libc::UMTX_OP_WAIT_UINT_PRIVATE, expected as libc::c_ulong, crate::ptr::without_provenance_mut(umtx_timeout_size), @@ -71,7 +71,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - // absolute time rather than a relative time. libc::syscall( libc::SYS_futex, - futex as *const AtomicU32, + futex as *const Atomic, libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG, expected, timespec.as_ref().map_or(null(), |t| t as *const libc::timespec), @@ -99,16 +99,16 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - /// /// On some platforms, this always returns false. #[cfg(any(target_os = "linux", target_os = "android"))] -pub fn futex_wake(futex: &AtomicU32) -> bool { - let ptr = futex as *const AtomicU32; +pub fn futex_wake(futex: &Atomic) -> bool { + let ptr = futex as *const Atomic; let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG; unsafe { libc::syscall(libc::SYS_futex, ptr, op, 1) > 0 } } /// Wakes up all threads that are waiting on `futex_wait` on this futex. #[cfg(any(target_os = "linux", target_os = "android"))] -pub fn futex_wake_all(futex: &AtomicU32) { - let ptr = futex as *const AtomicU32; +pub fn futex_wake_all(futex: &Atomic) { + let ptr = futex as *const Atomic; let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG; unsafe { libc::syscall(libc::SYS_futex, ptr, op, i32::MAX); @@ -117,11 +117,11 @@ pub fn futex_wake_all(futex: &AtomicU32) { // FreeBSD doesn't tell us how many threads are woken up, so this always returns false. #[cfg(target_os = "freebsd")] -pub fn futex_wake(futex: &AtomicU32) -> bool { +pub fn futex_wake(futex: &Atomic) -> bool { use crate::ptr::null_mut; unsafe { libc::_umtx_op( - futex as *const AtomicU32 as *mut _, + futex as *const Atomic as *mut _, libc::UMTX_OP_WAKE_PRIVATE, 1, null_mut(), @@ -132,11 +132,11 @@ pub fn futex_wake(futex: &AtomicU32) -> bool { } #[cfg(target_os = "freebsd")] -pub fn futex_wake_all(futex: &AtomicU32) { +pub fn futex_wake_all(futex: &Atomic) { use crate::ptr::null_mut; unsafe { libc::_umtx_op( - futex as *const AtomicU32 as *mut _, + futex as *const Atomic as *mut _, libc::UMTX_OP_WAKE_PRIVATE, i32::MAX as libc::c_ulong, null_mut(), @@ -146,7 +146,7 @@ pub fn futex_wake_all(futex: &AtomicU32) { } #[cfg(target_os = "openbsd")] -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { +pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) -> bool { use super::time::Timespec; use crate::ptr::{null, null_mut}; @@ -157,7 +157,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - let r = unsafe { libc::futex( - futex as *const AtomicU32 as *mut u32, + futex as *const Atomic as *mut u32, libc::FUTEX_WAIT, expected as i32, timespec.as_ref().map_or(null(), |t| t as *const libc::timespec), @@ -169,20 +169,25 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - } #[cfg(target_os = "openbsd")] -pub fn futex_wake(futex: &AtomicU32) -> bool { +pub fn futex_wake(futex: &Atomic) -> bool { use crate::ptr::{null, null_mut}; unsafe { - libc::futex(futex as *const AtomicU32 as *mut u32, libc::FUTEX_WAKE, 1, null(), null_mut()) - > 0 + libc::futex( + futex as *const Atomic as *mut u32, + libc::FUTEX_WAKE, + 1, + null(), + null_mut(), + ) > 0 } } #[cfg(target_os = "openbsd")] -pub fn futex_wake_all(futex: &AtomicU32) { +pub fn futex_wake_all(futex: &Atomic) { use crate::ptr::{null, null_mut}; unsafe { libc::futex( - futex as *const AtomicU32 as *mut u32, + futex as *const Atomic as *mut u32, libc::FUTEX_WAKE, i32::MAX, null(), @@ -192,7 +197,7 @@ pub fn futex_wake_all(futex: &AtomicU32) { } #[cfg(target_os = "dragonfly")] -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { +pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) -> bool { // A timeout of 0 means infinite. // We round smaller timeouts up to 1 millisecond. // Overflows are rounded up to an infinite timeout. @@ -200,7 +205,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - timeout.and_then(|d| Some(i32::try_from(d.as_millis()).ok()?.max(1))).unwrap_or(0); let r = unsafe { - libc::umtx_sleep(futex as *const AtomicU32 as *const i32, expected as i32, timeout_ms) + libc::umtx_sleep(futex as *const Atomic as *const i32, expected as i32, timeout_ms) }; r == 0 || super::os::errno() != libc::ETIMEDOUT @@ -208,28 +213,28 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - // DragonflyBSD doesn't tell us how many threads are woken up, so this always returns false. #[cfg(target_os = "dragonfly")] -pub fn futex_wake(futex: &AtomicU32) -> bool { - unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, 1) }; +pub fn futex_wake(futex: &Atomic) -> bool { + unsafe { libc::umtx_wakeup(futex as *const Atomic as *const i32, 1) }; false } #[cfg(target_os = "dragonfly")] -pub fn futex_wake_all(futex: &AtomicU32) { - unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, i32::MAX) }; +pub fn futex_wake_all(futex: &Atomic) { + unsafe { libc::umtx_wakeup(futex as *const Atomic as *const i32, i32::MAX) }; } #[cfg(target_os = "emscripten")] unsafe extern "C" { - fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int; + fn emscripten_futex_wake(addr: *const Atomic, count: libc::c_int) -> libc::c_int; fn emscripten_futex_wait( - addr: *const AtomicU32, + addr: *const Atomic, val: libc::c_uint, max_wait_ms: libc::c_double, ) -> libc::c_int; } #[cfg(target_os = "emscripten")] -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { +pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) -> bool { unsafe { emscripten_futex_wait( futex, @@ -240,72 +245,38 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - } #[cfg(target_os = "emscripten")] -pub fn futex_wake(futex: &AtomicU32) -> bool { +pub fn futex_wake(futex: &Atomic) -> bool { unsafe { emscripten_futex_wake(futex, 1) > 0 } } #[cfg(target_os = "emscripten")] -pub fn futex_wake_all(futex: &AtomicU32) { +pub fn futex_wake_all(futex: &Atomic) { unsafe { emscripten_futex_wake(futex, i32::MAX) }; } #[cfg(target_os = "fuchsia")] -pub mod zircon { - pub type zx_futex_t = crate::sync::atomic::AtomicU32; - pub type zx_handle_t = u32; - pub type zx_status_t = i32; - pub type zx_time_t = i64; - - pub const ZX_HANDLE_INVALID: zx_handle_t = 0; - - pub const ZX_TIME_INFINITE: zx_time_t = zx_time_t::MAX; - - pub const ZX_OK: zx_status_t = 0; - pub const ZX_ERR_INVALID_ARGS: zx_status_t = -10; - pub const ZX_ERR_BAD_HANDLE: zx_status_t = -11; - pub const ZX_ERR_WRONG_TYPE: zx_status_t = -12; - pub const ZX_ERR_BAD_STATE: zx_status_t = -20; - pub const ZX_ERR_TIMED_OUT: zx_status_t = -21; - - unsafe extern "C" { - pub fn zx_clock_get_monotonic() -> zx_time_t; - pub fn zx_futex_wait( - value_ptr: *const zx_futex_t, - current_value: zx_futex_t, - new_futex_owner: zx_handle_t, - deadline: zx_time_t, - ) -> zx_status_t; - pub fn zx_futex_wake(value_ptr: *const zx_futex_t, wake_count: u32) -> zx_status_t; - pub fn zx_futex_wake_single_owner(value_ptr: *const zx_futex_t) -> zx_status_t; - pub fn zx_thread_self() -> zx_handle_t; - } -} +pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) -> bool { + use super::fuchsia::*; -#[cfg(target_os = "fuchsia")] -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { // Sleep forever if the timeout is longer than fits in a i64. let deadline = timeout - .and_then(|d| { - i64::try_from(d.as_nanos()) - .ok()? - .checked_add(unsafe { zircon::zx_clock_get_monotonic() }) - }) - .unwrap_or(zircon::ZX_TIME_INFINITE); + .and_then(|d| i64::try_from(d.as_nanos()).ok()?.checked_add(zx_clock_get_monotonic())) + .unwrap_or(ZX_TIME_INFINITE); unsafe { - zircon::zx_futex_wait(futex, AtomicU32::new(expected), zircon::ZX_HANDLE_INVALID, deadline) - != zircon::ZX_ERR_TIMED_OUT + zx_futex_wait(futex, zx_futex_t::new(expected), ZX_HANDLE_INVALID, deadline) + != ZX_ERR_TIMED_OUT } } // Fuchsia doesn't tell us how many threads are woken up, so this always returns false. #[cfg(target_os = "fuchsia")] -pub fn futex_wake(futex: &AtomicU32) -> bool { - unsafe { zircon::zx_futex_wake(futex, 1) }; +pub fn futex_wake(futex: &Atomic) -> bool { + unsafe { super::fuchsia::zx_futex_wake(futex, 1) }; false } #[cfg(target_os = "fuchsia")] -pub fn futex_wake_all(futex: &AtomicU32) { - unsafe { zircon::zx_futex_wake(futex, u32::MAX) }; +pub fn futex_wake_all(futex: &Atomic) { + unsafe { super::fuchsia::zx_futex_wake(futex, u32::MAX) }; } diff --git a/library/std/src/sys/pal/unix/kernel_copy.rs b/library/std/src/sys/pal/unix/kernel_copy.rs index bbf29f3252341..b984afa149d06 100644 --- a/library/std/src/sys/pal/unix/kernel_copy.rs +++ b/library/std/src/sys/pal/unix/kernel_copy.rs @@ -62,7 +62,7 @@ use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use crate::os::unix::net::UnixStream; use crate::process::{ChildStderr, ChildStdin, ChildStdout}; use crate::ptr; -use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicU8, Ordering}; use crate::sys::cvt; use crate::sys::fs::CachedFileMetadata; use crate::sys::weak::syscall; @@ -596,7 +596,7 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> // 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: AtomicU8 = AtomicU8::new(NOT_PROBED); + static HAS_COPY_FILE_RANGE: Atomic = AtomicU8::new(NOT_PROBED); let mut have_probed = match HAS_COPY_FILE_RANGE.load(Ordering::Relaxed) { NOT_PROBED => false, @@ -604,16 +604,16 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> _ => true, }; - syscall! { + syscall!( 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::ssize_t - } + flags: libc::c_uint, + ) -> libc::ssize_t; + ); fn probe_copy_file_range_support() -> u8 { // In some cases, we cannot determine availability from the first @@ -721,22 +721,22 @@ enum SpliceMode { /// performs splice or sendfile between file descriptors /// Does _not_ fall back to a generic copy loop. fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> CopyResult { - static HAS_SENDFILE: AtomicBool = AtomicBool::new(true); - static HAS_SPLICE: AtomicBool = AtomicBool::new(true); + static HAS_SENDFILE: Atomic = AtomicBool::new(true); + static HAS_SPLICE: Atomic = AtomicBool::new(true); // Android builds use feature level 14, but the libc wrapper for splice is // gated on feature level 21+, so we have to invoke the syscall directly. #[cfg(target_os = "android")] - syscall! { + syscall!( fn splice( srcfd: libc::c_int, src_offset: *const i64, dstfd: libc::c_int, dst_offset: *const i64, len: libc::size_t, - flags: libc::c_int - ) -> libc::ssize_t - } + flags: libc::c_int, + ) -> libc::ssize_t; + ); #[cfg(target_os = "linux")] use libc::splice; diff --git a/library/std/src/sys/pal/unix/linux/pidfd.rs b/library/std/src/sys/pal/unix/linux/pidfd.rs index 78744430f3b51..2d949ec9e91f7 100644 --- a/library/std/src/sys/pal/unix/linux/pidfd.rs +++ b/library/std/src/sys/pal/unix/linux/pidfd.rs @@ -1,7 +1,7 @@ use crate::io; use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; use crate::sys::cvt; -use crate::sys::pal::unix::fd::FileDesc; +use crate::sys::fd::FileDesc; use crate::sys::process::ExitStatus; use crate::sys_common::{AsInner, FromInner, IntoInner}; diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index c0b56d8d2b28a..ba9e14b8009cd 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -6,10 +6,8 @@ use crate::io::ErrorKind; #[macro_use] pub mod weak; -pub mod args; -pub mod env; -pub mod fd; -pub mod fs; +#[cfg(target_os = "fuchsia")] +pub mod fuchsia; pub mod futex; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod kernel_copy; @@ -17,9 +15,7 @@ pub mod kernel_copy; pub mod linux; pub mod os; pub mod pipe; -pub mod process; pub mod stack_overflow; -pub mod stdio; pub mod sync; pub mod thread; pub mod thread_parking; @@ -29,6 +25,7 @@ pub mod time; pub fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} #[cfg(not(target_os = "espidf"))] +#[cfg_attr(target_os = "vita", allow(unused_variables))] // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. // See `fn init()` in `library/std/src/rt.rs` for docs on `sigpipe`. @@ -49,7 +46,8 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { reset_sigpipe(sigpipe); stack_overflow::init(); - args::init(argc, argv); + #[cfg(not(target_os = "vita"))] + crate::sys::args::init(argc, argv); // Normally, `thread::spawn` will call `Thread::set_name` but since this thread // already exists, we have to call it ourselves. We only do this on Apple targets @@ -206,7 +204,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "vxworks", target_os = "vita", )))] -static ON_BROKEN_PIPE_FLAG_USED: crate::sync::atomic::AtomicBool = +static ON_BROKEN_PIPE_FLAG_USED: crate::sync::atomic::Atomic = crate::sync::atomic::AtomicBool::new(false); #[cfg(not(any( @@ -275,6 +273,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { libc::ETXTBSY => ExecutableFileBusy, libc::EXDEV => CrossesDevices, libc::EINPROGRESS => InProgress, + libc::EOPNOTSUPP => Unsupported, libc::EACCES | libc::EPERM => PermissionDenied, @@ -382,7 +381,7 @@ cfg_if::cfg_if! { #[link(name = "pthread")] #[link(name = "rt")] unsafe extern "C" {} - } else if #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))] { + } else if #[cfg(any(target_os = "dragonfly", target_os = "openbsd", target_os = "cygwin"))] { #[link(name = "pthread")] unsafe extern "C" {} } else if #[cfg(target_os = "solaris")] { @@ -421,7 +420,7 @@ cfg_if::cfg_if! { } #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] -mod unsupported { +pub mod unsupported { use crate::io; pub fn unsupported() -> io::Result { diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 3a238d160cb57..48609030aed1a 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -5,20 +5,15 @@ #[cfg(test)] mod tests; -use core::slice::memchr; - use libc::{c_char, c_int, c_void}; use crate::error::Error as StdError; -use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::os::unix::prelude::*; use crate::path::{self, PathBuf}; -use crate::sync::{PoisonError, RwLock}; -use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; -#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] -use crate::sys::weak::weak; -use crate::sys::{cvt, fd}; -use crate::{fmt, io, iter, mem, ptr, slice, str, vec}; +use crate::sys::common::small_c_string::run_path_with_cstr; +use crate::sys::cvt; +use crate::{fmt, io, iter, mem, ptr, slice, str}; const TMPBUF_SZ: usize = 128; @@ -46,6 +41,7 @@ unsafe extern "C" { any( target_os = "netbsd", target_os = "openbsd", + target_os = "cygwin", target_os = "android", target_os = "redox", target_os = "nuttx", @@ -58,11 +54,14 @@ unsafe extern "C" { #[cfg_attr(any(target_os = "freebsd", target_vendor = "apple"), link_name = "__error")] #[cfg_attr(target_os = "haiku", link_name = "_errnop")] #[cfg_attr(target_os = "aix", link_name = "_Errno")] - fn errno_location() -> *mut c_int; + // SAFETY: this will always return the same pointer on a given thread. + #[unsafe(ffi_const)] + pub safe fn errno_location() -> *mut c_int; } /// Returns the platform-specific value of errno #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] +#[inline] pub fn errno() -> i32 { unsafe { (*errno_location()) as i32 } } @@ -71,16 +70,19 @@ pub fn errno() -> i32 { // needed for readdir and syscall! #[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))] #[allow(dead_code)] // but not all target cfgs actually end up using it +#[inline] pub fn set_errno(e: i32) { unsafe { *errno_location() = e as c_int } } #[cfg(target_os = "vxworks")] +#[inline] pub fn errno() -> i32 { unsafe { libc::errnoGet() } } #[cfg(target_os = "rtems")] +#[inline] pub fn errno() -> i32 { unsafe extern "C" { #[thread_local] @@ -91,6 +93,7 @@ pub fn errno() -> i32 { } #[cfg(target_os = "dragonfly")] +#[inline] pub fn errno() -> i32 { unsafe extern "C" { #[thread_local] @@ -102,6 +105,7 @@ pub fn errno() -> i32 { #[cfg(target_os = "dragonfly")] #[allow(dead_code)] +#[inline] pub fn set_errno(e: i32) { unsafe extern "C" { #[thread_local] @@ -118,7 +122,12 @@ pub fn error_string(errno: i32) -> String { unsafe extern "C" { #[cfg_attr( all( - any(target_os = "linux", target_os = "hurd", target_env = "newlib"), + any( + target_os = "linux", + target_os = "hurd", + target_env = "newlib", + target_os = "cygwin" + ), not(target_env = "ohos") ), link_name = "__xpg_strerror_r" @@ -395,6 +404,7 @@ pub fn current_exe() -> io::Result { #[cfg(any( target_os = "linux", + target_os = "cygwin", target_os = "hurd", target_os = "android", target_os = "nuttx", @@ -484,7 +494,12 @@ pub fn current_exe() -> io::Result { } } -#[cfg(any(target_os = "redox", target_os = "rtems"))] +#[cfg(target_os = "redox")] +pub fn current_exe() -> io::Result { + crate::fs::read_to_string("/scheme/sys/exe").map(PathBuf::from) +} + +#[cfg(target_os = "rtems")] pub fn current_exe() -> io::Result { crate::fs::read_to_string("sys:exe").map(PathBuf::from) } @@ -532,166 +547,6 @@ pub fn current_exe() -> io::Result { if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) } } -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -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() - } -} - -// Use `_NSGetEnviron` on Apple platforms. -// -// `_NSGetEnviron` is the documented alternative (see `man environ`), and has -// been available since the first versions of both macOS and iOS. -// -// Nowadays, specifically since macOS 10.8, `environ` has been exposed through -// `libdyld.dylib`, which is linked via. `libSystem.dylib`: -// -// -// So in the end, it likely doesn't really matter which option we use, but the -// performance cost of using `_NSGetEnviron` is extremely miniscule, and it -// might be ever so slightly more supported, so let's just use that. -// -// NOTE: The header where this is defined (`crt_externs.h`) was added to the -// iOS 13.0 SDK, which has been the source of a great deal of confusion in the -// past about the availability of this API. -// -// NOTE(madsmtm): Neither this nor using `environ` has been verified to not -// cause App Store rejections; if this is found to be the case, an alternative -// implementation of this is possible using `[NSProcessInfo environment]` -// - which internally uses `_NSGetEnviron` and a system-wide lock on the -// environment variables to protect against `setenv`, so using that might be -// desirable anyhow? Though it also means that we have to link to Foundation. -#[cfg(target_vendor = "apple")] -pub unsafe fn environ() -> *mut *const *const c_char { - libc::_NSGetEnviron() as *mut *const *const c_char -} - -// Use the `environ` static which is part of POSIX. -#[cfg(not(target_vendor = "apple"))] -pub unsafe fn environ() -> *mut *const *const c_char { - unsafe extern "C" { - static mut environ: *const *const c_char; - } - &raw mut environ -} - -static ENV_LOCK: RwLock<()> = RwLock::new(()); - -pub fn env_read_lock() -> impl Drop { - ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) -} - -/// 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_read_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() }; - } - - 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) -> Option { - // environment variables with a nul byte can't be set, so their value is - // always None as well - run_with_cstr(k.as_bytes(), &|k| { - let _guard = env_read_lock(); - let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - - if v.is_null() { - Ok(None) - } else { - // SAFETY: `v` cannot be mutated while executing this line since we've a read lock - let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); - - Ok(Some(OsStringExt::from_vec(bytes))) - } - }) - .ok() - .flatten() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), &|k| { - run_with_cstr(v.as_bytes(), &|v| { - let _guard = ENV_LOCK.write(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - }) - }) -} - -pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), &|nbuf| { - let _guard = ENV_LOCK.write(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - }) -} - #[cfg(not(target_os = "espidf"))] pub fn page_size() -> usize { unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } diff --git a/library/std/src/sys/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs index 4a992e32a9184..55510153dc847 100644 --- a/library/std/src/sys/pal/unix/pipe.rs +++ b/library/std/src/sys/pal/unix/pipe.rs @@ -27,6 +27,7 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { target_os = "linux", target_os = "netbsd", target_os = "openbsd", + target_os = "cygwin", target_os = "redox" ))] { unsafe { diff --git a/library/std/src/sys/pal/unix/process/mod.rs b/library/std/src/sys/pal/unix/process/mod.rs deleted file mode 100644 index 2751d51c44d2a..0000000000000 --- a/library/std/src/sys/pal/unix/process/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes}; -pub use self::process_inner::{ExitStatus, ExitStatusError, Process}; -pub use crate::ffi::OsString as EnvKey; - -#[cfg_attr(any(target_os = "espidf", target_os = "horizon", target_os = "nuttx"), allow(unused))] -mod process_common; - -#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] -mod process_unsupported; - -cfg_if::cfg_if! { - if #[cfg(target_os = "fuchsia")] { - #[path = "process_fuchsia.rs"] - mod process_inner; - mod zircon; - } else if #[cfg(target_os = "vxworks")] { - #[path = "process_vxworks.rs"] - mod process_inner; - } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] { - mod process_inner { - pub use super::process_unsupported::*; - } - } else { - #[path = "process_unix.rs"] - mod process_inner; - } -} diff --git a/library/std/src/sys/pal/unix/process/process_common.rs b/library/std/src/sys/pal/unix/process/process_common.rs deleted file mode 100644 index 342818ac91183..0000000000000 --- a/library/std/src/sys/pal/unix/process/process_common.rs +++ /dev/null @@ -1,673 +0,0 @@ -#[cfg(all(test, not(target_os = "emscripten")))] -mod tests; - -use libc::{EXIT_FAILURE, EXIT_SUCCESS, c_char, c_int, gid_t, pid_t, uid_t}; - -use crate::collections::BTreeMap; -use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::os::unix::prelude::*; -use crate::path::Path; -use crate::sys::fd::FileDesc; -use crate::sys::fs::File; -#[cfg(not(target_os = "fuchsia"))] -use crate::sys::fs::OpenOptions; -use crate::sys::pipe::{self, AnonPipe}; -use crate::sys_common::process::{CommandEnv, CommandEnvs}; -use crate::sys_common::{FromInner, IntoInner}; -use crate::{fmt, io, ptr}; - -cfg_if::cfg_if! { - if #[cfg(target_os = "fuchsia")] { - // fuchsia doesn't have /dev/null - } else if #[cfg(target_os = "redox")] { - const DEV_NULL: &CStr = c"null:"; - } else if #[cfg(target_os = "vxworks")] { - const DEV_NULL: &CStr = c"/null"; - } else { - const DEV_NULL: &CStr = c"/dev/null"; - } -} - -// 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://github.com/aosp-mirror/platform_bionic/blob/ad8dcd6023294b646e5a8288c0ed431b0845da49/libc/include/android/legacy_signal_inlines.h -cfg_if::cfg_if! { - if #[cfg(target_os = "android")] { - #[allow(dead_code)] - 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::{ - mem::{align_of, size_of}, - slice, - }; - use libc::{c_ulong, sigset_t}; - - // The implementations from bionic (android libc) type pun `sigset_t` as an - // array of `c_ulong`. This works, but lets add a smoke check to make sure - // that doesn't change. - const _: () = assert!( - align_of::() == align_of::() - && (size_of::() % size_of::()) == 0 - ); - - let bit = (signum - 1) as usize; - if set.is_null() || bit >= (8 * size_of::()) { - crate::sys::pal::unix::os::set_errno(libc::EINVAL); - return -1; - } - let raw = slice::from_raw_parts_mut( - set as *mut c_ulong, - size_of::() / size_of::(), - ); - const LONG_BIT: usize = size_of::() * 8; - raw[bit / LONG_BIT] |= 1 << (bit % LONG_BIT); - return 0; - } - } else { - #[allow(unused_imports)] - pub use libc::{sigemptyset, sigaddset}; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -pub struct Command { - program: CString, - args: Vec, - /// Exactly what will be passed to `execvp`. - /// - /// First element is a pointer to `program`, followed by pointers to - /// `args`, followed by a `null`. Be careful when modifying `program` or - /// `args` to properly update this as well. - argv: Argv, - env: CommandEnv, - - program_kind: ProgramKind, - cwd: Option, - uid: Option, - gid: Option, - saw_nul: bool, - closures: Vec io::Result<()> + Send + Sync>>, - groups: Option>, - stdin: Option, - stdout: Option, - stderr: Option, - #[cfg(target_os = "linux")] - create_pidfd: bool, - pgroup: 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 -#[cfg_attr(target_os = "vita", allow(dead_code))] -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, -} - -#[derive(Debug)] -pub enum Stdio { - Inherit, - Null, - MakePipe, - Fd(FileDesc), - StaticFd(BorrowedFd<'static>), -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum ProgramKind { - /// A program that would be looked up on the PATH (e.g. `ls`) - PathLookup, - /// A relative path (e.g. `my-dir/foo`, `../foo`, `./foo`) - Relative, - /// An absolute path. - Absolute, -} - -impl ProgramKind { - fn new(program: &OsStr) -> Self { - if program.as_encoded_bytes().starts_with(b"/") { - Self::Absolute - } else if program.as_encoded_bytes().contains(&b'/') { - // If the program has more than one component in it, it is a relative path. - Self::Relative - } else { - Self::PathLookup - } - } -} - -impl Command { - #[cfg(not(target_os = "linux"))] - pub fn new(program: &OsStr) -> Command { - let mut saw_nul = false; - let program_kind = ProgramKind::new(program.as_ref()); - let program = os2c(program, &mut saw_nul); - Command { - argv: Argv(vec![program.as_ptr(), ptr::null()]), - args: vec![program.clone()], - program, - program_kind, - env: Default::default(), - cwd: None, - uid: None, - gid: None, - saw_nul, - closures: Vec::new(), - groups: None, - stdin: None, - stdout: None, - stderr: None, - pgroup: None, - } - } - - #[cfg(target_os = "linux")] - pub fn new(program: &OsStr) -> Command { - let mut saw_nul = false; - let program_kind = ProgramKind::new(program.as_ref()); - let program = os2c(program, &mut saw_nul); - Command { - argv: Argv(vec![program.as_ptr(), ptr::null()]), - args: vec![program.clone()], - program, - program_kind, - env: Default::default(), - cwd: None, - uid: None, - gid: None, - saw_nul, - closures: Vec::new(), - groups: None, - stdin: None, - stdout: None, - stderr: None, - create_pidfd: false, - pgroup: 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 groups(&mut self, groups: &[gid_t]) { - self.groups = Some(Box::from(groups)); - } - pub fn pgroup(&mut self, pgroup: pid_t) { - self.pgroup = Some(pgroup); - } - - #[cfg(target_os = "linux")] - pub fn create_pidfd(&mut self, val: bool) { - self.create_pidfd = val; - } - - #[cfg(not(target_os = "linux"))] - #[allow(dead_code)] - pub fn get_create_pidfd(&self) -> bool { - false - } - - #[cfg(target_os = "linux")] - pub fn get_create_pidfd(&self) -> bool { - self.create_pidfd - } - - pub fn saw_nul(&self) -> bool { - self.saw_nul - } - - pub fn get_program(&self) -> &OsStr { - OsStr::from_bytes(self.program.as_bytes()) - } - - #[allow(dead_code)] - pub fn get_program_kind(&self) -> ProgramKind { - self.program_kind - } - - pub fn get_args(&self) -> CommandArgs<'_> { - let mut iter = self.args.iter(); - iter.next(); - CommandArgs { iter } - } - - pub fn get_envs(&self) -> CommandEnvs<'_> { - self.env.iter() - } - - pub fn get_current_dir(&self) -> Option<&Path> { - self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes()))) - } - - pub fn get_argv(&self) -> &Vec<*const c_char> { - &self.argv.0 - } - - pub fn get_program_cstr(&self) -> &CStr { - &*self.program - } - - #[allow(dead_code)] - pub fn get_cwd(&self) -> Option<&CStr> { - self.cwd.as_deref() - } - #[allow(dead_code)] - pub fn get_uid(&self) -> Option { - self.uid - } - #[allow(dead_code)] - pub fn get_gid(&self) -> Option { - self.gid - } - #[allow(dead_code)] - pub fn get_groups(&self) -> Option<&[gid_t]> { - self.groups.as_deref() - } - #[allow(dead_code)] - pub fn get_pgroup(&self) -> Option { - self.pgroup - } - - 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() - } - - #[allow(dead_code)] - pub fn program_is_path(&self) -> bool { - self.program.to_bytes().contains(&b'/') - } - - 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; - c"".to_owned() - }) -} - -// 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.as_raw_fd() >= 0 && fd.as_raw_fd() <= libc::STDERR_FILENO { - Ok((ChildStdio::Owned(fd.duplicate()?), None)) - } else { - Ok((ChildStdio::Explicit(fd.as_raw_fd()), None)) - } - } - - Stdio::StaticFd(fd) => { - let fd = FileDesc::from_inner(fd.try_clone_to_owned()?); - Ok((ChildStdio::Owned(fd), None)) - } - - Stdio::MakePipe => { - let (reader, writer) = pipe::anon_pipe()?; - let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) }; - Ok((ChildStdio::Owned(theirs.into_inner()), Some(ours))) - } - - #[cfg(not(target_os = "fuchsia"))] - Stdio::Null => { - let mut opts = OpenOptions::new(); - opts.read(readable); - opts.write(!readable); - let fd = File::open_c(DEV_NULL, &opts)?; - Ok((ChildStdio::Owned(fd.into_inner()), None)) - } - - #[cfg(target_os = "fuchsia")] - Stdio::Null => Ok((ChildStdio::Null, None)), - } - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - Stdio::Fd(pipe.into_inner()) - } -} - -impl From for Stdio { - fn from(file: File) -> Stdio { - Stdio::Fd(file.into_inner()) - } -} - -impl From for Stdio { - fn from(_: io::Stdout) -> Stdio { - // This ought really to be is Stdio::StaticFd(input_argument.as_fd()). - // But AsFd::as_fd takes its argument by reference, and yields - // a bounded lifetime, so it's no use here. There is no AsStaticFd. - // - // Additionally AsFd is only implemented for the *locked* versions. - // We don't want to lock them here. (The implications of not locking - // are the same as those for process::Stdio::inherit().) - // - // Arguably the hypothetical AsStaticFd and AsFd<'static> - // should be implemented for io::Stdout, not just for StdoutLocked. - Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) }) - } -} - -impl From for Stdio { - fn from(_: io::Stderr) -> Stdio { - Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) }) - } -} - -impl ChildStdio { - pub fn fd(&self) -> Option { - match *self { - ChildStdio::Inherit => None, - ChildStdio::Explicit(fd) => Some(fd), - ChildStdio::Owned(ref fd) => Some(fd.as_raw_fd()), - - #[cfg(target_os = "fuchsia")] - ChildStdio::Null => None, - } - } -} - -impl fmt::Debug for Command { - // show all attributes but `self.closures` which does not implement `Debug` - // and `self.argv` which is not useful for debugging - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - let mut debug_command = f.debug_struct("Command"); - debug_command.field("program", &self.program).field("args", &self.args); - if !self.env.is_unchanged() { - debug_command.field("env", &self.env); - } - - if self.cwd.is_some() { - debug_command.field("cwd", &self.cwd); - } - if self.uid.is_some() { - debug_command.field("uid", &self.uid); - } - if self.gid.is_some() { - debug_command.field("gid", &self.gid); - } - - if self.groups.is_some() { - debug_command.field("groups", &self.groups); - } - - if self.stdin.is_some() { - debug_command.field("stdin", &self.stdin); - } - if self.stdout.is_some() { - debug_command.field("stdout", &self.stdout); - } - if self.stderr.is_some() { - debug_command.field("stderr", &self.stderr); - } - if self.pgroup.is_some() { - debug_command.field("pgroup", &self.pgroup); - } - - #[cfg(target_os = "linux")] - { - debug_command.field("create_pidfd", &self.create_pidfd); - } - - debug_command.finish() - } else { - if let Some(ref cwd) = self.cwd { - write!(f, "cd {cwd:?} && ")?; - } - if self.env.does_clear() { - write!(f, "env -i ")?; - // Altered env vars will be printed next, that should exactly work as expected. - } else { - // Removed env vars need the command to be wrapped in `env`. - let mut any_removed = false; - for (key, value_opt) in self.get_envs() { - if value_opt.is_none() { - if !any_removed { - write!(f, "env ")?; - any_removed = true; - } - write!(f, "-u {} ", key.to_string_lossy())?; - } - } - } - // Altered env vars can just be added in front of the program. - for (key, value_opt) in self.get_envs() { - if let Some(value) = value_opt { - write!(f, "{}={value:?} ", key.to_string_lossy())?; - } - } - 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)] -pub struct ExitCode(u8); - -impl fmt::Debug for ExitCode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("unix_exit_status").field(&self.0).finish() - } -} - -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 - } -} - -impl From for ExitCode { - fn from(code: u8) -> Self { - Self(code) - } -} - -pub struct CommandArgs<'a> { - iter: crate::slice::Iter<'a, CString>, -} - -impl<'a> Iterator for CommandArgs<'a> { - type Item = &'a OsStr; - fn next(&mut self) -> Option<&'a OsStr> { - self.iter.next().map(|cs| OsStr::from_bytes(cs.as_bytes())) - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl<'a> ExactSizeIterator for CommandArgs<'a> { - fn len(&self) -> usize { - self.iter.len() - } - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -impl<'a> fmt::Debug for CommandArgs<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter.clone()).finish() - } -} diff --git a/library/std/src/sys/pal/unix/process/process_fuchsia.rs b/library/std/src/sys/pal/unix/process/process_fuchsia.rs deleted file mode 100644 index 4ddc96356b996..0000000000000 --- a/library/std/src/sys/pal/unix/process/process_fuchsia.rs +++ /dev/null @@ -1,326 +0,0 @@ -use libc::{c_int, size_t}; - -use crate::num::NonZero; -use crate::sys::process::process_common::*; -use crate::sys::process::zircon::{Handle, zx_handle_t}; -use crate::{fmt, io, mem, ptr}; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -impl Command { - pub fn spawn( - &mut self, - default: Stdio, - needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - let envp = self.capture_env(); - - if self.saw_nul() { - return Err(io::const_error!( - io::ErrorKind::InvalidInput, - "nul byte found in provided data", - )); - } - - let (ours, theirs) = self.setup_io(default, needs_stdin)?; - - let process_handle = unsafe { self.do_exec(theirs, envp.as_ref())? }; - - Ok((Process { handle: Handle::new(process_handle) }, ours)) - } - - pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; - crate::sys_common::process::wait_with_output(proc, pipes) - } - - pub fn exec(&mut self, default: Stdio) -> io::Error { - if self.saw_nul() { - return io::const_error!( - io::ErrorKind::InvalidInput, - "nul byte found in provided data", - ); - } - - match self.setup_io(default, true) { - Ok((_, _)) => { - // FIXME: This is tough because we don't support the exec syscalls - unimplemented!(); - } - Err(e) => e, - } - } - - unsafe fn do_exec( - &mut self, - stdio: ChildPipes, - maybe_envp: Option<&CStringArray>, - ) -> io::Result { - use crate::sys::process::zircon::*; - - let envp = match maybe_envp { - // None means to clone the current environment, which is done in the - // flags below. - None => ptr::null(), - Some(envp) => envp.as_ptr(), - }; - - let make_action = |local_io: &ChildStdio, target_fd| -> io::Result { - if let Some(local_fd) = local_io.fd() { - Ok(fdio_spawn_action_t { - action: FDIO_SPAWN_ACTION_TRANSFER_FD, - local_fd, - target_fd, - ..Default::default() - }) - } else { - if let ChildStdio::Null = local_io { - // acts as no-op - return Ok(Default::default()); - } - - let mut handle = ZX_HANDLE_INVALID; - let status = fdio_fd_clone(target_fd, &mut handle); - if status == ERR_INVALID_ARGS || status == ERR_NOT_SUPPORTED { - // This descriptor is closed; skip it rather than generating an - // error. - return Ok(Default::default()); - } - zx_cvt(status)?; - - let mut cloned_fd = 0; - zx_cvt(fdio_fd_create(handle, &mut cloned_fd))?; - - Ok(fdio_spawn_action_t { - action: FDIO_SPAWN_ACTION_TRANSFER_FD, - local_fd: cloned_fd as i32, - target_fd, - ..Default::default() - }) - } - }; - - // Clone stdin, stdout, and stderr - let action1 = make_action(&stdio.stdin, 0)?; - let action2 = make_action(&stdio.stdout, 1)?; - let action3 = make_action(&stdio.stderr, 2)?; - let actions = [action1, action2, action3]; - - // We don't want FileDesc::drop to be called on any stdio. fdio_spawn_etc - // always consumes transferred file descriptors. - mem::forget(stdio); - - for callback in self.get_closures().iter_mut() { - callback()?; - } - - let mut process_handle: zx_handle_t = 0; - zx_cvt(fdio_spawn_etc( - ZX_HANDLE_INVALID, - FDIO_SPAWN_CLONE_JOB - | FDIO_SPAWN_CLONE_LDSVC - | FDIO_SPAWN_CLONE_NAMESPACE - | FDIO_SPAWN_CLONE_ENVIRON // this is ignored when envp is non-null - | FDIO_SPAWN_CLONE_UTC_CLOCK, - self.get_program_cstr().as_ptr(), - self.get_argv().as_ptr(), - envp, - actions.len() as size_t, - actions.as_ptr(), - &mut process_handle, - ptr::null_mut(), - ))?; - // FIXME: See if we want to do something with that err_msg - - Ok(process_handle) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Processes -//////////////////////////////////////////////////////////////////////////////// - -pub struct Process { - handle: Handle, -} - -impl Process { - pub fn id(&self) -> u32 { - self.handle.raw() as u32 - } - - pub fn kill(&mut self) -> io::Result<()> { - use crate::sys::process::zircon::*; - - unsafe { - zx_cvt(zx_task_kill(self.handle.raw()))?; - } - - Ok(()) - } - - pub fn wait(&mut self) -> io::Result { - use crate::sys::process::zircon::*; - - let mut proc_info: zx_info_process_t = Default::default(); - let mut actual: size_t = 0; - let mut avail: size_t = 0; - - unsafe { - zx_cvt(zx_object_wait_one( - self.handle.raw(), - ZX_TASK_TERMINATED, - ZX_TIME_INFINITE, - ptr::null_mut(), - ))?; - zx_cvt(zx_object_get_info( - self.handle.raw(), - ZX_INFO_PROCESS, - (&raw mut proc_info) as *mut libc::c_void, - mem::size_of::(), - &mut actual, - &mut avail, - ))?; - } - if actual != 1 { - return Err(io::const_error!( - io::ErrorKind::InvalidData, - "failed to get exit status of process", - )); - } - Ok(ExitStatus(proc_info.return_code)) - } - - pub fn try_wait(&mut self) -> io::Result> { - use crate::sys::process::zircon::*; - - let mut proc_info: zx_info_process_t = Default::default(); - let mut actual: size_t = 0; - let mut avail: size_t = 0; - - unsafe { - let status = - zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED, 0, ptr::null_mut()); - match status { - 0 => {} // Success - x if x == ERR_TIMED_OUT => { - return Ok(None); - } - _ => { - panic!("Failed to wait on process handle: {status}"); - } - } - zx_cvt(zx_object_get_info( - self.handle.raw(), - ZX_INFO_PROCESS, - (&raw mut proc_info) as *mut libc::c_void, - mem::size_of::(), - &mut actual, - &mut avail, - ))?; - } - if actual != 1 { - return Err(io::const_error!( - io::ErrorKind::InvalidData, - "failed to get exit status of process", - )); - } - Ok(Some(ExitStatus(proc_info.return_code))) - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] -pub struct ExitStatus(i64); - -impl ExitStatus { - pub fn exit_ok(&self) -> Result<(), ExitStatusError> { - match NonZero::try_from(self.0) { - /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), - /* was zero, couldn't convert */ Err(_) => Ok(()), - } - } - - pub fn code(&self) -> Option { - // FIXME: support extracting return code as an i64 - self.0.try_into().ok() - } - - pub fn signal(&self) -> Option { - None - } - - // FIXME: The actually-Unix implementation in process_unix.rs uses WSTOPSIG, WCOREDUMP et al. - // I infer from the implementation of `success`, `code` and `signal` above that these are not - // available on Fuchsia. - // - // It does not appear that Fuchsia is Unix-like enough to implement ExitStatus (or indeed many - // other things from std::os::unix) properly. This veneer is always going to be a bodge. So - // while I don't know if these implementations are actually correct, I think they will do for - // now at least. - pub fn core_dumped(&self) -> bool { - false - } - pub fn stopped_signal(&self) -> Option { - None - } - pub fn continued(&self) -> bool { - false - } - - pub fn into_raw(&self) -> c_int { - // We don't know what someone who calls into_raw() will do with this value, but it should - // have the conventional Unix representation. Despite the fact that this is not - // standardised in SuS or POSIX, all Unix systems encode the signal and exit status the - // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behavior on every - // Unix.) - // - // The caller of `std::os::unix::into_raw` is probably wanting a Unix exit status, and may - // do their own shifting and masking, or even pass the status to another computer running a - // different Unix variant. - // - // The other view would be to say that the caller on Fuchsia ought to know that `into_raw` - // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is - // not possible here because we must return a c_int because that's what Unix (including - // SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't - // necessarily fit. - // - // It seems to me that the right answer would be to provide std::os::fuchsia with its - // own ExitStatusExt, rather that trying to provide a not very convincing imitation of - // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But - // fixing this up that is beyond the scope of my efforts now. - let exit_status_as_if_unix: u8 = self.0.try_into().expect("Fuchsia process return code bigger than 8 bits, but std::os::unix::ExitStatusExt::into_raw() was called to try to convert the value into a traditional Unix-style wait status, which cannot represent values greater than 255."); - let wait_status_as_if_unix = (exit_status_as_if_unix as c_int) << 8; - wait_status_as_if_unix - } -} - -/// 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 as i64) - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "exit code: {}", self.0) - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitStatusError(NonZero); - -impl Into for ExitStatusError { - fn into(self) -> ExitStatus { - ExitStatus(self.0.into()) - } -} - -impl ExitStatusError { - pub fn code(self) -> Option> { - // fixme: affected by the same bug as ExitStatus::code() - ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) - } -} diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs deleted file mode 100644 index 1f3abd4cc1286..0000000000000 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ /dev/null @@ -1,1251 +0,0 @@ -#[cfg(target_os = "vxworks")] -use libc::RTP_ID as pid_t; -#[cfg(not(target_os = "vxworks"))] -use libc::{c_int, pid_t}; -#[cfg(not(any( - target_os = "vxworks", - target_os = "l4re", - target_os = "tvos", - target_os = "watchos", -)))] -use libc::{gid_t, uid_t}; - -use crate::io::{self, Error, ErrorKind}; -use crate::num::NonZero; -use crate::sys::cvt; -#[cfg(target_os = "linux")] -use crate::sys::pal::unix::linux::pidfd::PidFd; -use crate::sys::process::process_common::*; -use crate::{fmt, mem, sys}; - -cfg_if::cfg_if! { - if #[cfg(target_os = "nto")] { - use crate::thread; - use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; - use crate::time::Duration; - use crate::sync::LazyLock; - // Get smallest amount of time we can sleep. - // Return a common value if it cannot be determined. - fn get_clock_resolution() -> Duration { - static MIN_DELAY: LazyLock Duration> = LazyLock::new(|| { - let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 }; - if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0 - { - Duration::from_nanos(mindelay.tv_nsec as u64) - } else { - Duration::from_millis(1) - } - }); - *MIN_DELAY - } - // Arbitrary minimum sleep duration for retrying fork/spawn - const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1); - // Maximum duration of sleeping before giving up and returning an error - const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -impl Command { - pub fn spawn( - &mut self, - default: Stdio, - needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX"; - - let envp = self.capture_env(); - - if self.saw_nul() { - return Err(io::const_error!( - ErrorKind::InvalidInput, - "nul byte found in provided data", - )); - } - - let (ours, theirs) = self.setup_io(default, needs_stdin)?; - - if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? { - return Ok((ret, ours)); - } - - #[cfg(target_os = "linux")] - let (input, output) = sys::net::Socket::new_pair(libc::AF_UNIX, libc::SOCK_SEQPACKET)?; - - #[cfg(not(target_os = "linux"))] - let (input, output) = sys::pipe::anon_pipe()?; - - // Whatever happens after the fork is almost for sure going to touch or - // look at the environment in one way or another (PATH in `execvp` or - // accessing the `environ` pointer ourselves). Make sure no other thread - // is accessing the environment when we do the fork itself. - // - // Note that as soon as we're done with the fork there's no need to hold - // a lock any more because the parent won't do anything and the child is - // in its own process. Thus the parent drops the lock guard immediately. - // The child calls `mem::forget` to leak the lock, which is crucial because - // releasing a lock is not async-signal-safe. - let env_lock = sys::os::env_read_lock(); - let pid = unsafe { self.do_fork()? }; - - if pid == 0 { - crate::panic::always_abort(); - mem::forget(env_lock); // avoid non-async-signal-safe unlocking - drop(input); - #[cfg(target_os = "linux")] - if self.get_create_pidfd() { - self.send_pidfd(&output); - } - let Err(err) = unsafe { self.do_exec(theirs, envp.as_ref()) }; - let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; - let errno = errno.to_be_bytes(); - let bytes = [ - errno[0], - errno[1], - errno[2], - errno[3], - CLOEXEC_MSG_FOOTER[0], - CLOEXEC_MSG_FOOTER[1], - CLOEXEC_MSG_FOOTER[2], - CLOEXEC_MSG_FOOTER[3], - ]; - // pipe I/O up to PIPE_BUF bytes should be atomic, and then - // we want to be sure we *don't* run at_exit destructors as - // we're being torn down regardless - rtassert!(output.write(&bytes).is_ok()); - unsafe { libc::_exit(1) } - } - - drop(env_lock); - drop(output); - - #[cfg(target_os = "linux")] - let pidfd = if self.get_create_pidfd() { self.recv_pidfd(&input) } else { -1 }; - - #[cfg(not(target_os = "linux"))] - let pidfd = -1; - - // Safety: We obtained the pidfd (on Linux) using SOCK_SEQPACKET, so it's valid. - let mut p = unsafe { Process::new(pid, pidfd) }; - let mut bytes = [0; 8]; - - // loop to handle EINTR - loop { - match input.read(&mut bytes) { - Ok(0) => return Ok((p, ours)), - Ok(8) => { - let (errno, footer) = bytes.split_at(4); - assert_eq!( - CLOEXEC_MSG_FOOTER, footer, - "Validation on the CLOEXEC pipe failed: {:?}", - bytes - ); - let errno = i32::from_be_bytes(errno.try_into().unwrap()); - assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); - return Err(Error::from_raw_os_error(errno)); - } - Err(ref e) if e.is_interrupted() => {} - Err(e) => { - assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); - panic!("the CLOEXEC pipe failed: {e:?}") - } - Ok(..) => { - // pipe I/O up to PIPE_BUF bytes should be atomic - // similarly SOCK_SEQPACKET messages should arrive whole - assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); - panic!("short read on the CLOEXEC pipe") - } - } - } - } - - pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; - crate::sys_common::process::wait_with_output(proc, pipes) - } - - // WatchOS and TVOS headers mark the `fork`/`exec*` functions with - // `__WATCHOS_PROHIBITED __TVOS_PROHIBITED`, and indicate that the - // `posix_spawn*` functions should be used instead. It isn't entirely clear - // what `PROHIBITED` means here (e.g. if calls to these functions are - // allowed to exist in dead code), but it sounds bad, so we go out of our - // way to avoid that all-together. - #[cfg(any(target_os = "tvos", target_os = "watchos"))] - const ERR_APPLE_TV_WATCH_NO_FORK_EXEC: Error = io::const_error!( - ErrorKind::Unsupported, - "`fork`+`exec`-based process spawning is not supported on this target", - ); - - #[cfg(any(target_os = "tvos", target_os = "watchos"))] - unsafe fn do_fork(&mut self) -> Result { - return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); - } - - // Attempts to fork the process. If successful, returns Ok((0, -1)) - // in the child, and Ok((child_pid, -1)) in the parent. - #[cfg(not(any(target_os = "watchos", target_os = "tvos", target_os = "nto")))] - unsafe fn do_fork(&mut self) -> Result { - cvt(libc::fork()) - } - - // On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened - // or closed a file descriptor while the fork() was occurring". - // Documentation says "... or try calling fork() again". This is what we do here. - // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html - #[cfg(target_os = "nto")] - unsafe fn do_fork(&mut self) -> Result { - use crate::sys::os::errno; - - let mut delay = MIN_FORKSPAWN_SLEEP; - - loop { - let r = libc::fork(); - if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF { - if delay < get_clock_resolution() { - // We cannot sleep this short (it would be longer). - // Yield instead. - thread::yield_now(); - } else if delay < MAX_FORKSPAWN_SLEEP { - thread::sleep(delay); - } else { - return Err(io::const_error!( - ErrorKind::WouldBlock, - "forking returned EBADF too often", - )); - } - delay *= 2; - continue; - } else { - return cvt(r); - } - } - } - - pub fn exec(&mut self, default: Stdio) -> io::Error { - let envp = self.capture_env(); - - if self.saw_nul() { - return io::const_error!(ErrorKind::InvalidInput, "nul byte found in provided data"); - } - - match self.setup_io(default, true) { - Ok((_, theirs)) => { - unsafe { - // Similar to when forking, we want to ensure that access to - // the environment is synchronized, so make sure to grab the - // environment lock before we try to exec. - let _lock = sys::os::env_read_lock(); - - let Err(e) = self.do_exec(theirs, envp.as_ref()); - e - } - } - Err(e) => e, - } - } - - // And at this point we've reached a special time in the life of the - // child. The child must now be considered hamstrung and unable to - // do anything other than syscalls really. Consider the following - // scenario: - // - // 1. Thread A of process 1 grabs the malloc() mutex - // 2. Thread B of process 1 forks(), creating thread C - // 3. Thread C of process 2 then attempts to malloc() - // 4. The memory of process 2 is the same as the memory of - // process 1, so the mutex is locked. - // - // This situation looks a lot like deadlock, right? It turns out - // that this is what pthread_atfork() takes care of, which is - // presumably implemented across platforms. The first thing that - // threads to *before* forking is to do things like grab the malloc - // mutex, and then after the fork they unlock it. - // - // Despite this information, libnative's spawn has been witnessed to - // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but - // all collected backtraces point at malloc/free traffic in the - // child spawned process. - // - // For this reason, the block of code below should contain 0 - // invocations of either malloc of free (or their related friends). - // - // As an example of not having malloc/free traffic, we don't close - // this file descriptor by dropping the FileDesc (which contains an - // allocation). Instead we just close it manually. This will never - // have the drop glue anyway because this code never returns (the - // child will either exec() or invoke libc::exit) - #[cfg(not(any(target_os = "tvos", target_os = "watchos")))] - unsafe fn do_exec( - &mut self, - stdio: ChildPipes, - maybe_envp: Option<&CStringArray>, - ) -> Result { - use crate::sys::{self, cvt_r}; - - if let Some(fd) = stdio.stdin.fd() { - cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?; - } - if let Some(fd) = stdio.stdout.fd() { - cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?; - } - if let Some(fd) = stdio.stderr.fd() { - cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?; - } - - #[cfg(not(target_os = "l4re"))] - { - if let Some(_g) = self.get_groups() { - //FIXME: Redox kernel does not support setgroups yet - #[cfg(not(target_os = "redox"))] - cvt(libc::setgroups(_g.len().try_into().unwrap(), _g.as_ptr()))?; - } - if let Some(u) = self.get_gid() { - cvt(libc::setgid(u as gid_t))?; - } - if let Some(u) = self.get_uid() { - // When dropping privileges from root, the `setgroups` call - // will remove any extraneous groups. We only drop groups - // if we have CAP_SETGID and we weren't given an explicit - // set of groups. If we don't call this, then even though our - // uid has dropped, we may still have groups that enable us to - // do super-user things. - //FIXME: Redox kernel does not support setgroups yet - #[cfg(not(target_os = "redox"))] - if self.get_groups().is_none() { - let res = cvt(libc::setgroups(0, crate::ptr::null())); - if let Err(e) = res { - // Here we ignore the case of not having CAP_SETGID. - // An alternative would be to require CAP_SETGID (in - // addition to CAP_SETUID) for setting the UID. - if e.raw_os_error() != Some(libc::EPERM) { - return Err(e.into()); - } - } - } - cvt(libc::setuid(u as uid_t))?; - } - } - if let Some(cwd) = self.get_cwd() { - cvt(libc::chdir(cwd.as_ptr()))?; - } - - if let Some(pgroup) = self.get_pgroup() { - cvt(libc::setpgid(0, pgroup))?; - } - - // emscripten has no signal support. - #[cfg(not(target_os = "emscripten"))] - { - // Inherit the signal mask from the parent rather than resetting it (i.e. do not call - // pthread_sigmask). - - // If -Zon-broken-pipe is used, don't reset SIGPIPE to SIG_DFL. - // If -Zon-broken-pipe is not used, reset SIGPIPE to SIG_DFL for backward compatibility. - // - // -Zon-broken-pipe is an opportunity to change the default here. - if !crate::sys::pal::on_broken_pipe_flag_used() { - #[cfg(target_os = "android")] // see issue #88585 - { - let mut action: libc::sigaction = mem::zeroed(); - action.sa_sigaction = libc::SIG_DFL; - cvt(libc::sigaction(libc::SIGPIPE, &action, crate::ptr::null_mut()))?; - } - #[cfg(not(target_os = "android"))] - { - let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL); - if ret == libc::SIG_ERR { - return Err(io::Error::last_os_error()); - } - } - #[cfg(target_os = "hurd")] - { - let ret = sys::signal(libc::SIGLOST, libc::SIG_DFL); - if ret == libc::SIG_ERR { - return Err(io::Error::last_os_error()); - } - } - } - } - - for callback in self.get_closures().iter_mut() { - callback()?; - } - - // Although we're performing an exec here we may also return with an - // error from this function (without actually exec'ing) in which case we - // want to be sure to restore the global environment back to what it - // once was, ensuring that our temporary override, when free'd, doesn't - // corrupt our process's environment. - let mut _reset = None; - if let Some(envp) = maybe_envp { - struct Reset(*const *const libc::c_char); - - impl Drop for Reset { - fn drop(&mut self) { - unsafe { - *sys::os::environ() = self.0; - } - } - } - - _reset = Some(Reset(*sys::os::environ())); - *sys::os::environ() = envp.as_ptr(); - } - - libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr()); - Err(io::Error::last_os_error()) - } - - #[cfg(any(target_os = "tvos", target_os = "watchos"))] - unsafe fn do_exec( - &mut self, - _stdio: ChildPipes, - _maybe_envp: Option<&CStringArray>, - ) -> Result { - return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); - } - - #[cfg(not(any( - target_os = "freebsd", - all(target_os = "linux", target_env = "gnu"), - all(target_os = "linux", target_env = "musl"), - target_os = "nto", - target_vendor = "apple", - )))] - fn posix_spawn( - &mut self, - _: &ChildPipes, - _: Option<&CStringArray>, - ) -> io::Result> { - Ok(None) - } - - // Only support platforms for which posix_spawn() can return ENOENT - // directly. - #[cfg(any( - target_os = "freebsd", - all(target_os = "linux", target_env = "gnu"), - all(target_os = "linux", target_env = "musl"), - target_os = "nto", - target_vendor = "apple", - ))] - fn posix_spawn( - &mut self, - stdio: &ChildPipes, - envp: Option<&CStringArray>, - ) -> io::Result> { - #[cfg(target_os = "linux")] - use core::sync::atomic::{AtomicU8, Ordering}; - - use crate::mem::MaybeUninit; - use crate::sys::{self, cvt_nz, on_broken_pipe_flag_used}; - - if self.get_gid().is_some() - || self.get_uid().is_some() - || (self.env_saw_path() && !self.program_is_path()) - || !self.get_closures().is_empty() - || self.get_groups().is_some() - { - return Ok(None); - } - - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - use crate::sys::weak::weak; - - weak! { - fn pidfd_spawnp( - *mut libc::c_int, - *const libc::c_char, - *const libc::posix_spawn_file_actions_t, - *const libc::posix_spawnattr_t, - *const *mut libc::c_char, - *const *mut libc::c_char - ) -> libc::c_int - } - - weak! { fn pidfd_getpid(libc::c_int) -> libc::c_int } - - static PIDFD_SUPPORTED: AtomicU8 = AtomicU8::new(0); - const UNKNOWN: u8 = 0; - const SPAWN: u8 = 1; - // Obtaining a pidfd via the fork+exec path might work - const FORK_EXEC: u8 = 2; - // Neither pidfd_spawn nor fork/exec will get us a pidfd. - // Instead we'll just posix_spawn if the other preconditions are met. - const NO: u8 = 3; - - if self.get_create_pidfd() { - let mut support = PIDFD_SUPPORTED.load(Ordering::Relaxed); - if support == FORK_EXEC { - return Ok(None); - } - if support == UNKNOWN { - support = NO; - let our_pid = crate::process::id(); - let pidfd = cvt(unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) } as c_int); - match pidfd { - Ok(pidfd) => { - support = FORK_EXEC; - if let Some(Ok(pid)) = pidfd_getpid.get().map(|f| cvt(unsafe { f(pidfd) } as i32)) { - if pidfd_spawnp.get().is_some() && pid as u32 == our_pid { - support = SPAWN - } - } - unsafe { libc::close(pidfd) }; - } - Err(e) if e.raw_os_error() == Some(libc::EMFILE) => { - // We're temporarily(?) out of file descriptors. In this case obtaining a pidfd would also fail - // Don't update the support flag so we can probe again later. - return Err(e) - } - _ => {} - } - PIDFD_SUPPORTED.store(support, Ordering::Relaxed); - if support == FORK_EXEC { - return Ok(None); - } - } - core::assert_matches::debug_assert_matches!(support, SPAWN | NO); - } - } else { - if self.get_create_pidfd() { - unreachable!("only implemented on linux") - } - } - } - - // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly. - #[cfg(all(target_os = "linux", target_env = "gnu"))] - { - if let Some(version) = sys::os::glibc_version() { - if version < (2, 24) { - return Ok(None); - } - } else { - return Ok(None); - } - } - - // On QNX Neutrino, posix_spawnp can fail with EBADF in case "another thread might have opened - // or closed a file descriptor while the posix_spawn() was occurring". - // Documentation says "... or try calling posix_spawn() again". This is what we do here. - // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html - #[cfg(target_os = "nto")] - unsafe fn retrying_libc_posix_spawnp( - pid: *mut pid_t, - file: *const c_char, - file_actions: *const posix_spawn_file_actions_t, - attrp: *const posix_spawnattr_t, - argv: *const *mut c_char, - envp: *const *mut c_char, - ) -> io::Result { - let mut delay = MIN_FORKSPAWN_SLEEP; - loop { - match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) { - libc::EBADF => { - if delay < get_clock_resolution() { - // We cannot sleep this short (it would be longer). - // Yield instead. - thread::yield_now(); - } else if delay < MAX_FORKSPAWN_SLEEP { - thread::sleep(delay); - } else { - return Err(io::const_error!( - ErrorKind::WouldBlock, - "posix_spawnp returned EBADF too often", - )); - } - delay *= 2; - continue; - } - r => { - return Ok(r); - } - } - } - } - - type PosixSpawnAddChdirFn = unsafe extern "C" fn( - *mut libc::posix_spawn_file_actions_t, - *const libc::c_char, - ) -> libc::c_int; - - /// Get the function pointer for adding a chdir action to a - /// `posix_spawn_file_actions_t`, if available, assuming a dynamic libc. - /// - /// Some platforms can set a new working directory for a spawned process in the - /// `posix_spawn` path. This function looks up the function pointer for adding - /// such an action to a `posix_spawn_file_actions_t` struct. - #[cfg(not(all(target_os = "linux", target_env = "musl")))] - fn get_posix_spawn_addchdir() -> Option { - use crate::sys::weak::weak; - - weak! { - fn posix_spawn_file_actions_addchdir_np( - *mut libc::posix_spawn_file_actions_t, - *const libc::c_char - ) -> libc::c_int - } - - posix_spawn_file_actions_addchdir_np.get() - } - - /// Get the function pointer for adding a chdir action to a - /// `posix_spawn_file_actions_t`, if available, on platforms where the function - /// is known to exist. - /// - /// Weak symbol lookup doesn't work with statically linked libcs, so in cases - /// where static linking is possible we need to either check for the presence - /// of the symbol at compile time or know about it upfront. - #[cfg(all(target_os = "linux", target_env = "musl"))] - fn get_posix_spawn_addchdir() -> Option { - // Our minimum required musl supports this function, so we can just use it. - Some(libc::posix_spawn_file_actions_addchdir_np) - } - - let addchdir = match self.get_cwd() { - Some(cwd) => { - if cfg!(target_vendor = "apple") { - // There is a bug in macOS where a relative executable - // path like "../myprogram" will cause `posix_spawn` to - // successfully launch the program, but erroneously return - // ENOENT when used with posix_spawn_file_actions_addchdir_np - // which was introduced in macOS 10.15. - if self.get_program_kind() == ProgramKind::Relative { - return Ok(None); - } - } - // Check for the availability of the posix_spawn addchdir - // function now. If it isn't available, bail and use the - // fork/exec path. - match get_posix_spawn_addchdir() { - Some(f) => Some((f, cwd)), - None => return Ok(None), - } - } - None => None, - }; - - let pgroup = self.get_pgroup(); - - struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit); - - impl Drop for PosixSpawnFileActions<'_> { - fn drop(&mut self) { - unsafe { - libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr()); - } - } - } - - struct PosixSpawnattr<'a>(&'a mut MaybeUninit); - - impl Drop for PosixSpawnattr<'_> { - fn drop(&mut self) { - unsafe { - libc::posix_spawnattr_destroy(self.0.as_mut_ptr()); - } - } - } - - unsafe { - let mut attrs = MaybeUninit::uninit(); - cvt_nz(libc::posix_spawnattr_init(attrs.as_mut_ptr()))?; - let attrs = PosixSpawnattr(&mut attrs); - - let mut flags = 0; - - let mut file_actions = MaybeUninit::uninit(); - cvt_nz(libc::posix_spawn_file_actions_init(file_actions.as_mut_ptr()))?; - let file_actions = PosixSpawnFileActions(&mut file_actions); - - if let Some(fd) = stdio.stdin.fd() { - cvt_nz(libc::posix_spawn_file_actions_adddup2( - file_actions.0.as_mut_ptr(), - fd, - libc::STDIN_FILENO, - ))?; - } - if let Some(fd) = stdio.stdout.fd() { - cvt_nz(libc::posix_spawn_file_actions_adddup2( - file_actions.0.as_mut_ptr(), - fd, - libc::STDOUT_FILENO, - ))?; - } - if let Some(fd) = stdio.stderr.fd() { - cvt_nz(libc::posix_spawn_file_actions_adddup2( - file_actions.0.as_mut_ptr(), - fd, - libc::STDERR_FILENO, - ))?; - } - if let Some((f, cwd)) = addchdir { - cvt_nz(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?; - } - - if let Some(pgroup) = pgroup { - flags |= libc::POSIX_SPAWN_SETPGROUP; - cvt_nz(libc::posix_spawnattr_setpgroup(attrs.0.as_mut_ptr(), pgroup))?; - } - - // Inherit the signal mask from this process rather than resetting it (i.e. do not call - // posix_spawnattr_setsigmask). - - // If -Zon-broken-pipe is used, don't reset SIGPIPE to SIG_DFL. - // If -Zon-broken-pipe is not used, reset SIGPIPE to SIG_DFL for backward compatibility. - // - // -Zon-broken-pipe is an opportunity to change the default here. - if !on_broken_pipe_flag_used() { - let mut default_set = MaybeUninit::::uninit(); - cvt(sigemptyset(default_set.as_mut_ptr()))?; - cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGPIPE))?; - #[cfg(target_os = "hurd")] - { - cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGLOST))?; - } - cvt_nz(libc::posix_spawnattr_setsigdefault( - attrs.0.as_mut_ptr(), - default_set.as_ptr(), - ))?; - flags |= libc::POSIX_SPAWN_SETSIGDEF; - } - - cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; - - // Make sure we synchronize access to the global `environ` resource - let _env_lock = sys::os::env_read_lock(); - let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); - - #[cfg(not(target_os = "nto"))] - let spawn_fn = libc::posix_spawnp; - #[cfg(target_os = "nto")] - let spawn_fn = retrying_libc_posix_spawnp; - - #[cfg(target_os = "linux")] - if self.get_create_pidfd() && PIDFD_SUPPORTED.load(Ordering::Relaxed) == SPAWN { - let mut pidfd: libc::c_int = -1; - let spawn_res = pidfd_spawnp.get().unwrap()( - &mut pidfd, - self.get_program_cstr().as_ptr(), - file_actions.0.as_ptr(), - attrs.0.as_ptr(), - self.get_argv().as_ptr() as *const _, - envp as *const _, - ); - - let spawn_res = cvt_nz(spawn_res); - if let Err(ref e) = spawn_res - && e.raw_os_error() == Some(libc::ENOSYS) - { - PIDFD_SUPPORTED.store(FORK_EXEC, Ordering::Relaxed); - return Ok(None); - } - spawn_res?; - - let pid = match cvt(pidfd_getpid.get().unwrap()(pidfd)) { - Ok(pid) => pid, - Err(e) => { - // The child has been spawned and we are holding its pidfd. - // But we cannot obtain its pid even though pidfd_getpid support was verified earlier. - // This might happen if libc can't open procfs because the file descriptor limit has been reached. - libc::close(pidfd); - return Err(Error::new( - e.kind(), - "pidfd_spawnp succeeded but the child's PID could not be obtained", - )); - } - }; - - return Ok(Some(Process::new(pid, pidfd))); - } - - // Safety: -1 indicates we don't have a pidfd. - let mut p = Process::new(0, -1); - - let spawn_res = spawn_fn( - &mut p.pid, - self.get_program_cstr().as_ptr(), - file_actions.0.as_ptr(), - attrs.0.as_ptr(), - self.get_argv().as_ptr() as *const _, - envp as *const _, - ); - - #[cfg(target_os = "nto")] - let spawn_res = spawn_res?; - - cvt_nz(spawn_res)?; - Ok(Some(p)) - } - } - - #[cfg(target_os = "linux")] - fn send_pidfd(&self, sock: &crate::sys::net::Socket) { - use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; - - use crate::io::IoSlice; - use crate::os::fd::RawFd; - use crate::sys::cvt_r; - - unsafe { - let child_pid = libc::getpid(); - // pidfd_open sets CLOEXEC by default - let pidfd = libc::syscall(libc::SYS_pidfd_open, child_pid, 0); - - let fds: [c_int; 1] = [pidfd as RawFd]; - - const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>(); - - #[repr(C)] - union Cmsg { - buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }], - _align: libc::cmsghdr, - } - - let mut cmsg: Cmsg = mem::zeroed(); - - // 0-length message to send through the socket so we can pass along the fd - let mut iov = [IoSlice::new(b"")]; - let mut msg: libc::msghdr = mem::zeroed(); - - msg.msg_iov = (&raw mut iov) as *mut _; - msg.msg_iovlen = 1; - - // only attach cmsg if we successfully acquired the pidfd - if pidfd >= 0 { - msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _; - msg.msg_control = (&raw mut cmsg.buf) as *mut _; - - let hdr = CMSG_FIRSTHDR((&raw mut msg) as *mut _); - (*hdr).cmsg_level = SOL_SOCKET; - (*hdr).cmsg_type = SCM_RIGHTS; - (*hdr).cmsg_len = CMSG_LEN(SCM_MSG_LEN as _) as _; - let data = CMSG_DATA(hdr); - crate::ptr::copy_nonoverlapping( - fds.as_ptr().cast::(), - data as *mut _, - SCM_MSG_LEN, - ); - } - - // we send the 0-length message even if we failed to acquire the pidfd - // so we get a consistent SEQPACKET order - match cvt_r(|| libc::sendmsg(sock.as_raw(), &msg, 0)) { - Ok(0) => {} - other => rtabort!("failed to communicate with parent process. {:?}", other), - } - } - } - - #[cfg(target_os = "linux")] - fn recv_pidfd(&self, sock: &crate::sys::net::Socket) -> pid_t { - use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; - - use crate::io::IoSliceMut; - use crate::sys::cvt_r; - - unsafe { - const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>(); - - #[repr(C)] - union Cmsg { - _buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }], - _align: libc::cmsghdr, - } - let mut cmsg: Cmsg = mem::zeroed(); - // 0-length read to get the fd - let mut iov = [IoSliceMut::new(&mut [])]; - - let mut msg: libc::msghdr = mem::zeroed(); - - msg.msg_iov = (&raw mut iov) as *mut _; - msg.msg_iovlen = 1; - msg.msg_controllen = mem::size_of::() as _; - msg.msg_control = (&raw mut cmsg) as *mut _; - - match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, libc::MSG_CMSG_CLOEXEC)) { - Err(_) => return -1, - Ok(_) => {} - } - - let hdr = CMSG_FIRSTHDR((&raw mut msg) as *mut _); - if hdr.is_null() - || (*hdr).cmsg_level != SOL_SOCKET - || (*hdr).cmsg_type != SCM_RIGHTS - || (*hdr).cmsg_len != CMSG_LEN(SCM_MSG_LEN as _) as _ - { - return -1; - } - let data = CMSG_DATA(hdr); - - let mut fds = [-1 as c_int]; - - crate::ptr::copy_nonoverlapping( - data as *const _, - fds.as_mut_ptr().cast::(), - SCM_MSG_LEN, - ); - - fds[0] - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Processes -//////////////////////////////////////////////////////////////////////////////// - -/// The unique ID of the process (this should never be negative). -pub struct Process { - pid: pid_t, - status: Option, - // On Linux, stores the pidfd created for this child. - // This is None if the user did not request pidfd creation, - // or if the pidfd could not be created for some reason - // (e.g. the `pidfd_open` syscall was not available). - #[cfg(target_os = "linux")] - pidfd: Option, -} - -impl Process { - #[cfg(target_os = "linux")] - /// # Safety - /// - /// `pidfd` must either be -1 (representing no file descriptor) or a valid, exclusively owned file - /// descriptor (See [I/O Safety]). - /// - /// [I/O Safety]: crate::io#io-safety - unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self { - use crate::os::unix::io::FromRawFd; - use crate::sys_common::FromInner; - // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned. - let pidfd = (pidfd >= 0).then(|| PidFd::from_inner(sys::fd::FileDesc::from_raw_fd(pidfd))); - Process { pid, status: None, pidfd } - } - - #[cfg(not(target_os = "linux"))] - unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self { - Process { pid, status: None } - } - - pub fn id(&self) -> u32 { - self.pid as u32 - } - - pub fn kill(&mut self) -> io::Result<()> { - // If we've already waited on this process then the pid can be recycled - // and used for another process, and we probably shouldn't be killing - // random processes, so return Ok because the process has exited already. - if self.status.is_some() { - return Ok(()); - } - #[cfg(target_os = "linux")] - if let Some(pid_fd) = self.pidfd.as_ref() { - // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too - return pid_fd.kill(); - } - cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) - } - - pub fn wait(&mut self) -> io::Result { - use crate::sys::cvt_r; - if let Some(status) = self.status { - return Ok(status); - } - #[cfg(target_os = "linux")] - if let Some(pid_fd) = self.pidfd.as_ref() { - let status = pid_fd.wait()?; - self.status = Some(status); - return Ok(status); - } - let mut status = 0 as c_int; - cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; - self.status = Some(ExitStatus::new(status)); - Ok(ExitStatus::new(status)) - } - - pub fn try_wait(&mut self) -> io::Result> { - if let Some(status) = self.status { - return Ok(Some(status)); - } - #[cfg(target_os = "linux")] - if let Some(pid_fd) = self.pidfd.as_ref() { - let status = pid_fd.try_wait()?; - if let Some(status) = status { - self.status = Some(status) - } - return Ok(status); - } - let mut status = 0 as c_int; - let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?; - if pid == 0 { - Ok(None) - } else { - self.status = Some(ExitStatus::new(status)); - Ok(Some(ExitStatus::new(status))) - } - } -} - -/// Unix exit statuses -// -// This is not actually an "exit status" in Unix terminology. Rather, it is a "wait status". -// See the discussion in comments and doc comments for `std::process::ExitStatus`. -#[derive(PartialEq, Eq, Clone, Copy, Default)] -pub struct ExitStatus(c_int); - -impl fmt::Debug for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("unix_wait_status").field(&self.0).finish() - } -} - -impl ExitStatus { - pub fn new(status: c_int) -> ExitStatus { - ExitStatus(status) - } - - #[cfg(target_os = "linux")] - pub fn from_waitid_siginfo(siginfo: libc::siginfo_t) -> ExitStatus { - let status = unsafe { siginfo.si_status() }; - - match siginfo.si_code { - libc::CLD_EXITED => ExitStatus((status & 0xff) << 8), - libc::CLD_KILLED => ExitStatus(status), - libc::CLD_DUMPED => ExitStatus(status | 0x80), - libc::CLD_CONTINUED => ExitStatus(0xffff), - libc::CLD_STOPPED | libc::CLD_TRAPPED => ExitStatus(((status & 0xff) << 8) | 0x7f), - _ => unreachable!("waitid() should only return the above codes"), - } - } - - fn exited(&self) -> bool { - libc::WIFEXITED(self.0) - } - - pub fn exit_ok(&self) -> Result<(), ExitStatusError> { - // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is - // true on all actual versions of Unix, is widely assumed, and is specified in SuS - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not - // true for a platform pretending to be Unix, the tests (our doctests, and also - // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. - match NonZero::try_from(self.0) { - /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), - /* was zero, couldn't convert */ Err(_) => Ok(()), - } - } - - pub fn code(&self) -> Option { - self.exited().then(|| libc::WEXITSTATUS(self.0)) - } - - pub fn signal(&self) -> Option { - libc::WIFSIGNALED(self.0).then(|| libc::WTERMSIG(self.0)) - } - - pub fn core_dumped(&self) -> bool { - libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0) - } - - pub fn stopped_signal(&self) -> Option { - libc::WIFSTOPPED(self.0).then(|| libc::WSTOPSIG(self.0)) - } - - pub fn continued(&self) -> bool { - libc::WIFCONTINUED(self.0) - } - - pub fn into_raw(&self) -> c_int { - self.0 - } -} - -/// 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) - } -} - -/// Converts a signal number to a readable, searchable name. -/// -/// This string should be displayed right after the signal number. -/// If a signal is unrecognized, it returns the empty string, so that -/// you just get the number like "0". If it is recognized, you'll get -/// something like "9 (SIGKILL)". -fn signal_string(signal: i32) -> &'static str { - match signal { - libc::SIGHUP => " (SIGHUP)", - libc::SIGINT => " (SIGINT)", - libc::SIGQUIT => " (SIGQUIT)", - libc::SIGILL => " (SIGILL)", - libc::SIGTRAP => " (SIGTRAP)", - libc::SIGABRT => " (SIGABRT)", - #[cfg(not(target_os = "l4re"))] - libc::SIGBUS => " (SIGBUS)", - libc::SIGFPE => " (SIGFPE)", - libc::SIGKILL => " (SIGKILL)", - #[cfg(not(target_os = "l4re"))] - libc::SIGUSR1 => " (SIGUSR1)", - libc::SIGSEGV => " (SIGSEGV)", - #[cfg(not(target_os = "l4re"))] - libc::SIGUSR2 => " (SIGUSR2)", - libc::SIGPIPE => " (SIGPIPE)", - libc::SIGALRM => " (SIGALRM)", - libc::SIGTERM => " (SIGTERM)", - #[cfg(not(target_os = "l4re"))] - libc::SIGCHLD => " (SIGCHLD)", - #[cfg(not(target_os = "l4re"))] - libc::SIGCONT => " (SIGCONT)", - #[cfg(not(target_os = "l4re"))] - libc::SIGSTOP => " (SIGSTOP)", - #[cfg(not(target_os = "l4re"))] - libc::SIGTSTP => " (SIGTSTP)", - #[cfg(not(target_os = "l4re"))] - libc::SIGTTIN => " (SIGTTIN)", - #[cfg(not(target_os = "l4re"))] - libc::SIGTTOU => " (SIGTTOU)", - #[cfg(not(target_os = "l4re"))] - libc::SIGURG => " (SIGURG)", - #[cfg(not(target_os = "l4re"))] - libc::SIGXCPU => " (SIGXCPU)", - #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] - libc::SIGXFSZ => " (SIGXFSZ)", - #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] - libc::SIGVTALRM => " (SIGVTALRM)", - #[cfg(not(target_os = "l4re"))] - libc::SIGPROF => " (SIGPROF)", - #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] - libc::SIGWINCH => " (SIGWINCH)", - #[cfg(not(any(target_os = "haiku", target_os = "l4re")))] - libc::SIGIO => " (SIGIO)", - #[cfg(target_os = "haiku")] - libc::SIGPOLL => " (SIGPOLL)", - #[cfg(not(target_os = "l4re"))] - libc::SIGSYS => " (SIGSYS)", - // For information on Linux signals, run `man 7 signal` - #[cfg(all( - target_os = "linux", - any( - target_arch = "x86_64", - target_arch = "x86", - target_arch = "arm", - target_arch = "aarch64" - ) - ))] - libc::SIGSTKFLT => " (SIGSTKFLT)", - #[cfg(any(target_os = "linux", target_os = "nto"))] - libc::SIGPWR => " (SIGPWR)", - #[cfg(any( - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "dragonfly", - target_os = "nto", - target_vendor = "apple", - ))] - libc::SIGEMT => " (SIGEMT)", - #[cfg(any( - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "dragonfly", - target_vendor = "apple", - ))] - libc::SIGINFO => " (SIGINFO)", - #[cfg(target_os = "hurd")] - libc::SIGLOST => " (SIGLOST)", - #[cfg(target_os = "freebsd")] - libc::SIGTHR => " (SIGTHR)", - #[cfg(target_os = "freebsd")] - libc::SIGLIBRT => " (SIGLIBRT)", - _ => "", - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(code) = self.code() { - write!(f, "exit status: {code}") - } else if let Some(signal) = self.signal() { - let signal_string = signal_string(signal); - if self.core_dumped() { - write!(f, "signal: {signal}{signal_string} (core dumped)") - } else { - write!(f, "signal: {signal}{signal_string}") - } - } else if let Some(signal) = self.stopped_signal() { - let signal_string = signal_string(signal); - write!(f, "stopped (not terminated) by signal: {signal}{signal_string}") - } else if self.continued() { - write!(f, "continued (WIFCONTINUED)") - } else { - write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0) - } - } -} - -#[derive(PartialEq, Eq, Clone, Copy)] -pub struct ExitStatusError(NonZero); - -impl Into for ExitStatusError { - fn into(self) -> ExitStatus { - ExitStatus(self.0.into()) - } -} - -impl fmt::Debug for ExitStatusError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("unix_wait_status").field(&self.0).finish() - } -} - -impl ExitStatusError { - pub fn code(self) -> Option> { - ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) - } -} - -#[cfg(target_os = "linux")] -mod linux_child_ext { - - use crate::os::linux::process as os; - use crate::sys::pal::unix::ErrorKind; - use crate::sys::pal::unix::linux::pidfd as imp; - use crate::sys_common::FromInner; - use crate::{io, mem}; - - #[unstable(feature = "linux_pidfd", issue = "82971")] - impl crate::os::linux::process::ChildExt for crate::process::Child { - fn pidfd(&self) -> io::Result<&os::PidFd> { - self.handle - .pidfd - .as_ref() - // SAFETY: The os type is a transparent wrapper, therefore we can transmute references - .map(|fd| unsafe { mem::transmute::<&imp::PidFd, &os::PidFd>(fd) }) - .ok_or_else(|| io::const_error!(ErrorKind::Uncategorized, "no pidfd was created.")) - } - - fn into_pidfd(mut self) -> Result { - self.handle - .pidfd - .take() - .map(|fd| >::from_inner(fd)) - .ok_or_else(|| self) - } - } -} - -#[cfg(test)] -#[path = "process_unix/tests.rs"] -mod tests; - -// See [`process_unsupported_wait_status::compare_with_linux`]; -#[cfg(all(test, target_os = "linux"))] -#[path = "process_unsupported/wait_status.rs"] -mod process_unsupported_wait_status; diff --git a/library/std/src/sys/pal/unix/process/process_unsupported.rs b/library/std/src/sys/pal/unix/process/process_unsupported.rs deleted file mode 100644 index c58548835ff3d..0000000000000 --- a/library/std/src/sys/pal/unix/process/process_unsupported.rs +++ /dev/null @@ -1,72 +0,0 @@ -use libc::{c_int, pid_t}; - -use crate::io; -use crate::num::NonZero; -use crate::sys::pal::unix::unsupported::*; -use crate::sys::process::process_common::*; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -impl Command { - pub fn spawn( - &mut self, - _default: Stdio, - _needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - unsupported() - } - - pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - unsupported() - } - - pub fn exec(&mut self, _default: Stdio) -> io::Error { - unsupported_err() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Processes -//////////////////////////////////////////////////////////////////////////////// - -pub struct Process { - _handle: pid_t, -} - -impl Process { - pub fn id(&self) -> u32 { - 0 - } - - pub fn kill(&mut self) -> io::Result<()> { - unsupported() - } - - pub fn wait(&mut self) -> io::Result { - unsupported() - } - - pub fn try_wait(&mut self) -> io::Result> { - unsupported() - } -} - -mod wait_status; -pub use wait_status::ExitStatus; - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitStatusError(NonZero); - -impl Into for ExitStatusError { - fn into(self) -> ExitStatus { - ExitStatus::from(c_int::from(self.0)) - } -} - -impl ExitStatusError { - pub fn code(self) -> Option> { - ExitStatus::from(c_int::from(self.0)).code().map(|st| st.try_into().unwrap()) - } -} diff --git a/library/std/src/sys/pal/unix/process/process_unsupported/wait_status/tests.rs b/library/std/src/sys/pal/unix/process/process_unsupported/wait_status/tests.rs deleted file mode 100644 index 5132eab10a112..0000000000000 --- a/library/std/src/sys/pal/unix/process/process_unsupported/wait_status/tests.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Note that tests in this file are run on Linux as well as on platforms using process_unsupported - -// Test that our emulation exactly matches Linux -// -// This test runs *on Linux* but it tests -// the implementation used on non-Unix `#[cfg(unix)]` platforms. -// -// I.e. we're using Linux as a proxy for "trad unix". -#[cfg(target_os = "linux")] -#[test] -fn compare_with_linux() { - use super::ExitStatus as Emulated; - use crate::os::unix::process::ExitStatusExt as _; - use crate::process::ExitStatus as Real; - - // Check that we handle out-of-range values similarly, too. - for wstatus in -0xf_ffff..0xf_ffff { - let emulated = Emulated::from(wstatus); - let real = Real::from_raw(wstatus); - - macro_rules! compare { { $method:ident } => { - assert_eq!( - emulated.$method(), - real.$method(), - "{wstatus:#x}.{}()", - stringify!($method), - ); - } } - compare!(code); - compare!(signal); - compare!(core_dumped); - compare!(stopped_signal); - compare!(continued); - compare!(into_raw); - } -} diff --git a/library/std/src/sys/pal/unix/process/process_vxworks.rs b/library/std/src/sys/pal/unix/process/process_vxworks.rs deleted file mode 100644 index e2c1b6a032624..0000000000000 --- a/library/std/src/sys/pal/unix/process/process_vxworks.rs +++ /dev/null @@ -1,268 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] -use libc::{self, RTP_ID, c_char, c_int}; - -use crate::io::{self, ErrorKind}; -use crate::num::NonZero; -use crate::sys::cvt; -use crate::sys::pal::unix::thread; -use crate::sys::process::process_common::*; -use crate::{fmt, sys}; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -impl Command { - pub fn spawn( - &mut self, - default: Stdio, - needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - use crate::sys::cvt_r; - let envp = self.capture_env(); - - if self.saw_nul() { - return Err(io::const_error!( - ErrorKind::InvalidInput, - "nul byte found in provided data", - )); - } - let (ours, theirs) = self.setup_io(default, needs_stdin)?; - let mut p = Process { pid: 0, status: None }; - - unsafe { - macro_rules! t { - ($e:expr) => { - match $e { - Ok(e) => e, - Err(e) => return Err(e.into()), - } - }; - } - - let mut orig_stdin = libc::STDIN_FILENO; - let mut orig_stdout = libc::STDOUT_FILENO; - let mut orig_stderr = libc::STDERR_FILENO; - - if let Some(fd) = theirs.stdin.fd() { - orig_stdin = t!(cvt_r(|| libc::dup(libc::STDIN_FILENO))); - t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))); - } - if let Some(fd) = theirs.stdout.fd() { - orig_stdout = t!(cvt_r(|| libc::dup(libc::STDOUT_FILENO))); - t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))); - } - if let Some(fd) = theirs.stderr.fd() { - orig_stderr = t!(cvt_r(|| libc::dup(libc::STDERR_FILENO))); - t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))); - } - - if let Some(cwd) = self.get_cwd() { - t!(cvt(libc::chdir(cwd.as_ptr()))); - } - - // pre_exec closures are ignored on VxWorks - let _ = self.get_closures(); - - let c_envp = envp - .as_ref() - .map(|c| c.as_ptr()) - .unwrap_or_else(|| *sys::os::environ() as *const _); - let stack_size = crate::cmp::max( - crate::env::var_os("RUST_MIN_STACK") - .and_then(|s| s.to_str().and_then(|s| s.parse().ok())) - .unwrap_or(thread::DEFAULT_MIN_STACK_SIZE), - libc::PTHREAD_STACK_MIN, - ); - - // ensure that access to the environment is synchronized - let _lock = sys::os::env_read_lock(); - - let ret = libc::rtpSpawn( - self.get_program_cstr().as_ptr(), - self.get_argv().as_ptr() as *mut *const c_char, // argv - c_envp as *mut *const c_char, - 100 as c_int, // initial priority - stack_size, // initial stack size. - 0, // options - 0, // task options - ); - - // Because FileDesc was not used, each duplicated file descriptor - // needs to be closed manually - if orig_stdin != libc::STDIN_FILENO { - t!(cvt_r(|| libc::dup2(orig_stdin, libc::STDIN_FILENO))); - libc::close(orig_stdin); - } - if orig_stdout != libc::STDOUT_FILENO { - t!(cvt_r(|| libc::dup2(orig_stdout, libc::STDOUT_FILENO))); - libc::close(orig_stdout); - } - if orig_stderr != libc::STDERR_FILENO { - t!(cvt_r(|| libc::dup2(orig_stderr, libc::STDERR_FILENO))); - libc::close(orig_stderr); - } - - if ret != libc::RTP_ID_ERROR { - p.pid = ret; - Ok((p, ours)) - } else { - Err(io::Error::last_os_error()) - } - } - } - - pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; - crate::sys_common::process::wait_with_output(proc, pipes) - } - - pub fn exec(&mut self, default: Stdio) -> io::Error { - let ret = Command::spawn(self, default, false); - match ret { - Ok(t) => unsafe { - let mut status = 0 as c_int; - libc::waitpid(t.0.pid, &mut status, 0); - libc::exit(0); - }, - Err(e) => e, - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Processes -//////////////////////////////////////////////////////////////////////////////// - -/// The unique id of the process (this should never be negative). -pub struct Process { - pid: RTP_ID, - status: Option, -} - -impl Process { - pub fn id(&self) -> u32 { - self.pid as u32 - } - - pub fn kill(&mut self) -> io::Result<()> { - // If we've already waited on this process then the pid can be recycled - // and used for another process, and we probably shouldn't be killing - // random processes, so return Ok because the process has exited already. - if self.status.is_some() { - Ok(()) - } else { - cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) - } - } - - pub fn wait(&mut self) -> io::Result { - use crate::sys::cvt_r; - if let Some(status) = self.status { - return Ok(status); - } - let mut status = 0 as c_int; - cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; - self.status = Some(ExitStatus::new(status)); - Ok(ExitStatus::new(status)) - } - - pub fn try_wait(&mut self) -> io::Result> { - if let Some(status) = self.status { - return Ok(Some(status)); - } - let mut status = 0 as c_int; - let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?; - if pid == 0 { - Ok(None) - } else { - self.status = Some(ExitStatus::new(status)); - Ok(Some(ExitStatus::new(status))) - } - } -} - -/// Unix exit statuses -#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] -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 exit_ok(&self) -> Result<(), ExitStatusError> { - // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is - // true on all actual versions of Unix, is widely assumed, and is specified in SuS - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not - // true for a platform pretending to be Unix, the tests (our doctests, and also - // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. - match NonZero::try_from(self.0) { - Ok(failure) => Err(ExitStatusError(failure)), - Err(_) => Ok(()), - } - } - - 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 } - } - - pub fn core_dumped(&self) -> bool { - // This method is not yet properly implemented on VxWorks - false - } - - pub fn stopped_signal(&self) -> Option { - if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None } - } - - pub fn continued(&self) -> bool { - // This method is not yet properly implemented on VxWorks - false - } - - pub fn into_raw(&self) -> c_int { - self.0 - } -} - -/// 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 ExitStatusError(NonZero); - -impl Into for ExitStatusError { - fn into(self) -> ExitStatus { - ExitStatus(self.0.into()) - } -} - -impl ExitStatusError { - pub fn code(self) -> Option> { - ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) - } -} diff --git a/library/std/src/sys/pal/unix/process/zircon.rs b/library/std/src/sys/pal/unix/process/zircon.rs deleted file mode 100644 index 7932bd26d76c3..0000000000000 --- a/library/std/src/sys/pal/unix/process/zircon.rs +++ /dev/null @@ -1,309 +0,0 @@ -#![allow(non_camel_case_types, unused)] - -use libc::{c_int, c_void, size_t}; - -use crate::io; -use crate::mem::MaybeUninit; -use crate::os::raw::c_char; - -pub type zx_handle_t = u32; -pub type zx_vaddr_t = usize; -pub type zx_rights_t = u32; -pub type zx_status_t = i32; - -pub const ZX_HANDLE_INVALID: zx_handle_t = 0; - -pub type zx_time_t = i64; -pub const ZX_TIME_INFINITE: zx_time_t = i64::MAX; - -pub type zx_signals_t = u32; - -pub const ZX_OBJECT_SIGNAL_3: zx_signals_t = 1 << 3; - -pub const ZX_TASK_TERMINATED: zx_signals_t = ZX_OBJECT_SIGNAL_3; - -pub const ZX_RIGHT_SAME_RIGHTS: zx_rights_t = 1 << 31; - -// The upper four bits gives the minor version. -pub type zx_object_info_topic_t = u32; - -pub const ZX_INFO_PROCESS: zx_object_info_topic_t = 3 | (1 << 28); - -pub type zx_info_process_flags_t = u32; - -pub fn zx_cvt(t: T) -> io::Result -where - T: TryInto + Copy, -{ - if let Ok(status) = TryInto::try_into(t) { - if status < 0 { Err(io::Error::from_raw_os_error(status)) } else { Ok(t) } - } else { - Err(io::Error::last_os_error()) - } -} - -// Safe wrapper around zx_handle_t -pub struct Handle { - raw: zx_handle_t, -} - -impl Handle { - pub fn new(raw: zx_handle_t) -> Handle { - Handle { raw } - } - - pub fn raw(&self) -> zx_handle_t { - self.raw - } -} - -impl Drop for Handle { - fn drop(&mut self) { - unsafe { - zx_cvt(zx_handle_close(self.raw)).expect("Failed to close zx_handle_t"); - } - } -} - -// Returned for topic ZX_INFO_PROCESS -#[derive(Default)] -#[repr(C)] -pub struct zx_info_process_t { - pub return_code: i64, - pub start_time: zx_time_t, - pub flags: zx_info_process_flags_t, - pub reserved1: u32, -} - -unsafe extern "C" { - pub fn zx_job_default() -> zx_handle_t; - - pub fn zx_task_kill(handle: zx_handle_t) -> zx_status_t; - - pub fn zx_handle_close(handle: zx_handle_t) -> zx_status_t; - - pub fn zx_handle_duplicate( - handle: zx_handle_t, - rights: zx_rights_t, - out: *const zx_handle_t, - ) -> zx_handle_t; - - pub fn zx_object_wait_one( - handle: zx_handle_t, - signals: zx_signals_t, - timeout: zx_time_t, - pending: *mut zx_signals_t, - ) -> zx_status_t; - - pub fn zx_object_get_info( - handle: zx_handle_t, - topic: u32, - buffer: *mut c_void, - buffer_size: size_t, - actual_size: *mut size_t, - avail: *mut size_t, - ) -> zx_status_t; -} - -#[derive(Default)] -#[repr(C)] -pub struct fdio_spawn_action_t { - pub action: u32, - pub reserved0: u32, - pub local_fd: i32, - pub target_fd: i32, - pub reserved1: u64, -} - -unsafe extern "C" { - pub fn fdio_spawn_etc( - job: zx_handle_t, - flags: u32, - path: *const c_char, - argv: *const *const c_char, - envp: *const *const c_char, - action_count: size_t, - actions: *const fdio_spawn_action_t, - process: *mut zx_handle_t, - err_msg: *mut c_char, - ) -> zx_status_t; - - pub fn fdio_fd_clone(fd: c_int, out_handle: *mut zx_handle_t) -> zx_status_t; - pub fn fdio_fd_create(handle: zx_handle_t, fd: *mut c_int) -> zx_status_t; -} - -// fdio_spawn_etc flags - -pub const FDIO_SPAWN_CLONE_JOB: u32 = 0x0001; -pub const FDIO_SPAWN_CLONE_LDSVC: u32 = 0x0002; -pub const FDIO_SPAWN_CLONE_NAMESPACE: u32 = 0x0004; -pub const FDIO_SPAWN_CLONE_STDIO: u32 = 0x0008; -pub const FDIO_SPAWN_CLONE_ENVIRON: u32 = 0x0010; -pub const FDIO_SPAWN_CLONE_UTC_CLOCK: u32 = 0x0020; -pub const FDIO_SPAWN_CLONE_ALL: u32 = 0xFFFF; - -// fdio_spawn_etc actions - -pub const FDIO_SPAWN_ACTION_CLONE_FD: u32 = 0x0001; -pub const FDIO_SPAWN_ACTION_TRANSFER_FD: u32 = 0x0002; - -// Errors - -#[allow(unused)] -pub const ERR_INTERNAL: zx_status_t = -1; - -// ERR_NOT_SUPPORTED: The operation is not implemented, supported, -// or enabled. -#[allow(unused)] -pub const ERR_NOT_SUPPORTED: zx_status_t = -2; - -// ERR_NO_RESOURCES: The system was not able to allocate some resource -// needed for the operation. -#[allow(unused)] -pub const ERR_NO_RESOURCES: zx_status_t = -3; - -// ERR_NO_MEMORY: The system was not able to allocate memory needed -// for the operation. -#[allow(unused)] -pub const ERR_NO_MEMORY: zx_status_t = -4; - -// ERR_CALL_FAILED: The second phase of zx_channel_call(; did not complete -// successfully. -#[allow(unused)] -pub const ERR_CALL_FAILED: zx_status_t = -5; - -// ERR_INTERRUPTED_RETRY: The system call was interrupted, but should be -// retried. This should not be seen outside of the VDSO. -#[allow(unused)] -pub const ERR_INTERRUPTED_RETRY: zx_status_t = -6; - -// ======= Parameter errors ======= -// ERR_INVALID_ARGS: an argument is invalid, ex. null pointer -#[allow(unused)] -pub const ERR_INVALID_ARGS: zx_status_t = -10; - -// ERR_BAD_HANDLE: A specified handle value does not refer to a handle. -#[allow(unused)] -pub const ERR_BAD_HANDLE: zx_status_t = -11; - -// ERR_WRONG_TYPE: The subject of the operation is the wrong type to -// perform the operation. -// Example: Attempting a message_read on a thread handle. -#[allow(unused)] -pub const ERR_WRONG_TYPE: zx_status_t = -12; - -// ERR_BAD_SYSCALL: The specified syscall number is invalid. -#[allow(unused)] -pub const ERR_BAD_SYSCALL: zx_status_t = -13; - -// ERR_OUT_OF_RANGE: An argument is outside the valid range for this -// operation. -#[allow(unused)] -pub const ERR_OUT_OF_RANGE: zx_status_t = -14; - -// ERR_BUFFER_TOO_SMALL: A caller provided buffer is too small for -// this operation. -#[allow(unused)] -pub const ERR_BUFFER_TOO_SMALL: zx_status_t = -15; - -// ======= Precondition or state errors ======= -// ERR_BAD_STATE: operation failed because the current state of the -// object does not allow it, or a precondition of the operation is -// not satisfied -#[allow(unused)] -pub const ERR_BAD_STATE: zx_status_t = -20; - -// ERR_TIMED_OUT: The time limit for the operation elapsed before -// the operation completed. -#[allow(unused)] -pub const ERR_TIMED_OUT: zx_status_t = -21; - -// ERR_SHOULD_WAIT: The operation cannot be performed currently but -// potentially could succeed if the caller waits for a prerequisite -// to be satisfied, for example waiting for a handle to be readable -// or writable. -// Example: Attempting to read from a message pipe that has no -// messages waiting but has an open remote will return ERR_SHOULD_WAIT. -// Attempting to read from a message pipe that has no messages waiting -// and has a closed remote end will return ERR_REMOTE_CLOSED. -#[allow(unused)] -pub const ERR_SHOULD_WAIT: zx_status_t = -22; - -// ERR_CANCELED: The in-progress operation (e.g., a wait) has been -// // canceled. -#[allow(unused)] -pub const ERR_CANCELED: zx_status_t = -23; - -// ERR_PEER_CLOSED: The operation failed because the remote end -// of the subject of the operation was closed. -#[allow(unused)] -pub const ERR_PEER_CLOSED: zx_status_t = -24; - -// ERR_NOT_FOUND: The requested entity is not found. -#[allow(unused)] -pub const ERR_NOT_FOUND: zx_status_t = -25; - -// ERR_ALREADY_EXISTS: An object with the specified identifier -// already exists. -// Example: Attempting to create a file when a file already exists -// with that name. -#[allow(unused)] -pub const ERR_ALREADY_EXISTS: zx_status_t = -26; - -// ERR_ALREADY_BOUND: The operation failed because the named entity -// is already owned or controlled by another entity. The operation -// could succeed later if the current owner releases the entity. -#[allow(unused)] -pub const ERR_ALREADY_BOUND: zx_status_t = -27; - -// ERR_UNAVAILABLE: The subject of the operation is currently unable -// to perform the operation. -// Note: This is used when there's no direct way for the caller to -// observe when the subject will be able to perform the operation -// and should thus retry. -#[allow(unused)] -pub const ERR_UNAVAILABLE: zx_status_t = -28; - -// ======= Permission check errors ======= -// ERR_ACCESS_DENIED: The caller did not have permission to perform -// the specified operation. -#[allow(unused)] -pub const ERR_ACCESS_DENIED: zx_status_t = -30; - -// ======= Input-output errors ======= -// ERR_IO: Otherwise unspecified error occurred during I/O. -#[allow(unused)] -pub const ERR_IO: zx_status_t = -40; - -// ERR_REFUSED: The entity the I/O operation is being performed on -// rejected the operation. -// Example: an I2C device NAK'ing a transaction or a disk controller -// rejecting an invalid command. -#[allow(unused)] -pub const ERR_IO_REFUSED: zx_status_t = -41; - -// ERR_IO_DATA_INTEGRITY: The data in the operation failed an integrity -// check and is possibly corrupted. -// Example: CRC or Parity error. -#[allow(unused)] -pub const ERR_IO_DATA_INTEGRITY: zx_status_t = -42; - -// ERR_IO_DATA_LOSS: The data in the operation is currently unavailable -// and may be permanently lost. -// Example: A disk block is irrecoverably damaged. -#[allow(unused)] -pub const ERR_IO_DATA_LOSS: zx_status_t = -43; - -// Filesystem specific errors -#[allow(unused)] -pub const ERR_BAD_PATH: zx_status_t = -50; -#[allow(unused)] -pub const ERR_NOT_DIR: zx_status_t = -51; -#[allow(unused)] -pub const ERR_NOT_FILE: zx_status_t = -52; -// ERR_FILE_BIG: A file exceeds a filesystem-specific size limit. -#[allow(unused)] -pub const ERR_FILE_BIG: zx_status_t = -53; -// ERR_NO_SPACE: Filesystem or device space is exhausted. -#[allow(unused)] -pub const ERR_NO_SPACE: zx_status_t = -54; diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 43ece63457fe6..a3be2cdf738f5 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -25,15 +25,36 @@ impl Drop for Handler { } } -#[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", +#[cfg(all( + not(miri), + any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + ), +))] +mod thread_info; + +// miri doesn't model signals nor stack overflows and this code has some +// synchronization properties that we don't want to expose to user code, +// hence we disable it on miri. +#[cfg(all( + not(miri), + any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + ) ))] mod imp { use libc::{ @@ -46,22 +67,13 @@ mod imp { use libc::{mmap64, mprotect, munmap}; use super::Handler; - use crate::cell::Cell; + use super::thread_info::{delete_current_info, set_current_info, with_current_info}; use crate::ops::Range; use crate::sync::OnceLock; - use crate::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; + use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; - use crate::{io, mem, ptr, thread}; - - // We use a TLS variable to store the address of the guard page. While TLS - // variables are not guaranteed to be signal-safe, this works out in practice - // since we make sure to write to the variable before the signal stack is - // installed, thereby ensuring that the variable is always allocated when - // the signal handler is called. - thread_local! { - // FIXME: use `Range` once that implements `Copy`. - static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) }; - } + use crate::thread::with_current_name; + use crate::{io, mem, panic, ptr}; // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages // (unmapped pages) at the end of every thread's stack, so if a thread ends @@ -93,34 +105,40 @@ mod imp { info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { - let (start, end) = GUARD.get(); // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`. - let addr = unsafe { (*info).si_addr().addr() }; + let fault_addr = unsafe { (*info).si_addr().addr() }; + + // `with_current_info` expects that the process aborts after it is + // called. If the signal was not caused by a memory access, this might + // not be true. We detect this by noticing that the `si_addr` field is + // zero if the signal is synthetic. + if fault_addr != 0 { + with_current_info(|thread_info| { + // If the faulting address is within the guard page, then we print a + // message saying so and abort. + if let Some(thread_info) = thread_info + && thread_info.guard_page_range.contains(&fault_addr) + { + let name = thread_info.thread_name.as_deref().unwrap_or(""); + rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + rtabort!("stack overflow"); + } + }) + } - // If the faulting address is within the guard page, then we print a - // message saying so and abort. - if start <= addr && addr < end { - thread::with_current_name(|name| { - let name = name.unwrap_or(""); - rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); - }); + // Unregister ourselves by reverting back to the default behavior. + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; + action.sa_sigaction = SIG_DFL; + // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction + unsafe { sigaction(signum, &action, ptr::null_mut()) }; - rtabort!("stack overflow"); - } else { - // Unregister ourselves by reverting back to the default behavior. - // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" - let mut action: sigaction = unsafe { mem::zeroed() }; - action.sa_sigaction = SIG_DFL; - // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction - unsafe { sigaction(signum, &action, ptr::null_mut()) }; - - // See comment above for why this function returns. - } + // See comment above for why this function returns. } - static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); - static MAIN_ALTSTACK: AtomicPtr = AtomicPtr::new(ptr::null_mut()); - static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false); + static PAGE_SIZE: Atomic = AtomicUsize::new(0); + static MAIN_ALTSTACK: Atomic<*mut libc::c_void> = AtomicPtr::new(ptr::null_mut()); + static NEED_ALTSTACK: Atomic = AtomicBool::new(false); /// # Safety /// Must be called only once @@ -128,9 +146,7 @@ mod imp { pub unsafe fn init() { PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); - // Always write to GUARD to ensure the TLS variable is allocated. - let guard = unsafe { install_main_guard().unwrap_or(0..0) }; - GUARD.set((guard.start, guard.end)); + let mut guard_page_range = unsafe { install_main_guard() }; // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" let mut action: sigaction = unsafe { mem::zeroed() }; @@ -145,7 +161,13 @@ mod imp { let handler = unsafe { make_handler(true) }; MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); + + if let Some(guard_page_range) = guard_page_range.take() { + let thread_name = with_current_name(|name| name.map(Box::from)); + set_current_info(guard_page_range, thread_name); + } } + action.sa_flags = SA_SIGINFO | SA_ONSTACK; action.sa_sigaction = signal_handler as sighandler_t; // SAFETY: only overriding signals if the default is set @@ -214,9 +236,10 @@ mod imp { } if !main_thread { - // Always write to GUARD to ensure the TLS variable is allocated. - let guard = unsafe { current_guard() }.unwrap_or(0..0); - GUARD.set((guard.start, guard.end)); + if let Some(guard_page_range) = unsafe { current_guard() } { + let thread_name = with_current_name(|name| name.map(Box::from)); + set_current_info(guard_page_range, thread_name); + } } // SAFETY: assuming stack_t is zero-initializable @@ -261,6 +284,8 @@ mod imp { // a mapping that started one page earlier, so walk back a page and unmap from there. unsafe { munmap(data.sub(page_size), sigstack_size + page_size) }; } + + delete_current_info(); } /// Modern kernels on modern hardware can have dynamic signal stack sizes. @@ -424,18 +449,32 @@ mod imp { let pages = PAGES.get_or_init(|| { use crate::sys::weak::dlsym; - dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int); + dlsym!( + fn sysctlbyname( + name: *const libc::c_char, + oldp: *mut libc::c_void, + oldlenp: *mut libc::size_t, + newp: *const libc::c_void, + newlen: libc::size_t, + ) -> libc::c_int; + ); let mut guard: usize = 0; - let mut size = mem::size_of_val(&guard); + let mut size = size_of_val(&guard); let oid = c"security.bsd.stack_guard_page"; match sysctlbyname.get() { - Some(fcn) if unsafe { - fcn(oid.as_ptr(), - (&raw mut guard).cast(), - &raw mut size, - ptr::null_mut(), - 0) == 0 - } => guard, + Some(fcn) + if unsafe { + fcn( + oid.as_ptr(), + (&raw mut guard).cast(), + &raw mut size, + ptr::null_mut(), + 0, + ) == 0 + } => + { + guard + } _ => 1, } }); @@ -576,16 +615,20 @@ mod imp { // usually have fewer qualms about forwards compatibility, since the runtime // is shipped with the OS): // -#[cfg(not(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "illumos", -)))] +#[cfg(any( + miri, + not(any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + target_os = "cygwin", + )) +))] mod imp { pub unsafe fn init() {} @@ -597,3 +640,89 @@ mod imp { pub unsafe fn drop_handler(_data: *mut libc::c_void) {} } + +#[cfg(target_os = "cygwin")] +mod imp { + mod c { + pub type PVECTORED_EXCEPTION_HANDLER = + Option i32>; + pub type NTSTATUS = i32; + pub type BOOL = i32; + + unsafe extern "system" { + pub fn AddVectoredExceptionHandler( + first: u32, + handler: PVECTORED_EXCEPTION_HANDLER, + ) -> *mut core::ffi::c_void; + pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL; + } + + pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = 0xC00000FD_u32 as _; + pub const EXCEPTION_CONTINUE_SEARCH: i32 = 1i32; + + #[repr(C)] + #[derive(Clone, Copy)] + pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + // We don't need this field here + // pub Context: *mut CONTEXT, + } + #[repr(C)] + #[derive(Clone, Copy)] + pub struct EXCEPTION_RECORD { + pub ExceptionCode: NTSTATUS, + pub ExceptionFlags: u32, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: *mut core::ffi::c_void, + pub NumberParameters: u32, + pub ExceptionInformation: [usize; 15], + } + } + + /// Reserve stack space for use in stack overflow exceptions. + fn reserve_stack() { + let result = unsafe { c::SetThreadStackGuarantee(&mut 0x5000) }; + // Reserving stack space is not critical so we allow it to fail in the released build of libstd. + // We still use debug assert here so that CI will test that we haven't made a mistake calling the function. + debug_assert_ne!(result, 0, "failed to reserve stack space for exception handling"); + } + + unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> i32 { + // SAFETY: It's up to the caller (which in this case is the OS) to ensure that `ExceptionInfo` is valid. + unsafe { + let rec = &(*(*ExceptionInfo).ExceptionRecord); + let code = rec.ExceptionCode; + + if code == c::EXCEPTION_STACK_OVERFLOW { + crate::thread::with_current_name(|name| { + let name = name.unwrap_or(""); + rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + }); + } + c::EXCEPTION_CONTINUE_SEARCH + } + } + + pub unsafe fn init() { + // SAFETY: `vectored_handler` has the correct ABI and is safe to call during exception handling. + unsafe { + let result = c::AddVectoredExceptionHandler(0, Some(vectored_handler)); + // Similar to the above, adding the stack overflow handler is allowed to fail + // but a debug assert is used so CI will still test that it normally works. + debug_assert!(!result.is_null(), "failed to install exception handler"); + } + // Set the thread stack guarantee for the main thread. + reserve_stack(); + } + + pub unsafe fn cleanup() {} + + pub unsafe fn make_handler(main_thread: bool) -> super::Handler { + if !main_thread { + reserve_stack(); + } + super::Handler::null() + } + + pub unsafe fn drop_handler(_data: *mut libc::c_void) {} +} diff --git a/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs new file mode 100644 index 0000000000000..e81429b98a6c7 --- /dev/null +++ b/library/std/src/sys/pal/unix/stack_overflow/thread_info.rs @@ -0,0 +1,129 @@ +//! TLS, but async-signal-safe. +//! +//! Unfortunately, because thread local storage isn't async-signal-safe, we +//! cannot soundly use it in our stack overflow handler. While this works +//! without problems on most platforms, it can lead to undefined behaviour +//! on others (such as GNU/Linux). Luckily, the POSIX specification documents +//! two thread-specific values that can be accessed in asynchronous signal +//! handlers: the value of `pthread_self()` and the address of `errno`. As +//! `pthread_t` is an opaque platform-specific type, we use the address of +//! `errno` here. As it is thread-specific and does not change over the +//! lifetime of a thread, we can use `&errno` as a key for a `BTreeMap` +//! that stores thread-specific data. +//! +//! Concurrent access to this map is synchronized by two locks – an outer +//! [`Mutex`] and an inner spin lock that also remembers the identity of +//! the lock owner: +//! * The spin lock is the primary means of synchronization: since it only +//! uses native atomics, it can be soundly used inside the signal handle +//! as opposed to [`Mutex`], which might not be async-signal-safe. +//! * The [`Mutex`] prevents busy-waiting in the setup logic, as all accesses +//! there are performed with the [`Mutex`] held, which makes the spin-lock +//! redundant in the common case. +//! * Finally, by using the `errno` address as the locked value of the spin +//! lock, we can detect cases where a SIGSEGV occurred while the thread +//! info is being modified. + +use crate::collections::BTreeMap; +use crate::hint::spin_loop; +use crate::ops::Range; +use crate::sync::Mutex; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sys::os::errno_location; + +pub struct ThreadInfo { + pub guard_page_range: Range, + pub thread_name: Option>, +} + +static LOCK: Mutex<()> = Mutex::new(()); +static SPIN_LOCK: AtomicUsize = AtomicUsize::new(0); +// This uses a `BTreeMap` instead of a hashmap since it supports constant +// initialization and automatically reduces the amount of memory used when +// items are removed. +static mut THREAD_INFO: BTreeMap = BTreeMap::new(); + +struct UnlockOnDrop; + +impl Drop for UnlockOnDrop { + fn drop(&mut self) { + SPIN_LOCK.store(0, Ordering::Release); + } +} + +/// Get the current thread's information, if available. +/// +/// Calling this function might freeze other threads if they attempt to modify +/// their thread information. Thus, the caller should ensure that the process +/// is aborted shortly after this function is called. +/// +/// This function is guaranteed to be async-signal-safe if `f` is too. +pub fn with_current_info(f: impl FnOnce(Option<&ThreadInfo>) -> R) -> R { + let this = errno_location().addr(); + let mut attempt = 0; + let _guard = loop { + // If we are just spinning endlessly, it's very likely that the thread + // modifying the thread info map has a lower priority than us and will + // not continue until we stop running. Just give up in that case. + if attempt == 10_000_000 { + rtprintpanic!("deadlock in SIGSEGV handler"); + return f(None); + } + + match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => break UnlockOnDrop, + Err(owner) if owner == this => { + rtabort!("a thread received SIGSEGV while modifying its stack overflow information") + } + // Spin until the lock can be acquired – there is nothing better to + // do. This is unfortunately a priority hole, but a stack overflow + // is a fatal error anyway. + Err(_) => { + spin_loop(); + attempt += 1; + } + } + }; + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased. + let thread_info = unsafe { &*(&raw const THREAD_INFO) }; + f(thread_info.get(&this)) +} + +fn spin_lock_in_setup(this: usize) -> UnlockOnDrop { + loop { + match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => return UnlockOnDrop, + Err(owner) if owner == this => { + unreachable!("the thread info setup logic isn't recursive") + } + // This function is always called with the outer lock held, + // meaning the only time locking can fail is if another thread has + // encountered a stack overflow. Since that will abort the process, + // we just stop the current thread until that time. We use `pause` + // instead of spinning to avoid priority inversion. + // SAFETY: this doesn't have any safety preconditions. + Err(_) => drop(unsafe { libc::pause() }), + } + } +} + +pub fn set_current_info(guard_page_range: Range, thread_name: Option>) { + let this = errno_location().addr(); + let _lock_guard = LOCK.lock(); + let _spin_guard = spin_lock_in_setup(this); + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot be aliased. + let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) }; + thread_info.insert(this, ThreadInfo { guard_page_range, thread_name }); +} + +pub fn delete_current_info() { + let this = errno_location().addr(); + let _lock_guard = LOCK.lock(); + let _spin_guard = spin_lock_in_setup(this); + + // SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased. + let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) }; + thread_info.remove(&this); +} diff --git a/library/std/src/sys/pal/unix/stdio.rs b/library/std/src/sys/pal/unix/stdio.rs deleted file mode 100644 index 8c2f61a40de3b..0000000000000 --- a/library/std/src/sys/pal/unix/stdio.rs +++ /dev/null @@ -1,99 +0,0 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::mem::ManuallyDrop; -use crate::os::unix::io::FromRawFd; -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 { - unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read(buf) } - } - - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_buf(buf) } - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(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 { - unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write(buf) } - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - unsafe { - ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write_vectored(bufs) - } - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - 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 { - unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write(buf) } - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - unsafe { - ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write_vectored(bufs) - } - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - 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::io::DEFAULT_BUF_SIZE; - -pub fn panic_output() -> Option { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/pal/unix/sync/condvar.rs b/library/std/src/sys/pal/unix/sync/condvar.rs index 73631053e9f47..efa6f8d776559 100644 --- a/library/std/src/sys/pal/unix/sync/condvar.rs +++ b/library/std/src/sys/pal/unix/sync/condvar.rs @@ -64,7 +64,10 @@ impl Condvar { // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c // // To work around this issue, the timeout is clamped to 1000 years. - #[cfg(target_vendor = "apple")] + // + // Cygwin implementation is based on NT API and a super large timeout + // makes the syscall block forever. + #[cfg(any(target_vendor = "apple", target_os = "cygwin"))] let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400)); let timeout = Timespec::now(Self::CLOCK).checked_add_duration(&dur); diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 3dedc8d1257cb..d8b189413f4a3 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -8,31 +8,19 @@ use crate::sys::weak::weak; use crate::sys::{os, stack_overflow}; use crate::time::Duration; use crate::{cmp, io, ptr}; -#[cfg(not(any(target_os = "l4re", target_os = "vxworks", target_os = "espidf")))] +#[cfg(not(any( + target_os = "l4re", + target_os = "vxworks", + target_os = "espidf", + target_os = "nuttx" +)))] pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; #[cfg(target_os = "l4re")] pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; #[cfg(target_os = "vxworks")] pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024; -#[cfg(target_os = "espidf")] -pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF menuconfig system should be used - -#[cfg(target_os = "fuchsia")] -mod zircon { - type zx_handle_t = u32; - type zx_status_t = i32; - pub const ZX_PROP_NAME: u32 = 3; - - unsafe extern "C" { - pub fn zx_object_set_property( - handle: zx_handle_t, - property: u32, - value: *const libc::c_void, - value_size: libc::size_t, - ) -> zx_status_t; - pub fn zx_thread_self() -> zx_handle_t; - } -} +#[cfg(any(target_os = "espidf", target_os = "nuttx"))] +pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF/NuttX menuconfig system should be used pub struct Thread { id: libc::pthread_t, @@ -52,10 +40,10 @@ impl Thread { let mut attr: mem::MaybeUninit = mem::MaybeUninit::uninit(); assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0); - #[cfg(target_os = "espidf")] + #[cfg(any(target_os = "espidf", target_os = "nuttx"))] if stack > 0 { // Only set the stack if a non-zero value is passed - // 0 is used as an indication that the default stack size configured in the ESP-IDF menuconfig system should be used + // 0 is used as an indication that the default stack size configured in the ESP-IDF/NuttX menuconfig system should be used assert_eq!( libc::pthread_attr_setstacksize( attr.as_mut_ptr(), @@ -65,7 +53,7 @@ impl Thread { ); } - #[cfg(not(target_os = "espidf"))] + #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] { let stack_size = cmp::max(stack, min_stack_size(attr.as_ptr())); @@ -137,13 +125,14 @@ impl Thread { target_os = "linux", target_os = "freebsd", target_os = "dragonfly", - target_os = "nuttx" + target_os = "nuttx", + target_os = "cygwin" ))] pub fn set_name(name: &CStr) { unsafe { cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - // Linux limits the allowed length of the name. + if #[cfg(any(target_os = "linux", target_os = "cygwin"))] { + // Linux and Cygwin limits the allowed length of the name. const TASK_COMM_LEN: usize = 16; let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); } else { @@ -189,11 +178,12 @@ impl Thread { #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] pub fn set_name(name: &CStr) { - weak! { + weak!( fn pthread_setname_np( - libc::pthread_t, *const libc::c_char - ) -> libc::c_int - } + thread: libc::pthread_t, + name: *const libc::c_char, + ) -> libc::c_int; + ); if let Some(f) = pthread_setname_np.get() { #[cfg(target_os = "nto")] @@ -209,7 +199,7 @@ impl Thread { #[cfg(target_os = "fuchsia")] pub fn set_name(name: &CStr) { - use self::zircon::*; + use super::fuchsia::*; unsafe { zx_object_set_property( zx_thread_self(), @@ -232,16 +222,8 @@ impl Thread { #[cfg(target_os = "vxworks")] pub fn set_name(name: &CStr) { - // FIXME(libc): adding real STATUS, ERROR type eventually. - unsafe extern "C" { - fn taskNameSet(task_id: libc::TASK_ID, task_name: *mut libc::c_char) -> libc::c_int; - } - - // VX_TASK_NAME_LEN is 31 in VxWorks 7. - const VX_TASK_NAME_LEN: usize = 31; - - let mut name = truncate_cstr::<{ VX_TASK_NAME_LEN }>(name); - let res = unsafe { taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; + let mut name = truncate_cstr::<{ libc::VX_TASK_RENAME_LENGTH - 1 }>(name); + let res = unsafe { libc::taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; debug_assert_eq!(res, libc::OK); } @@ -342,6 +324,7 @@ impl Drop for Thread { target_os = "solaris", target_os = "illumos", target_os = "vxworks", + target_os = "cygwin", target_vendor = "apple", ))] fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { @@ -362,6 +345,7 @@ pub fn available_parallelism() -> io::Result> { target_os = "linux", target_os = "aix", target_vendor = "apple", + target_os = "cygwin", ))] { #[allow(unused_assignments)] #[allow(unused_mut)] @@ -372,7 +356,7 @@ pub fn available_parallelism() -> io::Result> { quota = cgroups::quota().max(1); let mut set: libc::cpu_set_t = unsafe { mem::zeroed() }; unsafe { - if libc::sched_getaffinity(0, mem::size_of::(), &mut set) == 0 { + if libc::sched_getaffinity(0, size_of::(), &mut set) == 0 { let count = libc::CPU_COUNT(&set) as usize; let count = count.min(quota); @@ -412,7 +396,7 @@ pub fn available_parallelism() -> io::Result> { libc::CPU_LEVEL_WHICH, libc::CPU_WHICH_PID, -1, - mem::size_of::(), + size_of::(), &mut set, ) == 0 { let count = libc::CPU_COUNT(&set) as usize; @@ -447,7 +431,7 @@ pub fn available_parallelism() -> io::Result> { } let mut cpus: libc::c_uint = 0; - let mut cpus_size = crate::mem::size_of_val(&cpus); + let mut cpus_size = size_of_val(&cpus); unsafe { cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint; @@ -756,7 +740,9 @@ unsafe fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { // We use dlsym to avoid an ELF version dependency on GLIBC_PRIVATE. (#23628) // We shouldn't really be using such an internal symbol, but there's currently // no other way to account for the TLS size. - dlsym!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t); + dlsym!( + fn __pthread_get_minstack(attr: *const libc::pthread_attr_t) -> libc::size_t; + ); match __pthread_get_minstack.get() { None => libc::PTHREAD_STACK_MIN, diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index 0271626380c5f..0074d7674741b 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -112,7 +112,12 @@ impl Timespec { // __clock_gettime64 was added to 32-bit arches in glibc 2.34, // and it handles both vDSO calls and ENOSYS fallbacks itself. - weak!(fn __clock_gettime64(libc::clockid_t, *mut __timespec64) -> libc::c_int); + weak!( + fn __clock_gettime64( + clockid: libc::clockid_t, + tp: *mut __timespec64, + ) -> libc::c_int; + ); if let Some(clock_gettime64) = __clock_gettime64.get() { let mut t = MaybeUninit::uninit(); diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs index 5a37598f43827..c8cf75b876c26 100644 --- a/library/std/src/sys/pal/unix/weak.rs +++ b/library/std/src/sys/pal/unix/weak.rs @@ -20,16 +20,17 @@ // 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)] +#![forbid(unsafe_op_in_unsafe_fn)] use crate::ffi::CStr; use crate::marker::PhantomData; -use crate::sync::atomic::{self, AtomicPtr, Ordering}; +use crate::sync::atomic::{self, Atomic, AtomicPtr, Ordering}; use crate::{mem, ptr}; // We can use true weak linkage on ELF targets. #[cfg(all(unix, not(target_vendor = "apple")))] pub(crate) macro weak { - (fn $name:ident($($t:ty),*) -> $ret:ty) => ( + (fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => ( let ref $name: ExternWeak $ret> = { unsafe extern "C" { #[linkage = "extern_weak"] @@ -62,10 +63,16 @@ impl ExternWeak { } pub(crate) macro dlsym { - (fn $name:ident($($t:ty),*) -> $ret:ty) => ( - dlsym!(fn $name($($t),*) -> $ret, stringify!($name)); + (fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => ( + dlsym!( + #[link_name = stringify!($name)] + fn $name($($param : $t),*) -> $ret; + ); ), - (fn $name:ident($($t:ty),*) -> $ret:ty, $sym:expr) => ( + ( + #[link_name = $sym:expr] + fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty; + ) => ( static DLSYM: DlsymWeak $ret> = DlsymWeak::new(concat!($sym, '\0')); let $name = &DLSYM; @@ -73,7 +80,7 @@ pub(crate) macro dlsym { } pub(crate) struct DlsymWeak { name: &'static str, - func: AtomicPtr, + func: Atomic<*mut libc::c_void>, _marker: PhantomData, } @@ -123,13 +130,17 @@ impl DlsymWeak { // Cold because it should only happen during first-time initialization. #[cold] unsafe fn initialize(&self) -> Option { - assert_eq!(mem::size_of::(), mem::size_of::<*mut libc::c_void>()); + assert_eq!(size_of::(), size_of::<*mut libc::c_void>()); - let val = fetch(self.name); + let val = unsafe { fetch(self.name) }; // This synchronizes with the acquire fence in `get`. self.func.store(val, Ordering::Release); - if val.is_null() { None } else { Some(mem::transmute_copy::<*mut libc::c_void, F>(&val)) } + if val.is_null() { + None + } else { + Some(unsafe { mem::transmute_copy::<*mut libc::c_void, F>(&val) }) + } } } @@ -138,17 +149,17 @@ unsafe fn fetch(name: &str) -> *mut libc::c_void { Ok(cstr) => cstr, Err(..) => return ptr::null_mut(), }; - libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) + unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) } } #[cfg(not(any(target_os = "linux", target_os = "android")))] pub(crate) macro syscall { - (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( - unsafe fn $name($($arg_name: $t),*) -> $ret { - weak! { fn $name($($t),*) -> $ret } + (fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => ( + unsafe fn $name($($param: $t),*) -> $ret { + weak!(fn $name($($param: $t),*) -> $ret;); if let Some(fun) = $name.get() { - fun($($arg_name),*) + unsafe { fun($($param),*) } } else { super::os::set_errno(libc::ENOSYS); -1 @@ -159,16 +170,18 @@ pub(crate) macro syscall { #[cfg(any(target_os = "linux", target_os = "android"))] pub(crate) macro syscall { - (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( - unsafe fn $name($($arg_name:$t),*) -> $ret { - weak! { fn $name($($t),*) -> $ret } + ( + fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty; + ) => ( + unsafe fn $name($($param: $t),*) -> $ret { + weak!(fn $name($($param: $t),*) -> $ret;); // Use a weak symbol from libc when possible, allowing `LD_PRELOAD` // interposition, but if it's not found just use a raw syscall. if let Some(fun) = $name.get() { - fun($($arg_name),*) + unsafe { fun($($param),*) } } else { - libc::syscall(libc::${concat(SYS_, $name)}, $($arg_name),*) as $ret + unsafe { libc::syscall(libc::${concat(SYS_, $name)}, $($param),*) as $ret } } } ) @@ -176,9 +189,9 @@ pub(crate) macro syscall { #[cfg(any(target_os = "linux", target_os = "android"))] pub(crate) macro raw_syscall { - (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( - unsafe fn $name($($arg_name:$t),*) -> $ret { - libc::syscall(libc::${concat(SYS_, $name)}, $($arg_name),*) as $ret + (fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => ( + unsafe fn $name($($param: $t),*) -> $ret { + unsafe { libc::syscall(libc::${concat(SYS_, $name)}, $($param),*) as $ret } } ) } diff --git a/library/std/src/sys/pal/unsupported/args.rs b/library/std/src/sys/pal/unsupported/args.rs deleted file mode 100644 index a2d75a6197633..0000000000000 --- a/library/std/src/sys/pal/unsupported/args.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::ffi::OsString; -use crate::fmt; - -pub struct Args {} - -pub fn args() -> Args { - Args {} -} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().finish() - } -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option { - None - } - fn size_hint(&self) -> (usize, Option) { - (0, Some(0)) - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - 0 - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - None - } -} diff --git a/library/std/src/sys/pal/unsupported/env.rs b/library/std/src/sys/pal/unsupported/env.rs deleted file mode 100644 index d2efec506c56b..0000000000000 --- a/library/std/src/sys/pal/unsupported/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = ""; - pub const OS: &str = ""; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ""; - pub const DLL_EXTENSION: &str = ""; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} diff --git a/library/std/src/sys/pal/unsupported/fs.rs b/library/std/src/sys/pal/unsupported/fs.rs deleted file mode 100644 index 45e93deffa3a4..0000000000000 --- a/library/std/src/sys/pal/unsupported/fs.rs +++ /dev/null @@ -1,348 +0,0 @@ -use crate::ffi::OsString; -use crate::fmt; -use crate::hash::{Hash, Hasher}; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::path::{Path, PathBuf}; -use crate::sys::time::SystemTime; -use crate::sys::unsupported; - -pub struct File(!); - -pub struct FileAttr(!); - -pub struct ReadDir(!); - -pub struct DirEntry(!); - -#[derive(Clone, Debug)] -pub struct OpenOptions {} - -#[derive(Copy, Clone, Debug, Default)] -pub struct FileTimes {} - -pub struct FilePermissions(!); - -pub struct FileType(!); - -#[derive(Debug)] -pub struct DirBuilder {} - -impl FileAttr { - pub fn size(&self) -> u64 { - self.0 - } - - pub fn perm(&self) -> FilePermissions { - self.0 - } - - pub fn file_type(&self) -> FileType { - self.0 - } - - pub fn modified(&self) -> io::Result { - self.0 - } - - pub fn accessed(&self) -> io::Result { - self.0 - } - - pub fn created(&self) -> io::Result { - self.0 - } -} - -impl Clone for FileAttr { - fn clone(&self) -> FileAttr { - self.0 - } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.0 - } - - pub fn set_readonly(&mut self, _readonly: bool) { - self.0 - } -} - -impl Clone for FilePermissions { - fn clone(&self) -> FilePermissions { - self.0 - } -} - -impl PartialEq for FilePermissions { - fn eq(&self, _other: &FilePermissions) -> bool { - self.0 - } -} - -impl Eq for FilePermissions {} - -impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -impl FileTimes { - pub fn set_accessed(&mut self, _t: SystemTime) {} - pub fn set_modified(&mut self, _t: SystemTime) {} -} - -impl FileType { - pub fn is_dir(&self) -> bool { - self.0 - } - - pub fn is_file(&self) -> bool { - self.0 - } - - pub fn is_symlink(&self) -> bool { - self.0 - } -} - -impl Clone for FileType { - fn clone(&self) -> FileType { - self.0 - } -} - -impl Copy for FileType {} - -impl PartialEq for FileType { - fn eq(&self, _other: &FileType) -> bool { - self.0 - } -} - -impl Eq for FileType {} - -impl Hash for FileType { - fn hash(&self, _h: &mut H) { - self.0 - } -} - -impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -impl Iterator for ReadDir { - type Item = io::Result; - - fn next(&mut self) -> Option> { - self.0 - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - self.0 - } - - pub fn file_name(&self) -> OsString { - self.0 - } - - pub fn metadata(&self) -> io::Result { - self.0 - } - - pub fn file_type(&self) -> io::Result { - self.0 - } -} - -impl OpenOptions { - pub fn new() -> OpenOptions { - OpenOptions {} - } - - pub fn read(&mut self, _read: bool) {} - pub fn write(&mut self, _write: bool) {} - pub fn append(&mut self, _append: bool) {} - pub fn truncate(&mut self, _truncate: bool) {} - pub fn create(&mut self, _create: bool) {} - pub fn create_new(&mut self, _create_new: bool) {} -} - -impl File { - pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { - unsupported() - } - - pub fn file_attr(&self) -> io::Result { - self.0 - } - - pub fn fsync(&self) -> io::Result<()> { - self.0 - } - - pub fn datasync(&self) -> io::Result<()> { - self.0 - } - - pub fn lock(&self) -> io::Result<()> { - self.0 - } - - pub fn lock_shared(&self) -> io::Result<()> { - self.0 - } - - pub fn try_lock(&self) -> io::Result { - self.0 - } - - pub fn try_lock_shared(&self) -> io::Result { - self.0 - } - - pub fn unlock(&self) -> io::Result<()> { - self.0 - } - - pub fn truncate(&self, _size: u64) -> io::Result<()> { - self.0 - } - - pub fn read(&self, _buf: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn write(&self, _buf: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { - self.0 - } - - pub fn is_write_vectored(&self) -> bool { - self.0 - } - - pub fn flush(&self) -> io::Result<()> { - self.0 - } - - pub fn seek(&self, _pos: SeekFrom) -> io::Result { - self.0 - } - - pub fn tell(&self) -> io::Result { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - self.0 - } - - pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { - self.0 - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder {} - } - - pub fn mkdir(&self, _p: &Path) -> io::Result<()> { - unsupported() - } -} - -impl fmt::Debug for File { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub fn readdir(_p: &Path) -> io::Result { - unsupported() -} - -pub fn unlink(_p: &Path) -> io::Result<()> { - unsupported() -} - -pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { - unsupported() -} - -pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { - match perm.0 {} -} - -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() -} - -pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - unsupported() -} - -pub fn exists(_path: &Path) -> io::Result { - unsupported() -} - -pub fn readlink(_p: &Path) -> io::Result { - unsupported() -} - -pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { - unsupported() -} - -pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { - unsupported() -} - -pub fn stat(_p: &Path) -> io::Result { - unsupported() -} - -pub fn lstat(_p: &Path) -> io::Result { - unsupported() -} - -pub fn canonicalize(_p: &Path) -> io::Result { - unsupported() -} - -pub fn copy(_from: &Path, _to: &Path) -> io::Result { - unsupported() -} diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index b1aaeb1b4c814..5e3295b1331a3 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -1,12 +1,7 @@ #![deny(unsafe_op_in_unsafe_fn)] -pub mod args; -pub mod env; -pub mod fs; pub mod os; pub mod pipe; -pub mod process; -pub mod stdio; pub mod thread; pub mod time; diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs index 48de4312885fe..a8ef97ecf67ac 100644 --- a/library/std/src/sys/pal/unsupported/os.rs +++ b/library/std/src/sys/pal/unsupported/os.rs @@ -62,47 +62,6 @@ pub fn current_exe() -> io::Result { unsupported() } -pub struct Env(!); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self(inner) = self; - match *inner {} - } -} - -pub fn env() -> Env { - panic!("not supported on this platform") -} - -pub fn getenv(_: &OsStr) -> Option { - None -} - -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) -} - -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } diff --git a/library/std/src/sys/pal/unsupported/pipe.rs b/library/std/src/sys/pal/unsupported/pipe.rs index 6799d21a1ff75..988e551de5223 100644 --- a/library/std/src/sys/pal/unsupported/pipe.rs +++ b/library/std/src/sys/pal/unsupported/pipe.rs @@ -1,5 +1,6 @@ use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::sys_common::{FromInner, IntoInner}; pub struct AnonPipe(!); @@ -54,3 +55,53 @@ impl AnonPipe { pub fn read2(p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { match p1.0 {} } + +impl FromInner for AnonPipe { + fn from_inner(inner: !) -> Self { + inner + } +} + +impl IntoInner for AnonPipe { + fn into_inner(self) -> ! { + self.0 + } +} + +#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))] +mod unix_traits { + use super::AnonPipe; + use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; + use crate::sys_common::FromInner; + + impl AsRawFd for AnonPipe { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0 + } + } + + impl AsFd for AnonPipe { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0 + } + } + + impl IntoRawFd for AnonPipe { + fn into_raw_fd(self) -> RawFd { + self.0 + } + } + + impl FromRawFd for AnonPipe { + unsafe fn from_raw_fd(_: RawFd) -> Self { + panic!("creating pipe on this platform is unsupported!") + } + } + + impl FromInner for AnonPipe { + fn from_inner(_: OwnedFd) -> Self { + panic!("creating pipe on this platform is unsupported!") + } + } +} diff --git a/library/std/src/sys/pal/unsupported/process.rs b/library/std/src/sys/pal/unsupported/process.rs deleted file mode 100644 index fee81744f09ec..0000000000000 --- a/library/std/src/sys/pal/unsupported/process.rs +++ /dev/null @@ -1,322 +0,0 @@ -pub use crate::ffi::OsString as EnvKey; -use crate::ffi::{OsStr, OsString}; -use crate::num::NonZero; -use crate::path::Path; -use crate::sys::fs::File; -use crate::sys::pipe::AnonPipe; -use crate::sys::unsupported; -use crate::sys_common::process::{CommandEnv, CommandEnvs}; -use crate::{fmt, io}; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -pub struct Command { - program: OsString, - args: Vec, - env: CommandEnv, - - cwd: Option, - stdin: Option, - stdout: Option, - stderr: Option, -} - -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - -#[derive(Debug)] -pub enum Stdio { - Inherit, - Null, - MakePipe, - ParentStdout, - ParentStderr, - #[allow(dead_code)] // This variant exists only for the Debug impl - InheritFile(File), -} - -impl Command { - pub fn new(program: &OsStr) -> Command { - Command { - program: program.to_owned(), - args: vec![program.to_owned()], - env: Default::default(), - cwd: None, - stdin: None, - stdout: None, - stderr: None, - } - } - - pub fn arg(&mut self, arg: &OsStr) { - self.args.push(arg.to_owned()); - } - - pub fn env_mut(&mut self) -> &mut CommandEnv { - &mut self.env - } - - pub fn cwd(&mut self, dir: &OsStr) { - self.cwd = Some(dir.to_owned()); - } - - 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 get_program(&self) -> &OsStr { - &self.program - } - - pub fn get_args(&self) -> CommandArgs<'_> { - let mut iter = self.args.iter(); - iter.next(); - CommandArgs { iter } - } - - pub fn get_envs(&self) -> CommandEnvs<'_> { - self.env.iter() - } - - pub fn get_current_dir(&self) -> Option<&Path> { - self.cwd.as_ref().map(|cs| Path::new(cs)) - } - - pub fn spawn( - &mut self, - _default: Stdio, - _needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - unsupported() - } - - pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - unsupported() - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - pipe.diverge() - } -} - -impl From for Stdio { - fn from(_: io::Stdout) -> Stdio { - Stdio::ParentStdout - } -} - -impl From for Stdio { - fn from(_: io::Stderr) -> Stdio { - Stdio::ParentStderr - } -} - -impl From for Stdio { - fn from(file: File) -> Stdio { - Stdio::InheritFile(file) - } -} - -impl fmt::Debug for Command { - // show all attributes - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - let mut debug_command = f.debug_struct("Command"); - debug_command.field("program", &self.program).field("args", &self.args); - if !self.env.is_unchanged() { - debug_command.field("env", &self.env); - } - - if self.cwd.is_some() { - debug_command.field("cwd", &self.cwd); - } - - if self.stdin.is_some() { - debug_command.field("stdin", &self.stdin); - } - if self.stdout.is_some() { - debug_command.field("stdout", &self.stdout); - } - if self.stderr.is_some() { - debug_command.field("stderr", &self.stderr); - } - - debug_command.finish() - } else { - if let Some(ref cwd) = self.cwd { - write!(f, "cd {cwd:?} && ")?; - } - if self.env.does_clear() { - write!(f, "env -i ")?; - // Altered env vars will be printed next, that should exactly work as expected. - } else { - // Removed env vars need the command to be wrapped in `env`. - let mut any_removed = false; - for (key, value_opt) in self.get_envs() { - if value_opt.is_none() { - if !any_removed { - write!(f, "env ")?; - any_removed = true; - } - write!(f, "-u {} ", key.to_string_lossy())?; - } - } - } - // Altered env vars can just be added in front of the program. - for (key, value_opt) in self.get_envs() { - if let Some(value) = value_opt { - write!(f, "{}={value:?} ", key.to_string_lossy())?; - } - } - 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, Default)] -#[non_exhaustive] -pub struct ExitStatus(); - -impl ExitStatus { - pub fn exit_ok(&self) -> Result<(), ExitStatusError> { - Ok(()) - } - - pub fn code(&self) -> Option { - Some(0) - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "") - } -} - -pub struct ExitStatusError(!); - -impl Clone for ExitStatusError { - fn clone(&self) -> ExitStatusError { - self.0 - } -} - -impl Copy for ExitStatusError {} - -impl PartialEq for ExitStatusError { - fn eq(&self, _other: &ExitStatusError) -> bool { - self.0 - } -} - -impl Eq for ExitStatusError {} - -impl fmt::Debug for ExitStatusError { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -impl Into for ExitStatusError { - fn into(self) -> ExitStatus { - self.0 - } -} - -impl ExitStatusError { - pub fn code(self) -> Option> { - self.0 - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(u8); - -impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(0); - pub const FAILURE: ExitCode = ExitCode(1); - - pub fn as_i32(&self) -> i32 { - self.0 as i32 - } -} - -impl From for ExitCode { - fn from(code: u8) -> Self { - Self(code) - } -} - -pub struct Process(!); - -impl Process { - pub fn id(&self) -> u32 { - self.0 - } - - pub fn kill(&mut self) -> io::Result<()> { - self.0 - } - - pub fn wait(&mut self) -> io::Result { - self.0 - } - - pub fn try_wait(&mut self) -> io::Result> { - self.0 - } -} - -pub struct CommandArgs<'a> { - iter: crate::slice::Iter<'a, OsString>, -} - -impl<'a> Iterator for CommandArgs<'a> { - type Item = &'a OsStr; - fn next(&mut self) -> Option<&'a OsStr> { - self.iter.next().map(|os| &**os) - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl<'a> ExactSizeIterator for CommandArgs<'a> { - fn len(&self) -> usize { - self.iter.len() - } - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -impl<'a> fmt::Debug for CommandArgs<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter.clone()).finish() - } -} diff --git a/library/std/src/sys/pal/unsupported/stdio.rs b/library/std/src/sys/pal/unsupported/stdio.rs deleted file mode 100644 index b5e3f5be9885b..0000000000000 --- a/library/std/src/sys/pal/unsupported/stdio.rs +++ /dev/null @@ -1,59 +0,0 @@ -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/library/std/src/sys/pal/wasi/args.rs b/library/std/src/sys/pal/wasi/args.rs deleted file mode 100644 index 52cfa202af825..0000000000000 --- a/library/std/src/sys/pal/wasi/args.rs +++ /dev/null @@ -1,61 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use crate::ffi::{CStr, OsStr, OsString}; -use crate::os::wasi::ffi::OsStrExt; -use crate::{fmt, vec}; - -pub struct Args { - iter: vec::IntoIter, -} - -impl !Send for Args {} -impl !Sync for Args {} - -/// Returns the command line arguments -pub fn args() -> Args { - Args { iter: maybe_args().unwrap_or(Vec::new()).into_iter() } -} - -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 fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.iter.as_slice().fmt(f) - } -} - -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/library/std/src/sys/pal/wasi/env.rs b/library/std/src/sys/pal/wasi/env.rs deleted file mode 100644 index 8d44498267360..0000000000000 --- a/library/std/src/sys/pal/wasi/env.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -pub mod os { - pub const FAMILY: &str = ""; - pub const OS: &str = ""; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ".wasm"; - pub const DLL_EXTENSION: &str = "wasm"; - pub const EXE_SUFFIX: &str = ".wasm"; - pub const EXE_EXTENSION: &str = "wasm"; -} diff --git a/library/std/src/sys/pal/wasi/fd.rs b/library/std/src/sys/pal/wasi/fd.rs deleted file mode 100644 index 19b60157e2e00..0000000000000 --- a/library/std/src/sys/pal/wasi/fd.rs +++ /dev/null @@ -1,332 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] -#![allow(dead_code)] - -use super::err2io; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem; -use crate::net::Shutdown; -use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - -#[derive(Debug)] -pub struct WasiFd { - fd: OwnedFd, -} - -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. - // We decorate our `IoSliceMut` with `repr(transparent)` (see `io.rs`), and - // `crate::io::IoSliceMut` is a `repr(transparent)` wrapper around our type, so this is - // guaranteed. - 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. - // We decorate our `IoSlice` with `repr(transparent)` (see `io.rs`), and - // `crate::io::IoSlice` is a `repr(transparent)` wrapper around our type, so this is - // guaranteed. - unsafe { mem::transmute(a) } -} - -impl WasiFd { - pub fn datasync(&self) -> io::Result<()> { - unsafe { wasi::fd_datasync(self.as_raw_fd() as wasi::Fd).map_err(err2io) } - } - - pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - unsafe { wasi::fd_pread(self.as_raw_fd() as wasi::Fd, iovec(bufs), offset).map_err(err2io) } - } - - pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - unsafe { - wasi::fd_pwrite(self.as_raw_fd() as wasi::Fd, ciovec(bufs), offset).map_err(err2io) - } - } - - pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) } - } - - pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { - unsafe { - let bufs = [wasi::Iovec { - buf: buf.as_mut().as_mut_ptr() as *mut u8, - buf_len: buf.capacity(), - }]; - match wasi::fd_read(self.as_raw_fd() as wasi::Fd, &bufs) { - Ok(n) => { - buf.advance_unchecked(n); - Ok(()) - } - Err(e) => Err(err2io(e)), - } - } - } - - pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result { - unsafe { wasi::fd_write(self.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, offset, whence).map_err(err2io) } - } - - pub fn tell(&self) -> io::Result { - unsafe { wasi::fd_tell(self.as_raw_fd() as wasi::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.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, base, inheriting) - .map_err(err2io) - } - } - - pub fn sync(&self) -> io::Result<()> { - unsafe { wasi::fd_sync(self.as_raw_fd() as wasi::Fd).map_err(err2io) } - } - - pub(crate) fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> { - unsafe { - wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io) - } - } - - pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { - unsafe { wasi::fd_allocate(self.as_raw_fd() as wasi::Fd, offset, len).map_err(err2io) } - } - - pub fn create_directory(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, - old_flags, - old_path, - new_fd.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, - dirflags, - path, - oflags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - ) - .map(|fd| WasiFd::from_raw_fd(fd as RawFd)) - .map_err(err2io) - } - } - - pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result { - unsafe { - wasi::fd_readdir(self.as_raw_fd() as wasi::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.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, - old_path, - new_fd.as_raw_fd() as wasi::Fd, - new_path, - ) - .map_err(err2io) - } - } - - pub(crate) fn filestat_get(&self) -> io::Result { - unsafe { wasi::fd_filestat_get(self.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, atim, mtim, fstflags) - .map_err(err2io) - } - } - - pub fn filestat_set_size(&self, size: u64) -> io::Result<()> { - unsafe { wasi::fd_filestat_set_size(self.as_raw_fd() as wasi::Fd, size).map_err(err2io) } - } - - pub(crate) fn path_filestat_get( - &self, - flags: wasi::Lookupflags, - path: &str, - ) -> io::Result { - unsafe { - wasi::path_filestat_get(self.as_raw_fd() as wasi::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.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, new_path).map_err(err2io) - } - } - - pub fn unlink_file(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_unlink_file(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } - } - - pub fn remove_directory(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } - } - - pub fn sock_accept(&self, flags: wasi::Fdflags) -> io::Result { - unsafe { wasi::sock_accept(self.as_raw_fd() as wasi::Fd, flags).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.as_raw_fd() as wasi::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.as_raw_fd() as wasi::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.as_raw_fd() as wasi::Fd, how).map_err(err2io) } - } -} - -impl AsInner for WasiFd { - #[inline] - fn as_inner(&self) -> &OwnedFd { - &self.fd - } -} - -impl AsInnerMut for WasiFd { - #[inline] - fn as_inner_mut(&mut self) -> &mut OwnedFd { - &mut self.fd - } -} - -impl IntoInner for WasiFd { - fn into_inner(self) -> OwnedFd { - self.fd - } -} - -impl FromInner for WasiFd { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self { fd: owned_fd } - } -} - -impl AsFd for WasiFd { - fn as_fd(&self) -> BorrowedFd<'_> { - self.fd.as_fd() - } -} - -impl AsRawFd for WasiFd { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.fd.as_raw_fd() - } -} - -impl IntoRawFd for WasiFd { - fn into_raw_fd(self) -> RawFd { - self.fd.into_raw_fd() - } -} - -impl FromRawFd for WasiFd { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } } - } -} diff --git a/library/std/src/sys/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs deleted file mode 100644 index 39978346d7382..0000000000000 --- a/library/std/src/sys/pal/wasi/fs.rs +++ /dev/null @@ -1,872 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use super::fd::WasiFd; -use crate::ffi::{CStr, OsStr, OsString}; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem::{self, ManuallyDrop}; -use crate::os::raw::c_int; -use crate::os::wasi::ffi::{OsStrExt, OsStringExt}; -use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::path::{Path, PathBuf}; -use crate::sync::Arc; -use crate::sys::common::small_c_string::run_path_with_cstr; -use crate::sys::time::SystemTime; -use crate::sys::unsupported; -pub use crate::sys_common::fs::exists; -use crate::sys_common::{AsInner, FromInner, IntoInner, ignore_notfound}; -use crate::{fmt, iter, ptr}; - -pub struct File { - fd: WasiFd, -} - -#[derive(Clone)] -pub struct FileAttr { - meta: wasi::Filestat, -} - -pub struct ReadDir { - inner: Arc, - state: ReadDirState, -} - -enum ReadDirState { - /// Fill `buf` with `buf.len()` bytes starting from `next_read_offset`. - FillBuffer { - next_read_offset: wasi::Dircookie, - buf: Vec, - }, - ProcessEntry { - buf: Vec, - next_read_offset: Option, - offset: usize, - }, - /// There is no more data to get in [`Self::FillBuffer`]; keep returning - /// entries via ProcessEntry until `buf` is exhausted. - RunUntilExhaustion { - buf: Vec, - offset: usize, - }, - Done, -} - -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(Copy, Clone, Debug, Default)] -pub struct FileTimes { - accessed: Option, - modified: Option, -} - -#[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(crate) 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 FileTimes { - pub fn set_accessed(&mut self, t: SystemTime) { - self.accessed = Some(t); - } - - pub fn set_modified(&mut self, t: SystemTime) { - self.modified = Some(t); - } -} - -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(crate) fn bits(&self) -> wasi::Filetype { - self.bits - } -} - -impl ReadDir { - fn new(dir: File, root: PathBuf) -> ReadDir { - ReadDir { - inner: Arc::new(ReadDirInner { dir, root }), - state: ReadDirState::FillBuffer { next_read_offset: 0, buf: vec![0; 128] }, - } - } -} - -impl fmt::Debug for ReadDir { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ReadDir").finish_non_exhaustive() - } -} - -impl core::iter::FusedIterator for ReadDir {} - -impl Iterator for ReadDir { - type Item = io::Result; - - fn next(&mut self) -> Option> { - match &mut self.state { - ReadDirState::FillBuffer { next_read_offset, buf } => { - let result = self.inner.dir.fd.readdir(buf, *next_read_offset); - match result { - Ok(read_bytes) => { - if read_bytes < buf.len() { - buf.truncate(read_bytes); - self.state = - ReadDirState::RunUntilExhaustion { buf: mem::take(buf), offset: 0 }; - } else { - debug_assert_eq!(read_bytes, buf.len()); - self.state = ReadDirState::ProcessEntry { - buf: mem::take(buf), - offset: 0, - next_read_offset: Some(*next_read_offset), - }; - } - self.next() - } - Err(e) => { - self.state = ReadDirState::Done; - return Some(Err(e)); - } - } - } - ReadDirState::ProcessEntry { buf, next_read_offset, offset } => { - let contents = &buf[*offset..]; - const DIRENT_SIZE: usize = crate::mem::size_of::(); - if contents.len() >= DIRENT_SIZE { - let (dirent, data) = contents.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. - if data.len() < dirent.d_namlen as usize { - if buf.len() < dirent.d_namlen as usize + DIRENT_SIZE { - buf.resize(dirent.d_namlen as usize + DIRENT_SIZE, 0); - } - if let Some(next_read_offset) = *next_read_offset { - self.state = - ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; - } else { - self.state = ReadDirState::Done; - } - - return self.next(); - } - next_read_offset.as_mut().map(|cookie| { - *cookie = dirent.d_next; - }); - *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".." { - return self.next(); - } - - return Some(Ok(DirEntry { - meta: dirent, - name: name.to_vec(), - inner: self.inner.clone(), - })); - } else if let Some(next_read_offset) = *next_read_offset { - self.state = ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; - } else { - self.state = ReadDirState::Done; - } - self.next() - } - ReadDirState::RunUntilExhaustion { buf, offset } => { - if *offset >= buf.len() { - self.state = ReadDirState::Done; - } else { - self.state = ReadDirState::ProcessEntry { - buf: mem::take(buf), - offset: *offset, - next_read_offset: None, - }; - } - - self.next() - } - ReadDirState::Done => None, - } - } -} - -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; - 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; - - 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 lock(&self) -> io::Result<()> { - unsupported() - } - - pub fn lock_shared(&self) -> io::Result<()> { - unsupported() - } - - pub fn try_lock(&self) -> io::Result { - unsupported() - } - - pub fn try_lock_shared(&self) -> io::Result { - unsupported() - } - - pub fn unlock(&self) -> io::Result<()> { - unsupported() - } - - 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 read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.fd.read_buf(cursor) - } - - 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 tell(&self) -> io::Result { - self.fd.tell() - } - - 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 set_times(&self, times: FileTimes) -> io::Result<()> { - let to_timestamp = |time: Option| match time { - Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), - Some(_) => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time", - )), - None => Ok(0), - }; - self.fd.filestat_set_times( - to_timestamp(times.accessed)?, - to_timestamp(times.modified)?, - times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM) - | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM), - ) - } - - pub fn read_link(&self, file: &Path) -> io::Result { - read_link(&self.fd, file) - } -} - -impl AsInner for File { - #[inline] - fn as_inner(&self) -> &WasiFd { - &self.fd - } -} - -impl IntoInner for File { - fn into_inner(self) -> WasiFd { - self.fd - } -} - -impl FromInner for File { - fn from_inner(fd: WasiFd) -> File { - File { fd } - } -} - -impl AsFd for File { - fn as_fd(&self) -> BorrowedFd<'_> { - self.fd.as_fd() - } -} - -impl AsRawFd for File { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.fd.as_raw_fd() - } -} - -impl IntoRawFd for File { - fn into_raw_fd(self) -> RawFd { - self.fd.into_raw_fd() - } -} - -impl FromRawFd for File { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self { fd: FromRawFd::from_raw_fd(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.as_raw_fd()).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::new(dir, 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(original: &Path, link: &Path) -> io::Result<()> { - let (link, link_file) = open_parent(link)?; - link.symlink(osstr2str(original.as_ref())?, osstr2str(link_file.as_ref())?) -} - -pub fn link(original: &Path, link: &Path) -> io::Result<()> { - let (original, original_file) = open_parent(original)?; - let (link, link_file) = open_parent(link)?; - // Pass 0 as the flags argument, meaning don't follow symlinks. - original.link(0, osstr2str(original_file.as_ref())?, &link, osstr2str(link_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)> { - run_path_with_cstr(p, &|p| { - let mut buf = Vec::::with_capacity(512); - loop { - unsafe { - let mut relative_path = buf.as_ptr().cast(); - let mut abs_prefix = ptr::null(); - let fd = __wasilibc_find_relpath( - p.as_ptr(), - &mut abs_prefix, - &mut relative_path, - buf.capacity(), - ); - if fd == -1 { - if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) { - // 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); - continue; - } - let msg = format!( - "failed to find a pre-opened file descriptor \ - through which {p:?} could be opened", - ); - return Err(io::Error::new(io::ErrorKind::Uncategorized, msg)); - } - let relative = CStr::from_ptr(relative_path).to_bytes().to_vec(); - - return Ok(( - ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)), - PathBuf::from(OsString::from_vec(relative)), - )); - } - } - - unsafe extern "C" { - pub fn __wasilibc_find_relpath( - path: *const libc::c_char, - abs_prefix: *mut *const libc::c_char, - relative_path: *mut *const libc::c_char, - relative_path_len: libc::size_t, - ) -> libc::c_int; - } - }) -} - -pub fn osstr2str(f: &OsStr) -> io::Result<&str> { - f.to_str().ok_or_else(|| io::const_error!(io::ErrorKind::Uncategorized, "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) -} - -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - let (parent, path) = open_parent(path)?; - remove_dir_all_recursive(&parent, &path) -} - -fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { - // Open up a file descriptor for the directory itself. Note that we don't - // follow symlinks here and we specifically open directories. - // - // At the root invocation of this function this will correctly handle - // symlinks passed to the top-level `remove_dir_all`. At the recursive - // level this will double-check that after the `readdir` call deduced this - // was a directory it's still a directory by the time we open it up. - // - // If the opened file was actually a symlink then the symlink is deleted, - // not the directory recursively. - let mut opts = OpenOptions::new(); - opts.lookup_flags(0); - opts.directory(true); - opts.read(true); - let fd = open_at(parent, path, &opts)?; - if fd.file_attr()?.file_type().is_symlink() { - return parent.unlink_file(osstr2str(path.as_ref())?); - } - - // this "root" is only used by `DirEntry::path` which we don't use below so - // it's ok for this to be a bogus value - let dummy_root = PathBuf::new(); - - // Iterate over all the entries in this directory, and travel recursively if - // necessary - for entry in ReadDir::new(fd, dummy_root) { - let entry = entry?; - let path = crate::str::from_utf8(&entry.name).map_err(|_| { - io::const_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found") - })?; - - let result: io::Result<()> = try { - if entry.file_type()?.is_dir() { - remove_dir_all_recursive(&entry.inner.dir.fd, path.as_ref())?; - } else { - entry.inner.dir.fd.unlink_file(path)?; - } - }; - // ignore internal NotFound errors - if let Err(err) = &result - && err.kind() != io::ErrorKind::NotFound - { - return result; - } - } - - // Once all this directory's contents are deleted it should be safe to - // delete the directory tiself. - ignore_notfound(parent.remove_directory(osstr2str(path.as_ref())?)) -} diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index f4588a60ea9a4..61dd1c3f98b10 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -13,10 +13,6 @@ //! compiling for wasm. That way it's a compile time error for something that's //! guaranteed to be a runtime error! -pub mod args; -pub mod env; -pub mod fd; -pub mod fs; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; @@ -24,9 +20,6 @@ pub mod futex; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/process.rs"] -pub mod process; -pub mod stdio; pub mod thread; pub mod time; diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index ba2b65a1f40dc..672cf70d1a5b2 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -1,19 +1,16 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use core::slice::memchr; - use crate::error::Error as StdError; use crate::ffi::{CStr, OsStr, OsString}; use crate::marker::PhantomData; -use crate::ops::Drop; use crate::os::wasi::prelude::*; use crate::path::{self, PathBuf}; -use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; +use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::unsupported; -use crate::{fmt, io, str, vec}; +use crate::{fmt, io, str}; // Add a few symbols not in upstream `libc` just yet. -mod libc { +pub mod libc { pub use libc::*; unsafe extern "C" { @@ -23,28 +20,6 @@ mod libc { } } -cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { - // Access to the environment must be protected by a lock in multi-threaded scenarios. - use crate::sync::{PoisonError, RwLock}; - static ENV_LOCK: RwLock<()> = RwLock::new(()); - pub fn env_read_lock() -> impl Drop { - ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) - } - pub fn env_write_lock() -> impl Drop { - ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner) - } - } else { - // No need for a lock if we are single-threaded. - pub fn env_read_lock() -> impl Drop { - Box::new(()) - } - pub fn env_write_lock() -> impl Drop { - Box::new(()) - } - } -} - pub fn errno() -> i32 { unsafe extern "C" { #[thread_local] @@ -141,123 +116,6 @@ pub fn current_exe() -> io::Result { unsupported() } -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -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_read_lock(); - - // Use `__wasilibc_get_environ` instead of `environ` here so that we - // don't require wasi-libc to eagerly initialize the environment - // variables. - let mut environ = libc::__wasilibc_get_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() }; - } - - // See src/libstd/sys/pal/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) -> Option { - // environment variables with a nul byte can't be set, so their value is - // always None as well - run_with_cstr(k.as_bytes(), &|k| { - let _guard = env_read_lock(); - let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - - if v.is_null() { - Ok(None) - } else { - // SAFETY: `v` cannot be mutated while executing this line since we've a read lock - let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); - - Ok(Some(OsStringExt::from_vec(bytes))) - } - }) - .ok() - .flatten() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), &|k| { - run_with_cstr(v.as_bytes(), &|v| unsafe { - let _guard = env_write_lock(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - }) - }) -} - -pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), &|nbuf| unsafe { - let _guard = env_write_lock(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - }) -} - #[allow(dead_code)] pub fn page_size() -> usize { unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } @@ -294,6 +152,6 @@ macro_rules! impl_is_minus_one { impl_is_minus_one! { i8 i16 i32 i64 isize } -fn cvt(t: T) -> io::Result { +pub fn cvt(t: T) -> io::Result { if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } } diff --git a/library/std/src/sys/pal/wasi/stdio.rs b/library/std/src/sys/pal/wasi/stdio.rs deleted file mode 100644 index fb21cb4d393d0..0000000000000 --- a/library/std/src/sys/pal/wasi/stdio.rs +++ /dev/null @@ -1,117 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use super::fd::WasiFd; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::mem::ManuallyDrop; -use crate::os::raw; -use crate::os::wasi::io::{AsRawFd, FromRawFd}; - -pub struct Stdin; -pub struct Stdout; -pub struct Stderr; - -impl Stdin { - pub const fn new() -> Stdin { - Stdin - } -} - -impl AsRawFd for Stdin { - #[inline] - fn as_raw_fd(&self) -> raw::c_int { - 0 - } -} - -impl io::Read for Stdin { - fn read(&mut self, data: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(data)]) - } - - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).read_buf(buf) - } - - fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result { - ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).read(data) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - true - } -} - -impl Stdout { - pub const fn new() -> Stdout { - Stdout - } -} - -impl AsRawFd for Stdout { - #[inline] - fn as_raw_fd(&self) -> raw::c_int { - 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_fd(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 - } -} - -impl AsRawFd for Stderr { - #[inline] - fn as_raw_fd(&self) -> raw::c_int { - 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_fd(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::io::DEFAULT_BUF_SIZE; - -pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(wasi::ERRNO_BADF.raw().into()) -} - -pub fn panic_output() -> Option { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 0ae0236941061..cc569bb3daf68 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -13,16 +13,15 @@ cfg_if::cfg_if! { // Add a few symbols not in upstream `libc` just yet. mod libc { pub use crate::ffi; - pub use crate::mem; pub use libc::*; // defined in wasi-libc // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108 #[repr(C)] union pthread_attr_union { - __i: [ffi::c_int; if mem::size_of::() == 8 { 14 } else { 9 }], - __vi: [ffi::c_int; if mem::size_of::() == 8 { 14 } else { 9 }], - __s: [ffi::c_ulong; if mem::size_of::() == 8 { 7 } else { 9 }], + __i: [ffi::c_int; if size_of::() == 8 { 14 } else { 9 }], + __vi: [ffi::c_int; if size_of::() == 8 { 14 } else { 9 }], + __s: [ffi::c_ulong; if size_of::() == 8 { 7 } else { 9 }], } #[repr(C)] @@ -68,7 +67,7 @@ cfg_if::cfg_if! { } } -pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; +pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 72c9742b2e549..47fe3221c9093 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -6,14 +6,6 @@ //! To begin with, this target mirrors the wasi target 1 to 1, but over //! time this will change significantly. -#[path = "../wasi/args.rs"] -pub mod args; -#[path = "../wasi/env.rs"] -pub mod env; -#[path = "../wasi/fd.rs"] -pub mod fd; -#[path = "../wasi/fs.rs"] -pub mod fs; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; @@ -22,10 +14,6 @@ pub mod futex; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/process.rs"] -pub mod process; -#[path = "../wasi/stdio.rs"] -pub mod stdio; #[path = "../wasi/thread.rs"] pub mod thread; #[path = "../wasi/time.rs"] @@ -45,7 +33,6 @@ mod helpers; // import conflict rules. If we glob export `helpers` and `common` together, // then the compiler complains about conflicts. -use helpers::err2io; -pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; +pub(crate) use helpers::{abort_internal, decode_error_kind, err2io, is_interrupted}; mod cabi_realloc; diff --git a/library/std/src/sys/pal/wasm/atomics/futex.rs b/library/std/src/sys/pal/wasm/atomics/futex.rs index bdad0da73f0a5..6676aa7e8e3a5 100644 --- a/library/std/src/sys/pal/wasm/atomics/futex.rs +++ b/library/std/src/sys/pal/wasm/atomics/futex.rs @@ -3,16 +3,16 @@ use core::arch::wasm32 as wasm; #[cfg(target_arch = "wasm64")] use core::arch::wasm64 as wasm; -use crate::sync::atomic::AtomicU32; +use crate::sync::atomic::Atomic; use crate::time::Duration; /// An atomic for use as a futex that is at least 32-bits but may be larger -pub type Futex = AtomicU32; +pub type Futex = Atomic; /// Must be the underlying type of Futex pub type Primitive = u32; /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallFutex = AtomicU32; +pub type SmallFutex = Atomic; /// Must be the underlying type of SmallFutex pub type SmallPrimitive = u32; @@ -21,11 +21,14 @@ pub type SmallPrimitive = u32; /// Returns directly if the futex doesn't hold the expected value. /// /// Returns false on timeout, and true in all other cases. -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { +pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) -> bool { let timeout = timeout.and_then(|t| t.as_nanos().try_into().ok()).unwrap_or(-1); unsafe { - wasm::memory_atomic_wait32(futex as *const AtomicU32 as *mut i32, expected as i32, timeout) - < 2 + wasm::memory_atomic_wait32( + futex as *const Atomic as *mut i32, + expected as i32, + timeout, + ) < 2 } } @@ -33,13 +36,13 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - /// /// Returns true if this actually woke up such a thread, /// or false if no thread was waiting on this futex. -pub fn futex_wake(futex: &AtomicU32) -> bool { - unsafe { wasm::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, 1) > 0 } +pub fn futex_wake(futex: &Atomic) -> bool { + unsafe { wasm::memory_atomic_notify(futex as *const Atomic as *mut i32, 1) > 0 } } /// Wakes up all threads that are waiting on `futex_wait` on this futex. -pub fn futex_wake_all(futex: &AtomicU32) { +pub fn futex_wake_all(futex: &Atomic) { unsafe { - wasm::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, i32::MAX as u32); + wasm::memory_atomic_notify(futex as *const Atomic as *mut i32, i32::MAX as u32); } } diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs index afdb159fe6f8b..dd5aff391fd8b 100644 --- a/library/std/src/sys/pal/wasm/atomics/thread.rs +++ b/library/std/src/sys/pal/wasm/atomics/thread.rs @@ -6,7 +6,7 @@ use crate::time::Duration; pub struct Thread(!); -pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; +pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements diff --git a/library/std/src/sys/pal/wasm/env.rs b/library/std/src/sys/pal/wasm/env.rs deleted file mode 100644 index 730e356d7fe95..0000000000000 --- a/library/std/src/sys/pal/wasm/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = ""; - pub const OS: &str = ""; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ".wasm"; - pub const DLL_EXTENSION: &str = "wasm"; - pub const EXE_SUFFIX: &str = ".wasm"; - pub const EXE_EXTENSION: &str = "wasm"; -} diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 32d59c4d0f7c4..37cb46a8f6b3f 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -16,19 +16,10 @@ #![deny(unsafe_op_in_unsafe_fn)] -#[path = "../unsupported/args.rs"] -pub mod args; -pub mod env; -#[path = "../unsupported/fs.rs"] -pub mod fs; #[path = "../unsupported/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/process.rs"] -pub mod process; -#[path = "../unsupported/stdio.rs"] -pub mod stdio; #[path = "../unsupported/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index ebe207fde935c..6b5f9aeace28a 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -137,7 +137,7 @@ pub const fn to_utf16(s: &str) -> [u16; UTF16_LEN] { /// use frequent `as` casts. This is risky because they are too powerful. /// For example, the following will compile today: /// -/// `std::mem::size_of:: as u32` +/// `size_of:: as u32` /// /// Note that `size_of` is never actually called, instead a function pointer is /// converted to a `u32`. Clippy would warn about this but, alas, it's not run @@ -147,7 +147,7 @@ const fn win32_size_of() -> u32 { // Uses a trait to workaround restriction on using generic types in inner items. trait Win32SizeOf: Sized { const WIN32_SIZE_OF: u32 = { - let size = core::mem::size_of::(); + let size = size_of::(); assert!(size <= u32::MAX as usize); size as u32 }; diff --git a/library/std/src/sys/pal/windows/args.rs b/library/std/src/sys/pal/windows/args.rs deleted file mode 100644 index 3447a0157e4c5..0000000000000 --- a/library/std/src/sys/pal/windows/args.rs +++ /dev/null @@ -1,445 +0,0 @@ -//! The Windows command line is just a string -//! -//! -//! This module implements the parsing necessary to turn that string into a list of arguments. - -#[cfg(test)] -mod tests; - -use super::os::current_exe; -use crate::ffi::{OsStr, OsString}; -use crate::num::NonZero; -use crate::os::windows::prelude::*; -use crate::path::{Path, PathBuf}; -use crate::sys::path::get_long_path; -use crate::sys::process::ensure_no_nuls; -use crate::sys::{c, to_u16s}; -use crate::sys_common::AsInner; -use crate::sys_common::wstr::WStrUnits; -use crate::{fmt, io, iter, vec}; - -pub fn args() -> Args { - // SAFETY: `GetCommandLineW` returns a pointer to a null terminated UTF-16 - // string so it's safe for `WStrUnits` to use. - unsafe { - let lp_cmd_line = c::GetCommandLineW(); - let parsed_args_list = parse_lp_cmd_line(WStrUnits::new(lp_cmd_line), || { - 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 -/// -/// -/// A more in-depth explanation is here: -/// -/// -/// Windows includes a function to do command line parsing in shell32.dll. -/// However, this is not used for two reasons: -/// -/// 1. 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 -/// . -/// -/// 2. It does not follow the modern C/C++ argv rules outlined in the first two links above. -/// -/// This function was tested for equivalence to the C/C++ parsing rules using an -/// extensive test suite available at -/// . -fn parse_lp_cmd_line<'a, F: Fn() -> OsString>( - lp_cmd_line: Option>, - exe_name: F, -) -> Vec { - const BACKSLASH: NonZero = NonZero::new(b'\\' as u16).unwrap(); - const QUOTE: NonZero = NonZero::new(b'"' as u16).unwrap(); - const TAB: NonZero = NonZero::new(b'\t' as u16).unwrap(); - const SPACE: NonZero = NonZero::new(b' ' as u16).unwrap(); - - let mut ret_val = Vec::new(); - // If the cmd line pointer is null or it points to an empty string then - // return the name of the executable as argv[0]. - if lp_cmd_line.as_ref().and_then(|cmd| cmd.peek()).is_none() { - ret_val.push(exe_name()); - return ret_val; - } - let mut code_units = lp_cmd_line.unwrap(); - - // The executable name at the beginning is special. - let mut in_quotes = false; - let mut cur = Vec::new(); - for w in &mut code_units { - match w { - // A quote mark always toggles `in_quotes` no matter what because - // there are no escape characters when parsing the executable name. - QUOTE => in_quotes = !in_quotes, - // If not `in_quotes` then whitespace ends argv[0]. - SPACE | TAB if !in_quotes => break, - // In all other cases the code unit is taken literally. - _ => cur.push(w.get()), - } - } - // Skip whitespace. - code_units.advance_while(|w| w == SPACE || w == TAB); - ret_val.push(OsString::from_wide(&cur)); - - // Parse the arguments according to these rules: - // * All code units are taken literally except space, tab, quote and backslash. - // * When not `in_quotes`, space and tab separate arguments. Consecutive spaces and tabs are - // treated as a single separator. - // * A space or tab `in_quotes` is taken literally. - // * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally. - // * A quote can be escaped if preceded by an odd number of backslashes. - // * If any number of backslashes is immediately followed by a quote then the number of - // backslashes is halved (rounding down). - // * Backslashes not followed by a quote are all taken literally. - // * If `in_quotes` then a quote can also be escaped using another quote - // (i.e. two consecutive quotes become one literal quote). - let mut cur = Vec::new(); - let mut in_quotes = false; - while let Some(w) = code_units.next() { - match w { - // If not `in_quotes`, a space or tab ends the argument. - SPACE | TAB if !in_quotes => { - ret_val.push(OsString::from_wide(&cur[..])); - cur.truncate(0); - - // Skip whitespace. - code_units.advance_while(|w| w == SPACE || w == TAB); - } - // Backslashes can escape quotes or backslashes but only if consecutive backslashes are followed by a quote. - BACKSLASH => { - let backslash_count = code_units.advance_while(|w| w == BACKSLASH) + 1; - if code_units.peek() == Some(QUOTE) { - cur.extend(iter::repeat(BACKSLASH.get()).take(backslash_count / 2)); - // The quote is escaped if there are an odd number of backslashes. - if backslash_count % 2 == 1 { - code_units.next(); - cur.push(QUOTE.get()); - } - } else { - // If there is no quote on the end then there is no escaping. - cur.extend(iter::repeat(BACKSLASH.get()).take(backslash_count)); - } - } - // If `in_quotes` and not backslash escaped (see above) then a quote either - // unsets `in_quote` or is escaped by another quote. - QUOTE if in_quotes => match code_units.peek() { - // Two consecutive quotes when `in_quotes` produces one literal quote. - Some(QUOTE) => { - cur.push(QUOTE.get()); - code_units.next(); - } - // Otherwise set `in_quotes`. - Some(_) => in_quotes = false, - // The end of the command line. - // Push `cur` even if empty, which we do by breaking while `in_quotes` is still set. - None => break, - }, - // If not `in_quotes` and not BACKSLASH escaped (see above) then a quote sets `in_quote`. - QUOTE => in_quotes = true, - // Everything else is always taken literally. - _ => cur.push(w.get()), - } - } - // Push the final argument, if any. - if !cur.is_empty() || in_quotes { - ret_val.push(OsString::from_wide(&cur[..])); - } - ret_val -} - -pub struct Args { - parsed_args_list: vec::IntoIter, -} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.parsed_args_list.as_slice().fmt(f) - } -} - -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() - } -} - -#[derive(Debug)] -pub(crate) enum Arg { - /// Add quotes (if needed) - Regular(OsString), - /// Append raw string without quoting - Raw(OsString), -} - -enum Quote { - // Every arg is quoted - Always, - // Whitespace and empty args are quoted - Auto, - // Arg appended without any changes (#29494) - Never, -} - -pub(crate) fn append_arg(cmd: &mut Vec, arg: &Arg, force_quotes: bool) -> io::Result<()> { - let (arg, quote) = match arg { - Arg::Regular(arg) => (arg, if force_quotes { Quote::Always } else { Quote::Auto }), - Arg::Raw(arg) => (arg, Quote::Never), - }; - - // 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_encoded_bytes(); - let (quote, escape) = match quote { - Quote::Always => (true, true), - Quote::Auto => { - (arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') || arg_bytes.is_empty(), true) - } - Quote::Never => (false, false), - }; - if quote { - cmd.push('"' as u16); - } - - let mut backslashes: usize = 0; - for x in arg.encode_wide() { - if escape { - 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 append_bat_arg(cmd: &mut Vec, arg: &OsStr, mut quote: bool) -> io::Result<()> { - ensure_no_nuls(arg)?; - // 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. - // - // We also need to quote the argument if it ends with `\` to guard against - // bat usage such as `"%~2"` (i.e. force quote arguments) otherwise a - // trailing slash will escape the closing quote. - if arg.is_empty() || arg.as_encoded_bytes().last() == Some(&b'\\') { - quote = true; - } - for cp in arg.as_inner().inner.code_points() { - if let Some(cp) = cp.to_char() { - // Rather than trying to find every ascii symbol that must be quoted, - // we assume that all ascii symbols must be quoted unless they're known to be good. - // We also quote Unicode control blocks for good measure. - // Note an unquoted `\` is fine so long as the argument isn't otherwise quoted. - static UNQUOTED: &str = r"#$*+-./:?@\_"; - let ascii_needs_quotes = - cp.is_ascii() && !(cp.is_ascii_alphanumeric() || UNQUOTED.contains(cp)); - if ascii_needs_quotes || cp.is_control() { - quote = true; - } - } - } - - if quote { - cmd.push('"' as u16); - } - // Loop through the string, escaping `\` only if followed by `"`. - // And escaping `"` by doubling them. - let mut backslashes: usize = 0; - for x in arg.encode_wide() { - if x == '\\' as u16 { - backslashes += 1; - } else { - if x == '"' as u16 { - // Add n backslashes to total 2n before internal `"`. - cmd.extend((0..backslashes).map(|_| '\\' as u16)); - // Appending an additional double-quote acts as an escape. - cmd.push(b'"' as u16) - } else if x == '%' as u16 || x == '\r' as u16 { - // yt-dlp hack: replaces `%` with `%%cd:~,%` to stop %VAR% being expanded as an environment variable. - // - // # Explanation - // - // cmd supports extracting a substring from a variable using the following syntax: - // %variable:~start_index,end_index% - // - // In the above command `cd` is used as the variable and the start_index and end_index are left blank. - // `cd` is a built-in variable that dynamically expands to the current directory so it's always available. - // Explicitly omitting both the start and end index creates a zero-length substring. - // - // Therefore it all resolves to nothing. However, by doing this no-op we distract cmd.exe - // from potentially expanding %variables% in the argument. - cmd.extend_from_slice(&[ - '%' as u16, '%' as u16, 'c' as u16, 'd' as u16, ':' as u16, '~' as u16, - ',' 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(()) -} - -pub(crate) fn make_bat_command_line( - script: &[u16], - args: &[Arg], - force_quotes: bool, -) -> io::Result> { - const INVALID_ARGUMENT_ERROR: io::Error = - io::const_error!(io::ErrorKind::InvalidInput, r#"batch file arguments are invalid"#); - // Set the start of the command line to `cmd.exe /c "` - // It is necessary to surround the command in an extra pair of quotes, - // hence the trailing quote here. It will be closed after all arguments - // have been added. - // Using /e:ON enables "command extensions" which is essential for the `%` hack to work. - let mut cmd: Vec = "cmd.exe /e:ON /v:OFF /d /c \"".encode_utf16().collect(); - - // Push the script name surrounded by its quote pair. - cmd.push(b'"' as u16); - // Windows file names cannot contain a `"` character or end with `\\`. - // If the script name does then return an error. - if script.contains(&(b'"' as u16)) || script.last() == Some(&(b'\\' as u16)) { - return Err(io::const_error!( - io::ErrorKind::InvalidInput, - "Windows file names may not contain `\"` or end with `\\`" - )); - } - cmd.extend_from_slice(script.strip_suffix(&[0]).unwrap_or(script)); - cmd.push(b'"' as u16); - - // Append the arguments. - // FIXME: This needs tests to ensure that the arguments are properly - // reconstructed by the batch script by default. - for arg in args { - cmd.push(' ' as u16); - match arg { - Arg::Regular(arg_os) => { - let arg_bytes = arg_os.as_encoded_bytes(); - // Disallow \r and \n as they may truncate the arguments. - const DISALLOWED: &[u8] = b"\r\n"; - if arg_bytes.iter().any(|c| DISALLOWED.contains(c)) { - return Err(INVALID_ARGUMENT_ERROR); - } - append_bat_arg(&mut cmd, arg_os, force_quotes)?; - } - _ => { - // Raw arguments are passed on as-is. - // It's the user's responsibility to properly handle arguments in this case. - append_arg(&mut cmd, arg, force_quotes)?; - } - }; - } - - // Close the quote we left opened earlier. - cmd.push(b'"' as u16); - - Ok(cmd) -} - -/// Takes a path and tries to return a non-verbatim path. -/// -/// This is necessary because cmd.exe does not support verbatim paths. -pub(crate) fn to_user_path(path: &Path) -> io::Result> { - from_wide_to_user_path(to_u16s(path)?) -} -pub(crate) fn from_wide_to_user_path(mut path: Vec) -> io::Result> { - use super::fill_utf16_buf; - use crate::ptr; - - // UTF-16 encoded code points, used in parsing and building UTF-16 paths. - // All of these are in the ASCII range so they can be cast directly to `u16`. - const SEP: u16 = b'\\' as _; - const QUERY: u16 = b'?' as _; - const COLON: u16 = b':' as _; - const U: u16 = b'U' as _; - const N: u16 = b'N' as _; - const C: u16 = b'C' as _; - - // Early return if the path is too long to remove the verbatim prefix. - const LEGACY_MAX_PATH: usize = 260; - if path.len() > LEGACY_MAX_PATH { - return Ok(path); - } - - match &path[..] { - // `\\?\C:\...` => `C:\...` - [SEP, SEP, QUERY, SEP, _, COLON, SEP, ..] => unsafe { - let lpfilename = path[4..].as_ptr(); - fill_utf16_buf( - |buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()), - |full_path: &[u16]| { - if full_path == &path[4..path.len() - 1] { - let mut path: Vec = full_path.into(); - path.push(0); - path - } else { - path - } - }, - ) - }, - // `\\?\UNC\...` => `\\...` - [SEP, SEP, QUERY, SEP, U, N, C, SEP, ..] => unsafe { - // Change the `C` in `UNC\` to `\` so we can get a slice that starts with `\\`. - path[6] = b'\\' as u16; - let lpfilename = path[6..].as_ptr(); - fill_utf16_buf( - |buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()), - |full_path: &[u16]| { - if full_path == &path[6..path.len() - 1] { - let mut path: Vec = full_path.into(); - path.push(0); - path - } else { - // Restore the 'C' in "UNC". - path[6] = b'C' as u16; - path - } - }, - ) - }, - // For everything else, leave the path unchanged. - _ => get_long_path(path, false), - } -} diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 4fbdc839939c9..ac1c5e9932e1c 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -6,7 +6,7 @@ #![allow(clippy::style)] use core::ffi::{CStr, c_uint, c_ulong, c_ushort, c_void}; -use core::{mem, ptr}; +use core::ptr; mod windows_sys; pub use windows_sys::*; @@ -39,15 +39,15 @@ pub fn nt_success(status: NTSTATUS) -> bool { impl UNICODE_STRING { pub fn from_ref(slice: &[u16]) -> Self { - let len = mem::size_of_val(slice); + let len = size_of_val(slice); Self { Length: len as _, MaximumLength: len as _, Buffer: slice.as_ptr() as _ } } } -impl Default for OBJECT_ATTRIBUTES { - fn default() -> Self { +impl OBJECT_ATTRIBUTES { + pub fn with_length() -> Self { Self { - Length: mem::size_of::() as _, + Length: size_of::() as _, RootDirectory: ptr::null_mut(), ObjectName: ptr::null_mut(), Attributes: 0, @@ -237,6 +237,17 @@ compat_fn_with_fallback! { STATUS_NOT_IMPLEMENTED } #[cfg(target_vendor = "uwp")] + pub fn NtOpenFile( + filehandle: *mut HANDLE, + desiredaccess: u32, + objectattributes: *const OBJECT_ATTRIBUTES, + iostatusblock: *mut IO_STATUS_BLOCK, + shareaccess: u32, + openoptions: u32 + ) -> NTSTATUS { + STATUS_NOT_IMPLEMENTED + } + #[cfg(target_vendor = "uwp")] pub fn NtReadFile( filehandle: HANDLE, event: HANDLE, diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index e2c2163327968..d5fbb453c6f96 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -1,7 +1,8 @@ --out windows_sys.rs --flat --sys ---no-core +--no-deps +--link windows_targets --filter !INVALID_HANDLE_VALUE ABOVE_NORMAL_PRIORITY_CLASS @@ -19,7 +20,6 @@ ALL_PROCESSOR_GROUPS ARM64_NT_NEON128 BELOW_NORMAL_PRIORITY_CLASS bind -BOOL BY_HANDLE_FILE_INFORMATION CALLBACK_CHUNK_FINISHED CALLBACK_STREAM_SWITCH diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 1d0e89f5d0f0e..eb2914b864473 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.59.0 +// Bindings generated by `windows-bindgen` 0.61.0 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] @@ -141,7 +141,7 @@ windows_targets::link!("ws2_32.dll" "system" fn setsockopt(s : SOCKET, level : i windows_targets::link!("ws2_32.dll" "system" fn shutdown(s : SOCKET, how : WINSOCK_SHUTDOWN_HOW) -> i32); pub const ABOVE_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 32768u32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct ACL { pub AclRevision: u8, pub Sbz1: u8, @@ -162,6 +162,11 @@ pub struct ADDRINFOA { pub ai_addr: *mut SOCKADDR, pub ai_next: *mut ADDRINFOA, } +impl Default for ADDRINFOA { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const AF_INET: ADDRESS_FAMILY = 2u16; pub const AF_INET6: ADDRESS_FAMILY = 23u16; pub const AF_UNIX: u16 = 1u16; @@ -176,8 +181,13 @@ pub union ARM64_NT_NEON128 { pub H: [u16; 8], pub B: [u8; 16], } +impl Default for ARM64_NT_NEON128 { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct ARM64_NT_NEON128_0 { pub Low: u64, pub High: i64, @@ -185,7 +195,7 @@ pub struct ARM64_NT_NEON128_0 { pub const BELOW_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 16384u32; pub type BOOL = i32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct BY_HANDLE_FILE_INFORMATION { pub dwFileAttributes: u32, pub ftCreationTime: FILETIME, @@ -206,9 +216,14 @@ pub type COMPARESTRING_RESULT = i32; pub struct CONDITION_VARIABLE { pub Ptr: *mut core::ffi::c_void, } +impl Default for CONDITION_VARIABLE { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub type CONSOLE_MODE = u32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct CONSOLE_READCONSOLE_CONTROL { pub nLength: u32, pub nInitialChars: u32, @@ -245,6 +260,12 @@ pub struct CONTEXT { pub SegSs: u32, pub ExtendedRegisters: [u8; 512], } +#[cfg(target_arch = "x86")] +impl Default for CONTEXT { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] #[derive(Clone, Copy)] @@ -296,6 +317,12 @@ pub struct CONTEXT { pub LastExceptionToRip: u64, pub LastExceptionFromRip: u64, } +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +impl Default for CONTEXT { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] #[derive(Clone, Copy)] @@ -303,6 +330,12 @@ pub union CONTEXT_0 { pub FltSave: XSAVE_FORMAT, pub Anonymous: CONTEXT_0_0, } +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +impl Default for CONTEXT_0 { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] #[derive(Clone, Copy)] @@ -326,6 +359,12 @@ pub struct CONTEXT_0_0 { pub Xmm14: M128A, pub Xmm15: M128A, } +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +impl Default for CONTEXT_0_0 { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[cfg(target_arch = "aarch64")] #[derive(Clone, Copy)] @@ -343,6 +382,12 @@ pub struct CONTEXT { pub Wcr: [u32; 2], pub Wvr: [u64; 2], } +#[cfg(target_arch = "aarch64")] +impl Default for CONTEXT { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[cfg(target_arch = "aarch64")] #[derive(Clone, Copy)] @@ -350,9 +395,15 @@ pub union CONTEXT_0 { pub Anonymous: CONTEXT_0_0, pub X: [u64; 31], } +#[cfg(target_arch = "aarch64")] +impl Default for CONTEXT_0 { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[cfg(target_arch = "aarch64")] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct CONTEXT_0_0 { pub X0: u64, pub X1: u64, @@ -2305,6 +2356,11 @@ pub struct EXCEPTION_POINTERS { pub ExceptionRecord: *mut EXCEPTION_RECORD, pub ContextRecord: *mut CONTEXT, } +impl Default for EXCEPTION_POINTERS { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[derive(Clone, Copy)] pub struct EXCEPTION_RECORD { @@ -2315,6 +2371,11 @@ pub struct EXCEPTION_RECORD { pub NumberParameters: u32, pub ExceptionInformation: [usize; 15], } +impl Default for EXCEPTION_RECORD { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = 0xC00000FD_u32 as _; pub const EXTENDED_STARTUPINFO_PRESENT: PROCESS_CREATION_FLAGS = 524288u32; pub const E_NOTIMPL: HRESULT = 0x80004001_u32 as _; @@ -2333,8 +2394,13 @@ pub struct FD_SET { pub fd_count: u32, pub fd_array: [SOCKET; 64], } +impl Default for FD_SET { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct FILETIME { pub dwLowDateTime: u32, pub dwHighDateTime: u32, @@ -2343,7 +2409,7 @@ pub type FILE_ACCESS_RIGHTS = u32; pub const FILE_ADD_FILE: FILE_ACCESS_RIGHTS = 2u32; pub const FILE_ADD_SUBDIRECTORY: FILE_ACCESS_RIGHTS = 4u32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct FILE_ALLOCATION_INFO { pub AllocationSize: i64, } @@ -2369,7 +2435,7 @@ pub const FILE_ATTRIBUTE_REPARSE_POINT: FILE_FLAGS_AND_ATTRIBUTES = 1024u32; pub const FILE_ATTRIBUTE_SPARSE_FILE: FILE_FLAGS_AND_ATTRIBUTES = 512u32; pub const FILE_ATTRIBUTE_SYSTEM: FILE_FLAGS_AND_ATTRIBUTES = 4u32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct FILE_ATTRIBUTE_TAG_INFO { pub FileAttributes: u32, pub ReparseTag: u32, @@ -2378,7 +2444,7 @@ pub const FILE_ATTRIBUTE_TEMPORARY: FILE_FLAGS_AND_ATTRIBUTES = 256u32; pub const FILE_ATTRIBUTE_UNPINNED: FILE_FLAGS_AND_ATTRIBUTES = 1048576u32; pub const FILE_ATTRIBUTE_VIRTUAL: FILE_FLAGS_AND_ATTRIBUTES = 65536u32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct FILE_BASIC_INFO { pub CreationTime: i64, pub LastAccessTime: i64, @@ -2405,19 +2471,19 @@ pub const FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE: FILE_DISPOSITION_INFO pub const FILE_DISPOSITION_FLAG_ON_CLOSE: FILE_DISPOSITION_INFO_EX_FLAGS = 8u32; pub const FILE_DISPOSITION_FLAG_POSIX_SEMANTICS: FILE_DISPOSITION_INFO_EX_FLAGS = 2u32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct FILE_DISPOSITION_INFO { pub DeleteFile: bool, } #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct FILE_DISPOSITION_INFO_EX { pub Flags: FILE_DISPOSITION_INFO_EX_FLAGS, } pub type FILE_DISPOSITION_INFO_EX_FLAGS = u32; pub const FILE_END: SET_FILE_POINTER_MOVE_METHOD = 2u32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct FILE_END_OF_FILE_INFO { pub EndOfFile: i64, } @@ -2457,9 +2523,14 @@ pub struct FILE_ID_BOTH_DIR_INFO { pub FileId: i64, pub FileName: [u16; 1], } +impl Default for FILE_ID_BOTH_DIR_INFO { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub type FILE_INFO_BY_HANDLE_CLASS = i32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct FILE_IO_PRIORITY_HINT_INFO { pub PriorityHint: PRIORITY_HINT, } @@ -2494,12 +2565,22 @@ pub struct FILE_RENAME_INFO { pub FileNameLength: u32, pub FileName: [u16; 1], } +impl Default for FILE_RENAME_INFO { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[derive(Clone, Copy)] pub union FILE_RENAME_INFO_0 { pub ReplaceIfExists: bool, pub Flags: u32, } +impl Default for FILE_RENAME_INFO_0 { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const FILE_RESERVE_OPFILTER: NTCREATEFILE_CREATE_OPTIONS = 1048576u32; pub const FILE_SEQUENTIAL_ONLY: NTCREATEFILE_CREATE_OPTIONS = 4u32; pub const FILE_SESSION_AWARE: NTCREATEFILE_CREATE_OPTIONS = 262144u32; @@ -2509,7 +2590,7 @@ pub const FILE_SHARE_NONE: FILE_SHARE_MODE = 0u32; pub const FILE_SHARE_READ: FILE_SHARE_MODE = 1u32; pub const FILE_SHARE_WRITE: FILE_SHARE_MODE = 2u32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct FILE_STANDARD_INFO { pub AllocationSize: i64, pub EndOfFile: i64, @@ -2549,6 +2630,12 @@ pub struct FLOATING_SAVE_AREA { pub RegisterArea: [u8; 80], pub Spare0: u32, } +#[cfg(target_arch = "x86")] +impl Default for FLOATING_SAVE_AREA { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] #[derive(Clone, Copy)] @@ -2563,6 +2650,12 @@ pub struct FLOATING_SAVE_AREA { pub RegisterArea: [u8; 80], pub Cr0NpxState: u32, } +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] +impl Default for FLOATING_SAVE_AREA { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32; pub const FORMAT_MESSAGE_ARGUMENT_ARRAY: FORMAT_MESSAGE_OPTIONS = 8192u32; pub const FORMAT_MESSAGE_FROM_HMODULE: FORMAT_MESSAGE_OPTIONS = 2048u32; @@ -2639,12 +2732,22 @@ pub const IDLE_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 64u32; pub struct IN6_ADDR { pub u: IN6_ADDR_0, } +impl Default for IN6_ADDR { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[derive(Clone, Copy)] pub union IN6_ADDR_0 { pub Byte: [u8; 16], pub Word: [u16; 8], } +impl Default for IN6_ADDR_0 { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const INFINITE: u32 = 4294967295u32; pub const INHERIT_CALLER_PRIORITY: PROCESS_CREATION_FLAGS = 131072u32; pub const INHERIT_PARENT_AFFINITY: PROCESS_CREATION_FLAGS = 65536u32; @@ -2653,6 +2756,11 @@ pub const INHERIT_PARENT_AFFINITY: PROCESS_CREATION_FLAGS = 65536u32; pub union INIT_ONCE { pub Ptr: *mut core::ffi::c_void, } +impl Default for INIT_ONCE { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const INIT_ONCE_INIT_FAILED: u32 = 4u32; pub const INVALID_FILE_ATTRIBUTES: u32 = 4294967295u32; pub const INVALID_SOCKET: SOCKET = -1i32 as _; @@ -2661,6 +2769,11 @@ pub const INVALID_SOCKET: SOCKET = -1i32 as _; pub struct IN_ADDR { pub S_un: IN_ADDR_0, } +impl Default for IN_ADDR { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[derive(Clone, Copy)] pub union IN_ADDR_0 { @@ -2668,8 +2781,13 @@ pub union IN_ADDR_0 { pub S_un_w: IN_ADDR_0_1, pub S_addr: u32, } +impl Default for IN_ADDR_0 { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct IN_ADDR_0_0 { pub s_b1: u8, pub s_b2: u8, @@ -2677,7 +2795,7 @@ pub struct IN_ADDR_0_0 { pub s_b4: u8, } #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct IN_ADDR_0_1 { pub s_w1: u16, pub s_w2: u16, @@ -2690,12 +2808,22 @@ pub struct IO_STATUS_BLOCK { pub Anonymous: IO_STATUS_BLOCK_0, pub Information: usize, } +impl Default for IO_STATUS_BLOCK { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[derive(Clone, Copy)] pub union IO_STATUS_BLOCK_0 { pub Status: NTSTATUS, pub Pointer: *mut core::ffi::c_void, } +impl Default for IO_STATUS_BLOCK_0 { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub type IPPROTO = i32; pub const IPPROTO_AH: IPPROTO = 51i32; pub const IPPROTO_CBT: IPPROTO = 7i32; @@ -2742,6 +2870,11 @@ pub struct IPV6_MREQ { pub ipv6mr_multiaddr: IN6_ADDR, pub ipv6mr_interface: u32, } +impl Default for IPV6_MREQ { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const IPV6_MULTICAST_LOOP: i32 = 11i32; pub const IPV6_V6ONLY: i32 = 27i32; pub const IP_ADD_MEMBERSHIP: i32 = 12i32; @@ -2752,11 +2885,16 @@ pub struct IP_MREQ { pub imr_multiaddr: IN_ADDR, pub imr_interface: IN_ADDR, } +impl Default for IP_MREQ { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const IP_MULTICAST_LOOP: i32 = 11i32; pub const IP_MULTICAST_TTL: i32 = 10i32; pub const IP_TTL: i32 = 4i32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct LINGER { pub l_onoff: u16, pub l_linger: u16, @@ -2797,7 +2935,7 @@ pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = Option< ), >; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct M128A { pub Low: u64, pub High: i64, @@ -2838,6 +2976,11 @@ pub struct OBJECT_ATTRIBUTES { pub SecurityDescriptor: *const SECURITY_DESCRIPTOR, pub SecurityQualityOfService: *const SECURITY_QUALITY_OF_SERVICE, } +impl Default for OBJECT_ATTRIBUTES { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub type OBJECT_ATTRIBUTE_FLAGS = u32; pub const OBJ_DONT_REPARSE: OBJECT_ATTRIBUTE_FLAGS = 4096u32; pub const OPEN_ALWAYS: FILE_CREATION_DISPOSITION = 4u32; @@ -2850,14 +2993,24 @@ pub struct OVERLAPPED { pub Anonymous: OVERLAPPED_0, pub hEvent: HANDLE, } +impl Default for OVERLAPPED { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[derive(Clone, Copy)] pub union OVERLAPPED_0 { pub Anonymous: OVERLAPPED_0_0, pub Pointer: *mut core::ffi::c_void, } +impl Default for OVERLAPPED_0 { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct OVERLAPPED_0_0 { pub Offset: u32, pub OffsetHigh: u32, @@ -2895,6 +3048,11 @@ pub struct PROCESS_INFORMATION { pub dwProcessId: u32, pub dwThreadId: u32, } +impl Default for PROCESS_INFORMATION { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const PROCESS_MODE_BACKGROUND_BEGIN: PROCESS_CREATION_FLAGS = 1048576u32; pub const PROCESS_MODE_BACKGROUND_END: PROCESS_CREATION_FLAGS = 2097152u32; pub const PROFILE_KERNEL: PROCESS_CREATION_FLAGS = 536870912u32; @@ -2926,6 +3084,11 @@ pub struct SECURITY_ATTRIBUTES { pub lpSecurityDescriptor: *mut core::ffi::c_void, pub bInheritHandle: BOOL, } +impl Default for SECURITY_ATTRIBUTES { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const SECURITY_CONTEXT_TRACKING: FILE_FLAGS_AND_ATTRIBUTES = 262144u32; pub const SECURITY_DELEGATION: FILE_FLAGS_AND_ATTRIBUTES = 196608u32; #[repr(C)] @@ -2939,13 +3102,18 @@ pub struct SECURITY_DESCRIPTOR { pub Sacl: *mut ACL, pub Dacl: *mut ACL, } +impl Default for SECURITY_DESCRIPTOR { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub type SECURITY_DESCRIPTOR_CONTROL = u16; pub const SECURITY_EFFECTIVE_ONLY: FILE_FLAGS_AND_ATTRIBUTES = 524288u32; pub const SECURITY_IDENTIFICATION: FILE_FLAGS_AND_ATTRIBUTES = 65536u32; pub const SECURITY_IMPERSONATION: FILE_FLAGS_AND_ATTRIBUTES = 131072u32; pub type SECURITY_IMPERSONATION_LEVEL = i32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct SECURITY_QUALITY_OF_SERVICE { pub Length: u32, pub ImpersonationLevel: SECURITY_IMPERSONATION_LEVEL, @@ -2962,6 +3130,11 @@ pub struct SOCKADDR { pub sa_family: ADDRESS_FAMILY, pub sa_data: [i8; 14], } +impl Default for SOCKADDR { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[derive(Clone, Copy)] pub struct SOCKADDR_STORAGE { @@ -2970,12 +3143,22 @@ pub struct SOCKADDR_STORAGE { pub __ss_align: i64, pub __ss_pad2: [i8; 112], } +impl Default for SOCKADDR_STORAGE { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[derive(Clone, Copy)] pub struct SOCKADDR_UN { pub sun_family: ADDRESS_FAMILY, pub sun_path: [i8; 108], } +impl Default for SOCKADDR_UN { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub type SOCKET = usize; pub const SOCKET_ERROR: i32 = -1i32; pub const SOCK_DGRAM: WINSOCK_SOCKET_TYPE = 2i32; @@ -2995,6 +3178,11 @@ pub const SPECIFIC_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 65535u32; pub struct SRWLOCK { pub Ptr: *mut core::ffi::c_void, } +impl Default for SRWLOCK { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const STACK_SIZE_PARAM_IS_A_RESERVATION: THREAD_CREATION_FLAGS = 65536u32; pub const STANDARD_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 2031616u32; pub const STANDARD_RIGHTS_EXECUTE: FILE_ACCESS_RIGHTS = 131072u32; @@ -3021,6 +3209,11 @@ pub struct STARTUPINFOEXW { pub StartupInfo: STARTUPINFOW, pub lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST, } +impl Default for STARTUPINFOEXW { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[derive(Clone, Copy)] pub struct STARTUPINFOW { @@ -3043,6 +3236,11 @@ pub struct STARTUPINFOW { pub hStdOutput: HANDLE, pub hStdError: HANDLE, } +impl Default for STARTUPINFOW { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub type STARTUPINFOW_FLAGS = u32; pub const STATUS_DELETE_PENDING: NTSTATUS = 0xC0000056_u32 as _; pub const STATUS_DIRECTORY_NOT_EMPTY: NTSTATUS = 0xC0000101_u32 as _; @@ -3078,14 +3276,24 @@ pub struct SYSTEM_INFO { pub wProcessorLevel: u16, pub wProcessorRevision: u16, } +impl Default for SYSTEM_INFO { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[derive(Clone, Copy)] pub union SYSTEM_INFO_0 { pub dwOemId: u32, pub Anonymous: SYSTEM_INFO_0_0, } +impl Default for SYSTEM_INFO_0 { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct SYSTEM_INFO_0_0 { pub wProcessorArchitecture: PROCESSOR_ARCHITECTURE, pub wReserved: u16, @@ -3097,7 +3305,7 @@ pub type THREAD_CREATION_FLAGS = u32; pub const TIMER_ALL_ACCESS: SYNCHRONIZATION_ACCESS_RIGHTS = 2031619u32; pub const TIMER_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32; #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct TIMEVAL { pub tv_sec: i32, pub tv_usec: i32, @@ -3134,6 +3342,11 @@ pub struct UNICODE_STRING { pub MaximumLength: u16, pub Buffer: PWSTR, } +impl Default for UNICODE_STRING { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const VOLUME_NAME_DOS: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32; pub const VOLUME_NAME_GUID: GETFINALPATHNAMEBYHANDLE_FLAGS = 1u32; pub const VOLUME_NAME_NONE: GETFINALPATHNAMEBYHANDLE_FLAGS = 4u32; @@ -3160,6 +3373,11 @@ pub struct WIN32_FIND_DATAW { pub cFileName: [u16; 260], pub cAlternateFileName: [u16; 14], } +impl Default for WIN32_FIND_DATAW { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub type WINSOCK_SHUTDOWN_HOW = i32; pub type WINSOCK_SOCKET_TYPE = i32; pub const WRITE_DAC: FILE_ACCESS_RIGHTS = 262144u32; @@ -3171,6 +3389,11 @@ pub struct WSABUF { pub len: u32, pub buf: PSTR, } +impl Default for WSABUF { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[cfg(target_arch = "x86")] #[derive(Clone, Copy)] @@ -3183,6 +3406,12 @@ pub struct WSADATA { pub iMaxUdpDg: u16, pub lpVendorInfo: PSTR, } +#[cfg(target_arch = "x86")] +impl Default for WSADATA { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] #[derive(Clone, Copy)] @@ -3195,6 +3424,12 @@ pub struct WSADATA { pub szDescription: [i8; 257], pub szSystemStatus: [i8; 129], } +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] +impl Default for WSADATA { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const WSAEACCES: WSA_ERROR = 10013i32; pub const WSAEADDRINUSE: WSA_ERROR = 10048i32; pub const WSAEADDRNOTAVAIL: WSA_ERROR = 10049i32; @@ -3255,6 +3490,11 @@ pub struct WSAPROTOCOLCHAIN { pub ChainLen: i32, pub ChainEntries: [u32; 7], } +impl Default for WSAPROTOCOLCHAIN { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[derive(Clone, Copy)] pub struct WSAPROTOCOL_INFOW { @@ -3279,6 +3519,11 @@ pub struct WSAPROTOCOL_INFOW { pub dwProviderReserved: u32, pub szProtocol: [u16; 256], } +impl Default for WSAPROTOCOL_INFOW { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} pub const WSASERVICE_NOT_FOUND: WSA_ERROR = 10108i32; pub const WSASYSCALLFAILURE: WSA_ERROR = 10107i32; pub const WSASYSNOTREADY: WSA_ERROR = 10091i32; @@ -3348,6 +3593,12 @@ pub struct XSAVE_FORMAT { pub XmmRegisters: [M128A; 8], pub Reserved4: [u8; 224], } +#[cfg(target_arch = "x86")] +impl Default for XSAVE_FORMAT { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[repr(C)] #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] #[derive(Clone, Copy)] @@ -3369,6 +3620,12 @@ pub struct XSAVE_FORMAT { pub XmmRegisters: [M128A; 16], pub Reserved4: [u8; 96], } +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] +impl Default for XSAVE_FORMAT { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} #[cfg(target_arch = "arm")] #[repr(C)] diff --git a/library/std/src/sys/pal/windows/compat.rs b/library/std/src/sys/pal/windows/compat.rs index 2b9838437e9c1..14f2c8d881cf1 100644 --- a/library/std/src/sys/pal/windows/compat.rs +++ b/library/std/src/sys/pal/windows/compat.rs @@ -145,7 +145,7 @@ macro_rules! compat_fn_with_fallback { use super::*; use crate::mem; use crate::ffi::CStr; - use crate::sync::atomic::{AtomicPtr, Ordering}; + use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::sys::compat::Module; type F = unsafe extern "system" fn($($argtype),*) -> $rettype; @@ -155,7 +155,7 @@ macro_rules! compat_fn_with_fallback { /// When that is called it attempts to load the requested symbol. /// If it succeeds, `PTR` is set to the address of that symbol. /// If it fails, then `PTR` is set to `fallback`. - static PTR: AtomicPtr = AtomicPtr::new(load as *mut _); + static PTR: Atomic<*mut c_void> = AtomicPtr::new(load as *mut _); unsafe extern "system" fn load($($argname: $argtype),*) -> $rettype { unsafe { @@ -212,9 +212,9 @@ macro_rules! compat_fn_optional { use crate::ffi::c_void; use crate::mem; use crate::ptr::{self, NonNull}; - use crate::sync::atomic::{AtomicPtr, Ordering}; + use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; - pub(in crate::sys) static PTR: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + pub(in crate::sys) static PTR: Atomic<*mut c_void> = AtomicPtr::new(ptr::null_mut()); type F = unsafe extern "system" fn($($argtype),*) $(-> $rettype)?; diff --git a/library/std/src/sys/pal/windows/env.rs b/library/std/src/sys/pal/windows/env.rs deleted file mode 100644 index f0a99d6200cac..0000000000000 --- a/library/std/src/sys/pal/windows/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = "windows"; - pub const OS: &str = "windows"; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ".dll"; - pub const DLL_EXTENSION: &str = "dll"; - pub const EXE_SUFFIX: &str = ".exe"; - pub const EXE_EXTENSION: &str = "exe"; -} diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs deleted file mode 100644 index 623a7d89ba5a0..0000000000000 --- a/library/std/src/sys/pal/windows/fs.rs +++ /dev/null @@ -1,1730 +0,0 @@ -use super::api::{self, WinError, set_file_information_by_handle}; -use super::{IoResult, to_u16s}; -use crate::alloc::{alloc, handle_alloc_error}; -use crate::borrow::Cow; -use crate::ffi::{OsStr, OsString, c_void}; -use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem::{self, MaybeUninit}; -use crate::os::windows::io::{AsHandle, BorrowedHandle}; -use crate::os::windows::prelude::*; -use crate::path::{Path, PathBuf}; -use crate::sync::Arc; -use crate::sys::handle::Handle; -use crate::sys::path::maybe_verbatim; -use crate::sys::time::SystemTime; -use crate::sys::{Align8, c, cvt}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::{fmt, ptr, slice}; - -mod remove_dir_all; -use remove_dir_all::remove_dir_all_iterative; - -pub struct File { - handle: Handle, -} - -#[derive(Clone)] -pub struct FileAttr { - attributes: u32, - creation_time: c::FILETIME, - last_access_time: c::FILETIME, - last_write_time: c::FILETIME, - change_time: Option, - file_size: u64, - reparse_tag: u32, - volume_serial_number: Option, - number_of_links: Option, - file_index: Option, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct FileType { - attributes: u32, - reparse_tag: u32, -} - -pub struct ReadDir { - handle: Option, - root: Arc, - first: Option, -} - -struct FindNextFileHandle(c::HANDLE); - -unsafe impl Send for FindNextFileHandle {} -unsafe impl Sync for FindNextFileHandle {} - -pub struct DirEntry { - root: Arc, - data: c::WIN32_FIND_DATAW, -} - -unsafe impl Send for OpenOptions {} -unsafe impl Sync for OpenOptions {} - -#[derive(Clone, Debug)] -pub struct OpenOptions { - // generic - read: bool, - write: bool, - append: bool, - truncate: bool, - create: bool, - create_new: bool, - // system-specific - custom_flags: u32, - access_mode: Option, - attributes: u32, - share_mode: u32, - security_qos_flags: u32, - security_attributes: *mut c::SECURITY_ATTRIBUTES, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FilePermissions { - attrs: u32, -} - -#[derive(Copy, Clone, Debug, Default)] -pub struct FileTimes { - accessed: Option, - modified: Option, - created: Option, -} - -impl fmt::Debug for c::FILETIME { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let time = ((self.dwHighDateTime as u64) << 32) | self.dwLowDateTime as u64; - f.debug_tuple("FILETIME").field(&time).finish() - } -} - -#[derive(Debug)] -pub struct DirBuilder; - -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("C:\")' - fmt::Debug::fmt(&*self.root, f) - } -} - -impl Iterator for ReadDir { - type Item = io::Result; - fn next(&mut self) -> Option> { - let Some(handle) = self.handle.as_ref() else { - // This iterator was initialized with an `INVALID_HANDLE_VALUE` as its handle. - // Simply return `None` because this is only the case when `FindFirstFileExW` in - // the construction of this iterator returns `ERROR_FILE_NOT_FOUND` which means - // no matchhing files can be found. - return None; - }; - if let Some(first) = self.first.take() { - if let Some(e) = DirEntry::new(&self.root, &first) { - return Some(Ok(e)); - } - } - unsafe { - let mut wfd = mem::zeroed(); - loop { - if c::FindNextFileW(handle.0, &mut wfd) == 0 { - match api::get_last_error() { - WinError::NO_MORE_FILES => return None, - WinError { code } => { - return Some(Err(Error::from_raw_os_error(code as i32))); - } - } - } - if let Some(e) = DirEntry::new(&self.root, &wfd) { - return Some(Ok(e)); - } - } - } - } -} - -impl Drop for FindNextFileHandle { - fn drop(&mut self) { - let r = unsafe { c::FindClose(self.0) }; - debug_assert!(r != 0); - } -} - -impl DirEntry { - fn new(root: &Arc, wfd: &c::WIN32_FIND_DATAW) -> Option { - match &wfd.cFileName[0..3] { - // check for '.' and '..' - &[46, 0, ..] | &[46, 46, 0, ..] => return None, - _ => {} - } - - Some(DirEntry { root: root.clone(), data: *wfd }) - } - - pub fn path(&self) -> PathBuf { - self.root.join(self.file_name()) - } - - pub fn file_name(&self) -> OsString { - let filename = super::truncate_utf16_at_nul(&self.data.cFileName); - OsString::from_wide(filename) - } - - pub fn file_type(&self) -> io::Result { - Ok(FileType::new( - self.data.dwFileAttributes, - /* reparse_tag = */ self.data.dwReserved0, - )) - } - - pub fn metadata(&self) -> io::Result { - Ok(self.data.into()) - } -} - -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, - access_mode: None, - share_mode: c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE, - attributes: 0, - security_qos_flags: 0, - security_attributes: ptr::null_mut(), - } - } - - 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: u32) { - self.custom_flags = flags; - } - pub fn access_mode(&mut self, access_mode: u32) { - self.access_mode = Some(access_mode); - } - pub fn share_mode(&mut self, share_mode: u32) { - self.share_mode = share_mode; - } - pub fn attributes(&mut self, attrs: u32) { - self.attributes = attrs; - } - pub fn security_qos_flags(&mut self, flags: u32) { - // We have to set `SECURITY_SQOS_PRESENT` here, because one of the valid flags we can - // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on. - self.security_qos_flags = flags | c::SECURITY_SQOS_PRESENT; - } - pub fn security_attributes(&mut self, attrs: *mut c::SECURITY_ATTRIBUTES) { - self.security_attributes = attrs; - } - - fn get_access_mode(&self) -> io::Result { - match (self.read, self.write, self.append, self.access_mode) { - (.., Some(mode)) => Ok(mode), - (true, false, false, None) => Ok(c::GENERIC_READ), - (false, true, false, None) => Ok(c::GENERIC_WRITE), - (true, true, false, None) => Ok(c::GENERIC_READ | c::GENERIC_WRITE), - (false, _, true, None) => Ok(c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA), - (true, _, true, None) => { - Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA)) - } - (false, false, false, None) => { - Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)) - } - } - } - - 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(c::ERROR_INVALID_PARAMETER as i32)); - } - } - (_, true) => { - if self.truncate && !self.create_new { - return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)); - } - } - } - - Ok(match (self.create, self.truncate, self.create_new) { - (false, false, false) => c::OPEN_EXISTING, - (true, false, false) => c::OPEN_ALWAYS, - (false, true, false) => c::TRUNCATE_EXISTING, - // `CREATE_ALWAYS` has weird semantics so we emulate it using - // `OPEN_ALWAYS` and a manual truncation step. See #115745. - (true, true, false) => c::OPEN_ALWAYS, - (_, _, true) => c::CREATE_NEW, - }) - } - - fn get_flags_and_attributes(&self) -> u32 { - self.custom_flags - | self.attributes - | self.security_qos_flags - | if self.create_new { c::FILE_FLAG_OPEN_REPARSE_POINT } else { 0 } - } -} - -impl File { - pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let path = maybe_verbatim(path)?; - Self::open_native(&path, opts) - } - - fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result { - let creation = opts.get_creation_mode()?; - let handle = unsafe { - c::CreateFileW( - path.as_ptr(), - opts.get_access_mode()?, - opts.share_mode, - opts.security_attributes, - creation, - opts.get_flags_and_attributes(), - ptr::null_mut(), - ) - }; - let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) }; - if let Ok(handle) = OwnedHandle::try_from(handle) { - // Manual truncation. See #115745. - if opts.truncate - && creation == c::OPEN_ALWAYS - && api::get_last_error() == WinError::ALREADY_EXISTS - { - // This first tries `FileAllocationInfo` but falls back to - // `FileEndOfFileInfo` in order to support WINE. - // If WINE gains support for FileAllocationInfo, we should - // remove the fallback. - let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 }; - set_file_information_by_handle(handle.as_raw_handle(), &alloc) - .or_else(|_| { - let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 }; - set_file_information_by_handle(handle.as_raw_handle(), &eof) - }) - .io_result()?; - } - Ok(File { handle: Handle::from_inner(handle) }) - } else { - Err(Error::last_os_error()) - } - } - - pub fn fsync(&self) -> io::Result<()> { - cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?; - Ok(()) - } - - pub fn datasync(&self) -> io::Result<()> { - self.fsync() - } - - fn acquire_lock(&self, flags: c::LOCK_FILE_FLAGS) -> io::Result<()> { - unsafe { - let mut overlapped: c::OVERLAPPED = mem::zeroed(); - let event = c::CreateEventW(ptr::null_mut(), c::FALSE, c::FALSE, ptr::null()); - if event.is_null() { - return Err(io::Error::last_os_error()); - } - overlapped.hEvent = event; - let lock_result = cvt(c::LockFileEx( - self.handle.as_raw_handle(), - flags, - 0, - u32::MAX, - u32::MAX, - &mut overlapped, - )); - - let final_result = match lock_result { - Ok(_) => Ok(()), - Err(err) => { - if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) { - // Wait for the lock to be acquired, and get the lock operation status. - // This can happen asynchronously, if the file handle was opened for async IO - let mut bytes_transferred = 0; - cvt(c::GetOverlappedResult( - self.handle.as_raw_handle(), - &mut overlapped, - &mut bytes_transferred, - c::TRUE, - )) - .map(|_| ()) - } else { - Err(err) - } - } - }; - c::CloseHandle(overlapped.hEvent); - final_result - } - } - - pub fn lock(&self) -> io::Result<()> { - self.acquire_lock(c::LOCKFILE_EXCLUSIVE_LOCK) - } - - pub fn lock_shared(&self) -> io::Result<()> { - self.acquire_lock(0) - } - - pub fn try_lock(&self) -> io::Result { - let result = cvt(unsafe { - let mut overlapped = mem::zeroed(); - c::LockFileEx( - self.handle.as_raw_handle(), - c::LOCKFILE_EXCLUSIVE_LOCK | c::LOCKFILE_FAIL_IMMEDIATELY, - 0, - u32::MAX, - u32::MAX, - &mut overlapped, - ) - }); - - match result { - Ok(_) => Ok(true), - Err(err) - if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) - || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => - { - Ok(false) - } - Err(err) => Err(err), - } - } - - pub fn try_lock_shared(&self) -> io::Result { - let result = cvt(unsafe { - let mut overlapped = mem::zeroed(); - c::LockFileEx( - self.handle.as_raw_handle(), - c::LOCKFILE_FAIL_IMMEDIATELY, - 0, - u32::MAX, - u32::MAX, - &mut overlapped, - ) - }); - - match result { - Ok(_) => Ok(true), - Err(err) - if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) - || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) => - { - Ok(false) - } - Err(err) => Err(err), - } - } - - pub fn unlock(&self) -> io::Result<()> { - // Unlock the handle twice because LockFileEx() allows a file handle to acquire - // both an exclusive and shared lock, in which case the documentation states that: - // "...two unlock operations are necessary to unlock the region; the first unlock operation - // unlocks the exclusive lock, the second unlock operation unlocks the shared lock" - cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) })?; - let result = - cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) }); - match result { - Ok(_) => Ok(()), - Err(err) if err.raw_os_error() == Some(c::ERROR_NOT_LOCKED as i32) => Ok(()), - Err(err) => Err(err), - } - } - - pub fn truncate(&self, size: u64) -> io::Result<()> { - let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 }; - api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() - } - - #[cfg(not(target_vendor = "uwp"))] - pub fn file_attr(&self) -> io::Result { - unsafe { - let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed(); - cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?; - let mut reparse_tag = 0; - if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed(); - cvt(c::GetFileInformationByHandleEx( - self.handle.as_raw_handle(), - c::FileAttributeTagInfo, - (&raw mut attr_tag).cast(), - mem::size_of::().try_into().unwrap(), - ))?; - if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - reparse_tag = attr_tag.ReparseTag; - } - } - Ok(FileAttr { - attributes: info.dwFileAttributes, - creation_time: info.ftCreationTime, - last_access_time: info.ftLastAccessTime, - last_write_time: info.ftLastWriteTime, - change_time: None, // Only available in FILE_BASIC_INFO - file_size: (info.nFileSizeLow as u64) | ((info.nFileSizeHigh as u64) << 32), - reparse_tag, - volume_serial_number: Some(info.dwVolumeSerialNumber), - number_of_links: Some(info.nNumberOfLinks), - file_index: Some( - (info.nFileIndexLow as u64) | ((info.nFileIndexHigh as u64) << 32), - ), - }) - } - } - - #[cfg(target_vendor = "uwp")] - pub fn file_attr(&self) -> io::Result { - unsafe { - let mut info: c::FILE_BASIC_INFO = mem::zeroed(); - let size = mem::size_of_val(&info); - cvt(c::GetFileInformationByHandleEx( - self.handle.as_raw_handle(), - c::FileBasicInfo, - (&raw mut info) as *mut c_void, - size as u32, - ))?; - let mut attr = FileAttr { - attributes: info.FileAttributes, - creation_time: c::FILETIME { - dwLowDateTime: info.CreationTime as u32, - dwHighDateTime: (info.CreationTime >> 32) as u32, - }, - last_access_time: c::FILETIME { - dwLowDateTime: info.LastAccessTime as u32, - dwHighDateTime: (info.LastAccessTime >> 32) as u32, - }, - last_write_time: c::FILETIME { - dwLowDateTime: info.LastWriteTime as u32, - dwHighDateTime: (info.LastWriteTime >> 32) as u32, - }, - change_time: Some(c::FILETIME { - dwLowDateTime: info.ChangeTime as u32, - dwHighDateTime: (info.ChangeTime >> 32) as u32, - }), - file_size: 0, - reparse_tag: 0, - volume_serial_number: None, - number_of_links: None, - file_index: None, - }; - let mut info: c::FILE_STANDARD_INFO = mem::zeroed(); - let size = mem::size_of_val(&info); - cvt(c::GetFileInformationByHandleEx( - self.handle.as_raw_handle(), - c::FileStandardInfo, - (&raw mut info) as *mut c_void, - size as u32, - ))?; - attr.file_size = info.AllocationSize as u64; - attr.number_of_links = Some(info.NumberOfLinks); - if attr.file_type().is_reparse_point() { - let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed(); - cvt(c::GetFileInformationByHandleEx( - self.handle.as_raw_handle(), - c::FileAttributeTagInfo, - (&raw mut attr_tag).cast(), - mem::size_of::().try_into().unwrap(), - ))?; - if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - attr.reparse_tag = attr_tag.ReparseTag; - } - } - Ok(attr) - } - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.handle.read(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.handle.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.handle.is_read_vectored() - } - - pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - self.handle.read_at(buf, offset) - } - - pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.handle.read_buf(cursor) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.handle.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.handle.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.handle.is_write_vectored() - } - - pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - self.handle.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, `SetFilePointerEx` reinterprets this - // integer as `u64`. - SeekFrom::Start(n) => (c::FILE_BEGIN, n as i64), - SeekFrom::End(n) => (c::FILE_END, n), - SeekFrom::Current(n) => (c::FILE_CURRENT, n), - }; - let pos = pos as i64; - let mut newpos = 0; - cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?; - Ok(newpos as u64) - } - - pub fn tell(&self) -> io::Result { - self.seek(SeekFrom::Current(0)) - } - - pub fn duplicate(&self) -> io::Result { - Ok(Self { handle: self.handle.try_clone()? }) - } - - // NB: returned pointer is derived from `space`, and has provenance to - // match. A raw pointer is returned rather than a reference in order to - // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`. - fn reparse_point( - &self, - space: &mut Align8<[MaybeUninit]>, - ) -> io::Result<(u32, *mut c::REPARSE_DATA_BUFFER)> { - unsafe { - let mut bytes = 0; - cvt({ - // Grab this in advance to avoid it invalidating the pointer - // we get from `space.0.as_mut_ptr()`. - let len = space.0.len(); - c::DeviceIoControl( - self.handle.as_raw_handle(), - c::FSCTL_GET_REPARSE_POINT, - ptr::null_mut(), - 0, - space.0.as_mut_ptr().cast(), - len as u32, - &mut bytes, - ptr::null_mut(), - ) - })?; - const _: () = assert!(core::mem::align_of::() <= 8); - Ok((bytes, space.0.as_mut_ptr().cast::())) - } - } - - fn readlink(&self) -> io::Result { - let mut space = - Align8([MaybeUninit::::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]); - let (_bytes, buf) = self.reparse_point(&mut space)?; - unsafe { - let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag { - c::IO_REPARSE_TAG_SYMLINK => { - let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = (&raw mut (*buf).rest).cast(); - assert!(info.is_aligned()); - ( - (&raw mut (*info).PathBuffer).cast::(), - (*info).SubstituteNameOffset / 2, - (*info).SubstituteNameLength / 2, - (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0, - ) - } - c::IO_REPARSE_TAG_MOUNT_POINT => { - let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = (&raw mut (*buf).rest).cast(); - assert!(info.is_aligned()); - ( - (&raw mut (*info).PathBuffer).cast::(), - (*info).SubstituteNameOffset / 2, - (*info).SubstituteNameLength / 2, - false, - ) - } - _ => { - return Err(io::const_error!( - io::ErrorKind::Uncategorized, - "Unsupported reparse point type", - )); - } - }; - let subst_ptr = path_buffer.add(subst_off.into()); - let subst = slice::from_raw_parts_mut(subst_ptr, subst_len as usize); - // Absolute paths start with an NT internal namespace prefix `\??\` - // We should not let it leak through. - if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) { - // Turn `\??\` into `\\?\` (a verbatim path). - subst[1] = b'\\' as u16; - // Attempt to convert to a more user-friendly path. - let user = super::args::from_wide_to_user_path( - subst.iter().copied().chain([0]).collect(), - )?; - Ok(PathBuf::from(OsString::from_wide(user.strip_suffix(&[0]).unwrap_or(&user)))) - } else { - Ok(PathBuf::from(OsString::from_wide(subst))) - } - } - } - - pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { - let info = c::FILE_BASIC_INFO { - CreationTime: 0, - LastAccessTime: 0, - LastWriteTime: 0, - ChangeTime: 0, - FileAttributes: perm.attrs, - }; - api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() - } - - pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - let is_zero = |t: c::FILETIME| t.dwLowDateTime == 0 && t.dwHighDateTime == 0; - if times.accessed.map_or(false, is_zero) - || times.modified.map_or(false, is_zero) - || times.created.map_or(false, is_zero) - { - return Err(io::const_error!( - io::ErrorKind::InvalidInput, - "cannot set file timestamp to 0", - )); - } - let is_max = |t: c::FILETIME| t.dwLowDateTime == u32::MAX && t.dwHighDateTime == u32::MAX; - if times.accessed.map_or(false, is_max) - || times.modified.map_or(false, is_max) - || times.created.map_or(false, is_max) - { - return Err(io::const_error!( - io::ErrorKind::InvalidInput, - "cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF", - )); - } - cvt(unsafe { - let created = - times.created.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); - let accessed = - times.accessed.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); - let modified = - times.modified.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); - c::SetFileTime(self.as_raw_handle(), created, accessed, modified) - })?; - Ok(()) - } - - /// Gets only basic file information such as attributes and file times. - fn basic_info(&self) -> io::Result { - unsafe { - let mut info: c::FILE_BASIC_INFO = mem::zeroed(); - let size = mem::size_of_val(&info); - cvt(c::GetFileInformationByHandleEx( - self.handle.as_raw_handle(), - c::FileBasicInfo, - (&raw mut info) as *mut c_void, - size as u32, - ))?; - Ok(info) - } - } - - /// Deletes the file, consuming the file handle to ensure the delete occurs - /// as immediately as possible. - /// This attempts to use `posix_delete` but falls back to `win32_delete` - /// if that is not supported by the filesystem. - #[allow(unused)] - fn delete(self) -> Result<(), WinError> { - // If POSIX delete is not supported for this filesystem then fallback to win32 delete. - match self.posix_delete() { - Err(WinError::INVALID_PARAMETER) - | Err(WinError::NOT_SUPPORTED) - | Err(WinError::INVALID_FUNCTION) => self.win32_delete(), - result => result, - } - } - - /// Delete using POSIX semantics. - /// - /// Files will be deleted as soon as the handle is closed. This is supported - /// for Windows 10 1607 (aka RS1) and later. However some filesystem - /// drivers will not support it even then, e.g. FAT32. - /// - /// If the operation is not supported for this filesystem or OS version - /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`. - #[allow(unused)] - fn posix_delete(&self) -> Result<(), WinError> { - let info = c::FILE_DISPOSITION_INFO_EX { - Flags: c::FILE_DISPOSITION_FLAG_DELETE - | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS - | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE, - }; - api::set_file_information_by_handle(self.handle.as_raw_handle(), &info) - } - - /// Delete a file using win32 semantics. The file won't actually be deleted - /// until all file handles are closed. However, marking a file for deletion - /// will prevent anyone from opening a new handle to the file. - #[allow(unused)] - fn win32_delete(&self) -> Result<(), WinError> { - let info = c::FILE_DISPOSITION_INFO { DeleteFile: true }; - api::set_file_information_by_handle(self.handle.as_raw_handle(), &info) - } - - /// Fill the given buffer with as many directory entries as will fit. - /// This will remember its position and continue from the last call unless - /// `restart` is set to `true`. - /// - /// The returned bool indicates if there are more entries or not. - /// It is an error if `self` is not a directory. - /// - /// # Symlinks and other reparse points - /// - /// On Windows a file is either a directory or a non-directory. - /// A symlink directory is simply an empty directory with some "reparse" metadata attached. - /// So if you open a link (not its target) and iterate the directory, - /// you will always iterate an empty directory regardless of the target. - #[allow(unused)] - fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> Result { - let class = - if restart { c::FileIdBothDirectoryRestartInfo } else { c::FileIdBothDirectoryInfo }; - - unsafe { - let result = c::GetFileInformationByHandleEx( - self.as_raw_handle(), - class, - buffer.as_mut_ptr().cast(), - buffer.capacity() as _, - ); - if result == 0 { - let err = api::get_last_error(); - if err.code == c::ERROR_NO_MORE_FILES { Ok(false) } else { Err(err) } - } else { - Ok(true) - } - } - } -} - -/// A buffer for holding directory entries. -struct DirBuff { - buffer: Box; Self::BUFFER_SIZE]>>, -} -impl DirBuff { - const BUFFER_SIZE: usize = 1024; - fn new() -> Self { - Self { - // Safety: `Align8<[MaybeUninit; N]>` does not need - // initialization. - buffer: unsafe { Box::new_uninit().assume_init() }, - } - } - fn capacity(&self) -> usize { - self.buffer.0.len() - } - fn as_mut_ptr(&mut self) -> *mut u8 { - self.buffer.0.as_mut_ptr().cast() - } - /// Returns a `DirBuffIter`. - fn iter(&self) -> DirBuffIter<'_> { - DirBuffIter::new(self) - } -} -impl AsRef<[MaybeUninit]> for DirBuff { - fn as_ref(&self) -> &[MaybeUninit] { - &self.buffer.0 - } -} - -/// An iterator over entries stored in a `DirBuff`. -/// -/// Currently only returns file names (UTF-16 encoded). -struct DirBuffIter<'a> { - buffer: Option<&'a [MaybeUninit]>, - cursor: usize, -} -impl<'a> DirBuffIter<'a> { - fn new(buffer: &'a DirBuff) -> Self { - Self { buffer: Some(buffer.as_ref()), cursor: 0 } - } -} -impl<'a> Iterator for DirBuffIter<'a> { - type Item = (Cow<'a, [u16]>, bool); - fn next(&mut self) -> Option { - use crate::mem::size_of; - let buffer = &self.buffer?[self.cursor..]; - - // Get the name and next entry from the buffer. - // SAFETY: - // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last - // field (the file name) is unsized. So an offset has to be used to - // get the file name slice. - // - The OS has guaranteed initialization of the fields of - // `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least - // `FileNameLength` bytes) - let (name, is_directory, next_entry) = unsafe { - let info = buffer.as_ptr().cast::(); - // While this is guaranteed to be aligned in documentation for - // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info - // it does not seem that reality is so kind, and assuming this - // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530) - // presumably, this can be blamed on buggy filesystem drivers, but who knows. - let next_entry = (&raw const (*info).NextEntryOffset).read_unaligned() as usize; - let length = (&raw const (*info).FileNameLength).read_unaligned() as usize; - let attrs = (&raw const (*info).FileAttributes).read_unaligned(); - let name = from_maybe_unaligned( - (&raw const (*info).FileName).cast::(), - length / size_of::(), - ); - let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0; - - (name, is_directory, next_entry) - }; - - if next_entry == 0 { - self.buffer = None - } else { - self.cursor += next_entry - } - - // Skip `.` and `..` pseudo entries. - const DOT: u16 = b'.' as u16; - match &name[..] { - [DOT] | [DOT, DOT] => self.next(), - _ => Some((name, is_directory)), - } - } -} - -unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> { - unsafe { - if p.is_aligned() { - Cow::Borrowed(crate::slice::from_raw_parts(p, len)) - } else { - Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect()) - } - } -} - -impl AsInner for File { - #[inline] - fn as_inner(&self) -> &Handle { - &self.handle - } -} - -impl IntoInner for File { - fn into_inner(self) -> Handle { - self.handle - } -} - -impl FromInner for File { - fn from_inner(handle: Handle) -> File { - File { handle } - } -} - -impl AsHandle for File { - fn as_handle(&self) -> BorrowedHandle<'_> { - self.as_inner().as_handle() - } -} - -impl AsRawHandle for File { - fn as_raw_handle(&self) -> RawHandle { - self.as_inner().as_raw_handle() - } -} - -impl IntoRawHandle for File { - fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_raw_handle() - } -} - -impl FromRawHandle for File { - unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { - unsafe { - Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) } - } - } -} - -impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // FIXME(#24570): add more info here (e.g., mode) - let mut b = f.debug_struct("File"); - b.field("handle", &self.handle.as_raw_handle()); - if let Ok(path) = get_path(self) { - b.field("path", &path); - } - b.finish() - } -} - -impl FileAttr { - pub fn size(&self) -> u64 { - self.file_size - } - - pub fn perm(&self) -> FilePermissions { - FilePermissions { attrs: self.attributes } - } - - pub fn attrs(&self) -> u32 { - self.attributes - } - - pub fn file_type(&self) -> FileType { - FileType::new(self.attributes, self.reparse_tag) - } - - pub fn modified(&self) -> io::Result { - Ok(SystemTime::from(self.last_write_time)) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from(self.last_access_time)) - } - - pub fn created(&self) -> io::Result { - Ok(SystemTime::from(self.creation_time)) - } - - pub fn modified_u64(&self) -> u64 { - to_u64(&self.last_write_time) - } - - pub fn accessed_u64(&self) -> u64 { - to_u64(&self.last_access_time) - } - - pub fn created_u64(&self) -> u64 { - to_u64(&self.creation_time) - } - - pub fn changed_u64(&self) -> Option { - self.change_time.as_ref().map(|c| to_u64(c)) - } - - pub fn volume_serial_number(&self) -> Option { - self.volume_serial_number - } - - pub fn number_of_links(&self) -> Option { - self.number_of_links - } - - pub fn file_index(&self) -> Option { - self.file_index - } -} -impl From for FileAttr { - fn from(wfd: c::WIN32_FIND_DATAW) -> Self { - FileAttr { - attributes: wfd.dwFileAttributes, - creation_time: wfd.ftCreationTime, - last_access_time: wfd.ftLastAccessTime, - last_write_time: wfd.ftLastWriteTime, - change_time: None, - file_size: ((wfd.nFileSizeHigh as u64) << 32) | (wfd.nFileSizeLow as u64), - reparse_tag: if wfd.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - // reserved unless this is a reparse point - wfd.dwReserved0 - } else { - 0 - }, - volume_serial_number: None, - number_of_links: None, - file_index: None, - } - } -} - -fn to_u64(ft: &c::FILETIME) -> u64 { - (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.attrs & c::FILE_ATTRIBUTE_READONLY != 0 - } - - pub fn set_readonly(&mut self, readonly: bool) { - if readonly { - self.attrs |= c::FILE_ATTRIBUTE_READONLY; - } else { - self.attrs &= !c::FILE_ATTRIBUTE_READONLY; - } - } -} - -impl FileTimes { - pub fn set_accessed(&mut self, t: SystemTime) { - self.accessed = Some(t.into_inner()); - } - - pub fn set_modified(&mut self, t: SystemTime) { - self.modified = Some(t.into_inner()); - } - - pub fn set_created(&mut self, t: SystemTime) { - self.created = Some(t.into_inner()); - } -} - -impl FileType { - fn new(attrs: u32, reparse_tag: u32) -> FileType { - FileType { attributes: attrs, reparse_tag } - } - pub fn is_dir(&self) -> bool { - !self.is_symlink() && self.is_directory() - } - pub fn is_file(&self) -> bool { - !self.is_symlink() && !self.is_directory() - } - pub fn is_symlink(&self) -> bool { - self.is_reparse_point() && self.is_reparse_tag_name_surrogate() - } - pub fn is_symlink_dir(&self) -> bool { - self.is_symlink() && self.is_directory() - } - pub fn is_symlink_file(&self) -> bool { - self.is_symlink() && !self.is_directory() - } - fn is_directory(&self) -> bool { - self.attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0 - } - fn is_reparse_point(&self) -> bool { - self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 - } - fn is_reparse_tag_name_surrogate(&self) -> bool { - self.reparse_tag & 0x20000000 != 0 - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder - } - - pub fn mkdir(&self, p: &Path) -> io::Result<()> { - let p = maybe_verbatim(p)?; - cvt(unsafe { c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) })?; - Ok(()) - } -} - -pub fn readdir(p: &Path) -> io::Result { - // We push a `*` to the end of the path which cause the empty path to be - // treated as the current directory. So, for consistency with other platforms, - // we explicitly error on the empty path. - if p.as_os_str().is_empty() { - // Return an error code consistent with other ways of opening files. - // E.g. fs::metadata or File::open. - return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32)); - } - let root = p.to_path_buf(); - let star = p.join("*"); - let path = maybe_verbatim(&star)?; - - unsafe { - let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed(); - // this is like FindFirstFileW (see https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfileexw), - // but with FindExInfoBasic it should skip filling WIN32_FIND_DATAW.cAlternateFileName - // (see https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-win32_find_dataw) - // (which will be always null string value and currently unused) and should be faster. - // - // We can pass FIND_FIRST_EX_LARGE_FETCH to dwAdditionalFlags to speed up things more, - // but as we don't know user's use profile of this function, lets be conservative. - let find_handle = c::FindFirstFileExW( - path.as_ptr(), - c::FindExInfoBasic, - &mut wfd as *mut _ as _, - c::FindExSearchNameMatch, - ptr::null(), - 0, - ); - - if find_handle != c::INVALID_HANDLE_VALUE { - Ok(ReadDir { - handle: Some(FindNextFileHandle(find_handle)), - root: Arc::new(root), - first: Some(wfd), - }) - } else { - // The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileExW` function - // if no matching files can be found, but not necessarily that the path to find the - // files in does not exist. - // - // Hence, a check for whether the path to search in exists is added when the last - // os error returned by Windows is `ERROR_FILE_NOT_FOUND` to handle this scenario. - // If that is the case, an empty `ReadDir` iterator is returned as it returns `None` - // in the initial `.next()` invocation because `ERROR_NO_MORE_FILES` would have been - // returned by the `FindNextFileW` function. - // - // See issue #120040: https://github.com/rust-lang/rust/issues/120040. - let last_error = api::get_last_error(); - if last_error == WinError::FILE_NOT_FOUND { - return Ok(ReadDir { handle: None, root: Arc::new(root), first: None }); - } - - // Just return the error constructed from the raw OS error if the above is not the case. - // - // Note: `ERROR_PATH_NOT_FOUND` would have been returned by the `FindFirstFileExW` function - // when the path to search in does not exist in the first place. - Err(Error::from_raw_os_error(last_error.code as i32)) - } - } -} - -pub fn unlink(p: &Path) -> io::Result<()> { - let p_u16s = maybe_verbatim(p)?; - if unsafe { c::DeleteFileW(p_u16s.as_ptr()) } == 0 { - let err = api::get_last_error(); - // if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove - // the file while ignoring the readonly attribute. - // This is accomplished by calling the `posix_delete` function on an open file handle. - if err == WinError::ACCESS_DENIED { - let mut opts = OpenOptions::new(); - opts.access_mode(c::DELETE); - opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT); - if let Ok(f) = File::open_native(&p_u16s, &opts) { - if f.posix_delete().is_ok() { - return Ok(()); - } - } - } - // return the original error if any of the above fails. - Err(io::Error::from_raw_os_error(err.code as i32)) - } else { - Ok(()) - } -} - -pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - let old = maybe_verbatim(old)?; - let new = maybe_verbatim(new)?; - - let new_len_without_nul_in_bytes = (new.len() - 1).try_into().unwrap(); - - // The last field of FILE_RENAME_INFO, the file name, is unsized, - // and FILE_RENAME_INFO has two padding bytes. - // Therefore we need to make sure to not allocate less than - // size_of::() bytes, which would be the case with - // 0 or 1 character paths + a null byte. - let struct_size = mem::size_of::() - .max(mem::offset_of!(c::FILE_RENAME_INFO, FileName) + new.len() * mem::size_of::()); - - let struct_size: u32 = struct_size.try_into().unwrap(); - - let create_file = |extra_access, extra_flags| { - let handle = unsafe { - HandleOrInvalid::from_raw_handle(c::CreateFileW( - old.as_ptr(), - c::SYNCHRONIZE | c::DELETE | extra_access, - c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE, - ptr::null(), - c::OPEN_EXISTING, - c::FILE_ATTRIBUTE_NORMAL | c::FILE_FLAG_BACKUP_SEMANTICS | extra_flags, - ptr::null_mut(), - )) - }; - - OwnedHandle::try_from(handle).map_err(|_| io::Error::last_os_error()) - }; - - // The following code replicates `MoveFileEx`'s behavior as reverse-engineered from its disassembly. - // If `old` refers to a mount point, we move it instead of the target. - let handle = match create_file(c::FILE_READ_ATTRIBUTES, c::FILE_FLAG_OPEN_REPARSE_POINT) { - Ok(handle) => { - let mut file_attribute_tag_info: MaybeUninit = - MaybeUninit::uninit(); - - let result = unsafe { - cvt(c::GetFileInformationByHandleEx( - handle.as_raw_handle(), - c::FileAttributeTagInfo, - file_attribute_tag_info.as_mut_ptr().cast(), - mem::size_of::().try_into().unwrap(), - )) - }; - - if let Err(err) = result { - if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) - || err.raw_os_error() == Some(c::ERROR_INVALID_FUNCTION as _) - { - // `GetFileInformationByHandleEx` documents that not all underlying drivers support all file information classes. - // Since we know we passed the correct arguments, this means the underlying driver didn't understand our request; - // `MoveFileEx` proceeds by reopening the file without inhibiting reparse point behavior. - None - } else { - Some(Err(err)) - } - } else { - // SAFETY: The struct has been initialized by GetFileInformationByHandleEx - let file_attribute_tag_info = unsafe { file_attribute_tag_info.assume_init() }; - let file_type = FileType::new( - file_attribute_tag_info.FileAttributes, - file_attribute_tag_info.ReparseTag, - ); - - if file_type.is_symlink() { - // The file is a mount point, junction point or symlink so - // don't reopen the file so that the link gets renamed. - Some(Ok(handle)) - } else { - // Otherwise reopen the file without inhibiting reparse point behavior. - None - } - } - } - // The underlying driver may not support `FILE_FLAG_OPEN_REPARSE_POINT`: Retry without it. - Err(err) if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) => None, - Err(err) => Some(Err(err)), - } - .unwrap_or_else(|| create_file(0, 0))?; - - let layout = core::alloc::Layout::from_size_align( - struct_size as _, - mem::align_of::(), - ) - .unwrap(); - - let file_rename_info = unsafe { alloc(layout) } as *mut c::FILE_RENAME_INFO; - - if file_rename_info.is_null() { - handle_alloc_error(layout); - } - - // SAFETY: file_rename_info is a non-null pointer pointing to memory allocated by the global allocator. - let mut file_rename_info = unsafe { Box::from_raw(file_rename_info) }; - - // SAFETY: We have allocated enough memory for a full FILE_RENAME_INFO struct and a filename. - unsafe { - (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 { - Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c::FILE_RENAME_FLAG_POSIX_SEMANTICS, - }); - - (&raw mut (*file_rename_info).RootDirectory).write(ptr::null_mut()); - (&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes); - - new.as_ptr() - .copy_to_nonoverlapping((&raw mut (*file_rename_info).FileName) as *mut u16, new.len()); - } - - // We don't use `set_file_information_by_handle` here as `FILE_RENAME_INFO` is used for both `FileRenameInfo` and `FileRenameInfoEx`. - let result = unsafe { - cvt(c::SetFileInformationByHandle( - handle.as_raw_handle(), - c::FileRenameInfoEx, - (&raw const *file_rename_info).cast::(), - struct_size, - )) - }; - - if let Err(err) = result { - if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) { - // FileRenameInfoEx and FILE_RENAME_FLAG_POSIX_SEMANTICS were added in Windows 10 1607; retry with FileRenameInfo. - file_rename_info.Anonymous.ReplaceIfExists = true; - - cvt(unsafe { - c::SetFileInformationByHandle( - handle.as_raw_handle(), - c::FileRenameInfo, - (&raw const *file_rename_info).cast::(), - struct_size, - ) - })?; - } else { - return Err(err); - } - } - - Ok(()) -} - -pub fn rmdir(p: &Path) -> io::Result<()> { - let p = maybe_verbatim(p)?; - cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?; - Ok(()) -} - -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - // Open a file or directory without following symlinks. - let mut opts = OpenOptions::new(); - opts.access_mode(c::FILE_LIST_DIRECTORY); - // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories. - // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target. - opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); - let file = File::open(path, &opts)?; - - // Test if the file is not a directory or a symlink to a directory. - if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 { - return Err(io::Error::from_raw_os_error(c::ERROR_DIRECTORY as _)); - } - - // Remove the directory and all its contents. - remove_dir_all_iterative(file).io_result() -} - -pub fn readlink(path: &Path) -> io::Result { - // Open the link with no access mode, instead of generic read. - // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so - // this is needed for a common case. - let mut opts = OpenOptions::new(); - opts.access_mode(0); - opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); - let file = File::open(path, &opts)?; - file.readlink() -} - -pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { - symlink_inner(original, link, false) -} - -pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()> { - let original = to_u16s(original)?; - let link = maybe_verbatim(link)?; - let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 }; - // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10 - // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the - // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be - // added to dwFlags to opt into this behavior. - let result = cvt(unsafe { - c::CreateSymbolicLinkW( - link.as_ptr(), - original.as_ptr(), - flags | c::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, - ) as c::BOOL - }); - if let Err(err) = result { - if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as i32) { - // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, - // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag. - cvt(unsafe { - c::CreateSymbolicLinkW(link.as_ptr(), original.as_ptr(), flags) as c::BOOL - })?; - } else { - return Err(err); - } - } - Ok(()) -} - -#[cfg(not(target_vendor = "uwp"))] -pub fn link(original: &Path, link: &Path) -> io::Result<()> { - let original = maybe_verbatim(original)?; - let link = maybe_verbatim(link)?; - cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?; - Ok(()) -} - -#[cfg(target_vendor = "uwp")] -pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { - return Err(io::const_error!(io::ErrorKind::Unsupported, "hard link are not supported on UWP")); -} - -pub fn stat(path: &Path) -> io::Result { - match metadata(path, ReparsePoint::Follow) { - Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => { - if let Ok(attrs) = lstat(path) { - if !attrs.file_type().is_symlink() { - return Ok(attrs); - } - } - Err(err) - } - result => result, - } -} - -pub fn lstat(path: &Path) -> io::Result { - metadata(path, ReparsePoint::Open) -} - -#[repr(u32)] -#[derive(Clone, Copy, PartialEq, Eq)] -enum ReparsePoint { - Follow = 0, - Open = c::FILE_FLAG_OPEN_REPARSE_POINT, -} -impl ReparsePoint { - fn as_flag(self) -> u32 { - self as u32 - } -} - -fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result { - let mut opts = OpenOptions::new(); - // No read or write permissions are necessary - opts.access_mode(0); - opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag()); - - // Attempt to open the file normally. - // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileExW`. - // If the fallback fails for any reason we return the original error. - match File::open(path, &opts) { - Ok(file) => file.file_attr(), - Err(e) - if [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)] - .contains(&e.raw_os_error()) => - { - // `ERROR_ACCESS_DENIED` is returned when the user doesn't have permission for the resource. - // One such example is `System Volume Information` as default but can be created as well - // `ERROR_SHARING_VIOLATION` will almost never be returned. - // Usually if a file is locked you can still read some metadata. - // However, there are special system files, such as - // `C:\hiberfil.sys`, that are locked in a way that denies even that. - unsafe { - let path = maybe_verbatim(path)?; - - // `FindFirstFileExW` accepts wildcard file names. - // Fortunately wildcards are not valid file names and - // `ERROR_SHARING_VIOLATION` means the file exists (but is locked) - // therefore it's safe to assume the file name given does not - // include wildcards. - let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed(); - let handle = c::FindFirstFileExW( - path.as_ptr(), - c::FindExInfoBasic, - &mut wfd as *mut _ as _, - c::FindExSearchNameMatch, - ptr::null(), - 0, - ); - - if handle == c::INVALID_HANDLE_VALUE { - // This can fail if the user does not have read access to the - // directory. - Err(e) - } else { - // We no longer need the find handle. - c::FindClose(handle); - - // `FindFirstFileExW` reads the cached file information from the - // directory. The downside is that this metadata may be outdated. - let attrs = FileAttr::from(wfd); - if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() { - Err(e) - } else { - Ok(attrs) - } - } - } - } - Err(e) => Err(e), - } -} - -pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - let p = maybe_verbatim(p)?; - unsafe { - cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?; - Ok(()) - } -} - -fn get_path(f: &File) -> io::Result { - super::fill_utf16_buf( - |buf, sz| unsafe { - c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS) - }, - |buf| PathBuf::from(OsString::from_wide(buf)), - ) -} - -pub fn canonicalize(p: &Path) -> io::Result { - let mut opts = OpenOptions::new(); - // No read or write permissions are necessary - opts.access_mode(0); - // This flag is so we can open directories too - opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); - let f = File::open(p, &opts)?; - get_path(&f) -} - -pub fn copy(from: &Path, to: &Path) -> io::Result { - unsafe extern "system" fn callback( - _TotalFileSize: i64, - _TotalBytesTransferred: i64, - _StreamSize: i64, - StreamBytesTransferred: i64, - dwStreamNumber: u32, - _dwCallbackReason: u32, - _hSourceFile: c::HANDLE, - _hDestinationFile: c::HANDLE, - lpData: *const c_void, - ) -> u32 { - unsafe { - if dwStreamNumber == 1 { - *(lpData as *mut i64) = StreamBytesTransferred; - } - c::PROGRESS_CONTINUE - } - } - let pfrom = maybe_verbatim(from)?; - let pto = maybe_verbatim(to)?; - let mut size = 0i64; - cvt(unsafe { - c::CopyFileExW( - pfrom.as_ptr(), - pto.as_ptr(), - Some(callback), - (&raw mut size) as *mut _, - ptr::null_mut(), - 0, - ) - })?; - Ok(size as u64) -} - -pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { - // Create and open a new directory in one go. - let mut opts = OpenOptions::new(); - opts.create_new(true); - opts.write(true); - opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_POSIX_SEMANTICS); - opts.attributes(c::FILE_ATTRIBUTE_DIRECTORY); - - let d = File::open(link, &opts)?; - - // We need to get an absolute, NT-style path. - let path_bytes = original.as_os_str().as_encoded_bytes(); - let abs_path: Vec = if path_bytes.starts_with(br"\\?\") || path_bytes.starts_with(br"\??\") - { - // It's already an absolute path, we just need to convert the prefix to `\??\` - let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&path_bytes[4..]) }; - r"\??\".encode_utf16().chain(bytes.encode_wide()).collect() - } else { - // Get an absolute path and then convert the prefix to `\??\` - let abs_path = crate::path::absolute(original)?.into_os_string().into_encoded_bytes(); - if abs_path.len() > 0 && abs_path[1..].starts_with(br":\") { - let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path) }; - r"\??\".encode_utf16().chain(bytes.encode_wide()).collect() - } else if abs_path.starts_with(br"\\.\") { - let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[4..]) }; - r"\??\".encode_utf16().chain(bytes.encode_wide()).collect() - } else if abs_path.starts_with(br"\\") { - let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[2..]) }; - r"\??\UNC\".encode_utf16().chain(bytes.encode_wide()).collect() - } else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, "path is not valid")); - } - }; - // Defined inline so we don't have to mess about with variable length buffer. - #[repr(C)] - pub struct MountPointBuffer { - ReparseTag: u32, - ReparseDataLength: u16, - Reserved: u16, - SubstituteNameOffset: u16, - SubstituteNameLength: u16, - PrintNameOffset: u16, - PrintNameLength: u16, - PathBuffer: [MaybeUninit; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize], - } - let data_len = 12 + (abs_path.len() * 2); - if data_len > u16::MAX as usize { - return Err(io::const_error!(io::ErrorKind::InvalidInput, "`original` path is too long")); - } - let data_len = data_len as u16; - let mut header = MountPointBuffer { - ReparseTag: c::IO_REPARSE_TAG_MOUNT_POINT, - ReparseDataLength: data_len, - Reserved: 0, - SubstituteNameOffset: 0, - SubstituteNameLength: (abs_path.len() * 2) as u16, - PrintNameOffset: ((abs_path.len() + 1) * 2) as u16, - PrintNameLength: 0, - PathBuffer: [MaybeUninit::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize], - }; - unsafe { - let ptr = header.PathBuffer.as_mut_ptr(); - ptr.copy_from(abs_path.as_ptr().cast::>(), abs_path.len()); - - let mut ret = 0; - cvt(c::DeviceIoControl( - d.as_raw_handle(), - c::FSCTL_SET_REPARSE_POINT, - (&raw const header).cast::(), - data_len as u32 + 8, - ptr::null_mut(), - 0, - &mut ret, - ptr::null_mut(), - )) - .map(drop) - } -} - -// Try to see if a file exists but, unlike `exists`, report I/O errors. -pub fn exists(path: &Path) -> io::Result { - // Open the file to ensure any symlinks are followed to their target. - let mut opts = OpenOptions::new(); - // No read, write, etc access rights are needed. - opts.access_mode(0); - // Backup semantics enables opening directories as well as files. - opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); - match File::open(path, &opts) { - Err(e) => match e.kind() { - // The file definitely does not exist - io::ErrorKind::NotFound => Ok(false), - - // `ERROR_SHARING_VIOLATION` means that the file has been locked by - // another process. This is often temporary so we simply report it - // as the file existing. - _ if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => Ok(true), - - // `ERROR_CANT_ACCESS_FILE` means that a file exists but that the - // reparse point could not be handled by `CreateFile`. - // This can happen for special files such as: - // * Unix domain sockets which you need to `connect` to - // * App exec links which require using `CreateProcess` - _ if e.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => Ok(true), - - // Other errors such as `ERROR_ACCESS_DENIED` may indicate that the - // file exists. However, these types of errors are usually more - // permanent so we report them here. - _ => Err(e), - }, - // The file was opened successfully therefore it must exist, - Ok(_) => Ok(true), - } -} diff --git a/library/std/src/sys/pal/windows/futex.rs b/library/std/src/sys/pal/windows/futex.rs index 38afb8c043b3b..cfa0a6b3815bd 100644 --- a/library/std/src/sys/pal/windows/futex.rs +++ b/library/std/src/sys/pal/windows/futex.rs @@ -1,21 +1,21 @@ use core::ffi::c_void; +use core::ptr; use core::sync::atomic::{ - AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, AtomicPtr, AtomicU8, - AtomicU16, AtomicU32, AtomicU64, AtomicUsize, + Atomic, AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, AtomicPtr, + AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize, }; use core::time::Duration; -use core::{mem, ptr}; use super::api::{self, WinError}; use crate::sys::{c, dur2timeout}; /// An atomic for use as a futex that is at least 32-bits but may be larger -pub type Futex = AtomicU32; +pub type Futex = Atomic; /// Must be the underlying type of Futex pub type Primitive = u32; /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallFutex = AtomicU8; +pub type SmallFutex = Atomic; /// Must be the underlying type of SmallFutex pub type SmallPrimitive = u8; @@ -47,10 +47,10 @@ unsafe_waitable_int! { (usize, AtomicUsize), } unsafe impl Waitable for *const T { - type Futex = AtomicPtr; + type Futex = Atomic<*mut T>; } unsafe impl Waitable for *mut T { - type Futex = AtomicPtr; + type Futex = Atomic<*mut T>; } unsafe impl Futexable for AtomicPtr {} @@ -61,7 +61,7 @@ pub fn wait_on_address( ) -> bool { unsafe { let addr = ptr::from_ref(address).cast::(); - let size = mem::size_of::(); + let size = size_of::(); let compare_addr = (&raw const compare).cast::(); let timeout = timeout.map(dur2timeout).unwrap_or(c::INFINITE); c::WaitOnAddress(addr, compare_addr, size, timeout) == c::TRUE diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 1eca346b76c2b..8f54e2376eb8b 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -14,17 +14,12 @@ pub mod compat; pub mod api; -pub mod args; pub mod c; -pub mod env; -pub mod fs; #[cfg(not(target_vendor = "win7"))] pub mod futex; pub mod handle; pub mod os; pub mod pipe; -pub mod process; -pub mod stdio; pub mod thread; pub mod time; cfg_if::cfg_if! { @@ -37,7 +32,7 @@ cfg_if::cfg_if! { } /// Map a [`Result`] to [`io::Result`](crate::io::Result). -trait IoResult { +pub trait IoResult { fn io_result(self) -> crate::io::Result; } impl IoResult for Result { @@ -289,6 +284,14 @@ pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] { } } +pub fn ensure_no_nuls>(s: T) -> crate::io::Result { + if s.as_ref().encode_wide().any(|b| b == 0) { + Err(crate::io::const_error!(ErrorKind::InvalidInput, "nul byte found in provided data")) + } else { + Ok(s) + } +} + pub trait IsZero { fn is_zero(&self) -> bool; } @@ -325,8 +328,12 @@ pub fn dur2timeout(dur: Duration) -> u32 { /// 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` +/// 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, which +/// will still terminate the process but might run some exception handlers. +/// +/// https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail #[cfg(not(miri))] // inline assembly does not work in Miri pub fn abort_internal() -> ! { unsafe { diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index 044dc2e8cd8fa..1ebbbec9e914f 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -5,16 +5,16 @@ #[cfg(test)] mod tests; +use super::api; #[cfg(not(target_vendor = "uwp"))] use super::api::WinError; -use super::{api, to_u16s}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::os::windows::ffi::EncodeWide; use crate::os::windows::prelude::*; use crate::path::{self, PathBuf}; -use crate::sys::{c, cvt}; -use crate::{fmt, io, ptr, slice}; +use crate::sys::pal::{c, cvt}; +use crate::{fmt, io, ptr}; pub fn errno() -> i32 { api::get_last_error().code as i32 @@ -76,108 +76,6 @@ pub fn error_string(mut errnum: i32) -> String { } } -pub struct Env { - base: *mut c::WCHAR, - iter: EnvIterator, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - iter: &'a EnvIterator, -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - let iter: EnvIterator = (*iter).clone(); - let mut list = f.debug_list(); - for (a, b) in iter { - list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); - } - list.finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { base: _, iter } = self; - EnvStrDebug { iter } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { base: _, iter } = self; - f.debug_list().entries(iter.clone()).finish() - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self { base: _, iter } = self; - iter.next() - } -} - -#[derive(Clone)] -struct EnvIterator(*mut c::WCHAR); - -impl Iterator for EnvIterator { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self(cur) = self; - loop { - unsafe { - if **cur == 0 { - return None; - } - let p = *cur as *const u16; - let mut len = 0; - while *p.add(len) != 0 { - len += 1; - } - let s = slice::from_raw_parts(p, len); - *cur = cur.add(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.is_null() { - panic!("failure getting env string from OS: {}", io::Error::last_os_error()); - } - Env { base: ch, iter: EnvIterator(ch) } - } -} - pub struct SplitPaths<'a> { data: EncodeWide<'a>, must_yield: bool, @@ -290,33 +188,6 @@ pub fn chdir(p: &path::Path) -> io::Result<()> { cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop) } -pub fn getenv(k: &OsStr) -> Option { - let k = to_u16s(k).ok()?; - super::fill_utf16_buf( - |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) }, - OsStringExt::from_wide, - ) - .ok() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - // SAFETY: We ensure that k and v are null-terminated wide strings. - unsafe { - let k = to_u16s(k)?; - let v = to_u16s(v)?; - - cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop) - } -} - -pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { - // SAFETY: We ensure that v is a null-terminated wide strings. - unsafe { - let v = to_u16s(n)?; - cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop) - } -} - pub fn temp_dir() -> PathBuf { super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPath2W(sz, buf) }, super::os2path).unwrap() } @@ -331,6 +202,8 @@ fn home_dir_crt() -> Option { |buf, mut sz| { // GetUserProfileDirectoryW does not quite use the usual protocol for // negotiating the buffer size, so we have to translate. + // FIXME(#141254): We rely on the *undocumented* property that this function will + // always set the size, not just on failure. match c::GetUserProfileDirectoryW( ptr::without_provenance_mut(CURRENT_PROCESS_TOKEN), buf, diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index a8f6617c9dc8f..00d469fbaf8c7 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -3,8 +3,8 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::os::windows::prelude::*; use crate::path::Path; use crate::random::{DefaultRandomSource, Random}; -use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::Relaxed; +use crate::sync::atomic::{Atomic, AtomicUsize}; use crate::sys::c; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; @@ -74,7 +74,6 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res let ours; let mut name; let mut tries = 0; - let mut reject_remote_clients_flag = c::PIPE_REJECT_REMOTE_CLIENTS; loop { tries += 1; name = format!( @@ -96,7 +95,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res c::PIPE_TYPE_BYTE | c::PIPE_READMODE_BYTE | c::PIPE_WAIT - | reject_remote_clients_flag, + | c::PIPE_REJECT_REMOTE_CLIENTS, 1, PIPE_BUFFER_CAPACITY, PIPE_BUFFER_CAPACITY, @@ -112,30 +111,15 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res // // Don't try again too much though as this could also perhaps be a // legit error. - // If `ERROR_INVALID_PARAMETER` is returned, this probably means we're - // running on pre-Vista version where `PIPE_REJECT_REMOTE_CLIENTS` is - // not supported, so we continue retrying without it. This implies - // reduced security on Windows versions older than Vista by allowing - // connections to this pipe from remote machines. - // Proper fix would increase the number of FFI imports and introduce - // significant amount of Windows XP specific code with no clean - // testing strategy - // For more info, see https://github.com/rust-lang/rust/pull/37677. if handle == c::INVALID_HANDLE_VALUE { let error = api::get_last_error(); - if tries < 10 { - if error == WinError::ACCESS_DENIED { - continue; - } else if reject_remote_clients_flag != 0 - && error == WinError::INVALID_PARAMETER - { - reject_remote_clients_flag = 0; - tries -= 1; - continue; - } + if tries < 10 && error == WinError::ACCESS_DENIED { + continue; + } else { + return Err(io::Error::from_raw_os_error(error.code as i32)); } - return Err(io::Error::from_raw_os_error(error.code as i32)); } + ours = Handle::from_raw_handle(handle); break; } @@ -151,7 +135,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res opts.write(ours_readable); opts.read(!ours_readable); opts.share_mode(0); - let size = mem::size_of::(); + let size = size_of::(); let mut sa = c::SECURITY_ATTRIBUTES { nLength: size as u32, lpSecurityDescriptor: ptr::null_mut(), @@ -159,7 +143,6 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res }; opts.security_attributes(&mut sa); let theirs = File::open(Path::new(&name), &opts)?; - let theirs = AnonPipe { inner: theirs.into_inner() }; Ok(Pipes { ours: AnonPipe { inner: ours }, @@ -209,7 +192,7 @@ pub fn spawn_pipe_relay( } fn random_number() -> usize { - static N: AtomicUsize = AtomicUsize::new(0); + static N: Atomic = AtomicUsize::new(0); loop { if N.load(Relaxed) != 0 { return N.fetch_add(1, Relaxed); diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs deleted file mode 100644 index 6eff471f38670..0000000000000 --- a/library/std/src/sys/pal/windows/process.rs +++ /dev/null @@ -1,923 +0,0 @@ -#![unstable(feature = "process_internals", issue = "none")] - -#[cfg(test)] -mod tests; - -use core::ffi::c_void; - -use super::api::{self, WinError}; -use crate::collections::BTreeMap; -use crate::env::consts::{EXE_EXTENSION, EXE_SUFFIX}; -use crate::ffi::{OsStr, OsString}; -use crate::io::{self, Error, ErrorKind}; -use crate::num::NonZero; -use crate::os::windows::ffi::{OsStrExt, OsStringExt}; -use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle}; -use crate::os::windows::process::ProcThreadAttributeList; -use crate::path::{Path, PathBuf}; -use crate::sync::Mutex; -use crate::sys::args::{self, Arg}; -use crate::sys::c::{self, EXIT_FAILURE, EXIT_SUCCESS}; -use crate::sys::fs::{File, OpenOptions}; -use crate::sys::handle::Handle; -use crate::sys::pipe::{self, AnonPipe}; -use crate::sys::{cvt, path, stdio}; -use crate::sys_common::IntoInner; -use crate::sys_common::process::{CommandEnv, CommandEnvs}; -use crate::{cmp, env, fmt, mem, ptr}; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Clone, Debug, Eq)] -#[doc(hidden)] -pub struct EnvKey { - os_string: OsString, - // This stores a UTF-16 encoded string to workaround the mismatch between - // Rust's OsString (WTF-8) and the Windows API string type (UTF-16). - // Normally converting on every API call is acceptable but here - // `c::CompareStringOrdinal` will be called for every use of `==`. - utf16: Vec, -} - -impl EnvKey { - fn new>(key: T) -> Self { - EnvKey::from(key.into()) - } -} - -// Comparing Windows environment variable keys[1] are behaviorally the -// composition of two operations[2]: -// -// 1. Case-fold both strings. This is done using a language-independent -// uppercase mapping that's unique to Windows (albeit based on data from an -// older Unicode spec). It only operates on individual UTF-16 code units so -// surrogates are left unchanged. This uppercase mapping can potentially change -// between Windows versions. -// -// 2. Perform an ordinal comparison of the strings. A comparison using ordinal -// is just a comparison based on the numerical value of each UTF-16 code unit[3]. -// -// Because the case-folding mapping is unique to Windows and not guaranteed to -// be stable, we ask the OS to compare the strings for us. This is done by -// calling `CompareStringOrdinal`[4] with `bIgnoreCase` set to `TRUE`. -// -// [1] https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#choosing-a-stringcomparison-member-for-your-method-call -// [2] https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#stringtoupper-and-stringtolower -// [3] https://docs.microsoft.com/en-us/dotnet/api/system.stringcomparison?view=net-5.0#System_StringComparison_Ordinal -// [4] https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-comparestringordinal -impl Ord for EnvKey { - fn cmp(&self, other: &Self) -> cmp::Ordering { - unsafe { - let result = c::CompareStringOrdinal( - self.utf16.as_ptr(), - self.utf16.len() as _, - other.utf16.as_ptr(), - other.utf16.len() as _, - c::TRUE, - ); - match result { - c::CSTR_LESS_THAN => cmp::Ordering::Less, - c::CSTR_EQUAL => cmp::Ordering::Equal, - c::CSTR_GREATER_THAN => cmp::Ordering::Greater, - // `CompareStringOrdinal` should never fail so long as the parameters are correct. - _ => panic!("comparing environment keys failed: {}", Error::last_os_error()), - } - } - } -} -impl PartialOrd for EnvKey { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl PartialEq for EnvKey { - fn eq(&self, other: &Self) -> bool { - if self.utf16.len() != other.utf16.len() { - false - } else { - self.cmp(other) == cmp::Ordering::Equal - } - } -} -impl PartialOrd for EnvKey { - fn partial_cmp(&self, other: &str) -> Option { - Some(self.cmp(&EnvKey::new(other))) - } -} -impl PartialEq for EnvKey { - fn eq(&self, other: &str) -> bool { - if self.os_string.len() != other.len() { - false - } else { - self.cmp(&EnvKey::new(other)) == cmp::Ordering::Equal - } - } -} - -// Environment variable keys should preserve their original case even though -// they are compared using a caseless string mapping. -impl From for EnvKey { - fn from(k: OsString) -> Self { - EnvKey { utf16: k.encode_wide().collect(), os_string: k } - } -} - -impl From for OsString { - fn from(k: EnvKey) -> Self { - k.os_string - } -} - -impl From<&OsStr> for EnvKey { - fn from(k: &OsStr) -> Self { - Self::from(k.to_os_string()) - } -} - -impl AsRef for EnvKey { - fn as_ref(&self) -> &OsStr { - &self.os_string - } -} - -pub(crate) fn ensure_no_nuls>(s: T) -> io::Result { - if s.as_ref().encode_wide().any(|b| b == 0) { - Err(io::const_error!(ErrorKind::InvalidInput, "nul byte found in provided data")) - } else { - Ok(s) - } -} - -pub struct Command { - program: OsString, - args: Vec, - env: CommandEnv, - cwd: Option, - flags: u32, - show_window: Option, - detach: bool, // not currently exposed in std::process - stdin: Option, - stdout: Option, - stderr: Option, - force_quotes_enabled: bool, -} - -pub enum Stdio { - Inherit, - InheritSpecific { from_stdio_id: u32 }, - Null, - MakePipe, - Pipe(AnonPipe), - Handle(Handle), -} - -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - -impl Command { - pub fn new(program: &OsStr) -> Command { - Command { - program: program.to_os_string(), - args: Vec::new(), - env: Default::default(), - cwd: None, - flags: 0, - show_window: None, - detach: false, - stdin: None, - stdout: None, - stderr: None, - force_quotes_enabled: false, - } - } - - pub fn arg(&mut self, arg: &OsStr) { - self.args.push(Arg::Regular(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 show_window(&mut self, cmd_show: Option) { - self.show_window = cmd_show; - } - - pub fn force_quotes(&mut self, enabled: bool) { - self.force_quotes_enabled = enabled; - } - - pub fn raw_arg(&mut self, command_str_to_append: &OsStr) { - self.args.push(Arg::Raw(command_str_to_append.to_os_string())) - } - - pub fn get_program(&self) -> &OsStr { - &self.program - } - - pub fn get_args(&self) -> CommandArgs<'_> { - let iter = self.args.iter(); - CommandArgs { iter } - } - - pub fn get_envs(&self) -> CommandEnvs<'_> { - self.env.iter() - } - - pub fn get_current_dir(&self) -> Option<&Path> { - self.cwd.as_ref().map(Path::new) - } - - pub fn spawn( - &mut self, - default: Stdio, - needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - self.spawn_with_attributes(default, needs_stdin, None) - } - - pub fn spawn_with_attributes( - &mut self, - default: Stdio, - needs_stdin: bool, - proc_thread_attribute_list: Option<&ProcThreadAttributeList<'_>>, - ) -> io::Result<(Process, StdioPipes)> { - let env_saw_path = self.env.have_changed_path(); - let maybe_env = self.env.capture_if_changed(); - - let child_paths = if env_saw_path && let Some(env) = maybe_env.as_ref() { - env.get(&EnvKey::new("PATH")).map(|s| s.as_os_str()) - } else { - None - }; - let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?; - let has_bat_extension = |program: &[u16]| { - matches!( - // Case insensitive "ends_with" of UTF-16 encoded ".bat" or ".cmd" - program.len().checked_sub(4).and_then(|i| program.get(i..)), - Some([46, 98 | 66, 97 | 65, 116 | 84] | [46, 99 | 67, 109 | 77, 100 | 68]) - ) - }; - let is_batch_file = if path::is_verbatim(&program) { - has_bat_extension(&program[..program.len() - 1]) - } else { - super::fill_utf16_buf( - |buffer, size| unsafe { - // resolve the path so we can test the final file name. - c::GetFullPathNameW(program.as_ptr(), size, buffer, ptr::null_mut()) - }, - |program| has_bat_extension(program), - )? - }; - let (program, mut cmd_str) = if is_batch_file { - ( - command_prompt()?, - args::make_bat_command_line(&program, &self.args, self.force_quotes_enabled)?, - ) - } else { - let cmd_str = make_command_line(&self.program, &self.args, self.force_quotes_enabled)?; - (program, cmd_str) - }; - 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: - // https://support.microsoft.com/kb/315939 - static CREATE_PROCESS_LOCK: Mutex<()> = Mutex::new(()); - - let _guard = CREATE_PROCESS_LOCK.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)?; - - let mut si = zeroed_startupinfo(); - - // If at least one of stdin, stdout or stderr are set (i.e. are non null) - // then set the `hStd` fields in `STARTUPINFO`. - // Otherwise skip this and allow the OS to apply its default behavior. - // This provides more consistent behavior between Win7 and Win8+. - let is_set = |stdio: &Handle| !stdio.as_raw_handle().is_null(); - if is_set(&stderr) || is_set(&stdout) || is_set(&stdin) { - si.dwFlags |= c::STARTF_USESTDHANDLES; - si.hStdInput = stdin.as_raw_handle(); - si.hStdOutput = stdout.as_raw_handle(); - si.hStdError = stderr.as_raw_handle(); - } - - if let Some(cmd_show) = self.show_window { - si.dwFlags |= c::STARTF_USESHOWWINDOW; - si.wShowWindow = cmd_show; - } - - let si_ptr: *mut c::STARTUPINFOW; - - let mut si_ex; - - if let Some(proc_thread_attribute_list) = proc_thread_attribute_list { - si.cb = mem::size_of::() as u32; - flags |= c::EXTENDED_STARTUPINFO_PRESENT; - - si_ex = c::STARTUPINFOEXW { - StartupInfo: si, - // SAFETY: Casting this `*const` pointer to a `*mut` pointer is "safe" - // here because windows does not internally mutate the attribute list. - // Ideally this should be reflected in the interface of the `windows-sys` crate. - lpAttributeList: proc_thread_attribute_list.as_ptr().cast::().cast_mut(), - }; - si_ptr = (&raw mut si_ex) as _; - } else { - si.cb = mem::size_of::() as u32; - si_ptr = (&raw mut si) as _; - } - - unsafe { - cvt(c::CreateProcessW( - program.as_ptr(), - cmd_str.as_mut_ptr(), - ptr::null_mut(), - ptr::null_mut(), - c::TRUE, - flags, - envp, - dirp, - si_ptr, - &mut pi, - )) - }?; - - unsafe { - Ok(( - Process { - handle: Handle::from_raw_handle(pi.hProcess), - main_thread_handle: Handle::from_raw_handle(pi.hThread), - }, - pipes, - )) - } - } - - pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; - crate::sys_common::process::wait_with_output(proc, pipes) - } -} - -impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.program.fmt(f)?; - for arg in &self.args { - f.write_str(" ")?; - match arg { - Arg::Regular(s) => s.fmt(f), - Arg::Raw(s) => f.write_str(&s.to_string_lossy()), - }?; - } - Ok(()) - } -} - -// Resolve `exe_path` to the executable name. -// -// * If the path is simply a file name then use the paths given by `search_paths` to find the executable. -// * Otherwise use the `exe_path` as given. -// -// This function may also append `.exe` to the name. The rationale for doing so is as follows: -// -// It is a very strong convention that Windows executables have the `exe` extension. -// In Rust, it is common to omit this extension. -// Therefore this functions first assumes `.exe` was intended. -// It falls back to the plain file name if a full path is given and the extension is omitted -// or if only a file name is given and it already contains an extension. -fn resolve_exe<'a>( - exe_path: &'a OsStr, - parent_paths: impl FnOnce() -> Option, - child_paths: Option<&OsStr>, -) -> io::Result> { - // Early return if there is no filename. - if exe_path.is_empty() || path::has_trailing_slash(exe_path) { - return Err(io::const_error!(io::ErrorKind::InvalidInput, "program path has no file name")); - } - // Test if the file name has the `exe` extension. - // This does a case-insensitive `ends_with`. - let has_exe_suffix = if exe_path.len() >= EXE_SUFFIX.len() { - exe_path.as_encoded_bytes()[exe_path.len() - EXE_SUFFIX.len()..] - .eq_ignore_ascii_case(EXE_SUFFIX.as_bytes()) - } else { - false - }; - - // If `exe_path` is an absolute path or a sub-path then don't search `PATH` for it. - if !path::is_file_name(exe_path) { - if has_exe_suffix { - // The application name is a path to a `.exe` file. - // Let `CreateProcessW` figure out if it exists or not. - return args::to_user_path(Path::new(exe_path)); - } - let mut path = PathBuf::from(exe_path); - - // Append `.exe` if not already there. - path = path::append_suffix(path, EXE_SUFFIX.as_ref()); - if let Some(path) = program_exists(&path) { - return Ok(path); - } else { - // It's ok to use `set_extension` here because the intent is to - // remove the extension that was just added. - path.set_extension(""); - return args::to_user_path(&path); - } - } else { - ensure_no_nuls(exe_path)?; - // From the `CreateProcessW` docs: - // > If the file name does not contain an extension, .exe is appended. - // Note that this rule only applies when searching paths. - let has_extension = exe_path.as_encoded_bytes().contains(&b'.'); - - // Search the directories given by `search_paths`. - let result = search_paths(parent_paths, child_paths, |mut path| { - path.push(exe_path); - if !has_extension { - path.set_extension(EXE_EXTENSION); - } - program_exists(&path) - }); - if let Some(path) = result { - return Ok(path); - } - } - // If we get here then the executable cannot be found. - Err(io::const_error!(io::ErrorKind::NotFound, "program not found")) -} - -// Calls `f` for every path that should be used to find an executable. -// Returns once `f` returns the path to an executable or all paths have been searched. -fn search_paths( - parent_paths: Paths, - child_paths: Option<&OsStr>, - mut exists: Exists, -) -> Option> -where - Paths: FnOnce() -> Option, - Exists: FnMut(PathBuf) -> Option>, -{ - // 1. Child paths - // This is for consistency with Rust's historic behavior. - if let Some(paths) = child_paths { - for path in env::split_paths(paths).filter(|p| !p.as_os_str().is_empty()) { - if let Some(path) = exists(path) { - return Some(path); - } - } - } - - // 2. Application path - if let Ok(mut app_path) = env::current_exe() { - app_path.pop(); - if let Some(path) = exists(app_path) { - return Some(path); - } - } - - // 3 & 4. System paths - // SAFETY: This uses `fill_utf16_buf` to safely call the OS functions. - unsafe { - if let Ok(Some(path)) = super::fill_utf16_buf( - |buf, size| c::GetSystemDirectoryW(buf, size), - |buf| exists(PathBuf::from(OsString::from_wide(buf))), - ) { - return Some(path); - } - #[cfg(not(target_vendor = "uwp"))] - { - if let Ok(Some(path)) = super::fill_utf16_buf( - |buf, size| c::GetWindowsDirectoryW(buf, size), - |buf| exists(PathBuf::from(OsString::from_wide(buf))), - ) { - return Some(path); - } - } - } - - // 5. Parent paths - if let Some(parent_paths) = parent_paths() { - for path in env::split_paths(&parent_paths).filter(|p| !p.as_os_str().is_empty()) { - if let Some(path) = exists(path) { - return Some(path); - } - } - } - None -} - -/// Checks if a file exists without following symlinks. -fn program_exists(path: &Path) -> Option> { - unsafe { - let path = args::to_user_path(path).ok()?; - // Getting attributes using `GetFileAttributesW` does not follow symlinks - // and it will almost always be successful if the link exists. - // There are some exceptions for special system files (e.g. the pagefile) - // but these are not executable. - if c::GetFileAttributesW(path.as_ptr()) == c::INVALID_FILE_ATTRIBUTES { - None - } else { - Some(path) - } - } -} - -impl Stdio { - fn to_handle(&self, stdio_id: u32, pipe: &mut Option) -> io::Result { - let use_stdio_id = |stdio_id| match stdio::get_handle(stdio_id) { - Ok(io) => unsafe { - let io = Handle::from_raw_handle(io); - let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); - let _ = io.into_raw_handle(); // Don't close the handle - ret - }, - // If no stdio handle is available, then propagate the null value. - Err(..) => unsafe { Ok(Handle::from_raw_handle(ptr::null_mut())) }, - }; - match *self { - Stdio::Inherit => use_stdio_id(stdio_id), - Stdio::InheritSpecific { from_stdio_id } => use_stdio_id(from_stdio_id), - - 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::Pipe(ref source) => { - let ours_readable = stdio_id != c::STD_INPUT_HANDLE; - pipe::spawn_pipe_relay(source, ours_readable, true).map(AnonPipe::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 u32, - 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(r"\\.\NUL"), &opts).map(|file| file.into_inner()) - } - } - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - Stdio::Pipe(pipe) - } -} - -impl From for Stdio { - fn from(file: File) -> Stdio { - Stdio::Handle(file.into_inner()) - } -} - -impl From for Stdio { - fn from(_: io::Stdout) -> Stdio { - Stdio::InheritSpecific { from_stdio_id: c::STD_OUTPUT_HANDLE } - } -} - -impl From for Stdio { - fn from(_: io::Stderr) -> Stdio { - Stdio::InheritSpecific { from_stdio_id: c::STD_ERROR_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, - main_thread_handle: Handle, -} - -impl Process { - pub fn kill(&mut self) -> io::Result<()> { - let result = unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) }; - if result == c::FALSE { - let error = api::get_last_error(); - // TerminateProcess returns ERROR_ACCESS_DENIED if the process has already been - // terminated (by us, or for any other reason). So check if the process was actually - // terminated, and if so, do not return an error. - if error != WinError::ACCESS_DENIED || self.try_wait().is_err() { - return Err(crate::io::Error::from_raw_os_error(error.code as i32)); - } - } - Ok(()) - } - - pub fn id(&self) -> u32 { - unsafe { c::GetProcessId(self.handle.as_raw_handle()) } - } - - pub fn main_thread_handle(&self) -> BorrowedHandle<'_> { - self.main_thread_handle.as_handle() - } - - pub fn wait(&mut self) -> io::Result { - unsafe { - let res = c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE); - if res != c::WAIT_OBJECT_0 { - return Err(Error::last_os_error()); - } - let mut status = 0; - cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?; - Ok(ExitStatus(status)) - } - } - - pub fn try_wait(&mut self) -> io::Result> { - unsafe { - match c::WaitForSingleObject(self.handle.as_raw_handle(), 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.as_raw_handle(), &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, Default)] -pub struct ExitStatus(u32); - -impl ExitStatus { - pub fn exit_ok(&self) -> Result<(), ExitStatusError> { - match NonZero::::try_from(self.0) { - /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), - /* was zero, couldn't convert */ Err(_) => Ok(()), - } - } - pub fn code(&self) -> Option { - Some(self.0 as i32) - } -} - -/// Converts a raw `u32` to a type-safe `ExitStatus` by wrapping it without copying. -impl From for ExitStatus { - fn from(u: u32) -> ExitStatus { - ExitStatus(u) - } -} - -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 ExitStatusError(NonZero); - -impl Into for ExitStatusError { - fn into(self) -> ExitStatus { - ExitStatus(self.0.into()) - } -} - -impl ExitStatusError { - pub fn code(self) -> Option> { - Some((u32::from(self.0) as i32).try_into().unwrap()) - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(u32); - -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 - } -} - -impl From for ExitCode { - fn from(code: u8) -> Self { - ExitCode(u32::from(code)) - } -} - -impl From for ExitCode { - fn from(code: u32) -> Self { - ExitCode(u32::from(code)) - } -} - -fn zeroed_startupinfo() -> c::STARTUPINFOW { - c::STARTUPINFOW { - cb: 0, - lpReserved: ptr::null_mut(), - lpDesktop: ptr::null_mut(), - lpTitle: ptr::null_mut(), - dwX: 0, - dwY: 0, - dwXSize: 0, - dwYSize: 0, - dwXCountChars: 0, - dwYCountChars: 0, - dwFillAttribute: 0, - dwFlags: 0, - wShowWindow: 0, - cbReserved2: 0, - lpReserved2: ptr::null_mut(), - hStdInput: ptr::null_mut(), - hStdOutput: ptr::null_mut(), - hStdError: ptr::null_mut(), - } -} - -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(argv0: &OsStr, args: &[Arg], force_quotes: bool) -> 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 to avoid ambiguity when - // the child process parses its arguments. - // Note that quotes aren't escaped here because they can't be used in arg0. - // But that's ok because file paths can't contain quotes. - cmd.push(b'"' as u16); - cmd.extend(argv0.encode_wide()); - cmd.push(b'"' as u16); - - for arg in args { - cmd.push(' ' as u16); - args::append_arg(&mut cmd, arg, force_quotes)?; - } - Ok(cmd) -} - -// Get `cmd.exe` for use with bat scripts, encoded as a UTF-16 string. -fn command_prompt() -> io::Result> { - let mut system: Vec = super::fill_utf16_buf( - |buf, size| unsafe { c::GetSystemDirectoryW(buf, size) }, - |buf| buf.into(), - )?; - system.extend("\\cmd.exe".encode_utf16().chain([0])); - Ok(system) -} - -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(); - - // If there are no environment variables to set then signal this by - // pushing a null. - if env.is_empty() { - blk.push(0); - } - - for (k, v) in env { - ensure_no_nuls(k.os_string)?; - blk.extend(k.utf16); - 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())), - } -} - -pub struct CommandArgs<'a> { - iter: crate::slice::Iter<'a, Arg>, -} - -impl<'a> Iterator for CommandArgs<'a> { - type Item = &'a OsStr; - fn next(&mut self) -> Option<&'a OsStr> { - self.iter.next().map(|arg| match arg { - Arg::Regular(s) | Arg::Raw(s) => s.as_ref(), - }) - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl<'a> ExactSizeIterator for CommandArgs<'a> { - fn len(&self) -> usize { - self.iter.len() - } - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -impl<'a> fmt::Debug for CommandArgs<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter.clone()).finish() - } -} diff --git a/library/std/src/sys/pal/windows/stdio.rs b/library/std/src/sys/pal/windows/stdio.rs deleted file mode 100644 index 1b245991aa797..0000000000000 --- a/library/std/src/sys/pal/windows/stdio.rs +++ /dev/null @@ -1,472 +0,0 @@ -#![unstable(issue = "none", feature = "windows_stdio")] - -use core::char::MAX_LEN_UTF8; -use core::str::utf8_char_width; - -use super::api::{self, WinError}; -use crate::mem::MaybeUninit; -use crate::os::windows::io::{FromRawHandle, IntoRawHandle}; -use crate::sys::handle::Handle; -use crate::sys::{c, cvt}; -use crate::{cmp, io, ptr, str}; - -#[cfg(test)] -mod tests; - -// 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, - incomplete_utf8: IncompleteUtf8, -} - -pub struct Stdout { - incomplete_utf8: IncompleteUtf8, -} - -pub struct Stderr { - incomplete_utf8: IncompleteUtf8, -} - -struct IncompleteUtf8 { - bytes: [u8; 4], - len: u8, -} - -impl IncompleteUtf8 { - // Implemented for use in Stdin::read. - fn read(&mut self, buf: &mut [u8]) -> usize { - // Write to buffer until the buffer is full or we run out of bytes. - let to_write = cmp::min(buf.len(), self.len as usize); - buf[..to_write].copy_from_slice(&self.bytes[..to_write]); - - // Rotate the remaining bytes if not enough remaining space in buffer. - if usize::from(self.len) > buf.len() { - self.bytes.copy_within(to_write.., 0); - self.len -= to_write as u8; - } else { - self.len = 0; - } - - to_write - } -} - -// 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: u32) -> 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 } -} - -/// Returns true if the attached console's code page is currently UTF-8. -#[cfg(not(target_vendor = "win7"))] -fn is_utf8_console() -> bool { - unsafe { c::GetConsoleOutputCP() == c::CP_UTF8 } -} - -#[cfg(target_vendor = "win7")] -fn is_utf8_console() -> bool { - // Windows 7 has a fun "feature" where WriteFile on a console handle will return - // the number of UTF-16 code units written and not the number of bytes from the input string. - // So we always claim the console isn't UTF-8 to trigger the WriteConsole fallback code. - false -} - -fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> io::Result { - if data.is_empty() { - return Ok(0); - } - - let handle = get_handle(handle_id)?; - if !is_console(handle) || is_utf8_console() { - unsafe { - let handle = Handle::from_raw_handle(handle); - let ret = handle.write(data); - let _ = handle.into_raw_handle(); // Don't close the handle - return ret; - } - } else { - write_console_utf16(data, incomplete_utf8, handle) - } -} - -fn write_console_utf16( - data: &[u8], - incomplete_utf8: &mut IncompleteUtf8, - handle: c::HANDLE, -) -> io::Result { - if incomplete_utf8.len > 0 { - assert!( - incomplete_utf8.len < 4, - "Unexpected number of bytes for incomplete UTF-8 codepoint." - ); - if data[0] >> 6 != 0b10 { - // not a continuation byte - reject - incomplete_utf8.len = 0; - return Err(io::const_error!( - io::ErrorKind::InvalidData, - "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", - )); - } - incomplete_utf8.bytes[incomplete_utf8.len as usize] = data[0]; - incomplete_utf8.len += 1; - let char_width = utf8_char_width(incomplete_utf8.bytes[0]); - if (incomplete_utf8.len as usize) < char_width { - // more bytes needed - return Ok(1); - } - let s = str::from_utf8(&incomplete_utf8.bytes[0..incomplete_utf8.len as usize]); - incomplete_utf8.len = 0; - match s { - Ok(s) => { - assert_eq!(char_width, s.len()); - let written = write_valid_utf8_to_console(handle, s)?; - assert_eq!(written, s.len()); // guaranteed by write_valid_utf8_to_console() for single codepoint writes - return Ok(1); - } - Err(_) => { - return Err(io::const_error!( - io::ErrorKind::InvalidData, - "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", - )); - } - } - } - - // As the console is meant for presenting text, we assume bytes of `data` 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. - // If the first byte is invalid it is either first byte of a multi-byte sequence but the - // provided byte slice is too short or it is the first byte of an invalid multi-byte sequence. - 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 => { - let first_byte_char_width = utf8_char_width(data[0]); - if first_byte_char_width > 1 && data.len() < first_byte_char_width { - incomplete_utf8.bytes[0] = data[0]; - incomplete_utf8.len = 1; - return Ok(1); - } else { - return Err(io::const_error!( - 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(), - }; - - write_valid_utf8_to_console(handle, utf8) -} - -fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result { - debug_assert!(!utf8.is_empty()); - - let mut utf16 = [MaybeUninit::::uninit(); MAX_BUFFER_SIZE / 2]; - let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len())]; - - let utf16: &[u16] = unsafe { - // Note that this theoretically checks validity twice in the (most common) case - // where the underlying byte sequence is valid utf-8 (given the check in `write()`). - let result = c::MultiByteToWideChar( - c::CP_UTF8, // CodePage - c::MB_ERR_INVALID_CHARS, // dwFlags - utf8.as_ptr(), // lpMultiByteStr - utf8.len() as i32, // cbMultiByte - utf16.as_mut_ptr() as *mut c::WCHAR, // lpWideCharStr - utf16.len() as i32, // cchWideChar - ); - assert!(result != 0, "Unexpected error in MultiByteToWideChar"); - - // Safety: MultiByteToWideChar initializes `result` values. - utf16[..result as usize].assume_init_ref() - }; - - 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_code_unit_remaining = utf16[written]; - if matches!(first_code_unit_remaining, 0xDCEE..=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 { - debug_assert!(data.len() < u32::MAX as usize); - let mut written = 0; - cvt(unsafe { - c::WriteConsoleW(handle, data.as_ptr(), data.len() as u32, &mut written, ptr::null_mut()) - })?; - Ok(written as usize) -} - -impl Stdin { - pub const fn new() -> Stdin { - Stdin { surrogate: 0, incomplete_utf8: IncompleteUtf8::new() } - } -} - -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) { - unsafe { - let handle = Handle::from_raw_handle(handle); - let ret = handle.read(buf); - let _ = handle.into_raw_handle(); // Don't close the handle - return ret; - } - } - - // If there are bytes in the incomplete utf-8, start with those. - // (No-op if there is nothing in the buffer.) - let mut bytes_copied = self.incomplete_utf8.read(buf); - - if bytes_copied == buf.len() { - Ok(bytes_copied) - } else if buf.len() - bytes_copied < 4 { - // Not enough space to get a UTF-8 byte. We will use the incomplete UTF8. - let mut utf16_buf = [MaybeUninit::new(0); 1]; - // Read one u16 character. - let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, 1, &mut self.surrogate)?; - // Read bytes, using the (now-empty) self.incomplete_utf8 as extra space. - let read_bytes = utf16_to_utf8( - unsafe { utf16_buf[..read].assume_init_ref() }, - &mut self.incomplete_utf8.bytes, - )?; - - // Read in the bytes from incomplete_utf8 until the buffer is full. - self.incomplete_utf8.len = read_bytes as u8; - // No-op if no bytes. - bytes_copied += self.incomplete_utf8.read(&mut buf[bytes_copied..]); - Ok(bytes_copied) - } else { - let mut utf16_buf = [MaybeUninit::::uninit(); MAX_BUFFER_SIZE / 2]; - - // In the worst case, a UTF-8 string can take 3 bytes for every `u16` of a 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)?; - // Safety `read_u16s_fixup_surrogates` returns the number of items - // initialized. - let utf16s = unsafe { utf16_buf[..read].assume_init_ref() }; - match utf16_to_utf8(utf16s, buf) { - Ok(value) => return Ok(bytes_copied + value), - Err(e) => return Err(e), - } - } - } -} - -// 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 might not work if we are not the only reader on Stdin. -fn read_u16s_fixup_surrogates( - handle: c::HANDLE, - buf: &mut [MaybeUninit], - 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] = MaybeUninit::new(*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 { - // Safety: The returned `amount` is the number of values initialized, - // and it is not 0, so we know that `buf[amount - 1]` have been - // initialized. - let last_char = unsafe { buf[amount - 1].assume_init() }; - if matches!(last_char, 0xD800..=0xDBFF) { - // high surrogate - *surrogate = last_char; - amount -= 1; - } - } - Ok(amount) -} - -// Returns `Ok(n)` if it initialized `n` values in `buf`. -fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit]) -> 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: u32 = 1 << CTRL_Z; - let input_control = c::CONSOLE_READCONSOLE_CONTROL { - nLength: crate::mem::size_of::() as u32, - nInitialChars: 0, - dwCtrlWakeupMask: CTRL_Z_MASK, - dwControlKeyState: 0, - }; - - let mut amount = 0; - loop { - cvt(unsafe { - c::SetLastError(0); - c::ReadConsoleW( - handle, - buf.as_mut_ptr() as *mut core::ffi::c_void, - buf.len() as u32, - &mut amount, - &input_control, - ) - })?; - - // ReadConsoleW returns success with ERROR_OPERATION_ABORTED for Ctrl-C or Ctrl-Break. - // Explicitly check for that case here and try again. - if amount == 0 && api::get_last_error() == WinError::OPERATION_ABORTED { - continue; - } - break; - } - // Safety: if `amount > 0`, then that many bytes were written, so - // `buf[amount as usize - 1]` has been initialized. - if amount > 0 && unsafe { buf[amount as usize - 1].assume_init() } == CTRL_Z { - amount -= 1; - } - Ok(amount as usize) -} - -fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { - debug_assert!(utf16.len() <= i32::MAX as usize); - debug_assert!(utf8.len() <= i32::MAX as usize); - - if utf16.is_empty() { - return Ok(0); - } - - let result = unsafe { - c::WideCharToMultiByte( - c::CP_UTF8, // CodePage - c::WC_ERR_INVALID_CHARS, // dwFlags - utf16.as_ptr(), // lpWideCharStr - utf16.len() as i32, // cchWideChar - utf8.as_mut_ptr(), // lpMultiByteStr - utf8.len() as i32, // cbMultiByte - ptr::null(), // lpDefaultChar - ptr::null_mut(), // lpUsedDefaultChar - ) - }; - if result == 0 { - // We can't really do any better than forget all data and return an error. - Err(io::const_error!( - io::ErrorKind::InvalidData, - "Windows stdin in console mode does not support non-UTF-16 input; \ - encountered unpaired surrogate", - )) - } else { - Ok(result as usize) - } -} - -impl IncompleteUtf8 { - pub const fn new() -> IncompleteUtf8 { - IncompleteUtf8 { bytes: [0; MAX_LEN_UTF8], len: 0 } - } -} - -impl Stdout { - pub const fn new() -> Stdout { - Stdout { incomplete_utf8: IncompleteUtf8::new() } - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - write(c::STD_OUTPUT_HANDLE, buf, &mut self.incomplete_utf8) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Stderr { - pub const fn new() -> Stderr { - Stderr { incomplete_utf8: IncompleteUtf8::new() } - } -} - -impl io::Write for Stderr { - fn write(&mut self, buf: &[u8]) -> io::Result { - write(c::STD_ERROR_HANDLE, buf, &mut self.incomplete_utf8) - } - - 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/library/std/src/sys/pal/windows/time.rs b/library/std/src/sys/pal/windows/time.rs index d9010e3996109..68126bd8d2fa0 100644 --- a/library/std/src/sys/pal/windows/time.rs +++ b/library/std/src/sys/pal/windows/time.rs @@ -164,7 +164,7 @@ fn intervals2dur(intervals: u64) -> Duration { mod perf_counter { use super::NANOS_PER_SEC; - use crate::sync::atomic::{AtomicU64, Ordering}; + use crate::sync::atomic::{Atomic, AtomicU64, Ordering}; use crate::sys::{c, cvt}; use crate::sys_common::mul_div_u64; use crate::time::Duration; @@ -199,7 +199,7 @@ mod perf_counter { // uninitialized. Storing this as a single `AtomicU64` allows us to use // `Relaxed` operations, as we are only interested in the effects on a // single memory location. - static FREQUENCY: AtomicU64 = AtomicU64::new(0); + static FREQUENCY: Atomic = AtomicU64::new(0); let cached = FREQUENCY.load(Ordering::Relaxed); // If a previous thread has filled in this global state, use that. diff --git a/library/std/src/sys/pal/xous/args.rs b/library/std/src/sys/pal/xous/args.rs deleted file mode 100644 index 00c44ca220a9e..0000000000000 --- a/library/std/src/sys/pal/xous/args.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::ffi::OsString; -use crate::sys::pal::xous::os::get_application_parameters; -use crate::sys::pal::xous::os::params::ArgumentList; -use crate::{fmt, vec}; - -pub struct Args { - parsed_args_list: vec::IntoIter, -} - -pub fn args() -> Args { - let Some(params) = get_application_parameters() else { - return Args { parsed_args_list: vec![].into_iter() }; - }; - - for param in params { - if let Ok(args) = ArgumentList::try_from(¶m) { - let mut parsed_args = vec![]; - for arg in args { - parsed_args.push(arg.into()); - } - return Args { parsed_args_list: parsed_args.into_iter() }; - } - } - Args { parsed_args_list: vec![].into_iter() } -} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.parsed_args_list.as_slice().fmt(f) - } -} - -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/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index 1bd0e67f37162..042c4ff862ff6 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -1,19 +1,17 @@ #![forbid(unsafe_op_in_unsafe_fn)] -pub mod args; -#[path = "../unsupported/env.rs"] -pub mod env; -#[path = "../unsupported/fs.rs"] -pub mod fs; +use crate::os::xous::ffi::exit; + pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/process.rs"] -pub mod process; -pub mod stdio; pub mod thread; pub mod time; #[path = "../unsupported/common.rs"] mod common; pub use common::*; + +pub fn abort_internal() -> ! { + exit(101); +} diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index 2c87e7d91f27d..d612a27d2bdb6 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -1,17 +1,15 @@ use super::unsupported; -use crate::collections::HashMap; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::os::xous::ffi::Error as XousError; use crate::path::{self, PathBuf}; -use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; -use crate::sync::{Mutex, Once}; -use crate::{fmt, io, vec}; +use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; +use crate::{fmt, io}; pub(crate) mod params; -static PARAMS_ADDRESS: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); +static PARAMS_ADDRESS: Atomic<*mut u8> = AtomicPtr::new(core::ptr::null_mut()); #[cfg(not(test))] #[cfg(feature = "panic_unwind")] @@ -64,14 +62,6 @@ mod c_compat { } exit(unsafe { main() }); } - - // 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. - #[unsafe(no_mangle)] - // NB. used by both libunwind and libpanic_abort - pub extern "C" fn __rust_abort() -> ! { - exit(101); - } } pub fn errno() -> i32 { @@ -136,100 +126,6 @@ pub(crate) fn get_application_parameters() -> Option>; - -fn get_env_store() -> &'static EnvStore { - ENV_INIT.call_once(|| { - let env_store = EnvStore::default(); - if let Some(params) = get_application_parameters() { - for param in params { - if let Ok(envs) = params::EnvironmentBlock::try_from(¶m) { - let mut env_store = env_store.lock().unwrap(); - for env in envs { - env_store.insert(env.key.into(), env.value.into()); - } - break; - } - } - } - ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed) - }); - unsafe { &*core::ptr::with_exposed_provenance::(ENV.load(Ordering::Relaxed)) } -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -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 { - let clone_to_vec = |map: &HashMap| -> Vec<_> { - map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() - }; - - let iter = clone_to_vec(&*get_env_store().lock().unwrap()).into_iter(); - Env { iter } -} - -pub fn getenv(k: &OsStr) -> Option { - get_env_store().lock().unwrap().get(k).cloned() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let (k, v) = (k.to_owned(), v.to_owned()); - get_env_store().lock().unwrap().insert(k, v); - Ok(()) -} - -pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - get_env_store().lock().unwrap().remove(k); - Ok(()) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } diff --git a/library/std/src/sys/pal/xous/stdio.rs b/library/std/src/sys/pal/xous/stdio.rs deleted file mode 100644 index dfd47a1775ae2..0000000000000 --- a/library/std/src/sys/pal/xous/stdio.rs +++ /dev/null @@ -1,133 +0,0 @@ -use crate::io; - -pub struct Stdin; -pub struct Stdout {} -pub struct Stderr; - -use crate::os::xous::ffi::{Connection, lend, try_lend, try_scalar}; -use crate::os::xous::services::{LogLend, LogScalar, log_server, try_connect}; - -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 { - #[repr(C, align(4096))] - struct LendBuffer([u8; 4096]); - let mut lend_buffer = LendBuffer([0u8; 4096]); - let connection = log_server(); - for chunk in buf.chunks(lend_buffer.0.len()) { - for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) { - *dest = *src; - } - lend(connection, LogLend::StandardOutput.into(), &lend_buffer.0, 0, chunk.len()) - .unwrap(); - } - 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 { - #[repr(C, align(4096))] - struct LendBuffer([u8; 4096]); - let mut lend_buffer = LendBuffer([0u8; 4096]); - let connection = log_server(); - for chunk in buf.chunks(lend_buffer.0.len()) { - for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) { - *dest = *src; - } - lend(connection, LogLend::StandardError.into(), &lend_buffer.0, 0, chunk.len()) - .unwrap(); - } - 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 -} - -#[derive(Copy, Clone)] -pub struct PanicWriter { - log: Connection, - gfx: Option, -} - -impl io::Write for PanicWriter { - fn write(&mut self, s: &[u8]) -> core::result::Result { - for c in s.chunks(core::mem::size_of::() * 4) { - // Text is grouped into 4x `usize` words. The id is 1100 plus - // the number of characters in this message. - // Ignore errors since we're already panicking. - try_scalar(self.log, LogScalar::AppendPanicMessage(&c).into()).ok(); - } - - // Serialize the text to the graphics panic handler, only if we were able - // to acquire a connection to it. Text length is encoded in the `valid` field, - // the data itself in the buffer. Typically several messages are require to - // fully transmit the entire panic message. - if let Some(gfx) = self.gfx { - #[repr(C, align(4096))] - struct Request([u8; 4096]); - let mut request = Request([0u8; 4096]); - for (&s, d) in s.iter().zip(request.0.iter_mut()) { - *d = s; - } - try_lend(gfx, 0 /* AppendPanicText */, &request.0, 0, s.len()).ok(); - } - Ok(s.len()) - } - - // Tests show that this does not seem to be reliably called at the end of a panic - // print, so, we can't rely on this to e.g. trigger a graphics update. - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub fn panic_output() -> Option { - // Generally this won't fail because every server has already connected, so - // this is likely to succeed. - let log = log_server(); - - // Send the "We're panicking" message (1000). - try_scalar(log, LogScalar::BeginPanic.into()).ok(); - - // This is will fail in the case that the connection table is full, or if the - // graphics server is not running. Most servers do not already have this connection. - let gfx = try_connect("panic-to-screen!"); - - Some(PanicWriter { log, gfx }) -} diff --git a/library/std/src/sys/pal/zkvm/args.rs b/library/std/src/sys/pal/zkvm/args.rs deleted file mode 100644 index 47857f6c448bc..0000000000000 --- a/library/std/src/sys/pal/zkvm/args.rs +++ /dev/null @@ -1,81 +0,0 @@ -use super::{WORD_SIZE, abi}; -use crate::ffi::OsString; -use crate::fmt; -use crate::sys::os_str; -use crate::sys_common::FromInner; - -pub struct Args { - i_forward: usize, - i_back: usize, - count: usize, -} - -pub fn args() -> Args { - let count = unsafe { abi::sys_argc() }; - Args { i_forward: 0, i_back: 0, count } -} - -impl Args { - /// Use sys_argv to get the arg at the requested index. Does not check that i is less than argc - /// and will not return if the index is out of bounds. - fn argv(i: usize) -> OsString { - let arg_len = unsafe { abi::sys_argv(crate::ptr::null_mut(), 0, i) }; - - let arg_len_words = (arg_len + WORD_SIZE - 1) / WORD_SIZE; - let words = unsafe { abi::sys_alloc_words(arg_len_words) }; - - let arg_len2 = unsafe { abi::sys_argv(words, arg_len_words, i) }; - debug_assert_eq!(arg_len, arg_len2); - - // Convert to OsString. - // - // FIXME: We can probably get rid of the extra copy here if we - // reimplement "os_str" instead of just using the generic unix - // "os_str". - let arg_bytes: &[u8] = - unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, arg_len) }; - OsString::from_inner(os_str::Buf { inner: arg_bytes.to_vec() }) - } -} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().finish() - } -} - -impl Iterator for Args { - type Item = OsString; - - fn next(&mut self) -> Option { - if self.i_forward >= self.count - self.i_back { - None - } else { - let arg = Self::argv(self.i_forward); - self.i_forward += 1; - Some(arg) - } - } - - fn size_hint(&self) -> (usize, Option) { - (self.count, Some(self.count)) - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.count - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - if self.i_back >= self.count - self.i_forward { - None - } else { - let arg = Self::argv(self.count - 1 - self.i_back); - self.i_back += 1; - Some(arg) - } - } -} diff --git a/library/std/src/sys/pal/zkvm/env.rs b/library/std/src/sys/pal/zkvm/env.rs deleted file mode 100644 index b85153642b1c9..0000000000000 --- a/library/std/src/sys/pal/zkvm/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = ""; - pub const OS: &str = ""; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ".elf"; - pub const DLL_EXTENSION: &str = "elf"; - pub const EXE_SUFFIX: &str = ".elf"; - pub const EXE_EXTENSION: &str = "elf"; -} diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 054c867f90d8e..e1efa2406858f 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -8,20 +8,12 @@ //! will likely change over time. #![forbid(unsafe_op_in_unsafe_fn)] -const WORD_SIZE: usize = core::mem::size_of::(); +pub const WORD_SIZE: usize = size_of::(); pub mod abi; -#[path = "../zkvm/args.rs"] -pub mod args; -pub mod env; -#[path = "../unsupported/fs.rs"] -pub mod fs; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/process.rs"] -pub mod process; -pub mod stdio; #[path = "../unsupported/thread.rs"] pub mod thread; #[path = "../unsupported/time.rs"] diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index 868b19e33b672..a8ef97ecf67ac 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -1,10 +1,8 @@ -use super::{WORD_SIZE, abi, unsupported}; +use super::unsupported; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; -use crate::sys::os_str; -use crate::sys_common::FromInner; use crate::{fmt, io}; pub fn errno() -> i32 { @@ -64,64 +62,6 @@ pub fn current_exe() -> io::Result { unsupported() } -pub struct Env(!); - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.0 - } -} - -pub fn env() -> Env { - panic!("not supported on this platform") -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -pub fn getenv(varname: &OsStr) -> Option { - let varname = varname.as_encoded_bytes(); - let nbytes = - unsafe { abi::sys_getenv(crate::ptr::null_mut(), 0, varname.as_ptr(), varname.len()) }; - if nbytes == usize::MAX { - return None; - } - - let nwords = (nbytes + WORD_SIZE - 1) / WORD_SIZE; - let words = unsafe { abi::sys_alloc_words(nwords) }; - - let nbytes2 = unsafe { abi::sys_getenv(words, nwords, varname.as_ptr(), varname.len()) }; - debug_assert_eq!(nbytes, nbytes2); - - // Convert to OsString. - // - // FIXME: We can probably get rid of the extra copy here if we - // reimplement "os_str" instead of just using the generic unix - // "os_str". - let u8s: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, nbytes) }; - Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() })) -} - -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) -} - -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } diff --git a/library/std/src/sys/pal/zkvm/stdio.rs b/library/std/src/sys/pal/zkvm/stdio.rs deleted file mode 100644 index 0bcb54744b0b6..0000000000000 --- a/library/std/src/sys/pal/zkvm/stdio.rs +++ /dev/null @@ -1,73 +0,0 @@ -use super::abi; -use super::abi::fileno; -use crate::io::{self, BorrowedCursor}; - -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(unsafe { abi::sys_read(fileno::STDIN, buf.as_mut_ptr(), buf.len()) }) - } - - fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { - unsafe { - let n = abi::sys_read(fileno::STDIN, buf.as_mut().as_mut_ptr().cast(), buf.capacity()); - buf.advance_unchecked(n); - } - Ok(()) - } -} - -impl Stdout { - pub const fn new() -> Stdout { - Stdout - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - unsafe { abi::sys_write(fileno::STDOUT, buf.as_ptr(), buf.len()) } - - 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 { - unsafe { abi::sys_write(fileno::STDERR, buf.as_ptr(), buf.len()) } - - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; - -pub fn is_ebadf(_err: &io::Error) -> bool { - true -} - -pub fn panic_output() -> Option { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/path/unix.rs b/library/std/src/sys/path/unix.rs index 361e99964f18c..faa2616a6320d 100644 --- a/library/std/src/sys/path/unix.rs +++ b/library/std/src/sys/path/unix.rs @@ -62,10 +62,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result { } pub(crate) fn is_absolute(path: &Path) -> bool { - if cfg!(target_os = "redox") { - // FIXME: Allow Redox prefixes - path.has_root() || crate::path::has_redox_scheme(path.as_u8_slice()) - } else if cfg!(any(unix, target_os = "hermit", target_os = "wasi")) { + if cfg!(any(unix, target_os = "hermit", target_os = "wasi")) { path.has_root() } else { path.has_root() && path.prefix().is_some() diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs index 1c53472191699..e0e003f6a8192 100644 --- a/library/std/src/sys/path/windows.rs +++ b/library/std/src/sys/path/windows.rs @@ -10,6 +10,40 @@ mod tests; pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; +/// A null terminated wide string. +#[repr(transparent)] +pub struct WCStr([u16]); + +impl WCStr { + /// Convert a slice to a WCStr without checks. + /// + /// Though it is memory safe, the slice should also not contain interior nulls + /// as this may lead to unwanted truncation. + /// + /// # Safety + /// + /// The slice must end in a null. + pub unsafe fn from_wchars_with_null_unchecked(s: &[u16]) -> &Self { + unsafe { &*(s as *const [u16] as *const Self) } + } + + pub fn as_ptr(&self) -> *const u16 { + self.0.as_ptr() + } + + pub fn count_bytes(&self) -> usize { + self.0.len() + } +} + +#[inline] +pub fn with_native_path(path: &Path, f: &dyn Fn(&WCStr) -> io::Result) -> io::Result { + let path = maybe_verbatim(path)?; + // SAFETY: maybe_verbatim returns null-terminated strings + let path = unsafe { WCStr::from_wchars_with_null_unchecked(&path) }; + f(path) +} + #[inline] pub fn is_sep_byte(b: u8) -> bool { b == b'/' || b == b'\\' @@ -350,3 +384,46 @@ pub(crate) fn absolute(path: &Path) -> io::Result { pub(crate) fn is_absolute(path: &Path) -> bool { path.has_root() && path.prefix().is_some() } + +/// Test that the path is absolute, fully qualified and unchanged when processed by the Windows API. +/// +/// For example: +/// +/// - `C:\path\to\file` will return true. +/// - `C:\path\to\nul` returns false because the Windows API will convert it to \\.\NUL +/// - `C:\path\to\..\file` returns false because it will be resolved to `C:\path\file`. +/// +/// This is a useful property because it means the path can be converted from and to and verbatim +/// path just by changing the prefix. +pub(crate) fn is_absolute_exact(path: &[u16]) -> bool { + // This is implemented by checking that passing the path through + // GetFullPathNameW does not change the path in any way. + + // Windows paths are limited to i16::MAX length + // though the API here accepts a u32 for the length. + if path.is_empty() || path.len() > u32::MAX as usize || path.last() != Some(&0) { + return false; + } + // The path returned by `GetFullPathNameW` must be the same length as the + // given path, otherwise they're not equal. + let buffer_len = path.len(); + let mut new_path = Vec::with_capacity(buffer_len); + let result = unsafe { + c::GetFullPathNameW( + path.as_ptr(), + new_path.capacity() as u32, + new_path.as_mut_ptr(), + crate::ptr::null_mut(), + ) + }; + // Note: if non-zero, the returned result is the length of the buffer without the null termination + if result == 0 || result as usize != buffer_len - 1 { + false + } else { + // SAFETY: `GetFullPathNameW` initialized `result` bytes and does not exceed `nBufferLength - 1` (capacity). + unsafe { + new_path.set_len((result as usize) + 1); + } + path == &new_path + } +} diff --git a/library/std/src/sys/path/windows/tests.rs b/library/std/src/sys/path/windows/tests.rs index f2a60e30bc610..9eb79203dcac7 100644 --- a/library/std/src/sys/path/windows/tests.rs +++ b/library/std/src/sys/path/windows/tests.rs @@ -135,3 +135,15 @@ fn broken_unc_path() { assert_eq!(components.next(), Some(Component::Normal("foo".as_ref()))); assert_eq!(components.next(), Some(Component::Normal("bar".as_ref()))); } + +#[test] +fn test_is_absolute_exact() { + use crate::sys::pal::api::wide_str; + // These paths can be made verbatim by only changing their prefix. + assert!(is_absolute_exact(wide_str!(r"C:\path\to\file"))); + assert!(is_absolute_exact(wide_str!(r"\\server\share\path\to\file"))); + // These paths change more substantially + assert!(!is_absolute_exact(wide_str!(r"C:\path\to\..\file"))); + assert!(!is_absolute_exact(wide_str!(r"\\server\share\path\to\..\file"))); + assert!(!is_absolute_exact(wide_str!(r"C:\path\to\NUL"))); // Converts to \\.\NUL +} diff --git a/library/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs index 778d8686f023e..ef5112ad74f13 100644 --- a/library/std/src/sys/personality/dwarf/eh.rs +++ b/library/std/src/sys/personality/dwarf/eh.rs @@ -12,7 +12,7 @@ #![allow(non_upper_case_globals)] #![allow(unused)] -use core::{mem, ptr}; +use core::ptr; use super::DwarfReader; @@ -245,8 +245,7 @@ unsafe fn read_encoded_pointer( DW_EH_PE_datarel => (*context.get_data_start)(), // aligned means the value is aligned to the size of a pointer DW_EH_PE_aligned => { - reader.ptr = - reader.ptr.with_addr(round_up(reader.ptr.addr(), mem::size_of::<*const u8>())?); + reader.ptr = reader.ptr.with_addr(round_up(reader.ptr.addr(), size_of::<*const u8>())?); core::ptr::null() } _ => return Err(()), diff --git a/library/std/src/sys/personality/dwarf/mod.rs b/library/std/src/sys/personality/dwarf/mod.rs index 5c52d96c4cad4..2bc91951b49fd 100644 --- a/library/std/src/sys/personality/dwarf/mod.rs +++ b/library/std/src/sys/personality/dwarf/mod.rs @@ -12,8 +12,6 @@ mod tests; pub mod eh; -use core::mem; - pub struct DwarfReader { pub ptr: *const u8, } @@ -29,7 +27,7 @@ impl DwarfReader { pub unsafe fn read(&mut self) -> T { unsafe { let result = self.ptr.cast::().read_unaligned(); - self.ptr = self.ptr.byte_add(mem::size_of::()); + self.ptr = self.ptr.byte_add(size_of::()); result } } diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index cd2c7899f4bf1..b012e47f9aa24 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -220,7 +220,7 @@ cfg_if::cfg_if! { Ok(action) => action, Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, }; - if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { + if actions & uw::_UA_SEARCH_PHASE != 0 { match eh_action { EHAction::None | EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND, EHAction::Catch(_) | EHAction::Filter(_) => uw::_URC_HANDLER_FOUND, @@ -230,7 +230,7 @@ cfg_if::cfg_if! { match eh_action { EHAction::None => uw::_URC_CONTINUE_UNWIND, // Forced unwinding hits a terminate action. - EHAction::Filter(_) if actions as i32 & uw::_UA_FORCE_UNWIND as i32 != 0 => uw::_URC_CONTINUE_UNWIND, + EHAction::Filter(_) if actions & uw::_UA_FORCE_UNWIND != 0 => uw::_URC_CONTINUE_UNWIND, EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { uw::_Unwind_SetGR( context, @@ -248,7 +248,10 @@ cfg_if::cfg_if! { } cfg_if::cfg_if! { - if #[cfg(all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), target_env = "gnu"))] { + if #[cfg(any( + all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), target_env = "gnu"), + target_os = "cygwin", + ))] { /// personality fn called by [Windows Structured Exception Handling][windows-eh] /// /// On x86_64 and AArch64 MinGW targets, the unwinding mechanism is SEH, diff --git a/library/std/src/sys/process/env.rs b/library/std/src/sys/process/env.rs new file mode 100644 index 0000000000000..e08b476540ef9 --- /dev/null +++ b/library/std/src/sys/process/env.rs @@ -0,0 +1,115 @@ +use crate::collections::BTreeMap; +use crate::ffi::{OsStr, OsString}; +use crate::sys::process::EnvKey; +use crate::{env, fmt}; + +/// Stores a set of changes to an environment +#[derive(Clone, Default)] +pub struct CommandEnv { + clear: bool, + saw_path: bool, + vars: BTreeMap>, +} + +impl fmt::Debug for CommandEnv { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut debug_command_env = f.debug_struct("CommandEnv"); + debug_command_env.field("clear", &self.clear).field("vars", &self.vars); + debug_command_env.finish() + } +} + +impl CommandEnv { + // Capture the current environment with these changes applied + pub fn capture(&self) -> BTreeMap { + let mut result = BTreeMap::::new(); + if !self.clear { + for (k, v) in env::vars_os() { + result.insert(k.into(), v); + } + } + for (k, maybe_v) in &self.vars { + if let &Some(ref v) = maybe_v { + result.insert(k.clone(), v.clone()); + } else { + result.remove(k); + } + } + result + } + + pub fn is_unchanged(&self) -> bool { + !self.clear && self.vars.is_empty() + } + + pub fn capture_if_changed(&self) -> Option> { + if self.is_unchanged() { None } else { Some(self.capture()) } + } + + // The following functions build up changes + pub fn set(&mut self, key: &OsStr, value: &OsStr) { + let key = EnvKey::from(key); + self.maybe_saw_path(&key); + self.vars.insert(key, Some(value.to_owned())); + } + + pub fn remove(&mut self, key: &OsStr) { + let key = EnvKey::from(key); + self.maybe_saw_path(&key); + if self.clear { + self.vars.remove(&key); + } else { + self.vars.insert(key, None); + } + } + + pub fn clear(&mut self) { + self.clear = true; + self.vars.clear(); + } + + pub fn does_clear(&self) -> bool { + self.clear + } + + pub fn have_changed_path(&self) -> bool { + self.saw_path || self.clear + } + + fn maybe_saw_path(&mut self, key: &EnvKey) { + if !self.saw_path && key == "PATH" { + self.saw_path = true; + } + } + + pub fn iter(&self) -> CommandEnvs<'_> { + let iter = self.vars.iter(); + CommandEnvs { iter } + } +} + +#[derive(Debug)] +pub struct CommandEnvs<'a> { + iter: crate::collections::btree_map::Iter<'a, EnvKey, Option>, +} + +impl<'a> Iterator for CommandEnvs<'a> { + type Item = (&'a OsStr, Option<&'a OsStr>); + + fn next(&mut self) -> Option { + self.iter.next().map(|(key, value)| (key.as_ref(), value.as_deref())) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CommandEnvs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} diff --git a/library/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs new file mode 100644 index 0000000000000..91c7005a32855 --- /dev/null +++ b/library/std/src/sys/process/mod.rs @@ -0,0 +1,78 @@ +cfg_if::cfg_if! { + if #[cfg(target_family = "unix")] { + mod unix; + use unix as imp; + } else if #[cfg(target_os = "windows")] { + mod windows; + use windows as imp; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + use uefi as imp; + } else { + mod unsupported; + use unsupported as imp; + } +} + +// This module is shared by all platforms, but nearly all platforms except for +// the "normal" UNIX ones leave some of this code unused. +#[cfg_attr(not(target_os = "linux"), allow(dead_code))] +mod env; + +pub use env::CommandEnvs; +pub use imp::{ + Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio, StdioPipes, +}; + +#[cfg(any( + all( + target_family = "unix", + not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + )) + ), + target_os = "windows", +))] +pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec)> { + use crate::sys::pipe::read2; + + let (mut process, mut pipes) = cmd.spawn(Stdio::MakePipe, false)?; + + drop(pipes.stdin.take()); + let (mut stdout, mut stderr) = (Vec::new(), Vec::new()); + match (pipes.stdout.take(), pipes.stderr.take()) { + (None, None) => {} + (Some(out), None) => { + let res = out.read_to_end(&mut stdout); + res.unwrap(); + } + (None, Some(err)) => { + let res = err.read_to_end(&mut stderr); + res.unwrap(); + } + (Some(out), Some(err)) => { + let res = read2(out, &mut stdout, err, &mut stderr); + res.unwrap(); + } + } + + let status = process.wait()?; + Ok((status, stdout, stderr)) +} + +#[cfg(not(any( + all( + target_family = "unix", + not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + )) + ), + target_os = "windows", +)))] +pub use imp::output; diff --git a/library/std/src/sys/process/uefi.rs b/library/std/src/sys/process/uefi.rs new file mode 100644 index 0000000000000..4864c58698817 --- /dev/null +++ b/library/std/src/sys/process/uefi.rs @@ -0,0 +1,895 @@ +use r_efi::protocols::{simple_text_input, simple_text_output}; + +use super::env::{CommandEnv, CommandEnvs}; +use crate::collections::BTreeMap; +pub use crate::ffi::OsString as EnvKey; +use crate::ffi::{OsStr, OsString}; +use crate::num::{NonZero, NonZeroI32}; +use crate::path::Path; +use crate::sys::fs::File; +use crate::sys::pal::helpers; +use crate::sys::pal::os::error_string; +use crate::sys::pipe::AnonPipe; +use crate::sys::unsupported; +use crate::{fmt, io}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +#[derive(Debug)] +pub struct Command { + prog: OsString, + args: Vec, + stdout: Option, + stderr: Option, + stdin: Option, + 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, +} + +#[derive(Copy, Clone, Debug)] +pub enum Stdio { + Inherit, + Null, + MakePipe, +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { + prog: program.to_os_string(), + args: Vec::new(), + stdout: None, + stderr: None, + stdin: None, + env: Default::default(), + } + } + + 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) { + panic!("unsupported") + } + + 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 get_program(&self) -> &OsStr { + self.prog.as_ref() + } + + pub fn get_args(&self) -> CommandArgs<'_> { + CommandArgs { iter: self.args.iter() } + } + + pub fn get_envs(&self) -> CommandEnvs<'_> { + self.env.iter() + } + + pub fn get_current_dir(&self) -> Option<&Path> { + None + } + + pub fn spawn( + &mut self, + _default: Stdio, + _needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + unsupported() + } + + fn create_pipe( + s: Stdio, + ) -> io::Result>> { + match s { + Stdio::MakePipe => unsafe { + helpers::OwnedProtocol::create( + uefi_command_internal::PipeProtocol::new(), + simple_text_output::PROTOCOL_GUID, + ) + } + .map(Some), + Stdio::Null => unsafe { + helpers::OwnedProtocol::create( + uefi_command_internal::PipeProtocol::null(), + simple_text_output::PROTOCOL_GUID, + ) + } + .map(Some), + Stdio::Inherit => Ok(None), + } + } + + fn create_stdin( + s: Stdio, + ) -> io::Result>> { + match s { + Stdio::Null => unsafe { + helpers::OwnedProtocol::create( + uefi_command_internal::InputProtocol::null(), + simple_text_input::PROTOCOL_GUID, + ) + } + .map(Some), + Stdio::Inherit => Ok(None), + Stdio::MakePipe => unsupported(), + } + } +} + +pub fn output(command: &mut Command) -> io::Result<(ExitStatus, Vec, Vec)> { + let mut cmd = uefi_command_internal::Image::load_image(&command.prog)?; + + // UEFI adds the bin name by default + if !command.args.is_empty() { + let args = uefi_command_internal::create_args(&command.prog, &command.args); + cmd.set_args(args); + } + + // Setup Stdout + let stdout = command.stdout.unwrap_or(Stdio::MakePipe); + let stdout = Command::create_pipe(stdout)?; + if let Some(con) = stdout { + cmd.stdout_init(con) + } else { + cmd.stdout_inherit() + }; + + // Setup Stderr + let stderr = command.stderr.unwrap_or(Stdio::MakePipe); + let stderr = Command::create_pipe(stderr)?; + if let Some(con) = stderr { + cmd.stderr_init(con) + } else { + cmd.stderr_inherit() + }; + + // Setup Stdin + let stdin = command.stdin.unwrap_or(Stdio::Null); + let stdin = Command::create_stdin(stdin)?; + if let Some(con) = stdin { + cmd.stdin_init(con) + } else { + cmd.stdin_inherit() + }; + + let env = env_changes(&command.env); + + // Set any new vars + if let Some(e) = &env { + for (k, (_, v)) in e { + match v { + Some(v) => unsafe { crate::env::set_var(k, v) }, + None => unsafe { crate::env::remove_var(k) }, + } + } + } + + let stat = cmd.start_image()?; + + // Rollback any env changes + if let Some(e) = env { + for (k, (v, _)) in e { + match v { + Some(v) => unsafe { crate::env::set_var(k, v) }, + None => unsafe { crate::env::remove_var(k) }, + } + } + } + + let stdout = cmd.stdout()?; + let stderr = cmd.stderr()?; + + Ok((ExitStatus(stat), stdout, stderr)) +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl From for Stdio { + fn from(_file: File) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[non_exhaustive] +pub struct ExitStatus(r_efi::efi::Status); + +impl ExitStatus { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + if self.0 == r_efi::efi::Status::SUCCESS { Ok(()) } else { Err(ExitStatusError(self.0)) } + } + + pub fn code(&self) -> Option { + Some(self.0.as_usize() as i32) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let err_str = error_string(self.0.as_usize()); + write!(f, "{}", err_str) + } +} + +impl Default for ExitStatus { + fn default() -> Self { + ExitStatus(r_efi::efi::Status::SUCCESS) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct ExitStatusError(r_efi::efi::Status); + +impl fmt::Debug for ExitStatusError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let err_str = error_string(self.0.as_usize()); + write!(f, "{}", err_str) + } +} + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option> { + NonZeroI32::new(self.0.as_usize() as i32) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(bool); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(false); + pub const FAILURE: ExitCode = ExitCode(true); + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +impl From for ExitCode { + fn from(code: u8) -> Self { + match code { + 0 => Self::SUCCESS, + 1..=255 => Self::FAILURE, + } + } +} + +pub struct Process(!); + +impl Process { + pub fn id(&self) -> u32 { + self.0 + } + + pub fn kill(&mut self) -> io::Result<()> { + self.0 + } + + pub fn wait(&mut self) -> io::Result { + self.0 + } + + pub fn try_wait(&mut self) -> io::Result> { + self.0 + } +} + +pub struct CommandArgs<'a> { + iter: crate::slice::Iter<'a, OsString>, +} + +impl<'a> Iterator for CommandArgs<'a> { + type Item = &'a OsStr; + + fn next(&mut self) -> Option<&'a OsStr> { + self.iter.next().map(|x| x.as_ref()) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +impl<'a> fmt::Debug for CommandArgs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.clone()).finish() + } +} + +#[allow(dead_code)] +mod uefi_command_internal { + use r_efi::protocols::{loaded_image, simple_text_input, simple_text_output}; + + use crate::ffi::{OsStr, OsString}; + use crate::io::{self, const_error}; + use crate::mem::MaybeUninit; + use crate::os::uefi::env::{boot_services, image_handle, system_table}; + use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; + use crate::ptr::NonNull; + use crate::slice; + use crate::sys::pal::helpers::{self, OwnedTable}; + use crate::sys_common::wstr::WStrUnits; + + pub struct Image { + handle: NonNull, + stdout: Option>, + stderr: Option>, + stdin: Option>, + st: OwnedTable, + args: Option<(*mut u16, usize)>, + } + + impl Image { + pub fn load_image(p: &OsStr) -> io::Result { + let path = helpers::OwnedDevicePath::from_text(p)?; + let boot_services: NonNull = boot_services() + .ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))? + .cast(); + let mut child_handle: MaybeUninit = MaybeUninit::uninit(); + let image_handle = image_handle(); + + let r = unsafe { + ((*boot_services.as_ptr()).load_image)( + r_efi::efi::Boolean::FALSE, + image_handle.as_ptr(), + path.as_ptr(), + crate::ptr::null_mut(), + 0, + child_handle.as_mut_ptr(), + ) + }; + + if r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + let child_handle = unsafe { child_handle.assume_init() }; + let child_handle = NonNull::new(child_handle).unwrap(); + + let loaded_image: NonNull = + helpers::open_protocol(child_handle, loaded_image::PROTOCOL_GUID).unwrap(); + let st = OwnedTable::from_table(unsafe { (*loaded_image.as_ptr()).system_table }); + + Ok(Self { + handle: child_handle, + stdout: None, + stderr: None, + stdin: None, + st, + args: None, + }) + } + } + + pub(crate) fn start_image(&mut self) -> io::Result { + self.update_st_crc32()?; + + // Use our system table instead of the default one + let loaded_image: NonNull = + helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); + unsafe { + (*loaded_image.as_ptr()).system_table = self.st.as_mut_ptr(); + } + + let boot_services: NonNull = boot_services() + .ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))? + .cast(); + let mut exit_data_size: usize = 0; + let mut exit_data: MaybeUninit<*mut u16> = MaybeUninit::uninit(); + + let r = unsafe { + ((*boot_services.as_ptr()).start_image)( + self.handle.as_ptr(), + &mut exit_data_size, + exit_data.as_mut_ptr(), + ) + }; + + // Drop exitdata + if exit_data_size != 0 { + unsafe { + let exit_data = exit_data.assume_init(); + ((*boot_services.as_ptr()).free_pool)(exit_data as *mut crate::ffi::c_void); + } + } + + Ok(r) + } + + fn set_stdout( + &mut self, + handle: r_efi::efi::Handle, + protocol: *mut simple_text_output::Protocol, + ) { + unsafe { + (*self.st.as_mut_ptr()).console_out_handle = handle; + (*self.st.as_mut_ptr()).con_out = protocol; + } + } + + fn set_stderr( + &mut self, + handle: r_efi::efi::Handle, + protocol: *mut simple_text_output::Protocol, + ) { + unsafe { + (*self.st.as_mut_ptr()).standard_error_handle = handle; + (*self.st.as_mut_ptr()).std_err = protocol; + } + } + + fn set_stdin( + &mut self, + handle: r_efi::efi::Handle, + protocol: *mut simple_text_input::Protocol, + ) { + unsafe { + (*self.st.as_mut_ptr()).console_in_handle = handle; + (*self.st.as_mut_ptr()).con_in = protocol; + } + } + + pub fn stdout_init(&mut self, protocol: helpers::OwnedProtocol) { + self.set_stdout( + protocol.handle().as_ptr(), + protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol, + ); + self.stdout = Some(protocol); + } + + pub fn stdout_inherit(&mut self) { + let st: NonNull = system_table().cast(); + unsafe { self.set_stdout((*st.as_ptr()).console_out_handle, (*st.as_ptr()).con_out) } + } + + pub fn stderr_init(&mut self, protocol: helpers::OwnedProtocol) { + self.set_stderr( + protocol.handle().as_ptr(), + protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol, + ); + self.stderr = Some(protocol); + } + + pub fn stderr_inherit(&mut self) { + let st: NonNull = system_table().cast(); + unsafe { self.set_stderr((*st.as_ptr()).standard_error_handle, (*st.as_ptr()).std_err) } + } + + pub(crate) fn stdin_init(&mut self, protocol: helpers::OwnedProtocol) { + self.set_stdin( + protocol.handle().as_ptr(), + protocol.as_ref() as *const InputProtocol as *mut simple_text_input::Protocol, + ); + self.stdin = Some(protocol); + } + + pub(crate) fn stdin_inherit(&mut self) { + let st: NonNull = system_table().cast(); + unsafe { self.set_stdin((*st.as_ptr()).console_in_handle, (*st.as_ptr()).con_in) } + } + + pub fn stderr(&self) -> io::Result> { + match &self.stderr { + Some(stderr) => stderr.as_ref().utf8(), + None => Ok(Vec::new()), + } + } + + pub fn stdout(&self) -> io::Result> { + match &self.stdout { + Some(stdout) => stdout.as_ref().utf8(), + None => Ok(Vec::new()), + } + } + + pub fn set_args(&mut self, args: Box<[u16]>) { + let loaded_image: NonNull = + helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); + + let len = args.len(); + let args_size: u32 = (len * size_of::()).try_into().unwrap(); + let ptr = Box::into_raw(args).as_mut_ptr(); + + unsafe { + (*loaded_image.as_ptr()).load_options = ptr as *mut crate::ffi::c_void; + (*loaded_image.as_ptr()).load_options_size = args_size; + } + + self.args = Some((ptr, len)); + } + + fn update_st_crc32(&mut self) -> io::Result<()> { + let bt: NonNull = boot_services().unwrap().cast(); + let st_size = unsafe { (*self.st.as_ptr()).hdr.header_size as usize }; + let mut crc32: u32 = 0; + + // Set crc to 0 before calculation + unsafe { + (*self.st.as_mut_ptr()).hdr.crc32 = 0; + } + + let r = unsafe { + ((*bt.as_ptr()).calculate_crc32)( + self.st.as_mut_ptr() as *mut crate::ffi::c_void, + st_size, + &mut crc32, + ) + }; + + if r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + unsafe { + (*self.st.as_mut_ptr()).hdr.crc32 = crc32; + } + Ok(()) + } + } + } + + impl Drop for Image { + fn drop(&mut self) { + if let Some(bt) = boot_services() { + let bt: NonNull = bt.cast(); + unsafe { + ((*bt.as_ptr()).unload_image)(self.handle.as_ptr()); + } + } + + if let Some((ptr, len)) = self.args { + let _ = unsafe { Box::from_raw(crate::ptr::slice_from_raw_parts_mut(ptr, len)) }; + } + } + } + + #[repr(C)] + pub struct PipeProtocol { + reset: simple_text_output::ProtocolReset, + output_string: simple_text_output::ProtocolOutputString, + test_string: simple_text_output::ProtocolTestString, + query_mode: simple_text_output::ProtocolQueryMode, + set_mode: simple_text_output::ProtocolSetMode, + set_attribute: simple_text_output::ProtocolSetAttribute, + clear_screen: simple_text_output::ProtocolClearScreen, + set_cursor_position: simple_text_output::ProtocolSetCursorPosition, + enable_cursor: simple_text_output::ProtocolEnableCursor, + mode: *mut simple_text_output::Mode, + _buffer: Vec, + } + + impl PipeProtocol { + pub fn new() -> Self { + let mode = Box::new(simple_text_output::Mode { + max_mode: 0, + mode: 0, + attribute: 0, + cursor_column: 0, + cursor_row: 0, + cursor_visible: r_efi::efi::Boolean::FALSE, + }); + Self { + reset: Self::reset, + output_string: Self::output_string, + test_string: Self::test_string, + query_mode: Self::query_mode, + set_mode: Self::set_mode, + set_attribute: Self::set_attribute, + clear_screen: Self::clear_screen, + set_cursor_position: Self::set_cursor_position, + enable_cursor: Self::enable_cursor, + mode: Box::into_raw(mode), + _buffer: Vec::new(), + } + } + + pub fn null() -> Self { + let mode = Box::new(simple_text_output::Mode { + max_mode: 0, + mode: 0, + attribute: 0, + cursor_column: 0, + cursor_row: 0, + cursor_visible: r_efi::efi::Boolean::FALSE, + }); + Self { + reset: Self::reset_null, + output_string: Self::output_string_null, + test_string: Self::test_string, + query_mode: Self::query_mode, + set_mode: Self::set_mode, + set_attribute: Self::set_attribute, + clear_screen: Self::clear_screen, + set_cursor_position: Self::set_cursor_position, + enable_cursor: Self::enable_cursor, + mode: Box::into_raw(mode), + _buffer: Vec::new(), + } + } + + pub fn utf8(&self) -> io::Result> { + OsString::from_wide(&self._buffer) + .into_string() + .map(Into::into) + .map_err(|_| const_error!(io::ErrorKind::Other, "UTF-8 conversion failed")) + } + + extern "efiapi" fn reset( + proto: *mut simple_text_output::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + let proto: *mut PipeProtocol = proto.cast(); + unsafe { + (*proto)._buffer.clear(); + } + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn reset_null( + _: *mut simple_text_output::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn output_string( + proto: *mut simple_text_output::Protocol, + buf: *mut r_efi::efi::Char16, + ) -> r_efi::efi::Status { + let proto: *mut PipeProtocol = proto.cast(); + let buf_len = unsafe { + if let Some(x) = WStrUnits::new(buf) { + x.count() + } else { + return r_efi::efi::Status::INVALID_PARAMETER; + } + }; + let buf_slice = unsafe { slice::from_raw_parts(buf, buf_len) }; + + unsafe { + (*proto)._buffer.extend_from_slice(buf_slice); + }; + + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn output_string_null( + _: *mut simple_text_output::Protocol, + _: *mut r_efi::efi::Char16, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn test_string( + _: *mut simple_text_output::Protocol, + _: *mut r_efi::efi::Char16, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn query_mode( + _: *mut simple_text_output::Protocol, + _: usize, + _: *mut usize, + _: *mut usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn set_mode( + _: *mut simple_text_output::Protocol, + _: usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn set_attribute( + _: *mut simple_text_output::Protocol, + _: usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn clear_screen( + _: *mut simple_text_output::Protocol, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn set_cursor_position( + _: *mut simple_text_output::Protocol, + _: usize, + _: usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn enable_cursor( + _: *mut simple_text_output::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + } + + impl Drop for PipeProtocol { + fn drop(&mut self) { + unsafe { + let _ = Box::from_raw(self.mode); + } + } + } + + #[repr(C)] + pub(crate) struct InputProtocol { + reset: simple_text_input::ProtocolReset, + read_key_stroke: simple_text_input::ProtocolReadKeyStroke, + wait_for_key: r_efi::efi::Event, + } + + impl InputProtocol { + pub(crate) fn null() -> Self { + let evt = helpers::OwnedEvent::new( + r_efi::efi::EVT_NOTIFY_WAIT, + r_efi::efi::TPL_CALLBACK, + Some(Self::empty_notify), + None, + ) + .unwrap(); + + Self { + reset: Self::null_reset, + read_key_stroke: Self::null_read_key, + wait_for_key: evt.into_raw(), + } + } + + extern "efiapi" fn null_reset( + _: *mut simple_text_input::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn null_read_key( + _: *mut simple_text_input::Protocol, + _: *mut simple_text_input::InputKey, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn empty_notify(_: r_efi::efi::Event, _: *mut crate::ffi::c_void) {} + } + + impl Drop for InputProtocol { + fn drop(&mut self) { + // Close wait_for_key + unsafe { + let _ = helpers::OwnedEvent::from_raw(self.wait_for_key); + } + } + } + + pub fn create_args(prog: &OsStr, args: &[OsString]) -> Box<[u16]> { + const QUOTE: u16 = 0x0022; + const SPACE: u16 = 0x0020; + const CARET: u16 = 0x005e; + const NULL: u16 = 0; + + // This is the lower bound on the final length under the assumption that + // the arguments only contain ASCII characters. + let mut res = Vec::with_capacity(args.iter().map(|arg| arg.len() + 3).sum()); + + // Wrap program name in quotes to avoid any problems + res.push(QUOTE); + res.extend(prog.encode_wide()); + res.push(QUOTE); + + for arg in args { + res.push(SPACE); + + // Wrap the argument in quotes to be treat as single arg + res.push(QUOTE); + for c in arg.encode_wide() { + // CARET in quotes is used to escape CARET or QUOTE + if c == QUOTE || c == CARET { + res.push(CARET); + } + res.push(c); + } + res.push(QUOTE); + } + + res.into_boxed_slice() + } +} + +/// Create a map of environment variable changes. Allows efficient setting and rolling back of +/// environment variable changes. +/// +/// Entry: (Old Value, New Value) +fn env_changes(env: &CommandEnv) -> Option, Option)>> { + if env.is_unchanged() { + return None; + } + + let mut result = BTreeMap::, Option)>::new(); + + // Check if we want to clear all prior variables + if env.does_clear() { + for (k, v) in crate::env::vars_os() { + result.insert(k.into(), (Some(v), None)); + } + } + + for (k, v) in env.iter() { + let v: Option = v.map(Into::into); + result + .entry(k.into()) + .and_modify(|cur| *cur = (cur.0.clone(), v.clone())) + .or_insert((crate::env::var_os(k), v)); + } + + Some(result) +} diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs new file mode 100644 index 0000000000000..e205a8390052f --- /dev/null +++ b/library/std/src/sys/process/unix/common.rs @@ -0,0 +1,687 @@ +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests; + +use libc::{EXIT_FAILURE, EXIT_SUCCESS, c_char, c_int, gid_t, pid_t, uid_t}; + +use crate::collections::BTreeMap; +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::os::unix::prelude::*; +use crate::path::Path; +use crate::sys::fd::FileDesc; +use crate::sys::fs::File; +#[cfg(not(target_os = "fuchsia"))] +use crate::sys::fs::OpenOptions; +use crate::sys::pipe::{self, AnonPipe}; +use crate::sys::process::env::{CommandEnv, CommandEnvs}; +use crate::sys_common::{FromInner, IntoInner}; +use crate::{fmt, io, ptr}; + +cfg_if::cfg_if! { + if #[cfg(target_os = "fuchsia")] { + // fuchsia doesn't have /dev/null + } else if #[cfg(target_os = "vxworks")] { + const DEV_NULL: &CStr = c"/null"; + } else { + const DEV_NULL: &CStr = c"/dev/null"; + } +} + +// 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://github.com/aosp-mirror/platform_bionic/blob/ad8dcd6023294b646e5a8288c0ed431b0845da49/libc/include/android/legacy_signal_inlines.h +cfg_if::cfg_if! { + if #[cfg(target_os = "android")] { + #[allow(dead_code)] + 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; + use libc::{c_ulong, sigset_t}; + + // The implementations from bionic (android libc) type pun `sigset_t` as an + // array of `c_ulong`. This works, but lets add a smoke check to make sure + // that doesn't change. + const _: () = assert!( + align_of::() == align_of::() + && (size_of::() % size_of::()) == 0 + ); + + let bit = (signum - 1) as usize; + if set.is_null() || bit >= (8 * size_of::()) { + crate::sys::pal::os::set_errno(libc::EINVAL); + return -1; + } + let raw = slice::from_raw_parts_mut( + set as *mut c_ulong, + size_of::() / size_of::(), + ); + const LONG_BIT: usize = size_of::() * 8; + raw[bit / LONG_BIT] |= 1 << (bit % LONG_BIT); + return 0; + } + } else { + #[allow(unused_imports)] + pub use libc::{sigemptyset, sigaddset}; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + program: CString, + args: Vec, + /// Exactly what will be passed to `execvp`. + /// + /// First element is a pointer to `program`, followed by pointers to + /// `args`, followed by a `null`. Be careful when modifying `program` or + /// `args` to properly update this as well. + argv: Argv, + env: CommandEnv, + + program_kind: ProgramKind, + cwd: Option, + chroot: Option, + uid: Option, + gid: Option, + saw_nul: bool, + closures: Vec io::Result<()> + Send + Sync>>, + groups: Option>, + stdin: Option, + stdout: Option, + stderr: Option, + #[cfg(target_os = "linux")] + create_pidfd: bool, + pgroup: 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 +#[cfg_attr(target_os = "vita", allow(dead_code))] +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, +} + +#[derive(Debug)] +pub enum Stdio { + Inherit, + Null, + MakePipe, + Fd(FileDesc), + StaticFd(BorrowedFd<'static>), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ProgramKind { + /// A program that would be looked up on the PATH (e.g. `ls`) + PathLookup, + /// A relative path (e.g. `my-dir/foo`, `../foo`, `./foo`) + Relative, + /// An absolute path. + Absolute, +} + +impl ProgramKind { + fn new(program: &OsStr) -> Self { + if program.as_encoded_bytes().starts_with(b"/") { + Self::Absolute + } else if program.as_encoded_bytes().contains(&b'/') { + // If the program has more than one component in it, it is a relative path. + Self::Relative + } else { + Self::PathLookup + } + } +} + +impl Command { + #[cfg(not(target_os = "linux"))] + pub fn new(program: &OsStr) -> Command { + let mut saw_nul = false; + let program_kind = ProgramKind::new(program.as_ref()); + let program = os2c(program, &mut saw_nul); + Command { + argv: Argv(vec![program.as_ptr(), ptr::null()]), + args: vec![program.clone()], + program, + program_kind, + env: Default::default(), + cwd: None, + chroot: None, + uid: None, + gid: None, + saw_nul, + closures: Vec::new(), + groups: None, + stdin: None, + stdout: None, + stderr: None, + pgroup: None, + } + } + + #[cfg(target_os = "linux")] + pub fn new(program: &OsStr) -> Command { + let mut saw_nul = false; + let program_kind = ProgramKind::new(program.as_ref()); + let program = os2c(program, &mut saw_nul); + Command { + argv: Argv(vec![program.as_ptr(), ptr::null()]), + args: vec![program.clone()], + program, + program_kind, + env: Default::default(), + cwd: None, + chroot: None, + uid: None, + gid: None, + saw_nul, + closures: Vec::new(), + groups: None, + stdin: None, + stdout: None, + stderr: None, + create_pidfd: false, + pgroup: 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 groups(&mut self, groups: &[gid_t]) { + self.groups = Some(Box::from(groups)); + } + pub fn pgroup(&mut self, pgroup: pid_t) { + self.pgroup = Some(pgroup); + } + pub fn chroot(&mut self, dir: &Path) { + self.chroot = Some(os2c(dir.as_os_str(), &mut self.saw_nul)); + if self.cwd.is_none() { + self.cwd(&OsStr::new("/")); + } + } + + #[cfg(target_os = "linux")] + pub fn create_pidfd(&mut self, val: bool) { + self.create_pidfd = val; + } + + #[cfg(not(target_os = "linux"))] + #[allow(dead_code)] + pub fn get_create_pidfd(&self) -> bool { + false + } + + #[cfg(target_os = "linux")] + pub fn get_create_pidfd(&self) -> bool { + self.create_pidfd + } + + pub fn saw_nul(&self) -> bool { + self.saw_nul + } + + pub fn get_program(&self) -> &OsStr { + OsStr::from_bytes(self.program.as_bytes()) + } + + #[allow(dead_code)] + pub fn get_program_kind(&self) -> ProgramKind { + self.program_kind + } + + pub fn get_args(&self) -> CommandArgs<'_> { + let mut iter = self.args.iter(); + iter.next(); + CommandArgs { iter } + } + + pub fn get_envs(&self) -> CommandEnvs<'_> { + self.env.iter() + } + + pub fn get_current_dir(&self) -> Option<&Path> { + self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes()))) + } + + pub fn get_argv(&self) -> &Vec<*const c_char> { + &self.argv.0 + } + + pub fn get_program_cstr(&self) -> &CStr { + &*self.program + } + + #[allow(dead_code)] + pub fn get_cwd(&self) -> Option<&CStr> { + self.cwd.as_deref() + } + #[allow(dead_code)] + pub fn get_uid(&self) -> Option { + self.uid + } + #[allow(dead_code)] + pub fn get_gid(&self) -> Option { + self.gid + } + #[allow(dead_code)] + pub fn get_groups(&self) -> Option<&[gid_t]> { + self.groups.as_deref() + } + #[allow(dead_code)] + pub fn get_pgroup(&self) -> Option { + self.pgroup + } + #[allow(dead_code)] + pub fn get_chroot(&self) -> Option<&CStr> { + self.chroot.as_deref() + } + + 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() + } + + #[allow(dead_code)] + pub fn program_is_path(&self) -> bool { + self.program.to_bytes().contains(&b'/') + } + + 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; + c"".to_owned() + }) +} + +// 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.as_raw_fd() >= 0 && fd.as_raw_fd() <= libc::STDERR_FILENO { + Ok((ChildStdio::Owned(fd.duplicate()?), None)) + } else { + Ok((ChildStdio::Explicit(fd.as_raw_fd()), None)) + } + } + + Stdio::StaticFd(fd) => { + let fd = FileDesc::from_inner(fd.try_clone_to_owned()?); + Ok((ChildStdio::Owned(fd), None)) + } + + Stdio::MakePipe => { + let (reader, writer) = pipe::anon_pipe()?; + let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) }; + Ok((ChildStdio::Owned(theirs.into_inner()), Some(ours))) + } + + #[cfg(not(target_os = "fuchsia"))] + Stdio::Null => { + let mut opts = OpenOptions::new(); + opts.read(readable); + opts.write(!readable); + let fd = File::open_c(DEV_NULL, &opts)?; + Ok((ChildStdio::Owned(fd.into_inner()), None)) + } + + #[cfg(target_os = "fuchsia")] + Stdio::Null => Ok((ChildStdio::Null, None)), + } + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + Stdio::Fd(pipe.into_inner()) + } +} + +impl From for Stdio { + fn from(fd: FileDesc) -> Stdio { + Stdio::Fd(fd) + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + Stdio::Fd(file.into_inner()) + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + // This ought really to be is Stdio::StaticFd(input_argument.as_fd()). + // But AsFd::as_fd takes its argument by reference, and yields + // a bounded lifetime, so it's no use here. There is no AsStaticFd. + // + // Additionally AsFd is only implemented for the *locked* versions. + // We don't want to lock them here. (The implications of not locking + // are the same as those for process::Stdio::inherit().) + // + // Arguably the hypothetical AsStaticFd and AsFd<'static> + // should be implemented for io::Stdout, not just for StdoutLocked. + Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) }) + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) }) + } +} + +impl ChildStdio { + pub fn fd(&self) -> Option { + match *self { + ChildStdio::Inherit => None, + ChildStdio::Explicit(fd) => Some(fd), + ChildStdio::Owned(ref fd) => Some(fd.as_raw_fd()), + + #[cfg(target_os = "fuchsia")] + ChildStdio::Null => None, + } + } +} + +impl fmt::Debug for Command { + // show all attributes but `self.closures` which does not implement `Debug` + // and `self.argv` which is not useful for debugging + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + let mut debug_command = f.debug_struct("Command"); + debug_command.field("program", &self.program).field("args", &self.args); + if !self.env.is_unchanged() { + debug_command.field("env", &self.env); + } + + if self.cwd.is_some() { + debug_command.field("cwd", &self.cwd); + } + if self.uid.is_some() { + debug_command.field("uid", &self.uid); + } + if self.gid.is_some() { + debug_command.field("gid", &self.gid); + } + + if self.groups.is_some() { + debug_command.field("groups", &self.groups); + } + + if self.stdin.is_some() { + debug_command.field("stdin", &self.stdin); + } + if self.stdout.is_some() { + debug_command.field("stdout", &self.stdout); + } + if self.stderr.is_some() { + debug_command.field("stderr", &self.stderr); + } + if self.pgroup.is_some() { + debug_command.field("pgroup", &self.pgroup); + } + + #[cfg(target_os = "linux")] + { + debug_command.field("create_pidfd", &self.create_pidfd); + } + + debug_command.finish() + } else { + if let Some(ref cwd) = self.cwd { + write!(f, "cd {cwd:?} && ")?; + } + if self.env.does_clear() { + write!(f, "env -i ")?; + // Altered env vars will be printed next, that should exactly work as expected. + } else { + // Removed env vars need the command to be wrapped in `env`. + let mut any_removed = false; + for (key, value_opt) in self.get_envs() { + if value_opt.is_none() { + if !any_removed { + write!(f, "env ")?; + any_removed = true; + } + write!(f, "-u {} ", key.to_string_lossy())?; + } + } + } + // Altered env vars can just be added in front of the program. + for (key, value_opt) in self.get_envs() { + if let Some(value) = value_opt { + write!(f, "{}={value:?} ", key.to_string_lossy())?; + } + } + 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)] +pub struct ExitCode(u8); + +impl fmt::Debug for ExitCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("unix_exit_status").field(&self.0).finish() + } +} + +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 + } +} + +impl From for ExitCode { + fn from(code: u8) -> Self { + Self(code) + } +} + +pub struct CommandArgs<'a> { + iter: crate::slice::Iter<'a, CString>, +} + +impl<'a> Iterator for CommandArgs<'a> { + type Item = &'a OsStr; + fn next(&mut self) -> Option<&'a OsStr> { + self.iter.next().map(|cs| OsStr::from_bytes(cs.as_bytes())) + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +impl<'a> fmt::Debug for CommandArgs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.clone()).finish() + } +} diff --git a/library/std/src/sys/pal/unix/process/process_common/tests.rs b/library/std/src/sys/process/unix/common/tests.rs similarity index 100% rename from library/std/src/sys/pal/unix/process/process_common/tests.rs rename to library/std/src/sys/process/unix/common/tests.rs diff --git a/library/std/src/sys/process/unix/fuchsia.rs b/library/std/src/sys/process/unix/fuchsia.rs new file mode 100644 index 0000000000000..017ab91797ce6 --- /dev/null +++ b/library/std/src/sys/process/unix/fuchsia.rs @@ -0,0 +1,313 @@ +use libc::{c_int, size_t}; + +use super::common::*; +use crate::num::NonZero; +use crate::sys::pal::fuchsia::*; +use crate::{fmt, io, mem, ptr}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +impl Command { + pub fn spawn( + &mut self, + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + let envp = self.capture_env(); + + if self.saw_nul() { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + "nul byte found in provided data", + )); + } + + let (ours, theirs) = self.setup_io(default, needs_stdin)?; + + let process_handle = unsafe { self.do_exec(theirs, envp.as_ref())? }; + + Ok((Process { handle: Handle::new(process_handle) }, ours)) + } + + pub fn exec(&mut self, default: Stdio) -> io::Error { + if self.saw_nul() { + return io::const_error!( + io::ErrorKind::InvalidInput, + "nul byte found in provided data", + ); + } + + match self.setup_io(default, true) { + Ok((_, _)) => { + // FIXME: This is tough because we don't support the exec syscalls + unimplemented!(); + } + Err(e) => e, + } + } + + unsafe fn do_exec( + &mut self, + stdio: ChildPipes, + maybe_envp: Option<&CStringArray>, + ) -> io::Result { + let envp = match maybe_envp { + // None means to clone the current environment, which is done in the + // flags below. + None => ptr::null(), + Some(envp) => envp.as_ptr(), + }; + + let make_action = |local_io: &ChildStdio, target_fd| -> io::Result { + if let Some(local_fd) = local_io.fd() { + Ok(fdio_spawn_action_t { + action: FDIO_SPAWN_ACTION_TRANSFER_FD, + local_fd, + target_fd, + ..Default::default() + }) + } else { + if let ChildStdio::Null = local_io { + // acts as no-op + return Ok(Default::default()); + } + + let mut handle = ZX_HANDLE_INVALID; + let status = fdio_fd_clone(target_fd, &mut handle); + if status == ZX_ERR_INVALID_ARGS || status == ZX_ERR_NOT_SUPPORTED { + // This descriptor is closed; skip it rather than generating an + // error. + return Ok(Default::default()); + } + zx_cvt(status)?; + + let mut cloned_fd = 0; + zx_cvt(fdio_fd_create(handle, &mut cloned_fd))?; + + Ok(fdio_spawn_action_t { + action: FDIO_SPAWN_ACTION_TRANSFER_FD, + local_fd: cloned_fd as i32, + target_fd, + ..Default::default() + }) + } + }; + + // Clone stdin, stdout, and stderr + let action1 = make_action(&stdio.stdin, 0)?; + let action2 = make_action(&stdio.stdout, 1)?; + let action3 = make_action(&stdio.stderr, 2)?; + let actions = [action1, action2, action3]; + + // We don't want FileDesc::drop to be called on any stdio. fdio_spawn_etc + // always consumes transferred file descriptors. + mem::forget(stdio); + + for callback in self.get_closures().iter_mut() { + callback()?; + } + + let mut process_handle: zx_handle_t = 0; + zx_cvt(fdio_spawn_etc( + ZX_HANDLE_INVALID, + FDIO_SPAWN_CLONE_JOB + | FDIO_SPAWN_CLONE_LDSVC + | FDIO_SPAWN_CLONE_NAMESPACE + | FDIO_SPAWN_CLONE_ENVIRON // this is ignored when envp is non-null + | FDIO_SPAWN_CLONE_UTC_CLOCK, + self.get_program_cstr().as_ptr(), + self.get_argv().as_ptr(), + envp, + actions.len() as size_t, + actions.as_ptr(), + &mut process_handle, + ptr::null_mut(), + ))?; + // FIXME: See if we want to do something with that err_msg + + Ok(process_handle) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +pub struct Process { + handle: Handle, +} + +impl Process { + pub fn id(&self) -> u32 { + self.handle.raw() as u32 + } + + pub fn kill(&mut self) -> io::Result<()> { + unsafe { + zx_cvt(zx_task_kill(self.handle.raw()))?; + } + + Ok(()) + } + + pub fn wait(&mut self) -> io::Result { + let mut proc_info: zx_info_process_t = Default::default(); + let mut actual: size_t = 0; + let mut avail: size_t = 0; + + unsafe { + zx_cvt(zx_object_wait_one( + self.handle.raw(), + ZX_TASK_TERMINATED, + ZX_TIME_INFINITE, + ptr::null_mut(), + ))?; + zx_cvt(zx_object_get_info( + self.handle.raw(), + ZX_INFO_PROCESS, + (&raw mut proc_info) as *mut libc::c_void, + size_of::(), + &mut actual, + &mut avail, + ))?; + } + if actual != 1 { + return Err(io::const_error!( + io::ErrorKind::InvalidData, + "failed to get exit status of process", + )); + } + Ok(ExitStatus(proc_info.return_code)) + } + + pub fn try_wait(&mut self) -> io::Result> { + let mut proc_info: zx_info_process_t = Default::default(); + let mut actual: size_t = 0; + let mut avail: size_t = 0; + + unsafe { + let status = + zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED, 0, ptr::null_mut()); + match status { + 0 => {} // Success + x if x == ZX_ERR_TIMED_OUT => { + return Ok(None); + } + _ => { + panic!("Failed to wait on process handle: {status}"); + } + } + zx_cvt(zx_object_get_info( + self.handle.raw(), + ZX_INFO_PROCESS, + (&raw mut proc_info) as *mut libc::c_void, + size_of::(), + &mut actual, + &mut avail, + ))?; + } + if actual != 1 { + return Err(io::const_error!( + io::ErrorKind::InvalidData, + "failed to get exit status of process", + )); + } + Ok(Some(ExitStatus(proc_info.return_code))) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] +pub struct ExitStatus(i64); + +impl ExitStatus { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + match NonZero::try_from(self.0) { + /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), + /* was zero, couldn't convert */ Err(_) => Ok(()), + } + } + + pub fn code(&self) -> Option { + // FIXME: support extracting return code as an i64 + self.0.try_into().ok() + } + + pub fn signal(&self) -> Option { + None + } + + // FIXME: The actually-Unix implementation in unix.rs uses WSTOPSIG, WCOREDUMP et al. + // I infer from the implementation of `success`, `code` and `signal` above that these are not + // available on Fuchsia. + // + // It does not appear that Fuchsia is Unix-like enough to implement ExitStatus (or indeed many + // other things from std::os::unix) properly. This veneer is always going to be a bodge. So + // while I don't know if these implementations are actually correct, I think they will do for + // now at least. + pub fn core_dumped(&self) -> bool { + false + } + pub fn stopped_signal(&self) -> Option { + None + } + pub fn continued(&self) -> bool { + false + } + + pub fn into_raw(&self) -> c_int { + // We don't know what someone who calls into_raw() will do with this value, but it should + // have the conventional Unix representation. Despite the fact that this is not + // standardised in SuS or POSIX, all Unix systems encode the signal and exit status the + // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behavior on every + // Unix.) + // + // The caller of `std::os::unix::into_raw` is probably wanting a Unix exit status, and may + // do their own shifting and masking, or even pass the status to another computer running a + // different Unix variant. + // + // The other view would be to say that the caller on Fuchsia ought to know that `into_raw` + // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is + // not possible here because we must return a c_int because that's what Unix (including + // SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't + // necessarily fit. + // + // It seems to me that the right answer would be to provide std::os::fuchsia with its + // own ExitStatusExt, rather that trying to provide a not very convincing imitation of + // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But + // fixing this up that is beyond the scope of my efforts now. + let exit_status_as_if_unix: u8 = self.0.try_into().expect("Fuchsia process return code bigger than 8 bits, but std::os::unix::ExitStatusExt::into_raw() was called to try to convert the value into a traditional Unix-style wait status, which cannot represent values greater than 255."); + let wait_status_as_if_unix = (exit_status_as_if_unix as c_int) << 8; + wait_status_as_if_unix + } +} + +/// 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 as i64) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "exit code: {}", self.0) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatusError(NonZero); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option> { + // fixme: affected by the same bug as ExitStatus::code() + ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) + } +} diff --git a/library/std/src/sys/process/unix/mod.rs b/library/std/src/sys/process/unix/mod.rs new file mode 100644 index 0000000000000..ee8fd8b2ca3c6 --- /dev/null +++ b/library/std/src/sys/process/unix/mod.rs @@ -0,0 +1,24 @@ +#[cfg_attr(any(target_os = "espidf", target_os = "horizon", target_os = "nuttx"), allow(unused))] +mod common; + +cfg_if::cfg_if! { + if #[cfg(target_os = "fuchsia")] { + mod fuchsia; + use fuchsia as imp; + } else if #[cfg(target_os = "vxworks")] { + mod vxworks; + use vxworks as imp; + } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] { + mod unsupported; + use unsupported as imp; + pub use unsupported::output; + } else { + mod unix; + use unix as imp; + } +} + +pub use imp::{ExitStatus, ExitStatusError, Process}; + +pub use self::common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes}; +pub use crate::ffi::OsString as EnvKey; diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs new file mode 100644 index 0000000000000..4f595ac9a1c5f --- /dev/null +++ b/library/std/src/sys/process/unix/unix.rs @@ -0,0 +1,1276 @@ +#[cfg(target_os = "vxworks")] +use libc::RTP_ID as pid_t; +#[cfg(not(target_os = "vxworks"))] +use libc::{c_int, pid_t}; +#[cfg(not(any( + target_os = "vxworks", + target_os = "l4re", + target_os = "tvos", + target_os = "watchos", +)))] +use libc::{gid_t, uid_t}; + +use super::common::*; +use crate::io::{self, Error, ErrorKind}; +use crate::num::NonZero; +use crate::sys::cvt; +#[cfg(target_os = "linux")] +use crate::sys::pal::linux::pidfd::PidFd; +use crate::{fmt, mem, sys}; + +cfg_if::cfg_if! { + if #[cfg(target_os = "nto")] { + use crate::thread; + use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; + use crate::time::Duration; + use crate::sync::LazyLock; + // Get smallest amount of time we can sleep. + // Return a common value if it cannot be determined. + fn get_clock_resolution() -> Duration { + static MIN_DELAY: LazyLock Duration> = LazyLock::new(|| { + let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0 + { + Duration::from_nanos(mindelay.tv_nsec as u64) + } else { + Duration::from_millis(1) + } + }); + *MIN_DELAY + } + // Arbitrary minimum sleep duration for retrying fork/spawn + const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1); + // Maximum duration of sleeping before giving up and returning an error + const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +impl Command { + pub fn spawn( + &mut self, + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX"; + + let envp = self.capture_env(); + + if self.saw_nul() { + return Err(io::const_error!( + ErrorKind::InvalidInput, + "nul byte found in provided data", + )); + } + + let (ours, theirs) = self.setup_io(default, needs_stdin)?; + + if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? { + return Ok((ret, ours)); + } + + #[cfg(target_os = "linux")] + let (input, output) = sys::net::Socket::new_pair(libc::AF_UNIX, libc::SOCK_SEQPACKET)?; + + #[cfg(not(target_os = "linux"))] + let (input, output) = sys::pipe::anon_pipe()?; + + // Whatever happens after the fork is almost for sure going to touch or + // look at the environment in one way or another (PATH in `execvp` or + // accessing the `environ` pointer ourselves). Make sure no other thread + // is accessing the environment when we do the fork itself. + // + // Note that as soon as we're done with the fork there's no need to hold + // a lock any more because the parent won't do anything and the child is + // in its own process. Thus the parent drops the lock guard immediately. + // The child calls `mem::forget` to leak the lock, which is crucial because + // releasing a lock is not async-signal-safe. + let env_lock = sys::env::env_read_lock(); + let pid = unsafe { self.do_fork()? }; + + if pid == 0 { + crate::panic::always_abort(); + mem::forget(env_lock); // avoid non-async-signal-safe unlocking + drop(input); + #[cfg(target_os = "linux")] + if self.get_create_pidfd() { + self.send_pidfd(&output); + } + let Err(err) = unsafe { self.do_exec(theirs, envp.as_ref()) }; + let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; + let errno = errno.to_be_bytes(); + let bytes = [ + errno[0], + errno[1], + errno[2], + errno[3], + CLOEXEC_MSG_FOOTER[0], + CLOEXEC_MSG_FOOTER[1], + CLOEXEC_MSG_FOOTER[2], + CLOEXEC_MSG_FOOTER[3], + ]; + // pipe I/O up to PIPE_BUF bytes should be atomic, and then + // we want to be sure we *don't* run at_exit destructors as + // we're being torn down regardless + rtassert!(output.write(&bytes).is_ok()); + unsafe { libc::_exit(1) } + } + + drop(env_lock); + drop(output); + + #[cfg(target_os = "linux")] + let pidfd = if self.get_create_pidfd() { self.recv_pidfd(&input) } else { -1 }; + + #[cfg(not(target_os = "linux"))] + let pidfd = -1; + + // Safety: We obtained the pidfd (on Linux) using SOCK_SEQPACKET, so it's valid. + let mut p = unsafe { Process::new(pid, pidfd) }; + let mut bytes = [0; 8]; + + // loop to handle EINTR + loop { + match input.read(&mut bytes) { + Ok(0) => return Ok((p, ours)), + Ok(8) => { + let (errno, footer) = bytes.split_at(4); + assert_eq!( + CLOEXEC_MSG_FOOTER, footer, + "Validation on the CLOEXEC pipe failed: {:?}", + bytes + ); + let errno = i32::from_be_bytes(errno.try_into().unwrap()); + assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); + return Err(Error::from_raw_os_error(errno)); + } + Err(ref e) if e.is_interrupted() => {} + Err(e) => { + assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); + panic!("the CLOEXEC pipe failed: {e:?}") + } + Ok(..) => { + // pipe I/O up to PIPE_BUF bytes should be atomic + // similarly SOCK_SEQPACKET messages should arrive whole + assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); + panic!("short read on the CLOEXEC pipe") + } + } + } + } + + // WatchOS and TVOS headers mark the `fork`/`exec*` functions with + // `__WATCHOS_PROHIBITED __TVOS_PROHIBITED`, and indicate that the + // `posix_spawn*` functions should be used instead. It isn't entirely clear + // what `PROHIBITED` means here (e.g. if calls to these functions are + // allowed to exist in dead code), but it sounds bad, so we go out of our + // way to avoid that all-together. + #[cfg(any(target_os = "tvos", target_os = "watchos"))] + const ERR_APPLE_TV_WATCH_NO_FORK_EXEC: Error = io::const_error!( + ErrorKind::Unsupported, + "`fork`+`exec`-based process spawning is not supported on this target", + ); + + #[cfg(any(target_os = "tvos", target_os = "watchos"))] + unsafe fn do_fork(&mut self) -> Result { + return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); + } + + // Attempts to fork the process. If successful, returns Ok((0, -1)) + // in the child, and Ok((child_pid, -1)) in the parent. + #[cfg(not(any(target_os = "watchos", target_os = "tvos", target_os = "nto")))] + unsafe fn do_fork(&mut self) -> Result { + cvt(libc::fork()) + } + + // On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened + // or closed a file descriptor while the fork() was occurring". + // Documentation says "... or try calling fork() again". This is what we do here. + // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html + #[cfg(target_os = "nto")] + unsafe fn do_fork(&mut self) -> Result { + use crate::sys::os::errno; + + let mut delay = MIN_FORKSPAWN_SLEEP; + + loop { + let r = libc::fork(); + if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF { + if delay < get_clock_resolution() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else if delay < MAX_FORKSPAWN_SLEEP { + thread::sleep(delay); + } else { + return Err(io::const_error!( + ErrorKind::WouldBlock, + "forking returned EBADF too often", + )); + } + delay *= 2; + continue; + } else { + return cvt(r); + } + } + } + + pub fn exec(&mut self, default: Stdio) -> io::Error { + let envp = self.capture_env(); + + if self.saw_nul() { + return io::const_error!(ErrorKind::InvalidInput, "nul byte found in provided data"); + } + + match self.setup_io(default, true) { + Ok((_, theirs)) => { + unsafe { + // Similar to when forking, we want to ensure that access to + // the environment is synchronized, so make sure to grab the + // environment lock before we try to exec. + let _lock = sys::env::env_read_lock(); + + let Err(e) = self.do_exec(theirs, envp.as_ref()); + e + } + } + Err(e) => e, + } + } + + // And at this point we've reached a special time in the life of the + // child. The child must now be considered hamstrung and unable to + // do anything other than syscalls really. Consider the following + // scenario: + // + // 1. Thread A of process 1 grabs the malloc() mutex + // 2. Thread B of process 1 forks(), creating thread C + // 3. Thread C of process 2 then attempts to malloc() + // 4. The memory of process 2 is the same as the memory of + // process 1, so the mutex is locked. + // + // This situation looks a lot like deadlock, right? It turns out + // that this is what pthread_atfork() takes care of, which is + // presumably implemented across platforms. The first thing that + // threads to *before* forking is to do things like grab the malloc + // mutex, and then after the fork they unlock it. + // + // Despite this information, libnative's spawn has been witnessed to + // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but + // all collected backtraces point at malloc/free traffic in the + // child spawned process. + // + // For this reason, the block of code below should contain 0 + // invocations of either malloc of free (or their related friends). + // + // As an example of not having malloc/free traffic, we don't close + // this file descriptor by dropping the FileDesc (which contains an + // allocation). Instead we just close it manually. This will never + // have the drop glue anyway because this code never returns (the + // child will either exec() or invoke libc::exit) + #[cfg(not(any(target_os = "tvos", target_os = "watchos")))] + unsafe fn do_exec( + &mut self, + stdio: ChildPipes, + maybe_envp: Option<&CStringArray>, + ) -> Result { + use crate::sys::{self, cvt_r}; + + if let Some(fd) = stdio.stdin.fd() { + cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?; + } + if let Some(fd) = stdio.stdout.fd() { + cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?; + } + if let Some(fd) = stdio.stderr.fd() { + cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?; + } + + #[cfg(not(target_os = "l4re"))] + { + if let Some(_g) = self.get_groups() { + //FIXME: Redox kernel does not support setgroups yet + #[cfg(not(target_os = "redox"))] + cvt(libc::setgroups(_g.len().try_into().unwrap(), _g.as_ptr()))?; + } + if let Some(u) = self.get_gid() { + cvt(libc::setgid(u as gid_t))?; + } + if let Some(u) = self.get_uid() { + // When dropping privileges from root, the `setgroups` call + // will remove any extraneous groups. We only drop groups + // if we have CAP_SETGID and we weren't given an explicit + // set of groups. If we don't call this, then even though our + // uid has dropped, we may still have groups that enable us to + // do super-user things. + //FIXME: Redox kernel does not support setgroups yet + #[cfg(not(target_os = "redox"))] + if self.get_groups().is_none() { + let res = cvt(libc::setgroups(0, crate::ptr::null())); + if let Err(e) = res { + // Here we ignore the case of not having CAP_SETGID. + // An alternative would be to require CAP_SETGID (in + // addition to CAP_SETUID) for setting the UID. + if e.raw_os_error() != Some(libc::EPERM) { + return Err(e.into()); + } + } + } + cvt(libc::setuid(u as uid_t))?; + } + } + if let Some(chroot) = self.get_chroot() { + #[cfg(not(target_os = "fuchsia"))] + cvt(libc::chroot(chroot.as_ptr()))?; + #[cfg(target_os = "fuchsia")] + return Err(io::const_error!( + io::ErrorKind::Unsupported, + "chroot not supported by fuchsia" + )); + } + if let Some(cwd) = self.get_cwd() { + cvt(libc::chdir(cwd.as_ptr()))?; + } + + if let Some(pgroup) = self.get_pgroup() { + cvt(libc::setpgid(0, pgroup))?; + } + + // emscripten has no signal support. + #[cfg(not(target_os = "emscripten"))] + { + // Inherit the signal mask from the parent rather than resetting it (i.e. do not call + // pthread_sigmask). + + // If -Zon-broken-pipe is used, don't reset SIGPIPE to SIG_DFL. + // If -Zon-broken-pipe is not used, reset SIGPIPE to SIG_DFL for backward compatibility. + // + // -Zon-broken-pipe is an opportunity to change the default here. + if !crate::sys::pal::on_broken_pipe_flag_used() { + #[cfg(target_os = "android")] // see issue #88585 + { + let mut action: libc::sigaction = mem::zeroed(); + action.sa_sigaction = libc::SIG_DFL; + cvt(libc::sigaction(libc::SIGPIPE, &action, crate::ptr::null_mut()))?; + } + #[cfg(not(target_os = "android"))] + { + let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL); + if ret == libc::SIG_ERR { + return Err(io::Error::last_os_error()); + } + } + #[cfg(target_os = "hurd")] + { + let ret = sys::signal(libc::SIGLOST, libc::SIG_DFL); + if ret == libc::SIG_ERR { + return Err(io::Error::last_os_error()); + } + } + } + } + + for callback in self.get_closures().iter_mut() { + callback()?; + } + + // Although we're performing an exec here we may also return with an + // error from this function (without actually exec'ing) in which case we + // want to be sure to restore the global environment back to what it + // once was, ensuring that our temporary override, when free'd, doesn't + // corrupt our process's environment. + let mut _reset = None; + if let Some(envp) = maybe_envp { + struct Reset(*const *const libc::c_char); + + impl Drop for Reset { + fn drop(&mut self) { + unsafe { + *sys::env::environ() = self.0; + } + } + } + + _reset = Some(Reset(*sys::env::environ())); + *sys::env::environ() = envp.as_ptr(); + } + + libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr()); + Err(io::Error::last_os_error()) + } + + #[cfg(any(target_os = "tvos", target_os = "watchos"))] + unsafe fn do_exec( + &mut self, + _stdio: ChildPipes, + _maybe_envp: Option<&CStringArray>, + ) -> Result { + return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); + } + + #[cfg(not(any( + target_os = "freebsd", + target_os = "illumos", + all(target_os = "linux", target_env = "gnu"), + all(target_os = "linux", target_env = "musl"), + target_os = "nto", + target_vendor = "apple", + target_os = "cygwin", + )))] + fn posix_spawn( + &mut self, + _: &ChildPipes, + _: Option<&CStringArray>, + ) -> io::Result> { + Ok(None) + } + + // Only support platforms for which posix_spawn() can return ENOENT + // directly. + #[cfg(any( + target_os = "freebsd", + target_os = "illumos", + all(target_os = "linux", target_env = "gnu"), + all(target_os = "linux", target_env = "musl"), + target_os = "nto", + target_vendor = "apple", + target_os = "cygwin", + ))] + fn posix_spawn( + &mut self, + stdio: &ChildPipes, + envp: Option<&CStringArray>, + ) -> io::Result> { + #[cfg(target_os = "linux")] + use core::sync::atomic::{Atomic, AtomicU8, Ordering}; + + use crate::mem::MaybeUninit; + use crate::sys::{self, cvt_nz, on_broken_pipe_flag_used}; + + if self.get_gid().is_some() + || self.get_uid().is_some() + || (self.env_saw_path() && !self.program_is_path()) + || !self.get_closures().is_empty() + || self.get_groups().is_some() + || self.get_chroot().is_some() + { + return Ok(None); + } + + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + use crate::sys::weak::weak; + + weak!( + fn pidfd_spawnp( + pidfd: *mut libc::c_int, + path: *const libc::c_char, + file_actions: *const libc::posix_spawn_file_actions_t, + attrp: *const libc::posix_spawnattr_t, + argv: *const *mut libc::c_char, + envp: *const *mut libc::c_char, + ) -> libc::c_int; + ); + + weak!( + fn pidfd_getpid(pidfd: libc::c_int) -> libc::c_int; + ); + + static PIDFD_SUPPORTED: Atomic = AtomicU8::new(0); + const UNKNOWN: u8 = 0; + const SPAWN: u8 = 1; + // Obtaining a pidfd via the fork+exec path might work + const FORK_EXEC: u8 = 2; + // Neither pidfd_spawn nor fork/exec will get us a pidfd. + // Instead we'll just posix_spawn if the other preconditions are met. + const NO: u8 = 3; + + if self.get_create_pidfd() { + let mut support = PIDFD_SUPPORTED.load(Ordering::Relaxed); + if support == FORK_EXEC { + return Ok(None); + } + if support == UNKNOWN { + support = NO; + let our_pid = crate::process::id(); + let pidfd = cvt(unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) } as c_int); + match pidfd { + Ok(pidfd) => { + support = FORK_EXEC; + if let Some(Ok(pid)) = pidfd_getpid.get().map(|f| cvt(unsafe { f(pidfd) } as i32)) { + if pidfd_spawnp.get().is_some() && pid as u32 == our_pid { + support = SPAWN + } + } + unsafe { libc::close(pidfd) }; + } + Err(e) if e.raw_os_error() == Some(libc::EMFILE) => { + // We're temporarily(?) out of file descriptors. In this case obtaining a pidfd would also fail + // Don't update the support flag so we can probe again later. + return Err(e) + } + _ => {} + } + PIDFD_SUPPORTED.store(support, Ordering::Relaxed); + if support == FORK_EXEC { + return Ok(None); + } + } + core::assert_matches::debug_assert_matches!(support, SPAWN | NO); + } + } else { + if self.get_create_pidfd() { + unreachable!("only implemented on linux") + } + } + } + + // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly. + #[cfg(all(target_os = "linux", target_env = "gnu"))] + { + if let Some(version) = sys::os::glibc_version() { + if version < (2, 24) { + return Ok(None); + } + } else { + return Ok(None); + } + } + + // On QNX Neutrino, posix_spawnp can fail with EBADF in case "another thread might have opened + // or closed a file descriptor while the posix_spawn() was occurring". + // Documentation says "... or try calling posix_spawn() again". This is what we do here. + // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html + #[cfg(target_os = "nto")] + unsafe fn retrying_libc_posix_spawnp( + pid: *mut pid_t, + file: *const c_char, + file_actions: *const posix_spawn_file_actions_t, + attrp: *const posix_spawnattr_t, + argv: *const *mut c_char, + envp: *const *mut c_char, + ) -> io::Result { + let mut delay = MIN_FORKSPAWN_SLEEP; + loop { + match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) { + libc::EBADF => { + if delay < get_clock_resolution() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else if delay < MAX_FORKSPAWN_SLEEP { + thread::sleep(delay); + } else { + return Err(io::const_error!( + ErrorKind::WouldBlock, + "posix_spawnp returned EBADF too often", + )); + } + delay *= 2; + continue; + } + r => { + return Ok(r); + } + } + } + } + + type PosixSpawnAddChdirFn = unsafe extern "C" fn( + *mut libc::posix_spawn_file_actions_t, + *const libc::c_char, + ) -> libc::c_int; + + /// Get the function pointer for adding a chdir action to a + /// `posix_spawn_file_actions_t`, if available, assuming a dynamic libc. + /// + /// Some platforms can set a new working directory for a spawned process in the + /// `posix_spawn` path. This function looks up the function pointer for adding + /// such an action to a `posix_spawn_file_actions_t` struct. + #[cfg(not(any(all(target_os = "linux", target_env = "musl"), target_os = "cygwin")))] + fn get_posix_spawn_addchdir() -> Option { + use crate::sys::weak::weak; + + // POSIX.1-2024 standardizes this function: + // https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawn_file_actions_addchdir.html. + // The _np version is more widely available, though, so try that first. + + weak!( + fn posix_spawn_file_actions_addchdir_np( + file_actions: *mut libc::posix_spawn_file_actions_t, + path: *const libc::c_char, + ) -> libc::c_int; + ); + + weak!( + fn posix_spawn_file_actions_addchdir( + file_actions: *mut libc::posix_spawn_file_actions_t, + path: *const libc::c_char, + ) -> libc::c_int; + ); + + posix_spawn_file_actions_addchdir_np + .get() + .or_else(|| posix_spawn_file_actions_addchdir.get()) + } + + /// Get the function pointer for adding a chdir action to a + /// `posix_spawn_file_actions_t`, if available, on platforms where the function + /// is known to exist. + /// + /// Weak symbol lookup doesn't work with statically linked libcs, so in cases + /// where static linking is possible we need to either check for the presence + /// of the symbol at compile time or know about it upfront. + /// + /// Cygwin doesn't support weak symbol, so just link it. + #[cfg(any(all(target_os = "linux", target_env = "musl"), target_os = "cygwin"))] + fn get_posix_spawn_addchdir() -> Option { + // Our minimum required musl supports this function, so we can just use it. + Some(libc::posix_spawn_file_actions_addchdir_np) + } + + let addchdir = match self.get_cwd() { + Some(cwd) => { + if cfg!(target_vendor = "apple") { + // There is a bug in macOS where a relative executable + // path like "../myprogram" will cause `posix_spawn` to + // successfully launch the program, but erroneously return + // ENOENT when used with posix_spawn_file_actions_addchdir_np + // which was introduced in macOS 10.15. + if self.get_program_kind() == ProgramKind::Relative { + return Ok(None); + } + } + // Check for the availability of the posix_spawn addchdir + // function now. If it isn't available, bail and use the + // fork/exec path. + match get_posix_spawn_addchdir() { + Some(f) => Some((f, cwd)), + None => return Ok(None), + } + } + None => None, + }; + + let pgroup = self.get_pgroup(); + + struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit); + + impl Drop for PosixSpawnFileActions<'_> { + fn drop(&mut self) { + unsafe { + libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr()); + } + } + } + + struct PosixSpawnattr<'a>(&'a mut MaybeUninit); + + impl Drop for PosixSpawnattr<'_> { + fn drop(&mut self) { + unsafe { + libc::posix_spawnattr_destroy(self.0.as_mut_ptr()); + } + } + } + + unsafe { + let mut attrs = MaybeUninit::uninit(); + cvt_nz(libc::posix_spawnattr_init(attrs.as_mut_ptr()))?; + let attrs = PosixSpawnattr(&mut attrs); + + let mut flags = 0; + + let mut file_actions = MaybeUninit::uninit(); + cvt_nz(libc::posix_spawn_file_actions_init(file_actions.as_mut_ptr()))?; + let file_actions = PosixSpawnFileActions(&mut file_actions); + + if let Some(fd) = stdio.stdin.fd() { + cvt_nz(libc::posix_spawn_file_actions_adddup2( + file_actions.0.as_mut_ptr(), + fd, + libc::STDIN_FILENO, + ))?; + } + if let Some(fd) = stdio.stdout.fd() { + cvt_nz(libc::posix_spawn_file_actions_adddup2( + file_actions.0.as_mut_ptr(), + fd, + libc::STDOUT_FILENO, + ))?; + } + if let Some(fd) = stdio.stderr.fd() { + cvt_nz(libc::posix_spawn_file_actions_adddup2( + file_actions.0.as_mut_ptr(), + fd, + libc::STDERR_FILENO, + ))?; + } + if let Some((f, cwd)) = addchdir { + cvt_nz(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?; + } + + if let Some(pgroup) = pgroup { + flags |= libc::POSIX_SPAWN_SETPGROUP; + cvt_nz(libc::posix_spawnattr_setpgroup(attrs.0.as_mut_ptr(), pgroup))?; + } + + // Inherit the signal mask from this process rather than resetting it (i.e. do not call + // posix_spawnattr_setsigmask). + + // If -Zon-broken-pipe is used, don't reset SIGPIPE to SIG_DFL. + // If -Zon-broken-pipe is not used, reset SIGPIPE to SIG_DFL for backward compatibility. + // + // -Zon-broken-pipe is an opportunity to change the default here. + if !on_broken_pipe_flag_used() { + let mut default_set = MaybeUninit::::uninit(); + cvt(sigemptyset(default_set.as_mut_ptr()))?; + cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGPIPE))?; + #[cfg(target_os = "hurd")] + { + cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGLOST))?; + } + cvt_nz(libc::posix_spawnattr_setsigdefault( + attrs.0.as_mut_ptr(), + default_set.as_ptr(), + ))?; + flags |= libc::POSIX_SPAWN_SETSIGDEF; + } + + cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; + + // Make sure we synchronize access to the global `environ` resource + let _env_lock = sys::env::env_read_lock(); + let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::env::environ() as *const _); + + #[cfg(not(target_os = "nto"))] + let spawn_fn = libc::posix_spawnp; + #[cfg(target_os = "nto")] + let spawn_fn = retrying_libc_posix_spawnp; + + #[cfg(target_os = "linux")] + if self.get_create_pidfd() && PIDFD_SUPPORTED.load(Ordering::Relaxed) == SPAWN { + let mut pidfd: libc::c_int = -1; + let spawn_res = pidfd_spawnp.get().unwrap()( + &mut pidfd, + self.get_program_cstr().as_ptr(), + file_actions.0.as_ptr(), + attrs.0.as_ptr(), + self.get_argv().as_ptr() as *const _, + envp as *const _, + ); + + let spawn_res = cvt_nz(spawn_res); + if let Err(ref e) = spawn_res + && e.raw_os_error() == Some(libc::ENOSYS) + { + PIDFD_SUPPORTED.store(FORK_EXEC, Ordering::Relaxed); + return Ok(None); + } + spawn_res?; + + let pid = match cvt(pidfd_getpid.get().unwrap()(pidfd)) { + Ok(pid) => pid, + Err(e) => { + // The child has been spawned and we are holding its pidfd. + // But we cannot obtain its pid even though pidfd_getpid support was verified earlier. + // This might happen if libc can't open procfs because the file descriptor limit has been reached. + libc::close(pidfd); + return Err(Error::new( + e.kind(), + "pidfd_spawnp succeeded but the child's PID could not be obtained", + )); + } + }; + + return Ok(Some(Process::new(pid, pidfd))); + } + + // Safety: -1 indicates we don't have a pidfd. + let mut p = Process::new(0, -1); + + let spawn_res = spawn_fn( + &mut p.pid, + self.get_program_cstr().as_ptr(), + file_actions.0.as_ptr(), + attrs.0.as_ptr(), + self.get_argv().as_ptr() as *const _, + envp as *const _, + ); + + #[cfg(target_os = "nto")] + let spawn_res = spawn_res?; + + cvt_nz(spawn_res)?; + Ok(Some(p)) + } + } + + #[cfg(target_os = "linux")] + fn send_pidfd(&self, sock: &crate::sys::net::Socket) { + use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; + + use crate::io::IoSlice; + use crate::os::fd::RawFd; + use crate::sys::cvt_r; + + unsafe { + let child_pid = libc::getpid(); + // pidfd_open sets CLOEXEC by default + let pidfd = libc::syscall(libc::SYS_pidfd_open, child_pid, 0); + + let fds: [c_int; 1] = [pidfd as RawFd]; + + const SCM_MSG_LEN: usize = size_of::<[c_int; 1]>(); + + #[repr(C)] + union Cmsg { + buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }], + _align: libc::cmsghdr, + } + + let mut cmsg: Cmsg = mem::zeroed(); + + // 0-length message to send through the socket so we can pass along the fd + let mut iov = [IoSlice::new(b"")]; + let mut msg: libc::msghdr = mem::zeroed(); + + msg.msg_iov = (&raw mut iov) as *mut _; + msg.msg_iovlen = 1; + + // only attach cmsg if we successfully acquired the pidfd + if pidfd >= 0 { + msg.msg_controllen = size_of_val(&cmsg.buf) as _; + msg.msg_control = (&raw mut cmsg.buf) as *mut _; + + let hdr = CMSG_FIRSTHDR((&raw mut msg) as *mut _); + (*hdr).cmsg_level = SOL_SOCKET; + (*hdr).cmsg_type = SCM_RIGHTS; + (*hdr).cmsg_len = CMSG_LEN(SCM_MSG_LEN as _) as _; + let data = CMSG_DATA(hdr); + crate::ptr::copy_nonoverlapping( + fds.as_ptr().cast::(), + data as *mut _, + SCM_MSG_LEN, + ); + } + + // we send the 0-length message even if we failed to acquire the pidfd + // so we get a consistent SEQPACKET order + match cvt_r(|| libc::sendmsg(sock.as_raw(), &msg, 0)) { + Ok(0) => {} + other => rtabort!("failed to communicate with parent process. {:?}", other), + } + } + } + + #[cfg(target_os = "linux")] + fn recv_pidfd(&self, sock: &crate::sys::net::Socket) -> pid_t { + use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; + + use crate::io::IoSliceMut; + use crate::sys::cvt_r; + + unsafe { + const SCM_MSG_LEN: usize = size_of::<[c_int; 1]>(); + + #[repr(C)] + union Cmsg { + _buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }], + _align: libc::cmsghdr, + } + let mut cmsg: Cmsg = mem::zeroed(); + // 0-length read to get the fd + let mut iov = [IoSliceMut::new(&mut [])]; + + let mut msg: libc::msghdr = mem::zeroed(); + + msg.msg_iov = (&raw mut iov) as *mut _; + msg.msg_iovlen = 1; + msg.msg_controllen = size_of::() as _; + msg.msg_control = (&raw mut cmsg) as *mut _; + + match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, libc::MSG_CMSG_CLOEXEC)) { + Err(_) => return -1, + Ok(_) => {} + } + + let hdr = CMSG_FIRSTHDR((&raw mut msg) as *mut _); + if hdr.is_null() + || (*hdr).cmsg_level != SOL_SOCKET + || (*hdr).cmsg_type != SCM_RIGHTS + || (*hdr).cmsg_len != CMSG_LEN(SCM_MSG_LEN as _) as _ + { + return -1; + } + let data = CMSG_DATA(hdr); + + let mut fds = [-1 as c_int]; + + crate::ptr::copy_nonoverlapping( + data as *const _, + fds.as_mut_ptr().cast::(), + SCM_MSG_LEN, + ); + + fds[0] + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +/// The unique ID of the process (this should never be negative). +pub struct Process { + pid: pid_t, + status: Option, + // On Linux, stores the pidfd created for this child. + // This is None if the user did not request pidfd creation, + // or if the pidfd could not be created for some reason + // (e.g. the `pidfd_open` syscall was not available). + #[cfg(target_os = "linux")] + pidfd: Option, +} + +impl Process { + #[cfg(target_os = "linux")] + /// # Safety + /// + /// `pidfd` must either be -1 (representing no file descriptor) or a valid, exclusively owned file + /// descriptor (See [I/O Safety]). + /// + /// [I/O Safety]: crate::io#io-safety + unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self { + use crate::os::unix::io::FromRawFd; + use crate::sys_common::FromInner; + // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned. + let pidfd = (pidfd >= 0).then(|| PidFd::from_inner(sys::fd::FileDesc::from_raw_fd(pidfd))); + Process { pid, status: None, pidfd } + } + + #[cfg(not(target_os = "linux"))] + unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self { + Process { pid, status: None } + } + + pub fn id(&self) -> u32 { + self.pid as u32 + } + + pub fn kill(&mut self) -> io::Result<()> { + // If we've already waited on this process then the pid can be recycled + // and used for another process, and we probably shouldn't be killing + // random processes, so return Ok because the process has exited already. + if self.status.is_some() { + return Ok(()); + } + #[cfg(target_os = "linux")] + if let Some(pid_fd) = self.pidfd.as_ref() { + // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too + return pid_fd.kill(); + } + cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) + } + + pub fn wait(&mut self) -> io::Result { + use crate::sys::cvt_r; + if let Some(status) = self.status { + return Ok(status); + } + #[cfg(target_os = "linux")] + if let Some(pid_fd) = self.pidfd.as_ref() { + let status = pid_fd.wait()?; + self.status = Some(status); + return Ok(status); + } + let mut status = 0 as c_int; + cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; + self.status = Some(ExitStatus::new(status)); + Ok(ExitStatus::new(status)) + } + + pub fn try_wait(&mut self) -> io::Result> { + if let Some(status) = self.status { + return Ok(Some(status)); + } + #[cfg(target_os = "linux")] + if let Some(pid_fd) = self.pidfd.as_ref() { + let status = pid_fd.try_wait()?; + if let Some(status) = status { + self.status = Some(status) + } + return Ok(status); + } + let mut status = 0 as c_int; + let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?; + if pid == 0 { + Ok(None) + } else { + self.status = Some(ExitStatus::new(status)); + Ok(Some(ExitStatus::new(status))) + } + } +} + +/// Unix exit statuses +// +// This is not actually an "exit status" in Unix terminology. Rather, it is a "wait status". +// See the discussion in comments and doc comments for `std::process::ExitStatus`. +#[derive(PartialEq, Eq, Clone, Copy, Default)] +pub struct ExitStatus(c_int); + +impl fmt::Debug for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("unix_wait_status").field(&self.0).finish() + } +} + +impl ExitStatus { + pub fn new(status: c_int) -> ExitStatus { + ExitStatus(status) + } + + #[cfg(target_os = "linux")] + pub fn from_waitid_siginfo(siginfo: libc::siginfo_t) -> ExitStatus { + let status = unsafe { siginfo.si_status() }; + + match siginfo.si_code { + libc::CLD_EXITED => ExitStatus((status & 0xff) << 8), + libc::CLD_KILLED => ExitStatus(status), + libc::CLD_DUMPED => ExitStatus(status | 0x80), + libc::CLD_CONTINUED => ExitStatus(0xffff), + libc::CLD_STOPPED | libc::CLD_TRAPPED => ExitStatus(((status & 0xff) << 8) | 0x7f), + _ => unreachable!("waitid() should only return the above codes"), + } + } + + fn exited(&self) -> bool { + libc::WIFEXITED(self.0) + } + + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is + // true on all actual versions of Unix, is widely assumed, and is specified in SuS + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not + // true for a platform pretending to be Unix, the tests (our doctests, and also + // unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. + match NonZero::try_from(self.0) { + /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), + /* was zero, couldn't convert */ Err(_) => Ok(()), + } + } + + pub fn code(&self) -> Option { + self.exited().then(|| libc::WEXITSTATUS(self.0)) + } + + pub fn signal(&self) -> Option { + libc::WIFSIGNALED(self.0).then(|| libc::WTERMSIG(self.0)) + } + + pub fn core_dumped(&self) -> bool { + libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0) + } + + pub fn stopped_signal(&self) -> Option { + libc::WIFSTOPPED(self.0).then(|| libc::WSTOPSIG(self.0)) + } + + pub fn continued(&self) -> bool { + libc::WIFCONTINUED(self.0) + } + + pub fn into_raw(&self) -> c_int { + self.0 + } +} + +/// 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) + } +} + +/// Converts a signal number to a readable, searchable name. +/// +/// This string should be displayed right after the signal number. +/// If a signal is unrecognized, it returns the empty string, so that +/// you just get the number like "0". If it is recognized, you'll get +/// something like "9 (SIGKILL)". +fn signal_string(signal: i32) -> &'static str { + match signal { + libc::SIGHUP => " (SIGHUP)", + libc::SIGINT => " (SIGINT)", + libc::SIGQUIT => " (SIGQUIT)", + libc::SIGILL => " (SIGILL)", + libc::SIGTRAP => " (SIGTRAP)", + libc::SIGABRT => " (SIGABRT)", + #[cfg(not(target_os = "l4re"))] + libc::SIGBUS => " (SIGBUS)", + libc::SIGFPE => " (SIGFPE)", + libc::SIGKILL => " (SIGKILL)", + #[cfg(not(target_os = "l4re"))] + libc::SIGUSR1 => " (SIGUSR1)", + libc::SIGSEGV => " (SIGSEGV)", + #[cfg(not(target_os = "l4re"))] + libc::SIGUSR2 => " (SIGUSR2)", + libc::SIGPIPE => " (SIGPIPE)", + libc::SIGALRM => " (SIGALRM)", + libc::SIGTERM => " (SIGTERM)", + #[cfg(not(target_os = "l4re"))] + libc::SIGCHLD => " (SIGCHLD)", + #[cfg(not(target_os = "l4re"))] + libc::SIGCONT => " (SIGCONT)", + #[cfg(not(target_os = "l4re"))] + libc::SIGSTOP => " (SIGSTOP)", + #[cfg(not(target_os = "l4re"))] + libc::SIGTSTP => " (SIGTSTP)", + #[cfg(not(target_os = "l4re"))] + libc::SIGTTIN => " (SIGTTIN)", + #[cfg(not(target_os = "l4re"))] + libc::SIGTTOU => " (SIGTTOU)", + #[cfg(not(target_os = "l4re"))] + libc::SIGURG => " (SIGURG)", + #[cfg(not(target_os = "l4re"))] + libc::SIGXCPU => " (SIGXCPU)", + #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] + libc::SIGXFSZ => " (SIGXFSZ)", + #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] + libc::SIGVTALRM => " (SIGVTALRM)", + #[cfg(not(target_os = "l4re"))] + libc::SIGPROF => " (SIGPROF)", + #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] + libc::SIGWINCH => " (SIGWINCH)", + #[cfg(not(any(target_os = "haiku", target_os = "l4re")))] + libc::SIGIO => " (SIGIO)", + #[cfg(target_os = "haiku")] + libc::SIGPOLL => " (SIGPOLL)", + #[cfg(not(target_os = "l4re"))] + libc::SIGSYS => " (SIGSYS)", + // For information on Linux signals, run `man 7 signal` + #[cfg(all( + target_os = "linux", + any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "arm", + target_arch = "aarch64" + ) + ))] + libc::SIGSTKFLT => " (SIGSTKFLT)", + #[cfg(any(target_os = "linux", target_os = "nto", target_os = "cygwin"))] + libc::SIGPWR => " (SIGPWR)", + #[cfg(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "nto", + target_vendor = "apple", + target_os = "cygwin", + ))] + libc::SIGEMT => " (SIGEMT)", + #[cfg(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_vendor = "apple", + ))] + libc::SIGINFO => " (SIGINFO)", + #[cfg(target_os = "hurd")] + libc::SIGLOST => " (SIGLOST)", + #[cfg(target_os = "freebsd")] + libc::SIGTHR => " (SIGTHR)", + #[cfg(target_os = "freebsd")] + libc::SIGLIBRT => " (SIGLIBRT)", + _ => "", + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(code) = self.code() { + write!(f, "exit status: {code}") + } else if let Some(signal) = self.signal() { + let signal_string = signal_string(signal); + if self.core_dumped() { + write!(f, "signal: {signal}{signal_string} (core dumped)") + } else { + write!(f, "signal: {signal}{signal_string}") + } + } else if let Some(signal) = self.stopped_signal() { + let signal_string = signal_string(signal); + write!(f, "stopped (not terminated) by signal: {signal}{signal_string}") + } else if self.continued() { + write!(f, "continued (WIFCONTINUED)") + } else { + write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0) + } + } +} + +#[derive(PartialEq, Eq, Clone, Copy)] +pub struct ExitStatusError(NonZero); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +impl fmt::Debug for ExitStatusError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("unix_wait_status").field(&self.0).finish() + } +} + +impl ExitStatusError { + pub fn code(self) -> Option> { + ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) + } +} + +#[cfg(target_os = "linux")] +mod linux_child_ext { + use crate::io::ErrorKind; + use crate::os::linux::process as os; + use crate::sys::pal::linux::pidfd as imp; + use crate::sys_common::FromInner; + use crate::{io, mem}; + + #[unstable(feature = "linux_pidfd", issue = "82971")] + impl crate::os::linux::process::ChildExt for crate::process::Child { + fn pidfd(&self) -> io::Result<&os::PidFd> { + self.handle + .pidfd + .as_ref() + // SAFETY: The os type is a transparent wrapper, therefore we can transmute references + .map(|fd| unsafe { mem::transmute::<&imp::PidFd, &os::PidFd>(fd) }) + .ok_or_else(|| io::const_error!(ErrorKind::Uncategorized, "no pidfd was created.")) + } + + fn into_pidfd(mut self) -> Result { + self.handle + .pidfd + .take() + .map(|fd| >::from_inner(fd)) + .ok_or_else(|| self) + } + } +} + +#[cfg(test)] +mod tests; + +// See [`unsupported_wait_status::compare_with_linux`]; +#[cfg(all(test, target_os = "linux"))] +#[path = "unsupported/wait_status.rs"] +mod unsupported_wait_status; diff --git a/library/std/src/sys/pal/unix/process/process_unix/tests.rs b/library/std/src/sys/process/unix/unix/tests.rs similarity index 100% rename from library/std/src/sys/pal/unix/process/process_unix/tests.rs rename to library/std/src/sys/process/unix/unix/tests.rs diff --git a/library/std/src/sys/process/unix/unsupported.rs b/library/std/src/sys/process/unix/unsupported.rs new file mode 100644 index 0000000000000..e86561a5c5c4f --- /dev/null +++ b/library/std/src/sys/process/unix/unsupported.rs @@ -0,0 +1,72 @@ +use libc::{c_int, pid_t}; + +use super::common::*; +use crate::io; +use crate::num::NonZero; +use crate::sys::pal::unsupported::*; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +impl Command { + pub fn spawn( + &mut self, + _default: Stdio, + _needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + unsupported() + } + + pub fn exec(&mut self, _default: Stdio) -> io::Error { + unsupported_err() + } +} + +pub fn output(_: &mut Command) -> io::Result<(ExitStatus, Vec, Vec)> { + unsupported() +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +pub struct Process { + _handle: pid_t, +} + +impl Process { + pub fn id(&self) -> u32 { + 0 + } + + pub fn kill(&mut self) -> io::Result<()> { + unsupported() + } + + pub fn wait(&mut self) -> io::Result { + unsupported() + } + + pub fn try_wait(&mut self) -> io::Result> { + unsupported() + } +} + +mod wait_status; +pub use wait_status::ExitStatus; + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatusError(NonZero); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus::from(c_int::from(self.0)) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option> { + ExitStatus::from(c_int::from(self.0)).code().map(|st| st.try_into().unwrap()) + } +} diff --git a/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs b/library/std/src/sys/process/unix/unsupported/wait_status.rs similarity index 91% rename from library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs rename to library/std/src/sys/process/unix/unsupported/wait_status.rs index f04036bde4922..f348d557e4b7e 100644 --- a/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs +++ b/library/std/src/sys/process/unix/unsupported/wait_status.rs @@ -7,7 +7,7 @@ use crate::ffi::c_int; use crate::fmt; use crate::num::NonZero; -/// Emulated wait status for use by `process_unsupported.rs` +/// Emulated wait status for use by `unsupported.rs` /// /// Uses the "traditional unix" encoding. For use on platfors which are `#[cfg(unix)]` /// but do not actually support subprocesses at all. @@ -48,7 +48,7 @@ impl ExitStatus { // true on all actual versions of Unix, is widely assumed, and is specified in SuS // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not // true for a platform pretending to be Unix, the tests (our doctests, and also - // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. + // unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. match NonZero::try_from(self.wait_status) { /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), /* was zero, couldn't convert */ Err(_) => Ok(()), @@ -79,5 +79,6 @@ impl ExitStatus { } #[cfg(test)] -#[path = "wait_status/tests.rs"] // needed because of strange layout of process_unsupported +#[path = "wait_status/tests.rs"] +// needed because this module is also imported through #[path] for testing purposes mod tests; diff --git a/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs b/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs new file mode 100644 index 0000000000000..0d9232fac5e4e --- /dev/null +++ b/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs @@ -0,0 +1,36 @@ +// Note that tests in this file are run on Linux as well as on platforms using unsupported + +// Test that our emulation exactly matches Linux +// +// This test runs *on Linux* but it tests +// the implementation used on non-Unix `#[cfg(unix)]` platforms. +// +// I.e. we're using Linux as a proxy for "trad unix". +#[cfg(target_os = "linux")] +#[test] +fn compare_with_linux() { + use super::ExitStatus as Emulated; + use crate::os::unix::process::ExitStatusExt as _; + use crate::process::ExitStatus as Real; + + // Check that we handle out-of-range values similarly, too. + for wstatus in -0xf_ffff..0xf_ffff { + let emulated = Emulated::from(wstatus); + let real = Real::from_raw(wstatus); + + macro_rules! compare { { $method:ident } => { + assert_eq!( + emulated.$method(), + real.$method(), + "{wstatus:#x}.{}()", + stringify!($method), + ); + } } + compare!(code); + compare!(signal); + compare!(core_dumped); + compare!(stopped_signal); + compare!(continued); + compare!(into_raw); + } +} diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs new file mode 100644 index 0000000000000..f33b4a375da83 --- /dev/null +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -0,0 +1,269 @@ +#![forbid(unsafe_op_in_unsafe_fn)] +use libc::{self, RTP_ID, c_char, c_int}; + +use super::common::*; +use crate::io::{self, ErrorKind}; +use crate::num::NonZero; +use crate::sys::cvt; +use crate::sys::pal::thread; +use crate::{fmt, sys}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +impl Command { + pub fn spawn( + &mut self, + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + use crate::sys::cvt_r; + let envp = self.capture_env(); + + if self.saw_nul() { + return Err(io::const_error!( + ErrorKind::InvalidInput, + "nul byte found in provided data", + )); + } + if self.get_chroot().is_some() { + return Err(io::const_error!( + ErrorKind::Unsupported, + "chroot not supported by vxworks", + )); + } + let (ours, theirs) = self.setup_io(default, needs_stdin)?; + let mut p = Process { pid: 0, status: None }; + + unsafe { + macro_rules! t { + ($e:expr) => { + match $e { + Ok(e) => e, + Err(e) => return Err(e.into()), + } + }; + } + + let mut orig_stdin = libc::STDIN_FILENO; + let mut orig_stdout = libc::STDOUT_FILENO; + let mut orig_stderr = libc::STDERR_FILENO; + + if let Some(fd) = theirs.stdin.fd() { + orig_stdin = t!(cvt_r(|| libc::dup(libc::STDIN_FILENO))); + t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))); + } + if let Some(fd) = theirs.stdout.fd() { + orig_stdout = t!(cvt_r(|| libc::dup(libc::STDOUT_FILENO))); + t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))); + } + if let Some(fd) = theirs.stderr.fd() { + orig_stderr = t!(cvt_r(|| libc::dup(libc::STDERR_FILENO))); + t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))); + } + + if let Some(cwd) = self.get_cwd() { + t!(cvt(libc::chdir(cwd.as_ptr()))); + } + + // pre_exec closures are ignored on VxWorks + let _ = self.get_closures(); + + let c_envp = envp + .as_ref() + .map(|c| c.as_ptr()) + .unwrap_or_else(|| *sys::env::environ() as *const _); + let stack_size = crate::cmp::max( + crate::env::var_os("RUST_MIN_STACK") + .and_then(|s| s.to_str().and_then(|s| s.parse().ok())) + .unwrap_or(thread::DEFAULT_MIN_STACK_SIZE), + libc::PTHREAD_STACK_MIN, + ); + + // ensure that access to the environment is synchronized + let _lock = sys::env::env_read_lock(); + + let ret = libc::rtpSpawn( + self.get_program_cstr().as_ptr(), + self.get_argv().as_ptr() as *mut *const c_char, // argv + c_envp as *mut *const c_char, + 100 as c_int, // initial priority + stack_size, // initial stack size. + 0, // options + 0, // task options + ); + + // Because FileDesc was not used, each duplicated file descriptor + // needs to be closed manually + if orig_stdin != libc::STDIN_FILENO { + t!(cvt_r(|| libc::dup2(orig_stdin, libc::STDIN_FILENO))); + libc::close(orig_stdin); + } + if orig_stdout != libc::STDOUT_FILENO { + t!(cvt_r(|| libc::dup2(orig_stdout, libc::STDOUT_FILENO))); + libc::close(orig_stdout); + } + if orig_stderr != libc::STDERR_FILENO { + t!(cvt_r(|| libc::dup2(orig_stderr, libc::STDERR_FILENO))); + libc::close(orig_stderr); + } + + if ret != libc::RTP_ID_ERROR { + p.pid = ret; + Ok((p, ours)) + } else { + Err(io::Error::last_os_error()) + } + } + } + + pub fn exec(&mut self, default: Stdio) -> io::Error { + let ret = Command::spawn(self, default, false); + match ret { + Ok(t) => unsafe { + let mut status = 0 as c_int; + libc::waitpid(t.0.pid, &mut status, 0); + libc::exit(0); + }, + Err(e) => e, + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +/// The unique id of the process (this should never be negative). +pub struct Process { + pid: RTP_ID, + status: Option, +} + +impl Process { + pub fn id(&self) -> u32 { + self.pid as u32 + } + + pub fn kill(&mut self) -> io::Result<()> { + // If we've already waited on this process then the pid can be recycled + // and used for another process, and we probably shouldn't be killing + // random processes, so return Ok because the process has exited already. + if self.status.is_some() { + Ok(()) + } else { + cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) + } + } + + pub fn wait(&mut self) -> io::Result { + use crate::sys::cvt_r; + if let Some(status) = self.status { + return Ok(status); + } + let mut status = 0 as c_int; + cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; + self.status = Some(ExitStatus::new(status)); + Ok(ExitStatus::new(status)) + } + + pub fn try_wait(&mut self) -> io::Result> { + if let Some(status) = self.status { + return Ok(Some(status)); + } + let mut status = 0 as c_int; + let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?; + if pid == 0 { + Ok(None) + } else { + self.status = Some(ExitStatus::new(status)); + Ok(Some(ExitStatus::new(status))) + } + } +} + +/// Unix exit statuses +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] +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 exit_ok(&self) -> Result<(), ExitStatusError> { + // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is + // true on all actual versions of Unix, is widely assumed, and is specified in SuS + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not + // true for a platform pretending to be Unix, the tests (our doctests, and also + // unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. + match NonZero::try_from(self.0) { + Ok(failure) => Err(ExitStatusError(failure)), + Err(_) => Ok(()), + } + } + + 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 } + } + + pub fn core_dumped(&self) -> bool { + // This method is not yet properly implemented on VxWorks + false + } + + pub fn stopped_signal(&self) -> Option { + if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None } + } + + pub fn continued(&self) -> bool { + // This method is not yet properly implemented on VxWorks + false + } + + pub fn into_raw(&self) -> c_int { + self.0 + } +} + +/// 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 ExitStatusError(NonZero); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option> { + ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) + } +} diff --git a/library/std/src/sys/process/unsupported.rs b/library/std/src/sys/process/unsupported.rs new file mode 100644 index 0000000000000..469922c78aca2 --- /dev/null +++ b/library/std/src/sys/process/unsupported.rs @@ -0,0 +1,322 @@ +use super::env::{CommandEnv, CommandEnvs}; +pub use crate::ffi::OsString as EnvKey; +use crate::ffi::{OsStr, OsString}; +use crate::num::NonZero; +use crate::path::Path; +use crate::sys::fs::File; +use crate::sys::pipe::AnonPipe; +use crate::sys::unsupported; +use crate::{fmt, io}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + program: OsString, + args: Vec, + env: CommandEnv, + + cwd: Option, + stdin: Option, + stdout: Option, + stderr: Option, +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +#[derive(Debug)] +pub enum Stdio { + Inherit, + Null, + MakePipe, + ParentStdout, + ParentStderr, + #[allow(dead_code)] // This variant exists only for the Debug impl + InheritFile(File), +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { + program: program.to_owned(), + args: vec![program.to_owned()], + env: Default::default(), + cwd: None, + stdin: None, + stdout: None, + stderr: None, + } + } + + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_owned()); + } + + pub fn env_mut(&mut self) -> &mut CommandEnv { + &mut self.env + } + + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.to_owned()); + } + + 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 get_program(&self) -> &OsStr { + &self.program + } + + pub fn get_args(&self) -> CommandArgs<'_> { + let mut iter = self.args.iter(); + iter.next(); + CommandArgs { iter } + } + + pub fn get_envs(&self) -> CommandEnvs<'_> { + self.env.iter() + } + + pub fn get_current_dir(&self) -> Option<&Path> { + self.cwd.as_ref().map(|cs| Path::new(cs)) + } + + pub fn spawn( + &mut self, + _default: Stdio, + _needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + unsupported() + } +} + +pub fn output(_cmd: &mut Command) -> io::Result<(ExitStatus, Vec, Vec)> { + unsupported() +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + Stdio::ParentStdout + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + Stdio::ParentStderr + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + Stdio::InheritFile(file) + } +} + +impl fmt::Debug for Command { + // show all attributes + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + let mut debug_command = f.debug_struct("Command"); + debug_command.field("program", &self.program).field("args", &self.args); + if !self.env.is_unchanged() { + debug_command.field("env", &self.env); + } + + if self.cwd.is_some() { + debug_command.field("cwd", &self.cwd); + } + + if self.stdin.is_some() { + debug_command.field("stdin", &self.stdin); + } + if self.stdout.is_some() { + debug_command.field("stdout", &self.stdout); + } + if self.stderr.is_some() { + debug_command.field("stderr", &self.stderr); + } + + debug_command.finish() + } else { + if let Some(ref cwd) = self.cwd { + write!(f, "cd {cwd:?} && ")?; + } + if self.env.does_clear() { + write!(f, "env -i ")?; + // Altered env vars will be printed next, that should exactly work as expected. + } else { + // Removed env vars need the command to be wrapped in `env`. + let mut any_removed = false; + for (key, value_opt) in self.get_envs() { + if value_opt.is_none() { + if !any_removed { + write!(f, "env ")?; + any_removed = true; + } + write!(f, "-u {} ", key.to_string_lossy())?; + } + } + } + // Altered env vars can just be added in front of the program. + for (key, value_opt) in self.get_envs() { + if let Some(value) = value_opt { + write!(f, "{}={value:?} ", key.to_string_lossy())?; + } + } + 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, Default)] +#[non_exhaustive] +pub struct ExitStatus(); + +impl ExitStatus { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + Ok(()) + } + + pub fn code(&self) -> Option { + Some(0) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "") + } +} + +pub struct ExitStatusError(!); + +impl Clone for ExitStatusError { + fn clone(&self) -> ExitStatusError { + self.0 + } +} + +impl Copy for ExitStatusError {} + +impl PartialEq for ExitStatusError { + fn eq(&self, _other: &ExitStatusError) -> bool { + self.0 + } +} + +impl Eq for ExitStatusError {} + +impl fmt::Debug for ExitStatusError { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + self.0 + } +} + +impl ExitStatusError { + pub fn code(self) -> Option> { + self.0 + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(u8); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(0); + pub const FAILURE: ExitCode = ExitCode(1); + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +impl From for ExitCode { + fn from(code: u8) -> Self { + Self(code) + } +} + +pub struct Process(!); + +impl Process { + pub fn id(&self) -> u32 { + self.0 + } + + pub fn kill(&mut self) -> io::Result<()> { + self.0 + } + + pub fn wait(&mut self) -> io::Result { + self.0 + } + + pub fn try_wait(&mut self) -> io::Result> { + self.0 + } +} + +pub struct CommandArgs<'a> { + iter: crate::slice::Iter<'a, OsString>, +} + +impl<'a> Iterator for CommandArgs<'a> { + type Item = &'a OsStr; + fn next(&mut self) -> Option<&'a OsStr> { + self.iter.next().map(|os| &**os) + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +impl<'a> fmt::Debug for CommandArgs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.clone()).finish() + } +} diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs new file mode 100644 index 0000000000000..1ee3fbd285f52 --- /dev/null +++ b/library/std/src/sys/process/windows.rs @@ -0,0 +1,975 @@ +#![unstable(feature = "process_internals", issue = "none")] + +#[cfg(test)] +mod tests; + +use core::ffi::c_void; + +use super::env::{CommandEnv, CommandEnvs}; +use crate::collections::BTreeMap; +use crate::env::consts::{EXE_EXTENSION, EXE_SUFFIX}; +use crate::ffi::{OsStr, OsString}; +use crate::io::{self, Error}; +use crate::num::NonZero; +use crate::os::windows::ffi::{OsStrExt, OsStringExt}; +use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle}; +use crate::os::windows::process::ProcThreadAttributeList; +use crate::path::{Path, PathBuf}; +use crate::sync::Mutex; +use crate::sys::args::{self, Arg}; +use crate::sys::c::{self, EXIT_FAILURE, EXIT_SUCCESS}; +use crate::sys::fs::{File, OpenOptions}; +use crate::sys::handle::Handle; +use crate::sys::pal::api::{self, WinError, utf16}; +use crate::sys::pal::{ensure_no_nuls, fill_utf16_buf}; +use crate::sys::pipe::{self, AnonPipe}; +use crate::sys::{cvt, path, stdio}; +use crate::sys_common::IntoInner; +use crate::{cmp, env, fmt, ptr}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +#[derive(Clone, Debug, Eq)] +#[doc(hidden)] +pub struct EnvKey { + os_string: OsString, + // This stores a UTF-16 encoded string to workaround the mismatch between + // Rust's OsString (WTF-8) and the Windows API string type (UTF-16). + // Normally converting on every API call is acceptable but here + // `c::CompareStringOrdinal` will be called for every use of `==`. + utf16: Vec, +} + +impl EnvKey { + fn new>(key: T) -> Self { + EnvKey::from(key.into()) + } +} + +// Comparing Windows environment variable keys[1] are behaviorally the +// composition of two operations[2]: +// +// 1. Case-fold both strings. This is done using a language-independent +// uppercase mapping that's unique to Windows (albeit based on data from an +// older Unicode spec). It only operates on individual UTF-16 code units so +// surrogates are left unchanged. This uppercase mapping can potentially change +// between Windows versions. +// +// 2. Perform an ordinal comparison of the strings. A comparison using ordinal +// is just a comparison based on the numerical value of each UTF-16 code unit[3]. +// +// Because the case-folding mapping is unique to Windows and not guaranteed to +// be stable, we ask the OS to compare the strings for us. This is done by +// calling `CompareStringOrdinal`[4] with `bIgnoreCase` set to `TRUE`. +// +// [1] https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#choosing-a-stringcomparison-member-for-your-method-call +// [2] https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#stringtoupper-and-stringtolower +// [3] https://docs.microsoft.com/en-us/dotnet/api/system.stringcomparison?view=net-5.0#System_StringComparison_Ordinal +// [4] https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-comparestringordinal +impl Ord for EnvKey { + fn cmp(&self, other: &Self) -> cmp::Ordering { + unsafe { + let result = c::CompareStringOrdinal( + self.utf16.as_ptr(), + self.utf16.len() as _, + other.utf16.as_ptr(), + other.utf16.len() as _, + c::TRUE, + ); + match result { + c::CSTR_LESS_THAN => cmp::Ordering::Less, + c::CSTR_EQUAL => cmp::Ordering::Equal, + c::CSTR_GREATER_THAN => cmp::Ordering::Greater, + // `CompareStringOrdinal` should never fail so long as the parameters are correct. + _ => panic!("comparing environment keys failed: {}", Error::last_os_error()), + } + } + } +} +impl PartialOrd for EnvKey { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl PartialEq for EnvKey { + fn eq(&self, other: &Self) -> bool { + if self.utf16.len() != other.utf16.len() { + false + } else { + self.cmp(other) == cmp::Ordering::Equal + } + } +} +impl PartialOrd for EnvKey { + fn partial_cmp(&self, other: &str) -> Option { + Some(self.cmp(&EnvKey::new(other))) + } +} +impl PartialEq for EnvKey { + fn eq(&self, other: &str) -> bool { + if self.os_string.len() != other.len() { + false + } else { + self.cmp(&EnvKey::new(other)) == cmp::Ordering::Equal + } + } +} + +// Environment variable keys should preserve their original case even though +// they are compared using a caseless string mapping. +impl From for EnvKey { + fn from(k: OsString) -> Self { + EnvKey { utf16: k.encode_wide().collect(), os_string: k } + } +} + +impl From for OsString { + fn from(k: EnvKey) -> Self { + k.os_string + } +} + +impl From<&OsStr> for EnvKey { + fn from(k: &OsStr) -> Self { + Self::from(k.to_os_string()) + } +} + +impl AsRef for EnvKey { + fn as_ref(&self) -> &OsStr { + &self.os_string + } +} + +pub struct Command { + program: OsString, + args: Vec, + env: CommandEnv, + cwd: Option, + flags: u32, + show_window: Option, + detach: bool, // not currently exposed in std::process + stdin: Option, + stdout: Option, + stderr: Option, + force_quotes_enabled: bool, + startupinfo_fullscreen: bool, + startupinfo_untrusted_source: bool, + startupinfo_force_feedback: Option, +} + +pub enum Stdio { + Inherit, + InheritSpecific { from_stdio_id: u32 }, + Null, + MakePipe, + Pipe(AnonPipe), + Handle(Handle), +} + +pub struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { + program: program.to_os_string(), + args: Vec::new(), + env: Default::default(), + cwd: None, + flags: 0, + show_window: None, + detach: false, + stdin: None, + stdout: None, + stderr: None, + force_quotes_enabled: false, + startupinfo_fullscreen: false, + startupinfo_untrusted_source: false, + startupinfo_force_feedback: None, + } + } + + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(Arg::Regular(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 show_window(&mut self, cmd_show: Option) { + self.show_window = cmd_show; + } + + pub fn force_quotes(&mut self, enabled: bool) { + self.force_quotes_enabled = enabled; + } + + pub fn raw_arg(&mut self, command_str_to_append: &OsStr) { + self.args.push(Arg::Raw(command_str_to_append.to_os_string())) + } + + pub fn startupinfo_fullscreen(&mut self, enabled: bool) { + self.startupinfo_fullscreen = enabled; + } + + pub fn startupinfo_untrusted_source(&mut self, enabled: bool) { + self.startupinfo_untrusted_source = enabled; + } + + pub fn startupinfo_force_feedback(&mut self, enabled: Option) { + self.startupinfo_force_feedback = enabled; + } + + pub fn get_program(&self) -> &OsStr { + &self.program + } + + pub fn get_args(&self) -> CommandArgs<'_> { + let iter = self.args.iter(); + CommandArgs { iter } + } + + pub fn get_envs(&self) -> CommandEnvs<'_> { + self.env.iter() + } + + pub fn get_current_dir(&self) -> Option<&Path> { + self.cwd.as_ref().map(Path::new) + } + + pub fn spawn( + &mut self, + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + self.spawn_with_attributes(default, needs_stdin, None) + } + + pub fn spawn_with_attributes( + &mut self, + default: Stdio, + needs_stdin: bool, + proc_thread_attribute_list: Option<&ProcThreadAttributeList<'_>>, + ) -> io::Result<(Process, StdioPipes)> { + let env_saw_path = self.env.have_changed_path(); + let maybe_env = self.env.capture_if_changed(); + + let child_paths = if env_saw_path && let Some(env) = maybe_env.as_ref() { + env.get(&EnvKey::new("PATH")).map(|s| s.as_os_str()) + } else { + None + }; + let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?; + let has_bat_extension = |program: &[u16]| { + matches!( + // Case insensitive "ends_with" of UTF-16 encoded ".bat" or ".cmd" + program.len().checked_sub(4).and_then(|i| program.get(i..)), + Some([46, 98 | 66, 97 | 65, 116 | 84] | [46, 99 | 67, 109 | 77, 100 | 68]) + ) + }; + let is_batch_file = if path::is_verbatim(&program) { + has_bat_extension(&program[..program.len() - 1]) + } else { + fill_utf16_buf( + |buffer, size| unsafe { + // resolve the path so we can test the final file name. + c::GetFullPathNameW(program.as_ptr(), size, buffer, ptr::null_mut()) + }, + |program| has_bat_extension(program), + )? + }; + let (program, mut cmd_str) = if is_batch_file { + ( + command_prompt()?, + args::make_bat_command_line(&program, &self.args, self.force_quotes_enabled)?, + ) + } else { + let cmd_str = make_command_line(&self.program, &self.args, self.force_quotes_enabled)?; + (program, cmd_str) + }; + 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: + // https://support.microsoft.com/kb/315939 + static CREATE_PROCESS_LOCK: Mutex<()> = Mutex::new(()); + + let _guard = CREATE_PROCESS_LOCK.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)?; + + let mut si = zeroed_startupinfo(); + + // If at least one of stdin, stdout or stderr are set (i.e. are non null) + // then set the `hStd` fields in `STARTUPINFO`. + // Otherwise skip this and allow the OS to apply its default behavior. + // This provides more consistent behavior between Win7 and Win8+. + let is_set = |stdio: &Handle| !stdio.as_raw_handle().is_null(); + if is_set(&stderr) || is_set(&stdout) || is_set(&stdin) { + si.dwFlags |= c::STARTF_USESTDHANDLES; + si.hStdInput = stdin.as_raw_handle(); + si.hStdOutput = stdout.as_raw_handle(); + si.hStdError = stderr.as_raw_handle(); + } + + if let Some(cmd_show) = self.show_window { + si.dwFlags |= c::STARTF_USESHOWWINDOW; + si.wShowWindow = cmd_show; + } + + if self.startupinfo_fullscreen { + si.dwFlags |= c::STARTF_RUNFULLSCREEN; + } + + if self.startupinfo_untrusted_source { + si.dwFlags |= c::STARTF_UNTRUSTEDSOURCE; + } + + match self.startupinfo_force_feedback { + Some(true) => { + si.dwFlags |= c::STARTF_FORCEONFEEDBACK; + } + Some(false) => { + si.dwFlags |= c::STARTF_FORCEOFFFEEDBACK; + } + None => {} + } + + let si_ptr: *mut c::STARTUPINFOW; + + let mut si_ex; + + if let Some(proc_thread_attribute_list) = proc_thread_attribute_list { + si.cb = size_of::() as u32; + flags |= c::EXTENDED_STARTUPINFO_PRESENT; + + si_ex = c::STARTUPINFOEXW { + StartupInfo: si, + // SAFETY: Casting this `*const` pointer to a `*mut` pointer is "safe" + // here because windows does not internally mutate the attribute list. + // Ideally this should be reflected in the interface of the `windows-sys` crate. + lpAttributeList: proc_thread_attribute_list.as_ptr().cast::().cast_mut(), + }; + si_ptr = (&raw mut si_ex) as _; + } else { + si.cb = size_of::() as u32; + si_ptr = (&raw mut si) as _; + } + + unsafe { + cvt(c::CreateProcessW( + program.as_ptr(), + cmd_str.as_mut_ptr(), + ptr::null_mut(), + ptr::null_mut(), + c::TRUE, + flags, + envp, + dirp, + si_ptr, + &mut pi, + )) + }?; + + unsafe { + Ok(( + Process { + handle: Handle::from_raw_handle(pi.hProcess), + main_thread_handle: Handle::from_raw_handle(pi.hThread), + }, + pipes, + )) + } + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.program.fmt(f)?; + for arg in &self.args { + f.write_str(" ")?; + match arg { + Arg::Regular(s) => s.fmt(f), + Arg::Raw(s) => f.write_str(&s.to_string_lossy()), + }?; + } + Ok(()) + } +} + +// Resolve `exe_path` to the executable name. +// +// * If the path is simply a file name then use the paths given by `search_paths` to find the executable. +// * Otherwise use the `exe_path` as given. +// +// This function may also append `.exe` to the name. The rationale for doing so is as follows: +// +// It is a very strong convention that Windows executables have the `exe` extension. +// In Rust, it is common to omit this extension. +// Therefore this functions first assumes `.exe` was intended. +// It falls back to the plain file name if a full path is given and the extension is omitted +// or if only a file name is given and it already contains an extension. +fn resolve_exe<'a>( + exe_path: &'a OsStr, + parent_paths: impl FnOnce() -> Option, + child_paths: Option<&OsStr>, +) -> io::Result> { + // Early return if there is no filename. + if exe_path.is_empty() || path::has_trailing_slash(exe_path) { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "program path has no file name")); + } + // Test if the file name has the `exe` extension. + // This does a case-insensitive `ends_with`. + let has_exe_suffix = if exe_path.len() >= EXE_SUFFIX.len() { + exe_path.as_encoded_bytes()[exe_path.len() - EXE_SUFFIX.len()..] + .eq_ignore_ascii_case(EXE_SUFFIX.as_bytes()) + } else { + false + }; + + // If `exe_path` is an absolute path or a sub-path then don't search `PATH` for it. + if !path::is_file_name(exe_path) { + if has_exe_suffix { + // The application name is a path to a `.exe` file. + // Let `CreateProcessW` figure out if it exists or not. + return args::to_user_path(Path::new(exe_path)); + } + let mut path = PathBuf::from(exe_path); + + // Append `.exe` if not already there. + path = path::append_suffix(path, EXE_SUFFIX.as_ref()); + if let Some(path) = program_exists(&path) { + return Ok(path); + } else { + // It's ok to use `set_extension` here because the intent is to + // remove the extension that was just added. + path.set_extension(""); + return args::to_user_path(&path); + } + } else { + ensure_no_nuls(exe_path)?; + // From the `CreateProcessW` docs: + // > If the file name does not contain an extension, .exe is appended. + // Note that this rule only applies when searching paths. + let has_extension = exe_path.as_encoded_bytes().contains(&b'.'); + + // Search the directories given by `search_paths`. + let result = search_paths(parent_paths, child_paths, |mut path| { + path.push(exe_path); + if !has_extension { + path.set_extension(EXE_EXTENSION); + } + program_exists(&path) + }); + if let Some(path) = result { + return Ok(path); + } + } + // If we get here then the executable cannot be found. + Err(io::const_error!(io::ErrorKind::NotFound, "program not found")) +} + +// Calls `f` for every path that should be used to find an executable. +// Returns once `f` returns the path to an executable or all paths have been searched. +fn search_paths( + parent_paths: Paths, + child_paths: Option<&OsStr>, + mut exists: Exists, +) -> Option> +where + Paths: FnOnce() -> Option, + Exists: FnMut(PathBuf) -> Option>, +{ + // 1. Child paths + // This is for consistency with Rust's historic behavior. + if let Some(paths) = child_paths { + for path in env::split_paths(paths).filter(|p| !p.as_os_str().is_empty()) { + if let Some(path) = exists(path) { + return Some(path); + } + } + } + + // 2. Application path + if let Ok(mut app_path) = env::current_exe() { + app_path.pop(); + if let Some(path) = exists(app_path) { + return Some(path); + } + } + + // 3 & 4. System paths + // SAFETY: This uses `fill_utf16_buf` to safely call the OS functions. + unsafe { + if let Ok(Some(path)) = fill_utf16_buf( + |buf, size| c::GetSystemDirectoryW(buf, size), + |buf| exists(PathBuf::from(OsString::from_wide(buf))), + ) { + return Some(path); + } + #[cfg(not(target_vendor = "uwp"))] + { + if let Ok(Some(path)) = fill_utf16_buf( + |buf, size| c::GetWindowsDirectoryW(buf, size), + |buf| exists(PathBuf::from(OsString::from_wide(buf))), + ) { + return Some(path); + } + } + } + + // 5. Parent paths + if let Some(parent_paths) = parent_paths() { + for path in env::split_paths(&parent_paths).filter(|p| !p.as_os_str().is_empty()) { + if let Some(path) = exists(path) { + return Some(path); + } + } + } + None +} + +/// Checks if a file exists without following symlinks. +fn program_exists(path: &Path) -> Option> { + unsafe { + let path = args::to_user_path(path).ok()?; + // Getting attributes using `GetFileAttributesW` does not follow symlinks + // and it will almost always be successful if the link exists. + // There are some exceptions for special system files (e.g. the pagefile) + // but these are not executable. + if c::GetFileAttributesW(path.as_ptr()) == c::INVALID_FILE_ATTRIBUTES { + None + } else { + Some(path) + } + } +} + +impl Stdio { + fn to_handle(&self, stdio_id: u32, pipe: &mut Option) -> io::Result { + let use_stdio_id = |stdio_id| match stdio::get_handle(stdio_id) { + Ok(io) => unsafe { + let io = Handle::from_raw_handle(io); + let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); + let _ = io.into_raw_handle(); // Don't close the handle + ret + }, + // If no stdio handle is available, then propagate the null value. + Err(..) => unsafe { Ok(Handle::from_raw_handle(ptr::null_mut())) }, + }; + match *self { + Stdio::Inherit => use_stdio_id(stdio_id), + Stdio::InheritSpecific { from_stdio_id } => use_stdio_id(from_stdio_id), + + 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::Pipe(ref source) => { + let ours_readable = stdio_id != c::STD_INPUT_HANDLE; + pipe::spawn_pipe_relay(source, ours_readable, true).map(AnonPipe::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 = size_of::(); + let mut sa = c::SECURITY_ATTRIBUTES { + nLength: size as u32, + 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(r"\\.\NUL"), &opts).map(|file| file.into_inner()) + } + } + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + Stdio::Pipe(pipe) + } +} + +impl From for Stdio { + fn from(pipe: Handle) -> Stdio { + Stdio::Handle(pipe) + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + Stdio::Handle(file.into_inner()) + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + Stdio::InheritSpecific { from_stdio_id: c::STD_OUTPUT_HANDLE } + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + Stdio::InheritSpecific { from_stdio_id: c::STD_ERROR_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, + main_thread_handle: Handle, +} + +impl Process { + pub fn kill(&mut self) -> io::Result<()> { + let result = unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) }; + if result == c::FALSE { + let error = api::get_last_error(); + // TerminateProcess returns ERROR_ACCESS_DENIED if the process has already been + // terminated (by us, or for any other reason). So check if the process was actually + // terminated, and if so, do not return an error. + if error != WinError::ACCESS_DENIED || self.try_wait().is_err() { + return Err(crate::io::Error::from_raw_os_error(error.code as i32)); + } + } + Ok(()) + } + + pub fn id(&self) -> u32 { + unsafe { c::GetProcessId(self.handle.as_raw_handle()) } + } + + pub fn main_thread_handle(&self) -> BorrowedHandle<'_> { + self.main_thread_handle.as_handle() + } + + pub fn wait(&mut self) -> io::Result { + unsafe { + let res = c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE); + if res != c::WAIT_OBJECT_0 { + return Err(Error::last_os_error()); + } + let mut status = 0; + cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?; + Ok(ExitStatus(status)) + } + } + + pub fn try_wait(&mut self) -> io::Result> { + unsafe { + match c::WaitForSingleObject(self.handle.as_raw_handle(), 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.as_raw_handle(), &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, Default)] +pub struct ExitStatus(u32); + +impl ExitStatus { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + match NonZero::::try_from(self.0) { + /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), + /* was zero, couldn't convert */ Err(_) => Ok(()), + } + } + pub fn code(&self) -> Option { + Some(self.0 as i32) + } +} + +/// Converts a raw `u32` to a type-safe `ExitStatus` by wrapping it without copying. +impl From for ExitStatus { + fn from(u: u32) -> ExitStatus { + ExitStatus(u) + } +} + +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 ExitStatusError(NonZero); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option> { + Some((u32::from(self.0) as i32).try_into().unwrap()) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(u32); + +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 + } +} + +impl From for ExitCode { + fn from(code: u8) -> Self { + ExitCode(u32::from(code)) + } +} + +impl From for ExitCode { + fn from(code: u32) -> Self { + ExitCode(u32::from(code)) + } +} + +fn zeroed_startupinfo() -> c::STARTUPINFOW { + c::STARTUPINFOW { + cb: 0, + lpReserved: ptr::null_mut(), + lpDesktop: ptr::null_mut(), + lpTitle: ptr::null_mut(), + dwX: 0, + dwY: 0, + dwXSize: 0, + dwYSize: 0, + dwXCountChars: 0, + dwYCountChars: 0, + dwFillAttribute: 0, + dwFlags: 0, + wShowWindow: 0, + cbReserved2: 0, + lpReserved2: ptr::null_mut(), + hStdInput: ptr::null_mut(), + hStdOutput: ptr::null_mut(), + hStdError: ptr::null_mut(), + } +} + +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(argv0: &OsStr, args: &[Arg], force_quotes: bool) -> 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 to avoid ambiguity when + // the child process parses its arguments. + // Note that quotes aren't escaped here because they can't be used in arg0. + // But that's ok because file paths can't contain quotes. + cmd.push(b'"' as u16); + cmd.extend(argv0.encode_wide()); + cmd.push(b'"' as u16); + + for arg in args { + cmd.push(' ' as u16); + args::append_arg(&mut cmd, arg, force_quotes)?; + } + Ok(cmd) +} + +// Get `cmd.exe` for use with bat scripts, encoded as a UTF-16 string. +fn command_prompt() -> io::Result> { + let mut system: Vec = + fill_utf16_buf(|buf, size| unsafe { c::GetSystemDirectoryW(buf, size) }, |buf| buf.into())?; + system.extend("\\cmd.exe".encode_utf16().chain([0])); + Ok(system) +} + +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(); + + // If there are no environment variables to set then signal this by + // pushing a null. + if env.is_empty() { + blk.push(0); + } + + for (k, v) in env { + ensure_no_nuls(k.os_string)?; + blk.extend(k.utf16); + 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().chain([0]).collect(); + // Try to remove the `\\?\` prefix, if any. + // This is necessary because the current directory does not support verbatim paths. + // However. this can only be done if it doesn't change how the path will be resolved. + let ptr = if dir_str.starts_with(utf16!(r"\\?\UNC")) { + // Turn the `C` in `UNC` into a `\` so we can then use `\\rest\of\path`. + let start = r"\\?\UN".len(); + dir_str[start] = b'\\' as u16; + if path::is_absolute_exact(&dir_str[start..]) { + dir_str[start..].as_ptr() + } else { + // Revert the above change. + dir_str[start] = b'C' as u16; + dir_str.as_ptr() + } + } else if dir_str.starts_with(utf16!(r"\\?\")) { + // Strip the leading `\\?\` + let start = r"\\?\".len(); + if path::is_absolute_exact(&dir_str[start..]) { + dir_str[start..].as_ptr() + } else { + dir_str.as_ptr() + } + } else { + dir_str.as_ptr() + }; + Ok((ptr, dir_str)) + } + None => Ok((ptr::null(), Vec::new())), + } +} + +pub struct CommandArgs<'a> { + iter: crate::slice::Iter<'a, Arg>, +} + +impl<'a> Iterator for CommandArgs<'a> { + type Item = &'a OsStr; + fn next(&mut self) -> Option<&'a OsStr> { + self.iter.next().map(|arg| match arg { + Arg::Regular(s) | Arg::Raw(s) => s.as_ref(), + }) + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +impl<'a> fmt::Debug for CommandArgs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.clone()).finish() + } +} diff --git a/library/std/src/sys/pal/windows/process/tests.rs b/library/std/src/sys/process/windows/tests.rs similarity index 100% rename from library/std/src/sys/pal/windows/process/tests.rs rename to library/std/src/sys/process/windows/tests.rs diff --git a/library/std/src/sys/random/horizon.rs b/library/std/src/sys/random/getrandom.rs similarity index 100% rename from library/std/src/sys/random/horizon.rs rename to library/std/src/sys/random/getrandom.rs diff --git a/library/std/src/sys/random/linux.rs b/library/std/src/sys/random/linux.rs index e3cb79285cd15..18196fae28bee 100644 --- a/library/std/src/sys/random/linux.rs +++ b/library/std/src/sys/random/linux.rs @@ -64,8 +64,8 @@ use crate::fs::File; use crate::io::Read; use crate::os::fd::AsRawFd; use crate::sync::OnceLock; -use crate::sync::atomic::AtomicBool; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sync::atomic::{Atomic, AtomicBool}; use crate::sys::pal::os::errno; use crate::sys::pal::weak::syscall; @@ -73,17 +73,17 @@ fn getrandom(mut bytes: &mut [u8], insecure: bool) { // A weak symbol allows interposition, e.g. for perf measurements that want to // disable randomness for consistency. Otherwise, we'll try a raw syscall. // (`getrandom` was added in glibc 2.25, musl 1.1.20, android API level 28) - syscall! { + syscall!( fn getrandom( buffer: *mut libc::c_void, length: libc::size_t, - flags: libc::c_uint - ) -> libc::ssize_t - } + flags: libc::c_uint, + ) -> libc::ssize_t; + ); - static GETRANDOM_AVAILABLE: AtomicBool = AtomicBool::new(true); - static GRND_INSECURE_AVAILABLE: AtomicBool = AtomicBool::new(true); - static URANDOM_READY: AtomicBool = AtomicBool::new(false); + static GETRANDOM_AVAILABLE: Atomic = AtomicBool::new(true); + static GRND_INSECURE_AVAILABLE: Atomic = AtomicBool::new(true); + static URANDOM_READY: Atomic = AtomicBool::new(false); static DEVICE: OnceLock = OnceLock::new(); if GETRANDOM_AVAILABLE.load(Relaxed) { diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index f42351deb92c0..013e886a99b6b 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -35,10 +35,10 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "hermit")] { mod hermit; pub use hermit::fill_bytes; - } else if #[cfg(target_os = "horizon")] { - // FIXME: add arc4random_buf to shim-3ds - mod horizon; - pub use horizon::fill_bytes; + } else if #[cfg(any(target_os = "horizon", target_os = "cygwin"))] { + // FIXME(horizon): add arc4random_buf to shim-3ds + mod getrandom; + pub use getrandom::fill_bytes; } else if #[cfg(any( target_os = "aix", target_os = "hurd", @@ -60,6 +60,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "teeos")] { mod teeos; pub use teeos::fill_bytes; + } else if #[cfg(target_os = "trusty")] { + mod trusty; + pub use trusty::fill_bytes; } else if #[cfg(target_os = "uefi")] { mod uefi; pub use uefi::fill_bytes; diff --git a/library/std/src/sys/random/trusty.rs b/library/std/src/sys/random/trusty.rs new file mode 100644 index 0000000000000..e4db24695f8bd --- /dev/null +++ b/library/std/src/sys/random/trusty.rs @@ -0,0 +1,7 @@ +unsafe extern "C" { + fn trusty_rng_secure_rand(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t); +} + +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { trusty_rng_secure_rand(bytes.as_mut_ptr().cast(), bytes.len()) } +} diff --git a/library/std/src/sys/random/vxworks.rs b/library/std/src/sys/random/vxworks.rs index d549ccebdb2cd..14f02e8ecd220 100644 --- a/library/std/src/sys/random/vxworks.rs +++ b/library/std/src/sys/random/vxworks.rs @@ -1,7 +1,7 @@ -use crate::sync::atomic::AtomicBool; use crate::sync::atomic::Ordering::Relaxed; +use crate::sync::atomic::{Atomic, AtomicBool}; -static RNG_INIT: AtomicBool = AtomicBool::new(false); +static RNG_INIT: Atomic = AtomicBool::new(false); pub fn fill_bytes(mut bytes: &mut [u8]) { while !RNG_INIT.load(Relaxed) { diff --git a/library/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs new file mode 100644 index 0000000000000..336d4c8527db3 --- /dev/null +++ b/library/std/src/sys/stdio/mod.rs @@ -0,0 +1,41 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +cfg_if::cfg_if! { + if #[cfg(any( + target_family = "unix", + target_os = "hermit" + ))] { + mod unix; + pub use unix::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + pub use sgx::*; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::*; + } else if #[cfg(target_os = "teeos")] { + mod teeos; + pub use teeos::*; + } else if #[cfg(target_os = "trusty")] { + mod trusty; + pub use trusty::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::*; + } else if #[cfg(target_os = "xous")] { + mod xous; + pub use xous::*; + } else if #[cfg(target_os = "zkvm")] { + mod zkvm; + pub use zkvm::*; + } else { + mod unsupported; + pub use unsupported::*; + } +} diff --git a/library/std/src/sys/stdio/sgx.rs b/library/std/src/sys/stdio/sgx.rs new file mode 100644 index 0000000000000..2cf47f4919209 --- /dev/null +++ b/library/std/src/sys/stdio/sgx.rs @@ -0,0 +1,101 @@ +use fortanix_sgx_abi as abi; + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +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)) + } + + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + with_std_fd(abi::FD_STDIN, |fd| fd.read_buf(buf)) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + with_std_fd(abi::FD_STDIN, |fd| fd.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 { + 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()) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + with_std_fd(abi::FD_STDOUT, |fd| fd.write_vectored(bufs)) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } +} + +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()) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + with_std_fd(abi::FD_STDERR, |fd| fd.write_vectored(bufs)) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } +} + +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; + +pub fn is_ebadf(err: &io::Error) -> bool { + // FIXME: Rust normally maps Unix EBADF to `Uncategorized` + err.raw_os_error() == Some(abi::Error::BrokenPipe as _) +} + +pub fn panic_output() -> Option { + crate::sys::pal::abi::panic::SgxPanicOutput::new() +} diff --git a/library/std/src/sys/stdio/solid.rs b/library/std/src/sys/stdio/solid.rs new file mode 100644 index 0000000000000..55daf0b54b928 --- /dev/null +++ b/library/std/src/sys/stdio/solid.rs @@ -0,0 +1,37 @@ +#[expect(dead_code)] +#[path = "unsupported.rs"] +mod unsupported_stdio; + +use crate::io; +use crate::sys::pal::abi; + +pub type Stdin = unsupported_stdio::Stdin; +pub struct Stdout; +pub type Stderr = Stdout; + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) }; + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = unsupported_stdio::STDIN_BUF_SIZE; + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/stdio/teeos.rs b/library/std/src/sys/stdio/teeos.rs new file mode 100644 index 0000000000000..27b3292bf8f00 --- /dev/null +++ b/library/std/src/sys/stdio/teeos.rs @@ -0,0 +1,62 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +#[expect(dead_code)] +#[path = "unsupported.rs"] +mod unsupported_stdio; + +use core::arch::asm; + +use crate::io; + +pub type Stdin = unsupported_stdio::Stdin; +pub struct Stdout; +pub type Stderr = Stdout; + +const KCALL_DEBUG_CMD_PUT_BYTES: i64 = 2; + +unsafe fn debug_call(cap_ref: u64, call_no: i64, arg1: u64, arg2: u64) -> i32 { + let ret: u64; + unsafe { + asm!( + "svc #99", + inout("x0") cap_ref => ret, + in("x1") call_no, + in("x2") arg1, + in("x3") arg2, + ); + } + + ret as i32 +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + // Corresponds to `HM_DEBUG_PUT_BYTES_LIMIT`. + const MAX_LEN: usize = 512; + let len = buf.len().min(MAX_LEN); + let result = + unsafe { debug_call(0, KCALL_DEBUG_CMD_PUT_BYTES, buf.as_ptr() as u64, len as u64) }; + + if result == 0 { Ok(len) } else { Err(io::Error::from(io::ErrorKind::InvalidInput)) } + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = unsupported_stdio::STDIN_BUF_SIZE; + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(libc::EBADF as i32) +} + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/stdio/trusty.rs b/library/std/src/sys/stdio/trusty.rs new file mode 100644 index 0000000000000..e05461aa44a73 --- /dev/null +++ b/library/std/src/sys/stdio/trusty.rs @@ -0,0 +1,92 @@ +#[expect(dead_code)] +#[path = "unsupported.rs"] +mod unsupported_stdio; + +use crate::cmp; +use crate::io::{self, IoSlice}; + +pub type Stdin = unsupported_stdio::Stdin; +pub struct Stdout; +pub struct Stderr; + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + write(libc::STDOUT_FILENO, buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + write_vectored(libc::STDOUT_FILENO, 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 { + write(libc::STDERR_FILENO, buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + write_vectored(libc::STDERR_FILENO, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = unsupported_stdio::STDIN_BUF_SIZE; + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} + +pub fn panic_output() -> Option { + Some(Stderr) +} + +fn write(fd: i32, buf: &[u8]) -> io::Result { + let iov = libc::iovec { iov_base: buf.as_ptr() as *mut _, iov_len: buf.len() }; + // SAFETY: syscall, safe arguments. + let ret = unsafe { libc::writev(fd, &iov, 1) }; + // This check includes ret < 0, since the length is at most isize::MAX. + if ret as usize > iov.iov_len { + return Err(io::Error::last_os_error()); + } + Ok(ret as usize) +} + +fn write_vectored(fd: i32, bufs: &[IoSlice<'_>]) -> io::Result { + let iov = bufs.as_ptr() as *const libc::iovec; + let len = cmp::min(bufs.len(), libc::c_int::MAX as usize) as libc::c_int; + // SAFETY: syscall, safe arguments. + let ret = unsafe { libc::writev(fd, iov, len) }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(ret as usize) +} diff --git a/library/std/src/sys/stdio/uefi.rs b/library/std/src/sys/stdio/uefi.rs new file mode 100644 index 0000000000000..ccd6bf658b0ff --- /dev/null +++ b/library/std/src/sys/stdio/uefi.rs @@ -0,0 +1,223 @@ +use crate::io; +use crate::iter::Iterator; +use crate::mem::MaybeUninit; +use crate::os::uefi; +use crate::ptr::NonNull; + +pub struct Stdin { + surrogate: Option, + incomplete_utf8: IncompleteUtf8, +} + +struct IncompleteUtf8 { + bytes: [u8; 4], + len: u8, +} + +impl IncompleteUtf8 { + pub const fn new() -> IncompleteUtf8 { + IncompleteUtf8 { bytes: [0; 4], len: 0 } + } + + // Implemented for use in Stdin::read. + fn read(&mut self, buf: &mut [u8]) -> usize { + // Write to buffer until the buffer is full or we run out of bytes. + let to_write = crate::cmp::min(buf.len(), self.len as usize); + buf[..to_write].copy_from_slice(&self.bytes[..to_write]); + + // Rotate the remaining bytes if not enough remaining space in buffer. + if usize::from(self.len) > buf.len() { + self.bytes.copy_within(to_write.., 0); + self.len -= to_write as u8; + } else { + self.len = 0; + } + + to_write + } +} + +pub struct Stdout; +pub struct Stderr; + +impl Stdin { + pub const fn new() -> Stdin { + Stdin { surrogate: None, incomplete_utf8: IncompleteUtf8::new() } + } +} + +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + // If there are bytes in the incomplete utf-8, start with those. + // (No-op if there is nothing in the buffer.) + let mut bytes_copied = self.incomplete_utf8.read(buf); + + let stdin: *mut r_efi::protocols::simple_text_input::Protocol = unsafe { + let st: NonNull = uefi::env::system_table().cast(); + (*st.as_ptr()).con_in + }; + + if bytes_copied == buf.len() { + return Ok(bytes_copied); + } + + let ch = simple_text_input_read(stdin)?; + // Only 1 character should be returned. + let mut ch: Vec> = + if let Some(x) = self.surrogate.take() { + char::decode_utf16([x, ch]).collect() + } else { + char::decode_utf16([ch]).collect() + }; + + if ch.len() > 1 { + return Err(io::const_error!(io::ErrorKind::InvalidData, "invalid UTF-16 sequence")); + } + + match ch.pop().unwrap() { + Err(e) => { + self.surrogate = Some(e.unpaired_surrogate()); + } + Ok(x) => { + // This will always be > 0 + let buf_free_count = buf.len() - bytes_copied; + assert!(buf_free_count > 0); + + if buf_free_count >= x.len_utf8() { + // There is enough space in the buffer for the character. + bytes_copied += x.encode_utf8(&mut buf[bytes_copied..]).len(); + } else { + // There is not enough space in the buffer for the character. + // Store the character in the incomplete buffer. + self.incomplete_utf8.len = + x.encode_utf8(&mut self.incomplete_utf8.bytes).len() as u8; + // write partial character to buffer. + bytes_copied += self.incomplete_utf8.read(buf); + } + } + } + + Ok(bytes_copied) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + let st: NonNull = uefi::env::system_table().cast(); + let stdout = unsafe { (*st.as_ptr()).con_out }; + + write(stdout, 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 { + let st: NonNull = uefi::env::system_table().cast(); + let stderr = unsafe { (*st.as_ptr()).std_err }; + + write(stderr, buf) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +// UTF-16 character should occupy 4 bytes at most in UTF-8 +pub const STDIN_BUF_SIZE: usize = 4; + +pub fn is_ebadf(err: &io::Error) -> bool { + if let Some(x) = err.raw_os_error() { + r_efi::efi::Status::UNSUPPORTED.as_usize() == x + } else { + false + } +} + +pub fn panic_output() -> Option { + uefi::env::try_system_table().map(|_| Stderr::new()) +} + +fn write( + protocol: *mut r_efi::protocols::simple_text_output::Protocol, + buf: &[u8], +) -> io::Result { + // Get valid UTF-8 buffer + let utf8 = match crate::str::from_utf8(buf) { + Ok(x) => x, + Err(e) => unsafe { crate::str::from_utf8_unchecked(&buf[..e.valid_up_to()]) }, + }; + + let mut utf16: Vec = utf8.encode_utf16().collect(); + // NULL terminate the string + utf16.push(0); + + unsafe { simple_text_output(protocol, &mut utf16) }?; + + Ok(utf8.len()) +} + +unsafe fn simple_text_output( + protocol: *mut r_efi::protocols::simple_text_output::Protocol, + buf: &mut [u16], +) -> io::Result<()> { + let res = unsafe { ((*protocol).output_string)(protocol, buf.as_mut_ptr()) }; + if res.is_error() { Err(io::Error::from_raw_os_error(res.as_usize())) } else { Ok(()) } +} + +fn simple_text_input_read( + stdin: *mut r_efi::protocols::simple_text_input::Protocol, +) -> io::Result { + loop { + match read_key_stroke(stdin) { + Ok(x) => return Ok(x.unicode_char), + Err(e) if e == r_efi::efi::Status::NOT_READY => wait_stdin(stdin)?, + Err(e) => return Err(io::Error::from_raw_os_error(e.as_usize())), + } + } +} + +fn wait_stdin(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> { + let boot_services: NonNull = + uefi::env::boot_services().unwrap().cast(); + let wait_for_event = unsafe { (*boot_services.as_ptr()).wait_for_event }; + let wait_for_key_event = unsafe { (*stdin).wait_for_key }; + + let r = { + let mut x: usize = 0; + (wait_for_event)(1, [wait_for_key_event].as_mut_ptr(), &mut x) + }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } +} + +fn read_key_stroke( + stdin: *mut r_efi::protocols::simple_text_input::Protocol, +) -> Result { + let mut input_key: MaybeUninit = + MaybeUninit::uninit(); + + let r = unsafe { ((*stdin).read_key_stroke)(stdin, input_key.as_mut_ptr()) }; + + if r.is_error() { + Err(r) + } else { + let input_key = unsafe { input_key.assume_init() }; + Ok(input_key) + } +} diff --git a/library/std/src/sys/stdio/unix.rs b/library/std/src/sys/stdio/unix.rs new file mode 100644 index 0000000000000..8535e3539e9fd --- /dev/null +++ b/library/std/src/sys/stdio/unix.rs @@ -0,0 +1,103 @@ +#[cfg(target_os = "hermit")] +use hermit_abi::{EBADF, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; +#[cfg(target_family = "unix")] +use libc::{EBADF, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::mem::ManuallyDrop; +#[cfg(target_os = "hermit")] +use crate::os::hermit::io::FromRawFd; +#[cfg(target_family = "unix")] +use crate::os::unix::io::FromRawFd; +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 { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(STDIN_FILENO)).read(buf) } + } + + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(STDIN_FILENO)).read_buf(buf) } + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(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 { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(STDOUT_FILENO)).write(buf) } + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(STDOUT_FILENO)).write_vectored(bufs) } + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + 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 { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(STDERR_FILENO)).write(buf) } + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(STDERR_FILENO)).write_vectored(bufs) } + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(EBADF as i32) +} + +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/stdio/unsupported.rs b/library/std/src/sys/stdio/unsupported.rs new file mode 100644 index 0000000000000..177264f5c1042 --- /dev/null +++ b/library/std/src/sys/stdio/unsupported.rs @@ -0,0 +1,106 @@ +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; + +pub struct Stdin; +pub struct Stdout; +pub type Stderr = Stdout; + +impl Stdin { + pub const fn new() -> Stdin { + Stdin + } +} + +impl io::Read for Stdin { + #[inline] + fn read(&mut self, _buf: &mut [u8]) -> io::Result { + Ok(0) + } + + #[inline] + fn read_buf(&mut self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn read_vectored(&mut self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + Ok(0) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + // Do not force `Chain` or `Chain` to use vectored + // reads, unless the other reader is vectored. + false + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } + } + + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + if cursor.capacity() != 0 { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } + } + + #[inline] + fn read_to_end(&mut self, _buf: &mut Vec) -> io::Result { + Ok(0) + } + + #[inline] + fn read_to_string(&mut self, _buf: &mut String) -> io::Result { + Ok(0) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + #[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 write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + // Keep the default write_fmt so the `fmt::Arguments` are still evaluated. + + #[inline] + 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/library/std/src/sys/stdio/wasi.rs b/library/std/src/sys/stdio/wasi.rs new file mode 100644 index 0000000000000..b70efd026f945 --- /dev/null +++ b/library/std/src/sys/stdio/wasi.rs @@ -0,0 +1,117 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::mem::ManuallyDrop; +use crate::os::raw; +use crate::os::wasi::io::{AsRawFd, FromRawFd}; +use crate::sys::fd::WasiFd; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +impl Stdin { + pub const fn new() -> Stdin { + Stdin + } +} + +impl AsRawFd for Stdin { + #[inline] + fn as_raw_fd(&self) -> raw::c_int { + 0 + } +} + +impl io::Read for Stdin { + fn read(&mut self, data: &mut [u8]) -> io::Result { + self.read_vectored(&mut [IoSliceMut::new(data)]) + } + + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).read_buf(buf) + } + + fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result { + ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).read(data) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + true + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl AsRawFd for Stdout { + #[inline] + fn as_raw_fd(&self) -> raw::c_int { + 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_fd(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 + } +} + +impl AsRawFd for Stderr { + #[inline] + fn as_raw_fd(&self) -> raw::c_int { + 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_fd(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::io::DEFAULT_BUF_SIZE; + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(wasi::ERRNO_BADF.raw().into()) +} + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/stdio/windows.rs b/library/std/src/sys/stdio/windows.rs new file mode 100644 index 0000000000000..9b27f76b9dd1a --- /dev/null +++ b/library/std/src/sys/stdio/windows.rs @@ -0,0 +1,472 @@ +#![unstable(issue = "none", feature = "windows_stdio")] + +use core::char::MAX_LEN_UTF8; +use core::str::utf8_char_width; + +use crate::mem::MaybeUninit; +use crate::os::windows::io::{FromRawHandle, IntoRawHandle}; +use crate::sys::handle::Handle; +use crate::sys::pal::api::{self, WinError}; +use crate::sys::{c, cvt}; +use crate::{cmp, io, ptr, str}; + +#[cfg(test)] +mod tests; + +// 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, + incomplete_utf8: IncompleteUtf8, +} + +pub struct Stdout { + incomplete_utf8: IncompleteUtf8, +} + +pub struct Stderr { + incomplete_utf8: IncompleteUtf8, +} + +struct IncompleteUtf8 { + bytes: [u8; 4], + len: u8, +} + +impl IncompleteUtf8 { + // Implemented for use in Stdin::read. + fn read(&mut self, buf: &mut [u8]) -> usize { + // Write to buffer until the buffer is full or we run out of bytes. + let to_write = cmp::min(buf.len(), self.len as usize); + buf[..to_write].copy_from_slice(&self.bytes[..to_write]); + + // Rotate the remaining bytes if not enough remaining space in buffer. + if usize::from(self.len) > buf.len() { + self.bytes.copy_within(to_write.., 0); + self.len -= to_write as u8; + } else { + self.len = 0; + } + + to_write + } +} + +// 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: u32) -> 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 } +} + +/// Returns true if the attached console's code page is currently UTF-8. +#[cfg(not(target_vendor = "win7"))] +fn is_utf8_console() -> bool { + unsafe { c::GetConsoleOutputCP() == c::CP_UTF8 } +} + +#[cfg(target_vendor = "win7")] +fn is_utf8_console() -> bool { + // Windows 7 has a fun "feature" where WriteFile on a console handle will return + // the number of UTF-16 code units written and not the number of bytes from the input string. + // So we always claim the console isn't UTF-8 to trigger the WriteConsole fallback code. + false +} + +fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> io::Result { + if data.is_empty() { + return Ok(0); + } + + let handle = get_handle(handle_id)?; + if !is_console(handle) || is_utf8_console() { + unsafe { + let handle = Handle::from_raw_handle(handle); + let ret = handle.write(data); + let _ = handle.into_raw_handle(); // Don't close the handle + return ret; + } + } else { + write_console_utf16(data, incomplete_utf8, handle) + } +} + +fn write_console_utf16( + data: &[u8], + incomplete_utf8: &mut IncompleteUtf8, + handle: c::HANDLE, +) -> io::Result { + if incomplete_utf8.len > 0 { + assert!( + incomplete_utf8.len < 4, + "Unexpected number of bytes for incomplete UTF-8 codepoint." + ); + if data[0] >> 6 != 0b10 { + // not a continuation byte - reject + incomplete_utf8.len = 0; + return Err(io::const_error!( + io::ErrorKind::InvalidData, + "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", + )); + } + incomplete_utf8.bytes[incomplete_utf8.len as usize] = data[0]; + incomplete_utf8.len += 1; + let char_width = utf8_char_width(incomplete_utf8.bytes[0]); + if (incomplete_utf8.len as usize) < char_width { + // more bytes needed + return Ok(1); + } + let s = str::from_utf8(&incomplete_utf8.bytes[0..incomplete_utf8.len as usize]); + incomplete_utf8.len = 0; + match s { + Ok(s) => { + assert_eq!(char_width, s.len()); + let written = write_valid_utf8_to_console(handle, s)?; + assert_eq!(written, s.len()); // guaranteed by write_valid_utf8_to_console() for single codepoint writes + return Ok(1); + } + Err(_) => { + return Err(io::const_error!( + io::ErrorKind::InvalidData, + "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", + )); + } + } + } + + // As the console is meant for presenting text, we assume bytes of `data` 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. + // If the first byte is invalid it is either first byte of a multi-byte sequence but the + // provided byte slice is too short or it is the first byte of an invalid multi-byte sequence. + 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 => { + let first_byte_char_width = utf8_char_width(data[0]); + if first_byte_char_width > 1 && data.len() < first_byte_char_width { + incomplete_utf8.bytes[0] = data[0]; + incomplete_utf8.len = 1; + return Ok(1); + } else { + return Err(io::const_error!( + 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(), + }; + + write_valid_utf8_to_console(handle, utf8) +} + +fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result { + debug_assert!(!utf8.is_empty()); + + let mut utf16 = [MaybeUninit::::uninit(); MAX_BUFFER_SIZE / 2]; + let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len())]; + + let utf16: &[u16] = unsafe { + // Note that this theoretically checks validity twice in the (most common) case + // where the underlying byte sequence is valid utf-8 (given the check in `write()`). + let result = c::MultiByteToWideChar( + c::CP_UTF8, // CodePage + c::MB_ERR_INVALID_CHARS, // dwFlags + utf8.as_ptr(), // lpMultiByteStr + utf8.len() as i32, // cbMultiByte + utf16.as_mut_ptr() as *mut c::WCHAR, // lpWideCharStr + utf16.len() as i32, // cchWideChar + ); + assert!(result != 0, "Unexpected error in MultiByteToWideChar"); + + // Safety: MultiByteToWideChar initializes `result` values. + utf16[..result as usize].assume_init_ref() + }; + + 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_code_unit_remaining = utf16[written]; + if matches!(first_code_unit_remaining, 0xDCEE..=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 { + debug_assert!(data.len() < u32::MAX as usize); + let mut written = 0; + cvt(unsafe { + c::WriteConsoleW(handle, data.as_ptr(), data.len() as u32, &mut written, ptr::null_mut()) + })?; + Ok(written as usize) +} + +impl Stdin { + pub const fn new() -> Stdin { + Stdin { surrogate: 0, incomplete_utf8: IncompleteUtf8::new() } + } +} + +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) { + unsafe { + let handle = Handle::from_raw_handle(handle); + let ret = handle.read(buf); + let _ = handle.into_raw_handle(); // Don't close the handle + return ret; + } + } + + // If there are bytes in the incomplete utf-8, start with those. + // (No-op if there is nothing in the buffer.) + let mut bytes_copied = self.incomplete_utf8.read(buf); + + if bytes_copied == buf.len() { + Ok(bytes_copied) + } else if buf.len() - bytes_copied < 4 { + // Not enough space to get a UTF-8 byte. We will use the incomplete UTF8. + let mut utf16_buf = [MaybeUninit::new(0); 1]; + // Read one u16 character. + let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, 1, &mut self.surrogate)?; + // Read bytes, using the (now-empty) self.incomplete_utf8 as extra space. + let read_bytes = utf16_to_utf8( + unsafe { utf16_buf[..read].assume_init_ref() }, + &mut self.incomplete_utf8.bytes, + )?; + + // Read in the bytes from incomplete_utf8 until the buffer is full. + self.incomplete_utf8.len = read_bytes as u8; + // No-op if no bytes. + bytes_copied += self.incomplete_utf8.read(&mut buf[bytes_copied..]); + Ok(bytes_copied) + } else { + let mut utf16_buf = [MaybeUninit::::uninit(); MAX_BUFFER_SIZE / 2]; + + // In the worst case, a UTF-8 string can take 3 bytes for every `u16` of a 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)?; + // Safety `read_u16s_fixup_surrogates` returns the number of items + // initialized. + let utf16s = unsafe { utf16_buf[..read].assume_init_ref() }; + match utf16_to_utf8(utf16s, buf) { + Ok(value) => return Ok(bytes_copied + value), + Err(e) => return Err(e), + } + } + } +} + +// 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 might not work if we are not the only reader on Stdin. +fn read_u16s_fixup_surrogates( + handle: c::HANDLE, + buf: &mut [MaybeUninit], + 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] = MaybeUninit::new(*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 { + // Safety: The returned `amount` is the number of values initialized, + // and it is not 0, so we know that `buf[amount - 1]` have been + // initialized. + let last_char = unsafe { buf[amount - 1].assume_init() }; + if matches!(last_char, 0xD800..=0xDBFF) { + // high surrogate + *surrogate = last_char; + amount -= 1; + } + } + Ok(amount) +} + +// Returns `Ok(n)` if it initialized `n` values in `buf`. +fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit]) -> 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: u32 = 1 << CTRL_Z; + let input_control = c::CONSOLE_READCONSOLE_CONTROL { + nLength: size_of::() as u32, + nInitialChars: 0, + dwCtrlWakeupMask: CTRL_Z_MASK, + dwControlKeyState: 0, + }; + + let mut amount = 0; + loop { + cvt(unsafe { + c::SetLastError(0); + c::ReadConsoleW( + handle, + buf.as_mut_ptr() as *mut core::ffi::c_void, + buf.len() as u32, + &mut amount, + &input_control, + ) + })?; + + // ReadConsoleW returns success with ERROR_OPERATION_ABORTED for Ctrl-C or Ctrl-Break. + // Explicitly check for that case here and try again. + if amount == 0 && api::get_last_error() == WinError::OPERATION_ABORTED { + continue; + } + break; + } + // Safety: if `amount > 0`, then that many bytes were written, so + // `buf[amount as usize - 1]` has been initialized. + if amount > 0 && unsafe { buf[amount as usize - 1].assume_init() } == CTRL_Z { + amount -= 1; + } + Ok(amount as usize) +} + +fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { + debug_assert!(utf16.len() <= i32::MAX as usize); + debug_assert!(utf8.len() <= i32::MAX as usize); + + if utf16.is_empty() { + return Ok(0); + } + + let result = unsafe { + c::WideCharToMultiByte( + c::CP_UTF8, // CodePage + c::WC_ERR_INVALID_CHARS, // dwFlags + utf16.as_ptr(), // lpWideCharStr + utf16.len() as i32, // cchWideChar + utf8.as_mut_ptr(), // lpMultiByteStr + utf8.len() as i32, // cbMultiByte + ptr::null(), // lpDefaultChar + ptr::null_mut(), // lpUsedDefaultChar + ) + }; + if result == 0 { + // We can't really do any better than forget all data and return an error. + Err(io::const_error!( + io::ErrorKind::InvalidData, + "Windows stdin in console mode does not support non-UTF-16 input; \ + encountered unpaired surrogate", + )) + } else { + Ok(result as usize) + } +} + +impl IncompleteUtf8 { + pub const fn new() -> IncompleteUtf8 { + IncompleteUtf8 { bytes: [0; MAX_LEN_UTF8], len: 0 } + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout { incomplete_utf8: IncompleteUtf8::new() } + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + write(c::STD_OUTPUT_HANDLE, buf, &mut self.incomplete_utf8) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr { incomplete_utf8: IncompleteUtf8::new() } + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + write(c::STD_ERROR_HANDLE, buf, &mut self.incomplete_utf8) + } + + 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/library/std/src/sys/pal/windows/stdio/tests.rs b/library/std/src/sys/stdio/windows/tests.rs similarity index 100% rename from library/std/src/sys/pal/windows/stdio/tests.rs rename to library/std/src/sys/stdio/windows/tests.rs diff --git a/library/std/src/sys/stdio/xous.rs b/library/std/src/sys/stdio/xous.rs new file mode 100644 index 0000000000000..a92167642b707 --- /dev/null +++ b/library/std/src/sys/stdio/xous.rs @@ -0,0 +1,124 @@ +#[expect(dead_code)] +#[path = "unsupported.rs"] +mod unsupported_stdio; + +use crate::io; +use crate::os::xous::ffi::{Connection, lend, try_lend, try_scalar}; +use crate::os::xous::services::{LogLend, LogScalar, log_server, try_connect}; + +pub type Stdin = unsupported_stdio::Stdin; +pub struct Stdout; +pub struct Stderr; + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + #[repr(C, align(4096))] + struct LendBuffer([u8; 4096]); + let mut lend_buffer = LendBuffer([0u8; 4096]); + let connection = log_server(); + for chunk in buf.chunks(lend_buffer.0.len()) { + for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) { + *dest = *src; + } + lend(connection, LogLend::StandardOutput.into(), &lend_buffer.0, 0, chunk.len()) + .unwrap(); + } + 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 { + #[repr(C, align(4096))] + struct LendBuffer([u8; 4096]); + let mut lend_buffer = LendBuffer([0u8; 4096]); + let connection = log_server(); + for chunk in buf.chunks(lend_buffer.0.len()) { + for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) { + *dest = *src; + } + lend(connection, LogLend::StandardError.into(), &lend_buffer.0, 0, chunk.len()) + .unwrap(); + } + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = unsupported_stdio::STDIN_BUF_SIZE; + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} + +#[derive(Copy, Clone)] +pub struct PanicWriter { + log: Connection, + gfx: Option, +} + +impl io::Write for PanicWriter { + fn write(&mut self, s: &[u8]) -> core::result::Result { + for c in s.chunks(size_of::() * 4) { + // Text is grouped into 4x `usize` words. The id is 1100 plus + // the number of characters in this message. + // Ignore errors since we're already panicking. + try_scalar(self.log, LogScalar::AppendPanicMessage(&c).into()).ok(); + } + + // Serialize the text to the graphics panic handler, only if we were able + // to acquire a connection to it. Text length is encoded in the `valid` field, + // the data itself in the buffer. Typically several messages are require to + // fully transmit the entire panic message. + if let Some(gfx) = self.gfx { + #[repr(C, align(4096))] + struct Request([u8; 4096]); + let mut request = Request([0u8; 4096]); + for (&s, d) in s.iter().zip(request.0.iter_mut()) { + *d = s; + } + try_lend(gfx, 0 /* AppendPanicText */, &request.0, 0, s.len()).ok(); + } + Ok(s.len()) + } + + // Tests show that this does not seem to be reliably called at the end of a panic + // print, so, we can't rely on this to e.g. trigger a graphics update. + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub fn panic_output() -> Option { + // Generally this won't fail because every server has already connected, so + // this is likely to succeed. + let log = log_server(); + + // Send the "We're panicking" message (1000). + try_scalar(log, LogScalar::BeginPanic.into()).ok(); + + // This is will fail in the case that the connection table is full, or if the + // graphics server is not running. Most servers do not already have this connection. + let gfx = try_connect("panic-to-screen!"); + + Some(PanicWriter { log, gfx }) +} diff --git a/library/std/src/sys/stdio/zkvm.rs b/library/std/src/sys/stdio/zkvm.rs new file mode 100644 index 0000000000000..f31c6c26e87cd --- /dev/null +++ b/library/std/src/sys/stdio/zkvm.rs @@ -0,0 +1,72 @@ +use crate::io::{self, BorrowedCursor}; +use crate::sys::pal::abi::{self, fileno}; + +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(unsafe { abi::sys_read(fileno::STDIN, buf.as_mut_ptr(), buf.len()) }) + } + + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { + unsafe { + let n = abi::sys_read(fileno::STDIN, buf.as_mut().as_mut_ptr().cast(), buf.capacity()); + buf.advance_unchecked(n); + } + Ok(()) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + unsafe { abi::sys_write(fileno::STDOUT, buf.as_ptr(), buf.len()) } + + 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 { + unsafe { abi::sys_write(fileno::STDERR, buf.as_ptr(), buf.len()) } + + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/sync/condvar/pthread.rs b/library/std/src/sys/sync/condvar/pthread.rs index 5bb7431eecf0c..938b7071b88a7 100644 --- a/library/std/src/sys/sync/condvar/pthread.rs +++ b/library/std/src/sys/sync/condvar/pthread.rs @@ -2,15 +2,15 @@ use crate::pin::Pin; use crate::ptr; -use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::Relaxed; +use crate::sync::atomic::{Atomic, AtomicUsize}; use crate::sys::pal::sync as pal; use crate::sys::sync::{Mutex, OnceBox}; use crate::time::{Duration, Instant}; pub struct Condvar { cvar: OnceBox, - mutex: AtomicUsize, + mutex: Atomic, } impl Condvar { diff --git a/library/std/src/sys/sync/condvar/xous.rs b/library/std/src/sys/sync/condvar/xous.rs index b9e5f47abfcc2..21a1587214a11 100644 --- a/library/std/src/sys/sync/condvar/xous.rs +++ b/library/std/src/sys/sync/condvar/xous.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicUsize, Ordering}; +use core::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::os::xous::ffi::{blocking_scalar, scalar}; use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; @@ -11,8 +11,8 @@ use crate::time::Duration; const NOTIFY_TRIES: usize = 3; pub struct Condvar { - counter: AtomicUsize, - timed_out: AtomicUsize, + counter: Atomic, + timed_out: Atomic, } unsafe impl Send for Condvar {} diff --git a/library/std/src/sys/sync/mutex/fuchsia.rs b/library/std/src/sys/sync/mutex/fuchsia.rs index 3e871285bea01..cbb1926530f5f 100644 --- a/library/std/src/sys/sync/mutex/fuchsia.rs +++ b/library/std/src/sys/sync/mutex/fuchsia.rs @@ -37,9 +37,9 @@ //! //! [mutex in Fuchsia's libsync]: https://cs.opensource.google/fuchsia/fuchsia/+/main:zircon/system/ulib/sync/mutex.c -use crate::sync::atomic::AtomicU32; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sys::futex::zircon::{ +use crate::sync::atomic::{Atomic, AtomicU32}; +use crate::sys::fuchsia::{ ZX_ERR_BAD_HANDLE, ZX_ERR_BAD_STATE, ZX_ERR_INVALID_ARGS, ZX_ERR_TIMED_OUT, ZX_ERR_WRONG_TYPE, ZX_OK, ZX_TIME_INFINITE, zx_futex_wait, zx_futex_wake_single_owner, zx_handle_t, zx_thread_self, @@ -52,7 +52,7 @@ const CONTESTED_BIT: u32 = 1; const UNLOCKED: u32 = 0; pub struct Mutex { - futex: AtomicU32, + futex: Atomic, } #[inline] @@ -83,13 +83,13 @@ impl Mutex { #[inline] pub fn try_lock(&self) -> bool { - let thread_self = unsafe { zx_thread_self() }; + let thread_self = zx_thread_self(); self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed).is_ok() } #[inline] pub fn lock(&self) { - let thread_self = unsafe { zx_thread_self() }; + let thread_self = zx_thread_self(); if let Err(state) = self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed) { diff --git a/library/std/src/sys/sync/mutex/xous.rs b/library/std/src/sys/sync/mutex/xous.rs index c6b954c1711e6..d16faa5aea319 100644 --- a/library/std/src/sys/sync/mutex/xous.rs +++ b/library/std/src/sys/sync/mutex/xous.rs @@ -1,7 +1,7 @@ use crate::os::xous::ffi::{blocking_scalar, do_yield}; use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::atomic::{AtomicBool, AtomicUsize}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize}; pub struct Mutex { /// The "locked" value indicates how many threads are waiting on this @@ -14,12 +14,12 @@ pub struct Mutex { /// for a lock, or it is locked for long periods of time. Rather than /// spinning, these locks send a Message to the ticktimer server /// requesting that they be woken up when a lock is unlocked. - locked: AtomicUsize, + locked: Atomic, /// Whether this Mutex ever was contended, and therefore made a trip /// to the ticktimer server. If this was never set, then we were never /// on the slow path and can skip deregistering the mutex. - contended: AtomicBool, + contended: Atomic, } impl Mutex { diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index fde1e0ca51029..6a2ab0dcf1b33 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -57,7 +57,7 @@ use crate::cell::Cell; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Release}; -use crate::sync::atomic::{AtomicBool, AtomicPtr}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr}; use crate::sync::poison::once::ExclusiveState; use crate::thread::{self, Thread}; use crate::{fmt, ptr, sync as public}; @@ -65,7 +65,7 @@ use crate::{fmt, ptr, sync as public}; type StateAndQueue = *mut (); pub struct Once { - state_and_queue: AtomicPtr<()>, + state_and_queue: Atomic<*mut ()>, } pub struct OnceState { @@ -94,7 +94,7 @@ const QUEUE_MASK: usize = !STATE_MASK; #[repr(align(4))] // Ensure the two lower bits are free to use as state bits. struct Waiter { thread: Thread, - signaled: AtomicBool, + signaled: Atomic, next: Cell<*const Waiter>, } @@ -102,7 +102,7 @@ struct Waiter { // Every node is a struct on the stack of a waiting thread. // Will wake up the waiters when it gets dropped, i.e. also on panic. struct WaiterQueue<'a> { - state_and_queue: &'a AtomicPtr<()>, + state_and_queue: &'a Atomic<*mut ()>, set_state_on_drop_to: StateAndQueue, } @@ -232,7 +232,7 @@ impl Once { } fn wait( - state_and_queue: &AtomicPtr<()>, + state_and_queue: &Atomic<*mut ()>, mut current: StateAndQueue, return_on_poisoned: bool, ) -> StateAndQueue { diff --git a/library/std/src/sys/sync/once_box.rs b/library/std/src/sys/sync/once_box.rs index 6953b91999ad1..088f51aae78e6 100644 --- a/library/std/src/sys/sync/once_box.rs +++ b/library/std/src/sys/sync/once_box.rs @@ -8,11 +8,11 @@ use crate::mem::replace; use crate::pin::Pin; use crate::ptr::null_mut; -use crate::sync::atomic::AtomicPtr; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sync::atomic::{Atomic, AtomicPtr}; pub(crate) struct OnceBox { - ptr: AtomicPtr, + ptr: Atomic<*mut T>, } impl OnceBox { diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs index bd15f8ee952c9..62f084acfd259 100644 --- a/library/std/src/sys/sync/rwlock/queue.rs +++ b/library/std/src/sys/sync/rwlock/queue.rs @@ -117,11 +117,11 @@ use crate::hint::spin_loop; use crate::mem; use crate::ptr::{self, NonNull, null_mut, without_provenance_mut}; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release}; -use crate::sync::atomic::{AtomicBool, AtomicPtr}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr}; use crate::thread::{self, Thread}; /// The atomic lock state. -type AtomicState = AtomicPtr<()>; +type AtomicState = Atomic; /// The inner lock state. type State = *mut (); @@ -181,11 +181,11 @@ struct Node { tail: AtomicLink, write: bool, thread: OnceCell, - completed: AtomicBool, + completed: Atomic, } /// An atomic node pointer with relaxed operations. -struct AtomicLink(AtomicPtr); +struct AtomicLink(Atomic<*mut Node>); impl AtomicLink { fn new(v: Option>) -> AtomicLink { diff --git a/library/std/src/sys/sync/thread_parking/darwin.rs b/library/std/src/sys/sync/thread_parking/darwin.rs index a0d24a91e7c69..b9bcc538c65ab 100644 --- a/library/std/src/sys/sync/thread_parking/darwin.rs +++ b/library/std/src/sys/sync/thread_parking/darwin.rs @@ -13,8 +13,8 @@ #![allow(non_camel_case_types)] use crate::pin::Pin; -use crate::sync::atomic::AtomicI8; use crate::sync::atomic::Ordering::{Acquire, Release}; +use crate::sync::atomic::{Atomic, AtomicI8}; use crate::time::Duration; type dispatch_semaphore_t = *mut crate::ffi::c_void; @@ -38,7 +38,7 @@ const PARKED: i8 = -1; pub struct Parker { semaphore: dispatch_semaphore_t, - state: AtomicI8, + state: Atomic, } unsafe impl Sync for Parker {} diff --git a/library/std/src/sys/sync/thread_parking/id.rs b/library/std/src/sys/sync/thread_parking/id.rs index 6496435183770..fcc6ecca62867 100644 --- a/library/std/src/sys/sync/thread_parking/id.rs +++ b/library/std/src/sys/sync/thread_parking/id.rs @@ -10,12 +10,12 @@ use crate::cell::UnsafeCell; use crate::pin::Pin; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::atomic::{AtomicI8, fence}; +use crate::sync::atomic::{Atomic, AtomicI8, fence}; use crate::sys::thread_parking::{ThreadId, current, park, park_timeout, unpark}; use crate::time::Duration; pub struct Parker { - state: AtomicI8, + state: Atomic, tid: UnsafeCell>, } diff --git a/library/std/src/sys/sync/thread_parking/pthread.rs b/library/std/src/sys/sync/thread_parking/pthread.rs index 19cabd7dd75c8..14bc793c15de2 100644 --- a/library/std/src/sys/sync/thread_parking/pthread.rs +++ b/library/std/src/sys/sync/thread_parking/pthread.rs @@ -1,8 +1,8 @@ //! Thread parking without `futex` using the `pthread` synchronization primitives. use crate::pin::Pin; -use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sync::atomic::{Atomic, AtomicUsize}; use crate::sys::pal::sync::{Condvar, Mutex}; use crate::time::Duration; @@ -11,7 +11,7 @@ const PARKED: usize = 1; const NOTIFIED: usize = 2; pub struct Parker { - state: AtomicUsize, + state: Atomic, lock: Mutex, cvar: Condvar, } diff --git a/library/std/src/sys/sync/thread_parking/windows7.rs b/library/std/src/sys/sync/thread_parking/windows7.rs index a1a0f8427cd83..96e94a8053c4c 100644 --- a/library/std/src/sys/sync/thread_parking/windows7.rs +++ b/library/std/src/sys/sync/thread_parking/windows7.rs @@ -60,13 +60,13 @@ use core::ffi::c_void; use crate::pin::Pin; -use crate::sync::atomic::AtomicI8; use crate::sync::atomic::Ordering::{Acquire, Release}; +use crate::sync::atomic::{Atomic, AtomicI8}; use crate::sys::{c, dur2timeout}; use crate::time::Duration; pub struct Parker { - state: AtomicI8, + state: Atomic, } const PARKED: i8 = -1; @@ -186,8 +186,8 @@ impl Parker { mod keyed_events { use core::pin::Pin; use core::ptr; - use core::sync::atomic::AtomicPtr; use core::sync::atomic::Ordering::{Acquire, Relaxed}; + use core::sync::atomic::{Atomic, AtomicPtr}; use core::time::Duration; use super::{EMPTY, NOTIFIED, Parker}; @@ -244,7 +244,7 @@ mod keyed_events { fn keyed_event_handle() -> c::HANDLE { const INVALID: c::HANDLE = ptr::without_provenance_mut(!0); - static HANDLE: AtomicPtr = AtomicPtr::new(INVALID); + static HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(INVALID); match HANDLE.load(Relaxed) { INVALID => { let mut handle = c::INVALID_HANDLE_VALUE; diff --git a/library/std/src/sys/sync/thread_parking/xous.rs b/library/std/src/sys/sync/thread_parking/xous.rs index 28c90249dc2c2..0f451c0ac29f9 100644 --- a/library/std/src/sys/sync/thread_parking/xous.rs +++ b/library/std/src/sys/sync/thread_parking/xous.rs @@ -2,8 +2,8 @@ use crate::os::xous::ffi::{blocking_scalar, scalar}; use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; use crate::pin::Pin; use crate::ptr; -use crate::sync::atomic::AtomicI8; use crate::sync::atomic::Ordering::{Acquire, Release}; +use crate::sync::atomic::{Atomic, AtomicI8}; use crate::time::Duration; const NOTIFIED: i8 = 1; @@ -11,7 +11,7 @@ const EMPTY: i8 = 0; const PARKED: i8 = -1; pub struct Parker { - state: AtomicI8, + state: Atomic, } impl Parker { diff --git a/library/std/src/sys/thread_local/destructors/linux_like.rs b/library/std/src/sys/thread_local/destructors/linux_like.rs index 817941229eefe..d7cbaeb89f42e 100644 --- a/library/std/src/sys/thread_local/destructors/linux_like.rs +++ b/library/std/src/sys/thread_local/destructors/linux_like.rs @@ -12,9 +12,6 @@ use crate::mem::transmute; -// FIXME: The Rust compiler currently omits weakly function definitions (i.e., -// __cxa_thread_atexit_impl) and its metadata from LLVM IR. -#[no_sanitize(cfi, kcfi)] pub unsafe fn register(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { /// This is necessary because the __cxa_thread_atexit_impl implementation /// std links to by default may be a C or C++ implementation that was not diff --git a/library/std/src/sys/thread_local/key/racy.rs b/library/std/src/sys/thread_local/key/racy.rs index e1bc08eabb358..a12ff7ac36ba5 100644 --- a/library/std/src/sys/thread_local/key/racy.rs +++ b/library/std/src/sys/thread_local/key/racy.rs @@ -6,7 +6,7 @@ //! should be more lightweight and avoids circular dependencies with the rest of //! `std`. -use crate::sync::atomic::{self, AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; /// A type for TLS keys that are statically allocated. /// @@ -14,7 +14,7 @@ use crate::sync::atomic::{self, AtomicUsize, Ordering}; /// dependencies with the rest of `std`. pub struct LazyKey { /// Inner static TLS key (internals). - key: AtomicUsize, + key: Atomic, /// Destructor for the TLS value. dtor: Option, } @@ -31,7 +31,7 @@ const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1; impl LazyKey { pub const fn new(dtor: Option) -> LazyKey { - LazyKey { key: atomic::AtomicUsize::new(KEY_SENTVAL), dtor } + LazyKey { key: AtomicUsize::new(KEY_SENTVAL), dtor } } #[inline] diff --git a/library/std/src/sys/thread_local/key/windows.rs b/library/std/src/sys/thread_local/key/windows.rs index f4e0f25a476ee..c34c7bc204fd4 100644 --- a/library/std/src/sys/thread_local/key/windows.rs +++ b/library/std/src/sys/thread_local/key/windows.rs @@ -27,7 +27,7 @@ use crate::cell::UnsafeCell; use crate::ptr; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release}; -use crate::sync::atomic::{AtomicPtr, AtomicU32}; +use crate::sync::atomic::{Atomic, AtomicPtr, AtomicU32}; use crate::sys::c; use crate::sys::thread_local::guard; @@ -38,9 +38,9 @@ pub struct LazyKey { /// The key value shifted up by one. Since TLS_OUT_OF_INDEXES == u32::MAX /// is not a valid key value, this allows us to use zero as sentinel value /// without risking overflow. - key: AtomicU32, + key: Atomic, dtor: Option, - next: AtomicPtr, + next: Atomic<*mut LazyKey>, /// Currently, destructors cannot be unregistered, so we cannot use racy /// initialization for keys. Instead, we need synchronize initialization. /// Use the Windows-provided `Once` since it does not require TLS. @@ -142,7 +142,7 @@ pub unsafe fn get(key: Key) -> *mut u8 { unsafe { c::TlsGetValue(key).cast() } } -static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); +static DTORS: Atomic<*mut LazyKey> = AtomicPtr::new(ptr::null_mut()); /// Should only be called once per key, otherwise loops or breaks may occur in /// the linked list. diff --git a/library/std/src/sys/thread_local/key/xous.rs b/library/std/src/sys/thread_local/key/xous.rs index 55ac5b20e1ab0..a27cec5ca1a60 100644 --- a/library/std/src/sys/thread_local/key/xous.rs +++ b/library/std/src/sys/thread_local/key/xous.rs @@ -42,7 +42,7 @@ use crate::mem::ManuallyDrop; use crate::os::xous::ffi::{MemoryFlags, map_memory, unmap_memory}; use crate::ptr; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::atomic::{AtomicPtr, AtomicUsize}; +use crate::sync::atomic::{Atomic, AtomicPtr, AtomicUsize}; pub type Key = usize; pub type Dtor = unsafe extern "C" fn(*mut u8); @@ -52,19 +52,19 @@ const TLS_MEMORY_SIZE: usize = 4096; /// TLS keys start at `1`. Index `0` is unused #[cfg(not(test))] #[unsafe(export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE")] -static TLS_KEY_INDEX: AtomicUsize = AtomicUsize::new(1); +static TLS_KEY_INDEX: Atomic = AtomicUsize::new(1); #[cfg(not(test))] #[unsafe(export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key9DTORSE")] -static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); +static DTORS: Atomic<*mut Node> = AtomicPtr::new(ptr::null_mut()); #[cfg(test)] unsafe extern "Rust" { #[link_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE"] - static TLS_KEY_INDEX: AtomicUsize; + static TLS_KEY_INDEX: Atomic; #[link_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key9DTORSE"] - static DTORS: AtomicPtr; + static DTORS: Atomic<*mut Node>; } fn tls_ptr_addr() -> *mut *mut u8 { @@ -85,7 +85,7 @@ fn tls_table() -> &'static mut [*mut u8] { if !tp.is_null() { return unsafe { - core::slice::from_raw_parts_mut(tp, TLS_MEMORY_SIZE / core::mem::size_of::<*mut u8>()) + core::slice::from_raw_parts_mut(tp, TLS_MEMORY_SIZE / size_of::<*mut u8>()) }; } // If the TP register is `0`, then this thread hasn't initialized @@ -94,7 +94,7 @@ fn tls_table() -> &'static mut [*mut u8] { map_memory( None, None, - TLS_MEMORY_SIZE / core::mem::size_of::<*mut u8>(), + TLS_MEMORY_SIZE / size_of::<*mut u8>(), MemoryFlags::R | MemoryFlags::W, ) .expect("Unable to allocate memory for thread local storage") @@ -177,11 +177,8 @@ pub unsafe fn destroy_tls() { // Finally, free the TLS array unsafe { - unmap_memory(core::slice::from_raw_parts_mut( - tp, - TLS_MEMORY_SIZE / core::mem::size_of::(), - )) - .unwrap() + unmap_memory(core::slice::from_raw_parts_mut(tp, TLS_MEMORY_SIZE / size_of::())) + .unwrap() }; } diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index f0a13323ec93f..9fafac3aa5b41 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -28,10 +28,11 @@ cfg_if::cfg_if! { all(target_family = "wasm", not(target_feature = "atomics")), target_os = "uefi", target_os = "zkvm", + target_os = "trusty", ))] { - mod statik; - pub use statik::{EagerStorage, LazyStorage, thread_local_inner}; - pub(crate) use statik::{LocalPointer, local_pointer}; + mod no_threads; + pub use no_threads::{EagerStorage, LazyStorage, thread_local_inner}; + pub(crate) use no_threads::{LocalPointer, local_pointer}; } else if #[cfg(target_thread_local)] { mod native; pub use native::{EagerStorage, LazyStorage, thread_local_inner}; @@ -91,6 +92,7 @@ pub(crate) mod guard { )), target_os = "uefi", target_os = "zkvm", + target_os = "trusty", ))] { pub(crate) fn enable() { // FIXME: Right now there is no concept of "thread exit" on @@ -136,6 +138,7 @@ pub(crate) mod key { not(target_family = "wasm"), target_family = "unix", ), + all(not(target_thread_local), target_vendor = "apple"), target_os = "teeos", all(target_os = "wasi", target_env = "p1", target_feature = "atomics"), ))] { diff --git a/library/std/src/sys/thread_local/statik.rs b/library/std/src/sys/thread_local/no_threads.rs similarity index 100% rename from library/std/src/sys/thread_local/statik.rs rename to library/std/src/sys/thread_local/no_threads.rs diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 4dc67d26bd8ff..b7f4656fa3701 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -20,8 +20,6 @@ #[cfg(test)] mod tests; -pub mod fs; -pub mod process; pub mod wstr; pub mod wtf8; diff --git a/library/std/src/sys_common/process.rs b/library/std/src/sys_common/process.rs deleted file mode 100644 index 9f61d69d85875..0000000000000 --- a/library/std/src/sys_common/process.rs +++ /dev/null @@ -1,153 +0,0 @@ -#![allow(dead_code)] -#![unstable(feature = "process_internals", issue = "none")] - -use crate::collections::BTreeMap; -use crate::ffi::{OsStr, OsString}; -use crate::sys::pipe::read2; -use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes}; -use crate::{env, fmt, io}; - -// Stores a set of changes to an environment -#[derive(Clone, Default)] -pub struct CommandEnv { - clear: bool, - saw_path: bool, - vars: BTreeMap>, -} - -impl fmt::Debug for CommandEnv { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut debug_command_env = f.debug_struct("CommandEnv"); - debug_command_env.field("clear", &self.clear).field("vars", &self.vars); - debug_command_env.finish() - } -} - -impl CommandEnv { - // Capture the current environment with these changes applied - pub fn capture(&self) -> BTreeMap { - let mut result = BTreeMap::::new(); - if !self.clear { - for (k, v) in env::vars_os() { - result.insert(k.into(), v); - } - } - for (k, maybe_v) in &self.vars { - if let &Some(ref v) = maybe_v { - result.insert(k.clone(), v.clone()); - } else { - result.remove(k); - } - } - result - } - - pub fn is_unchanged(&self) -> bool { - !self.clear && self.vars.is_empty() - } - - pub fn capture_if_changed(&self) -> Option> { - if self.is_unchanged() { None } else { Some(self.capture()) } - } - - // The following functions build up changes - pub fn set(&mut self, key: &OsStr, value: &OsStr) { - let key = EnvKey::from(key); - self.maybe_saw_path(&key); - self.vars.insert(key, Some(value.to_owned())); - } - - pub fn remove(&mut self, key: &OsStr) { - let key = EnvKey::from(key); - self.maybe_saw_path(&key); - if self.clear { - self.vars.remove(&key); - } else { - self.vars.insert(key, None); - } - } - - pub fn clear(&mut self) { - self.clear = true; - self.vars.clear(); - } - - pub fn does_clear(&self) -> bool { - self.clear - } - - pub fn have_changed_path(&self) -> bool { - self.saw_path || self.clear - } - - fn maybe_saw_path(&mut self, key: &EnvKey) { - if !self.saw_path && key == "PATH" { - self.saw_path = true; - } - } - - pub fn iter(&self) -> CommandEnvs<'_> { - let iter = self.vars.iter(); - CommandEnvs { iter } - } -} - -/// An iterator over the command environment variables. -/// -/// This struct is created by -/// [`Command::get_envs`][crate::process::Command::get_envs]. See its -/// documentation for more. -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "command_access", since = "1.57.0")] -#[derive(Debug)] -pub struct CommandEnvs<'a> { - iter: crate::collections::btree_map::Iter<'a, EnvKey, Option>, -} - -#[stable(feature = "command_access", since = "1.57.0")] -impl<'a> Iterator for CommandEnvs<'a> { - type Item = (&'a OsStr, Option<&'a OsStr>); - fn next(&mut self) -> Option { - self.iter.next().map(|(key, value)| (key.as_ref(), value.as_deref())) - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[stable(feature = "command_access", since = "1.57.0")] -impl<'a> ExactSizeIterator for CommandEnvs<'a> { - fn len(&self) -> usize { - self.iter.len() - } - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -pub fn wait_with_output( - mut process: Process, - mut pipes: StdioPipes, -) -> io::Result<(ExitStatus, Vec, Vec)> { - drop(pipes.stdin.take()); - - let (mut stdout, mut stderr) = (Vec::new(), Vec::new()); - match (pipes.stdout.take(), pipes.stderr.take()) { - (None, None) => {} - (Some(out), None) => { - let res = out.read_to_end(&mut stdout); - res.unwrap(); - } - (None, Some(err)) => { - let res = err.read_to_end(&mut stderr); - res.unwrap(); - } - (Some(out), Some(err)) => { - let res = read2(out, &mut stdout, err, &mut stderr); - res.unwrap(); - } - } - - let status = process.wait()?; - Ok((status, stdout, stderr)) -} diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index d5a5d10205dd8..7cd448733130d 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -22,12 +22,16 @@ use crate::fmt; /// /// Initialization is dynamically performed on the first call to a setter (e.g. /// [`with`]) within a thread, and values that implement [`Drop`] get -/// destructed when a thread exits. Some caveats apply, which are explained below. +/// destructed when a thread exits. Some platform-specific caveats apply, which +/// are explained below. +/// Note that, should the destructor panics, the whole process will be [aborted]. /// /// A `LocalKey`'s initializer cannot recursively depend on itself. Using a /// `LocalKey` in this way may cause panics, aborts or infinite recursion on /// the first call to `with`. /// +/// [aborted]: crate::process::abort +/// /// # Single-thread Synchronization /// /// Though there is no potential race with other threads, it is still possible to diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 3f3ba02361cc8..26b2fb4472436 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -166,7 +166,7 @@ use crate::mem::{self, ManuallyDrop, forget}; use crate::num::NonZero; use crate::pin::Pin; use crate::sync::Arc; -use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::sys::sync::Parker; use crate::sys::thread as imp; use crate::sys_common::{AsInner, IntoInner}; @@ -481,7 +481,7 @@ impl Builder { let Builder { name, stack_size, no_hooks } = self; let stack_size = stack_size.unwrap_or_else(|| { - static MIN: AtomicUsize = AtomicUsize::new(0); + static MIN: Atomic = AtomicUsize::new(0); match MIN.load(Ordering::Relaxed) { 0 => {} @@ -1195,9 +1195,9 @@ impl ThreadId { cfg_if::cfg_if! { if #[cfg(target_has_atomic = "64")] { - use crate::sync::atomic::AtomicU64; + use crate::sync::atomic::{Atomic, AtomicU64}; - static COUNTER: AtomicU64 = AtomicU64::new(0); + static COUNTER: Atomic = AtomicU64::new(0); let mut last = COUNTER.load(Ordering::Relaxed); loop { @@ -1302,10 +1302,10 @@ pub(crate) mod main_thread { cfg_if::cfg_if! { if #[cfg(target_has_atomic = "64")] { use super::ThreadId; - use crate::sync::atomic::AtomicU64; + use crate::sync::atomic::{Atomic, AtomicU64}; use crate::sync::atomic::Ordering::Relaxed; - static MAIN: AtomicU64 = AtomicU64::new(0); + static MAIN: Atomic = AtomicU64::new(0); pub(super) fn get() -> Option { ThreadId::from_u64(MAIN.load(Relaxed)) @@ -1319,10 +1319,10 @@ pub(crate) mod main_thread { } else { use super::ThreadId; use crate::mem::MaybeUninit; - use crate::sync::atomic::AtomicBool; + use crate::sync::atomic::{Atomic, AtomicBool}; use crate::sync::atomic::Ordering::{Acquire, Release}; - static INIT: AtomicBool = AtomicBool::new(false); + static INIT: Atomic = AtomicBool::new(false); static mut MAIN: MaybeUninit = MaybeUninit::uninit(); pub(super) fn get() -> Option { @@ -1676,6 +1676,7 @@ impl fmt::Debug for Thread { /// [`Result`]: crate::result::Result /// [`std::panic::resume_unwind`]: crate::panic::resume_unwind #[stable(feature = "rust1", since = "1.0.0")] +#[doc(search_unbox)] pub type Result = crate::result::Result>; // This packet is used to communicate the return value between the spawned diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 0033fc3a73283..a4c0ca5417d00 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -2,7 +2,7 @@ use super::{Builder, JoinInner, Result, Thread, current_or_unnamed}; use crate::marker::PhantomData; use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; use crate::sync::Arc; -use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering}; use crate::{fmt, io}; /// A scope to spawn scoped threads in. @@ -35,8 +35,8 @@ pub struct Scope<'scope, 'env: 'scope> { pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>); pub(super) struct ScopeData { - num_running_threads: AtomicUsize, - a_thread_panicked: AtomicBool, + num_running_threads: Atomic, + a_thread_panicked: Atomic, main_thread: Thread, } diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index 99b5ad9cb9fe5..98f471ad54b2e 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -113,18 +113,23 @@ where pub(super) fn run_spawn_hooks(thread: &Thread) -> ChildSpawnHooks { // Get a snapshot of the spawn hooks. // (Increments the refcount to the first node.) - let hooks = SPAWN_HOOKS.with(|hooks| { + if let Ok(hooks) = SPAWN_HOOKS.try_with(|hooks| { let snapshot = hooks.take(); hooks.set(snapshot.clone()); snapshot - }); - // Iterate over the hooks, run them, and collect the results in a vector. - let to_run: Vec<_> = iter::successors(hooks.first.as_deref(), |hook| hook.next.as_deref()) - .map(|hook| (hook.hook)(thread)) - .collect(); - // Pass on the snapshot of the hooks and the results to the new thread, - // which will then run SpawnHookResults::run(). - ChildSpawnHooks { hooks, to_run } + }) { + // Iterate over the hooks, run them, and collect the results in a vector. + let to_run: Vec<_> = iter::successors(hooks.first.as_deref(), |hook| hook.next.as_deref()) + .map(|hook| (hook.hook)(thread)) + .collect(); + // Pass on the snapshot of the hooks and the results to the new thread, + // which will then run SpawnHookResults::run(). + ChildSpawnHooks { hooks, to_run } + } else { + // TLS has been destroyed. Skip running the hooks. + // See https://github.com/rust-lang/rust/issues/138696 + ChildSpawnHooks::default() + } } /// The results of running the spawn hooks. diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index ff45e82bd9c71..59ec48a57d1c6 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -1,12 +1,12 @@ use super::Builder; use crate::any::Any; use crate::panic::panic_any; +use crate::result; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::mpsc::{Sender, channel}; use crate::sync::{Arc, Barrier}; use crate::thread::{self, Scope, ThreadId}; use crate::time::{Duration, Instant}; -use crate::{mem, result}; // !!! These tests are dangerous. If something is buggy, they will hang, !!! // !!! instead of exiting cleanly. This might wedge the buildbots. !!! @@ -108,6 +108,7 @@ fn test_is_finished() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_join_panic() { match thread::spawn(move || panic!()).join() { result::Result::Err(_) => (), @@ -210,6 +211,7 @@ fn test_simple_newsched_spawn() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_try_panic_message_string_literal() { match thread::spawn(move || { panic!("static string"); @@ -226,6 +228,7 @@ fn test_try_panic_message_string_literal() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_try_panic_any_message_owned_str() { match thread::spawn(move || { panic_any("owned string".to_string()); @@ -242,6 +245,7 @@ fn test_try_panic_any_message_owned_str() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_try_panic_any_message_any() { match thread::spawn(move || { panic_any(Box::new(413u16) as Box); @@ -260,6 +264,7 @@ fn test_try_panic_any_message_any() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_try_panic_any_message_unit_struct() { struct Juju; @@ -327,7 +332,7 @@ fn sleep_ms_smoke() { #[test] fn test_size_of_option_thread_id() { - assert_eq!(mem::size_of::>(), mem::size_of::()); + assert_eq!(size_of::>(), size_of::()); } #[test] diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 5ab71413586dc..03af35e809c91 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -205,8 +205,8 @@ pub struct Instant(time::Instant); /// println!("{}", elapsed.as_secs()); /// } /// Err(e) => { -/// // an error occurred! -/// println!("Error: {e:?}"); +/// // the system clock went backwards! +/// println!("Great Scott! {e:?}"); /// } /// } /// } @@ -245,6 +245,7 @@ pub struct Instant(time::Instant); /// > structure cannot represent the new point in time. /// /// [`add`]: SystemTime::add +/// [`UNIX_EPOCH`]: SystemTime::UNIX_EPOCH #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[stable(feature = "time2", since = "1.8.0")] pub struct SystemTime(time::SystemTime); diff --git a/library/std/tests/floats/f128.rs b/library/std/tests/floats/f128.rs index d0e8b157e6b6f..e7c90faa05c23 100644 --- a/library/std/tests/floats/f128.rs +++ b/library/std/tests/floats/f128.rs @@ -1,48 +1,27 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy -#![cfg(reliable_f128)] +#![cfg(target_has_reliable_f128)] use std::f128::consts; -use std::num::FpCategory as Fp; -#[cfg(reliable_f128_math)] -use std::ops::Rem; use std::ops::{Add, Div, Mul, Sub}; // Note these tolerances make sense around zero, but not for more extreme exponents. -/// For operations that are near exact, usually not involving math of different -/// signs. -const TOL_PRECISE: f128 = 1e-28; - /// Default tolerances. Works for values that should be near precise but not exact. Roughly /// the precision carried by `100 * 100`. +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] const TOL: f128 = 1e-12; +/// For operations that are near exact, usually not involving math of different +/// signs. +const TOL_PRECISE: f128 = 1e-28; + /// Tolerances for math that is allowed to be imprecise, usually due to multiple chained /// operations. -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] const TOL_IMPR: f128 = 1e-10; -/// Smallest number -const TINY_BITS: u128 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u128 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u128 = 0x7ffefffffffffffffffffffffffffffe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u128 = 0x0000ffffffffffffffffffffffffffff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u128 = 0x00010000000000000000000000000000; - -/// First pattern over the mantissa -const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u128 = 0x00005555555555555555555555555555; - /// Compare by representation #[allow(unused_macros)] macro_rules! assert_f128_biteq { @@ -66,438 +45,11 @@ fn test_num_f128() { assert_eq!(ten.div(two), ten / two); } -#[test] -#[cfg(reliable_f128_math)] -fn test_num_f128_rem() { - let ten = 10f128; - let two = 2f128; - assert_eq!(ten.rem(two), ten % two); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_min_nan() { - assert_eq!(f128::NAN.min(2.0), 2.0); - assert_eq!(2.0f128.min(f128::NAN), 2.0); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_max_nan() { - assert_eq!(f128::NAN.max(2.0), 2.0); - assert_eq!(2.0f128.max(f128::NAN), 2.0); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_minimum() { - assert!(f128::NAN.minimum(2.0).is_nan()); - assert!(2.0f128.minimum(f128::NAN).is_nan()); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_maximum() { - assert!(f128::NAN.maximum(2.0).is_nan()); - assert!(2.0f128.maximum(f128::NAN).is_nan()); -} - -#[test] -fn test_nan() { - let nan: f128 = f128::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert!(!nan.is_normal()); - assert_eq!(Fp::Nan, nan.classify()); -} - -#[test] -fn test_infinity() { - let inf: f128 = f128::INFINITY; - assert!(inf.is_infinite()); - assert!(!inf.is_finite()); - assert!(inf.is_sign_positive()); - assert!(!inf.is_sign_negative()); - assert!(!inf.is_nan()); - assert!(!inf.is_normal()); - assert_eq!(Fp::Infinite, inf.classify()); -} - -#[test] -fn test_neg_infinity() { - let neg_inf: f128 = f128::NEG_INFINITY; - assert!(neg_inf.is_infinite()); - assert!(!neg_inf.is_finite()); - assert!(!neg_inf.is_sign_positive()); - assert!(neg_inf.is_sign_negative()); - assert!(!neg_inf.is_nan()); - assert!(!neg_inf.is_normal()); - assert_eq!(Fp::Infinite, neg_inf.classify()); -} - -#[test] -fn test_zero() { - let zero: f128 = 0.0f128; - assert_eq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert_eq!(Fp::Zero, zero.classify()); -} - -#[test] -fn test_neg_zero() { - let neg_zero: f128 = -0.0; - assert_eq!(0.0, neg_zero); - assert!(!neg_zero.is_infinite()); - assert!(neg_zero.is_finite()); - assert!(!neg_zero.is_sign_positive()); - assert!(neg_zero.is_sign_negative()); - assert!(!neg_zero.is_nan()); - assert!(!neg_zero.is_normal()); - assert_eq!(Fp::Zero, neg_zero.classify()); -} - -#[test] -fn test_one() { - let one: f128 = 1.0f128; - assert_eq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert_eq!(Fp::Normal, one.classify()); -} - -#[test] -fn test_is_nan() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert!(nan.is_nan()); - assert!(!0.0f128.is_nan()); - assert!(!5.3f128.is_nan()); - assert!(!(-10.732f128).is_nan()); - assert!(!inf.is_nan()); - assert!(!neg_inf.is_nan()); -} - -#[test] -fn test_is_infinite() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert!(!nan.is_infinite()); - assert!(inf.is_infinite()); - assert!(neg_inf.is_infinite()); - assert!(!0.0f128.is_infinite()); - assert!(!42.8f128.is_infinite()); - assert!(!(-109.2f128).is_infinite()); -} - -#[test] -fn test_is_finite() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert!(!nan.is_finite()); - assert!(!inf.is_finite()); - assert!(!neg_inf.is_finite()); - assert!(0.0f128.is_finite()); - assert!(42.8f128.is_finite()); - assert!((-109.2f128).is_finite()); -} - -#[test] -fn test_is_normal() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let zero: f128 = 0.0f128; - let neg_zero: f128 = -0.0; - assert!(!nan.is_normal()); - assert!(!inf.is_normal()); - assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); - assert!(!neg_zero.is_normal()); - assert!(1f128.is_normal()); - assert!(1e-4931f128.is_normal()); - assert!(!1e-4932f128.is_normal()); -} - -#[test] -fn test_classify() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let zero: f128 = 0.0f128; - let neg_zero: f128 = -0.0; - assert_eq!(nan.classify(), Fp::Nan); - assert_eq!(inf.classify(), Fp::Infinite); - assert_eq!(neg_inf.classify(), Fp::Infinite); - assert_eq!(zero.classify(), Fp::Zero); - assert_eq!(neg_zero.classify(), Fp::Zero); - assert_eq!(1f128.classify(), Fp::Normal); - assert_eq!(1e-4931f128.classify(), Fp::Normal); - assert_eq!(1e-4932f128.classify(), Fp::Subnormal); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_floor() { - assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_ceil() { - assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_round() { - assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE); - assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_round_ties_even() { - assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_trunc() { - assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_fract() { - assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_abs() { - assert_eq!(f128::INFINITY.abs(), f128::INFINITY); - assert_eq!(1f128.abs(), 1f128); - assert_eq!(0f128.abs(), 0f128); - assert_eq!((-0f128).abs(), 0f128); - assert_eq!((-1f128).abs(), 1f128); - assert_eq!(f128::NEG_INFINITY.abs(), f128::INFINITY); - assert_eq!((1f128 / f128::NEG_INFINITY).abs(), 0f128); - assert!(f128::NAN.abs().is_nan()); -} - -#[test] -fn test_is_sign_positive() { - assert!(f128::INFINITY.is_sign_positive()); - assert!(1f128.is_sign_positive()); - assert!(0f128.is_sign_positive()); - assert!(!(-0f128).is_sign_positive()); - assert!(!(-1f128).is_sign_positive()); - assert!(!f128::NEG_INFINITY.is_sign_positive()); - assert!(!(1f128 / f128::NEG_INFINITY).is_sign_positive()); - assert!(f128::NAN.is_sign_positive()); - assert!(!(-f128::NAN).is_sign_positive()); -} - -#[test] -fn test_is_sign_negative() { - assert!(!f128::INFINITY.is_sign_negative()); - assert!(!1f128.is_sign_negative()); - assert!(!0f128.is_sign_negative()); - assert!((-0f128).is_sign_negative()); - assert!((-1f128).is_sign_negative()); - assert!(f128::NEG_INFINITY.is_sign_negative()); - assert!((1f128 / f128::NEG_INFINITY).is_sign_negative()); - assert!(!f128::NAN.is_sign_negative()); - assert!((-f128::NAN).is_sign_negative()); -} - -#[test] -fn test_next_up() { - let tiny = f128::from_bits(TINY_BITS); - let tiny_up = f128::from_bits(TINY_UP_BITS); - let max_down = f128::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); - assert_f128_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN); - assert_f128_biteq!(f128::MIN.next_up(), -max_down); - assert_f128_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0); - assert_f128_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f128_biteq!((-tiny_up).next_up(), -tiny); - assert_f128_biteq!((-tiny).next_up(), -0.0f128); - assert_f128_biteq!((-0.0f128).next_up(), tiny); - assert_f128_biteq!(0.0f128.next_up(), tiny); - assert_f128_biteq!(tiny.next_up(), tiny_up); - assert_f128_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f128_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON); - assert_f128_biteq!(f128::MAX.next_up(), f128::INFINITY); - assert_f128_biteq!(f128::INFINITY.next_up(), f128::INFINITY); - - // Check that NaNs roundtrip. - let nan0 = f128::NAN; - let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); - assert_f128_biteq!(nan0.next_up(), nan0); - assert_f128_biteq!(nan1.next_up(), nan1); - assert_f128_biteq!(nan2.next_up(), nan2); -} - -#[test] -fn test_next_down() { - let tiny = f128::from_bits(TINY_BITS); - let tiny_up = f128::from_bits(TINY_UP_BITS); - let max_down = f128::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); - assert_f128_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY); - assert_f128_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY); - assert_f128_biteq!((-max_down).next_down(), f128::MIN); - assert_f128_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON); - assert_f128_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f128_biteq!((-tiny).next_down(), -tiny_up); - assert_f128_biteq!((-0.0f128).next_down(), -tiny); - assert_f128_biteq!((0.0f128).next_down(), -tiny); - assert_f128_biteq!(tiny.next_down(), 0.0f128); - assert_f128_biteq!(tiny_up.next_down(), tiny); - assert_f128_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f128_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128); - assert_f128_biteq!(f128::MAX.next_down(), max_down); - assert_f128_biteq!(f128::INFINITY.next_down(), f128::MAX); - - // Check that NaNs roundtrip. - let nan0 = f128::NAN; - let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); - assert_f128_biteq!(nan0.next_down(), nan0); - assert_f128_biteq!(nan1.next_down(), nan1); - assert_f128_biteq!(nan2.next_down(), nan2); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_mul_add() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE); - assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE); - assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE); - assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f128.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_recip() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.recip(), 1.0); - assert_eq!(2.0f128.recip(), 0.5); - assert_eq!((-0.4f128).recip(), -2.5); - assert_eq!(0.0f128.recip(), inf); - assert_approx_eq!( - f128::MAX.recip(), - 8.40525785778023376565669454330438228902076605e-4933, - 1e-4900 - ); - assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); -} - // Many math functions allow for less accurate results, so the next tolerance up is used #[test] -#[cfg(reliable_f128_math)] -fn test_powi() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.powi(1), 1.0); - assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL); - assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL); - assert_eq!(8.3f128.powi(0), 1.0); - assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); -} - -#[test] -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] fn test_powf() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; @@ -514,19 +66,8 @@ fn test_powf() { } #[test] -#[cfg(reliable_f128_math)] -fn test_sqrt_domain() { - assert!(f128::NAN.sqrt().is_nan()); - assert!(f128::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f128).sqrt().is_nan()); - assert_eq!((-0.0f128).sqrt(), -0.0); - assert_eq!(0.0f128.sqrt(), 0.0); - assert_eq!(1.0f128.sqrt(), 1.0); - assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); -} - -#[test] -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] fn test_exp() { assert_eq!(1.0, 0.0f128.exp()); assert_approx_eq!(consts::E, 1.0f128.exp(), TOL); @@ -541,7 +82,8 @@ fn test_exp() { } #[test] -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] fn test_exp2() { assert_eq!(32.0, 5.0f128.exp2()); assert_eq!(1.0, 0.0f128.exp2()); @@ -555,7 +97,8 @@ fn test_exp2() { } #[test] -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] fn test_ln() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; @@ -571,7 +114,8 @@ fn test_ln() { } #[test] -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] fn test_log() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; @@ -590,7 +134,8 @@ fn test_log() { } #[test] -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] fn test_log2() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; @@ -607,7 +152,8 @@ fn test_log2() { } #[test] -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] fn test_log10() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; @@ -625,39 +171,8 @@ fn test_log10() { } #[test] -fn test_to_degrees() { - let pi: f128 = consts::PI; - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(0.0f128.to_degrees(), 0.0); - assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL); - assert_approx_eq!(pi.to_degrees(), 180.0, TOL); - assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703); -} - -#[test] -fn test_to_radians() { - let pi: f128 = consts::PI; - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(0.0f128.to_radians(), 0.0); - assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL); - assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL); - // check approx rather than exact because round trip for pi doesn't fall on an exactly - // representable value (unlike `f32` and `f64`). - assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE); - assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); -} - -#[test] -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] fn test_asinh() { // Lower accuracy results are allowed, use increased tolerances assert_eq!(0.0f128.asinh(), 0.0f128); @@ -688,7 +203,8 @@ fn test_asinh() { } #[test] -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] fn test_acosh() { assert_eq!(1.0f128.acosh(), 0.0f128); assert!(0.999f128.acosh().is_nan()); @@ -707,7 +223,8 @@ fn test_acosh() { } #[test] -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] fn test_atanh() { assert_eq!(0.0f128.atanh(), 0.0f128); assert_eq!((-0.0f128).atanh(), -0.0f128); @@ -727,7 +244,8 @@ fn test_atanh() { } #[test] -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] fn test_gamma() { // precision can differ among platforms assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR); @@ -748,7 +266,8 @@ fn test_gamma() { } #[test] -#[cfg(reliable_f128_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f128_math)] fn test_ln_gamma() { assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); assert_eq!(1.0f128.ln_gamma().1, 1); @@ -779,7 +298,8 @@ fn test_real_consts() { assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE); assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE); - #[cfg(reliable_f128_math)] + #[cfg(not(miri))] + #[cfg(target_has_reliable_f128_math)] { let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; let sqrt2: f128 = consts::SQRT_2; @@ -799,187 +319,3 @@ fn test_real_consts() { assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); } } - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000); - assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); - assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); - assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); - assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE); - assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE); - assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE); - assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f128::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f128::NAN.to_bits() ^ NAN_MASK2; - assert!(f128::from_bits(masked_nan1).is_nan()); - assert!(f128::from_bits(masked_nan2).is_nan()); - - assert_eq!(f128::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2); -} - -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f128.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f128.clamp(f128::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f128.clamp(3.0, f128::NAN); -} - -#[test] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u128 { - 1 << (f128::MANTISSA_DIGITS - 2) - } - - // FIXME(f16_f128): test subnormals when powf is available - // fn min_subnorm() -> f128 { - // f128::MIN_POSITIVE / f128::powf(2.0, f128::MANTISSA_DIGITS as f128 - 1.0) - // } - - // fn max_subnorm() -> f128 { - // f128::MIN_POSITIVE - min_subnorm() - // } - - fn q_nan() -> f128 { - f128::from_bits(f128::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f128 { - f128::from_bits((f128::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f128::INFINITY).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Equal, (-f128::MAX).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f128).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f128).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f128).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f128).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f128::MIN_POSITIVE).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f128).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f128.total_cmp(&0.0)); - // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f128::MIN_POSITIVE.total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f128.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f128.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f128.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f128.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f128::MAX.total_cmp(&f128::MAX)); - assert_eq!(Ordering::Equal, f128::INFINITY.total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Less, (-f128::INFINITY).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Less, (-f128::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f128).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f128).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f128).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f128).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-f128::MIN_POSITIVE).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f128).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, 0.0_f128.total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f128::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f128.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f128.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f128.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f128.total_cmp(&f128::MAX)); - assert_eq!(Ordering::Less, f128::MAX.total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Less, f128::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f128::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f128::MAX).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f128).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f128).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f128).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f128).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f128::MIN_POSITIVE).total_cmp(&-0.5)); - // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Greater, (-0.0_f128).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f128.total_cmp(&-0.0)); - // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Greater, f128::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f128.total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f128.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f128.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f128.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f128::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f128::INFINITY.total_cmp(&f128::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} diff --git a/library/std/tests/floats/f16.rs b/library/std/tests/floats/f16.rs index 5180f3d40f3a7..0f8b4138d2266 100644 --- a/library/std/tests/floats/f16.rs +++ b/library/std/tests/floats/f16.rs @@ -1,8 +1,7 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy -#![cfg(reliable_f16)] +#![cfg(target_has_reliable_f16)] use std::f16::consts; -use std::num::FpCategory as Fp; /// Tolerance for results on the order of 10.0e-2 #[allow(unused)] @@ -20,27 +19,6 @@ const TOL_P2: f16 = 0.5; #[allow(unused)] const TOL_P4: f16 = 10.0; -/// Smallest number -const TINY_BITS: u16 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u16 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u16 = 0x7bfe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u16 = 0x03ff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u16 = 0x0400; - -/// First pattern over the mantissa -const NAN_MASK1: u16 = 0x02aa; - -/// Second pattern over the mantissa -const NAN_MASK2: u16 = 0x0155; - /// Compare by representation #[allow(unused_macros)] macro_rules! assert_f16_biteq { @@ -53,430 +31,8 @@ macro_rules! assert_f16_biteq { } #[test] -fn test_num_f16() { - crate::test_num(10f16, 2f16); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_min_nan() { - assert_eq!(f16::NAN.min(2.0), 2.0); - assert_eq!(2.0f16.min(f16::NAN), 2.0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_max_nan() { - assert_eq!(f16::NAN.max(2.0), 2.0); - assert_eq!(2.0f16.max(f16::NAN), 2.0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_minimum() { - assert!(f16::NAN.minimum(2.0).is_nan()); - assert!(2.0f16.minimum(f16::NAN).is_nan()); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_maximum() { - assert!(f16::NAN.maximum(2.0).is_nan()); - assert!(2.0f16.maximum(f16::NAN).is_nan()); -} - -#[test] -fn test_nan() { - let nan: f16 = f16::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert!(!nan.is_normal()); - assert_eq!(Fp::Nan, nan.classify()); -} - -#[test] -fn test_infinity() { - let inf: f16 = f16::INFINITY; - assert!(inf.is_infinite()); - assert!(!inf.is_finite()); - assert!(inf.is_sign_positive()); - assert!(!inf.is_sign_negative()); - assert!(!inf.is_nan()); - assert!(!inf.is_normal()); - assert_eq!(Fp::Infinite, inf.classify()); -} - -#[test] -fn test_neg_infinity() { - let neg_inf: f16 = f16::NEG_INFINITY; - assert!(neg_inf.is_infinite()); - assert!(!neg_inf.is_finite()); - assert!(!neg_inf.is_sign_positive()); - assert!(neg_inf.is_sign_negative()); - assert!(!neg_inf.is_nan()); - assert!(!neg_inf.is_normal()); - assert_eq!(Fp::Infinite, neg_inf.classify()); -} - -#[test] -fn test_zero() { - let zero: f16 = 0.0f16; - assert_eq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert_eq!(Fp::Zero, zero.classify()); -} - -#[test] -fn test_neg_zero() { - let neg_zero: f16 = -0.0; - assert_eq!(0.0, neg_zero); - assert!(!neg_zero.is_infinite()); - assert!(neg_zero.is_finite()); - assert!(!neg_zero.is_sign_positive()); - assert!(neg_zero.is_sign_negative()); - assert!(!neg_zero.is_nan()); - assert!(!neg_zero.is_normal()); - assert_eq!(Fp::Zero, neg_zero.classify()); -} - -#[test] -fn test_one() { - let one: f16 = 1.0f16; - assert_eq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert_eq!(Fp::Normal, one.classify()); -} - -#[test] -fn test_is_nan() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert!(nan.is_nan()); - assert!(!0.0f16.is_nan()); - assert!(!5.3f16.is_nan()); - assert!(!(-10.732f16).is_nan()); - assert!(!inf.is_nan()); - assert!(!neg_inf.is_nan()); -} - -#[test] -fn test_is_infinite() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert!(!nan.is_infinite()); - assert!(inf.is_infinite()); - assert!(neg_inf.is_infinite()); - assert!(!0.0f16.is_infinite()); - assert!(!42.8f16.is_infinite()); - assert!(!(-109.2f16).is_infinite()); -} - -#[test] -fn test_is_finite() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert!(!nan.is_finite()); - assert!(!inf.is_finite()); - assert!(!neg_inf.is_finite()); - assert!(0.0f16.is_finite()); - assert!(42.8f16.is_finite()); - assert!((-109.2f16).is_finite()); -} - -#[test] -fn test_is_normal() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let zero: f16 = 0.0f16; - let neg_zero: f16 = -0.0; - assert!(!nan.is_normal()); - assert!(!inf.is_normal()); - assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); - assert!(!neg_zero.is_normal()); - assert!(1f16.is_normal()); - assert!(1e-4f16.is_normal()); - assert!(!1e-5f16.is_normal()); -} - -#[test] -fn test_classify() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let zero: f16 = 0.0f16; - let neg_zero: f16 = -0.0; - assert_eq!(nan.classify(), Fp::Nan); - assert_eq!(inf.classify(), Fp::Infinite); - assert_eq!(neg_inf.classify(), Fp::Infinite); - assert_eq!(zero.classify(), Fp::Zero); - assert_eq!(neg_zero.classify(), Fp::Zero); - assert_eq!(1f16.classify(), Fp::Normal); - assert_eq!(1e-4f16.classify(), Fp::Normal); - assert_eq!(1e-5f16.classify(), Fp::Subnormal); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_floor() { - assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0); - assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0); - assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_ceil() { - assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0); - assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0); - assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0); - assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0); - assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_round() { - assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0); - assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0); - assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0); - assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0); - assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_round_ties_even() { - assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0); - assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0); - assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0); - assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0); - assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_trunc() { - assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0); - assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_fract() { - assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0); - assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0); - assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0); - assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0); - assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0); - assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0); - assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0); - assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_abs() { - assert_eq!(f16::INFINITY.abs(), f16::INFINITY); - assert_eq!(1f16.abs(), 1f16); - assert_eq!(0f16.abs(), 0f16); - assert_eq!((-0f16).abs(), 0f16); - assert_eq!((-1f16).abs(), 1f16); - assert_eq!(f16::NEG_INFINITY.abs(), f16::INFINITY); - assert_eq!((1f16 / f16::NEG_INFINITY).abs(), 0f16); - assert!(f16::NAN.abs().is_nan()); -} - -#[test] -fn test_is_sign_positive() { - assert!(f16::INFINITY.is_sign_positive()); - assert!(1f16.is_sign_positive()); - assert!(0f16.is_sign_positive()); - assert!(!(-0f16).is_sign_positive()); - assert!(!(-1f16).is_sign_positive()); - assert!(!f16::NEG_INFINITY.is_sign_positive()); - assert!(!(1f16 / f16::NEG_INFINITY).is_sign_positive()); - assert!(f16::NAN.is_sign_positive()); - assert!(!(-f16::NAN).is_sign_positive()); -} - -#[test] -fn test_is_sign_negative() { - assert!(!f16::INFINITY.is_sign_negative()); - assert!(!1f16.is_sign_negative()); - assert!(!0f16.is_sign_negative()); - assert!((-0f16).is_sign_negative()); - assert!((-1f16).is_sign_negative()); - assert!(f16::NEG_INFINITY.is_sign_negative()); - assert!((1f16 / f16::NEG_INFINITY).is_sign_negative()); - assert!(!f16::NAN.is_sign_negative()); - assert!((-f16::NAN).is_sign_negative()); -} - -#[test] -fn test_next_up() { - let tiny = f16::from_bits(TINY_BITS); - let tiny_up = f16::from_bits(TINY_UP_BITS); - let max_down = f16::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); - assert_f16_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN); - assert_f16_biteq!(f16::MIN.next_up(), -max_down); - assert_f16_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0); - assert_f16_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f16_biteq!((-tiny_up).next_up(), -tiny); - assert_f16_biteq!((-tiny).next_up(), -0.0f16); - assert_f16_biteq!((-0.0f16).next_up(), tiny); - assert_f16_biteq!(0.0f16.next_up(), tiny); - assert_f16_biteq!(tiny.next_up(), tiny_up); - assert_f16_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f16_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON); - assert_f16_biteq!(f16::MAX.next_up(), f16::INFINITY); - assert_f16_biteq!(f16::INFINITY.next_up(), f16::INFINITY); - - // Check that NaNs roundtrip. - let nan0 = f16::NAN; - let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); - assert_f16_biteq!(nan0.next_up(), nan0); - assert_f16_biteq!(nan1.next_up(), nan1); - assert_f16_biteq!(nan2.next_up(), nan2); -} - -#[test] -fn test_next_down() { - let tiny = f16::from_bits(TINY_BITS); - let tiny_up = f16::from_bits(TINY_UP_BITS); - let max_down = f16::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); - assert_f16_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY); - assert_f16_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY); - assert_f16_biteq!((-max_down).next_down(), f16::MIN); - assert_f16_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON); - assert_f16_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f16_biteq!((-tiny).next_down(), -tiny_up); - assert_f16_biteq!((-0.0f16).next_down(), -tiny); - assert_f16_biteq!((0.0f16).next_down(), -tiny); - assert_f16_biteq!(tiny.next_down(), 0.0f16); - assert_f16_biteq!(tiny_up.next_down(), tiny); - assert_f16_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f16_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16); - assert_f16_biteq!(f16::MAX.next_down(), max_down); - assert_f16_biteq!(f16::INFINITY.next_down(), f16::MAX); - - // Check that NaNs roundtrip. - let nan0 = f16::NAN; - let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); - assert_f16_biteq!(nan0.next_down(), nan0); - assert_f16_biteq!(nan1.next_down(), nan1); - assert_f16_biteq!(nan2.next_down(), nan2); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_mul_add() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2); - assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2); - assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0); - assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f16.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_recip() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(1.0f16.recip(), 1.0); - assert_eq!(2.0f16.recip(), 0.5); - assert_eq!((-0.4f16).recip(), -2.5); - assert_eq!(0.0f16.recip(), inf); - assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); - assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_powi() { - // FIXME(llvm19): LLVM misoptimizes `powi.f16` - // - // let nan: f16 = f16::NAN; - // let inf: f16 = f16::INFINITY; - // let neg_inf: f16 = f16::NEG_INFINITY; - // assert_eq!(1.0f16.powi(1), 1.0); - // assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0); - // assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2); - // assert_eq!(8.3f16.powi(0), 1.0); - // assert!(nan.powi(2).is_nan()); - // assert_eq!(inf.powi(3), inf); - // assert_eq!(neg_inf.powi(2), inf); -} - -#[test] -#[cfg(reliable_f16_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] fn test_powf() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; @@ -493,19 +49,8 @@ fn test_powf() { } #[test] -#[cfg(reliable_f16_math)] -fn test_sqrt_domain() { - assert!(f16::NAN.sqrt().is_nan()); - assert!(f16::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f16).sqrt().is_nan()); - assert_eq!((-0.0f16).sqrt(), -0.0); - assert_eq!(0.0f16.sqrt(), 0.0); - assert_eq!(1.0f16.sqrt(), 1.0); - assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); -} - -#[test] -#[cfg(reliable_f16_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] fn test_exp() { assert_eq!(1.0, 0.0f16.exp()); assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0); @@ -520,7 +65,8 @@ fn test_exp() { } #[test] -#[cfg(reliable_f16_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] fn test_exp2() { assert_eq!(32.0, 5.0f16.exp2()); assert_eq!(1.0, 0.0f16.exp2()); @@ -534,7 +80,8 @@ fn test_exp2() { } #[test] -#[cfg(reliable_f16_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] fn test_ln() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; @@ -550,7 +97,8 @@ fn test_ln() { } #[test] -#[cfg(reliable_f16_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] fn test_log() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; @@ -569,7 +117,8 @@ fn test_log() { } #[test] -#[cfg(reliable_f16_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] fn test_log2() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; @@ -586,7 +135,8 @@ fn test_log2() { } #[test] -#[cfg(reliable_f16_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] fn test_log10() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; @@ -604,37 +154,8 @@ fn test_log10() { } #[test] -fn test_to_degrees() { - let pi: f16 = consts::PI; - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(0.0f16.to_degrees(), 0.0); - assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2); - assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2); - assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703); -} - -#[test] -fn test_to_radians() { - let pi: f16 = consts::PI; - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(0.0f16.to_radians(), 0.0); - assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0); - assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0); - assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0); - assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); -} - -#[test] -#[cfg(reliable_f16_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] fn test_asinh() { assert_eq!(0.0f16.asinh(), 0.0f16); assert_eq!((-0.0f16).asinh(), -0.0f16); @@ -659,7 +180,8 @@ fn test_asinh() { } #[test] -#[cfg(reliable_f16_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] fn test_acosh() { assert_eq!(1.0f16.acosh(), 0.0f16); assert!(0.999f16.acosh().is_nan()); @@ -678,7 +200,8 @@ fn test_acosh() { } #[test] -#[cfg(reliable_f16_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] fn test_atanh() { assert_eq!(0.0f16.atanh(), 0.0f16); assert_eq!((-0.0f16).atanh(), -0.0f16); @@ -698,7 +221,8 @@ fn test_atanh() { } #[test] -#[cfg(reliable_f16_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] fn test_gamma() { // precision can differ among platforms assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0); @@ -719,7 +243,8 @@ fn test_gamma() { } #[test] -#[cfg(reliable_f16_math)] +#[cfg(not(miri))] +#[cfg(target_has_reliable_f16_math)] fn test_ln_gamma() { assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0); assert_eq!(1.0f16.ln_gamma().1, 1); @@ -752,7 +277,8 @@ fn test_real_consts() { assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0); assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0); - #[cfg(reliable_f16_math)] + #[cfg(not(miri))] + #[cfg(target_has_reliable_f16_math)] { let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; let sqrt2: f16 = consts::SQRT_2; @@ -772,186 +298,3 @@ fn test_real_consts() { assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); } } - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f16).to_bits(), 0x3c00); - assert_eq!((12.5f16).to_bits(), 0x4a40); - assert_eq!((1337f16).to_bits(), 0x6539); - assert_eq!((-14.25f16).to_bits(), 0xcb20); - assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0); - assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0); - assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4); - assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f16::NAN.to_bits() ^ NAN_MASK2; - assert!(f16::from_bits(masked_nan1).is_nan()); - assert!(f16::from_bits(masked_nan2).is_nan()); - - assert_eq!(f16::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2); -} - -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f16.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f16.clamp(f16::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f16.clamp(3.0, f16::NAN); -} - -#[test] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u16 { - 1 << (f16::MANTISSA_DIGITS - 2) - } - - // FIXME(f16_f128): test subnormals when powf is available - // fn min_subnorm() -> f16 { - // f16::MIN_POSITIVE / f16::powf(2.0, f16::MANTISSA_DIGITS as f16 - 1.0) - // } - - // fn max_subnorm() -> f16 { - // f16::MIN_POSITIVE - min_subnorm() - // } - - fn q_nan() -> f16 { - f16::from_bits(f16::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f16 { - f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f16::INFINITY).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Equal, (-f16::MAX).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f16).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f16).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f16).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f16).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f16::MIN_POSITIVE).total_cmp(&-f16::MIN_POSITIVE)); - // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f16).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f16.total_cmp(&0.0)); - // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f16::MIN_POSITIVE.total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f16.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f16.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f16.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f16.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f16::MAX.total_cmp(&f16::MAX)); - assert_eq!(Ordering::Equal, f16::INFINITY.total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Less, (-f16::INFINITY).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Less, (-f16::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f16).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f16).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f16).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f16).total_cmp(&-f16::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-f16::MIN_POSITIVE).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f16).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, 0.0_f16.total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f16::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f16.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f16.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f16.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f16.total_cmp(&f16::MAX)); - assert_eq!(Ordering::Less, f16::MAX.total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f16::MAX).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f16).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f16).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f16).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f16).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f16::MIN_POSITIVE).total_cmp(&-0.5)); - // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f16::MIN_POSITIVE)); - // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Greater, (-0.0_f16).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f16.total_cmp(&-0.0)); - // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Greater, f16::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f16.total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f16.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f16.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f16.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f16::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f16::INFINITY.total_cmp(&f16::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} diff --git a/library/std/tests/floats/f32.rs b/library/std/tests/floats/f32.rs index bf7641986ada8..e54f227bb774b 100644 --- a/library/std/tests/floats/f32.rs +++ b/library/std/tests/floats/f32.rs @@ -1,26 +1,4 @@ use std::f32::consts; -use std::num::FpCategory as Fp; - -/// Smallest number -const TINY_BITS: u32 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u32 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u32 = 0x7f7f_fffe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000; - -/// First pattern over the mantissa -const NAN_MASK1: u32 = 0x002a_aaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u32 = 0x0055_5555; #[allow(unused_macros)] macro_rules! assert_f32_biteq { @@ -33,424 +11,6 @@ macro_rules! assert_f32_biteq { }; } -#[test] -fn test_num_f32() { - crate::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_minimum() { - assert!(f32::NAN.minimum(2.0).is_nan()); - assert!(2.0f32.minimum(f32::NAN).is_nan()); -} - -#[test] -fn test_maximum() { - assert!(f32::NAN.maximum(2.0).is_nan()); - assert!(2.0f32.maximum(f32::NAN).is_nan()); -} - -#[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!(2.5f32.round(), 3.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); - 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_round_ties_even() { - assert_approx_eq!(2.5f32.round_ties_even(), 2.0f32); - assert_approx_eq!(1.0f32.round_ties_even(), 1.0f32); - assert_approx_eq!(1.3f32.round_ties_even(), 1.0f32); - assert_approx_eq!(1.5f32.round_ties_even(), 2.0f32); - assert_approx_eq!(1.7f32.round_ties_even(), 2.0f32); - assert_approx_eq!(0.0f32.round_ties_even(), 0.0f32); - assert_approx_eq!((-0.0f32).round_ties_even(), -0.0f32); - assert_approx_eq!((-1.0f32).round_ties_even(), -1.0f32); - assert_approx_eq!((-1.3f32).round_ties_even(), -1.0f32); - assert_approx_eq!((-1.5f32).round_ties_even(), -2.0f32); - assert_approx_eq!((-1.7f32).round_ties_even(), -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_next_up() { - let tiny = f32::from_bits(TINY_BITS); - let tiny_up = f32::from_bits(TINY_UP_BITS); - let max_down = f32::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); - assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); - assert_f32_biteq!(f32::MIN.next_up(), -max_down); - assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0); - assert_f32_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f32_biteq!((-tiny_up).next_up(), -tiny); - assert_f32_biteq!((-tiny).next_up(), -0.0f32); - assert_f32_biteq!((-0.0f32).next_up(), tiny); - assert_f32_biteq!(0.0f32.next_up(), tiny); - assert_f32_biteq!(tiny.next_up(), tiny_up); - assert_f32_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f32_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON); - assert_f32_biteq!(f32::MAX.next_up(), f32::INFINITY); - assert_f32_biteq!(f32::INFINITY.next_up(), f32::INFINITY); - - // Check that NaNs roundtrip. - let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); - assert_f32_biteq!(nan0.next_up(), nan0); - assert_f32_biteq!(nan1.next_up(), nan1); - assert_f32_biteq!(nan2.next_up(), nan2); -} - -#[test] -fn test_next_down() { - let tiny = f32::from_bits(TINY_BITS); - let tiny_up = f32::from_bits(TINY_UP_BITS); - let max_down = f32::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); - assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); - assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); - assert_f32_biteq!((-max_down).next_down(), f32::MIN); - assert_f32_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); - assert_f32_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f32_biteq!((-tiny).next_down(), -tiny_up); - assert_f32_biteq!((-0.0f32).next_down(), -tiny); - assert_f32_biteq!((0.0f32).next_down(), -tiny); - assert_f32_biteq!(tiny.next_down(), 0.0f32); - assert_f32_biteq!(tiny_up.next_down(), tiny); - assert_f32_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f32_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32); - assert_f32_biteq!(f32::MAX.next_down(), max_down); - assert_f32_biteq!(f32::INFINITY.next_down(), f32::MAX); - - // Check that NaNs roundtrip. - let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); - assert_f32_biteq!(nan0.next_down(), nan0); - assert_f32_biteq!(nan1.next_down(), nan1); - assert_f32_biteq!(nan2.next_down(), nan2); -} - -#[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; @@ -467,17 +27,6 @@ fn test_powf() { 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()); @@ -571,36 +120,6 @@ fn test_log10() { 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); @@ -732,186 +251,3 @@ fn test_real_consts() { 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() ^ NAN_MASK1; - let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2; - assert!(f32::from_bits(masked_nan1).is_nan()); - assert!(f32::from_bits(masked_nan2).is_nan()); - - 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/tests/floats/f64.rs b/library/std/tests/floats/f64.rs index cbbfcd15efd26..2d8dd1cf0915b 100644 --- a/library/std/tests/floats/f64.rs +++ b/library/std/tests/floats/f64.rs @@ -1,26 +1,4 @@ use std::f64::consts; -use std::num::FpCategory as Fp; - -/// Smallest number -const TINY_BITS: u64 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u64 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000; - -/// First pattern over the mantissa -const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u64 = 0x0005_5555_5555_5555; #[allow(unused_macros)] macro_rules! assert_f64_biteq { @@ -33,409 +11,6 @@ macro_rules! assert_f64_biteq { }; } -#[test] -fn test_num_f64() { - crate::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()); -} - -#[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()); -} - -#[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()); -} - -#[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!(2.5f64.round(), 3.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); - 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_round_ties_even() { - assert_approx_eq!(2.5f64.round_ties_even(), 2.0f64); - assert_approx_eq!(1.0f64.round_ties_even(), 1.0f64); - assert_approx_eq!(1.3f64.round_ties_even(), 1.0f64); - assert_approx_eq!(1.5f64.round_ties_even(), 2.0f64); - assert_approx_eq!(1.7f64.round_ties_even(), 2.0f64); - assert_approx_eq!(0.0f64.round_ties_even(), 0.0f64); - assert_approx_eq!((-0.0f64).round_ties_even(), -0.0f64); - assert_approx_eq!((-1.0f64).round_ties_even(), -1.0f64); - assert_approx_eq!((-1.3f64).round_ties_even(), -1.0f64); - assert_approx_eq!((-1.5f64).round_ties_even(), -2.0f64); - assert_approx_eq!((-1.7f64).round_ties_even(), -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_next_up() { - let tiny = f64::from_bits(TINY_BITS); - let tiny_up = f64::from_bits(TINY_UP_BITS); - let max_down = f64::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); - assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); - assert_f64_biteq!(f64::MIN.next_up(), -max_down); - assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0); - assert_f64_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f64_biteq!((-tiny_up).next_up(), -tiny); - assert_f64_biteq!((-tiny).next_up(), -0.0f64); - assert_f64_biteq!((-0.0f64).next_up(), tiny); - assert_f64_biteq!(0.0f64.next_up(), tiny); - assert_f64_biteq!(tiny.next_up(), tiny_up); - assert_f64_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f64_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON); - assert_f64_biteq!(f64::MAX.next_up(), f64::INFINITY); - assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY); - - let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); - assert_f64_biteq!(nan0.next_up(), nan0); - assert_f64_biteq!(nan1.next_up(), nan1); - assert_f64_biteq!(nan2.next_up(), nan2); -} - -#[test] -fn test_next_down() { - let tiny = f64::from_bits(TINY_BITS); - let tiny_up = f64::from_bits(TINY_UP_BITS); - let max_down = f64::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); - assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); - assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); - assert_f64_biteq!((-max_down).next_down(), f64::MIN); - assert_f64_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); - assert_f64_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f64_biteq!((-tiny).next_down(), -tiny_up); - assert_f64_biteq!((-0.0f64).next_down(), -tiny); - assert_f64_biteq!((0.0f64).next_down(), -tiny); - assert_f64_biteq!(tiny.next_down(), 0.0f64); - assert_f64_biteq!(tiny_up.next_down(), tiny); - assert_f64_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f64_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64); - assert_f64_biteq!(f64::MAX.next_down(), max_down); - assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX); - - let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); - assert_f64_biteq!(nan0.next_down(), nan0); - assert_f64_biteq!(nan1.next_down(), nan1); - assert_f64_biteq!(nan2.next_down(), nan2); -} - -#[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; @@ -452,17 +27,6 @@ fn test_powf() { 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()); @@ -556,35 +120,6 @@ fn test_log10() { 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); @@ -712,185 +247,3 @@ fn test_real_consts() { 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 - let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f64::NAN.to_bits() ^ NAN_MASK2; - assert!(f64::from_bits(masked_nan1).is_nan()); - assert!(f64::from_bits(masked_nan2).is_nan()); - - 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/library/std/tests/floats/lib.rs b/library/std/tests/floats/lib.rs index ad82f1a44e711..8bb8eb4bfc1ae 100644 --- a/library/std/tests/floats/lib.rs +++ b/library/std/tests/floats/lib.rs @@ -1,4 +1,5 @@ -#![feature(f16, f128, float_gamma, float_minimum_maximum)] +#![feature(f16, f128, float_gamma, float_minimum_maximum, cfg_target_has_reliable_f16_f128)] +#![expect(internal_features)] // for reliable_f16_f128 use std::fmt; use std::ops::{Add, Div, Mul, Rem, Sub}; @@ -10,7 +11,7 @@ macro_rules! assert_approx_eq { let (a, b) = (&$a, &$b); let diff = (*a - *b).abs(); assert!( - diff < $lim, + diff <= $lim, "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", lim = $lim ); diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index 00d99a578d580..c51a4459e718b 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -1,5 +1,3 @@ -#![feature(anonymous_pipe)] - fn main() { #[cfg(all(not(miri), any(unix, windows), not(target_os = "emscripten")))] { diff --git a/library/std/tests/run-time-detect.rs b/library/std/tests/run-time-detect.rs index dd14c0266aa4d..e59ae2f3d7f18 100644 --- a/library/std/tests/run-time-detect.rs +++ b/library/std/tests/run-time-detect.rs @@ -8,6 +8,10 @@ all(target_arch = "aarch64", any(target_os = "linux", target_os = "android")), feature(stdarch_aarch64_feature_detection) )] +#![cfg_attr( + all(target_arch = "s390x", target_os = "linux"), + feature(stdarch_s390x_feature_detection) +)] #![cfg_attr( all(target_arch = "powerpc", target_os = "linux"), feature(stdarch_powerpc_feature_detection) @@ -132,6 +136,32 @@ fn powerpc64_linux() { // tidy-alphabetical-end } +#[test] +#[cfg(all(target_arch = "s390x", target_os = "linux"))] +fn s390x_linux() { + use std::arch::is_s390x_feature_detected; + // tidy-alphabetical-start + println!("deflate-conversion: {}", is_s390x_feature_detected!("deflate-conversion")); + println!("enhanced-sort: {}", is_s390x_feature_detected!("enhanced-sort")); + println!("guarded-storage: {}", is_s390x_feature_detected!("guarded-storage")); + println!("high-word: {}", is_s390x_feature_detected!("high-word")); + println!("nnp-assist: {}", is_s390x_feature_detected!("nnp-assist")); + println!("transactional-execution: {}", is_s390x_feature_detected!("transactional-execution")); + println!("vector-enhancements-1: {}", is_s390x_feature_detected!("vector-enhancements-1")); + println!("vector-enhancements-2: {}", is_s390x_feature_detected!("vector-enhancements-2")); + println!( + "vector-packed-decimal-enhancement-2: {}", + is_s390x_feature_detected!("vector-packed-decimal-enhancement-2") + ); + println!( + "vector-packed-decimal-enhancement: {}", + is_s390x_feature_detected!("vector-packed-decimal-enhancement") + ); + println!("vector-packed-decimal: {}", is_s390x_feature_detected!("vector-packed-decimal")); + println!("vector: {}", is_s390x_feature_detected!("vector")); + // tidy-alphabetical-end +} + #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn x86_all() { diff --git a/library/std/tests/sync/mpmc.rs b/library/std/tests/sync/mpmc.rs index 81b92297f76a3..78abcb3bcbe1d 100644 --- a/library/std/tests/sync/mpmc.rs +++ b/library/std/tests/sync/mpmc.rs @@ -63,6 +63,24 @@ fn smoke_port_gone() { assert!(tx.send(1).is_err()); } +#[test] +fn smoke_receiver_clone() { + let (tx, rx) = channel::(); + let rx2 = rx.clone(); + drop(rx); + tx.send(1).unwrap(); + assert_eq!(rx2.recv().unwrap(), 1); +} + +#[test] +fn smoke_receiver_clone_port_gone() { + let (tx, rx) = channel::(); + let rx2 = rx.clone(); + drop(rx); + drop(rx2); + assert!(tx.send(1).is_err()); +} + #[test] fn smoke_shared_port_gone() { let (tx, rx) = channel::(); @@ -124,6 +142,18 @@ fn chan_gone_concurrent() { while rx.recv().is_ok() {} } +#[test] +fn receiver_cloning() { + let (tx, rx) = channel::(); + let rx2 = rx.clone(); + + tx.send(1).unwrap(); + tx.send(2).unwrap(); + + assert_eq!(rx2.recv(), Ok(1)); + assert_eq!(rx.recv(), Ok(2)); +} + #[test] fn stress() { let count = if cfg!(miri) { 100 } else { 10000 }; diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index 74c627201073e..ac82914d6de46 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -118,6 +118,7 @@ fn test_into_inner_drop() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_into_inner_poison() { let m = new_poisoned_mutex(NonCopy(10)); @@ -135,6 +136,7 @@ fn test_get_cloned() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_get_cloned_poison() { let m = new_poisoned_mutex(Cloneable(10)); @@ -152,6 +154,7 @@ fn test_get_mut() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_get_mut_poison() { let mut m = new_poisoned_mutex(NonCopy(10)); @@ -179,6 +182,7 @@ fn test_set() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_set_poison() { fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) where @@ -217,6 +221,7 @@ fn test_replace() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_replace_poison() { fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) where @@ -261,6 +266,7 @@ fn test_mutex_arc_condvar() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_arc_condvar_poison() { let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); let packet2 = Packet(packet.0.clone()); @@ -290,6 +296,7 @@ fn test_arc_condvar_poison() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_mutex_arc_poison() { let arc = Arc::new(Mutex::new(1)); assert!(!arc.is_poisoned()); @@ -304,6 +311,7 @@ fn test_mutex_arc_poison() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_mutex_arc_poison_mapped() { let arc = Arc::new(Mutex::new(1)); assert!(!arc.is_poisoned()); @@ -335,6 +343,7 @@ fn test_mutex_arc_nested() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_mutex_arc_access_in_unwind() { let arc = Arc::new(Mutex::new(1)); let arc2 = arc.clone(); @@ -381,6 +390,7 @@ fn test_mapping_mapped_guard() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_while_mapping_unlocked_poison() { let lock = Mutex::new(()); @@ -399,13 +409,13 @@ fn panic_while_mapping_unlocked_poison() { let _ = panic::catch_unwind(|| { let guard = lock.lock().unwrap(); - let _guard = MutexGuard::try_map::<(), _>(guard, |_| panic!()); + let _guard = MutexGuard::filter_map::<(), _>(guard, |_| panic!()); }); match lock.try_lock() { - Ok(_) => panic!("panicking in a MutexGuard::try_map closure should poison the Mutex"), + Ok(_) => panic!("panicking in a MutexGuard::filter_map closure should poison the Mutex"), Err(TryLockError::WouldBlock) => { - panic!("panicking in a MutexGuard::try_map closure should unlock the mutex") + panic!("panicking in a MutexGuard::filter_map closure should unlock the mutex") } Err(TryLockError::Poisoned(_)) => {} } @@ -427,13 +437,15 @@ fn panic_while_mapping_unlocked_poison() { let _ = panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let guard = MutexGuard::map::<(), _>(guard, |val| val); - let _guard = MappedMutexGuard::try_map::<(), _>(guard, |_| panic!()); + let _guard = MappedMutexGuard::filter_map::<(), _>(guard, |_| panic!()); }); match lock.try_lock() { - Ok(_) => panic!("panicking in a MappedMutexGuard::try_map closure should poison the Mutex"), + Ok(_) => { + panic!("panicking in a MappedMutexGuard::filter_map closure should poison the Mutex") + } Err(TryLockError::WouldBlock) => { - panic!("panicking in a MappedMutexGuard::try_map closure should unlock the mutex") + panic!("panicking in a MappedMutexGuard::filter_map closure should unlock the mutex") } Err(TryLockError::Poisoned(_)) => {} } diff --git a/library/std/tests/sync/once.rs b/library/std/tests/sync/once.rs index a3ffc73fe06b9..1b43831df3a4b 100644 --- a/library/std/tests/sync/once.rs +++ b/library/std/tests/sync/once.rs @@ -52,6 +52,7 @@ fn stampede_once() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn poison_bad() { static O: Once = Once::new(); @@ -80,6 +81,7 @@ fn poison_bad() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn wait_for_force_to_finish() { static O: Once = Once::new(); @@ -137,6 +139,7 @@ fn wait() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn wait_on_poisoned() { let once = Once::new(); @@ -145,6 +148,7 @@ fn wait_on_poisoned() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn wait_force_on_poisoned() { let once = Once::new(); diff --git a/library/std/tests/sync/once_lock.rs b/library/std/tests/sync/once_lock.rs index ac9aaa8892eff..922fd7da3d445 100644 --- a/library/std/tests/sync/once_lock.rs +++ b/library/std/tests/sync/once_lock.rs @@ -77,8 +77,10 @@ fn get_or_try_init() { let cell: OnceLock = OnceLock::new(); assert!(cell.get().is_none()); - let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); - assert!(res.is_err()); + if cfg!(panic = "unwind") { + let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); + assert!(res.is_err()); + } assert!(cell.get().is_none()); assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); diff --git a/library/std/tests/sync/rwlock.rs b/library/std/tests/sync/rwlock.rs index 49f260648c6ac..1d55a1769483a 100644 --- a/library/std/tests/sync/rwlock.rs +++ b/library/std/tests/sync/rwlock.rs @@ -73,6 +73,7 @@ fn frob() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_rw_arc_poison_wr() { let arc = Arc::new(RwLock::new(1)); let arc2 = arc.clone(); @@ -85,6 +86,7 @@ fn test_rw_arc_poison_wr() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_rw_arc_poison_mapped_w_r() { let arc = Arc::new(RwLock::new(1)); let arc2 = arc.clone(); @@ -98,6 +100,7 @@ fn test_rw_arc_poison_mapped_w_r() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_rw_arc_poison_ww() { let arc = Arc::new(RwLock::new(1)); assert!(!arc.is_poisoned()); @@ -112,6 +115,7 @@ fn test_rw_arc_poison_ww() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_rw_arc_poison_mapped_w_w() { let arc = Arc::new(RwLock::new(1)); let arc2 = arc.clone(); @@ -126,6 +130,7 @@ fn test_rw_arc_poison_mapped_w_w() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_rw_arc_no_poison_rr() { let arc = Arc::new(RwLock::new(1)); let arc2 = arc.clone(); @@ -139,6 +144,7 @@ fn test_rw_arc_no_poison_rr() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_rw_arc_no_poison_mapped_r_r() { let arc = Arc::new(RwLock::new(1)); let arc2 = arc.clone(); @@ -153,6 +159,7 @@ fn test_rw_arc_no_poison_mapped_r_r() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_rw_arc_no_poison_rw() { let arc = Arc::new(RwLock::new(1)); let arc2 = arc.clone(); @@ -166,6 +173,7 @@ fn test_rw_arc_no_poison_rw() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_rw_arc_no_poison_mapped_r_w() { let arc = Arc::new(RwLock::new(1)); let arc2 = arc.clone(); @@ -218,6 +226,7 @@ fn test_rw_arc() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_rw_arc_access_in_unwind() { let arc = Arc::new(RwLock::new(1)); let arc2 = arc.clone(); @@ -316,6 +325,7 @@ fn test_into_inner_drop() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_into_inner_poison() { let m = new_poisoned_rwlock(NonCopy(10)); @@ -333,6 +343,7 @@ fn test_get_cloned() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_get_cloned_poison() { let m = new_poisoned_rwlock(Cloneable(10)); @@ -350,6 +361,7 @@ fn test_get_mut() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_get_mut_poison() { let mut m = new_poisoned_rwlock(NonCopy(10)); @@ -377,6 +389,7 @@ fn test_set() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_set_poison() { fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) where @@ -415,6 +428,7 @@ fn test_replace() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_replace_poison() { fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) where @@ -482,6 +496,7 @@ fn test_mapping_mapped_guard() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_while_mapping_read_unlocked_no_poison() { let lock = RwLock::new(()); @@ -502,16 +517,20 @@ fn panic_while_mapping_read_unlocked_no_poison() { let _ = panic::catch_unwind(|| { let guard = lock.read().unwrap(); - let _guard = RwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); + let _guard = RwLockReadGuard::filter_map::<(), _>(guard, |_| panic!()); }); match lock.try_write() { Ok(_) => {} Err(TryLockError::WouldBlock) => { - panic!("panicking in a RwLockReadGuard::try_map closure should release the read lock") + panic!( + "panicking in a RwLockReadGuard::filter_map closure should release the read lock" + ) } Err(TryLockError::Poisoned(_)) => { - panic!("panicking in a RwLockReadGuard::try_map closure should not poison the RwLock") + panic!( + "panicking in a RwLockReadGuard::filter_map closure should not poison the RwLock" + ) } } @@ -534,16 +553,16 @@ fn panic_while_mapping_read_unlocked_no_poison() { let _ = panic::catch_unwind(|| { let guard = lock.read().unwrap(); let guard = RwLockReadGuard::map::<(), _>(guard, |val| val); - let _guard = MappedRwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); + let _guard = MappedRwLockReadGuard::filter_map::<(), _>(guard, |_| panic!()); }); match lock.try_write() { Ok(_) => {} Err(TryLockError::WouldBlock) => panic!( - "panicking in a MappedRwLockReadGuard::try_map closure should release the read lock" + "panicking in a MappedRwLockReadGuard::filter_map closure should release the read lock" ), Err(TryLockError::Poisoned(_)) => panic!( - "panicking in a MappedRwLockReadGuard::try_map closure should not poison the RwLock" + "panicking in a MappedRwLockReadGuard::filter_map closure should not poison the RwLock" ), } @@ -551,6 +570,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_while_mapping_write_unlocked_poison() { let lock = RwLock::new(()); @@ -569,15 +589,17 @@ fn panic_while_mapping_write_unlocked_poison() { let _ = panic::catch_unwind(|| { let guard = lock.write().unwrap(); - let _guard = RwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); + let _guard = RwLockWriteGuard::filter_map::<(), _>(guard, |_| panic!()); }); match lock.try_write() { Ok(_) => { - panic!("panicking in a RwLockWriteGuard::try_map closure should poison the RwLock") + panic!("panicking in a RwLockWriteGuard::filter_map closure should poison the RwLock") } Err(TryLockError::WouldBlock) => { - panic!("panicking in a RwLockWriteGuard::try_map closure should release the write lock") + panic!( + "panicking in a RwLockWriteGuard::filter_map closure should release the write lock" + ) } Err(TryLockError::Poisoned(_)) => {} } @@ -601,15 +623,15 @@ fn panic_while_mapping_write_unlocked_poison() { let _ = panic::catch_unwind(|| { let guard = lock.write().unwrap(); let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val); - let _guard = MappedRwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); + let _guard = MappedRwLockWriteGuard::filter_map::<(), _>(guard, |_| panic!()); }); match lock.try_write() { Ok(_) => panic!( - "panicking in a MappedRwLockWriteGuard::try_map closure should poison the RwLock" + "panicking in a MappedRwLockWriteGuard::filter_map closure should poison the RwLock" ), Err(TryLockError::WouldBlock) => panic!( - "panicking in a MappedRwLockWriteGuard::try_map closure should release the write lock" + "panicking in a MappedRwLockWriteGuard::filter_map closure should release the write lock" ), Err(TryLockError::Poisoned(_)) => {} } diff --git a/library/std/tests/thread_local/lib.rs b/library/std/tests/thread_local/lib.rs index c52914354253c..26af5f1eb0a9d 100644 --- a/library/std/tests/thread_local/lib.rs +++ b/library/std/tests/thread_local/lib.rs @@ -1,3 +1,5 @@ +#![feature(cfg_target_thread_local)] + #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] mod tests; diff --git a/library/std/tests/thread_local/tests.rs b/library/std/tests/thread_local/tests.rs index aa020c2559cc5..e8278361d9337 100644 --- a/library/std/tests/thread_local/tests.rs +++ b/library/std/tests/thread_local/tests.rs @@ -1,7 +1,7 @@ use std::cell::{Cell, UnsafeCell}; use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::{Arc, Condvar, Mutex}; -use std::thread::{self, Builder, LocalKey}; +use std::thread::{self, LocalKey}; use std::thread_local; #[derive(Clone, Default)] @@ -345,8 +345,27 @@ fn join_orders_after_tls_destructors() { } // Test that thread::current is still available in TLS destructors. +// +// The test won't currently work without target_thread_local, aka with slow tls. +// The runtime tries very hard to drop last the TLS variable that keeps the information about the +// current thread, by using several tricks like deffering the drop to a later round of TLS destruction. +// However, this only seems to work with fast tls. +// +// With slow TLS, it seems that multiple libc implementations will just set the value to null the first +// time they encounter it, regardless of it having a destructor or not. This means that trying to +// retrieve it later in a drop impl of another TLS variable will not work. +// +// ** Apple libc: https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread_tsd.c#L293 +// Sets the variable to null if it has a destructor and the value is not null. However, all variables +// created with pthread_key_create are marked as having a destructor, even if the fn ptr called with +// it is null. +// ** glibc: https://github.com/bminor/glibc/blob/e5893e6349541d871e8a25120bca014551d13ff5/nptl/nptl_deallocate_tsd.c#L59 +// ** musl: https://github.com/kraj/musl/blob/1880359b54ff7dd9f5016002bfdae4b136007dde/src/thread/pthread_key_create.c#L87 +#[cfg(target_thread_local)] #[test] fn thread_current_in_dtor() { + use std::thread::Builder; + // Go through one round of TLS destruction first. struct Defer; impl Drop for Defer { diff --git a/library/stdarch b/library/stdarch index 684de0d6fef70..1dfaa4db24797 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit 684de0d6fef708cae08214fef9643dd9ec7296e1 +Subproject commit 1dfaa4db2479753a46a3e90f2c3c89d89d0b21f1 diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 0f6fa2d291ab3..c149d513c32b4 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -3,7 +3,7 @@ cargo-features = ["public-dependency"] [package] name = "sysroot" version = "0.0.0" -edition = "2021" +edition = "2024" # this is a dummy crate to ensure that all required crates appear in the sysroot [dependencies] @@ -31,5 +31,4 @@ panic_immediate_abort = ["std/panic_immediate_abort"] profiler = ["dep:profiler_builtins"] std_detect_file_io = ["std/std_detect_file_io"] std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] -std_detect_env_override = ["std/std_detect_env_override"] windows_raw_dylib = ["std/windows_raw_dylib"] diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index 241ef324b0088..2a32a7dd76eed 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -3,7 +3,7 @@ cargo-features = ["public-dependency"] [package] name = "test" version = "0.0.0" -edition = "2021" +edition = "2024" [dependencies] getopts = { version = "0.2.21", features = ['rustc-dep-of-std'] } diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index ef6786f431670..8840714a66238 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -61,7 +61,7 @@ fn optgroups() -> getopts::Options { .optopt("", "logfile", "Write logs to the specified file (deprecated)", "PATH") .optflag( "", - "nocapture", + "no-capture", "don't capture stdout/stderr of each \ task, allow printing directly", ) @@ -172,7 +172,7 @@ tests in the same order again. Note that --shuffle and --shuffle-seed do not affect whether the tests are run in parallel. All tests have their standard output and standard error captured by default. -This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE +This can be overridden with the --no-capture flag or setting RUST_TEST_NOCAPTURE environment variable to a value other than "0". Logging is not captured by default. Test Attributes: @@ -199,7 +199,10 @@ Test Attributes: /// otherwise creates a `TestOpts` object and returns it. pub fn parse_opts(args: &[String]) -> Option { // Parse matches. - let opts = optgroups(); + let mut opts = optgroups(); + // Flags hidden from `usage` + opts.optflag("", "nocapture", "Deprecated, use `--no-capture`"); + let binary = args.first().map(|c| &**c).unwrap_or("..."); let args = args.get(1..).unwrap_or(args); let matches = match opts.parse(args) { @@ -210,7 +213,7 @@ pub fn parse_opts(args: &[String]) -> Option { // Check if help was requested. if matches.opt_present("h") { // Show help and do nothing more. - usage(binary, &opts); + usage(binary, &optgroups()); return None; } @@ -447,7 +450,7 @@ fn get_color_config(matches: &getopts::Matches) -> OptPartRes { } fn get_nocapture(matches: &getopts::Matches) -> OptPartRes { - let mut nocapture = matches.opt_present("nocapture"); + let mut nocapture = matches.opt_present("nocapture") || matches.opt_present("no-capture"); if !nocapture { nocapture = match env::var("RUST_TEST_NOCAPTURE") { Ok(val) => &val != "0", diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 7ada3f269a002..7f56d1e362698 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -98,6 +98,15 @@ const SECONDARY_TEST_BENCH_BENCHMARKS_VAR: &str = "__RUST_TEST_BENCH_BENCHMARKS" // 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) { + test_main_with_exit_callback(args, tests, options, || {}) +} + +pub fn test_main_with_exit_callback( + args: &[String], + tests: Vec, + options: Option, + exit_callback: F, +) { let mut opts = match cli::parse_opts(args) { Some(Ok(o)) => o, Some(Err(msg)) => { @@ -151,6 +160,7 @@ pub fn test_main(args: &[String], tests: Vec, options: Option {} Ok(false) => process::exit(ERROR_EXIT_CODE), @@ -666,10 +676,11 @@ fn run_test_in_process( io::set_output_capture(None); - let test_result = match result { - Ok(()) => calc_result(&desc, Ok(()), time_opts.as_ref(), exec_time.as_ref()), - Err(e) => calc_result(&desc, Err(e.as_ref()), time_opts.as_ref(), exec_time.as_ref()), - }; + // Determine whether the test passed or failed, by comparing its panic + // payload (if any) with its `ShouldPanic` value, and by checking for + // fatal timeout. + let test_result = + calc_result(&desc, result.err().as_deref(), time_opts.as_ref(), exec_time.as_ref()); let stdout = data.lock().unwrap_or_else(|e| e.into_inner()).to_vec(); let message = CompletedTest::new(id, desc, test_result, exec_time, stdout); monitor_ch.send(message).unwrap(); @@ -741,10 +752,7 @@ fn spawn_test_subprocess( fn run_test_in_spawned_subprocess(desc: TestDesc, runnable_test: RunnableTest) -> ! { let builtin_panic_hook = panic::take_hook(); let record_result = Arc::new(move |panic_info: Option<&'_ PanicHookInfo<'_>>| { - let test_result = match panic_info { - Some(info) => calc_result(&desc, Err(info.payload()), None, None), - None => calc_result(&desc, Ok(()), None, None), - }; + let test_result = calc_result(&desc, panic_info.map(|info| info.payload()), None, None); // We don't support serializing TrFailedMsg, so just // print the message out to stderr. diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index 73dcc2e2a0cca..4cb43fc45fd6c 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -39,15 +39,18 @@ pub enum TestResult { /// Creates a `TestResult` depending on the raw result of test execution /// and associated data. -pub(crate) fn calc_result<'a>( +pub(crate) fn calc_result( desc: &TestDesc, - task_result: Result<(), &'a (dyn Any + 'static + Send)>, + panic_payload: Option<&(dyn Any + Send)>, time_opts: Option<&time::TestTimeOptions>, exec_time: Option<&time::TestExecTime>, ) -> TestResult { - let result = match (&desc.should_panic, task_result) { - (&ShouldPanic::No, Ok(())) | (&ShouldPanic::Yes, Err(_)) => TestResult::TrOk, - (&ShouldPanic::YesWithMessage(msg), Err(err)) => { + let result = match (desc.should_panic, panic_payload) { + // The test did or didn't panic, as expected. + (ShouldPanic::No, None) | (ShouldPanic::Yes, Some(_)) => TestResult::TrOk, + + // Check the actual panic message against the expected message. + (ShouldPanic::YesWithMessage(msg), Some(err)) => { let maybe_panic_str = err .downcast_ref::() .map(|e| &**e) @@ -58,23 +61,31 @@ pub(crate) fn calc_result<'a>( } else if let Some(panic_str) = maybe_panic_str { TestResult::TrFailedMsg(format!( r#"panic did not contain expected string - panic message: `{panic_str:?}`, - expected substring: `{msg:?}`"# + panic message: {panic_str:?} + expected substring: {msg:?}"# )) } else { TestResult::TrFailedMsg(format!( r#"expected panic with string value, found non-string value: `{:?}` - expected substring: `{:?}`"#, - (*err).type_id(), - msg + expected substring: {msg:?}"#, + (*err).type_id() )) } } - (&ShouldPanic::Yes, Ok(())) | (&ShouldPanic::YesWithMessage(_), Ok(())) => { - TestResult::TrFailedMsg("test did not panic as expected".to_string()) + + // The test should have panicked, but didn't panic. + (ShouldPanic::Yes, None) | (ShouldPanic::YesWithMessage(_), None) => { + let fn_location = if !desc.source_file.is_empty() { + &format!(" at {}:{}:{}", desc.source_file, desc.start_line, desc.start_col) + } else { + "" + }; + TestResult::TrFailedMsg(format!("test did not panic as expected{}", fn_location)) } - _ => TestResult::TrFailed, + + // The test should not have panicked, but did panic. + (ShouldPanic::No, Some(_)) => TestResult::TrFailed, }; // If test is already failed (or allowed to fail), do not change the result. diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index 47f581fefae1f..d986bd74f772b 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -200,8 +200,8 @@ fn test_should_panic_bad_message() { } let expected = "foobar"; let failed_msg = r#"panic did not contain expected string - panic message: `"an error message"`, - expected substring: `"foobar"`"#; + panic message: "an error message" + expected substring: "foobar""#; let desc = TestDescAndFn { desc: TestDesc { name: StaticTestName("whatever"), @@ -238,7 +238,7 @@ fn test_should_panic_non_string_message_type() { let failed_msg = format!( r#"expected panic with string value, found non-string value: `{:?}` - expected substring: `"foobar"`"#, + expected substring: "foobar""#, TypeId::of::() ); let desc = TestDescAndFn { diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index 66e8d1a3ffe5f..df43e6ae80fb0 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -3,7 +3,7 @@ name = "unwind" version = "0.0.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" -edition = "2021" +edition = "2024" include = [ '/libunwind/*', ] @@ -22,7 +22,7 @@ cfg-if = "1.0" libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false } [target.'cfg(target_os = "xous")'.dependencies] -unwinding = { version = "0.2.5", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false } +unwinding = { version = "0.2.6", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false } [features] diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 1a640bbde71d7..12582569a573b 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -27,10 +27,10 @@ pub type _Unwind_Trace_Fn = #[cfg(target_arch = "x86")] pub const unwinder_private_data_size: usize = 5; -#[cfg(all(target_arch = "x86_64", not(target_os = "windows")))] +#[cfg(all(target_arch = "x86_64", not(any(target_os = "windows", target_os = "cygwin"))))] pub const unwinder_private_data_size: usize = 2; -#[cfg(all(target_arch = "x86_64", target_os = "windows"))] +#[cfg(all(target_arch = "x86_64", any(target_os = "windows", target_os = "cygwin")))] pub const unwinder_private_data_size: usize = 6; #[cfg(all(target_arch = "arm", not(target_vendor = "apple")))] @@ -72,9 +72,12 @@ pub const unwinder_private_data_size: usize = 2; #[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))] pub const unwinder_private_data_size: usize = 2; -#[cfg(target_os = "emscripten")] +#[cfg(all(target_arch = "wasm32", target_os = "emscripten"))] pub const unwinder_private_data_size: usize = 20; +#[cfg(all(target_arch = "wasm32", target_os = "linux"))] +pub const unwinder_private_data_size: usize = 2; + #[cfg(all(target_arch = "hexagon", target_os = "linux"))] pub const unwinder_private_data_size: usize = 35; @@ -125,16 +128,13 @@ if #[cfg(any(target_vendor = "apple", target_os = "netbsd", not(target_arch = "a // // 32-bit ARM on iOS/tvOS/watchOS use either DWARF/Compact unwinding or // "setjmp-longjmp" / SjLj unwinding. - #[repr(C)] - #[derive(Copy, Clone, PartialEq)] - pub enum _Unwind_Action { - _UA_SEARCH_PHASE = 1, - _UA_CLEANUP_PHASE = 2, - _UA_HANDLER_FRAME = 4, - _UA_FORCE_UNWIND = 8, - _UA_END_OF_STACK = 16, - } - pub use _Unwind_Action::*; + pub type _Unwind_Action = c_int; + + pub const _UA_SEARCH_PHASE: c_int = 1; + pub const _UA_CLEANUP_PHASE: c_int = 2; + pub const _UA_HANDLER_FRAME: c_int = 4; + pub const _UA_FORCE_UNWIND: c_int = 8; + pub const _UA_END_OF_STACK: c_int = 16; #[cfg_attr( all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")), @@ -289,7 +289,10 @@ if #[cfg(all(target_vendor = "apple", not(target_os = "watchos"), target_arch = } // cfg_if! cfg_if::cfg_if! { -if #[cfg(all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), target_env = "gnu"))] { +if #[cfg(any( + all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), target_env = "gnu"), + target_os = "cygwin", + ))] { // We declare these as opaque types. This is fine since you just need to // pass them to _GCC_specific_handler and forget about them. pub enum EXCEPTION_RECORD {} diff --git a/library/unwind/src/unwinding.rs b/library/unwind/src/unwinding.rs index 1b94005ab6cd0..36120bc868ee9 100644 --- a/library/unwind/src/unwinding.rs +++ b/library/unwind/src/unwinding.rs @@ -2,16 +2,13 @@ use core::ffi::{c_int, c_void}; -#[repr(C)] -#[derive(Copy, Clone, PartialEq)] -pub enum _Unwind_Action { - _UA_SEARCH_PHASE = 1, - _UA_CLEANUP_PHASE = 2, - _UA_HANDLER_FRAME = 4, - _UA_FORCE_UNWIND = 8, - _UA_END_OF_STACK = 16, -} -pub use _Unwind_Action::*; +pub type _Unwind_Action = c_int; + +pub const _UA_SEARCH_PHASE: c_int = 1; +pub const _UA_CLEANUP_PHASE: c_int = 2; +pub const _UA_HANDLER_FRAME: c_int = 4; +pub const _UA_FORCE_UNWIND: c_int = 8; +pub const _UA_END_OF_STACK: c_int = 16; #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq)] @@ -39,9 +36,9 @@ pub type _Unwind_Exception_Class = u64; pub type _Unwind_Word = *const u8; pub type _Unwind_Ptr = *const u8; -pub const unwinder_private_data_size: usize = core::mem::size_of::() - - core::mem::size_of::<_Unwind_Exception_Class>() - - core::mem::size_of::<_Unwind_Exception_Cleanup_Fn>(); +pub const unwinder_private_data_size: usize = size_of::() + - size_of::<_Unwind_Exception_Class>() + - size_of::<_Unwind_Exception_Cleanup_Fn>(); pub type _Unwind_Exception_Cleanup_Fn = Option; diff --git a/library/windows_targets/Cargo.toml b/library/windows_targets/Cargo.toml index 94d7c8210647c..705c9e0438119 100644 --- a/library/windows_targets/Cargo.toml +++ b/library/windows_targets/Cargo.toml @@ -2,7 +2,7 @@ name = "windows-targets" description = "A drop-in replacement for the real windows-targets crate for use in std only." version = "0.0.0" -edition = "2021" +edition = "2024" [features] # Enable using raw-dylib for Windows imports. diff --git a/library/windows_targets/src/lib.rs b/library/windows_targets/src/lib.rs index e89bde8b1abf4..bce54c5ffcef0 100644 --- a/library/windows_targets/src/lib.rs +++ b/library/windows_targets/src/lib.rs @@ -12,7 +12,7 @@ pub macro link { ($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => ( #[cfg_attr(not(target_arch = "x86"), link(name = $library, kind = "raw-dylib", modifiers = "+verbatim"))] #[cfg_attr(target_arch = "x86", link(name = $library, kind = "raw-dylib", modifiers = "+verbatim", import_name_type = "undecorated"))] - extern $abi { + unsafe extern $abi { $(#[link_name=$link_name])? pub fn $($function)*; } @@ -26,7 +26,7 @@ pub macro link { // libraries below by using an empty extern block. This works because extern blocks are not // connected to the library given in the #[link] attribute. #[link(name = "kernel32")] - extern $abi { + unsafe extern $abi { $(#[link_name=$link_name])? pub fn $($function)*; } @@ -34,7 +34,8 @@ pub macro link { } #[cfg(not(feature = "windows_raw_dylib"))] -#[link(name = "advapi32")] +#[cfg(not(target_os = "cygwin"))] // Cygwin doesn't need these libs +#[cfg_attr(target_vendor = "win7", link(name = "advapi32"))] #[link(name = "ntdll")] #[link(name = "userenv")] #[link(name = "ws2_32")] diff --git a/rustfmt.toml b/rustfmt.toml index 8feeb60ca12c2..d9857a7e3e788 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -19,6 +19,7 @@ ignore = [ "/tests/debuginfo/", # These tests are somewhat sensitive to source code layout. "/tests/incremental/", # These tests are somewhat sensitive to source code layout. "/tests/pretty/", # These tests are very sensitive to source code layout. + "/tests/run-make/export", # These tests contain syntax errors. "/tests/run-make/translation/test.rs", # This test contains syntax errors. "/tests/rustdoc/", # Some have syntax errors, some are whitespace-sensitive. "/tests/rustdoc-gui/", # Some tests are sensitive to source code layout. @@ -49,9 +50,12 @@ ignore = [ # These are ignored by a standard cargo fmt run. "compiler/rustc_codegen_cranelift/scripts", - "compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs", # uses edition 2024 "compiler/rustc_codegen_gcc/tests", # Code automatically generated and included. "compiler/rustc_codegen_gcc/src/intrinsic/archs.rs", "compiler/rustc_codegen_gcc/example", + + # Rustfmt doesn't support use closures yet + "tests/mir-opt/ergonomic-clones/closure.rs", + "tests/codegen/ergonomic-clones/closure.rs", ] diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 890e64e2babbc..0c8e66335609b 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -56,6 +56,7 @@ dependencies = [ "sha2", "sysinfo", "tar", + "tempfile", "termcolor", "toml", "tracing", @@ -88,9 +89,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.22" +version = "1.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" dependencies = [ "shlex", ] @@ -150,19 +151,13 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "cmake" -version = "0.1.48" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" dependencies = [ "cc", ] -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - [[package]] name = "cpufeatures" version = "0.2.15" @@ -225,23 +220,29 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "fd-lock" -version = "4.0.2" +version = "4.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -266,6 +267,18 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + [[package]] name = "globset" version = "0.4.15" @@ -334,9 +347,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.167" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libredox" @@ -351,9 +364,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" [[package]] name = "log" @@ -415,6 +428,25 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "object" version = "0.36.5" @@ -486,6 +518,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "redox_syscall" version = "0.5.7" @@ -541,15 +579,15 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" -version = "0.38.40" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -656,14 +694,15 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.33.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "948512566b1895f93b1592c7574baeb2de842f224f2aab158799ecadb8ebbb46" +checksum = "b897c8ea620e181c7955369a31be5f48d9a9121cb59fd33ecef9ff2a34323422" dependencies = [ - "core-foundation-sys", "libc", "memchr", "ntapi", + "objc2-core-foundation", + "objc2-io-kit", "windows", ] @@ -678,6 +717,19 @@ dependencies = [ "xattr", ] +[[package]] +name = "tempfile" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -824,6 +876,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "winapi" version = "0.3.9" @@ -857,31 +918,54 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.57.0" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ "windows-core", - "windows-targets", ] [[package]] name = "windows-core" -version = "0.57.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ "windows-implement", "windows-interface", + "windows-link", "windows-result", - "windows-targets", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" +dependencies = [ + "windows-core", + "windows-link", ] [[package]] name = "windows-implement" -version = "0.57.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", @@ -890,22 +974,47 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.57.0" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core", + "windows-link", +] + [[package]] name = "windows-result" -version = "0.1.2" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ - "windows-targets", + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", ] [[package]] @@ -990,14 +1099,22 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + [[package]] name = "xattr" -version = "1.3.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" dependencies = [ "libc", - "linux-raw-sys", "rustix", ] diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 2c1d85b01e6af..b12b3dfc7b2a6 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bootstrap" version = "0.0.0" -edition = "2021" +edition = "2024" build = "build.rs" default-run = "bootstrap" @@ -28,19 +28,12 @@ name = "rustdoc" path = "src/bin/rustdoc.rs" test = false -[[bin]] -name = "sccache-plus-cl" -path = "src/bin/sccache-plus-cl.rs" -test = false - [dependencies] # Most of the time updating these dependencies requires modifications to the # bootstrap codebase(e.g., https://github.com/rust-lang/rust/issues/124565); # otherwise, some targets will fail. That's why these dependencies are explicitly pinned. -# -# Do not upgrade this crate unless https://github.com/rust-lang/cc-rs/issues/1317 is fixed. -cc = "=1.1.22" -cmake = "=0.1.48" +cc = "=1.2.23" +cmake = "=0.1.54" build_helper = { path = "../build_helper" } clap = { version = "4.4", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] } @@ -65,7 +58,7 @@ walkdir = "2.4" xz2 = "0.1" # Dependencies needed by the build-metrics feature -sysinfo = { version = "0.33.0", default-features = false, optional = true, features = ["system"] } +sysinfo = { version = "0.35.0", default-features = false, optional = true, features = ["system"] } # Dependencies needed by the `tracing` feature tracing = { version = "0.1", optional = true, features = ["attributes"] } @@ -77,7 +70,7 @@ tracing-tree = { version = "0.4.0", optional = true } version = "1.0.0" [target.'cfg(windows)'.dependencies.windows] -version = "0.57" +version = "0.61" features = [ "Win32_Foundation", "Win32_Security", @@ -90,6 +83,7 @@ features = [ [dev-dependencies] pretty_assertions = "1.4" +tempfile = "3.15.0" # We care a lot about bootstrap's compile times, so don't include debuginfo for # dependencies, only bootstrap itself. diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index f036603ee707b..f9d7c811f600a 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -163,7 +163,7 @@ compiler, which will then build the bootstrap binary written in Rust. Because there are two separate codebases behind `x.py`, they need to be kept in sync. In particular, both `bootstrap.py` and the bootstrap binary -parse `config.toml` and read the same command line arguments. `bootstrap.py` +parse `bootstrap.toml` and read the same command line arguments. `bootstrap.py` keeps these in sync by setting various environment variables, and the programs sometimes have to add arguments that are explicitly ignored, to be read by the other. diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 01a9792f1b375..42ad14a81d029 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -726,7 +726,7 @@ def download_toolchain(self): def should_fix_bins_and_dylibs(self): """Whether or not `fix_bin_or_dylib` needs to be run; can only be True - on NixOS or if config.toml has `build.patch-binaries-for-nix` set. + on NixOS or if bootstrap.toml has `build.patch-binaries-for-nix` set. """ if self._should_fix_bins_and_dylibs is not None: return self._should_fix_bins_and_dylibs @@ -775,7 +775,7 @@ def get_answer(): "The IN_NIX_SHELL environment variable is `{}`;".format( in_nix_shell ), - "you may need to set `patch-binaries-for-nix=true` in config.toml", + "you may need to set `patch-binaries-for-nix=true` in bootstrap.toml", ) return is_nixos @@ -884,7 +884,7 @@ def bin_root(self): return os.path.join(self.build_dir, self.build, subdir) def get_toml(self, key, section=None): - """Returns the value of the given key in config.toml, otherwise returns None + """Returns the value of the given key in bootstrap.toml, otherwise returns None >>> rb = RustBuild() >>> rb.config_toml = 'key1 = "value1"\\nkey2 = "value2"' @@ -1162,6 +1162,30 @@ def build_triple(self): config = self.get_toml("build") return config or default_build_triple(self.verbose) + def is_git_repository(self, repo_path): + return os.path.isdir(os.path.join(repo_path, ".git")) + + def get_latest_commit(self): + repo_path = self.rust_root + author_email = self.stage0_data.get("git_merge_commit_email") + if not self.is_git_repository(repo_path): + return "" + cmd = [ + "git", + "-C", + repo_path, + "rev-list", + "--author", + author_email, + "-n1", + "HEAD", + ] + try: + commit = subprocess.check_output(cmd, universal_newlines=True).strip() + return commit or "" + except subprocess.CalledProcessError: + return "" + def check_vendored_status(self): """Check that vendoring is configured properly""" # keep this consistent with the equivalent check in bootstrap: @@ -1174,7 +1198,8 @@ def check_vendored_status(self): eprint(" use vendored sources by default.") cargo_dir = os.path.join(self.rust_root, ".cargo") - url = "https://ci-artifacts.rust-lang.org/rustc-builds//rustc-nightly-src.tar.xz" + commit = self.get_latest_commit() + url = f"https://ci-artifacts.rust-lang.org/rustc-builds/{commit}/rustc-nightly-src.tar.xz" if self.use_vendored_sources: vendor_dir = os.path.join(self.rust_root, "vendor") if not os.path.exists(vendor_dir): @@ -1250,17 +1275,23 @@ def bootstrap(args): "unless you put them in place manually." ) - # Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then `./config.toml`, - # then `config.toml` in the root directory. + # Read from `--config` first, followed by `RUST_BOOTSTRAP_CONFIG`. + # If neither is set, check `./bootstrap.toml`, then `bootstrap.toml` in the root directory. + # If those are unavailable, fall back to `./config.toml`, then `config.toml` for + # backward compatibility. toml_path = args.config or os.getenv("RUST_BOOTSTRAP_CONFIG") using_default_path = toml_path is None if using_default_path: - toml_path = "config.toml" + toml_path = "bootstrap.toml" if not os.path.exists(toml_path): - toml_path = os.path.join(rust_root, toml_path) + toml_path = os.path.join(rust_root, "bootstrap.toml") + if not os.path.exists(toml_path): + toml_path = "config.toml" + if not os.path.exists(toml_path): + toml_path = os.path.join(rust_root, "config.toml") # Give a hard error if `--config` or `RUST_BOOTSTRAP_CONFIG` are set to a missing path, - # but not if `config.toml` hasn't been created. + # but not if `bootstrap.toml` hasn't been created. if not using_default_path or os.path.exists(toml_path): with open(toml_path) as config: config_toml = config.read() @@ -1278,7 +1309,9 @@ def bootstrap(args): # profiles to be renamed while maintaining back compatibility # Keep in sync with `profile_aliases` in config.rs profile_aliases = {"user": "dist"} - include_file = "config.{}.toml".format(profile_aliases.get(profile) or profile) + include_file = "bootstrap.{}.toml".format( + profile_aliases.get(profile) or profile + ) include_dir = os.path.join(rust_root, "src", "bootstrap", "defaults") include_path = os.path.join(include_dir, include_file) @@ -1298,7 +1331,7 @@ def bootstrap(args): build.check_vendored_status() if not os.path.exists(build.build_dir): - os.makedirs(build.build_dir) + os.makedirs(os.path.realpath(build.build_dir)) # Fetch/build the bootstrap build.download_toolchain() diff --git a/src/bootstrap/bootstrap_test.py b/src/bootstrap/bootstrap_test.py index 7494536539d7b..9e12982a43dc3 100644 --- a/src/bootstrap/bootstrap_test.py +++ b/src/bootstrap/bootstrap_test.py @@ -156,7 +156,7 @@ def test_known_args(self): class GenerateAndParseConfig(unittest.TestCase): - """Test that we can serialize and deserialize a config.toml file""" + """Test that we can serialize and deserialize a bootstrap.toml file""" def test_no_args(self): build = serialize_and_parse([]) @@ -206,11 +206,11 @@ def build_args(self, configure_args=None, args=None, env=None): # problem in most cases, but there is a scenario where it would cause # the test to fail. # - # When a custom local Cargo is configured in config.toml (with the + # When a custom local Cargo is configured in bootstrap.toml (with the # build.cargo setting), no Cargo is downloaded to any location known by # bootstrap, and bootstrap relies on that setting to find it. # - # In this test though we are not using the config.toml of the caller: + # In this test though we are not using the bootstrap.toml of the caller: # we are generating a blank one instead. If we don't set build.cargo in # it, the test will have no way to find Cargo, failing the test. cargo_bin = os.environ.get("BOOTSTRAP_TEST_CARGO_BIN") diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index e3f58d97cbc4c..0d4d6e0ff54c6 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -249,6 +249,11 @@ def v(*args): "target.mips64el-unknown-linux-muslabi64.musl-root", "mips64el-unknown-linux-muslabi64 install directory", ) +v( + "musl-root-powerpc64", + "target.powerpc64-unknown-linux-musl.musl-root", + "powerpc64-unknown-linux-musl install directory", +) v( "musl-root-powerpc64le", "target.powerpc64le-unknown-linux-musl.musl-root", @@ -269,6 +274,11 @@ def v(*args): "target.loongarch64-unknown-linux-musl.musl-root", "loongarch64-unknown-linux-musl install directory", ) +v( + "musl-root-wali-wasm32", + "target.wasm32-wali-linux-musl.musl-root", + "wasm32-wali-linux-musl install directory", +) v( "qemu-armhf-rootfs", "target.arm-unknown-linux-gnueabihf.qemu-rootfs", @@ -292,7 +302,7 @@ def v(*args): v("release-channel", "rust.channel", "the name of the release channel to build") v( "release-description", - "rust.description", + "build.description", "optional descriptive string for version output", ) v("dist-compression-formats", None, "List of compression formats to use") @@ -362,8 +372,8 @@ def is_value_list(key): print("\t\t" + option.desc) print("") print("This configure script is a thin configuration shim over the true") - print("configuration system, `config.toml`. You can explore the comments") - print("in `config.example.toml` next to this configure script to see") + print("configuration system, `bootstrap.toml`. You can explore the comments") + print("in `bootstrap.example.toml` next to this configure script to see") print("more information about what each option is. Additionally you can") print("pass `--set` as an argument to set arbitrary key/value pairs") print("in the TOML configuration if desired") @@ -562,8 +572,8 @@ def apply_args(known_args, option_checking, config): raise RuntimeError("unhandled option {}".format(option.name)) -# "Parse" the `config.example.toml` file into the various sections, and we'll -# use this as a template of a `config.toml` to write out which preserves +# "Parse" the `bootstrap.example.toml` file into the various sections, and we'll +# use this as a template of a `bootstrap.toml` to write out which preserves # all the various comments and whatnot. # # Note that the `target` section is handled separately as we'll duplicate it @@ -576,7 +586,7 @@ def parse_example_config(known_args, config): targets = {} top_level_keys = [] - with open(rust_dir + "/config.example.toml") as example_config: + with open(rust_dir + "/bootstrap.example.toml") as example_config: example_lines = example_config.read().split("\n") for line in example_lines: if cur_section is None: @@ -750,8 +760,8 @@ def quit_if_file_exists(file): if __name__ == "__main__": - # If 'config.toml' already exists, exit the script at this point - quit_if_file_exists("config.toml") + # If 'bootstrap.toml' already exists, exit the script at this point + quit_if_file_exists("bootstrap.toml") if "GITHUB_ACTIONS" in os.environ: print("::group::Configure the build") @@ -761,11 +771,11 @@ def quit_if_file_exists(file): p("") section_order, sections, targets = parse_args(sys.argv[1:]) - # Now that we've built up our `config.toml`, write it all out in the same + # Now that we've built up our `bootstrap.toml`, write it all out in the same # order that we read it in. p("") - p("writing `config.toml` in current directory") - with bootstrap.output("config.toml") as f: + p("writing `bootstrap.toml` in current directory") + with bootstrap.output("bootstrap.toml") as f: write_config_toml(f, section_order, targets, sections) with bootstrap.output("Makefile") as f: diff --git a/src/bootstrap/defaults/bootstrap.compiler.toml b/src/bootstrap/defaults/bootstrap.compiler.toml new file mode 100644 index 0000000000000..9dcb20ed48323 --- /dev/null +++ b/src/bootstrap/defaults/bootstrap.compiler.toml @@ -0,0 +1,35 @@ +# These defaults are meant for contributors to the compiler who do not modify codegen or LLVM +[build] +# Contributors working on the compiler will probably expect compiler docs to be generated. +compiler-docs = true + +[rust] +# This enables `RUSTC_LOG=debug`, avoiding confusing situations +# where adding `debug!()` appears to do nothing. +# However, it makes running the compiler slightly slower. +debug-logging = true +# Enables debug assertions, which guard from many mistakes when working on the compiler. +debug-assertions = true +# Get actually-useful information from backtraces, profiling, etc. with minimal added bytes +debuginfo-level = "line-tables-only" +# This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. +incremental = true +# Print backtrace on internal compiler errors during bootstrap +backtrace-on-ice = true +# Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown. +lto = "off" +# Forces frame pointers to be used with `-Cforce-frame-pointers`. +# This can be helpful for profiling at a small performance cost. +frame-pointers = true +# Compiler contributors often want to build rustc even without any changes to +# e.g. check that it builds locally and check the baseline behavior of a +# compiler built from latest `master` commit. +download-rustc = false + +[llvm] +# Having this set to true disrupts compiler development workflows for people who use `llvm.download-ci-llvm = true` +# because we don't provide ci-llvm on the `rustc-alt-builds` server. Therefore, it is kept off by default. +assertions = false +# Will download LLVM from CI if available on your platform. +# If you intend to modify `src/llvm-project`, use `"if-unchanged"` or `false` instead. +download-ci-llvm = true diff --git a/src/bootstrap/defaults/bootstrap.dist.toml b/src/bootstrap/defaults/bootstrap.dist.toml new file mode 100644 index 0000000000000..f0cb34eb45856 --- /dev/null +++ b/src/bootstrap/defaults/bootstrap.dist.toml @@ -0,0 +1,29 @@ +# These defaults are meant for users and distro maintainers building from source, without intending to make multiple changes. +[build] +# When compiling from source, you almost always want a full stage 2 build, +# which has all the latest optimizations from nightly. +build-stage = 2 +test-stage = 2 +doc-stage = 2 +# When compiling from source, you usually want all tools. +extended = true +# Use libtest built from the source tree instead of the precompiled one from stage 0. +compiletest-use-stage0-libtest = false + +# Most users installing from source want to build all parts of the project from source. +[llvm] +download-ci-llvm = false + +[rust] +# We have several defaults in bootstrap that depend on whether the channel is `dev` (e.g. `omit-git-hash` and `download-ci-llvm`). +# Make sure they don't get set when installing from source. +channel = "auto-detect" +# Never download a rustc, distributions must build a fresh compiler. +download-rustc = false +lld = true +# Build the llvm-bitcode-linker +llvm-bitcode-linker = true + +[dist] +# Use better compression when preparing tarballs. +compression-profile = "balanced" diff --git a/src/bootstrap/defaults/config.library.toml b/src/bootstrap/defaults/bootstrap.library.toml similarity index 100% rename from src/bootstrap/defaults/config.library.toml rename to src/bootstrap/defaults/bootstrap.library.toml diff --git a/src/bootstrap/defaults/config.tools.toml b/src/bootstrap/defaults/bootstrap.tools.toml similarity index 100% rename from src/bootstrap/defaults/config.tools.toml rename to src/bootstrap/defaults/bootstrap.tools.toml diff --git a/src/bootstrap/defaults/config.compiler.toml b/src/bootstrap/defaults/config.compiler.toml deleted file mode 100644 index 269b90106e3aa..0000000000000 --- a/src/bootstrap/defaults/config.compiler.toml +++ /dev/null @@ -1,37 +0,0 @@ -# These defaults are meant for contributors to the compiler who do not modify codegen or LLVM -[build] -# Contributors working on the compiler will probably expect compiler docs to be generated. -compiler-docs = true - -[rust] -# This enables `RUSTC_LOG=debug`, avoiding confusing situations -# where adding `debug!()` appears to do nothing. -# However, it makes running the compiler slightly slower. -debug-logging = true -# Enables debug assertions, which guard from many mistakes when working on the compiler. -debug-assertions = true -# Get actually-useful information from backtraces, profiling, etc. with minimal added bytes -debuginfo-level = "line-tables-only" -# This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. -incremental = true -# Print backtrace on internal compiler errors during bootstrap -backtrace-on-ice = true -# Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown. -lto = "off" -# Forces frame pointers to be used with `-Cforce-frame-pointers`. -# This can be helpful for profiling at a small performance cost. -frame-pointers = true -# Compiler contributors often want to build rustc even without any changes to -# e.g. check that it builds locally and check the baseline behavior of a -# compiler built from latest `master` commit. -download-rustc = false - -[llvm] -# Having this set to true disrupts compiler development workflows for people who use `llvm.download-ci-llvm = true` -# because we don't provide ci-llvm on the `rustc-alt-builds` server. Therefore, it is kept off by default. -assertions = false -# Enable warnings during the LLVM compilation (when LLVM is changed, causing a compilation) -enable-warnings = true -# Will download LLVM from CI if available on your platform. -# If you intend to modify `src/llvm-project`, use `"if-unchanged"` or `false` instead. -download-ci-llvm = true diff --git a/src/bootstrap/defaults/config.dist.toml b/src/bootstrap/defaults/config.dist.toml deleted file mode 100644 index 7b381b416ca8a..0000000000000 --- a/src/bootstrap/defaults/config.dist.toml +++ /dev/null @@ -1,27 +0,0 @@ -# These defaults are meant for users and distro maintainers building from source, without intending to make multiple changes. -[build] -# When compiling from source, you almost always want a full stage 2 build, -# which has all the latest optimizations from nightly. -build-stage = 2 -test-stage = 2 -doc-stage = 2 -# When compiling from source, you usually want all tools. -extended = true - -# Most users installing from source want to build all parts of the project from source. -[llvm] -download-ci-llvm = false - -[rust] -# We have several defaults in bootstrap that depend on whether the channel is `dev` (e.g. `omit-git-hash` and `download-ci-llvm`). -# Make sure they don't get set when installing from source. -channel = "nightly" -# Never download a rustc, distributions must build a fresh compiler. -download-rustc = false -lld = true -# Build the llvm-bitcode-linker -llvm-bitcode-linker = true - -[dist] -# Use better compression when preparing tarballs. -compression-profile = "balanced" diff --git a/src/bootstrap/download-ci-gcc-stamp b/src/bootstrap/download-ci-gcc-stamp new file mode 100644 index 0000000000000..bbe26afc95269 --- /dev/null +++ b/src/bootstrap/download-ci-gcc-stamp @@ -0,0 +1,4 @@ +Change this file to make users of the `download-ci-gcc` configuration download +a new version of GCC from CI, even if the GCC submodule hasn’t changed. + +Last change is for: https://github.com/rust-lang/rust/pull/138051 diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index d2c0d380fb40b..b70d452b427c8 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/134740 +Last change is for: https://github.com/rust-lang/rust/pull/139931 diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 88aa70d4f2f83..08a288170fa30 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -56,6 +56,7 @@ check-aux: # Run standard library tests in Miri. $(Q)$(BOOTSTRAP) miri --stage 2 \ library/coretests \ + library/alloctests \ library/alloc \ $(BOOTSTRAP_ARGS) \ --no-doc @@ -63,6 +64,7 @@ check-aux: $(Q)MIRIFLAGS="-Zmiri-disable-isolation" \ $(BOOTSTRAP) miri --stage 2 \ library/coretests \ + library/alloctests \ library/alloc \ $(BOOTSTRAP_ARGS) \ --doc @@ -71,12 +73,12 @@ check-aux: $(BOOTSTRAP) miri --stage 2 library/std \ $(BOOTSTRAP_ARGS) \ --no-doc -- \ - --skip fs:: --skip net:: --skip process:: --skip sys::pal:: + --skip fs:: --skip net:: --skip process:: --skip sys::fd:: --skip sys::pal:: $(Q)MIRIFLAGS="-Zmiri-disable-isolation" \ $(BOOTSTRAP) miri --stage 2 library/std \ $(BOOTSTRAP_ARGS) \ --doc -- \ - --skip fs:: --skip net:: --skip process:: --skip sys::pal:: + --skip fs:: --skip net:: --skip process:: --skip sys::fd:: --skip sys::pal:: # Also test some very target-specific modules on other targets # (making sure to cover an i686 target as well). $(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ @@ -118,10 +120,6 @@ ci-msvc: ci-msvc-py ci-msvc-ps1 # Set of tests that should represent half of the time of the test suite. # Used to split tests across multiple CI runners. # Test both x and bootstrap entrypoints. -ci-mingw-x-1: - $(Q)$(CFG_SRC_DIR)/x test --stage 2 $(SKIP_COMPILER) $(TEST_SET2) -ci-mingw-x-2: - $(Q)$(CFG_SRC_DIR)/x test --stage 2 $(SKIP_SRC) $(TEST_SET2) ci-mingw-x: $(Q)$(CFG_SRC_DIR)/x test --stage 2 $(TEST_SET1) ci-mingw-bootstrap: diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 38b380e3db824..833f80279517a 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -11,10 +11,9 @@ use std::str::FromStr; use std::{env, process}; use bootstrap::{ - Build, CONFIG_CHANGE_HISTORY, Config, Flags, Subcommand, debug, find_recent_config_change_ids, - human_readable_changes, t, + Build, CONFIG_CHANGE_HISTORY, ChangeId, Config, Flags, Subcommand, debug, + find_recent_config_change_ids, human_readable_changes, t, }; -use build_helper::ci::CiEnv; #[cfg(feature = "tracing")] use tracing::instrument; @@ -70,20 +69,23 @@ fn main() { } // check_version warnings are not printed during setup, or during CI - let changelog_suggestion = if matches!(config.cmd, Subcommand::Setup { .. }) || CiEnv::is_ci() { + let changelog_suggestion = if matches!(config.cmd, Subcommand::Setup { .. }) + || config.is_running_on_ci + || config.dry_run() + { None } else { check_version(&config) }; - // NOTE: Since `./configure` generates a `config.toml`, distro maintainers will see the + // NOTE: Since `./configure` generates a `bootstrap.toml`, distro maintainers will see the // changelog warning, not the `x.py setup` message. let suggest_setup = config.config.is_none() && !matches!(config.cmd, Subcommand::Setup { .. }); if suggest_setup { - println!("WARNING: you have not made a `config.toml`"); + println!("WARNING: you have not made a `bootstrap.toml`"); println!( - "HELP: consider running `./x.py setup` or copying `config.example.toml` by running \ - `cp config.example.toml config.toml`" + "HELP: consider running `./x.py setup` or copying `bootstrap.example.toml` by running \ + `cp bootstrap.example.toml bootstrap.toml`" ); } else if let Some(suggestion) = &changelog_suggestion { println!("{suggestion}"); @@ -97,10 +99,10 @@ fn main() { Build::new(config).build(); if suggest_setup { - println!("WARNING: you have not made a `config.toml`"); + println!("WARNING: you have not made a `bootstrap.toml`"); println!( - "HELP: consider running `./x.py setup` or copying `config.example.toml` by running \ - `cp config.example.toml config.toml`" + "HELP: consider running `./x.py setup` or copying `bootstrap.example.toml` by running \ + `cp bootstrap.example.toml bootstrap.toml`" ); } else if let Some(suggestion) = &changelog_suggestion { println!("{suggestion}"); @@ -153,48 +155,52 @@ fn check_version(config: &Config) -> Option { let latest_change_id = CONFIG_CHANGE_HISTORY.last().unwrap().change_id; let warned_id_path = config.out.join("bootstrap").join(".last-warned-change-id"); - if let Some(mut id) = config.change_id { - if id == latest_change_id { - return None; + let mut id = match config.change_id { + Some(ChangeId::Id(id)) if id == latest_change_id => return None, + Some(ChangeId::Ignore) => return None, + Some(ChangeId::Id(id)) => id, + None => { + msg.push_str("WARNING: The `change-id` is missing in the `bootstrap.toml`. This means that you will not be able to track the major changes made to the bootstrap configurations.\n"); + msg.push_str("NOTE: to silence this warning, "); + msg.push_str(&format!( + "add `change-id = {latest_change_id}` or `change-id = \"ignore\"` at the top of `bootstrap.toml`" + )); + return Some(msg); } + }; - // Always try to use `change-id` from .last-warned-change-id first. If it doesn't exist, - // then use the one from the config.toml. This way we never show the same warnings - // more than once. - if let Ok(t) = fs::read_to_string(&warned_id_path) { - let last_warned_id = usize::from_str(&t) - .unwrap_or_else(|_| panic!("{} is corrupted.", warned_id_path.display())); - - // We only use the last_warned_id if it exists in `CONFIG_CHANGE_HISTORY`. - // Otherwise, we may retrieve all the changes if it's not the highest value. - // For better understanding, refer to `change_tracker::find_recent_config_change_ids`. - if CONFIG_CHANGE_HISTORY.iter().any(|config| config.change_id == last_warned_id) { - id = last_warned_id; - } - }; + // Always try to use `change-id` from .last-warned-change-id first. If it doesn't exist, + // then use the one from the bootstrap.toml. This way we never show the same warnings + // more than once. + if let Ok(t) = fs::read_to_string(&warned_id_path) { + let last_warned_id = usize::from_str(&t) + .unwrap_or_else(|_| panic!("{} is corrupted.", warned_id_path.display())); + + // We only use the last_warned_id if it exists in `CONFIG_CHANGE_HISTORY`. + // Otherwise, we may retrieve all the changes if it's not the highest value. + // For better understanding, refer to `change_tracker::find_recent_config_change_ids`. + if CONFIG_CHANGE_HISTORY.iter().any(|config| config.change_id == last_warned_id) { + id = last_warned_id; + } + }; - let changes = find_recent_config_change_ids(id); + let changes = find_recent_config_change_ids(id); - if changes.is_empty() { - return None; - } + if changes.is_empty() { + return None; + } - msg.push_str("There have been changes to x.py since you last updated:\n"); - msg.push_str(&human_readable_changes(&changes)); + msg.push_str("There have been changes to x.py since you last updated:\n"); + msg.push_str(&human_readable_changes(changes)); - msg.push_str("NOTE: to silence this warning, "); - msg.push_str(&format!( - "update `config.toml` to use `change-id = {latest_change_id}` instead" - )); + msg.push_str("NOTE: to silence this warning, "); + msg.push_str(&format!( + "update `bootstrap.toml` to use `change-id = {latest_change_id}` or `change-id = \"ignore\"` instead" + )); - if io::stdout().is_terminal() && !config.dry_run() { - t!(fs::write(warned_id_path, latest_change_id.to_string())); - } - } else { - msg.push_str("WARNING: The `change-id` is missing in the `config.toml`. This means that you will not be able to track the major changes made to the bootstrap configurations.\n"); - msg.push_str("NOTE: to silence this warning, "); - msg.push_str(&format!("add `change-id = {latest_change_id}` at the top of `config.toml`")); - }; + if io::stdout().is_terminal() { + t!(fs::write(warned_id_path, latest_change_id.to_string())); + } Some(msg) } diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 6104506759282..374884d8a9a07 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -58,8 +58,8 @@ fn main() { let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set"); let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new); - let rustc_real = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc)); - let libdir = env::var_os(libdir).unwrap_or_else(|| panic!("{:?} was not set", libdir)); + let rustc_real = env::var_os(rustc).unwrap_or_else(|| panic!("{rustc:?} was not set")); + let libdir = env::var_os(libdir).unwrap_or_else(|| panic!("{libdir:?} was not set")); let mut dylib_path = dylib_path(); dylib_path.insert(0, PathBuf::from(&libdir)); @@ -342,14 +342,14 @@ fn format_rusage_data(child: Child) -> Option { use windows::Win32::System::Threading::GetProcessTimes; use windows::Win32::System::Time::FileTimeToSystemTime; - let handle = HANDLE(child.as_raw_handle() as isize); + let handle = HANDLE(child.as_raw_handle()); let mut user_filetime = Default::default(); let mut user_time = Default::default(); let mut kernel_filetime = Default::default(); let mut kernel_time = Default::default(); let mut memory_counters = PROCESS_MEMORY_COUNTERS::default(); - let memory_counters_size = std::mem::size_of_val(&memory_counters); + let memory_counters_size = size_of_val(&memory_counters); unsafe { GetProcessTimes( diff --git a/src/bootstrap/src/bin/sccache-plus-cl.rs b/src/bootstrap/src/bin/sccache-plus-cl.rs deleted file mode 100644 index 6e87d4222e863..0000000000000 --- a/src/bootstrap/src/bin/sccache-plus-cl.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::env; -use std::process::{self, Command}; - -fn main() { - let target = env::var("SCCACHE_TARGET").unwrap(); - // Locate the actual compiler that we're invoking - env::set_var("CC", env::var_os("SCCACHE_CC").unwrap()); - env::set_var("CXX", env::var_os("SCCACHE_CXX").unwrap()); - let mut cfg = cc::Build::new(); - cfg.cargo_metadata(false) - .out_dir("/") - .target(&target) - .host(&target) - .opt_level(0) - .warnings(false) - .debug(false); - let compiler = cfg.get_compiler(); - - // Invoke sccache with said compiler - let sccache_path = env::var_os("SCCACHE_PATH").unwrap(); - let mut cmd = Command::new(sccache_path); - cmd.arg(compiler.path()); - for (k, v) in compiler.env() { - cmd.env(k, v); - } - for arg in env::args().skip(1) { - cmd.arg(arg); - } - - if let Ok(s) = env::var("SCCACHE_EXTRA_ARGS") { - for s in s.split_whitespace() { - cmd.arg(s); - } - } - - let status = cmd.status().expect("failed to spawn"); - process::exit(status.code().unwrap_or(2)) -} diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index b8bbe1eb5f8a6..fa848c492b4d2 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -3,7 +3,7 @@ use crate::core::build_steps::compile::{ add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make, }; -use crate::core::build_steps::tool::{SourceType, prepare_tool_cargo}; +use crate::core::build_steps::tool::{COMPILETEST_ALLOW_FEATURES, SourceType, prepare_tool_cargo}; use crate::core::builder::{ self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, crate_description, }; @@ -45,7 +45,10 @@ impl Step for Std { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.crate_or_deps("sysroot").crate_or_deps("coretests").path("library") + run.crate_or_deps("sysroot") + .crate_or_deps("coretests") + .crate_or_deps("alloctests") + .path("library") } fn make_run(run: RunConfig<'_>) { @@ -366,6 +369,69 @@ impl Step for RustAnalyzer { } } +/// Compiletest is implicitly "checked" when it gets built in order to run tests, +/// so this is mainly for people working on compiletest to run locally. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Compiletest { + pub target: TargetSelection, +} + +impl Step for Compiletest { + type Output = (); + const ONLY_HOSTS: bool = true; + const DEFAULT: bool = false; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/compiletest") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Compiletest { target: run.target }); + } + + fn run(self, builder: &Builder<'_>) { + let mode = if builder.config.compiletest_use_stage0_libtest { + Mode::ToolBootstrap + } else { + Mode::ToolStd + }; + + let compiler = builder.compiler( + if mode == Mode::ToolBootstrap { 0 } else { builder.top_stage }, + builder.config.build, + ); + + if mode != Mode::ToolBootstrap { + builder.ensure(Rustc::new(self.target, builder)); + } + + let mut cargo = prepare_tool_cargo( + builder, + compiler, + mode, + self.target, + builder.kind, + "src/tools/compiletest", + SourceType::InTree, + &[], + ); + + cargo.allow_features(COMPILETEST_ALLOW_FEATURES); + + // For ./x.py clippy, don't run with --all-targets because + // linting tests and benchmarks can produce very noisy results + if builder.kind != Kind::Clippy { + cargo.arg("--all-targets"); + } + + let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, self.target)) + .with_prefix("compiletest-check"); + + let _guard = builder.msg_check("compiletest artifacts", self.target); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); + } +} + macro_rules! tool_check_step { ( $name:ident { @@ -451,7 +517,6 @@ tool_check_step!(Rustdoc { path: "src/tools/rustdoc", alt_path: "src/librustdoc" tool_check_step!(Clippy { path: "src/tools/clippy" }); tool_check_step!(Miri { path: "src/tools/miri" }); tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri" }); -tool_check_step!(Rls { path: "src/tools/rls" }); tool_check_step!(Rustfmt { path: "src/tools/rustfmt" }); tool_check_step!(MiroptTestTools { path: "src/tools/miropt-test-tools" }); tool_check_step!(TestFloatParse { path: "src/etc/test-float-parse" }); @@ -463,6 +528,69 @@ tool_check_step!(Bootstrap { path: "src/bootstrap", default: false }); // check to make it easier to work on. tool_check_step!(RunMakeSupport { path: "src/tools/run-make-support", default: false }); -// Compiletest is implicitly "checked" when it gets built in order to run tests, -// so this is mainly for people working on compiletest to run locally. -tool_check_step!(Compiletest { path: "src/tools/compiletest", default: false }); +/// Check step for the `coverage-dump` bootstrap tool. The coverage-dump tool +/// is used internally by coverage tests. +/// +/// FIXME(Zalathar): This is temporarily separate from the other tool check +/// steps so that it can use the stage 0 compiler instead of `top_stage`, +/// without introducing conflicts with the stage 0 redesign (#119899). +/// +/// After the stage 0 redesign lands, we can look into using the stage 0 +/// compiler to check all bootstrap tools (#139170). +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub(crate) struct CoverageDump; + +impl CoverageDump { + const PATH: &str = "src/tools/coverage-dump"; +} + +impl Step for CoverageDump { + type Output = (); + + /// Most contributors won't care about coverage-dump, so don't make their + /// check builds slower unless they opt in and check it explicitly. + const DEFAULT: bool = false; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path(Self::PATH) + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Self {}); + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + // Make sure we haven't forgotten any fields, if there are any. + let Self {} = self; + let display_name = "coverage-dump"; + let host = builder.config.build; + let target = host; + let mode = Mode::ToolBootstrap; + + let compiler = builder.compiler(0, host); + let cargo = prepare_tool_cargo( + builder, + compiler, + mode, + target, + builder.kind, + Self::PATH, + SourceType::InTree, + &[], + ); + + let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, target)) + .with_prefix(&format!("{display_name}-check")); + + let _guard = builder.msg_tool( + builder.kind, + mode, + display_name, + compiler.stage, + &compiler.host, + &target, + ); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); + } +} diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index 21e9fea936387..882fcd087800b 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -129,7 +129,7 @@ fn clean_specific_stage(build: &Build, stage: u32) { for entry in entries { let entry = t!(entry); - let stage_prefix = format!("stage{}", stage); + let stage_prefix = format!("stage{stage}"); // if current entry is not related with the target stage, continue if !entry.file_name().to_str().unwrap_or("").contains(&stage_prefix) { diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index fe8c89f7a5394..07fd51919d44f 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -59,7 +59,7 @@ fn lint_args(builder: &Builder<'_>, config: &LintConfig, ignored_rules: &[&str]) let all_args = std::env::args().collect::>(); args.extend(get_clippy_rules_in_order(&all_args, config)); - args.extend(ignored_rules.iter().map(|lint| format!("-Aclippy::{}", lint))); + args.extend(ignored_rules.iter().map(|lint| format!("-Aclippy::{lint}"))); args.extend(builder.config.free_args.clone()); args } @@ -346,7 +346,6 @@ lint_any!( OptDist, "src/tools/opt-dist", "opt-dist"; RemoteTestClient, "src/tools/remote-test-client", "remote-test-client"; RemoteTestServer, "src/tools/remote-test-server", "remote-test-server"; - Rls, "src/tools/rls", "rls"; RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer"; Rustdoc, "src/librustdoc", "clippy"; Rustfmt, "src/tools/rustfmt", "rustfmt"; diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index d645d922ef403..d5ea96b43f56d 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -33,7 +33,7 @@ use crate::utils::exec::command; use crate::utils::helpers::{ exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, }; -use crate::{CLang, Compiler, DependencyType, GitRepo, LLVM_TOOLS, Mode, debug, trace}; +use crate::{CLang, Compiler, DependencyType, FileType, GitRepo, LLVM_TOOLS, Mode, debug, trace}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Std { @@ -67,7 +67,7 @@ impl Std { self } - #[allow(clippy::wrong_self_convention)] + #[expect(clippy::wrong_self_convention)] pub fn is_for_mir_opt_tests(mut self, is_for_mir_opt_tests: bool) -> Self { self.is_for_mir_opt_tests = is_for_mir_opt_tests; self @@ -111,14 +111,10 @@ impl Step for Std { // the `rust.download-rustc=true` option. let force_recompile = builder.rust_info().is_managed_git_subrepository() && builder.download_rustc() - && builder.config.last_modified_commit(&["library"], "download-rustc", true).is_none(); + && builder.config.has_changes_from_upstream(&["library"]); trace!("is managed git repo: {}", builder.rust_info().is_managed_git_subrepository()); trace!("download_rustc: {}", builder.download_rustc()); - trace!( - "last modified commit: {:?}", - builder.config.last_modified_commit(&["library"], "download-rustc", true) - ); trace!(force_recompile); run.builder.ensure(Std { @@ -155,7 +151,7 @@ impl Step for Std { // When using `download-rustc`, we already have artifacts for the host available. Don't // recompile them. - if builder.download_rustc() && builder.is_builder_target(target) + if builder.download_rustc() && builder.config.is_host_target(target) // NOTE: the beta compiler may generate different artifacts than the downloaded compiler, so // its artifacts can't be reused. && compiler.stage != 0 @@ -194,11 +190,7 @@ impl Step for Std { trace!(?compiler_to_use); if compiler_to_use != compiler { - trace!( - ?compiler_to_use, - ?compiler, - "compiler != compiler_to_use, handling cross-compile scenario" - ); + trace!(?compiler_to_use, ?compiler, "compiler != compiler_to_use, uplifting library"); builder.ensure(Std::new(compiler_to_use, target)); let msg = if compiler_to_use.host == target { @@ -233,7 +225,7 @@ impl Step for Std { // The LLD wrappers and `rust-lld` are self-contained linking components that can be // necessary to link the stdlib on some targets. We'll also need to copy these binaries to // the `stage0-sysroot` to ensure the linker is found when bootstrapping on such a target. - if compiler.stage == 0 && builder.is_builder_target(compiler.host) { + if compiler.stage == 0 && builder.config.is_host_target(compiler.host) { trace!( "(build == host) copying linking components to `stage0-sysroot` for bootstrapping" ); @@ -325,7 +317,7 @@ fn copy_and_stamp( dependency_type: DependencyType, ) { let target = libdir.join(name); - builder.copy_link(&sourcedir.join(name), &target); + builder.copy_link(&sourcedir.join(name), &target, FileType::Regular); target_deps.push((target, dependency_type)); } @@ -334,7 +326,7 @@ fn copy_llvm_libunwind(builder: &Builder<'_>, target: TargetSelection, libdir: & let libunwind_path = builder.ensure(llvm::Libunwind { target }); let libunwind_source = libunwind_path.join("libunwind.a"); let libunwind_target = libdir.join("libunwind.a"); - builder.copy_link(&libunwind_source, &libunwind_target); + builder.copy_link(&libunwind_source, &libunwind_target, FileType::NativeLibrary); libunwind_target } @@ -390,24 +382,38 @@ fn copy_self_contained_objects( let srcdir = builder.musl_libdir(target).unwrap_or_else(|| { panic!("Target {:?} does not have a \"musl-libdir\" key", target.triple) }); - for &obj in &["libc.a", "crt1.o", "Scrt1.o", "rcrt1.o", "crti.o", "crtn.o"] { - copy_and_stamp( - builder, - &libdir_self_contained, - &srcdir, - obj, - &mut target_deps, - DependencyType::TargetSelfContained, - ); - } - let crt_path = builder.ensure(llvm::CrtBeginEnd { target }); - for &obj in &["crtbegin.o", "crtbeginS.o", "crtend.o", "crtendS.o"] { - let src = crt_path.join(obj); - let target = libdir_self_contained.join(obj); - builder.copy_link(&src, &target); - target_deps.push((target, DependencyType::TargetSelfContained)); + if !target.starts_with("wasm32") { + for &obj in &["libc.a", "crt1.o", "Scrt1.o", "rcrt1.o", "crti.o", "crtn.o"] { + copy_and_stamp( + builder, + &libdir_self_contained, + &srcdir, + obj, + &mut target_deps, + DependencyType::TargetSelfContained, + ); + } + let crt_path = builder.ensure(llvm::CrtBeginEnd { target }); + for &obj in &["crtbegin.o", "crtbeginS.o", "crtend.o", "crtendS.o"] { + let src = crt_path.join(obj); + let target = libdir_self_contained.join(obj); + builder.copy_link(&src, &target, FileType::NativeLibrary); + target_deps.push((target, DependencyType::TargetSelfContained)); + } + } else { + // For wasm32 targets, we need to copy the libc.a and crt1-command.o files from the + // musl-libdir, but we don't need the other files. + for &obj in &["libc.a", "crt1-command.o"] { + copy_and_stamp( + builder, + &libdir_self_contained, + &srcdir, + obj, + &mut target_deps, + DependencyType::TargetSelfContained, + ); + } } - if !target.starts_with("s390x") { let libunwind_path = copy_llvm_libunwind(builder, target, &libdir_self_contained); target_deps.push((libunwind_path, DependencyType::TargetSelfContained)); @@ -415,7 +421,7 @@ fn copy_self_contained_objects( } else if target.contains("-wasi") { let srcdir = builder.wasi_libdir(target).unwrap_or_else(|| { panic!( - "Target {:?} does not have a \"wasi-root\" key in Config.toml \ + "Target {:?} does not have a \"wasi-root\" key in bootstrap.toml \ or `$WASI_SDK_PATH` set", target.triple ) @@ -433,9 +439,9 @@ fn copy_self_contained_objects( } else if target.is_windows_gnu() { for obj in ["crt2.o", "dllcrt2.o"].iter() { let src = compiler_file(builder, &builder.cc(target), target, CLang::C, obj); - let target = libdir_self_contained.join(obj); - builder.copy_link(&src, &target); - target_deps.push((target, DependencyType::TargetSelfContained)); + let dst = libdir_self_contained.join(obj); + builder.copy_link(&src, &dst, FileType::NativeLibrary); + target_deps.push((dst, DependencyType::TargetSelfContained)); } } @@ -780,8 +786,11 @@ impl Step for StdLink { let file = t!(file); let path = file.path(); if path.is_file() { - builder - .copy_link(&path, &sysroot.join("lib").join(path.file_name().unwrap())); + builder.copy_link( + &path, + &sysroot.join("lib").join(path.file_name().unwrap()), + FileType::Regular, + ); } } } @@ -819,7 +828,7 @@ fn copy_sanitizers( for runtime in &runtimes { let dst = libdir.join(&runtime.name); - builder.copy_link(&runtime.path, &dst); + builder.copy_link(&runtime.path, &dst, FileType::NativeLibrary); // The `aarch64-apple-ios-macabi` and `x86_64-apple-ios-macabi` are also supported for // sanitizers, but they share a sanitizer runtime with `${arch}-apple-darwin`, so we do @@ -924,9 +933,9 @@ impl Step for StartupObjects { .run(builder); } - let target = sysroot_dir.join((*file).to_string() + ".o"); - builder.copy_link(dst_file, &target); - target_deps.push((target, DependencyType::Target)); + let obj = sysroot_dir.join((*file).to_string() + ".o"); + builder.copy_link(dst_file, &obj, FileType::NativeLibrary); + target_deps.push((obj, DependencyType::Target)); } target_deps @@ -942,7 +951,7 @@ fn cp_rustc_component_to_ci_sysroot(builder: &Builder<'_>, sysroot: &Path, conte if src.is_dir() { t!(fs::create_dir_all(dst)); } else { - builder.copy_link(&src, &dst); + builder.copy_link(&src, &dst, FileType::Regular); } } } @@ -994,7 +1003,9 @@ impl Step for Rustc { fn make_run(run: RunConfig<'_>) { let crates = run.cargo_crates_in_set(); run.builder.ensure(Rustc { - compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), + compiler: run + .builder + .compiler(run.builder.top_stage.saturating_sub(1), run.build_triple()), target: run.target, crates, }); @@ -1179,8 +1190,7 @@ pub fn rustc_cargo( let enzyme_dir = builder.build.out.join(arch).join("enzyme").join("lib"); cargo.rustflag("-L").rustflag(enzyme_dir.to_str().expect("Invalid path")); - if !builder.config.dry_run() { - let llvm_config = builder.llvm_config(builder.config.build).unwrap(); + if let Some(llvm_config) = builder.llvm_config(builder.config.build) { let llvm_version_major = llvm::get_llvm_version_major(builder, &llvm_config); cargo.rustflag("-l").rustflag(&format!("Enzyme-{llvm_version_major}")); } @@ -1282,7 +1292,7 @@ pub fn rustc_cargo_env( .env("CFG_VERSION", builder.rust_version()); // Some tools like Cargo detect their own git information in build scripts. When omit-git-hash - // is enabled in config.toml, we pass this environment variable to tell build scripts to avoid + // is enabled in bootstrap.toml, we pass this environment variable to tell build scripts to avoid // detecting git information on their own. if builder.config.omit_git_hash { cargo.env("CFG_OMIT_GIT_HASH", "1"); @@ -1359,7 +1369,7 @@ pub fn rustc_cargo_env( /// Pass down configuration from the LLVM build into the build of /// rustc_llvm and rustc_codegen_llvm. fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelection) { - if builder.is_rust_llvm(target) { + if builder.config.is_rust_llvm(target) { cargo.env("LLVM_RUSTLLVM", "1"); } if builder.config.llvm_enzyme { @@ -1526,7 +1536,7 @@ fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool { { run.builder.info( "WARNING: no codegen-backends config matched the requested path to build a codegen backend. \ - HELP: add backend to codegen-backends in config.toml.", + HELP: add backend to codegen-backends in bootstrap.toml.", ); return true; } @@ -1538,7 +1548,7 @@ fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool { impl Step for CodegenBackend { type Output = (); const ONLY_HOSTS: bool = true; - /// Only the backends specified in the `codegen-backends` entry of `config.toml` are built. + /// Only the backends specified in the `codegen-backends` entry of `bootstrap.toml` are built. const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1695,7 +1705,7 @@ fn copy_codegen_backends_to_sysroot( let dot = filename.find('.').unwrap(); format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..]) }; - builder.copy_link(file, &dst.join(target_filename)); + builder.copy_link(file, &dst.join(target_filename), FileType::NativeLibrary); } } @@ -1761,7 +1771,7 @@ impl Step for Sysroot { } else if builder.download_rustc() && compiler.stage != builder.top_stage { host_dir.join("ci-rustc-sysroot") } else { - host_dir.join(format!("stage{}", stage)) + host_dir.join(format!("stage{stage}")) } }; let sysroot = sysroot_dir(compiler.stage); @@ -1911,7 +1921,7 @@ impl Step for Assemble { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Assemble { - target_compiler: run.builder.compiler(run.builder.top_stage + 1, run.target), + target_compiler: run.builder.compiler(run.builder.top_stage, run.target), }); } @@ -1974,17 +1984,20 @@ impl Step for Assemble { trace!("installing `{tool}`"); let tool_exe = exe(tool, target_compiler.host); let src_path = llvm_bin_dir.join(&tool_exe); - // When using `download-ci-llvm`, some of the tools - // may not exist, so skip trying to copy them. - if src_path.exists() { - // There is a chance that these tools are being installed from an external LLVM. - // Use `Builder::resolve_symlink_and_copy` instead of `Builder::copy_link` to ensure - // we are copying the original file not the symlinked path, which causes issues for - // tarball distribution. - // - // See https://github.com/rust-lang/rust/issues/135554. - builder.resolve_symlink_and_copy(&src_path, &libdir_bin.join(&tool_exe)); + + // When using `download-ci-llvm`, some of the tools may not exist, so skip trying to copy them. + if !src_path.exists() && builder.config.llvm_from_ci { + eprintln!("{} does not exist; skipping copy", src_path.display()); + continue; } + + // There is a chance that these tools are being installed from an external LLVM. + // Use `Builder::resolve_symlink_and_copy` instead of `Builder::copy_link` to ensure + // we are copying the original file not the symlinked path, which causes issues for + // tarball distribution. + // + // See https://github.com/rust-lang/rust/issues/135554. + builder.resolve_symlink_and_copy(&src_path, &libdir_bin.join(&tool_exe)); } } } @@ -1999,7 +2012,11 @@ impl Step for Assemble { extra_features: vec![], }); let tool_exe = exe("llvm-bitcode-linker", target_compiler.host); - builder.copy_link(&llvm_bitcode_linker.tool_path, &libdir_bin.join(tool_exe)); + builder.copy_link( + &llvm_bitcode_linker.tool_path, + &libdir_bin.join(tool_exe), + FileType::Executable, + ); } }; @@ -2018,7 +2035,9 @@ impl Step for Assemble { builder.info(&format!("Creating a sysroot for stage{stage} compiler (use `rustup toolchain link 'name' build/host/stage{stage}`)", stage=target_compiler.stage)); } - maybe_install_llvm_bitcode_linker(target_compiler); + let mut precompiled_compiler = target_compiler; + precompiled_compiler.forced_compiler(true); + maybe_install_llvm_bitcode_linker(precompiled_compiler); return target_compiler; } @@ -2058,8 +2077,8 @@ impl Step for Assemble { builder.sysroot_target_libdir(target_compiler, target_compiler.host); let dst_lib = libdir.join(&libenzyme).with_extension(lib_ext); let target_dst_lib = target_libdir.join(&libenzyme).with_extension(lib_ext); - builder.copy_link(&src_lib, &dst_lib); - builder.copy_link(&src_lib, &target_dst_lib); + builder.copy_link(&src_lib, &dst_lib, FileType::NativeLibrary); + builder.copy_link(&src_lib, &target_dst_lib, FileType::NativeLibrary); } // Build the libraries for this compiler to link to (i.e., the libraries @@ -2154,14 +2173,14 @@ impl Step for Assemble { }; if is_dylib_or_debug && can_be_rustc_dynamic_dep && !is_proc_macro { - builder.copy_link(&f.path(), &rustc_libdir.join(&filename)); + builder.copy_link(&f.path(), &rustc_libdir.join(&filename), FileType::Regular); } } debug!("copying codegen backends to sysroot"); copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler); - if builder.config.lld_enabled { + if builder.config.lld_enabled && !builder.config.is_system_llvm(target_compiler.host) { builder.ensure(crate::core::build_steps::tool::LldWrapper { build_compiler, target_compiler, @@ -2182,7 +2201,11 @@ impl Step for Assemble { // See . let src_exe = exe("llvm-objcopy", target_compiler.host); let dst_exe = exe("rust-objcopy", target_compiler.host); - builder.copy_link(&libdir_bin.join(src_exe), &libdir_bin.join(dst_exe)); + builder.copy_link( + &libdir_bin.join(src_exe), + &libdir_bin.join(dst_exe), + FileType::Executable, + ); } // In addition to `rust-lld` also install `wasm-component-ld` when @@ -2198,10 +2221,11 @@ impl Step for Assemble { builder.copy_link( &wasm_component.tool_path, &libdir_bin.join(wasm_component.tool_path.file_name().unwrap()), + FileType::Executable, ); } - maybe_install_llvm_bitcode_linker(build_compiler); + maybe_install_llvm_bitcode_linker(target_compiler); // Ensure that `libLLVM.so` ends up in the newly build compiler directory, // so that it can be found when the newly built `rustc` is run. @@ -2220,7 +2244,7 @@ impl Step for Assemble { t!(fs::create_dir_all(bindir)); let compiler = builder.rustc(target_compiler); debug!(src = ?rustc, dst = ?compiler, "linking compiler binary itself"); - builder.copy_link(&rustc, &compiler); + builder.copy_link(&rustc, &compiler, FileType::Executable); target_compiler } @@ -2246,7 +2270,7 @@ pub fn add_to_sysroot( DependencyType::Target => sysroot_dst, DependencyType::TargetSelfContained => self_contained_dst, }; - builder.copy_link(&path, &dst.join(path.file_name().unwrap())); + builder.copy_link(&path, &dst.join(path.file_name().unwrap()), FileType::Regular); } } @@ -2372,7 +2396,9 @@ pub fn run_cargo( // Ok now we need to actually find all the files listed in `toplevel`. We've // got a list of prefix/extensions and we basically just need to find the // most recent file in the `deps` folder corresponding to each one. - let contents = t!(target_deps_dir.read_dir()) + let contents = target_deps_dir + .read_dir() + .unwrap_or_else(|e| panic!("Couldn't read {}: {}", target_deps_dir.display(), e)) .map(|e| t!(e)) .map(|e| (e.path(), e.file_name().into_string().unwrap(), t!(e.metadata()))) .collect::>(); @@ -2504,7 +2530,9 @@ pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path) // FIXME: to make things simpler for now, limit this to the host and target where we know // `strip -g` is both available and will fix the issue, i.e. on a x64 linux host that is not // cross-compiling. Expand this to other appropriate targets in the future. - if target != "x86_64-unknown-linux-gnu" || !builder.is_builder_target(target) || !path.exists() + if target != "x86_64-unknown-linux-gnu" + || !builder.config.is_host_target(target) + || !path.exists() { return; } diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 26b3e0b701c4b..253fa224152cc 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -32,7 +32,7 @@ use crate::utils::helpers::{ exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit, }; use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball}; -use crate::{Compiler, DependencyType, LLVM_TOOLS, Mode, trace}; +use crate::{Compiler, DependencyType, FileType, LLVM_TOOLS, Mode, trace}; pub fn pkgname(builder: &Builder<'_>, component: &str) -> String { format!("{}-{}", component, builder.rust_package_vers()) @@ -81,7 +81,7 @@ impl Step for Docs { let mut tarball = Tarball::new(builder, "rust-docs", &host.triple); tarball.set_product_name("Rust Documentation"); tarball.add_bulk_dir(builder.doc_out(host), dest); - tarball.add_file(builder.src.join("src/doc/robots.txt"), dest, 0o644); + tarball.add_file(builder.src.join("src/doc/robots.txt"), dest, FileType::Regular); Some(tarball.generate()) } } @@ -230,8 +230,10 @@ fn make_win_dist( "libiconv.a", "libmoldname.a", "libpthread.a", - //Windows import libs - //This should contain only the set of libraries necessary to link the standard library. + // Windows import libs + // This *should* contain only the set of libraries necessary to link the standard library, + // however we've had problems with people accidentally depending on extra libs being here, + // so we can't easily remove entries. "libadvapi32.a", "libbcrypt.a", "libcomctl32.a", @@ -416,17 +418,22 @@ impl Step for Rustc { .is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc")) { let rustdoc = builder.rustdoc(compiler); - builder.install(&rustdoc, &image.join("bin"), 0o755); + builder.install(&rustdoc, &image.join("bin"), FileType::Executable); } + let ra_proc_macro_srv_compiler = + builder.compiler_for(compiler.stage, builder.config.build, compiler.host); + builder.ensure(compile::Rustc::new(ra_proc_macro_srv_compiler, compiler.host)); + if let Some(ra_proc_macro_srv) = builder.ensure_if_default( tool::RustAnalyzerProcMacroSrv { - compiler: builder.compiler(compiler.stage, builder.config.build), + compiler: ra_proc_macro_srv_compiler, target: compiler.host, }, builder.kind, ) { - builder.install(&ra_proc_macro_srv.tool_path, &image.join("libexec"), 0o755); + let dst = image.join("libexec"); + builder.install(&ra_proc_macro_srv.tool_path, &dst, FileType::Executable); } let libdir_relative = builder.libdir_relative(compiler); @@ -438,7 +445,7 @@ impl Step for Rustc { if is_dylib(&entry.path()) { // Don't use custom libdir here because ^lib/ will be resolved again // with installer - builder.install(&entry.path(), &image.join("lib"), 0o644); + builder.install(&entry.path(), &image.join("lib"), FileType::NativeLibrary); } } } @@ -457,7 +464,11 @@ impl Step for Rustc { if builder.config.lld_enabled { let src_dir = builder.sysroot_target_bindir(compiler, host); let rust_lld = exe("rust-lld", compiler.host); - builder.copy_link(&src_dir.join(&rust_lld), &dst_dir.join(&rust_lld)); + builder.copy_link( + &src_dir.join(&rust_lld), + &dst_dir.join(&rust_lld), + FileType::Executable, + ); let self_contained_lld_src_dir = src_dir.join("gcc-ld"); let self_contained_lld_dst_dir = dst_dir.join("gcc-ld"); t!(fs::create_dir(&self_contained_lld_dst_dir)); @@ -466,6 +477,7 @@ impl Step for Rustc { builder.copy_link( &self_contained_lld_src_dir.join(&exe_name), &self_contained_lld_dst_dir.join(&exe_name), + FileType::Executable, ); } } @@ -474,13 +486,17 @@ impl Step for Rustc { let src_dir = builder.sysroot_target_bindir(compiler, host); let llvm_objcopy = exe("llvm-objcopy", compiler.host); let rust_objcopy = exe("rust-objcopy", compiler.host); - builder.copy_link(&src_dir.join(&llvm_objcopy), &dst_dir.join(&rust_objcopy)); + builder.copy_link( + &src_dir.join(&llvm_objcopy), + &dst_dir.join(&rust_objcopy), + FileType::Executable, + ); } if builder.tool_enabled("wasm-component-ld") { let src_dir = builder.sysroot_target_bindir(compiler, host); let ld = exe("wasm-component-ld", compiler.host); - builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld)); + builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld), FileType::Executable); } // Man pages @@ -505,15 +521,19 @@ impl Step for Rustc { // HTML copyright files let file_list = builder.ensure(super::run::GenerateCopyright); for file in file_list { - builder.install(&file, &image.join("share/doc/rust"), 0o644); + builder.install(&file, &image.join("share/doc/rust"), FileType::Regular); } // README - builder.install(&builder.src.join("README.md"), &image.join("share/doc/rust"), 0o644); + builder.install( + &builder.src.join("README.md"), + &image.join("share/doc/rust"), + FileType::Regular, + ); // The REUSE-managed license files let license = |path: &Path| { - builder.install(path, &image.join("share/doc/rust/licences"), 0o644); + builder.install(path, &image.join("share/doc/rust/licenses"), FileType::Regular); }; for entry in t!(std::fs::read_dir(builder.src.join("LICENSES"))).flatten() { license(&entry.path()); @@ -542,14 +562,14 @@ impl Step for DebuggerScripts { let dst = sysroot.join("lib/rustlib/etc"); t!(fs::create_dir_all(&dst)); let cp_debugger_script = |file: &str| { - builder.install(&builder.src.join("src/etc/").join(file), &dst, 0o644); + builder.install(&builder.src.join("src/etc/").join(file), &dst, FileType::Regular); }; if host.contains("windows-msvc") { // windbg debugger scripts builder.install( &builder.src.join("src/etc/rust-windbg.cmd"), &sysroot.join("bin"), - 0o755, + FileType::Script, ); cp_debugger_script("natvis/intrinsic.natvis"); @@ -561,15 +581,27 @@ impl Step for DebuggerScripts { cp_debugger_script("rust_types.py"); // gdb debugger scripts - builder.install(&builder.src.join("src/etc/rust-gdb"), &sysroot.join("bin"), 0o755); - builder.install(&builder.src.join("src/etc/rust-gdbgui"), &sysroot.join("bin"), 0o755); + builder.install( + &builder.src.join("src/etc/rust-gdb"), + &sysroot.join("bin"), + FileType::Script, + ); + builder.install( + &builder.src.join("src/etc/rust-gdbgui"), + &sysroot.join("bin"), + FileType::Script, + ); cp_debugger_script("gdb_load_rust_pretty_printers.py"); cp_debugger_script("gdb_lookup.py"); cp_debugger_script("gdb_providers.py"); // lldb debugger scripts - builder.install(&builder.src.join("src/etc/rust-lldb"), &sysroot.join("bin"), 0o755); + builder.install( + &builder.src.join("src/etc/rust-lldb"), + &sysroot.join("bin"), + FileType::Script, + ); cp_debugger_script("lldb_lookup.py"); cp_debugger_script("lldb_providers.py"); @@ -580,7 +612,7 @@ impl Step for DebuggerScripts { fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool { // The only true set of target libraries came from the build triple, so // let's reduce redundant work by only producing archives from that host. - if !builder.is_builder_target(compiler.host) { + if !builder.config.is_host_target(compiler.host) { builder.info("\tskipping, not a build host"); true } else { @@ -634,9 +666,14 @@ fn copy_target_libs( t!(fs::create_dir_all(&self_contained_dst)); for (path, dependency_type) in builder.read_stamp_file(stamp) { if dependency_type == DependencyType::TargetSelfContained { - builder.copy_link(&path, &self_contained_dst.join(path.file_name().unwrap())); - } else if dependency_type == DependencyType::Target || builder.is_builder_target(target) { - builder.copy_link(&path, &dst.join(path.file_name().unwrap())); + builder.copy_link( + &path, + &self_contained_dst.join(path.file_name().unwrap()), + FileType::NativeLibrary, + ); + } else if dependency_type == DependencyType::Target || builder.config.is_host_target(target) + { + builder.copy_link(&path, &dst.join(path.file_name().unwrap()), FileType::NativeLibrary); } } } @@ -744,7 +781,11 @@ impl Step for RustcDev { &tarball.image_dir().join("lib/rustlib/rustc-src/rust"), ); for file in src_files { - tarball.add_file(builder.src.join(file), "lib/rustlib/rustc-src/rust", 0o644); + tarball.add_file( + builder.src.join(file), + "lib/rustlib/rustc-src/rust", + FileType::Regular, + ); } Some(tarball.generate()) @@ -771,7 +812,11 @@ impl Step for Analysis { // Find the actual compiler (handling the full bootstrap option) which // produced the save-analysis data because that data isn't copied // through the sysroot uplifting. - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), + compiler: run.builder.compiler_for( + run.builder.top_stage, + run.builder.config.build, + run.target, + ), target: run.target, }); } @@ -780,7 +825,7 @@ impl Step for Analysis { fn run(self, builder: &Builder<'_>) -> Option { let compiler = self.compiler; let target = self.target; - if !builder.is_builder_target(compiler.host) { + if !builder.config.is_host_target(compiler.host) { return None; } @@ -990,9 +1035,9 @@ impl Step for PlainSourceTarball { let src_files = [ // tidy-alphabetical-start ".gitmodules", + "bootstrap.example.toml", "Cargo.lock", "Cargo.toml", - "config.example.toml", "configure", "CONTRIBUTING.md", "COPYRIGHT", @@ -1035,7 +1080,11 @@ impl Step for PlainSourceTarball { // Copy the files normally for item in &src_files { - builder.copy_link(&builder.src.join(item), &plain_dst_src.join(item)); + builder.copy_link( + &builder.src.join(item), + &plain_dst_src.join(item), + FileType::Regular, + ); } // Create the version file @@ -1116,7 +1165,11 @@ impl Step for Cargo { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Cargo { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), + compiler: run.builder.compiler_for( + run.builder.top_stage, + run.builder.config.build, + run.target, + ), target: run.target, }); } @@ -1125,6 +1178,8 @@ impl Step for Cargo { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let cargo = builder.ensure(tool::Cargo { compiler, target }); let src = builder.src.join("src/tools/cargo"); let etc = src.join("src/etc"); @@ -1133,9 +1188,14 @@ impl Step for Cargo { let mut tarball = Tarball::new(builder, "cargo", &target.triple); tarball.set_overlay(OverlayKind::Cargo); - tarball.add_file(cargo.tool_path, "bin", 0o755); - tarball.add_file(etc.join("_cargo"), "share/zsh/site-functions", 0o644); - tarball.add_renamed_file(etc.join("cargo.bashcomp.sh"), "etc/bash_completion.d", "cargo"); + tarball.add_file(&cargo.tool_path, "bin", FileType::Executable); + tarball.add_file(etc.join("_cargo"), "share/zsh/site-functions", FileType::Regular); + tarball.add_renamed_file( + etc.join("cargo.bashcomp.sh"), + "etc/bash_completion.d", + "cargo", + FileType::Regular, + ); tarball.add_dir(etc.join("man"), "share/man/man1"); tarball.add_legal_and_readme_to("share/doc/cargo"); @@ -1143,44 +1203,6 @@ impl Step for Cargo { } } -#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] -pub struct Rls { - pub compiler: Compiler, - pub target: TargetSelection, -} - -impl Step for Rls { - type Output = Option; - const ONLY_HOSTS: bool = true; - const DEFAULT: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - let default = should_build_extended_tool(run.builder, "rls"); - run.alias("rls").default_condition(default) - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Rls { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), - target: run.target, - }); - } - - fn run(self, builder: &Builder<'_>) -> Option { - let compiler = self.compiler; - let target = self.target; - - let rls = builder.ensure(tool::Rls { compiler, target }); - - let mut tarball = Tarball::new(builder, "rls", &target.triple); - tarball.set_overlay(OverlayKind::Rls); - tarball.is_preview(true); - tarball.add_file(rls.tool_path, "bin", 0o755); - tarball.add_legal_and_readme_to("share/doc/rls"); - Some(tarball.generate()) - } -} - #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct RustAnalyzer { pub compiler: Compiler, @@ -1199,7 +1221,11 @@ impl Step for RustAnalyzer { fn make_run(run: RunConfig<'_>) { run.builder.ensure(RustAnalyzer { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), + compiler: run.builder.compiler_for( + run.builder.top_stage, + run.builder.config.build, + run.target, + ), target: run.target, }); } @@ -1208,12 +1234,14 @@ impl Step for RustAnalyzer { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let rust_analyzer = builder.ensure(tool::RustAnalyzer { compiler, target }); let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple); tarball.set_overlay(OverlayKind::RustAnalyzer); tarball.is_preview(true); - tarball.add_file(rust_analyzer.tool_path, "bin", 0o755); + tarball.add_file(&rust_analyzer.tool_path, "bin", FileType::Executable); tarball.add_legal_and_readme_to("share/doc/rust-analyzer"); Some(tarball.generate()) } @@ -1237,7 +1265,11 @@ impl Step for Clippy { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Clippy { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), + compiler: run.builder.compiler_for( + run.builder.top_stage, + run.builder.config.build, + run.target, + ), target: run.target, }); } @@ -1246,6 +1278,8 @@ impl Step for Clippy { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + // Prepare the image directory // We expect clippy to build, because we've exited this step above if tool // state for clippy isn't testing. @@ -1255,8 +1289,8 @@ impl Step for Clippy { let mut tarball = Tarball::new(builder, "clippy", &target.triple); tarball.set_overlay(OverlayKind::Clippy); tarball.is_preview(true); - tarball.add_file(clippy.tool_path, "bin", 0o755); - tarball.add_file(cargoclippy.tool_path, "bin", 0o755); + tarball.add_file(&clippy.tool_path, "bin", FileType::Executable); + tarball.add_file(&cargoclippy.tool_path, "bin", FileType::Executable); tarball.add_legal_and_readme_to("share/doc/clippy"); Some(tarball.generate()) } @@ -1280,7 +1314,11 @@ impl Step for Miri { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Miri { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), + compiler: run.builder.compiler_for( + run.builder.top_stage, + run.builder.config.build, + run.target, + ), target: run.target, }); } @@ -1292,17 +1330,20 @@ impl Step for Miri { if !builder.build.unstable_features() { return None; } + let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let miri = builder.ensure(tool::Miri { compiler, target }); let cargomiri = builder.ensure(tool::CargoMiri { compiler, target }); let mut tarball = Tarball::new(builder, "miri", &target.triple); tarball.set_overlay(OverlayKind::Miri); tarball.is_preview(true); - tarball.add_file(miri.tool_path, "bin", 0o755); - tarball.add_file(cargomiri.tool_path, "bin", 0o755); + tarball.add_file(&miri.tool_path, "bin", FileType::Executable); + tarball.add_file(&cargomiri.tool_path, "bin", FileType::Executable); tarball.add_legal_and_readme_to("share/doc/miri"); Some(tarball.generate()) } @@ -1362,14 +1403,14 @@ impl Step for CodegenBackend { let backend = self.backend; let mut tarball = - Tarball::new(builder, &format!("rustc-codegen-{}", backend), &compiler.host.triple); + Tarball::new(builder, &format!("rustc-codegen-{backend}"), &compiler.host.triple); if backend == "cranelift" { tarball.set_overlay(OverlayKind::RustcCodegenCranelift); } else { - panic!("Unknown backend rustc_codegen_{}", backend); + panic!("Unknown backend rustc_codegen_{backend}"); } tarball.is_preview(true); - tarball.add_legal_and_readme_to(format!("share/doc/rustc_codegen_{}", backend)); + tarball.add_legal_and_readme_to(format!("share/doc/rustc_codegen_{backend}")); let src = builder.sysroot(compiler); let backends_src = builder.sysroot_codegen_backends(compiler); @@ -1381,12 +1422,16 @@ impl Step for CodegenBackend { // Don't use custom libdir here because ^lib/ will be resolved again with installer let backends_dst = PathBuf::from("lib").join(backends_rel); - let backend_name = format!("rustc_codegen_{}", backend); + let backend_name = format!("rustc_codegen_{backend}"); let mut found_backend = false; for backend in fs::read_dir(&backends_src).unwrap() { let file_name = backend.unwrap().file_name(); if file_name.to_str().unwrap().contains(&backend_name) { - tarball.add_file(backends_src.join(file_name), &backends_dst, 0o644); + tarball.add_file( + backends_src.join(file_name), + &backends_dst, + FileType::NativeLibrary, + ); found_backend = true; } } @@ -1414,7 +1459,11 @@ impl Step for Rustfmt { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Rustfmt { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), + compiler: run.builder.compiler_for( + run.builder.top_stage, + run.builder.config.build, + run.target, + ), target: run.target, }); } @@ -1423,13 +1472,15 @@ impl Step for Rustfmt { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let rustfmt = builder.ensure(tool::Rustfmt { compiler, target }); let cargofmt = builder.ensure(tool::Cargofmt { compiler, target }); let mut tarball = Tarball::new(builder, "rustfmt", &target.triple); tarball.set_overlay(OverlayKind::Rustfmt); tarball.is_preview(true); - tarball.add_file(rustfmt.tool_path, "bin", 0o755); - tarball.add_file(cargofmt.tool_path, "bin", 0o755); + tarball.add_file(&rustfmt.tool_path, "bin", FileType::Executable); + tarball.add_file(&cargofmt.tool_path, "bin", FileType::Executable); tarball.add_legal_and_readme_to("share/doc/rustfmt"); Some(tarball.generate()) } @@ -1464,7 +1515,7 @@ impl Step for Extended { fn run(self, builder: &Builder<'_>) { let target = self.target; let stage = self.stage; - let compiler = builder.compiler(self.stage, self.host); + let compiler = builder.compiler_for(self.stage, self.host, self.target); builder.info(&format!("Dist extended stage{} ({})", compiler.stage, target)); @@ -1494,7 +1545,6 @@ impl Step for Extended { add_component!("rust-json-docs" => JsonDocs { host: target }); add_component!("cargo" => Cargo { compiler, target }); add_component!("rustfmt" => Rustfmt { compiler, target }); - add_component!("rls" => Rls { compiler, target }); add_component!("rust-analyzer" => RustAnalyzer { compiler, target }); add_component!("llvm-components" => LlvmTools { target }); add_component!("clippy" => Clippy { compiler, target }); @@ -1573,7 +1623,7 @@ impl Step for Extended { let pkgbuild = |component: &str| { let mut cmd = command("pkgbuild"); cmd.arg("--identifier") - .arg(format!("org.rust-lang.{}", component)) + .arg(format!("org.rust-lang.{component}")) .arg("--scripts") .arg(pkg.join(component)) .arg("--nopayload") @@ -1587,27 +1637,33 @@ impl Step for Extended { &work.join(format!("{}-{}", pkgname(builder, name), target.triple)), &pkg.join(name), ); - builder.install(&etc.join("pkg/postinstall"), &pkg.join(name), 0o755); + builder.install(&etc.join("pkg/postinstall"), &pkg.join(name), FileType::Script); pkgbuild(name); }; prepare("rustc"); prepare("cargo"); prepare("rust-std"); prepare("rust-analysis"); - prepare("clippy"); - prepare("rust-analyzer"); - for tool in &["rust-docs", "miri", "rustc-codegen-cranelift"] { + + for tool in &[ + "clippy", + "rustfmt", + "rust-analyzer", + "rust-docs", + "miri", + "rustc-codegen-cranelift", + ] { if built_tools.contains(tool) { prepare(tool); } } // create an 'uninstall' package - builder.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), 0o755); + builder.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), FileType::Script); pkgbuild("uninstall"); builder.create_dir(&pkg.join("res")); builder.create(&pkg.join("res/LICENSE.txt"), &license); - builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644); + builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), FileType::Regular); let mut cmd = command("productbuild"); cmd.arg("--distribution") .arg(xform(&etc.join("pkg/Distribution.xml"))) @@ -1636,6 +1692,8 @@ impl Step for Extended { "rust-analyzer-preview".to_string() } else if name == "clippy" { "clippy-preview".to_string() + } else if name == "rustfmt" { + "rustfmt-preview".to_string() } else if name == "miri" { "miri-preview".to_string() } else if name == "rustc-codegen-cranelift" { @@ -1655,7 +1713,7 @@ impl Step for Extended { prepare("cargo"); prepare("rust-analysis"); prepare("rust-std"); - for tool in &["clippy", "rust-analyzer", "rust-docs", "miri"] { + for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] { if built_tools.contains(tool) { prepare(tool); } @@ -1664,7 +1722,7 @@ impl Step for Extended { prepare("rust-mingw"); } - builder.install(&etc.join("gfx/rust-logo.ico"), &exe, 0o644); + builder.install(&etc.join("gfx/rust-logo.ico"), &exe, FileType::Regular); // Generate msi installer let wix_path = env::var_os("WIX") @@ -1773,6 +1831,24 @@ impl Step for Extended { .arg(etc.join("msi/remove-duplicates.xsl")) .run(builder); } + if built_tools.contains("rustfmt") { + command(&heat) + .current_dir(&exe) + .arg("dir") + .arg("rustfmt") + .args(heat_flags) + .arg("-cg") + .arg("RustFmtGroup") + .arg("-dr") + .arg("RustFmt") + .arg("-var") + .arg("var.RustFmtDir") + .arg("-out") + .arg(exe.join("RustFmtGroup.wxs")) + .arg("-t") + .arg(etc.join("msi/remove-duplicates.xsl")) + .run(builder); + } if built_tools.contains("miri") { command(&heat) .current_dir(&exe) @@ -1839,11 +1915,14 @@ impl Step for Extended { .arg("-out") .arg(&output) .arg(input); - add_env(builder, &mut cmd, target); + add_env(builder, &mut cmd, target, &built_tools); if built_tools.contains("clippy") { cmd.arg("-dClippyDir=clippy"); } + if built_tools.contains("rustfmt") { + cmd.arg("-dRustFmtDir=rustfmt"); + } if built_tools.contains("rust-docs") { cmd.arg("-dDocsDir=rust-docs"); } @@ -1870,6 +1949,9 @@ impl Step for Extended { if built_tools.contains("clippy") { candle("ClippyGroup.wxs".as_ref()); } + if built_tools.contains("rustfmt") { + candle("RustFmtGroup.wxs".as_ref()); + } if built_tools.contains("miri") { candle("MiriGroup.wxs".as_ref()); } @@ -1883,8 +1965,8 @@ impl Step for Extended { } builder.create(&exe.join("LICENSE.rtf"), &rtf); - builder.install(&etc.join("gfx/banner.bmp"), &exe, 0o644); - builder.install(&etc.join("gfx/dialogbg.bmp"), &exe, 0o644); + builder.install(&etc.join("gfx/banner.bmp"), &exe, FileType::Regular); + builder.install(&etc.join("gfx/dialogbg.bmp"), &exe, FileType::Regular); builder.info(&format!("building `msi` installer with {light:?}")); let filename = format!("{}-{}.msi", pkgname(builder, "rust"), target.triple); @@ -1908,6 +1990,9 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("ClippyGroup.wixobj"); } + if built_tools.contains("rustfmt") { + cmd.arg("RustFmtGroup.wixobj"); + } if built_tools.contains("miri") { cmd.arg("MiriGroup.wixobj"); } @@ -1934,7 +2019,12 @@ impl Step for Extended { } } -fn add_env(builder: &Builder<'_>, cmd: &mut BootstrapCommand, target: TargetSelection) { +fn add_env( + builder: &Builder<'_>, + cmd: &mut BootstrapCommand, + target: TargetSelection, + built_tools: &HashSet<&'static str>, +) { let mut parts = builder.version.split('.'); cmd.env("CFG_RELEASE_INFO", builder.rust_version()) .env("CFG_RELEASE_NUM", &builder.version) @@ -1955,6 +2045,15 @@ fn add_env(builder: &Builder<'_>, cmd: &mut BootstrapCommand, target: TargetSele } else { cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC"); } + + // ensure these variables are defined + let mut define_optional_tool = |tool_name: &str, env_name: &str| { + cmd.env(env_name, if built_tools.contains(tool_name) { "1" } else { "0" }); + }; + define_optional_tool("rustfmt", "CFG_RUSTFMT"); + define_optional_tool("clippy", "CFG_CLIPPY"); + define_optional_tool("miri", "CFG_MIRI"); + define_optional_tool("rust-analyzer", "CFG_RA"); } fn install_llvm_file( @@ -1970,13 +2069,13 @@ fn install_llvm_file( if source.is_symlink() { // If we have a symlink like libLLVM-18.so -> libLLVM.so.18.1, install the target of the // symlink, which is what will actually get loaded at runtime. - builder.install(&t!(fs::canonicalize(source)), destination, 0o644); + builder.install(&t!(fs::canonicalize(source)), destination, FileType::NativeLibrary); let full_dest = destination.join(source.file_name().unwrap()); if install_symlink { // For download-ci-llvm, also install the symlink, to match what LLVM does. Using a // symlink is fine here, as this is not a rustup component. - builder.copy_link(source, &full_dest); + builder.copy_link(source, &full_dest, FileType::NativeLibrary); } else { // Otherwise, replace the symlink with an equivalent linker script. This is used when // projects like miri link against librustc_driver.so. We don't use a symlink, as @@ -1993,7 +2092,7 @@ fn install_llvm_file( } } } else { - builder.install(source, destination, 0o644); + builder.install(source, destination, FileType::NativeLibrary); } } @@ -2031,7 +2130,7 @@ fn maybe_install_llvm( // // If the LLVM is coming from ourselves (just from CI) though, we // still want to install it, as it otherwise won't be available. - if builder.is_system_llvm(target) { + if builder.config.is_system_llvm(target) { trace!("system LLVM requested, no install"); return false; } @@ -2045,7 +2144,7 @@ fn maybe_install_llvm( 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); + builder.install(&llvm_dylib_path, dst_libdir, FileType::NativeLibrary); } !builder.config.dry_run() } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) = @@ -2112,8 +2211,7 @@ pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, ), )] pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) { - let dst_libdir = - sysroot.join(builder.sysroot_libdir_relative(Compiler { stage: 1, host: target })); + let dst_libdir = sysroot.join(builder.sysroot_libdir_relative(Compiler::new(1, target))); // 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. @@ -2176,9 +2274,9 @@ impl Step for LlvmTools { let target = self.target; - /* run only if llvm-config isn't used */ + // Run only if a custom llvm-config is not used if let Some(config) = builder.config.target_config.get(&target) { - if let Some(ref _s) = config.llvm_config { + if !builder.config.llvm_from_ci && config.llvm_config.is_some() { builder.info(&format!("Skipping LlvmTools ({target}): external LLVM")); return None; } @@ -2196,7 +2294,13 @@ impl Step for LlvmTools { let dst_bindir = format!("lib/rustlib/{}/bin", target.triple); for tool in tools_to_install(&builder.paths) { let exe = src_bindir.join(exe(tool, target)); - tarball.add_file(&exe, &dst_bindir, 0o755); + // When using `download-ci-llvm`, some of the tools may not exist, so skip trying to copy them. + if !exe.exists() && builder.config.llvm_from_ci { + eprintln!("{} does not exist; skipping copy", exe.display()); + continue; + } + + tarball.add_file(&exe, &dst_bindir, FileType::Executable); } } @@ -2228,7 +2332,11 @@ impl Step for LlvmBitcodeLinker { fn make_run(run: RunConfig<'_>) { run.builder.ensure(LlvmBitcodeLinker { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), + compiler: run.builder.compiler_for( + run.builder.top_stage, + run.builder.config.build, + run.target, + ), target: run.target, }); } @@ -2237,6 +2345,8 @@ impl Step for LlvmBitcodeLinker { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let llbc_linker = builder.ensure(tool::LlvmBitcodeLinker { compiler, target, extra_features: vec![] }); @@ -2247,7 +2357,7 @@ impl Step for LlvmBitcodeLinker { tarball.set_overlay(OverlayKind::LlvmBitcodeLinker); tarball.is_preview(true); - tarball.add_file(llbc_linker.tool_path, self_contained_bin_dir, 0o755); + tarball.add_file(&llbc_linker.tool_path, self_contained_bin_dir, FileType::Executable); Some(tarball.generate()) } @@ -2308,7 +2418,7 @@ impl Step for RustDev { let entry = t!(entry); if entry.file_type().is_file() && !entry.path_is_symlink() { let name = entry.file_name().to_str().unwrap(); - tarball.add_file(src_bindir.join(name), "bin", 0o755); + tarball.add_file(src_bindir.join(name), "bin", FileType::Executable); } } } @@ -2320,11 +2430,11 @@ impl Step for RustDev { // We don't build LLD on some platforms, so only add it if it exists let lld_path = lld_out.join("bin").join(exe("lld", target)); if lld_path.exists() { - tarball.add_file(lld_path, "bin", 0o755); + tarball.add_file(&lld_path, "bin", FileType::Executable); } } - tarball.add_file(builder.llvm_filecheck(target), "bin", 0o755); + tarball.add_file(builder.llvm_filecheck(target), "bin", FileType::Executable); // Copy the include directory as well; needed mostly to build // librustc_llvm properly (e.g., llvm-config.h is in here). But also @@ -2384,8 +2494,12 @@ impl Step for Bootstrap { let tarball = Tarball::new(builder, "bootstrap", &target.triple); let bootstrap_outdir = &builder.bootstrap_out; - for file in &["bootstrap", "rustc", "rustdoc", "sccache-plus-cl"] { - tarball.add_file(bootstrap_outdir.join(exe(file, target)), "bootstrap/bin", 0o755); + for file in &["bootstrap", "rustc", "rustdoc"] { + tarball.add_file( + bootstrap_outdir.join(exe(file, target)), + "bootstrap/bin", + FileType::Executable, + ); } Some(tarball.generate()) @@ -2418,7 +2532,7 @@ impl Step for BuildManifest { let build_manifest = builder.tool_exe(Tool::BuildManifest); let tarball = Tarball::new(builder, "build-manifest", &self.target.triple); - tarball.add_file(build_manifest, "bin", 0o755); + tarball.add_file(&build_manifest, "bin", FileType::Executable); tarball.generate() } } @@ -2450,17 +2564,44 @@ impl Step for ReproducibleArtifacts { let mut added_anything = false; let tarball = Tarball::new(builder, "reproducible-artifacts", &self.target.triple); if let Some(path) = builder.config.rust_profile_use.as_ref() { - tarball.add_file(path, ".", 0o644); + tarball.add_file(path, ".", FileType::Regular); added_anything = true; } if let Some(path) = builder.config.llvm_profile_use.as_ref() { - tarball.add_file(path, ".", 0o644); + tarball.add_file(path, ".", FileType::Regular); added_anything = true; } for profile in &builder.config.reproducible_artifacts { - tarball.add_file(profile, ".", 0o644); + tarball.add_file(profile, ".", FileType::Regular); added_anything = true; } if added_anything { Some(tarball.generate()) } else { None } } } + +/// Tarball containing a prebuilt version of the libgccjit library, +/// needed as a dependency for the GCC codegen backend (similarly to the LLVM +/// backend needing a prebuilt libLLVM). +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct Gcc { + pub target: TargetSelection, +} + +impl Step for Gcc { + type Output = GeneratedTarball; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("gcc") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Gcc { target: run.target }); + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + let tarball = Tarball::new(builder, "gcc", &self.target.triple); + let output = builder.ensure(super::gcc::Gcc { target: self.target }); + tarball.add_file(&output.libgccjit, "lib", FileType::NativeLibrary); + tarball.generate() + } +} diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 8e913f6818423..7fccf85a0ea9f 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -11,7 +11,6 @@ use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::{env, fs, mem}; -use crate::Mode; use crate::core::build_steps::compile; use crate::core::build_steps::tool::{self, SourceType, Tool, prepare_tool_cargo}; use crate::core::builder::{ @@ -19,6 +18,7 @@ use crate::core::builder::{ }; use crate::core::config::{Config, TargetSelection}; use crate::helpers::{submodule_path_of, symlink_dir, t, up_to_date}; +use crate::{FileType, Mode}; macro_rules! book { ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => { @@ -546,6 +546,7 @@ impl Step for SharedAssets { builder.copy_link( &builder.src.join("src").join("doc").join("rust.css"), &out.join("rust.css"), + FileType::Regular, ); SharedAssetsPaths { version_info } @@ -1218,7 +1219,7 @@ impl Step for RustcBook { cmd.env("RUSTC_BOOTSTRAP", "1"); // If the lib directories are in an unusual location (changed in - // config.toml), then this needs to explicitly update the dylib search + // bootstrap.toml), then this needs to explicitly update the dylib search // path. builder.add_rustc_lib_path(self.compiler, &mut cmd); let doc_generator_guard = builder.msg( diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index c7eafadee2df3..1c317ce4b86b1 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -6,11 +6,10 @@ use std::process::Command; use std::sync::Mutex; use std::sync::mpsc::SyncSender; -use build_helper::ci::CiEnv; use build_helper::git::get_git_modified_files; use ignore::WalkBuilder; -use crate::core::builder::Builder; +use crate::core::builder::{Builder, Kind}; use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::command; use crate::utils::helpers::{self, t}; @@ -27,12 +26,12 @@ fn rustfmt( rustfmt: &Path, paths: &[PathBuf], check: bool, -) -> impl FnMut(bool) -> RustfmtStatus { +) -> impl FnMut(bool) -> RustfmtStatus + use<> { let mut cmd = Command::new(rustfmt); // Avoid the submodule config paths from coming into play. We only allow a single global config // for the workspace for now. cmd.arg("--config-path").arg(src.canonicalize().unwrap()); - cmd.arg("--edition").arg("2021"); + cmd.arg("--edition").arg("2024"); cmd.arg("--unstable-features"); cmd.arg("--skip-children"); if check { @@ -59,7 +58,7 @@ fn rustfmt( fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, BuildStamp)> { let stamp_file = BuildStamp::new(&build.out).with_prefix("rustfmt"); - let mut cmd = command(build.initial_rustfmt()?); + let mut cmd = command(build.config.initial_rustfmt.as_ref()?); cmd.arg("--version"); let output = cmd.allow_failure().run_capture(build); @@ -82,19 +81,24 @@ fn update_rustfmt_version(build: &Builder<'_>) { let Some((version, stamp_file)) = get_rustfmt_version(build) else { return; }; - t!(std::fs::write(stamp_file.path(), version)) + + t!(stamp_file.add_stamp(version).write()); } -/// Returns the Rust files modified between the `merge-base` of HEAD and -/// rust-lang/master and what is now on the disk. Does not include removed files. +/// Returns the Rust files modified between the last merge commit and what is now on the disk. +/// Does not include removed files. /// /// Returns `None` if all files should be formatted. fn get_modified_rs_files(build: &Builder<'_>) -> Result>, String> { + // In CI `get_git_modified_files` returns something different to normal environment. + // This shouldn't be called in CI anyway. + assert!(!build.config.is_running_on_ci); + if !verify_rustfmt_version(build) { return Ok(None); } - get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &["rs"]) + get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &["rs"]).map(Some) } #[derive(serde_derive::Deserialize)] @@ -115,12 +119,15 @@ fn print_paths(verb: &str, adjective: Option<&str>, paths: &[String]) { } else { println!("fmt: {verb} {len} {adjective}files"); } - if len > 1000 && !CiEnv::is_ci() { - println!("hint: if this number seems too high, try running `git fetch origin master`"); - } } pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { + if build.kind == Kind::Format && build.top_stage != 0 { + eprintln!("ERROR: `x fmt` only supports stage 0."); + eprintln!("HELP: Use `x run rustfmt` to run in-tree rustfmt."); + crate::exit!(1); + } + if !paths.is_empty() { eprintln!( "fmt error: path arguments are no longer accepted; use `--all` to format everything" @@ -135,7 +142,7 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { // `--all` is specified or we are in CI. We check all files in CI to avoid bugs in // `get_modified_rs_files` letting regressions slip through; we also care about CI time less // since this is still very fast compared to building the compiler. - let all = all || CiEnv::is_ci(); + let all = all || build.config.is_running_on_ci; let mut builder = ignore::types::TypesBuilder::new(); builder.add_defaults(); @@ -213,7 +220,13 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { override_builder.add(&format!("/{file}")).expect(&file); } } - Ok(None) => {} + Ok(None) => { + // NOTE: `Ok(None)` signifies that we need to format all files. + // The tricky part here is that if `override_builder` isn't given any white + // list files (i.e. files to be formatted, added without leading `!`), it + // will instead look for *all* files. So, by doing nothing here, we are + // actually making it so we format all files. + } Err(err) => { eprintln!("fmt warning: Something went wrong running git commands:"); eprintln!("fmt warning: {err}"); @@ -230,7 +243,7 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { let override_ = override_builder.build().unwrap(); // `override` is a reserved keyword - let rustfmt_path = build.initial_rustfmt().unwrap_or_else(|| { + let rustfmt_path = build.config.initial_rustfmt.clone().unwrap_or_else(|| { eprintln!("fmt error: `x fmt` is not supported on this channel"); crate::exit!(1); }); @@ -329,7 +342,10 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { crate::exit!(1); } - if !check { - update_rustfmt_version(build); - } + // Update `build/.rustfmt-stamp`, allowing this code to ignore files which have not been changed + // since last merge. + // + // NOTE: Because of the exit above, this is only reachable if formatting / format checking + // succeeded. So we are not commiting the version if formatting was not good. + update_rustfmt_version(build); } diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index e8b6a577cea5b..ee8bd8286daec 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -12,15 +12,72 @@ use std::fs; use std::path::{Path, PathBuf}; use std::sync::OnceLock; -use build_helper::ci::CiEnv; - -use crate::Kind; -use crate::core::builder::{Builder, Cargo, RunConfig, ShouldRun, Step}; +use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; use crate::utils::helpers::{self, t}; +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct Gcc { + pub target: TargetSelection, +} + +#[derive(Clone)] +pub struct GccOutput { + pub libgccjit: PathBuf, +} + +impl Step for Gcc { + type Output = GccOutput; + + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/gcc").alias("gcc") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Gcc { target: run.target }); + } + + /// Compile GCC (specifically `libgccjit`) for `target`. + fn run(self, builder: &Builder<'_>) -> Self::Output { + let target = self.target; + + // If GCC has already been built, we avoid building it again. + let metadata = match get_gcc_build_status(builder, target) { + GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: path }, + GccBuildStatus::ShouldBuild(m) => m, + }; + + let _guard = builder.msg_unstaged(Kind::Build, "GCC", target); + t!(metadata.stamp.remove()); + let _time = helpers::timeit(builder); + + let libgccjit_path = libgccjit_built_path(&metadata.install_dir); + if builder.config.dry_run() { + return GccOutput { libgccjit: libgccjit_path }; + } + + build_gcc(&metadata, builder, target); + create_lib_alias(builder, &libgccjit_path); + + t!(metadata.stamp.write()); + + GccOutput { libgccjit: libgccjit_path } + } +} + +/// Creates a libgccjit.so.0 alias next to libgccjit.so if it does not +/// already exist +fn create_lib_alias(builder: &Builder<'_>, libgccjit: &PathBuf) { + let lib_alias = libgccjit.parent().unwrap().join("libgccjit.so.0"); + if !lib_alias.exists() { + t!(builder.symlink_file(libgccjit, lib_alias)); + } +} + pub struct Meta { stamp: BuildStamp, out_dir: PathBuf, @@ -34,17 +91,71 @@ pub enum GccBuildStatus { ShouldBuild(Meta), } -/// This returns whether we've already previously built GCC. +/// Tries to download GCC from CI if it is enabled and GCC artifacts +/// are available for the given target. +/// Returns a path to the libgccjit.so file. +#[cfg(not(test))] +fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option { + use build_helper::git::PathFreshness; + + // Try to download GCC from CI if configured and available + if !matches!(builder.config.gcc_ci_mode, crate::core::config::GccCiMode::DownloadFromCi) { + return None; + } + if target != "x86_64-unknown-linux-gnu" { + eprintln!("GCC CI download is only available for the `x86_64-unknown-linux-gnu` target"); + return None; + } + let source = detect_gcc_freshness( + &builder.config, + builder.config.rust_info.is_managed_git_subrepository(), + ); + builder.verbose(|| { + eprintln!("GCC freshness: {source:?}"); + }); + match source { + PathFreshness::LastModifiedUpstream { upstream } => { + // Download from upstream CI + let root = ci_gcc_root(&builder.config); + let gcc_stamp = BuildStamp::new(&root).with_prefix("gcc").add_stamp(&upstream); + if !gcc_stamp.is_up_to_date() && !builder.config.dry_run() { + builder.config.download_ci_gcc(&upstream, &root); + t!(gcc_stamp.write()); + } + + let libgccjit = root.join("lib").join("libgccjit.so"); + create_lib_alias(builder, &libgccjit); + Some(libgccjit) + } + PathFreshness::HasLocalModifications { .. } => { + // We have local modifications, rebuild GCC. + eprintln!("Found local GCC modifications, GCC will *not* be downloaded"); + None + } + PathFreshness::MissingUpstream => { + eprintln!("error: could not find commit hash for downloading GCC"); + eprintln!("HELP: maybe your repository history is too shallow?"); + eprintln!("HELP: consider disabling `download-ci-gcc`"); + eprintln!("HELP: or fetch enough history to include one upstream commit"); + None + } + } +} + +#[cfg(test)] +fn try_download_gcc(_builder: &Builder<'_>, _target: TargetSelection) -> Option { + None +} + +/// This returns information about whether GCC should be built or if it's already built. +/// It transparently handles downloading GCC from CI if needed. /// /// It's used to avoid busting caches during x.py check -- if we've already built /// GCC, it's fine for us to not try to avoid doing so. -pub fn prebuilt_gcc_config(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus { - // Initialize the gcc submodule if not initialized already. - builder.config.update_submodule("src/gcc"); - - let root = builder.src.join("src/gcc"); - let out_dir = builder.gcc_out(target).join("build"); - let install_dir = builder.gcc_out(target).join("install"); +pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus { + if let Some(path) = try_download_gcc(builder, target) { + return GccBuildStatus::AlreadyBuilt(path); + } static STAMP_HASH_MEMO: OnceLock = OnceLock::new(); let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { @@ -55,6 +166,13 @@ pub fn prebuilt_gcc_config(builder: &Builder<'_>, target: TargetSelection) -> Gc ) }); + // Initialize the gcc submodule if not initialized already. + builder.config.update_submodule("src/gcc"); + + let root = builder.src.join("src/gcc"); + let out_dir = builder.gcc_out(target).join("build"); + let install_dir = builder.gcc_out(target).join("install"); + let stamp = BuildStamp::new(&out_dir).with_prefix("gcc").add_stamp(smart_stamp_hash); if stamp.is_up_to_date() { @@ -87,116 +205,75 @@ fn libgccjit_built_path(install_dir: &Path) -> PathBuf { install_dir.join("lib/libgccjit.so") } -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct Gcc { - pub target: TargetSelection, -} - -#[derive(Clone)] -pub struct GccOutput { - pub libgccjit: PathBuf, -} - -impl Step for Gcc { - type Output = GccOutput; - - const ONLY_HOSTS: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/gcc").alias("gcc") - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Gcc { target: run.target }); +fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) { + if builder.build.cc_tool(target).is_like_clang() + || builder.build.cxx_tool(target).is_like_clang() + { + panic!( + "Attempting to build GCC using Clang, which is known to misbehave. Please use GCC as the host C/C++ compiler. " + ); } - /// Compile GCC (specifically `libgccjit`) for `target`. - fn run(self, builder: &Builder<'_>) -> Self::Output { - let target = self.target; - - // If GCC has already been built, we avoid building it again. - let Meta { stamp, out_dir, install_dir, root } = match prebuilt_gcc_config(builder, target) - { - GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: path }, - GccBuildStatus::ShouldBuild(m) => m, - }; + let Meta { stamp: _, out_dir, install_dir, root } = metadata; - let _guard = builder.msg_unstaged(Kind::Build, "GCC", target); - t!(stamp.remove()); - let _time = helpers::timeit(builder); - t!(fs::create_dir_all(&out_dir)); + t!(fs::create_dir_all(out_dir)); + t!(fs::create_dir_all(install_dir)); - let libgccjit_path = libgccjit_built_path(&install_dir); - if builder.config.dry_run() { - return GccOutput { libgccjit: libgccjit_path }; + // GCC creates files (e.g. symlinks to the downloaded dependencies) + // in the source directory, which does not work with our CI setup, where we mount + // source directories as read-only on Linux. + // Therefore, as a part of the build in CI, we first copy the whole source directory + // to the build directory, and perform the build from there. + let src_dir = if builder.config.is_running_on_ci { + let src_dir = builder.gcc_out(target).join("src"); + if src_dir.exists() { + builder.remove_dir(&src_dir); } + builder.create_dir(&src_dir); + builder.cp_link_r(root, &src_dir); + src_dir + } else { + root.clone() + }; - // GCC creates files (e.g. symlinks to the downloaded dependencies) - // in the source directory, which does not work with our CI setup, where we mount - // source directories as read-only on Linux. - // Therefore, as a part of the build in CI, we first copy the whole source directory - // to the build directory, and perform the build from there. - let src_dir = if CiEnv::is_ci() { - let src_dir = builder.gcc_out(target).join("src"); - if src_dir.exists() { - builder.remove_dir(&src_dir); - } - builder.create_dir(&src_dir); - builder.cp_link_r(&root, &src_dir); - src_dir - } else { - root - }; + command(src_dir.join("contrib/download_prerequisites")).current_dir(&src_dir).run(builder); + let mut configure_cmd = command(src_dir.join("configure")); + configure_cmd + .current_dir(out_dir) + .arg("--enable-host-shared") + .arg("--enable-languages=c,jit,lto") + .arg("--enable-checking=release") + .arg("--disable-bootstrap") + .arg("--disable-multilib") + .arg(format!("--prefix={}", install_dir.display())); + + let cc = builder.build.cc(target).display().to_string(); + let cc = builder + .build + .config + .ccache + .as_ref() + .map_or_else(|| cc.clone(), |ccache| format!("{ccache} {cc}")); + configure_cmd.env("CC", cc); - command(src_dir.join("contrib/download_prerequisites")).current_dir(&src_dir).run(builder); - let mut configure_cmd = command(src_dir.join("configure")); - configure_cmd - .current_dir(&out_dir) - // On CI, we compile GCC with Clang. - // The -Wno-everything flag is needed to make GCC compile with Clang 19. - // `-g -O2` are the default flags that are otherwise used by Make. - // FIXME(kobzol): change the flags once we have [gcc] configuration in config.toml. - .env("CXXFLAGS", "-Wno-everything -g -O2") - .env("CFLAGS", "-Wno-everything -g -O2") - .arg("--enable-host-shared") - .arg("--enable-languages=jit") - .arg("--enable-checking=release") - .arg("--disable-bootstrap") - .arg("--disable-multilib") - .arg(format!("--prefix={}", install_dir.display())); - let cc = builder.build.cc(target).display().to_string(); - let cc = builder + if let Ok(ref cxx) = builder.build.cxx(target) { + let cxx = cxx.display().to_string(); + let cxx = builder .build .config .ccache .as_ref() - .map_or_else(|| cc.clone(), |ccache| format!("{ccache} {cc}")); - configure_cmd.env("CC", cc); - - if let Ok(ref cxx) = builder.build.cxx(target) { - let cxx = cxx.display().to_string(); - let cxx = builder - .build - .config - .ccache - .as_ref() - .map_or_else(|| cxx.clone(), |ccache| format!("{ccache} {cxx}")); - configure_cmd.env("CXX", cxx); - } - configure_cmd.run(builder); - - command("make").current_dir(&out_dir).arg(format!("-j{}", builder.jobs())).run(builder); - command("make").current_dir(&out_dir).arg("install").run(builder); - - let lib_alias = install_dir.join("lib/libgccjit.so.0"); - if !lib_alias.exists() { - t!(builder.symlink_file(&libgccjit_path, lib_alias)); - } - - t!(stamp.write()); - - GccOutput { libgccjit: libgccjit_path } + .map_or_else(|| cxx.clone(), |ccache| format!("{ccache} {cxx}")); + configure_cmd.env("CXX", cxx); } + configure_cmd.run(builder); + + command("make") + .current_dir(out_dir) + .arg("--silent") + .arg(format!("-j{}", builder.jobs())) + .run_capture_stdout(builder); + command("make").current_dir(out_dir).arg("--silent").arg("install").run_capture_stdout(builder); } /// Configures a Cargo invocation so that it can build the GCC codegen backend. @@ -204,3 +281,23 @@ pub fn add_cg_gcc_cargo_flags(cargo: &mut Cargo, gcc: &GccOutput) { // Add the path to libgccjit.so to the linker search paths. cargo.rustflag(&format!("-L{}", gcc.libgccjit.parent().unwrap().to_str().unwrap())); } + +/// The absolute path to the downloaded GCC artifacts. +#[cfg(not(test))] +fn ci_gcc_root(config: &crate::Config) -> PathBuf { + config.out.join(config.build).join("ci-gcc") +} + +/// Detect whether GCC sources have been modified locally or not. +#[cfg(not(test))] +fn detect_gcc_freshness(config: &crate::Config, is_git: bool) -> build_helper::git::PathFreshness { + use build_helper::git::PathFreshness; + + if is_git { + config.check_path_modifications(&["src/gcc", "src/bootstrap/download-ci-gcc-stamp"]) + } else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) { + PathFreshness::LastModifiedUpstream { upstream: info.sha.trim().to_owned() } + } else { + PathFreshness::MissingUpstream + } +} diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 4f96d67a5bd89..585adf9be1604 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -53,7 +53,7 @@ fn is_dir_writable_for_user(dir: &Path) -> bool { if e.kind() == std::io::ErrorKind::PermissionDenied { false } else { - panic!("Failed the write access check for the current user. {}", e); + panic!("Failed the write access check for the current user. {e}"); } } } @@ -85,11 +85,11 @@ fn install_sh( } else { assert!( is_dir_writable_for_user(&prefix), - "User doesn't have write access on `install.prefix` path in the `config.toml`.", + "User doesn't have write access on `install.prefix` path in the `bootstrap.toml`.", ); assert!( is_dir_writable_for_user(&sysconfdir), - "User doesn't have write access on `install.sysconfdir` path in `config.toml`." + "User doesn't have write access on `install.sysconfdir` path in `bootstrap.toml`." ); } @@ -244,7 +244,7 @@ install!((self, builder, _config), ); } }; - LlvmTools, alias = "llvm-tools", Self::should_build(_config), only_hosts: true, { + LlvmTools, alias = "llvm-tools", _config.llvm_tools_enabled && _config.llvm_enabled(_config.build), only_hosts: true, { if let Some(tarball) = builder.ensure(dist::LlvmTools { target: self.target }) { install_sh(builder, "llvm-tools", self.compiler.stage, Some(self.target), &tarball); } else { diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 40d701f22c137..a378819747164 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -14,8 +14,7 @@ use std::path::{Path, PathBuf}; use std::sync::OnceLock; use std::{env, fs}; -use build_helper::ci::CiEnv; -use build_helper::git::get_closest_merge_commit; +use build_helper::git::PathFreshness; #[cfg(feature = "tracing")] use tracing::instrument; @@ -174,43 +173,30 @@ pub fn prebuilt_llvm_config( LlvmBuildStatus::ShouldBuild(Meta { stamp, res, out_dir, root: root.into() }) } -/// This retrieves the LLVM sha we *want* to use, according to git history. -pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String { - let llvm_sha = if is_git { - get_closest_merge_commit( - Some(&config.src), - &config.git_config(), - &[ - config.src.join("src/llvm-project"), - config.src.join("src/bootstrap/download-ci-llvm-stamp"), - // the LLVM shared object file is named `LLVM-12-rust-{version}-nightly` - config.src.join("src/version"), - ], - ) - .unwrap() +/// Paths whose changes invalidate LLVM downloads. +pub const LLVM_INVALIDATION_PATHS: &[&str] = &[ + "src/llvm-project", + "src/bootstrap/download-ci-llvm-stamp", + // the LLVM shared object file is named `LLVM--rust-{version}-nightly` + "src/version", +]; + +/// Detect whether LLVM sources have been modified locally or not. +pub(crate) fn detect_llvm_freshness(config: &Config, is_git: bool) -> PathFreshness { + if is_git { + config.check_path_modifications(LLVM_INVALIDATION_PATHS) } else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) { - info.sha.trim().to_owned() + PathFreshness::LastModifiedUpstream { upstream: info.sha.trim().to_owned() } } else { - "".to_owned() - }; - - if llvm_sha.is_empty() { - eprintln!("error: could not find commit hash for downloading LLVM"); - eprintln!("HELP: maybe your repository history is too shallow?"); - eprintln!("HELP: consider disabling `download-ci-llvm`"); - eprintln!("HELP: or fetch enough history to include one upstream commit"); - panic!(); + PathFreshness::MissingUpstream } - - llvm_sha } /// Returns whether the CI-found LLVM is currently usable. /// -/// This checks both the build triple platform to confirm we're usable at all, -/// and then verifies if the current HEAD matches the detected LLVM SHA head, -/// in which case LLVM is indicated as not available. -pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool { +/// This checks the build triple platform to confirm we're usable at all, and if LLVM +/// with/without assertions is available. +pub(crate) fn is_ci_llvm_available_for_target(config: &Config, asserts: bool) -> bool { // This is currently all tier 1 targets and tier 2 targets with host tools // (since others may not have CI artifacts) // https://doc.rust-lang.org/rustc/platform-support.html#tier-1 @@ -255,41 +241,9 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool { return false; } - if is_ci_llvm_modified(config) { - eprintln!("Detected LLVM as non-available: running in CI and modified LLVM in this change"); - return false; - } - true } -/// Returns true if we're running in CI with modified LLVM (and thus can't download it) -pub(crate) fn is_ci_llvm_modified(config: &Config) -> bool { - // If not running in a CI environment, return false. - if !CiEnv::is_ci() { - return false; - } - - // In rust-lang/rust managed CI, assert the existence of the LLVM submodule. - if CiEnv::is_rust_lang_managed_ci_job() { - assert!( - config.in_tree_llvm_info.is_managed_git_subrepository(), - "LLVM submodule must be fetched in rust-lang/rust managed CI builders." - ); - } - // If LLVM submodule isn't present, skip the change check as it won't work. - else if !config.in_tree_llvm_info.is_managed_git_subrepository() { - return false; - } - - let llvm_sha = detect_llvm_sha(config, true); - let head_sha = crate::output( - helpers::git(Some(&config.src)).arg("rev-parse").arg("HEAD").as_command_mut(), - ); - let head_sha = head_sha.trim(); - llvm_sha == head_sha -} - #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Llvm { pub target: TargetSelection, @@ -350,7 +304,7 @@ impl Step for Llvm { (true, true) => "RelWithDebInfo", }; - // NOTE: remember to also update `config.example.toml` when changing the + // NOTE: remember to also update `bootstrap.example.toml` when changing the // defaults! let llvm_targets = match &builder.config.llvm_targets { Some(s) => s, @@ -381,8 +335,6 @@ impl Step for Llvm { .define("LLVM_INCLUDE_DOCS", "OFF") .define("LLVM_INCLUDE_BENCHMARKS", "OFF") .define("LLVM_INCLUDE_TESTS", enable_tests) - // FIXME: remove this when minimal llvm is 19 - .define("LLVM_ENABLE_TERMINFO", "OFF") .define("LLVM_ENABLE_LIBEDIT", "OFF") .define("LLVM_ENABLE_BINDINGS", "OFF") .define("LLVM_ENABLE_Z3_SOLVER", "OFF") @@ -407,8 +359,8 @@ impl Step for Llvm { cfg.define("LLVM_PROFDATA_FILE", path); } - // Libraries for ELF section compression. - if !target.is_windows() { + // Libraries for ELF section compression and profraw files merging. + if !target.is_msvc() { cfg.define("LLVM_ENABLE_ZLIB", "ON"); } else { cfg.define("LLVM_ENABLE_ZLIB", "OFF"); @@ -420,9 +372,6 @@ impl Step for Llvm { || target.contains("apple-watchos") || target.contains("apple-visionos") { - // These two defines prevent CMake from automatically trying to add a MacOSX sysroot, which leads to a compiler error. - cfg.define("CMAKE_OSX_SYSROOT", "/"); - cfg.define("CMAKE_OSX_DEPLOYMENT_TARGET", ""); // Prevent cmake from adding -bundle to CFLAGS automatically, which leads to a compiler error because "-bitcode_bundle" also gets added. cfg.define("LLVM_ENABLE_PLUGINS", "OFF"); // Zlib fails to link properly, leading to a compiler error. @@ -471,11 +420,14 @@ impl Step for Llvm { cfg.define("LLVM_BUILD_32_BITS", "ON"); } + if target.starts_with("x86_64") && target.contains("ohos") { + cfg.define("LLVM_TOOL_LLVM_RTDYLD_BUILD", "OFF"); + } + let mut enabled_llvm_projects = Vec::new(); if helpers::forcing_clang_based_tests() { enabled_llvm_projects.push("clang"); - enabled_llvm_projects.push("compiler-rt"); } if builder.config.llvm_polly { @@ -498,6 +450,10 @@ impl Step for Llvm { let mut enabled_llvm_runtimes = Vec::new(); + if helpers::forcing_clang_based_tests() { + enabled_llvm_runtimes.push("compiler-rt"); + } + if builder.config.llvm_offload { enabled_llvm_runtimes.push("offload"); //FIXME(ZuseZ4): LLVM intends to drop the offload dependency on openmp. @@ -518,7 +474,7 @@ impl Step for Llvm { } // https://llvm.org/docs/HowToCrossCompileLLVM.html - if !builder.is_builder_target(target) { + if !builder.config.is_host_target(target) { let LlvmResult { llvm_config, .. } = builder.ensure(Llvm { target: builder.config.build }); if !builder.config.dry_run() { @@ -641,11 +597,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { let version = get_llvm_version(builder, llvm_config); let mut parts = version.split('.').take(2).filter_map(|s| s.parse::().ok()); if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) { - if major >= 18 { + if major >= 19 { return; } } - panic!("\n\nbad LLVM version: {version}, need >=18\n\n") + panic!("\n\nbad LLVM version: {version}, need >=19\n\n") } fn configure_cmake( @@ -670,13 +626,20 @@ fn configure_cmake( } cfg.target(&target.triple).host(&builder.config.build.triple); - if !builder.is_builder_target(target) { + if !builder.config.is_host_target(target) { cfg.define("CMAKE_CROSSCOMPILING", "True"); + // NOTE: Ideally, we wouldn't have to do this, and `cmake-rs` would just handle it for us. + // But it currently determines this based on the `CARGO_CFG_TARGET_OS` environment variable, + // which isn't set when compiling outside `build.rs` (like bootstrap is). + // + // So for now, we define `CMAKE_SYSTEM_NAME` ourselves, to panicking in `cmake-rs`. if target.contains("netbsd") { cfg.define("CMAKE_SYSTEM_NAME", "NetBSD"); } else if target.contains("dragonfly") { cfg.define("CMAKE_SYSTEM_NAME", "DragonFly"); + } else if target.contains("openbsd") { + cfg.define("CMAKE_SYSTEM_NAME", "OpenBSD"); } else if target.contains("freebsd") { cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD"); } else if target.is_windows() { @@ -687,10 +650,27 @@ fn configure_cmake( cfg.define("CMAKE_SYSTEM_NAME", "SunOS"); } else if target.contains("linux") { cfg.define("CMAKE_SYSTEM_NAME", "Linux"); + } else if target.contains("darwin") { + // macOS + cfg.define("CMAKE_SYSTEM_NAME", "Darwin"); + } else if target.contains("ios") { + cfg.define("CMAKE_SYSTEM_NAME", "iOS"); + } else if target.contains("tvos") { + cfg.define("CMAKE_SYSTEM_NAME", "tvOS"); + } else if target.contains("visionos") { + cfg.define("CMAKE_SYSTEM_NAME", "visionOS"); + } else if target.contains("watchos") { + cfg.define("CMAKE_SYSTEM_NAME", "watchOS"); + } else if target.contains("none") { + // "none" should be the last branch + cfg.define("CMAKE_SYSTEM_NAME", "Generic"); } else { builder.info(&format!( "could not determine CMAKE_SYSTEM_NAME from the target `{target}`, build may fail", )); + // Fallback, set `CMAKE_SYSTEM_NAME` anyhow to avoid the logic `cmake-rs` tries, and + // to avoid CMAKE_SYSTEM_NAME being inferred from the host. + cfg.define("CMAKE_SYSTEM_NAME", "Generic"); } // When cross-compiling we should also set CMAKE_SYSTEM_VERSION, but in @@ -700,7 +680,19 @@ fn configure_cmake( // CMakeFiles (and then only in tests), and so far no issues have been // reported, the system version is currently left unset. - if target.contains("darwin") { + if target.contains("apple") { + if !target.contains("darwin") { + // FIXME(madsmtm): compiler-rt's CMake setup is kinda weird, it seems like they do + // version testing etc. for macOS (i.e. Darwin), even while building for iOS? + // + // So for now we set it to "Darwin" on all Apple platforms. + cfg.define("CMAKE_SYSTEM_NAME", "Darwin"); + + // These two defines prevent CMake from automatically trying to add a MacOSX sysroot, which leads to a compiler error. + cfg.define("CMAKE_OSX_SYSROOT", "/"); + cfg.define("CMAKE_OSX_DEPLOYMENT_TARGET", ""); + } + // Make sure that CMake does not build universal binaries on macOS. // Explicitly specify the one single target architecture. if target.starts_with("aarch64") { @@ -735,57 +727,17 @@ fn configure_cmake( None => (builder.cc(target), builder.cxx(target).unwrap()), }; - // Handle msvc + ninja + ccache specially (this is what the bots use) - if target.is_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"); - - cfg.define("CMAKE_C_COMPILER", sanitize_cc(&wrap_cc)) - .define("CMAKE_CXX_COMPILER", sanitize_cc(&wrap_cc)); - cfg.env("SCCACHE_PATH", builder.config.ccache.as_ref().unwrap()) - .env("SCCACHE_TARGET", target.triple) - .env("SCCACHE_CC", &cc) - .env("SCCACHE_CXX", &cxx); - - // Building LLVM on MSVC can be a little ludicrous at times. We're so far - // off the beaten path here that I'm not really sure this is even half - // supported any more. Here we're trying to: - // - // * Build LLVM on MSVC - // * Build LLVM with `clang-cl` instead of `cl.exe` - // * Build a project with `sccache` - // * Build for 32-bit as well - // * Build with Ninja - // - // For `cl.exe` there are different binaries to compile 32/64 bit which - // we use but for `clang-cl` there's only one which internally - // multiplexes via flags. As a result it appears that CMake's detection - // of a compiler's architecture and such on MSVC **doesn't** pass any - // custom flags we pass in CMAKE_CXX_FLAGS below. This means that if we - // use `clang-cl.exe` it's always diagnosed as a 64-bit compiler which - // definitely causes problems since all the env vars are pointing to - // 32-bit libraries. - // - // To hack around this... again... we pass an argument that's - // unconditionally passed in the sccache shim. This'll get CMake to - // correctly diagnose it's doing a 32-bit compilation and LLVM will - // internally configure itself appropriately. - if builder.config.llvm_clang_cl.is_some() && target.contains("i686") { - cfg.env("SCCACHE_EXTRA_ARGS", "-m32"); + // If ccache is configured we inform the build a little differently how + // to invoke ccache while also invoking our compilers. + if use_compiler_launcher { + if let Some(ref ccache) = builder.config.ccache { + cfg.define("CMAKE_C_COMPILER_LAUNCHER", ccache) + .define("CMAKE_CXX_COMPILER_LAUNCHER", ccache); } - } else { - // If ccache is configured we inform the build a little differently how - // to invoke ccache while also invoking our compilers. - if use_compiler_launcher { - if let Some(ref ccache) = builder.config.ccache { - cfg.define("CMAKE_C_COMPILER_LAUNCHER", ccache) - .define("CMAKE_CXX_COMPILER_LAUNCHER", ccache); - } - } - cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc)) - .define("CMAKE_CXX_COMPILER", sanitize_cc(&cxx)) - .define("CMAKE_ASM_COMPILER", sanitize_cc(&cc)); } + cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc)) + .define("CMAKE_CXX_COMPILER", sanitize_cc(&cxx)) + .define("CMAKE_ASM_COMPILER", sanitize_cc(&cc)); cfg.build_arg("-j").build_arg(builder.jobs().to_string()); // FIXME(madsmtm): Allow `cmake-rs` to select flags by itself by passing @@ -809,7 +761,9 @@ fn configure_cmake( cflags.push(" "); cflags.push(s); } - + if target.contains("ohos") { + cflags.push(" -D_LINUX_SYSINFO_H"); + } if builder.config.llvm_clang_cl.is_some() { cflags.push(format!(" --target={target}")); } @@ -830,6 +784,9 @@ fn configure_cmake( cxxflags.push(" "); cxxflags.push(s); } + if target.contains("ohos") { + cxxflags.push(" -D_LINUX_SYSINFO_H"); + } if builder.config.llvm_clang_cl.is_some() { cxxflags.push(format!(" --target={target}")); } @@ -913,8 +870,8 @@ fn get_var(var_base: &str, host: &str, target: &str) -> Option { let kind = if host == target { "HOST" } else { "TARGET" }; let target_u = target.replace('-', "_"); env::var_os(format!("{var_base}_{target}")) - .or_else(|| env::var_os(format!("{}_{}", var_base, target_u))) - .or_else(|| env::var_os(format!("{}_{}", kind, var_base))) + .or_else(|| env::var_os(format!("{var_base}_{target_u}"))) + .or_else(|| env::var_os(format!("{kind}_{var_base}"))) .or_else(|| env::var_os(var_base)) } @@ -987,7 +944,7 @@ impl Step for Enzyme { } trace!(?target, "(re)building enzyme artifacts"); - builder.info(&format!("Building Enzyme for {}", target)); + builder.info(&format!("Building Enzyme for {target}")); t!(stamp.remove()); let _time = helpers::timeit(builder); t!(fs::create_dir_all(&out_dir)); @@ -1130,7 +1087,7 @@ impl Step for Lld { .define("LLVM_CMAKE_DIR", llvm_cmake_dir) .define("LLVM_INCLUDE_TESTS", "OFF"); - if !builder.is_builder_target(target) { + if !builder.config.is_host_target(target) { // Use the host llvm-tblgen binary. cfg.define( "LLVM_TABLEGEN_EXE", @@ -1216,6 +1173,10 @@ impl Step for Sanitizers { cfg.define("COMPILER_RT_USE_LIBCXX", "OFF"); cfg.define("LLVM_CONFIG_PATH", &llvm_config); + if self.target.contains("ohos") { + cfg.define("COMPILER_RT_USE_BUILTINS_LIBRARY", "ON"); + } + // On Darwin targets the sanitizer runtimes are build as universal binaries. // Unfortunately sccache currently lacks support to build them successfully. // Disable compiler launcher on Darwin targets to avoid potential issues. @@ -1268,10 +1229,9 @@ fn supported_sanitizers( components .iter() .map(move |c| SanitizerRuntime { - cmake_target: format!("clang_rt.{}_{}_dynamic", c, os), - path: out_dir - .join(format!("build/lib/darwin/libclang_rt.{}_{}_dynamic.dylib", c, os)), - name: format!("librustc-{}_rt.{}.dylib", channel, c), + cmake_target: format!("clang_rt.{c}_{os}_dynamic"), + path: out_dir.join(format!("build/lib/darwin/libclang_rt.{c}_{os}_dynamic.dylib")), + name: format!("librustc-{channel}_rt.{c}.dylib"), }) .collect() }; @@ -1280,9 +1240,9 @@ fn supported_sanitizers( components .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)), - name: format!("librustc-{}_rt.{}.a", channel, c), + cmake_target: format!("clang_rt.{c}-{arch}"), + path: out_dir.join(format!("build/lib/{os}/libclang_rt.{c}-{arch}.a")), + name: format!("librustc-{channel}_rt.{c}.a"), }) .collect() }; @@ -1401,8 +1361,8 @@ impl Step for CrtBeginEnd { for obj in objs { let base_name = unhashed_basename(&obj); assert!(base_name == "crtbegin" || base_name == "crtend"); - t!(fs::copy(&obj, out_dir.join(format!("{}S.o", base_name)))); - t!(fs::rename(&obj, out_dir.join(format!("{}.o", base_name)))); + t!(fs::copy(&obj, out_dir.join(format!("{base_name}S.o")))); + t!(fs::rename(&obj, out_dir.join(format!("{base_name}.o")))); } out_dir diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs index 6962001fdc240..71cdb665ed47e 100644 --- a/src/bootstrap/src/core/build_steps/perf.rs +++ b/src/bootstrap/src/core/build_steps/perf.rs @@ -1,3 +1,4 @@ +use std::env::consts::EXE_EXTENSION; use std::fmt::{Display, Formatter}; use crate::core::build_steps::compile::{Std, Sysroot}; @@ -147,7 +148,7 @@ pub fn perf(builder: &Builder<'_>, args: &PerfArgs) { }; if is_profiling && builder.build.config.rust_debuginfo_level_rustc == DebuginfoLevel::None { builder.info(r#"WARNING: You are compiling rustc without debuginfo, this will make profiling less useful. -Consider setting `rust.debuginfo-level = 1` in `config.toml`."#); +Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); } let compiler = builder.compiler(builder.top_stage, builder.config.build); @@ -160,7 +161,10 @@ Consider setting `rust.debuginfo-level = 1` in `config.toml`."#); } let sysroot = builder.ensure(Sysroot::new(compiler)); - let rustc = sysroot.join("bin/rustc"); + let mut rustc = sysroot.clone(); + rustc.push("bin"); + rustc.push("rustc"); + rustc.set_extension(EXE_EXTENSION); let rustc_perf_dir = builder.build.tempdir().join("rustc-perf"); let results_dir = rustc_perf_dir.join("results"); diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index fea8232296eca..0bba441c3fa26 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -35,10 +35,10 @@ impl Step for BuildManifest { // (https://github.com/rust-lang/promote-release). let mut cmd = builder.tool_cmd(Tool::BuildManifest); let sign = builder.config.dist_sign_folder.as_ref().unwrap_or_else(|| { - panic!("\n\nfailed to specify `dist.sign-folder` in `config.toml`\n\n") + panic!("\n\nfailed to specify `dist.sign-folder` in `bootstrap.toml`\n\n") }); let addr = builder.config.dist_upload_addr.as_ref().unwrap_or_else(|| { - panic!("\n\nfailed to specify `dist.upload-addr` in `config.toml`\n\n") + panic!("\n\nfailed to specify `dist.upload-addr` in `bootstrap.toml`\n\n") }); let today = command("date").arg("+%Y-%m-%d").run_capture_stdout(builder).stdout(); @@ -367,3 +367,109 @@ impl Step for FeaturesStatusDump { cmd.run(builder); } } + +/// Dummy step that can be used to deliberately trigger bootstrap's step cycle +/// detector, for automated and manual testing. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct CyclicStep { + n: u32, +} + +impl Step for CyclicStep { + type Output = (); + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("cyclic-step") + } + + fn make_run(run: RunConfig<'_>) { + // Start with n=2, so that we build up a few stack entries before panicking. + run.builder.ensure(CyclicStep { n: 2 }) + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + // When n=0, the step will try to ensure itself, causing a step cycle. + builder.ensure(CyclicStep { n: self.n.saturating_sub(1) }) + } +} + +/// Step to manually run the coverage-dump tool (`./x run coverage-dump`). +/// +/// The coverage-dump tool is an internal detail of coverage tests, so this run +/// step is only needed when testing coverage-dump manually. +#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] +pub struct CoverageDump; + +impl Step for CoverageDump { + type Output = (); + + const DEFAULT: bool = false; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/coverage-dump") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Self {}); + } + + fn run(self, builder: &Builder<'_>) { + let mut cmd = builder.tool_cmd(Tool::CoverageDump); + cmd.args(&builder.config.free_args); + cmd.run(builder); + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Rustfmt; + +impl Step for Rustfmt { + type Output = (); + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/rustfmt") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Rustfmt); + } + + fn run(self, builder: &Builder<'_>) { + let host = builder.build.build; + + // `x run` uses stage 0 by default but rustfmt does not work well with stage 0. + // Change the stage to 1 if it's not set explicitly. + let stage = if builder.config.is_explicit_stage() || builder.top_stage >= 1 { + builder.top_stage + } else { + 1 + }; + + if stage == 0 { + eprintln!("rustfmt cannot be run at stage 0"); + eprintln!("HELP: Use `x fmt` to use stage 0 rustfmt."); + std::process::exit(1); + } + + let compiler = builder.compiler(stage, host); + let rustfmt_build = builder.ensure(tool::Rustfmt { compiler, target: host }); + + let mut rustfmt = tool::prepare_tool_cargo( + builder, + rustfmt_build.build_compiler, + Mode::ToolRustc, + host, + Kind::Run, + "src/tools/rustfmt", + SourceType::InTree, + &[], + ); + + rustfmt.args(["--bin", "rustfmt", "--"]); + rustfmt.args(builder.config.args()); + + rustfmt.into_cmd().run(builder); + } +} diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index f25dfaab0f115..31ec462134d5d 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -2,8 +2,8 @@ //! //! These are build-and-run steps for `./x.py setup`, which allows quickly setting up the directory //! for modifying, building, and running the compiler and library. Running arbitrary configuration -//! allows setting up things that cannot be simply captured inside the config.toml, in addition to -//! leading people away from manually editing most of the config.toml values. +//! allows setting up things that cannot be simply captured inside the bootstrap.toml, in addition to +//! leading people away from manually editing most of the bootstrap.toml values. use std::env::consts::EXE_SUFFIX; use std::fmt::Write as _; @@ -37,7 +37,7 @@ static PROFILE_DIR: &str = "src/bootstrap/defaults"; impl Profile { fn include_path(&self, src_path: &Path) -> PathBuf { - PathBuf::from(format!("{}/{PROFILE_DIR}/config.{}.toml", src_path.display(), self)) + PathBuf::from(format!("{}/{PROFILE_DIR}/bootstrap.{}.toml", src_path.display(), self)) } pub fn all() -> impl Iterator { @@ -53,7 +53,7 @@ impl Profile { Compiler => "Contribute to the compiler itself", Tools => "Contribute to tools which depend on the compiler, but do not modify it directly (e.g. rustdoc, clippy, miri)", Dist => "Install Rust from source", - None => "Do not modify `config.toml`" + None => "Do not modify `bootstrap.toml`" } .to_string() } @@ -85,9 +85,7 @@ impl FromStr for Profile { "lib" | "library" => Ok(Profile::Library), "compiler" => Ok(Profile::Compiler), "maintainer" | "dist" | "user" => Ok(Profile::Dist), - "tools" | "tool" | "rustdoc" | "clippy" | "miri" | "rustfmt" | "rls" => { - Ok(Profile::Tools) - } + "tools" | "tool" | "rustdoc" | "clippy" | "miri" | "rustfmt" => Ok(Profile::Tools), "none" => Ok(Profile::None), "llvm" | "codegen" => Err("the \"llvm\" and \"codegen\" profiles have been removed,\ use \"compiler\" instead which has the same functionality" @@ -119,7 +117,7 @@ impl Step for Profile { return; } - let path = &run.builder.config.config.clone().unwrap_or(PathBuf::from("config.toml")); + let path = &run.builder.config.config.clone().unwrap_or(PathBuf::from("bootstrap.toml")); if path.exists() { eprintln!(); eprintln!( @@ -205,7 +203,7 @@ pub fn setup(config: &Config, profile: Profile) { ) } - let path = &config.config.clone().unwrap_or(PathBuf::from("config.toml")); + let path = &config.config.clone().unwrap_or(PathBuf::from("bootstrap.toml")); setup_config_toml(path, profile, config); } @@ -216,8 +214,9 @@ fn setup_config_toml(path: &Path, profile: Profile, config: &Config) { let latest_change_id = CONFIG_CHANGE_HISTORY.last().unwrap().change_id; let settings = format!( - "# Includes one of the default files in {PROFILE_DIR}\n\ - profile = \"{profile}\"\n\ + "# See bootstrap.example.toml for documentation of available options\n\ + #\n\ + profile = \"{profile}\" # Includes one of the default files in {PROFILE_DIR}\n\ change-id = {latest_change_id}\n" ); @@ -553,7 +552,7 @@ Select which editor you would like to set up [default: None]: "; let mut input = String::new(); loop { - print!("{}", prompt_str); + print!("{prompt_str}"); io::stdout().flush()?; io::stdin().read_line(&mut input)?; @@ -584,10 +583,13 @@ Select which editor you would like to set up [default: None]: "; EditorKind::Emacs => &[ "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0", "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45", + "b5dd299b93dca3ceeb9b335f929293cb3d4bf4977866fbe7ceeac2a8a9f99088", + "631c837b0e98ae35fd48b0e5f743b1ca60adadf2d0a2b23566ba25df372cf1a9", ], EditorKind::Helix => &[ "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233", "6736d61409fbebba0933afd2e4c44ff2f97c1cb36cf0299a7f4a7819b8775040", + "f252dcc30ca85a193a699581e5e929d5bd6c19d40d7a7ade5e257a9517a124a5", ], EditorKind::Vim | EditorKind::VsCode => &[ "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", @@ -600,10 +602,14 @@ Select which editor you would like to set up [default: None]: "; "811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d", "4eecb58a2168b252077369da446c30ed0e658301efe69691979d1ef0443928f4", "c394386e6133bbf29ffd32c8af0bb3d4aac354cba9ee051f29612aa9350f8f8d", + "e53e9129ca5ee5dcbd6ec8b68c2d87376474eb154992deba3c6d9ab1703e0717", + "f954316090936c7e590c253ca9d524008375882fa13c5b41d7e2547a896ff893", + ], + EditorKind::Zed => &[ + "bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c", + "a5380cf5dd9328731aecc5dfb240d16dac46ed272126b9728006151ef42f5909", + "2e96bf0d443852b12f016c8fc9840ab3d0a2b4fe0b0fb3a157e8d74d5e7e0e26", ], - EditorKind::Zed => { - &["bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c"] - } } } @@ -677,7 +683,7 @@ impl Step for Editor { match EditorKind::prompt_user() { Ok(editor_kind) => { if let Some(editor_kind) = editor_kind { - while !t!(create_editor_settings_maybe(config, editor_kind.clone())) {} + while !t!(create_editor_settings_maybe(config, &editor_kind)) {} } else { println!("Ok, skipping editor setup!"); } @@ -689,7 +695,7 @@ impl Step for Editor { /// Create the recommended editor LSP config file for rustc development, or just print it /// If this method should be re-called, it returns `false`. -fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Result { +fn create_editor_settings_maybe(config: &Config, editor: &EditorKind) -> io::Result { let hashes = editor.hashes(); let (current_hash, historical_hashes) = hashes.split_last().unwrap(); let settings_path = editor.settings_path(config); @@ -746,7 +752,7 @@ fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Resu // exists but user modified, back it up Some(false) => { // exists and is not current version or outdated, so back it up - let backup = settings_path.clone().with_extension(editor.backup_extension()); + let backup = settings_path.with_extension(editor.backup_extension()); eprintln!( "WARNING: copying `{}` to `{}`", settings_path.file_name().unwrap().to_str().unwrap(), @@ -758,7 +764,7 @@ fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Resu _ => "Created", }; fs::write(&settings_path, editor.settings_template())?; - println!("{verb} `{}`", settings_filename); + println!("{verb} `{settings_filename}`"); } else { println!("\n{}", editor.settings_template()); } diff --git a/src/bootstrap/src/core/build_steps/suggest.rs b/src/bootstrap/src/core/build_steps/suggest.rs index ba9b1b2fc3317..fd4918961adba 100644 --- a/src/bootstrap/src/core/build_steps/suggest.rs +++ b/src/bootstrap/src/core/build_steps/suggest.rs @@ -1,7 +1,5 @@ //! Attempt to magically identify good tests to run -#![cfg_attr(feature = "build-metrics", allow(unused))] - use std::path::PathBuf; use std::str::FromStr; @@ -15,7 +13,6 @@ pub fn suggest(builder: &Builder<'_>, run: bool) { let git_config = builder.config.git_config(); let suggestions = builder .tool_cmd(Tool::SuggestTests) - .env("SUGGEST_TESTS_GIT_REPOSITORY", git_config.git_repository) .env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch) .env("SUGGEST_TESTS_MERGE_COMMIT_EMAIL", git_config.git_merge_commit_email) .run_capture_stdout(builder) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index c9fa2e9ba9663..27791825aa0f6 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -15,7 +15,7 @@ use crate::core::build_steps::doc::DocumentationFormat; use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags}; use crate::core::build_steps::llvm::get_llvm_version; use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; -use crate::core::build_steps::tool::{self, SourceType, Tool}; +use crate::core::build_steps::tool::{self, COMPILETEST_ALLOW_FEATURES, SourceType, Tool}; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, dist, llvm}; use crate::core::builder::{ @@ -54,6 +54,7 @@ impl Step for CrateBootstrap { run.path("src/tools/jsondoclint") .path("src/tools/suggest-tests") .path("src/tools/replace-version-placeholder") + .path("src/tools/coverage-dump") // We want `./x test tidy` to _run_ the tidy tool, not its tests. // So we need a separate alias to test the tidy tool itself. .alias("tidyselftest") @@ -90,7 +91,7 @@ impl Step for CrateBootstrap { ); let crate_name = path.rsplit_once('/').unwrap().1; - run_cargo_test(cargo, &[], &[], crate_name, crate_name, bootstrap_host, builder); + run_cargo_test(cargo, &[], &[], crate_name, bootstrap_host, builder); } } @@ -140,15 +141,7 @@ You can skip linkcheck with --skip src/tools/linkchecker" SourceType::InTree, &[], ); - run_cargo_test( - cargo, - &[], - &[], - "linkchecker", - "linkchecker self tests", - bootstrap_host, - builder, - ); + run_cargo_test(cargo, &[], &[], "linkchecker self tests", bootstrap_host, builder); if builder.doc_tests == DocTests::No { return; @@ -269,13 +262,7 @@ impl Step for Cargotest { .args(builder.config.test_args()) .env("RUSTC", builder.rustc(compiler)) .env("RUSTDOC", builder.rustdoc(compiler)); - add_rustdoc_cargo_linker_args( - &mut cmd, - builder, - compiler.host, - LldThreads::No, - compiler.stage, - ); + add_rustdoc_cargo_linker_args(&mut cmd, builder, compiler.host, LldThreads::No); cmd.delay_failure().run(builder); } } @@ -337,7 +324,7 @@ impl Step for Cargo { ); // NOTE: can't use `run_cargo_test` because we need to overwrite `PATH` - let mut cargo = prepare_cargo_test(cargo, &[], &[], "cargo", self.host, builder); + let mut cargo = prepare_cargo_test(cargo, &[], &[], self.host, builder); // Don't run cross-compile tests, we may not have cross-compiled libstd libs // available. @@ -423,7 +410,7 @@ impl Step for RustAnalyzer { cargo.env("SKIP_SLOW_TESTS", "1"); cargo.add_rustc_lib_path(builder); - run_cargo_test(cargo, &[], &[], "rust-analyzer", "rust-analyzer", host, builder); + run_cargo_test(cargo, &[], &[], "rust-analyzer", host, builder); } } @@ -472,7 +459,7 @@ impl Step for Rustfmt { cargo.add_rustc_lib_path(builder); - run_cargo_test(cargo, &[], &[], "rustfmt", "rustfmt", host, builder); + run_cargo_test(cargo, &[], &[], "rustfmt", host, builder); } } @@ -588,7 +575,7 @@ impl Step for Miri { // We can NOT use `run_cargo_test` since Miri's integration tests do not use the usual test // harness and therefore do not understand the flags added by `add_flags_and_try_run_test`. - let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", host, builder); + let mut cargo = prepare_cargo_test(cargo, &[], &[], host, builder); // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", &miri_sysroot); @@ -735,8 +722,8 @@ impl Step for CompiletestTest { SourceType::InTree, &[], ); - cargo.allow_features("test"); - run_cargo_test(cargo, &[], &[], "compiletest", "compiletest self test", host, builder); + cargo.allow_features(COMPILETEST_ALLOW_FEATURES); + run_cargo_test(cargo, &[], &[], "compiletest self test", host, builder); } } @@ -797,7 +784,7 @@ impl Step for Clippy { cargo.env("HOST_LIBS", host_libs); cargo.add_rustc_lib_path(builder); - let cargo = prepare_cargo_test(cargo, &[], &[], "clippy", host, builder); + let cargo = prepare_cargo_test(cargo, &[], &[], host, builder); let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "clippy", host, host); @@ -853,7 +840,7 @@ impl Step for RustdocTheme { .env("CFG_RELEASE_CHANNEL", &builder.config.channel) .env("RUSTDOC_REAL", builder.rustdoc(self.compiler)) .env("RUSTC_BOOTSTRAP", "1"); - cmd.args(linker_args(builder, self.compiler.host, LldThreads::No, self.compiler.stage)); + cmd.args(linker_args(builder, self.compiler.host, LldThreads::No)); cmd.delay_failure().run(builder); } @@ -1029,13 +1016,7 @@ impl Step for RustdocGUI { cmd.env("RUSTDOC", builder.rustdoc(self.compiler)) .env("RUSTC", builder.rustc(self.compiler)); - add_rustdoc_cargo_linker_args( - &mut cmd, - builder, - self.compiler.host, - LldThreads::No, - self.compiler.stage, - ); + add_rustdoc_cargo_linker_args(&mut cmd, builder, self.compiler.host, LldThreads::No); for path in &builder.paths { if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) { @@ -1121,13 +1102,13 @@ impl Step for Tidy { if builder.config.channel == "dev" || builder.config.channel == "nightly" { if !builder.config.json_output { builder.info("fmt check"); - if builder.initial_rustfmt().is_none() { + if builder.config.initial_rustfmt.is_none() { let inferred_rustfmt_dir = builder.initial_sysroot.join("bin"); eprintln!( "\ ERROR: no `rustfmt` binary found in {PATH} INFO: `rust.channel` is currently set to \"{CHAN}\" -HELP: if you are testing a beta branch, set `rust.channel` to \"beta\" in the `config.toml` file +HELP: if you are testing a beta branch, set `rust.channel` to \"beta\" in the `bootstrap.toml` file HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to `x.py test`", PATH = inferred_rustfmt_dir.display(), CHAN = builder.config.channel, @@ -1242,59 +1223,6 @@ macro_rules! test { }; } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] -pub struct RunMakeSupport { - pub compiler: Compiler, - pub target: TargetSelection, -} - -impl Step for RunMakeSupport { - type Output = PathBuf; - const DEFAULT: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.never() - } - - fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - run.builder.ensure(RunMakeSupport { compiler, target: run.build_triple() }); - } - - /// Builds run-make-support and returns the path to the resulting rlib. - fn run(self, builder: &Builder<'_>) -> PathBuf { - builder.ensure(compile::Std::new(self.compiler, self.target)); - - let cargo = tool::prepare_tool_cargo( - builder, - self.compiler, - Mode::ToolStd, - self.target, - Kind::Build, - "src/tools/run-make-support", - SourceType::InTree, - &[], - ); - - let _guard = builder.msg_tool( - Kind::Build, - Mode::ToolStd, - "run-make-support", - self.compiler.stage, - &self.compiler.host, - &self.target, - ); - cargo.into_cmd().run(builder); - - let lib_name = "librun_make_support.rlib"; - let lib = builder.tools_dir(self.compiler).join(lib_name); - - let cargo_out = builder.cargo_out(self.compiler, Mode::ToolStd, self.target).join(lib_name); - builder.copy_link(&cargo_out, &lib); - lib - } -} - /// Runs `cargo test` on the `src/tools/run-make-support` crate. /// That crate is used by run-make tests. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -1330,15 +1258,7 @@ impl Step for CrateRunMakeSupport { &[], ); cargo.allow_features("test"); - run_cargo_test( - cargo, - &[], - &[], - "run-make-support", - "run-make-support self test", - host, - builder, - ); + run_cargo_test(cargo, &[], &[], "run-make-support self test", host, builder); } } @@ -1375,7 +1295,7 @@ impl Step for CrateBuildHelper { &[], ); cargo.allow_features("test"); - run_cargo_test(cargo, &[], &[], "build_helper", "build_helper self test", host, builder); + run_cargo_test(cargo, &[], &[], "build_helper self test", host, builder); } } @@ -1446,40 +1366,7 @@ test!(Pretty { only_hosts: true, }); -/// Special-handling is needed for `run-make`, so don't use `test!` for defining `RunMake` -/// tests. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct RunMake { - pub compiler: Compiler, - pub target: TargetSelection, -} - -impl Step for RunMake { - type Output = (); - const DEFAULT: bool = true; - const ONLY_HOSTS: bool = false; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.suite_path("tests/run-make") - } - - fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - run.builder.ensure(RunMakeSupport { compiler, target: run.build_triple() }); - run.builder.ensure(RunMake { compiler, target: run.target }); - } - - fn run(self, builder: &Builder<'_>) { - builder.ensure(Compiletest { - compiler: self.compiler, - target: self.target, - mode: "run-make", - suite: "run-make", - path: "tests/run-make", - compare_mode: None, - }); - } -} +test!(RunMake { path: "tests/run-make", mode: "run-make", suite: "run-make", default: true }); test!(Assembly { path: "tests/assembly", mode: "assembly", suite: "assembly", default: true }); @@ -1706,10 +1593,10 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let build = builder.build.build; compiler = builder.compiler(compiler.stage - 1, build); let test_stage = compiler.stage + 1; - (test_stage, format!("stage{}-{}", test_stage, build)) + (test_stage, format!("stage{test_stage}-{build}")) } else { let stage = compiler.stage; - (stage, format!("stage{}-{}", stage, target)) + (stage, format!("stage{stage}-{target}")) }; if suite.ends_with("fulldeps") { @@ -1722,9 +1609,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the host: target, }); } - - // Also provide `rust_test_helpers` for the host. - builder.ensure(TestHelpers { target: compiler.host }); + if suite == "run-make" { + builder.tool_exe(Tool::RunMakeSupport); + } // ensure that `libproc_macro` is available on the host. if suite == "mir-opt" { @@ -1733,11 +1620,6 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the builder.ensure(compile::Std::new(compiler, compiler.host)); } - // As well as the target - if suite != "mir-opt" { - builder.ensure(TestHelpers { target }); - } - let mut cmd = builder.tool_cmd(Tool::Compiletest); if suite == "mir-opt" { @@ -1774,6 +1656,11 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the }; cmd.arg("--cargo-path").arg(cargo_path); + + // We need to pass the compiler that was used to compile run-make-support, + // because we have to use the same compiler to compile rmake.rs recipes. + let stage0_rustc_path = builder.compiler(0, compiler.host); + cmd.arg("--stage0-rustc-path").arg(builder.rustc(stage0_rustc_path)); } // Avoid depending on rustdoc when we don't need it. @@ -1898,11 +1785,18 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } let mut hostflags = flags.clone(); - hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display())); - hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No, compiler.stage)); + hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No)); let mut targetflags = flags; - targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display())); + + // Provide `rust_test_helpers` for both host and target. + if suite == "ui" || suite == "incremental" { + builder.ensure(TestHelpers { target: compiler.host }); + builder.ensure(TestHelpers { target }); + hostflags + .push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display())); + targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display())); + } for flag in hostflags { cmd.arg("--host-rustcflags").arg(flag); @@ -2001,9 +1895,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the .arg(llvm_components.trim()); llvm_components_passed = true; } - if !builder.is_rust_llvm(target) { - // FIXME: missing Rust patches is not the same as being system llvm; we should rename the flag at some point. - // Inspecting the tests with `// no-system-llvm` in src/test *looks* like this is doing the right thing, though. + if !builder.config.is_rust_llvm(target) { cmd.arg("--system-llvm"); } @@ -2173,7 +2065,6 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } let git_config = builder.config.git_config(); - cmd.arg("--git-repository").arg(git_config.git_repository); cmd.arg("--nightly-branch").arg(git_config.nightly_branch); cmd.arg("--git-merge-commit-email").arg(git_config.git_merge_commit_email); cmd.force_coloring_in_ci(); @@ -2585,13 +2476,12 @@ fn run_cargo_test<'a>( cargo: builder::Cargo, libtest_args: &[&str], crates: &[String], - primary_crate: &str, description: impl Into>, target: TargetSelection, builder: &Builder<'_>, ) -> bool { let compiler = cargo.compiler(); - let mut cargo = prepare_cargo_test(cargo, libtest_args, crates, primary_crate, target, builder); + let mut cargo = prepare_cargo_test(cargo, libtest_args, crates, target, builder); let _time = helpers::timeit(builder); let _group = description.into().and_then(|what| { builder.msg_sysroot_tool(Kind::Test, compiler.stage, what, compiler.host, target) @@ -2615,7 +2505,6 @@ fn prepare_cargo_test( cargo: builder::Cargo, libtest_args: &[&str], crates: &[String], - primary_crate: &str, target: TargetSelection, builder: &Builder<'_>, ) -> BootstrapCommand { @@ -2645,13 +2534,6 @@ fn prepare_cargo_test( cargo.arg("--doc"); } DocTests::No => { - let krate = &builder - .crates - .get(primary_crate) - .unwrap_or_else(|| panic!("missing crate {primary_crate}")); - if krate.has_lib { - cargo.arg("--lib"); - } cargo.args(["--bins", "--examples", "--tests", "--benches"]); } DocTests::Yes => {} @@ -2675,9 +2557,9 @@ fn prepare_cargo_test( // We skip everything on Miri as then this overwrites the libdir set up // by `Cargo::new` and that actually makes things go wrong. if builder.kind != Kind::Miri { - let mut dylib_path = dylib_path(); - dylib_path.insert(0, PathBuf::from(&*builder.sysroot_target_libdir(compiler, target))); - cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); + let mut dylib_paths = builder.rustc_lib_paths(compiler); + dylib_paths.push(PathBuf::from(&builder.sysroot_target_libdir(compiler, target))); + helpers::add_dylib_path(dylib_paths, &mut cargo); } if builder.remote_tested(target) { @@ -2712,7 +2594,7 @@ impl Step for Crate { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.crate_or_deps("sysroot").crate_or_deps("coretests") + run.crate_or_deps("sysroot").crate_or_deps("coretests").crate_or_deps("alloctests") } fn make_run(run: RunConfig<'_>) { @@ -2786,7 +2668,7 @@ impl Step for Crate { cargo } else { // Also prepare a sysroot for the target. - if !builder.is_builder_target(target) { + if !builder.config.is_host_target(target) { builder.ensure(compile::Std::new(compiler, target).force_recompile(true)); builder.ensure(RemoteCopyLibs { compiler, target }); } @@ -2826,15 +2708,19 @@ impl Step for Crate { _ => panic!("can only test libraries"), }; - run_cargo_test( - cargo, - &[], - &self.crates, - &self.crates[0], - &*crate_description(&self.crates), - target, - builder, - ); + let mut crates = self.crates.clone(); + // The core and alloc crates can't directly be tested. We + // could silently ignore them, but adding their own test + // crates is less confusing for users. We still keep core and + // alloc themself for doctests + if crates.iter().any(|crate_| crate_ == "core") { + crates.push("coretests".to_owned()); + } + if crates.iter().any(|crate_| crate_ == "alloc") { + crates.push("alloctests".to_owned()); + } + + run_cargo_test(cargo, &[], &crates, &*crate_description(&self.crates), target, builder); } } @@ -2927,15 +2813,7 @@ impl Step for CrateRustdoc { dylib_path.insert(0, PathBuf::from(&*libdir)); cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); - run_cargo_test( - cargo, - &[], - &["rustdoc:0.0.0".to_string()], - "rustdoc", - "rustdoc", - target, - builder, - ); + run_cargo_test(cargo, &[], &["rustdoc:0.0.0".to_string()], "rustdoc", target, builder); } } @@ -2992,7 +2870,6 @@ impl Step for CrateRustdocJsonTypes { libtest_args, &["rustdoc-json-types".to_string()], "rustdoc-json-types", - "rustdoc-json-types", target, builder, ); @@ -3172,7 +3049,7 @@ impl Step for Bootstrap { // bootstrap tests are racy on directory creation so just run them one at a time. // Since there's not many this shouldn't be a problem. - run_cargo_test(cargo, &["--test-threads=1"], &[], "bootstrap", None, host, builder); + run_cargo_test(cargo, &["--test-threads=1"], &[], None, host, builder); } fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -3297,7 +3174,7 @@ impl Step for RustInstaller { bootstrap_host, bootstrap_host, ); - run_cargo_test(cargo, &[], &[], "installer", None, bootstrap_host, builder); + run_cargo_test(cargo, &[], &[], None, bootstrap_host, builder); // We currently don't support running the test.sh script outside linux(?) environments. // Eventually this should likely migrate to #[test]s in rust-installer proper rather than a @@ -3485,7 +3362,7 @@ impl Step for CodegenCranelift { /* let mut prepare_cargo = build_cargo(); prepare_cargo.arg("--").arg("prepare").arg("--download-dir").arg(&download_dir); - #[allow(deprecated)] + #[expect(deprecated)] builder.config.try_run(&mut prepare_cargo.into()).unwrap(); */ @@ -3616,7 +3493,7 @@ impl Step for CodegenGCC { /* let mut prepare_cargo = build_cargo(); prepare_cargo.arg("--").arg("prepare"); - #[allow(deprecated)] + #[expect(deprecated)] builder.config.try_run(&mut prepare_cargo.into()).unwrap(); */ @@ -3677,7 +3554,7 @@ impl Step for TestFloatParse { builder.ensure(tool::TestFloatParse { host: self.host }); // Run any unit tests in the crate - let cargo_test = tool::prepare_tool_cargo( + let mut cargo_test = tool::prepare_tool_cargo( builder, compiler, Mode::ToolStd, @@ -3687,8 +3564,9 @@ impl Step for TestFloatParse { SourceType::InTree, &[], ); + cargo_test.allow_features(tool::TestFloatParse::ALLOW_FEATURES); - run_cargo_test(cargo_test, &[], &[], crate_name, crate_name, bootstrap_host, builder); + run_cargo_test(cargo_test, &[], &[], crate_name, bootstrap_host, builder); // Run the actual parse tests. let mut cargo_run = tool::prepare_tool_cargo( @@ -3701,6 +3579,7 @@ impl Step for TestFloatParse { SourceType::InTree, &[], ); + cargo_run.allow_features(tool::TestFloatParse::ALLOW_FEATURES); if !matches!(env::var("FLOAT_PARSE_TESTS_NO_SKIP_HUGE").as_deref(), Ok("1") | Ok("true")) { cargo_run.args(["--", "--skip-huge"]); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 39acb646dff4d..678aa9b01e4ad 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -26,7 +26,7 @@ use crate::core::config::{DebuginfoLevel, RustcLto, TargetSelection}; use crate::utils::channel::GitInfo; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{add_dylib_path, exe, t}; -use crate::{Compiler, Kind, Mode, gha}; +use crate::{Compiler, FileType, Kind, Mode, gha}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum SourceType { @@ -34,6 +34,12 @@ pub enum SourceType { Submodule, } +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub enum ToolArtifactKind { + Binary, + Library, +} + #[derive(Debug, Clone, Hash, PartialEq, Eq)] struct ToolBuild { compiler: Compiler, @@ -47,6 +53,8 @@ struct ToolBuild { allow_features: &'static str, /// Additional arguments to pass to the `cargo` invocation. cargo_args: Vec, + /// Whether the tool builds a binary or a library. + artifact_kind: ToolArtifactKind, } impl Builder<'_> { @@ -79,7 +87,7 @@ impl Builder<'_> { /// for using this type as `type Output = ToolBuildResult;` #[derive(Clone)] pub struct ToolBuildResult { - /// Executable path of the corresponding tool that was built. + /// Artifact path of the corresponding tool that was built. pub tool_path: PathBuf, /// Compiler used to build the tool. For non-`ToolRustc` tools this is equal to `target_compiler`. /// For `ToolRustc` this is one stage before of the `target_compiler`. @@ -113,10 +121,18 @@ impl Step for ToolBuild { match self.mode { Mode::ToolRustc => { - builder.ensure(compile::Std::new(self.compiler, self.compiler.host)); - builder.ensure(compile::Rustc::new(self.compiler, target)); + // If compiler was forced, its artifacts should be prepared earlier. + if !self.compiler.is_forced_compiler() { + builder.ensure(compile::Std::new(self.compiler, self.compiler.host)); + builder.ensure(compile::Rustc::new(self.compiler, target)); + } + } + Mode::ToolStd => { + // If compiler was forced, its artifacts should be prepared earlier. + if !self.compiler.is_forced_compiler() { + builder.ensure(compile::Std::new(self.compiler, target)) + } } - Mode::ToolStd => builder.ensure(compile::Std::new(self.compiler, target)), Mode::ToolBootstrap => {} // uses downloaded stage0 compiler libs _ => panic!("unexpected Mode for tool build"), } @@ -132,10 +148,9 @@ impl Step for ToolBuild { &self.extra_features, ); - if path.ends_with("/rustdoc") && - // rustdoc is performance sensitive, so apply LTO to it. - is_lto_stage(&self.compiler) - { + // Rustc tools (miri, clippy, cargo, rustfmt, rust-analyzer) + // could use the additional optimizations. + if self.mode == Mode::ToolRustc && is_lto_stage(&self.compiler) { let lto = match builder.config.rust_lto { RustcLto::Off => Some("off"), RustcLto::Thin => Some("thin"), @@ -179,15 +194,21 @@ impl Step for ToolBuild { if tool == "tidy" { tool = "rust-tidy"; } - let tool_path = - copy_link_tool_bin(builder, self.compiler, self.target, self.mode, tool); + let tool_path = match self.artifact_kind { + ToolArtifactKind::Binary => { + copy_link_tool_bin(builder, self.compiler, self.target, self.mode, tool) + } + ToolArtifactKind::Library => builder + .cargo_out(self.compiler, self.mode, self.target) + .join(format!("lib{tool}.rlib")), + }; ToolBuildResult { tool_path, build_compiler: self.compiler, target_compiler } } } } -#[allow(clippy::too_many_arguments)] // FIXME: reduce the number of args and remove this. +#[expect(clippy::too_many_arguments)] // FIXME: reduce the number of args and remove this. pub fn prepare_tool_cargo( builder: &Builder<'_>, compiler: Compiler, @@ -206,7 +227,6 @@ pub fn prepare_tool_cargo( let mut features = extra_features.to_vec(); if builder.build.config.cargo_native_static { if path.ends_with("cargo") - || path.ends_with("rls") || path.ends_with("clippy") || path.ends_with("miri") || path.ends_with("rustfmt") @@ -234,23 +254,32 @@ pub fn prepare_tool_cargo( cargo.env("CFG_VERSION", builder.rust_version()); cargo.env("CFG_RELEASE_NUM", &builder.version); cargo.env("DOC_RUST_LANG_ORG_CHANNEL", builder.doc_rust_lang_org_channel()); + if let Some(ref ver_date) = builder.rust_info().commit_date() { cargo.env("CFG_VER_DATE", ver_date); } + if let Some(ref ver_hash) = builder.rust_info().sha() { cargo.env("CFG_VER_HASH", ver_hash); } + if let Some(description) = &builder.config.description { + cargo.env("CFG_VER_DESCRIPTION", description); + } + let info = GitInfo::new(builder.config.omit_git_hash, &dir); if let Some(sha) = info.sha() { cargo.env("CFG_COMMIT_HASH", sha); } + if let Some(sha_short) = info.sha_short() { cargo.env("CFG_SHORT_COMMIT_HASH", sha_short); } + if let Some(date) = info.commit_date() { cargo.env("CFG_COMMIT_DATE", date); } + if !features.is_empty() { cargo.arg("--features").arg(features.join(", ")); } @@ -296,16 +325,20 @@ pub(crate) fn get_tool_rustc_compiler( builder: &Builder<'_>, target_compiler: Compiler, ) -> Compiler { - if builder.download_rustc() && target_compiler.stage == 1 { - // We already have the stage 1 compiler, we don't need to cut the stage. - builder.compiler(target_compiler.stage, builder.config.build) - } else { - // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise - // we'd have stageN/bin/rustc and stageN/bin/$rustc_tool be effectively different stage - // compilers, which isn't what we want. Rustc tools should be linked in the same way as the - // compiler it's paired with, so it must be built with the previous stage compiler. - builder.compiler(target_compiler.stage.saturating_sub(1), builder.config.build) + if target_compiler.is_forced_compiler() { + return target_compiler; } + + if builder.download_rustc() && target_compiler.stage > 0 { + // We already have the stage N compiler, we don't need to cut the stage. + return builder.compiler(target_compiler.stage, builder.config.build); + } + + // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise + // we'd have stageN/bin/rustc and stageN/bin/$rustc_tool be effectively different stage + // compilers, which isn't what we want. Rustc tools should be linked in the same way as the + // compiler it's paired with, so it must be built with the previous stage compiler. + builder.compiler(target_compiler.stage.saturating_sub(1), builder.config.build) } /// Links a built tool binary with the given `name` from the build directory to the @@ -319,7 +352,7 @@ fn copy_link_tool_bin( ) -> PathBuf { let cargo_out = builder.cargo_out(compiler, mode, target).join(exe(name, target)); let bin = builder.tools_dir(compiler).join(exe(name, target)); - builder.copy_link(&cargo_out, &bin); + builder.copy_link(&cargo_out, &bin, FileType::Executable); bin } @@ -330,6 +363,7 @@ macro_rules! bootstrap_tool { $(,is_unstable_tool = $unstable:expr)* $(,allow_features = $allow_features:expr)? $(,submodules = $submodules:expr)? + $(,artifact_kind = $artifact_kind:expr)? ; )+) => { #[derive(PartialEq, Eq, Clone)] @@ -389,11 +423,15 @@ macro_rules! bootstrap_tool { builder.require_submodule(submodule, None); } )* + + let is_unstable = false $(|| $unstable)*; + let compiletest_wants_stage0 = $tool_name == "compiletest" && builder.config.compiletest_use_stage0_libtest; + builder.ensure(ToolBuild { compiler: self.compiler, target: self.target, tool: $tool_name, - mode: if false $(|| $unstable)* { + mode: if is_unstable && !compiletest_wants_stage0 { // use in-tree libraries for unstable features Mode::ToolStd } else { @@ -406,8 +444,17 @@ macro_rules! bootstrap_tool { SourceType::InTree }, extra_features: vec![], - allow_features: concat!($($allow_features)*), - cargo_args: vec![] + allow_features: { + let mut _value = ""; + $( _value = $allow_features; )? + _value + }, + cargo_args: vec![], + artifact_kind: if false $(|| $artifact_kind == ToolArtifactKind::Library)* { + ToolArtifactKind::Library + } else { + ToolArtifactKind::Binary + } }) } } @@ -415,6 +462,8 @@ macro_rules! bootstrap_tool { } } +pub(crate) const COMPILETEST_ALLOW_FEATURES: &str = "internal_output_capture"; + bootstrap_tool!( // This is marked as an external tool because it includes dependencies // from submodules. Trying to keep the lints in sync between all the repos @@ -425,7 +474,7 @@ bootstrap_tool!( Tidy, "src/tools/tidy", "tidy"; Linkchecker, "src/tools/linkchecker", "linkchecker"; CargoTest, "src/tools/cargotest", "cargotest"; - Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true, allow_features = "test"; + Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true, allow_features = COMPILETEST_ALLOW_FEATURES; BuildManifest, "src/tools/build-manifest", "build-manifest"; RemoteTestClient, "src/tools/remote-test-client", "remote-test-client"; RustInstaller, "src/tools/rust-installer", "rust-installer"; @@ -440,56 +489,20 @@ bootstrap_tool!( GenerateCopyright, "src/tools/generate-copyright", "generate-copyright"; SuggestTests, "src/tools/suggest-tests", "suggest-tests"; GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys"; - RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = "test"; + // rustdoc-gui-test has a crate dependency on compiletest, so it needs the same unstable features. + RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = COMPILETEST_ALLOW_FEATURES; CoverageDump, "src/tools/coverage-dump", "coverage-dump"; WasmComponentLd, "src/tools/wasm-component-ld", "wasm-component-ld", is_unstable_tool = true, allow_features = "min_specialization"; UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator"; FeaturesStatusDump, "src/tools/features-status-dump", "features-status-dump"; + OptimizedDist, "src/tools/opt-dist", "opt-dist", submodules = &["src/tools/rustc-perf"]; + RunMakeSupport, "src/tools/run-make-support", "run_make_support", artifact_kind = ToolArtifactKind::Library; ); /// These are the submodules that are required for rustbook to work due to /// depending on mdbook plugins. pub static SUBMODULES_FOR_RUSTBOOK: &[&str] = &["src/doc/book", "src/doc/reference"]; -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct OptimizedDist { - pub compiler: Compiler, - pub target: TargetSelection, -} - -impl Step for OptimizedDist { - type Output = ToolBuildResult; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/tools/opt-dist") - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(OptimizedDist { - compiler: run.builder.compiler(0, run.builder.config.build), - target: run.target, - }); - } - - fn run(self, builder: &Builder<'_>) -> ToolBuildResult { - // We need to ensure the rustc-perf submodule is initialized when building opt-dist since - // the tool requires it to be in place to run. - builder.require_submodule("src/tools/rustc-perf", None); - - builder.ensure(ToolBuild { - compiler: self.compiler, - target: self.target, - tool: "opt-dist", - mode: Mode::ToolBootstrap, - path: "src/tools/opt-dist", - source_type: SourceType::InTree, - extra_features: Vec::new(), - allow_features: "", - cargo_args: Vec::new(), - }) - } -} - /// The [rustc-perf](https://github.com/rust-lang/rustc-perf) benchmark suite, which is added /// as a submodule at `src/tools/rustc-perf`. #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -529,6 +542,7 @@ impl Step for RustcPerf { // Only build the collector package, which is used for benchmarking through // a CLI. cargo_args: vec!["-p".to_string(), "collector".to_string()], + artifact_kind: ToolArtifactKind::Binary, }; let res = builder.ensure(tool.clone()); // We also need to symlink the `rustc-fake` binary to the corresponding directory, @@ -586,6 +600,7 @@ impl Step for ErrorIndex { extra_features: Vec::new(), allow_features: "", cargo_args: Vec::new(), + artifact_kind: ToolArtifactKind::Binary, }) } } @@ -621,6 +636,7 @@ impl Step for RemoteTestServer { extra_features: Vec::new(), allow_features: "", cargo_args: Vec::new(), + artifact_kind: ToolArtifactKind::Binary, }) } } @@ -680,8 +696,7 @@ impl Step for Rustdoc { let files_to_track = &["src/librustdoc", "src/tools/rustdoc"]; // Check if unchanged - if builder.config.last_modified_commit(files_to_track, "download-rustc", true).is_some() - { + if !builder.config.has_changes_from_upstream(files_to_track) { let precompiled_rustdoc = builder .config .ci_rustc_dir() @@ -689,7 +704,7 @@ impl Step for Rustdoc { .join(exe("rustdoc", target_compiler.host)); let bin_rustdoc = bin_rustdoc(); - builder.copy_link(&precompiled_rustdoc, &bin_rustdoc); + builder.copy_link(&precompiled_rustdoc, &bin_rustdoc, FileType::Executable); return ToolBuildResult { tool_path: bin_rustdoc, @@ -725,6 +740,7 @@ impl Step for Rustdoc { extra_features, allow_features: "", cargo_args: Vec::new(), + artifact_kind: ToolArtifactKind::Binary, }); // don't create a stage0-sysroot/bin directory. @@ -735,7 +751,7 @@ impl Step for Rustdoc { compile::strip_debug(builder, target, &tool_path); } let bin_rustdoc = bin_rustdoc(); - builder.copy_link(&tool_path, &bin_rustdoc); + builder.copy_link(&tool_path, &bin_rustdoc, FileType::Executable); ToolBuildResult { tool_path: bin_rustdoc, build_compiler, target_compiler } } else { ToolBuildResult { tool_path, build_compiler, target_compiler } @@ -779,6 +795,7 @@ impl Step for Cargo { extra_features: Vec::new(), allow_features: "", cargo_args: Vec::new(), + artifact_kind: ToolArtifactKind::Binary, }) } } @@ -805,7 +822,6 @@ impl Step for LldWrapper { fields(build_compiler = ?self.build_compiler, target_compiler = ?self.target_compiler), ), )] - fn run(self, builder: &Builder<'_>) -> ToolBuildResult { if builder.config.dry_run() { return ToolBuildResult { @@ -827,6 +843,7 @@ impl Step for LldWrapper { extra_features: Vec::new(), allow_features: "", cargo_args: Vec::new(), + artifact_kind: ToolArtifactKind::Binary, }); let libdir_bin = builder.sysroot_target_bindir(self.target_compiler, target); @@ -836,13 +853,20 @@ impl Step for LldWrapper { let src_exe = exe("lld", target); let dst_exe = exe("rust-lld", target); - builder.copy_link(&lld_install.join("bin").join(src_exe), &libdir_bin.join(dst_exe)); + builder.copy_link( + &lld_install.join("bin").join(src_exe), + &libdir_bin.join(dst_exe), + FileType::Executable, + ); let self_contained_lld_dir = libdir_bin.join("gcc-ld"); t!(fs::create_dir_all(&self_contained_lld_dir)); for name in crate::LLD_FILE_NAMES { - builder - .copy_link(&tool_result.tool_path, &self_contained_lld_dir.join(exe(name, target))); + builder.copy_link( + &tool_result.tool_path, + &self_contained_lld_dir.join(exe(name, target)), + FileType::Executable, + ); } tool_result @@ -887,6 +911,7 @@ impl Step for RustAnalyzer { source_type: SourceType::InTree, allow_features: RustAnalyzer::ALLOW_FEATURES, cargo_args: Vec::new(), + artifact_kind: ToolArtifactKind::Binary, }) } } @@ -931,14 +956,18 @@ impl Step for RustAnalyzerProcMacroSrv { source_type: SourceType::InTree, allow_features: RustAnalyzer::ALLOW_FEATURES, cargo_args: Vec::new(), + artifact_kind: ToolArtifactKind::Binary, }); // Copy `rust-analyzer-proc-macro-srv` to `/libexec/` // so that r-a can use it. let libexec_path = builder.sysroot(self.compiler).join("libexec"); t!(fs::create_dir_all(&libexec_path)); - builder - .copy_link(&tool_result.tool_path, &libexec_path.join("rust-analyzer-proc-macro-srv")); + builder.copy_link( + &tool_result.tool_path, + &libexec_path.join("rust-analyzer-proc-macro-srv"), + FileType::Executable, + ); Some(tool_result) } @@ -985,6 +1014,7 @@ impl Step for LlvmBitcodeLinker { extra_features: self.extra_features, allow_features: "", cargo_args: Vec::new(), + artifact_kind: ToolArtifactKind::Binary, }); if tool_result.target_compiler.stage > 0 { @@ -994,7 +1024,7 @@ impl Step for LlvmBitcodeLinker { t!(fs::create_dir_all(&bindir_self_contained)); let bin_destination = bindir_self_contained .join(exe("llvm-bitcode-linker", tool_result.target_compiler.host)); - builder.copy_link(&tool_result.tool_path, &bin_destination); + builder.copy_link(&tool_result.tool_path, &bin_destination, FileType::Executable); ToolBuildResult { tool_path: bin_destination, build_compiler: tool_result.build_compiler, @@ -1011,7 +1041,7 @@ pub struct LibcxxVersionTool { pub target: TargetSelection, } -#[allow(dead_code)] +#[expect(dead_code)] #[derive(Debug, Clone)] pub enum LibcxxVersion { Gnu(usize), @@ -1164,6 +1194,7 @@ fn run_tool_build_step( source_type: SourceType::InTree, allow_features: "", cargo_args: vec![], + artifact_kind: ToolArtifactKind::Binary, }); // FIXME: This should just be an if-let-chain, but those are unstable. @@ -1175,7 +1206,7 @@ fn run_tool_build_step( for add_bin in add_bins_to_sysroot { let bin_destination = bindir.join(exe(add_bin, target_compiler.host)); - builder.copy_link(&tool_path, &bin_destination); + builder.copy_link(&tool_path, &bin_destination, FileType::Executable); } // Return a path into the bin dir. @@ -1186,13 +1217,23 @@ fn run_tool_build_step( } } -tool_extended!(Cargofmt { path: "src/tools/rustfmt", tool_name: "cargo-fmt", stable: true }); -tool_extended!(CargoClippy { path: "src/tools/clippy", tool_name: "cargo-clippy", stable: true }); +tool_extended!(Cargofmt { + path: "src/tools/rustfmt", + tool_name: "cargo-fmt", + stable: true, + add_bins_to_sysroot: ["cargo-fmt"] +}); +tool_extended!(CargoClippy { + path: "src/tools/clippy", + tool_name: "cargo-clippy", + stable: true, + add_bins_to_sysroot: ["cargo-clippy"] +}); tool_extended!(Clippy { path: "src/tools/clippy", tool_name: "clippy-driver", stable: true, - add_bins_to_sysroot: ["clippy-driver", "cargo-clippy"] + add_bins_to_sysroot: ["clippy-driver"] }); tool_extended!(Miri { path: "src/tools/miri", @@ -1206,12 +1247,11 @@ tool_extended!(CargoMiri { stable: false, add_bins_to_sysroot: ["cargo-miri"] }); -tool_extended!(Rls { path: "src/tools/rls", tool_name: "rls", stable: true }); tool_extended!(Rustfmt { path: "src/tools/rustfmt", tool_name: "rustfmt", stable: true, - add_bins_to_sysroot: ["rustfmt", "cargo-fmt"] + add_bins_to_sysroot: ["rustfmt"] }); #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -1219,6 +1259,10 @@ pub struct TestFloatParse { pub host: TargetSelection, } +impl TestFloatParse { + pub const ALLOW_FEATURES: &'static str = "f16,cfg_target_has_reliable_f16_f128"; +} + impl Step for TestFloatParse { type Output = ToolBuildResult; const ONLY_HOSTS: bool = true; @@ -1240,8 +1284,9 @@ impl Step for TestFloatParse { path: "src/etc/test-float-parse", source_type: SourceType::InTree, extra_features: Vec::new(), - allow_features: "", + allow_features: Self::ALLOW_FEATURES, cargo_args: Vec::new(), + artifact_kind: ToolArtifactKind::Binary, }) } } diff --git a/src/bootstrap/src/core/build_steps/toolstate.rs b/src/bootstrap/src/core/build_steps/toolstate.rs index a65623de95a3f..fc7acd1fa149b 100644 --- a/src/bootstrap/src/core/build_steps/toolstate.rs +++ b/src/bootstrap/src/core/build_steps/toolstate.rs @@ -128,7 +128,7 @@ impl Step for ToolStateCheck { /// Checks tool state status. /// /// This is intended to be used in the `checktools.sh` script. To use - /// this, set `save-toolstates` in `config.toml` so that tool status will + /// this, set `save-toolstates` in `bootstrap.toml` so that tool status will /// be saved to a JSON file. Then, run `x.py test --no-fail-fast` for all /// of the tools to populate the JSON file. After that is done, this /// command can be run to check for any status failures, and exits with an @@ -254,7 +254,7 @@ impl Builder<'_> { /// Updates the actual toolstate of a tool. /// /// The toolstates are saved to the file specified by the key - /// `rust.save-toolstates` in `config.toml`. If unspecified, nothing will be + /// `rust.save-toolstates` in `bootstrap.toml`. If unspecified, nothing will be /// done. The file is updated immediately after this function completes. pub fn save_toolstate(&self, tool: &str, state: ToolState) { use std::io::Write; diff --git a/src/bootstrap/src/core/build_steps/vendor.rs b/src/bootstrap/src/core/build_steps/vendor.rs index 984c70955d7da..0caeb328811aa 100644 --- a/src/bootstrap/src/core/build_steps/vendor.rs +++ b/src/bootstrap/src/core/build_steps/vendor.rs @@ -103,6 +103,7 @@ impl Step for Vendor { // Will read the libstd Cargo.toml // which uses the unstable `public-dependency` feature. cmd.env("RUSTC_BOOTSTRAP", "1"); + cmd.env("RUSTC", &builder.initial_rustc); cmd.current_dir(self.root_dir).arg(&self.output_dir); diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index d1d52d82eaa0f..e41f6f16b0232 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -112,9 +112,8 @@ impl Cargo { let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind); match cmd_kind { - // No need to configure the target linker for these command types, - // as they don't invoke rustc at all. - Kind::Clean | Kind::Suggest | Kind::Format | Kind::Setup => {} + // No need to configure the target linker for these command types. + Kind::Clean | Kind::Check | Kind::Suggest | Kind::Format | Kind::Setup => {} _ => { cargo.configure_linker(builder); } @@ -205,6 +204,8 @@ impl Cargo { self } + // FIXME(onur-ozkan): Add coverage to make sure modifications to this function + // doesn't cause cache invalidations (e.g., #130108). fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo { let target = self.target; let compiler = self.compiler; @@ -215,7 +216,7 @@ impl Cargo { // dynamic libraries. We use this by default on Unix platforms to ensure // that our nightlies behave the same on Windows, that is they work out // of the box. This can be disabled by setting `rpath = false` in `[rust]` - // table of `config.toml` + // table of `bootstrap.toml` // // Ok, so the astute might be wondering "why isn't `-C rpath` used // here?" and that is indeed a good question to ask. This codegen @@ -260,7 +261,7 @@ impl Cargo { } } - for arg in linker_args(builder, compiler.host, LldThreads::Yes, 0) { + for arg in linker_args(builder, compiler.host, LldThreads::Yes) { self.hostflags.arg(&arg); } @@ -270,10 +271,10 @@ impl Cargo { } // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not // `linker_args` here. - for flag in linker_flags(builder, target, LldThreads::Yes, compiler.stage) { + for flag in linker_flags(builder, target, LldThreads::Yes) { self.rustflags.arg(&flag); } - for arg in linker_args(builder, target, LldThreads::Yes, compiler.stage) { + for arg in linker_args(builder, target, LldThreads::Yes) { self.rustdocflags.arg(&arg); } @@ -597,7 +598,7 @@ impl Builder<'_> { // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native // library unnecessary. This can be removed when windows-rs enables raw-dylib // unconditionally. - if let Mode::Rustc | Mode::ToolRustc = mode { + if let Mode::Rustc | Mode::ToolRustc | Mode::ToolBootstrap = mode { rustflags.arg("--cfg=windows_raw_dylib"); } @@ -872,11 +873,15 @@ impl Builder<'_> { } cargo.env( profile_var("DEBUG_ASSERTIONS"), - if mode == Mode::Std { - self.config.std_debug_assertions.to_string() - } else { - self.config.rustc_debug_assertions.to_string() - }, + match mode { + Mode::Std => self.config.std_debug_assertions, + Mode::Rustc => self.config.rustc_debug_assertions, + Mode::Codegen => self.config.rustc_debug_assertions, + Mode::ToolBootstrap => self.config.tools_debug_assertions, + Mode::ToolStd => self.config.tools_debug_assertions, + Mode::ToolRustc => self.config.tools_debug_assertions, + } + .to_string(), ); cargo.env( profile_var("OVERFLOW_CHECKS"), @@ -1045,8 +1050,11 @@ impl Builder<'_> { // so this line allows the use of custom libcs. cargo.env("LIBC_CHECK_CFG", "1"); + let mut lint_flags = Vec::new(); + + // Lints for all in-tree code: compiler, rustdoc, cranelift, gcc, + // clippy, rustfmt, rust-analyzer, etc. if source_type == SourceType::InTree { - let mut lint_flags = Vec::new(); // When extending this list, add the new lints to the RUSTFLAGS of the // build_bootstrap function of src/bootstrap/bootstrap.py as well as // some code doesn't go through this `rustc` wrapper. @@ -1058,27 +1066,33 @@ impl Builder<'_> { rustdocflags.arg("-Dwarnings"); } - // This does not use RUSTFLAGS due to caching issues with Cargo. - // Clippy is treated as an "in tree" tool, but shares the same - // cache as other "submodule" tools. With these options set in - // RUSTFLAGS, that causes *every* shared dependency to be rebuilt. - // By injecting this into the rustc wrapper, this circumvents - // Cargo's fingerprint detection. This is fine because lint flags - // are always ignored in dependencies. Eventually this should be - // fixed via better support from Cargo. - cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" ")); - rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes"); } + // Lints just for `compiler/` crates. if mode == Mode::Rustc { - rustflags.arg("-Wrustc::internal"); - rustflags.arg("-Drustc::symbol_intern_string_literal"); + lint_flags.push("-Wrustc::internal"); + lint_flags.push("-Drustc::symbol_intern_string_literal"); // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all // of the individual lints are satisfied. - rustflags.arg("-Wkeyword_idents_2024"); - rustflags.arg("-Wunsafe_op_in_unsafe_fn"); - } + lint_flags.push("-Wkeyword_idents_2024"); + lint_flags.push("-Wunreachable_pub"); + lint_flags.push("-Wunsafe_op_in_unsafe_fn"); + lint_flags.push("-Wunused_crate_dependencies"); + } + + // This does not use RUSTFLAGS for two reasons. + // - Due to caching issues with Cargo. Clippy is treated as an "in + // tree" tool, but shares the same cache as other "submodule" tools. + // With these options set in RUSTFLAGS, that causes *every* shared + // dependency to be rebuilt. By injecting this into the rustc + // wrapper, this circumvents Cargo's fingerprint detection. This is + // fine because lint flags are always ignored in dependencies. + // Eventually this should be fixed via better support from Cargo. + // - RUSTFLAGS is ignored for proc macro crates that are being built on + // the host (because `--target` is given). But we want the lint flags + // to be applied to proc macro crates. + cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" ")); if self.config.rust_frame_pointers { rustflags.arg("-Cforce-frame-pointers=true"); @@ -1264,5 +1278,5 @@ impl Builder<'_> { pub fn cargo_profile_var(name: &str, config: &Config) -> String { let profile = if config.rust_optimize.is_release() { "RELEASE" } else { "DEV" }; - format!("CARGO_PROFILE_{}_{}", profile, name) + format!("CARGO_PROFILE_{profile}_{name}") } diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index c8e2856bdc874..af3e3cc37b928 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -31,9 +31,9 @@ mod cargo; mod tests; /// Builds and performs different [`Self::kind`]s of stuff and actions, taking -/// into account build configuration from e.g. config.toml. +/// into account build configuration from e.g. bootstrap.toml. pub struct Builder<'a> { - /// Build configuration from e.g. config.toml. + /// Build configuration from e.g. bootstrap.toml. pub build: &'a Build, /// The stage to use. Either implicitly determined based on subcommand, or @@ -50,7 +50,7 @@ pub struct Builder<'a> { /// A stack of [`Step`]s to run before we can run this builder. The output /// of steps is cached in [`Self::cache`]. - stack: RefCell>>, + stack: RefCell>>, /// The total amount of time we spent running [`Step`]s in [`Self::stack`]. time_spent_on_dependencies: Cell, @@ -69,6 +69,21 @@ impl Deref for Builder<'_> { } } +/// This trait is similar to `Any`, except that it also exposes the underlying +/// type's [`Debug`] implementation. +/// +/// (Trying to debug-print `dyn Any` results in the unhelpful `"Any { .. }"`.) +trait AnyDebug: Any + Debug {} +impl AnyDebug for T {} +impl dyn AnyDebug { + /// Equivalent to `::downcast_ref`. + fn downcast_ref(&self) -> Option<&T> { + (self as &dyn Any).downcast_ref() + } + + // Feel free to add other `dyn Any` methods as necessary. +} + pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash { /// Result type of `Step::run`. type Output: Clone; @@ -86,13 +101,13 @@ pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash { /// Primary function to implement `Step` logic. /// /// This function can be triggered in two ways: - /// 1. Directly from [`Builder::execute_cli`]. - /// 2. Indirectly by being called from other `Step`s using [`Builder::ensure`]. + /// 1. Directly from [`Builder::execute_cli`]. + /// 2. Indirectly by being called from other `Step`s using [`Builder::ensure`]. /// - /// When called with [`Builder::execute_cli`] (as done by `Build::build`), this function executed twice: - /// - First in "dry-run" mode to validate certain things (like cyclic Step invocations, - /// directory creation, etc) super quickly. - /// - Then it's called again to run the actual, very expensive process. + /// When called with [`Builder::execute_cli`] (as done by `Build::build`), this function is executed twice: + /// - First in "dry-run" mode to validate certain things (like cyclic Step invocations, + /// directory creation, etc) super quickly. + /// - Then it's called again to run the actual, very expensive process. /// /// When triggered indirectly from other `Step`s, it may still run twice (as dry-run and real mode) /// depending on the `Step::run` implementation of the caller. @@ -317,7 +332,7 @@ impl PathSet { } const PATH_REMAP: &[(&str, &[&str])] = &[ - // config.toml uses `rust-analyzer-proc-macro-srv`, but the + // bootstrap.toml uses `rust-analyzer-proc-macro-srv`, but the // actual path is `proc-macro-srv-cli` ("rust-analyzer-proc-macro-srv", &["src/tools/rust-analyzer/crates/proc-macro-srv-cli"]), // Make `x test tests` function the same as `x t tests/*` @@ -489,8 +504,8 @@ impl StepDescription { match std::path::absolute(p) { Ok(p) => p.strip_prefix(&builder.src).unwrap_or(&p).to_path_buf(), Err(e) => { - eprintln!("ERROR: {:?}", e); - panic!("Due to the above error, failed to resolve path: {:?}", p); + eprintln!("ERROR: {e:?}"); + panic!("Due to the above error, failed to resolve path: {p:?}"); } } }) @@ -679,8 +694,7 @@ impl<'a> ShouldRun<'a> { if !submodules_paths.iter().any(|sm_p| p.contains(sm_p)) { assert!( self.builder.src.join(p).exists(), - "`should_run.paths` should correspond to real on-disk paths - use `alias` if there is no relevant path: {}", - p + "`should_run.paths` should correspond to real on-disk paths - use `alias` if there is no relevant path: {p}" ); } @@ -880,7 +894,6 @@ impl<'a> Builder<'a> { tool::RemoteTestClient, tool::RustInstaller, tool::Cargo, - tool::Rls, tool::RustAnalyzer, tool::RustAnalyzerProcMacroSrv, tool::Rustdoc, @@ -890,6 +903,7 @@ impl<'a> Builder<'a> { gcc::Gcc, llvm::Sanitizers, tool::Rustfmt, + tool::Cargofmt, tool::Miri, tool::CargoMiri, llvm::Lld, @@ -922,7 +936,6 @@ impl<'a> Builder<'a> { clippy::OptDist, clippy::RemoteTestClient, clippy::RemoteTestServer, - clippy::Rls, clippy::RustAnalyzer, clippy::Rustdoc, clippy::Rustfmt, @@ -940,7 +953,6 @@ impl<'a> Builder<'a> { check::Miri, check::CargoMiri, check::MiroptTestTools, - check::Rls, check::Rustfmt, check::RustAnalyzer, check::TestFloatParse, @@ -948,6 +960,7 @@ impl<'a> Builder<'a> { check::RunMakeSupport, check::Compiletest, check::FeaturesStatusDump, + check::CoverageDump, ), Kind::Test => describe!( crate::core::build_steps::toolstate::ToolStateCheck, @@ -1055,7 +1068,6 @@ impl<'a> Builder<'a> { dist::Analysis, dist::Src, dist::Cargo, - dist::Rls, dist::RustAnalyzer, dist::Rustfmt, dist::Clippy, @@ -1072,6 +1084,7 @@ impl<'a> Builder<'a> { dist::PlainSourceTarball, dist::BuildManifest, dist::ReproducibleArtifacts, + dist::Gcc ), Kind::Install => describe!( install::Docs, @@ -1100,6 +1113,9 @@ impl<'a> Builder<'a> { run::GenerateCompletions, run::UnicodeTableGenerator, run::FeaturesStatusDump, + run::CyclicStep, + run::CoverageDump, + run::Rustfmt, ), Kind::Setup => { describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor) @@ -1234,7 +1250,7 @@ impl<'a> Builder<'a> { ), )] pub fn compiler(&self, stage: u32, host: TargetSelection) -> Compiler { - self.ensure(compile::Assemble { target_compiler: Compiler { stage, host } }) + self.ensure(compile::Assemble { target_compiler: Compiler::new(stage, host) }) } /// Similar to `compiler`, except handles the full-bootstrap option to @@ -1262,7 +1278,6 @@ impl<'a> Builder<'a> { ), ), )] - /// FIXME: This function is unnecessary (and dangerous, see ). /// We already have uplifting logic for the compiler, so remove this. pub fn compiler_for( @@ -1271,8 +1286,7 @@ impl<'a> Builder<'a> { host: TargetSelection, target: TargetSelection, ) -> Compiler { - #![allow(clippy::let_and_return)] - let resolved_compiler = if self.build.force_use_stage2(stage) { + let mut resolved_compiler = if self.build.force_use_stage2(stage) { trace!(target: "COMPILER_FOR", ?stage, "force_use_stage2"); self.compiler(2, self.config.build) } else if self.build.force_use_stage1(stage, target) { @@ -1282,6 +1296,11 @@ impl<'a> Builder<'a> { trace!(target: "COMPILER_FOR", ?stage, ?host, "no force, fallback to `compiler()`"); self.compiler(stage, host) }; + + if stage != resolved_compiler.stage { + resolved_compiler.forced_compiler(true); + } + trace!(target: "COMPILER_FOR", ?resolved_compiler); resolved_compiler } @@ -1369,7 +1388,7 @@ impl<'a> Builder<'a> { // Windows doesn't need dylib path munging because the dlls for the // compiler live next to the compiler and the system will find them // automatically. - if cfg!(windows) { + if cfg!(any(windows, target_os = "cygwin")) { return; } @@ -1462,7 +1481,7 @@ impl<'a> Builder<'a> { cmd.arg("-Dwarnings"); } cmd.arg("-Znormalize-docs"); - cmd.args(linker_args(self, compiler.host, LldThreads::Yes, compiler.stage)); + cmd.args(linker_args(self, compiler.host, LldThreads::Yes)); cmd } @@ -1517,7 +1536,7 @@ impl<'a> Builder<'a> { let out = step.clone().run(self); let dur = start.elapsed(); let deps = self.time_spent_on_dependencies.replace(parent + dur); - (out, dur - deps) + (out, dur.saturating_sub(deps)) }; if self.config.print_step_timings && !self.config.dry_run() { diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 0eaa89792bd97..51852099dc398 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1,11 +1,14 @@ -use std::thread; +use std::env::VarError; +use std::{panic, thread}; +use build_helper::stage0_parser::parse_stage0_file; use llvm::prebuilt_llvm_config; use super::*; use crate::Flags; use crate::core::build_steps::doc::DocumentationFormat; use crate::core::config::Config; +use crate::utils::tests::git::{GitCtx, git_test}; static TEST_TRIPLE_1: &str = "i686-unknown-haiku"; static TEST_TRIPLE_2: &str = "i686-unknown-hurd-gnu"; @@ -71,7 +74,7 @@ fn check_cli(paths: [&str; N]) { macro_rules! std { ($host:ident => $target:ident, stage = $stage:literal) => { compile::Std::new( - Compiler { host: TargetSelection::from_user($host), stage: $stage }, + Compiler::new($stage, TargetSelection::from_user($host)), TargetSelection::from_user($target), ) }; @@ -84,7 +87,7 @@ macro_rules! doc_std { macro_rules! rustc { ($host:ident => $target:ident, stage = $stage:literal) => { compile::Rustc::new( - Compiler { host: TargetSelection::from_user($host), stage: $stage }, + Compiler::new($stage, TargetSelection::from_user($host)), TargetSelection::from_user($target), ) }; @@ -239,36 +242,80 @@ fn alias_and_path_for_library() { } #[test] -fn ci_rustc_if_unchanged_logic() { - let config = Config::parse_inner( - Flags::parse(&[ - "build".to_owned(), - "--dry-run".to_owned(), - "--set=rust.download-rustc='if-unchanged'".to_owned(), - ]), - |&_| Ok(Default::default()), - ); +fn ci_rustc_if_unchanged_invalidate_on_compiler_changes() { + git_test(|ctx| { + prepare_rustc_checkout(ctx); + ctx.create_upstream_merge(&["compiler/bar"]); + // This change should invalidate download-ci-rustc + ctx.create_nonupstream_merge(&["compiler/foo"]); + + let config = parse_config_download_rustc_at(ctx.get_path(), "if-unchanged", true); + assert_eq!(config.download_rustc_commit, None); + }); +} - let build = Build::new(config.clone()); - let builder = Builder::new(&build); +#[test] +fn ci_rustc_if_unchanged_invalidate_on_library_changes_in_ci() { + git_test(|ctx| { + prepare_rustc_checkout(ctx); + ctx.create_upstream_merge(&["compiler/bar"]); + // This change should invalidate download-ci-rustc + ctx.create_nonupstream_merge(&["library/foo"]); + + let config = parse_config_download_rustc_at(ctx.get_path(), "if-unchanged", true); + assert_eq!(config.download_rustc_commit, None); + }); +} - if config.out.exists() { - fs::remove_dir_all(&config.out).unwrap(); - } +#[test] +fn ci_rustc_if_unchanged_do_not_invalidate_on_library_changes_outside_ci() { + git_test(|ctx| { + prepare_rustc_checkout(ctx); + let sha = ctx.create_upstream_merge(&["compiler/bar"]); + // This change should not invalidate download-ci-rustc + ctx.create_nonupstream_merge(&["library/foo"]); + + let config = parse_config_download_rustc_at(ctx.get_path(), "if-unchanged", false); + assert_eq!(config.download_rustc_commit, Some(sha)); + }); +} - builder.run_step_descriptions(&Builder::get_step_descriptions(config.cmd.kind()), &[]); +#[test] +fn ci_rustc_if_unchanged_do_not_invalidate_on_tool_changes() { + git_test(|ctx| { + prepare_rustc_checkout(ctx); + let sha = ctx.create_upstream_merge(&["compiler/bar"]); + // This change should not invalidate download-ci-rustc + ctx.create_nonupstream_merge(&["src/tools/foo"]); + + let config = parse_config_download_rustc_at(ctx.get_path(), "if-unchanged", true); + assert_eq!(config.download_rustc_commit, Some(sha)); + }); +} - // Make sure "if-unchanged" logic doesn't try to use CI rustc while there are changes - // in compiler and/or library. - if config.download_rustc_commit.is_some() { - let has_changes = - config.last_modified_commit(&["compiler", "library"], "download-rustc", true).is_none(); +/// Prepares the given directory so that it looks like a rustc checkout. +/// Also configures `GitCtx` to use the correct merge bot e-mail for upstream merge commits. +fn prepare_rustc_checkout(ctx: &mut GitCtx) { + ctx.merge_bot_email = + format!("Merge bot <{}>", parse_stage0_file().config.git_merge_commit_email); + ctx.write("src/ci/channel", "nightly"); + ctx.commit(); +} - assert!( - !has_changes, - "CI-rustc can't be used with 'if-unchanged' while there are changes in compiler and/or library." - ); - } +/// Parses a Config directory from `path`, with the given value of `download_rustc`. +fn parse_config_download_rustc_at(path: &Path, download_rustc: &str, ci: bool) -> Config { + Config::parse_inner( + Flags::parse(&[ + "build".to_owned(), + "--dry-run".to_owned(), + "--ci".to_owned(), + if ci { "true" } else { "false" }.to_owned(), + format!("--set=rust.download-rustc='{download_rustc}'"), + "--src".to_owned(), + path.to_str().unwrap().to_owned(), + ]), + |&_| Ok(Default::default()), + ) } mod defaults { @@ -296,7 +343,7 @@ mod defaults { first(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 } }], + &[tool::Rustdoc { compiler: Compiler::new(1, a) }], ); assert_eq!( first(cache.all::()), @@ -319,7 +366,7 @@ mod defaults { first(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 } }], + &[tool::Rustdoc { compiler: Compiler::new(0, a) }], ); assert!(cache.all::().is_empty()); } @@ -352,16 +399,16 @@ mod defaults { assert_eq!( first(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: b, stage: 1 } }, + compile::Assemble { target_compiler: Compiler::new(0, a) }, + compile::Assemble { target_compiler: Compiler::new(1, a) }, + compile::Assemble { target_compiler: Compiler::new(1, b) }, ] ); assert_eq!( first(cache.all::()), &[ - tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }, - tool::Rustdoc { compiler: Compiler { host: b, stage: 1 } }, + tool::Rustdoc { compiler: Compiler::new(1, a) }, + tool::Rustdoc { compiler: Compiler::new(1, b) }, ], ); assert_eq!( @@ -386,14 +433,14 @@ mod defaults { assert_eq!(first(cache.all::()), &[doc::ErrorIndex { target: a },]); assert_eq!( first(cache.all::()), - &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 0 } }] + &[tool::ErrorIndex { compiler: Compiler::new(0, a) }] ); // 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(cache.all::()), - &[tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } },] + &[tool::Rustdoc { compiler: Compiler::new(0, a) },] ); } } @@ -402,6 +449,7 @@ mod dist { use pretty_assertions::assert_eq; use super::{Config, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, first, run_build}; + use crate::Flags; use crate::core::builder::*; fn configure(host: &[&str], target: &[&str]) -> Config { @@ -418,17 +466,17 @@ mod dist { assert_eq!(first(cache.all::()), &[dist::Mingw { host: a },]); assert_eq!( first(cache.all::()), - &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },] + &[dist::Rustc { compiler: Compiler::new(2, a) },] ); assert_eq!( first(cache.all::()), - &[dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },] + &[dist::Std { compiler: Compiler::new(1, a), target: a },] ); assert_eq!(first(cache.all::()), &[dist::Src]); // Make sure rustdoc is only built once. assert_eq!( first(cache.all::()), - &[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },] + &[tool::Rustdoc { compiler: Compiler::new(2, a) },] ); } @@ -450,13 +498,13 @@ mod dist { ); assert_eq!( first(cache.all::()), - &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },] + &[dist::Rustc { compiler: Compiler::new(2, a) },] ); assert_eq!( first(cache.all::()), &[ - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - dist::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, + dist::Std { compiler: Compiler::new(1, a), target: a }, + dist::Std { compiler: Compiler::new(2, a), target: b }, ] ); assert_eq!(first(cache.all::()), &[dist::Src]); @@ -483,15 +531,15 @@ mod dist { assert_eq!( first(cache.all::()), &[ - dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, - dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, + dist::Rustc { compiler: Compiler::new(2, a) }, + dist::Rustc { compiler: Compiler::new(2, b) }, ] ); assert_eq!( first(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::new(1, a), target: a }, + dist::Std { compiler: Compiler::new(1, a), target: b }, ] ); assert_eq!( @@ -519,13 +567,12 @@ mod dist { assert_eq!( first(cache.all::()), - &[dist::Rustc { compiler: Compiler { host: b, stage: 2 } },] + &[dist::Rustc { compiler: Compiler::new(2, b) },] ); assert_eq!( first(cache.all::()), &[ rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), ] ); @@ -556,16 +603,16 @@ mod dist { assert_eq!( first(cache.all::()), &[ - dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, - dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, + dist::Rustc { compiler: Compiler::new(2, a) }, + dist::Rustc { compiler: Compiler::new(2, b) }, ] ); assert_eq!( first(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 }, + dist::Std { compiler: Compiler::new(1, a), target: a }, + dist::Std { compiler: Compiler::new(1, a), target: b }, + dist::Std { compiler: Compiler::new(2, a), target: c }, ] ); assert_eq!(first(cache.all::()), &[dist::Src]); @@ -583,7 +630,7 @@ mod dist { assert_eq!(first(cache.all::()), &[dist::Mingw { host: c },]); assert_eq!( first(cache.all::()), - &[dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c },] + &[dist::Std { compiler: Compiler::new(2, a), target: c },] ); } @@ -608,15 +655,15 @@ mod dist { assert_eq!( first(cache.all::()), &[ - dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, - dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, + dist::Rustc { compiler: Compiler::new(2, a) }, + dist::Rustc { compiler: Compiler::new(2, b) }, ] ); assert_eq!( first(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::new(1, a), target: a }, + dist::Std { compiler: Compiler::new(1, a), target: b }, ] ); assert_eq!(first(cache.all::()), &[dist::Src]); @@ -633,10 +680,41 @@ mod dist { assert_eq!( first(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 } }, + compile::Assemble { target_compiler: Compiler::new(0, a) }, + compile::Assemble { target_compiler: Compiler::new(1, a) }, + compile::Assemble { target_compiler: Compiler::new(2, a) }, + compile::Assemble { target_compiler: Compiler::new(2, b) }, + ] + ); + } + + /// This also serves as an important regression test for + /// and . + #[test] + fn dist_all_cross() { + let cmd_args = + &["dist", "--stage", "2", "--dry-run", "--config=/does/not/exist"].map(str::to_owned); + let config_str = r#" + [rust] + channel = "nightly" + + [build] + extended = true + + build = "i686-unknown-haiku" + host = ["i686-unknown-netbsd"] + target = ["i686-unknown-netbsd"] + "#; + let config = Config::parse_inner(Flags::parse(cmd_args), |&_| toml::from_str(config_str)); + let mut cache = run_build(&[], config); + + // Stage 2 `compile::Rustc` should **NEVER** be cached here. + assert_eq!( + first(cache.all::()), + &[ + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_3, stage = 1), ] ); } @@ -653,6 +731,20 @@ mod dist { &["compiler/rustc".into(), "library".into()], ); + assert_eq!(builder.config.stage, 2); + + // `compile::Rustc` includes one-stage-off compiler information as the target compiler + // artifacts get copied from there to the target stage sysroot. + // For example, `stage2/bin/rustc` gets copied from the `stage1-rustc` build directory. + assert_eq!( + first(builder.cache.all::()), + &[ + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), + ] + ); + assert_eq!( first(builder.cache.all::()), &[ @@ -664,15 +756,22 @@ mod dist { std!(TEST_TRIPLE_1 => TEST_TRIPLE_3, stage = 2), ] ); - assert_eq!(builder.cache.all::().len(), 5); + assert_eq!( - first(builder.cache.all::()), + first(builder.cache.all::()), &[ - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 2), - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 2), + compile::Assemble { + target_compiler: Compiler::new(0, TargetSelection::from_user(TEST_TRIPLE_1),) + }, + compile::Assemble { + target_compiler: Compiler::new(1, TargetSelection::from_user(TEST_TRIPLE_1),) + }, + compile::Assemble { + target_compiler: Compiler::new(2, TargetSelection::from_user(TEST_TRIPLE_1),) + }, + compile::Assemble { + target_compiler: Compiler::new(2, TargetSelection::from_user(TEST_TRIPLE_2),) + }, ] ); } @@ -714,9 +813,9 @@ mod dist { 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::new(0, a) }, + compile::Assemble { target_compiler: Compiler::new(1, a) }, + compile::Assemble { target_compiler: Compiler::new(2, a) }, ] ); assert_eq!( @@ -765,7 +864,7 @@ mod dist { assert_eq!( first(builder.cache.all::()), &[test::Crate { - compiler: Compiler { host, stage: 0 }, + compiler: Compiler::new(0, host), target: host, mode: crate::Mode::Std, crates: vec!["std".to_owned()], @@ -791,13 +890,13 @@ mod dist { ); assert_eq!( first(builder.cache.all::()), - &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }] + &[tool::ErrorIndex { compiler: Compiler::new(1, a) }] ); // 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 } },] + &[tool::Rustdoc { compiler: Compiler::new(2, a) },] ); } @@ -837,7 +936,7 @@ mod dist { ); assert_eq!( first(builder.cache.all::()), - &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }] + &[tool::ErrorIndex { compiler: Compiler::new(1, a) }] ); // Unfortunately rustdoc is built twice. Once from stage1 for compiletest // (and other things), and once from stage0 for std crates. Ideally it @@ -853,9 +952,9 @@ mod dist { assert_eq!( first(builder.cache.all::()), &[ - tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } }, - tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }, - tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } }, + tool::Rustdoc { compiler: Compiler::new(0, a) }, + tool::Rustdoc { compiler: Compiler::new(1, a) }, + tool::Rustdoc { compiler: Compiler::new(2, a) }, ] ); } @@ -871,7 +970,7 @@ mod sysroot_target_dirs { let build = Build::new(configure("build", &[TEST_TRIPLE_1], &[TEST_TRIPLE_1])); let builder = Builder::new(&build); let target_triple_1 = TargetSelection::from_user(TEST_TRIPLE_1); - let compiler = Compiler { stage: 1, host: target_triple_1 }; + let compiler = Compiler::new(1, target_triple_1); let target_triple_2 = TargetSelection::from_user(TEST_TRIPLE_2); let actual = builder.sysroot_target_libdir(compiler, target_triple_2); @@ -891,7 +990,7 @@ mod sysroot_target_dirs { let build = Build::new(configure("build", &[TEST_TRIPLE_1], &[TEST_TRIPLE_1])); let builder = Builder::new(&build); let target_triple_1 = TargetSelection::from_user(TEST_TRIPLE_1); - let compiler = Compiler { stage: 1, host: target_triple_1 }; + let compiler = Compiler::new(1, target_triple_1); let target_triple_2 = TargetSelection::from_user(TEST_TRIPLE_2); let actual = builder.sysroot_target_bindir(compiler, target_triple_2); @@ -1048,7 +1147,7 @@ fn test_prebuilt_llvm_config_path_resolution() { let config = configure( r#" [llvm] - download-ci-llvm = true + download-ci-llvm = "if-unchanged" "#, ); @@ -1081,8 +1180,8 @@ fn test_is_builder_target() { let build = Build::new(config); let builder = Builder::new(&build); - assert!(builder.is_builder_target(target1)); - assert!(!builder.is_builder_target(target2)); + assert!(builder.config.is_host_target(target1)); + assert!(!builder.config.is_host_target(target2)); } } @@ -1095,13 +1194,13 @@ fn test_get_tool_rustc_compiler() { let target_triple_1 = TargetSelection::from_user(TEST_TRIPLE_1); - let compiler = Compiler { stage: 2, host: target_triple_1 }; - let expected = Compiler { stage: 1, host: target_triple_1 }; + let compiler = Compiler::new(2, target_triple_1); + let expected = Compiler::new(1, target_triple_1); let actual = tool::get_tool_rustc_compiler(&builder, compiler); assert_eq!(expected, actual); - let compiler = Compiler { stage: 1, host: target_triple_1 }; - let expected = Compiler { stage: 0, host: target_triple_1 }; + let compiler = Compiler::new(1, target_triple_1); + let expected = Compiler::new(0, target_triple_1); let actual = tool::get_tool_rustc_compiler(&builder, compiler); assert_eq!(expected, actual); @@ -1110,8 +1209,40 @@ fn test_get_tool_rustc_compiler() { let build = Build::new(config); let builder = Builder::new(&build); - let compiler = Compiler { stage: 1, host: target_triple_1 }; - let expected = Compiler { stage: 1, host: target_triple_1 }; + let compiler = Compiler::new(1, target_triple_1); + let expected = Compiler::new(1, target_triple_1); let actual = tool::get_tool_rustc_compiler(&builder, compiler); assert_eq!(expected, actual); } + +/// When bootstrap detects a step dependency cycle (which is a bug), its panic +/// message should show the actual steps on the stack, not just several copies +/// of `Any { .. }`. +#[test] +fn step_cycle_debug() { + let cmd = ["run", "cyclic-step"].map(str::to_owned); + let config = configure_with_args(&cmd, &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]); + + let err = panic::catch_unwind(|| run_build(&config.paths.clone(), config)).unwrap_err(); + let err = err.downcast_ref::().unwrap().as_str(); + + assert!(!err.contains("Any")); + assert!(err.contains("CyclicStep { n: 1 }")); +} + +/// The `AnyDebug` trait should delegate to the underlying type's `Debug`, and +/// should also allow downcasting as expected. +#[test] +fn any_debug() { + #[derive(Debug, PartialEq, Eq)] + struct MyStruct { + x: u32, + } + + let x: &dyn AnyDebug = &MyStruct { x: 7 }; + + // Debug-formatting should delegate to the underlying type. + assert_eq!(format!("{x:?}"), format!("{:?}", MyStruct { x: 7 })); + // Downcasting to the underlying type should succeed. + assert_eq!(x.downcast_ref::(), Some(&MyStruct { x: 7 })); +} diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index d0e0ed50ad89f..3b8c3655b8d1c 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1,21 +1,22 @@ //! Serialized configuration of a build. //! -//! This module implements parsing `config.toml` configuration files to tweak +//! This module implements parsing `bootstrap.toml` configuration files to tweak //! how the build runs. -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::{self, Display}; +use std::hash::Hash; use std::io::IsTerminal; use std::path::{Path, PathBuf, absolute}; use std::process::Command; use std::str::FromStr; -use std::sync::OnceLock; +use std::sync::{Arc, Mutex, OnceLock}; use std::{cmp, env, fs}; use build_helper::ci::CiEnv; use build_helper::exit; -use build_helper::git::{GitConfig, get_closest_merge_commit, output_result}; +use build_helper::git::{GitConfig, PathFreshness, check_path_modifications, output_result}; use serde::{Deserialize, Deserializer}; use serde_derive::Deserialize; #[cfg(feature = "tracing")] @@ -23,6 +24,7 @@ use tracing::{instrument, span}; use crate::core::build_steps::compile::CODEGEN_BACKEND_PREFIX; use crate::core::build_steps::llvm; +use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS; pub use crate::core::config::flags::Subcommand; use crate::core::config::flags::{Color, Flags, Warnings}; use crate::core::download::is_download_ci_available; @@ -64,7 +66,7 @@ macro_rules! check_ci_llvm { /// useful in scenarios where developers want to see how the tarball sources were /// generated. /// -/// We also use this file to compare the host's config.toml against the CI rustc builder +/// We also use this file to compare the host's bootstrap.toml against the CI rustc builder /// configuration to detect any incompatible options. pub(crate) const BUILDER_CONFIG_FILENAME: &str = "builder-config"; @@ -171,17 +173,28 @@ impl LldMode { } } +/// Determines how will GCC be provided. +#[derive(Default, Clone)] +pub enum GccCiMode { + /// Build GCC from the local `src/gcc` submodule. + #[default] + BuildLocally, + /// Try to download GCC from CI. + /// If it is not available on CI, it will be built locally instead. + DownloadFromCi, +} + /// Global configuration for the entire build and/or bootstrap. /// -/// This structure is parsed from `config.toml`, and some of the fields are inferred from `git` or build-time parameters. +/// This structure is parsed from `bootstrap.toml`, and some of the fields are inferred from `git` or build-time parameters. /// /// Note that this structure is not decoded directly into, but rather it is /// filled out from the decoded forms of the structs below. For documentation -/// each field, see the corresponding fields in -/// `config.example.toml`. +/// on each field, see the corresponding fields in +/// `bootstrap.example.toml`. #[derive(Default, Clone)] pub struct Config { - pub change_id: Option, + pub change_id: Option, pub bypass_bootstrap_lock: bool, pub ccache: Option, /// Call Build::ninja() instead of this. @@ -224,7 +237,7 @@ pub struct Config { pub keep_stage: Vec, pub keep_stage_std: Vec, pub src: PathBuf, - /// defaults to `config.toml` + /// defaults to `bootstrap.toml` pub config: Option, pub jobs: Option, pub cmd: Subcommand, @@ -283,6 +296,9 @@ pub struct Config { pub llvm_ldflags: Option, pub llvm_use_libcxx: bool, + // gcc codegen options + pub gcc_ci_mode: GccCiMode, + // rust codegen options pub rust_optimize: RustOptimize, pub rust_codegen_units: Option, @@ -290,6 +306,7 @@ pub struct Config { pub rustc_debug_assertions: bool, pub std_debug_assertions: bool, + pub tools_debug_assertions: bool, pub rust_overflow_checks: bool, pub rust_overflow_checks_std: bool, @@ -389,11 +406,7 @@ pub struct Config { pub initial_rustc: PathBuf, pub initial_cargo_clippy: Option, pub initial_sysroot: PathBuf, - - #[cfg(not(test))] - initial_rustfmt: RefCell, - #[cfg(test)] - pub initial_rustfmt: RefCell, + pub initial_rustfmt: Option, /// The paths to work with. For example: with `./x check foo bar` we get /// `paths=["foo", "bar"]`. @@ -401,15 +414,14 @@ pub struct Config { /// Command for visual diff display, e.g. `diff-tool --color=always`. pub compiletest_diff_tool: Option, -} -#[derive(Clone, Debug, Default)] -pub enum RustfmtState { - SystemToolchain(PathBuf), - Downloaded(PathBuf), - Unavailable, - #[default] - LazyEvaluated, + /// Whether to use the precompiled stage0 libtest with compiletest. + pub compiletest_use_stage0_libtest: bool, + + pub is_running_on_ci: bool, + + /// Cache for determining path modifications + pub path_modification_cache: Arc, PathFreshness>>>, } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] @@ -456,7 +468,7 @@ impl std::str::FromStr for SplitDebuginfo { impl SplitDebuginfo { /// Returns the default `-Csplit-debuginfo` value for the current target. See the comment for - /// `rust.split-debuginfo` in `config.example.toml`. + /// `rust.split-debuginfo` in `bootstrap.example.toml`. fn default_for_platform(target: TargetSelection) -> Self { if target.contains("apple") { SplitDebuginfo::Unpacked @@ -663,7 +675,7 @@ impl Target { target } } -/// Structure of the `config.toml` file that configuration is read from. +/// Structure of the `bootstrap.toml` file that configuration is read from. /// /// This structure uses `Decodable` to automatically decode a TOML configuration /// file into this format, and then this is traversed and written into the above @@ -676,10 +688,19 @@ pub(crate) struct TomlConfig { build: Option, install: Option, llvm: Option, + gcc: Option, rust: Option, target: Option>, dist: Option, profile: Option, + include: Option>, +} + +/// This enum is used for deserializing change IDs from TOML, allowing both numeric values and the string `"ignore"`. +#[derive(Clone, Debug, PartialEq)] +pub enum ChangeId { + Ignore, + Id(usize), } /// Since we use `#[serde(deny_unknown_fields)]` on `TomlConfig`, we need a wrapper type @@ -688,8 +709,23 @@ pub(crate) struct TomlConfig { /// to allow developers to potentially find the reason for the failure in the logs.. #[derive(Deserialize, Default)] pub(crate) struct ChangeIdWrapper { - #[serde(alias = "change-id")] - pub(crate) inner: Option, + #[serde(alias = "change-id", default, deserialize_with = "deserialize_change_id")] + pub(crate) inner: Option, +} + +fn deserialize_change_id<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + let value = toml::Value::deserialize(deserializer)?; + Ok(match value { + toml::Value::String(s) if s == "ignore" => Some(ChangeId::Ignore), + toml::Value::Integer(i) => Some(ChangeId::Id(i as usize)), + _ => { + return Err(serde::de::Error::custom( + "expected \"ignore\" or an integer for change-id", + )); + } + }) } /// Describes how to handle conflicts in merging two [`TomlConfig`] @@ -704,31 +740,40 @@ enum ReplaceOpt { } trait Merge { - fn merge(&mut self, other: Self, replace: ReplaceOpt); + fn merge( + &mut self, + parent_config_path: Option, + included_extensions: &mut HashSet, + other: Self, + replace: ReplaceOpt, + ); } impl Merge for TomlConfig { fn merge( &mut self, - TomlConfig { build, install, llvm, rust, dist, target, profile, change_id }: Self, + parent_config_path: Option, + included_extensions: &mut HashSet, + TomlConfig { build, install, llvm, gcc, rust, dist, target, profile, change_id, include }: Self, replace: ReplaceOpt, ) { fn do_merge(x: &mut Option, y: Option, replace: ReplaceOpt) { if let Some(new) = y { if let Some(original) = x { - original.merge(new, replace); + original.merge(None, &mut Default::default(), new, replace); } else { *x = Some(new); } } } - self.change_id.inner.merge(change_id.inner, replace); - self.profile.merge(profile, replace); + self.change_id.inner.merge(None, &mut Default::default(), change_id.inner, replace); + self.profile.merge(None, &mut Default::default(), profile, replace); do_merge(&mut self.build, build, replace); do_merge(&mut self.install, install, replace); do_merge(&mut self.llvm, llvm, replace); + do_merge(&mut self.gcc, gcc, replace); do_merge(&mut self.rust, rust, replace); do_merge(&mut self.dist, dist, replace); @@ -738,13 +783,50 @@ impl Merge for TomlConfig { (Some(original_target), Some(new_target)) => { for (triple, new) in new_target { if let Some(original) = original_target.get_mut(&triple) { - original.merge(new, replace); + original.merge(None, &mut Default::default(), new, replace); } else { original_target.insert(triple, new); } } } } + + let parent_dir = parent_config_path + .as_ref() + .and_then(|p| p.parent().map(ToOwned::to_owned)) + .unwrap_or_default(); + + // `include` handled later since we ignore duplicates using `ReplaceOpt::IgnoreDuplicate` to + // keep the upper-level configuration to take precedence. + for include_path in include.clone().unwrap_or_default().iter().rev() { + let include_path = parent_dir.join(include_path); + let include_path = include_path.canonicalize().unwrap_or_else(|e| { + eprintln!("ERROR: Failed to canonicalize '{}' path: {e}", include_path.display()); + exit!(2); + }); + + let included_toml = Config::get_toml_inner(&include_path).unwrap_or_else(|e| { + eprintln!("ERROR: Failed to parse '{}': {e}", include_path.display()); + exit!(2); + }); + + assert!( + included_extensions.insert(include_path.clone()), + "Cyclic inclusion detected: '{}' is being included again before its previous inclusion was fully processed.", + include_path.display() + ); + + self.merge( + Some(include_path.clone()), + included_extensions, + included_toml, + // Ensures that parent configuration always takes precedence + // over child configurations. + ReplaceOpt::IgnoreDuplicate, + ); + + included_extensions.remove(&include_path); + } } } @@ -759,7 +841,13 @@ macro_rules! define_config { } impl Merge for $name { - fn merge(&mut self, other: Self, replace: ReplaceOpt) { + fn merge( + &mut self, + _parent_config_path: Option, + _included_extensions: &mut HashSet, + other: Self, + replace: ReplaceOpt + ) { $( match replace { ReplaceOpt::IgnoreDuplicate => { @@ -859,7 +947,13 @@ macro_rules! define_config { } impl Merge for Option { - fn merge(&mut self, other: Self, replace: ReplaceOpt) { + fn merge( + &mut self, + _parent_config_path: Option, + _included_extensions: &mut HashSet, + other: Self, + replace: ReplaceOpt, + ) { match replace { ReplaceOpt::IgnoreDuplicate => { if self.is_none() { @@ -894,6 +988,7 @@ define_config! { #[derive(Default)] struct Build { build: Option = "build", + description: Option = "description", host: Option> = "host", target: Option> = "target", build_dir: Option = "build-dir", @@ -941,7 +1036,9 @@ define_config! { optimized_compiler_builtins: Option = "optimized-compiler-builtins", jobs: Option = "jobs", compiletest_diff_tool: Option = "compiletest-diff-tool", + compiletest_use_stage0_libtest: Option = "compiletest-use-stage0-libtest", ccache: Option = "ccache", + exclude: Option> = "exclude", } } @@ -994,6 +1091,13 @@ define_config! { } } +define_config! { + /// TOML representation of how the GCC build is configured. + struct Gcc { + download_ci_gcc: Option = "download-ci-gcc", + } +} + define_config! { struct Dist { sign_folder: Option = "sign-folder", @@ -1164,6 +1268,7 @@ define_config! { rustc_debug_assertions: Option = "debug-assertions", randomize_layout: Option = "randomize-layout", std_debug_assertions: Option = "debug-assertions-std", + tools_debug_assertions: Option = "debug-assertions-tools", overflow_checks: Option = "overflow-checks", overflow_checks_std: Option = "overflow-checks-std", debug_logging: Option = "debug-logging", @@ -1176,6 +1281,7 @@ define_config! { incremental: Option = "incremental", default_linker: Option = "default-linker", channel: Option = "channel", + // FIXME: Remove this field at Q2 2025, it has been replaced by build.description description: Option = "description", musl_root: Option = "musl-root", rpath: Option = "rpath", @@ -1308,13 +1414,15 @@ impl Config { Self::get_toml(&builder_config_path) } - #[cfg(test)] - pub(crate) fn get_toml(_: &Path) -> Result { - Ok(TomlConfig::default()) + pub(crate) fn get_toml(file: &Path) -> Result { + #[cfg(test)] + return Ok(TomlConfig::default()); + + #[cfg(not(test))] + Self::get_toml_inner(file) } - #[cfg(not(test))] - pub(crate) fn get_toml(file: &Path) -> Result { + fn get_toml_inner(file: &Path) -> Result { let contents = t!(fs::read_to_string(file), format!("config file {} not found", file.display())); // Deserialize to Value and then TomlConfig to prevent the Deserialize impl of @@ -1322,14 +1430,15 @@ impl Config { toml::from_str(&contents) .and_then(|table: toml::Value| TomlConfig::deserialize(table)) .inspect_err(|_| { - if let Ok(Some(changes)) = toml::from_str(&contents) - .and_then(|table: toml::Value| ChangeIdWrapper::deserialize(table)) - .map(|change_id| change_id.inner.map(crate::find_recent_config_change_ids)) + if let Ok(ChangeIdWrapper { inner: Some(ChangeId::Id(id)) }) = + toml::from_str::(&contents) + .and_then(|table: toml::Value| ChangeIdWrapper::deserialize(table)) { + let changes = crate::find_recent_config_change_ids(id); if !changes.is_empty() { println!( "WARNING: There have been changes to x.py since you last updated:\n{}", - crate::human_readable_changes(&changes) + crate::human_readable_changes(changes) ); } } @@ -1372,22 +1481,6 @@ impl Config { "flags.exclude" = ?flags.exclude ); - config.skip = flags - .skip - .into_iter() - .chain(flags.exclude) - .map(|p| { - // Never return top-level path here as it would break `--skip` - // logic on rustc's internal test framework which is utilized - // by compiletest. - if cfg!(windows) { - PathBuf::from(p.to_str().unwrap().replace('/', "\\")) - } else { - p - } - }) - .collect(); - #[cfg(feature = "tracing")] span!( target: "CONFIG_HANDLING", @@ -1412,55 +1505,60 @@ impl Config { config.llvm_profile_generate = flags.llvm_profile_generate; config.enable_bolt_settings = flags.enable_bolt_settings; config.bypass_bootstrap_lock = flags.bypass_bootstrap_lock; + config.is_running_on_ci = flags.ci.unwrap_or(CiEnv::is_ci()); // Infer the rest of the configuration. - // Infer the source directory. This is non-trivial because we want to support a downloaded bootstrap binary, - // running on a completely different machine from where it was compiled. - let mut cmd = helpers::git(None); - // NOTE: we cannot support running from outside the repository because the only other path we have available - // is set at compile time, which can be wrong if bootstrap was downloaded rather than compiled locally. - // We still support running outside the repository if we find we aren't in a git directory. - - // NOTE: We get a relative path from git to work around an issue on MSYS/mingw. If we used an absolute path, - // and end up using MSYS's git rather than git-for-windows, we would get a unix-y MSYS path. But as bootstrap - // has already been (kinda-cross-)compiled to Windows land, we require a normal Windows path. - cmd.arg("rev-parse").arg("--show-cdup"); - // Discard stderr because we expect this to fail when building from a tarball. - let output = cmd - .as_command_mut() - .stderr(std::process::Stdio::null()) - .output() - .ok() - .and_then(|output| if output.status.success() { Some(output) } else { None }); - if let Some(output) = output { - let git_root_relative = String::from_utf8(output.stdout).unwrap(); - // We need to canonicalize this path to make sure it uses backslashes instead of forward slashes, - // and to resolve any relative components. - let git_root = env::current_dir() - .unwrap() - .join(PathBuf::from(git_root_relative.trim())) - .canonicalize() - .unwrap(); - let s = git_root.to_str().unwrap(); - - // Bootstrap is quite bad at handling /? in front of paths - let git_root = match s.strip_prefix("\\\\?\\") { - Some(p) => PathBuf::from(p), - None => git_root, - }; - // If this doesn't have at least `stage0`, we guessed wrong. This can happen when, - // for example, the build directory is inside of another unrelated git directory. - // In that case keep the original `CARGO_MANIFEST_DIR` handling. - // - // NOTE: this implies that downloadable bootstrap isn't supported when the build directory is outside - // the source directory. We could fix that by setting a variable from all three of python, ./x, and x.ps1. - if git_root.join("src").join("stage0").exists() { - config.src = git_root; - } + if let Some(src) = flags.src { + config.src = src } else { - // We're building from a tarball, not git sources. - // We don't support pre-downloaded bootstrap in this case. + // Infer the source directory. This is non-trivial because we want to support a downloaded bootstrap binary, + // running on a completely different machine from where it was compiled. + let mut cmd = helpers::git(None); + // NOTE: we cannot support running from outside the repository because the only other path we have available + // is set at compile time, which can be wrong if bootstrap was downloaded rather than compiled locally. + // We still support running outside the repository if we find we aren't in a git directory. + + // NOTE: We get a relative path from git to work around an issue on MSYS/mingw. If we used an absolute path, + // and end up using MSYS's git rather than git-for-windows, we would get a unix-y MSYS path. But as bootstrap + // has already been (kinda-cross-)compiled to Windows land, we require a normal Windows path. + cmd.arg("rev-parse").arg("--show-cdup"); + // Discard stderr because we expect this to fail when building from a tarball. + let output = cmd + .as_command_mut() + .stderr(std::process::Stdio::null()) + .output() + .ok() + .and_then(|output| if output.status.success() { Some(output) } else { None }); + if let Some(output) = output { + let git_root_relative = String::from_utf8(output.stdout).unwrap(); + // We need to canonicalize this path to make sure it uses backslashes instead of forward slashes, + // and to resolve any relative components. + let git_root = env::current_dir() + .unwrap() + .join(PathBuf::from(git_root_relative.trim())) + .canonicalize() + .unwrap(); + let s = git_root.to_str().unwrap(); + + // Bootstrap is quite bad at handling /? in front of paths + let git_root = match s.strip_prefix("\\\\?\\") { + Some(p) => PathBuf::from(p), + None => git_root, + }; + // If this doesn't have at least `stage0`, we guessed wrong. This can happen when, + // for example, the build directory is inside of another unrelated git directory. + // In that case keep the original `CARGO_MANIFEST_DIR` handling. + // + // NOTE: this implies that downloadable bootstrap isn't supported when the build directory is outside + // the source directory. We could fix that by setting a variable from all three of python, ./x, and x.ps1. + if git_root.join("src").join("stage0").exists() { + config.src = git_root; + } + } else { + // We're building from a tarball, not git sources. + // We don't support pre-downloaded bootstrap in this case. + } } if cfg!(test) { @@ -1475,29 +1573,36 @@ impl Config { config.stage0_metadata = build_helper::stage0_parser::parse_stage0_file(); - // Find configuration file, with the following cascading fallback (first match wins): - // - `--config ` - // - `RUST_BOOTSTRAP_CONFIG` - // - `./config.toml` - // - `config.toml` in the root directory. + // Locate the configuration file using the following priority (first match wins): + // 1. `--config ` (explicit flag) + // 2. `RUST_BOOTSTRAP_CONFIG` environment variable + // 3. `./bootstrap.toml` (local file) + // 4. `/bootstrap.toml` + // 5. `./config.toml` (fallback for backward compatibility) + // 6. `/config.toml` let toml_path = flags .config .clone() .or_else(|| env::var_os("RUST_BOOTSTRAP_CONFIG").map(PathBuf::from)); let using_default_path = toml_path.is_none(); - let mut toml_path = toml_path.unwrap_or_else(|| PathBuf::from("config.toml")); + let mut toml_path = toml_path.unwrap_or_else(|| PathBuf::from("bootstrap.toml")); + if using_default_path && !toml_path.exists() { - toml_path = config.src.join(toml_path); + toml_path = config.src.join(PathBuf::from("bootstrap.toml")); + if !toml_path.exists() { + toml_path = PathBuf::from("config.toml"); + if !toml_path.exists() { + toml_path = config.src.join(PathBuf::from("config.toml")); + } + } } - let file_content = t!(fs::read_to_string(config.src.join("src/ci/channel"))); - let ci_channel = file_content.trim_end(); - // Give a hard error if `--config` or `RUST_BOOTSTRAP_CONFIG` are set to a missing path, - // but not if `config.toml` hasn't been created. + // but not if `bootstrap.toml` hasn't been created. let mut toml = if !using_default_path || toml_path.exists() { config.config = Some(if cfg!(not(test)) { - toml_path.canonicalize().unwrap() + toml_path = toml_path.canonicalize().unwrap(); + toml_path.clone() } else { toml_path.clone() }); @@ -1515,7 +1620,7 @@ impl Config { // same ones used to call the tests (if custom ones are not defined in the toml). If we // don't do that, bootstrap will use its own detection logic to find a suitable rustc // and Cargo, which doesn't work when the caller is specìfying a custom local rustc or - // Cargo in their config.toml. + // Cargo in their bootstrap.toml. let build = toml.build.get_or_insert_with(Default::default); build.rustc = build.rustc.take().or(std::env::var_os("RUSTC").map(|p| p.into())); build.cargo = build.cargo.take().or(std::env::var_os("CARGO").map(|p| p.into())); @@ -1525,6 +1630,26 @@ impl Config { toml.profile = Some("dist".into()); } + // Reverse the list to ensure the last added config extension remains the most dominant. + // For example, given ["a.toml", "b.toml"], "b.toml" should take precedence over "a.toml". + // + // This must be handled before applying the `profile` since `include`s should always take + // precedence over `profile`s. + for include_path in toml.include.clone().unwrap_or_default().iter().rev() { + let include_path = toml_path.parent().unwrap().join(include_path); + + let included_toml = get_toml(&include_path).unwrap_or_else(|e| { + eprintln!("ERROR: Failed to parse '{}': {e}", include_path.display()); + exit!(2); + }); + toml.merge( + Some(include_path), + &mut Default::default(), + included_toml, + ReplaceOpt::IgnoreDuplicate, + ); + } + if let Some(include) = &toml.profile { // Allows creating alias for profile names, allowing // profiles to be renamed while maintaining back compatibility @@ -1538,7 +1663,7 @@ impl Config { include_path.push("src"); include_path.push("bootstrap"); include_path.push("defaults"); - include_path.push(format!("config.{include}.toml")); + include_path.push(format!("bootstrap.{include}.toml")); let included_toml = get_toml(&include_path).unwrap_or_else(|e| { eprintln!( "ERROR: Failed to parse default config profile at '{}': {e}", @@ -1546,7 +1671,12 @@ impl Config { ); exit!(2); }); - toml.merge(included_toml, ReplaceOpt::IgnoreDuplicate); + toml.merge( + Some(include_path), + &mut Default::default(), + included_toml, + ReplaceOpt::IgnoreDuplicate, + ); } let mut override_toml = TomlConfig::default(); @@ -1557,7 +1687,12 @@ impl Config { let mut err = match get_table(option) { Ok(v) => { - override_toml.merge(v, ReplaceOpt::ErrorOnDuplicate); + override_toml.merge( + None, + &mut Default::default(), + v, + ReplaceOpt::ErrorOnDuplicate, + ); continue; } Err(e) => e, @@ -1568,7 +1703,12 @@ impl Config { if !value.contains('"') { match get_table(&format!(r#"{key}="{value}""#)) { Ok(v) => { - override_toml.merge(v, ReplaceOpt::ErrorOnDuplicate); + override_toml.merge( + None, + &mut Default::default(), + v, + ReplaceOpt::ErrorOnDuplicate, + ); continue; } Err(e) => err = e, @@ -1578,11 +1718,12 @@ impl Config { eprintln!("failed to parse override `{option}`: `{err}"); exit!(2) } - toml.merge(override_toml, ReplaceOpt::Override); + toml.merge(None, &mut Default::default(), override_toml, ReplaceOpt::Override); config.change_id = toml.change_id.inner; let Build { + mut description, build, host, target, @@ -1631,9 +1772,31 @@ impl Config { optimized_compiler_builtins, jobs, compiletest_diff_tool, + compiletest_use_stage0_libtest, mut ccache, + exclude, } = toml.build.unwrap_or_default(); + let mut paths: Vec = flags.skip.into_iter().chain(flags.exclude).collect(); + + if let Some(exclude) = exclude { + paths.extend(exclude); + } + + config.skip = paths + .into_iter() + .map(|p| { + // Never return top-level path here as it would break `--skip` + // logic on rustc's internal test framework which is utilized + // by compiletest. + if cfg!(windows) { + PathBuf::from(p.to_str().unwrap().replace('/', "\\")) + } else { + p + } + }) + .collect(); + config.jobs = Some(threads_from_config(flags.jobs.unwrap_or(jobs.unwrap_or(0)))); if let Some(file_build) = build { @@ -1763,6 +1926,7 @@ impl Config { let mut debug = None; let mut rustc_debug_assertions = None; let mut std_debug_assertions = None; + let mut tools_debug_assertions = None; let mut overflow_checks = None; let mut overflow_checks_std = None; let mut debug_logging = None; @@ -1775,17 +1939,21 @@ impl Config { let mut lld_enabled = None; let mut std_features = None; - let is_user_configured_rust_channel = - if let Some(channel) = toml.rust.as_ref().and_then(|r| r.channel.clone()) { - if channel == "auto-detect" { - config.channel = ci_channel.into(); - } else { - config.channel = channel; - } + let file_content = t!(fs::read_to_string(config.src.join("src/ci/channel"))); + let ci_channel = file_content.trim_end(); + + let toml_channel = toml.rust.as_ref().and_then(|r| r.channel.clone()); + let is_user_configured_rust_channel = match toml_channel { + Some(channel) if channel == "auto-detect" => { + config.channel = ci_channel.into(); true - } else { - false - }; + } + Some(channel) => { + config.channel = channel; + true + } + None => false, + }; let default = config.channel == "dev"; config.omit_git_hash = toml.rust.as_ref().and_then(|r| r.omit_git_hash).unwrap_or(default); @@ -1810,6 +1978,10 @@ impl Config { && config.src.join(".cargo/config.toml").exists(), ); + if !is_user_configured_rust_channel && config.rust_info.is_from_tarball() { + config.channel = ci_channel.into(); + } + if let Some(rust) = toml.rust { let Rust { optimize: optimize_toml, @@ -1818,6 +1990,7 @@ impl Config { codegen_units_std, rustc_debug_assertions: rustc_debug_assertions_toml, std_debug_assertions: std_debug_assertions_toml, + tools_debug_assertions: tools_debug_assertions_toml, overflow_checks: overflow_checks_toml, overflow_checks_std: overflow_checks_std_toml, debug_logging: debug_logging_toml, @@ -1831,7 +2004,7 @@ impl Config { randomize_layout, default_linker, channel: _, // already handled above - description, + description: rust_description, musl_root, rpath, verbose_tests, @@ -1902,6 +2075,7 @@ impl Config { debug = debug_toml; rustc_debug_assertions = rustc_debug_assertions_toml; std_debug_assertions = std_debug_assertions_toml; + tools_debug_assertions = tools_debug_assertions_toml; overflow_checks = overflow_checks_toml; overflow_checks_std = overflow_checks_std_toml; debug_logging = debug_logging_toml; @@ -1924,7 +2098,12 @@ impl Config { set(&mut config.jemalloc, jemalloc); set(&mut config.test_compare_mode, test_compare_mode); set(&mut config.backtrace, backtrace); - config.description = description; + if rust_description.is_some() { + eprintln!( + "Warning: rust.description is deprecated. Use build.description instead." + ); + } + description = description.or(rust_description); set(&mut config.rust_dist_src, dist_src); set(&mut config.verbose_tests, verbose_tests); // in the case "false" is set explicitly, do not overwrite the command line args @@ -1990,9 +2169,10 @@ impl Config { } config.reproducible_artifacts = flags.reproducible_artifact; + config.description = description; // We need to override `rust.channel` if it's manually specified when using the CI rustc. - // This is because if the compiler uses a different channel than the one specified in config.toml, + // This is because if the compiler uses a different channel than the one specified in bootstrap.toml, // tests may fail due to using a different channel than the one used by the compiler during tests. if let Some(commit) = &config.download_rustc_commit { if is_user_configured_rust_channel { @@ -2007,8 +2187,6 @@ impl Config { config.channel = channel; } - } else if config.rust_info.is_from_tarball() && !is_user_configured_rust_channel { - ci_channel.clone_into(&mut config.channel); } if let Some(llvm) = toml.llvm { @@ -2127,6 +2305,16 @@ impl Config { config.llvm_from_ci = config.parse_download_ci_llvm(None, false); } + if let Some(gcc) = toml.gcc { + config.gcc_ci_mode = match gcc.download_ci_gcc { + Some(value) => match value { + true => GccCiMode::DownloadFromCi, + false => GccCiMode::BuildLocally, + }, + None => GccCiMode::default(), + }; + } + if let Some(t) = toml.target { for (triple, cfg) in t { let mut target = Target::from_triple(&triple); @@ -2247,13 +2435,8 @@ impl Config { }); } - if let Some(r) = rustfmt { - *config.initial_rustfmt.borrow_mut() = if r.exists() { - RustfmtState::SystemToolchain(r) - } else { - RustfmtState::Unavailable - }; - } + config.initial_rustfmt = + if let Some(r) = rustfmt { Some(r) } else { config.maybe_download_rustfmt() }; // Now that we've reached the end of our configuration, infer the // default values for all options that we haven't otherwise stored yet. @@ -2301,12 +2484,20 @@ impl Config { ); } + if config.lld_enabled && config.is_system_llvm(config.build) { + eprintln!( + "Warning: LLD is enabled when using external llvm-config. LLD will not be built and copied to the sysroot." + ); + } + let default_std_features = BTreeSet::from([String::from("panic-unwind")]); config.rust_std_features = std_features.unwrap_or(default_std_features); let default = debug == Some(true); config.rustc_debug_assertions = rustc_debug_assertions.unwrap_or(default); config.std_debug_assertions = std_debug_assertions.unwrap_or(config.rustc_debug_assertions); + config.tools_debug_assertions = + tools_debug_assertions.unwrap_or(config.rustc_debug_assertions); config.rust_overflow_checks = overflow_checks.unwrap_or(default); config.rust_overflow_checks_std = overflow_checks_std.unwrap_or(config.rust_overflow_checks); @@ -2327,6 +2518,7 @@ impl Config { config.optimized_compiler_builtins = optimized_compiler_builtins.unwrap_or(config.channel != "dev"); config.compiletest_diff_tool = compiletest_diff_tool; + config.compiletest_use_stage0_libtest = compiletest_use_stage0_libtest.unwrap_or(true); let download_rustc = config.download_rustc_commit.is_some(); config.explicit_stage_from_cli = flags.stage.is_some(); @@ -2368,7 +2560,7 @@ impl Config { // CI should always run stage 2 builds, unless it specifically states otherwise #[cfg(not(test))] - if flags.stage.is_none() && build_helper::ci::CiEnv::is_ci() { + if flags.stage.is_none() && config.is_running_on_ci { match config.cmd { Subcommand::Test { .. } | Subcommand::Miri { .. } @@ -2590,7 +2782,7 @@ impl Config { if !self.llvm_from_ci { // This happens when LLVM submodule is updated in CI, we should disable ci-rustc without an error // to not break CI. For non-CI environments, we should return an error. - if CiEnv::is_ci() { + if self.is_running_on_ci { println!("WARNING: LLVM submodule has changes, `download-rustc` will be disabled."); return None; } else { @@ -2607,7 +2799,7 @@ impl Config { return None; }, Err(e) => { - eprintln!("ERROR: Failed to parse CI rustc config.toml: {e}"); + eprintln!("ERROR: Failed to parse CI rustc bootstrap.toml: {e}"); exit!(2); }, }; @@ -2641,25 +2833,6 @@ impl Config { .as_deref() } - pub(crate) fn initial_rustfmt(&self) -> Option { - match &mut *self.initial_rustfmt.borrow_mut() { - RustfmtState::SystemToolchain(p) | RustfmtState::Downloaded(p) => Some(p.clone()), - RustfmtState::Unavailable => None, - r @ RustfmtState::LazyEvaluated => { - if self.dry_run() { - return Some(PathBuf::new()); - } - let path = self.maybe_download_rustfmt(); - *r = if let Some(p) = &path { - RustfmtState::Downloaded(p.clone()) - } else { - RustfmtState::Unavailable - }; - path - } - } - } - /// Runs a function if verbosity is greater than 0 pub fn verbose(&self, f: impl Fn()) { if self.is_verbose() { @@ -2760,7 +2933,6 @@ impl Config { pub fn git_config(&self) -> GitConfig<'_> { GitConfig { - git_repository: &self.stage0_metadata.config.git_repository, nightly_branch: &self.stage0_metadata.config.nightly_branch, git_merge_commit_email: &self.stage0_metadata.config.git_merge_commit_email, } @@ -2770,7 +2942,7 @@ impl Config { /// /// `relative_path` should be relative to the root of the git repository, not an absolute path. /// - /// This *does not* update the submodule if `config.toml` explicitly says + /// This *does not* update the submodule if `bootstrap.toml` explicitly says /// not to, or if we're not in a git repository (like a plain source /// tarball). Typically [`crate::Build::require_submodule`] should be /// used instead to provide a nice error to the user if the submodule is @@ -2791,6 +2963,13 @@ impl Config { let absolute_path = self.src.join(relative_path); + // NOTE: This check is required because `jj git clone` doesn't create directories for + // submodules, they are completely ignored. The code below assumes this directory exists, + // so create it here. + if !absolute_path.exists() { + t!(fs::create_dir_all(&absolute_path)); + } + // NOTE: The check for the empty directory is here because when running x.py the first time, // the submodule won't be checked out. Check it out now so we can build it. if !GitInfo::new(false, &absolute_path).is_managed_git_subrepository() @@ -2826,7 +3005,7 @@ impl Config { let actual_hash = recorded .split_whitespace() .nth(2) - .unwrap_or_else(|| panic!("unexpected output `{}`", recorded)); + .unwrap_or_else(|| panic!("unexpected output `{recorded}`")); if actual_hash == checked_out_hash { // already checked out @@ -2976,24 +3155,40 @@ impl Config { // these changes to speed up the build process for library developers. This provides consistent // functionality for library developers between `download-rustc=true` and `download-rustc="if-unchanged"` // options. - if !CiEnv::is_ci() { + // + // If you update "library" logic here, update `builder::tests::ci_rustc_if_unchanged_logic` test + // logic accordingly. + if !self.is_running_on_ci { allowed_paths.push(":!library"); } let commit = if self.rust_info.is_managed_git_subrepository() { // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. - match self.last_modified_commit(&allowed_paths, "download-rustc", if_unchanged) { - Some(commit) => commit, - None => { + let freshness = self.check_path_modifications(&allowed_paths); + self.verbose(|| { + eprintln!("rustc freshness: {freshness:?}"); + }); + match freshness { + PathFreshness::LastModifiedUpstream { upstream } => upstream, + PathFreshness::HasLocalModifications { upstream } => { if if_unchanged { return None; } - println!("ERROR: could not find commit hash for downloading rustc"); - println!("HELP: maybe your repository history is too shallow?"); - println!("HELP: consider setting `rust.download-rustc=false` in config.toml"); - println!("HELP: or fetch enough history to include one upstream commit"); - crate::exit!(1); + + if self.is_running_on_ci { + eprintln!("CI rustc commit matches with HEAD and we are in CI."); + eprintln!( + "`rustc.download-ci` functionality will be skipped as artifacts are not available." + ); + return None; + } + + upstream + } + PathFreshness::MissingUpstream => { + eprintln!("No upstream commit found"); + return None; } } } else { @@ -3002,19 +3197,6 @@ impl Config { .expect("git-commit-info is missing in the project root") }; - if CiEnv::is_ci() && { - let head_sha = - output(helpers::git(Some(&self.src)).arg("rev-parse").arg("HEAD").as_command_mut()); - let head_sha = head_sha.trim(); - commit == head_sha - } { - eprintln!("CI rustc commit matches with HEAD and we are in CI."); - eprintln!( - "`rustc.download-ci` functionality will be skipped as artifacts are not available." - ); - return None; - } - if debug_assertions_requested { eprintln!( "WARN: `rust.debug-assertions = true` will prevent downloading CI rustc as alt CI \ @@ -3031,7 +3213,14 @@ impl Config { download_ci_llvm: Option, asserts: bool, ) -> bool { - let download_ci_llvm = download_ci_llvm.unwrap_or(StringOrBool::Bool(true)); + // We don't ever want to use `true` on CI, as we should not + // download upstream artifacts if there are any local modifications. + let default = if self.is_running_on_ci { + StringOrBool::String("if-unchanged".to_string()) + } else { + StringOrBool::Bool(true) + }; + let download_ci_llvm = download_ci_llvm.unwrap_or(default); let if_unchanged = || { if self.rust_info.is_from_tarball() { @@ -3044,13 +3233,11 @@ impl Config { #[cfg(not(test))] self.update_submodule("src/llvm-project"); - // Check for untracked changes in `src/llvm-project`. - let has_changes = self - .last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true) - .is_none(); + // Check for untracked changes in `src/llvm-project` and other important places. + let has_changes = self.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); // Return false if there are untracked changes, otherwise check if CI LLVM is available. - if has_changes { false } else { llvm::is_ci_llvm_available(self, asserts) } + if has_changes { false } else { llvm::is_ci_llvm_available_for_target(self, asserts) } }; match download_ci_llvm { @@ -3061,61 +3248,87 @@ impl Config { ); } + if b && self.is_running_on_ci { + // On CI, we must always rebuild LLVM if there were any modifications to it + panic!( + "`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead." + ); + } + // If download-ci-llvm=true we also want to check that CI llvm is available - b && llvm::is_ci_llvm_available(self, asserts) + b && llvm::is_ci_llvm_available_for_target(self, asserts) } StringOrBool::String(s) if s == "if-unchanged" => if_unchanged(), StringOrBool::String(other) => { - panic!("unrecognized option for download-ci-llvm: {:?}", other) + panic!("unrecognized option for download-ci-llvm: {other:?}") } } } - /// Returns the last commit in which any of `modified_paths` were changed, - /// or `None` if there are untracked changes in the working directory and `if_unchanged` is true. - pub fn last_modified_commit( - &self, - modified_paths: &[&str], - option_name: &str, - if_unchanged: bool, - ) -> Option { - assert!( - self.rust_info.is_managed_git_subrepository(), - "Can't run `Config::last_modified_commit` on a non-git source." - ); - - // Look for a version to compare to based on the current commit. - // Only commits merged by bors will have CI artifacts. - let commit = get_closest_merge_commit(Some(&self.src), &self.git_config(), &[]).unwrap(); - if commit.is_empty() { - println!("error: could not find commit hash for downloading components from CI"); - println!("help: maybe your repository history is too shallow?"); - println!("help: consider disabling `{option_name}`"); - println!("help: or fetch enough history to include one upstream commit"); - crate::exit!(1); + /// Returns true if any of the `paths` have been modified locally. + pub fn has_changes_from_upstream(&self, paths: &[&'static str]) -> bool { + match self.check_path_modifications(paths) { + PathFreshness::LastModifiedUpstream { .. } => false, + PathFreshness::HasLocalModifications { .. } | PathFreshness::MissingUpstream => true, } + } - // Warn if there were changes to the compiler or standard library since the ancestor commit. - let mut git = helpers::git(Some(&self.src)); - git.args(["diff-index", "--quiet", &commit, "--"]).args(modified_paths); + /// Checks whether any of the given paths have been modified w.r.t. upstream. + pub fn check_path_modifications(&self, paths: &[&'static str]) -> PathFreshness { + // Checking path modifications through git can be relatively expensive (>100ms). + // We do not assume that the sources would change during bootstrap's execution, + // so we can cache the results here. + // Note that we do not use a static variable for the cache, because it would cause problems + // in tests that create separate `Config` instsances. + self.path_modification_cache + .lock() + .unwrap() + .entry(paths.to_vec()) + .or_insert_with(|| { + check_path_modifications(&self.src, &self.git_config(), paths, CiEnv::current()) + .unwrap() + }) + .clone() + } - let has_changes = !t!(git.as_command_mut().status()).success(); - if has_changes { - if if_unchanged { - if self.is_verbose() { - println!( - "warning: saw changes to one of {modified_paths:?} since {commit}; \ - ignoring `{option_name}`" - ); - } - return None; + /// Checks if the given target is the same as the host target. + pub fn is_host_target(&self, target: TargetSelection) -> bool { + self.build == target + } + + /// Returns `true` if this is an external version of LLVM not managed by bootstrap. + /// In particular, we expect llvm sources to be available when this is false. + /// + /// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. + pub fn is_system_llvm(&self, target: TargetSelection) -> bool { + match self.target_config.get(&target) { + Some(Target { llvm_config: Some(_), .. }) => { + let ci_llvm = self.llvm_from_ci && self.is_host_target(target); + !ci_llvm } - println!( - "warning: `{option_name}` is enabled, but there are changes to one of {modified_paths:?}" - ); + // We're building from the in-tree src/llvm-project sources. + Some(Target { llvm_config: None, .. }) => false, + None => false, } + } + + /// Returns `true` if this is our custom, patched, version of LLVM. + /// + /// This does not necessarily imply that we're managing the `llvm-project` submodule. + pub fn is_rust_llvm(&self, target: TargetSelection) -> bool { + match self.target_config.get(&target) { + // We're using a user-controlled version of LLVM. The user has explicitly told us whether the version has our patches. + // (They might be wrong, but that's not a supported use-case.) + // In particular, this tries to support `submodules = false` and `patches = false`, for using a newer version of LLVM that's not through `rust-lang/llvm-project`. + Some(Target { llvm_has_rust_patches: Some(patched), .. }) => *patched, + // The user hasn't promised the patches match. + // This only has our patches if it's downloaded from CI or built from source. + _ => !self.is_system_llvm(target), + } + } - Some(commit.to_string()) + pub fn ci_env(&self) -> CiEnv { + if self.is_running_on_ci { CiEnv::GitHubActions } else { CiEnv::None } } } @@ -3325,6 +3538,7 @@ fn check_incompatible_options_for_ci_rustc( codegen_units_std: _, rustc_debug_assertions: _, std_debug_assertions: _, + tools_debug_assertions: _, overflow_checks: _, overflow_checks_std: _, debuginfo_level: _, diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 3bb62bbe38082..08bd87e03a13b 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -54,7 +54,7 @@ pub struct Flags { /// TOML configuration file for build pub config: Option, #[arg(global = true, long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")] - /// Build directory, overrides `build.build-dir` in `config.toml` + /// Build directory, overrides `build.build-dir` in `bootstrap.toml` pub build_dir: Option, #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "BUILD")] @@ -173,12 +173,15 @@ pub struct Flags { #[arg(global = true)] /// paths for the subcommand pub paths: Vec, - /// override options in config.toml + /// override options in bootstrap.toml #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "section.option=value")] pub set: Vec, /// arguments passed to subcommands #[arg(global = true, last(true), value_name = "ARGS")] pub free_args: Vec, + /// Make bootstrap to behave as it's running on the CI environment or not. + #[arg(global = true, long, value_name = "bool")] + pub ci: Option, } impl Flags { @@ -448,7 +451,7 @@ pub enum Subcommand { /// Set up the environment for development #[command(long_about = format!( "\n -x.py setup creates a `config.toml` which changes the defaults for x.py itself, +x.py setup creates a `bootstrap.toml` which changes the defaults for x.py itself, as well as setting up a git pre-push hook, VS Code config and toolchain link. Arguments: This subcommand accepts a 'profile' to use for builds. For example: @@ -461,7 +464,7 @@ Arguments: ./x.py setup editor ./x.py setup link", Profile::all_for_help(" ").trim_end()))] Setup { - /// Either the profile for `config.toml` or another setup action. + /// Either the profile for `bootstrap.toml` or another setup action. /// May be omitted to set up interactively #[arg(value_name = "|hook|editor|link")] profile: Option, diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs index 9f09dd13f2985..179e15e778bff 100644 --- a/src/bootstrap/src/core/config/mod.rs +++ b/src/bootstrap/src/core/config/mod.rs @@ -1,4 +1,4 @@ -#[allow(clippy::module_inception)] +#[expect(clippy::module_inception)] mod config; pub mod flags; #[cfg(test)] diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index eff5e0337428c..96ac8a6d52fab 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -1,17 +1,22 @@ use std::collections::BTreeSet; -use std::env; use std::fs::{File, remove_file}; use std::io::Write; -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::{env, fs}; +use build_helper::ci::CiEnv; +use build_helper::git::PathFreshness; use clap::CommandFactory; use serde::Deserialize; use super::flags::Flags; use super::{ChangeIdWrapper, Config, RUSTC_IF_UNCHANGED_ALLOWED_PATHS}; +use crate::ChangeId; use crate::core::build_steps::clippy::{LintConfig, get_clippy_rules_in_order}; use crate::core::build_steps::llvm; +use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS; use crate::core::config::{LldMode, Target, TargetSelection, TomlConfig}; +use crate::utils::tests::git::git_test; pub(crate) fn parse(config: &str) -> Config { Config::parse_inner( @@ -20,28 +25,35 @@ pub(crate) fn parse(config: &str) -> Config { ) } -#[test] -fn download_ci_llvm() { - let config = parse(""); - let is_available = llvm::is_ci_llvm_available(&config, config.llvm_assertions); - if is_available { - assert!(config.llvm_from_ci); - } +fn get_toml(file: &Path) -> Result { + let contents = std::fs::read_to_string(file).unwrap(); + toml::from_str(&contents).and_then(|table: toml::Value| TomlConfig::deserialize(table)) +} - let config = parse("llvm.download-ci-llvm = true"); - let is_available = llvm::is_ci_llvm_available(&config, config.llvm_assertions); - if is_available { - assert!(config.llvm_from_ci); - } +/// Helps with debugging by using consistent test-specific directories instead of +/// random temporary directories. +fn prepare_test_specific_dir() -> PathBuf { + let current = std::thread::current(); + // Replace "::" with "_" to make it safe for directory names on Windows systems + let test_path = current.name().unwrap().replace("::", "_"); + + let testdir = parse("").tempdir().join(test_path); + + // clean up any old test files + let _ = fs::remove_dir_all(&testdir); + let _ = fs::create_dir_all(&testdir); + + testdir +} +#[test] +fn download_ci_llvm() { let config = parse("llvm.download-ci-llvm = false"); assert!(!config.llvm_from_ci); let if_unchanged_config = parse("llvm.download-ci-llvm = \"if-unchanged\""); - if if_unchanged_config.llvm_from_ci { - let has_changes = if_unchanged_config - .last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true) - .is_none(); + if if_unchanged_config.llvm_from_ci && if_unchanged_config.is_running_on_ci { + let has_changes = if_unchanged_config.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); assert!( !has_changes, @@ -69,7 +81,7 @@ fn detect_src_and_out() { let expected_src = manifest_dir.ancestors().nth(2).unwrap(); assert_eq!(&cfg.src, expected_src); - // test if build-dir was manually given in config.toml + // test if build-dir was manually given in bootstrap.toml if let Some(custom_build_dir) = build_dir { assert_eq!(&cfg.out, Path::new(custom_build_dir)); } @@ -161,7 +173,7 @@ runner = "x86_64-runner" ) }, ); - assert_eq!(config.change_id, Some(1), "setting top-level value"); + assert_eq!(config.change_id, Some(ChangeId::Id(1)), "setting top-level value"); assert_eq!( config.rust_lto, crate::core::config::RustcLto::Fat, @@ -229,13 +241,15 @@ fn override_toml_duplicate() { #[test] fn profile_user_dist() { fn get_toml(file: &Path) -> Result { - let contents = - if file.ends_with("config.toml") || env::var_os("RUST_BOOTSTRAP_CONFIG").is_some() { - "profile = \"user\"".to_owned() - } else { - assert!(file.ends_with("config.dist.toml")); - std::fs::read_to_string(file).unwrap() - }; + let contents = if file.ends_with("bootstrap.toml") + || file.ends_with("config.toml") + || env::var_os("RUST_BOOTSTRAP_CONFIG").is_some() + { + "profile = \"user\"".to_owned() + } else { + assert!(file.ends_with("config.dist.toml") || file.ends_with("bootstrap.dist.toml")); + std::fs::read_to_string(file).unwrap() + }; toml::from_str(&contents).and_then(|table: toml::Value| TomlConfig::deserialize(table)) } @@ -299,7 +313,7 @@ fn parse_change_id_with_unknown_field() { "#; let change_id_wrapper: ChangeIdWrapper = toml::from_str(config).unwrap(); - assert_eq!(change_id_wrapper.inner, Some(3461)); + assert_eq!(change_id_wrapper.inner, Some(ChangeId::Id(3461))); } #[test] @@ -402,7 +416,7 @@ fn jobs_precedence() { ); assert_eq!(config.jobs, Some(67890)); - // `--set build.jobs` should take precedence over `config.toml`. + // `--set build.jobs` should take precedence over `bootstrap.toml`. let config = Config::parse_inner( Flags::parse(&[ "check".to_owned(), @@ -420,7 +434,7 @@ fn jobs_precedence() { ); assert_eq!(config.jobs, Some(12345)); - // `--jobs` > `--set build.jobs` > `config.toml` + // `--jobs` > `--set build.jobs` > `bootstrap.toml` let config = Config::parse_inner( Flags::parse(&[ "check".to_owned(), @@ -515,3 +529,459 @@ fn test_explicit_stage() { assert!(!config.explicit_stage_from_config); assert!(!config.is_explicit_stage()); } + +#[test] +fn test_exclude() { + let exclude_path = "compiler"; + let config = parse(&format!("build.exclude=[\"{}\"]", exclude_path)); + + let first_excluded = config + .skip + .first() + .expect("Expected at least one excluded path") + .to_str() + .expect("Failed to convert excluded path to string"); + + assert_eq!(first_excluded, exclude_path); +} + +#[test] +fn test_ci_flag() { + let config = Config::parse_inner(Flags::parse(&["check".into(), "--ci=false".into()]), |&_| { + toml::from_str("") + }); + assert!(!config.is_running_on_ci); + + let config = Config::parse_inner(Flags::parse(&["check".into(), "--ci=true".into()]), |&_| { + toml::from_str("") + }); + assert!(config.is_running_on_ci); + + let config = Config::parse_inner(Flags::parse(&["check".into()]), |&_| toml::from_str("")); + assert_eq!(config.is_running_on_ci, CiEnv::is_ci()); +} + +#[test] +fn test_precedence_of_includes() { + let testdir = prepare_test_specific_dir(); + + let root_config = testdir.join("config.toml"); + let root_config_content = br#" + include = ["./extension.toml"] + + [llvm] + link-jobs = 2 + "#; + File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + + let extension = testdir.join("extension.toml"); + let extension_content = br#" + change-id=543 + include = ["./extension2.toml"] + "#; + File::create(extension).unwrap().write_all(extension_content).unwrap(); + + let extension = testdir.join("extension2.toml"); + let extension_content = br#" + change-id=742 + + [llvm] + link-jobs = 10 + + [build] + description = "Some creative description" + "#; + File::create(extension).unwrap().write_all(extension_content).unwrap(); + + let config = Config::parse_inner( + Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), + get_toml, + ); + + assert_eq!(config.change_id.unwrap(), ChangeId::Id(543)); + assert_eq!(config.llvm_link_jobs.unwrap(), 2); + assert_eq!(config.description.unwrap(), "Some creative description"); +} + +#[test] +#[should_panic(expected = "Cyclic inclusion detected")] +fn test_cyclic_include_direct() { + let testdir = prepare_test_specific_dir(); + + let root_config = testdir.join("config.toml"); + let root_config_content = br#" + include = ["./extension.toml"] + "#; + File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + + let extension = testdir.join("extension.toml"); + let extension_content = br#" + include = ["./config.toml"] + "#; + File::create(extension).unwrap().write_all(extension_content).unwrap(); + + let config = Config::parse_inner( + Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), + get_toml, + ); +} + +#[test] +#[should_panic(expected = "Cyclic inclusion detected")] +fn test_cyclic_include_indirect() { + let testdir = prepare_test_specific_dir(); + + let root_config = testdir.join("config.toml"); + let root_config_content = br#" + include = ["./extension.toml"] + "#; + File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + + let extension = testdir.join("extension.toml"); + let extension_content = br#" + include = ["./extension2.toml"] + "#; + File::create(extension).unwrap().write_all(extension_content).unwrap(); + + let extension = testdir.join("extension2.toml"); + let extension_content = br#" + include = ["./extension3.toml"] + "#; + File::create(extension).unwrap().write_all(extension_content).unwrap(); + + let extension = testdir.join("extension3.toml"); + let extension_content = br#" + include = ["./extension.toml"] + "#; + File::create(extension).unwrap().write_all(extension_content).unwrap(); + + let config = Config::parse_inner( + Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), + get_toml, + ); +} + +#[test] +fn test_include_absolute_paths() { + let testdir = prepare_test_specific_dir(); + + let extension = testdir.join("extension.toml"); + File::create(&extension).unwrap().write_all(&[]).unwrap(); + + let root_config = testdir.join("config.toml"); + let extension_absolute_path = + extension.canonicalize().unwrap().to_str().unwrap().replace('\\', r"\\"); + let root_config_content = format!(r#"include = ["{}"]"#, extension_absolute_path); + File::create(&root_config).unwrap().write_all(root_config_content.as_bytes()).unwrap(); + + let config = Config::parse_inner( + Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), + get_toml, + ); +} + +#[test] +fn test_include_relative_paths() { + let testdir = prepare_test_specific_dir(); + + let _ = fs::create_dir_all(&testdir.join("subdir/another_subdir")); + + let root_config = testdir.join("config.toml"); + let root_config_content = br#" + include = ["./subdir/extension.toml"] + "#; + File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + + let extension = testdir.join("subdir/extension.toml"); + let extension_content = br#" + include = ["../extension2.toml"] + "#; + File::create(extension).unwrap().write_all(extension_content).unwrap(); + + let extension = testdir.join("extension2.toml"); + let extension_content = br#" + include = ["./subdir/another_subdir/extension3.toml"] + "#; + File::create(extension).unwrap().write_all(extension_content).unwrap(); + + let extension = testdir.join("subdir/another_subdir/extension3.toml"); + let extension_content = br#" + include = ["../../extension4.toml"] + "#; + File::create(extension).unwrap().write_all(extension_content).unwrap(); + + let extension = testdir.join("extension4.toml"); + File::create(extension).unwrap().write_all(&[]).unwrap(); + + let config = Config::parse_inner( + Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), + get_toml, + ); +} + +#[test] +fn test_include_precedence_over_profile() { + let testdir = prepare_test_specific_dir(); + + let root_config = testdir.join("config.toml"); + let root_config_content = br#" + profile = "dist" + include = ["./extension.toml"] + "#; + File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + + let extension = testdir.join("extension.toml"); + let extension_content = br#" + [rust] + channel = "dev" + "#; + File::create(extension).unwrap().write_all(extension_content).unwrap(); + + let config = Config::parse_inner( + Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), + get_toml, + ); + + // "dist" profile would normally set the channel to "auto-detect", but includes should + // override profile settings, so we expect this to be "dev" here. + assert_eq!(config.channel, "dev"); +} + +#[test] +fn test_pr_ci_unchanged_anywhere() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.create_nonupstream_merge(&["b"]); + let src = ctx.check_modifications(&["c"], CiEnv::GitHubActions); + assert_eq!(src, PathFreshness::LastModifiedUpstream { upstream: sha }); + }); +} + +#[test] +fn test_pr_ci_changed_in_pr() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.create_nonupstream_merge(&["b"]); + let src = ctx.check_modifications(&["b"], CiEnv::GitHubActions); + assert_eq!(src, PathFreshness::HasLocalModifications { upstream: sha }); + }); +} + +#[test] +fn test_auto_ci_unchanged_anywhere_select_parent() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.create_upstream_merge(&["b"]); + let src = ctx.check_modifications(&["c"], CiEnv::GitHubActions); + assert_eq!(src, PathFreshness::LastModifiedUpstream { upstream: sha }); + }); +} + +#[test] +fn test_auto_ci_changed_in_pr() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.create_upstream_merge(&["b", "c"]); + let src = ctx.check_modifications(&["c", "d"], CiEnv::GitHubActions); + assert_eq!(src, PathFreshness::HasLocalModifications { upstream: sha }); + }); +} + +#[test] +fn test_local_uncommitted_modifications() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.create_branch("feature"); + ctx.modify("a"); + + assert_eq!( + ctx.check_modifications(&["a", "d"], CiEnv::None), + PathFreshness::HasLocalModifications { upstream: sha } + ); + }); +} + +#[test] +fn test_local_committed_modifications() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.create_upstream_merge(&["b", "c"]); + ctx.create_branch("feature"); + ctx.modify("x"); + ctx.commit(); + ctx.modify("a"); + ctx.commit(); + + assert_eq!( + ctx.check_modifications(&["a", "d"], CiEnv::None), + PathFreshness::HasLocalModifications { upstream: sha } + ); + }); +} + +#[test] +fn test_local_committed_modifications_subdirectory() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a/b/c"]); + ctx.create_upstream_merge(&["b", "c"]); + ctx.create_branch("feature"); + ctx.modify("a/b/d"); + ctx.commit(); + + assert_eq!( + ctx.check_modifications(&["a/b"], CiEnv::None), + PathFreshness::HasLocalModifications { upstream: sha } + ); + }); +} + +#[test] +fn test_local_changes_in_head_upstream() { + git_test(|ctx| { + // We want to resolve to the upstream commit that made modifications to a, + // even if it is currently HEAD + let sha = ctx.create_upstream_merge(&["a"]); + assert_eq!( + ctx.check_modifications(&["a", "d"], CiEnv::None), + PathFreshness::LastModifiedUpstream { upstream: sha } + ); + }); +} + +#[test] +fn test_local_changes_in_previous_upstream() { + git_test(|ctx| { + // We want to resolve to this commit, which modified a + let sha = ctx.create_upstream_merge(&["a", "e"]); + // Not to this commit, which is the latest upstream commit + ctx.create_upstream_merge(&["b", "c"]); + ctx.create_branch("feature"); + ctx.modify("d"); + ctx.commit(); + + assert_eq!( + ctx.check_modifications(&["a"], CiEnv::None), + PathFreshness::LastModifiedUpstream { upstream: sha } + ); + }); +} + +#[test] +fn test_local_no_upstream_commit_with_changes() { + git_test(|ctx| { + ctx.create_upstream_merge(&["a", "e"]); + ctx.create_upstream_merge(&["a", "e"]); + // We want to fall back to this commit, because there are no commits + // that modified `x`. + let sha = ctx.create_upstream_merge(&["a", "e"]); + ctx.create_branch("feature"); + ctx.modify("d"); + ctx.commit(); + assert_eq!( + ctx.check_modifications(&["x"], CiEnv::None), + PathFreshness::LastModifiedUpstream { upstream: sha } + ); + }); +} + +#[test] +fn test_local_no_upstream_commit() { + git_test(|ctx| { + let src = ctx.check_modifications(&["c", "d"], CiEnv::None); + assert_eq!(src, PathFreshness::MissingUpstream); + }); +} + +#[test] +fn test_local_changes_negative_path() { + git_test(|ctx| { + let upstream = ctx.create_upstream_merge(&["a"]); + ctx.create_branch("feature"); + ctx.modify("b"); + ctx.modify("d"); + ctx.commit(); + + assert_eq!( + ctx.check_modifications(&[":!b", ":!d"], CiEnv::None), + PathFreshness::LastModifiedUpstream { upstream: upstream.clone() } + ); + assert_eq!( + ctx.check_modifications(&[":!c"], CiEnv::None), + PathFreshness::HasLocalModifications { upstream: upstream.clone() } + ); + assert_eq!( + ctx.check_modifications(&[":!d", ":!x"], CiEnv::None), + PathFreshness::HasLocalModifications { upstream } + ); + }); +} + +#[test] +fn test_local_changes_subtree_that_used_bors() { + // Here we simulate a very specific situation related to subtrees. + // When you have merge commits locally, we should ignore them w.r.t. the artifact download + // logic. + // The upstream search code currently uses a simple heuristic: + // - Find commits by bors (or in general an author with the merge commit e-mail) + // - Find the newest such commit + // This should make it work even for subtrees that: + // - Used bors in the past (so they have bors merge commits in their history). + // - Use Josh to merge rustc into the subtree, in a way that the rustc history is the second + // parent, not the first one. + // + // In addition, when searching for modified files, we cannot simply start from HEAD, because + // in this situation git wouldn't find the right commit. + // + // This test checks that this specific scenario will resolve to the right rustc commit, both + // when finding a modified file and when finding a non-existent file (which essentially means + // that we just lookup the most recent upstream commit). + // + // See https://github.com/rust-lang/rust/issues/101907#issuecomment-2697671282 for more details. + git_test(|ctx| { + ctx.create_upstream_merge(&["a"]); + + // Start unrelated subtree history + ctx.run_git(&["switch", "--orphan", "subtree"]); + ctx.modify("bar"); + ctx.commit(); + // Now we need to emulate old bors commits in the subtree. + // Git only has a resolution of one second, which is a problem, since our git logic orders + // merge commits by their date. + // To avoid sleeping in the test, we modify the commit date to be forcefully in the past. + ctx.create_upstream_merge(&["subtree/a"]); + ctx.run_git(&["commit", "--amend", "--date", "Wed Feb 16 14:00 2011 +0100", "--no-edit"]); + + // Merge the subtree history into rustc + ctx.switch_to_branch("main"); + ctx.run_git(&["merge", "subtree", "--allow-unrelated"]); + + // Create a rustc commit that modifies a path that we're interested in (`x`) + let upstream_1 = ctx.create_upstream_merge(&["x"]); + // Create another bors commit + let upstream_2 = ctx.create_upstream_merge(&["a"]); + + ctx.switch_to_branch("subtree"); + + // Create a subtree branch + ctx.create_branch("subtree-pr"); + ctx.modify("baz"); + ctx.commit(); + // We merge rustc into this branch (simulating a "subtree pull") + ctx.merge("main", "committer "); + + // And then merge that branch into the subtree (simulating a situation right before a + // "subtree push") + ctx.switch_to_branch("subtree"); + ctx.merge("subtree-pr", "committer "); + + // And we want to check that we resolve to the right commits. + assert_eq!( + ctx.check_modifications(&["x"], CiEnv::None), + PathFreshness::LastModifiedUpstream { upstream: upstream_1 } + ); + assert_eq!( + ctx.check_modifications(&["nonexistent"], CiEnv::None), + PathFreshness::LastModifiedUpstream { upstream: upstream_2 } + ); + }); +} diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index c477bdb829a91..e0c9877cd55db 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -6,7 +6,6 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::sync::OnceLock; -use build_helper::ci::CiEnv; use xz2::bufread::XzDecoder; use crate::core::config::BUILDER_CONFIG_FILENAME; @@ -19,7 +18,7 @@ static SHOULD_FIX_BINS_AND_DYLIBS: OnceLock = OnceLock::new(); /// `Config::try_run` wrapper for this module to avoid warnings on `try_run`, since we don't have access to a `builder` yet. fn try_run(config: &Config, cmd: &mut Command) -> Result<(), ()> { - #[allow(deprecated)] + #[expect(deprecated)] config.try_run(cmd) } @@ -58,7 +57,7 @@ impl Config { if self.dry_run() { return; } - fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f)); + fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {f:?}")); } /// Create a temporary directory in `out` and return its path. @@ -113,7 +112,7 @@ impl Config { // The latter one does not exist on NixOS when using tmpfs as root. let is_nixos = match File::open("/etc/os-release") { Err(e) if e.kind() == ErrorKind::NotFound => false, - Err(e) => panic!("failed to access /etc/os-release: {}", e), + Err(e) => panic!("failed to access /etc/os-release: {e}"), Ok(os_release) => BufReader::new(os_release).lines().any(|l| { let l = l.expect("reading /etc/os-release"); matches!(l.trim(), "ID=nixos" | "ID='nixos'" | "ID=\"nixos\"") @@ -124,7 +123,7 @@ impl Config { if let Ok(in_nix_shell) = in_nix_shell { eprintln!( "The IN_NIX_SHELL environment variable is `{in_nix_shell}`; \ - you may need to set `patch-binaries-for-nix=true` in config.toml" + you may need to set `patch-binaries-for-nix=true` in bootstrap.toml" ); } } @@ -262,7 +261,7 @@ impl Config { "--fail", ]); // Don't print progress in CI; the \r wrapping looks bad and downloads don't take long enough for progress to be useful. - if CiEnv::is_ci() { + if self.is_running_on_ci { curl.arg("--silent"); } else { curl.arg("--progress-bar"); @@ -418,7 +417,7 @@ enum DownloadSource { Dist, } -/// Functions that are only ever called once, but named for clarify and to avoid thousand-line functions. +/// Functions that are only ever called once, but named for clarity and to avoid thousand-line functions. impl Config { pub(crate) fn download_clippy(&self) -> PathBuf { self.verbose(|| println!("downloading stage0 clippy artifacts")); @@ -447,7 +446,7 @@ impl Config { #[cfg(test)] pub(crate) fn maybe_download_rustfmt(&self) -> Option { - None + Some(PathBuf::new()) } /// NOTE: rustfmt is a completely different toolchain than the bootstrap compiler, so it can't @@ -456,6 +455,10 @@ impl Config { pub(crate) fn maybe_download_rustfmt(&self) -> Option { use build_helper::stage0_parser::VersionMetadata; + if self.dry_run() { + return Some(PathBuf::new()); + } + let VersionMetadata { date, version } = self.stage0_metadata.rustfmt.as_ref()?; let channel = format!("{version}-{date}"); @@ -699,7 +702,7 @@ impl Config { help_on_error = "ERROR: failed to download pre-built rustc from CI NOTE: old builds get deleted after a certain time -HELP: if trying to compile an old commit of rustc, disable `download-rustc` in config.toml: +HELP: if trying to compile an old commit of rustc, disable `download-rustc` in bootstrap.toml: [rust] download-rustc = false @@ -721,8 +724,9 @@ download-rustc = false #[cfg(not(test))] pub(crate) fn maybe_download_ci_llvm(&self) { use build_helper::exit; + use build_helper::git::PathFreshness; - use crate::core::build_steps::llvm::detect_llvm_sha; + use crate::core::build_steps::llvm::detect_llvm_freshness; use crate::core::config::check_incompatible_options_for_ci_llvm; if !self.llvm_from_ci { @@ -730,7 +734,22 @@ download-rustc = false } let llvm_root = self.ci_llvm_root(); - let llvm_sha = detect_llvm_sha(self, self.rust_info.is_managed_git_subrepository()); + let llvm_freshness = + detect_llvm_freshness(self, self.rust_info.is_managed_git_subrepository()); + self.verbose(|| { + eprintln!("LLVM freshness: {llvm_freshness:?}"); + }); + let llvm_sha = match llvm_freshness { + PathFreshness::LastModifiedUpstream { upstream } => upstream, + PathFreshness::HasLocalModifications { upstream } => upstream, + PathFreshness::MissingUpstream => { + eprintln!("error: could not find commit hash for downloading LLVM"); + eprintln!("HELP: maybe your repository history is too shallow?"); + eprintln!("HELP: consider disabling `download-ci-llvm`"); + eprintln!("HELP: or fetch enough history to include one upstream commit"); + crate::exit!(1); + } + }; let stamp_key = format!("{}{}", llvm_sha, self.llvm_assertions); let llvm_stamp = BuildStamp::new(&llvm_root).with_prefix("llvm").add_stamp(stamp_key); if !llvm_stamp.is_up_to_date() && !self.dry_run() { @@ -783,7 +802,7 @@ download-rustc = false println!("HELP: Consider rebasing to a newer commit if available."); } Err(e) => { - eprintln!("ERROR: Failed to parse CI LLVM config.toml: {e}"); + eprintln!("ERROR: Failed to parse CI LLVM bootstrap.toml: {e}"); exit!(2); } }; @@ -816,7 +835,7 @@ download-rustc = false HELP: There could be two reasons behind this: 1) The host triple is not supported for `download-ci-llvm`. 2) Old builds get deleted after a certain time. - HELP: In either case, disable `download-ci-llvm` in your config.toml: + HELP: In either case, disable `download-ci-llvm` in your bootstrap.toml: [llvm] download-ci-llvm = false @@ -826,6 +845,35 @@ download-rustc = false let llvm_root = self.ci_llvm_root(); self.unpack(&tarball, &llvm_root, "rust-dev"); } + + pub fn download_ci_gcc(&self, gcc_sha: &str, root_dir: &Path) { + let cache_prefix = format!("gcc-{gcc_sha}"); + let cache_dst = + self.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| self.out.join("cache")); + + let gcc_cache = cache_dst.join(cache_prefix); + if !gcc_cache.exists() { + t!(fs::create_dir_all(&gcc_cache)); + } + let base = &self.stage0_metadata.config.artifacts_server; + let version = self.artifact_version_part(gcc_sha); + let filename = format!("gcc-{version}-{}.tar.xz", self.build.triple); + let tarball = gcc_cache.join(&filename); + if !tarball.exists() { + let help_on_error = "ERROR: failed to download gcc from ci + + HELP: There could be two reasons behind this: + 1) The host triple is not supported for `download-ci-gcc`. + 2) Old builds get deleted after a certain time. + HELP: In either case, disable `download-ci-gcc` in your bootstrap.toml: + + [gcc] + download-ci-gcc = false + "; + self.download_file(&format!("{base}/{gcc_sha}/{filename}"), &tarball, help_on_error); + } + self.unpack(&tarball, root_dir, "gcc"); + } } fn path_is_dylib(path: &Path) -> bool { diff --git a/src/bootstrap/src/core/metadata.rs b/src/bootstrap/src/core/metadata.rs index 01cbf6629408d..2706aba5ffc8d 100644 --- a/src/bootstrap/src/core/metadata.rs +++ b/src/bootstrap/src/core/metadata.rs @@ -28,7 +28,6 @@ struct Package { source: Option, manifest_path: String, dependencies: Vec, - targets: Vec, features: BTreeMap>, } @@ -40,11 +39,6 @@ struct Dependency { source: Option, } -#[derive(Debug, Deserialize)] -struct Target { - kind: Vec, -} - /// Collects and stores package metadata of each workspace members into `build`, /// by executing `cargo metadata` commands. pub fn build(build: &mut Build) { @@ -59,12 +53,10 @@ pub fn build(build: &mut Build) { .filter(|dep| dep.source.is_none()) .map(|dep| dep.name) .collect(); - let has_lib = package.targets.iter().any(|t| t.kind.iter().any(|k| k == "lib")); let krate = Crate { name: name.clone(), deps, path, - has_lib, features: package.features.keys().cloned().collect(), }; let relative_path = krate.local_path(build); diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index fdac7f3cb179d..af4ec679d080d 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -13,7 +13,6 @@ use std::ffi::{OsStr, OsString}; use std::path::PathBuf; use std::{env, fs}; -use crate::Build; #[cfg(not(test))] use crate::builder::Builder; use crate::builder::Kind; @@ -21,6 +20,7 @@ use crate::builder::Kind; use crate::core::build_steps::tool; use crate::core::config::Target; use crate::utils::exec::command; +use crate::{Build, Subcommand}; pub struct Finder { cache: HashMap>, @@ -117,8 +117,8 @@ pub fn check(build: &mut Build) { eprintln!( "\nYour system's libstdc++ version is too old for the `llvm.download-ci-llvm` option." ); - eprintln!("Current version detected: '{}'", version); - eprintln!("Minimum required version: '{}'", LIBSTDCXX_MIN_VERSION_THRESHOLD); + eprintln!("Current version detected: '{version}'"); + eprintln!("Minimum required version: '{LIBSTDCXX_MIN_VERSION_THRESHOLD}'"); eprintln!( "Consider upgrading libstdc++ or disabling the `llvm.download-ci-llvm` option." ); @@ -152,7 +152,7 @@ pub fn check(build: &mut Build) { Couldn't find required command: cmake You should install cmake, or set `download-ci-llvm = true` in the -`[llvm]` section of `config.toml` to download LLVM rather +`[llvm]` section of `bootstrap.toml` to download LLVM rather than building it. " ); @@ -205,6 +205,20 @@ than building it. .map(|s| s.to_string()) .collect(); + // Compiler tools like `cc` and `ar` are not configured for cross-targets on certain subcommands + // because they are not needed. + // + // See `cc_detect::find` for more details. + let skip_tools_checks = build.config.dry_run() + || matches!( + build.config.cmd, + Subcommand::Clean { .. } + | Subcommand::Check { .. } + | Subcommand::Suggest { .. } + | Subcommand::Format { .. } + | Subcommand::Setup { .. } + ); + // We're gonna build some custom C code here and there, host triples // also build some C++ shims for LLVM so we need a C++ compiler. for target in &build.targets { @@ -278,7 +292,7 @@ than building it. } } - if !build.config.dry_run() { + if !skip_tools_checks { cmd_finder.must_have(build.cc(*target)); if let Some(ar) = build.ar(*target) { cmd_finder.must_have(ar); @@ -286,7 +300,7 @@ than building it. } } - if !build.config.dry_run() { + if !skip_tools_checks { for host in &build.hosts { cmd_finder.must_have(build.cxx(*host).unwrap()); @@ -325,7 +339,7 @@ than building it. if target.contains("musl") && !target.contains("unikraft") { // If this is a native target (host is also musl) and no musl-root is given, // fall back to the system toolchain in /usr before giving up - if build.musl_root(*target).is_none() && build.is_builder_target(*target) { + if build.musl_root(*target).is_none() && build.config.is_host_target(*target) { let target = build.config.target_config.entry(*target).or_default(); target.musl_root = Some("/usr".into()); } @@ -338,7 +352,7 @@ than building it. None => panic!( "when targeting MUSL either the rust.musl-root \ option or the target.$TARGET.musl-root option must \ - be specified in config.toml" + be specified in bootstrap.toml" ), } } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 351e67f6702d1..9492ffaed7566 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -15,6 +15,7 @@ //! //! 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. +#![cfg_attr(test, allow(unused))] use std::cell::{Cell, RefCell}; use std::collections::{BTreeSet, HashMap, HashSet}; @@ -27,28 +28,32 @@ use std::{env, fs, io, str}; use build_helper::ci::gha; use build_helper::exit; +use cc::Tool; use termcolor::{ColorChoice, StandardStream, WriteColor}; use utils::build_stamp::BuildStamp; use utils::channel::GitInfo; use crate::core::builder; use crate::core::builder::Kind; -use crate::core::config::{DryRun, LldMode, LlvmLibunwind, Target, TargetSelection, flags}; +use crate::core::config::{DryRun, LldMode, LlvmLibunwind, TargetSelection, flags}; use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode, command}; -use crate::utils::helpers::{self, dir_is_empty, exe, libdir, output, set_file_times, symlink_dir}; +use crate::utils::helpers::{ + self, dir_is_empty, exe, libdir, output, set_file_times, split_debuginfo, symlink_dir, +}; mod core; mod utils; pub use core::builder::PathSet; -pub use core::config::Config; pub use core::config::flags::{Flags, Subcommand}; +pub use core::config::{ChangeId, Config}; #[cfg(feature = "tracing")] use tracing::{instrument, span}; pub use utils::change_tracker::{ CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes, }; +pub use utils::helpers::PanicTracker; use crate::core::build_steps::vendor::VENDOR_DIR; @@ -74,7 +79,7 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"]; /// Extra `--check-cfg` to add when building the compiler or tools /// (Mode restriction, config name, config values (if any)) -#[allow(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above. +#[expect(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above. const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ (None, "bootstrap", None), (Some(Mode::Rustc), "llvm_enzyme", None), @@ -92,10 +97,27 @@ const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ /// Each compiler has a `stage` that it is associated with and a `host` that /// corresponds to the platform the compiler runs on. This structure is used as /// a parameter to many methods below. -#[derive(Eq, PartialOrd, Ord, PartialEq, Clone, Copy, Hash, Debug)] +#[derive(Eq, PartialOrd, Ord, Clone, Copy, Debug)] pub struct Compiler { stage: u32, host: TargetSelection, + /// Indicates whether the compiler was forced to use a specific stage. + /// This field is ignored in `Hash` and `PartialEq` implementations as only the `stage` + /// and `host` fields are relevant for those. + forced_compiler: bool, +} + +impl std::hash::Hash for Compiler { + fn hash(&self, state: &mut H) { + self.stage.hash(state); + self.host.hash(state); + } +} + +impl PartialEq for Compiler { + fn eq(&self, other: &Self) -> bool { + self.stage == other.stage && self.host == other.host + } } #[derive(PartialEq, Eq, Copy, Clone, Debug)] @@ -125,7 +147,7 @@ pub enum GitRepo { /// organize). #[derive(Clone)] pub struct Build { - /// User-specified configuration from `config.toml`. + /// User-specified configuration from `bootstrap.toml`. config: Config, // Version information @@ -185,7 +207,6 @@ struct Crate { name: String, deps: HashSet, path: PathBuf, - has_lib: bool, features: Vec, } @@ -237,7 +258,7 @@ pub enum Mode { /// Build a tool which uses the locally built rustc and the target std, /// placing the output in the "stageN-tools" directory. This is used for /// anything that needs a fully functional rustc, such as rustdoc, clippy, - /// cargo, rls, rustfmt, miri, etc. + /// cargo, rustfmt, miri, etc. ToolRustc, } @@ -257,6 +278,35 @@ pub enum CLang { Cxx, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FileType { + /// An executable binary file (like a `.exe`). + Executable, + /// A native, binary library file (like a `.so`, `.dll`, `.a`, `.lib` or `.o`). + NativeLibrary, + /// An executable (non-binary) script file (like a `.py` or `.sh`). + Script, + /// Any other regular file that is non-executable. + Regular, +} + +impl FileType { + /// Get Unix permissions appropriate for this file type. + pub fn perms(self) -> u32 { + match self { + FileType::Executable | FileType::Script => 0o755, + FileType::Regular | FileType::NativeLibrary => 0o644, + } + } + + pub fn could_have_split_debuginfo(self) -> bool { + match self { + FileType::Executable | FileType::NativeLibrary => true, + FileType::Script | FileType::Regular => false, + } + } +} + macro_rules! forward { ( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => { impl Build { @@ -275,7 +325,6 @@ forward! { tempdir() -> PathBuf, llvm_link_shared() -> bool, download_rustc() -> bool, - initial_rustfmt() -> Option, } impl Build { @@ -521,7 +570,7 @@ impl Build { /// This avoids contributors checking in a submodule change by accident. fn update_existing_submodules(&self) { // Avoid running git when there isn't a git checkout, or the user has - // explicitly disabled submodules in `config.toml`. + // explicitly disabled submodules in `bootstrap.toml`. if !self.config.submodules() { return; } @@ -564,10 +613,6 @@ impl Build { crate::utils::job::setup(self); } - // Download rustfmt early so that it can be used in rust-analyzer configs. - trace!("downloading rustfmt early"); - let _ = &builder::Builder::new(self).initial_rustfmt(); - // Handle hard-coded subcommands. { #[cfg(feature = "tracing")] @@ -654,7 +699,7 @@ impl Build { } /// Gets the space-separated set of activated features for the standard library. - /// This can be configured with the `std-features` key in config.toml. + /// This can be configured with the `std-features` key in bootstrap.toml. fn std_features(&self, target: TargetSelection) -> String { let mut features: BTreeSet<&str> = self.config.rust_std_features.iter().map(|s| s.as_str()).collect(); @@ -699,7 +744,7 @@ impl Build { features.push("llvm"); } // keep in sync with `bootstrap/compile.rs:rustc_cargo_env` - if self.config.rust_randomize_layout { + if self.config.rust_randomize_layout && check("rustc_randomized_layouts") { features.push("rustc_randomized_layouts"); } @@ -754,7 +799,7 @@ impl Build { /// Note that if LLVM is configured externally then the directory returned /// will likely be empty. fn llvm_out(&self, target: TargetSelection) -> PathBuf { - if self.config.llvm_from_ci && self.is_builder_target(target) { + if self.config.llvm_from_ci && self.config.is_host_target(target) { self.config.ci_llvm_root() } else { self.out.join(target).join("llvm") @@ -802,37 +847,6 @@ impl Build { if self.config.vendor { Some(self.src.join(VENDOR_DIR)) } else { None } } - /// Returns `true` if this is an external version of LLVM not managed by bootstrap. - /// In particular, we expect llvm sources to be available when this is false. - /// - /// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. - fn is_system_llvm(&self, target: TargetSelection) -> bool { - match self.config.target_config.get(&target) { - Some(Target { llvm_config: Some(_), .. }) => { - let ci_llvm = self.config.llvm_from_ci && self.is_builder_target(target); - !ci_llvm - } - // We're building from the in-tree src/llvm-project sources. - Some(Target { llvm_config: None, .. }) => false, - None => false, - } - } - - /// Returns `true` if this is our custom, patched, version of LLVM. - /// - /// This does not necessarily imply that we're managing the `llvm-project` submodule. - fn is_rust_llvm(&self, target: TargetSelection) -> bool { - match self.config.target_config.get(&target) { - // We're using a user-controlled version of LLVM. The user has explicitly told us whether the version has our patches. - // (They might be wrong, but that's not a supported use-case.) - // In particular, this tries to support `submodules = false` and `patches = false`, for using a newer version of LLVM that's not through `rust-lang/llvm-project`. - Some(Target { llvm_has_rust_patches: Some(patched), .. }) => *patched, - // The user hasn't promised the patches match. - // This only has our patches if it's downloaded from CI or built from source. - _ => !self.is_system_llvm(target), - } - } - /// Returns the path to `FileCheck` binary for the specified target fn llvm_filecheck(&self, target: TargetSelection) -> PathBuf { let target_config = self.config.target_config.get(&target); @@ -1202,6 +1216,16 @@ Executed at: {executed_at}"#, self.cc.borrow()[&target].path().into() } + /// Returns the internal `cc::Tool` for the C compiler. + fn cc_tool(&self, target: TargetSelection) -> Tool { + self.cc.borrow()[&target].clone() + } + + /// Returns the internal `cc::Tool` for the C++ compiler. + fn cxx_tool(&self, target: TargetSelection) -> Tool { + self.cxx.borrow()[&target].clone() + } + /// Returns C flags that `cc-rs` thinks should be enabled for the /// specified target by default. fn cc_handled_clags(&self, target: TargetSelection, c: CLang) -> Vec { @@ -1297,7 +1321,7 @@ Executed at: {executed_at}"#, // need to use CXX compiler as linker to resolve the exception functions // that are only existed in CXX libraries Some(self.cxx.borrow()[&target].path().into()) - } else if !self.is_builder_target(target) + } else if !self.config.is_host_target(target) && helpers::use_host_linker(target) && !target.is_msvc() { @@ -1498,7 +1522,7 @@ Executed at: {executed_at}"#, !self.config.full_bootstrap && !self.config.download_rustc() && stage >= 2 - && (self.hosts.iter().any(|h| *h == target) || target == self.build) + && (self.hosts.contains(&target) || target == self.build) } /// Checks whether the `compiler` compiling for `target` should be forced to @@ -1717,8 +1741,18 @@ Executed at: {executed_at}"#, /// Attempts to use hard links if possible, falling back to copying. /// You can neither rely on this being a copy nor it being a link, /// so do not write to dst. - pub fn copy_link(&self, src: &Path, dst: &Path) { + pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) { self.copy_link_internal(src, dst, false); + + if file_type.could_have_split_debuginfo() { + if let Some(dbg_file) = split_debuginfo(src) { + self.copy_link_internal( + &dbg_file, + &dst.with_extension(dbg_file.extension().unwrap()), + false, + ); + } + } } fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) { @@ -1781,7 +1815,7 @@ Executed at: {executed_at}"#, t!(fs::create_dir_all(&dst)); self.cp_link_r(&path, &dst); } else { - self.copy_link(&path, &dst); + self.copy_link(&path, &dst, FileType::Regular); } } } @@ -1817,7 +1851,7 @@ Executed at: {executed_at}"#, self.cp_link_filtered_recurse(&path, &dst, &relative, filter); } else { let _ = fs::remove_file(&dst); - self.copy_link(&path, &dst); + self.copy_link(&path, &dst, FileType::Regular); } } } @@ -1826,10 +1860,10 @@ Executed at: {executed_at}"#, fn copy_link_to_folder(&self, src: &Path, dest_folder: &Path) { let file_name = src.file_name().unwrap(); let dest = dest_folder.join(file_name); - self.copy_link(src, &dest); + self.copy_link(src, &dest, FileType::Regular); } - fn install(&self, src: &Path, dstdir: &Path, perms: u32) { + fn install(&self, src: &Path, dstdir: &Path, file_type: FileType) { if self.config.dry_run() { return; } @@ -1839,8 +1873,16 @@ Executed at: {executed_at}"#, if !src.exists() { panic!("ERROR: File \"{}\" not found!", src.display()); } + self.copy_link_internal(src, &dst, true); - chmod(&dst, perms); + chmod(&dst, file_type.perms()); + + // If this file can have debuginfo, look for split debuginfo and install it too. + if file_type.could_have_split_debuginfo() { + if let Some(dbg_file) = split_debuginfo(src) { + self.install(&dbg_file, dstdir, FileType::Regular); + } + } } fn read(&self, path: &Path) -> String { @@ -1898,7 +1940,7 @@ Couldn't find required command: ninja (or ninja-build) You should install ninja as described at , -or set `ninja = false` in the `[llvm]` section of `config.toml`. +or set `ninja = false` in the `[llvm]` section of `bootstrap.toml`. Alternatively, set `download-ci-llvm = true` in that `[llvm]` section to download LLVM rather than building it. " @@ -1948,11 +1990,6 @@ to download LLVM rather than building it. stream.reset().unwrap(); result } - - /// Checks if the given target is the same as the builder target. - fn is_builder_target(&self, target: TargetSelection) -> bool { - self.config.build == target - } } #[cfg(unix)] @@ -1964,6 +2001,14 @@ fn chmod(path: &Path, perms: u32) { fn chmod(_path: &Path, _perms: u32) {} impl Compiler { + pub fn new(stage: u32, host: TargetSelection) -> Self { + Self { stage, host, forced_compiler: false } + } + + pub fn forced_compiler(&mut self, forced_compiler: bool) { + self.forced_compiler = forced_compiler; + } + pub fn with_stage(mut self, stage: u32) -> Compiler { self.stage = stage; self @@ -1973,6 +2018,11 @@ impl Compiler { pub fn is_snapshot(&self, build: &Build) -> bool { self.stage == 0 && self.host == build.build } + + /// Indicates whether the compiler was forced to use a specific stage. + pub fn is_forced_compiler(&self) -> bool { + self.forced_compiler + } } fn envify(s: &str) -> String { diff --git a/src/bootstrap/src/utils/cache.rs b/src/bootstrap/src/utils/cache.rs index 1c8cc4025df11..46eeffad88c38 100644 --- a/src/bootstrap/src/utils/cache.rs +++ b/src/bootstrap/src/utils/cache.rs @@ -20,7 +20,6 @@ use std::collections::HashMap; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::ops::Deref; -use std::path::PathBuf; use std::sync::{LazyLock, Mutex}; use std::{fmt, mem}; @@ -51,26 +50,11 @@ impl PartialEq for Interned { } impl Eq for Interned {} -impl PartialEq for Interned { - fn eq(&self, other: &str) -> bool { - *self == other - } -} impl PartialEq<&str> for Interned { fn eq(&self, other: &&str) -> bool { **self == **other } } -impl PartialEq<&Interned> for Interned { - fn eq(&self, other: &&Self) -> bool { - self.0 == other.0 - } -} -impl PartialEq> for &Interned { - fn eq(&self, other: &Interned) -> bool { - self.0 == other.0 - } -} unsafe impl Send for Interned {} unsafe impl Sync for Interned {} @@ -188,8 +172,6 @@ impl TyIntern { #[derive(Default)] pub struct Interner { strs: Mutex>, - paths: Mutex>, - lists: Mutex>>, } /// Defines the behavior required for a type to be internable. @@ -210,18 +192,6 @@ impl Internable for String { } } -impl Internable for PathBuf { - fn intern_cache() -> &'static Mutex> { - &INTERNER.paths - } -} - -impl Internable for Vec { - fn intern_cache() -> &'static Mutex> { - &INTERNER.lists - } -} - impl Interner { /// Interns a string reference, ensuring it is stored uniquely. /// diff --git a/src/bootstrap/src/utils/cache/tests.rs b/src/bootstrap/src/utils/cache/tests.rs index 28f5563a589b1..8562a35b3e06b 100644 --- a/src/bootstrap/src/utils/cache/tests.rs +++ b/src/bootstrap/src/utils/cache/tests.rs @@ -12,26 +12,6 @@ fn test_string_interning() { assert_ne!(s1, s3, "Different strings should have different interned values"); } -#[test] -fn test_path_interning() { - let p1 = PathBuf::from("/tmp/file").intern(); - let p2 = PathBuf::from("/tmp/file").intern(); - let p3 = PathBuf::from("/tmp/other").intern(); - - assert_eq!(p1, p2); - assert_ne!(p1, p3); -} - -#[test] -fn test_vec_interning() { - let v1 = vec!["a".to_string(), "b".to_string()].intern(); - let v2 = vec!["a".to_string(), "b".to_string()].intern(); - let v3 = vec!["c".to_string()].intern(); - - assert_eq!(v1, v2); - assert_ne!(v1, v3); -} - #[test] fn test_interned_equality() { let s1 = INTERNER.intern_str("test"); diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index 1e84a7deff12e..5c9e30706ee0d 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -4,8 +4,8 @@ //! C and C++ compilers for each target configured. A compiler is found through //! a number of vectors (in order of precedence) //! -//! 1. Configuration via `target.$target.cc` in `config.toml`. -//! 2. Configuration via `target.$target.android-ndk` in `config.toml`, if +//! 1. Configuration via `target.$target.cc` in `bootstrap.toml`. +//! 2. Configuration via `target.$target.android-ndk` in `bootstrap.toml`, if //! applicable //! 3. Special logic to probe on OpenBSD //! 4. The `CC_$target` environment variable. @@ -22,43 +22,13 @@ //! everything. use std::collections::HashSet; +use std::iter; use std::path::{Path, PathBuf}; -use std::{env, iter}; use crate::core::config::TargetSelection; use crate::utils::exec::{BootstrapCommand, command}; use crate::{Build, CLang, GitRepo}; -/// Finds archiver tool for the given target if possible. -/// FIXME(onur-ozkan): This logic should be replaced by calling into the `cc` crate. -fn cc2ar(cc: &Path, target: TargetSelection, default_ar: PathBuf) -> Option { - if let Some(ar) = env::var_os(format!("AR_{}", target.triple.replace('-', "_"))) { - Some(PathBuf::from(ar)) - } else if let Some(ar) = env::var_os("AR") { - Some(PathBuf::from(ar)) - } else if target.is_msvc() { - None - } else if target.contains("musl") || target.contains("openbsd") { - Some(PathBuf::from("ar")) - } else if target.contains("vxworks") { - Some(PathBuf::from("wr-ar")) - } else if target.contains("-nto-") { - if target.starts_with("i586") { - Some(PathBuf::from("ntox86-ar")) - } else if target.starts_with("aarch64") { - Some(PathBuf::from("ntoaarch64-ar")) - } else if target.starts_with("x86_64") { - Some(PathBuf::from("ntox86_64-ar")) - } else { - panic!("Unknown architecture, cannot determine archiver for Neutrino QNX"); - } - } else if target.contains("android") || target.contains("-wasi") { - Some(cc.parent().unwrap().join(PathBuf::from("llvm-ar"))) - } else { - Some(default_ar) - } -} - /// Creates and configures a new [`cc::Build`] instance for the given target. fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build { let mut cfg = cc::Build::new(); @@ -96,6 +66,7 @@ pub fn find(build: &Build) { let targets: HashSet<_> = match build.config.cmd { // We don't need to check cross targets for these commands. crate::Subcommand::Clean { .. } + | crate::Subcommand::Check { .. } | crate::Subcommand::Suggest { .. } | crate::Subcommand::Format { .. } | crate::Subcommand::Setup { .. } => { @@ -122,7 +93,7 @@ pub fn find(build: &Build) { /// Probes and configures the C and C++ compilers for a single target. /// -/// This function uses both user-specified configuration (from `config.toml`) and auto-detection +/// This function uses both user-specified configuration (from `bootstrap.toml`) and auto-detection /// logic to determine the correct C/C++ compilers for the target. It also determines the appropriate /// archiver (`ar`) and sets up additional compilation flags (both handled and unhandled). pub fn find_target(build: &Build, target: TargetSelection) { @@ -139,7 +110,7 @@ pub fn find_target(build: &Build, target: TargetSelection) { let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) { ar } else { - cc2ar(compiler.path(), target, PathBuf::from(cfg.get_archiver().get_program())) + cfg.try_get_archiver().map(|c| PathBuf::from(c.get_program())).ok() }; build.cc.borrow_mut().insert(target, compiler.clone()); @@ -186,7 +157,7 @@ pub fn find_target(build: &Build, target: TargetSelection) { } /// Determines the default compiler for a given target and language when not explicitly -/// configured in `config.toml`. +/// configured in `bootstrap.toml`. fn default_compiler( cfg: &mut cc::Build, compiler: Language, @@ -195,7 +166,7 @@ fn default_compiler( ) -> Option { match &*target.triple { // When compiling for android we may have the NDK configured in the - // config.toml in which case we look there. Otherwise the default + // bootstrap.toml in which case we look there. Otherwise the default // compiler already takes into account the triple in question. t if t.contains("android") => { build.config.android_ndk.as_ref().map(|ndk| ndk_compiler(compiler, &target.triple, ndk)) diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs index 006dfe7e5d7b3..225fb7619b55a 100644 --- a/src/bootstrap/src/utils/cc_detect/tests.rs +++ b/src/bootstrap/src/utils/cc_detect/tests.rs @@ -5,103 +5,6 @@ use super::*; use crate::core::config::{Target, TargetSelection}; use crate::{Build, Config, Flags}; -#[test] -fn test_cc2ar_env_specific() { - let triple = "x86_64-unknown-linux-gnu"; - let key = "AR_x86_64_unknown_linux_gnu"; - env::set_var(key, "custom-ar"); - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - env::remove_var(key); - assert_eq!(result, Some(PathBuf::from("custom-ar"))); -} - -#[test] -fn test_cc2ar_musl() { - let triple = "x86_64-unknown-linux-musl"; - env::remove_var("AR_x86_64_unknown_linux_musl"); - env::remove_var("AR"); - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ar"))); -} - -#[test] -fn test_cc2ar_openbsd() { - let triple = "x86_64-unknown-openbsd"; - env::remove_var("AR_x86_64_unknown_openbsd"); - env::remove_var("AR"); - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/cc"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ar"))); -} - -#[test] -fn test_cc2ar_vxworks() { - let triple = "armv7-wrs-vxworks"; - env::remove_var("AR_armv7_wrs_vxworks"); - env::remove_var("AR"); - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("wr-ar"))); -} - -#[test] -fn test_cc2ar_nto_i586() { - let triple = "i586-unknown-nto-something"; - env::remove_var("AR_i586_unknown_nto_something"); - env::remove_var("AR"); - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ntox86-ar"))); -} - -#[test] -fn test_cc2ar_nto_aarch64() { - let triple = "aarch64-unknown-nto-something"; - env::remove_var("AR_aarch64_unknown_nto_something"); - env::remove_var("AR"); - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ntoaarch64-ar"))); -} - -#[test] -fn test_cc2ar_nto_x86_64() { - let triple = "x86_64-unknown-nto-something"; - env::remove_var("AR_x86_64_unknown_nto_something"); - env::remove_var("AR"); - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let result = cc2ar(cc, target, default_ar); - assert_eq!(result, Some(PathBuf::from("ntox86_64-ar"))); -} - -#[test] -#[should_panic(expected = "Unknown architecture, cannot determine archiver for Neutrino QNX")] -fn test_cc2ar_nto_unknown() { - let triple = "powerpc-unknown-nto-something"; - env::remove_var("AR_powerpc_unknown_nto_something"); - env::remove_var("AR"); - let target = TargetSelection::from_user(triple); - let cc = Path::new("/usr/bin/clang"); - let default_ar = PathBuf::from("default-ar"); - let _ = cc2ar(cc, target, default_ar); -} - #[test] fn test_ndk_compiler_c() { let ndk_path = PathBuf::from("/ndk"); @@ -165,7 +68,7 @@ fn test_language_clang() { #[test] fn test_new_cc_build() { - let build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) }); + let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) }); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let cfg = new_cc_build(&build, target.clone()); let compiler = cfg.get_compiler(); @@ -174,10 +77,11 @@ fn test_new_cc_build() { #[test] fn test_default_compiler_wasi() { - let build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) }); + let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) }); let target = TargetSelection::from_user("wasm32-wasi"); let wasi_sdk = PathBuf::from("/wasi-sdk"); - env::set_var("WASI_SDK_PATH", &wasi_sdk); + // SAFETY: bootstrap tests run on a single thread + unsafe { env::set_var("WASI_SDK_PATH", &wasi_sdk) }; let mut cfg = cc::Build::new(); if let Some(result) = default_compiler(&mut cfg, Language::C, target.clone(), &build) { let expected = { @@ -190,12 +94,15 @@ fn test_default_compiler_wasi() { "default_compiler should return a compiler path for wasi target when WASI_SDK_PATH is set" ); } - env::remove_var("WASI_SDK_PATH"); + // SAFETY: bootstrap tests run on a single thread + unsafe { + env::remove_var("WASI_SDK_PATH"); + } } #[test] fn test_default_compiler_fallback() { - let build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) }); + let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) }); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let mut cfg = cc::Build::new(); let result = default_compiler(&mut cfg, Language::C, target, &build); @@ -204,7 +111,7 @@ fn test_default_compiler_fallback() { #[test] fn test_find_target_with_config() { - let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) }); + let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) }); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let mut target_config = Target::default(); target_config.cc = Some(PathBuf::from("dummy-cc")); @@ -229,7 +136,7 @@ fn test_find_target_with_config() { #[test] fn test_find_target_without_config() { - let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) }); + let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) }); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); build.config.target_config.clear(); find_target(&build, target.clone()); @@ -242,9 +149,9 @@ fn test_find_target_without_config() { #[test] fn test_find() { - let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) }); + let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) }); let target1 = TargetSelection::from_user("x86_64-unknown-linux-gnu"); - let target2 = TargetSelection::from_user("arm-linux-androideabi"); + let target2 = TargetSelection::from_user("x86_64-unknown-openbsd"); build.targets.push(target1.clone()); build.hosts.push(target2.clone()); find(&build); diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 5f49c50c5ad3a..1d0ea3ebf6105 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -35,29 +35,25 @@ impl Display for ChangeSeverity { } } -pub fn find_recent_config_change_ids(current_id: usize) -> Vec { - if !CONFIG_CHANGE_HISTORY.iter().any(|config| config.change_id == current_id) { +pub fn find_recent_config_change_ids(current_id: usize) -> &'static [ChangeInfo] { + if let Some(index) = + CONFIG_CHANGE_HISTORY.iter().position(|config| config.change_id == current_id) + { + // Skip the current_id and IDs before it + &CONFIG_CHANGE_HISTORY[index + 1..] + } else { // If the current change-id is greater than the most recent one, return // an empty list (it may be due to switching from a recent branch to an // older one); otherwise, return the full list (assuming the user provided // the incorrect change-id by accident). if let Some(config) = CONFIG_CHANGE_HISTORY.iter().max_by_key(|config| config.change_id) { if current_id > config.change_id { - return Vec::new(); + return &[]; } } - return CONFIG_CHANGE_HISTORY.to_vec(); + CONFIG_CHANGE_HISTORY } - - let index = - CONFIG_CHANGE_HISTORY.iter().position(|config| config.change_id == current_id).unwrap(); - - CONFIG_CHANGE_HISTORY - .iter() - .skip(index + 1) // Skip the current_id and IDs before it - .cloned() - .collect() } pub fn human_readable_changes(changes: &[ChangeInfo]) -> String { @@ -365,4 +361,54 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "`rust.channel` now supports \"auto-detect\" to load the channel from `src/ci/channel`", }, + ChangeInfo { + change_id: 137723, + severity: ChangeSeverity::Info, + summary: "The rust.description option has moved to build.description and rust.description is now deprecated.", + }, + ChangeInfo { + change_id: 138051, + severity: ChangeSeverity::Info, + summary: "There is now a new `gcc` config section that can be used to download GCC from CI using `gcc.download-ci-gcc = true`", + }, + ChangeInfo { + change_id: 126856, + severity: ChangeSeverity::Warning, + summary: "Removed `src/tools/rls` tool as it was deprecated long time ago.", + }, + ChangeInfo { + change_id: 137147, + severity: ChangeSeverity::Info, + summary: "Added new option `build.exclude` which works the same way as `--exclude` flag on `x`.", + }, + ChangeInfo { + change_id: 137081, + severity: ChangeSeverity::Warning, + summary: "The default configuration filename has changed from `config.toml` to `bootstrap.toml`. `config.toml` is deprecated but remains supported for backward compatibility.", + }, + ChangeInfo { + change_id: 138986, + severity: ChangeSeverity::Info, + summary: "You can now use `change-id = \"ignore\"` to suppress `change-id ` warnings in the console.", + }, + ChangeInfo { + change_id: 139386, + severity: ChangeSeverity::Info, + summary: "Added a new option `build.compiletest-use-stage0-libtest` to force `compiletest` to use the stage 0 libtest.", + }, + ChangeInfo { + change_id: 138934, + severity: ChangeSeverity::Info, + summary: "Added new option `include` to create config extensions.", + }, + ChangeInfo { + change_id: 140438, + severity: ChangeSeverity::Info, + summary: "Added a new option `rust.debug-assertions-tools` to control debug asssertions for tools.", + }, + ChangeInfo { + change_id: 140732, + severity: ChangeSeverity::Info, + summary: "`./x run` now supports running in-tree `rustfmt`, e.g., `./x run rustfmt -- --check /path/to/file.rs`.", + }, ]; diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 7eb9ab96c8a4a..d07300e21d003 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -125,7 +125,7 @@ impl BootstrapCommand { Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self } } - #[allow(dead_code)] + #[expect(dead_code)] pub fn fail_fast(self) -> Self { Self { failure_behavior: BehaviorOnFailure::Exit, ..self } } @@ -280,7 +280,7 @@ impl CommandOutput { !self.is_success() } - #[allow(dead_code)] + #[expect(dead_code)] pub fn status(&self) -> Option { match self.status { CommandStatus::Finished(status) => Some(status), @@ -332,7 +332,6 @@ impl Default for CommandOutput { /// Helper trait to format both Command and BootstrapCommand as a short execution line, /// without all the other details (e.g. environment variables). -#[allow(unused)] pub trait FormatShortCmd { fn format_short_cmd(&self) -> String; } diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 7ad308cd06728..f2c3e8c0df464 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -7,8 +7,9 @@ use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::sync::OnceLock; +use std::thread::panicking; use std::time::{Instant, SystemTime, UNIX_EPOCH}; -use std::{env, fs, io, str}; +use std::{env, fs, io, panic, str}; use build_helper::util::fail; use object::read::archive::ArchiveFile; @@ -22,6 +23,23 @@ pub use crate::utils::shared_helpers::{dylib_path, dylib_path_var}; #[cfg(test)] mod tests; +/// A wrapper around `std::panic::Location` used to track the location of panics +/// triggered by `t` macro usage. +pub struct PanicTracker<'a>(pub &'a panic::Location<'a>); + +impl Drop for PanicTracker<'_> { + fn drop(&mut self) { + if panicking() { + eprintln!( + "Panic was initiated from {}:{}:{}", + self.0.file(), + self.0.line(), + self.0.column() + ); + } + } +} + /// A helper macro to `unwrap` a result except also print out details like: /// /// * The file/line of the panic @@ -32,19 +50,21 @@ mod tests; /// using a `Result` with `try!`, but this may change one day... #[macro_export] macro_rules! t { - ($e:expr) => { + ($e:expr) => {{ + let _panic_guard = $crate::PanicTracker(std::panic::Location::caller()); match $e { Ok(e) => e, Err(e) => panic!("{} failed with {}", stringify!($e), e), } - }; + }}; // it can show extra info in the second parameter - ($e:expr, $extra:expr) => { + ($e:expr, $extra:expr) => {{ + let _panic_guard = $crate::PanicTracker(std::panic::Location::caller()); match $e { Ok(e) => e, Err(e) => panic!("{} failed with {} ({:?})", stringify!($e), e, $extra), } - }; + }}; } pub use t; @@ -52,6 +72,23 @@ pub fn exe(name: &str, target: TargetSelection) -> String { crate::utils::shared_helpers::exe(name, &target.triple) } +/// Returns the path to the split debug info for the specified file if it exists. +pub fn split_debuginfo(name: impl Into) -> Option { + // FIXME: only msvc is currently supported + + let path = name.into(); + let pdb = path.with_extension("pdb"); + if pdb.exists() { + return Some(pdb); + } + + // pdbs get named with '-' replaced by '_' + let file_name = pdb.file_name()?.to_str()?.replace("-", "_"); + + let pdb: PathBuf = [path.parent()?, Path::new(&file_name)].into_iter().collect(); + pdb.exists().then_some(pdb) +} + /// Returns `true` if the file name given looks like a dynamic library. pub fn is_dylib(path: &Path) -> bool { path.extension().and_then(|ext| ext.to_str()).is_some_and(|ext| { @@ -93,7 +130,7 @@ pub fn is_debug_info(name: &str) -> bool { /// Returns the corresponding relative library directory that the compiler's /// dylibs will be found in. pub fn libdir(target: TargetSelection) -> &'static str { - if target.is_windows() { "bin" } else { "lib" } + if target.is_windows() || target.contains("cygwin") { "bin" } else { "lib" } } /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path. @@ -286,13 +323,13 @@ pub fn output(cmd: &mut Command) -> String { /// to finish and then return its output. This allows the spawned process /// to do work without immediately blocking bootstrap. #[track_caller] -pub fn start_process(cmd: &mut Command) -> impl FnOnce() -> String { +pub fn start_process(cmd: &mut Command) -> impl FnOnce() -> String + use<> { let child = match cmd.stderr(Stdio::inherit()).stdout(Stdio::piped()).spawn() { Ok(child) => child, Err(e) => fail(&format!("failed to execute command: {cmd:?}\nERROR: {e}")), }; - let command = format!("{:?}", cmd); + let command = format!("{cmd:?}"); move || { let output = child.wait_with_output().unwrap(); @@ -415,9 +452,7 @@ pub fn dir_is_empty(dir: &Path) -> bool { /// the "y" part from the string. pub fn extract_beta_rev(version: &str) -> Option { let parts = version.splitn(2, "-beta.").collect::>(); - let count = parts.get(1).and_then(|s| s.find(' ').map(|p| s[..p].to_string())); - - count + parts.get(1).and_then(|s| s.find(' ').map(|p| s[..p].to_string())) } pub enum LldThreads { @@ -430,9 +465,8 @@ pub fn linker_args( builder: &Builder<'_>, target: TargetSelection, lld_threads: LldThreads, - stage: u32, ) -> Vec { - let mut args = linker_flags(builder, target, lld_threads, stage); + let mut args = linker_flags(builder, target, lld_threads); if let Some(linker) = builder.linker(target) { args.push(format!("-Clinker={}", linker.display())); @@ -447,23 +481,17 @@ pub fn linker_flags( builder: &Builder<'_>, target: TargetSelection, lld_threads: LldThreads, - stage: u32, ) -> Vec { let mut args = vec![]; if !builder.is_lld_direct_linker(target) && builder.config.lld_mode.is_used() { match builder.config.lld_mode { LldMode::External => { - // cfg(bootstrap) - remove after updating bootstrap compiler (#137498) - if stage == 0 && target.is_windows() { - args.push("-Clink-arg=-fuse-ld=lld".to_string()); - } else { - args.push("-Clinker-flavor=gnu-lld-cc".to_string()); - } + args.push("-Zlinker-features=+lld".to_string()); // FIXME(kobzol): remove this flag once MCP510 gets stabilized args.push("-Zunstable-options".to_string()); } LldMode::SelfContained => { - args.push("-Clinker-flavor=gnu-lld-cc".to_string()); + args.push("-Zlinker-features=+lld".to_string()); args.push("-Clink-self-contained=+linker".to_string()); // FIXME(kobzol): remove this flag once MCP510 gets stabilized args.push("-Zunstable-options".to_string()); @@ -486,9 +514,8 @@ pub fn add_rustdoc_cargo_linker_args( builder: &Builder<'_>, target: TargetSelection, lld_threads: LldThreads, - stage: u32, ) { - let args = linker_args(builder, target, lld_threads, stage); + let args = linker_args(builder, target, lld_threads); let mut flags = cmd .get_envs() .find_map(|(k, v)| if k == OsStr::new("RUSTDOCFLAGS") { v } else { None }) @@ -513,7 +540,7 @@ where use std::fmt::Write; input.as_ref().iter().fold(String::with_capacity(input.as_ref().len() * 2), |mut acc, &byte| { - write!(&mut acc, "{:02x}", byte).expect("Failed to write byte to the hex String."); + write!(&mut acc, "{byte:02x}").expect("Failed to write byte to the hex String."); acc }) } diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs index 10efed130d624..887deb41ca8bc 100644 --- a/src/bootstrap/src/utils/job.rs +++ b/src/bootstrap/src/utils/job.rs @@ -7,7 +7,9 @@ pub unsafe fn setup(_build: &mut crate::Build) {} #[cfg(all(unix, not(target_os = "haiku")))] pub unsafe fn setup(build: &mut crate::Build) { if build.config.low_priority { - libc::setpriority(libc::PRIO_PGRP as _, 0, 10); + unsafe { + libc::setpriority(libc::PRIO_PGRP as _, 0, 10); + } } } @@ -42,7 +44,7 @@ pub unsafe fn setup(build: &mut crate::Build) { #[cfg(windows)] mod for_windows { use std::ffi::c_void; - use std::{io, mem}; + use std::io; use windows::Win32::Foundation::CloseHandle; use windows::Win32::System::Diagnostics::Debug::{ @@ -59,38 +61,40 @@ mod for_windows { use crate::Build; pub unsafe fn setup(build: &mut Build) { - // Enable the Windows Error Reporting dialog which msys disables, - // so we can JIT debug rustc - let mode = SetErrorMode(THREAD_ERROR_MODE::default()); - let mode = THREAD_ERROR_MODE(mode); - SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX); + // SAFETY: pretty much everything below is unsafe + unsafe { + // Enable the Windows Error Reporting dialog which msys disables, + // so we can JIT debug rustc + let mode = SetErrorMode(THREAD_ERROR_MODE::default()); + SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX); - // Create a new job object for us to use - let job = CreateJobObjectW(None, PCWSTR::null()).unwrap(); + // Create a new job object for us to use + let job = CreateJobObjectW(None, PCWSTR::null()).unwrap(); - // Indicate that when all handles to the job object are gone that all - // process in the object should be killed. Note that this includes our - // entire process tree by default because we've added ourselves and our - // children will reside in the job by default. - let mut info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default(); - info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; - if build.config.low_priority { - info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS; - info.BasicLimitInformation.PriorityClass = BELOW_NORMAL_PRIORITY_CLASS.0; - } - let r = SetInformationJobObject( - job, - JobObjectExtendedLimitInformation, - &info as *const _ as *const c_void, - mem::size_of_val(&info) as u32, - ); - assert!(r.is_ok(), "{}", io::Error::last_os_error()); + // Indicate that when all handles to the job object are gone that all + // process in the object should be killed. Note that this includes our + // entire process tree by default because we've added ourselves and our + // children will reside in the job by default. + let mut info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default(); + info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + if build.config.low_priority { + info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS; + info.BasicLimitInformation.PriorityClass = BELOW_NORMAL_PRIORITY_CLASS.0; + } + let r = SetInformationJobObject( + job, + JobObjectExtendedLimitInformation, + &info as *const _ as *const c_void, + size_of_val(&info) as u32, + ); + assert!(r.is_ok(), "{}", io::Error::last_os_error()); - // Assign our process to this job object. - let r = AssignProcessToJobObject(job, GetCurrentProcess()); - if r.is_err() { - CloseHandle(job).ok(); - return; + // Assign our process to this job object. + let r = AssignProcessToJobObject(job, GetCurrentProcess()); + if r.is_err() { + CloseHandle(job).ok(); + return; + } } // Note: we intentionally leak the job object handle. When our process exits diff --git a/src/bootstrap/src/utils/metrics.rs b/src/bootstrap/src/utils/metrics.rs index b51fd490535a3..862c444962415 100644 --- a/src/bootstrap/src/utils/metrics.rs +++ b/src/bootstrap/src/utils/metrics.rs @@ -9,9 +9,10 @@ use std::fs::File; use std::io::BufWriter; use std::time::{Duration, Instant, SystemTime}; +use build_helper::ci::CiEnv; use build_helper::metrics::{ - JsonInvocation, JsonInvocationSystemStats, JsonNode, JsonRoot, JsonStepSystemStats, Test, - TestOutcome, TestSuite, TestSuiteMetadata, + CiMetadata, JsonInvocation, JsonInvocationSystemStats, JsonNode, JsonRoot, JsonStepSystemStats, + Test, TestOutcome, TestSuite, TestSuiteMetadata, }; use sysinfo::{CpuRefreshKind, RefreshKind, System}; @@ -76,7 +77,7 @@ impl BuildMetrics { // Consider all the stats gathered so far as the parent's. if !state.running_steps.is_empty() { - self.collect_stats(&mut *state); + self.collect_stats(&mut state); } state.system_info.refresh_cpu_usage(); @@ -102,7 +103,7 @@ impl BuildMetrics { let mut state = self.state.borrow_mut(); - self.collect_stats(&mut *state); + self.collect_stats(&mut state); let step = state.running_steps.pop().unwrap(); if state.running_steps.is_empty() { @@ -200,6 +201,14 @@ impl BuildMetrics { } }; invocations.push(JsonInvocation { + // The command-line invocation with which bootstrap was invoked. + // Skip the first argument, as it is a potentially long absolute + // path that is not interesting. + cmdline: std::env::args_os() + .skip(1) + .map(|arg| arg.to_string_lossy().to_string()) + .collect::>() + .join(" "), start_time: state .invocation_start .duration_since(SystemTime::UNIX_EPOCH) @@ -209,13 +218,19 @@ impl BuildMetrics { children: steps.into_iter().map(|step| self.prepare_json_step(step)).collect(), }); - let json = JsonRoot { format_version: CURRENT_FORMAT_VERSION, system_stats, invocations }; + let json = JsonRoot { + format_version: CURRENT_FORMAT_VERSION, + system_stats, + invocations, + ci_metadata: get_ci_metadata(CiEnv::current()), + }; t!(std::fs::create_dir_all(dest.parent().unwrap())); let mut file = BufWriter::new(t!(File::create(&dest))); t!(serde_json::to_writer(&mut file, &json)); } + #[expect(clippy::only_used_in_recursion)] fn prepare_json_step(&self, step: StepMetrics) -> JsonNode { let mut children = Vec::new(); children.extend(step.children.into_iter().map(|child| self.prepare_json_step(child))); @@ -236,6 +251,16 @@ impl BuildMetrics { } } +fn get_ci_metadata(ci_env: CiEnv) -> Option { + if ci_env != CiEnv::GitHubActions { + return None; + } + let workflow_run_id = + std::env::var("GITHUB_WORKFLOW_RUN_ID").ok().and_then(|id| id.parse::().ok())?; + let repository = std::env::var("GITHUB_REPOSITORY").ok()?; + Some(CiMetadata { workflow_run_id, repository }) +} + struct MetricsState { finished_steps: Vec, running_steps: Vec, diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs index caef8ce3088a7..169fcec303e90 100644 --- a/src/bootstrap/src/utils/mod.rs +++ b/src/bootstrap/src/utils/mod.rs @@ -20,4 +20,4 @@ pub(crate) mod tracing; pub(crate) mod metrics; #[cfg(test)] -mod tests; +pub(crate) mod tests; diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index 34bf6bb7013ff..b61fa3bb8d690 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -5,50 +5,61 @@ pub static CRATES: &[&str] = &[ // tidy-alphabetical-start "annotate-snippets", "anstyle", + "askama_parser", "basic-toml", + "block-buffer", "bumpalo", + "cfg-if", + "cpufeatures", + "crypto-common", "darling", "darling_core", "derive_builder_core", + "digest", "fluent-bundle", "fluent-langneg", "fluent-syntax", "fnv", + "generic-array", "heck", "ident_case", "intl-memoizer", "intl_pluralrules", + "libc", "log", "memchr", - "mime", - "mime_guess", "minimal-lexical", "nom", - "num-conv", + "once_cell", + "pest", + "pest_generator", + "pest_meta", "proc-macro2", "quote", - "rinja_parser", "rustc-hash", "self_cell", "serde", + "sha2", "smallvec", "stable_deref_trait", "strsim", "syn", "synstructure", "thiserror", - "time-core", "tinystr", "type-map", + "typenum", + "ucd-trie", "unic-langid", "unic-langid-impl", "unic-langid-macros", - "unicase", "unicode-ident", "unicode-width", + "version_check", "wasm-bindgen-backend", "wasm-bindgen-macro-support", "wasm-bindgen-shared", + "winnow", "yoke", "zerofrom", "zerovec", diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 3f58328a5b594..418f3ff975d94 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -10,7 +10,6 @@ use std::io::{BufRead, BufReader, Read, Write}; use std::process::{ChildStdout, Stdio}; use std::time::Duration; -use build_helper::ci::CiEnv; use termcolor::{Color, ColorSpec, WriteColor}; use crate::core::builder::Builder; @@ -187,7 +186,7 @@ impl<'a> Renderer<'a> { if self.builder.config.verbose_tests { self.render_test_outcome_verbose(outcome, test); - } else if CiEnv::is_ci() { + } else if self.builder.config.is_running_on_ci { self.render_test_outcome_ci(outcome, test); } else { self.render_test_outcome_terse(outcome, test); diff --git a/src/bootstrap/src/utils/shared_helpers.rs b/src/bootstrap/src/utils/shared_helpers.rs index 7b206c3ffe826..08e1c21e58e7c 100644 --- a/src/bootstrap/src/utils/shared_helpers.rs +++ b/src/bootstrap/src/utils/shared_helpers.rs @@ -20,7 +20,7 @@ use std::str::FromStr; /// Returns the environment variable which the dynamic library lookup path /// resides in for this platform. pub fn dylib_path_var() -> &'static str { - if cfg!(target_os = "windows") { + if cfg!(any(target_os = "windows", target_os = "cygwin")) { "PATH" } else if cfg!(target_vendor = "apple") { "DYLD_LIBRARY_PATH" @@ -90,7 +90,7 @@ pub fn maybe_dump(dump_name: String, cmd: &Command) { let mut file = OpenOptions::new().create(true).append(true).open(dump_file).unwrap(); - let cmd_dump = format!("{:?}\n", cmd); + let cmd_dump = format!("{cmd:?}\n"); let cmd_dump = cmd_dump.replace(&env::var("BUILD_OUT").unwrap(), "${BUILD_OUT}"); let cmd_dump = cmd_dump.replace(&env::var("CARGO_HOME").unwrap(), "${CARGO_HOME}"); diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index 843ea65e83862..7b77b21293413 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -7,6 +7,7 @@ use std::path::{Path, PathBuf}; +use crate::FileType; use crate::core::build_steps::dist::distdir; use crate::core::builder::{Builder, Kind}; use crate::core::config::BUILDER_CONFIG_FILENAME; @@ -22,7 +23,6 @@ pub(crate) enum OverlayKind { Clippy, Miri, Rustfmt, - Rls, RustAnalyzer, RustcCodegenCranelift, LlvmBitcodeLinker, @@ -56,7 +56,6 @@ impl OverlayKind { "src/tools/rustfmt/LICENSE-APACHE", "src/tools/rustfmt/LICENSE-MIT", ], - OverlayKind::Rls => &["src/tools/rls/README.md", "LICENSE-APACHE", "LICENSE-MIT"], OverlayKind::RustAnalyzer => &[ "src/tools/rust-analyzer/README.md", "src/tools/rust-analyzer/LICENSE-APACHE", @@ -90,7 +89,6 @@ impl OverlayKind { OverlayKind::Rustfmt => { builder.rustfmt_info.version(builder, &builder.release_num("rustfmt")) } - OverlayKind::Rls => builder.release(&builder.release_num("rls")), OverlayKind::RustAnalyzer => builder .rust_analyzer_info .version(builder, &builder.release_num("rust-analyzer/crates/rust-analyzer")), @@ -185,7 +183,12 @@ impl<'a> Tarball<'a> { &self.image_dir } - pub(crate) fn add_file(&self, src: impl AsRef, destdir: impl AsRef, perms: u32) { + pub(crate) fn add_file( + &self, + src: impl AsRef, + destdir: impl AsRef, + file_type: FileType, + ) { // create_dir_all fails to create `foo/bar/.`, so when the destination is "." this simply // uses the base directory as the destination directory. let destdir = if destdir.as_ref() == Path::new(".") { @@ -195,7 +198,7 @@ impl<'a> Tarball<'a> { }; t!(std::fs::create_dir_all(&destdir)); - self.builder.install(src.as_ref(), &destdir, perms); + self.builder.install(src.as_ref(), &destdir, file_type); } pub(crate) fn add_renamed_file( @@ -203,15 +206,16 @@ impl<'a> Tarball<'a> { src: impl AsRef, destdir: impl AsRef, new_name: &str, + file_type: FileType, ) { let destdir = self.image_dir.join(destdir.as_ref()); t!(std::fs::create_dir_all(&destdir)); - self.builder.copy_link(src.as_ref(), &destdir.join(new_name)); + self.builder.copy_link(src.as_ref(), &destdir.join(new_name), file_type); } pub(crate) fn add_legal_and_readme_to(&self, destdir: impl AsRef) { for file in self.overlay.legal_and_readme() { - self.add_file(self.builder.src.join(file), destdir.as_ref(), 0o644); + self.add_file(self.builder.src.join(file), destdir.as_ref(), FileType::Regular); } } @@ -321,11 +325,20 @@ impl<'a> Tarball<'a> { // Add config file if present. if let Some(config) = &self.builder.config.config { - self.add_renamed_file(config, &self.overlay_dir, BUILDER_CONFIG_FILENAME); + self.add_renamed_file( + config, + &self.overlay_dir, + BUILDER_CONFIG_FILENAME, + FileType::Regular, + ); } for file in self.overlay.legal_and_readme() { - self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644); + self.builder.install( + &self.builder.src.join(file), + &self.overlay_dir, + FileType::Regular, + ); } let mut cmd = self.builder.tool_cmd(crate::core::build_steps::tool::Tool::RustInstaller); diff --git a/src/bootstrap/src/utils/tests/git.rs b/src/bootstrap/src/utils/tests/git.rs new file mode 100644 index 0000000000000..d9dd9ab980088 --- /dev/null +++ b/src/bootstrap/src/utils/tests/git.rs @@ -0,0 +1,154 @@ +use std::ffi::OsStr; +use std::fs::OpenOptions; +use std::path::Path; +use std::process::Command; + +use build_helper::ci::CiEnv; +use build_helper::git::{GitConfig, PathFreshness, check_path_modifications}; + +pub struct GitCtx { + dir: tempfile::TempDir, + pub git_repo: String, + pub nightly_branch: String, + pub merge_bot_email: String, +} + +impl GitCtx { + fn new() -> Self { + let dir = tempfile::TempDir::new().unwrap(); + let ctx = Self { + dir, + git_repo: "rust-lang/rust".to_string(), + nightly_branch: "nightly".to_string(), + merge_bot_email: "Merge bot ".to_string(), + }; + ctx.run_git(&["init"]); + ctx.run_git(&["config", "user.name", "Tester"]); + ctx.run_git(&["config", "user.email", "tester@rust-lang.org"]); + ctx.modify("README.md"); + ctx.commit(); + ctx.run_git(&["branch", "-m", "main"]); + ctx + } + + pub fn get_path(&self) -> &Path { + self.dir.path() + } + + pub fn check_modifications(&self, target_paths: &[&str], ci_env: CiEnv) -> PathFreshness { + check_path_modifications(self.dir.path(), &self.git_config(), target_paths, ci_env).unwrap() + } + + pub fn create_upstream_merge(&self, modified_files: &[&str]) -> String { + self.create_branch_and_merge("previous-pr", modified_files, &self.merge_bot_email) + } + + pub fn create_nonupstream_merge(&self, modified_files: &[&str]) -> String { + self.create_branch_and_merge("pr", modified_files, "Tester ") + } + + pub fn create_branch_and_merge( + &self, + branch: &str, + modified_files: &[&str], + author: &str, + ) -> String { + let current_branch = self.get_current_branch(); + + self.create_branch(branch); + for file in modified_files { + self.modify(file); + } + self.commit(); + self.switch_to_branch(¤t_branch); + self.merge(branch, author); + self.run_git(&["branch", "-d", branch]); + self.get_current_commit() + } + + pub fn get_current_commit(&self) -> String { + self.run_git(&["rev-parse", "HEAD"]) + } + + pub fn get_current_branch(&self) -> String { + self.run_git(&["rev-parse", "--abbrev-ref", "HEAD"]) + } + + pub fn merge(&self, branch: &str, author: &str) { + self.run_git(&["merge", "--no-commit", "--no-ff", branch]); + self.run_git(&[ + "commit".to_string(), + "-m".to_string(), + format!("Merge of {branch} into {}", self.get_current_branch()), + "--author".to_string(), + author.to_string(), + ]); + } + + pub fn modify(&self, path: &str) { + self.write(path, "line"); + } + + pub fn write(&self, path: &str, data: &str) { + use std::io::Write; + + let path = self.dir.path().join(path); + std::fs::create_dir_all(&path.parent().unwrap()).unwrap(); + + let mut file = OpenOptions::new().create(true).append(true).open(path).unwrap(); + writeln!(file, "{data}").unwrap(); + } + + pub fn commit(&self) -> String { + self.run_git(&["add", "."]); + self.run_git(&["commit", "-m", "commit message"]); + self.get_current_commit() + } + + pub fn switch_to_branch(&self, name: &str) { + self.run_git(&["switch", name]); + } + + /// Creates a branch and switches to it. + pub fn create_branch(&self, name: &str) { + self.run_git(&["checkout", "-b", name]); + } + + pub fn run_git>(&self, args: &[S]) -> String { + let mut cmd = self.git_cmd(); + cmd.args(args); + eprintln!("Running {cmd:?}"); + let output = cmd.output().unwrap(); + let stdout = String::from_utf8(output.stdout).unwrap().trim().to_string(); + let stderr = String::from_utf8(output.stderr).unwrap().trim().to_string(); + if !output.status.success() { + panic!("Git command `{cmd:?}` failed\nStdout\n{stdout}\nStderr\n{stderr}"); + } + stdout + } + + fn git_cmd(&self) -> Command { + let mut cmd = Command::new("git"); + cmd.env("GIT_CONFIG_NOSYSTEM", "1"); + cmd.env("GIT_CONFIG_SYSTEM", "/tmp/nonexistent"); + cmd.env("GIT_CONFIG_GLOBAL", "/tmp/nonexistent"); + cmd.current_dir(&self.dir); + cmd + } + + fn git_config(&self) -> GitConfig<'_> { + GitConfig { + nightly_branch: &self.nightly_branch, + git_merge_commit_email: &self.merge_bot_email, + } + } +} + +/// Run an end-to-end test that allows testing git logic. +pub fn git_test(test_fn: F) +where + F: FnOnce(&mut GitCtx), +{ + let mut ctx = GitCtx::new(); + test_fn(&mut ctx); +} diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index 0791f7a6e2074..73d55db994cc0 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -1 +1,2 @@ +pub mod git; mod shared_helpers_tests; diff --git a/src/build_helper/src/fs/mod.rs b/src/build_helper/src/fs/mod.rs index 02029846fd147..123df76e6a2e9 100644 --- a/src/build_helper/src/fs/mod.rs +++ b/src/build_helper/src/fs/mod.rs @@ -22,21 +22,27 @@ where /// A wrapper around [`std::fs::remove_dir_all`] that can also be used on *non-directory entries*, /// including files and symbolic links. /// -/// - This will produce an error if the target path is not found. +/// - This will not produce an error if the target path is not found. /// - Like [`std::fs::remove_dir_all`], this helper does not traverse symbolic links, will remove /// symbolic link itself. /// - This helper is **not** robust against races on the underlying filesystem, behavior is /// unspecified if this helper is called concurrently. /// - This helper is not robust against TOCTOU problems. /// -/// FIXME: this implementation is insufficiently robust to replace bootstrap's clean `rm_rf` -/// implementation: -/// -/// - This implementation currently does not perform retries. +/// FIXME: Audit whether this implementation is robust enough to replace bootstrap's clean `rm_rf`. #[track_caller] pub fn recursive_remove>(path: P) -> io::Result<()> { let path = path.as_ref(); - let metadata = fs::symlink_metadata(path)?; + + // If the path doesn't exist, we treat it as a successful no-op. + // From the caller's perspective, the goal is simply "ensure this file/dir is gone" — + // if it's already not there, that's a success, not an error. + let metadata = match fs::symlink_metadata(path) { + Ok(m) => m, + Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()), + Err(e) => return Err(e), + }; + #[cfg(windows)] let is_dir_like = |meta: &fs::Metadata| { use std::os::windows::fs::FileTypeExt; @@ -45,11 +51,35 @@ pub fn recursive_remove>(path: P) -> io::Result<()> { #[cfg(not(windows))] let is_dir_like = fs::Metadata::is_dir; - if is_dir_like(&metadata) { - fs::remove_dir_all(path) - } else { - try_remove_op_set_perms(fs::remove_file, path, metadata) + const MAX_RETRIES: usize = 5; + const RETRY_DELAY_MS: u64 = 100; + + let try_remove = || { + if is_dir_like(&metadata) { + fs::remove_dir_all(path) + } else { + try_remove_op_set_perms(fs::remove_file, path, metadata.clone()) + } + }; + + // Retry deletion a few times to handle transient filesystem errors. + // This is unusual for local file operations, but it's a mitigation + // against unlikely events where malware scanners may be holding a + // file beyond our control, to give the malware scanners some opportunity + // to release their hold. + for attempt in 0..MAX_RETRIES { + match try_remove() { + Ok(()) => return Ok(()), + Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()), + Err(_) if attempt < MAX_RETRIES - 1 => { + std::thread::sleep(std::time::Duration::from_millis(RETRY_DELAY_MS)); + continue; + } + Err(e) => return Err(e), + } } + + Ok(()) } fn try_remove_op_set_perms<'p, Op>(mut op: Op, path: &'p Path, metadata: Metadata) -> io::Result<()> @@ -67,3 +97,9 @@ where Err(e) => Err(e), } } + +pub fn remove_and_create_dir_all>(path: P) -> io::Result<()> { + let path = path.as_ref(); + recursive_remove(path)?; + fs::create_dir_all(path) +} diff --git a/src/build_helper/src/fs/tests.rs b/src/build_helper/src/fs/tests.rs index 1e694393127cb..7ce1d8928d1cb 100644 --- a/src/build_helper/src/fs/tests.rs +++ b/src/build_helper/src/fs/tests.rs @@ -14,7 +14,7 @@ mod recursive_remove_tests { let tmpdir = env::temp_dir(); let path = tmpdir.join("__INTERNAL_BOOTSTRAP_nonexistent_path"); assert!(fs::symlink_metadata(&path).is_err_and(|e| e.kind() == io::ErrorKind::NotFound)); - assert!(recursive_remove(&path).is_err_and(|e| e.kind() == io::ErrorKind::NotFound)); + assert!(recursive_remove(&path).is_ok()); } #[test] diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index 3ef9c7ac35e9d..438cd14389c1c 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -1,10 +1,10 @@ -use std::path::{Path, PathBuf}; +use std::path::Path; use std::process::{Command, Stdio}; use crate::ci::CiEnv; +#[derive(Debug)] pub struct GitConfig<'a> { - pub git_repository: &'a str, pub nightly_branch: &'a str, pub git_merge_commit_email: &'a str, } @@ -27,145 +27,234 @@ pub fn output_result(cmd: &mut Command) -> Result { String::from_utf8(output.stdout).map_err(|err| format!("{err:?}")) } -/// Finds the remote for rust-lang/rust. -/// For example for these remotes it will return `upstream`. -/// ```text -/// origin https://github.com/pietroalbani/rust.git (fetch) -/// origin https://github.com/pietroalbani/rust.git (push) -/// upstream https://github.com/rust-lang/rust (fetch) -/// upstream https://github.com/rust-lang/rust (push) -/// ``` -pub fn get_rust_lang_rust_remote( +/// Represents the result of checking whether a set of paths +/// have been modified locally or not. +#[derive(PartialEq, Debug, Clone)] +pub enum PathFreshness { + /// Artifacts should be downloaded from this upstream commit, + /// there are no local modifications. + LastModifiedUpstream { upstream: String }, + /// There are local modifications to a certain set of paths. + /// "Local" essentially means "not-upstream" here. + /// `upstream` is the latest upstream merge commit that made modifications to the + /// set of paths. + HasLocalModifications { upstream: String }, + /// No upstream commit was found. + /// This should not happen in most reasonable circumstances, but one never knows. + MissingUpstream, +} + +/// This function figures out if a set of paths was last modified upstream or +/// if there are some local modifications made to them. +/// It can be used to figure out if we should download artifacts from CI or rather +/// build them locally. +/// +/// The function assumes that at least a single upstream bors merge commit is in the +/// local git history. +/// +/// `target_paths` should be a non-empty slice of paths (git `pathspec`s) relative to `git_dir` +/// whose modifications would invalidate the artifact. +/// Each pathspec can also be a negative match, i.e. `:!foo`. This matches changes outside +/// the `foo` directory. +/// See +/// for how git `pathspec` works. +/// +/// The function behaves differently in CI and outside CI. +/// +/// - Outside CI, we want to find out if `target_paths` were modified in some local commit on +/// top of the latest upstream commit that is available in local git history. +/// If not, we try to find the most recent upstream commit (which we assume are commits +/// made by bors) that modified `target_paths`. +/// We don't want to simply take the latest master commit to avoid changing the output of +/// this function frequently after rebasing on the latest master branch even if `target_paths` +/// were not modified upstream in the meantime. In that case we would be redownloading CI +/// artifacts unnecessarily. +/// +/// - In CI, we use a shallow clone of depth 2, i.e., we fetch only a single parent commit +/// (which will be the most recent bors merge commit) and do not have access +/// to the full git history. Luckily, we only need to distinguish between two situations: +/// 1) The current PR made modifications to `target_paths`. +/// In that case, a build is typically necessary. +/// 2) The current PR did not make modifications to `target_paths`. +/// In that case we simply take the latest upstream commit, because on CI there is no need to avoid +/// redownloading. +pub fn check_path_modifications( + git_dir: &Path, config: &GitConfig<'_>, - git_dir: Option<&Path>, -) -> Result { - let mut git = Command::new("git"); - if let Some(git_dir) = git_dir { - git.current_dir(git_dir); + target_paths: &[&str], + ci_env: CiEnv, +) -> Result { + assert!(!target_paths.is_empty()); + for path in target_paths { + assert!(Path::new(path.trim_start_matches(":!")).is_relative()); } - git.args(["config", "--local", "--get-regex", "remote\\..*\\.url"]); - let stdout = output_result(&mut git)?; - let rust_lang_remote = stdout - .lines() - .find(|remote| remote.contains(config.git_repository)) - .ok_or_else(|| format!("{} remote not found", config.git_repository))?; + let upstream_sha = if matches!(ci_env, CiEnv::GitHubActions) { + // Here the situation is different for PR CI and try/auto CI. + // For PR CI, we have the following history: + // + // 1-N PR commits + // upstream merge commit made by bors + // + // For try/auto CI, we have the following history: + // <**non-upstream** merge commit made by bors> + // 1-N PR commits + // upstream merge commit made by bors + // + // But on both cases, HEAD should be a merge commit. + // So if HEAD contains modifications of `target_paths`, our PR has modified + // them. If not, we can use the only available upstream commit for downloading + // artifacts. - let remote_name = - rust_lang_remote.split('.').nth(1).ok_or_else(|| "remote name not found".to_owned())?; - Ok(remote_name.into()) -} + // Do not include HEAD, as it is never an upstream commit + // If we do not find an upstream commit in CI, something is seriously wrong. + Some( + get_closest_upstream_commit(Some(git_dir), config, ci_env)? + .expect("No upstream commit was found on CI"), + ) + } else { + // Outside CI, we want to find the most recent upstream commit that + // modified the set of paths, to have an upstream reference that does not change + // unnecessarily often. + // However, if such commit is not found, we can fall back to the latest upstream commit + let upstream_with_modifications = + get_latest_upstream_commit_that_modified_files(git_dir, config, target_paths)?; + match upstream_with_modifications { + Some(sha) => Some(sha), + None => get_closest_upstream_commit(Some(git_dir), config, ci_env)?, + } + }; -pub fn rev_exists(rev: &str, git_dir: Option<&Path>) -> Result { - let mut git = Command::new("git"); - if let Some(git_dir) = git_dir { - git.current_dir(git_dir); - } - git.args(["rev-parse", rev]); - let output = git.output().map_err(|err| format!("{err:?}"))?; - - match output.status.code() { - Some(0) => Ok(true), - Some(128) => Ok(false), - None => Err(format!( - "git didn't exit properly: {}", - String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? - )), - Some(code) => Err(format!( - "git command exited with status code: {code}: {}", - String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? - )), + let Some(upstream_sha) = upstream_sha else { + return Ok(PathFreshness::MissingUpstream); + }; + + // For local environments, we want to find out if something has changed + // from the latest upstream commit. + // However, that should be equivalent to checking if something has changed + // from the latest upstream commit *that modified `target_paths`*, and + // with this approach we do not need to invoke git an additional time. + if has_changed_since(git_dir, &upstream_sha, target_paths) { + Ok(PathFreshness::HasLocalModifications { upstream: upstream_sha }) + } else { + Ok(PathFreshness::LastModifiedUpstream { upstream: upstream_sha }) } } -/// Returns the master branch from which we can take diffs to see changes. -/// This will usually be rust-lang/rust master, but sometimes this might not exist. -/// This could be because the user is updating their forked master branch using the GitHub UI -/// and therefore doesn't need an upstream master branch checked out. -/// We will then fall back to origin/master in the hope that at least this exists. -pub fn updated_master_branch( - config: &GitConfig<'_>, - git_dir: Option<&Path>, -) -> Result { - let upstream_remote = get_rust_lang_rust_remote(config, git_dir)?; - let branch = config.nightly_branch; - for upstream_master in [format!("{upstream_remote}/{branch}"), format!("origin/{branch}")] { - if rev_exists(&upstream_master, git_dir)? { - return Ok(upstream_master); - } - } +/// Returns true if any of the passed `paths` have changed since the `base` commit. +pub fn has_changed_since(git_dir: &Path, base: &str, paths: &[&str]) -> bool { + let mut git = Command::new("git"); + git.current_dir(git_dir); + + git.args(["diff-index", "--quiet", base, "--"]).args(paths); - Err("Cannot find any suitable upstream master branch".to_owned()) + // Exit code 0 => no changes + // Exit code 1 => some changes were detected + !git.status().expect("cannot run git diff-index").success() } -/// Finds the nearest merge commit by comparing the local `HEAD` with the upstream branch's state. -/// To work correctly, the upstream remote must be properly configured using `git remote add `. -/// In most cases `get_closest_merge_commit` is the function you are looking for as it doesn't require remote -/// to be configured. -fn git_upstream_merge_base( - config: &GitConfig<'_>, - git_dir: Option<&Path>, -) -> Result { - let updated_master = updated_master_branch(config, git_dir)?; +/// Returns the latest upstream commit that modified `target_paths`, or `None` if no such commit +/// was found. +fn get_latest_upstream_commit_that_modified_files( + git_dir: &Path, + git_config: &GitConfig<'_>, + target_paths: &[&str], +) -> Result, String> { let mut git = Command::new("git"); - if let Some(git_dir) = git_dir { - git.current_dir(git_dir); + git.current_dir(git_dir); + + // In theory, we could just use + // `git rev-list --first-parent HEAD --author= -- ` + // to find the latest upstream commit that modified ``. + // However, this does not work if you are in a subtree sync branch that contains merge commits + // which have the subtree history as their first parent, and the rustc history as second parent: + // `--first-parent` will just walk up the subtree history and never see a single rustc commit. + // We thus have to take a two-pronged approach. First lookup the most recent upstream commit + // by *date* (this should work even in a subtree sync branch), and then start the lookup for + // modified paths starting from that commit. + // + // See https://github.com/rust-lang/rust/pull/138591#discussion_r2037081858 for more details. + let upstream = get_closest_upstream_commit(Some(git_dir), git_config, CiEnv::None)? + .unwrap_or_else(|| "HEAD".to_string()); + + git.args([ + "rev-list", + "--first-parent", + "-n1", + &upstream, + "--author", + git_config.git_merge_commit_email, + ]); + + if !target_paths.is_empty() { + git.arg("--").args(target_paths); } - Ok(output_result(git.arg("merge-base").arg(&updated_master).arg("HEAD"))?.trim().to_owned()) + let output = output_result(&mut git)?.trim().to_owned(); + if output.is_empty() { Ok(None) } else { Ok(Some(output)) } } -/// Searches for the nearest merge commit in the repository that also exists upstream. +/// Returns the most recent (ordered chronologically) commit found in the local history that +/// should exist upstream. We identify upstream commits by the e-mail of the commit +/// author. /// -/// It looks for the most recent commit made by the merge bot by matching the author's email -/// address with the merge bot's email. -pub fn get_closest_merge_commit( +/// If we are in CI, we simply return our first parent. +fn get_closest_upstream_commit( git_dir: Option<&Path>, config: &GitConfig<'_>, - target_paths: &[PathBuf], -) -> Result { + env: CiEnv, +) -> Result, String> { + let base = match env { + CiEnv::None => "HEAD", + CiEnv::GitHubActions => { + // On CI, we should always have a non-upstream merge commit at the tip, + // and our first parent should be the most recently merged upstream commit. + // We thus simply return our first parent. + return resolve_commit_sha(git_dir, "HEAD^1").map(Some); + } + }; + let mut git = Command::new("git"); if let Some(git_dir) = git_dir { git.current_dir(git_dir); } - let channel = include_str!("../../ci/channel"); - - let merge_base = { - if CiEnv::is_ci() && - // FIXME: When running on rust-lang managed CI and it's not a nightly build, - // `git_upstream_merge_base` fails with an error message similar to this: - // ``` - // called `Result::unwrap()` on an `Err` value: "command did not execute successfully: - // cd \"/checkout\" && \"git\" \"merge-base\" \"origin/master\" \"HEAD\"\nexpected success, got: exit status: 1\n" - // ``` - // Investigate and resolve this issue instead of skipping it like this. - (channel == "nightly" || !CiEnv::is_rust_lang_managed_ci_job()) - { - git_upstream_merge_base(config, git_dir).unwrap() - } else { - // For non-CI environments, ignore rust-lang/rust upstream as it usually gets - // outdated very quickly. - "HEAD".to_string() - } - }; - + // We do not use `--first-parent`, because we can be in a situation (outside CI) where we have + // a subtree merge that actually has the main rustc history as its second parent. + // Using `--first-parent` would recurse into the history of the subtree, which could have some + // old bors commits that are not relevant to us. + // With `--author-date-order`, git recurses into all parent subtrees, and returns the most + // chronologically recent bors commit. + // Here we assume that none of our subtrees use bors anymore, and that all their old bors + // commits are way older than recent rustc bors commits! git.args([ "rev-list", + "--author-date-order", &format!("--author={}", config.git_merge_commit_email), "-n1", - "--first-parent", - &merge_base, + &base, ]); - if !target_paths.is_empty() { - git.arg("--").args(target_paths); + let output = output_result(&mut git)?.trim().to_owned(); + if output.is_empty() { Ok(None) } else { Ok(Some(output)) } +} + +/// Resolve the commit SHA of `commit_ref`. +fn resolve_commit_sha(git_dir: Option<&Path>, commit_ref: &str) -> Result { + let mut git = Command::new("git"); + + if let Some(git_dir) = git_dir { + git.current_dir(git_dir); } + git.args(["rev-parse", commit_ref]); + Ok(output_result(&mut git)?.trim().to_owned()) } /// Returns the files that have been modified in the current branch compared to the master branch. +/// This includes committed changes, uncommitted changes, and changes that are not even staged. +/// /// The `extensions` parameter can be used to filter the files by their extension. /// Does not include removed files. /// If `extensions` is empty, all files will be returned. @@ -173,8 +262,10 @@ pub fn get_git_modified_files( config: &GitConfig<'_>, git_dir: Option<&Path>, extensions: &[&str], -) -> Result>, String> { - let merge_base = get_closest_merge_commit(git_dir, config, &[])?; +) -> Result, String> { + let Some(merge_base) = get_closest_upstream_commit(git_dir, config, CiEnv::None)? else { + return Err("No upstream commit was found".to_string()); + }; let mut git = Command::new("git"); if let Some(git_dir) = git_dir { @@ -186,7 +277,10 @@ pub fn get_git_modified_files( let (status, name) = f.trim().split_once(char::is_whitespace).unwrap(); if status == "D" { None - } else if Path::new(name).extension().map_or(false, |ext| { + } else if Path::new(name).extension().map_or(extensions.is_empty(), |ext| { + // If there is no extension, we allow the path if `extensions` is empty + // If there is an extension, we allow it if `extension` is empty or it contains the + // extension. extensions.is_empty() || extensions.contains(&ext.to_str().unwrap()) }) { Some(name.to_owned()) @@ -195,17 +289,11 @@ pub fn get_git_modified_files( } }) .collect(); - Ok(Some(files)) + Ok(files) } /// Returns the files that haven't been added to git yet. -pub fn get_git_untracked_files( - config: &GitConfig<'_>, - git_dir: Option<&Path>, -) -> Result>, String> { - let Ok(_updated_master) = updated_master_branch(config, git_dir) else { - return Ok(None); - }; +pub fn get_git_untracked_files(git_dir: Option<&Path>) -> Result>, String> { let mut git = Command::new("git"); if let Some(git_dir) = git_dir { git.current_dir(git_dir); diff --git a/src/build_helper/src/metrics.rs b/src/build_helper/src/metrics.rs index 538c33e9b1591..8b82e62a32770 100644 --- a/src/build_helper/src/metrics.rs +++ b/src/build_helper/src/metrics.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use serde_derive::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] @@ -7,11 +9,26 @@ pub struct JsonRoot { pub format_version: usize, pub system_stats: JsonInvocationSystemStats, pub invocations: Vec, + #[serde(default)] + pub ci_metadata: Option, +} + +/// Represents metadata about bootstrap's execution in CI. +#[derive(Serialize, Deserialize)] +pub struct CiMetadata { + /// GitHub run ID of the workflow where bootstrap was executed. + /// Note that the run ID will be shared amongst all jobs executed in that workflow. + pub workflow_run_id: u64, + /// Full name of a GitHub repository where bootstrap was executed in CI. + /// e.g. `rust-lang-ci/rust`. + pub repository: String, } #[derive(Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub struct JsonInvocation { + // Remembers the command-line invocation with which bootstrap was invoked. + pub cmdline: String, // Unix timestamp in seconds // // This is necessary to easily correlate this invocation with logs or other data. @@ -70,7 +87,7 @@ pub struct Test { pub outcome: TestOutcome, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] #[serde(tag = "outcome", rename_all = "snake_case")] pub enum TestOutcome { Passed, @@ -98,3 +115,102 @@ fn null_as_f64_nan<'de, D: serde::Deserializer<'de>>(d: D) -> Result::deserialize(d).map(|f| f.unwrap_or(f64::NAN)) } + +/// Represents a single bootstrap step, with the accumulated duration of all its children. +#[derive(Clone, Debug)] +pub struct BuildStep { + pub r#type: String, + pub children: Vec, + pub duration: Duration, + // Full name of the step, including all parent names + pub full_name: String, +} + +impl BuildStep { + /// Create a `BuildStep` representing a single invocation of bootstrap. + /// The most important thing is that the build step aggregates the + /// durations of all children, so that it can be easily accessed. + pub fn from_invocation(invocation: &JsonInvocation) -> Self { + fn parse(node: &JsonNode, parent_name: &str) -> Option { + match node { + JsonNode::RustbuildStep { + type_: kind, + children, + duration_excluding_children_sec, + .. + } => { + let full_name = format!("{parent_name}-{kind}"); + let children: Vec<_> = + children.into_iter().filter_map(|s| parse(s, &full_name)).collect(); + let children_duration = children.iter().map(|c| c.duration).sum::(); + Some(BuildStep { + r#type: kind.to_string(), + children, + full_name, + duration: children_duration + + Duration::from_secs_f64(*duration_excluding_children_sec), + }) + } + JsonNode::TestSuite(_) => None, + } + } + + let duration = Duration::from_secs_f64(invocation.duration_including_children_sec); + + // The root "total" step is kind of a virtual step that encompasses all other steps, + // but it is not a real parent of the other steps. + // We thus start the parent name hierarchy here and use an empty string + // as the parent name of the top-level steps. + let children: Vec<_> = invocation.children.iter().filter_map(|s| parse(s, "")).collect(); + Self { r#type: "total".to_string(), children, duration, full_name: "total".to_string() } + } + + pub fn find_all_by_type(&self, r#type: &str) -> Vec<&Self> { + let mut result = Vec::new(); + self.find_by_type(r#type, &mut result); + result + } + + fn find_by_type<'a>(&'a self, r#type: &str, result: &mut Vec<&'a Self>) { + if self.r#type == r#type { + result.push(self); + } + for child in &self.children { + child.find_by_type(r#type, result); + } + } + + /// Returns a Vec with all substeps, ordered by their hierarchical order. + /// The first element of the tuple is the depth of a given step. + pub fn linearize_steps(&self) -> Vec<(u32, &BuildStep)> { + let mut substeps: Vec<(u32, &BuildStep)> = Vec::new(); + + fn visit<'a>(step: &'a BuildStep, level: u32, substeps: &mut Vec<(u32, &'a BuildStep)>) { + substeps.push((level, step)); + for child in &step.children { + visit(child, level + 1, substeps); + } + } + + visit(self, 0, &mut substeps); + substeps + } +} + +/// Writes build steps into a nice indented table. +pub fn format_build_steps(root: &BuildStep) -> String { + use std::fmt::Write; + + let mut output = String::new(); + for (level, step) in root.linearize_steps() { + let label = format!("{}{}", ".".repeat(level as usize), escape_step_name(step)); + writeln!(output, "{label:.<65}{:>8.2}s", step.duration.as_secs_f64()).unwrap(); + } + output +} + +/// Bootstrap steps can be generic and thus contain angle brackets (<...>). +/// However, Markdown interprets these as HTML, so we need to escap ethem. +pub fn escape_step_name(step: &BuildStep) -> String { + step.r#type.replace('<', "<").replace('>', ">") +} diff --git a/src/build_helper/src/stage0_parser.rs b/src/build_helper/src/stage0_parser.rs index 2a0c12a1c91c7..2723f4aa7b914 100644 --- a/src/build_helper/src/stage0_parser.rs +++ b/src/build_helper/src/stage0_parser.rs @@ -20,7 +20,6 @@ pub struct Stage0Config { pub artifacts_server: String, pub artifacts_with_llvm_assertions_server: String, pub git_merge_commit_email: String, - pub git_repository: String, pub nightly_branch: String, } @@ -49,7 +48,6 @@ pub fn parse_stage0_file() -> Stage0 { stage0.config.artifacts_with_llvm_assertions_server = value.to_owned() } "git_merge_commit_email" => stage0.config.git_merge_commit_email = value.to_owned(), - "git_repository" => stage0.config.git_repository = value.to_owned(), "nightly_branch" => stage0.config.nightly_branch = value.to_owned(), "compiler_date" => stage0.compiler.date = value.to_owned(), diff --git a/src/ci/citool/Cargo.lock b/src/ci/citool/Cargo.lock index 39b6b44da643b..43321d12cafcd 100644 --- a/src/ci/citool/Cargo.lock +++ b/src/ci/citool/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "anstream" version = "0.6.18" @@ -38,7 +44,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -49,7 +55,7 @@ checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", "once_cell", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -58,16 +64,108 @@ version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +[[package]] +name = "askama" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7" +dependencies = [ + "askama_derive", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac" +dependencies = [ + "askama_parser", + "basic-toml", + "memchr", + "proc-macro2", + "quote", + "rustc-hash", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "askama_parser" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f" +dependencies = [ + "memchr", + "serde", + "serde_derive", + "winnow", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "basic-toml" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" +dependencies = [ + "serde", +] + +[[package]] +name = "build_helper" +version = "0.1.0" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "bytes" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" + +[[package]] +name = "cc" +version = "1.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "citool" version = "0.1.0" dependencies = [ "anyhow", + "askama", + "build_helper", "clap", + "csv", + "diff", + "glob-match", "insta", "serde", "serde_json", "serde_yaml", + "ureq", ] [[package]] @@ -125,7 +223,101 @@ dependencies = [ "encode_unicode", "libc", "once_cell", - "windows-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "csv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +dependencies = [ + "memchr", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", ] [[package]] @@ -140,6 +332,48 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "flate2" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob-match" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d" + [[package]] name = "hashbrown" version = "0.15.2" @@ -152,6 +386,162 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.7.1" @@ -199,18 +589,57 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "once_cell" version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project" version = "1.1.9" @@ -231,6 +660,12 @@ dependencies = [ "syn", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.93" @@ -249,6 +684,67 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ring" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustls" +version = "0.23.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.19" @@ -300,18 +796,42 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "similar" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +[[package]] +name = "smallvec" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.98" @@ -323,6 +843,58 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb041120f25f8fbe8fd2dbe4671c7c2ed74d83be2e7a77529bf7e0790ae3f472" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" + +[[package]] +name = "time-macros" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "unicode-ident" version = "1.0.16" @@ -335,12 +907,110 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06f78313c985f2fba11100dd06d60dd402d0cabb458af4d94791b8e09c025323" +dependencies = [ + "base64", + "cookie_store", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "ureq-proto", + "utf-8", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64adb55464bad1ab1aa9229133d0d59d2f679180f4d15f0d9debe616f541f25e" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "webpki-roots" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -413,3 +1083,97 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +dependencies = [ + "memchr", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/ci/citool/Cargo.toml b/src/ci/citool/Cargo.toml index e77c67c71477a..0e2aba3b9e3fc 100644 --- a/src/ci/citool/Cargo.toml +++ b/src/ci/citool/Cargo.toml @@ -5,10 +5,17 @@ edition = "2021" [dependencies] anyhow = "1" +askama = "0.13" clap = { version = "4.5", features = ["derive"] } +csv = "1" +diff = "0.1" +glob-match = "0.2" serde = { version = "1", features = ["derive"] } serde_yaml = "0.9" serde_json = "1" +ureq = { version = "3", features = ["json"] } + +build_helper = { path = "../../build_helper" } [dev-dependencies] insta = "1" @@ -17,3 +24,7 @@ insta = "1" # If this is omitted, cargo will look for a workspace elsewhere. # We want to avoid this, since citool is independent of the other crates. [workspace] + +# Make compilation faster +[profile.dev] +debug = 0 diff --git a/src/ci/citool/src/analysis.rs b/src/ci/citool/src/analysis.rs new file mode 100644 index 0000000000000..62974be2dbe8c --- /dev/null +++ b/src/ci/citool/src/analysis.rs @@ -0,0 +1,560 @@ +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::fmt::Debug; +use std::time::Duration; + +use build_helper::metrics::{ + BuildStep, JsonRoot, TestOutcome, TestSuite, TestSuiteMetadata, escape_step_name, + format_build_steps, +}; + +use crate::github::JobInfoResolver; +use crate::metrics::{JobMetrics, JobName, get_test_suites}; +use crate::utils::{output_details, pluralize}; +use crate::{metrics, utils}; + +/// Outputs durations of individual bootstrap steps from the gathered bootstrap invocations, +/// and also a table with summarized information about executed tests. +pub fn output_bootstrap_stats(metrics: &JsonRoot, parent_metrics: Option<&JsonRoot>) { + if !metrics.invocations.is_empty() { + println!("# Bootstrap steps"); + record_bootstrap_step_durations(&metrics, parent_metrics); + record_test_suites(&metrics); + } +} + +fn record_bootstrap_step_durations(metrics: &JsonRoot, parent_metrics: Option<&JsonRoot>) { + let parent_steps: HashMap = parent_metrics + .map(|metrics| { + metrics + .invocations + .iter() + .map(|invocation| { + (invocation.cmdline.clone(), BuildStep::from_invocation(invocation)) + }) + .collect() + }) + .unwrap_or_default(); + + for invocation in &metrics.invocations { + let step = BuildStep::from_invocation(invocation); + let table = format_build_steps(&step); + eprintln!("Step `{}`\n{table}\n", invocation.cmdline); + output_details(&format!("{} (steps)", invocation.cmdline), || { + println!("
{table}
"); + }); + + // If there was a parent bootstrap invocation with the same cmdline, diff it + if let Some(parent_step) = parent_steps.get(&invocation.cmdline) { + let table = format_build_step_diffs(&step, parent_step); + + let duration_before = parent_step.duration.as_secs(); + let duration_after = step.duration.as_secs(); + output_details( + &format!("{} (diff) ({duration_before}s -> {duration_after}s)", invocation.cmdline), + || { + println!("{table}"); + }, + ); + } + } + eprintln!("Recorded {} bootstrap invocation(s)", metrics.invocations.len()); +} + +/// Creates a table that displays a diff between the durations of steps between +/// two bootstrap invocations. +/// It also shows steps that were missing before/after. +fn format_build_step_diffs(current: &BuildStep, parent: &BuildStep) -> String { + use std::fmt::Write; + + // Helper struct that compares steps by their full name + struct StepByName<'a>((u32, &'a BuildStep)); + + impl<'a> PartialEq for StepByName<'a> { + fn eq(&self, other: &Self) -> bool { + self.0.1.full_name.eq(&other.0.1.full_name) + } + } + + fn get_steps(step: &BuildStep) -> Vec { + step.linearize_steps().into_iter().map(|v| StepByName(v)).collect() + } + + let steps_before = get_steps(parent); + let steps_after = get_steps(current); + + let mut table = "| Step | Before | After | Change |\n".to_string(); + writeln!(table, "|:-----|-------:|------:|-------:|").unwrap(); + + // Try to match removed, added and same steps using a classic diff algorithm + for result in diff::slice(&steps_before, &steps_after) { + let (step, before, after, change) = match result { + // The step was found both before and after + diff::Result::Both(before, after) => { + let duration_before = before.0.1.duration; + let duration_after = after.0.1.duration; + let pct_change = duration_after.as_secs_f64() / duration_before.as_secs_f64(); + let pct_change = pct_change * 100.0; + // Normalize around 100, to get + for regression and - for improvements + let pct_change = pct_change - 100.0; + ( + before, + format!("{:.2}s", duration_before.as_secs_f64()), + format!("{:.2}s", duration_after.as_secs_f64()), + format!("{pct_change:.1}%"), + ) + } + // The step was only found in the parent, so it was likely removed + diff::Result::Left(removed) => ( + removed, + format!("{:.2}s", removed.0.1.duration.as_secs_f64()), + "".to_string(), + "(removed)".to_string(), + ), + // The step was only found in the current commit, so it was likely added + diff::Result::Right(added) => ( + added, + "".to_string(), + format!("{:.2}s", added.0.1.duration.as_secs_f64()), + "(added)".to_string(), + ), + }; + + let prefix = ".".repeat(step.0.0 as usize); + writeln!( + table, + "| {prefix}{} | {before} | {after} | {change} |", + escape_step_name(step.0.1), + ) + .unwrap(); + } + + table +} + +fn record_test_suites(metrics: &JsonRoot) { + let suites = metrics::get_test_suites(&metrics); + + if !suites.is_empty() { + let aggregated = aggregate_test_suites(&suites); + let table = render_table(aggregated); + println!("\n# Test results\n"); + println!("{table}"); + } else { + eprintln!("No test suites found in metrics"); + } +} + +fn render_table(suites: BTreeMap) -> String { + use std::fmt::Write; + + let mut table = "| Test suite | Passed ✅ | Ignored 🚫 | Failed ❌ |\n".to_string(); + writeln!(table, "|:------|------:|------:|------:|").unwrap(); + + fn compute_pct(value: f64, total: f64) -> f64 { + if total == 0.0 { 0.0 } else { value / total } + } + + fn write_row( + buffer: &mut String, + name: &str, + record: &TestSuiteRecord, + surround: &str, + ) -> std::fmt::Result { + let TestSuiteRecord { passed, ignored, failed } = record; + let total = (record.passed + record.ignored + record.failed) as f64; + let passed_pct = compute_pct(*passed as f64, total) * 100.0; + let ignored_pct = compute_pct(*ignored as f64, total) * 100.0; + let failed_pct = compute_pct(*failed as f64, total) * 100.0; + + write!(buffer, "| {surround}{name}{surround} |")?; + write!(buffer, " {surround}{passed} ({passed_pct:.0}%){surround} |")?; + write!(buffer, " {surround}{ignored} ({ignored_pct:.0}%){surround} |")?; + writeln!(buffer, " {surround}{failed} ({failed_pct:.0}%){surround} |")?; + + Ok(()) + } + + let mut total = TestSuiteRecord::default(); + for (name, record) in suites { + write_row(&mut table, &name, &record, "").unwrap(); + total.passed += record.passed; + total.ignored += record.ignored; + total.failed += record.failed; + } + write_row(&mut table, "Total", &total, "**").unwrap(); + table +} + +/// Outputs a report of test differences between the `parent` and `current` commits. +pub fn output_test_diffs( + job_metrics: &HashMap, + job_info_resolver: &mut JobInfoResolver, +) { + let aggregated_test_diffs = aggregate_test_diffs(&job_metrics); + report_test_diffs(aggregated_test_diffs, job_metrics, job_info_resolver); +} + +/// Prints the ten largest differences in bootstrap durations. +pub fn output_largest_duration_changes( + job_metrics: &HashMap, + job_info_resolver: &mut JobInfoResolver, +) { + struct Entry<'a> { + job: &'a JobName, + before: Duration, + after: Duration, + change: f64, + } + + let mut changes: Vec = vec![]; + for (job, metrics) in job_metrics { + if let Some(parent) = &metrics.parent { + let duration_before = parent + .invocations + .iter() + .map(|i| BuildStep::from_invocation(i).duration) + .sum::(); + let duration_after = metrics + .current + .invocations + .iter() + .map(|i| BuildStep::from_invocation(i).duration) + .sum::(); + let pct_change = duration_after.as_secs_f64() / duration_before.as_secs_f64(); + let pct_change = pct_change * 100.0; + // Normalize around 100, to get + for regression and - for improvements + let pct_change = pct_change - 100.0; + changes.push(Entry { + job, + before: duration_before, + after: duration_after, + change: pct_change, + }); + } + } + changes.sort_by(|e1, e2| e1.change.abs().partial_cmp(&e2.change.abs()).unwrap().reverse()); + + println!("# Job duration changes"); + for (index, entry) in changes.into_iter().take(10).enumerate() { + println!( + "{}. {}: {:.1}s -> {:.1}s ({:.1}%)", + index + 1, + format_job_link(job_info_resolver, job_metrics, entry.job), + entry.before.as_secs_f64(), + entry.after.as_secs_f64(), + entry.change + ); + } + + println!(); + output_details("How to interpret the job duration changes?", || { + println!( + r#"Job durations can vary a lot, based on the actual runner instance +that executed the job, system noise, invalidated caches, etc. The table above is provided +mostly for t-infra members, for simpler debugging of potential CI slow-downs."# + ); + }); +} + +#[derive(Default)] +struct TestSuiteRecord { + passed: u64, + ignored: u64, + failed: u64, +} + +fn test_metadata_name(metadata: &TestSuiteMetadata) -> String { + match metadata { + TestSuiteMetadata::CargoPackage { crates, stage, .. } => { + format!("{} (stage {stage})", crates.join(", ")) + } + TestSuiteMetadata::Compiletest { suite, stage, .. } => { + format!("{suite} (stage {stage})") + } + } +} + +fn aggregate_test_suites(suites: &[&TestSuite]) -> BTreeMap { + let mut records: BTreeMap = BTreeMap::new(); + for suite in suites { + let name = test_metadata_name(&suite.metadata); + let record = records.entry(name).or_default(); + for test in &suite.tests { + match test.outcome { + TestOutcome::Passed => { + record.passed += 1; + } + TestOutcome::Failed => { + record.failed += 1; + } + TestOutcome::Ignored { .. } => { + record.ignored += 1; + } + } + } + } + records +} + +/// Represents a difference in the outcome of tests between a base and a current commit. +/// Maps test diffs to jobs that contained them. +#[derive(Debug)] +struct AggregatedTestDiffs { + diffs: HashMap>, +} + +fn aggregate_test_diffs(jobs: &HashMap) -> AggregatedTestDiffs { + let mut diffs: HashMap> = HashMap::new(); + + // Aggregate test suites + for (name, metrics) in jobs { + if let Some(parent) = &metrics.parent { + let tests_parent = aggregate_tests(parent); + let tests_current = aggregate_tests(&metrics.current); + for diff in calculate_test_diffs(tests_parent, tests_current) { + diffs.entry(diff).or_default().push(name.to_string()); + } + } + } + + AggregatedTestDiffs { diffs } +} + +#[derive(Eq, PartialEq, Hash, Debug)] +enum TestOutcomeDiff { + ChangeOutcome { before: TestOutcome, after: TestOutcome }, + Missing { before: TestOutcome }, + Added(TestOutcome), +} + +#[derive(Eq, PartialEq, Hash, Debug)] +struct TestDiff { + test: Test, + diff: TestOutcomeDiff, +} + +fn calculate_test_diffs(parent: TestSuiteData, current: TestSuiteData) -> HashSet { + let mut diffs = HashSet::new(); + for (test, outcome) in ¤t.tests { + match parent.tests.get(test) { + Some(before) => { + if before != outcome { + diffs.insert(TestDiff { + test: test.clone(), + diff: TestOutcomeDiff::ChangeOutcome { + before: before.clone(), + after: outcome.clone(), + }, + }); + } + } + None => { + diffs.insert(TestDiff { + test: test.clone(), + diff: TestOutcomeDiff::Added(outcome.clone()), + }); + } + } + } + for (test, outcome) in &parent.tests { + if !current.tests.contains_key(test) { + diffs.insert(TestDiff { + test: test.clone(), + diff: TestOutcomeDiff::Missing { before: outcome.clone() }, + }); + } + } + + diffs +} + +/// Aggregates test suite executions from all bootstrap invocations in a given CI job. +#[derive(Default)] +struct TestSuiteData { + tests: HashMap, +} + +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +struct Test { + name: String, + stage: u8, + is_doctest: bool, +} + +/// Extracts all tests from the passed metrics and map them to their outcomes. +fn aggregate_tests(metrics: &JsonRoot) -> TestSuiteData { + let mut tests = HashMap::new(); + let test_suites = get_test_suites(&metrics); + for suite in test_suites { + let stage = match suite.metadata { + TestSuiteMetadata::CargoPackage { stage, .. } => stage, + TestSuiteMetadata::Compiletest { stage, .. } => stage, + } as u8; + for test in &suite.tests { + // Poor man's detection of doctests based on the "(line XYZ)" suffix + let is_doctest = matches!(suite.metadata, TestSuiteMetadata::CargoPackage { .. }) + && test.name.contains("(line"); + let test_entry = Test { + name: utils::normalize_path_delimiters(&test.name).to_string(), + stage, + is_doctest, + }; + tests.insert(test_entry, test.outcome.clone()); + } + } + TestSuiteData { tests } +} + +/// Prints test changes in Markdown format to stdout. +fn report_test_diffs( + diff: AggregatedTestDiffs, + job_metrics: &HashMap, + job_info_resolver: &mut JobInfoResolver, +) { + println!("# Test differences"); + if diff.diffs.is_empty() { + println!("No test diffs found"); + return; + } + + fn format_outcome(outcome: &TestOutcome) -> String { + match outcome { + TestOutcome::Passed => "pass".to_string(), + TestOutcome::Failed => "fail".to_string(), + TestOutcome::Ignored { ignore_reason } => { + let reason = match ignore_reason { + Some(reason) => format!(" ({reason})"), + None => String::new(), + }; + format!("ignore{reason}") + } + } + } + + fn format_diff(diff: &TestOutcomeDiff) -> String { + match diff { + TestOutcomeDiff::ChangeOutcome { before, after } => { + format!("{} -> {}", format_outcome(before), format_outcome(after)) + } + TestOutcomeDiff::Missing { before } => { + format!("{} -> [missing]", format_outcome(before)) + } + TestOutcomeDiff::Added(outcome) => { + format!("[missing] -> {}", format_outcome(outcome)) + } + } + } + + fn format_job_group(group: u64) -> String { + format!("**J{group}**") + } + + // It would be quite noisy to repeat the jobs that contained the test changes after/next to + // every test diff. At the same time, grouping the test diffs by + // [unique set of jobs that contained them] also doesn't work well, because the test diffs + // would have to be duplicated several times. + // Instead, we create a set of unique job groups, and then print a job group after each test. + // We then print the job groups at the end, as a sort of index. + let mut grouped_diffs: Vec<(&TestDiff, u64)> = vec![]; + let mut job_list_to_group: HashMap<&[JobName], u64> = HashMap::new(); + let mut job_index: Vec<&[JobName]> = vec![]; + + let original_diff_count = diff.diffs.len(); + let diffs = diff + .diffs + .into_iter() + .filter(|(diff, _)| !diff.test.is_doctest) + .map(|(diff, mut jobs)| { + jobs.sort(); + (diff, jobs) + }) + .collect::>(); + let doctest_count = original_diff_count.saturating_sub(diffs.len()); + + let max_diff_count = 100; + for (diff, jobs) in diffs.iter().take(max_diff_count) { + let jobs = &*jobs; + let job_group = match job_list_to_group.get(jobs.as_slice()) { + Some(id) => *id, + None => { + let id = job_index.len() as u64; + job_index.push(jobs); + job_list_to_group.insert(jobs, id); + id + } + }; + grouped_diffs.push((diff, job_group)); + } + + // Sort diffs by job group and test name + grouped_diffs.sort_by(|(d1, g1), (d2, g2)| g1.cmp(&g2).then(d1.test.name.cmp(&d2.test.name))); + + // Now group the tests by stage + let mut grouped_by_stage: BTreeMap> = Default::default(); + for (diff, group) in grouped_diffs { + grouped_by_stage.entry(diff.test.stage).or_default().push((diff, group)) + } + + output_details( + &format!("Show {} test {}\n", original_diff_count, pluralize("diff", original_diff_count)), + || { + for (stage, diffs) in grouped_by_stage { + println!("## Stage {stage}"); + for (diff, job_group) in diffs { + println!( + "- `{}`: {} ({})", + diff.test.name, + format_diff(&diff.diff), + format_job_group(job_group) + ); + } + } + + let extra_diffs = diffs.len().saturating_sub(max_diff_count); + if extra_diffs > 0 { + println!( + "\n(and {extra_diffs} additional {})", + pluralize("test diff", extra_diffs) + ); + } + + if doctest_count > 0 { + let prefix = + if doctest_count < original_diff_count { "Additionally, " } else { "" }; + println!( + "\n{prefix}{doctest_count} doctest {} were found. These are ignored, as they are noisy.", + pluralize("diff", doctest_count) + ); + } + + // Now print the job group index + if !job_index.is_empty() { + println!("\n**Job group index**\n"); + for (group, jobs) in job_index.into_iter().enumerate() { + println!( + "- {}: {}", + format_job_group(group as u64), + jobs.iter() + .map(|j| format_job_link(job_info_resolver, job_metrics, j)) + .collect::>() + .join(", ") + ); + } + } + }, + ); +} + +/// Tries to get a GitHub Actions job summary URL from the resolver. +/// If it is not available, just wraps the job name in backticks. +fn format_job_link( + job_info_resolver: &mut JobInfoResolver, + job_metrics: &HashMap, + job_name: &str, +) -> String { + job_metrics + .get(job_name) + .and_then(|metrics| job_info_resolver.get_job_summary_link(job_name, &metrics.current)) + .map(|summary_url| format!("[{job_name}]({summary_url})")) + .unwrap_or_else(|| format!("`{job_name}`")) +} diff --git a/src/ci/citool/src/cpu_usage.rs b/src/ci/citool/src/cpu_usage.rs new file mode 100644 index 0000000000000..fa9a1203f4191 --- /dev/null +++ b/src/ci/citool/src/cpu_usage.rs @@ -0,0 +1,24 @@ +use std::path::Path; + +/// Loads CPU usage records from a CSV generated by the `src/ci/scripts/collect-cpu-stats.sh` +/// script. +pub fn load_cpu_usage(path: &Path) -> anyhow::Result> { + let reader = csv::ReaderBuilder::new().flexible(true).from_path(path)?; + + let mut entries = vec![]; + for row in reader.into_records() { + let row = row?; + let cols = row.into_iter().collect::>(); + + // The log might contain incomplete rows or some Python exception + if cols.len() == 2 { + if let Ok(idle) = cols[1].parse::() { + entries.push(100.0 - idle); + } else { + eprintln!("Warning: cannot parse CPU CSV entry {}", cols[1]); + } + } + } + + Ok(entries) +} diff --git a/src/ci/citool/src/datadog.rs b/src/ci/citool/src/datadog.rs new file mode 100644 index 0000000000000..837257420d6a2 --- /dev/null +++ b/src/ci/citool/src/datadog.rs @@ -0,0 +1,43 @@ +use anyhow::Context; + +use crate::utils::load_env_var; + +/// Uploads a custom CI pipeline metric to Datadog. +/// Expects to be executed from within the context of a GitHub Actions job. +pub fn upload_datadog_metric(name: &str, value: f64) -> anyhow::Result<()> { + let datadog_api_key = load_env_var("DATADOG_API_KEY")?; + let github_server_url = load_env_var("GITHUB_SERVER_URL")?; + let github_repository = load_env_var("GITHUB_REPOSITORY")?; + let github_run_id = load_env_var("GITHUB_RUN_ID")?; + let github_run_attempt = load_env_var("GITHUB_RUN_ATTEMPT")?; + let github_job = load_env_var("GITHUB_JOB")?; + let dd_github_job_name = load_env_var("DD_GITHUB_JOB_NAME")?; + + // This API endpoint is not documented in Datadog's API reference currently. + // It was reverse-engineered from the `datadog-ci measure` npm command. + ureq::post("https://api.datadoghq.com/api/v2/ci/pipeline/metrics") + .header("DD-API-KEY", datadog_api_key) + .send_json(serde_json::json!({ + "data": { + "attributes": { + "ci_env": { + "GITHUB_SERVER_URL": github_server_url, + "GITHUB_REPOSITORY": github_repository, + "GITHUB_RUN_ID": github_run_id, + "GITHUB_RUN_ATTEMPT": github_run_attempt, + "GITHUB_JOB": github_job, + "DD_GITHUB_JOB_NAME": dd_github_job_name + }, + // Job level + "ci_level": 1, + "metrics": { + name: value + }, + "provider": "github" + }, + "type": "ci_custom_metric" + } + })) + .context("cannot send metric to DataDog")?; + Ok(()) +} diff --git a/src/ci/citool/src/github.rs b/src/ci/citool/src/github.rs new file mode 100644 index 0000000000000..35e4c3f9599d6 --- /dev/null +++ b/src/ci/citool/src/github.rs @@ -0,0 +1,109 @@ +use std::collections::HashMap; + +use anyhow::Context; +use build_helper::metrics::{CiMetadata, JsonRoot}; + +pub struct GitHubClient; + +impl GitHubClient { + fn get_workflow_run_jobs( + &self, + repo: &str, + workflow_run_id: u64, + ) -> anyhow::Result> { + let req = ureq::get(format!( + "https://api.github.com/repos/{repo}/actions/runs/{workflow_run_id}/jobs?per_page=100" + )) + .header("User-Agent", "rust-lang/rust/citool") + .header("Accept", "application/vnd.github+json") + .header("X-GitHub-Api-Version", "2022-11-28") + .call() + .context("cannot get workflow job list")?; + + let status = req.status(); + let mut body = req.into_body(); + if status.is_success() { + // This API response is actually paged, but we assume for now that there are at + // most 100 jobs per workflow. + let response = body + .read_json::() + .context("cannot deserialize workflow run jobs response")?; + // The CI job names have a prefix, e.g. `auto - foo`. We remove the prefix here to + // normalize the job name. + Ok(response + .jobs + .into_iter() + .map(|mut job| { + job.name = job + .name + .split_once(" - ") + .map(|res| res.1.to_string()) + .unwrap_or_else(|| job.name); + job + }) + .collect()) + } else { + Err(anyhow::anyhow!( + "Cannot get jobs of workflow run {workflow_run_id}: {status}\n{}", + body.read_to_string()? + )) + } + } +} + +#[derive(serde::Deserialize)] +struct WorkflowRunJobsResponse { + jobs: Vec, +} + +#[derive(serde::Deserialize)] +struct GitHubJob { + name: String, + id: u64, +} + +/// Can be used to resolve information about GitHub Actions jobs. +/// Caches results internally to avoid too unnecessary GitHub API calls. +pub struct JobInfoResolver { + client: GitHubClient, + // Workflow run ID -> jobs + workflow_job_cache: HashMap>, +} + +impl JobInfoResolver { + pub fn new() -> Self { + Self { client: GitHubClient, workflow_job_cache: Default::default() } + } + + /// Get a link to a job summary for the given job name and bootstrap execution. + pub fn get_job_summary_link(&mut self, job_name: &str, metrics: &JsonRoot) -> Option { + metrics.ci_metadata.as_ref().and_then(|metadata| { + self.get_job_id(metadata, job_name).map(|job_id| { + format!( + "https://github.com/{}/actions/runs/{}#summary-{job_id}", + metadata.repository, metadata.workflow_run_id + ) + }) + }) + } + + fn get_job_id(&mut self, ci_metadata: &CiMetadata, job_name: &str) -> Option { + if let Some(job) = self + .workflow_job_cache + .get(&ci_metadata.workflow_run_id) + .and_then(|jobs| jobs.iter().find(|j| j.name == job_name)) + { + return Some(job.id); + } + + let jobs = self + .client + .get_workflow_run_jobs(&ci_metadata.repository, ci_metadata.workflow_run_id) + .inspect_err(|e| eprintln!("Cannot download workflow jobs: {e:?}")) + .ok()?; + let job_id = jobs.iter().find(|j| j.name == job_name).map(|j| j.id); + // Save the cache even if the job name was not found, it could be useful for further lookups + self.workflow_job_cache.insert(ci_metadata.workflow_run_id, jobs); + job_id + } +} diff --git a/src/ci/citool/src/jobs.rs b/src/ci/citool/src/jobs.rs new file mode 100644 index 0000000000000..5600d7b4db59b --- /dev/null +++ b/src/ci/citool/src/jobs.rs @@ -0,0 +1,294 @@ +#[cfg(test)] +mod tests; + +use std::collections::BTreeMap; + +use anyhow::Context as _; +use serde_yaml::Value; + +use crate::GitHubContext; +use crate::utils::load_env_var; + +/// Representation of a job loaded from the `src/ci/github-actions/jobs.yml` file. +#[derive(serde::Deserialize, Debug, Clone)] +#[serde(deny_unknown_fields)] +pub struct Job { + /// Name of the job, e.g. mingw-check + pub name: String, + /// GitHub runner on which the job should be executed + pub os: String, + pub env: BTreeMap, + /// Should the job be only executed on a specific channel? + #[serde(default)] + pub only_on_channel: Option, + /// Do not cancel the whole workflow if this job fails. + #[serde(default)] + pub continue_on_error: Option, + /// Free additional disk space in the job, by removing unused packages. + #[serde(default)] + pub free_disk: Option, + /// Documentation link to a resource that could help people debug this CI job. + pub doc_url: Option, + /// Whether the job is executed on AWS CodeBuild. + pub codebuild: Option, +} + +impl Job { + /// By default, the Docker image of a job is based on its name. + /// However, it can be overridden by its IMAGE environment variable. + pub fn image(&self) -> String { + self.env + .get("IMAGE") + .map(|v| v.as_str().expect("IMAGE value should be a string").to_string()) + .unwrap_or_else(|| self.name.clone()) + } + + fn is_linux(&self) -> bool { + self.os.contains("ubuntu") + } +} + +#[derive(serde::Deserialize, Debug)] +struct JobEnvironments { + #[serde(rename = "pr")] + pr_env: BTreeMap, + #[serde(rename = "try")] + try_env: BTreeMap, + #[serde(rename = "auto")] + auto_env: BTreeMap, +} + +#[derive(serde::Deserialize, Debug)] +pub struct JobDatabase { + #[serde(rename = "pr")] + pub pr_jobs: Vec, + #[serde(rename = "try")] + pub try_jobs: Vec, + #[serde(rename = "auto")] + pub auto_jobs: Vec, + + /// Shared environments for the individual run types. + envs: JobEnvironments, +} + +impl JobDatabase { + /// Find `auto` jobs that correspond to the passed `pattern`. + /// Patterns are matched using the glob syntax. + /// For example `dist-*` matches all jobs starting with `dist-`. + fn find_auto_jobs_by_pattern(&self, pattern: &str) -> Vec { + self.auto_jobs + .iter() + .filter(|j| glob_match::glob_match(pattern, &j.name)) + .cloned() + .collect() + } +} + +pub fn load_job_db(db: &str) -> anyhow::Result { + let mut db: Value = serde_yaml::from_str(db)?; + + // We need to expand merge keys (<<), because serde_yaml can't deal with them + // `apply_merge` only applies the merge once, so do it a few times to unwrap nested merges. + db.apply_merge()?; + db.apply_merge()?; + + let db: JobDatabase = serde_yaml::from_value(db)?; + Ok(db) +} + +/// Representation of a job outputted to a GitHub Actions workflow. +#[derive(serde::Serialize, Debug)] +struct GithubActionsJob { + /// The main identifier of the job, used by CI scripts to determine what should be executed. + name: String, + /// Helper label displayed in GitHub Actions interface, containing the job name and a run type + /// prefix (PR/try/auto). + full_name: String, + os: String, + env: BTreeMap, + #[serde(skip_serializing_if = "Option::is_none")] + continue_on_error: Option, + #[serde(skip_serializing_if = "Option::is_none")] + free_disk: Option, + #[serde(skip_serializing_if = "Option::is_none")] + doc_url: Option, + #[serde(skip_serializing_if = "Option::is_none")] + codebuild: Option, +} + +/// Replace GitHub context variables with environment variables in job configs. +/// Used for codebuild jobs like +/// `codebuild-ubuntu-22-8c-$github.run_id-$github.run_attempt` +fn substitute_github_vars(jobs: Vec) -> anyhow::Result> { + let run_id = load_env_var("GITHUB_RUN_ID")?; + let run_attempt = load_env_var("GITHUB_RUN_ATTEMPT")?; + + let jobs = jobs + .into_iter() + .map(|mut job| { + job.os = job + .os + .replace("$github.run_id", &run_id) + .replace("$github.run_attempt", &run_attempt); + job + }) + .collect(); + + Ok(jobs) +} + +/// Skip CI jobs that are not supposed to be executed on the given `channel`. +fn skip_jobs(jobs: Vec, channel: &str) -> Vec { + jobs.into_iter() + .filter(|job| { + job.only_on_channel.is_none() || job.only_on_channel.as_deref() == Some(channel) + }) + .collect() +} + +/// Type of workflow that is being executed on CI +#[derive(Debug)] +pub enum RunType { + /// Workflows that run after a push to a PR branch + PullRequest, + /// Try run started with @bors try + TryJob { job_patterns: Option> }, + /// Merge attempt workflow + AutoJob, +} + +/// Maximum number of custom try jobs that can be requested in a single +/// `@bors try` request. +const MAX_TRY_JOBS_COUNT: usize = 20; + +fn calculate_jobs( + run_type: &RunType, + db: &JobDatabase, + channel: &str, +) -> anyhow::Result> { + let (jobs, prefix, base_env) = match run_type { + RunType::PullRequest => (db.pr_jobs.clone(), "PR", &db.envs.pr_env), + RunType::TryJob { job_patterns } => { + let jobs = if let Some(patterns) = job_patterns { + let mut jobs: Vec = vec![]; + let mut unknown_patterns = vec![]; + for pattern in patterns { + let matched_jobs = db.find_auto_jobs_by_pattern(pattern); + if matched_jobs.is_empty() { + unknown_patterns.push(pattern.clone()); + } else { + for job in matched_jobs { + if !jobs.iter().any(|j| j.name == job.name) { + jobs.push(job); + } + } + } + } + if !unknown_patterns.is_empty() { + return Err(anyhow::anyhow!( + "Patterns `{}` did not match any auto jobs", + unknown_patterns.join(", ") + )); + } + if jobs.len() > MAX_TRY_JOBS_COUNT { + return Err(anyhow::anyhow!( + "It is only possible to schedule up to {MAX_TRY_JOBS_COUNT} custom jobs, received {} custom jobs expanded from {} pattern(s)", + jobs.len(), + patterns.len() + )); + } + jobs + } else { + db.try_jobs.clone() + }; + (jobs, "try", &db.envs.try_env) + } + RunType::AutoJob => (db.auto_jobs.clone(), "auto", &db.envs.auto_env), + }; + let jobs = substitute_github_vars(jobs.clone()) + .context("Failed to substitute GitHub context variables in jobs")?; + let jobs = skip_jobs(jobs, channel); + let jobs = jobs + .into_iter() + .map(|job| { + let mut env: BTreeMap = crate::yaml_map_to_json(base_env); + env.extend(crate::yaml_map_to_json(&job.env)); + let full_name = format!("{prefix} - {}", job.name); + + // When the default `@bors try` job is executed (which is usually done + // for benchmarking performance, running crater or for downloading the + // built toolchain using `rustup-toolchain-install-master`), + // we inject the `DIST_TRY_BUILD` environment variable to the jobs + // to tell `opt-dist` to make the build faster by skipping certain steps. + if let RunType::TryJob { job_patterns } = run_type { + if job_patterns.is_none() { + env.insert( + "DIST_TRY_BUILD".to_string(), + serde_json::value::Value::Number(1.into()), + ); + } + } + + GithubActionsJob { + name: job.name, + full_name, + os: job.os, + env, + continue_on_error: job.continue_on_error, + free_disk: job.free_disk, + doc_url: job.doc_url, + codebuild: job.codebuild, + } + }) + .collect(); + + Ok(jobs) +} + +pub fn calculate_job_matrix( + db: JobDatabase, + gh_ctx: GitHubContext, + channel: &str, +) -> anyhow::Result<()> { + let run_type = gh_ctx.get_run_type().ok_or_else(|| { + anyhow::anyhow!("Cannot determine the type of workflow that is being executed") + })?; + eprintln!("Run type: {run_type:?}"); + + let jobs = calculate_jobs(&run_type, &db, channel)?; + if jobs.is_empty() { + return Err(anyhow::anyhow!("Computed job list is empty")); + } + + let run_type = match run_type { + RunType::PullRequest => "pr", + RunType::TryJob { .. } => "try", + RunType::AutoJob => "auto", + }; + + eprintln!("Output"); + eprintln!("jobs={jobs:?}"); + eprintln!("run_type={run_type}"); + println!("jobs={}", serde_json::to_string(&jobs)?); + println!("run_type={run_type}"); + + Ok(()) +} + +pub fn find_linux_job<'a>(jobs: &'a [Job], name: &str) -> anyhow::Result<&'a Job> { + let Some(job) = jobs.iter().find(|j| j.name == name) else { + let available_jobs: Vec<&Job> = jobs.iter().filter(|j| j.is_linux()).collect(); + let mut available_jobs = + available_jobs.iter().map(|j| j.name.to_string()).collect::>(); + available_jobs.sort(); + return Err(anyhow::anyhow!( + "Job {name} not found. The following jobs are available:\n{}", + available_jobs.join(", ") + )); + }; + if !job.is_linux() { + return Err(anyhow::anyhow!("Only Linux jobs can be executed locally")); + } + + Ok(job) +} diff --git a/src/ci/citool/src/jobs/tests.rs b/src/ci/citool/src/jobs/tests.rs new file mode 100644 index 0000000000000..a489656fa5dc7 --- /dev/null +++ b/src/ci/citool/src/jobs/tests.rs @@ -0,0 +1,64 @@ +use crate::jobs::{JobDatabase, load_job_db}; + +#[test] +fn lookup_job_pattern() { + let db = load_job_db( + r#" +envs: + pr: + try: + auto: + +pr: +try: +auto: + - name: dist-a + os: ubuntu + env: {} + - name: dist-a-alt + os: ubuntu + env: {} + - name: dist-b + os: ubuntu + env: {} + - name: dist-b-alt + os: ubuntu + env: {} + - name: test-a + os: ubuntu + env: {} + - name: test-a-alt + os: ubuntu + env: {} + - name: test-i686 + os: ubuntu + env: {} + - name: dist-i686 + os: ubuntu + env: {} + - name: test-msvc-i686-1 + os: ubuntu + env: {} + - name: test-msvc-i686-2 + os: ubuntu + env: {} +"#, + ) + .unwrap(); + check_pattern(&db, "dist-*", &["dist-a", "dist-a-alt", "dist-b", "dist-b-alt", "dist-i686"]); + check_pattern(&db, "*-alt", &["dist-a-alt", "dist-b-alt", "test-a-alt"]); + check_pattern(&db, "dist*-alt", &["dist-a-alt", "dist-b-alt"]); + check_pattern( + &db, + "*i686*", + &["test-i686", "dist-i686", "test-msvc-i686-1", "test-msvc-i686-2"], + ); +} + +#[track_caller] +fn check_pattern(db: &JobDatabase, pattern: &str, expected: &[&str]) { + let jobs = + db.find_auto_jobs_by_pattern(pattern).into_iter().map(|j| j.name).collect::>(); + + assert_eq!(jobs, expected); +} diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs index ad9cc8b82a6f9..87ce09cfb233d 100644 --- a/src/ci/citool/src/main.rs +++ b/src/ci/citool/src/main.rs @@ -1,113 +1,35 @@ -use std::collections::BTreeMap; +mod analysis; +mod cpu_usage; +mod datadog; +mod github; +mod jobs; +mod metrics; +mod test_dashboard; +mod utils; + +use std::collections::{BTreeMap, HashMap}; use std::path::{Path, PathBuf}; use std::process::Command; +use analysis::output_bootstrap_stats; use anyhow::Context; use clap::Parser; +use jobs::JobDatabase; use serde_yaml::Value; +use crate::analysis::{output_largest_duration_changes, output_test_diffs}; +use crate::cpu_usage::load_cpu_usage; +use crate::datadog::upload_datadog_metric; +use crate::github::JobInfoResolver; +use crate::jobs::RunType; +use crate::metrics::{JobMetrics, download_auto_job_metrics, download_job_metrics, load_metrics}; +use crate::test_dashboard::generate_test_dashboard; +use crate::utils::{load_env_var, output_details}; + const CI_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/.."); const DOCKER_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../docker"); const JOBS_YML_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../github-actions/jobs.yml"); -/// Representation of a job loaded from the `src/ci/github-actions/jobs.yml` file. -#[derive(serde::Deserialize, Debug, Clone)] -struct Job { - /// Name of the job, e.g. mingw-check - name: String, - /// GitHub runner on which the job should be executed - os: String, - env: BTreeMap, - /// Should the job be only executed on a specific channel? - #[serde(default)] - only_on_channel: Option, - /// Rest of attributes that will be passed through to GitHub actions - #[serde(flatten)] - extra_keys: BTreeMap, -} - -impl Job { - fn is_linux(&self) -> bool { - self.os.contains("ubuntu") - } - - /// By default, the Docker image of a job is based on its name. - /// However, it can be overridden by its IMAGE environment variable. - fn image(&self) -> String { - self.env - .get("IMAGE") - .map(|v| v.as_str().expect("IMAGE value should be a string").to_string()) - .unwrap_or_else(|| self.name.clone()) - } -} - -#[derive(serde::Deserialize, Debug)] -struct JobEnvironments { - #[serde(rename = "pr")] - pr_env: BTreeMap, - #[serde(rename = "try")] - try_env: BTreeMap, - #[serde(rename = "auto")] - auto_env: BTreeMap, -} - -#[derive(serde::Deserialize, Debug)] -struct JobDatabase { - #[serde(rename = "pr")] - pr_jobs: Vec, - #[serde(rename = "try")] - try_jobs: Vec, - #[serde(rename = "auto")] - auto_jobs: Vec, - - /// Shared environments for the individual run types. - envs: JobEnvironments, -} - -impl JobDatabase { - fn find_auto_job_by_name(&self, name: &str) -> Option { - self.auto_jobs.iter().find(|j| j.name == name).cloned() - } -} - -fn load_job_db(path: &Path) -> anyhow::Result { - let db = read_to_string(path)?; - let mut db: Value = serde_yaml::from_str(&db)?; - - // We need to expand merge keys (<<), because serde_yaml can't deal with them - // `apply_merge` only applies the merge once, so do it a few times to unwrap nested merges. - db.apply_merge()?; - db.apply_merge()?; - - let db: JobDatabase = serde_yaml::from_value(db)?; - Ok(db) -} - -/// Representation of a job outputted to a GitHub Actions workflow. -#[derive(serde::Serialize, Debug)] -struct GithubActionsJob { - /// The main identifier of the job, used by CI scripts to determine what should be executed. - name: String, - /// Helper label displayed in GitHub Actions interface, containing the job name and a run type - /// prefix (PR/try/auto). - full_name: String, - os: String, - env: BTreeMap, - #[serde(flatten)] - extra_keys: BTreeMap, -} - -/// Type of workflow that is being executed on CI -#[derive(Debug)] -enum RunType { - /// Workflows that run after a push to a PR branch - PullRequest, - /// Try run started with @bors try - TryJob { custom_jobs: Option> }, - /// Merge attempt workflow - AutoJob, -} - struct GitHubContext { event_name: String, branch_ref: String, @@ -118,24 +40,31 @@ impl GitHubContext { fn get_run_type(&self) -> Option { match (self.event_name.as_str(), self.branch_ref.as_str()) { ("pull_request", _) => Some(RunType::PullRequest), - ("push", "refs/heads/try-perf") => Some(RunType::TryJob { custom_jobs: None }), + ("push", "refs/heads/try-perf") => Some(RunType::TryJob { job_patterns: None }), ("push", "refs/heads/try" | "refs/heads/automation/bors/try") => { - let custom_jobs = self.get_custom_jobs(); - let custom_jobs = if !custom_jobs.is_empty() { Some(custom_jobs) } else { None }; - Some(RunType::TryJob { custom_jobs }) + let patterns = self.get_try_job_patterns(); + let patterns = if !patterns.is_empty() { Some(patterns) } else { None }; + Some(RunType::TryJob { job_patterns: patterns }) } ("push", "refs/heads/auto") => Some(RunType::AutoJob), _ => None, } } - /// Tries to parse names of specific CI jobs that should be executed in the form of - /// try-job: - /// from the commit message of the passed GitHub context. - fn get_custom_jobs(&self) -> Vec { + /// Tries to parse patterns of CI jobs that should be executed + /// from the commit message of the passed GitHub context + /// + /// They can be specified in the form of + /// try-job: + /// or + /// try-job: `` + /// (to avoid GitHub rendering the glob patterns as Markdown) + fn get_try_job_patterns(&self) -> Vec { if let Some(ref msg) = self.commit_message { msg.lines() .filter_map(|line| line.trim().strip_prefix("try-job: ")) + // Strip backticks if present + .map(|l| l.trim_matches('`')) .map(|l| l.trim().to_string()) .collect() } else { @@ -144,10 +73,6 @@ impl GitHubContext { } } -fn load_env_var(name: &str) -> anyhow::Result { - std::env::var(name).with_context(|| format!("Cannot find variable {name}")) -} - fn load_github_ctx() -> anyhow::Result { let event_name = load_env_var("GITHUB_EVENT_NAME")?; let commit_message = @@ -156,15 +81,6 @@ fn load_github_ctx() -> anyhow::Result { Ok(GitHubContext { event_name, branch_ref: load_env_var("GITHUB_REF")?, commit_message }) } -/// Skip CI jobs that are not supposed to be executed on the given `channel`. -fn skip_jobs(jobs: Vec, channel: &str) -> Vec { - jobs.into_iter() - .filter(|job| { - job.only_on_channel.is_none() || job.only_on_channel.as_deref() == Some(channel) - }) - .collect() -} - fn yaml_map_to_json(map: &BTreeMap) -> BTreeMap { map.into_iter() .map(|(key, value)| { @@ -176,120 +92,13 @@ fn yaml_map_to_json(map: &BTreeMap) -> BTreeMap anyhow::Result> { - let (jobs, prefix, base_env) = match run_type { - RunType::PullRequest => (db.pr_jobs.clone(), "PR", &db.envs.pr_env), - RunType::TryJob { custom_jobs } => { - let jobs = if let Some(custom_jobs) = custom_jobs { - if custom_jobs.len() > 10 { - return Err(anyhow::anyhow!( - "It is only possible to schedule up to 10 custom jobs, received {} custom jobs", - custom_jobs.len() - )); - } - - let mut jobs = vec![]; - let mut unknown_jobs = vec![]; - for custom_job in custom_jobs { - if let Some(job) = db.find_auto_job_by_name(custom_job) { - jobs.push(job); - } else { - unknown_jobs.push(custom_job.clone()); - } - } - if !unknown_jobs.is_empty() { - return Err(anyhow::anyhow!( - "Custom job(s) `{}` not found in auto jobs", - unknown_jobs.join(", ") - )); - } - jobs - } else { - db.try_jobs.clone() - }; - (jobs, "try", &db.envs.try_env) - } - RunType::AutoJob => (db.auto_jobs.clone(), "auto", &db.envs.auto_env), - }; - let jobs = skip_jobs(jobs, channel); - let jobs = jobs - .into_iter() - .map(|job| { - let mut env: BTreeMap = yaml_map_to_json(base_env); - env.extend(yaml_map_to_json(&job.env)); - let full_name = format!("{prefix} - {}", job.name); - - GithubActionsJob { - name: job.name, - full_name, - os: job.os, - env, - extra_keys: yaml_map_to_json(&job.extra_keys), - } - }) - .collect(); - - Ok(jobs) -} - -fn calculate_job_matrix( - db: JobDatabase, - gh_ctx: GitHubContext, - channel: &str, -) -> anyhow::Result<()> { - let run_type = gh_ctx.get_run_type().ok_or_else(|| { - anyhow::anyhow!("Cannot determine the type of workflow that is being executed") - })?; - eprintln!("Run type: {run_type:?}"); - - let jobs = calculate_jobs(&run_type, &db, channel)?; - if jobs.is_empty() { - return Err(anyhow::anyhow!("Computed job list is empty")); - } - - let run_type = match run_type { - RunType::PullRequest => "pr", - RunType::TryJob { .. } => "try", - RunType::AutoJob => "auto", - }; - - eprintln!("Output"); - eprintln!("jobs={jobs:?}"); - eprintln!("run_type={run_type}"); - println!("jobs={}", serde_json::to_string(&jobs)?); - println!("run_type={run_type}"); - - Ok(()) -} - -fn find_linux_job<'a>(jobs: &'a [Job], name: &str) -> anyhow::Result<&'a Job> { - let Some(job) = jobs.iter().find(|j| j.name == name) else { - let available_jobs: Vec<&Job> = jobs.iter().filter(|j| j.is_linux()).collect(); - let mut available_jobs = - available_jobs.iter().map(|j| j.name.to_string()).collect::>(); - available_jobs.sort(); - return Err(anyhow::anyhow!( - "Job {name} not found. The following jobs are available:\n{}", - available_jobs.join(", ") - )); - }; - if !job.is_linux() { - return Err(anyhow::anyhow!("Only Linux jobs can be executed locally")); - } - - Ok(job) -} - fn run_workflow_locally(db: JobDatabase, job_type: JobType, name: String) -> anyhow::Result<()> { let jobs = match job_type { JobType::Auto => &db.auto_jobs, JobType::PR => &db.pr_jobs, }; - let job = find_linux_job(jobs, &name).with_context(|| format!("Cannot find job {name}"))?; + let job = + jobs::find_linux_job(jobs, &name).with_context(|| format!("Cannot find job {name}"))?; let mut custom_env: BTreeMap = BTreeMap::new(); // Replicate src/ci/scripts/setup-environment.sh @@ -321,6 +130,83 @@ fn run_workflow_locally(db: JobDatabase, job_type: JobType, name: String) -> any if !result.success() { Err(anyhow::anyhow!("Job failed")) } else { Ok(()) } } +fn upload_ci_metrics(cpu_usage_csv: &Path) -> anyhow::Result<()> { + let usage = load_cpu_usage(cpu_usage_csv).context("Cannot load CPU usage from input CSV")?; + eprintln!("CPU usage\n{usage:?}"); + + let avg = if !usage.is_empty() { usage.iter().sum::() / usage.len() as f64 } else { 0.0 }; + eprintln!("CPU usage average: {avg}"); + + upload_datadog_metric("avg-cpu-usage", avg).context("Cannot upload Datadog metric")?; + + Ok(()) +} + +fn postprocess_metrics( + metrics_path: PathBuf, + parent: Option, + job_name: Option, +) -> anyhow::Result<()> { + let metrics = load_metrics(&metrics_path)?; + + let mut job_info_resolver = JobInfoResolver::new(); + if let (Some(parent), Some(job_name)) = (parent, job_name) { + // This command is executed also on PR builds, which might not have parent metrics + // available, because some PR jobs don't run on auto builds, and PR jobs do not upload metrics + // due to missing permissions. + // To avoid having to detect if this is a PR job, and to avoid having failed steps in PR jobs, + // we simply print an error if the parent metrics were not found, but otherwise exit + // successfully. + match download_job_metrics(&job_name, &parent).context("cannot download parent metrics") { + Ok(parent_metrics) => { + output_bootstrap_stats(&metrics, Some(&parent_metrics)); + + let job_metrics = HashMap::from([( + job_name, + JobMetrics { parent: Some(parent_metrics), current: metrics }, + )]); + output_test_diffs(&job_metrics, &mut job_info_resolver); + return Ok(()); + } + Err(error) => { + eprintln!( + "Metrics for job `{job_name}` and commit `{parent}` not found: {error:?}" + ); + } + } + } + + output_bootstrap_stats(&metrics, None); + + Ok(()) +} + +fn post_merge_report(db: JobDatabase, current: String, parent: String) -> anyhow::Result<()> { + let metrics = download_auto_job_metrics(&db, Some(&parent), ¤t)?; + + println!("\nComparing {parent} (parent) -> {current} (this PR)\n"); + + let mut job_info_resolver = JobInfoResolver::new(); + output_test_diffs(&metrics, &mut job_info_resolver); + + output_details("Test dashboard", || { + println!( + r#"Run + +```bash +cargo run --manifest-path src/ci/citool/Cargo.toml -- \ + test-dashboard {current} --output-dir test-dashboard +``` +And then open `test-dashboard/index.html` in your browser to see an overview of all executed tests. +"# + ); + }); + + output_largest_duration_changes(&metrics, &mut job_info_resolver); + + Ok(()) +} + #[derive(clap::Parser)] enum Args { /// Calculate a list of jobs that should be executed on CI. @@ -338,10 +224,44 @@ enum Args { #[clap(long = "type", default_value = "auto")] job_type: JobType, }, + /// Postprocess the metrics.json file generated by bootstrap and output + /// various statistics. + /// If `--parent` and `--job-name` are provided, also display a diff + /// against previous metrics that are downloaded from CI. + PostprocessMetrics { + /// Path to the metrics.json file + metrics_path: PathBuf, + /// A parent SHA against which to compare. + #[clap(long, requires("job_name"))] + parent: Option, + /// The name of the current job. + #[clap(long, requires("parent"))] + job_name: Option, + }, + /// Upload CI metrics to Datadog. + UploadBuildMetrics { + /// Path to a CSV containing the CI job CPU usage. + cpu_usage_csv: PathBuf, + }, + /// Generate a report of test execution changes between two rustc commits. + PostMergeReport { + /// Parent commit to use as a base of the comparison. + parent: String, + /// Current commit that will be compared to `parent`. + current: String, + }, + /// Generate a directory containing a HTML dashboard of test results from a CI run. + TestDashboard { + /// Commit SHA that was tested on CI to analyze. + current: String, + /// Output path for the HTML directory. + #[clap(long)] + output_dir: PathBuf, + }, } #[derive(clap::ValueEnum, Clone)] -enum JobType { +pub enum JobType { /// Merge attempt ("auto") job Auto, /// Pull request job @@ -351,30 +271,41 @@ enum JobType { fn main() -> anyhow::Result<()> { let args = Args::parse(); let default_jobs_file = Path::new(JOBS_YML_PATH); - let load_db = |jobs_path| load_job_db(jobs_path).context("Cannot load jobs.yml"); + let load_db = |jobs_path| { + let db = utils::read_to_string(jobs_path)?; + Ok::<_, anyhow::Error>(jobs::load_job_db(&db).context("Cannot load jobs.yml")?) + }; match args { Args::CalculateJobMatrix { jobs_file } => { let jobs_path = jobs_file.as_deref().unwrap_or(default_jobs_file); let gh_ctx = load_github_ctx() .context("Cannot load environment variables from GitHub Actions")?; - let channel = read_to_string(Path::new(CI_DIRECTORY).join("channel")) + let channel = utils::read_to_string(Path::new(CI_DIRECTORY).join("channel")) .context("Cannot read channel file")? .trim() .to_string(); - calculate_job_matrix(load_db(jobs_path)?, gh_ctx, &channel) + jobs::calculate_job_matrix(load_db(jobs_path)?, gh_ctx, &channel) .context("Failed to calculate job matrix")?; } Args::RunJobLocally { job_type, name } => { - run_workflow_locally(load_db(default_jobs_file)?, job_type, name)? + run_workflow_locally(load_db(default_jobs_file)?, job_type, name)?; + } + Args::UploadBuildMetrics { cpu_usage_csv } => { + upload_ci_metrics(&cpu_usage_csv)?; + } + Args::PostprocessMetrics { metrics_path, parent, job_name } => { + postprocess_metrics(metrics_path, parent, job_name)?; + } + Args::PostMergeReport { current, parent } => { + post_merge_report(load_db(&default_jobs_file)?, current, parent)?; + } + Args::TestDashboard { current, output_dir } => { + let db = load_db(&default_jobs_file)?; + generate_test_dashboard(db, ¤t, &output_dir)?; } } Ok(()) } - -fn read_to_string>(path: P) -> anyhow::Result { - let error = format!("Cannot read file {:?}", path.as_ref()); - std::fs::read_to_string(path).context(error) -} diff --git a/src/ci/citool/src/metrics.rs b/src/ci/citool/src/metrics.rs new file mode 100644 index 0000000000000..3d8b1ad84cf72 --- /dev/null +++ b/src/ci/citool/src/metrics.rs @@ -0,0 +1,115 @@ +use std::collections::HashMap; +use std::path::{Path, PathBuf}; + +use anyhow::Context; +use build_helper::metrics::{JsonNode, JsonRoot, TestSuite}; + +use crate::jobs::JobDatabase; + +pub type JobName = String; + +pub fn get_test_suites(metrics: &JsonRoot) -> Vec<&TestSuite> { + fn visit_test_suites<'a>(nodes: &'a [JsonNode], suites: &mut Vec<&'a TestSuite>) { + for node in nodes { + match node { + JsonNode::RustbuildStep { children, .. } => { + visit_test_suites(&children, suites); + } + JsonNode::TestSuite(suite) => { + suites.push(&suite); + } + } + } + } + + let mut suites = vec![]; + for invocation in &metrics.invocations { + visit_test_suites(&invocation.children, &mut suites); + } + suites +} + +pub fn load_metrics(path: &Path) -> anyhow::Result { + let metrics = std::fs::read_to_string(path) + .with_context(|| format!("Cannot read JSON metrics from {path:?}"))?; + let metrics: JsonRoot = serde_json::from_str(&metrics) + .with_context(|| format!("Cannot deserialize JSON metrics from {path:?}"))?; + Ok(metrics) +} + +pub struct JobMetrics { + pub parent: Option, + pub current: JsonRoot, +} + +/// Download before/after metrics for all auto jobs in the job database. +/// `parent` and `current` should be commit SHAs. +pub fn download_auto_job_metrics( + job_db: &JobDatabase, + parent: Option<&str>, + current: &str, +) -> anyhow::Result> { + let mut jobs = HashMap::default(); + + for job in &job_db.auto_jobs { + eprintln!("Downloading metrics of job {}", job.name); + let metrics_parent = + parent.and_then(|parent| match download_job_metrics(&job.name, parent) { + Ok(metrics) => Some(metrics), + Err(error) => { + eprintln!( + r#"Did not find metrics for job `{}` at `{parent}`: {error:?}. +Maybe it was newly added?"#, + job.name + ); + None + } + }); + let metrics_current = download_job_metrics(&job.name, current)?; + jobs.insert( + job.name.clone(), + JobMetrics { parent: metrics_parent, current: metrics_current }, + ); + } + Ok(jobs) +} + +pub fn download_job_metrics(job_name: &str, sha: &str) -> anyhow::Result { + // Best effort cache to speed-up local re-executions of citool + let cache_path = PathBuf::from(".citool-cache").join(sha).join(format!("{job_name}.json")); + if cache_path.is_file() { + if let Ok(metrics) = std::fs::read_to_string(&cache_path) + .map_err(|err| err.into()) + .and_then(|data| anyhow::Ok::(serde_json::from_str::(&data)?)) + { + return Ok(metrics); + } + } + + let url = get_metrics_url(job_name, sha); + let mut response = ureq::get(&url).call()?; + if !response.status().is_success() { + return Err(anyhow::anyhow!( + "Cannot fetch metrics from {url}: {}\n{}", + response.status(), + response.body_mut().read_to_string()? + )); + } + let data: JsonRoot = response + .body_mut() + .read_json() + .with_context(|| anyhow::anyhow!("cannot deserialize metrics from {url}"))?; + + if let Ok(_) = std::fs::create_dir_all(cache_path.parent().unwrap()) { + if let Ok(data) = serde_json::to_string(&data) { + let _ = std::fs::write(cache_path, data); + } + } + + Ok(data) +} + +fn get_metrics_url(job_name: &str, sha: &str) -> String { + let suffix = if job_name.ends_with("-alt") { "-alt" } else { "" }; + format!("https://ci-artifacts.rust-lang.org/rustc-builds{suffix}/{sha}/metrics-{job_name}.json") +} diff --git a/src/ci/citool/src/test_dashboard.rs b/src/ci/citool/src/test_dashboard.rs new file mode 100644 index 0000000000000..8fbd0d3f200d4 --- /dev/null +++ b/src/ci/citool/src/test_dashboard.rs @@ -0,0 +1,216 @@ +use std::collections::{BTreeMap, HashMap}; +use std::fs::File; +use std::io::BufWriter; +use std::path::{Path, PathBuf}; + +use askama::Template; +use build_helper::metrics::{TestOutcome, TestSuiteMetadata}; + +use crate::jobs::JobDatabase; +use crate::metrics::{JobMetrics, JobName, download_auto_job_metrics, get_test_suites}; +use crate::utils::normalize_path_delimiters; + +/// Generate a set of HTML files into a directory that contain a dashboard of test results. +pub fn generate_test_dashboard( + db: JobDatabase, + current: &str, + output_dir: &Path, +) -> anyhow::Result<()> { + let metrics = download_auto_job_metrics(&db, None, current)?; + let suites = gather_test_suites(&metrics); + + std::fs::create_dir_all(output_dir)?; + + let test_count = suites.test_count(); + write_page(output_dir, "index.html", &TestSuitesPage { suites, test_count })?; + + Ok(()) +} + +fn write_page(dir: &Path, name: &str, template: &T) -> anyhow::Result<()> { + let mut file = BufWriter::new(File::create(dir.join(name))?); + Template::write_into(template, &mut file)?; + Ok(()) +} + +fn gather_test_suites(job_metrics: &HashMap) -> TestSuites { + struct CoarseTestSuite<'a> { + tests: BTreeMap>, + } + + let mut suites: HashMap = HashMap::new(); + + // First, gather tests from all jobs, stages and targets, and aggregate them per suite + // Only work with compiletest suites. + for (job, metrics) in job_metrics { + let test_suites = get_test_suites(&metrics.current); + for suite in test_suites { + let (suite_name, stage, target) = match &suite.metadata { + TestSuiteMetadata::CargoPackage { .. } => { + continue; + } + TestSuiteMetadata::Compiletest { suite, stage, target, .. } => { + (suite.clone(), *stage, target) + } + }; + let suite_entry = suites + .entry(suite_name.clone()) + .or_insert_with(|| CoarseTestSuite { tests: Default::default() }); + let test_metadata = TestMetadata { job, stage, target }; + + for test in &suite.tests { + let test_name = normalize_test_name(&test.name, &suite_name); + let (test_name, variant_name) = match test_name.rsplit_once('#') { + Some((name, variant)) => (name.to_string(), variant.to_string()), + None => (test_name, "".to_string()), + }; + let test_entry = suite_entry + .tests + .entry(test_name.clone()) + .or_insert_with(|| Test { revisions: Default::default() }); + let variant_entry = test_entry + .revisions + .entry(variant_name) + .or_insert_with(|| TestResults { passed: vec![], ignored: vec![] }); + + match test.outcome { + TestOutcome::Passed => { + variant_entry.passed.push(test_metadata); + } + TestOutcome::Ignored { ignore_reason: _ } => { + variant_entry.ignored.push(test_metadata); + } + TestOutcome::Failed => { + eprintln!("Warning: failed test {test_name}"); + } + } + } + } + } + + // Then, split the suites per directory + let mut suites = suites.into_iter().collect::>(); + suites.sort_by(|a, b| a.0.cmp(&b.0)); + + let suites = suites + .into_iter() + .map(|(suite_name, suite)| TestSuite { group: build_test_group(&suite_name, suite.tests) }) + .collect(); + + TestSuites { suites } +} + +/// Recursively expand a test group based on filesystem hierarchy. +fn build_test_group<'a>(name: &str, tests: BTreeMap>) -> TestGroup<'a> { + let mut root_tests = vec![]; + let mut subdirs: BTreeMap>> = Default::default(); + + // Split tests into root tests and tests located in subdirectories + for (name, test) in tests { + let mut components = Path::new(&name).components().peekable(); + let subdir = components.next().unwrap(); + + if components.peek().is_none() { + // This is a root test + root_tests.push((name, test)); + } else { + // This is a test in a nested directory + let subdir_tests = + subdirs.entry(subdir.as_os_str().to_str().unwrap().to_string()).or_default(); + let test_name = + components.into_iter().collect::().to_str().unwrap().to_string(); + subdir_tests.insert(test_name, test); + } + } + let dirs = subdirs + .into_iter() + .map(|(name, tests)| { + let group = build_test_group(&name, tests); + (name, group) + }) + .collect(); + + TestGroup { name: name.to_string(), root_tests, groups: dirs } +} + +/// Compiletest tests start with `[suite] tests/[suite]/a/b/c...`. +/// Remove the `[suite] tests/[suite]/` prefix so that we can find the filesystem path. +/// Also normalizes path delimiters. +fn normalize_test_name(name: &str, suite_name: &str) -> String { + let name = normalize_path_delimiters(name); + let name = name.as_ref(); + let name = name.strip_prefix(&format!("[{suite_name}]")).unwrap_or(name).trim(); + let name = name.strip_prefix("tests/").unwrap_or(name); + let name = name.strip_prefix(suite_name).unwrap_or(name); + name.trim_start_matches("/").to_string() +} + +struct TestSuites<'a> { + suites: Vec>, +} + +impl<'a> TestSuites<'a> { + fn test_count(&self) -> u64 { + self.suites.iter().map(|suite| suite.group.test_count()).sum::() + } +} + +struct TestSuite<'a> { + group: TestGroup<'a>, +} + +struct TestResults<'a> { + passed: Vec>, + ignored: Vec>, +} + +struct Test<'a> { + revisions: BTreeMap>, +} + +impl<'a> Test<'a> { + /// If this is a test without revisions, it will have a single entry in `revisions` with + /// an empty string as the revision name. + fn single_test(&self) -> Option<&TestResults<'a>> { + if self.revisions.len() == 1 { + self.revisions.iter().next().take_if(|e| e.0.is_empty()).map(|e| e.1) + } else { + None + } + } +} + +#[derive(Clone, Copy)] +#[allow(dead_code)] +struct TestMetadata<'a> { + job: &'a str, + stage: u32, + target: &'a str, +} + +// We have to use a template for the TestGroup instead of a macro, because +// macros cannot be recursive in askama at the moment. +#[derive(Template)] +#[template(path = "test_group.askama")] +/// Represents a group of tests +struct TestGroup<'a> { + name: String, + /// Tests located directly in this directory + root_tests: Vec<(String, Test<'a>)>, + /// Nested directories with additional tests + groups: Vec<(String, TestGroup<'a>)>, +} + +impl<'a> TestGroup<'a> { + fn test_count(&self) -> u64 { + let root = self.root_tests.len() as u64; + self.groups.iter().map(|(_, group)| group.test_count()).sum::() + root + } +} + +#[derive(Template)] +#[template(path = "test_suites.askama")] +struct TestSuitesPage<'a> { + suites: TestSuites<'a>, + test_count: u64, +} diff --git a/src/ci/citool/src/utils.rs b/src/ci/citool/src/utils.rs new file mode 100644 index 0000000000000..0367d349a1ef4 --- /dev/null +++ b/src/ci/citool/src/utils.rs @@ -0,0 +1,36 @@ +use std::borrow::Cow; +use std::path::Path; + +use anyhow::Context; + +pub fn load_env_var(name: &str) -> anyhow::Result { + std::env::var(name).with_context(|| format!("Cannot find environment variable `{name}`")) +} + +pub fn read_to_string>(path: P) -> anyhow::Result { + std::fs::read_to_string(&path).with_context(|| format!("Cannot read file {:?}", path.as_ref())) +} + +pub fn pluralize(text: &str, count: usize) -> String { + if count == 1 { text.to_string() } else { format!("{text}s") } +} + +/// Outputs a HTML
section with the provided summary. +/// Output printed by `func` will be contained within the section. +pub fn output_details(summary: &str, func: F) +where + F: FnOnce(), +{ + println!( + r"
+{summary} +" + ); + func(); + println!("
\n"); +} + +/// Normalizes Windows-style path delimiters to Unix-style paths. +pub fn normalize_path_delimiters(name: &str) -> Cow { + if name.contains("\\") { name.replace('\\', "/").into() } else { name.into() } +} diff --git a/src/ci/citool/templates/layout.askama b/src/ci/citool/templates/layout.askama new file mode 100644 index 0000000000000..3b3b6f23741d4 --- /dev/null +++ b/src/ci/citool/templates/layout.askama @@ -0,0 +1,22 @@ + + + + Rust CI Test Dashboard + + + + +{% block content %}{% endblock %} +{% block scripts %}{% endblock %} + + diff --git a/src/ci/citool/templates/test_group.askama b/src/ci/citool/templates/test_group.askama new file mode 100644 index 0000000000000..95731103f3b9d --- /dev/null +++ b/src/ci/citool/templates/test_group.askama @@ -0,0 +1,42 @@ +{% macro test_result(r) -%} +passed: {{ r.passed.len() }}, ignored: {{ r.ignored.len() }} +{%- endmacro %} + +
  • +
    +{{ name }} ({{ test_count() }} test{{ test_count() | pluralize }}{% if !root_tests.is_empty() && root_tests.len() as u64 != test_count() -%} + , {{ root_tests.len() }} root test{{ root_tests.len() | pluralize }} +{%- endif %}{% if !groups.is_empty() -%} + , {{ groups.len() }} subdir{{ groups.len() | pluralize }} +{%- endif %}) + + +{% if !groups.is_empty() %} +
      + {% for (dir_name, subgroup) in groups %} + {{ subgroup|safe }} + {% endfor %} +
    +{% endif %} + +{% if !root_tests.is_empty() %} +
      + {% for (name, test) in root_tests %} +
    • + {% if let Some(result) = test.single_test() %} + {{ name }} ({% call test_result(result) %}) + {% else %} + {{ name }} ({{ test.revisions.len() }} revision{{ test.revisions.len() | pluralize }}) +
        + {% for (revision, result) in test.revisions %} +
      • #{{ revision }} ({% call test_result(result) %})
      • + {% endfor %} +
      + {% endif %} +
    • + {% endfor %} +
    +{% endif %} + +
    +
  • diff --git a/src/ci/citool/templates/test_suites.askama b/src/ci/citool/templates/test_suites.askama new file mode 100644 index 0000000000000..4997f6a3f1c9a --- /dev/null +++ b/src/ci/citool/templates/test_suites.askama @@ -0,0 +1,108 @@ +{% extends "layout.askama" %} + +{% block content %} +

    Rust CI test dashboard

    +
    +Here's how to interpret the "passed" and "ignored" counts: +the count includes all combinations of "stage" x "target" x "CI job where the test was executed or ignored". +
    +
    +
    +
    +
    Total tests: {{ test_count }}
    +
    + To find tests that haven't been executed anywhere, click on "Open all" and search for "passed: 0". +
    +
    +
    + + +
    +
    + +
      + {% for suite in suites.suites %} + {{ suite.group|safe }} + {% endfor %} +
    +
    +{% endblock %} + +{% block styles %} +h1 { + text-align: center; + color: #333333; + margin-bottom: 30px; +} + +.summary { + display: flex; + justify-content: space-between; +} + +.test-count { + font-size: 1.2em; +} + +.test-suites { + background: white; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + padding: 20px; +} + +ul { + padding-left: 0; +} + +li { + list-style: none; + padding-left: 20px; +} +summary { + margin-bottom: 5px; + padding: 6px; + background-color: #F4F4F4; + border: 1px solid #ddd; + border-radius: 4px; + cursor: pointer; +} +summary:hover { + background-color: #CFCFCF; +} + +/* Style the disclosure triangles */ +details > summary { + list-style: none; + position: relative; +} + +details > summary::before { + content: "▶"; + position: absolute; + left: -15px; + transform: rotate(0); + transition: transform 0.2s; +} + +details[open] > summary::before { + transform: rotate(90deg); +} +{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/src/ci/citool/tests/jobs.rs b/src/ci/citool/tests/jobs.rs index 1d81d58f89311..c644f885be30c 100644 --- a/src/ci/citool/tests/jobs.rs +++ b/src/ci/citool/tests/jobs.rs @@ -14,10 +14,10 @@ fn auto_jobs() { #[test] fn try_jobs() { let stdout = get_matrix("push", "commit", "refs/heads/try"); - insta::assert_snapshot!(stdout, @r#" + insta::assert_snapshot!(stdout, @r###" jobs=[{"name":"dist-x86_64-linux","full_name":"try - dist-x86_64-linux","os":"ubuntu-22.04-16core-64gb","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_TRY_BUILD":1,"TOOLSTATE_PUBLISH":1}}] run_type=try - "#); + "###); } #[test] @@ -30,17 +30,17 @@ try-job: aarch64-gnu try-job: dist-i686-msvc"#, "refs/heads/try", ); - insta::assert_snapshot!(stdout, @r#" - jobs=[{"name":"aarch64-gnu","full_name":"try - aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DIST_TRY_BUILD":1,"TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"dist-i686-msvc","full_name":"try - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"DIST_TRY_BUILD":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}}] + insta::assert_snapshot!(stdout, @r###" + jobs=[{"name":"aarch64-gnu","full_name":"try - aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"dist-i686-msvc","full_name":"try - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}}] run_type=try - "#); + "###); } #[test] fn pr_jobs() { let stdout = get_matrix("pull_request", "commit", "refs/heads/pr/1234"); insta::assert_snapshot!(stdout, @r#" - jobs=[{"name":"mingw-check","full_name":"PR - mingw-check","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"free_disk":true},{"name":"mingw-check-tidy","full_name":"PR - mingw-check-tidy","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"continue_on_error":true,"free_disk":true}] + jobs=[{"name":"mingw-check","full_name":"PR - mingw-check","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"free_disk":true},{"name":"mingw-check-tidy","full_name":"PR - mingw-check-tidy","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"continue_on_error":true,"free_disk":true,"doc_url":"https://foo.bar"}] run_type=pr "#); } diff --git a/src/ci/citool/tests/test-jobs.yml b/src/ci/citool/tests/test-jobs.yml index 56b9ced20719e..d81be88b70872 100644 --- a/src/ci/citool/tests/test-jobs.yml +++ b/src/ci/citool/tests/test-jobs.yml @@ -27,7 +27,7 @@ runners: <<: *base-job envs: env-x86_64-apple-tests: &env-x86_64-apple-tests - SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact + SCRIPT: ./x.py check compiletest --set build.compiletest-use-stage0-libtest=true && ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 # Ensure that host tooling is tested on our minimum supported macOS version. @@ -53,13 +53,6 @@ envs: try: <<: *production - # The following env var activates faster `try` builds in `opt-dist` by, e.g. - # - building only the more commonly useful components (we rarely need e.g. rust-docs in try - # builds) - # - not running `opt-dist`'s post-optimization smoke tests on the resulting toolchain - # - # If you *want* these to happen however, temporarily comment it before triggering a try build. - DIST_TRY_BUILD: 1 auto: <<: *production @@ -75,6 +68,7 @@ pr: <<: *job-linux-4c - name: mingw-check-tidy continue_on_error: true + doc_url: https://foo.bar <<: *job-linux-4c # Jobs that run when you perform a try build (@bors try) diff --git a/src/ci/cpu-usage-over-time.py b/src/ci/cpu-usage-over-time.py index 3d9dc86734fcc..19a39f349532c 100755 --- a/src/ci/cpu-usage-over-time.py +++ b/src/ci/cpu-usage-over-time.py @@ -170,7 +170,7 @@ def idle_since(self, prev): while True: time.sleep(1) next_state = State() - now = datetime.datetime.utcnow().isoformat() + now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None).isoformat() idle = next_state.idle_since(cur_state) print("%s,%s" % (now, idle)) sys.stdout.flush() diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index a1a3a1c37ced7..488a6a2bce122 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -11,12 +11,12 @@ the [`jobs.yml`](../github-actions/jobs.yml) file. To run a specific CI job locally, you can use the `citool` Rust crate: ``` -cargo --manifest-path src/ci/citool/Cargo.toml run run-local +cargo run --manifest-path src/ci/citool/Cargo.toml run-local ``` -For example, to run the `x86_64-gnu-llvm-18-1` job: +For example, to run the `x86_64-gnu-llvm-19-1` job: ``` -cargo --manifest-path src/ci/citool/Cargo.toml run run-local x86_64-gnu-llvm-18-1 +cargo run --manifest-path src/ci/citool/Cargo.toml run-local x86_64-gnu-llvm-19-1 ``` The job will output artifacts in an `obj/` dir at the root of a repository. Note @@ -27,10 +27,10 @@ Docker image executed in the given CI job. while locally, to the `obj/` directory. This is primarily to prevent strange linker errors when using multiple Docker images. -For some Linux workflows (for example `x86_64-gnu-llvm-18-N`), the process is more involved. You will need to see which script is executed for the given workflow inside the [`jobs.yml`](../github-actions/jobs.yml) file and pass it through the `DOCKER_SCRIPT` environment variable. For example, to reproduce the `x86_64-gnu-llvm-18-3` workflow, you can run the following script: +For some Linux workflows (for example `x86_64-gnu-llvm-19-N`), the process is more involved. You will need to see which script is executed for the given workflow inside the [`jobs.yml`](../github-actions/jobs.yml) file and pass it through the `DOCKER_SCRIPT` environment variable. For example, to reproduce the `x86_64-gnu-llvm-19-3` workflow, you can run the following script: ``` -DOCKER_SCRIPT=x86_64-gnu-llvm3.sh ./src/ci/docker/run.sh x86_64-gnu-llvm-18 +DOCKER_SCRIPT=x86_64-gnu-llvm3.sh ./src/ci/docker/run.sh x86_64-gnu-llvm-19 ``` ## Local Development diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile index eb39861d8c703..cf030f6830e5b 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile @@ -30,7 +30,6 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV RUSTBUILD_FORCE_CLANG_BASED_TESTS 1 -ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1 # llvm.use-linker conflicts with downloading CI LLVM ENV NO_DOWNLOAD_CI_LLVM 1 diff --git a/src/ci/docker/host-x86_64/arm-android/Dockerfile b/src/ci/docker/host-x86_64/arm-android/Dockerfile index aade95882685f..bc311be05808d 100644 --- a/src/ci/docker/host-x86_64/arm-android/Dockerfile +++ b/src/ci/docker/host-x86_64/arm-android/Dockerfile @@ -28,6 +28,7 @@ RUN /scripts/android-sdk.sh ENV PATH=$PATH:/android/sdk/emulator ENV PATH=$PATH:/android/sdk/tools ENV PATH=$PATH:/android/sdk/platform-tools +ENV PATH=$PATH:/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin ENV TARGETS=arm-linux-androideabi diff --git a/src/ci/docker/host-x86_64/dist-android/Dockerfile b/src/ci/docker/host-x86_64/dist-android/Dockerfile index 95fed6ee767b0..7b73326e35900 100644 --- a/src/ci/docker/host-x86_64/dist-android/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-android/Dockerfile @@ -22,6 +22,8 @@ ENV RUST_CONFIGURE_ARGS \ --android-ndk=/android/ndk/ \ --disable-docs +ENV PATH=$PATH:/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin + ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile new file mode 100644 index 0000000000000..996dacd712478 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/Dockerfile @@ -0,0 +1,35 @@ +FROM ghcr.io/rust-lang/ubuntu:22.04 + +COPY scripts/cross-apt-packages.sh /scripts/ +RUN sh /scripts/cross-apt-packages.sh + +COPY scripts/crosstool-ng.sh /scripts/ +RUN sh /scripts/crosstool-ng.sh + +WORKDIR /build + +COPY scripts/rustbuild-setup.sh /scripts/ +RUN sh /scripts/rustbuild-setup.sh +WORKDIR /tmp + +COPY scripts/crosstool-ng-build.sh /scripts/ +COPY host-x86_64/dist-arm-linux-gnueabi/arm-linux-gnueabi.defconfig /tmp/crosstool.defconfig +RUN /scripts/crosstool-ng-build.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV PATH=$PATH:/x-tools/arm-unknown-linux-gnueabi/bin + +ENV CC_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-gcc \ + AR_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-ar \ + CXX_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-g++ + +ENV HOSTS=arm-unknown-linux-gnueabi + +ENV RUST_CONFIGURE_ARGS \ + --enable-full-tools \ + --disable-docs \ + --enable-sanitizers \ + --enable-profiler +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-arm-linux/arm-linux-gnueabi.defconfig b/src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/arm-linux-gnueabi.defconfig similarity index 100% rename from src/ci/docker/host-x86_64/dist-arm-linux/arm-linux-gnueabi.defconfig rename to src/ci/docker/host-x86_64/dist-arm-linux-gnueabi/arm-linux-gnueabi.defconfig diff --git a/src/ci/docker/host-x86_64/dist-arm-linux-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux-musl/Dockerfile new file mode 100644 index 0000000000000..6e055cd2bd5aa --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-arm-linux-musl/Dockerfile @@ -0,0 +1,37 @@ +FROM ghcr.io/rust-lang/ubuntu:22.04 + +COPY scripts/cross-apt-packages.sh /scripts/ +RUN sh /scripts/cross-apt-packages.sh + +COPY scripts/crosstool-ng.sh /scripts/ +RUN sh /scripts/crosstool-ng.sh + +WORKDIR /build + +COPY scripts/musl-toolchain.sh /build/ +# We need to mitigate rust-lang/rust#34978 when compiling musl itself as well +RUN CFLAGS="-Wa,--compress-debug-sections=none -Wl,--compress-debug-sections=none" \ + CXXFLAGS="-Wa,--compress-debug-sections=none -Wl,--compress-debug-sections=none" \ + bash musl-toolchain.sh aarch64 && rm -rf build + +COPY scripts/rustbuild-setup.sh /scripts/ +RUN sh /scripts/rustbuild-setup.sh +WORKDIR /tmp + +COPY scripts/crosstool-ng-build.sh /scripts/ +COPY host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig /tmp/crosstool.defconfig +RUN /scripts/crosstool-ng-build.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV HOSTS=aarch64-unknown-linux-musl + +ENV RUST_CONFIGURE_ARGS \ + --enable-full-tools \ + --disable-docs \ + --musl-root-aarch64=/usr/local/aarch64-linux-musl \ + --enable-sanitizers \ + --enable-profiler \ + --set target.aarch64-unknown-linux-musl.crt-static=false +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig new file mode 100644 index 0000000000000..e7afdbe9d4dea --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-arm-linux-musl/arm-linux-musl.defconfig @@ -0,0 +1,13 @@ +CT_CONFIG_VERSION="4" +CT_PREFIX_DIR="/x-tools/${CT_TARGET}" +CT_USE_MIRROR=y +CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_ARCH_ARM=y +CT_ARCH_ARCH="armv6" +CT_ARCH_FLOAT_SW=y +CT_KERNEL_LINUX=y +CT_LINUX_V_3_2=y +CT_BINUTILS_V_2_32=y +CT_GLIBC_V_2_17=y +CT_GCC_V_8=y +CT_CC_LANG_CXX=y diff --git a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile deleted file mode 100644 index 420c42bc9d807..0000000000000 --- a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -FROM ubuntu:22.04 - -COPY scripts/cross-apt-packages.sh /scripts/ -RUN sh /scripts/cross-apt-packages.sh - -COPY scripts/crosstool-ng.sh /scripts/ -RUN sh /scripts/crosstool-ng.sh - -WORKDIR /build - -COPY scripts/musl-toolchain.sh /build/ -# We need to mitigate rust-lang/rust#34978 when compiling musl itself as well -RUN CFLAGS="-Wa,--compress-debug-sections=none -Wl,--compress-debug-sections=none" \ - CXXFLAGS="-Wa,--compress-debug-sections=none -Wl,--compress-debug-sections=none" \ - bash musl-toolchain.sh aarch64 && rm -rf build - -COPY scripts/rustbuild-setup.sh /scripts/ -RUN sh /scripts/rustbuild-setup.sh -WORKDIR /tmp - -COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-arm-linux/arm-linux-gnueabi.defconfig /tmp/crosstool.defconfig -RUN /scripts/crosstool-ng-build.sh - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV PATH=$PATH:/x-tools/arm-unknown-linux-gnueabi/bin - -ENV CC_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-gcc \ - AR_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-ar \ - CXX_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-g++ - -ENV HOSTS=arm-unknown-linux-gnueabi,aarch64-unknown-linux-musl - -ENV RUST_CONFIGURE_ARGS \ - --enable-full-tools \ - --disable-docs \ - --musl-root-aarch64=/usr/local/aarch64-linux-musl \ - --enable-sanitizers \ - --enable-profiler \ - --set target.aarch64-unknown-linux-musl.crt-static=false -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-ohos-aarch64/Dockerfile b/src/ci/docker/host-x86_64/dist-ohos-aarch64/Dockerfile new file mode 100644 index 0000000000000..adeaf809f4208 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-ohos-aarch64/Dockerfile @@ -0,0 +1,53 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + libssl-dev \ + pkg-config \ + xz-utils \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/ohos-sdk.sh /scripts/ +RUN sh /scripts/ohos-sdk.sh + +COPY scripts/ohos-openssl.sh /scripts/ +RUN sh /scripts/ohos-openssl.sh + +COPY scripts/ohos/aarch64-unknown-linux-ohos-clang.sh /usr/local/bin/ +COPY scripts/ohos/aarch64-unknown-linux-ohos-clang++.sh /usr/local/bin/ + +# env +ENV AARCH64_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/arm64-v8a + +ENV AARCH64_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 + +ENV TARGETS=aarch64-unknown-linux-ohos + +ENV \ + CC_aarch64_unknown_linux_ohos=/usr/local/bin/aarch64-unknown-linux-ohos-clang.sh \ + AR_aarch64_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ + CXX_aarch64_unknown_linux_ohos=/usr/local/bin/aarch64-unknown-linux-ohos-clang++.sh + +ENV RUST_CONFIGURE_ARGS \ + --enable-profiler \ + --disable-docs \ + --tools=cargo,clippy,rustdocs,rustfmt,rust-analyzer,rust-analyzer-proc-macro-srv,analysis,src,wasm-component-ld \ + --enable-extended \ + --enable-sanitizers + +ENV SCRIPT python3 ../x.py dist --host=$TARGETS --target $TARGETS + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile b/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile new file mode 100644 index 0000000000000..2a23d8aec2376 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-ohos-armv7/Dockerfile @@ -0,0 +1,53 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + libssl-dev \ + pkg-config \ + xz-utils \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/ohos-sdk.sh /scripts/ +RUN sh /scripts/ohos-sdk.sh + +COPY scripts/ohos-openssl.sh /scripts/ +RUN sh /scripts/ohos-openssl.sh + +COPY scripts/ohos/armv7-unknown-linux-ohos-clang.sh /usr/local/bin/ +COPY scripts/ohos/armv7-unknown-linux-ohos-clang++.sh /usr/local/bin/ + +# env +ENV ARMV7_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/armeabi-v7a + +ENV ARMV7_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 + +ENV TARGETS=armv7-unknown-linux-ohos + +ENV \ + CC_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang.sh \ + AR_armv7_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ + CXX_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang++.sh + +ENV RUST_CONFIGURE_ARGS \ + --enable-profiler \ + --disable-docs \ + --tools=cargo,clippy,rustdocs,rustfmt,rust-analyzer,rust-analyzer-proc-macro-srv,analysis,src,wasm-component-ld \ + --enable-extended \ + --enable-sanitizers + +ENV SCRIPT python3 ../x.py dist --host=$TARGETS --target $TARGETS + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/dist-ohos-x86_64/Dockerfile b/src/ci/docker/host-x86_64/dist-ohos-x86_64/Dockerfile new file mode 100644 index 0000000000000..98e402adf2a69 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-ohos-x86_64/Dockerfile @@ -0,0 +1,53 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + libssl-dev \ + pkg-config \ + xz-utils \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/ohos-sdk.sh /scripts/ +RUN sh /scripts/ohos-sdk.sh + +COPY scripts/ohos-openssl.sh /scripts/ +RUN sh /scripts/ohos-openssl.sh + +COPY scripts/ohos/x86_64-unknown-linux-ohos-clang.sh /usr/local/bin/ +COPY scripts/ohos/x86_64-unknown-linux-ohos-clang++.sh /usr/local/bin/ + +# env +ENV X86_64_UNKNOWN_LINUX_OHOS_OPENSSL_DIR=/opt/ohos-openssl/prelude/x86_64 + +ENV X86_64_UNKNOWN_LINUX_OHOS_OPENSSL_NO_VENDOR=1 + +ENV TARGETS=x86_64-unknown-linux-ohos + +ENV \ + CC_x86_64_unknown_linux_ohos=/usr/local/bin/x86_64-unknown-linux-ohos-clang.sh \ + AR_x86_64_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ + CXX_x86_64_unknown_linux_ohos=/usr/local/bin/x86_64-unknown-linux-ohos-clang++.sh + +ENV RUST_CONFIGURE_ARGS \ + --enable-profiler \ + --disable-docs \ + --tools=cargo,clippy,rustdocs,rustfmt,rust-analyzer,rust-analyzer-proc-macro-srv,analysis,src,wasm-component-ld \ + --enable-extended \ + --enable-sanitizers + +ENV SCRIPT python3 ../x.py dist --host=$TARGETS --target $TARGETS + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/dist-ohos/Dockerfile b/src/ci/docker/host-x86_64/dist-ohos/Dockerfile deleted file mode 100644 index bbbf0b3adf2a2..0000000000000 --- a/src/ci/docker/host-x86_64/dist-ohos/Dockerfile +++ /dev/null @@ -1,57 +0,0 @@ -FROM ubuntu:24.04 - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - make \ - ninja-build \ - file \ - curl \ - ca-certificates \ - python3 \ - git \ - cmake \ - sudo \ - gdb \ - libssl-dev \ - pkg-config \ - xz-utils \ - unzip \ - && rm -rf /var/lib/apt/lists/* - -COPY scripts/ohos-sdk.sh /scripts/ -RUN sh /scripts/ohos-sdk.sh - -COPY scripts/ohos/aarch64-unknown-linux-ohos-clang.sh /usr/local/bin/ -COPY scripts/ohos/aarch64-unknown-linux-ohos-clang++.sh /usr/local/bin/ -COPY scripts/ohos/armv7-unknown-linux-ohos-clang.sh /usr/local/bin/ -COPY scripts/ohos/armv7-unknown-linux-ohos-clang++.sh /usr/local/bin/ -COPY scripts/ohos/x86_64-unknown-linux-ohos-clang.sh /usr/local/bin/ -COPY scripts/ohos/x86_64-unknown-linux-ohos-clang++.sh /usr/local/bin/ - -# env -ENV TARGETS=aarch64-unknown-linux-ohos -ENV TARGETS=$TARGETS,armv7-unknown-linux-ohos -ENV TARGETS=$TARGETS,x86_64-unknown-linux-ohos - -ENV \ - CC_aarch64_unknown_linux_ohos=/usr/local/bin/aarch64-unknown-linux-ohos-clang.sh \ - AR_aarch64_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ - CXX_aarch64_unknown_linux_ohos=/usr/local/bin/aarch64-unknown-linux-ohos-clang++.sh -ENV \ - CC_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang.sh \ - AR_armv7_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ - CXX_armv7_unknown_linux_ohos=/usr/local/bin/armv7-unknown-linux-ohos-clang++.sh -ENV \ - CC_x86_64_unknown_linux_ohos=/usr/local/bin/x86_64-unknown-linux-ohos-clang.sh \ - AR_x86_64_unknown_linux_ohos=/opt/ohos-sdk/native/llvm/bin/llvm-ar \ - CXX_x86_64_unknown_linux_ohos=/usr/local/bin/x86_64-unknown-linux-ohos-clang++.sh - -ENV RUST_CONFIGURE_ARGS \ - --enable-profiler \ - --disable-docs - -ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile new file mode 100644 index 0000000000000..d2f1b9400ad84 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile @@ -0,0 +1,41 @@ +FROM ubuntu:22.04 + +COPY scripts/cross-apt-packages.sh /scripts/ +RUN sh /scripts/cross-apt-packages.sh + +COPY scripts/crosstool-ng.sh /scripts/ +RUN sh /scripts/crosstool-ng.sh + +COPY scripts/rustbuild-setup.sh /scripts/ +RUN sh /scripts/rustbuild-setup.sh + +WORKDIR /tmp + +COPY scripts/crosstool-ng-build.sh /scripts/ +COPY host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig /tmp/crosstool.defconfig +RUN /scripts/crosstool-ng-build.sh + +WORKDIR /build + +RUN apt-get install -y --no-install-recommends rpm2cpio cpio +COPY scripts/shared.sh scripts/build-powerpc64le-toolchain.sh /build/ +RUN ./build-powerpc64le-toolchain.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV \ + AR_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-ar \ + CC_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-gcc \ + CXX_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-g++ + +ENV HOSTS=powerpc64le-unknown-linux-gnu + +ENV RUST_CONFIGURE_ARGS \ + --enable-extended \ + --enable-full-tools \ + --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-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig new file mode 100644 index 0000000000000..363e5850894e3 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig @@ -0,0 +1,14 @@ +CT_CONFIG_VERSION="4" +CT_EXPERIMENTAL=y +CT_PREFIX_DIR="/x-tools/${CT_TARGET}" +CT_USE_MIRROR=y +CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" +CT_ARCH_POWERPC=y +CT_ARCH_LE=y +CT_ARCH_64=y +# CT_DEMULTILIB is not set +CT_ARCH_ARCH="powerpc64le" +CT_KERNEL_LINUX=y +CT_LINUX_V_4_19=y +CT_CC_LANG_CXX=y +CT_GETTEXT_NEEDED=y diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile new file mode 100644 index 0000000000000..f045b2a5f6558 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile @@ -0,0 +1,45 @@ +FROM ubuntu:22.04 + +COPY scripts/cross-apt-packages.sh /scripts/ +RUN sh /scripts/cross-apt-packages.sh + +COPY scripts/crosstool-ng.sh /scripts/ +RUN sh /scripts/crosstool-ng.sh + +COPY scripts/rustbuild-setup.sh /scripts/ +RUN sh /scripts/rustbuild-setup.sh + +WORKDIR /tmp + +COPY scripts/crosstool-ng-build.sh /scripts/ +COPY host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig /tmp/crosstool.defconfig +RUN /scripts/crosstool-ng-build.sh + +WORKDIR /build + +RUN apt-get install -y --no-install-recommends rpm2cpio cpio +COPY scripts/shared.sh scripts/build-powerpc64le-toolchain.sh /build/ +RUN ./build-powerpc64le-toolchain.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV PATH=$PATH:/x-tools/powerpc64le-unknown-linux-musl/bin + +ENV \ + AR_powerpc64le_unknown_linux_musl=powerpc64le-unknown-linux-musl-ar \ + CC_powerpc64le_unknown_linux_musl=powerpc64le-unknown-linux-musl-gcc \ + CXX_powerpc64le_unknown_linux_musl=powerpc64le-unknown-linux-musl-g++ + +ENV HOSTS=powerpc64le-unknown-linux-musl + +ENV RUST_CONFIGURE_ARGS \ + --enable-extended \ + --enable-full-tools \ + --enable-profiler \ + --enable-sanitizers \ + --disable-docs \ + --set target.powerpc64le-unknown-linux-musl.crt-static=false \ + --musl-root-powerpc64le=/x-tools/powerpc64le-unknown-linux-musl/powerpc64le-unknown-linux-musl/sysroot/usr + +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/powerpc64le-unknown-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig similarity index 100% rename from src/ci/docker/host-x86_64/dist-powerpc64le-linux/powerpc64le-unknown-linux-musl.defconfig rename to src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile deleted file mode 100644 index cb20f43cff706..0000000000000 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile +++ /dev/null @@ -1,48 +0,0 @@ -FROM ubuntu:22.04 - -COPY scripts/cross-apt-packages.sh /scripts/ -RUN sh /scripts/cross-apt-packages.sh - -COPY scripts/crosstool-ng.sh /scripts/ -RUN sh /scripts/crosstool-ng.sh - -COPY scripts/rustbuild-setup.sh /scripts/ -RUN sh /scripts/rustbuild-setup.sh - -WORKDIR /tmp - -COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-powerpc64le-linux/powerpc64le-unknown-linux-musl.defconfig /tmp/crosstool.defconfig -RUN /scripts/crosstool-ng-build.sh - -WORKDIR /build - -RUN apt-get install -y --no-install-recommends rpm2cpio cpio -COPY scripts/shared.sh host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh /build/ -RUN ./build-powerpc64le-toolchain.sh - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV PATH=$PATH:/x-tools/powerpc64le-unknown-linux-musl/bin - -ENV \ - AR_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-ar \ - CC_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-gcc \ - CXX_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-g++ \ - AR_powerpc64le_unknown_linux_musl=powerpc64le-unknown-linux-musl-ar \ - CC_powerpc64le_unknown_linux_musl=powerpc64le-unknown-linux-musl-gcc \ - CXX_powerpc64le_unknown_linux_musl=powerpc64le-unknown-linux-musl-g++ - -ENV HOSTS=powerpc64le-unknown-linux-gnu,powerpc64le-unknown-linux-musl - -ENV RUST_CONFIGURE_ARGS \ - --enable-extended \ - --enable-full-tools \ - --enable-profiler \ - --enable-sanitizers \ - --disable-docs \ - --set target.powerpc64le-unknown-linux-musl.crt-static=false \ - --musl-root-powerpc64le=/x-tools/powerpc64le-unknown-linux-musl/powerpc64le-unknown-linux-musl/sysroot/usr - -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 0b4682ac32ba0..bedf45c8630cf 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 @@ -2,7 +2,7 @@ # CentOS 7 has headers for kernel 3.10, but that's fine as long as we don't # actually use newer APIs in rustc or std without a fallback. It's more # important that we match glibc for ELF symbol versioning. -FROM centos:7 +FROM ghcr.io/rust-lang/centos:7 WORKDIR /build @@ -101,7 +101,9 @@ ENV SCRIPT python3 ../x.py build --set rust.debug=true opt-dist && \ ./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ --host $HOSTS --target $HOSTS \ --include-default-paths \ - build-manifest bootstrap + build-manifest bootstrap && \ + # Use GCC for building GCC, as it seems to behave badly when built with Clang + CC=/rustroot/bin/cc CXX=/rustroot/bin/c++ python3 ../x.py dist gcc ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang # This is the only builder which will create source tarballs diff --git a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh index 4a42f5da29fcc..ad21836253b9e 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh @@ -41,8 +41,6 @@ cd netbsd mkdir -p /x-tools/x86_64-unknown-netbsd/sysroot -# URL=https://ci-mirrors.rust-lang.org/rustc - # Hashes come from https://cdn.netbsd.org/pub/NetBSD/security/hashes/NetBSD-9.0_hashes.asc SRC_SHA=2c791ae009a6929c6fc893ec5df7e62910ee8207e0b2159d6937309c03efe175b6ae1e445829a13d041b6851334ad35c521f2fa03c97675d4a05f1fafe58ede0 GNUSRC_SHA=3710085a73feecf6a843415271ec794c90146b03f6bbd30f07c9e0c79febf8995d557e40194f1e05db655e4f5ef2fae97563f8456fceaae65d4ea98857a83b1c @@ -51,22 +49,16 @@ SYSSRC_SHA=60b9ddf4cc6402256473e2e1eefeabd9001aa4e205208715ecc6d6fc3f5b400e46994 BASE_SHA=b5926b107cebf40c3c19b4f6cd039b610987dd7f819e7cdde3bd1e5230a856906e7930b15ab242d52ced9f0bda01d574be59488b8dbb95fa5df2987d0a70995f COMP_SHA=38ea54f30d5fc2afea87e5096f06873e00182789e8ad9cec0cb3e9f7c538c1aa4779e63fd401a36ba02676158e83fa5c95e8e87898db59c1914fb206aecd82d2 -# FIXME: the archive URL is being used temporarily while the CDN is down. -# We should serve this from our own CDN -# SOURCE_URL=https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/source/sets -SOURCE_URL=http://archive.netbsd.org/pub/NetBSD-archive/NetBSD-9.0/source/sets -download src.tgz "$SOURCE_URL/src.tgz" "$SRC_SHA" tar xzf src.tgz -download gnusrc.tgz "$SOURCE_URL/gnusrc.tgz" "$GNUSRC_SHA" tar xzf gnusrc.tgz -download sharesrc.tgz "$SOURCE_URL/sharesrc.tgz" "$SHARESRC_SHA" tar xzf sharesrc.tgz -download syssrc.tgz "$SOURCE_URL/syssrc.tgz" "$SYSSRC_SHA" tar xzf syssrc.tgz - -# FIXME: the archive URL is being used temporarily while the CDN is down. -# We should serve this from our own CDN -# BINARY_URL=https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/amd64/binary/sets -BINARY_URL=http://archive.netbsd.org/pub/NetBSD-archive/NetBSD-9.0/amd64/binary/sets -download base.tar.xz "$BINARY_URL/base.tar.xz" "$BASE_SHA" \ +SOURCE_URL=https://ci-mirrors.rust-lang.org/rustc/2025-03-14-netbsd-9.0-src +download src.tgz "$SOURCE_URL-src.tgz" "$SRC_SHA" tar xzf src.tgz +download gnusrc.tgz "$SOURCE_URL-gnusrc.tgz" "$GNUSRC_SHA" tar xzf gnusrc.tgz +download sharesrc.tgz "$SOURCE_URL-sharesrc.tgz" "$SHARESRC_SHA" tar xzf sharesrc.tgz +download syssrc.tgz "$SOURCE_URL-syssrc.tgz" "$SYSSRC_SHA" tar xzf syssrc.tgz + +BINARY_URL=https://ci-mirrors.rust-lang.org/rustc/2025-03-14-netbsd-9.0-amd64-binary +download base.tar.xz "$BINARY_URL-base.tar.xz" "$BASE_SHA" \ tar xJf base.tar.xz -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib ./lib -download comp.tar.xz "$BINARY_URL/comp.tar.xz" "$COMP_SHA" \ +download comp.tar.xz "$BINARY_URL-comp.tar.xz" "$COMP_SHA" \ tar xJf comp.tar.xz -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib cd usr/src diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index 9234c6dc921ee..418408e9242ae 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -41,15 +41,14 @@ COPY host-x86_64/mingw-check/check-default-config-profiles.sh /scripts/ COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/ COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/ -ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1 - # Check library crates on all tier 1 targets. # We disable optimized compiler built-ins because that requires a C toolchain for the target. # We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs. ENV SCRIPT \ python3 ../x.py check --stage 0 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ /scripts/check-default-config-profiles.sh && \ - python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ + python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \ + python3 ../x.py check --target=x86_64-pc-windows-gnu --host=x86_64-pc-windows-gnu && \ python3 ../x.py clippy ci && \ python3 ../x.py build --stage 0 src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ diff --git a/src/ci/docker/host-x86_64/mingw-check/check-default-config-profiles.sh b/src/ci/docker/host-x86_64/mingw-check/check-default-config-profiles.sh index d706927d6d950..0c85d4b449db0 100755 --- a/src/ci/docker/host-x86_64/mingw-check/check-default-config-profiles.sh +++ b/src/ci/docker/host-x86_64/mingw-check/check-default-config-profiles.sh @@ -8,5 +8,6 @@ config_dir="../src/bootstrap/defaults" # Loop through each configuration file in the directory for config_file in "$config_dir"/*.toml; do - python3 ../x.py check --config $config_file --dry-run + # Disable CI mode, because it is not compatible with all profiles + python3 ../x.py check --config $config_file --dry-run --ci=false done diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock index dacf531e4048d..8b6a664ad9397 100644 --- a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock +++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "r-efi" -version = "4.5.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e935efc5854715dfc0a4c9ef18dc69dee0ec3bf9cc3ab740db831c0fdd86a3" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "uefi_qemu_test" diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml index 976245f5bdd06..1a8d0d94368f6 100644 --- a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml +++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" resolver = "2" [dependencies] -r-efi = "4.1.0" +r-efi = "5.2.0" 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 292dbfd20a509..b97568b081980 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 @@ -30,7 +30,6 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV RUSTBUILD_FORCE_CLANG_BASED_TESTS 1 -ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1 # llvm.use-linker conflicts with downloading CI LLVM ENV NO_DOWNLOAD_CI_LLVM 1 diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile deleted file mode 100644 index aefc0f376f689..0000000000000 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile +++ /dev/null @@ -1,69 +0,0 @@ -FROM ubuntu:24.04 - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && apt-get install -y --no-install-recommends \ - bzip2 \ - g++ \ - gcc-multilib \ - make \ - ninja-build \ - file \ - curl \ - ca-certificates \ - python3 \ - git \ - cmake \ - sudo \ - gdb \ - llvm-18-tools \ - llvm-18-dev \ - libedit-dev \ - libssl-dev \ - pkg-config \ - zlib1g-dev \ - xz-utils \ - nodejs \ - mingw-w64 \ - # libgccjit dependencies - flex \ - libmpfr-dev \ - libgmp-dev \ - libmpc3 \ - libmpc-dev \ - && rm -rf /var/lib/apt/lists/* - -# Install powershell (universal package) so we can test x.ps1 on Linux -# FIXME: need a "universal" version that supports libicu74, but for now it still works to ignore that dep. -RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ - dpkg --ignore-depends=libicu72 -i powershell.deb && \ - rm -f powershell.deb - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -# We are disabling CI LLVM since this builder is intentionally using a host -# LLVM, rather than the typical src/llvm-project LLVM. -ENV NO_DOWNLOAD_CI_LLVM 1 -ENV EXTERNAL_LLVM 1 - -# Using llvm-link-shared due to libffi issues -- see #34486 -ENV RUST_CONFIGURE_ARGS \ - --build=x86_64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-18 \ - --enable-llvm-link-shared \ - --set rust.randomize-layout=true \ - --set rust.thin-lto-import-instr-limit=10 - -COPY scripts/shared.sh /scripts/ - -ARG SCRIPT_ARG - -COPY scripts/add_dummy_commit.sh /tmp/ -COPY scripts/x86_64-gnu-llvm.sh /tmp/ -COPY scripts/x86_64-gnu-llvm2.sh /tmp/ -COPY scripts/x86_64-gnu-llvm3.sh /tmp/ -COPY scripts/stage_2_test_set1.sh /tmp/ -COPY scripts/stage_2_test_set2.sh /tmp/ - -ENV SCRIPT "/tmp/add_dummy_commit.sh && /tmp/${SCRIPT_ARG}" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile index e0ed2e227f810..c09be047c6a80 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:24.10 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ + bzip2 \ g++ \ gcc-multilib \ make \ @@ -58,11 +59,10 @@ COPY scripts/shared.sh /scripts/ ARG SCRIPT_ARG -COPY scripts/add_dummy_commit.sh /tmp/ COPY scripts/x86_64-gnu-llvm.sh /tmp/ COPY scripts/x86_64-gnu-llvm2.sh /tmp/ COPY scripts/x86_64-gnu-llvm3.sh /tmp/ COPY scripts/stage_2_test_set1.sh /tmp/ COPY scripts/stage_2_test_set2.sh /tmp/ -ENV SCRIPT "/tmp/add_dummy_commit.sh && /tmp/${SCRIPT_ARG}" +ENV SCRIPT "/tmp/${SCRIPT_ARG}" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile new file mode 100644 index 0000000000000..83a3bfb37a54b --- /dev/null +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile @@ -0,0 +1,68 @@ +FROM ubuntu:25.04 + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + bzip2 \ + g++ \ + gcc-multilib \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + llvm-20-tools \ + llvm-20-dev \ + libedit-dev \ + libssl-dev \ + pkg-config \ + zlib1g-dev \ + xz-utils \ + nodejs \ + mingw-w64 \ + # libgccjit dependencies + flex \ + libmpfr-dev \ + libgmp-dev \ + libmpc3 \ + libmpc-dev \ + && rm -rf /var/lib/apt/lists/* + +# Install powershell (universal package) so we can test x.ps1 on Linux +# FIXME: need a "universal" version that supports libicu74, but for now it still works to ignore that dep. +RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ + dpkg --ignore-depends=libicu72 -i powershell.deb && \ + rm -f powershell.deb + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +# We are disabling CI LLVM since this builder is intentionally using a host +# LLVM, rather than the typical src/llvm-project LLVM. +ENV NO_DOWNLOAD_CI_LLVM 1 +ENV EXTERNAL_LLVM 1 + +# Using llvm-link-shared due to libffi issues -- see #34486 +ENV RUST_CONFIGURE_ARGS \ + --build=x86_64-unknown-linux-gnu \ + --llvm-root=/usr/lib/llvm-20 \ + --enable-llvm-link-shared \ + --set rust.randomize-layout=true \ + --set rust.thin-lto-import-instr-limit=10 + +COPY scripts/shared.sh /scripts/ + +ARG SCRIPT_ARG + +COPY scripts/x86_64-gnu-llvm.sh /tmp/ +COPY scripts/x86_64-gnu-llvm2.sh /tmp/ +COPY scripts/x86_64-gnu-llvm3.sh /tmp/ +COPY scripts/stage_2_test_set1.sh /tmp/ +COPY scripts/stage_2_test_set2.sh /tmp/ + +ENV SCRIPT "/tmp/${SCRIPT_ARG}" 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 89806634c6c26..05c90af780732 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 @@ -101,4 +101,5 @@ COPY scripts/shared.sh /scripts/ # the local version of the package is different than the one used by the CI. ENV SCRIPT /tmp/checktools.sh ../x.py && \ npm install browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true && \ + python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \ python3 ../x.py test tests/rustdoc-gui --stage 2 --test-args "'--jobs 1'" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index 3428dd4826a73..e15121e0f3162 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.20.3 \ No newline at end of file +0.20.6 \ No newline at end of file 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 8324d1ec58624..9222710b84376 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 @@ -40,6 +40,14 @@ if [ -z "${PR_CI_JOB:-}" ]; then else python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri fi +# We re-run the test suite for a chance to find bugs in the intrinsic fallback bodies and in MIR +# optimizations. This can miss UB, so we only run the "pass" tests. We need to enable debug +# assertions as `-O` disables them but some tests rely on them. We also set a cfg flag so tests can +# adjust their expectations if needed. This can change the output of the tests so we ignore that, +# we only ensure that all assertions still pass. +MIRIFLAGS="-Zmiri-force-intrinsic-fallback --cfg force_intrinsic_fallback -O -Zmir-opt-level=4 -Cdebug-assertions=yes" \ + MIRI_SKIP_UI_CHECKS=1 \ + python3 "$X_PY" test --stage 2 src/tools/miri -- tests/pass tests/panic # We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. # Also cover some other targets via cross-testing, in particular all tier 1 targets. case $HOST_TARGET in @@ -62,7 +70,6 @@ case $HOST_TARGET in # See # For now, these tests are moved to `x86_64-msvc-ext2` in `src/ci/github-actions/jobs.yml`. #python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass - #python3 "$X_PY" test --stage 2 src/tools/miri --target i686-pc-windows-gnu --test-args pass ;; *) echo "FATAL: unexpected host $HOST_TARGET" diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 6658b83efc8d2..4e69fb2f37057 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -97,9 +97,8 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then docker --version REGISTRY=ghcr.io - # Hardcode username to reuse cache between auto and pr jobs - # FIXME: should be changed after move from rust-lang-ci - REGISTRY_USERNAME=rust-lang-ci + # Default to `rust-lang` to allow reusing the cache for local builds + REGISTRY_USERNAME=${GITHUB_REPOSITORY_OWNER:-rust-lang} # Tag used to push the final Docker image, so that it can be pulled by e.g. rustup IMAGE_TAG=${REGISTRY}/${REGISTRY_USERNAME}/rust-ci:${cksum} # Tag used to cache the Docker build @@ -288,7 +287,7 @@ args="$args --privileged" # `LOCAL_USER_ID` (recognized in `src/ci/run.sh`) to ensure that files are all # read/written as the same user as the bare-metal user. if [ -f /.dockerenv ]; then - docker create -v /checkout --name checkout alpine:3.4 /bin/true + docker create -v /checkout --name checkout ghcr.io/rust-lang/alpine:3.4 /bin/true docker cp . checkout:/checkout args="$args --volumes-from checkout" else @@ -355,12 +354,15 @@ docker \ --env GITHUB_ACTIONS \ --env GITHUB_REF \ --env GITHUB_STEP_SUMMARY="/checkout/obj/${SUMMARY_FILE}" \ + --env GITHUB_WORKFLOW_RUN_ID \ + --env GITHUB_REPOSITORY \ --env RUST_BACKTRACE \ --env TOOLSTATE_REPO_ACCESS_TOKEN \ --env TOOLSTATE_REPO \ --env TOOLSTATE_PUBLISH \ --env RUST_CI_OVERRIDE_RELEASE_CHANNEL \ --env CI_JOB_NAME="${CI_JOB_NAME-$IMAGE}" \ + --env CI_JOB_DOC_URL="${CI_JOB_DOC_URL}" \ --env BASE_COMMIT="$BASE_COMMIT" \ --env DIST_TRY_BUILD \ --env PR_CI_JOB \ diff --git a/src/ci/docker/scripts/add_dummy_commit.sh b/src/ci/docker/scripts/add_dummy_commit.sh deleted file mode 100755 index 029e4ae141f8f..0000000000000 --- a/src/ci/docker/scripts/add_dummy_commit.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -set -ex - -if [ "$READ_ONLY_SRC" = "0" ]; then - # `core::builder::tests::ci_rustc_if_unchanged_logic` bootstrap test ensures that - # "download-rustc=if-unchanged" logic don't use CI rustc while there are changes on - # compiler and/or library. Here we are adding a dummy commit on compiler and running - # that test to make sure we never download CI rustc with a change on the compiler tree. - echo "" >> ../compiler/rustc/src/main.rs - git config --global user.email "dummy@dummy.com" - git config --global user.name "dummy" - git add ../compiler/rustc/src/main.rs - git commit -m "test commit for rust.download-rustc=if-unchanged logic" - DISABLE_CI_RUSTC_IF_INCOMPATIBLE=0 ../x.py test bootstrap \ - -- core::builder::tests::ci_rustc_if_unchanged_logic - # Revert the dummy commit - git reset --hard HEAD~1 -fi diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh b/src/ci/docker/scripts/build-powerpc64le-toolchain.sh similarity index 100% rename from src/ci/docker/host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh rename to src/ci/docker/scripts/build-powerpc64le-toolchain.sh diff --git a/src/ci/docker/scripts/ohos-openssl.sh b/src/ci/docker/scripts/ohos-openssl.sh new file mode 100644 index 0000000000000..713ab6131e31a --- /dev/null +++ b/src/ci/docker/scripts/ohos-openssl.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -ex + +URL=https://github.com/ohos-rs/ohos-openssl/archive/refs/tags/0.1.0.tar.gz + +mkdir -p /opt/ohos-openssl +curl -fL $URL | tar xz -C /opt/ohos-openssl --strip-components=1 diff --git a/src/ci/docker/scripts/ohos-sdk.sh b/src/ci/docker/scripts/ohos-sdk.sh index 321be2b8697b0..47d630ca76bc9 100755 --- a/src/ci/docker/scripts/ohos-sdk.sh +++ b/src/ci/docker/scripts/ohos-sdk.sh @@ -1,9 +1,10 @@ #!/bin/sh set -ex -URL=https://repo.huaweicloud.com/openharmony/os/4.0-Release/ohos-sdk-windows_linux-public.tar.gz +URL=https://repo.huaweicloud.com/openharmony/os/5.0.0-Release/ohos-sdk-windows_linux-public.tar.gz -curl $URL | tar xz -C /tmp ohos-sdk/linux/native-linux-x64-4.0.10.13-Release.zip +curl $URL | tar xz -C /tmp linux/native-linux-x64-5.0.0.71-Release.zip mkdir /opt/ohos-sdk cd /opt/ohos-sdk -unzip -qq /tmp/ohos-sdk/linux/native-linux-x64-4.0.10.13-Release.zip +unzip -qq /tmp/linux/native-linux-x64-5.0.0.71-Release.zip +rm /tmp/linux/native-linux-x64-5.0.0.71-Release.zip diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 573821c3e59cd..1d280948ebe68 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -2,22 +2,16 @@ set -euo pipefail -LINUX_VERSION=v6.14-rc3 +LINUX_VERSION=v6.15-rc4 # Build rustc, rustdoc, cargo, clippy-driver and rustfmt ../x.py build --stage 2 library rustdoc clippy rustfmt ../x.py build --stage 0 cargo -# Install rustup so that we can use the built toolchain easily, and also -# install bindgen in an easy way. -curl --proto '=https' --tlsv1.2 -sSf -o rustup.sh https://sh.rustup.rs -sh rustup.sh -y --default-toolchain none +BUILD_DIR=$(realpath ./build/x86_64-unknown-linux-gnu) -source /cargo/env - -BUILD_DIR=$(realpath ./build) -rustup toolchain link local "${BUILD_DIR}"/x86_64-unknown-linux-gnu/stage2 -rustup default local +# Provide path to rustc, rustdoc, clippy-driver and rustfmt to RfL +export PATH=${PATH}:${BUILD_DIR}/stage2/bin mkdir -p rfl cd rfl @@ -33,10 +27,14 @@ git -C linux fetch --depth 1 origin ${LINUX_VERSION} git -C linux checkout FETCH_HEAD # Install bindgen -"${BUILD_DIR}"/x86_64-unknown-linux-gnu/stage0/bin/cargo install \ +"${BUILD_DIR}"/stage0/bin/cargo install \ --version $(linux/scripts/min-tool-version.sh bindgen) \ + --root ${BUILD_DIR}/bindgen \ bindgen-cli +# Provide path to bindgen to RfL +export PATH=${PATH}:${BUILD_DIR}/bindgen/bin + # Configure Rust for Linux cat < linux/kernel/configs/rfl-for-rust-ci.config # CONFIG_WERROR is not set diff --git a/src/ci/docker/scripts/sccache.sh b/src/ci/docker/scripts/sccache.sh index f66671c64d25c..dba617d8bc80d 100644 --- a/src/ci/docker/scripts/sccache.sh +++ b/src/ci/docker/scripts/sccache.sh @@ -6,10 +6,10 @@ set -ex case "$(uname -m)" in x86_64) - url="https://ci-mirrors.rust-lang.org/rustc/2025-01-07-sccache-v0.9.1-x86_64-unknown-linux-musl" + url="https://ci-mirrors.rust-lang.org/rustc/2025-02-24-sccache-v0.10.0-x86_64-unknown-linux-musl" ;; aarch64) - url="https://ci-mirrors.rust-lang.org/rustc/2025-01-07-sccache-v0.9.1-aarch64-unknown-linux-musl" + url="https://ci-mirrors.rust-lang.org/rustc/2025-02-24-sccache-v0.10.0-aarch64-unknown-linux-musl" ;; *) echo "unsupported architecture: $(uname -m)" diff --git a/src/ci/docker/scripts/x86_64-gnu-llvm3.sh b/src/ci/docker/scripts/x86_64-gnu-llvm3.sh index d1bf2dab1e2d7..17eb2cea59ac1 100755 --- a/src/ci/docker/scripts/x86_64-gnu-llvm3.sh +++ b/src/ci/docker/scripts/x86_64-gnu-llvm3.sh @@ -2,8 +2,6 @@ set -ex -/tmp/add_dummy_commit.sh - ##### Test stage 1 ##### ../x.py --stage 1 test --skip src/tools/tidy diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 1504e5c60ce7e..005007bbccdd1 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -10,11 +10,6 @@ runners: free_disk: true <<: *base-job - # Large runner used mainly for its bigger disk capacity - - &job-linux-4c-largedisk - os: ubuntu-24.04-4core-16gb - <<: *base-job - - &job-linux-8c os: ubuntu-24.04-8core-32gb <<: *base-job @@ -23,8 +18,8 @@ runners: os: ubuntu-24.04-16core-64gb <<: *base-job - - &job-macos-xl - os: macos-13 # We use the standard runner for now + - &job-macos + os: macos-13 <<: *base-job - &job-macos-m1 @@ -35,6 +30,8 @@ runners: os: windows-2022 <<: *base-job + # FIXME(#141022): Windows Server 2025 20250504.1.0 currently experiences + # insufficient disk space. - &job-windows-25 os: windows-2025 <<: *base-job @@ -54,11 +51,26 @@ runners: <<: *base-job - &job-aarch64-linux-8c - os: ubuntu-22.04-arm64-8core-32gb + os: ubuntu-24.04-arm64-8core-32gb + <<: *base-job + + # Codebuild runners are provisioned in + # https://github.com/rust-lang/simpleinfra/blob/b7ddd5e6bec8a93ec30510cdddec02c5666fefe9/terragrunt/accounts/ci-prod/ci-runners/terragrunt.hcl#L2 + - &job-linux-36c-codebuild + free_disk: true + codebuild: true + os: codebuild-ubuntu-22-36c-$github.run_id-$github.run_attempt + <<: *base-job + + - &job-linux-8c-codebuild + free_disk: true + codebuild: true + os: codebuild-ubuntu-22-8c-$github.run_id-$github.run_attempt <<: *base-job + envs: env-x86_64-apple-tests: &env-x86_64-apple-tests - SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact + SCRIPT: ./x.py check compiletest --set build.compiletest-use-stage0-libtest=true && ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 # Ensure that host tooling is tested on our minimum supported macOS version. @@ -82,15 +94,13 @@ envs: AWS_REGION: us-west-1 TOOLSTATE_PUBLISH: 1 + # Try builds started through `@bors try` (without specifying custom jobs + # in the PR description) will be passed the `DIST_TRY_BUILD` environment + # variable by citool. + # This tells the `opt-dist` tool to skip building certain components + # and skip running tests, so that the try build finishes faster. try: <<: *production - # The following env var activates faster `try` builds in `opt-dist` by, e.g. - # - building only the more commonly useful components (we rarely need e.g. rust-docs in try - # builds) - # - not running `opt-dist`'s post-optimization smoke tests on the resulting toolchain - # - # If you *want* these to happen however, temporarily comment it before triggering a try build. - DIST_TRY_BUILD: 1 auto: <<: *production @@ -107,11 +117,9 @@ pr: - name: mingw-check-tidy continue_on_error: true <<: *job-linux-4c - - name: x86_64-gnu-llvm-18 + - name: x86_64-gnu-llvm-19 env: ENABLE_GCC_CODEGEN: "1" - # We are adding (temporarily) a dummy commit on the compiler - READ_ONLY_SRC: "0" DOCKER_SCRIPT: x86_64-gnu-llvm.sh <<: *job-linux-16c - name: x86_64-gnu-tools @@ -154,8 +162,11 @@ auto: - name: dist-android <<: *job-linux-4c - - name: dist-arm-linux - <<: *job-linux-8c + - name: dist-arm-linux-gnueabi + <<: *job-linux-4c + + - name: dist-arm-linux-musl + <<: *job-linux-4c - name: dist-armhf-linux <<: *job-linux-4c @@ -175,7 +186,13 @@ auto: - name: dist-loongarch64-musl <<: *job-linux-4c - - name: dist-ohos + - name: dist-ohos-aarch64 + <<: *job-linux-4c + + - name: dist-ohos-armv7 + <<: *job-linux-4c + + - name: dist-ohos-x86_64 <<: *job-linux-4c - name: dist-powerpc-linux @@ -184,8 +201,11 @@ auto: - name: dist-powerpc64-linux <<: *job-linux-4c - - name: dist-powerpc64le-linux - <<: *job-linux-4c-largedisk + - name: dist-powerpc64le-linux-gnu + <<: *job-linux-4c + + - name: dist-powerpc64le-linux-musl + <<: *job-linux-4c - name: dist-riscv64-linux <<: *job-linux-4c @@ -208,7 +228,7 @@ auto: - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-16c + <<: *job-linux-36c-codebuild - name: dist-x86_64-linux-alt env: @@ -262,18 +282,22 @@ auto: - name: test-various <<: *job-linux-4c - - name: x86_64-fuchsia - # Only run this job on the nightly channel. Fuchsia requires - # nightly features to compile, and this job would fail if - # executed on beta and stable. - only_on_channel: nightly - <<: *job-linux-8c + # FIXME: temporarily disabled due to fuchsia server rate limits. See + # . + # + #- name: x86_64-fuchsia + # # Only run this job on the nightly channel. Fuchsia requires + # # nightly features to compile, and this job would fail if + # # executed on beta and stable. + # only_on_channel: nightly + # doc_url: https://rustc-dev-guide.rust-lang.org/tests/fuchsia.html + # <<: *job-linux-8c # Tests integration with Rust for Linux. # Builds stage 1 compiler and tries to compile a few RfL examples with it. - # FIXME: fix rustup 1.28 issue - # - name: x86_64-rust-for-linux - # <<: *job-linux-4c + - name: x86_64-rust-for-linux + doc_url: https://rustc-dev-guide.rust-lang.org/tests/rust-for-linux.html + <<: *job-linux-4c - name: x86_64-gnu <<: *job-linux-4c @@ -302,56 +326,53 @@ auto: - name: x86_64-gnu-distcheck <<: *job-linux-8c - # The x86_64-gnu-llvm-19 job is split into multiple jobs to run tests in parallel. - # x86_64-gnu-llvm-19-1 skips tests that run in x86_64-gnu-llvm-19-{2,3}. - - name: x86_64-gnu-llvm-19-1 + # The x86_64-gnu-llvm-20 job is split into multiple jobs to run tests in parallel. + # x86_64-gnu-llvm-20-1 skips tests that run in x86_64-gnu-llvm-20-{2,3}. + - name: x86_64-gnu-llvm-20-1 env: RUST_BACKTRACE: 1 - IMAGE: x86_64-gnu-llvm-19 + IMAGE: x86_64-gnu-llvm-20 DOCKER_SCRIPT: stage_2_test_set1.sh <<: *job-linux-4c - # Skip tests that run in x86_64-gnu-llvm-19-{1,3} - - name: x86_64-gnu-llvm-19-2 + # Skip tests that run in x86_64-gnu-llvm-20-{1,3} + - name: x86_64-gnu-llvm-20-2 env: RUST_BACKTRACE: 1 - IMAGE: x86_64-gnu-llvm-19 + IMAGE: x86_64-gnu-llvm-20 DOCKER_SCRIPT: x86_64-gnu-llvm2.sh <<: *job-linux-4c - # Skip tests that run in x86_64-gnu-llvm-19-{1,2} - - name: x86_64-gnu-llvm-19-3 + # Skip tests that run in x86_64-gnu-llvm-20-{1,2} + - name: x86_64-gnu-llvm-20-3 env: RUST_BACKTRACE: 1 - IMAGE: x86_64-gnu-llvm-19 + IMAGE: x86_64-gnu-llvm-20 DOCKER_SCRIPT: x86_64-gnu-llvm3.sh <<: *job-linux-4c - # The x86_64-gnu-llvm-18 job is split into multiple jobs to run tests in parallel. - # x86_64-gnu-llvm-18-1 skips tests that run in x86_64-gnu-llvm-18-{2,3}. - - name: x86_64-gnu-llvm-18-1 + # The x86_64-gnu-llvm-19 job is split into multiple jobs to run tests in parallel. + # x86_64-gnu-llvm-19-1 skips tests that run in x86_64-gnu-llvm-19-{2,3}. + - name: x86_64-gnu-llvm-19-1 env: RUST_BACKTRACE: 1 - READ_ONLY_SRC: "0" - IMAGE: x86_64-gnu-llvm-18 + IMAGE: x86_64-gnu-llvm-19 DOCKER_SCRIPT: stage_2_test_set1.sh <<: *job-linux-4c - # Skip tests that run in x86_64-gnu-llvm-18-{1,3} - - name: x86_64-gnu-llvm-18-2 + # Skip tests that run in x86_64-gnu-llvm-19-{1,3} + - name: x86_64-gnu-llvm-19-2 env: RUST_BACKTRACE: 1 - READ_ONLY_SRC: "0" - IMAGE: x86_64-gnu-llvm-18 + IMAGE: x86_64-gnu-llvm-19 DOCKER_SCRIPT: x86_64-gnu-llvm2.sh <<: *job-linux-4c - # Skip tests that run in x86_64-gnu-llvm-18-{1,2} - - name: x86_64-gnu-llvm-18-3 + # Skip tests that run in x86_64-gnu-llvm-19-{1,2} + - name: x86_64-gnu-llvm-19-3 env: RUST_BACKTRACE: 1 - READ_ONLY_SRC: "0" - IMAGE: x86_64-gnu-llvm-18 + IMAGE: x86_64-gnu-llvm-19 DOCKER_SCRIPT: x86_64-gnu-llvm3.sh <<: *job-linux-4c @@ -381,7 +402,7 @@ auto: NO_OVERFLOW_CHECKS: 1 DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift - <<: *job-macos-xl + <<: *job-macos - name: dist-apple-various env: @@ -398,18 +419,18 @@ auto: NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 - <<: *job-macos-xl + <<: *job-macos - name: x86_64-apple-1 env: <<: *env-x86_64-apple-tests - <<: *job-macos-xl + <<: *job-macos - name: x86_64-apple-2 env: SCRIPT: ./x.py --stage 2 test tests/ui tests/rustdoc <<: *env-x86_64-apple-tests - <<: *job-macos-xl + <<: *job-macos - name: dist-aarch64-apple env: @@ -464,13 +485,17 @@ auto: env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler SCRIPT: make ci-msvc-py - <<: *job-windows-25 + # FIXME(#141022): Windows Server 2025 20250504.1.0 currently experiences + # insufficient disk space. + <<: *job-windows - name: x86_64-msvc-2 env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler SCRIPT: make ci-msvc-ps1 - <<: *job-windows-25 + # FIXME(#141022): Windows Server 2025 20250504.1.0 currently experiences + # insufficient disk space. + <<: *job-windows # i686-msvc is split into two jobs to run tests in parallel. - name: i686-msvc-1 @@ -499,7 +524,7 @@ auto: env: SCRIPT: > python x.py test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass && - python x.py test --stage 2 src/tools/miri --target i686-pc-windows-gnu --test-args pass && + python x.py test --stage 2 src/tools/miri --target x86_64-pc-windows-gnu --test-args pass && python x.py miri --stage 2 library/core --test-args notest && python x.py miri --stage 2 library/alloc --test-args notest && python x.py miri --stage 2 library/std --test-args notest @@ -531,31 +556,6 @@ auto: # came from the mingw-w64 SourceForge download site. Unfortunately # SourceForge is notoriously flaky, so we mirror it on our own infrastructure. - # i686-mingw is split into three jobs to run tests in parallel. - - name: i686-mingw-1 - env: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu - SCRIPT: make ci-mingw-x-1 - # There is no dist-i686-mingw-alt, so there is no prebuilt LLVM with assertions - NO_DOWNLOAD_CI_LLVM: 1 - <<: *job-windows-25 - - - name: i686-mingw-2 - env: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu - SCRIPT: make ci-mingw-x-2 - # There is no dist-i686-mingw-alt, so there is no prebuilt LLVM with assertions - NO_DOWNLOAD_CI_LLVM: 1 - <<: *job-windows-25 - - - name: i686-mingw-3 - env: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu - SCRIPT: make ci-mingw-bootstrap - # There is no dist-i686-mingw-alt, so there is no prebuilt LLVM with assertions - NO_DOWNLOAD_CI_LLVM: 1 - <<: *job-windows-25 - # x86_64-mingw is split into two jobs to run tests in parallel. - name: x86_64-mingw-1 env: @@ -592,7 +592,7 @@ auto: RUST_CONFIGURE_ARGS: >- --build=i686-pc-windows-msvc --host=i686-pc-windows-msvc - --target=i686-pc-windows-msvc,i586-pc-windows-msvc + --target=i686-pc-windows-msvc --enable-full-tools --enable-profiler SCRIPT: python x.py dist bootstrap --include-default-paths diff --git a/src/ci/package-lock.json b/src/ci/package-lock.json deleted file mode 100644 index 6bbdc6adcfd31..0000000000000 --- a/src/ci/package-lock.json +++ /dev/null @@ -1,5004 +0,0 @@ -{ - "name": "ci", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "@datadog/datadog-ci": "^2.45.1" - } - }, - "node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs": { - "version": "3.703.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.703.0.tgz", - "integrity": "sha512-KkLMwrNhkLZr3OCRWakJY16OF8pUZCoexxZkWzHfR/Pw3WBcDtBMEiEO13P6oHlGAn1VvIDxxMBSxeg/89xyJA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.699.0", - "@aws-sdk/client-sts": "3.699.0", - "@aws-sdk/core": "3.696.0", - "@aws-sdk/credential-provider-node": "3.699.0", - "@aws-sdk/middleware-host-header": "3.696.0", - "@aws-sdk/middleware-logger": "3.696.0", - "@aws-sdk/middleware-recursion-detection": "3.696.0", - "@aws-sdk/middleware-user-agent": "3.696.0", - "@aws-sdk/region-config-resolver": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@aws-sdk/util-endpoints": "3.696.0", - "@aws-sdk/util-user-agent-browser": "3.696.0", - "@aws-sdk/util-user-agent-node": "3.696.0", - "@smithy/config-resolver": "^3.0.12", - "@smithy/core": "^2.5.3", - "@smithy/eventstream-serde-browser": "^3.0.13", - "@smithy/eventstream-serde-config-resolver": "^3.0.10", - "@smithy/eventstream-serde-node": "^3.0.12", - "@smithy/fetch-http-handler": "^4.1.1", - "@smithy/hash-node": "^3.0.10", - "@smithy/invalid-dependency": "^3.0.10", - "@smithy/middleware-content-length": "^3.0.12", - "@smithy/middleware-endpoint": "^3.2.3", - "@smithy/middleware-retry": "^3.0.27", - "@smithy/middleware-serde": "^3.0.10", - "@smithy/middleware-stack": "^3.0.10", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/node-http-handler": "^3.3.1", - "@smithy/protocol-http": "^4.1.7", - "@smithy/smithy-client": "^3.4.4", - "@smithy/types": "^3.7.1", - "@smithy/url-parser": "^3.0.10", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.27", - "@smithy/util-defaults-mode-node": "^3.0.27", - "@smithy/util-endpoints": "^2.1.6", - "@smithy/util-middleware": "^3.0.10", - "@smithy/util-retry": "^3.0.10", - "@smithy/util-utf8": "^3.0.0", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.699.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.699.0.tgz", - "integrity": "sha512-9tFt+we6AIvj/f1+nrLHuCWcQmyfux5gcBSOy9d9+zIG56YxGEX7S9TaZnybogpVV8A0BYWml36WvIHS9QjIpA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.699.0", - "@aws-sdk/client-sts": "3.699.0", - "@aws-sdk/core": "3.696.0", - "@aws-sdk/credential-provider-node": "3.699.0", - "@aws-sdk/middleware-host-header": "3.696.0", - "@aws-sdk/middleware-logger": "3.696.0", - "@aws-sdk/middleware-recursion-detection": "3.696.0", - "@aws-sdk/middleware-user-agent": "3.696.0", - "@aws-sdk/region-config-resolver": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@aws-sdk/util-endpoints": "3.696.0", - "@aws-sdk/util-user-agent-browser": "3.696.0", - "@aws-sdk/util-user-agent-node": "3.696.0", - "@smithy/config-resolver": "^3.0.12", - "@smithy/core": "^2.5.3", - "@smithy/fetch-http-handler": "^4.1.1", - "@smithy/hash-node": "^3.0.10", - "@smithy/invalid-dependency": "^3.0.10", - "@smithy/middleware-content-length": "^3.0.12", - "@smithy/middleware-endpoint": "^3.2.3", - "@smithy/middleware-retry": "^3.0.27", - "@smithy/middleware-serde": "^3.0.10", - "@smithy/middleware-stack": "^3.0.10", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/node-http-handler": "^3.3.1", - "@smithy/protocol-http": "^4.1.7", - "@smithy/smithy-client": "^3.4.4", - "@smithy/types": "^3.7.1", - "@smithy/url-parser": "^3.0.10", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.27", - "@smithy/util-defaults-mode-node": "^3.0.27", - "@smithy/util-endpoints": "^2.1.6", - "@smithy/util-middleware": "^3.0.10", - "@smithy/util-retry": "^3.0.10", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-iam": { - "version": "3.699.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-iam/-/client-iam-3.699.0.tgz", - "integrity": "sha512-JBVcmkGaV7tW/mEntqt6KdkhsyYU2oIMUbkNHJextKqu6odSkuB1mN70TgctwFP8x2LDgFzvT6WcWVCKc/g3Ng==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.699.0", - "@aws-sdk/client-sts": "3.699.0", - "@aws-sdk/core": "3.696.0", - "@aws-sdk/credential-provider-node": "3.699.0", - "@aws-sdk/middleware-host-header": "3.696.0", - "@aws-sdk/middleware-logger": "3.696.0", - "@aws-sdk/middleware-recursion-detection": "3.696.0", - "@aws-sdk/middleware-user-agent": "3.696.0", - "@aws-sdk/region-config-resolver": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@aws-sdk/util-endpoints": "3.696.0", - "@aws-sdk/util-user-agent-browser": "3.696.0", - "@aws-sdk/util-user-agent-node": "3.696.0", - "@smithy/config-resolver": "^3.0.12", - "@smithy/core": "^2.5.3", - "@smithy/fetch-http-handler": "^4.1.1", - "@smithy/hash-node": "^3.0.10", - "@smithy/invalid-dependency": "^3.0.10", - "@smithy/middleware-content-length": "^3.0.12", - "@smithy/middleware-endpoint": "^3.2.3", - "@smithy/middleware-retry": "^3.0.27", - "@smithy/middleware-serde": "^3.0.10", - "@smithy/middleware-stack": "^3.0.10", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/node-http-handler": "^3.3.1", - "@smithy/protocol-http": "^4.1.7", - "@smithy/smithy-client": "^3.4.4", - "@smithy/types": "^3.7.1", - "@smithy/url-parser": "^3.0.10", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.27", - "@smithy/util-defaults-mode-node": "^3.0.27", - "@smithy/util-endpoints": "^2.1.6", - "@smithy/util-middleware": "^3.0.10", - "@smithy/util-retry": "^3.0.10", - "@smithy/util-utf8": "^3.0.0", - "@smithy/util-waiter": "^3.1.9", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-iam/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-lambda": { - "version": "3.699.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.699.0.tgz", - "integrity": "sha512-K9TGvQB8hkjwNhfWSfYllUpttqxTcd78ShSRCIhlcwzzsmQphET10xEb0Tm1k8sqriSQ+CiVOFSkX78gqoHzBg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.699.0", - "@aws-sdk/client-sts": "3.699.0", - "@aws-sdk/core": "3.696.0", - "@aws-sdk/credential-provider-node": "3.699.0", - "@aws-sdk/middleware-host-header": "3.696.0", - "@aws-sdk/middleware-logger": "3.696.0", - "@aws-sdk/middleware-recursion-detection": "3.696.0", - "@aws-sdk/middleware-user-agent": "3.696.0", - "@aws-sdk/region-config-resolver": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@aws-sdk/util-endpoints": "3.696.0", - "@aws-sdk/util-user-agent-browser": "3.696.0", - "@aws-sdk/util-user-agent-node": "3.696.0", - "@smithy/config-resolver": "^3.0.12", - "@smithy/core": "^2.5.3", - "@smithy/eventstream-serde-browser": "^3.0.13", - "@smithy/eventstream-serde-config-resolver": "^3.0.10", - "@smithy/eventstream-serde-node": "^3.0.12", - "@smithy/fetch-http-handler": "^4.1.1", - "@smithy/hash-node": "^3.0.10", - "@smithy/invalid-dependency": "^3.0.10", - "@smithy/middleware-content-length": "^3.0.12", - "@smithy/middleware-endpoint": "^3.2.3", - "@smithy/middleware-retry": "^3.0.27", - "@smithy/middleware-serde": "^3.0.10", - "@smithy/middleware-stack": "^3.0.10", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/node-http-handler": "^3.3.1", - "@smithy/protocol-http": "^4.1.7", - "@smithy/smithy-client": "^3.4.4", - "@smithy/types": "^3.7.1", - "@smithy/url-parser": "^3.0.10", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.27", - "@smithy/util-defaults-mode-node": "^3.0.27", - "@smithy/util-endpoints": "^2.1.6", - "@smithy/util-middleware": "^3.0.10", - "@smithy/util-retry": "^3.0.10", - "@smithy/util-stream": "^3.3.1", - "@smithy/util-utf8": "^3.0.0", - "@smithy/util-waiter": "^3.1.9", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sfn": { - "version": "3.699.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sfn/-/client-sfn-3.699.0.tgz", - "integrity": "sha512-66/+rOMVvjKRhKDDsrxtfRzXXsAfVu/RbhxPYepcVhO+0/ii6DL2uQCeNhMN3+MPg+HWX695H5RyBVJ6QGli8w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.699.0", - "@aws-sdk/client-sts": "3.699.0", - "@aws-sdk/core": "3.696.0", - "@aws-sdk/credential-provider-node": "3.699.0", - "@aws-sdk/middleware-host-header": "3.696.0", - "@aws-sdk/middleware-logger": "3.696.0", - "@aws-sdk/middleware-recursion-detection": "3.696.0", - "@aws-sdk/middleware-user-agent": "3.696.0", - "@aws-sdk/region-config-resolver": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@aws-sdk/util-endpoints": "3.696.0", - "@aws-sdk/util-user-agent-browser": "3.696.0", - "@aws-sdk/util-user-agent-node": "3.696.0", - "@smithy/config-resolver": "^3.0.12", - "@smithy/core": "^2.5.3", - "@smithy/fetch-http-handler": "^4.1.1", - "@smithy/hash-node": "^3.0.10", - "@smithy/invalid-dependency": "^3.0.10", - "@smithy/middleware-content-length": "^3.0.12", - "@smithy/middleware-endpoint": "^3.2.3", - "@smithy/middleware-retry": "^3.0.27", - "@smithy/middleware-serde": "^3.0.10", - "@smithy/middleware-stack": "^3.0.10", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/node-http-handler": "^3.3.1", - "@smithy/protocol-http": "^4.1.7", - "@smithy/smithy-client": "^3.4.4", - "@smithy/types": "^3.7.1", - "@smithy/url-parser": "^3.0.10", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.27", - "@smithy/util-defaults-mode-node": "^3.0.27", - "@smithy/util-endpoints": "^2.1.6", - "@smithy/util-middleware": "^3.0.10", - "@smithy/util-retry": "^3.0.10", - "@smithy/util-utf8": "^3.0.0", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sfn/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.696.0.tgz", - "integrity": "sha512-q5TTkd08JS0DOkHfUL853tuArf7NrPeqoS5UOvqJho8ibV9Ak/a/HO4kNvy9Nj3cib/toHYHsQIEtecUPSUUrQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.696.0", - "@aws-sdk/middleware-host-header": "3.696.0", - "@aws-sdk/middleware-logger": "3.696.0", - "@aws-sdk/middleware-recursion-detection": "3.696.0", - "@aws-sdk/middleware-user-agent": "3.696.0", - "@aws-sdk/region-config-resolver": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@aws-sdk/util-endpoints": "3.696.0", - "@aws-sdk/util-user-agent-browser": "3.696.0", - "@aws-sdk/util-user-agent-node": "3.696.0", - "@smithy/config-resolver": "^3.0.12", - "@smithy/core": "^2.5.3", - "@smithy/fetch-http-handler": "^4.1.1", - "@smithy/hash-node": "^3.0.10", - "@smithy/invalid-dependency": "^3.0.10", - "@smithy/middleware-content-length": "^3.0.12", - "@smithy/middleware-endpoint": "^3.2.3", - "@smithy/middleware-retry": "^3.0.27", - "@smithy/middleware-serde": "^3.0.10", - "@smithy/middleware-stack": "^3.0.10", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/node-http-handler": "^3.3.1", - "@smithy/protocol-http": "^4.1.7", - "@smithy/smithy-client": "^3.4.4", - "@smithy/types": "^3.7.1", - "@smithy/url-parser": "^3.0.10", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.27", - "@smithy/util-defaults-mode-node": "^3.0.27", - "@smithy/util-endpoints": "^2.1.6", - "@smithy/util-middleware": "^3.0.10", - "@smithy/util-retry": "^3.0.10", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.699.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.699.0.tgz", - "integrity": "sha512-u8a1GorY5D1l+4FQAf4XBUC1T10/t7neuwT21r0ymrtMFSK2a9QqVHKMoLkvavAwyhJnARSBM9/UQC797PFOFw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.696.0", - "@aws-sdk/credential-provider-node": "3.699.0", - "@aws-sdk/middleware-host-header": "3.696.0", - "@aws-sdk/middleware-logger": "3.696.0", - "@aws-sdk/middleware-recursion-detection": "3.696.0", - "@aws-sdk/middleware-user-agent": "3.696.0", - "@aws-sdk/region-config-resolver": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@aws-sdk/util-endpoints": "3.696.0", - "@aws-sdk/util-user-agent-browser": "3.696.0", - "@aws-sdk/util-user-agent-node": "3.696.0", - "@smithy/config-resolver": "^3.0.12", - "@smithy/core": "^2.5.3", - "@smithy/fetch-http-handler": "^4.1.1", - "@smithy/hash-node": "^3.0.10", - "@smithy/invalid-dependency": "^3.0.10", - "@smithy/middleware-content-length": "^3.0.12", - "@smithy/middleware-endpoint": "^3.2.3", - "@smithy/middleware-retry": "^3.0.27", - "@smithy/middleware-serde": "^3.0.10", - "@smithy/middleware-stack": "^3.0.10", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/node-http-handler": "^3.3.1", - "@smithy/protocol-http": "^4.1.7", - "@smithy/smithy-client": "^3.4.4", - "@smithy/types": "^3.7.1", - "@smithy/url-parser": "^3.0.10", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.27", - "@smithy/util-defaults-mode-node": "^3.0.27", - "@smithy/util-endpoints": "^2.1.6", - "@smithy/util-middleware": "^3.0.10", - "@smithy/util-retry": "^3.0.10", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.699.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.699.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.699.0.tgz", - "integrity": "sha512-++lsn4x2YXsZPIzFVwv3fSUVM55ZT0WRFmPeNilYIhZClxHLmVAWKH4I55cY9ry60/aTKYjzOXkWwyBKGsGvQg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.699.0", - "@aws-sdk/core": "3.696.0", - "@aws-sdk/credential-provider-node": "3.699.0", - "@aws-sdk/middleware-host-header": "3.696.0", - "@aws-sdk/middleware-logger": "3.696.0", - "@aws-sdk/middleware-recursion-detection": "3.696.0", - "@aws-sdk/middleware-user-agent": "3.696.0", - "@aws-sdk/region-config-resolver": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@aws-sdk/util-endpoints": "3.696.0", - "@aws-sdk/util-user-agent-browser": "3.696.0", - "@aws-sdk/util-user-agent-node": "3.696.0", - "@smithy/config-resolver": "^3.0.12", - "@smithy/core": "^2.5.3", - "@smithy/fetch-http-handler": "^4.1.1", - "@smithy/hash-node": "^3.0.10", - "@smithy/invalid-dependency": "^3.0.10", - "@smithy/middleware-content-length": "^3.0.12", - "@smithy/middleware-endpoint": "^3.2.3", - "@smithy/middleware-retry": "^3.0.27", - "@smithy/middleware-serde": "^3.0.10", - "@smithy/middleware-stack": "^3.0.10", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/node-http-handler": "^3.3.1", - "@smithy/protocol-http": "^4.1.7", - "@smithy/smithy-client": "^3.4.4", - "@smithy/types": "^3.7.1", - "@smithy/url-parser": "^3.0.10", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.27", - "@smithy/util-defaults-mode-node": "^3.0.27", - "@smithy/util-endpoints": "^2.1.6", - "@smithy/util-middleware": "^3.0.10", - "@smithy/util-retry": "^3.0.10", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.696.0.tgz", - "integrity": "sha512-3c9III1k03DgvRZWg8vhVmfIXPG6hAciN9MzQTzqGngzWAELZF/WONRTRQuDFixVtarQatmLHYVw/atGeA2Byw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.696.0", - "@smithy/core": "^2.5.3", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/property-provider": "^3.1.9", - "@smithy/protocol-http": "^4.1.7", - "@smithy/signature-v4": "^4.2.2", - "@smithy/smithy-client": "^3.4.4", - "@smithy/types": "^3.7.1", - "@smithy/util-middleware": "^3.0.10", - "fast-xml-parser": "4.4.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/fast-xml-parser": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", - "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.699.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.699.0.tgz", - "integrity": "sha512-iuaTnudaBfEET+o444sDwf71Awe6UiZfH+ipUPmswAi2jZDwdFF1nxMKDEKL8/LV5WpXsdKSfwgS0RQeupURew==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.699.0", - "@aws-sdk/types": "3.696.0", - "@smithy/property-provider": "^3.1.9", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.696.0.tgz", - "integrity": "sha512-T9iMFnJL7YTlESLpVFT3fg1Lkb1lD+oiaIC8KMpepb01gDUBIpj9+Y+pA/cgRWW0yRxmkDXNazAE2qQTVFGJzA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@smithy/property-provider": "^3.1.9", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.696.0.tgz", - "integrity": "sha512-GV6EbvPi2eq1+WgY/o2RFA3P7HGmnkIzCNmhwtALFlqMroLYWKE7PSeHw66Uh1dFQeVESn0/+hiUNhu1mB0emA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@smithy/fetch-http-handler": "^4.1.1", - "@smithy/node-http-handler": "^3.3.1", - "@smithy/property-provider": "^3.1.9", - "@smithy/protocol-http": "^4.1.7", - "@smithy/smithy-client": "^3.4.4", - "@smithy/types": "^3.7.1", - "@smithy/util-stream": "^3.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.699.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.699.0.tgz", - "integrity": "sha512-dXmCqjJnKmG37Q+nLjPVu22mNkrGHY8hYoOt3Jo9R2zr5MYV7s/NHsCHr+7E+BZ+tfZYLRPeB1wkpTeHiEcdRw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.696.0", - "@aws-sdk/credential-provider-env": "3.696.0", - "@aws-sdk/credential-provider-http": "3.696.0", - "@aws-sdk/credential-provider-process": "3.696.0", - "@aws-sdk/credential-provider-sso": "3.699.0", - "@aws-sdk/credential-provider-web-identity": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@smithy/credential-provider-imds": "^3.2.6", - "@smithy/property-provider": "^3.1.9", - "@smithy/shared-ini-file-loader": "^3.1.10", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.699.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.699.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.699.0.tgz", - "integrity": "sha512-MmEmNDo1bBtTgRmdNfdQksXu4uXe66s0p1hi1YPrn1h59Q605eq/xiWbGL6/3KdkViH6eGUuABeV2ODld86ylg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.696.0", - "@aws-sdk/credential-provider-http": "3.696.0", - "@aws-sdk/credential-provider-ini": "3.699.0", - "@aws-sdk/credential-provider-process": "3.696.0", - "@aws-sdk/credential-provider-sso": "3.699.0", - "@aws-sdk/credential-provider-web-identity": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@smithy/credential-provider-imds": "^3.2.6", - "@smithy/property-provider": "^3.1.9", - "@smithy/shared-ini-file-loader": "^3.1.10", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.696.0.tgz", - "integrity": "sha512-mL1RcFDe9sfmyU5K1nuFkO8UiJXXxLX4JO1gVaDIOvPqwStpUAwi3A1BoeZhWZZNQsiKI810RnYGo0E0WB/hUA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@smithy/property-provider": "^3.1.9", - "@smithy/shared-ini-file-loader": "^3.1.10", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.699.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.699.0.tgz", - "integrity": "sha512-Ekp2cZG4pl9D8+uKWm4qO1xcm8/MeiI8f+dnlZm8aQzizeC+aXYy9GyoclSf6daK8KfRPiRfM7ZHBBL5dAfdMA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.696.0", - "@aws-sdk/core": "3.696.0", - "@aws-sdk/token-providers": "3.699.0", - "@aws-sdk/types": "3.696.0", - "@smithy/property-provider": "^3.1.9", - "@smithy/shared-ini-file-loader": "^3.1.10", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.696.0.tgz", - "integrity": "sha512-XJ/CVlWChM0VCoc259vWguFUjJDn/QwDqHwbx+K9cg3v6yrqXfK5ai+p/6lx0nQpnk4JzPVeYYxWRpaTsGC9rg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@smithy/property-provider": "^3.1.9", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.696.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers": { - "version": "3.699.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.699.0.tgz", - "integrity": "sha512-jBjOntl9zN9Nvb0jmbMGRbiTzemDz64ij7W6BDavxBJRZpRoNeN0QCz6RolkCyXnyUJjo5mF2unY2wnv00A+LQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.699.0", - "@aws-sdk/client-sso": "3.696.0", - "@aws-sdk/client-sts": "3.699.0", - "@aws-sdk/core": "3.696.0", - "@aws-sdk/credential-provider-cognito-identity": "3.699.0", - "@aws-sdk/credential-provider-env": "3.696.0", - "@aws-sdk/credential-provider-http": "3.696.0", - "@aws-sdk/credential-provider-ini": "3.699.0", - "@aws-sdk/credential-provider-node": "3.699.0", - "@aws-sdk/credential-provider-process": "3.696.0", - "@aws-sdk/credential-provider-sso": "3.699.0", - "@aws-sdk/credential-provider-web-identity": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@smithy/credential-provider-imds": "^3.2.6", - "@smithy/property-provider": "^3.1.9", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.696.0.tgz", - "integrity": "sha512-zELJp9Ta2zkX7ELggMN9qMCgekqZhFC5V2rOr4hJDEb/Tte7gpfKSObAnw/3AYiVqt36sjHKfdkoTsuwGdEoDg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.696.0", - "@smithy/protocol-http": "^4.1.7", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.696.0.tgz", - "integrity": "sha512-KhkHt+8AjCxcR/5Zp3++YPJPpFQzxpr+jmONiT/Jw2yqnSngZ0Yspm5wGoRx2hS1HJbyZNuaOWEGuJoxLeBKfA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.696.0", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.696.0.tgz", - "integrity": "sha512-si/maV3Z0hH7qa99f9ru2xpS5HlfSVcasRlNUXKSDm611i7jFMWwGNLUOXFAOLhXotPX5G3Z6BLwL34oDeBMug==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.696.0", - "@smithy/protocol-http": "^4.1.7", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.696.0.tgz", - "integrity": "sha512-Lvyj8CTyxrHI6GHd2YVZKIRI5Fmnugt3cpJo0VrKKEgK5zMySwEZ1n4dqPK6czYRWKd5+WnYHYAuU+Wdk6Jsjw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@aws-sdk/util-endpoints": "3.696.0", - "@smithy/core": "^2.5.3", - "@smithy/protocol-http": "^4.1.7", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.696.0.tgz", - "integrity": "sha512-7EuH142lBXjI8yH6dVS/CZeiK/WZsmb/8zP6bQbVYpMrppSTgB3MzZZdxVZGzL5r8zPQOU10wLC4kIMy0qdBVQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.696.0", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/types": "^3.7.1", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.10", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.699.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.699.0.tgz", - "integrity": "sha512-kuiEW9DWs7fNos/SM+y58HCPhcIzm1nEZLhe2/7/6+TvAYLuEWURYsbK48gzsxXlaJ2k/jGY3nIsA7RptbMOwA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.696.0", - "@smithy/property-provider": "^3.1.9", - "@smithy/shared-ini-file-loader": "^3.1.10", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.699.0" - } - }, - "node_modules/@aws-sdk/token-providers/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.696.0.tgz", - "integrity": "sha512-9rTvUJIAj5d3//U5FDPWGJ1nFJLuWb30vugGOrWk7aNZ6y9tuA3PI7Cc9dP8WEXKVyK1vuuk8rSFP2iqXnlgrw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.696.0.tgz", - "integrity": "sha512-T5s0IlBVX+gkb9g/I6CLt4yAZVzMSiGnbUqWihWsHvQR1WOoIcndQy/Oz/IJXT9T2ipoy7a80gzV6a5mglrioA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.696.0", - "@smithy/types": "^3.7.1", - "@smithy/util-endpoints": "^2.1.6", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.693.0.tgz", - "integrity": "sha512-ttrag6haJLWABhLqtg1Uf+4LgHWIMOVSYL+VYZmAp2v4PUGOwWmWQH0Zk8RM7YuQcLfH/EoR72/Yxz6A4FKcuw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.696.0.tgz", - "integrity": "sha512-Z5rVNDdmPOe6ELoM5AhF/ja5tSjbe6ctSctDPb0JdDf4dT0v2MfwhJKzXju2RzX8Es/77Glh7MlaXLE0kCB9+Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.696.0", - "@smithy/types": "^3.7.1", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.696.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.696.0.tgz", - "integrity": "sha512-KhKqcfyXIB0SCCt+qsu4eJjsfiOrNzK5dCV7RAW2YIpp+msxGUUX0NdRE9rkzjiv+3EMktgJm3eEIS+yxtlVdQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.696.0", - "@aws-sdk/types": "3.696.0", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/types": "^3.7.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@datadog/datadog-ci": { - "version": "2.45.1", - "resolved": "https://registry.npmjs.org/@datadog/datadog-ci/-/datadog-ci-2.45.1.tgz", - "integrity": "sha512-na4c4UuhT2jGFTOh+/uUVGR0CVj7c2B6Q7pRp9AMxb6NF2o3McjnSFJzmVc7YFdZwr+eHXeKXKf6bEYw/PjNWw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-cloudwatch-logs": "^3.624.0", - "@aws-sdk/client-iam": "^3.624.0", - "@aws-sdk/client-lambda": "^3.624.0", - "@aws-sdk/client-sfn": "^3.624.0", - "@aws-sdk/core": "^3.624.0", - "@aws-sdk/credential-provider-ini": "^3.624.0", - "@aws-sdk/credential-providers": "^3.624.0", - "@google-cloud/logging": "^11.1.0", - "@google-cloud/run": "^1.4.0", - "@smithy/property-provider": "^2.0.12", - "@smithy/util-retry": "^2.0.4", - "@types/datadog-metrics": "0.6.1", - "ajv": "^8.12.0", - "ajv-formats": "^2.1.1", - "async-retry": "1.3.1", - "axios": "^1.7.4", - "chalk": "3.0.0", - "clipanion": "^3.2.1", - "datadog-metrics": "0.9.3", - "deep-extend": "0.6.0", - "deep-object-diff": "^1.1.9", - "fast-levenshtein": "^3.0.0", - "fast-xml-parser": "^4.4.1", - "form-data": "4.0.0", - "fuzzy": "^0.1.3", - "glob": "^7.1.4", - "google-auth-library": "^9.12.0", - "inquirer": "^8.2.5", - "inquirer-checkbox-plus-prompt": "^1.4.2", - "js-yaml": "3.13.1", - "jszip": "^3.10.1", - "ora": "5.4.1", - "proxy-agent": "^6.4.0", - "rimraf": "^3.0.2", - "semver": "^7.5.3", - "simple-git": "3.16.0", - "ssh2": "^1.15.0", - "ssh2-streams": "0.4.10", - "sshpk": "1.16.1", - "terminal-link": "2.1.1", - "tiny-async-pool": "^2.1.0", - "typanion": "^3.14.0", - "uuid": "^9.0.0", - "ws": "^7.5.10", - "xml2js": "0.5.0", - "yamux-js": "0.1.2" - }, - "bin": { - "datadog-ci": "dist/cli.js" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@google-cloud/common": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-5.0.2.tgz", - "integrity": "sha512-V7bmBKYQyu0eVG2BFejuUjlBt+zrya6vtsKdY+JxMM/dNntPF41vZ9+LhOshEUH01zOHEqBSvI7Dad7ZS6aUeA==", - "license": "Apache-2.0", - "dependencies": { - "@google-cloud/projectify": "^4.0.0", - "@google-cloud/promisify": "^4.0.0", - "arrify": "^2.0.1", - "duplexify": "^4.1.1", - "extend": "^3.0.2", - "google-auth-library": "^9.0.0", - "html-entities": "^2.5.2", - "retry-request": "^7.0.0", - "teeny-request": "^9.0.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@google-cloud/logging": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@google-cloud/logging/-/logging-11.2.0.tgz", - "integrity": "sha512-Ma94jvuoMpbgNniwtelOt8w82hxK62FuOXZonEv0Hyk3B+/YVuLG/SWNyY9yMso/RXnPEc1fP2qo9kDrjf/b2w==", - "license": "Apache-2.0", - "dependencies": { - "@google-cloud/common": "^5.0.0", - "@google-cloud/paginator": "^5.0.0", - "@google-cloud/projectify": "^4.0.0", - "@google-cloud/promisify": "^4.0.0", - "@opentelemetry/api": "^1.7.0", - "arrify": "^2.0.1", - "dot-prop": "^6.0.0", - "eventid": "^2.0.0", - "extend": "^3.0.2", - "gcp-metadata": "^6.0.0", - "google-auth-library": "^9.0.0", - "google-gax": "^4.0.3", - "on-finished": "^2.3.0", - "pumpify": "^2.0.1", - "stream-events": "^1.0.5", - "uuid": "^9.0.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@google-cloud/paginator": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", - "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", - "license": "Apache-2.0", - "dependencies": { - "arrify": "^2.0.0", - "extend": "^3.0.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@google-cloud/projectify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", - "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@google-cloud/promisify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", - "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@google-cloud/run": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@google-cloud/run/-/run-1.5.0.tgz", - "integrity": "sha512-Ct0ZIuicd2O6fJHv7Lbwl4CcCWKK7NjACvUc4pOJVbKo75B5proOa7/9TrXVpI6oWu1n7EFx1s8xsavYHLxRAg==", - "license": "Apache-2.0", - "dependencies": { - "google-gax": "^4.0.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.12.4", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.4.tgz", - "integrity": "sha512-NBhrxEWnFh0FxeA0d//YP95lRFsSx2TNLEUQg4/W+5f/BMxcCjgOOIT24iD+ZB/tZw057j44DaIxja7w4XMrhg==", - "license": "Apache-2.0", - "dependencies": { - "@grpc/proto-loader": "^0.7.13", - "@js-sdsl/ordered-map": "^4.4.2" - }, - "engines": { - "node": ">=12.10.0" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.7.13", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", - "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", - "license": "Apache-2.0", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.5", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@js-sdsl/ordered-map": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/@kwsites/file-exists": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", - "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.1" - } - }, - "node_modules/@kwsites/file-exists/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@kwsites/file-exists/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/@kwsites/promise-deferred": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", - "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", - "license": "MIT" - }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" - }, - "node_modules/@smithy/abort-controller": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", - "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", - "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.5.tgz", - "integrity": "sha512-G8G/sDDhXA7o0bOvkc7bgai6POuSld/+XhNnWAbpQTpLv2OZPvyqQ58tLPPlz0bSNsXktldDDREIv1LczFeNEw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^3.0.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-stream": "^3.3.2", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", - "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.10.tgz", - "integrity": "sha512-323B8YckSbUH0nMIpXn7HZsAVKHYHFUODa8gG9cHo0ySvA1fr5iWaNT+iIL0UCqUzG6QPHA3BSsBtRQou4mMqQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.7.2", - "@smithy/util-hex-encoding": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.14.tgz", - "integrity": "sha512-kbrt0vjOIihW3V7Cqj1SXQvAI5BR8SnyQYsandva0AOR307cXAc+IhPngxIPslxTLfxwDpNu0HzCAq6g42kCPg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.13", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.11.tgz", - "integrity": "sha512-P2pnEp4n75O+QHjyO7cbw/vsw5l93K/8EWyjNCAAybYwUmj3M+hjSQZ9P5TVdUgEG08ueMAP5R4FkuSkElZ5tQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.13.tgz", - "integrity": "sha512-zqy/9iwbj8Wysmvi7Lq7XFLeDgjRpTbCfwBhJa8WbrylTAHiAu6oQTwdY7iu2lxigbc9YYr9vPv5SzYny5tCXQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.13", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-universal": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.13.tgz", - "integrity": "sha512-L1Ib66+gg9uTnqp/18Gz4MDpJPKRE44geOjOQ2SVc0eiaO5l255ADziATZgjQjqumC7yPtp1XnjHlF1srcwjKw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-codec": "^3.1.10", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.2.tgz", - "integrity": "sha512-R7rU7Ae3ItU4rC0c5mB2sP5mJNbCfoDc8I5XlYjIZnquyUwec7fEo78F6DA3SmgJgkU1qTMcZJuGblxZsl10ZA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.8", - "@smithy/querystring-builder": "^3.0.11", - "@smithy/types": "^3.7.2", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/hash-node": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", - "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", - "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", - "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", - "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.5.tgz", - "integrity": "sha512-VhJNs/s/lyx4weiZdXSloBgoLoS8osV0dKIain8nGmx7of3QFKu5BSdEuk1z/U8x9iwes1i+XCiNusEvuK1ijg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.5.5", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-middleware": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "3.0.29", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.29.tgz", - "integrity": "sha512-/FfI/N2tIVCG9escHYLLABJlNHbO1gWFSdMlvbPOTV+Y+Aa8Q1FuS7Yaghb8NUfFmGYjkbeIu3bnbta6KbarsA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/protocol-http": "^4.1.8", - "@smithy/service-error-classification": "^3.0.11", - "@smithy/smithy-client": "^3.4.6", - "@smithy/types": "^3.7.2", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-retry/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", - "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", - "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", - "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/node-config-provider/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.2.tgz", - "integrity": "sha512-t4ng1DAd527vlxvOfKFYEe6/QFBcsj7WpNlWTyjorwXXcKw3XlltBGbyHfSJ24QT84nF+agDha9tNYpzmSRZPA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^3.1.9", - "@smithy/protocol-http": "^4.1.8", - "@smithy/querystring-builder": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.2.0.tgz", - "integrity": "sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^2.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/property-provider/node_modules/@smithy/types": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", - "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", - "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", - "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-uri-escape": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", - "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", - "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", - "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.4.tgz", - "integrity": "sha512-5JWeMQYg81TgU4cG+OexAWdvDTs5JDdbEZx+Qr1iPbvo91QFGzjy0IkXAKaXUHqmKUJgSHK0ZxnCkgZpzkeNTA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-uri-escape": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "3.4.6", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.6.tgz", - "integrity": "sha512-j/WYdfzdx4asqb0YZch1rb6Y5OQcuTdXY7xnEMtc05diB5jfLQQtys9J/2lzmGKri9m9mUBrcnNTv3jbXvtk7A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.5.5", - "@smithy/middleware-endpoint": "^3.2.5", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-stream": "^3.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", - "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", - "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/util-base64": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", - "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", - "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", - "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", - "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", - "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.29", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.29.tgz", - "integrity": "sha512-7k0kOfDjnSM44GG74UnjEixtHmtF/do6pCNEvp1DQlFjktEz8pv06p9RSjxp2J3Lv6I3+Ba9FLfurpFeBv+Pvw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^3.1.11", - "@smithy/smithy-client": "^3.4.6", - "@smithy/types": "^3.7.2", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.29", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.29.tgz", - "integrity": "sha512-lu8jaXAt1TIVkTAKMuGdZwWQ1L03iV/n8WPbY5hdjcNqLg2ysp5DqiINmtuLL8EgzDv1TXvKDaBAN+9bGH4sEQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^3.0.13", - "@smithy/credential-provider-imds": "^3.2.8", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/smithy-client": "^3.4.6", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-endpoints": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", - "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", - "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", - "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.2.0.tgz", - "integrity": "sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^2.1.5", - "@smithy/types": "^2.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@smithy/util-retry/node_modules/@smithy/service-error-classification": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.5.tgz", - "integrity": "sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^2.12.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-retry/node_modules/@smithy/types": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", - "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.2.tgz", - "integrity": "sha512-sInAqdiVeisUGYAv/FrXpmJ0b4WTFmciTRqzhb7wVuem9BHvhIG7tpiYHLDWrl2stOokNZpTTGqz3mzB2qFwXg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/types": "^3.7.2", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-uri-escape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", - "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", - "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@smithy/util-waiter": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.2.0.tgz", - "integrity": "sha512-PpjSboaDUE6yl+1qlg3Si57++e84oXdWGbuFUSAciXsVfEZJJJupR2Nb0QuXHiunt2vGR+1PTizOMvnUPaG2Qg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^3.1.9", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "license": "MIT" - }, - "node_modules/@types/caseless": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", - "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", - "license": "MIT" - }, - "node_modules/@types/datadog-metrics": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@types/datadog-metrics/-/datadog-metrics-0.6.1.tgz", - "integrity": "sha512-p6zVpfmNcXwtcXjgpz7do/fKyfndGhU5sGJVtb5Gn5PvLDiQUAgD0mI/itf/99sBi9DRxeyhFQ9dQF6OxxQNbA==", - "license": "MIT" - }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", - "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/request": { - "version": "2.48.12", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", - "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", - "license": "MIT", - "dependencies": { - "@types/caseless": "*", - "@types/node": "*", - "@types/tough-cookie": "*", - "form-data": "^2.5.0" - } - }, - "node_modules/@types/request/node_modules/form-data": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", - "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12", - "safe-buffer": "^5.2.1" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/@types/tough-cookie": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "license": "MIT" - }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "license": "MIT" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/async-retry": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz", - "integrity": "sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==", - "license": "MIT", - "dependencies": { - "retry": "0.12.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/bignumber.js": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", - "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "node_modules/buildcheck": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", - "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==", - "optional": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "license": "MIT" - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "license": "ISC", - "engines": { - "node": ">= 10" - } - }, - "node_modules/clipanion": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/clipanion/-/clipanion-3.2.1.tgz", - "integrity": "sha512-dYFdjLb7y1ajfxQopN05mylEpK9ZX0sO1/RfMXdfmwjlIsPkbh4p7A682x++zFPLDCo1x3p82dtljHf5cW2LKA==", - "license": "MIT", - "workspaces": [ - "website" - ], - "dependencies": { - "typanion": "^3.8.0" - }, - "peerDependencies": { - "typanion": "*" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/cpu-features": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", - "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "buildcheck": "~0.0.6", - "nan": "^2.19.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/datadog-metrics": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/datadog-metrics/-/datadog-metrics-0.9.3.tgz", - "integrity": "sha512-BVsBX2t+4yA3tHs7DnB5H01cHVNiGJ/bHA8y6JppJDyXG7s2DLm6JaozPGpgsgVGd42Is1CHRG/yMDQpt877Xg==", - "license": "MIT", - "dependencies": { - "debug": "3.1.0", - "dogapi": "2.8.4" - } - }, - "node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-object-diff": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz", - "integrity": "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==", - "license": "MIT" - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "license": "MIT", - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dogapi": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/dogapi/-/dogapi-2.8.4.tgz", - "integrity": "sha512-065fsvu5dB0o4+ENtLjZILvXMClDNH/yA9H6L8nsdcNiz9l0Hzpn7aQaCOPYXxqyzq4CRPOdwkFXUjDOXfRGbg==", - "license": "MIT", - "dependencies": { - "extend": "^3.0.2", - "json-bigint": "^1.0.0", - "lodash": "^4.17.21", - "minimist": "^1.2.5", - "rc": "^1.2.8" - }, - "bin": { - "dogapi": "bin/dogapi" - } - }, - "node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "license": "MIT", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.2" - } - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ecc-jsbn/node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "license": "MIT" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eventid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/eventid/-/eventid-2.0.1.tgz", - "integrity": "sha512-sPNTqiMokAvV048P2c9+foqVJzk49o6d4e0D/sq5jog3pw+4kBgyR0gaM1FM7Mx6Kzd9dztesh9oYz1LWWOpzw==", - "license": "Apache-2.0", - "dependencies": { - "uuid": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eventid/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", - "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==", - "license": "MIT", - "dependencies": { - "fastest-levenshtein": "^1.0.7" - } - }, - "node_modules/fast-uri": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", - "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", - "license": "BSD-3-Clause" - }, - "node_modules/fast-xml-parser": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", - "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, - "node_modules/fuzzy": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/fuzzy/-/fuzzy-0.1.3.tgz", - "integrity": "sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/gaxios": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", - "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", - "license": "Apache-2.0", - "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "is-stream": "^2.0.0", - "node-fetch": "^2.6.9", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/gcp-metadata": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", - "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", - "license": "Apache-2.0", - "dependencies": { - "gaxios": "^6.0.0", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-uri": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", - "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", - "license": "MIT", - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/get-uri/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/get-uri/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/google-auth-library": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.0.tgz", - "integrity": "sha512-7ccSEJFDFO7exFbO6NRyC+xH8/mZ1GZGG2xxx9iHxZWcjUjJpjWxIMw3cofAKcueZ6DATiukmmprD7yavQHOyQ==", - "license": "Apache-2.0", - "dependencies": { - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "^6.1.1", - "gcp-metadata": "^6.1.0", - "gtoken": "^7.0.0", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/google-gax": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", - "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", - "license": "Apache-2.0", - "dependencies": { - "@grpc/grpc-js": "^1.10.9", - "@grpc/proto-loader": "^0.7.13", - "@types/long": "^4.0.0", - "abort-controller": "^3.0.0", - "duplexify": "^4.0.0", - "google-auth-library": "^9.3.0", - "node-fetch": "^2.7.0", - "object-hash": "^3.0.0", - "proto3-json-serializer": "^2.0.2", - "protobufjs": "^7.3.2", - "retry-request": "^7.0.0", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/gtoken": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", - "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", - "license": "MIT", - "dependencies": { - "gaxios": "^6.0.0", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/html-entities": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", - "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ], - "license": "MIT" - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/inquirer-checkbox-plus-prompt": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/inquirer-checkbox-plus-prompt/-/inquirer-checkbox-plus-prompt-1.4.2.tgz", - "integrity": "sha512-W8/NL9x5A81Oq9ZfbYW5c1LuwtAhc/oB/u9YZZejna0pqrajj27XhnUHygJV0Vn5TvcDy1VJcD2Ld9kTk40dvg==", - "license": "MIT", - "dependencies": { - "chalk": "4.1.2", - "cli-cursor": "^3.1.0", - "figures": "^3.0.0", - "lodash": "^4.17.5", - "rxjs": "^6.6.7" - }, - "peerDependencies": { - "inquirer": "< 9.x" - } - }, - "node_modules/inquirer-checkbox-plus-prompt/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/inquirer-checkbox-plus-prompt/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/inquirer-checkbox-plus-prompt/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "license": "BSD-3-Clause" - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "license": "MIT" - }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/jszip/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "license": "MIT", - "dependencies": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", - "license": "Apache-2.0" - }, - "node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "license": "ISC" - }, - "node_modules/nan": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", - "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", - "license": "MIT", - "optional": true - }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pac-proxy-agent": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.1.0.tgz", - "integrity": "sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==", - "license": "MIT", - "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.6", - "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/pac-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", - "license": "MIT", - "dependencies": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/proto3-json-serializer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", - "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", - "license": "Apache-2.0", - "dependencies": { - "protobufjs": "^7.2.5" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/proxy-agent": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", - "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.6", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.1.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", - "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", - "license": "MIT", - "dependencies": { - "duplexify": "^4.1.1", - "inherits": "^2.0.3", - "pump": "^3.0.0" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/retry-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", - "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", - "license": "MIT", - "dependencies": { - "@types/request": "^2.48.8", - "extend": "^3.0.2", - "teeny-request": "^9.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "license": "ISC" - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/simple-git": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.16.0.tgz", - "integrity": "sha512-zuWYsOLEhbJRWVxpjdiXl6eyAyGo/KzVW+KFhhw9MqEEJttcq+32jTWSGyxTdf9e/YCohxRE+9xpWFj9FdiJNw==", - "license": "MIT", - "dependencies": { - "@kwsites/file-exists": "^1.1.1", - "@kwsites/promise-deferred": "^1.1.1", - "debug": "^4.3.4" - }, - "funding": { - "type": "github", - "url": "https://github.com/steveukx/git-js?sponsor=1" - } - }, - "node_modules/simple-git/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/simple-git/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", - "license": "MIT", - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/socks-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socks-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause" - }, - "node_modules/ssh2": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz", - "integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==", - "hasInstallScript": true, - "dependencies": { - "asn1": "^0.2.6", - "bcrypt-pbkdf": "^1.0.2" - }, - "engines": { - "node": ">=10.16.0" - }, - "optionalDependencies": { - "cpu-features": "~0.0.10", - "nan": "^2.20.0" - } - }, - "node_modules/ssh2-streams": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz", - "integrity": "sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==", - "dependencies": { - "asn1": "~0.2.0", - "bcrypt-pbkdf": "^1.0.2", - "streamsearch": "~0.1.2" - }, - "engines": { - "node": ">=5.2.0" - } - }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sshpk/node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "license": "MIT" - }, - "node_modules/stream-events": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", - "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", - "license": "MIT", - "dependencies": { - "stubs": "^3.0.0" - } - }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", - "license": "MIT" - }, - "node_modules/streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "license": "MIT" - }, - "node_modules/stubs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", - "license": "MIT" - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/teeny-request": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", - "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", - "license": "Apache-2.0", - "dependencies": { - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.9", - "stream-events": "^1.0.5", - "uuid": "^9.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/teeny-request/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/teeny-request/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/teeny-request/node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "license": "MIT", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/teeny-request/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/teeny-request/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "license": "MIT" - }, - "node_modules/tiny-async-pool": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-2.1.0.tgz", - "integrity": "sha512-ltAHPh/9k0STRQqaoUX52NH4ZQYAJz24ZAEwf1Zm+HYg3l9OXTWeqWKyYsHu40wF/F0rxd2N2bk5sLvX2qlSvg==", - "license": "MIT" - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "license": "Unlicense" - }, - "node_modules/typanion": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/typanion/-/typanion-3.14.0.tgz", - "integrity": "sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==", - "license": "MIT", - "workspaces": [ - "website" - ] - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "license": "MIT" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yamux-js": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yamux-js/-/yamux-js-0.1.2.tgz", - "integrity": "sha512-bhsPlPZ9xB4Dawyf6nkS58u4F3IvGCaybkEKGnneUeepcI7MPoG3Tt6SaKCU5x/kP2/2w20Qm/GqbpwAM16vYw==", - "license": "MIT" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - } - } -} diff --git a/src/ci/package.json b/src/ci/package.json deleted file mode 100644 index 91f21e5531e77..0000000000000 --- a/src/ci/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "@datadog/datadog-ci": "^2.45.1" - } -} diff --git a/src/ci/run.sh b/src/ci/run.sh index b874f71832d73..b6143af632ddc 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -6,6 +6,10 @@ if [ -n "$CI_JOB_NAME" ]; then echo "[CI_JOB_NAME=$CI_JOB_NAME]" fi +if [ -n "$CI_JOB_DOC_URL" ]; then + echo "[CI_JOB_DOC_URL=$CI_JOB_DOC_URL]" +fi + if [ "$NO_CHANGE_USER" = "" ]; then if [ "$LOCAL_USER_ID" != "" ]; then id -u user &>/dev/null || useradd --shell /bin/bash -u $LOCAL_USER_ID -o -c "" -m user @@ -54,13 +58,8 @@ if [ "$FORCE_CI_RUSTC" == "" ]; then DISABLE_CI_RUSTC_IF_INCOMPATIBLE=1 fi -if ! isCI || isCiBranch auto || isCiBranch beta || isCiBranch try || isCiBranch try-perf || \ - isCiBranch automation/bors/try; then - RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings --enable-verbose-tests" - RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.metrics" - HAS_METRICS=1 -fi - +RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings --enable-verbose-tests" +RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.metrics" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-verbose-configure" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-sccache" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-manage-submodules" @@ -113,7 +112,11 @@ export RUST_RELEASE_CHANNEL=$(releaseChannel) RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --release-channel=$RUST_RELEASE_CHANNEL" if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then - RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp" + if [[ "$CI_JOB_NAME" == *ohos* ]]; then + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-static-stdcpp" + else + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp" + fi RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.remap-debuginfo" if [ "$DEPLOY_ALT" != "" ] && isLinux; then @@ -180,6 +183,9 @@ else RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.static-libstdcpp" fi + # Download GCC from CI on test builders + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set gcc.download-ci-gcc=true" + if [ "$NO_DOWNLOAD_CI_RUSTC" = "" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.download-rustc=if-unchanged" fi @@ -226,8 +232,8 @@ trap datecheck EXIT # sccache server at the start of the build, but no need to worry if this fails. SCCACHE_IDLE_TIMEOUT=10800 sccache --start-server || true -# Our build may overwrite config.toml, so we remove it here -rm -f config.toml +# Our build may overwrite bootstrap.toml, so we remove it here +rm -f bootstrap.toml $SRC/configure $RUST_CONFIGURE_ARGS @@ -261,23 +267,6 @@ else do_make "$RUST_CHECK_TARGET" fi -if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then - rm -f config.toml - $SRC/configure --set change-id=99999999 - - # Save the build metrics before we wipe the directory - if [ "$HAS_METRICS" = 1 ]; then - mv build/metrics.json . - fi - rm -rf build - if [ "$HAS_METRICS" = 1 ]; then - mkdir build - mv metrics.json build - fi - - CARGO_INCREMENTAL=0 ../x check -fi - echo "::group::sccache stats" -sccache --show-stats || true +sccache --show-adv-stats || true echo "::endgroup::" diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh index 055a6ac2211e3..173f64858b371 100755 --- a/src/ci/scripts/free-disk-space.sh +++ b/src/ci/scripts/free-disk-space.sh @@ -14,6 +14,17 @@ isX86() { fi } +# Check if we're on a GitHub hosted runner. +# In aws codebuild, the variable RUNNER_ENVIRONMENT is "self-hosted". +isGitHubRunner() { + # `:-` means "use the value of RUNNER_ENVIRONMENT if it exists, otherwise use an empty string". + if [[ "${RUNNER_ENVIRONMENT:-}" == "github-hosted" ]]; then + return 0 + else + return 1 + fi +} + # print a line of the specified character printSeparationLine() { for ((i = 0; i < 80; i++)); do @@ -32,7 +43,7 @@ getAvailableSpace() { # make Kb human readable (assume the input is Kb) # REF: https://unix.stackexchange.com/a/44087/60849 formatByteCount() { - numfmt --to=iec-i --suffix=B --padding=7 "$1"'000' + numfmt --to=iec-i --suffix=B --padding=7 "${1}000" } # macro to output saved space @@ -45,6 +56,11 @@ printSavedSpace() { after=$(getAvailableSpace) local saved=$((after - before)) + if [ "$saved" -lt 0 ]; then + echo "::warning::Saved space is negative: $saved. Using '0' as saved space." + saved=0 + fi + echo "" printSeparationLine "*" if [ -n "${title}" ]; then @@ -71,58 +87,83 @@ printDF() { removeUnusedFilesAndDirs() { local to_remove=( - "/usr/local/aws-sam-cli" - "/usr/local/doc/cmake" - "/usr/local/julia"* - "/usr/local/lib/android" - "/usr/local/share/chromedriver-"* - "/usr/local/share/chromium" - "/usr/local/share/cmake-"* - "/usr/local/share/edge_driver" - "/usr/local/share/gecko_driver" - "/usr/local/share/icons" - "/usr/local/share/vim" - "/usr/local/share/emacs" - "/usr/local/share/powershell" - "/usr/local/share/vcpkg" - "/usr/share/apache-maven-"* - "/usr/share/gradle-"* "/usr/share/java" - "/usr/share/kotlinc" - "/usr/share/miniconda" - "/usr/share/php" - "/usr/share/ri" - "/usr/share/swift" - - # binaries - "/usr/local/bin/azcopy" - "/usr/local/bin/bicep" - "/usr/local/bin/ccmake" - "/usr/local/bin/cmake-"* - "/usr/local/bin/cmake" - "/usr/local/bin/cpack" - "/usr/local/bin/ctest" - "/usr/local/bin/helm" - "/usr/local/bin/kind" - "/usr/local/bin/kustomize" - "/usr/local/bin/minikube" - "/usr/local/bin/packer" - "/usr/local/bin/phpunit" - "/usr/local/bin/pulumi-"* - "/usr/local/bin/pulumi" - "/usr/local/bin/stack" - - # Haskell runtime - "/usr/local/.ghcup" - - # Azure - "/opt/az" - "/usr/share/az_"* - - # Environment variable set by GitHub Actions - "$AGENT_TOOLSDIRECTORY" ) + if isGitHubRunner; then + to_remove+=( + "/usr/local/aws-sam-cli" + "/usr/local/doc/cmake" + "/usr/local/julia"* + "/usr/local/lib/android" + "/usr/local/share/chromedriver-"* + "/usr/local/share/chromium" + "/usr/local/share/cmake-"* + "/usr/local/share/edge_driver" + "/usr/local/share/emacs" + "/usr/local/share/gecko_driver" + "/usr/local/share/icons" + "/usr/local/share/powershell" + "/usr/local/share/vcpkg" + "/usr/local/share/vim" + "/usr/share/apache-maven-"* + "/usr/share/gradle-"* + "/usr/share/kotlinc" + "/usr/share/miniconda" + "/usr/share/php" + "/usr/share/ri" + "/usr/share/swift" + + # binaries + "/usr/local/bin/azcopy" + "/usr/local/bin/bicep" + "/usr/local/bin/ccmake" + "/usr/local/bin/cmake-"* + "/usr/local/bin/cmake" + "/usr/local/bin/cpack" + "/usr/local/bin/ctest" + "/usr/local/bin/helm" + "/usr/local/bin/kind" + "/usr/local/bin/kustomize" + "/usr/local/bin/minikube" + "/usr/local/bin/packer" + "/usr/local/bin/phpunit" + "/usr/local/bin/pulumi-"* + "/usr/local/bin/pulumi" + "/usr/local/bin/stack" + + # Haskell runtime + "/usr/local/.ghcup" + + # Azure + "/opt/az" + "/usr/share/az_"* + ) + + if [ -n "${AGENT_TOOLSDIRECTORY:-}" ]; then + # Environment variable set by GitHub Actions + to_remove+=( + "${AGENT_TOOLSDIRECTORY}" + ) + else + echo "::warning::AGENT_TOOLSDIRECTORY is not set. Skipping removal." + fi + else + # Remove folders and files present in AWS CodeBuild + to_remove+=( + # binaries + "/usr/local/bin/ecs-cli" + "/usr/local/bin/eksctl" + "/usr/local/bin/kubectl" + + "${HOME}/.gradle" + "${HOME}/.dotnet" + "${HOME}/.goenv" + "${HOME}/.phpenv" + + ) + fi + for element in "${to_remove[@]}"; do if [ ! -e "$element" ]; then # The file or directory doesn't exist. @@ -155,19 +196,28 @@ cleanPackages() { '^dotnet-.*' '^llvm-.*' '^mongodb-.*' - 'azure-cli' 'firefox' 'libgl1-mesa-dri' 'mono-devel' 'php.*' ) - if isX86; then + if isGitHubRunner; then + packages+=( + azure-cli + ) + + if isX86; then + packages+=( + 'google-chrome-stable' + 'google-cloud-cli' + 'google-cloud-sdk' + 'powershell' + ) + fi + else packages+=( 'google-chrome-stable' - 'google-cloud-cli' - 'google-cloud-sdk' - 'powershell' ) fi diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh index 5522095e3049b..a9528e9291595 100755 --- a/src/ci/scripts/install-clang.sh +++ b/src/ci/scripts/install-clang.sh @@ -10,8 +10,8 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" # Update both macOS's and Windows's tarballs when bumping the version here. -# Try to keep this in sync with src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh -LLVM_VERSION="18.1.4" +# Try to keep this in sync with src/ci/docker/scripts/build-clang.sh +LLVM_VERSION="20.1.3" if isMacOS; then # FIXME: This is the latest pre-built version of LLVM that's available for diff --git a/src/ci/scripts/install-mingw.sh b/src/ci/scripts/install-mingw.sh index c8c501e646a9d..ad852071f2950 100755 --- a/src/ci/scripts/install-mingw.sh +++ b/src/ci/scripts/install-mingw.sh @@ -42,5 +42,5 @@ if isWindows && isKnownToBeMingwBuild; then curl -o mingw.7z "${MIRRORS_BASE}/${mingw_archive}" 7z x -y mingw.7z > /dev/null - ciCommandAddPath "$(pwd)/${mingw_dir}/bin" + ciCommandAddPath "$(cygpath -m "$(pwd)/${mingw_dir}/bin")" fi diff --git a/src/ci/scripts/install-ninja.sh b/src/ci/scripts/install-ninja.sh index 23cbc2eb6d107..7ac19173923cf 100755 --- a/src/ci/scripts/install-ninja.sh +++ b/src/ci/scripts/install-ninja.sh @@ -12,7 +12,7 @@ if isWindows; then 7z x -oninja ninja.zip rm ninja.zip ciCommandSetEnv "RUST_CONFIGURE_ARGS" "${RUST_CONFIGURE_ARGS} --enable-ninja" - ciCommandAddPath "$(pwd)/ninja" + ciCommandAddPath "$(cygpath -m "$(pwd)/ninja")" elif isMacOS; then brew install ninja fi diff --git a/src/ci/scripts/install-sccache.sh b/src/ci/scripts/install-sccache.sh index e143152f330cf..fed06063fa0b3 100755 --- a/src/ci/scripts/install-sccache.sh +++ b/src/ci/scripts/install-sccache.sh @@ -8,12 +8,14 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" if isMacOS; then - curl -fo /usr/local/bin/sccache "${MIRRORS_BASE}/2021-08-25-sccache-v0.2.15-x86_64-apple-darwin" + curl -fo /usr/local/bin/sccache \ + "${MIRRORS_BASE}/2025-02-24-sccache-v0.10.0-x86_64-apple-darwin" chmod +x /usr/local/bin/sccache elif isWindows; then mkdir -p sccache - curl -fo sccache/sccache.exe "${MIRRORS_BASE}/2018-04-26-sccache-x86_64-pc-windows-msvc" - ciCommandAddPath "$(pwd)/sccache" + curl -fo sccache/sccache.exe \ + "${MIRRORS_BASE}/2025-02-24-sccache-v0.10.0-x86_64-pc-windows-msvc.exe" + ciCommandAddPath "$(cygpath -m "$(pwd)/sccache")" fi # FIXME: we should probably install sccache outside the containers and then diff --git a/src/ci/scripts/setup-upstream-remote.sh b/src/ci/scripts/setup-upstream-remote.sh deleted file mode 100755 index 52b4c98a89016..0000000000000 --- a/src/ci/scripts/setup-upstream-remote.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# In CI environments, bootstrap is forced to use the remote upstream based -# on "git_repository" and "nightly_branch" values from src/stage0 file. -# This script configures the remote as it may not exist by default. - -set -euo pipefail -IFS=$'\n\t' - -ci_dir=$(cd $(dirname $0) && pwd)/.. -source "$ci_dir/shared.sh" - -git_repository=$(parse_stage0_file_by_key "git_repository") -nightly_branch=$(parse_stage0_file_by_key "nightly_branch") - -# Configure "rust-lang/rust" upstream remote only when it's not origin. -if [ -z "$(git config remote.origin.url | grep $git_repository)" ]; then - echo "Configuring https://github.com/$git_repository remote as upstream." - git remote add upstream "https://github.com/$git_repository" - REMOTE_NAME="upstream" -else - REMOTE_NAME="origin" -fi - -git fetch $REMOTE_NAME $nightly_branch diff --git a/src/ci/scripts/upload-build-metrics.py b/src/ci/scripts/upload-build-metrics.py deleted file mode 100644 index 915ba95398466..0000000000000 --- a/src/ci/scripts/upload-build-metrics.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -This script postprocesses data gathered during a CI run, computes certain metrics -from them, and uploads these metrics to DataDog. - -This script is expected to be executed from within a GitHub Actions job. - -It expects the following environment variables: -- DATADOG_SITE: path to the DataDog API endpoint -- DATADOG_API_KEY: DataDog API token -- DD_GITHUB_JOB_NAME: Name of the current GitHub Actions job - -It expects the presence of a binary called `datadog-ci` inside `node_modules`. -It can be installed with `npm ci` at `src/ci`. - -Usage: -```bash -$ python3 upload-build-metrics.py -``` - -`path-to-CPU-usage-CSV` is a path to a CSV generated by the `src/ci/cpu-usage-over-time.py` script. -""" - -import argparse -import csv -import os -import subprocess -import sys -from pathlib import Path -from typing import List - - -def load_cpu_usage(path: Path) -> List[float]: - usage = [] - with open(path) as f: - reader = csv.reader(f, delimiter=",") - for row in reader: - # The log might contain incomplete rows or some Python exception - if len(row) == 2: - try: - idle = float(row[1]) - usage.append(100.0 - idle) - except ValueError: - pass - return usage - - -def upload_datadog_measure(name: str, value: float): - """ - Uploads a single numeric metric for the current GitHub Actions job to DataDog. - """ - print(f"Metric {name}: {value:.4f}") - - cmd = "npx" - if os.getenv("GITHUB_ACTIONS") is not None and sys.platform.lower().startswith( - "win" - ): - # Due to weird interaction of MSYS2 and Python, we need to use an absolute path, - # and also specify the ".cmd" at the end. See https://github.com/rust-lang/rust/pull/125771. - cmd = "C:\\Program Files\\nodejs\\npx.cmd" - - subprocess.run( - [ - cmd, - "datadog-ci", - "measure", - "--level", - "job", - "--measures", - f"{name}:{value}", - ], - check=False, - ) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(prog="DataDog metric uploader") - parser.add_argument("cpu-usage-history-csv") - args = parser.parse_args() - - build_usage_csv = vars(args)["cpu-usage-history-csv"] - usage_timeseries = load_cpu_usage(Path(build_usage_csv)) - if len(usage_timeseries) > 0: - avg_cpu_usage = sum(usage_timeseries) / len(usage_timeseries) - else: - avg_cpu_usage = 0 - upload_datadog_measure("avg-cpu-usage", avg_cpu_usage) diff --git a/src/doc/book b/src/doc/book index 4a01a9182496f..230c68bc1e08f 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 4a01a9182496f807aaa5f72d93a25ce18bcbe105 +Subproject commit 230c68bc1e08f5f3228384a28cc228c81dfbd10d diff --git a/src/doc/edition-guide b/src/doc/edition-guide index daa4b763cd848..aa6ce337c0adf 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit daa4b763cd848f986813b5cf8069e1649f7147af +Subproject commit aa6ce337c0adf7a63e33960d184270f2a45ab9ef diff --git a/src/doc/nomicon b/src/doc/nomicon index 8f5c7322b65d0..c76a20f0d9871 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 8f5c7322b65d079aa5b242eb10d89a98e12471e1 +Subproject commit c76a20f0d987145dcedf05c5c073ce8d91f2e82a diff --git a/src/doc/reference b/src/doc/reference index 615b4cec60c26..118fd1f1f0854 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 615b4cec60c269cfc105d511c93287620032d5b0 +Subproject commit 118fd1f1f0854f50e3ae1fe4b64862aad23009ca diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 66543bbc5b7db..c9d151f9147c4 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 66543bbc5b7dbd4e679092c07ae06ba6c73fd912 +Subproject commit c9d151f9147c4808c77f0375ba3fa5d54443cb9e diff --git a/src/doc/rustc-dev-guide/.github/workflows/ci.yml b/src/doc/rustc-dev-guide/.github/workflows/ci.yml index 22a4fb1901ab8..daf5223cbd4ac 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/ci.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/ci.yml @@ -14,10 +14,11 @@ jobs: if: github.repository == 'rust-lang/rustc-dev-guide' runs-on: ubuntu-latest env: - MDBOOK_VERSION: 0.4.21 + MDBOOK_VERSION: 0.4.48 MDBOOK_LINKCHECK2_VERSION: 0.9.1 MDBOOK_MERMAID_VERSION: 0.12.6 MDBOOK_TOC_VERSION: 0.11.2 + MDBOOK_OUTPUT__LINKCHECK__FOLLOW_WEB_LINKS: ${{ github.event_name != 'pull_request' }} DEPLOY_DIR: book/html BASE_SHA: ${{ github.event.pull_request.base.sha }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml index b19eccf9eb8c1..1e430d8b4e65a 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml @@ -28,7 +28,7 @@ jobs: # Cache the josh directory with checked out rustc cache-directories: "/home/runner/.cache/rustc-dev-guide-josh" - name: Install josh - run: RUSTFLAGS="--cap-lints warn" cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 + run: RUSTFLAGS="--cap-lints warn" cargo install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 - name: Setup bot git name and email run: | git config --global user.name 'The rustc-dev-guide Cronjob Bot' diff --git a/src/doc/rustc-dev-guide/README.md b/src/doc/rustc-dev-guide/README.md index 2464ffbbc5032..0425c15f83c1c 100644 --- a/src/doc/rustc-dev-guide/README.md +++ b/src/doc/rustc-dev-guide/README.md @@ -43,13 +43,13 @@ rustdocs][rustdocs]. To build a local static HTML site, install [`mdbook`](https://github.com/rust-lang/mdBook) with: ``` -> cargo install mdbook mdbook-linkcheck2 mdbook-toc mdbook-mermaid +cargo install mdbook mdbook-linkcheck2 mdbook-toc mdbook-mermaid ``` and execute the following command in the root of the repository: ``` -> mdbook build --open +mdbook build --open ``` The build files are found in the `book/html` directory. @@ -61,8 +61,8 @@ checking is **not** run by default locally, though it is in CI. To enable it locally, set the environment variable `ENABLE_LINKCHECK=1` like in the following example. -```console -$ ENABLE_LINKCHECK=1 mdbook serve +``` +ENABLE_LINKCHECK=1 mdbook serve ``` ### Table of Contents @@ -77,21 +77,44 @@ This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.g You'll need to install `josh-proxy` locally via ``` -cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 +cargo install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 ``` Older versions of `josh-proxy` may not round trip commits losslessly so it is important to install this exact version. ### Pull changes from `rust-lang/rust` into this repository + 1) Checkout a new branch that will be used to create a PR into `rust-lang/rustc-dev-guide` 2) Run the pull command ``` - $ cargo run --manifest-path josh-sync/Cargo.toml rustc-pull + cargo run --manifest-path josh-sync/Cargo.toml rustc-pull ``` 3) Push the branch to your fork and create a PR into `rustc-dev-guide` ### Push changes from this repository into `rust-lang/rust` + +NOTE: If you use Git protocol to push to your fork of `rust-lang/rust`, +ensure that you have this entry in your Git config, +else the 2 steps that follow would prompt for a username and password: + +``` +[url "git@github.com:"] +insteadOf = "https://github.com/" +``` + 1) Run the push command to create a branch named `` in a `rustc` fork under the `` account ``` - $ cargo run --manifest-path josh-sync/Cargo.toml rustc-push + cargo run --manifest-path josh-sync/Cargo.toml rustc-push ``` 2) Create a PR from `` into `rust-lang/rust` + +#### Minimal git config + +For simplicity (ease of implementation purposes), the josh-sync script simply calls out to system git. This means that the git invocation may be influenced by global (or local) git configuration. + +You may observe "Nothing to pull" even if you *know* rustc-pull has something to pull if your global git config sets `fetch.prunetags = true` (and possibly other configurations may cause unexpected outcomes). + +To minimize the likelihood of this happening, you may wish to keep a separate *minimal* git config that *only* has `[user]` entries from global git config, then repoint system git to use the minimal git config instead. E.g. + +``` +GIT_CONFIG_GLOBAL=/path/to/minimal/gitconfig GIT_CONFIG_SYSTEM='' cargo run --manifest-path josh-sync/Cargo.toml -- rustc-pull +``` diff --git a/src/doc/rustc-dev-guide/book.toml b/src/doc/rustc-dev-guide/book.toml index 67069d9930f57..b84b1e7548a86 100644 --- a/src/doc/rustc-dev-guide/book.toml +++ b/src/doc/rustc-dev-guide/book.toml @@ -1,6 +1,6 @@ [book] title = "Rust Compiler Development Guide" -author = "The Rust Project Developers" +authors = ["The Rust Project Developers"] description = "A guide to developing the Rust compiler (rustc)" [build] @@ -62,5 +62,7 @@ warning-policy = "error" "/diagnostics/sessiondiagnostic.html" = "diagnostic-structs.html" "/diagnostics/diagnostic-codes.html" = "error-codes.html" "/miri.html" = "const-eval/interpret.html" -"/tests/integration.html" = "ecosystem.html" +"/tests/fuchsia.html" = "ecosystem-test-jobs/fuchsia.html" "/tests/headers.html" = "directives.html" +"/tests/integration.html" = "ecosystem.html" +"/tests/rust-for-linux.html" = "ecosystem-test-jobs/rust-for-linux.html" diff --git a/src/doc/rustc-dev-guide/ci/date-check/Cargo.lock b/src/doc/rustc-dev-guide/ci/date-check/Cargo.lock index 6326b2daf12c0..7107547332f13 100644 --- a/src/doc/rustc-dev-guide/ci/date-check/Cargo.lock +++ b/src/doc/rustc-dev-guide/ci/date-check/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -28,21 +28,24 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "cc" -version = "1.0.106" +version = "1.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2" +checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -52,27 +55,27 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-link", ] [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "date-check" -version = "0.1.0" +version = "0.0.0" dependencies = [ "chrono", "glob", @@ -81,20 +84,21 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -110,24 +114,25 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "libc" -version = "0.2.155" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" @@ -146,33 +151,33 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.10.5" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -182,9 +187,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -193,15 +198,27 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" -version = "2.0.70" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -210,29 +227,30 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -241,9 +259,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -251,9 +269,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -264,79 +282,68 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "windows-core" -version = "0.52.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-targets", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "windows-targets" -version = "0.52.6" +name = "windows-implement" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" +name = "windows-interface" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" +name = "windows-link" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" +name = "windows-result" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" +name = "windows-strings" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] diff --git a/src/doc/rustc-dev-guide/ci/date-check/Cargo.toml b/src/doc/rustc-dev-guide/ci/date-check/Cargo.toml index 472529511d062..f49e6d0db940e 100644 --- a/src/doc/rustc-dev-guide/ci/date-check/Cargo.toml +++ b/src/doc/rustc-dev-guide/ci/date-check/Cargo.toml @@ -1,10 +1,6 @@ [package] name = "date-check" -version = "0.1.0" -authors = ["Noah Lev "] -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +edition = "2024" [dependencies] glob = "0.3" diff --git a/src/doc/rustc-dev-guide/ci/date-check/src/main.rs b/src/doc/rustc-dev-guide/ci/date-check/src/main.rs index 5ab3e6c8b65a8..0a32f4e9b7b66 100644 --- a/src/doc/rustc-dev-guide/ci/date-check/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/date-check/src/main.rs @@ -1,11 +1,8 @@ -use std::{ - collections::BTreeMap, - convert::TryInto as _, - env, fmt, fs, - path::{Path, PathBuf}, - process, - str::FromStr, -}; +use std::collections::BTreeMap; +use std::convert::TryInto as _; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::{env, fmt, fs, process}; use chrono::{Datelike as _, Month, TimeZone as _, Utc}; use glob::glob; @@ -19,19 +16,13 @@ struct Date { impl Date { fn months_since(self, other: Date) -> Option { - let self_chrono = Utc - .with_ymd_and_hms(self.year.try_into().unwrap(), self.month, 1, 0, 0, 0) - .unwrap(); - let other_chrono = Utc - .with_ymd_and_hms(other.year.try_into().unwrap(), other.month, 1, 0, 0, 0) - .unwrap(); + let self_chrono = + Utc.with_ymd_and_hms(self.year.try_into().unwrap(), self.month, 1, 0, 0, 0).unwrap(); + let other_chrono = + Utc.with_ymd_and_hms(other.year.try_into().unwrap(), other.month, 1, 0, 0, 0).unwrap(); let duration_since = self_chrono.signed_duration_since(other_chrono); let months_since = duration_since.num_days() / 30; - if months_since < 0 { - None - } else { - Some(months_since.try_into().unwrap()) - } + if months_since < 0 { None } else { Some(months_since.try_into().unwrap()) } } } @@ -66,26 +57,18 @@ fn collect_dates_from_file(date_regex: &Regex, text: &str) -> Vec<(usize, Date)> date_regex .captures_iter(text) .filter_map(|cap| { - if let (Some(month), Some(year), None, None) | (None, None, Some(month), Some(year)) = ( - cap.name("m1"), - cap.name("y1"), - cap.name("m2"), - cap.name("y2"), - ) { + if let (Some(month), Some(year), None, None) | (None, None, Some(month), Some(year)) = + (cap.name("m1"), cap.name("y1"), cap.name("m2"), cap.name("y2")) + { let year = year.as_str().parse().expect("year"); - let month = Month::from_str(month.as_str()) - .expect("month") - .number_from_month(); + let month = Month::from_str(month.as_str()).expect("month").number_from_month(); Some((cap.get(0).expect("all").range(), Date { year, month })) } else { None } }) .map(|(byte_range, date)| { - line += text[end_of_last_cap..byte_range.end] - .chars() - .filter(|c| *c == '\n') - .count(); + line += text[end_of_last_cap..byte_range.end].chars().filter(|c| *c == '\n').count(); end_of_last_cap = byte_range.end; (line, date) }) @@ -131,17 +114,14 @@ fn filter_dates( fn main() { let mut args = env::args(); if args.len() == 1 { - eprintln!("error: expected root Markdown directory as CLI argument"); + eprintln!("error: expected root of Markdown directory as CLI argument"); process::exit(1); } let root_dir = args.nth(1).unwrap(); let root_dir_path = Path::new(&root_dir); let glob_pat = format!("{}/**/*.md", root_dir); let today_chrono = Utc::now().date_naive(); - let current_month = Date { - year: today_chrono.year_ce().1, - month: today_chrono.month(), - }; + let current_month = Date { year: today_chrono.year_ce().1, month: today_chrono.month() }; let dates_by_file = collect_dates(glob(&glob_pat).unwrap().map(Result::unwrap)); let dates_by_file: BTreeMap<_, _> = @@ -173,10 +153,7 @@ fn main() { println!(); for (path, dates) in dates_by_file { - println!( - "- {}", - path.strip_prefix(&root_dir_path).unwrap_or(&path).display(), - ); + println!("- {}", path.strip_prefix(&root_dir_path).unwrap_or(&path).display(),); for (line, date) in dates { println!(" - [ ] line {}: {}", line, date); } @@ -191,14 +168,8 @@ mod tests { #[test] fn test_months_since() { - let date1 = Date { - year: 2020, - month: 3, - }; - let date2 = Date { - year: 2021, - month: 1, - }; + let date1 = Date { year: 2020, month: 3 }; + let date2 = Date { year: 2021, month: 1 }; assert_eq!(date2.months_since(date1), Some(10)); } @@ -273,83 +244,17 @@ Test8 assert_eq!( collect_dates_from_file(&make_date_regex(), text), vec![ - ( - 3, - Date { - year: 2021, - month: 1, - } - ), - ( - 6, - Date { - year: 2021, - month: 2, - } - ), - ( - 9, - Date { - year: 2021, - month: 3, - } - ), - ( - 11, - Date { - year: 2021, - month: 4, - } - ), - ( - 17, - Date { - year: 2021, - month: 5, - } - ), - ( - 20, - Date { - year: 2021, - month: 1, - } - ), - ( - 23, - Date { - year: 2021, - month: 2, - } - ), - ( - 26, - Date { - year: 2021, - month: 3, - } - ), - ( - 28, - Date { - year: 2021, - month: 4, - } - ), - ( - 34, - Date { - year: 2021, - month: 5, - } - ), - ( - 38, - Date { - year: 2021, - month: 6, - } - ), + (3, Date { year: 2021, month: 1 }), + (6, Date { year: 2021, month: 2 }), + (9, Date { year: 2021, month: 3 }), + (11, Date { year: 2021, month: 4 }), + (17, Date { year: 2021, month: 5 }), + (20, Date { year: 2021, month: 1 }), + (23, Date { year: 2021, month: 2 }), + (26, Date { year: 2021, month: 3 }), + (28, Date { year: 2021, month: 4 }), + (34, Date { year: 2021, month: 5 }), + (38, Date { year: 2021, month: 6 }), ], ); } diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs index 984bd3e37ae30..db6ac18578542 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs @@ -1,4 +1,4 @@ -// Tested with nightly-2025-02-13 +// Tested with nightly-2025-03-28 #![feature(rustc_private)] @@ -34,9 +34,9 @@ impl rustc_span::source_map::FileLoader for MyFileLoader { fn read_file(&self, path: &Path) -> io::Result { if path == Path::new("main.rs") { Ok(r#" +static MESSAGE: &str = "Hello, World!"; fn main() { - let message = "Hello, World!"; - println!("{message}"); + println!("{MESSAGE}"); } "# .to_string()) @@ -71,14 +71,12 @@ impl rustc_driver::Callbacks for MyCallbacks { fn after_analysis(&mut self, _compiler: &Compiler, tcx: TyCtxt<'_>) -> Compilation { // Analyze the program and inspect the types of definitions. - for id in tcx.hir().items() { - let hir = tcx.hir(); - let item = hir.item(id); + for id in tcx.hir_free_items() { + let item = &tcx.hir_item(id); match item.kind { - rustc_hir::ItemKind::Static(_, _, _) | rustc_hir::ItemKind::Fn { .. } => { - let name = item.ident; + rustc_hir::ItemKind::Static(ident, ..) | rustc_hir::ItemKind::Fn { ident, .. } => { let ty = tcx.type_of(item.hir_id().owner.def_id); - println!("{name:?}:\t{ty:?}") + println!("{ident:?}:\t{ty:?}") } _ => (), } diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs index 3270c722e0772..c0d7f977d3500 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs @@ -1,4 +1,4 @@ -// Tested with nightly-2025-02-13 +// Tested with nightly-2025-03-28 #![feature(rustc_private)] @@ -20,7 +20,7 @@ use std::path::Path; use std::sync::Arc; use rustc_ast_pretty::pprust::item_to_string; -use rustc_driver::{run_compiler, Compilation}; +use rustc_driver::{Compilation, run_compiler}; use rustc_interface::interface::{Compiler, Config}; use rustc_middle::ty::TyCtxt; @@ -70,11 +70,9 @@ impl rustc_driver::Callbacks for MyCallbacks { } fn after_analysis(&mut self, _compiler: &Compiler, tcx: TyCtxt<'_>) -> Compilation { - // Every compilation contains a single crate. - let hir_krate = tcx.hir(); // Iterate over the top-level items in the crate, looking for the main function. - for id in hir_krate.items() { - let item = hir_krate.item(id); + for id in tcx.hir_free_items() { + let item = &tcx.hir_item(id); // Use pattern-matching to find a specific node inside the main function. if let rustc_hir::ItemKind::Fn { body, .. } = item.kind { let expr = &tcx.hir_body(body).value; diff --git a/src/doc/rustc-dev-guide/examples/rustc-interface-example.rs b/src/doc/rustc-dev-guide/examples/rustc-interface-example.rs index 70f27c2a82a9c..360f70c8e865a 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-interface-example.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-interface-example.rs @@ -1,4 +1,4 @@ -// Tested with nightly-2025-02-13 +// Tested with nightly-2025-03-28 #![feature(rustc_private)] @@ -64,14 +64,13 @@ fn main() { println!("{krate:?}"); // Analyze the program and inspect the types of definitions. rustc_interface::create_and_enter_global_ctxt(&compiler, krate, |tcx| { - for id in tcx.hir().items() { - let hir = tcx.hir(); - let item = hir.item(id); + for id in tcx.hir_free_items() { + let item = tcx.hir_item(id); match item.kind { - rustc_hir::ItemKind::Static(_, _, _) | rustc_hir::ItemKind::Fn { .. } => { - let name = item.ident; + rustc_hir::ItemKind::Static(ident, ..) + | rustc_hir::ItemKind::Fn { ident, .. } => { let ty = tcx.type_of(item.hir_id().owner.def_id); - println!("{name:?}:\t{ty:?}") + println!("{ident:?}:\t{ty:?}") } _ => (), } diff --git a/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs b/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs index 39b236e1783a6..2512ba3c3f924 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs @@ -1,4 +1,4 @@ -// Tested with nightly-2025-02-13 +// Tested with nightly-2025-03-28 #![feature(rustc_private)] @@ -86,8 +86,10 @@ fn main() { rustc_interface::run_compiler(config, |compiler| { let krate = rustc_interface::passes::parse(&compiler.sess); rustc_interface::create_and_enter_global_ctxt(&compiler, krate, |tcx| { - // Run the analysis phase on the local crate to trigger the type error. - let _ = tcx.analysis(()); + // Iterate all the items defined and perform type checking. + tcx.par_hir_body_owners(|item_def_id| { + tcx.ensure_ok().typeck(item_def_id); + }); }); // If the compiler has encountered errors when this closure returns, it will abort (!) the program. // We avoid this by resetting the error count before returning diff --git a/src/doc/rustc-dev-guide/josh-sync/Cargo.lock b/src/doc/rustc-dev-guide/josh-sync/Cargo.lock index 844518628c437..a8183a740db3b 100644 --- a/src/doc/rustc-dev-guide/josh-sync/Cargo.lock +++ b/src/doc/rustc-dev-guide/josh-sync/Cargo.lock @@ -161,7 +161,7 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "josh-sync" -version = "0.1.0" +version = "0.0.0" dependencies = [ "anyhow", "clap", diff --git a/src/doc/rustc-dev-guide/josh-sync/Cargo.toml b/src/doc/rustc-dev-guide/josh-sync/Cargo.toml index 81d0d1ebd22d8..1f8bf2a009333 100644 --- a/src/doc/rustc-dev-guide/josh-sync/Cargo.toml +++ b/src/doc/rustc-dev-guide/josh-sync/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "josh-sync" -version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] anyhow = "1.0.95" diff --git a/src/doc/rustc-dev-guide/josh-sync/src/main.rs b/src/doc/rustc-dev-guide/josh-sync/src/main.rs index 175f016f73907..aeedee5be22d1 100644 --- a/src/doc/rustc-dev-guide/josh-sync/src/main.rs +++ b/src/doc/rustc-dev-guide/josh-sync/src/main.rs @@ -1,4 +1,5 @@ use clap::Parser; + use crate::sync::{GitSync, RustcPullError}; mod sync; @@ -11,10 +12,7 @@ enum Args { /// Push changes from `rustc-dev-guide` to the given `branch` of a `rustc` fork under the given /// GitHub `username`. /// The pushed branch should then be merged into the `rustc` repository. - RustcPush { - branch: String, - github_username: String - } + RustcPush { branch: String, github_username: String }, } fn main() -> anyhow::Result<()> { diff --git a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs index cd64be6367032..ed38d1403a02b 100644 --- a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs +++ b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs @@ -1,10 +1,11 @@ +use std::io::Write; use std::ops::Not; use std::path::PathBuf; -use std::{env, net, process}; -use std::io::Write; use std::time::Duration; -use anyhow::{anyhow, bail, Context}; -use xshell::{cmd, Shell}; +use std::{env, net, process}; + +use anyhow::{Context, anyhow, bail}; +use xshell::{Shell, cmd}; /// Used for rustc syncs. const JOSH_FILTER: &str = ":/src/doc/rustc-dev-guide"; @@ -15,10 +16,13 @@ pub enum RustcPullError { /// No changes are available to be pulled. NothingToPull, /// A rustc-pull has failed, probably a git operation error has occurred. - PullFailed(anyhow::Error) + PullFailed(anyhow::Error), } -impl From for RustcPullError where E: Into { +impl From for RustcPullError +where + E: Into, +{ fn from(error: E) -> Self { Self::PullFailed(error.into()) } @@ -32,9 +36,7 @@ pub struct GitSync { /// (https://github.com/rust-lang/miri/blob/6a68a79f38064c3bc30617cca4bdbfb2c336b140/miri-script/src/commands.rs#L236). impl GitSync { pub fn from_current_dir() -> anyhow::Result { - Ok(Self { - dir: std::env::current_dir()? - }) + Ok(Self { dir: std::env::current_dir()? }) } pub fn rustc_pull(&self, commit: Option) -> Result<(), RustcPullError> { @@ -51,7 +53,10 @@ impl GitSync { })?; // Make sure the repo is clean. if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { - return Err(anyhow::anyhow!("working directory must be clean before performing rustc pull").into()); + return Err(anyhow::anyhow!( + "working directory must be clean before performing rustc pull" + ) + .into()); } // Make sure josh is running. let josh = Self::start_josh()?; @@ -94,7 +99,8 @@ impl GitSync { }; let num_roots_before = num_roots()?; - let sha = cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout; + let sha = + cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout; // Merge the fetched commit. const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc"; @@ -102,18 +108,24 @@ impl GitSync { .run() .context("FAILED to merge new commits, something went wrong")?; - let current_sha = cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout; + let current_sha = + cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout; if current_sha == sha { cmd!(sh, "git reset --hard HEAD^") .run() .expect("FAILED to clean up after creating the preparation commit"); - eprintln!("No merge was performed, no changes to pull were found. Rolled back the preparation commit."); + eprintln!( + "No merge was performed, no changes to pull were found. Rolled back the preparation commit." + ); return Err(RustcPullError::NothingToPull); } // Check that the number of roots did not increase. if num_roots()? != num_roots_before { - return Err(anyhow::anyhow!("Josh created a new root commit. This is probably not the history you want.").into()); + return Err(anyhow::anyhow!( + "Josh created a new root commit. This is probably not the history you want." + ) + .into()); } drop(josh); @@ -194,7 +206,7 @@ impl GitSync { ); println!( // Open PR with `subtree update` title to silence the `no-merges` triagebot check - " https://github.com/{UPSTREAM_REPO}/compare/{github_user}:{branch}?quick_pull=1&title=Rustc+dev+guide+subtree+update&body=r?+@ghost" + " https://github.com/{UPSTREAM_REPO}/compare/{github_user}:{branch}?quick_pull=1&title=rustc-dev-guide+subtree+update&body=r?+@ghost" ); drop(josh); diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index ce21bb8ef3907..0d889a5d5b991 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -4ecd70ddd1039a3954056c1071e40278048476fa +e42bbfe1f7c26f8760a99c4b1f27d33aba1040bb diff --git a/src/doc/rustc-dev-guide/rustfmt.toml b/src/doc/rustc-dev-guide/rustfmt.toml new file mode 100644 index 0000000000000..b285329c78e61 --- /dev/null +++ b/src/doc/rustc-dev-guide/rustfmt.toml @@ -0,0 +1,7 @@ +# matches that of rust-lang/rust +style_edition = "2024" +use_small_heuristics = "Max" +merge_derives = false +group_imports = "StdExternalCrate" +imports_granularity = "Module" +use_field_init_shorthand = true diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 106db508ebbc7..a7b76233d1978 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -1,6 +1,7 @@ # Summary [Getting Started](./getting-started.md) + [About this guide](./about-this-guide.md) --- @@ -10,9 +11,9 @@ - [How to build and run the compiler](./building/how-to-build-and-run.md) - [Quickstart](./building/quickstart.md) - [Prerequisites](./building/prerequisites.md) - - [Suggested Workflows](./building/suggested.md) + - [Suggested workflows](./building/suggested.md) - [Distribution artifacts](./building/build-install-distribution-artifacts.md) - - [Building Documentation](./building/compiler-documenting.md) + - [Building documentation](./building/compiler-documenting.md) - [Rustdoc overview](./rustdoc.md) - [Adding a new target](./building/new-target.md) - [Optimized build](./building/optimized-build.md) @@ -28,8 +29,11 @@ - [Minicore](./tests/minicore.md) - [Ecosystem testing](./tests/ecosystem.md) - [Crater](./tests/crater.md) - - [Fuchsia](./tests/fuchsia.md) - - [Rust for Linux](./tests/rust-for-linux.md) + - [Fuchsia](./tests/ecosystem-test-jobs/fuchsia.md) + - [Rust for Linux](./tests/ecosystem-test-jobs/rust-for-linux.md) + - [Codegen backend testing](./tests/codegen-backend-tests/intro.md) + - [Cranelift codegen backend](./tests/codegen-backend-tests/cg_clif.md) + - [GCC codegen backend](./tests/codegen-backend-tests/cg_gcc.md) - [Performance testing](./tests/perf.md) - [Suggest tests tool](./tests/suggest-tests.md) - [Misc info](./tests/misc.md) @@ -39,11 +43,11 @@ - [with the linux perf tool](./profiling/with_perf.md) - [with Windows Performance Analyzer](./profiling/wpa_profiling.md) - [with the Rust benchmark suite](./profiling/with_rustc_perf.md) -- [crates.io Dependencies](./crates-io.md) +- [crates.io dependencies](./crates-io.md) # Contributing to Rust -- [Contribution Procedures](./contributing.md) +- [Contribution procedures](./contributing.md) - [About the compiler team](./compiler-team.md) - [Using Git](./git.md) - [Mastering @rustbot](./rustbot.md) @@ -53,7 +57,7 @@ - [Stabilizing Features](./stabilization_guide.md) - [Feature Gates](./feature-gates.md) - [Coding conventions](./conventions.md) -- [Procedures for Breaking Changes](./bug-fix-procedure.md) +- [Procedures for breaking changes](./bug-fix-procedure.md) - [Using external repositories](./external-repos.md) - [Fuzzing](./fuzzing.md) - [Notification groups](notification-groups/about.md) @@ -61,12 +65,13 @@ - [ARM](notification-groups/arm.md) - [Cleanup Crew](notification-groups/cleanup-crew.md) - [Emscripten](notification-groups/emscripten.md) + - [Fuchsia](notification-groups/fuchsia.md) - [LLVM](notification-groups/llvm.md) - [RISC-V](notification-groups/risc-v.md) + - [Rust for Linux](notification-groups/rust-for-linux.md) - [WASI](notification-groups/wasi.md) - [WebAssembly](notification-groups/wasm.md) - [Windows](notification-groups/windows.md) - - [Rust for Linux](notification-groups/rust-for-linux.md) - [Licenses](./licenses.md) - [Editions](guides/editions.md) @@ -77,6 +82,7 @@ - [How Bootstrap does it](./building/bootstrapping/how-bootstrap-does-it.md) - [Writing tools in Bootstrap](./building/bootstrapping/writing-tools-in-bootstrap.md) - [Debugging bootstrap](./building/bootstrapping/debugging-bootstrap.md) +- [cfg(bootstrap) in dependencies](./building/bootstrapping/bootstrap-in-dependencies.md) # High-level Compiler Architecture @@ -84,29 +90,35 @@ - [Overview of the compiler](./overview.md) - [The compiler source code](./compiler-src.md) - [Queries: demand-driven compilation](./query.md) - - [The Query Evaluation Model in Detail](./queries/query-evaluation-model-in-detail.md) + - [The Query Evaluation Model in detail](./queries/query-evaluation-model-in-detail.md) - [Incremental compilation](./queries/incremental-compilation.md) - - [Incremental compilation In Detail](./queries/incremental-compilation-in-detail.md) - - [Debugging and Testing](./incrcomp-debugging.md) + - [Incremental compilation in detail](./queries/incremental-compilation-in-detail.md) + - [Debugging and testing](./incrcomp-debugging.md) - [Salsa](./queries/salsa.md) -- [Memory Management in Rustc](./memory.md) -- [Serialization in Rustc](./serialization.md) -- [Parallel Compilation](./parallel-rustc.md) +- [Memory management in rustc](./memory.md) +- [Serialization in rustc](./serialization.md) +- [Parallel compilation](./parallel-rustc.md) - [Rustdoc internals](./rustdoc-internals.md) - [Search](./rustdoc-internals/search.md) + - [The `rustdoc` test suite](./rustdoc-internals/rustdoc-test-suite.md) +- [Autodiff internals](./autodiff/internals.md) + - [Installation](./autodiff/installation.md) + - [How to debug](./autodiff/debugging.md) + - [Autodiff flags](./autodiff/flags.md) + - [Current limitations](./autodiff/limitations.md) # Source Code Representation - [Prologue](./part-3-intro.md) - [Syntax and the AST](./syntax-intro.md) - - [Lexing and Parsing](./the-parser.md) + - [Lexing and parsing](./the-parser.md) - [Macro expansion](./macro-expansion.md) - [Name resolution](./name-resolution.md) - [Attributes](./attributes.md) - - [`#[test]` Implementation](./test-implementation.md) - - [Panic Implementation](./panic-implementation.md) - - [AST Validation](./ast-validation.md) - - [Feature Gate Checking](./feature-gate-ck.md) + - [`#[test]` implementation](./test-implementation.md) + - [Panic implementation](./panic-implementation.md) + - [AST validation](./ast-validation.md) + - [Feature gate checking](./feature-gate-ck.md) - [Lang Items](./lang-items.md) - [The HIR (High-level IR)](./hir.md) - [Lowering AST to HIR](./ast-lowering.md) @@ -122,9 +134,10 @@ - [Command-line arguments](./cli.md) - [rustc_driver and rustc_interface](./rustc-driver/intro.md) + - [Remarks on perma-unstable features](./rustc-driver/remarks-on-perma-unstable-features.md) - [Example: Type checking](./rustc-driver/interacting-with-the-ast.md) - [Example: Getting diagnostics](./rustc-driver/getting-diagnostics.md) -- [Errors and Lints](diagnostics.md) +- [Errors and lints](diagnostics.md) - [Diagnostic and subdiagnostic structs](./diagnostics/diagnostic-structs.md) - [Translation](./diagnostics/translation.md) - [`LintStore`](./diagnostics/lintstore.md) @@ -144,10 +157,8 @@ - [ADTs and Generic Arguments](./ty_module/generic_arguments.md) - [Parameter types/consts/regions](./ty_module/param_ty_const_regions.md) - [`TypeFolder` and `TypeFoldable`](./ty-fold.md) -- [Parameter Environments](./param_env/param_env_summary.md) - - [What is it?](./param_env/param_env_what_is_it.md) - - [How are `ParamEnv`'s constructed internally](./param_env/param_env_construction_internals.md) - - [Which `ParamEnv` do I use?](./param_env/param_env_acquisition.md) +- [Aliases and Normalization](./normalization.md) +- [Typing/Param Envs](./typing_parameter_envs.md) - [Type inference](./type-inference.md) - [Trait solving](./traits/resolution.md) - [Higher-ranked trait bounds](./traits/hrtb.md) @@ -166,21 +177,20 @@ - [Coinduction](./solve/coinduction.md) - [Caching](./solve/caching.md) - [Proof trees](./solve/proof-trees.md) - - [Normalization](./solve/normalization.md) - [Opaque types](./solve/opaque-types.md) - [Significant changes and quirks](./solve/significant-changes.md) - [`Unsize` and `CoerceUnsized` traits](./traits/unsize.md) - [Type checking](./type-checking.md) - [Method Lookup](./method-lookup.md) - [Variance](./variance.md) - - [Coherence Checking](./coherence.md) - - [Opaque Types](./opaque-types-type-alias-impl-trait.md) + - [Coherence checking](./coherence.md) + - [Opaque types](./opaque-types-type-alias-impl-trait.md) - [Inference details](./opaque-types-impl-trait-inference.md) - [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md) - [Region inference restrictions][opaque-infer] -- [Effect checking](./effects.md) +- [Const condition checking](./effects.md) - [Pattern and Exhaustiveness Checking](./pat-exhaustive-checking.md) -- [Unsafety Checking](./unsafety-checking.md) +- [Unsafety checking](./unsafety-checking.md) - [MIR dataflow](./mir/dataflow.md) - [Drop elaboration](./mir/drop-elaboration.md) - [The borrow checker](./borrow_check.md) @@ -222,9 +232,13 @@ --- [Appendix A: Background topics](./appendix/background.md) + [Appendix B: Glossary](./appendix/glossary.md) + [Appendix C: Code Index](./appendix/code-index.md) + [Appendix D: Compiler Lecture Series](./appendix/compiler-lecture.md) + [Appendix E: Bibliography](./appendix/bibliography.md) [Appendix Z: HumorRust](./appendix/humorust.md) diff --git a/src/doc/rustc-dev-guide/src/about-this-guide.md b/src/doc/rustc-dev-guide/src/about-this-guide.md index 781a5c51bf7a8..057e4a4cceed3 100644 --- a/src/doc/rustc-dev-guide/src/about-this-guide.md +++ b/src/doc/rustc-dev-guide/src/about-this-guide.md @@ -3,33 +3,41 @@ This guide is meant to help document how rustc – the Rust compiler – works, as well as to help new contributors get involved in rustc development. -There are seven parts to this guide: +There are several parts to this guide: -1. [Building `rustc`][p1]: +1. [Building and debugging `rustc`][p1]: Contains information that should be useful no matter how you are contributing, about building, debugging, profiling, etc. -2. [Contributing to `rustc`][p2]: +1. [Contributing to Rust][p2]: Contains information that should be useful no matter how you are contributing, about procedures for contribution, using git and Github, stabilizing features, etc. -3. [High-Level Compiler Architecture][p3]: +1. [Bootstrapping][p3]: + Describes how the Rust compiler builds itself using previous versions, including + an introduction to the bootstrap process and debugging methods. +1. [High-level Compiler Architecture][p4]: Discusses the high-level architecture of the compiler and stages of the compile process. -4. [Source Code Representation][p4]: +1. [Source Code Representation][p5]: Describes the process of taking raw source code from the user and transforming it into various forms that the compiler can work with easily. -5. [Analysis][p5]: - discusses the analyses that the compiler uses to check various properties of the code +1. [Supporting Infrastructure][p6]: + Covers command-line argument conventions, compiler entry points like rustc_driver and + rustc_interface, and the design and implementation of errors and lints. +1. [Analysis][p7]: + Discusses the analyses that the compiler uses to check various properties of the code and inform later stages of the compile process (e.g., type checking). -6. [From MIR to Binaries][p6]: How linked executable machine code is generated. -7. [Appendices][p7] at the end with useful reference information. +1. [MIR to Binaries][p8]: How linked executable machine code is generated. +1. [Appendices][p9] at the end with useful reference information. There are a few of these with different information, including a glossary. [p1]: ./building/how-to-build-and-run.html [p2]: ./contributing.md -[p3]: ./part-2-intro.md -[p4]: ./part-3-intro.md -[p5]: ./part-4-intro.md -[p6]: ./part-5-intro.md -[p7]: ./appendix/background.md +[p3]: ./building/bootstrapping/intro.md +[p4]: ./part-2-intro.md +[p5]: ./part-3-intro.md +[p6]: ./cli.md +[p7]: ./part-4-intro.md +[p8]: ./part-5-intro.md +[p9]: ./appendix/background.md ### Constant change diff --git a/src/doc/rustc-dev-guide/src/appendix/code-index.md b/src/doc/rustc-dev-guide/src/appendix/code-index.md index b96ede68eab51..65fbf752d7921 100644 --- a/src/doc/rustc-dev-guide/src/appendix/code-index.md +++ b/src/doc/rustc-dev-guide/src/appendix/code-index.md @@ -40,5 +40,5 @@ Item | Kind | Short description | Chapter | [Emitting Diagnostics]: ../diagnostics.html [Macro expansion]: ../macro-expansion.html [Name resolution]: ../name-resolution.html -[Parameter Environment]: ../param_env/param_env_summary.html +[Parameter Environment]: ../typing_parameter_envs.html [Trait Solving: Goals and Clauses]: ../traits/goals-and-clauses.html#domain-goals diff --git a/src/doc/rustc-dev-guide/src/appendix/glossary.md b/src/doc/rustc-dev-guide/src/appendix/glossary.md index a7c3236d356ba..1837b59e850ae 100644 --- a/src/doc/rustc-dev-guide/src/appendix/glossary.md +++ b/src/doc/rustc-dev-guide/src/appendix/glossary.md @@ -31,7 +31,6 @@ Term | Meaning generics | The list of generic parameters defined on an item. There are three kinds of generic parameters: Type, lifetime and const parameters. HIR | The _high-level [IR](#ir)_, created by lowering and desugaring the AST. ([see more](../hir.md)) `HirId` | Identifies a particular node in the HIR by combining a def-id with an "intra-definition offset". See [the HIR chapter for more](../hir.md#identifiers-in-the-hir). -HIR map | The HIR map, accessible via `tcx.hir()`, allows you to quickly navigate the HIR and convert between various forms of identifiers. ICE | Short for _internal compiler error_, this is when the compiler crashes. ICH | Short for _incremental compilation hash_, these are used as fingerprints for things such as HIR and crate metadata, to check if changes have been made. This is useful in incremental compilation to see if part of a crate has changed and should be recompiled. `infcx` | The type inference context (`InferCtxt`). (see `rustc_middle::infer`) diff --git a/src/doc/rustc-dev-guide/src/ast-validation.md b/src/doc/rustc-dev-guide/src/ast-validation.md index fa0f1d954f86d..8f10bbecf21d0 100644 --- a/src/doc/rustc-dev-guide/src/ast-validation.md +++ b/src/doc/rustc-dev-guide/src/ast-validation.md @@ -1,4 +1,4 @@ -# AST Validation +# AST validation _AST validation_ is a separate AST pass that visits each item in the tree and performs simple checks. This pass diff --git a/src/doc/rustc-dev-guide/src/autodiff/debugging.md b/src/doc/rustc-dev-guide/src/autodiff/debugging.md new file mode 100644 index 0000000000000..bd46a66fade47 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/autodiff/debugging.md @@ -0,0 +1,113 @@ +# Reporting backend crashes + +If after a compilation failure you are greeted by a large amount of llvm-ir code, then our enzyme backend likely failed to compile your code. These cases are harder to debug, so your help is highly appreciated. Please also keep in mind that release builds are usually much more likely to work at the moment. + +The final goal here is to reproduce your bug in the enzyme [compiler explorer](https://enzyme.mit.edu/explorer/), in order to create a bug report in the [Enzyme](https://github.com/enzymead/enzyme/issues) repository. + +We have an `autodiff` flag which you can pass to `rustflags` to help with this. it will print the whole llvm-ir module, along with some `__enzyme_fwddiff` or `__enzyme_autodiff` calls. A potential workflow on linux could look like: + +## Controlling llvm-ir generation + +Before generating the llvm-ir, keep in mind two techniques that can help ensure the relevant rust code is visible for debugging: + +- **`std::hint::black_box`**: wrap rust variables or expressions in `std::hint::black_box()` to prevent rust and llvm from optimizing them away. This is useful when you need to inspect or manually manipulate specific values in the llvm-ir. +- **`extern "rust"` or `extern "c"`**: if you want to see how a specific function declaration is lowered to llvm-ir, you can declare it as `extern "rust"` or `extern "c"`. You can also look for existing `__enzyme_autodiff` or similar declarations within the generated module for examples. + +## 1) Generate an llvm-ir reproducer + +```sh +rustflags="-z autodiff=enable,printmodbefore" cargo +enzyme build --release &> out.ll +``` + +This also captures a few warnings and info messages above and below your module. open out.ll and remove every line above `; moduleid = `. Now look at the end of the file and remove everything that's not part of llvm-ir, i.e. remove errors and warnings. The last line of your llvm-ir should now start with `! = `, i.e. `!40831 = !{i32 0, i32 1037508, i32 1037538, i32 1037559}` or `!43760 = !dilocation(line: 297, column: 5, scope: !43746)`. + +The actual numbers will depend on your code. + +## 2) Check your llvm-ir reproducer + +To confirm that your previous step worked, we will use llvm's `opt` tool. find your path to the opt binary, with a path similar to `/rust/build//build/bin/opt`. also find `llvmenzyme-19.` path, similar to `/rust/build/target-tripple/enzyme/build/enzyme/llvmenzyme-19`. Please keep in mind that llvm frequently updates it's llvm backend, so the version number might be higher (20, 21, ...). Once you have both, run the following command: + +```sh + out.ll -load-pass-plugin=/path/to/llvmenzyme-19.so -passes="enzyme" -s +``` + +If the previous step succeeded, you are going to see the same error that you saw when compiling your rust code with cargo. + +If you fail to get the same error, please open an issue in the rust repository. If you succeed, congrats! the file is still huge, so let's automatically minimize it. + +## 3) Minimize your llvm-ir reproducer + +First find your `llvm-extract` binary, it's in the same folder as your opt binary. then run: + +```sh + -s --func= --recursive --rfunc="enzyme_autodiff*" --rfunc="enzyme_fwddiff*" --rfunc= out.ll -o mwe.ll +``` + +This command creates `mwe.ll`, a minimal working example. + +Please adjust the name passed with the last `--func` flag. You can either apply the `#[no_mangle]` attribute to the function you differentiate, then you can replace it with the rust name. otherwise you will need to look up the mangled function name. To do that, open `out.ll` and search for `__enzyme_fwddiff` or `__enzyme_autodiff`. the first string in that function call is the name of your function. example: + +```llvm-ir +define double @enzyme_opt_helper_0(ptr %0, i64 %1, double %2) { + %4 = call double (...) @__enzyme_fwddiff(ptr @_zn2ad3_f217h3b3b1800bd39fde3e, metadata !"enzyme_const", ptr %0, metadata !"enzyme_const", i64 %1, metadata !"enzyme_dup", double %2, double %2) + ret double %4 +} +``` + +Here, `_zn2ad3_f217h3b3b1800bd39fde3e` is the correct name. make sure to not copy the leading `@`. redo step 2) by running the `opt` command again, but this time passing `mwe.ll` as the input file instead of `out.ll`. Check if this minimized example still reproduces the crash. + +## 4) (Optional) Minimize your llvm-ir reproducer further. + +After the previous step you should have an `mwe.ll` file with ~5k loc. let's try to get it down to 50. find your `llvm-reduce` binary next to `opt` and `llvm-extract`. Copy the first line of your error message, an example could be: + +```sh +opt: /home/manuel/prog/rust/src/llvm-project/llvm/lib/ir/instructions.cpp:686: void llvm::callinst::init(llvm::functiontype*, llvm::value*, llvm::arrayref, llvm::arrayref >, const llvm::twine&): assertion `(args.size() == fty->getnumparams() || (fty->isvararg() && args.size() > fty->getnumparams())) && "calling a function with bad signature!"' failed. +``` + +If you just get a `segfault` there is no sensible error message and not much to do automatically, so continue to 5). +otherwise, create a `script.sh` file containing + +```sh +#!/bin/bash + $1 -load-pass-plugin=/path/to/llvmenzyme-19.so -passes="enzyme" \ + |& grep "/some/path.cpp:686: void llvm::callinst::init" +``` + +Experiment a bit with which error message you pass to grep. it should be long enough to make sure that the error is unique. However, for longer errors including `(` or `)` you will need to escape them correctly which can become annoying. Run + +```sh + --test=script.sh mwe.ll +``` + +If you see `input isn't interesting! verify interesting-ness test`, you got the error message in script.sh wrong, you need to make sure that grep matches your actual error. If all works out, you will see a lot of iterations, ending with a new `reduced.ll` file. Verify with `opt` that you still get the same error. + +### Advanced debugging: manual llvm-ir investigation + +Once you have a minimized reproducer (`mwe.ll` or `reduced.ll`), you can delve deeper: + +- **manual editing:** try manually rewriting the llvm-ir. for certain issues, like those involving indirect calls, you might investigate enzyme-specific intrinsics like `__enzyme_virtualreverse`. Understanding how to use these might require consulting enzyme's documentation or source code. +- **enzyme test cases:** look for relevant test cases within the [enzyme repository](https://github.com/enzymead/enzyme/tree/main/enzyme/test) that might demonstrate the correct usage of features or intrinsics related to your problem. + +## 5) Report your bug. + +Afterwards, you should be able to copy and paste your `mwe.ll` (or `reduced.ll`) example into our [compiler explorer](https://enzyme.mit.edu/explorer/). + +- Select `llvm ir` as language and `opt 20` as compiler. +- Replace the field to the right of your compiler with `-passes="enzyme"`, if it is not already set. +- Hopefully, you will see once again your now familiar error. +- Please use the share button to copy links to them. +- Please create an issue on [https://github.com/enzymead/enzyme/issues](https://github.com/enzymead/enzyme/issues) and share `mwe.ll` and (if you have it) `reduced.ll`, as well as links to the compiler explorer. Please feel free to also add your rust code or a link to it. + +#### Documenting findings + +some enzyme errors, like `"attempting to call an indirect active function whose runtime value is inactive"`, have historically caused confusion. If you investigate such an issue, even if you don't find a complete solution, please consider documenting your findings. If the insights are general to enzyme and not specific to its rust usage, contributing them to the main [enzyme documentation](https://github.com/enzymead/www) is often the best first step. You can also mention your findings in the relevant enzyme github issue or propose updates to these docs if appropriate. This helps prevent others from starting from scratch. + +With a clear reproducer and documentation, hopefully an enzyme developer will be able to fix your bug. Once that happens, the enzyme submodule inside the rust compiler will be updated, which should allow you to differentiate your rust code. Thanks for helping us to improve rust-ad. + +# Minimize rust code + +Beyond having a minimal llvm-ir reproducer, it is also helpful to have a minimal rust reproducer without dependencies. This allows us to add it as a test case to ci once we fix it, which avoids regressions for the future. + +There are a few solutions to help you with minimizing the rust reproducer. This is probably the most simple automated approach: [cargo-minimize](https://github.com/nilstrieb/cargo-minimize). + +Otherwise we have various alternatives, including [`treereduce`](https://github.com/langston-barrett/treereduce), [`halfempty`](https://github.com/googleprojectzero/halfempty), or [`picireny`](https://github.com/renatahodovan/picireny), potentially also [`creduce`](https://github.com/csmith-project/creduce). diff --git a/src/doc/rustc-dev-guide/src/autodiff/flags.md b/src/doc/rustc-dev-guide/src/autodiff/flags.md new file mode 100644 index 0000000000000..946ae1d03ae6a --- /dev/null +++ b/src/doc/rustc-dev-guide/src/autodiff/flags.md @@ -0,0 +1,42 @@ +# Supported `RUSTFLAGS` + +To support you while debugging or profiling, we have added support for an experimental `-Z autodiff` rustc flag (which can be passed to cargo via `RUSTFLAGS`), which allow changing the behaviour of Enzyme, without recompiling rustc. We currently support the following values for `autodiff`. + +### Debug Flags + +```text +PrintTA // Print TypeAnalysis information +PrintAA // Print ActivityAnalysis information +Print // Print differentiated functions while they are being generated and optimized +PrintPerf // Print AD related Performance warnings +PrintModBefore // Print the whole LLVM-IR module directly before running AD +PrintModAfter // Print the whole LLVM-IR module after running AD, before optimizations +PrintModFinal // Print the whole LLVM-IR module after running optimizations and AD +LooseTypes // Risk incorrect derivatives instead of aborting when missing Type Info +``` + +
    +`LooseTypes` is often helpful to get rid of Enzyme errors stating `Can not deduce type of ` and to be able to run some code. But please keep in mind that this flag absolutely has the chance to cause incorrect gradients. Even worse, the gradients might be correct for certain input values, but not for others. So please create issues about such bugs and only use this flag temporarily while you wait for your bug to be fixed. +
    + +### Benchmark flags + +For performance experiments and benchmarking we also support + +```text +NoPostopt // We won't optimize the LLVM-IR Module after AD +RuntimeActivity // Enables the runtime activity feature from Enzyme +Inline // Instructs Enzyme to maximize inlining as far as possible, beyond LLVM's default +``` + +You can combine multiple `autodiff` values using a comma as separator: + +```bash +RUSTFLAGS="-Z autodiff=Enable,LooseTypes,PrintPerf" cargo +enzyme build +``` + +Using `-Zautodiff=Enable` will allow using autodiff and update your normal rustc compilation pipeline: + +1. Run your selected compilation pipeline. If you selected a release build, we will disable vectorization and loop unrolling. +2. Differentiate your functions. +3. Run your selected compilation pipeline again on the whole module. This time we do not disable vectorization or loop unrolling. diff --git a/src/doc/rustc-dev-guide/src/autodiff/installation.md b/src/doc/rustc-dev-guide/src/autodiff/installation.md new file mode 100644 index 0000000000000..c9b28dc43a6d1 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/autodiff/installation.md @@ -0,0 +1,88 @@ +# Installation + +In the near future, `std::autodiff` should become available in nightly builds for users. As a contributor however, you will still need to build rustc from source. Please be aware that the msvc target is not supported at the moment, all other tier 1 targets should work. Please open an issue if you encounter any problems on a supported tier 1 target, or if you successfully build this project on a tier2/tier3 target. + +## Build instructions + +First you need to clone and configure the Rust repository: +```bash +git clone --depth=1 git@github.com:rust-lang/rust.git +cd rust +./configure --enable-llvm-link-shared --enable-llvm-plugins --enable-llvm-enzyme --release-channel=nightly --enable-llvm-assertions --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs +``` + +Afterwards you can build rustc using: +```bash +./x.py build --stage 1 library +``` + +Afterwards rustc toolchain link will allow you to use it through cargo: +``` +rustup toolchain link enzyme build/host/stage1 +rustup toolchain install nightly # enables -Z unstable-options +``` + +You can then run our test cases: + +```bash +./x.py test --stage 1 tests/codegen/autodiff +./x.py test --stage 1 tests/pretty/autodiff +./x.py test --stage 1 tests/ui/autodiff +./x.py test --stage 1 tests/ui/feature-gates/feature-gate-autodiff.rs +``` + +Autodiff is still experimental, so if you want to use it in your own projects, you will need to add `lto="fat"` to your Cargo.toml +and use `RUSTFLAGS="-Zautodiff=Enable" cargo +enzyme` instead of `cargo` or `cargo +nightly`. + +## Compiler Explorer and dist builds + +Our compiler explorer instance can be updated to a newer rustc in a similar way. First, prepare a docker instance. +```bash +docker run -it ubuntu:22.04 +export CC=clang CXX=clang++ +apt update +apt install wget vim python3 git curl libssl-dev pkg-config lld ninja-build cmake clang build-essential +``` +Then build rustc in a slightly altered way: +```bash +git clone --depth=1 https://github.com/rust-lang/rust.git +cd rust +./configure --enable-llvm-link-shared --enable-llvm-plugins --enable-llvm-enzyme --release-channel=nightly --enable-llvm-assertions --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs +./x dist +``` +We then copy the tarball to our host. The dockerid is the newest entry under `docker ps -a`. +```bash +docker cp :/rust/build/dist/rust-nightly-x86_64-unknown-linux-gnu.tar.gz rust-nightly-x86_64-unknown-linux-gnu.tar.gz +``` +Afterwards we can create a new (pre-release) tag on the EnzymeAD/rust repository and make a PR against the EnzymeAD/enzyme-explorer repository to update the tag. +Remember to ping `tgymnich` on the PR to run his update script. Note: We should archive EnzymeAD/rust and update the instructions here. The explorer should soon +be able to get the rustc toolchain from the official rust servers. + + +## Build instruction for Enzyme itself + +Following the Rust build instruction above will build LLVMEnzyme, LLDEnzyme, and ClangEnzyme along with the Rust compiler. +We recommend that approach, if you just want to use any of them and have no experience with cmake. +However, if you prefer to just build Enzyme without Rust, then these instructions might help. + +```bash +git clone --depth=1 git@github.com:llvm/llvm-project.git +cd llvm-project +mkdir build +cd build +cmake -G Ninja ../llvm -DLLVM_TARGETS_TO_BUILD="host" -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_ENABLE_RUNTIMES="openmp" -DLLVM_ENABLE_PLUGINS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=. +ninja +ninja install +``` +This gives you a working LLVM build, now we can continue with building Enzyme. +Leave the `llvm-project` folder, and execute the following commands: +```bash +git clone git@github.com:EnzymeAD/Enzyme.git +cd Enzyme/enzyme +mkdir build +cd build +cmake .. -G Ninja -DLLVM_DIR=/llvm-project/build/lib/cmake/llvm/ -DLLVM_EXTERNAL_LIT=/llvm-project/llvm/utils/lit/lit.py -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=YES -DBUILD_SHARED_LIBS=ON +ninja +``` +This will build Enzyme, and you can find it in `Enzyme/enzyme/build/lib/Enzyme.so`. (Endings might differ based on your OS). + diff --git a/src/doc/rustc-dev-guide/src/autodiff/internals.md b/src/doc/rustc-dev-guide/src/autodiff/internals.md new file mode 100644 index 0000000000000..0093ef044c80b --- /dev/null +++ b/src/doc/rustc-dev-guide/src/autodiff/internals.md @@ -0,0 +1,27 @@ +The `std::autodiff` module in Rust allows differentiable programming: + +```rust +#![feature(autodiff)] +use std::autodiff::autodiff; + +// f(x) = x * x, f'(x) = 2.0 * x +// bar therefore returns (x * x, 2.0 * x) +#[autodiff(bar, Reverse, Active, Active)] +fn foo(x: f32) -> f32 { x * x } + +fn main() { + assert_eq!(bar(3.0, 1.0), (9.0, 6.0)); + assert_eq!(bar(4.0, 1.0), (16.0, 8.0)); +} +``` + +The detailed documentation for the `std::autodiff` module is available at [std::autodiff](https://doc.rust-lang.org/std/autodiff/index.html). + +Differentiable programing is used in various fields like numerical computing, [solid mechanics][ratel], [computational chemistry][molpipx], [fluid dynamics][waterlily] or for Neural Network training via Backpropagation, [ODE solver][diffsol], [differentiable rendering][libigl], [quantum computing][catalyst], and climate simulations. + +[ratel]: https://gitlab.com/micromorph/ratel +[molpipx]: https://arxiv.org/abs/2411.17011v +[waterlily]: https://github.com/WaterLily-jl/WaterLily.jl +[diffsol]: https://github.com/martinjrobins/diffsol +[libigl]: https://github.com/alecjacobson/libigl-enzyme-example?tab=readme-ov-file#run +[catalyst]: https://github.com/PennyLaneAI/catalyst diff --git a/src/doc/rustc-dev-guide/src/autodiff/limitations.md b/src/doc/rustc-dev-guide/src/autodiff/limitations.md new file mode 100644 index 0000000000000..90afbd51f3fd9 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/autodiff/limitations.md @@ -0,0 +1,27 @@ +# Current limitations + +## Safety and Soundness + +Enzyme currently assumes that the user passes shadow arguments (`dx`, `dy`, ...) of appropriate size. Under Reverse Mode, we additionally assume that shadow arguments are mutable. In Reverse Mode we adjust the outermost pointer or reference to be mutable. Therefore `&f32` will receive the shadow type `&mut f32`. However, we do not check length for other types than slices (e.g. enums, Vec). We also do not enforce mutability of inner references, but will warn if we recognize them. We do intend to add additional checks over time. + +## ABI adjustments + +In some cases, a function parameter might get lowered in a way that we currently don't handle correctly, leading to a compile time type mismatch in the `rustc_codegen_llvm` backend. Here are some [examples](https://github.com/EnzymeAD/rust/issues/105). + +## Compile Times + +Enzyme will often achieve excellent runtime performance, but might increase your compile time by a large factor. For Rust, we already have made significant improvements and have a list of further improvements planed - please reach out if you have time to help here. + +### Type Analysis + +Most of the times, Type Analysis (TA) is the reason of large (>5x) compile time increases when using Enzyme. This poster explains why we need to run Type Analysis in the bottom left part: [Poster Link](https://c.wsmoses.com/posters/Enzyme-llvmdev.pdf). + +We intend to increase the number of locations where we pass down Type information based on Rust types, which in turn will reduce the number of locations where Enzyme has to run Type Analysis, which will help compile times. + +### Duplicated Optimizations + +The key reason for Enzyme offering often excellent performance is that Enzyme differentiates already optimized LLVM-IR. However, we also (have to) run LLVM's optimization pipeline after differentiating, to make sure that the code which Enzyme generates is optimized properly. As a result you should have excellent runtime performance (please fill an issue if not), but at a compile time cost for running optimizations twice. + +### Fat-LTO + +The usage of `#[autodiff(...)]` currently requires compiling your project with Fat-LTO. We technically only need LTO if the function being differentiated calls functions in other compilation units. Therefore, other solutions are possible, but this is the most simple one to get started. diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 805291017c274..4f8712dfaf3cc 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -38,7 +38,7 @@ which means that LLVM assertion failures can show up as compiler crashes (not ICEs but "real" crashes) and other sorts of weird behavior. If you are encountering these, it is a good idea to try using a compiler with LLVM assertions enabled - either an "alt" nightly or a compiler you build yourself -by setting `[llvm] assertions=true` in your config.toml - and see whether +by setting `[llvm] assertions=true` in your bootstrap.toml - and see whether anything turns up. The rustc build process builds the LLVM tools into @@ -160,7 +160,7 @@ from `./build//llvm/bin/` with the LLVM IR emitted by rustc. When investigating the implementation of LLVM itself, you should be aware of its [internal debug infrastructure][llvm-debug]. This is provided in LLVM Debug builds, which you enable for rustc -LLVM builds by changing this setting in the config.toml: +LLVM builds by changing this setting in the bootstrap.toml: ``` [llvm] # Indicates whether the LLVM assertions are enabled or not diff --git a/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md b/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md index 556b3fdf8f84f..eeb2af5e6bc8b 100644 --- a/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md +++ b/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md @@ -28,8 +28,8 @@ format is specific to `rustc`, and may change over time. This file contains: [`-C embed-bitcode=no`][embed-bitcode] CLI option to improve compile times and reduce disk space if LTO is not needed. * `rustc` [metadata], in a file named `lib.rmeta`. -* A symbol table, which is generally a list of symbols with offsets to the - object file that contain that symbol. This is pretty standard for archive +* A symbol table, which is essentially a list of symbols with offsets to the + object files that contain that symbol. This is pretty standard for archive files. [archive file]: https://en.wikipedia.org/wiki/Ar_(Unix) @@ -46,12 +46,11 @@ A `dylib` is a platform-specific shared library. It includes the `rustc` ### rmeta -An `rmeta` file is custom binary format that contains the [metadata] for the -crate. This file can be used for fast "checks" of a project by skipping all -code generation (as is done with `cargo check`), collecting enough information -for documentation (as is done with `cargo doc`), or for -[pipelining](#pipelining). This file is created if the -[`--emit=metadata`][emit] CLI option is used. +An `rmeta` file is a custom binary format that contains the [metadata] for the +crate. This file can be used for fast "checks" of a project by skipping all code +generation (as is done with `cargo check`), collecting enough information for +documentation (as is done with `cargo doc`), or for [pipelining](#pipelining). +This file is created if the [`--emit=metadata`][emit] CLI option is used. `rmeta` files do not support linking, since they do not contain compiled object files. @@ -60,8 +59,8 @@ object files. ## Metadata -The metadata contains a wide swath of different elements. This guide will not -go into detail of every field it contains. You are encouraged to browse the +The metadata contains a wide swath of different elements. This guide will not go +into detail about every field it contains. You are encouraged to browse the [`CrateRoot`] definition to get a sense of the different elements it contains. Everything about metadata encoding and decoding is in the [`rustc_metadata`] package. @@ -110,7 +109,7 @@ See [`compute_hir_hash`] for where the hash is actually computed. [SVH]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_data_structures/svh/struct.Svh.html [incremental compilation]: ../queries/incremental-compilation.md -[`compute_hir_hash`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_lowering/struct.LoweringContext.html#method.compute_hir_hash +[`compute_hir_hash`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_lowering/fn.compute_hir_hash.html ### Stable Crate Id @@ -122,9 +121,9 @@ much more. By default, all Rust symbols are mangled and incorporate the stable crate id. This allows multiple versions of the same crate to be included together. Cargo -automatically generates `-C metadata` hashes based on a variety of factors, -like the package version, source, and the target kind (a lib and test can have -the same crate name, so they need to be disambiguated). +automatically generates `-C metadata` hashes based on a variety of factors, like +the package version, source, and target kind (a lib and test can have the same +crate name, so they need to be disambiguated). [`StableCrateId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/def_id/struct.StableCrateId.html [`StableCrateId::new`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/def_id/struct.StableCrateId.html#method.new @@ -154,7 +153,7 @@ will also look at the [sysroot] to find dependencies. As crates are loaded, they are kept in the [`CStore`] with the crate metadata wrapped in the [`CrateMetadata`] struct. After resolution and expansion, the -`CStore` will make its way into the [`GlobalCtxt`] for the rest of +`CStore` will make its way into the [`GlobalCtxt`] for the rest of the compilation. [name resolution]: ../name-resolution.md diff --git a/src/doc/rustc-dev-guide/src/backend/updating-llvm.md b/src/doc/rustc-dev-guide/src/backend/updating-llvm.md index 92d4ce32f9248..18c822aad790e 100644 --- a/src/doc/rustc-dev-guide/src/backend/updating-llvm.md +++ b/src/doc/rustc-dev-guide/src/backend/updating-llvm.md @@ -116,14 +116,14 @@ so let's go through each in detail. at the time of the branch, and the remaining part is the current date. -2. Apply Rust-specific patches to the llvm-project repository. +1. Apply Rust-specific patches to the llvm-project repository. All features and bugfixes are upstream, but there's often some weird build-related patches that don't make sense to upstream. These patches are typically the latest patches in the rust-lang/llvm-project branch that rustc is currently using. -3. Build the new LLVM in the `rust` repository. +1. Build the new LLVM in the `rust` repository. To do this, you'll want to update the `src/llvm-project` repository to your branch, and the revision you've created. @@ -144,14 +144,14 @@ so let's go through each in detail. Note that `profile = "compiler"` and other defaults set by `./x setup` download LLVM from CI instead of building it from source. You should disable this temporarily to make sure your changes are being used. - This is done by having the following setting in `config.toml`: + This is done by having the following setting in `bootstrap.toml`: ```toml [llvm] download-ci-llvm = false ``` -4. Test for regressions across other platforms. LLVM often has at least one bug +1. Test for regressions across other platforms. LLVM often has at least one bug for non-tier-1 architectures, so it's good to do some more testing before sending this to bors! If you're low on resources you can send the PR as-is now to bors, though, and it'll get tested anyway. @@ -170,22 +170,17 @@ so let's go through each in detail. * `./src/ci/docker/run.sh dist-various-2` * `./src/ci/docker/run.sh armhf-gnu` -5. Prepare a PR to `rust-lang/rust`. Work with maintainers of +1. Prepare a PR to `rust-lang/rust`. Work with maintainers of `rust-lang/llvm-project` to get your commit in a branch of that repository, and then you can send a PR to `rust-lang/rust`. You'll change at least `src/llvm-project` and will likely also change [`llvm-wrapper`] as well. - + > For prior art, here are some previous LLVM updates: - > - [LLVM 11](https://github.com/rust-lang/rust/pull/73526) - > - [LLVM 12](https://github.com/rust-lang/rust/pull/81451) - > - [LLVM 13](https://github.com/rust-lang/rust/pull/87570) - > - [LLVM 14](https://github.com/rust-lang/rust/pull/93577) - > - [LLVM 15](https://github.com/rust-lang/rust/pull/99464) - > - [LLVM 16](https://github.com/rust-lang/rust/pull/109474) > - [LLVM 17](https://github.com/rust-lang/rust/pull/115959) > - [LLVM 18](https://github.com/rust-lang/rust/pull/120055) > - [LLVM 19](https://github.com/rust-lang/rust/pull/127513) + > - [LLVM 20](https://github.com/rust-lang/rust/pull/135763) Note that sometimes it's easiest to land [`llvm-wrapper`] compatibility as a PR before actually updating `src/llvm-project`. @@ -194,7 +189,7 @@ so let's go through each in detail. others interested in trying out the new LLVM can benefit from work you've done to update the C++ bindings. -3. Over the next few months, +1. Over the next few months, LLVM will continually push commits to its `release/a.b` branch. We will often want to have those bug fixes as well. The merge process for that is to use `git merge` itself to merge LLVM's @@ -202,9 +197,9 @@ so let's go through each in detail. This is typically done multiple times when necessary while LLVM's release branch is baking. -4. LLVM then announces the release of version `a.b`. +1. LLVM then announces the release of version `a.b`. -5. After LLVM's official release, +1. After LLVM's official release, we follow the process of creating a new branch on the rust-lang/llvm-project repository again, this time with a new date. diff --git a/src/doc/rustc-dev-guide/src/bug-fix-procedure.md b/src/doc/rustc-dev-guide/src/bug-fix-procedure.md index e6a16df6d2a9c..55436261fdefa 100644 --- a/src/doc/rustc-dev-guide/src/bug-fix-procedure.md +++ b/src/doc/rustc-dev-guide/src/bug-fix-procedure.md @@ -1,4 +1,4 @@ -# Procedures for Breaking Changes +# Procedures for breaking changes @@ -80,41 +80,11 @@ approachable and practical; it may make sense to direct users to an RFC or some other issue for the full details. The issue also serves as a place where users can comment with questions or other concerns. -A template for these breaking-change tracking issues can be found below. An -example of how such an issue should look can be [found +A template for these breaking-change tracking issues can be found +[here][template]. An example of how such an issue should look can be [found here][breaking-change-issue]. -The issue should be tagged with (at least) `B-unstable` and `T-compiler`. - -### Tracking issue template - -This is a template to use for tracking issues: - -``` -This is the **summary issue** for the `YOUR_LINT_NAME_HERE` -future-compatibility warning and other related errors. The goal of -this page is describe why this change was made and how you can fix -code that is affected by it. It also provides a place to ask questions -or register a complaint if you feel the change should not be made. For -more information on the policy around future-compatibility warnings, -see our [breaking change policy guidelines][guidelines]. - -[guidelines]: LINK_TO_THIS_RFC - -#### What is the warning for? - -*Describe the conditions that trigger the warning and how they can be -fixed. Also explain why the change was made.** - -#### When will this warning become a hard error? - -At the beginning of each 6-week release cycle, the Rust compiler team -will review the set of outstanding future compatibility warnings and -nominate some of them for **Final Comment Period**. Toward the end of -the cycle, we will review any comments and make a final determination -whether to convert the warning into a hard error or remove it -entirely. -``` +[template]: https://github.com/rust-lang/rust/issues/new?template=tracking_issue_future.md ### Issuing future compatibility warnings diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/bootstrap-in-dependencies.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/bootstrap-in-dependencies.md new file mode 100644 index 0000000000000..68c5c2386bd5e --- /dev/null +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/bootstrap-in-dependencies.md @@ -0,0 +1,53 @@ +# `cfg(bootstrap)` in compiler dependencies + +The rust compiler uses some external crates that can run into cyclic dependencies with the compiler itself: the compiler needs an updated crate to build, but the crate needs an updated compiler. This page describes how `#[cfg(bootstrap)]` can be used to break this cycle. + +## Enabling `#[cfg(bootstrap)]` + +Usually the use of `#[cfg(bootstrap)]` in an external crate causes a warning: + +``` +warning: unexpected `cfg` condition name: `bootstrap` + --> src/main.rs:1:7 + | +1 | #[cfg(bootstrap)] + | ^^^^^^^^^ + | + = help: expected names are: `docsrs`, `feature`, and `test` and 31 more + = help: consider using a Cargo feature instead + = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: + [lints.rust] + unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)'] } + = help: or consider adding `println!("cargo::rustc-check-cfg=cfg(bootstrap)");` to the top of the `build.rs` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default +``` + +This warning can be silenced by adding these lines to the project's `Cargo.toml`: + +```toml +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)'] } +``` + +Now `#[cfg(bootstrap)]` can be used in the crate just like it can be in the compiler: when the bootstrap compiler is used, code annotated with `#[cfg(bootstrap)]` is compiled, otherwise code annotated with `#[cfg(not(bootstrap))]` is compiled. + +## The update dance + +As a concrete example we'll use a change where the `#[naked]` attribute was made into an unsafe attribute, which caused a cyclic dependency with the `compiler-builtins` crate. + +### Step 1: accept the new behavior in the compiler ([#139797](https://github.com/rust-lang/rust/pull/139797)) + +In this example it is possible to accept both the old and new behavior at the same time by disabling an error. + +### Step 2: update the crate ([#821](https://github.com/rust-lang/compiler-builtins/pull/821)) + +Now in the crate, use `#[cfg(bootstrap)]` to use the old behavior, or `#[cfg(not(bootstrap))]` to use the new behavior. + +### Step 3: update the crate version used by the compiler ([#139934](https://github.com/rust-lang/rust/pull/139934)) + +For `compiler-builtins` this meant a version bump, in other cases it may be a git submodule update. + +### Step 4: remove the old behavior from the compiler ([#139753](https://github.com/rust-lang/rust/pull/139753)) + +The updated crate can now be used. In this example that meant that the old behavior could be removed. diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md index 24b9783ddf092..35d33ebdb0e7f 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md @@ -129,7 +129,7 @@ Both `tracing::*` macros and the `tracing::instrument` proc-macro attribute need ```rs #[cfg(feature = "tracing")] -use tracing::{instrument, trace}; +use tracing::instrument; struct Foo; @@ -138,7 +138,6 @@ impl Step for Foo { #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "Foo::should_run", skip_all))] fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - #[cfg(feature = "tracing")] trace!(?run, "entered Foo::should_run"); todo!() @@ -154,7 +153,6 @@ impl Step for Foo { ), )] fn run(self, builder: &Builder<'_>) -> Self::Output { - #[cfg(feature = "tracing")] trace!(?run, "entered Foo::run"); todo!() diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md index f72918c8377fc..7f53097824cc9 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md @@ -6,8 +6,8 @@ of the same compiler. This raises a chicken-and-egg paradox: where did the first compiler come from? It must have been written in a different language. In Rust's case it was -[written in OCaml][ocaml-compiler]. However it was abandoned long ago and the -only way to build a modern version of rustc is a slightly less modern +[written in OCaml][ocaml-compiler]. However, it was abandoned long ago, and the +only way to build a modern version of rustc is with a slightly less modern version. This is exactly how `x.py` works: it downloads the current beta release of diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md index 1dd5402f4cde7..a2930b3e42723 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md @@ -8,8 +8,8 @@ the same compiler. This raises a chicken-and-egg paradox: where did the first compiler come from? It must have been written in a different language. In Rust's case it was -[written in OCaml][ocaml-compiler]. However it was abandoned long ago and the -only way to build a modern version of `rustc` is a slightly less modern version. +[written in OCaml][ocaml-compiler]. However, it was abandoned long ago, and the +only way to build a modern version of `rustc` is with a slightly less modern version. This is exactly how [`./x.py`] works: it downloads the current beta release of `rustc`, then uses it to compile the new compiler. @@ -394,8 +394,8 @@ will be rare to want to use it. Finally, `MAGIC_EXTRA_RUSTFLAGS` bypasses the this is `compiletest`. For unit tests and doc tests this is the `libtest` runner. -Most test runner accept `--help`, which you can use to find out the options -accepted by the runner. +Most test runners accept `--help`, +which you can use to find out the options accepted by the runner. ## Environment Variables diff --git a/src/doc/rustc-dev-guide/src/building/compiler-documenting.md b/src/doc/rustc-dev-guide/src/building/compiler-documenting.md index 948571ce82eeb..b031462ea15fb 100644 --- a/src/doc/rustc-dev-guide/src/building/compiler-documenting.md +++ b/src/doc/rustc-dev-guide/src/building/compiler-documenting.md @@ -36,7 +36,7 @@ like the standard library (std) or the compiler (rustc). - Document internal rustc items Compiler documentation is not built by default. - To create it by default with `x doc`, modify `config.toml`: + To create it by default with `x doc`, modify `bootstrap.toml`: ```toml [build] diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index f9a089c23f265..c3c1c41e3f697 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -63,7 +63,7 @@ cd rust > **NOTE**: A shallow clone limits which `git` commands can be run. > If you intend to work on and contribute to the compiler, it is > generally recommended to fully clone the repository [as shown above](#get-the-source-code), -> or to perform a [partial clone](#shallow-clone-the-repository) instead. +> or to perform a [partial clone](#partial-clone-the-repository) instead. > > For example, `git bisect` and `git blame` require access to the commit history, > so they don't work if the repository was cloned with `--depth 1`. @@ -159,15 +159,15 @@ similar to the one declared in section [What is `x.py`](#what-is-xpy), but it works as an independent process to execute the `x.py` rather than calling the shell to run the platform related scripts. -## Create a `config.toml` +## Create a `bootstrap.toml` To start, run `./x setup` and select the `compiler` defaults. This will do some initialization -and create a `config.toml` for you with reasonable defaults. If you use a different default (which +and create a `bootstrap.toml` for you with reasonable defaults. If you use a different default (which you'll likely want to do if you want to contribute to an area of rust other than the compiler, such as rustdoc), make sure to read information about that default (located in `src/bootstrap/defaults`) as the build process may be different for other defaults. -Alternatively, you can write `config.toml` by hand. See `config.example.toml` for all the available +Alternatively, you can write `bootstrap.toml` by hand. See `bootstrap.example.toml` for all the available settings and explanations of them. See `src/bootstrap/defaults` for common settings to change. If you have already built `rustc` and you change settings related to LLVM, then you may have to @@ -206,7 +206,7 @@ See the chapters on Note that building will require a relatively large amount of storage space. You may want to have upwards of 10 or 15 gigabytes available to build the compiler. -Once you've created a `config.toml`, you are now ready to run +Once you've created a `bootstrap.toml`, you are now ready to run `x`. There are a lot of options here, but let's start with what is probably the best "go to" command for building a local compiler: @@ -326,7 +326,7 @@ involve proc macros or build scripts, you must be sure to explicitly build targe host platform (in this case, `x86_64-unknown-linux-gnu`). If you want to always build for other targets without needing to pass flags to `x build`, -you can configure this in the `[build]` section of your `config.toml` like so: +you can configure this in the `[build]` section of your `bootstrap.toml` like so: ```toml [build] @@ -336,8 +336,8 @@ target = ["x86_64-unknown-linux-gnu", "wasm32-wasip1"] Note that building for some targets requires having external dependencies installed (e.g. building musl targets requires a local copy of musl). Any target-specific configuration (e.g. the path to a local copy of musl) -will need to be provided by your `config.toml`. -Please see `config.example.toml` for information on target-specific configuration keys. +will need to be provided by your `bootstrap.toml`. +Please see `bootstrap.example.toml` for information on target-specific configuration keys. For examples of the complete configuration necessary to build a target, please visit [the rustc book](https://doc.rust-lang.org/rustc/platform-support.html), diff --git a/src/doc/rustc-dev-guide/src/building/new-target.md b/src/doc/rustc-dev-guide/src/building/new-target.md index cd215277e69f6..09ffbe8c8828e 100644 --- a/src/doc/rustc-dev-guide/src/building/new-target.md +++ b/src/doc/rustc-dev-guide/src/building/new-target.md @@ -4,12 +4,11 @@ These are a set of steps to add support for a new target. There are numerous end states and paths to get there, so not all sections may be relevant to your desired goal. -See also the associated documentation in the -[target tier policy][target_tier_policy_add]. +See also the associated documentation in the [target tier policy]. -[target_tier_policy_add]: https://doc.rust-lang.org/rustc/target-tier-policy.html#adding-a-new-target +[target tier policy]: https://doc.rust-lang.org/rustc/target-tier-policy.html#adding-a-new-target ## Specifying a new LLVM @@ -38,7 +37,7 @@ able to configure Rust to treat your build as the system LLVM to avoid redundant builds. You can tell Rust to use a pre-built version of LLVM using the `target` section -of `config.toml`: +of `bootstrap.toml`: ```toml [target.x86_64-unknown-linux-gnu] @@ -56,8 +55,8 @@ for codegen tests. This tool is normally built with LLVM, but if you use your own preinstalled LLVM, you will need to provide `FileCheck` in some other way. On Debian-based systems, you can install the `llvm-N-tools` package (where `N` is the LLVM version number, e.g. `llvm-8-tools`). Alternately, you can specify -the path to `FileCheck` with the `llvm-filecheck` config item in `config.toml` -or you can disable codegen test with the `codegen-tests` item in `config.toml`. +the path to `FileCheck` with the `llvm-filecheck` config item in `bootstrap.toml` +or you can disable codegen test with the `codegen-tests` item in `bootstrap.toml`. ## Creating a target specification @@ -142,14 +141,14 @@ After this, run `cargo update -p libc` to update the lockfiles. Beware that if you patch to a local `path` dependency, this will enable warnings for that dependency. Some dependencies are not warning-free, and due -to the `deny-warnings` setting in `config.toml`, the build may suddenly start +to the `deny-warnings` setting in `bootstrap.toml`, the build may suddenly start to fail. To work around warnings, you may want to: - Modify the dependency to remove the warnings -- Or for local development purposes, suppress the warnings by setting deny-warnings = false in config.toml. +- Or for local development purposes, suppress the warnings by setting deny-warnings = false in bootstrap.toml. ```toml -# config.toml +# bootstrap.toml [rust] deny-warnings = false ``` diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md index f8ca1d0dc3919..62dfaca89d24e 100644 --- a/src/doc/rustc-dev-guide/src/building/optimized-build.md +++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md @@ -13,7 +13,7 @@ This page describes how you can use these approaches when building `rustc` yours Link-time optimization is a powerful compiler technique that can increase program performance. To enable (Thin-)LTO when building `rustc`, set the `rust.lto` config option to `"thin"` -in `config.toml`: +in `bootstrap.toml`: ```toml [rust] @@ -34,7 +34,7 @@ Enabling LTO on Linux has [produced] speed-ups by up to 10%. Using a different memory allocator for `rustc` can provide significant performance benefits. If you want to enable the `jemalloc` allocator, you can set the `rust.jemalloc` option to `true` -in `config.toml`: +in `bootstrap.toml`: ```toml [rust] @@ -46,7 +46,7 @@ jemalloc = true ## Codegen units Reducing the amount of codegen units per `rustc` crate can produce a faster build of the compiler. -You can modify the number of codegen units for `rustc` and `libstd` in `config.toml` with the +You can modify the number of codegen units for `rustc` and `libstd` in `bootstrap.toml` with the following options: ```toml @@ -67,7 +67,7 @@ RUSTFLAGS="-C target_cpu=x86-64-v3" ./x build ... ``` If you also want to compile LLVM for a specific instruction set, you can set `llvm` flags -in `config.toml`: +in `bootstrap.toml`: ```toml [llvm] @@ -109,11 +109,16 @@ like Python or LLVM. Here is an example of how can `opt-dist` be used locally (outside of CI): -1. Build the tool with the following command: +1. Enable metrics in your `bootstrap.toml` file, because `opt-dist` expects it to be enabled: + ```toml + [build] + metrics = true + ``` +2. Build the tool with the following command: ```bash ./x build tools/opt-dist ``` -2. Run the tool with the `local` mode and provide necessary parameters: +3. Run the tool with the `local` mode and provide necessary parameters: ```bash ./build/host/stage0-tools-bin/opt-dist local \ --target-triple \ # select target, e.g. "x86_64-unknown-linux-gnu" diff --git a/src/doc/rustc-dev-guide/src/building/prerequisites.md b/src/doc/rustc-dev-guide/src/building/prerequisites.md index f49f6bb052789..6761cabac1f14 100644 --- a/src/doc/rustc-dev-guide/src/building/prerequisites.md +++ b/src/doc/rustc-dev-guide/src/building/prerequisites.md @@ -38,4 +38,4 @@ incremental compilation ([see here][config]). This will make compilation take longer (especially after a rebase), but will save a ton of space from the incremental caches. -[config]: ./how-to-build-and-run.md#create-a-configtoml +[config]: ./how-to-build-and-run.md#create-a-bootstraptoml diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index 2498838144e70..f8a28b7f2e9a9 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -1,4 +1,4 @@ -# Suggested Workflows +# Suggested workflows The full bootstrapping process takes quite a while. Here are some suggestions to make your life easier. @@ -20,6 +20,43 @@ your `.git/hooks` folder as `pre-push` (without the `.sh` extension!). You can also install the hook as a step of running `./x setup`! +## Config extensions + +When working on different tasks, you might need to switch between different bootstrap configurations. +Sometimes you may want to keep an old configuration for future use. But saving raw config values in +random files and manually copying and pasting them can quickly become messy, especially if you have a +long history of different configurations. + +To simplify managing multiple configurations, you can create config extensions. + +For example, you can create a simple config file named `cross.toml`: + +```toml +[build] +build = "x86_64-unknown-linux-gnu" +host = ["i686-unknown-linux-gnu"] +target = ["i686-unknown-linux-gnu"] + + +[llvm] +download-ci-llvm = false + +[target.x86_64-unknown-linux-gnu] +llvm-config = "/path/to/llvm-19/bin/llvm-config" +``` + +Then, include this in your `bootstrap.toml`: + +```toml +include = ["cross.toml"] +``` + +You can also include extensions within extensions recursively. + +**Note:** In the `include` field, the overriding logic follows a right-to-left order. For example, +in `include = ["a.toml", "b.toml"]`, extension `b.toml` overrides `a.toml`. Also, parent extensions +always overrides the inner ones. + ## Configuring `rust-analyzer` for `rustc` ### Project-local rust-analyzer setup @@ -123,6 +160,30 @@ Another way is without a plugin, and creating your own logic in your configuration. The following code will work for any checkout of rust-lang/rust (newer than Febuary 2025): ```lua +local function expand_config_variables(option) + local var_placeholders = { + ['${workspaceFolder}'] = function(_) + return vim.lsp.buf.list_workspace_folders()[1] + end, + } + + if type(option) == "table" then + local mt = getmetatable(option) + local result = {} + for k, v in pairs(option) do + result[expand_config_variables(k)] = expand_config_variables(v) + end + return setmetatable(result, mt) + end + if type(option) ~= "string" then + return option + end + local ret = option + for key, fn in pairs(var_placeholders) do + ret = ret:gsub(key, fn) + end + return ret +end lspconfig.rust_analyzer.setup { root_dir = function() local default = lspconfig.rust_analyzer.config_def.default_config.root_dir() @@ -142,7 +203,7 @@ lspconfig.rust_analyzer.setup { -- load rust-lang/rust settings local file = io.open(config) local json = vim.json.decode(file:read("*a")) - client.config.settings["rust-analyzer"] = json.lsp["rust-analyzer"].initialization_options + client.config.settings["rust-analyzer"] = expand_config_variables(json.lsp["rust-analyzer"].initialization_options) client.notify("workspace/didChangeConfiguration", { settings = client.config.settings }) end return true @@ -305,7 +366,7 @@ subsequent rebuilds: ``` If you don't want to include the flag with every command, you can enable it in -the `config.toml`: +the `bootstrap.toml`: ```toml [rust] @@ -384,20 +445,20 @@ ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc # Use nix-shell ### Note Note that when using nix on a not-NixOS distribution, it may be necessary to set -**`patch-binaries-for-nix = true` in `config.toml`**. Bootstrap tries to detect +**`patch-binaries-for-nix = true` in `bootstrap.toml`**. Bootstrap tries to detect whether it's running in nix and enable patching automatically, but this detection can have false negatives. -You can also use your nix shell to manage `config.toml`: +You can also use your nix shell to manage `bootstrap.toml`: ```nix let config = pkgs.writeText "rustc-config" '' - # Your config.toml content goes here + # Your bootstrap.toml content goes here '' pkgs.mkShell { /* ... */ - # This environment variable tells bootstrap where our config.toml is. + # This environment variable tells bootstrap where our bootstrap.toml is. RUST_BOOTSTRAP_CONFIG = config; } ``` diff --git a/src/doc/rustc-dev-guide/src/cli.md b/src/doc/rustc-dev-guide/src/cli.md index 408ae20700430..4c77007ea4415 100644 --- a/src/doc/rustc-dev-guide/src/cli.md +++ b/src/doc/rustc-dev-guide/src/cli.md @@ -28,6 +28,6 @@ adding a new command-line argument. unstable-options` flag. [cli-docs]: https://doc.rust-lang.org/rustc/command-line-arguments.html -[forge guide for new options]: https://forge.rust-lang.org/compiler/new_option.html +[forge guide for new options]: https://forge.rust-lang.org/compiler/proposals-and-stabilization.html#compiler-flags [unstable book]: https://doc.rust-lang.org/nightly/unstable-book/ [`parse_bool`]: https://github.com/rust-lang/rust/blob/e5335592e78354e33d798d20c04bcd677c1df62d/src/librustc_session/options.rs#L307-L313 diff --git a/src/doc/rustc-dev-guide/src/coherence.md b/src/doc/rustc-dev-guide/src/coherence.md index b3af101fb876d..73f9213bf4056 100644 --- a/src/doc/rustc-dev-guide/src/coherence.md +++ b/src/doc/rustc-dev-guide/src/coherence.md @@ -1,4 +1,3 @@ - # Coherence > NOTE: this is based on [notes by @lcnr](https://github.com/rust-lang/rust/pull/121848) diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index c16b3ee7abdb7..102e20207792e 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -11,13 +11,13 @@ chapter](./backend/debugging.md)). ## Configuring the compiler By default, rustc is built without most debug information. To enable debug info, -set `debug = true` in your config.toml. +set `debug = true` in your bootstrap.toml. Setting `debug = true` turns on many different debug options (e.g., `debug-assertions`, `debug-logging`, etc.) which can be individually tweaked if you want to, but many people simply set `debug = true`. -If you want to use GDB to debug rustc, please set `config.toml` with options: +If you want to use GDB to debug rustc, please set `bootstrap.toml` with options: ```toml [rust] @@ -35,14 +35,14 @@ debuginfo-level = 2 The default configuration will enable `symbol-mangling-version` v0. This requires at least GDB v10.2, -otherwise you need to disable new symbol-mangling-version in `config.toml`. +otherwise you need to disable new symbol-mangling-version in `bootstrap.toml`. ```toml [rust] new-symbol-mangling = false ``` -> See the comments in `config.example.toml` for more info. +> See the comments in `bootstrap.example.toml` for more info. You will need to rebuild the compiler after changing any configuration option. @@ -301,7 +301,8 @@ Right below you can find elaborate explainers on a selected few. Some compiler options for debugging specific features yield graphviz graphs - e.g. the `#[rustc_mir(borrowck_graphviz_postflow="suffix.dot")]` attribute -dumps various borrow-checker dataflow graphs. +on a function dumps various borrow-checker dataflow graphs in conjunction with +`-Zdump-mir-dataflow`. These all produce `.dot` files. To view these files, install graphviz (e.g. `apt-get install graphviz`) and then run the following commands: @@ -373,7 +374,7 @@ error: aborting due to previous error ## Configuring CodeLLDB for debugging `rustc` -If you are using VSCode, and have edited your `config.toml` to request debugging +If you are using VSCode, and have edited your `bootstrap.toml` to request debugging level 1 or 2 for the parts of the code you're interested in, then you should be able to use the [CodeLLDB] extension in VSCode to debug it. diff --git a/src/doc/rustc-dev-guide/src/compiler-src.md b/src/doc/rustc-dev-guide/src/compiler-src.md index c538fc8b788d7..00aa96226849d 100644 --- a/src/doc/rustc-dev-guide/src/compiler-src.md +++ b/src/doc/rustc-dev-guide/src/compiler-src.md @@ -62,21 +62,20 @@ huge. There is also the `rustc` crate which is the actual binary (i.e. the [`rustc_driver`] crate, which drives the various parts of compilation in other crates. -The dependency structure of these crates is complex, but roughly it is +The dependency order of these crates is complex, but roughly it is something like this: -- `rustc` (the binary) calls [`rustc_driver::main`][main]. - - [`rustc_driver`] depends on a lot of other crates, but the main one is - [`rustc_interface`]. - - [`rustc_interface`] depends on most of the other compiler crates. It - is a fairly generic interface for driving the whole compilation. - - Most of the other `rustc_*` crates depend on [`rustc_middle`], - which defines a lot of central data structures in the compiler. - - [`rustc_middle`] and most of the other crates depend on a - handful of crates representing the early parts of the - compiler (e.g. the parser), fundamental data structures (e.g. - [`Span`]), or error reporting: [`rustc_data_structures`], - [`rustc_span`], [`rustc_errors`], etc. +1. `rustc` (the binary) calls [`rustc_driver::main`][main]. +1. [`rustc_driver`] depends on a lot of other crates, but the main one is + [`rustc_interface`]. +1. [`rustc_interface`] depends on most of the other compiler crates. It is a + fairly generic interface for driving the whole compilation. +1. Most of the other `rustc_*` crates depend on [`rustc_middle`], which defines + a lot of central data structures in the compiler. +1. [`rustc_middle`] and most of the other crates depend on a handful of crates + representing the early parts of the compiler (e.g. the parser), fundamental + data structures (e.g. [`Span`]), or error reporting: + [`rustc_data_structures`], [`rustc_span`], [`rustc_errors`], etc. [`rustc_data_structures`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_data_structures/index.html [`rustc_driver`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/index.html @@ -87,8 +86,12 @@ something like this: [`Span`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html [main]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/fn.main.html -You can see the exact dependencies by reading the [`Cargo.toml`] for the various -crates, just like a normal Rust crate. +You can see the exact dependencies by running `cargo tree`, +just like you would for any other Rust package: + +```console +cargo tree --package rustc_driver +``` One final thing: [`src/llvm-project`] is a submodule for our fork of LLVM. During bootstrapping, LLVM is built and the [`compiler/rustc_llvm`] crate diff --git a/src/doc/rustc-dev-guide/src/const-eval.md b/src/doc/rustc-dev-guide/src/const-eval.md index 69329a3e08511..ca6a35a5e97eb 100644 --- a/src/doc/rustc-dev-guide/src/const-eval.md +++ b/src/doc/rustc-dev-guide/src/const-eval.md @@ -35,7 +35,7 @@ They're the wrappers of the `const_eval` query. Statics are special; all other functions do not represent statics correctly and have thus assertions preventing their use on statics. -The `const_eval_*` functions use a [`ParamEnv`](./param_env/param_env_summary.html) of environment +The `const_eval_*` functions use a [`ParamEnv`](./typing_parameter_envs.html) of environment in which the constant is evaluated (e.g. the function within which the constant is used) and a [`GlobalId`]. The `GlobalId` is made up of an `Instance` referring to a constant or static or of an `Instance` of a function and an index into the function's `Promoted` table. diff --git a/src/doc/rustc-dev-guide/src/contributing.md b/src/doc/rustc-dev-guide/src/contributing.md index 9817326f07ba9..0575de642eeb3 100644 --- a/src/doc/rustc-dev-guide/src/contributing.md +++ b/src/doc/rustc-dev-guide/src/contributing.md @@ -1,4 +1,4 @@ -# Contribution Procedures +# Contribution procedures @@ -81,7 +81,7 @@ smaller user-facing changes. into a PR that ends up not getting merged!** [See this document][mcpinfo] for more info on MCPs. -[mcpinfo]: https://forge.rust-lang.org/compiler/mcp.html +[mcpinfo]: https://forge.rust-lang.org/compiler/proposals-and-stabilization.html#how-do-i-submit-an-mcp [zulip]: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler ### Performance @@ -150,6 +150,20 @@ when contributing to Rust under [the git section](./git.md). [t-compiler]: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler [triagebot]: https://github.com/rust-lang/rust/blob/master/triagebot.toml +### Keeping your branch up-to-date + +The CI in rust-lang/rust applies your patches directly against the current master, +not against the commit your branch is based on. This can lead to unexpected failures +if your branch is outdated, even when there are no explicit merge conflicts. + +Before submitting or updating a PR, make sure to update your branch +as mentioned [here](git.md#keeping-things-up-to-date) if it's significantly +behind the master branch (e.g., more than 100 commits behind). +This fetches the latest master branch and rebases your changes on top of it, +ensuring your PR is tested against the latest code. + +After rebasing, it's recommended to [run the relevant tests locally](tests/intro.md) to catch any issues before CI runs. + ### r? All pull requests are reviewed by another person. We have a bot, @@ -346,7 +360,7 @@ function in the same way as other pull requests. [`src/doc`]: https://github.com/rust-lang/rust/tree/master/src/doc [std-root]: https://github.com/rust-lang/rust/blob/master/library/std/src/lib.rs#L1 -To find documentation-related issues, sort by the [A-docs label]. +To find documentation-related issues, use the [A-docs label]. You can find documentation style guidelines in [RFC 1574]. @@ -373,7 +387,7 @@ Just a few things to keep in mind: There is no strict limit on line lengths; let the sentence or part of the sentence flow to its proper end on the same line. - When contributing text to the guide, please contextualize the information with some time period - and/or a reason so that the reader knows how much to trust or mistrust the information. + and/or a reason so that the reader knows how much to trust the information. Aim to provide a reasonable amount of context, possibly including but not limited to: - A reason for why the data may be out of date other than "change", @@ -387,28 +401,28 @@ Just a few things to keep in mind: - jan 2021 - january 2021 - There is a CI action (in `~/.github/workflows/date-check.yml`) - that generates a monthly showing those that are over 6 months old + There is a CI action (in `.github/workflows/date-check.yml`) + that generates a monthly report showing those that are over 6 months old ([example](https://github.com/rust-lang/rustc-dev-guide/issues/2052)). For the action to pick the date, add a special annotation before specifying the date: ```md - Sep 2024 + Apr 2025 ``` Example: ```md - As of Sep 2024, the foo did the bar. + As of Apr 2025, the foo did the bar. ``` For cases where the date should not be part of the visible rendered output, use the following instead: ```md - + ``` - A link to a relevant WG, tracking issue, `rustc` rustdoc page, or similar, that may provide diff --git a/src/doc/rustc-dev-guide/src/conventions.md b/src/doc/rustc-dev-guide/src/conventions.md index 0e624a4566d2a..4356cf246f89e 100644 --- a/src/doc/rustc-dev-guide/src/conventions.md +++ b/src/doc/rustc-dev-guide/src/conventions.md @@ -1,3 +1,5 @@ +# Coding conventions + This file offers some tips on the coding conventions for rustc. This chapter covers [formatting](#formatting), [coding for correctness](#cc), [using crates from crates.io](#cio), and some tips on @@ -5,7 +7,7 @@ chapter covers [formatting](#formatting), [coding for correctness](#cc),
    -# Formatting and the tidy script +## Formatting and the tidy script rustc is moving towards the [Rust standard coding style][fmt]. @@ -20,44 +22,42 @@ Formatting is checked by the `tidy` script. It runs automatically when you do `./x test` and can be run in isolation with `./x fmt --check`. If you want to use format-on-save in your editor, the pinned version of -`rustfmt` is built under `build//stage0/bin/rustfmt`. You'll have to -pass the `--edition=2021` argument yourself when calling -`rustfmt` directly. +`rustfmt` is built under `build//stage0/bin/rustfmt`. [fmt]: https://github.com/rust-dev-tools/fmt-rfcs - [`rustfmt`]:https://github.com/rust-lang/rustfmt -## Formatting C++ code +### Formatting C++ code The compiler contains some C++ code for interfacing with parts of LLVM that don't have a stable C API. When modifying that code, use this command to format it: -```sh -./x test tidy --extra-checks=cpp:fmt --bless +```console +./x test tidy --extra-checks cpp:fmt --bless ``` This uses a pinned version of `clang-format`, to avoid relying on the local environment. -## Formatting and linting Python code +### Formatting and linting Python code The Rust repository contains quite a lot of Python code. We try to keep -it both linted and formatted by the [ruff][ruff] tool. +it both linted and formatted by the [ruff] tool. When modifying Python code, use this command to format it: -```sh -./x test tidy --extra-checks=py:fmt --bless + +```console +./x test tidy --extra-checks py:fmt --bless ``` -and the following command to run lints: -```sh -./x test tidy --extra-checks=py:lint +And, the following command to run lints: + +```console +./x test tidy --extra-checks py:lint ``` -This uses a pinned version of `ruff`, to avoid relying on the local -environment. +These use a pinned version of `ruff`, to avoid relying on the local environment. [ruff]: https://github.com/astral-sh/ruff @@ -65,7 +65,7 @@ environment. -## Copyright notice +### Copyright notice In the past, files began with a copyright and license notice. Please **omit** @@ -75,41 +75,42 @@ MIT/Apache-2.0). All of the copyright notices should be gone by now, but if you come across one in the rust-lang/rust repo, feel free to open a PR to remove it. -## Line length +### Line length Lines should be at most 100 characters. It's even better if you can keep things to 80. -**Ignoring the line length limit.** Sometimes – in particular for -tests – it can be necessary to exempt yourself from this limit. In -that case, you can add a comment towards the top of the file like so: +Sometimes, and particularly for tests, it can be necessary to exempt yourself from this limit. +In that case, you can add a comment towards the top of the file like so: ```rust // ignore-tidy-linelength ``` -## Tabs vs spaces +### Tabs vs spaces -Prefer 4-space indent. +Prefer 4-space indents. -# Coding for correctness +## Coding for correctness Beyond formatting, there are a few other tips that are worth following. -## Prefer exhaustive matches +### Prefer exhaustive matches Using `_` in a match is convenient, but it means that when new variants are added to the enum, they may not get handled correctly. Ask yourself: if a new variant were added to this enum, what's the chance that it would want to use the `_` code, versus having some other treatment? Unless the answer is "low", then prefer an -exhaustive match. (The same advice applies to `if let` and `while -let`, which are effectively tests for a single variant.) +exhaustive match. + +The same advice applies to `if let` and `while let`, +which are effectively tests for a single variant. -## Use "TODO" comments for things you don't want to forget +### Use "TODO" comments for things you don't want to forget As a useful tool to yourself, you can insert a `// TODO` comment for something that you want to get back to before you land your PR: @@ -136,13 +137,13 @@ if foo { -# Using crates from crates.io +## Using crates from crates.io See the [crates.io dependencies][crates] section. -# How to structure your PR +## How to structure your PR How you prepare the commits in your PR can make a big difference for the reviewer. Here are some tips. @@ -172,7 +173,7 @@ require that every intermediate commit successfully builds – we only expect to be able to bisect at a PR level. However, if you *can* make individual commits build, that is always helpful. -# Naming conventions +## Naming conventions Apart from normal Rust style/naming conventions, there are also some specific to the compiler. diff --git a/src/doc/rustc-dev-guide/src/crates-io.md b/src/doc/rustc-dev-guide/src/crates-io.md index 403d61a81dada..4431585a2f02b 100644 --- a/src/doc/rustc-dev-guide/src/crates-io.md +++ b/src/doc/rustc-dev-guide/src/crates-io.md @@ -1,4 +1,4 @@ -# crates.io Dependencies +# crates.io dependencies The Rust compiler supports building with some dependencies from `crates.io`. Examples are `log` and `env_logger`. diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index 972309b5cd343..2f8f4b0ab8a0c 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -1,4 +1,4 @@ -# Errors and Lints +# Errors and lints @@ -772,7 +772,7 @@ store.register_renamed("single_use_lifetime", "single_use_lifetimes"); [`store.register_removed`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_removed [`rustc_lint::register_builtins`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_builtins.html -### Lint Groups +### Lint groups Lints can be turned on in groups. These groups are declared in the [`register_builtins`][rbuiltins] function in [`rustc_lint::lib`][builtin]. The @@ -954,9 +954,6 @@ application of these fields based on a variety of attributes when using `Self="std::iter::Iterator"`. This is needed because `Self` is a keyword which cannot appear in attributes. - `direct`: user-specified rather than derived obligation. - - `from_method`: usable both as boolean (whether the flag is present, like - `crate_local`) or matching against a particular method. Currently used - for `try`. - `from_desugaring`: usable both as boolean (whether the flag is present) or matching against a particular desugaring. The desugaring is identified with its variant name in the `DesugaringKind` enum. diff --git a/src/doc/rustc-dev-guide/src/effects.md b/src/doc/rustc-dev-guide/src/effects.md index 1fda7bcbb1380..c7aa271466868 100644 --- a/src/doc/rustc-dev-guide/src/effects.md +++ b/src/doc/rustc-dev-guide/src/effects.md @@ -1,66 +1,159 @@ -# Effects and effect checking - -Note: all of this describes the implementation of the unstable `effects` and -`const_trait_impl` features. None of this implementation is usable or visible from -stable Rust. - -The implementation of const traits and `~const` bounds is a limited effect system. -It is used to allow trait bounds on `const fn` to be used within the `const fn` for -method calls. Within the function, in order to know whether a method on a trait -bound is `const`, we need to know whether there is a `~const` bound for the trait. -In order to know whether we can instantiate a `~const` bound on a `const fn`, we -need to know whether there is a `const_trait` impl for the type and trait being -used (or whether the `const fn` is used at runtime, then any type implementing the -trait is ok, just like with other bounds). - -We perform these checks via a const generic boolean that gets attached to all -`const fn` and `const trait`. The following sections will explain the desugarings -and the way we perform the checks at call sites. - -The const generic boolean is inverted to the meaning of `const`. In the compiler -it is called `host`, because it enables "host APIs" like `static` items, network -access, disk access, random numbers and everything else that isn't available in -`const` contexts. So `false` means "const", `true` means "not const" and if it's -a generic parameter, it means "maybe const" (meaning we're in a const fn or const -trait). - -## `const fn` - -All `const fn` have a `#[rustc_host] const host: bool` generic parameter that is -hidden from users. Any `~const Trait` bounds in the generics list or `where` bounds -of a `const fn` get converted to `Trait + Trait` bounds. The `Trait` -exists so that associated types of the generic param can be used from projections -like `::Assoc`, because there are no `` projections for now. - -## `#[const_trait] trait`s - -The `#[const_trait]` attribute gives the marked trait a `#[rustc_host] const host: bool` -generic parameter. All functions of the trait "inherit" this generic parameter, just like -they have all the regular generic parameters of the trait. Any `~const Trait` super-trait -bounds get desugared to `Trait + Trait` in order to allow using associated -types and consts of the super traits in the trait declaration. This is necessary, because -`::Assoc` is always `>::Assoc` as there is -no `` syntax. - -## `typeck` performing method and function call checks. - -When generic parameters are instantiated for any items, the `host` generic parameter -is always instantiated as an inference variable. This is a special kind of inference var -that is not part of the type or const inference variables, similar to how we have -special inference variables for type variables that we know to be an integer, but not -yet which one. These separate inference variables fall back to `true` at -the end of typeck (in `fallback_effects`) to ensure that `let _ = some_fn_item_name;` -will keep compiling. - -All actually used (in function calls, casts, or anywhere else) function items, will -have the `enforce_context_effects` method invoked. -It trivially returns if the function being called has no `host` generic parameter. - -In order to error if a non-const function is called in a const context, we have not -yet disabled the const-check logic that happens on MIR, because -`enforce_context_effects` does not yet perform this check. - -The function call's `host` parameter is then equated to the context's `host` value, -which almost always trivially succeeds, as it was an inference var. If the inference -var has already been bound (since the function item is invoked twice), the second -invocation checks it against the first. +# Effects and const condition checking + +## The `HostEffect` predicate + +[`HostEffectPredicate`]s are a kind of predicate from `~const Tr` or `const Tr` +bounds. It has a trait reference, and a `constness` which could be `Maybe` or +`Const` depending on the bound. Because `~const Tr`, or rather `Maybe` bounds +apply differently based on whichever contexts they are in, they have different +behavior than normal bounds. Where normal trait bounds on a function such as +`T: Tr` are collected within the [`predicates_of`] query to be proven when a +function is called and to be assumed within the function, bounds such as +`T: ~const Tr` will behave as a normal trait bound and add `T: Tr` to the result +from `predicates_of`, but also adds a `HostEffectPredicate` to the +[`const_conditions`] query. + +On the other hand, `T: const Tr` bounds do not change meaning across contexts, +therefore they will result in `HostEffect(T: Tr, const)` being added to +`predicates_of`, and not `const_conditions`. + +[`HostEffectPredicate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/predicate/struct.HostEffectPredicate.html +[`predicates_of`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.predicates_of +[`const_conditions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.const_conditions + +## The `const_conditions` query + +`predicates_of` represents a set of predicates that need to be proven to use an +item. For example, to use `foo` in the example below: + +```rust +fn foo() where T: Default {} +``` + +We must be able to prove that `T` implements `Default`. In a similar vein, +`const_conditions` represents a set of predicates that need to be proven to use +an item *in const contexts*. If we adjust the example above to use `const` trait +bounds: + +```rust +const fn foo() where T: ~const Default {} +``` + +Then `foo` would get a `HostEffect(T: Default, maybe)` in the `const_conditions` +query, suggesting that in order to call `foo` from const contexts, one must +prove that `T` has a const implementation of `Default`. + +## Enforcement of `const_conditions` + +`const_conditions` are currently checked in various places. + +Every call in HIR from a const context (which includes `const fn` and `const` +items) will check that `const_conditions` of the function we are calling hold. +This is done in [`FnCtxt::enforce_context_effects`]. Note that we don't check +if the function is only referred to but not called, as the following code needs +to compile: + +```rust +const fn hi() -> T { + T::default() +} +const X: fn() -> u32 = hi::; +``` + +For a trait `impl` to be well-formed, we must be able to prove the +`const_conditions` of the trait from the `impl`'s environment. This is checked +in [`wfcheck::check_impl`]. + +Here's an example: + +```rust +#[const_trait] +trait Bar {} +#[const_trait] +trait Foo: ~const Bar {} +// `const_conditions` contains `HostEffect(Self: Bar, maybe)` + +impl const Bar for () {} +impl const Foo for () {} +// ^ here we check `const_conditions` for the impl to be well-formed +``` + +Methods of trait impls must not have stricter bounds than the method of the +trait that they are implementing. To check that the methods are compatible, a +hybrid environment is constructed with the predicates of the `impl` plus the +predicates of the trait method, and we attempt to prove the predicates of the +impl method. We do the same for `const_conditions`: + +```rust +#[const_trait] +trait Foo { + fn hi(); +} + +impl Foo for Vec { + fn hi(); + // ^ we can't prove `T: ~const PartialEq` given `T: ~const Clone` and + // `T: ~const Default`, therefore we know that the method on the impl + // is stricter than the method on the trait. +} +``` + +These checks are done in [`compare_method_predicate_entailment`]. A similar +function that does the same check for associated types is called +[`compare_type_predicate_entailment`]. Both of these need to consider +`const_conditions` when in const contexts. + +In MIR, as part of const checking, `const_conditions` of items that are called +are revalidated again in [`Checker::revalidate_conditional_constness`]. + +[`compare_method_predicate_entailment`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html +[`compare_type_predicate_entailment`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_type_predicate_entailment.html +[`FnCtxt::enforce_context_effects`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#method.enforce_context_effects +[`wfcheck::check_impl`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/wfcheck/fn.check_impl.html +[`Checker::revalidate_conditional_constness`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_const_eval/check_consts/check/struct.Checker.html#method.revalidate_conditional_constness + +## `explicit_implied_const_bounds` on associated types and traits + +Bounds on associated types, opaque types, and supertraits such as +```rust +trait Foo: ~const PartialEq { + type X: ~const PartialEq; +} + +fn foo() -> impl ~const PartialEq { + // ^ unimplemented syntax +} +``` + +Have their bounds represented differently. Unlike `const_conditions` which need +to be proved for callers, and can be assumed inside the definition (e.g. trait +bounds on functions), these bounds need to be proved at definition (at the impl, +or when returning the opaque) but can be assumed for callers. The non-const +equivalent of these bounds are called [`explicit_item_bounds`]. + +These bounds are checked in [`compare_impl_item::check_type_bounds`] for HIR +typeck, [`evaluate_host_effect_from_item_bounds`] in the old solver and +[`consider_additional_alias_assumptions`] in the new solver. + +[`explicit_item_bounds`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.explicit_item_bounds +[`compare_impl_item::check_type_bounds`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.check_type_bounds.html +[`evaluate_host_effect_from_item_bounds`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/effects/fn.evaluate_host_effect_from_item_bounds.html +[`consider_additional_alias_assumptions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_next_trait_solver/solve/assembly/trait.GoalKind.html#tymethod.consider_additional_alias_assumptions + +## Proving `HostEffectPredicate`s + +`HostEffectPredicate`s are implemented both in the [old solver] and the [new +trait solver]. In general, we can prove a `HostEffect` predicate when either of +these conditions are met: + +* The predicate can be assumed from caller bounds; +* The type has a `const` `impl` for the trait, *and* that const conditions on +the impl holds, *and* that the `explicit_implied_const_bounds` on the trait +holds; or +* The type has a built-in implementation for the trait in const contexts. For +example, `Fn` may be implemented by function items if their const conditions +are satisfied, or `Destruct` is implemented in const contexts if the type can +be dropped at compile time. + +[old solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_trait_selection/traits/effects.rs.html +[new trait solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_next_trait_solver/solve/effect_goals.rs.html diff --git a/src/doc/rustc-dev-guide/src/feature-gates.md b/src/doc/rustc-dev-guide/src/feature-gates.md index 24ce9bb71bfdf..9806f73c483c2 100644 --- a/src/doc/rustc-dev-guide/src/feature-gates.md +++ b/src/doc/rustc-dev-guide/src/feature-gates.md @@ -1,4 +1,4 @@ -# Feature Gates +# Feature gates This chapter is intended to provide basic help for adding, removing, and modifying feature gates. diff --git a/src/doc/rustc-dev-guide/src/fuzzing.md b/src/doc/rustc-dev-guide/src/fuzzing.md index 869fc2f71cc04..b588ca104cb8c 100644 --- a/src/doc/rustc-dev-guide/src/fuzzing.md +++ b/src/doc/rustc-dev-guide/src/fuzzing.md @@ -123,7 +123,7 @@ what actually results in superior throughput. You may want to build rustc from source with debug assertions to find additional bugs, though this is a trade-off: it can slow down fuzzing by requiring extra work for every execution. To enable debug assertions, add this -to `config.toml` when compiling rustc: +to `bootstrap.toml` when compiling rustc: ```toml [rust] diff --git a/src/doc/rustc-dev-guide/src/getting-started.md b/src/doc/rustc-dev-guide/src/getting-started.md index 8bf14bef2a033..0e5b32a06f898 100644 --- a/src/doc/rustc-dev-guide/src/getting-started.md +++ b/src/doc/rustc-dev-guide/src/getting-started.md @@ -89,7 +89,7 @@ filtering the search to areas you're interested in. For example: Not all important or beginner work has issue labels. See below for how to find work that isn't labelled. -[help-wanted-search]: https://github.com/issues?q=is%3Aopen+is%3Aissue+org%3Arust-lang+no%3Aassignee+label%3AE-easy%2C%22good+first+issue%22%2Cgood-first-issue%2CE-medium%2CEasy%2CE-help-wanted%2CE-mentor+-label%3AS-blocked+ +[help-wanted-search]: https://github.com/issues?q=is%3Aopen+is%3Aissue+org%3Arust-lang+no%3Aassignee+label%3AE-easy%2C%22good+first+issue%22%2Cgood-first-issue%2CE-medium%2CEasy%2CE-help-wanted%2CE-mentor+-label%3AS-blocked+-linked:pr+ [Triage]: ./contributing.md#issue-triage ### Recurring work diff --git a/src/doc/rustc-dev-guide/src/git.md b/src/doc/rustc-dev-guide/src/git.md index 177495b536212..8118ddff10c92 100644 --- a/src/doc/rustc-dev-guide/src/git.md +++ b/src/doc/rustc-dev-guide/src/git.md @@ -38,13 +38,13 @@ If you've cloned your fork, then you will be able to reference it with `origin` in your local repo. It may be helpful to also set up a remote for the official rust-lang/rust repo via -```sh +```console git remote add upstream https://github.com/rust-lang/rust.git ``` if you're using HTTPS, or -```sh +```console git remote add upstream git@github.com:rust-lang/rust.git ``` @@ -112,7 +112,7 @@ See [Rebasing](#rebasing) for more about rebasing. This is not a problem from git's perspective. If you run `git remote -v`, it will say something like this: -``` +```console $ git remote -v origin git@github.com:jyn514/rust.git (fetch) origin git@github.com:jyn514/rust.git (push) @@ -158,11 +158,11 @@ To fix it, do the following things: ### I see "error: cannot rebase" when I try to rebase These are two common errors to see when rebasing: -``` +```console error: cannot rebase: Your index contains uncommitted changes. error: Please commit or stash them. ``` -``` +```console error: cannot rebase: You have unstaged changes. error: Please commit or stash them. ``` @@ -174,7 +174,7 @@ commit your changes, or make a temporary commit called a "stash" to have them st when you finish rebasing. You may want to configure git to make this "stash" automatically, which will prevent the "cannot rebase" error in nearly all cases: -``` +```console git config --global rebase.autostash true ``` @@ -205,7 +205,7 @@ git reset --hard master `git push` will not work properly and say something like this: -``` +```console ! [rejected] issue-xxxxx -> issue-xxxxx (non-fast-forward) error: failed to push some refs to 'https://github.com/username/rust.git' hint: Updates were rejected because the tip of your current branch is behind @@ -226,7 +226,7 @@ didn't write, it likely means you're trying to rebase over the wrong branch. For have a `rust-lang/rust` remote `upstream`, but ran `git rebase origin/master` instead of `git rebase upstream/master`. The fix is to abort the rebase and use the correct branch instead: -``` +```console git rebase --abort git rebase -i upstream/master ``` @@ -243,7 +243,7 @@ When updating your local repository with `git pull`, you may notice that sometim Git says you have modified some files that you have never edited. For example, running `git status` gives you something like (note the `new commits` mention): -``` +```console On branch master Your branch is up to date with 'origin/master'. @@ -256,9 +256,12 @@ Changes not staged for commit: no changes added to commit (use "git add" and/or "git commit -a") ``` -These changes are not changes to files: they are changes to submodules (more on this -[later](#git-submodules)). To get rid of those, run `./x --help`, which will automatically update -the submodules. +These changes are not changes to files: they are changes to submodules (more on this [later](#git-submodules)). +To get rid of those: + +```console +git submodule update +``` Some submodules are not actually needed; for example, `src/llvm-project` doesn't need to be checked out if you're using `download-ci-llvm`. To avoid having to keep fetching its history, you can use @@ -278,12 +281,12 @@ merged. To do that, you need to rebase your work on top of rust-lang/rust. To rebase your feature branch on top of the newest version of the master branch of rust-lang/rust, checkout your branch, and then run this command: -``` +```console git pull --rebase https://github.com/rust-lang/rust.git master ``` > If you are met with the following error: -> ``` +> ```console > error: cannot pull with rebase: Your index contains uncommitted changes. > error: please commit or stash them. > ``` @@ -300,13 +303,13 @@ reapply the changes fails because your changes conflicted with other changes that have been made. You can tell that this happened because you'll see lines in the output that look like -``` +```console CONFLICT (content): Merge conflict in file.rs ``` When you open these files, you'll see sections of the form -``` +```console <<<<<<< HEAD Original code ======= @@ -346,7 +349,7 @@ will keep it up-to-date. You will also want to rebase your feature branches up-to-date as well. After pulling, you can checkout the feature branches and rebase them: -``` +```console git checkout master git pull upstream master --ff-only # to make certain there are no merge commits git rebase master feature_branch @@ -384,7 +387,7 @@ change the order in which they are applied, or "squash" them into each other. Alternatively, you can sacrifice the commit history like this: -``` +```console # squash all the changes into one commit so you only have to worry about conflicts once git rebase -i --keep-base master # and squash all changes along the way git rebase master @@ -422,7 +425,7 @@ it shows you the differences between your old diff and your new diff. Here's an example of `git range-diff` output (taken from [Git's docs][range-diff-example-docs]): -``` +```console -: ------- > 1: 0ddba11 Prepare for the inevitable! 1: c0debee = 2: cab005e Add a helpful message at the start 2: f00dbal ! 3: decafe1 Describe a bug @@ -499,7 +502,7 @@ Git and Github's default diff view for large moves *within* a file is quite poor line as deleted and each line as added, forcing you to compare each line yourself. Git has an option to show moved lines in a different color: -``` +```console git log -p --color-moved=dimmed-zebra --color-moved-ws=allow-indentation-change ``` @@ -515,7 +518,7 @@ that was force-pushed to make sure there are no unexpected changes. Many large files in the repo are autogenerated. To view a diff that ignores changes to those files, you can use the following syntax (e.g. Cargo.lock): -``` +```console git log -p ':!Cargo.lock' ``` @@ -545,7 +548,7 @@ The contents of submodules are ignored by Git: submodules are in some sense isol from the rest of the repository. However, if you try to `cd src/llvm-project` and then run `git status`: -``` +```console HEAD detached at 9567f08afc943 nothing to commit, working tree clean ``` @@ -576,7 +579,7 @@ that Git can nicely and fairly conveniently handle for us. Sometimes you might run into (when you run `git status`) -``` +```console Changes not staged for commit: (use "git add ..." to update what will be committed) (use "git restore ..." to discard changes in working directory) @@ -586,7 +589,7 @@ Changes not staged for commit: and when you try to run `git submodule update` it breaks horribly with errors like -``` +```console error: RPC failed; curl 92 HTTP/2 stream 7 was not closed cleanly: CANCEL (err 8) error: 2782 bytes of body are still expected fetch-pack: unexpected disconnect while reading sideband packet @@ -597,8 +600,8 @@ fatal: Fetched in submodule path 'src/llvm-project', but it did not contain 5a51 If you see `(new commits, modified content)` you can run -```bash -$ git submodule foreach git reset --hard +```console +git submodule foreach git reset --hard ``` and then try `git submodule update` again. @@ -607,7 +610,7 @@ and then try `git submodule update` again. If that doesn't work, you can try to deinit all git submodules... -``` +```console git submodule deinit -f --all ``` @@ -618,7 +621,7 @@ completely messed up for some reason. Sometimes, for some forsaken reason, you might run into -```text +```console fatal: not a git repository: src/gcc/../../.git/modules/src/gcc ``` diff --git a/src/doc/rustc-dev-guide/src/guides/editions.md b/src/doc/rustc-dev-guide/src/guides/editions.md index ea207167791b7..9a92d4ebcb510 100644 --- a/src/doc/rustc-dev-guide/src/guides/editions.md +++ b/src/doc/rustc-dev-guide/src/guides/editions.md @@ -193,6 +193,23 @@ When a user runs `cargo fix --edition`, cargo will pass the `--force-warn rust-2 flag to force all of these lints to appear during the edition migration. Cargo also passes `--cap-lints=allow` so that no other lints interfere with the edition migration. +Make sure that the example code sets the correct edition. The example should illustrate the previous edition, and show what the migration warning would look like. For example, this lint for a 2024 migration shows an example in 2021: + +```rust,ignore +declare_lint! { + /// The `keyword_idents_2024` lint detects ... + /// + /// ### Example + /// + /// ```rust,edition2021 + /// #![warn(keyword_idents_2024)] + /// fn gen() {} + /// ``` + /// + /// {{produces}} +} +``` + Migration lints can be either `Allow` or `Warn` by default. If it is `Allow`, users usually won't see this warning unless they are doing an edition migration manually or there is a problem during the migration. @@ -334,3 +351,40 @@ In general it is recommended to avoid these special cases except for very high v [into-iter]: https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html [panic-macro]: https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html [`non_fmt_panics`]: https://doc.rust-lang.org/nightly/rustc/lints/listing/warn-by-default.html#non-fmt-panics + +### Migrating the standard library edition + +Updating the edition of the standard library itself roughly involves the following process: + +- Wait until the newly stabilized edition has reached beta and the bootstrap compiler has been updated. +- Apply migration lints. This can be an involved process since some code is in external submodules[^std-submodules], and the standard library makes heavy use of conditional compilation. Also, running `cargo fix --edition` can be impractical on the standard library itself. One approach is to individually add `#![warn(...)]` at the top of each crate for each lint, run `./x check library`, apply the migrations, remove the `#![warn(...)]` and commit each migration separately. You'll likely need to run `./x check` with `--target` for many different targets to get full coverage (otherwise you'll likely spend days or weeks getting CI to pass)[^ed-docker]. See also the [advanced migration guide] for more tips. + - Apply migrations to [`backtrace-rs`]. [Example for 2024](https://github.com/rust-lang/backtrace-rs/pull/700). Note that this doesn't update the edition of the crate itself because that is published independently on crates.io, and that would otherwise restrict the minimum Rust version. Consider adding some `#![deny()]` attributes to avoid regressions until its edition gets updated. + - Apply migrations to [`stdarch`], and update its edition, and formatting. [Example for 2024](https://github.com/rust-lang/stdarch/pull/1710). + - Post PRs to update the backtrace and stdarch submodules, and wait for those to land. + - Apply migration lints to the standard library crates, and update their edition. I recommend working one crate at a time starting with `core`. [Example for 2024](https://github.com/rust-lang/rust/pull/138162). + +[^std-submodules]: This will hopefully change in the future to pull these submodules into `rust-lang/rust`. +[^ed-docker]: You'll also likely need to do a lot of testing for different targets, and this is where [docker testing](../tests/docker.md) comes in handy. + +[advanced migration guide]: https://doc.rust-lang.org/nightly/edition-guide/editions/advanced-migrations.html +[`backtrace-rs`]: https://github.com/rust-lang/backtrace-rs/ +[`stdarch`]: https://github.com/rust-lang/stdarch/ + +## Stabilizing an edition + +After the edition team has given the go-ahead, the process for stabilizing an edition is roughly: + +- Update [`LATEST_STABLE_EDITION`]. +- Update [`Edition::is_stable`]. +- Hunt and find any document that refers to edition by number, and update it: + - [`--edition` flag](https://github.com/rust-lang/rust/blob/master/src/doc/rustc/src/command-line-arguments.md#--edition-specify-the-edition-to-use) + - [Rustdoc attributes](https://github.com/rust-lang/rust/blob/master/src/doc/rustdoc/src/write-documentation/documentation-tests.md#attributes) +- Clean up any tests that use the `//@ edition` header to remove the `-Zunstable-options` flag to ensure they are indeed stable. Note: Ideally this should be automated, see [#133582]. +- Bless any tests that change. +- Update `lint-docs` to default to the new edition. + +See [example for 2024](https://github.com/rust-lang/rust/pull/133349). + +[`LATEST_STABLE_EDITION`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/edition/constant.LATEST_STABLE_EDITION.html +[`Edition::is_stable`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/edition/enum.Edition.html#method.is_stable +[#133582]: https://github.com/rust-lang/rust/issues/133582 diff --git a/src/doc/rustc-dev-guide/src/hir.md b/src/doc/rustc-dev-guide/src/hir.md index 51893d537d750..0c1c9941572dd 100644 --- a/src/doc/rustc-dev-guide/src/hir.md +++ b/src/doc/rustc-dev-guide/src/hir.md @@ -100,7 +100,7 @@ The HIR uses a bunch of different identifiers that coexist and serve different p a wrapper around a [`HirId`]. For more info about HIR bodies, please refer to the [HIR chapter][hir-bodies]. -These identifiers can be converted into one another through the [HIR map][map]. +These identifiers can be converted into one another through the `TyCtxt`. [`DefId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html [`LocalDefId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.LocalDefId.html @@ -110,47 +110,42 @@ These identifiers can be converted into one another through the [HIR map][map]. [`CrateNum`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.CrateNum.html [`DefIndex`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefIndex.html [`Body`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.Body.html -[hir-map]: ./hir.md#the-hir-map [hir-bodies]: ./hir.md#hir-bodies -[map]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html -## The HIR Map +## HIR Operations Most of the time when you are working with the HIR, you will do so via -the **HIR Map**, accessible in the tcx via [`tcx.hir()`] (and defined in -the [`hir::map`] module). The [HIR map] contains a [number of methods] to -convert between IDs of various kinds and to lookup data associated -with a HIR node. +`TyCtxt`. It contains a number of methods, defined in the `hir::map` module and +mostly prefixed with `hir_`, to convert between IDs of various kinds and to +lookup data associated with a HIR node. -[`tcx.hir()`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.hir -[`hir::map`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/index.html -[HIR map]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html -[number of methods]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#methods +[`TyCtxt`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html For example, if you have a [`LocalDefId`], and you would like to convert it -to a [`HirId`], you can use [`tcx.hir().local_def_id_to_hir_id(def_id)`][local_def_id_to_hir_id]. +to a [`HirId`], you can use [`tcx.local_def_id_to_hir_id(def_id)`][local_def_id_to_hir_id]. You need a `LocalDefId`, rather than a `DefId`, since only local items have HIR nodes. -[local_def_id_to_hir_id]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.local_def_id_to_hir_id +[local_def_id_to_hir_id]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.local_def_id_to_hir_id -Similarly, you can use [`tcx.hir().find(n)`][find] to lookup the node for a +Similarly, you can use [`tcx.hir_node(n)`][hir_node] to lookup the node for a [`HirId`]. This returns a `Option>`, where [`Node`] is an enum defined in the map. By matching on this, you can find out what sort of node the `HirId` referred to and also get a pointer to the data itself. Often, you know what sort of node `n` is – e.g. if you know that `n` must be some HIR expression, you can do -[`tcx.hir().expect_expr(n)`][expect_expr], which will extract and return the +[`tcx.hir_expect_expr(n)`][expect_expr], which will extract and return the [`&hir::Expr`][Expr], panicking if `n` is not in fact an expression. -[find]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.find +[hir_node]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.hir_node [`Node`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.Node.html -[expect_expr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.expect_expr +[expect_expr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.expect_expr [Expr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.Expr.html -Finally, you can use the HIR map to find the parents of nodes, via -calls like [`tcx.hir().get_parent(n)`][get_parent]. +Finally, you can find the parents of nodes, via +calls like [`tcx.parent_hir_node(n)`][parent_hir_node]. + +[parent_hir_node]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.parent_hir_node -[get_parent]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.get_parent ## HIR Bodies @@ -158,10 +153,10 @@ A [`rustc_hir::Body`] represents some kind of executable code, such as the body of a function/closure or the definition of a constant. Bodies are associated with an **owner**, which is typically some kind of item (e.g. an `fn()` or `const`), but could also be a closure expression -(e.g. `|x, y| x + y`). You can use the HIR map to find the body -associated with a given def-id ([`maybe_body_owned_by`]) or to find -the owner of a body ([`body_owner_def_id`]). +(e.g. `|x, y| x + y`). You can use the `TyCtxt` to find the body +associated with a given def-id ([`hir_maybe_body_owned_by`]) or to find +the owner of a body ([`hir_body_owner_def_id`]). [`rustc_hir::Body`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.Body.html -[`maybe_body_owned_by`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.maybe_body_owned_by -[`body_owner_def_id`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.body_owner_def_id +[`hir_maybe_body_owned_by`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.hir_maybe_body_owned_by +[`hir_body_owner_def_id`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.hir_body_owner_def_id diff --git a/src/doc/rustc-dev-guide/src/implementing_new_features.md b/src/doc/rustc-dev-guide/src/implementing_new_features.md index fda38ef4fc01a..d7561bbbad22a 100644 --- a/src/doc/rustc-dev-guide/src/implementing_new_features.md +++ b/src/doc/rustc-dev-guide/src/implementing_new_features.md @@ -44,7 +44,7 @@ like this; for example, the compiler team recommends filing a Major Change Proposal ([MCP][mcp]) as a lightweight way to garner support and feedback without requiring full consensus. -[mcp]: https://forge.rust-lang.org/compiler/mcp.html#public-facing-changes-require-rfcbot-fcp +[mcp]: https://forge.rust-lang.org/compiler/proposals-and-stabilization.html#how-do-i-submit-an-mcp You don't need to have the implementation fully ready for r+ to propose an FCP, but it is generally a good idea to have at least a proof diff --git a/src/doc/rustc-dev-guide/src/incrcomp-debugging.md b/src/doc/rustc-dev-guide/src/incrcomp-debugging.md index 7045d3fa39d32..a548215cf0cee 100644 --- a/src/doc/rustc-dev-guide/src/incrcomp-debugging.md +++ b/src/doc/rustc-dev-guide/src/incrcomp-debugging.md @@ -1,4 +1,4 @@ -# Debugging and Testing Dependencies +# Debugging and testing dependencies ## Testing the dependency graph diff --git a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md index 3078ae094c34d..6bc21b6deeb82 100644 --- a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md +++ b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md @@ -34,7 +34,7 @@ Detailed instructions and examples are documented in the [coverage map]: https://llvm.org/docs/CoverageMappingFormat.html [rustc-book-instrument-coverage]: https://doc.rust-lang.org/nightly/rustc/instrument-coverage.html -## Recommended `config.toml` settings +## Recommended `bootstrap.toml` settings When working on the coverage instrumentation code, it is usually necessary to **enable the profiler runtime** by setting `profiler = true` in `[build]`. @@ -83,7 +83,7 @@ statically links coverage-instrumented binaries with LLVM runtime code In the `rustc` source tree, `library/profiler_builtins` bundles the LLVM `compiler-rt` code into a Rust library crate. Note that when building `rustc`, -`profiler_builtins` is only included when `build.profiler = true` is set in `config.toml`. +`profiler_builtins` is only included when `build.profiler = true` is set in `bootstrap.toml`. When compiling with `-C instrument-coverage`, [`CrateLoader::postprocess()`][crate-loader-postprocess] dynamically loads @@ -115,7 +115,7 @@ human-readable coverage report. > Tests in `coverage-run` mode have an implicit `//@ needs-profiler-runtime` > directive, so they will be skipped if the profiler runtime has not been -> [enabled in `config.toml`](#recommended-configtoml-settings). +> [enabled in `bootstrap.toml`](#recommended-configtoml-settings). Finally, the [`tests/codegen/instrument-coverage/testprog.rs`] test compiles a simple Rust program with `-C instrument-coverage` and compares the compiled program's LLVM IR to diff --git a/src/doc/rustc-dev-guide/src/macro-expansion.md b/src/doc/rustc-dev-guide/src/macro-expansion.md index ebab56ad20af0..a90f717004f0b 100644 --- a/src/doc/rustc-dev-guide/src/macro-expansion.md +++ b/src/doc/rustc-dev-guide/src/macro-expansion.md @@ -2,9 +2,6 @@ -> N.B. [`rustc_ast`], [`rustc_expand`], and [`rustc_builtin_macros`] are all -> undergoing refactoring, so some of the links in this chapter may be broken. - Rust has a very powerful macro system. In the previous chapter, we saw how the parser sets aside macros to be expanded (using temporary [placeholders]). This chapter is about the process of expanding those macros iteratively until @@ -12,9 +9,6 @@ we have a complete [*Abstract Syntax Tree* (AST)][ast] for our crate with no unexpanded macros (or a compile error). [ast]: ./ast-validation.md -[`rustc_ast`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/index.html -[`rustc_expand`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/index.html -[`rustc_builtin_macros`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_builtin_macros/index.html [placeholders]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/placeholders/index.html First, we discuss the algorithm that expands and integrates macro output into diff --git a/src/doc/rustc-dev-guide/src/memory.md b/src/doc/rustc-dev-guide/src/memory.md index 1e030ff45a789..eeb4a813980a6 100644 --- a/src/doc/rustc-dev-guide/src/memory.md +++ b/src/doc/rustc-dev-guide/src/memory.md @@ -1,4 +1,4 @@ -# Memory Management in Rustc +# Memory management in rustc Generally rustc tries to be pretty careful how it manages memory. The compiler allocates _a lot_ of data structures throughout compilation, diff --git a/src/doc/rustc-dev-guide/src/mir/dataflow.md b/src/doc/rustc-dev-guide/src/mir/dataflow.md index f31da5ca22ee3..85e57dd839b81 100644 --- a/src/doc/rustc-dev-guide/src/mir/dataflow.md +++ b/src/doc/rustc-dev-guide/src/mir/dataflow.md @@ -148,8 +148,7 @@ whereas this code uses [`ResultsCursor`]: ```rust,ignore let mut results = MyAnalysis::new() - .into_engine(tcx, body, def_id) - .iterate_to_fixpoint() + .iterate_to_fixpoint(tcx, body, None); .into_results_cursor(body); // Inspect the fixpoint state immediately before each `Drop` terminator. diff --git a/src/doc/rustc-dev-guide/src/name-resolution.md b/src/doc/rustc-dev-guide/src/name-resolution.md index 2727b8142f2a8..719ebce855366 100644 --- a/src/doc/rustc-dev-guide/src/name-resolution.md +++ b/src/doc/rustc-dev-guide/src/name-resolution.md @@ -120,9 +120,9 @@ even though they should be visible by ordinary scoping rules. An example: fn do_something(val: T) { // <- New rib in both types and values (1) // `val` is accessible, as is the helper function // `T` is accessible - let helper = || { // New rib on `helper` (2) and another on the block (3) + let helper = || { // New rib on the block (2) // `val` is accessible here - }; // End of (3) + }; // End of (2), new rib on `helper` (3) // `val` is accessible, `helper` variable shadows `helper` function fn helper() { // <- New rib in both types and values (4) // `val` is not accessible here, (4) is not transparent for locals @@ -130,7 +130,7 @@ fn do_something(val: T) { // <- New rib in both types and values (1) } // End of (4) let val = T::default(); // New rib (5) // `val` is the variable, not the parameter here -} // End of (5), (2) and (1) +} // End of (5), (3) and (1) ``` Because the rules for different namespaces are a bit different, each namespace diff --git a/src/doc/rustc-dev-guide/src/normalization.md b/src/doc/rustc-dev-guide/src/normalization.md new file mode 100644 index 0000000000000..ef530ccc5ed95 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/normalization.md @@ -0,0 +1,309 @@ +# Aliases and Normalization + + + +## Aliases + +In Rust there are a number of types that are considered equal to some "underlying" type, for example inherent associated types, trait associated types, free type aliases (`type Foo = u32`), and opaque types (`-> impl RPIT`). We consider such types to be "aliases", alias types are represented by the [`TyKind::Alias`][tykind_alias] variant, with the kind of alias tracked by the [`AliasTyKind`][aliaskind] enum. + +Normalization is the process of taking these alias types and replacing them with the underlying type that they are equal to. For example given some type alias `type Foo = u32`, normalizing `Foo` would give `u32`. + +The concept of an alias is not unique to *types* and the concept also applies to constants/const generics. However, right now in the compiler we don't really treat const aliases as a "first class concept" so this chapter mostly discusses things in the context of types (even though the concepts transfer just fine). + +[tykind_alias]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/enum.TyKind.html#variant.Alias +[aliaskind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/enum.AliasTyKind.html + +### Rigid, Ambiguous and Unnormalized Aliases + +Aliases can either be "rigid", "ambiguous", or simply unnormalized. + +We consider types to be rigid if their "shape" isn't going to change, for example `Box` is rigid as no amount of normalization can turn a `Box` into a `u32`, whereas ` as Iterator>::Item` is not rigid as it can be normalized to `u32`. + +Aliases are rigid when we will never be able to normalize them further. A concrete example of a *rigid* alias would be `::Item` in an environment where there is no `T: Iterator` bound, only a `T: Iterator` bound: +```rust +fn foo() { + // This alias is *rigid* + let _: ::Item; +} + +fn bar>() { + // This alias is *not* rigid as it can be normalized to `u32` + let _: ::Item; +} +``` + +When an alias can't yet be normalized but may wind up normalizable in the [current environment](./typing_parameter_envs.md), we consider it to be an "ambiguous" alias. This can occur when an alias contains inference variables which prevent being able to determine how the trait is implemented: +```rust +fn foo() { + // This alias is considered to be "ambiguous" + let _: <_ as Iterator>::Item; +} +``` + +The reason we call them "ambiguous" aliases is because its *ambiguous* whether this is a rigid alias or not. + +The source of the `_: Iterator` trait impl is *ambiguous* (i.e. unknown), it could be some `impl Iterator for u32` or it could be some `T: Iterator` trait bound, we don't know yet. Depending on why `_: Iterator` holds the alias could be an unnormalized alias or it could be a rigid alias; it's *ambiguous* what kind of alias this is. + +Finally, an alias can just be unnormalized, ` as IntoIterator>::Iter` is an unnormalized alias as it can already be normalized to `std::vec::IntoIter`, it just hasn't been done yet. + +--- + +It is worth noting that Free and Inherent aliases cannot be rigid or ambiguous as naming them also implies having resolved the definition of the alias, which specifies the underlying type of the alias. + +### Diverging Aliases + +An alias is considered to "diverge" if its definition does not specify an underlying non-alias type to normalize to. A concrete example of diverging aliases: +```rust +type Diverges = Diverges; + +trait Trait { + type DivergingAssoc; +} +impl Trait for () { + type DivergingAssoc = <() as Trait>::DivergingAssoc; +} +``` +In this example both `Diverges` and `DivergingAssoc` are "trivial" cases of diverging type aliases where they have been defined as being equal to themselves. There is no underlying type that `Diverges` can ever be normalized to. + +We generally try to error when diverging aliases are defined, but this is entirely a "best effort" check. In the previous example the definitions are "simple enough" to be detected and so errors are emitted. However, in more complex cases, or cases where only some instantiations of generic parameters would result in a diverging alias, we don't emit an error: +```rust +trait Trait { + type DivergingAssoc; +} +impl Trait for T { + // This alias always diverges but we don't emit an error because + // the compiler can't "see" that. + type DivergingAssoc = ::DivergingAssoc; +} +``` + +Ultimately this means that we have no guarantee that aliases in the type system are non-diverging. As aliases may only diverge for some specific generic arguments, it also means that we only know whether an alias diverges once it is fully concrete. This means that codegen/const-evaluation also has to handle diverging aliases: +```rust +trait Trait { + type Diverges; +} +impl Trait for T { + type Diverges = ::Diverges; +} + +fn foo() { + let a: T::Diverges; +} + +fn main() { + foo::<()>(); +} +``` +In this example we only encounter an error from the diverging alias during codegen of `foo::<()>`, if the call to `foo` is removed then no compilation error will be emitted. + +### Opaque Types + +Opaque types are a relatively special kind of alias, and are covered in their own chapter: [Opaque types](./opaque-types-type-alias-impl-trait.md). + +### Const Aliases + +Unlike type aliases, const aliases are not represented directly in the type system, instead const aliases are always an anonymous body containing a path expression to a const item. This means that the only "const alias" in the type system is an anonymous unevaluated const body. + +As such there is no `ConstKind::Alias(AliasCtKind::Projection/Inherent/Free, _)`, instead we only have `ConstKind::Unevaluated` which is used for representing anonymous constants. + +```rust +fn foo() {} + +const FREE_CONST: usize = 1 + 1; + +fn bar() { + foo::<{ FREE_CONST }>(); + // The const arg is represented with some anonymous constant: + // ```pseudo-rust + // const ANON: usize = FREE_CONST; + // foo::(); + // ``` +} +``` + +This is likely to change as const generics functionality is improved, for example `feature(associated_const_equality)` and `feature(min_generic_const_args)` both require handling const aliases similarly to types (without an anonymous constant wrapping all const args). + +## What is Normalization + +### Structural vs Deep normalization + +There are two forms of normalization, structural (sometimes called *shallow*) and deep. Structural normalization should be thought of as only normalizing the "outermost" part of a type. On the other hand deep normalization will normalize *all* aliases in a type. + +In practice structural normalization can result in more than just the outer layer of the type being normalized, but this behaviour should not be relied upon. Unnormalizable non-rigid aliases making use of bound variables (`for<'a>`) cannot be normalized by either kind of normalization. + +As an example: conceptually, structurally normalizing the type `Vec<::Assoc>` would be a no-op, whereas deeply normalizing would give `Vec`. In practice even structural normalization would give `Vec`, though, again, this should not be relied upon. + +Changing the alias to use bound variables will result in different behaviour; `Vec fn(<&'a u8 as Identity>::Assoc)>` would result in no change when structurally normalized, but would result in `Vec fn(&'a u8)>` when deeply normalized. + +### Core normalization logic + +Structurally normalizing aliases is a little bit more nuanced than replacing the alias with whatever it is defined as being equal to in its definition; the result of normalizing an alias should either be a rigid type or an inference variable (which will later be inferred to a rigid type). To accomplish this we do two things: + +First, when normalizing an ambiguous alias it is normalized to an inference variable instead of leaving it as-is, this has two main effects: +- Even though an inference variable is not a rigid type, it will always wind up inferred *to* a rigid type so we ensure that the result of normalization will not need to be normalized again +- Inference variables are used in all cases where a type is non-rigid, allowing the rest of the compiler to not have to deal with *both* ambiguous aliases *and* inference variables + +Secondly, instead of having normalization directly return the type specified in the definition of the alias, we normalize the type first before returning it[^1]. We do this so that normalization is idempotent/callers do not need to run it in a loop. + +```rust +#![feature(lazy_type_alias)] + +type Foo = Bar; +type Bar = ::Item; + +fn foo() { + let a_: Foo<_>; +} +``` + +In this example: +- Normalizing `Foo` would result in `Bar`, except we want to normalize aliases in the type `Foo` is defined as equal to +- Normalizing `Bar` would result in `::Item`, except, again, we want to normalize aliases in the type `Bar` is defined as equal to +- Normalizing `::Item` results in some new inference variable `?y`, as `::Item` is an ambiguous alias +- The final result is that normalizing `Foo` results in `?y` + +## How to normalize + +When interfacing with the type system it will often be the case that it's necessary to request a type be normalized. There are a number of different entry points to the underlying normalization logic and each entry point should only be used in specific parts of the compiler. + +An additional complication is that the compiler is currently undergoing a transition from the old trait solver to the new trait solver. As part of this transition our approach to normalization in the compiler has changed somewhat significantly, resulting in some normalization entry points being "old solver only" slated for removal in the long-term once the new solver has stabilized. + +Here is a rough overview of the different entry points to normalization in the compiler: +- `infcx.at.structurally_normalize` +- `infcx.at.(deeply_)?normalize` +- `infcx.query_normalize` +- `tcx.normalize_erasing_regions` +- `traits::normalize_with_depth(_to)` +- `EvalCtxt::structurally_normalize` + +### Outside of the trait solver + +The [`InferCtxt`][infcx] type exposes the "main" ways to normalize during analysis: [`normalize`][normalize], [`deeply_normalize`][deeply_normalize] and [`structurally_normalize`][structurally_normalize]. These functions are often wrapped and re-exposed on various `InferCtxt` wrapper types, such as [`FnCtxt`][fcx] or [`ObligationCtxt`][ocx] with minor API tweaks to handle some arguments or parts of the return type automatically. + +#### Structural `InferCtxt` normalization + +[`infcx.at.structurally_normalize`][structurally_normalize] exposes structural normalization that is able to handle inference variables and regions. It should generally be used whenever inspecting the kind of a type. + +Inside of HIR Typeck there is a related method of normalization- [`fcx.structurally_resolve`][structurally_resolve], which will error if the type being resolved is an unresolved inference variable. When the new solver is enabled it will also attempt to structurally normalize the type. + +Due to this there is a pattern in HIR typeck where a type is first normalized via `normalize` (only normalizing in the old solver), and then `structurally_resolve`'d (only normalizing in the new solver). This pattern should be preferred over calling `structurally_normalize` during HIR typeck as `structurally_resolve` will attempt to make inference progress by evaluating goals whereas `structurally_normalize` does not. + +#### Deep `InferCtxt` normalization + +##### `infcx.at.(deeply_)?normalize` + +There are two ways to deeply normalize with an `InferCtxt`, `normalize` and `deeply_normalize`. The reason for this is that `normalize` is a "legacy" normalization entry point used only by the old solver, whereas `deeply_normalize` is intended to be the long term way to deeply normalize. Both of these methods can handle regions. + +When the new solver is stabilized the `infcx.at.normalize` function will be removed and everything will have been migrated to the new deep or structural normalization methods. For this reason the `normalize` function is a no-op under the new solver, making it suitable only when the old solver needs normalization but the new solver does not. + +Using `deeply_normalize` will result in errors being emitted when encountering ambiguous aliases[^2] as it is not possible to support normalizing *all* ambiguous aliases to inference variables[^3]. `deeply_normalize` should generally only be used in cases where we do not expect to encounter ambiguous aliases, for example when working with types from item signatures. + +##### `infcx.query_normalize` + +[`infcx.query_normalize`][query_norm] is very rarely used, it has almost all the same restrictions as `normalize_erasing_regions` (cannot handle inference variables, no diagnostics support) with the main difference being that it retains lifetime information. For this reason `normalize_erasing_regions` is the better choice in almost all circumstances as it is more efficient due to caching lifetime-erased queries. + +In practice `query_normalize` is used for normalization in the borrow checker, and elsewhere as a performance optimization over `infcx.normalize`. Once the new solver is stabilized it is expected that `query_normalize` can be removed from the compiler as the new solvers normalization implementation should be performant enough for it to not be a performance regression. + +##### `tcx.normalize_erasing_regions` + +[`normalize_erasing_regions`][norm_erasing_regions] is generally used by parts of the compiler that are not doing type system analysis. This normalization entry point does not handle inference variables, lifetimes, or any diagnostics. Lints and codegen make heavy use of this entry point as they typically are working with fully inferred aliases that can be assumed to be well formed (or at least, are not responsible for erroring on). + +[query_norm]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.query_normalize +[norm_erasing_regions]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.normalize_erasing_regions +[normalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize +[deeply_normalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/normalize/trait.NormalizeExt.html#tymethod.deeply_normalize +[structurally_normalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/trait.StructurallyNormalizeExt.html#tymethod.structurally_normalize_ty +[infcx]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/struct.InferCtxt.html +[fcx]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html +[ocx]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/struct.ObligationCtxt.html +[structurally_resolve]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#method.structurally_resolve_type + +### Inside of the trait solver + +[`traits::normalize_with_depth(_to)`][norm_with_depth] and [`EvalCtxt::structurally_normalize`][eval_ctxt_structural_norm] are only used by the internals of the trait solvers (old and new respectively). It is effectively a raw entry point to the internals of how normalization is implemented by each trait solver. Other normalization entry points cannot be used from within the internals of trait solving as it wouldn't handle goal cycles and recursion depth correctly. + +[norm_with_depth]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/normalize/fn.normalize_with_depth.html +[eval_ctxt_structural_norm]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_next_trait_solver/solve/struct.EvalCtxt.html#method.structurally_normalize_term + +## When/Where to normalize (Old vs New solver) + +One of the big changes between the old and new solver is our approach to when we expect aliases to be normalized. + +### Old solver + +All types are expected to be normalized as soon as possible, so that all types encountered in the type system are either rigid or an inference variable (which will later be inferred to a rigid term). + +As a concrete example: equality of aliases is implemented by assuming they're rigid and recursively equating the generic arguments of the alias. + +### New solver + +It's expected that all types potentially contain ambiguous or unnormalized aliases. Whenever an operation is performed that requires aliases to be normalized, it's the responsibility of that logic to normalize the alias (this means that matching on `ty.kind()` pretty much always has to structurally normalize first). + +As a concrete example: equality of aliases is implemented by a custom goal kind ([`PredicateKind::AliasRelate`][aliasrelate]) so that it can handle normalization of the aliases itself instead of assuming all alias types being equated are rigid. + +Despite this approach we still deeply normalize during [writeback][writeback] for performance/simplicity, so that types in the MIR can still be assumed to have been deeply normalized. + +[aliasrelate]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.PredicateKind.html#variant.AliasRelate +[writeback]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/writeback/index.html + +--- + +There were a few main issues with the old solver's approach to normalization that motivated changing things in the new solver: + +### Missing normalization calls + +It was a frequent occurrence that normalization calls would be missing, resulting in passing unnormalized types to APIs expecting everything to already be normalized. Treating ambiguous or unnormalized aliases as rigid would result in all sorts of weird errors from aliases not being considered equal to one another, or surprising inference guidance from equating unnormalized aliases' generic arguments. + +### Normalizing parameter environments + +Another problem was that it was not possible to normalize `ParamEnv`s correctly in the old solver as normalization itself would expect a normalized `ParamEnv` in order to give correct results. See the chapter on `ParamEnv`s for more information: [`Typing/ParamEnv`s: Normalizing all bounds](./typing_parameter_envs.md#normalizing-all-bounds) + +### Unnormalizable non-rigid aliases in higher ranked types + +Given a type such as `for<'a> fn(::Assoc>)`, it is not possible to correctly handle this with the old solver's approach to normalization. + +If we were to normalize it to `for<'a> fn(?y)` and register a goal to normalize `for<'a> >::Assoc -> ?y`, this would result in errors in cases where `>::Assoc` normalized to `&'a u32`. The inference variable `?y` would be in a lower [universe][universes] than the placeholders made when instantiating the `for<'a>` binder. + +Leaving the alias unnormalized would also be wrong as the old solver expects all aliases to be rigid. This was a soundness bug before the new solver was stabilized in coherence: [relating projection substs is unsound during coherence](https://github.com/rust-lang/rust/issues/102048). + +Ultimately this means that it is not always possible to ensure all aliases inside of a value are rigid. + +[universes]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html#what-is-a-universe +[deeply_normalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/normalize/trait.NormalizeExt.html#tymethod.deeply_normalize + +## Handling uses of diverging aliases + +Diverging aliases, like ambiguous aliases, are normalized to inference variables. As normalizing diverging aliases results in trait solver cycles, it always results in an error in the old solver. In the new solver it only results in an error if we wind up requiring all goals to hold in the current context. E.g. normalizing diverging aliases during HIR typeck will result in an error in both solvers. + +Alias well formedness doesn't require that the alias doesn't diverge[^4], this means that checking an alias is well formed isn't sufficient to cause an error to be emitted for diverging aliases; actually attempting to normalize the alias is required. + +Erroring on diverging aliases being a side effect of normalization means that it is very *arbitrary* whether we actually emit an error, it also differs between the old and new solver as we now normalize in less places. + +An example of the ad-hoc nature of erroring on diverging aliases causing "problems": +```rust +trait Trait { + type Diverges; +} + +impl Trait for T { + type Diverges = D::Diverges; +} + +struct Bar::Diverges>(Box); +``` + +In this example a diverging alias is used but we happen to not emit an error as we never explicitly normalize the defaults of generic parameters. If the `?Sized` opt out is removed then an error is emitted because we wind up happening to normalize a `::Diverges: Sized` goal which as a side effect results in erroring about the diverging alias. + +Const aliases differ from type aliases a bit here; well formedness of const aliases requires that they can be successfully evaluated (via [`ConstEvaluatable`][const_evaluatable] goals). This means that simply checking well formedness of const arguments is sufficient to error if they would fail to evaluate. It is somewhat unclear whether it would make sense to adopt this for type aliases too or if const aliases should stop requiring this for well formedness[^5]. + +[^1]: In the new solver this is done implicitly + +[^2]: There is a subtle difference in how ambiguous aliases in binders are handled between old and new solver. In the old solver we fail to error on some ambiguous aliases inside of higher ranked types whereas the new solver correctly errors. + +[^3]: Ambiguous aliases inside of binders cannot be normalized to inference variables, this will be covered more later. + +[^4]: As checking aliases are non-diverging cannot be done until they are fully concrete, this would either imply that we cant check aliases are well formed before codegen/const-evaluation or that aliases would go from being well-formed to not well-formed after monomorphization. + +[^5]: Const aliases certainly wouldn't be *less* sound than type aliases if we stopped doing this + +[const_evaluatable]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ClauseKind.html#variant.ConstEvaluatable \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/notification-groups/about.md b/src/doc/rustc-dev-guide/src/notification-groups/about.md index 74629aa08acec..af305f0103ae8 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/about.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/about.md @@ -23,7 +23,7 @@ Here's the list of the notification groups: - [ARM](./arm.md) - [Cleanup Crew](./cleanup-crew.md) - [Emscripten](./emscripten.md) -- [LLVM](./llvm.md) +- [LLVM Icebreakers](./llvm.md) - [RISC-V](./risc-v.md) - [WASI](./wasi.md) - [WebAssembly](./wasm.md) @@ -83,7 +83,7 @@ group. For example: @rustbot ping arm @rustbot ping cleanup-crew @rustbot ping emscripten -@rustbot ping llvm +@rustbot ping icebreakers-llvm @rustbot ping risc-v @rustbot ping wasi @rustbot ping wasm diff --git a/src/doc/rustc-dev-guide/src/notification-groups/fuchsia.md b/src/doc/rustc-dev-guide/src/notification-groups/fuchsia.md new file mode 100644 index 0000000000000..e3c1a7148d3c7 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/notification-groups/fuchsia.md @@ -0,0 +1,12 @@ +# Fuchsia notification group + +**Github Label:** [O-fuchsia]
    +**Ping command:** `@rustbot ping fuchsia` + +[O-fuchsia]: https://github.com/rust-lang/rust/labels/O-fuchsia + +This list will be used to notify [Fuchsia][fuchsia] maintainers +when the compiler or the standard library changes in a way that would +break the Fuchsia integration. + +[fuchsia]: ../tests/ecosystem-test-jobs/fuchsia.md diff --git a/src/doc/rustc-dev-guide/src/notification-groups/llvm.md b/src/doc/rustc-dev-guide/src/notification-groups/llvm.md index 2eff63713a9c9..9d0087285438d 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/llvm.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/llvm.md @@ -1,13 +1,16 @@ -# LLVM Notification group +# LLVM Icebreakers Notification group **Github Label:** [A-LLVM]
    -**Ping command:** `@rustbot ping llvm` +**Ping command:** `@rustbot ping icebreakers-llvm` [A-LLVM]: https://github.com/rust-lang/rust/labels/A-LLVM -The "LLVM Notification Group" are focused on bugs that center around LLVM. -These bugs often arise because of LLVM optimizations gone awry, or as -the result of an LLVM upgrade. The goal here is: +*Note*: this notification group is *not* the same as the LLVM working group +(WG-llvm). + +The "LLVM Icebreakers Notification Group" are focused on bugs that center around +LLVM. These bugs often arise because of LLVM optimizations gone awry, or as the +result of an LLVM upgrade. The goal here is: - to determine whether the bug is a result of us generating invalid LLVM IR, or LLVM misoptimizing; diff --git a/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md b/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md index 9ba4eff629e7c..696f2038e1a4b 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/rust-for-linux.md @@ -1,9 +1,9 @@ # Rust for Linux notification group -**Github Label:** [O-rfl]
    +**Github Label:** [A-rust-for-linux]
    **Ping command:** `@rustbot ping rfl` -[O-rfl]: https://github.com/rust-lang/rust/labels/O-rfl +[A-rust-for-linux]: https://github.com/rust-lang/rust/labels/A-rust-for-linux This list will be used to notify [Rust for Linux (RfL)][rfl] maintainers when the compiler or the standard library changes in a way that would diff --git a/src/doc/rustc-dev-guide/src/overview.md b/src/doc/rustc-dev-guide/src/overview.md index 21ab0040646b4..92d0c7b0c38c8 100644 --- a/src/doc/rustc-dev-guide/src/overview.md +++ b/src/doc/rustc-dev-guide/src/overview.md @@ -351,7 +351,7 @@ approach is to turn [`RefCell`]s into [`Mutex`]s -- that is, we switch to thread-safe internal mutability. However, there are ongoing challenges with lock contention, maintaining query-system invariants under concurrency, and the complexity of the code base. One can try out the current -work by enabling parallel compilation in `config.toml`. It's still early days, +work by enabling parallel compilation in `bootstrap.toml`. It's still early days, but there are already some promising performance improvements. [`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html diff --git a/src/doc/rustc-dev-guide/src/panic-implementation.md b/src/doc/rustc-dev-guide/src/panic-implementation.md index f358742866719..468190ffccd50 100644 --- a/src/doc/rustc-dev-guide/src/panic-implementation.md +++ b/src/doc/rustc-dev-guide/src/panic-implementation.md @@ -1,4 +1,4 @@ -# Panicking in rust +# Panicking in Rust diff --git a/src/doc/rustc-dev-guide/src/parallel-rustc.md b/src/doc/rustc-dev-guide/src/parallel-rustc.md index c5b70706a8180..ce69b66c2daf5 100644 --- a/src/doc/rustc-dev-guide/src/parallel-rustc.md +++ b/src/doc/rustc-dev-guide/src/parallel-rustc.md @@ -1,4 +1,4 @@ -# Parallel Compilation +# Parallel compilation
    As of November 2024, @@ -28,7 +28,7 @@ The following sections are kept for now but are quite outdated. [codegen]: backend/codegen.md -## Code Generation +## Code generation During monomorphization the compiler splits up all the code to be generated into smaller chunks called _codegen units_. These are then generated by @@ -38,7 +38,7 @@ occurs in the [`rustc_codegen_ssa::base`] module. [`rustc_codegen_ssa::base`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/base/index.html -## Data Structures +## Data structures The underlying thread-safe data-structures used in the parallel compiler can be found in the [`rustc_data_structures::sync`] module. These data structures @@ -46,10 +46,8 @@ are implemented differently depending on whether `parallel-compiler` is true. | data structure | parallel | non-parallel | | -------------------------------- | --------------------------------------------------- | ------------ | -| OnceCell | std::sync::OnceLock | std::cell::OnceCell | | Lock\ | (parking_lot::Mutex\) | (std::cell::RefCell) | | RwLock\ | (parking_lot::RwLock\) | (std::cell::RefCell) | -| MTRef<'a, T> | &'a T | &'a mut T | | MTLock\ | (Lock\) | (T) | | ReadGuard | parking_lot::RwLockReadGuard | std::cell::Ref | | MappedReadGuard | parking_lot::MappedRwLockReadGuard | std::cell::Ref | @@ -85,7 +83,7 @@ can be accessed directly through `Deref::deref`. [`rustc_data_structures::sync::worker_local`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_data_structures/sync/worker_local/index.html [`WorkerLocal`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_data_structures/sync/worker_local/struct.WorkerLocal.html -## Parallel Iterator +## Parallel iterator The parallel iterators provided by the [`rayon`] crate are easy ways to implement parallelism. In the current implementation of the parallel compiler @@ -126,7 +124,7 @@ the parallel iterator function has been used are as follows: There are still many loops that have the potential to use parallel iterators. -## Query System +## Query system The query model has some properties that make it actually feasible to evaluate multiple queries in parallel without too much effort: diff --git a/src/doc/rustc-dev-guide/src/param_env/param_env_acquisition.md b/src/doc/rustc-dev-guide/src/param_env/param_env_acquisition.md deleted file mode 100644 index f6cff2d6c63cc..0000000000000 --- a/src/doc/rustc-dev-guide/src/param_env/param_env_acquisition.md +++ /dev/null @@ -1,43 +0,0 @@ - -# Which `ParamEnv` do I use? - -When needing a [`ParamEnv`][pe] in the compiler there are a few options for obtaining one: -- The correct env is already in scope simply use it (or pass it down the call stack to where you are). -- The [`tcx.param_env(def_id)` query][param_env_query] -- Use [`ParamEnv::new`][param_env_new] to construct an env with an arbitrary set of where clauses. Then call [`traits::normalize_param_env_or_error`][normalize_env_or_error] which will handle normalizing and elaborating all the where clauses in the env for you. -- Creating an empty environment via [`ParamEnv::reveal_all`][env_reveal_all] or [`ParamEnv::empty`][env_empty] - -In the large majority of cases a `ParamEnv` when required already exists somewhere in scope or above in the call stack and should be passed down. A non exhaustive list of places where you might find an existing `ParamEnv`: -- During typeck `FnCtxt` has a [`param_env` field][fnctxt_param_env] -- When writing late lints the `LateContext` has a [`param_env` field][latectxt_param_env] -- During well formedness checking the `WfCheckingCtxt` has a [`param_env` field][wfckctxt_param_env] -- The `TypeChecker` used by Mir Typeck has a [`param_env` field][mirtypeck_param_env] -- In the next-gen trait solver all `Goal`s have a [`param_env` field][goal_param_env] specifying what environment to prove the goal in -- When editing an existing [`TypeRelation`][typerelation] if it implements `PredicateEmittingRelation` then a [`param_env` method][typerelation_param_env] will be available. - -Using the `param_env` query to obtain an env is generally done at the start of some kind of analysis and then passed everywhere that a `ParamEnv` is required. For example the type checker will create a `ParamEnv` for the item it is type checking and then pass it around everywhere. - -Creating an env from an arbitrary set of where clauses is usually unnecessary and should only be done if the environment you need does not correspond to an actual item in the source code (i.e. [`compare_method_predicate_entailment`][method_pred_entailment] as mentioned earlier). - -Creating an empty environment via `ParamEnv::empty` is almost always wrong. There are very few places where we actually know that the environment should be empty. One of the only places where we do actually know this is after monomorphization, however the `ParamEnv` there should be constructed via `ParamEnv::reveal_all` instead as at this point we should be able to determine the hidden type of opaque types. Codegen/Post-mono is one of the only places that should be using `ParamEnv::reveal_all`. - -An additional piece of complexity here is specifying the `Reveal` (see linked docs for explanation of what reveal does) used for the `ParamEnv`. When constructing a param env using the `param_env` query it will have `Reveal::UserFacing`, if `Reveal::All` is desired then the [`tcx.param_env_reveal_all_normalized`][env_reveal_all_normalized] query can be used instead. - -The `ParamEnv` type has a method [`ParamEnv::with_reveal_all_normalized`][with_reveal_all] which converts an existing `ParamEnv` into one with `Reveal::All` specified. Where possible the previously mentioned query should be preferred as it is more efficient. - -[param_env_new]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.new -[normalize_env_or_error]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/fn.normalize_param_env_or_error.html -[fnctxt_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#structfield.param_env -[latectxt_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/context/struct.LateContext.html#structfield.param_env -[wfckctxt_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/wfcheck/struct.WfCheckingCtxt.html#structfield.param_env -[goal_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/canonical/ir/solve/struct.Goal.html#structfield.param_env -[typerelation_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/trait.PredicateEmittingRelation.html#tymethod.param_env -[typerelation]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/relate/trait.TypeRelation.html -[mirtypeck_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/type_check/struct.TypeChecker.html#structfield.param_env -[env_reveal_all_normalized]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.param_env_reveal_all_normalized -[with_reveal_all]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.with_reveal_all_normalized -[env_reveal_all]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.reveal_all -[env_empty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.empty -[pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html -[param_env_query]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#structfield.param_env -[method_pred_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html diff --git a/src/doc/rustc-dev-guide/src/param_env/param_env_construction_internals.md b/src/doc/rustc-dev-guide/src/param_env/param_env_construction_internals.md deleted file mode 100644 index 69a262a176e09..0000000000000 --- a/src/doc/rustc-dev-guide/src/param_env/param_env_construction_internals.md +++ /dev/null @@ -1,83 +0,0 @@ - -# How are `ParamEnv`'s constructed internally? - -Creating a [`ParamEnv`][pe] is more complicated than simply using the list of where clauses defined on an item as written by the user. We need to both elaborate supertraits into the env and fully normalize all aliases. This logic is handled by [`traits::normalize_param_env_or_error`][normalize_env_or_error] (even though it does not mention anything about elaboration). - -## Elaborating supertraits - -When we have a function such as `fn foo()` we would like to be able to prove `T: Clone` inside of the function as the `Copy` trait has a `Clone` supertrait. Constructing a `ParamEnv` looks at all of the trait bounds in the env and explicitly adds new where clauses to the `ParamEnv` for any supertraits found on the traits. - -A concrete example would be the following function: -```rust -trait Trait: SuperTrait {} -trait SuperTrait: SuperSuperTrait {} - -// `bar`'s unelaborated `ParamEnv` would be: -// `[T: Sized, T: Copy, T: Trait]` -fn bar(a: T) { - requires_impl(a); -} - -fn requires_impl(a: T) {} -``` - -If we did not elaborate the env then the `requires_impl` call would fail to typecheck as we would not be able to prove `T: Clone` or `T: SuperSuperTrait`. In practice we elaborate the env which means that `bar`'s `ParamEnv` is actually: -`[T: Sized, T: Copy, T: Clone, T: Trait, T: SuperTrait, T: SuperSuperTrait]` -This allows us to prove `T: Clone` and `T: SuperSuperTrait` when type checking `bar`. - -The `Clone` trait has a `Sized` supertrait however we do not end up with two `T: Sized` bounds in the env (one for the supertrait and one for the implicitly added `T: Sized` bound). This is because the elaboration process (implemented via [`util::elaborate`][elaborate]) deduplicates the where clauses to avoid this. - -As a side effect this also means that even if no actual elaboration of supertraits takes place, the existing where clauses in the env are _also_ deduplicated. See the following example: -```rust -trait Trait {} -// The unelaborated `ParamEnv` would be: -// `[T: Sized, T: Trait, T: Trait]` -// but after elaboration it would be: -// `[T: Sized, T: Trait]` -fn foo() {} -``` - -The [next-gen trait solver][next-gen-solver] also requires this elaboration to take place. - -[elaborate]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/traits/util/fn.elaborate.html -[next-gen-solver]: ../solve/trait-solving.md - -## Normalizing all bounds - -In the old trait solver the where clauses stored in `ParamEnv` are required to be fully normalized or else the trait solver will not function correctly. A concrete example of needing to normalize the `ParamEnv` is the following: -```rust -trait Trait { - type Assoc; -} - -trait Other { - type Bar; -} - -impl Other for T { - type Bar = u32; -} - -// `foo`'s unnormalized `ParamEnv` would be: -// `[T: Sized, U: Sized, U: Trait]` -fn foo(a: U) -where - U: Trait<::Bar>, -{ - requires_impl(a); -} - -fn requires_impl>(_: U) {} -``` - -As humans we can tell that `::Bar` is equal to `u32` so the trait bound on `U` is equivalent to `U: Trait`. In practice trying to prove `U: Trait` in the old solver in this environment would fail as it is unable to determine that `::Bar` is equal to `u32`. - -To work around this we normalize `ParamEnv`'s after constructing them so that `foo`'s `ParamEnv` is actually: `[T: Sized, U: Sized, U: Trait]` which means the trait solver is now able to use the `U: Trait` in the `ParamEnv` to determine that the trait bound `U: Trait` holds. - -This workaround does not work in all cases as normalizing associated types requires a `ParamEnv` which introduces a bootstrapping problem. We need a normalized `ParamEnv` in order for normalization to give correct results, but we need to normalize to get that `ParamEnv`. Currently we normalize the `ParamEnv` once using the unnormalized param env and it tends to give okay results in practice even though there are some examples where this breaks ([example]). - -In the next-gen trait solver the requirement for all where clauses in the `ParamEnv` to be fully normalized is not present and so we do not normalize when constructing `ParamEnv`s. - -[example]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e6933265ea3e84eaa47019465739992c -[pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html -[normalize_env_or_error]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/fn.normalize_param_env_or_error.html diff --git a/src/doc/rustc-dev-guide/src/param_env/param_env_summary.md b/src/doc/rustc-dev-guide/src/param_env/param_env_summary.md deleted file mode 100644 index 0ff6d8fc394e2..0000000000000 --- a/src/doc/rustc-dev-guide/src/param_env/param_env_summary.md +++ /dev/null @@ -1,18 +0,0 @@ -# The `ParamEnv` type - -## Summary - -The [`ParamEnv`][pe] is used to store information about the environment that we are interacting with the type system from. For example the set of in-scope where-clauses is stored in `ParamEnv` as it differs between each item whereas the list of user written impls is not stored in the `ParamEnv` as this does not change for each item. - -This chapter of the dev guide covers: -- A high level summary of what a `ParamEnv` is and what it is used for -- Technical details about what the process of constructing a `ParamEnv` involves -- Guidance about how to acquire a `ParamEnv` when one is required - -## Bundling - -A useful API on `ParamEnv` is the [`and`][and] method which allows bundling a value with the `ParamEnv`. The `and` method produces a [`ParamEnvAnd`][pea] making it clearer that using the inner value is intended to be done in that specific environment. - -[and]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.and -[pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html -[pea]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnvAnd.html \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/param_env/param_env_what_is_it.md b/src/doc/rustc-dev-guide/src/param_env/param_env_what_is_it.md deleted file mode 100644 index 5c2f4d594052e..0000000000000 --- a/src/doc/rustc-dev-guide/src/param_env/param_env_what_is_it.md +++ /dev/null @@ -1,59 +0,0 @@ - -# What is a `ParamEnv`? - -The type system relies on information in the environment in order for it to function correctly. This information is stored in the [`ParamEnv`][pe] type and it is important to use the correct `ParamEnv` when interacting with the type system. - -The information represented by `ParamEnv` is a list of in-scope where-clauses, and a `Reveal` (see linked docs for more information). A `ParamEnv` typically corresponds to a specific item's where clauses, some clauses are not explicitly written bounds and instead are implicitly added in [`predicates_of`][predicates_of] such as `ConstArgHasType` or some implied bounds. - -A `ParamEnv` can also be created with arbitrary data that is not derived from a specific item such as in [`compare_method_predicate_entailment`][method_pred_entailment] which creates a hybrid `ParamEnv` consisting of the impl's where clauses and the trait definition's function's where clauses. In most cases `ParamEnv`s are initially created via the [`param_env` query][query] which returns a `ParamEnv` derived from the provided item's where clauses. - -If we have a function such as: -```rust -// `foo` would have a `ParamEnv` of: -// `[T: Sized, T: Trait, ::Assoc: Clone]` -fn foo() -where - ::Assoc: Clone, -{} -``` -If we were conceptually inside of `foo` (for example, type-checking or linting it) we would use this `ParamEnv` everywhere that we interact with the type system. This would allow things such as normalization (TODO: write a chapter about normalization and link it), evaluating generic constants, and proving where clauses/goals, to rely on `T` being sized, implementing `Trait`, etc. - -A more concrete example: -```rust -// `foo` would have a `ParamEnv` of: -// `[T: Sized, T: Clone]` -fn foo(a: T) { - // when typechecking `foo` we require all the where clauses on `bar` - // to hold in order for it to be legal to call. This means we have to - // prove `T: Clone`. As we are type checking `foo` we use `foo`'s - // environment when trying to check that `T: Clone` holds. - // - // Trying to prove `T: Clone` with a `ParamEnv` of `[T: Sized, T: Clone]` - // will trivially succeed as bound we want to prove is in our environment. - requires_clone(a); -} -``` - -Or alternatively an example that would not compile: -```rust -// `foo2` would have a `ParamEnv` of: -// `[T: Sized]` -fn foo2(a: T) { - // When typechecking `foo2` we attempt to prove `T: Clone`. - // As we are type checking `foo2` we use `foo2`'s environment - // when trying to prove `T: Clone`. - // - // Trying to prove `T: Clone` with a `ParamEnv` of `[T: Sized]` will - // fail as there is nothing in the environment telling the trait solver - // that `T` implements `Clone` and there exists no user written impl - // that could apply. - requires_clone(a); -} -``` - -It's very important to use the correct `ParamEnv` when interacting with the type system as otherwise it can lead to ICEs or things compiling when they shouldn't (or vice versa). See [#82159](https://github.com/rust-lang/rust/pull/82159) and [#82067](https://github.com/rust-lang/rust/pull/82067) as examples of PRs that changed rustc to use the correct param env to avoid ICE. Determining how to acquire the correct `ParamEnv` is explained later in this chapter. - -[predicates_of]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/collect/predicates_of/fn.predicates_of.html -[method_pred_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html -[pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html -[query]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.param_env diff --git a/src/doc/rustc-dev-guide/src/profile-guided-optimization.md b/src/doc/rustc-dev-guide/src/profile-guided-optimization.md index 995752b0b8b53..39bc8b5e86246 100644 --- a/src/doc/rustc-dev-guide/src/profile-guided-optimization.md +++ b/src/doc/rustc-dev-guide/src/profile-guided-optimization.md @@ -120,7 +120,7 @@ The `rustc` version of this can be found in `library/profiler_builtins` which basically packs the C code from `compiler-rt` into a Rust crate. In order for `profiler_builtins` to be built, `profiler = true` must be set -in `rustc`'s `config.toml`. +in `rustc`'s `bootstrap.toml`. [compiler-rt-profile]: https://github.com/llvm/llvm-project/tree/main/compiler-rt/lib/profile diff --git a/src/doc/rustc-dev-guide/src/profiling.md b/src/doc/rustc-dev-guide/src/profiling.md index df987e00a581b..de06bd7cda7b3 100644 --- a/src/doc/rustc-dev-guide/src/profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling.md @@ -87,7 +87,7 @@ Example output for the compiler: Since this doesn't seem to work with incremental compilation or `./x check`, you will be compiling rustc _a lot_. -I recommend changing a few settings in `config.toml` to make it bearable: +I recommend changing a few settings in `bootstrap.toml` to make it bearable: ``` [rust] # A debug build takes _a third_ as long on my machine, diff --git a/src/doc/rustc-dev-guide/src/profiling/with_perf.md b/src/doc/rustc-dev-guide/src/profiling/with_perf.md index 51a22d18577ed..742ea1c41a6c7 100644 --- a/src/doc/rustc-dev-guide/src/profiling/with_perf.md +++ b/src/doc/rustc-dev-guide/src/profiling/with_perf.md @@ -6,7 +6,7 @@ This is a guide for how to profile rustc with [perf](https://perf.wiki.kernel.or - Get a clean checkout of rust-lang/master, or whatever it is you want to profile. -- Set the following settings in your `config.toml`: +- Set the following settings in your `bootstrap.toml`: - `debuginfo-level = 1` - enables line debuginfo - `jemalloc = false` - lets you do memory use profiling with valgrind - leave everything else the defaults diff --git a/src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md b/src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md index eda8c3a179ca5..c47fed24e6e31 100644 --- a/src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md +++ b/src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md @@ -9,7 +9,7 @@ which will download and build the suite for you, build a local compiler toolchai You can use the `./x perf [options]` command to use this integration. -You can use normal bootstrap flags for this command, such as `--stage 1` or `--stage 2`, for example to modify the stage of the created sysroot. It might also be useful to configure `config.toml` to better support profiling, e.g. set `rust.debuginfo-level = 1` to add source line information to the built compiler. +You can use normal bootstrap flags for this command, such as `--stage 1` or `--stage 2`, for example to modify the stage of the created sysroot. It might also be useful to configure `bootstrap.toml` to better support profiling, e.g. set `rust.debuginfo-level = 1` to add source line information to the built compiler. `x perf` currently supports the following commands: - `benchmark `: Benchmark the compiler and store the results under the passed `id`. diff --git a/src/doc/rustc-dev-guide/src/profiling/wpa_profiling.md b/src/doc/rustc-dev-guide/src/profiling/wpa_profiling.md index a800c5717e352..d2680d40853f7 100644 --- a/src/doc/rustc-dev-guide/src/profiling/wpa_profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling/wpa_profiling.md @@ -44,7 +44,7 @@ compiler we're using to build rustc will aid our analysis greatly by allowing WP symbols correctly. Unfortunately, the stage 0 compiler does not have symbols turned on which is why we'll need to build a stage 1 compiler and then a stage 2 compiler ourselves. -To do this, make sure you have set `debuginfo-level = 1` in your `config.toml` file. This tells +To do this, make sure you have set `debuginfo-level = 1` in your `bootstrap.toml` file. This tells rustc to generate debug information which includes stack frames when bootstrapping. Now you can build the stage 1 compiler: `x build --stage 1 -i library` or however diff --git a/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md b/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md index 4133b196c0af7..03c822d4feed6 100644 --- a/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md +++ b/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md @@ -1,4 +1,4 @@ -# Incremental Compilation In Detail +# Incremental Compilation in detail diff --git a/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md b/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md index f7f204bf79d3b..444e20bc580e3 100644 --- a/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md +++ b/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md @@ -1,4 +1,4 @@ -# The Query Evaluation Model in Detail +# The Query Evaluation Model in detail diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/getting-diagnostics.md b/src/doc/rustc-dev-guide/src/rustc-driver/getting-diagnostics.md index 1043df6ecb65c..518cf4e821a72 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver/getting-diagnostics.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver/getting-diagnostics.md @@ -7,7 +7,7 @@ otherwise be printed to stderr. To get diagnostics from the compiler, configure [`rustc_interface::Config`] to output diagnostic to a buffer, -and run [`TyCtxt.analysis`]. +and run [`rustc_hir_typeck::typeck`] for each item. ```rust {{#include ../../examples/rustc-interface-getting-diagnostics.rs}} @@ -16,3 +16,4 @@ and run [`TyCtxt.analysis`]. [`rustc_interface`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/index.html [`rustc_interface::Config`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html [`TyCtxt.analysis`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/passes/fn.analysis.html +[`rustc_hir_typeck::typeck`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn.typeck.html diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/intro.md b/src/doc/rustc-dev-guide/src/rustc-driver/intro.md index 40500e6bc7a5f..a3684397b2995 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver/intro.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver/intro.md @@ -7,8 +7,8 @@ It acts as the glue for running the various phases of the compiler in the correc using the interface defined in the [`rustc_interface`] crate. Where possible, using [`rustc_driver`] rather than [`rustc_interface`] is recommended. The main entry point of [`rustc_driver`] is [`rustc_driver::run_compiler`][rd_rc]. -This builder accepts the same command-line args as rustc as well as an implementation of [`Callbacks`][cb] and a couple of other optional options. -[`Callbacks`][cb] is a `trait` that allows for custom compiler configuration, +This builder accepts the same command-line args as rustc as well as an implementation of [`Callbacks`] and a couple of other optional options. +[`Callbacks`] is a `trait` that allows for custom compiler configuration, as well as allowing custom code to run after different phases of the compilation. ## `rustc_interface` @@ -33,14 +33,8 @@ specifically [`rustc_driver_impl::run_compiler`][rdi_rc] [`Compiler`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Compiler.html [`rustc_driver`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/ [`rustc_interface`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/index.html -[`Session`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/struct.Session.html -[`SourceMap`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/source_map/struct.SourceMap.html -[`TyCtxt`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html -[Appendix A]: appendix/stupid-stats.html -[cb]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/trait.Callbacks.html +[`Callbacks`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/trait.Callbacks.html [example]: https://github.com/rust-lang/rustc-dev-guide/blob/master/examples/rustc-interface-example.rs [i_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/fn.run_compiler.html [rd_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/fn.run_compiler.html [rdi_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver_impl/fn.run_compiler.html -[stupid-stats]: https://github.com/nrc/stupid-stats -[`nightly-rustc`]: https://doc.rust-lang.org/nightly/nightly-rustc/ diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/remarks-on-perma-unstable-features.md b/src/doc/rustc-dev-guide/src/rustc-driver/remarks-on-perma-unstable-features.md new file mode 100644 index 0000000000000..b434cfc9cf146 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/rustc-driver/remarks-on-perma-unstable-features.md @@ -0,0 +1,54 @@ +# Remarks on perma unstable features + +## `rustc_private` + +### Overview + +The `rustc_private` feature allows external crates to use compiler internals. + +### Using `rustc-private` with Official Toolchains + +When using the `rustc_private` feature with official Rust toolchains distributed via rustup, you need to install two additional components: + +1. **`rustc-dev`**: Provides compiler libraries +2. **`llvm-tools`**: Provides LLVM libraries required for linking + +#### Installation Steps + +Install both components using rustup: + +```text +rustup component add rustc-dev llvm-tools +``` + +#### Common Error + +Without the `llvm-tools` component, you'll encounter linking errors like: + +```text +error: linking with `cc` failed: exit status: 1 + | + = note: rust-lld: error: unable to find library -lLLVM-{version} +``` + +### Using `rustc-private` with Custom Toolchains + +For custom-built toolchains or environments not using rustup, additional configuration is typically required: + +#### Requirements + +- LLVM libraries must be available in your system's library search paths +- The LLVM version must match the one used to build your Rust toolchain + +#### Troubleshooting Steps + +1. **Check LLVM installation**: Verify LLVM is installed and accessible +2. **Configure library paths**: You may need to set environment variables: + ```text + export LD_LIBRARY_PATH=/path/to/llvm/lib:$LD_LIBRARY_PATH + ``` +3. **Check version compatibility**: Ensure your LLVM version is compatible with your Rust toolchain + +### Additional Resources + +- [GitHub Issue #137421](https://github.com/rust-lang/rust/issues/137421): Explains that `rustc_private` linker failures often occur because `llvm-tools` is not installed diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals.md b/src/doc/rustc-dev-guide/src/rustdoc-internals.md index 7f1c83e00f98d..80421b85bf055 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals.md @@ -55,8 +55,8 @@ The first step in [`clean::utils::krate`][ck1] is to invoke * inlining public `use` exports of private items, or showing a "Reexport" line in the module page * inlining items with `#[doc(hidden)]` if the base item is hidden but the - * showing `#[macro_export]`-ed macros at the crate root, regardless of where - they're defined reexport is not + * showing `#[macro_export]`-ed macros at the crate root, regardless of whether + they're defined as a reexport or not After this step, `clean::krate` invokes [`clean_doc_module`], which actually converts the `HIR` items to the cleaned [`AST`][ast]. This is also the step where cross- diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md new file mode 100644 index 0000000000000..bad7ac19da2cb --- /dev/null +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md @@ -0,0 +1,112 @@ +# The `rustdoc` test suite + +This page is specifically about the test suite named `rustdoc`. +For other test suites used for testing rustdoc, see [Rustdoc tests](../rustdoc.md#tests). + +The `rustdoc` test suite is specifically used to test the HTML output of rustdoc. + +This is achieved by means of `htmldocck.py`, a custom checker script that leverages [XPath]. + +[XPath]: https://en.wikipedia.org/wiki/XPath + +## Directives +Directives to htmldocck are similar to those given to `compiletest` in that they take the form of `//@` comments. + +In addition to the directives listed here, +`rustdoc` tests also support most +[compiletest directives](../tests/directives.html). + +All `PATH`s in directives are relative to the rustdoc output directory (`build/TARGET/test/rustdoc/TESTNAME`), +so it is conventional to use a `#![crate_name = "foo"]` attribute to avoid +having to write a long crate name multiple times. +To avoid repetition, `-` can be used in any `PATH` argument to re-use the previous `PATH` argument. + +All arguments take the form of quoted strings +(both single and double quotes are supported), +with the exception of `COUNT` and the special `-` form of `PATH`. + +Directives are assertions that place constraints on the generated HTML. + +All directives (except `files`) can be negated by putting a `!` in front of their name. + +Similar to shell commands, +directives can extend across multiple lines if their last char is `\`. +In this case, the start of the next line should be `//`, with no `@`. + +For example, `//@ !has 'foo/struct.Bar.html'` checks that crate `foo` does not have a page for a struct named `Bar` in the crate root. + +### `has` + +Usage 1: `//@ has PATH` +Usage 2: `//@ has PATH XPATH PATTERN` + +In the first form, `has` checks that a given file exists. + +In the second form, `has` is an alias for `matches`, +except `PATTERN` is a whitespace-normalized[^1] string instead of a regex. + +### `matches` + +Usage: `//@ matches PATH XPATH PATTERN` + +Checks that the text of each element selected by `XPATH` in `PATH` matches the python-flavored regex `PATTERN`. + +### `matchesraw` + +Usage: `//@ matchesraw PATH PATTERN` + +Checks that the contents of the file `PATH` matches the regex `PATTERN`. + +### `hasraw` + +Usage: `//@ hasraw PATH PATTERN` + +Same as `matchesraw`, except `PATTERN` is a whitespace-normalized[^1] string instead of a regex. + +### `count` + +Usage: `//@ count PATH XPATH COUNT` + +Checks that there are exactly `COUNT` matches for `XPATH` within the file `PATH`. + +### `snapshot` + +Usage: `//@ snapshot NAME PATH XPATH` + +Creates a snapshot test named NAME. +A snapshot test captures a subtree of the DOM, at the location +determined by the XPath, and compares it to a pre-recorded value +in a file. The file's name is the test's name with the `.rs` extension +replaced with `.NAME.html`, where NAME is the snapshot's name. + +htmldocck supports the `--bless` option to accept the current subtree +as expected, saving it to the file determined by the snapshot's name. +compiletest's `--bless` flag is forwarded to htmldocck. + +### `has-dir` + +Usage: `//@ has-dir PATH` + +Checks for the existence of directory `PATH`. + +### `files` + +Usage: `//@ files PATH ENTRIES` + +Checks that the directory `PATH` contains exactly `ENTRIES`. +`ENTRIES` is a python list of strings inside a quoted string, +as if it were to be parsed by `eval`. +(note that the list is actually parsed by `shlex.split`, +so it cannot contain arbitrary python expressions). + +Example: `//@ files "foo/bar" '["index.html", "sidebar-items.js"]'` + +[^1]: Whitespace normalization means that all spans of consecutive whitespace are replaced with a single space. The files themselves are also whitespace-normalized. + +## Limitations +`htmldocck.py` uses the xpath implementation from the standard library. +This leads to several limitations: +* All `XPATH` arguments must start with `//` due to a flaw in the implementation. +* Many XPath features (functions, axies, etc.) are not supported. +* Only well-formed HTML can be parsed (hopefully rustdoc doesn't output mismatched tags). + diff --git a/src/doc/rustc-dev-guide/src/rustdoc.md b/src/doc/rustc-dev-guide/src/rustdoc.md index 2a0e212f98e31..e36d6a388a981 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc.md +++ b/src/doc/rustc-dev-guide/src/rustdoc.md @@ -58,12 +58,12 @@ does is call the `main()` that's in this crate's `lib.rs`, though.) * If you want to copy those docs to a webserver, copy all of `build/host/doc`, since that's where the CSS, JS, fonts, and landing page are. - * For frontend debugging, disable the `rust.docs-minification` option in [`config.toml`]. + * For frontend debugging, disable the `rust.docs-minification` option in [`bootstrap.toml`]. * Use `./x test tests/rustdoc*` to run the tests using a stage1 rustdoc. * See [Rustdoc internals] for more information about tests. -[`config.toml`]: ./building/how-to-build-and-run.md +[`bootstrap.toml`]: ./building/how-to-build-and-run.md ## Code structure @@ -77,29 +77,33 @@ does is call the `main()` that's in this crate's `lib.rs`, though.) `doctest.rs`. * The Markdown renderer is loaded up in `html/markdown.rs`, including functions for extracting doctests from a given block of Markdown. -* The tests on the structure of rustdoc HTML output are located in `tests/rustdoc`, where - they're handled by the test runner of bootstrap and the supplementary script - `src/etc/htmldocck.py`. * Frontend CSS and JavaScript are stored in `html/static/`. ## Tests -* All paths in this section are relative to `tests` in the rust-lang/rust repository. -* Tests on search engine and index are located in `rustdoc-js` and `rustdoc-js-std`. +* Tests on search engine and index are located in `tests/rustdoc-js` and `tests/rustdoc-js-std`. The format is specified [in the search guide](rustdoc-internals/search.md#testing-the-search-engine). * Tests on the "UI" of rustdoc (the terminal output it produces when run) are in - `rustdoc-ui` + `tests/rustdoc-ui` * Tests on the "GUI" of rustdoc (the HTML, JS, and CSS as rendered in a browser) - are in `rustdoc-gui`. These use a [NodeJS tool called + are in `tests/rustdoc-gui`. These use a [NodeJS tool called browser-UI-test](https://github.com/GuillaumeGomez/browser-UI-test/) that uses puppeteer to run tests in a headless browser and check rendering and - interactivity. + interactivity. For information on how to write this form of test, + see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme] + as well as [the description of the `.goml` format][goml-script] * Additionally, JavaScript type annotations are written using [TypeScript-flavored JSDoc] comments and an external d.ts file. The code itself is plain, valid JavaScript; we only use tsc as a linter. +* The tests on the structure of rustdoc HTML output are located in `tests/rustdoc`, + where they're handled by the test runner of bootstrap and + the supplementary script `src/etc/htmldocck.py`. + [These tests have several extra directives available to them](./rustdoc-internals/rustdoc-test-suite.md). [TypeScript-flavored JSDoc]: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html +[rustdoc-gui-readme]: https://github.com/rust-lang/rust/blob/master/tests/rustdoc-gui/README.md +[goml-script]: https://github.com/GuillaumeGomez/browser-UI-test/blob/master/goml-script.md ## Constraints diff --git a/src/doc/rustc-dev-guide/src/sanitizers.md b/src/doc/rustc-dev-guide/src/sanitizers.md index 73e888eae68fa..b1654b15e0819 100644 --- a/src/doc/rustc-dev-guide/src/sanitizers.md +++ b/src/doc/rustc-dev-guide/src/sanitizers.md @@ -32,7 +32,7 @@ implementation: * The sanitizer runtime libraries are part of the [compiler-rt] project, and [will be built][sanitizer-build] on [supported targets][sanitizer-targets] - when enabled in `config.toml`: + when enabled in `bootstrap.toml`: ```toml [build] @@ -80,7 +80,7 @@ Sanitizers are validated by code generation tests in [`tests/ui/sanitizer/`][test-ui] directory. Testing sanitizer functionality requires the sanitizer runtimes (built when -`sanitizer = true` in `config.toml`) and target providing support for particular +`sanitizer = true` in `bootstrap.toml`) and target providing support for particular sanitizer. When sanitizer is unsupported on given target, sanitizers tests will be ignored. This behaviour is controlled by compiletest `needs-sanitizer-*` directives. diff --git a/src/doc/rustc-dev-guide/src/serialization.md b/src/doc/rustc-dev-guide/src/serialization.md index 0ff0499012863..47667061edaed 100644 --- a/src/doc/rustc-dev-guide/src/serialization.md +++ b/src/doc/rustc-dev-guide/src/serialization.md @@ -1,4 +1,4 @@ -# Serialization in Rustc +# Serialization in rustc rustc has to [serialize] and deserialize various data during compilation. Specifically: @@ -169,7 +169,7 @@ The `LazyArray<[T]>` and `LazyTable` types provide some functionality over than the one being read. **note**: `LazyValue` does not cache its value after being deserialized the -first time. Instead the query system its self is the main way of caching these +first time. Instead the query system itself is the main way of caching these results. [`LazyArray`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/rmeta/struct.LazyValue.html diff --git a/src/doc/rustc-dev-guide/src/solve/normalization.md b/src/doc/rustc-dev-guide/src/solve/normalization.md deleted file mode 100644 index 99dc20c46b5d4..0000000000000 --- a/src/doc/rustc-dev-guide/src/solve/normalization.md +++ /dev/null @@ -1,127 +0,0 @@ -# Normalization in the new solver - -> FIXME: Normalization has been changed significantly since this chapter was written. - -With the new solver we've made some fairly significant changes to normalization when compared -to the existing implementation. - -We now differentiate between "one-step normalization", "structural normalization" and -"deep normalization". - -## One-step normalization - -One-step normalization is implemented via `NormalizesTo` goals. Unlike other goals -in the trait solver, `NormalizesTo` always expects the term to be an unconstrained -inference variable[^opaques]. Think of it as a function, taking an alias as input -and returning its underlying value. If the alias is rigid, `NormalizesTo` fails and -returns `NoSolution`. This is the case for `::Assoc` if there's a `T: Trait` -where-bound and for opaque types with `Reveal::UserFacing` unless they are in the -defining scope. We must not treat any aliases as rigid in coherence. - -The underlying value may itself be an unnormalized alias, e.g. -`NormalizesTo(<<() as Id>::This as Id>::This)` only returns `<() as Id>::This`, -even though that alias can be further normalized to `()`. As the term is -always an unconstrained inference variable, the expected term cannot influence -normalization, see [trait-system-refactor-initiative#22] for more. - -Only ever computing `NormalizesTo` goals with an unconstrained inference variable -requires special solver support. It is only used by `AliasRelate` goals and pending -`NormalizesTo` goals are tracked separately from other goals: [source][try-eval-norm]. -As the expected term is always erased in `NormalizesTo`, we have to return its -ambiguous nested goals to its caller as not doing so weakens inference. See -[#122687] for more details. - -[trait-system-refactor-initiative#22]: https://github.com/rust-lang/trait-system-refactor-initiative/issues/22 -[try-eval-norm]: https://github.com/rust-lang/rust/blob/2627e9f3012a97d3136b3e11bf6bd0853c38a534/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs#L523-L537 -[#122687]: https://github.com/rust-lang/rust/pull/122687 - -## `AliasRelate` and structural normalization - -We structurally normalize an alias by applying one-step normalization until -we end up with a rigid alias, ambiguity, or overflow. This is done by repeatedly -evaluating `NormalizesTo` goals inside of a snapshot: [source][structural_norm]. - -`AliasRelate(lhs, rhs)` is implemented by first structurally normalizing both the -`lhs` and the `rhs` and then relating the resulting rigid types (or inference -variables). Importantly, if `lhs` or `rhs` ends up as an alias, this alias can -now be treated as rigid and gets unified without emitting a nested `AliasRelate` -goal: [source][structural-relate]. - -This means that `AliasRelate` with an unconstrained `rhs` ends up functioning -similar to `NormalizesTo`, acting as a function which fully normalizes `lhs` -before assigning the resulting rigid type to an inference variable. This is used by -`fn structurally_normalize_ty` both [inside] and [outside] of the trait solver. -This has to be used whenever we match on the value of some type, both inside -and outside of the trait solver. - - - -[structural_norm]: https://github.com/rust-lang/rust/blob/2627e9f3012a97d3136b3e11bf6bd0853c38a534/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L140-L175 -[structural-relate]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L88-L107 -[inside]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_trait_selection/src/solve/mod.rs#L278-L299 -[outside]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_trait_selection/src/traits/structural_normalize.rs#L17-L48 - -## Deep normalization - -By walking over a type, and using `fn structurally_normalize_ty` for each encountered -alias, it is possible to deeply normalize a type, normalizing all aliases as much as -possible. However, this only works for aliases referencing bound variables if they are -not ambiguous as we're unable to replace the alias with a corresponding inference -variable without leaking universes. - - - -[generalize-no-alias]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_infer/src/infer/relate/generalize.rs#L353-L358 - -## Outside of the trait solver - -The core type system - relating types and trait solving - will not need deep -normalization with the new solver. There are still some areas which depend on it. -For these areas there is the function `At::deeply_normalize`. Without additional -trait solver support deep normalization does not always work in case of ambiguity. -Luckily deep normalization is currently only necessary in places where there is no ambiguity. -`At::deeply_normalize` immediately fails if there's ambiguity. - -If we only care about the outermost layer of types, we instead use -`At::structurally_normalize` or `FnCtxt::(try_)structurally_resolve_type`. -Unlike `At::deeply_normalize`, structural normalization is also used in cases where we -have to handle ambiguity. - -Because this may result in behavior changes depending on how the trait solver handles -ambiguity, it is safer to also require full normalization there. This happens in -`FnCtxt::structurally_resolve_type` which always emits a hard error if the self type ends -up as an inference variable. There are some existing places which have a fallback for -inference variables instead. These places use `try_structurally_resolve_type` instead. - -## Why deep normalization with ambiguity is hard - -Fully correct deep normalization is very challenging, especially with the new solver -given that we do not want to deeply normalize inside of the solver. Mostly deeply normalizing -but sometimes failing to do so is bound to cause very hard to minimize and understand bugs. -If possible, avoiding any reliance on deep normalization entirely therefore feels preferable. - -If the solver itself does not deeply normalize, any inference constraints returned by the -solver would require normalization. Handling this correctly is ugly. This also means that -we change goals we provide to the trait solver by "normalizing away" some projections. - -The way we (mostly) guarantee deep normalization with the old solver is by eagerly replacing -the projection with an inference variable and emitting a nested `Projection` goal. This works -as `Projection` goals in the old solver deeply normalize. Unless we add another `PredicateKind` -for deep normalization to the new solver we cannot emulate this behavior. This does not work -for projections with bound variables, sometimes leaving them unnormalized. An approach which -also supports projections with bound variables will be even more involved. - -[^opaques]: opaque types are currently handled a bit differently. this may change in the future diff --git a/src/doc/rustc-dev-guide/src/solve/opaque-types.md b/src/doc/rustc-dev-guide/src/solve/opaque-types.md index 672aab7708018..509c34a4d3a75 100644 --- a/src/doc/rustc-dev-guide/src/solve/opaque-types.md +++ b/src/doc/rustc-dev-guide/src/solve/opaque-types.md @@ -33,7 +33,7 @@ For opaque types in the defining scope and in the implicit-negative coherence mo always done in two steps. Outside of the defining scope `normalizes-to` for opaques always returns `Err(NoSolution)`. -We start by trying to to assign the expected type as a hidden type. +We start by trying to assign the expected type as a hidden type. In the implicit-negative coherence mode, this currently always results in ambiguity without interacting with the opaque types storage. We could instead add allow 'defining' all opaque types, diff --git a/src/doc/rustc-dev-guide/src/solve/significant-changes.md b/src/doc/rustc-dev-guide/src/solve/significant-changes.md index c82b5d468961a..eac8f0318fb19 100644 --- a/src/doc/rustc-dev-guide/src/solve/significant-changes.md +++ b/src/doc/rustc-dev-guide/src/solve/significant-changes.md @@ -106,4 +106,4 @@ their ambiguous nested goals are returned to the caller which then evaluates the See [#122687] for more details. [#122687]: https://github.com/rust-lang/rust/pull/122687 -[normalization]: ./normalization.md +[normalization]: ../normalization.md diff --git a/src/doc/rustc-dev-guide/src/solve/trait-solving.md b/src/doc/rustc-dev-guide/src/solve/trait-solving.md index 345ee0b094e8f..c1eb1a94b9634 100644 --- a/src/doc/rustc-dev-guide/src/solve/trait-solving.md +++ b/src/doc/rustc-dev-guide/src/solve/trait-solving.md @@ -2,8 +2,7 @@ This chapter describes how trait solving works with the new WIP solver located in [`rustc_trait_selection/solve`][solve]. Feel free to also look at the docs for -[the current solver](../traits/resolution.md) and [the chalk solver](../traits/chalk.md) -can be found separately. +[the current solver](../traits/resolution.md) and [the chalk solver](../traits/chalk.md). ## Core concepts diff --git a/src/doc/rustc-dev-guide/src/test-implementation.md b/src/doc/rustc-dev-guide/src/test-implementation.md index bee783c0fa66c..e906dd29f25f5 100644 --- a/src/doc/rustc-dev-guide/src/test-implementation.md +++ b/src/doc/rustc-dev-guide/src/test-implementation.md @@ -83,7 +83,7 @@ with your hand-written one, it will not share a [Symbol][Symbol]. This technique prevents name collision during code generation and is the foundation of Rust's [`macro`] hygiene. -## Step 2: Harness Generation +## Step 2: Harness generation Now that our tests are accessible from the root of our crate, we need to do something with them using [`rustc_ast`][ast] generates a module like so: @@ -106,7 +106,7 @@ called [`test`][test] that is part of Rust core, that implements all of the runtime for testing. [`test`][test]'s interface is unstable, so the only stable way to interact with it is through the `#[test]` macro. -## Step 3: Test Object Generation +## Step 3: Test object generation If you've written tests in Rust before, you may be familiar with some of the optional attributes available on test functions. For example, a test can be diff --git a/src/doc/rustc-dev-guide/src/tests/best-practices.md b/src/doc/rustc-dev-guide/src/tests/best-practices.md index 6905ee13283a6..be00207e3fb93 100644 --- a/src/doc/rustc-dev-guide/src/tests/best-practices.md +++ b/src/doc/rustc-dev-guide/src/tests/best-practices.md @@ -70,6 +70,11 @@ related tests. > //! > //! Regression test for . > ``` +> +> One exception to this rule is [crashes tests]: there it is canonical that +> tests are named only after issue numbers because its purpose is to track +> snippets from which issues no longer ICE/crash, and they would either be +> removed or converted into proper ui/other tests in the fix PRs. ## Test organization @@ -175,6 +180,8 @@ See [compiletest directives] for a listing of directives. - For `ignore-*`/`needs-*`/`only-*` directives, unless extremely obvious, provide a brief remark on why the directive is needed. E.g. `"//@ ignore-wasi (wasi codegens the main symbol differently)"`. +- When using `//@ ignore-auxiliary`, specify the corresponding main test files, + e.g. ``//@ ignore-auxiliary (used by `./foo.rs`)``. ## FileCheck best practices @@ -192,3 +199,4 @@ See [LLVM FileCheck guide][FileCheck] for details. [compiletest directives]: ./directives.md [`run-make`]: ./compiletest.md#run-make-tests [FileCheck]: https://llvm.org/docs/CommandGuide/FileCheck.html +[crashes tests]: ./compiletest.md#crashes-tests diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index ae6adb678af14..825be11c82a97 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -133,29 +133,41 @@ There are several use-cases for try builds: Again, a working compiler build is needed for this, which can be produced by the [dist-x86_64-linux] CI job. - Run a specific CI job (e.g. Windows tests) on a PR, to quickly test if it - passes the test suite executed by that job. You can select which CI jobs will - be executed in the try build by adding up to 10 lines containing `try-job: - ` to the PR description. All such specified jobs will be executed - in the try build once the `@bors try` command is used on the PR. If no try - jobs are specified in this way, the jobs defined in the `try` section of - [`jobs.yml`] will be executed by default. + passes the test suite executed by that job. + +By default, if you send a comment with `@bors try`, the jobs defined in the `try` section of +[`jobs.yml`] will be executed. We call this mode a "fast try build". Such a try build +will not execute any tests, and it will allow compilation warnings. It is useful when you want to +get an optimized toolchain as fast as possible, for a crater run or performance benchmarks, +even if it might not be working fully correctly. + +If you want to run a custom CI job in a try build and make sure that it passes all tests and does +not produce any compilation warnings, you can select CI jobs to be executed by adding lines +containing `try-job: ` to the PR description. All such specified jobs will be executed +in the try build once the `@bors try` command is used on the PR. + +Each pattern can either be an exact name of a job or a glob pattern that matches multiple jobs, +for example `*msvc*` or `*-alt`. You can start at most 20 jobs in a single try build. When using +glob patterns, you might want to wrap them in backticks (`` ` ``) to avoid GitHub rendering +the pattern as Markdown. > **Using `try-job` PR description directives** > -> 1. Identify which set of try-jobs (max 10) you would like to exercise. You can +> 1. Identify which set of try-jobs you would like to exercise. You can > find the name of the CI jobs in [`jobs.yml`]. > -> 2. Amend PR description to include (usually at the end of the PR description) -> e.g. +> 2. Amend PR description to include a set of patterns (usually at the end +> of the PR description), for example: > > ```text > This PR fixes #123456. > > try-job: x86_64-msvc > try-job: test-various +> try-job: `*-alt` > ``` > -> Each `try-job` directive must be on its own line. +> Each `try-job` pattern must be on its own line. > > 3. Run the prescribed try jobs with `@bors try`. As aforementioned, this > requires the user to either (1) have `try` permissions or (2) be delegated @@ -172,6 +184,8 @@ their results can be seen [here](https://github.com/rust-lang-ci/rust/actions), although usually you will be notified of the result by a comment made by bors on the corresponding PR. +Note that if you start the default try job using `@bors try`, it will skip building several `dist` components and running post-optimization tests, to make the build duration shorter. If you want to execute the full build as it would happen before a merge, add an explicit `try-job` pattern with the name of the default try job (currently `dist-x86_64-linux`). + Multiple try builds can execute concurrently across different PRs.
    @@ -427,7 +441,7 @@ To learn more about the dashboard, see the [Datadog CI docs]. ## Determining the CI configuration -If you want to determine which `config.toml` settings are used in CI for a +If you want to determine which `bootstrap.toml` settings are used in CI for a particular job, it is probably easiest to just look at the build log. To do this: diff --git a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_clif.md b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_clif.md new file mode 100644 index 0000000000000..030ddd7dff571 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_clif.md @@ -0,0 +1,3 @@ +# Cranelift codegen backend tests + +TODO: please add some more information to this page. diff --git a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md new file mode 100644 index 0000000000000..4caf4c0e0eefa --- /dev/null +++ b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md @@ -0,0 +1,3 @@ +# GCC codegen backend tests + +TODO: please add some more information to this page. diff --git a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/intro.md b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/intro.md new file mode 100644 index 0000000000000..6bf46ddcd21a6 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/intro.md @@ -0,0 +1,13 @@ +# Codegen backend testing + +See also the [Code generation](../../backend/codegen.md) chapter. + +In addition to the primary LLVM codegen backend, the rust-lang/rust CI also runs tests of the [cranelift][cg_clif] and [GCC][cg_gcc] codegen backends in certain test jobs. + +For more details on the tests involved, see: + +- [Cranelift codegen backend tests](./cg_clif.md) +- [GCC codegen backend tests](./cg_gcc.md) + +[cg_clif]: https://github.com/rust-lang/rustc_codegen_cranelift +[cg_gcc]: https://github.com/rust-lang/rustc_codegen_gcc diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 459c082906eba..0ba078f0b49e7 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -74,8 +74,7 @@ The following test suites are available, with links for more information: ### General purpose test suite -[`run-make`](#run-make-tests) are general purpose tests using Rust programs (or -Makefiles (legacy)). +[`run-make`](#run-make-tests) are general purpose tests using Rust programs. ### Rustdoc test suites @@ -326,12 +325,8 @@ The tests in [`tests/codegen-units`] test the [monomorphization](../backend/monomorph.md) collector and CGU partitioning. These tests work by running `rustc` with a flag to print the result of the -monomorphization collection pass, and then special annotations in the file are -used to compare against that. - -Each test should be annotated with the `//@ -compile-flags:-Zprint-mono-items=VAL` directive with the appropriate `VAL` to -instruct `rustc` to print the monomorphization information. +monomorphization collection pass, i.e., `-Zprint-mono-items`, and then special +annotations in the file are used to compare against that. Then, the test should be annotated with comments of the form `//~ MONO_ITEM name` where `name` is the monomorphized string printed by rustc like `fn **Note on phasing out `Makefile`s** -> -> We are planning to migrate all existing Makefile-based `run-make` tests -> to Rust programs. You should not be adding new Makefile-based `run-make` -> tests. -> -> See . - The tests in [`tests/run-make`] are general-purpose tests using Rust *recipes*, which are small programs (`rmake.rs`) allowing arbitrary Rust code such as `rustc` invocations, and is supported by a [`run_make_support`] library. Using @@ -424,10 +411,9 @@ Compiletest directives like `//@ only-` or `//@ ignore-` are supported in `rmake.rs`, like in UI tests. However, revisions or building auxiliary via directives are not currently supported. -Two `run-make` tests are ported over to Rust recipes as examples: - -- -- +`rmake.rs` and `run-make-support` may *not* use any nightly/unstable features, +as they must be compilable by a stage 0 rustc that may be a beta or even stable +rustc. #### Quickly check if `rmake.rs` tests can be compiled @@ -481,20 +467,6 @@ Then add a corresponding entry to `"rust-analyzer.linkedProjects"` ], ``` -#### Using Makefiles (legacy) - -
    -You should avoid writing new Makefile-based `run-make` tests. -
    - -Each test should be in a separate directory with a `Makefile` indicating the -commands to run. - -There is a [`tools.mk`] Makefile which you can include which provides a bunch of -utilities to make it easier to run commands and compare outputs. Take a look at -some of the other tests for some examples on how to get started. - -[`tools.mk`]: https://github.com/rust-lang/rust/blob/master/tests/run-make/tools.mk [`tests/run-make`]: https://github.com/rust-lang/rust/tree/master/tests/run-make [`run_make_support`]: https://github.com/rust-lang/rust/tree/master/src/tools/run-make-support @@ -549,10 +521,10 @@ data into a human-readable code coverage report. Instrumented binaries need to be linked against the LLVM profiler runtime, so `coverage-run` tests are **automatically skipped** unless the profiler runtime -is enabled in `config.toml`: +is enabled in `bootstrap.toml`: ```toml -# config.toml +# bootstrap.toml [build] profiler = true ``` diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 00bb2bc4dbb1e..dae659e6317b4 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -6,10 +6,8 @@ FIXME(jieyouxu) completely revise this chapter. --> -Directives are special comments that tell compiletest how to build and interpret -a test. They must appear before the Rust source in the test. They may also -appear in `rmake.rs` or legacy Makefiles for [run-make -tests](compiletest.md#run-make-tests). +Directives are special comments that tell compiletest how to build and interpret a test. +They may also appear in `rmake.rs` [run-make tests](compiletest.md#run-make-tests). They are normally put after the short comment that explains the point of this test. Compiletest test suites use `//@` to signal that a comment is a directive. @@ -103,6 +101,7 @@ for more details. | `normalize-stdout` | Normalize actual stdout with a rule `"" -> ""` before comparing against snapshot | `ui`, `incremental` | `"" -> ""`, ``/`` is regex capture and replace syntax | | `dont-check-compiler-stderr` | Don't check actual compiler stderr vs stderr snapshot | `ui` | N/A | | `dont-check-compiler-stdout` | Don't check actual compiler stdout vs stdout snapshot | `ui` | N/A | +| `dont-require-annotations` | Don't require line annotations for the given diagnostic kind (`//~ KIND`) to be exhaustive | `ui`, `incremental` | `ERROR`, `WARN`, `NOTE`, `HELP`, `SUGGESTION` | | `run-rustfix` | Apply all suggestions via `rustfix`, snapshot fixed output, and check fixed output builds | `ui` | N/A | | `rustfix-only-machine-applicable` | `run-rustfix` but only machine-applicable suggestions | `ui` | N/A | | `exec-env` | Env var to set when executing a test | `ui`, `crashes` | `=` | @@ -122,10 +121,12 @@ for more details. These directives are used to ignore the test in some situations, which means the test won't be compiled or run. -* `ignore-X` where `X` is a target detail or stage will ignore the test - accordingly (see below) +* `ignore-X` where `X` is a target detail or other criteria on which to ignore the test (see below) * `only-X` is like `ignore-X`, but will *only* run the test on that target or stage +* `ignore-auxiliary` is intended for files that *participate* in one or more other + main test files but that `compiletest` should not try to build the file itself. + Please backlink to which main test is actually using the auxiliary file. * `ignore-test` always ignores the test. This can be used to temporarily disable a test if it is currently not working, but you want to keep it in tree to re-enable it later. @@ -142,7 +143,8 @@ Some examples of `X` in `ignore-X` or `only-X`: matches that target as well as the emscripten targets. - Pointer width: `32bit`, `64bit` - Endianness: `endian-big` -- Stage: `stage0`, `stage1`, `stage2` +- Stage: `stage1`, `stage2` +- Binary format: `elf` - Channel: `stable`, `beta` - When cross compiling: `cross-compile` - When [remote testing] is used: `remote` @@ -163,9 +165,9 @@ settings: stable support for `asm!` - `needs-profiler-runtime` — ignores the test if the profiler runtime was not enabled for the target - (`build.profiler = true` in rustc's `config.toml`) + (`build.profiler = true` in rustc's `bootstrap.toml`) - `needs-sanitizer-support` — ignores if the sanitizer support was not enabled - for the target (`sanitizers = true` in rustc's `config.toml`) + for the target (`sanitizers = true` in rustc's `bootstrap.toml`) - `needs-sanitizer-{address,hwaddress,leak,memory,thread}` — ignores if the corresponding sanitizer is not enabled for the target (AddressSanitizer, hardware-assisted AddressSanitizer, LeakSanitizer, MemorySanitizer or @@ -175,7 +177,7 @@ settings: flag, or running on fuchsia. - `needs-unwind` — ignores if the target does not support unwinding - `needs-rust-lld` — ignores if the rust lld support is not enabled (`rust.lld = - true` in `config.toml`) + true` in `bootstrap.toml`) - `needs-threads` — ignores if the target does not have threading support - `needs-subprocess` — ignores if the target does not have subprocess support - `needs-symlink` — ignores if the target does not support symlinks. This can be @@ -193,12 +195,16 @@ settings: specified atomic widths, e.g. the test with `//@ needs-target-has-atomic: 8, 16, ptr` will only run if it supports the comma-separated list of atomic widths. -- `needs-dynamic-linking` - ignores if target does not support dynamic linking +- `needs-dynamic-linking` — ignores if target does not support dynamic linking (which is orthogonal to it being unable to create `dylib` and `cdylib` crate types) +- `needs-crate-type` — ignores if target platform does not support one or more + of the comma-delimited list of specified crate types. For example, + `//@ needs-crate-type: cdylib, proc-macro` will cause the test to be ignored + on `wasm32-unknown-unknown` target because the target does not support the + `proc-macro` crate type. The following directives will check LLVM support: -- `no-system-llvm` — ignores if the system llvm is used - `exact-llvm-major-version: 19` — ignores if the llvm major version does not match the specified llvm major version. - `min-llvm-version: 13.0` — ignored if the LLVM version is less than the given @@ -221,8 +227,6 @@ The following directives will check LLVM support: [`aarch64-gnu-debug`]), which only runs a subset of `run-make` tests. Other tests with this directive will not run at all, which is usually not what you want. - - Notably, the [`aarch64-gnu-debug`] CI job *currently* only runs `run-make` - tests which additionally contain `clang` in their test name. See also [Debuginfo tests](compiletest.md#debuginfo-tests) for directives for ignoring debuggers. @@ -234,14 +238,14 @@ ignoring debuggers. ### Affecting how tests are built -| Directive | Explanation | Supported test suites | Possible values | -|---------------------|----------------------------------------------------------------------------------------------|---------------------------|------------------------------------------------------------------------------| -| `compile-flags` | Flags passed to `rustc` when building the test or aux file | All except for `run-make` | Any valid `rustc` flags, e.g. `-Awarnings -Dfoo`. Cannot be `-Cincremental`. | -| `edition` | Alias for `compile-flags: --edition=xxx` | All except for `run-make` | Any valid `--edition` value | -| `rustc-env` | Env var to set when running `rustc` | All except for `run-make` | `=` | -| `unset-rustc-env` | Env var to unset when running `rustc` | All except for `run-make` | Any env var name | -| `incremental` | Proper incremental support for tests outside of incremental test suite | `ui`, `crashes` | N/A | -| `no-prefer-dynamic` | Don't use `-C prefer-dynamic`, don't build as a dylib via a `--crate-type=dylib` preset flag | `ui`, `crashes` | N/A | +| Directive | Explanation | Supported test suites | Possible values | +|---------------------|----------------------------------------------------------------------------------------------|---------------------------|--------------------------------------------------------------------------------------------| +| `compile-flags` | Flags passed to `rustc` when building the test or aux file | All except for `run-make` | Any valid `rustc` flags, e.g. `-Awarnings -Dfoo`. Cannot be `-Cincremental` or `--edition` | +| `edition` | The edition used to build the test | All except for `run-make` | Any valid `--edition` value | +| `rustc-env` | Env var to set when running `rustc` | All except for `run-make` | `=` | +| `unset-rustc-env` | Env var to unset when running `rustc` | All except for `run-make` | Any env var name | +| `incremental` | Proper incremental support for tests outside of incremental test suite | `ui`, `crashes` | N/A | +| `no-prefer-dynamic` | Don't use `-C prefer-dynamic`, don't build as a dylib via a `--crate-type=dylib` preset flag | `ui`, `crashes` | N/A |
    Tests (outside of `run-make`) that want to use incremental tests not in the diff --git a/src/doc/rustc-dev-guide/src/tests/docker.md b/src/doc/rustc-dev-guide/src/tests/docker.md index a8a388ef90cfb..032da1ca1e8da 100644 --- a/src/doc/rustc-dev-guide/src/tests/docker.md +++ b/src/doc/rustc-dev-guide/src/tests/docker.md @@ -21,7 +21,7 @@ The [`src/ci/docker/run.sh`] script is used to build a specific Docker image, ru build Rust within the image, and either run tests or prepare a set of archives designed for distribution. The script will mount your local Rust source tree in read-only mode, and an `obj` directory in read-write mode. All the compiler artifacts will be stored in the `obj` directory. The shell will start out in the `obj`directory. From there, it will execute `../src/ci/run.sh` which starts the build as defined by the Docker image. You can run `src/ci/docker/run.sh ` directly. A few important notes regarding the `run.sh` script: -- When executed on CI, the script expects that all submodules are checked out. If some submodule that is accessed by the job is not available, the build will result in an error. You should thus make sure that you have all required submodules checked out locally. You can either do that manually through git, or set `submodules = true` in your `config.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). +- When executed on CI, the script expects that all submodules are checked out. If some submodule that is accessed by the job is not available, the build will result in an error. You should thus make sure that you have all required submodules checked out locally. You can either do that manually through git, or set `submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). - `` corresponds to a single directory located in one of the `src/ci/docker/host-*` directories. Note that image name does not necessarily correspond to a job name, as some jobs execute the same image, but with different environment variables or Docker build arguments (this is a part of the complexity that makes it difficult to run CI jobs locally). - If you are executing a "dist" job (job beginning with `dist-`), you should set the `DEPLOY=1` environment variable. - If you are executing an "alternative dist" job (job beginning with `dist-` and ending with `-alt`), you should set the `DEPLOY_ALT=1` environment variable. diff --git a/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/fuchsia.md b/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/fuchsia.md new file mode 100644 index 0000000000000..b19d94d6ff734 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/fuchsia.md @@ -0,0 +1,177 @@ +# Fuchsia integration tests + +[Fuchsia](https://fuchsia.dev) is an open-source operating system with about 2 +million lines of Rust code.[^loc] It has caught a large number of [regressions] +in the past and was subsequently included in CI. + +## What to do if the Fuchsia job breaks? + +Please contact the [fuchsia][fuchsia-ping] ping group and ask them for help. + +```text +@rustbot ping fuchsia +``` + +## Building Fuchsia in CI + +Fuchsia builds as part of the suite of bors tests that run before a pull request +is merged. + +If you are worried that a pull request might break the Fuchsia builder and want +to test it out before submitting it to the bors queue, simply add this line to +your PR description: + +> try-job: x86_64-fuchsia + +Then when you `@bors try` it will pick the job that builds Fuchsia. + +## Building Fuchsia locally + +Because Fuchsia uses languages other than Rust, it does not use Cargo as a build +system. It also requires the toolchain build to be configured in a [certain +way][build-toolchain]. + +The recommended way to build Fuchsia is to use the Docker scripts that check out +and run a Fuchsia build for you. If you've run Docker tests before, you can +simply run this command from your Rust checkout to download and build Fuchsia +using your local Rust toolchain. + +``` +src/ci/docker/run.sh x86_64-fuchsia +``` + +See the [Testing with Docker](../docker.md) chapter for more details on how to run +and debug jobs with Docker. + +Note that a Fuchsia checkout is *large* – as of this writing, a checkout and +build takes 46G of space – and as you might imagine, it takes a while to +complete. + +### Modifying the Fuchsia checkout + +The main reason you would want to build Fuchsia locally is because you need to +investigate a regression. After running a Docker build, you'll find the Fuchsia +checkout inside the `obj/fuchsia` directory of your Rust checkout. If you +modify the `KEEP_CHECKOUT` line in the [build-fuchsia.sh] script to +`KEEP_CHECKOUT=1`, you can change the checkout as needed and rerun the build +command above. This will reuse all the build results from before. + +You can find more options to customize the Fuchsia checkout in the +[build-fuchsia.sh] script. + +### Customizing the Fuchsia build + +You can find more info about the options used to build Fuchsia in Rust CI in the +[build_fuchsia_from_rust_ci.sh] script invoked by [build-fuchsia.sh]. + +The Fuchsia build system uses [GN], a metabuild system that generates [Ninja] +files and then hands off the work of running the build to Ninja. + +Fuchsia developers use `fx` to run builds and perform other development tasks. +This tool is located in `.jiri_root/bin` of the Fuchsia checkout; you may need +to add this to your `$PATH` for some workflows. + +There are a few `fx` subcommands that are relevant, including: + +- `fx set` accepts build arguments, writes them to `out/default/args.gn`, and + runs GN. +- `fx build` builds the Fuchsia project using Ninja. It will automatically pick + up changes to build arguments and rerun GN. By default it builds everything, + but it also accepts target paths to build specific targets (see below). +- `fx clippy` runs Clippy on specific Rust targets (or all of them). We use this + in the Rust CI build to avoid running codegen on most Rust targets. Underneath + it invokes Ninja, just like `fx build`. The clippy results are saved in json + files inside the build output directory before being printed. + +#### Target paths + +GN uses paths like the following to identify build targets: + +``` +//src/starnix/kernel:starnix_core +``` + +The initial `//` means the root of the checkout, and the remaining slashes are +directory names. The string after `:` is the _target name_ of a target defined +in the `BUILD.gn` file of that directory. + +The target name can be omitted if it is the same as the directory name. In other +words, `//src/starnix/kernel` is the same as `//src/starnix/kernel:kernel`. + +These target paths are used inside `BUILD.gn` files to reference dependencies, +and can also be used in `fx build`. + +#### Modifying compiler flags + +You can put custom compiler flags inside a GN `config` that is added to a +target. As a simple example: + +``` +config("everybody_loops") { + rustflags = [ "-Zeverybody-loops" ] +} + +rustc_binary("example") { + crate_root = "src/bin.rs" + # ...existing keys here... + configs += [ ":everybody_loops" ] +} +``` + +This will add the flag `-Zeverybody-loops` to rustc when building the `example` +target. Note that you can also use [`public_configs`] for a config to be added +to every target that depends on that target. + +If you want to add a flag to every Rust target in the build, you can add +rustflags to the [`//build/config:compiler`] config or to the OS-specific +configs referenced in that file. Note that `cflags` and `ldflags` are ignored on +Rust targets. + +#### Running ninja and rustc commands directly + +Going down one layer, `fx build` invokes `ninja`, which in turn eventually +invokes `rustc`. All build actions are run inside the out directory, which is +usually `out/default` inside the Fuchsia checkout. + +You can get ninja to print the actual command it invokes by forcing that command +to fail, e.g. by adding a syntax error to one of the source files of the target. +Once you have the command, you can run it from inside the output directory. + +After changing the toolchain itself, the build setting `rustc_version_string` in +`out/default/args.gn` needs to be changed so that `fx build` or `ninja` will +rebuild all the Rust targets. This can be done in a text editor and the contents +of the string do not matter, as long as it changes from one build to the next. +[build_fuchsia_from_rust_ci.sh] does this for you by hashing the toolchain +directory. + +The Fuchsia website has more detailed documentation of the [build system]. + +#### Other tips and tricks + +When using `build_fuchsia_from_rust_ci.sh` you can comment out the `fx set` +command after the initial run so it won't rerun GN each time. If you do this you +can also comment out the version_string line to save a couple seconds. + +`export NINJA_PERSISTENT_MODE=1` to get faster ninja startup times after the +initial build. + +## Fuchsia target support + +To learn more about Fuchsia target support, see the Fuchsia chapter in [the +rustc book][platform-support]. + +[regressions]: https://gist.github.com/tmandry/7103eba4bd6a6fb0c439b5a90ae355fa +[build-toolchain]: https://fuchsia.dev/fuchsia-src/development/build/rust_toolchain +[build-fuchsia.sh]: https://github.com/rust-lang/rust/blob/221e2741c39515a5de6da42d8c76ee1e132c2c74/src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh +[build_fuchsia_from_rust_ci.sh]: https://cs.opensource.google/fuchsia/fuchsia/+/main:scripts/rust/build_fuchsia_from_rust_ci.sh?q=build_fuchsia_from_rust_ci&ss=fuchsia +[platform-support]: https://doc.rust-lang.org/nightly/rustc/platform-support/fuchsia.html +[GN]: https://gn.googlesource.com/gn/+/main#gn +[Ninja]: https://ninja-build.org/ +[`public_configs`]: https://gn.googlesource.com/gn/+/main/docs/reference.md#var_public_configs +[`//build/config:compiler`]: https://cs.opensource.google/fuchsia/fuchsia/+/main:build/config/BUILD.gn;l=121;drc=c26c473bef93b33117ae417893118907a026fec7 +[build system]: https://fuchsia.dev/fuchsia-src/development/build/build_system +[fuchsia-ping]: ../../notification-groups/fuchsia.md + +[^loc]: As of June 2024, Fuchsia had about 2 million lines of first-party Rust +code and a roughly equal amount of third-party code, as counted by tokei +(excluding comments and blanks). diff --git a/src/doc/rustc-dev-guide/src/tests/rust-for-linux.md b/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/rust-for-linux.md similarity index 95% rename from src/doc/rustc-dev-guide/src/tests/rust-for-linux.md rename to src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/rust-for-linux.md index c674d1575b781..d549ec6fca529 100644 --- a/src/doc/rustc-dev-guide/src/tests/rust-for-linux.md +++ b/src/doc/rustc-dev-guide/src/tests/ecosystem-test-jobs/rust-for-linux.md @@ -3,26 +3,7 @@ [Rust for Linux](https://rust-for-linux.com/) (RfL) is an effort for adding support for the Rust programming language into the Linux kernel. -## Building Rust for Linux in CI - -Rust for Linux builds as part of the suite of bors tests that run before a pull -request is merged. - -The workflow builds a stage1 sysroot of the Rust compiler, downloads the Linux -kernel, and tries to compile several Rust for Linux drivers and examples using -this sysroot. RfL uses several unstable compiler/language features, therefore -this workflow notifies us if a given compiler change would break it. - -If you are worried that a pull request might break the Rust for Linux builder -and want to test it out before submitting it to the bors queue, simply add this -line to your PR description: - -> try-job: x86_64-rust-for-linux - -Then when you `@bors try` it will pick the job that builds the Rust for Linux -integration. - -## What to do in case of failure +## What to do if the Rust for Linux job breaks? If a PR breaks the Rust for Linux CI job, then: @@ -48,4 +29,23 @@ ping group to ask for help: @rustbot ping rfl ``` -[rfl-ping]: ../notification-groups/rust-for-linux.md +## Building Rust for Linux in CI + +Rust for Linux builds as part of the suite of bors tests that run before a pull +request is merged. + +The workflow builds a stage1 sysroot of the Rust compiler, downloads the Linux +kernel, and tries to compile several Rust for Linux drivers and examples using +this sysroot. RfL uses several unstable compiler/language features, therefore +this workflow notifies us if a given compiler change would break it. + +If you are worried that a pull request might break the Rust for Linux builder +and want to test it out before submitting it to the bors queue, simply add this +line to your PR description: + +> try-job: x86_64-rust-for-linux + +Then when you `@bors try` it will pick the job that builds the Rust for Linux +integration. + +[rfl-ping]: ../../notification-groups/rust-for-linux.md diff --git a/src/doc/rustc-dev-guide/src/tests/ecosystem.md b/src/doc/rustc-dev-guide/src/tests/ecosystem.md index 083601404255b..eee07dd079bbf 100644 --- a/src/doc/rustc-dev-guide/src/tests/ecosystem.md +++ b/src/doc/rustc-dev-guide/src/tests/ecosystem.md @@ -15,14 +15,16 @@ CI. See the [Crater chapter](crater.md) for more details. `cargotest` is a small tool which runs `cargo test` on a few sample projects (such as `servo`, `ripgrep`, `tokei`, etc.). This runs as part of CI and ensures -there aren't any significant regressions. +there aren't any significant regressions: -> Example: `./x test src/tools/cargotest` +```console +./x test src/tools/cargotest +``` ### Large OSS Project builders We have CI jobs that build large open-source Rust projects that are used as regression tests in CI. Our integration jobs build the following projects: -- [Fuchsia](fuchsia.md) -- [Rust for Linux](rust-for-linux.md) +- [Fuchsia](./ecosystem-test-jobs/fuchsia.md) +- [Rust for Linux](./ecosystem-test-jobs/rust-for-linux.md) diff --git a/src/doc/rustc-dev-guide/src/tests/fuchsia.md b/src/doc/rustc-dev-guide/src/tests/fuchsia.md deleted file mode 100644 index e96290b921529..0000000000000 --- a/src/doc/rustc-dev-guide/src/tests/fuchsia.md +++ /dev/null @@ -1,168 +0,0 @@ -# Fuchsia integration tests - -[Fuchsia](https://fuchsia.dev) is an open-source operating system with about 2 -million lines of Rust code.[^loc] It has caught a large number of [regressions] -in the past and was subsequently included in CI. - -## Building Fuchsia in CI - -Fuchsia builds as part of the suite of bors tests that run before a pull request -is merged. - -If you are worried that a pull request might break the Fuchsia builder and want -to test it out before submitting it to the bors queue, simply add this line to -your PR description: - -> try-job: x86_64-fuchsia - -Then when you `@bors try` it will pick the job that builds Fuchsia. - -## Building Fuchsia locally - -Because Fuchsia uses languages other than Rust, it does not use Cargo as a build -system. It also requires the toolchain build to be configured in a [certain -way][build-toolchain]. - -The recommended way to build Fuchsia is to use the Docker scripts that check out -and run a Fuchsia build for you. If you've run Docker tests before, you can -simply run this command from your Rust checkout to download and build Fuchsia -using your local Rust toolchain. - -``` -src/ci/docker/run.sh x86_64-fuchsia -``` - -See the [Testing with Docker](docker.md) chapter for more details on how to run -and debug jobs with Docker. - -Note that a Fuchsia checkout is *large* – as of this writing, a checkout and -build takes 46G of space – and as you might imagine, it takes a while to -complete. - -### Modifying the Fuchsia checkout - -The main reason you would want to build Fuchsia locally is because you need to -investigate a regression. After running a Docker build, you'll find the Fuchsia -checkout inside the `obj/fuchsia` directory of your Rust checkout. If you -modify the `KEEP_CHECKOUT` line in the [build-fuchsia.sh] script to -`KEEP_CHECKOUT=1`, you can change the checkout as needed and rerun the build -command above. This will reuse all the build results from before. - -You can find more options to customize the Fuchsia checkout in the -[build-fuchsia.sh] script. - -### Customizing the Fuchsia build - -You can find more info about the options used to build Fuchsia in Rust CI in the -[build_fuchsia_from_rust_ci.sh] script invoked by [build-fuchsia.sh]. - -The Fuchsia build system uses [GN], a metabuild system that generates [Ninja] -files and then hands off the work of running the build to Ninja. - -Fuchsia developers use `fx` to run builds and perform other development tasks. -This tool is located in `.jiri_root/bin` of the Fuchsia checkout; you may need -to add this to your `$PATH` for some workflows. - -There are a few `fx` subcommands that are relevant, including: - -- `fx set` accepts build arguments, writes them to `out/default/args.gn`, and - runs GN. -- `fx build` builds the Fuchsia project using Ninja. It will automatically pick - up changes to build arguments and rerun GN. By default it builds everything, - but it also accepts target paths to build specific targets (see below). -- `fx clippy` runs Clippy on specific Rust targets (or all of them). We use this - in the Rust CI build to avoid running codegen on most Rust targets. Underneath - it invokes Ninja, just like `fx build`. The clippy results are saved in json - files inside the build output directory before being printed. - -#### Target paths - -GN uses paths like the following to identify build targets: - -``` -//src/starnix/kernel:starnix_core -``` - -The initial `//` means the root of the checkout, and the remaining slashes are -directory names. The string after `:` is the _target name_ of a target defined -in the `BUILD.gn` file of that directory. - -The target name can be omitted if it is the same as the directory name. In other -words, `//src/starnix/kernel` is the same as `//src/starnix/kernel:kernel`. - -These target paths are used inside `BUILD.gn` files to reference dependencies, -and can also be used in `fx build`. - -#### Modifying compiler flags - -You can put custom compiler flags inside a GN `config` that is added to a -target. As a simple example: - -``` -config("everybody_loops") { - rustflags = [ "-Zeverybody-loops" ] -} - -rustc_binary("example") { - crate_root = "src/bin.rs" - # ...existing keys here... - configs += [ ":everybody_loops" ] -} -``` - -This will add the flag `-Zeverybody-loops` to rustc when building the `example` -target. Note that you can also use [`public_configs`] for a config to be added -to every target that depends on that target. - -If you want to add a flag to every Rust target in the build, you can add -rustflags to the [`//build/config:compiler`] config or to the OS-specific -configs referenced in that file. Note that `cflags` and `ldflags` are ignored on -Rust targets. - -#### Running ninja and rustc commands directly - -Going down one layer, `fx build` invokes `ninja`, which in turn eventually -invokes `rustc`. All build actions are run inside the out directory, which is -usually `out/default` inside the Fuchsia checkout. - -You can get ninja to print the actual command it invokes by forcing that command -to fail, e.g. by adding a syntax error to one of the source files of the target. -Once you have the command, you can run it from inside the output directory. - -After changing the toolchain itself, the build setting `rustc_version_string` in -`out/default/args.gn` needs to be changed so that `fx build` or `ninja` will -rebuild all the Rust targets. This can be done in a text editor and the contents -of the string do not matter, as long as it changes from one build to the next. -[build_fuchsia_from_rust_ci.sh] does this for you by hashing the toolchain -directory. - -The Fuchsia website has more detailed documentation of the [build system]. - -#### Other tips and tricks - -When using `build_fuchsia_from_rust_ci.sh` you can comment out the `fx set` -command after the initial run so it won't rerun GN each time. If you do this you -can also comment out the version_string line to save a couple seconds. - -`export NINJA_PERSISTENT_MODE=1` to get faster ninja startup times after the -initial build. - -## Fuchsia target support - -To learn more about Fuchsia target support, see the Fuchsia chapter in [the -rustc book][platform-support]. - -[regressions]: https://gist.github.com/tmandry/7103eba4bd6a6fb0c439b5a90ae355fa -[build-toolchain]: https://fuchsia.dev/fuchsia-src/development/build/rust_toolchain -[build-fuchsia.sh]: https://github.com/rust-lang/rust/blob/221e2741c39515a5de6da42d8c76ee1e132c2c74/src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh -[build_fuchsia_from_rust_ci.sh]: https://cs.opensource.google/fuchsia/fuchsia/+/main:scripts/rust/build_fuchsia_from_rust_ci.sh?q=build_fuchsia_from_rust_ci&ss=fuchsia -[platform-support]: https://doc.rust-lang.org/nightly/rustc/platform-support/fuchsia.html -[GN]: https://gn.googlesource.com/gn/+/main#gn -[Ninja]: https://ninja-build.org/ -[`public_configs`]: https://gn.googlesource.com/gn/+/main/docs/reference.md#var_public_configs -[`//build/config:compiler`]: https://cs.opensource.google/fuchsia/fuchsia/+/main:build/config/BUILD.gn;l=121;drc=c26c473bef93b33117ae417893118907a026fec7 -[build system]: https://fuchsia.dev/fuchsia-src/development/build/build_system - -[^loc]: As of June 2024, Fuchsia had about 2 million lines of first-party Rust -code and a roughly equal amount of third-party code, as counted by tokei -(excluding comments and blanks). diff --git a/src/doc/rustc-dev-guide/src/tests/intro.md b/src/doc/rustc-dev-guide/src/tests/intro.md index ba44a969bf950..c55d60f4a5c7b 100644 --- a/src/doc/rustc-dev-guide/src/tests/intro.md +++ b/src/doc/rustc-dev-guide/src/tests/intro.md @@ -38,7 +38,7 @@ directory, and `x` will essentially run `cargo test` on that package. Examples: | Command | Description | -| ----------------------------------------- | ------------------------------------- | +|-------------------------------------------|---------------------------------------| | `./x test library/std` | Runs tests on `std` only | | `./x test library/core` | Runs tests on `core` only | | `./x test compiler/rustc_data_structures` | Runs tests on `rustc_data_structures` | @@ -86,7 +86,7 @@ above. Examples: | Command | Description | -| ----------------------- | ------------------------------------------------------------------ | +|-------------------------|--------------------------------------------------------------------| | `./x fmt --check` | Checks formatting and exits with an error if formatting is needed. | | `./x fmt` | Runs rustfmt across the entire codebase. | | `./x test tidy --bless` | First runs rustfmt to format the codebase, then runs tidy checks. | @@ -102,11 +102,12 @@ by passing a path to a book to `./x test`. ### Documentation link checker -Links across all documentation is validated with a link checker tool. +Links across all documentation is validated with a link checker tool, +and it can be invoked so: -> Example: `./x test src/tools/linkchecker` - -> Example: `./x test linkchecker` +```console +./x test linkchecker +``` This requires building all of the documentation, which might take a while. @@ -155,6 +156,10 @@ chapter](ecosystem.md) for more details. A separate infrastructure is used for testing and tracking performance of the compiler. See the [Performance testing chapter](perf.md) for more details. +### Codegen backend testing + +See [Codegen backend testing](./codegen-backend-tests/intro.md). + ## Miscellaneous information There are some other useful testing-related info at [Misc info](misc.md). diff --git a/src/doc/rustc-dev-guide/src/tests/minicore.md b/src/doc/rustc-dev-guide/src/tests/minicore.md index e4853b6d40e35..507b259e0275d 100644 --- a/src/doc/rustc-dev-guide/src/tests/minicore.md +++ b/src/doc/rustc-dev-guide/src/tests/minicore.md @@ -6,25 +6,37 @@ ui/codegen/assembly test suites. It provides `core` stubs for tests that need to build for cross-compiled targets but do not need/want to run. +
    +Please note that [`minicore`] is only intended for `core` items, and explicitly +**not** `std` or `alloc` items because `core` items are applicable to a wider +range of tests. +
    + A test can use [`minicore`] by specifying the `//@ add-core-stubs` directive. Then, mark the test with `#![feature(no_core)]` + `#![no_std]` + `#![no_core]`. Due to Edition 2015 extern prelude rules, you will probably need to declare `minicore` as an extern crate. +## Implied compiler flags + Due to the `no_std` + `no_core` nature of these tests, `//@ add-core-stubs` implies and requires that the test will be built with `-C panic=abort`. -Unwinding panics are not supported. +**Unwinding panics are not supported.** + +Tests will also be built with `-C force-unwind-tables=yes` to preserve CFI +directives in assembly tests. + +TL;DR: `//@ add-core-stubs` implies two compiler flags: + +1. `-C panic=abort` +2. `-C force-unwind-tables=yes` + +## Adding more `core` stubs If you find a `core` item to be missing from the [`minicore`] stub, consider adding it to the test auxiliary if it's likely to be used or is already needed by more than one test. -
    -Please note that [`minicore`] is only intended for `core` items, and explicitly -**not** `std` or `alloc` items because `core` items are applicable to a wider -range of tests. -
    - ## Example codegen test that uses `minicore` ```rust,no_run diff --git a/src/doc/rustc-dev-guide/src/tests/running.md b/src/doc/rustc-dev-guide/src/tests/running.md index 6ce65092389bd..73c38736812a6 100644 --- a/src/doc/rustc-dev-guide/src/tests/running.md +++ b/src/doc/rustc-dev-guide/src/tests/running.md @@ -18,14 +18,14 @@ a subset of test collections, and merge queue CI will exercise all of the test collection.
    -```bash +```text ./x test ``` The test results are cached and previously successful tests are `ignored` during testing. The stdout/stderr contents as well as a timestamp file for every test -can be found under `build//test/` for the given -``. To force-rerun a test (e.g. in case the test runner fails to +can be found under `build//test/` for the given +``. To force-rerun a test (e.g. in case the test runner fails to notice a change) you can use the `--force-rerun` CLI option. > **Note on requirements of external dependencies** @@ -45,22 +45,22 @@ tests. For example, a good "smoke test" that can be used after modifying rustc to see if things are generally working correctly would be to exercise the `ui` test suite ([`tests/ui`]): -```bash +```text ./x test tests/ui ``` -This will run the `ui` test suite. Of course, the choice of test suites is +Of course, the choice of test suites is somewhat arbitrary, and may not suit the task you are doing. For example, if you are hacking on debuginfo, you may be better off with the debuginfo test suite: -```bash +```text ./x test tests/debuginfo ``` If you only need to test a specific subdirectory of tests for any given test suite, you can pass that directory as a filter to `./x test`: -```bash +```text ./x test tests/ui/const-generics ``` @@ -73,7 +73,7 @@ suite, you can pass that directory as a filter to `./x test`: Likewise, you can test a single file by passing its path: -```bash +```text ./x test tests/ui/const-generics/const-test.rs ``` @@ -81,19 +81,19 @@ Likewise, you can test a single file by passing its path: have to use the `--test-args` argument as described [below](#running-an-individual-test). -```bash +```text ./x test src/tools/miri --test-args tests/fail/uninit/padding-enum.rs ``` ### Run only the tidy script -```bash +```text ./x test tidy ``` ### Run tests on the standard library -```bash +```text ./x test --stage 0 library/std ``` @@ -102,18 +102,18 @@ crates, you have to specify those explicitly. ### Run the tidy script and tests on the standard library -```bash +```text ./x test --stage 0 tidy library/std ``` ### Run tests on the standard library using a stage 1 compiler -```bash +```text ./x test --stage 1 library/std ``` -By listing which test suites you want to run you avoid having to run tests for -components you did not change at all. +By listing which test suites you want to run, +you avoid having to run tests for components you did not change at all.
    Note that bors only runs the tests with the full stage 2 build; therefore, while @@ -122,7 +122,7 @@ the tests **usually** work fine with stage 1, there are some limitations. ### Run all tests using a stage 2 compiler -```bash +```text ./x test --stage 2 ``` @@ -134,13 +134,13 @@ You almost never need to do this; CI will run these tests for you. You may want to run unit tests on a specific file with following: -```bash +```text ./x test compiler/rustc_data_structures/src/thin_vec/tests.rs ``` But unfortunately, it's impossible. You should invoke the following instead: -```bash +```text ./x test compiler/rustc_data_structures/ --test-args thin_vec ``` @@ -151,7 +151,7 @@ often the test they are trying to fix. As mentioned earlier, you may pass the full file path to achieve this, or alternatively one may invoke `x` with the `--test-args` option: -```bash +```text ./x test tests/ui --test-args issue-1234 ``` @@ -172,25 +172,27 @@ additional arguments to the compiler when building the tests. ## Editing and updating the reference files If you have changed the compiler's output intentionally, or you are making a new -test, you can pass `--bless` to the test subcommand. E.g. if some tests in -`tests/ui` are failing, you can run +test, you can pass `--bless` to the test subcommand. + +As an example, +if some tests in `tests/ui` are failing, you can run this command: ```text ./x test tests/ui --bless ``` -to automatically adjust the `.stderr`, `.stdout` or `.fixed` files of -all tests. Of course you can also target just specific tests with the -`--test-args your_test_name` flag, just like when running the tests. +It automatically adjusts the `.stderr`, `.stdout`, or `.fixed` files of all `test/ui` tests. +Of course you can also target just specific tests with the `--test-args your_test_name` flag, +just like when running the tests without the `--bless` flag. ## Configuring test running There are a few options for running tests: -* `config.toml` has the `rust.verbose-tests` option. If `false`, each test will +* `bootstrap.toml` has the `rust.verbose-tests` option. If `false`, each test will print a single dot (the default). If `true`, the name of every test will be printed. This is equivalent to the `--quiet` option in the [Rust test - harness](https://doc.rust-lang.org/rustc/tests/) + harness](https://doc.rust-lang.org/rustc/tests/). * The environment variable `RUST_TEST_THREADS` can be set to the number of concurrent threads to use for testing. @@ -201,7 +203,7 @@ When `--pass $mode` is passed, these tests will be forced to run under the given `$mode` unless the directive `//@ ignore-pass` exists in the test file. For example, you can run all the tests in `tests/ui` as `check-pass`: -```bash +```text ./x test tests/ui --pass check ``` @@ -217,7 +219,7 @@ first look for expected output in `foo.polonius.stderr`, falling back to the usual `foo.stderr` if not found. The following will run the UI test suite in Polonius mode: -```bash +```text ./x test tests/ui --compare-mode=polonius ``` @@ -230,7 +232,7 @@ just `.rs` files, so after [creating a rustup toolchain](../building/how-to-build-and-run.md#creating-a-rustup-toolchain), you can do something like: -```bash +```text rustc +stage1 tests/ui/issue-1234.rs ``` @@ -238,30 +240,6 @@ This is much faster, but doesn't always work. For example, some tests include directives that specify specific compiler flags, or which rely on other crates, and they may not run the same without those options. -## Running `run-make` tests - -### Windows - -Running the `run-make` test suite on Windows is a currently bit more involved. -There are numerous prerequisites and environmental requirements: - -- Install msys2: -- Specify `MSYS2_PATH_TYPE=inherit` in `msys2.ini` in the msys2 installation directory, run the - following with `MSYS2 MSYS`: - - `pacman -Syuu` - - `pacman -S make` - - `pacman -S diffutils` - - `pacman -S binutils` - - `./x test run-make` (`./x test tests/run-make` doesn't work) - -There is [on-going work][port-run-make] to not rely on `Makefile`s in the -run-make test suite. Once this work is completed, you can run the entire -`run-make` test suite on native Windows inside `cmd` or `PowerShell` without -needing to install and use MSYS2. As of Oct 2024, it is -already possible to run the vast majority of the `run-make` test suite outside -of MSYS2, but there will be failures for the tests that still use `Makefile`s -due to not finding `make`. - ## Running tests on a remote machine Tests may be run on a remote machine (e.g. to test builds for a different @@ -274,7 +252,7 @@ execution* so be careful where it is used. To do this, first build `remote-test-server` for the remote machine, e.g. for RISC-V -```sh +```text ./x build src/tools/remote-test-server --target riscv64gc-unknown-linux-gnu ``` @@ -286,7 +264,7 @@ On the remote machine, run the `remote-test-server` with the `--bind 0.0.0.0:12345` flag (and optionally `-v` for verbose output). Output should look like this: -```sh +```text $ ./remote-test-server -v --bind 0.0.0.0:12345 starting test server listening on 0.0.0.0:12345! @@ -300,7 +278,7 @@ restrictive IP address when binding. You can test if the `remote-test-server` is working by connecting to it and sending `ping\n`. It should reply `pong`: -```sh +```text $ nc $REMOTE_IP 12345 ping pong @@ -310,7 +288,7 @@ To run tests using the remote runner, set the `TEST_DEVICE_ADDR` environment variable then use `x` as usual. For example, to run `ui` tests for a RISC-V machine with the IP address `1.2.3.4` use -```sh +```text export TEST_DEVICE_ADDR="1.2.3.4:12345" ./x test tests/ui --target riscv64gc-unknown-linux-gnu ``` @@ -318,7 +296,7 @@ export TEST_DEVICE_ADDR="1.2.3.4:12345" If `remote-test-server` was run with the verbose flag, output on the test machine may look something like -``` +```text [...] run "/tmp/work/test1007/a" run "/tmp/work/test1008/a" @@ -375,7 +353,7 @@ coordinate running tests (see [src/bootstrap/src/core/build_steps/test.rs]). First thing to know is that it only supports linux x86_64 at the moment. We will extend its support later on. -You need to update `codegen-backends` value in your `config.toml` file in the +You need to update `codegen-backends` value in your `bootstrap.toml` file in the `[rust]` section and add "gcc" in the array: ```toml @@ -384,21 +362,21 @@ codegen-backends = ["llvm", "gcc"] Then you need to install libgccjit 12. For example with `apt`: -```bash -$ apt install libgccjit-12-dev +```text +apt install libgccjit-12-dev ``` Now you can run the following command: -```bash -$ ./x test compiler/rustc_codegen_gcc/ +```text +./x test compiler/rustc_codegen_gcc/ ``` If it cannot find the `.so` library (if you installed it with `apt` for example), you need to pass the library file path with `LIBRARY_PATH`: -```bash -$ LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/ ./x test compiler/rustc_codegen_gcc/ +```text +LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/ ./x test compiler/rustc_codegen_gcc/ ``` If you encounter bugs or problems, don't hesitate to open issues on the @@ -406,4 +384,3 @@ If you encounter bugs or problems, don't hesitate to open issues on the repository](https://github.com/rust-lang/rustc_codegen_gcc/). [`tests/ui`]: https://github.com/rust-lang/rust/tree/master/tests/ui -[port-run-make]: https://github.com/rust-lang/rust/issues/121876 diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index c8536b0045cfd..721d20b65c5a7 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -202,6 +202,12 @@ several ways to match the message with the line (see the examples below): * `~|`: Associates the error level and message with the *same* line as the *previous comment*. This is more convenient than using multiple carets when there are multiple messages associated with the same line. +* `~v`: Associates the error level and message with the *next* error + annotation line. Each symbol (`v`) that you add adds a line to this, so `~vvv` + is three lines below the error annotation line. +* `~?`: Used to match error levels and messages with errors not having line + information. These can be placed on any line in the test file, but are + conventionally placed at the end. Example: @@ -270,10 +276,34 @@ fn main() { //~| ERROR this pattern has 1 field, but the corresponding tuple struct has 3 fields [E0023] ``` +#### Positioned above error line + +Use the `//~v` idiom with number of v's in the string to indicate the number +of lines below. This is typically used in lexer or parser tests matching on errors like unclosed +delimiter or unclosed literal happening at the end of file. + +```rust,ignore +// ignore-tidy-trailing-newlines +//~v ERROR this file contains an unclosed delimiter +fn main((ؼ +``` + +#### Error without line information + +Use `//~?` to match an error without line information. +`//~?` is precise and will not match errors if their line information is available. +It should be preferred to using `error-pattern`, which is imprecise and non-exhaustive. + +```rust,ignore +//@ compile-flags: --print yyyy + +//~? ERROR unknown print request: `yyyy` +``` + ### `error-pattern` -The `error-pattern` [directive](directives.md) can be used for messages that don't -have a specific span. +The `error-pattern` [directive](directives.md) can be used for runtime messages, which don't +have a specific span, or in exceptional cases, for compile time messages. Let's think about this test: @@ -286,8 +316,8 @@ fn main() { } ``` -We want to ensure this shows "index out of bounds" but we cannot use the `ERROR` -annotation since the error doesn't have any span. Then it's time to use the +We want to ensure this shows "index out of bounds", but we cannot use the `ERROR` +annotation since the runtime error doesn't have any span. Then it's time to use the `error-pattern` directive: ```rust,ignore @@ -300,22 +330,53 @@ fn main() { } ``` -But for strict testing, try to use the `ERROR` annotation as much as possible. +Use of `error-pattern` is not recommended in general. + +For strict testing of compile time output, try to use the line annotations `//~` as much as +possible, including `//~?` annotations for diagnostics without spans. + +If the compile time output is target dependent or too verbose, use directive +`//@ dont-require-annotations: ` to make the line annotation checking +non-exhaustive. +Some of the compiler messages can stay uncovered by annotations in this mode. + +For checking runtime output, `//@ check-run-results` may be preferable. + +Only use `error-pattern` if none of the above works. + +Line annotations `//~` and `error-pattern` are compatible and can be used in the same test. -### Error levels +### Diagnostic kinds (error levels) -The error levels that you can have are: +The diagnostic kinds that you can have are: - `ERROR` -- `WARN` or `WARNING` +- `WARN` (or `WARNING`) - `NOTE` -- `HELP` and `SUGGESTION` +- `HELP` +- `SUGGESTION` +- `RAW` -You are allowed to not include a level, but you should include it at least for -the primary message. - -The `SUGGESTION` level is used for specifying what the expected replacement text +The `SUGGESTION` kind is used for specifying what the expected replacement text should be for a diagnostic suggestion. +The `RAW` kind can be used for matching on lines from non-structured output sometimes emitted +by the compiler instead of or in addition to structured json. + +`ERROR` and `WARN` kinds are required to be exhaustively covered by line annotations +`//~` by default. + +Other kinds only need to be line-annotated if at least one annotation of that kind appears +in the test file. For example, one `//~ NOTE` will also require all other `//~ NOTE`s in the file +to be written out explicitly. + +Use directive `//@ dont-require-annotations` to opt out of exhaustive annotations. +E.g. use `//@ dont-require-annotations: NOTE` to annotate notes selectively. +Avoid using this directive for `ERROR`s and `WARN`ings, unless there's a serious reason, like +target-dependent compiler output. + +Some diagnostics are never required to be line-annotated, regardless of their kind or directives, +for example secondary lines of multiline diagnostics, +or ubiquitous diagnostics like `aborting due to N previous errors`. UI tests use the `-A unused` flag by default to ignore all unused warnings, as unused warnings are usually not the focus of a test. However, simple code @@ -353,7 +414,7 @@ would be a `.mir.stderr` and `.thir.stderr` file with the different outputs of the different revisions. > Note: cfg revisions also work inside the source code with `#[cfg]` attributes. -> +> > By convention, the `FALSE` cfg is used to have an always-false config. ## Controlling pass/fail expectations @@ -415,6 +476,14 @@ reasons, including: can alert the developer so they know that the associated issue has been fixed and can possibly be closed. +This directive takes comma-separated issue numbers as arguments, or `"unknown"`: + +- `//@ known-bug: #123, #456` (when the issues are on rust-lang/rust) +- `//@ known-bug: rust-lang/chalk#123456` + (allows arbitrary text before the `#`, which is useful when the issue is on another repo) +- `//@ known-bug: unknown` + (when there is no known issue yet; preferrably open one if it does not already exist) + Do not include [error annotations](#error-annotations) in a test with `known-bug`. The test should still include other normal directives and stdout/stderr files. @@ -530,4 +599,27 @@ with "user-facing" Rust alone. Indeed, one could say that this slightly abuses the term "UI" (*user* interface) and turns such UI tests from black-box tests into white-box ones. Use them carefully and sparingly. -[compiler debugging]: ../compiler-debugging.md#rustc_test-attributes +[compiler debugging]: ../compiler-debugging.md#rustc_-test-attributes + +## UI test mode preset lint levels + +By default, test suites under UI test mode (`tests/ui`, `tests/ui-fulldeps`, +but not `tests/rustdoc-ui`) will specify + +- `-A unused` +- `-A internal_features` + +If: + +- The ui test's pass mode is below `run` (i.e. check or build). +- No compare modes are specified. + +Since they can be very noisy in ui tests. + +You can override them with `compile-flags` lint level flags or +in-source lint level attributes as required. + +Note that the `rustfix` version will *not* have `-A unused` passed, +meaning that you may have to `#[allow(unused)]` to suppress `unused` +lints on the rustfix'd file (because we might be testing rustfix +on `unused` lints themselves). diff --git a/src/doc/rustc-dev-guide/src/the-parser.md b/src/doc/rustc-dev-guide/src/the-parser.md index 60a71ae3873ff..601a81e2e485b 100644 --- a/src/doc/rustc-dev-guide/src/the-parser.md +++ b/src/doc/rustc-dev-guide/src/the-parser.md @@ -1,4 +1,4 @@ -# Lexing and Parsing +# Lexing and parsing The very first thing the compiler does is take the program (in UTF-8 Unicode text) and turn it into a data format the compiler can work with more conveniently than strings. @@ -59,7 +59,7 @@ Note that while parsing, we may encounter macro definitions or invocations. We set these aside to be expanded (see [Macro Expansion](./macro-expansion.md)). Expansion itself may require parsing the output of a macro, which may reveal more macros to be expanded, and so on. -## More on Lexical Analysis +## More on lexical analysis Code for lexical analysis is split between two crates: diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md index af484ab5f8fdb..0cfdf306e92d1 100644 --- a/src/doc/rustc-dev-guide/src/tracing.md +++ b/src/doc/rustc-dev-guide/src/tracing.md @@ -185,11 +185,11 @@ rustc. While calls to `error!`, `warn!` and `info!` are included in every build of the compiler, calls to `debug!` and `trace!` are only included in the program if -`debug-logging=true` is turned on in config.toml (it is +`debug-logging=true` is turned on in bootstrap.toml (it is turned off by default), so if you don't see `DEBUG` logs, especially if you run the compiler with `RUSTC_LOG=rustc rustc some.rs` and only see `INFO` logs, make sure that `debug-logging=true` is turned on in your -config.toml. +bootstrap.toml. ## Logging etiquette and conventions diff --git a/src/doc/rustc-dev-guide/src/traits/caching.md b/src/doc/rustc-dev-guide/src/traits/caching.md index a9f20969b5746..c44722a1d9a33 100644 --- a/src/doc/rustc-dev-guide/src/traits/caching.md +++ b/src/doc/rustc-dev-guide/src/traits/caching.md @@ -61,7 +61,7 @@ to be pretty clearly safe and also still retains a very high hit rate **TODO**: it looks like `pick_candidate_cache` no longer exists. In general, is this section still accurate at all? -[`ParamEnv`]: ../param_env/param_env_summary.html +[`ParamEnv`]: ../typing_parameter_envs.html [`tcx`]: ../ty.html [#18290]: https://github.com/rust-lang/rust/issues/18290 [#22019]: https://github.com/rust-lang/rust/issues/22019 diff --git a/src/doc/rustc-dev-guide/src/traits/resolution.md b/src/doc/rustc-dev-guide/src/traits/resolution.md index 26eb724588600..c62b0593694f1 100644 --- a/src/doc/rustc-dev-guide/src/traits/resolution.md +++ b/src/doc/rustc-dev-guide/src/traits/resolution.md @@ -183,7 +183,7 @@ in that list. If so, it is considered satisfied. More precisely, we want to check whether there is a where-clause obligation that is for the same trait (or some subtrait) and which can match against the obligation. -[parameter environment]: ../param_env/param_env_summary.html +[parameter environment]: ../typing_parameter_envs.html Consider this simple example: diff --git a/src/doc/rustc-dev-guide/src/traits/unsize.md b/src/doc/rustc-dev-guide/src/traits/unsize.md index dd57a1b079673..98a4452574817 100644 --- a/src/doc/rustc-dev-guide/src/traits/unsize.md +++ b/src/doc/rustc-dev-guide/src/traits/unsize.md @@ -32,21 +32,21 @@ Built-in implementations are provided for: ## Structural implementations -There are two implementations of `Unsize` which can be thought of as +There is one implementation of `Unsize` which can be thought of as structural: -* `(A1, A2, .., An): Unsize<(A1, A2, .., U)>` given `An: Unsize`, which - allows the tail field of a tuple to be unsized. This is gated behind the - [`unsized_tuple_coercion`] feature. * `Struct<.., Pi, .., Pj, ..>: Unsize>` given `TailField: Unsize`, which allows the tail field of a struct to be unsized if it is the only field that mentions generic parameters `Pi`, .., `Pj` (which don't need to be contiguous). -The rules for the latter implementation are slightly complicated, since they +The rules for struct unsizing are slightly complicated, since they may allow more than one parameter to be changed (not necessarily unsized) and are best stated in terms of the tail field of the struct. -[`unsized_tuple_coercion`]: https://doc.rust-lang.org/beta/unstable-book/language-features/unsized-tuple-coercion.html +(Tuple unsizing was previously implemented behind the feature gate +`unsized_tuple_coercion`, but the implementation was removed by [#137728].) + +[#137728]: https://github.com/rust-lang/rust/pull/137728 ## Upcasting implementations diff --git a/src/doc/rustc-dev-guide/src/ty-fold.md b/src/doc/rustc-dev-guide/src/ty-fold.md index d4d0952fcc340..23253022ffe27 100644 --- a/src/doc/rustc-dev-guide/src/ty-fold.md +++ b/src/doc/rustc-dev-guide/src/ty-fold.md @@ -1,26 +1,28 @@ + # `TypeFoldable` and `TypeFolder` -In the previous chapter we discussed instantiating binders. This must involves looking at everything inside of a `Early/Binder` -to find any usages of the bound vars in order to replace them. Binders can wrap an arbitrary rust type `T` not just a `Ty` so -how do we implement the `instantiate` methods on the `Early/Binder` types. +In [a previous chapter], we discussed instantiating binders. +This involves looking at everything inside of a `Early(Binder)` +to find any usages of the bound vars in order to replace them. +Binders can wrap an arbitrary Rust type `T`, not just a `Ty`. +So, how do we implement the `instantiate` methods on the `Early/Binder` types? The answer is a couple of traits: -[`TypeFoldable`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/fold/trait.TypeFoldable.html) +[`TypeFoldable`] and -[`TypeFolder`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/fold/trait.TypeFolder.html). +[`TypeFolder`]. - `TypeFoldable` is implemented by types that embed type information. It allows you to recursively process the contents of the `TypeFoldable` and do stuff to them. - `TypeFolder` defines what you want to do with the types you encounter while processing the `TypeFoldable`. -For example, the `TypeFolder` trait has a method -[`fold_ty`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/fold/trait.TypeFolder.html#method.fold_ty) -that takes a type as input and returns a new type as a result. `TypeFoldable` invokes the -`TypeFolder` `fold_foo` methods on itself, giving the `TypeFolder` access to its contents (the -types, regions, etc that are contained within). +For example, the `TypeFolder` trait has a method [`fold_ty`] +that takes a type as input and returns a new type as a result. +`TypeFoldable` invokes the `TypeFolder` `fold_foo` methods on itself, +giving the `TypeFolder` access to its contents (the types, regions, etc that are contained within). -You can think of it with this analogy to the iterator combinators we have come to love in rust: +You can think of it with this analogy to the iterator combinators we have come to love in Rust: ```rust,ignore vec.iter().map(|e1| foo(e2)).collect() @@ -33,8 +35,7 @@ So to reiterate: - `TypeFolder` is a trait that defines a “map” operation. - `TypeFoldable` is a trait that is implemented by things that embed types. -In the case of `subst`, we can see that it is implemented as a `TypeFolder`: -[`ArgFolder`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/binder/struct.ArgFolder.html). +In the case of `subst`, we can see that it is implemented as a `TypeFolder`: [`ArgFolder`]. Looking at its implementation, we see where the actual substitutions are happening. However, you might also notice that the implementation calls this `super_fold_with` method. What is @@ -88,17 +89,22 @@ things. We only want to do something when we reach a type. That means there may `TypeFoldable` types whose implementations basically just forward to their fields’ `TypeFoldable` implementations. Such implementations of `TypeFoldable` tend to be pretty tedious to write by hand. For this reason, there is a `derive` macro that allows you to `#![derive(TypeFoldable)]`. It is -defined -[here](https://github.com/rust-lang/rust/blob/master/compiler/rustc_macros/src/type_foldable.rs). - -**`subst`** In the case of substitutions the [actual -folder](https://github.com/rust-lang/rust/blob/75ff3110ac6d8a0259023b83fd20d7ab295f8dd6/src/librustc_middle/ty/subst.rs#L440-L451) -is going to be doing the indexing we’ve already mentioned. There we define a `Folder` and call -`fold_with` on the `TypeFoldable` to process yourself. Then -[fold_ty](https://github.com/rust-lang/rust/blob/75ff3110ac6d8a0259023b83fd20d7ab295f8dd6/src/librustc_middle/ty/subst.rs#L512-L536) -the method that process each type it looks for a `ty::Param` and for those it replaces it for -something from the list of substitutions, otherwise recursively process the type. To replace it, -calls -[ty_for_param](https://github.com/rust-lang/rust/blob/75ff3110ac6d8a0259023b83fd20d7ab295f8dd6/src/librustc_middle/ty/subst.rs#L552-L587) +defined [here]. + +**`subst`** In the case of substitutions the [actual folder] +is going to be doing the indexing we’ve already mentioned. +There we define a `Folder` and call `fold_with` on the `TypeFoldable` to process yourself. +Then [fold_ty] the method that process each type it looks for a `ty::Param` and for those +it replaces it for something from the list of substitutions, otherwise recursively process the type. +To replace it, calls [ty_for_param] and all that does is index into the list of substitutions with the index of the `Param`. +[a previous chapter]: ty_module/instantiating_binders.md +[`TypeFoldable`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/trait.TypeFoldable.html +[`TypeFolder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/trait.TypeFolder.html +[`fold_ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/trait.TypeFolder.html#method.fold_ty +[`ArgFolder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/binder/struct.ArgFolder.html +[here]: https://github.com/rust-lang/rust/blob/master/compiler/rustc_macros/src/type_foldable.rs +[actual folder]: https://github.com/rust-lang/rust/blob/75ff3110ac6d8a0259023b83fd20d7ab295f8dd6/src/librustc_middle/ty/subst.rs#L440-L451 +[fold_ty]: https://github.com/rust-lang/rust/blob/75ff3110ac6d8a0259023b83fd20d7ab295f8dd6/src/librustc_middle/ty/subst.rs#L512-L536 +[ty_for_param]: https://github.com/rust-lang/rust/blob/75ff3110ac6d8a0259023b83fd20d7ab295f8dd6/src/librustc_middle/ty/subst.rs#L552-L587 diff --git a/src/doc/rustc-dev-guide/src/ty.md b/src/doc/rustc-dev-guide/src/ty.md index b33d540358642..ce6cffec1adb7 100644 --- a/src/doc/rustc-dev-guide/src/ty.md +++ b/src/doc/rustc-dev-guide/src/ty.md @@ -61,11 +61,11 @@ Here is a summary: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Describe the *syntax* of a type: what the user wrote (with some desugaring). | Describe the *semantics* of a type: the meaning of what the user wrote. | | Each `rustc_hir::Ty` has its own spans corresponding to the appropriate place in the program. | Doesn’t correspond to a single place in the user’s program. | -| `rustc_hir::Ty` has generics and lifetimes; however, some of those lifetimes are special markers like [`LifetimeName::Implicit`][implicit]. | `ty::Ty` has the full type, including generics and lifetimes, even if the user left them out | +| `rustc_hir::Ty` has generics and lifetimes; however, some of those lifetimes are special markers like [`LifetimeKind::Implicit`][implicit]. | `ty::Ty` has the full type, including generics and lifetimes, even if the user left them out | | `fn foo(x: u32) → u32 { }` - Two `rustc_hir::Ty` representing each usage of `u32`, each has its own `Span`s, and `rustc_hir::Ty` doesn’t tell us that both are the same type | `fn foo(x: u32) → u32 { }` - One `ty::Ty` for all instances of `u32` throughout the program, and `ty::Ty` tells us that both usages of `u32` mean the same type. | -| `fn foo(x: &u32) -> &u32)` - Two `rustc_hir::Ty` again. Lifetimes for the references show up in the `rustc_hir::Ty`s using a special marker, [`LifetimeName::Implicit`][implicit]. | `fn foo(x: &u32) -> &u32)`- A single `ty::Ty`. The `ty::Ty` has the hidden lifetime param. | +| `fn foo(x: &u32) -> &u32)` - Two `rustc_hir::Ty` again. Lifetimes for the references show up in the `rustc_hir::Ty`s using a special marker, [`LifetimeKind::Implicit`][implicit]. | `fn foo(x: &u32) -> &u32)`- A single `ty::Ty`. The `ty::Ty` has the hidden lifetime param. | -[implicit]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.LifetimeName.html#variant.Implicit +[implicit]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.LifetimeKind.html#variant.Implicit **Order** @@ -323,4 +323,4 @@ When looking at the debug output of `Ty` or simply talking about different types - Generic parameters: `{name}/#{index}` e.g. `T/#0`, where `index` corresponds to its position in the list of generic parameters - Inference variables: `?{id}` e.g. `?x`/`?0`, where `id` identifies the inference variable - Variables from binders: `^{binder}_{index}` e.g. `^0_x`/`^0_2`, where `binder` and `index` identify which variable from which binder is being referred to -- Placeholders: `!{id}` or `!{id}_{universe}` e.g. `!x`/`!0`/`!x_2`/`!0_2`, representing some unique type in the specified universe. The universe is often elided when it is `0` \ No newline at end of file +- Placeholders: `!{id}` or `!{id}_{universe}` e.g. `!x`/`!0`/`!x_2`/`!0_2`, representing some unique type in the specified universe. The universe is often elided when it is `0` diff --git a/src/doc/rustc-dev-guide/src/ty_module/binders.md b/src/doc/rustc-dev-guide/src/ty_module/binders.md index defb7cde514a0..71157eca9b11e 100644 --- a/src/doc/rustc-dev-guide/src/ty_module/binders.md +++ b/src/doc/rustc-dev-guide/src/ty_module/binders.md @@ -40,7 +40,7 @@ We did not always explicitly track the set of bound vars introduced by each `Bin ``` Binder( fn(&'^1_0 &'^1 T/#0), - &[BoundVariarbleKind::Region(...)], + &[BoundVariableKind::Region(...)], ) ``` This would cause all kinds of issues as the region `'^1_0` refers to a binder at a higher level than the outermost binder i.e. it is an escaping bound var. The `'^1` region (also writeable as `'^0_1`) is also ill formed as the binder it refers to does not introduce a second parameter. Modern day rustc will ICE when constructing this binder due to both of those regions, in the past we would have simply allowed this to work and then ran into issues in other parts of the codebase. diff --git a/src/doc/rustc-dev-guide/src/type-checking.md b/src/doc/rustc-dev-guide/src/type-checking.md index b60694201f388..4e8b30b19fc7b 100644 --- a/src/doc/rustc-dev-guide/src/type-checking.md +++ b/src/doc/rustc-dev-guide/src/type-checking.md @@ -17,7 +17,7 @@ Type "collection" is the process of converting the types found in the HIR **internal representation** used by the compiler (`Ty<'tcx>`) – we also do similar conversions for where-clauses and other bits of the function signature. -To try and get a sense for the difference, consider this function: +To try and get a sense of the difference, consider this function: ```rust,ignore struct Foo { } diff --git a/src/doc/rustc-dev-guide/src/type-inference.md b/src/doc/rustc-dev-guide/src/type-inference.md index c03fa36d79d11..888eb2439c5bc 100644 --- a/src/doc/rustc-dev-guide/src/type-inference.md +++ b/src/doc/rustc-dev-guide/src/type-inference.md @@ -19,7 +19,7 @@ Here, the type of `things` is *inferred* to be `Vec<&str>` because of the value we push into `things`. The type inference is based on the standard Hindley-Milner (HM) type inference -algorithm, but extended in various way to accommodate subtyping, region +algorithm, but extended in various ways to accommodate subtyping, region inference, and higher-ranked types. ## A note on terminology diff --git a/src/doc/rustc-dev-guide/src/typing_parameter_envs.md b/src/doc/rustc-dev-guide/src/typing_parameter_envs.md new file mode 100644 index 0000000000000..67eaf51bf2985 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/typing_parameter_envs.md @@ -0,0 +1,206 @@ +# Typing/Parameter Environments + + + +## Typing Environments + +When interacting with the type system there are a few variables to consider that can affect the results of trait solving. The set of in-scope where clauses, and what phase of the compiler type system operations are being performed in (the [`ParamEnv`][penv] and [`TypingMode`][tmode] structs respectively). + +When an environment to perform type system operations in has not yet been created, the [`TypingEnv`][tenv] can be used to bundle all of the external context required into a single type. + +Once a context to perform type system operations in has been created (e.g. an [`ObligationCtxt`][ocx] or [`FnCtxt`][fnctxt]) a `TypingEnv` is typically not stored anywhere as only the `TypingMode` is a property of the whole environment, whereas different `ParamEnv`s can be used on a per-goal basis. + +[ocx]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/struct.ObligationCtxt.html +[fnctxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html + +## Parameter Environments + +### What is a `ParamEnv` + +The [`ParamEnv`][penv] is a list of in-scope where-clauses, it typically corresponds to a specific item's where clauses. Some clauses are not explicitly written but are instead implicitly added in the [`predicates_of`][predicates_of] query, such as `ConstArgHasType` or (some) implied bounds. + +In most cases `ParamEnv`s are initially created via the [`param_env` query][query] which returns a `ParamEnv` derived from the provided item's where clauses. A `ParamEnv` can also be created with arbitrary sets of clauses that are not derived from a specific item, such as in [`compare_method_predicate_entailment`][method_pred_entailment] where we create a hybrid `ParamEnv` consisting of the impl's where clauses and the trait definition's function's where clauses. + +--- + +If we have a function such as: +```rust +// `foo` would have a `ParamEnv` of: +// `[T: Sized, T: Trait, ::Assoc: Clone]` +fn foo() +where + ::Assoc: Clone, +{} +``` +If we were conceptually inside of `foo` (for example, type-checking or linting it) we would use this `ParamEnv` everywhere that we interact with the type system. This would allow things such as normalization (TODO: write a chapter about normalization and link it), evaluating generic constants, and proving where clauses/goals, to rely on `T` being sized, implementing `Trait`, etc. + +A more concrete example: +```rust +// `foo` would have a `ParamEnv` of: +// `[T: Sized, T: Clone]` +fn foo(a: T) { + // when typechecking `foo` we require all the where clauses on `requires_clone` + // to hold in order for it to be legal to call. This means we have to + // prove `T: Clone`. As we are type checking `foo` we use `foo`'s + // environment when trying to check that `T: Clone` holds. + // + // Trying to prove `T: Clone` with a `ParamEnv` of `[T: Sized, T: Clone]` + // will trivially succeed as bound we want to prove is in our environment. + requires_clone(a); +} +``` + +Or alternatively an example that would not compile: +```rust +// `foo2` would have a `ParamEnv` of: +// `[T: Sized]` +fn foo2(a: T) { + // When typechecking `foo2` we attempt to prove `T: Clone`. + // As we are type checking `foo2` we use `foo2`'s environment + // when trying to prove `T: Clone`. + // + // Trying to prove `T: Clone` with a `ParamEnv` of `[T: Sized]` will + // fail as there is nothing in the environment telling the trait solver + // that `T` implements `Clone` and there exists no user written impl + // that could apply. + requires_clone(a); +} +``` + +[predicates_of]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/collect/predicates_of/fn.predicates_of.html +[method_pred_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html +[query]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.param_env + +### Acquiring a `ParamEnv` + +Using the wrong [`ParamEnv`][penv] when interacting with the type system can lead to ICEs, illformed programs compiling, or erroring when we shouldn't. See [#82159](https://github.com/rust-lang/rust/pull/82159) and [#82067](https://github.com/rust-lang/rust/pull/82067) as examples of PRs that modified the compiler to use the correct param env and in the process fixed ICEs. + +In the large majority of cases, when a `ParamEnv` is required it either already exists somewhere in scope, or above in the call stack and should be passed down. A non exhaustive list of places where you might find an existing `ParamEnv`: +- During typeck `FnCtxt` has a [`param_env` field][fnctxt_param_env] +- When writing late lints the `LateContext` has a [`param_env` field][latectxt_param_env] +- During well formedness checking the `WfCheckingCtxt` has a [`param_env` field][wfckctxt_param_env] +- The `TypeChecker` used for MIR Typeck has a [`param_env` field][mirtypeck_param_env] +- In the next-gen trait solver all `Goal`s have a [`param_env` field][goal_param_env] specifying what environment to prove the goal in +- When editing an existing [`TypeRelation`][typerelation] if it implements [`PredicateEmittingRelation`][predicate_emitting_relation] then a [`param_env` method][typerelation_param_env] will be available. + +If you aren't sure if there's a `ParamEnv` in scope somewhere that can be used it can be worth opening a thread in the [`#t-compiler/help`][compiler_help] zulip stream where someone may be able to point out where a `ParamEnv` can be acquired from. + +Manually constructing a `ParamEnv` is typically only needed at the start of some kind of top level analysis (e.g. hir typeck or borrow checking). In such cases there are three ways it can be done: +- Calling the [`tcx.param_env(def_id)` query][param_env_query] which returns the environment associated with a given definition. +- Creating an empty environment with [`ParamEnv::empty`][env_empty]. +- Using [`ParamEnv::new`][param_env_new] to construct an env with an arbitrary set of where clauses. Then calling [`traits::normalize_param_env_or_error`][normalize_env_or_error] to handle normalizing and elaborating all the where clauses in the env. + +Using the `param_env` query is by far the most common way to construct a `ParamEnv` as most of the time the compiler is performing an analysis as part of some specific definition. + +Creating an empty environment with `ParamEnv::empty` is typically only done either in codegen (indirectly via [`TypingEnv::fully_monomorphized`][tenv_mono]), or as part of some analysis that do not expect to ever encounter generic parameters (e.g. various parts of coherence/orphan check). + +Creating an env from an arbitrary set of where clauses is usually unnecessary and should only be done if the environment you need does not correspond to an actual item in the source code (e.g. [`compare_method_predicate_entailment`][method_pred_entailment]). + +[param_env_new]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.new +[normalize_env_or_error]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/fn.normalize_param_env_or_error.html +[fnctxt_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#structfield.param_env +[latectxt_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/context/struct.LateContext.html#structfield.param_env +[wfckctxt_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/wfcheck/struct.WfCheckingCtxt.html#structfield.param_env +[goal_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/canonical/ir/solve/struct.Goal.html#structfield.param_env +[typerelation_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/trait.PredicateEmittingRelation.html#tymethod.param_env +[typerelation]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/relate/trait.TypeRelation.html +[mirtypeck_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/type_check/struct.TypeChecker.html#structfield.param_env +[env_empty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.empty +[param_env_query]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#structfield.param_env +[method_pred_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html +[predicate_emitting_relation]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/relate/combine/trait.PredicateEmittingRelation.html +[tenv_mono]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypingEnv.html#method.fully_monomorphized +[compiler_help]: https://rust-lang.zulipchat.com/#narrow/channel/182449-t-compiler.2Fhelp + +### How are `ParamEnv`s constructed + +Creating a [`ParamEnv`][pe] is more complicated than simply using the list of where clauses defined on an item as written by the user. We need to both elaborate supertraits into the env and fully normalize all aliases. This logic is handled by [`traits::normalize_param_env_or_error`][normalize_env_or_error] (even though it does not mention anything about elaboration). + +#### Elaborating supertraits + +When we have a function such as `fn foo()` we would like to be able to prove `T: Clone` inside of the function as the `Copy` trait has a `Clone` supertrait. Constructing a `ParamEnv` looks at all of the trait bounds in the env and explicitly adds new where clauses to the `ParamEnv` for any supertraits found on the traits. + +A concrete example would be the following function: +```rust +trait Trait: SuperTrait {} +trait SuperTrait: SuperSuperTrait {} + +// `bar`'s unelaborated `ParamEnv` would be: +// `[T: Sized, T: Copy, T: Trait]` +fn bar(a: T) { + requires_impl(a); +} + +fn requires_impl(a: T) {} +``` + +If we did not elaborate the env then the `requires_impl` call would fail to typecheck as we would not be able to prove `T: Clone` or `T: SuperSuperTrait`. In practice we elaborate the env which means that `bar`'s `ParamEnv` is actually: +`[T: Sized, T: Copy, T: Clone, T: Trait, T: SuperTrait, T: SuperSuperTrait]` +This allows us to prove `T: Clone` and `T: SuperSuperTrait` when type checking `bar`. + +The `Clone` trait has a `Sized` supertrait however we do not end up with two `T: Sized` bounds in the env (one for the supertrait and one for the implicitly added `T: Sized` bound) as the elaboration process (implemented via [`util::elaborate`][elaborate]) deduplicates where clauses. + +A side effect of this is that even if no actual elaboration of supertraits takes place, the existing where clauses in the env are _also_ deduplicated. See the following example: +```rust +trait Trait {} +// The unelaborated `ParamEnv` would be: +// `[T: Sized, T: Trait, T: Trait]` +// but after elaboration it would be: +// `[T: Sized, T: Trait]` +fn foo() {} +``` + +The [next-gen trait solver][next-gen-solver] also requires this elaboration to take place. + +[elaborate]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/traits/util/fn.elaborate.html +[next-gen-solver]: ./solve/trait-solving.md + +#### Normalizing all bounds + +In the old trait solver the where clauses stored in `ParamEnv` are required to be fully normalized as otherwise the trait solver will not function correctly. A concrete example of needing to normalize the `ParamEnv` is the following: +```rust +trait Trait { + type Assoc; +} + +trait Other { + type Bar; +} + +impl Other for T { + type Bar = u32; +} + +// `foo`'s unnormalized `ParamEnv` would be: +// `[T: Sized, U: Sized, U: Trait]` +fn foo(a: U) +where + U: Trait<::Bar>, +{ + requires_impl(a); +} + +fn requires_impl>(_: U) {} +``` + +As humans we can tell that `::Bar` is equal to `u32` so the trait bound on `U` is equivalent to `U: Trait`. In practice trying to prove `U: Trait` in the old solver in this environment would fail as it is unable to determine that `::Bar` is equal to `u32`. + +To work around this we normalize `ParamEnv`'s after constructing them so that `foo`'s `ParamEnv` is actually: `[T: Sized, U: Sized, U: Trait]` which means the trait solver is now able to use the `U: Trait` in the `ParamEnv` to determine that the trait bound `U: Trait` holds. + +This workaround does not work in all cases as normalizing associated types requires a `ParamEnv` which introduces a bootstrapping problem. We need a normalized `ParamEnv` in order for normalization to give correct results, but we need to normalize to get that `ParamEnv`. Currently we normalize the `ParamEnv` once using the unnormalized param env and it tends to give okay results in practice even though there are some examples where this breaks ([example]). + +In the next-gen trait solver the requirement for all where clauses in the `ParamEnv` to be fully normalized is not present and so we do not normalize when constructing `ParamEnv`s. + +[example]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e6933265ea3e84eaa47019465739992c +[pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html +[normalize_env_or_error]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/fn.normalize_param_env_or_error.html + +## Typing Modes + +Depending on what context we are performing type system operations in, different behaviour may be required. For example during coherence there are stronger requirements about when we can consider goals to not hold or when we can consider types to be unequal. + +Tracking which "phase" of the compiler type system operations are being performed in is done by the [`TypingMode`][tenv] enum. The documentation on the `TypingMode` enum is quite good so instead of repeating it here verbatim we would recommend reading the API documentation directly. + +[penv]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html +[tenv]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/infer_ctxt/enum.TypingMode.html +[tmode]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.TypingMode.html diff --git a/src/doc/rustc-dev-guide/src/unsafety-checking.md b/src/doc/rustc-dev-guide/src/unsafety-checking.md index 1130878944d2a..fbc19d8961c59 100644 --- a/src/doc/rustc-dev-guide/src/unsafety-checking.md +++ b/src/doc/rustc-dev-guide/src/unsafety-checking.md @@ -1,4 +1,4 @@ -# Unsafety Checking +# Unsafety checking Certain expressions in Rust can violate memory safety and as such need to be inside an `unsafe` block or function. The compiler will also warn if an unsafe diff --git a/src/doc/rustc-dev-guide/src/walkthrough.md b/src/doc/rustc-dev-guide/src/walkthrough.md index 6e07ceb7d7379..48b3f8bb15d3b 100644 --- a/src/doc/rustc-dev-guide/src/walkthrough.md +++ b/src/doc/rustc-dev-guide/src/walkthrough.md @@ -221,7 +221,7 @@ There are a couple of things that may happen for some PRs during the review proc some merge conflicts with other PRs that happen to get merged first. You should fix these merge conflicts using the normal git procedures. -[crater]: ./tests/intro.html#crater +[crater]: ./tests/crater.html If you are not doing a new feature or something like that (e.g. if you are fixing a bug), then that's it! Thanks for your contribution :) diff --git a/src/doc/rustc-dev-guide/triagebot.toml b/src/doc/rustc-dev-guide/triagebot.toml index 12aa0b7b8ff15..53fa72469fd2d 100644 --- a/src/doc/rustc-dev-guide/triagebot.toml +++ b/src/doc/rustc-dev-guide/triagebot.toml @@ -7,5 +7,12 @@ allow-unauthenticated = [ "blocked", ] +[no-mentions] + +[canonicalize-issue-links] + # Automatically close and reopen PRs made by bots to run CI on them [bot-pull-requests] + +[behind-upstream] +days-threshold = 7 \ No newline at end of file diff --git a/src/doc/rustc/book.toml b/src/doc/rustc/book.toml index 167aece0ed6a3..01f127ad39054 100644 --- a/src/doc/rustc/book.toml +++ b/src/doc/rustc/book.toml @@ -6,6 +6,8 @@ title = "The rustc book" [output.html] git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustc" edit-url-template = "https://github.com/rust-lang/rust/edit/master/src/doc/rustc/{path}" +additional-css = ["theme/pagetoc.css"] +additional-js = ["theme/pagetoc.js"] [output.html.search] use-boolean-and = true diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 6c7cdec348024..a3939e5a5c451 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -2,6 +2,7 @@ - [What is rustc?](what-is-rustc.md) - [Command-line Arguments](command-line-arguments.md) + - [Print Options](command-line-arguments/print-options.md) - [Codegen Options](codegen-options/index.md) - [Jobserver](jobserver.md) - [Lints](lints/index.md) @@ -13,6 +14,22 @@ - [Deny-by-default Lints](lints/listing/deny-by-default.md) - [JSON Output](json.md) - [Tests](tests/index.md) +- [Targets](targets/index.md) + - [Built-in Targets](targets/built-in.md) + - [Custom Targets](targets/custom.md) + - [Known Issues](targets/known-issues.md) +- [Profile-guided Optimization](profile-guided-optimization.md) +- [Instrumentation-based Code Coverage](instrument-coverage.md) +- [Linker-plugin-based LTO](linker-plugin-lto.md) +- [Checking Conditional Configurations](check-cfg.md) + - [Cargo Specifics](check-cfg/cargo-specifics.md) +- [Exploit Mitigations](exploit-mitigations.md) +- [Symbol Mangling](symbol-mangling/index.md) + - [v0 Symbol Format](symbol-mangling/v0.md) +- [Contributing to `rustc`](contributing.md) + +-------- + - [Platform Support](platform-support.md) - [Target Tier Policy](target-tier-policy.md) - [Template for Target-specific Documentation](platform-support/TEMPLATE.md) @@ -41,6 +58,7 @@ - [thumbv7m-none-eabi](./platform-support/thumbv7m-none-eabi.md) - [thumbv8m.base-none-eabi](./platform-support/thumbv8m.base-none-eabi.md) - [thumbv8m.main-none-eabi\*](./platform-support/thumbv8m.main-none-eabi.md) + - [armv5te-unknown-linux-gnueabi](platform-support/armv5te-unknown-linux-gnueabi.md) - [armv6k-nintendo-3ds](platform-support/armv6k-nintendo-3ds.md) - [armv7-rtems-eabihf](platform-support/armv7-rtems-eabihf.md) - [armv7-sony-vita-newlibeabihf](platform-support/armv7-sony-vita-newlibeabihf.md) @@ -50,6 +68,7 @@ - [\*-linux-ohos](platform-support/openharmony.md) - [\*-hurd-gnu](platform-support/hurd.md) - [aarch64-unknown-teeos](platform-support/aarch64-unknown-teeos.md) + - [avr-none](platform-support/avr-none.md) - [\*-espidf](platform-support/esp-idf.md) - [\*-unknown-fuchsia](platform-support/fuchsia.md) - [\*-unknown-trusty](platform-support/trusty.md) @@ -60,16 +79,21 @@ - [illumos](platform-support/illumos.md) - [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md) - [loongarch\*-unknown-none\*](platform-support/loongarch-none.md) + - [\*-lynxos178-\*](platform-support/lynxos178.md) - [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md) - [m68k-unknown-none-elf](platform-support/m68k-unknown-none-elf.md) - [mips64-openwrt-linux-musl](platform-support/mips64-openwrt-linux-musl.md) - [mipsel-sony-psx](platform-support/mipsel-sony-psx.md) + - [mipsel-unknown-linux-gnu](platform-support/mipsel-unknown-linux-gnu.md) - [mips\*-mti-none-elf](platform-support/mips-mti-none-elf.md) - [mipsisa\*r6\*-unknown-linux-gnu\*](platform-support/mips-release-6.md) - [nvptx64-nvidia-cuda](platform-support/nvptx64-nvidia-cuda.md) - [powerpc-unknown-openbsd](platform-support/powerpc-unknown-openbsd.md) + - [powerpc-unknown-linux-gnuspe](platform-support/powerpc-unknown-linux-gnuspe.md) - [powerpc-unknown-linux-muslspe](platform-support/powerpc-unknown-linux-muslspe.md) - [powerpc64-ibm-aix](platform-support/aix.md) + - [powerpc64-unknown-linux-musl](platform-support/powerpc64-unknown-linux-musl.md) + - [powerpc64le-unknown-linux-gnu](platform-support/powerpc64le-unknown-linux-gnu.md) - [powerpc64le-unknown-linux-musl](platform-support/powerpc64le-unknown-linux-musl.md) - [riscv32e\*-unknown-none-elf](platform-support/riscv32e-unknown-none-elf.md) - [riscv32i\*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) @@ -80,8 +104,7 @@ - [s390x-unknown-linux-gnu](platform-support/s390x-unknown-linux-gnu.md) - [s390x-unknown-linux-musl](platform-support/s390x-unknown-linux-musl.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) - - [sparcv9-sun-solaris](platform-support/solaris.md) - - [\*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) + - [solaris](platform-support/solaris.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) - [\*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) - [\*-unknown-hermit](platform-support/hermit.md) @@ -95,29 +118,18 @@ - [wasm32-wasip1](platform-support/wasm32-wasip1.md) - [wasm32-wasip1-threads](platform-support/wasm32-wasip1-threads.md) - [wasm32-wasip2](platform-support/wasm32-wasip2.md) + - [wasm32-wali-linux-musl](platform-support/wasm32-wali-linux.md) - [wasm32-unknown-emscripten](platform-support/wasm32-unknown-emscripten.md) - [wasm32-unknown-unknown](platform-support/wasm32-unknown-unknown.md) - [wasm32v1-none](platform-support/wasm32v1-none.md) - [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md) + - [windows-gnu](platform-support/windows-gnu.md) + - [windows-gnullvm](platform-support/windows-gnullvm.md) - [\*-win7-windows-gnu](platform-support/win7-windows-gnu.md) - [\*-win7-windows-msvc](platform-support/win7-windows-msvc.md) - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md) - [x86_64-pc-cygwin](platform-support/x86_64-pc-cygwin.md) - - [x86_64-pc-solaris](platform-support/solaris.md) - [x86_64-unknown-linux-none.md](platform-support/x86_64-unknown-linux-none.md) - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md) - [xtensa-\*-none-elf](platform-support/xtensa.md) - [\*-nuttx-\*](platform-support/nuttx.md) -- [Targets](targets/index.md) - - [Built-in Targets](targets/built-in.md) - - [Custom Targets](targets/custom.md) - - [Known Issues](targets/known-issues.md) -- [Profile-guided Optimization](profile-guided-optimization.md) -- [Instrumentation-based Code Coverage](instrument-coverage.md) -- [Linker-plugin-based LTO](linker-plugin-lto.md) -- [Checking Conditional Configurations](check-cfg.md) - - [Cargo Specifics](check-cfg/cargo-specifics.md) -- [Exploit Mitigations](exploit-mitigations.md) -- [Symbol Mangling](symbol-mangling/index.md) - - [v0 Symbol Format](symbol-mangling/v0.md) -- [Contributing to `rustc`](contributing.md) diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 8c1769a8c7722..a3b70e7f97711 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -110,6 +110,19 @@ It takes a path to [the dlltool executable](https://sourceware.org/binutils/docs If this flag is not specified, a dlltool executable will be inferred based on the host environment and target. +## dwarf-version + +This option controls the version of DWARF that the compiler emits, on platforms +that use DWARF to encode debug information. It takes one of the following +values: + +* `2`: DWARF version 2 (the default on certain platforms, like Android). +* `3`: DWARF version 3 (the default on certain platforms, like AIX). +* `4`: DWARF version 4 (the default on most platforms, like Linux & macOS). +* `5`: DWARF version 5. + +DWARF version 1 is not supported. + ## embed-bitcode This flag controls whether or not the compiler embeds LLVM bitcode into object diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 9dd2e7de1b330..b704cee705b05 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -247,58 +247,7 @@ types to stdout at the same time will result in an error. ## `--print`: print compiler information -This flag prints out various information about the compiler. This flag may be -specified multiple times, and the information is printed in the order the -flags are specified. Specifying a `--print` flag will usually disable the -[`--emit`](#option-emit) step and will only print the requested information. -The valid types of print values are: - -- `crate-name` — The name of the crate. -- `file-names` — The names of the files created by the `link` emit kind. -- `sysroot` — Path to the sysroot. -- `target-libdir` — Path to the target libdir. -- `host-tuple` — The target-tuple string of the host compiler (e.g. `x86_64-unknown-linux-gnu`) -- `cfg` — List of cfg values. See [conditional compilation] for more - information about cfg values. -- `target-list` — List of known targets. The target may be selected with the - `--target` flag. -- `target-cpus` — List of available CPU values for the current target. The - target CPU may be selected with the [`-C target-cpu=val` - flag](codegen-options/index.md#target-cpu). -- `target-features` — List of available target features for the current - target. Target features may be enabled with the [`-C target-feature=val` - flag](codegen-options/index.md#target-feature). This flag is unsafe. See - [known issues](targets/known-issues.md) for more details. -- `relocation-models` — List of relocation models. Relocation models may be - selected with the [`-C relocation-model=val` - flag](codegen-options/index.md#relocation-model). -- `code-models` — List of code models. Code models may be selected with the - [`-C code-model=val` flag](codegen-options/index.md#code-model). -- `tls-models` — List of Thread Local Storage models supported. The model may - be selected with the `-Z tls-model=val` flag. -- `native-static-libs` — This may be used when creating a `staticlib` crate - type. If this is the only flag, it will perform a full compilation and - include a diagnostic note that indicates the linker flags to use when - linking the resulting static library. The note starts with the text - `native-static-libs:` to make it easier to fetch the output. -- `link-args` — This flag does not disable the `--emit` step. When linking, - this flag causes `rustc` to print the full linker invocation in a - human-readable form. This can be useful when debugging linker options. The - exact format of this debugging output is not a stable guarantee, other than - that it will include the linker executable and the text of each command-line - argument passed to the linker. -- `deployment-target` — The currently selected [deployment target] (or minimum OS version) - for the selected Apple platform target. This value can be used or passed along to other - components alongside a Rust build that need this information, such as C compilers. - This returns rustc's minimum supported deployment target if no `*_DEPLOYMENT_TARGET` variable - is present in the environment, or otherwise returns the variable's parsed value. - -A filepath may optionally be specified for each requested information kind, in -the format `--print KIND=PATH`, just like for `--emit`. When a path is -specified, information will be written there instead of to stdout. - -[conditional compilation]: ../reference/conditional-compilation.html -[deployment target]: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html +This flag will allow you to set [print options](command-line-arguments/print-options.md). ## `-g`: include debug information diff --git a/src/doc/rustc/src/command-line-arguments/print-options.md b/src/doc/rustc/src/command-line-arguments/print-options.md new file mode 100644 index 0000000000000..1f33e91e5d1b6 --- /dev/null +++ b/src/doc/rustc/src/command-line-arguments/print-options.md @@ -0,0 +1,212 @@ +# Print Options + +All of these options are passed to `rustc` via the `--print` flag. + +Those options prints out various information about the compiler. Multiple options can be +specified, and the information is printed in the order the options are specified. + +Specifying an option will usually disable the [`--emit`](../command-line-arguments.md#option-emit) +step and will only print the requested information. + +A filepath may optionally be specified for each requested information kind, in the format +`--print KIND=PATH`, just like for `--emit`. When a path is specified, information will be +written there instead of to stdout. + +## `crate-name` + +The name of the crate. + +Generally coming from either from the `#![crate_name = "..."]` attribute, +[`--crate-name` flag](../command-line-arguments.md#option-crate-name) or the filename. + +Example: + +```bash +$ rustc --print crate-name --crate-name my_crate a.rs +my_crate +``` + +## `file-names` + +The names of the files created by the `link` emit kind. + +## `sysroot` + +Abosulte path to the sysroot. + +Example (with rustup and the stable toolchain): + +```bash +$ rustc --print sysroot a.rs +/home/[REDACTED]/.rustup/toolchains/stable-x86_64-unknown-linux-gnu +``` + +## `target-libdir` + +Path to the target libdir. + +Example (with rustup and the stable toolchain): + +```bash +$ rustc --print target-libdir a.rs +/home/[REDACTED]/.rustup/toolchains/beta-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib +``` + +## `host-tuple` + +The target-tuple string of the host compiler. + +Example: + +```bash +$ rustc --print host-tuple a.rs +x86_64-unknown-linux-gnu +``` + +Example with the `--target` flag: + +```bash +$ rustc --print host-tuple --target "armv7-unknown-linux-gnueabihf" a.rs +x86_64-unknown-linux-gnu +``` + +## `cfg` + +List of cfg values. See [conditional compilation] for more information about cfg values. + +Example (for `x86_64-unknown-linux-gnu`): + +```bash +$ rustc --print cfg a.rs +debug_assertions +panic="unwind" +target_abi="" +target_arch="x86_64" +target_endian="little" +target_env="gnu" +target_family="unix" +target_feature="fxsr" +target_feature="sse" +target_feature="sse2" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="linux" +target_pointer_width="64" +target_vendor="unknown" +unix +``` + +## `target-list` + +List of known targets. The target may be selected with the `--target` flag. + +## `target-cpus` + +List of available CPU values for the current target. The target CPU may be selected with +the [`-C target-cpu=val` flag](../codegen-options/index.md#target-cpu). + +## `target-features` + +List of available target features for the *current target*. + +Target features may be enabled with the **unsafe** +[`-C target-feature=val` flag](../codegen-options/index.md#target-feature). + +See [known issues](../targets/known-issues.md) for more details. + +## `relocation-models` + +List of relocation models. Relocation models may be selected with the +[`-C relocation-model=val` flag](../codegen-options/index.md#relocation-model). + +Example: + +```bash +$ rustc --print relocation-models a.rs +Available relocation models: + static + pic + pie + dynamic-no-pic + ropi + rwpi + ropi-rwpi + default +``` + +## `code-models` + +List of code models. Code models may be selected with the +[`-C code-model=val` flag](../codegen-options/index.md#code-model). + +Example: + +```bash +$ rustc --print code-models a.rs +Available code models: + tiny + small + kernel + medium + large +``` + +## `tls-models` + +List of Thread Local Storage models supported. The model may be selected with the +`-Z tls-model=val` flag. + +Example: + +```bash +$ rustc --print tls-models a.rs +Available TLS models: + global-dynamic + local-dynamic + initial-exec + local-exec + emulated +``` + +## `native-static-libs` + +This may be used when creating a `staticlib` crate type. + +If this is the only flag, it will perform a full compilation and include a diagnostic note +that indicates the linker flags to use when linking the resulting static library. + +The note starts with the text `native-static-libs:` to make it easier to fetch the output. + +Example: + +```bash +$ rustc --print native-static-libs --crate-type staticlib a.rs +note: Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms. + +note: native-static-libs: -lgcc_s -lutil [REDACTED] -lpthread -lm -ldl -lc +``` + +## `link-args` + +This flag does not disable the `--emit` step. This can be useful when debugging linker options. + +When linking, this flag causes `rustc` to print the full linker invocation in a human-readable +form. The exact format of this debugging output is not a stable guarantee, other than that it +will include the linker executable and the text of each command-line argument passed to the +linker. + +## `deployment-target` + +The currently selected [deployment target] (or minimum OS version) for the selected Apple +platform target. + +This value can be used or passed along to other components alongside a Rust build that need +this information, such as C compilers. This returns rustc's minimum supported deployment target +if no `*_DEPLOYMENT_TARGET` variable is present in the environment, or otherwise returns the +variable's parsed value. + +[conditional compilation]: ../../reference/conditional-compilation.html +[deployment target]: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html diff --git a/src/doc/rustc/src/exploit-mitigations.md b/src/doc/rustc/src/exploit-mitigations.md index d4e2fc52e973f..f8bafe032140c 100644 --- a/src/doc/rustc/src/exploit-mitigations.md +++ b/src/doc/rustc/src/exploit-mitigations.md @@ -42,8 +42,7 @@ understood within a given context. This section documents the exploit mitigations applicable to the Rust compiler when building programs for the Linux operating system on the AMD64 architecture -and equivalent.1 All examples in this section were built using +and equivalent.[^all-targets] All examples in this section were built using nightly builds of the Rust compiler on Debian testing. The Rust Programming Language currently has no specification. The Rust compiler @@ -67,11 +66,8 @@ equivalent. | Forward-edge control flow protection | Yes | Nightly | | Backward-edge control flow protection (e.g., shadow and safe stack) | Yes | Nightly | -1\. See - -for a list of targets and their default options. - +[^all-targets]: See + for a list of targets and their default options. ### Position-independent executable @@ -141,18 +137,15 @@ Integer overflow checks are enabled when debug assertions are enabled (see Fig. 3), and disabled when debug assertions are disabled (see Fig. 4). To enable integer overflow checks independently, use the option to control integer overflow checks, scoped attributes, or explicit checking methods such as -`checked_add`2. +`checked_add`[^checked-methods]. It is recommended that explicit wrapping methods such as `wrapping_add` be used when wrapping semantics are intended, and that explicit checking and wrapping methods always be used when using Unsafe Rust. -2\. See [the `u32` docs](../std/primitive.u32.html) for more -information on the checked, overflowing, saturating, and wrapping methods -(using u32 as an example). - +[^checked-methods]: See [the `u32` docs](../std/primitive.u32.html) for more + information on the checked, overflowing, saturating, and wrapping methods + (using u32 as an example). ### Non-executable memory regions @@ -180,17 +173,14 @@ binary. The presence of an element of type `PT_GNU_STACK` in the program header table with the `PF_X` (i.e., executable) flag unset indicates non-executable memory -regions3 are enabled for a given binary (see Fig. 5). +regions[^other-regions] are enabled for a given binary (see Fig. 5). Conversely, the presence of an element of type `PT_GNU_STACK` in the program header table with the `PF_X` flag set or the absence of an element of type `PT_GNU_STACK` in the program header table indicates non-executable memory regions are not enabled for a given binary. -3\. See the Appendix section for more information on why it -affects other memory regions besides the stack. - +[^other-regions]: See the [Appendix section](#appendix) for more information + on why it affects other memory regions besides the stack. ### Stack clashing protection @@ -270,8 +260,7 @@ $ readelf -d target/release/hello-rust | grep BIND_NOW Fig. 10. Checking if immediate binding is enabled for a given binary. The presence of an element with the `DT_BIND_NOW` tag and the `DF_BIND_NOW` -flag4 in the dynamic section indicates immediate binding +flag[^bind-now] in the dynamic section indicates immediate binding is enabled for a given binary (see Fig. 10). Conversely, the absence of an element with the `DT_BIND_NOW` tag and the `DF_BIND_NOW` flag in the dynamic section indicates immediate binding is not enabled for a given binary. @@ -281,9 +270,7 @@ table and of an element with the `DT_BIND_NOW` tag and the `DF_BIND_NOW` flag in the dynamic section indicates full RELRO is enabled for a given binary (see Figs. 9–10). -4\. And the `DF_1_NOW` flag for some link editors. - +[^bind-now]: And the `DF_1_NOW` flag for some link editors. ### Heap corruption protection @@ -303,8 +290,7 @@ Rust’s default allocator has historically been [jemalloc](http://jemalloc.net/), and it has long been the cause of issues and the subject of much discussion[32]–[38]. Consequently, it has been removed as the default allocator in favor of the operating system’s standard C library -default allocator5 since version 1.32.0 (2019-01-17)[39]. +default allocator[^linx-allocator] since version 1.32.0 (2019-01-17)[39]. ```rust,no_run fn main() { @@ -343,11 +329,9 @@ Fig. 13. Build and execution of hello-rust-heap with debug assertions disabled Heap corruption checks are performed when using the default allocator (i.e., the GNU Allocator) (see Figs. 12–13). -5\. Linux's standard C library default allocator is the GNU -Allocator, which is derived from ptmalloc (pthreads malloc) by Wolfram Gloger, -which in turn is derived from dlmalloc (Doug Lea malloc) by Doug Lea. - +[^linx-allocator]: Linux's standard C library default allocator is the GNU + Allocator, which is derived from ptmalloc (pthreads malloc) by Wolfram Gloger, + which in turn is derived from dlmalloc (Doug Lea malloc) by Doug Lea. ### Stack smashing protection @@ -385,8 +369,7 @@ commercially available [grsecurity/PaX Reuse Attack Protector (RAP)](https://grsecurity.net/rap_faq). The Rust compiler supports forward-edge control flow protection on nightly -builds[41]-[42] 6. +builds[41]-[42] [^win-cfg]. ```text $ readelf -s -W target/release/hello-rust | grep "\.cfi" @@ -401,10 +384,8 @@ of symbols suffixed with ".cfi" or the `__cfi_init` symbol (and references to `__cfi_check`) indicates that LLVM CFI is not enabled for a given binary (see Fig. 15). -6\. It also supports Control Flow Guard (CFG) on Windows (see -). - +[^win-cfg]: It also supports Control Flow Guard (CFG) on Windows (see + ). ### Backward-edge control flow protection @@ -431,8 +412,7 @@ Newer processors provide hardware assistance for backward-edge control flow protection, such as ARM Pointer Authentication, and Intel Shadow Stack as part of Intel CET. -The Rust compiler supports shadow stack for the AArch64 architecture7on +The Rust compiler supports shadow stack for the AArch64 architecture[^amd64-shadow] on nightly builds[43]-[44], and also supports safe stack on nightly builds[45]-[46]. @@ -447,9 +427,8 @@ enabled for a given binary. Conversely, the absence of the `__safestack_init` symbol indicates that LLVM SafeStack is not enabled for a given binary (see Fig. 16). -7\. The shadow stack implementation for the AMD64 architecture -and equivalent in LLVM was removed due to performance and security issues. +[^amd64-shadow]: The shadow stack implementation for the AMD64 architecture + and equivalent in LLVM was removed due to performance and security issues. ## Appendix diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 41da47e9206ca..57679f82f48d2 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -27,7 +27,7 @@ Rust's source-based code coverage requires the Rust "profiler runtime". Without The Rust `nightly` distribution channel includes the profiler runtime, by default. -> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.example.toml`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.]`): +> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `bootstrap.example.toml`. Edit your `bootstrap.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.]`): > > ```toml > # Build the profiler runtime (required when compiling with options that depend diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index c4e5c1aac2f5d..60002a5f9e5da 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -34,16 +34,17 @@ target | notes -------|------- [`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) `aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1, glibc 2.17+) -`i686-pc-windows-gnu` | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] -`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] +`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment] `i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI] [`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+) -`x86_64-pc-windows-gnu` | 64-bit MinGW (Windows 10+, Windows Server 2016+) +[`x86_64-pc-windows-gnu`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+) `x86_64-pc-windows-msvc` | 64-bit MSVC (Windows 10+, Windows Server 2016+) `x86_64-unknown-linux-gnu` | 64-bit Linux (kernel 3.2+, glibc 2.17+) [^x86_32-floats-return-ABI]: Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant: floating-point return values are passed via an x87 register, so NaN payload bits can be lost. Functions with the default Rust ABI are not affected. See [issue #115567][x86-32-float-return-issue]. +[^win32-msvc-alignment]: Due to non-standard behavior of MSVC, native C code on this target can cause types with an alignment of more than 4 bytes to be incorrectly aligned to only 4 bytes (this affects, e.g., `u64` and `i64`). Rust applies some mitigations to reduce the impact of this issue, but this can still cause unsoundness due to unsafe code that (correctly) assumes that references are always properly aligned. See [issue #112480](https://github.com/rust-lang/rust/issues/112480). + [77071]: https://github.com/rust-lang/rust/issues/77071 [x86-32-float-return-issue]: https://github.com/rust-lang/rust/issues/115567 @@ -89,14 +90,17 @@ target | notes -------|------- `aarch64-pc-windows-msvc` | ARM64 Windows MSVC `aarch64-unknown-linux-musl` | ARM64 Linux with musl 1.2.3 +[`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony `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) `armv7-unknown-linux-gnueabihf` | Armv7-A Linux, hardfloat (kernel 3.2, glibc 2.17) +[`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | Armv7-A OpenHarmony [`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36) [`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, musl 1.2.5) +[`i686-pc-windows-gnu`](platform-support/windows-gnu.md) | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment] `powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2, glibc 2.17) `powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2, glibc 2.17) -`powerpc64le-unknown-linux-gnu` | PPC64LE Linux (kernel 3.10, glibc 2.17) +[`powerpc64le-unknown-linux-gnu`](platform-support/powerpc64le-unknown-linux-gnu.md) | PPC64LE Linux (kernel 3.10, glibc 2.17) [`powerpc64le-unknown-linux-musl`](platform-support/powerpc64le-unknown-linux-musl.md) | PPC64LE Linux (kernel 4.19, musl 1.2.3) [`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20, glibc 2.29) [`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20, musl 1.2.3) @@ -104,6 +108,7 @@ target | notes [`x86_64-unknown-freebsd`](platform-support/freebsd.md) | 64-bit x86 FreeBSD [`x86_64-unknown-illumos`](platform-support/illumos.md) | illumos `x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.3 +[`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | x86_64 OpenHarmony [`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64 ## Tier 2 without Host Tools @@ -140,9 +145,8 @@ target | std | notes [`aarch64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on ARM64 [`aarch64-apple-ios-sim`](platform-support/apple-ios.md) | ✓ | Apple iOS Simulator on ARM64 [`aarch64-linux-android`](platform-support/android.md) | ✓ | ARM64 Android -[`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ARM64 MinGW (Windows 10+), LLVM ABI +[`aarch64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ✓ | ARM64 MinGW (Windows 10+), LLVM ABI [`aarch64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | ARM64 Fuchsia -[`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | ARM64 OpenHarmony `aarch64-unknown-none` | * | Bare ARM64, hardfloat `aarch64-unknown-none-softfloat` | * | Bare ARM64, softfloat [`aarch64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | ARM64 UEFI @@ -152,24 +156,22 @@ target | std | notes [`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ✓ | Arm64EC Windows MSVC [`armebv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian [`armebv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian, hardfloat -`armv5te-unknown-linux-gnueabi` | ✓ | Armv5TE Linux (kernel 4.4, glibc 2.23) +[`armv5te-unknown-linux-gnueabi`](platform-support/armv5te-unknown-linux-gnueabi.md) | ✓ | Armv5TE Linux (kernel 4.4, glibc 2.23) `armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3 [`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android `armv7-unknown-linux-gnueabi` | ✓ | Armv7-A Linux (kernel 4.15, glibc 2.27) `armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.3 `armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.3, hardfloat -[`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | Armv7-A OpenHarmony [`armv7a-none-eabi`](platform-support/arm-none-eabi.md) | * | Bare Armv7-A [`armv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R [`armv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, hardfloat -`i586-pc-windows-msvc` | * | 32-bit Windows (original Pentium) [^x86_32-floats-x87] `i586-unknown-linux-gnu` | ✓ | 32-bit Linux (kernel 3.2, glibc 2.17, original Pentium) [^x86_32-floats-x87] `i586-unknown-linux-musl` | ✓ | 32-bit Linux (musl 1.2.3, original Pentium) [^x86_32-floats-x87] [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android ([Pentium 4 plus various extensions](https://developer.android.com/ndk/guides/abis.html#x86)) [^x86_32-floats-return-ABI] -[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI] +[`i686-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI] [`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD (Pentium 4) [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 (Pentium 4) [^x86_32-floats-return-ABI] -[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) +[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) [^win32-msvc-alignment] [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI) [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs] @@ -202,10 +204,9 @@ target | std | notes [`x86_64-fortanix-unknown-sgx`](platform-support/x86_64-fortanix-unknown-sgx.md) | ✓ | [Fortanix ABI] for 64-bit Intel SGX [`x86_64-linux-android`](platform-support/android.md) | ✓ | 64-bit x86 Android [`x86_64-pc-solaris`](platform-support/solaris.md) | ✓ | 64-bit x86 Solaris 11.4 -[`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 64-bit x86 MinGW (Windows 10+), LLVM ABI +[`x86_64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ✓ | 64-bit x86 MinGW (Windows 10+), LLVM ABI [`x86_64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | 64-bit x86 Fuchsia `x86_64-unknown-linux-gnux32` | ✓ | 64-bit Linux (x32 ABI) (kernel 4.15, glibc 2.27) -[`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | x86_64 OpenHarmony [`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat [`x86_64-unknown-redox`](platform-support/redox.md) | ✓ | Redox OS [`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 64-bit UEFI @@ -266,7 +267,7 @@ target | std | host | notes [`aarch64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | ARM64 OpenBSD [`aarch64-unknown-redox`](platform-support/redox.md) | ✓ | | ARM64 Redox OS [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? | | ARM64 TEEOS | -[`aarch64-unknown-trusty`](platform-support/trusty.md) | ? | | +[`aarch64-unknown-trusty`](platform-support/trusty.md) | ✓ | | [`aarch64-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [`aarch64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | ARM64 VxWorks OS `aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian) @@ -291,7 +292,7 @@ target | std | host | notes [`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | Armv7-A Linux with uClibc, softfloat [`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | Armv7-A Linux with uClibc, hardfloat [`armv7-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv7-A NetBSD w/hard-float -[`armv7-unknown-trusty`](platform-support/trusty.md) | ? | | +[`armv7-unknown-trusty`](platform-support/trusty.md) | ✓ | | [`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ✓ | | Armv7-A for VxWorks [`armv7a-kmc-solid_asp3-eabi`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3 [`armv7a-kmc-solid_asp3-eabihf`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3, hardfloat @@ -301,7 +302,7 @@ target | std | host | notes [`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * | | Bare Armv8-R, hardfloat [`armv7a-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX [`armv7a-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX, hardfloat -`avr-none` | * | | AVR; requires `-Zbuild-std=core` and `-Ctarget-cpu=...` +[`avr-none`](platform-support/avr-none.md) | * | | AVR; requires `-Zbuild-std=core` and `-Ctarget-cpu=...` `bpfeb-unknown-none` | * | | BPF (big endian) `bpfel-unknown-none` | * | | BPF (little endian) `csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian) @@ -318,9 +319,9 @@ target | std | host | notes [`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 (Pentium 4) [^x86_32-floats-return-ABI] [`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD (Pentium 4) [^x86_32-floats-return-ABI] `i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] -[`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] +[`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] [^win32-msvc-alignment] [`i686-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] -[`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] +[`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [^win32-msvc-alignment] [`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] [`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | LoongArch64 OpenHarmony [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux @@ -335,7 +336,7 @@ target | std | host | notes `mips64el-unknown-linux-muslabi64` | ✓ | | MIPS64 (little endian) Linux, N64 ABI, musl 1.2.3 `mipsel-sony-psp` | * | | MIPS (LE) Sony PlayStation Portable (PSP) [`mipsel-sony-psx`](platform-support/mipsel-sony-psx.md) | * | | MIPS (LE) Sony PlayStation 1 (PSX) -`mipsel-unknown-linux-gnu` | ✓ | ✓ | MIPS (little endian) Linux (kernel 4.4, glibc 2.23) +[`mipsel-unknown-linux-gnu`](platform-support/mipsel-unknown-linux-gnu.md) | ✓ | ✓ | MIPS (little endian) Linux (kernel 4.4, glibc 2.23) `mipsel-unknown-linux-musl` | ✓ | | MIPS (little endian) Linux with musl 1.2.3 `mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc [`mipsel-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | 32-bit MIPS (LE), requires mips32 cpu support @@ -348,16 +349,16 @@ target | std | host | notes [`mipsisa64r6el-unknown-linux-gnuabi64`](platform-support/mips-release-6.md) | ✓ | ✓ | 64-bit MIPS Release 6 Little Endian `msp430-none-elf` | * | | 16-bit MSP430 microcontrollers [`powerpc-unknown-freebsd`](platform-support/freebsd.md) | ? | | PowerPC FreeBSD -`powerpc-unknown-linux-gnuspe` | ✓ | | PowerPC SPE Linux +[`powerpc-unknown-linux-gnuspe`](platform-support/powerpc-unknown-linux-gnuspe.md) | ✓ | | PowerPC SPE Linux `powerpc-unknown-linux-musl` | ? | | PowerPC Linux with musl 1.2.3 -[`powerpc-unknown-linux-muslspe`](platform-support/powerpc-unknown-linux-muslspe.md) | ? | | PowerPC SPE Linux +[`powerpc-unknown-linux-muslspe`](platform-support/powerpc-unknown-linux-muslspe.md) | ? | | PowerPC SPE Linux with musl 1.2.3 [`powerpc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD 32-bit powerpc systems [`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * | | [`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ | | [`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer) [`powerpc64-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64 FreeBSD (ELFv2) -`powerpc64-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3 +[`powerpc64-unknown-linux-musl`](platform-support/powerpc64-unknown-linux-musl.md) | ✓ | ✓ | PPC64 Linux (kernel 4.19, musl 1.2.3) [`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64 [`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`powerpc64le-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64LE FreeBSD @@ -405,9 +406,11 @@ target | std | host | notes [`thumbv8m.main-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv8M Mainline with NuttX [`thumbv8m.main-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv8M Mainline with NuttX, hardfloat [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? | | WebAssembly +[`wasm32-wali-linux-musl`](platform-support/wasm32-wali-linux.md) | ? | | WebAssembly with [WALI](https://github.com/arjunr2/WALI) [`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ✓ | | x86 64-bit tvOS [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator -[`x86_64-pc-cygwin`](platform-support/x86_64-pc-cygwin.md) | ? | | 64-bit x86 Cygwin | +[`x86_64-lynx-lynxos178`](platform-support/lynxos178.md) | | | x86_64 LynxOS-178 +[`x86_64-pc-cygwin`](platform-support/x86_64-pc-cygwin.md) | ✓ | | 64-bit x86 Cygwin | [`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS with default network stack (io-pkt) | [`x86_64-pc-nto-qnx710_iosock`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS with new network stack (io-sock) | [`x86_64-pc-nto-qnx800`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 8.0 RTOS | @@ -419,7 +422,7 @@ target | std | host | notes `x86_64-unknown-l4re-uclibc` | ? | | [`x86_64-unknown-linux-none`](platform-support/x86_64-unknown-linux-none.md) | * | | 64-bit Linux with no libc [`x86_64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 64-bit OpenBSD -[`x86_64-unknown-trusty`](platform-support/trusty.md) | ? | | +[`x86_64-unknown-trusty`](platform-support/trusty.md) | ✓ | | `x86_64-uwp-windows-gnu` | ✓ | | [`x86_64-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [`x86_64-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 64-bit Windows 7 support diff --git a/src/doc/rustc/src/platform-support/TEMPLATE.md b/src/doc/rustc/src/platform-support/TEMPLATE.md index 87dde722558b0..f523237ab06ff 100644 --- a/src/doc/rustc/src/platform-support/TEMPLATE.md +++ b/src/doc/rustc/src/platform-support/TEMPLATE.md @@ -6,7 +6,8 @@ One-sentence description of the target (e.g. CPU, OS) ## Target maintainers -- Some Person, https://github.com/... +[@Ghost](https://github.com/Ghost) +[@octocat](https://github.com/octocat) ## Requirements @@ -28,7 +29,7 @@ What format do binaries use by default? ELF, PE, something else? ## Building the target If Rust doesn't build the target by default, how can users build it? Can users -just add it to the `target` list in `config.toml`? +just add it to the `target` list in `bootstrap.toml`? ## Building Rust programs diff --git a/src/doc/rustc/src/platform-support/aarch64-nintendo-switch-freestanding.md b/src/doc/rustc/src/platform-support/aarch64-nintendo-switch-freestanding.md index 308e1fe2f92a0..6951d7f23f8d8 100644 --- a/src/doc/rustc/src/platform-support/aarch64-nintendo-switch-freestanding.md +++ b/src/doc/rustc/src/platform-support/aarch64-nintendo-switch-freestanding.md @@ -4,10 +4,10 @@ Nintendo Switch with pure-Rust toolchain. -## Designated Developers +## Target Maintainers -* [@leo60228](https://github.com/leo60228) -* [@jam1garner](https://github.com/jam1garner) +[@leo60228](https://github.com/leo60228) +[@jam1garner](https://github.com/jam1garner) ## Requirements diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md b/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md index 7a6609b2d769f..be11d0cdd1037 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md @@ -20,8 +20,8 @@ TEEOS is open source in progress. [MORE about](https://gitee.com/opentrustee-gro ## Target maintainers -- Petrochenkov Vadim -- Sword-Destiny +[@petrochenkov](https://github.com/petrochenkov) +[@Sword-Destiny](https://github.com/Sword-Destiny) ## Setup We use OpenHarmony SDK for TEEOS. @@ -54,7 +54,7 @@ exec /path/to/ohos-sdk/linux/native/llvm/bin/clang++ \ ## Building the target -To build a rust toolchain, create a `config.toml` with the following contents: +To build a rust toolchain, create a `bootstrap.toml` with the following contents: ```toml profile = "compiler" diff --git a/src/doc/rustc/src/platform-support/aix.md b/src/doc/rustc/src/platform-support/aix.md index 5a198062b9529..3002a5c4b2cd9 100644 --- a/src/doc/rustc/src/platform-support/aix.md +++ b/src/doc/rustc/src/platform-support/aix.md @@ -6,8 +6,8 @@ Rust for AIX operating system, currently only 64-bit PowerPC is supported. ## Target maintainers -- David Tenty `daltenty@ibm.com`, https://github.com/daltenty -- Chris Cambly, `ccambly@ca.ibm.com`, https://github.com/gilamn5tr +[@daltenty](https://github.com/daltenty) +[@gilamn5tr](https://github.com/gilamn5tr) ## Requirements diff --git a/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md index 0b2f798e66de1..16152dd2dad51 100644 --- a/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md +++ b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md @@ -6,7 +6,7 @@ AMD GPU target for compute/HSA (Heterogeneous System Architecture). ## Target maintainers -- [@Flakebi](https://github.com/Flakebi) +[@Flakebi](https://github.com/Flakebi) ## Requirements diff --git a/src/doc/rustc/src/platform-support/android.md b/src/doc/rustc/src/platform-support/android.md index 54e7ddca32aab..a54288f8f050e 100644 --- a/src/doc/rustc/src/platform-support/android.md +++ b/src/doc/rustc/src/platform-support/android.md @@ -8,9 +8,9 @@ ## Target maintainers -- Chris Wailes ([@chriswailes](https://github.com/chriswailes)) -- Matthew Maurer ([@maurer](https://github.com/maurer)) -- Martin Geisler ([@mgeisler](https://github.com/mgeisler)) +[@chriswailes](https://github.com/chriswailes) +[@maurer](https://github.com/maurer) +[@mgeisler](https://github.com/mgeisler) ## Requirements diff --git a/src/doc/rustc/src/platform-support/apple-darwin.md b/src/doc/rustc/src/platform-support/apple-darwin.md index dba2c4b2aaf0e..e41aee9bdb248 100644 --- a/src/doc/rustc/src/platform-support/apple-darwin.md +++ b/src/doc/rustc/src/platform-support/apple-darwin.md @@ -9,8 +9,8 @@ Apple macOS targets. ## Target maintainers -- [@thomcc](https://github.com/thomcc) -- [@madsmtm](https://github.com/madsmtm) +[@thomcc](https://github.com/thomcc) +[@madsmtm](https://github.com/madsmtm) ## Requirements @@ -70,4 +70,5 @@ the `-mmacosx-version-min=...`, `-miphoneos-version-min=...` or similar flags to disambiguate. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun --sdk macosx --show-sdk-path`. diff --git a/src/doc/rustc/src/platform-support/apple-ios-macabi.md b/src/doc/rustc/src/platform-support/apple-ios-macabi.md index a54656190d1f3..d4b71dbd4f492 100644 --- a/src/doc/rustc/src/platform-support/apple-ios-macabi.md +++ b/src/doc/rustc/src/platform-support/apple-ios-macabi.md @@ -9,9 +9,9 @@ Apple Mac Catalyst targets. ## Target maintainers -- [@badboy](https://github.com/badboy) -- [@BlackHoleFox](https://github.com/BlackHoleFox) -- [@madsmtm](https://github.com/madsmtm) +[@badboy](https://github.com/badboy) +[@BlackHoleFox](https://github.com/BlackHoleFox) +[@madsmtm](https://github.com/madsmtm) ## Requirements @@ -20,7 +20,8 @@ These targets are cross-compiled, and require the corresponding macOS SDK iOS-specific headers, as provided by Xcode 11 or higher. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun --sdk macosx --show-sdk-path`. ### OS version diff --git a/src/doc/rustc/src/platform-support/apple-ios.md b/src/doc/rustc/src/platform-support/apple-ios.md index 5045f810400f5..64325554ab60b 100644 --- a/src/doc/rustc/src/platform-support/apple-ios.md +++ b/src/doc/rustc/src/platform-support/apple-ios.md @@ -15,9 +15,9 @@ Apple iOS / iPadOS targets. ## Target maintainers -- [@badboy](https://github.com/badboy) -- [@deg4uss3r](https://github.com/deg4uss3r) -- [@madsmtm](https://github.com/madsmtm) +[@badboy](https://github.com/badboy) +[@deg4uss3r](https://github.com/deg4uss3r) +[@madsmtm](https://github.com/madsmtm) ## Requirements @@ -26,7 +26,8 @@ These targets are cross-compiled, and require the corresponding iOS SDK ARM64 targets, Xcode 12 or higher is required. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun --sdk iphoneos --show-sdk-path`. ### OS version @@ -47,7 +48,7 @@ $ rustup target add x86_64-apple-ios ``` The tier 3 targets can be built by enabling them for a `rustc` build in -`config.toml`, by adding, for example: +`bootstrap.toml`, by adding, for example: ```toml [build] diff --git a/src/doc/rustc/src/platform-support/apple-tvos.md b/src/doc/rustc/src/platform-support/apple-tvos.md index 7a3b601579a01..193d64666121e 100644 --- a/src/doc/rustc/src/platform-support/apple-tvos.md +++ b/src/doc/rustc/src/platform-support/apple-tvos.md @@ -10,8 +10,8 @@ Apple tvOS targets. ## Target maintainers -- [@thomcc](https://github.com/thomcc) -- [@madsmtm](https://github.com/madsmtm) +[@thomcc](https://github.com/thomcc) +[@madsmtm](https://github.com/madsmtm) ## Requirements @@ -20,7 +20,8 @@ These targets are cross-compiled, and require the corresponding tvOS SDK ARM64 targets, Xcode 12 or higher is required. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun --sdk appletvos --show-sdk-path`. ### OS version @@ -52,7 +53,7 @@ The following APIs are currently known to have missing or incomplete support: ## Building the target The targets can be built by enabling them for a `rustc` build in -`config.toml`, by adding, for example: +`bootstrap.toml`, by adding, for example: ```toml [build] diff --git a/src/doc/rustc/src/platform-support/apple-visionos.md b/src/doc/rustc/src/platform-support/apple-visionos.md index 56224d7e20d4e..ed96912da7a48 100644 --- a/src/doc/rustc/src/platform-support/apple-visionos.md +++ b/src/doc/rustc/src/platform-support/apple-visionos.md @@ -9,8 +9,8 @@ Apple visionOS / xrOS targets. ## Target maintainers -- [@agg23](https://github.com/agg23) -- [@madsmtm](https://github.com/madsmtm) +[@agg23](https://github.com/agg23) +[@madsmtm](https://github.com/madsmtm) ## Requirements @@ -18,7 +18,8 @@ These targets are cross-compiled, and require the corresponding visionOS SDK (`XROS.sdk` or `XRSimulator.sdk`), as provided by Xcode 15 or newer. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun --sdk xros --show-sdk-path`. ### OS version @@ -31,7 +32,7 @@ case `XROS_DEPLOYMENT_TARGET`. ## Building the target The targets can be built by enabling them for a `rustc` build in -`config.toml`, by adding, for example: +`bootstrap.toml`, by adding, for example: ```toml [build] diff --git a/src/doc/rustc/src/platform-support/apple-watchos.md b/src/doc/rustc/src/platform-support/apple-watchos.md index 8ba35f70b8578..6ac09d0d1e538 100644 --- a/src/doc/rustc/src/platform-support/apple-watchos.md +++ b/src/doc/rustc/src/platform-support/apple-watchos.md @@ -12,10 +12,10 @@ Apple watchOS targets. ## Target maintainers -- [@deg4uss3r](https://github.com/deg4uss3r) -- [@vladimir-ea](https://github.com/vladimir-ea) -- [@leohowell](https://github.com/leohowell) -- [@madsmtm](https://github.com/madsmtm) +[@deg4uss3r](https://github.com/deg4uss3r) +[@vladimir-ea](https://github.com/vladimir-ea) +[@leohowell](https://github.com/leohowell) +[@madsmtm](https://github.com/madsmtm) ## Requirements @@ -24,7 +24,8 @@ These targets are cross-compiled, and require the corresponding watchOS SDK ARM64 targets, Xcode 12 or higher is required. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun --sdk watchos --show-sdk-path`. ### OS version @@ -37,7 +38,7 @@ case `WATCHOS_DEPLOYMENT_TARGET`. ## Building the target The targets can be built by enabling them for a `rustc` build in -`config.toml`, by adding, for example: +`bootstrap.toml`, by adding, for example: ```toml [build] diff --git a/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md b/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md index 322a07c573970..2043b34210531 100644 --- a/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md +++ b/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md @@ -6,7 +6,7 @@ ARM64e macOS (11.0+, Big Sur+) ## Target maintainers -- Artyom Tetyukhin ([@arttet](https://github.com/arttet)) +[@arttet](https://github.com/arttet) ## Requirements @@ -16,7 +16,7 @@ See the docs on [`*-apple-darwin`](apple-darwin.md) for general macOS requiremen ## Building the target -You can build Rust with support for the targets by adding it to the `target` list in `config.toml`: +You can build Rust with support for the targets by adding it to the `target` list in `bootstrap.toml`: ```toml [build] diff --git a/src/doc/rustc/src/platform-support/arm64e-apple-ios.md b/src/doc/rustc/src/platform-support/arm64e-apple-ios.md index 3d8ba5c282a60..a2b09e0772822 100644 --- a/src/doc/rustc/src/platform-support/arm64e-apple-ios.md +++ b/src/doc/rustc/src/platform-support/arm64e-apple-ios.md @@ -6,7 +6,7 @@ ARM64e iOS (14.0+) ## Target maintainers -- Artyom Tetyukhin ([@arttet](https://github.com/arttet)) +[@arttet](https://github.com/arttet) ## Requirements @@ -14,7 +14,7 @@ See the docs on [`*-apple-ios`](apple-ios.md) for general iOS requirements. ## Building the target -You can build Rust with support for the targets by adding it to the `target` list in `config.toml`: +You can build Rust with support for the targets by adding it to the `target` list in `bootstrap.toml`: ```toml [build] diff --git a/src/doc/rustc/src/platform-support/arm64e-apple-tvos.md b/src/doc/rustc/src/platform-support/arm64e-apple-tvos.md index ec8a996549e11..36588c5a96430 100644 --- a/src/doc/rustc/src/platform-support/arm64e-apple-tvos.md +++ b/src/doc/rustc/src/platform-support/arm64e-apple-tvos.md @@ -6,7 +6,7 @@ ARM64e tvOS (10.0+) ## Target maintainers -- Artyom Tetyukhin ([@arttet](https://github.com/arttet)) +[@arttet](https://github.com/arttet) ## Requirements @@ -15,7 +15,7 @@ To build this target Xcode 12 or higher on macOS is required. ## Building the target -You can build Rust with support for the targets by adding it to the `target` list in `config.toml`: +You can build Rust with support for the targets by adding it to the `target` list in `bootstrap.toml`: ```toml [build] diff --git a/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md b/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md index 15bf55d35a26b..d02043b2ae9e8 100644 --- a/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md +++ b/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md @@ -7,7 +7,7 @@ applications on AArch64 Windows 11. See +[@Patryk27](https://github.com/Patryk27) ## Requirements diff --git a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md index 32e4f85531375..e69d606ccd2fe 100644 --- a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md +++ b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md @@ -22,7 +22,7 @@ other links: ## Target maintainers -* [@Dirreke](https://github.com/Dirreke) +[@Dirreke](https://github.com/Dirreke) ## Requirements @@ -36,7 +36,7 @@ If you don't already have a suitable toolchain, you can download from [here](htt ### Configure rust -The target can be built by enabling it for a `rustc` build, by placing the following in `config.toml`: +The target can be built by enabling it for a `rustc` build, by placing the following in `bootstrap.toml`: ```toml [build] diff --git a/src/doc/rustc/src/platform-support/esp-idf.md b/src/doc/rustc/src/platform-support/esp-idf.md index 91d7d66627d09..baf42ab29a66a 100644 --- a/src/doc/rustc/src/platform-support/esp-idf.md +++ b/src/doc/rustc/src/platform-support/esp-idf.md @@ -6,9 +6,9 @@ Targets for the [ESP-IDF](https://github.com/espressif/esp-idf) development fram ## Target maintainers -- Ivan Markov [@ivmarkov](https://github.com/ivmarkov) -- Scott Mabin [@MabezDev](https://github.com/MabezDev) -- Sergio Gasquez [@SergioGasquez](https://github.com/SergioGasquez) +[@ivmarkov](https://github.com/ivmarkov) +[@MabezDev](https://github.com/MabezDev) +[@SergioGasquez](https://github.com/SergioGasquez) ## Requirements diff --git a/src/doc/rustc/src/platform-support/freebsd.md b/src/doc/rustc/src/platform-support/freebsd.md index 9d34d3649208a..9d7218b258ec4 100644 --- a/src/doc/rustc/src/platform-support/freebsd.md +++ b/src/doc/rustc/src/platform-support/freebsd.md @@ -6,8 +6,8 @@ ## Target maintainers -- Alan Somers `asomers@FreeBSD.org`, https://github.com/asomers -- Mikael Urankar `mikael@FreeBSD.org`, https://github.com/MikaelUrankar +[@asomers](https://github.com/asomers) +[@MikaelUrankar](https://github.com/MikaelUrankar) ## Requirements diff --git a/src/doc/rustc/src/platform-support/fuchsia.md b/src/doc/rustc/src/platform-support/fuchsia.md index 489f46e1cb98d..e2befc5d9955b 100644 --- a/src/doc/rustc/src/platform-support/fuchsia.md +++ b/src/doc/rustc/src/platform-support/fuchsia.md @@ -7,9 +7,11 @@ updatable, and performant. ## Target maintainers -See [`fuchsia.toml`] in the `team` repository for current target maintainers. +[@erickt](https://github.com/erickt) +[@Nashenas88](https://github.com/Nashenas88) -[`fuchsia.toml`]: https://github.com/rust-lang/team/blob/master/teams/fuchsia.toml +The up-to-date list can be also found via the +[fuchsia marker team](https://github.com/rust-lang/team/blob/master/teams/fuchsia.toml). ## Table of contents @@ -180,7 +182,7 @@ Fuchsia as well. A recent version (14+) of clang should be sufficient to compile Rust for Fuchsia. x86-64 and AArch64 Fuchsia targets can be enabled using the following -configuration in `config.toml`: +configuration in `bootstrap.toml`: ```toml [build] @@ -212,7 +214,7 @@ cxx = "clang++" By default, the Rust compiler installs itself to `/usr/local` on most UNIX systems. You may want to install it to another location (e.g. a local `install` -directory) by setting a custom prefix in `config.toml`: +directory) by setting a custom prefix in `bootstrap.toml`: ```toml [install] @@ -695,7 +697,7 @@ We can then use the script to start our test environment with: ) ``` -Where `${RUST_SRC_PATH}/build` is the `build-dir` set in `config.toml`. +Where `${RUST_SRC_PATH}/build` is the `build-dir` set in `bootstrap.toml`. Once our environment is started, we can run our tests using `x.py` as usual. The test runner script will run the compiled tests on an emulated Fuchsia device. To @@ -705,7 +707,7 @@ run the full `tests/ui` test suite: ( \ source config-env.sh && \ ./x.py \ - --config config.toml \ + --config bootstrap.toml \ --stage=2 \ test tests/ui \ --target x86_64-unknown-fuchsia \ @@ -893,7 +895,7 @@ through our `x.py` invocation. The full invocation is: ( \ source config-env.sh && \ ./x.py \ - --config config.toml \ + --config bootstrap.toml \ --stage=2 \ test tests/${TEST} \ --target x86_64-unknown-fuchsia \ diff --git a/src/doc/rustc/src/platform-support/hermit.md b/src/doc/rustc/src/platform-support/hermit.md index 5aa787e53e1eb..069c253bd38a3 100644 --- a/src/doc/rustc/src/platform-support/hermit.md +++ b/src/doc/rustc/src/platform-support/hermit.md @@ -14,8 +14,8 @@ Target triplets available so far: ## Target maintainers -- Stefan Lankes ([@stlankes](https://github.com/stlankes)) -- Martin Kröning ([@mkroening](https://github.com/mkroening)) +[@stlankes](https://github.com/stlankes) +[@mkroening](https://github.com/mkroening) ## Requirements @@ -31,7 +31,7 @@ Hermit binaries have the ELF format. ## Building the target -You can build Rust with support for the targets by adding it to the `target` list in `config.toml`. +You can build Rust with support for the targets by adding it to the `target` list in `bootstrap.toml`. To run the Hermit build scripts, you also have to enable your host target. The build scripts rely on `llvm-tools` and binaries are linked using `rust-lld`, so those have to be enabled as well. diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md index d858337a9499f..be6e17883f4eb 100644 --- a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md +++ b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md @@ -11,7 +11,7 @@ DSP architecture. ## Target maintainers -- [Brian Cain](https://github.com/androm3da), `bcain@quicinc.com` +[@androm3da](https://github.com/androm3da) ## Requirements The target is cross-compiled. This target supports `std`. By default, code @@ -44,7 +44,7 @@ Because it is Tier 3, rust does not yet ship pre-compiled artifacts for this target. Therefore, you can build Rust with support for the target by adding it to the -target list in `config.toml`, a sample configuration is shown below. +target list in `bootstrap.toml`, a sample configuration is shown below. ```toml [build] diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md b/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md index a0e26b798ac2b..b07b0bb08d60a 100644 --- a/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md +++ b/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md @@ -10,7 +10,7 @@ Rust for baremetal Hexagon DSPs. ## Target maintainers -- [Brian Cain](https://github.com/androm3da), `bcain@quicinc.com` +[@androm3da](https://github.com/androm3da) ## Requirements @@ -28,7 +28,7 @@ This target generates PIC ELF binaries. ## Building the target You can build Rust with support for the target by adding it to the `target` -list in `config.toml`: +list in `bootstrap.toml`: ```toml [build] diff --git a/src/doc/rustc/src/platform-support/hurd.md b/src/doc/rustc/src/platform-support/hurd.md index 2521f79dc5e65..6ecde1db5111e 100644 --- a/src/doc/rustc/src/platform-support/hurd.md +++ b/src/doc/rustc/src/platform-support/hurd.md @@ -6,7 +6,7 @@ ## Target maintainers -- Samuel Thibault, `samuel.thibault@ens-lyon.org`, https://github.com/sthibaul/ +[@sthibaul](https://github.com/sthibaul) ## Requirements diff --git a/src/doc/rustc/src/platform-support/i686-apple-darwin.md b/src/doc/rustc/src/platform-support/i686-apple-darwin.md index d69fa97ce6373..5f18a5e271acb 100644 --- a/src/doc/rustc/src/platform-support/i686-apple-darwin.md +++ b/src/doc/rustc/src/platform-support/i686-apple-darwin.md @@ -4,8 +4,8 @@ Apple macOS on 32-bit x86. ## Target maintainers -- [@thomcc](https://github.com/thomcc) -- [@madsmtm](https://github.com/madsmtm) +[@thomcc](https://github.com/thomcc) +[@madsmtm](https://github.com/madsmtm) ## Requirements @@ -17,7 +17,7 @@ You'll need the macOS 10.13 SDK shipped with Xcode 9. The location of the SDK can be passed to `rustc` using the common `SDKROOT` environment variable. Once you have that, you can build Rust with support for the target by adding -it to the `target` list in `config.toml`: +it to the `target` list in `bootstrap.toml`: ```toml [build] diff --git a/src/doc/rustc/src/platform-support/illumos.md b/src/doc/rustc/src/platform-support/illumos.md index dd2ae90f6741f..c03238269d37a 100644 --- a/src/doc/rustc/src/platform-support/illumos.md +++ b/src/doc/rustc/src/platform-support/illumos.md @@ -7,8 +7,8 @@ including advanced system debugging, next generation filesystem, networking, and ## Target maintainers -- Joshua M. Clulow ([@jclulow](https://github.com/jclulow)) -- Patrick Mooney ([@pfmooney](https://github.com/pfmooney)) +[@jclulow](https://github.com/jclulow) +[@pfmooney](https://github.com/pfmooney) ## Requirements diff --git a/src/doc/rustc/src/platform-support/kmc-solid.md b/src/doc/rustc/src/platform-support/kmc-solid.md index bbcd0f711c663..838662a3741ae 100644 --- a/src/doc/rustc/src/platform-support/kmc-solid.md +++ b/src/doc/rustc/src/platform-support/kmc-solid.md @@ -14,9 +14,9 @@ The target names follow this format: `$ARCH-kmc-solid_$KERNEL-$ABI`, where `$ARC | `armv7a-kmc-solid_asp3-eabi` | `arm` | `kmc` | `solid_asp3` | | `armv7a-kmc-solid_asp3-eabihf` | `arm` | `kmc` | `solid_asp3` | -## Designated Developers +## Target Maintainers -- [@kawadakk](https://github.com/kawadakk) +[@kawadakk](https://github.com/kawadakk) ## Requirements @@ -32,7 +32,7 @@ The target can be built by enabling it for a `rustc` build. target = ["aarch64-kmc-solid_asp3"] ``` -Make sure `aarch64-kmc-elf-gcc` is included in `$PATH`. Alternatively, you can use GNU Arm Embedded Toolchain by adding the following to `config.toml`: +Make sure `aarch64-kmc-elf-gcc` is included in `$PATH`. Alternatively, you can use GNU Arm Embedded Toolchain by adding the following to `bootstrap.toml`: ```toml [target.aarch64-kmc-solid_asp3] diff --git a/src/doc/rustc/src/platform-support/loongarch-linux.md b/src/doc/rustc/src/platform-support/loongarch-linux.md index 45eb0a81216d8..817d3a892303a 100644 --- a/src/doc/rustc/src/platform-support/loongarch-linux.md +++ b/src/doc/rustc/src/platform-support/loongarch-linux.md @@ -22,10 +22,10 @@ Reference material: ## Target maintainers -- [WANG Rui](https://github.com/heiher) `wangrui@loongson.cn` -- [ZHAI Xiang](https://github.com/xiangzhai) `zhaixiang@loongson.cn` -- [ZHAI Xiaojuan](https://github.com/zhaixiaojuan) `zhaixiaojuan@loongson.cn` -- [WANG Xuerui](https://github.com/xen0n) `git@xen0n.name` +[@heiher](https://github.com/heiher) +[@xiangzhai](https://github.com/xiangzhai) +[@zhaixiaojuan](https://github.com/zhaixiaojuan) +[@xen0n](https://github.com/xen0n) ## Requirements @@ -65,7 +65,7 @@ These targets are distributed through `rustup`, and otherwise require no special configuration. If you need to build your own Rust for some reason though, the targets can be -simply enabled in `config.toml`. For example: +simply enabled in `bootstrap.toml`. For example: ```toml [build] @@ -73,7 +73,7 @@ target = ["loongarch64-unknown-linux-gnu"] ``` Make sure the LoongArch toolchain binaries are reachable from `$PATH`. -Alternatively, you can explicitly configure the paths in `config.toml`: +Alternatively, you can explicitly configure the paths in `bootstrap.toml`: ```toml [target.loongarch64-unknown-linux-gnu] diff --git a/src/doc/rustc/src/platform-support/loongarch-none.md b/src/doc/rustc/src/platform-support/loongarch-none.md index bafa85c26e290..a2bd6e5734cd4 100644 --- a/src/doc/rustc/src/platform-support/loongarch-none.md +++ b/src/doc/rustc/src/platform-support/loongarch-none.md @@ -11,8 +11,8 @@ Freestanding/bare-metal LoongArch64 binaries in ELF format: firmware, kernels, e ## Target maintainers -- [WANG Rui](https://github.com/heiher) `wangrui@loongson.cn` -- [WANG Xuerui](https://github.com/xen0n) `git@xen0n.name` +[@heiher](https://github.com/heiher) +[@xen0n](https://github.com/xen0n) ## Requirements @@ -46,7 +46,7 @@ scripts. ## Building the target You can build Rust with support for the targets by adding them to the `target` -list in `config.toml`: +list in `bootstrap.toml`: ```toml [build] diff --git a/src/doc/rustc/src/platform-support/lynxos178.md b/src/doc/rustc/src/platform-support/lynxos178.md new file mode 100644 index 0000000000000..6463f95a0b8e0 --- /dev/null +++ b/src/doc/rustc/src/platform-support/lynxos178.md @@ -0,0 +1,77 @@ +# `*-lynxos178-*` + +**Tier: 3** + +Targets for the LynxOS-178 operating system. + +[LynxOS-178](https://www.lynx.com/products/lynxos-178-do-178c-certified-posix-rtos) +is a commercial RTOS designed for safety-critical real-time systems. It is +developed by Lynx Software Technologies as part of the +[MOSA.ic](https://www.lynx.com/solutions/safe-and-secure-operating-environment) +product suite. + +Target triples available: +- `x86_64-lynx-lynxos178` + +## Target maintainers + +- Renat Fatykhov, https://github.com/rfatykhov-lynx + +## Requirements + +To build Rust programs for LynxOS-178, you must first have LYNX MOSA.ic +installed on the build machine. + +This target supports only cross-compilation, from the same hosts supported by +the Lynx CDK. + +Currently only `no_std` programs are supported. Work to support `std` is in +progress. + +## Building the target + +You can build Rust with support for x86_64-lynx-lynxos178 by adding that +to the `target` list in `config.toml`, and then running `./x build --target +x86_64-lynx-lynxos178 compiler`. + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To compile for +this target, you will need to build Rust with the target enabled (see "Building +the target" above). + +Before executing `cargo`, you must configure the environment to build LynxOS-178 +binaries by running `source setup.sh` from the los178 directory. + +If your program/crates contain procedural macros, Rust must be able to build +binaries for the host as well. The host gcc is hidden by sourcing setup.sh. To +deal with this, add the following to your project's `.cargo/config.toml`: +```toml +[target.x86_64-unknown-linux-gnu] +linker = "lynx-host-gcc" +``` +(If necessary substitute your host target triple for x86_64-unknown-linux-gnu.) + +To point `cargo` at the correct rustc binary, set the RUSTC environment +variable. + +The core library should be usable. You can try by building it as part of your +project: +```bash +cargo +nightly build -Z build-std=core --target x86_64-lynx-lynxos178 +``` + +## Testing + +Binaries built with rust can be provided to a LynxOS-178 instance on its file +system, where they can be executed. Rust binaries tend to be large, so it may +be necessary to strip them first. + +It is possible to run the Rust testsuite by providing a test runner that takes +the test binary and executes it under LynxOS-178. Most (all?) tests won't run +without std support though, which is not yet supported. + +## Cross-compilation toolchains and C code + +LYNX MOSA.ic comes with all the tools required to cross-compile C code for +LynxOS-178. diff --git a/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md index b18a125f3b095..1efea86df92bc 100644 --- a/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md +++ b/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md @@ -4,10 +4,10 @@ Motorola 680x0 Linux -## Designated Developers +## Target Maintainers -* [@glaubitz](https://github.com/glaubitz) -* [@ricky26](https://github.com/ricky26) +[@glaubitz](https://github.com/glaubitz) +[@ricky26](https://github.com/ricky26) ## Requirements diff --git a/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md b/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md index 92780cb5a5ca5..e390ba0aee96b 100644 --- a/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md +++ b/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md @@ -4,9 +4,9 @@ Bare metal Motorola 680x0 -## Designated Developers +## Target Maintainers -* [@knickish](https://github.com/knickish) +[@knickish](https://github.com/knickish) ## Requirements diff --git a/src/doc/rustc/src/platform-support/mips-mti-none-elf.md b/src/doc/rustc/src/platform-support/mips-mti-none-elf.md index 731f0a8c42f18..c060ebf7c7e00 100644 --- a/src/doc/rustc/src/platform-support/mips-mti-none-elf.md +++ b/src/doc/rustc/src/platform-support/mips-mti-none-elf.md @@ -9,7 +9,7 @@ MIPS32r2 baremetal softfloat, Big Endian or Little Endian. ## Target maintainers -- YunQiang Su, `syq@debian.org`, https://github.com/wzssyqa +[@wzssyqa](https://github.com/wzssyqa) ## Background diff --git a/src/doc/rustc/src/platform-support/mips-release-6.md b/src/doc/rustc/src/platform-support/mips-release-6.md index 9203a31e9f4e7..77f495751c15d 100644 --- a/src/doc/rustc/src/platform-support/mips-release-6.md +++ b/src/doc/rustc/src/platform-support/mips-release-6.md @@ -16,10 +16,10 @@ The target name follow this format: `--`, where ## Target Maintainers -- [Xuan Chen](https://github.com/chenx97) -- [Walter Ji](https://github.com/709924470) -- [Xinhui Yang](https://github.com/Cyanoxygen) -- [Lain Yang](https://github.com/Fearyncess) +[@chenx97](https://github.com/chenx97) +[@709924470](https://github.com/709924470) +[@Cyanoxygen](https://github.com/Cyanoxygen) +[@Fearyncess](https://github.com/Fearyncess) ## Requirements @@ -67,7 +67,7 @@ The following procedure outlines the build process for the MIPS64 R6 target with ### Prerequisite: Disable debuginfo -An LLVM bug makes rustc crash if debug or debug info generation is enabled. You need to edit `config.toml` to disable this: +An LLVM bug makes rustc crash if debug or debug info generation is enabled. You need to edit `bootstrap.toml` to disable this: ```toml [rust] @@ -83,7 +83,7 @@ The crate `rustix` may try to link itself against MIPS R2 assembly, resulting in export RUSTFLAGS="--cfg rustix_use_libc" ``` -This will trigger warnings during build, as `-D warnings` is enabled by default. Disable `-D warnings` by editing `config.toml` to append the following: +This will trigger warnings during build, as `-D warnings` is enabled by default. Disable `-D warnings` by editing `bootstrap.toml` to append the following: ```toml [rust] @@ -116,7 +116,7 @@ target = ["mipsisa64r6el-unknown-linux-gnuabi64"] Make sure that `mipsisa64r6el-unknown-linux-gnuabi64-gcc` is available from your executable search path (`$PATH`). -Alternatively, you can specify the directories to all necessary toolchain executables in `config.toml`: +Alternatively, you can specify the directories to all necessary toolchain executables in `bootstrap.toml`: ```toml [target.mipsisa64r6el-unknown-linux-gnuabi64] diff --git a/src/doc/rustc/src/platform-support/mips64-openwrt-linux-musl.md b/src/doc/rustc/src/platform-support/mips64-openwrt-linux-musl.md index 07470eef051d8..2ad33c9e20de6 100644 --- a/src/doc/rustc/src/platform-support/mips64-openwrt-linux-musl.md +++ b/src/doc/rustc/src/platform-support/mips64-openwrt-linux-musl.md @@ -2,7 +2,8 @@ **Tier: 3** ## Target maintainers -- Donald Hoskins `grommish@gmail.com`, https://github.com/Itus-Shield + +[@Itus-Shield](https://github.com/Itus-Shield) ## Requirements This target is cross-compiled. There is no support for `std`. There is no diff --git a/src/doc/rustc/src/platform-support/mipsel-sony-psx.md b/src/doc/rustc/src/platform-support/mipsel-sony-psx.md index 589100e8888b3..2343df227f5db 100644 --- a/src/doc/rustc/src/platform-support/mipsel-sony-psx.md +++ b/src/doc/rustc/src/platform-support/mipsel-sony-psx.md @@ -6,7 +6,7 @@ Sony PlayStation 1 (psx) ## Designated Developer -* [@ayrtonm](https://github.com/ayrtonm) +[@ayrtonm](https://github.com/ayrtonm) ## Requirements diff --git a/src/doc/rustc/src/platform-support/mipsel-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/mipsel-unknown-linux-gnu.md new file mode 100644 index 0000000000000..eed0ce4437ac5 --- /dev/null +++ b/src/doc/rustc/src/platform-support/mipsel-unknown-linux-gnu.md @@ -0,0 +1,28 @@ +# `mipsel-unknown-linux-gnu` + +**Tier: 3** + +Little-endian 32 bit MIPS for Linux with `glibc. + +## Target maintainers + +[@LukasWoodtli](https://github.com/LukasWoodtli) + +## Requirements + +The target supports std on Linux. Host tools are supported but not tested. + + +## Building the target + +For cross compilation the GNU C compiler for the mipsel architecture needs to +be installed. On Ubuntu install the packets: `gcc-mipsel-linux-gnu` and +`g++-mipsel-linux-gnu`. + +Add `mipsel-unknown-linux-gnu` as `target` list in `config.toml`. + +## Building Rust programs + +Rust does not ship pre-compiled artifacts for this target. To compile for +this target, you will need to build Rust with the target enabled (see +"Building the target" above). diff --git a/src/doc/rustc/src/platform-support/netbsd.md b/src/doc/rustc/src/platform-support/netbsd.md index ef9337befa643..9040ef637be3d 100644 --- a/src/doc/rustc/src/platform-support/netbsd.md +++ b/src/doc/rustc/src/platform-support/netbsd.md @@ -31,10 +31,13 @@ are built for NetBSD 9.x, although some exceptions exist (some are built for NetBSD 8.x but also work on newer OS versions). -## Designated Developers +## Target Maintainers -- [@he32](https://github.com/he32), `he@NetBSD.org` -- [NetBSD/pkgsrc-wip's rust](https://github.com/NetBSD/pkgsrc-wip/blob/master/rust/Makefile) maintainer (see MAINTAINER variable). This package is part of "pkgsrc work-in-progress" and is used for deployment and testing of new versions of rust +[@he32](https://github.com/he32) + +Further contacts: + +- [NetBSD/pkgsrc-wip's rust](https://github.com/NetBSD/pkgsrc-wip/blob/master/rust185/Makefile) maintainer (see MAINTAINER variable). This package is part of "pkgsrc work-in-progress" and is used for deployment and testing of new versions of rust - [NetBSD's pkgsrc lang/rust](https://github.com/NetBSD/pkgsrc/tree/trunk/lang/rust) for the "proper" package in pkgsrc. - [NetBSD's pkgsrc lang/rust-bin](https://github.com/NetBSD/pkgsrc/tree/trunk/lang/rust-bin) which re-uses the bootstrap kit as a binary distribution and therefore avoids the rather protracted native build time of rust itself @@ -46,7 +49,7 @@ bug reporting system. The `x86_64-unknown-netbsd` artifacts is being distributed by the rust project. -The other targets are built by the designated developers (see above), +The other targets are built by the target maintainers (see above), and the targets are initially cross-compiled, but many if not most of them are also built natively as part of testing. diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md index 77e8caaee4cdc..9f8960899c169 100644 --- a/src/doc/rustc/src/platform-support/nto-qnx.md +++ b/src/doc/rustc/src/platform-support/nto-qnx.md @@ -13,10 +13,10 @@ and [QNX][qnx.com]. ## Target maintainers -- Florian Bartels, `Florian.Bartels@elektrobit.com`, https://github.com/flba-eb -- Tristan Roach, `TRoach@blackberry.com`, https://github.com/gh-tr -- Jonathan Pallant `Jonathan.Pallant@ferrous-systems.com`, https://github.com/jonathanpallant -- Jorge Aparicio `Jorge.Aparicio@ferrous-systems.com`, https://github.com/japaric +[@flba-eb](https://github.com/flba-eb) +[@gh-tr](https://github.com/gh-tr) +[@jonathanpallant](https://github.com/jonathanpallant) +[@japaric](https://github.com/japaric) ## Requirements @@ -124,7 +124,7 @@ For conditional compilation, following QNX specific attributes are defined: ## Building the target -1. Create a `config.toml` +1. Create a `bootstrap.toml` Example content: diff --git a/src/doc/rustc/src/platform-support/nuttx.md b/src/doc/rustc/src/platform-support/nuttx.md index f76fe0887b5dd..df3f4e7b394ed 100644 --- a/src/doc/rustc/src/platform-support/nuttx.md +++ b/src/doc/rustc/src/platform-support/nuttx.md @@ -12,7 +12,7 @@ For brevity, many parts of the documentation will refer to Apache NuttX as simpl ## Target maintainers -- Qi Huang [@no1wudi](https://github.com/no1wudi) +[@no1wudi](https://github.com/no1wudi) ## Requirements diff --git a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md index 1af1410d4bb60..106ec562bfc79 100644 --- a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md +++ b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md @@ -7,8 +7,8 @@ platform. ## Target maintainers -- Riccardo D'Ambrosio, https://github.com/RDambrosio016 -- Kjetil Kjeka, https://github.com/kjetilkjeka +[@RDambrosio016](https://github.com/RDambrosio016) +[@kjetilkjeka](https://github.com/kjetilkjeka) + + + + + + + + + + + + + @@ -279,7 +292,49 @@ - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/etc/lldb_batchmode.py b/src/etc/lldb_batchmode.py index cf88c53e085e1..5cc9bb23628f0 100644 --- a/src/etc/lldb_batchmode.py +++ b/src/etc/lldb_batchmode.py @@ -40,7 +40,7 @@ def print_debug(s): def normalize_whitespace(s): """Replace newlines, tabs, multiple spaces, etc with exactly one space""" - return re.sub("\s+", " ", s) + return re.sub(r"\s+", " ", s) def breakpoint_callback(frame, bp_loc, dict): @@ -234,7 +234,7 @@ def watchdog(): if ( command == "run" or command == "r" - or re.match("^process\s+launch.*", command) + or re.match(r"^process\s+launch.*", command) ): # Before starting to run the program, let the thread sleep a bit, so all # breakpoint added events can be processed diff --git a/src/etc/natvis/libcore.natvis b/src/etc/natvis/libcore.natvis index 8a441cf209356..8fe87a999895c 100644 --- a/src/etc/natvis/libcore.natvis +++ b/src/etc/natvis/libcore.natvis @@ -69,9 +69,9 @@ - Pin({(void*)__pointer}: {__pointer}) + Pin({(void*)pointer}: {pointer}) - __pointer + pointer diff --git a/src/etc/pre-push.sh b/src/etc/pre-push.sh index 6f86c7ab8a448..7bacc943f258b 100755 --- a/src/etc/pre-push.sh +++ b/src/etc/pre-push.sh @@ -7,6 +7,20 @@ set -Euo pipefail +# Check if the push is doing anything other than deleting remote branches +SKIP=true +while read LOCAL_REF LOCAL_SHA REMOTE_REF REMOTE_SHA; do + if [[ "$LOCAL_REF" != "(delete)" || \ + "$LOCAL_SHA" != "0000000000000000000000000000000000000000" ]]; then + SKIP=false + fi +done + +if $SKIP; then + echo "Skipping tidy check for branch deletion" + exit 0 +fi + ROOT_DIR="$(git rev-parse --show-toplevel)" echo "Running pre-push script $ROOT_DIR/x test tidy" diff --git a/src/etc/rust_analyzer_eglot.el b/src/etc/rust_analyzer_eglot.el index 7b4309f8e188f..90bd38aa89477 100644 --- a/src/etc/rust_analyzer_eglot.el +++ b/src/etc/rust_analyzer_eglot.el @@ -8,11 +8,11 @@ "check" "--json-output"]) :linkedProjects ["Cargo.toml" - "src/tools/x/Cargo.toml" - "src/bootstrap/Cargo.toml" - "src/tools/rust-analyzer/Cargo.toml" "compiler/rustc_codegen_cranelift/Cargo.toml" - "compiler/rustc_codegen_gcc/Cargo.toml"] + "compiler/rustc_codegen_gcc/Cargo.toml" + "library/Cargo.toml" + "src/bootstrap/Cargo.toml" + "src/tools/rust-analyzer/Cargo.toml"] :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt" "--edition=2021"]) :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv" diff --git a/src/etc/rust_analyzer_helix.toml b/src/etc/rust_analyzer_helix.toml index afddd089eb168..05fc7716a7252 100644 --- a/src/etc/rust_analyzer_helix.toml +++ b/src/etc/rust_analyzer_helix.toml @@ -15,7 +15,6 @@ linkedProjects = [ "library/Cargo.toml", "src/bootstrap/Cargo.toml", "src/tools/rust-analyzer/Cargo.toml", - "src/tools/x/Cargo.toml", ] [language-server.rust-analyzer.config.check] diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json index b104356d44cb5..5ce886a9b6594 100644 --- a/src/etc/rust_analyzer_settings.json +++ b/src/etc/rust_analyzer_settings.json @@ -9,12 +9,11 @@ ], "rust-analyzer.linkedProjects": [ "Cargo.toml", + "compiler/rustc_codegen_cranelift/Cargo.toml", + "compiler/rustc_codegen_gcc/Cargo.toml", "library/Cargo.toml", - "src/tools/x/Cargo.toml", "src/bootstrap/Cargo.toml", - "src/tools/rust-analyzer/Cargo.toml", - "compiler/rustc_codegen_cranelift/Cargo.toml", - "compiler/rustc_codegen_gcc/Cargo.toml" + "src/tools/rust-analyzer/Cargo.toml" ], "rust-analyzer.rustfmt.overrideCommand": [ "${workspaceFolder}/build/host/rustfmt/bin/rustfmt", @@ -37,5 +36,10 @@ }, "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "nightly" + }, + "files.associations": { + "*.fixed": "rust", + "*.pp": "rust", + "*.mir": "rust" } } diff --git a/src/etc/rust_analyzer_zed.json b/src/etc/rust_analyzer_zed.json index 469ea0506218f..3461ff887d9b4 100644 --- a/src/etc/rust_analyzer_zed.json +++ b/src/etc/rust_analyzer_zed.json @@ -21,16 +21,15 @@ }, "linkedProjects": [ "Cargo.toml", + "compiler/rustc_codegen_cranelift/Cargo.toml", + "compiler/rustc_codegen_gcc/Cargo.toml", "library/Cargo.toml", - "src/tools/x/Cargo.toml", "src/bootstrap/Cargo.toml", - "src/tools/rust-analyzer/Cargo.toml", - "compiler/rustc_codegen_cranelift/Cargo.toml", - "compiler/rustc_codegen_gcc/Cargo.toml" + "src/tools/rust-analyzer/Cargo.toml" ], "procMacro": { - "enable": true, - "server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv" + "enable": true, + "server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv" }, "rustc": { "source": "./Cargo.toml" @@ -48,5 +47,8 @@ } } } + }, + "file_types": { + "Rust": ["fixed", "pp", "mir"] } } diff --git a/src/etc/test-float-parse/Cargo.toml b/src/etc/test-float-parse/Cargo.toml index 56cb5cddeea52..e407e322f9e19 100644 --- a/src/etc/test-float-parse/Cargo.toml +++ b/src/etc/test-float-parse/Cargo.toml @@ -1,15 +1,22 @@ [package] name = "test-float-parse" version = "0.1.0" -edition = "2021" +edition = "2024" publish = false [dependencies] indicatif = { version = "0.17.8", default-features = false } num = "0.4.3" -rand = "0.8.5" -rand_chacha = "0.3" +rand = "0.9.0" +rand_chacha = "0.9.0" rayon = "1" [lib] name = "test_float_parse" + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + # Internal features aren't marked known config by default + 'cfg(target_has_reliable_f16)', +] diff --git a/src/etc/test-float-parse/src/gen/exhaustive.rs b/src/etc/test-float-parse/src/gen/exhaustive.rs deleted file mode 100644 index 5d4b6df8e59b9..0000000000000 --- a/src/etc/test-float-parse/src/gen/exhaustive.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::fmt::Write; -use std::ops::RangeInclusive; - -use crate::{Float, Generator, Int}; - -/// Test every possible bit pattern. This is infeasible to run on any float types larger than -/// `f32` (which takes about an hour). -pub struct Exhaustive { - iter: RangeInclusive, -} - -impl Generator for Exhaustive -where - RangeInclusive: Iterator, -{ - const NAME: &'static str = "exhaustive"; - const SHORT_NAME: &'static str = "exhaustive"; - - type WriteCtx = F; - - fn total_tests() -> u64 { - F::Int::MAX.try_into().unwrap_or(u64::MAX) - } - - fn new() -> Self { - Self { iter: F::Int::ZERO..=F::Int::MAX } - } - - fn write_string(s: &mut String, ctx: Self::WriteCtx) { - write!(s, "{ctx:e}").unwrap(); - } -} - -impl Iterator for Exhaustive -where - RangeInclusive: Iterator, -{ - type Item = F; - - fn next(&mut self) -> Option { - Some(F::from_bits(self.iter.next()?)) - } -} diff --git a/src/etc/test-float-parse/src/gen_/exhaustive.rs b/src/etc/test-float-parse/src/gen_/exhaustive.rs new file mode 100644 index 0000000000000..01458fb0b6084 --- /dev/null +++ b/src/etc/test-float-parse/src/gen_/exhaustive.rs @@ -0,0 +1,42 @@ +use std::fmt::Write; +use std::ops::RangeInclusive; + +use crate::{Float, Generator, Int}; + +/// Test every possible bit pattern. This is infeasible to run on any float types larger than +/// `f32` (which takes about an hour). +pub struct Exhaustive { + iter: RangeInclusive, +} + +impl Generator for Exhaustive +where + RangeInclusive: Iterator, +{ + const SHORT_NAME: &'static str = "exhaustive"; + + type WriteCtx = F; + + fn total_tests() -> u64 { + 1u64.checked_shl(F::Int::BITS).expect("More than u64::MAX tests") + } + + fn new() -> Self { + Self { iter: F::Int::ZERO..=F::Int::MAX } + } + + fn write_string(s: &mut String, ctx: Self::WriteCtx) { + write!(s, "{ctx:e}").unwrap(); + } +} + +impl Iterator for Exhaustive +where + RangeInclusive: Iterator, +{ + type Item = F; + + fn next(&mut self) -> Option { + Some(F::from_bits(self.iter.next()?)) + } +} diff --git a/src/etc/test-float-parse/src/gen/exponents.rs b/src/etc/test-float-parse/src/gen_/exponents.rs similarity index 100% rename from src/etc/test-float-parse/src/gen/exponents.rs rename to src/etc/test-float-parse/src/gen_/exponents.rs diff --git a/src/etc/test-float-parse/src/gen/fuzz.rs b/src/etc/test-float-parse/src/gen_/fuzz.rs similarity index 91% rename from src/etc/test-float-parse/src/gen/fuzz.rs rename to src/etc/test-float-parse/src/gen_/fuzz.rs index 0c63e8aae26ea..1d6c5562a14c8 100644 --- a/src/etc/test-float-parse/src/gen/fuzz.rs +++ b/src/etc/test-float-parse/src/gen_/fuzz.rs @@ -6,7 +6,7 @@ use std::ops::Range; use std::sync::Mutex; use rand::Rng; -use rand::distributions::{Distribution, Standard}; +use rand::distr::{Distribution, StandardUniform}; use rand_chacha::ChaCha8Rng; use rand_chacha::rand_core::SeedableRng; @@ -47,9 +47,8 @@ impl Fuzz { impl Generator for Fuzz where - Standard: Distribution<::Int>, + StandardUniform: Distribution<::Int>, { - const NAME: &'static str = "fuzz"; const SHORT_NAME: &'static str = "fuzz"; type WriteCtx = F; @@ -75,13 +74,13 @@ where impl Iterator for Fuzz where - Standard: Distribution<::Int>, + StandardUniform: Distribution<::Int>, { type Item = >::WriteCtx; fn next(&mut self) -> Option { let _ = self.iter.next()?; - let i: F::Int = self.rng.gen(); + let i: F::Int = self.rng.random(); Some(F::from_bits(i)) } diff --git a/src/etc/test-float-parse/src/gen/integers.rs b/src/etc/test-float-parse/src/gen_/integers.rs similarity index 100% rename from src/etc/test-float-parse/src/gen/integers.rs rename to src/etc/test-float-parse/src/gen_/integers.rs diff --git a/src/etc/test-float-parse/src/gen/long_fractions.rs b/src/etc/test-float-parse/src/gen_/long_fractions.rs similarity index 100% rename from src/etc/test-float-parse/src/gen/long_fractions.rs rename to src/etc/test-float-parse/src/gen_/long_fractions.rs diff --git a/src/etc/test-float-parse/src/gen/many_digits.rs b/src/etc/test-float-parse/src/gen_/many_digits.rs similarity index 80% rename from src/etc/test-float-parse/src/gen/many_digits.rs rename to src/etc/test-float-parse/src/gen_/many_digits.rs index aab8d5d704bec..741e11437fe29 100644 --- a/src/etc/test-float-parse/src/gen/many_digits.rs +++ b/src/etc/test-float-parse/src/gen_/many_digits.rs @@ -3,7 +3,7 @@ use std::fmt::Write; use std::marker::PhantomData; use std::ops::{Range, RangeInclusive}; -use rand::distributions::{Distribution, Uniform}; +use rand::distr::{Distribution, Uniform}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; @@ -40,7 +40,7 @@ impl Generator for RandDigits { fn new() -> Self { let rng = ChaCha8Rng::from_seed(SEED); - let range = Uniform::from(0..10); + let range = Uniform::try_from(0..10).unwrap(); Self { rng, iter: 0..ITERATIONS, uniform: range, marker: PhantomData } } @@ -55,11 +55,11 @@ impl Iterator for RandDigits { fn next(&mut self) -> Option { let _ = self.iter.next()?; - let num_digits = self.rng.gen_range(POSSIBLE_NUM_DIGITS); - let has_decimal = self.rng.gen_bool(0.2); - let has_exp = self.rng.gen_bool(0.2); + let num_digits = self.rng.random_range(POSSIBLE_NUM_DIGITS); + let has_decimal = self.rng.random_bool(0.2); + let has_exp = self.rng.random_bool(0.2); - let dec_pos = if has_decimal { Some(self.rng.gen_range(0..num_digits)) } else { None }; + let dec_pos = if has_decimal { Some(self.rng.random_range(0..num_digits)) } else { None }; let mut s = String::with_capacity(num_digits); @@ -75,7 +75,7 @@ impl Iterator for RandDigits { } if has_exp { - let exp = self.rng.gen_range(EXP_RANGE); + let exp = self.rng.random_range(EXP_RANGE); write!(s, "e{exp}").unwrap(); } diff --git a/src/etc/test-float-parse/src/gen/sparse.rs b/src/etc/test-float-parse/src/gen_/sparse.rs similarity index 97% rename from src/etc/test-float-parse/src/gen/sparse.rs rename to src/etc/test-float-parse/src/gen_/sparse.rs index 389b71056a3e5..72b65d4ce7f10 100644 --- a/src/etc/test-float-parse/src/gen/sparse.rs +++ b/src/etc/test-float-parse/src/gen_/sparse.rs @@ -35,7 +35,6 @@ impl Generator for FewOnesInt where >::Error: std::fmt::Debug, { - const NAME: &'static str = "few ones int"; const SHORT_NAME: &'static str = "few ones int"; type WriteCtx = F::Int; diff --git a/src/etc/test-float-parse/src/gen/spot_checks.rs b/src/etc/test-float-parse/src/gen_/spot_checks.rs similarity index 100% rename from src/etc/test-float-parse/src/gen/spot_checks.rs rename to src/etc/test-float-parse/src/gen_/spot_checks.rs diff --git a/src/etc/test-float-parse/src/gen/subnorm.rs b/src/etc/test-float-parse/src/gen_/subnorm.rs similarity index 91% rename from src/etc/test-float-parse/src/gen/subnorm.rs rename to src/etc/test-float-parse/src/gen_/subnorm.rs index 4fe3b90a3ddf4..654f324b9b011 100644 --- a/src/etc/test-float-parse/src/gen/subnorm.rs +++ b/src/etc/test-float-parse/src/gen_/subnorm.rs @@ -1,4 +1,3 @@ -use std::cmp::min; use std::fmt::Write; use std::ops::RangeInclusive; @@ -83,7 +82,13 @@ where } fn new() -> Self { - Self { iter: F::Int::ZERO..=min(F::Int::ONE << 22, F::MAN_BITS.try_into().unwrap()) } + let upper_lim = if F::MAN_BITS >= 22 { + F::Int::ONE << 22 + } else { + (F::Int::ONE << F::MAN_BITS) - F::Int::ONE + }; + + Self { iter: F::Int::ZERO..=upper_lim } } fn write_string(s: &mut String, ctx: Self::WriteCtx) { diff --git a/src/etc/test-float-parse/src/lib.rs b/src/etc/test-float-parse/src/lib.rs index 3c71b0dc32e74..0bd4878f9a626 100644 --- a/src/etc/test-float-parse/src/lib.rs +++ b/src/etc/test-float-parse/src/lib.rs @@ -1,23 +1,27 @@ +#![feature(f16)] +#![feature(cfg_target_has_reliable_f16_f128)] +#![expect(internal_features)] // reliable_f16_f128 + mod traits; mod ui; mod validate; -use std::any::{TypeId, type_name}; +use std::any::type_name; use std::cmp::min; use std::ops::RangeInclusive; use std::process::ExitCode; +use std::sync::OnceLock; use std::sync::atomic::{AtomicU64, Ordering}; -use std::sync::{OnceLock, mpsc}; use std::{fmt, time}; -use indicatif::{MultiProgress, ProgressBar}; -use rand::distributions::{Distribution, Standard}; +use rand::distr::{Distribution, StandardUniform}; use rayon::prelude::*; use time::{Duration, Instant}; use traits::{Float, Generator, Int}; +use validate::CheckError; /// Test generators. -mod gen { +mod gen_ { pub mod exhaustive; pub mod exponents; pub mod fuzz; @@ -43,7 +47,7 @@ const HUGE_TEST_CUTOFF: u64 = 5_000_000; /// Seed for tests that use a deterministic RNG. const SEED: [u8; 32] = *b"3.141592653589793238462643383279"; -/// Global configuration +/// Global configuration. #[derive(Debug)] pub struct Config { pub timeout: Duration, @@ -104,9 +108,9 @@ pub fn run(cfg: Config, include: &[String], exclude: &[String]) -> ExitCode { println!("Skipping test '{exc}'"); } - println!("launching"); + println!("Launching all"); let elapsed = launch_tests(&mut tests, &cfg); - ui::finish(&tests, elapsed, &cfg) + ui::finish_all(&tests, elapsed, &cfg) } /// Enumerate tests to run but don't actually run them. @@ -114,6 +118,9 @@ pub fn register_tests(cfg: &Config) -> Vec { let mut tests = Vec::new(); // Register normal generators for all floats. + + #[cfg(target_has_reliable_f16)] + register_float::(&mut tests, cfg); register_float::(&mut tests, cfg); register_float::(&mut tests, cfg); @@ -132,46 +139,46 @@ fn register_float(tests: &mut Vec, cfg: &Config) where RangeInclusive: Iterator, >::Error: std::fmt::Debug, - Standard: Distribution<::Int>, + StandardUniform: Distribution<::Int>, { if F::BITS <= MAX_BITS_FOR_EXHAUUSTIVE { // Only run exhaustive tests if there is a chance of completion. - TestInfo::register::>(tests); + TestInfo::register::>(tests); } - gen::fuzz::Fuzz::::set_iterations(cfg.fuzz_count); - - TestInfo::register::>(tests); - TestInfo::register::>(tests); - TestInfo::register::>(tests); - TestInfo::register::>(tests); - TestInfo::register::(tests); - TestInfo::register::(tests); - TestInfo::register::>(tests); - TestInfo::register::>(tests); - TestInfo::register::>(tests); - TestInfo::register::(tests); - TestInfo::register::(tests); - TestInfo::register::>(tests); - TestInfo::register::>(tests); + gen_::fuzz::Fuzz::::set_iterations(cfg.fuzz_count); + + TestInfo::register::>(tests); + TestInfo::register::>(tests); + TestInfo::register::>(tests); + TestInfo::register::>(tests); + TestInfo::register::(tests); + TestInfo::register::(tests); + TestInfo::register::>(tests); + TestInfo::register::>(tests); + TestInfo::register::>(tests); + TestInfo::register::(tests); + TestInfo::register::(tests); + TestInfo::register::>(tests); + TestInfo::register::>(tests); } /// Configuration for a single test. #[derive(Debug)] pub struct TestInfo { pub name: String, - /// Tests are identified by the type ID of `(F, G)` (tuple of the float and generator type). - /// This gives an easy way to associate messages with tests. - id: TypeId, float_name: &'static str, + float_bits: u32, gen_name: &'static str, /// Name for display in the progress bar. short_name: String, + /// Pad the short name to a common width for progress bar use. + short_name_padded: String, total_tests: u64, /// Function to launch this test. - launch: fn(&mpsc::Sender, &TestInfo, &Config), + launch: fn(&TestInfo, &Config), /// Progress bar to be updated. - pb: Option, + progress: Option, /// Once completed, this will be set. completed: OnceLock, } @@ -187,14 +194,18 @@ impl TestInfo { let f_name = type_name::(); let gen_name = G::NAME; let gen_short_name = G::SHORT_NAME; + let name = format!("{f_name} {gen_name}"); + let short_name = format!("{f_name} {gen_short_name}"); + let short_name_padded = format!("{short_name:18}"); let info = TestInfo { - id: TypeId::of::<(F, G)>(), float_name: f_name, + float_bits: F::BITS, gen_name, - pb: None, - name: format!("{f_name} {gen_name}"), - short_name: format!("{f_name} {gen_short_name}"), + progress: None, + name, + short_name_padded, + short_name, launch: test_runner::, total_tests: G::total_tests(), completed: OnceLock::new(), @@ -202,106 +213,18 @@ impl TestInfo { v.push(info); } - /// Pad the short name to a common width for progress bar use. - fn short_name_padded(&self) -> String { - format!("{:18}", self.short_name) - } - - /// Create a progress bar for this test within a multiprogress bar. - fn register_pb(&mut self, mp: &MultiProgress, drop_bars: &mut Vec) { - self.pb = Some(ui::create_pb(mp, self.total_tests, &self.short_name_padded(), drop_bars)); - } - - /// When the test is finished, update progress bar messages and finalize. - fn finalize_pb(&self, c: &Completed) { - let pb = self.pb.as_ref().unwrap(); - ui::finalize_pb(pb, &self.short_name_padded(), c); - } - /// True if this should be run after all others. fn is_huge_test(&self) -> bool { self.total_tests >= HUGE_TEST_CUTOFF } -} - -/// A message sent from test runner threads to the UI/log thread. -#[derive(Clone, Debug)] -struct Msg { - id: TypeId, - update: Update, -} - -impl Msg { - /// Wrap an `Update` into a message for the specified type. We use the `TypeId` of `(F, G)` to - /// identify which test a message in the channel came from. - fn new>(u: Update) -> Self { - Self { id: TypeId::of::<(F, G)>(), update: u } - } - - /// Get the matching test from a list. Panics if not found. - fn find_test<'a>(&self, tests: &'a [TestInfo]) -> &'a TestInfo { - tests.iter().find(|t| t.id == self.id).unwrap() - } - - /// Update UI as needed for a single message received from the test runners. - fn handle(self, tests: &[TestInfo], mp: &MultiProgress) { - let test = self.find_test(tests); - let pb = test.pb.as_ref().unwrap(); - match self.update { - Update::Started => { - mp.println(format!("Testing '{}'", test.name)).unwrap(); - } - Update::Progress { executed, failures } => { - pb.set_message(format! {"{failures}"}); - pb.set_position(executed); - } - Update::Failure { fail, input, float_res } => { - mp.println(format!( - "Failure in '{}': {fail}. parsing '{input}'. Parsed as: {float_res}", - test.name - )) - .unwrap(); - } - Update::Completed(c) => { - test.finalize_pb(&c); - - let prefix = match c.result { - Ok(FinishedAll) => "Completed tests for", - Err(EarlyExit::Timeout) => "Timed out", - Err(EarlyExit::MaxFailures) => "Max failures reached for", - }; - - mp.println(format!( - "{prefix} generator '{}' in {:?}. {} tests run, {} failures", - test.name, c.elapsed, c.executed, c.failures - )) - .unwrap(); - test.completed.set(c).unwrap(); - } - }; + /// When the test is finished, update progress bar messages and finalize. + fn complete(&self, c: Completed) { + self.progress.as_ref().unwrap().complete(&c, 0); + self.completed.set(c).unwrap(); } } -/// Status sent with a message. -#[derive(Clone, Debug)] -enum Update { - /// Starting a new test runner. - Started, - /// Completed a out of b tests. - Progress { executed: u64, failures: u64 }, - /// Received a failed test. - Failure { - fail: CheckFailure, - /// String for which parsing was attempted. - input: Box, - /// The parsed & decomposed `FloatRes`, already stringified so we don't need generics here. - float_res: Box, - }, - /// Exited with an unexpected condition. - Completed(Completed), -} - /// Result of an input did not parsing successfully. #[derive(Clone, Debug)] enum CheckFailure { @@ -329,6 +252,10 @@ enum CheckFailure { /// two representable values. incorrect_midpoint_rounding: bool, }, + /// String did not parse successfully. + ParsingFailed(Box), + /// A panic was caught. + Panic(Box), } impl fmt::Display for CheckFailure { @@ -363,6 +290,8 @@ impl fmt::Display for CheckFailure { } Ok(()) } + CheckFailure::ParsingFailed(e) => write!(f, "parsing failed: {e}"), + CheckFailure::Panic(e) => write!(f, "function panicked: {e}"), } } } @@ -398,55 +327,21 @@ enum EarlyExit { /// This launches a main thread that receives messages and handlees UI updates, and uses the /// rest of the thread pool to execute the tests. fn launch_tests(tests: &mut [TestInfo], cfg: &Config) -> Duration { - // Run shorter tests first - tests.sort_unstable_by_key(|test| test.total_tests); + // Run shorter tests and smaller float types first. + tests.sort_unstable_by_key(|test| (test.total_tests, test.float_bits)); for test in tests.iter() { println!("Launching test '{}'", test.name); } - // Configure progress bars let mut all_progress_bars = Vec::new(); - let mp = MultiProgress::new(); - mp.set_move_cursor(true); - for test in tests.iter_mut() { - test.register_pb(&mp, &mut all_progress_bars); - } - - ui::set_panic_hook(all_progress_bars); - - let (tx, rx) = mpsc::channel::(); let start = Instant::now(); - rayon::scope(|scope| { - // Thread that updates the UI - scope.spawn(|_scope| { - let rx = rx; // move rx - - loop { - if tests.iter().all(|t| t.completed.get().is_some()) { - break; - } - - let msg = rx.recv().unwrap(); - msg.handle(tests, &mp); - } - - // All tests completed; finish things up - drop(mp); - assert_eq!(rx.try_recv().unwrap_err(), mpsc::TryRecvError::Empty); - }); - - // Don't let the thread pool be starved by huge tests. Run faster tests first in parallel, - // then parallelize only within the rest of the tests. - let (huge_tests, normal_tests): (Vec<_>, Vec<_>) = - tests.iter().partition(|t| t.is_huge_test()); - - // Run the actual tests - normal_tests.par_iter().for_each(|test| ((test.launch)(&tx, test, cfg))); - - huge_tests.par_iter().for_each(|test| ((test.launch)(&tx, test, cfg))); - }); + for test in tests.iter_mut() { + test.progress = Some(ui::Progress::new(test, &mut all_progress_bars)); + ui::set_panic_hook(&all_progress_bars); + ((test.launch)(test, cfg)); + } start.elapsed() } @@ -454,15 +349,12 @@ fn launch_tests(tests: &mut [TestInfo], cfg: &Config) -> Duration { /// Test runer for a single generator. /// /// This calls the generator's iterator multiple times (in parallel) and validates each output. -fn test_runner>(tx: &mpsc::Sender, _info: &TestInfo, cfg: &Config) { - tx.send(Msg::new::(Update::Started)).unwrap(); - - let total = G::total_tests(); - let gen = G::new(); +fn test_runner>(test: &TestInfo, cfg: &Config) { + let gen_ = G::new(); let executed = AtomicU64::new(0); let failures = AtomicU64::new(0); - let checks_per_update = min(total, 1000); + let checks_per_update = min(test.total_tests, 1000); let started = Instant::now(); // Function to execute for a single test iteration. @@ -474,7 +366,12 @@ fn test_runner>(tx: &mpsc::Sender, _info: &TestIn match validate::validate::(buf) { Ok(()) => (), Err(e) => { - tx.send(Msg::new::(e)).unwrap(); + let CheckError { fail, input, float_res } = e; + test.progress.as_ref().unwrap().println(&format!( + "Failure in '{}': {fail}. parsing '{input}'. Parsed as: {float_res}", + test.name + )); + let f = failures.fetch_add(1, Ordering::Relaxed); // End early if the limit is exceeded. if f >= cfg.max_failures { @@ -486,9 +383,7 @@ fn test_runner>(tx: &mpsc::Sender, _info: &TestIn // Send periodic updates if executed % checks_per_update == 0 { let failures = failures.load(Ordering::Relaxed); - - tx.send(Msg::new::(Update::Progress { executed, failures })).unwrap(); - + test.progress.as_ref().unwrap().update(executed, failures); if started.elapsed() > cfg.timeout { return Err(EarlyExit::Timeout); } @@ -499,15 +394,19 @@ fn test_runner>(tx: &mpsc::Sender, _info: &TestIn // Run the test iterations in parallel. Each thread gets a string buffer to write // its check values to. - let res = gen.par_bridge().try_for_each_init(|| String::with_capacity(100), check_one); + let res = gen_.par_bridge().try_for_each_init(String::new, check_one); let elapsed = started.elapsed(); let executed = executed.into_inner(); let failures = failures.into_inner(); // Warn about bad estimates if relevant. - let warning = if executed != total && res.is_ok() { - let msg = format!("executed tests != estimated ({executed} != {total}) for {}", G::NAME); + let warning = if executed != test.total_tests && res.is_ok() { + let msg = format!( + "executed tests != estimated ({executed} != {}) for {}", + test.total_tests, + G::NAME + ); Some(msg.into()) } else { @@ -515,12 +414,5 @@ fn test_runner>(tx: &mpsc::Sender, _info: &TestIn }; let result = res.map(|()| FinishedAll); - tx.send(Msg::new::(Update::Completed(Completed { - executed, - failures, - result, - warning, - elapsed, - }))) - .unwrap(); + test.complete(Completed { executed, failures, result, warning, elapsed }); } diff --git a/src/etc/test-float-parse/src/traits.rs b/src/etc/test-float-parse/src/traits.rs index f5333d63b3693..65a8721bfa5cd 100644 --- a/src/etc/test-float-parse/src/traits.rs +++ b/src/etc/test-float-parse/src/traits.rs @@ -98,7 +98,7 @@ macro_rules! impl_int { } } -impl_int!(u32, i32; u64, i64); +impl_int!(u16, i16; u32, i32; u64, i64); /// Floating point types. pub trait Float: @@ -147,12 +147,12 @@ pub trait Float: } macro_rules! impl_float { - ($($fty:ty, $ity:ty, $bits:literal);+) => { + ($($fty:ty, $ity:ty);+) => { $( impl Float for $fty { type Int = $ity; type SInt = ::Signed; - const BITS: u32 = $bits; + const BITS: u32 = <$ity>::BITS; const MAN_BITS: u32 = Self::MANTISSA_DIGITS - 1; const MAN_MASK: Self::Int = (Self::Int::ONE << Self::MAN_BITS) - Self::Int::ONE; const SIGN_MASK: Self::Int = Self::Int::ONE << (Self::BITS-1); @@ -168,7 +168,10 @@ macro_rules! impl_float { } } -impl_float!(f32, u32, 32; f64, u64, 64); +impl_float!(f32, u32; f64, u64); + +#[cfg(target_has_reliable_f16)] +impl_float!(f16, u16); /// A test generator. Should provide an iterator that produces unique patterns to parse. /// @@ -177,7 +180,7 @@ impl_float!(f32, u32, 32; f64, u64, 64); /// allocations (which otherwise turn out to be a pretty expensive part of these tests). pub trait Generator: Iterator + Send + 'static { /// Full display and filtering name - const NAME: &'static str; + const NAME: &'static str = Self::SHORT_NAME; /// Name for display with the progress bar const SHORT_NAME: &'static str; diff --git a/src/etc/test-float-parse/src/ui.rs b/src/etc/test-float-parse/src/ui.rs index f333bd4a55dc9..73473eef0bfdb 100644 --- a/src/etc/test-float-parse/src/ui.rs +++ b/src/etc/test-float-parse/src/ui.rs @@ -1,67 +1,92 @@ //! Progress bars and such. +use std::any::type_name; +use std::fmt; use std::io::{self, Write}; use std::process::ExitCode; use std::time::Duration; -use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; +use indicatif::{ProgressBar, ProgressStyle}; use crate::{Completed, Config, EarlyExit, FinishedAll, TestInfo}; /// Templates for progress bars. -const PB_TEMPLATE: &str = "[{elapsed:3} {percent:3}%] {bar:20.cyan/blue} NAME ({pos}/{len}, {msg} f, {per_sec}, eta {eta})"; -const PB_TEMPLATE_FINAL: &str = - "[{elapsed:3} {percent:3}%] NAME ({pos}/{len}, {msg:.COLOR}, {per_sec}, {elapsed_precise})"; - -/// Create a new progress bar within a multiprogress bar. -pub fn create_pb( - mp: &MultiProgress, - total_tests: u64, - short_name_padded: &str, - all_bars: &mut Vec, -) -> ProgressBar { - let pb = mp.add(ProgressBar::new(total_tests)); - let pb_style = ProgressStyle::with_template(&PB_TEMPLATE.replace("NAME", short_name_padded)) - .unwrap() - .progress_chars("##-"); - - pb.set_style(pb_style.clone()); - pb.set_message("0"); - all_bars.push(pb.clone()); - pb +const PB_TEMPLATE: &str = "[{elapsed:3} {percent:3}%] {bar:20.cyan/blue} NAME \ + {human_pos:>8}/{human_len:8} {msg} f {per_sec:14} eta {eta:8}"; +const PB_TEMPLATE_FINAL: &str = "[{elapsed:3} {percent:3}%] {bar:20.cyan/blue} NAME \ + {human_pos:>8}/{human_len:8} {msg:.COLOR} {per_sec:18} {elapsed_precise}"; + +/// Thin abstraction over our usage of a `ProgressBar`. +#[derive(Debug)] +pub struct Progress { + pb: ProgressBar, + make_final_style: NoDebug ProgressStyle + Sync>>, } -/// Removes the status bar and replace it with a message. -pub fn finalize_pb(pb: &ProgressBar, short_name_padded: &str, c: &Completed) { - let f = c.failures; +impl Progress { + /// Create a new progress bar within a multiprogress bar. + pub fn new(test: &TestInfo, all_bars: &mut Vec) -> Self { + let initial_template = PB_TEMPLATE.replace("NAME", &test.short_name_padded); + let final_template = PB_TEMPLATE_FINAL.replace("NAME", &test.short_name_padded); + let initial_style = + ProgressStyle::with_template(&initial_template).unwrap().progress_chars("##-"); + let make_final_style = move |color| { + ProgressStyle::with_template(&final_template.replace("COLOR", color)) + .unwrap() + .progress_chars("##-") + }; - // Use a tuple so we can use colors - let (color, msg, finish_pb): (&str, String, fn(&ProgressBar, String)) = match &c.result { - Ok(FinishedAll) if f > 0 => { - ("red", format!("{f} f (finished with errors)",), ProgressBar::finish_with_message) - } - Ok(FinishedAll) => { - ("green", format!("{f} f (finished successfully)",), ProgressBar::finish_with_message) - } - Err(EarlyExit::Timeout) => { - ("red", format!("{f} f (timed out)"), ProgressBar::abandon_with_message) + let pb = ProgressBar::new(test.total_tests); + pb.set_style(initial_style); + pb.set_length(test.total_tests); + pb.set_message("0"); + all_bars.push(pb.clone()); + + Progress { pb, make_final_style: NoDebug(Box::new(make_final_style)) } + } + + /// Completed a out of b tests. + pub fn update(&self, completed: u64, failures: u64) { + // Infrequently update the progress bar. + if completed % 5_000 == 0 || failures > 0 { + self.pb.set_position(completed); } - Err(EarlyExit::MaxFailures) => { - ("red", format!("{f} f (failure limit)"), ProgressBar::abandon_with_message) + + if failures > 0 { + self.pb.set_message(format! {"{failures}"}); } - }; + } + + /// Finalize the progress bar. + pub fn complete(&self, c: &Completed, real_total: u64) { + let f = c.failures; + let (color, msg, finish_fn): (&str, String, fn(&ProgressBar)) = match &c.result { + Ok(FinishedAll) if f > 0 => { + ("red", format!("{f} f (completed with errors)",), ProgressBar::finish) + } + Ok(FinishedAll) => { + ("green", format!("{f} f (completed successfully)",), ProgressBar::finish) + } + Err(EarlyExit::Timeout) => ("red", format!("{f} f (timed out)"), ProgressBar::abandon), + Err(EarlyExit::MaxFailures) => { + ("red", format!("{f} f (failure limit)"), ProgressBar::abandon) + } + }; - let pb_style = ProgressStyle::with_template( - &PB_TEMPLATE_FINAL.replace("NAME", short_name_padded).replace("COLOR", color), - ) - .unwrap(); + self.pb.set_position(real_total); + self.pb.set_style(self.make_final_style.0(color)); + self.pb.set_message(msg); + finish_fn(&self.pb); + } - pb.set_style(pb_style); - finish_pb(pb, msg); + /// Print a message to stdout above the current progress bar. + pub fn println(&self, msg: &str) { + self.pb.suspend(|| println!("{msg}")); + } } /// Print final messages after all tests are complete. -pub fn finish(tests: &[TestInfo], total_elapsed: Duration, cfg: &Config) -> ExitCode { +pub fn finish_all(tests: &[TestInfo], total_elapsed: Duration, cfg: &Config) -> ExitCode { println!("\n\nResults:"); let mut failed_generators = 0; @@ -118,8 +143,9 @@ pub fn finish(tests: &[TestInfo], total_elapsed: Duration, cfg: &Config) -> Exit /// indicatif likes to eat panic messages. This workaround isn't ideal, but it improves things. /// . -pub fn set_panic_hook(drop_bars: Vec) { +pub fn set_panic_hook(drop_bars: &[ProgressBar]) { let hook = std::panic::take_hook(); + let drop_bars = drop_bars.to_owned(); std::panic::set_hook(Box::new(move |info| { for bar in &drop_bars { bar.abandon(); @@ -130,3 +156,13 @@ pub fn set_panic_hook(drop_bars: Vec) { hook(info); })); } + +/// Allow non-Debug items in a `derive(Debug)` struct. +#[derive(Clone)] +struct NoDebug(T); + +impl fmt::Debug for NoDebug { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(type_name::()) + } +} diff --git a/src/etc/test-float-parse/src/validate.rs b/src/etc/test-float-parse/src/validate.rs index 1eb3699cfb9f7..40dda274e3b86 100644 --- a/src/etc/test-float-parse/src/validate.rs +++ b/src/etc/test-float-parse/src/validate.rs @@ -1,6 +1,6 @@ //! Everything related to verifying that parsed outputs are correct. -use std::any::type_name; +use std::any::{Any, type_name}; use std::collections::BTreeMap; use std::ops::RangeInclusive; use std::str::FromStr; @@ -9,7 +9,7 @@ use std::sync::LazyLock; use num::bigint::ToBigInt; use num::{BigInt, BigRational, FromPrimitive, Signed, ToPrimitive}; -use crate::{CheckFailure, Float, Int, Update}; +use crate::{CheckFailure, Float, Int}; /// Powers of two that we store for constants. Account for binary128 which has a 15-bit exponent. const POWERS_OF_TWO_RANGE: RangeInclusive = (-(2 << 15))..=(2 << 15); @@ -89,10 +89,16 @@ impl Constants { } /// Validate that a string parses correctly -pub fn validate(input: &str) -> Result<(), Update> { - let parsed: F = input - .parse() - .unwrap_or_else(|e| panic!("parsing failed for {}: {e}. Input: {input}", type_name::())); +pub fn validate(input: &str) -> Result<(), CheckError> { + // Catch panics in case debug assertions within `std` fail. + let parsed = std::panic::catch_unwind(|| { + input.parse::().map_err(|e| CheckError { + fail: CheckFailure::ParsingFailed(e.to_string().into()), + input: input.into(), + float_res: "none".into(), + }) + }) + .map_err(|e| convert_panic_error(&e, input))??; // Parsed float, decoded into significand and exponent let decoded = decode(parsed); @@ -104,6 +110,21 @@ pub fn validate(input: &str) -> Result<(), Update> { decoded.check(rational, input) } +/// Turn panics into concrete error types. +fn convert_panic_error(e: &dyn Any, input: &str) -> CheckError { + let msg = e + .downcast_ref::() + .map(|s| s.as_str()) + .or_else(|| e.downcast_ref::<&str>().copied()) + .unwrap_or("(no contents)"); + + CheckError { + fail: CheckFailure::Panic(msg.into()), + input: input.into(), + float_res: "none".into(), + } +} + /// The result of parsing a string to a float type. #[derive(Clone, Copy, Debug, PartialEq)] pub enum FloatRes { @@ -118,10 +139,19 @@ pub enum FloatRes { }, } +#[derive(Clone, Debug)] +pub struct CheckError { + pub fail: CheckFailure, + /// String for which parsing was attempted. + pub input: Box, + /// The parsed & decomposed `FloatRes`, already stringified so we don't need generics here. + pub float_res: Box, +} + impl FloatRes { /// Given a known exact rational, check that this representation is accurate within the /// limits of the float representation. If not, construct a failure `Update` to send. - fn check(self, expected: Rational, input: &str) -> Result<(), Update> { + fn check(self, expected: Rational, input: &str) -> Result<(), CheckError> { let consts = F::constants(); // let bool_helper = |cond: bool, err| cond.then_some(()).ok_or(err); @@ -173,7 +203,7 @@ impl FloatRes { (Rational::Finite(r), FloatRes::Real { sig, exp }) => Self::validate_real(r, sig, exp), }; - res.map_err(|fail| Update::Failure { + res.map_err(|fail| CheckError { fail, input: input.into(), float_res: format!("{self:?}").into(), diff --git a/src/gcc b/src/gcc index 48664a6cab29d..04ce66d8c918d 160000 --- a/src/gcc +++ b/src/gcc @@ -1 +1 @@ -Subproject commit 48664a6cab29d48138ffa004b7978d52ef73e3ac +Subproject commit 04ce66d8c918de9273bd7101638ad8724edf5e21 diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 91cc408878826..dbfdd8ebd167c 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustdoc" version = "0.0.0" -edition = "2021" +edition = "2024" build = "build.rs" [lib] @@ -9,12 +9,11 @@ path = "lib.rs" [dependencies] arrayvec = { version = "0.7", default-features = false } -rinja = { version = "0.3", default-features = false, features = ["config"] } +askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] } base64 = "0.21.7" itertools = "0.12" indexmap = "2" minifier = { version = "0.3.5", default-features = false } -pulldown-cmark-old = { version = "0.9.6", package = "pulldown-cmark", default-features = false } pulldown-cmark-escape = { version = "0.11.0", features = ["simd"] } regex = "1" rustdoc-json-types = { path = "../rustdoc-json-types" } diff --git a/src/librustdoc/rinja.toml b/src/librustdoc/askama.toml similarity index 100% rename from src/librustdoc/rinja.toml rename to src/librustdoc/askama.toml diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 185898c31b75b..138ac3c97f7b6 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -2,8 +2,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_hir as hir; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_middle::bug; -use rustc_middle::ty::fold::fold_regions; -use rustc_middle::ty::{self, Region, Ty}; +use rustc_middle::ty::{self, Region, Ty, fold_regions}; use rustc_span::def_id::DefId; use rustc_span::symbol::{Symbol, kw}; use rustc_trait_selection::traits::auto_trait::{self, RegionTarget}; @@ -115,8 +114,8 @@ fn synthesize_auto_trait_impl<'tcx>( }; Some(clean::Item { - name: None, inner: Box::new(clean::ItemInner { + name: None, attrs: Default::default(), stability: None, kind: clean::ImplItem(Box::new(clean::Impl { @@ -128,10 +127,10 @@ fn synthesize_auto_trait_impl<'tcx>( polarity, kind: clean::ImplKind::Auto, })), + item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id }, + cfg: None, + inline_stmt_id: None, }), - item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id }, - cfg: None, - inline_stmt_id: None, }) } @@ -183,7 +182,7 @@ fn clean_param_env<'tcx>( .is_some_and(|pred| tcx.lang_items().sized_trait() == Some(pred.def_id())) }) .map(|pred| { - fold_regions(tcx, pred, |r, _| match *r { + fold_regions(tcx, pred, |r, _| match r.kind() { // FIXME: Don't `unwrap_or`, I think we should panic if we encounter an infer var that // we can't map to a concrete region. However, `AutoTraitFinder` *does* leak those kinds // of `ReVar`s for some reason at the time of writing. See `rustdoc-ui/` tests. @@ -363,7 +362,7 @@ fn clean_region_outlives_constraints<'tcx>( } fn early_bound_region_name(region: Region<'_>) -> Option { - match *region { + match region.kind() { ty::ReEarlyParam(r) => Some(r.name), _ => None, } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index a6d9676dd84ad..89245fee51551 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -83,9 +83,9 @@ pub(crate) fn synthesize_blanket_impls( cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id)); blanket_impls.push(clean::Item { - name: None, - item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, inner: Box::new(clean::ItemInner { + name: None, + item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, attrs: Default::default(), stability: None, kind: clean::ImplItem(Box::new(clean::Impl { @@ -122,9 +122,9 @@ pub(crate) fn synthesize_blanket_impls( None, ))), })), + cfg: None, + inline_stmt_id: None, }), - cfg: None, - inline_stmt_id: None, }); } } diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index bec7fbe8f52bd..439777843fb06 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -8,7 +8,6 @@ use std::{mem, ops}; use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit}; use rustc_data_structures::fx::FxHashSet; -use rustc_feature::Features; use rustc_session::parse::ParseSess; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; @@ -48,12 +47,12 @@ impl Cfg { exclude: &FxHashSet, ) -> Result, InvalidCfgError> { match nested_cfg { - MetaItemInner::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude), + MetaItemInner::MetaItem(cfg) => Cfg::parse_without(cfg, exclude), MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => match *b { true => Ok(Some(Cfg::True)), false => Ok(Some(Cfg::False)), }, - MetaItemInner::Lit(ref lit) => { + MetaItemInner::Lit(lit) => { Err(InvalidCfgError { msg: "unexpected literal", span: lit.span }) } } @@ -132,25 +131,20 @@ impl Cfg { /// Checks whether the given configuration can be matched in the current session. /// /// Equivalent to `attr::cfg_matches`. - // FIXME: Actually make use of `features`. - pub(crate) fn matches(&self, psess: &ParseSess, features: Option<&Features>) -> bool { + pub(crate) fn matches(&self, psess: &ParseSess) -> bool { match *self { Cfg::False => false, Cfg::True => true, - Cfg::Not(ref child) => !child.matches(psess, features), - Cfg::All(ref sub_cfgs) => { - sub_cfgs.iter().all(|sub_cfg| sub_cfg.matches(psess, features)) - } - Cfg::Any(ref sub_cfgs) => { - sub_cfgs.iter().any(|sub_cfg| sub_cfg.matches(psess, features)) - } + Cfg::Not(ref child) => !child.matches(psess), + Cfg::All(ref sub_cfgs) => sub_cfgs.iter().all(|sub_cfg| sub_cfg.matches(psess)), + Cfg::Any(ref sub_cfgs) => sub_cfgs.iter().any(|sub_cfg| sub_cfg.matches(psess)), Cfg::Cfg(name, value) => psess.config.contains(&(name, value)), } } /// Whether the configuration consists of just `Cfg` or `Not`. fn is_simple(&self) -> bool { - match *self { + match self { Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) => true, Cfg::All(..) | Cfg::Any(..) => false, } @@ -158,7 +152,7 @@ impl Cfg { /// Whether the configuration consists of just `Cfg`, `Not` or `All`. fn is_all(&self) -> bool { - match *self { + match self { Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) | Cfg::All(..) => true, Cfg::Any(..) => false, } @@ -210,7 +204,7 @@ impl Cfg { } fn should_append_only_to_description(&self) -> bool { - match *self { + match self { Cfg::False | Cfg::True => false, Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true, Cfg::Not(box Cfg::Cfg(..)) => true, @@ -267,17 +261,17 @@ impl ops::Not for Cfg { impl ops::BitAndAssign for Cfg { fn bitand_assign(&mut self, other: Cfg) { match (self, other) { - (&mut Cfg::False, _) | (_, Cfg::True) => {} + (Cfg::False, _) | (_, Cfg::True) => {} (s, Cfg::False) => *s = Cfg::False, - (s @ &mut Cfg::True, b) => *s = b, - (&mut Cfg::All(ref mut a), Cfg::All(ref mut b)) => { + (s @ Cfg::True, b) => *s = b, + (Cfg::All(a), Cfg::All(ref mut b)) => { for c in b.drain(..) { if !a.contains(&c) { a.push(c); } } } - (&mut Cfg::All(ref mut a), ref mut b) => { + (Cfg::All(a), ref mut b) => { if !a.contains(b) { a.push(mem::replace(b, Cfg::True)); } @@ -311,15 +305,15 @@ impl ops::BitOrAssign for Cfg { fn bitor_assign(&mut self, other: Cfg) { match (self, other) { (Cfg::True, _) | (_, Cfg::False) | (_, Cfg::True) => {} - (s @ &mut Cfg::False, b) => *s = b, - (&mut Cfg::Any(ref mut a), Cfg::Any(ref mut b)) => { + (s @ Cfg::False, b) => *s = b, + (Cfg::Any(a), Cfg::Any(ref mut b)) => { for c in b.drain(..) { if !a.contains(&c) { a.push(c); } } } - (&mut Cfg::Any(ref mut a), ref mut b) => { + (Cfg::Any(a), ref mut b) => { if !a.contains(b) { a.push(mem::replace(b, Cfg::True)); } @@ -446,40 +440,34 @@ impl Display<'_> { impl fmt::Display for Display<'_> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self.0 { - Cfg::Not(ref child) => match **child { - Cfg::Any(ref sub_cfgs) => { - let separator = - if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " }; - fmt.write_str("neither ")?; - - sub_cfgs - .iter() - .map(|sub_cfg| { - fmt::from_fn(|fmt| { - write_with_opt_paren( - fmt, - !sub_cfg.is_all(), - Display(sub_cfg, self.1), - ) - }) + match self.0 { + Cfg::Not(box Cfg::Any(sub_cfgs)) => { + let separator = + if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " }; + fmt.write_str("neither ")?; + + sub_cfgs + .iter() + .map(|sub_cfg| { + fmt::from_fn(|fmt| { + write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1)) }) - .joined(separator, fmt) - } - ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Display(simple, self.1)), - ref c => write!(fmt, "not ({})", Display(c, self.1)), - }, + }) + .joined(separator, fmt) + } + Cfg::Not(box simple @ Cfg::Cfg(..)) => write!(fmt, "non-{}", Display(simple, self.1)), + Cfg::Not(box c) => write!(fmt, "not ({})", Display(c, self.1)), - Cfg::Any(ref sub_cfgs) => { + Cfg::Any(sub_cfgs) => { let separator = if sub_cfgs.iter().all(Cfg::is_simple) { " or " } else { ", or " }; self.display_sub_cfgs(fmt, sub_cfgs, separator) } - Cfg::All(ref sub_cfgs) => self.display_sub_cfgs(fmt, sub_cfgs, " and "), + Cfg::All(sub_cfgs) => self.display_sub_cfgs(fmt, sub_cfgs, " and "), Cfg::True => fmt.write_str("everywhere"), Cfg::False => fmt.write_str("nowhere"), - Cfg::Cfg(name, value) => { + &Cfg::Cfg(name, value) => { let human_readable = match (name, value) { (sym::unix, None) => "Unix", (sym::windows, None) => "Windows", diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index e10a74221ae2e..55a116a018a8a 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -67,9 +67,13 @@ pub(crate) fn try_inline( record_extern_fqn(cx, did, ItemType::Trait); cx.with_param_env(did, |cx| { build_impls(cx, did, attrs_without_docs, &mut ret); - clean::TraitItem(Box::new(build_external_trait(cx, did))) + clean::TraitItem(Box::new(build_trait(cx, did))) }) } + Res::Def(DefKind::TraitAlias, did) => { + record_extern_fqn(cx, did, ItemType::TraitAlias); + cx.with_param_env(did, |cx| clean::TraitAliasItem(build_trait_alias(cx, did))) + } Res::Def(DefKind::Fn, did) => { record_extern_fqn(cx, did, ItemType::Function); cx.with_param_env(did, |cx| { @@ -151,7 +155,7 @@ pub(crate) fn try_inline( let mut item = crate::clean::generate_item_with_correct_attrs(cx, kind, did, name, import_def_id, None); // The visibility needs to reflect the one from the reexport and not from the "source" DefId. - item.inline_stmt_id = import_def_id; + item.inner.inline_stmt_id = import_def_id; ret.push(item); Some(ret) } @@ -181,7 +185,7 @@ pub(crate) fn try_inline_glob( .filter_map(|child| child.res.opt_def_id()) .filter(|def_id| !cx.tcx.is_doc_hidden(def_id)) .collect(); - let attrs = cx.tcx.hir().attrs(import.hir_id()); + let attrs = cx.tcx.hir_attrs(import.hir_id()); let mut items = build_module_items( cx, did, @@ -211,17 +215,7 @@ pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> &'hir [hir: } pub(crate) fn item_relative_path(tcx: TyCtxt<'_>, def_id: DefId) -> Vec { - tcx.def_path(def_id) - .data - .into_iter() - .filter_map(|elem| { - // extern blocks (and a few others things) have an empty name. - match elem.data.get_opt_name() { - Some(s) if !s.is_empty() => Some(s), - _ => None, - } - }) - .collect() + tcx.def_path(def_id).data.into_iter().filter_map(|elem| elem.data.get_opt_name()).collect() } /// Record an external fully qualified name in the external_paths cache. @@ -261,7 +255,7 @@ pub(crate) fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemT } } -pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean::Trait { +pub(crate) fn build_trait(cx: &mut DocContext<'_>, did: DefId) -> clean::Trait { let trait_items = cx .tcx .associated_items(did) @@ -273,11 +267,18 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean let predicates = cx.tcx.predicates_of(did); let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates); let generics = filter_non_trait_generics(did, generics); - let (generics, supertrait_bounds) = separate_supertrait_bounds(generics); + let (generics, supertrait_bounds) = separate_self_bounds(generics); clean::Trait { def_id: did, generics, items: trait_items, bounds: supertrait_bounds } } -pub(crate) fn build_function(cx: &mut DocContext<'_>, def_id: DefId) -> Box { +fn build_trait_alias(cx: &mut DocContext<'_>, did: DefId) -> clean::TraitAlias { + let predicates = cx.tcx.predicates_of(did); + let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates); + let (generics, bounds) = separate_self_bounds(generics); + clean::TraitAlias { generics, bounds } +} + +pub(super) fn build_function(cx: &mut DocContext<'_>, def_id: DefId) -> Box { let sig = cx.tcx.fn_sig(def_id).instantiate_identity(); // The generics need to be cleaned before the signature. let mut generics = @@ -455,7 +456,7 @@ pub(crate) fn build_impl( } let impl_item = match did.as_local() { - Some(did) => match &tcx.hir().expect_item(did).kind { + Some(did) => match &tcx.hir_expect_item(did).kind { hir::ItemKind::Impl(impl_) => Some(impl_), _ => panic!("`DefID` passed to `build_impl` is not an `impl"), }, @@ -500,17 +501,17 @@ pub(crate) fn build_impl( return true; } if let Some(associated_trait) = associated_trait { - let assoc_kind = match item.kind { - hir::ImplItemKind::Const(..) => ty::AssocKind::Const, - hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, - hir::ImplItemKind::Type(..) => ty::AssocKind::Type, + let assoc_tag = match item.kind { + hir::ImplItemKind::Const(..) => ty::AssocTag::Const, + hir::ImplItemKind::Fn(..) => ty::AssocTag::Fn, + hir::ImplItemKind::Type(..) => ty::AssocTag::Type, }; let trait_item = tcx .associated_items(associated_trait.def_id) - .find_by_name_and_kind( + .find_by_ident_and_kind( tcx, item.ident, - assoc_kind, + assoc_tag, associated_trait.def_id, ) .unwrap(); // SAFETY: For all impl items there exists trait item that has the same name. @@ -534,10 +535,10 @@ pub(crate) fn build_impl( if let Some(associated_trait) = associated_trait { let trait_item = tcx .associated_items(associated_trait.def_id) - .find_by_name_and_kind( + .find_by_ident_and_kind( tcx, item.ident(tcx), - item.kind, + item.as_tag(), associated_trait.def_id, ) .unwrap(); // corresponding associated item has to exist @@ -563,11 +564,13 @@ pub(crate) fn build_impl( // Return if the trait itself or any types of the generic parameters are doc(hidden). let mut stack: Vec<&Type> = vec![&for_]; - if let Some(did) = trait_.as_ref().map(|t| t.def_id()) { - if !document_hidden && tcx.is_doc_hidden(did) { - return; - } + if let Some(did) = trait_.as_ref().map(|t| t.def_id()) + && !document_hidden + && tcx.is_doc_hidden(did) + { + return; } + if let Some(generics) = trait_.as_ref().and_then(|t| t.generics()) { stack.extend(generics); } @@ -663,11 +666,11 @@ fn build_module_items( // Primitive types can't be inlined so generate an import instead. let prim_ty = clean::PrimitiveType::from(p); items.push(clean::Item { - name: None, - // We can use the item's `DefId` directly since the only information ever used - // from it is `DefId.krate`. - item_id: ItemId::DefId(did), inner: Box::new(clean::ItemInner { + name: None, + // We can use the item's `DefId` directly since the only information ever + // used from it is `DefId.krate`. + item_id: ItemId::DefId(did), attrs: Default::default(), stability: None, kind: clean::ImportItem(clean::Import::new_simple( @@ -687,9 +690,9 @@ fn build_module_items( }, true, )), + cfg: None, + inline_stmt_id: None, }), - cfg: None, - inline_stmt_id: None, }); } else if let Some(i) = try_inline(cx, res, item.ident.name, attrs, visited) { items.extend(i) @@ -796,12 +799,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: g } -/// Supertrait bounds for a trait are also listed in the generics coming from -/// the metadata for a crate, so we want to separate those out and create a new -/// list of explicit supertrait bounds to render nicely. -fn separate_supertrait_bounds( - mut g: clean::Generics, -) -> (clean::Generics, Vec) { +fn separate_self_bounds(mut g: clean::Generics) -> (clean::Generics, Vec) { let mut ty_bounds = Vec::new(); g.where_predicates.retain(|pred| match *pred { clean::WherePredicate::BoundPredicate { ty: clean::SelfTy, ref bounds, .. } => { @@ -814,22 +812,17 @@ fn separate_supertrait_bounds( } pub(crate) fn record_extern_trait(cx: &mut DocContext<'_>, did: DefId) { - if did.is_local() { - return; - } - + if did.is_local() + || cx.external_traits.contains_key(&did) + || cx.active_extern_traits.contains(&did) { - if cx.external_traits.contains_key(&did) || cx.active_extern_traits.contains(&did) { - return; - } + return; } - { - cx.active_extern_traits.insert(did); - } + cx.active_extern_traits.insert(did); debug!("record_extern_trait: {did:?}"); - let trait_ = build_external_trait(cx, did); + let trait_ = build_trait(cx, did); cx.external_traits.insert(did, trait_); cx.active_extern_traits.remove(&did); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index ceffe5e5ce04e..28dfa01534ead 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -112,12 +112,19 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< items.extend(doc.inlined_foreigns.iter().flat_map(|((_, renamed), (res, local_import_id))| { let Some(def_id) = res.opt_def_id() else { return Vec::new() }; let name = renamed.unwrap_or_else(|| cx.tcx.item_name(def_id)); - let import = cx.tcx.hir().expect_item(*local_import_id); + let import = cx.tcx.hir_expect_item(*local_import_id); match import.kind { hir::ItemKind::Use(path, kind) => { let hir::UsePath { segments, span, .. } = *path; let path = hir::Path { segments, res: *res, span }; - clean_use_statement_inner(import, name, &path, kind, cx, &mut Default::default()) + clean_use_statement_inner( + import, + Some(name), + &path, + kind, + cx, + &mut Default::default(), + ) } _ => unreachable!(), } @@ -125,8 +132,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< items.extend(doc.items.values().flat_map(|(item, renamed, _)| { // Now we actually lower the imports, skipping everything else. if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind { - let name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id())); - clean_use_statement(item, name, path, hir::UseKind::Glob, cx, &mut inserted) + clean_use_statement(item, *renamed, path, hir::UseKind::Glob, cx, &mut inserted) } else { // skip everything else Vec::new() @@ -210,7 +216,7 @@ fn generate_item_with_correct_attrs( let name = renamed.or(Some(name)); let mut item = Item::from_def_id_and_attrs_and_parts(def_id, name, kind, attrs, cfg); - item.inline_stmt_id = import_id; + item.inner.inline_stmt_id = import_id; item } @@ -218,9 +224,9 @@ fn clean_generic_bound<'tcx>( bound: &hir::GenericBound<'tcx>, cx: &mut DocContext<'tcx>, ) -> Option { - Some(match *bound { + Some(match bound { hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)), - hir::GenericBound::Trait(ref t) => { + hir::GenericBound::Trait(t) => { // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op. if let hir::BoundConstness::Maybe(_) = t.modifiers.constness && cx.tcx.lang_items().destruct_trait() == Some(t.trait_ref.trait_def_id().unwrap()) @@ -231,7 +237,7 @@ fn clean_generic_bound<'tcx>( GenericBound::TraitBound(clean_poly_trait_ref(t, cx), t.modifiers) } hir::GenericBound::Use(args, ..) => { - GenericBound::Use(args.iter().map(|arg| arg.name()).collect()) + GenericBound::Use(args.iter().map(|arg| clean_precise_capturing_arg(arg, cx)).collect()) } }) } @@ -286,6 +292,18 @@ fn clean_lifetime(lifetime: &hir::Lifetime, cx: &DocContext<'_>) -> Lifetime { Lifetime(lifetime.ident.name) } +pub(crate) fn clean_precise_capturing_arg( + arg: &hir::PreciseCapturingArg<'_>, + cx: &DocContext<'_>, +) -> PreciseCapturingArg { + match arg { + hir::PreciseCapturingArg::Lifetime(lt) => { + PreciseCapturingArg::Lifetime(clean_lifetime(lt, cx)) + } + hir::PreciseCapturingArg::Param(param) => PreciseCapturingArg::Param(param.ident.name), + } +} + pub(crate) fn clean_const<'tcx>( constant: &hir::ConstArg<'tcx>, _cx: &mut DocContext<'tcx>, @@ -308,7 +326,7 @@ pub(crate) fn clean_middle_const<'tcx>( } pub(crate) fn clean_middle_region(region: ty::Region<'_>) -> Option { - match *region { + match region.kind() { ty::ReStatic => Some(Lifetime::statik()), _ if !region.has_name() => None, ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(_, name), .. }) => { @@ -334,8 +352,8 @@ fn clean_where_predicate<'tcx>( if !predicate.kind.in_where_clause() { return None; } - Some(match *predicate.kind { - hir::WherePredicateKind::BoundPredicate(ref wbp) => { + Some(match predicate.kind { + hir::WherePredicateKind::BoundPredicate(wbp) => { let bound_params = wbp .bound_generic_params .iter() @@ -348,12 +366,12 @@ fn clean_where_predicate<'tcx>( } } - hir::WherePredicateKind::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate { + hir::WherePredicateKind::RegionPredicate(wrp) => WherePredicate::RegionPredicate { lifetime: clean_lifetime(wrp.lifetime, cx), bounds: wrp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }, - hir::WherePredicateKind::EqPredicate(ref wrp) => WherePredicate::EqPredicate { + hir::WherePredicateKind::EqPredicate(wrp) => WherePredicate::EqPredicate { lhs: clean_ty(wrp.lhs_ty, cx), rhs: clean_ty(wrp.rhs_ty, cx).into(), }, @@ -509,7 +527,7 @@ fn projection_to_path_segment<'tcx>( let item = cx.tcx.associated_item(def_id); let generics = cx.tcx.generics_of(def_id); PathSegment { - name: item.name, + name: item.name(), args: GenericArgs::AngleBracketed { args: clean_middle_generic_args( cx, @@ -741,7 +759,7 @@ pub(crate) fn clean_generics<'tcx>( for p in gens.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) { let mut p = clean_generic_param(cx, Some(gens), p); match &mut p.kind { - GenericParamDefKind::Lifetime { ref mut outlives } => { + GenericParamDefKind::Lifetime { outlives } => { if let Some(region_pred) = region_predicates.get_mut(&Lifetime(p.name)) { // We merge bounds in the `where` clause. for outlive in outlives.drain(..) { @@ -828,30 +846,26 @@ fn clean_ty_generics<'tcx>( .iter() .flat_map(|(pred, _)| { let mut projection = None; - let param_idx = (|| { + let param_idx = { let bound_p = pred.kind(); match bound_p.skip_binder() { - ty::ClauseKind::Trait(pred) => { - if let ty::Param(param) = pred.self_ty().kind() { - return Some(param.index); - } + ty::ClauseKind::Trait(pred) if let ty::Param(param) = pred.self_ty().kind() => { + Some(param.index) } - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { - if let ty::Param(param) = ty.kind() { - return Some(param.index); - } + ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) + if let ty::Param(param) = ty.kind() => + { + Some(param.index) } - ty::ClauseKind::Projection(p) => { - if let ty::Param(param) = p.projection_term.self_ty().kind() { - projection = Some(bound_p.rebind(p)); - return Some(param.index); - } + ty::ClauseKind::Projection(p) + if let ty::Param(param) = p.projection_term.self_ty().kind() => + { + projection = Some(bound_p.rebind(p)); + Some(param.index) } - _ => (), + _ => None, } - - None - })(); + }; if let Some(param_idx) = param_idx && let Some(bounds) = impl_trait.get_mut(¶m_idx) @@ -990,7 +1004,7 @@ fn clean_proc_macro<'tcx>( kind: MacroKind, cx: &mut DocContext<'tcx>, ) -> ItemKind { - let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attrs = cx.tcx.hir_attrs(item.hir_id()); if kind == MacroKind::Derive && let Some(derive_name) = hir_attr_lists(attrs, sym::proc_macro_derive).find_map(|mi| mi.ident()) @@ -1023,7 +1037,7 @@ fn clean_fn_or_proc_macro<'tcx>( name: &mut Symbol, cx: &mut DocContext<'tcx>, ) -> ItemKind { - let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attrs = cx.tcx.hir_attrs(item.hir_id()); let macro_kind = attrs.iter().find_map(|a| { if a.has_name(sym::proc_macro) { Some(MacroKind::Bang) @@ -1038,7 +1052,7 @@ fn clean_fn_or_proc_macro<'tcx>( match macro_kind { Some(kind) => clean_proc_macro(item, name, kind, cx), None => { - let mut func = clean_function(cx, sig, generics, FunctionArgs::Body(body_id)); + let mut func = clean_function(cx, sig, generics, ParamsSrc::Body(body_id)); clean_fn_decl_legacy_const_generics(&mut func, attrs); FunctionItem(func) } @@ -1057,17 +1071,12 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[hir::Attrib for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.lit()).enumerate() { match literal.kind { ast::LitKind::Int(a, _) => { - let param = func.generics.params.remove(0); - if let GenericParamDef { - name, - kind: GenericParamDefKind::Const { ty, .. }, - .. - } = param - { - func.decl - .inputs - .values - .insert(a.get() as _, Argument { name, type_: *ty, is_const: true }); + let GenericParamDef { name, kind, .. } = func.generics.params.remove(0); + if let GenericParamDefKind::Const { ty, .. } = kind { + func.decl.inputs.insert( + a.get() as _, + Parameter { name: Some(name), type_: *ty, is_const: true }, + ); } else { panic!("unexpected non const in position {pos}"); } @@ -1078,93 +1087,71 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[hir::Attrib } } -enum FunctionArgs<'tcx> { +enum ParamsSrc<'tcx> { Body(hir::BodyId), - Names(&'tcx [Ident]), + Idents(&'tcx [Option]), } fn clean_function<'tcx>( cx: &mut DocContext<'tcx>, sig: &hir::FnSig<'tcx>, generics: &hir::Generics<'tcx>, - args: FunctionArgs<'tcx>, + params: ParamsSrc<'tcx>, ) -> Box { let (generics, decl) = enter_impl_trait(cx, |cx| { - // NOTE: generics must be cleaned before args + // NOTE: Generics must be cleaned before params. let generics = clean_generics(generics, cx); - let args = match args { - FunctionArgs::Body(body_id) => { - clean_args_from_types_and_body_id(cx, sig.decl.inputs, body_id) - } - FunctionArgs::Names(names) => { - clean_args_from_types_and_names(cx, sig.decl.inputs, names) - } + let params = match params { + ParamsSrc::Body(body_id) => clean_params_via_body(cx, sig.decl.inputs, body_id), + // Let's not perpetuate anon params from Rust 2015; use `_` for them. + ParamsSrc::Idents(idents) => clean_params(cx, sig.decl.inputs, idents, |ident| { + Some(ident.map_or(kw::Underscore, |ident| ident.name)) + }), }; - let decl = clean_fn_decl_with_args(cx, sig.decl, Some(&sig.header), args); + let decl = clean_fn_decl_with_params(cx, sig.decl, Some(&sig.header), params); (generics, decl) }); Box::new(Function { decl, generics }) } -fn clean_args_from_types_and_names<'tcx>( +fn clean_params<'tcx>( cx: &mut DocContext<'tcx>, types: &[hir::Ty<'tcx>], - names: &[Ident], -) -> Arguments { - fn nonempty_name(ident: &Ident) -> Option { - if ident.name == kw::Underscore || ident.name == kw::Empty { - None - } else { - Some(ident.name) - } - } - - // If at least one argument has a name, use `_` as the name of unnamed - // arguments. Otherwise omit argument names. - let default_name = if names.iter().any(|ident| nonempty_name(ident).is_some()) { - kw::Underscore - } else { - kw::Empty - }; - - Arguments { - values: types - .iter() - .enumerate() - .map(|(i, ty)| Argument { - type_: clean_ty(ty, cx), - name: names.get(i).and_then(nonempty_name).unwrap_or(default_name), - is_const: false, - }) - .collect(), - } + idents: &[Option], + postprocess: impl Fn(Option) -> Option, +) -> Vec { + types + .iter() + .enumerate() + .map(|(i, ty)| Parameter { + name: postprocess(idents[i]), + type_: clean_ty(ty, cx), + is_const: false, + }) + .collect() } -fn clean_args_from_types_and_body_id<'tcx>( +fn clean_params_via_body<'tcx>( cx: &mut DocContext<'tcx>, types: &[hir::Ty<'tcx>], body_id: hir::BodyId, -) -> Arguments { - let body = cx.tcx.hir_body(body_id); - - Arguments { - values: types - .iter() - .zip(body.params) - .map(|(ty, param)| Argument { - name: name_from_pat(param.pat), - type_: clean_ty(ty, cx), - is_const: false, - }) - .collect(), - } +) -> Vec { + types + .iter() + .zip(cx.tcx.hir_body(body_id).params) + .map(|(ty, param)| Parameter { + name: Some(name_from_pat(param.pat)), + type_: clean_ty(ty, cx), + is_const: false, + }) + .collect() } -fn clean_fn_decl_with_args<'tcx>( +fn clean_fn_decl_with_params<'tcx>( cx: &mut DocContext<'tcx>, decl: &hir::FnDecl<'tcx>, header: Option<&hir::FnHeader>, - args: Arguments, + params: Vec, ) -> FnDecl { let mut output = match decl.output { hir::FnRetTy::Return(typ) => clean_ty(typ, cx), @@ -1175,7 +1162,7 @@ fn clean_fn_decl_with_args<'tcx>( { output = output.sugared_async_return_type(); } - FnDecl { inputs: args, output, c_variadic: decl.c_variadic } + FnDecl { inputs: params, output, c_variadic: decl.c_variadic } } fn clean_poly_fn_sig<'tcx>( @@ -1183,10 +1170,6 @@ fn clean_poly_fn_sig<'tcx>( did: Option, sig: ty::PolyFnSig<'tcx>, ) -> FnDecl { - let mut names = did.map_or(&[] as &[_], |did| cx.tcx.fn_arg_names(did)).iter(); - - // We assume all empty tuples are default return type. This theoretically can discard `-> ()`, - // but shouldn't change any code meaning. let mut output = clean_middle_ty(sig.output(), cx, None, None); // If the return type isn't an `impl Trait`, we can safely assume that this @@ -1199,25 +1182,25 @@ fn clean_poly_fn_sig<'tcx>( output = output.sugared_async_return_type(); } - FnDecl { - output, - c_variadic: sig.skip_binder().c_variadic, - inputs: Arguments { - values: sig - .inputs() - .iter() - .map(|t| Argument { - type_: clean_middle_ty(t.map_bound(|t| *t), cx, None, None), - name: names - .next() - .map(|i| i.name) - .filter(|i| !i.is_empty()) - .unwrap_or(kw::Underscore), - is_const: false, - }) - .collect(), - }, - } + let mut idents = did.map(|did| cx.tcx.fn_arg_idents(did)).unwrap_or_default().iter().copied(); + + // If this comes from a fn item, let's not perpetuate anon params from Rust 2015; use `_` for them. + // If this comes from a fn ptr ty, we just keep params unnamed since it's more conventional stylistically. + // Since the param name is not part of the semantic type, these params never bear a name unlike + // in the HIR case, thus we can't peform any fancy fallback logic unlike `clean_bare_fn_ty`. + let fallback = did.map(|_| kw::Underscore); + + let params = sig + .inputs() + .iter() + .map(|ty| Parameter { + name: idents.next().flatten().map(|ident| ident.name).or(fallback), + type_: clean_middle_ty(ty.map_bound(|ty| *ty), cx, None, None), + is_const: false, + }) + .collect(); + + FnDecl { inputs: params, output, c_variadic: sig.skip_binder().c_variadic } } fn clean_trait_ref<'tcx>(trait_ref: &hir::TraitRef<'tcx>, cx: &mut DocContext<'tcx>) -> Path { @@ -1257,11 +1240,11 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext RequiredAssocConstItem(generics, Box::new(clean_ty(ty, cx))) } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body)); + let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Body(body)); MethodItem(m, None) } - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(names)) => { - let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Names(names)); + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(idents)) => { + let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Idents(idents)); RequiredMethodItem(m) } hir::TraitItemKind::Type(bounds, Some(default)) => { @@ -1302,7 +1285,7 @@ pub(crate) fn clean_impl_item<'tcx>( type_: clean_ty(ty, cx), })), hir::ImplItemKind::Fn(ref sig, body) => { - let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body)); + let m = clean_function(cx, sig, impl_.generics, ParamsSrc::Body(body)); let defaultness = cx.tcx.defaultness(impl_.owner_id); MethodItem(m, Some(defaultness)) } @@ -1330,7 +1313,7 @@ pub(crate) fn clean_impl_item<'tcx>( pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocContext<'_>) -> Item { let tcx = cx.tcx; let kind = match assoc_item.kind { - ty::AssocKind::Const => { + ty::AssocKind::Const { .. } => { let ty = clean_middle_ty( ty::Binder::dummy(tcx.type_of(assoc_item.def_id).instantiate_identity()), cx, @@ -1364,26 +1347,26 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } } } - ty::AssocKind::Fn => { + ty::AssocKind::Fn { has_self, .. } => { let mut item = inline::build_function(cx, assoc_item.def_id); - if assoc_item.fn_has_self_parameter { + if has_self { let self_ty = match assoc_item.container { ty::AssocItemContainer::Impl => { tcx.type_of(assoc_item.container_id(tcx)).instantiate_identity() } ty::AssocItemContainer::Trait => tcx.types.self_param, }; - let self_arg_ty = + let self_param_ty = tcx.fn_sig(assoc_item.def_id).instantiate_identity().input(0).skip_binder(); - if self_arg_ty == self_ty { - item.decl.inputs.values[0].type_ = SelfTy; - } else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() { - if ty == self_ty { - match item.decl.inputs.values[0].type_ { - BorrowedRef { ref mut type_, .. } => **type_ = SelfTy, - _ => unreachable!(), - } + if self_param_ty == self_ty { + item.decl.inputs[0].type_ = SelfTy; + } else if let ty::Ref(_, ty, _) = *self_param_ty.kind() + && ty == self_ty + { + match item.decl.inputs[0].type_ { + BorrowedRef { ref mut type_, .. } => **type_ = SelfTy, + _ => unreachable!(), } } } @@ -1402,8 +1385,8 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo RequiredMethodItem(item) } } - ty::AssocKind::Type => { - let my_name = assoc_item.name; + ty::AssocKind::Type { .. } => { + let my_name = assoc_item.name(); fn param_eq_arg(param: &GenericParamDef, arg: &GenericArg) -> bool { match (¶m.kind, arg) { @@ -1480,6 +1463,9 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo // The only time this happens is if we're inside the rustdoc for Fn(), // which only has one associated type, which is not a GAT, so whatever. } + GenericArgs::ReturnTypeNotation => { + // Never move these. + } } bounds.extend(mem::take(pred_bounds)); false @@ -1541,7 +1527,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } }; - Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx) + Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name()), kind, cx) } fn first_non_private_clean_path<'tcx>( @@ -1618,8 +1604,7 @@ fn first_non_private<'tcx>( if let Some(use_def_id) = reexp.id() && let Some(local_use_def_id) = use_def_id.as_local() && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_use_def_id) - && !item.ident.name.is_empty() - && let hir::ItemKind::Use(path, _) = item.kind + && let hir::ItemKind::Use(path, hir::UseKind::Single(_)) = item.kind { for res in &path.res { if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { @@ -1760,11 +1745,11 @@ fn maybe_expand_private_type_alias<'tcx>( let alias = if !cx.cache.effective_visibilities.is_exported(cx.tcx, def_id.to_def_id()) && !cx.current_type_aliases.contains_key(&def_id.to_def_id()) { - &cx.tcx.hir().expect_item(def_id).kind + &cx.tcx.hir_expect_item(def_id).kind } else { return None; }; - let hir::ItemKind::TyAlias(ty, generics) = alias else { return None }; + let hir::ItemKind::TyAlias(_, ty, generics) = alias else { return None }; let final_seg = &path.segments.last().expect("segments were empty"); let mut args = DefIdMap::default(); @@ -1929,16 +1914,13 @@ fn clean_trait_object_lifetime_bound<'tcx>( // Since there is a semantic difference between an implicitly elided (i.e. "defaulted") object // lifetime and an explicitly elided object lifetime (`'_`), we intentionally don't hide the // latter contrary to `clean_middle_region`. - match *region { + match region.kind() { ty::ReStatic => Some(Lifetime::statik()), - ty::ReEarlyParam(region) if region.name != kw::Empty => Some(Lifetime(region.name)), - ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(_, name), .. }) - if name != kw::Empty => - { + ty::ReEarlyParam(region) => Some(Lifetime(region.name)), + ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(_, name), .. }) => { Some(Lifetime(name)) } - ty::ReEarlyParam(_) - | ty::ReBound(..) + ty::ReBound(..) | ty::ReLateParam(_) | ty::ReVar(_) | ty::RePlaceholder(_) @@ -1963,7 +1945,7 @@ fn can_elide_trait_object_lifetime_bound<'tcx>( // > If there is a unique bound from the containing type then that is the default // If there is a default object lifetime and the given region is lexically equal to it, elide it. match default { - ObjectLifetimeDefault::Static => return *region == ty::ReStatic, + ObjectLifetimeDefault::Static => return region.kind() == ty::ReStatic, // FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly. ObjectLifetimeDefault::Arg(default) => return region.get_name() == default.get_name(), // > If there is more than one bound from the containing type then an explicit bound must be specified @@ -1983,7 +1965,7 @@ fn can_elide_trait_object_lifetime_bound<'tcx>( // Note however that at the time of this writing it should be fine to disregard this subtlety // as we neither render const exprs faithfully anyway (hiding them in some places or using `_` instead) // nor show the contents of fn bodies. - [] => *region == ty::ReStatic, + [] => region.kind() == ty::ReStatic, // > If the trait is defined with a single lifetime bound then that bound is used. // > If 'static is used for any lifetime bound then 'static is used. // FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly. @@ -2130,7 +2112,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ); Type::Path { path } } - ty::Dynamic(obj, ref reg, _) => { + ty::Dynamic(obj, reg, _) => { // HACK: pick the first `did` as the `did` of the trait object. Someone // might want to implement "native" support for marker-trait-only // trait objects. @@ -2147,7 +2129,7 @@ pub(crate) fn clean_middle_ty<'tcx>( inline::record_extern_fqn(cx, did, ItemType::Trait); - let lifetime = clean_trait_object_lifetime_bound(*reg, container, obj, cx.tcx); + let lifetime = clean_trait_object_lifetime_bound(reg, container, obj, cx.tcx); let mut bounds = dids .map(|did| { @@ -2214,7 +2196,7 @@ pub(crate) fn clean_middle_ty<'tcx>( Type::QPath(Box::new(QPathData { assoc: PathSegment { - name: cx.tcx.associated_item(def_id).name, + name: cx.tcx.associated_item(def_id).name(), args: GenericArgs::AngleBracketed { args: clean_middle_generic_args( cx, @@ -2231,9 +2213,9 @@ pub(crate) fn clean_middle_ty<'tcx>( })) } - ty::Alias(ty::Weak, data) => { + ty::Alias(ty::Free, data) => { if cx.tcx.features().lazy_type_alias() { - // Weak type alias `data` represents the `type X` in `type X = Y`. If we need `Y`, + // Free type alias `data` represents the `type X` in `type X = Y`. If we need `Y`, // we need to use `type_of`. let path = clean_middle_path( cx, @@ -2331,25 +2313,22 @@ fn clean_middle_opaque_bounds<'tcx>( let bindings: ThinVec<_> = bounds .iter() .filter_map(|(bound, _)| { - if let ty::ClauseKind::Projection(proj) = bound.kind().skip_binder() { - if proj.projection_term.trait_ref(cx.tcx) == trait_ref.skip_binder() { - Some(AssocItemConstraint { - assoc: projection_to_path_segment( - // FIXME: This needs to be made resilient for `AliasTerm`s that - // are associated consts. - bound.kind().rebind(proj.projection_term.expect_ty(cx.tcx)), - cx, - ), - kind: AssocItemConstraintKind::Equality { - term: clean_middle_term(bound.kind().rebind(proj.term), cx), - }, - }) - } else { - None - } - } else { - None + if let ty::ClauseKind::Projection(proj) = bound.kind().skip_binder() + && proj.projection_term.trait_ref(cx.tcx) == trait_ref.skip_binder() + { + return Some(AssocItemConstraint { + assoc: projection_to_path_segment( + // FIXME: This needs to be made resilient for `AliasTerm`s that + // are associated consts. + bound.kind().rebind(proj.projection_term.expect_ty(cx.tcx)), + cx, + ), + kind: AssocItemConstraintKind::Equality { + term: clean_middle_term(bound.kind().rebind(proj.term), cx), + }, + }); } + None }) .collect(); @@ -2371,7 +2350,18 @@ fn clean_middle_opaque_bounds<'tcx>( } if let Some(args) = cx.tcx.rendered_precise_capturing_args(impl_trait_def_id) { - bounds.push(GenericBound::Use(args.to_vec())); + bounds.push(GenericBound::Use( + args.iter() + .map(|arg| match arg { + hir::PreciseCapturingArgKind::Lifetime(lt) => { + PreciseCapturingArg::Lifetime(Lifetime(*lt)) + } + hir::PreciseCapturingArgKind::Param(param) => { + PreciseCapturingArg::Param(*param) + } + }) + .collect(), + )); } ImplTrait(bounds) @@ -2537,37 +2527,42 @@ fn clean_generic_args<'tcx>( generic_args: &hir::GenericArgs<'tcx>, cx: &mut DocContext<'tcx>, ) -> GenericArgs { - // FIXME(return_type_notation): Fix RTN parens rendering - if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() { - let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect::>().into(); - let output = match output.kind { - hir::TyKind::Tup(&[]) => None, - _ => Some(Box::new(clean_ty(output, cx))), - }; - GenericArgs::Parenthesized { inputs, output } - } else { - let args = generic_args - .args - .iter() - .map(|arg| match arg { - hir::GenericArg::Lifetime(lt) if !lt.is_anonymous() => { - GenericArg::Lifetime(clean_lifetime(lt, cx)) - } - hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()), - hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty.as_unambig_ty(), cx)), - hir::GenericArg::Const(ct) => { - GenericArg::Const(Box::new(clean_const(ct.as_unambig_ct(), cx))) - } - hir::GenericArg::Infer(_inf) => GenericArg::Infer, - }) - .collect::>() - .into(); - let constraints = generic_args - .constraints - .iter() - .map(|c| clean_assoc_item_constraint(c, cx)) - .collect::>(); - GenericArgs::AngleBracketed { args, constraints } + match generic_args.parenthesized { + hir::GenericArgsParentheses::No => { + let args = generic_args + .args + .iter() + .map(|arg| match arg { + hir::GenericArg::Lifetime(lt) if !lt.is_anonymous() => { + GenericArg::Lifetime(clean_lifetime(lt, cx)) + } + hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()), + hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty.as_unambig_ty(), cx)), + hir::GenericArg::Const(ct) => { + GenericArg::Const(Box::new(clean_const(ct.as_unambig_ct(), cx))) + } + hir::GenericArg::Infer(_inf) => GenericArg::Infer, + }) + .collect(); + let constraints = generic_args + .constraints + .iter() + .map(|c| clean_assoc_item_constraint(c, cx)) + .collect::>(); + GenericArgs::AngleBracketed { args, constraints } + } + hir::GenericArgsParentheses::ParenSugar => { + let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() else { + bug!(); + }; + let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect(); + let output = match output.kind { + hir::TyKind::Tup(&[]) => None, + _ => Some(Box::new(clean_ty(output, cx))), + }; + GenericArgs::Parenthesized { inputs, output } + } + hir::GenericArgsParentheses::ReturnTypeNotation => GenericArgs::ReturnTypeNotation, } } @@ -2583,15 +2578,25 @@ fn clean_bare_fn_ty<'tcx>( cx: &mut DocContext<'tcx>, ) -> BareFunctionDecl { let (generic_params, decl) = enter_impl_trait(cx, |cx| { - // NOTE: generics must be cleaned before args + // NOTE: Generics must be cleaned before params. let generic_params = bare_fn .generic_params .iter() .filter(|p| !is_elided_lifetime(p)) .map(|x| clean_generic_param(cx, None, x)) .collect(); - let args = clean_args_from_types_and_names(cx, bare_fn.decl.inputs, bare_fn.param_names); - let decl = clean_fn_decl_with_args(cx, bare_fn.decl, None, args); + // Since it's more conventional stylistically, elide the name of all params called `_` + // unless there's at least one interestingly named param in which case don't elide any + // name since mixing named and unnamed params is less legible. + let filter = |ident: Option| { + ident.map(|ident| ident.name).filter(|&ident| ident != kw::Underscore) + }; + let fallback = + bare_fn.param_idents.iter().copied().find_map(filter).map(|_| kw::Underscore); + let params = clean_params(cx, bare_fn.decl.inputs, bare_fn.param_idents, |ident| { + filter(ident).or(fallback) + }); + let decl = clean_fn_decl_with_params(cx, bare_fn.decl, None, params); (generic_params, decl) }); BareFunctionDecl { safety: bare_fn.safety, abi: bare_fn.abi, decl, generic_params } @@ -2601,7 +2606,6 @@ fn clean_unsafe_binder_ty<'tcx>( unsafe_binder_ty: &hir::UnsafeBinderTy<'tcx>, cx: &mut DocContext<'tcx>, ) -> UnsafeBinderTy { - // NOTE: generics must be cleaned before args let generic_params = unsafe_binder_ty .generic_params .iter() @@ -2688,7 +2692,7 @@ fn filter_doc_attr_ident(ident: Symbol, is_inline: bool) -> bool { /// Before calling this function, make sure `normal` is a `#[doc]` attribute. fn filter_doc_attr(args: &mut hir::AttrArgs, is_inline: bool) { match args { - hir::AttrArgs::Delimited(ref mut args) => { + hir::AttrArgs::Delimited(args) => { let tokens = filter_tokens_from_list(&args.tokens, |token| { !matches!( token, @@ -2743,23 +2747,20 @@ fn add_without_unwanted_attributes<'hir>( } let mut attr = attr.clone(); match attr { - hir::Attribute::Unparsed(ref mut normal) => { - if let [ident] = &*normal.path.segments { - let ident = ident.name; - if ident == sym::doc { - filter_doc_attr(&mut normal.args, is_inline); - attrs.push((Cow::Owned(attr), import_parent)); - } else if is_inline || ident != sym::cfg { - // If it's not a `cfg()` attribute, we keep it. - attrs.push((Cow::Owned(attr), import_parent)); - } - } - } - hir::Attribute::Parsed(..) => { - if is_inline { + hir::Attribute::Unparsed(ref mut normal) if let [ident] = &*normal.path.segments => { + let ident = ident.name; + if ident == sym::doc { + filter_doc_attr(&mut normal.args, is_inline); + attrs.push((Cow::Owned(attr), import_parent)); + } else if is_inline || ident != sym::cfg_trace { + // If it's not a `cfg()` attribute, we keep it. attrs.push((Cow::Owned(attr), import_parent)); } } + hir::Attribute::Parsed(..) if is_inline => { + attrs.push((Cow::Owned(attr), import_parent)); + } + _ => {} } } } @@ -2772,21 +2773,47 @@ fn clean_maybe_renamed_item<'tcx>( ) -> Vec { use hir::ItemKind; + fn get_name( + cx: &DocContext<'_>, + item: &hir::Item<'_>, + renamed: Option, + ) -> Option { + renamed.or_else(|| cx.tcx.hir_opt_name(item.hir_id())) + } + let def_id = item.owner_id.to_def_id(); - let mut name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id())); cx.with_param_env(def_id, |cx| { + // These kinds of item either don't need a `name` or accept a `None` one so we handle them + // before. + match item.kind { + ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx), + ItemKind::Use(path, kind) => { + return clean_use_statement( + item, + get_name(cx, item, renamed), + path, + kind, + cx, + &mut FxHashSet::default(), + ); + } + _ => {} + } + + let mut name = get_name(cx, item, renamed).unwrap(); + let kind = match item.kind { - ItemKind::Static(ty, mutability, body_id) => StaticItem(Static { + ItemKind::Static(_, ty, mutability, body_id) => StaticItem(Static { type_: Box::new(clean_ty(ty, cx)), mutability, expr: Some(body_id), }), - ItemKind::Const(ty, generics, body_id) => ConstantItem(Box::new(Constant { + ItemKind::Const(_, ty, generics, body_id) => ConstantItem(Box::new(Constant { generics: clean_generics(generics, cx), type_: clean_ty(ty, cx), kind: ConstantKind::Local { body: body_id, def_id }, })), - ItemKind::TyAlias(hir_ty, generics) => { + ItemKind::TyAlias(_, hir_ty, generics) => { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; let rustdoc_ty = clean_ty(hir_ty, cx); let type_ = @@ -2819,34 +2846,33 @@ fn clean_maybe_renamed_item<'tcx>( )); return ret; } - ItemKind::Enum(ref def, generics) => EnumItem(Enum { + ItemKind::Enum(_, def, generics) => EnumItem(Enum { variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(), generics: clean_generics(generics, cx), }), - ItemKind::TraitAlias(generics, bounds) => TraitAliasItem(TraitAlias { + ItemKind::TraitAlias(_, generics, bounds) => TraitAliasItem(TraitAlias { generics: clean_generics(generics, cx), bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }), - ItemKind::Union(ref variant_data, generics) => UnionItem(Union { + ItemKind::Union(_, variant_data, generics) => UnionItem(Union { generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), - ItemKind::Struct(ref variant_data, generics) => StructItem(Struct { + ItemKind::Struct(_, variant_data, generics) => StructItem(Struct { ctor_kind: variant_data.ctor_kind(), generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), - ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx), - ItemKind::Macro(macro_def, MacroKind::Bang) => MacroItem(Macro { + ItemKind::Macro(_, macro_def, MacroKind::Bang) => MacroItem(Macro { source: display_macro_source(cx, name, macro_def), macro_rules: macro_def.macro_rules, }), - ItemKind::Macro(_, macro_kind) => clean_proc_macro(item, &mut name, macro_kind, cx), + ItemKind::Macro(_, _, macro_kind) => clean_proc_macro(item, &mut name, macro_kind, cx), // proc macros can have a name set by attributes ItemKind::Fn { ref sig, generics, body: body_id, .. } => { clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) } - ItemKind::Trait(_, _, generics, bounds, item_ids) => { + ItemKind::Trait(_, _, _, generics, bounds, item_ids) => { let items = item_ids .iter() .map(|ti| clean_trait_item(cx.tcx.hir_trait_item(ti.id), cx)) @@ -2859,12 +2885,9 @@ fn clean_maybe_renamed_item<'tcx>( bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), })) } - ItemKind::ExternCrate(orig_name) => { + ItemKind::ExternCrate(orig_name, _) => { return clean_extern_crate(item, name, orig_name, cx); } - ItemKind::Use(path, kind) => { - return clean_use_statement(item, name, path, kind, cx, &mut FxHashSet::default()); - } _ => span_bug!(item.span, "not yet converted"), }; @@ -2948,7 +2971,7 @@ fn clean_extern_crate<'tcx>( let cnum = cx.tcx.extern_mod_stmt_cnum(krate.owner_id.def_id).unwrap_or(LOCAL_CRATE); // this is the ID of the crate itself let crate_def_id = cnum.as_def_id(); - let attrs = cx.tcx.hir().attrs(krate.hir_id()); + let attrs = cx.tcx.hir_attrs(krate.hir_id()); let ty_vis = cx.tcx.visibility(krate.owner_id); let please_inline = ty_vis.is_public() && attrs.iter().any(|a| { @@ -2961,16 +2984,16 @@ fn clean_extern_crate<'tcx>( && !cx.is_json_output(); let krate_owner_def_id = krate.owner_id.def_id; - if please_inline { - if let Some(items) = inline::try_inline( + if please_inline + && let Some(items) = inline::try_inline( cx, Res::Def(DefKind::Mod, crate_def_id), name, Some((attrs, Some(krate_owner_def_id))), &mut Default::default(), - ) { - return items; - } + ) + { + return items; } vec![Item::from_def_id_and_parts( @@ -2983,7 +3006,7 @@ fn clean_extern_crate<'tcx>( fn clean_use_statement<'tcx>( import: &hir::Item<'tcx>, - name: Symbol, + name: Option, path: &hir::UsePath<'tcx>, kind: hir::UseKind, cx: &mut DocContext<'tcx>, @@ -3000,7 +3023,7 @@ fn clean_use_statement<'tcx>( fn clean_use_statement_inner<'tcx>( import: &hir::Item<'tcx>, - name: Symbol, + name: Option, path: &hir::Path<'tcx>, kind: hir::UseKind, cx: &mut DocContext<'tcx>, @@ -3017,9 +3040,9 @@ fn clean_use_statement_inner<'tcx>( } let visibility = cx.tcx.visibility(import.owner_id); - let attrs = cx.tcx.hir().attrs(import.hir_id()); + let attrs = cx.tcx.hir_attrs(import.hir_id()); let inline_attr = hir_attr_lists(attrs, sym::doc).get_word_attr(sym::inline); - let pub_underscore = visibility.is_public() && name == kw::Underscore; + let pub_underscore = visibility.is_public() && name == Some(kw::Underscore); let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id); let import_def_id = import.owner_id.def_id; @@ -3085,6 +3108,7 @@ fn clean_use_statement_inner<'tcx>( } Import::new_glob(resolve_use_source(cx, path), true) } else { + let name = name.unwrap(); if inline_attr.is_none() && let Res::Def(DefKind::Mod, did) = path.res && !did.is_local() @@ -3125,8 +3149,8 @@ fn clean_maybe_renamed_foreign_item<'tcx>( let def_id = item.owner_id.to_def_id(); cx.with_param_env(def_id, |cx| { let kind = match item.kind { - hir::ForeignItemKind::Fn(sig, names, generics) => ForeignFunctionItem( - clean_function(cx, &sig, generics, FunctionArgs::Names(names)), + hir::ForeignItemKind::Fn(sig, idents, generics) => ForeignFunctionItem( + clean_function(cx, &sig, generics, ParamsSrc::Idents(idents)), sig.header.safety(), ), hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem( diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs index 3cc5f8d615a4e..d684e6f8650f9 100644 --- a/src/librustdoc/clean/render_macro_matchers.rs +++ b/src/librustdoc/clean/render_macro_matchers.rs @@ -1,11 +1,11 @@ -use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw}; +use rustc_ast::token::{self, Delimiter, IdentIsRaw}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast_pretty::pprust::PrintState; use rustc_ast_pretty::pprust::state::State as Printer; use rustc_middle::ty::TyCtxt; use rustc_session::parse::ParseSess; -use rustc_span::Span; use rustc_span::symbol::{Ident, Symbol, kw}; +use rustc_span::{FileName, Span}; /// Render a macro matcher in a format suitable for displaying to the user /// as part of an item declaration. @@ -34,20 +34,20 @@ pub(super) fn render_macro_matcher(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Stri // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~! // ) => {...}; // } - printer.cbox(8); + let cb = printer.cbox(8); printer.word("("); printer.zerobreak(); - printer.ibox(0); + let ib = printer.ibox(0); match matcher { TokenTree::Delimited(_span, _spacing, _delim, tts) => print_tts(&mut printer, tts), // Matcher which is not a Delimited is unexpected and should've failed // to compile, but we render whatever it is wrapped in parens. TokenTree::Token(..) => print_tt(&mut printer, matcher), } - printer.end(); + printer.end(ib); printer.break_offset_if_not_bol(0, -4); printer.word(")"); - printer.end(); + printer.end(cb); printer.s.eof() } @@ -63,7 +63,7 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option parser, @@ -96,7 +96,7 @@ fn print_tt(printer: &mut Printer<'_>, tt: &TokenTree) { } } TokenTree::Delimited(_span, _spacing, delim, tts) => { - let open_delim = printer.token_kind_to_string(&token::OpenDelim(*delim)); + let open_delim = printer.token_kind_to_string(&delim.as_open_token_kind()); printer.word(open_delim); if !tts.is_empty() { if *delim == Delimiter::Brace { @@ -107,7 +107,7 @@ fn print_tt(printer: &mut Printer<'_>, tt: &TokenTree) { printer.space(); } } - let close_delim = printer.token_kind_to_string(&token::CloseDelim(*delim)); + let close_delim = printer.token_kind_to_string(&delim.as_close_token_kind()); printer.word(close_delim); } } @@ -137,15 +137,10 @@ fn print_tts(printer: &mut Printer<'_>, tts: &TokenStream) { (Dollar, token::Ident(..)) => (false, DollarIdent), (DollarIdent, token::Colon) => (false, DollarIdentColon), (DollarIdentColon, token::Ident(..)) => (false, Other), - ( - DollarParen, - token::BinOp(BinOpToken::Plus | BinOpToken::Star) | token::Question, - ) => (false, Other), + (DollarParen, token::Plus | token::Star | token::Question) => (false, Other), (DollarParen, _) => (false, DollarParenSep), - (DollarParenSep, token::BinOp(BinOpToken::Plus | BinOpToken::Star)) => { - (false, Other) - } - (Pound, token::Not) => (false, PoundBang), + (DollarParenSep, token::Plus | token::Star) => (false, Other), + (Pound, token::Bang) => (false, PoundBang), (_, token::Ident(symbol, IdentIsRaw::No)) if !usually_needs_space_between_keyword_and_open_delim(*symbol, tt.span) => { @@ -172,7 +167,7 @@ fn print_tts(printer: &mut Printer<'_>, tts: &TokenStream) { } fn usually_needs_space_between_keyword_and_open_delim(symbol: Symbol, span: Span) -> bool { - let ident = Ident { name: symbol, span }; + let ident = Ident::new(symbol, span); let is_keyword = ident.is_used_keyword() || ident.is_unused_keyword(); if !is_keyword { // An identifier that is not a keyword usually does not need a space diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index b8db82e540c61..41943b94d1e21 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -102,6 +102,10 @@ pub(crate) fn merge_bounds( } } }, + PP::ReturnTypeNotation => { + // Cannot merge bounds with RTN. + return false; + } }; true }) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 178b6a60b41f7..e45f28444fe0d 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -5,13 +5,14 @@ use std::{fmt, iter}; use arrayvec::ArrayVec; use rustc_abi::{ExternAbi, VariantIdx}; -use rustc_attr_parsing::{ConstStability, Deprecation, Stability, StableSince}; +use rustc_attr_data_structures::{ + AttributeKind, ConstStability, Deprecation, Stability, StableSince, +}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::{BodyId, Mutability}; -use rustc_hir_analysis::check::intrinsic::intrinsic_operation_unsafety; use rustc_index::IndexVec; use rustc_metadata::rendered_const; use rustc_middle::span_bug; @@ -208,11 +209,11 @@ impl ExternalCrate { .get_attrs(def_id, sym::doc) .flat_map(|attr| attr.meta_item_list().unwrap_or_default()); for meta in meta_items { - if meta.has_name(sym::keyword) { - if let Some(v) = meta.value_str() { - keyword = Some(v); - break; - } + if meta.has_name(sym::keyword) + && let Some(v) = meta.value_str() + { + keyword = Some(v); + break; } } return keyword.map(|p| (def_id, p)); @@ -226,7 +227,7 @@ impl ExternalCrate { .filter_map(|&id| { let item = tcx.hir_item(id); match item.kind { - hir::ItemKind::Mod(_) => { + hir::ItemKind::Mod(..) => { as_keyword(Res::Def(DefKind::Mod, id.owner_id.to_def_id())) } _ => None, @@ -282,7 +283,7 @@ impl ExternalCrate { .filter_map(|&id| { let item = tcx.hir_item(id); match item.kind { - hir::ItemKind::Mod(_) => { + hir::ItemKind::Mod(..) => { as_primitive(Res::Def(DefKind::Mod, id.owner_id.to_def_id())) } _ => None, @@ -311,26 +312,31 @@ pub(crate) enum ExternalLocation { /// directly to the AST's concept of an item; it's a strict superset. #[derive(Clone)] pub(crate) struct Item { - /// The name of this item. - /// Optional because not every item has a name, e.g. impls. - pub(crate) name: Option, pub(crate) inner: Box, - pub(crate) item_id: ItemId, - /// This is the `LocalDefId` of the `use` statement if the item was inlined. - /// The crate metadata doesn't hold this information, so the `use` statement - /// always belongs to the current crate. - pub(crate) inline_stmt_id: Option, - pub(crate) cfg: Option>, } +// Why does the `Item`/`ItemInner` split exist? `Vec`s are common, and +// without the split `Item` would be a large type (100+ bytes) which results in +// lots of wasted space in the unused parts of a `Vec`. With the split, +// `Item` is just 8 bytes, and the wasted space is avoided, at the cost of an +// extra allocation per item. This is a performance win. #[derive(Clone)] pub(crate) struct ItemInner { + /// The name of this item. + /// Optional because not every item has a name, e.g. impls. + pub(crate) name: Option, /// Information about this item that is specific to what kind of item it is. /// E.g., struct vs enum vs function. pub(crate) kind: ItemKind, pub(crate) attrs: Attributes, /// The effective stability, filled out by the `propagate-stability` pass. pub(crate) stability: Option, + pub(crate) item_id: ItemId, + /// This is the `LocalDefId` of the `use` statement if the item was inlined. + /// The crate metadata doesn't hold this information, so the `use` statement + /// always belongs to the current crate. + pub(crate) inline_stmt_id: Option, + pub(crate) cfg: Option>, } impl std::ops::Deref for Item { @@ -362,10 +368,7 @@ impl fmt::Debug for Item { pub(crate) fn rustc_span(def_id: DefId, tcx: TyCtxt<'_>) -> Span { Span::new(def_id.as_local().map_or_else( || tcx.def_span(def_id), - |local| { - let hir = tcx.hir(); - hir.span_with_body(tcx.local_def_id_to_hir_id(local)) - }, + |local| tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(local)), )) } @@ -402,13 +405,13 @@ impl Item { // versions; the paths that are exposed through it are "deprecated" because they // were never supposed to work at all. let stab = self.stability(tcx)?; - if let rustc_attr_parsing::StabilityLevel::Stable { + if let rustc_attr_data_structures::StabilityLevel::Stable { allowed_through_unstable_modules: Some(note), .. } = stab.level { Some(Deprecation { - since: rustc_attr_parsing::DeprecatedSince::Unspecified, + since: rustc_attr_data_structures::DeprecatedSince::Unspecified, note: Some(note), suggestion: None, }) @@ -488,11 +491,15 @@ impl Item { trace!("name={name:?}, def_id={def_id:?} cfg={cfg:?}"); Item { - item_id: def_id.into(), - inner: Box::new(ItemInner { kind, attrs, stability: None }), - name, - cfg, - inline_stmt_id: None, + inner: Box::new(ItemInner { + item_id: def_id.into(), + kind, + attrs, + stability: None, + name, + cfg, + inline_stmt_id: None, + }), } } @@ -502,7 +509,7 @@ impl Item { let Some(links) = cx.cache().intra_doc_links.get(&self.item_id) else { return vec![] }; links .iter() - .filter_map(|ItemLink { link: s, link_text, page_id: id, ref fragment }| { + .filter_map(|ItemLink { link: s, link_text, page_id: id, fragment }| { debug!(?id); if let Ok((mut href, ..)) = href(*id, cx) { debug!(?href); @@ -512,7 +519,7 @@ impl Item { Some(RenderedLink { original_text: s.clone(), new_text: link_text.clone(), - tooltip: link_tooltip(*id, fragment, cx), + tooltip: link_tooltip(*id, fragment, cx).to_string(), href, }) } else { @@ -681,8 +688,6 @@ impl Item { hir::FnHeader { safety: if tcx.codegen_fn_attrs(def_id).safe_target_features { hir::HeaderSafety::SafeTargetFeatures - } else if abi == ExternAbi::RustIntrinsic { - intrinsic_operation_unsafety(tcx, def_id.expect_local()).into() } else { safety.into() }, @@ -756,12 +761,7 @@ impl Item { Some(tcx.visibility(def_id)) } - pub(crate) fn attributes( - &self, - tcx: TyCtxt<'_>, - cache: &Cache, - keep_as_is: bool, - ) -> Vec { + pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Vec { const ALLOWED_ATTRIBUTES: &[Symbol] = &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive]; @@ -772,9 +772,29 @@ impl Item { .other_attrs .iter() .filter_map(|attr| { - if keep_as_is { - Some(rustc_hir_pretty::attribute_to_string(&tcx, attr)) - } else if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) { + if is_json { + match attr { + hir::Attribute::Parsed(AttributeKind::Deprecation { .. }) => { + // rustdoc-json stores this in `Item::deprecation`, so we + // don't want it it `Item::attrs`. + None + } + rustc_hir::Attribute::Parsed( + rustc_attr_data_structures::AttributeKind::Repr(..), + ) => { + // We have separate pretty-printing logic for `#[repr(..)]` attributes. + // For example, there are circumstances where `#[repr(transparent)]` + // is applied but should not be publicly shown in rustdoc + // because it isn't public API. + None + } + _ => Some({ + let mut s = rustc_hir_pretty::attribute_to_string(&tcx, attr); + assert_eq!(s.pop(), Some('\n')); + s + }), + } + } else if attr.has_any_name(ALLOWED_ATTRIBUTES) { Some( rustc_hir_pretty::attribute_to_string(&tcx, attr) .replace("\\\n", "") @@ -786,8 +806,9 @@ impl Item { } }) .collect(); - if !keep_as_is - && let Some(def_id) = self.def_id() + + // Add #[repr(...)] + if let Some(def_id) = self.def_id() && let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_() { let adt = tcx.adt_def(def_id); @@ -1001,7 +1022,6 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet, ) -> Option> { - let sess = tcx.sess; let doc_cfg_active = tcx.features().doc_cfg(); let doc_auto_cfg_active = tcx.features().doc_auto_cfg(); @@ -1022,15 +1042,33 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator .filter(|attr| attr.has_name(sym::cfg)) .peekable(); if doc_cfg.peek().is_some() && doc_cfg_active { - doc_cfg - .filter_map(|attr| Cfg::parse(&attr).ok()) - .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) + let sess = tcx.sess; + doc_cfg.fold(Cfg::True, |mut cfg, item| { + if let Some(cfg_mi) = + item.meta_item().and_then(|item| rustc_expand::config::parse_cfg(item, sess)) + { + // The result is unused here but we can gate unstable predicates + rustc_attr_parsing::cfg_matches( + cfg_mi, + tcx.sess, + rustc_ast::CRATE_NODE_ID, + Some(tcx.features()), + ); + match Cfg::parse(cfg_mi) { + Ok(new_cfg) => cfg &= new_cfg, + Err(e) => { + sess.dcx().span_err(e.span, e.msg); + } + } + } + cfg + }) } else if doc_auto_cfg_active { // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because // `doc(cfg())` overrides `cfg()`). attrs .clone() - .filter(|attr| attr.has_name(sym::cfg)) + .filter(|attr| attr.has_name(sym::cfg_trace)) .filter_map(|attr| single(attr.meta_item_list()?)) .filter_map(|attr| Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten()) .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) @@ -1041,46 +1079,17 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator Cfg::True }; - for attr in attrs.clone() { - // #[doc] - if attr.doc_str().is_none() && attr.has_name(sym::doc) { - // #[doc(...)] - if let Some(list) = attr.meta_item_list() { - for item in list { - // #[doc(hidden)] - if !item.has_name(sym::cfg) { - continue; - } - // #[doc(cfg(...))] - if let Some(cfg_mi) = item - .meta_item() - .and_then(|item| rustc_expand::config::parse_cfg(item, sess)) - { - match Cfg::parse(cfg_mi) { - Ok(new_cfg) => cfg &= new_cfg, - Err(e) => { - sess.dcx().span_err(e.span, e.msg); - } - } - } - } - } - } - } - // treat #[target_feature(enable = "feat")] attributes as if they were // #[doc(cfg(target_feature = "feat"))] attributes as well for attr in hir_attr_lists(attrs, sym::target_feature) { - if attr.has_name(sym::enable) { - if attr.value_str().is_some() { - // Clone `enable = "feat"`, change to `target_feature = "feat"`. - // Unwrap is safe because `value_str` succeeded above. - let mut meta = attr.meta_item().unwrap().clone(); - meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature)); - - if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) { - cfg &= feat_cfg; - } + if attr.has_name(sym::enable) && attr.value_str().is_some() { + // Clone `enable = "feat"`, change to `target_feature = "feat"`. + // Unwrap is safe because `value_str` succeeded above. + let mut meta = attr.meta_item().unwrap().clone(); + meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature)); + + if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) { + cfg &= feat_cfg; } } } @@ -1150,7 +1159,7 @@ pub(crate) struct Attributes { } impl Attributes { - pub(crate) fn lists(&self, name: Symbol) -> impl Iterator + '_ { + pub(crate) fn lists(&self, name: Symbol) -> impl Iterator { hir_attr_lists(&self.other_attrs[..], name) } @@ -1160,10 +1169,10 @@ impl Attributes { continue; } - if let Some(items) = attr.meta_item_list() { - if items.iter().filter_map(|i| i.meta_item()).any(|it| it.has_name(flag)) { - return true; - } + if let Some(items) = attr.meta_item_list() + && items.iter().filter_map(|i| i.meta_item()).any(|it| it.has_name(flag)) + { + return true; } } @@ -1242,7 +1251,7 @@ pub(crate) enum GenericBound { TraitBound(PolyTrait, hir::TraitBoundModifiers), Outlives(Lifetime), /// `use<'a, T>` precise-capturing bound syntax - Use(Vec), + Use(Vec), } impl GenericBound { @@ -1306,6 +1315,21 @@ impl Lifetime { } } +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +pub(crate) enum PreciseCapturingArg { + Lifetime(Lifetime), + Param(Symbol), +} + +impl PreciseCapturingArg { + pub(crate) fn name(self) -> Symbol { + match self { + PreciseCapturingArg::Lifetime(lt) => lt.0, + PreciseCapturingArg::Param(param) => param, + } + } +} + #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) enum WherePredicate { BoundPredicate { ty: Type, bounds: Vec, bound_params: Vec }, @@ -1315,9 +1339,9 @@ pub(crate) enum WherePredicate { impl WherePredicate { pub(crate) fn get_bounds(&self) -> Option<&[GenericBound]> { - match *self { - WherePredicate::BoundPredicate { ref bounds, .. } => Some(bounds), - WherePredicate::RegionPredicate { ref bounds, .. } => Some(bounds), + match self { + WherePredicate::BoundPredicate { bounds, .. } => Some(bounds), + WherePredicate::RegionPredicate { bounds, .. } => Some(bounds), _ => None, } } @@ -1389,34 +1413,30 @@ pub(crate) struct Function { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct FnDecl { - pub(crate) inputs: Arguments, + pub(crate) inputs: Vec, pub(crate) output: Type, pub(crate) c_variadic: bool, } impl FnDecl { pub(crate) fn receiver_type(&self) -> Option<&Type> { - self.inputs.values.first().and_then(|v| v.to_receiver()) + self.inputs.first().and_then(|v| v.to_receiver()) } } +/// A function parameter. #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub(crate) struct Arguments { - pub(crate) values: Vec, -} - -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub(crate) struct Argument { +pub(crate) struct Parameter { + pub(crate) name: Option, pub(crate) type_: Type, - pub(crate) name: Symbol, /// This field is used to represent "const" arguments from the `rustc_legacy_const_generics` /// feature. More information in . pub(crate) is_const: bool, } -impl Argument { +impl Parameter { pub(crate) fn to_receiver(&self) -> Option<&Type> { - if self.name == kw::SelfLower { Some(&self.type_) } else { None } + if self.name == Some(kw::SelfLower) { Some(&self.type_) } else { None } } } @@ -1520,6 +1540,10 @@ impl Type { matches!(self, Type::BorrowedRef { .. }) } + fn is_type_alias(&self) -> bool { + matches!(self, Type::Path { path: Path { res: Res::Def(DefKind::TyAlias, _), .. } }) + } + /// Check if two types are "the same" for documentation purposes. /// /// This is different from `Eq`, because it knows that things like @@ -1548,6 +1572,16 @@ impl Type { } else { (self, other) }; + + // FIXME: `Cache` does not have the data required to unwrap type aliases, + // so we just assume they are equal. + // This is only remotely acceptable because we were previously + // assuming all types were equal when used + // as a generic parameter of a type in `Deref::Target`. + if self_cleared.is_type_alias() || other_cleared.is_type_alias() { + return true; + } + match (self_cleared, other_cleared) { // Recursive cases. (Type::Tuple(a), Type::Tuple(b)) => { @@ -1677,13 +1711,13 @@ impl Type { /// /// [clean]: crate::clean pub(crate) fn def_id(&self, cache: &Cache) -> Option { - let t: PrimitiveType = match *self { - Type::Path { ref path } => return Some(path.def_id()), - DynTrait(ref bounds, _) => return bounds.first().map(|b| b.trait_.def_id()), - Primitive(p) => return cache.primitive_locations.get(&p).cloned(), + let t: PrimitiveType = match self { + Type::Path { path } => return Some(path.def_id()), + DynTrait(bounds, _) => return bounds.first().map(|b| b.trait_.def_id()), + Primitive(p) => return cache.primitive_locations.get(p).cloned(), BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference, - BorrowedRef { ref type_, .. } => return type_.def_id(cache), - Tuple(ref tys) => { + BorrowedRef { type_, .. } => return type_.def_id(cache), + Tuple(tys) => { if tys.is_empty() { PrimitiveType::Unit } else { @@ -1695,7 +1729,7 @@ impl Type { Array(..) => PrimitiveType::Array, Type::Pat(..) => PrimitiveType::Pat, RawPointer(..) => PrimitiveType::RawPointer, - QPath(box QPathData { ref self_type, .. }) => return self_type.def_id(cache), + QPath(box QPathData { self_type, .. }) => return self_type.def_id(cache), Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => return None, }; Primitive(t).def_id(cache) @@ -1864,7 +1898,7 @@ impl PrimitiveType { .copied() } - pub(crate) fn all_impls(tcx: TyCtxt<'_>) -> impl Iterator + '_ { + pub(crate) fn all_impls(tcx: TyCtxt<'_>) -> impl Iterator { Self::simplified_types() .values() .flatten() @@ -2246,8 +2280,12 @@ impl GenericArg { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum GenericArgs { + /// `` AngleBracketed { args: ThinVec, constraints: ThinVec }, + /// `(inputs) -> output` Parenthesized { inputs: ThinVec, output: Option> }, + /// `(..)` + ReturnTypeNotation, } impl GenericArgs { @@ -2257,9 +2295,10 @@ impl GenericArgs { args.is_empty() && constraints.is_empty() } GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(), + GenericArgs::ReturnTypeNotation => false, } } - pub(crate) fn constraints<'a>(&'a self) -> Box + 'a> { + pub(crate) fn constraints(&self) -> Box + '_> { match self { GenericArgs::AngleBracketed { constraints, .. } => { Box::new(constraints.iter().cloned()) @@ -2281,6 +2320,7 @@ impl GenericArgs { }) .into_iter(), ), + GenericArgs::ReturnTypeNotation => Box::new([].into_iter()), } } } @@ -2292,8 +2332,10 @@ impl<'a> IntoIterator for &'a GenericArgs { match self { GenericArgs::AngleBracketed { args, .. } => Box::new(args.iter().cloned()), GenericArgs::Parenthesized { inputs, .. } => { + // FIXME: This isn't really right, since `Fn(A, B)` is `Fn<(A, B)>` Box::new(inputs.iter().cloned().map(GenericArg::Type)) } + GenericArgs::ReturnTypeNotation => Box::new([].into_iter()), } } } @@ -2419,7 +2461,7 @@ impl ConstantKind { ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { rendered_const(tcx, tcx.hir_body(body), tcx.hir_body_owner_def_id(body)) } - ConstantKind::Infer { .. } => "_".to_string(), + ConstantKind::Infer => "_".to_string(), } } @@ -2464,7 +2506,7 @@ impl Impl { self.trait_ .as_ref() .map(|t| t.def_id()) - .map(|did| tcx.provided_trait_methods(did).map(|meth| meth.name).collect()) + .map(|did| tcx.provided_trait_methods(did).map(|meth| meth.name()).collect()) .unwrap_or_default() } @@ -2585,13 +2627,14 @@ mod size_asserts { use super::*; // tidy-alphabetical-start - static_assert_size!(Crate, 56); // frequently moved by-value + static_assert_size!(Crate, 16); // frequently moved by-value static_assert_size!(DocFragment, 32); static_assert_size!(GenericArg, 32); static_assert_size!(GenericArgs, 24); static_assert_size!(GenericParamDef, 40); static_assert_size!(Generics, 16); - static_assert_size!(Item, 48); + static_assert_size!(Item, 8); + static_assert_size!(ItemInner, 136); static_assert_size!(ItemKind, 48); static_assert_size!(PathSegment, 32); static_assert_size!(Type, 32); diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 34656b26ce28c..af7986d030ee7 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -60,7 +60,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { let primitives = local_crate.primitives(cx.tcx); let keywords = local_crate.keywords(cx.tcx); { - let ItemKind::ModuleItem(ref mut m) = &mut module.inner.kind else { unreachable!() }; + let ItemKind::ModuleItem(m) = &mut module.inner.kind else { unreachable!() }; m.items.extend(primitives.iter().map(|&(def_id, prim)| { Item::from_def_id_and_parts( def_id, @@ -223,7 +223,7 @@ fn clean_middle_generic_args_with_constraints<'tcx>( let args = clean_middle_generic_args(cx, args.map_bound(|args| &args[..]), has_self, did); - GenericArgs::AngleBracketed { args: args.into(), constraints } + GenericArgs::AngleBracketed { args, constraints } } pub(super) fn clean_middle_path<'tcx>( @@ -234,7 +234,7 @@ pub(super) fn clean_middle_path<'tcx>( args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, ) -> Path { let def_kind = cx.tcx.def_kind(did); - let name = cx.tcx.opt_item_name(did).unwrap_or(kw::Empty); + let name = cx.tcx.opt_item_name(did).unwrap_or(sym::dummy); Path { res: Res::Def(def_kind, did), segments: thin_vec![PathSegment { @@ -302,19 +302,20 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { use rustc_hir::*; debug!("trying to get a name from pattern: {p:?}"); - Symbol::intern(&match p.kind { - // FIXME(never_patterns): does this make sense? - PatKind::Wild - | PatKind::Err(_) + Symbol::intern(&match &p.kind { + PatKind::Err(_) + | PatKind::Missing // Let's not perpetuate anon params from Rust 2015; use `_` for them. | PatKind::Never + | PatKind::Range(..) | PatKind::Struct(..) - | PatKind::Range(..) => { + | PatKind::Wild => { return kw::Underscore; } PatKind::Binding(_, _, ident, _) => return ident.name, PatKind::Box(p) | PatKind::Ref(p, _) | PatKind::Guard(p, _) => return name_from_pat(p), - PatKind::TupleStruct(ref p, ..) - | PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref p), .. }) => qpath_to_string(p), + PatKind::TupleStruct(p, ..) | PatKind::Expr(PatExpr { kind: PatExprKind::Path(p), .. }) => { + qpath_to_string(p) + } PatKind::Or(pats) => { fmt::from_fn(|f| pats.iter().map(|p| name_from_pat(p)).joined(" | ", f)).to_string() } @@ -329,7 +330,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { return Symbol::intern("()"); } PatKind::Slice(begin, mid, end) => { - fn print_pat<'a>(pat: &'a Pat<'a>, wild: bool) -> impl Display + 'a { + fn print_pat(pat: &Pat<'_>, wild: bool) -> impl Display { fmt::from_fn(move |f| { if wild { f.write_str("..")?; @@ -393,7 +394,7 @@ pub(crate) fn print_evaluated_const( fn format_integer_with_underscore_sep(num: &str) -> String { let num_chars: Vec<_> = num.chars().collect(); let mut num_start_index = if num_chars.first() == Some(&'-') { 1 } else { 0 }; - let chunk_size = match num[num_start_index..].as_bytes() { + let chunk_size = match &num.as_bytes()[num_start_index..] { [b'0', b'b' | b'x', ..] => { num_start_index += 2; 4 @@ -493,7 +494,7 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { pub(crate) fn synthesize_auto_trait_and_blanket_impls( cx: &mut DocContext<'_>, item_def_id: DefId, -) -> impl Iterator { +) -> impl Iterator + use<> { let auto_impls = cx .sess() .prof @@ -523,7 +524,7 @@ pub(crate) fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId { | AssocConst | Variant | Fn - | TyAlias { .. } + | TyAlias | Enum | Trait | Struct diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index e67fe6c88ea41..f93aa8ffd0de9 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -103,6 +103,8 @@ pub(crate) struct Options { /// compiling doctests from the crate. pub(crate) edition: Edition, /// The path to the sysroot. Used during the compilation process. + pub(crate) sysroot: PathBuf, + /// Has the same value as `sysroot` except is `None` when the user didn't pass `---sysroot`. pub(crate) maybe_sysroot: Option, /// Lint information passed over the command-line. pub(crate) lint_opts: Vec<(String, Level)>, @@ -122,13 +124,9 @@ pub(crate) struct Options { /// temporary directory if not set. pub(crate) persist_doctests: Option, /// Runtool to run doctests with - pub(crate) runtool: Option, + pub(crate) test_runtool: Option, /// Arguments to pass to the runtool - pub(crate) runtool_args: Vec, - /// Whether to allow ignoring doctests on a per-target basis - /// For example, using ignore-foo to ignore running the doctest on any target that - /// contains "foo" as a substring - pub(crate) enable_per_target_ignores: bool, + pub(crate) test_runtool_args: Vec, /// Do not run doctests, compile them if should_test is active. pub(crate) no_run: bool, /// What sources are being mapped. @@ -176,7 +174,7 @@ pub(crate) struct Options { pub(crate) expanded_args: Vec, /// Arguments to be used when compiling doctests. - pub(crate) doctest_compilation_args: Vec, + pub(crate) doctest_build_args: Vec, } impl fmt::Debug for Options { @@ -202,6 +200,7 @@ impl fmt::Debug for Options { .field("unstable_options", &"...") .field("target", &self.target) .field("edition", &self.edition) + .field("sysroot", &self.sysroot) .field("maybe_sysroot", &self.maybe_sysroot) .field("lint_opts", &self.lint_opts) .field("describe_lints", &self.describe_lints) @@ -212,9 +211,8 @@ impl fmt::Debug for Options { .field("persist_doctests", &self.persist_doctests) .field("show_coverage", &self.show_coverage) .field("crate_version", &self.crate_version) - .field("runtool", &self.runtool) - .field("runtool_args", &self.runtool_args) - .field("enable-per-target-ignores", &self.enable_per_target_ignores) + .field("test_runtool", &self.test_runtool) + .field("test_runtool_args", &self.test_runtool_args) .field("run_check", &self.run_check) .field("no_run", &self.no_run) .field("test_builder_wrappers", &self.test_builder_wrappers) @@ -645,10 +643,10 @@ impl Options { let extension_css = matches.opt_str("e").map(|s| PathBuf::from(&s)); - if let Some(ref p) = extension_css { - if !p.is_file() { - dcx.fatal("option --extend-css argument must be a file"); - } + if let Some(ref p) = extension_css + && !p.is_file() + { + dcx.fatal("option --extend-css argument must be a file"); } let mut themes = Vec::new(); @@ -720,21 +718,16 @@ impl Options { } let index_page = matches.opt_str("index-page").map(|s| PathBuf::from(&s)); - if let Some(ref index_page) = index_page { - if !index_page.is_file() { - dcx.fatal("option `--index-page` argument must be a file"); - } + if let Some(ref index_page) = index_page + && !index_page.is_file() + { + dcx.fatal("option `--index-page` argument must be a file"); } let target = parse_target_triple(early_dcx, matches); let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from); - let sysroot = match &maybe_sysroot { - Some(s) => s.clone(), - None => { - rustc_session::filesearch::get_or_default_sysroot().expect("Failed finding sysroot") - } - }; + let sysroot = rustc_session::filesearch::materialize_sysroot(maybe_sysroot.clone()); let libs = matches .opt_strs("L") @@ -781,9 +774,8 @@ impl Options { let unstable_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"); - let runtool_args = matches.opt_strs("runtool-arg"); - let enable_per_target_ignores = matches.opt_present("enable-per-target-ignores"); + let test_runtool = matches.opt_str("test-runtool"); + let test_runtool_args = matches.opt_strs("test-runtool-arg"); let document_private = matches.opt_present("document-private-items"); let document_hidden = matches.opt_present("document-hidden-items"); let run_check = matches.opt_present("check"); @@ -810,7 +802,7 @@ impl Options { let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx); let with_examples = matches.opt_strs("with-examples"); let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx); - let doctest_compilation_args = matches.opt_strs("doctest-compilation-args"); + let doctest_build_args = matches.opt_strs("doctest-build-arg"); let unstable_features = rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref()); @@ -834,6 +826,7 @@ impl Options { unstable_opts_strs, target, edition, + sysroot, maybe_sysroot, lint_opts, describe_lints, @@ -844,9 +837,8 @@ impl Options { crate_version, test_run_directory, persist_doctests, - runtool, - runtool_args, - enable_per_target_ignores, + test_runtool, + test_runtool_args, test_builder, run_check, no_run, @@ -859,7 +851,7 @@ impl Options { scrape_examples_options, unstable_features, expanded_args: args, - doctest_compilation_args, + doctest_build_args, }; let render_options = RenderOptions { output, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 679921c32693f..9d1c9ff00b143 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -27,7 +27,7 @@ use rustc_span::source_map; use rustc_span::symbol::sym; use tracing::{debug, info}; -use crate::clean::inline::build_external_trait; +use crate::clean::inline::build_trait; use crate::clean::{self, ItemId}; use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions}; use crate::formats::cache::Cache; @@ -154,13 +154,12 @@ pub(crate) fn new_dcx( false, ); let emitter: Box = match error_format { - ErrorOutputType::HumanReadable(kind, color_config) => { + ErrorOutputType::HumanReadable { kind, color_config } => { let short = kind.short(); Box::new( HumanEmitter::new(stderr_destination(color_config), fallback_bundle) .sm(source_map.map(|sm| sm as _)) .short_message(short) - .teach(unstable_opts.teach) .diagnostic_width(diagnostic_width) .track_diagnostics(unstable_opts.track_diagnostics) .theme(if let HumanReadableErrorType::Unicode = kind { @@ -211,7 +210,7 @@ pub(crate) fn create_config( unstable_opts, target, edition, - maybe_sysroot, + sysroot, lint_opts, describe_lints, lint_cap, @@ -254,7 +253,7 @@ pub(crate) fn create_config( let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false); // plays with error output here! let sessopts = config::Options { - maybe_sysroot, + sysroot, search_paths: libs, crate_types, lint_opts, @@ -322,6 +321,7 @@ pub(crate) fn create_config( (rustc_interface::DEFAULT_QUERY_PROVIDERS.typeck)(tcx, def_id) }; }), + extra_symbols: Vec::new(), make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), ice_file: None, @@ -385,7 +385,7 @@ pub(crate) fn run_global_ctxt( // // Note that in case of `#![no_core]`, the trait is not available. if let Some(sized_trait_did) = ctxt.tcx.lang_items().sized_trait() { - let sized_trait = build_external_trait(&mut ctxt, sized_trait_did); + let sized_trait = build_trait(&mut ctxt, sized_trait_did); ctxt.external_traits.insert(sized_trait_did, sized_trait); } @@ -412,9 +412,7 @@ pub(crate) fn run_global_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.attrs.lists(sym::doc) { - let name = attr.name_or_empty(); - - if attr.is_word() && name == sym::document_private_items { + if attr.is_word() && attr.has_name(sym::document_private_items) { ctxt.render_options.document_private = true; } } diff --git a/src/librustdoc/display.rs b/src/librustdoc/display.rs index ee8dde013ee93..aa0fad265208d 100644 --- a/src/librustdoc/display.rs +++ b/src/librustdoc/display.rs @@ -22,7 +22,7 @@ where let mut iter = self.into_iter(); let Some(first) = iter.next() else { return Ok(()) }; first.fmt(f)?; - while let Some(item) = iter.next() { + for item in iter { f.write_str(sep)?; item.fmt(f)?; } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 4a379b4235ff0..ef70b8621857a 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -51,52 +51,12 @@ pub(crate) struct GlobalTestOptions { pub(crate) args_file: PathBuf, } -/// Function used to split command line arguments just like a shell would. -fn split_args(args: &str) -> Vec { - let mut out = Vec::new(); - let mut iter = args.chars(); - let mut current = String::new(); - - while let Some(c) = iter.next() { - if c == '\\' { - if let Some(c) = iter.next() { - // If it's escaped, even a quote or a whitespace will be ignored. - current.push(c); - } - } else if c == '"' || c == '\'' { - while let Some(new_c) = iter.next() { - if new_c == c { - break; - } else if new_c == '\\' { - if let Some(c) = iter.next() { - // If it's escaped, even a quote will be ignored. - current.push(c); - } - } else { - current.push(new_c); - } - } - } else if " \n\t\r".contains(c) { - if !current.is_empty() { - out.push(current.clone()); - current.clear(); - } - } else { - current.push(c); - } - } - if !current.is_empty() { - out.push(current); - } - out -} - pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> Result<(), String> { let mut file = File::create(file_path) .map_err(|error| format!("failed to create args file: {error:?}"))?; // We now put the common arguments into the file we created. - let mut content = vec!["--crate-type=bin".to_string()]; + let mut content = vec![]; for cfg in &options.cfgs { content.push(format!("--cfg={cfg}")); @@ -119,9 +79,7 @@ pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> content.push(format!("-Z{unstable_option_str}")); } - for compilation_args in &options.doctest_compilation_args { - content.extend(split_args(compilation_args)); - } + content.extend(options.doctest_build_args.clone()); let content = content.join("\n"); @@ -158,7 +116,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions if options.proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] }; let sessopts = config::Options { - maybe_sysroot: options.maybe_sysroot.clone(), + sysroot: options.sysroot.clone(), search_paths: options.libs.clone(), crate_types, lint_opts, @@ -191,6 +149,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions hash_untracked_state: None, register_lints: Some(Box::new(crate::lint::register_lints)), override_queries: None, + extra_symbols: Vec::new(), make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), ice_file: None, @@ -216,13 +175,11 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions let collector = rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { let crate_name = tcx.crate_name(LOCAL_CRATE).to_string(); - let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID); + let crate_attrs = tcx.hir_attrs(CRATE_HIR_ID); let opts = scrape_test_config(crate_name, crate_attrs, args_path); - let enable_per_target_ignores = options.enable_per_target_ignores; let hir_collector = HirCollector::new( ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), - enable_per_target_ignores, tcx, ); let tests = hir_collector.collect_crate(); @@ -263,11 +220,21 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions Ok(None) => return, Err(error) => { eprintln!("{error}"); + // Since some files in the temporary folder are still owned and alive, we need + // to manually remove the folder. + let _ = std::fs::remove_dir_all(temp_dir.path()); std::process::exit(1); } }; - run_tests(opts, &rustdoc_options, &unused_extern_reports, standalone_tests, mergeable_tests); + run_tests( + opts, + &rustdoc_options, + &unused_extern_reports, + standalone_tests, + mergeable_tests, + Some(temp_dir), + ); let compiling_test_count = compiling_test_count.load(Ordering::SeqCst); @@ -317,6 +284,8 @@ pub(crate) fn run_tests( unused_extern_reports: &Arc>>, mut standalone_tests: Vec, mergeable_tests: FxIndexMap>, + // We pass this argument so we can drop it manually before using `exit`. + mut temp_dir: Option, ) { let mut test_args = Vec::with_capacity(rustdoc_options.test_args.len() + 1); test_args.insert(0, "rustdoctest".to_string()); @@ -383,9 +352,14 @@ pub(crate) fn run_tests( // `running 0 tests...`. if ran_edition_tests == 0 || !standalone_tests.is_empty() { standalone_tests.sort_by(|a, b| a.desc.name.as_slice().cmp(b.desc.name.as_slice())); - test::test_main(&test_args, standalone_tests, None); + test::test_main_with_exit_callback(&test_args, standalone_tests, None, || { + // We ensure temp dir destructor is called. + std::mem::drop(temp_dir.take()); + }); } if nb_errors != 0 { + // We ensure temp dir destructor is called. + std::mem::drop(temp_dir); // libtest::ERROR_EXIT_CODE is not public but it's the same value. std::process::exit(101); } @@ -451,7 +425,7 @@ enum TestFailure { } enum DirState { - Temp(tempfile::TempDir), + Temp(TempDir), Perm(PathBuf), } @@ -513,12 +487,18 @@ pub(crate) struct RunnableDocTest { line: usize, edition: Edition, no_run: bool, - is_multiple_tests: bool, + merged_test_code: Option, } impl RunnableDocTest { - fn path_for_merged_doctest(&self) -> PathBuf { - self.test_opts.outdir.path().join(format!("doctest_{}.rs", self.edition)) + fn path_for_merged_doctest_bundle(&self) -> PathBuf { + self.test_opts.outdir.path().join(format!("doctest_bundle_{}.rs", self.edition)) + } + fn path_for_merged_doctest_runner(&self) -> PathBuf { + self.test_opts.outdir.path().join(format!("doctest_runner_{}.rs", self.edition)) + } + fn is_multiple_tests(&self) -> bool { + self.merged_test_code.is_some() } } @@ -537,91 +517,108 @@ fn run_test( let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target); let output_file = doctest.test_opts.outdir.path().join(rust_out); - let rustc_binary = rustdoc_options - .test_builder - .as_deref() - .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); - let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); + // Common arguments used for compiling the doctest runner. + // On merged doctests, the compiler is invoked twice: once for the test code itself, + // and once for the runner wrapper (which needs to use `#![feature]` on stable). + let mut compiler_args = vec![]; - compiler.arg(format!("@{}", doctest.global_opts.args_file.display())); + compiler_args.push(format!("@{}", doctest.global_opts.args_file.display())); if let Some(sysroot) = &rustdoc_options.maybe_sysroot { - compiler.arg(format!("--sysroot={}", sysroot.display())); + compiler_args.push(format!("--sysroot={}", sysroot.display())); } - compiler.arg("--edition").arg(doctest.edition.to_string()); - if !doctest.is_multiple_tests { - // Setting these environment variables is unneeded if this is a merged doctest. - compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path); - compiler.env( - "UNSTABLE_RUSTDOC_TEST_LINE", - format!("{}", doctest.line as isize - doctest.full_test_line_offset as isize), - ); - } - compiler.arg("-o").arg(&output_file); + compiler_args.extend_from_slice(&["--edition".to_owned(), doctest.edition.to_string()]); if langstr.test_harness { - compiler.arg("--test"); + compiler_args.push("--test".to_owned()); } if rustdoc_options.json_unused_externs.is_enabled() && !langstr.compile_fail { - compiler.arg("--error-format=json"); - compiler.arg("--json").arg("unused-externs"); - compiler.arg("-W").arg("unused_crate_dependencies"); - compiler.arg("-Z").arg("unstable-options"); + compiler_args.push("--error-format=json".to_owned()); + compiler_args.extend_from_slice(&["--json".to_owned(), "unused-externs".to_owned()]); + compiler_args.extend_from_slice(&["-W".to_owned(), "unused_crate_dependencies".to_owned()]); + compiler_args.extend_from_slice(&["-Z".to_owned(), "unstable-options".to_owned()]); } if doctest.no_run && !langstr.compile_fail && rustdoc_options.persist_doctests.is_none() { // FIXME: why does this code check if it *shouldn't* persist doctests // -- shouldn't it be the negation? - compiler.arg("--emit=metadata"); + compiler_args.push("--emit=metadata".to_owned()); } - compiler.arg("--target").arg(match &rustdoc_options.target { - TargetTuple::TargetTuple(s) => s, - TargetTuple::TargetJson { path_for_rustdoc, .. } => { - path_for_rustdoc.to_str().expect("target path must be valid unicode") - } - }); - if let ErrorOutputType::HumanReadable(kind, color_config) = rustdoc_options.error_format { + compiler_args.extend_from_slice(&[ + "--target".to_owned(), + match &rustdoc_options.target { + TargetTuple::TargetTuple(s) => s.clone(), + TargetTuple::TargetJson { path_for_rustdoc, .. } => { + path_for_rustdoc.to_str().expect("target path must be valid unicode").to_owned() + } + }, + ]); + if let ErrorOutputType::HumanReadable { kind, color_config } = rustdoc_options.error_format { let short = kind.short(); let unicode = kind == HumanReadableErrorType::Unicode; if short { - compiler.arg("--error-format").arg("short"); + compiler_args.extend_from_slice(&["--error-format".to_owned(), "short".to_owned()]); } if unicode { - compiler.arg("--error-format").arg("human-unicode"); + compiler_args + .extend_from_slice(&["--error-format".to_owned(), "human-unicode".to_owned()]); } match color_config { ColorConfig::Never => { - compiler.arg("--color").arg("never"); + compiler_args.extend_from_slice(&["--color".to_owned(), "never".to_owned()]); } ColorConfig::Always => { - compiler.arg("--color").arg("always"); + compiler_args.extend_from_slice(&["--color".to_owned(), "always".to_owned()]); } ColorConfig::Auto => { - compiler.arg("--color").arg(if supports_color { "always" } else { "never" }); + compiler_args.extend_from_slice(&[ + "--color".to_owned(), + if supports_color { "always" } else { "never" }.to_owned(), + ]); } } } + let rustc_binary = rustdoc_options + .test_builder + .as_deref() + .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); + let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); + + compiler.args(&compiler_args); + // If this is a merged doctest, we need to write it into a file instead of using stdin // because if the size of the merged doctests is too big, it'll simply break stdin. - if doctest.is_multiple_tests { + if doctest.is_multiple_tests() { // It makes the compilation failure much faster if it is for a combined doctest. compiler.arg("--error-format=short"); - let input_file = doctest.path_for_merged_doctest(); + let input_file = doctest.path_for_merged_doctest_bundle(); if std::fs::write(&input_file, &doctest.full_test_code).is_err() { // If we cannot write this file for any reason, we leave. All combined tests will be // tested as standalone tests. return Err(TestFailure::CompileError); } - compiler.arg(input_file); if !rustdoc_options.nocapture { // If `nocapture` is disabled, then we don't display rustc's output when compiling // the merged doctests. compiler.stderr(Stdio::null()); } + // bundled tests are an rlib, loaded by a separate runner executable + compiler + .arg("--crate-type=lib") + .arg("--out-dir") + .arg(doctest.test_opts.outdir.path()) + .arg(input_file); } else { + compiler.arg("--crate-type=bin").arg("-o").arg(&output_file); + // Setting these environment variables is unneeded if this is a merged doctest. + compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path); + compiler.env( + "UNSTABLE_RUSTDOC_TEST_LINE", + format!("{}", doctest.line as isize - doctest.full_test_line_offset as isize), + ); compiler.arg("-"); compiler.stdin(Stdio::piped()); compiler.stderr(Stdio::piped()); @@ -630,8 +627,65 @@ fn run_test( debug!("compiler invocation for doctest: {compiler:?}"); let mut child = compiler.spawn().expect("Failed to spawn rustc process"); - let output = if doctest.is_multiple_tests { + let output = if let Some(merged_test_code) = &doctest.merged_test_code { + // compile-fail tests never get merged, so this should always pass let status = child.wait().expect("Failed to wait"); + + // the actual test runner is a separate component, built with nightly-only features; + // build it now + let runner_input_file = doctest.path_for_merged_doctest_runner(); + + let mut runner_compiler = + wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); + // the test runner does not contain any user-written code, so this doesn't allow + // the user to exploit nightly-only features on stable + runner_compiler.env("RUSTC_BOOTSTRAP", "1"); + runner_compiler.args(compiler_args); + runner_compiler.args(&["--crate-type=bin", "-o"]).arg(&output_file); + let mut extern_path = std::ffi::OsString::from(format!( + "--extern=doctest_bundle_{edition}=", + edition = doctest.edition + )); + for extern_str in &rustdoc_options.extern_strs { + if let Some((_cratename, path)) = extern_str.split_once('=') { + // Direct dependencies of the tests themselves are + // indirect dependencies of the test runner. + // They need to be in the library search path. + let dir = Path::new(path) + .parent() + .filter(|x| x.components().count() > 0) + .unwrap_or(Path::new(".")); + runner_compiler.arg("-L").arg(dir); + } + } + let output_bundle_file = doctest + .test_opts + .outdir + .path() + .join(format!("libdoctest_bundle_{edition}.rlib", edition = doctest.edition)); + extern_path.push(&output_bundle_file); + runner_compiler.arg(extern_path); + runner_compiler.arg(&runner_input_file); + if std::fs::write(&runner_input_file, &merged_test_code).is_err() { + // If we cannot write this file for any reason, we leave. All combined tests will be + // tested as standalone tests. + return Err(TestFailure::CompileError); + } + if !rustdoc_options.nocapture { + // If `nocapture` is disabled, then we don't display rustc's output when compiling + // the merged doctests. + runner_compiler.stderr(Stdio::null()); + } + runner_compiler.arg("--error-format=short"); + debug!("compiler invocation for doctest runner: {runner_compiler:?}"); + + let status = if !status.success() { + status + } else { + let mut child_runner = runner_compiler.spawn().expect("Failed to spawn rustc process"); + child_runner.wait().expect("Failed to wait") + }; + process::Output { status, stdout: Vec::new(), stderr: Vec::new() } } else { let stdin = child.stdin.as_mut().expect("Failed to open stdin"); @@ -701,14 +755,14 @@ fn run_test( let mut cmd; let output_file = make_maybe_absolute_path(output_file); - if let Some(tool) = &rustdoc_options.runtool { + if let Some(tool) = &rustdoc_options.test_runtool { let tool = make_maybe_absolute_path(tool.into()); cmd = Command::new(tool); - cmd.args(&rustdoc_options.runtool_args); + cmd.args(&rustdoc_options.test_runtool_args); cmd.arg(&output_file); } else { cmd = Command::new(&output_file); - if doctest.is_multiple_tests { + if doctest.is_multiple_tests() { cmd.env("RUSTDOC_DOCTEST_BIN_PATH", &output_file); } } @@ -716,7 +770,7 @@ fn run_test( cmd.current_dir(run_directory); } - let result = if doctest.is_multiple_tests || rustdoc_options.nocapture { + let result = if doctest.is_multiple_tests() || rustdoc_options.nocapture { cmd.status().map(|status| process::Output { status, stdout: Vec::new(), @@ -1003,7 +1057,7 @@ fn doctest_run_fn( line: scraped_test.line, edition: scraped_test.edition(&rustdoc_options), no_run: scraped_test.no_run(&rustdoc_options), - is_multiple_tests: false, + merged_test_code: None, }; let res = run_test(runnable_test, &rustdoc_options, doctest.supports_color, report_unused_externs); diff --git a/src/librustdoc/doctest/extracted.rs b/src/librustdoc/doctest/extracted.rs index 03c8814a4c960..ce362eabfc4c9 100644 --- a/src/librustdoc/doctest/extracted.rs +++ b/src/librustdoc/doctest/extracted.rs @@ -33,7 +33,7 @@ impl ExtractedDocTests { opts: &super::GlobalTestOptions, options: &RustdocOptions, ) { - let edition = scraped_test.edition(&options); + let edition = scraped_test.edition(options); let ScrapedDocTest { filename, line, langstr, text, name } = scraped_test; @@ -48,7 +48,7 @@ impl ExtractedDocTests { let (full_test_code, size) = doctest.generate_unique_doctest( &text, langstr.test_harness, - &opts, + opts, Some(&opts.crate_name), ); self.doctests.push(ExtractedDocTest { diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 9935074877bc8..d4fbfb12582e7 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -5,22 +5,36 @@ use std::fmt::{self, Write as _}; use std::io; use std::sync::Arc; -use rustc_ast as ast; +use rustc_ast::token::{Delimiter, TokenKind}; +use rustc_ast::tokenstream::TokenTree; +use rustc_ast::{self as ast, AttrStyle, HasAttrs, StmtKind}; +use rustc_errors::ColorConfig; use rustc_errors::emitter::stderr_destination; -use rustc_errors::{ColorConfig, FatalError}; use rustc_parse::new_parser_from_source_str; -use rustc_parse::parser::attr::InnerAttrPolicy; use rustc_session::parse::ParseSess; -use rustc_span::FileName; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; +use rustc_span::{FileName, kw}; use tracing::debug; use super::GlobalTestOptions; use crate::display::Joined as _; use crate::html::markdown::LangString; +#[derive(Default)] +struct ParseSourceInfo { + has_main_fn: bool, + already_has_extern_crate: bool, + supports_color: bool, + has_global_allocator: bool, + has_macro_def: bool, + everything_else: String, + crates: String, + crate_attrs: String, + maybe_crate_attrs: String, +} + /// This struct contains information about the doctest itself which is then used to generate /// doctest source code appropriately. pub(crate) struct DocTestBuilder { @@ -34,7 +48,7 @@ pub(crate) struct DocTestBuilder { pub(crate) crates: String, pub(crate) everything_else: String, pub(crate) test_id: Option, - pub(crate) failed_ast: bool, + pub(crate) invalid_ast: bool, pub(crate) can_be_merged: bool, } @@ -53,9 +67,26 @@ impl DocTestBuilder { !lang_str.compile_fail && !lang_str.test_harness && !lang_str.standalone_crate }); - let Some(SourceInfo { crate_attrs, maybe_crate_attrs, crates, everything_else }) = - partition_source(source, edition) + let result = rustc_driver::catch_fatal_errors(|| { + rustc_span::create_session_if_not_set_then(edition, |_| { + parse_source(source, &crate_name) + }) + }); + + let Ok(Ok(ParseSourceInfo { + has_main_fn, + already_has_extern_crate, + supports_color, + has_global_allocator, + has_macro_def, + everything_else, + crates, + crate_attrs, + maybe_crate_attrs, + })) = result else { + // If the AST returned an error, we don't want this doctest to be merged with the + // others. return Self::invalid( String::new(), String::new(), @@ -65,35 +96,12 @@ impl DocTestBuilder { ); }; - // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern - // crate already is included. - let Ok(( - ParseSourceInfo { - has_main_fn, - found_extern_crate, - supports_color, - has_global_allocator, - has_macro_def, - .. - }, - failed_ast, - )) = check_for_main_and_extern_crate( - crate_name, - source, - &everything_else, - &crates, - edition, - can_merge_doctests, - ) - else { - // If the parser panicked due to a fatal error, pass the test code through unchanged. - // The error will be reported during compilation. - return Self::invalid(crate_attrs, maybe_crate_attrs, crates, everything_else, test_id); - }; - // If the AST returned an error, we don't want this doctest to be merged with the - // others. Same if it contains `#[feature]` or `#[no_std]`. + debug!("crate_attrs:\n{crate_attrs}{maybe_crate_attrs}"); + debug!("crates:\n{crates}"); + debug!("after:\n{everything_else}"); + + // If it contains `#[feature]` or `#[no_std]`, we don't want it to be merged either. let can_be_merged = can_merge_doctests - && !failed_ast && !has_global_allocator && crate_attrs.is_empty() // If this is a merged doctest and a defined macro uses `$crate`, then the path will @@ -106,9 +114,9 @@ impl DocTestBuilder { maybe_crate_attrs, crates, everything_else, - already_has_extern_crate: found_extern_crate, + already_has_extern_crate, test_id, - failed_ast: false, + invalid_ast: false, can_be_merged, } } @@ -129,7 +137,7 @@ impl DocTestBuilder { everything_else, already_has_extern_crate: false, test_id, - failed_ast: true, + invalid_ast: true, can_be_merged: false, } } @@ -143,9 +151,10 @@ impl DocTestBuilder { opts: &GlobalTestOptions, crate_name: Option<&str>, ) -> (String, usize) { - if self.failed_ast { + if self.invalid_ast { // If the AST failed to compile, no need to go generate a complete doctest, the error // will be better this way. + debug!("invalid AST:\n{test_code}"); return (test_code.to_string(), 0); } let mut line_offset = 0; @@ -168,9 +177,24 @@ impl DocTestBuilder { // Now push any outer attributes from the example, assuming they // are intended to be crate attributes. - prog.push_str(&self.crate_attrs); - prog.push_str(&self.maybe_crate_attrs); - prog.push_str(&self.crates); + if !self.crate_attrs.is_empty() { + prog.push_str(&self.crate_attrs); + if !self.crate_attrs.ends_with('\n') { + prog.push('\n'); + } + } + if !self.maybe_crate_attrs.is_empty() { + prog.push_str(&self.maybe_crate_attrs); + if !self.maybe_crate_attrs.ends_with('\n') { + prog.push('\n'); + } + } + if !self.crates.is_empty() { + prog.push_str(&self.crates); + if !self.crates.ends_with('\n') { + prog.push('\n'); + } + } // Don't inject `extern crate std` because it's already injected by the // compiler. @@ -255,14 +279,7 @@ impl DocTestBuilder { } } -#[derive(PartialEq, Eq, Debug)] -enum ParsingResult { - Failed, - AstError, - Ok, -} - -fn cancel_error_count(psess: &ParseSess) { +fn reset_error_count(psess: &ParseSess) { // Reset errors so that they won't be reported as compiler bugs when dropping the // dcx. Any errors in the tests will be reported when the test file is compiled, // Note that we still need to cancel the errors above otherwise `Diag` will panic on @@ -270,20 +287,20 @@ fn cancel_error_count(psess: &ParseSess) { psess.dcx().reset_err_count(); } -fn parse_source( - source: String, - info: &mut ParseSourceInfo, - crate_name: &Option<&str>, -) -> ParsingResult { +const DOCTEST_CODE_WRAPPER: &str = "fn f(){"; + +fn parse_source(source: &str, crate_name: &Option<&str>) -> Result { use rustc_errors::DiagCtxt; use rustc_errors::emitter::{Emitter, HumanEmitter}; - use rustc_parse::parser::ForceCollect; use rustc_span::source_map::FilePathMapping; - let filename = FileName::anon_source_code(&source); + let mut info = + ParseSourceInfo { already_has_extern_crate: crate_name.is_none(), ..Default::default() }; + + let wrapped_source = format!("{DOCTEST_CODE_WRAPPER}{source}\n}}"); + + let filename = FileName::anon_source_code(&wrapped_source); - // 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 = Arc::new(SourceMap::new(FilePathMapping::empty())); let fallback_bundle = rustc_errors::fallback_fluent_bundle( rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), @@ -292,353 +309,176 @@ fn parse_source( info.supports_color = HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) .supports_color(); - + // Any errors in parsing should also appear when the doctest is compiled for real, so just + // send all the errors that the parser emits directly into a `Sink` instead of stderr. let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); let psess = ParseSess::with_dcx(dcx, sm); - let mut parser = match new_parser_from_source_str(&psess, filename, source) { + let mut parser = match new_parser_from_source_str(&psess, filename, wrapped_source) { Ok(p) => p, Err(errs) => { errs.into_iter().for_each(|err| err.cancel()); - cancel_error_count(&psess); - return ParsingResult::Failed; + reset_error_count(&psess); + return Err(()); } }; - let mut parsing_result = ParsingResult::Ok; - - // Recurse through functions body. It is necessary because the doctest source code is - // wrapped in a function to limit the number of AST errors. If we don't recurse into - // functions, we would thing all top-level items (so basically nothing). - fn check_item( - item: &ast::Item, - info: &mut ParseSourceInfo, - crate_name: &Option<&str>, - is_top_level: bool, - ) { + + fn push_to_s(s: &mut String, source: &str, span: rustc_span::Span, prev_span_hi: &mut usize) { + let extra_len = DOCTEST_CODE_WRAPPER.len(); + // We need to shift by the length of `DOCTEST_CODE_WRAPPER` because we + // added it at the beginning of the source we provided to the parser. + let mut hi = span.hi().0 as usize - extra_len; + if hi > source.len() { + hi = source.len(); + } + s.push_str(&source[*prev_span_hi..hi]); + *prev_span_hi = hi; + } + + fn check_item(item: &ast::Item, info: &mut ParseSourceInfo, crate_name: &Option<&str>) -> bool { + let mut is_extern_crate = false; if !info.has_global_allocator - && item.attrs.iter().any(|attr| attr.name_or_empty() == sym::global_allocator) + && item.attrs.iter().any(|attr| attr.has_name(sym::global_allocator)) { info.has_global_allocator = true; } match item.kind { ast::ItemKind::Fn(ref fn_item) if !info.has_main_fn => { - if item.ident.name == sym::main && is_top_level { + if fn_item.ident.name == sym::main { info.has_main_fn = true; } - if let Some(ref body) = fn_item.body { - for stmt in &body.stmts { - match stmt.kind { - ast::StmtKind::Item(ref item) => { - check_item(item, info, crate_name, false) - } - ast::StmtKind::MacCall(..) => info.found_macro = true, - _ => {} - } - } - } } - ast::ItemKind::ExternCrate(original) => { - if !info.found_extern_crate + ast::ItemKind::ExternCrate(original, ident) => { + is_extern_crate = true; + if !info.already_has_extern_crate && let Some(crate_name) = crate_name { - info.found_extern_crate = match original { + info.already_has_extern_crate = match original { Some(name) => name.as_str() == *crate_name, - None => item.ident.as_str() == *crate_name, + None => ident.as_str() == *crate_name, }; } } - ast::ItemKind::MacCall(..) => info.found_macro = true, - ast::ItemKind::MacroDef(..) => info.has_macro_def = true, + ast::ItemKind::MacroDef(..) => { + info.has_macro_def = true; + } _ => {} } + is_extern_crate } - loop { - match parser.parse_item(ForceCollect::No) { - Ok(Some(item)) => { - check_item(&item, info, crate_name, true); + let mut prev_span_hi = 0; + let not_crate_attrs = &[sym::forbid, sym::allow, sym::warn, sym::deny, sym::expect]; + let parsed = parser.parse_item(rustc_parse::parser::ForceCollect::No); - if info.has_main_fn && info.found_extern_crate { - break; + let result = match parsed { + Ok(Some(ref item)) + if let ast::ItemKind::Fn(ref fn_item) = item.kind + && let Some(ref body) = fn_item.body => + { + for attr in &item.attrs { + if attr.style == AttrStyle::Outer || attr.has_any_name(not_crate_attrs) { + // There is one exception to these attributes: + // `#![allow(internal_features)]`. If this attribute is used, we need to + // consider it only as a crate-level attribute. + if attr.has_name(sym::allow) + && let Some(list) = attr.meta_item_list() + && list.iter().any(|sub_attr| sub_attr.has_name(sym::internal_features)) + { + push_to_s(&mut info.crate_attrs, source, attr.span, &mut prev_span_hi); + } else { + push_to_s( + &mut info.maybe_crate_attrs, + source, + attr.span, + &mut prev_span_hi, + ); + } + } else { + push_to_s(&mut info.crate_attrs, source, attr.span, &mut prev_span_hi); } } - Ok(None) => break, - Err(e) => { - parsing_result = ParsingResult::AstError; - e.cancel(); - break; - } - } - - // The supplied item is only used for diagnostics, - // which are swallowed here anyway. - parser.maybe_consume_incorrect_semicolon(None); - } - - cancel_error_count(&psess); - parsing_result -} - -#[derive(Default)] -struct ParseSourceInfo { - has_main_fn: bool, - found_extern_crate: bool, - found_macro: bool, - supports_color: bool, - has_global_allocator: bool, - has_macro_def: bool, -} - -fn check_for_main_and_extern_crate( - crate_name: Option<&str>, - original_source_code: &str, - everything_else: &str, - crates: &str, - edition: Edition, - can_merge_doctests: bool, -) -> Result<(ParseSourceInfo, bool), FatalError> { - let result = rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_if_not_set_then(edition, |_| { - let mut info = - ParseSourceInfo { found_extern_crate: crate_name.is_none(), ..Default::default() }; - - let mut parsing_result = - parse_source(format!("{crates}{everything_else}"), &mut info, &crate_name); - // No need to double-check this if the "merged doctests" feature isn't enabled (so - // before the 2024 edition). - if can_merge_doctests && parsing_result != ParsingResult::Ok { - // If we found an AST error, we want to ensure it's because of an expression being - // used outside of a function. - // - // To do so, we wrap in a function in order to make sure that the doctest AST is - // correct. For example, if your doctest is `foo::bar()`, if we don't wrap it in a - // block, it would emit an AST error, which would be problematic for us since we - // want to filter out such errors which aren't "real" errors. - // - // The end goal is to be able to merge as many doctests as possible as one for much - // faster doctests run time. - parsing_result = parse_source( - format!("{crates}\nfn __doctest_wrap(){{{everything_else}\n}}"), - &mut info, - &crate_name, - ); - } - - (info, parsing_result) - }) - }); - let (mut info, parsing_result) = match result { - Err(..) | Ok((_, ParsingResult::Failed)) => return Err(FatalError), - Ok((info, parsing_result)) => (info, parsing_result), - }; - - // 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 - if info.found_macro - && !info.has_main_fn - && original_source_code - .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")) - { - info.has_main_fn = true; - } - - Ok((info, parsing_result != ParsingResult::Ok)) -} - -enum AttrKind { - CrateAttr, - Attr, -} - -/// Returns `Some` if the attribute is complete and `Some(true)` if it is an attribute that can be -/// placed at the crate root. -fn check_if_attr_is_complete(source: &str, edition: Edition) -> Option { - if source.is_empty() { - // Empty content so nothing to check in here... - return None; - } - let not_crate_attrs = [sym::forbid, sym::allow, sym::warn, sym::deny]; - - rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_if_not_set_then(edition, |_| { - use rustc_errors::DiagCtxt; - use rustc_errors::emitter::HumanEmitter; - use rustc_span::source_map::FilePathMapping; - - let filename = FileName::anon_source_code(source); - // 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 = Arc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); - - let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); - - let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); - let psess = ParseSess::with_dcx(dcx, sm); - let mut parser = match new_parser_from_source_str(&psess, filename, source.to_owned()) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - // If there is an unclosed delimiter, an error will be returned by the - // tokentrees. - return None; - } - }; - // If a parsing error happened, it's very likely that the attribute is incomplete. - let ret = match parser.parse_attribute(InnerAttrPolicy::Permitted) { - Ok(attr) => { - let attr_name = attr.name_or_empty(); - - if not_crate_attrs.contains(&attr_name) { - // There is one exception to these attributes: - // `#![allow(internal_features)]`. If this attribute is used, we need to - // consider it only as a crate-level attribute. - if attr_name == sym::allow - && let Some(list) = attr.meta_item_list() - && list.iter().any(|sub_attr| { - sub_attr.name_or_empty().as_str() == "internal_features" - }) - { - Some(AttrKind::CrateAttr) - } else { - Some(AttrKind::Attr) + let mut has_non_items = false; + for stmt in &body.stmts { + let mut is_extern_crate = false; + match stmt.kind { + StmtKind::Item(ref item) => { + is_extern_crate = check_item(item, &mut info, crate_name); + } + // We assume that the macro calls will expand to item(s) even though they could + // expand to statements and expressions. + StmtKind::MacCall(ref mac_call) => { + if !info.has_main_fn { + // For backward compatibility, we look for the token sequence `fn main(…)` + // in the macro input (!) to crudely detect main functions "masked by a + // wrapper macro". For the record, this is a horrible heuristic! + // See . + let mut iter = mac_call.mac.args.tokens.iter(); + while let Some(token) = iter.next() { + if let TokenTree::Token(token, _) = token + && let TokenKind::Ident(kw::Fn, _) = token.kind + && let Some(TokenTree::Token(ident, _)) = iter.peek() + && let TokenKind::Ident(sym::main, _) = ident.kind + && let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, _)) = { + iter.next(); + iter.peek() + } + { + info.has_main_fn = true; + break; + } + } } - } else { - Some(AttrKind::CrateAttr) } + StmtKind::Expr(ref expr) => { + if matches!(expr.kind, ast::ExprKind::Err(_)) { + reset_error_count(&psess); + return Err(()); + } + has_non_items = true; + } + StmtKind::Let(_) | StmtKind::Semi(_) | StmtKind::Empty => has_non_items = true, } - Err(e) => { - e.cancel(); - None - } - }; - ret - }) - }) - .unwrap_or(None) -} -fn handle_attr(mod_attr_pending: &mut String, source_info: &mut SourceInfo, edition: Edition) { - if let Some(attr_kind) = check_if_attr_is_complete(mod_attr_pending, edition) { - let push_to = match attr_kind { - AttrKind::CrateAttr => &mut source_info.crate_attrs, - AttrKind::Attr => &mut source_info.maybe_crate_attrs, - }; - push_to.push_str(mod_attr_pending); - push_to.push('\n'); - // If it's complete, then we can clear the pending content. - mod_attr_pending.clear(); - } else { - mod_attr_pending.push('\n'); - } -} - -#[derive(Default)] -struct SourceInfo { - crate_attrs: String, - maybe_crate_attrs: String, - crates: String, - everything_else: String, -} - -fn partition_source(s: &str, edition: Edition) -> Option { - #[derive(Copy, Clone, PartialEq)] - enum PartitionState { - Attrs, - Crates, - Other, - } - let mut source_info = SourceInfo::default(); - let mut state = PartitionState::Attrs; - let mut mod_attr_pending = 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("#![") { - mod_attr_pending = line.to_owned(); - handle_attr(&mut mod_attr_pending, &mut source_info, edition); - continue; - } else if trimline.chars().all(|c| c.is_whitespace()) - || (trimline.starts_with("//") && !trimline.starts_with("///")) + // Weirdly enough, the `Stmt` span doesn't include its attributes, so we need to + // tweak the span to include the attributes as well. + let mut span = stmt.span; + if let Some(attr) = + stmt.kind.attrs().iter().find(|attr| attr.style == AttrStyle::Outer) { - PartitionState::Attrs - } else if trimline.starts_with("extern crate") - || trimline.starts_with("#[macro_use] extern crate") + span = span.with_lo(attr.span.lo()); + } + if info.everything_else.is_empty() + && (!info.maybe_crate_attrs.is_empty() || !info.crate_attrs.is_empty()) { - PartitionState::Crates + // To keep the doctest code "as close as possible" to the original, we insert + // all the code located between this new span and the previous span which + // might contain code comments and backlines. + push_to_s(&mut info.crates, source, span.shrink_to_lo(), &mut prev_span_hi); + } + if !is_extern_crate { + push_to_s(&mut info.everything_else, source, span, &mut prev_span_hi); } else { - // First we check if the previous attribute was "complete"... - if !mod_attr_pending.is_empty() { - // If not, then we append the new line into the pending attribute to check - // if this time it's complete... - mod_attr_pending.push_str(line); - if !trimline.is_empty() { - handle_attr(&mut mod_attr_pending, &mut source_info, edition); - } - continue; - } else { - PartitionState::Other - } - }; + push_to_s(&mut info.crates, source, span, &mut prev_span_hi); + } } - 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 - }; + if has_non_items { + // FIXME: if `info.has_main_fn` is `true`, emit a warning here to mention that + // this code will not be called. + info.has_main_fn = false; } - PartitionState::Other => {} + Ok(info) } - - match state { - PartitionState::Attrs => { - source_info.crate_attrs.push_str(line); - source_info.crate_attrs.push('\n'); - } - PartitionState::Crates => { - source_info.crates.push_str(line); - source_info.crates.push('\n'); - } - PartitionState::Other => { - source_info.everything_else.push_str(line); - source_info.everything_else.push('\n'); - } + Err(e) => { + e.cancel(); + Err(()) } - } - - if !mod_attr_pending.is_empty() { - debug!("invalid doctest code: {s:?}"); - return None; - } - - source_info.everything_else = source_info.everything_else.trim().to_string(); - - debug!("crate_attrs:\n{}{}", source_info.crate_attrs, source_info.maybe_crate_attrs); - debug!("crates:\n{}", source_info.crates); - debug!("after:\n{}", source_info.everything_else); + _ => Err(()), + }; - Some(source_info) + reset_error_count(&psess); + result } diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index a0d39ce749d83..b3a3ce08a05a6 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -104,13 +104,7 @@ pub(crate) fn test(input: &Input, options: Options) -> Result<(), String> { }; let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); - find_testable_code( - &input_str, - &mut md_collector, - codes, - options.enable_per_target_ignores, - None, - ); + find_testable_code(&input_str, &mut md_collector, codes, None); let mut collector = CreateRunnableDocTests::new(options.clone(), opts); md_collector.tests.into_iter().for_each(|t| collector.add_test(t)); @@ -122,6 +116,7 @@ pub(crate) fn test(input: &Input, options: Options) -> Result<(), String> { &Arc::new(Mutex::new(Vec::new())), standalone_tests, mergeable_tests, + None, ); Ok(()) } diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 234f40c6c1ab2..39a4f23560a7e 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -14,6 +14,7 @@ pub(crate) struct DocTestRunner { crate_attrs: FxIndexSet, ids: String, output: String, + output_merged_tests: String, supports_color: bool, nb_tests: usize, } @@ -24,6 +25,7 @@ impl DocTestRunner { crate_attrs: FxIndexSet::default(), ids: String::new(), output: String::new(), + output_merged_tests: String::new(), supports_color: true, nb_tests: 0, } @@ -45,17 +47,15 @@ impl DocTestRunner { self.crate_attrs.insert(line.to_string()); } } - if !self.ids.is_empty() { - self.ids.push(','); - } self.ids.push_str(&format!( - "{}::TEST", + "tests.push({}::TEST);\n", generate_mergeable_doctest( doctest, scraped_test, ignore, self.nb_tests, - &mut self.output + &mut self.output, + &mut self.output_merged_tests, ), )); self.supports_color &= doctest.supports_color; @@ -78,9 +78,11 @@ impl DocTestRunner { " .to_string(); + let mut code_prefix = String::new(); + for crate_attr in &self.crate_attrs { - code.push_str(crate_attr); - code.push('\n'); + code_prefix.push_str(crate_attr); + code_prefix.push('\n'); } if opts.attrs.is_empty() { @@ -88,15 +90,16 @@ impl DocTestRunner { // 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. - code.push_str("#![allow(unused)]\n"); + code_prefix.push_str("#![allow(unused)]\n"); } // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. for attr in &opts.attrs { - code.push_str(&format!("#![{attr}]\n")); + code_prefix.push_str(&format!("#![{attr}]\n")); } code.push_str("extern crate test;\n"); + writeln!(code, "extern crate doctest_bundle_{edition} as doctest_bundle;").unwrap(); let test_args = test_args.iter().fold(String::new(), |mut x, arg| { write!(x, "{arg:?}.to_string(),").unwrap(); @@ -110,6 +113,7 @@ impl DocTestRunner { mod __doctest_mod {{ use std::sync::OnceLock; use std::path::PathBuf; + use std::process::ExitCode; pub static BINARY_PATH: OnceLock = OnceLock::new(); pub const RUN_OPTION: &str = \"RUSTDOC_DOCTEST_RUN_NB_TEST\"; @@ -120,35 +124,54 @@ mod __doctest_mod {{ }} #[allow(unused)] - pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> Result<(), String> {{ + pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> ExitCode {{ let out = std::process::Command::new(bin) .env(self::RUN_OPTION, test_nb.to_string()) .args(std::env::args().skip(1).collect::>()) .output() .expect(\"failed to run command\"); if !out.status.success() {{ - Err(String::from_utf8_lossy(&out.stderr).to_string()) + if let Some(code) = out.status.code() {{ + eprintln!(\"Test executable failed (exit status: {{code}}).\"); + }} else {{ + eprintln!(\"Test executable failed (terminated by signal).\"); + }} + if !out.stdout.is_empty() || !out.stderr.is_empty() {{ + eprintln!(); + }} + if !out.stdout.is_empty() {{ + eprintln!(\"stdout:\"); + eprintln!(\"{{}}\", String::from_utf8_lossy(&out.stdout)); + }} + if !out.stderr.is_empty() {{ + eprintln!(\"stderr:\"); + eprintln!(\"{{}}\", String::from_utf8_lossy(&out.stderr)); + }} + ExitCode::FAILURE }} else {{ - Ok(()) + ExitCode::SUCCESS }} }} }} #[rustc_main] fn main() -> std::process::ExitCode {{ -const TESTS: [test::TestDescAndFn; {nb_tests}] = [{ids}]; -let test_marker = std::ffi::OsStr::new(__doctest_mod::RUN_OPTION); +let tests = {{ + let mut tests = Vec::with_capacity({nb_tests}); + {ids} + tests +}}; let test_args = &[{test_args}]; const ENV_BIN: &'static str = \"RUSTDOC_DOCTEST_BIN_PATH\"; if let Ok(binary) = std::env::var(ENV_BIN) {{ let _ = crate::__doctest_mod::BINARY_PATH.set(binary.into()); unsafe {{ std::env::remove_var(ENV_BIN); }} - return std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None)); + return std::process::Termination::report(test::test_main(test_args, tests, None)); }} else if let Ok(nb_test) = std::env::var(__doctest_mod::RUN_OPTION) {{ if let Ok(nb_test) = nb_test.parse::() {{ - if let Some(test) = TESTS.get(nb_test) {{ - if let test::StaticTestFn(f) = test.testfn {{ + if let Some(test) = tests.get(nb_test) {{ + if let test::StaticTestFn(f) = &test.testfn {{ return std::process::Termination::report(f()); }} }} @@ -158,15 +181,15 @@ if let Ok(binary) = std::env::var(ENV_BIN) {{ eprintln!(\"WARNING: No rustdoc doctest environment variable provided so doctests will be run in \ the same process\"); -std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None)) +std::process::Termination::report(test::test_main(test_args, tests, None)) }}", nb_tests = self.nb_tests, - output = self.output, + output = self.output_merged_tests, ids = self.ids, ) .expect("failed to generate test code"); let runnable_test = RunnableDocTest { - full_test_code: code, + full_test_code: format!("{code_prefix}{code}", code = self.output), full_test_line_offset: 0, test_opts: test_options, global_opts: opts.clone(), @@ -174,7 +197,7 @@ std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), N line: 0, edition, no_run: false, - is_multiple_tests: true, + merged_test_code: Some(code), }; let ret = run_test(runnable_test, rustdoc_options, self.supports_color, |_: UnusedExterns| {}); @@ -189,14 +212,15 @@ fn generate_mergeable_doctest( ignore: bool, id: usize, output: &mut String, + output_merged_tests: &mut String, ) -> String { let test_id = format!("__doctest_{id}"); if ignore { // We generate nothing else. - writeln!(output, "mod {test_id} {{\n").unwrap(); + writeln!(output, "pub mod {test_id} {{}}\n").unwrap(); } else { - writeln!(output, "mod {test_id} {{\n{}{}", doctest.crates, doctest.maybe_crate_attrs) + writeln!(output, "pub mod {test_id} {{\n{}{}", doctest.crates, doctest.maybe_crate_attrs) .unwrap(); if doctest.has_main_fn { output.push_str(&doctest.everything_else); @@ -216,11 +240,17 @@ fn main() {returns_result} {{ ) .unwrap(); } + writeln!( + output, + "\npub fn __main_fn() -> impl std::process::Termination {{ main() }} \n}}\n" + ) + .unwrap(); } let not_running = ignore || scraped_test.langstr.no_run; writeln!( - output, + output_merged_tests, " +mod {test_id} {{ pub const TEST: test::TestDescAndFn = test::TestDescAndFn::new_doctest( {test_name:?}, {ignore}, {file:?}, {line}, {no_run}, {should_panic}, test::StaticTestFn( @@ -242,7 +272,7 @@ test::StaticTestFn( if let Some(bin_path) = crate::__doctest_mod::doctest_path() {{ test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id})) }} else {{ - test::assert_test_result(self::main()) + test::assert_test_result(doctest_bundle::{test_id}::__main_fn()) }} ", ) diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 3ac7abd0aa5d0..43dcfab880b5a 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -63,24 +63,23 @@ impl DocTestVisitor for RustCollector { pub(super) struct HirCollector<'tcx> { codes: ErrorCodes, tcx: TyCtxt<'tcx>, - enable_per_target_ignores: bool, collector: RustCollector, } impl<'tcx> HirCollector<'tcx> { - pub fn new(codes: ErrorCodes, enable_per_target_ignores: bool, tcx: TyCtxt<'tcx>) -> Self { + pub fn new(codes: ErrorCodes, tcx: TyCtxt<'tcx>) -> Self { let collector = RustCollector { source_map: tcx.sess.psess.clone_source_map(), cur_path: vec![], position: DUMMY_SP, tests: vec![], }; - Self { codes, enable_per_target_ignores, tcx, collector } + Self { codes, tcx, collector } } pub fn collect_crate(mut self) -> Vec { let tcx = self.tcx; - self.visit_testable("".to_string(), CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID), |this| { + self.visit_testable(None, CRATE_DEF_ID, tcx.hir_span(CRATE_HIR_ID), |this| { tcx.hir_walk_toplevel_module(this) }); self.collector.tests @@ -90,23 +89,23 @@ impl<'tcx> HirCollector<'tcx> { impl HirCollector<'_> { fn visit_testable( &mut self, - name: String, + name: Option, def_id: LocalDefId, sp: Span, nested: F, ) { - let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id)); + let ast_attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id)); if let Some(ref cfg) = extract_cfg_from_attrs(ast_attrs.iter(), self.tcx, &FxHashSet::default()) + && !cfg.matches(&self.tcx.sess.psess) { - if !cfg.matches(&self.tcx.sess.psess, Some(self.tcx.features())) { - return; - } + return; } - let has_name = !name.is_empty(); - if has_name { + let mut has_name = false; + if let Some(name) = name { self.collector.cur_path.push(name); + has_name = true; } // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with @@ -131,7 +130,6 @@ impl HirCollector<'_> { &doc, &mut self.collector, self.codes, - self.enable_per_target_ignores, Some(&crate::html::markdown::ExtraInfo::new(self.tcx, def_id, span)), ); } @@ -154,9 +152,9 @@ impl<'tcx> intravisit::Visitor<'tcx> for HirCollector<'tcx> { fn visit_item(&mut self, item: &'tcx hir::Item<'_>) { let name = match &item.kind { hir::ItemKind::Impl(impl_) => { - rustc_hir_pretty::id_to_string(&self.tcx, impl_.self_ty.hir_id) + Some(rustc_hir_pretty::id_to_string(&self.tcx, impl_.self_ty.hir_id)) } - _ => item.ident.to_string(), + _ => item.kind.ident().map(|ident| ident.to_string()), }; self.visit_testable(name, item.owner_id.def_id, item.span, |this| { @@ -165,31 +163,46 @@ impl<'tcx> intravisit::Visitor<'tcx> for HirCollector<'tcx> { } fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem<'_>) { - self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { - intravisit::walk_trait_item(this, item); - }); + self.visit_testable( + Some(item.ident.to_string()), + item.owner_id.def_id, + item.span, + |this| { + intravisit::walk_trait_item(this, item); + }, + ); } fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem<'_>) { - self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { - intravisit::walk_impl_item(this, item); - }); + self.visit_testable( + Some(item.ident.to_string()), + item.owner_id.def_id, + item.span, + |this| { + intravisit::walk_impl_item(this, item); + }, + ); } fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'_>) { - self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { - intravisit::walk_foreign_item(this, item); - }); + self.visit_testable( + Some(item.ident.to_string()), + item.owner_id.def_id, + item.span, + |this| { + intravisit::walk_foreign_item(this, item); + }, + ); } fn visit_variant(&mut self, v: &'tcx hir::Variant<'_>) { - self.visit_testable(v.ident.to_string(), v.def_id, v.span, |this| { + self.visit_testable(Some(v.ident.to_string()), v.def_id, v.span, |this| { intravisit::walk_variant(this, v); }); } fn visit_field_def(&mut self, f: &'tcx hir::FieldDef<'_>) { - self.visit_testable(f.ident.to_string(), f.def_id, f.span, |this| { + self.visit_testable(Some(f.ident.to_string()), f.def_id, f.span, |this| { intravisit::walk_field_def(this, f); }); } diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index e2b964bf5af57..618c2041b43c9 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -197,6 +197,7 @@ fn make_test_crate_attrs() { assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] #![feature(sick_rad)] + fn main() { assert_eq!(2+2, 4); }" @@ -228,8 +229,8 @@ fn make_test_fake_main() { let input = "//Ceci n'est pas une `fn main` assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] -//Ceci n'est pas une `fn main` fn main() { +//Ceci n'est pas une `fn main` assert_eq!(2+2, 4); }" .to_string(); @@ -259,8 +260,8 @@ fn make_test_issues_21299() { assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] -// fn main fn main() { +// fn main assert_eq!(2+2, 4); }" .to_string(); @@ -381,23 +382,74 @@ fn main() { } #[test] -fn check_split_args() { - fn compare(input: &str, expected: &[&str]) { - let output = super::split_args(input); - let expected = expected.iter().map(|s| s.to_string()).collect::>(); - assert_eq!(expected, output, "test failed for {input:?}"); - } +fn comment_in_attrs() { + // If there is an inline code comment after attributes, we need to ensure that + // a backline will be added to prevent generating code "inside" it (and thus generating) + // invalid code. + let opts = default_global_opts(""); + let input = "\ +#![feature(rustdoc_internals)] +#![allow(internal_features)] +#![doc(rust_logo)] +//! This crate has the Rust(tm) branding on it."; + let expected = "\ +#![allow(unused)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] +#![doc(rust_logo)] +//! This crate has the Rust(tm) branding on it. +fn main() { + +}" + .to_string(); + let (output, len) = make_test(input, None, false, &opts, None); + assert_eq!((output, len), (expected, 2)); + + // And same, if there is a `main` function provided by the user, we ensure that it's + // correctly separated. + let input = "\ +#![feature(rustdoc_internals)] +#![allow(internal_features)] +#![doc(rust_logo)] +//! This crate has the Rust(tm) branding on it. +fn main() {}"; + let expected = "\ +#![allow(unused)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] +#![doc(rust_logo)] +//! This crate has the Rust(tm) branding on it. + +fn main() {}" + .to_string(); + let (output, len) = make_test(input, None, false, &opts, None); + assert_eq!((output, len), (expected, 1)); +} + +// This test ensures that the only attributes taken into account when we switch between +// "crate level" content and the rest doesn't include inner attributes span, as it would +// include part of the item and generate broken code. +#[test] +fn inner_attributes() { + let opts = default_global_opts(""); + let input = r#" +//! A doc comment that applies to the implicit anonymous module of this crate + +pub mod outer_module { + //!! - Still an inner line doc (but with a bang at the beginning) +} +"#; + let expected = "#![allow(unused)] - compare("'a' \"b\"c", &["a", "bc"]); - compare("'a' \"b \"c d", &["a", "b c", "d"]); - compare("'a' \"b\\\"c\"", &["a", "b\"c"]); - compare("'a\"'", &["a\""]); - compare("\"a'\"", &["a'"]); - compare("\\ a", &[" a"]); - compare("\\\\", &["\\"]); - compare("a'", &["a"]); - compare("a ", &["a"]); - compare("a b", &["a", "b"]); - compare("a\n\t \rb", &["a", "b"]); - compare("a\n\t1 \rb", &["a", "1", "b"]); +//! A doc comment that applies to the implicit anonymous module of this crate + + +fn main() { +pub mod outer_module { + //!! - Still an inner line doc (but with a bang at the beginning) +} +}" + .to_string(); + let (output, len) = make_test(input, None, false, &opts, None); + assert_eq!((output, len), (expected, 2)); } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 4760e579199aa..4989bd718c9f9 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -1,6 +1,6 @@ use std::mem; -use rustc_attr_parsing::StabilityLevel; +use rustc_attr_data_structures::StabilityLevel; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; use rustc_middle::ty::{self, TyCtxt}; @@ -288,7 +288,7 @@ impl DocFolder for CacheBuilder<'_, '_> { // Keep track of the fully qualified path for this item. let pushed = match item.name { - Some(n) if !n.is_empty() => { + Some(n) => { self.cache.stack.push(n); true } @@ -385,7 +385,6 @@ impl DocFolder for CacheBuilder<'_, '_> { // implementations elsewhere. let ret = if let clean::Item { inner: box clean::ItemInner { kind: clean::ImplItem(ref i), .. }, - .. } = item { // Figure out the id of this impl. This may map to a @@ -419,7 +418,9 @@ impl DocFolder for CacheBuilder<'_, '_> { } } - if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { + if let Some(trait_) = &i.trait_ + && let Some(generics) = trait_.generics() + { for bound in generics { dids.extend(bound.def_id(self.cache)); } diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index de6537e992f19..3aba7a370adb2 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -70,12 +70,12 @@ impl Serialize for ItemType { impl<'a> From<&'a clean::Item> for ItemType { fn from(item: &'a clean::Item) -> ItemType { - let kind = match item.kind { - clean::StrippedItem(box ref item) => item, - ref kind => kind, + let kind = match &item.kind { + clean::StrippedItem(box item) => item, + kind => kind, }; - match *kind { + match kind { clean::ModuleItem(..) => ItemType::Module, clean::ExternCrateItem { .. } => ItemType::ExternCrate, clean::ImportItem(..) => ItemType::Import, @@ -103,7 +103,7 @@ impl<'a> From<&'a clean::Item> for ItemType { clean::ForeignTypeItem => ItemType::ForeignType, clean::KeywordItem => ItemType::Keyword, clean::TraitAliasItem(..) => ItemType::TraitAlias, - clean::ProcMacroItem(ref mac) => match mac.kind { + clean::ProcMacroItem(mac) => match mac.kind { MacroKind::Bang => ItemType::Macro, MacroKind::Attr => ItemType::ProcAttribute, MacroKind::Derive => ItemType::ProcDerive, @@ -134,22 +134,15 @@ impl ItemType { DefKind::Trait => Self::Trait, DefKind::TyAlias => Self::TypeAlias, DefKind::TraitAlias => Self::TraitAlias, - DefKind::Macro(kind) => match kind { - MacroKind::Bang => ItemType::Macro, - MacroKind::Attr => ItemType::ProcAttribute, - MacroKind::Derive => ItemType::ProcDerive, - }, + DefKind::Macro(MacroKind::Bang) => ItemType::Macro, + DefKind::Macro(MacroKind::Attr) => ItemType::ProcAttribute, + DefKind::Macro(MacroKind::Derive) => ItemType::ProcDerive, DefKind::ForeignTy => Self::ForeignType, DefKind::Variant => Self::Variant, DefKind::Field => Self::StructField, DefKind::AssocTy => Self::AssocType, - DefKind::AssocFn => { - if let Some(DefKind::Trait) = parent_kind { - Self::TyMethod - } else { - Self::Method - } - } + DefKind::AssocFn if let Some(DefKind::Trait) = parent_kind => Self::TyMethod, + DefKind::AssocFn => Self::Method, DefKind::Ctor(CtorOf::Struct, _) => Self::Struct, DefKind::Ctor(CtorOf::Variant, _) => Self::Variant, DefKind::AssocConst => Self::AssocConst, @@ -170,7 +163,7 @@ impl ItemType { } pub(crate) fn as_str(&self) -> &'static str { - match *self { + match self { ItemType::Module => "mod", ItemType::ExternCrate => "externcrate", ItemType::Import => "import", @@ -199,10 +192,10 @@ impl ItemType { } } pub(crate) fn is_method(&self) -> bool { - matches!(*self, ItemType::Method | ItemType::TyMethod) + matches!(self, ItemType::Method | ItemType::TyMethod) } pub(crate) fn is_adt(&self) -> bool { - matches!(*self, ItemType::Struct | ItemType::Union | ItemType::Enum) + matches!(self, ItemType::Struct | ItemType::Union | ItemType::Enum) } } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 522ef1af376cb..486d4ae932d97 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -11,11 +11,11 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::fmt::{self, Display, Write}; use std::iter::{self, once}; +use std::slice; use itertools::Either; use rustc_abi::ExternAbi; -use rustc_attr_parsing::{ConstStability, StabilityLevel, StableSince}; -use rustc_data_structures::captures::Captures; +use rustc_attr_data_structures::{ConstStability, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -41,10 +41,10 @@ pub(crate) fn write_str(s: &mut String, f: fmt::Arguments<'_>) { s.write_fmt(f).unwrap(); } -pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>( - bounds: &'a [clean::GenericBound], - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +pub(crate) fn print_generic_bounds( + bounds: &[clean::GenericBound], + cx: &Context<'_>, +) -> impl Display { fmt::from_fn(move |f| { let mut bounds_dup = FxHashSet::default(); @@ -57,10 +57,7 @@ pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>( } impl clean::GenericParamDef { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match &self.kind { clean::GenericParamDefKind::Lifetime { outlives } => { write!(f, "{}", self.name)?; @@ -80,7 +77,7 @@ impl clean::GenericParamDef { print_generic_bounds(bounds, cx).fmt(f)?; } - if let Some(ref ty) = default { + if let Some(ty) = default { f.write_str(" = ")?; ty.print(cx).fmt(f)?; } @@ -107,10 +104,7 @@ impl clean::GenericParamDef { } impl clean::Generics { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable(); if real_params.peek().is_none() { @@ -134,10 +128,7 @@ pub(crate) enum Ending { NoNewline, } -fn print_where_predicate<'a, 'tcx: 'a>( - predicate: &'a clean::WherePredicate, - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +fn print_where_predicate(predicate: &clean::WherePredicate, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { match predicate { clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => { @@ -173,12 +164,12 @@ fn print_where_predicate<'a, 'tcx: 'a>( /// * The Generics from which to emit a where-clause. /// * The number of spaces to indent each line with. /// * Whether the where-clause needs to add a comma and newline after the last bound. -pub(crate) fn print_where_clause<'a, 'tcx: 'a>( - gens: &'a clean::Generics, - cx: &'a Context<'tcx>, +pub(crate) fn print_where_clause( + gens: &clean::Generics, + cx: &Context<'_>, indent: usize, ending: Ending, -) -> Option> { +) -> Option { if gens.where_predicates.is_empty() { return None; } @@ -250,13 +241,13 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( } impl clean::Lifetime { - pub(crate) fn print(&self) -> impl Display + '_ { + pub(crate) fn print(&self) -> impl Display { self.0.as_str() } } impl clean::ConstantKind { - pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl Display + '_ { + pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl Display { let expr = self.expr(tcx); fmt::from_fn(move |f| { if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) } @@ -265,7 +256,7 @@ impl clean::ConstantKind { } impl clean::PolyTrait { - fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { + fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { print_higher_ranked_params_with_space(&self.generic_params, cx, "for").fmt(f)?; self.trait_.print(cx).fmt(f) @@ -274,10 +265,7 @@ impl clean::PolyTrait { } impl clean::GenericBound { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self { clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()), clean::GenericBound::TraitBound(ty, modifiers) => { @@ -296,7 +284,7 @@ impl clean::GenericBound { } else { f.write_str("use<")?; } - args.iter().joined(", ", f)?; + args.iter().map(|arg| arg.name()).joined(", ", f)?; if f.alternate() { f.write_str(">") } else { f.write_str(">") } } }) @@ -304,7 +292,7 @@ impl clean::GenericBound { } impl clean::GenericArgs { - fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { + fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { match self { clean::GenericArgs::AngleBracketed { args, constraints } => { @@ -345,6 +333,9 @@ impl clean::GenericArgs { } } } + clean::GenericArgs::ReturnTypeNotation => { + f.write_str("(..)")?; + } } Ok(()) }) @@ -636,10 +627,9 @@ pub(crate) fn href_relative_parts<'fqp>( // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1) if f != r { let dissimilar_part_count = relative_to_fqp.len() - i; - let fqp_module = &fqp[i..fqp.len()]; + let fqp_module = &fqp[i..]; return Box::new( - iter::repeat(sym::dotdot) - .take(dissimilar_part_count) + iter::repeat_n(sym::dotdot, dissimilar_part_count) .chain(fqp_module.iter().copied()), ); } @@ -652,7 +642,7 @@ pub(crate) fn href_relative_parts<'fqp>( Ordering::Greater => { // e.g. linking to std::sync from std::sync::atomic let dissimilar_part_count = relative_to_fqp.len() - fqp.len(); - Box::new(iter::repeat(sym::dotdot).take(dissimilar_part_count)) + Box::new(iter::repeat_n(sym::dotdot, dissimilar_part_count)) } Ordering::Equal => { // linking to the same module @@ -661,33 +651,35 @@ pub(crate) fn href_relative_parts<'fqp>( } } -pub(crate) fn link_tooltip(did: DefId, fragment: &Option, cx: &Context<'_>) -> String { - let cache = cx.cache(); - let Some((fqp, shortty)) = cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) - else { - return String::new(); - }; - let mut buf = String::new(); - let fqp = if *shortty == ItemType::Primitive { - // primitives are documented in a crate, but not actually part of it - &fqp[fqp.len() - 1..] - } else { - fqp - }; - if let &Some(UrlFragment::Item(id)) = fragment { - write_str(&mut buf, format_args!("{} ", cx.tcx().def_descr(id))); - for component in fqp { - write_str(&mut buf, format_args!("{component}::")); - } - write_str(&mut buf, format_args!("{}", cx.tcx().item_name(id))); - } else if !fqp.is_empty() { - let mut fqp_it = fqp.iter(); - write_str(&mut buf, format_args!("{shortty} {}", fqp_it.next().unwrap())); - for component in fqp_it { - write_str(&mut buf, format_args!("::{component}")); +pub(crate) fn link_tooltip( + did: DefId, + fragment: &Option, + cx: &Context<'_>, +) -> impl fmt::Display { + fmt::from_fn(move |f| { + let cache = cx.cache(); + let Some((fqp, shortty)) = cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) + else { + return Ok(()); + }; + let fqp = if *shortty == ItemType::Primitive { + // primitives are documented in a crate, but not actually part of it + slice::from_ref(fqp.last().unwrap()) + } else { + fqp + }; + if let &Some(UrlFragment::Item(id)) = fragment { + write!(f, "{} ", cx.tcx().def_descr(id))?; + for component in fqp { + write!(f, "{component}::")?; + } + write!(f, "{}", cx.tcx().item_name(id))?; + } else if !fqp.is_empty() { + write!(f, "{shortty} ")?; + fqp.iter().joined("::", f)?; } - } - buf + Ok(()) + }) } /// Used to render a [`clean::Path`]. @@ -783,10 +775,9 @@ fn primitive_link_fragment( ExternalLocation::Local => { let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); Some(if cx.current.first() == Some(&cname_sym) { - iter::repeat(sym::dotdot).take(cx.current.len() - 1).collect() + iter::repeat_n(sym::dotdot, cx.current.len() - 1).collect() } else { - iter::repeat(sym::dotdot) - .take(cx.current.len()) + iter::repeat_n(sym::dotdot, cx.current.len()) .chain(iter::once(cname_sym)) .collect() }) @@ -809,11 +800,11 @@ fn primitive_link_fragment( Ok(()) } -fn tybounds<'a, 'tcx: 'a>( - bounds: &'a [clean::PolyTrait], - lt: &'a Option, - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +fn tybounds( + bounds: &[clean::PolyTrait], + lt: &Option, + cx: &Context<'_>, +) -> impl Display { fmt::from_fn(move |f| { bounds.iter().map(|bound| bound.print(cx)).joined(" + ", f)?; if let Some(lt) = lt { @@ -825,11 +816,11 @@ fn tybounds<'a, 'tcx: 'a>( }) } -fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>( - params: &'a [clean::GenericParamDef], - cx: &'a Context<'tcx>, +fn print_higher_ranked_params_with_space( + params: &[clean::GenericParamDef], + cx: &Context<'_>, keyword: &'static str, -) -> impl Display + 'a + Captures<'tcx> { +) -> impl Display { fmt::from_fn(move |f| { if !params.is_empty() { f.write_str(keyword)?; @@ -841,11 +832,7 @@ fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>( }) } -pub(crate) fn anchor<'a: 'cx, 'cx>( - did: DefId, - text: Symbol, - cx: &'cx Context<'a>, -) -> impl Display + Captures<'a> + 'cx { +pub(crate) fn anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { let parts = href(did, cx); if let Ok((url, short_ty, fqp)) = parts { @@ -869,15 +856,15 @@ fn fmt_type( ) -> fmt::Result { trace!("fmt_type(t = {t:?})"); - match *t { + match t { clean::Generic(name) => f.write_str(name.as_str()), clean::SelfTy => f.write_str("Self"), - clean::Type::Path { ref path } => { + clean::Type::Path { path } => { // Paths like `T::Output` and `Self::Output` should be rendered with all segments. let did = path.def_id(); resolved_path(f, did, path, path.is_assoc_ty(), use_absolute, cx) } - clean::DynTrait(ref bounds, ref lt) => { + clean::DynTrait(bounds, lt) => { f.write_str("dyn ")?; tybounds(bounds, lt, cx).fmt(f) } @@ -885,8 +872,8 @@ fn fmt_type( clean::Primitive(clean::PrimitiveType::Never) => { primitive_link(f, PrimitiveType::Never, format_args!("!"), cx) } - clean::Primitive(prim) => primitive_link(f, prim, format_args!("{}", prim.as_sym()), cx), - clean::BareFunction(ref decl) => { + &clean::Primitive(prim) => primitive_link(f, prim, format_args!("{}", prim.as_sym()), cx), + clean::BareFunction(decl) => { print_higher_ranked_params_with_space(&decl.generic_params, cx, "for").fmt(f)?; decl.safety.print_with_space().fmt(f)?; print_abi_with_space(decl.abi).fmt(f)?; @@ -897,11 +884,11 @@ fn fmt_type( } decl.decl.print(cx).fmt(f) } - clean::UnsafeBinder(ref binder) => { + clean::UnsafeBinder(binder) => { print_higher_ranked_params_with_space(&binder.generic_params, cx, "unsafe").fmt(f)?; binder.ty.print(cx).fmt(f) } - clean::Tuple(ref typs) => match &typs[..] { + clean::Tuple(typs) => match &typs[..] { &[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx), [one] => { if let clean::Generic(name) = one { @@ -938,45 +925,36 @@ fn fmt_type( } } }, - clean::Slice(ref t) => match **t { - clean::Generic(name) => { - primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx) - } - _ => { - write!(f, "[")?; - t.print(cx).fmt(f)?; - write!(f, "]") - } - }, - clean::Type::Pat(ref t, ref pat) => { + clean::Slice(box clean::Generic(name)) => { + primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx) + } + clean::Slice(t) => { + write!(f, "[")?; + t.print(cx).fmt(f)?; + write!(f, "]") + } + clean::Type::Pat(t, pat) => { fmt::Display::fmt(&t.print(cx), f)?; write!(f, " is {pat}") } - clean::Array(ref t, ref n) => match **t { - clean::Generic(name) if !f.alternate() => primitive_link( - f, - PrimitiveType::Array, - format_args!("[{name}; {n}]", n = Escape(n)), - cx, - ), - _ => { - write!(f, "[")?; - t.print(cx).fmt(f)?; - if f.alternate() { - write!(f, "; {n}")?; - } else { - write!(f, "; ")?; - primitive_link( - f, - PrimitiveType::Array, - format_args!("{n}", n = Escape(n)), - cx, - )?; - } - write!(f, "]") + clean::Array(box clean::Generic(name), n) if !f.alternate() => primitive_link( + f, + PrimitiveType::Array, + format_args!("[{name}; {n}]", n = Escape(n)), + cx, + ), + clean::Array(t, n) => { + write!(f, "[")?; + t.print(cx).fmt(f)?; + if f.alternate() { + write!(f, "; {n}")?; + } else { + write!(f, "; ")?; + primitive_link(f, PrimitiveType::Array, format_args!("{n}", n = Escape(n)), cx)?; } - }, - clean::RawPointer(m, ref t) => { + write!(f, "]") + } + clean::RawPointer(m, t) => { let m = match m { hir::Mutability::Mut => "mut", hir::Mutability::Not => "const", @@ -1004,7 +982,7 @@ fn fmt_type( t.print(cx).fmt(f) } } - clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => { + clean::BorrowedRef { lifetime: l, mutability, type_: ty } => { let lt = fmt::from_fn(|f| match l { Some(l) => write!(f, "{} ", l.print()), _ => Ok(()), @@ -1041,11 +1019,11 @@ fn fmt_type( } Ok(()) } - clean::ImplTrait(ref bounds) => { + clean::ImplTrait(bounds) => { f.write_str("impl ")?; print_generic_bounds(bounds, cx).fmt(f) } - clean::QPath(box clean::QPathData { + &clean::QPath(box clean::QPathData { ref assoc, ref self_type, ref trait_, @@ -1121,29 +1099,19 @@ fn fmt_type( } impl clean::Type { - pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'b + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| fmt_type(self, f, false, cx)) } } impl clean::Path { - pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'b + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx)) } } impl clean::Impl { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - use_absolute: bool, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, use_absolute: bool, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { f.write_str("impl")?; self.generics.print(cx).fmt(f)?; @@ -1182,12 +1150,12 @@ impl clean::Impl { print_where_clause(&self.generics, cx, 0, Ending::Newline).maybe_display().fmt(f) }) } - fn print_type<'a, 'tcx: 'a>( + fn print_type( &self, type_: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool, - cx: &'a Context<'tcx>, + cx: &Context<'_>, ) -> Result<(), fmt::Error> { if let clean::Type::Tuple(types) = type_ && let [clean::Type::Generic(name)] = &types[..] @@ -1209,8 +1177,8 @@ impl clean::Impl { { primitive_link(f, PrimitiveType::Array, format_args!("[{name}; N]"), cx)?; } else if let clean::BareFunction(bare_fn) = &type_ - && let [clean::Argument { type_: clean::Type::Generic(name), .. }] = - &bare_fn.decl.inputs.values[..] + && let [clean::Parameter { type_: clean::Type::Generic(name), .. }] = + &bare_fn.decl.inputs[..] && (self.kind.is_fake_variadic() || self.kind.is_auto()) { // Hardcoded anchor library/core/src/primitive_docs.rs @@ -1257,25 +1225,20 @@ impl clean::Impl { } } -impl clean::Arguments { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { - fmt::from_fn(move |f| { - self.values - .iter() - .map(|input| { - fmt::from_fn(|f| { - if !input.name.is_empty() { - write!(f, "{}: ", input.name)?; - } - input.type_.print(cx).fmt(f) - }) +pub(crate) fn print_params(params: &[clean::Parameter], cx: &Context<'_>) -> impl Display { + fmt::from_fn(move |f| { + params + .iter() + .map(|param| { + fmt::from_fn(|f| { + if let Some(name) = param.name { + write!(f, "{}: ", name)?; + } + param.type_.print(cx).fmt(f) }) - .joined(", ", f) - }) - } + }) + .joined(", ", f) + }) } // Implements Write but only counts the bytes "written". @@ -1301,25 +1264,22 @@ impl Display for Indent { } impl clean::FnDecl { - pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'b + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { let ellipsis = if self.c_variadic { ", ..." } else { "" }; if f.alternate() { write!( f, - "({args:#}{ellipsis}){arrow:#}", - args = self.inputs.print(cx), + "({params:#}{ellipsis}){arrow:#}", + params = print_params(&self.inputs, cx), ellipsis = ellipsis, arrow = self.print_output(cx) ) } else { write!( f, - "({args}{ellipsis}){arrow}", - args = self.inputs.print(cx), + "({params}{ellipsis}){arrow}", + params = print_params(&self.inputs, cx), ellipsis = ellipsis, arrow = self.print_output(cx) ) @@ -1333,12 +1293,12 @@ impl clean::FnDecl { /// are preserved. /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is /// necessary. - pub(crate) fn full_print<'a, 'tcx: 'a>( - &'a self, + pub(crate) fn full_print( + &self, header_len: usize, indent: usize, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + cx: &Context<'_>, + ) -> impl Display { fmt::from_fn(move |f| { // First, generate the text form of the declaration, with no line wrapping, and count the bytes. let mut counter = WriteCounter(0); @@ -1365,14 +1325,14 @@ impl clean::FnDecl { write!(f, "(")?; if let Some(n) = line_wrapping_indent - && !self.inputs.values.is_empty() + && !self.inputs.is_empty() { write!(f, "\n{}", Indent(n + 4))?; } - let last_input_index = self.inputs.values.len().checked_sub(1); - for (i, input) in self.inputs.values.iter().enumerate() { - if let Some(selfty) = input.to_receiver() { + let last_input_index = self.inputs.len().checked_sub(1); + for (i, param) in self.inputs.iter().enumerate() { + if let Some(selfty) = param.to_receiver() { match selfty { clean::SelfTy => { write!(f, "self")?; @@ -1390,11 +1350,13 @@ impl clean::FnDecl { } } } else { - if input.is_const { + if param.is_const { write!(f, "const ")?; } - write!(f, "{}: ", input.name)?; - input.type_.print(cx).fmt(f)?; + if let Some(name) = param.name { + write!(f, "{}: ", name)?; + } + param.type_.print(cx).fmt(f)?; } match (line_wrapping_indent, last_input_index) { (_, None) => (), @@ -1420,10 +1382,7 @@ impl clean::FnDecl { self.print_output(cx).fmt(f) } - fn print_output<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + fn print_output(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match &self.output { clean::Tuple(tys) if tys.is_empty() => Ok(()), ty if f.alternate() => { @@ -1434,10 +1393,7 @@ impl clean::FnDecl { } } -pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>( - item: &clean::Item, - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +pub(crate) fn visibility_print_with_space(item: &clean::Item, cx: &Context<'_>) -> impl Display { use std::fmt::Write as _; let vis: Cow<'static, str> = match item.visibility(cx.tcx()) { None => "".into(), @@ -1546,10 +1502,7 @@ pub(crate) fn print_constness_with_space( } impl clean::Import { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self.kind { clean::ImportKind::Simple(name) => { if name == self.source.path.last() { @@ -1570,10 +1523,7 @@ impl clean::Import { } impl clean::ImportSource { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self.did { Some(did) => resolved_path(f, did, &self.path, true, false, cx), _ => { @@ -1593,10 +1543,7 @@ impl clean::ImportSource { } impl clean::AssocItemConstraint { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { f.write_str(self.assoc.name.as_str())?; self.assoc.args.print(cx).fmt(f)?; @@ -1627,15 +1574,12 @@ pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display { }) } -pub(crate) fn print_default_space<'a>(v: bool) -> &'a str { +pub(crate) fn print_default_space(v: bool) -> &'static str { if v { "default " } else { "" } } impl clean::GenericArg { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self { clean::GenericArg::Lifetime(lt) => lt.print().fmt(f), clean::GenericArg::Type(ty) => ty.print(cx).fmt(f), @@ -1646,10 +1590,7 @@ impl clean::GenericArg { } impl clean::Term { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self { clean::Term::Type(ty) => ty.print(cx).fmt(f), clean::Term::Constant(ct) => ct.print(cx.tcx()).fmt(f), diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index ed4b97d36252c..2db1ea8450ce1 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -9,7 +9,7 @@ use std::collections::VecDeque; use std::fmt::{Display, Write}; use rustc_data_structures::fx::FxIndexMap; -use rustc_lexer::{Cursor, LiteralKind, TokenKind}; +use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind}; use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, DUMMY_SP, Span}; @@ -100,7 +100,7 @@ fn write_header( } if let Some(extra) = extra_content { - out.push_str(&extra); + out.push_str(extra); } if class.is_empty() { write_str( @@ -131,7 +131,7 @@ fn write_header( /// * If the other `Class` is unclassified and only contains white characters (backline, /// whitespace, etc), it can be merged. /// * `Class::Ident` is considered the same as unclassified (because it doesn't have an associated -/// CSS class). +/// CSS class). fn can_merge(class1: Option, class2: Option, text: &str) -> bool { match (class1, class2) { (Some(c1), Some(c2)) => c1.is_equal_to(c2), @@ -233,7 +233,7 @@ impl TokenHandler<'_, '_, F> { #[inline] fn write_line_number(&mut self, line: u32, extra: &'static str) { - (self.write_line_number)(&mut self.out, line, extra); + (self.write_line_number)(self.out, line, extra); } } @@ -610,7 +610,7 @@ impl Decorations { let (mut starts, mut ends): (Vec<_>, Vec<_>) = info .0 .iter() - .flat_map(|(&kind, ranges)| ranges.into_iter().map(move |&(lo, hi)| ((lo, kind), hi))) + .flat_map(|(&kind, ranges)| ranges.iter().map(move |&(lo, hi)| ((lo, kind), hi))) .unzip(); // Sort the sequences in document order. @@ -638,7 +638,8 @@ impl<'src> Classifier<'src> { /// Takes as argument the source code to HTML-ify, the rust edition to use and the source code /// file span which will be used later on by the `span_correspondence_map`. fn new(src: &'src str, file_span: Span, decoration_info: Option<&DecorationInfo>) -> Self { - let tokens = PeekIter::new(TokenIter { src, cursor: Cursor::new(src) }); + let tokens = + PeekIter::new(TokenIter { src, cursor: Cursor::new(src, FrontmatterAllowed::Yes) }); let decorations = decoration_info.map(Decorations::new); Classifier { tokens, @@ -884,6 +885,7 @@ impl<'src> Classifier<'src> { | TokenKind::At | TokenKind::Tilde | TokenKind::Colon + | TokenKind::Frontmatter { .. } | TokenKind::Unknown => return no_highlight(sink), TokenKind::Question => Class::QuestionMark, @@ -1102,53 +1104,52 @@ fn string_without_closing_tag( }); } - if let Some(href_context) = href_context { - if let Some(href) = - href_context.context.shared.span_correspondence_map.get(&def_span).and_then(|href| { - let context = href_context.context; - // FIXME: later on, it'd be nice to provide two links (if possible) for all items: - // one to the documentation page and one to the source definition. - // FIXME: currently, external items only generate a link to their documentation, - // a link to their definition can be generated using this: - // https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338 - match href { - LinkFromSrc::Local(span) => { - context.href_from_span_relative(*span, &href_context.current_href) - } - LinkFromSrc::External(def_id) => { - format::href_with_root_path(*def_id, context, Some(href_context.root_path)) - .ok() - .map(|(url, _, _)| url) - } - LinkFromSrc::Primitive(prim) => format::href_with_root_path( - PrimitiveType::primitive_locations(context.tcx())[prim], - context, - Some(href_context.root_path), - ) - .ok() - .map(|(url, _, _)| url), - LinkFromSrc::Doc(def_id) => { - format::href_with_root_path(*def_id, context, Some(href_context.root_path)) - .ok() - .map(|(doc_link, _, _)| doc_link) - } + if let Some(href_context) = href_context + && let Some(href) = href_context.context.shared.span_correspondence_map.get(&def_span) + && let Some(href) = { + let context = href_context.context; + // FIXME: later on, it'd be nice to provide two links (if possible) for all items: + // one to the documentation page and one to the source definition. + // FIXME: currently, external items only generate a link to their documentation, + // a link to their definition can be generated using this: + // https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338 + match href { + LinkFromSrc::Local(span) => { + context.href_from_span_relative(*span, &href_context.current_href) } - }) - { - if !open_tag { - // We're already inside an element which has the same klass, no need to give it - // again. + LinkFromSrc::External(def_id) => { + format::href_with_root_path(*def_id, context, Some(href_context.root_path)) + .ok() + .map(|(url, _, _)| url) + } + LinkFromSrc::Primitive(prim) => format::href_with_root_path( + PrimitiveType::primitive_locations(context.tcx())[prim], + context, + Some(href_context.root_path), + ) + .ok() + .map(|(url, _, _)| url), + LinkFromSrc::Doc(def_id) => { + format::href_with_root_path(*def_id, context, Some(href_context.root_path)) + .ok() + .map(|(doc_link, _, _)| doc_link) + } + } + } + { + if !open_tag { + // We're already inside an element which has the same klass, no need to give it + // again. + write!(out, "{text_s}").unwrap(); + } else { + let klass_s = klass.as_html(); + if klass_s.is_empty() { write!(out, "{text_s}").unwrap(); } else { - let klass_s = klass.as_html(); - if klass_s.is_empty() { - write!(out, "{text_s}").unwrap(); - } else { - write!(out, "{text_s}").unwrap(); - } + write!(out, "{text_s}").unwrap(); } - return Some(""); } + return Some(""); } if !open_tag { write!(out, "{}", text_s).unwrap(); diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index df70df062fe0b..3b5f9b5a45897 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -1,7 +1,7 @@ -use std::fmt::{self, Display}; +use std::fmt::Display; use std::path::PathBuf; -use rinja::Template; +use askama::Template; use rustc_data_structures::fx::FxIndexMap; use super::static_files::{STATIC_FILES, StaticFiles}; @@ -71,23 +71,6 @@ struct PageLayout<'a> { pub(crate) use crate::html::render::sidebar::filters; -/// Implements [`Display`] for a function that accepts a mutable reference to a [`String`], and (optionally) writes to it. -/// -/// The wrapped function will receive an empty string, and can modify it, -/// and the `Display` implementation will write the contents of the string after the function has finished. -pub(crate) struct BufDisplay(pub F); - -impl Display for BufDisplay -where - F: Fn(&mut String), -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut buf = String::new(); - self.0(&mut buf); - f.write_str(&buf) - } -} - pub(crate) fn render( layout: &Layout, page: &Page<'_>, diff --git a/src/librustdoc/html/length_limit/tests.rs b/src/librustdoc/html/length_limit/tests.rs index 2185c034890fa..4ebecfd5f2414 100644 --- a/src/librustdoc/html/length_limit/tests.rs +++ b/src/librustdoc/html/length_limit/tests.rs @@ -9,41 +9,41 @@ fn empty() { #[test] fn basic() { let mut buf = HtmlWithLimit::new(60); - buf.push("Hello "); + let _ = buf.push("Hello "); buf.open_tag("em"); - buf.push("world"); + let _ = buf.push("world"); buf.close_tag(); - buf.push("!"); + let _ = buf.push("!"); assert_eq!(buf.finish(), "Hello world!"); } #[test] fn no_tags() { let mut buf = HtmlWithLimit::new(60); - buf.push("Hello"); - buf.push(" world!"); + let _ = buf.push("Hello"); + let _ = buf.push(" world!"); assert_eq!(buf.finish(), "Hello world!"); } #[test] fn limit_0() { let mut buf = HtmlWithLimit::new(0); - buf.push("Hello "); + let _ = buf.push("Hello "); buf.open_tag("em"); - buf.push("world"); + let _ = buf.push("world"); buf.close_tag(); - buf.push("!"); + let _ = buf.push("!"); assert_eq!(buf.finish(), ""); } #[test] fn exactly_limit() { let mut buf = HtmlWithLimit::new(12); - buf.push("Hello "); + let _ = buf.push("Hello "); buf.open_tag("em"); - buf.push("world"); + let _ = buf.push("world"); buf.close_tag(); - buf.push("!"); + let _ = buf.push("!"); assert_eq!(buf.finish(), "Hello world!"); } @@ -51,11 +51,11 @@ fn exactly_limit() { fn multiple_nested_tags() { let mut buf = HtmlWithLimit::new(60); buf.open_tag("p"); - buf.push("This is a "); + let _ = buf.push("This is a "); buf.open_tag("em"); - buf.push("paragraph"); + let _ = buf.push("paragraph"); buf.open_tag("strong"); - buf.push("!"); + let _ = buf.push("!"); buf.close_tag(); buf.close_tag(); buf.close_tag(); @@ -66,11 +66,11 @@ fn multiple_nested_tags() { fn forgot_to_close_tags() { let mut buf = HtmlWithLimit::new(60); buf.open_tag("p"); - buf.push("This is a "); + let _ = buf.push("This is a "); buf.open_tag("em"); - buf.push("paragraph"); + let _ = buf.push("paragraph"); buf.open_tag("strong"); - buf.push("!"); + let _ = buf.push("!"); assert_eq!(buf.finish(), "

    This is a paragraph!

    "); } @@ -78,10 +78,10 @@ fn forgot_to_close_tags() { fn past_the_limit() { let mut buf = HtmlWithLimit::new(20); buf.open_tag("p"); - (0..10).try_for_each(|n| { + let _ = (0..10).try_for_each(|n| { buf.open_tag("strong"); - buf.push("word#")?; - buf.push(&n.to_string())?; + let _ = buf.push("word#")?; + let _ = buf.push(&n.to_string())?; buf.close_tag(); ControlFlow::Continue(()) }); @@ -100,8 +100,8 @@ fn past_the_limit() { fn quickly_past_the_limit() { let mut buf = HtmlWithLimit::new(6); buf.open_tag("p"); - buf.push("Hello"); - buf.push(" World"); + let _ = buf.push("Hello"); + let _ = buf.push(" World"); // intentionally not closing

    before finishing assert_eq!(buf.finish(), "

    Hello

    "); } @@ -110,7 +110,7 @@ fn quickly_past_the_limit() { fn close_too_many() { let mut buf = HtmlWithLimit::new(60); buf.open_tag("p"); - buf.push("Hello"); + let _ = buf.push("Hello"); buf.close_tag(); // This call does not panic because there are valid cases // where `close_tag()` is called with no tags left to close. diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index d9e49577d3929..fc46293e7eaac 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -246,7 +246,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { match kind { CodeBlockKind::Fenced(ref lang) => { let parse_result = - LangString::parse_without_check(lang, self.check_error_codes, false); + LangString::parse_without_check(lang, self.check_error_codes); if !parse_result.rust { let added_classes = parse_result.added_classes; let lang_string = if let Some(lang) = parse_result.unknown.first() { @@ -707,21 +707,19 @@ pub(crate) fn find_testable_code( doc: &str, tests: &mut T, error_codes: ErrorCodes, - enable_per_target_ignores: bool, extra_info: Option<&ExtraInfo<'_>>, ) { - find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false) + find_codes(doc, tests, error_codes, extra_info, false) } pub(crate) fn find_codes( doc: &str, tests: &mut T, error_codes: ErrorCodes, - enable_per_target_ignores: bool, extra_info: Option<&ExtraInfo<'_>>, include_non_rust: bool, ) { - let mut parser = Parser::new(doc).into_offset_iter(); + let mut parser = Parser::new_ext(doc, main_body_opts()).into_offset_iter(); let mut prev_offset = 0; let mut nb_lines = 0; let mut register_header = None; @@ -733,12 +731,7 @@ pub(crate) fn find_codes( if lang.is_empty() { Default::default() } else { - LangString::parse( - lang, - error_codes, - enable_per_target_ignores, - extra_info, - ) + LangString::parse(lang, error_codes, extra_info) } } CodeBlockKind::Indented => Default::default(), @@ -1162,18 +1155,13 @@ impl Default for LangString { } impl LangString { - fn parse_without_check( - string: &str, - allow_error_code_check: ErrorCodes, - enable_per_target_ignores: bool, - ) -> Self { - Self::parse(string, allow_error_code_check, enable_per_target_ignores, None) + fn parse_without_check(string: &str, allow_error_code_check: ErrorCodes) -> Self { + Self::parse(string, allow_error_code_check, None) } fn parse( string: &str, allow_error_code_check: ErrorCodes, - enable_per_target_ignores: bool, extra: Option<&ExtraInfo<'_>>, ) -> Self { let allow_error_code_check = allow_error_code_check.as_bool(); @@ -1200,11 +1188,11 @@ impl LangString { data.ignore = Ignore::All; seen_rust_tags = !seen_other_tags; } - LangStringToken::LangToken(x) if x.starts_with("ignore-") => { - if enable_per_target_ignores { - ignores.push(x.trim_start_matches("ignore-").to_owned()); - seen_rust_tags = !seen_other_tags; - } + LangStringToken::LangToken(x) + if let Some(ignore) = x.strip_prefix("ignore-") => + { + ignores.push(ignore.to_owned()); + seen_rust_tags = !seen_other_tags; } LangStringToken::LangToken("rust") => { data.rust = true; @@ -1226,37 +1214,39 @@ impl LangString { data.standalone_crate = true; seen_rust_tags = !seen_other_tags || seen_rust_tags; } - LangStringToken::LangToken(x) if x.starts_with("edition") => { - data.edition = x[7..].parse::().ok(); + LangStringToken::LangToken(x) + if let Some(edition) = x.strip_prefix("edition") => + { + data.edition = edition.parse::().ok(); } LangStringToken::LangToken(x) - if x.starts_with("rust") && x[4..].parse::().is_ok() => + if let Some(edition) = x.strip_prefix("rust") + && edition.parse::().is_ok() + && let Some(extra) = extra => { - if let Some(extra) = extra { - extra.error_invalid_codeblock_attr_with_help( - format!("unknown attribute `{x}`"), - |lint| { - lint.help(format!( - "there is an attribute with a similar name: `edition{}`", - &x[4..], - )); - }, - ); - } + extra.error_invalid_codeblock_attr_with_help( + format!("unknown attribute `{x}`"), + |lint| { + lint.help(format!( + "there is an attribute with a similar name: `edition{edition}`" + )); + }, + ); } LangStringToken::LangToken(x) - if allow_error_code_check && x.starts_with('E') && x.len() == 5 => + if allow_error_code_check + && let Some(error_code) = x.strip_prefix('E') + && error_code.len() == 4 => { - if x[1..].parse::().is_ok() { + if error_code.parse::().is_ok() { data.error_codes.push(x.to_owned()); seen_rust_tags = !seen_other_tags || seen_rust_tags; } else { seen_other_tags = true; } } - LangStringToken::LangToken(x) if extra.is_some() => { - let s = x.to_lowercase(); - if let Some(help) = match s.as_str() { + LangStringToken::LangToken(x) if let Some(extra) = extra => { + if let Some(help) = match x.to_lowercase().as_str() { "compile-fail" | "compile_fail" | "compilefail" => Some( "use `compile_fail` to invert the results of this test, so that it \ passes if it cannot be compiled and fails if it can", @@ -1273,33 +1263,27 @@ impl LangString { "use `test_harness` to run functions marked `#[test]` instead of a \ potentially-implicit `main` function", ), - "standalone" | "standalone_crate" | "standalone-crate" => { - if let Some(extra) = extra - && extra.sp.at_least_rust_2024() - { - Some( - "use `standalone_crate` to compile this code block \ + "standalone" | "standalone_crate" | "standalone-crate" + if extra.sp.at_least_rust_2024() => + { + Some( + "use `standalone_crate` to compile this code block \ separately", - ) - } else { - None - } + ) } _ => None, } { - if let Some(extra) = extra { - extra.error_invalid_codeblock_attr_with_help( - format!("unknown attribute `{x}`"), - |lint| { - lint.help(help).help( - "this code block may be skipped during testing, \ + extra.error_invalid_codeblock_attr_with_help( + format!("unknown attribute `{x}`"), + |lint| { + lint.help(help).help( + "this code block may be skipped during testing, \ because unknown attributes are treated as markers for \ code samples written in other programming languages, \ unless it is also explicitly marked as `rust`", - ); - }, - ); - } + ); + }, + ); } seen_other_tags = true; data.unknown.push(x.to_owned()); @@ -1308,18 +1292,17 @@ impl LangString { seen_other_tags = true; data.unknown.push(x.to_owned()); } - LangStringToken::KeyValueAttribute(key, value) => { - if key == "class" { - data.added_classes.push(value.to_owned()); - } else if let Some(extra) = extra { - extra.error_invalid_codeblock_attr(format!( - "unsupported attribute `{key}`" - )); - } + LangStringToken::KeyValueAttribute("class", value) => { + data.added_classes.push(value.to_owned()); + } + LangStringToken::KeyValueAttribute(key, ..) if let Some(extra) = extra => { + extra + .error_invalid_codeblock_attr(format!("unsupported attribute `{key}`")); } LangStringToken::ClassAttribute(class) => { data.added_classes.push(class.to_owned()); } + _ => {} } } }; @@ -1569,7 +1552,7 @@ fn markdown_summary_with_limit( let mut buf = HtmlWithLimit::new(length_limit); let mut stopped_early = false; - p.try_for_each(|event| { + let _ = p.try_for_each(|event| { match &event { Event::Text(text) => { let r = @@ -1727,6 +1710,7 @@ pub(crate) fn markdown_links<'md, R>( md: &'md str, preprocess_link: impl Fn(MarkdownLink) -> Option, ) -> Vec { + use itertools::Itertools; if md.is_empty() { return vec![]; } @@ -1792,7 +1776,7 @@ pub(crate) fn markdown_links<'md, R>( } } } else if !c.is_ascii_whitespace() { - while let Some((j, c)) = iter.next() { + for (j, c) in iter.by_ref() { if c.is_ascii_whitespace() { return MarkdownLinkRange::Destination(i + span.start..j + span.start); } @@ -1885,7 +1869,7 @@ pub(crate) fn markdown_links<'md, R>( let mut links = Vec::new(); let mut refdefs = FxIndexMap::default(); - for (label, refdef) in event_iter.reference_definitions().iter() { + for (label, refdef) in event_iter.reference_definitions().iter().sorted_by_key(|x| x.0) { refdefs.insert(label.to_string(), (false, refdef.dest.to_string(), refdef.span.clone())); } @@ -1970,7 +1954,7 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec::new(); - find_testable_code(input, &mut lines, ErrorCodes::No, false, None); + find_testable_code(input, &mut lines, ErrorCodes::No, None); assert_eq!(lines, expect); } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 5f69e79f3ab1f..1f7201b8ca8b2 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -5,7 +5,7 @@ use std::io; use std::path::{Path, PathBuf}; use std::sync::mpsc::{Receiver, channel}; -use rinja::Template; +use askama::Template; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE}; use rustc_middle::ty::TyCtxt; @@ -28,11 +28,10 @@ use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::format::join_with_double_colon; -use crate::html::layout::{self, BufDisplay}; use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary}; use crate::html::render::write_shared::write_shared; use crate::html::url_parts_builder::UrlPartsBuilder; -use crate::html::{sources, static_files}; +use crate::html::{layout, sources, static_files}; use crate::scrape_examples::AllCallLocations; use crate::{DOC_RUST_LANG_ORG_VERSION, try_err}; @@ -250,9 +249,7 @@ impl<'tcx> Context<'tcx> { layout::render( &self.shared.layout, &page, - BufDisplay(|buf: &mut String| { - print_sidebar(self, it, buf); - }), + fmt::from_fn(|f| print_sidebar(self, it, f)), content, &self.shared.style_files, ) @@ -521,23 +518,23 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { // Crawl the crate attributes looking for attributes which control how we're // going to emit HTML for attr in krate.module.attrs.lists(sym::doc) { - match (attr.name_or_empty(), attr.value_str()) { - (sym::html_favicon_url, Some(s)) => { + match (attr.name(), attr.value_str()) { + (Some(sym::html_favicon_url), Some(s)) => { layout.favicon = s.to_string(); } - (sym::html_logo_url, Some(s)) => { + (Some(sym::html_logo_url), Some(s)) => { layout.logo = s.to_string(); } - (sym::html_playground_url, Some(s)) => { + (Some(sym::html_playground_url), Some(s)) => { playground = Some(markdown::Playground { crate_name: Some(krate.name(tcx)), url: s.to_string(), }); } - (sym::issue_tracker_base_url, Some(s)) => { + (Some(sym::issue_tracker_base_url), Some(s)) => { issue_tracker_base_url = Some(s.to_string()); } - (sym::html_no_source, None) if attr.is_word() => { + (Some(sym::html_no_source), None) if attr.is_word() => { include_sources = false; } _ => {} @@ -650,15 +647,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { bar.render_into(&mut sidebar).unwrap(); - let v = layout::render( - &shared.layout, - &page, - sidebar, - BufDisplay(|buf: &mut String| { - all.print(buf); - }), - &shared.style_files, - ); + let v = layout::render(&shared.layout, &page, sidebar, all.print(), &shared.style_files); shared.fs.write(final_file, v)?; // if to avoid writing help, settings files to doc root unless we're on the final invocation diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index bd4af359404a0..06cb9269cc87f 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -13,6 +13,9 @@ //! is cloned per-thread and contains information about what is currently being //! rendered. //! +//! The main entry point to the rendering system is the implementation of +//! `FormatRenderer` on `Context`. +//! //! 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 @@ -37,17 +40,18 @@ mod span_map; mod type_layout; mod write_shared; +use std::borrow::Cow; use std::collections::VecDeque; use std::fmt::{self, Display as _, Write}; use std::iter::Peekable; use std::path::PathBuf; use std::{fs, str}; -use rinja::Template; -use rustc_attr_parsing::{ +use askama::Template; +use itertools::Either; +use rustc_attr_data_structures::{ ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince, }; -use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::Mutability; use rustc_hir::def_id::{DefId, DefIdSet}; @@ -82,7 +86,7 @@ use crate::html::{highlight, sources}; use crate::scrape_examples::{CallData, CallLocation}; use crate::{DOC_RUST_LANG_ORG_VERSION, try_none}; -pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { +pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display { fmt::from_fn(move |f| { if !v.ends_with('/') && !v.is_empty() { write!(f, "{v}/") } else { f.write_str(v) } }) @@ -90,16 +94,29 @@ pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { /// Specifies whether rendering directly implemented trait items or ones from a certain Deref /// impl. -#[derive(Copy, Clone)] -pub(crate) enum AssocItemRender<'a> { +#[derive(Copy, Clone, Debug)] +enum AssocItemRender<'a> { All, DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool }, } +impl AssocItemRender<'_> { + fn render_mode(&self) -> RenderMode { + match self { + Self::All => RenderMode::Normal, + &Self::DerefFor { deref_mut_, .. } => RenderMode::ForDeref { mut_: deref_mut_ }, + } + } + + fn class(&self) -> Option<&'static str> { + if let Self::DerefFor { .. } = self { Some("impl-items") } else { None } + } +} + /// For different handling of associated items from the Deref target of a type rather than the type /// itself. #[derive(Copy, Clone, PartialEq)] -pub(crate) enum RenderMode { +enum RenderMode { Normal, ForDeref { mut_: bool }, } @@ -127,7 +144,7 @@ pub(crate) struct IndexItem { /// A type used for the search index. #[derive(Debug, Eq, PartialEq)] -pub(crate) struct RenderType { +struct RenderType { id: Option, generics: Option>, bindings: Option)>>, @@ -138,7 +155,7 @@ impl RenderType { // The contents of the lists are always integers in self-terminating hex // form, handled by `RenderTypeId::write_to_string`, so no commas are // needed to separate the items. - pub fn write_to_string(&self, string: &mut String) { + fn write_to_string(&self, string: &mut String) { fn write_optional_id(id: Option, string: &mut String) { // 0 is a sentinel, everything else is one-indexed match id { @@ -178,7 +195,7 @@ impl RenderType { } #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub(crate) enum RenderTypeId { +enum RenderTypeId { DefId(DefId), Primitive(clean::PrimitiveType), AssociatedType(Symbol), @@ -187,7 +204,7 @@ pub(crate) enum RenderTypeId { } impl RenderTypeId { - pub fn write_to_string(&self, string: &mut String) { + fn write_to_string(&self, string: &mut String) { let id: i32 = match &self { // 0 is a sentinel, everything else is one-indexed // concrete type @@ -206,11 +223,11 @@ pub(crate) struct IndexItemFunctionType { inputs: Vec, output: Vec, where_clause: Vec>, - param_names: Vec, + param_names: Vec>, } impl IndexItemFunctionType { - pub fn write_to_string<'a>( + fn write_to_string<'a>( &'a self, string: &mut String, backref_queue: &mut VecDeque<&'a IndexItemFunctionType>, @@ -310,7 +327,7 @@ impl ItemEntry { } impl ItemEntry { - pub(crate) fn print(&self) -> impl fmt::Display + '_ { + fn print(&self) -> impl fmt::Display { fmt::from_fn(move |f| write!(f, "{}", self.url, Escape(&self.name))) } } @@ -437,44 +454,49 @@ impl AllTypes { sections } - fn print(&self, f: &mut String) { - fn print_entries(f: &mut String, e: &FxIndexSet, kind: ItemSection) { - if !e.is_empty() { + fn print(&self) -> impl fmt::Display { + fn print_entries(e: &FxIndexSet, kind: ItemSection) -> impl fmt::Display { + fmt::from_fn(move |f| { + if e.is_empty() { + return Ok(()); + } + let mut e: Vec<&ItemEntry> = e.iter().collect(); e.sort(); - write_str( + write!( f, - format_args!( - "

    {title}

      ", - id = kind.id(), - title = kind.name(), - ), - ); + "

      {title}

        ", + id = kind.id(), + title = kind.name(), + )?; for s in e.iter() { - write_str(f, format_args!("
      • {}
      • ", s.print())); + write!(f, "
      • {}
      • ", s.print())?; } - f.push_str("
      "); - } + f.write_str("
    ") + }) } - f.push_str("

    List of all items

    "); - // Note: print_entries does not escape the title, because we know the current set of titles - // doesn't require escaping. - print_entries(f, &self.structs, ItemSection::Structs); - print_entries(f, &self.enums, ItemSection::Enums); - print_entries(f, &self.unions, ItemSection::Unions); - print_entries(f, &self.primitives, ItemSection::PrimitiveTypes); - print_entries(f, &self.traits, ItemSection::Traits); - print_entries(f, &self.macros, ItemSection::Macros); - print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros); - print_entries(f, &self.derive_macros, ItemSection::DeriveMacros); - print_entries(f, &self.functions, ItemSection::Functions); - print_entries(f, &self.type_aliases, ItemSection::TypeAliases); - print_entries(f, &self.trait_aliases, ItemSection::TraitAliases); - print_entries(f, &self.statics, ItemSection::Statics); - print_entries(f, &self.constants, ItemSection::Constants); + fmt::from_fn(|f| { + f.write_str("

    List of all items

    ")?; + // Note: print_entries does not escape the title, because we know the current set of titles + // doesn't require escaping. + print_entries(&self.structs, ItemSection::Structs).fmt(f)?; + print_entries(&self.enums, ItemSection::Enums).fmt(f)?; + print_entries(&self.unions, ItemSection::Unions).fmt(f)?; + print_entries(&self.primitives, ItemSection::PrimitiveTypes).fmt(f)?; + print_entries(&self.traits, ItemSection::Traits).fmt(f)?; + print_entries(&self.macros, ItemSection::Macros).fmt(f)?; + print_entries(&self.attribute_macros, ItemSection::AttributeMacros).fmt(f)?; + print_entries(&self.derive_macros, ItemSection::DeriveMacros).fmt(f)?; + print_entries(&self.functions, ItemSection::Functions).fmt(f)?; + print_entries(&self.type_aliases, ItemSection::TypeAliases).fmt(f)?; + print_entries(&self.trait_aliases, ItemSection::TraitAliases).fmt(f)?; + print_entries(&self.statics, ItemSection::Statics).fmt(f)?; + print_entries(&self.constants, ItemSection::Constants).fmt(f)?; + Ok(()) + }) } } @@ -505,18 +527,18 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String { ) } -fn document<'a, 'cx: 'a>( - cx: &'a Context<'cx>, - item: &'a clean::Item, - parent: Option<&'a clean::Item>, +fn document( + cx: &Context<'_>, + item: &clean::Item, + parent: Option<&clean::Item>, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { if let Some(ref name) = item.name { info!("Documenting {name}"); } fmt::from_fn(move |f| { - document_item_info(cx, item, parent).render_into(f).unwrap(); + document_item_info(cx, item, parent).render_into(f)?; if parent.is_none() { write!(f, "{}", document_full_collapsible(item, cx, heading_offset)) } else { @@ -526,12 +548,12 @@ fn document<'a, 'cx: 'a>( } /// Render md_text as markdown. -fn render_markdown<'a, 'cx: 'a>( - cx: &'a Context<'cx>, - md_text: &'a str, +fn render_markdown( + cx: &Context<'_>, + md_text: &str, links: Vec, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { fmt::from_fn(move |f| { write!( f, @@ -552,15 +574,15 @@ fn render_markdown<'a, 'cx: 'a>( /// 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<'a, 'cx: 'a>( - item: &'a clean::Item, - cx: &'a Context<'cx>, - link: AssocItemLink<'a>, - parent: &'a clean::Item, +fn document_short( + item: &clean::Item, + cx: &Context<'_>, + link: AssocItemLink<'_>, + parent: &clean::Item, show_def_docs: bool, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { fmt::from_fn(move |f| { - document_item_info(cx, item, Some(parent)).render_into(f).unwrap(); + document_item_info(cx, item, Some(parent)).render_into(f)?; if !show_def_docs { return Ok(()); } @@ -595,28 +617,28 @@ fn document_short<'a, 'cx: 'a>( }) } -fn document_full_collapsible<'a, 'cx: 'a>( - item: &'a clean::Item, - cx: &'a Context<'cx>, +fn document_full_collapsible( + item: &clean::Item, + cx: &Context<'_>, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { document_full_inner(item, cx, true, heading_offset) } -fn document_full<'a, 'cx: 'a>( - item: &'a clean::Item, - cx: &'a Context<'cx>, +fn document_full( + item: &clean::Item, + cx: &Context<'_>, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { document_full_inner(item, cx, false, heading_offset) } -fn document_full_inner<'a, 'cx: 'a>( - item: &'a clean::Item, - cx: &'a Context<'cx>, +fn document_full_inner( + item: &clean::Item, + cx: &Context<'_>, is_collapsible: bool, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { fmt::from_fn(move |f| { if let Some(s) = item.opt_doc_value() { debug!("Doc block: =====\n{s}\n====="); @@ -639,7 +661,7 @@ fn document_full_inner<'a, 'cx: 'a>( }; if let clean::ItemKind::FunctionItem(..) | clean::ItemKind::MethodItem(..) = kind { - render_call_locations(f, cx, item); + render_call_locations(f, cx, item)?; } Ok(()) }) @@ -761,7 +783,7 @@ fn short_item_info( // Render the list of items inside one of the sections "Trait Implementations", // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages). -pub(crate) fn render_impls( +fn render_impls( cx: &Context<'_>, mut w: impl Write, impls: &[&Impl], @@ -797,11 +819,11 @@ pub(crate) fn render_impls( } /// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item. -fn assoc_href_attr<'a, 'tcx>( +fn assoc_href_attr( it: &clean::Item, - link: AssocItemLink<'a>, - cx: &Context<'tcx>, -) -> Option> { + link: AssocItemLink<'_>, + cx: &Context<'_>, +) -> Option { let name = it.name.unwrap(); let item_type = it.type_(); @@ -812,7 +834,7 @@ fn assoc_href_attr<'a, 'tcx>( } let href = match link { - AssocItemLink::Anchor(Some(ref id)) => Href::AnchorId(id), + AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id), AssocItemLink::Anchor(None) => Href::Anchor(item_type), AssocItemLink::GotoSource(did, provided_methods) => { // We're creating a link from the implementation of an associated item to its @@ -877,15 +899,15 @@ enum AssocConstValue<'a> { None, } -fn assoc_const<'a, 'tcx>( - it: &'a clean::Item, - generics: &'a clean::Generics, - ty: &'a clean::Type, - value: AssocConstValue<'a>, - link: AssocItemLink<'a>, +fn assoc_const( + it: &clean::Item, + generics: &clean::Generics, + ty: &clean::Type, + value: AssocConstValue<'_>, + link: AssocItemLink<'_>, indent: usize, - cx: &'a Context<'tcx>, -) -> impl fmt::Display + 'a + Captures<'tcx> { + cx: &Context<'_>, +) -> impl fmt::Display { let tcx = cx.tcx(); fmt::from_fn(move |w| { write!( @@ -917,15 +939,15 @@ fn assoc_const<'a, 'tcx>( }) } -fn assoc_type<'a, 'tcx>( - it: &'a clean::Item, - generics: &'a clean::Generics, - bounds: &'a [clean::GenericBound], - default: Option<&'a clean::Type>, - link: AssocItemLink<'a>, +fn assoc_type( + it: &clean::Item, + generics: &clean::Generics, + bounds: &[clean::GenericBound], + default: Option<&clean::Type>, + link: AssocItemLink<'_>, indent: usize, - cx: &'a Context<'tcx>, -) -> impl fmt::Display + 'a + Captures<'tcx> { + cx: &Context<'_>, +) -> impl fmt::Display { fmt::from_fn(move |w| { write!( w, @@ -947,15 +969,15 @@ fn assoc_type<'a, 'tcx>( }) } -fn assoc_method<'a, 'tcx>( - meth: &'a clean::Item, - g: &'a clean::Generics, - d: &'a clean::FnDecl, - link: AssocItemLink<'a>, +fn assoc_method( + meth: &clean::Item, + g: &clean::Generics, + d: &clean::FnDecl, + link: AssocItemLink<'_>, parent: ItemType, - cx: &'a Context<'tcx>, + cx: &Context<'_>, render_mode: RenderMode, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl fmt::Display { let tcx = cx.tcx(); let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item"); let name = meth.name.as_ref().unwrap(); @@ -1031,7 +1053,7 @@ fn render_stability_since_raw_with_extra( stable_version: Option, const_stability: Option, extra_class: &str, -) -> Option { +) -> Option { let mut title = String::new(); let mut stability = String::new(); @@ -1102,13 +1124,13 @@ fn render_stability_since_raw( render_stability_since_raw_with_extra(ver, const_stability, "") } -fn render_assoc_item<'a, 'tcx>( - item: &'a clean::Item, - link: AssocItemLink<'a>, +fn render_assoc_item( + item: &clean::Item, + link: AssocItemLink<'_>, parent: ItemType, - cx: &'a Context<'tcx>, + cx: &Context<'_>, render_mode: RenderMode, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl fmt::Display { fmt::from_fn(move |f| match &item.kind { clean::StrippedItem(..) => Ok(()), clean::RequiredMethodItem(m) | clean::MethodItem(m, _) => { @@ -1144,7 +1166,7 @@ fn render_assoc_item<'a, 'tcx>( cx, ) .fmt(f), - clean::RequiredAssocTypeItem(ref generics, ref bounds) => assoc_type( + clean::RequiredAssocTypeItem(generics, bounds) => assoc_type( item, generics, bounds, @@ -1154,7 +1176,7 @@ fn render_assoc_item<'a, 'tcx>( cx, ) .fmt(f), - clean::AssocTypeItem(ref ty, ref bounds) => assoc_type( + clean::AssocTypeItem(ty, bounds) => assoc_type( item, &ty.generics, bounds, @@ -1170,11 +1192,7 @@ fn render_assoc_item<'a, 'tcx>( // When an attribute is rendered inside a `
    ` tag, it is formatted using
     // a whitespace prefix and newline.
    -fn render_attributes_in_pre<'a, 'tcx: 'a>(
    -    it: &'a clean::Item,
    -    prefix: &'a str,
    -    cx: &'a Context<'tcx>,
    -) -> impl fmt::Display + Captures<'a> + Captures<'tcx> {
    +fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display {
         fmt::from_fn(move |f| {
             for a in it.attributes(cx.tcx(), cx.cache(), false) {
                 writeln!(f, "{prefix}{a}")?;
    @@ -1206,12 +1224,12 @@ impl<'a> AssocItemLink<'a> {
         }
     }
     
    -pub fn write_section_heading<'a>(
    -    title: &'a str,
    -    id: &'a str,
    -    extra_class: Option<&'a str>,
    -    extra: impl fmt::Display + 'a,
    -) -> impl fmt::Display + 'a {
    +fn write_section_heading(
    +    title: impl fmt::Display,
    +    id: &str,
    +    extra_class: Option<&str>,
    +    extra: impl fmt::Display,
    +) -> impl fmt::Display {
         fmt::from_fn(move |w| {
             let (extra_class, whitespace) = match extra_class {
                 Some(extra) => (extra, " "),
    @@ -1227,11 +1245,11 @@ pub fn write_section_heading<'a>(
         })
     }
     
    -fn write_impl_section_heading<'a>(title: &'a str, id: &'a str) -> impl fmt::Display + 'a {
    +fn write_impl_section_heading(title: impl fmt::Display, id: &str) -> impl fmt::Display {
         write_section_heading(title, id, None, "")
     }
     
    -pub(crate) fn render_all_impls(
    +fn render_all_impls(
         mut w: impl Write,
         cx: &Context<'_>,
         containing_item: &clean::Item,
    @@ -1276,12 +1294,12 @@ pub(crate) fn render_all_impls(
         }
     }
     
    -fn render_assoc_items<'a, 'cx: 'a>(
    -    cx: &'a Context<'cx>,
    -    containing_item: &'a clean::Item,
    +fn render_assoc_items(
    +    cx: &Context<'_>,
    +    containing_item: &clean::Item,
         it: DefId,
    -    what: AssocItemRender<'a>,
    -) -> impl fmt::Display + 'a + Captures<'cx> {
    +    what: AssocItemRender<'_>,
    +) -> impl fmt::Display {
         fmt::from_fn(move |f| {
             let mut derefs = DefIdSet::default();
             derefs.insert(it);
    @@ -1301,44 +1319,54 @@ fn render_assoc_items_inner(
         info!("Documenting associated items of {:?}", containing_item.name);
         let cache = &cx.shared.cache;
         let Some(v) = cache.impls.get(&it) else { return };
    -    let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
    +    let (mut non_trait, traits): (Vec<_>, _) =
    +        v.iter().partition(|i| i.inner_impl().trait_.is_none());
         if !non_trait.is_empty() {
    -        let mut close_tags = >::with_capacity(1);
    -        let mut tmp_buf = String::new();
    -        let (render_mode, id, class_html) = match what {
    -            AssocItemRender::All => {
    -                write_str(
    -                    &mut tmp_buf,
    -                    format_args!(
    -                        "{}",
    -                        write_impl_section_heading("Implementations", "implementations")
    -                    ),
    -                );
    -                (RenderMode::Normal, "implementations-list".to_owned(), "")
    -            }
    -            AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
    +        let render_mode = what.render_mode();
    +        let class_html = what
    +            .class()
    +            .map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#)))
    +            .maybe_display();
    +        let (section_heading, id) = match what {
    +            AssocItemRender::All => (
    +                Either::Left(write_impl_section_heading("Implementations", "implementations")),
    +                Cow::Borrowed("implementations-list"),
    +            ),
    +            AssocItemRender::DerefFor { trait_, type_, .. } => {
                     let id =
                         cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
    +                // the `impls.get` above only looks at the outermost type,
    +                // and the Deref impl may only be implemented for certain
    +                // values of generic parameters.
    +                // for example, if an item impls `Deref<[u8]>`,
    +                // we should not show methods from `[MaybeUninit]`.
    +                // this `retain` filters out any instances where
    +                // the types do not line up perfectly.
    +                non_trait.retain(|impl_| {
    +                    type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
    +                });
                     let derived_id = cx.derive_id(&id);
    -                close_tags.push("
    "); - write_str( - &mut tmp_buf, - format_args!( - "
    {}", - write_impl_section_heading( - &format!( - "Methods from {trait_}<Target = {type_}>", - trait_ = trait_.print(cx), - type_ = type_.print(cx), - ), - &id, - ) - ), - ); if let Some(def_id) = type_.def_id(cx.cache()) { - cx.deref_id_map.borrow_mut().insert(def_id, id); + cx.deref_id_map.borrow_mut().insert(def_id, id.clone()); } - (RenderMode::ForDeref { mut_: deref_mut_ }, derived_id, r#" class="impl-items""#) + ( + Either::Right(fmt::from_fn(move |f| { + write!( + f, + "
    {}", + write_impl_section_heading( + fmt::from_fn(|f| write!( + f, + "Methods from {trait_}<Target = {type_}>", + trait_ = trait_.print(cx), + type_ = type_.print(cx), + )), + &id, + ) + ) + })), + Cow::Owned(derived_id), + ) } }; let mut impls_buf = String::new(); @@ -1366,10 +1394,14 @@ fn render_assoc_items_inner( ); } if !impls_buf.is_empty() { - write!(w, "{tmp_buf}
    {impls_buf}
    ").unwrap(); - for tag in close_tags.into_iter().rev() { - w.write_str(tag).unwrap(); - } + write!( + w, + "{section_heading}
    {impls_buf}
    {}", + matches!(what, AssocItemRender::DerefFor { .. }) + .then_some("
    ") + .maybe_display(), + ) + .unwrap(); } } @@ -1397,6 +1429,7 @@ fn render_assoc_items_inner( } } +/// `derefs` is the set of all deref targets that have already been handled. fn render_deref_methods( mut w: impl Write, cx: &Context<'_>, @@ -1466,12 +1499,7 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> } } -pub(crate) fn notable_traits_button<'a, 'tcx>( - ty: &'a clean::Type, - cx: &'a Context<'tcx>, -) -> Option> { - let mut has_notable_trait = false; - +fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option { if ty.is_unit() { // Very common fast path. return None; @@ -1489,27 +1517,19 @@ pub(crate) fn notable_traits_button<'a, 'tcx>( return None; } - if let Some(impls) = cx.cache().impls.get(&did) { - for i in impls { - let impl_ = i.inner_impl(); - if impl_.polarity != ty::ImplPolarity::Positive { - continue; - } - - if !ty.is_doc_subtype_of(&impl_.for_, cx.cache()) { + let impls = cx.cache().impls.get(&did)?; + let has_notable_trait = impls + .iter() + .map(Impl::inner_impl) + .filter(|impl_| { + impl_.polarity == ty::ImplPolarity::Positive // Two different types might have the same did, // without actually being the same. - continue; - } - if let Some(trait_) = &impl_.trait_ { - let trait_did = trait_.def_id(); - - if cx.cache().traits.get(&trait_did).is_some_and(|t| t.is_notable_trait(cx.tcx())) { - has_notable_trait = true; - } - } - } - } + && ty.is_doc_subtype_of(&impl_.for_, cx.cache()) + }) + .filter_map(|impl_| impl_.trait_.as_ref()) + .filter_map(|trait_| cx.cache().traits.get(&trait_.def_id())) + .any(|t| t.is_notable_trait(cx.tcx())); has_notable_trait.then(|| { cx.types_with_notable_traits.borrow_mut().insert(ty.clone()); @@ -1591,10 +1611,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { (format!("{:#}", ty.print(cx)), out) } -pub(crate) fn notable_traits_json<'a>( - tys: impl Iterator, - cx: &Context<'_>, -) -> String { +fn notable_traits_json<'a>(tys: impl Iterator, cx: &Context<'_>) -> String { let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect(); mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2)); struct NotableTraitsMap(Vec<(String, String)>); @@ -1623,16 +1640,16 @@ struct ImplRenderingParameters { toggle_open_by_default: bool, } -fn render_impl<'a, 'tcx>( - cx: &'a Context<'tcx>, - i: &'a Impl, - parent: &'a clean::Item, - link: AssocItemLink<'a>, +fn render_impl( + cx: &Context<'_>, + i: &Impl, + parent: &clean::Item, + link: AssocItemLink<'_>, render_mode: RenderMode, use_absolute: Option, - aliases: &'a [String], + aliases: &[String], rendering_params: ImplRenderingParameters, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl fmt::Display { fmt::from_fn(move |w| { let cache = &cx.shared.cache; let traits = &cache.traits; @@ -1645,8 +1662,8 @@ fn render_impl<'a, 'tcx>( // `containing_item` is used for rendering stability info. If the parent is a trait impl, // `containing_item` will the grandparent, since trait impls can't have stability attached. fn doc_impl_item( - boring: &mut String, - interesting: &mut String, + boring: impl fmt::Write, + interesting: impl fmt::Write, cx: &Context<'_>, item: &clean::Item, parent: &clean::Item, @@ -1655,7 +1672,7 @@ fn render_impl<'a, 'tcx>( is_default_item: bool, trait_: Option<&clean::Trait>, rendering_params: ImplRenderingParameters, - ) { + ) -> fmt::Result { let item_type = item.type_(); let name = item.name.as_ref().unwrap(); @@ -1730,15 +1747,16 @@ fn render_impl<'a, 'tcx>( ); } } - let w = if short_documented && trait_.is_some() { interesting } else { boring }; + let mut w = if short_documented && trait_.is_some() { + Either::Left(interesting) + } else { + Either::Right(boring) + }; let toggled = !doc_buffer.is_empty(); if toggled { let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; - write_str( - w, - format_args!("
    "), - ); + write!(w, "
    ")?; } match &item.kind { clean::MethodItem(..) | clean::RequiredMethodItem(_) => { @@ -1753,172 +1771,151 @@ fn render_impl<'a, 'tcx>( .find(|item| item.name.map(|n| n == *name).unwrap_or(false)) }) .map(|item| format!("{}.{name}", item.type_())); - write_str( + write!( w, - format_args!( - "
    \ + "
    \ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode) + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("§")); + write!(w, "§")?; } - write_str( + write!( w, - format_args!( - "

    {}

    ", - render_assoc_item( - item, - link.anchor(source_id.as_ref().unwrap_or(&id)), - ItemType::Impl, - cx, - render_mode, - ), + "

    {}

    ", + render_assoc_item( + item, + link.anchor(source_id.as_ref().unwrap_or(&id)), + ItemType::Impl, + cx, + render_mode, ), - ); + )?; } } - clean::RequiredAssocConstItem(ref generics, ref ty) => { + clean::RequiredAssocConstItem(generics, ty) => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); - write_str( + write!( w, - format_args!( - "
    \ + "
    \ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode) + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("§")); + write!(w, "§")?; } - write_str( + write!( w, - format_args!( - "

    {}

    ", - assoc_const( - item, - generics, - ty, - AssocConstValue::None, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ) + "

    {}

    ", + assoc_const( + item, + generics, + ty, + AssocConstValue::None, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, ), - ); + )?; } clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); - write_str( + write!( w, - format_args!( - "
    \ + "
    \ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode), + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("§")); + write!(w, "§")?; } - write_str( + write!( w, - format_args!( - "

    {}

    ", - assoc_const( - item, - &ci.generics, - &ci.type_, - match item.kind { - clean::ProvidedAssocConstItem(_) => - AssocConstValue::TraitDefault(&ci.kind), - clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind), - _ => unreachable!(), - }, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ) + "

    {}

    ", + assoc_const( + item, + &ci.generics, + &ci.type_, + match item.kind { + clean::ProvidedAssocConstItem(_) => + AssocConstValue::TraitDefault(&ci.kind), + clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind), + _ => unreachable!(), + }, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, ), - ); + )?; } - clean::RequiredAssocTypeItem(ref generics, ref bounds) => { + clean::RequiredAssocTypeItem(generics, bounds) => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); - write_str( + write!( w, - format_args!( - "
    \ + "
    \ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode), + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("§")); + write!(w, "§")?; } - write_str( + write!( w, - format_args!( - "

    {}

    ", - assoc_type( - item, - generics, - bounds, - None, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ) + "

    {}

    ", + assoc_type( + item, + generics, + bounds, + None, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, ), - ); + )?; } clean::AssocTypeItem(tydef, _bounds) => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); - write_str( + write!( w, - format_args!( - "
    \ + "
    \ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode), + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("§")); + write!(w, "§")?; } - write_str( + write!( w, - format_args!( - "

    {}

    ", - assoc_type( - item, - &tydef.generics, - &[], // intentionally leaving out bounds - Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)), - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ) + "

    {}

    ", + assoc_type( + item, + &tydef.generics, + &[], // intentionally leaving out bounds + Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)), + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, ), - ); + )?; } - clean::StrippedItem(..) => return, + clean::StrippedItem(..) => return Ok(()), _ => panic!("can't make docs for trait item with name {:?}", item.name), } - w.push_str(&info_buffer); + w.write_str(&info_buffer)?; if toggled { - w.push_str("
    "); - w.push_str(&doc_buffer); - w.push_str("
    "); + write!(w, "
    {doc_buffer}
    ")?; } + Ok(()) } let mut impl_items = String::new(); @@ -1961,7 +1958,7 @@ fn render_impl<'a, 'tcx>( false, trait_, rendering_params, - ); + )?; } _ => {} } @@ -1979,7 +1976,7 @@ fn render_impl<'a, 'tcx>( false, trait_, rendering_params, - ); + )?; } for method in methods { doc_impl_item( @@ -1993,20 +1990,20 @@ fn render_impl<'a, 'tcx>( false, trait_, rendering_params, - ); + )?; } } fn render_default_items( - boring: &mut String, - interesting: &mut String, + mut boring: impl fmt::Write, + mut interesting: impl fmt::Write, cx: &Context<'_>, t: &clean::Trait, i: &clean::Impl, parent: &clean::Item, render_mode: RenderMode, rendering_params: ImplRenderingParameters, - ) { + ) -> fmt::Result { for trait_item in &t.items { // Skip over any default trait items that are impossible to reference // (e.g. if it has a `Self: Sized` bound on an unsized type). @@ -2026,8 +2023,8 @@ fn render_impl<'a, 'tcx>( let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods); doc_impl_item( - boring, - interesting, + &mut boring, + &mut interesting, cx, trait_item, parent, @@ -2036,8 +2033,9 @@ fn render_impl<'a, 'tcx>( true, Some(t), rendering_params, - ); + )?; } + Ok(()) } // If we've implemented a trait, then also emit documentation for all @@ -2057,7 +2055,7 @@ fn render_impl<'a, 'tcx>( &i.impl_item, render_mode, rendering_params, - ); + )?; } } if render_mode == RenderMode::Normal { @@ -2088,6 +2086,7 @@ fn render_impl<'a, 'tcx>( .split_summary_and_content() }) .unwrap_or((None, None)); + write!( w, "{}", @@ -2099,24 +2098,19 @@ fn render_impl<'a, 'tcx>( use_absolute, aliases, before_dox.as_deref(), + trait_.is_none() && impl_.items.is_empty(), ) )?; if toggled { w.write_str("")?; } - if before_dox.is_some() { - if trait_.is_none() && impl_.items.is_empty() { - w.write_str( - "
    \ -
    This impl block contains no items.
    \ -
    ", - )?; - } - if let Some(after_dox) = after_dox { - write!(w, "
    {after_dox}
    ")?; - } + if before_dox.is_some() + && let Some(after_dox) = after_dox + { + write!(w, "
    {after_dox}
    ")?; } + if !default_impl_items.is_empty() || !impl_items.is_empty() { w.write_str("
    ")?; close_tags.push("
    "); @@ -2135,11 +2129,11 @@ fn render_impl<'a, 'tcx>( // Render the items that appear on the right side of methods, impls, and // associated types. For example "1.0.0 (const: 1.39.0) · source". -fn render_rightside<'a, 'tcx>( - cx: &'a Context<'tcx>, - item: &'a clean::Item, +fn render_rightside( + cx: &Context<'_>, + item: &clean::Item, render_mode: RenderMode, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl fmt::Display { let tcx = cx.tcx(); fmt::from_fn(move |w| { @@ -2174,17 +2168,18 @@ fn render_rightside<'a, 'tcx>( }) } -pub(crate) fn render_impl_summary<'a, 'tcx>( - cx: &'a Context<'tcx>, - i: &'a Impl, - parent: &'a clean::Item, +fn render_impl_summary( + cx: &Context<'_>, + i: &Impl, + parent: &clean::Item, show_def_docs: bool, use_absolute: Option, // 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: &'a [String], - doc: Option<&'a str>, -) -> impl fmt::Display + 'a + Captures<'tcx> { + aliases: &[String], + doc: Option<&str>, + impl_is_empty: bool, +) -> impl fmt::Display { fmt::from_fn(move |w| { let inner_impl = i.inner_impl(); let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id)); @@ -2239,6 +2234,13 @@ pub(crate) fn render_impl_summary<'a, 'tcx>( } if let Some(doc) = doc { + if impl_is_empty { + w.write_str( + "
    \ +
    This impl block contains no items.
    \ +
    ", + )?; + } write!(w, "
    {doc}
    ")?; } @@ -2582,11 +2584,15 @@ const MAX_FULL_EXAMPLES: usize = 5; const NUM_VISIBLE_LINES: usize = 10; /// Generates the HTML for example call locations generated via the --scrape-examples flag. -fn render_call_locations(mut w: W, cx: &Context<'_>, item: &clean::Item) { +fn render_call_locations( + mut w: W, + cx: &Context<'_>, + item: &clean::Item, +) -> fmt::Result { let tcx = cx.tcx(); let def_id = item.item_id.expect_def_id(); let key = tcx.def_path_hash(def_id); - let Some(call_locations) = cx.shared.call_locations.get(&key) else { return }; + let Some(call_locations) = cx.shared.call_locations.get(&key) else { return Ok(()) }; // Generate a unique ID so users can link to this section for a given method let id = cx.derive_id("scraped-examples"); @@ -2600,8 +2606,7 @@ fn render_call_locations(mut w: W, cx: &Context<'_>, item: &clean ", root_path = cx.root_path(), id = id - ) - .unwrap(); + )?; // Create a URL to a particular location in a reverse-dependency's source file let link_to_loc = |call_data: &CallData, loc: &CallLocation| -> (String, String) { @@ -2703,7 +2708,8 @@ fn render_call_locations(mut w: W, cx: &Context<'_>, item: &clean title: init_title, locations: locations_encoded, }), - ); + ) + .unwrap(); true }; @@ -2759,8 +2765,7 @@ fn render_call_locations(mut w: W, cx: &Context<'_>, item: &clean
    Hide additional examples
    \
    \
    " - ) - .unwrap(); + )?; // Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could // make the page arbitrarily huge! @@ -2772,9 +2777,8 @@ fn render_call_locations(mut w: W, cx: &Context<'_>, item: &clean if it.peek().is_some() { w.write_str( r#"").unwrap(); + })?; + w.write_str("
    ")?; } - w.write_str("
    ").unwrap(); + w.write_str("")?; } - w.write_str("").unwrap(); + w.write_str("") } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index b647b2aad75a7..39a631b637bd4 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1,10 +1,9 @@ use std::cmp::Ordering; -use std::fmt; -use std::fmt::{Display, Write as _}; +use std::fmt::{self, Display, Write as _}; +use std::iter; -use rinja::Template; +use askama::Template; use rustc_abi::VariantIdx; -use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_hir as hir; use rustc_hir::def::CtorKind; @@ -12,7 +11,7 @@ use rustc_hir::def_id::DefId; use rustc_index::IndexVec; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::hygiene::MacroKind; -use rustc_span::symbol::{Symbol, kw, sym}; +use rustc_span::symbol::{Symbol, sym}; use tracing::{debug, info}; use super::type_layout::document_type_layout; @@ -38,7 +37,7 @@ use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; use crate::html::render::{document_full, document_item_info}; use crate::html::url_parts_builder::UrlPartsBuilder; -/// Generates a Rinja template struct for rendering items with common methods. +/// Generates an Askama template struct for rendering items with common methods. /// /// Usage: /// ```ignore (illustrative) @@ -92,44 +91,32 @@ macro_rules! item_template { macro_rules! item_template_methods { () => {}; (document $($rest:tt)*) => { - fn document<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let (item, cx) = self.item_and_cx(); - let v = document(cx, item, None, HeadingOffset::H2); - write!(f, "{v}") - }) + fn document(&self) -> impl fmt::Display { + let (item, cx) = self.item_and_cx(); + document(cx, item, None, HeadingOffset::H2) } item_template_methods!($($rest)*); }; (document_type_layout $($rest:tt)*) => { - fn document_type_layout<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let (item, cx) = self.item_and_cx(); - let def_id = item.item_id.expect_def_id(); - let v = document_type_layout(cx, def_id); - write!(f, "{v}") - }) + fn document_type_layout(&self) -> impl fmt::Display { + let (item, cx) = self.item_and_cx(); + let def_id = item.item_id.expect_def_id(); + document_type_layout(cx, def_id) } item_template_methods!($($rest)*); }; (render_attributes_in_pre $($rest:tt)*) => { - fn render_attributes_in_pre<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let (item, cx) = self.item_and_cx(); - let v = render_attributes_in_pre(item, "", cx); - write!(f, "{v}") - }) + fn render_attributes_in_pre(&self) -> impl fmt::Display { + let (item, cx) = self.item_and_cx(); + render_attributes_in_pre(item, "", cx) } item_template_methods!($($rest)*); }; (render_assoc_items $($rest:tt)*) => { - fn render_assoc_items<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let (item, cx) = self.item_and_cx(); - let def_id = item.item_id.expect_def_id(); - let v = render_assoc_items(cx, item, def_id, AssocItemRender::All); - write!(f, "{v}") - }) + fn render_assoc_items(&self) -> impl fmt::Display { + let (item, cx) = self.item_and_cx(); + let def_id = item.item_id.expect_def_id(); + render_assoc_items(cx, item, def_id, AssocItemRender::All) } item_template_methods!($($rest)*); }; @@ -162,10 +149,7 @@ struct ItemVars<'a> { src_href: Option<&'a str>, } -pub(super) fn print_item<'a, 'tcx>( - cx: &'a Context<'tcx>, - item: &'a clean::Item, -) -> impl fmt::Display + 'a + Captures<'tcx> { +pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item) -> impl fmt::Display { debug_assert!(!item.is_stripped()); fmt::from_fn(|buf| { @@ -241,30 +225,30 @@ pub(super) fn print_item<'a, 'tcx>( item_vars.render_into(buf).unwrap(); match &item.kind { - clean::ModuleItem(ref m) => { + clean::ModuleItem(m) => { write!(buf, "{}", item_module(cx, item, &m.items)) } - clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f, _) => { + clean::FunctionItem(f) | clean::ForeignFunctionItem(f, _) => { write!(buf, "{}", item_function(cx, item, f)) } - clean::TraitItem(ref t) => write!(buf, "{}", item_trait(cx, item, t)), - clean::StructItem(ref s) => { + clean::TraitItem(t) => write!(buf, "{}", item_trait(cx, item, t)), + clean::StructItem(s) => { write!(buf, "{}", item_struct(cx, item, s)) } - clean::UnionItem(ref s) => write!(buf, "{}", item_union(cx, item, s)), - clean::EnumItem(ref e) => write!(buf, "{}", item_enum(cx, item, e)), - clean::TypeAliasItem(ref t) => { + clean::UnionItem(s) => write!(buf, "{}", item_union(cx, item, s)), + clean::EnumItem(e) => write!(buf, "{}", item_enum(cx, item, e)), + clean::TypeAliasItem(t) => { write!(buf, "{}", item_type_alias(cx, item, t)) } - clean::MacroItem(ref m) => write!(buf, "{}", item_macro(cx, item, m)), - clean::ProcMacroItem(ref m) => { + clean::MacroItem(m) => write!(buf, "{}", item_macro(cx, item, m)), + clean::ProcMacroItem(m) => { write!(buf, "{}", item_proc_macro(cx, item, m)) } clean::PrimitiveItem(_) => write!(buf, "{}", item_primitive(cx, item)), - clean::StaticItem(ref i) => { + clean::StaticItem(i) => { write!(buf, "{}", item_static(cx, item, i, None)) } - clean::ForeignStaticItem(ref i, safety) => { + clean::ForeignStaticItem(i, safety) => { write!(buf, "{}", item_static(cx, item, i, Some(*safety))) } clean::ConstantItem(ci) => { @@ -274,7 +258,7 @@ pub(super) fn print_item<'a, 'tcx>( write!(buf, "{}", item_foreign_type(cx, item)) } clean::KeywordItem => write!(buf, "{}", item_keyword(cx, item)), - clean::TraitAliasItem(ref ta) => { + clean::TraitAliasItem(ta) => { write!(buf, "{}", item_trait_alias(cx, item, ta)) } _ => { @@ -317,15 +301,11 @@ fn toggle_close(mut w: impl fmt::Write) { w.write_str("").unwrap(); } -trait ItemTemplate<'a, 'cx: 'a>: rinja::Template + Display { +trait ItemTemplate<'a, 'cx: 'a>: askama::Template + Display { fn item_and_cx(&self) -> (&'a clean::Item, &'a Context<'cx>); } -fn item_module<'a, 'tcx>( - cx: &'a Context<'tcx>, - item: &'a clean::Item, - items: &'a [clean::Item], -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> impl fmt::Display { fmt::from_fn(|w| { write!(w, "{}", document(cx, item, None, HeadingOffset::H2))?; @@ -367,9 +347,12 @@ fn item_module<'a, 'tcx>( // but we actually want stable items to come first return is_stable2.cmp(&is_stable1); } - let lhs = i1.name.unwrap_or(kw::Empty); - let rhs = i2.name.unwrap_or(kw::Empty); - compare_names(lhs.as_str(), rhs.as_str()) + match (i1.name, i2.name) { + (Some(name1), Some(name2)) => compare_names(name1.as_str(), name2.as_str()), + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (None, None) => Ordering::Equal, + } } let tcx = cx.tcx(); @@ -541,14 +524,14 @@ fn item_module<'a, 'tcx>( /// Render the stability, deprecation and portability tags that are displayed in the item's summary /// at the module level. -fn extra_info_tags<'a, 'tcx: 'a>( - tcx: TyCtxt<'tcx>, - item: &'a clean::Item, - parent: &'a clean::Item, +fn extra_info_tags( + tcx: TyCtxt<'_>, + item: &clean::Item, + parent: &clean::Item, import_def_id: Option, -) -> impl Display + 'a + Captures<'tcx> { +) -> impl Display { fmt::from_fn(move |f| { - fn tag_html<'a>(class: &'a str, title: &'a str, contents: &'a str) -> impl Display + 'a { + fn tag_html(class: &str, title: &str, contents: &str) -> impl Display { fmt::from_fn(move |f| { write!( f, @@ -597,11 +580,7 @@ fn extra_info_tags<'a, 'tcx: 'a>( }) } -fn item_function<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - f: &'a clean::Function, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> impl fmt::Display { fmt::from_fn(|w| { let tcx = cx.tcx(); let header = it.fn_header(tcx).expect("printing a function which isn't a function"); @@ -657,11 +636,7 @@ fn item_function<'a, 'tcx>( }) } -fn item_trait<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - t: &'a clean::Trait, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt::Display { fmt::from_fn(|w| { let tcx = cx.tcx(); let bounds = bounds(&t.bounds, false, cx); @@ -831,11 +806,7 @@ fn item_trait<'a, 'tcx>( // Trait documentation write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; - fn trait_item<'a, 'tcx>( - cx: &'a Context<'tcx>, - m: &'a clean::Item, - t: &'a clean::Item, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + fn trait_item(cx: &Context<'_>, m: &clean::Item, t: &clean::Item) -> impl fmt::Display { fmt::from_fn(|w| { let name = m.name.unwrap(); info!("Documenting {name} on {ty_name:?}", ty_name = t.name); @@ -1021,7 +992,7 @@ fn item_trait<'a, 'tcx>( extern_crates.insert(did.krate); } match implementor.inner_impl().for_.without_borrowed_ref() { - clean::Type::Path { ref path } if !path.is_assoc_ty() => { + clean::Type::Path { path } if !path.is_assoc_ty() => { let did = path.def_id(); let &mut (prev_did, ref mut has_duplicates) = implementor_dups.entry(path.last()).or_insert((did, false)); @@ -1224,10 +1195,8 @@ fn item_trait<'a, 'tcx>( // to already be in the HTML, and will be ignored. // // [JSONP]: https://en.wikipedia.org/wiki/JSONP - let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..") - .take(cx.current.len()) - .chain(std::iter::once("trait.impl")) - .collect(); + let mut js_src_path: UrlPartsBuilder = + iter::repeat_n("..", cx.current.len()).chain(iter::once("trait.impl")).collect(); if let Some(did) = it.item_id.as_def_id() && let get_extern = { || cx.shared.cache.external_paths.get(&did).map(|s| &s.0) } && let Some(fqp) = cx.shared.cache.exact_paths.get(&did).or_else(get_extern) @@ -1254,21 +1223,22 @@ fn item_trait<'a, 'tcx>( }) } -fn item_trait_alias<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - t: &'a clean::TraitAlias, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_trait_alias( + cx: &Context<'_>, + it: &clean::Item, + t: &clean::TraitAlias, +) -> impl fmt::Display { fmt::from_fn(|w| { wrap_item(w, |w| { write!( w, - "{attrs}trait {name}{generics}{where_b} = {bounds};", + "{attrs}trait {name}{generics} = {bounds}{where_clause};", attrs = render_attributes_in_pre(it, "", cx), name = it.name.unwrap(), generics = t.generics.print(cx), - where_b = print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(), bounds = bounds(&t.bounds, true, cx), + where_clause = + print_where_clause(&t.generics, cx, 0, Ending::NoNewline).maybe_display(), ) })?; @@ -1285,11 +1255,7 @@ fn item_trait_alias<'a, 'tcx>( }) } -fn item_type_alias<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - t: &'a clean::TypeAlias, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> impl fmt::Display { fmt::from_fn(|w| { wrap_item(w, |w| { write!( @@ -1482,10 +1448,8 @@ fn item_type_alias<'a, 'tcx>( && let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) } && let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) { - let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..") - .take(cx.current.len()) - .chain(std::iter::once("type.impl")) - .collect(); + let mut js_src_path: UrlPartsBuilder = + iter::repeat_n("..", cx.current.len()).chain(iter::once("type.impl")).collect(); js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied()); js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap())); let self_path = fmt::from_fn(|f| self_fqp.iter().joined("::", f)); @@ -1499,11 +1463,7 @@ fn item_type_alias<'a, 'tcx>( }) } -fn item_union<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - s: &'a clean::Union, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { item_template!( #[template(path = "item_union.html")] struct ItemUnion<'a, 'cx> { @@ -1515,40 +1475,25 @@ fn item_union<'a, 'tcx>( ); impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { - fn render_union<'b>(&'b self) -> impl Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let v = render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx); - write!(f, "{v}") - }) + fn render_union(&self) -> impl Display { + render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx) } - fn document_field<'b>( - &'b self, - field: &'a clean::Item, - ) -> impl Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let v = document(self.cx, field, Some(self.it), HeadingOffset::H3); - write!(f, "{v}") - }) + fn document_field(&self, field: &'a clean::Item) -> impl Display { + document(self.cx, field, Some(self.it), HeadingOffset::H3) } fn stability_field(&self, field: &clean::Item) -> Option { field.stability_class(self.cx.tcx()) } - fn print_ty<'b>( - &'b self, - ty: &'a clean::Type, - ) -> impl Display + Captures<'a> + 'b + Captures<'cx> { - fmt::from_fn(move |f| { - let v = ty.print(self.cx); - write!(f, "{v}") - }) + fn print_ty(&self, ty: &'a clean::Type) -> impl Display { + ty.print(self.cx) } fn fields_iter( &self, - ) -> std::iter::Peekable> { + ) -> iter::Peekable> { self.s .fields .iter() @@ -1566,10 +1511,7 @@ fn item_union<'a, 'tcx>( }) } -fn print_tuple_struct_fields<'a, 'cx: 'a>( - cx: &'a Context<'cx>, - s: &'a [clean::Item], -) -> impl Display + 'a + Captures<'cx> { +fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Display { fmt::from_fn(|f| { if !s.is_empty() && s.iter().all(|field| { @@ -1591,11 +1533,7 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>( }) } -fn item_enum<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - e: &'a clean::Enum, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display { fmt::from_fn(|w| { let count_variants = e.variants().count(); wrap_item(w, |w| { @@ -1658,14 +1596,14 @@ fn should_show_enum_discriminant( repr.c() || repr.int.is_some() } -fn display_c_like_variant<'a, 'tcx>( - cx: &'a Context<'tcx>, - item: &'a clean::Item, - variant: &'a clean::Variant, +fn display_c_like_variant( + cx: &Context<'_>, + item: &clean::Item, + variant: &clean::Variant, index: VariantIdx, should_show_enum_discriminant: bool, enum_def_id: DefId, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl fmt::Display { fmt::from_fn(move |w| { let name = item.name.unwrap(); if let Some(ref value) = variant.discriminant { @@ -1685,15 +1623,15 @@ fn display_c_like_variant<'a, 'tcx>( }) } -fn render_enum_fields<'a, 'tcx>( - cx: &'a Context<'tcx>, - g: Option<&'a clean::Generics>, - variants: &'a IndexVec, +fn render_enum_fields( + cx: &Context<'_>, + g: Option<&clean::Generics>, + variants: &IndexVec, count_variants: usize, has_stripped_entries: bool, is_non_exhaustive: bool, enum_def_id: DefId, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl fmt::Display { fmt::from_fn(move |w| { let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants); @@ -1764,12 +1702,12 @@ fn render_enum_fields<'a, 'tcx>( }) } -fn item_variants<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - variants: &'a IndexVec, +fn item_variants( + cx: &Context<'_>, + it: &clean::Item, + variants: &IndexVec, enum_def_id: DefId, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl fmt::Display { fmt::from_fn(move |w| { let tcx = cx.tcx(); write!( @@ -1895,11 +1833,7 @@ fn item_variants<'a, 'tcx>( }) } -fn item_macro<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - t: &'a clean::Macro, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_macro(cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) -> impl fmt::Display { fmt::from_fn(|w| { wrap_item(w, |w| { // FIXME: Also print `#[doc(hidden)]` for `macro_rules!` if it `is_doc_hidden`. @@ -1912,11 +1846,7 @@ fn item_macro<'a, 'tcx>( }) } -fn item_proc_macro<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - m: &'a clean::ProcMacro, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_proc_macro(cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) -> impl fmt::Display { fmt::from_fn(|w| { wrap_item(w, |w| { let name = it.name.expect("proc-macros always have names"); @@ -1941,16 +1871,13 @@ fn item_proc_macro<'a, 'tcx>( } } } - Ok(()) + fmt::Result::Ok(()) })?; write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) }) } -fn item_primitive<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_primitive(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display { fmt::from_fn(|w| { let def_id = it.item_id.expect_def_id(); write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; @@ -1968,13 +1895,13 @@ fn item_primitive<'a, 'tcx>( }) } -fn item_constant<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - generics: &'a clean::Generics, - ty: &'a clean::Type, - c: &'a clean::ConstantKind, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_constant( + cx: &Context<'_>, + it: &clean::Item, + generics: &clean::Generics, + ty: &clean::Type, + c: &clean::ConstantKind, +) -> impl fmt::Display { fmt::from_fn(|w| { wrap_item(w, |w| { let tcx = cx.tcx(); @@ -2021,18 +1948,14 @@ fn item_constant<'a, 'tcx>( } } } - Ok(()) + Ok::<(), fmt::Error>(()) })?; write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) }) } -fn item_struct<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - s: &'a clean::Struct, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { fmt::from_fn(|w| { wrap_item(w, |w| { render_attributes_in_code(w, it, cx); @@ -2056,12 +1979,12 @@ fn item_struct<'a, 'tcx>( }) } -fn item_fields<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - fields: &'a [clean::Item], +fn item_fields( + cx: &Context<'_>, + it: &clean::Item, + fields: &[clean::Item], ctor_kind: Option, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl fmt::Display { fmt::from_fn(move |w| { let mut fields = fields .iter() @@ -2111,12 +2034,12 @@ fn item_fields<'a, 'tcx>( }) } -fn item_static<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, - s: &'a clean::Static, +fn item_static( + cx: &Context<'_>, + it: &clean::Item, + s: &clean::Static, safety: Option, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl fmt::Display { fmt::from_fn(move |w| { wrap_item(w, |w| { render_attributes_in_code(w, it, cx); @@ -2135,10 +2058,7 @@ fn item_static<'a, 'tcx>( }) } -fn item_foreign_type<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_foreign_type(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display { fmt::from_fn(|w| { wrap_item(w, |w| { w.write_str("extern {\n")?; @@ -2155,10 +2075,7 @@ fn item_foreign_type<'a, 'tcx>( }) } -fn item_keyword<'a, 'tcx>( - cx: &'a Context<'tcx>, - it: &'a clean::Item, -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn item_keyword(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display { document(cx, it, None, HeadingOffset::H2) } @@ -2268,18 +2185,14 @@ pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String { s } -pub(super) fn item_path(ty: ItemType, name: &str) -> impl Display + '_ { +pub(super) fn item_path(ty: ItemType, name: &str) -> impl Display { fmt::from_fn(move |f| match ty { ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)), _ => write!(f, "{ty}.{name}.html"), }) } -fn bounds<'a, 'tcx>( - bounds: &'a [clean::GenericBound], - trait_alias: bool, - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +fn bounds(bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> impl Display { (!bounds.is_empty()) .then_some(fmt::from_fn(move |f| { let has_lots_of_bounds = bounds.len() > 2; @@ -2329,13 +2242,13 @@ impl Ord for ImplString { } } -fn render_implementor<'a, 'tcx>( - cx: &'a Context<'tcx>, - implementor: &'a Impl, - trait_: &'a clean::Item, - implementor_dups: &'a FxHashMap, - aliases: &'a [String], -) -> impl fmt::Display + 'a + Captures<'tcx> { +fn render_implementor( + cx: &Context<'_>, + implementor: &Impl, + trait_: &clean::Item, + implementor_dups: &FxHashMap, + aliases: &[String], +) -> impl fmt::Display { // If there's already another implementor that has the same abridged name, use the // full path, for example in `std::iter::ExactSizeIterator` let use_absolute = match implementor.inner_impl().for_ { @@ -2364,12 +2277,12 @@ fn render_implementor<'a, 'tcx>( ) } -fn render_union<'a, 'cx: 'a>( - it: &'a clean::Item, - g: Option<&'a clean::Generics>, - fields: &'a [clean::Item], - cx: &'a Context<'cx>, -) -> impl Display + 'a + Captures<'cx> { +fn render_union( + it: &clean::Item, + g: Option<&clean::Generics>, + fields: &[clean::Item], + cx: &Context<'_>, +) -> impl Display { fmt::from_fn(move |mut f| { write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?; @@ -2421,15 +2334,15 @@ fn render_union<'a, 'cx: 'a>( }) } -fn render_struct<'a, 'tcx>( - it: &'a clean::Item, - g: Option<&'a clean::Generics>, +fn render_struct( + it: &clean::Item, + g: Option<&clean::Generics>, ty: Option, - fields: &'a [clean::Item], - tab: &'a str, + fields: &[clean::Item], + tab: &str, structhead: bool, - cx: &'a Context<'tcx>, -) -> impl fmt::Display + 'a + Captures<'tcx> { + cx: &Context<'_>, +) -> impl fmt::Display { fmt::from_fn(move |w| { write!( w, @@ -2457,15 +2370,15 @@ fn render_struct<'a, 'tcx>( }) } -fn render_struct_fields<'a, 'tcx>( - g: Option<&'a clean::Generics>, +fn render_struct_fields( + g: Option<&clean::Generics>, ty: Option, - fields: &'a [clean::Item], - tab: &'a str, + fields: &[clean::Item], + tab: &str, structhead: bool, has_stripped_entries: bool, - cx: &'a Context<'tcx>, -) -> impl fmt::Display + 'a + Captures<'tcx> { + cx: &Context<'_>, +) -> impl fmt::Display { fmt::from_fn(move |w| { match ty { None => { @@ -2581,7 +2494,7 @@ fn document_non_exhaustive_header(item: &clean::Item) -> &str { if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" } } -fn document_non_exhaustive(item: &clean::Item) -> impl Display + '_ { +fn document_non_exhaustive(item: &clean::Item) -> impl Display { fmt::from_fn(|f| { if item.is_non_exhaustive() { write!( diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 95f617c98390a..aff8684ee3a09 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -709,8 +709,11 @@ pub(crate) fn build_index( let mut result = Vec::new(); for (index, item) in self.items.iter().enumerate() { if let Some(ty) = &item.search_type - && let my = - ty.param_names.iter().map(|sym| sym.as_str()).collect::>() + && let my = ty + .param_names + .iter() + .filter_map(|sym| sym.map(|sym| sym.to_string())) + .collect::>() && my != prev { result.push((index, my.join(","))); @@ -842,10 +845,7 @@ pub(crate) fn get_function_type_for_search( } clean::ConstantItem(ref c) => make_nullary_fn(&c.type_), clean::StaticItem(ref s) => make_nullary_fn(&s.type_), - clean::StructFieldItem(ref t) => { - let Some(parent) = parent else { - return None; - }; + clean::StructFieldItem(ref t) if let Some(parent) = parent => { let mut rgen: FxIndexMap)> = Default::default(); let output = get_index_type(t, vec![], &mut rgen); @@ -1112,7 +1112,7 @@ fn simplify_fn_type<'a, 'tcx>( } Type::BareFunction(ref bf) => { let mut ty_generics = Vec::new(); - for ty in bf.decl.inputs.values.iter().map(|arg| &arg.type_) { + for ty in bf.decl.inputs.iter().map(|arg| &arg.type_) { simplify_fn_type( self_, generics, @@ -1375,7 +1375,7 @@ fn simplify_fn_constraint<'a>( /// Used to allow type-based search on constants and statics. fn make_nullary_fn( clean_type: &clean::Type, -) -> (Vec, Vec, Vec, Vec>) { +) -> (Vec, Vec, Vec>, Vec>) { let mut rgen: FxIndexMap)> = Default::default(); let output = get_index_type(clean_type, vec![], &mut rgen); (vec![], vec![output], vec![], vec![]) @@ -1390,7 +1390,7 @@ fn get_fn_inputs_and_outputs( tcx: TyCtxt<'_>, impl_or_trait_generics: Option<&(clean::Type, clean::Generics)>, cache: &Cache, -) -> (Vec, Vec, Vec, Vec>) { +) -> (Vec, Vec, Vec>, Vec>) { let decl = &func.decl; let mut rgen: FxIndexMap)> = Default::default(); @@ -1418,15 +1418,15 @@ fn get_fn_inputs_and_outputs( (None, &func.generics) }; - let mut arg_types = Vec::new(); - for arg in decl.inputs.values.iter() { + let mut param_types = Vec::new(); + for param in decl.inputs.iter() { simplify_fn_type( self_, generics, - &arg.type_, + ¶m.type_, tcx, 0, - &mut arg_types, + &mut param_types, &mut rgen, false, cache, @@ -1439,15 +1439,15 @@ fn get_fn_inputs_and_outputs( let mut simplified_params = rgen.into_iter().collect::>(); simplified_params.sort_by_key(|(_, (idx, _))| -idx); ( - arg_types, + param_types, ret_types, simplified_params .iter() .map(|(name, (_idx, _traits))| match name { - SimplifiedParam::Symbol(name) => *name, - SimplifiedParam::Anonymous(_) => kw::Empty, + SimplifiedParam::Symbol(name) => Some(*name), + SimplifiedParam::Anonymous(_) => None, SimplifiedParam::AssociatedType(def_id, name) => { - Symbol::intern(&format!("{}::{}", tcx.item_name(*def_id), name)) + Some(Symbol::intern(&format!("{}::{}", tcx.item_name(*def_id), name))) } }) .collect(), diff --git a/src/librustdoc/html/render/search_index/encode.rs b/src/librustdoc/html/render/search_index/encode.rs index 8816ea650593b..de2f54558ff81 100644 --- a/src/librustdoc/html/render/search_index/encode.rs +++ b/src/librustdoc/html/render/search_index/encode.rs @@ -182,9 +182,9 @@ pub(crate) fn write_bitmap_to_bytes( out.write_all(&[b])?; } if size < NO_OFFSET_THRESHOLD { - 4 + 4 * size + ((size + 7) / 8) + 4 + 4 * size + size.div_ceil(8) } else { - 4 + 8 * size + ((size + 7) / 8) + 4 + 8 * size + size.div_ceil(8) } } else { out.write_all(&u32::to_le_bytes(SERIAL_COOKIE_NO_RUNCONTAINER))?; diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 64dbaf9083e70..a9029972d9630 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -1,7 +1,8 @@ use std::borrow::Cow; use std::cmp::Ordering; +use std::fmt; -use rinja::Template; +use askama::Template; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefIdMap, DefIdSet}; @@ -79,7 +80,7 @@ impl<'a> LinkBlock<'a> { } /// A link to an item. Content should not be escaped. -#[derive(Ord, PartialEq, Eq, Hash, Clone)] +#[derive(PartialEq, Eq, Hash, Clone)] pub(crate) struct Link<'a> { /// The content for the anchor tag and title attr name: Cow<'a, str>, @@ -91,13 +92,13 @@ pub(crate) struct Link<'a> { children: Vec>, } -impl PartialOrd for Link<'_> { - fn partial_cmp(&self, other: &Link<'_>) -> Option { +impl Ord for Link<'_> { + fn cmp(&self, other: &Self) -> Ordering { match compare_names(&self.name, &other.name) { - Ordering::Equal => (), - result => return Some(result), + Ordering::Equal => {} + result => return result, } - (&self.name_html, &self.href, &self.children).partial_cmp(&( + (&self.name_html, &self.href, &self.children).cmp(&( &other.name_html, &other.href, &other.children, @@ -105,6 +106,12 @@ impl PartialOrd for Link<'_> { } } +impl PartialOrd for Link<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + impl<'a> Link<'a> { pub fn new(href: impl Into>, name: impl Into>) -> Self { Self { href: href.into(), name: name.into(), children: vec![], name_html: None } @@ -117,10 +124,10 @@ impl<'a> Link<'a> { pub(crate) mod filters { use std::fmt::{self, Display}; - use rinja::filters::Safe; + use askama::filters::Safe; use crate::html::escape::EscapeBodyTextWithWbr; - pub(crate) fn wrapped(v: T) -> rinja::Result> + pub(crate) fn wrapped(v: T) -> askama::Result> where T: Display, { @@ -129,7 +136,11 @@ pub(crate) mod filters { } } -pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut String) { +pub(super) fn print_sidebar( + cx: &Context<'_>, + it: &clean::Item, + mut buffer: impl fmt::Write, +) -> fmt::Result { let mut ids = IdMap::new(); let mut blocks: Vec> = docblock_toc(cx, it, &mut ids).into_iter().collect(); let deref_id_map = cx.deref_id_map.borrow(); @@ -189,7 +200,8 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Str blocks, path, }; - sidebar.render_into(buffer).unwrap(); + sidebar.render_into(&mut buffer)?; + Ok(()) } fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec> { @@ -527,7 +539,10 @@ fn sidebar_deref_methods<'a>( debug!("found inner_impl: {impls:?}"); let mut ret = impls .iter() - .filter(|i| i.inner_impl().trait_.is_none()) + .filter(|i| { + i.inner_impl().trait_.is_none() + && real_target.is_doc_subtype_of(&i.inner_impl().for_, &c) + }) .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx())) .collect::>(); if !ret.is_empty() { @@ -725,20 +740,20 @@ fn get_methods<'a>( ) -> Vec> { i.items .iter() - .filter_map(|item| match item.name { - Some(ref name) if !name.is_empty() && item.is_method() => { - if !for_deref || super::should_render_item(item, deref_mut, tcx) { - Some(Link::new( - get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::Method)), - name.as_str(), - )) - } else { - None - } + .filter_map(|item| { + if let Some(ref name) = item.name + && item.is_method() + && (!for_deref || super::should_render_item(item, deref_mut, tcx)) + { + Some(Link::new( + get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::Method)), + name.as_str(), + )) + } else { + None } - _ => None, }) - .collect::>() + .collect() } fn get_associated_constants<'a>( @@ -747,14 +762,19 @@ fn get_associated_constants<'a>( ) -> Vec> { i.items .iter() - .filter_map(|item| match item.name { - Some(ref name) if !name.is_empty() && item.is_associated_const() => Some(Link::new( - get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)), - name.as_str(), - )), - _ => None, + .filter_map(|item| { + if let Some(ref name) = item.name + && item.is_associated_const() + { + Some(Link::new( + get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)), + name.as_str(), + )) + } else { + None + } }) - .collect::>() + .collect() } fn get_associated_types<'a>( @@ -763,12 +783,17 @@ fn get_associated_types<'a>( ) -> Vec> { i.items .iter() - .filter_map(|item| match item.name { - Some(ref name) if !name.is_empty() && item.is_associated_type() => Some(Link::new( - get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)), - name.as_str(), - )), - _ => None, + .filter_map(|item| { + if let Some(ref name) = item.name + && item.is_associated_type() + { + Some(Link::new( + get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)), + name.as_str(), + )) + } else { + None + } }) - .collect::>() + .collect() } diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index ce9c42c01cc73..846d3ad310ce4 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -95,10 +95,8 @@ impl SpanMapVisitor<'_> { .unwrap_or(path.span); self.matches.insert(span, link); } - Res::Local(_) => { - if let Some(span) = self.tcx.hir().res_span(path.res) { - self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span))); - } + Res::Local(_) if let Some(span) = self.tcx.hir_res_span(path.res) => { + self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span))); } Res::PrimTy(p) => { // FIXME: Doesn't handle "path-like" primitives like arrays or tuples. @@ -111,15 +109,15 @@ impl SpanMapVisitor<'_> { /// Used to generate links on items' definition to go to their documentation page. pub(crate) fn extract_info_from_hir_id(&mut self, hir_id: HirId) { - if let Node::Item(item) = self.tcx.hir_node(hir_id) { - if let Some(span) = self.tcx.def_ident_span(item.owner_id) { - let cspan = clean::Span::new(span); - // If the span isn't from the current crate, we ignore it. - if cspan.inner().is_dummy() || cspan.cnum(self.tcx.sess) != LOCAL_CRATE { - return; - } - self.matches.insert(span, LinkFromSrc::Doc(item.owner_id.to_def_id())); + if let Node::Item(item) = self.tcx.hir_node(hir_id) + && let Some(span) = self.tcx.def_ident_span(item.owner_id) + { + let cspan = clean::Span::new(span); + // If the span isn't from the current crate, we ignore it. + if cspan.inner().is_dummy() || cspan.cnum(self.tcx.sess) != LOCAL_CRATE { + return; } + self.matches.insert(span, LinkFromSrc::Doc(item.owner_id.to_def_id())); } } @@ -244,16 +242,15 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { // Now that we confirmed it's a file import, we want to get the span for the module // name only and not all the "mod foo;". if let Node::Item(item) = self.tcx.hir_node(id) { - self.matches.insert( - item.ident.span, - LinkFromSrc::Local(clean::Span::new(m.spans.inner_span)), - ); + let (ident, _) = item.expect_mod(); + self.matches + .insert(ident.span, LinkFromSrc::Local(clean::Span::new(m.spans.inner_span))); } } else { // If it's a "mod foo {}", we want to look to its documentation page. self.extract_info_from_hir_id(id); } - intravisit::walk_mod(self, m, id); + intravisit::walk_mod(self, m); } fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) { @@ -274,23 +271,23 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { fn visit_item(&mut self, item: &'tcx Item<'tcx>) { match item.kind { - ItemKind::Static(_, _, _) - | ItemKind::Const(_, _, _) + ItemKind::Static(..) + | ItemKind::Const(..) | ItemKind::Fn { .. } - | ItemKind::Macro(_, _) - | ItemKind::TyAlias(_, _) - | ItemKind::Enum(_, _) - | ItemKind::Struct(_, _) - | ItemKind::Union(_, _) - | ItemKind::Trait(_, _, _, _, _) - | ItemKind::TraitAlias(_, _) => self.extract_info_from_hir_id(item.hir_id()), + | ItemKind::Macro(..) + | ItemKind::TyAlias(..) + | ItemKind::Enum(..) + | ItemKind::Struct(..) + | ItemKind::Union(..) + | ItemKind::Trait(..) + | ItemKind::TraitAlias(..) => self.extract_info_from_hir_id(item.hir_id()), ItemKind::Impl(_) - | ItemKind::Use(_, _) - | ItemKind::ExternCrate(_) + | ItemKind::Use(..) + | ItemKind::ExternCrate(..) | ItemKind::ForeignMod { .. } | ItemKind::GlobalAsm { .. } // We already have "visit_mod" above so no need to check it here. - | ItemKind::Mod(_) => {} + | ItemKind::Mod(..) => {} } intravisit::walk_item(self, item); } diff --git a/src/librustdoc/html/render/tests.rs b/src/librustdoc/html/render/tests.rs index 657cd3c82aae3..327a30887b1db 100644 --- a/src/librustdoc/html/render/tests.rs +++ b/src/librustdoc/html/render/tests.rs @@ -47,8 +47,7 @@ fn test_all_types_prints_header_once() { // Regression test for #82477 let all_types = AllTypes::new(); - let mut buffer = String::new(); - all_types.print(&mut buffer); + let buffer = all_types.print().to_string(); assert_eq!(1, buffer.matches("List of all items").count()); } diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs index 0f01db5f6bcc7..fb1f0271c2ad7 100644 --- a/src/librustdoc/html/render/type_layout.rs +++ b/src/librustdoc/html/render/type_layout.rs @@ -1,8 +1,7 @@ use std::fmt; -use rinja::Template; +use askama::Template; use rustc_abi::{Primitive, TagEncoding, Variants}; -use rustc_data_structures::captures::Captures; use rustc_hir::def_id::DefId; use rustc_middle::span_bug; use rustc_middle::ty::layout::LayoutError; @@ -26,10 +25,7 @@ struct TypeLayoutSize { size: u64, } -pub(crate) fn document_type_layout<'a, 'cx: 'a>( - cx: &'a Context<'cx>, - ty_def_id: DefId, -) -> impl fmt::Display + 'a + Captures<'cx> { +pub(crate) fn document_type_layout(cx: &Context<'_>, ty_def_id: DefId) -> impl fmt::Display { fmt::from_fn(move |f| { if !cx.shared.show_type_layout { return Ok(()); diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 78c86a27632b1..1fa6b5a60f3a7 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -3,7 +3,7 @@ use std::ffi::OsStr; use std::path::{Component, Path, PathBuf}; use std::{fmt, fs}; -use rinja::Template; +use askama::Template; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::ty::TyCtxt; @@ -11,9 +11,8 @@ use rustc_session::Session; use rustc_span::{FileName, FileNameDisplayPreference, RealFileName, sym}; use tracing::info; -use super::highlight; -use super::layout::{self, BufDisplay}; use super::render::Context; +use super::{highlight, layout}; use crate::clean; use crate::clean::utils::has_doc_flag; use crate::docfs::PathError; @@ -243,16 +242,16 @@ impl SourceCollector<'_, '_> { &shared.layout, &page, "", - BufDisplay(|buf: &mut String| { + fmt::from_fn(|f| { print_src( - buf, + f, contents, file_span, self.cx, &root_path, &highlight::DecorationInfo::default(), &source_context, - ); + ) }), &shared.style_files, ); @@ -331,9 +330,9 @@ pub(crate) fn print_src( root_path: &str, decoration_info: &highlight::DecorationInfo, source_context: &SourceContext<'_>, -) { +) -> fmt::Result { let mut lines = s.lines().count(); - let line_info = if let SourceContext::Embedded(ref info) = source_context { + let line_info = if let SourceContext::Embedded(info) = source_context { highlight::LineInfo::new_scraped(lines as u32, info.offset as u32) } else { highlight::LineInfo::new(lines as u32) @@ -367,12 +366,10 @@ pub(crate) fn print_src( }, max_nb_digits, } - .render_into(&mut writer) - .unwrap(), + .render_into(&mut writer), SourceContext::Embedded(info) => { - ScrapedSource { info, code_html: code, max_nb_digits } - .render_into(&mut writer) - .unwrap(); + ScrapedSource { info, code_html: code, max_nb_digits }.render_into(&mut writer) } - }; + }?; + Ok(()) } diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index 477a79d63e948..a3c6bf9816169 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -43,6 +43,7 @@ nav.sub { --settings-button-border-focus: #717171; --sidebar-background-color: #f5f5f5; --sidebar-background-color-hover: #e0e0e0; + --sidebar-border-color: #ddd; --code-block-background-color: #f5f5f5; --scrollbar-track-background-color: #dcdcdc; --scrollbar-thumb-background-color: rgba(36, 37, 39, 0.6); @@ -149,6 +150,7 @@ nav.sub { --settings-button-border-focus: #ffb900; --sidebar-background-color: #505050; --sidebar-background-color-hover: #676767; + --sidebar-border-color: #2A2A2A; --code-block-background-color: #2A2A2A; --scrollbar-track-background-color: #717171; --scrollbar-thumb-background-color: rgba(32, 34, 37, .6); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 0ea4d8f1e3914..a81d5c9c49b4b 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1,4 +1,6 @@ -/* When static files are updated, their suffixes need to be updated. +/* ignore-tidy-filelength */ +/* + When static files are updated, their suffixes need to be updated. 1. In the top directory run: ./x.py doc --stage 1 library/core 2. Find the directory containing files named with updated suffixes: @@ -55,6 +57,9 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ --collapse-arrow-image: url('data:image/svg+xml,'); + --hamburger-image: url('data:image/svg+xml,\ + '); } :root.sans-serif { @@ -493,12 +498,13 @@ img { top: 0; left: 0; z-index: var(--desktop-sidebar-z-index); + /* resize indicator: hide this when on touch or mobile */ + border-right: solid 1px var(--sidebar-border-color); } .rustdoc.src .sidebar { flex-basis: 50px; width: 50px; - border-right: 1px solid; overflow-x: hidden; /* The sidebar is by default hidden */ overflow-y: hidden; @@ -512,12 +518,27 @@ img { .sidebar-resizer { touch-action: none; width: 9px; - cursor: col-resize; + cursor: ew-resize; z-index: calc(var(--desktop-sidebar-z-index) + 1); position: fixed; height: 100%; - /* make sure there's a 1px gap between the scrollbar and resize handle */ - left: calc(var(--desktop-sidebar-width) + 1px); + left: var(--desktop-sidebar-width); + display: flex; + align-items: center; + justify-content: flex-start; + color: var(--right-side-color); +} +.sidebar-resizer::before { + content: ""; + border-right: dotted 2px currentColor; + width: 2px; + height: 12px; +} +.sidebar-resizer::after { + content: ""; + border-right: dotted 2px currentColor; + width: 2px; + height: 16px; } .rustdoc.src .sidebar-resizer { @@ -540,11 +561,12 @@ img { } .sidebar-resizing * { - cursor: col-resize !important; + cursor: ew-resize !important; } .sidebar-resizing .sidebar { position: fixed; + border-right: solid 2px var(--sidebar-resizer-active); } .sidebar-resizing > body { padding-left: var(--resizing-sidebar-width); @@ -558,8 +580,9 @@ img { margin: 0; /* when active or hovered, place resizer glow on top of the sidebar (right next to, or even on top of, the scrollbar) */ - left: var(--desktop-sidebar-width); + left: calc(var(--desktop-sidebar-width) - 1px); border-left: solid 1px var(--sidebar-resizer-hover); + color: var(--sidebar-resizer-hover); } .src-sidebar-expanded .rustdoc.src .sidebar-resizer:hover, @@ -575,21 +598,20 @@ img { /* too easy to hit the resizer while trying to hit the [-] toggle */ display: none !important; } + .sidebar { + /* resize indicator: hide this when on touch or mobile */ + border-right: none; + } } .sidebar-resizer.active { /* make the resize tool bigger when actually resizing, to avoid :hover styles on other stuff while resizing */ padding: 0 140px; - width: 2px; + width: calc(140px + 140px + 9px + 2px); margin-left: -140px; border-left: none; -} -.sidebar-resizer.active:before { - border-left: solid 2px var(--sidebar-resizer-active); - display: block; - height: 100%; - content: ""; + color: var(--sidebar-resizer-active); } .sidebar, .mobile-topbar, .sidebar-menu-toggle, @@ -1446,6 +1468,9 @@ so that we can apply CSS-filters to change the arrow color in themes */ align-items: center; cursor: pointer; } +.setting-check input { + flex-shrink: 0; +} .setting-radio input:checked { box-shadow: inset 0 0 0 3px var(--main-background-color); @@ -1998,9 +2023,11 @@ a.tooltip:hover::after { display: flex; margin-right: 4px; position: fixed; - left: 6px; height: 34px; width: 34px; +} +.hide-sidebar #sidebar-button { + left: 6px; background-color: var(--main-background-color); z-index: 1; } @@ -2016,6 +2043,8 @@ a.tooltip:hover::after { align-items: center; justify-content: center; flex-direction: column; +} +#settings-menu > a, #help-button > a, button#toggle-all-docs { border: 1px solid transparent; border-radius: var(--button-border-radius); color: var(--main-color); @@ -2028,20 +2057,21 @@ a.tooltip:hover::after { min-width: 0; } #sidebar-button > a { - background-color: var(--button-background-color); - border-color: var(--border-color); + background-color: var(--sidebar-background-color); width: 33px; } +#sidebar-button > a:hover, #sidebar-button > a:focus-visible { + background-color: var(--main-background-color); +} #settings-menu > a:hover, #settings-menu > a:focus-visible, #help-button > a:hover, #help-button > a:focus-visible, -#sidebar-button > a:hover, #sidebar-button > a:focus-visible, button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible { border-color: var(--settings-button-border-focus); text-decoration: none; } -#settings-menu > a:before { +#settings-menu > a::before { /* Wheel */ content: url('data:image/svg+xml,\ @@ -2061,7 +2091,7 @@ button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible { filter: var(--settings-menu-filter); } -button#toggle-all-docs:before { +button#toggle-all-docs::before { /* Custom arrow icon */ content: url('data:image/svg+xml,\ @@ -2071,14 +2101,14 @@ button#toggle-all-docs:before { filter: var(--settings-menu-filter); } -button#toggle-all-docs.will-expand:before { +button#toggle-all-docs.will-expand::before { /* Custom arrow icon */ content: url('data:image/svg+xml,\ '); } -#help-button > a:before { +#help-button > a::before { /* Question mark with circle */ content: url('data:image/svg+xml,\ @@ -2090,17 +2120,17 @@ button#toggle-all-docs.will-expand:before { filter: var(--settings-menu-filter); } -button#toggle-all-docs:before, -#help-button > a:before, -#settings-menu > a:before { +button#toggle-all-docs::before, +#help-button > a::before, +#settings-menu > a::before { filter: var(--settings-menu-filter); margin: 8px; } @media not (pointer: coarse) { - button#toggle-all-docs:hover:before, - #help-button > a:hover:before, - #settings-menu > a:hover:before { + button#toggle-all-docs:hover::before, + #help-button > a:hover::before, + #settings-menu > a:hover::before { filter: var(--settings-menu-hover-filter); } } @@ -2122,7 +2152,7 @@ rustdoc-toolbar span.label { padding-bottom: 4px; } -#sidebar-button > a:before { +#sidebar-button > a::before { /* sidebar resizer image */ content: url('data:image/svg+xml,\ @@ -2316,7 +2346,10 @@ details.toggle > summary:not(.hideme)::before { doc block while aligning it with the impl block items. */ .implementors-toggle > .docblock, /* We indent trait items as well. */ -#main-content > .methods > :not(.item-info) { +#main-content > .methods > :not(.item-info), +.impl > .item-info, +.impl > .docblock, +.impl + .docblock { margin-left: var(--impl-items-indent); } @@ -2397,30 +2430,18 @@ However, it's not needed with smaller screen width because the doc/code block is /* sidebar button opens modal use hamburger button */ -.src #sidebar-button > a:before, .sidebar-menu-toggle:before { +.src #sidebar-button > a::before, .sidebar-menu-toggle::before { /* hamburger button image */ - content: url('data:image/svg+xml,\ - '); + content: var(--hamburger-image); opacity: 0.75; + filter: var(--mobile-sidebar-menu-filter); } -.sidebar-menu-toggle:hover:before, -.sidebar-menu-toggle:active:before, -.sidebar-menu-toggle:focus:before { +.sidebar-menu-toggle:hover::before, +.sidebar-menu-toggle:active::before, +.sidebar-menu-toggle:focus::before { opacity: 1; } -/* src sidebar button opens a folder view */ -.src #sidebar-button > a:before { - /* folder image */ - content: url('data:image/svg+xml,\ - \ - \ - '); - opacity: 0.75; -} - /* Media Queries */ /* Make sure all the buttons line wrap at the same time */ @@ -2507,6 +2528,8 @@ in src-script.js and main.js /* Reduce height slightly to account for mobile topbar. */ height: calc(100vh - 45px); width: 200px; + /* resize indicator: hide this when on touch or mobile */ + border-right: none; } /* The source view uses a different design for the sidebar toggle, and doesn't have a topbar, @@ -2596,7 +2619,7 @@ in src-script.js and main.js } /* sidebar button becomes topbar button */ - #sidebar-button > a:before { + #sidebar-button > a::before { content: url('data:image/svg+xml,\ \ @@ -2605,9 +2628,6 @@ in src-script.js and main.js width: 22px; height: 22px; } - .sidebar-menu-toggle:before { - filter: var(--mobile-sidebar-menu-filter); - } .sidebar-menu-toggle:hover { background: var(--main-background-color); } @@ -2665,6 +2685,14 @@ in src-script.js and main.js margin: 0 0 -25px 0; padding: var(--nav-sub-mobile-padding); } + + html:not(.src-sidebar-expanded) .src #sidebar-button > a { + background-color: var(--main-background-color); + } + html:not(.src-sidebar-expanded) .src #sidebar-button > a:hover, + html:not(.src-sidebar-expanded) .src #sidebar-button > a:focus-visible { + background-color: var(--sidebar-background-color); + } } @@ -2890,6 +2918,7 @@ by default. --settings-button-border-focus: #717171; --sidebar-background-color: #f5f5f5; --sidebar-background-color-hover: #e0e0e0; + --sidebar-border-color: #ddd; --code-block-background-color: #f5f5f5; --scrollbar-track-background-color: #dcdcdc; --scrollbar-thumb-background-color: rgba(36, 37, 39, 0.6); @@ -2995,6 +3024,7 @@ by default. --settings-button-border-focus: #ffb900; --sidebar-background-color: #505050; --sidebar-background-color-hover: #676767; + --sidebar-border-color: #999; --code-block-background-color: #2A2A2A; --scrollbar-track-background-color: #717171; --scrollbar-thumb-background-color: rgba(32, 34, 37, .6); @@ -3107,6 +3137,7 @@ Original by Dempfi (https://github.com/dempfi/ayu) --settings-button-border-focus: #e0e0e0; --sidebar-background-color: #14191f; --sidebar-background-color-hover: rgba(70, 70, 70, 0.33); + --sidebar-border-color: #5c6773; --code-block-background-color: #191f26; --scrollbar-track-background-color: transparent; --scrollbar-thumb-background-color: #5c6773; @@ -3284,7 +3315,7 @@ Original by Dempfi (https://github.com/dempfi/ayu) } :root[data-theme="ayu"] #settings-menu > a img, -:root[data-theme="ayu"] #sidebar-button > a:before { +:root[data-theme="ayu"] #sidebar-button > a::before { filter: invert(100); } /* End theme: ayu */ diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index edfcc1291b976..a7ce2bf9048bf 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1,5 +1,5 @@ // Local js definitions: -/* global addClass, getSettingValue, hasClass, searchState, updateLocalStorage */ +/* global addClass, getSettingValue, hasClass, updateLocalStorage */ /* global onEachLazy, removeClass, getVar */ "use strict"; @@ -121,12 +121,9 @@ function getNakedUrl() { * doesn't have a parent node. * * @param {HTMLElement} newNode - * @param {HTMLElement} referenceNode + * @param {HTMLElement & { parentNode: HTMLElement }} referenceNode */ function insertAfter(newNode, referenceNode) { - // You're not allowed to pass an element with no parent. - // I dunno how to make TS's typechecker see that. - // @ts-expect-error referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } @@ -305,11 +302,10 @@ function preLoadCss(cssUrl) { window.searchState.timeout = null; } }, - // @ts-expect-error isDisplayed: () => { const outputElement = window.searchState.outputElement(); - return outputElement && - outputElement.parentElement && + return !!outputElement && + !!outputElement.parentElement && outputElement.parentElement.id === ALTERNATIVE_DISPLAY_ID; }, // Sets the focus on the search bar at the top of the page @@ -325,8 +321,6 @@ function preLoadCss(cssUrl) { search = window.searchState.outputElement(); } switchDisplayedElement(search); - // @ts-expect-error - window.searchState.mouseMovedAfterSearch = false; document.title = window.searchState.title; }, removeQueryParameters: () => { @@ -503,34 +497,40 @@ function preLoadCss(cssUrl) { handleHashes(ev); } - // @ts-expect-error + /** + * @param {HTMLElement|null} elem + */ function openParentDetails(elem) { while (elem) { if (elem.tagName === "DETAILS") { + // @ts-expect-error elem.open = true; } - elem = elem.parentNode; + elem = elem.parentElement; } } - // @ts-expect-error + /** + * @param {string} id + */ function expandSection(id) { openParentDetails(document.getElementById(id)); } - // @ts-expect-error + /** + * @param {KeyboardEvent} ev + */ function handleEscape(ev) { - // @ts-expect-error - searchState.clearInputTimeout(); - // @ts-expect-error - searchState.hideResults(); + window.searchState.clearInputTimeout(); + window.searchState.hideResults(); ev.preventDefault(); - // @ts-expect-error - searchState.defocus(); + window.searchState.defocus(); window.hideAllModals(true); // true = reset focus for tooltips } - // @ts-expect-error + /** + * @param {KeyboardEvent} ev + */ function handleShortcut(ev) { // Don't interfere with browser shortcuts const disableShortcuts = getSettingValue("disable-shortcuts") === "true"; @@ -538,8 +538,8 @@ function preLoadCss(cssUrl) { return; } - // @ts-expect-error - if (document.activeElement.tagName === "INPUT" && + if (document.activeElement && + document.activeElement.tagName === "INPUT" && // @ts-expect-error document.activeElement.type !== "checkbox" && // @ts-expect-error @@ -559,8 +559,7 @@ function preLoadCss(cssUrl) { case "S": case "/": ev.preventDefault(); - // @ts-expect-error - searchState.focus(); + window.searchState.focus(); break; case "+": @@ -586,7 +585,6 @@ function preLoadCss(cssUrl) { document.addEventListener("keydown", handleShortcut); function addSidebarItems() { - // @ts-expect-error if (!window.SIDEBAR_ITEMS) { return; } @@ -675,7 +673,6 @@ function preLoadCss(cssUrl) { } // - // @ts-expect-error window.register_implementors = imp => { const implementors = document.getElementById("implementors-list"); const synthetic_implementors = document.getElementById("synthetic-implementors-list"); @@ -767,9 +764,7 @@ function preLoadCss(cssUrl) { } } }; - // @ts-expect-error if (window.pending_implementors) { - // @ts-expect-error window.register_implementors(window.pending_implementors); } @@ -802,16 +797,14 @@ function preLoadCss(cssUrl) { * * - After processing all of the impls, it sorts the sidebar items by name. * - * @param {{[cratename: string]: Array>}} imp + * @param {rustdoc.TypeImpls} imp */ - // @ts-expect-error window.register_type_impls = imp => { // @ts-expect-error if (!imp || !imp[window.currentCrate]) { return; } - // @ts-expect-error - window.pending_type_impls = null; + window.pending_type_impls = undefined; const idMap = new Map(); let implementations = document.getElementById("implementations-list"); @@ -997,9 +990,7 @@ function preLoadCss(cssUrl) { list.replaceChildren(...newChildren); } }; - // @ts-expect-error if (window.pending_type_impls) { - // @ts-expect-error window.register_type_impls(window.pending_type_impls); } @@ -1110,7 +1101,6 @@ function preLoadCss(cssUrl) { }); }()); - // @ts-expect-error window.rustdoc_add_line_numbers_to_examples = () => { // @ts-expect-error function generateLine(nb) { @@ -1132,7 +1122,6 @@ function preLoadCss(cssUrl) { }); }; - // @ts-expect-error window.rustdoc_remove_line_numbers_from_examples = () => { onEachLazy( document.querySelectorAll(".rustdoc:not(.src) :not(.scraped-example) > .example-wrap"), @@ -1141,7 +1130,6 @@ function preLoadCss(cssUrl) { }; if (getSettingValue("line-numbers") === "true") { - // @ts-expect-error window.rustdoc_add_line_numbers_to_examples(); } @@ -1605,7 +1593,7 @@ function preLoadCss(cssUrl) { /** * Hide popover menus, clickable tooltips, and the sidebar (if applicable). * - * Pass "true" to reset focus for tooltip popovers. + * Pass `true` to reset focus for tooltip popovers. */ window.hideAllModals = switchFocus => { hideSidebar(); @@ -1695,8 +1683,7 @@ function preLoadCss(cssUrl) { addSidebarCrates(); onHashChange(null); window.addEventListener("hashchange", onHashChange); - // @ts-expect-error - searchState.setup(); + window.searchState.setup(); }()); // Hide, show, and resize the sidebar diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index acea7828e86a9..0d2e19e019f34 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -4,9 +4,17 @@ /* eslint-disable */ declare global { + /** Map from crate name to directory structure, for source view */ + declare var srcIndex: Map; + /** Defined and documented in `storage.js` */ + declare function nonnull(x: T|null, msg: string|undefined); + /** Defined and documented in `storage.js` */ + declare function nonundef(x: T|undefined, msg: string|undefined); interface Window { /** Make the current theme easy to find */ currentTheme: HTMLLinkElement|null; + /** Generated in `render/context.rs` */ + SIDEBAR_ITEMS?: { [key: string]: string[] }; /** Used by the popover tooltip code. */ RUSTDOC_TOOLTIP_HOVER_MS: number; /** Used by the popover tooltip code. */ @@ -22,6 +30,8 @@ declare global { currentCrate: string|null; /** * Hide popovers, tooltips, or the mobile sidebar. + * + * Pass `true` to reset focus for tooltip popovers. */ hideAllModals: function(boolean), /** @@ -38,10 +48,40 @@ declare global { * or if this is a docs page, this function does nothing. */ rustdocShowSourceSidebar: function(), + /** + * Close the sidebar in source code view + */ + rustdocCloseSourceSidebar?: function(), + /** + * Shows the sidebar in source code view + */ + rustdocShowSourceSidebar?: function(), + /** + * Toggles the sidebar in source code view + */ + rustdocToggleSrcSidebar?: function(), + /** + * create's the sidebar in source code view. + * called in generated `src-files.js`. + */ + createSrcSidebar?: function(), /** * Set up event listeners for a scraped source example. */ updateScrapedExample?: function(HTMLElement, HTMLElement), + /** + * register trait implementors, called by code generated in + * `write_shared.rs` + */ + register_implementors?: function(rustdoc.Implementors): void, + /** + * fallback in case `register_implementors` isn't defined yet. + */ + pending_implementors?: rustdoc.Implementors, + register_type_impls?: function(rustdoc.TypeImpls): void, + pending_type_impls?: rustdoc.TypeImpls, + rustdoc_add_line_numbers_to_examples?: function(), + rustdoc_remove_line_numbers_from_examples?: function(), } interface HTMLElement { /** Used by the popover tooltip code. */ @@ -123,7 +163,7 @@ declare namespace rustdoc { * Same as QueryElement, but bindings and typeFilter support strings */ interface ParserQueryElement { - name: string, + name: string|null, id: number|null, fullPath: Array, pathWithoutLast: Array, @@ -131,10 +171,16 @@ declare namespace rustdoc { normalizedPathLast: string, generics: Array, bindings: Map>, - bindingName: {name: string, generics: ParserQueryElement[]}|null, - typeFilter: string|null, + bindingName: {name: string|null, generics: ParserQueryElement[]}|null, + typeFilter: number|string|null, } + /** + * Same as ParserQueryElement, but all fields are optional. + */ + type ParserQueryElementFields = { + [K in keyof ParserQueryElement]?: ParserQueryElement[T] + } /** * Intermediate parser state. Discarded when parsing is done. */ @@ -176,10 +222,11 @@ declare namespace rustdoc { name: string, normalizedName: string, word: string, + paramNames: string[], parent: ({ty: number, name: string, path: string, exactPath: string}|null|undefined), path: string, ty: number, - type?: FunctionSearchType + type: FunctionSearchType | null, } /** @@ -214,12 +261,20 @@ declare namespace rustdoc { ty: number, type?: FunctionSearchType, paramNames?: string[], - displayType: Promise>>|null, - displayTypeMappedNames: Promise]>>|null, + displayTypeSignature: Promise | null, item: Row, dontValidate?: boolean, } + /** + * output of `formatDisplayTypeSignature` + */ + interface DisplayTypeSignature { + type: Array, + mappedNames: Map, + whereClause: Map>, + } + /** * A pair of [inputs, outputs], or 0 for null. This is stored in the search index. * The JavaScript deserializes this into FunctionSearchType. @@ -390,7 +445,7 @@ declare namespace rustdoc { */ type RawSearchIndexCrate = { doc: string, - a: Object, + a: { [key: string]: number[] }, n: Array, t: string, D: string, @@ -406,4 +461,34 @@ declare namespace rustdoc { }; type VlqData = VlqData[] | number; + + /** + * Maps from crate names to trait implementation data. + * Provied by generated `trait.impl` files. + */ + type Implementors = { + [key: string]: Array<[string, number, Array]> + } + + type TypeImpls = { + [cratename: string]: Array> + } + + /** + * Directory structure for source code view, + * defined in generated `src-files.js`. + * + * is a tuple of (filename, subdirs, filenames). + */ + type Dir = [string, rustdoc.Dir[], string[]] + + /** + * Indivitual setting object, used in `settings.js` + */ + interface Setting { + js_name: string, + name: string, + options?: string[], + default: string | boolean, + } } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index ccbd6811b0712..dce5fddb3177e 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -33,6 +33,20 @@ function onEachBtwn(arr, func, funcBtwn) { } } +/** + * Convert any `undefined` to `null`. + * + * @template T + * @param {T|undefined} x + * @returns {T|null} + */ +function undef2null(x) { + if (x !== undefined) { + return x; + } + return null; +} + // ==================== Core search logic begin ==================== // This mapping table should match the discriminants of // `rustdoc::formats::item_type::ItemType` type in Rust. @@ -338,7 +352,7 @@ function getFilteredNextElem(query, parserState, elems, isInGenerics) { // The type filter doesn't count as an element since it's a modifier. const typeFilterElem = elems.pop(); checkExtraTypeFilterCharacters(start, parserState); - // typeFilterElem is not null. If it was, the elems.length check would have fired. + // typeFilterElem is not undefined. If it was, the elems.length check would have fired. // @ts-expect-error parserState.typeFilter = typeFilterElem.normalizedPathLast; parserState.pos += 1; @@ -638,7 +652,6 @@ function getNextElem(query, parserState, elems, isInGenerics) { getFilteredNextElem(query, parserState, generics, isInGenerics); generics[generics.length - 1].bindingName = makePrimitiveElement("output"); } else { - // @ts-expect-error generics.push(makePrimitiveElement(null, { bindingName: makePrimitiveElement("output"), typeFilter: null, @@ -791,7 +804,7 @@ function createQueryElement(query, parserState, name, generics, isInGenerics) { generics: generics.filter(gen => { // Syntactically, bindings are parsed as generics, // but the query engine treats them differently. - if (gen.bindingName !== null) { + if (gen.bindingName !== null && gen.bindingName.name !== null) { if (gen.name !== null) { gen.bindingName.generics.unshift(gen); } @@ -811,8 +824,8 @@ function createQueryElement(query, parserState, name, generics, isInGenerics) { /** * - * @param {string} name - * @param {Object=} extra + * @param {string|null} name + * @param {rustdoc.ParserQueryElementFields=} extra * @returns {rustdoc.ParserQueryElement} */ function makePrimitiveElement(name, extra) { @@ -1312,10 +1325,9 @@ class NameTrie { let sste; if (substart >= 2) { const tail = name.substring(substart - 2, substart + 1); - if (tailTable.has(tail)) { - // it's not undefined - // @ts-expect-error - sste = tailTable.get(tail); + const entry = tailTable.get(tail); + if (entry !== undefined) { + sste = entry; } else { sste = []; tailTable.set(tail, sste); @@ -1339,10 +1351,9 @@ class NameTrie { new Lev1TParametricDescription(name.length); this.searchLev(name, 0, levParams, results); const tail = name.substring(0, 3); - if (tailTable.has(tail)) { - // it's not undefined - // @ts-expect-error - for (const entry of tailTable.get(tail)) { + const list = tailTable.get(tail); + if (list !== undefined) { + for (const entry of list) { entry.searchSubstringPrefix(name, 3, results); } } @@ -1478,73 +1489,61 @@ class DocSearch { * Special type name IDs for searching by array. * @type {number} */ - // @ts-expect-error this.typeNameIdOfArray = this.buildTypeMapIndex("array"); /** * Special type name IDs for searching by slice. * @type {number} */ - // @ts-expect-error this.typeNameIdOfSlice = this.buildTypeMapIndex("slice"); /** * Special type name IDs for searching by both array and slice (`[]` syntax). * @type {number} */ - // @ts-expect-error this.typeNameIdOfArrayOrSlice = this.buildTypeMapIndex("[]"); /** * Special type name IDs for searching by tuple. * @type {number} */ - // @ts-expect-error this.typeNameIdOfTuple = this.buildTypeMapIndex("tuple"); /** * Special type name IDs for searching by unit. * @type {number} */ - // @ts-expect-error this.typeNameIdOfUnit = this.buildTypeMapIndex("unit"); /** * Special type name IDs for searching by both tuple and unit (`()` syntax). * @type {number} */ - // @ts-expect-error this.typeNameIdOfTupleOrUnit = this.buildTypeMapIndex("()"); /** * Special type name IDs for searching `fn`. * @type {number} */ - // @ts-expect-error this.typeNameIdOfFn = this.buildTypeMapIndex("fn"); /** * Special type name IDs for searching `fnmut`. * @type {number} */ - // @ts-expect-error this.typeNameIdOfFnMut = this.buildTypeMapIndex("fnmut"); /** * Special type name IDs for searching `fnonce`. * @type {number} */ - // @ts-expect-error this.typeNameIdOfFnOnce = this.buildTypeMapIndex("fnonce"); /** * Special type name IDs for searching higher order functions (`->` syntax). * @type {number} */ - // @ts-expect-error this.typeNameIdOfHof = this.buildTypeMapIndex("->"); /** * Special type name IDs the output assoc type. * @type {number} */ - // @ts-expect-error this.typeNameIdOfOutput = this.buildTypeMapIndex("output", true); /** * Special type name IDs for searching by reference. * @type {number} */ - // @ts-expect-error this.typeNameIdOfReference = this.buildTypeMapIndex("reference"); /** @@ -1586,7 +1585,6 @@ class DocSearch { /** * @type {Array} */ - // @ts-expect-error this.searchIndex = this.buildIndex(rawSearchIndex); } @@ -1598,20 +1596,24 @@ class DocSearch { * done more quickly. Two types with the same name but different item kinds * get the same ID. * - * @param {string} name + * @template T extends string + * @overload + * @param {T} name * @param {boolean=} isAssocType - True if this is an assoc type + * @returns {T extends "" ? null : number} + * + * @param {string} name + * @param {boolean=} isAssocType + * @returns {number | null} * - * @returns {number?} */ buildTypeMapIndex(name, isAssocType) { if (name === "" || name === null) { return null; } - if (this.typeNameIdMap.has(name)) { - /** @type {{id: number, assocOnly: boolean}} */ - // @ts-expect-error - const obj = this.typeNameIdMap.get(name); + const obj = this.typeNameIdMap.get(name); + if (obj !== undefined) { obj.assocOnly = !!(isAssocType && obj.assocOnly); return obj.id; } else { @@ -1909,6 +1911,7 @@ class DocSearch { * Convert raw search index into in-memory search index. * * @param {Map} rawSearchIndex + * @returns {rustdoc.Row[]} */ buildIndex(rawSearchIndex) { /** @@ -2008,6 +2011,7 @@ class DocSearch { return cb; }; + /** @type {rustdoc.Row[]} */ const searchIndex = []; let currentIndex = 0; let id = 0; @@ -2088,7 +2092,8 @@ class DocSearch { descIndex += 1; } - // a String of one character item type codes + // see `RawSearchIndexCrate` in `rustdoc.d.ts` for a more + // up to date description of these fields const itemTypes = crateCorpus.t; // an array of (String) item names const itemNames = crateCorpus.n; @@ -2105,11 +2110,7 @@ class DocSearch { const itemParentIdxDecoder = new VlqHexDecoder(crateCorpus.i, noop => noop); // a map Number, string for impl disambiguators const implDisambiguator = new Map(crateCorpus.b); - // an array of [(Number) item type, - // (String) name] const rawPaths = crateCorpus.p; - // an array of [(String) alias name - // [Number] index to items] const aliases = crateCorpus.a; // an array of [(Number) item index, // (String) comma-separated list of function generic param names] @@ -2147,30 +2148,31 @@ class DocSearch { // convert `rawPaths` entries into object form // generate normalizedPaths for function search mode let len = rawPaths.length; - let lastPath = itemPaths.get(0); + let lastPath = undef2null(itemPaths.get(0)); for (let i = 0; i < len; ++i) { const elem = rawPaths[i]; const ty = elem[0]; const name = elem[1]; - let path = null; - if (elem.length > 2 && elem[2] !== null) { - // @ts-expect-error - path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath; - lastPath = path; - } - let exactPath = elem.length > 3 && elem[3] !== null ? - // @ts-expect-error - itemPaths.get(elem[3]) : - path; + /** + * @param {2|3} idx + * @param {string|null} if_null + * @param {string|null} if_not_found + * @returns {string|null} + */ + const elemPath = (idx, if_null, if_not_found) => { + if (elem.length > idx && elem[idx] !== undefined) { + const p = itemPaths.get(elem[idx]); + if (p !== undefined) { + return p; + } + return if_not_found; + } + return if_null; + }; + const path = elemPath(2, lastPath, null); + const exactPath = elemPath(3, path, path); const unboxFlag = elem.length > 4 && !!elem[4]; - if (path === undefined) { - path = null; - } - if (exactPath === undefined) { - exactPath = null; - } - lowercasePaths.push({ ty, name: name.toLowerCase(), path, exactPath, unboxFlag }); paths[i] = { ty, name, path, exactPath, unboxFlag }; } @@ -2204,12 +2206,11 @@ class DocSearch { } const name = itemNames[i] === "" ? lastName : itemNames[i]; const word = itemNames[i] === "" ? lastWord : itemNames[i].toLowerCase(); - /** @type {string} */ - // @ts-expect-error - const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; - const paramNames = itemParamNames.has(i) ? - // @ts-expect-error - itemParamNames.get(i).split(",") : + const pathU = itemPaths.get(i); + const path = pathU !== undefined ? pathU : lastPath; + const paramNameString = itemParamNames.get(i); + const paramNames = paramNameString !== undefined ? + paramNameString.split(",") : lastParamNames; const type = itemFunctionDecoder.next(); if (type !== null) { @@ -2232,6 +2233,7 @@ class DocSearch { // object defined above. const itemParentIdx = itemParentIdxDecoder.next(); normalizedName = word.indexOf("_") === -1 ? word : word.replace(/_/g, ""); + /** @type {rustdoc.Row} */ const row = { crate, ty: itemTypes.charCodeAt(i) - 65, // 65 = "A" @@ -2250,8 +2252,7 @@ class DocSearch { word, normalizedName, bitIndex, - implDisambiguator: implDisambiguator.has(i) ? - implDisambiguator.get(i) : null, + implDisambiguator: undef2null(implDisambiguator.get(i)), }; this.nameTrie.insert(normalizedName, id, this.tailTable); id += 1; @@ -2274,16 +2275,14 @@ class DocSearch { continue; } - // @ts-expect-error + /** @type{number[]} */ let currentNameAliases; if (currentCrateAliases.has(alias_name)) { currentNameAliases = currentCrateAliases.get(alias_name); } else { currentNameAliases = []; - // @ts-expect-error currentCrateAliases.set(alias_name, currentNameAliases); } - // @ts-expect-error for (const local_alias of aliases[alias_name]) { currentNameAliases.push(local_alias + currentIndex); } @@ -2326,15 +2325,13 @@ class DocSearch { * @param {rustdoc.ParserQueryElement} elem */ function convertTypeFilterOnElem(elem) { - if (elem.typeFilter !== null) { + if (typeof elem.typeFilter === "string") { let typeFilter = elem.typeFilter; if (typeFilter === "const") { typeFilter = "constant"; } - // @ts-expect-error elem.typeFilter = itemTypeFromName(typeFilter); } else { - // @ts-expect-error elem.typeFilter = NO_TYPE_FILTER; } for (const elem2 of elem.generics) { @@ -2407,9 +2404,9 @@ class DocSearch { continue; } if (!foundStopChar) { - let extra = ""; + /** @type String[] */ + let extra = []; if (isLastElemGeneric(query.elems, parserState)) { - // @ts-expect-error extra = [" after ", ">"]; } else if (prevIs(parserState, "\"")) { throw ["Cannot have more than one element if you use quotes"]; @@ -2493,9 +2490,13 @@ class DocSearch { } } catch (err) { query = newParsedQuery(userQuery); - // is string list - // @ts-expect-error - query.error = err; + if (Array.isArray(err) && err.every(elem => typeof elem === "string")) { + query.error = err; + } else { + // rethrow the error if it isn't a string array + throw err; + } + return query; } if (!query.literalSearch) { @@ -2547,7 +2548,7 @@ class DocSearch { * See `buildTypeMapIndex` for more information. * * @param {rustdoc.QueryElement} elem - * @param {boolean} isAssocType + * @param {boolean=} isAssocType */ const convertNameToId = (elem, isAssocType) => { const loweredName = elem.pathLast.toLowerCase(); @@ -2583,9 +2584,9 @@ class DocSearch { if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 && elem.generics.length === 0 && elem.bindings.size === 0) || elem.typeFilter === TY_GENERIC) { - if (genericSymbols.has(elem.normalizedPathLast)) { - // @ts-expect-error - elem.id = genericSymbols.get(elem.normalizedPathLast); + const id = genericSymbols.get(elem.normalizedPathLast); + if (id !== undefined) { + elem.id = id; } else { elem.id = -(genericSymbols.size + 1); genericSymbols.set(elem.normalizedPathLast, elem.id); @@ -2627,7 +2628,6 @@ class DocSearch { ]; } for (const elem2 of elem.generics) { - // @ts-expect-error convertNameToId(elem2); } elem.bindings = new Map(Array.from(elem.bindings.entries()) @@ -2750,7 +2750,11 @@ class DocSearch { return [displayPath, href, `${exactPath}::${name}`]; }; - // @ts-expect-error + /** + * + * @param {string} path + * @returns {string} + */ function pathSplitter(path) { const tmp = "" + path.replace(/::/g, "::"); if (tmp.endsWith("")) { @@ -2763,9 +2767,9 @@ class DocSearch { * Add extra data to result objects, and filter items that have been * marked for removal. * - * @param {[rustdoc.ResultObject]} results + * @param {rustdoc.ResultObject[]} results * @param {"sig"|"elems"|"returned"|null} typeInfo - * @returns {[rustdoc.ResultObject]} + * @returns {rustdoc.ResultObject[]} */ const transformResults = (results, typeInfo) => { const duplicates = new Set(); @@ -2774,15 +2778,28 @@ class DocSearch { for (const result of results) { if (result.id !== -1) { const res = buildHrefAndPath(this.searchIndex[result.id]); + // many of these properties don't strictly need to be + // copied over, but copying them over satisfies tsc, + // and hopefully plays nice with the shape optimization + // of the browser engine. + /** @type {rustdoc.ResultObject} */ const obj = Object.assign({ + parent: result.parent, + type: result.type, dist: result.dist, + path_dist: result.path_dist, + index: result.index, + desc: result.desc, + item: result.item, displayPath: pathSplitter(res[0]), + fullPath: "", + href: "", + displayTypeSignature: null, }, this.searchIndex[result.id]); // To be sure than it some items aren't considered as duplicate. - // @ts-expect-error obj.fullPath = res[2] + "|" + obj.ty; - // @ts-expect-error + if (duplicates.has(obj.fullPath)) { continue; } @@ -2795,18 +2812,15 @@ class DocSearch { if (duplicates.has(res[2] + "|" + TY_IMPORT)) { continue; } - // @ts-expect-error duplicates.add(obj.fullPath); duplicates.add(res[2]); if (typeInfo !== null) { - // @ts-expect-error obj.displayTypeSignature = // @ts-expect-error this.formatDisplayTypeSignature(obj, typeInfo); } - // @ts-expect-error obj.href = res[1]; out.push(obj); if (out.length >= MAX_RESULTS) { @@ -2814,7 +2828,6 @@ class DocSearch { } } } - // @ts-expect-error return out; }; @@ -2827,11 +2840,7 @@ class DocSearch { * * @param {rustdoc.ResultObject} obj * @param {"sig"|"elems"|"returned"|null} typeInfo - * @returns {Promise<{ - * "type": Array, - * "mappedNames": Map, - * "whereClause": Map>, - * }>} + * @returns {Promise} */ this.formatDisplayTypeSignature = async(obj, typeInfo) => { const objType = obj.type; @@ -2840,7 +2849,7 @@ class DocSearch { } let fnInputs = null; let fnOutput = null; - // @ts-expect-error + /** @type {Map | null} */ let mgens = null; if (typeInfo !== "elems" && typeInfo !== "returned") { fnInputs = unifyFunctionTypes( @@ -2893,7 +2902,7 @@ class DocSearch { const whereClause = new Map(); const fnParamNames = obj.paramNames || []; - // @ts-expect-error + /** @type {string[]} */ const queryParamNames = []; /** * Recursively writes a map of IDs to query generic names, @@ -2926,7 +2935,7 @@ class DocSearch { * index 2 is not highlighted, etc. * * @param {{name?: string, highlighted?: boolean}} fnType - input - * @param {[string]} result + * @param {string[]} result */ const pushText = (fnType, result) => { // If !!(result.length % 2) == false, then pushing a new slot starts an even @@ -2938,7 +2947,6 @@ class DocSearch { // needs coerced to a boolean. if (!!(result.length % 2) === !!fnType.highlighted) { result.push(""); - // @ts-expect-error } else if (result.length === 0 && !!fnType.highlighted) { result.push(""); result.push(""); @@ -2952,7 +2960,7 @@ class DocSearch { * or a trait bound on Fn, FnMut, or FnOnce. * * @param {rustdoc.HighlightedFunctionType} fnType - input - * @param {[string]} result + * @param {string[]} result */ const writeHof = (fnType, result) => { const hofOutput = fnType.bindings.get(this.typeNameIdOfOutput) || []; @@ -2992,7 +3000,7 @@ class DocSearch { * Returns `false` if the supplied type isn't special. * * @param {rustdoc.HighlightedFunctionType} fnType - * @param {[string]} result + * @param {string[]} result */ const writeSpecialPrimitive = (fnType, result) => { if (fnType.id === this.typeNameIdOfArray || fnType.id === this.typeNameIdOfSlice || @@ -3039,7 +3047,7 @@ class DocSearch { * updating the where clause and generic type param map. * * @param {rustdoc.HighlightedFunctionType} fnType - * @param {[string]} result + * @param {string[]} result */ const writeFn = (fnType, result) => { if (fnType.id !== null && fnType.id < 0) { @@ -3053,12 +3061,10 @@ class DocSearch { writeFn(nested, result); } return; - // @ts-expect-error } else if (mgens) { for (const [queryId, fnId] of mgens) { if (fnId === fnType.id) { mappedNames.set( - // @ts-expect-error queryParamNames[-1 - queryId], fnParamNames[-1 - fnType.id], ); @@ -3069,17 +3075,15 @@ class DocSearch { name: fnParamNames[-1 - fnType.id], highlighted: !!fnType.highlighted, }, result); - // @ts-expect-error + /** @type{string[]} */ const where = []; onEachBtwn( fnType.generics, - // @ts-expect-error nested => writeFn(nested, where), // @ts-expect-error () => pushText({ name: " + ", highlighted: false }, where), ); if (where.length > 0) { - // @ts-expect-error whereClause.set(fnParamNames[-1 - fnType.id], where); } } else { @@ -3109,7 +3113,6 @@ class DocSearch { // shown in the where clause and name mapping output, but is // redundant in this spot for (const value of values) { - // @ts-expect-error writeFn(value, []); } return true; @@ -3151,26 +3154,22 @@ class DocSearch { } } }; - // @ts-expect-error + /** @type {string[]} */ const type = []; onEachBtwn( fnInputs, - // @ts-expect-error fnType => writeFn(fnType, type), // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, type), ); - // @ts-expect-error pushText({ name: " -> ", highlighted: false }, type); onEachBtwn( fnOutput, - // @ts-expect-error fnType => writeFn(fnType, type), // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, type), ); - // @ts-expect-error return {type, mappedNames, whereClause}; }; @@ -3181,7 +3180,7 @@ class DocSearch { * @param {rustdoc.Results} results * @param {"sig"|"elems"|"returned"|null} typeInfo * @param {string} preferredCrate - * @returns {Promise<[rustdoc.ResultObject]>} + * @returns {Promise} */ const sortResults = async(results, typeInfo, preferredCrate) => { const userQuery = parsedQuery.userQuery; @@ -3337,7 +3336,6 @@ class DocSearch { return 0; }); - // @ts-expect-error return transformResults(result_list, typeInfo); }; @@ -3486,8 +3484,7 @@ class DocSearch { } } } - // @ts-expect-error - return false; + return null; } // Multiple element recursive case @@ -3543,7 +3540,7 @@ class DocSearch { } /** @type {rustdoc.HighlightedFunctionType[]|null} */ let unifiedGenerics = []; - // @ts-expect-error + /** @type {null|Map} */ let unifiedGenericsMgens = null; /** @type {rustdoc.HighlightedFunctionType[]|null} */ const passesUnification = unifyFunctionTypes( @@ -3597,7 +3594,6 @@ class DocSearch { // @ts-expect-error queryElem.bindings.get(k), whereClause, - // @ts-expect-error unifiedGenericsMgens, solutionCb, unboxingDepth, @@ -3995,7 +3991,7 @@ class DocSearch { } const fnTypeBindings = fnType.bindings.get(name); mgensSolutionSet = mgensSolutionSet.flatMap(mgens => { - // @ts-expect-error + /** @type{Array | null>} */ const newSolutions = []; unifyFunctionTypes( // @ts-expect-error @@ -4011,7 +4007,6 @@ class DocSearch { }, unboxingDepth, ); - // @ts-expect-error return newSolutions; }); } @@ -4259,6 +4254,7 @@ class DocSearch { return false; } + // this does not yet have a type in `rustdoc.d.ts`. // @ts-expect-error function createAliasFromItem(item) { return { diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 5f1bbd27328cb..2430b5829b2ba 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -1,22 +1,39 @@ // Local js definitions: /* global getSettingValue, updateLocalStorage, updateTheme */ /* global addClass, removeClass, onEach, onEachLazy */ -/* global MAIN_ID, getVar, getSettingsButton, getHelpButton */ - -// Eventually fix this. -// @ts-nocheck +/* global MAIN_ID, getVar, getSettingsButton, getHelpButton, nonnull */ "use strict"; (function() { const isSettingsPage = window.location.pathname.endsWith("/settings.html"); + /** + * @param {Element} elem + * @param {EventTarget|null} target + */ + function elemContainsTarget(elem, target) { + if (target instanceof Node) { + return elem.contains(target); + } else { + return false; + } + } + + /** + * @overload {"theme"|"preferred-dark-theme"|"preferred-light-theme"} + * @param {string} settingName + * @param {string} value + * @returns + * @param {string} settingName + * @param {string|boolean} value + */ function changeSetting(settingName, value) { if (settingName === "theme") { const useSystem = value === "system preference" ? "true" : "false"; updateLocalStorage("use-system-theme", useSystem); } - updateLocalStorage(settingName, value); + updateLocalStorage(settingName, "" + value); switch (settingName) { case "theme": @@ -27,9 +44,15 @@ break; case "line-numbers": if (value === true) { - window.rustdoc_add_line_numbers_to_examples(); + const f = window.rustdoc_add_line_numbers_to_examples; + if (f !== undefined) { + f(); + } } else { - window.rustdoc_remove_line_numbers_from_examples(); + const f = window.rustdoc_remove_line_numbers_from_examples; + if (f !== undefined) { + f(); + } } break; case "hide-sidebar": @@ -89,6 +112,9 @@ } } + /** + * @param {HTMLElement} settingsElement + */ function setEvents(settingsElement) { updateLightAndDark(); onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"), toggle => { @@ -101,23 +127,27 @@ changeSetting(toggle.id, toggle.checked); }; }); - onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => { - const settingId = elem.name; - let settingValue = getSettingValue(settingId); - if (settingId === "theme") { - const useSystem = getSettingValue("use-system-theme"); - if (useSystem === "true" || settingValue === null) { - // "light" is the default theme - settingValue = useSystem === "false" ? "light" : "system preference"; + onEachLazy( + settingsElement.querySelectorAll("input[type=\"radio\"]"), + /** @param {HTMLInputElement} elem */ + elem => { + const settingId = elem.name; + let settingValue = getSettingValue(settingId); + if (settingId === "theme") { + const useSystem = getSettingValue("use-system-theme"); + if (useSystem === "true" || settingValue === null) { + // "light" is the default theme + settingValue = useSystem === "false" ? "light" : "system preference"; + } } - } - if (settingValue !== null && settingValue !== "null") { - elem.checked = settingValue === elem.value; - } - elem.addEventListener("change", ev => { - changeSetting(ev.target.name, ev.target.value); - }); - }); + if (settingValue !== null && settingValue !== "null") { + elem.checked = settingValue === elem.value; + } + elem.addEventListener("change", () => { + changeSetting(elem.name, elem.value); + }); + }, + ); } /** @@ -125,7 +155,7 @@ * as argument which describes each setting and how to render it. It returns a string * representing the raw HTML. * - * @param {Array} settings + * @param {Array} settings * * @return {string} */ @@ -133,11 +163,6 @@ let output = ""; for (const setting of settings) { - if (setting === "hr") { - output += "
    "; - continue; - } - const js_data_name = setting["js_name"]; const setting_name = setting["name"]; @@ -182,7 +207,9 @@ * @return {HTMLElement} */ function buildSettingsPage() { - const theme_names = getVar("themes").split(",").filter(t => t); + const theme_list = getVar("themes"); + const theme_names = (theme_list === null ? "" : theme_list) + .split(",").filter(t => t); theme_names.push("light", "dark", "ayu"); const settings = [ @@ -272,10 +299,16 @@ el.innerHTML = innerHTML; if (isSettingsPage) { - document.getElementById(MAIN_ID).appendChild(el); + const mainElem = document.getElementById(MAIN_ID); + if (mainElem !== null) { + mainElem.appendChild(el); + } } else { el.setAttribute("tabindex", "-1"); - getSettingsButton().appendChild(el); + const settingsBtn = getSettingsButton(); + if (settingsBtn !== null) { + settingsBtn.appendChild(el); + } } return el; } @@ -293,34 +326,44 @@ }); } + /** + * @param {FocusEvent} event + */ function settingsBlurHandler(event) { - if (!getHelpButton().contains(document.activeElement) && - !getHelpButton().contains(event.relatedTarget) && - !getSettingsButton().contains(document.activeElement) && - !getSettingsButton().contains(event.relatedTarget) - ) { + const helpBtn = getHelpButton(); + const settingsBtn = getSettingsButton(); + const helpUnfocused = helpBtn === null || + (!helpBtn.contains(document.activeElement) && + !elemContainsTarget(helpBtn, event.relatedTarget)); + const settingsUnfocused = settingsBtn === null || + (!settingsBtn.contains(document.activeElement) && + !elemContainsTarget(settingsBtn, event.relatedTarget)); + if (helpUnfocused && settingsUnfocused) { window.hidePopoverMenus(); } } if (!isSettingsPage) { // We replace the existing "onclick" callback. - const settingsButton = getSettingsButton(); - const settingsMenu = document.getElementById("settings"); + // These elements must exist, as (outside of the settings page) + // `settings.js` is only loaded after the settings button is clicked. + const settingsButton = nonnull(getSettingsButton()); + const settingsMenu = nonnull(document.getElementById("settings")); settingsButton.onclick = event => { - if (settingsMenu.contains(event.target)) { + if (elemContainsTarget(settingsMenu, event.target)) { return; } event.preventDefault(); const shouldDisplaySettings = settingsMenu.style.display === "none"; - window.hideAllModals(); + window.hideAllModals(false); if (shouldDisplaySettings) { displaySettings(); } }; settingsButton.onblur = settingsBlurHandler; - settingsButton.querySelector("a").onblur = settingsBlurHandler; + // the settings button should always have a link in it + nonnull(settingsButton.querySelector("a")).onblur = settingsBlurHandler; onEachLazy(settingsMenu.querySelectorAll("input"), el => { el.onblur = settingsBlurHandler; }); diff --git a/src/librustdoc/html/static/js/src-script.js b/src/librustdoc/html/static/js/src-script.js index fc27241334bfe..b9ab6e85603bc 100644 --- a/src/librustdoc/html/static/js/src-script.js +++ b/src/librustdoc/html/static/js/src-script.js @@ -3,10 +3,8 @@ // Local js definitions: /* global addClass, onEachLazy, removeClass, browserSupportsHistoryApi */ -/* global updateLocalStorage, getVar */ +/* global updateLocalStorage, getVar, nonnull */ -// Eventually fix this. -// @ts-nocheck "use strict"; @@ -29,6 +27,14 @@ function closeSidebarIfMobile() { } } +/** + * @param {rustdoc.Dir} elem + * @param {HTMLElement} parent + * @param {string} fullPath + * @param {boolean} hasFoundFile + * + * @returns {boolean} - new value for hasFoundFile + */ function createDirEntry(elem, parent, fullPath, hasFoundFile) { const dirEntry = document.createElement("details"); const summary = document.createElement("summary"); @@ -95,7 +101,7 @@ window.rustdocToggleSrcSidebar = () => { // This function is called from "src-files.js", generated in `html/render/write_shared.rs`. // eslint-disable-next-line no-unused-vars function createSrcSidebar() { - const container = document.querySelector("nav.sidebar"); + const container = nonnull(document.querySelector("nav.sidebar")); const sidebar = document.createElement("div"); sidebar.id = "src-sidebar"; @@ -111,6 +117,7 @@ function createSrcSidebar() { // Focus on the current file in the source files sidebar. const selected_elem = sidebar.getElementsByClassName("selected")[0]; if (typeof selected_elem !== "undefined") { + // @ts-expect-error selected_elem.focus(); } } @@ -130,11 +137,12 @@ function highlightSrcLines() { to = from; from = tmp; } - let elem = document.getElementById(from); + const from_s = "" + from; + let elem = document.getElementById(from_s); if (!elem) { return; } - const x = document.getElementById(from); + const x = document.getElementById(from_s); if (x) { x.scrollIntoView(); } @@ -142,7 +150,7 @@ function highlightSrcLines() { removeClass(e, "line-highlighted"); }); for (let i = from; i <= to; ++i) { - elem = document.getElementById(i); + elem = document.getElementById("" + i); if (!elem) { break; } @@ -153,11 +161,12 @@ function highlightSrcLines() { const handleSrcHighlight = (function() { let prev_line_id = 0; + /** @type {function(string): void} */ const set_fragment = name => { const x = window.scrollX, y = window.scrollY; if (browserSupportsHistoryApi()) { - history.replaceState(null, null, "#" + name); + history.replaceState(null, "", "#" + name); highlightSrcLines(); } else { location.replace("#" + name); @@ -166,6 +175,7 @@ const handleSrcHighlight = (function() { window.scrollTo(x, y); }; + // @ts-expect-error return ev => { let cur_line_id = parseInt(ev.target.id, 10); // This event handler is attached to the entire line number column, but it should only @@ -191,7 +201,7 @@ const handleSrcHighlight = (function() { } else { prev_line_id = cur_line_id; - set_fragment(cur_line_id); + set_fragment("" + cur_line_id); } }; }()); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 425b915b5f94e..7611372689478 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -21,6 +21,50 @@ const settingsDataset = (function() { return settingsElement && settingsElement.dataset ? settingsElement.dataset : null; })(); +/** + * Assert that the passed value is nonnull, then return it. + * + * Takes an optional error message argument. + * + * Must be defined in this file, as it is loaded before all others. + * + * @template T + * @param {T|null} x + * @param {string=} msg + * @returns T + */ +// used in other files, not yet used in this one. +// eslint-disable-next-line no-unused-vars +function nonnull(x, msg) { + if (x === null) { + throw (msg || "unexpected null value!"); + } else { + return x; + } +} + +/** + * Assert that the passed value is not undefined, then return it. + * + * Takes an optional error message argument. + * + * Must be defined in this file, as it is loaded before all others. + * + * @template T + * @param {T|undefined} x + * @param {string=} msg + * @returns T + */ +// used in other files, not yet used in this one. +// eslint-disable-next-line no-unused-vars +function nonundef(x, msg) { + if (x === undefined) { + throw (msg || "unexpected null value!"); + } else { + return x; + } +} + /** * Get a configuration value. If it's not set, get the default. * diff --git a/src/librustdoc/html/templates/STYLE.md b/src/librustdoc/html/templates/STYLE.md index 32bacb11475cf..12c2553cffdbb 100644 --- a/src/librustdoc/html/templates/STYLE.md +++ b/src/librustdoc/html/templates/STYLE.md @@ -1,13 +1,13 @@ # Style for Templates -This directory has templates in the [Rinja templating language][rinjadoc], which is very +This directory has templates in the [Askama templating language][askamadoc], which is very similar to [Jinja2][jinjadoc]. [jinjadoc]: https://jinja.palletsprojects.com/en/3.1.x/templates/ -[rinjadoc]: https://docs.rs/rinja/latest/rinja/ +[askamadoc]: https://docs.rs/askama/latest/askama/ We want our rendered output to have as little unnecessary whitespace as -possible, so that pages load quickly. To achieve that we use Rinja's +possible, so that pages load quickly. To achieve that we use Askama's [whitespace control] features. By default, whitespace characters are removed around jinja tags (`{% %}` for example). At the end of most lines, we put an empty comment tag: `{# #}`. This causes all whitespace between the end of the @@ -18,7 +18,7 @@ remove following whitespace but not preceding. We also use the whitespace control characters in most instances of tags with control flow, for example `{% if foo %}`. -[whitespace control]: https://rinja.readthedocs.io/en/stable/configuration.html#whitespace-control +[whitespace control]: https://askama.readthedocs.io/en/stable/configuration.html#whitespace-control We want our templates to be readable, so we use indentation and newlines liberally. We indent by four spaces after opening an HTML tag _or_ a Jinja @@ -26,11 +26,11 @@ tag. In most cases an HTML tag should be followed by a newline, but if the tag has simple contents and fits with its close tag on a single line, the contents don't necessarily need a new line. -Rinja templates support quite sophisticated control flow. To keep our templates +Askama templates support quite sophisticated control flow. To keep our templates simple and understandable, we use only a subset: `if` and `for`. In particular -we avoid [assignments in the template logic][assignments] and [Rinja +we avoid [assignments in the template logic][assignments] and [Askama macros][macros]. This also may make things easier if we switch to a different Jinja-style template system in the future. -[assignments]: https://rinja.readthedocs.io/en/stable/template_syntax.html#assignments -[macros]: https://rinja.readthedocs.io/en/stable/template_syntax.html#macros +[assignments]: https://askama.readthedocs.io/en/stable/template_syntax.html#assignments +[macros]: https://askama.readthedocs.io/en/stable/template_syntax.html#macros diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index 5ef376f4acbc9..7af99e7097c37 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -114,7 +114,7 @@

    Files

    {# #} {% endif %} {{ sidebar|safe }} {# #} - {# #} + {# #}
    {% if page.css_class != "src" %}
    {% endif %} {# defined in storage.js to avoid duplicating complex UI across every page #} diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 9606ba76991a7..705f9b2202c60 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -6,15 +6,14 @@ use rustc_abi::ExternAbi; use rustc_ast::ast; -use rustc_attr_parsing::DeprecatedSince; -use rustc_hir::def::{CtorKind, DefKind}; +use rustc_attr_data_structures::{self as attrs, DeprecatedSince}; +use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefId; use rustc_metadata::rendered_const; use rustc_middle::{bug, ty}; -use rustc_span::{Pos, Symbol, sym}; +use rustc_span::{Pos, Symbol, kw}; use rustdoc_json_types::*; -use super::FullItemId; use crate::clean::{self, ItemId}; use crate::formats::FormatRenderer; use crate::formats::item_type::ItemType; @@ -44,7 +43,7 @@ impl JsonRenderer<'_> { let attrs = item.attributes(self.tcx, self.cache(), true); let span = item.span(self.tcx); let visibility = item.visibility(self.tcx); - let clean::Item { name, item_id, .. } = item; + let clean::ItemInner { name, item_id, .. } = *item.inner; let id = self.id_from_item(&item); let inner = match item.kind { clean::KeywordItem => return None, @@ -85,8 +84,8 @@ impl JsonRenderer<'_> { let lo = span.lo(self.sess()); Some(Span { filename: local_path, - begin: (lo.line, lo.col.to_usize()), - end: (hi.line, hi.col.to_usize()), + begin: (lo.line, lo.col.to_usize() + 1), + end: (hi.line, hi.col.to_usize() + 1), }) } else { None @@ -108,67 +107,6 @@ impl JsonRenderer<'_> { } } - pub(crate) fn id_from_item_default(&self, item_id: ItemId) -> Id { - self.id_from_item_inner(item_id, None, None) - } - - pub(crate) fn id_from_item_inner( - &self, - item_id: ItemId, - name: Option, - extra: Option, - ) -> Id { - let make_part = |def_id: DefId, name: Option, extra: Option| { - let name = match name { - Some(name) => Some(name), - None => { - // We need this workaround because primitive types' DefId actually refers to - // their parent module, which isn't present in the output JSON items. So - // instead, we directly get the primitive symbol - if matches!(self.tcx.def_kind(def_id), DefKind::Mod) - && let Some(prim) = self - .tcx - .get_attrs(def_id, sym::rustc_doc_primitive) - .find_map(|attr| attr.value_str()) - { - Some(prim) - } else { - self.tcx.opt_item_name(def_id) - } - } - }; - - FullItemId { def_id, name, extra } - }; - - let key = match item_id { - ItemId::DefId(did) => (make_part(did, name, extra), None), - ItemId::Blanket { for_, impl_id } => { - (make_part(impl_id, None, None), Some(make_part(for_, name, extra))) - } - ItemId::Auto { for_, trait_ } => { - (make_part(trait_, None, None), Some(make_part(for_, name, extra))) - } - }; - - let mut interner = self.id_interner.borrow_mut(); - let len = interner.len(); - *interner - .entry(key) - .or_insert_with(|| Id(len.try_into().expect("too many items in a crate"))) - } - - pub(crate) fn id_from_item(&self, item: &clean::Item) -> Id { - match item.kind { - clean::ItemKind::ImportItem(ref import) => { - let extra = - import.source.did.map(ItemId::from).map(|i| self.id_from_item_default(i)); - self.id_from_item_inner(item.item_id, item.name, extra) - } - _ => self.id_from_item_inner(item.item_id, item.name, None), - } - } - fn ids(&self, items: impl IntoIterator) -> Vec { items .into_iter() @@ -215,8 +153,8 @@ where } } -pub(crate) fn from_deprecation(deprecation: rustc_attr_parsing::Deprecation) -> Deprecation { - let rustc_attr_parsing::Deprecation { since, note, suggestion: _ } = deprecation; +pub(crate) fn from_deprecation(deprecation: attrs::Deprecation) -> Deprecation { + let attrs::Deprecation { since, note, suggestion: _ } = deprecation; let since = match since { DeprecatedSince::RustcVersion(version) => Some(version.to_string()), DeprecatedSince::Future => Some("TBD".to_owned()), @@ -238,6 +176,7 @@ impl FromClean for GenericArgs { inputs: inputs.into_json(renderer), output: output.map(|a| (*a).into_json(renderer)), }, + ReturnTypeNotation => GenericArgs::ReturnTypeNotation, } } } @@ -548,7 +487,18 @@ impl FromClean for GenericBound { } } Outlives(lifetime) => GenericBound::Outlives(convert_lifetime(lifetime)), - Use(args) => GenericBound::Use(args.into_iter().map(|arg| arg.to_string()).collect()), + Use(args) => GenericBound::Use( + args.iter() + .map(|arg| match arg { + clean::PreciseCapturingArg::Lifetime(lt) => { + PreciseCapturingArg::Lifetime(convert_lifetime(*lt)) + } + clean::PreciseCapturingArg::Param(param) => { + PreciseCapturingArg::Param(param.to_string()) + } + }) + .collect(), + ), } } } @@ -659,9 +609,13 @@ impl FromClean for FunctionSignature { let clean::FnDecl { inputs, output, c_variadic } = decl; FunctionSignature { inputs: inputs - .values .into_iter() - .map(|arg| (arg.name.to_string(), arg.type_.into_json(renderer))) + .map(|param| { + // `_` is the most sensible name for missing param names. + let name = param.name.unwrap_or(kw::Underscore).to_string(); + let type_ = param.type_.into_json(renderer); + (name, type_) + }) .collect(), output: if output.is_unit() { None } else { Some(output.into_json(renderer)) }, is_c_variadic: c_variadic, diff --git a/src/librustdoc/json/ids.rs b/src/librustdoc/json/ids.rs new file mode 100644 index 0000000000000..737148bad4e88 --- /dev/null +++ b/src/librustdoc/json/ids.rs @@ -0,0 +1,122 @@ +//! Id handling for rustdoc-json. +//! +//! Manages the creation of [`rustdoc_json_types::Id`] and the +//! fact that these don't correspond exactly to [`DefId`], because +//! [`rustdoc_json_types::Item`] doesn't correspond exactly to what +//! other phases think of as an "item". + +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_span::{Symbol, sym}; +use rustdoc_json_types as types; + +use super::JsonRenderer; +use crate::clean; + +pub(super) type IdInterner = FxHashMap; + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +/// An uninterned id. +/// +/// Each one corresponds to exactly one of both: +/// 1. [`rustdoc_json_types::Item`]. +/// 2. [`rustdoc_json_types::Id`] transitively (as each `Item` has an `Id`). +/// +/// It's *broadly* equivalent to a [`DefId`], but needs slightly more information +/// to fully disambiguate items, because sometimes we choose to split a single HIR +/// item into multiple JSON items, or have items with no corresponding HIR item. +pub(super) struct FullItemId { + /// The "main" id of the item. + /// + /// In most cases this uniquely identifies the item, the other fields are just + /// used for edge-cases. + def_id: DefId, + + /// An extra [`DefId`], which we need for: + /// + /// 1. Auto-trait impls synthesized by rustdoc. + /// 2. Blanket impls synthesized by rustdoc. + /// 3. Splitting of reexports of multiple items. + /// + /// E.g: + /// + /// ```rust + /// mod module { + /// pub struct Foo {} // Exists in type namespace + /// pub fn Foo(){} // Exists in value namespace + /// } + /// + /// pub use module::Foo; // Imports both items + /// ``` + /// + /// In HIR, the `pub use` is just 1 item, but in rustdoc-json it's 2, so + /// we need to disambiguate. + extra_id: Option, + + /// Needed for `#[rustc_doc_primitive]` modules. + /// + /// For these, 1 [`DefId`] is used for both the primitive and the fake-module + /// that holds its docs. + /// + /// N.B. This only matters when documenting the standard library with + /// `--document-private-items`. Maybe we should delete that module, and + /// remove this. + name: Option, +} + +impl JsonRenderer<'_> { + pub(crate) fn id_from_item_default(&self, item_id: clean::ItemId) -> types::Id { + self.id_from_item_inner(item_id, None, None) + } + + fn id_from_item_inner( + &self, + item_id: clean::ItemId, + name: Option, + imported_id: Option, + ) -> types::Id { + let (def_id, extra_id) = match item_id { + clean::ItemId::DefId(did) => (did, imported_id), + clean::ItemId::Blanket { for_, impl_id } => (for_, Some(impl_id)), + clean::ItemId::Auto { for_, trait_ } => (for_, Some(trait_)), + }; + + let name = match name { + Some(name) => Some(name), + None => { + // We need this workaround because primitive types' DefId actually refers to + // their parent module, which isn't present in the output JSON items. So + // instead, we directly get the primitive symbol + if matches!(self.tcx.def_kind(def_id), DefKind::Mod) + && let Some(prim) = self + .tcx + .get_attrs(def_id, sym::rustc_doc_primitive) + .find_map(|attr| attr.value_str()) + { + Some(prim) + } else { + self.tcx.opt_item_name(def_id) + } + } + }; + + let key = FullItemId { def_id, extra_id, name }; + + let mut interner = self.id_interner.borrow_mut(); + let len = interner.len(); + *interner + .entry(key) + .or_insert_with(|| types::Id(len.try_into().expect("too many items in a crate"))) + } + + pub(crate) fn id_from_item(&self, item: &clean::Item) -> types::Id { + match item.kind { + clean::ItemKind::ImportItem(ref import) => { + let imported_id = import.source.did; + self.id_from_item_inner(item.item_id, item.name, imported_id) + } + _ => self.id_from_item_inner(item.item_id, item.name, None), + } + } +} diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 6f8cf25554fd2..131a12ce228f7 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -5,6 +5,7 @@ //! docs for usage and details. mod conversions; +mod ids; mod import_finder; use std::cell::RefCell; @@ -13,10 +14,10 @@ use std::io::{BufWriter, Write, stdout}; use std::path::PathBuf; use std::rc::Rc; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::Symbol; use rustc_span::def_id::LOCAL_CRATE; use rustdoc_json_types as types; // It's important to use the FxHashMap from rustdoc_json_types here, instead of @@ -35,14 +36,6 @@ use crate::formats::cache::Cache; use crate::json::conversions::IntoJson; use crate::{clean, try_err}; -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -struct FullItemId { - def_id: DefId, - name: Option, - /// Used to distinguish imports of different items with the same name - extra: Option, -} - #[derive(Clone)] pub(crate) struct JsonRenderer<'tcx> { tcx: TyCtxt<'tcx>, @@ -55,7 +48,7 @@ pub(crate) struct JsonRenderer<'tcx> { out_dir: Option, cache: Rc, imported_items: DefIdSet, - id_interner: Rc), types::Id>>>, + id_interner: Rc>, } impl<'tcx> JsonRenderer<'tcx> { @@ -131,6 +124,58 @@ impl<'tcx> JsonRenderer<'tcx> { } } +fn target(sess: &rustc_session::Session) -> types::Target { + // Build a set of which features are enabled on this target + let globally_enabled_features: FxHashSet<&str> = + sess.unstable_target_features.iter().map(|name| name.as_str()).collect(); + + // Build a map of target feature stability by feature name + use rustc_target::target_features::Stability; + let feature_stability: FxHashMap<&str, Stability> = sess + .target + .rust_target_features() + .into_iter() + .copied() + .map(|(name, stability, _)| (name, stability)) + .collect(); + + types::Target { + triple: sess.opts.target_triple.tuple().into(), + target_features: sess + .target + .rust_target_features() + .into_iter() + .copied() + .filter(|(_, stability, _)| { + // Describe only target features which the user can toggle + stability.toggle_allowed().is_ok() + }) + .map(|(name, stability, implied_features)| { + types::TargetFeature { + name: name.into(), + unstable_feature_gate: match stability { + Stability::Unstable(feature_gate) => Some(feature_gate.as_str().into()), + _ => None, + }, + implies_features: implied_features + .into_iter() + .copied() + .filter(|name| { + // Imply only target features which the user can toggle + feature_stability + .get(name) + .map(|stability| stability.toggle_allowed().is_ok()) + .unwrap_or(false) + }) + .map(String::from) + .collect(), + globally_enabled: globally_enabled_features.contains(name), + } + }) + .collect(), + } +} + impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { fn descr() -> &'static str { "json" @@ -256,6 +301,12 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { let e = ExternalCrate { crate_num: LOCAL_CRATE }; let index = (*self.index).clone().into_inner(); + // Note that tcx.rust_target_features is inappropriate here because rustdoc tries to run for + // multiple targets: https://github.com/rust-lang/rust/pull/137632 + // + // We want to describe a single target, so pass tcx.sess rather than tcx. + let target = target(self.tcx.sess); + debug!("Constructing Output"); let output_crate = types::Crate { root: self.id_from_item_default(e.def_id().into()), @@ -296,6 +347,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { ) }) .collect(), + target, format_version: types::FORMAT_VERSION, }; if let Some(ref out_dir) = self.out_dir { diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c1e6d324d5a1a..001668c54a745 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -11,7 +11,6 @@ #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(never_type)] #![feature(round_char_boundary)] #![feature(test)] @@ -37,6 +36,7 @@ extern crate pulldown_cmark; extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_ast_pretty; +extern crate rustc_attr_data_structures; extern crate rustc_attr_parsing; extern crate rustc_data_structures; extern crate rustc_driver; @@ -73,9 +73,11 @@ extern crate tikv_jemalloc_sys as jemalloc_sys; use std::env::{self, VarError}; use std::io::{self, IsTerminal}; +use std::path::Path; use std::process; use rustc_errors::DiagCtxtHandle; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_interface::interface; use rustc_middle::ty::TyCtxt; use rustc_session::config::{ErrorOutputType, RustcOptGroup, make_crate_type_option}; @@ -147,7 +149,7 @@ pub fn main() { #[cfg(target_os = "macos")] { - extern "C" { + unsafe extern "C" { fn _rjem_je_zone_register(); } @@ -170,12 +172,28 @@ pub fn main() { // NOTE: this compiles both versions of tracing unconditionally, because // - The compile time hit is not that bad, especially compared to rustdoc's incremental times, and // - Otherwise, there's no warning that logging is being ignored when `download-rustc` is enabled - // NOTE: The reason this doesn't show double logging when `download-rustc = false` and - // `debug_logging = true` is because all rustc logging goes to its version of tracing (the one - // in the sysroot), and all of rustdoc's logging goes to its version (the one in Cargo.toml). - init_logging(&early_dcx); - rustc_driver::init_logger(&early_dcx, rustc_log::LoggerConfig::from_env("RUSTDOC_LOG")); + crate::init_logging(&early_dcx); + match rustc_log::init_logger(rustc_log::LoggerConfig::from_env("RUSTDOC_LOG")) { + Ok(()) => {} + // With `download-rustc = true` there are definitely 2 distinct tracing crates in the + // dependency graph: one in the downloaded sysroot and one built just now as a dependency of + // rustdoc. So the sysroot's tracing is definitely not yet initialized here. + // + // But otherwise, depending on link style, there may or may not be 2 tracing crates in play. + // The one we just initialized in `crate::init_logging` above is rustdoc's direct dependency + // on tracing. When rustdoc is built by x.py using Cargo, rustc_driver's and rustc_log's + // tracing dependency is distinct from this one and also needs to be initialized (using the + // same RUSTDOC_LOG environment variable for both). Other build systems may use just a + // single tracing crate throughout the rustc and rustdoc build. + // + // The reason initializing 2 tracings does not show double logging when `download-rustc = + // false` and `debug_logging = true` is because all rustc logging goes only to its version + // of tracing (the one in the sysroot) and all of rustdoc's logging only goes to its version + // (the one in Cargo.toml). + Err(rustc_log::Error::AlreadyInit(_)) => {} + Err(error) => early_dcx.early_fatal(error.to_string()), + } let exit_code = rustc_driver::catch_with_exit_code(|| { let at_args = rustc_driver::args::raw_args(&early_dcx); @@ -507,28 +525,20 @@ fn opts() -> Vec { "", ), opt( - Unstable, - FlagMulti, - "", - "enable-per-target-ignores", - "parse ignore-foo for ignoring doctests on a per-target basis", - "", - ), - opt( - Unstable, + Stable, Opt, "", - "runtool", + "test-runtool", "", "The tool to run tests with when building for a different target than host", ), opt( - Unstable, + Stable, Multi, "", - "runtool-arg", + "test-runtool-arg", "", - "One (of possibly many) arguments to pass to the runtool", + "One argument (of possibly many) to pass to the runtool", ), opt( Unstable, @@ -646,9 +656,9 @@ fn opts() -> Vec { Unstable, Multi, "", - "doctest-compilation-args", - "", - "add arguments to be used when compiling doctests", + "doctest-build-arg", + "One argument (of possibly many) to be used when compiling doctests", + "ARG", ), opt( Unstable, @@ -896,6 +906,10 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { rustc_interface::passes::write_dep_info(tcx); } + if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { + dump_feature_usage_metrics(tcx, metrics_dir); + } + if run_check { // Since we're in "check" mode, no need to generate anything beyond this point. return; @@ -915,3 +929,16 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { }) }) } + +fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &Path) { + let hash = tcxt.crate_hash(LOCAL_CRATE); + let crate_name = tcxt.crate_name(LOCAL_CRATE); + let metrics_file_name = format!("unstable_feature_usage_metrics-{crate_name}-{hash}.json"); + let metrics_path = metrics_dir.join(metrics_file_name); + if let Err(error) = tcxt.features().dump_feature_usage_metrics(metrics_path) { + // FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit + // default metrics" to only produce a warning when metrics are enabled by default and emit + // an error only when the user manually enables metrics + tcxt.dcx().err(format!("cannot emit feature usage metrics: {error}")); + } +} diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index dcc27cd62e389..b09ea05688595 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -196,14 +196,6 @@ declare_rustdoc_lint! { "detects redundant explicit links in doc comments" } -declare_rustdoc_lint! { - /// This compatibility lint checks for Markdown syntax that works in the old engine but not - /// the new one. - UNPORTABLE_MARKDOWN, - Warn, - "detects markdown that is interpreted differently in different parser" -} - pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { vec![ BROKEN_INTRA_DOC_LINKS, @@ -217,7 +209,6 @@ pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { MISSING_CRATE_LEVEL_DOCS, UNESCAPED_BACKTICKS, REDUNDANT_EXPLICIT_LINKS, - UNPORTABLE_MARKDOWN, ] }); @@ -241,4 +232,5 @@ pub(crate) fn register_lints(_sess: &Session, lint_store: &mut LintStore) { .register_renamed("intra_doc_link_resolution_failure", "rustdoc::broken_intra_doc_links"); lint_store.register_renamed("non_autolinks", "rustdoc::bare_urls"); lint_store.register_renamed("rustdoc::non_autolinks", "rustdoc::bare_urls"); + lint_store.register_removed("rustdoc::unportable_markdown", "old parser removed"); } diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 45b8dafa90792..c9f0baaaa4c1b 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -5,7 +5,7 @@ use std::ops; use rustc_hir as hir; use rustc_lint::builtin::MISSING_DOCS; -use rustc_middle::lint::LintLevelSource; +use rustc_middle::lint::{LevelAndSource, LintLevelSource}; use rustc_session::lint; use rustc_span::FileName; use serde::Serialize; @@ -212,11 +212,12 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> { let has_docs = !i.attrs.doc_strings.is_empty(); let mut tests = Tests { found_tests: 0 }; - find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, false, None); + find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, None); let has_doc_example = tests.found_tests != 0; let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap(); - let (level, source) = self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id); + let LevelAndSource { level, src, .. } = + self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id); // In case we have: // @@ -240,7 +241,7 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> { data: hir::VariantData::Tuple(_, _, _), .. }) | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(hir::VariantData::Tuple(_, _, _), _), + kind: hir::ItemKind::Struct(_, hir::VariantData::Tuple(_, _, _), _), .. }) ) @@ -251,7 +252,7 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> { // unless the user had an explicit `allow`. // let should_have_docs = !should_be_ignored - && (level != lint::Level::Allow || matches!(source, LintLevelSource::Default)); + && (level != lint::Level::Allow || matches!(src, LintLevelSource::Default)); if let Some(span) = i.span(self.ctx.tcx) { let filename = span.filename(self.ctx.sess()); diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 0fefd13f76333..8028afea363d5 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -6,7 +6,7 @@ //! - PRIVATE_DOC_TESTS: this lint is **STABLE** and looks for private items with doctests. use rustc_hir as hir; -use rustc_middle::lint::LintLevelSource; +use rustc_middle::lint::{LevelAndSource, LintLevelSource}; use rustc_session::lint; use tracing::debug; @@ -107,11 +107,11 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - { return false; } - let (level, source) = cx.tcx.lint_level_at_node( + let LevelAndSource { level, src, .. } = cx.tcx.lint_level_at_node( crate::lint::MISSING_DOC_CODE_EXAMPLES, cx.tcx.local_def_id_to_hir_id(def_id), ); - level != lint::Level::Allow || matches!(source, LintLevelSource::Default) + level != lint::Level::Allow || matches!(src, LintLevelSource::Default) } pub(crate) fn look_for_tests(cx: &DocContext<'_>, dox: &str, item: &Item) { @@ -122,7 +122,7 @@ pub(crate) fn look_for_tests(cx: &DocContext<'_>, dox: &str, item: &Item) { let mut tests = Tests { found_tests: 0 }; - find_testable_code(dox, &mut tests, ErrorCodes::No, false, None); + find_testable_code(dox, &mut tests, ErrorCodes::No, None); if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples() { if should_have_doc_example(cx, item) { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 97e6d3146427c..f3e2138d1a575 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -58,9 +58,14 @@ fn filter_assoc_items_by_name_and_namespace( assoc_items_of: DefId, ident: Ident, ns: Namespace, -) -> impl Iterator + '_ { - tcx.associated_items(assoc_items_of).filter_by_name_unhygienic(ident.name).filter(move |item| { - item.kind.namespace() == ns && tcx.hygienic_eq(ident, item.ident(tcx), assoc_items_of) +) -> impl Iterator { + let iter: Box> = if !ident.name.is_empty() { + Box::new(tcx.associated_items(assoc_items_of).filter_by_name_unhygienic(ident.name)) + } else { + Box::new([].iter()) + }; + iter.filter(move |item| { + item.namespace() == ns && tcx.hygienic_eq(ident, item.ident(tcx), assoc_items_of) }) } @@ -491,12 +496,21 @@ impl<'tcx> LinkCollector<'_, 'tcx> { // Try looking for methods and associated items. // NB: `path_root` could be empty when resolving in the root namespace (e.g. `::std`). - let (path_root, item_str) = path_str.rsplit_once("::").ok_or_else(|| { - // 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. - debug!("found no `::`, assuming {path_str} was correctly not in scope"); - UnresolvedPath { item_id, module_id, partial_res: None, unresolved: path_str.into() } - })?; + let (path_root, item_str) = match path_str.rsplit_once("::") { + Some(res @ (_path_root, item_str)) if !item_str.is_empty() => res, + _ => { + // If there's no `::`, or the `::` is at the end (e.g. `String::`) it's not an + // associated item. So we can be sure that `rustc_resolve` was accurate when it + // said it wasn't resolved. + debug!("`::` missing or at end, assuming {path_str} was not in scope"); + return Err(UnresolvedPath { + item_id, + module_id, + partial_res: None, + unresolved: path_str.into(), + }); + } + }; let item_name = Symbol::intern(item_str); // FIXME(#83862): this arbitrarily gives precedence to primitives over modules to support @@ -743,7 +757,7 @@ impl<'tcx> LinkCollector<'_, 'tcx> { ns, ) .map(|item| { - let res = Res::Def(item.kind.as_def_kind(), item.def_id); + let res = Res::Def(item.as_def_kind(), item.def_id); (res, item.def_id) }) .collect::>(), @@ -1334,14 +1348,12 @@ impl LinkCollector<'_, '_> { } // item can be non-local e.g. when using `#[rustc_doc_primitive = "pointer"]` - if let Some((src_id, dst_id)) = id.as_local().and_then(|dst_id| { - diag_info.item.item_id.expect_def_id().as_local().map(|src_id| (src_id, dst_id)) - }) { - if self.cx.tcx.effective_visibilities(()).is_exported(src_id) - && !self.cx.tcx.effective_visibilities(()).is_exported(dst_id) - { - privacy_error(self.cx, diag_info, path_str); - } + if let Some(dst_id) = id.as_local() + && let Some(src_id) = diag_info.item.item_id.expect_def_id().as_local() + && self.cx.tcx.effective_visibilities(()).is_exported(src_id) + && !self.cx.tcx.effective_visibilities(()).is_exported(dst_id) + { + privacy_error(self.cx, diag_info, path_str); } Some(()) @@ -1405,10 +1417,10 @@ impl LinkCollector<'_, '_> { // which we want in some cases but not in others. cache_errors: bool, ) -> Option)>> { - if let Some(res) = self.visited_links.get(&key) { - if res.is_some() || cache_errors { - return res.clone().map(|r| vec![r]); - } + if let Some(res) = self.visited_links.get(&key) + && (res.is_some() || cache_errors) + { + return res.clone().map(|r| vec![r]); } let mut candidates = self.resolve_with_disambiguator(&key, diag.clone()); @@ -1432,10 +1444,10 @@ impl LinkCollector<'_, '_> { // and after removing duplicated kinds, only one remains, the `ambiguity_error` function // won't emit an error. So at this point, we can just take the first candidate as it was // the first retrieved and use it to generate the link. - if let [candidate, _candidate2, ..] = *candidates { - if !ambiguity_error(self.cx, &diag, &key.path_str, &candidates, false) { - candidates = vec![candidate]; - } + if let [candidate, _candidate2, ..] = *candidates + && !ambiguity_error(self.cx, &diag, &key.path_str, &candidates, false) + { + candidates = vec![candidate]; } let mut out = Vec::with_capacity(candidates.len()); @@ -1480,17 +1492,16 @@ impl LinkCollector<'_, '_> { // See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach. let mut err = ResolutionFailure::NotResolved(err); for other_ns in [TypeNS, ValueNS, MacroNS] { - if other_ns != expected_ns { - if let Ok(&[res, ..]) = self + if other_ns != expected_ns + && let Ok(&[res, ..]) = self .resolve(path_str, other_ns, None, item_id, module_id) .as_deref() - { - err = ResolutionFailure::WrongNamespace { - res: full_res(self.cx.tcx, res), - expected_ns, - }; - break; - } + { + err = ResolutionFailure::WrongNamespace { + res: full_res(self.cx.tcx, res), + expected_ns, + }; + break; } } resolution_failure(self, diag, path_str, disambiguator, smallvec![err]); @@ -1674,11 +1685,11 @@ impl Disambiguator { Ok(Some((d, &rest[1..], &rest[1..]))) } else { for (suffix, kind) in suffixes { - if let Some(path_str) = link.strip_suffix(suffix) { - // Avoid turning `!` or `()` into an empty string - if !path_str.is_empty() { - return Ok(Some((Kind(kind), path_str, link))); - } + // Avoid turning `!` or `()` into an empty string + if let Some(path_str) = link.strip_suffix(suffix) + && !path_str.is_empty() + { + return Ok(Some((Kind(kind), path_str, link))); } } Ok(None) @@ -2060,7 +2071,7 @@ fn resolution_failure( return; } Trait - | TyAlias { .. } + | TyAlias | ForeignTy | OpaqueTy | TraitAlias diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 87f85c5731528..f4e4cd924f7ff 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -177,23 +177,22 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> } else if let Some(did) = target.def_id(&cx.cache) { cleaner.items.insert(did.into()); } - if let Some(for_did) = for_.def_id(&cx.cache) { - if type_did_to_deref_target.insert(for_did, target).is_none() { - // Since only the `DefId` portion of the `Type` instances is known to be same for both the - // `Deref` target type and the impl for type positions, this map of types is keyed by - // `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly. - if cleaner.keep_impl_with_def_id(for_did.into()) { - let mut targets = DefIdSet::default(); - targets.insert(for_did); - add_deref_target( - cx, - &type_did_to_deref_target, - &mut cleaner, - &mut targets, - for_did, - ); - } - } + if let Some(for_did) = for_.def_id(&cx.cache) + && type_did_to_deref_target.insert(for_did, target).is_none() + // Since only the `DefId` portion of the `Type` instances is known to be same for both the + // `Deref` target type and the impl for type positions, this map of types is keyed by + // `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly. + && cleaner.keep_impl_with_def_id(for_did.into()) + { + let mut targets = DefIdSet::default(); + targets.insert(for_did); + add_deref_target( + cx, + &type_did_to_deref_target, + &mut cleaner, + &mut targets, + for_did, + ); } } } diff --git a/src/librustdoc/passes/lint.rs b/src/librustdoc/passes/lint.rs index 1ecb53e61ac39..7740d14148bf0 100644 --- a/src/librustdoc/passes/lint.rs +++ b/src/librustdoc/passes/lint.rs @@ -6,7 +6,6 @@ mod check_code_block_syntax; mod html_tags; mod redundant_explicit_links; mod unescaped_backticks; -mod unportable_markdown; use super::Pass; use crate::clean::*; @@ -49,9 +48,6 @@ impl DocVisitor<'_> for Linter<'_, '_> { } if may_have_block_comment_or_html { html_tags::visit_item(self.cx, item, hir_id, &dox); - unportable_markdown::visit_item(self.cx, item, hir_id, &dox); - } else if may_have_link { - unportable_markdown::visit_item(self.cx, item, hir_id, &dox); } } diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs index 3fb154dc51549..b9739726c9569 100644 --- a/src/librustdoc/passes/lint/html_tags.rs +++ b/src/librustdoc/passes/lint/html_tags.rs @@ -28,9 +28,9 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & // We don't try to detect stuff `` because that's not valid HTML, // and we don't try to detect stuff `` because that's not valid Rust. let mut generics_end = range.end; - if let Some(Some(mut generics_start)) = (is_open_tag - && dox[..generics_end].ends_with('>')) - .then(|| extract_path_backwards(dox, range.start)) + if is_open_tag + && dox[..generics_end].ends_with('>') + && let Some(mut generics_start) = extract_path_backwards(dox, range.start) { while generics_start != 0 && generics_end < dox.len() diff --git a/src/librustdoc/passes/lint/unportable_markdown.rs b/src/librustdoc/passes/lint/unportable_markdown.rs deleted file mode 100644 index a3c3134f4c2c5..0000000000000 --- a/src/librustdoc/passes/lint/unportable_markdown.rs +++ /dev/null @@ -1,145 +0,0 @@ -//! Detects specific markdown syntax that's different between pulldown-cmark -//! 0.9 and 0.11. -//! -//! This is a mitigation for old parser bugs that affected some -//! real crates' docs. The old parser claimed to comply with CommonMark, -//! but it did not. These warnings will eventually be removed, -//! though some of them may become Clippy lints. -//! -//! -//! -//! - -use std::collections::{BTreeMap, BTreeSet}; - -use rustc_hir::HirId; -use rustc_lint_defs::Applicability; -use rustc_resolve::rustdoc::source_span_for_markdown_range; -use {pulldown_cmark as cmarkn, pulldown_cmark_old as cmarko}; - -use crate::clean::Item; -use crate::core::DocContext; - -pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &str) { - let tcx = cx.tcx; - - // P1: unintended strikethrough was fixed by requiring single-tildes to flank - // the same way underscores do, so nothing is done here - - // P2: block quotes without following space parsed wrong - // - // This is the set of starting points for block quotes with no space after - // the `>`. It is populated by the new parser, and if the old parser fails to - // clear it out, it'll produce a warning. - let mut spaceless_block_quotes = BTreeSet::new(); - - // P3: missing footnote references - // - // This is populated by listening for FootnoteReference from - // the new parser and old parser. - let mut missing_footnote_references = BTreeMap::new(); - let mut found_footnote_references = BTreeSet::new(); - - // populate problem cases from new parser - { - pub fn main_body_opts_new() -> cmarkn::Options { - cmarkn::Options::ENABLE_TABLES - | cmarkn::Options::ENABLE_FOOTNOTES - | cmarkn::Options::ENABLE_STRIKETHROUGH - | cmarkn::Options::ENABLE_TASKLISTS - | cmarkn::Options::ENABLE_SMART_PUNCTUATION - } - let parser_new = cmarkn::Parser::new_ext(dox, main_body_opts_new()).into_offset_iter(); - for (event, span) in parser_new { - if let cmarkn::Event::Start(cmarkn::Tag::BlockQuote(_)) = event { - if !dox[span.clone()].starts_with("> ") { - spaceless_block_quotes.insert(span.start); - } - } - if let cmarkn::Event::FootnoteReference(_) = event { - found_footnote_references.insert(span.start + 1); - } - } - } - - // remove cases where they don't actually differ - { - pub fn main_body_opts_old() -> cmarko::Options { - cmarko::Options::ENABLE_TABLES - | cmarko::Options::ENABLE_FOOTNOTES - | cmarko::Options::ENABLE_STRIKETHROUGH - | cmarko::Options::ENABLE_TASKLISTS - | cmarko::Options::ENABLE_SMART_PUNCTUATION - } - let parser_old = cmarko::Parser::new_ext(dox, main_body_opts_old()).into_offset_iter(); - for (event, span) in parser_old { - if let cmarko::Event::Start(cmarko::Tag::BlockQuote) = event { - if !dox[span.clone()].starts_with("> ") { - spaceless_block_quotes.remove(&span.start); - } - } - if let cmarko::Event::FootnoteReference(_) = event { - if !found_footnote_references.contains(&(span.start + 1)) { - missing_footnote_references.insert(span.start + 1, span); - } - } - } - } - - for start in spaceless_block_quotes { - let (span, precise) = - source_span_for_markdown_range(tcx, dox, &(start..start + 1), &item.attrs.doc_strings) - .map(|span| (span, true)) - .unwrap_or_else(|| (item.attr_span(tcx), false)); - - tcx.node_span_lint(crate::lint::UNPORTABLE_MARKDOWN, hir_id, span, |lint| { - lint.primary_message("unportable markdown"); - lint.help("confusing block quote with no space after the `>` marker".to_string()); - if precise { - lint.span_suggestion( - span.shrink_to_hi(), - "if the quote is intended, add a space", - " ", - Applicability::MaybeIncorrect, - ); - lint.span_suggestion( - span.shrink_to_lo(), - "if it should not be a quote, escape it", - "\\", - Applicability::MaybeIncorrect, - ); - } - }); - } - for (_caret, span) in missing_footnote_references { - let (ref_span, precise) = - source_span_for_markdown_range(tcx, dox, &span, &item.attrs.doc_strings) - .map(|span| (span, true)) - .unwrap_or_else(|| (item.attr_span(tcx), false)); - - tcx.node_span_lint(crate::lint::UNPORTABLE_MARKDOWN, hir_id, ref_span, |lint| { - lint.primary_message("unportable markdown"); - if precise { - lint.span_suggestion( - ref_span.shrink_to_lo(), - "if it should not be a footnote, escape it", - "\\", - Applicability::MaybeIncorrect, - ); - } - if dox.as_bytes().get(span.end) == Some(&b'[') { - lint.help("confusing footnote reference and link"); - if precise { - lint.span_suggestion( - ref_span.shrink_to_hi(), - "if the footnote is intended, add a space", - " ", - Applicability::MaybeIncorrect, - ); - } else { - lint.help("there should be a space between the link and the footnote"); - } - } - }); - } -} diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 572c9bf7552fa..eddafa9ba8e49 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -61,7 +61,7 @@ impl CfgPropagator<'_, '_> { let (_, cfg) = merge_attrs(self.cx, item.attrs.other_attrs.as_slice(), Some((&attrs, None))); - item.cfg = cfg; + item.inner.cfg = cfg; } } @@ -71,7 +71,7 @@ impl DocFolder for CfgPropagator<'_, '_> { self.merge_with_parent_attributes(&mut item); - let new_cfg = match (self.parent_cfg.take(), item.cfg.take()) { + let new_cfg = match (self.parent_cfg.take(), item.inner.cfg.take()) { (None, None) => None, (Some(rc), None) | (None, Some(rc)) => Some(rc), (Some(mut a), Some(b)) => { @@ -81,7 +81,7 @@ impl DocFolder for CfgPropagator<'_, '_> { } }; self.parent_cfg = new_cfg.clone(); - item.cfg = new_cfg; + item.inner.cfg = new_cfg; let old_parent = if let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) { diff --git a/src/librustdoc/passes/propagate_stability.rs b/src/librustdoc/passes/propagate_stability.rs index 8cf39afd55c51..7b3da8d7c0fbd 100644 --- a/src/librustdoc/passes/propagate_stability.rs +++ b/src/librustdoc/passes/propagate_stability.rs @@ -6,7 +6,7 @@ //! [`core::error`] module is marked as stable since 1.81.0, so we want to show //! [`core::error::Error`] as stable since 1.81.0 as well. -use rustc_attr_parsing::{Stability, StabilityLevel}; +use rustc_attr_data_structures::{Stability, StabilityLevel}; use rustc_hir::def_id::CRATE_DEF_ID; use crate::clean::{Crate, Item, ItemId, ItemKind}; @@ -39,15 +39,15 @@ impl DocFolder for StabilityPropagator<'_, '_> { let item_stability = self.cx.tcx.lookup_stability(def_id); let inline_stability = item.inline_stmt_id.and_then(|did| self.cx.tcx.lookup_stability(did)); - let is_glob_export = item.inline_stmt_id.and_then(|id| { + let is_glob_export = item.inline_stmt_id.map(|id| { let hir_id = self.cx.tcx.local_def_id_to_hir_id(id); - Some(matches!( + matches!( self.cx.tcx.hir_node(hir_id), rustc_hir::Node::Item(rustc_hir::Item { kind: rustc_hir::ItemKind::Use(_, rustc_hir::UseKind::Glob), .. }) - )) + ) }); let own_stability = if let Some(item_stab) = item_stability && let StabilityLevel::Stable { since: _, allowed_through_unstable_modules } = diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index a71bb62e56c74..692ce21d6cfbc 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -7,9 +7,8 @@ use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; use tracing::debug; -use crate::clean; use crate::clean::utils::inherits_doc_hidden; -use crate::clean::{Item, ItemIdSet}; +use crate::clean::{self, Item, ItemIdSet, reexport_chain}; use crate::core::DocContext; use crate::fold::{DocFolder, strip_item}; use crate::passes::{ImplStripper, Pass}; @@ -89,6 +88,27 @@ impl Stripper<'_, '_> { impl DocFolder for Stripper<'_, '_> { fn fold_item(&mut self, i: Item) -> Option { let has_doc_hidden = i.is_doc_hidden(); + + if let clean::ImportItem(clean::Import { source, .. }) = &i.kind + && let Some(source_did) = source.did + { + if self.tcx.is_doc_hidden(source_did) { + return None; + } else if let Some(import_def_id) = i.def_id().and_then(|def_id| def_id.as_local()) { + let reexports = reexport_chain(self.tcx, import_def_id, source_did); + + // Check if any reexport in the chain has a hidden source + let has_hidden_source = reexports + .iter() + .filter_map(|reexport| reexport.id()) + .any(|reexport_did| self.tcx.is_doc_hidden(reexport_did)); + + if has_hidden_source { + return None; + } + } + } + let is_impl_or_exported_macro = match i.kind { clean::ImplItem(..) => true, // If the macro has the `#[macro_export]` attribute, it means it's accessible at the diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 44551d75903da..fceacb69fb55e 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -176,8 +176,7 @@ where // If the enclosing item has a span coming from a proc macro, then we also don't want to // include the example. - let enclosing_item_span = - tcx.hir().span_with_body(tcx.hir_get_parent_item(ex.hir_id).into()); + let enclosing_item_span = tcx.hir_span_with_body(tcx.hir_get_parent_item(ex.hir_id).into()); if enclosing_item_span.from_expansion() { trace!("Rejecting expr ({call_span:?}) from macro item: {enclosing_item_span:?}"); return; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 7b6921afa0808..254549e72c649 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -143,18 +143,16 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { && self.cx.tcx.has_attr(def_id, sym::macro_export) && inserted.insert(def_id) { - let item = self.cx.tcx.hir().expect_item(local_def_id); - top_level_module - .items - .insert((local_def_id, Some(item.ident.name)), (item, None, None)); + let item = self.cx.tcx.hir_expect_item(local_def_id); + let (ident, _, _) = item.expect_macro(); + top_level_module.items.insert((local_def_id, Some(ident.name)), (item, None, None)); } } self.cx.cache.hidden_cfg = self .cx .tcx - .hir() - .attrs(CRATE_HIR_ID) + .hir_attrs(CRATE_HIR_ID) .iter() .filter(|attr| attr.has_name(sym::doc)) .flat_map(|attr| attr.meta_item_list().into_iter().flatten()) @@ -225,11 +223,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { def_id: LocalDefId, res: Res, renamed: Option, - glob: bool, please_inline: bool, ) -> bool { debug!("maybe_inline_local (renamed: {renamed:?}) res: {res:?}"); + let glob = renamed.is_none(); if renamed == Some(kw::Underscore) { // We never inline `_` reexports. return false; @@ -245,7 +243,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { }; let document_hidden = self.cx.render_options.document_hidden; - let use_attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(def_id)); + let use_attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); // Don't inline `doc(hidden)` imports so they can be stripped at a later stage. let is_no_inline = hir_attr_lists(use_attrs, sym::doc).has_word(sym::no_inline) || (document_hidden && hir_attr_lists(use_attrs, sym::doc).has_word(sym::hidden)); @@ -287,7 +285,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { is_hidden && // If it's a doc hidden module, we need to keep it in case some of its inner items // are re-exported. - !matches!(item, Node::Item(&hir::Item { kind: hir::ItemKind::Mod(_), .. })) + !matches!(item, Node::Item(&hir::Item { kind: hir::ItemKind::Mod(..), .. })) ) || // The imported item is public and not `doc(hidden)` so no need to inline it. self.reexport_public_and_not_hidden(def_id, res_did) @@ -298,7 +296,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let is_bang_macro = matches!( item, - Node::Item(&hir::Item { kind: hir::ItemKind::Macro(_, MacroKind::Bang), .. }) + Node::Item(&hir::Item { kind: hir::ItemKind::Macro(_, _, MacroKind::Bang), .. }) ); if !self.view_item_stack.insert(res_did) && !is_bang_macro { @@ -312,7 +310,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { Node::Item(_) if is_bang_macro && !please_inline && renamed.is_some() && is_hidden => { return false; } - Node::Item(&hir::Item { kind: hir::ItemKind::Mod(m), .. }) if glob => { + Node::Item(&hir::Item { kind: hir::ItemKind::Mod(_, m), .. }) if glob => { let prev = mem::replace(&mut self.inlining, true); for &i in m.item_ids { let i = tcx.hir_item(i); @@ -379,7 +377,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // attribute can still be visible. || match item.kind { hir::ItemKind::Impl(..) => true, - hir::ItemKind::Macro(_, MacroKind::Bang) => { + hir::ItemKind::Macro(_, _, MacroKind::Bang) => { self.cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) } _ => false, @@ -420,7 +418,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } return; } - let name = renamed.unwrap_or(item.ident.name); + let get_name = || renamed.unwrap_or(item.kind.ident().unwrap().name); let tcx = self.cx.tcx; let def_id = item.owner_id.to_def_id(); @@ -449,7 +447,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { continue; } - let attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(item.owner_id.def_id)); + let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(item.owner_id.def_id)); // If there was a private module in the current path then don't bother inlining // anything as it will probably be stripped anyway. @@ -460,15 +458,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } _ => false, }); - let is_glob = kind == hir::UseKind::Glob; - let ident = if is_glob { None } else { Some(name) }; - if self.maybe_inline_local( - item.owner_id.def_id, - res, - ident, - is_glob, - please_inline, - ) { + let ident = match kind { + hir::UseKind::Single(ident) => Some(renamed.unwrap_or(ident.name)), + hir::UseKind::Glob => None, + hir::UseKind::ListStem => unreachable!(), + }; + if self.maybe_inline_local(item.owner_id.def_id, res, ident, please_inline) + { debug!("Inlining {:?}", item.owner_id.def_id); continue; } @@ -476,7 +472,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { self.add_to_current_mod(item, renamed, import_id); } } - hir::ItemKind::Macro(macro_def, _) => { + hir::ItemKind::Macro(_, macro_def, _) => { // `#[macro_export] macro_rules!` items are handled separately in `visit()`, // above, since they need to be documented at the module top level. Accordingly, // we only want to handle macros if one of three conditions holds: @@ -496,8 +492,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { self.add_to_current_mod(item, renamed, import_id); } } - hir::ItemKind::Mod(m) => { - self.enter_mod(item.owner_id.def_id, m, name, renamed, import_id); + hir::ItemKind::Mod(_, m) => { + self.enter_mod(item.owner_id.def_id, m, get_name(), renamed, import_id); } hir::ItemKind::Fn { .. } | hir::ItemKind::ExternCrate(..) @@ -513,7 +509,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { hir::ItemKind::Const(..) => { // Underscore constants do not correspond to a nameable item and // so are never useful in documentation. - if name != kw::Underscore { + if get_name() != kw::Underscore { self.add_to_current_mod(item, renamed, import_id); } } diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs index 3e66ed9f56da8..369fc52860ee2 100644 --- a/src/librustdoc/visit_lib.rs +++ b/src/librustdoc/visit_lib.rs @@ -57,10 +57,10 @@ impl LibEmbargoVisitor<'_, '_> { } for item in self.tcx.module_children(def_id).iter() { - if let Some(def_id) = item.res.opt_def_id() { - if item.vis.is_public() { - self.visit_item(def_id); - } + if let Some(def_id) = item.res.opt_def_id() + && item.vis.is_public() + { + self.visit_item(def_id); } } } diff --git a/src/llvm-project b/src/llvm-project index 1c3bb96fdb6db..c1118fdbb3024 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 1c3bb96fdb6db7b8e8f24edb016099c223fdd27e +Subproject commit c1118fdbb3024157df7f4cfe765f2b0b4339e8a2 diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 8f6496e9626c8..64223b5b75896 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -30,7 +30,7 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc /// This integer is incremented with every breaking change to the API, /// and is returned along with the JSON blob as [`Crate::format_version`]. /// Consuming code should assert that this value matches the format version(s) that it supports. -pub const FORMAT_VERSION: u32 = 40; +pub const FORMAT_VERSION: u32 = 45; /// The root of the emitted JSON blob. /// @@ -52,11 +52,67 @@ pub struct Crate { pub paths: HashMap, /// Maps `crate_id` of items to a crate name and html_root_url if it exists. pub external_crates: HashMap, + /// Information about the target for which this documentation was generated + pub target: Target, /// A single version number to be used in the future when making backwards incompatible changes /// to the JSON output. pub format_version: u32, } +/// Information about a target +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Target { + /// The target triple for which this documentation was generated + pub triple: String, + /// A list of features valid for use in `#[target_feature]` attributes + /// for the target where this rustdoc JSON was generated. + pub target_features: Vec, +} + +/// Information about a target feature. +/// +/// Rust target features are used to influence code generation, especially around selecting +/// instructions which are not universally supported by the target architecture. +/// +/// Target features are commonly enabled by the [`#[target_feature]` attribute][1] to influence code +/// generation for a particular function, and less commonly enabled by compiler options like +/// `-Ctarget-feature` or `-Ctarget-cpu`. Targets themselves automatically enable certain target +/// features by default, for example because the target's ABI specification requires saving specific +/// registers which only exist in an architectural extension. +/// +/// Target features can imply other target features: for example, x86-64 `avx2` implies `avx`, and +/// aarch64 `sve2` implies `sve`, since both of these architectural extensions depend on their +/// predecessors. +/// +/// Target features can be probed at compile time by [`#[cfg(target_feature)]`][2] or `cfg!(…)` +/// conditional compilation to determine whether a target feature is enabled in a particular +/// context. +/// +/// [1]: https://doc.rust-lang.org/stable/reference/attributes/codegen.html#the-target_feature-attribute +/// [2]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct TargetFeature { + /// The name of this target feature. + pub name: String, + /// Other target features which are implied by this target feature, if any. + pub implies_features: Vec, + /// If this target feature is unstable, the name of the associated language feature gate. + pub unstable_feature_gate: Option, + /// Whether this feature is globally enabled for this compilation session. + /// + /// Target features can be globally enabled implicitly as a result of the target's definition. + /// For example, x86-64 hardware floating point ABIs require saving x87 and SSE2 registers, + /// which in turn requires globally enabling the `x87` and `sse2` target features so that the + /// generated machine code conforms to the target's ABI. + /// + /// Target features can also be globally enabled explicitly as a result of compiler flags like + /// [`-Ctarget-feature`][1] or [`-Ctarget-cpu`][2]. + /// + /// [1]: https://doc.rust-lang.org/beta/rustc/codegen-options/index.html#target-feature + /// [2]: https://doc.rust-lang.org/beta/rustc/codegen-options/index.html#target-cpu + pub globally_enabled: bool, +} + /// Metadata of a crate, either the same crate on which `rustdoc` was invoked, or its dependency. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ExternalCrate { @@ -120,9 +176,23 @@ pub struct Item { pub docs: Option, /// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs pub links: HashMap, - /// Stringified versions of parsed attributes on this item. - /// Essentially debug printed (e.g. `#[inline]` becomes something similar to `#[attr="Inline(Hint)"]`). - /// Equivalent to the hir pretty-printing of attributes. + /// Attributes on this item. + /// + /// Does not include `#[deprecated]` attributes: see the [`Self::deprecation`] field instead. + /// + /// Some attributes appear in pretty-printed Rust form, regardless of their formatting + /// in the original source code. For example: + /// - `#[non_exhaustive]` and `#[must_use]` are represented as themselves. + /// - `#[no_mangle]` and `#[export_name]` are also represented as themselves. + /// - `#[repr(C)]` and other reprs also appear as themselves, + /// though potentially with a different order: e.g. `repr(i8, C)` may become `repr(C, i8)`. + /// Multiple repr attributes on the same item may be combined into an equivalent single attr. + /// + /// Other attributes may appear debug-printed. For example: + /// - `#[inline]` becomes something similar to `#[attr="Inline(Hint)"]`. + /// + /// As an internal implementation detail subject to change, this debug-printing format + /// is currently equivalent to the HIR pretty-printing of parsed attributes. pub attrs: Vec, /// Information about the item’s deprecation, if present. pub deprecation: Option, @@ -135,9 +205,9 @@ pub struct Item { pub struct Span { /// The path to the source file for this span relative to the path `rustdoc` was invoked with. pub filename: PathBuf, - /// Zero indexed Line and Column of the first character of the `Span` + /// One indexed Line and Column of the first character of the `Span`. pub begin: (usize, usize), - /// Zero indexed Line and Column of the last character of the `Span` + /// One indexed Line and Column of the last character of the `Span`. pub end: (usize, usize), } @@ -229,6 +299,8 @@ pub enum GenericArgs { /// The output type provided after the `->`, if present. output: Option, }, + /// `T::method(..)` + ReturnTypeNotation, } /// One argument in a list of generic arguments to a path segment. @@ -909,7 +981,7 @@ pub enum GenericBound { /// ``` Outlives(String), /// `use<'a, T>` precise-capturing bound syntax - Use(Vec), + Use(Vec), } /// A set of modifiers applied to a trait. @@ -927,6 +999,22 @@ pub enum TraitBoundModifier { MaybeConst, } +/// One precise capturing argument. See [the rust reference](https://doc.rust-lang.org/reference/types/impl-trait.html#precise-capturing). +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum PreciseCapturingArg { + /// A lifetime. + /// ```rust + /// pub fn hello<'a, T, const N: usize>() -> impl Sized + use<'a, T, N> {} + /// // ^^ + Lifetime(String), + /// A type or constant parameter. + /// ```rust + /// pub fn hello<'a, T, const N: usize>() -> impl Sized + use<'a, T, N> {} + /// // ^ ^ + Param(String), +} + /// Either a type or a constant, usually stored as the right-hand side of an equation in places like /// [`AssocItemConstraint`] #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] diff --git a/src/stage0 b/src/stage0 index 9d6a08d378d8e..8ca6860490ca5 100644 --- a/src/stage0 +++ b/src/stage0 @@ -2,7 +2,6 @@ dist_server=https://static.rust-lang.org artifacts_server=https://ci-artifacts.rust-lang.org/rustc-builds artifacts_with_llvm_assertions_server=https://ci-artifacts.rust-lang.org/rustc-builds-alt git_merge_commit_email=bors@rust-lang.org -git_repository=rust-lang/rust nightly_branch=master # The configuration above this comment is editable, and can be changed @@ -14,468 +13,466 @@ nightly_branch=master # All changes below this comment will be overridden the next time the # tool is executed. -compiler_date=2025-02-18 +compiler_date=2025-05-12 compiler_version=beta -rustfmt_date=2025-02-18 +rustfmt_date=2025-05-12 rustfmt_version=nightly -dist/2025-02-18/rustc-beta-aarch64-apple-darwin.tar.gz=1b51ca064350d8b15c7ab6c8ec996a497e912dc237cafc2c205066fc6416e0ff -dist/2025-02-18/rustc-beta-aarch64-apple-darwin.tar.xz=e4b71f6456d9e62ada6909254606da7f6681f3da0f5bc7d2c3d5c387fea35743 -dist/2025-02-18/rustc-beta-aarch64-pc-windows-msvc.tar.gz=1ff70a5bd238d959d806c3b471a5b03c6cde784944a96a585e0ae0837d2a9c92 -dist/2025-02-18/rustc-beta-aarch64-pc-windows-msvc.tar.xz=5017b351bb90a08041757eae61386b224ec0161c5734293184a87d8903f30098 -dist/2025-02-18/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=8a58b6cc4577615efc76bb9472229098d6f938c1f051ea540409e9dc812dbd8f -dist/2025-02-18/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=330e217dbd1c507c8706aef5fbd0baf9584495445743f38668cdc962adfb125e -dist/2025-02-18/rustc-beta-aarch64-unknown-linux-musl.tar.gz=4060ec54281975880a9819b815120d6a450e4c31ddf1136ecce348e28875d50d -dist/2025-02-18/rustc-beta-aarch64-unknown-linux-musl.tar.xz=0f92b9267a55ac0c764cde63b8cbc8a0a317f7e0817185d380fc2aa35a933687 -dist/2025-02-18/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=4e989e00725e7cfb08ea834c74ec6dd352eda74c13218f7da423ed598af9f9df -dist/2025-02-18/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=b06181daf8842c2e544e5d54169f9bbfc70ee76115495de033ac4e341617a588 -dist/2025-02-18/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=93fe2efcde1032ad636ef509a73168f0ab7fadbca699a27e2882b3832539e8fa -dist/2025-02-18/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=b49d0256381c928785035578e5b7624c37ba732ea7aefca37dbb66b5162c8090 -dist/2025-02-18/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=56fd36c20d78085f39f0df40f15f7694e8744714104539865b9c3d7b06d47e2f -dist/2025-02-18/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=86587bea60c2b8a114ab33fe65a7152fcf8e1dcca14550712dca72af9fd65674 -dist/2025-02-18/rustc-beta-i686-pc-windows-gnu.tar.gz=32c95c78b223efea2ee8e02fbe3a58ac753ce9284e792bc87feaa051fcff5687 -dist/2025-02-18/rustc-beta-i686-pc-windows-gnu.tar.xz=d2e623f11aee7e81eceb6947e3f7150bfd727eb2f527e4abc6b10d3322959802 -dist/2025-02-18/rustc-beta-i686-pc-windows-msvc.tar.gz=f7ffd07eb2b5e83df513c6a313e28003e3ce923778583e78da9efbb5e62405dc -dist/2025-02-18/rustc-beta-i686-pc-windows-msvc.tar.xz=cd2e61b548a6849b17183fcacb2ac8f94955d893be439793580c39080feb28be -dist/2025-02-18/rustc-beta-i686-unknown-linux-gnu.tar.gz=fedeca202c1651fe4b15cfc411365ecf016376b3cc7c772d2e0d739e0ec02dc6 -dist/2025-02-18/rustc-beta-i686-unknown-linux-gnu.tar.xz=6389f10e2328bdfa81ef1f34406bb4ec8bcb6dcf64d39c95946d32c9fee7f0b7 -dist/2025-02-18/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=21b565bbaacc2be5d066c5d0c49b7c0f5b0bd24690ca35e9c88e6ec91846f744 -dist/2025-02-18/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=4a728e3ec41834775f0b288cdca5ae152314edcaf20d7ea46ea62fab1b9ef327 -dist/2025-02-18/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=0a72f650aa70708f9a72886ea33b7a2e3ffe2a6e2cbcb1d2248f3d9eca74a9d4 -dist/2025-02-18/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=877c3c447f4c25068f70bd7d6dd5078d75b0c194777b2f8a9311a66fc6eda701 -dist/2025-02-18/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=67af5e05ab0a367d3f76c0656823b530a23fb26d9c86db2b433684b9191b8881 -dist/2025-02-18/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=2da086d39eaa000ba629ee15bec740db57039ca3e5c7c55feb9cb9ca6d39c785 -dist/2025-02-18/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=b079df9a3e5be95a755d22f5ecf3ddeb43f94d96eaa3985770ae98ad0e7e15bb -dist/2025-02-18/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=916fe3b67094bb351320371de9587f01bb65f9b9aed2c7aff7930e499822b660 -dist/2025-02-18/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=356e3173c960aadfb91dcb607a26647895fb1ae11a7cb596b019c71c6dd808e7 -dist/2025-02-18/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=9115c4c85d8c4b5254df41a5d3f55733648ba282711e18a692ee100ed13fb550 -dist/2025-02-18/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=b8a267f9ca2d23385c529924782f633b6b9f66b50cda5986eff91c223d715307 -dist/2025-02-18/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=84e65fc1d4282d737b6d0b1aaa1c2abd395af215983eb5b3700e925ca6ba3bc3 -dist/2025-02-18/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=19b1d8618edb5d3a6cf620c814f6b76a462bc965f4aac2cde7aca5849b92cac9 -dist/2025-02-18/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=9914408b5590fe7c43b2aa10c261bb5162b024e396c625b06546564a5eaddb89 -dist/2025-02-18/rustc-beta-s390x-unknown-linux-gnu.tar.gz=37ac73ea85f43a4859d8c4526cb9480e69bcaa778e842128852c8b21c6db6b45 -dist/2025-02-18/rustc-beta-s390x-unknown-linux-gnu.tar.xz=af1cab661b26cab1ade53f12ec08d13d60a74c2f370d9d3617154e0f01cb3719 -dist/2025-02-18/rustc-beta-x86_64-apple-darwin.tar.gz=aa9999ad56f8968e3e0f5417661ff42fbd1bd482a649d1a8c01d6ba5a6e78a72 -dist/2025-02-18/rustc-beta-x86_64-apple-darwin.tar.xz=bfd09bf8db9bbd7557df3f206207631cc4b10d59d0cf6b072e610646b5c7fa4a -dist/2025-02-18/rustc-beta-x86_64-pc-windows-gnu.tar.gz=dabab346a003a5b13265da6bab96fc703c34f728c852092bec4cf08d13daeadc -dist/2025-02-18/rustc-beta-x86_64-pc-windows-gnu.tar.xz=c2252dea69c8dcf6ba0213da8b05b8d9173676fb7448cde684da7b56d2c52577 -dist/2025-02-18/rustc-beta-x86_64-pc-windows-msvc.tar.gz=9ae9c3cb963872b2ef02650bcec15906e0b5e89cc6d3bee0aadfffcec7a1e6df -dist/2025-02-18/rustc-beta-x86_64-pc-windows-msvc.tar.xz=bcbf75c92e9afe6b25bbde275bd38c3cdeda6324baf5b8c99173ca5738760a7f -dist/2025-02-18/rustc-beta-x86_64-unknown-freebsd.tar.gz=5313d0780f6a9587e809da0f1ffc973721afad88a0ef8fb83005c383d79229d8 -dist/2025-02-18/rustc-beta-x86_64-unknown-freebsd.tar.xz=52ab3212d64b56a8da207fe976cbc8d266e962a61c742e6069137b10ff25c3c1 -dist/2025-02-18/rustc-beta-x86_64-unknown-illumos.tar.gz=527f839ddedc7bdff48527a276d3d7f64d475dd815b81c6664f1ce25668e0ce4 -dist/2025-02-18/rustc-beta-x86_64-unknown-illumos.tar.xz=3861d9928983a415cd44e5dc50a99af948fac392adfb6c2147b14fb98dd08890 -dist/2025-02-18/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=0054d14cf00b25cfbcb2a1560887c825f703223312ca9cdd0ad51076bf54a3cc -dist/2025-02-18/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=75a9d69d13e50bb22ec721f9c64d08282d76f285482b285bb61bacabeecd710c -dist/2025-02-18/rustc-beta-x86_64-unknown-linux-musl.tar.gz=305765ca6a413ea86360b970bd062a19f6bc52756551af43d74920fc2a021d95 -dist/2025-02-18/rustc-beta-x86_64-unknown-linux-musl.tar.xz=c7230b578a97fb234ac106c7333615074b9a7a8abc1422f149ad613c2af28134 -dist/2025-02-18/rustc-beta-x86_64-unknown-netbsd.tar.gz=881bd9b260b2c7838ea1b4de2cd6baf2ff4d317e0c159faba1a683c4c32ba267 -dist/2025-02-18/rustc-beta-x86_64-unknown-netbsd.tar.xz=0c4f2cc0bafbf8b41b257e851870b64d3b5fc112f02227f561c645dc439c96db -dist/2025-02-18/rust-std-beta-aarch64-apple-darwin.tar.gz=958434edfc07bf6d10da3d41f01d1511b28d1178423a78bff2f60cd10046dbca -dist/2025-02-18/rust-std-beta-aarch64-apple-darwin.tar.xz=80043af05fb96c497bce55063f2733e37f243f85084f9aa60ab504d7c15c5cce -dist/2025-02-18/rust-std-beta-aarch64-apple-ios.tar.gz=509aa8803b29ccbb97a1a8c12e2ca6b27310d5af313650c7afff45ab1843106a -dist/2025-02-18/rust-std-beta-aarch64-apple-ios.tar.xz=f0f05dafc9a3277b075023bb449675946706307f769590c065cb53ae615708d9 -dist/2025-02-18/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=c37c0aee56c5858f91fb5aa60df28cc92649d4884d5edde57eb6690f494ba5f5 -dist/2025-02-18/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=e5120697e4a118824fdebf6d2a644f8f338bb83e209303fc684095b8f6b4ab1c -dist/2025-02-18/rust-std-beta-aarch64-apple-ios-sim.tar.gz=d5b851917a3f57703378596828e92068c28e00fe0b02331891737c2fc697b5ff -dist/2025-02-18/rust-std-beta-aarch64-apple-ios-sim.tar.xz=ef33164320931007db65f4915b533e5078a7279a7a134fc80045751a5198834a -dist/2025-02-18/rust-std-beta-aarch64-linux-android.tar.gz=4b94607d7c09b4f0f85236fb2237f1859150e12745ee2020cb134db904c1e05b -dist/2025-02-18/rust-std-beta-aarch64-linux-android.tar.xz=3aa7807ef9da83e1a4deebe4a7085e4df1b60edd4e9f237870f827073100d09c -dist/2025-02-18/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=2d4c8a5a400c35443228087f8203f320253299a5018c1b92526f99023581c3e9 -dist/2025-02-18/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=df8741055b3d4f7eeedec9e0101a1c184a37ffb75b2d4474bfbca577300355f2 -dist/2025-02-18/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=8db9c07d4b68018cd3c67be1e7bc496078dfa8a6852a35c449db5ba19f6bf0df -dist/2025-02-18/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=f112e3d51013ceff37e8c05d80c3605b76ddec8e55474d55815b4860f96febc8 -dist/2025-02-18/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=2bb6d934225823c2dcdb86dca91dd8e488f69b238b283607f75abe9b5de46486 -dist/2025-02-18/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=99036fde94fe0cb3ab2d2f48cd9f9bbb4d2fa784f1bd21afaa71b032cd95b069 -dist/2025-02-18/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=c49fee34a02f5d3fe4e03ed7da90e045f7967c3e2e8f7a30804f4da5d535225c -dist/2025-02-18/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=88170a13f9448b9671d252f0315aed94b6324716230db7307061d4890cfda70a -dist/2025-02-18/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=a5e7442d722f6742fd0436d3c7d75738bc6cbd936f3399190086a88ef311e34e -dist/2025-02-18/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=5d07a95e3203ebe98eed1b271a2e6ae44bead53e7bda019a8d256c8553c21bd1 -dist/2025-02-18/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=0af6b8fc7f46641d4de61ada9bc9ff01af7775d727121267177fa4c43cc9ed7b -dist/2025-02-18/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=fd68b34e7aba25d8a834c018febbde6c3de7d967e014a738681642f41ec61603 -dist/2025-02-18/rust-std-beta-aarch64-unknown-none.tar.gz=46025b0892498de8e311be7bd6df261065f80a70720cb9285062161a30af5d76 -dist/2025-02-18/rust-std-beta-aarch64-unknown-none.tar.xz=b4cbf42364270c158d801ac7d4b6cfd7bf1f5f9e74a3044e6d959f1971f5bc34 -dist/2025-02-18/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=ae2a5c63cce6ce237c81ae78cabc6e1e0634c956789c2e2f39e3a6c0d864636f -dist/2025-02-18/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=6bba5069c89d9a0d9a2013715814fb55ebcd77b342ed0dfc72115a18367c5e8c -dist/2025-02-18/rust-std-beta-aarch64-unknown-uefi.tar.gz=0bef07ceece7523d6f41f1d640569c52ebe8a6b97b35da84240d873fd68250da -dist/2025-02-18/rust-std-beta-aarch64-unknown-uefi.tar.xz=cff8b58e158786cee1a719552fb97cb2cd3a12b541641437809428a65eed42fa -dist/2025-02-18/rust-std-beta-arm-linux-androideabi.tar.gz=a34fbf0d01cea60876a6d0aa4ee96587a5e31b6d4f84aa7c1ba5b7fed261b639 -dist/2025-02-18/rust-std-beta-arm-linux-androideabi.tar.xz=7d72d638f5984726fb4a61e81671d9557d0a9a876bf5bbf39b2df3c9983d2962 -dist/2025-02-18/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=9ef3aa1bcbe1a18658bd16359cbf3e94ae1b07f65bd5c69ffbfa964ad845baf5 -dist/2025-02-18/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=93020195c2ce07204179e2d2f900953707e341a71d9371551c4a727afc58378e -dist/2025-02-18/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=5b0a4c4831534de85a5ba5b0149bbd824ca83648bf66babe5dbf13291b06e03d -dist/2025-02-18/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=60f1ff95cc88330b6c9741520c02ec845e7b14943f2927091a9110b7fb1c4305 -dist/2025-02-18/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=0f254a943b6e56873d2ab84e8a93fa7a3ab723df5a7926faeadae4696ed06121 -dist/2025-02-18/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=0c682e7cbba8463682a0a20785ff7fd331d2bc7a32c0cf86b8159897fc5c5ee7 -dist/2025-02-18/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=6c5be45f79035f57ac41fd209f6e7d4439cab5acaa766f7bf001b24196411b1e -dist/2025-02-18/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=bf25eb6650ad27377414d53af6857133a539530c841cf96e3cae4406bf4ad485 -dist/2025-02-18/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=39ec7fd0ecf47b36c6badc1fc71f1290e99d2edfa9d7cfa72446fbb32ba9c8b0 -dist/2025-02-18/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=f1d578e9251d95c94c20e3ee7888993ec7ea3d967a880f89963df7bea0bbe93b -dist/2025-02-18/rust-std-beta-armebv7r-none-eabi.tar.gz=3990bce70402da79a27fd0f4adb169e4e9faf617bdbca2676bc41d9f85845dd7 -dist/2025-02-18/rust-std-beta-armebv7r-none-eabi.tar.xz=ac944400a3da94e32d7010e10b30879bbb5da456d3d54dfd0efc792884b035de -dist/2025-02-18/rust-std-beta-armebv7r-none-eabihf.tar.gz=1196e2ae338b325ceb89edaab8b165478ec481270a7ce4c65f47f45d76e7b33b -dist/2025-02-18/rust-std-beta-armebv7r-none-eabihf.tar.xz=fead57620d21252c958020e340fc793102d177f76fd90b3936eaf3a22a3b87c9 -dist/2025-02-18/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=e5e81e1501554c2b28745c4d905bbe50f909dce3b7806f6010a9d48cc528304e -dist/2025-02-18/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=d3c9513cdb058685848de3d7864a7fa5a9b1e45f779a7ecf447ac7faae652772 -dist/2025-02-18/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=6ca5598fbdbf77bcf971e62d5b3f486e25d01e95773670bdc600448f29b68a09 -dist/2025-02-18/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=7e0a8798586538c4b322b58b9e1ac90e27dea4a0b5b750873f90b8ec9dcdbe2e -dist/2025-02-18/rust-std-beta-armv7-linux-androideabi.tar.gz=cbd81d4f949248c0c48a60545648a384aa321ee5f590f52d50e8d3639a649745 -dist/2025-02-18/rust-std-beta-armv7-linux-androideabi.tar.xz=75216a970ba5a023717dbbd45b5a1a90ff9533f25deca241c11ebce80dc6229e -dist/2025-02-18/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=5acf37dc6035d3d83d003d70d3db3c196b20d461a70385e8e5d545be1f4392b4 -dist/2025-02-18/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=8c75b0c815465126d6c7516883c478c78fc83cfb294bd5b9387d5bad65c9810f -dist/2025-02-18/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=733fca96e3d6dd232c746a6034bd7cc864f06bfce3d5da3bfd72c5ca4cea221d -dist/2025-02-18/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=e9ef142105d0073bf070a6de74e7173fbc09f3f22fd50eef0fea8ab6cf0ab4d7 -dist/2025-02-18/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=13f2b55f297628bea201f3caaae0178f0d898c67a696d4b60a37c3ba5af0582b -dist/2025-02-18/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=455605ff7e88d69052a4b3798ba27a673807ac1be197a8ac9e57497b5bac1661 -dist/2025-02-18/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=5b4b8eae2d86baeb470ad2a143f5d91f0dedeb225607189d9d0f8c8115e5251f -dist/2025-02-18/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=fd36a0f0a001436195eacdb52baee2462c43a1f9899b2e01ed60019f8285a95d -dist/2025-02-18/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=093900dfc07c4c4f59bd84aa8e9b505890cd8736a994798da8cfd7fb6210ab5b -dist/2025-02-18/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=e5cef28308348a378835ced449affa965f49d9b7382d28cbf32337ae94ebc9cd -dist/2025-02-18/rust-std-beta-armv7a-none-eabi.tar.gz=210229d27f6d574670e9406b98748869212e66713a8ad37f2e5b67ebf27e43ab -dist/2025-02-18/rust-std-beta-armv7a-none-eabi.tar.xz=323197f1dc3fe70e0a540df8a5f5d9475dcb390f1685bef54ba355ad3b48f316 -dist/2025-02-18/rust-std-beta-armv7r-none-eabi.tar.gz=f66d3a794ea7ea0df73b5df389738e6b3e1790b27c06187de2ed1888743ecb57 -dist/2025-02-18/rust-std-beta-armv7r-none-eabi.tar.xz=ae1465d82ea49e5ed33ac95dc0ece4c8fd0ce3df20b21a6a44ed93f33d131aca -dist/2025-02-18/rust-std-beta-armv7r-none-eabihf.tar.gz=fa0d84655bfb7488c9c378ecf833edbde08c652d25fbc9092ed2707b320f657a -dist/2025-02-18/rust-std-beta-armv7r-none-eabihf.tar.xz=251ba3b71c4a0bbdc327a84ee1b3c3deeeed3917fe55aadff9a52a44063f6270 -dist/2025-02-18/rust-std-beta-i586-pc-windows-msvc.tar.gz=9d9d89b206dc85323a7ee765447d1cafc2fab9189be88e1558709d94c51f2298 -dist/2025-02-18/rust-std-beta-i586-pc-windows-msvc.tar.xz=b124483bdffbb41b5c806f6bcc1003ba15d031cf5fe02eaead555abe15da20a6 -dist/2025-02-18/rust-std-beta-i586-unknown-linux-gnu.tar.gz=914925fb75c45cd9939c8692b02efd337b814040ca9bce369d812b97698a4c3e -dist/2025-02-18/rust-std-beta-i586-unknown-linux-gnu.tar.xz=eefceae8f0d42a5e3524ac134afa9a13e938f1680edf605cca2e2d9dfbd33682 -dist/2025-02-18/rust-std-beta-i586-unknown-linux-musl.tar.gz=4baafd6924c5ab59c0c4dfd30272c08328ea1f31b70e8a9a3dababb94c1dee03 -dist/2025-02-18/rust-std-beta-i586-unknown-linux-musl.tar.xz=c75b6e4b50cd731d7f955956ce0afc02f04e2adc4268a1bec8b076eb1733ad28 -dist/2025-02-18/rust-std-beta-i686-linux-android.tar.gz=e067c5483336195763c2f1e9625644075fd93cc86180e6d24bee63aa26b22e99 -dist/2025-02-18/rust-std-beta-i686-linux-android.tar.xz=d4892feae881cc914b94b8bfd66b5e75cc4d62cbb697b8faa3f29d1d70d15f5f -dist/2025-02-18/rust-std-beta-i686-pc-windows-gnu.tar.gz=ff6f43c40e2f8edc9ca21df771c3c28ab77bcb0e254adaa09d8c9433bd56fa97 -dist/2025-02-18/rust-std-beta-i686-pc-windows-gnu.tar.xz=8d6088d7ef7f13bbf576fe238e8a032091359a773845f35e3329b5d8273d1fcc -dist/2025-02-18/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=cc62a0e5c529bb041455ed15f646e5c9c918f20a644ed7306ad8beb1abf07c1d -dist/2025-02-18/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=9cdcd32f73d9839c5e6322f8b1e18e3abf825ddbbe9bb498616f0c058c5fb061 -dist/2025-02-18/rust-std-beta-i686-pc-windows-msvc.tar.gz=fecfb22d75b38e9c92c64790833c8ed8b4ea70c40d3d9c67a23277216c1246bb -dist/2025-02-18/rust-std-beta-i686-pc-windows-msvc.tar.xz=a592d638118b0b95e8ef0e174b94c1825d0e3a3aab87d03737eb7415d1b32076 -dist/2025-02-18/rust-std-beta-i686-unknown-freebsd.tar.gz=e8546b2f4fe5746706d2b0d56fe174ee5993bd61a9fe451fd233236d7af0feee -dist/2025-02-18/rust-std-beta-i686-unknown-freebsd.tar.xz=09efaf8f4f26ce6d466c1e20758901dc64348cdf390f4b878b4ee9542f50dbae -dist/2025-02-18/rust-std-beta-i686-unknown-linux-gnu.tar.gz=e173f1ac1662deba8c719965d5d4c77e711cc0eac732d933b6c8ac021a44de5c -dist/2025-02-18/rust-std-beta-i686-unknown-linux-gnu.tar.xz=600aa6b6ed1b49afdcc4978c897cd1e1f801193b804b0d1723319464c8378c88 -dist/2025-02-18/rust-std-beta-i686-unknown-linux-musl.tar.gz=38f7ee73e7067df2b98064b9f0ed00b8aa7c7eeae5955719fa0e2cf1f126ed19 -dist/2025-02-18/rust-std-beta-i686-unknown-linux-musl.tar.xz=6e639d9c45eda6b43ce0ce52e3213172df1875adfa0c13e52385b5aa9fa05da1 -dist/2025-02-18/rust-std-beta-i686-unknown-uefi.tar.gz=2ae2a44ad2610bbe3a97d176bbfd3e43efa22b5713c9591b9f3bcd7609f3a30b -dist/2025-02-18/rust-std-beta-i686-unknown-uefi.tar.xz=d8fcf03e16ce9ada0253a2f8899ee9193083e1638d9e0da6cc76886b6ee14c91 -dist/2025-02-18/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=cb0f09f7d81b1e49a761a94396dcb4e999dbbea7d70e044785992da9a2aa38e2 -dist/2025-02-18/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=1cecfc8d91f5f3cb7bc67f49795d5e2c74638df0f7d66a3051a851522dae2860 -dist/2025-02-18/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=0369af7a8b56dac39448a22f1ad27df42c72ccdcb43cb96e7eaecf0c0d8b94e2 -dist/2025-02-18/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=a395fd16ea741c72b39db5d398a46a90cae8531be620a3468d0dc13f8a9979f0 -dist/2025-02-18/rust-std-beta-loongarch64-unknown-none.tar.gz=e2ce2c57ad140b9088052009efae96ed04ce14c258cd954ee049dfc9146242a7 -dist/2025-02-18/rust-std-beta-loongarch64-unknown-none.tar.xz=a055dd6cc47dad744fe9343d100750ea013cbe3a5bf4fcdac54e80809352a6d3 -dist/2025-02-18/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=26f8ba1c9594f7336ad530eef0076f2d30752d2edcf96d944cc962dde3d3caff -dist/2025-02-18/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=32c80782a30c7bf300510e4fa29dad76d9a40bed2d5746c4d55b17fb261d788c -dist/2025-02-18/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=2cd25291aa0c7610f10ab04cdf4ba4d214e0c684bed73d2ef31ae84af2da0eaf -dist/2025-02-18/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=a7f02677a5c35388593142ef95cf56ffe1632df7fd42ff35bff89dafb34a0bdf -dist/2025-02-18/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=64ee7c6fb36ea45490911ae5557f6a051728a05a7c88b1476489be87456be4bb -dist/2025-02-18/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=250993b5a1915a8970d91f7a10f3f50c907cc9508d71d3b2c8e20914f90254a6 -dist/2025-02-18/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=deb267998a725fb0288432a51aeac3004d6430fce8683c91d102b6708e5b99ea -dist/2025-02-18/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=49ba80f0a2970948699b78fc60ac09f77dda94e56931399c57c2c81fce8df9e4 -dist/2025-02-18/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=06ff5073e946ed2a5f80f9a5befd7b0c27e9a959cebf26b6cefc5762a0fcdb03 -dist/2025-02-18/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=27f12f5835f564b03f55d85121c9474b9fc93d3084c5b5d0fc63d33c8c9dcc64 -dist/2025-02-18/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=78aff087bbef054b5a6a8d2a1649dd59f69b7f98635eb0c130c4ba87b3018850 -dist/2025-02-18/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=e82b6bdfed001c91f8f88f6c37e31ef35f2f25cdca6fe8adfd90d024cc068e15 -dist/2025-02-18/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=8dd6fdb684882ce4087bb96ec2d982c0d808c93c053c774b49cfc705959b4309 -dist/2025-02-18/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=4d6db36241c3d8735e7c2ee84a37ae0cfbc2fc79c6fd259ca4b74e7edb68b8f0 -dist/2025-02-18/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=32e8a2eb316b2a82f80e7268a318df89f732d7cbaba779debe10664aec7d40de -dist/2025-02-18/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=562030f1bdc1307bc0261a2e66c12a6f21fed0ed21fdb2140b1b1d55664aab00 -dist/2025-02-18/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=52e8c7ab87f5d2cbb712fb54ad8394b50a7f95961e1a7e5ffce981a962899a2c -dist/2025-02-18/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=74f13bf5f0ff428c323c3041771b653d3ccf5a999105869378bf09a6ce6ca040 -dist/2025-02-18/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=e2b8a784b4c6826d00a69de8e1891a40619f4b17c6e54c5cbed349fedefbd8f1 -dist/2025-02-18/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=a231848519003a9ea1306a7da2139675516c0d94e5cbd8af140bb1a37515d7fa -dist/2025-02-18/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=82d33d6527db7adaff8bec04c6edb4f3f7a1609fb5e5a91011a3b48dd47af93e -dist/2025-02-18/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=bfe440ce80c4547afef2c6b8379bccde9f7206a5406c5a7ed5c06ab460ad8ba1 -dist/2025-02-18/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=728c559f432bf8487c345967a2ebc9e4eada676a6d56a68ec32d26ee9775857e -dist/2025-02-18/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=51e7990ff209c27a15d40a684d634e5cad26d55913b897c2be6d92fcb849b7d8 -dist/2025-02-18/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=ea66ba0e6af1fc7ce741c0838bc005abc04c6ad85c6831d2e05d04f30066281b -dist/2025-02-18/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=7429a295ccda168e41e9717647ba4639eceb81a73d66ad7ba01c9e7f1ca19c03 -dist/2025-02-18/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=01824c3ca49cfb433de4508282f0395bae8053866691869fb804a3058a403b02 -dist/2025-02-18/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=67e324c82c6a5c9fd279b9bf5ebabaecf35374c741e5728c2db3fbbb6eab1fd9 -dist/2025-02-18/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=466d4e5c1912937481fbd22d739987d663f3c771ff0efd676b34fc34e9266f87 -dist/2025-02-18/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=409c2870098222c09abcb1c89c7394633222bef2cba9756638e8cad058e3c524 -dist/2025-02-18/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=b31e3389756f78bf680982d6c6415c3712567e5050beddae10d2df52de560e61 -dist/2025-02-18/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=729b7433746947b88b8d4f488e2831bff1a92911b8c25f16a1208542e38d5834 -dist/2025-02-18/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=56caed9608e88e9ed6dec56ec3265d6464633ed57b14a455c626983defc87411 -dist/2025-02-18/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=9c9d16cac691185cf055117d6888a25f14c72cd110e3bc699c6e78b5d0dc70d8 -dist/2025-02-18/rust-std-beta-sparcv9-sun-solaris.tar.gz=6963215ffa1d2cf9650db8bc36f1eb5c97fb4d824a010c2a65f842fa00b4b4c0 -dist/2025-02-18/rust-std-beta-sparcv9-sun-solaris.tar.xz=9077823bd323a251347336f3c656c6847d5fefb90efed0cacadd510f42afbb74 -dist/2025-02-18/rust-std-beta-thumbv6m-none-eabi.tar.gz=f6d1b17e822403428a6ddf254caf78c75c2d91c208e035590779b51d45b7e081 -dist/2025-02-18/rust-std-beta-thumbv6m-none-eabi.tar.xz=1eb4e8ca0d8a52ddf754aec64b70e7a1f91d2746eef7e3c6d4580a0794cbfae3 -dist/2025-02-18/rust-std-beta-thumbv7em-none-eabi.tar.gz=7793950c2a0016ae90ee8fb70e881047a1f85c82f087fb02532009d6419ba6a8 -dist/2025-02-18/rust-std-beta-thumbv7em-none-eabi.tar.xz=14464141719b2b2c5d4a4b29685f3a2e336c2542ea33af01cfaec2ed7511baba -dist/2025-02-18/rust-std-beta-thumbv7em-none-eabihf.tar.gz=62612b4da1872950af31bf3d9f0a595f019e6c4074874f4cdfa9166c5ff39434 -dist/2025-02-18/rust-std-beta-thumbv7em-none-eabihf.tar.xz=07ab29714f76693f69273a5567408307186b272061501a9ac83eebe944f66d31 -dist/2025-02-18/rust-std-beta-thumbv7m-none-eabi.tar.gz=b2da60ea5d746b607c505279b84aa2a8f3bac3e258ca867f6aeca958e50691c8 -dist/2025-02-18/rust-std-beta-thumbv7m-none-eabi.tar.xz=3eee9280fdd35f30ee9b4a2e5d8614ee916f36cd08629a574f6450721a1fe05b -dist/2025-02-18/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=c5be169574de4fcd550b67d19834e064b81084e673764390b9f177631f3fb0ed -dist/2025-02-18/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=bcd6ddf6493cbe17236a74c26da7e16a9a752fd39ab453866ae3301cff0c8375 -dist/2025-02-18/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=e52c2e8272a9ff9442ae6bafb7526cac424d15d4d6d1d8d210fe981b42da9b73 -dist/2025-02-18/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=81adef669c6f8f08b941b0befc6af30eb78b2717c3aacd52a1526d9545548c53 -dist/2025-02-18/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=1036f47867b7d29ae468a040c50da016e20a6a3a291714df4193895234591c00 -dist/2025-02-18/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=508b4f1e307d0b033eab345ab5106eb83ad8dad212f491f1ba92b9680f699bc6 -dist/2025-02-18/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=41c806851db8e59dc9ad8a756c8efde7dc14e8ef5dc4feb81be9351d267b6157 -dist/2025-02-18/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=f3bb9c783f4350e491fd683d057521f497314e85576b26808f4edc7d1d8612c6 -dist/2025-02-18/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=36a3e0675cc972fe6f4f860da47c9d4e0ac175191622c651fbb002207506b627 -dist/2025-02-18/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=c3287e3ebd4583a576f4bbdcfaa77fc21f6165b18573cbfdeb28593494ec14dc -dist/2025-02-18/rust-std-beta-wasm32-unknown-emscripten.tar.gz=4a7128b50478a05d4fdad69715a0a7c9dcc745aab33cbc2e137cb670c6265001 -dist/2025-02-18/rust-std-beta-wasm32-unknown-emscripten.tar.xz=6e3b5981cca095c2414ad4d23f3e69a942a12c1d51dc9e055ad8409c5edbdcf9 -dist/2025-02-18/rust-std-beta-wasm32-unknown-unknown.tar.gz=3b0cbbb35daf13711e8f211da8e2ed4ade96f728be5e5ad3ca02e4d3e32b7262 -dist/2025-02-18/rust-std-beta-wasm32-unknown-unknown.tar.xz=4e50db69d0917f205a16fa3c4ee5e4012c000194ff75641ddeb49bbec9a2ec49 -dist/2025-02-18/rust-std-beta-wasm32-wasip1.tar.gz=d382d7f9787018480329518035fc4ce3247775633423e0f2940c3670cbdd4705 -dist/2025-02-18/rust-std-beta-wasm32-wasip1.tar.xz=e1d9c800db62bf606d9be9a75dd00ac7190acf3fd3592cbdeba67170ddb092aa -dist/2025-02-18/rust-std-beta-wasm32-wasip1-threads.tar.gz=cc7cd664a483eace96710d341132de4f9cf2113f358722425ddcf84cfe821116 -dist/2025-02-18/rust-std-beta-wasm32-wasip1-threads.tar.xz=17156e3e57e304ab3ab10f8191a9952400182d2a46ebcd39d1cfde97b94c0756 -dist/2025-02-18/rust-std-beta-wasm32-wasip2.tar.gz=7ab89b39693bd1bc3f344dce6ad1c127ef38f64591c7be769d07603b2422800d -dist/2025-02-18/rust-std-beta-wasm32-wasip2.tar.xz=2f53c60d0c0388ff644c17e341368e8b15d4c41b2827b03507d4efee100a1841 -dist/2025-02-18/rust-std-beta-wasm32v1-none.tar.gz=3277ec65149e708925ca7fc062cde92953d01569d2700d2831e6ff68768e5b30 -dist/2025-02-18/rust-std-beta-wasm32v1-none.tar.xz=24df037c2c4bcb3d593033d93b6e26aa1dc38161452220d7a73a23a696dc93bc -dist/2025-02-18/rust-std-beta-x86_64-apple-darwin.tar.gz=3878e1506ee4642fdd1bd5e10025c9c289f8d03b7cea876d2348fabc2675b721 -dist/2025-02-18/rust-std-beta-x86_64-apple-darwin.tar.xz=46bdd522559b61848cfcbc8c55d95b190a134f2a0ac19e3c7ebfaea867f9780b -dist/2025-02-18/rust-std-beta-x86_64-apple-ios.tar.gz=e914a0a4a2824f12764b0b91b0dd97a25416686839f35dea2aabde41c011f850 -dist/2025-02-18/rust-std-beta-x86_64-apple-ios.tar.xz=b44ac0cc8cab86ba9d000d2164786b5bdc115ace7990ead57aaba2b0e02750aa -dist/2025-02-18/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=497c75ec8b5f99a625d898b406d437b8902b8ad42ee1d629a0efd27cfee96e44 -dist/2025-02-18/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=a53ed7bfd16d62a7f51509d32fb6cc6b1f3af331f4c4867cbc1eb57da5c3d521 -dist/2025-02-18/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=3cec5b6714f156e43c85b4b15377475bc38f65325f61de020bddfc2708b25a7b -dist/2025-02-18/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=ea2cc73a03644647dec905100154a3238dd819fd0421f770dbc433cd6dc49f33 -dist/2025-02-18/rust-std-beta-x86_64-linux-android.tar.gz=2eaebfe572503b69e34674ed414357c514bcfbf013e6a2a7bb3fb14f501872e8 -dist/2025-02-18/rust-std-beta-x86_64-linux-android.tar.xz=66c4f03b907515831f9a48c8f5036d4c3115e814507a27abe1e423ef4a7e7c0b -dist/2025-02-18/rust-std-beta-x86_64-pc-solaris.tar.gz=4c8f54f1709908a9edabd9e60d8492cda771db4b51fe766f2a2323a10f184e1e -dist/2025-02-18/rust-std-beta-x86_64-pc-solaris.tar.xz=60d74169e80b3e1297b010501fa5a46a29a7541c84ceeff304b4c2ace7631540 -dist/2025-02-18/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=9d043fbcd7e180052b903180114d50c71a2ccbeb22fff7020514b4b0a11a8123 -dist/2025-02-18/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=f08905c0b8192f23f214465d757d93e30aee0250dda279d648e948961e0745ca -dist/2025-02-18/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=e35a42a9a6eccc4b738b389fdff24ba01ef23ff8d00fbd34100acafffb9c3763 -dist/2025-02-18/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=a1fd2d1b1bb1fe9593df7835b5fb81e5c9453c0bbbc17b9ee5deb0512ad12550 -dist/2025-02-18/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=feada263ce77cd0ec635ae78c8ce34099385a1f89d1eb680576a85a8adf0d75e -dist/2025-02-18/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=ee8def01984eba376773f7a055a18a4096750d26f96ab9150326dc60ece9fbe5 -dist/2025-02-18/rust-std-beta-x86_64-unknown-freebsd.tar.gz=7791a0155a0a3464d07b176383a4b61dcfaef4bf0247c58d447402fac3123186 -dist/2025-02-18/rust-std-beta-x86_64-unknown-freebsd.tar.xz=11bae401884cce0306b415bb1202c2c63f81395677057fbbe12e9302951a9d3d -dist/2025-02-18/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=4d05cd943f4e4250d50a5f732a64f32431a6cfef59b38bc087b1dbe1aa4b9561 -dist/2025-02-18/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=7bdc7161d66b6c330f027f53b15f164c9ad4d55debe77b7c34971a877bf82caa -dist/2025-02-18/rust-std-beta-x86_64-unknown-illumos.tar.gz=01edf8fdab00df25cfea2ade367dc4f66a1d08d11bfd9a7e56b65a723e14b517 -dist/2025-02-18/rust-std-beta-x86_64-unknown-illumos.tar.xz=9efccf8ba6fc68997123924cddd7008b15f1a57170d03597a370346fdc6c83d6 -dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=9dbfabe77853e672d576e70332e07727882cd6af14dee9a00d5186b43763ce82 -dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=04e31482a3ff18d8fdff40c4e95b22bc667bc0dd1ba06fadbe2479bae5d97288 -dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=ebb6e0c56d905ef064fa15449a72746daa9e3998f5aba5c6a3c132bc676228cd -dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=818fc179c8380f0fb70c46e04086926a69b026591ab62cfbc1051abc5c80012a -dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=c0a1c692a334821d61a9b92cd1332f047175d4335336db3e2129687ba96ce5bc -dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=2c71b87db4cec49e3c620e6b4e0e07f1aaaffe69020b07c87546232ed1ad3b20 -dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=c1bbf445dd0b187904af5871bf48a720151ef75fdd3dd13161dad01ef5e87bbc -dist/2025-02-18/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=19963165ec2c35c15024869c16a707bdc90e72032ebf6e203cc67411ec8ba7c7 -dist/2025-02-18/rust-std-beta-x86_64-unknown-netbsd.tar.gz=63fbbd51047125fe30f1954ea19a05d6f051d2a77929952bab326c8ded6db8d3 -dist/2025-02-18/rust-std-beta-x86_64-unknown-netbsd.tar.xz=1c255d1de9c2629210575b862600bf369499a0eb8873178ceff4a337ba9dccfe -dist/2025-02-18/rust-std-beta-x86_64-unknown-none.tar.gz=781f14a54c5e68acde1e68042905a48f687294e2bc61450f422a6e15bf6ab509 -dist/2025-02-18/rust-std-beta-x86_64-unknown-none.tar.xz=d45921b98740f3cce5833bc4fb1e36816f34b81e96c3ca2c76deff4744134c6c -dist/2025-02-18/rust-std-beta-x86_64-unknown-redox.tar.gz=0cd76d3c5b051b0b2015cb08079ea3b5ecbb9b0b68779eb35946721b24c07008 -dist/2025-02-18/rust-std-beta-x86_64-unknown-redox.tar.xz=6dec719cdb7f35f6eafd901414525b40f030566bdf3fb55e3b05de162dbf0741 -dist/2025-02-18/rust-std-beta-x86_64-unknown-uefi.tar.gz=aad1e2cfd4f428acb67d9dac1fab4450eccfe9212f52f99f06c09899285b6b1e -dist/2025-02-18/rust-std-beta-x86_64-unknown-uefi.tar.xz=4252b618ffc906949756ad28a5437d11b0353fe1d755ee1d23502a208bdee74c -dist/2025-02-18/cargo-beta-aarch64-apple-darwin.tar.gz=2f7f459d2595d3f60bcef4ccbd3c72095a3ec54fa3f2221a647ae892c308d5b2 -dist/2025-02-18/cargo-beta-aarch64-apple-darwin.tar.xz=501feb9138f3c11a13fd649d6573924ead10f5e6fb1e759d880684bff11c12be -dist/2025-02-18/cargo-beta-aarch64-pc-windows-msvc.tar.gz=b589177b0287a42ce17939dbbee92d883dfec88ebad33bf9a719a3cb2f446533 -dist/2025-02-18/cargo-beta-aarch64-pc-windows-msvc.tar.xz=d795082d304ecdde233a4b6bb3aa558392b2c5a1553fab213ee99648c49f7a51 -dist/2025-02-18/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=8444d938514ad2d55769cf614774dfb4e5a27c34f8ba8b49329a2bcf0b000162 -dist/2025-02-18/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=02e52fc13ae52066031ac40dad039ad752b83582e5f9e74ecef206aa9206ac16 -dist/2025-02-18/cargo-beta-aarch64-unknown-linux-musl.tar.gz=e89709bb552e7ee73de02b08c6d8820b07bccdc09777eff9716efabb2bdd50cd -dist/2025-02-18/cargo-beta-aarch64-unknown-linux-musl.tar.xz=50944f0e51190f9db065764afc7a41a3a1e39767bd1ef5ec659c98fe9243cc98 -dist/2025-02-18/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=0343dfc2f015d476b3aca59b33dd75f907f595bc24715e6c518194ab1a5dfec1 -dist/2025-02-18/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=3dd6c378c10172b1abfdd1db2fa764d1b48153ac1b5065cf124399941d036e56 -dist/2025-02-18/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=6eac5ffb9adaf2591d3b1872249f4a959c04afdf6694e62dc850936aa8d35972 -dist/2025-02-18/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=484510e6b9519c2b047864584253c8c1f725ce843e8dd3117349032314737462 -dist/2025-02-18/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=fcf4d4b2d401ea689ee32f38b4a61c77b52ff4d3d114b23d0262bd02c3d1ab5d -dist/2025-02-18/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=1a75296c42d6cf93a5e51ecb2b2bfbf0cdea350668d610e8b0c90937163b4f33 -dist/2025-02-18/cargo-beta-i686-pc-windows-gnu.tar.gz=d1b5147ec79466ab9441825b4761e89cebe57ad21a3199b0eca0779683c45ed6 -dist/2025-02-18/cargo-beta-i686-pc-windows-gnu.tar.xz=980850e06ccd90512baebbfe3baaa65b5c089edbae8146fd940c708fd7556abc -dist/2025-02-18/cargo-beta-i686-pc-windows-msvc.tar.gz=78c67ba481cc7b855b54a2edc0297bfb5eadcd83d5a028b05291415417bc6947 -dist/2025-02-18/cargo-beta-i686-pc-windows-msvc.tar.xz=6ea74c750e6e0a794965c6616e5b1a82dee295815b0c24a3898952b1a5a02c8a -dist/2025-02-18/cargo-beta-i686-unknown-linux-gnu.tar.gz=a0c8f67fe77b94674698b4816c081e2ef08500990c0f9eb069d0190df9c4f129 -dist/2025-02-18/cargo-beta-i686-unknown-linux-gnu.tar.xz=9b13f958ec4edff194858e90c54866a129e88769a76fe22582b07e17540708c3 -dist/2025-02-18/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=f4d2f1c8a7bec6d2b8378aa87a324c42f0f414962174b40b6f0d1dc21d0cacf4 -dist/2025-02-18/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=f25659ef3be1a28da8821eb178e02108f41cd5c0b87aa01a052a3a90e7a9da50 -dist/2025-02-18/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=b687a7c3fac08680818804e034af6689f1bbcf8fc0e406c99b2a49ddae1e900d -dist/2025-02-18/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=e33818d2f94e8ba148dbf8cd53d6396e370157acba6d0865a5ac71d292939a0a -dist/2025-02-18/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=9cdc2115bd2022cb0b0ac4f1c71142c064957137decc028dde7585acabb8b780 -dist/2025-02-18/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=f722447494f0a7af9c31b71364aa4a2fde79a9887440d8281e4561f7d71620d3 -dist/2025-02-18/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=d8a30f9e48e89cb1d18532fd7fbef8eeceef6bc602ffaf9e730c635e2fdbb156 -dist/2025-02-18/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=ec6b3b1595c5761b8092a04a412e79534c28910ed78ed792c9af7ddc4d92a389 -dist/2025-02-18/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=4bf9c73173199fd3ca952d33702288deb483c7fd392179352135b64e7e8fadb4 -dist/2025-02-18/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=12aa06644e07344a77d0c16dc1ed96bfba63de02df1f432d57b82f8a1e2282b6 -dist/2025-02-18/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=eab362146d449dcdd10740c8e6f9dec6e640ffcca162eb51febbc835e34acdfd -dist/2025-02-18/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=867b716eae9225c66c0ab4642e4e497055ad2effd24343bdf80354ce2845d6f9 -dist/2025-02-18/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=0503ba2e4b1d668fa3a3174f96ec3fcc12ff767574a7af8ef5fc824ca86dc965 -dist/2025-02-18/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=478b9f1d4f517f93f15530d27a7d55ea29cdd06eccf01176ffd08c0ee3204c1f -dist/2025-02-18/cargo-beta-s390x-unknown-linux-gnu.tar.gz=419a679aaf6e164649538916daf28081a063bd56c7b86ff0e2df58072f334fdc -dist/2025-02-18/cargo-beta-s390x-unknown-linux-gnu.tar.xz=31b39e242035036c1101412b7c86387e1183063ff5f6ce67d311d0764435a588 -dist/2025-02-18/cargo-beta-x86_64-apple-darwin.tar.gz=d6d5224228dd54b6d16be30271111620c02fd88e656b8bbd22452b66b0d5cb56 -dist/2025-02-18/cargo-beta-x86_64-apple-darwin.tar.xz=cbeff7c031b4ab4732207b90ee8849b27f03cb28d3e085bf4d70444347dbfc99 -dist/2025-02-18/cargo-beta-x86_64-pc-windows-gnu.tar.gz=ae5922955ec579732aacd8112bc53bf5333438eb81d0f81dc5f387888ac979a3 -dist/2025-02-18/cargo-beta-x86_64-pc-windows-gnu.tar.xz=553c55a2bc8eae2c8ba831823a97f2232808af1f753642ec4cdd09c33dd3f6ae -dist/2025-02-18/cargo-beta-x86_64-pc-windows-msvc.tar.gz=d9b2cf14565478429fba6266f0c86a58f3bbd1ce11a63268828b33ccb55759cf -dist/2025-02-18/cargo-beta-x86_64-pc-windows-msvc.tar.xz=2e8c0560355d11a038219072d2503860f5c5b3cd137b89ad931f54d9bba60499 -dist/2025-02-18/cargo-beta-x86_64-unknown-freebsd.tar.gz=4b528361607845e6f8ece403bbdb8d77c6159ec137396319562ea2772502792f -dist/2025-02-18/cargo-beta-x86_64-unknown-freebsd.tar.xz=c244ec4f97420c29c690e32bd6d8f14994bf1d990747f31a3dc0f2b37644493e -dist/2025-02-18/cargo-beta-x86_64-unknown-illumos.tar.gz=7d3b49df93e87322a9e99e36c6df020d3311c5538ad2298d8b5d8f2d9ad3e0d3 -dist/2025-02-18/cargo-beta-x86_64-unknown-illumos.tar.xz=136ce90487448ee770472d4bd2c0d9c96200f0ec762419feb42fefa26ba36b58 -dist/2025-02-18/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=510538965ea142405925d3f4f03a87783516407989b8aa7be07fb84c0680e9fa -dist/2025-02-18/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=a8c569398d71cab0b90c809b1d869a2e3c5037407b5af804df08c205775981c2 -dist/2025-02-18/cargo-beta-x86_64-unknown-linux-musl.tar.gz=1493f66e5cb12e8b955841804e7df745e26daea2278144aa57670192c4c62cef -dist/2025-02-18/cargo-beta-x86_64-unknown-linux-musl.tar.xz=472bbe49415557b75b572879d0c33750731c1fe9e56cc6ef3b0fd5532e56446c -dist/2025-02-18/cargo-beta-x86_64-unknown-netbsd.tar.gz=d3baa6019e13449c01fbeebebce42027ce4ba70841cf28733f6849e7c6ce5d89 -dist/2025-02-18/cargo-beta-x86_64-unknown-netbsd.tar.xz=0db9af42e9ad914a99db4924721c895cdf490bc5351bc8c2d8993e3569e952e4 -dist/2025-02-18/clippy-beta-aarch64-apple-darwin.tar.gz=6ce3b464744082131c99a213454225e5702564c83032e11dd0f3d95530a4e0af -dist/2025-02-18/clippy-beta-aarch64-apple-darwin.tar.xz=925baff0d91b959a99c989a4ada70973baf3da03e1d7e7e8be1fa334c3acbc50 -dist/2025-02-18/clippy-beta-aarch64-pc-windows-msvc.tar.gz=76d265c2fb6280eb8ed399501964f301a50e09e548cee708ebacc978d8db538c -dist/2025-02-18/clippy-beta-aarch64-pc-windows-msvc.tar.xz=e3d1d69cbc838ab639b2e41537b10266f1b6998a228882d4041f35cbb8984909 -dist/2025-02-18/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=918191cad727d795b51f892abb6e4fc87ed735bfcb51433cfb4371cb8be8690c -dist/2025-02-18/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=157d6eb9e7dd80dda7daae5584d095a5e68d13ba8b1d1229e90b5b51f6670e8d -dist/2025-02-18/clippy-beta-aarch64-unknown-linux-musl.tar.gz=fd4de290450e3d3c9188263d9506167e298f4c2d4c781fb3573806694b5ca1ba -dist/2025-02-18/clippy-beta-aarch64-unknown-linux-musl.tar.xz=335c5fd24d1c118e4d1d8abc43d3529dafc9b30af39f6b009924cd0fea676182 -dist/2025-02-18/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=209cdd2042e293d1061b149d97333515821dda0ffeb9b30f24dd2dbb4c1ad548 -dist/2025-02-18/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=57dbfdc34ff4e74aaa8776b80d35aaf0317fdc314ee4b0f3bf58fc672963cf38 -dist/2025-02-18/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=a89a2562fe9ac6492a5de220c395f433b0e53a8b29c72b3a8d4843f996649ca6 -dist/2025-02-18/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=0e6ff6453df1397e7b12e7e356e7c7300cfb1f85bc3a6705735067128d2a8135 -dist/2025-02-18/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=7764c2dbaf217f4b1d8ca4ed2ceaacbb91e8010d26ae1bb10b808afd5dc6a798 -dist/2025-02-18/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=b7a042eb6830653a623c6299ef57492ae6c9b51a9cd8480f0a6fc2fb6d378bbb -dist/2025-02-18/clippy-beta-i686-pc-windows-gnu.tar.gz=ff41e5b666b3531ccdc0ee836dceb270179d6a849e47024eedc6ea309d36791a -dist/2025-02-18/clippy-beta-i686-pc-windows-gnu.tar.xz=98c6386b63ed89dd96beea223f4c720320b6ca460f11b77c46427e128034cbbb -dist/2025-02-18/clippy-beta-i686-pc-windows-msvc.tar.gz=12bd371402e4d3251f60fa29b3b3aca88b4480229088b68791dffbb4ae1554ce -dist/2025-02-18/clippy-beta-i686-pc-windows-msvc.tar.xz=62a22d1e9b4fdba254e549f5edb276bef5252754ba67a76526d5aeca85515646 -dist/2025-02-18/clippy-beta-i686-unknown-linux-gnu.tar.gz=04179b3a20a68ae2fd8444832ea4edd8155dc9296c7a1edf101053e3ff934891 -dist/2025-02-18/clippy-beta-i686-unknown-linux-gnu.tar.xz=c955e464e99097025fc7d0e42bf90342f74f475c5148526df2f51ca46ce8db4d -dist/2025-02-18/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=6f58d326a1f2a35246384640b999f95a961a9fe1e5a3c4a3e67d7f96e1ef43fb -dist/2025-02-18/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=583b1d55a38fdd473a3f3f3cc0fbeac808fbeb593b8b45c7a6b8a09dec0d68cf -dist/2025-02-18/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=80d73fe94ecbbcdb14b35e1616d52e0bb9b1f04dcdd4fc384e7103cc36325014 -dist/2025-02-18/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=1bae908e4d3931eb4dc71ac1524f990bbccaadd9d30897bf516899baebd4c5d5 -dist/2025-02-18/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=b882e7ea97e0d36b289a46ef84b14b2b44ccea93ebdc77b2ab3430361ad15b1f -dist/2025-02-18/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=91e52dc398dccbafd040e401f155c204f2a14d5f62aecfc24912910d5e45008d -dist/2025-02-18/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=c81497f2b2d2670b9dea2e35804e10780c1631a8761d4bc3331767c1ccaad6b9 -dist/2025-02-18/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=6236e129f3e551a3126fa07662cc8ad512ad5fcf436a8da9e27a915ad4c234cf -dist/2025-02-18/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=1b031f055ea4ab4a9da0c8bfb0037e92b493caf7879351f8b404a96c447ec92c -dist/2025-02-18/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=2f07224e49f7cab10d673116d9dee9f964b4c44ac3db336e7ddbab9e0b1bc698 -dist/2025-02-18/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=84a8295a7066d258404c87d2269a01d89cc7efd0f143ab53689597f3fb351417 -dist/2025-02-18/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=019460a6b0e1c5c1331d5a54f3e45306ba0d1b1c2206e2e69f38c1c3501cf741 -dist/2025-02-18/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=aba8f7bd0a89205961f9803418cdf9bb63a7d72bab4a1ff937f4a142921f4833 -dist/2025-02-18/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=4607503ba131ca655dab86ad48c1737f216f8ec25ba6dfddbf1f0807e5a6dd2a -dist/2025-02-18/clippy-beta-s390x-unknown-linux-gnu.tar.gz=b0170f9b9e13d4f6e016124fd40c858e00155479fb4f96c1edc896130bb76dcd -dist/2025-02-18/clippy-beta-s390x-unknown-linux-gnu.tar.xz=397f0a8944beffb87c74717bd0f422836b3daccfa848d91c0a65875031bf12fa -dist/2025-02-18/clippy-beta-x86_64-apple-darwin.tar.gz=11be90b9b09f4e24e56df2a602ed8161023314514121d9f76e424fb628df468c -dist/2025-02-18/clippy-beta-x86_64-apple-darwin.tar.xz=16d313798f12d0e74f888b627e079606958de58eb2ca4c70d5f7de8cce72620c -dist/2025-02-18/clippy-beta-x86_64-pc-windows-gnu.tar.gz=41ed081e4a7b205cde3fe958db18ffe1c1ac5a9c336f965a03fe89499a3eddbd -dist/2025-02-18/clippy-beta-x86_64-pc-windows-gnu.tar.xz=daaa3267dcbb3daa0cae47861683f29da5b7419214ec5949e242aa93e3f87148 -dist/2025-02-18/clippy-beta-x86_64-pc-windows-msvc.tar.gz=6d9b8f549e34d8fb65987b39c01c27fbb0e18e8950a07ec6fd1888d01e253971 -dist/2025-02-18/clippy-beta-x86_64-pc-windows-msvc.tar.xz=1c26b51c484f5b10ee693212e258fb86c63a2b59e631542918799d480a3587d4 -dist/2025-02-18/clippy-beta-x86_64-unknown-freebsd.tar.gz=a42777c542773a4262ac9fcb07ed13f769957f58a795160441241ad4e4f5258a -dist/2025-02-18/clippy-beta-x86_64-unknown-freebsd.tar.xz=9e809fa141ac68557f203aec2a6cc02a4ddd22169595c40ed4a964801ccadb1d -dist/2025-02-18/clippy-beta-x86_64-unknown-illumos.tar.gz=4e65b5db249a18e73247029506f619393be7042f2c91a00301962effb76a18ea -dist/2025-02-18/clippy-beta-x86_64-unknown-illumos.tar.xz=6714018d069fbce271c177f1941a6b77f18b67af462596aff273f7db1970ef4a -dist/2025-02-18/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=72a8ee3bad3d638af0cc3ba6c0c0ed1594abe609573de71d9c7a884a8ac7645c -dist/2025-02-18/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=f622befd60c3695d3d62642b953deabc3e3f04b32cae8a7cc228cb534244adbc -dist/2025-02-18/clippy-beta-x86_64-unknown-linux-musl.tar.gz=8946e02ab98281b6949c4d725a5fe3fb1bfc86651a675e451f126fec8d053762 -dist/2025-02-18/clippy-beta-x86_64-unknown-linux-musl.tar.xz=425907bd4320c516647e5dd6df6154746d64854fe2e78f27283b7fcb27164de7 -dist/2025-02-18/clippy-beta-x86_64-unknown-netbsd.tar.gz=505208d2d73ed7278e5b29d8450c382a89e4c0992d6199243e07c8640c611b85 -dist/2025-02-18/clippy-beta-x86_64-unknown-netbsd.tar.xz=d4b0306e7d7db5cb7da977a7ca72fff2070f102ac385c3b35286da25033582af -dist/2025-02-18/rustfmt-nightly-aarch64-apple-darwin.tar.gz=13854358645915af29d9337bb87301aa1a5e76ecc86a934dd640d02af3255ea2 -dist/2025-02-18/rustfmt-nightly-aarch64-apple-darwin.tar.xz=0cd190bd5a064235f9cd6f6e7eae4725b3b53ae36493b3ea54b8f190372ba3ee -dist/2025-02-18/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=20b3f8c4c7b02158181970804d419b16f85a1083943e1c384e0bcef441f32aff -dist/2025-02-18/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=1ace0f10060d2fc35a1f05a1d61adebf95212e8b46a2234c2e78030481c1b3e9 -dist/2025-02-18/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=e5ad64d67931df49f6b7d3448e9860699d6549c2b4a96abda1a03069b45f339b -dist/2025-02-18/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=e8112ae80c8fd43452612b79dabf471be561b7e828c9a2141c698429d761a49b -dist/2025-02-18/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=8ad36bbe888e61b6d7e540ba4a2478652f14c1b28dbbcaab732003293b7c985b -dist/2025-02-18/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=1794af320a273f4a951942ff0cd73a359fd82a971c572af1ab7ff2961340791c -dist/2025-02-18/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=67b9d1d3c46bcce0aa94d6cb4155cdf7376677caf2e19685553266f781eb7cc1 -dist/2025-02-18/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=9d7b97a85b1368cfc78f1df42afb08aa04eb17fbb91ceb3c379d59fab4294890 -dist/2025-02-18/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=42396c7bd206d0fa07f3b44fcb894ac074e2af07eb158c1ef630bb5467151c8f -dist/2025-02-18/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=6881c9681342f1430fd9cc8c9786cee40522be3dcd0fc4dcf2b3957d9d53f03e -dist/2025-02-18/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=6cb8973ac4215d67aad9bb56521024626d7883a6e00748623a728dd9107698db -dist/2025-02-18/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=920d877e04260be6ccf9963bea12220e886df673cbc48b55925650f8f5b21d9f -dist/2025-02-18/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=ec5c7105f2c2a2055f704435fff9edbbee0a234e63050676c034f983d4e341ee -dist/2025-02-18/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=ab408b64db4bd47abf8e24c0f611f2c67a3c9ce7e2138a92772e0510c6dce5f9 -dist/2025-02-18/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=d04250bc2e57fcb03f1d80722f2ac1117f797d768262d7630c9b53af808a8c9d -dist/2025-02-18/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=6d7be9f04a46040f068544e32eb84c98afc1f81b750beb229def84a7513150fb -dist/2025-02-18/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=94f585a7c666dc9c95c3ae14744e614a3fbe37b3e31b476b48bc4ef3289bdd46 -dist/2025-02-18/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=83e82932245b3c1d3c275126184f942b9cb8bf20f0d40c7cb1efb6f918327b9d -dist/2025-02-18/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=9c8cb5a14294c48f8ee972d5f460751a8bae5b541bff3d872c85812b621c39d8 -dist/2025-02-18/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=4f616740bef30599e6950ae2d8ae694513003e14122f6002a9d60bdc64f77cfc -dist/2025-02-18/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=a668e7be7946f6a9099486a40256f17832288322c9237d931f172ed76caf678d -dist/2025-02-18/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=b02c57c217f358ff4344ee2afcbb8353cffc0a012f970d9520ad233d188d59e2 -dist/2025-02-18/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=9baf04e268999020372b21484ecc4f5aa001b3bcee05619ae5b0a11571a3a45f -dist/2025-02-18/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=b2c2c3a0eecca7b4c35eabf09e215158c48eb426899e365db1c91c1c560ad255 -dist/2025-02-18/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=c42df8e575a24a5edbc58b5b46a89a226188d2aafc59da89d568acbb6e9cceb6 -dist/2025-02-18/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=fee7c373bb8fee5c7d4e43d7a7046d3bb156d3e496ed21cddf20c21ffdef3e69 -dist/2025-02-18/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=a0ae21a3b56e20e667c72d92ad4bacd4abb77d1fea32949d7dd62a56d6b531d3 -dist/2025-02-18/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=fc7e21cbd61169b37e81c73d4d5353d7e54ca322fd01491d9f37ff3666557e7e -dist/2025-02-18/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=e08ceaa9241c2e53041b107af09674ca4fbc4e12b1371254869e3d80d5c907ab -dist/2025-02-18/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=9f208dc6e5938a128174263349345e9fe7f587689f6a53fe5d026ae3c6fbed2c -dist/2025-02-18/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=44abe49dfc0492cb3ab28f0aae0d784c982063399a38f8f13016f3dde123948a -dist/2025-02-18/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=72fa294f92ab244608c384d6ad6eea0e540e6fc58181fd65a4fce0c5029c39fd -dist/2025-02-18/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=556dcf393856c6d9c9c6731b02e34146f9f956f588025b6a614b8802032d125a -dist/2025-02-18/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=93906364910891f71e13e0ecb07f02b707e42e11a0f1b117c590804dfa16a9d1 -dist/2025-02-18/rustfmt-nightly-x86_64-apple-darwin.tar.gz=7022c760f457075fb353e59984dcb551600581a315fd17ea1320b892b1d99a55 -dist/2025-02-18/rustfmt-nightly-x86_64-apple-darwin.tar.xz=e085ee261acbaa41361a0263dd8f13644f8a4e3679eca6bb0851b03c647b80e8 -dist/2025-02-18/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=2436e18fefae02554e620a1131455d923c1b67e00e878c75a941a6eedb4eae28 -dist/2025-02-18/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=9f6e37b5973925d07115e517e6a3e490a221218f48fd101a20670da2c3d01add -dist/2025-02-18/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=78f492a26981e7a6f9f3e8bbd1e880682e55cede081f3cfb13a2ec5f736edbb2 -dist/2025-02-18/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=d4d788fa3e3b9a598aa7baec47af3b56362224533be85dd68a411f35f8800b2d -dist/2025-02-18/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=8922186dedf0b12e9aa2bb041b76be8fccd43b9322c3496c688355f34bbd7a59 -dist/2025-02-18/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=6325e43f2c28118a9525f5c5fc09de9fb9a09ffb4aaad31f28d394c233ee8398 -dist/2025-02-18/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=7f317107f8319c650973cef52f745c2a8e57ea6a087910b6979404f2cb2f1cff -dist/2025-02-18/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=b273be4897e83f786020bce1dc5dd1caf01e2ea43f085391c0031277718feba0 -dist/2025-02-18/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=0373011266a9ab62f45fa137a4bfdb970ccad8bbbb74bf776466d203f28d226b -dist/2025-02-18/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=62748525678370dbda7f1cbd12a384e95d4af76103de998785278f6d1f076177 -dist/2025-02-18/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=30eeb980720d8c22bc45c012c4fd212e118195206b8b993630f074e526e6693a -dist/2025-02-18/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=2f3843d9544fa56dc87f1d13ef79c68c1e68d84bec007f0345159a7448368dfe -dist/2025-02-18/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=a86eaeab8f5f45d16eaf355a46d8b19afcd8c46db732432091156e381aa79cb6 -dist/2025-02-18/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=144cd28da601c331f6501c634ad89286be213a69cf521435e35657c5f6fe2afd -dist/2025-02-18/rustc-nightly-aarch64-apple-darwin.tar.gz=21101db859a550ab39de1ecdec75f4f058934ed8e0ab7dfadbb2efab57bc5e0a -dist/2025-02-18/rustc-nightly-aarch64-apple-darwin.tar.xz=a40da26e908810701112b9d3f9ab83515c8e2f4b33219f8f773c5459366d37e1 -dist/2025-02-18/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=5391bc993b7208c10a6f4256417c9f76a4a33d167b2fd547b57af614d3fc8458 -dist/2025-02-18/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=d72a205f52173b1726d8341a9436e62439b1987fe0cd4d2bbff740e38f629b41 -dist/2025-02-18/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=185e052bf2ba1cda4b6ad1c49a9f65b906899ad1ca09cd864d6041941ad344dc -dist/2025-02-18/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=640f7af3fef5f0abacdaa292ce17255433ee16f12bfc2d2e81d70afcf9fcdd8f -dist/2025-02-18/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=8cdee468a90606b21a8cc3305f2b6b3eb7e3d687263f4ca8319c709cda2a324f -dist/2025-02-18/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=358bbfb24816c781e07d5480a5606dce27068f856c0a606873ceba05c9540c3c -dist/2025-02-18/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=b220757cc8ed39c429c49b55e293ded3e07239d2e2bab8a9054ce55581393c47 -dist/2025-02-18/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=18d3cbc49e44b7810b498517d21390e39bee7ca8351c8b321592e83301ad79e9 -dist/2025-02-18/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=77ac127bae48859f57d9bd8337ac4a0bda95b1190e451abeaf7e77f1a240a647 -dist/2025-02-18/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=79009bbf0325f9db0c905d74cb0f139c92478a0b2fb5edaf383366b0e654a172 -dist/2025-02-18/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=8264c7ed7bc47953ae76cf87236655562a55846abb195bc9281833a0da168db6 -dist/2025-02-18/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=c6a85e7f2a469fb05f0a84ceaea25dc0d95b57bd4d68dcee447918162cb36b2a -dist/2025-02-18/rustc-nightly-i686-pc-windows-gnu.tar.gz=0f31c9ed4ba7e942806fc2927d305f9d3ad676cf81e53e0f7c4123aef375fedc -dist/2025-02-18/rustc-nightly-i686-pc-windows-gnu.tar.xz=350f4b46dee2aa6ebfc31912cfae5c7c1db99b298c52834308e95c504cadbe7d -dist/2025-02-18/rustc-nightly-i686-pc-windows-msvc.tar.gz=e12a40b7178995ac223eb7fa317e1114a988256d99fe615f70d64cf3f2891fa7 -dist/2025-02-18/rustc-nightly-i686-pc-windows-msvc.tar.xz=09fb4a4744a55bf8fd6371e5496f6a9c00b119ddf20b19855653d66d9a618214 -dist/2025-02-18/rustc-nightly-i686-unknown-linux-gnu.tar.gz=52361a34730f8bf14eefad7e51f66fc3ba3be405a364d4520103208fe8fdc056 -dist/2025-02-18/rustc-nightly-i686-unknown-linux-gnu.tar.xz=ea81964fc8bcb8b43968acb6a4f7fdec2ddea9f75a8be5446fb01b4267d0d75f -dist/2025-02-18/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=11e7a143112b6abf818652e425b6f092464cc9d8f5286e90180c7af8d5939643 -dist/2025-02-18/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=86f5d40eecccb623ff744bbc1fb40458949db9b2f8501cadc6be9bca95f6c693 -dist/2025-02-18/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=1be7ecdf609958fb0ef945203b22a600f1e343ee7fc581f6451eecd39f1e0b7e -dist/2025-02-18/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=03d3b67092da64f7b7be7ba4e115cfad4071c0bea3abb5e60ac166127713b169 -dist/2025-02-18/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=4e33311490c2dcf79a16307e1141ce37574ec3829a1e93d0f5b06ff68ad5c861 -dist/2025-02-18/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=504c102fa1c8dc5042cb354a60b87c0f6d47f9adab341f496af723f3ea8bd548 -dist/2025-02-18/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=c559971ae42a5e0e999e2fe119033bffbfe8c64b767e697e0b95f5dfc271d13e -dist/2025-02-18/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=51159ab0ade5a62e128b6c794da5c9815324470c468e5808b50663c3a0df6d39 -dist/2025-02-18/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=a8134f8f7ea4531d54ab5c0a0894f140ce5182a384c57395edbe06ed551970ab -dist/2025-02-18/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=8f2f5d88ba9f4ee946db5f00d56c4852213dcb0b46ce2793966af6c96d934dc6 -dist/2025-02-18/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=7bfc6666757b57fa6160d076a9eb52852dcb5bf788bd12c0f014bbd552b8a7ef -dist/2025-02-18/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=e2c3293e2a90012ad48bbc2b6f285adf20f356ff1f4146b3f11543511bbb3c44 -dist/2025-02-18/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=f582a59b5a6cbe6d58400c69f1cad4f34d0f1541c1ffb9df3ce71846edd828fe -dist/2025-02-18/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=244384bd94f7c3606a086abc8be9d36a855faf09d9bb6b121f42e168c526e717 -dist/2025-02-18/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=327e29662140cb76b1ff362fe51f9a12fb4ec304929317ed8de7295fdb9c5b9f -dist/2025-02-18/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=a2c4e19b3a8dabd5c8fe72edd60d1925947cad5971c77c62f12fca764c6e4e7a -dist/2025-02-18/rustc-nightly-x86_64-apple-darwin.tar.gz=b6812f92e08cff1b706380637fdd553b04b9cea746ffb69e6488fbec70680136 -dist/2025-02-18/rustc-nightly-x86_64-apple-darwin.tar.xz=f2502b2a810938c41e2302a9112c97d04998ada16583373e6488bcca00595761 -dist/2025-02-18/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=9159f7323718c6a8b98f7de5186ed507b3dca1bfcce9bdfa6437313bd630a989 -dist/2025-02-18/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=10d54c9b80570b6729d0e68f017d862c5126f09b42f150d004aa8fd5382b165c -dist/2025-02-18/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=1f29c1aef1dcc3ff9f2e3013b7ea0521829e6e474f9058cb70fea65180230b41 -dist/2025-02-18/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=18cddaf7653f146dcc8a44ca0cc33c6dd3c24f28fff87aedca01fe3beaaa9915 -dist/2025-02-18/rustc-nightly-x86_64-unknown-freebsd.tar.gz=e57c1477cf9b75e727db48b8b75435d65aa0a6ef0eb566ac1890b576c330f64f -dist/2025-02-18/rustc-nightly-x86_64-unknown-freebsd.tar.xz=725f9e9d25b7ad6f60d5596684964b0c3a63d98a62a2aeda6a134b9f02fdb681 -dist/2025-02-18/rustc-nightly-x86_64-unknown-illumos.tar.gz=86a9034fa275c0fc73d639fe6437fc4d2621bf12897c9f83a81ab6056173a95f -dist/2025-02-18/rustc-nightly-x86_64-unknown-illumos.tar.xz=4aeef66ad8a908589ddf0d89581a6ca809b7c2a57f06bbda6fd3927b531fe07b -dist/2025-02-18/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=907aa282fb9f3d96c9fb03dadd62f4ea766e1242fe19106ae331a4f49a9b20f0 -dist/2025-02-18/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=e63d13a6acd596ebfd7bf65b292eedd2a5e260b536ca11517a3fe1d8e6a6241f -dist/2025-02-18/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=b7bee098c32b321551f68e96e002f85e3a0323c864aa7f65641a58ae24f4238e -dist/2025-02-18/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=e5c22c2fab89c7ba548b11104c54e8f82cafb1979ba73a318682bb10ed9e4fb9 -dist/2025-02-18/rustc-nightly-x86_64-unknown-netbsd.tar.gz=7bf40bffe95437244f6f8a5fde69499f42d2b716e166115530fbcdbdcd837887 -dist/2025-02-18/rustc-nightly-x86_64-unknown-netbsd.tar.xz=0adeaa382f289233ffc9229e116a340ac03a861f0fdbb5bd35aaf5d0d7370877 \ No newline at end of file +dist/2025-05-12/rustc-beta-aarch64-apple-darwin.tar.gz=e5ec8453efc1f51d37d5031d87d45a327647614b00993d1b7f477c7d2e6c7b16 +dist/2025-05-12/rustc-beta-aarch64-apple-darwin.tar.xz=6711902d59079cd57d6f93e951d3028acb5cef0f59a2ab87e1688edee96f6471 +dist/2025-05-12/rustc-beta-aarch64-pc-windows-msvc.tar.gz=7168682081144b8eacab42efe6c9ddb9ee6964712d271988345e63d2d6faac9c +dist/2025-05-12/rustc-beta-aarch64-pc-windows-msvc.tar.xz=5794e0d6bed097d349e138c7602a083f4025604f711328c0a4548e27f191444b +dist/2025-05-12/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=c4d31776d1b74dcc6184c2ed6064667b1ade59c68fb355bee812a805f61234f9 +dist/2025-05-12/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=970b0c910f8ba2b5b470ffa7959466526b0f99211578f7d8ceca8d0aaa23fe1d +dist/2025-05-12/rustc-beta-aarch64-unknown-linux-musl.tar.gz=8e9c80f826b4571136f082d3cadbb4668167f19688a3da91fc732464b5a604b5 +dist/2025-05-12/rustc-beta-aarch64-unknown-linux-musl.tar.xz=09731185aeb15263cfed5786ccc78fee0db70f82aeb5409f8bd8b03b0566d491 +dist/2025-05-12/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=4ae8dec81d8f2d1aff7710a357e3c56323cae56bacd6b014fdb4058c06bb75f0 +dist/2025-05-12/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=4767de7ea81913c6ed33907d93dfb56664d9bce0d095f33f0ca5662b284a94d7 +dist/2025-05-12/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=d2233f4e687bb1bd407593f6d7a8c288581b7209d758be49f0681e1f556083e4 +dist/2025-05-12/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=a8f9778a765d9fa8a0651b7d6fac8fdebcbaa61e903a32e7cbcd88bcd9418bd3 +dist/2025-05-12/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=bd2ee7918df85f24a34911b91a233663b4cf706e7c54784c78fea8e58c12ca91 +dist/2025-05-12/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=9b1a1c4eb35d3c1ec97132e33fc6551ffb280d6b2c9d049bf0392441674d338c +dist/2025-05-12/rustc-beta-i686-pc-windows-gnu.tar.gz=729e4d7a116d8ee2a42489484429b138bafc14b43c87adfedaad442515e61c15 +dist/2025-05-12/rustc-beta-i686-pc-windows-gnu.tar.xz=8272b95f1d99dff28f22161d0181ac0e64e1909d51448f9ba4bcbe09690e79a9 +dist/2025-05-12/rustc-beta-i686-pc-windows-msvc.tar.gz=13a7327d08d26ba1911071c798d520b74422e320f5cc1c41d4e215a5615e692e +dist/2025-05-12/rustc-beta-i686-pc-windows-msvc.tar.xz=0f9ce8fb06bb1ae460ee82601c269b885c109729df342e5b6b05b9dd9b51560a +dist/2025-05-12/rustc-beta-i686-unknown-linux-gnu.tar.gz=82b54be8042baa56e1e6c0346f2044a84c4a50b3df6fe813d45eab21e1fe8935 +dist/2025-05-12/rustc-beta-i686-unknown-linux-gnu.tar.xz=48c9a8181b6ac7b7b6fb4535391c0498965127f5b5ac694de7eb1dba7ed8e9d5 +dist/2025-05-12/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=768156149054211735ec45d0091a8e7dfac16a39c44e122af5b28b316a45fd00 +dist/2025-05-12/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=50a38f72a253bfb8005a9cdd49621289f8b4a2373247957f520f5c5d1f12db29 +dist/2025-05-12/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=f79bb58d8e2c80270a4c9d7076ce8645b2ea3f64db5077b085cb4cc6763f5e17 +dist/2025-05-12/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=eafeaea2813e34ef0606a9f935fe1a104417604686ef9144b899fe97de53aa67 +dist/2025-05-12/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=f557e00500071835712afdc9d91161a95b1cca5cc4e32abebcf5d35a9147eb2b +dist/2025-05-12/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=72fc4d26e06d74349e65415da211429ec92cd479aae78f82e223f3f760b0e63a +dist/2025-05-12/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=a67b7e5e0b30227b07a41829c5e88180d9c404c2ce37fcb10d8df702c2b3c222 +dist/2025-05-12/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=0c4cfeb6555283e58b75533930783e7cc3c838f9c8eb34938fa60656b15568a1 +dist/2025-05-12/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=7e6f02eede8d87cd5bbcd8dcf8235ebabd1237fb294cf1d0dcfaf961f3628d95 +dist/2025-05-12/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=a5e9612d42f999a7b0fe22b2d5d5def21162aeb604c4625fc70259c5ec2b669e +dist/2025-05-12/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=8fc92b9e35110a53458e08b49db1809a23060f8d05e742561cd746fd206085f2 +dist/2025-05-12/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=8b3fc4ac4423bc71b7402554436d1e6e62ff06b36c69f7be724e8ec5ebf96352 +dist/2025-05-12/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=d0777e5ea794a9d19a2a1744acff649a1bac8fc616f6df41410553ac0b3c275d +dist/2025-05-12/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=849039740272c91141862a028f45889d4874ddc83842a66b906df37b7a30f9de +dist/2025-05-12/rustc-beta-s390x-unknown-linux-gnu.tar.gz=3230ab1516a19cf803952138ef7f815ce321d7123539539249b76f6afadcf9ed +dist/2025-05-12/rustc-beta-s390x-unknown-linux-gnu.tar.xz=12c8e476a73d71d58d5438ce94bb2fa822a8d043015b0961af14096d68c52daf +dist/2025-05-12/rustc-beta-x86_64-apple-darwin.tar.gz=01717cd3b5141d29896caeab17ad61a27b8b7af6460745f245d67dd066a09924 +dist/2025-05-12/rustc-beta-x86_64-apple-darwin.tar.xz=fceb7e0f431f84621a22ae50ec9694cd0ecdf90801f953295b1975b0aedb4fff +dist/2025-05-12/rustc-beta-x86_64-pc-windows-gnu.tar.gz=1f7abd7650cab64cd09848ac8de9b7e0047f6c77eb433140fbae8ae8b522c019 +dist/2025-05-12/rustc-beta-x86_64-pc-windows-gnu.tar.xz=4e3e07967e44907cb2b2ccb733b969014ee6efedb82412dc81f95533d2d473be +dist/2025-05-12/rustc-beta-x86_64-pc-windows-msvc.tar.gz=3cc10eb4187e09a48efa5351250e09c83edda4296d605dcb886eb81f9d6580af +dist/2025-05-12/rustc-beta-x86_64-pc-windows-msvc.tar.xz=9ce5c89a9b2e7360c7991c3f976bbbe9bf9685854d1019aa6dc1cc5b9d13eb88 +dist/2025-05-12/rustc-beta-x86_64-unknown-freebsd.tar.gz=3a9e92319e91c0498a3e54ff5ae00f4e1ecfac9b0d4f291885c9feef89d356df +dist/2025-05-12/rustc-beta-x86_64-unknown-freebsd.tar.xz=6930ccd83b6b63d0a876eb5ac52c32a8449fd4cea8666b919494ce6358c6122c +dist/2025-05-12/rustc-beta-x86_64-unknown-illumos.tar.gz=c1a4ad2cfa4b7c3181ea0facc3b18baea7f4138d089825915eb41630e5bac500 +dist/2025-05-12/rustc-beta-x86_64-unknown-illumos.tar.xz=44ae303a09cbc8a198c0cd947f958229b0e605842666a3b0aadb0f1f69f34ffc +dist/2025-05-12/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=fc55fe3f5b2d206417452de880a177f59004762e58fbfa4404f0b59fdd7075dd +dist/2025-05-12/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=a5ce304c4798bbacc998b2350d6ef79e9845a7ffb28bdf0af6066869667a0c86 +dist/2025-05-12/rustc-beta-x86_64-unknown-linux-musl.tar.gz=391cb81e61589377ab0a6780289628a805a5b1d842adc29e66ee5731f36372af +dist/2025-05-12/rustc-beta-x86_64-unknown-linux-musl.tar.xz=7c3ac5df14b28b99e3e2d0072b5aacc59acc08621731fdebaa3199059ccbeb76 +dist/2025-05-12/rustc-beta-x86_64-unknown-netbsd.tar.gz=670beaf2ec21118fb099a1b034b33665e360b8f1920b9cbd5fb58271a8aab9ca +dist/2025-05-12/rustc-beta-x86_64-unknown-netbsd.tar.xz=e4982c3e4b9f757485ff9aee183d973e31b2c485dbb39387c1afe4bee0fdbc30 +dist/2025-05-12/rust-std-beta-aarch64-apple-darwin.tar.gz=c2786d9e874eecea00935c62c58e2e3ddfbe11b4c99f9ce807e2251640c8f7f8 +dist/2025-05-12/rust-std-beta-aarch64-apple-darwin.tar.xz=0f2a6b28befa7d44055f32683d7b9c4de19ffd39c02fe6ce44aeffbdd1d13ea8 +dist/2025-05-12/rust-std-beta-aarch64-apple-ios.tar.gz=3cccf751678cc229a7ca3b39cbee4467230bec235e16b48acc576c825e0be15c +dist/2025-05-12/rust-std-beta-aarch64-apple-ios.tar.xz=9ed9b7f1672d887fac4a0386027440651ef99c682ff21b1bd9c1ddd850934613 +dist/2025-05-12/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=e7e6c0c7d9fa99f268d7601a127c6ce07df620fb27462dbaf933124a5786ef8a +dist/2025-05-12/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=abcecf3ecdb72714f35981847a91190c3f038dd5dce23a68253c7129fa6abf3b +dist/2025-05-12/rust-std-beta-aarch64-apple-ios-sim.tar.gz=c50ac5245e87b5e251fce3ff847ddf7d62df4490843e8a5f592515517b04d406 +dist/2025-05-12/rust-std-beta-aarch64-apple-ios-sim.tar.xz=1c913535759d008327eef49e47870d3afcf609c29aab4a188209c3cfea954682 +dist/2025-05-12/rust-std-beta-aarch64-linux-android.tar.gz=6120c1b159fa4f0279f8952aebf8cf1513f5b843905d64d1efaccaceac79c1f1 +dist/2025-05-12/rust-std-beta-aarch64-linux-android.tar.xz=57ab4652b879df33556cf04596f0f9ad9b0eee832b67e33c8c8cdf812c229a6e +dist/2025-05-12/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=7afcbb49691f8286ac21107598a7a44363a8e385eaa648ab2e7711f87ddedfca +dist/2025-05-12/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=ea8af597e49e924f1e04eb3435afa09720c81f43dc467461de1058265d36dd64 +dist/2025-05-12/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=2d8791f8ebff5f5f679c8b1735fdd1f0a4d7968983a5c2ddc5e036ad35b31f1e +dist/2025-05-12/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=a7f7bb3269dd7312edea5c6fef81d373499a670804259cf7853ef346fff42ee0 +dist/2025-05-12/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=9469cb7871dc724148489180df240dd51c0388cd9bb478adf272934e38916b73 +dist/2025-05-12/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=315f3dea48c50819f925bd32a3a5181591d4370eee4def8e37448828e622ab06 +dist/2025-05-12/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=abfaa164202c7d5d3c7e956b10a5ea612b092ee45d6c05d5c19a097617cfd703 +dist/2025-05-12/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=71ef1275726f6c61113bf1d23099a7557461205b6be243a952fa806ef15d9413 +dist/2025-05-12/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=d651e5e46e1251952e719237dde30ed7ecdb6b95a7cc0398fc635a76b94c552a +dist/2025-05-12/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=0a67ebf159539bc7f5a4e5698a0c74550da3c5e2cb0b5e1dd694ad29e1f35834 +dist/2025-05-12/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=c0f1ecbbdd5234230d2439620c0ebe9b1c3d331388cd174cdeaf48d724172aab +dist/2025-05-12/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=e2ba0a2853d685679422c065f266ee57f269bb5a231c5af5a791559a3609fb25 +dist/2025-05-12/rust-std-beta-aarch64-unknown-none.tar.gz=04b4eaf5910e662364b5ac3ee08ddffc2eda3957892ba99c8c945f5e1a18747a +dist/2025-05-12/rust-std-beta-aarch64-unknown-none.tar.xz=065751b346f9c3d76e164a9edc123f277492ebfaf1d00db61027e4fb17d50f79 +dist/2025-05-12/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=056a135278dfdafb5b22c8f01bfc77b17396511d67b55c1404693d801e584262 +dist/2025-05-12/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=fc086ae7ca3a5c05790cb41dfc382fc65f929c669efd540c07131b851b78a743 +dist/2025-05-12/rust-std-beta-aarch64-unknown-uefi.tar.gz=97a6301cdd34da68d5c6b243cc125f7e34215853e405d9b34bc715aeda3223ab +dist/2025-05-12/rust-std-beta-aarch64-unknown-uefi.tar.xz=3f2055ce638671316dc074595a35b893eea7be596cff218ec1416f3259ff86cb +dist/2025-05-12/rust-std-beta-arm-linux-androideabi.tar.gz=299158c865df15424564be4d72921b8b25993b0671e4d462ff69f49ea29367db +dist/2025-05-12/rust-std-beta-arm-linux-androideabi.tar.xz=6e71d518bf5f4a29b91938ee28b3c9b22509f3d97d4331ddd8ae0c1069192310 +dist/2025-05-12/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=0a00703833d46720e470ed90f81a08d9c20f63932d852e379fe63df955e61c9b +dist/2025-05-12/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=57be85e4c2d4eeb4cbb19f48150693d4e6dd2969d380b1d55feb431c858e4c35 +dist/2025-05-12/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=7b96461125b04d98a550bac5a7c3dad9c1df65ce849758d867c72ffc0b475012 +dist/2025-05-12/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=a66602af671667fe5686c7a4e395d3dca8374ddae10cc9260e23e20f65022549 +dist/2025-05-12/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=deaf5c7ed339c8a7bc2af94888841b647f8118854f698ece4ddbf900df921bd9 +dist/2025-05-12/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=eac2d4d330a5300ee297c2eb61914b86efded3d494c5a73e2f91d989cb2896c4 +dist/2025-05-12/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=479a5941193d14e2d4d50fcdbecb31103f6a143bcd3afae887d068c2ebe14163 +dist/2025-05-12/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=2483258323175c1e338be84ce52d44e15177096643beabba9d806c69cbed23dd +dist/2025-05-12/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=528803fac28b0a0025dc50324a6980a4b561e7e3b99d7428b8ed0a73fd3dd462 +dist/2025-05-12/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=6603b9aa82cfd563d7c462ebe50058c36aff403aa9e3a1d6a305780126aee481 +dist/2025-05-12/rust-std-beta-armebv7r-none-eabi.tar.gz=6ae7f3e39e974e20e9cbfae276fd4995063c5702c41085c2b764f3c37cbbfdec +dist/2025-05-12/rust-std-beta-armebv7r-none-eabi.tar.xz=404ae1fc0f5a6995ced2f66fa863cfff17c863096e99b5a04c841b97e6f0e28f +dist/2025-05-12/rust-std-beta-armebv7r-none-eabihf.tar.gz=bf6aaeeba558ac148b693c4e4d231415f6e72506b50ee06b0a1f987374a08df7 +dist/2025-05-12/rust-std-beta-armebv7r-none-eabihf.tar.xz=ed3f8767f5e824c5b81178e56c6084c45c67653793128d2c08146533333cc0ba +dist/2025-05-12/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=7d2fae89c459d65fe2cd28acaa225f0ccc35b3f49c84ce6aa86e2c40dba38e03 +dist/2025-05-12/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=fc05ce5ee26be4a233181b9841975c0975fc45ad5466d1001a24a01e2a31123b +dist/2025-05-12/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=0335813546a1f905e274135b2bd97c3a0c95f2e0d992d7396bc110b800d3ca8c +dist/2025-05-12/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=7031aeca445d4f8fa351c7ad2e0e06df0386ed11f91080ea65968f1716006bd3 +dist/2025-05-12/rust-std-beta-armv7-linux-androideabi.tar.gz=9abd7fe0b7163a141d758ccdca422bd32ed4ad3618066ac022671b082f4641f9 +dist/2025-05-12/rust-std-beta-armv7-linux-androideabi.tar.xz=2bcdeb652d42755528a17b86a3b64b13b32d1ba9207cd2c9ccb43fa0d7a1c6bc +dist/2025-05-12/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=1bad15b2e9806e7858d5d4d58f6b2864c3f04e65d4ecb1cc448efdbf0e0030b0 +dist/2025-05-12/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=f2d9039c903e5c309bbd17c7567462d4663665cbb7e1d98154022d98a9883719 +dist/2025-05-12/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=13aa4a3ef68a87de259726c7c2a3906cbf013836f753b707a453bf91879f023b +dist/2025-05-12/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=8c4f8c044aa4ec6813cec1fed11326f67b0f2db3f20e4b441aba5656af7f0ae3 +dist/2025-05-12/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=4c2e5ae8c903577e963af32fdbb39de6180db52907c3f508064a87a21feb9390 +dist/2025-05-12/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=c1a12c15792f6b0de81a6e24317d7bea9af023a977ae0558ee3b4598539aa7cb +dist/2025-05-12/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=f789db5aebd9395daf198d5248323fee1eec27533f6d95d0f454339cbc997950 +dist/2025-05-12/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=0376d2f2ad8f82719eabb378de3404e066da7d603e27ae4e1620509ccd6eb5b6 +dist/2025-05-12/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=9312a8d530c6ca1e72ed35ef82700853e1fba8a1f39bcaad61277a86a974ab18 +dist/2025-05-12/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=8c7d99202e5468bbd6fcd818cb832376c00a7c4b09973e5d00b84aa4964b7ff6 +dist/2025-05-12/rust-std-beta-armv7a-none-eabi.tar.gz=c4fb94b25d21802136bc36289eea9b95e50b101f64de925a1e9d8ad8ee70aef6 +dist/2025-05-12/rust-std-beta-armv7a-none-eabi.tar.xz=6ac88ec457fd554268da3307d40664d2926174cf8e89eb173112c7248776e060 +dist/2025-05-12/rust-std-beta-armv7r-none-eabi.tar.gz=c436c2c58d224e1f9bea4703f8ab57cd3f427c60432cca50eb294dde65994002 +dist/2025-05-12/rust-std-beta-armv7r-none-eabi.tar.xz=220906e1eca686d6e4a76a80417f527d37b0659adbec940566570292496715f8 +dist/2025-05-12/rust-std-beta-armv7r-none-eabihf.tar.gz=eee1788aec77c48c76bc5ba807d42d4bbb7c8f3e9220ba1135764061a9ddf3d9 +dist/2025-05-12/rust-std-beta-armv7r-none-eabihf.tar.xz=136f3486bdd8a7e91d738a3f8c1c3b96b853aa054a76c4e83e427ea56d3eea0d +dist/2025-05-12/rust-std-beta-i586-unknown-linux-gnu.tar.gz=72e3c031fa55d131a206d5815899a48ff7bcb19c9ac4b3dbaeab38a3cc4a3630 +dist/2025-05-12/rust-std-beta-i586-unknown-linux-gnu.tar.xz=91ca56a1e5a07e1c147a8906d366985548bd961af2aa31dfba60938e457ddece +dist/2025-05-12/rust-std-beta-i586-unknown-linux-musl.tar.gz=839ece94670a9295148231c77573f5b2d8ec5fb9727ab6aa45b8f320201f40d5 +dist/2025-05-12/rust-std-beta-i586-unknown-linux-musl.tar.xz=27ec97a6e24184edf4a51de5500d5bb4d4833ad2b7bc771a4506589ce2190062 +dist/2025-05-12/rust-std-beta-i686-linux-android.tar.gz=d8f529a63a46bba2bd358e491d7fe0be10fee6dabf0075c40177402aeeb49721 +dist/2025-05-12/rust-std-beta-i686-linux-android.tar.xz=a061a703858aa0770d51c6c8bcdfca048efe96b561c460464b835b4ccfdca387 +dist/2025-05-12/rust-std-beta-i686-pc-windows-gnu.tar.gz=8b7eb90ad7edb050599dd477c520455ad7e02696426692a0a72094381e189285 +dist/2025-05-12/rust-std-beta-i686-pc-windows-gnu.tar.xz=f99e1d82a3cfaef05e6f088e766932a3860e7df60e1f392162746bb08eb72ddc +dist/2025-05-12/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=5d57965f2a6ffff01619e84acdc0f7d9b2afe3c361e5094eecccfa9893eaa501 +dist/2025-05-12/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=c1b218aac6370cef9564043c98f361a2938c6ebc7784cb49b361aad3a1bfb6f1 +dist/2025-05-12/rust-std-beta-i686-pc-windows-msvc.tar.gz=fdcd4b6f391338fc0f7b72d11fc8dad9df903fb4639b893b57e729de387a9cf9 +dist/2025-05-12/rust-std-beta-i686-pc-windows-msvc.tar.xz=c8faa9123c9df0d764cac59e10e94f1562ec7bc7a792f5c63f9a9decd48a3280 +dist/2025-05-12/rust-std-beta-i686-unknown-freebsd.tar.gz=e8882425b127d01afcf6269e820bb8c4b813619b6d10f0422fea17c87d5921bf +dist/2025-05-12/rust-std-beta-i686-unknown-freebsd.tar.xz=dabd3bb2560a7949f8984e1dcab35aa46f8e46b09e68c7f2ff32894370ed80b7 +dist/2025-05-12/rust-std-beta-i686-unknown-linux-gnu.tar.gz=dd296784ed2199b4c2d85053bce686e01cf867851b175b24781e7e8e6f6ef8bb +dist/2025-05-12/rust-std-beta-i686-unknown-linux-gnu.tar.xz=3bb9069b4456de27cc9fba5dd2b350e5e8215f0460ce9ee375f65856958e4a82 +dist/2025-05-12/rust-std-beta-i686-unknown-linux-musl.tar.gz=008ea77ae8d982461c65c25bfcc0c41642ca51a33007a4c8d1ede8612df8f20f +dist/2025-05-12/rust-std-beta-i686-unknown-linux-musl.tar.xz=fdfeb6df04afe1f4e414ad8292a7b75191c2507d020e69f402f97ee9ab3ccf90 +dist/2025-05-12/rust-std-beta-i686-unknown-uefi.tar.gz=dbca5a983d2eb2bd84aa7779fc54562bccf9043b31a7f52a3043f1e1e59695c8 +dist/2025-05-12/rust-std-beta-i686-unknown-uefi.tar.xz=c0d9abf38ba7b1847fc70b9dbe68f4c27d5a1adb9726dbbee77911f1d271b6aa +dist/2025-05-12/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=c923562d0a1d2830d41212ba140225b9c36087087dde6753e7a891383a095a10 +dist/2025-05-12/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=5ca633c2e218939983d77cbf5738ab7d5fc4aa89093a0d1fb701ab06ed7ecf51 +dist/2025-05-12/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=a38b4748085b3c06f2154376cdda41fcee2154f1fb409ac5137b63034cfe8cab +dist/2025-05-12/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=43601e0aecb02535ee46b0ddd076867248cd8654be302ae6580a81af33660faa +dist/2025-05-12/rust-std-beta-loongarch64-unknown-none.tar.gz=04dc49b516a638589d907f885aeafa19170683b023d0ee1bf5d78f0d91d0b94a +dist/2025-05-12/rust-std-beta-loongarch64-unknown-none.tar.xz=123f388b208842b3ee46a01ae8efab900c0b5b01b97eb896d26b12bb3aecdeaf +dist/2025-05-12/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=a57452e86c5b768f1feb7f903e4ef8e76518e625c09b5f555885e1d9aaf9b76f +dist/2025-05-12/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=e9d8b99bc4686e199f3aeda5cbfd99d49416a7ba104b494c18ae67a8d1133d9d +dist/2025-05-12/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=3a9f4744fc128be61877967586e6c163cd6ef4e017e04578cb9101c8a9a60cdc +dist/2025-05-12/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=0e693f7c27a34876728565152f7b6b407e1773a187742792ea2ac3f53d6c9839 +dist/2025-05-12/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=9d0f6d9cc2b7d1ceff5934a00c780337d2fa77cd9a81cbe9e041e5b18adb43ff +dist/2025-05-12/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=afcd5c9d2e67d6c514630443d9e50d37d36722712e9275e3eaf4f460f7eb779f +dist/2025-05-12/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=9c02e0eb75361a024d25863456c3906b845314481cd9173a6708104a21265e88 +dist/2025-05-12/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=5b0628ca22f762796c9215606314babc1237baea075c990e146ee9f9ba1ed834 +dist/2025-05-12/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=9e12bd3f2b61b8753aca3a1ed117cae0b4bae2267634a6e24afc0c642d998784 +dist/2025-05-12/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=70a6cf1d3e6767656657e5f76e8dd35049bd20a30517f85832c35847c9f63bf7 +dist/2025-05-12/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=e2feb3c8bf2390281c71f3b76f07a5a9700e454236bdd2c8f75403cb2247b252 +dist/2025-05-12/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=0b533328ff7dfffdfb11826811fa9474c36faebe909f176d60898477d5b9d23b +dist/2025-05-12/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=42b46c1d8ebec202131d08aa21fb6ead760a630199822b4fe88c94a5447f0491 +dist/2025-05-12/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=24bb2a24d41bfdb76dfb8817e99759dfd314ce52309d51b294db7a558114f936 +dist/2025-05-12/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=5d50c5a766344cacc6e7ebdabddfe720199fca74d1d4284a80ff5625150d7bcc +dist/2025-05-12/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=f6f6e68c0d495b2833566deacac8a6154a220fe1f92deacd031e6b649a63a04f +dist/2025-05-12/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=a80d128b4d0b3b5bb9316da1297b0c1cfee026eea3e9e23c546d62dda9cebd3d +dist/2025-05-12/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=03e27d02bf685f6eb1281fc48d417dcf9f934587fbc743d6e7aac6e0c3691d5c +dist/2025-05-12/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=67185b764c3423704af10318f44f0f310349191d62785bd8cb85ca2bac7f935a +dist/2025-05-12/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=8ef88ac6044c84815bbbcd2b5ce4128349633addf40bb8c439b9a0a07fc5e179 +dist/2025-05-12/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=2ab3bbb6de6a5281f8aa586e5fc15d575a34b17b4f44908347d7a776c924add2 +dist/2025-05-12/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=321b0167c9481ab88ff44bf920fa15bdb4e07c864a90b6777f3c8dfd0e5c5ec6 +dist/2025-05-12/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=bede2674247df8ff2153808f499ee1c1a7a909ff87600513ebc2998f43c7c1ea +dist/2025-05-12/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=b903c9ca2344fd1323695052f74b9562f6dd3cdde4872f935bcba6c0fb988dce +dist/2025-05-12/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=ad90fed7ed9137d04aa8c41d1c7e856dd8cc57a0f4b7836b22c9b1932a24a769 +dist/2025-05-12/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=78372e3e32174a2cfa12dcd426e36fe29ff76779d8815944e6f6c7be4a3c55fe +dist/2025-05-12/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=a781d0ee95ae3012e3d016ae1b029ca8507ff549a6b1e0a6f052bca6d4afbc7b +dist/2025-05-12/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=23b1cf7192f044a0f46ccedd654aa203dc0e9fad47c5ffc2a1e6717bf6598d69 +dist/2025-05-12/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=dfb15324b8047bd26a58a26d373af441182808203c06a3d4e595d79bca21b757 +dist/2025-05-12/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=432dfeb9231b67537dc5c77941ee26fd73404ea16dc1be4071b98c13680ddcaf +dist/2025-05-12/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=cff18fbbbe323c67779651dd6e3b94a76a573567720985d59a091c26a3c33110 +dist/2025-05-12/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=c5f25038ba5be3ffddb6966e89017de862a0d9f267a57eeaae81b3b2a44d5690 +dist/2025-05-12/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=104d762d5a45fea227880d2395068824f9202e5a7fbd30bea478bb1ee6899ee2 +dist/2025-05-12/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=de1f15d6cfafc275108c4584a294128962dabe54bf5a1f6e81da3508ea9e8a14 +dist/2025-05-12/rust-std-beta-sparcv9-sun-solaris.tar.gz=531562c65d558a993128054fcfb29f0d408a40318ecd5623b5b24636bd7b0a07 +dist/2025-05-12/rust-std-beta-sparcv9-sun-solaris.tar.xz=af866deae0c10ce2b11c0ebe37fdafef79285bc694eaba75213316ab125b198d +dist/2025-05-12/rust-std-beta-thumbv6m-none-eabi.tar.gz=ff623d437bda1c0b8cd8affd2a6bc165b8224a5467894aa54dee63b1b6939fc6 +dist/2025-05-12/rust-std-beta-thumbv6m-none-eabi.tar.xz=d8743e42057014ef2742cec5b93e34d5cde5a658d3ed9e7e738276387985122e +dist/2025-05-12/rust-std-beta-thumbv7em-none-eabi.tar.gz=0b097cef25dfe72f692cd6d9dd2df85a2fc5ea9db87b8c06b8f310c239c74624 +dist/2025-05-12/rust-std-beta-thumbv7em-none-eabi.tar.xz=e7fd61ad7660c7f8c62ae6dbbd238305d997fe7539dfffb8fd0df2205656b5a9 +dist/2025-05-12/rust-std-beta-thumbv7em-none-eabihf.tar.gz=d6b3c40bd84fe352c1a88dfbc3c0f9012dcc1d82b860ce68c1d21a8d452fa662 +dist/2025-05-12/rust-std-beta-thumbv7em-none-eabihf.tar.xz=b2be1305ae382359f81e0bff16341719b6ea7731ff833205dc3fd99e7e978fb9 +dist/2025-05-12/rust-std-beta-thumbv7m-none-eabi.tar.gz=5452dc0f152065e887178423e324bf3082885b922ac57ff22c156cf7c432e184 +dist/2025-05-12/rust-std-beta-thumbv7m-none-eabi.tar.xz=5331de420a79f521351a1ea3dd501cb00b21e1979eb23dfc871ce33abca68dd7 +dist/2025-05-12/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=0b2ffb463dca747f00cf063d8fb07971df80882d3890c34ba82fbf1b77655dd0 +dist/2025-05-12/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=2f7c2bde8ae4b911889dc24a8fbe2d1539685d46c71689e5e8362cf46c391019 +dist/2025-05-12/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=1d29e86aa77e277ce1598313d6851f2f077b023217f1712d59eb76305fc773fb +dist/2025-05-12/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=73dc975b217329d6ad44b8e8b3f72a3396597a207df7d7222d983a155ca05758 +dist/2025-05-12/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=c34c686a62afb45b9e57b3d487dcc1f66396bd7804a9c0d9696def0936a2ba1f +dist/2025-05-12/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=1527843f87588ee28aaedbb0590bb809c24cbde6a5264151ce5fe01baf70176d +dist/2025-05-12/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=8225d6b35a55d7937bbcb7f2e74ab8ec0f23fcd69a48c59391e9016d9863151f +dist/2025-05-12/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=8c59ed4aa0a62ff8999570b60a6b9c468ea52c45642ecfdc515d6f2722fd821b +dist/2025-05-12/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=6a5804a7dc199f696867e4612d1381910ff9a13b5516b2906e651451d8ec23e8 +dist/2025-05-12/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=11205a43892169cd0aad2764f5d7604a52d13292978e7e851cef2d8e65ae6fe5 +dist/2025-05-12/rust-std-beta-wasm32-unknown-emscripten.tar.gz=962e092960bd3074dc966c1928a4adfdc16d6d811060e719dc1a84061132566c +dist/2025-05-12/rust-std-beta-wasm32-unknown-emscripten.tar.xz=5d7fe7b3fe3b022c95d96e4027767b44a7e7980ca5c894839868919a0bb4b5bc +dist/2025-05-12/rust-std-beta-wasm32-unknown-unknown.tar.gz=38afbfef695bad377ac9d3a4d7d9037b500795c3a75f907bf60acd4cac2b4cd4 +dist/2025-05-12/rust-std-beta-wasm32-unknown-unknown.tar.xz=f5f670d35a843cda6f5213ae02a99c3c6d1e30f3ab651be0087bf8e4de0911f2 +dist/2025-05-12/rust-std-beta-wasm32-wasip1.tar.gz=e7faeb24fac65f565e0145166d67a30b02b26f0df20791f3bdc31169846a0e2b +dist/2025-05-12/rust-std-beta-wasm32-wasip1.tar.xz=0136f4434e8a0edbbe050899a17ae2b2825aeb4b98c4fb80f8eb25c9ea6623ab +dist/2025-05-12/rust-std-beta-wasm32-wasip1-threads.tar.gz=2ed823ff5c3704f91048300fa31624cddeea8086cfc654fa2fea4adff58fd901 +dist/2025-05-12/rust-std-beta-wasm32-wasip1-threads.tar.xz=6f156a460db83c271b43c37709ce5724fb8059c44b29e08c2b2da27c32c06e5c +dist/2025-05-12/rust-std-beta-wasm32-wasip2.tar.gz=ecd1ba1fec2e1e87b5f30b341e8228ca98545143adb8acd6ba53c7503f581e34 +dist/2025-05-12/rust-std-beta-wasm32-wasip2.tar.xz=593976f715c77796cecf6f7f2b79fbd4f49c10ded0349762e8312052497f1e28 +dist/2025-05-12/rust-std-beta-wasm32v1-none.tar.gz=396eb4c1e4cd930f045b092bbc8203315f494ea32c836d62e84f63ead124d886 +dist/2025-05-12/rust-std-beta-wasm32v1-none.tar.xz=9b827d1941a1d67a32a255342b476a19f57de06e53a9e6798bf00688b86eb2e0 +dist/2025-05-12/rust-std-beta-x86_64-apple-darwin.tar.gz=2796de44843d68141c6330f0e09fbabb5c3a8f34470d2948f1ed93b1b9dac088 +dist/2025-05-12/rust-std-beta-x86_64-apple-darwin.tar.xz=881e98599e5b2475e8c9f6b81e0ad6a51e8058cb2c7fc893ab57c19cdcc80804 +dist/2025-05-12/rust-std-beta-x86_64-apple-ios.tar.gz=8203faeaf21dc2c86b35d4362413c12d01de33da4524008c6261d3c87be9e51d +dist/2025-05-12/rust-std-beta-x86_64-apple-ios.tar.xz=13a59816008d3d4b0fb20680bfe2f1c2ae8ca7eed0bdf717817e03693724eb25 +dist/2025-05-12/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=5b906fe2d801c572696cd93564723338385eb574587769f79506cb3e6c87452d +dist/2025-05-12/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=1624a408800a895d8fe71bfc71876e52349c3508e9ddabd46d89d3274ede2dd7 +dist/2025-05-12/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=6722e76457289c6551f77fd462058862d7fb8597e1714cf66925b21e5af75c7b +dist/2025-05-12/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=8097f509383cab4e8e444ccbf7f5d91fe35bcd2cd2017ab78bcc692c9fd1ecf4 +dist/2025-05-12/rust-std-beta-x86_64-linux-android.tar.gz=eb3124653c908003185b36aa9829ea983f4b44e11a96da69c2585664a67bfeaf +dist/2025-05-12/rust-std-beta-x86_64-linux-android.tar.xz=4f29c6a0458ed5e37ee7a17643ff7854bd6ed029c46cdd0707019d01523a7a62 +dist/2025-05-12/rust-std-beta-x86_64-pc-solaris.tar.gz=319663b24b449df3f8063f64bd849969999a441b9376c86e6eea15cf3b872e5b +dist/2025-05-12/rust-std-beta-x86_64-pc-solaris.tar.xz=13280470aa4c84ed6ca200664ebf3a6aa084550a82c06505b3178caefe3072ef +dist/2025-05-12/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=d97cf2b52f013b5cfdd9c5a3885ea70accdf52e2f957e086018d88731c8c1964 +dist/2025-05-12/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=a2685ab1c204823b19809e47b00f2c48c5f2cc2faea05ac2df935732a7412441 +dist/2025-05-12/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=50c0f770a938123f704837bd3313dcb12842aba75b687282a9aca6c11b11ba8e +dist/2025-05-12/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=8f0d04c8d55f23235f8dec94c5d5035405afd513b082f00b257bbb86cd481240 +dist/2025-05-12/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=e633aebc178d4846a3d26f796405dde13115560c23bd2955c82afea8ab7c8d7b +dist/2025-05-12/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=8125d5bb9a9205ffab43d0dcd56402320643101169a49098a98ee6ae785c0ed3 +dist/2025-05-12/rust-std-beta-x86_64-unknown-freebsd.tar.gz=c089415c86c9f74a454b82955911e84c9138ad66757e4da689381a1bfbd4cee5 +dist/2025-05-12/rust-std-beta-x86_64-unknown-freebsd.tar.xz=a930b94bc005ce2b09b4d67abf47bfeafad8c7ab6ca5c15acc10e023818e7b25 +dist/2025-05-12/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=f74e77eb803d1ca244e1e97272578ec008e9c373af92887318d9281204a798fa +dist/2025-05-12/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=2fa583fcde17c1ab2f2d148af9467fa65f6bf6a0a1801e957fa15a79e6de4f78 +dist/2025-05-12/rust-std-beta-x86_64-unknown-illumos.tar.gz=20d16ce11adf468da51b30c0b55a46ce3bd030eea9f9fdb3f65f36aa442a3d71 +dist/2025-05-12/rust-std-beta-x86_64-unknown-illumos.tar.xz=dc1f2d3c1a0ae59cbfaa09b2d646645cb4fabb151edbf92975e4c8a0bfa54eba +dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=05b2e5ded14501cbdc86c0510faecbf873e30d2d70724013bbb176b6f4039b44 +dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=a18281579cb61ea26ae0062428f7a49e51c4a928102a5eba7ff96b0ca38490c0 +dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=a5285ae02217d64c7bbddaa3dd1f68c361f2849479a6d75edf1d551751886f7d +dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=4c41edc4f4cd1f24107b1b003a1713af3b456ff3e933781c5d4ef21a490df5e7 +dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=2d4c1666d456e810353f8b386d0d331812f84d9a17344953e5f4f4370bdccb0f +dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=287f51dbc6e4273208869140b9c2e0de2896c0cd40f7492396ec0bbb8989a82b +dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=e20af62d1900a5e10cf766ddcda9550176ab5f41111e09d57167e4e23e68d005 +dist/2025-05-12/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=7837f4880ce5d5251213d17867a6c61977504840678388fe245e0433086f409e +dist/2025-05-12/rust-std-beta-x86_64-unknown-netbsd.tar.gz=17d2a43bc24e4e49d54315c7eb0e4952c3118b278b0a564fd588ea4ce0e8d90e +dist/2025-05-12/rust-std-beta-x86_64-unknown-netbsd.tar.xz=ab34b5b10273c639805956665cd6543749cff2748c53980f80342facb9171b2d +dist/2025-05-12/rust-std-beta-x86_64-unknown-none.tar.gz=d8387a8478f6a937944d684f852dee18d584344ab84425d228489dee324c318c +dist/2025-05-12/rust-std-beta-x86_64-unknown-none.tar.xz=6ed8c2c72d547c7cc6b32a6080c346915de02a1ac02f032b6320fc7e3d45e330 +dist/2025-05-12/rust-std-beta-x86_64-unknown-redox.tar.gz=7f3a62578694121ef90fd08ab7a82a8fb27d86f164d7f73edb56a2e360198f41 +dist/2025-05-12/rust-std-beta-x86_64-unknown-redox.tar.xz=44f7ba0ca447050ad3eb7be0a0e41fee304dad2ce359c854848b7430c42b22d8 +dist/2025-05-12/rust-std-beta-x86_64-unknown-uefi.tar.gz=f78e6eca6ff517571480a6bbe20099d170f6a6b2ff0e64544c41dc77588ed890 +dist/2025-05-12/rust-std-beta-x86_64-unknown-uefi.tar.xz=d2a733aad6929be6135676307bd4576eb168e11192c24051e0be4a713b5733c5 +dist/2025-05-12/cargo-beta-aarch64-apple-darwin.tar.gz=43afffa0c5f7287e205a63871b555be144e900f8d8d67e4ed0654b50809b7338 +dist/2025-05-12/cargo-beta-aarch64-apple-darwin.tar.xz=705f051543ed8cc7011d7a866f345c3aa22c9d24f5325bffb9d9676e3c26142b +dist/2025-05-12/cargo-beta-aarch64-pc-windows-msvc.tar.gz=c0911e84ca85de5e8c9550e2be08dd85458ba31516e282044c9149bf8bb56fa1 +dist/2025-05-12/cargo-beta-aarch64-pc-windows-msvc.tar.xz=7335470fc1338b95edc81777eb0975cd5cf5cdcdcaefc7658f356ef3e0c54fda +dist/2025-05-12/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=99071e036041b47f78b71f1ff2ef5699b96a126ea84010ac031ee8d52d7c5873 +dist/2025-05-12/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=a9be2eeeed37905e83beb4265f4f45086675a0f5ff25db0e6bc0c5164257e1e1 +dist/2025-05-12/cargo-beta-aarch64-unknown-linux-musl.tar.gz=b38fa8d68c27b4989b1dc94caaf6bec833cc8e6d4464b859451d495b081c5b1b +dist/2025-05-12/cargo-beta-aarch64-unknown-linux-musl.tar.xz=95a839bd2f928afafbe1058cb185b95e0099ae15d5d3030a3493724f40300ae9 +dist/2025-05-12/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=34cef4599ece9c218c3841ccff9a627a69909eb733c19441c19de5b68841845b +dist/2025-05-12/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=cedfde42e95a0e86c3be841965c20f1c8bcebd20d88f38b2e694017a8afa745e +dist/2025-05-12/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=b8f1a0fca9b32362da6169b41fd58d53af6b02992ac5666cdeed03aa6150dd0c +dist/2025-05-12/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=8f5b040e4099a03418b72b5975419089e7fa15a947b04ce6dd18f450cc21f2b4 +dist/2025-05-12/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=2f526034ad1280d152861e700fad2aef95759eaf17780a3a00d71e8fc6d8520a +dist/2025-05-12/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=b6fdc7a08740d06e29aa678f4f9cb2dfb57fb863605fba1cce67d71ae1c1ace7 +dist/2025-05-12/cargo-beta-i686-pc-windows-gnu.tar.gz=f8b1e0227f5c1c2334cbcf53ebe5e94e01215ce21de2c5c9846e0ea7dce8e777 +dist/2025-05-12/cargo-beta-i686-pc-windows-gnu.tar.xz=149bc0d8cba9924db3b882795b6dd17f3d0a01bedfa75143dfdb7623cc7c4684 +dist/2025-05-12/cargo-beta-i686-pc-windows-msvc.tar.gz=b8462286bb1746bb789f580a14f1c5c37b108037633d9e8fbc5e2e6638e12a5c +dist/2025-05-12/cargo-beta-i686-pc-windows-msvc.tar.xz=f07104b3439e4cfcf5c96dbf6bf4428f677f45449ce2a5595551884ab0a6870a +dist/2025-05-12/cargo-beta-i686-unknown-linux-gnu.tar.gz=f68dece61dc087622d9e622944c4c13cdfb056eecdd93c9527c71637c73a708a +dist/2025-05-12/cargo-beta-i686-unknown-linux-gnu.tar.xz=3272d868a2bc44b80d0ab11d133f66ed7a40b75d00fbb7a341adbee083dfd8c0 +dist/2025-05-12/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=2fa7ef9b0f5247a650c1cf649e7f5514989a22b6c7927fa1df809e54466bc18f +dist/2025-05-12/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=3eddae3525cd8b446a4b31ea933cb859d335b0309900379868230d4a63979afe +dist/2025-05-12/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=88d56208387b4aa9707729f0b9337c32a0516dacc4c891b3c80140874dec6043 +dist/2025-05-12/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=8e4ceefb3d64560d989bf69f3d58cc07ab2e6a68d1f761ef92cb1826351834bb +dist/2025-05-12/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=ed5705fb6dba34981727e4af215d8875de2c39d41b1c3e8653a93cdc06873975 +dist/2025-05-12/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=be618816cd7706709fc13ab268249a74f7b905e7ae6abe6ca1fda336dd38baa2 +dist/2025-05-12/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=8b53a21201661914e3291ebc6912083e1cd86ed5d202d6940c2be15724371bc7 +dist/2025-05-12/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=546260a68ec029f228f280fc439e93dc1f64b3e597cf615ff3915548ab67b435 +dist/2025-05-12/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=343a00f2cc571ac779fd7647560b215650a01e877c9b15f95668cfc33c67ec77 +dist/2025-05-12/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=efc6a23ffb467e1459f3fe5932e8303d0ee550853ad13b3ace12c9aa6514f24c +dist/2025-05-12/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=5c4e53aca46fcfb7d669b74872130fa2b8bf05b09d14bdce34f0322030450e47 +dist/2025-05-12/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=c2e33c9522924cbfde1109f87d12d27225ceb23c7ad801d3a5559a72715ca402 +dist/2025-05-12/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=91d578317c8fa147c22e81728da411fd01c1fcb0bdf2e054948537476b8371e8 +dist/2025-05-12/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=83fc425704b7673943583e38c31a944695984ffabcdaa4ab79b43aea03cef48e +dist/2025-05-12/cargo-beta-s390x-unknown-linux-gnu.tar.gz=dac65289a906a32908ff0af9e9b829111295b49099fd5d9f90b2e454b4ecb422 +dist/2025-05-12/cargo-beta-s390x-unknown-linux-gnu.tar.xz=02a3972bfd62d4097da252fed278d741193f2c4face2e35ce8e84974e42cb1e1 +dist/2025-05-12/cargo-beta-x86_64-apple-darwin.tar.gz=148d0410ec2d3e540cfc27b6756e50d98b7ed214c2e5a702a9f2326e75ec249c +dist/2025-05-12/cargo-beta-x86_64-apple-darwin.tar.xz=65e993adfc14eb7a9c3946a3d1ce35f5aa9767ece65cd759669bb82deda0adc8 +dist/2025-05-12/cargo-beta-x86_64-pc-windows-gnu.tar.gz=a69c23bfe9ec73737c22d0b6ce308a4f19625aab2f1846bc223ec6974cdd9163 +dist/2025-05-12/cargo-beta-x86_64-pc-windows-gnu.tar.xz=56b33a8c9e0bcbbdb2c6be13d7b84d077a896b21d800a3c6da64aa2ef64ecada +dist/2025-05-12/cargo-beta-x86_64-pc-windows-msvc.tar.gz=cfd22dda3987642606f9e869264fa709d87b8ac5894547f809f60abce268ff76 +dist/2025-05-12/cargo-beta-x86_64-pc-windows-msvc.tar.xz=7075d67ef2dbf1e0d3889039d4db66042db538304c53cacd3e983eb9aa9d0275 +dist/2025-05-12/cargo-beta-x86_64-unknown-freebsd.tar.gz=419ce0f856113509f58f2fbccf9e5f864aa56c3c1a2c4029ecdb546464393214 +dist/2025-05-12/cargo-beta-x86_64-unknown-freebsd.tar.xz=d8f73cb808471883a5f6ee8db3dd5165fff5084ae744f4ffdca89fb545faaba8 +dist/2025-05-12/cargo-beta-x86_64-unknown-illumos.tar.gz=69e63b33c7f8d469232504c373a4e35df97016735be633a818023ea21de8f0be +dist/2025-05-12/cargo-beta-x86_64-unknown-illumos.tar.xz=aa86cbf46dd2e35c10bb5725c627dc40ecb33329a866c2b0c5c274728f384ed3 +dist/2025-05-12/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=f77e6d762e13eb95d6369a26971e4108de448eb23690554914f650fadd2898de +dist/2025-05-12/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=8e4b379bd88e8f18e5b6efe6058bad4ee60fb6c2e734ec165fee188f893f948d +dist/2025-05-12/cargo-beta-x86_64-unknown-linux-musl.tar.gz=a04b711f9a07eee991b1ab13ab56e0f9e2c2ba2a16186be6c0d04529ca68af59 +dist/2025-05-12/cargo-beta-x86_64-unknown-linux-musl.tar.xz=587b214ddf5b85697b78d8baa9164a4b81604b8dccc969a03b1bf06ae7c11240 +dist/2025-05-12/cargo-beta-x86_64-unknown-netbsd.tar.gz=81a468f1db3cbdaddf6a1785297457d4780fbec472d0bdfda64fb7a398782a78 +dist/2025-05-12/cargo-beta-x86_64-unknown-netbsd.tar.xz=32212f4273171d78e10170c4a863d6f9990e29e26fdf6857dd3d134eb803161d +dist/2025-05-12/clippy-beta-aarch64-apple-darwin.tar.gz=e5de69a84edb22eeaaeea2d94aafb07ed408508f68fc0989268e6dec8bae6a8e +dist/2025-05-12/clippy-beta-aarch64-apple-darwin.tar.xz=03a9ebedbf11cf151d19f46b9eeb3f8ea765ac779b55356b51db21e83195c610 +dist/2025-05-12/clippy-beta-aarch64-pc-windows-msvc.tar.gz=5a9e27ab31a382ba91f9621508cf28fb4f5d0f2521452369ea2441598d34b2bf +dist/2025-05-12/clippy-beta-aarch64-pc-windows-msvc.tar.xz=951c9f03a6fe0de1e94ab8f064cfc1b29b06606c38e891c2f9f1c550e9d94678 +dist/2025-05-12/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=1a241694ef544259a3c87bf271b1248ebb6fd32ac35b3ac16154e509b80c6e47 +dist/2025-05-12/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=679c8ed606c22490fb0a5a8503d898e61199e3cd17d9dd7a34c121781ca7306a +dist/2025-05-12/clippy-beta-aarch64-unknown-linux-musl.tar.gz=26ba8ec943e4f8cfa27afcde06fd34dcf546c3a5c7668acf703a9b962a1977c8 +dist/2025-05-12/clippy-beta-aarch64-unknown-linux-musl.tar.xz=051112fc6bd906c62cf14d2fa9c7f1505540a6aa86ee0b1889e11b1925274c23 +dist/2025-05-12/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=a44d29c794e49742417de03a955922ff3634ad45a5e6b5799c767f3feb2ae7ea +dist/2025-05-12/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=1650c464df6d87fcf3cea65722a515a1f1625d9e1ad6d27359455ecab849a592 +dist/2025-05-12/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=1c4f6c22361665705334faf35a0a7c17d55fb3fbd2622721e8cd7c76418cfc41 +dist/2025-05-12/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=f75400fc72fd358be80cbedefc53a9002fe6cc22637687e941835acb8c5eced0 +dist/2025-05-12/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=f1a2db6029e9d881dbfe7c6589873b323358d8317865824705c0cd358fa3ef49 +dist/2025-05-12/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=9cc0a2212a36bfb39379008b781304da67c74ab4ce0909da18f8cad50fcbbfd0 +dist/2025-05-12/clippy-beta-i686-pc-windows-gnu.tar.gz=06051eca41cbd1b570725847b4d8b79f29bd20ac06878ef5689167626fd4b137 +dist/2025-05-12/clippy-beta-i686-pc-windows-gnu.tar.xz=857d43d424e718e04714562132802aa5fc9028945a3c40c34508abd165a909c1 +dist/2025-05-12/clippy-beta-i686-pc-windows-msvc.tar.gz=58bf660a2f3ecf4671de4624b12b5a35f1e530d3c16f47eb7e114d1deb1891ad +dist/2025-05-12/clippy-beta-i686-pc-windows-msvc.tar.xz=5a36ec9ff4e35f1a49775e6657ea4f65543b47ebbb776fa1c60fa7898666de62 +dist/2025-05-12/clippy-beta-i686-unknown-linux-gnu.tar.gz=30df536f3cf6fbea2cf745ca8177f88831ed5b5e25d8fbdeee5f300fb35b97fe +dist/2025-05-12/clippy-beta-i686-unknown-linux-gnu.tar.xz=a491efcade35834adcbcfa8f08004b6a181a8d8fbe36f6a1bfd8e092443a82ad +dist/2025-05-12/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=a16579fb92973f609f0eb215d81e1125ad9dfa9e22d5d869236bbe0a7bf8050c +dist/2025-05-12/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=45ff10aa52e6162b015b1a927dd23ef7404fbbec554e5a1b655c085d59a378e7 +dist/2025-05-12/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=37e4ca4776fb278cac2ac05ece43ae569780503d0b122545eebc7a746dca69f3 +dist/2025-05-12/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=9c33b12b9c0a6d94b16a52066e3a1a8a2581db1c7549de002f0d6f4670021f0f +dist/2025-05-12/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=a7939ed010f6cef23e23e17c7ad905c6c0f4e549c85a8ae38d743232fe8de321 +dist/2025-05-12/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=21046d6fe31c0930e4611a18dcd48f5cacdcf3b64b5d035b4449b8b5af417254 +dist/2025-05-12/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=a03df872f97472d9a4310c8097042ef80ca859485fdb95ed9bcd853de3cbe9ec +dist/2025-05-12/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=925ff3b371f6c4ec871920c5e9fa5ab046f203c0af95f10f0996a750bd125582 +dist/2025-05-12/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=5f159a1913f6a5d10b5d5140093c9af4277d8a632db5cc116065a08fc0ff8bb6 +dist/2025-05-12/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=a2385ac96c42af4d77eb84ca70931e005aff1dc0e1ba272483ee82a837d96709 +dist/2025-05-12/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=9c289ed719cd18c8e5b883aeecc03e46f35b6b90d191b4fb0d0b4b6c7fc5073c +dist/2025-05-12/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=1a62cf477d5ad2ce4904a4438ab5756f75b894288a7449ae70c9f63d3b7badda +dist/2025-05-12/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=c1abab08e81632db27613f3ac7036d8ffdeaf92e345b345bf2c3535f4d9c16f0 +dist/2025-05-12/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=611252f8b142af9a86e511ae783f41cc97104d2e5ec5835c7d5006421ff6207c +dist/2025-05-12/clippy-beta-s390x-unknown-linux-gnu.tar.gz=d436be0f0f72db3c4933e8e34fcbb71e33b90ddcca58bc4b4360fe22e7a89404 +dist/2025-05-12/clippy-beta-s390x-unknown-linux-gnu.tar.xz=9f8086f13b6f53d44f03bc53fa3d750a9f4dc13b3612b10dba48958f4b61706d +dist/2025-05-12/clippy-beta-x86_64-apple-darwin.tar.gz=1b4a51c42bcc9e3241ceaceab3fb22bbf8060e9f4c2c55357603c1bf2fbf75f2 +dist/2025-05-12/clippy-beta-x86_64-apple-darwin.tar.xz=42556126bad0e0554dc5464396383c75a1fcb76257249c62ca4e40971129c458 +dist/2025-05-12/clippy-beta-x86_64-pc-windows-gnu.tar.gz=59a2a00a0c4e05cd0900fd119f43d4354b9f6b9df9dd9a9b44a1cfee9c674eb3 +dist/2025-05-12/clippy-beta-x86_64-pc-windows-gnu.tar.xz=35290a11740a2fc0c02d534375ca4ac0392de41f281383d7396179f670ddf309 +dist/2025-05-12/clippy-beta-x86_64-pc-windows-msvc.tar.gz=db01970a436b89d5fe3cb5eb65ea075f7dfd15b649958b35ea8d88835d8fe1c3 +dist/2025-05-12/clippy-beta-x86_64-pc-windows-msvc.tar.xz=9df8c8ed117b2e975bcb0520601c9b4e19e0440b14d9e510d09c9b54b872379f +dist/2025-05-12/clippy-beta-x86_64-unknown-freebsd.tar.gz=736361d62d33e969bda4cb98ea592ee7128e88c047f05b77cc025c982c27acb6 +dist/2025-05-12/clippy-beta-x86_64-unknown-freebsd.tar.xz=72f50e46dd2697c32b20ac2d0ae9ae2ea10485225dfd41dc9fa4e24d3b61a26e +dist/2025-05-12/clippy-beta-x86_64-unknown-illumos.tar.gz=4c856630844d01f655dc9855efb3685c2c30fcf199edfe665d9cf4230774ae0d +dist/2025-05-12/clippy-beta-x86_64-unknown-illumos.tar.xz=70bad50bffa518c4658e44dda7b6723558d68a545511228b97e18efc37a3ad0b +dist/2025-05-12/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=4c1e0fc35732f19effc50e67f637c57699ed7e846e4201db3897740c1e34a43a +dist/2025-05-12/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=fe53a5340c93485ac496453752a15222d323755cb20427b29b952b49f317a4bc +dist/2025-05-12/clippy-beta-x86_64-unknown-linux-musl.tar.gz=c56f80644373fbe9bb87310d26876a86325fccb1756716db30a5bf70293d328c +dist/2025-05-12/clippy-beta-x86_64-unknown-linux-musl.tar.xz=f4597f7ed6d0def07a32e952330cc964e49d42f84d65eead84192a29978c1a41 +dist/2025-05-12/clippy-beta-x86_64-unknown-netbsd.tar.gz=ecbc80189d470c1cc221360b94964fbd26d52b7583ea065cdd52795a48bf6271 +dist/2025-05-12/clippy-beta-x86_64-unknown-netbsd.tar.xz=f08204b9216fcb127934f2ceefeb7abe4338bb2ab79576a3a2e2077201f521e6 +dist/2025-05-12/rustfmt-nightly-aarch64-apple-darwin.tar.gz=269b22b568f60889c4841feff1c11d9c151d2655d134e966f7344f7affc6db57 +dist/2025-05-12/rustfmt-nightly-aarch64-apple-darwin.tar.xz=474f13aa57c73f4f9e3c63edb9a126ca845e63a376b7b8e35b5c6aa8fb0d9573 +dist/2025-05-12/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=9f24753d7abc9aa196a72ac54bb574f5eb375ecd5b2da42d0ed34bf0fb8eb947 +dist/2025-05-12/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=daae34864734810ff8ea563db7bf691f6c0fa56b9087fe285f7a3060247ef6e3 +dist/2025-05-12/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=c21f59bc03b8097f066be7bd3a7d0febe873f321583a4c7a9a0cdf5448d92ced +dist/2025-05-12/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=574fce0d0ff06850db47da008fdc6c6551f2cc459f63f69dcf8edae5e5ff51eb +dist/2025-05-12/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=6379365fb729e0f5d57873ad028f0c2641d60bc19ac5c905a2d1772b6730cb93 +dist/2025-05-12/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=a274c20436d31f74b4144f165a2b383297316f1f96b0d89b2b86bbf38e57be98 +dist/2025-05-12/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=03c3270a78c5d62517ec1b5c61414634ad58e5d4afb914f31bdc12ee0893ff2b +dist/2025-05-12/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=b309c052cdae48b23c2e89dcd7362af97f50181745191dee596ac176c2ade8a0 +dist/2025-05-12/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=300baf318827928f0c824e20ccc8966d3fe9e5b5f62a0d1aeba5feae1d183a11 +dist/2025-05-12/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=09b764e2038499d23b28b8cbdb01c9480f2100a01d864b7f03905bc78412fa00 +dist/2025-05-12/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=47c087899d4155750e71a261a0c93c9f736530d991dfa7e34c1a7bb7f2aedd8b +dist/2025-05-12/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=7e589aaaac2ab2c1211e5f5e1090b2ce1633f8b8682425aff01afd4dbd25e088 +dist/2025-05-12/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=0169fb75018dd644d7ed842472c04a5c82d46f3bfebe6d49931839809d1824b7 +dist/2025-05-12/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=96f3e288c8ccf073b1ea983ba382e341c8f6664135ad9aed7168bc05cf06ac4e +dist/2025-05-12/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=29b1f7a4b1454bb1c6af1e720e05bda846725a8e866266a147335920e99e66a9 +dist/2025-05-12/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=71a2f81ff29fd7e4c8dbdb2ce85bebf5e8ea5889cbb41f98fd3c3816918a6a3d +dist/2025-05-12/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=ae5458b4c0d58bc3e307c289aa44daf82218aaafc7911dadd4a09f4ca7cf6e12 +dist/2025-05-12/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=cf19b582a8336aa3f3959803cb24ad4499bc529bd58cd0766e668af5083de93b +dist/2025-05-12/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=474a34a9566402e313f5fcfaefe29188a6db1c0bd17caa20f186787267ac8e5d +dist/2025-05-12/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=c02f75eaa71f6c4d613a80dc7092d57cd4f6ef8a7de7511711fa818c0612da24 +dist/2025-05-12/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=95b47139ab6e9c16acee5ac78744c3e9ac917a5e811f45adfec4fddd45e98cf3 +dist/2025-05-12/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=fe13340e51d7d81629e03019d375a72874b80f19420c77ea083292a22a9be589 +dist/2025-05-12/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=a95ed14a5bc2f926c2ffb5dfe49813817638154edef7f29522661c57ec2dec09 +dist/2025-05-12/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=d9060c0aa08e0ade2fb54fb5381f0f69dc94166741200b2ed35a46b5d9885036 +dist/2025-05-12/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=060213e707c6b8911517e786b21515e169e062bbbf96302e012a442d260789e1 +dist/2025-05-12/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=f1d4dd54017937490f559a472893fb8a00236b46bf0f57ef9222ec3bbd191004 +dist/2025-05-12/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=38a57b7fac63608992995b3b983643ae213f6fa3d6a1021691334d84a5491542 +dist/2025-05-12/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=f26658ea60a6424707a027b1e36488f99490bce045978c3919c7320638f60d68 +dist/2025-05-12/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=07fbca58abf5fc57560e20fe7aede77137dd3f2f4cf2a6da11a80eaf6672bed3 +dist/2025-05-12/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=f56f7bb1091fbb1a8d1583beb586194e5dd526f7a0268b4ebe997e0ce7c9d9cb +dist/2025-05-12/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=3ec40438a95a086a1c4c522c6ae018393469f605b03d392562fca4926bdf0631 +dist/2025-05-12/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=d7c342edbefe3fc22631961c2aca53cb808bc8f1df17673ec5cafcc56eaf0475 +dist/2025-05-12/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=4c1a2aa84e8e1c67a111b9a622b2c6ed96eebcec9752ccc5e940460ce048f22e +dist/2025-05-12/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=e26a0359223ca793d34ac9e4e5731923c4531dcdbf32aa8789bc9d1bda17013f +dist/2025-05-12/rustfmt-nightly-x86_64-apple-darwin.tar.gz=dbf20af35cbe11baab7ead72ec254717642b01fdf30140589510413058af3e49 +dist/2025-05-12/rustfmt-nightly-x86_64-apple-darwin.tar.xz=7beb25f2df0877ee74231abe03e74a09c6e41a356d0cea27956b2091382dbf47 +dist/2025-05-12/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=527d2d68bfd519d49936fd8941a04d787df1edf8c2c3ecc39103d55d1683a970 +dist/2025-05-12/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=8744bef9d00d6f7397ef2b1b36971ad7af6389e93b5286ca60feb6137c4f6b10 +dist/2025-05-12/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=50f8f2db4f410e60a6cd4ad03a762ea636076d85af05d511f40d2d2ea98bc833 +dist/2025-05-12/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=183f8742c505ab1d0488ca915509c1b0558166c6d19d8dc864d0a1686d66a791 +dist/2025-05-12/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=f042a8c4ef96911b2cc6cc2228ff832229196b4ab5b1b04b05b22b5b9a90649d +dist/2025-05-12/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=9b93acd9cb8c8e062f3e47f5415adb8eae67479318b6201bf66119d467b81e11 +dist/2025-05-12/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=fe9073a3bbd3b6513ba0fc38005b8ab1d44052e1bb10c1976bc98a62f8df5934 +dist/2025-05-12/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=4c99f67e351758fe0db0bc7cdfe177018083b9ada2feeee952180b420e2c6ac9 +dist/2025-05-12/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=c5a5702c66ae7de6b7a10d1c8c39af6c973c6eeebbc1fdba3b427c1ec9588756 +dist/2025-05-12/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=8da51f6150fa5c53dead4c3db2c2d7493cc46b36d64b978e605a9d5755dfd779 +dist/2025-05-12/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=3d77d2579fcb53a9bb6d942d44353f7b818b10504b64b790ecc3630d8b17a565 +dist/2025-05-12/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=7e75748bcb8b25bebeb1b5aeb2afc2fc1c48f38ccff9c624cd002a8e051424b7 +dist/2025-05-12/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=9c05c902b0db8fd8f8b44d83a95bc8722bb714d333d2a61a2e1ef140092b6d83 +dist/2025-05-12/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=d614cb69e1484f3653bc148280e7518640ec830ab8f02ddf512206ac265d6746 +dist/2025-05-12/rustc-nightly-aarch64-apple-darwin.tar.gz=ac2c35cd19b85e6356bcdb987031314afbb7e41f26418ddb0d943fc3482245c6 +dist/2025-05-12/rustc-nightly-aarch64-apple-darwin.tar.xz=a3c53f15d7b6f7c7e5f1e55c107663ef102cdb123394bcbe8a8c9c32a7e715f5 +dist/2025-05-12/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=29e3bae16967111ce72d00b931d32410ab526617bf1c88bbf90e4d32825ea7dd +dist/2025-05-12/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=116103ab4251b366644239f8ef8d7129ae3d9588d768b8e66671497b1fa36c95 +dist/2025-05-12/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=911acda80c362dd7690e5a4596e166b8ea49425f6dbbfd78ef697e69dc826c85 +dist/2025-05-12/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=9cabea351ef05117d8cdfae0df334c98b12a99c4191d3e4f382c336c326520dc +dist/2025-05-12/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=81c9ed04939e8d363e060ef2808bee8dbd63435b111f37325bc8fd2891726560 +dist/2025-05-12/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=a44b2f887aeafd5ff57ff67d8c4eeaa94cb4edd2f7d5912618ee186a4d609c73 +dist/2025-05-12/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=7a4047a85297d3012c00377241f3daa50b34ddc54d68d67787d76eb45f5db616 +dist/2025-05-12/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=09acd09fbfa3c43738c43c8c423d3fce6dc4451ca4ee8650ab3392279cfc288a +dist/2025-05-12/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=88ffa28a612cfb661a731dd4feeb6d6fae88d7236469ded88ee74a06a1576a8f +dist/2025-05-12/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=7c5747fb16062a786ffba5d00e1bc0e3c81ccf6154f09e21a6aa5b87c2fc9594 +dist/2025-05-12/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=c1bd5074d4664f0ac8019151aea13e051cf2d89b8bd8fa77b9ed3831a1b7c217 +dist/2025-05-12/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=20fa9e5531e4be0e54af97c8d033722c68d54ef984be3619ad84be6b579d0c73 +dist/2025-05-12/rustc-nightly-i686-pc-windows-gnu.tar.gz=fe7511b5bf7830efeec083d3414e389286ec117b53db0501d5c314eba24e3bdd +dist/2025-05-12/rustc-nightly-i686-pc-windows-gnu.tar.xz=95677d845a5c7677b951300f17d810301397df022145f16674a58ebb1cd52a56 +dist/2025-05-12/rustc-nightly-i686-pc-windows-msvc.tar.gz=a8f1e4852ffab09aeab1ccc09fff930444871fd3b490e68a1f9ae504c0dce6ed +dist/2025-05-12/rustc-nightly-i686-pc-windows-msvc.tar.xz=11fd3093a95e379d6472f063bfdccf6f3cf6c44956d68d121adcd1c927812eba +dist/2025-05-12/rustc-nightly-i686-unknown-linux-gnu.tar.gz=d95876f9a84ebcc97033c81dd07fe8852f0f472db94c074f5029458fec512d2e +dist/2025-05-12/rustc-nightly-i686-unknown-linux-gnu.tar.xz=766182f4d375138f4871abba6a8b50c3ca342edb7842b6d4bf7162e466cb32fe +dist/2025-05-12/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=58e41cb37fb5b974a78e7891c7aca2786bdf8153ac9cd134b713fc73771017b3 +dist/2025-05-12/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=ec6990871579f86c0587a6f7262bb53dd7de3a79a39ca55b994475ad96f20f4f +dist/2025-05-12/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=39b7b026e95bdee7eba78804d2f8f3703a141ff37c24ac636deb755fc669f081 +dist/2025-05-12/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=0b066061a1a55836b3b81667c0c35d864055578370f00365db7226fc41f0f11c +dist/2025-05-12/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=040b0718e4f460bb6136628ce24dca390608671b609d8e222e4ccbfedff43d6e +dist/2025-05-12/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=f9206ff2fad2acaab1b3a30e1d7a634384533329f71ceed5ef2fce0bd288bd43 +dist/2025-05-12/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=9c6c12d9d5486c4d26d1f7d9a61625a20e3e7703af79195ec4cb7e7e22358f4e +dist/2025-05-12/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=2cace3cec2973aa8f93f1d5bbe8cdcb36134fc2313b0131c51d2d4885bb18492 +dist/2025-05-12/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=60adf24efc4a8207709ccb39bf45ff5fb08c4a853de816c239a2aec795c22e46 +dist/2025-05-12/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=038e9220451a497885e7886a293986b37b83979a4a6f70b112d42245f9e4a924 +dist/2025-05-12/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=66d700e4a734f1a1a4f2c5d9125fee2c20e400b85a4a72ec4d6963f7d438a591 +dist/2025-05-12/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=1198a73d12b6f556a5016a2181e1c95adf929f24df1be5a17b1ff8cf6635656f +dist/2025-05-12/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=e1b12d459eeed0496a93db5ca6962bd15bd307a400e8bb870623d20479d75aa0 +dist/2025-05-12/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=2339af50b563056c4ad58cff24b1d59198e71e06c85f1860461e9384a0aeac0a +dist/2025-05-12/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=4977999e15a893215a7f86ad55e195249f63c416b7a0bee3423950575a952d1e +dist/2025-05-12/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=5745e2dd22c39abd35b19117b5514ba383058c057265b3003cda3da4aadfa18b +dist/2025-05-12/rustc-nightly-x86_64-apple-darwin.tar.gz=008b5b604e3fb66026eca67f29ed65262f85a2e305286a5ad11642edc8eaee2a +dist/2025-05-12/rustc-nightly-x86_64-apple-darwin.tar.xz=b2c071998e209e6b4989eae799938268dee9d8ada531956d41147e747128f328 +dist/2025-05-12/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=8791712c5513a077d2936dd26c7157b12fd8b4bfc93180f97273eb534461837f +dist/2025-05-12/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=02fd232fa95660aa19665089a191fe350d0dfc44fcee4436be28fad82324fd00 +dist/2025-05-12/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=9f7d67cadca7abf25c5445a9f7c911a3e0a2db2e52c088cc6833e40b52bef0de +dist/2025-05-12/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=ef2853ac4f2a5c6932f16768fb1df277b9edb8d91615869b8cfa574d6bda026a +dist/2025-05-12/rustc-nightly-x86_64-unknown-freebsd.tar.gz=117efae53fc69e481498c1f268bbb12e382f479dc6859ad04fdfc4a84659d677 +dist/2025-05-12/rustc-nightly-x86_64-unknown-freebsd.tar.xz=14b67230c06ed6ec7597e31c6b7385782ab6a1f6bc723c5d2f171defa02c669d +dist/2025-05-12/rustc-nightly-x86_64-unknown-illumos.tar.gz=881a6c5ff0222eaca1fa278fb517963b30f51714c3724956bb2d29c142af0add +dist/2025-05-12/rustc-nightly-x86_64-unknown-illumos.tar.xz=3e708bcafdf8da1ceb92ad0e27407ea210144d91e30ba2486bd6758085153caf +dist/2025-05-12/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=b264a719d90f6842e3cbc8dc7d74ec356328f0a94cca279795ada5f4b22c54ed +dist/2025-05-12/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=fbe22ac8c9995feac7b13f92b8d4c16fc1cdfb4a15c06e127420762db0198443 +dist/2025-05-12/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=48cf6d33fdba4e38dcc19710efd24eb863fe13bbca634e0ca02fc1647255bd6a +dist/2025-05-12/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=7767dd1b6baf7065dfc74b4e9ce4c200616294ecd664243c6fe756522fb4a328 +dist/2025-05-12/rustc-nightly-x86_64-unknown-netbsd.tar.gz=bde7b39870fbce418257278ae56159af3f80f1688efd01d6d52b16127fd0b64a +dist/2025-05-12/rustc-nightly-x86_64-unknown-netbsd.tar.xz=6018c06dda8f5a0ff5ef7754bf2e8692b2dfd48be525d896261aea27d682f4e5 diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index cd5ca74f8ade5..741d7e3fa16c1 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -97,7 +97,6 @@ static TARGETS: &[&str] = &[ "bpfeb-unknown-none", "bpfel-unknown-none", "i386-apple-ios", - "i586-pc-windows-msvc", "i586-unknown-linux-gnu", "i586-unknown-linux-musl", "i586-unknown-redox", @@ -387,7 +386,6 @@ impl Builder { // NOTE: this profile is effectively deprecated; do not add new components to it. let mut complete = default; complete.extend([ - Rls, RustAnalyzer, RustSrc, LlvmTools, @@ -476,7 +474,6 @@ impl Builder { // but might be marked as unavailable if they weren't built. PkgType::Clippy | PkgType::Miri - | PkgType::Rls | PkgType::RustAnalyzer | PkgType::Rustfmt | PkgType::LlvmTools diff --git a/src/tools/build-manifest/src/versions.rs b/src/tools/build-manifest/src/versions.rs index 495cab582eb07..6ef8a0e83de3f 100644 --- a/src/tools/build-manifest/src/versions.rs +++ b/src/tools/build-manifest/src/versions.rs @@ -51,7 +51,6 @@ pkg_type! { Cargo = "cargo", HtmlDocs = "rust-docs", RustAnalysis = "rust-analysis", - Rls = "rls"; preview = true, RustAnalyzer = "rust-analyzer"; preview = true, Clippy = "clippy"; preview = true, Rustfmt = "rustfmt"; preview = true, @@ -77,7 +76,6 @@ impl PkgType { fn should_use_rust_version(&self) -> bool { match self { PkgType::Cargo => false, - PkgType::Rls => false, PkgType::RustAnalyzer => false, PkgType::Clippy => false, PkgType::Rustfmt => false, @@ -118,7 +116,6 @@ impl PkgType { HtmlDocs => HOSTS, JsonDocs => HOSTS, RustSrc => &["*"], - Rls => HOSTS, RustAnalyzer => HOSTS, Clippy => HOSTS, Miri => HOSTS, diff --git a/src/tools/bump-stage0/src/main.rs b/src/tools/bump-stage0/src/main.rs index f51072718a349..680437cce4faa 100644 --- a/src/tools/bump-stage0/src/main.rs +++ b/src/tools/bump-stage0/src/main.rs @@ -61,36 +61,35 @@ impl Tool { artifacts_server, artifacts_with_llvm_assertions_server, git_merge_commit_email, - git_repository, nightly_branch, } = &self.config; - file_content.push_str(&format!("dist_server={}", dist_server)); - file_content.push_str(&format!("\nartifacts_server={}", artifacts_server)); + file_content.push_str(&format!("dist_server={}\n", dist_server)); + file_content.push_str(&format!("artifacts_server={}\n", artifacts_server)); file_content.push_str(&format!( - "\nartifacts_with_llvm_assertions_server={}", + "artifacts_with_llvm_assertions_server={}\n", artifacts_with_llvm_assertions_server )); - file_content.push_str(&format!("\ngit_merge_commit_email={}", git_merge_commit_email)); - file_content.push_str(&format!("\ngit_repository={}", git_repository)); - file_content.push_str(&format!("\nnightly_branch={}", nightly_branch)); + file_content.push_str(&format!("git_merge_commit_email={}\n", git_merge_commit_email)); + file_content.push_str(&format!("nightly_branch={}\n", nightly_branch)); - file_content.push_str("\n\n"); + file_content.push_str("\n"); file_content.push_str(COMMENTS); + file_content.push_str("\n"); let compiler = self.detect_compiler()?; - file_content.push_str(&format!("\ncompiler_date={}", compiler.date)); - file_content.push_str(&format!("\ncompiler_version={}", compiler.version)); + file_content.push_str(&format!("compiler_date={}\n", compiler.date)); + file_content.push_str(&format!("compiler_version={}\n", compiler.version)); if let Some(rustfmt) = self.detect_rustfmt()? { - file_content.push_str(&format!("\nrustfmt_date={}", rustfmt.date)); - file_content.push_str(&format!("\nrustfmt_version={}", rustfmt.version)); + file_content.push_str(&format!("rustfmt_date={}\n", rustfmt.date)); + file_content.push_str(&format!("rustfmt_version={}\n", rustfmt.version)); } file_content.push_str("\n"); for (key, value) in self.checksums { - file_content.push_str(&format!("\n{}={}", key, value)); + file_content.push_str(&format!("{}={}\n", key, value)); } std::fs::write(PATH, file_content)?; diff --git a/src/tools/cargo b/src/tools/cargo index 2622e844bc1e2..47c911e9e6f64 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 2622e844bc1e2e6123e54e94e4706f7b6195ce3d +Subproject commit 47c911e9e6f6461f90ce19142031fe16876a3b95 diff --git a/src/tools/clippy/.github/workflows/clippy_changelog.yml b/src/tools/clippy/.github/workflows/clippy_changelog.yml index a2657bfea490d..4d84d6b6dae4d 100644 --- a/src/tools/clippy/.github/workflows/clippy_changelog.yml +++ b/src/tools/clippy/.github/workflows/clippy_changelog.yml @@ -15,27 +15,18 @@ jobs: changelog: runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: # Run - name: Check Changelog if: ${{ github.event_name == 'pull_request' }} run: | - body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR_NUMBER" | \ - python -c "import sys, json; print(json.load(sys.stdin)['body'])") - output=$(awk '/^changelog:\s*\S/ && !/changelog: \[.*\]: your change/' <<< "$body" | sed "s/changelog:\s*//g") - if [ -z "$output" ]; then - echo "ERROR: pull request message must contain 'changelog: ...' with your changelog. Please add it." + if [[ -z $(grep -oP 'changelog: *\K\S+' <<< "$PR_BODY") ]]; then + echo "::error::Pull request message must contain 'changelog: ...' with your changelog. Please add it." exit 1 - else - echo "changelog: $output" fi env: - PYTHONIOENCODING: 'utf-8' - PR_NUMBER: '${{ github.event.number }}' + PR_BODY: ${{ github.event.pull_request.body }}) + # We need to have the "conclusion" job also on PR CI, to make it possible # to add PRs to a merge queue. diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index c337a96bdac52..07d5a08304e87 100644 --- a/src/tools/clippy/.github/workflows/clippy_mq.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -27,6 +27,8 @@ jobs: host: x86_64-pc-windows-msvc - os: macos-13 host: x86_64-apple-darwin + - os: macos-latest + host: aarch64-apple-darwin runs-on: ${{ matrix.os }} @@ -64,7 +66,7 @@ jobs: run: cargo test --features internal -- --skip dogfood - name: Test clippy_lints - run: cargo test --features internal + run: cargo test working-directory: clippy_lints - name: Test clippy_utils diff --git a/src/tools/clippy/.github/workflows/clippy_pr.yml b/src/tools/clippy/.github/workflows/clippy_pr.yml index 80523d91f4fc8..880ebd6e5d5c7 100644 --- a/src/tools/clippy/.github/workflows/clippy_pr.yml +++ b/src/tools/clippy/.github/workflows/clippy_pr.yml @@ -42,7 +42,7 @@ jobs: run: cargo test --features internal - name: Test clippy_lints - run: cargo test --features internal + run: cargo test working-directory: clippy_lints - name: Test clippy_utils diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml index b42f3e7712f10..ede19c11257ee 100644 --- a/src/tools/clippy/.github/workflows/deploy.yml +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -8,6 +8,10 @@ on: tags: - rust-1.** +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + env: TARGET_BRANCH: 'gh-pages' SHA: '${{ github.sha }}' diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index d487c7d949857..70c805903d36e 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -66,7 +66,7 @@ jobs: - name: Run lintcheck if: steps.cache-json.outputs.cache-hit != 'true' - run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml + run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload base JSON uses: actions/upload-artifact@v4 @@ -97,7 +97,7 @@ jobs: run: cargo build --manifest-path=lintcheck/Cargo.toml - name: Run lintcheck - run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml + run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload head JSON uses: actions/upload-artifact@v4 diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index 13902f78b541d..7e7e26818c094 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -37,6 +37,7 @@ jobs: - name: Linkcheck book run: | rustup toolchain install nightly --component rust-docs + rustup override set nightly curl https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh -o linkcheck.sh sh linkcheck.sh clippy --path ./book diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 51441ab9fc0d8..3a98217f625a0 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,166 @@ document. ## Unreleased / Beta / In Rust Nightly -[609cd310...master](https://github.com/rust-lang/rust-clippy/compare/609cd310...master) +[1e5237f4...master](https://github.com/rust-lang/rust-clippy/compare/1e5237f4...master) + +## Rust 1.87 + +Current stable, released 2025-05-15 + +[View all 127 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-02-06T14%3A54%3A28Z..2025-03-20T20%3A07%3A53Z+base%3Amaster) + +### New Lints + +* Added [`doc_comment_double_space_linebreaks`] to `pedantic` [#12876](https://github.com/rust-lang/rust-clippy/pull/12876) +* Added [`manual_midpoint`] to `pedantic` [#13851](https://github.com/rust-lang/rust-clippy/pull/13851) +* Added [`io_other_error`] to `style` [#14022](https://github.com/rust-lang/rust-clippy/pull/14022) +* Added [`owned_cow`] to `pedantic` [#13948](https://github.com/rust-lang/rust-clippy/pull/13948) +* Added [`manual_contains`] to `perf` [#13817](https://github.com/rust-lang/rust-clippy/pull/13817) +* Added [`unnecessary_debug_formatting`] to `pedantic` [#13893](https://github.com/rust-lang/rust-clippy/pull/13893) +* Added [`elidable_lifetime_names`] to `pedantic` [#13960](https://github.com/rust-lang/rust-clippy/pull/13960) +* Added [`mem_replace_option_with_some`] to `style` [#14197](https://github.com/rust-lang/rust-clippy/pull/14197) +* Added [`unbuffered_bytes`] to `perf` [#14089](https://github.com/rust-lang/rust-clippy/pull/14089) +* Added [`single_option_map`] to `nursery` [#14033](https://github.com/rust-lang/rust-clippy/pull/14033) + +### Moves and Deprecations + +* Moved [`comparison_chain`] to `pedantic` (from `style`) + [#14219](https://github.com/rust-lang/rust-clippy/pull/14219) +* Moved [`manual_ok_or`] to `style` (from `pedantic`) + [#14027](https://github.com/rust-lang/rust-clippy/pull/14027) +* Deprecated [`option_map_or_err_ok`] in favor of [`manual_ok_or`] + [#14027](https://github.com/rust-lang/rust-clippy/pull/14027) + +### Enhancements + +* Add `allow_expect_in_consts` and `allow_unwrap_in_consts` configuration options to [`unwrap_used`], [`expect_used`] + [#14200](https://github.com/rust-lang/rust-clippy/pull/14200) +* Add `check-incompatible-msrv-in-tests` configuration option to [`incompatible_msrv`] + [#14279](https://github.com/rust-lang/rust-clippy/pull/14279) +* [`len_zero`] now also triggers if deref target implements `is_empty()` + [#13871](https://github.com/rust-lang/rust-clippy/pull/13871) +* [`ptr_eq`] now handles more cases, including `!=` in addition to `==` + [#14339](https://github.com/rust-lang/rust-clippy/pull/14339) +* [`struct_field_names`] now also checks private fields of public structs + [#14076](https://github.com/rust-lang/rust-clippy/pull/14076) +* [`needless_pass_by_value`] suggests using a reference on the innermost `Option` content + [#14392](https://github.com/rust-lang/rust-clippy/pull/14392) +* [`obfuscated_if_else`] now supports `then().unwrap_or_else()` and `then_some().unwrap_or_else()` + [#14165](https://github.com/rust-lang/rust-clippy/pull/14165) +* Format macros: all format-handling lints now validate `todo!` and `unimplemented!` macros + [#14266](https://github.com/rust-lang/rust-clippy/pull/14266) +* [`disallowed_methods`] now supports replacements + [#13669](https://github.com/rust-lang/rust-clippy/pull/13669) +* Added MSRV checks for several lints: + * [`question_mark`] [#14436](https://github.com/rust-lang/rust-clippy/pull/14436) + * [`repeat_vec_with_capacity`] [#14126](https://github.com/rust-lang/rust-clippy/pull/14126) + * [`manual_flatten`] [#14086](https://github.com/rust-lang/rust-clippy/pull/14086) + * [`lines_filter_map_ok`] [#14130](https://github.com/rust-lang/rust-clippy/pull/14130) + +### False Positive Fixes + +* [`missing_const_for_fn`] no longer triggers on unstable const traits [#14294](https://github.com/rust-lang/rust-clippy/pull/14294) +* [`unnecessary_to_owned`] now avoids suggesting to call `iter()` on a temporary object [#14243](https://github.com/rust-lang/rust-clippy/pull/14243) +* [`unnecessary_debug_formatting`] no longer triggers in tests [#14347](https://github.com/rust-lang/rust-clippy/pull/14347) +* [`option_if_let_else`] now handles cases when value is partially moved [#14209](https://github.com/rust-lang/rust-clippy/pull/14209) +* [`blocks_in_conditions`] no longer triggers when the condition contains a `return` [#14338](https://github.com/rust-lang/rust-clippy/pull/14338) +* [`undocumented_unsafe_blocks`] no longer triggers on trait/impl items [#13888](https://github.com/rust-lang/rust-clippy/pull/13888) +* [`manual_slice_fill`] no longer triggers due to missing index checks [#14193](https://github.com/rust-lang/rust-clippy/pull/14193) +* [`useless_asref`] no longer suggests using `.clone()` if the target type doesn't implement `Clone` [#14174](https://github.com/rust-lang/rust-clippy/pull/14174) +* [`unnecessary_safety_comment`] no longer triggers on desugared assign [#14371](https://github.com/rust-lang/rust-clippy/pull/14371) +* [`unnecessary_map_or`] no longer consumes the comparison value if it does not implement `Copy` [#14207](https://github.com/rust-lang/rust-clippy/pull/14207) +* [`let_and_return`] no longer triggers involving short-lived block temporary variables [#14180](https://github.com/rust-lang/rust-clippy/pull/14180) +* [`manual_async_fn`] no longer emits suggestions inside macros [#14142](https://github.com/rust-lang/rust-clippy/pull/14142) +* [`use_self`] skips analysis inside macro expansions of a `impl Self` block [#13128](https://github.com/rust-lang/rust-clippy/pull/13128) +* [`double_ended_iterator_last`] no longer triggers on non-reference immutable receiver [#14140](https://github.com/rust-lang/rust-clippy/pull/14140) + +### ICE Fixes + +* [`macro_use_imports`] Fix ICE when checking attributes + [#14317](https://github.com/rust-lang/rust-clippy/pull/14317) +* [`doc_nested_refdefs`] Fix ICE by avoiding invalid ranges + [#14308](https://github.com/rust-lang/rust-clippy/pull/14308) +* [`just_underscores_and_digits`] Fix ICE in error recovery scenario + [#14168](https://github.com/rust-lang/rust-clippy/pull/14168) +* [`declare_interior_mutable_const`], [`borrow_interior_mutable_const`] Fix ICE by properly resolving `::AssocT` projections + [#14125](https://github.com/rust-lang/rust-clippy/pull/14125) + +### Documentation Improvements + +* [`struct_excessive_bools`] Documentation improved with rationale + [#14351](https://github.com/rust-lang/rust-clippy/pull/14351) + +### Others + +* Use edition=2021 in `rustc_tools_util` + [#14211](https://github.com/rust-lang/rust-clippy/pull/14211) +* Fix rustc_tools_util's `version.host_compiler` release channel, expose the rustc version, and add tests + [#14123](https://github.com/rust-lang/rust-clippy/pull/14123) +* Make UI test annotations mandatory + [#11421](https://github.com/rust-lang/rust-clippy/pull/11421) + [#14388](https://github.com/rust-lang/rust-clippy/pull/14388) + [#14393](https://github.com/rust-lang/rust-clippy/pull/14393) + +## Rust 1.86 + +Current stable, released 2025-04-03 + +[View all 108 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-12-27T15%3A11%3A38Z..2025-02-06T13%3A57%3A58Z+base%3Amaster) + +### New Lints + +* Added [`unneeded_struct_pattern`] to `style` [#13465](https://github.com/rust-lang/rust-clippy/pull/13465) +* Added [`doc_overindented_list_items`] to `style` [#13711](https://github.com/rust-lang/rust-clippy/pull/13711) +* Added [`manual_ok_err`] to `complexity` [#13740](https://github.com/rust-lang/rust-clippy/pull/13740) +* Added [`non_std_lazy_statics`] to `pedantic` [#13770](https://github.com/rust-lang/rust-clippy/pull/13770) +* Added [`manual_repeat_n`] to `style` [#13858](https://github.com/rust-lang/rust-clippy/pull/13858) +* Added [`manual_option_as_slice`] to `complexity` [#13901](https://github.com/rust-lang/rust-clippy/pull/13901) +* Added [`double_ended_iterator_last`] to `perf` [#13922](https://github.com/rust-lang/rust-clippy/pull/13922) +* Added [`useless_nonzero_new_unchecked`] to `complexity` [#13993](https://github.com/rust-lang/rust-clippy/pull/13993) +* Added [`sliced_string_as_bytes`] to `perf` [#14002](https://github.com/rust-lang/rust-clippy/pull/14002) +* Added [`unnecessary_semicolon`] to `pedantic` [#14032](https://github.com/rust-lang/rust-clippy/pull/14032) +* Added [`return_and_then`] to `restriction` [#14051](https://github.com/rust-lang/rust-clippy/pull/14051) +* Added [`manual_slice_fill`] to `style` [#14082](https://github.com/rust-lang/rust-clippy/pull/14082) +* Added [`precedence_bits`] to `restriction` [#14115](https://github.com/rust-lang/rust-clippy/pull/14115) + +### Moves and Deprecations + +* Moved [`redundant_locals`] to `suspicious` (from `correctness`, now warn-by-default) + [#13747](https://github.com/rust-lang/rust-clippy/pull/13747) +* Moved [`format_push_string`] to `pedantic` (from `restriction`) + [#13894](https://github.com/rust-lang/rust-clippy/pull/13894) +* Moved [`format_collect`] to `pedantic` (from `perf`, now allow-by-default) + [#13894](https://github.com/rust-lang/rust-clippy/pull/13894) +* Moved [`mutex_integer`] to `restriction` (from `nursery`) [#14110](https://github.com/rust-lang/rust-clippy/pull/14110) + +### Enhancements + +* Add `lint-inconsistent-struct-field-initializers` configuration option to [`inconsistent_struct_constructor`] + [#13737](https://github.com/rust-lang/rust-clippy/pull/13737) +* [`len_zero`] now also triggers if deref target implements `is_empty()` + [#13871](https://github.com/rust-lang/rust-clippy/pull/13871) +* [`obfuscated_if_else`] now also triggers for the `.then(..).unwrap_or(..)` pattern + [#14021](https://github.com/rust-lang/rust-clippy/pull/14021) + +### False Positive Fixes + +* [`trailing_empty_array`] no longer triggers in tests [#13844](https://github.com/rust-lang/rust-clippy/pull/13844) +* [`missing_const_for_fn`] no longer triggers in tests [#13945](https://github.com/rust-lang/rust-clippy/pull/13945) +* [`significant_drop_in_scrutinee`]: do not falsely warn for temporaries created by `.await` expansion + [#13985](https://github.com/rust-lang/rust-clippy/pull/13985) + +### ICE Fixes + +* [`borrow_interior_mutable_const`] Fix an ICE that can occur when taking a reference to a tuple/`struct` field of an + interior mutable `const` [#13877](https://github.com/rust-lang/rust-clippy/pull/13877) + +### Others + +* Clippy now uses Rust edition 2024 [#13751](https://github.com/rust-lang/rust-clippy/pull/13751) ## Rust 1.85 -Current stable, released 2025-02-20 +Released 2025-02-20 [View all 72 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-11-15T19%3A31%3A08Z..2024-12-26T13%3A59%3A48Z+base%3Amaster) @@ -5516,6 +5671,7 @@ Released 2018-09-13 [`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes [`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts [`cfg_not_test`]: https://rust-lang.github.io/rust-clippy/master/index.html#cfg_not_test +[`char_indices_as_byte_indices`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_indices_as_byte_indices [`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8 [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp @@ -5525,6 +5681,7 @@ Released 2018-09-13 [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy [`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr [`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied +[`cloned_ref_to_slice_refs`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_ref_to_slice_refs [`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned @@ -5536,6 +5693,7 @@ Released 2018-09-13 [`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty +[`confusing_method_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#confusing_method_to_numeric_cast [`const_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_is_empty [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator @@ -5570,6 +5728,7 @@ Released 2018-09-13 [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression +[`doc_comment_double_space_linebreaks`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_comment_double_space_linebreaks [`doc_include_without_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_include_without_cfg [`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation [`doc_link_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_code @@ -5680,6 +5839,7 @@ Released 2018-09-13 [`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else [`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none [`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond +[`ignore_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignore_without_reason [`ignored_unit_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignored_unit_patterns [`impl_hash_borrow_with_str_and_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_hash_borrow_with_str_and_bytes [`impl_trait_in_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_trait_in_params @@ -5782,12 +5942,14 @@ Released 2018-09-13 [`macro_metavars_in_unsafe`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion +[`manual_abs_diff`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_abs_diff [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp [`manual_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_contains +[`manual_dangling_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_dangling_ptr [`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil [`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map @@ -6054,6 +6216,7 @@ Released 2018-09-13 [`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate [`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes +[`redundant_test_prefix`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_test_prefix [`redundant_type_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_type_annotations [`ref_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_as_ptr [`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference @@ -6155,6 +6318,7 @@ Released 2018-09-13 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`suspicious_xor_used_as_pow`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_xor_used_as_pow [`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref +[`swap_with_temporary`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_with_temporary [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`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 @@ -6276,6 +6440,7 @@ Released 2018-09-13 [`used_underscore_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_items [`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref [`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute +[`useless_concat`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_concat [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion [`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format [`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq @@ -6319,6 +6484,7 @@ Released 2018-09-13 [`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement [`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero [`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests +[`allow-exact-repetitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-exact-repetitions [`allow-expect-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-consts [`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests [`allow-indexing-slicing-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-indexing-slicing-in-tests @@ -6345,6 +6511,7 @@ Released 2018-09-13 [`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types [`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish [`check-incompatible-msrv-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-incompatible-msrv-in-tests +[`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold [`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros @@ -6361,7 +6528,7 @@ Released 2018-09-13 [`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold -[`lint-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-inconsistent-struct-field-initializers +[`lint-commented-code`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-commented-code [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold [`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else [`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools @@ -6370,8 +6537,10 @@ Released 2018-09-13 [`max-suggested-slice-pattern-length`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-suggested-slice-pattern-length [`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds [`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold +[`missing-docs-allow-unused`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-allow-unused [`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items [`module-item-order-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-item-order-groupings +[`module-items-ordered-within-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-items-ordered-within-groupings [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index 3b33f71906383..72ab08792d37e 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -51,7 +51,7 @@ Clippy team directly by mentioning them in the issue or over on [Zulip]. All currently active team members can be found [here](https://github.com/rust-lang/rust-clippy/blob/master/triagebot.toml#L18) -Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy +Some issues are easier than others. The [`good first issue`] label can be used to find the easy issues. You can use `@rustbot claim` to assign the issue to yourself. There are also some abandoned PRs, marked with [`S-inactive-closed`]. @@ -70,16 +70,16 @@ To figure out how this syntax structure is encoded in the AST, it is recommended Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting]. But we can make it nest-less by using [let chains], [like this][nest-less]. -[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`] +[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`] first. Sometimes they are only somewhat involved code wise, but not difficult per-se. Note that [`E-medium`] issues may require some knowledge of Clippy internals or some debugging to find the actual problem behind the issue. [`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of -an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful. +an AST expression). -[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue +[`good first issue`]: https://github.com/rust-lang/rust-clippy/labels/good%20first%20issue [`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed [`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST [`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle diff --git a/src/tools/clippy/COPYRIGHT b/src/tools/clippy/COPYRIGHT index 219693d63d973..f402dcf465a34 100644 --- a/src/tools/clippy/COPYRIGHT +++ b/src/tools/clippy/COPYRIGHT @@ -1,6 +1,6 @@ // REUSE-IgnoreStart -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index c4588002dc990..f69e5bee4bb2b 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy" -# begin autogenerated version -version = "0.1.87" -# end autogenerated version +version = "0.1.89" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -25,15 +23,17 @@ path = "src/driver.rs" [dependencies] clippy_config = { path = "clippy_config" } clippy_lints = { path = "clippy_lints" } +clippy_utils = { path = "clippy_utils" } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } -tempfile = { version = "3.3", optional = true } +clippy_lints_internal = { path = "clippy_lints_internal", optional = true } +tempfile = { version = "3.20", optional = true } termize = "0.1" color-print = "0.3.4" anstream = "0.6.18" [dev-dependencies] cargo_metadata = "0.18.1" -ui_test = "0.26.4" +ui_test = "0.29.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" @@ -42,10 +42,9 @@ walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } -rinja = { version = "0.3", default-features = false, features = ["config"] } +askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] } # UI test dependencies -clippy_utils = { path = "clippy_utils" } if_chain = "1.0" quote = "1.0.25" syn = { version = "2.0", features = ["full"] } @@ -57,8 +56,8 @@ tokio = { version = "1", features = ["io-util"] } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } [features] -integration = ["tempfile"] -internal = ["clippy_lints/internal", "tempfile"] +integration = ["dep:tempfile"] +internal = ["dep:clippy_lints_internal", "dep:tempfile"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/src/tools/clippy/LICENSE-APACHE b/src/tools/clippy/LICENSE-APACHE index 506582c31d6d5..9990a0cec4743 100644 --- a/src/tools/clippy/LICENSE-APACHE +++ b/src/tools/clippy/LICENSE-APACHE @@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work. same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/tools/clippy/LICENSE-MIT b/src/tools/clippy/LICENSE-MIT index 6d8ee9afb6163..5d6e36ef6bfc2 100644 --- a/src/tools/clippy/LICENSE-MIT +++ b/src/tools/clippy/LICENSE-MIT @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2014-2024 The Rust Project Developers +Copyright (c) 2014-2025 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index 32c1d33e2ed36..20a5e997e629b 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -277,7 +277,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/src/tools/clippy/rinja.toml b/src/tools/clippy/askama.toml similarity index 100% rename from src/tools/clippy/rinja.toml rename to src/tools/clippy/askama.toml diff --git a/src/tools/clippy/book/src/SUMMARY.md b/src/tools/clippy/book/src/SUMMARY.md index 19328fdd3cd47..39fe7358ed87a 100644 --- a/src/tools/clippy/book/src/SUMMARY.md +++ b/src/tools/clippy/book/src/SUMMARY.md @@ -30,6 +30,7 @@ - [Updating the Changelog](development/infrastructure/changelog_update.md) - [Release a New Version](development/infrastructure/release.md) - [The Clippy Book](development/infrastructure/book.md) + - [Benchmarking Clippy](development/infrastructure/benchmarking.md) - [Proposals](development/proposals/README.md) - [Roadmap 2021](development/proposals/roadmap-2021.md) - [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md) diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index 0b9010f01071f..2b89e94cf8f4f 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -99,6 +99,7 @@ struct A; impl A { pub fn fo(&self) {} pub fn foo(&self) {} + //~^ foo_functions pub fn food(&self) {} } @@ -106,12 +107,14 @@ impl A { trait B { fn fo(&self) {} fn foo(&self) {} + //~^ foo_functions fn food(&self) {} } // Plain functions fn fo() {} fn foo() {} +//~^ foo_functions fn food() {} fn main() { @@ -122,17 +125,24 @@ fn main() { } ``` -Now we can run the test with `TESTNAME=foo_functions cargo uibless`, currently -this test is meaningless though. +Note that we are adding comment annotations with the name of our lint to mark +lines where we expect an error. Except for very specific situations +(`//@check-pass`), at least one error marker must be present in a test file for +it to be accepted. + +Once we have implemented our lint we can run `TESTNAME=foo_functions cargo +uibless` to generate the `.stderr` file. If our lint makes use of structured +suggestions then this command will also generate the corresponding `.fixed` +file. While we are working on implementing our lint, we can keep running the UI test. That allows us to check if the output is turning into what we want by checking the `.stderr` file that gets updated on every test run. -Running `TESTNAME=foo_functions cargo uitest` should pass on its own. When we -commit our lint, we need to commit the generated `.stderr` files, too. In -general, you should only commit files changed by `cargo bless` for the -specific lint you are creating/editing. +Once we have implemented our lint running `TESTNAME=foo_functions cargo uitest` +should pass on its own. When we commit our lint, we need to commit the generated + `.stderr` and if applicable `.fixed` files, too. In general, you should only + commit files changed by `cargo bless` for the specific lint you are creating/editing. > _Note:_ you can run multiple test files by specifying a comma separated list: > `TESTNAME=foo_functions,test2,test3`. @@ -406,7 +416,7 @@ In our example, `is_foo_fn` looks like: fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { match fn_kind { - FnKind::Fn(_, ident, ..) => { + FnKind::Fn(_, _, Fn { ident, .. }) => { // check if `fn` name is `foo` ident.name.as_str() == "foo" } diff --git a/src/tools/clippy/book/src/development/basics.md b/src/tools/clippy/book/src/development/basics.md index 931e5c3a2942a..fc405249bcfe8 100644 --- a/src/tools/clippy/book/src/development/basics.md +++ b/src/tools/clippy/book/src/development/basics.md @@ -145,42 +145,32 @@ unclear to you. If you are hacking on Clippy and want to install it from source, do the following: -First, take note of the toolchain -[override](https://rust-lang.github.io/rustup/overrides.html) in -`/rust-toolchain`. We will use this override to install Clippy into the right -toolchain. - -> Tip: You can view the active toolchain for the current directory with `rustup -> show active-toolchain`. - From the Clippy project root, run the following command to build the Clippy -binaries and copy them into the toolchain directory. This will override the -currently installed Clippy component. +binaries and copy them into the toolchain directory. This will create a new +toolchain called `clippy` by default, see `cargo dev setup toolchain --help` +for other options. ```terminal -cargo build --release --bin cargo-clippy --bin clippy-driver -Zunstable-options --out-dir "$(rustc --print=sysroot)/bin" +cargo dev setup toolchain ``` -Now you may run `cargo clippy` in any project, using the toolchain where you -just installed Clippy. +Now you may run `cargo +clippy clippy` in any project using the new toolchain. ```terminal cd my-project -cargo +nightly-2021-07-01 clippy +cargo +clippy clippy ``` ...or `clippy-driver` ```terminal -clippy-driver +nightly-2021-07-01 +clippy-driver +clippy ``` -If you need to restore the default Clippy installation, run the following (from -the Clippy project root). +If you no longer need the toolchain it can be uninstalled using `rustup`: ```terminal -rustup component remove clippy -rustup component add clippy +rustup toolchain uninstall clippy ``` > **DO NOT** install using `cargo install --path . --force` since this will diff --git a/src/tools/clippy/book/src/development/common_tools_writing_lints.md b/src/tools/clippy/book/src/development/common_tools_writing_lints.md index 051febc2ca5da..e23b32039c90b 100644 --- a/src/tools/clippy/book/src/development/common_tools_writing_lints.md +++ b/src/tools/clippy/book/src/development/common_tools_writing_lints.md @@ -86,7 +86,7 @@ arguments have to be checked separately. ```rust use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{paths, match_def_path}; +use clippy_utils::paths; use rustc_span::symbol::sym; use rustc_hir::LangItem; @@ -108,7 +108,7 @@ impl LateLintPass<'_> for MyStructLint { // 3. Using the type path // This method should be avoided if possible - if match_def_path(cx, def_id, &paths::RESULT) { + if paths::RESULT.matches_ty(cx, ty) { // The type is a `core::result::Result` } } @@ -159,19 +159,21 @@ paths for Clippy can be found in [paths.rs][paths] To check if our type defines a method called `some_method`: ```rust -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::return_ty; +use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{sym, return_ty}; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { // Check if item is a method/function if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // Check the method is named `some_method` - && impl_item.ident.name.as_str() == "some_method" + // + // Add `some_method` to `clippy_utils::sym` if it's not already there + && impl_item.ident.name == sym::some_method // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go further and even check if its return type is `String` - && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)) + && is_type_lang_item(cx, return_ty(cx, impl_item.hir_id), LangItem::String) { // ... } diff --git a/src/tools/clippy/book/src/development/defining_lints.md b/src/tools/clippy/book/src/development/defining_lints.md index 169cecd7d11dc..cb6d7b740dbd1 100644 --- a/src/tools/clippy/book/src/development/defining_lints.md +++ b/src/tools/clippy/book/src/development/defining_lints.md @@ -9,7 +9,7 @@ lint involves some boilerplate code. A lint type is the category of items and expressions in which your lint focuses on. -As of the writing of this documentation update, there are 12 _types_ of lints +As of the writing of this documentation update, there are 11 _types_ of lints besides the numerous standalone lints living under `clippy_lints/src/`: - `cargo` @@ -23,7 +23,6 @@ besides the numerous standalone lints living under `clippy_lints/src/`: - `transmute` - `types` - `unit_types` -- `utils / internal` (Clippy internal lints) These types group together lints that share some common behaviors. For instance, `functions` groups together lints that deal with some aspects of functions in diff --git a/src/tools/clippy/book/src/development/infrastructure/benchmarking.md b/src/tools/clippy/book/src/development/infrastructure/benchmarking.md new file mode 100644 index 0000000000000..a3ebce922f6c9 --- /dev/null +++ b/src/tools/clippy/book/src/development/infrastructure/benchmarking.md @@ -0,0 +1,55 @@ +# Benchmarking Clippy + +Benchmarking Clippy is similar to using our Lintcheck tool, in fact, it even +uses the same tool! Just by adding a `--perf` flag it will transform Lintcheck +into a very simple but powerful benchmarking tool! + +It requires having the [`perf` tool][perf] installed, as `perf` is what's actually +profiling Clippy under the hood. + +The lintcheck `--perf` tool generates a series of `perf.data` in the +`target/lintcheck/sources/-` directories. Each `perf.data` +corresponds to the package which is contained. + +Lintcheck uses the `-g` flag, meaning that you can get stack traces for richer +analysis, including with tools such as [flamegraph][flamegraph-perf] +(or [`flamegraph-rs`][flamegraph-rs]). + +Currently, we only measure instruction count, as it's the most reproducible metric +and [rustc-perf][rustc-perf] also considers it the main number to focus on. + +## Benchmarking a PR + +Having a benchmarking tool directly implemented into lintcheck gives us the +ability to benchmark any given PR just by making a before and after + +Here's the way you can get into any PR, benchmark it, and then benchmark +`master`. + +The first `perf.data` will not have any numbers appended, but any subsequent +benchmark will be written to `perf.data.number` with a number growing for 0. +All benchmarks are compressed so that you can + +```bash +git fetch upstream pull//head: +git switch BRANCHNAME + +# Bench +cargo lintcheck --perf + +# Get last common commit, checkout that +LAST_COMMIT=$(git log BRANCHNAME..master --oneline | tail -1 | cut -c 1-11) +git switch -c temporary $LAST_COMMIT + +# We're now on master + +# Bench +cargo lintcheck --perf +perf diff ./target/lintcheck/sources/CRATE/perf.data ./target/lintcheck/sources/CRATE/perf.data.0 +``` + + +[perf]: https://perfwiki.github.io/main/ +[flamegraph-perf]: https://github.com/brendangregg/FlameGraph +[flamegraph-rs]: https://github.com/flamegraph-rs/flamegraph +[rustc-perf]: https://github.com/rust-lang/rustc-perf diff --git a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md index 2b2c096b0496f..eede6b78d9216 100644 --- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md +++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md @@ -51,7 +51,9 @@ Once you've got the correct commit range, run util/fetch_prs_between.sh commit1 commit2 > changes.txt ``` -and open that file in your editor of choice. +where `commit2` is the commit hash from the previous command and `commit1` +is the commit hash from the current CHANGELOG file. +Open `changes.txt` file in your editor of choice. When updating the changelog it's also a good idea to make sure that `commit1` is already correct in the current changelog. @@ -60,8 +62,8 @@ already correct in the current changelog. The above script should have dumped all the relevant PRs to the file you specified. It should have filtered out most of the irrelevant PRs already, but -it's a good idea to do a manual cleanup pass where you look for more irrelevant -PRs. If you're not sure about some PRs, just leave them in for the review and +it's a good idea to do a manual cleanup pass and choose valuable PRs. +If you're not sure about some PRs, just leave them in for the review and ask for feedback. With the PRs filtered, you can start to take each PR and move the `changelog: ` @@ -74,10 +76,9 @@ The order should roughly be: 2. Moves or deprecations of lints 3. Changes that expand what code existing lints cover 4. False positive fixes -5. Suggestion fixes/improvements -6. ICE fixes -7. Documentation improvements -8. Others +5. ICE fixes +6. Documentation improvements +7. Others As section headers, we use: @@ -91,7 +92,6 @@ As section headers, we use: ### Enhancements ### False Positive Fixes -### Suggestion Fixes/Improvements ### ICE Fixes ### Documentation Improvements ### Others @@ -112,7 +112,16 @@ that label in the changelog. If you can, remove the `beta-accepted` labels ### 4. Update `clippy::version` attributes Next, make sure to check that the `#[clippy::version]` attributes for the added -lints contain the correct version. +lints contain the correct version. +In order to find lints that need a version update, go through the lints in the +"New Lints" section and run the following command for each lint name: + +``` +grep -rB1 "pub $LINT_NAME" . +``` + +The version shown should match the version of the release the changelog is +written for. If not, update the version to the changelog version. [changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md [forge]: https://forge.rust-lang.org/ diff --git a/src/tools/clippy/book/src/development/infrastructure/release.md b/src/tools/clippy/book/src/development/infrastructure/release.md index 8b080c099b81c..a429e0d953c46 100644 --- a/src/tools/clippy/book/src/development/infrastructure/release.md +++ b/src/tools/clippy/book/src/development/infrastructure/release.md @@ -88,9 +88,6 @@ git push upstream stable After updating the `stable` branch, tag the HEAD commit and push it to the Clippy repo. -> Note: Only push the tag once the Deploy GitHub action of the `beta` branch is -> finished. Otherwise the deploy for the tag might fail. - ```bash git tag rust-1.XX.0 # XX should be exchanged with the corresponding version git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote diff --git a/src/tools/clippy/book/src/development/infrastructure/sync.md b/src/tools/clippy/book/src/development/infrastructure/sync.md index da1ad586607f9..2bbdf47a83581 100644 --- a/src/tools/clippy/book/src/development/infrastructure/sync.md +++ b/src/tools/clippy/book/src/development/infrastructure/sync.md @@ -86,7 +86,7 @@ to be run inside the `rust` directory): 4. Bump the nightly version in the Clippy repository by running these commands: ```bash cargo dev sync update_nightly - git commit -m "Bump nightly version -> YYYY-MM-DD" rust-toolchain clippy_utils/README.md + git commit -m "Bump nightly version -> YYYY-MM-DD" rust-toolchain.toml clippy_utils/README.md ``` 5. 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 diff --git a/src/tools/clippy/book/src/development/macro_expansions.md b/src/tools/clippy/book/src/development/macro_expansions.md index 36092f82e2607..ed547130b358e 100644 --- a/src/tools/clippy/book/src/development/macro_expansions.md +++ b/src/tools/clippy/book/src/development/macro_expansions.md @@ -150,9 +150,85 @@ if foo_span.in_external_macro(cx.sess().source_map()) { } ``` +### The `is_from_proc_macro` function +A common point of confusion is the existence of [`is_from_proc_macro`] +and how it differs from the other [`in_external_macro`]/[`from_expansion`] functions. + +While [`in_external_macro`] and [`from_expansion`] both work perfectly fine for detecting expanded code +from *declarative* macros (i.e. `macro_rules!` and macros 2.0), +detecting *proc macro*-generated code is a bit more tricky, as proc macros can (and often do) +freely manipulate the span of returned tokens. + +In practice, this often happens through the use of [`quote::quote_spanned!`] with a span from the input tokens. + +In those cases, there is no *reliable* way for the compiler (and tools like Clippy) +to distinguish code that comes from such a proc macro from code that the user wrote directly, +and [`in_external_macro`] will return `false`. + +This is usually not an issue for the compiler and actually helps proc macro authors create better error messages, +as it allows associating parts of the expansion with parts of the macro input and lets the compiler +point the user to the relevant code in case of a compile error. + +However, for Clippy this is inconvenient, because most of the time *we don't* want +to lint proc macro-generated code and this makes it impossible to tell what is and isn't proc macro code. + +> NOTE: this is specifically only an issue when a proc macro explicitly sets the span to that of an **input span**. +> +> For example, other common ways of creating `TokenStream`s, such as `"fn foo() {...}".parse::()`, +> sets each token's span to `Span::call_site()`, which already marks the span as coming from a proc macro +> and the usual span methods have no problem detecting that as a macro span. + +As such, Clippy has its own `is_from_proc_macro` function which tries to *approximate* +whether a span comes from a proc macro, by checking whether the source text at the given span +lines up with the given AST node. + +This function is typically used in combination with the other mentioned macro span functions, +but is usually called much later into the condition chain as it's a bit heavier than most other conditions, +so that the other cheaper conditions can fail faster. For example, the `borrow_deref_ref` lint: +```rs +impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { + if let ... = ... + && ... + && !e.span.from_expansion() + && ... + && ... + && !is_from_proc_macro(cx, e) + && ... + { + ... + } + } +} +``` + +### Testing lints with macro expansions +To test that all of these cases are handled correctly in your lint, +we have a helper auxiliary crate that exposes various macros, used by tests like so: +```rust +//@aux-build:proc_macros.rs + +extern crate proc_macros; + +fn main() { + proc_macros::external!{ code_that_should_trigger_your_lint } + proc_macros::with_span!{ span code_that_should_trigger_your_lint } +} +``` +This exercises two cases: +- `proc_macros::external!` is a simple proc macro that echos the input tokens back but with a macro span: +this represents the usual, common case where an external macro expands to code that your lint would trigger, +and is correctly handled by `in_external_macro` and `Span::from_expansion`. + +- `proc_macros::with_span!` echos back the input tokens starting from the second token +with the span of the first token: this is where the other functions will fail and `is_from_proc_macro` is needed + + [`ctxt`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html#method.ctxt [expansion]: https://rustc-dev-guide.rust-lang.org/macro-expansion.html#expansion-and-ast-integration [`from_expansion`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion [`in_external_macro`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html#method.in_external_macro [Span]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html [SyntaxContext]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html +[`is_from_proc_macro`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.is_from_proc_macro.html +[`quote::quote_spanned!`]: https://docs.rs/quote/latest/quote/macro.quote_spanned.html diff --git a/src/tools/clippy/book/src/development/the_team.md b/src/tools/clippy/book/src/development/the_team.md index 6bc0783b166a9..d22123231868e 100644 --- a/src/tools/clippy/book/src/development/the_team.md +++ b/src/tools/clippy/book/src/development/the_team.md @@ -33,7 +33,7 @@ this group to help with triaging, which can include: 1. **Labeling issues** - For the `good-first-issue` label, it can still be good to use `@rustbot` to + For the `good first issue` label, it can still be good to use `@rustbot` to subscribe to the issue and help interested parties, if they post questions in the comments. @@ -102,8 +102,7 @@ is responsible for maintaining Clippy. 5. **Update the changelog** - This needs to be done for every release, every six weeks. This is usually - done by @xFrednet. + This needs to be done for every release, every six weeks. ### Membership diff --git a/src/tools/clippy/book/src/development/trait_checking.md b/src/tools/clippy/book/src/development/trait_checking.md index b7d229ccc1930..cc4eb966f596a 100644 --- a/src/tools/clippy/book/src/development/trait_checking.md +++ b/src/tools/clippy/book/src/development/trait_checking.md @@ -73,22 +73,24 @@ impl LateLintPass<'_> for CheckDropTraitLint { ## Using Type Path If neither diagnostic item nor a language item is available, we can use -[`clippy_utils::paths`][paths] with the `match_trait_method` to determine trait -implementation. +[`clippy_utils::paths`][paths] to determine get a trait's `DefId`. > **Note**: This approach should be avoided if possible, the best thing to do would be to make a PR to [`rust-lang/rust`][rust] adding a diagnostic item. -Below, we check if the given `expr` implements the `Iterator`'s trait method `cloned` : +Below, we check if the given `expr` implements [`core::iter::Step`](https://doc.rust-lang.org/std/iter/trait.Step.html): ```rust -use clippy_utils::{match_trait_method, paths}; +use clippy_utils::{implements_trait, paths}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; -impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait { +impl LateLintPass<'_> for CheckIterStep { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if match_trait_method(cx, expr, &paths::CORE_ITER_CLONED) { - println!("`expr` implements `CORE_ITER_CLONED` trait!"); + let ty = cx.typeck_results().expr_ty(expr); + if let Some(trait_def_id) = paths::ITER_STEP.first(cx) + && implements_trait(cx, ty, trait_def_id, &[]) + { + println!("`expr` implements the `core::iter::Step` trait!"); } } } diff --git a/src/tools/clippy/book/src/development/writing_tests.md b/src/tools/clippy/book/src/development/writing_tests.md index d4cca2a72f0be..1da54322fcf72 100644 --- a/src/tools/clippy/book/src/development/writing_tests.md +++ b/src/tools/clippy/book/src/development/writing_tests.md @@ -41,20 +41,23 @@ Update the file with some examples to get started: struct A; impl A { pub fn fo(&self) {} - pub fn foo(&self) {} //~ ERROR: function called "foo" + pub fn foo(&self) {} + //~^ foo_functions pub fn food(&self) {} } // Default trait methods trait B { fn fo(&self) {} - fn foo(&self) {} //~ ERROR: function called "foo" + fn foo(&self) {} + //~^ foo_functions fn food(&self) {} } // Plain functions fn fo() {} -fn foo() {} //~ ERROR: function called "foo" +fn foo() {} +//~^ foo_functions fn food() {} fn main() { @@ -66,19 +69,38 @@ fn main() { ``` Without actual lint logic to emit the lint when we see a `foo` function name, -this test will just pass, because no lint will be emitted. However, we can now -run the test with the following command: +this test will fail, because we expect errors at lines marked with +`//~^ foo_functions`. However, we can now run the test with the following command: ```sh $ TESTNAME=foo_functions cargo uitest ``` -Clippy will compile and it will conclude with an `ok` for the tests: +Clippy will compile and it will fail complaining it didn't receive any errors: ``` ...Clippy warnings and test outputs... -test compile_test ... ok -test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s +error: diagnostic code `clippy::foo_functions` not found on line 8 + --> tests/ui/foo_functions.rs:9:10 + | +9 | //~^ foo_functions + | ^^^^^^^^^^^^^ expected because of this pattern + | + +error: diagnostic code `clippy::foo_functions` not found on line 16 + --> tests/ui/foo_functions.rs:17:10 + | +17 | //~^ foo_functions + | ^^^^^^^^^^^^^ expected because of this pattern + | + +error: diagnostic code `clippy::foo_functions` not found on line 23 + --> tests/ui/foo_functions.rs:24:6 + | +24 | //~^ foo_functions + | ^^^^^^^^^^^^^ expected because of this pattern + | + ``` This is normal. After all, we wrote a bunch of Rust code but we haven't really diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 28b613ea32951..9809e32de8a44 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -71,6 +71,16 @@ Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` * [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro) +## `allow-exact-repetitions` +Whether an item should be allowed to have the same name as its containing module + +**Default Value:** `true` + +--- +**Affected lints:** +* [`module_name_repetitions`](https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions) + + ## `allow-expect-in-consts` Whether `expect` should be allowed in code always evaluated at compile time @@ -425,6 +435,33 @@ Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. * [`incompatible_msrv`](https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv) +## `check-inconsistent-struct-field-initializers` +Whether to suggest reordering constructor fields when initializers are present. + +Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the +suggested code would compile, it can change semantics if the initializer expressions have side effects. The +following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + +```rust +struct MyStruct { + vector: Vec, + length: usize +} +fn main() { + let vector = vec![1,2,3]; + MyStruct { length: vector.len(), vector}; +} +``` + +[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + +**Default Value:** `false` + +--- +**Affected lints:** +* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor) + + ## `check-private-items` Whether to also run the listed lints on private items. @@ -613,31 +650,15 @@ The maximum size of the `Err`-variant in a `Result` returned from a function * [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) -## `lint-inconsistent-struct-field-initializers` -Whether to suggest reordering constructor fields when initializers are present. - -Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the -suggested code would compile, it can change semantics if the initializer expressions have side effects. The -following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: - -```rust -struct MyStruct { - vector: Vec, - length: usize -} -fn main() { - let vector = vec![1,2,3]; - MyStruct { length: vector.len(), vector}; -} -``` - -[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 +## `lint-commented-code` +Whether collapsible `if` chains are linted if they contain comments inside the parts +that would be collapsed. **Default Value:** `false` --- **Affected lints:** -* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor) +* [`collapsible_if`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if) ## `literal-representation-threshold` @@ -723,6 +744,16 @@ Minimum chars an ident can have, anything below or equal to this will be linted. * [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars) +## `missing-docs-allow-unused` +Whether to allow fields starting with an underscore to skip documentation requirements + +**Default Value:** `false` + +--- +**Affected lints:** +* [`missing_docs_in_private_items`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items) + + ## `missing-docs-in-crate-items` Whether to **only** check for missing documentation in items visible within the current crate. For example, `pub(crate)` items. @@ -744,6 +775,19 @@ The named groupings of different source item kinds within modules. * [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering) +## `module-items-ordered-within-groupings` +Whether the items within module groups should be ordered alphabetically or not. + +This option can be configured to "all", "none", or a list of specific grouping names that should be checked +(e.g. only "enums"). + +**Default Value:** `"none"` + +--- +**Affected lints:** +* [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering) + + ## `msrv` The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` @@ -773,6 +817,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) * [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants) * [`lines_filter_map_ok`](https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok) +* [`manual_abs_diff`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_abs_diff) * [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits) * [`manual_c_str_literals`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals) * [`manual_clamp`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) @@ -780,6 +825,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_flatten`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten) * [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one) * [`manual_is_ascii_check`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) +* [`manual_is_power_of_two`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two) * [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) * [`manual_midpoint`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint) * [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive) @@ -790,6 +836,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_repeat_n`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n) * [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) * [`manual_slice_fill`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_fill) +* [`manual_slice_size_calculation`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_size_calculation) * [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) * [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) * [`manual_strip`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip) @@ -806,12 +853,14 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) * [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) +* [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) * [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes) * [`repeat_vec_with_capacity`](https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity) * [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) * [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) +* [`to_digit_is_some`](https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some) * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) * [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) @@ -1045,7 +1094,8 @@ The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' ## `warn-on-all-wildcard-imports` -Whether to allow certain wildcard imports (prelude, super in tests). +Whether to emit warnings on all wildcard imports, including those from `prelude`, from `super` in tests, +or for `pub use` reexports. **Default Value:** `false` diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml index f4789c9d03035..77573105d86a3 100644 --- a/src/tools/clippy/clippy.toml +++ b/src/tools/clippy/clippy.toml @@ -1,6 +1,8 @@ avoid-breaking-exported-api = false -lint-inconsistent-struct-field-initializers = true +check-inconsistent-struct-field-initializers = true + +lint-commented-code = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 934725fccb8ec..0606245f990c1 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy_config" -# begin autogenerated version -version = "0.1.87" -# end autogenerated version +version = "0.1.89" edition = "2024" publish = false diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index a61acbaa96bcf..4ce8d001c2f00 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -3,14 +3,17 @@ use crate::types::{ DisallowedPath, DisallowedPathWithoutReplacement, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, + SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::msrvs::Msrv; +use itertools::Itertools; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; use serde::de::{IgnoredAny, IntoDeserializer, MapAccess, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; +use std::collections::HashMap; use std::fmt::{Debug, Display, Formatter}; use std::ops::Range; use std::path::PathBuf; @@ -79,6 +82,7 @@ const DEFAULT_SOURCE_ITEM_ORDERING: &[SourceItemOrderingCategory] = { #[derive(Default)] struct TryConf { conf: Conf, + value_spans: HashMap>, errors: Vec, warnings: Vec, } @@ -87,6 +91,7 @@ impl TryConf { fn from_toml_error(file: &SourceFile, error: &toml::de::Error) -> Self { Self { conf: Conf::default(), + value_spans: HashMap::default(), errors: vec![ConfError::from_toml(file, error)], warnings: vec![], } @@ -115,12 +120,7 @@ impl ConfError { Self { message: message.into(), suggestion, - span: Span::new( - file.start_pos + BytePos::from_usize(span.start), - file.start_pos + BytePos::from_usize(span.end), - SyntaxContext::root(), - None, - ), + span: span_from_toml_range(file, span), } } } @@ -171,11 +171,61 @@ macro_rules! default_text { }; } +macro_rules! deserialize { + ($map:expr, $ty:ty, $errors:expr, $file:expr) => {{ + let raw_value = $map.next_value::>()?; + let value_span = raw_value.span(); + let value = match <$ty>::deserialize(raw_value.into_inner()) { + Err(e) => { + $errors.push(ConfError::spanned( + $file, + e.to_string().replace('\n', " ").trim(), + None, + value_span, + )); + continue; + }, + Ok(value) => value, + }; + (value, value_span) + }}; + + ($map:expr, $ty:ty, $errors:expr, $file:expr, $replacements_allowed:expr) => {{ + let array = $map.next_value::>>()?; + let mut disallowed_paths_span = Range { + start: usize::MAX, + end: usize::MIN, + }; + let mut disallowed_paths = Vec::new(); + for raw_value in array { + let value_span = raw_value.span(); + let mut disallowed_path = match DisallowedPath::<$replacements_allowed>::deserialize(raw_value.into_inner()) + { + Err(e) => { + $errors.push(ConfError::spanned( + $file, + e.to_string().replace('\n', " ").trim(), + None, + value_span, + )); + continue; + }, + Ok(disallowed_path) => disallowed_path, + }; + disallowed_paths_span = union(&disallowed_paths_span, &value_span); + disallowed_path.set_span(span_from_toml_range($file, value_span)); + disallowed_paths.push(disallowed_path); + } + (disallowed_paths, disallowed_paths_span) + }}; +} + macro_rules! define_Conf { ($( $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])? $(#[default_text = $default_text:expr])? + $(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])? $(#[lints($($for_lints:ident),* $(,)?)])? $name:ident: $ty:ty = $default:expr, )*) => { @@ -210,47 +260,53 @@ macro_rules! define_Conf { } fn visit_map(self, mut map: V) -> Result where V: MapAccess<'de> { + let mut value_spans = HashMap::new(); let mut errors = Vec::new(); let mut warnings = Vec::new(); + + // Declare a local variable for each field available to a configuration file. $(let mut $name = None;)* + // could get `Field` here directly, but get `String` first for diagnostics while let Some(name) = map.next_key::>()? { - match Field::deserialize(name.get_ref().as_str().into_deserializer()) { + let field = match Field::deserialize(name.get_ref().as_str().into_deserializer()) { Err(e) => { let e: FieldError = e; errors.push(ConfError::spanned(self.0, e.error, e.suggestion, name.span())); + continue; } - $(Ok(Field::$name) => { + Ok(field) => field + }; + + match field { + $(Field::$name => { + // Is this a deprecated field, i.e., is `$dep` set? If so, push a warning. $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)? - let raw_value = map.next_value::>()?; - let value_span = raw_value.span(); - match <$ty>::deserialize(raw_value.into_inner()) { - Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), None, value_span)), - Ok(value) => match $name { - Some(_) => { - errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); - } - None => { - $name = Some(value); - // $new_conf is the same as one of the defined `$name`s, so - // this variable is defined in line 2 of this function. - $(match $new_conf { - Some(_) => errors.push(ConfError::spanned(self.0, concat!( - "duplicate field `", stringify!($new_conf), - "` (provided as `", stringify!($name), "`)" - ), None, name.span())), - None => $new_conf = $name.clone(), - })? - }, - } + let (value, value_span) = + deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?); + // Was this field set previously? + if $name.is_some() { + errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); + continue; } + $name = Some(value); + value_spans.insert(name.get_ref().as_str().to_string(), value_span); + // If this is a deprecated field, was the new field (`$new_conf`) set previously? + // Note that `$new_conf` is one of the defined `$name`s. + $(match $new_conf { + Some(_) => errors.push(ConfError::spanned(self.0, concat!( + "duplicate field `", stringify!($new_conf), + "` (provided as `", stringify!($name), "`)" + ), None, name.span())), + None => $new_conf = $name.clone(), + })? })* // ignore contents of the third_party key - Ok(Field::third_party) => drop(map.next_value::()) + Field::third_party => drop(map.next_value::()) } } let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* }; - Ok(TryConf { conf, errors, warnings }) + Ok(TryConf { conf, value_spans, errors, warnings }) } } @@ -268,6 +324,22 @@ macro_rules! define_Conf { }; } +fn union(x: &Range, y: &Range) -> Range { + Range { + start: cmp::min(x.start, y.start), + end: cmp::max(x.end, y.end), + } +} + +fn span_from_toml_range(file: &SourceFile, span: Range) -> Span { + Span::new( + file.start_pos + BytePos::from_usize(span.start), + file.start_pos + BytePos::from_usize(span.end), + SyntaxContext::root(), + None, + ) +} + define_Conf! { /// Which crates to allow absolute paths from #[lints(absolute_paths)] @@ -288,6 +360,9 @@ define_Conf! { /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` #[lints(dbg_macro)] allow_dbg_in_tests: bool = false, + /// Whether an item should be allowed to have the same name as its containing module + #[lints(module_name_repetitions)] + allow_exact_repetitions: bool = true, /// Whether `expect` should be allowed in code always evaluated at compile time #[lints(expect_used)] allow_expect_in_consts: bool = true, @@ -454,6 +529,7 @@ define_Conf! { )] avoid_breaking_exported_api: bool = true, /// The list of types which may not be held across an await point. + #[disallowed_paths_allow_replacements = false] #[lints(await_holding_invalid_type)] await_holding_invalid_types: Vec = Vec::new(), /// DEPRECATED LINT: BLACKLISTED_NAME. @@ -467,6 +543,26 @@ define_Conf! { /// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. #[lints(incompatible_msrv)] check_incompatible_msrv_in_tests: bool = false, + /// Whether to suggest reordering constructor fields when initializers are present. + /// + /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the + /// suggested code would compile, it can change semantics if the initializer expressions have side effects. The + /// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + /// + /// ```rust + /// struct MyStruct { + /// vector: Vec, + /// length: usize + /// } + /// fn main() { + /// let vector = vec![1,2,3]; + /// MyStruct { length: vector.len(), vector}; + /// } + /// ``` + /// + /// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + #[lints(inconsistent_struct_constructor)] + check_inconsistent_struct_field_initializers: bool = false, /// Whether to also run the listed lints on private items. #[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)] check_private_items: bool = false, @@ -479,9 +575,11 @@ define_Conf! { #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)] cyclomatic_complexity_threshold: u64 = 25, /// The list of disallowed macros, written as fully qualified paths. + #[disallowed_paths_allow_replacements = true] #[lints(disallowed_macros)] disallowed_macros: Vec = Vec::new(), /// The list of disallowed methods, written as fully qualified paths. + #[disallowed_paths_allow_replacements = true] #[lints(disallowed_methods)] disallowed_methods: Vec = Vec::new(), /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value @@ -490,6 +588,7 @@ define_Conf! { #[lints(disallowed_names)] disallowed_names: Vec = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(), /// The list of disallowed types, written as fully qualified paths. + #[disallowed_paths_allow_replacements = true] #[lints(disallowed_types)] disallowed_types: Vec = Vec::new(), /// The list of words this lint should not consider as identifiers needing ticks. The value @@ -542,25 +641,15 @@ define_Conf! { /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, + /// Whether collapsible `if` chains are linted if they contain comments inside the parts + /// that would be collapsed. + #[lints(collapsible_if)] + lint_commented_code: bool = false, /// Whether to suggest reordering constructor fields when initializers are present. + /// DEPRECATED CONFIGURATION: lint-inconsistent-struct-field-initializers /// - /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the - /// suggested code would compile, it can change semantics if the initializer expressions have side effects. The - /// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: - /// - /// ```rust - /// struct MyStruct { - /// vector: Vec, - /// length: usize - /// } - /// fn main() { - /// let vector = vec![1,2,3]; - /// MyStruct { length: vector.len(), vector}; - /// } - /// ``` - /// - /// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 - #[lints(inconsistent_struct_constructor)] + /// Use the `check-inconsistent-struct-field-initializers` configuration instead. + #[conf_deprecated("Please use `check-inconsistent-struct-field-initializers` instead", check_inconsistent_struct_field_initializers)] lint_inconsistent_struct_field_initializers: bool = false, /// The lower bound for linting decimal literals #[lints(decimal_literal_representation)] @@ -589,6 +678,9 @@ define_Conf! { /// Minimum chars an ident can have, anything below or equal to this will be linted. #[lints(min_ident_chars)] min_ident_chars_threshold: u64 = 1, + /// Whether to allow fields starting with an underscore to skip documentation requirements + #[lints(missing_docs_in_private_items)] + missing_docs_allow_unused: bool = false, /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. #[lints(missing_docs_in_private_items)] @@ -596,6 +688,13 @@ define_Conf! { /// The named groupings of different source item kinds within modules. #[lints(arbitrary_source_item_ordering)] module_item_order_groupings: SourceItemOrderingModuleItemGroupings = DEFAULT_MODULE_ITEM_ORDERING_GROUPS.into(), + /// Whether the items within module groups should be ordered alphabetically or not. + /// + /// This option can be configured to "all", "none", or a list of specific grouping names that should be checked + /// (e.g. only "enums"). + #[lints(arbitrary_source_item_ordering)] + module_items_ordered_within_groupings: SourceItemOrderingWithinModuleItemGroupings = + SourceItemOrderingWithinModuleItemGroupings::None, /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` #[default_text = "current version"] #[lints( @@ -621,6 +720,7 @@ define_Conf! { iter_kv_map, legacy_numeric_constants, lines_filter_map_ok, + manual_abs_diff, manual_bits, manual_c_str_literals, manual_clamp, @@ -628,6 +728,7 @@ define_Conf! { manual_flatten, manual_hash_one, manual_is_ascii_check, + manual_is_power_of_two, manual_let_else, manual_midpoint, manual_non_exhaustive, @@ -638,6 +739,7 @@ define_Conf! { manual_repeat_n, manual_retain, manual_slice_fill, + manual_slice_size_calculation, manual_split_once, manual_str_repeat, manual_strip, @@ -654,12 +756,14 @@ define_Conf! { option_as_ref_deref, option_map_unwrap_or, ptr_as_ptr, + question_mark, redundant_field_names, redundant_static_lifetimes, repeat_vec_with_capacity, same_item_push, seek_from_current, seek_rewind, + to_digit_is_some, transmute_ptr_to_ref, tuple_array_conversions, type_repetition_in_bounds, @@ -745,7 +849,8 @@ define_Conf! { /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' #[lints(verbose_bit_mask)] verbose_bit_mask_threshold: u64 = 1, - /// Whether to allow certain wildcard imports (prelude, super in tests). + /// Whether to emit warnings on all wildcard imports, including those from `prelude`, from `super` in tests, + /// or for `pub use` reexports. #[lints(wildcard_imports)] warn_on_all_wildcard_imports: bool = false, /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. @@ -815,6 +920,36 @@ fn deserialize(file: &SourceFile) -> TryConf { &mut conf.conf.allow_renamed_params_for, DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS, ); + + // Confirms that the user has not accidentally configured ordering requirements for groups that + // aren't configured. + if let SourceItemOrderingWithinModuleItemGroupings::Custom(groupings) = + &conf.conf.module_items_ordered_within_groupings + { + for grouping in groupings { + if !conf.conf.module_item_order_groupings.is_grouping(grouping) { + // Since this isn't fixable by rustfix, don't emit a `Suggestion`. This just adds some useful + // info for the user instead. + + let names = conf.conf.module_item_order_groupings.grouping_names(); + let suggestion = suggest_candidate(grouping, names.iter().map(String::as_str)) + .map(|s| format!(" perhaps you meant `{s}`?")) + .unwrap_or_default(); + let names = names.iter().map(|s| format!("`{s}`")).join(", "); + let message = format!( + "unknown ordering group: `{grouping}` was not specified in `module-items-ordered-within-groupings`,{suggestion} expected one of: {names}" + ); + + let span = conf + .value_spans + .get("module_item_order_groupings") + .cloned() + .unwrap_or_default(); + conf.errors.push(ConfError::spanned(file, message, None, span)); + } + } + } + // TODO: THIS SHOULD BE TESTED, this comment will be gone soon if conf.conf.allowed_idents_below_min_chars.iter().any(|e| e == "..") { conf.conf @@ -860,6 +995,7 @@ impl Conf { let TryConf { mut conf, + value_spans: _, errors, warnings, } = match path { @@ -935,7 +1071,23 @@ impl serde::de::Error for FieldError { // set and allows it. use fmt::Write; - let mut expected = expected.to_vec(); + let metadata = get_configuration_metadata(); + let deprecated = metadata + .iter() + .filter_map(|conf| { + if conf.deprecation_reason.is_some() { + Some(conf.name.as_str()) + } else { + None + } + }) + .collect::>(); + + let mut expected = expected + .iter() + .copied() + .filter(|name| !deprecated.contains(name)) + .collect::>(); expected.sort_unstable(); let (rows, column_widths) = calculate_dimensions(&expected); @@ -950,17 +1102,10 @@ impl serde::de::Error for FieldError { } } - let suggestion = expected - .iter() - .filter_map(|expected| { - let dist = edit_distance(field, expected, 4)?; - Some((dist, expected)) - }) - .min_by_key(|&(dist, _)| dist) - .map(|(_, suggestion)| Suggestion { - message: "perhaps you meant", - suggestion, - }); + let suggestion = suggest_candidate(field, expected).map(|suggestion| Suggestion { + message: "perhaps you meant", + suggestion, + }); Self { error: msg, suggestion } } @@ -998,6 +1143,22 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec) { (rows, column_widths) } +/// Given a user-provided value that couldn't be matched to a known option, finds the most likely +/// candidate among candidates that the user might have meant. +fn suggest_candidate<'a, I>(value: &str, candidates: I) -> Option<&'a str> +where + I: IntoIterator, +{ + candidates + .into_iter() + .filter_map(|expected| { + let dist = edit_distance(value, expected, 4)?; + Some((dist, expected)) + }) + .min_by_key(|&(dist, _)| dist) + .map(|(_, suggestion)| suggestion) +} + #[cfg(test)] mod tests { use serde::de::IgnoredAny; @@ -1009,7 +1170,13 @@ mod tests { fn configs_are_tested() { let mut names: HashSet = crate::get_configuration_metadata() .into_iter() - .map(|meta| meta.name.replace('_', "-")) + .filter_map(|meta| { + if meta.deprecation_reason.is_none() { + Some(meta.name.replace('_', "-")) + } else { + None + } + }) .collect(); let toml_files = WalkDir::new("../tests") diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs index 5d6e8b875166c..67904b4fcdc85 100644 --- a/src/tools/clippy/clippy_config/src/lib.rs +++ b/src/tools/clippy/clippy_config/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(rustc_private, array_windows, let_chains)] +#![feature(rustc_private)] #![warn( trivial_casts, trivial_numeric_casts, @@ -12,7 +12,9 @@ rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic )] +#![deny(clippy::derive_deserialize_allowing_unknown)] +extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_middle; diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index c72291e9799f1..f64eefa0c232d 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -1,14 +1,18 @@ -use clippy_utils::def_path_def_ids; +use clippy_utils::paths::{PathNS, find_crates, lookup_path}; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diag}; +use rustc_hir::PrimTy; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_middle::ty::TyCtxt; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use serde::de::{self, Deserializer, Visitor}; use serde::{Deserialize, Serialize, ser}; use std::collections::HashMap; use std::fmt; #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] pub struct Rename { pub path: String, pub rename: String, @@ -21,6 +25,17 @@ pub struct DisallowedPath { path: String, reason: Option, replacement: Option, + /// Setting `allow_invalid` to true suppresses a warning if `path` does not refer to an existing + /// definition. + /// + /// This could be useful when conditional compilation is used, or when a clippy.toml file is + /// shared among multiple projects. + allow_invalid: bool, + /// The span of the `DisallowedPath`. + /// + /// Used for diagnostics. + #[serde(skip_serializing)] + span: Span, } impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath { @@ -36,6 +51,8 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath Deserialize<'de> for DisallowedPath, replacement: Option, + #[serde(rename = "allow-invalid")] + allow_invalid: Option, }, } @@ -58,7 +77,7 @@ impl DisallowedPath { &self.path } - pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) + use<'_, REPLACEMENT_ALLOWED> { + pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) { move |diag| { if let Some(replacement) = &self.replacement { diag.span_suggestion( @@ -72,6 +91,14 @@ impl DisallowedPath { } } } + + pub fn span(&self) -> Span { + self.span + } + + pub fn set_span(&mut self, span: Span) { + self.span = span; + } } impl DisallowedPathEnum { @@ -94,20 +121,78 @@ impl DisallowedPathEnum { Self::Simple(_) => None, } } + + fn allow_invalid(&self) -> bool { + match &self { + Self::WithReason { allow_invalid, .. } => allow_invalid.unwrap_or_default(), + Self::Simple(_) => false, + } + } } /// Creates a map of disallowed items to the reason they were disallowed. +#[allow(clippy::type_complexity)] pub fn create_disallowed_map( tcx: TyCtxt<'_>, - disallowed: &'static [DisallowedPath], -) -> DefIdMap<(&'static str, &'static DisallowedPath)> { - disallowed - .iter() - .map(|x| (x.path(), x.path().split("::").collect::>(), x)) - .flat_map(|(name, path, disallowed_path)| { - def_path_def_ids(tcx, &path).map(move |id| (id, (name, disallowed_path))) - }) - .collect() + disallowed_paths: &'static [DisallowedPath], + ns: PathNS, + def_kind_predicate: impl Fn(DefKind) -> bool, + predicate_description: &str, + allow_prim_tys: bool, +) -> ( + DefIdMap<(&'static str, &'static DisallowedPath)>, + FxHashMap)>, +) { + let mut def_ids: DefIdMap<(&'static str, &'static DisallowedPath)> = DefIdMap::default(); + let mut prim_tys: FxHashMap)> = + FxHashMap::default(); + for disallowed_path in disallowed_paths { + let path = disallowed_path.path(); + let sym_path: Vec = path.split("::").map(Symbol::intern).collect(); + let mut resolutions = lookup_path(tcx, ns, &sym_path); + resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id))); + + let (prim_ty, found_prim_ty) = if let &[name] = sym_path.as_slice() + && let Some(prim) = PrimTy::from_name(name) + { + (allow_prim_tys.then_some(prim), true) + } else { + (None, false) + }; + + if resolutions.is_empty() + && prim_ty.is_none() + && !disallowed_path.allow_invalid + // Don't warn about unloaded crates: + // https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221 + && (sym_path.len() < 2 || !find_crates(tcx, sym_path[0]).is_empty()) + { + // Relookup the path in an arbitrary namespace to get a good `expected, found` message + let found_def_ids = lookup_path(tcx, PathNS::Arbitrary, &sym_path); + let message = if let Some(&def_id) = found_def_ids.first() { + let (article, description) = tcx.article_and_description(def_id); + format!("expected a {predicate_description}, found {article} {description}") + } else if found_prim_ty { + format!("expected a {predicate_description}, found a primitive type") + } else { + format!("`{path}` does not refer to a reachable {predicate_description}") + }; + tcx.sess + .dcx() + .struct_span_warn(disallowed_path.span(), message) + .with_help("add `allow-invalid = true` to the entry to suppress this warning") + .emit(); + } + + for def_id in resolutions { + def_ids.insert(def_id, (path, disallowed_path)); + } + if let Some(ty) = prim_ty { + prim_tys.insert(ty, (path, disallowed_path)); + } + } + + (def_ids, prim_tys) } #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] @@ -305,6 +390,7 @@ impl SourceItemOrderingModuleItemKind { pub struct SourceItemOrderingModuleItemGroupings { groups: Vec<(String, Vec)>, lut: HashMap, + back_lut: HashMap, } impl SourceItemOrderingModuleItemGroupings { @@ -320,6 +406,30 @@ impl SourceItemOrderingModuleItemGroupings { lut } + fn build_back_lut( + groups: &[(String, Vec)], + ) -> HashMap { + let mut lut = HashMap::new(); + for (group_name, items) in groups { + for item in items { + lut.insert(item.clone(), group_name.clone()); + } + } + lut + } + + pub fn grouping_name_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option<&String> { + self.back_lut.get(item) + } + + pub fn grouping_names(&self) -> Vec { + self.groups.iter().map(|(name, _)| name.clone()).collect() + } + + pub fn is_grouping(&self, grouping: &str) -> bool { + self.groups.iter().any(|(g, _)| g == grouping) + } + pub fn module_level_order_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option { self.lut.get(item).copied() } @@ -330,7 +440,8 @@ impl From<&[(&str, &[SourceItemOrderingModuleItemKind])]> for SourceItemOrdering let groups: Vec<(String, Vec)> = value.iter().map(|item| (item.0.to_string(), item.1.to_vec())).collect(); let lut = Self::build_lut(&groups); - Self { groups, lut } + let back_lut = Self::build_back_lut(&groups); + Self { groups, lut, back_lut } } } @@ -348,6 +459,7 @@ impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings { let groups = Vec::<(String, Vec)>::deserialize(deserializer)?; let items_total: usize = groups.iter().map(|(_, v)| v.len()).sum(); let lut = Self::build_lut(&groups); + let back_lut = Self::build_back_lut(&groups); let mut expected_items = SourceItemOrderingModuleItemKind::all_variants(); for item in lut.keys() { @@ -370,7 +482,7 @@ impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings { )); } - Ok(Self { groups, lut }) + Ok(Self { groups, lut, back_lut }) } else if items_total != all_items.len() { Err(de::Error::custom(format!( "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \ @@ -482,6 +594,83 @@ impl Serialize for SourceItemOrderingTraitAssocItemKinds { } } +/// Describes which specific groupings should have their items ordered +/// alphabetically. +/// +/// This is separate from defining and enforcing groupings. For example, +/// defining enums are grouped before structs still allows for an enum B to be +/// placed before an enum A. Only when enforcing ordering within the grouping, +/// will it be checked if A is placed before B. +#[derive(Clone, Debug)] +pub enum SourceItemOrderingWithinModuleItemGroupings { + /// All groupings should have their items ordered. + All, + + /// None of the groupings should have their order checked. + None, + + /// Only the specified groupings should have their order checked. + Custom(Vec), +} + +impl SourceItemOrderingWithinModuleItemGroupings { + pub fn ordered_within(&self, grouping_name: &String) -> bool { + match self { + SourceItemOrderingWithinModuleItemGroupings::All => true, + SourceItemOrderingWithinModuleItemGroupings::None => false, + SourceItemOrderingWithinModuleItemGroupings::Custom(groups) => groups.contains(grouping_name), + } + } +} + +/// Helper struct for deserializing the [`SourceItemOrderingWithinModuleItemGroupings`]. +#[derive(Deserialize)] +#[serde(untagged)] +enum StringOrVecOfString { + String(String), + Vec(Vec), +} + +impl<'de> Deserialize<'de> for SourceItemOrderingWithinModuleItemGroupings { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let description = "The available options for configuring an ordering within module item groups are: \ + \"all\", \"none\", or a list of module item group names \ + (as configured with the `module-item-order-groupings` configuration option)."; + + match StringOrVecOfString::deserialize(deserializer) { + Ok(StringOrVecOfString::String(preset)) if preset == "all" => { + Ok(SourceItemOrderingWithinModuleItemGroupings::All) + }, + Ok(StringOrVecOfString::String(preset)) if preset == "none" => { + Ok(SourceItemOrderingWithinModuleItemGroupings::None) + }, + Ok(StringOrVecOfString::String(preset)) => Err(de::Error::custom(format!( + "Unknown configuration option: {preset}.\n{description}" + ))), + Ok(StringOrVecOfString::Vec(groupings)) => { + Ok(SourceItemOrderingWithinModuleItemGroupings::Custom(groupings)) + }, + Err(e) => Err(de::Error::custom(format!("{e}\n{description}"))), + } + } +} + +impl Serialize for SourceItemOrderingWithinModuleItemGroupings { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + match self { + SourceItemOrderingWithinModuleItemGroupings::All => serializer.serialize_str("all"), + SourceItemOrderingWithinModuleItemGroupings::None => serializer.serialize_str("none"), + SourceItemOrderingWithinModuleItemGroupings::Custom(vec) => vec.serialize(serializer), + } + } +} + // these impls are never actually called but are used by the various config options that default to // empty lists macro_rules! unimplemented_serialize { diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index 47b7b3758613c..10c08dba50b90 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -5,13 +5,11 @@ version = "0.0.1" edition = "2024" [dependencies] -aho-corasick = "1.0" chrono = { version = "0.4.38", default-features = false, features = ["clock"] } clap = { version = "4.4", features = ["derive"] } indoc = "1.0" itertools = "0.12" opener = "0.7" -shell-escape = "0.1" walkdir = "2.3" [package.metadata.rust-analyzer] diff --git a/src/tools/clippy/clippy_dev/src/deprecate_lint.rs b/src/tools/clippy/clippy_dev/src/deprecate_lint.rs new file mode 100644 index 0000000000000..3bdc5b2772327 --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/deprecate_lint.rs @@ -0,0 +1,161 @@ +use crate::update_lints::{DeprecatedLint, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints}; +use crate::utils::{UpdateMode, Version}; +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; +use std::{fs, io}; + +/// Runs the `deprecate` command +/// +/// This does the following: +/// * Adds an entry to `deprecated_lints.rs`. +/// * Removes the lint declaration (and the entire file if applicable) +/// +/// # Panics +/// +/// If a file path could not read from or written to +pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { + if let Some((prefix, _)) = name.split_once("::") { + panic!("`{name}` should not contain the `{prefix}` prefix"); + } + + let mut lints = find_lint_decls(); + let (mut deprecated_lints, renamed_lints) = read_deprecated_lints(); + + let Some(lint) = lints.iter().find(|l| l.name == name) else { + eprintln!("error: failed to find lint `{name}`"); + return; + }; + + let prefixed_name = String::from_iter(["clippy::", name]); + match deprecated_lints.binary_search_by(|x| x.name.cmp(&prefixed_name)) { + Ok(_) => { + println!("`{name}` is already deprecated"); + return; + }, + Err(idx) => deprecated_lints.insert( + idx, + DeprecatedLint { + name: prefixed_name, + reason: reason.into(), + version: clippy_version.rust_display().to_string(), + }, + ), + } + + let mod_path = { + let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); + if mod_path.is_dir() { + mod_path = mod_path.join("mod"); + } + + mod_path.set_extension("rs"); + mod_path + }; + + if remove_lint_declaration(name, &mod_path, &mut lints).unwrap_or(false) { + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("info: `{name}` has successfully been deprecated"); + println!("note: you must run `cargo uitest` to update the test results"); + } else { + eprintln!("error: lint not found"); + } +} + +fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io::Result { + fn remove_lint(name: &str, lints: &mut Vec) { + lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); + } + + fn remove_test_assets(name: &str) { + let test_file_stem = format!("tests/ui/{name}"); + let path = Path::new(&test_file_stem); + + // Some lints have their own directories, delete them + if path.is_dir() { + let _ = fs::remove_dir_all(path); + return; + } + + // Remove all related test files + let _ = fs::remove_file(path.with_extension("rs")); + let _ = fs::remove_file(path.with_extension("stderr")); + let _ = fs::remove_file(path.with_extension("fixed")); + } + + fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { + let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| { + content + .find("declare_lint_pass!") + .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`")) + }); + let mut impl_lint_pass_end = content[impl_lint_pass_start..] + .find(']') + .expect("failed to find `impl_lint_pass` terminator"); + + impl_lint_pass_end += impl_lint_pass_start; + if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) { + let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len()); + for c in content[lint_name_end..impl_lint_pass_end].chars() { + // Remove trailing whitespace + if c == ',' || c.is_whitespace() { + lint_name_end += 1; + } else { + break; + } + } + + content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, ""); + } + } + + if path.exists() + && let Some(lint) = lints.iter().find(|l| l.name == name) + { + if lint.module == name { + // The lint name is the same as the file, we can just delete the entire file + fs::remove_file(path)?; + } else { + // We can't delete the entire file, just remove the declaration + + if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { + // Remove clippy_lints/src/some_mod/some_lint.rs + let mut lint_mod_path = path.to_path_buf(); + lint_mod_path.set_file_name(name); + lint_mod_path.set_extension("rs"); + + let _ = fs::remove_file(lint_mod_path); + } + + let mut content = + fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); + + eprintln!( + "warn: you will have to manually remove any code related to `{name}` from `{}`", + path.display() + ); + + assert!( + content[lint.declaration_range.clone()].contains(&name.to_uppercase()), + "error: `{}` does not contain lint `{}`'s declaration", + path.display(), + lint.name + ); + + // Remove lint declaration (declare_clippy_lint!) + content.replace_range(lint.declaration_range.clone(), ""); + + // Remove the module declaration (mod xyz;) + let mod_decl = format!("\nmod {name};"); + content = content.replacen(&mod_decl, "", 1); + + remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); + fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); + } + + remove_test_assets(name); + remove_lint(name, lints); + return Ok(true); + } + + Ok(false) +} diff --git a/src/tools/clippy/clippy_dev/src/dogfood.rs b/src/tools/clippy/clippy_dev/src/dogfood.rs index 05fa24d8d4ee5..7e9d92458d057 100644 --- a/src/tools/clippy/clippy_dev/src/dogfood.rs +++ b/src/tools/clippy/clippy_dev/src/dogfood.rs @@ -1,4 +1,4 @@ -use crate::utils::{clippy_project_root, exit_if_err}; +use crate::utils::exit_if_err; use std::process::Command; /// # Panics @@ -8,8 +8,7 @@ use std::process::Command; pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) { let mut cmd = Command::new("cargo"); - cmd.current_dir(clippy_project_root()) - .args(["test", "--test", "dogfood"]) + cmd.args(["test", "--test", "dogfood"]) .args(["--features", "internal"]) .args(["--", "dogfood_clippy", "--nocapture"]); diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index bdddf46a2cb19..13d6b1285dcdb 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -1,20 +1,18 @@ -use crate::utils::clippy_project_root; +use crate::utils::{ + ClippyInfo, ErrAction, FileUpdater, UpdateMode, UpdateStatus, panic_action, run_with_args_split, run_with_output, +}; use itertools::Itertools; use rustc_lexer::{TokenKind, tokenize}; -use shell_escape::escape; -use std::ffi::{OsStr, OsString}; +use std::fmt::Write; +use std::fs; +use std::io::{self, Read}; use std::ops::ControlFlow; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::{self, Command, Stdio}; -use std::{fs, io}; use walkdir::WalkDir; pub enum Error { - CommandFailed(String, String), Io(io::Error), - RustfmtNotInstalled, - WalkDir(walkdir::Error), - IntellijSetupActive, Parse(PathBuf, usize, String), CheckFailed, } @@ -25,37 +23,15 @@ impl From for Error { } } -impl From for Error { - fn from(error: walkdir::Error) -> Self { - Self::WalkDir(error) - } -} - impl Error { fn display(&self) { match self { Self::CheckFailed => { eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update."); }, - Self::CommandFailed(command, stderr) => { - eprintln!("error: command `{command}` failed!\nstderr: {stderr}"); - }, Self::Io(err) => { eprintln!("error: {err}"); }, - Self::RustfmtNotInstalled => { - eprintln!("error: rustfmt nightly is not installed."); - }, - Self::WalkDir(err) => { - eprintln!("error: {err}"); - }, - Self::IntellijSetupActive => { - eprintln!( - "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\ - Not formatting because that would format the local repo as well!\n\ - Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`." - ); - }, Self::Parse(path, line, msg) => { eprintln!("error parsing `{}:{line}`: {msg}", path.display()); }, @@ -63,12 +39,6 @@ impl Error { } } -struct FmtContext { - check: bool, - verbose: bool, - rustfmt_path: String, -} - struct ClippyConf<'a> { name: &'a str, attrs: &'a str, @@ -104,15 +74,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { Field, } - let path: PathBuf = [ - clippy_project_root().as_path(), - "clippy_config".as_ref(), - "src".as_ref(), - "conf.rs".as_ref(), - ] - .into_iter() - .collect(); - let text = fs::read_to_string(&path)?; + let path = "clippy_config/src/conf.rs"; + let text = fs::read_to_string(path)?; let (pre, conf) = text .split_once("define_Conf! {\n") @@ -203,7 +166,7 @@ fn fmt_conf(check: bool) -> Result<(), Error> { | (State::Lints, TokenKind::Comma | TokenKind::OpenParen | TokenKind::CloseParen) => {}, _ => { return Err(Error::Parse( - path, + PathBuf::from(path), offset_to_line(&text, conf_offset + i), format!("unexpected token `{}`", &conf[i..i + t.len as usize]), )); @@ -213,7 +176,7 @@ fn fmt_conf(check: bool) -> Result<(), Error> { if !matches!(state, State::Field) { return Err(Error::Parse( - path, + PathBuf::from(path), offset_to_line(&text, conf_offset + conf.len()), "incomplete field".into(), )); @@ -260,162 +223,158 @@ fn fmt_conf(check: bool) -> Result<(), Error> { if check { return Err(Error::CheckFailed); } - fs::write(&path, new_text.as_bytes())?; + fs::write(path, new_text.as_bytes())?; } Ok(()) } -fn run_rustfmt(context: &FmtContext) -> Result<(), Error> { - let project_root = clippy_project_root(); - - // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to - // format because rustfmt would also format the entire rustc repo as it is a local - // dependency - if fs::read_to_string(project_root.join("Cargo.toml")) - .expect("Failed to read clippy Cargo.toml") - .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") - { - return Err(Error::IntellijSetupActive); - } - - check_for_rustfmt(context)?; - - cargo_fmt(context, project_root.as_path())?; - cargo_fmt(context, &project_root.join("clippy_dev"))?; - cargo_fmt(context, &project_root.join("rustc_tools_util"))?; - cargo_fmt(context, &project_root.join("lintcheck"))?; - - let chunks = WalkDir::new(project_root.join("tests")) - .into_iter() - .filter_map(|entry| { - let entry = entry.expect("failed to find tests"); - let path = entry.path(); - if path.extension() != Some("rs".as_ref()) - || path - .components() - .nth_back(1) - .is_some_and(|c| c.as_os_str() == "syntax-error-recovery") - || entry.file_name() == "ice-3891.rs" - { - None +/// Format the symbols list +fn fmt_syms(update_mode: UpdateMode) { + FileUpdater::default().update_file_checked( + "cargo dev fmt", + update_mode, + "clippy_utils/src/sym.rs", + &mut |_, text: &str, new_text: &mut String| { + let (pre, conf) = text.split_once("generate! {\n").expect("can't find generate! call"); + let (conf, post) = conf.split_once("\n}\n").expect("can't find end of generate! call"); + let mut lines = conf + .lines() + .map(|line| { + let line = line.trim(); + line.strip_suffix(',').unwrap_or(line).trim_end() + }) + .collect::>(); + lines.sort_unstable(); + write!( + new_text, + "{pre}generate! {{\n {},\n}}\n{post}", + lines.join(",\n "), + ) + .unwrap(); + if text == new_text { + UpdateStatus::Unchanged } else { - Some(entry.into_path().into_os_string()) + UpdateStatus::Changed } - }) - .chunks(250); - - for chunk in &chunks { - rustfmt(context, chunk)?; - } - Ok(()) + }, + ); } -// the "main" function of cargo dev fmt -pub fn run(check: bool, verbose: bool) { - let output = Command::new("rustup") - .args(["which", "rustfmt"]) - .stderr(Stdio::inherit()) - .output() - .expect("error running `rustup which rustfmt`"); - if !output.status.success() { - eprintln!("`rustup which rustfmt` did not execute successfully"); - process::exit(1); - } - let mut rustfmt_path = String::from_utf8(output.stdout).expect("invalid rustfmt path"); +fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) { + let mut rustfmt_path = String::from_utf8(run_with_output( + "rustup which rustfmt", + Command::new("rustup").args(["which", "rustfmt"]), + )) + .expect("invalid rustfmt path"); rustfmt_path.truncate(rustfmt_path.trim_end().len()); - let context = FmtContext { - check, - verbose, - rustfmt_path, - }; - if let Err(e) = run_rustfmt(&context).and_then(|()| fmt_conf(check)) { - e.display(); - process::exit(1); - } -} - -fn format_command(program: impl AsRef, dir: impl AsRef, args: &[impl AsRef]) -> String { - let arg_display: Vec<_> = args.iter().map(|a| escape(a.as_ref().to_string_lossy())).collect(); - - format!( - "cd {} && {} {}", - escape(dir.as_ref().to_string_lossy()), - escape(program.as_ref().to_string_lossy()), - arg_display.join(" ") - ) -} - -fn exec_fmt_command( - context: &FmtContext, - program: impl AsRef, - dir: impl AsRef, - args: &[impl AsRef], -) -> Result<(), Error> { - if context.verbose { - println!("{}", format_command(&program, &dir, args)); + let mut cargo_path = String::from_utf8(run_with_output( + "rustup which cargo", + Command::new("rustup").args(["which", "cargo"]), + )) + .expect("invalid cargo path"); + cargo_path.truncate(cargo_path.trim_end().len()); + + // Start all format jobs first before waiting on the results. + let mut children = Vec::with_capacity(16); + for &path in &[ + ".", + "clippy_config", + "clippy_dev", + "clippy_lints", + "clippy_lints_internal", + "clippy_utils", + "rustc_tools_util", + "lintcheck", + ] { + let mut cmd = Command::new(&cargo_path); + cmd.current_dir(clippy.path.join(path)) + .args(["fmt"]) + .env("RUSTFMT", &rustfmt_path) + .stdout(Stdio::null()) + .stdin(Stdio::null()) + .stderr(Stdio::piped()); + if update_mode.is_check() { + cmd.arg("--check"); + } + match cmd.spawn() { + Ok(x) => children.push(("cargo fmt", x)), + Err(ref e) => panic_action(&e, ErrAction::Run, "cargo fmt".as_ref()), + } } - let output = Command::new(&program) - .env("RUSTFMT", &context.rustfmt_path) - .current_dir(&dir) - .args(args.iter()) - .output() - .unwrap(); - let success = output.status.success(); - - match (context.check, success) { - (_, true) => Ok(()), - (true, false) => Err(Error::CheckFailed), - (false, false) => { - let stderr = std::str::from_utf8(&output.stderr).unwrap_or(""); - Err(Error::CommandFailed( - format_command(&program, &dir, args), - String::from(stderr), - )) + run_with_args_split( + || { + let mut cmd = Command::new(&rustfmt_path); + if update_mode.is_check() { + cmd.arg("--check"); + } + cmd.stdout(Stdio::null()) + .stdin(Stdio::null()) + .stderr(Stdio::piped()) + .args(["--config", "show_parse_errors=false"]); + cmd }, - } -} + |cmd| match cmd.spawn() { + Ok(x) => children.push(("rustfmt", x)), + Err(ref e) => panic_action(&e, ErrAction::Run, "rustfmt".as_ref()), + }, + WalkDir::new("tests") + .into_iter() + .filter_entry(|p| p.path().file_name().is_none_or(|x| x != "skip_rustfmt")) + .filter_map(|e| { + let e = e.expect("error reading `tests`"); + e.path() + .as_os_str() + .as_encoded_bytes() + .ends_with(b".rs") + .then(|| e.into_path().into_os_string()) + }), + ); -fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<(), Error> { - let mut args = vec!["fmt", "--all"]; - if context.check { - args.push("--check"); + for (name, child) in &mut children { + match child.wait() { + Ok(status) => match (update_mode, status.exit_ok()) { + (UpdateMode::Check | UpdateMode::Change, Ok(())) => {}, + (UpdateMode::Check, Err(_)) => { + let mut s = String::new(); + if let Some(mut stderr) = child.stderr.take() + && stderr.read_to_string(&mut s).is_ok() + { + eprintln!("{s}"); + } + eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update."); + process::exit(1); + }, + (UpdateMode::Change, Err(e)) => { + let mut s = String::new(); + if let Some(mut stderr) = child.stderr.take() + && stderr.read_to_string(&mut s).is_ok() + { + eprintln!("{s}"); + } + panic_action(&e, ErrAction::Run, name.as_ref()); + }, + }, + Err(ref e) => panic_action(e, ErrAction::Run, name.as_ref()), + } } - exec_fmt_command(context, "cargo", path, &args) } -fn check_for_rustfmt(context: &FmtContext) -> Result<(), Error> { - let program = "rustfmt"; - let dir = std::env::current_dir()?; - let args = &["--version"]; - - if context.verbose { - println!("{}", format_command(program, &dir, args)); - } - - let output = Command::new(program).current_dir(&dir).args(args.iter()).output()?; - - if output.status.success() { - Ok(()) - } else if std::str::from_utf8(&output.stderr) - .unwrap_or("") - .starts_with("error: 'rustfmt' is not installed") - { - Err(Error::RustfmtNotInstalled) - } else { - Err(Error::CommandFailed( - format_command(program, &dir, args), - std::str::from_utf8(&output.stderr).unwrap_or("").to_string(), - )) +// the "main" function of cargo dev fmt +pub fn run(clippy: &ClippyInfo, update_mode: UpdateMode) { + if clippy.has_intellij_hook { + eprintln!( + "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\ + Not formatting because that would format the local repo as well!\n\ + Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`." + ); + return; } -} - -fn rustfmt(context: &FmtContext, paths: impl Iterator) -> Result<(), Error> { - let mut args = Vec::new(); - if context.check { - args.push(OsString::from("--check")); + run_rustfmt(clippy, update_mode); + fmt_syms(update_mode); + if let Err(e) = fmt_conf(update_mode.is_check()) { + e.display(); + process::exit(1); } - args.extend(paths); - exec_fmt_command(context, &context.rustfmt_path, std::env::current_dir()?, &args) } diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 9280369c23b8f..3361443196ab5 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,5 +1,12 @@ -#![feature(let_chains)] -#![feature(rustc_private)] +#![feature( + rustc_private, + exit_status_error, + if_let_guard, + let_chains, + os_str_slice, + os_string_truncate, + slice_split_once +)] #![warn( trivial_casts, trivial_numeric_casts, @@ -13,12 +20,15 @@ #[allow(unused_extern_crates)] extern crate rustc_driver; extern crate rustc_lexer; +extern crate rustc_literal_escaper; +pub mod deprecate_lint; pub mod dogfood; pub mod fmt; pub mod lint; pub mod new_lint; pub mod release; +pub mod rename_lint; pub mod serve; pub mod setup; pub mod sync; diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 074dea4ab77b6..ebcd8611d78cd 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -3,11 +3,18 @@ #![warn(rust_2018_idioms, unused_lifetimes)] use clap::{Args, Parser, Subcommand}; -use clippy_dev::{dogfood, fmt, lint, new_lint, release, serve, setup, sync, update_lints, utils}; +use clippy_dev::{ + deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync, update_lints, utils, +}; use std::convert::Infallible; +use std::env; fn main() { let dev = Dev::parse(); + let clippy = utils::ClippyInfo::search_for_manifest(); + if let Err(e) = env::set_current_dir(&clippy.path) { + panic!("error setting current directory to `{}`: {e}", clippy.path.display()); + } match dev.command { DevCommand::Bless => { @@ -19,23 +26,15 @@ fn main() { allow_staged, allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), - DevCommand::Fmt { check, verbose } => fmt::run(check, verbose), - DevCommand::UpdateLints { print_only, check } => { - if print_only { - update_lints::print_lints(); - } else if check { - update_lints::update(utils::UpdateMode::Check); - } else { - update_lints::update(utils::UpdateMode::Change); - } - }, + DevCommand::Fmt { check } => fmt::run(&clippy, utils::UpdateMode::from_check(check)), + DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)), DevCommand::NewLint { pass, name, category, r#type, msrv, - } => match new_lint::create(pass, &name, &category, r#type.as_deref(), msrv) { + } => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) { Ok(()) => update_lints::update(utils::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {e}"), }, @@ -79,13 +78,18 @@ fn main() { old_name, new_name, uplift, - } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift), - DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason), + } => rename_lint::rename( + clippy.version, + &old_name, + new_name.as_ref().unwrap_or(&old_name), + uplift, + ), + DevCommand::Deprecate { name, reason } => deprecate_lint::deprecate(clippy.version, &name, &reason), DevCommand::Sync(SyncCommand { subcommand }) => match subcommand { SyncSubcommand::UpdateNightly => sync::update_nightly(), }, DevCommand::Release(ReleaseCommand { subcommand }) => match subcommand { - ReleaseSubcommand::BumpVersion => release::bump_version(), + ReleaseSubcommand::BumpVersion => release::bump_version(clippy.version), }, } } @@ -121,9 +125,6 @@ enum DevCommand { #[arg(long)] /// Use the rustfmt --check option check: bool, - #[arg(short, long)] - /// Echo commands run - verbose: bool, }, #[command(name = "update_lints")] /// Updates lint registration and information from the source code @@ -135,11 +136,6 @@ enum DevCommand { /// * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod` {n} /// * all lints are registered in the lint store UpdateLints { - #[arg(long)] - /// Print a table of lints to STDOUT - /// - /// This does not include deprecated and internal lints. (Does not modify any files) - print_only: bool, #[arg(long)] /// Checks that `cargo dev update_lints` has been run. Used on CI. check: bool, @@ -170,7 +166,6 @@ enum DevCommand { "restriction", "cargo", "nursery", - "internal", ], default_value = "nursery", )] @@ -334,7 +329,7 @@ struct SyncCommand { #[derive(Subcommand)] enum SyncSubcommand { #[command(name = "update_nightly")] - /// Update nightly version in rust-toolchain and `clippy_utils` + /// Update nightly version in `rust-toolchain.toml` and `clippy_utils` UpdateNightly, } diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 96e12706c9e24..4121daa85e6d7 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -1,4 +1,4 @@ -use crate::utils::{clippy_project_root, clippy_version}; +use crate::utils::{RustSearcher, Token, Version}; use clap::ValueEnum; use indoc::{formatdoc, writedoc}; use std::fmt::{self, Write as _}; @@ -22,11 +22,11 @@ impl fmt::Display for Pass { } struct LintData<'a> { + clippy_version: Version, pass: Pass, name: &'a str, category: &'a str, ty: Option<&'a str>, - project_root: PathBuf, } trait Context { @@ -50,18 +50,25 @@ impl Context for io::Result { /// # Errors /// /// This function errors out if the files couldn't be created or written to. -pub fn create(pass: Pass, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> { +pub fn create( + clippy_version: Version, + pass: Pass, + name: &str, + category: &str, + mut ty: Option<&str>, + msrv: bool, +) -> io::Result<()> { if category == "cargo" && ty.is_none() { // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo` ty = Some("cargo"); } let lint = LintData { + clippy_version, pass, name, category, ty, - project_root: clippy_project_root(), }; create_lint(&lint, msrv).context("Unable to create lint implementation")?; @@ -88,7 +95,7 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { } else { let lint_contents = get_lint_file_contents(lint, enable_msrv); let lint_path = format!("clippy_lints/src/{}.rs", lint.name); - write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?; + write_file(&lint_path, lint_contents.as_bytes())?; println!("Generated lint file: `{lint_path}`"); Ok(()) @@ -115,8 +122,7 @@ fn create_test(lint: &LintData<'_>, msrv: bool) -> io::Result<()> { } if lint.category == "cargo" { - let relative_test_dir = format!("tests/ui-cargo/{}", lint.name); - let test_dir = lint.project_root.join(&relative_test_dir); + let test_dir = format!("tests/ui-cargo/{}", lint.name); fs::create_dir(&test_dir)?; create_project_layout( @@ -134,11 +140,11 @@ fn create_test(lint: &LintData<'_>, msrv: bool) -> io::Result<()> { false, )?; - println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`"); + println!("Generated test directories: `{test_dir}/pass`, `{test_dir}/fail`"); } else { let test_path = format!("tests/ui/{}.rs", lint.name); let test_contents = get_test_file_contents(lint.name, msrv); - write_file(lint.project_root.join(&test_path), test_contents)?; + write_file(&test_path, test_contents)?; println!("Generated test file: `{test_path}`"); } @@ -193,11 +199,6 @@ fn to_camel_case(name: &str) -> String { .collect() } -pub(crate) fn get_stabilization_version() -> String { - let (minor, patch) = clippy_version(); - format!("{minor}.{patch}.0") -} - fn get_test_file_contents(lint_name: &str, msrv: bool) -> String { let mut test = formatdoc!( r" @@ -292,7 +293,11 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { ); } - let _: fmt::Result = writeln!(result, "{}", get_lint_declaration(&name_upper, category)); + let _: fmt::Result = writeln!( + result, + "{}", + get_lint_declaration(lint.clippy_version, &name_upper, category) + ); if enable_msrv { let _: fmt::Result = writedoc!( @@ -330,7 +335,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result } -fn get_lint_declaration(name_upper: &str, category: &str) -> String { +fn get_lint_declaration(version: Version, name_upper: &str, category: &str) -> String { let justification_heading = if category == "restriction" { "Why restrict this?" } else { @@ -355,9 +360,8 @@ fn get_lint_declaration(name_upper: &str, category: &str) -> String { pub {name_upper}, {category}, "default lint description" - }} - "#, - get_stabilization_version(), + }}"#, + version.rust_display(), ) } @@ -371,7 +375,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R _ => {}, } - let ty_dir = lint.project_root.join(format!("clippy_lints/src/{ty}")); + let ty_dir = PathBuf::from(format!("clippy_lints/src/{ty}")); assert!( ty_dir.exists() && ty_dir.is_dir(), "Directory `{}` does not exist!", @@ -441,95 +445,25 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R #[allow(clippy::too_many_lines)] fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> { - use super::update_lints::{LintDeclSearchResult, match_tokens}; - use rustc_lexer::TokenKind; - let lint_name_upper = lint.name.to_uppercase(); let mut file_contents = fs::read_to_string(path)?; assert!( - !file_contents.contains(&lint_name_upper), + !file_contents.contains(&format!("pub {lint_name_upper},")), "Lint `{}` already defined in `{}`", lint.name, path.display() ); - let mut offset = 0usize; - let mut last_decl_curly_offset = None; - let mut lint_context = None; - - let mut iter = rustc_lexer::tokenize(&file_contents).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &file_contents[range.clone()], - range, - } - }); - - // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl - while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) { - let mut iter = iter - .by_ref() - .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); - - match content { - "declare_clippy_lint" => { - // matches `!{` - match_tokens!(iter, Bang OpenBrace); - if let Some(LintDeclSearchResult { range, .. }) = - iter.find(|result| result.token_kind == TokenKind::CloseBrace) - { - last_decl_curly_offset = Some(range.end); - } - }, - "impl" => { - let mut token = iter.next(); - match token { - // matches <'foo> - Some(LintDeclSearchResult { - token_kind: TokenKind::Lt, - .. - }) => { - match_tokens!(iter, Lifetime { .. } Gt); - token = iter.next(); - }, - None => break, - _ => {}, - } - - if let Some(LintDeclSearchResult { - token_kind: TokenKind::Ident, - content, - .. - }) = token - { - // Get the appropriate lint context struct - lint_context = match content { - "LateLintPass" => Some("LateContext"), - "EarlyLintPass" => Some("EarlyContext"), - _ => continue, - }; - } - }, - _ => {}, - } - } - - drop(iter); - - let last_decl_curly_offset = - last_decl_curly_offset.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display())); - let lint_context = - lint_context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display())); + let (lint_context, lint_decl_end) = parse_mod_file(path, &file_contents); // Add the lint declaration to `mod.rs` - file_contents.replace_range( - // Remove the trailing newline, which should always be present - last_decl_curly_offset..=last_decl_curly_offset, - &format!("\n\n{}", get_lint_declaration(&lint_name_upper, lint.category)), + file_contents.insert_str( + lint_decl_end, + &format!( + "\n\n{}", + get_lint_declaration(lint.clippy_version, &lint_name_upper, lint.category) + ), ); // Add the lint to `impl_lint_pass`/`declare_lint_pass` @@ -580,6 +514,41 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> Ok(lint_context) } +// Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl +fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) { + #[allow(clippy::enum_glob_use)] + use Token::*; + + let mut context = None; + let mut decl_end = None; + let mut searcher = RustSearcher::new(contents); + while let Some(name) = searcher.find_capture_token(CaptureIdent) { + match name { + "declare_clippy_lint" => { + if searcher.match_tokens(&[Bang, OpenBrace], &mut []) && searcher.find_token(CloseBrace) { + decl_end = Some(searcher.pos()); + } + }, + "impl" => { + let mut capture = ""; + if searcher.match_tokens(&[Lt, Lifetime, Gt, CaptureIdent], &mut [&mut capture]) { + match capture { + "LateLintPass" => context = Some("LateContext"), + "EarlyLintPass" => context = Some("EarlyContext"), + _ => {}, + } + } + }, + _ => {}, + } + } + + ( + context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display())), + decl_end.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display())) as usize, + ) +} + #[test] fn test_camel_case() { let s = "a_lint"; diff --git a/src/tools/clippy/clippy_dev/src/release.rs b/src/tools/clippy/clippy_dev/src/release.rs index ac75516870103..62c1bee81850e 100644 --- a/src/tools/clippy/clippy_dev/src/release.rs +++ b/src/tools/clippy/clippy_dev/src/release.rs @@ -1,27 +1,29 @@ +use crate::utils::{FileUpdater, UpdateStatus, Version, parse_cargo_package}; use std::fmt::Write; -use std::path::Path; -use crate::utils::{UpdateMode, clippy_version, replace_region_in_file}; - -const CARGO_TOML_FILES: [&str; 4] = [ +static CARGO_TOML_FILES: &[&str] = &[ "clippy_config/Cargo.toml", "clippy_lints/Cargo.toml", "clippy_utils/Cargo.toml", "Cargo.toml", ]; -pub fn bump_version() { - let (minor, mut patch) = clippy_version(); - patch += 1; - for file in &CARGO_TOML_FILES { - replace_region_in_file( - UpdateMode::Change, - Path::new(file), - "# begin autogenerated version\n", - "# end autogenerated version", - |res| { - writeln!(res, "version = \"0.{minor}.{patch}\"").unwrap(); - }, - ); +pub fn bump_version(mut version: Version) { + version.minor += 1; + + let mut updater = FileUpdater::default(); + for file in CARGO_TOML_FILES { + updater.update_file(file, &mut |_, src, dst| { + let package = parse_cargo_package(src); + if package.version_range.is_empty() { + dst.push_str(src); + UpdateStatus::Unchanged + } else { + dst.push_str(&src[..package.version_range.start]); + write!(dst, "\"{}\"", version.toml_display()).unwrap(); + dst.push_str(&src[package.version_range.end..]); + UpdateStatus::from_changed(src.get(package.version_range.clone()) != dst.get(package.version_range)) + } + }); } } diff --git a/src/tools/clippy/clippy_dev/src/rename_lint.rs b/src/tools/clippy/clippy_dev/src/rename_lint.rs new file mode 100644 index 0000000000000..be8b27c7a9e9f --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/rename_lint.rs @@ -0,0 +1,403 @@ +use crate::update_lints::{RenamedLint, find_lint_decls, generate_lint_files, read_deprecated_lints}; +use crate::utils::{ + FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, + try_rename_dir, try_rename_file, +}; +use rustc_lexer::TokenKind; +use std::ffi::OsString; +use std::fs; +use std::path::Path; +use walkdir::WalkDir; + +/// Runs the `rename_lint` command. +/// +/// This does the following: +/// * Adds an entry to `renamed_lints.rs`. +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). +/// * Renames the lint struct to the new name. +/// * Renames the module containing the lint struct to the new name if it shares a name with the +/// lint. +/// +/// # Panics +/// Panics for the following conditions: +/// * If a file path could not read from or then written to +/// * If either lint name has a prefix +/// * If `old_name` doesn't name an existing lint. +/// * If `old_name` names a deprecated or renamed lint. +#[expect(clippy::too_many_lines)] +pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: bool) { + if let Some((prefix, _)) = old_name.split_once("::") { + panic!("`{old_name}` should not contain the `{prefix}` prefix"); + } + if let Some((prefix, _)) = new_name.split_once("::") { + panic!("`{new_name}` should not contain the `{prefix}` prefix"); + } + + let mut updater = FileUpdater::default(); + let mut lints = find_lint_decls(); + let (deprecated_lints, mut renamed_lints) = read_deprecated_lints(); + + let Ok(lint_idx) = lints.binary_search_by(|x| x.name.as_str().cmp(old_name)) else { + panic!("could not find lint `{old_name}`"); + }; + let lint = &lints[lint_idx]; + + let old_name_prefixed = String::from_iter(["clippy::", old_name]); + let new_name_prefixed = if uplift { + new_name.to_owned() + } else { + String::from_iter(["clippy::", new_name]) + }; + + for lint in &mut renamed_lints { + if lint.new_name == old_name_prefixed { + lint.new_name.clone_from(&new_name_prefixed); + } + } + match renamed_lints.binary_search_by(|x| x.old_name.cmp(&old_name_prefixed)) { + Ok(_) => { + println!("`{old_name}` already has a rename registered"); + return; + }, + Err(idx) => { + renamed_lints.insert( + idx, + RenamedLint { + old_name: old_name_prefixed, + new_name: if uplift { + new_name.to_owned() + } else { + String::from_iter(["clippy::", new_name]) + }, + version: clippy_version.rust_display().to_string(), + }, + ); + }, + } + + // Some tests are named `lint_name_suffix` which should also be renamed, + // but we can't do that if the renamed lint's name overlaps with another + // lint. e.g. renaming 'foo' to 'bar' when a lint 'foo_bar' also exists. + let change_prefixed_tests = lints.get(lint_idx + 1).is_none_or(|l| !l.name.starts_with(old_name)); + + let mut mod_edit = ModEdit::None; + if uplift { + let is_unique_mod = lints[..lint_idx].iter().any(|l| l.module == lint.module) + || lints[lint_idx + 1..].iter().any(|l| l.module == lint.module); + if is_unique_mod { + if delete_file_if_exists(lint.path.as_ref()) { + mod_edit = ModEdit::Delete; + } + } else { + updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus { + let mut start = &src[..lint.declaration_range.start]; + if start.ends_with("\n\n") { + start = &start[..start.len() - 1]; + } + let mut end = &src[lint.declaration_range.end..]; + if end.starts_with("\n\n") { + end = &end[1..]; + } + dst.push_str(start); + dst.push_str(end); + UpdateStatus::Changed + }); + } + delete_test_files(old_name, change_prefixed_tests); + lints.remove(lint_idx); + } else if lints.binary_search_by(|x| x.name.as_str().cmp(new_name)).is_err() { + let lint = &mut lints[lint_idx]; + if lint.module.ends_with(old_name) + && lint + .path + .file_stem() + .is_some_and(|x| x.as_encoded_bytes() == old_name.as_bytes()) + { + let mut new_path = lint.path.with_file_name(new_name).into_os_string(); + new_path.push(".rs"); + if try_rename_file(lint.path.as_ref(), new_path.as_ref()) { + mod_edit = ModEdit::Rename; + } + + let mod_len = lint.module.len(); + lint.module.truncate(mod_len - old_name.len()); + lint.module.push_str(new_name); + } + rename_test_files(old_name, new_name, change_prefixed_tests); + new_name.clone_into(&mut lints[lint_idx].name); + lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + } else { + println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("Since `{new_name}` already exists the existing code has not been changed"); + return; + } + + let mut update_fn = file_update_fn(old_name, new_name, mod_edit); + for file in WalkDir::new(".").into_iter().filter_entry(|e| { + // Skip traversing some of the larger directories. + e.path() + .as_os_str() + .as_encoded_bytes() + .get(2..) + .is_none_or(|x| x != "target".as_bytes() && x != ".git".as_bytes()) + }) { + let file = file.expect("error reading clippy directory"); + if file.path().as_os_str().as_encoded_bytes().ends_with(b".rs") { + updater.update_file(file.path(), &mut update_fn); + } + } + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + + if uplift { + println!("Uplifted `clippy::{old_name}` as `{new_name}`"); + if matches!(mod_edit, ModEdit::None) { + println!("Only the rename has been registered, the code will need to be edited manually"); + } else { + println!("All the lint's code has been deleted"); + println!("Make sure to inspect the results as some things may have been missed"); + } + } else { + println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("All code referencing the old name has been updated"); + println!("Make sure to inspect the results as some things may have been missed"); + } + println!("note: `cargo uibless` still needs to be run to update the test results"); +} + +#[derive(Clone, Copy)] +enum ModEdit { + None, + Delete, + Rename, +} + +fn collect_ui_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { + for e in fs::read_dir("tests/ui").expect("error reading `tests/ui`") { + let e = e.expect("error reading `tests/ui`"); + let name = e.file_name(); + if let Some((name_only, _)) = name.as_encoded_bytes().split_once(|&x| x == b'.') { + if name_only.starts_with(lint.as_bytes()) && (rename_prefixed || name_only.len() == lint.len()) { + dst.push((name, true)); + } + } else if name.as_encoded_bytes().starts_with(lint.as_bytes()) && (rename_prefixed || name.len() == lint.len()) + { + dst.push((name, false)); + } + } +} + +fn collect_ui_toml_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { + if rename_prefixed { + for e in fs::read_dir("tests/ui-toml").expect("error reading `tests/ui-toml`") { + let e = e.expect("error reading `tests/ui-toml`"); + let name = e.file_name(); + if name.as_encoded_bytes().starts_with(lint.as_bytes()) && e.file_type().is_ok_and(|ty| ty.is_dir()) { + dst.push((name, false)); + } + } + } else { + dst.push((lint.into(), false)); + } +} + +/// Renames all test files for the given lint. +/// +/// If `rename_prefixed` is `true` this will also rename tests which have the lint name as a prefix. +fn rename_test_files(old_name: &str, new_name: &str, rename_prefixed: bool) { + let mut tests = Vec::new(); + + let mut old_buf = OsString::from("tests/ui/"); + let mut new_buf = OsString::from("tests/ui/"); + collect_ui_test_names(old_name, rename_prefixed, &mut tests); + for &(ref name, is_file) in &tests { + old_buf.push(name); + new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); + if is_file { + try_rename_file(old_buf.as_ref(), new_buf.as_ref()); + } else { + try_rename_dir(old_buf.as_ref(), new_buf.as_ref()); + } + old_buf.truncate("tests/ui/".len()); + new_buf.truncate("tests/ui/".len()); + } + + tests.clear(); + old_buf.truncate("tests/ui".len()); + new_buf.truncate("tests/ui".len()); + old_buf.push("-toml/"); + new_buf.push("-toml/"); + collect_ui_toml_test_names(old_name, rename_prefixed, &mut tests); + for (name, _) in &tests { + old_buf.push(name); + new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); + try_rename_dir(old_buf.as_ref(), new_buf.as_ref()); + old_buf.truncate("tests/ui/".len()); + new_buf.truncate("tests/ui/".len()); + } +} + +fn delete_test_files(lint: &str, rename_prefixed: bool) { + let mut tests = Vec::new(); + + let mut buf = OsString::from("tests/ui/"); + collect_ui_test_names(lint, rename_prefixed, &mut tests); + for &(ref name, is_file) in &tests { + buf.push(name); + if is_file { + delete_file_if_exists(buf.as_ref()); + } else { + delete_dir_if_exists(buf.as_ref()); + } + buf.truncate("tests/ui/".len()); + } + + buf.truncate("tests/ui".len()); + buf.push("-toml/"); + + tests.clear(); + collect_ui_toml_test_names(lint, rename_prefixed, &mut tests); + for (name, _) in &tests { + buf.push(name); + delete_dir_if_exists(buf.as_ref()); + buf.truncate("tests/ui/".len()); + } +} + +fn snake_to_pascal(s: &str) -> String { + let mut dst = Vec::with_capacity(s.len()); + let mut iter = s.bytes(); + || -> Option<()> { + dst.push(iter.next()?.to_ascii_uppercase()); + while let Some(c) = iter.next() { + if c == b'_' { + dst.push(iter.next()?.to_ascii_uppercase()); + } else { + dst.push(c); + } + } + Some(()) + }(); + String::from_utf8(dst).unwrap() +} + +#[expect(clippy::too_many_lines)] +fn file_update_fn<'a, 'b>( + old_name: &'a str, + new_name: &'b str, + mod_edit: ModEdit, +) -> impl use<'a, 'b> + FnMut(&Path, &str, &mut String) -> UpdateStatus { + let old_name_pascal = snake_to_pascal(old_name); + let new_name_pascal = snake_to_pascal(new_name); + let old_name_upper = old_name.to_ascii_uppercase(); + let new_name_upper = new_name.to_ascii_uppercase(); + move |_, src, dst| { + let mut copy_pos = 0u32; + let mut changed = false; + let mut searcher = RustSearcher::new(src); + let mut capture = ""; + loop { + match searcher.peek() { + TokenKind::Eof => break, + TokenKind::Ident => { + let match_start = searcher.pos(); + let text = searcher.peek_text(); + searcher.step(); + match text { + // clippy::line_name or clippy::lint-name + "clippy" => { + if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture]) + && capture == old_name + { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos(); + changed = true; + } + }, + // mod lint_name + "mod" => { + if !matches!(mod_edit, ModEdit::None) + && searcher.match_tokens(&[Token::CaptureIdent], &mut [&mut capture]) + && capture == old_name + { + match mod_edit { + ModEdit::Rename => { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos(); + changed = true; + }, + ModEdit::Delete if searcher.match_tokens(&[Token::Semi], &mut []) => { + let mut start = &src[copy_pos as usize..match_start as usize]; + if start.ends_with("\n\n") { + start = &start[..start.len() - 1]; + } + dst.push_str(start); + copy_pos = searcher.pos(); + if src[copy_pos as usize..].starts_with("\n\n") { + copy_pos += 1; + } + changed = true; + }, + ModEdit::Delete | ModEdit::None => {}, + } + } + }, + // lint_name:: + name if matches!(mod_edit, ModEdit::Rename) && name == old_name => { + let name_end = searcher.pos(); + if searcher.match_tokens(&[Token::DoubleColon], &mut []) { + dst.push_str(&src[copy_pos as usize..match_start as usize]); + dst.push_str(new_name); + copy_pos = name_end; + changed = true; + } + }, + // LINT_NAME or LintName + name => { + let replacement = if name == old_name_upper { + &new_name_upper + } else if name == old_name_pascal { + &new_name_pascal + } else { + continue; + }; + dst.push_str(&src[copy_pos as usize..match_start as usize]); + dst.push_str(replacement); + copy_pos = searcher.pos(); + changed = true; + }, + } + }, + // //~ lint_name + TokenKind::LineComment { doc_style: None } => { + let text = searcher.peek_text(); + if text.starts_with("//~") + && let Some(text) = text.strip_suffix(old_name) + && !text.ends_with(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')) + { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize + text.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos() + searcher.peek_len(); + changed = true; + } + searcher.step(); + }, + // ::lint_name + TokenKind::Colon + if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture]) + && capture == old_name => + { + dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]); + dst.push_str(new_name); + copy_pos = searcher.pos(); + changed = true; + }, + _ => searcher.step(), + } + } + + dst.push_str(&src[copy_pos as usize..]); + UpdateStatus::from_changed(changed) + } +} diff --git a/src/tools/clippy/clippy_dev/src/setup/toolchain.rs b/src/tools/clippy/clippy_dev/src/setup/toolchain.rs index 2966629cf70a3..ecd80215f7e8f 100644 --- a/src/tools/clippy/clippy_dev/src/setup/toolchain.rs +++ b/src/tools/clippy/clippy_dev/src/setup/toolchain.rs @@ -62,7 +62,7 @@ pub fn create(standalone: bool, force: bool, release: bool, name: &str) { println!("Created toolchain {name}, use it in other projects with e.g. `cargo +{name} clippy`"); if !standalone { - println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain` changes"); + println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain.toml` changes"); } } diff --git a/src/tools/clippy/clippy_dev/src/sync.rs b/src/tools/clippy/clippy_dev/src/sync.rs index 3522d182e90ac..98fd72fc0bd1a 100644 --- a/src/tools/clippy/clippy_dev/src/sync.rs +++ b/src/tools/clippy/clippy_dev/src/sync.rs @@ -1,33 +1,25 @@ -use std::fmt::Write; -use std::path::Path; - +use crate::utils::{FileUpdater, update_text_region_fn}; use chrono::offset::Utc; - -use crate::utils::{UpdateMode, replace_region_in_file}; +use std::fmt::Write; pub fn update_nightly() { - // Update rust-toolchain nightly version let date = Utc::now().format("%Y-%m-%d").to_string(); - replace_region_in_file( - UpdateMode::Change, - Path::new("rust-toolchain"), + let toolchain_update = &mut update_text_region_fn( "# begin autogenerated nightly\n", "# end autogenerated nightly", - |res| { - writeln!(res, "channel = \"nightly-{date}\"").unwrap(); + |dst| { + writeln!(dst, "channel = \"nightly-{date}\"").unwrap(); }, ); - - // Update clippy_utils nightly version - replace_region_in_file( - UpdateMode::Change, - Path::new("clippy_utils/README.md"), + let readme_update = &mut update_text_region_fn( "\n", "", - |res| { - writeln!(res, "```").unwrap(); - writeln!(res, "nightly-{date}").unwrap(); - writeln!(res, "```").unwrap(); + |dst| { + writeln!(dst, "```\nnightly-{date}\n```").unwrap(); }, ); + + let mut updater = FileUpdater::default(); + updater.update_file("rust-toolchain.toml", toolchain_update); + updater.update_file("clippy_utils/README.md", readme_update); } diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index b80ee5aac7e76..25ba2c7204904 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -1,12 +1,9 @@ -use crate::utils::{UpdateMode, clippy_project_root, exit_with_failure, replace_region_in_file}; -use aho_corasick::AhoCorasickBuilder; +use crate::utils::{ + ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_action, update_text_region_fn, +}; use itertools::Itertools; -use rustc_lexer::{LiteralKind, TokenKind, tokenize, unescape}; -use std::collections::{HashMap, HashSet}; -use std::ffi::OsStr; -use std::fmt::{self, Write}; -use std::fs::{self, OpenOptions}; -use std::io::{self, Read, Seek, Write as _}; +use std::collections::HashSet; +use std::fmt::Write; use std::ops::Range; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; @@ -27,866 +24,343 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht /// /// Panics if a file path could not read from or then written to pub fn update(update_mode: UpdateMode) { - let (lints, deprecated_lints, renamed_lints) = gather_all(); - generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); + let lints = find_lint_decls(); + let (deprecated, renamed) = read_deprecated_lints(); + generate_lint_files(update_mode, &lints, &deprecated, &renamed); } -fn generate_lint_files( +#[expect(clippy::too_many_lines)] +pub fn generate_lint_files( update_mode: UpdateMode, lints: &[Lint], - deprecated_lints: &[DeprecatedLint], - renamed_lints: &[RenamedLint], + deprecated: &[DeprecatedLint], + renamed: &[RenamedLint], ) { - let internal_lints = Lint::internal_lints(lints); - let mut usable_lints = Lint::usable_lints(lints); - usable_lints.sort_by_key(|lint| lint.name.clone()); - - replace_region_in_file( + FileUpdater::default().update_files_checked( + "cargo dev update_lints", update_mode, - Path::new("README.md"), - "[There are over ", - " lints included in this crate!]", - |res| { - write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap(); - }, - ); - - replace_region_in_file( - update_mode, - Path::new("book/src/README.md"), - "[There are over ", - " lints included in this crate!]", - |res| { - write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap(); - }, - ); - - replace_region_in_file( - update_mode, - Path::new("CHANGELOG.md"), - "\n", - "", - |res| { - for lint in usable_lints - .iter() - .map(|l| &*l.name) - .chain(deprecated_lints.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) - .chain(renamed_lints.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) - .sorted() - { - writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); - } - }, - ); - - // This has to be in lib.rs, otherwise rustfmt doesn't work - replace_region_in_file( - update_mode, - Path::new("clippy_lints/src/lib.rs"), - "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n", - "// end lints modules, do not remove this comment, it’s used in `update_lints`", - |res| { - for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() { - writeln!(res, "mod {lint_mod};").unwrap(); - } - }, - ); - - process_file( - "clippy_lints/src/declared_lints.rs", - update_mode, - &gen_declared_lints(internal_lints.iter(), usable_lints.iter()), - ); - - let content = gen_deprecated_lints_test(deprecated_lints); - process_file("tests/ui/deprecated.rs", update_mode, &content); - - let content = gen_renamed_lints_test(renamed_lints); - process_file("tests/ui/rename.rs", update_mode, &content); -} - -pub fn print_lints() { - let (lint_list, _, _) = gather_all(); - let usable_lints = Lint::usable_lints(&lint_list); - let usable_lint_count = usable_lints.len(); - let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); - - for (lint_group, mut lints) in grouped_by_lint_group { - println!("\n## {lint_group}"); - - lints.sort_by_key(|l| l.name.clone()); - - for lint in lints { - println!("* [{}]({DOCS_LINK}#{}) ({})", lint.name, lint.name, lint.desc); - } - } - - println!("there are {usable_lint_count} lints"); -} - -/// Runs the `rename_lint` command. -/// -/// This does the following: -/// * Adds an entry to `renamed_lints.rs`. -/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). -/// * Renames the lint struct to the new name. -/// * Renames the module containing the lint struct to the new name if it shares a name with the -/// lint. -/// -/// # Panics -/// Panics for the following conditions: -/// * If a file path could not read from or then written to -/// * If either lint name has a prefix -/// * If `old_name` doesn't name an existing lint. -/// * If `old_name` names a deprecated or renamed lint. -#[allow(clippy::too_many_lines)] -pub fn rename(old_name: &str, new_name: &str, uplift: bool) { - if let Some((prefix, _)) = old_name.split_once("::") { - panic!("`{old_name}` should not contain the `{prefix}` prefix"); - } - if let Some((prefix, _)) = new_name.split_once("::") { - panic!("`{new_name}` should not contain the `{prefix}` prefix"); - } - - let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); - let mut old_lint_index = None; - let mut found_new_name = false; - for (i, lint) in lints.iter().enumerate() { - if lint.name == old_name { - old_lint_index = Some(i); - } else if lint.name == new_name { - found_new_name = true; - } - } - let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`")); - - let lint = RenamedLint { - old_name: format!("clippy::{old_name}"), - new_name: if uplift { - new_name.into() - } else { - format!("clippy::{new_name}") - }, - }; - - // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in - // case. - assert!( - !renamed_lints.iter().any(|l| lint.old_name == l.old_name), - "`{old_name}` has already been renamed" - ); - assert!( - !deprecated_lints.iter().any(|l| lint.old_name == l.name), - "`{old_name}` has already been deprecated" - ); - - // Update all lint level attributes. (`clippy::lint_name`) - for file in WalkDir::new(clippy_project_root()) - .into_iter() - .map(Result::unwrap) - .filter(|f| { - let name = f.path().file_name(); - let ext = f.path().extension(); - (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) - && name != Some(OsStr::new("rename.rs")) - && name != Some(OsStr::new("deprecated_lints.rs")) - }) - { - rewrite_file(file.path(), |s| { - replace_ident_like(s, &[(&lint.old_name, &lint.new_name)]) - }); - } - - let version = crate::new_lint::get_stabilization_version(); - rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| { - insert_at_marker( - s, - "// end renamed lints. used by `cargo dev rename_lint`", - &format!( - "#[clippy::version = \"{version}\"]\n \ - (\"{}\", \"{}\"),\n ", - lint.old_name, lint.new_name, + &mut [ + ( + "README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); + }), ), - ) - }); - - renamed_lints.push(lint); - renamed_lints.sort_by(|lhs, rhs| { - lhs.new_name - .starts_with("clippy::") - .cmp(&rhs.new_name.starts_with("clippy::")) - .reverse() - .then_with(|| lhs.old_name.cmp(&rhs.old_name)) - }); - - if uplift { - write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); - println!( - "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually." - ); - } else if found_new_name { - write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); - println!( - "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually." - ); - } else { - // Rename the lint struct and source files sharing a name with the lint. - let lint = &mut lints[old_lint_index]; - let old_name_upper = old_name.to_uppercase(); - let new_name_upper = new_name.to_uppercase(); - lint.name = new_name.into(); - - // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. - if try_rename_file( - Path::new(&format!("tests/ui/{old_name}.rs")), - Path::new(&format!("tests/ui/{new_name}.rs")), - ) { - try_rename_file( - Path::new(&format!("tests/ui/{old_name}.stderr")), - Path::new(&format!("tests/ui/{new_name}.stderr")), - ); - try_rename_file( - Path::new(&format!("tests/ui/{old_name}.fixed")), - Path::new(&format!("tests/ui/{new_name}.fixed")), - ); - } - - // Try to rename the file containing the lint if the file name matches the lint's name. - let replacements; - let replacements = if lint.module == old_name - && try_rename_file( - Path::new(&format!("clippy_lints/src/{old_name}.rs")), - Path::new(&format!("clippy_lints/src/{new_name}.rs")), - ) { - // Edit the module name in the lint list. Note there could be multiple lints. - for lint in lints.iter_mut().filter(|l| l.module == old_name) { - lint.module = new_name.into(); - } - replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; - replacements.as_slice() - } else if !lint.module.contains("::") - // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` - && try_rename_file( - Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)), - Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)), - ) - { - // Edit the module name in the lint list. Note there could be multiple lints, or none. - let renamed_mod = format!("{}::{old_name}", lint.module); - for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { - lint.module = format!("{}::{new_name}", lint.module); - } - replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; - replacements.as_slice() - } else { - replacements = [(&*old_name_upper, &*new_name_upper), ("", "")]; - &replacements[0..1] - }; - - // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being - // renamed. - for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("deprecated_lints.rs")) - { - rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); - } - - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("{old_name} has been successfully renamed"); - } - - println!("note: `cargo uitest` still needs to be run to update the test results"); -} - -/// Runs the `deprecate` command -/// -/// This does the following: -/// * Adds an entry to `deprecated_lints.rs`. -/// * Removes the lint declaration (and the entire file if applicable) -/// -/// # Panics -/// -/// If a file path could not read from or written to -pub fn deprecate(name: &str, reason: &str) { - let prefixed_name = if name.starts_with("clippy::") { - name.to_owned() - } else { - format!("clippy::{name}") - }; - let stripped_name = &prefixed_name[8..]; - - let (mut lints, mut deprecated_lints, renamed_lints) = gather_all(); - let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { - eprintln!("error: failed to find lint `{name}`"); - return; - }; - - let mod_path = { - let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); - if mod_path.is_dir() { - mod_path = mod_path.join("mod"); - } - - mod_path.set_extension("rs"); - mod_path - }; - - let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs"); - - if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { - let version = crate::new_lint::get_stabilization_version(); - rewrite_file(deprecated_lints_path, |s| { - insert_at_marker( - s, - "// end deprecated lints. used by `cargo dev deprecate_lint`", - &format!("#[clippy::version = \"{version}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ",), - ) - }); - - deprecated_lints.push(DeprecatedLint { - name: prefixed_name, - reason: reason.into(), - }); - - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("info: `{name}` has successfully been deprecated"); - println!("note: you must run `cargo uitest` to update the test results"); - } else { - eprintln!("error: lint not found"); - } -} - -fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io::Result { - fn remove_lint(name: &str, lints: &mut Vec) { - lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); - } - - fn remove_test_assets(name: &str) { - let test_file_stem = format!("tests/ui/{name}"); - let path = Path::new(&test_file_stem); - - // Some lints have their own directories, delete them - if path.is_dir() { - let _ = fs::remove_dir_all(path); - return; - } - - // Remove all related test files - let _ = fs::remove_file(path.with_extension("rs")); - let _ = fs::remove_file(path.with_extension("stderr")); - let _ = fs::remove_file(path.with_extension("fixed")); - } - - fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { - let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| { - content - .find("declare_lint_pass!") - .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`")) - }); - let mut impl_lint_pass_end = content[impl_lint_pass_start..] - .find(']') - .expect("failed to find `impl_lint_pass` terminator"); - - impl_lint_pass_end += impl_lint_pass_start; - if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) { - let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len()); - for c in content[lint_name_end..impl_lint_pass_end].chars() { - // Remove trailing whitespace - if c == ',' || c.is_whitespace() { - lint_name_end += 1; - } else { - break; - } - } - - content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, ""); - } - } - - if path.exists() { - if let Some(lint) = lints.iter().find(|l| l.name == name) { - if lint.module == name { - // The lint name is the same as the file, we can just delete the entire file - fs::remove_file(path)?; - } else { - // We can't delete the entire file, just remove the declaration - - if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { - // Remove clippy_lints/src/some_mod/some_lint.rs - let mut lint_mod_path = path.to_path_buf(); - lint_mod_path.set_file_name(name); - lint_mod_path.set_extension("rs"); - - let _ = fs::remove_file(lint_mod_path); + ( + "book/src/README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); + }), + ), + ( + "CHANGELOG.md", + &mut update_text_region_fn( + "\n", + "", + |dst| { + for lint in lints + .iter() + .map(|l| &*l.name) + .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) + .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) + .sorted() + { + writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); + } + }, + ), + ), + ( + "clippy_lints/src/lib.rs", + &mut update_text_region_fn( + "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n", + "// end lints modules, do not remove this comment, it’s used in `update_lints`", + |dst| { + for lint_mod in lints.iter().map(|l| &l.module).sorted().dedup() { + writeln!(dst, "mod {lint_mod};").unwrap(); + } + }, + ), + ), + ("clippy_lints/src/declared_lints.rs", &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n"); + for (module_name, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() { + writeln!(dst, " crate::{module_name}::{lint_name}_INFO,").unwrap(); } - - let mut content = - fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); - - eprintln!( - "warn: you will have to manually remove any code related to `{name}` from `{}`", - path.display() - ); - + dst.push_str("];\n"); + UpdateStatus::from_changed(src != dst) + }), + ("clippy_lints/src/deprecated_lints.rs", &mut |_, src, dst| { + let mut searcher = RustSearcher::new(src); assert!( - content[lint.declaration_range.clone()].contains(&name.to_uppercase()), - "error: `{}` does not contain lint `{}`'s declaration", - path.display(), - lint.name + searcher.find_token(Token::Ident("declare_with_version")) + && searcher.find_token(Token::Ident("declare_with_version")), + "error reading deprecated lints" ); - - // Remove lint declaration (declare_clippy_lint!) - content.replace_range(lint.declaration_range.clone(), ""); - - // Remove the module declaration (mod xyz;) - let mod_decl = format!("\nmod {name};"); - content = content.replacen(&mod_decl, "", 1); - - remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); - fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); - } - - remove_test_assets(name); - remove_lint(name, lints); - return Ok(true); - } - } - - Ok(false) -} - -/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there -/// were no replacements. -fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option { - fn is_ident_char(c: u8) -> bool { - matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') - } - - let searcher = AhoCorasickBuilder::new() - .match_kind(aho_corasick::MatchKind::LeftmostLongest) - .build(replacements.iter().map(|&(x, _)| x.as_bytes())) - .unwrap(); - - let mut result = String::with_capacity(contents.len() + 1024); - let mut pos = 0; - let mut edited = false; - for m in searcher.find_iter(contents) { - let (old, new) = replacements[m.pattern()]; - result.push_str(&contents[pos..m.start()]); - result.push_str( - if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) - && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0)) - { - edited = true; - new - } else { - old - }, - ); - pos = m.end(); - } - result.push_str(&contents[pos..]); - edited.then_some(result) + dst.push_str(&src[..searcher.pos() as usize]); + dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); + for lint in deprecated { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + lint.version, lint.name, lint.reason, + ) + .unwrap(); + } + dst.push_str( + "]}\n\n\ + #[rustfmt::skip]\n\ + declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\ + ", + ); + for lint in renamed { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + lint.version, lint.old_name, lint.new_name, + ) + .unwrap(); + } + dst.push_str("]}\n"); + UpdateStatus::from_changed(src != dst) + }), + ("tests/ui/deprecated.rs", &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + for lint in deprecated { + writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }), + ("tests/ui/rename.rs", &mut move |_, src, dst| { + let mut seen_lints = HashSet::new(); + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); + for lint in renamed { + if seen_lints.insert(&lint.new_name) { + writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); + } + } + seen_lints.clear(); + for lint in renamed { + if seen_lints.insert(&lint.old_name) { + writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); + } + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }), + ], + ); } fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } -fn process_file(path: impl AsRef, update_mode: UpdateMode, content: &str) { - if update_mode == UpdateMode::Check { - let old_content = - fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {e}", path.as_ref().display())); - if content != old_content { - exit_with_failure(); - } - } else { - fs::write(&path, content.as_bytes()) - .unwrap_or_else(|e| panic!("Cannot write to {}: {e}", path.as_ref().display())); - } -} - /// Lint data parsed from the Clippy source code. -#[derive(Clone, PartialEq, Eq, Debug)] -struct Lint { - name: String, - group: String, - desc: String, - module: String, - declaration_range: Range, +#[derive(PartialEq, Eq, Debug)] +pub struct Lint { + pub name: String, + pub group: String, + pub module: String, + pub path: PathBuf, + pub declaration_range: Range, } -impl Lint { - #[must_use] - fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range) -> Self { - Self { - name: name.to_lowercase(), - group: group.into(), - desc: remove_line_splices(desc), - module: module.into(), - declaration_range, - } - } - - /// Returns all non-deprecated lints and non-internal lints - #[must_use] - fn usable_lints(lints: &[Self]) -> Vec { - lints - .iter() - .filter(|l| !l.group.starts_with("internal")) - .cloned() - .collect() - } - - /// Returns all internal lints - #[must_use] - fn internal_lints(lints: &[Self]) -> Vec { - lints.iter().filter(|l| l.group == "internal").cloned().collect() - } - - /// Returns the lints in a `HashMap`, grouped by the different lint groups - #[must_use] - fn by_lint_group(lints: impl Iterator) -> HashMap> { - lints.map(|lint| (lint.group.to_string(), lint)).into_group_map() - } -} - -#[derive(Clone, PartialEq, Eq, Debug)] -struct DeprecatedLint { - name: String, - reason: String, -} -impl DeprecatedLint { - fn new(name: &str, reason: &str) -> Self { - Self { - name: remove_line_splices(name), - reason: remove_line_splices(reason), - } - } +pub struct DeprecatedLint { + pub name: String, + pub reason: String, + pub version: String, } -struct RenamedLint { - old_name: String, - new_name: String, -} -impl RenamedLint { - fn new(old_name: &str, new_name: &str) -> Self { - Self { - old_name: remove_line_splices(old_name), - new_name: remove_line_splices(new_name), - } - } +pub struct RenamedLint { + pub old_name: String, + pub new_name: String, + pub version: String, } -/// Generates the code for registering lints +/// Finds all lint declarations (`declare_clippy_lint!`) #[must_use] -fn gen_declared_lints<'a>( - internal_lints: impl Iterator, - usable_lints: impl Iterator, -) -> String { - let mut details: Vec<_> = internal_lints - .map(|l| (false, &l.module, l.name.to_uppercase())) - .chain(usable_lints.map(|l| (true, &l.module, l.name.to_uppercase()))) - .collect(); - details.sort_unstable(); - - let mut output = GENERATED_FILE_COMMENT.to_string(); - output.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n"); - - for (is_public, module_name, lint_name) in details { - if !is_public { - output.push_str(" #[cfg(feature = \"internal\")]\n"); - } - let _: fmt::Result = writeln!(output, " crate::{module_name}::{lint_name}_INFO,"); - } - output.push_str("];\n"); - - output -} - -fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { - let mut res: String = GENERATED_FILE_COMMENT.into(); - for lint in lints { - writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); - } - res.push_str("\nfn main() {}\n"); - res -} - -fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { - let mut seen_lints = HashSet::new(); - let mut res: String = GENERATED_FILE_COMMENT.into(); - - res.push_str("#![allow(clippy::duplicated_attributes)]\n"); - for lint in lints { - if seen_lints.insert(&lint.new_name) { - writeln!(res, "#![allow({})]", lint.new_name).unwrap(); - } - } - seen_lints.clear(); - for lint in lints { - if seen_lints.insert(&lint.old_name) { - writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); - } +pub fn find_lint_decls() -> Vec { + let mut lints = Vec::with_capacity(1000); + let mut contents = String::new(); + for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) { + parse_clippy_lint_decls( + file.path(), + File::open_read_to_cleared_string(file.path(), &mut contents), + &module, + &mut lints, + ); } - res.push_str("\nfn main() {}\n"); - res + lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + lints } -/// Gathers all lints defined in `clippy_lints/src` -fn gather_all() -> (Vec, Vec, Vec) { - let mut lints = Vec::with_capacity(1000); - let mut deprecated_lints = Vec::with_capacity(50); - let mut renamed_lints = Vec::with_capacity(50); - - for (rel_path, file) in clippy_lints_src_files() { - let path = file.path(); - let contents = - fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); - let module = rel_path - .components() - .map(|c| c.as_os_str().to_str().unwrap()) - .collect::>() - .join("::"); - - // If the lints are stored in mod.rs, we get the module name from - // the containing directory: - let module = if let Some(module) = module.strip_suffix("::mod.rs") { - module - } else { - module.strip_suffix(".rs").unwrap_or(&module) +/// Reads the source files from the given root directory +fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator { + WalkDir::new(src_root).into_iter().filter_map(move |e| { + let e = match e { + Ok(e) => e, + Err(ref e) => panic_action(e, ErrAction::Read, src_root), }; - - if module == "deprecated_lints" { - parse_deprecated_contents(&contents, &mut deprecated_lints, &mut renamed_lints); + let path = e.path().as_os_str().as_encoded_bytes(); + if let Some(path) = path.strip_suffix(b".rs") + && let Some(path) = path.get("clippy_lints/src/".len()..) + { + if path == b"lib" { + Some((e, String::new())) + } else { + let path = if let Some(path) = path.strip_suffix(b"mod") + && let Some(path) = path.strip_suffix(b"/").or_else(|| path.strip_suffix(b"\\")) + { + path + } else { + path + }; + if let Ok(path) = str::from_utf8(path) { + let path = path.replace(['/', '\\'], "::"); + Some((e, path)) + } else { + None + } + } } else { - parse_contents(&contents, module, &mut lints); + None } - } - (lints, deprecated_lints, renamed_lints) -} - -fn clippy_lints_src_files() -> impl Iterator { - let root_path = clippy_project_root().join("clippy_lints/src"); - let iter = WalkDir::new(&root_path).into_iter(); - iter.map(Result::unwrap) - .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) - .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) + }) } -macro_rules! match_tokens { - ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => { - { - $(#[allow(clippy::redundant_pattern)] let Some(LintDeclSearchResult { - token_kind: TokenKind::$token $({$($fields)*})?, - content: $($capture @)? _, - .. - }) = $iter.next() else { - continue; - };)* - #[allow(clippy::unused_unit)] - { ($($($capture,)?)*) } +/// Parse a source file looking for `declare_clippy_lint` macro invocations. +fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mut Vec) { + #[allow(clippy::enum_glob_use)] + use Token::*; + #[rustfmt::skip] + static DECL_TOKENS: &[Token<'_>] = &[ + // !{ /// docs + Bang, OpenBrace, AnyComment, + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket, + // pub NAME, GROUP, + Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma, + ]; + + let mut searcher = RustSearcher::new(contents); + while searcher.find_token(Ident("declare_clippy_lint")) { + let start = searcher.pos() as usize - "declare_clippy_lint".len(); + let (mut name, mut group) = ("", ""); + if searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut group]) && searcher.find_token(CloseBrace) { + lints.push(Lint { + name: name.to_lowercase(), + group: group.into(), + module: module.into(), + path: path.into(), + declaration_range: start..searcher.pos() as usize, + }); } } } -pub(crate) use match_tokens; - -pub(crate) struct LintDeclSearchResult<'a> { - pub token_kind: TokenKind, - pub content: &'a str, - pub range: Range, -} - -/// Parse a source file looking for `declare_clippy_lint` macro invocations. -fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { - let mut offset = 0usize; - let mut iter = tokenize(contents).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &contents[range.clone()], - range, - } - }); +#[must_use] +pub fn read_deprecated_lints() -> (Vec, Vec) { + #[allow(clippy::enum_glob_use)] + use Token::*; + #[rustfmt::skip] + static DECL_TOKENS: &[Token<'_>] = &[ + // #[clippy::version = "version"] + Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, CloseBracket, + // ("first", "second"), + OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma, + ]; + #[rustfmt::skip] + static DEPRECATED_TOKENS: &[Token<'_>] = &[ + // !{ DEPRECATED(DEPRECATED_VERSION) = [ + Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket, + ]; + #[rustfmt::skip] + static RENAMED_TOKENS: &[Token<'_>] = &[ + // !{ RENAMED(RENAMED_VERSION) = [ + Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket, + ]; + + let path = "clippy_lints/src/deprecated_lints.rs"; + let mut deprecated = Vec::with_capacity(30); + let mut renamed = Vec::with_capacity(80); + let mut contents = String::new(); + File::open_read_to_cleared_string(path, &mut contents); + + let mut searcher = RustSearcher::new(&contents); + + // First instance is the macro definition. + assert!( + searcher.find_token(Ident("declare_with_version")), + "error reading deprecated lints" + ); - while let Some(LintDeclSearchResult { range, .. }) = iter.find( - |LintDeclSearchResult { - token_kind, content, .. - }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint", - ) { - let start = range.start; - let mut iter = iter - .by_ref() - .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); - // matches `!{` - match_tokens!(iter, Bang OpenBrace); - match iter.next() { - // #[clippy::version = "version"] pub - Some(LintDeclSearchResult { - token_kind: TokenKind::Pound, - .. - }) => { - match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident); - }, - // pub - Some(LintDeclSearchResult { - token_kind: TokenKind::Ident, - .. - }) => (), - _ => continue, + if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(DEPRECATED_TOKENS, &mut []) { + let mut version = ""; + let mut name = ""; + let mut reason = ""; + while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut name, &mut reason]) { + deprecated.push(DeprecatedLint { + name: parse_str_single_line(path.as_ref(), name), + reason: parse_str_single_line(path.as_ref(), reason), + version: parse_str_single_line(path.as_ref(), version), + }); } - - let (name, group, desc) = match_tokens!( - iter, - // LINT_NAME - Ident(name) Comma - // group, - Ident(group) Comma - // "description" - Literal{..}(desc) - ); - - if let Some(end) = iter.find_map(|t| { - if let LintDeclSearchResult { - token_kind: TokenKind::CloseBrace, - range, - .. - } = t - { - Some(range.end) - } else { - None - } - }) { - lints.push(Lint::new(name, group, desc, module, start..end)); + } else { + panic!("error reading deprecated lints"); + } + + if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(RENAMED_TOKENS, &mut []) { + let mut version = ""; + let mut old_name = ""; + let mut new_name = ""; + while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut old_name, &mut new_name]) { + renamed.push(RenamedLint { + old_name: parse_str_single_line(path.as_ref(), old_name), + new_name: parse_str_single_line(path.as_ref(), new_name), + version: parse_str_single_line(path.as_ref(), version), + }); } + } else { + panic!("error reading renamed lints"); } -} - -/// Parse a source file looking for `declare_deprecated_lint` macro invocations. -fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec, renamed: &mut Vec) { - let Some((_, contents)) = contents.split_once("\ndeclare_with_version! { DEPRECATED") else { - return; - }; - let Some((deprecated_src, renamed_src)) = contents.split_once("\ndeclare_with_version! { RENAMED") else { - return; - }; - - for line in deprecated_src.lines() { - let mut offset = 0usize; - let mut iter = tokenize(line).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &line[range.clone()], - range, - } - }); - - let (name, reason) = match_tokens!( - iter, - // ("old_name", - Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(name) Comma - // "new_name"), - Whitespace Literal{kind: LiteralKind::Str{..},..}(reason) CloseParen Comma - ); - deprecated.push(DeprecatedLint::new(name, reason)); - } - for line in renamed_src.lines() { - let mut offset = 0usize; - let mut iter = tokenize(line).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &line[range.clone()], - range, - } - }); - let (old_name, new_name) = match_tokens!( - iter, - // ("old_name", - Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma - // "new_name"), - Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma - ); - renamed.push(RenamedLint::new(old_name, new_name)); - } + deprecated.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(&rhs.old_name)); + (deprecated, renamed) } /// Removes the line splices and surrounding quotes from a string literal -fn remove_line_splices(s: &str) -> String { +fn parse_str_lit(s: &str) -> String { + let (s, mode) = if let Some(s) = s.strip_prefix("r") { + (s.trim_matches('#'), rustc_literal_escaper::Mode::RawStr) + } else { + (s, rustc_literal_escaper::Mode::Str) + }; let s = s - .strip_prefix('r') - .unwrap_or(s) - .trim_matches('#') .strip_prefix('"') .and_then(|s| s.strip_suffix('"')) .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); let mut res = String::with_capacity(s.len()); - unescape::unescape_unicode(s, unescape::Mode::Str, &mut |range, ch| { - if ch.is_ok() { - res.push_str(&s[range]); + rustc_literal_escaper::unescape_unicode(s, mode, &mut |_, ch| { + if let Ok(ch) = ch { + res.push(ch); } }); res } -fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { - match OpenOptions::new().create_new(true).write(true).open(new_name) { - Ok(file) => drop(file), - Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, - Err(e) => panic_file(e, new_name, "create"), - } - match fs::rename(old_name, new_name) { - Ok(()) => true, - Err(e) => { - drop(fs::remove_file(new_name)); - if e.kind() == io::ErrorKind::NotFound { - false - } else { - panic_file(e, old_name, "rename"); - } - }, - } -} - -#[allow(clippy::needless_pass_by_value)] -fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { - panic!("failed to {action} file `{}`: {error}", name.display()) -} - -fn insert_at_marker(text: &str, marker: &str, new_text: &str) -> Option { - let i = text.find(marker)?; - let (pre, post) = text.split_at(i); - Some([pre, new_text, post].into_iter().collect()) -} - -fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { - let mut file = OpenOptions::new() - .write(true) - .read(true) - .open(path) - .unwrap_or_else(|e| panic_file(e, path, "open")); - let mut buf = String::new(); - file.read_to_string(&mut buf) - .unwrap_or_else(|e| panic_file(e, path, "read")); - if let Some(new_contents) = f(&buf) { - file.rewind().unwrap_or_else(|e| panic_file(e, path, "write")); - file.write_all(new_contents.as_bytes()) - .unwrap_or_else(|e| panic_file(e, path, "write")); - file.set_len(new_contents.len() as u64) - .unwrap_or_else(|e| panic_file(e, path, "write")); - } -} -fn write_file(path: &Path, contents: &str) { - fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write")); +fn parse_str_single_line(path: &Path, s: &str) -> String { + let value = parse_str_lit(s); + assert!( + !value.contains('\n'), + "error parsing `{}`: `{s}` should be a single line string", + path.display(), + ); + value } #[cfg(test)] @@ -894,7 +368,7 @@ mod tests { use super::*; #[test] - fn test_parse_contents() { + fn test_parse_clippy_lint_decls() { static CONTENTS: &str = r#" declare_clippy_lint! { #[clippy::version = "Hello Clippy!"] @@ -912,96 +386,27 @@ mod tests { } "#; let mut result = Vec::new(); - parse_contents(CONTENTS, "module_name", &mut result); + parse_clippy_lint_decls("".as_ref(), CONTENTS, "module_name", &mut result); for r in &mut result { r.declaration_range = Range::default(); } let expected = vec![ - Lint::new( - "ptr_arg", - "style", - "\"really long text\"", - "module_name", - Range::default(), - ), - Lint::new( - "doc_markdown", - "pedantic", - "\"single line\"", - "module_name", - Range::default(), - ), + Lint { + name: "ptr_arg".into(), + group: "style".into(), + module: "module_name".into(), + path: PathBuf::new(), + declaration_range: Range::default(), + }, + Lint { + name: "doc_markdown".into(), + group: "pedantic".into(), + module: "module_name".into(), + path: PathBuf::new(), + declaration_range: Range::default(), + }, ]; assert_eq!(expected, result); } - - #[test] - fn test_usable_lints() { - let lints = vec![ - Lint::new( - "should_assert_eq2", - "Not Deprecated", - "\"abc\"", - "module_name", - Range::default(), - ), - Lint::new( - "should_assert_eq2", - "internal", - "\"abc\"", - "module_name", - Range::default(), - ), - Lint::new( - "should_assert_eq2", - "internal_style", - "\"abc\"", - "module_name", - Range::default(), - ), - ]; - let expected = vec![Lint::new( - "should_assert_eq2", - "Not Deprecated", - "\"abc\"", - "module_name", - Range::default(), - )]; - assert_eq!(expected, Lint::usable_lints(&lints)); - } - - #[test] - fn test_by_lint_group() { - let lints = vec![ - Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), - Lint::new( - "should_assert_eq2", - "group2", - "\"abc\"", - "module_name", - Range::default(), - ), - Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), - ]; - let mut expected: HashMap> = HashMap::new(); - expected.insert( - "group1".to_string(), - vec![ - Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), - Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), - ], - ); - expected.insert( - "group2".to_string(), - vec![Lint::new( - "should_assert_eq2", - "group2", - "\"abc\"", - "module_name", - Range::default(), - )], - ); - assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); - } } diff --git a/src/tools/clippy/clippy_dev/src/utils.rs b/src/tools/clippy/clippy_dev/src/utils.rs index b87fcca13b1ce..255e36afe69c2 100644 --- a/src/tools/clippy/clippy_dev/src/utils.rs +++ b/src/tools/clippy/clippy_dev/src/utils.rs @@ -1,12 +1,118 @@ +use core::fmt::{self, Display}; +use core::ops::Range; +use core::slice; +use core::str::FromStr; +use rustc_lexer::{self as lexer, FrontmatterAllowed}; +use std::env; +use std::ffi::OsStr; +use std::fs::{self, OpenOptions}; +use std::io::{self, Read as _, Seek as _, SeekFrom, Write}; use std::path::{Path, PathBuf}; -use std::process::{self, ExitStatus}; -use std::{fs, io}; +use std::process::{self, Command, ExitStatus, Stdio}; #[cfg(not(windows))] static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; #[cfg(windows)] static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; +#[derive(Clone, Copy)] +pub enum ErrAction { + Open, + Read, + Write, + Create, + Rename, + Delete, + Run, +} +impl ErrAction { + fn as_str(self) -> &'static str { + match self { + Self::Open => "opening", + Self::Read => "reading", + Self::Write => "writing", + Self::Create => "creating", + Self::Rename => "renaming", + Self::Delete => "deleting", + Self::Run => "running", + } + } +} + +#[cold] +#[track_caller] +pub fn panic_action(err: &impl Display, action: ErrAction, path: &Path) -> ! { + panic!("error {} `{}`: {}", action.as_str(), path.display(), *err) +} + +/// Wrapper around `std::fs::File` which panics with a path on failure. +pub struct File<'a> { + pub inner: fs::File, + pub path: &'a Path, +} +impl<'a> File<'a> { + /// Opens a file panicking on failure. + #[track_caller] + pub fn open(path: &'a (impl AsRef + ?Sized), options: &mut OpenOptions) -> Self { + let path = path.as_ref(); + match options.open(path) { + Ok(inner) => Self { inner, path }, + Err(e) => panic_action(&e, ErrAction::Open, path), + } + } + + /// Opens a file if it exists, panicking on any other failure. + #[track_caller] + pub fn open_if_exists(path: &'a (impl AsRef + ?Sized), options: &mut OpenOptions) -> Option { + let path = path.as_ref(); + match options.open(path) { + Ok(inner) => Some(Self { inner, path }), + Err(e) if e.kind() == io::ErrorKind::NotFound => None, + Err(e) => panic_action(&e, ErrAction::Open, path), + } + } + + /// Opens and reads a file into a string, panicking of failure. + #[track_caller] + pub fn open_read_to_cleared_string<'dst>( + path: &'a (impl AsRef + ?Sized), + dst: &'dst mut String, + ) -> &'dst mut String { + Self::open(path, OpenOptions::new().read(true)).read_to_cleared_string(dst) + } + + /// Read the entire contents of a file to the given buffer. + #[track_caller] + pub fn read_append_to_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { + match self.inner.read_to_string(dst) { + Ok(_) => {}, + Err(e) => panic_action(&e, ErrAction::Read, self.path), + } + dst + } + + #[track_caller] + pub fn read_to_cleared_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { + dst.clear(); + self.read_append_to_string(dst) + } + + /// Replaces the entire contents of a file. + #[track_caller] + pub fn replace_contents(&mut self, data: &[u8]) { + let res = match self.inner.seek(SeekFrom::Start(0)) { + Ok(_) => match self.inner.write_all(data) { + Ok(()) => self.inner.set_len(data.len() as u64), + Err(e) => Err(e), + }, + Err(e) => Err(e), + }; + if let Err(e) = res { + panic_action(&e, ErrAction::Write, self.path); + } + } +} + /// Returns the path to the `cargo-clippy` binary /// /// # Panics @@ -14,34 +120,167 @@ static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; /// Panics if the path of current executable could not be retrieved. #[must_use] pub fn cargo_clippy_path() -> PathBuf { - let mut path = std::env::current_exe().expect("failed to get current executable name"); + let mut path = env::current_exe().expect("failed to get current executable name"); path.set_file_name(CARGO_CLIPPY_EXE); path } -/// Returns the path to the Clippy project directory -/// -/// # Panics -/// -/// Panics if the current directory could not be retrieved, there was an error reading any of the -/// Cargo.toml files or ancestor directory is the clippy root directory -#[must_use] -pub fn clippy_project_root() -> PathBuf { - let current_dir = std::env::current_dir().unwrap(); - for path in current_dir.ancestors() { - let result = fs::read_to_string(path.join("Cargo.toml")); - if let Err(err) = &result { - if err.kind() == io::ErrorKind::NotFound { - continue; +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Version { + pub major: u16, + pub minor: u16, +} +impl FromStr for Version { + type Err = (); + fn from_str(s: &str) -> Result { + if let Some(s) = s.strip_prefix("0.") + && let Some((major, minor)) = s.split_once('.') + && let Ok(major) = major.parse() + && let Ok(minor) = minor.parse() + { + Ok(Self { major, minor }) + } else { + Err(()) + } + } +} +impl Version { + /// Displays the version as a rust version. i.e. `x.y.0` + #[must_use] + pub fn rust_display(self) -> impl Display { + struct X(Version); + impl Display for X { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}.{}.0", self.0.major, self.0.minor) } } + X(self) + } - let content = result.unwrap(); - if content.contains("[package]\nname = \"clippy\"") { - return path.to_path_buf(); + /// Displays the version as it should appear in clippy's toml files. i.e. `0.x.y` + #[must_use] + pub fn toml_display(self) -> impl Display { + struct X(Version); + impl Display for X { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0.{}.{}", self.0.major, self.0.minor) + } + } + X(self) + } +} + +enum TomlPart<'a> { + Table(&'a str), + Value(&'a str, &'a str), +} + +fn toml_iter(s: &str) -> impl Iterator)> { + let mut pos = 0; + s.split('\n') + .map(move |s| { + let x = pos; + pos += s.len() + 1; + (x, s) + }) + .filter_map(|(pos, s)| { + if let Some(s) = s.strip_prefix('[') { + s.split_once(']').map(|(name, _)| (pos, TomlPart::Table(name))) + } else if matches!(s.bytes().next(), Some(b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')) { + s.split_once('=').map(|(key, value)| (pos, TomlPart::Value(key, value))) + } else { + None + } + }) +} + +pub struct CargoPackage<'a> { + pub name: &'a str, + pub version_range: Range, + pub not_a_platform_range: Range, +} + +#[must_use] +pub fn parse_cargo_package(s: &str) -> CargoPackage<'_> { + let mut in_package = false; + let mut in_platform_deps = false; + let mut name = ""; + let mut version_range = 0..0; + let mut not_a_platform_range = 0..0; + for (offset, part) in toml_iter(s) { + match part { + TomlPart::Table(name) => { + if in_platform_deps { + not_a_platform_range.end = offset; + } + in_package = false; + in_platform_deps = false; + + match name.trim() { + "package" => in_package = true, + "target.'cfg(NOT_A_PLATFORM)'.dependencies" => { + in_platform_deps = true; + not_a_platform_range.start = offset; + }, + _ => {}, + } + }, + TomlPart::Value(key, value) if in_package => match key.trim_end() { + "name" => name = value.trim(), + "version" => { + version_range.start = offset + (value.len() - value.trim().len()) + key.len() + 1; + version_range.end = offset + key.len() + value.trim_end().len() + 1; + }, + _ => {}, + }, + TomlPart::Value(..) => {}, + } + } + CargoPackage { + name, + version_range, + not_a_platform_range, + } +} + +pub struct ClippyInfo { + pub path: PathBuf, + pub version: Version, + pub has_intellij_hook: bool, +} +impl ClippyInfo { + #[must_use] + pub fn search_for_manifest() -> Self { + let mut path = env::current_dir().expect("error reading the working directory"); + let mut buf = String::new(); + loop { + path.push("Cargo.toml"); + if let Some(mut file) = File::open_if_exists(&path, OpenOptions::new().read(true)) { + file.read_to_cleared_string(&mut buf); + let package = parse_cargo_package(&buf); + if package.name == "\"clippy\"" { + if let Some(version) = buf[package.version_range].strip_prefix('"') + && let Some(version) = version.strip_suffix('"') + && let Ok(version) = version.parse() + { + path.pop(); + return ClippyInfo { + path, + version, + has_intellij_hook: !package.not_a_platform_range.is_empty(), + }; + } + panic!("error reading clippy version from `{}`", file.path.display()); + } + } + + path.pop(); + assert!( + path.pop(), + "error finding project root, please run from inside the clippy directory" + ); } } - panic!("error: Can't determine root of project. Please run inside a Clippy working dir."); } /// # Panics @@ -57,86 +296,427 @@ pub fn exit_if_err(status: io::Result) { } } -pub(crate) fn clippy_version() -> (u32, u32) { - fn parse_manifest(contents: &str) -> Option<(u32, u32)> { - let version = contents - .lines() - .filter_map(|l| l.split_once('=')) - .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?; - let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else { - return None; - }; - let (minor, patch) = version.split_once('.')?; - Some((minor.parse().ok()?, patch.parse().ok()?)) +#[derive(Clone, Copy)] +pub enum UpdateStatus { + Unchanged, + Changed, +} +impl UpdateStatus { + #[must_use] + pub fn from_changed(value: bool) -> Self { + if value { Self::Changed } else { Self::Unchanged } + } + + #[must_use] + pub fn is_changed(self) -> bool { + matches!(self, Self::Changed) } - let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`"); - parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`") } -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy)] pub enum UpdateMode { - Check, Change, + Check, } +impl UpdateMode { + #[must_use] + pub fn from_check(check: bool) -> Self { + if check { Self::Check } else { Self::Change } + } -pub(crate) fn exit_with_failure() { - println!( - "Not all lints defined properly. \ - Please run `cargo dev update_lints` to make sure all lints are defined properly." - ); - process::exit(1); + #[must_use] + pub fn is_check(self) -> bool { + matches!(self, Self::Check) + } } -/// Replaces a region in a file delimited by two lines matching regexes. -/// -/// `path` is the relative path to the file on which you want to perform the replacement. -/// -/// See `replace_region_in_text` for documentation of the other options. -/// -/// # Panics -/// -/// Panics if the path could not read or then written -pub(crate) fn replace_region_in_file( - update_mode: UpdateMode, +#[derive(Default)] +pub struct FileUpdater { + src_buf: String, + dst_buf: String, +} +impl FileUpdater { + fn update_file_checked_inner( + &mut self, + tool: &str, + mode: UpdateMode, + path: &Path, + update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + ) { + let mut file = File::open(path, OpenOptions::new().read(true).write(true)); + file.read_to_cleared_string(&mut self.src_buf); + self.dst_buf.clear(); + match (mode, update(path, &self.src_buf, &mut self.dst_buf)) { + (UpdateMode::Check, UpdateStatus::Changed) => { + eprintln!( + "the contents of `{}` are out of date\nplease run `{tool}` to update", + path.display() + ); + process::exit(1); + }, + (UpdateMode::Change, UpdateStatus::Changed) => file.replace_contents(self.dst_buf.as_bytes()), + (UpdateMode::Check | UpdateMode::Change, UpdateStatus::Unchanged) => {}, + } + } + + fn update_file_inner(&mut self, path: &Path, update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus) { + let mut file = File::open(path, OpenOptions::new().read(true).write(true)); + file.read_to_cleared_string(&mut self.src_buf); + self.dst_buf.clear(); + if update(path, &self.src_buf, &mut self.dst_buf).is_changed() { + file.replace_contents(self.dst_buf.as_bytes()); + } + } + + pub fn update_file_checked( + &mut self, + tool: &str, + mode: UpdateMode, + path: impl AsRef, + update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + ) { + self.update_file_checked_inner(tool, mode, path.as_ref(), update); + } + + #[expect(clippy::type_complexity)] + pub fn update_files_checked( + &mut self, + tool: &str, + mode: UpdateMode, + files: &mut [( + impl AsRef, + &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + )], + ) { + for (path, update) in files { + self.update_file_checked_inner(tool, mode, path.as_ref(), update); + } + } + + pub fn update_file( + &mut self, + path: impl AsRef, + update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus, + ) { + self.update_file_inner(path.as_ref(), update); + } +} + +/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters +/// were found, or the missing delimiter if not. +pub fn update_text_region( path: &Path, start: &str, end: &str, - write_replacement: impl FnMut(&mut String), -) { - let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); - let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) { - Ok(x) => x, - Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()), + src: &str, + dst: &mut String, + insert: &mut impl FnMut(&mut String), +) -> UpdateStatus { + let Some((src_start, src_end)) = src.split_once(start) else { + panic!("`{}` does not contain `{start}`", path.display()); + }; + let Some((replaced_text, src_end)) = src_end.split_once(end) else { + panic!("`{}` does not contain `{end}`", path.display()); }; + dst.push_str(src_start); + dst.push_str(start); + let new_start = dst.len(); + insert(dst); + let changed = dst[new_start..] != *replaced_text; + dst.push_str(end); + dst.push_str(src_end); + UpdateStatus::from_changed(changed) +} + +pub fn update_text_region_fn( + start: &str, + end: &str, + mut insert: impl FnMut(&mut String), +) -> impl FnMut(&Path, &str, &mut String) -> UpdateStatus { + move |path, src, dst| update_text_region(path, start, end, src, dst, &mut insert) +} + +#[derive(Clone, Copy)] +pub enum Token<'a> { + /// Matches any number of comments / doc comments. + AnyComment, + Ident(&'a str), + CaptureIdent, + LitStr, + CaptureLitStr, + Bang, + CloseBrace, + CloseBracket, + CloseParen, + /// This will consume the first colon even if the second doesn't exist. + DoubleColon, + Comma, + Eq, + Lifetime, + Lt, + Gt, + OpenBrace, + OpenBracket, + OpenParen, + Pound, + Semi, + Slash, +} + +pub struct RustSearcher<'txt> { + text: &'txt str, + cursor: lexer::Cursor<'txt>, + pos: u32, + next_token: lexer::Token, +} +impl<'txt> RustSearcher<'txt> { + #[must_use] + #[expect(clippy::inconsistent_struct_constructor)] + pub fn new(text: &'txt str) -> Self { + let mut cursor = lexer::Cursor::new(text, FrontmatterAllowed::Yes); + Self { + text, + pos: 0, + next_token: cursor.advance_token(), + cursor, + } + } + + #[must_use] + pub fn peek_text(&self) -> &'txt str { + &self.text[self.pos as usize..(self.pos + self.next_token.len) as usize] + } + + #[must_use] + pub fn peek_len(&self) -> u32 { + self.next_token.len + } + + #[must_use] + pub fn peek(&self) -> lexer::TokenKind { + self.next_token.kind + } + + #[must_use] + pub fn pos(&self) -> u32 { + self.pos + } + + #[must_use] + pub fn at_end(&self) -> bool { + self.next_token.kind == lexer::TokenKind::Eof + } + + pub fn step(&mut self) { + // `next_len` is zero for the sentinel value and the eof marker. + self.pos += self.next_token.len; + self.next_token = self.cursor.advance_token(); + } + + /// Consumes the next token if it matches the requested value and captures the value if + /// requested. Returns true if a token was matched. + fn read_token(&mut self, token: Token<'_>, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool { + loop { + match (token, self.next_token.kind) { + (_, lexer::TokenKind::Whitespace) + | ( + Token::AnyComment, + lexer::TokenKind::BlockComment { terminated: true, .. } | lexer::TokenKind::LineComment { .. }, + ) => self.step(), + (Token::AnyComment, _) => return true, + (Token::Bang, lexer::TokenKind::Bang) + | (Token::CloseBrace, lexer::TokenKind::CloseBrace) + | (Token::CloseBracket, lexer::TokenKind::CloseBracket) + | (Token::CloseParen, lexer::TokenKind::CloseParen) + | (Token::Comma, lexer::TokenKind::Comma) + | (Token::Eq, lexer::TokenKind::Eq) + | (Token::Lifetime, lexer::TokenKind::Lifetime { .. }) + | (Token::Lt, lexer::TokenKind::Lt) + | (Token::Gt, lexer::TokenKind::Gt) + | (Token::OpenBrace, lexer::TokenKind::OpenBrace) + | (Token::OpenBracket, lexer::TokenKind::OpenBracket) + | (Token::OpenParen, lexer::TokenKind::OpenParen) + | (Token::Pound, lexer::TokenKind::Pound) + | (Token::Semi, lexer::TokenKind::Semi) + | (Token::Slash, lexer::TokenKind::Slash) + | ( + Token::LitStr, + lexer::TokenKind::Literal { + kind: lexer::LiteralKind::Str { terminated: true } | lexer::LiteralKind::RawStr { .. }, + .. + }, + ) => { + self.step(); + return true; + }, + (Token::Ident(x), lexer::TokenKind::Ident) if x == self.peek_text() => { + self.step(); + return true; + }, + (Token::DoubleColon, lexer::TokenKind::Colon) => { + self.step(); + if !self.at_end() && matches!(self.next_token.kind, lexer::TokenKind::Colon) { + self.step(); + return true; + } + return false; + }, + ( + Token::CaptureLitStr, + lexer::TokenKind::Literal { + kind: lexer::LiteralKind::Str { terminated: true } | lexer::LiteralKind::RawStr { .. }, + .. + }, + ) + | (Token::CaptureIdent, lexer::TokenKind::Ident) => { + **captures.next().unwrap() = self.peek_text(); + self.step(); + return true; + }, + _ => return false, + } + } + } + + #[must_use] + pub fn find_token(&mut self, token: Token<'_>) -> bool { + let mut capture = [].iter_mut(); + while !self.read_token(token, &mut capture) { + self.step(); + if self.at_end() { + return false; + } + } + true + } - match update_mode { - UpdateMode::Check if contents != new_contents => exit_with_failure(), - UpdateMode::Check => (), - UpdateMode::Change => { - if let Err(e) = fs::write(path, new_contents.as_bytes()) { - panic!("Cannot write to `{}`: {e}", path.display()); + #[must_use] + pub fn find_capture_token(&mut self, token: Token<'_>) -> Option<&'txt str> { + let mut res = ""; + let mut capture = &mut res; + let mut capture = slice::from_mut(&mut capture).iter_mut(); + while !self.read_token(token, &mut capture) { + self.step(); + if self.at_end() { + return None; + } + } + Some(res) + } + + #[must_use] + pub fn match_tokens(&mut self, tokens: &[Token<'_>], captures: &mut [&mut &'txt str]) -> bool { + let mut captures = captures.iter_mut(); + tokens.iter().all(|&t| self.read_token(t, &mut captures)) + } +} + +#[expect(clippy::must_use_candidate)] +pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { + match OpenOptions::new().create_new(true).write(true).open(new_name) { + Ok(file) => drop(file), + Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, + Err(ref e) => panic_action(e, ErrAction::Create, new_name), + } + match fs::rename(old_name, new_name) { + Ok(()) => true, + Err(ref e) => { + drop(fs::remove_file(new_name)); + // `NotADirectory` happens on posix when renaming a directory to an existing file. + // Windows will ignore this and rename anyways. + if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::NotADirectory) { + false + } else { + panic_action(e, ErrAction::Rename, old_name); } }, } } -/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters -/// were found, or the missing delimiter if not. -pub(crate) fn replace_region_in_text<'a>( - text: &str, - start: &'a str, - end: &'a str, - mut write_replacement: impl FnMut(&mut String), -) -> Result { - let (text_start, rest) = text.split_once(start).ok_or(start)?; - let (_, text_end) = rest.split_once(end).ok_or(end)?; - - let mut res = String::with_capacity(text.len() + 4096); - res.push_str(text_start); - res.push_str(start); - write_replacement(&mut res); - res.push_str(end); - res.push_str(text_end); - - Ok(res) +#[expect(clippy::must_use_candidate)] +pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { + match fs::create_dir(new_name) { + Ok(()) => {}, + Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, + Err(ref e) => panic_action(e, ErrAction::Create, new_name), + } + // Windows can't reliably rename to an empty directory. + #[cfg(windows)] + drop(fs::remove_dir(new_name)); + match fs::rename(old_name, new_name) { + Ok(()) => true, + Err(ref e) => { + // Already dropped earlier on windows. + #[cfg(not(windows))] + drop(fs::remove_dir(new_name)); + // `NotADirectory` happens on posix when renaming a file to an existing directory. + if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::NotADirectory) { + false + } else { + panic_action(e, ErrAction::Rename, old_name); + } + }, + } +} + +pub fn write_file(path: &Path, contents: &str) { + fs::write(path, contents).unwrap_or_else(|e| panic_action(&e, ErrAction::Write, path)); +} + +#[must_use] +pub fn run_with_output(path: &(impl AsRef + ?Sized), cmd: &mut Command) -> Vec { + fn f(path: &Path, cmd: &mut Command) -> Vec { + match cmd + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .output() + { + Ok(x) => match x.status.exit_ok() { + Ok(()) => x.stdout, + Err(ref e) => panic_action(e, ErrAction::Run, path), + }, + Err(ref e) => panic_action(e, ErrAction::Run, path), + } + } + f(path.as_ref(), cmd) +} + +pub fn run_with_args_split( + mut make_cmd: impl FnMut() -> Command, + mut run_cmd: impl FnMut(&mut Command), + args: impl Iterator>, +) { + let mut cmd = make_cmd(); + let mut len = 0; + for arg in args { + len += arg.as_ref().len(); + cmd.arg(arg); + // Very conservative limit + if len > 10000 { + run_cmd(&mut cmd); + cmd = make_cmd(); + len = 0; + } + } + if len != 0 { + run_cmd(&mut cmd); + } +} + +#[expect(clippy::must_use_candidate)] +pub fn delete_file_if_exists(path: &Path) -> bool { + match fs::remove_file(path) { + Ok(()) => true, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::IsADirectory) => false, + Err(ref e) => panic_action(e, ErrAction::Delete, path), + } +} + +pub fn delete_dir_if_exists(path: &Path) { + match fs::remove_dir_all(path) { + Ok(()) => {}, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound | io::ErrorKind::NotADirectory) => {}, + Err(ref e) => panic_action(e, ErrAction::Delete, path), + } } diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 54347043a13d4..39e4e2e365ea3 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy_lints" -# begin autogenerated version -version = "0.1.87" -# end autogenerated version +version = "0.1.89" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -19,10 +17,7 @@ itertools = "0.12" quine-mc_cluskey = "0.2" regex-syntax = "0.8" serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0", optional = true } -tempfile = { version = "3.3.0", optional = true } toml = "0.7.3" -regex = { version = "1.5", optional = true } unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } semver = "1.0" @@ -31,10 +26,6 @@ url = "2.2" [dev-dependencies] walkdir = "2.3" -[features] -# build clippy with internal lints enabled, off by default -internal = ["serde_json", "tempfile", "regex"] - [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] rustc_private = true diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index 9ae746c13b261..852e48cbcaeec 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; -use rustc_attr_parsing::RustcVersion; +use rustc_attr_data_structures::RustcVersion; use rustc_hir::{HirId, Lit}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index c0ae4960e10d9..5c1c85d39184d 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -2,10 +2,13 @@ use clippy_config::Conf; use clippy_config::types::{ SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, + SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::is_cfg_test; +use rustc_attr_data_structures::AttributeKind; use rustc_hir::{ - AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, UseKind, + AssocItemKind, Attribute, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, Variant, VariantData, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -26,6 +29,11 @@ declare_clippy_lint! { /// implemented in the code. Sometimes this will be referred to as /// "bikeshedding". /// + /// The content of items with a representation clause attribute, such as + /// `#[repr(C)]` will not be checked, as the order of their fields or + /// variants might be dictated by an external API (application binary + /// interface). + /// /// ### Default Ordering and Configuration /// /// As there is no generally applicable rule, and each project may have @@ -36,7 +44,7 @@ declare_clippy_lint! { /// 2. Individual ordering rules per item kind. /// /// The item kinds that can be linted are: - /// - Module (with customized groupings, alphabetical within) + /// - Module (with customized groupings, alphabetical within - configurable) /// - Trait (with customized order of associated items, alphabetical within) /// - Enum, Impl, Struct (purely alphabetical) /// @@ -57,8 +65,31 @@ declare_clippy_lint! { /// | `PascalCase` | "ty_alias", "opaque_ty", "enum", "struct", "union", "trait", "trait_alias", "impl" | /// | `lower_snake_case` | "fn" | /// + /// The groups' names are arbitrary and can be changed to suit the + /// conventions that should be enforced for a specific project. + /// /// All item kinds must be accounted for to create an enforceable linting - /// rule set. + /// rule set. Following are some example configurations that may be useful. + /// + /// Example: *module inclusions and use statements to be at the top* + /// + /// ```toml + /// module-item-order-groupings = [ + /// [ "modules", [ "extern_crate", "mod", "foreign_mod" ], ], + /// [ "use", [ "use", ], ], + /// [ "everything_else", [ "macro", "global_asm", "static", "const", "ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl", "fn", ], ], + /// ] + /// ``` + /// + /// Example: *only consts and statics should be alphabetically ordered* + /// + /// It is also possible to configure a selection of module item groups that + /// should be ordered alphabetically. This may be useful if for example + /// statics and consts should be ordered, but the rest should be left open. + /// + /// ```toml + /// module-items-ordered-within-groupings = ["UPPER_SNAKE_CASE"] + /// ``` /// /// ### Known Problems /// @@ -143,6 +174,7 @@ pub struct ArbitrarySourceItemOrdering { enable_ordering_for_struct: bool, enable_ordering_for_trait: bool, module_item_order_groupings: SourceItemOrderingModuleItemGroupings, + module_items_ordered_within_groupings: SourceItemOrderingWithinModuleItemGroupings, } impl ArbitrarySourceItemOrdering { @@ -157,6 +189,7 @@ impl ArbitrarySourceItemOrdering { enable_ordering_for_struct: conf.source_item_ordering.contains(&Struct), enable_ordering_for_trait: conf.source_item_ordering.contains(&Trait), module_item_order_groupings: conf.module_item_order_groupings.clone(), + module_items_ordered_within_groupings: conf.module_items_ordered_within_groupings.clone(), } } @@ -176,11 +209,7 @@ impl ArbitrarySourceItemOrdering { } /// Produces a linting warning for incorrectly ordered item members. - fn lint_member_name( - cx: &T, - ident: &rustc_span::symbol::Ident, - before_ident: &rustc_span::symbol::Ident, - ) { + fn lint_member_name(cx: &T, ident: &rustc_span::Ident, before_ident: &rustc_span::Ident) { span_lint_and_note( cx, ARBITRARY_SOURCE_ITEM_ORDERING, @@ -191,22 +220,19 @@ impl ArbitrarySourceItemOrdering { ); } - fn lint_member_item(cx: &T, item: &Item<'_>, before_item: &Item<'_>) { - let span = if item.ident.as_str().is_empty() { - &item.span + fn lint_member_item(cx: &T, item: &Item<'_>, before_item: &Item<'_>, msg: &'static str) { + let span = if let Some(ident) = item.kind.ident() { + ident.span } else { - &item.ident.span + item.span }; - let (before_span, note) = if before_item.ident.as_str().is_empty() { - ( - &before_item.span, - "should be placed before the following item".to_owned(), - ) + let (before_span, note) = if let Some(ident) = before_item.kind.ident() { + (ident.span, format!("should be placed before `{}`", ident.as_str(),)) } else { ( - &before_item.ident.span, - format!("should be placed before `{}`", before_item.ident.as_str(),), + before_item.span, + "should be placed before the following item".to_owned(), ) }; @@ -215,14 +241,7 @@ impl ArbitrarySourceItemOrdering { return; } - span_lint_and_note( - cx, - ARBITRARY_SOURCE_ITEM_ORDERING, - *span, - "incorrect ordering of items (must be alphabetically ordered)", - Some(*before_span), - note, - ); + span_lint_and_note(cx, ARBITRARY_SOURCE_ITEM_ORDERING, span, msg, Some(before_span), note); } /// Produces a linting warning for incorrectly ordered trait items. @@ -243,38 +262,49 @@ impl ArbitrarySourceItemOrdering { impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if cx + .tcx + .hir_attrs(item.hir_id()) + .iter() + .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr(..)))) + { + // Do not lint items with a `#[repr]` attribute as their layout may be imposed by an external API. + return; + } match &item.kind { - ItemKind::Enum(enum_def, _generics) if self.enable_ordering_for_enum => { + ItemKind::Enum(_, enum_def, _generics) if self.enable_ordering_for_enum => { let mut cur_v: Option<&Variant<'_>> = None; for variant in enum_def.variants { if variant.span.in_external_macro(cx.sess().source_map()) { continue; } - if let Some(cur_v) = cur_v { - if cur_v.ident.name.as_str() > variant.ident.name.as_str() && cur_v.span != variant.span { - Self::lint_member_name(cx, &variant.ident, &cur_v.ident); - } + if let Some(cur_v) = cur_v + && cur_v.ident.name.as_str() > variant.ident.name.as_str() + && cur_v.span != variant.span + { + Self::lint_member_name(cx, &variant.ident, &cur_v.ident); } cur_v = Some(variant); } }, - ItemKind::Struct(VariantData::Struct { fields, .. }, _generics) if self.enable_ordering_for_struct => { + ItemKind::Struct(_, VariantData::Struct { fields, .. }, _generics) if self.enable_ordering_for_struct => { let mut cur_f: Option<&FieldDef<'_>> = None; for field in *fields { if field.span.in_external_macro(cx.sess().source_map()) { continue; } - if let Some(cur_f) = cur_f { - if cur_f.ident.name.as_str() > field.ident.name.as_str() && cur_f.span != field.span { - Self::lint_member_name(cx, &field.ident, &cur_f.ident); - } + if let Some(cur_f) = cur_f + && cur_f.ident.name.as_str() > field.ident.name.as_str() + && cur_f.span != field.span + { + Self::lint_member_name(cx, &field.ident, &cur_f.ident); } cur_f = Some(field); } }, - ItemKind::Trait(is_auto, _safety, _generics, _generic_bounds, item_ref) + ItemKind::Trait(is_auto, _safety, _ident, _generics, _generic_bounds, item_ref) if self.enable_ordering_for_trait && *is_auto == IsAuto::No => { let mut cur_t: Option<&TraitItemRef> = None; @@ -330,7 +360,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { struct CurItem<'a> { item: &'a Item<'a>, order: usize, - name: String, + name: Option, } let mut cur_t: Option> = None; @@ -347,61 +377,40 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { // as no sorting by source map/line of code has to be applied. // for item in items { + if is_cfg_test(cx.tcx, item.hir_id()) { + continue; + } + if item.span.in_external_macro(cx.sess().source_map()) { continue; } - // The following exceptions (skipping with `continue;`) may not be - // complete, edge cases have not been explored further than what - // appears in the existing code base. - if item.ident.name == rustc_span::symbol::kw::Empty { - if let ItemKind::Impl(_) = item.kind { - // Sorting trait impls for unnamed types makes no sense. - if get_item_name(item).is_empty() { - continue; - } - } else if let ItemKind::ForeignMod { .. } = item.kind { - continue; - } else if let ItemKind::GlobalAsm { .. } = item.kind { + if let Some(ident) = item.kind.ident() { + if ident.name.as_str().starts_with('_') { + // Filters out unnamed macro-like impls for various derives, + // e.g. serde::Serialize or num_derive::FromPrimitive. continue; - } else if let ItemKind::Use(path, use_kind) = item.kind { - if path.segments.is_empty() { - // Use statements that contain braces get caught here. - // They will still be linted internally. + } + + if ident.name == rustc_span::sym::std && item.span.is_dummy() { + if let ItemKind::ExternCrate(None, _) = item.kind { + // Filters the auto-included Rust standard library. continue; - } else if path.segments.len() >= 2 - && (path.segments[0].ident.name == rustc_span::sym::std - || path.segments[0].ident.name == rustc_span::sym::core) - && path.segments[1].ident.name == rustc_span::sym::prelude - { - // Filters the autogenerated prelude use statement. - // e.g. `use std::prelude::rustc_2021` - } else if use_kind == UseKind::Glob { - // Filters glob kinds of uses. - // e.g. `use std::sync::*` - } else { - // This can be used for debugging. - // println!("Unknown autogenerated use statement: {:?}", item); } - continue; + if cfg!(debug_assertions) { + rustc_middle::bug!("unknown item: {item:?}"); + } } - } - - if item.ident.name.as_str().starts_with('_') { - // Filters out unnamed macro-like impls for various derives, - // e.g. serde::Serialize or num_derive::FromPrimitive. + } else if let ItemKind::Impl(_) = item.kind + && get_item_name(item).is_some() + { + // keep going below + } else { continue; } - if item.ident.name == rustc_span::sym::std && item.span.is_dummy() { - if let ItemKind::ExternCrate(None) = item.kind { - // Filters the auto-included Rust standard library. - continue; - } - println!("Unknown item: {item:?}"); - } - let item_kind = convert_module_item_kind(&item.kind); + let grouping_name = self.module_item_order_groupings.grouping_name_of(&item_kind); let module_level_order = self .module_item_order_groupings .module_level_order_of(&item_kind) @@ -411,13 +420,27 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { use std::cmp::Ordering; // Better legibility. match module_level_order.cmp(&cur_t.order) { Ordering::Less => { - Self::lint_member_item(cx, item, cur_t.item); + Self::lint_member_item( + cx, + item, + cur_t.item, + "incorrect ordering of items (module item groupings specify another order)", + ); }, Ordering::Equal if item_kind == SourceItemOrderingModuleItemKind::Use => { // Skip ordering use statements, as these should be ordered by rustfmt. }, - Ordering::Equal if cur_t.name > get_item_name(item) => { - Self::lint_member_item(cx, item, cur_t.item); + Ordering::Equal + if (grouping_name.is_some_and(|grouping_name| { + self.module_items_ordered_within_groupings.ordered_within(grouping_name) + }) && cur_t.name > get_item_name(item)) => + { + Self::lint_member_item( + cx, + item, + cur_t.item, + "incorrect ordering of items (must be alphabetically ordered)", + ); }, Ordering::Equal | Ordering::Greater => { // Nothing to do in this case, they're already in the right order. @@ -492,7 +515,7 @@ fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleIte /// further in the [Rust Reference, Paths Chapter][rust_ref]. /// /// [rust_ref]: https://doc.rust-lang.org/reference/paths.html#crate-1 -fn get_item_name(item: &Item<'_>) -> String { +fn get_item_name(item: &Item<'_>) -> Option { match item.kind { ItemKind::Impl(im) => { if let TyKind::Path(path) = im.self_ty.kind { @@ -512,19 +535,19 @@ fn get_item_name(item: &Item<'_>) -> String { } segs.push(String::new()); - segs.join("!!") + Some(segs.join("!!")) }, QPath::TypeRelative(_, _path_seg) => { // This case doesn't exist in the clippy tests codebase. - String::new() + None }, - QPath::LangItem(_, _) => String::new(), + QPath::LangItem(_, _) => None, } } else { // Impls for anything that isn't a named type can be skipped. - String::new() + None } }, - _ => item.ident.name.as_str().to_owned(), + _ => item.kind.ident().map(|name| name.as_str().to_owned()), } } diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs index 78102772927c0..27e304a848e33 100644 --- a/src/tools/clippy/clippy_lints/src/as_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs @@ -12,17 +12,17 @@ declare_clippy_lint! { /// regardless of whether good alternatives exist or not. If you want more /// precise lints for `as`, please consider using these separate lints: /// - /// - clippy::cast_lossless - /// - clippy::cast_possible_truncation - /// - clippy::cast_possible_wrap - /// - clippy::cast_precision_loss - /// - clippy::cast_sign_loss - /// - clippy::char_lit_as_u8 - /// - clippy::fn_to_numeric_cast - /// - clippy::fn_to_numeric_cast_with_truncation - /// - clippy::ptr_as_ptr - /// - clippy::unnecessary_cast - /// - invalid_reference_casting + /// - `clippy::cast_lossless` + /// - `clippy::cast_possible_truncation` + /// - `clippy::cast_possible_wrap` + /// - `clippy::cast_precision_loss` + /// - `clippy::cast_sign_loss` + /// - `clippy::char_lit_as_u8` + /// - `clippy::fn_to_numeric_cast` + /// - `clippy::fn_to_numeric_cast_with_truncation` + /// - `clippy::ptr_as_ptr` + /// - `clippy::unnecessary_cast` + /// - `invalid_reference_casting` /// /// There is a good explanation the reason why this lint should work in this /// way and how it is useful [in this diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs index c073dee855e20..6f2a6a36a38b7 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs @@ -3,14 +3,13 @@ use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_no use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{is_expr_final_block_expr, path_res}; +use clippy_utils::{is_expr_final_block_expr, path_res, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -68,11 +67,11 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { return; } } - let (message, replacement) = match method_segment.ident.as_str() { - "is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => { + let (message, replacement) = match method_segment.ident.name { + sym::is_ok if type_suitable_to_unwrap(cx, args.type_at(1)) => { ("called `assert!` with `Result::is_ok`", "unwrap") }, - "is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => { + sym::is_err if type_suitable_to_unwrap(cx, args.type_at(0)) => { ("called `assert!` with `Result::is_err`", "unwrap_err") }, _ => return, diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index ab34af7c31745..8b8b42bbf7228 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -3,14 +3,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local}; +use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir; use rustc_middle::ty::{self, Instance, Mutability}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::sym; use rustc_span::{Span, SyntaxContext}; declare_clippy_lint! { @@ -86,9 +85,9 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { && ctxt.is_root() && let which_trait = match fn_name { sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone, - _ if fn_name.as_str() == "to_owned" - && is_diag_trait_item(cx, fn_id, sym::ToOwned) - && self.msrv.meets(cx, msrvs::CLONE_INTO) => + sym::to_owned + if is_diag_trait_item(cx, fn_id, sym::ToOwned) + && self.msrv.meets(cx, msrvs::CLONE_INTO) => { CloneTrait::ToOwned }, @@ -111,8 +110,8 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { // Only suggest if `clone_from`/`clone_into` is explicitly implemented && resolved_assoc_items.in_definition_order().any(|assoc| match which_trait { - CloneTrait::Clone => assoc.name == sym::clone_from, - CloneTrait::ToOwned => assoc.name.as_str() == "clone_into", + CloneTrait::Clone => assoc.name() == sym::clone_from, + CloneTrait::ToOwned => assoc.name() == sym::clone_into, } ) && !clone_source_borrows_from_dest(cx, lhs, rhs.span) @@ -243,7 +242,7 @@ fn build_sugg<'tcx>( // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` Sugg::hir_with_applicability(cx, lhs, "_", app) } - .maybe_par(); + .maybe_paren(); // Determine whether we need to reference the argument to clone_from(). let clone_receiver_type = cx.typeck_results().expr_ty(fn_arg); @@ -284,7 +283,7 @@ fn build_sugg<'tcx>( let rhs_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { // `*lhs = rhs.to_owned()` -> `rhs.clone_into(lhs)` // `*lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, lhs)` - let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", app).maybe_par(); + let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", app).maybe_paren(); let inner_type = cx.typeck_results().expr_ty(ref_expr); // If after unwrapping the dereference, the type is not a mutable reference, we add &mut to make it // deref to a mutable reference. @@ -296,7 +295,7 @@ fn build_sugg<'tcx>( } else { // `lhs = rhs.to_owned()` -> `rhs.clone_into(&mut lhs)` // `lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, &mut lhs)` - Sugg::hir_with_applicability(cx, lhs, "_", app).maybe_par().mut_addr() + Sugg::hir_with_applicability(cx, lhs, "_", app).maybe_paren().mut_addr() }; match call_kind { diff --git a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs index fecf316640636..4d64eec25d273 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs @@ -1,24 +1,23 @@ use super::BLANKET_CLIPPY_RESTRICTION_LINTS; use super::utils::extract_clippy_lint; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::sym; use rustc_ast::MetaItemInner; use rustc_lint::{EarlyContext, Level, LintContext}; +use rustc_span::DUMMY_SP; use rustc_span::symbol::Symbol; -use rustc_span::{DUMMY_SP, sym}; pub(super) fn check(cx: &EarlyContext<'_>, name: Symbol, items: &[MetaItemInner]) { for lint in items { - if let Some(lint_name) = extract_clippy_lint(lint) { - if lint_name.as_str() == "restriction" && name != sym::allow { - span_lint_and_help( - cx, - BLANKET_CLIPPY_RESTRICTION_LINTS, - lint.span(), - "`clippy::restriction` is not meant to be enabled as a group", - None, - "enable the restriction lints you need individually", - ); - } + if name != sym::allow && extract_clippy_lint(lint) == Some(sym::restriction) { + span_lint_and_help( + cx, + BLANKET_CLIPPY_RESTRICTION_LINTS, + lint.span(), + "`clippy::restriction` is not meant to be enabled as a group", + None, + "enable the restriction lints you need individually", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs index cd38aed26a3e0..0edb50be8c778 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs @@ -1,10 +1,10 @@ use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR, unnecessary_clippy_cfg}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, MsrvStack}; +use clippy_utils::sym; use rustc_ast::AttrStyle; use rustc_errors::Applicability; use rustc_lint::EarlyContext; -use rustc_span::sym; pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &MsrvStack) { // check cfg_attr @@ -18,7 +18,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &MsrvStack) { && msrv.meets(msrvs::TOOL_ATTRIBUTES) // check for `rustfmt_skip` and `rustfmt::skip` && let Some(skip_item) = &items[1].meta_item() - && (skip_item.has_name(sym!(rustfmt_skip)) + && (skip_item.has_name(sym::rustfmt_skip) || skip_item .path .segments @@ -73,7 +73,7 @@ fn check_deprecated_cfg_recursively(cx: &EarlyContext<'_>, attr: &rustc_ast::Met } fn check_cargo_clippy_attr(cx: &EarlyContext<'_>, item: &rustc_ast::MetaItem) { - if item.has_name(sym::feature) && item.value_str().is_some_and(|v| v.as_str() == "cargo-clippy") { + if item.has_name(sym::feature) && item.value_str() == Some(sym::cargo_clippy) { span_lint_and_sugg( cx, DEPRECATED_CLIPPY_CFG_ATTR, diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs index d3153ec6613b5..bd6459d6f9dbc 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs @@ -1,15 +1,16 @@ use super::DEPRECATED_SEMVER; use clippy_utils::diagnostics::span_lint; +use clippy_utils::sym; use rustc_ast::{LitKind, MetaItemLit}; use rustc_lint::EarlyContext; use rustc_span::Span; use semver::Version; pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) { - if let LitKind::Str(is, _) = lit.kind { - if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() { - return; - } + if let LitKind::Str(is, _) = lit.kind + && (is == sym::TBD || Version::parse(is.as_str()).is_ok()) + { + return; } span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs index 2ddbc7a6a76dc..a851daaede71b 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs @@ -36,14 +36,14 @@ fn check_duplicated_attr( } let Some(ident) = attr.ident() else { return }; let name = ident.name; - if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented || name == sym::reason { + if name == sym::doc || name == sym::cfg_attr_trace || name == sym::rustc_on_unimplemented || name == sym::reason { // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg // conditions are the same. // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. return; } if let Some(direct_parent) = parent.last() - && ["cfg", "cfg_attr"].contains(&direct_parent.as_str()) + && direct_parent == sym::cfg_trace.as_str() && [sym::all, sym::not, sym::any].contains(&name) { // FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 2b59c218d57a1..9a1242980418c 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -14,9 +14,10 @@ mod useless_attribute; mod utils; use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv, MsrvStack}; -use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind}; -use rustc_hir::{ImplItem, Item, TraitItem}; +use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind}; +use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::sym; @@ -448,6 +449,31 @@ declare_clippy_lint! { "duplicated attribute" } +declare_clippy_lint! { + /// ### What it does + /// Checks for ignored tests without messages. + /// + /// ### Why is this bad? + /// The reason for ignoring the test may not be obvious. + /// + /// ### Example + /// ```no_run + /// #[test] + /// #[ignore] + /// fn test() {} + /// ``` + /// Use instead: + /// ```no_run + /// #[test] + /// #[ignore = "Some good reason"] + /// fn test() {} + /// ``` + #[clippy::version = "1.88.0"] + pub IGNORE_WITHOUT_REASON, + pedantic, + "ignored tests without messages" +} + pub struct Attributes { msrv: Msrv, } @@ -465,22 +491,24 @@ impl Attributes { impl<'tcx> LateLintPass<'tcx> for Attributes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - if is_relevant_item(cx, item) { - inline_always::check(cx, item.span, item.ident.name, attrs); + let attrs = cx.tcx.hir_attrs(item.hir_id()); + if let ItemKind::Fn { ident, .. } = item.kind + && is_relevant_item(cx, item) + { + inline_always::check(cx, item.span, ident.name, attrs); } repr_attributes::check(cx, item.span, attrs, self.msrv); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { if is_relevant_impl(cx, item) { - inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())); + inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir_attrs(item.hir_id())); } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if is_relevant_trait(cx, item) { - inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())); + inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir_attrs(item.hir_id())); } } } @@ -530,6 +558,7 @@ impl_lint_pass!(PostExpansionEarlyAttributes => [ ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON, DEPRECATED_SEMVER, + IGNORE_WITHOUT_REASON, USELESS_ATTRIBUTE, BLANKET_CLIPPY_RESTRICTION_LINTS, SHOULD_PANIC_WITHOUT_EXPECT, @@ -544,28 +573,27 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { } fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { - if let Some(items) = &attr.meta_item_list() { - if let Some(ident) = attr.ident() { - if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { - allow_attributes::check(cx, attr); - } - if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) + if let Some(items) = &attr.meta_item_list() + && let Some(ident) = attr.ident() + { + if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { + allow_attributes::check(cx, attr); + } + if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { + allow_attributes_without_reason::check(cx, ident.name, items, attr); + } + if is_lint_level(ident.name, attr.id) { + blanket_clippy_restriction_lints::check(cx, ident.name, items); + } + if items.is_empty() || !attr.has_name(sym::deprecated) { + return; + } + for item in items { + if let MetaItemInner::MetaItem(mi) = &item + && let MetaItemKind::NameValue(lit) = &mi.kind + && mi.has_name(sym::since) { - allow_attributes_without_reason::check(cx, ident.name, items, attr); - } - if is_lint_level(ident.name, attr.id) { - blanket_clippy_restriction_lints::check(cx, ident.name, items); - } - if items.is_empty() || !attr.has_name(sym::deprecated) { - return; - } - for item in items { - if let MetaItemInner::MetaItem(mi) = &item - && let MetaItemKind::NameValue(lit) = &mi.kind - && mi.has_name(sym::since) - { - deprecated_semver::check(cx, item.span(), lit); - } + deprecated_semver::check(cx, item.span(), lit); } } } @@ -573,6 +601,22 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { if attr.has_name(sym::should_panic) { should_panic_without_expect::check(cx, attr); } + + if attr.has_name(sym::ignore) + && match &attr.kind { + AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrArgs::Eq { .. }), + AttrKind::DocComment(..) => true, + } + { + span_lint_and_help( + cx, + IGNORE_WITHOUT_REASON, + attr.span, + "`#[ignore]` without reason", + None, + "add a reason with `= \"..\"`", + ); + } } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &'_ ast::Item) { diff --git a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs index e5cfbaf952a70..05d8a8c26d1c1 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs @@ -1,4 +1,4 @@ -use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr}; +use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr}; use rustc_hir::Attribute; use rustc_lint::LateContext; use rustc_span::Span; @@ -30,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], diag.warn( "unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI", ) - .help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`") + .help("qualify the desired ABI explicitly via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`") .span_label(packed_span, "`packed` representation set here"); }, ); diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index e3e081ce08e9f..4059f9603c33c 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -2,10 +2,10 @@ use super::USELESS_ATTRIBUTE; use super::utils::{is_lint_level, is_word, namespace_and_lint}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{SpanRangeExt, first_line_of_span}; +use clippy_utils::sym; use rustc_ast::{Attribute, Item, ItemKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, LintContext}; -use rustc_span::sym; pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use)); @@ -14,75 +14,75 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { if attr.span.in_external_macro(cx.sess().source_map()) { return; } - if let Some(lint_list) = &attr.meta_item_list() { - if attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) { - for lint in lint_list { - match item.kind { - ItemKind::Use(..) => { - let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else { - return; - }; + if let Some(lint_list) = &attr.meta_item_list() + && attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) + { + for lint in lint_list { + match item.kind { + ItemKind::Use(..) => { + let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else { + return; + }; - if namespace.is_none() - && matches!( - name.as_str(), - "ambiguous_glob_reexports" - | "dead_code" - | "deprecated" - | "hidden_glob_reexports" - | "unreachable_pub" - | "unused" - | "unused_braces" - | "unused_import_braces" - | "unused_imports" - ) - { - return; - } + if namespace.is_none() + && matches!( + name, + sym::ambiguous_glob_reexports + | sym::dead_code + | sym::deprecated + | sym::hidden_glob_reexports + | sym::unreachable_pub + | sym::unused + | sym::unused_braces + | sym::unused_import_braces + | sym::unused_imports + ) + { + return; + } - if namespace == Some(sym::clippy) - && matches!( - name.as_str(), - "wildcard_imports" - | "enum_glob_use" - | "redundant_pub_crate" - | "macro_use_imports" - | "unsafe_removed_from_name" - | "module_name_repetitions" - | "single_component_path_imports" - | "disallowed_types" - | "unused_trait_names" - ) - { - return; - } - }, - ItemKind::ExternCrate(..) => { - if is_word(lint, sym::unused_imports) && skip_unused_imports { - return; - } - if is_word(lint, sym!(unused_extern_crates)) { - return; - } - }, - _ => {}, - } + if namespace == Some(sym::clippy) + && matches!( + name, + sym::wildcard_imports + | sym::enum_glob_use + | sym::redundant_pub_crate + | sym::macro_use_imports + | sym::unsafe_removed_from_name + | sym::module_name_repetitions + | sym::single_component_path_imports + | sym::disallowed_types + | sym::unused_trait_names + ) + { + return; + } + }, + ItemKind::ExternCrate(..) => { + if is_word(lint, sym::unused_imports) && skip_unused_imports { + return; + } + if is_word(lint, sym::unused_extern_crates) { + return; + } + }, + _ => {}, } - let line_span = first_line_of_span(cx, attr.span); + } + let line_span = first_line_of_span(cx, attr.span); - if let Some(src) = line_span.get_source_text(cx) { - if src.contains("#[") { - #[expect(clippy::collapsible_span_lint_calls)] - span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| { - diag.span_suggestion( - line_span, - "if you just forgot a `!`, use", - src.replacen("#[", "#![", 1), - Applicability::MaybeIncorrect, - ); - }); - } - } + if let Some(src) = line_span.get_source_text(cx) + && src.contains("#[") + { + #[expect(clippy::collapsible_span_lint_calls)] + span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| { + diag.span_suggestion( + line_span, + "if you just forgot a `!`, use", + src.replacen("#[", "#![", 1), + Applicability::MaybeIncorrect, + ); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/attrs/utils.rs b/src/tools/clippy/clippy_lints/src/attrs/utils.rs index 0e650e4939252..a5ce2137bffeb 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/utils.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/utils.rs @@ -24,7 +24,7 @@ pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if let ItemKind::Fn { body: eid, .. } = item.kind { is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value) } else { - true + false } } diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs index 92a0c7f9acbcd..31cc004f6855d 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{match_def_path, paths}; +use clippy_utils::paths::{self, PathNS}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_lint::{LateContext, LateLintPass}; @@ -179,9 +179,15 @@ pub struct AwaitHolding { impl AwaitHolding { pub(crate) fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - Self { - def_ids: create_disallowed_map(tcx, &conf.await_holding_invalid_types), - } + let (def_ids, _) = create_disallowed_map( + tcx, + &conf.await_holding_invalid_types, + PathNS::Type, + crate::disallowed_types::def_kind_predicate, + "type", + false, + ); + Self { def_ids } } } @@ -192,10 +198,9 @@ impl<'tcx> LateLintPass<'tcx> for AwaitHolding { def_id, .. }) = expr.kind + && let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id) { - if let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id) { - self.check_interior_types(cx, coroutine_layout); - } + self.check_interior_types(cx, coroutine_layout); } } } @@ -271,12 +276,10 @@ fn emit_invalid_type( } fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { - cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id) - || cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id) - || cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id) - || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) + match cx.tcx.get_diagnostic_name(def_id) { + Some(name) => matches!(name, sym::MutexGuard | sym::RwLockReadGuard | sym::RwLockWriteGuard), + None => paths::PARKING_LOT_GUARDS.iter().any(|guard| guard.matches(cx, def_id)), + } } fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs index aab0af0d743b9..011962846cb30 100644 --- a/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs +++ b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_block_with_applicability; -use clippy_utils::{higher, is_from_proc_macro}; +use clippy_utils::{contains_return, higher, is_from_proc_macro}; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -86,6 +86,13 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions { if expr.span.from_expansion() || ex.span.from_expansion() { return; } + + // Linting should not be triggered to cases where `return` is included in the condition. + // #9911 + if contains_return(block.expr) { + return; + } + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs index adac2f27ea8cf..ae36bb76117d7 100644 --- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; use clippy_utils::sugg::Sugg; +use clippy_utils::sym; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -53,10 +54,10 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) - .not_trait() .filter(|trait_id| implements_trait(cx, ty, *trait_id, &[])) .and_then(|trait_id| { - cx.tcx.associated_items(trait_id).find_by_name_and_kind( + cx.tcx.associated_items(trait_id).find_by_ident_and_kind( cx.tcx, Ident::from_str("Output"), - ty::AssocKind::Type, + ty::AssocTag::Type, trait_id, ) }) @@ -73,10 +74,9 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; - let macro_name = cx.tcx.item_name(macro_call.def_id); - let eq_macro = match macro_name.as_str() { - "assert_eq" | "debug_assert_eq" => true, - "assert_ne" | "debug_assert_ne" => false, + let eq_macro = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::assert_eq_macro | sym::debug_assert_eq_macro) => true, + Some(sym::assert_ne_macro | sym::debug_assert_ne_macro) => false, _ => return, }; let Some((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { @@ -115,6 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { return; } + let macro_name = cx.tcx.item_name(macro_call.def_id); let macro_name = macro_name.as_str(); let non_eq_mac = ¯o_name[..macro_name.len() - 3]; span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs index 612712d16843c..129e774784061 100644 --- a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs +++ b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::HasSession; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_else_clause, is_in_const_context}; +use clippy_utils::{higher, is_else_clause, is_in_const_context, span_contains_comment}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -46,18 +47,25 @@ declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let ExprKind::If(cond, then, Some(else_)) = expr.kind - && matches!(cond.kind, ExprKind::DropTemps(_)) + if !expr.span.from_expansion() + && let Some(higher::If { + cond, + then, + r#else: Some(r#else), + }) = higher::If::hir(expr) && let Some(then_lit) = as_int_bool_lit(then) - && let Some(else_lit) = as_int_bool_lit(else_) + && let Some(else_lit) = as_int_bool_lit(r#else) && then_lit != else_lit - && !expr.span.from_expansion() && !is_in_const_context(cx) { let ty = cx.typeck_results().expr_ty(then); - let mut applicability = Applicability::MachineApplicable; + let mut applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; let snippet = { - let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); + let mut sugg = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); if !then_lit { sugg = !sugg; } @@ -72,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { s }; - let into_snippet = snippet.clone().maybe_par(); + let into_snippet = snippet.clone().maybe_paren(); let as_snippet = snippet.as_ty(ty); span_lint_and_then( @@ -91,10 +99,11 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { } } -fn as_int_bool_lit(e: &Expr<'_>) -> Option { - if let ExprKind::Block(b, _) = e.kind +fn as_int_bool_lit(expr: &Expr<'_>) -> Option { + if let ExprKind::Block(b, _) = expr.kind && b.stmts.is_empty() && let Some(e) = b.expr + && !e.span.from_expansion() && let ExprKind::Lit(lit) = e.kind && let LitKind::Int(x, _) = lit.node { diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 48b5d4da88860..7c6fd91ca67f7 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -6,14 +6,14 @@ use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use rustc_ast::ast::LitKind; -use rustc_attr_parsing::RustcVersion; +use rustc_attr_data_structures::RustcVersion; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; +use rustc_span::{Span, SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does @@ -199,7 +199,7 @@ fn check_simplify_not(cx: &LateContext<'_>, msrv: Msrv, expr: &Expr<'_>) { && !expr.span.from_expansion() && !inner.span.from_expansion() && let Some(suggestion) = simplify_not(cx, msrv, inner) - && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow + && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).level != Level::Allow { use clippy_utils::sugg::{Sugg, has_enclosing_paren}; let maybe_par = if let Some(sug) = Sugg::hir_opt(cx, inner) { @@ -242,11 +242,11 @@ struct Hir2Qmm<'a, 'tcx, 'v> { impl<'v> Hir2Qmm<'_, '_, 'v> { fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec) -> Result, String> { for a in a { - if let ExprKind::Binary(binop, lhs, rhs) = &a.kind { - if binop.node == op { - v = self.extract(op, &[lhs, rhs], v)?; - continue; - } + if let ExprKind::Binary(binop, lhs, rhs) = &a.kind + && binop.node == op + { + v = self.extract(op, &[lhs, rhs], v)?; + continue; } v.push(self.run(a)?); } @@ -349,9 +349,13 @@ impl SuggestContext<'_, '_, '_> { if let Some(str) = simplify_not(self.cx, self.msrv, terminal) { self.output.push_str(&str); } else { - self.output.push('!'); - self.output - .push_str(&Sugg::hir_opt(self.cx, terminal)?.maybe_par().to_string()); + let mut app = Applicability::MachineApplicable; + let snip = Sugg::hir_with_context(self.cx, terminal, SyntaxContext::root(), "", &mut app); + // Ignore the case If the expression is inside a macro expansion, or the default snippet is used + if app != Applicability::MachineApplicable { + return None; + } + self.output.push_str(&(!snip).to_string()); } }, True | False | Not(_) => { @@ -414,12 +418,12 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio let lhs_snippet = lhs.span.get_source_text(cx)?; let rhs_snippet = rhs.span.get_source_text(cx)?; - if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) { - if let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) { - // e.g. `(a as u64) < b`. Without the parens the `<` is - // interpreted as a start of generic arguments for `u64` - return Some(format!("({lhs_snippet}){op}{rhs_snippet}")); - } + if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) + && let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) + { + // e.g. `(a as u64) < b`. Without the parens the `<` is + // interpreted as a start of generic arguments for `u64` + return Some(format!("({lhs_snippet}){op}{rhs_snippet}")); } Some(format!("{lhs_snippet}{op}{rhs_snippet}")) @@ -605,7 +609,7 @@ impl<'tcx> NonminimalBoolVisitor<'_, 'tcx> { } } let nonminimal_bool_lint = |mut suggestions: Vec<_>| { - if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).0 != Level::Allow { + if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).level != Level::Allow { suggestions.sort(); span_lint_hir_and_then( self.cx, diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs index 8892a9e6b6b08..7cde007a9b66d 100644 --- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs +++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs @@ -2,7 +2,7 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed}; +use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed, is_mutable}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -73,6 +73,9 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { } }) && !is_from_proc_macro(cx, e) + && let e_ty = cx.typeck_results().expr_ty_adjusted(e) + // check if the reference is coercing to a mutable reference + && (!matches!(e_ty.kind(), ty::Ref(_, _, Mutability::Mut)) || is_mutable(cx, deref_target)) && let Some(deref_text) = deref_target.span.get_source_text(cx) { span_lint_and_then( @@ -90,10 +93,10 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { // has deref trait -> give 2 help // doesn't have deref trait -> give 1 help - if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() { - if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) { - return; - } + if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() + && !implements_trait(cx, *inner_ty, deref_trait_id, &[]) + { + return; } diag.span_suggestion( diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs index d143629da3a0d..ad0a4f8cdf35a 100644 --- a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs @@ -1,12 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::Msrv; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::{is_lint_allowed, msrvs, std_or_core}; +use clippy_utils::{get_parent_expr, is_expr_temporary_value, is_lint_allowed, msrvs, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, AutoBorrow}; use rustc_span::BytePos; use super::BORROW_AS_PTR; @@ -25,20 +25,11 @@ pub(super) fn check<'tcx>( let mut app = Applicability::MachineApplicable; let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0; // Fix #9884 - if !e.is_place_expr(|base| { - cx.typeck_results() - .adjustments() - .get(base.hir_id) - .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) - }) { + if is_expr_temporary_value(cx, e) { return false; } let (suggestion, span) = if msrv.meets(cx, msrvs::RAW_REF_OP) { - let operator_kind = match mutability { - Mutability::Not => "const", - Mutability::Mut => "mut", - }; // Make sure that the span to be replaced doesn't include parentheses, that could break the // suggestion. let span = if has_enclosing_paren(snippet_with_applicability(cx, expr.span, "", &mut app)) { @@ -48,7 +39,7 @@ pub(super) fn check<'tcx>( } else { expr.span }; - (format!("&raw {operator_kind} {snip}"), span) + (format!("&raw {} {snip}", mutability.ptr_str()), span) } else { let Some(std_or_core) = std_or_core(cx) else { return false; @@ -65,3 +56,25 @@ pub(super) fn check<'tcx>( } false } + +/// Check for an implicit cast from reference to raw pointer outside an explicit `as`. +pub(super) fn check_implicit_cast(cx: &LateContext<'_>, expr: &Expr<'_>) { + if !expr.span.from_expansion() + && let ExprKind::AddrOf(BorrowKind::Ref, _, pointee) = expr.kind + && !matches!(get_parent_expr(cx, expr).map(|e| e.kind), Some(ExprKind::Cast(..))) + && let [deref, borrow] = cx.typeck_results().expr_adjustments(expr) + && matches!(deref.kind, Adjust::Deref(..)) + && let Adjust::Borrow(AutoBorrow::RawPtr(mutability)) = borrow.kind + // Do not suggest taking a raw pointer to a temporary value + && !is_expr_temporary_value(cx, pointee) + { + span_lint_and_then(cx, BORROW_AS_PTR, expr.span, "implicit borrow as raw pointer", |diag| { + diag.span_suggestion_verbose( + expr.span.until(pointee.span), + "use a raw pointer instead", + format!("&raw {} ", mutability.ptr_str()), + Applicability::MachineApplicable, + ); + }); + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 8b3529e84fc6e..ba31a51f738a6 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -19,7 +20,7 @@ pub(super) fn check( if let ty::Int(from) = cast_from.kind() && let ty::Uint(to) = cast_to.kind() && let ExprKind::MethodCall(method_path, receiver, [], _) = cast_expr.kind - && method_path.ident.name.as_str() == "abs" + && method_path.ident.name == sym::abs && msrv.meets(cx, msrvs::UNSIGNED_ABS) { let span = if from.bit_width() == to.bit_width() { @@ -36,7 +37,7 @@ pub(super) fn check( span, format!("casting the result of `{cast_from}::abs()` to {cast_to}"), "replace with", - format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_par()), + format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_paren()), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs index 3ae43732dc03f..0f066fae11844 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs @@ -42,7 +42,7 @@ pub(super) fn check( diag.span_suggestion_verbose( expr.span, "use `Into::into` instead", - format!("{}.into()", from_sugg.maybe_par()), + format!("{}.into()", from_sugg.maybe_paren()), applicability, ); }, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index ca973f4bb1aae..4120e5c8cb7d0 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -1,9 +1,9 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::expr_or_init; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; +use clippy_utils::{expr_or_init, sym}; use rustc_abi::IntegerType; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::{DefKind, Res}; @@ -56,7 +56,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b if signed { return nbits; } - let max_bits = if method.ident.as_str() == "min" { + let max_bits = if method.ident.name == sym::min { get_constant_bits(cx, right) } else { None @@ -64,16 +64,16 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::MAX)) }, ExprKind::MethodCall(method, _, [lo, hi], _) => { - if method.ident.as_str() == "clamp" { + if method.ident.name == sym::clamp //FIXME: make this a diagnostic item - if let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) { - return lo_bits.max(hi_bits); - } + && let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) + { + return lo_bits.max(hi_bits); } nbits }, ExprKind::MethodCall(method, _value, [], _) => { - if method.ident.name.as_str() == "signum" { + if method.ident.name == sym::signum { 0 // do not lint if cast comes from a `signum` function } else { nbits @@ -185,7 +185,7 @@ fn offer_suggestion( ) { let cast_to_snip = snippet(cx, cast_to_span, ".."); let suggestion = if cast_to_snip == "_" { - format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_par()) + format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_paren()) } else { format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, "..")) }; diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index 57a135abc2e2b..01020f3eee21e 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_c_void; -use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant}; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, sym}; use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; use super::CAST_PTR_ALIGNMENT; @@ -19,16 +18,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx.typeck_results().expr_ty(expr), ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind { - if method_path.ident.name.as_str() == "cast" - && let Some(generic_args) = method_path.args - && let [GenericArg::Type(cast_to)] = generic_args.args - // There probably is no obvious reason to do this, just to be consistent with `as` cases. - && !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty()) - { - let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); - lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } + } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind + && method_path.ident.name == sym::cast + && let Some(generic_args) = method_path.args + && let [GenericArg::Type(cast_to)] = generic_args.args + // There probably is no obvious reason to do this, just to be consistent with `as` cases. + && !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty()) + { + let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index c8abf9dac9af5..9a1ad8a747386 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -4,10 +4,11 @@ use std::ops::ControlFlow; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{method_chain_args, sext}; +use clippy_utils::{method_chain_args, sext, sym}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; +use rustc_span::Symbol; use super::CAST_SIGN_LOSS; @@ -16,24 +17,24 @@ use super::CAST_SIGN_LOSS; /// /// Methods that can overflow and return a negative value must not be included in this list, /// because casting their return values can still result in sign loss. -const METHODS_RET_POSITIVE: &[&str] = &[ - "checked_abs", - "saturating_abs", - "isqrt", - "checked_isqrt", - "rem_euclid", - "checked_rem_euclid", - "wrapping_rem_euclid", +const METHODS_RET_POSITIVE: &[Symbol] = &[ + sym::checked_abs, + sym::saturating_abs, + sym::isqrt, + sym::checked_isqrt, + sym::rem_euclid, + sym::checked_rem_euclid, + sym::wrapping_rem_euclid, ]; /// A list of methods that act like `pow()`. See `pow_call_result_sign()` for details. /// /// Methods that can overflow and return a negative value must not be included in this list, /// because casting their return values can still result in sign loss. -const METHODS_POW: &[&str] = &["pow", "saturating_pow", "checked_pow"]; +const METHODS_POW: &[Symbol] = &[sym::pow, sym::saturating_pow, sym::checked_pow]; /// A list of methods that act like `unwrap()`, and don't change the sign of the inner value. -const METHODS_UNWRAP: &[&str] = &["unwrap", "unwrap_unchecked", "expect", "into_ok"]; +const METHODS_UNWRAP: &[Symbol] = &[sym::unwrap, sym::unwrap_unchecked, sym::expect, sym::into_ok]; pub(super) fn check<'cx>( cx: &LateContext<'cx>, @@ -129,7 +130,7 @@ fn expr_sign<'cx, 'tcx>(cx: &LateContext<'cx>, mut expr: &'tcx Expr<'tcx>, ty: i // Calling on methods that always return non-negative values. if let ExprKind::MethodCall(path, caller, args, ..) = expr.kind { - let mut method_name = path.ident.name.as_str(); + let mut method_name = path.ident.name; // Peel unwrap(), expect(), etc. while let Some(&found_name) = METHODS_UNWRAP.iter().find(|&name| &method_name == name) @@ -138,7 +139,7 @@ fn expr_sign<'cx, 'tcx>(cx: &LateContext<'cx>, mut expr: &'tcx Expr<'tcx>, ty: i { // The original type has changed, but we can't use `ty` here anyway, because it has been // moved. - method_name = inner_path.ident.name.as_str(); + method_name = inner_path.ident.name; expr = recv; } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs index c48f253606dcc..a5b295c88b1c7 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -21,42 +21,41 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) start_ty, end_ty, }) = expr_cast_chain_tys(cx, expr) + && let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) { - if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) { - let from_size = from_layout.size.bytes(); - let to_size = to_layout.size.bytes(); - if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) { - span_lint_and_then( - cx, - CAST_SLICE_DIFFERENT_SIZES, - expr.span, - format!( - "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count", - start_ty.ty, end_ty.ty, - ), - |diag| { - let ptr_snippet = source::snippet(cx, left_cast.span, ".."); + let from_size = from_layout.size.bytes(); + let to_size = to_layout.size.bytes(); + if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) { + span_lint_and_then( + cx, + CAST_SLICE_DIFFERENT_SIZES, + expr.span, + format!( + "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count", + start_ty.ty, end_ty.ty, + ), + |diag| { + let ptr_snippet = source::snippet(cx, left_cast.span, ".."); - let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl { - Mutability::Mut => ("_mut", "mut"), - Mutability::Not => ("", "const"), - }; - let sugg = format!( - "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)", - // get just the ty from the TypeAndMut so that the printed type isn't something like `mut - // T`, extract just the `T` - end_ty.ty - ); + let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl { + Mutability::Mut => ("_mut", "mut"), + Mutability::Not => ("", "const"), + }; + let sugg = format!( + "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)", + // get just the ty from the TypeAndMut so that the printed type isn't something like `mut + // T`, extract just the `T` + end_ty.ty + ); - diag.span_suggestion( - expr.span, - format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), - sugg, - rustc_errors::Applicability::HasPlaceholders, - ); - }, - ); - } + diag.span_suggestion( + expr.span, + format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), + sugg, + rustc_errors::Applicability::HasPlaceholders, + ); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs new file mode 100644 index 0000000000000..769cc120c9504 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs @@ -0,0 +1,89 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, GenericArg, Ty}; +use rustc_span::Symbol; +use rustc_span::def_id::DefId; + +use super::CONFUSING_METHOD_TO_NUMERIC_CAST; + +fn get_primitive_ty_name(ty: Ty<'_>) -> Option<&'static str> { + match ty.kind() { + ty::Char => Some("char"), + ty::Int(int) => Some(int.name_str()), + ty::Uint(uint) => Some(uint.name_str()), + ty::Float(float) => Some(float.name_str()), + _ => None, + } +} + +fn get_const_name_and_ty_name( + cx: &LateContext<'_>, + method_name: Symbol, + method_def_id: DefId, + generics: &[GenericArg<'_>], +) -> Option<(&'static str, &'static str)> { + let diagnostic_name = cx.tcx.get_diagnostic_name(method_def_id); + + let ty_name = if diagnostic_name.is_some_and(|diag| diag == sym::cmp_ord_min || diag == sym::cmp_ord_max) { + // We get the type on which the `min`/`max` method of the `Ord` trait is implemented. + if let [ty] = generics + && let Some(ty) = ty.as_type() + { + get_primitive_ty_name(ty)? + } else { + return None; + } + } else if let Some(impl_id) = cx.tcx.impl_of_method(method_def_id) + && let Some(ty_name) = get_primitive_ty_name(cx.tcx.type_of(impl_id).instantiate_identity()) + && matches!( + method_name, + sym::min | sym::max | sym::minimum | sym::maximum | sym::min_value | sym::max_value + ) + { + ty_name + } else { + return None; + }; + + let const_name = if matches!(method_name, sym::max | sym::maximum | sym::max_value) { + "MAX" + } else { + "MIN" + }; + Some((const_name, ty_name)) +} + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + // We allow casts from any function type to any function type. + match cast_to.kind() { + ty::FnDef(..) | ty::FnPtr(..) => return, + _ => { /* continue to checks */ }, + } + + if let ty::FnDef(def_id, generics) = cast_from.kind() + && let Some(method_name) = cx.tcx.opt_item_name(*def_id) + && let Some((const_name, ty_name)) = get_const_name_and_ty_name(cx, method_name, *def_id, generics.as_slice()) + { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); + + span_lint_and_then( + cx, + CONFUSING_METHOD_TO_NUMERIC_CAST, + expr.span, + format!("casting function pointer `{from_snippet}` to `{cast_to}`"), + |diag| { + diag.span_suggestion_verbose( + expr.span, + "did you mean to use the associated constant?", + format!("{ty_name}::{const_name} as {cast_to}"), + applicability, + ); + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs new file mode 100644 index 0000000000000..61dfc0fc0425e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -0,0 +1,79 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::{expr_or_init, path_def_id, paths, std_or_core}; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind}; +use rustc_lint::LateContext; +use rustc_span::source_map::Spanned; + +use super::MANUAL_DANGLING_PTR; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { + if let TyKind::Ptr(ref ptr_ty) = to.kind { + let init_expr = expr_or_init(cx, from); + if is_expr_const_aligned(cx, init_expr, ptr_ty.ty) + && let Some(std_or_core) = std_or_core(cx) + { + let sugg_fn = match ptr_ty.mutbl { + Mutability::Not => "ptr::dangling", + Mutability::Mut => "ptr::dangling_mut", + }; + + let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind { + format!("{std_or_core}::{sugg_fn}()") + } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_source_text(cx) { + format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") + } else { + return; + }; + + span_lint_and_sugg( + cx, + MANUAL_DANGLING_PTR, + expr.span, + "manual creation of a dangling pointer", + "use", + sugg, + Applicability::MachineApplicable, + ); + } + } +} + +// Checks if the given expression is a call to `align_of` whose generic argument matches the target +// type, or a positive constant literal that matches the target type's alignment. +fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> bool { + match expr.kind { + ExprKind::Call(fun, _) => is_align_of_call(cx, fun, to), + ExprKind::Lit(lit) => is_literal_aligned(cx, lit, to), + _ => false, + } +} + +fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool { + if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind + && let Some(fun_id) = path_def_id(cx, fun) + && paths::ALIGN_OF.matches(cx, fun_id) + && let Some(args) = path.segments.last().and_then(|seg| seg.args) + && let [GenericArg::Type(generic_ty)] = args.args + { + let typeck = cx.typeck_results(); + return typeck.node_type(generic_ty.hir_id) == typeck.node_type(to.hir_id); + } + false +} + +fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned, to: &Ty<'_>) -> bool { + let LitKind::Int(val, _) = lit.node else { return false }; + if val == 0 { + return false; + } + let to_mid_ty = cx.typeck_results().node_type(to.hir_id); + cx.tcx + .layout_of(cx.typing_env().as_query_input(to_mid_ty)) + .is_ok_and(|layout| { + let align = u128::from(layout.align.abi.bytes()); + u128::from(val) <= align + }) +} diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index dc2a1fa85bf5c..daae9a8bb0850 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -14,9 +14,11 @@ mod cast_sign_loss; mod cast_slice_different_sizes; mod cast_slice_from_raw_parts; mod char_lit_as_u8; +mod confusing_method_to_numeric_cast; mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; +mod manual_dangling_ptr; mod ptr_as_ptr; mod ptr_cast_constness; mod ref_as_ptr; @@ -71,7 +73,7 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// let y: i8 = -1; - /// y as u128; // will return 18446744073709551615 + /// y as u64; // will return 18446744073709551615 /// ``` #[clippy::version = "pre 1.29.0"] pub CAST_SIGN_LOSS, @@ -759,6 +761,58 @@ declare_clippy_lint! { "detects `as *mut _` and `as *const _` conversion" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of small constant literals or `mem::align_of` results to raw pointers. + /// + /// ### Why is this bad? + /// This creates a dangling pointer and is better expressed as + /// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}. + /// + /// ### Example + /// ```no_run + /// let ptr = 4 as *const u32; + /// let aligned = std::mem::align_of::() as *const u32; + /// let mut_ptr: *mut i64 = 8 as *mut _; + /// ``` + /// Use instead: + /// ```no_run + /// let ptr = std::ptr::dangling::(); + /// let aligned = std::ptr::dangling::(); + /// let mut_ptr: *mut i64 = std::ptr::dangling_mut(); + /// ``` + #[clippy::version = "1.88.0"] + pub MANUAL_DANGLING_PTR, + style, + "casting small constant literals to pointers to create dangling pointers" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of a primitive method pointer like `max`/`min` to any integer type. + /// + /// ### Why restrict this? + /// Casting a function pointer to an integer can have surprising results and can occur + /// accidentally if parentheses are omitted from a function call. If you aren't doing anything + /// low-level with function pointers then you can opt out of casting functions to integers in + /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function + /// pointer casts in your code. + /// + /// ### Example + /// ```no_run + /// let _ = u16::max as usize; + /// ``` + /// + /// Use instead: + /// ```no_run + /// let _ = u16::MAX as usize; + /// ``` + #[clippy::version = "1.86.0"] + pub CONFUSING_METHOD_TO_NUMERIC_CAST, + suspicious, + "casting a primitive method pointer to any integer type" +} + pub struct Casts { msrv: Msrv, } @@ -795,6 +849,8 @@ impl_lint_pass!(Casts => [ ZERO_PTR, REF_AS_PTR, AS_POINTER_UNDERSCORE, + MANUAL_DANGLING_PTR, + CONFUSING_METHOD_TO_NUMERIC_CAST, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -819,10 +875,15 @@ impl<'tcx> LateLintPass<'tcx> for Casts { ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to); + confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); + if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) { + manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir); + } + if cast_to.is_numeric() { cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { @@ -846,6 +907,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } + if self.msrv.meets(cx, msrvs::RAW_REF_OP) { + borrow_as_ptr::check_implicit_cast(cx, expr); + } cast_ptr_alignment::check(cx, expr); char_lit_as_u8::check(cx, expr); ptr_as_ptr::check(cx, expr, self.msrv); diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs index d57e391b55d54..6f944914b8fd6 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs @@ -81,7 +81,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) { ( "try `pointer::cast`, a safer alternative", - format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()), + format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_paren()), ) }; diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs index cad9c1df273f0..c0c0a47f85515 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::std_or_core; use clippy_utils::sugg::Sugg; +use clippy_utils::{std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; -use rustc_span::sym; use super::PTR_CAST_CONSTNESS; @@ -53,7 +52,8 @@ pub(super) fn check<'tcx>( } if msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) { - let sugg = Sugg::hir(cx, cast_expr, "_"); + let mut app = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_context(cx, cast_expr, expr.span.ctxt(), "_", &mut app); let constness = match *to_mutbl { Mutability::Not => "const", Mutability::Mut => "mut", @@ -65,8 +65,8 @@ pub(super) fn check<'tcx>( expr.span, "`as` casting between raw pointers while changing only its constness", format!("try `pointer::cast_{constness}`, a safer alternative"), - format!("{}.cast_{constness}()", sugg.maybe_par()), - Applicability::MachineApplicable, + format!("{}.cast_{constness}()", sugg.maybe_paren()), + app, ); } } @@ -77,9 +77,9 @@ pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) && let ExprKind::Call(func, []) = cast_expr.kind && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind && let Some(defid) = path.res.opt_def_id() - && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.as_str()) { - (Some(sym::ptr_null), "cast_mut") => "null_mut", - (Some(sym::ptr_null_mut), "cast_const") => "null", + && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.name) { + (Some(sym::ptr_null), sym::cast_mut) => "null_mut", + (Some(sym::ptr_null_mut), sym::cast_const) => "null", _ => return, } && let Some(prefix) = std_or_core(cx) diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 7885f171461d7..8e8c55cf38329 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -8,7 +8,9 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; +use rustc_span::{Symbol, sym}; use std::ops::ControlFlow; use super::UNNECESSARY_CAST; @@ -130,11 +132,11 @@ pub(super) fn check<'tcx>( | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => { - if let Some(src) = cast_expr.span.get_source_text(cx) { - if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { - lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); - return true; - } + if let Some(src) = cast_expr.span.get_source_text(cx) + && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) + { + lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + return true; } }, _ => {}, @@ -142,23 +144,50 @@ pub(super) fn check<'tcx>( } if cast_from.kind() == cast_to.kind() && !expr.span.in_external_macro(cx.sess().source_map()) { + enum MaybeParenOrBlock { + Paren, + Block, + Nothing, + } + + fn is_borrow_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::AddrOf(..)) + || cx + .typeck_results() + .expr_adjustments(expr) + .first() + .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_))) + } + + fn is_in_allowed_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + const ALLOWED_MACROS: &[Symbol] = &[ + sym::format_args_macro, + sym::assert_eq_macro, + sym::debug_assert_eq_macro, + sym::assert_ne_macro, + sym::debug_assert_ne_macro, + ]; + matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if + cx.tcx.get_diagnostic_name(def_id).is_some_and(|sym| ALLOWED_MACROS.contains(&sym))) + } + if let Some(id) = path_to_local(cast_expr) - && !cx.tcx.hir().span(id).eq_ctxt(cast_expr.span) + && !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span) { // Binding context is different than the identifiers context. // Weird macro wizardry could be involved here. return false; } - // If the whole cast expression is a unary expression (`(*x as T)`) or an addressof - // expression (`(&x as T)`), then not surrounding the suggestion into a block risks us - // changing the precedence of operators if the cast expression is followed by an operation - // with higher precedence than the unary operator (`(*x as T).foo()` would become - // `*x.foo()`, which changes what the `*` applies on). - // The same is true if the expression encompassing the cast expression is a unary - // expression or an addressof expression. - let needs_block = matches!(cast_expr.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)) - || get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..))); + // Changing `&(x as i32)` to `&x` would change the meaning of the code because the previous creates + // a reference to the temporary while the latter creates a reference to the original value. + let surrounding = match cx.tcx.parent_hir_node(expr.hir_id) { + Node::Expr(parent) if is_borrow_expr(cx, parent) && !is_in_allowed_macro(cx, parent) => { + MaybeParenOrBlock::Block + }, + Node::Expr(parent) if cast_expr.precedence() < parent.precedence() => MaybeParenOrBlock::Paren, + _ => MaybeParenOrBlock::Nothing, + }; span_lint_and_sugg( cx, @@ -166,10 +195,10 @@ pub(super) fn check<'tcx>( expr.span, format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), "try", - if needs_block { - format!("{{ {cast_str} }}") - } else { - cast_str + match surrounding { + MaybeParenOrBlock::Paren => format!("({cast_str})"), + MaybeParenOrBlock::Block => format!("{{ {cast_str} }}"), + MaybeParenOrBlock::Nothing => cast_str, }, Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs index 84136a2e6c28f..7590fe96fd214 100644 --- a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs +++ b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs @@ -32,7 +32,7 @@ declare_lint_pass!(CfgNotTest => [CFG_NOT_TEST]); impl EarlyLintPass for CfgNotTest { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) { - if attr.has_name(rustc_span::sym::cfg) && contains_not_test(attr.meta_item_list().as_deref(), false) { + if attr.has_name(rustc_span::sym::cfg_trace) && contains_not_test(attr.meta_item_list().as_deref(), false) { span_lint_and_then( cx, CFG_NOT_TEST, diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index b36c8662289ca..8ada608049c7b 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -253,11 +253,11 @@ fn get_types_from_cast<'a>( match limit.kind { // `from_type::from(_)` ExprKind::Call(path, _) => { - if let ExprKind::Path(ref path) = path.kind { + if let ExprKind::Path(ref path) = path.kind // `to_type` - if let Some(to_type) = get_implementing_type(path, types, func) { - return Some((from_type, to_type)); - } + && let Some(to_type) = get_implementing_type(path, types, func) + { + return Some((from_type, to_type)); } }, // `to_type::MAX` diff --git a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs new file mode 100644 index 0000000000000..6b239a1541b0d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -0,0 +1,100 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::visitors::is_const_evaluatable; +use clippy_utils::{is_in_const_context, is_mutable, is_trait_method}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for slice references with cloned references such as `&[f.clone()]`. + /// + /// ### Why is this bad + /// + /// A reference does not need to be owned in order to used as a slice. + /// + /// ### Known problems + /// + /// This lint does not know whether or not a clone implementation has side effects. + /// + /// ### Example + /// + /// ```ignore + /// let data = 10; + /// let data_ref = &data; + /// take_slice(&[data_ref.clone()]); + /// ``` + /// Use instead: + /// ```ignore + /// use std::slice; + /// let data = 10; + /// let data_ref = &data; + /// take_slice(slice::from_ref(data_ref)); + /// ``` + #[clippy::version = "1.87.0"] + pub CLONED_REF_TO_SLICE_REFS, + perf, + "cloning a reference for slice references" +} + +pub struct ClonedRefToSliceRefs<'a> { + msrv: &'a Msrv, +} +impl<'a> ClonedRefToSliceRefs<'a> { + pub fn new(conf: &'a Conf) -> Self { + Self { msrv: &conf.msrv } + } +} + +impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]); + +impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if self.msrv.meets(cx, { + if is_in_const_context(cx) { + msrvs::CONST_SLICE_FROM_REF + } else { + msrvs::SLICE_FROM_REF + } + }) + // `&[foo.clone()]` expressions + && let ExprKind::AddrOf(_, mutability, arr) = &expr.kind + // mutable references would have a different meaning + && mutability.is_not() + + // check for single item arrays + && let ExprKind::Array([item]) = &arr.kind + + // check for clones + && let ExprKind::MethodCall(_, val, _, _) = item.kind + && is_trait_method(cx, item, sym::Clone) + + // check for immutability or purity + && (!is_mutable(cx, val) || is_const_evaluatable(cx, val)) + + // get appropriate crate for `slice::from_ref` + && let Some(builtin_crate) = clippy_utils::std_or_core(cx) + { + let mut sugg = Sugg::hir(cx, val, "_"); + if !cx.typeck_results().expr_ty(val).is_ref() { + sugg = sugg.addr(); + } + + span_lint_and_sugg( + cx, + CLONED_REF_TO_SLICE_REFS, + expr.span, + format!("this call to `clone` can be replaced with `{builtin_crate}::slice::from_ref`"), + "try", + format!("{builtin_crate}::slice::from_ref({sugg})"), + Applicability::MaybeIncorrect, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index a1ff20dee721f..5c64216dd92ca 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -3,14 +3,14 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn}; +use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn, sym}; use core::ops::ControlFlow; use rustc_hir::intravisit::FnKind; use rustc_hir::{Attribute, Body, Expr, ExprKind, FnDecl}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -62,6 +62,7 @@ impl CognitiveComplexity { let mut cc = 1u64; let mut returns = 0u64; + let mut prev_expr: Option<&ExprKind<'tcx>> = None; let _: Option = for_each_expr_without_closures(expr, |e| { match e.kind { ExprKind::If(_, _, _) => { @@ -73,9 +74,14 @@ impl CognitiveComplexity { } cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64; }, - ExprKind::Ret(_) => returns += 1, + ExprKind::Ret(_) => { + if !matches!(prev_expr, Some(ExprKind::Ret(_))) { + returns += 1; + } + }, _ => {}, } + prev_expr = Some(&e.kind); ControlFlow::Continue(()) }); @@ -98,7 +104,7 @@ impl CognitiveComplexity { FnKind::Closure => { let header_span = body_span.with_hi(decl.output.span().lo()); #[expect(clippy::range_plus_one)] - if let Some(range) = header_span.map_range(cx, |src, range| { + if let Some(range) = header_span.map_range(cx, |_, src, range| { let mut idxs = src.get(range.clone())?.match_indices('|'); Some(range.start + idxs.next()?.0..range.start + idxs.next()?.0 + 1) }) { @@ -151,9 +157,9 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { } fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { - self.limit.push_attrs(cx.sess(), attrs, "cognitive_complexity"); + self.limit.push_attrs(cx.sess(), attrs, sym::cognitive_complexity); } fn check_attributes_post(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { - self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity"); + self.limit.pop_attrs(cx.sess(), attrs, sym::cognitive_complexity); } } diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index e73bfc6ebf7a1..7f6ecea99fb02 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -1,10 +1,12 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability}; -use clippy_utils::sugg::Sugg; -use rustc_ast::ast; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability}; +use rustc_ast::BinOpKind; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::declare_lint_pass; +use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; use rustc_span::Span; declare_clippy_lint! { @@ -75,105 +77,152 @@ declare_clippy_lint! { "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)" } -declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); +pub struct CollapsibleIf { + msrv: Msrv, + lint_commented_code: bool, +} + +impl CollapsibleIf { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv, + lint_commented_code: conf.lint_commented_code, + } + } + + fn check_collapsible_else_if(cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) { + if !block_starts_with_comment(cx, else_block) + && let Some(else_) = expr_block(else_block) + && cx.tcx.hir_attrs(else_.hir_id).is_empty() + && !else_.span.from_expansion() + && let ExprKind::If(..) = else_.kind + { + // Prevent "elseif" + // Check that the "else" is followed by whitespace + let up_to_else = then_span.between(else_block.span); + let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { + !c.is_whitespace() + } else { + false + }; + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + COLLAPSIBLE_ELSE_IF, + else_block.span, + "this `else { if .. }` block can be collapsed", + "collapse nested if block", + format!( + "{}{}", + if requires_space { " " } else { "" }, + snippet_block_with_applicability(cx, else_.span, "..", Some(else_block.span), &mut applicability) + ), + applicability, + ); + } + } + + fn check_collapsible_if_if(&self, cx: &LateContext<'_>, expr: &Expr<'_>, check: &Expr<'_>, then: &Block<'_>) { + if let Some(inner) = expr_block(then) + && cx.tcx.hir_attrs(inner.hir_id).is_empty() + && let ExprKind::If(check_inner, _, None) = &inner.kind + && self.eligible_condition(cx, check_inner) + && let ctxt = expr.span.ctxt() + && inner.span.ctxt() == ctxt + && (self.lint_commented_code || !block_starts_with_comment(cx, then)) + { + span_lint_and_then( + cx, + COLLAPSIBLE_IF, + expr.span, + "this `if` statement can be collapsed", + |diag| { + let then_open_bracket = then.span.split_at(1).0.with_leading_whitespace(cx).into_span(); + let then_closing_bracket = { + let end = then.span.shrink_to_hi(); + end.with_lo(end.lo() - rustc_span::BytePos(1)) + .with_leading_whitespace(cx) + .into_span() + }; + let inner_if = inner.span.split_at(2).0; + let mut sugg = vec![ + // Remove the outer then block `{` + (then_open_bracket, String::new()), + // Remove the outer then block '}' + (then_closing_bracket, String::new()), + // Replace inner `if` by `&&` + (inner_if, String::from("&&")), + ]; + sugg.extend(parens_around(check)); + sugg.extend(parens_around(check_inner)); + + diag.multipart_suggestion("collapse nested if block", sugg, Applicability::MachineApplicable); + }, + ); + } + } + + fn eligible_condition(&self, cx: &LateContext<'_>, cond: &Expr<'_>) -> bool { + !matches!(cond.kind, ExprKind::Let(..)) + || (cx.tcx.sess.edition().at_least_rust_2024() && self.msrv.meets(cx, msrvs::LET_CHAINS)) + } +} + +impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); -impl EarlyLintPass for CollapsibleIf { - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let ast::ExprKind::If(cond, then, else_) = &expr.kind +impl LateLintPass<'_> for CollapsibleIf { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::If(cond, then, else_) = &expr.kind && !expr.span.from_expansion() { - if let Some(else_) = else_ { - check_collapsible_maybe_if_let(cx, then.span, else_); - } else if !matches!(cond.kind, ast::ExprKind::Let(..)) { - check_collapsible_no_if_let(cx, expr, cond, then); + if let Some(else_) = else_ + && let ExprKind::Block(else_, None) = else_.kind + { + Self::check_collapsible_else_if(cx, then.span, else_); + } else if else_.is_none() + && self.eligible_condition(cx, cond) + && let ExprKind::Block(then, None) = then.kind + { + self.check_collapsible_if_if(cx, expr, cond, then); } } } } -fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool { +fn block_starts_with_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { // We trim all opening braces and whitespaces and then check if the next string is a comment. - let trimmed_block_text = snippet_block(cx, expr.span, "..", None) + let trimmed_block_text = snippet_block(cx, block.span, "..", None) .trim_start_matches(|c: char| c.is_whitespace() || c == '{') .to_owned(); trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*") } -fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) { - if let ast::ExprKind::Block(ref block, _) = else_.kind - && !block_starts_with_comment(cx, block) - && let Some(else_) = expr_block(block) - && else_.attrs.is_empty() - && !else_.span.from_expansion() - && let ast::ExprKind::If(..) = else_.kind - { - // Prevent "elseif" - // Check that the "else" is followed by whitespace - let up_to_else = then_span.between(block.span); - let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { - !c.is_whitespace() - } else { - false - }; - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - COLLAPSIBLE_ELSE_IF, - block.span, - "this `else { if .. }` block can be collapsed", - "collapse nested if block", - format!( - "{}{}", - if requires_space { " " } else { "" }, - snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability) - ), - applicability, - ); - } -} - -fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) { - if !block_starts_with_comment(cx, then) - && let Some(inner) = expr_block(then) - && inner.attrs.is_empty() - && let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind - // Prevent triggering on `if c { if let a = b { .. } }`. - && !matches!(check_inner.kind, ast::ExprKind::Let(..)) - && let ctxt = expr.span.ctxt() - && inner.span.ctxt() == ctxt - { - span_lint_and_then( - cx, - COLLAPSIBLE_IF, - expr.span, - "this `if` statement can be collapsed", - |diag| { - let mut app = Applicability::MachineApplicable; - let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app); - let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app); - diag.span_suggestion( - expr.span, - "collapse nested if block", - format!( - "if {} {}", - lhs.and(&rhs), - snippet_block(cx, content.span, "..", Some(expr.span)), - ), - app, // snippet - ); +/// If `block` is a block with either one expression or a statement containing an expression, +/// return the expression. We don't peel blocks recursively, as extra blocks might be intentional. +fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { + match block.stmts { + [] => block.expr, + [ + Stmt { + kind: StmtKind::Semi(expr), + .. }, - ); + ] if block.expr.is_none() => Some(expr), + _ => None, } } -/// If the block contains only one expression, return it. -fn expr_block(block: &ast::Block) -> Option<&ast::Expr> { - if let [stmt] = &*block.stmts - && let ast::StmtKind::Expr(expr) | ast::StmtKind::Semi(expr) = &stmt.kind +/// If the expression is a `||`, suggest parentheses around it. +fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { + if let ExprKind::Binary(op, _, _) = expr.peel_drop_temps().kind + && op.node == BinOpKind::Or { - Some(expr) + vec![ + (expr.span.shrink_to_lo(), String::from("(")), + (expr.span.shrink_to_hi(), String::from(")")), + ] } else { - None + vec![] } } diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs index 61c92d441d097..238ebd4a444cf 100644 --- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs +++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs @@ -53,7 +53,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.40.0"] pub COMPARISON_CHAIN, - style, + pedantic, "`if`s that can be rewritten with `match` and `cmp`" } @@ -75,11 +75,15 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { } // Check that there exists at least one explicit else condition - let (conds, _) = if_sequence(expr); + let (conds, blocks) = if_sequence(expr); if conds.len() < 2 { return; } + if blocks.len() < 3 { + return; + } + for cond in conds.windows(2) { if let (&ExprKind::Binary(ref kind1, lhs1, rhs1), &ExprKind::Binary(ref kind2, lhs2, rhs2)) = (&cond[0].kind, &cond[1].kind) @@ -125,7 +129,8 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { let ExprKind::Binary(_, lhs, rhs) = conds[0].kind else { unreachable!(); }; - let lhs = Sugg::hir(cx, lhs, "..").maybe_par(); + + let lhs = Sugg::hir(cx, lhs, "..").maybe_paren(); let rhs = Sugg::hir(cx, rhs, "..").addr(); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index 03ed9c657b30a..2467fc95fd055 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; use clippy_utils::ty::{InteriorMut, needs_ordered_drop}; use clippy_utils::visitors::for_each_expr_without_closures; @@ -256,9 +256,9 @@ fn lint_branches_sharing_code<'tcx>( let suggestion = reindent_multiline(&suggestion, true, indent); let span = span.with_hi(last_block.span.hi()); - // Improve formatting if the inner block has indention (i.e. normal Rust formatting) + // Improve formatting if the inner block has indentation (i.e. normal Rust formatting) let span = span - .map_range(cx, |src, range| { + .map_range(cx, |_, src, range| { (range.start > 4 && src.get(range.start - 4..range.start)? == " ") .then_some(range.start - 4..range.end) }) @@ -539,10 +539,10 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo .filter(|stmt| !ignore_span.overlaps(stmt.span)) .try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); - if let Some(expr) = block.expr { - if res.is_continue() { - res = intravisit::walk_expr(&mut walker, expr); - } + if let Some(expr) = block.expr + && res.is_continue() + { + res = intravisit::walk_expr(&mut walker, expr); } res.is_break() @@ -567,7 +567,7 @@ fn method_caller_is_mutable<'tcx>( /// Implementation of `IFS_SAME_COND`. fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { - for (i, j) in search_same( + for group in search_same( conds, |e| hash_expr(cx, e), |lhs, rhs| { @@ -584,14 +584,8 @@ fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mu } }, ) { - span_lint_and_note( - cx, - IFS_SAME_COND, - j.span, - "this `if` has the same condition as a previous `if`", - Some(i.span), - "same as this", - ); + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition"); } } @@ -609,14 +603,13 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { SpanlessEq::new(cx).eq_expr(lhs, rhs) }; - for (i, j) in search_same(conds, |e| hash_expr(cx, e), eq) { - span_lint_and_note( + for group in search_same(conds, |e| hash_expr(cx, e), eq) { + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint( cx, SAME_FUNCTIONS_IN_IF_CONDITION, - j.span, - "this `if` has the same function call as a previous `if`", - Some(i.span), - "same as this", + spans, + "these `if` branches have the same function call", ); } } diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs index 7d86bd3e540a1..19f62e8bf79c6 100644 --- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs +++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs @@ -5,8 +5,8 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::{Span, kw}; declare_clippy_lint! { /// ### What it does @@ -53,7 +53,7 @@ declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); impl EarlyLintPass for CrateInMacroDef { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if let ItemKind::MacroDef(macro_def) = &item.kind + if let ItemKind::MacroDef(_, macro_def) = &item.kind && item.attrs.iter().any(is_macro_export) && let Some(span) = contains_unhygienic_crate_reference(¯o_def.body.tokens) { @@ -105,12 +105,11 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { fn is_crate_keyword(tt: &TokenTree) -> Option { if let TokenTree::Token( Token { - kind: TokenKind::Ident(symbol, _), + kind: TokenKind::Ident(kw::Crate, _), span, }, _, ) = tt - && symbol.as_str() == "crate" { Some(*span) } else { diff --git a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs index 4d908af4084f3..9f82f87672794 100644 --- a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs +++ b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs @@ -165,17 +165,4 @@ macro_rules! declare_clippy_lint { $(, $eval_always)? } }; - - ( - $(#[doc = $lit:literal])* - pub $lint_name:ident, - internal, - $desc:literal - ) => { - declare_clippy_lint! {@ - $(#[doc = $lit])* - pub $lint_name, Allow, crate::LintCategory::Internal, $desc, - None, "0.0.0" - } - }; } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 0834618499c7b..5fcb851dfebce 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -3,36 +3,6 @@ // Manual edits will be overwritten. pub static LINTS: &[&crate::LintInfo] = &[ - #[cfg(feature = "internal")] - crate::utils::internal_lints::almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::slow_symbol_comparisons::SLOW_SYMBOL_COMPARISONS_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO, - #[cfg(feature = "internal")] - crate::utils::internal_lints::unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS_INFO, crate::absolute_paths::ABSOLUTE_PATHS_INFO, crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO, crate::approx_const::APPROX_CONSTANT_INFO, @@ -52,6 +22,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO, crate::attrs::DEPRECATED_SEMVER_INFO, crate::attrs::DUPLICATED_ATTRIBUTES_INFO, + crate::attrs::IGNORE_WITHOUT_REASON_INFO, crate::attrs::INLINE_ALWAYS_INFO, crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO, crate::attrs::NON_MINIMAL_CFG_INFO, @@ -93,9 +64,11 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO, crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO, crate::casts::CHAR_LIT_AS_U8_INFO, + crate::casts::CONFUSING_METHOD_TO_NUMERIC_CAST_INFO, crate::casts::FN_TO_NUMERIC_CAST_INFO, crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, + crate::casts::MANUAL_DANGLING_PTR_INFO, crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::REF_AS_PTR_INFO, @@ -103,6 +76,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::ZERO_PTR_INFO, crate::cfg_not_test::CFG_NOT_TEST_INFO, crate::checked_conversions::CHECKED_CONVERSIONS_INFO, + crate::cloned_ref_to_slice_refs::CLONED_REF_TO_SLICE_REFS_INFO, crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, crate::collapsible_if::COLLAPSIBLE_IF_INFO, @@ -137,6 +111,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::disallowed_names::DISALLOWED_NAMES_INFO, crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO, crate::disallowed_types::DISALLOWED_TYPES_INFO, + crate::doc::DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS_INFO, crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO, crate::doc::DOC_LAZY_CONTINUATION_INFO, crate::doc::DOC_LINK_CODE_INFO, @@ -285,6 +260,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::literal_representation::UNREADABLE_LITERAL_INFO, crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO, crate::literal_string_with_formatting_args::LITERAL_STRING_WITH_FORMATTING_ARGS_INFO, + crate::loops::CHAR_INDICES_AS_BYTE_INDICES_INFO, crate::loops::EMPTY_LOOP_INFO, crate::loops::EXPLICIT_COUNTER_LOOP_INFO, crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, @@ -311,6 +287,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::macro_metavars_in_unsafe::MACRO_METAVARS_IN_UNSAFE_INFO, crate::macro_use::MACRO_USE_IMPORTS_INFO, crate::main_recursion::MAIN_RECURSION_INFO, + crate::manual_abs_diff::MANUAL_ABS_DIFF_INFO, crate::manual_assert::MANUAL_ASSERT_INFO, crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, crate::manual_bits::MANUAL_BITS_INFO, @@ -333,7 +310,6 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO, crate::manual_string_new::MANUAL_STRING_NEW_INFO, crate::manual_strip::MANUAL_STRIP_INFO, - crate::manual_unwrap_or_default::MANUAL_UNWRAP_OR_DEFAULT_INFO, crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO, crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO, crate::match_result_ok::MATCH_RESULT_OK_INFO, @@ -343,10 +319,10 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::matches::MANUAL_MAP_INFO, crate::matches::MANUAL_OK_ERR_INFO, crate::matches::MANUAL_UNWRAP_OR_INFO, + crate::matches::MANUAL_UNWRAP_OR_DEFAULT_INFO, crate::matches::MATCH_AS_REF_INFO, crate::matches::MATCH_BOOL_INFO, crate::matches::MATCH_LIKE_MATCHES_MACRO_INFO, - crate::matches::MATCH_ON_VEC_ITEMS_INFO, crate::matches::MATCH_OVERLAPPING_ARM_INFO, crate::matches::MATCH_REF_PATS_INFO, crate::matches::MATCH_SAME_ARMS_INFO, @@ -487,6 +463,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::SUSPICIOUS_OPEN_OPTIONS_INFO, crate::methods::SUSPICIOUS_SPLITN_INFO, crate::methods::SUSPICIOUS_TO_OWNED_INFO, + crate::methods::SWAP_WITH_TEMPORARY_INFO, crate::methods::TYPE_ID_ON_BOX_INFO, crate::methods::UNBUFFERED_BYTES_INFO, crate::methods::UNINIT_ASSUMED_INIT_INFO, @@ -614,7 +591,6 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::operators::MODULO_ONE_INFO, crate::operators::NEEDLESS_BITWISE_BOOL_INFO, crate::operators::OP_REF_INFO, - crate::operators::PTR_EQ_INFO, crate::operators::REDUNDANT_COMPARISONS_INFO, crate::operators::SELF_ASSIGNMENT_INFO, crate::operators::VERBOSE_BIT_MASK_INFO, @@ -638,9 +614,9 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::precedence::PRECEDENCE_INFO, crate::precedence::PRECEDENCE_BITS_INFO, crate::ptr::CMP_NULL_INFO, - crate::ptr::INVALID_NULL_PTR_USAGE_INFO, crate::ptr::MUT_FROM_REF_INFO, crate::ptr::PTR_ARG_INFO, + crate::ptr::PTR_EQ_INFO, crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO, crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO, crate::pub_use::PUB_USE_INFO, @@ -664,6 +640,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::redundant_slicing::DEREF_BY_SLICING_INFO, crate::redundant_slicing::REDUNDANT_SLICING_INFO, crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO, + crate::redundant_test_prefix::REDUNDANT_TEST_PREFIX_INFO, crate::redundant_type_annotations::REDUNDANT_TYPE_ANNOTATIONS_INFO, crate::ref_option_ref::REF_OPTION_REF_INFO, crate::ref_patterns::REF_PATTERNS_INFO, @@ -730,13 +707,9 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS_INFO, crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO, - crate::transmute::TRANSMUTE_FLOAT_TO_INT_INFO, crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, - crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO, - crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO, crate::transmute::TRANSMUTE_INT_TO_NON_ZERO_INFO, crate::transmute::TRANSMUTE_NULL_TO_FN_INFO, - crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO, crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO, @@ -791,6 +764,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unwrap_in_result::UNWRAP_IN_RESULT_INFO, crate::upper_case_acronyms::UPPER_CASE_ACRONYMS_INFO, crate::use_self::USE_SELF_INFO, + crate::useless_concat::USELESS_CONCAT_INFO, crate::useless_conversion::USELESS_CONVERSION_INFO, crate::vec::USELESS_VEC_INFO, crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index ffdd946aadb8b..886c325b355fe 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -134,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { && let ty::Adt(adt, args) = *binding_type.kind() && adt.is_struct() && let variant = adt.non_enum_variant() - && (adt.did().is_local() || !variant.is_field_list_non_exhaustive()) + && !variant.field_list_has_applicable_non_exhaustive() && let module_did = cx.tcx.parent_module(stmt.hir_id) && variant .fields diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs index bbd5dc15542d1..f8a9037fc8047 100644 --- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs +++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_ty_alias; +use clippy_utils::source::SpanRangeExt as _; use hir::ExprKind; use hir::def::Res; use rustc_errors::Applicability; @@ -70,15 +71,26 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { && let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant() && !var.is_field_list_non_exhaustive() && !expr.span.from_expansion() && !qpath.span().from_expansion() + // do not suggest replacing an expression by a type name with placeholders + && !base.is_suggestable_infer_ty() { - span_lint_and_sugg( + let mut removals = vec![(expr.span.with_lo(qpath.qself_span().hi()), String::new())]; + if expr.span.with_source_text(cx, |s| s.starts_with('<')) == Some(true) { + // Remove `<`, '>` has already been removed by the existing removal expression. + removals.push((expr.span.with_hi(qpath.qself_span().lo()), String::new())); + } + span_lint_and_then( cx, DEFAULT_CONSTRUCTED_UNIT_STRUCTS, - expr.span.with_lo(qpath.qself_span().hi()), + expr.span, "use of `default` to create a unit struct", - "remove this call to `default`", - String::new(), - Applicability::MachineApplicable, + |diag| { + diag.multipart_suggestion( + "remove this call to `default`", + removals, + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs index 085ed9222c91a..615421f3a40d0 100644 --- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs +++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr}; +use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr}; use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; @@ -97,7 +97,7 @@ fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: ty::GenericArgsR } fn has_c_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { - let attrs = cx.tcx.hir().attrs(hir_id); + let attrs = cx.tcx.hir_attrs(hir_id); find_attr!(attrs, AttributeKind::Repr(r) if r.iter().any(|(x, _)| *x == ReprAttr::ReprC)) } diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index 0031da406f17f..5204f73ea0a95 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -2,51 +2,52 @@ // Prefer to use those when possible. macro_rules! declare_with_version { - ($name:ident($name_version:ident): &[$ty:ty] = &[$( + ($name:ident($name_version:ident) = [$( #[clippy::version = $version:literal] $e:expr, )*]) => { - pub static $name: &[$ty] = &[$($e),*]; + pub static $name: &[(&str, &str)] = &[$($e),*]; #[allow(unused)] pub static $name_version: &[&str] = &[$($version),*]; }; } #[rustfmt::skip] -declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[ - #[clippy::version = "pre 1.29.0"] - ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), +declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ + #[clippy::version = "1.30.0"] + ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), + #[clippy::version = "1.86.0"] + ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), + #[clippy::version = "pre 1.29.0"] + ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), + #[clippy::version = "1.86.0"] + ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), + #[clippy::version = "1.54.0"] + ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), #[clippy::version = "pre 1.29.0"] ("clippy::range_step_by_zero", "`Iterator::step_by(0)` now panics and is no longer an infinite iterator"), + #[clippy::version = "1.47.0"] + ("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"), + #[clippy::version = "1.44.0"] + ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), #[clippy::version = "pre 1.29.0"] - ("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"), + ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), #[clippy::version = "pre 1.29.0"] - ("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"), + ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), #[clippy::version = "pre 1.29.0"] - ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), - #[clippy::version = "1.30.0"] - ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), + ("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"), #[clippy::version = "pre 1.29.0"] - ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), + ("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"), #[clippy::version = "1.39.0"] ("clippy::unused_collect", "`Iterator::collect` is now marked as `#[must_use]`"), - #[clippy::version = "1.44.0"] - ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), - #[clippy::version = "1.47.0"] - ("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"), - #[clippy::version = "1.54.0"] - ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), #[clippy::version = "1.54.0"] ("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"), - #[clippy::version = "1.86.0"] - ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), - // end deprecated lints. used by `cargo dev deprecate_lint` ]} #[rustfmt::skip] -declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ +declare_with_version! { RENAMED(RENAMED_VERSION) = [ #[clippy::version = ""] ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), #[clippy::version = ""] @@ -60,6 +61,12 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ #[clippy::version = ""] ("clippy::box_vec", "clippy::box_collection"), #[clippy::version = ""] + ("clippy::cast_ref_to_mut", "invalid_reference_casting"), + #[clippy::version = ""] + ("clippy::clone_double_ref", "suspicious_double_ref_op"), + #[clippy::version = ""] + ("clippy::cmp_nan", "invalid_nan_comparisons"), + #[clippy::version = ""] ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), #[clippy::version = ""] ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), @@ -69,15 +76,35 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ ("clippy::disallowed_method", "clippy::disallowed_methods"), #[clippy::version = ""] ("clippy::disallowed_type", "clippy::disallowed_types"), + #[clippy::version = "1.86.0"] + ("clippy::double_neg", "double_negations"), + #[clippy::version = ""] + ("clippy::drop_bounds", "drop_bounds"), + #[clippy::version = ""] + ("clippy::drop_copy", "dropping_copy_types"), + #[clippy::version = ""] + ("clippy::drop_ref", "dropping_references"), #[clippy::version = ""] ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), - #[clippy::version = "1.51.0"] - ("clippy::find_map", "clippy::manual_find_map"), #[clippy::version = "1.53.0"] ("clippy::filter_map", "clippy::manual_filter_map"), + #[clippy::version = "1.51.0"] + ("clippy::find_map", "clippy::manual_find_map"), #[clippy::version = ""] ("clippy::fn_address_comparisons", "unpredictable_function_pointer_comparisons"), #[clippy::version = ""] + ("clippy::fn_null_check", "useless_ptr_null_checks"), + #[clippy::version = ""] + ("clippy::for_loop_over_option", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loop_over_result", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::forget_copy", "forgetting_copy_types"), + #[clippy::version = ""] + ("clippy::forget_ref", "forgetting_references"), + #[clippy::version = ""] ("clippy::identity_conversion", "clippy::useless_conversion"), #[clippy::version = "pre 1.29.0"] ("clippy::if_let_redundant_pattern_matching", "clippy::redundant_pattern_matching"), @@ -90,7 +117,25 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ #[clippy::version = ""] ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), #[clippy::version = ""] + ("clippy::into_iter_on_array", "array_into_iter"), + #[clippy::version = ""] + ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), + #[clippy::version = "CURRENT_RUSTC_VERSION"] + ("clippy::invalid_null_ptr_usage", "invalid_null_arguments"), + #[clippy::version = ""] + ("clippy::invalid_ref", "invalid_value"), + #[clippy::version = ""] + ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), + #[clippy::version = ""] + ("clippy::let_underscore_drop", "let_underscore_drop"), + #[clippy::version = ""] ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), + #[clippy::version = "1.80.0"] + ("clippy::maybe_misused_cfg", "unexpected_cfgs"), + #[clippy::version = ""] + ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), + #[clippy::version = "1.80.0"] + ("clippy::mismatched_target_os", "unexpected_cfgs"), #[clippy::version = ""] ("clippy::new_without_default_derive", "clippy::new_without_default"), #[clippy::version = ""] @@ -106,6 +151,10 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ #[clippy::version = ""] ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"), #[clippy::version = ""] + ("clippy::panic_params", "non_fmt_panics"), + #[clippy::version = ""] + ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), + #[clippy::version = ""] ("clippy::ref_in_deref", "clippy::needless_borrow"), #[clippy::version = ""] ("clippy::result_expect_used", "clippy::expect_used"), @@ -114,65 +163,25 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ #[clippy::version = ""] ("clippy::result_unwrap_used", "clippy::unwrap_used"), #[clippy::version = ""] + ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), + #[clippy::version = ""] ("clippy::single_char_push_str", "clippy::single_char_add_str"), #[clippy::version = ""] ("clippy::stutter", "clippy::module_name_repetitions"), #[clippy::version = ""] + ("clippy::temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"), + #[clippy::version = ""] ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"), #[clippy::version = ""] ("clippy::to_string_in_display", "clippy::recursive_format_impl"), - #[clippy::version = ""] - ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), - #[clippy::version = ""] - ("clippy::zero_width_space", "clippy::invisible_characters"), - #[clippy::version = ""] - ("clippy::cast_ref_to_mut", "invalid_reference_casting"), - #[clippy::version = ""] - ("clippy::clone_double_ref", "suspicious_double_ref_op"), - #[clippy::version = ""] - ("clippy::cmp_nan", "invalid_nan_comparisons"), - #[clippy::version = "1.86.0"] - ("clippy::double_neg", "double_negations"), - #[clippy::version = ""] - ("clippy::drop_bounds", "drop_bounds"), - #[clippy::version = ""] - ("clippy::drop_copy", "dropping_copy_types"), - #[clippy::version = ""] - ("clippy::drop_ref", "dropping_references"), - #[clippy::version = ""] - ("clippy::fn_null_check", "useless_ptr_null_checks"), - #[clippy::version = ""] - ("clippy::for_loop_over_option", "for_loops_over_fallibles"), - #[clippy::version = ""] - ("clippy::for_loop_over_result", "for_loops_over_fallibles"), - #[clippy::version = ""] - ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), - #[clippy::version = ""] - ("clippy::forget_copy", "forgetting_copy_types"), - #[clippy::version = ""] - ("clippy::forget_ref", "forgetting_references"), - #[clippy::version = ""] - ("clippy::into_iter_on_array", "array_into_iter"), - #[clippy::version = ""] - ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), - #[clippy::version = ""] - ("clippy::invalid_ref", "invalid_value"), - #[clippy::version = ""] - ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), - #[clippy::version = ""] - ("clippy::let_underscore_drop", "let_underscore_drop"), - #[clippy::version = "1.80.0"] - ("clippy::maybe_misused_cfg", "unexpected_cfgs"), - #[clippy::version = ""] - ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), - #[clippy::version = "1.80.0"] - ("clippy::mismatched_target_os", "unexpected_cfgs"), - #[clippy::version = ""] - ("clippy::panic_params", "non_fmt_panics"), - #[clippy::version = ""] - ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), - #[clippy::version = ""] - ("clippy::temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_float_to_int", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_int_to_char", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_int_to_float", "unnecessary_transmutes"), + #[clippy::version = "1.88.0"] + ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), #[clippy::version = ""] ("clippy::undropped_manually_drops", "undropped_manually_drops"), #[clippy::version = ""] @@ -180,8 +189,9 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ #[clippy::version = ""] ("clippy::unused_label", "unused_labels"), #[clippy::version = ""] + ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), + #[clippy::version = ""] ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"), #[clippy::version = ""] - ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), - // end renamed lints. used by `cargo dev rename_lint` + ("clippy::zero_width_space", "clippy::invisible_characters"), ]} diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 849c60b89b97e..a22a2ee66d258 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -22,6 +22,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -252,13 +253,14 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } let typeck = cx.typeck_results(); - let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { + let Some((kind, sub_expr, skip_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { // The whole chain of reference operations has been seen if let Some((state, data)) = self.state.take() { report(cx, expr, state, data, typeck); } return; }; + self.skip_expr = skip_expr; match (self.state.take(), kind) { (None, kind) => { @@ -671,42 +673,38 @@ fn try_parse_ref_op<'tcx>( tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, -) -> Option<(RefOp, &'tcx Expr<'tcx>)> { - let (is_ufcs, def_id, arg) = match expr.kind { - ExprKind::MethodCall(_, arg, [], _) => (false, typeck.type_dependent_def_id(expr.hir_id)?, arg), +) -> Option<(RefOp, &'tcx Expr<'tcx>, Option)> { + let (call_path_id, def_id, arg) = match expr.kind { + ExprKind::MethodCall(_, arg, [], _) => (None, typeck.type_dependent_def_id(expr.hir_id)?, arg), ExprKind::Call( - Expr { - kind: ExprKind::Path(path), + &Expr { + kind: ExprKind::Path(QPath::Resolved(None, path)), hir_id, .. }, [arg], - ) => (true, typeck.qpath_res(path, *hir_id).opt_def_id()?, arg), + ) => (Some(hir_id), path.res.opt_def_id()?, arg), ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_raw_ptr() => { - return Some((RefOp::Deref, sub_expr)); + return Some((RefOp::Deref, sub_expr, None)); + }, + ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => { + return Some((RefOp::AddrOf(mutability), sub_expr, None)); }, - ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)), _ => return None, }; - if tcx.is_diagnostic_item(sym::deref_method, def_id) { - Some(( - RefOp::Method { - mutbl: Mutability::Not, - is_ufcs, - }, - arg, - )) - } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? { - Some(( - RefOp::Method { - mutbl: Mutability::Mut, - is_ufcs, - }, - arg, - )) - } else { - None - } + let mutbl = match tcx.get_diagnostic_name(def_id) { + Some(sym::deref_method) => Mutability::Not, + Some(sym::deref_mut_method) => Mutability::Mut, + _ => return None, + }; + Some(( + RefOp::Method { + mutbl, + is_ufcs: call_path_id.is_some(), + }, + arg, + call_path_id, + )) } // Checks if the adjustments contains a deref of `ManuallyDrop<_>` @@ -853,7 +851,7 @@ impl TyCoercionStability { continue; }, ty::Param(_) if for_return => Self::Deref, - ty::Alias(ty::Weak | ty::Inherent, _) => unreachable!("should have been normalized away above"), + ty::Alias(ty::Free | ty::Inherent, _) => unreachable!("should have been normalized away above"), ty::Alias(ty::Projection, _) if !for_return && ty.has_non_region_param() => Self::Reborrow, ty::Infer(_) | ty::Error(_) @@ -944,7 +942,7 @@ fn report<'tcx>( mutbl, } => { let mut app = Applicability::MachineApplicable; - let (expr_str, _expr_is_macro_call) = + let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let ty = typeck.expr_ty(expr); let (_, ref_count) = peel_middle_ty_refs(ty); @@ -968,20 +966,11 @@ fn report<'tcx>( "&" }; - // expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's - // `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary. - /* - expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < ExprPrecedence::Prefix { + let expr_str = if !expr_is_macro_call && is_ufcs && expr.precedence() < ExprPrecedence::Prefix { Cow::Owned(format!("({expr_str})")) } else { expr_str }; - */ - - // Fix #10850, do not lint if it's `Foo::deref` instead of `foo.deref()`. - if is_ufcs { - return; - } span_lint_and_sugg( cx, @@ -1133,61 +1122,60 @@ fn report<'tcx>( impl<'tcx> Dereferencing<'tcx> { fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { - if let Some(outer_pat) = self.ref_locals.get_mut(&local) { - if let Some(pat) = outer_pat { - // Check for auto-deref - if !matches!( - cx.typeck_results().expr_adjustments(e), - [ - Adjustment { - kind: Adjust::Deref(_), - .. - }, - Adjustment { - kind: Adjust::Deref(_), - .. - }, + if let Some(outer_pat) = self.ref_locals.get_mut(&local) + && let Some(pat) = outer_pat + // Check for auto-deref + && !matches!( + cx.typeck_results().expr_adjustments(e), + [ + Adjustment { + kind: Adjust::Deref(_), .. - ] - ) { - match get_parent_expr(cx, e) { - // Field accesses are the same no matter the number of references. - Some(Expr { - kind: ExprKind::Field(..), - .. - }) => (), - Some(&Expr { - span, - kind: ExprKind::Unary(UnOp::Deref, _), - .. - }) if !span.from_expansion() => { - // Remove explicit deref. - let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0; - pat.replacements.push((span, snip.into())); - }, - Some(parent) if !parent.span.from_expansion() => { - // Double reference might be needed at this point. - if parent.precedence() == ExprPrecedence::Unambiguous { - // Parentheses would be needed here, don't lint. - *outer_pat = None; - } else { - pat.always_deref = false; - let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0; - pat.replacements.push((e.span, format!("&{snip}"))); - } - }, - _ if !e.span.from_expansion() => { - // Double reference might be needed at this point. - pat.always_deref = false; - let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app); - pat.replacements.push((e.span, format!("&{snip}"))); - }, - // Edge case for macros. The span of the identifier will usually match the context of the - // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc - // macros - _ => *outer_pat = None, + }, + Adjustment { + kind: Adjust::Deref(_), + .. + }, + .. + ] + ) + { + match get_parent_expr(cx, e) { + // Field accesses are the same no matter the number of references. + Some(Expr { + kind: ExprKind::Field(..), + .. + }) => (), + Some(&Expr { + span, + kind: ExprKind::Unary(UnOp::Deref, _), + .. + }) if !span.from_expansion() => { + // Remove explicit deref. + let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0; + pat.replacements.push((span, snip.into())); + }, + Some(parent) if !parent.span.from_expansion() => { + // Double reference might be needed at this point. + if parent.precedence() == ExprPrecedence::Unambiguous { + // Parentheses would be needed here, don't lint. + *outer_pat = None; + } else { + pat.always_deref = false; + let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0; + pat.replacements.push((e.span, format!("&{snip}"))); } - } + }, + _ if !e.span.from_expansion() => { + // Double reference might be needed at this point. + pat.always_deref = false; + let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app); + pat.replacements.push((e.span, format!("&{snip}"))); + }, + // Edge case for macros. The span of the identifier will usually match the context of the + // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc + // macros + _ => *outer_pat = None, } } } diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index 66a3e5e3d3c7f..10331b3855b84 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -94,18 +94,18 @@ fn check_struct<'tcx>( ty_args: GenericArgsRef<'_>, typeck_results: &'tcx TypeckResults<'tcx>, ) { - if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { - if let Some(PathSegment { args, .. }) = p.segments.last() { - let args = args.map(|a| a.args).unwrap_or(&[]); - - // ty_args contains the generic parameters of the type declaration, while args contains the - // arguments used at instantiation time. If both len are not equal, it means that some - // parameters were not provided (which means that the default values were used); in this - // case we will not risk suggesting too broad a rewrite. We won't either if any argument - // is a type or a const. - if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) { - return; - } + if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind + && let Some(PathSegment { args, .. }) = p.segments.last() + { + let args = args.map(|a| a.args).unwrap_or(&[]); + + // ty_args contains the generic parameters of the type declaration, while args contains the + // arguments used at instantiation time. If both len are not equal, it means that some + // parameters were not provided (which means that the default values were used); in this + // case we will not risk suggesting too broad a rewrite. We won't either if any argument + // is a type or a const. + if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) { + return; } } @@ -188,7 +188,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { self_ty, .. }) = item.kind - && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) && !item.span.from_expansion() && let Some(def_id) = trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::Default, def_id) @@ -197,9 +197,9 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { && let ImplItemKind::Fn(_, b) = &impl_item.kind && let Body { value: func_expr, .. } = cx.tcx.hir_body(*b) && let &ty::Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind() - && let attrs = cx.tcx.hir().attrs(item.hir_id()) + && let attrs = cx.tcx.hir_attrs(item.hir_id()) && !attrs.iter().any(|attr| attr.doc_str().is_some()) - && cx.tcx.hir().attrs(impl_item_hir).is_empty() + && cx.tcx.hir_attrs(impl_item_hir).is_empty() { if adt_def.is_struct() { check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b)); diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index db3e6034c5baf..3443b36eb4f39 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; -use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths}; +use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, paths}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { }) = item.kind { let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let is_automatically_derived = cx.tcx.has_attr(item.owner_id, sym::automatically_derived); + let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); 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); @@ -235,7 +235,7 @@ fn check_hash_peq<'tcx>( { // Look for the PartialEq implementations for `ty` cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); + let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); if !hash_is_automatically_derived || peq_is_automatically_derived { return; @@ -254,7 +254,7 @@ fn check_hash_peq<'tcx>( |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir().span(hir_id), "`PartialEq` implemented here"); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here"); } }, ); @@ -278,7 +278,7 @@ fn check_ord_partial_ord<'tcx>( { // 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 = cx.tcx.has_attr(impl_id, sym::automatically_derived); + let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); if partial_ord_is_automatically_derived == ord_is_automatically_derived { return; @@ -298,7 +298,7 @@ fn check_ord_partial_ord<'tcx>( 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.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir().span(hir_id), "`PartialOrd` implemented here"); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); } }); } @@ -324,11 +324,9 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h // there's a Copy impl for any instance of the adt. if !is_copy(cx, ty) { if ty_subs.non_erasable_generics().next().is_some() { - let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).is_some_and(|impls| { - impls.iter().any(|&id| { - matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) + let has_copy_impl = cx.tcx.local_trait_impls(copy_id).iter().any(|&id| { + matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()) - }) }); if !has_copy_impl { return; @@ -351,6 +349,10 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h { return; } + // The presence of `unsafe` fields prevents deriving `Clone` automatically + if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) { + return; + } span_lint_and_note( cx, @@ -375,7 +377,7 @@ fn check_unsafe_derive_deserialize<'tcx>( } if let Some(trait_def_id) = trait_ref.trait_def_id() - && match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE) + && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) && let ty::Adt(def, _) = ty.kind() && let Some(local_def_id) = def.did().as_local() && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) @@ -384,7 +386,7 @@ fn check_unsafe_derive_deserialize<'tcx>( .tcx .inherent_impls(def.did()) .iter() - .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local())) + .map(|imp_did| cx.tcx.hir_expect_item(imp_did.expect_local())) .any(|imp| has_unsafe(cx, imp)) { span_lint_hir_and_then( @@ -428,10 +430,10 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { - if let ExprKind::Block(block, _) = expr.kind { - if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { - return ControlFlow::Break(()); - } + if let ExprKind::Block(block, _) = expr.kind + && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + { + return ControlFlow::Break(()); } walk_expr(self, expr) @@ -481,7 +483,7 @@ fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: De tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some() } -/// Creates the `ParamEnv` used for the give type's derived `Eq` impl. +/// Creates the `ParamEnv` used for the given type's derived `Eq` impl. fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { // Initial map from generic index to param def. // Vec<(param_def, needs_eq)> diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index 4b8a689e99478..9814d4fa84f95 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -3,7 +3,9 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; +use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_hir::{ AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, @@ -72,8 +74,16 @@ pub struct DisallowedMacros { impl DisallowedMacros { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf, earlies: AttrStorage) -> Self { + let (disallowed, _) = create_disallowed_map( + tcx, + &conf.disallowed_macros, + PathNS::Macro, + |def_kind| matches!(def_kind, DefKind::Macro(_)), + "macro", + false, + ); Self { - disallowed: create_disallowed_map(tcx, &conf.disallowed_macros), + disallowed, seen: FxHashSet::default(), derive_src: None, earlies, diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index 149cf1cf2def1..fb970e17f38f0 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::PathNS; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; @@ -63,9 +64,20 @@ pub struct DisallowedMethods { impl DisallowedMethods { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - Self { - disallowed: create_disallowed_map(tcx, &conf.disallowed_methods), - } + let (disallowed, _) = create_disallowed_map( + tcx, + &conf.disallowed_methods, + PathNS::Value, + |def_kind| { + matches!( + def_kind, + DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn + ) + }, + "function", + false, + ); + Self { disallowed } } } @@ -74,12 +86,7 @@ impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (id, span) = match &expr.kind { - ExprKind::Path(path) - if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = - cx.qpath_res(path, expr.hir_id) => - { - (id, expr.span) - }, + ExprKind::Path(path) if let Res::Def(_, id) = cx.qpath_res(path, expr.hir_id) => (id, expr.span), ExprKind::MethodCall(name, ..) if let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => { (id, name.ident.span) }, diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs index 53c24a3faf1d8..d1a8590c59b4d 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs @@ -69,7 +69,7 @@ impl EarlyLintPass for DisallowedScriptIdents { // Implementation is heavily inspired by the implementation of [`non_ascii_idents`] lint: // https://github.com/rust-lang/rust/blob/master/compiler/rustc_lint/src/non_ascii_idents.rs - let check_disallowed_script_idents = cx.builder.lint_level(DISALLOWED_SCRIPT_IDENTS).0 != Level::Allow; + let check_disallowed_script_idents = cx.builder.lint_level(DISALLOWED_SCRIPT_IDENTS).level != Level::Allow; if !check_disallowed_script_idents { return; } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index 3659946b70424..d0b2f0c8407ff 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; -use clippy_config::types::DisallowedPath; +use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -60,22 +61,14 @@ pub struct DisallowedTypes { impl DisallowedTypes { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let mut def_ids = DefIdMap::default(); - let mut prim_tys = FxHashMap::default(); - for disallowed_path in &conf.disallowed_types { - let path: Vec<_> = disallowed_path.path().split("::").collect::>(); - for res in clippy_utils::def_path_res(tcx, &path) { - match res { - Res::Def(_, id) => { - def_ids.insert(id, (disallowed_path.path(), disallowed_path)); - }, - Res::PrimTy(ty) => { - prim_tys.insert(ty, (disallowed_path.path(), disallowed_path)); - }, - _ => {}, - } - } - } + let (def_ids, prim_tys) = create_disallowed_map( + tcx, + &conf.disallowed_types, + PathNS::Type, + def_kind_predicate, + "type", + true, + ); Self { def_ids, prim_tys } } @@ -95,11 +88,24 @@ impl DisallowedTypes { } } +pub fn def_kind_predicate(def_kind: DefKind) -> bool { + matches!( + def_kind, + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::AssocTy + ) +} + impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Use(path, UseKind::Single) = &item.kind { + if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind { for res in &path.res { self.check_res_emit(cx, res, item.span); } diff --git a/src/tools/clippy/clippy_lints/src/doc/doc_comment_double_space_linebreaks.rs b/src/tools/clippy/clippy_lints/src/doc/doc_comment_double_space_linebreaks.rs new file mode 100644 index 0000000000000..4cc0270223339 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/doc/doc_comment_double_space_linebreaks.rs @@ -0,0 +1,33 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_errors::Applicability; +use rustc_lint::LateContext; +use rustc_span::{BytePos, Span}; + +use super::DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS; + +pub fn check(cx: &LateContext<'_>, collected_breaks: &[Span]) { + if collected_breaks.is_empty() { + return; + } + + let breaks: Vec<_> = collected_breaks + .iter() + .map(|span| span.with_hi(span.lo() + BytePos(2))) + .collect(); + + span_lint_and_then( + cx, + DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS, + breaks.clone(), + "doc comment uses two spaces for a hard line break", + |diag| { + let suggs: Vec<_> = breaks.iter().map(|span| (*span, "\\".to_string())).collect(); + diag.tool_only_multipart_suggestion( + "replace this double space with a backslash:", + suggs, + Applicability::MachineApplicable, + ); + diag.help("replace this double space with a backslash: `\\`"); + }, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/doc/markdown.rs b/src/tools/clippy/clippy_lints/src/doc/markdown.rs index 8cdaba88e5095..7a1c7c675d2ec 100644 --- a/src/tools/clippy/clippy_lints/src/doc/markdown.rs +++ b/src/tools/clippy/clippy_lints/src/doc/markdown.rs @@ -113,20 +113,20 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b s != "-" && s.contains('-') } - if let Ok(url) = Url::parse(word) { + if let Ok(url) = Url::parse(word) // try to get around the fact that `foo::bar` parses as a valid URL - if !url.cannot_be_a_base() { - span_lint_and_sugg( - cx, - DOC_MARKDOWN, - span, - "you should put bare URLs between `<`/`>` or make a proper Markdown link", - "try", - format!("<{word}>"), - Applicability::MachineApplicable, - ); - return; - } + && !url.cannot_be_a_base() + { + span_lint_and_sugg( + cx, + DOC_MARKDOWN, + span, + "you should put bare URLs between `<`/`>` or make a proper Markdown link", + "try", + format!("<{word}>"), + Applicability::MachineApplicable, + ); + return; } // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343) diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index e8638595c4b26..9ee32fced8c44 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -1,11 +1,14 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; -use clippy_utils::ty::{implements_trait_with_env, is_type_diagnostic_item}; -use clippy_utils::{is_doc_hidden, return_ty}; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; +use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item}; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{fulfill_or_allowed, is_doc_hidden, method_chain_args, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::{Span, sym}; +use std::ops::ControlFlow; pub fn check( cx: &LateContext<'_>, @@ -13,7 +16,6 @@ pub fn check( sig: FnSig<'_>, headers: DocHeaders, body_id: Option, - panic_info: Option<(Span, bool)>, check_private_items: bool, ) { if !check_private_items && !cx.effective_visibilities.is_exported(owner_id.def_id) { @@ -25,7 +27,7 @@ pub fn check( && cx .tcx .hir_parent_iter(owner_id.into()) - .any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id))) + .any(|(id, _node)| is_doc_hidden(cx.tcx.hir_attrs(id))) { return; } @@ -46,13 +48,16 @@ pub fn check( ), _ => (), } - if !headers.panics && panic_info.is_some_and(|el| !el.1) { + if !headers.panics + && let Some(body_id) = body_id + && let Some(panic_span) = find_panic(cx, body_id) + { span_lint_and_note( cx, MISSING_PANICS_DOC, span, "docs for function which may panic missing `# Panics` section", - panic_info.map(|el| el.0), + Some(panic_span), "first possible panic found here", ); } @@ -89,3 +94,40 @@ pub fn check( } } } + +fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { + let mut panic_span = None; + let typeck = cx.tcx.typeck_body(body_id); + for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| { + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && (is_panic(cx, macro_call.def_id) + || matches!( + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro) + )) + && !cx.tcx.hir_is_inside_const_context(expr.hir_id) + && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) + && panic_span.is_none() + { + panic_span = Some(macro_call.span); + } + + // check for `unwrap` and `expect` for both `Option` and `Result` + if let Some(arglists) = + method_chain_args(expr, &[sym::unwrap]).or_else(|| method_chain_args(expr, &[sym::expect])) + && let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs() + && matches!( + get_type_diagnostic_name(cx, receiver_ty), + Some(sym::Option | sym::Result) + ) + && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) + && panic_span.is_none() + { + panic_span = Some(expr.span); + } + + // Visit all nodes to fulfill any `#[expect]`s after the first linted panic + ControlFlow::::Continue(()) + }); + panic_span +} diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 0296ff13112f3..87da380e95401 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1,15 +1,10 @@ #![allow(clippy::lint_without_lint_pass)] -mod lazy_continuation; -mod too_long_first_doc_paragraph; - use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; -use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::Visitable; -use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; +use clippy_utils::source::snippet_opt; +use clippy_utils::{is_entrypoint_fn, is_trait_impl_item}; use pulldown_cmark::Event::{ Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start, TaskListMarker, Text, @@ -18,27 +13,27 @@ use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, It use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{AnonConst, Attribute, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind}; +use rustc_hir::{Attribute, ImplItemKind, ItemKind, Node, Safety, TraitItemKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::nested_filter; -use rustc_middle::ty; use rustc_resolve::rustdoc::{ DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, span_of_fragments, }; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::edition::Edition; -use rustc_span::{Span, sym}; use std::ops::Range; use url::Url; +mod doc_comment_double_space_linebreaks; mod include_in_doc_without_cfg; +mod lazy_continuation; mod link_with_quotes; mod markdown; mod missing_headers; mod needless_doctest_main; mod suspicious_doc_comments; +mod too_long_first_doc_paragraph; declare_clippy_lint! { /// ### What it does @@ -98,7 +93,7 @@ declare_clippy_lint! { /// ```no_run /// //! [first](x)second /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub DOC_LINK_CODE, nursery, "link with code back-to-back with other code" @@ -193,6 +188,19 @@ declare_clippy_lint! { /// } /// } /// ``` + /// + /// Individual panics within a function can be ignored with `#[expect]` or + /// `#[allow]`: + /// + /// ```no_run + /// # use std::num::NonZeroUsize; + /// pub fn will_not_panic(x: usize) { + /// #[expect(clippy::missing_panics_doc, reason = "infallible")] + /// let y = NonZeroUsize::new(1).unwrap(); + /// + /// // If any panics are added in the future the lint will still catch them + /// } + /// ``` #[clippy::version = "1.51.0"] pub MISSING_PANICS_DOC, pedantic, @@ -567,6 +575,39 @@ declare_clippy_lint! { "link reference defined in list item or quote" } +declare_clippy_lint! { + /// ### What it does + /// Detects doc comment linebreaks that use double spaces to separate lines, instead of back-slash (`\`). + /// + /// ### Why is this bad? + /// Double spaces, when used as doc comment linebreaks, can be difficult to see, and may + /// accidentally be removed during automatic formatting or manual refactoring. The use of a back-slash (`\`) + /// is clearer in this regard. + /// + /// ### Example + /// The two replacement dots in this example represent a double space. + /// ```no_run + /// /// This command takes two numbers as inputs and·· + /// /// adds them together, and then returns the result. + /// fn add(l: i32, r: i32) -> i32 { + /// l + r + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// /// This command takes two numbers as inputs and\ + /// /// adds them together, and then returns the result. + /// fn add(l: i32, r: i32) -> i32 { + /// l + r + /// } + /// ``` + #[clippy::version = "1.87.0"] + pub DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS, + pedantic, + "double-space used for doc comment linebreak instead of `\\`" +} + pub struct Documentation { valid_idents: FxHashSet, check_private_items: bool, @@ -598,6 +639,7 @@ impl_lint_pass!(Documentation => [ DOC_OVERINDENTED_LIST_ITEMS, TOO_LONG_FIRST_DOC_PARAGRAPH, DOC_INCLUDE_WITHOUT_CFG, + DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS ]); impl EarlyLintPass for Documentation { @@ -622,20 +664,16 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { self.check_private_items, ); match item.kind { - ItemKind::Fn { sig, body: body_id, .. } => { + ItemKind::Fn { sig, body, .. } => { if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || item.span.in_external_macro(cx.tcx.sess.source_map())) { - let body = cx.tcx.hir_body(body_id); - - let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); missing_headers::check( cx, item.owner_id, sig, headers, - Some(body_id), - panic_info, + Some(body), self.check_private_items, ); } @@ -662,15 +700,7 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { if let TraitItemKind::Fn(sig, ..) = trait_item.kind && !trait_item.span.in_external_macro(cx.tcx.sess.source_map()) { - missing_headers::check( - cx, - trait_item.owner_id, - sig, - headers, - None, - None, - self.check_private_items, - ); + missing_headers::check(cx, trait_item.owner_id, sig, headers, None, self.check_private_items); } }, Node::ImplItem(impl_item) => { @@ -678,16 +708,12 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { && !impl_item.span.in_external_macro(cx.tcx.sess.source_map()) && !is_trait_impl_item(cx, impl_item.hir_id()) { - let body = cx.tcx.hir_body(body_id); - - let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(impl_item.owner_id), body.value); missing_headers::check( cx, impl_item.owner_id, sig, headers, Some(body_id), - panic_span, self.check_private_items, ); } @@ -845,19 +871,18 @@ fn check_for_code_clusters<'a, Events: Iterator{}", doc[start..end].replace('`', "")); - diag.span_suggestion_verbose( - span, - "wrap the entire group in `` tags", - sugg, - Applicability::MaybeIncorrect, - ); - diag.help("separate code snippets will be shown with a gap"); - }); - } + span_lint_and_then(cx, DOC_LINK_CODE, span, "code link adjacent to code text", |diag| { + let sugg = format!("{}", doc[start..end].replace('`', "")); + diag.span_suggestion_verbose( + span, + "wrap the entire group in `` tags", + sugg, + Applicability::MaybeIncorrect, + ); + diag.help("separate code snippets will be shown with a gap"); + }); } code_includes_link = false; code_starts_at = None; @@ -894,6 +919,7 @@ fn check_doc<'a, Events: Iterator, Range = Vec::new(); let mut is_first_paragraph = true; let mut containers = Vec::new(); @@ -1010,11 +1036,7 @@ fn check_doc<'a, Events: Iterator, Range range.start { - start - range.start - } else { - 0 - } + start.saturating_sub(range.start) } } else { 0 @@ -1073,6 +1095,14 @@ fn check_doc<'a, Events: Iterator, Range { paragraph_range.end = range.end; @@ -1123,73 +1153,10 @@ fn check_doc<'a, Events: Iterator, Range {} } } - headers -} - -struct FindPanicUnwrap<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - is_const: bool, - panic_span: Option, - typeck_results: &'tcx ty::TypeckResults<'tcx>, -} - -impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> { - pub fn find_span( - cx: &'a LateContext<'tcx>, - typeck_results: &'tcx ty::TypeckResults<'tcx>, - body: impl Visitable<'tcx>, - ) -> Option<(Span, bool)> { - let mut vis = Self { - cx, - is_const: false, - panic_span: None, - typeck_results, - }; - body.visit(&mut vis); - vis.panic_span.map(|el| (el, vis.is_const)) - } -} - -impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.panic_span.is_some() { - return; - } - - if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) { - if is_panic(self.cx, macro_call.def_id) - || matches!( - self.cx.tcx.item_name(macro_call.def_id).as_str(), - "assert" | "assert_eq" | "assert_ne" - ) - { - self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id); - self.panic_span = Some(macro_call.span); - } - } - - // check for `unwrap` and `expect` for both `Option` and `Result` - if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or(method_chain_args(expr, &["expect"])) { - let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option) - || is_type_diagnostic_item(self.cx, receiver_ty, sym::Result) - { - self.panic_span = Some(expr.span); - } - } - - // and check sub-expressions - intravisit::walk_expr(self, expr); - } - // Panics in const blocks will cause compilation to fail. - fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} + doc_comment_double_space_linebreaks::check(cx, &collected_breaks); - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.cx.tcx - } + headers } #[expect(clippy::range_plus_one)] // inclusive ranges aren't the same type diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs index 3008082c2329d..ec4538039a918 100644 --- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs +++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs @@ -13,16 +13,16 @@ use rustc_parse::parser::ForceCollect; use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; use rustc_span::source_map::{FilePathMapping, SourceMap}; -use rustc_span::{FileName, Pos, sym}; +use rustc_span::{FileName, Ident, Pos, sym}; use super::Fragments; -fn get_test_spans(item: &Item, test_attr_spans: &mut Vec>) { +fn get_test_spans(item: &Item, ident: Ident, test_attr_spans: &mut Vec>) { test_attr_spans.extend( item.attrs .iter() .find(|attr| attr.has_name(sym::test)) - .map(|attr| attr.span.lo().to_usize()..item.ident.span.hi().to_usize()), + .map(|attr| attr.span.lo().to_usize()..ident.span.hi().to_usize()), ); } @@ -38,7 +38,7 @@ pub fn check( // of all `#[test]` attributes in not ignored code examples fn check_code_sample(code: String, edition: Edition, ignore: bool) -> (bool, Vec>) { rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_globals_then(edition, None, || { + rustc_span::create_session_globals_then(edition, &[], None, || { let mut test_attr_spans = vec![]; let filename = FileName::anon_source_code(&code); @@ -64,10 +64,13 @@ pub fn check( match parser.parse_item(ForceCollect::No) { Ok(Some(item)) => match &item.kind { ItemKind::Fn(box Fn { - sig, body: Some(block), .. - }) if item.ident.name == sym::main => { + ident, + sig, + body: Some(block), + .. + }) if ident.name == sym::main => { if !ignore { - get_test_spans(&item, &mut test_attr_spans); + get_test_spans(&item, *ident, &mut test_attr_spans); } let is_async = matches!(sig.header.coroutine_kind, Some(CoroutineKind::Async { .. })); let returns_nothing = match &sig.decl.output { @@ -85,10 +88,10 @@ pub fn check( } }, // Another function was found; this case is ignored for needless_doctest_main - ItemKind::Fn(box Fn { .. }) => { + ItemKind::Fn(fn_) => { eligible = false; if !ignore { - get_test_spans(&item, &mut test_attr_spans); + get_test_spans(&item, fn_.ident, &mut test_attr_spans); } }, // Tests with one of these items are ignored diff --git a/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs b/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs index 9637546b86800..ebfc9972aefd6 100644 --- a/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs +++ b/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::AttrStyle; use rustc_ast::token::CommentKind; -use rustc_attr_parsing::AttributeKind; +use rustc_attr_data_structures::AttributeKind; use rustc_errors::Applicability; use rustc_hir::Attribute; use rustc_lint::LateContext; diff --git a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index dc6cbb4254302..7f7224ecfc654 100644 --- a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -1,4 +1,4 @@ -use rustc_attr_parsing::AttributeKind; +use rustc_attr_data_structures::AttributeKind; use rustc_errors::Applicability; use rustc_hir::{Attribute, Item, ItemKind}; use rustc_lint::LateContext; 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 617982f4da30f..5c360ce6a5f7e 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -144,10 +144,10 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { // .. // } fn is_single_call_in_arm<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'_>, drop_expr: &'tcx Expr<'_>) -> bool { - if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) { - if let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id) { - return body.hir_id == drop_expr.hir_id; - } + if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) + && let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id) + { + return body.hir_id == drop_expr.hir_id; } false } diff --git a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs index 1dac7b971f957..ce551a64d9984 100644 --- a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs +++ b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs @@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind}; use rustc_errors::MultiSpan; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; +use rustc_middle::lint::LevelAndSource; use rustc_session::impl_lint_pass; use rustc_span::{FileName, Span}; use std::collections::BTreeMap; @@ -45,11 +46,10 @@ declare_clippy_lint! { "file loaded as module multiple times" } -#[derive(PartialOrd, Ord, PartialEq, Eq)] struct Modules { local_path: PathBuf, spans: Vec, - lint_levels: Vec, + lint_levels: Vec, } #[derive(Default)] @@ -63,7 +63,7 @@ impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]); impl EarlyLintPass for DuplicateMod { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, mod_spans, _)) = &item.kind + if let ItemKind::Mod(_, _, ModKind::Loaded(_, Inline::No, mod_spans, _)) = &item.kind && let FileName::Real(real) = cx.sess().source_map().span_to_filename(mod_spans.inner_span) && let Some(local_path) = real.into_local_path() && let Ok(absolute_path) = local_path.canonicalize() @@ -95,11 +95,11 @@ impl EarlyLintPass for DuplicateMod { .iter() .zip(lint_levels) .filter_map(|(span, lvl)| { - if let Some(id) = lvl.get_expectation_id() { + if let Some(id) = lvl.lint_id { cx.fulfill_expectation(id); } - (!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span) + (!matches!(lvl.level, Level::Allow | Level::Expect)).then_some(*span) }) .collect(); diff --git a/src/tools/clippy/clippy_lints/src/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/empty_line_after.rs index 80c2b03c41cf4..0c5f8bbf4ca53 100644 --- a/src/tools/clippy/clippy_lints/src/empty_line_after.rs +++ b/src/tools/clippy/clippy_lints/src/empty_line_after.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{SpanRangeExt, snippet_indent}; use clippy_utils::tokenize_with_text; @@ -8,8 +10,7 @@ use rustc_errors::{Applicability, Diag, SuggestionStyle}; use rustc_lexer::TokenKind; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::kw; -use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol}; +use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw}; declare_clippy_lint! { /// ### What it does @@ -90,7 +91,7 @@ declare_clippy_lint! { #[derive(Debug)] struct ItemInfo { kind: &'static str, - name: Symbol, + name: Option, span: Span, mod_items: Option, } @@ -316,8 +317,12 @@ impl EmptyLineAfter { for stop in gaps.iter().flat_map(|gap| gap.prev_chunk) { stop.comment_out(cx, &mut suggestions); } + let name = match info.name { + Some(name) => format!("{} `{name}`", info.kind).into(), + None => Cow::from("the following item"), + }; diag.multipart_suggestion_verbose( - format!("if the doc comment should not document `{}` comment it out", info.name), + format!("if the doc comment should not document {name} then comment it out"), suggestions, Applicability::MaybeIncorrect, ); @@ -375,21 +380,20 @@ impl EmptyLineAfter { &mut self, cx: &EarlyContext<'_>, kind: &ItemKind, - ident: &Ident, + ident: Option, span: Span, attrs: &[Attribute], id: NodeId, ) { self.items.push(ItemInfo { kind: kind.descr(), - name: ident.name, - span: if span.contains(ident.span) { - span.with_hi(ident.span.hi()) - } else { - span.with_hi(span.lo()) + name: ident.map(|ident| ident.name), + span: match ident { + Some(ident) => span.with_hi(ident.span.hi()), + None => span.shrink_to_lo(), }, mod_items: match kind { - ItemKind::Mod(_, ModKind::Loaded(items, _, _, _)) => items + ItemKind::Mod(_, _, ModKind::Loaded(items, _, _, _)) => items .iter() .filter(|i| !matches!(i.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_))) .map(|i| i.id) @@ -446,7 +450,7 @@ impl EarlyLintPass for EmptyLineAfter { fn check_crate(&mut self, _: &EarlyContext<'_>, krate: &Crate) { self.items.push(ItemInfo { kind: "crate", - name: kw::Crate, + name: Some(kw::Crate), span: krate.spans.inner_span.with_hi(krate.spans.inner_span.lo()), mod_items: krate .items @@ -471,7 +475,7 @@ impl EarlyLintPass for EmptyLineAfter { self.check_item_kind( cx, &item.kind.clone().into(), - &item.ident, + item.kind.ident(), item.span, &item.attrs, item.id, @@ -482,7 +486,7 @@ impl EarlyLintPass for EmptyLineAfter { self.check_item_kind( cx, &item.kind.clone().into(), - &item.ident, + item.kind.ident(), item.span, &item.attrs, item.id, @@ -490,6 +494,6 @@ impl EarlyLintPass for EmptyLineAfter { } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - self.check_item_kind(cx, &item.kind, &item.ident, item.span, &item.attrs, item.id); + self.check_item_kind(cx, &item.kind, item.kind.ident(), item.span, &item.attrs, item.id); } } diff --git a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs index 743ec5b9ea7fb..8c12364883c7e 100644 --- a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs +++ b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs @@ -1,10 +1,15 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{Item, ItemKind, Variant, VariantData}; +use clippy_utils::attrs::span_contains_cfg; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; -use rustc_lexer::TokenKind; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::declare_lint_pass; +use rustc_hir::def::CtorOf; +use rustc_hir::def::DefKind::Ctor; +use rustc_hir::def::Res::Def; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node, Path, QPath, Variant, VariantData}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; +use rustc_session::impl_lint_pass; use rustc_span::Span; declare_clippy_lint! { @@ -70,14 +75,27 @@ declare_clippy_lint! { "finds enum variants with empty brackets" } -declare_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]); +#[derive(Debug)] +enum Usage { + Unused { redundant_use_sites: Vec }, + Used, + NoDefinition { redundant_use_sites: Vec }, +} + +#[derive(Default)] +pub struct EmptyWithBrackets { + // Value holds `Usage::Used` if the empty tuple variant was used as a function + empty_tuple_enum_variants: FxIndexMap, +} -impl EarlyLintPass for EmptyWithBrackets { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let span_after_ident = item.span.with_lo(item.ident.span.hi()); +impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]); - if let ItemKind::Struct(var_data, _) = &item.kind +impl LateLintPass<'_> for EmptyWithBrackets { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if let ItemKind::Struct(ident, var_data, _) = &item.kind + && !item.span.from_expansion() && has_brackets(var_data) + && let span_after_ident = item.span.with_lo(ident.span.hi()) && has_no_fields(cx, var_data, span_after_ident) { span_lint_and_then( @@ -97,70 +115,175 @@ impl EarlyLintPass for EmptyWithBrackets { } } - fn check_variant(&mut self, cx: &EarlyContext<'_>, variant: &Variant) { + fn check_variant(&mut self, cx: &LateContext<'_>, variant: &Variant<'_>) { + // the span of the parentheses/braces let span_after_ident = variant.span.with_lo(variant.ident.span.hi()); - if has_brackets(&variant.data) && has_no_fields(cx, &variant.data, span_after_ident) { - span_lint_and_then( + if has_no_fields(cx, &variant.data, span_after_ident) { + match variant.data { + VariantData::Struct { .. } => { + // Empty struct variants can be linted immediately + span_lint_and_then( + cx, + EMPTY_ENUM_VARIANTS_WITH_BRACKETS, + span_after_ident, + "enum variant has empty brackets", + |diagnostic| { + diagnostic.span_suggestion_hidden( + span_after_ident, + "remove the brackets", + "", + Applicability::MaybeIncorrect, + ); + }, + ); + }, + VariantData::Tuple(.., local_def_id) => { + // Don't lint reachable tuple enums + if cx.effective_visibilities.is_reachable(variant.def_id) { + return; + } + if let Some(entry) = self.empty_tuple_enum_variants.get_mut(&local_def_id) { + // empty_tuple_enum_variants contains Usage::NoDefinition if the variant was called before the + // definition was encountered. Now that there's a definition, convert it + // to Usage::Unused. + if let Usage::NoDefinition { redundant_use_sites } = entry { + *entry = Usage::Unused { + redundant_use_sites: redundant_use_sites.clone(), + }; + } + } else { + self.empty_tuple_enum_variants.insert( + local_def_id, + Usage::Unused { + redundant_use_sites: vec![], + }, + ); + } + }, + VariantData::Unit(..) => {}, + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let Some(def_id) = check_expr_for_enum_as_function(expr) { + if let Some(parentheses_span) = call_parentheses_span(cx.tcx, expr) { + // Do not count expressions from macro expansion as a redundant use site. + if expr.span.from_expansion() { + return; + } + match self.empty_tuple_enum_variants.get_mut(&def_id) { + Some( + &mut (Usage::Unused { + ref mut redundant_use_sites, + } + | Usage::NoDefinition { + ref mut redundant_use_sites, + }), + ) => { + redundant_use_sites.push(parentheses_span); + }, + None => { + // The variant isn't in the IndexMap which means its definition wasn't encountered yet. + self.empty_tuple_enum_variants.insert( + def_id, + Usage::NoDefinition { + redundant_use_sites: vec![parentheses_span], + }, + ); + }, + _ => {}, + } + } else { + // The parentheses are not redundant. + self.empty_tuple_enum_variants.insert(def_id, Usage::Used); + } + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'_>) { + for (local_def_id, usage) in &self.empty_tuple_enum_variants { + // Ignore all variants with Usage::Used or Usage::NoDefinition + let Usage::Unused { redundant_use_sites } = usage else { + continue; + }; + // Attempt to fetch the Variant from LocalDefId. + let Node::Variant(variant) = cx.tcx.hir_node( + cx.tcx + .local_def_id_to_hir_id(cx.tcx.parent(local_def_id.to_def_id()).expect_local()), + ) else { + continue; + }; + // Span of the parentheses in variant definition + let span = variant.span.with_lo(variant.ident.span.hi()); + span_lint_hir_and_then( cx, EMPTY_ENUM_VARIANTS_WITH_BRACKETS, - span_after_ident, + variant.hir_id, + span, "enum variant has empty brackets", |diagnostic| { - diagnostic.span_suggestion_hidden( - span_after_ident, - "remove the brackets", - "", - Applicability::MaybeIncorrect, - ); + if redundant_use_sites.is_empty() { + // If there's no redundant use sites, the definition is the only place to modify. + diagnostic.span_suggestion_hidden( + span, + "remove the brackets", + "", + Applicability::MaybeIncorrect, + ); + } else { + let mut parentheses_spans: Vec<_> = + redundant_use_sites.iter().map(|span| (*span, String::new())).collect(); + parentheses_spans.push((span, String::new())); + diagnostic.multipart_suggestion( + "remove the brackets", + parentheses_spans, + Applicability::MaybeIncorrect, + ); + } }, ); } } } -fn has_no_ident_token(braces_span_str: &str) -> bool { - !rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident) -} - -fn has_brackets(var_data: &VariantData) -> bool { - !matches!(var_data, VariantData::Unit(_)) +fn has_brackets(var_data: &VariantData<'_>) -> bool { + !matches!(var_data, VariantData::Unit(..)) } -fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool { - if !var_data.fields().is_empty() { - return false; - } - +fn has_no_fields(cx: &LateContext<'_>, var_data: &VariantData<'_>, braces_span: Span) -> bool { + var_data.fields().is_empty() && // there might still be field declarations hidden from the AST // (conditionally compiled code using #[cfg(..)]) - - let Some(braces_span_str) = snippet_opt(cx, braces_span) else { - return false; - }; - - has_no_ident_token(braces_span_str.as_ref()) + !span_contains_cfg(cx, braces_span) } -#[cfg(test)] -mod unit_test { - use super::*; - - #[test] - fn test_has_no_ident_token() { - let input = "{ field: u8 }"; - assert!(!has_no_ident_token(input)); - - let input = "(u8, String);"; - assert!(!has_no_ident_token(input)); - - let input = " { - // test = 5 - } - "; - assert!(has_no_ident_token(input)); +// If expression HIR ID and callee HIR ID are same, returns the span of the parentheses, else, +// returns None. +fn call_parentheses_span(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option { + if let Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) + && let ExprKind::Call(callee, ..) = parent.kind + && callee.hir_id == expr.hir_id + { + Some(parent.span.with_lo(expr.span.hi())) + } else { + None + } +} - let input = " ();"; - assert!(has_no_ident_token(input)); +// Returns the LocalDefId of the variant being called as a function if it exists. +fn check_expr_for_enum_as_function(expr: &Expr<'_>) -> Option { + if let ExprKind::Path(QPath::Resolved( + _, + Path { + res: Def(Ctor(CtorOf::Variant, _), def_id), + .. + }, + )) = expr.kind + { + def_id.as_local() + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index f404bc59b3b88..182cb4e46d2bd 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}; +use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ SpanlessEq, can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, @@ -84,9 +85,11 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { return; }; + let lint_msg = format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()); let mut app = Applicability::MachineApplicable; let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0; let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0; + let sugg = if let Some(else_expr) = else_expr { let Some(else_search) = find_insert_calls(cx, &contains_expr, else_expr) else { return; @@ -95,6 +98,10 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { if then_search.edits.is_empty() && else_search.edits.is_empty() { // No insertions return; + } else if then_search.is_key_used_and_no_copy || else_search.is_key_used_and_no_copy { + // If there are other uses of the key, and the key is not copy, + // we cannot perform a fix automatically, but continue to emit a lint. + None } else if then_search.edits.is_empty() || else_search.edits.is_empty() { // if .. { insert } else { .. } or if .. { .. } else { insert } let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) { @@ -115,10 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app), ), }; - format!( + Some(format!( "if let {}::{entry_kind} = {map_str}.entry({key_str}) {then_str} else {else_str}", map_ty.entry_path(), - ) + )) } else { // if .. { insert } else { insert } let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated { @@ -134,13 +141,13 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { }; let indent_str = snippet_indent(cx, expr.span); let indent_str = indent_str.as_deref().unwrap_or(""); - format!( + Some(format!( "match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\ {indent_str} {entry}::{else_entry} => {}\n{indent_str}}}", reindent_multiline(&then_str, true, Some(4 + indent_str.len())), reindent_multiline(&else_str, true, Some(4 + indent_str.len())), entry = map_ty.entry_path(), - ) + )) } } else { if then_search.edits.is_empty() { @@ -155,17 +162,17 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { } else { then_search.snippet_occupied(cx, then_expr.span, &mut app) }; - format!( + Some(format!( "if let {}::{entry_kind} = {map_str}.entry({key_str}) {body_str}", map_ty.entry_path(), - ) + )) } else if let Some(insertion) = then_search.as_single_insertion() { let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0; if contains_expr.negated { if insertion.value.can_have_side_effects() { - format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});") + Some(format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});")) } else { - format!("{map_str}.entry({key_str}).or_insert({value_str});") + Some(format!("{map_str}.entry({key_str}).or_insert({value_str});")) } } else { // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. @@ -175,7 +182,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { } else { let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app); if contains_expr.negated { - format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});") + Some(format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});")) } else { // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. // This would need to be a different lint. @@ -184,15 +191,11 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { } }; - span_lint_and_sugg( - cx, - MAP_ENTRY, - expr.span, - format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()), - "try", - sugg, - app, - ); + if let Some(sugg) = sugg { + span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app); + } else { + span_lint(cx, MAP_ENTRY, expr.span, lint_msg); + } } } @@ -354,6 +357,8 @@ struct InsertSearcher<'cx, 'tcx> { key: &'tcx Expr<'tcx>, /// The context of the top level block. All insert calls must be in the same context. ctxt: SyntaxContext, + /// The spanless equality utility used to compare expressions. + spanless_eq: SpanlessEq<'cx, 'tcx>, /// Whether this expression can be safely moved into a closure. allow_insert_closure: bool, /// Whether this expression can use the entry api. @@ -364,6 +369,8 @@ struct InsertSearcher<'cx, 'tcx> { is_single_insert: bool, /// If the visitor has seen the map being used. is_map_used: bool, + /// If the visitor has seen the key being used. + is_key_used: bool, /// The locations where changes need to be made for the suggestion. edits: Vec>, /// A stack of loops the visitor is currently in. @@ -479,11 +486,11 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { } match try_parse_insert(self.cx, expr) { - Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => { + Some(insert_expr) if self.spanless_eq.eq_expr(self.map, insert_expr.map) => { self.visit_insert_expr_arguments(&insert_expr); // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api. if self.is_map_used - || !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key) + || !self.spanless_eq.eq_expr(self.key, insert_expr.key) || expr.span.ctxt() != self.ctxt { self.can_use_entry = false; @@ -502,9 +509,12 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { self.visit_non_tail_expr(insert_expr.value); self.is_single_insert = is_single_insert; }, - _ if is_any_expr_in_map_used(self.cx, self.map, expr) => { + _ if is_any_expr_in_map_used(self.cx, &mut self.spanless_eq, self.map, expr) => { self.is_map_used = true; }, + _ if self.spanless_eq.eq_expr(self.key, expr) => { + self.is_key_used = true; + }, _ => match expr.kind { ExprKind::If(cond_expr, then_expr, Some(else_expr)) => { self.is_single_insert = false; @@ -568,9 +578,14 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { /// Check if the given expression is used for each sub-expression in the given map. /// For example, in map `a.b.c.my_map`, The expression `a.b.c.my_map`, `a.b.c`, `a.b`, and `a` are /// all checked. -fn is_any_expr_in_map_used<'tcx>(cx: &LateContext<'tcx>, map: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { +fn is_any_expr_in_map_used<'tcx>( + cx: &LateContext<'tcx>, + spanless_eq: &mut SpanlessEq<'_, 'tcx>, + map: &'tcx Expr<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> bool { for_each_expr(cx, map, |e| { - if SpanlessEq::new(cx).eq_expr(e, expr) { + if spanless_eq.eq_expr(e, expr) { return ControlFlow::Break(()); } ControlFlow::Continue(()) @@ -582,6 +597,7 @@ struct InsertSearchResults<'tcx> { edits: Vec>, allow_insert_closure: bool, is_single_insert: bool, + is_key_used_and_no_copy: bool, } impl<'tcx> InsertSearchResults<'tcx> { fn as_single_insertion(&self) -> Option> { @@ -694,11 +710,13 @@ fn find_insert_calls<'tcx>( map: contains_expr.map, key: contains_expr.key, ctxt: expr.span.ctxt(), + spanless_eq: SpanlessEq::new(cx), allow_insert_closure: true, can_use_entry: true, in_tail_pos: true, is_single_insert: true, is_map_used: false, + is_key_used: false, edits: Vec::new(), loops: Vec::new(), locals: HirIdSet::default(), @@ -706,10 +724,12 @@ fn find_insert_calls<'tcx>( s.visit_expr(expr); let allow_insert_closure = s.allow_insert_closure; let is_single_insert = s.is_single_insert; + let is_key_used_and_no_copy = s.is_key_used && !is_copy(cx, cx.typeck_results().expr_ty(contains_expr.key)); let edits = s.edits; s.can_use_entry.then_some(InsertSearchResults { edits, allow_insert_closure, is_single_insert, + is_key_used_and_no_copy, }) } diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs index 2e1f8ac615a23..ec81294624efa 100644 --- a/src/tools/clippy/clippy_lints/src/enum_clike.rs +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -38,7 +38,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { if cx.tcx.data_layout.pointer_size.bits() != 64 { return; } - if let ItemKind::Enum(def, _) = &item.kind { + if let ItemKind::Enum(_, def, _) = &item.kind { for var in def.variants { if let Some(anon_const) = &var.disr_expr { let def_id = cx.tcx.hir_body_owner_def_id(anon_const.body); @@ -49,10 +49,10 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty)); if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) { - if let ty::Adt(adt, _) = ty.kind() { - if adt.is_enum() { - ty = adt.repr().discr_type().to_ty(cx.tcx); - } + if let ty::Adt(adt, _) = ty.kind() + && adt.is_enum() + { + ty = adt.repr().discr_type().to_ty(cx.tcx); } match ty.kind() { ty::Int(IntTy::Isize) => { diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index cd9ab2764ac48..72f5eaf8a4bcc 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -45,6 +45,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { pats.iter().all(unary_pattern) } match &pat.kind { + PatKind::Missing => unreachable!(), PatKind::Slice(_, _, _) | PatKind::Range(_, _, _) | PatKind::Binding(..) @@ -67,6 +68,38 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T } } +/// Check if the pattern has any type mismatch that would prevent it from being used in an equality +/// check. This can happen if the expr has a reference type and the corresponding pattern is a +/// literal. +fn contains_type_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + let mut result = false; + pat.walk(|p| { + if result { + return false; + } + + if p.span.in_external_macro(cx.sess().source_map()) { + return true; + } + + let adjust_pat = match p.kind { + PatKind::Or([p, ..]) => p, + _ => p, + }; + + if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) + && adjustments.first().is_some_and(|first| first.source.is_ref()) + { + result = true; + return false; + } + + true + }); + + result +} + impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Let(let_expr) = expr.kind @@ -77,7 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); let mut applicability = Applicability::MachineApplicable; - if is_structural_partial_eq(cx, exp_ty, pat_ty) { + if is_structural_partial_eq(cx, exp_ty, pat_ty) && !contains_type_mismatch(cx, let_expr.pat) { let pat_str = match let_expr.pat.kind { PatKind::Struct(..) => format!( "({})", diff --git a/src/tools/clippy/clippy_lints/src/error_impl_error.rs b/src/tools/clippy/clippy_lints/src/error_impl_error.rs index 1e6447dc25370..6525648efb1e9 100644 --- a/src/tools/clippy/clippy_lints/src/error_impl_error.rs +++ b/src/tools/clippy/clippy_lints/src/error_impl_error.rs @@ -37,8 +37,8 @@ declare_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]); impl<'tcx> LateLintPass<'tcx> for ErrorImplError { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { match item.kind { - ItemKind::TyAlias(..) - if item.ident.name == sym::Error + ItemKind::TyAlias(ident, ..) + if ident.name == sym::Error && is_visible_outside_module(cx, item.owner_id.def_id) && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) @@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { span_lint( cx, ERROR_IMPL_ERROR, - item.ident.span, + ident.span, "exported type alias named `Error` that implements `Error`", ); }, diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index 8d1e893cb1af2..2cb3b32babe89 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -72,10 +72,10 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { _: Span, fn_def_id: LocalDefId, ) { - if let Some(header) = fn_kind.header() { - if header.abi != ExternAbi::Rust { - return; - } + if let Some(header) = fn_kind.header() + && header.abi != ExternAbi::Rust + { + return; } let parent_id = cx @@ -91,14 +91,13 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { } // find `self` ty for this trait if relevant - if let ItemKind::Trait(_, _, _, _, items) = item.kind { + if let ItemKind::Trait(_, _, _, _, _, items) = item.kind { for trait_item in items { - if trait_item.id.owner_id.def_id == fn_def_id { + if trait_item.id.owner_id.def_id == fn_def_id // be sure we have `self` parameter in this function - if trait_item.kind == (AssocItemKind::Fn { has_self: true }) { - trait_self_ty = - Some(TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id()).self_ty()); - } + && trait_item.kind == (AssocItemKind::Fn { has_self: true }) + { + trait_self_ty = Some(TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id()).self_ty()); } } } @@ -120,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { cx, BOXED_LOCAL, node, - cx.tcx.hir().span(node), + cx.tcx.hir_span(node), "local variable doesn't need to be boxed here", ); } @@ -142,44 +141,44 @@ fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool { impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> { fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { - if cmt.place.projections.is_empty() { - if let PlaceBase::Local(lid) = cmt.place.base { - // FIXME(rust/#120456) - is `swap_remove` correct? - self.set.swap_remove(&lid); - } + if cmt.place.projections.is_empty() + && let PlaceBase::Local(lid) = cmt.place.base + { + // FIXME(rust/#120456) - is `swap_remove` correct? + self.set.swap_remove(&lid); } } + fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) { - if cmt.place.projections.is_empty() { - if let PlaceBase::Local(lid) = cmt.place.base { - // FIXME(rust/#120456) - is `swap_remove` correct? - self.set.swap_remove(&lid); - } + if cmt.place.projections.is_empty() + && let PlaceBase::Local(lid) = cmt.place.base + { + // FIXME(rust/#120456) - is `swap_remove` correct? + self.set.swap_remove(&lid); } } fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { - if cmt.place.projections.is_empty() { - let map = &self.cx.tcx.hir(); - if is_argument(self.cx.tcx, cmt.hir_id) { - // Skip closure arguments - let parent_id = self.cx.tcx.parent_hir_id(cmt.hir_id); - if let Node::Expr(..) = self.cx.tcx.parent_hir_node(parent_id) { - return; - } + if cmt.place.projections.is_empty() && is_argument(self.cx.tcx, cmt.hir_id) { + // Skip closure arguments + let parent_id = self.cx.tcx.parent_hir_id(cmt.hir_id); + if let Node::Expr(..) = self.cx.tcx.parent_hir_node(parent_id) { + return; + } - // skip if there is a `self` parameter binding to a type - // that contains `Self` (i.e.: `self: Box`), see #4804 - if let Some(trait_self_ty) = self.trait_self_ty { - if map.name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) { - return; - } - } + // skip if there is a `self` parameter binding to a type + // that contains `Self` (i.e.: `self: Box`), see #4804 + if let Some(trait_self_ty) = self.trait_self_ty + && self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower + && cmt.place.ty().contains(trait_self_ty) + { + return; + } - if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) { - self.set.insert(cmt.hir_id); - } + if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) { + self.set.insert(cmt.hir_id); } } } diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs index 54a1ac21c85c3..38d115b878c71 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs @@ -15,12 +15,17 @@ declare_clippy_lint! { /// use of bools in structs. /// /// ### Why is this bad? - /// Excessive bools in a struct - /// is often a sign that it's used as a state machine, - /// which is much better implemented as an enum. - /// If it's not the case, excessive bools usually benefit - /// from refactoring into two-variant enums for better - /// readability and API. + /// Excessive bools in a struct is often a sign that + /// the type is being used to represent a state + /// machine, which is much better implemented as an + /// enum. + /// + /// The reason an enum is better for state machines + /// over structs is that enums more easily forbid + /// invalid states. + /// + /// Structs with too many booleans may benefit from refactoring + /// into multi variant enums for better readability and API. /// /// ### Example /// ```no_run @@ -122,7 +127,7 @@ fn check_fn_decl(cx: &LateContext<'_>, decl: &FnDecl<'_>, sp: Span, max: u64) { impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Struct(variant_data, _) = &item.kind + if let ItemKind::Struct(_, variant_data, _) = &item.kind && variant_data.fields().len() as u64 > self.max_struct_bools && has_n_bools( variant_data.fields().iter().map(|field| field.ty), diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs index 9bf3baba4b597..5a74e97c97c5e 100644 --- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs +++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs @@ -76,7 +76,7 @@ impl LateLintPass<'_> for ExhaustiveItems { "exported enums should not be exhaustive", [].as_slice(), ), - ItemKind::Struct(v, ..) => ( + ItemKind::Struct(_, v, ..) => ( EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive", v.fields(), @@ -84,7 +84,7 @@ impl LateLintPass<'_> for ExhaustiveItems { _ => return, }; if cx.effective_visibilities.is_exported(item.owner_id.def_id) - && let attrs = cx.tcx.hir().attrs(item.hir_id()) + && let attrs = cx.tcx.hir_attrs(item.hir_id()) && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) && fields.iter().all(|f| cx.tcx.visibility(f.def_id).is_public()) { diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index a5a4e05b3a6d2..085ee4448a4ee 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_expn_of, path_def_id}; +use clippy_utils::{is_expn_of, path_def_id, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{ExpnId, sym}; +use rustc_span::ExpnId; declare_clippy_lint! { /// ### What it does @@ -72,9 +72,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { }; // ordering is important here, since `writeln!` uses `write!` internally - let calling_macro = if is_expn_of(write_call.span, "writeln").is_some() { + let calling_macro = if is_expn_of(write_call.span, sym::writeln).is_some() { Some("writeln") - } else if is_expn_of(write_call.span, "write").is_some() { + } else if is_expn_of(write_call.span, sym::write).is_some() { Some("write") } else { None diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index 6a217b6182ce7..c0b0fd88d9e16 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -273,7 +273,7 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { // Only lint on inherent methods, not trait methods. if let ImplItemKind::Fn(.., body_id) = item.kind && !item.generics.params.is_empty() - && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + && trait_ref_of_method(cx, item.owner_id).is_none() && !is_empty_body(cx, body_id) && (!self.avoid_breaking_exported_api || !cx.effective_visibilities.is_exported(item.owner_id.def_id)) && !item.span.in_external_macro(cx.sess().source_map()) diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs index f67d38d932b9a..68d0cd19c8a69 100644 --- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -75,14 +75,14 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) { - if is_panic(self.lcx, macro_call.def_id) { - self.result.push(expr.span); - } + if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) + && is_panic(self.lcx, macro_call.def_id) + { + self.result.push(expr.span); } // check for `unwrap` - if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { + if let Some(arglists) = method_chain_args(expr, &[sym::unwrap]) { let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option) || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result) diff --git a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs index ba2b37fbf11a3..aae8291905d37 100644 --- a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -51,7 +51,7 @@ declare_lint_pass!(FieldScopedVisibilityModifiers => [FIELD_SCOPED_VISIBILITY_MO impl EarlyLintPass for FieldScopedVisibilityModifiers { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let ItemKind::Struct(ref st, _) = item.kind else { + let ItemKind::Struct(_, ref st, _) = item.kind else { return; }; for field in st.fields() { diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index 012ad8e1a2298..c51267567d01d 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { }, ); } - } else if digits > max as usize && float_str.len() < sym_str.len() { + } else if digits > max as usize && count_digits(&float_str) < count_digits(sym_str) { span_lint_and_then( cx, EXCESSIVE_PRECISION, 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 daa199779e3cb..3c7e83b069726 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -3,7 +3,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate, - numeric_literal, peel_blocks, sugg, + numeric_literal, peel_blocks, sugg, sym, }; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -154,7 +154,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su }; } - suggestion.maybe_par() + suggestion.maybe_paren() } fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { @@ -165,7 +165,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, ar expr.span, "logarithm for bases 2, 10 and e can be computed more accurately", "consider using", - format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_par()), + format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_paren()), Applicability::MachineApplicable, ); } @@ -228,24 +228,24 @@ fn get_integer_from_float_constant(value: &Constant<'_>) -> Option { fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { // Check receiver - if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) { - if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { + if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) + && let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { Some("exp") } else if F32(2.0) == value || F64(2.0) == value { Some("exp2") } else { None - } { - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - "exponent for bases 2 and e can be computed more accurately", - "consider using", - format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])), - Applicability::MachineApplicable, - ); } + { + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "exponent for bases 2 and e can be computed more accurately", + "consider using", + format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])), + Applicability::MachineApplicable, + ); } // Check argument @@ -254,13 +254,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: ( SUBOPTIMAL_FLOPS, "square-root of a number can be computed more efficiently and accurately", - format!("{}.sqrt()", Sugg::hir(cx, receiver, "..").maybe_par()), + format!("{}.sqrt()", Sugg::hir(cx, receiver, "..").maybe_paren()), ) } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value { ( IMPRECISE_FLOPS, "cube-root of a number can be computed more accurately", - format!("{}.cbrt()", Sugg::hir(cx, receiver, "..").maybe_par()), + format!("{}.cbrt()", Sugg::hir(cx, receiver, "..").maybe_paren()), ) } else if let Some(exponent) = get_integer_from_float_constant(&value) { ( @@ -268,7 +268,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: "exponentiation with integer powers can be computed more efficiently", format!( "{}.powi({})", - Sugg::hir(cx, receiver, "..").maybe_par(), + Sugg::hir(cx, receiver, "..").maybe_paren(), numeric_literal::format(&exponent.to_string(), None, false) ), ) @@ -289,55 +289,53 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) { - if value == Int(2) { - if let Some(parent) = get_parent_expr(cx, expr) { - if let Some(grandparent) = get_parent_expr(cx, parent) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind - { - if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() { - return; - } - } - } + if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) + && value == Int(2) + && let Some(parent) = get_parent_expr(cx, expr) + { + if let Some(grandparent) = get_parent_expr(cx, parent) + && let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = grandparent.kind + && method.name == sym::sqrt + && detect_hypot(cx, receiver).is_some() + { + return; + } - if let ExprKind::Binary( - Spanned { - node: op @ (BinOpKind::Add | BinOpKind::Sub), - .. - }, - lhs, - rhs, - ) = parent.kind - { - let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; - - // Negate expr if original code has subtraction and expr is on the right side - let maybe_neg_sugg = |expr, hir_id| { - let sugg = Sugg::hir(cx, expr, ".."); - if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { - -sugg - } else { - sugg - } - }; - - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - parent.span, - "multiply and add expressions can be calculated more efficiently and accurately", - "consider using", - format!( - "{}.mul_add({}, {})", - Sugg::hir(cx, receiver, "..").maybe_par(), - maybe_neg_sugg(receiver, expr.hir_id), - maybe_neg_sugg(other_addend, other_addend.hir_id), - ), - Applicability::MachineApplicable, - ); + if let ExprKind::Binary( + Spanned { + node: op @ (BinOpKind::Add | BinOpKind::Sub), + .. + }, + lhs, + rhs, + ) = parent.kind + { + let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; + + // Negate expr if original code has subtraction and expr is on the right side + let maybe_neg_sugg = |expr, hir_id| { + let sugg = Sugg::hir(cx, expr, ".."); + if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { + -sugg + } else { + sugg } - } + }; + + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + parent.span, + "multiply and add expressions can be calculated more efficiently and accurately", + "consider using", + format!( + "{}.mul_add({}, {})", + Sugg::hir(cx, receiver, "..").maybe_paren(), + maybe_neg_sugg(receiver, expr.hir_id), + maybe_neg_sugg(other_addend, other_addend.hir_id), + ), + Applicability::MachineApplicable, + ); } } } @@ -371,30 +369,16 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { { return Some(format!( "{}.hypot({})", - Sugg::hir(cx, lmul_lhs, "..").maybe_par(), + Sugg::hir(cx, lmul_lhs, "..").maybe_paren(), Sugg::hir(cx, rmul_lhs, "..") )); } // check if expression of the form x.powi(2) + y.powi(2) - if let ExprKind::MethodCall( - PathSegment { - ident: lmethod_name, .. - }, - largs_0, - [largs_1, ..], - _, - ) = &add_lhs.kind - && let ExprKind::MethodCall( - PathSegment { - ident: rmethod_name, .. - }, - rargs_0, - [rargs_1, ..], - _, - ) = &add_rhs.kind - && lmethod_name.as_str() == "powi" - && rmethod_name.as_str() == "powi" + if let ExprKind::MethodCall(PathSegment { ident: lmethod, .. }, largs_0, [largs_1, ..], _) = &add_lhs.kind + && let ExprKind::MethodCall(PathSegment { ident: rmethod, .. }, rargs_0, [rargs_1, ..], _) = &add_rhs.kind + && lmethod.name == sym::powi + && rmethod.name == sym::powi && let ecx = ConstEvalCtxt::new(cx) && let Some(lvalue) = ecx.eval(largs_1) && let Some(rvalue) = ecx.eval(rargs_1) @@ -403,7 +387,7 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { { return Some(format!( "{}.hypot({})", - Sugg::hir(cx, largs_0, "..").maybe_par(), + Sugg::hir(cx, largs_0, "..").maybe_paren(), Sugg::hir(cx, rargs_0, "..") )); } @@ -437,7 +421,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = expr.kind && let ExprKind::MethodCall(path, self_arg, [], _) = &lhs.kind - && path.ident.name.as_str() == "exp" + && path.ident.name == sym::exp && cx.typeck_results().expr_ty(lhs).is_floating_point() && let Some(value) = ConstEvalCtxt::new(cx).eval(rhs) && (F32(1.0) == value || F64(1.0) == value) @@ -449,7 +433,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "(e.pow(x) - 1) can be computed more accurately", "consider using", - format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_par()), + format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_paren()), Applicability::MachineApplicable, ); } @@ -483,12 +467,12 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = &expr.kind { - if let Some(parent) = get_parent_expr(cx, expr) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind { - if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() { - return; - } - } + if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = parent.kind + && method.name == sym::sqrt + && detect_hypot(cx, receiver).is_some() + { + return; } let maybe_neg_sugg = |expr| { @@ -566,15 +550,15 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// If the two expressions are not negations of each other, then it /// 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::Neg, expr1_negated) = &expr1.kind { - if eq_expr_value(cx, expr1_negated, expr2) { - return Some((false, expr2)); - } + if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind + && eq_expr_value(cx, expr1_negated, expr2) + { + return Some((false, expr2)); } - if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind { - if eq_expr_value(cx, expr1, expr2_negated) { - return Some((true, expr1)); - } + if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind + && eq_expr_value(cx, expr1, expr2_negated) + { + return Some((true, expr1)); } None } @@ -591,11 +575,11 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { { let positive_abs_sugg = ( "manual implementation of `abs` method", - format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), + format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_paren()), ); let negative_abs_sugg = ( "manual implementation of negation of `abs` method", - format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), + format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_paren()), ); let sugg = if is_testing_positive(cx, cond, body) { if if_expr_positive { @@ -625,27 +609,13 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { } fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { - if let ExprKind::MethodCall( - PathSegment { - ident: method_name_a, .. - }, - _, - args_a, - _, - ) = expr_a.kind - && let ExprKind::MethodCall( - PathSegment { - ident: method_name_b, .. - }, - _, - args_b, - _, - ) = expr_b.kind + if let ExprKind::MethodCall(PathSegment { ident: method_a, .. }, _, args_a, _) = expr_a.kind + && let ExprKind::MethodCall(PathSegment { ident: method_b, .. }, _, args_b, _) = expr_b.kind { - return method_name_a.as_str() == method_name_b.as_str() + return method_a.name == method_b.name && args_a.len() == args_b.len() - && (["ln", "log2", "log10"].contains(&method_name_a.as_str()) - || method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])); + && (matches!(method_a.name, sym::ln | sym::log2 | sym::log10) + || method_a.name == sym::log && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])); } false @@ -672,7 +642,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { "consider using", format!( "{}.log({})", - Sugg::hir(cx, largs_self, "..").maybe_par(), + Sugg::hir(cx, largs_self, "..").maybe_paren(), Sugg::hir(cx, rargs_self, ".."), ), Applicability::MachineApplicable, @@ -703,7 +673,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && (F32(180_f32) == lvalue || F64(180_f64) == lvalue) { - let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); + let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_paren()); if let ExprKind::Lit(literal) = mul_lhs.kind && let ast::LitKind::Float(ref value, float_type) = literal.node && float_type == ast::LitFloatType::Unsuffixed @@ -726,7 +696,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { } else if (F32(180_f32) == rvalue || F64(180_f64) == rvalue) && (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue) { - let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); + let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_paren()); if let ExprKind::Lit(literal) = mul_lhs.kind && let ast::LitKind::Float(ref value, float_type) = literal.node && float_type == ast::LitFloatType::Unsuffixed @@ -761,12 +731,12 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { let recv_ty = cx.typeck_results().expr_ty(receiver); if recv_ty.is_floating_point() && !is_no_std_crate(cx) && is_inherent_method_call(cx, expr) { - match path.ident.name.as_str() { - "ln" => check_ln1p(cx, expr, receiver), - "log" => check_log_base(cx, expr, receiver, args), - "powf" => check_powf(cx, expr, receiver, args), - "powi" => check_powi(cx, expr, receiver, args), - "sqrt" => check_hypot(cx, expr, receiver), + match path.ident.name { + sym::ln => check_ln1p(cx, expr, receiver), + sym::log => check_log_base(cx, expr, receiver, args), + sym::powf => check_powf(cx, expr, receiver, args), + sym::powi => check_powi(cx, expr, receiver, args), + sym::sqrt => check_hypot(cx, expr, receiver), _ => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 5e3f6b6a13707..94e66769eb265 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { .into_owned() } else { let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "", &mut applicability); - format!("{}.to_string()", sugg.maybe_par()) + format!("{}.to_string()", sugg.maybe_paren()) }; span_useless_format(cx, call_site, sugg, applicability); } diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index fc5f76179f907..a26e736c7ae35 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -9,13 +9,13 @@ use clippy_utils::macros::{ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{implements_trait, is_type_lang_item}; -use clippy_utils::{is_diag_trait_item, is_from_proc_macro}; +use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test}; use itertools::Itertools; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, FormatPlaceholder, FormatTrait, }; -use rustc_attr_parsing::RustcVersion; +use rustc_attr_data_structures::RustcVersion; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode}; @@ -141,7 +141,7 @@ declare_clippy_lint! { /// format!("{var:.prec$}"); /// ``` /// - /// If allow-mixed-uninlined-format-args is set to false in clippy.toml, + /// If `allow-mixed-uninlined-format-args` is set to `false` in clippy.toml, /// the following code will also trigger the lint: /// ```no_run /// # let var = 42; @@ -159,7 +159,7 @@ declare_clippy_lint! { /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.66.0"] pub UNINLINED_FORMAT_ARGS, - pedantic, + style, "using non-inlined variables in `format!` calls" } @@ -484,7 +484,8 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { fn check_unnecessary_debug_formatting(&self, name: Symbol, value: &Expr<'tcx>) { let cx = self.cx; - if !value.span.from_expansion() + if !is_in_test(cx.tcx, value.hir_id) + && !value.span.from_expansion() && !is_from_proc_macro(cx, value) && let ty = cx.typeck_results().expr_ty(value) && self.can_display_format(ty) @@ -549,7 +550,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { // a `Target` that is in `self.ty_msrv_map`. if let Some(deref_trait_id) = self.cx.tcx.lang_items().deref_trait() && implements_trait(self.cx, ty, deref_trait_id, &[]) - && let Some(target_ty) = self.cx.get_associated_type(ty, deref_trait_id, "Target") + && let Some(target_ty) = self.cx.get_associated_type(ty, deref_trait_id, sym::Target) && let Some(msrv) = self.ty_msrv_map.get(&target_ty) && msrv.is_none_or(|msrv| self.msrv.meets(self.cx, msrv)) { diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index ff75fcf2b417c..0535ecf5240f9 100644 --- a/src/tools/clippy/clippy_lints/src/format_impl.rs +++ b/src/tools/clippy/clippy_lints/src/format_impl.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; -use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators}; +use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators, sym}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; +use rustc_span::Symbol; use rustc_span::symbol::kw; -use rustc_span::{Symbol, sym}; declare_clippy_lint! { /// ### What it does @@ -185,13 +185,13 @@ impl FormatImplExpr<'_, '_> { && let trait_name = match placeholder.format_trait { FormatTrait::Display => sym::Display, FormatTrait::Debug => sym::Debug, - FormatTrait::LowerExp => sym!(LowerExp), - FormatTrait::UpperExp => sym!(UpperExp), - FormatTrait::Octal => sym!(Octal), + FormatTrait::LowerExp => sym::LowerExp, + FormatTrait::UpperExp => sym::UpperExp, + FormatTrait::Octal => sym::Octal, FormatTrait::Pointer => sym::Pointer, - FormatTrait::Binary => sym!(Binary), - FormatTrait::LowerHex => sym!(LowerHex), - FormatTrait::UpperHex => sym!(UpperHex), + FormatTrait::Binary => sym::Binary, + FormatTrait::LowerHex => sym::LowerHex, + FormatTrait::UpperHex => sym::UpperHex, } && trait_name == self.format_trait_impl.name && let Ok(index) = placeholder.argument.index @@ -209,9 +209,8 @@ impl FormatImplExpr<'_, '_> { // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl) // Since the argument to fmt is itself a reference: &self let reference = peel_ref_operators(self.cx, arg); - let map = self.cx.tcx.hir(); // Is the reference self? - if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) { + if path_to_local(reference).map(|x| self.cx.tcx.hir_name(x)) == Some(kw::SelfLower) { let FormatTraitNames { name, .. } = self.format_trait_impl; span_lint( self.cx, diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs index 68cc50f39391f..b64d608c0c709 100644 --- a/src/tools/clippy/clippy_lints/src/format_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; use clippy_utils::ty::is_type_lang_item; -use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource}; +use rustc_hir::{AssignOpKind, Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString { return; } }, - ExprKind::AssignOp(op, left, arg) if op.node == BinOpKind::Add && is_string(cx, left) => arg, + ExprKind::AssignOp(op, left, arg) if op.node == AssignOpKind::AddAssign && is_string(cx, left) => arg, _ => return, }; if is_format(cx, arg) { diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs index c8fe7ac73cb34..4b482f7b233b8 100644 --- a/src/tools/clippy/clippy_lints/src/formatting.rs +++ b/src/tools/clippy/clippy_lints/src/formatting.rs @@ -138,27 +138,28 @@ impl EarlyLintPass for Formatting { /// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint. fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) { - if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind { - if !lhs.span.from_expansion() && !rhs.span.from_expansion() { - let eq_span = lhs.span.between(rhs.span); - if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind { - if let Some(eq_snippet) = snippet_opt(cx, eq_span) { - let op = op.as_str(); - let eqop_span = lhs.span.between(sub_rhs.span); - if eq_snippet.ends_with('=') { - span_lint_and_note( - cx, - SUSPICIOUS_ASSIGNMENT_FORMATTING, - eqop_span, - format!( - "this looks like you are trying to use `.. {op}= ..`, but you \ + if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind + && !lhs.span.from_expansion() + && !rhs.span.from_expansion() + { + let eq_span = lhs.span.between(rhs.span); + if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind + && let Some(eq_snippet) = snippet_opt(cx, eq_span) + { + let op = op.as_str(); + let eqop_span = lhs.span.between(sub_rhs.span); + if eq_snippet.ends_with('=') { + span_lint_and_note( + cx, + SUSPICIOUS_ASSIGNMENT_FORMATTING, + eqop_span, + format!( + "this looks like you are trying to use `.. {op}= ..`, but you \ really are doing `.. = ({op} ..)`" - ), - None, - format!("to remove this lint, use either `{op}=` or `= {op}`"), - ); - } - } + ), + None, + format!("to remove this lint, use either `{op}=` or `= {op}`"), + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs index 0bdb99d7b9a4d..8822b87f92f72 100644 --- a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs +++ b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs @@ -43,8 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { let sm = cx.sess().source_map(); let mut span = cx .tcx - .hir() - .attrs(item.hir_id()) + .hir_attrs(item.hir_id()) .iter() .filter(|i| i.is_doc_comment()) .fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span())); diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index 6da5567d9c709..be887b03ae4b6 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -176,8 +176,8 @@ fn convert_to_from( return None; }; let body = cx.tcx.hir_body(body_id); - let [input] = body.params else { return None }; - let PatKind::Binding(.., self_ident, None) = input.pat.kind else { + let [self_param] = body.params else { return None }; + let PatKind::Binding(.., self_ident, None) = self_param.pat.kind else { return None; }; @@ -194,12 +194,12 @@ fn convert_to_from( // impl Into for U -> impl Into for T // ~ ~ (self_ty.span, into.to_owned()), - // fn into(self) -> T -> fn from(self) -> T - // ~~~~ ~~~~ + // fn into(self: U) -> T -> fn from(self) -> T + // ~~~~ ~~~~ (impl_item.ident.span, String::from("from")), - // fn into([mut] self) -> T -> fn into([mut] v: T) -> T - // ~~~~ ~~~~ - (self_ident.span, format!("val: {from}")), + // fn into([mut] self: U) -> T -> fn into([mut] val: T) -> T + // ~~~~~~~ ~~~~~~ + (self_ident.span.to(self_param.ty_span), format!("val: {from}")), ]; if let FnRetTy::Return(_) = sig.decl.output { diff --git a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs index c8828c9361576..5e2e2c9dbf725 100644 --- a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_def_id; use clippy_utils::ty::is_c_void; +use clippy_utils::{path_def_id, sym}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -41,7 +40,7 @@ impl LateLintPass<'_> for FromRawWithVoidPtr { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(box_from_raw, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind - && seg.ident.name.as_str() == "from_raw" + && seg.ident.name == sym::from_raw && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) && let arg_kind = cx.typeck_results().expr_ty(arg).kind() && let ty::RawPtr(ty, _) = arg_kind diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs index 7361546153c27..b816963cc825b 100644 --- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs +++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{is_in_const_context, is_integer_literal}; +use clippy_utils::{is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -53,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { // check if the second part of the path indeed calls the associated // function `from_str_radix` - && pathseg.ident.name.as_str() == "from_str_radix" + && pathseg.ident.name == sym::from_str_radix // check if the first part of the path is some integer primitive && let TyKind::Path(ty_qpath) = &ty.kind @@ -73,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { }; let sugg = - Sugg::hir_with_applicability(cx, expr, "", &mut Applicability::MachineApplicable).maybe_par(); + Sugg::hir_with_applicability(cx, expr, "", &mut Applicability::MachineApplicable).maybe_paren(); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs index 854fe144c2919..fa63876410f01 100644 --- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs +++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, ImplicitSelfKind}; +use rustc_hir::{BlockCheckMode, Body, ExprKind, FnDecl, ImplicitSelfKind, UnsafeSource}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; @@ -40,14 +40,25 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: name }; - // Body must be &(mut) .name + // Body must be `&(mut) .name`, potentially in an `unsafe` block // self_data is not necessarily self, to also lint sub-getters, etc… let block_expr = if let ExprKind::Block(block, _) = body.value.kind && block.stmts.is_empty() && let Some(block_expr) = block.expr { - block_expr + if let ExprKind::Block(unsafe_block, _) = block_expr.kind + && unsafe_block.stmts.is_empty() + && matches!( + unsafe_block.rules, + BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + ) + && let Some(unsafe_block_expr) = unsafe_block.expr + { + unsafe_block_expr + } else { + block_expr + } } else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index 5f3fc5100e75b..d0d02a382d15e 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -9,8 +9,8 @@ mod too_many_arguments; mod too_many_lines; use clippy_config::Conf; -use clippy_utils::def_path_def_ids; use clippy_utils::msrvs::Msrv; +use clippy_utils::paths::{PathNS, lookup_path_str}; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; @@ -469,7 +469,7 @@ impl Functions { trait_ids: conf .allow_renamed_params_for .iter() - .flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::>())) + .flat_map(|p| lookup_path_str(tcx, PathNS::Type, p)) .collect(), msrv: conf.msrv, } diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index f1c9657f22404..70655838b6af0 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -21,7 +21,7 @@ use core::ops::ControlFlow; use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attrs = cx.tcx.hir_attrs(item.hir_id()); let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); if let hir::ItemKind::Fn { ref sig, @@ -51,11 +51,11 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attrs = cx.tcx.hir_attrs(item.hir_id()); let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig); - } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { + } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id).is_none() { check_must_use_candidate( cx, sig.decl, @@ -74,7 +74,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attrs = cx.tcx.hir_attrs(item.hir_id()); let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig); diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs index 5ad83f886e2ec..2d22bb157a93c 100644 --- a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs @@ -5,7 +5,7 @@ use rustc_hir::hir_id::OwnerId; use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef}; use rustc_lint::LateContext; use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, kw}; +use rustc_span::symbol::{Ident, kw}; use super::RENAMED_FUNCTION_PARAMS; @@ -22,8 +22,8 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored && let Some(did) = trait_item_def_id_of_impl(items, item.owner_id) && !is_from_ignored_trait(trait_ref, ignored_traits) { - let mut param_idents_iter = cx.tcx.hir_body_param_names(body_id); - let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied(); + let mut param_idents_iter = cx.tcx.hir_body_param_idents(body_id); + let mut default_param_idents_iter = cx.tcx.fn_arg_idents(did).iter().copied(); let renames = RenamedFnArgs::new(&mut default_param_idents_iter, &mut param_idents_iter); if !renames.0.is_empty() { @@ -51,22 +51,31 @@ struct RenamedFnArgs(Vec<(Span, String)>); impl RenamedFnArgs { /// Comparing between an iterator of default names and one with current names, /// then collect the ones that got renamed. - fn new(default_names: &mut I, current_names: &mut T) -> Self + fn new(default_idents: &mut I1, current_idents: &mut I2) -> Self where - I: Iterator, - T: Iterator, + I1: Iterator>, + I2: Iterator>, { let mut renamed: Vec<(Span, String)> = vec![]; - debug_assert!(default_names.size_hint() == current_names.size_hint()); - while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) { - let current_name = cur_name.name; - let default_name = def_name.name; - if is_unused_or_empty_symbol(current_name) || is_unused_or_empty_symbol(default_name) { - continue; - } - if current_name != default_name { - renamed.push((cur_name.span, default_name.to_string())); + debug_assert!(default_idents.size_hint() == current_idents.size_hint()); + while let (Some(default_ident), Some(current_ident)) = (default_idents.next(), current_idents.next()) { + let has_name_to_check = |ident: Option| { + if let Some(ident) = ident + && ident.name != kw::Underscore + && !ident.name.as_str().starts_with('_') + { + Some(ident) + } else { + None + } + }; + + if let Some(default_ident) = has_name_to_check(default_ident) + && let Some(current_ident) = has_name_to_check(current_ident) + && default_ident.name != current_ident.name + { + renamed.push((current_ident.span, default_ident.to_string())); } } @@ -83,14 +92,6 @@ impl RenamedFnArgs { } } -fn is_unused_or_empty_symbol(symbol: Symbol) -> bool { - // FIXME: `body_param_names` currently returning empty symbols for `wild` as well, - // so we need to check if the symbol is empty first. - // Therefore the check of whether it's equal to [`kw::Underscore`] has no use for now, - // but it would be nice to keep it here just to be future-proof. - symbol.is_empty() || symbol == kw::Underscore || symbol.as_str().starts_with('_') -} - /// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item. fn trait_item_def_id_of_impl(items: &[ImplItemRef], target: OwnerId) -> Option { items.iter().find_map(|item| { diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs index cade56f582261..00ce4cfcc5266 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result.rs @@ -55,7 +55,7 @@ pub(super) fn check_impl_item<'tcx>( // Don't lint if method is a trait's implementation, we can't do anything about those if let hir::ImplItemKind::Fn(ref sig, _) = item.kind && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span) - && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + && trait_ref_of_method(cx, item.owner_id).is_none() { if cx.effective_visibilities.is_exported(item.owner_id.def_id) { let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -103,7 +103,7 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty .did() .as_local() && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_def_id) - && let hir::ItemKind::Enum(ref def, _) = item.kind + && let hir::ItemKind::Enum(_, ref def, _) = item.kind { let variants_size = AdtVariantInfo::new(cx, *adt, subst); if let Some((first_variant, variants)) = variants_size.split_first() diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs index 05dc47f6fe580..48d050aa36aa0 100644 --- a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs +++ b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs @@ -47,16 +47,16 @@ pub(super) fn check_fn( } pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>, too_many_arguments_threshold: u64) { - if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { + if let hir::TraitItemKind::Fn(ref sig, _) = item.kind // don't lint extern functions decls, it's not their fault - if sig.header.abi == ExternAbi::Rust { - check_arg_number( - cx, - sig.decl, - item.span.with_hi(sig.decl.output.span().hi()), - too_many_arguments_threshold, - ); - } + && sig.header.abi == ExternAbi::Rust + { + check_arg_number( + cx, + sig.decl, + item.span.with_hi(sig.decl.output.span().hi()), + too_many_arguments_threshold, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index fbbd33efd02d6..9e94280fc0746 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { |diag| { let mut app = Applicability::MachineApplicable; let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) - .maybe_par() + .maybe_paren() .to_string(); let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; let method_body = if let Some(first_stmt) = then_block.stmts.first() { diff --git a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs index 54b8adbc8ac79..e4ace3bdabf0f 100644 --- a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs @@ -17,15 +17,15 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// match std::fs::create_dir("tmp-work-dir") { - /// Ok(_) => println!("Working directory created"), - /// Err(s) => eprintln!("Could not create directory: {s}"), + /// Ok(_) => println!("Working directory created"), + /// Err(s) => eprintln!("Could not create directory: {s}"), /// } /// ``` /// Use instead: /// ```no_run /// match std::fs::create_dir("tmp-work-dir") { - /// Ok(()) => println!("Working directory created"), - /// Err(s) => eprintln!("Could not create directory: {s}"), + /// Ok(()) => println!("Working directory created"), + /// Err(s) => eprintln!("Could not create directory: {s}"), /// } /// ``` #[clippy::version = "1.73.0"] diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index d2545e57652a8..cab7a9fb70990 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -10,10 +10,10 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; use rustc_span::Span; -use rustc_span::symbol::sym; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; declare_clippy_lint! { @@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { } let generics_suggestion_span = impl_.generics.span.substitute_dummy({ - let range = (item.span.lo()..target.span().lo()).map_range(cx, |src, range| { + let range = (item.span.lo()..target.span().lo()).map_range(cx, |_, src, range| { Some(src.get(range.clone())?.find("impl")? + 4..range.end) }); if let Some(range) = range { @@ -165,11 +165,12 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { continue; } let generics_suggestion_span = generics.span.substitute_dummy({ - let range = (item.span.lo()..body.params[0].pat.span.lo()).map_range(cx, |src, range| { - let (pre, post) = src.get(range.clone())?.split_once("fn")?; - let pos = post.find('(')? + pre.len() + 2; - Some(pos..pos) - }); + let range = + (item.span.lo()..body.params[0].pat.span.lo()).map_range(cx, |_, src, range| { + let (pre, post) = src.get(range.clone())?.split_once("fn")?; + let pos = post.find('(')? + pre.len() + 2; + Some(pos..pos) + }); if let Some(range) = range { range.with_ctxt(item.span.ctxt()) } else { @@ -326,6 +327,7 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { if let ExprKind::Call(fun, args) = e.kind && let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind + && matches!(method.ident.name, sym::new | sym::with_capacity) && let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind && let Some(ty_did) = ty_path.res.opt_def_id() { @@ -333,10 +335,11 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { return; } - if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) { - if method.ident.name == sym::new { + match (self.cx.tcx.get_diagnostic_name(ty_did), method.ident.name) { + (Some(sym::HashMap), sym::new) => { self.suggestions.insert(e.span, "HashMap::default()".to_string()); - } else if method.ident.name.as_str() == "with_capacity" { + }, + (Some(sym::HashMap), sym::with_capacity) => { self.suggestions.insert( e.span, format!( @@ -344,11 +347,11 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { snippet(self.cx, args[0].span, "capacity"), ), ); - } - } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) { - if method.ident.name == sym::new { + }, + (Some(sym::HashSet), sym::new) => { self.suggestions.insert(e.span, "HashSet::default()".to_string()); - } else if method.ident.name.as_str() == "with_capacity" { + }, + (Some(sym::HashSet), sym::with_capacity) => { self.suggestions.insert( e.span, format!( @@ -356,7 +359,8 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { snippet(self.cx, args[0].span, "capacity"), ), ); - } + }, + _ => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs index 5f95464e4d494..076017a247b4b 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_return.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro}; +use clippy_utils::{desugar_await, get_async_closure_expr, get_async_fn_body, is_async_fn, is_from_proc_macro}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -134,6 +134,10 @@ fn lint_implicit_returns( }, ExprKind::Match(_, arms, _) => { + if let Some(await_expr) = desugar_await(expr) { + lint_return(cx, await_expr.hir_id, await_expr.span); + return LintLocation::Inner; + } for arm in arms { let res = lint_implicit_returns( cx, @@ -153,18 +157,18 @@ fn lint_implicit_returns( ExprKind::Loop(block, ..) => { let mut add_return = false; let _: Option = for_each_expr_without_closures(block, |e| { - if let ExprKind::Break(dest, sub_expr) = e.kind { - if dest.target_id.ok() == Some(expr.hir_id) { - if call_site_span.is_none() && e.span.ctxt() == ctxt { - // At this point sub_expr can be `None` in async functions which either diverge, or return - // the unit type. - if let Some(sub_expr) = sub_expr { - lint_break(cx, e.hir_id, e.span, sub_expr.span); - } - } else { - // the break expression is from a macro call, add a return to the loop - add_return = true; + if let ExprKind::Break(dest, sub_expr) = e.kind + && dest.target_id.ok() == Some(expr.hir_id) + { + if call_site_span.is_none() && e.span.ctxt() == ctxt { + // At this point sub_expr can be `None` in async functions which either diverge, or return + // the unit type. + if let Some(sub_expr) = sub_expr { + lint_break(cx, e.hir_id, e.span, sub_expr.span); } + } else { + // the break expression is from a macro call, add a return to the loop + add_return = true; } } ControlFlow::Continue(()) @@ -241,6 +245,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { Some(e) => e, None => return, } + } else if let Some(expr) = get_async_closure_expr(cx.tcx, body.value) { + expr } else { body.value }; diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs index 41d2b18803d95..185fc2aa2d4ac 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs @@ -5,7 +5,7 @@ use clippy_utils::source::snippet_with_context; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_hir::{AssignOpKind, BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { && ex.span.ctxt() == ctxt && expr1.span.ctxt() == ctxt && clippy_utils::SpanlessEq::new(cx).eq_expr(l, target) - && BinOpKind::Add == op1.node + && AssignOpKind::AddAssign == op1.node && let ExprKind::Lit(lit) = value.kind && let LitKind::Int(Pu128(1), LitIntType::Unsuffixed) = lit.node && block.expr.is_none() 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 f2d16ff2e5649..0823ef53ef98a 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -1,14 +1,14 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_opt; +use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::{ - SpanlessEq, higher, is_in_const_context, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt, + SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, peel_blocks, peel_blocks_with_stmt, }; use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, HirId, QPath}; +use rustc_hir::{AssignOpKind, BinOp, BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -69,7 +69,7 @@ declare_clippy_lint! { /// /// let result = a.saturating_sub(b); /// ``` - #[clippy::version = "1.44.0"] + #[clippy::version = "1.83.0"] pub INVERTED_SATURATING_SUB, correctness, "Check if a variable is smaller than another one and still subtract from it even if smaller" @@ -170,22 +170,20 @@ fn check_gt( cx: &LateContext<'_>, condition_span: Span, expr_span: Span, - big_var: &Expr<'_>, - little_var: &Expr<'_>, + big_expr: &Expr<'_>, + little_expr: &Expr<'_>, if_block: &Expr<'_>, else_block: &Expr<'_>, msrv: Msrv, is_composited: bool, ) { - if let Some(big_var) = Var::new(big_var) - && let Some(little_var) = Var::new(little_var) - { + if is_side_effect_free(cx, big_expr) && is_side_effect_free(cx, little_expr) { check_subtraction( cx, condition_span, expr_span, - big_var, - little_var, + big_expr, + little_expr, if_block, else_block, msrv, @@ -194,18 +192,8 @@ fn check_gt( } } -struct Var { - span: Span, - hir_id: HirId, -} - -impl Var { - fn new(expr: &Expr<'_>) -> Option { - path_to_local(expr).map(|hir_id| Self { - span: expr.span, - hir_id, - }) - } +fn is_side_effect_free(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + eq_expr_value(cx, expr, expr) } #[allow(clippy::too_many_arguments)] @@ -213,8 +201,8 @@ fn check_subtraction( cx: &LateContext<'_>, condition_span: Span, expr_span: Span, - big_var: Var, - little_var: Var, + big_expr: &Expr<'_>, + little_expr: &Expr<'_>, if_block: &Expr<'_>, else_block: &Expr<'_>, msrv: Msrv, @@ -234,8 +222,8 @@ fn check_subtraction( cx, condition_span, expr_span, - little_var, - big_var, + little_expr, + big_expr, else_block, if_block, msrv, @@ -247,17 +235,15 @@ fn check_subtraction( && let ExprKind::Binary(op, left, right) = if_block.kind && let BinOpKind::Sub = op.node { - let local_left = path_to_local(left); - let local_right = path_to_local(right); - if Some(big_var.hir_id) == local_left && Some(little_var.hir_id) == local_right { + if eq_expr_value(cx, left, big_expr) && eq_expr_value(cx, right, little_expr) { // This part of the condition is voluntarily split from the one before to ensure that // if `snippet_opt` fails, it won't try the next conditions. - if let Some(big_var_snippet) = snippet_opt(cx, big_var.span) - && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) - && (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST)) + if (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST)) + && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_paren) + && let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr) { let sugg = format!( - "{}{big_var_snippet}.saturating_sub({little_var_snippet}){}", + "{}{big_expr_sugg}.saturating_sub({little_expr_sugg}){}", if is_composited { "{ " } else { "" }, if is_composited { " }" } else { "" } ); @@ -271,11 +257,12 @@ fn check_subtraction( Applicability::MachineApplicable, ); } - } else if Some(little_var.hir_id) == local_left - && Some(big_var.hir_id) == local_right - && let Some(big_var_snippet) = snippet_opt(cx, big_var.span) - && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) + } else if eq_expr_value(cx, left, little_expr) + && eq_expr_value(cx, right, big_expr) + && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr) + && let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr) { + let sugg = make_binop(BinOpKind::Sub, &big_expr_sugg, &little_expr_sugg); span_lint_and_then( cx, INVERTED_SATURATING_SUB, @@ -284,12 +271,12 @@ fn check_subtraction( |diag| { diag.span_note( if_block.span, - format!("this subtraction underflows when `{little_var_snippet} < {big_var_snippet}`"), + format!("this subtraction underflows when `{little_expr_sugg} < {big_expr_sugg}`"), ); diag.span_suggestion( if_block.span, "try replacing it with", - format!("{big_var_snippet} - {little_var_snippet}"), + format!("{sugg}"), Applicability::MaybeIncorrect, ); }, @@ -379,7 +366,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp match peel_blocks_with_stmt(expr).kind { ExprKind::AssignOp(ref op1, target, value) => { // Check if literal being subtracted is one - (BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target) + (AssignOpKind::SubAssign == op1.node && is_integer_literal(value, 1)).then_some(target) }, ExprKind::Assign(target, value, _) => { if let ExprKind::Binary(ref op1, left1, right1) = value.kind diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index d02d9b2102bda..6b89abdb0367f 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -8,7 +8,7 @@ use rustc_hir::{ }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt}; +use rustc_middle::ty::{self, AssocItem, ClauseKind, Generics, Ty, TyCtxt}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -315,7 +315,7 @@ fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) { assocs .filter_by_name_unhygienic(constraint.ident.name) .next() - .is_some_and(|assoc| assoc.kind == ty::AssocKind::Type) + .is_some_and(AssocItem::is_type) }) { emit_lint(cx, poly_trait, bounds, index, implied_constraints, bound); diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index 12dfb14c454d2..5d0bd3e8ca303 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -2,14 +2,14 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_in_test; use clippy_utils::msrvs::Msrv; -use rustc_attr_parsing::{RustcVersion, StabilityLevel, StableSince}; +use rustc_attr_data_structures::{RustcVersion, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_hir::{Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::def_id::DefId; -use rustc_span::{ExpnKind, Span}; +use rustc_span::{ExpnKind, Span, sym}; declare_clippy_lint! { /// ### What it does @@ -93,6 +93,21 @@ impl IncompatibleMsrv { // Intentionally not using `.from_expansion()`, since we do still care about macro expansions return; } + + // Functions coming from `core` while expanding a macro such as `assert*!()` get to cheat too: the + // macros may have existed prior to the checked MSRV, but their expansion with a recent compiler + // might use recent functions or methods. Compiling with an older compiler would not use those. + if span.from_expansion() + && cx.tcx.crate_name(def_id.krate) == sym::core + && span + .ctxt() + .outer_expn_data() + .macro_def_id + .is_some_and(|def_id| cx.tcx.crate_name(def_id.krate) == sym::core) + { + return; + } + if (self.check_in_tests || !is_in_test(cx.tcx, node)) && let Some(current) = self.msrv.current(cx) && let version = self.get_def_id_version(cx.tcx, def_id) @@ -118,8 +133,11 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { self.emit_lint_if_under_msrv(cx, method_did, expr.hir_id, span); } }, - ExprKind::Call(call, [_]) => { - if let ExprKind::Path(qpath) = call.kind + ExprKind::Call(call, _) => { + // Desugaring into function calls by the compiler will use `QPath::LangItem` variants. Those should + // not be linted as they will not be generated in older compilers if the function is not available, + // and the compiler is allowed to call unstable functions. + if let ExprKind::Path(qpath @ (QPath::Resolved(..) | QPath::TypeRelative(..))) = call.kind && let Some(path_def_id) = cx.qpath_res(&qpath, call.hir_id).opt_def_id() { self.emit_lint_if_under_msrv(cx, path_def_id, expr.hir_id, call.span); diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index 5b58113169b1e..e6129757e560f 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -65,13 +65,13 @@ declare_clippy_lint! { } pub struct InconsistentStructConstructor { - lint_inconsistent_struct_field_initializers: bool, + check_inconsistent_struct_field_initializers: bool, } impl InconsistentStructConstructor { pub fn new(conf: &'static Conf) -> Self { Self { - lint_inconsistent_struct_field_initializers: conf.lint_inconsistent_struct_field_initializers, + check_inconsistent_struct_field_initializers: conf.check_inconsistent_struct_field_initializers, } } } @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { let all_fields_are_shorthand = fields.iter().all(|f| f.is_shorthand); let applicability = if all_fields_are_shorthand { Applicability::MachineApplicable - } else if self.lint_inconsistent_struct_field_initializers { + } else if self.check_inconsistent_struct_field_initializers { Applicability::MaybeIncorrect } else { return; @@ -182,7 +182,7 @@ fn suggestion<'tcx>( } fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span { - if let Some(attr) = tcx.hir().attrs(field.hir_id).first() { + if let Some(attr) = tcx.hir_attrs(field.hir_id).first() { field.span.with_lo(attr.span().lo()) } else { field.span diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index d53e139de014b..0b1cae30ca501 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::is_copy; -use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; +use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local, sym}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -72,7 +72,7 @@ impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) - && (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some()) + && (!expr.span.from_expansion() || is_expn_of(expr.span, sym::if_chain).is_some()) && !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id) && let found_slices = find_slice_values(cx, let_pat) && !found_slices.is_empty() @@ -248,7 +248,7 @@ impl<'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'_, 'tcx> { { use_info .index_use - .push((index_value, cx.tcx.hir().span(parent_expr.hir_id))); + .push((index_value, cx.tcx.hir_span(parent_expr.hir_id))); return; } diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 33431385c7de3..99a393b4d53af 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -2,13 +2,12 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; -use clippy_utils::{higher, is_from_proc_macro, is_in_test}; +use clippy_utils::{higher, is_from_proc_macro, is_in_test, sym}; use rustc_ast::ast::RangeLimits; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -136,28 +135,28 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { let const_range = to_const_range(cx, range, size); - if let (Some(start), _) = const_range { - if start > size { - span_lint( - cx, - OUT_OF_BOUNDS_INDEXING, - range.start.map_or(expr.span, |start| start.span), - "range is out of bounds", - ); - return; - } + if let (Some(start), _) = const_range + && start > size + { + span_lint( + cx, + OUT_OF_BOUNDS_INDEXING, + range.start.map_or(expr.span, |start| start.span), + "range is out of bounds", + ); + return; } - if let (_, Some(end)) = const_range { - if end > size { - span_lint( - cx, - OUT_OF_BOUNDS_INDEXING, - range.end.map_or(expr.span, |end| end.span), - "range is out of bounds", - ); - return; - } + if let (_, Some(end)) = const_range + && end > size + { + span_lint( + cx, + OUT_OF_BOUNDS_INDEXING, + range.end.map_or(expr.span, |end| end.span), + "range is out of bounds", + ); + return; } if let (Some(_), Some(_)) = const_range { @@ -268,7 +267,7 @@ fn ty_has_applicable_get_function<'tcx>( index_expr: &Expr<'_>, ) -> bool { if let ty::Adt(_, _) = array_ty.kind() - && let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym!(get)).map(|m| { + && let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym::get).map(|m| { cx.tcx .fn_sig(m.def_id) .skip_binder() diff --git a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs index 3a28553b55e25..7a751514b6476 100644 --- a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs +++ b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs @@ -1,13 +1,13 @@ use crate::methods::method_call; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::peel_blocks; +use clippy_utils::{peel_blocks, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -57,7 +57,7 @@ fn index_if_arg_is_boolean(args: &[Expr<'_>], call_span: Span) -> Option { impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(("open", mut receiver, [_arg], _, _)) = method_call(expr) else { + let Some((sym::open, mut receiver, [_arg], _, _)) = method_call(expr) else { return; }; let receiver_ty = cx.typeck_results().expr_ty(receiver); @@ -70,9 +70,9 @@ impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions { let mut write = None; while let Some((name, recv, args, _, span)) = method_call(receiver) { - if name == "append" { + if name == sym::append { append = index_if_arg_is_boolean(args, span); - } else if name == "write" { + } else if name == sym::write { write = index_if_arg_is_boolean(args, span); } receiver = recv; diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index 960b9aa032bea..bf3eafe09b3d0 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::higher; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; +use clippy_utils::{higher, sym}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; +use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -120,33 +120,33 @@ use self::Heuristic::{All, Always, Any, First}; /// returns an infinite or possibly infinite iterator. The finiteness /// is an upper bound, e.g., some methods can return a possibly /// infinite iterator at worst, e.g., `take_while`. -const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [ - ("zip", 1, All, Infinite), - ("chain", 1, Any, Infinite), - ("cycle", 0, Always, Infinite), - ("map", 1, First, Infinite), - ("by_ref", 0, First, Infinite), - ("cloned", 0, First, Infinite), - ("rev", 0, First, Infinite), - ("inspect", 0, First, Infinite), - ("enumerate", 0, First, Infinite), - ("peekable", 1, First, Infinite), - ("fuse", 0, First, Infinite), - ("skip", 1, First, Infinite), - ("skip_while", 0, First, Infinite), - ("filter", 1, First, Infinite), - ("filter_map", 1, First, Infinite), - ("flat_map", 1, First, Infinite), - ("unzip", 0, First, Infinite), - ("take_while", 1, First, MaybeInfinite), - ("scan", 2, First, MaybeInfinite), +const HEURISTICS: [(Symbol, usize, Heuristic, Finiteness); 19] = [ + (sym::zip, 1, All, Infinite), + (sym::chain, 1, Any, Infinite), + (sym::cycle, 0, Always, Infinite), + (sym::map, 1, First, Infinite), + (sym::by_ref, 0, First, Infinite), + (sym::cloned, 0, First, Infinite), + (sym::rev, 0, First, Infinite), + (sym::inspect, 0, First, Infinite), + (sym::enumerate, 0, First, Infinite), + (sym::peekable, 1, First, Infinite), + (sym::fuse, 0, First, Infinite), + (sym::skip, 1, First, Infinite), + (sym::skip_while, 0, First, Infinite), + (sym::filter, 1, First, Infinite), + (sym::filter_map, 1, First, Infinite), + (sym::flat_map, 1, First, Infinite), + (sym::unzip, 0, First, Infinite), + (sym::take_while, 1, First, MaybeInfinite), + (sym::scan, 2, First, MaybeInfinite), ]; fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { ExprKind::MethodCall(method, receiver, args, _) => { for &(name, len, heuristic, cap) in &HEURISTICS { - if method.ident.name.as_str() == name && args.len() == len { + if method.ident.name == name && args.len() == len { return (match heuristic { Always => Infinite, First => is_infinite(cx, receiver), @@ -156,11 +156,12 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { .and(cap); } } - if method.ident.name.as_str() == "flat_map" && args.len() == 1 { - if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind { - let body = cx.tcx.hir_body(body); - return is_infinite(cx, body.value); - } + if method.ident.name == sym::flat_map + && args.len() == 1 + && let ExprKind::Closure(&Closure { body, .. }) = args[0].kind + { + let body = cx.tcx.hir_body(body); + return is_infinite(cx, body.value); } Finite }, @@ -183,36 +184,36 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { /// the names and argument lengths of methods that *may* exhaust their /// iterators -const POSSIBLY_COMPLETING_METHODS: [(&str, usize); 6] = [ - ("find", 1), - ("rfind", 1), - ("position", 1), - ("rposition", 1), - ("any", 1), - ("all", 1), +const POSSIBLY_COMPLETING_METHODS: [(Symbol, usize); 6] = [ + (sym::find, 1), + (sym::rfind, 1), + (sym::position, 1), + (sym::rposition, 1), + (sym::any, 1), + (sym::all, 1), ]; /// the names and argument lengths of methods that *always* exhaust /// their iterators -const COMPLETING_METHODS: [(&str, usize); 12] = [ - ("count", 0), - ("fold", 2), - ("for_each", 1), - ("partition", 1), - ("max", 0), - ("max_by", 1), - ("max_by_key", 1), - ("min", 0), - ("min_by", 1), - ("min_by_key", 1), - ("sum", 0), - ("product", 0), +const COMPLETING_METHODS: [(Symbol, usize); 12] = [ + (sym::count, 0), + (sym::fold, 2), + (sym::for_each, 1), + (sym::partition, 1), + (sym::max, 0), + (sym::max_by, 1), + (sym::max_by_key, 1), + (sym::min, 0), + (sym::min_by, 1), + (sym::min_by_key, 1), + (sym::sum, 0), + (sym::product, 0), ]; fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { ExprKind::MethodCall(method, receiver, args, _) => { - let method_str = method.ident.name.as_str(); + let method_str = method.ident.name; for &(name, len) in &COMPLETING_METHODS { if method_str == name && args.len() == len { return is_infinite(cx, receiver); @@ -223,7 +224,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { return MaybeInfinite.and(is_infinite(cx, receiver)); } } - if method.ident.name.as_str() == "last" && args.is_empty() { + if method.ident.name == sym::last && args.is_empty() { let not_double_ended = cx .tcx .get_diagnostic_item(sym::DoubleEndedIterator) @@ -231,7 +232,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { if not_double_ended { return is_infinite(cx, receiver); } - } else if method.ident.name.as_str() == "collect" { + } else if method.ident.name == sym::collect { let ty = cx.typeck_results().expr_ty(expr); if matches!( get_type_diagnostic_name(cx, ty), diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs index 1d582fb0223e1..7f2e25367a6a4 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs @@ -106,7 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { // Check if return type is String && is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String) // Filters instances of to_string which are required by a trait - && trait_ref_of_method(cx, impl_item.owner_id.def_id).is_none() + && trait_ref_of_method(cx, impl_item.owner_id).is_none() { show_lint(cx, impl_item); } 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 9b4a3b3f9c84c..da5ca5e677218 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 @@ -32,12 +32,7 @@ declare_lint_pass!(InlineFnWithoutBody => [INLINE_FN_WITHOUT_BODY]); impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind - && let Some(attr) = cx - .tcx - .hir() - .attrs(item.hir_id()) - .iter() - .find(|a| a.has_name(sym::inline)) + && let Some(attr) = cx.tcx.hir_attrs(item.hir_id()).iter().find(|a| a.has_name(sym::inline)) { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs index 4ae1119ab3a27..91f65d0b79ca0 100644 --- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs @@ -123,7 +123,7 @@ fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg expr.span, "manual implementation of `Instant::elapsed`", "try", - format!("{}.elapsed()", sugg.maybe_par()), + format!("{}.elapsed()", sugg.maybe_paren()), Applicability::MachineApplicable, ); } 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 fc575bff7e63f..67ce57de254d3 100644 --- a/src/tools/clippy/clippy_lints/src/int_plus_one.rs +++ b/src/tools/clippy/clippy_lints/src/int_plus_one.rs @@ -130,14 +130,14 @@ impl IntPlusOne { BinOpKind::Le => "<", _ => return None, }; - if let Some(snippet) = node.span.get_source_text(cx) { - if let Some(other_side_snippet) = other_side.span.get_source_text(cx) { - let rec = match side { - Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")), - Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")), - }; - return rec; - } + if let Some(snippet) = node.span.get_source_text(cx) + && let Some(other_side_snippet) = other_side.span.get_source_text(cx) + { + let rec = match side { + Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")), + Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")), + }; + return rec; } None } @@ -157,10 +157,10 @@ 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(rec) = Self::check_binop(cx, kind.node, lhs, rhs) { - Self::emit_warning(cx, item, rec); - } + if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind + && 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/invalid_upcast_comparisons.rs b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs index b42664340d1c8..b0ecc5d52ddb8 100644 --- a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs @@ -91,49 +91,49 @@ fn upcast_comparison_bounds_err<'tcx>( rhs: &'tcx Expr<'_>, invert: bool, ) { - if let Some((lb, ub)) = lhs_bounds { - if let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) { - if rel == Rel::Eq || rel == Rel::Ne { - if norm_rhs_val < lb || norm_rhs_val > ub { - err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); - } - } else if match rel { - Rel::Lt => { - if invert { - norm_rhs_val < lb - } else { - ub < norm_rhs_val - } - }, - Rel::Le => { - if invert { - norm_rhs_val <= lb - } else { - ub <= norm_rhs_val - } - }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, true); - } else if match rel { - Rel::Lt => { - if invert { - norm_rhs_val >= ub - } else { - lb >= norm_rhs_val - } - }, - Rel::Le => { - if invert { - norm_rhs_val > ub - } else { - lb > norm_rhs_val - } - }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, false); + if let Some((lb, ub)) = lhs_bounds + && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) + { + if rel == Rel::Eq || rel == Rel::Ne { + if norm_rhs_val < lb || norm_rhs_val > ub { + err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); } + } else if match rel { + Rel::Lt => { + if invert { + norm_rhs_val < lb + } else { + ub < norm_rhs_val + } + }, + Rel::Le => { + if invert { + norm_rhs_val <= lb + } else { + ub <= norm_rhs_val + } + }, + Rel::Eq | Rel::Ne => unreachable!(), + } { + err_upcast_comparison(cx, span, lhs, true); + } else if match rel { + Rel::Lt => { + if invert { + norm_rhs_val >= ub + } else { + lb >= norm_rhs_val + } + }, + Rel::Le => { + if invert { + norm_rhs_val > ub + } else { + lb > norm_rhs_val + } + }, + Rel::Eq | Rel::Ne => unreachable!(), + } { + err_upcast_comparison(cx, span, lhs, false); } } } diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index 6363f717a5c2f..3d4dcd0207023 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -5,10 +5,9 @@ use clippy_utils::macros::span_is_local; use clippy_utils::source::is_present_in_source; use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData}; +use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, QPath, TyKind, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::Span; use rustc_span::symbol::Symbol; declare_clippy_lint! { @@ -163,6 +162,7 @@ pub struct ItemNameRepetitions { enum_threshold: u64, struct_threshold: u64, avoid_breaking_exported_api: bool, + allow_exact_repetitions: bool, allow_private_module_inception: bool, allowed_prefixes: FxHashSet, } @@ -174,6 +174,7 @@ impl ItemNameRepetitions { enum_threshold: conf.enum_variant_name_threshold, struct_threshold: conf.struct_field_name_threshold, avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + allow_exact_repetitions: conf.allow_exact_repetitions, allow_private_module_inception: conf.allow_private_module_inception, allowed_prefixes: conf.allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(), } @@ -196,80 +197,176 @@ fn have_no_extra_prefix(prefixes: &[&str]) -> bool { prefixes.iter().all(|p| p == &"" || p == &"_") } -fn check_fields(cx: &LateContext<'_>, threshold: u64, item: &Item<'_>, fields: &[FieldDef<'_>]) { - if (fields.len() as u64) < threshold { - return; - } +impl ItemNameRepetitions { + /// Lint the names of enum variants against the name of the enum. + fn check_variants(&self, cx: &LateContext<'_>, item: &Item<'_>, def: &EnumDef<'_>) { + if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(item.owner_id.def_id) { + return; + } - check_struct_name_repetition(cx, item, fields); + if (def.variants.len() as u64) < self.enum_threshold { + return; + } - // if the SyntaxContext of the identifiers of the fields and struct differ dont lint them. - // this prevents linting in macros in which the location of the field identifier names differ - if !fields.iter().all(|field| item.ident.span.eq_ctxt(field.ident.span)) { - return; + let Some(ident) = item.kind.ident() else { + return; + }; + let item_name = ident.name.as_str(); + for var in def.variants { + check_enum_start(cx, item_name, var); + check_enum_end(cx, item_name, var); + } + + Self::check_enum_common_affix(cx, item, def); } - let mut pre: Vec<&str> = match fields.first() { - Some(first_field) => first_field.ident.name.as_str().split('_').collect(), - None => return, - }; - let mut post = pre.clone(); - post.reverse(); - for field in fields { - let field_split: Vec<&str> = field.ident.name.as_str().split('_').collect(); - if field_split.len() == 1 { + /// Lint the names of struct fields against the name of the struct. + fn check_fields(&self, cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) { + if (fields.len() as u64) < self.struct_threshold { return; } - pre = pre - .into_iter() - .zip(field_split.iter()) - .take_while(|(a, b)| &a == b) - .map(|e| e.0) - .collect(); - post = post - .into_iter() - .zip(field_split.iter().rev()) - .take_while(|(a, b)| &a == b) - .map(|e| e.0) - .collect(); + self.check_struct_name_repetition(cx, item, fields); + self.check_struct_common_affix(cx, item, fields); } - let prefix = pre.join("_"); - post.reverse(); - let postfix = match post.last() { - Some(&"") => post.join("_") + "_", - Some(_) | None => post.join("_"), - }; - if fields.len() > 1 { - let (what, value) = match ( - prefix.is_empty() || prefix.chars().all(|c| c == '_'), - postfix.is_empty(), - ) { - (true, true) => return, - (false, _) => ("pre", prefix), - (true, false) => ("post", postfix), + + fn check_enum_common_affix(cx: &LateContext<'_>, item: &Item<'_>, def: &EnumDef<'_>) { + let first = match def.variants.first() { + Some(variant) => variant.ident.name.as_str(), + None => return, }; - if fields.iter().all(|field| is_bool(field.ty)) { - // If all fields are booleans, we don't want to emit this lint. - return; + let mut pre = camel_case_split(first); + let mut post = pre.clone(); + post.reverse(); + for var in def.variants { + let name = var.ident.name.as_str(); + + let variant_split = camel_case_split(name); + if variant_split.len() == 1 { + return; + } + + pre = pre + .iter() + .zip(variant_split.iter()) + .take_while(|(a, b)| a == b) + .map(|e| *e.0) + .collect(); + post = post + .iter() + .zip(variant_split.iter().rev()) + .take_while(|(a, b)| a == b) + .map(|e| *e.0) + .collect(); } + let (what, value) = match (have_no_extra_prefix(&pre), post.is_empty()) { + (true, true) => return, + (false, _) => ("pre", pre.join("")), + (true, false) => { + post.reverse(); + ("post", post.join("")) + }, + }; span_lint_and_help( cx, - STRUCT_FIELD_NAMES, + ENUM_VARIANT_NAMES, item.span, - format!("all fields have the same {what}fix: `{value}`"), + format!("all variants have the same {what}fix: `{value}`"), None, - format!("remove the {what}fixes"), + format!( + "remove the {what}fixes and use full paths to \ + the variants instead of glob imports" + ), ); } -} -fn check_struct_name_repetition(cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) { - let snake_name = to_snake_case(item.ident.name.as_str()); - let item_name_words: Vec<&str> = snake_name.split('_').collect(); - for field in fields { - if field.ident.span.eq_ctxt(item.ident.span) { - //consider linting only if the field identifier has the same SyntaxContext as the item(struct) + fn check_struct_common_affix(&self, cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) { + // if the SyntaxContext of the identifiers of the fields and struct differ dont lint them. + // this prevents linting in macros in which the location of the field identifier names differ + if !fields + .iter() + .all(|field| item.kind.ident().is_some_and(|i| i.span.eq_ctxt(field.ident.span))) + { + return; + } + + if self.avoid_breaking_exported_api + && fields + .iter() + .any(|field| cx.effective_visibilities.is_exported(field.def_id)) + { + return; + } + + let mut pre: Vec<&str> = match fields.first() { + Some(first_field) => first_field.ident.name.as_str().split('_').collect(), + None => return, + }; + let mut post = pre.clone(); + post.reverse(); + for field in fields { + let field_split: Vec<&str> = field.ident.name.as_str().split('_').collect(); + if field_split.len() == 1 { + return; + } + + pre = pre + .into_iter() + .zip(field_split.iter()) + .take_while(|(a, b)| &a == b) + .map(|e| e.0) + .collect(); + post = post + .into_iter() + .zip(field_split.iter().rev()) + .take_while(|(a, b)| &a == b) + .map(|e| e.0) + .collect(); + } + let prefix = pre.join("_"); + post.reverse(); + let postfix = match post.last() { + Some(&"") => post.join("_") + "_", + Some(_) | None => post.join("_"), + }; + if fields.len() > 1 { + let (what, value) = match ( + prefix.is_empty() || prefix.chars().all(|c| c == '_'), + postfix.is_empty(), + ) { + (true, true) => return, + (false, _) => ("pre", prefix), + (true, false) => ("post", postfix), + }; + if fields.iter().all(|field| is_bool(field.ty)) { + // If all fields are booleans, we don't want to emit this lint. + return; + } + span_lint_and_help( + cx, + STRUCT_FIELD_NAMES, + item.span, + format!("all fields have the same {what}fix: `{value}`"), + None, + format!("remove the {what}fixes"), + ); + } + } + + fn check_struct_name_repetition(&self, cx: &LateContext<'_>, item: &Item<'_>, fields: &[FieldDef<'_>]) { + let Some(ident) = item.kind.ident() else { return }; + let snake_name = to_snake_case(ident.name.as_str()); + let item_name_words: Vec<&str> = snake_name.split('_').collect(); + for field in fields { + if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(field.def_id) { + continue; + } + + if !field.ident.span.eq_ctxt(ident.span) { + // consider linting only if the field identifier has the same SyntaxContext as the item(struct) + continue; + } + let field_words: Vec<&str> = field.ident.name.as_str().split('_').collect(); if field_words.len() >= item_name_words.len() { // if the field name is shorter than the struct name it cannot contain it @@ -282,22 +379,21 @@ fn check_struct_name_repetition(cx: &LateContext<'_>, item: &Item<'_>, fields: & "field name starts with the struct's name", ); } - if field_words.len() > item_name_words.len() { + if field_words.len() > item_name_words.len() // lint only if the end is not covered by the start - if field_words + && field_words .iter() .rev() .zip(item_name_words.iter().rev()) .all(|(a, b)| a == b) - { - span_lint_hir( - cx, - STRUCT_FIELD_NAMES, - field.hir_id, - field.span, - "field name ends with the struct's name", - ); - } + { + span_lint_hir( + cx, + STRUCT_FIELD_NAMES, + field.hir_id, + field.span, + "field name ends with the struct's name", + ); } } } @@ -311,6 +407,7 @@ fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_> if count_match_start(item_name, name).char_count == item_name_chars && name.chars().nth(item_name_chars).is_some_and(|c| !c.is_lowercase()) && name.chars().nth(item_name_chars + 1).is_some_and(|c| !c.is_numeric()) + && !check_enum_tuple_path_match(name, variant.data) { span_lint_hir( cx, @@ -326,7 +423,9 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) let name = variant.ident.name.as_str(); let item_name_chars = item_name.chars().count(); - if count_match_end(item_name, name).char_count == item_name_chars { + if count_match_end(item_name, name).char_count == item_name_chars + && !check_enum_tuple_path_match(name, variant.data) + { span_lint_hir( cx, ENUM_VARIANT_NAMES, @@ -337,138 +436,114 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) } } -fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_name: &str, span: Span) { - if (def.variants.len() as u64) < threshold { - return; - } - - for var in def.variants { - check_enum_start(cx, item_name, var); - check_enum_end(cx, item_name, var); - } - - let first = match def.variants.first() { - Some(variant) => variant.ident.name.as_str(), - None => return, +/// Checks if an enum tuple variant contains a single field +/// whose qualified path contains the variant's name. +fn check_enum_tuple_path_match(variant_name: &str, variant_data: VariantData<'_>) -> bool { + // Only check single-field tuple variants + let VariantData::Tuple(fields, ..) = variant_data else { + return false; }; - let mut pre = camel_case_split(first); - let mut post = pre.clone(); - post.reverse(); - for var in def.variants { - let name = var.ident.name.as_str(); - - let variant_split = camel_case_split(name); - if variant_split.len() == 1 { - return; - } - - pre = pre - .iter() - .zip(variant_split.iter()) - .take_while(|(a, b)| a == b) - .map(|e| *e.0) - .collect(); - post = post + if fields.len() != 1 { + return false; + } + // Check if field type is a path and contains the variant name + match fields[0].ty.kind { + TyKind::Path(QPath::Resolved(_, path)) => path + .segments .iter() - .zip(variant_split.iter().rev()) - .take_while(|(a, b)| a == b) - .map(|e| *e.0) - .collect(); + .any(|segment| segment.ident.name.as_str() == variant_name), + TyKind::Path(QPath::TypeRelative(_, segment)) => segment.ident.name.as_str() == variant_name, + _ => false, } - let (what, value) = match (have_no_extra_prefix(&pre), post.is_empty()) { - (true, true) => return, - (false, _) => ("pre", pre.join("")), - (true, false) => { - post.reverse(); - ("post", post.join("")) - }, - }; - span_lint_and_help( - cx, - ENUM_VARIANT_NAMES, - span, - format!("all variants have the same {what}fix: `{value}`"), - None, - format!( - "remove the {what}fixes and use full paths to \ - the variants instead of glob imports" - ), - ); } impl LateLintPass<'_> for ItemNameRepetitions { - fn check_item_post(&mut self, _cx: &LateContext<'_>, _item: &Item<'_>) { + fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { + let Some(_ident) = item.kind.ident() else { return }; + let last = self.modules.pop(); assert!(last.is_some()); } fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - let item_name = item.ident.name.as_str(); + let Some(ident) = item.kind.ident() else { return }; + + let item_name = ident.name.as_str(); let item_camel = to_camel_case(item_name); - if !item.span.from_expansion() && is_present_in_source(cx, item.span) { - if let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules { - // constants don't have surrounding modules - if !mod_camel.is_empty() { - if mod_name == &item.ident.name - && let ItemKind::Mod(..) = item.kind - && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public()) - { - span_lint( - cx, - MODULE_INCEPTION, - item.span, - "module has the same name as its containing module", - ); - } + if !item.span.from_expansion() && is_present_in_source(cx, item.span) + && let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules + // constants don't have surrounding modules + && !mod_camel.is_empty() + { + if mod_name == &ident.name + && let ItemKind::Mod(..) = item.kind + && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public()) + { + span_lint( + cx, + MODULE_INCEPTION, + item.span, + "module has the same name as its containing module", + ); + } + + // The `module_name_repetitions` lint should only trigger if the item has the module in its + // name. Having the same name is only accepted if `allow_exact_repetition` is set to `true`. + + let both_are_public = + cx.tcx.visibility(item.owner_id).is_public() && cx.tcx.visibility(mod_owner_id.def_id).is_public(); + + if both_are_public && !self.allow_exact_repetitions && item_camel == *mod_camel { + span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name is the same as its containing module's name", + ); + } + + if both_are_public && item_camel.len() > mod_camel.len() { + let matching = count_match_start(mod_camel, &item_camel); + let rmatching = count_match_end(mod_camel, &item_camel); + let nchars = mod_camel.chars().count(); + + let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric(); - // The `module_name_repetitions` lint should only trigger if the item has the module in its - // name. Having the same name is accepted. - if cx.tcx.visibility(item.owner_id).is_public() - && cx.tcx.visibility(mod_owner_id.def_id).is_public() - && item_camel.len() > mod_camel.len() - { - let matching = count_match_start(mod_camel, &item_camel); - let rmatching = count_match_end(mod_camel, &item_camel); - let nchars = mod_camel.chars().count(); - - let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric(); - - if matching.char_count == nchars { - match item_camel.chars().nth(nchars) { - Some(c) if is_word_beginning(c) => span_lint( - cx, - MODULE_NAME_REPETITIONS, - item.ident.span, - "item name starts with its containing module's name", - ), - _ => (), - } - } - if rmatching.char_count == nchars - && !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count]) - { - span_lint( - cx, - MODULE_NAME_REPETITIONS, - item.ident.span, - "item name ends with its containing module's name", - ); - } + if matching.char_count == nchars { + match item_camel.chars().nth(nchars) { + Some(c) if is_word_beginning(c) => span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name starts with its containing module's name", + ), + _ => (), } } + if rmatching.char_count == nchars + && !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count]) + { + span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name ends with its containing module's name", + ); + } } } - if !(self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(item.owner_id.def_id)) - && span_is_local(item.span) - { + + if span_is_local(item.span) { match item.kind { - ItemKind::Enum(def, _) => check_variant(cx, self.enum_threshold, &def, item_name, item.span), - ItemKind::Struct(VariantData::Struct { fields, .. }, _) => { - check_fields(cx, self.struct_threshold, item, fields); + ItemKind::Enum(_, def, _) => { + self.check_variants(cx, item, &def); + }, + ItemKind::Struct(_, VariantData::Struct { fields, .. }, _) => { + self.check_fields(cx, item, fields); }, _ => (), } } - self.modules.push((item.ident.name, item_camel, item.owner_id)); + self.modules.push((ident.name, item_camel, item.owner_id)); } } diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs index 9df044f25eb78..dd63de288b87f 100644 --- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs +++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs @@ -44,7 +44,7 @@ declare_clippy_lint! { declare_lint_pass!(ItemsAfterTestModule => [ITEMS_AFTER_TEST_MODULE]); fn cfg_test_module<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool { - if let ItemKind::Mod(test_mod) = item.kind + if let ItemKind::Mod(_, test_mod) = item.kind && item.span.hi() == test_mod.spans.inner_span.hi() && is_cfg_test(cx.tcx, item.hir_id()) && !item.span.from_expansion() @@ -67,14 +67,20 @@ impl LateLintPass<'_> for ItemsAfterTestModule { let after: Vec<_> = items .filter(|item| { // Ignore the generated test main function - !(item.ident.name == sym::main - && item.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::TestHarness)) + if let ItemKind::Fn { ident, .. } = item.kind + && ident.name == sym::main + && item.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::TestHarness) + { + false + } else { + true + } }) .collect(); if let Some(last) = after.last() && after.iter().all(|&item| { - !matches!(item.kind, ItemKind::Mod(_)) && !item.span.from_expansion() && !is_from_proc_macro(cx, item) + !matches!(item.kind, ItemKind::Mod(..)) && !item.span.from_expansion() && !is_from_proc_macro(cx, item) }) && !fulfill_or_allowed(cx, ITEMS_AFTER_TEST_MODULE, after.iter().map(|item| item.hir_id())) { diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs index 4bc6ad0798c9c..753360906d666 100644 --- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs @@ -28,9 +28,9 @@ declare_clippy_lint! { /// use std::str::Chars; /// struct Data {} /// impl Data { - /// fn iter(&self) -> Chars<'static> { - /// todo!() - /// } + /// fn iter(&self) -> Chars<'static> { + /// todo!() + /// } /// } /// ``` #[clippy::version = "1.57.0"] diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index 173232c511a57..900b20aa9cfb7 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_parent_as_impl; use clippy_utils::source::snippet; use clippy_utils::ty::{deref_chain, get_adt_inherent_method, implements_trait, make_normalized_projection}; +use clippy_utils::{get_parent_as_impl, sym}; use rustc_ast::Mutability; use rustc_errors::Applicability; use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -141,7 +140,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter { ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some() }) && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| { - if item.ident.name.as_str() == "IntoIter" { + if item.ident.name == sym::IntoIter { Some(cx.tcx.hir_impl_item(item.id).expect_type().span) } else { None 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 cabf10b7e0ea9..394005e991295 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -48,7 +48,7 @@ impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]); impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Const(_, generics, _) = &item.kind + if let ItemKind::Const(ident, _, generics, _) = &item.kind // Since static items may not have generics, skip generic const items. // FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it // doesn't account for empty where-clauses that only consist of keyword `where` IINM. @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) { - let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); + let hi_pos = ident.span.lo() - BytePos::from_usize(1); let sugg_span = Span::new( hi_pos - BytePos::from_usize("const".len()), hi_pos, diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs index d9953dbc261b0..d08efa0ec9cc0 100644 --- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs +++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs @@ -73,7 +73,7 @@ impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { - if let ItemKind::Enum(ref def, _) = item.kind + if let ItemKind::Enum(ident, ref def, _) = item.kind && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && let ty::Adt(adt, subst) = ty.kind() && adt.variants().len() > 1 @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { let mut applicability = Applicability::MaybeIncorrect; if is_copy(cx, ty) || maybe_copy(cx, ty) { diag.span_note( - item.ident.span, + ident.span, "boxing a variant would require the type no longer be `Copy`", ); } else { diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs index 3939318bee6ea..01b49403cac85 100644 --- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { // Integer modules are "TBD" deprecated, and the contents are too, // so lint on the `use` statement directly. - if let ItemKind::Use(path, kind @ (UseKind::Single | UseKind::Glob)) = item.kind + if let ItemKind::Use(path, kind @ (UseKind::Single(_) | UseKind::Glob)) = item.kind && !item.span.in_external_macro(cx.sess().source_map()) && let Some(def_id) = path.res[0].opt_def_id() && self.msrv.meets(cx, msrvs::NUMERIC_ASSOCIATED_CONSTANTS) @@ -72,7 +72,9 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { "importing a legacy numeric constant" }, |diag| { - if item.ident.name == kw::Underscore { + if let UseKind::Single(ident) = kind + && ident.name == kw::Underscore + { diag.help("remove this import"); return; } diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 98ba52f12707f..aded31971cec0 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -2,22 +2,23 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, is_trait_method, peel_ref_operators}; +use clippy_utils::{ + fulfill_or_allowed, get_parent_as_impl, is_trait_method, parent_item_name, peel_ref_operators, sym, +}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ - AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, + AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, HirId, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy, QPath, TraitItemRef, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; +use rustc_middle::ty::{self, FnSig, Ty}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use rustc_trait_selection::traits::supertrait_def_ids; declare_clippy_lint! { @@ -123,10 +124,10 @@ declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMP impl<'tcx> LateLintPass<'tcx> for LenZero { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind + if let ItemKind::Trait(_, _, ident, _, _, trait_items) = item.kind && !item.span.from_expansion() { - check_trait_items(cx, item, trait_items); + check_trait_items(cx, item, ident, trait_items); } } @@ -143,21 +144,30 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { && let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id() && let Some(local_id) = ty_id.as_local() && let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_id) - && !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id) && let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()) { let (name, kind) = match cx.tcx.hir_node(ty_hir_id) { Node::ForeignItem(x) => (x.ident.name, "extern type"), Node::Item(x) => match x.kind { - ItemKind::Struct(..) => (x.ident.name, "struct"), - ItemKind::Enum(..) => (x.ident.name, "enum"), - ItemKind::Union(..) => (x.ident.name, "union"), - _ => (x.ident.name, "type"), + ItemKind::Struct(ident, ..) => (ident.name, "struct"), + ItemKind::Enum(ident, ..) => (ident.name, "enum"), + ItemKind::Union(ident, ..) => (ident.name, "union"), + _ => (x.kind.ident().unwrap().name, "type"), }, _ => return, }; - check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind); + check_for_is_empty( + cx, + sig.span, + sig.decl.implicit_self, + output, + ty_id, + name, + kind, + item.hir_id(), + ty_hir_id, + ); } } @@ -180,7 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { let mut applicability = Applicability::MachineApplicable; let lit1 = peel_ref_operators(cx, lt.init); - let lit_str = Sugg::hir_with_context(cx, lit1, lt.span.ctxt(), "_", &mut applicability).maybe_par(); + let lit_str = Sugg::hir_with_context(cx, lit1, lt.span.ctxt(), "_", &mut applicability).maybe_paren(); span_lint_and_sugg( cx, @@ -202,7 +212,11 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { expr.span, lhs_expr, peel_ref_operators(cx, rhs_expr), - (method.ident.name == sym::ne).then_some("!").unwrap_or_default(), + if method.ident.name == sym::ne { + "!" + } else { + Default::default() + }, ); } @@ -250,7 +264,7 @@ fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span { } } -fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) { +fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, ident: Ident, trait_items: &[TraitItemRef]) { fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool { item.ident.name == name && if let AssocItemKind::Fn { has_self } = item.kind { @@ -282,16 +296,10 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items { let mut current_and_super_traits = DefIdSet::default(); fill_trait_set(visited_trait.owner_id.to_def_id(), &mut current_and_super_traits, cx); - let is_empty = sym!(is_empty); - let is_empty_method_found = current_and_super_traits .items() - .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(is_empty)) - .any(|i| { - i.kind == AssocKind::Fn - && i.fn_has_self_parameter - && cx.tcx.fn_sig(i.def_id).skip_binder().inputs().skip_binder().len() == 1 - }); + .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(sym::is_empty)) + .any(|i| i.is_method() && cx.tcx.fn_sig(i.def_id).skip_binder().inputs().skip_binder().len() == 1); if !is_empty_method_found { span_lint( @@ -300,7 +308,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items visited_trait.span, format!( "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method", - visited_trait.ident.name + ident.name ), ); } @@ -443,6 +451,7 @@ fn check_is_empty_sig<'tcx>( } /// Checks if the given type has an `is_empty` method with the appropriate signature. +#[expect(clippy::too_many_arguments)] fn check_for_is_empty( cx: &LateContext<'_>, span: Span, @@ -451,6 +460,8 @@ fn check_for_is_empty( impl_ty: DefId, item_name: Symbol, item_kind: &str, + len_method_hir_id: HirId, + ty_decl_hir_id: HirId, ) { // Implementor may be a type alias, in which case we need to get the `DefId` of the aliased type to // find the correct inherent impls. @@ -460,13 +471,12 @@ fn check_for_is_empty( return; }; - let is_empty = Symbol::intern("is_empty"); let is_empty = cx .tcx .inherent_impls(impl_ty) .iter() - .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty)) - .find(|item| item.kind == AssocKind::Fn); + .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(sym::is_empty)) + .find(|item| item.is_fn()); let (msg, is_empty_span, self_kind) = match is_empty { None => ( @@ -486,7 +496,7 @@ fn check_for_is_empty( None, ), Some(is_empty) - if !(is_empty.fn_has_self_parameter + if !(is_empty.is_method() && check_is_empty_sig( cx, cx.tcx.fn_sig(is_empty.def_id).instantiate_identity().skip_binder(), @@ -506,14 +516,16 @@ fn check_for_is_empty( Some(_) => return, }; - span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, msg, |db| { - if let Some(span) = is_empty_span { - db.span_note(span, "`is_empty` defined here"); - } - if let Some(self_kind) = self_kind { - db.note(output.expected_sig(self_kind)); - } - }); + if !fulfill_or_allowed(cx, LEN_WITHOUT_IS_EMPTY, [len_method_hir_id, ty_decl_hir_id]) { + span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, msg, |db| { + if let Some(span) = is_empty_span { + db.span_note(span, "`is_empty` defined here"); + } + if let Some(self_kind) = self_kind { + db.note(output.expected_sig(self_kind)); + } + }); + } } fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { @@ -523,10 +535,8 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method - if let Some(name) = get_item_name(cx, method) { - if name.as_str() == "is_empty" { - return; - } + if parent_item_name(cx, method) == Some(sym::is_empty) { + return; } check_len(cx, span, method_path.ident.name, receiver, &lit.node, op, compare_to); @@ -573,7 +583,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex let mut applicability = Applicability::MachineApplicable; let lit1 = peel_ref_operators(cx, lit1); - let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_par(); + let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_paren(); span_lint_and_sugg( cx, @@ -588,11 +598,11 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex } fn is_empty_string(expr: &Expr<'_>) -> bool { - if let ExprKind::Lit(lit) = expr.kind { - if let LitKind::Str(lit, _) = lit.node { - let lit = lit.as_str(); - return lit.is_empty(); - } + if let ExprKind::Lit(lit) = expr.kind + && let LitKind::Str(lit, _) = lit.node + { + let lit = lit.as_str(); + return lit.is_empty(); } false } @@ -608,7 +618,7 @@ fn is_empty_array(expr: &Expr<'_>) -> bool { fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { - if item.kind == AssocKind::Fn { + if item.is_fn() { let sig = cx.tcx.fn_sig(item.def_id).skip_binder(); let ty = sig.skip_binder(); ty.inputs().len() == 1 @@ -619,11 +629,10 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks the inherent impl's items for an `is_empty(self)` method. fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { - let is_empty = sym!(is_empty); cx.tcx.inherent_impls(id).iter().any(|imp| { cx.tcx .associated_items(*imp) - .filter_by_name_unhygienic(is_empty) + .filter_by_name_unhygienic(sym::is_empty) .any(|item| is_is_empty(cx, item)) }) } @@ -631,10 +640,9 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn ty_has_is_empty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, depth: usize) -> bool { match ty.kind() { ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { - let is_empty = sym!(is_empty); cx.tcx .associated_items(principal.def_id()) - .filter_by_name_unhygienic(is_empty) + .filter_by_name_unhygienic(sym::is_empty) .any(|item| is_is_empty(cx, item)) }), ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), @@ -644,7 +652,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { && cx.tcx.get_diagnostic_item(sym::Deref).is_some_and(|deref_id| { implements_trait(cx, ty, deref_id, &[]) && cx - .get_associated_type(ty, deref_id, "Target") + .get_associated_type(ty, deref_id, sym::Target) .is_some_and(|deref_ty| ty_has_is_empty(cx, deref_ty, depth + 1)) })) }, diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index bdbf5b37c5f0e..916191b2a7b0f 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; +use clippy_utils::ty::{implements_trait, is_must_use_ty}; use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths}; use rustc_hir::{LetStmt, LocalSource, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -129,12 +129,6 @@ declare_clippy_lint! { declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]); -const SYNC_GUARD_PATHS: [&[&str]; 3] = [ - &paths::PARKING_LOT_MUTEX_GUARD, - &paths::PARKING_LOT_RWLOCK_READ_GUARD, - &paths::PARKING_LOT_RWLOCK_WRITE_GUARD, -]; - impl<'tcx> LateLintPass<'tcx> for LetUnderscore { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if matches!(local.source, LocalSource::Normal) @@ -144,7 +138,9 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { { let init_ty = cx.typeck_results().expr_ty(init); let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)), + GenericArgKind::Type(inner_ty) => inner_ty + .ty_adt_def() + .is_some_and(|adt| paths::PARKING_LOT_GUARDS.iter().any(|path| path.matches(cx, adt.did()))), GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }); if contains_sync_guard { diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs index 9c8488ff381b3..1917ca24a05bd 100644 --- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; +use rustc_errors::Applicability; use rustc_hir::{LetStmt, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -32,13 +33,19 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { && !local.span.in_external_macro(cx.tcx.sess.source_map()) && !is_from_proc_macro(cx, ty) { - span_lint_and_help( + span_lint_and_then( cx, LET_WITH_TYPE_UNDERSCORE, local.span, "variable declared with type underscore", - Some(ty.span.with_lo(local.pat.span.hi())), - "remove the explicit type `_` declaration", + |diag| { + diag.span_suggestion_verbose( + ty.span.with_lo(local.pat.span.hi()), + "remove the explicit type `_` declaration", + "", + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index cc3d972f017b8..92eb3d7a7c59d 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -1,5 +1,4 @@ #![feature(array_windows)] -#![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] #![feature(macro_metavar_expr_concat)] #![feature(f128)] @@ -7,7 +6,6 @@ #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iter_partition_in_place)] -#![feature(let_chains)] #![feature(never_type)] #![feature(round_char_boundary)] #![feature(rustc_private)] @@ -37,7 +35,7 @@ extern crate rustc_abi; extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; -extern crate rustc_attr_parsing; +extern crate rustc_attr_data_structures; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; @@ -65,7 +63,6 @@ mod declare_clippy_lint; #[macro_use] extern crate clippy_utils; -#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; pub mod ctfe; // Very important lint, do not remove (rust#125116) @@ -97,6 +94,7 @@ mod cargo; mod casts; mod cfg_not_test; mod checked_conversions; +mod cloned_ref_to_slice_refs; mod cognitive_complexity; mod collapsible_if; mod collection_is_never_read; @@ -205,6 +203,7 @@ mod loops; mod macro_metavars_in_unsafe; mod macro_use; mod main_recursion; +mod manual_abs_diff; mod manual_assert; mod manual_async_fn; mod manual_bits; @@ -226,7 +225,6 @@ mod manual_rotate; mod manual_slice_size_calculation; mod manual_string_new; mod manual_strip; -mod manual_unwrap_or_default; mod map_unit_fn; mod match_result_ok; mod matches; @@ -320,6 +318,7 @@ mod redundant_locals; mod redundant_pub_crate; mod redundant_slicing; mod redundant_static_lifetimes; +mod redundant_test_prefix; mod redundant_type_annotations; mod ref_option_ref; mod ref_patterns; @@ -394,6 +393,7 @@ mod unwrap; mod unwrap_in_result; mod upper_case_acronyms; mod use_self; +mod useless_concat; mod useless_conversion; mod vec; mod vec_init_then_push; @@ -408,24 +408,9 @@ mod zombie_processes; use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_utils::macros::FormatArgsStorage; -use utils::attr_collector::{AttrCollector, AttrStorage}; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; - -/// Register all pre expansion lints -/// -/// Pre-expansion lints run before any macro expansion has happened. -/// -/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate -/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. -/// -/// Used in `./src/driver.rs`. -pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { - // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. - store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf))); - - store.register_early_pass(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))); -} +use utils::attr_collector::{AttrCollector, AttrStorage}; #[derive(Default)] struct RegistrationGroups { @@ -439,8 +424,6 @@ struct RegistrationGroups { restriction: Vec, style: Vec, suspicious: Vec, - #[cfg(feature = "internal")] - internal: Vec, } impl RegistrationGroups { @@ -456,8 +439,6 @@ impl RegistrationGroups { store.register_group(true, "clippy::restriction", Some("clippy_restriction"), self.restriction); store.register_group(true, "clippy::style", Some("clippy_style"), self.style); store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious); - #[cfg(feature = "internal")] - store.register_group(true, "clippy::internal", Some("clippy_internal"), self.internal); } } @@ -472,8 +453,6 @@ pub(crate) enum LintCategory { Restriction, Style, Suspicious, - #[cfg(feature = "internal")] - Internal, } #[allow(clippy::enum_glob_use)] @@ -495,8 +474,6 @@ impl LintCategory { Restriction => &mut groups.restriction, Style => &mut groups.style, Suspicious => &mut groups.suspicious, - #[cfg(feature = "internal")] - Internal => &mut groups.internal, } } } @@ -530,8 +507,6 @@ impl LintInfo { Restriction => "restriction", Style => "style", Suspicious => "suspicious", - #[cfg(feature = "internal")] - Internal => "internal", } } } @@ -589,6 +564,13 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_removed(name, reason); } + // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. + // Due to the architecture of the compiler, currently `cfg_attr` attributes on crate + // level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. + store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf))); + + store.register_early_pass(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))); + let format_args_storage = FormatArgsStorage::default(); let format_args = format_args_storage.clone(); store.register_early_pass(move || { @@ -601,30 +583,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { let attrs = attr_storage.clone(); store.register_early_pass(move || Box::new(AttrCollector::new(attrs.clone()))); - // all the internal lints - #[cfg(feature = "internal")] - { - store.register_early_pass(|| { - Box::new(utils::internal_lints::unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths) - }); - store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce)); - store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls)); - store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths)); - store.register_late_pass(|_| { - Box::::default() - }); - store.register_late_pass(|_| { - Box::::default() - }); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass)); - store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl)); - store.register_late_pass(|_| { - Box::new(utils::internal_lints::almost_standard_lint_formulation::AlmostStandardFormulation::new()) - }); - store.register_late_pass(|_| Box::new(utils::internal_lints::slow_symbol_comparisons::SlowSymbolComparisons)); - } - store.register_late_pass(|_| Box::new(ctfe::ClippyCtfe)); store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf))); @@ -771,8 +729,9 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints)); store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall)); store.register_early_pass(|| Box::new(unused_unit::UnusedUnit)); + store.register_late_pass(|_| Box::new(unused_unit::UnusedUnit)); store.register_late_pass(|_| Box::new(returns::Return)); - store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf)); + store.register_late_pass(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf))); store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); @@ -789,7 +748,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(conf))); store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(exit::Exit)); - store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome)); + store.register_late_pass(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf))); store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf))); store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf))); store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); @@ -857,7 +816,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(write::Write::new(conf, format_args.clone()))); store.register_late_pass(move |_| Box::new(cargo::Cargo::new(conf))); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); - store.register_early_pass(|| Box::new(empty_with_brackets::EmptyWithBrackets)); + store.register_late_pass(|_| Box::new(empty_with_brackets::EmptyWithBrackets::default())); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_late_pass(|_| Box::new(format_push_string::FormatPushString)); @@ -879,6 +838,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))); store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); + store.register_late_pass(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); @@ -907,7 +867,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); store.register_late_pass(move |_| Box::new(lines_filter_map_ok::LinesFilterMapOk::new(conf))); store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); - store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation)); + store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf))); store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))); store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule)); store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); @@ -960,7 +920,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(conf))); store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); - store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault)); store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))); store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf))); @@ -971,7 +930,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses)); store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)); store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); - store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); + store.register_late_pass(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf))); store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); store.register_late_pass(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)); store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); @@ -979,10 +938,13 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); store.register_early_pass(|| Box::new(empty_line_after::EmptyLineAfter::new())); store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); + store.register_late_pass(|_| Box::new(useless_concat::UselessConcat)); store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))); store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap)); + store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)); + store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 3dd2de1fafc72..8fe0c9d60f961 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -14,7 +14,7 @@ use rustc_hir::intravisit::{ }; use rustc_hir::{ AmbigArg, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, - Generics, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, + Generics, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeKind, LifetimeParamKind, Node, PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereBoundPredicate, WherePredicate, WherePredicateKind, lang_items, }; @@ -88,7 +88,7 @@ declare_clippy_lint! { /// x.chars() /// } /// ``` - #[clippy::version = "1.84.0"] + #[clippy::version = "1.87.0"] pub ELIDABLE_LIFETIME_NAMES, pedantic, "lifetime name that can be replaced with the anonymous lifetime" @@ -150,16 +150,16 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } = item.kind { check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv); - } else if let ItemKind::Impl(impl_) = item.kind { - if !item.span.from_expansion() { - report_extra_impl_lifetimes(cx, impl_); - } + } else if let ItemKind::Impl(impl_) = item.kind + && !item.span.from_expansion() + { + report_extra_impl_lifetimes(cx, impl_); } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { if let ImplItemKind::Fn(ref sig, id) = item.kind { - let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id.def_id).is_none(); + let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id).is_none(); check_fn_inner( cx, sig, @@ -189,7 +189,7 @@ fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, sig: &'tcx FnSig<'_>, body: Option, - trait_sig: Option<&[Ident]>, + trait_sig: Option<&[Option]>, generics: &'tcx Generics<'_>, span: Span, report_extra_lifetimes: bool, @@ -218,7 +218,7 @@ fn check_fn_inner<'tcx>( for bound in pred.bounds { let mut visitor = RefVisitor::new(cx); walk_param_bound(&mut visitor, bound); - if visitor.lts.iter().any(|lt| matches!(lt.res, LifetimeName::Param(_))) { + if visitor.lts.iter().any(|lt| matches!(lt.kind, LifetimeKind::Param(_))) { return; } if let GenericBound::Trait(ref trait_ref) = *bound { @@ -235,7 +235,7 @@ fn check_fn_inner<'tcx>( _ => None, }); for bound in lifetimes { - if bound.res != LifetimeName::Static && !bound.is_elided() { + if bound.kind != LifetimeKind::Static && !bound.is_elided() { return; } } @@ -264,7 +264,7 @@ fn could_use_elision<'tcx>( cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, body: Option, - trait_sig: Option<&[Ident]>, + trait_sig: Option<&[Option]>, named_generics: &'tcx [GenericParam<'_>], msrv: Msrv, ) -> Option<(Vec, Vec)> { @@ -300,8 +300,8 @@ fn could_use_elision<'tcx>( let input_lts = input_visitor.lts; let output_lts = output_visitor.lts; - if let Some(trait_sig) = trait_sig - && non_elidable_self_type(cx, func, trait_sig.first().copied(), msrv) + if let Some(&[trait_sig]) = trait_sig + && non_elidable_self_type(cx, func, trait_sig, msrv) { return None; } @@ -314,7 +314,7 @@ fn could_use_elision<'tcx>( return None; } - let mut checker = BodyLifetimeChecker; + let mut checker = BodyLifetimeChecker::new(cx); if checker.visit_expr(body.value).is_break() { return None; } @@ -421,8 +421,8 @@ fn named_lifetime_occurrences(lts: &[Lifetime]) -> Vec<(LocalDefId, usize)> { } fn named_lifetime(lt: &Lifetime) -> Option { - match lt.res { - LifetimeName::Param(id) if !lt.is_anonymous() => Some(id), + match lt.kind { + LifetimeKind::Param(id) if !lt.is_anonymous() => Some(id), _ => None, } } @@ -614,7 +614,7 @@ where // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - if let LifetimeName::Param(def_id) = lifetime.res + if let LifetimeKind::Param(def_id) = lifetime.kind && let Some(usages) = self.map.get_mut(&def_id) { usages.push(Usage { @@ -826,7 +826,7 @@ fn report_elidable_lifetimes( .iter() .map(|<| cx.tcx.def_span(lt)) .chain(usages.iter().filter_map(|usage| { - if let LifetimeName::Param(def_id) = usage.res + if let LifetimeKind::Param(def_id) = usage.kind && elidable_lts.contains(&def_id) { return Some(usage.ident.span); @@ -911,10 +911,23 @@ fn elision_suggestions( Some(suggestions) } -struct BodyLifetimeChecker; +struct BodyLifetimeChecker<'tcx> { + tcx: TyCtxt<'tcx>, +} -impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { +impl<'tcx> BodyLifetimeChecker<'tcx> { + fn new(cx: &LateContext<'tcx>) -> Self { + Self { tcx: cx.tcx } + } +} + +impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker<'tcx> { type Result = ControlFlow<()>; + type NestedFilter = middle_nested_filter::OnlyBodies; + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.tcx + } // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) -> ControlFlow<()> { if !lifetime.is_anonymous() && lifetime.ident.name != kw::StaticLifetime { diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs index d8af44233d3ee..14ccb6fce2239 100644 --- a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs +++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs @@ -2,12 +2,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id}; +use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id, sym}; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::sym; +use rustc_span::Symbol; pub struct LinesFilterMapOk { msrv: Msrv, @@ -74,17 +74,17 @@ impl LateLintPass<'_> for LinesFilterMapOk { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind && is_trait_method(cx, expr, sym::Iterator) - && let fm_method_str = fm_method.ident.as_str() - && matches!(fm_method_str, "filter_map" | "flat_map" | "flatten") + && let fm_method_name = fm_method.ident.name + && matches!(fm_method_name, sym::filter_map | sym::flat_map | sym::flatten) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines) - && should_lint(cx, fm_args, fm_method_str) + && should_lint(cx, fm_args, fm_method_name) && self.msrv.meets(cx, msrvs::MAP_WHILE) { span_lint_and_then( cx, LINES_FILTER_MAP_OK, fm_span, - format!("`{fm_method_str}()` will run forever if the iterator repeatedly produces an `Err`",), + format!("`{fm_method_name}()` will run forever if the iterator repeatedly produces an `Err`",), |diag| { diag.span_note( fm_receiver.span, @@ -101,9 +101,9 @@ impl LateLintPass<'_> for LinesFilterMapOk { } } -fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> bool { +fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_name: Symbol) -> bool { match args { - [] => method_str == "flatten", + [] => method_name == sym::flatten, [fm_arg] => { match &fm_arg.kind { // Detect `Result::ok` @@ -120,7 +120,7 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> boo && path_to_local_id(receiver, param.pat.hir_id) && let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id) { - is_diag_item_method(cx, method_did, sym::Result) && method.ident.as_str() == "ok" + is_diag_item_method(cx, method_did, sym::Result) && method.ident.name == sym::ok } else { false } diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 805de23408bf5..7cbfa2d097ae5 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -387,12 +387,11 @@ impl LiteralDigitGrouping { let first = groups.next().expect("At least one group"); - if radix == Radix::Binary || radix == Radix::Octal || radix == Radix::Hexadecimal { - if let Some(second_size) = groups.next() { - if !groups.all(|i| i == second_size) || first > second_size { - return Err(WarningType::UnusualByteGroupings); - } - } + if (radix == Radix::Binary || radix == Radix::Octal || radix == Radix::Hexadecimal) + && let Some(second_size) = groups.next() + && (!groups.all(|i| i == second_size) || first > second_size) + { + return Err(WarningType::UnusualByteGroupings); } if let Some(second) = groups.next() { diff --git a/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs b/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs index 975e6833a35f8..244e7c95122e0 100644 --- a/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs +++ b/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs @@ -45,15 +45,14 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, spans: &[(Span, Option = ControlFlow::Continue(()); + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr<'_>, body: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = iterable.kind + && let Some(method_id) = cx.typeck_results().type_dependent_def_id(iterable.hir_id) + && cx.tcx.is_diagnostic_item(sym::enumerate_method, method_id) + && let ExprKind::MethodCall(_, chars_recv, _, chars_span) = enumerate_recv.kind + && let Some(method_id) = cx.typeck_results().type_dependent_def_id(enumerate_recv.hir_id) + && cx.tcx.is_diagnostic_item(sym::str_chars, method_id) + { + if let PatKind::Tuple([pat, _], _) = pat.kind + && let PatKind::Binding(_, binding_id, ..) = pat.kind + { + // Destructured iterator element `(idx, _)`, look for uses of the binding + for_each_expr(cx, body, |expr| { + if path_to_local_id(expr, binding_id) { + check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); + } + CONTINUE + }); + } else if let PatKind::Binding(_, binding_id, ..) = pat.kind { + // Bound as a tuple, look for `tup.0` + for_each_expr(cx, body, |expr| { + if let ExprKind::Field(e, field) = expr.kind + && path_to_local_id(e, binding_id) + && field.name == sym::integer(0) + { + check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); + } + CONTINUE + }); + } + } +} + +fn check_index_usage<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + pat: &Pat<'_>, + enumerate_span: Span, + chars_span: Span, + chars_recv: &Expr<'_>, +) { + let Some(parent_expr) = index_consumed_at(cx, expr) else { + return; + }; + + let is_string_like = |ty: Ty<'_>| ty.is_str() || is_type_lang_item(cx, ty, LangItem::String); + let message = match parent_expr.kind { + ExprKind::MethodCall(segment, recv, ..) + // We currently only lint `str` methods (which `String` can deref to), so a `.is_str()` check is sufficient here + // (contrary to the `ExprKind::Index` case which needs to handle both with `is_string_like` because `String` implements + // `Index` directly and no deref to `str` would happen in that case). + if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_str() + && BYTE_INDEX_METHODS.contains(&segment.ident.name) + && eq_expr_value(cx, chars_recv, recv) => + { + "passing a character position to a method that expects a byte index" + }, + ExprKind::Index(target, ..) + if is_string_like(cx.typeck_results().expr_ty_adjusted(target).peel_refs()) + && eq_expr_value(cx, chars_recv, target) => + { + "indexing into a string with a character position where a byte index is expected" + }, + _ => return, + }; + + span_lint_hir_and_then( + cx, + CHAR_INDICES_AS_BYTE_INDICES, + expr.hir_id, + expr.span, + message, + |diag| { + diag.note("a character can take up more than one byte, so they are not interchangeable") + .span_note( + MultiSpan::from_spans(vec![pat.span, enumerate_span]), + "position comes from the enumerate iterator", + ) + .span_suggestion_verbose( + chars_span.to(enumerate_span), + "consider using `.char_indices()` instead", + "char_indices()", + Applicability::MaybeIncorrect, + ); + }, + ); +} + +/// Returns the expression which ultimately consumes the index. +/// This is usually the parent expression, i.e. `.split_at(idx)` for `idx`, +/// but for `.get(..idx)` we want to consider the method call the consuming expression, +/// which requires skipping past the range expression. +fn index_consumed_at<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) { + match node { + Node::Expr(expr) if higher::Range::hir(expr).is_some() => {}, + Node::ExprField(_) => {}, + Node::Expr(expr) => return Some(expr), + _ => break, + } + } + None +} diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs index d5ddc33e928db..4aa1c2e211d33 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -1,7 +1,7 @@ use super::EXPLICIT_INTO_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_trait_method; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -76,7 +76,7 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr< }; let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability); + let object = snippet_with_context(cx, self_arg.span, call_expr.span.ctxt(), "_", &mut applicability).0; span_lint_and_sugg( cx, EXPLICIT_INTO_ITER_LOOP, diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index 412c78cc80411..010652e1cb902 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -1,7 +1,8 @@ use super::EXPLICIT_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; +use clippy_utils::sym; use clippy_utils::ty::{ implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection, make_normalized_projection_with_regions, normalize_with_regions, @@ -11,7 +12,6 @@ use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, EarlyBinder, Ty}; -use rustc_span::sym; pub(super) fn check( cx: &LateContext<'_>, @@ -36,7 +36,7 @@ pub(super) fn check( } let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability); + let object = snippet_with_context(cx, self_arg.span, call_expr.span.ctxt(), "_", &mut applicability).0; span_lint_and_sugg( cx, EXPLICIT_ITER_LOOP, @@ -119,7 +119,7 @@ fn is_ref_iterable<'tcx>( && let typing_env = ty::TypingEnv::non_body_analysis(cx.tcx, fn_id) && implements_trait_with_env(cx.tcx, typing_env, req_self_ty, trait_id, Some(fn_id), &[]) && let Some(into_iter_ty) = - make_normalized_projection_with_regions(cx.tcx, typing_env, trait_id, sym!(IntoIter), [req_self_ty]) + make_normalized_projection_with_regions(cx.tcx, typing_env, trait_id, sym::IntoIter, [req_self_ty]) && let req_res_ty = normalize_with_regions(cx.tcx, typing_env, req_res_ty) && into_iter_ty == req_res_ty { @@ -152,7 +152,7 @@ fn is_ref_iterable<'tcx>( // Using by value won't consume anything if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [self_ty]) && ty == res_ty { return Some((AdjustKind::None, self_ty)); @@ -169,7 +169,7 @@ fn is_ref_iterable<'tcx>( }; if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [self_ty]) && ty == res_ty { return Some((AdjustKind::reborrow(mutbl), self_ty)); @@ -183,7 +183,7 @@ fn is_ref_iterable<'tcx>( let self_ty = Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, self_ty, mutbl); if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [self_ty]) && ty == res_ty { return Some((AdjustKind::borrow(mutbl), self_ty)); @@ -206,7 +206,7 @@ fn is_ref_iterable<'tcx>( && target != self_ty && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [target]) && ty == res_ty { Some((AdjustKind::auto_reborrow(mutbl), target)) @@ -224,7 +224,7 @@ fn is_ref_iterable<'tcx>( if is_copy(cx, target) && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [target]) && ty == res_ty { Some((AdjustKind::Deref, target)) @@ -242,7 +242,7 @@ fn is_ref_iterable<'tcx>( if self_ty.is_ref() && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = - make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target]) + make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym::IntoIter, [target]) && ty == res_ty { Some((AdjustKind::auto_borrow(mutbl), target)) diff --git a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs index 185d834becafc..e314bc2068b30 100644 --- a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs @@ -13,45 +13,45 @@ use rustc_span::sym; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) { let pat_span = pat.span; - if let PatKind::Tuple(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() { - ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { - (key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl), - (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not), - _ => return, - }, + if let PatKind::Tuple(pat, _) = pat.kind + && pat.len() == 2 + { + let arg_span = arg.span; + 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(cx, key, body) => (pat[1].span, "value", ty, mutbl), + (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not), _ => return, - }; - let mutbl = match mutbl { - Mutability::Not => "", - Mutability::Mut => "_mut", - }; - let arg = match arg.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr, - _ => arg, - }; + }, + _ => return, + }; + let mutbl = match mutbl { + Mutability::Not => "", + Mutability::Mut => "_mut", + }; + let arg = match arg.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr, + _ => arg, + }; - if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) { - span_lint_and_then( - cx, - FOR_KV_MAP, - arg_span, - format!("you seem to want to iterate on a map's {kind}s"), - |diag| { - let map = sugg::Sugg::hir(cx, arg, "map"); - diag.multipart_suggestion( - "use the corresponding method", - vec![ - (pat_span, snippet(cx, new_pat_span, kind).into_owned()), - (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_par())), - ], - Applicability::MachineApplicable, - ); - }, - ); - } + if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) { + span_lint_and_then( + cx, + FOR_KV_MAP, + arg_span, + format!("you seem to want to iterate on a map's {kind}s"), + |diag| { + let map = sugg::Sugg::hir(cx, arg, "map"); + diag.multipart_suggestion( + "use the corresponding method", + vec![ + (pat_span, snippet(cx, new_pat_span, kind).into_owned()), + (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_paren())), + ], + Applicability::MachineApplicable, + ); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs index aa8a2934f89bd..35737f3eafe23 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs @@ -3,6 +3,7 @@ use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; +use clippy_utils::usage::contains_return_break_continue_macro; use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -35,6 +36,7 @@ pub(super) fn check<'tcx>( && let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind && is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome) && path_res(cx, inner_ret) == Res::Local(binding_id) + && !contains_return_break_continue_macro(cond) && let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr) { let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs index 701567a7d84e7..d9c4b526da99e 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs @@ -28,37 +28,37 @@ pub(super) fn check<'tcx>( end: Some(end), limits, }) = higher::Range::hir(arg) - { // the var must be a single name - if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { - let mut starts = vec![Start { - id: canonical_id, - kind: StartKind::Range, - }]; - - // This is one of few ways to return different iterators - // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 - let mut iter_a = None; - let mut iter_b = None; - - if let ExprKind::Block(block, _) = body.kind { - if let Some(loop_counters) = get_loop_counters(cx, block, expr) { - starts.extend(loop_counters); - } - iter_a = Some(get_assignments(block, &starts)); - } else { - iter_b = Some(get_assignment(body)); + && let PatKind::Binding(_, canonical_id, _, _) = pat.kind + { + let mut starts = vec![Start { + id: canonical_id, + kind: StartKind::Range, + }]; + + // This is one of few ways to return different iterators + // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 + let mut iter_a = None; + let mut iter_b = None; + + if let ExprKind::Block(block, _) = body.kind { + if let Some(loop_counters) = get_loop_counters(cx, block, expr) { + starts.extend(loop_counters); } + iter_a = Some(get_assignments(block, &starts)); + } else { + iter_b = Some(get_assignment(body)); + } - let assignments = iter_a.into_iter().flatten().chain(iter_b); + let assignments = iter_a.into_iter().flatten().chain(iter_b); - let big_sugg = assignments - // The only statements in the for loops can be indexed assignments from - // indexed retrievals (except increments of loop counters). - .map(|o| { - o.and_then(|(lhs, rhs)| { - let rhs = fetch_cloned_expr(rhs); - if let ExprKind::Index(base_left, idx_left, _) = lhs.kind + let big_sugg = assignments + // The only statements in the for loops can be indexed assignments from + // indexed retrievals (except increments of loop counters). + .map(|o| { + o.and_then(|(lhs, rhs)| { + let rhs = fetch_cloned_expr(rhs); + if let ExprKind::Index(base_left, idx_left, _) = lhs.kind && let ExprKind::Index(base_right, idx_right, _) = rhs.kind && let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left)) && get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some() @@ -68,42 +68,41 @@ pub(super) fn check<'tcx>( && !local_used_in(cx, canonical_id, base_right) // Source and destination must be different && path_to_local(base_left) != path_to_local(base_right) - { - Some(( - ty, - IndexExpr { - base: base_left, - idx: start_left, - idx_offset: offset_left, - }, - IndexExpr { - base: base_right, - idx: start_right, - idx_offset: offset_right, - }, - )) - } else { - None - } - }) + { + Some(( + ty, + IndexExpr { + base: base_left, + idx: start_left, + idx_offset: offset_left, + }, + IndexExpr { + base: base_right, + idx: start_right, + idx_offset: offset_right, + }, + )) + } else { + None + } }) - .map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src))) - .collect::>>() - .filter(|v| !v.is_empty()) - .map(|v| v.join("\n ")); - - if let Some(big_sugg) = big_sugg { - span_lint_and_sugg( - cx, - MANUAL_MEMCPY, - expr.span, - "it looks like you're manually copying between slices", - "try replacing the loop by", - big_sugg, - Applicability::Unspecified, - ); - return true; - } + }) + .map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src))) + .collect::>>() + .filter(|v| !v.is_empty()) + .map(|v| v.join("\n ")); + + if let Some(big_sugg) = big_sugg { + span_lint_and_sugg( + cx, + MANUAL_MEMCPY, + expr.span, + "it looks like you're manually copying between slices", + "try replacing the loop by", + big_sugg, + Applicability::Unspecified, + ); + return true; } } false @@ -184,7 +183,12 @@ fn build_manual_memcpy_suggestion<'tcx>( { dst_base_str } else { - format!("{dst_base_str}[{}..{}]", dst_offset.maybe_par(), dst_limit.maybe_par()).into() + format!( + "{dst_base_str}[{}..{}]", + dst_offset.maybe_paren(), + dst_limit.maybe_paren() + ) + .into() }; let method_str = if is_copy(cx, elem_ty) { @@ -196,7 +200,12 @@ fn build_manual_memcpy_suggestion<'tcx>( let src = if is_array_length_equal_to_range(cx, start, end, src.base) { src_base_str } else { - format!("{src_base_str}[{}..{}]", src_offset.maybe_par(), src_limit.maybe_par()).into() + format!( + "{src_base_str}[{}..{}]", + src_offset.maybe_paren(), + src_limit.maybe_paren() + ) + .into() }; format!("{dst}.{method_str}(&{src});") diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs index 343f7c5d2d121..15c656cc7bc76 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{HasSession, snippet_with_applicability}; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{implements_trait, is_slice_like}; use clippy_utils::visitors::is_local_used; use clippy_utils::{higher, peel_blocks_with_stmt, span_contains_comment}; use rustc_ast::ast::LitKind; @@ -58,6 +58,8 @@ pub(super) fn check<'tcx>( && let Res::Local(idx_hir) = idx_path.res && !is_local_used(cx, assignval, idx_hir) && msrv.meets(cx, msrvs::SLICE_FILL) + && let slice_ty = cx.typeck_results().expr_ty(slice).peel_refs() + && is_slice_like(cx, slice_ty) { sugg(cx, body, expr, slice.span, assignval.span); } diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs index 4473a3343c7c6..9527e258db8ab 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs @@ -81,15 +81,15 @@ fn check_local(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, } fn check_call_arguments(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, loop_span: Span) { - if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind { - if let ExprKind::MethodCall(.., args, _) | ExprKind::Call(_, args) = expr.kind { - let offending_arg = args - .iter() - .find_map(|arg| is_vec_pop_unwrap(cx, arg, is_empty_recv).then_some(arg.span)); + if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind + && let ExprKind::MethodCall(.., args, _) | ExprKind::Call(_, args) = expr.kind + { + let offending_arg = args + .iter() + .find_map(|arg| is_vec_pop_unwrap(cx, arg, is_empty_recv).then_some(arg.span)); - if let Some(offending_arg) = offending_arg { - report_lint(cx, offending_arg, PopStmt::Anonymous, loop_span, is_empty_recv.span); - } + if let Some(offending_arg) = offending_arg { + report_lint(cx, offending_arg, PopStmt::Anonymous, loop_span, is_empty_recv.span); } } } diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index ed725a0398910..01c36b8cb12fc 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -1,3 +1,4 @@ +mod char_indices_as_byte_indices; mod empty_loop; mod explicit_counter_loop; mod explicit_into_iter_loop; @@ -24,8 +25,8 @@ mod while_let_loop; mod while_let_on_iterator; use clippy_config::Conf; -use clippy_utils::higher; use clippy_utils::msrvs::Msrv; +use clippy_utils::{higher, sym}; use rustc_ast::Label; use rustc_hir::{Expr, ExprKind, LoopSource, Pat}; use rustc_lint::{LateContext, LateLintPass}; @@ -469,7 +470,7 @@ declare_clippy_lint! { /// let item2 = 3; /// let mut vec: Vec = Vec::new(); /// for _ in 0..20 { - /// vec.push(item1); + /// vec.push(item1); /// } /// for _ in 0..30 { /// vec.push(item2); @@ -740,6 +741,49 @@ declare_clippy_lint! { "manually filling a slice with a value" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of a character position yielded by `.chars().enumerate()` in a context where a **byte index** is expected, + /// such as an argument to a specific `str` method or indexing into a `str` or `String`. + /// + /// ### Why is this bad? + /// A character (more specifically, a Unicode scalar value) that is yielded by `str::chars` can take up multiple bytes, + /// so a character position does not necessarily have the same byte index at which the character is stored. + /// Thus, using the character position where a byte index is expected can unexpectedly return wrong values + /// or panic when the string consists of multibyte characters. + /// + /// For example, the character `a` in `äa` is stored at byte index 2 but has the character position 1. + /// Using the character position 1 to index into the string will lead to a panic as it is in the middle of the first character. + /// + /// Instead of `.chars().enumerate()`, the correct iterator to use is `.char_indices()`, which yields byte indices. + /// + /// This pattern is technically fine if the strings are known to only use the ASCII subset, + /// though in those cases it would be better to use `bytes()` directly to make the intent clearer, + /// but there is also no downside to just using `.char_indices()` directly and supporting non-ASCII strings. + /// + /// You may also want to read the [chapter on strings in the Rust Book](https://doc.rust-lang.org/book/ch08-02-strings.html) + /// which goes into this in more detail. + /// + /// ### Example + /// ```no_run + /// # let s = "..."; + /// for (idx, c) in s.chars().enumerate() { + /// let _ = s[idx..]; // ⚠️ Panics for strings consisting of multibyte characters + /// } + /// ``` + /// Use instead: + /// ```no_run + /// # let s = "..."; + /// for (idx, c) in s.char_indices() { + /// let _ = s[idx..]; + /// } + /// ``` + #[clippy::version = "1.88.0"] + pub CHAR_INDICES_AS_BYTE_INDICES, + correctness, + "using the character position yielded by `.chars().enumerate()` in a context where a byte index is expected" +} + pub struct Loops { msrv: Msrv, enforce_iter_loop_reborrow: bool, @@ -777,6 +821,7 @@ impl_lint_pass!(Loops => [ UNUSED_ENUMERATE_INDEX, INFINITE_LOOP, MANUAL_SLICE_FILL, + CHAR_INDICES_AS_BYTE_INDICES, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -860,18 +905,21 @@ impl Loops { manual_flatten::check(cx, pat, arg, body, span, self.msrv); manual_find::check(cx, pat, arg, body, span, expr); unused_enumerate_index::check(cx, pat, arg, body); + char_indices_as_byte_indices::check(cx, pat, arg, body); } fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) { - if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind { - match method.ident.as_str() { - "iter" | "iter_mut" => { + if !arg.span.from_expansion() + && let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind + { + match method.ident.name { + sym::iter | sym::iter_mut => { explicit_iter_loop::check(cx, self_arg, arg, self.msrv, self.enforce_iter_loop_reborrow); }, - "into_iter" => { + sym::into_iter => { explicit_into_iter_loop::check(cx, self_arg, arg); }, - "next" => { + sym::next => { iter_next_loop::check(cx, arg); }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs index 39e5e140b7a40..70ca452013f91 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs @@ -79,15 +79,17 @@ struct MutatePairDelegate<'a, 'tcx> { impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { - if bk == ty::BorrowKind::Mutable { - if let PlaceBase::Local(id) = cmt.place.base { - if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); - } - if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); - } + if bk == ty::BorrowKind::Mutable + && let PlaceBase::Local(id) = cmt.place.base + { + if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { + self.span_low = Some(self.cx.tcx.hir_span(diag_expr_id)); + } + if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { + self.span_high = Some(self.cx.tcx.hir_span(diag_expr_id)); } } } @@ -95,10 +97,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { if let PlaceBase::Local(id) = cmt.place.base { if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); + self.span_low = Some(self.cx.tcx.hir_span(diag_expr_id)); } if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { - self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); + self.span_high = Some(self.cx.tcx.hir_span(diag_expr_id)); } } } @@ -127,7 +129,7 @@ impl BreakAfterExprVisitor { }; get_enclosing_block(cx, hir_id).is_some_and(|block| { - visitor.visit_block(block); + let _ = visitor.visit_block(block); visitor.break_after_expr }) } diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index 0f62183eb33d6..7837b18bcd36c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -31,155 +31,154 @@ pub(super) fn check<'tcx>( ref end, limits, }) = higher::Range::hir(arg) - { // the var must be a single name - if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { - let mut visitor = VarVisitor { - cx, - var: canonical_id, - indexed_mut: FxHashSet::default(), - indexed_indirectly: FxHashMap::default(), - indexed_directly: FxIndexMap::default(), - referenced: FxHashSet::default(), - nonindex: false, - prefer_mutable: false, - }; - walk_expr(&mut visitor, body); - - // linting condition: we only indexed one variable, and indexed it directly - if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 { - let (indexed, (indexed_extent, indexed_ty)) = visitor - .indexed_directly - .into_iter() - .next() - .expect("already checked that we have exactly 1 element"); + && let PatKind::Binding(_, canonical_id, ident, _) = pat.kind + { + let mut visitor = VarVisitor { + cx, + var: canonical_id, + indexed_mut: FxHashSet::default(), + indexed_indirectly: FxHashMap::default(), + indexed_directly: FxIndexMap::default(), + referenced: FxHashSet::default(), + nonindex: false, + prefer_mutable: false, + }; + walk_expr(&mut visitor, body); - // ensure that the indexed variable was declared before the loop, see #601 - if let Some(indexed_extent) = indexed_extent { - let parent_def_id = cx.tcx.hir_get_parent_item(expr.hir_id); - let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id); - let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); - if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { - return; - } - } + // linting condition: we only indexed one variable, and indexed it directly + if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 { + let (indexed, (indexed_extent, indexed_ty)) = visitor + .indexed_directly + .into_iter() + .next() + .expect("already checked that we have exactly 1 element"); - // don't lint if the container that is indexed does not have .iter() method - let has_iter = has_iter_method(cx, indexed_ty); - if has_iter.is_none() { + // ensure that the indexed variable was declared before the loop, see #601 + if let Some(indexed_extent) = indexed_extent { + let parent_def_id = cx.tcx.hir_get_parent_item(expr.hir_id); + let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id); + let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); + if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { return; } + } - // don't lint if the container that is indexed into is also used without - // indexing - if visitor.referenced.contains(&indexed) { - return; - } + // don't lint if the container that is indexed does not have .iter() method + let has_iter = has_iter_method(cx, indexed_ty); + if has_iter.is_none() { + return; + } - let starts_at_zero = is_integer_const(cx, start, 0); + // don't lint if the container that is indexed into is also used without + // indexing + if visitor.referenced.contains(&indexed) { + return; + } - let skip = if starts_at_zero { - String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start, cx) { - return; - } else { - format!(".skip({})", snippet(cx, start.span, "..")) - }; + let starts_at_zero = is_integer_const(cx, start, 0); - let mut end_is_start_plus_val = false; + let skip = if starts_at_zero { + String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start, cx) { + return; + } else { + format!(".skip({})", snippet(cx, start.span, "..")) + }; - let take = if let Some(end) = *end { - let mut take_expr = end; + let mut end_is_start_plus_val = false; - if let ExprKind::Binary(ref op, left, right) = end.kind { - if op.node == BinOpKind::Add { - let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); - let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); + let take = if let Some(end) = *end { + let mut take_expr = end; - if start_equal_left { - take_expr = right; - } else if start_equal_right { - take_expr = left; - } + if let ExprKind::Binary(ref op, left, right) = end.kind + && op.node == BinOpKind::Add + { + let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); + let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); - end_is_start_plus_val = start_equal_left | start_equal_right; - } + if start_equal_left { + take_expr = right; + } else if start_equal_right { + take_expr = left; } - if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { - String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr, cx) { - return; - } else { - match limits { - ast::RangeLimits::Closed => { - let take_expr = sugg::Sugg::hir(cx, take_expr, ""); - format!(".take({})", take_expr + sugg::ONE) - }, - ast::RangeLimits::HalfOpen => { - format!(".take({})", snippet(cx, take_expr.span, "..")) - }, - } - } - } else { - String::new() - }; + end_is_start_plus_val = start_equal_left | start_equal_right; + } - let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) { - ("mut ", "iter_mut") + if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { + String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr, cx) { + return; } else { - ("", "iter") - }; + match limits { + ast::RangeLimits::Closed => { + let take_expr = sugg::Sugg::hir(cx, take_expr, ""); + format!(".take({})", take_expr + sugg::ONE) + }, + ast::RangeLimits::HalfOpen => { + format!(".take({})", snippet(cx, take_expr.span, "..")) + }, + } + } + } else { + String::new() + }; - let take_is_empty = take.is_empty(); - let mut method_1 = take; - let mut method_2 = skip; + let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) { + ("mut ", "iter_mut") + } else { + ("", "iter") + }; - if end_is_start_plus_val { - mem::swap(&mut method_1, &mut method_2); - } + let take_is_empty = take.is_empty(); + let mut method_1 = take; + let mut method_2 = skip; - if visitor.nonindex { - span_lint_and_then( - cx, - NEEDLESS_RANGE_LOOP, - arg.span, - format!("the loop variable `{}` is used to index `{indexed}`", ident.name), - |diag| { - diag.multipart_suggestion( - "consider using an iterator and enumerate()", - vec![ - (pat.span, format!("({}, )", ident.name)), - ( - arg.span, - format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), - ), - ], - Applicability::HasPlaceholders, - ); - }, - ); + if end_is_start_plus_val { + mem::swap(&mut method_1, &mut method_2); + } + + if visitor.nonindex { + span_lint_and_then( + cx, + NEEDLESS_RANGE_LOOP, + arg.span, + format!("the loop variable `{}` is used to index `{indexed}`", ident.name), + |diag| { + diag.multipart_suggestion( + "consider using an iterator and enumerate()", + vec![ + (pat.span, format!("({}, )", ident.name)), + ( + arg.span, + format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), + ), + ], + Applicability::HasPlaceholders, + ); + }, + ); + } else { + let repl = if starts_at_zero && take_is_empty { + format!("&{ref_mut}{indexed}") } else { - let repl = if starts_at_zero && take_is_empty { - format!("&{ref_mut}{indexed}") - } else { - format!("{indexed}.{method}(){method_1}{method_2}") - }; + format!("{indexed}.{method}(){method_1}{method_2}") + }; - span_lint_and_then( - cx, - NEEDLESS_RANGE_LOOP, - arg.span, - format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), - |diag| { - diag.multipart_suggestion( - "consider using an iterator", - vec![(pat.span, "".to_string()), (arg.span, repl)], - Applicability::HasPlaceholders, - ); - }, - ); - } + span_lint_and_then( + cx, + NEEDLESS_RANGE_LOOP, + arg.span, + format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), + |diag| { + diag.multipart_suggestion( + "consider using an iterator", + vec![(pat.span, "".to_string()), (arg.span, repl)], + Applicability::HasPlaceholders, + ); + }, + ); } } } @@ -346,10 +345,10 @@ impl<'tcx> Visitor<'tcx> for VarVisitor<'_, '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 mutbl == Mutability::Mut { - self.prefer_mutable = true; - } + if let ty::Ref(_, _, mutbl) = *ty.kind() + && mutbl == Mutability::Mut + { + self.prefer_mutable = true; } self.visit_expr(expr); } @@ -361,10 +360,10 @@ impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> { iter::once(receiver).chain(args.iter()), ) { self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = *ty.kind() { - if mutbl == Mutability::Mut { - self.prefer_mutable = true; - } + if let ty::Ref(_, _, mutbl) = *ty.kind() + && mutbl == Mutability::Mut + { + self.prefer_mutable = true; } self.visit_expr(expr); } diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index b679fdfadc3a9..69c84bc7038ef 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -4,11 +4,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet; +use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use rustc_errors::Applicability; use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind, StructTailExpr}; use rustc_lint::LateContext; use rustc_span::{Span, sym}; use std::iter::once; +use std::ops::ControlFlow; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -24,17 +26,23 @@ pub(super) fn check<'tcx>( arg: iterator, pat, span: for_span, + label, .. }) = for_loop { - // Suggests using an `if let` instead. This is `Unspecified` because the - // loop may (probably) contain `break` statements which would be invalid - // in an `if let`. + // If the block contains a break or continue, or if the loop has a label, `MachineApplicable` is not + // appropriate. + let app = if !contains_any_break_or_continue(block) && label.is_none() { + Applicability::MachineApplicable + } else { + Applicability::Unspecified + }; + diag.span_suggestion_verbose( for_span.with_hi(iterator.span.hi()), "if you need the first element of the iterator, try writing", for_to_if_let_sugg(cx, iterator, pat), - Applicability::Unspecified, + app, ); } }); @@ -43,6 +51,15 @@ pub(super) fn check<'tcx>( } } +fn contains_any_break_or_continue(block: &Block<'_>) -> bool { + for_each_expr_without_closures(block, |e| match e.kind { + ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()), + ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), + _ => ControlFlow::Continue(Descend::Yes), + }) + .is_some() +} + /// The `never_loop` analysis keeps track of three things: /// /// * Has any (reachable) code path hit a `continue` of the main loop? @@ -160,6 +177,7 @@ fn never_loop_expr<'tcx>( | ExprKind::UnsafeBinderCast(_, e, _) => never_loop_expr(cx, e, local_labels, main_loop_id), ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, local_labels, main_loop_id), ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, es.iter(), local_labels, main_loop_id), + ExprKind::Use(expr, _) => never_loop_expr(cx, expr, local_labels, main_loop_id), ExprKind::MethodCall(_, receiver, es, _) => { never_loop_expr_all(cx, once(receiver).chain(es.iter()), local_labels, main_loop_id) }, @@ -226,10 +244,10 @@ fn never_loop_expr<'tcx>( }); combine_seq(first, || { // checks if break targets a block instead of a loop - if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind { - if let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) { - *reachable = true; - } + if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind + && let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) + { + *reachable = true; } NeverLoopResult::Diverging }) diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index 661b4b590d8fb..388034c39f522 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{msrvs, path_to_local, std_or_core}; +use clippy_utils::{msrvs, path_to_local, std_or_core, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -11,7 +11,6 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_span::SyntaxContext; -use rustc_span::symbol::sym; /// Detects for loop pushing the same item into a Vec pub(super) fn check<'tcx>( @@ -187,8 +186,8 @@ fn get_vec_push<'tcx>( // Extract method being called and figure out the parameters for the method call && let ExprKind::MethodCall(path, self_expr, [pushed_item], _) = &semi_stmt.kind // Check that the method being called is push() on a Vec + && path.ident.name == sym::push && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec) - && path.ident.name.as_str() == "push" { return Some((self_expr, pushed_item, semi_stmt.span.ctxt())); } diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs index a5185d38e7c33..2f6950b4380c3 100644 --- a/src/tools/clippy/clippy_lints/src/loops/utils.rs +++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs @@ -3,7 +3,7 @@ use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_loc use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr, walk_local}; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind}; +use rustc_hir::{AssignOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -58,7 +58,7 @@ impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> { match parent.kind { ExprKind::AssignOp(op, lhs, rhs) => { if lhs.hir_id == expr.hir_id { - *state = if op.node == BinOpKind::Add + *state = if op.node == AssignOpKind::AddAssign && is_integer_const(self.cx, rhs, 1) && *state == IncrementVisitorVarState::Initial && self.depth == 0 @@ -263,7 +263,7 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic if impls_iterator { format!( "{}", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() ) } else { // (&x).into_iter() ==> x.iter() @@ -281,12 +281,12 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic }; format!( "{}.{method_name}()", - sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_par(), + sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_paren(), ) }, _ => format!( "{}.into_iter()", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() ), } } diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs index 006addb987f5b..9071c9c95f9d7 100644 --- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -5,7 +5,8 @@ use itertools::Itertools; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; +use rustc_middle::lint::LevelAndSource; use rustc_session::impl_lint_pass; use rustc_span::{Span, SyntaxContext, sym}; use std::collections::BTreeMap; @@ -249,11 +250,25 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { }) .flatten() .copied() + .inspect(|&unsafe_block| { + if let LevelAndSource { + level: Level::Expect, + lint_id: Some(id), + .. + } = cx.tcx.lint_level_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block) + { + // Since we're going to deduplicate expanded unsafe blocks by its enclosing macro definition soon, + // which would lead to unfulfilled `#[expect()]`s in all other unsafe blocks that are filtered out + // except for the one we emit the warning at, we must manually fulfill the lint + // for all unsafe blocks here. + cx.fulfill_expectation(id); + } + }) .map(|id| { // Remove the syntax context to hide "in this macro invocation" in the diagnostic. // The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything // related to the callsite. - let span = cx.tcx.hir().span(id); + let span = cx.tcx.hir_span(id); (id, Span::new(span.lo(), span.hi(), SyntaxContext::root(), None)) }) diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index 20be22850b760..98ad1f6a160d8 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -98,7 +98,7 @@ impl LateLintPass<'_> for MacroUseImports { if cx.sess().opts.edition >= Edition::Edition2018 && let hir::ItemKind::Use(path, _kind) = &item.kind && let hir_id = item.hir_id() - && let attrs = cx.tcx.hir().attrs(hir_id) + && let attrs = cx.tcx.hir_attrs(hir_id) && let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)) && let Some(id) = path.res.iter().find_map(|res| match res { Res::Def(DefKind::Mod, id) => Some(id), @@ -153,9 +153,15 @@ impl LateLintPass<'_> for MacroUseImports { [] | [_] => return, [root, item] => { if !check_dup.contains(&(*item).to_string()) { - used.entry(((*root).to_string(), span, hir_id)) - .or_insert_with(Vec::new) - .push((*item).to_string()); + used.entry(( + (*root).to_string(), + span, + hir_id.local_id, + cx.tcx.def_path_hash(hir_id.owner.def_id.into()), + )) + .or_insert_with(|| (vec![], hir_id)) + .0 + .push((*item).to_string()); check_dup.push((*item).to_string()); } }, @@ -171,15 +177,27 @@ impl LateLintPass<'_> for MacroUseImports { } }) .collect::>(); - used.entry(((*root).to_string(), span, hir_id)) - .or_insert_with(Vec::new) - .push(filtered.join("::")); + used.entry(( + (*root).to_string(), + span, + hir_id.local_id, + cx.tcx.def_path_hash(hir_id.owner.def_id.into()), + )) + .or_insert_with(|| (vec![], hir_id)) + .0 + .push(filtered.join("::")); check_dup.extend(filtered); } else { let rest = rest.to_vec(); - used.entry(((*root).to_string(), span, hir_id)) - .or_insert_with(Vec::new) - .push(rest.join("::")); + used.entry(( + (*root).to_string(), + span, + hir_id.local_id, + cx.tcx.def_path_hash(hir_id.owner.def_id.into()), + )) + .or_insert_with(|| (vec![], hir_id)) + .0 + .push(rest.join("::")); check_dup.extend(rest.iter().map(ToString::to_string)); } }, @@ -190,7 +208,7 @@ impl LateLintPass<'_> for MacroUseImports { // If mac_refs is not empty we have encountered an import we could not handle // such as `std::prelude::v1::foo` or some other macro that expands to an import. if self.mac_refs.is_empty() { - for ((root, span, hir_id), path) in used { + for ((root, span, ..), (path, hir_id)) in used { let import = if let [single] = &path[..] { format!("{root}::{single}") } else { diff --git a/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs b/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs new file mode 100644 index 0000000000000..bac4b3d32f2a5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs @@ -0,0 +1,152 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::If; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::HasSession as _; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{eq_expr_value, peel_blocks, span_contains_comment}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::impl_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects patterns like `if a > b { a - b } else { b - a }` and suggests using `a.abs_diff(b)`. + /// + /// ### Why is this bad? + /// Using `abs_diff` is shorter, more readable, and avoids control flow. + /// + /// ### Examples + /// ```no_run + /// # let (a, b) = (5_usize, 3_usize); + /// if a > b { + /// a - b + /// } else { + /// b - a + /// } + /// # ; + /// ``` + /// Use instead: + /// ```no_run + /// # let (a, b) = (5_usize, 3_usize); + /// a.abs_diff(b) + /// # ; + /// ``` + #[clippy::version = "1.88.0"] + pub MANUAL_ABS_DIFF, + complexity, + "using an if-else pattern instead of `abs_diff`" +} + +impl_lint_pass!(ManualAbsDiff => [MANUAL_ABS_DIFF]); + +pub struct ManualAbsDiff { + msrv: Msrv, +} + +impl ManualAbsDiff { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} + +impl<'tcx> LateLintPass<'tcx> for ManualAbsDiff { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if !expr.span.from_expansion() + && let Some(if_expr) = If::hir(expr) + && let Some(r#else) = if_expr.r#else + && let ExprKind::Binary(op, rhs, lhs) = if_expr.cond.kind + && let (BinOpKind::Gt | BinOpKind::Ge, mut a, mut b) | (BinOpKind::Lt | BinOpKind::Le, mut b, mut a) = + (op.node, rhs, lhs) + && let Some(ty) = self.are_ty_eligible(cx, a, b) + && is_sub_expr(cx, if_expr.then, a, b, ty) + && is_sub_expr(cx, r#else, b, a, ty) + { + span_lint_and_then( + cx, + MANUAL_ABS_DIFF, + expr.span, + "manual absolute difference pattern without using `abs_diff`", + |diag| { + if is_unsuffixed_numeral_lit(a) && !is_unsuffixed_numeral_lit(b) { + (a, b) = (b, a); + } + let applicability = { + let source_map = cx.sess().source_map(); + if span_contains_comment(source_map, if_expr.then.span) + || span_contains_comment(source_map, r#else.span) + { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + } + }; + let sugg = format!( + "{}.abs_diff({})", + Sugg::hir(cx, a, "..").maybe_paren(), + Sugg::hir(cx, b, "..") + ); + diag.span_suggestion(expr.span, "replace with `abs_diff`", sugg, applicability); + }, + ); + } + } +} + +impl ManualAbsDiff { + /// Returns a type if `a` and `b` are both of it, and this lint can be applied to that + /// type (currently, any primitive int, or a `Duration`) + fn are_ty_eligible<'tcx>(&self, cx: &LateContext<'tcx>, a: &Expr<'_>, b: &Expr<'_>) -> Option> { + let is_int = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(_) | ty::Int(_)) && self.msrv.meets(cx, msrvs::ABS_DIFF); + let is_duration = + |ty| is_type_diagnostic_item(cx, ty, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); + + let a_ty = cx.typeck_results().expr_ty(a).peel_refs(); + (a_ty == cx.typeck_results().expr_ty(b).peel_refs() && (is_int(a_ty) || is_duration(a_ty))).then_some(a_ty) + } +} + +/// Checks if the given expression is a subtraction operation between two expected expressions, +/// i.e. if `expr` is `{expected_a} - {expected_b}`. +/// +/// If `expected_ty` is a signed primitive integer, this function will only return `Some` if the +/// subtraction expr is wrapped in a cast to the equivalent unsigned int. +fn is_sub_expr( + cx: &LateContext<'_>, + expr: &Expr<'_>, + expected_a: &Expr<'_>, + expected_b: &Expr<'_>, + expected_ty: Ty<'_>, +) -> bool { + let expr = peel_blocks(expr).kind; + + if let ty::Int(ty) = expected_ty.kind() { + let unsigned = Ty::new_uint(cx.tcx, ty.to_unsigned()); + + return if let ExprKind::Cast(expr, cast_ty) = expr + && cx.typeck_results().node_type(cast_ty.hir_id) == unsigned + { + is_sub_expr(cx, expr, expected_a, expected_b, unsigned) + } else { + false + }; + } + + if let ExprKind::Binary(op, a, b) = expr + && let BinOpKind::Sub = op.node + && eq_expr_value(cx, a, expected_a) + && eq_expr_value(cx, b, expected_b) + { + true + } else { + false + } +} + +fn is_unsuffixed_numeral_lit(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Lit(lit) if lit.node.is_numeric() && lit.node.is_unsuffixed()) +} diff --git a/src/tools/clippy/clippy_lints/src/manual_assert.rs b/src/tools/clippy/clippy_lints/src/manual_assert.rs index 83c16d4466d06..8378e15c581c6 100644 --- a/src/tools/clippy/clippy_lints/src/manual_assert.rs +++ b/src/tools/clippy/clippy_lints/src/manual_assert.rs @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { ExprKind::Unary(UnOp::Not, e) => (e, ""), _ => (cond, "!"), }; - let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); + let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_paren(); let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip}){semicolon}"); // we show to the user the suggestion without the comments, but when applying the fix, include the diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs index 39c4857b3e874..40fe88532729d 100644 --- a/src/tools/clippy/clippy_lints/src/manual_bits.rs +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -14,7 +14,7 @@ use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does - /// Checks for usage of `std::mem::size_of::() * 8` when + /// Checks for usage of `size_of::() * 8` when /// `T::BITS` is available. /// /// ### Why is this bad? @@ -22,7 +22,7 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// std::mem::size_of::() * 8; + /// size_of::() * 8; /// ``` /// Use instead: /// ```no_run @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { cx, MANUAL_BITS, expr.span, - "usage of `mem::size_of::()` to obtain the size of `T` in bits", + "usage of `size_of::()` to obtain the size of `T` in bits", "consider using", sugg, app, diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs index 50c8331eebab4..02afe9f0997de 100644 --- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs @@ -181,7 +181,7 @@ fn maybe_emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggest make_assignment, hir_with_ignore_attr, } = suggestion; - let input = Sugg::hir(cx, input, "..").maybe_par(); + let input = Sugg::hir(cx, input, "..").maybe_paren(); let min = Sugg::hir(cx, min, ".."); let max = Sugg::hir(cx, max, ".."); let semicolon = if make_assignment.is_some() { ";" } else { "" }; diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs index 9c1419175d55c..ed0cce754b954 100644 --- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs @@ -1,8 +1,9 @@ -use clippy_utils::SpanlessEq; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use clippy_utils::{SpanlessEq, sym}; use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -11,9 +12,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self}; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::symbol::Symbol; - -use clippy_config::Conf; declare_clippy_lint! { /// ### What it does @@ -64,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { && check_int_ty_and_feature(cx, div_lhs) && check_int_ty_and_feature(cx, div_rhs) && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind - && self.msrv.meets(cx, msrvs::MANUAL_DIV_CEIL) + && self.msrv.meets(cx, msrvs::DIV_CEIL) { // (x + (y - 1)) / y if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind @@ -141,8 +139,7 @@ fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr); match expr_ty.peel_refs().kind() { ty::Uint(_) => true, - ty::Int(_) => cx.tcx.features().enabled(Symbol::intern("int_roundings")), - + ty::Int(_) => cx.tcx.features().enabled(sym::int_roundings), _ => false, } } @@ -167,7 +164,7 @@ fn build_suggestion( rhs: &Expr<'_>, applicability: &mut Applicability, ) { - let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par(); + let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_paren(); let type_suffix = if cx.typeck_results().expr_ty(lhs).is_numeric() && matches!( lhs.kind, @@ -202,9 +199,9 @@ fn build_suggestion( } else { format!("{dividend_sugg_str}{type_suffix}") }; - let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability); + let divisor_snippet = snippet_with_context(cx, rhs.span, expr.span.ctxt(), "..", applicability); - let sugg = format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})"); + let sugg = format!("{suggestion_before_div_ceil}.div_ceil({})", divisor_snippet.0); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs index f71264a93ca84..b3ee45cc02098 100644 --- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -3,12 +3,11 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::visitors::{is_local_used, local_used_once}; -use clippy_utils::{is_trait_method, path_to_local_id}; +use clippy_utils::{is_trait_method, path_to_local_id, sym}; use rustc_errors::Applicability; use rustc_hir::{BindingMode, ExprKind, LetStmt, Node, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -66,7 +65,7 @@ impl LateLintPass<'_> for ManualHashOne { && let Some(init) = local.init && !init.span.from_expansion() && let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind - && seg.ident.name.as_str() == "build_hasher" + && seg.ident.name == sym::build_hasher && let Node::Stmt(local_stmt) = cx.tcx.parent_hir_node(local.hir_id) && let Node::Block(block) = cx.tcx.parent_hir_node(local_stmt.hir_id) @@ -94,7 +93,7 @@ impl LateLintPass<'_> for ManualHashOne { && let Node::Expr(finish_expr) = cx.tcx.parent_hir_node(path_expr.hir_id) && !finish_expr.span.from_expansion() && let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind - && seg.ident.name.as_str() == "finish" + && seg.ident.name == sym::finish && self.msrv.meets(cx, msrvs::BUILD_HASHER_HASH_ONE) { diff --git a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs index 506f4f6d9de1b..57c03fbb2ed2b 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs @@ -1,6 +1,7 @@ use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::{get_type_diagnostic_name, is_type_diagnostic_item, is_type_lang_item}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -10,7 +11,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::{Ty, UintTy}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -29,7 +30,7 @@ declare_clippy_lint! { /// Use instead: /// ```no_run /// fn compare(a: &str, b: &str) -> bool { - /// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc") + /// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc") /// } /// ``` #[clippy::version = "1.84.0"] @@ -47,9 +48,9 @@ enum MatchType<'a, 'b> { fn get_ascii_type<'a, 'b>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'b>) -> Option<(Span, MatchType<'a, 'b>)> { if let MethodCall(path, expr, _, _) = kind { - let is_lower = match path.ident.name.as_str() { - "to_ascii_lowercase" => true, - "to_ascii_uppercase" => false, + let is_lower = match path.ident.name { + sym::to_ascii_lowercase => true, + sym::to_ascii_uppercase => false, _ => return None, }; let ty_raw = cx.typeck_results().expr_ty(expr); diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index faf01a276a131..ac8c88f02057b 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators}; +use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators, sym}; use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -11,7 +11,7 @@ use rustc_hir::{Expr, ExprKind, Lit, Node, Param, PatExpr, PatExprKind, PatKind, use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { check_is_ascii(cx, macro_call.span, recv, &range, None); } } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind - && path.ident.name.as_str() == "contains" + && path.ident.name == sym::contains && let Some(higher::Range { start: Some(start), end: Some(end), @@ -148,7 +148,7 @@ fn check_is_ascii( }; let default_snip = ".."; let mut app = Applicability::MachineApplicable; - let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); + let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_paren(); let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))]; if let Some((ty_span, ty)) = ty_sugg { suggestion.push((ty_span, format!("{recv}: {ty}"))); diff --git a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs index 841adfec4624b..4439a28763a2f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs @@ -1,13 +1,14 @@ -use clippy_utils::SpanlessEq; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use rustc_ast::LitKind; -use rustc_data_structures::packed::Pu128; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::ty_from_hir_ty; +use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Uint; -use rustc_session::declare_lint_pass; +use rustc_middle::ty; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does @@ -33,112 +34,111 @@ declare_clippy_lint! { "manually reimplementing `is_power_of_two`" } -declare_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]); +pub struct ManualIsPowerOfTwo { + msrv: Msrv, +} -impl LateLintPass<'_> for ManualIsPowerOfTwo { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let mut applicability = Applicability::MachineApplicable; +impl_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]); - if let ExprKind::Binary(bin_op, left, right) = expr.kind - && bin_op.node == BinOpKind::Eq - { - // a.count_ones() == 1 - if let ExprKind::MethodCall(method_name, receiver, [], _) = left.kind - && method_name.ident.as_str() == "count_ones" - && let &Uint(_) = cx.typeck_results().expr_ty(receiver).kind() - && check_lit(right, 1) - { - build_sugg(cx, expr, receiver, &mut applicability); - } +impl ManualIsPowerOfTwo { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } - // 1 == a.count_ones() - if let ExprKind::MethodCall(method_name, receiver, [], _) = right.kind - && method_name.ident.as_str() == "count_ones" - && let &Uint(_) = cx.typeck_results().expr_ty(receiver).kind() - && check_lit(left, 1) - { - build_sugg(cx, expr, receiver, &mut applicability); - } + fn build_sugg(&self, cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { + if is_in_const_context(cx) && !self.msrv.meets(cx, msrvs::CONST_IS_POWER_OF_TWO) { + return; + } - // a & (a - 1) == 0 - if let ExprKind::Binary(op1, left1, right1) = left.kind - && op1.node == BinOpKind::BitAnd - && let ExprKind::Binary(op2, left2, right2) = right1.kind - && op2.node == BinOpKind::Sub - && check_eq_expr(cx, left1, left2) - && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() - && check_lit(right2, 1) - && check_lit(right, 0) - { - build_sugg(cx, expr, left1, &mut applicability); - } + let mut applicability = Applicability::MachineApplicable; + let snippet = Sugg::hir_with_applicability(cx, receiver, "_", &mut applicability); - // (a - 1) & a == 0; - if let ExprKind::Binary(op1, left1, right1) = left.kind - && op1.node == BinOpKind::BitAnd - && let ExprKind::Binary(op2, left2, right2) = left1.kind - && op2.node == BinOpKind::Sub - && check_eq_expr(cx, right1, left2) - && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind() - && check_lit(right2, 1) - && check_lit(right, 0) - { - build_sugg(cx, expr, right1, &mut applicability); - } + span_lint_and_sugg( + cx, + MANUAL_IS_POWER_OF_TWO, + expr.span, + "manually reimplementing `is_power_of_two`", + "consider using `.is_power_of_two()`", + format!("{}.is_power_of_two()", snippet.maybe_paren()), + applicability, + ); + } +} - // 0 == a & (a - 1); - if let ExprKind::Binary(op1, left1, right1) = right.kind - && op1.node == BinOpKind::BitAnd - && let ExprKind::Binary(op2, left2, right2) = right1.kind - && op2.node == BinOpKind::Sub - && check_eq_expr(cx, left1, left2) - && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() - && check_lit(right2, 1) - && check_lit(left, 0) +impl<'tcx> LateLintPass<'tcx> for ManualIsPowerOfTwo { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if !expr.span.from_expansion() + && let Some((lhs, rhs)) = unexpanded_binop_operands(expr, BinOpKind::Eq) + { + if let Some(a) = count_ones_receiver(cx, lhs) + && is_integer_literal(rhs, 1) { - build_sugg(cx, expr, left1, &mut applicability); - } - - // 0 == (a - 1) & a - if let ExprKind::Binary(op1, left1, right1) = right.kind - && op1.node == BinOpKind::BitAnd - && let ExprKind::Binary(op2, left2, right2) = left1.kind - && op2.node == BinOpKind::Sub - && check_eq_expr(cx, right1, left2) - && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind() - && check_lit(right2, 1) - && check_lit(left, 0) + self.build_sugg(cx, expr, a); + } else if let Some(a) = count_ones_receiver(cx, rhs) + && is_integer_literal(lhs, 1) + { + self.build_sugg(cx, expr, a); + } else if is_integer_literal(rhs, 0) + && let Some(a) = is_and_minus_one(cx, lhs) + { + self.build_sugg(cx, expr, a); + } else if is_integer_literal(lhs, 0) + && let Some(a) = is_and_minus_one(cx, rhs) { - build_sugg(cx, expr, right1, &mut applicability); + self.build_sugg(cx, expr, a); } } } } -fn build_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, applicability: &mut Applicability) { - let snippet = snippet_with_applicability(cx, receiver.span, "..", applicability); - - span_lint_and_sugg( - cx, - MANUAL_IS_POWER_OF_TWO, - expr.span, - "manually reimplementing `is_power_of_two`", - "consider using `.is_power_of_two()`", - format!("{snippet}.is_power_of_two()"), - *applicability, - ); +/// Return the unsigned integer receiver of `.count_ones()` or the argument of +/// `::count_ones(…)`. +fn count_ones_receiver<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + let (method, ty, receiver) = if let ExprKind::MethodCall(method_name, receiver, [], _) = expr.kind { + (method_name, cx.typeck_results().expr_ty_adjusted(receiver), receiver) + } else if let ExprKind::Call(func, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, func_name)) = func.kind + { + (func_name, ty_from_hir_ty(cx, ty), arg) + } else { + return None; + }; + (method.ident.name == sym::count_ones && matches!(ty.kind(), ty::Uint(_))).then_some(receiver) } -fn check_lit(expr: &Expr<'_>, expected_num: u128) -> bool { - if let ExprKind::Lit(lit) = expr.kind - && let LitKind::Int(Pu128(num), _) = lit.node - && num == expected_num +/// Return `greater` if `smaller == greater - 1` +fn is_one_less<'tcx>( + cx: &LateContext<'tcx>, + greater: &'tcx Expr<'tcx>, + smaller: &Expr<'tcx>, +) -> Option<&'tcx Expr<'tcx>> { + if let Some((lhs, rhs)) = unexpanded_binop_operands(smaller, BinOpKind::Sub) + && SpanlessEq::new(cx).eq_expr(greater, lhs) + && is_integer_literal(rhs, 1) + && matches!(cx.typeck_results().expr_ty_adjusted(greater).kind(), ty::Uint(_)) { - return true; + Some(greater) + } else { + None } - false } -fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool { - SpanlessEq::new(cx).eq_expr(lhs, rhs) +/// Return `v` if `expr` is `v & (v - 1)` or `(v - 1) & v` +fn is_and_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + let (lhs, rhs) = unexpanded_binop_operands(expr, BinOpKind::BitAnd)?; + is_one_less(cx, lhs, rhs).or_else(|| is_one_less(cx, rhs, lhs)) +} + +/// Return the operands of the `expr` binary operation if the operator is `op` and none of the +/// operands come from expansion. +fn unexpanded_binop_operands<'hir>(expr: &Expr<'hir>, op: BinOpKind) -> Option<(&'hir Expr<'hir>, &'hir Expr<'hir>)> { + if let ExprKind::Binary(binop, lhs, rhs) = expr.kind + && binop.node == op + && !lhs.span.from_expansion() + && !rhs.span.from_expansion() + { + Some((lhs, rhs)) + } else { + None + } } diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 47939767212ef..0b3bec714c0e1 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -4,10 +4,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use clippy_utils::{ + MaybePath, is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, path_res, peel_blocks, +}; +use rustc_ast::BindingMode; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::Span; @@ -90,14 +94,15 @@ impl<'tcx> QuestionMark { let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; + + let pat_arm = &arms[1 - idx]; // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement. // However, if it arrives in second position, its pattern may cover some cases already covered // by the diverging one. - // TODO: accept the non-diverging arm as a second position if patterns are disjointed. - if idx == 0 { + if idx == 0 && !is_arms_disjointed(cx, diverging_arm, pat_arm) { return; } - let pat_arm = &arms[1 - idx]; + let Some(ident_map) = expr_simple_identity_map(local.pat, pat_arm.pat, pat_arm.body) else { return; }; @@ -109,11 +114,68 @@ impl<'tcx> QuestionMark { } } +/// Checks if the patterns of the arms are disjointed. Currently, we only support patterns of simple +/// enum variants without nested patterns or bindings. +/// +/// TODO: Support more complex patterns. +fn is_arms_disjointed(cx: &LateContext<'_>, arm1: &Arm<'_>, arm2: &Arm<'_>) -> bool { + if arm1.guard.is_some() || arm2.guard.is_some() { + return false; + } + + if !is_enum_variant(cx, arm1.pat) || !is_enum_variant(cx, arm2.pat) { + return false; + } + + true +} + +/// Returns `true` if the given pattern is a variant of an enum. +pub fn is_enum_variant(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + struct Pat<'hir>(&'hir rustc_hir::Pat<'hir>); + + impl<'hir> MaybePath<'hir> for Pat<'hir> { + fn qpath_opt(&self) -> Option<&QPath<'hir>> { + match self.0.kind { + PatKind::Struct(ref qpath, fields, _) + if fields + .iter() + .all(|field| is_wild(field.pat) || matches!(field.pat.kind, PatKind::Binding(..))) => + { + Some(qpath) + }, + PatKind::TupleStruct(ref qpath, pats, _) + if pats + .iter() + .all(|pat| is_wild(pat) || matches!(pat.kind, PatKind::Binding(..))) => + { + Some(qpath) + }, + PatKind::Expr(&PatExpr { + kind: PatExprKind::Path(ref qpath), + .. + }) => Some(qpath), + _ => None, + } + } + + fn hir_id(&self) -> HirId { + self.0.hir_id + } + } + + let res = path_res(cx, &Pat(pat)); + matches!( + res, + Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) + ) +} + fn emit_manual_let_else( cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, - ident_map: &FxHashMap>, + ident_map: &FxHashMap, BindingMode)>, pat: &Pat<'_>, else_body: &Expr<'_>, ) { @@ -167,7 +229,7 @@ fn emit_manual_let_else( fn replace_in_pattern( cx: &LateContext<'_>, span: Span, - ident_map: &FxHashMap>, + ident_map: &FxHashMap, BindingMode)>, pat: &Pat<'_>, app: &mut Applicability, top_level: bool, @@ -185,15 +247,16 @@ fn replace_in_pattern( match pat.kind { PatKind::Binding(_ann, _id, binding_name, opt_subpt) => { - let Some(pat_to_put) = ident_map.get(&binding_name.name) else { + let Some((pat_to_put, binding_mode)) = ident_map.get(&binding_name.name) else { break 'a; }; + let sn_pfx = binding_mode.prefix_str(); let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); if let Some(subpt) = opt_subpt { let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); - return format!("{sn_ptp} @ {subpt}"); + return format!("{sn_pfx}{sn_ptp} @ {subpt}"); } - return sn_ptp.to_string(); + return format!("{sn_pfx}{sn_ptp}"); }, PatKind::Or(pats) => { let patterns = pats @@ -211,17 +274,18 @@ fn replace_in_pattern( .iter() .map(|fld| { if let PatKind::Binding(_, _, name, None) = fld.pat.kind - && let Some(pat_to_put) = ident_map.get(&name.name) + && let Some((pat_to_put, binding_mode)) = ident_map.get(&name.name) { + let sn_pfx = binding_mode.prefix_str(); let (sn_fld_name, _) = snippet_with_context(cx, fld.ident.span, span.ctxt(), "", app); let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); // TODO: this is a bit of a hack, but it does its job. Ideally, we'd check if pat_to_put is // a PatKind::Binding but that is also hard to get right. if sn_fld_name == sn_ptp { // Field init shorthand - return format!("{sn_fld_name}"); + return format!("{sn_pfx}{sn_fld_name}"); } - return format!("{sn_fld_name}: {sn_ptp}"); + return format!("{sn_fld_name}: {sn_pfx}{sn_ptp}"); } let (sn_fld, _) = snippet_with_context(cx, fld.span, span.ctxt(), "", app); sn_fld.into_owned() @@ -334,7 +398,7 @@ fn expr_simple_identity_map<'a, 'hir>( local_pat: &'a Pat<'hir>, let_pat: &'_ Pat<'hir>, expr: &'_ Expr<'hir>, -) -> Option>> { +) -> Option, BindingMode)>> { let peeled = peel_blocks(expr); let (sub_pats, paths) = match (local_pat.kind, peeled.kind) { (PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) | (PatKind::Slice(pats, ..), ExprKind::Array(exprs)) => { @@ -351,9 +415,9 @@ fn expr_simple_identity_map<'a, 'hir>( return None; } - let mut pat_bindings = FxHashSet::default(); - let_pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| { - pat_bindings.insert(ident); + let mut pat_bindings = FxHashMap::default(); + let_pat.each_binding_or_first(&mut |binding_mode, _hir_id, _sp, ident| { + pat_bindings.insert(ident, binding_mode); }); if pat_bindings.len() < paths.len() { // This rebinds some bindings from the outer scope, or it repeats some copy-able bindings multiple @@ -366,12 +430,10 @@ fn expr_simple_identity_map<'a, 'hir>( for (sub_pat, path) in sub_pats.iter().zip(paths.iter()) { if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind && let [path_seg] = path.segments + && let ident = path_seg.ident + && let Some(let_binding_mode) = pat_bindings.remove(&ident) { - let ident = path_seg.ident; - if !pat_bindings.remove(&ident) { - return None; - } - ident_map.insert(ident.name, sub_pat); + ident_map.insert(ident.name, (sub_pat, let_binding_mode)); } else { return None; } 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 496e0660d4f9a..067b92cd46eed 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -87,18 +87,18 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { } match item.kind { - ItemKind::Enum(def, _) if def.variants.len() > 1 => { + ItemKind::Enum(_, def, _) if def.variants.len() > 1 => { let iter = def.variants.iter().filter_map(|v| { - (matches!(v.data, VariantData::Unit(_, _)) && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))) + (matches!(v.data, VariantData::Unit(_, _)) && is_doc_hidden(cx.tcx.hir_attrs(v.hir_id))) .then_some((v.def_id, v.span)) }); if let Ok((id, span)) = iter.exactly_one() - && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive) + && !attr::contains_name(cx.tcx.hir_attrs(item.hir_id()), sym::non_exhaustive) { self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); } }, - ItemKind::Struct(variant_data, _) => { + ItemKind::Struct(_, variant_data, _) => { let fields = variant_data.fields(); let private_fields = fields .iter() @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { "this seems like a manual implementation of the non-exhaustive pattern", |diag| { if let Some(non_exhaustive) = - attr::find_by_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive) + attr::find_by_name(cx.tcx.hir_attrs(item.hir_id()), sym::non_exhaustive) { diag.span_note(non_exhaustive.span(), "the struct is already non-exhaustive"); } else { diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs index 8dee29b2a0b5d..b55c11f2d5b69 100644 --- a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs +++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs @@ -1,14 +1,14 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; -use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs}; +use clippy_utils::{is_none_arm, msrvs, paths, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -34,7 +34,7 @@ declare_clippy_lint! { /// _ = opt.as_slice(); /// _ = opt.as_slice(); /// ``` - #[clippy::version = "1.85.0"] + #[clippy::version = "1.86.0"] pub MANUAL_OPTION_AS_SLICE, complexity, "manual `Option::as_slice`" @@ -76,30 +76,30 @@ impl LateLintPass<'_> for ManualOptionAsSlice { } }, ExprKind::MethodCall(seg, callee, [], _) => { - if seg.ident.name.as_str() == "unwrap_or_default" { + if seg.ident.name == sym::unwrap_or_default { check_map(cx, callee, span, self.msrv); } }, - ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name.as_str() { - "unwrap_or" => { + ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name { + sym::unwrap_or => { if is_empty_slice(cx, or) { check_map(cx, callee, span, self.msrv); } }, - "unwrap_or_else" => { + sym::unwrap_or_else => { if returns_empty_slice(cx, or) { check_map(cx, callee, span, self.msrv); } }, _ => {}, }, - ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name.as_str() { - "map_or" => { + ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name { + sym::map_or => { if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { check_as_ref(cx, callee, span, self.msrv); } }, - "map_or_else" => { + sym::map_or_else => { if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { check_as_ref(cx, callee, span, self.msrv); } @@ -220,5 +220,5 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - clippy_utils::is_expr_path_def_path(cx, expr, &["core", "slice", "raw", "from_ref"]) + paths::SLICE_FROM_REF.matches_path(cx, expr) } diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs index 16dd1ad4e4784..98e8b1f5cf92c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_retain.rs +++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs @@ -92,10 +92,10 @@ fn check_into_iter( && let [filter_params] = filter_body.params { if match_map_type(cx, left_expr) { - if let hir::PatKind::Tuple([key_pat, value_pat], _) = filter_params.pat.kind { - if let Some(sugg) = make_sugg(cx, key_pat, value_pat, left_expr, filter_body) { - make_span_lint_and_sugg(cx, parent_expr_span, sugg); - } + if let hir::PatKind::Tuple([key_pat, value_pat], _) = filter_params.pat.kind + && let Some(sugg) = make_sugg(cx, key_pat, value_pat, left_expr, filter_body) + { + make_span_lint_and_sugg(cx, parent_expr_span, sugg); } // Cannot lint other cases because `retain` requires two parameters } else { @@ -196,22 +196,21 @@ fn check_to_owned( && let filter_body = cx.tcx.hir_body(closure.body) && let [filter_params] = filter_body.params && msrv.meets(cx, msrvs::STRING_RETAIN) + && let hir::PatKind::Ref(pat, _) = filter_params.pat.kind { - if let hir::PatKind::Ref(pat, _) = filter_params.pat.kind { - make_span_lint_and_sugg( - cx, - parent_expr_span, - format!( - "{}.retain(|{}| {})", - snippet(cx, left_expr.span, ".."), - snippet(cx, pat.span, ".."), - snippet(cx, filter_body.value.span, "..") - ), - ); - } - // Be conservative now. Do nothing for the `Binding` case. - // TODO: Ideally, we can rewrite the lambda by stripping one level of reference + make_span_lint_and_sugg( + cx, + parent_expr_span, + format!( + "{}.retain(|{}| {})", + snippet(cx, left_expr.span, ".."), + snippet(cx, pat.span, ".."), + snippet(cx, filter_body.value.span, "..") + ), + ); } + // Be conservative now. Do nothing for the `Binding` case. + // TODO: Ideally, we can rewrite the lambda by stripping one level of reference } fn make_sugg( diff --git a/src/tools/clippy/clippy_lints/src/manual_rotate.rs b/src/tools/clippy/clippy_lints/src/manual_rotate.rs index 07537fc65c08c..06ee00c2cef3c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rotate.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rotate.rs @@ -101,7 +101,7 @@ impl LateLintPass<'_> for ManualRotate { (r_shift_dir, r_amount) }; let mut applicability = Applicability::MachineApplicable; - let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_par(); + let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); span_lint_and_sugg( cx, MANUAL_ROTATE, diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs index 18901f7399d96..0c09a47c96518 100644 --- a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs +++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs @@ -1,11 +1,13 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::{expr_or_init, is_in_const_context, std_or_core}; 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; +use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; declare_clippy_lint! { @@ -24,32 +26,45 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// # let data : &[i32] = &[1, 2, 3]; - /// let newlen = data.len() * std::mem::size_of::(); + /// let newlen = data.len() * size_of::(); /// ``` /// Use instead: /// ```no_run /// # let data : &[i32] = &[1, 2, 3]; - /// let newlen = std::mem::size_of_val(data); + /// let newlen = size_of_val(data); /// ``` #[clippy::version = "1.70.0"] pub MANUAL_SLICE_SIZE_CALCULATION, complexity, "manual slice size calculation" } -declare_lint_pass!(ManualSliceSizeCalculation => [MANUAL_SLICE_SIZE_CALCULATION]); +impl_lint_pass!(ManualSliceSizeCalculation => [MANUAL_SLICE_SIZE_CALCULATION]); + +pub struct ManualSliceSizeCalculation { + msrv: Msrv, +} + +impl ManualSliceSizeCalculation { + pub fn new(conf: &Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Binary(ref op, left, right) = expr.kind && BinOpKind::Mul == op.node && !expr.span.from_expansion() - // Does not apply inside const because size_of_val is not cost in stable. - && !is_in_const_context(cx) && let Some((receiver, refs_count)) = simplify(cx, left, right) + && (!is_in_const_context(cx) || self.msrv.meets(cx, msrvs::CONST_SIZE_OF_VAL)) { let ctxt = expr.span.ctxt(); let mut app = Applicability::MachineApplicable; - let deref = "*".repeat(refs_count - 1); + let deref = if refs_count > 0 { + "*".repeat(refs_count - 1) + } else { + "&".into() + }; let val_name = snippet_with_context(cx, receiver.span, ctxt, "slice", &mut app).0; let Some(sugg) = std_or_core(cx) else { return }; diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs index 5c2a711b5cb23..7ca3b71206671 100644 --- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs +++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs @@ -113,15 +113,14 @@ fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, arg: &Expr<'_>) && is_expr_kind_empty_str(&arg.kind) { warn_then_suggest(cx, span); - } else if let QPath::Resolved(_, path) = qpath { + } else if let QPath::Resolved(_, path) = qpath // From::from(...) or TryFrom::try_from(...) - if let [path_seg1, path_seg2] = path.segments - && is_expr_kind_empty_str(&arg.kind) - && ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) - || (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)) - { - warn_then_suggest(cx, span); - } + && let [path_seg1, path_seg2] = path.segments + && is_expr_kind_empty_str(&arg.kind) + && ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) + || (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)) + { + warn_then_suggest(cx, span); } } } diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs deleted file mode 100644 index 87d2faa225c52..0000000000000 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs +++ /dev/null @@ -1,212 +0,0 @@ -use rustc_errors::Applicability; -use rustc_hir::def::Res; -use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::GenericArgKind; -use rustc_session::declare_lint_pass; -use rustc_span::sym; - -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::IfLetOrMatch; -use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{expr_type_is_certain, implements_trait}; -use clippy_utils::{is_default_equivalent, is_in_const_context, path_res, peel_blocks, span_contains_comment}; - -declare_clippy_lint! { - /// ### What it does - /// Checks if a `match` or `if let` expression can be simplified using - /// `.unwrap_or_default()`. - /// - /// ### Why is this bad? - /// It can be done in one call with `.unwrap_or_default()`. - /// - /// ### Example - /// ```no_run - /// let x: Option = Some(String::new()); - /// let y: String = match x { - /// Some(v) => v, - /// None => String::new(), - /// }; - /// - /// let x: Option> = Some(Vec::new()); - /// let y: Vec = if let Some(v) = x { - /// v - /// } else { - /// Vec::new() - /// }; - /// ``` - /// Use instead: - /// ```no_run - /// let x: Option = Some(String::new()); - /// let y: String = x.unwrap_or_default(); - /// - /// let x: Option> = Some(Vec::new()); - /// let y: Vec = x.unwrap_or_default(); - /// ``` - #[clippy::version = "1.79.0"] - pub MANUAL_UNWRAP_OR_DEFAULT, - suspicious, - "check if a `match` or `if let` can be simplified with `unwrap_or_default`" -} - -declare_lint_pass!(ManualUnwrapOrDefault => [MANUAL_UNWRAP_OR_DEFAULT]); - -fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option { - if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = pat.kind - && let PatKind::Binding(_, pat_id, _, _) = pat.kind - && let Some(def_id) = path.res.opt_def_id() - // Since it comes from a pattern binding, we need to get the parent to actually match - // against it. - && let Some(def_id) = cx.tcx.opt_parent(def_id) - && (cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id) - || cx.tcx.lang_items().get(LangItem::ResultOk) == Some(def_id)) - { - Some(pat_id) - } else { - None - } -} - -fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.pat.kind - && let Some(def_id) = path.res.opt_def_id() - // Since it comes from a pattern binding, we need to get the parent to actually match - // against it. - && let Some(def_id) = cx.tcx.opt_parent(def_id) - && cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id) - { - Some(arm.body) - } else if let PatKind::TupleStruct(QPath::Resolved(_, path), _, _)= arm.pat.kind - && let Some(def_id) = path.res.opt_def_id() - // Since it comes from a pattern binding, we need to get the parent to actually match - // against it. - && let Some(def_id) = cx.tcx.opt_parent(def_id) - && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id) - { - Some(arm.body) - } else if let PatKind::Wild = arm.pat.kind { - // We consider that the `Some` check will filter it out if it's not right. - Some(arm.body) - } else { - None - } -} - -fn get_some_and_none_bodies<'tcx>( - cx: &LateContext<'tcx>, - arm1: &'tcx Arm<'tcx>, - arm2: &'tcx Arm<'tcx>, -) -> Option<((&'tcx Expr<'tcx>, HirId), &'tcx Expr<'tcx>)> { - if let Some(binding_id) = get_some(cx, arm1.pat) - && let Some(body_none) = get_none(cx, arm2) - { - Some(((arm1.body, binding_id), body_none)) - } else if let Some(binding_id) = get_some(cx, arm2.pat) - && let Some(body_none) = get_none(cx, arm1) - { - Some(((arm2.body, binding_id), body_none)) - } else { - None - } -} - -#[allow(clippy::needless_pass_by_value)] -fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, expr: &'tcx Expr<'tcx>) { - // Get expr_name ("if let" or "match" depending on kind of expression), the condition, the body for - // the some arm, the body for the none arm and the binding id of the some arm - let (expr_name, condition, body_some, body_none, binding_id) = match if_let_or_match { - IfLetOrMatch::Match(condition, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar) - // Make sure there are no guards to keep things simple - if arm1.guard.is_none() - && arm2.guard.is_none() - // Get the some and none bodies and the binding id of the some arm - && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) => - { - ("match", condition, body_some, body_none, binding_id) - }, - IfLetOrMatch::IfLet(condition, pat, if_expr, Some(else_expr), _) - if let Some(binding_id) = get_some(cx, pat) => - { - ("if let", condition, if_expr, else_expr, binding_id) - }, - _ => { - // All other cases (match with number of arms != 2, if let without else, etc.) - return; - }, - }; - - // We check if the return type of the expression implements Default. - let expr_type = cx.typeck_results().expr_ty(expr); - if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) - && implements_trait(cx, expr_type, default_trait_id, &[]) - // We check if the initial condition implements Default. - && let Some(condition_ty) = cx.typeck_results().expr_ty(condition).walk().nth(1) - && let GenericArgKind::Type(condition_ty) = condition_ty.unpack() - && implements_trait(cx, condition_ty, default_trait_id, &[]) - // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. - && let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(body_some).kind - && let Res::Local(local_id) = path.res - && local_id == binding_id - // We now check the `None` arm is calling a method equivalent to `Default::default`. - && let body_none = peel_blocks(body_none) - && is_default_equivalent(cx, body_none) - && let Some(receiver) = Sugg::hir_opt(cx, condition).map(Sugg::maybe_par) - { - // Machine applicable only if there are no comments present - let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { - Applicability::MaybeIncorrect - } else { - Applicability::MachineApplicable - }; - - // We now check if the condition is a None variant, in which case we need to specify the type - if path_res(cx, condition) - .opt_def_id() - .is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant()) - { - return span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR_DEFAULT, - expr.span, - format!("{expr_name} can be simplified with `.unwrap_or_default()`"), - "replace it with", - format!("{receiver}::<{expr_type}>.unwrap_or_default()"), - applicability, - ); - } - - // We check if the expression type is still uncertain, in which case we ask the user to specify it - if !expr_type_is_certain(cx, condition) { - return span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR_DEFAULT, - expr.span, - format!("{expr_name} can be simplified with `.unwrap_or_default()`"), - format!("ascribe the type {expr_type} and replace your expression with"), - format!("{receiver}.unwrap_or_default()"), - Applicability::Unspecified, - ); - } - - span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR_DEFAULT, - expr.span, - format!("{expr_name} can be simplified with `.unwrap_or_default()`"), - "replace it with", - format!("{receiver}.unwrap_or_default()"), - applicability, - ); - } -} - -impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr) - && !expr.span.from_expansion() - && !is_in_const_context(cx) - { - handle(cx, if_let_or_match, expr); - } - } -} 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 56aead85e7c41..af6a1b07a4929 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -101,10 +101,10 @@ 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 Some(fn_type) = cx.tcx.fn_sig(id).instantiate_identity().no_bound_vars() { - return is_unit_type(fn_type.output()); - } + if let ty::FnDef(id, _) = *ty.kind() + && let Some(fn_type) = cx.tcx.fn_sig(id).instantiate_identity().no_bound_vars() + { + return is_unit_type(fn_type.output()); } false } @@ -255,7 +255,7 @@ impl LateLintPass<'_> for MapUnit { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) { if let hir::StmtKind::Semi(expr) = stmt.kind && !stmt.span.from_expansion() - && let Some(arglists) = method_chain_args(expr, &["map"]) + && let Some(arglists) = method_chain_args(expr, &[sym::map]) { lint_map_unit_fn(cx, stmt, expr, arglists[0]); } diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index 6f446bf956587..5b50efad3e44e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet; @@ -99,7 +99,7 @@ fn check_arm<'tcx>( } else { String::new() }; - span_lint_and_then(cx, COLLAPSIBLE_MATCH, inner_expr.span, msg, |diag| { + span_lint_hir_and_then(cx, COLLAPSIBLE_MATCH, inner_expr.hir_id, inner_expr.span, msg, |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding"); help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs index 4cc43e427ec61..abf723fa6f4ca 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs @@ -41,10 +41,10 @@ fn get_cond_expr<'tcx>( fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { // we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's // checked by `contains_unsafe_block` - if let ExprKind::Block(block, None) = expr.kind { - if block.stmts.is_empty() { - return block.expr; - } + if let ExprKind::Block(block, None) = expr.kind + && block.stmts.is_empty() + { + return block.expr; } None } @@ -61,13 +61,13 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { // } // Returns true if resolves to `Some(x)`, `false` otherwise fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &Expr<'_>) -> bool { - if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) // there can be not statements in the block as they would be removed when switching to `.filter` - if let ExprKind::Call(callee, [arg]) = inner_expr.kind { - return ctxt == expr.span.ctxt() - && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) - && path_to_local_id(arg, target); - } + && let ExprKind::Call(callee, [arg]) = inner_expr.kind + { + return ctxt == expr.span.ctxt() + && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) + && path_to_local_id(arg, target); } false } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs index 576e42a564c2b..4959908dad635 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs @@ -85,7 +85,7 @@ fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool /// contains `Err(IDENT)`, `None` otherwise. fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &'hir Ident)> { if let PatKind::TupleStruct(qpath, [arg], _) = &pat.kind - && let PatKind::Binding(BindingMode::NONE, _, ident, _) = &arg.kind + && let PatKind::Binding(BindingMode::NONE, _, ident, None) = &arg.kind && let res = cx.qpath_res(qpath, pat.hir_id) && let Res::Def(DefKind::Ctor(..), id) = res && let id @ Some(_) = cx.tcx.opt_parent(id) @@ -132,7 +132,7 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok } else { Applicability::MachineApplicable }; - let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_par(); + let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren(); let sugg = format!("{scrut}.{method}()"); // If the expression being expanded is the `if …` part of an `else if …`, it must be blockified. let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index 2bf7ec8ab7dde..3ac2c9fc2b361 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,133 +1,220 @@ use clippy_utils::consts::ConstEvalCtxt; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg}; +use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; +use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, ResultErr}; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, Pat, PatExpr, PatExprKind, PatKind}; -use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_hir::def::Res; +use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, QPath}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::{GenericArgKind, Ty}; use rustc_span::sym; -use super::MANUAL_UNWRAP_OR; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::{expr_type_is_certain, get_type_diagnostic_name, implements_trait}; +use clippy_utils::{is_default_equivalent, is_lint_allowed, path_res, peel_blocks, span_contains_comment}; -pub(super) fn check_match<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, - scrutinee: &'tcx Expr<'_>, - arms: &'tcx [Arm<'_>], -) { - let ty = cx.typeck_results().expr_ty(scrutinee); - if let Some((or_arm, unwrap_arm)) = applicable_or_arm(cx, arms) { - check_and_lint(cx, expr, unwrap_arm.pat, scrutinee, unwrap_arm.body, or_arm.body, ty); +use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; + +fn get_some(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { + if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = pat.kind + && let PatKind::Binding(BindingMode(ByRef::No, _), pat_id, _, _) = pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && let Some(lang_item) = cx.tcx.lang_items().from_def_id(def_id) + && matches!(lang_item, LangItem::OptionSome | LangItem::ResultOk) + { + Some(pat_id) + } else { + None } } -pub(super) fn check_if_let<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - let_pat: &'tcx Pat<'_>, - let_expr: &'tcx Expr<'_>, - then_expr: &'tcx Expr<'_>, - else_expr: &'tcx Expr<'_>, -) { - let ty = cx.typeck_results().expr_ty(let_expr); - let then_ty = cx.typeck_results().expr_ty(then_expr); - // The signature is `fn unwrap_or(self: Option, default: T) -> T`. - // When `expr_adjustments(then_expr).is_empty()`, `T` should equate to `default`'s type. - // Otherwise, type error will occur. - if cx.typeck_results().expr_adjustments(then_expr).is_empty() - && let rustc_middle::ty::Adt(_did, args) = ty.kind() - && let Some(some_ty) = args.first().and_then(|arg| arg.as_type()) - && some_ty != then_ty +fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id) { - return; + Some(arm.body) + } else if let PatKind::TupleStruct(QPath::Resolved(_, path), _, _)= arm.pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id) + { + Some(arm.body) + } else if let PatKind::Wild = arm.pat.kind { + // We consider that the `Some` check will filter it out if it's not right. + Some(arm.body) + } else { + None } - check_and_lint(cx, expr, let_pat, let_expr, then_expr, peel_blocks(else_expr), ty); } -fn check_and_lint<'tcx>( +fn get_some_and_none_bodies<'tcx>( cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - let_pat: &'tcx Pat<'_>, - let_expr: &'tcx Expr<'_>, - then_expr: &'tcx Expr<'_>, - else_expr: &'tcx Expr<'_>, - ty: Ty<'tcx>, + arm1: &'tcx Arm<'tcx>, + arm2: &'tcx Arm<'tcx>, +) -> Option<((&'tcx Expr<'tcx>, HirId), &'tcx Expr<'tcx>)> { + if let Some(binding_id) = get_some(cx, arm1.pat) + && let Some(body_none) = get_none(cx, arm2) + { + Some(((arm1.body, binding_id), body_none)) + } else if let Some(binding_id) = get_some(cx, arm2.pat) + && let Some(body_none) = get_none(cx, arm1) + { + Some(((arm2.body, binding_id), body_none)) + } else { + None + } +} + +fn handle( + cx: &LateContext<'_>, + expr: &Expr<'_>, + expr_name: &'static str, + condition: &Expr<'_>, + body_some: &Expr<'_>, + body_none: &Expr<'_>, + binding_id: HirId, ) { - if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = let_pat.kind - && let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id) - && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) - && (cx.tcx.lang_items().option_some_variant() == Some(variant_id) - || cx.tcx.lang_items().result_ok_variant() == Some(variant_id)) - && let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind - && path_to_local_id(peel_blocks(then_expr), binding_hir_id) - && cx.typeck_results().expr_adjustments(then_expr).is_empty() - && let Some(ty_name) = find_type_name(cx, ty) - && let Some(or_body_snippet) = else_expr.span.get_source_text(cx) - && let Some(indent) = indent_of(cx, expr.span) - && ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some() + // Only deal with situations where both alternatives return the same non-adjusted type. + if cx.typeck_results().expr_ty(body_some) != cx.typeck_results().expr_ty(body_none) { + return; + } + + let expr_type = cx.typeck_results().expr_ty(expr); + // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. + if let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(body_some).kind + && let Res::Local(local_id) = path.res + && local_id == binding_id { - lint(cx, expr, let_expr, ty_name, &or_body_snippet, indent); + // Machine applicable only if there are no comments present + let mut applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + let receiver = Sugg::hir_with_applicability(cx, condition, "_", &mut applicability).maybe_paren(); + + // We now check the `None` arm is calling a method equivalent to `Default::default`. + if !is_lint_allowed(cx, MANUAL_UNWRAP_OR_DEFAULT, expr.hir_id) + // We check if the return type of the expression implements Default. + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + && implements_trait(cx, expr_type, default_trait_id, &[]) + // We check if the initial condition implements Default. + && let Some(condition_ty) = cx.typeck_results().expr_ty(condition).walk().nth(1) + && let GenericArgKind::Type(condition_ty) = condition_ty.unpack() + && implements_trait(cx, condition_ty, default_trait_id, &[]) + && is_default_equivalent(cx, peel_blocks(body_none)) + { + // We now check if the condition is a None variant, in which case we need to specify the type + if path_res(cx, condition) + .opt_def_id() + .is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant()) + { + return span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + "replace it with", + format!("{receiver}::<{expr_type}>.unwrap_or_default()"), + applicability, + ); + } + + // We check if the expression type is still uncertain, in which case we ask the user to specify it + if !expr_type_is_certain(cx, condition) { + return span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + format!("ascribe the type {expr_type} and replace your expression with"), + format!("{receiver}.unwrap_or_default()"), + Applicability::Unspecified, + ); + } + + span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + "replace it with", + format!("{receiver}.unwrap_or_default()"), + applicability, + ); + } else if let Some(ty_name) = find_type_name(cx, cx.typeck_results().expr_ty(condition)) + && cx.typeck_results().expr_adjustments(body_some).is_empty() + && let Some(or_body_snippet) = peel_blocks(body_none).span.get_source_text(cx) + && let Some(indent) = indent_of(cx, expr.span) + && ConstEvalCtxt::new(cx).eval_simple(body_none).is_some() + { + let reindented_or_body = reindent_multiline(&or_body_snippet, true, Some(indent)); + let mut app = Applicability::MachineApplicable; + let suggestion = Sugg::hir_with_context(cx, condition, expr.span.ctxt(), "..", &mut app).maybe_paren(); + span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR, + expr.span, + format!("this pattern reimplements `{ty_name}::unwrap_or`"), + "replace with", + format!("{suggestion}.unwrap_or({reindented_or_body})",), + app, + ); + } } } fn find_type_name<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'static str> { - if is_type_diagnostic_item(cx, ty, sym::Option) { - Some("Option") - } else if is_type_diagnostic_item(cx, ty, sym::Result) { - Some("Result") - } else { - None + match get_type_diagnostic_name(cx, ty)? { + sym::Option => Some("Option"), + sym::Result => Some("Result"), + _ => None, } } -fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<(&'a Arm<'a>, &'a Arm<'a>)> { - if arms.len() == 2 - && arms.iter().all(|arm| arm.guard.is_none()) - && let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| match arm.pat.kind { - PatKind::Expr(PatExpr { - hir_id, - kind: PatExprKind::Path(qpath), - .. - }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), - PatKind::TupleStruct(ref qpath, [pat], _) => { - matches!(pat.kind, PatKind::Wild) - && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) - }, - _ => false, - }) - && let unwrap_arm = &arms[1 - idx] - && !contains_return_break_continue_macro(or_arm.body) +pub fn check_match<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + scrutinee: &'tcx Expr<'tcx>, + arms: &'tcx [Arm<'tcx>], +) { + if let [arm1, arm2] = arms + // Make sure there are no guards to keep things simple + && arm1.guard.is_none() + && arm2.guard.is_none() + // Get the some and none bodies and the binding id of the some arm + && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) { - Some((or_arm, unwrap_arm)) - } else { - None + handle(cx, expr, "match", scrutinee, body_some, body_none, binding_id); } } -fn lint<'tcx>( +pub fn check_if_let<'tcx>( cx: &LateContext<'tcx>, - expr: &Expr<'tcx>, - scrutinee: &'tcx Expr<'_>, - ty_name: &str, - or_body_snippet: &str, - indent: usize, + expr: &'tcx Expr<'tcx>, + pat: &'tcx Pat<'tcx>, + scrutinee: &'tcx Expr<'tcx>, + then_expr: &'tcx Expr<'tcx>, + else_expr: &'tcx Expr<'tcx>, ) { - let reindented_or_body = reindent_multiline(or_body_snippet, true, Some(indent)); - - let mut app = Applicability::MachineApplicable; - let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par(); - span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR, - expr.span, - format!("this pattern reimplements `{ty_name}::unwrap_or`"), - "replace with", - format!("{suggestion}.unwrap_or({reindented_or_body})",), - app, - ); + if let Some(binding_id) = get_some(cx, pat) { + handle( + cx, + expr, + "if let", + scrutinee, + peel_blocks(then_expr), + peel_blocks(else_expr), + binding_id, + ); + } } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index 09440c396ee16..d0905733ab506 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -99,7 +99,7 @@ where }); if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, + Some(CaptureKind::Value | CaptureKind::Use | CaptureKind::Ref(Mutability::Mut)) => return None, Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { return None; }, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index d697f427c7052..f14b69d91ce4b 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -42,7 +42,7 @@ pub(super) fn check_match<'tcx>( cx, scrutinee, arms.iter() - .map(|arm| (cx.tcx.hir().attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)), + .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)), e, false, ) @@ -76,17 +76,18 @@ where && first_attrs.is_empty() && iter.all(|arm| find_bool_lit(&arm.2.kind).is_some_and(|b| b == b0) && arm.3.is_none() && arm.0.is_empty()) { - if let Some(last_pat) = last_pat_opt { - if !is_wild(last_pat) { - return false; - } + if let Some(last_pat) = last_pat_opt + && !is_wild(last_pat) + { + return false; } for arm in iter_without_last.clone() { - if let Some(pat) = arm.1 { - if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some(pat.kind) { - return false; - } + if let Some(pat) = arm.1 + && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) + && is_some(pat.kind) + { + return false; } } @@ -113,10 +114,10 @@ where // strip potential borrows (#6503), but only if the type is a reference let mut ex_new = ex; - if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { - ex_new = ex_inner; - } + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind + && let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() + { + ex_new = ex_inner; } span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs b/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs deleted file mode 100644 index dd71560e169ea..0000000000000 --- a/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs +++ /dev/null @@ -1,50 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem}; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::MATCH_ON_VEC_ITEMS; - -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) { - if let Some(idx_expr) = is_vec_indexing(cx, scrutinee) - && let ExprKind::Index(vec, idx, _) = idx_expr.kind - { - // FIXME: could be improved to suggest surrounding every pattern with Some(_), - // but only when `or_patterns` are stabilized. - span_lint_and_sugg( - cx, - MATCH_ON_VEC_ITEMS, - scrutinee.span, - "indexing into a vector may panic", - "try", - format!("{}.get({})", snippet(cx, vec.span, ".."), snippet(cx, idx.span, "..")), - Applicability::MaybeIncorrect, - ); - } -} - -fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Index(array, index, _) = expr.kind - && is_vector(cx, array) - && !is_full_range(cx, index) - { - return Some(expr); - } - - None -} - -fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); - let ty = ty.peel_refs(); - is_type_diagnostic_item(cx, ty, sym::Vec) -} - -fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); - let ty = ty.peel_refs(); - is_type_lang_item(cx, ty, LangItem::RangeFull) -} diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 41e4c75f843e5..dbb29ee776b18 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -1,8 +1,9 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{SpanlessEq, SpanlessHash, is_lint_allowed, path_to_local, search_same}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::{SpanlessEq, SpanlessHash, fulfill_or_allowed, is_lint_allowed, path_to_local, search_same}; use core::cmp::Ordering; use core::{iter, slice}; +use itertools::Itertools; use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -75,7 +76,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { HirIdMapEntry::Occupied(entry) => return *entry.get() == b_id, } // the names technically don't have to match; this makes the lint more conservative - && cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id) + && cx.tcx.hir_name(a_id) == cx.tcx.hir_name(b_id) && cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b) && pat_contains_local(lhs.pat, a_id) && pat_contains_local(rhs.pat, b_id) @@ -110,57 +111,68 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { && check_same_body() }; - let mut appl = Applicability::MaybeIncorrect; let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); - for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) { - if matches!(arm2.pat.kind, PatKind::Wild) { - if !cx.tcx.features().non_exhaustive_omitted_patterns_lint() - || is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id) - { - let arm_span = adjusted_arm_span(cx, arm1.span); - span_lint_hir_and_then( - cx, - MATCH_SAME_ARMS, - arm1.hir_id, - arm_span, - "this match arm has an identical body to the `_` wildcard arm", - |diag| { - diag.span_suggestion(arm_span, "try removing the arm", "", appl) - .help("or try changing either arm body") - .span_note(arm2.span, "`_` wildcard arm here"); - }, - ); - } - } else { - let back_block = backwards_blocking_idxs[j]; - let (keep_arm, move_arm) = if back_block < i || (back_block == 0 && forwards_blocking_idxs[i] <= j) { - (arm1, arm2) - } else { - (arm2, arm1) - }; - - span_lint_hir_and_then( - cx, - MATCH_SAME_ARMS, - keep_arm.hir_id, - keep_arm.span, - "this match arm has an identical body to another arm", - |diag| { - let move_pat_snip = snippet_with_applicability(cx, move_arm.pat.span, "", &mut appl); - let keep_pat_snip = snippet_with_applicability(cx, keep_arm.pat.span, "", &mut appl); + for mut group in search_same(&indexed_arms, hash, eq) { + // Filter out (and fulfill) `#[allow]`ed and `#[expect]`ed arms + group.retain(|(_, arm)| !fulfill_or_allowed(cx, MATCH_SAME_ARMS, [arm.hir_id])); - diag.multipart_suggestion( - "or try merging the arm patterns and removing the obsolete arm", - vec![ - (keep_arm.pat.span, format!("{keep_pat_snip} | {move_pat_snip}")), - (adjusted_arm_span(cx, move_arm.span), String::new()), - ], - appl, - ) - .help("try changing either arm body"); - }, - ); + if group.len() < 2 { + continue; } + + span_lint_and_then( + cx, + MATCH_SAME_ARMS, + group.iter().map(|(_, arm)| arm.span).collect_vec(), + "these match arms have identical bodies", + |diag| { + diag.help("if this is unintentional make the arms return different values"); + + if let [prev @ .., (_, last)] = group.as_slice() + && is_wildcard_arm(last.pat) + && is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, last.hir_id) + { + diag.span_label(last.span, "the wildcard arm"); + + let s = if prev.len() > 1 { "s" } else { "" }; + diag.multipart_suggestion_verbose( + format!("otherwise remove the non-wildcard arm{s}"), + prev.iter() + .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new())) + .collect(), + Applicability::MaybeIncorrect, + ); + } else if let &[&(first_idx, _), .., &(last_idx, _)] = group.as_slice() { + let back_block = backwards_blocking_idxs[last_idx]; + let split = if back_block < first_idx + || (back_block == 0 && forwards_blocking_idxs[first_idx] <= last_idx) + { + group.split_first() + } else { + group.split_last() + }; + + if let Some(((_, dest), src)) = split + && let Some(pat_snippets) = group + .iter() + .map(|(_, arm)| arm.pat.span.get_source_text(cx)) + .collect::>>() + { + let mut suggs = src + .iter() + .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new())) + .collect_vec(); + + suggs.push((dest.pat.span, pat_snippets.iter().join(" | "))); + diag.multipart_suggestion_verbose( + "otherwise merge the patterns into a single arm", + suggs, + Applicability::MaybeIncorrect, + ); + } + } + }, + ); } } @@ -253,6 +265,7 @@ fn iter_matching_struct_fields<'a>( impl<'a> NormalizedPat<'a> { fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { + PatKind::Missing => unreachable!(), PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) @@ -449,3 +462,11 @@ fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool { pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.swap_remove(&id)); result && ids.is_empty() } + +fn is_wildcard_arm(pat: &Pat<'_>) -> bool { + match pat.kind { + PatKind::Wild => true, + PatKind::Or([.., last]) => matches!(last.kind, PatKind::Wild), + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs index 864923b27739d..adda35869900d 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::HirNode; -use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_applicability}; +use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_context}; use clippy_utils::{get_parent_expr, is_refutable, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind, StmtKind}; @@ -24,16 +24,10 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e let bind_names = arms[0].pat.span; let match_body = peel_blocks(arms[0].body); let mut app = Applicability::MaybeIncorrect; - let mut snippet_body = snippet_block_with_context( - cx, - match_body.span, - arms[0].span.ctxt(), - "..", - Some(expr.span), - &mut app, - ) - .0 - .to_string(); + let ctxt = expr.span.ctxt(); + let mut snippet_body = snippet_block_with_context(cx, match_body.span, ctxt, "..", Some(expr.span), &mut app) + .0 + .to_string(); // Do we need to add ';' to suggestion ? if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) @@ -77,10 +71,10 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e span, format!( "let {} = {};\n{}let {} = {snippet_body};", - snippet_with_applicability(cx, bind_names, "..", &mut app), - snippet_with_applicability(cx, matched_vars, "..", &mut app), + snippet_with_context(cx, bind_names, ctxt, "..", &mut app).0, + snippet_with_context(cx, matched_vars, ctxt, "..", &mut app).0, " ".repeat(indent_of(cx, expr.span).unwrap_or(0)), - snippet_with_applicability(cx, pat_span, "..", &mut app) + snippet_with_context(cx, pat_span, ctxt, "..", &mut app).0 ), ), None => { @@ -178,24 +172,24 @@ fn sugg_with_curlies<'a>( let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); - if let Some(parent_expr) = get_parent_expr(cx, match_expr) { - if let ExprKind::Closure { .. } = parent_expr.kind { - cbrace_end = format!("\n{indent}}}"); - // Fix body indent due to the closure - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{indent}"); - } + if let Some(parent_expr) = get_parent_expr(cx, match_expr) + && let ExprKind::Closure { .. } = parent_expr.kind + { + cbrace_end = format!("\n{indent}}}"); + // Fix body indent due to the closure + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{indent}"); } // If the parent is already an arm, and the body is another match statement, // we need curly braces around suggestion - if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) { - if let ExprKind::Match(..) = arm.body.kind { - cbrace_end = format!("\n{indent}}}"); - // Fix body indent due to the match - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{indent}"); - } + if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) + && let ExprKind::Match(..) = arm.body.kind + { + cbrace_end = format!("\n{indent}}}"); + // Fix body indent due to the match + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{indent}"); } let assignment_str = assignment.map_or_else(String::new, |span| { @@ -204,14 +198,17 @@ fn sugg_with_curlies<'a>( s }); + let ctxt = match_expr.span.ctxt(); let scrutinee = if needs_var_binding { format!( "let {} = {}", - snippet_with_applicability(cx, bind_names, "..", applicability), - snippet_with_applicability(cx, matched_vars, "..", applicability) + snippet_with_context(cx, bind_names, ctxt, "..", applicability).0, + snippet_with_context(cx, matched_vars, ctxt, "..", applicability).0 ) } else { - snippet_with_applicability(cx, matched_vars, "..", applicability).to_string() + snippet_with_context(cx, matched_vars, ctxt, "..", applicability) + .0 + .to_string() }; format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs index df1b83cbb516a..65b93a095b926 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -26,10 +26,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arm && let ty::Str = ty.kind() { let mut visitor = MatchExprVisitor { cx }; - if let ControlFlow::Break(case_method) = visitor.visit_expr(scrutinee) { - if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { - lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); - } + if let ControlFlow::Break(case_method) = visitor.visit_expr(scrutinee) + && let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) + { + lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); } } } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 11b588b33554d..24b4a6758004f 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -80,18 +80,20 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { path }, PatKind::TupleStruct(path, patterns, ..) => { - if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { - if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) { - missing_variants.retain(|e| e.ctor_def_id() != Some(id)); - } + if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() + && arm.guard.is_none() + && patterns.iter().all(|p| !is_refutable(cx, p)) + { + missing_variants.retain(|e| e.ctor_def_id() != Some(id)); } path }, PatKind::Struct(path, patterns, ..) => { - if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { - if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) { - missing_variants.retain(|e| e.def_id != id); - } + if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() + && arm.guard.is_none() + && patterns.iter().all(|p| !is_refutable(cx, p.pat)) + { + missing_variants.retain(|e| e.def_id != id); } path }, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs index d0d2025878e48..8ce8453360f78 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs @@ -26,11 +26,12 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' if !matching_wild { // Looking for unused bindings (i.e.: `_e`) for pat in inner { - if let PatKind::Binding(_, id, ident, None) = pat.kind { - if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) { - ident_bind_name = ident.name; - matching_wild = true; - } + if let PatKind::Binding(_, id, ident, None) = pat.kind + && ident.as_str().starts_with('_') + && !is_local_used(cx, arm.body, id) + { + ident_bind_name = ident.name; + matching_wild = true; } } } diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 35caa7d1f3a6d..c128fc40b7334 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -8,7 +8,6 @@ mod manual_utils; mod match_as_ref; mod match_bool; mod match_like_matches; -mod match_on_vec_items; mod match_ref_pats; mod match_same_arms; mod match_single_binding; @@ -29,7 +28,7 @@ use clippy_config::Conf; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::walk_span_to_context; use clippy_utils::{ - higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments, + higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments, sym, }; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -724,38 +723,39 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for `match vec[idx]` or `match vec[n..m]`. + /// Checks if a `match` or `if let` expression can be simplified using + /// `.unwrap_or_default()`. /// /// ### Why is this bad? - /// This can panic at runtime. + /// It can be done in one call with `.unwrap_or_default()`. /// /// ### Example - /// ```rust, no_run - /// let arr = vec![0, 1, 2, 3]; - /// let idx = 1; + /// ```no_run + /// let x: Option = Some(String::new()); + /// let y: String = match x { + /// Some(v) => v, + /// None => String::new(), + /// }; /// - /// match arr[idx] { - /// 0 => println!("{}", 0), - /// 1 => println!("{}", 3), - /// _ => {}, - /// } + /// let x: Option> = Some(Vec::new()); + /// let y: Vec = if let Some(v) = x { + /// v + /// } else { + /// Vec::new() + /// }; /// ``` - /// /// Use instead: - /// ```rust, no_run - /// let arr = vec![0, 1, 2, 3]; - /// let idx = 1; + /// ```no_run + /// let x: Option = Some(String::new()); + /// let y: String = x.unwrap_or_default(); /// - /// match arr.get(idx) { - /// Some(0) => println!("{}", 0), - /// Some(1) => println!("{}", 3), - /// _ => {}, - /// } + /// let x: Option> = Some(Vec::new()); + /// let y: Vec = x.unwrap_or_default(); /// ``` - #[clippy::version = "1.45.0"] - pub MATCH_ON_VEC_ITEMS, - pedantic, - "matching on vector elements can panic" + #[clippy::version = "1.79.0"] + pub MANUAL_UNWRAP_OR_DEFAULT, + suspicious, + "check if a `match` or `if let` can be simplified with `unwrap_or_default`" } declare_clippy_lint! { @@ -1040,7 +1040,7 @@ impl_lint_pass!(Matches => [ NEEDLESS_MATCH, COLLAPSIBLE_MATCH, MANUAL_UNWRAP_OR, - MATCH_ON_VEC_ITEMS, + MANUAL_UNWRAP_OR_DEFAULT, MATCH_STR_CASE_MISMATCH, SIGNIFICANT_DROP_IN_SCRUTINEE, TRY_ERR, @@ -1053,13 +1053,13 @@ impl_lint_pass!(Matches => [ impl<'tcx> LateLintPass<'tcx> for Matches { #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if is_direct_expn_of(expr.span, "matches").is_none() && expr.span.in_external_macro(cx.sess().source_map()) { + if is_direct_expn_of(expr.span, sym::matches).is_none() && expr.span.in_external_macro(cx.sess().source_map()) { return; } let from_expansion = expr.span.from_expansion(); if let ExprKind::Match(ex, arms, source) = expr.kind { - if is_direct_expn_of(expr.span, "matches").is_some() + if is_direct_expn_of(expr.span, sym::matches).is_some() && let [arm, _] = arms { redundant_pattern_match::check_match(cx, expr, ex, arms); @@ -1110,17 +1110,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } } - // If there are still comments, it means they are outside of the arms, therefore - // we should not lint. - if match_comments.is_empty() { - single_match::check(cx, ex, arms, expr); - } + // If there are still comments, it means they are outside of the arms. Tell the lint + // code about it. + single_match::check(cx, ex, arms, expr, !match_comments.is_empty()); match_bool::check(cx, ex, arms, expr); overlapping_arms::check(cx, ex, arms); match_wild_enum::check(cx, ex, arms); match_as_ref::check(cx, ex, arms, expr); needless_match::check_match(cx, ex, arms, expr); - match_on_vec_items::check(cx, ex); match_str_case_mismatch::check(cx, ex, arms); redundant_guards::check(cx, arms, self.msrv); diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 7e65d586110e5..b04db03f8d2e7 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -67,14 +67,14 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) for arm in arms { let arm_expr = peel_blocks_with_stmt(arm.body); - if let Some(guard_expr) = &arm.guard { - if guard_expr.can_have_side_effects() { - return false; - } + if let Some(guard_expr) = &arm.guard + && guard_expr.can_have_side_effects() + { + return false; } if let PatKind::Wild = arm.pat.kind { - if !eq_expr_value(cx, match_expr, strip_return(arm_expr)) { + if !eq_expr_value(cx, match_expr, arm_expr) { return false; } } else if !pat_same_as_expr(arm.pat, arm_expr) { @@ -103,27 +103,18 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool if matches!(else_expr.kind, ExprKind::Block(..)) { return false; } - let ret = strip_return(else_expr); let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { - return is_res_lang_ctor(cx, path_res(cx, ret), OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); + return is_res_lang_ctor(cx, path_res(cx, else_expr), OptionNone) + || eq_expr_value(cx, if_let.let_expr, else_expr); } - return eq_expr_value(cx, if_let.let_expr, ret); + return eq_expr_value(cx, if_let.let_expr, else_expr); } } false } -/// Strip `return` keyword if the expression type is `ExprKind::Ret`. -fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { - if let ExprKind::Ret(Some(ret)) = expr.kind { - ret - } else { - expr - } -} - /// Manually check for coercion casting by checking if the type of the match operand or let expr /// differs with the assigned local variable or the function return type. fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { @@ -161,7 +152,6 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> } fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { - let expr = strip_return(expr); match (&pat.kind, &expr.kind) { // Example: `Some(val) => Some(val)` (PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => { diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs index 4184f8b9e6e8a..d3136c89178e6 100644 --- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs @@ -11,17 +11,17 @@ use super::MATCH_OVERLAPPING_ARM; pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { if arms.len() >= 2 && cx.typeck_results().expr_ty(ex).is_integral() { let ranges = all_ranges(cx, arms, cx.typeck_results().expr_ty(ex)); - if !ranges.is_empty() { - if let Some((start, end)) = overlapping(&ranges) { - span_lint_and_note( - cx, - MATCH_OVERLAPPING_ARM, - start.span, - "some ranges overlap", - Some(end.span), - "overlaps with this", - ); - } + if !ranges.is_empty() + && let Some((start, end)) = overlapping(&ranges) + { + span_lint_and_note( + cx, + MATCH_OVERLAPPING_ARM, + start.span, + "some ranges overlap", + Some(end.span), + "overlaps with this", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index ab53ad98572e4..7c6d45e424006 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -3,14 +3,14 @@ use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet; use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used}; -use clippy_utils::{is_in_const_context, path_to_local}; +use clippy_utils::{is_in_const_context, path_to_local, sym}; use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_span::symbol::Ident; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; use std::ops::ControlFlow; @@ -95,7 +95,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: } else if let ExprKind::MethodCall(path, recv, args, ..) = guard.kind && let Some(binding) = get_pat_binding(cx, recv, outer_arm) { - check_method_calls(cx, outer_arm, path.ident.name.as_str(), recv, args, guard, &binding); + check_method_calls(cx, outer_arm, path.ident.name, recv, args, guard, &binding); } } } @@ -103,7 +103,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: fn check_method_calls<'tcx>( cx: &LateContext<'tcx>, arm: &Arm<'tcx>, - method: &str, + method: Symbol, recv: &Expr<'_>, args: &[Expr<'_>], if_expr: &Expr<'_>, @@ -112,7 +112,7 @@ fn check_method_calls<'tcx>( let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let slice_like = ty.is_slice() || ty.is_array(); - let sugg = if method == "is_empty" { + let sugg = if method == sym::is_empty { // `s if s.is_empty()` becomes "" // `arr if arr.is_empty()` becomes [] @@ -137,9 +137,9 @@ fn check_method_calls<'tcx>( if needles.is_empty() { sugg.insert_str(1, ".."); - } else if method == "starts_with" { + } else if method == sym::starts_with { sugg.insert_str(sugg.len() - 1, ", .."); - } else if method == "ends_with" { + } else if method == sym::ends_with { sugg.insert_str(1, ".., "); } else { return; @@ -246,7 +246,6 @@ fn emit_redundant_guards<'tcx>( fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { for_each_expr_without_closures(expr, |expr| { if match expr.kind { - ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat(), ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => { // Allow ctors matches!(cx.qpath_res(&qpath, c.hir_id), Res::Def(DefKind::Ctor(..), ..)) diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 722ea7042dd7f..aa9be61bf4d43 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -4,7 +4,7 @@ use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; -use clippy_utils::{higher, is_expn_of, is_trait_method}; +use clippy_utils::{higher, is_expn_of, is_trait_method, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; @@ -12,7 +12,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; use std::fmt::Write; use std::ops::ControlFlow; @@ -138,9 +138,9 @@ fn find_method_and_type<'tcx>( Some(("is_some()", op_ty)) } else if Some(id) == lang_items.poll_ready_variant() { Some(("is_ready()", op_ty)) - } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V4))) { + } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym::V4)) { Some(("is_ipv4()", op_ty)) - } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V6))) { + } else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym::V6)) { Some(("is_ipv6()", op_ty)) } else { None @@ -255,7 +255,7 @@ fn find_method_sugg_for_if_let<'tcx>( }; let sugg = Sugg::hir_with_context(cx, result_expr, ctxt, "_", &mut app) - .maybe_par() + .maybe_paren() .to_string(); diag.span_suggestion(span, "try", format!("{keyword} {sugg}.{good_method}"), app); @@ -273,13 +273,13 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); if let Some((good_method, maybe_guard)) = found_good_method(cx, arms, node_pair) { - let span = is_expn_of(expr.span, "matches").unwrap_or(expr.span.to(op.span)); + let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span)); let result_expr = match &op.kind { ExprKind::AddrOf(_, _, borrowed) => borrowed, _ => op, }; let mut app = Applicability::MachineApplicable; - let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_par(); + let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren(); let mut sugg = format!("{receiver_sugg}.{good_method}"); if let Some(guard) = maybe_guard { @@ -303,7 +303,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op } let guard = Sugg::hir(cx, guard, ".."); - let _ = write!(sugg, " && {}", guard.maybe_par()); + let _ = write!(sugg, " && {}", guard.maybe_paren()); } span_lint_and_sugg( @@ -345,8 +345,8 @@ fn found_good_method<'tcx>( arms, path_left, path_right, - Item::Diag(sym::IpAddr, sym!(V4)), - Item::Diag(sym::IpAddr, sym!(V6)), + Item::Diag(sym::IpAddr, sym::V4), + Item::Diag(sym::IpAddr, sym::V6), "is_ipv4()", "is_ipv6()", ) @@ -437,8 +437,8 @@ fn get_good_method<'tcx>( "None" => (Item::Lang(OptionNone), "is_none()", "is_some()"), "Ready" => (Item::Lang(PollReady), "is_ready()", "is_pending()"), "Pending" => (Item::Lang(PollPending), "is_pending()", "is_ready()"), - "V4" => (Item::Diag(sym::IpAddr, sym!(V4)), "is_ipv4()", "is_ipv6()"), - "V6" => (Item::Diag(sym::IpAddr, sym!(V6)), "is_ipv6()", "is_ipv4()"), + "V4" => (Item::Diag(sym::IpAddr, sym::V4), "is_ipv4()", "is_ipv6()"), + "V6" => (Item::Diag(sym::IpAddr, sym::V6), "is_ipv6()", "is_ipv4()"), _ => return None, }; return find_good_method_for_matches_macro( diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 37bac561a6e06..0f3ad40784d36 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -4,7 +4,7 @@ use crate::FxHashSet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{first_line_of_span, indent_of, snippet}; use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; -use clippy_utils::{get_attr, is_lint_allowed}; +use clippy_utils::{get_attr, is_lint_allowed, sym}; use itertools::Itertools; use rustc_ast::Mutability; use rustc_data_structures::fx::FxIndexSet; @@ -182,17 +182,16 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { } fn has_sig_drop_attr_impl(&mut self, ty: Ty<'tcx>) -> bool { - if let Some(adt) = ty.ty_adt_def() { - if get_attr( + if let Some(adt) = ty.ty_adt_def() + && get_attr( self.cx.sess(), self.cx.tcx.get_attrs_unchecked(adt.did()), - "has_significant_drop", + sym::has_significant_drop, ) .count() > 0 - { - return true; - } + { + return true; } if !self.seen_types.insert(ty) { diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 2f46eaaabb364..08c0caa4266cc 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -1,12 +1,14 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, expr_block, snippet, snippet_block_with_context}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{ + SpanRangeExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, +}; use clippy_utils::ty::implements_trait; use clippy_utils::{ is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, }; use core::ops::ControlFlow; use rustc_arena::DroplessArena; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, Diag}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_pat}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, StmtKind}; @@ -32,10 +34,9 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { } #[rustfmt::skip] -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>) { +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>, contains_comments: bool) { if let [arm1, arm2] = arms - && arm1.guard.is_none() - && arm2.guard.is_none() + && !arms.iter().any(|arm| arm.guard.is_some() || arm.pat.span.from_expansion()) && !expr.span.from_expansion() // don't lint for or patterns for now, this makes // the lint noisy in unnecessary situations @@ -77,20 +78,36 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tc } } - report_single_pattern(cx, ex, arm1, expr, els); + report_single_pattern(cx, ex, arm1, expr, els, contains_comments); } } } -fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, expr: &Expr<'_>, els: Option<&Expr<'_>>) { +fn report_single_pattern( + cx: &LateContext<'_>, + ex: &Expr<'_>, + arm: &Arm<'_>, + expr: &Expr<'_>, + els: Option<&Expr<'_>>, + contains_comments: bool, +) { let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH }; let ctxt = expr.span.ctxt(); - let mut app = Applicability::MachineApplicable; + let note = |diag: &mut Diag<'_, ()>| { + if contains_comments { + diag.note("you might want to preserve the comments from inside the `match`"); + } + }; + let mut app = if contains_comments { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; let els_str = els.map_or(String::new(), |els| { format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app)) }); - if snippet(cx, ex.span, "..") == snippet(cx, arm.pat.span, "..") { + if ex.span.eq_ctxt(expr.span) && snippet(cx, ex.span, "..") == snippet(cx, arm.pat.span, "..") { let msg = "this pattern is irrefutable, `match` is useless"; let (sugg, help) = if is_unit_expr(arm.body) { (String::new(), "`match` expression can be removed") @@ -109,7 +126,10 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp } (sugg, "try") }; - span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg.to_string(), app); + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + diag.span_suggestion(expr.span, help, sugg.to_string(), app); + note(diag); + }); return; } @@ -144,10 +164,10 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; let sugg = format!( "if {} == {}{} {}{els_str}", - snippet(cx, ex.span, ".."), + snippet_with_context(cx, ex.span, ctxt, "..", &mut app).0, // PartialEq for different reference counts may not exist. "&".repeat(ref_count_diff), - snippet(cx, arm.pat.span, ".."), + snippet_with_applicability(cx, arm.pat.span, "..", &mut app), expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app), ); (msg, sugg) @@ -155,14 +175,17 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"; let sugg = format!( "if let {} = {} {}{els_str}", - snippet(cx, arm.pat.span, ".."), - snippet(cx, ex.span, ".."), + snippet_with_applicability(cx, arm.pat.span, "..", &mut app), + snippet_with_context(cx, ex.span, ctxt, "..", &mut app).0, expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app), ); (msg, sugg) }; - span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app); + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + diag.span_suggestion(expr.span, "try", sugg.to_string(), app); + note(diag); + }); } struct PatVisitor<'tcx> { @@ -384,6 +407,7 @@ impl<'a> PatState<'a> { pats.iter().map(|p| p.pat), ), + PatKind::Missing => unreachable!(), PatKind::Wild | PatKind::Binding(_, _, _, None) | PatKind::Expr(_) diff --git a/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs b/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs index b75d1ab9a7aa3..43102d78bfebd 100644 --- a/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs +++ b/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs @@ -15,18 +15,18 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) { return; } for arm in arms { - if let PatKind::Or(fields) = arm.pat.kind { + if let PatKind::Or(fields) = arm.pat.kind // look for multiple fields in this arm that contains at least one Wild pattern - if fields.len() > 1 && fields.iter().any(is_wild) { - span_lint_and_help( - cx, - WILDCARD_IN_OR_PATTERNS, - arm.pat.span, - "wildcard pattern covers any other pattern as it will match anyway", - None, - "consider handling `_` separately", - ); - } + && fields.len() > 1 && fields.iter().any(is_wild) + { + span_lint_and_help( + cx, + WILDCARD_IN_OR_PATTERNS, + arm.pat.span, + "wildcard pattern covers any other pattern as it will match anyway", + None, + "consider handling `_` separately", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index a0919947b3fc7..28efd2038b387 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -62,7 +62,7 @@ declare_clippy_lint! { /// let mut an_option = Some(0); /// let taken = an_option.replace(1); /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub MEM_REPLACE_OPTION_WITH_SOME, style, "replacing an `Option` with `Some` instead of `replace()`" @@ -145,7 +145,7 @@ fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &E "consider `Option::take()` instead", format!( "{}.take()", - Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_par() + Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_paren() ), applicability, ); @@ -178,7 +178,7 @@ fn check_replace_option_with_some( "consider `Option::replace()` instead", format!( "{}.replace({})", - Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "_", &mut applicability).maybe_par(), + Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "_", &mut applicability).maybe_paren(), snippet_with_applicability(cx, src_arg.span, "_", &mut applicability) ), applicability, @@ -304,14 +304,12 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { && let ExprKind::Path(ref func_qpath) = func.kind && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::mem_replace, def_id) - { // Check that second argument is `Option::None` - if !check_replace_option_with_none(cx, src, dest, expr.span) - && !check_replace_option_with_some(cx, src, dest, expr.span, self.msrv) - && !check_replace_with_default(cx, src, dest, expr, self.msrv) - { - check_replace_with_uninit(cx, src, dest, expr.span); - } + && !check_replace_option_with_none(cx, src, dest, expr.span) + && !check_replace_option_with_some(cx, src, dest, expr.span, self.msrv) + && !check_replace_with_default(cx, src, dest, expr, self.msrv) + { + check_replace_with_uninit(cx, src, dest, expr.span); } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs index 1e9b29f567f41..f8520c23ea503 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs @@ -192,10 +192,10 @@ impl BindInsteadOfMap { } fn is_variant(&self, cx: &LateContext<'_>, res: Res) -> bool { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.lang_items().get(self.variant_lang_item) { - return cx.tcx.parent(id) == variant_id; - } + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res + && let Some(variant_id) = cx.tcx.lang_items().get(self.variant_lang_item) + { + return cx.tcx.parent(id) == variant_id; } false } diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs index de22514c37c66..02fc09170e59c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; @@ -25,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E if let Some(parent) = clippy_utils::get_parent_expr(cx, expr) && let Some((name, _, _, _, _)) = method_call(parent) - && name == "unwrap" + && name == sym::unwrap { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 18568e3661fe5..292fa08b59843 100644 --- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,5 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::sym; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -16,14 +18,15 @@ pub(super) fn check<'tcx>( call_span: Span, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, + msrv: Msrv, ) { - if let ExprKind::MethodCall(path_segment, ..) = recv.kind { - if matches!( - path_segment.ident.name.as_str(), - "to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase" - ) { - return; - } + if let ExprKind::MethodCall(path_segment, ..) = recv.kind + && matches!( + path_segment.ident.name, + sym::to_lowercase | sym::to_uppercase | sym::to_ascii_lowercase | sym::to_ascii_uppercase + ) + { + return; } if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) @@ -58,11 +61,15 @@ pub(super) fn check<'tcx>( let suggestion_source = reindent_multiline( &format!( - "std::path::Path::new({}) + "std::path::Path::new({recv_source}) .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", - recv_source, - ext_str.strip_prefix('.').unwrap() + .{}|ext| ext.eq_ignore_ascii_case(\"{}\"))", + if msrv.meets(cx, msrvs::OPTION_RESULT_IS_VARIANT_AND) { + "is_some_and(" + } else { + "map_or(false, " + }, + ext_str.strip_prefix('.').unwrap(), ), true, Some(indent_of(cx, call_span).unwrap_or(0) + 4), diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs index 4ae0aeea2d1c5..de27a45ba4d92 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs @@ -5,12 +5,13 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, Lint}; use rustc_middle::ty; +use rustc_span::Symbol; /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. pub(super) fn check( cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>, - chain_methods: &[&str], + chain_methods: &[Symbol], lint: &'static Lint, suggest: &str, ) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index 9c45ec2e56cbe..1c72a973cfa13 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -5,12 +5,13 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, Lint}; +use rustc_span::Symbol; /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. pub(super) fn check( cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>, - chain_methods: &[&str], + chain_methods: &[Symbol], lint: &'static Lint, suggest: &str, ) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs index 2efff4c3c5497..8729e91d191f8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs @@ -1,13 +1,14 @@ use crate::methods::chars_cmp; +use clippy_utils::sym; use rustc_lint::LateContext; use super::CHARS_LAST_CMP; /// Checks for the `CHARS_LAST_CMP` lint. pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { - if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { + if chars_cmp::check(cx, info, &[sym::chars, sym::last], CHARS_LAST_CMP, "ends_with") { true } else { - chars_cmp::check(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with") + chars_cmp::check(cx, info, &[sym::chars, sym::next_back], CHARS_LAST_CMP, "ends_with") } } diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs index 5b8713f7d7903..027d0a3947bfb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs @@ -1,13 +1,26 @@ use crate::methods::chars_cmp_with_unwrap; +use clippy_utils::sym; use rustc_lint::LateContext; use super::CHARS_LAST_CMP; /// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { - if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { + if chars_cmp_with_unwrap::check( + cx, + info, + &[sym::chars, sym::last, sym::unwrap], + CHARS_LAST_CMP, + "ends_with", + ) { true } else { - chars_cmp_with_unwrap::check(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with") + chars_cmp_with_unwrap::check( + cx, + info, + &[sym::chars, sym::next_back, sym::unwrap], + CHARS_LAST_CMP, + "ends_with", + ) } } diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs index b631fecab9729..2438843bf3ab1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs @@ -1,8 +1,9 @@ +use clippy_utils::sym; use rustc_lint::LateContext; use super::CHARS_NEXT_CMP; /// Checks for the `CHARS_NEXT_CMP` lint. pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { - crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with") + crate::methods::chars_cmp::check(cx, info, &[sym::chars, sym::next], CHARS_NEXT_CMP, "starts_with") } diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs index caf21d3ff3bcb..9b3609f19d724 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs @@ -1,8 +1,15 @@ +use clippy_utils::sym; use rustc_lint::LateContext; use super::CHARS_NEXT_CMP; /// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`. pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { - crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with") + crate::methods::chars_cmp_with_unwrap::check( + cx, + info, + &[sym::chars, sym::next, sym::unwrap], + CHARS_NEXT_CMP, + "starts_with", + ) } diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs index 1ee27d90d0545..2ecf3eb897988 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs @@ -40,10 +40,10 @@ pub(super) fn check( .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target); let ty = cx.typeck_results().expr_ty(expr); - if let ty::Ref(_, inner, _) = arg_ty.kind() { - if let ty::Ref(..) = inner.kind() { - return; // don't report clone_on_copy - } + if let ty::Ref(_, inner, _) = arg_ty.kind() + && let ty::Ref(..) = inner.kind() + { + return; // don't report clone_on_copy } if is_copy(cx, ty) { diff --git a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs index f7bf8764bdebc..6d0b944df55d6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{eq_expr_value, get_parent_expr}; +use clippy_utils::{eq_expr_value, get_parent_expr, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir as hir; @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>( // If the parent node's `to` argument is the same as the `to` argument // of the last replace call in the current chain, don't lint as it was already linted if let Some(parent) = get_parent_expr(cx, expr) - && let Some(("replace", _, [current_from, current_to], _, _)) = method_call(parent) + && let Some((sym::replace, _, [current_from, current_to], _, _)) = method_call(parent) && eq_expr_value(cx, to, current_to) && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind() { @@ -47,7 +47,7 @@ fn collect_replace_calls<'tcx>( let mut from_args = VecDeque::new(); let _: Option<()> = for_each_expr_without_closures(expr, |e| { - if let Some(("replace", _, [from, to], _, _)) = method_call(e) { + if let Some((sym::replace, _, [from, to], _, _)) = method_call(e) { if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { methods.push_front(e); from_args.push_front(from); diff --git a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs index e82211bbf3ef7..6d841853fbe5f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs +++ b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::implements_trait; -use clippy_utils::{is_mutable, is_trait_method, path_to_local}; +use clippy_utils::ty::{has_non_owning_mutable_access, implements_trait}; +use clippy_utils::{is_mutable, is_trait_method, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::Instance; -use rustc_span::{Span, sym}; +use rustc_span::Span; use super::DOUBLE_ENDED_ITERATOR_LAST; @@ -24,13 +24,18 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp && let Ok(Some(fn_def)) = Instance::try_resolve(cx.tcx, cx.typing_env(), id, args) // find the provided definition of Iterator::last && let Some(item) = cx.tcx.get_diagnostic_item(sym::Iterator) - && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name.as_str() == "last") + && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name() == sym::last) // if the resolved method is the same as the provided definition && fn_def.def_id() == last_def.def_id + && let self_ty = cx.typeck_results().expr_ty(self_expr) + && !has_non_owning_mutable_access(cx, self_ty) { let mut sugg = vec![(call_span, String::from("next_back()"))]; let mut dont_apply = false; + // if `self_expr` is a reference, it is mutable because it is used for `.last()` + // TODO: Change this to lint only when the referred iterator is not used later. If it is used later, + // changing to `next_back()` may change its behavior. if !(is_mutable(cx, self_expr) || self_type.is_ref()) { if let Some(hir_id) = path_to_local(self_expr) && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index daa6e0e7f940c..82e5a6d5a4120 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -6,8 +6,8 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::Span; use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; use super::EXPECT_FUN_CALL; @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>( format_args_storage: &FormatArgsStorage, expr: &hir::Expr<'_>, method_span: Span, - name: &str, + name: Symbol, receiver: &'tcx hir::Expr<'tcx>, args: &'tcx [hir::Expr<'tcx>], ) { @@ -54,10 +54,11 @@ pub(super) fn check<'tcx>( if is_type_lang_item(cx, arg_ty, hir::LangItem::String) { return false; } - if let ty::Ref(_, ty, ..) = arg_ty.kind() { - if ty.is_str() && can_be_static_str(cx, arg) { - return false; - } + if let ty::Ref(_, ty, ..) = arg_ty.kind() + && ty.is_str() + && can_be_static_str(cx, arg) + { + return false; } true } @@ -113,7 +114,7 @@ pub(super) fn check<'tcx>( } } - if args.len() != 1 || name != "expect" || !is_call(&args[0].kind) { + if args.len() != 1 || name != sym::expect || !is_call(&args[0].kind) { return; } diff --git a/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs index 460ec7b3640a6..db60061904f6f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use super::EXTEND_WITH_DRAIN; @@ -13,7 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: if is_type_diagnostic_item(cx, ty, sym::Vec) //check source object && let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind - && src_method.ident.as_str() == "drain" + && src_method.ident.name == sym::drain && let src_ty = cx.typeck_results().expr_ty(drain_vec) //check if actual src type is mutable for code suggestion && let immutable = src_ty.is_mutable_ptr() diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index ae300cd5fe56d..4dd54cf197450 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks}; +use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks, sym}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, sym}; +use rustc_span::symbol::{Ident, Symbol}; use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP, RESULT_FILTER_MAP}; @@ -43,10 +43,10 @@ fn is_method(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol) -> bool } fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { - is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) + is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym::is_some) } fn is_ok_filter_map(cx: &LateContext<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { - is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_ok)) + is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym::is_ok) } #[derive(Debug, Copy, Clone)] @@ -233,12 +233,12 @@ impl<'tcx> OffendingFilterExpr<'tcx> { // the latter only calls `effect` once let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span); - if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name.as_str() == "is_some" { + if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym::is_some { Some(Self::IsSome { receiver, side_effect_expr_span, }) - } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name.as_str() == "is_ok" { + } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym::is_ok { Some(Self::IsOk { receiver, side_effect_expr_span, @@ -429,16 +429,15 @@ fn is_find_or_filter<'a>( } fn acceptable_methods(method: &PathSegment<'_>) -> bool { - let methods: [Symbol; 8] = [ - sym::clone, - sym::as_ref, - sym!(copied), - sym!(cloned), - sym!(as_deref), - sym!(as_mut), - sym!(as_deref_mut), - sym!(to_owned), - ]; - - methods.contains(&method.ident.name) + matches!( + method.ident.name, + sym::clone + | sym::as_ref + | sym::copied + | sym::cloned + | sym::as_deref + | sym::as_mut + | sym::as_deref_mut + | sym::to_owned + ) } diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs index f7e116c5310ed..965993808f6b5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,10 +1,14 @@ use super::FILTER_MAP_BOOL_THEN; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; -use clippy_utils::{is_from_proc_macro, is_trait_method, peel_blocks}; +use clippy_utils::{ + CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, is_trait_method, peel_blocks, +}; +use rustc_ast::Mutability; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, HirId, Param, Pat}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::Binder; use rustc_middle::ty::adjustment::Adjust; @@ -44,17 +48,69 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & && let Some(filter) = recv.span.get_source_text(cx) && let Some(map) = then_body.span.get_source_text(cx) { - span_lint_and_sugg( + span_lint_and_then( cx, FILTER_MAP_BOOL_THEN, call_span, "usage of `bool::then` in `filter_map`", - "use `filter` then `map` instead", - format!( - "filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})", - derefs = "*".repeat(needed_derefs) - ), - Applicability::MachineApplicable, + |diag| { + if can_filter_and_then_move_to_closure(cx, ¶m, recv, then_body) { + diag.span_suggestion( + call_span, + "use `filter` then `map` instead", + format!( + "filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})", + derefs = "*".repeat(needed_derefs) + ), + Applicability::MachineApplicable, + ); + } else { + diag.help("consider using `filter` then `map` instead"); + } + }, ); } } + +/// Returns a set of all bindings found in the given pattern. +fn find_bindings_from_pat(pat: &Pat<'_>) -> FxHashSet { + let mut bindings = FxHashSet::default(); + pat.walk(|p| { + if let rustc_hir::PatKind::Binding(_, hir_id, _, _) = p.kind { + bindings.insert(hir_id); + } + true + }); + bindings +} + +/// Returns true if we can take a closure parameter and have it in both the `filter` function and +/// the`map` function. This is not the case if: +/// +/// - The `filter` would contain an early return, +/// - `filter` and `then` contain captures, and any of those are &mut +fn can_filter_and_then_move_to_closure<'tcx>( + cx: &LateContext<'tcx>, + param: &Param<'tcx>, + filter: &'tcx Expr<'tcx>, + then: &'tcx Expr<'tcx>, +) -> bool { + if contains_return(filter) { + return false; + } + + let Some(filter_captures) = can_move_expr_to_closure(cx, filter) else { + return true; + }; + let Some(then_captures) = can_move_expr_to_closure(cx, then) else { + return true; + }; + + let param_bindings = find_bindings_from_pat(param.pat); + filter_captures.iter().all(|(hir_id, filter_cap)| { + param_bindings.contains(hir_id) + || !then_captures + .get(hir_id) + .is_some_and(|then_cap| matches!(*filter_cap | *then_cap, CaptureKind::Ref(Mutability::Mut))) + }) +} diff --git a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs index f4840785584ef..045363058d198 100644 --- a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,25 +1,31 @@ +use std::fmt::Write as _; + use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::{is_path_diagnostic_item, sugg}; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::{self as hir, Expr, ExprKind, GenericArg, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::GenericParamDefKind; use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>], func: &Expr<'_>) { if is_path_diagnostic_item(cx, func, sym::from_iter_fn) - && let ty = cx.typeck_results().expr_ty(expr) && let arg_ty = cx.typeck_results().expr_ty(&args[0]) && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) && implements_trait(cx, arg_ty, iter_id, &[]) { - // `expr` implements `FromIterator` trait - let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); - let turbofish = extract_turbofish(cx, expr, ty); + let mut app = Applicability::MaybeIncorrect; + let turbofish = match func.kind { + ExprKind::Path(QPath::TypeRelative(hir_ty, _)) => build_full_type(cx, hir_ty, &mut app), + ExprKind::Path(QPath::Resolved(Some(self_ty), _)) => build_full_type(cx, self_ty, &mut app), + _ => return, + }; + let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_paren(); let sugg = format!("{iter_expr}.collect::<{turbofish}>()"); span_lint_and_sugg( cx, @@ -28,54 +34,47 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp "usage of `FromIterator::from_iter`", "use `.collect()` instead of `::from_iter()`", sugg, - Applicability::MaybeIncorrect, + app, ); } } -fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> String { - fn strip_angle_brackets(s: &str) -> Option<&str> { - s.strip_prefix('<')?.strip_suffix('>') - } - - let call_site = expr.span.source_callsite(); - if let Some(snippet) = call_site.get_source_text(cx) - && let snippet_split = snippet.split("::").collect::>() - && let Some((_, elements)) = snippet_split.split_last() +/// Build a type which can be used in a turbofish syntax from `hir_ty`, either by copying the +/// existing generic arguments with the exception of elided lifetimes, or by inserting placeholders +/// for types and consts without default values. +fn build_full_type(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, app: &mut Applicability) -> String { + if let TyKind::Path(ty_qpath) = hir_ty.kind + && let QPath::Resolved(None, ty_path) = &ty_qpath + && let Res::Def(_, ty_did) = ty_path.res { - if let [type_specifier, _] = snippet_split.as_slice() - && let Some(type_specifier) = strip_angle_brackets(type_specifier) - && let Some((type_specifier, ..)) = type_specifier.split_once(" as ") - { - type_specifier.to_string() + let mut ty_str = itertools::join(ty_path.segments.iter().map(|s| s.ident), "::"); + let mut first = true; + let mut append = |arg: &str| { + write!(&mut ty_str, "{}{arg}", [", ", "<"][usize::from(first)]).unwrap(); + first = false; + }; + if let Some(args) = ty_path.segments.last().and_then(|segment| segment.args) { + args.args + .iter() + .filter(|arg| !matches!(arg, GenericArg::Lifetime(lt) if lt.is_elided())) + .for_each(|arg| append(&snippet_with_applicability(cx, arg.span().source_callsite(), "_", app))); } else { - // is there a type specifier? (i.e.: like `` in `collections::BTreeSet::::`) - if let Some(type_specifier) = snippet_split.iter().find(|e| strip_angle_brackets(e).is_some()) { - // remove the type specifier from the path elements - let without_ts = elements - .iter() - .filter_map(|e| { - if e == type_specifier { - None - } else { - Some((*e).to_string()) - } - }) - .collect::>(); - // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) - format!("{}{type_specifier}", without_ts.join("::")) - } else { - // type is not explicitly specified so wildcards are needed - // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` - let ty_str = ty.to_string(); - let start = ty_str.find('<').unwrap_or(0); - let end = ty_str.find('>').unwrap_or(ty_str.len()); - let nb_wildcard = ty_str[start..end].split(',').count(); - let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); - format!("{}<{wildcards}>", elements.join("::")) - } + cx.tcx + .generics_of(ty_did) + .own_params + .iter() + .filter(|param| { + matches!( + param.kind, + GenericParamDefKind::Type { has_default: false, .. } + | GenericParamDefKind::Const { has_default: false, .. } + ) + }) + .for_each(|_| append("_")); } + ty_str.push_str([">", ""][usize::from(first)]); + ty_str } else { - ty.to_string() + snippet_with_applicability(cx, hir_ty.span.source_callsite(), "_", app).into() } } diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs index 519091406ccf3..9724463f0c084 100644 --- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs @@ -1,15 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs}; +use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::Symbol; use super::IMPLICIT_CLONE; -pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { +pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && is_clone_like(cx, method_name, method_def_id) && let return_type = cx.typeck_results().expr_ty(expr) @@ -43,12 +43,12 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv /// Note that `to_string` is not flagged by `implicit_clone`. So other lints that call /// `is_clone_like` and that do flag `to_string` must handle it separately. See, e.g., /// `is_to_owned_like` in `unnecessary_to_owned.rs`. -pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir::def_id::DefId) -> bool { +pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: hir::def_id::DefId) -> bool { match method_name { - "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr), - "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned), - "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path), - "to_vec" => cx + sym::to_os_string => is_diag_item_method(cx, method_def_id, sym::OsStr), + sym::to_owned => is_diag_trait_item(cx, method_def_id, sym::ToOwned), + sym::to_path_buf => is_diag_item_method(cx, method_def_id, sym::Path), + sym::to_vec => cx .tcx .impl_of_method(method_def_id) .filter(|&impl_did| { diff --git a/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs b/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs index 4659e9e163fe9..ec4b9c7ae2ee6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs +++ b/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{expr_or_init, paths}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; @@ -8,13 +9,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args if let [error_kind, error] = args && !expr.span.from_expansion() && !error_kind.span.from_expansion() - && clippy_utils::is_expr_path_def_path(cx, path, &clippy_utils::paths::IO_ERROR_NEW) - && clippy_utils::is_expr_path_def_path( - cx, - clippy_utils::expr_or_init(cx, error_kind), - &clippy_utils::paths::IO_ERRORKIND_OTHER, - ) && let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind + && paths::IO_ERROR_NEW.matches_path(cx, path) + && paths::IO_ERRORKIND_OTHER_CTOR.matches_path(cx, expr_or_init(cx, error_kind)) && msrv.meets(cx, msrvs::IO_ERROR_OTHER) { span_lint_and_then( @@ -27,7 +24,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args "use `std::io::Error::other`", vec![ (new_segment.ident.span, "other".to_owned()), - (error_kind.span.until(error.span), String::new()), + (error_kind.span.until(error.span.source_callsite()), String::new()), ], Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs index 1c64f78678aeb..545bef1a4c5bc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs +++ b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs @@ -14,15 +14,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ if expr.span.in_external_macro(cx.sess().source_map()) || !receiver.span.eq_ctxt(expr.span) { return; } - if let Some(parent) = get_parent_expr(cx, expr) { - if let Some(parent) = get_parent_expr(cx, parent) { - if is_inside_always_const_context(cx.tcx, expr.hir_id) - && let Some(macro_call) = root_macro_call(parent.span) - && is_assert_macro(cx, macro_call.def_id) - { - return; - } - } + if let Some(parent) = get_parent_expr(cx, expr) + && let Some(parent) = get_parent_expr(cx, parent) + && is_inside_always_const_context(cx.tcx, expr.hir_id) + && let Some(macro_call) = root_macro_call(parent.span) + && is_assert_macro(cx, macro_call.def_id) + { + return; } let init_expr = expr_or_init(cx, receiver); if !receiver.span.eq_ctxt(init_expr.span) { @@ -41,7 +39,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool { cx.tcx .hir_parent_id_iter(id) - .any(|id| cx.tcx.hir().attrs(id).iter().any(|attr| attr.has_name(sym::cfg))) + .any(|id| cx.tcx.hir_attrs(id).iter().any(|attr| attr.has_name(sym::cfg_trace))) } /// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs index 49de83885a1ca..b4ab313fe98d1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs @@ -1,16 +1,27 @@ use crate::methods::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{get_iterator_item_ty, is_type_diagnostic_item}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_middle::ty; +use rustc_span::{Symbol, sym}; use super::ITER_CLONED_COLLECT; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + method_name: Symbol, + expr: &hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, +) { + let expr_ty = cx.typeck_results().expr_ty(expr); + if is_type_diagnostic_item(cx, expr_ty, sym::Vec) && let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)) + && let ty::Adt(_, args) = expr_ty.kind() + && let Some(iter_item_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty(recv)) + && let ty::Ref(_, iter_item_ty, _) = iter_item_ty.kind() + && *iter_item_ty == args.type_at(0) && let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_count.rs b/src/tools/clippy/clippy_lints/src/methods/iter_count.rs index 209cf2fcc0a44..6b64cc8b50ae7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_count.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_count.rs @@ -5,11 +5,11 @@ use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use super::ITER_COUNT; -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, iter_method: &str) { +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, iter_method: Symbol) { let ty = cx.typeck_results().expr_ty(recv); let caller_type = if derefs_to_slice(cx, recv, ty).is_some() { "slice" diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs index bafabec7e0695..adeff375c8aad 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs @@ -6,12 +6,12 @@ use super::{ITER_FILTER_IS_OK, ITER_FILTER_IS_SOME}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline}; -use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_comment}; +use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::QPath; use rustc_span::Span; -use rustc_span::symbol::{Ident, Symbol, sym}; +use rustc_span::symbol::{Ident, Symbol}; /// /// Returns true if the expression is a method call to `method_name` @@ -154,7 +154,7 @@ fn expression_type( if let Some(opt_defid) = cx.tcx.get_diagnostic_item(sym::Option) && let opt_ty = cx.tcx.type_of(opt_defid).skip_binder() && iter_item_ty.ty_adt_def() == opt_ty.ty_adt_def() - && is_method(cx, filter_arg, sym::Option, sym!(is_some), &[]) + && is_method(cx, filter_arg, sym::Option, sym::is_some, &[]) { return Some(FilterType::IsSome); } @@ -162,7 +162,7 @@ fn expression_type( if let Some(opt_defid) = cx.tcx.get_diagnostic_item(sym::Result) && let opt_ty = cx.tcx.type_of(opt_defid).skip_binder() && iter_item_ty.ty_adt_def() == opt_ty.ty_adt_def() - && is_method(cx, filter_arg, sym::Result, sym!(is_ok), &[]) + && is_method(cx, filter_arg, sym::Result, sym::is_ok, &[]) { return Some(FilterType::IsOk); } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs index 94415fc91061e..c88462129af02 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs @@ -1,12 +1,12 @@ use super::ITER_KV_MAP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::pat_is_wild; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{pat_is_wild, sym}; use rustc_hir::{Body, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::Symbol; /// lint use of: /// @@ -16,13 +16,13 @@ use rustc_span::sym; /// on `HashMaps` and `BTreeMaps` in std pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - map_type: &'tcx str, // iter / into_iter + map_type: Symbol, // iter / into_iter expr: &'tcx Expr<'tcx>, // .iter().map(|(_, v_| v)) recv: &'tcx Expr<'tcx>, // hashmap m_arg: &'tcx Expr<'tcx>, // |(_, v)| v msrv: Msrv, ) { - if map_type == "into_iter" && !msrv.meets(cx, msrvs::INTO_KEYS) { + if map_type == sym::into_iter && !msrv.meets(cx, msrvs::INTO_KEYS) { return; } if !expr.span.from_expansion() @@ -37,12 +37,12 @@ pub(super) fn check<'tcx>( (PatKind::Binding(ann, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", ann, key), _ => return, } - && let ty = cx.typeck_results().expr_ty(recv) + && let ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) { let mut applicability = rustc_errors::Applicability::MachineApplicable; let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); - let into_prefix = if map_type == "into_iter" { "into_" } else { "" }; + let into_prefix = if map_type == sym::into_iter { "into_" } else { "" }; if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind && let [local_ident] = path.segments diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs index 82bda5d951225..1fdbd81bf240e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sym; use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use super::ITER_NTH; @@ -12,7 +12,7 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_recv: &'tcx hir::Expr<'tcx>, - iter_method: &str, + iter_method: Symbol, iter_span: Span, nth_span: Span, ) -> bool { @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( expr.span, format!("called `.{iter_method}().nth()` on a {caller_type}"), |diag| { - let get_method = if iter_method == "iter_mut" { "get_mut" } else { "get" }; + let get_method = if iter_method == sym::iter_mut { "get_mut" } else { "get" }; diag.span_suggestion_verbose( iter_span.to(nth_span), format!("`{get_method}` is equivalent but more concise"), diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 9d562f5e51d2c..c0366765234f9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -2,7 +2,7 @@ use std::iter::once; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core}; +use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -10,6 +10,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirId; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; +use rustc_span::Symbol; use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS}; @@ -51,7 +52,7 @@ fn is_arg_ty_unified_in_fn<'tcx>( .any(|(i, ty)| i != arg_id_in_args && ty.skip_binder().walk().any(|arg| arg.as_type() == Some(arg_ty_in_args))) } -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: &str, recv: &'tcx Expr<'tcx>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, recv: &'tcx Expr<'tcx>) { let item = match recv.kind { ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), @@ -60,9 +61,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method _ => return, }; let iter_type = match method_name { - "iter" => IterType::Iter, - "iter_mut" => IterType::IterMut, - "into_iter" => IterType::IntoIter, + sym::iter => IterType::Iter, + sym::iter_mut => IterType::IterMut, + sym::into_iter => IterType::IntoIter, _ => return, }; diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs index a80977459f21d..f5fe4316eb0d3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -8,7 +8,7 @@ use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, Pl use rustc_lint::LateContext; use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty::{self, BorrowKind}; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use super::ITER_OVEREAGER_CLONED; use crate::redundant_clone::REDUNDANT_CLONE; @@ -26,7 +26,7 @@ pub(super) enum Op<'a> { // later `.cloned()` // and add `&` to the parameter of closure parameter // e.g. `find` `filter` - FixClosure(&'a str, &'a Expr<'a>), + FixClosure(Symbol, &'a Expr<'a>), // later `.cloned()` // e.g. `skip` `take` @@ -48,7 +48,7 @@ pub(super) fn check<'tcx>( && let Some(method_id) = typeck.type_dependent_def_id(cloned_call.hir_id) && cx.tcx.trait_of_item(method_id) == Some(iter_id) && let cloned_recv_ty = typeck.expr_ty_adjusted(cloned_recv) - && let Some(iter_assoc_ty) = cx.get_associated_type(cloned_recv_ty, iter_id, "Item") + && let Some(iter_assoc_ty) = cx.get_associated_type(cloned_recv_ty, iter_id, sym::Item) && matches!(*iter_assoc_ty.kind(), ty::Ref(_, ty, _) if !is_copy(cx, ty)) { if needs_into_iter @@ -147,6 +147,8 @@ impl<'tcx> Delegate<'tcx> for MoveDelegate { } } + fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + fn borrow(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: BorrowKind) {} fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} diff --git a/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs index 9b358235a40df..90d5d9df55eed 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -8,14 +8,14 @@ use rustc_span::sym; use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { - if is_trait_method(cx, expr, sym::Iterator) { - if let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) { - span_lint( - cx, - ITERATOR_STEP_BY_ZERO, - expr.span, - "`Iterator::step_by(0)` will panic at runtime", - ); - } + if is_trait_method(cx, expr, sym::Iterator) + && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) + { + span_lint( + cx, + ITERATOR_STEP_BY_ZERO, + expr.span, + "`Iterator::step_by(0)` will panic at runtime", + ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs index 0274e31b4c338..a8445b68dd608 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; +use clippy_utils::{get_parent_expr, sym}; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::edition::Edition::Edition2021; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; use super::MANUAL_C_STR_LITERALS; @@ -71,15 +71,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args && cx.tcx.sess.edition() >= Edition2021 && msrv.meets(cx, msrvs::C_STR_LITERALS) { - match fn_name.as_str() { - name @ ("from_bytes_with_nul" | "from_bytes_with_nul_unchecked") + match fn_name { + sym::from_bytes_with_nul | sym::from_bytes_with_nul_unchecked if !arg.span.from_expansion() && let ExprKind::Lit(lit) = arg.kind && let LitKind::ByteStr(_, StrStyle::Cooked) | LitKind::Str(_, StrStyle::Cooked) = lit.node => { - check_from_bytes(cx, expr, arg, name); + check_from_bytes(cx, expr, arg, fn_name); }, - "from_ptr" => check_from_ptr(cx, expr, arg), + sym::from_ptr => check_from_ptr(cx, expr, arg), _ => {}, } } @@ -106,13 +106,13 @@ fn check_from_ptr(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) { } } /// Checks `CStr::from_bytes_with_nul(b"foo\0")` -fn check_from_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, method: &str) { +fn check_from_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, method: Symbol) { let (span, applicability) = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::MethodCall(method, ..) = parent.kind && [sym::unwrap, sym::expect].contains(&method.ident.name) { (parent.span, Applicability::MachineApplicable) - } else if method == "from_bytes_with_nul_unchecked" { + } else if method == sym::from_bytes_with_nul_unchecked { // `*_unchecked` returns `&CStr` directly, nothing needs to be changed (expr.span, Applicability::MachineApplicable) } else { @@ -168,7 +168,7 @@ fn rewrite_as_cstr(cx: &LateContext<'_>, span: Span) -> Option { fn get_cast_target<'tcx>(e: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { match &e.kind { - ExprKind::MethodCall(method, receiver, [], _) if method.ident.as_str() == "cast" => Some(receiver), + ExprKind::MethodCall(method, receiver, [], _) if method.ident.name == sym::cast => Some(receiver), ExprKind::Cast(expr, _) => Some(expr), _ => None, } diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs index 173ebcb7020b7..21f2ce8b7c900 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs @@ -3,18 +3,18 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_span::{DUMMY_SP, Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, Span, Symbol}; use super::MANUAL_INSPECT; #[expect(clippy::too_many_lines)] -pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: &str, name_span: Span, msrv: Msrv) { +pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: Symbol, name_span: Span, msrv: Msrv) { if let ExprKind::Closure(c) = arg.kind && matches!(c.kind, ClosureKind::Closure) && let typeck = cx.typeck_results() @@ -101,7 +101,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: UseKind::Return(s) => edits.push((s.with_leading_whitespace(cx).with_ctxt(s.ctxt()), String::new())), UseKind::Borrowed(s) => { #[expect(clippy::range_plus_one)] - let range = s.map_range(cx, |src, range| { + let range = s.map_range(cx, |_, src, range| { let src = src.get(range.clone())?; let trimmed = src.trim_start_matches([' ', '\t', '\n', '\r', '(']); trimmed.starts_with('&').then(|| { @@ -168,8 +168,8 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: edits.extend(addr_of_edits); } let edit = match name { - "map" => "inspect", - "map_err" => "inspect_err", + sym::map => "inspect", + sym::map_err => "inspect_err", _ => return, }; edits.push((name_span, edit.to_string())); diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 13918ed11b87d..e2df8ce1513c8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{match_def_path, path_def_id}; +use clippy_utils::{path_res, sym}; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def::Res; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; @@ -79,16 +80,15 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { } let ty = cx.typeck_results().expr_ty(expr); - let ty_str = ty.to_string(); - // `std::T::MAX` `std::T::MIN` constants - if let Some(id) = path_def_id(cx, expr) { - if match_def_path(cx, id, &["core", &ty_str, "MAX"]) { - return Some(MinMax::Max); - } - - if match_def_path(cx, id, &["core", &ty_str, "MIN"]) { - return Some(MinMax::Min); + // `T::MAX` and `T::MIN` constants + if let hir::ExprKind::Path(hir::QPath::TypeRelative(base, seg)) = expr.kind + && let Res::PrimTy(_) = path_res(cx, base) + { + match seg.ident.name { + sym::MAX => return Some(MinMax::Max), + sym::MIN => return Some(MinMax::Min), + _ => {}, } } @@ -106,15 +106,15 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { }; let check_lit = |expr: &hir::Expr<'_>, check_min: bool| { - if let hir::ExprKind::Lit(lit) = &expr.kind { - if let ast::LitKind::Int(value, _) = lit.node { - if value == maxval { - return Some(MinMax::Max); - } - - if check_min && value == minval { - return Some(MinMax::Min); - } + if let hir::ExprKind::Lit(lit) = &expr.kind + && let ast::LitKind::Int(value, _) = lit.node + { + if value == maxval { + return Some(MinMax::Max); + } + + if check_min && value == minval { + return Some(MinMax::Min); } } @@ -125,10 +125,10 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { return r; } - if ty.is_signed() { - if let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind { - return check_lit(val, true); - } + if ty.is_signed() + && let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind + { + return check_lit(val, true); } None diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs index 098721dc046f8..8167e4f960534 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs @@ -77,7 +77,7 @@ pub(super) fn check( s @ Cow::Borrowed(_) => s, }, RepeatKind::String => Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app) - .maybe_par() + .maybe_paren() .to_string() .into(), }; diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs index 128b3695f48b7..333a33f7527d6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -51,19 +51,19 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_ let closure_expr = peel_blocks(closure_body.value); match closure_body.params[0].pat.kind { hir::PatKind::Ref(inner, Mutability::Not) => { - if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind { - if ident_eq(name, closure_expr) { - lint_explicit_closure(cx, e.span, recv.span, true, msrv); - } + if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind + && ident_eq(name, closure_expr) + { + lint_explicit_closure(cx, e.span, recv.span, true, msrv); } }, hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) => { match closure_expr.kind { hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { - if ident_eq(name, inner) { - if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { - lint_explicit_closure(cx, e.span, recv.span, true, msrv); - } + if ident_eq(name, inner) + && let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() + { + lint_explicit_closure(cx, e.span, recv.span, true, msrv); } }, hir::ExprKind::MethodCall(method, obj, [], _) => { @@ -114,19 +114,17 @@ fn handle_path( ) { if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() && cx.tcx.lang_items().get(LangItem::CloneFn) == Some(path_def_id) - { // The `copied` and `cloned` methods are only available on `&T` and `&mut T` in `Option` // and `Result`. - if let ty::Adt(_, args) = cx.typeck_results().expr_ty(recv).kind() - && let args = args.as_slice() - && let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type()) - && let ty::Ref(_, ty, Mutability::Not) = ty.kind() - && let ty::FnDef(_, lst) = cx.typeck_results().expr_ty(arg).kind() - && lst.iter().all(|l| l.as_type() == Some(*ty)) - && !should_call_clone_as_function(cx, *ty) - { - lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); - } + && let ty::Adt(_, args) = cx.typeck_results().expr_ty(recv).kind() + && let args = args.as_slice() + && let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type()) + && let ty::Ref(_, ty, Mutability::Not) = ty.kind() + && let ty::FnDef(_, lst) = cx.typeck_results().expr_ty(arg).kind() + && lst.iter().all(|l| l.as_type() == Some(*ty)) + && !should_call_clone_as_function(cx, *ty) + { + lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs index 0536014465731..98def66ca1499 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -5,7 +5,7 @@ use rustc_ast::BindingMode; use rustc_errors::Applicability; use rustc_hir::{self as hir, Node, PatKind}; use rustc_lint::LateContext; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; use super::MAP_IDENTITY; @@ -14,7 +14,7 @@ pub(super) fn check( expr: &hir::Expr<'_>, caller: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, - name: &str, + name: Symbol, _map_span: Span, ) { let caller_ty = cx.typeck_results().expr_ty(caller); diff --git a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs index 6cf0936c598fa..a2a522a60687d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs @@ -41,7 +41,7 @@ fn extract_count_with_applicability( return Some(format!("{count}")); } let end_snippet = Sugg::hir_with_applicability(cx, end, "...", applicability) - .maybe_par() + .maybe_paren() .into_string(); if lower_bound == 0 { if range.limits == RangeLimits::Closed { diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 94d3657d9f123..d2d59f0013c0c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -114,6 +114,7 @@ mod suspicious_command_arg_space; mod suspicious_map; mod suspicious_splitn; mod suspicious_to_owned; +mod swap_with_temporary; mod type_id_on_box; mod unbuffered_bytes; mod uninit_assumed_init; @@ -149,7 +150,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty}; +use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; @@ -158,7 +159,7 @@ use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, kw}; declare_clippy_lint! { /// ### What it does @@ -478,9 +479,6 @@ declare_clippy_lint! { /// Because you usually call `expect()` on the `Result` /// directly to get a better error message. /// - /// ### Known problems - /// The error type needs to implement `Debug` - /// /// ### Example /// ```no_run /// # let x = Ok::<_, ()>(()); @@ -2429,7 +2427,7 @@ declare_clippy_lint! { /// /// ### Limitations /// This lint currently only looks for usages of - /// `.then_some(..).unwrap_or(..)` and `.then(..).unwrap_or(..)`, but will be expanded + /// `.{then, then_some}(..).{unwrap_or, unwrap_or_else, unwrap_or_default}(..)`, but will be expanded /// to account for similar patterns. /// /// ### Example @@ -4286,7 +4284,7 @@ declare_clippy_lint! { /// ```no_run /// let last_arg = "echo hello world".split(' ').next_back(); /// ``` - #[clippy::version = "1.85.0"] + #[clippy::version = "1.86.0"] pub DOUBLE_ENDED_ITERATOR_LAST, perf, "using `Iterator::last` on a `DoubleEndedIterator`" @@ -4372,11 +4370,10 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// - /// Detect functions that end with `Option::and_then` or `Result::and_then`, and suggest using a question mark (`?`) instead. + /// Detect functions that end with `Option::and_then` or `Result::and_then`, and suggest using + /// the `?` operator instead. /// /// ### Why is this bad? - /// /// The `and_then` method is used to chain a computation that returns an `Option` or a `Result`. /// This can be replaced with the `?` operator, which is more concise and idiomatic. /// @@ -4431,7 +4428,7 @@ declare_clippy_lint! { /// let file = BufReader::new(std::fs::File::open("./bytes.txt").unwrap()); /// file.bytes(); /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub UNBUFFERED_BYTES, perf, "calling .bytes() is very inefficient when data is not in memory" @@ -4447,16 +4444,16 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// fn foo(values: &[u8]) -> bool { - /// values.iter().any(|&v| v == 10) + /// values.iter().any(|&v| v == 10) /// } /// ``` /// Use instead: /// ```no_run /// fn foo(values: &[u8]) -> bool { - /// values.contains(&10) + /// values.contains(&10) /// } /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub MANUAL_CONTAINS, perf, "unnecessary `iter().any()` on slices that can be replaced with `contains()`" @@ -4478,12 +4475,59 @@ declare_clippy_lint! { /// ```no_run /// let _ = std::io::Error::other("bad".to_string()); /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub IO_OTHER_ERROR, style, "calling `std::io::Error::new(std::io::ErrorKind::Other, _)`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `std::mem::swap` with temporary values. + /// + /// ### Why is this bad? + /// Storing a new value in place of a temporary value which will + /// be dropped right after the `swap` is an inefficient way of performing + /// an assignment. The same result can be achieved by using a regular + /// assignment. + /// + /// ### Examples + /// ```no_run + /// fn replace_string(s: &mut String) { + /// std::mem::swap(s, &mut String::from("replaced")); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn replace_string(s: &mut String) { + /// *s = String::from("replaced"); + /// } + /// ``` + /// + /// Also, swapping two temporary values has no effect, as they will + /// both be dropped right after swapping them. This is likely an indication + /// of a bug. For example, the following code swaps the references to + /// the last element of the vectors, instead of swapping the elements + /// themselves: + /// + /// ```no_run + /// fn bug(v1: &mut [i32], v2: &mut [i32]) { + /// // Incorrect: swapping temporary references (`&mut &mut` passed to swap) + /// std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn correct(v1: &mut [i32], v2: &mut [i32]) { + /// std::mem::swap(v1.last_mut().unwrap(), v2.last_mut().unwrap()); + /// } + /// ``` + #[clippy::version = "1.88.0"] + pub SWAP_WITH_TEMPORARY, + complexity, + "detect swap with a temporary value" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4661,19 +4705,21 @@ impl_lint_pass!(Methods => [ UNBUFFERED_BYTES, MANUAL_CONTAINS, IO_OTHER_ERROR, + SWAP_WITH_TEMPORARY, ]); /// Extracts a method call name, args, and `Span` of the method name. -pub fn method_call<'tcx>( - recv: &'tcx Expr<'tcx>, -) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { - if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind { - if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() { - let name = path.ident.name.as_str(); - return Some((name, receiver, args, path.ident.span, call_span)); - } +/// This ensures that neither the receiver nor any of the arguments +/// come from expansion. +pub fn method_call<'tcx>(recv: &'tcx Expr<'tcx>) -> Option<(Symbol, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { + if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind + && !args.iter().any(|e| e.span.from_expansion()) + && !receiver.span.from_expansion() + { + Some((path.ident.name, receiver, args, path.ident.span, call_span)) + } else { + None } - None } impl<'tcx> LateLintPass<'tcx> for Methods { @@ -4691,16 +4737,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods { manual_c_str_literals::check(cx, expr, func, args, self.msrv); useless_nonzero_new_unchecked::check(cx, expr, func, args, self.msrv); io_other_error::check(cx, expr, func, args, self.msrv); + swap_with_temporary::check(cx, expr, func, args); }, ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; - or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args); + or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args); expect_fun_call::check( cx, &self.format_args, expr, method_span, - method_call.ident.as_str(), + method_call.ident.name, receiver, args, ); @@ -4729,9 +4776,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; } - let name = impl_item.ident.name.as_str(); + let name = impl_item.ident.name; let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; - let item = cx.tcx.hir().expect_item(parent); + let item = cx.tcx.hir_expect_item(parent); let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); @@ -4802,7 +4849,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - if name == "new" && ret_ty != self_ty { + if name == sym::new && ret_ty != self_ty { span_lint( cx, NEW_RET_NO_SELF, @@ -4832,7 +4879,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); wrong_self_convention::check( cx, - item.ident.name.as_str(), + item.ident.name, self_ty, first_arg_ty, first_arg_hir_ty.span, @@ -4860,16 +4907,20 @@ impl<'tcx> LateLintPass<'tcx> for Methods { impl Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // Handle method calls whose receiver and arguments may not come from expansion if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { - ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { + ( + sym::add | sym::offset | sym::sub | sym::wrapping_offset | sym::wrapping_add | sym::wrapping_sub, + [_arg], + ) => { zst_offset::check(cx, expr, recv); }, - ("all", [arg]) => { + (sym::all, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); needless_character_iteration::check(cx, expr, recv, arg, true); match method_call(recv) { - Some(("cloned", recv2, [], _, _)) => { + Some((sym::cloned, recv2, [], _, _)) => { iter_overeager_cloned::check( cx, expr, @@ -4879,13 +4930,13 @@ impl Methods { false, ); }, - Some(("map", _, [map_arg], _, map_call_span)) => { + Some((sym::map, _, [map_arg], _, map_call_span)) => { map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "all"); }, _ => {}, } }, - ("and_then", [arg]) => { + (sym::and_then, [arg]) => { let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg); let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg); if !biom_option_linted && !biom_result_linted { @@ -4895,11 +4946,11 @@ impl Methods { } } }, - ("any", [arg]) => { + (sym::any, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); needless_character_iteration::check(cx, expr, recv, arg, false); match method_call(recv) { - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -4907,80 +4958,79 @@ impl Methods { iter_overeager_cloned::Op::NeedlessMove(arg), false, ), - Some(("chars", recv, _, _, _)) + Some((sym::chars, recv, _, _, _)) if let ExprKind::Closure(arg) = arg.kind && let body = cx.tcx.hir_body(arg.body) && let [param] = body.params => { string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), self.msrv); }, - Some(("map", _, [map_arg], _, map_call_span)) => { + Some((sym::map, _, [map_arg], _, map_call_span)) => { map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "any"); }, - Some(("iter", iter_recv, ..)) => { + Some((sym::iter, iter_recv, ..)) => { manual_contains::check(cx, expr, iter_recv, arg); }, _ => {}, } }, - ("arg", [arg]) => { + (sym::arg, [arg]) => { suspicious_command_arg_space::check(cx, recv, arg, span); }, - ("as_deref" | "as_deref_mut", []) => { + (sym::as_deref | sym::as_deref_mut, []) => { needless_option_as_deref::check(cx, expr, recv, name); }, - ("as_bytes", []) => { - if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { + (sym::as_bytes, []) => { + if let Some((sym::as_str, recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); } sliced_string_as_bytes::check(cx, expr, recv); }, - ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), - ("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, self.msrv), - ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), - ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), - ("bytes", []) => unbuffered_bytes::check(cx, expr, recv), - ("cloned", []) => { + (sym::as_mut | sym::as_ref, []) => useless_asref::check(cx, expr, name, recv), + (sym::as_ptr, []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, self.msrv), + (sym::assume_init, []) => uninit_assumed_init::check(cx, expr, recv), + (sym::bytes, []) => unbuffered_bytes::check(cx, expr, recv), + (sym::cloned, []) => { cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv); option_as_ref_cloned::check(cx, recv, span); }, - ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { + (sym::collect, []) if is_trait_method(cx, expr, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); match method_call(recv) { - Some((name @ ("cloned" | "copied"), recv2, [], _, _)) => { + Some((name @ (sym::cloned | sym::copied), recv2, [], _, _)) => { iter_cloned_collect::check(cx, name, expr, recv2); }, - Some(("map", m_recv, [m_arg], m_ident_span, _)) => { + Some((sym::map, m_recv, [m_arg], m_ident_span, _)) => { map_collect_result_unit::check(cx, expr, m_recv, m_arg); format_collect::check(cx, expr, m_arg, m_ident_span); }, - Some(("take", take_self_arg, [take_arg], _, _)) => { + Some((sym::take, take_self_arg, [take_arg], _, _)) => { if self.msrv.meets(cx, msrvs::STR_REPEAT) { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } }, - Some(("drain", recv, args, ..)) => { + Some((sym::drain, recv, args, ..)) => { drain_collect::check(cx, args, expr, recv); }, _ => {}, } }, - ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { - Some(("cloned", recv2, [], _, _)) => { + (sym::count, []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { + Some((sym::cloned, recv2, [], _, _)) => { iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned, false); }, - Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => { + Some((name2 @ (sym::into_iter | sym::iter | sym::iter_mut), recv2, [], _, _)) => { iter_count::check(cx, expr, recv2, name2); }, - Some(("map", _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), - Some(("filter", recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), - Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), + Some((sym::map, _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), + Some((sym::filter, recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), + Some((sym::bytes, recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), _ => {}, }, - ("min" | "max", [arg]) => { + (sym::min | sym::max, [arg]) => { unnecessary_min_or_max::check(cx, expr, name, recv, arg); }, - ("drain", ..) => { + (sym::drain, ..) => { if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.parent_hir_node(expr.hir_id) && matches!(kind, StmtKind::Semi(_)) && args.len() <= 1 @@ -4990,48 +5040,31 @@ impl Methods { iter_with_drain::check(cx, expr, recv, span, arg); } }, - ("ends_with", [arg]) => { + (sym::ends_with, [arg]) => { if let ExprKind::MethodCall(.., span) = expr.kind { - case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg); + case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg, self.msrv); } path_ends_with_ext::check(cx, recv, arg, expr, self.msrv, &self.allowed_dotfiles); }, - ("expect", [_]) => { + (sym::expect, [_]) => { match method_call(recv) { - Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv), - Some(("err", recv, [], err_span, _)) => { + Some((sym::ok, recv, [], _, _)) => ok_expect::check(cx, expr, recv), + Some((sym::err, recv, [], err_span, _)) => { err_expect::check(cx, expr, recv, span, err_span, self.msrv); }, - _ => unwrap_expect_used::check( - cx, - expr, - recv, - false, - self.allow_expect_in_consts, - self.allow_expect_in_tests, - unwrap_expect_used::Variant::Expect, - ), + _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("expect_err", [_]) => { + (sym::expect_err, [_]) | (sym::unwrap_err | sym::unwrap_unchecked | sym::unwrap_err_unchecked, []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_expect_used::check( - cx, - expr, - recv, - true, - self.allow_expect_in_consts, - self.allow_expect_in_tests, - unwrap_expect_used::Variant::Expect, - ); }, - ("extend", [arg]) => { + (sym::extend, [arg]) => { string_extend_chars::check(cx, expr, recv, arg); extend_with_drain::check(cx, expr, recv, arg); }, - ("filter", [arg]) => { - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + (sym::filter, [arg]) => { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { // if `arg` has side-effect, the semantic will change iter_overeager_cloned::check( cx, @@ -5047,8 +5080,8 @@ impl Methods { iter_filter::check(cx, expr, arg, span); } }, - ("find", [arg]) => { - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + (sym::find, [arg]) => { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { // if `arg` has side-effect, the semantic will change iter_overeager_cloned::check( cx, @@ -5060,26 +5093,26 @@ impl Methods { ); } }, - ("filter_map", [arg]) => { + (sym::filter_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); unnecessary_filter_map::check(cx, expr, arg, name); filter_map_bool_then::check(cx, expr, arg, call_span); filter_map_identity::check(cx, expr, arg, span); }, - ("find_map", [arg]) => { + (sym::find_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); unnecessary_filter_map::check(cx, expr, arg, name); }, - ("flat_map", [arg]) => { + (sym::flat_map, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); flat_map_identity::check(cx, expr, arg, span); flat_map_option::check(cx, expr, arg, span); }, - ("flatten", []) => match method_call(recv) { - Some(("map", recv, [map_arg], map_span, _)) => { + (sym::flatten, []) => match method_call(recv) { + Some((sym::map, recv, [map_arg], map_span, _)) => { map_flatten::check(cx, expr, recv, map_arg, map_span); }, - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -5089,15 +5122,15 @@ impl Methods { ), _ => {}, }, - ("fold", [init, acc]) => { + (sym::fold, [init, acc]) => { manual_try_fold::check(cx, expr, init, acc, call_span, self.msrv); unnecessary_fold::check(cx, expr, init, acc, span); }, - ("for_each", [arg]) => { + (sym::for_each, [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); match method_call(recv) { - Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2), - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + Some((sym::inspect, _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2), + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -5108,44 +5141,44 @@ impl Methods { _ => {}, } }, - ("get", [arg]) => { + (sym::get, [arg]) => { get_first::check(cx, expr, recv, arg); get_last_with_len::check(cx, expr, recv, arg); }, - ("get_or_insert_with", [arg]) => { + (sym::get_or_insert_with, [arg]) => { unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"); }, - ("hash", [arg]) => { + (sym::hash, [arg]) => { unit_hash::check(cx, expr, recv, arg); }, - ("is_empty", []) => { + (sym::is_empty, []) => { match method_call(recv) { - Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) => { - needless_as_bytes::check(cx, prev_method, "is_empty", prev_recv, expr.span); + Some((prev_method @ (sym::as_bytes | sym::bytes), prev_recv, [], _, _)) => { + needless_as_bytes::check(cx, prev_method, name, prev_recv, expr.span); }, - Some(("as_str", recv, [], as_str_span, _)) => { + Some((sym::as_str, recv, [], as_str_span, _)) => { redundant_as_str::check(cx, expr, recv, as_str_span, span); }, _ => {}, } is_empty::check(cx, expr, recv); }, - ("is_file", []) => filetype_is_file::check(cx, expr, recv), - ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), - ("is_none", []) => check_is_some_is_none(cx, expr, recv, call_span, false), - ("is_some", []) => check_is_some_is_none(cx, expr, recv, call_span, true), - ("iter" | "iter_mut" | "into_iter", []) => { + (sym::is_file, []) => filetype_is_file::check(cx, expr, recv), + (sym::is_digit, [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), + (sym::is_none, []) => check_is_some_is_none(cx, expr, recv, call_span, false), + (sym::is_some, []) => check_is_some_is_none(cx, expr, recv, call_span, true), + (sym::iter | sym::iter_mut | sym::into_iter, []) => { iter_on_single_or_empty_collections::check(cx, expr, name, recv); }, - ("join", [join_arg]) => { - if let Some(("collect", _, _, span, _)) = method_call(recv) { + (sym::join, [join_arg]) => { + if let Some((sym::collect, _, _, span, _)) = method_call(recv) { unnecessary_join::check(cx, expr, recv, join_arg, span); } else { join_absolute_paths::check(cx, recv, join_arg, expr.span); } }, - ("last", []) => { - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + (sym::last, []) => { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check( cx, expr, @@ -5157,24 +5190,24 @@ impl Methods { } double_ended_iterator_last::check(cx, expr, recv, call_span); }, - ("len", []) => { - if let Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) = method_call(recv) { - needless_as_bytes::check(cx, prev_method, "len", prev_recv, expr.span); + (sym::len, []) => { + if let Some((prev_method @ (sym::as_bytes | sym::bytes), prev_recv, [], _, _)) = method_call(recv) { + needless_as_bytes::check(cx, prev_method, sym::len, prev_recv, expr.span); } }, - ("lock", []) => { + (sym::lock, []) => { mut_mutex_lock::check(cx, expr, recv, span); }, - (name @ ("map" | "map_err"), [m_arg]) => { - if name == "map" { + (name @ (sym::map | sym::map_err), [m_arg]) => { + if name == sym::map { unused_enumerate_index::check(cx, expr, recv, m_arg); map_clone::check(cx, expr, recv, m_arg, self.msrv); map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, self.msrv, span); match method_call(recv) { - Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => { + Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) => { iter_kv_map::check(cx, map_name, expr, recv2, m_arg, self.msrv); }, - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -5189,12 +5222,12 @@ impl Methods { } if let Some((name, recv2, args, span2, _)) = method_call(recv) { match (name, args) { - ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), - ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), - ("filter", [f_arg]) => { + (sym::as_mut, []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), + (sym::as_ref, []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), + (sym::filter, [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); }, - ("find", [f_arg]) => { + (sym::find, [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true); }, _ => {}, @@ -5204,22 +5237,22 @@ impl Methods { manual_inspect::check(cx, expr, m_arg, name, span, self.msrv); crate::useless_conversion::check_function_application(cx, expr, recv, m_arg); }, - ("map_break" | "map_continue", [m_arg]) => { + (sym::map_break | sym::map_continue, [m_arg]) => { crate::useless_conversion::check_function_application(cx, expr, recv, m_arg); }, - ("map_or", [def, map]) => { + (sym::map_or, [def, map]) => { option_map_or_none::check(cx, expr, recv, def, map); manual_ok_or::check(cx, expr, recv, def, map); unnecessary_map_or::check(cx, expr, recv, def, map, span, self.msrv); }, - ("map_or_else", [def, map]) => { + (sym::map_or_else, [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); unnecessary_result_map_or_else::check(cx, expr, recv, def, map); }, - ("next", []) => { + (sym::next, []) => { if let Some((name2, recv2, args2, _, _)) = method_call(recv) { match (name2, args2) { - ("cloned", []) => iter_overeager_cloned::check( + (sym::cloned, []) => iter_overeager_cloned::check( cx, expr, recv, @@ -5227,19 +5260,19 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), - ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), - ("iter", []) => iter_next_slice::check(cx, expr, recv2), - ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), - ("skip_while", [_]) => skip_while_next::check(cx, expr), - ("rev", []) => manual_next_back::check(cx, expr, recv, recv2), + (sym::filter, [arg]) => filter_next::check(cx, expr, recv2, arg), + (sym::filter_map, [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), + (sym::iter, []) => iter_next_slice::check(cx, expr, recv2), + (sym::skip, [arg]) => iter_skip_next::check(cx, expr, recv2, arg), + (sym::skip_while, [_]) => skip_while_next::check(cx, expr), + (sym::rev, []) => manual_next_back::check(cx, expr, recv, recv2), _ => {}, } } }, - ("nth", [n_arg]) => match method_call(recv) { - Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + (sym::nth, [n_arg]) => match method_call(recv) { + Some((sym::bytes, recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, expr, recv, @@ -5247,54 +5280,54 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - Some((iter_method @ ("iter" | "iter_mut"), iter_recv, [], iter_span, _)) => { + Some((iter_method @ (sym::iter | sym::iter_mut), iter_recv, [], iter_span, _)) => { if !iter_nth::check(cx, expr, iter_recv, iter_method, iter_span, span) { iter_nth_zero::check(cx, expr, recv, n_arg); } }, _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, - ("ok_or_else", [arg]) => { + (sym::ok_or_else, [arg]) => { unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); }, - ("open", [_]) => { + (sym::open, [_]) => { open_options::check(cx, expr, recv); }, - ("or_else", [arg]) => { + (sym::or_else, [arg]) => { if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) { unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } }, - ("push", [arg]) => { + (sym::push, [arg]) => { path_buf_push_overwrite::check(cx, expr, arg); }, - ("read_to_end", [_]) => { + (sym::read_to_end, [_]) => { verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG); }, - ("read_to_string", [_]) => { + (sym::read_to_string, [_]) => { verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG); }, - ("read_line", [arg]) => { + (sym::read_line, [arg]) => { read_line_without_trim::check(cx, expr, recv, arg); }, - ("repeat", [arg]) => { + (sym::repeat, [arg]) => { repeat_once::check(cx, expr, recv, arg); }, - (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => { + (name @ (sym::replace | sym::replacen), [arg1, arg2] | [arg1, arg2, _]) => { no_effect_replace::check(cx, expr, arg1, arg2); // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint if self.msrv.meets(cx, msrvs::PATTERN_TRAIT_CHAR_ARRAY) - && name == "replace" - && let Some(("replace", ..)) = method_call(recv) + && name == sym::replace + && let Some((sym::replace, ..)) = method_call(recv) { collapsible_str_replace::check(cx, expr, arg1, arg2); } }, - ("resize", [count_arg, default_arg]) => { + (sym::resize, [count_arg, default_arg]) => { vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span); }, - ("seek", [arg]) => { + (sym::seek, [arg]) => { if self.msrv.meets(cx, msrvs::SEEK_FROM_CURRENT) { seek_from_current::check(cx, expr, recv, arg); } @@ -5302,11 +5335,11 @@ impl Methods { seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span); } }, - ("skip", [arg]) => { + (sym::skip, [arg]) => { iter_skip_zero::check(cx, expr, arg); iter_out_of_bounds::check_skip(cx, expr, recv, arg); - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check( cx, expr, @@ -5317,34 +5350,34 @@ impl Methods { ); } }, - ("sort", []) => { + (sym::sort, []) => { stable_sort_primitive::check(cx, expr, recv); }, - ("sort_by", [arg]) => { + (sym::sort_by, [arg]) => { unnecessary_sort_by::check(cx, expr, recv, arg, false); }, - ("sort_unstable_by", [arg]) => { + (sym::sort_unstable_by, [arg]) => { unnecessary_sort_by::check(cx, expr, recv, arg, true); }, - ("split", [arg]) => { + (sym::split, [arg]) => { str_split::check(cx, expr, recv, arg); }, - ("splitn" | "rsplitn", [count_arg, pat_arg]) => { + (sym::splitn | sym::rsplitn, [count_arg, pat_arg]) => { if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv); } }, - ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { + (sym::splitn_mut | sym::rsplitn_mut, [count_arg, _]) => { if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); } }, - ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), - ("take", [arg]) => { + (sym::step_by, [arg]) => iterator_step_by_zero::check(cx, expr, arg), + (sym::take, [arg]) => { iter_out_of_bounds::check_take(cx, expr, recv, arg); manual_repeat_n::check(cx, expr, recv, arg, self.msrv); - if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check( cx, expr, @@ -5355,93 +5388,98 @@ impl Methods { ); } }, - ("take", []) => needless_option_take::check(cx, expr, recv), - ("then", [arg]) => { + (sym::take, []) => needless_option_take::check(cx, expr, recv), + (sym::then, [arg]) => { if !self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { return; } unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); }, - ("try_into", []) if is_trait_method(cx, expr, sym::TryInto) => { + (sym::try_into, []) if is_trait_method(cx, expr, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); }, - ("to_owned", []) => { + (sym::to_owned, []) => { if !suspicious_to_owned::check(cx, expr, recv) { implicit_clone::check(cx, name, expr, recv); } }, - ("to_os_string" | "to_path_buf" | "to_vec", []) => { + (sym::to_os_string | sym::to_path_buf | sym::to_vec, []) => { implicit_clone::check(cx, name, expr, recv); }, - ("type_id", []) => { + (sym::type_id, []) => { type_id_on_box::check(cx, recv, expr.span); }, - ("unwrap", []) => { + (sym::unwrap, []) => { match method_call(recv) { - Some(("get", recv, [get_arg], _, _)) => { + Some((sym::get, recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, false); }, - Some(("get_mut", recv, [get_arg], _, _)) => { + Some((sym::get_mut, recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, true); }, - Some(("or", recv, [or_arg], or_span, _)) => { + Some((sym::or, recv, [or_arg], or_span, _)) => { or_then_unwrap::check(cx, expr, recv, or_arg, or_span); }, _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_expect_used::check( - cx, - expr, - recv, - false, - self.allow_unwrap_in_consts, - self.allow_unwrap_in_tests, - unwrap_expect_used::Variant::Unwrap, - ); }, - ("unwrap_err", []) => { - unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_expect_used::check( - cx, - expr, - recv, - true, - self.allow_unwrap_in_consts, - self.allow_unwrap_in_tests, - unwrap_expect_used::Variant::Unwrap, - ); - }, - ("unwrap_or", [u_arg]) => { + (sym::unwrap_or, [u_arg]) => { match method_call(recv) { - Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => { - manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); + Some((arith @ (sym::checked_add | sym::checked_sub | sym::checked_mul), lhs, [rhs], _, _)) => { + manual_saturating_arithmetic::check( + cx, + expr, + lhs, + rhs, + u_arg, + &arith.as_str()[const { "checked_".len() }..], + ); }, - Some(("map", m_recv, [m_arg], span, _)) => { + Some((sym::map, m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv); }, - Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method, "unwrap_or"); + Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { + obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, name); }, _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_or_default", []) => { - if let Some(("map", m_recv, [arg], span, _)) = method_call(recv) { - manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv); + (sym::unwrap_or_default, []) => { + match method_call(recv) { + Some((sym::map, m_recv, [arg], span, _)) => { + manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv); + }, + Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { + obfuscated_if_else::check( + cx, + expr, + t_recv, + t_arg, + None, + then_method, + sym::unwrap_or_default, + ); + }, + _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_unchecked" | "unwrap_err_unchecked", []) => { - unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - }, - ("unwrap_or_else", [u_arg]) => { + (sym::unwrap_or_else, [u_arg]) => { match method_call(recv) { - Some(("map", recv, [map_arg], _, _)) + Some((sym::map, recv, [map_arg], _, _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, - Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method, "unwrap_or_else"); + Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { + obfuscated_if_else::check( + cx, + expr, + t_recv, + t_arg, + Some(u_arg), + then_method, + sym::unwrap_or_else, + ); }, _ => { unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); @@ -5449,13 +5487,13 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("wake", []) => { + (sym::wake, []) => { waker_clone_wake::check(cx, expr, recv); }, - ("write", []) => { + (sym::write, []) => { readonly_write_lock::check(cx, expr, recv); }, - ("zip", [arg]) => { + (sym::zip, [arg]) => { if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind && name.ident.name == sym::iter { @@ -5465,18 +5503,68 @@ impl Methods { _ => {}, } } + // Handle method calls whose receiver and arguments may come from expansion + if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind { + match (path.ident.name, args) { + (sym::expect, [_]) if !matches!(method_call(recv), Some((sym::ok | sym::err, _, [], _, _))) => { + unwrap_expect_used::check( + cx, + expr, + recv, + false, + self.allow_expect_in_consts, + self.allow_expect_in_tests, + unwrap_expect_used::Variant::Expect, + ); + }, + (sym::expect_err, [_]) => { + unwrap_expect_used::check( + cx, + expr, + recv, + true, + self.allow_expect_in_consts, + self.allow_expect_in_tests, + unwrap_expect_used::Variant::Expect, + ); + }, + (sym::unwrap, []) => { + unwrap_expect_used::check( + cx, + expr, + recv, + false, + self.allow_unwrap_in_consts, + self.allow_unwrap_in_tests, + unwrap_expect_used::Variant::Unwrap, + ); + }, + (sym::unwrap_err, []) => { + unwrap_expect_used::check( + cx, + expr, + recv, + true, + self.allow_unwrap_in_consts, + self.allow_unwrap_in_tests, + unwrap_expect_used::Variant::Unwrap, + ); + }, + _ => {}, + } + } } } fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, call_span: Span, is_some: bool) { match method_call(recv) { - Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span, _)) => { + Some((name @ (sym::find | sym::position | sym::rposition), f_recv, [arg], span, _)) => { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); }, - Some(("get", f_recv, [arg], _, _)) => { + Some((sym::get, f_recv, [arg], _, _)) => { unnecessary_get_then_check::check(cx, call_span, recv, f_recv, arg, is_some); }, - Some(("first", f_recv, [], _, _)) => { + Some((sym::first, f_recv, [], _, _)) => { unnecessary_first_then_check::check(cx, call_span, recv, f_recv, is_some); }, _ => {}, @@ -5520,7 +5608,7 @@ const FN_HEADER: hir::FnHeader = hir::FnHeader { struct ShouldImplTraitCase { trait_name: &'static str, - method_name: &'static str, + method_name: Symbol, param_count: usize, fn_header: hir::FnHeader, // implicit self kind expected (none, self, &self, ...) @@ -5533,7 +5621,7 @@ struct ShouldImplTraitCase { impl ShouldImplTraitCase { const fn new( trait_name: &'static str, - method_name: &'static str, + method_name: Symbol, param_count: usize, fn_header: hir::FnHeader, self_kind: SelfKind, @@ -5566,36 +5654,36 @@ impl ShouldImplTraitCase { #[rustfmt::skip] 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), - 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), + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ]; #[derive(Clone, Copy, PartialEq, Eq, Debug)] diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs index 7c9f7bae99063..635d06330e058 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs @@ -4,11 +4,11 @@ use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use super::NEEDLESS_AS_BYTES; -pub fn check(cx: &LateContext<'_>, prev_method: &str, method: &str, prev_recv: &Expr<'_>, span: Span) { +pub fn check(cx: &LateContext<'_>, prev_method: Symbol, method: Symbol, prev_recv: &Expr<'_>, span: Span) { let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs(); if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() { let mut app = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs index 743aacf058856..71c1576cd57d1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -7,9 +7,8 @@ use rustc_span::Span; use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::paths::CHAR_IS_ASCII; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; +use clippy_utils::{is_path_diagnostic_item, path_to_local_id, peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { while let ExprKind::AddrOf(_, _, e) = expr.kind { @@ -32,7 +31,7 @@ fn handle_expr( // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is // `is_ascii`, then only `.all()` should warn. if revert != is_all - && method.ident.name.as_str() == "is_ascii" + && method.ident.name == sym::is_ascii && path_to_local_id(receiver, first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char @@ -76,9 +75,7 @@ fn handle_expr( // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is // `is_ascii`, then only `.all()` should warn. if revert != is_all - && let ExprKind::Path(path) = fn_path.kind - && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id() - && match_def_path(cx, fn_def_id, &CHAR_IS_ASCII) + && is_path_diagnostic_item(cx, fn_path, sym::char_is_ascii) && path_to_local_id(peels_expr_ref(arg), first_param) && let Some(snippet) = before_chars.get_source_text(cx) { @@ -102,7 +99,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, && let body = cx.tcx.hir_body(body) && let Some(first_param) = body.params.first() && let ExprKind::MethodCall(method, mut recv, [], _) = recv.kind - && method.ident.name.as_str() == "chars" + && method.ident.name == sym::chars && let str_ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() && *str_ty.kind() == ty::Str { diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index 45f79dd44f2a4..4c1ed6a1d833e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -1,26 +1,31 @@ +use std::ops::ControlFlow; + use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection}; +use clippy_utils::ty::{ + get_type_diagnostic_name, has_non_owning_mutable_access, make_normalized_projection, make_projection, +}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, - path_to_local_id, + path_to_local_id, sym, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::intravisit::{Visitor, walk_block, walk_expr}; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{ - BindingMode, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, PatKind, Stmt, StmtKind, + BindingMode, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, Pat, PatKind, Stmt, StmtKind, }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, AssocKind, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_middle::ty::{self, AssocTag, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_span::Span; use rustc_span::symbol::Ident; -use rustc_span::{Span, sym}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, name_span: Span, @@ -28,6 +33,11 @@ pub(super) fn check<'tcx>( iter_expr: &'tcx Expr<'tcx>, call_span: Span, ) { + let iter_ty = cx.typeck_results().expr_ty(iter_expr); + if has_non_owning_mutable_access(cx, iter_ty) { + return; // don't lint if the iterator has side effects + } + match cx.tcx.parent_hir_node(collect_expr.hir_id) { Node::Expr(parent) => { check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr); @@ -103,6 +113,12 @@ pub(super) fn check<'tcx>( return; } + if let IterFunctionKind::IntoIter(hir_id) = iter_call.func + && !check_iter_expr_used_only_as_iterator(cx, hir_id, block) + { + return; + } + // Suggest replacing iter_call with iter_replacement, and removing stmt let mut span = MultiSpan::from_span(name_span); span.push_span_label(iter_call.span, "the iterator could be used here instead"); @@ -230,10 +246,10 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) - .instantiate_bound_regions_with_erased(sig.rebind(search_ty)) .kind() && let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) - && let Some(iter_item) = cx.tcx.associated_items(iter_trait).find_by_name_and_kind( + && let Some(iter_item) = cx.tcx.associated_items(iter_trait).find_by_ident_and_kind( cx.tcx, Ident::with_dummy_span(sym::Item), - AssocKind::Type, + AssocTag::Type, iter_trait, ) && let args = cx.tcx.mk_args(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))]) @@ -253,7 +269,7 @@ struct IterFunction { impl IterFunction { fn get_iter_method(&self, cx: &LateContext<'_>) -> String { match &self.func { - IterFunctionKind::IntoIter => String::new(), + IterFunctionKind::IntoIter(_) => String::new(), IterFunctionKind::Len => String::from(".count()"), IterFunctionKind::IsEmpty => String::from(".next().is_none()"), IterFunctionKind::Contains(span) => { @@ -268,7 +284,7 @@ impl IterFunction { } fn get_suggestion_text(&self) -> &'static str { match &self.func { - IterFunctionKind::IntoIter => { + IterFunctionKind::IntoIter(_) => { "use the original Iterator instead of collecting it and then producing a new one" }, IterFunctionKind::Len => { @@ -284,7 +300,7 @@ impl IterFunction { } } enum IterFunctionKind { - IntoIter, + IntoIter(HirId), Len, IsEmpty, Contains(Span), @@ -323,7 +339,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { // Check function calls on our collection if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind { if args.is_empty() - && method_name.ident.name.as_str() == "collect" + && method_name.ident.name == sym::collect && is_trait_method(self.cx, expr, sym::Iterator) { self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv)); @@ -341,20 +357,20 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { if let Some(hir_id) = self.current_statement_hir_id { self.hir_id_uses_map.insert(hir_id, self.uses.len()); } - match method_name.ident.name.as_str() { - "into_iter" => self.uses.push(Some(IterFunction { - func: IterFunctionKind::IntoIter, + match method_name.ident.name { + sym::into_iter => self.uses.push(Some(IterFunction { + func: IterFunctionKind::IntoIter(expr.hir_id), span: expr.span, })), - "len" => self.uses.push(Some(IterFunction { + sym::len => self.uses.push(Some(IterFunction { func: IterFunctionKind::Len, span: expr.span, })), - "is_empty" => self.uses.push(Some(IterFunction { + sym::is_empty => self.uses.push(Some(IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span, })), - "contains" => self.uses.push(Some(IterFunction { + sym::contains => self.uses.push(Some(IterFunction { func: IterFunctionKind::Contains(args[0].span), span: expr.span, })), @@ -369,20 +385,20 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { return; } - if let Some(hir_id) = path_to_local(recv) { - if let Some(index) = self.hir_id_uses_map.remove(&hir_id) { - if self - .illegal_mutable_capture_ids - .intersection(&self.current_mutably_captured_ids) - .next() - .is_none() - { - if let Some(hir_id) = self.current_statement_hir_id { - self.hir_id_uses_map.insert(hir_id, index); - } - } else { - self.uses[index] = None; + if let Some(hir_id) = path_to_local(recv) + && let Some(index) = self.hir_id_uses_map.remove(&hir_id) + { + if self + .illegal_mutable_capture_ids + .intersection(&self.current_mutably_captured_ids) + .next() + .is_none() + { + if let Some(hir_id) = self.current_statement_hir_id { + self.hir_id_uses_map.insert(hir_id, index); } + } else { + self.uses[index] = None; } } } @@ -520,3 +536,61 @@ fn get_captured_ids(cx: &LateContext<'_>, ty: Ty<'_>) -> HirIdSet { set } + +struct IteratorMethodCheckVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + hir_id_of_expr: HirId, + hir_id_of_let_binding: Option, +} + +impl<'tcx> Visitor<'tcx> for IteratorMethodCheckVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> ControlFlow<()> { + if let ExprKind::MethodCall(_method_name, recv, _args, _) = &expr.kind + && (recv.hir_id == self.hir_id_of_expr + || self + .hir_id_of_let_binding + .is_some_and(|hid| path_to_local_id(recv, hid))) + && !is_trait_method(self.cx, expr, sym::Iterator) + { + return ControlFlow::Break(()); + } else if let ExprKind::Assign(place, value, _span) = &expr.kind + && value.hir_id == self.hir_id_of_expr + && let Some(id) = path_to_local(place) + { + // our iterator was directly assigned to a variable + self.hir_id_of_let_binding = Some(id); + } + walk_expr(self, expr) + } + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'tcx>) -> ControlFlow<()> { + if let StmtKind::Let(LetStmt { + init: Some(expr), + pat: + Pat { + kind: PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None), + .. + }, + .. + }) = &stmt.kind + && expr.hir_id == self.hir_id_of_expr + { + // our iterator was directly assigned to a variable + self.hir_id_of_let_binding = Some(*id); + } + walk_stmt(self, stmt) + } +} + +fn check_iter_expr_used_only_as_iterator<'tcx>( + cx: &LateContext<'tcx>, + hir_id_of_expr: HirId, + block: &'tcx Block<'tcx>, +) -> bool { + let mut visitor = IteratorMethodCheckVisitor { + cx, + hir_id_of_expr, + hir_id_of_let_binding: None, + }; + visitor.visit_block(block).is_continue() +} diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs index 538aa9097a40f..d77d044340dca 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,22 +1,22 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::path_res; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; +use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_hir::def::Res; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::Symbol; use super::NEEDLESS_OPTION_AS_DEREF; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name: &str) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name: Symbol) { let typeck = cx.typeck_results(); let outer_ty = typeck.expr_ty(expr); if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) { - if name == "as_deref_mut" && recv.is_syntactic_place_expr() { + if name == sym::as_deref_mut && recv.is_syntactic_place_expr() { let Res::Local(binding_id) = path_res(cx, recv) else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs index 88b9c69f6f949..1544a12e6ba83 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs @@ -3,32 +3,33 @@ use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use super::NEEDLESS_OPTION_TAKE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place - if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) { - if let Some(function_name) = source_of_temporary_value(recv) { - span_lint_and_then( - cx, - NEEDLESS_OPTION_TAKE, - expr.span, - "called `Option::take()` on a temporary value", - |diag| { - diag.note(format!( - "`{function_name}` creates a temporary value, so calling take() has no effect" - )); - diag.span_suggestion( - expr.span.with_lo(recv.span.hi()), - "remove", - "", - Applicability::MachineApplicable, - ); - }, - ); - } + if !recv.is_syntactic_place_expr() + && is_expr_option(cx, recv) + && let Some(function_name) = source_of_temporary_value(recv) + { + span_lint_and_then( + cx, + NEEDLESS_OPTION_TAKE, + expr.span, + "called `Option::take()` on a temporary value", + |diag| { + diag.note(format!( + "`{function_name}` creates a temporary value, so calling take() has no effect" + )); + diag.span_suggestion( + expr.span.with_lo(recv.span.hi()), + "remove", + "", + Applicability::MachineApplicable, + ); + }, + ); } } @@ -41,20 +42,20 @@ fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// When this function is called, we are reasonably certain that the `ExprKind` is either /// `Call` or `MethodCall` because we already checked that the expression is not /// `is_syntactic_place_expr()`. -fn source_of_temporary_value<'a>(expr: &'a Expr<'_>) -> Option<&'a str> { +fn source_of_temporary_value(expr: &Expr<'_>) -> Option { match expr.peel_borrows().kind { ExprKind::Call(function, _) => { - if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind { - if !func_path.segments.is_empty() { - return Some(func_path.segments[0].ident.name.as_str()); - } + if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind + && !func_path.segments.is_empty() + { + return Some(func_path.segments[0].ident.name); } if let ExprKind::Path(QPath::TypeRelative(_, func_path_segment)) = function.kind { - return Some(func_path_segment.ident.name.as_str()); + return Some(func_path_segment.ident.name); } None }, - ExprKind::MethodCall(path_segment, ..) => Some(path_segment.ident.name.as_str()), + ExprKind::MethodCall(path_segment, ..) => Some(path_segment.ident.name), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs index 9a5ffdeaf4e8e..604b48656aeac 100644 --- a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs @@ -1,51 +1,56 @@ use super::OBFUSCATED_IF_ELSE; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_eager_eval; -use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; +use clippy_utils::{get_parent_expr, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind; use rustc_lint::LateContext; +use rustc_span::Symbol; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, then_recv: &'tcx hir::Expr<'_>, then_arg: &'tcx hir::Expr<'_>, - unwrap_arg: &'tcx hir::Expr<'_>, - then_method_name: &str, - unwrap_method_name: &str, + unwrap_arg: Option<&'tcx hir::Expr<'_>>, + then_method_name: Symbol, + unwrap_method_name: Symbol, ) { let recv_ty = cx.typeck_results().expr_ty(then_recv); if recv_ty.is_bool() { - let mut applicability = if switch_to_eager_eval(cx, then_arg) && switch_to_eager_eval(cx, unwrap_arg) { + let then_eager = switch_to_eager_eval(cx, then_arg); + let unwrap_eager = unwrap_arg.is_none_or(|arg| switch_to_eager_eval(cx, arg)); + + let mut applicability = if then_eager && unwrap_eager { Applicability::MachineApplicable } else { Applicability::MaybeIncorrect }; let if_then = match then_method_name { - "then" if let ExprKind::Closure(closure) = then_arg.kind => { + sym::then if let ExprKind::Closure(closure) = then_arg.kind => { let body = cx.tcx.hir_body(closure.body); snippet_with_applicability(cx, body.value.span, "..", &mut applicability) }, - "then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), + sym::then_some => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), _ => return, }; - // FIXME: Add `unwrap_or_else` symbol + // FIXME: Add `unwrap_or_else` and `unwrap_or_default` symbol let els = match unwrap_method_name { - "unwrap_or" => snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability), - "unwrap_or_else" if let ExprKind::Closure(closure) = unwrap_arg.kind => { + sym::unwrap_or => snippet_with_applicability(cx, unwrap_arg.unwrap().span, "..", &mut applicability), + sym::unwrap_or_else if let ExprKind::Closure(closure) = unwrap_arg.unwrap().kind => { let body = cx.tcx.hir_body(closure.body); snippet_with_applicability(cx, body.value.span, "..", &mut applicability) }, - "unwrap_or_else" if let ExprKind::Path(_) = unwrap_arg.kind => { - snippet_with_applicability(cx, unwrap_arg.span, "_", &mut applicability) + "()" + sym::unwrap_or_else if let ExprKind::Path(_) = unwrap_arg.unwrap().kind => { + snippet_with_applicability(cx, unwrap_arg.unwrap().span, "_", &mut applicability) + "()" }, + sym::unwrap_or_default => "Default::default()".into(), _ => return, }; diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index da084871402a7..bce314e64f053 100644 --- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxHashMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::ty::{is_type_diagnostic_item, match_type}; -use clippy_utils::{match_any_def_paths, paths}; +use clippy_utils::paths; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -13,7 +13,7 @@ use rustc_span::{Span, sym}; use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS}; fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || match_type(cx, ty, &paths::TOKIO_IO_OPEN_OPTIONS) + is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty) } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { @@ -126,14 +126,14 @@ fn get_open_options( && let ExprKind::Path(path) = callee.kind && let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id() { - let std_file_options = [sym::file_options, sym::open_options_new]; - - let tokio_file_options: &[&[&str]] = &[&paths::TOKIO_IO_OPEN_OPTIONS_NEW, &paths::TOKIO_FILE_OPTIONS]; + let is_std_options = matches!( + cx.tcx.get_diagnostic_name(did), + Some(sym::file_options | sym::open_options_new) + ); - let is_std_options = std_file_options - .into_iter() - .any(|sym| cx.tcx.is_diagnostic_item(sym, did)); - is_std_options || match_any_def_paths(cx, did, tokio_file_options).is_some() + is_std_options + || paths::TOKIO_IO_OPEN_OPTIONS_NEW.matches(cx, did) + || paths::TOKIO_FILE_OPTIONS.matches(cx, did) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs index 9b22494888fb2..3c38deca6cd1f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -1,14 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_span::{Span, sym}; +use rustc_span::Span; use super::{OPTION_AS_REF_CLONED, method_call}; pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { - if let Some((method @ ("as_ref" | "as_mut"), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv) + if let Some((method @ (sym::as_ref | sym::as_mut), as_ref_recv, [], as_ref_ident_span, _)) = + method_call(cloned_recv) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(as_ref_recv).peel_refs(), sym::Option) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index c03420a5143e6..c74c42e9e5bd5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -6,13 +6,13 @@ use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ - contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, + contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; -use rustc_span::symbol::{self, Symbol, sym}; +use rustc_span::symbol::{self, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_span: Span, - name: &str, + name: Symbol, receiver: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], ) { @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. fn check_unwrap_or_default( cx: &LateContext<'_>, - name: &str, + name: Symbol, receiver: &hir::Expr<'_>, fun: &hir::Expr<'_>, call_expr: Option<&hir::Expr<'_>>, @@ -66,8 +66,8 @@ pub(super) fn check<'tcx>( }; let sugg = match (name, call_expr.is_some()) { - ("unwrap_or", true) | ("unwrap_or_else", false) => sym!(unwrap_or_default), - ("or_insert", true) | ("or_insert_with", false) => sym!(or_default), + (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, + (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, _ => return false, }; @@ -78,8 +78,7 @@ pub(super) fn check<'tcx>( .iter() .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) .find_map(|assoc| { - if assoc.fn_has_self_parameter - && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 + if assoc.is_method() && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 { Some(assoc.def_id) } else { @@ -127,7 +126,7 @@ pub(super) fn check<'tcx>( #[expect(clippy::too_many_arguments)] fn check_or_fn_call<'tcx>( cx: &LateContext<'tcx>, - name: &str, + name: Symbol, method_span: Span, self_expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, @@ -138,11 +137,16 @@ pub(super) fn check<'tcx>( fun_span: Option, ) -> bool { // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [ - (sym::BTreeEntry, false, &["or_insert"], "with"), - (sym::HashMapEntry, false, &["or_insert"], "with"), - (sym::Option, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), - (sym::Result, true, &["or", "unwrap_or"], "else"), + const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 4] = [ + (sym::BTreeEntry, false, &[sym::or_insert], "with"), + (sym::HashMapEntry, false, &[sym::or_insert], "with"), + ( + sym::Option, + false, + &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or], + "else", + ), + (sym::Result, true, &[sym::or, sym::unwrap_or], "else"), ]; if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs index c9251c1b84979..407f2e80aff25 100644 --- a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs +++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs @@ -1,17 +1,16 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_parent_expr; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_local_use_after_expr; +use clippy_utils::{get_parent_expr, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; use super::READ_LINE_WITHOUT_TRIM; @@ -40,11 +39,11 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< // We've checked that `call` is a call to `Stdin::read_line()` with the right receiver, // now let's check if the first use of the string passed to `::read_line()` // is used for operations that will always fail (e.g. parsing "6\n" into a number) - for_each_local_use_after_expr(cx, local_id, call.hir_id, |expr| { + let _ = for_each_local_use_after_expr(cx, local_id, call.hir_id, |expr| { if let Some(parent) = get_parent_expr(cx, expr) { let data = if let ExprKind::MethodCall(segment, recv, args, span) = parent.kind { if args.is_empty() - && segment.ident.name.as_str() == "parse" + && segment.ident.name == sym::parse && let parse_result_ty = cx.typeck_results().expr_ty(parent) && is_type_diagnostic_item(cx, parse_result_ty, sym::Result) && let ty::Adt(_, substs) = parse_result_ty.kind() @@ -58,7 +57,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< "calling `.parse()` on a string without trimming the trailing newline character", "checking", )) - } else if segment.ident.name.as_str() == "ends_with" + } else if segment.ident.name == sym::ends_with && recv.span == expr.span && let [arg] = args && expr_is_string_literal_without_trailing_newline(arg) diff --git a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs index e8861935d4216..df8544f92203e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs @@ -9,7 +9,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::for_each_unconsumed_temporary; -use clippy_utils::{is_expr_final_block_expr, peel_blocks}; +use clippy_utils::{get_parent_expr, peel_blocks}; use super::RETURN_AND_THEN; @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'_>, ) { - if !is_expr_final_block_expr(cx.tcx, expr) { + if cx.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_none() { return; } @@ -55,13 +55,32 @@ pub(super) fn check<'tcx>( None => &body_snip, }; - let msg = "use the question mark operator instead of an `and_then` call"; - let sugg = format!( - "let {} = {}?;\n{}", - arg_snip, - recv_snip, - reindent_multiline(inner, false, indent_of(cx, expr.span)) - ); + // If suggestion is going to get inserted as part of a `return` expression, it must be blockified. + let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { + let base_indent = indent_of(cx, parent_expr.span); + let inner_indent = base_indent.map(|i| i + 4); + format!( + "{}\n{}\n{}", + reindent_multiline(&format!("{{\nlet {arg_snip} = {recv_snip}?;"), true, inner_indent), + reindent_multiline(inner, false, inner_indent), + reindent_multiline("}", false, base_indent), + ) + } else { + format!( + "let {} = {}?;\n{}", + arg_snip, + recv_snip, + reindent_multiline(inner, false, indent_of(cx, expr.span)) + ) + }; - span_lint_and_sugg(cx, RETURN_AND_THEN, expr.span, msg, "try", sugg, applicability); + span_lint_and_sugg( + cx, + RETURN_AND_THEN, + expr.span, + "use the `?` operator instead of an `and_then` call", + "try", + sugg, + applicability, + ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index 97c8ce2bcdd28..855babb797a21 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -2,14 +2,13 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs}; +use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs, sym}; use hir::ExprKind; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::PatKind; use rustc_lint::LateContext; -use rustc_span::Span; -use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use super::SEARCH_IS_SOME; @@ -19,7 +18,7 @@ use super::SEARCH_IS_SOME; pub(super) fn check<'tcx>( cx: &LateContext<'_>, expr: &'tcx hir::Expr<'_>, - search_method: &str, + search_method: Symbol, is_some: bool, search_recv: &hir::Expr<'_>, search_arg: &'tcx hir::Expr<'_>, @@ -35,7 +34,7 @@ pub(super) fn check<'tcx>( // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` let mut applicability = Applicability::MachineApplicable; - let any_search_snippet = if search_method == "find" + let any_search_snippet = if search_method == sym::find && let ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind && let closure_body = cx.tcx.hir_body(body) && let Some(closure_arg) = closure_body.params.first() @@ -107,7 +106,7 @@ pub(super) fn check<'tcx>( } } // lint if `find()` is called by `String` or `&str` - else if search_method == "find" { + else if search_method == sym::find { let is_string_or_str_slice = |e| { let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); if is_type_lang_item(cx, self_ty, hir::LangItem::String) { diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs index d318462e58415..8b51268da465e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs +++ b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs @@ -3,33 +3,33 @@ use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_enum_variant_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; +use clippy_utils::{is_enum_variant_ctor, sym}; use super::SEEK_FROM_CURRENT; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv); - if let Some(def_id) = cx.tcx.get_diagnostic_item(sym::IoSeek) { - if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) { - let mut applicability = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability); + if let Some(def_id) = cx.tcx.get_diagnostic_item(sym::IoSeek) + && implements_trait(cx, ty, def_id, &[]) + && arg_is_seek_from_current(cx, arg) + { + let mut applicability = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability); - span_lint_and_sugg( - cx, - SEEK_FROM_CURRENT, - expr.span, - "using `SeekFrom::Current` to start from current position", - "replace with", - format!("{snip}.stream_position()"), - applicability, - ); - } + span_lint_and_sugg( + cx, + SEEK_FROM_CURRENT, + expr.span, + "using `SeekFrom::Current` to start from current position", + "replace with", + format!("{snip}.stream_position()"), + applicability, + ); } } @@ -37,14 +37,12 @@ fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) if let ExprKind::Call(f, [arg]) = expr.kind && let ExprKind::Path(ref path) = f.kind && let Some(ctor_call_id) = cx.qpath_res(path, f.hir_id).opt_def_id() - && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Current), ctor_call_id) - { + && is_enum_variant_ctor(cx, sym::SeekFrom, sym::Current, ctor_call_id) // check if argument of `SeekFrom::Current` is `0` - if let ExprKind::Lit(lit) = arg.kind - && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node - { - return true; - } + && let ExprKind::Lit(lit) = arg.kind + && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node + { + return true; } false diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs index 7b1dd9e58c50e..b8405a78f23a9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs +++ b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_enum_variant_ctor, is_expr_used_or_unified}; +use clippy_utils::{is_enum_variant_ctor, is_expr_used_or_unified, sym}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::{Span, sym}; +use rustc_span::Span; use super::SEEK_TO_START_INSTEAD_OF_REWIND; @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>( && let ExprKind::Call(func, [arg]) = arg.kind && let ExprKind::Path(ref path) = func.kind && let Some(ctor_call_id) = cx.qpath_res(path, func.hir_id).opt_def_id() - && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Start), ctor_call_id) + && is_enum_variant_ctor(cx, sym::SeekFrom, sym::Start, ctor_call_id) && let ExprKind::Lit(lit) = arg.kind && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node { diff --git a/src/tools/clippy/clippy_lints/src/methods/str_split.rs b/src/tools/clippy/clippy_lints/src/methods/str_split.rs index 3586e11f56ab2..479064a0671e5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_split.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_split.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; +use clippy_utils::sym; use clippy_utils::visitors::is_const_evaluatable; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -14,12 +15,12 @@ pub(super) fn check<'a>(cx: &LateContext<'a>, expr: &'_ Expr<'_>, split_recv: &' // or `"\r\n"`). There are a lot of ways to specify a pattern, and this lint only checks the most // basic ones: a `'\n'`, `"\n"`, and `"\r\n"`. if let ExprKind::MethodCall(trim_method_name, trim_recv, [], _) = split_recv.kind - && trim_method_name.ident.as_str() == "trim" + && trim_method_name.ident.name == sym::trim && cx.typeck_results().expr_ty_adjusted(trim_recv).peel_refs().is_str() && !is_const_evaluatable(cx, trim_recv) && let ExprKind::Lit(split_lit) = split_arg.kind && (matches!(split_lit.node, LitKind::Char('\n')) - || matches!(split_lit.node, LitKind::Str(sym, _) if (sym.as_str() == "\n" || sym.as_str() == "\r\n"))) + || matches!(split_lit.node, LitKind::Str(sym::LF | sym::CRLF, _))) { let mut app = Applicability::MaybeIncorrect; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index 4ccefb7ec9d77..6935ae1f391fa 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -4,7 +4,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; +use clippy_utils::{is_diag_item_method, path_to_local_id, paths, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ @@ -12,13 +12,13 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{Span, Symbol, SyntaxContext, sym}; +use rustc_span::{Span, Symbol, SyntaxContext}; use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; pub(super) fn check( cx: &LateContext<'_>, - method_name: &str, + method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, @@ -45,9 +45,9 @@ pub(super) fn check( } } -fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) { +fn lint_needless(cx: &LateContext<'_>, method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) { let mut app = Applicability::MachineApplicable; - let r = if method_name == "splitn" { "" } else { "r" }; + let r = if method_name == sym::splitn { "" } else { "r" }; span_lint_and_sugg( cx, @@ -66,14 +66,14 @@ fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_ fn check_manual_split_once( cx: &LateContext<'_>, - method_name: &str, + method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, usage: &IterUsage, ) { let ctxt = expr.span.ctxt(); - let (msg, reverse) = if method_name == "splitn" { + let (msg, reverse) = if method_name == sym::splitn { ("manual implementation of `split_once`", false) } else { ("manual implementation of `rsplit_once`", true) @@ -121,7 +121,7 @@ fn check_manual_split_once( /// ``` fn check_manual_split_once_indirect( cx: &LateContext<'_>, - method_name: &str, + method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, @@ -143,7 +143,7 @@ fn check_manual_split_once_indirect( && first.name != second.name && !local_used_after_expr(cx, iter_binding_id, second.init_expr) { - let (r, lhs, rhs) = if method_name == "splitn" { + let (r, lhs, rhs) = if method_name == sym::splitn { ("", first.name, second.name) } else { ("r", second.name, first.name) @@ -238,15 +238,14 @@ fn indirect_usage<'tcx>( unwrap_kind: Some(unwrap_kind), .. } = iter_usage + && parent_id == local_hir_id { - if parent_id == local_hir_id { - return Some(IndirectUsage { - name: ident.name, - span: stmt.span, - init_expr, - unwrap_kind, - }); - } + return Some(IndirectUsage { + name: ident.name, + span: stmt.span, + init_expr, + unwrap_kind, + }); } } @@ -289,7 +288,7 @@ fn parse_iter_usage<'tcx>( match (name.ident.as_str(), args) { ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span), ("next_tuple", []) => { - return if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE) + return if paths::ITERTOOLS_NEXT_TUPLE.matches(cx, did) && let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind() && cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) && let ty::Tuple(subs) = subs.type_at(0).kind() diff --git a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs index c7885f689d75b..f11a41f90f1ab 100644 --- a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs +++ b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::method_chain_args; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{method_chain_args, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -13,7 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if !is_type_lang_item(cx, obj_ty, hir::LangItem::String) { return; } - if let Some(arglists) = method_chain_args(arg, &["chars"]) { + if let Some(arglists) = method_chain_args(arg, &[sym::chars]) { let target = &arglists[0].0; let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let ref_str = if self_ty.is_str() { diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs index 1bd48525f12d8..788014d9bb632 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs @@ -13,11 +13,11 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr< && let closure_body = cx.tcx.hir_body(closure.body) && !cx.typeck_results().expr_ty(closure_body.value).is_unit() { - if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) { + if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) // A variable is used mutably inside of the closure. Suppress the lint. - if !map_mutated_vars.is_empty() { - return; - } + && !map_mutated_vars.is_empty() + { + return; } span_lint_and_help( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs index ff5c1d1a40193..f8b6d4349fbeb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs @@ -2,11 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_note; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_span::Symbol; use rustc_span::source_map::Spanned; use super::SUSPICIOUS_SPLITN; -pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) { +pub(super) fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) { if count <= 1 && let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(call_id) diff --git a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs new file mode 100644 index 0000000000000..de729fb343a34 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs @@ -0,0 +1,125 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg::Sugg; +use rustc_ast::BorrowKind; +use rustc_errors::{Applicability, Diag}; +use rustc_hir::{Expr, ExprKind, Node, QPath}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::SWAP_WITH_TEMPORARY; + +const MSG_TEMPORARY: &str = "this expression returns a temporary value"; +const MSG_TEMPORARY_REFMUT: &str = "this is a mutable reference to a temporary value"; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) { + if let ExprKind::Path(QPath::Resolved(_, func_path)) = func.kind + && let Some(func_def_id) = func_path.res.opt_def_id() + && cx.tcx.is_diagnostic_item(sym::mem_swap, func_def_id) + { + match (ArgKind::new(&args[0]), ArgKind::new(&args[1])) { + (ArgKind::RefMutToTemp(left_temp), ArgKind::RefMutToTemp(right_temp)) => { + emit_lint_useless(cx, expr, &args[0], &args[1], left_temp, right_temp); + }, + (ArgKind::RefMutToTemp(left_temp), right) => emit_lint_assign(cx, expr, &right, &args[0], left_temp), + (left, ArgKind::RefMutToTemp(right_temp)) => emit_lint_assign(cx, expr, &left, &args[1], right_temp), + _ => {}, + } + } +} + +enum ArgKind<'tcx> { + // Mutable reference to a place, coming from a macro + RefMutToPlaceAsMacro(&'tcx Expr<'tcx>), + // Place behind a mutable reference + RefMutToPlace(&'tcx Expr<'tcx>), + // Temporary value behind a mutable reference + RefMutToTemp(&'tcx Expr<'tcx>), + // Any other case + Expr(&'tcx Expr<'tcx>), +} + +impl<'tcx> ArgKind<'tcx> { + fn new(arg: &'tcx Expr<'tcx>) -> Self { + if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind { + if target.is_syntactic_place_expr() { + if arg.span.from_expansion() { + ArgKind::RefMutToPlaceAsMacro(arg) + } else { + ArgKind::RefMutToPlace(target) + } + } else { + ArgKind::RefMutToTemp(target) + } + } else { + ArgKind::Expr(arg) + } + } +} + +// Emits a note either on the temporary expression if it can be found in the same context as the +// base and returns `true`, or on the mutable reference to the temporary expression otherwise and +// returns `false`. +fn emit_note(diag: &mut Diag<'_, ()>, base: &Expr<'_>, expr: &Expr<'_>, expr_temp: &Expr<'_>) -> bool { + if base.span.eq_ctxt(expr.span) { + diag.span_note(expr_temp.span.source_callsite(), MSG_TEMPORARY); + true + } else { + diag.span_note(expr.span.source_callsite(), MSG_TEMPORARY_REFMUT); + false + } +} + +fn emit_lint_useless( + cx: &LateContext<'_>, + expr: &Expr<'_>, + left: &Expr<'_>, + right: &Expr<'_>, + left_temp: &Expr<'_>, + right_temp: &Expr<'_>, +) { + span_lint_and_then( + cx, + SWAP_WITH_TEMPORARY, + expr.span, + "swapping temporary values has no effect", + |diag| { + emit_note(diag, expr, left, left_temp); + emit_note(diag, expr, right, right_temp); + }, + ); +} + +fn emit_lint_assign(cx: &LateContext<'_>, expr: &Expr<'_>, target: &ArgKind<'_>, reftemp: &Expr<'_>, temp: &Expr<'_>) { + span_lint_and_then( + cx, + SWAP_WITH_TEMPORARY, + expr.span, + "swapping with a temporary value is inefficient", + |diag| { + if !emit_note(diag, expr, reftemp, temp) { + return; + } + + // Make the suggestion only when the original `swap()` call is a statement + // or the last expression in a block. + if matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(..) | Node::Block(..)) { + let mut applicability = Applicability::MachineApplicable; + let ctxt = expr.span.ctxt(); + let assign_target = match target { + ArgKind::Expr(target) | ArgKind::RefMutToPlaceAsMacro(target) => { + Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref() + }, + ArgKind::RefMutToPlace(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability), + ArgKind::RefMutToTemp(_) => unreachable!(), + }; + let assign_source = Sugg::hir_with_context(cx, temp, ctxt, "_", &mut applicability); + diag.span_suggestion( + expr.span, + "use assignment instead", + format!("{assign_target} = {assign_source}"), + applicability, + ); + } + }, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index ca42a9ac04e0b..d260e0ef6e197 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,20 +1,24 @@ use super::utils::clone_or_copy_needed; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; +use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id, sym}; use core::ops::ControlFlow; -use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::sym; +use rustc_span::Symbol; use super::{UNNECESSARY_FILTER_MAP, UNNECESSARY_FIND_MAP}; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, name: &str) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + arg: &'tcx hir::Expr<'tcx>, + name: Symbol, +) { if !is_trait_method(cx, expr, sym::Iterator) { return; } @@ -40,46 +44,49 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); let sugg = if !found_filtering { // Check if the closure is .filter_map(|x| Some(x)) - if name == "filter_map" + if name == sym::filter_map && let hir::ExprKind::Call(expr, args) = body.value.kind && is_res_lang_ctor(cx, path_res(cx, expr), OptionSome) && let hir::ExprKind::Path(_) = args[0].kind { - span_lint_and_sugg( + span_lint( cx, UNNECESSARY_FILTER_MAP, expr.span, - format!("{name} is unnecessary"), - "try removing the filter_map", - String::new(), - Applicability::MaybeIncorrect, + String::from("this call to `.filter_map(..)` is unnecessary"), ); + return; + } + if name == sym::filter_map { + "map(..)" + } else { + "map(..).next()" } - if name == "filter_map" { "map" } else { "map(..).next()" } } else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) { match cx.typeck_results().expr_ty(body.value).kind() { ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) => { - if name == "filter_map" { "filter" } else { "find" } + if name == sym::filter_map { + "filter(..)" + } else { + "find(..)" + } }, _ => return, } } else { return; }; - span_lint_and_sugg( + span_lint( cx, - if name == "filter_map" { + if name == sym::filter_map { UNNECESSARY_FILTER_MAP } else { UNNECESSARY_FIND_MAP }, expr.span, - format!("this `.{name}` can be written more simply"), - "try instead", - sugg.to_string(), - Applicability::MaybeIncorrect, + format!("this `.{name}(..)` can be written more simply using `.{sugg}`"), ); } } @@ -97,7 +104,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc (true, true) }, hir::ExprKind::MethodCall(segment, recv, [arg], _) => { - if segment.ident.name.as_str() == "then_some" + if segment.ident.name == sym::then_some && cx.typeck_results().expr_ty(recv).is_bool() && path_to_local_id(arg, arg_id) { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index c0e0156858811..20cf35363d13f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -99,7 +99,7 @@ pub fn check_for_loop_iter( && let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) && let collection_ty = cx.typeck_results().expr_ty(collection) && implements_trait(cx, collection_ty, into_iterator_trait_id, &[]) - && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item") + && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, sym::Item) && iter_item_ty == into_iter_item_ty && let Some(collection_snippet) = collection.span.get_source_text(cx) { 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 index 9f4080100da20..71e606add526e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -23,56 +23,61 @@ pub(super) fn check<'tcx>( let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); - if is_option || is_result || is_bool { - if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind { - let body = cx.tcx.hir_body(body); - let body_expr = &body.value; + if (is_option || is_result || is_bool) + && let hir::ExprKind::Closure(&hir::Closure { + body, + fn_decl, + kind: hir::ClosureKind::Closure, + .. + }) = arg.kind + { + let body = cx.tcx.hir_body(body); + let body_expr = &body.value; - if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) { - return false; - } + if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) { + return false; + } - if eager_or_lazy::switch_to_eager_eval(cx, body_expr) { - let msg = if is_option { - "unnecessary closure used to substitute value for `Option::None`" - } else if is_result { - "unnecessary closure used to substitute value for `Result::Err`" - } else { - "unnecessary closure used with `bool::then`" - }; - let applicability = if body - .params - .iter() - // bindings are checked to be unused above - .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) - && matches!( - fn_decl.output, - FnRetTy::DefaultReturn(_) - | FnRetTy::Return(hir::Ty { - kind: hir::TyKind::Infer(()), - .. - }) - ) { - Applicability::MachineApplicable - } else { - // replacing the lambda may break type inference - Applicability::MaybeIncorrect - }; + if eager_or_lazy::switch_to_eager_eval(cx, body_expr) { + let msg = if is_option { + "unnecessary closure used to substitute value for `Option::None`" + } else if is_result { + "unnecessary closure used to substitute value for `Result::Err`" + } else { + "unnecessary closure used with `bool::then`" + }; + let applicability = if body + .params + .iter() + // bindings are checked to be unused above + .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) + && matches!( + fn_decl.output, + FnRetTy::DefaultReturn(_) + | FnRetTy::Return(hir::Ty { + kind: hir::TyKind::Infer(()), + .. + }) + ) { + Applicability::MachineApplicable + } else { + // replacing the lambda may break type inference + Applicability::MaybeIncorrect + }; - // This is a duplicate of what's happening in clippy_lints::methods::method_call, - // which isn't ideal, We want to get the method call span, - // but prefer to avoid changing the signature of the function itself. - if let hir::ExprKind::MethodCall(.., span) = expr.kind { - span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { - diag.span_suggestion_verbose( - span, - format!("use `{simplify_using}` instead"), - format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), - applicability, - ); - }); - return true; - } + // This is a duplicate of what's happening in clippy_lints::methods::method_call, + // which isn't ideal, We want to get the method call span, + // but prefer to avoid changing the signature of the function itself. + if let hir::ExprKind::MethodCall(.., span) = expr.kind { + span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { + diag.span_suggestion_verbose( + span, + format!("use `{simplify_using}` instead"), + format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), + applicability, + ); + }); + return true; } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index fa3a29e366709..cc4448192d3e7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res}; +use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::print::with_forced_trimmed_paths; +use rustc_span::Symbol; use super::UNNECESSARY_LITERAL_UNWRAP; @@ -25,7 +26,7 @@ pub(super) fn check( cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, - method: &str, + method: Symbol, args: &[hir::Expr<'_>], ) { let init = clippy_utils::expr_or_init(cx, recv); @@ -42,17 +43,17 @@ pub(super) fn check( let res = cx.qpath_res(qpath, call.hir_id()); if is_res_lang_ctor(cx, res, hir::LangItem::OptionSome) { - ("Some", call_args, get_ty_from_args(args, 0)) + (sym::Some, call_args, get_ty_from_args(args, 0)) } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultOk) { - ("Ok", call_args, get_ty_from_args(args, 0)) + (sym::Ok, call_args, get_ty_from_args(args, 0)) } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultErr) { - ("Err", call_args, get_ty_from_args(args, 1)) + (sym::Err, call_args, get_ty_from_args(args, 1)) } else { return; } } else if is_res_lang_ctor(cx, path_res(cx, init), hir::LangItem::OptionNone) { let call_args: &[hir::Expr<'_>] = &[]; - ("None", call_args, None) + (sym::None, call_args, None) } else { return; }; @@ -62,12 +63,12 @@ pub(super) fn check( span_lint_and_then(cx, UNNECESSARY_LITERAL_UNWRAP, expr.span, help_message, |diag| { let suggestions = match (constructor, method, ty) { - ("None", "unwrap", _) => Some(vec![(expr.span, "panic!()".to_string())]), - ("None", "expect", _) => Some(vec![ + (sym::None, sym::unwrap, _) => Some(vec![(expr.span, "panic!()".to_string())]), + (sym::None, sym::expect, _) => Some(vec![ (expr.span.with_hi(args[0].span.lo()), "panic!(".to_string()), (expr.span.with_lo(args[0].span.hi()), ")".to_string()), ]), - ("Some" | "Ok", "unwrap_unchecked", _) | ("Err", "unwrap_err_unchecked", _) => { + (sym::Some | sym::Ok, sym::unwrap_unchecked, _) | (sym::Err, sym::unwrap_err_unchecked, _) => { let mut suggs = vec![ (recv.span.with_hi(call_args[0].span.lo()), String::new()), (expr.span.with_lo(call_args[0].span.hi()), String::new()), @@ -83,7 +84,7 @@ pub(super) fn check( } Some(suggs) }, - ("None", "unwrap_or_default", _) => { + (sym::None, sym::unwrap_or_default, _) => { let ty = cx.typeck_results().expr_ty(expr); let default_ty_string = if let ty::Adt(def, ..) = ty.kind() { with_forced_trimmed_paths!(format!("{}", cx.tcx.def_path_str(def.did()))) @@ -92,11 +93,11 @@ pub(super) fn check( }; Some(vec![(expr.span, format!("{default_ty_string}::default()"))]) }, - ("None", "unwrap_or", _) => Some(vec![ + (sym::None, sym::unwrap_or, _) => Some(vec![ (expr.span.with_hi(args[0].span.lo()), String::new()), (expr.span.with_lo(args[0].span.hi()), String::new()), ]), - ("None", "unwrap_or_else", _) => match args[0].kind { + (sym::None, sym::unwrap_or_else, _) => match args[0].kind { hir::ExprKind::Closure(hir::Closure { body, .. }) => Some(vec![ (expr.span.with_hi(cx.tcx.hir_body(*body).value.span.lo()), String::new()), (expr.span.with_lo(args[0].span.hi()), String::new()), @@ -105,14 +106,14 @@ pub(super) fn check( }, _ if call_args.is_empty() => None, (_, _, Some(_)) => None, - ("Ok", "unwrap_err", None) | ("Err", "unwrap", None) => Some(vec![ + (sym::Ok, sym::unwrap_err, None) | (sym::Err, sym::unwrap, None) => Some(vec![ ( recv.span.with_hi(call_args[0].span.lo()), "panic!(\"{:?}\", ".to_string(), ), (expr.span.with_lo(call_args[0].span.hi()), ")".to_string()), ]), - ("Ok", "expect_err", None) | ("Err", "expect", None) => Some(vec![ + (sym::Ok, sym::expect_err, None) | (sym::Err, sym::expect, None) => Some(vec![ ( recv.span.with_hi(call_args[0].span.lo()), "panic!(\"{1}: {:?}\", ".to_string(), diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs index d7bd522ddab94..b90748dd1585f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs @@ -76,7 +76,7 @@ pub(super) fn check<'a>( && ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool)) && let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l } && switch_to_eager_eval(cx, non_binding_location) - // xor, because if its both then thats a strange edge case and + // xor, because if its both then that's a strange edge case and // we can just ignore it, since by default clippy will error on this && (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id)) && !is_local_used(cx, non_binding_location, hir_id) @@ -92,7 +92,7 @@ pub(super) fn check<'a>( // we may need to add parens around the suggestion // in case the parent expression has additional method calls, // since for example `Some(5).map_or(false, |x| x == 5).then(|| 1)` - // being converted to `Some(5) == Some(5).then(|| 1)` isnt + // being converted to `Some(5) == Some(5).then(|| 1)` isn't // the same thing let inner_non_binding = Sugg::NonParen(Cow::Owned(format!( @@ -109,8 +109,8 @@ pub(super) fn check<'a>( let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { match parent_expr.kind { - ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_par(), - ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_par(), + ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_paren(), + ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_paren(), _ => binop, } } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs index 7d01bdc2269bd..413881d5ec99e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -5,16 +5,17 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, - name: &str, + name: Symbol, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, ) { @@ -47,10 +48,10 @@ pub(super) fn check<'tcx>( } } -fn lint(cx: &LateContext<'_>, expr: &Expr<'_>, name: &str, lhs: Span, rhs: Span, order: Ordering) { +fn lint(cx: &LateContext<'_>, expr: &Expr<'_>, name: Symbol, lhs: Span, rhs: Span, order: Ordering) { let cmp_str = if order.is_ge() { "smaller" } else { "greater" }; - let suggested_value = if (name == "min" && order.is_ge()) || (name == "max" && order.is_le()) { + let suggested_value = if (name == sym::min && order.is_ge()) || (name == sym::max && order.is_le()) { snippet(cx, rhs, "..") } else { snippet(cx, lhs, "..") diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index a71b3659fd245..29a0d2950bc6a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -6,7 +6,8 @@ use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, return_ty, + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, peel_middle_ty_refs, + return_ty, sym, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -19,7 +20,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::{ self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{Obligation, ObligationCause}; @@ -152,7 +153,7 @@ fn check_addr_of_expr( } if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) && implements_trait(cx, receiver_ty, deref_trait_id, &[]) - && cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty) + && cx.get_associated_type(receiver_ty, deref_trait_id, sym::Target) == Some(target_ty) // Make sure that it's actually calling the right `.to_string()`, (#10033) // *or* this is a `Cow::into_owned()` call (which would be the wrong into_owned receiver (str != Cow) // but that's ok for Cow::into_owned specifically) @@ -219,6 +220,8 @@ fn check_into_iter_call_arg( && let Some(receiver_snippet) = receiver.span.get_source_text(cx) // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. && !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow) + // Calling `iter()` on a temporary object can lead to false positives. #14242 + && !is_expr_temporary_value(cx, receiver) { if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; @@ -309,8 +312,7 @@ fn check_string_from_utf8<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, /// call of a `to_owned`-like function is unnecessary. fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if let Some(parent) = get_parent_expr(cx, expr) - && let Some((fn_name, argument_expr)) = get_fn_name_and_arg(cx, parent) - && fn_name.as_str() == "split" + && let Some((sym::split, argument_expr)) = get_fn_name_and_arg(cx, parent) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) && let Some(arg_snippet) = argument_expr.span.get_source_text(cx) { @@ -319,7 +321,7 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb // add `.as_ref()` to the suggestion. let as_ref = if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) - && cx.get_associated_type(cx.typeck_results().expr_ty(receiver), deref_trait_id, "Target") + && cx.get_associated_type(cx.typeck_results().expr_ty(receiver), deref_trait_id, sym::Target) != Some(cx.tcx.types.str_) { ".as_ref()" @@ -611,21 +613,20 @@ fn has_lifetime(ty: Ty<'_>) -> bool { /// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`. fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - (method_name.as_str() == "cloned" || method_name.as_str() == "copied") - && is_diag_trait_item(cx, method_def_id, sym::Iterator) + matches!(method_name, sym::cloned | sym::copied) && is_diag_trait_item(cx, method_def_id, sym::Iterator) } /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { - is_clone_like(cx, method_name.as_str(), method_def_id) + is_clone_like(cx, method_name, method_def_id) || is_cow_into_owned(cx, method_name, method_def_id) || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) } /// Returns true if the named method is `Cow::into_owned`. fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow) + method_name == sym::into_owned && is_diag_item_method(cx, method_def_id, sym::Cow) } /// Returns true if the named method is `ToString::to_string` and it's called on a type that @@ -645,7 +646,7 @@ fn is_to_string_on_string_like<'a>( && let GenericArgKind::Type(ty) = generic_arg.unpack() && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) - && (cx.get_associated_type(ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_) + && (cx.get_associated_type(ty, deref_trait_id, sym::Target) == Some(cx.tcx.types.str_) || implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) { true @@ -654,11 +655,18 @@ fn is_to_string_on_string_like<'a>( } } -fn is_a_std_map_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::HashSet) - || is_type_diagnostic_item(cx, ty, sym::HashMap) - || is_type_diagnostic_item(cx, ty, sym::BTreeMap) - || is_type_diagnostic_item(cx, ty, sym::BTreeSet) +fn std_map_key<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + match ty.kind() { + ty::Adt(adt, args) + if matches!( + cx.tcx.get_diagnostic_name(adt.did()), + Some(sym::BTreeMap | sym::BTreeSet | sym::HashMap | sym::HashSet) + ) => + { + Some(args.type_at(0)) + }, + _ => None, + } } fn is_str_and_string(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { @@ -678,11 +686,11 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, expr) = arg.kind && let ExprKind::MethodCall(method_path, caller, &[], _) = expr.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let method_name = method_path.ident.name.as_str() + && let method_name = method_path.ident.name && match method_name { - "to_owned" => cx.tcx.is_diagnostic_item(sym::to_owned_method, method_def_id), - "to_string" => cx.tcx.is_diagnostic_item(sym::to_string_method, method_def_id), - "to_vec" => cx + sym::to_owned => cx.tcx.is_diagnostic_item(sym::to_owned_method, method_def_id), + sym::to_string => cx.tcx.is_diagnostic_item(sym::to_string_method, method_def_id), + sym::to_vec => cx .tcx .impl_of_method(method_def_id) .filter(|&impl_did| cx.tcx.type_of(impl_did).instantiate_identity().is_slice()) @@ -720,6 +728,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx // 1. This is a method with only one argument that doesn't come from a trait. // 2. That it has `Borrow` in its generic predicates. // 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, `BTreeSet`, `BTreeMap`). +// 4. The key to the "map type" is not a reference. fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::MethodCall(_, caller, &[arg], _) = expr.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) @@ -737,7 +746,9 @@ fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { }) && let caller_ty = cx.typeck_results().expr_ty(caller) // For now we limit it to "map types". - && is_a_std_map_type(cx, caller_ty) + && let Some(key_ty) = std_map_key(cx, caller_ty) + // We need to check that the key type is not a reference. + && !key_ty.is_ref() { check_if_applicable_to_argument(cx, &arg); } diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index 0cbf6004be3a0..d30c12e0c4830 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -1,15 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth}; -use clippy_utils::{ - get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs, -}; +use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; use core::ops::ControlFlow; @@ -41,7 +39,7 @@ fn get_enum_ty(enum_ty: Ty<'_>) -> Option> { } /// Checks for the `USELESS_ASREF` lint. -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbol, recvr: &hir::Expr<'_>) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" // check if the call is to the actual `AsRef` or `AsMut` trait let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { @@ -81,8 +79,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, applicability, ); } - } else if match_def_path(cx, def_id, &["core", "option", "Option", call_name]) - || match_def_path(cx, def_id, &["core", "result", "Result", call_name]) + } else if let Some(impl_id) = cx.tcx.impl_of_method(def_id) + && let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def() + && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Option | sym::Result)) { let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs(); let res_ty = cx.typeck_results().expr_ty(expr).peel_refs(); @@ -162,7 +161,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { } } -fn lint_as_ref_clone(cx: &LateContext<'_>, span: Span, recvr: &hir::Expr<'_>, call_name: &str) { +fn lint_as_ref_clone(cx: &LateContext<'_>, span: Span, recvr: &hir::Expr<'_>, call_name: Symbol) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs index 3611b341897a6..b0cc7a785bc31 100644 --- a/src/tools/clippy/clippy_lints/src/methods/utils.rs +++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs @@ -8,6 +8,9 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use rustc_span::symbol::sym; +/// Checks if `expr`, of type `ty`, corresponds to a slice or can be dereferenced to a slice, or if +/// `expr` is a method call to `.iter()` on such a type. In these cases, return the slice-like +/// expression. pub(super) fn derefs_to_slice<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs index 7384e534ed7d7..ad9b3c3645425 100644 --- a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs +++ b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_copy; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use std::fmt; use super::WRONG_SELF_CONVENTION; @@ -83,17 +83,18 @@ impl fmt::Display for Convention { #[allow(clippy::too_many_arguments)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - item_name: &str, + item_name: Symbol, self_ty: Ty<'tcx>, first_arg_ty: Ty<'tcx>, first_arg_span: Span, implements_trait: bool, is_trait_item: bool, ) { + let item_name_str = item_name.as_str(); if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| { convs .iter() - .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item)) + .all(|conv| conv.check(cx, self_ty, item_name_str, implements_trait, is_trait_item)) }) { // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032) if implements_trait diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs index ed89b3b34386f..64eafc0ebccdc 100644 --- a/src/tools/clippy/clippy_lints/src/minmax.rs +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -1,10 +1,9 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_trait_method; +use clippy_utils::{is_trait_method, sym}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; use std::cmp::Ordering::{Equal, Greater, Less}; declare_clippy_lint! { @@ -79,12 +78,10 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { - if path.ident.name.as_str() == "max" { - fetch_const(cx, Some(receiver), args, MinMax::Max) - } else if path.ident.name.as_str() == "min" { - fetch_const(cx, Some(receiver), args, MinMax::Min) - } else { - None + match path.ident.name { + sym::max => fetch_const(cx, Some(receiver), args, MinMax::Max), + sym::min => fetch_const(cx, Some(receiver), args, MinMax::Min), + _ => None, } } else { None diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index 693d1a8dd7643..09ee6f7037c64 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -329,7 +329,7 @@ fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let name = ident.name.as_str(); if name.starts_with('_') && !name.starts_with("__") - && let definition_span = cx.tcx.hir().span(definition_hir_id) + && let definition_span = cx.tcx.hir_span(definition_hir_id) && !definition_span.from_expansion() && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id]) { diff --git a/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs b/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs index 662f7cd8500cf..9ee1e2f3fd17a 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/builtin_type_shadow.rs @@ -6,14 +6,14 @@ use rustc_lint::EarlyContext; use super::BUILTIN_TYPE_SHADOW; pub(super) fn check(cx: &EarlyContext<'_>, param: &GenericParam) { - if let GenericParamKind::Type { .. } = param.kind { - if let Some(prim_ty) = PrimTy::from_name(param.ident.name) { - span_lint( - cx, - BUILTIN_TYPE_SHADOW, - param.ident.span, - format!("this generic shadows the built-in type `{}`", prim_ty.name()), - ); - } + if let GenericParamKind::Type { .. } = param.kind + && let Some(prim_ty) = PrimTy::from_name(param.ident.name) + { + span_lint( + cx, + BUILTIN_TYPE_SHADOW, + param.ident.span, + format!("this generic shadows the built-in type `{}`", prim_ty.name()), + ); } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index 9151cc6332004..4fbd3c9874db1 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -1,32 +1,51 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_lint::EarlyContext; use rustc_span::Span; use super::MIXED_CASE_HEX_LITERALS; pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, suffix: &str, lit_snip: &str) { - let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { - return; // It's useless so shouldn't lint. + let num_end_idx = match lit_snip.strip_suffix(suffix) { + Some(p) if p.ends_with('_') => lit_snip.len() - (suffix.len() + 1), + Some(_) => lit_snip.len() - suffix.len(), + None => lit_snip.len(), }; - if maybe_last_sep_idx <= 2 { + + if num_end_idx <= 2 { // It's meaningless or causes range error. return; } + let mut seen = (false, false); - for ch in &lit_snip.as_bytes()[2..=maybe_last_sep_idx] { + for ch in &lit_snip.as_bytes()[2..num_end_idx] { match ch { b'a'..=b'f' => seen.0 = true, b'A'..=b'F' => seen.1 = true, _ => {}, } if seen.0 && seen.1 { - span_lint( + let raw_digits = &lit_snip[2..num_end_idx]; + let (sugg_lower, sugg_upper) = if suffix.is_empty() { + ( + format!("0x{}", raw_digits.to_lowercase()), + format!("0x{}", raw_digits.to_uppercase()), + ) + } else { + ( + format!("0x{}_{}", raw_digits.to_lowercase(), suffix), + format!("0x{}_{}", raw_digits.to_uppercase(), suffix), + ) + }; + + span_lint_and_help( cx, MIXED_CASE_HEX_LITERALS, lit_span, "inconsistent casing in hexadecimal literal", + None, + format!("consider using `{sugg_lower}` or `{sugg_upper}`"), ); - break; + return; } } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs index d5b5b2bf2dd1b..3cb51671aaf18 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/redundant_pattern.rs @@ -6,20 +6,20 @@ use rustc_lint::EarlyContext; use super::REDUNDANT_PATTERN; pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::Ident(ann, ident, Some(ref right)) = pat.kind { - if let PatKind::Wild = right.kind { - span_lint_and_sugg( - cx, - REDUNDANT_PATTERN, - pat.span, - format!( - "the `{} @ _` pattern can be written as just `{}`", - ident.name, ident.name, - ), - "try", - format!("{}{}", ann.prefix_str(), ident.name), - Applicability::MachineApplicable, - ); - } + if let PatKind::Ident(ann, ident, Some(ref right)) = pat.kind + && let PatKind::Wild = right.kind + { + span_lint_and_sugg( + cx, + REDUNDANT_PATTERN, + pat.span, + format!( + "the `{} @ _` pattern can be written as just `{}`", + ident.name, ident.name, + ), + "try", + format!("{}{}", ann.prefix_str(), ident.name), + Applicability::MachineApplicable, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs index 00f46629f102c..fffaf40c9d141 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs @@ -7,30 +7,30 @@ use rustc_span::Span; use super::UNNEEDED_WILDCARD_PATTERN; pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::TupleStruct(_, _, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind { - if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) { - if let Some((left_index, left_pat)) = patterns[..rest_index] - .iter() - .rev() - .take_while(|pat| matches!(pat.kind, PatKind::Wild)) - .enumerate() - .last() - { - span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0); - } + if let PatKind::TupleStruct(_, _, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind + && let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) + { + if let Some((left_index, left_pat)) = patterns[..rest_index] + .iter() + .rev() + .take_while(|pat| matches!(pat.kind, PatKind::Wild)) + .enumerate() + .last() + { + span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0); + } - if let Some((right_index, right_pat)) = patterns[rest_index + 1..] - .iter() - .take_while(|pat| matches!(pat.kind, PatKind::Wild)) - .enumerate() - .last() - { - span_lint( - cx, - patterns[rest_index].span.shrink_to_hi().to(right_pat.span), - right_index == 0, - ); - } + if let Some((right_index, right_pat)) = patterns[rest_index + 1..] + .iter() + .take_while(|pat| matches!(pat.kind, PatKind::Wild)) + .enumerate() + .last() + { + span_lint( + cx, + patterns[rest_index].span.shrink_to_hi().to(right_pat.span), + right_index == 0, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs index d52fe7e7d5b9c..394bc4aef1cc7 100644 --- a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs +++ b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs @@ -111,10 +111,10 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { // Checks if impl_param_name is the same as one of type_param_names, // and is in a different position fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool { - if let Some(j) = type_param_names.get(impl_param_name) { - if i != *j { - return true; - } + if let Some(j) = type_param_names.get(impl_param_name) + && i != *j + { + return true; } false } diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index d78299fe08be8..c8e3462b24ef4 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -3,14 +3,15 @@ use std::ops::ControlFlow; use clippy_utils::comparisons::{Rel, normalize_comparison}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, hash_expr, higher}; -use rustc_ast::{LitKind, RangeLimits}; +use rustc_ast::{BinOpKind, LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{BinOp, Block, Body, Expr, ExprKind, UnOp}; +use rustc_hir::{Block, Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; @@ -97,7 +98,7 @@ enum LengthComparison { /// /// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, v.len()))` fn len_comparison<'hir>( - bin_op: BinOp, + bin_op: BinOpKind, left: &'hir Expr<'hir>, right: &'hir Expr<'hir>, ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { @@ -112,7 +113,7 @@ fn len_comparison<'hir>( // normalize comparison, `v.len() > 4` becomes `4 < v.len()` // this simplifies the logic a bit - let (op, left, right) = normalize_comparison(bin_op.node, left, right)?; + let (op, left, right) = normalize_comparison(bin_op, left, right)?; match (op, left.kind, right.kind) { (Rel::Lt, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanLength, left as usize, right)), (Rel::Lt, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanInt, right as usize, left)), @@ -134,18 +135,30 @@ fn assert_len_expr<'hir>( cx: &LateContext<'_>, expr: &'hir Expr<'hir>, ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { - if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) + let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind && let ExprKind::Binary(bin_op, left, right) = &condition.kind - - && let Some((cmp, asserted_len, slice_len)) = len_comparison(*bin_op, left, right) - && let ExprKind::MethodCall(method, recv, [], _) = &slice_len.kind - && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() - && method.ident.name == sym::len - // check if `then` block has a never type expression && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind && cx.typeck_results().expr_ty(then_expr).is_never() + { + len_comparison(bin_op.node, left, right)? + } else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| { + match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::assert_eq_macro) => Some((macro_call, BinOpKind::Eq)), + Some(sym::assert_ne_macro) => Some((macro_call, BinOpKind::Ne)), + _ => None, + } + }) && let Some((left, right, _)) = find_assert_eq_args(cx, expr, macro_call.expn) + { + len_comparison(bin_op, left, right)? + } else { + return None; + }; + + if let ExprKind::MethodCall(method, recv, [], _) = &slice_len.kind + && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() + && method.ident.name == sym::len { Some((cmp, asserted_len, recv)) } else { @@ -168,6 +181,7 @@ enum IndexEntry<'hir> { /// if the `assert!` asserts the right length. AssertWithIndex { highest_index: usize, + is_first_highest: bool, asserted_len: usize, assert_span: Span, slice: &'hir Expr<'hir>, @@ -177,6 +191,7 @@ enum IndexEntry<'hir> { /// Indexing without an `assert!` IndexWithoutAssert { highest_index: usize, + is_first_highest: bool, indexes: Vec, slice: &'hir Expr<'hir>, }, @@ -244,28 +259,41 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni assert_span, slice, } => { - *entry = IndexEntry::AssertWithIndex { - highest_index: index, - asserted_len: *asserted_len, - assert_span: *assert_span, - slice, - indexes: vec![expr.span], - comparison: *comparison, - }; + if slice.span.lo() > assert_span.lo() { + *entry = IndexEntry::AssertWithIndex { + highest_index: index, + is_first_highest: true, + asserted_len: *asserted_len, + assert_span: *assert_span, + slice, + indexes: vec![expr.span], + comparison: *comparison, + }; + } }, IndexEntry::IndexWithoutAssert { - highest_index, indexes, .. + highest_index, + indexes, + is_first_highest, + .. } | IndexEntry::AssertWithIndex { - highest_index, indexes, .. + highest_index, + indexes, + is_first_highest, + .. } => { indexes.push(expr.span); + if *is_first_highest { + (*is_first_highest) = *highest_index >= index; + } *highest_index = (*highest_index).max(index); }, } } else { indexes.push(IndexEntry::IndexWithoutAssert { highest_index: index, + is_first_highest: true, indexes: vec![expr.span], slice, }); @@ -284,15 +312,18 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un if let Some(entry) = entry { if let IndexEntry::IndexWithoutAssert { highest_index, + is_first_highest, indexes, slice, } = entry + && expr.span.lo() <= slice.span.lo() { *entry = IndexEntry::AssertWithIndex { highest_index: *highest_index, indexes: mem::take(indexes), + is_first_highest: *is_first_highest, slice, - assert_span: expr.span, + assert_span: expr.span.source_callsite(), comparison, asserted_len, }; @@ -301,7 +332,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un indexes.push(IndexEntry::StrayAssert { asserted_len, comparison, - assert_span: expr.span, + assert_span: expr.span.source_callsite(), slice, }); } @@ -325,12 +356,13 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap match *entry { IndexEntry::AssertWithIndex { highest_index, + is_first_highest, asserted_len, ref indexes, comparison, assert_span, slice, - } if indexes.len() > 1 => { + } if indexes.len() > 1 && !is_first_highest => { // if we have found an `assert!`, let's also check that it's actually right // and if it covers the highest index and if not, suggest the correct length let sugg = match comparison { @@ -378,8 +410,9 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap IndexEntry::IndexWithoutAssert { ref indexes, highest_index, + is_first_highest, slice, - } if indexes.len() > 1 => { + } if indexes.len() > 1 && !is_first_highest => { // if there was no `assert!` but more than one index, suggest // adding an `assert!` that covers the highest index report_lint( 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 38a19dd2999bb..a6be7581c9a38 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 @@ -7,7 +7,7 @@ use rustc_abi::ExternAbi; use rustc_errors::Applicability; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; -use rustc_hir::{self as hir, Body, Constness, FnDecl, GenericParamKind}; +use rustc_hir::{self as hir, Body, Constness, FnDecl, GenericParamKind, OwnerId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -125,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { } }, FnKind::Method(_, sig, ..) => { - if already_const(sig.header) || trait_ref_of_method(cx, def_id).is_some() { + if already_const(sig.header) || trait_ref_of_method(cx, OwnerId { def_id }).is_some() { return; } }, @@ -139,12 +139,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { // Const fns are not allowed as methods in a trait. { let parent = cx.tcx.hir_get_parent_item(hir_id).def_id; - if parent != CRATE_DEF_ID { - if let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) { - if let hir::ItemKind::Trait(..) = &item.kind { - return; - } - } + if parent != CRATE_DEF_ID + && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) + && let hir::ItemKind::Trait(..) = &item.kind + { + return; } } @@ -198,7 +197,7 @@ fn fn_inputs_has_impl_trait_ty(cx: &LateContext<'_>, def_id: LocalDefId) -> bool inputs.iter().any(|input| { matches!( input.kind(), - ty::Alias(ty::AliasTyKind::Weak, alias_ty) if cx.tcx.type_of(alias_ty.def_id).skip_binder().is_impl_trait() + ty::Alias(ty::AliasTyKind::Free, alias_ty) if cx.tcx.type_of(alias_ty.def_id).skip_binder().is_impl_trait() ) }) } diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 47a9e17b3cfe1..7772051eb5c61 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -48,6 +48,8 @@ pub struct MissingDoc { /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. crate_items_only: bool, + /// Whether to allow fields starting with an underscore to skip documentation requirements + allow_unused: bool, /// Stack of whether #[doc(hidden)] is set /// at each level which has lint attributes. doc_hidden_stack: Vec, @@ -59,6 +61,7 @@ impl MissingDoc { pub fn new(conf: &'static Conf) -> Self { Self { crate_items_only: conf.missing_docs_in_crate_items, + allow_unused: conf.missing_docs_allow_unused, doc_hidden_stack: vec![false], prev_span: None, } @@ -182,7 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_crate(&mut self, cx: &LateContext<'tcx>) { - let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID); + let attrs = cx.tcx.hir_attrs(hir::CRATE_HIR_ID); self.check_missing_docs_attrs(cx, CRATE_DEF_ID, attrs, cx.tcx.def_span(CRATE_DEF_ID), "the", "crate"); } @@ -192,17 +195,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { match it.kind { - hir::ItemKind::Fn { .. } => { + hir::ItemKind::Fn { ident, .. } => { // ignore main() - if it.ident.name == sym::main { + if ident.name == sym::main { let at_root = cx.tcx.local_parent(it.owner_id.def_id) == CRATE_DEF_ID; if at_root { note_prev_span_then_ret!(self.prev_span, it.span); } } }, - hir::ItemKind::Const(..) => { - if it.ident.name == kw::Underscore { + hir::ItemKind::Const(ident, ..) => { + if ident.name == kw::Underscore { note_prev_span_then_ret!(self.prev_span, it.span); } }, @@ -224,7 +227,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id()); - let attrs = cx.tcx.hir().attrs(it.hir_id()); + let attrs = cx.tcx.hir_attrs(it.hir_id()); if !is_from_proc_macro(cx, it) { self.check_missing_docs_attrs(cx, it.owner_id.def_id, attrs, it.span, article, desc); } @@ -234,7 +237,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) { let (article, desc) = cx.tcx.article_and_description(trait_item.owner_id.to_def_id()); - let attrs = cx.tcx.hir().attrs(trait_item.hir_id()); + let attrs = cx.tcx.hir_attrs(trait_item.hir_id()); if !is_from_proc_macro(cx, trait_item) { self.check_missing_docs_attrs(cx, trait_item.owner_id.def_id, attrs, trait_item.span, article, desc); } @@ -252,7 +255,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id()); - let attrs = cx.tcx.hir().attrs(impl_item.hir_id()); + let attrs = cx.tcx.hir_attrs(impl_item.hir_id()); if !is_from_proc_macro(cx, impl_item) { self.check_missing_docs_attrs(cx, impl_item.owner_id.def_id, attrs, impl_item.span, article, desc); } @@ -260,17 +263,18 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) { - if !sf.is_positional() { - let attrs = cx.tcx.hir().attrs(sf.hir_id); - if !is_from_proc_macro(cx, sf) { - self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); - } + if !(sf.is_positional() + || is_from_proc_macro(cx, sf) + || self.allow_unused && sf.ident.as_str().starts_with('_')) + { + let attrs = cx.tcx.hir_attrs(sf.hir_id); + self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); } self.prev_span = Some(sf.span); } fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { - let attrs = cx.tcx.hir().attrs(v.hir_id); + let attrs = cx.tcx.hir_attrs(v.hir_id); if !is_from_proc_macro(cx, v) { self.check_missing_docs_attrs(cx, v.def_id, attrs, v.span, "a", "variant"); } diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index 59f29d242abf3..a1e621cc9f6b5 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; -use clippy_utils::def_path_def_ids; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths::{PathNS, lookup_path_str}; use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -56,8 +56,12 @@ impl ImportRename { renames: conf .enforced_import_renames .iter() - .map(|x| (x.path.split("::").collect::>(), Symbol::intern(&x.rename))) - .flat_map(|(path, rename)| def_path_def_ids(tcx, &path).map(move |id| (id, rename))) + .map(|x| (&x.path, Symbol::intern(&x.rename))) + .flat_map(|(path, rename)| { + lookup_path_str(tcx, PathNS::Arbitrary, path) + .into_iter() + .map(move |id| (id, rename)) + }) .collect(), } } @@ -67,7 +71,7 @@ impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]); impl LateLintPass<'_> for ImportRename { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if let ItemKind::Use(path, UseKind::Single) = &item.kind { + if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind { for &res in &path.res { if let Res::Def(_, id) = res && let Some(name) = self.renames.get(&id) diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index 675989156cad0..be7dd74fd62b9 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -1,9 +1,9 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_path_lang_item; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Visitable, for_each_expr}; +use clippy_utils::{is_path_lang_item, sym}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; @@ -13,7 +13,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -116,7 +116,7 @@ fn should_lint<'tcx>( if path.ident.name == sym::debug_struct && is_type_diagnostic_item(cx, recv_ty, sym::Formatter) { has_debug_struct = true; - } else if path.ident.name.as_str() == "finish_non_exhaustive" + } else if path.ident.name == sym::finish_non_exhaustive && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) { has_finish_non_exhaustive = true; @@ -209,7 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { && let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, self_path_did) = self_path.res && cx.tcx.is_diagnostic_item(sym::Debug, trait_def_id) // don't trigger if this impl was derived - && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) && !item.span.from_expansion() // find `Debug::fmt` function && let Some(fmt_item) = items.iter().find(|i| i.ident.name == sym::fmt) @@ -224,11 +224,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { // NB: can't call cx.typeck_results() as we are not in a body && let typeck_results = cx.tcx.typeck_body(*body_id) && should_lint(cx, typeck_results, block) - { // we intentionally only lint structs, see lint description - if let ItemKind::Struct(data, _) = &self_item.kind { - check_struct(cx, typeck_results, block, self_ty, item, data); - } + && let ItemKind::Struct(_, data, _) = &self_item.kind + { + check_struct(cx, typeck_results, block, self_ty, item, data); } } } diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 3cf1a80607e8b..1f613171b46e8 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// /// struct Baz; /// impl Baz { - /// fn private() {} // ok + /// fn private() {} // ok /// } /// /// impl Bar for Baz { @@ -46,13 +46,13 @@ declare_clippy_lint! { /// /// pub struct PubBaz; /// impl PubBaz { - /// fn private() {} // ok - /// pub fn not_private() {} // missing #[inline] + /// fn private() {} // ok + /// pub fn not_private() {} // missing #[inline] /// } /// /// impl Bar for PubBaz { - /// fn bar() {} // missing #[inline] - /// fn def_bar() {} // missing #[inline] + /// fn bar() {} // missing #[inline] + /// fn def_bar() {} // missing #[inline] /// } /// ``` /// @@ -98,10 +98,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { match it.kind { hir::ItemKind::Fn { .. } => { let desc = "a function"; - let attrs = cx.tcx.hir().attrs(it.hir_id()); + let attrs = cx.tcx.hir_attrs(it.hir_id()); check_missing_inline_attrs(cx, attrs, it.span, desc); }, - hir::ItemKind::Trait(ref _is_auto, ref _unsafe, _generics, _bounds, trait_items) => { + hir::ItemKind::Trait(ref _is_auto, ref _unsafe, _ident, _generics, _bounds, trait_items) => { // note: we need to check if the trait is exported so we can't use // `LateLintPass::check_trait_item` here. for tit in trait_items { @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { // an impl is not provided let desc = "a default trait method"; let item = cx.tcx.hir_trait_item(tit.id); - let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attrs = cx.tcx.hir_attrs(item.hir_id()); check_missing_inline_attrs(cx, attrs, item.span, desc); } }, @@ -160,15 +160,16 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { AssocItemContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), }; - if let Some(trait_def_id) = trait_def_id { - if trait_def_id.is_local() && !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { - // If a trait is being implemented for an item, and the - // trait is not exported, we don't need #[inline] - return; - } + if let Some(trait_def_id) = trait_def_id + && trait_def_id.is_local() + && !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) + { + // If a trait is being implemented for an item, and the + // trait is not exported, we don't need #[inline] + return; } - let attrs = cx.tcx.hir().attrs(impl_item.hir_id()); + let attrs = cx.tcx.hir_attrs(impl_item.hir_id()); check_missing_inline_attrs(cx, attrs, impl_item.span, desc); } } diff --git a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs index 7ee746365d102..e266c36b6e734 100644 --- a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs +++ b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { cx, MISSING_TRAIT_METHODS, cx.tcx.def_span(item.owner_id), - format!("missing trait method provided by default: `{}`", assoc.name), + format!("missing trait method provided by default: `{}`", assoc.name()), |diag| { diag.span_help(cx.tcx.def_span(assoc.def_id), "implement the method"); }, diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index a7452c8a3c84e..d9f4fb271fb4b 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; +use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id, sym}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -135,10 +135,10 @@ impl<'tcx> DivergenceVisitor<'_, 'tcx> { } fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) { - if let Some(macro_call) = root_macro_call_first_node(self.cx, e) { - if self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { - return; - } + if let Some(macro_call) = root_macro_call_first_node(self.cx, e) + && self.cx.tcx.is_diagnostic_item(sym::todo_macro, macro_call.def_id) + { + return; } span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges"); } @@ -261,10 +261,11 @@ fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> St | ExprKind::Assign(..) | ExprKind::Index(..) | ExprKind::Repeat(_, _) - | ExprKind::Struct(_, _, _) => { + | ExprKind::Struct(_, _, _) + | ExprKind::AssignOp(_, _, _) => { walk_expr(vis, expr); }, - ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => { + ExprKind::Binary(op, _, _) => { if op.node == BinOpKind::And || op.node == BinOpKind::Or { // x && y and x || y always evaluate x first, so these are // strictly sequenced. @@ -327,22 +328,22 @@ impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> { return; } - if path_to_local_id(expr, self.var) { + if path_to_local_id(expr, self.var) // Check that this is a read, not a write. - if !is_in_assignment_position(self.cx, expr) { - span_lint_and_then( - self.cx, - MIXED_READ_WRITE_IN_EXPRESSION, - expr.span, - format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)), - |diag| { - diag.span_note( - self.write_expr.span, - "whether read occurs before this write depends on evaluation order", - ); - }, - ); - } + && !is_in_assignment_position(self.cx, expr) + { + span_lint_and_then( + self.cx, + MIXED_READ_WRITE_IN_EXPRESSION, + expr.span, + format!("unsequenced read of `{}`", self.cx.tcx.hir_name(self.var)), + |diag| { + diag.span_note( + self.write_expr.span, + "whether read occurs before this write depends on evaluation order", + ); + }, + ); } match expr.kind { // We're about to descend a closure. Since we don't know when (or @@ -372,10 +373,10 @@ impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> { /// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`. fn is_in_assignment_position(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(parent) = get_parent_expr(cx, expr) { - if let ExprKind::Assign(lhs, ..) = parent.kind { - return lhs.hir_id == expr.hir_id; - } + if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::Assign(lhs, ..) = parent.kind + { + return lhs.hir_id == expr.hir_id; } false } diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index 676d608eb318c..98614baffcea6 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -73,8 +73,8 @@ impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]); impl EarlyLintPass for ModStyle { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { - if cx.builder.lint_level(MOD_MODULE_FILES).0 == Level::Allow - && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).0 == Level::Allow + if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow + && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow { return; } @@ -119,22 +119,22 @@ impl EarlyLintPass for ModStyle { } for folder in &folder_segments { - if !mod_folders.contains(folder) { - if let Some((file, path)) = file_map.get(folder) { - span_lint_and_then( - cx, - SELF_NAMED_MODULE_FILES, - Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), - format!("`mod.rs` files are required, found `{}`", path.display()), - |diag| { - let mut correct = path.to_path_buf(); - correct.pop(); - correct.push(folder); - correct.push("mod.rs"); - diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),)); - }, - ); - } + if !mod_folders.contains(folder) + && let Some((file, path)) = file_map.get(folder) + { + span_lint_and_then( + cx, + SELF_NAMED_MODULE_FILES, + Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), + format!("`mod.rs` files are required, found `{}`", path.display()), + |diag| { + let mut correct = path.to_path_buf(); + correct.pop(); + correct.push(folder); + correct.push("mod.rs"); + diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),)); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs index 0e1980a6acb61..4b32ba83b325e 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs @@ -39,7 +39,7 @@ declare_lint_pass!(MultipleBoundLocations => [MULTIPLE_BOUND_LOCATIONS]); impl EarlyLintPass for MultipleBoundLocations { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: NodeId) { - if let FnKind::Fn(_, _, _, Fn { generics, .. }) = kind + if let FnKind::Fn(_, _, Fn { generics, .. }) = kind && !generics.params.is_empty() && !generics.where_clause.predicates.is_empty() { diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 2adc27c0b709a..c6c27e22b90e5 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -142,10 +142,9 @@ fn collect_unsafe_exprs<'tcx>( .typeck_results() .type_dependent_def_id(expr.hir_id) .map(|def_id| cx.tcx.fn_sig(def_id)) + && sig.skip_binder().safety().is_unsafe() { - if sig.skip_binder().safety().is_unsafe() { - unsafe_ops.push(("unsafe method call occurs here", expr.span)); - } + unsafe_ops.push(("unsafe method call occurs here", expr.span)); } }, diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index 7abc5870d00e0..98a9a98d281af 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -82,10 +82,10 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> { } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { - if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind { - if trait_ref_of_method(cx, item.owner_id.def_id).is_none() { - self.check_sig(cx, item.owner_id.def_id, sig.decl); - } + if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind + && trait_ref_of_method(cx, item.owner_id).is_none() + { + self.check_sig(cx, item.owner_id.def_id, sig.decl); } } diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs index 3c4ba5141dd9b..d98c70e7f5a85 100644 --- a/src/tools/clippy/clippy_lints/src/mut_mut.rs +++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs @@ -77,16 +77,16 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { expr.span, "generally you want to avoid `&mut &mut _` if possible", ); - } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { - if ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) { - span_lint_hir( - self.cx, - MUT_MUT, - expr.hir_id, - expr.span, - "this expression mutably borrows a mutable reference. Consider reborrowing", - ); - } + } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() + && ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) + { + span_lint_hir( + self.cx, + MUT_MUT, + expr.hir_id, + expr.span, + "this expression mutably borrows a mutable reference. Consider reborrowing", + ); } } } 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 13a23a13b9c24..3b271ca0dc147 100644 --- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs +++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::sym; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -42,10 +43,9 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { let Some(macro_call) = root_macro_call_first_node(cx, e) else { return; }; - let macro_name = cx.tcx.item_name(macro_call.def_id); if !matches!( - macro_name.as_str(), - "debug_assert" | "debug_assert_eq" | "debug_assert_ne" + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::debug_assert_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro) ) { return; } @@ -60,7 +60,10 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { cx, DEBUG_ASSERT_WITH_MUT_CALL, span, - format!("do not call a function with mutable arguments inside of `{macro_name}!`"), + format!( + "do not call a function with mutable arguments inside of `{}!`", + cx.tcx.item_name(macro_call.def_id) + ), ); } } @@ -101,14 +104,13 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> { return; }, ExprKind::Path(_) => { - if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { - if adj + if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) + && adj .iter() .any(|a| matches!(a.target.kind(), ty::Ref(_, _, Mutability::Mut))) - { - self.found = true; - return; - } + { + self.found = true; + return; } }, // Don't check await desugars diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 49fd29d1dd6dc..fe2157ca533a6 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -91,19 +91,19 @@ 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 is_type_diagnostic_item(cx, ty, sym::Mutex) { - let mutex_param = subst.type_at(0); - if let Some(atomic_name) = get_atomic_name(mutex_param) { - let msg = format!( - "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ + if let ty::Adt(_, subst) = ty.kind() + && is_type_diagnostic_item(cx, ty, sym::Mutex) + { + let mutex_param = subst.type_at(0); + if let Some(atomic_name) = get_atomic_name(mutex_param) { + let msg = format!( + "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ behavior and not the internal type, consider using `Mutex<()>`" - ); - match *mutex_param.kind() { - ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), - } + ); + match *mutex_param.kind() { + ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), + ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), + _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 2eacd6875d6b4..3ed4b1c2ea947 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -3,7 +3,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, - is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_extract_comment, + is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_extract_comment, sym, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { || is_receiver_of_method_call(cx, e) || is_as_argument(cx, e) { - snip = snip.maybe_par(); + snip = snip.maybe_paren(); } span_lint_and_sugg( @@ -320,7 +320,7 @@ fn check_comparison<'a, 'tcx>( cx.typeck_results().expr_ty(left_side), cx.typeck_results().expr_ty(right_side), ); - if is_expn_of(left_side.span, "cfg").is_some() || is_expn_of(right_side.span, "cfg").is_some() { + if is_expn_of(left_side.span, sym::cfg).is_some() || is_expn_of(right_side.span, sym::cfg).is_some() { return; } if l_ty.is_bool() && r_ty.is_bool() { @@ -426,10 +426,10 @@ fn fetch_bool_block(expr: &Expr<'_>) -> Option { } fn fetch_bool_expr(expr: &Expr<'_>) -> Option { - if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind { - if let LitKind::Bool(value) = lit_ptr.node { - return Some(value); - } + if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind + && let LitKind::Bool(value) = lit_ptr.node + { + return Some(value); } None } diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs index f686cc912ddb0..e579dd5947d74 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -299,7 +299,7 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { .associated_items(trait_def_id) .in_definition_order() .any(|assoc_item| { - if assoc_item.fn_has_self_parameter { + if assoc_item.is_method() { let self_ty = cx .tcx .fn_sig(assoc_item.def_id) diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs index 90b27f5dbac82..7dd96f1f037fd 100644 --- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -3,12 +3,12 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::has_iter_method; +use clippy_utils::{is_trait_method, sym}; declare_clippy_lint! { /// ### What it does @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { iter_recv.kind, ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ) - && method_name.ident.name.as_str() == "for_each" + && method_name.ident.name == sym::for_each && is_trait_method(cx, expr, sym::Iterator) // Checks the type of the `iter` method receiver is NOT a user defined type. && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() diff --git a/src/tools/clippy/clippy_lints/src/needless_if.rs b/src/tools/clippy/clippy_lints/src/needless_if.rs index 7eefb016aca98..c90019f6ee161 100644 --- a/src/tools/clippy/clippy_lints/src/needless_if.rs +++ b/src/tools/clippy/clippy_lints/src/needless_if.rs @@ -65,7 +65,7 @@ impl LateLintPass<'_> for NeedlessIf { stmt.span, "this `if` branch is empty", "you can remove it", - if cond.can_have_side_effects() || !cx.tcx.hir().attrs(stmt.hir_id).is_empty() { + if cond.can_have_side_effects() || !cx.tcx.hir_attrs(stmt.hir_id).is_empty() { // `{ foo }` or `{ foo } && bar` placed into a statement position would be // interpreted as a block statement, force it to be an expression if cond_snippet.starts_with('{') { diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index 863a1f895c937..a914267cf5002 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; -use clippy_utils::source::{SourceText, SpanRangeExt}; +use clippy_utils::source::{SourceText, SpanRangeExt, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; use core::ops::ControlFlow; @@ -100,7 +100,6 @@ fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { #[derive(Debug)] struct LocalAssign { lhs_id: HirId, - lhs_span: Span, rhs_span: Span, span: Span, } @@ -118,7 +117,6 @@ impl LocalAssign { Some(Self { lhs_id: path_to_local(lhs)?, - lhs_span: lhs.span, rhs_span: rhs.span.source_callsite(), span, }) @@ -261,7 +259,7 @@ fn check<'tcx>( binding_id: HirId, ) -> Option<()> { let usage = first_usage(cx, binding_id, local_stmt.hir_id, block)?; - let binding_name = cx.tcx.hir().opt_name(binding_id)?; + let binding_name = cx.tcx.hir_opt_name(binding_id)?; let let_snippet = local_snippet_without_semicolon(cx, local)?; match usage.expr.kind { @@ -281,7 +279,10 @@ fn check<'tcx>( format!("move the declaration `{binding_name}` here"), vec![ (local_stmt.span, String::new()), - (assign.lhs_span, let_snippet.to_owned()), + ( + assign.span, + let_snippet.to_owned() + " = " + &snippet(cx, assign.rhs_span, ".."), + ), ], Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index d5c5679c990df..7052e1d0fbe5d 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -86,11 +86,11 @@ fn should_skip<'tcx>( return false; } - if let PatKind::Binding(.., name, _) = arg.pat.kind { + if let PatKind::Binding(.., name, _) = arg.pat.kind // If it's a potentially unused variable, we don't check it. - if name.name == kw::Underscore || name.as_str().starts_with('_') { - return true; - } + && (name.name == kw::Underscore || name.as_str().starts_with('_')) + { + return true; } // All spans generated from a proc-macro invocation are the same... @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // We don't check unsafe functions. return; } - let attrs = cx.tcx.hir().attrs(hir_id); + let attrs = cx.tcx.hir_attrs(hir_id); if header.abi != ExternAbi::Rust || requires_exact_signature(attrs) { return; } @@ -164,13 +164,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { }; // Exclude non-inherent impls - if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - if matches!( + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + && matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) - ) { - return; - } + ) + { + return; } let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity(); @@ -280,7 +280,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { diag.span_suggestion( sp, "consider changing to".to_string(), - format!("&{}", snippet(cx, cx.tcx.hir().span(inner_ty.ty.hir_id), "_"),), + format!("&{}", snippet(cx, cx.tcx.hir_span(inner_ty.ty.hir_id), "_"),), Applicability::Unspecified, ); if cx.effective_visibilities.is_exported(*fn_def_id) { @@ -353,10 +353,10 @@ impl MutablyUsedVariablesCtxt<'_> { for (parent, node) in self.tcx.hir_parent_iter(item) { if let Some(fn_sig) = self.tcx.hir_fn_sig_by_hir_id(parent) { return fn_sig.header.is_unsafe(); - } else if let Node::Block(block) = node { - if matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) { - return true; - } + } else if let Node::Block(block) = node + && matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) + { + return true; } } false @@ -396,6 +396,8 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { } } + fn use_cloned(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} + #[allow(clippy::if_same_then_else)] fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId, borrow: ty::BorrowKind) { self.prev_bind = None; @@ -424,10 +426,10 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { // upon! self.add_mutably_used_var(*vid); } - } else if borrow == ty::BorrowKind::Immutable { + } else if borrow == ty::BorrowKind::Immutable // If there is an `async block`, it'll contain a call to a closure which we need to // go into to ensure all "mutate" checks are found. - if let Node::Expr(Expr { + && let Node::Expr(Expr { kind: ExprKind::Call( _, @@ -440,9 +442,8 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { ), .. }) = self.tcx.hir_node(cmt.hir_id) - { - self.async_closures.insert(*def_id); - } + { + self.async_closures.insert(*def_id); } } @@ -458,10 +459,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { }), .. } = &cmt.place + && !projections.is_empty() { - if !projections.is_empty() { - self.add_mutably_used_var(*vid); - } + self.add_mutably_used_var(*vid); } } @@ -475,10 +475,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { }), .. } = &cmt.place + && self.is_in_unsafe_block(id) { - if self.is_in_unsafe_block(id) { - self.add_mutably_used_var(*vid); - } + self.add_mutably_used_var(*vid); } self.prev_bind = None; } @@ -497,15 +496,14 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { }), .. } = &cmt.place + && let FakeReadCause::ForLet(Some(inner)) = cause { - if let FakeReadCause::ForLet(Some(inner)) = cause { - // Seems like we are inside an async function. We need to store the closure `DefId` - // to go through it afterwards. - self.async_closures.insert(inner); - self.add_alias(cmt.hir_id, *vid); - self.prev_move_to_closure.insert(*vid); - self.prev_bind = None; - } + // Seems like we are inside an async function. We need to store the closure `DefId` + // to go through it afterwards. + self.async_closures.insert(inner); + self.add_alias(cmt.hir_id, *vid); + self.prev_move_to_closure.insert(*vid); + self.prev_bind = None; } } @@ -520,10 +518,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { }), .. } = &cmt.place + && self.is_in_unsafe_block(id) { - if self.is_in_unsafe_block(id) { - self.add_mutably_used_var(*vid); - } + self.add_mutably_used_var(*vid); } } } 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 380cc380ad0f2..95623467b8153 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 @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_self; use clippy_utils::ptr::get_spans; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; +use clippy_utils::{is_self, peel_hir_ty_options}; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; @@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { match kind { FnKind::ItemFn(.., header) => { - let attrs = cx.tcx.hir().attrs(hir_id); + let attrs = cx.tcx.hir_attrs(hir_id); if header.abi != ExternAbi::Rust || requires_exact_signature(attrs) { return; } @@ -98,13 +98,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } // Exclude non-inherent impls - if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - if matches!( + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + && matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) - ) { - return; - } + ) + { + return; } // Allow `Borrow` or functions to be taken by value @@ -197,24 +197,22 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { { // Dereference suggestion let sugg = |diag: &mut Diag<'_, ()>| { - if let ty::Adt(def, ..) = ty.kind() { - if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { - if type_allowed_to_implement_copy( - cx.tcx, - cx.param_env, - ty, - traits::ObligationCause::dummy_with_span(span), - rustc_hir::Safety::Safe, - ) - .is_ok() - { - diag.span_help(span, "or consider marking this type as `Copy`"); - } - } + if let ty::Adt(def, ..) = ty.kind() + && let Some(span) = cx.tcx.hir_span_if_local(def.did()) + && type_allowed_to_implement_copy( + cx.tcx, + cx.param_env, + ty, + traits::ObligationCause::dummy_with_span(span), + rustc_hir::Safety::Safe, + ) + .is_ok() + { + diag.span_help(span, "or consider marking this type as `Copy`"); } if is_type_diagnostic_item(cx, ty, sym::Vec) - && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]) + && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[(sym::clone, ".to_owned()")]) && let TyKind::Path(QPath::Resolved(_, path)) = input.kind && let Some(elem_ty) = path .segments @@ -254,35 +252,38 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { return; } - if is_type_lang_item(cx, ty, LangItem::String) { - if let Some(clone_spans) = - get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) - { + if is_type_lang_item(cx, ty, LangItem::String) + && let Some(clone_spans) = get_spans( + cx, + Some(body.id()), + idx, + &[(sym::clone, ".to_string()"), (sym::as_str, "")], + ) + { + diag.span_suggestion( + input.span, + "consider changing the type to", + "&str", + Applicability::Unspecified, + ); + + for (span, suggestion) in clone_spans { diag.span_suggestion( - input.span, - "consider changing the type to", - "&str", + span, + span.get_source_text(cx) + .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")), + suggestion, Applicability::Unspecified, ); - - for (span, suggestion) in clone_spans { - diag.span_suggestion( - span, - span.get_source_text(cx) - .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")), - suggestion, - Applicability::Unspecified, - ); - } - - return; } + + return; } - diag.span_suggestion( - input.span, + diag.span_suggestion_verbose( + peel_hir_ty_options(cx, input).span.shrink_to_lo(), "consider taking a reference instead", - format!("&{}", snippet(cx, input.span, "_")), + '&', Applicability::MaybeIncorrect, ); }; @@ -326,6 +327,8 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { self.move_common(cmt); } + fn use_cloned(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} + fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {} fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs index 37463cfec9a21..2a2160c3be2d1 100644 --- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs @@ -1,6 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::path_res; -use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Block, Body, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -9,52 +8,38 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Suggests alternatives for useless applications of `?` in terminating expressions + /// Suggests replacing `Ok(x?)` or `Some(x?)` with `x` in return positions where the `?` operator + /// is not needed to convert the type of `x`. /// /// ### Why is this bad? /// There's no reason to use `?` to short-circuit when execution of the body will end there anyway. /// /// ### Example /// ```no_run - /// struct TO { - /// magic: Option, + /// # use std::num::ParseIntError; + /// fn f(s: &str) -> Option { + /// Some(s.find('x')?) /// } /// - /// fn f(to: TO) -> Option { - /// Some(to.magic?) + /// fn g(s: &str) -> Result { + /// Ok(s.parse()?) /// } - /// - /// struct TR { - /// magic: Result, - /// } - /// - /// fn g(tr: Result) -> Result { - /// tr.and_then(|t| Ok(t.magic?)) - /// } - /// /// ``` /// Use instead: /// ```no_run - /// struct TO { - /// magic: Option, + /// # use std::num::ParseIntError; + /// fn f(s: &str) -> Option { + /// s.find('x') /// } /// - /// fn f(to: TO) -> Option { - /// to.magic - /// } - /// - /// struct TR { - /// magic: Result, - /// } - /// - /// fn g(tr: Result) -> Result { - /// tr.and_then(|t| t.magic) + /// fn g(s: &str) -> Result { + /// s.parse() /// } /// ``` #[clippy::version = "1.51.0"] pub NEEDLESS_QUESTION_MARK, complexity, - "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result`." + "using `Ok(x?)` or `Some(x?)` where `x` would be equivalent" } declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); @@ -111,10 +96,10 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(path, [arg]) = expr.kind && let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path) && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) - && let sugg_remove = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { - "Some()" + && let variant = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { + "Some" } else if cx.tcx.lang_items().result_ok_variant() == Some(variant_id) { - "Ok()" + "Ok" } else { return; } @@ -126,14 +111,25 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { && let inner_ty = cx.typeck_results().expr_ty(inner_expr) && expr_ty == inner_ty { - span_lint_and_sugg( + span_lint_hir_and_then( cx, NEEDLESS_QUESTION_MARK, + expr.hir_id, expr.span, - "question mark operator is useless here", - format!("try removing question mark and `{sugg_remove}`"), - format!("{}", snippet(cx, inner_expr.span, r#""...""#)), - Applicability::MachineApplicable, + format!("enclosing `{variant}` and `?` operator are unneeded"), + |diag| { + diag.multipart_suggestion( + format!("remove the enclosing `{variant}` and `?` operator"), + vec![ + (expr.span.until(inner_expr.span), String::new()), + ( + inner_expr.span.shrink_to_hi().to(expr.span.shrink_to_hi()), + String::new(), + ), + ], + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/needless_update.rs b/src/tools/clippy/clippy_lints/src/needless_update.rs index 0cba72bd2c6a9..4a86c3720ca24 100644 --- a/src/tools/clippy/clippy_lints/src/needless_update.rs +++ b/src/tools/clippy/clippy_lints/src/needless_update.rs @@ -53,17 +53,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Struct(_, fields, StructTailExpr::Base(base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, _) = ty.kind() { - if fields.len() == def.non_enum_variant().fields.len() - && !def.variant(0_usize.into()).is_field_list_non_exhaustive() - { - span_lint( - cx, - NEEDLESS_UPDATE, - base.span, - "struct update has no effect, all the fields in the struct have already been specified", - ); - } + if let ty::Adt(def, _) = ty.kind() + && fields.len() == def.non_enum_variant().fields.len() + && !def.variant(0_usize.into()).is_field_list_non_exhaustive() + { + span_lint( + cx, + NEEDLESS_UPDATE, + base.span, + "struct update has no effect, all the fields in the struct have already been specified", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs index 429afff9b6642..74c8142787ebc 100644 --- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs +++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs @@ -16,9 +16,6 @@ declare_clippy_lint! { /// ### Why is this bad? /// It's more readable to just negate. /// - /// ### Known problems - /// This only catches integers (for now). - /// /// ### Example /// ```rust,ignore /// let a = x * -1; @@ -38,23 +35,32 @@ declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, left, right) = e.kind { - if BinOpKind::Mul == op.node { - match (&left.kind, &right.kind) { - (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, - (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), - (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), - _ => {}, - } + if let ExprKind::Binary(ref op, left, right) = e.kind + && BinOpKind::Mul == op.node + { + match (&left.kind, &right.kind) { + (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, + (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), + (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), + _ => {}, } } } } fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { + const F16_ONE: u16 = 1.0_f16.to_bits(); + const F128_ONE: u128 = 1.0_f128.to_bits(); if let ExprKind::Lit(l) = lit.kind - && consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1) - && cx.typeck_results().expr_ty(exp).is_integral() + && matches!( + consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)), + Constant::Int(1) + | Constant::F16(F16_ONE) + | Constant::F32(1.0) + | Constant::F64(1.0) + | Constant::F128(F128_ONE) + ) + && cx.typeck_results().expr_ty(exp).is_numeric() { let mut applicability = Applicability::MachineApplicable; let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); 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 f0ee613791fb9..4b73a4455f55b 100644 --- a/src/tools/clippy/clippy_lints/src/new_without_default.rs +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -97,14 +97,14 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { { if self.impling_types.is_none() { let mut impls = HirIdSet::default(); - cx.tcx.for_each_impl(default_trait_id, |d| { + for &d in cx.tcx.local_trait_impls(default_trait_id) { let ty = cx.tcx.type_of(d).instantiate_identity(); - if let Some(ty_def) = ty.ty_adt_def() { - if let Some(local_def_id) = ty_def.did().as_local() { - impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); - } + if let Some(ty_def) = ty.ty_adt_def() + && let Some(local_def_id) = ty_def.did().as_local() + { + impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); } - }); + } self.impling_types = Some(impls); } diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 7187a8f2c11a1..02c48166131ef 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -182,23 +182,22 @@ impl NoEffect { ); return true; } - } else if let StmtKind::Let(local) = stmt.kind { - if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) - && !matches!(local.source, LocalSource::AsyncFn) - && let Some(init) = local.init - && local.els.is_none() - && !local.pat.span.from_expansion() - && has_no_effect(cx, init) - && let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind - && ident.name.to_ident_string().starts_with('_') - && !in_automatically_derived(cx.tcx, local.hir_id) - { - if let Some(l) = self.local_bindings.last_mut() { - l.push(hir_id); - self.underscore_bindings.insert(hir_id, ident.span); - } - return true; + } else if let StmtKind::Let(local) = stmt.kind + && !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) + && !matches!(local.source, LocalSource::AsyncFn) + && let Some(init) = local.init + && local.els.is_none() + && !local.pat.span.from_expansion() + && has_no_effect(cx, init) + && let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind + && ident.name.to_ident_string().starts_with('_') + && !in_automatically_derived(cx.tcx, local.hir_id) + { + if let Some(l) = self.local_bindings.last_mut() { + l.push(hir_id); + self.underscore_bindings.insert(hir_id, ident.span); } + return true; } false } @@ -222,10 +221,16 @@ fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } +/// Checks if dropping `expr` might have a visible side effect. +fn expr_ty_has_significant_drop(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + ty.has_significant_drop(cx.tcx, cx.typing_env()) +} + fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Lit(..) | ExprKind::Closure { .. } => true, - ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)), + ExprKind::Path(..) => !expr_ty_has_significant_drop(cx, expr), ExprKind::Index(a, b, _) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b), ExprKind::Array(v) | ExprKind::Tup(v) => v.iter().all(|val| has_no_effect(cx, val)), ExprKind::Repeat(inner, _) @@ -234,8 +239,8 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | ExprKind::Unary(_, inner) | ExprKind::Field(inner, _) | ExprKind::AddrOf(_, _, inner) => has_no_effect(cx, inner), - ExprKind::Struct(_, fields, ref base) => { - !has_drop(cx, cx.typeck_results().expr_ty(expr)) + ExprKind::Struct(_, fields, base) => { + !expr_ty_has_significant_drop(cx, expr) && fields.iter().all(|field| has_no_effect(cx, field.expr)) && match &base { StructTailExpr::None | StructTailExpr::DefaultFields(_) => true, @@ -253,7 +258,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..) ); if def_matched || is_range_literal(expr) { - !has_drop(cx, cx.typeck_results().expr_ty(expr)) && args.iter().all(|arg| has_no_effect(cx, arg)) + !expr_ty_has_significant_drop(cx, expr) && args.iter().all(|arg| has_no_effect(cx, arg)) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs index 1baa3cb2f0fdc..b71dde9069184 100644 --- a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs +++ b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -37,12 +37,12 @@ declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]); impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Fn { sig: fn_sig, .. } = &item.kind + if let ItemKind::Fn { ident, sig: fn_sig, .. } = &item.kind && !item.span.from_expansion() { - let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attrs = cx.tcx.hir_attrs(item.hir_id()); let mut app = Applicability::MaybeIncorrect; - let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(item.ident.span.lo()), "..", &mut app); + let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(ident.span.lo()), "..", &mut app); for attr in attrs { if let Some(ident) = attr.ident() && ident.name == rustc_span::sym::no_mangle diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs index 448bb603cf2c9..93865197ec965 100644 --- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs +++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs @@ -1,6 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core}; +use clippy_utils::{ + is_diag_trait_item, is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core, +}; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp}; @@ -98,7 +100,7 @@ declare_clippy_lint! { /// /// impl PartialOrd for A { /// fn partial_cmp(&self, other: &Self) -> Option { - /// Some(self.cmp(other)) + /// Some(self.cmp(other)) // or self.cmp(other).into() /// } /// } /// ``` @@ -185,65 +187,66 @@ impl LateLintPass<'_> for NonCanonicalImpls { if block.stmts.is_empty() && let Some(expr) = block.expr - && expr_is_cmp(cx, &expr.kind, impl_item, &mut needs_fully_qualified) + && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) { + return; } // Fix #12683, allow [`needless_return`] here else if block.expr.is_none() && let Some(stmt) = block.stmts.first() && let rustc_hir::StmtKind::Semi(Expr { - kind: ExprKind::Ret(Some(Expr { kind: ret_kind, .. })), + kind: ExprKind::Ret(Some(ret)), .. }) = stmt.kind - && expr_is_cmp(cx, ret_kind, impl_item, &mut needs_fully_qualified) + && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) { - } else { - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid - // suggestion tons more complex. - if let [lhs, rhs, ..] = trait_impl.args.as_slice() - && lhs != rhs - { - return; - } + return; + } + // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid + // suggestion tons more complex. + else if let [lhs, rhs, ..] = trait_impl.args.as_slice() + && lhs != rhs + { + return; + } - span_lint_and_then( - cx, - NON_CANONICAL_PARTIAL_ORD_IMPL, - item.span, - "non-canonical implementation of `partial_cmp` on an `Ord` type", - |diag| { - let [_, other] = body.params else { - return; - }; - let Some(std_or_core) = std_or_core(cx) else { - return; - }; + span_lint_and_then( + cx, + NON_CANONICAL_PARTIAL_ORD_IMPL, + item.span, + "non-canonical implementation of `partial_cmp` on an `Ord` type", + |diag| { + let [_, other] = body.params else { + return; + }; + let Some(std_or_core) = std_or_core(cx) else { + return; + }; - let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { - (Some(other_ident), true) => vec![( + let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { + (Some(other_ident), true) => vec![( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), + )], + (Some(other_ident), false) => { + vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] + }, + (None, true) => vec![ + ( block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), - )], - (Some(other_ident), false) => { - vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] - }, - (None, true) => vec![ - ( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), - ), - (other.pat.span, "other".to_owned()), - ], - (None, false) => vec![ - (block.span, "{ Some(self.cmp(other)) }".to_owned()), - (other.pat.span, "other".to_owned()), - ], - }; + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), + ), + (other.pat.span, "other".to_owned()), + ], + (None, false) => vec![ + (block.span, "{ Some(self.cmp(other)) }".to_owned()), + (other.pat.span, "other".to_owned()), + ], + }; - diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); - }, - ); - } + diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); + }, + ); } } } @@ -251,10 +254,11 @@ impl LateLintPass<'_> for NonCanonicalImpls { /// Return true if `expr_kind` is a `cmp` call. fn expr_is_cmp<'tcx>( cx: &LateContext<'tcx>, - expr_kind: &'tcx ExprKind<'tcx>, + expr: &'tcx Expr<'tcx>, impl_item: &ImplItem<'_>, needs_fully_qualified: &mut bool, ) -> bool { + let impl_item_did = impl_item.owner_id.def_id; if let ExprKind::Call( Expr { kind: ExprKind::Path(some_path), @@ -262,11 +266,17 @@ fn expr_is_cmp<'tcx>( .. }, [cmp_expr], - ) = expr_kind + ) = expr.kind { is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) // Fix #11178, allow `Self::cmp(self, ..)` too - && self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, needs_fully_qualified) + && self_cmp_call(cx, cmp_expr, impl_item_did, needs_fully_qualified) + } else if let ExprKind::MethodCall(_, recv, [], _) = expr.kind { + cx.tcx + .typeck(impl_item_did) + .type_dependent_def_id(expr.hir_id) + .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) + && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) } else { false } 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 d4da12451f181..a27c6aa75e369 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -1,56 +1,78 @@ -use std::ptr; +// Implementation for lints detecting interior mutability in constants. +// +// For `declare_interior_mutable_const` there are three strategies used to +// determine if a value has interior mutability: +// * A type-based check. This is the least accurate, but can always run. +// * A const-eval based check. This is the most accurate, but this requires that the value is +// defined and does not work with generics. +// * A HIR-tree based check. This is less accurate than const-eval, but it can be applied to generic +// values. +// +// For `borrow_interior_mutable_const` the same three strategies are applied +// when checking a constant's value, but field and array index projections at +// the borrow site are taken into account as well. As an example: `FOO.bar` may +// have interior mutability, but `FOO.baz` may not. When borrowing `FOO.baz` no +// warning will be issued. +// +// No matter the lint or strategy, a warning should only be issued if a value +// definitely contains interior mutability. use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::is_in_const_context; use clippy_utils::macros::macro_backtrace; -use clippy_utils::ty::{InteriorMut, implements_trait}; -use rustc_abi::VariantIdx; +use clippy_utils::paths::{PathNS, lookup_path_str}; +use clippy_utils::ty::{get_field_idx_by_name, implements_trait}; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ - BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, + Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, StructTailExpr, TraitItem, TraitItemKind, UnOp, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::mir::{ConstValue, UnevaluatedConst}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::{ + self, AliasTyKind, EarlyBinder, GenericArgs, GenericArgsRef, Instance, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, + TypeckResults, TypingEnv, }; -use rustc_lint::{LateContext, LateLintPass, Lint}; -use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId, ReportedErrorInfo}; -use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, sym}; +use std::collections::hash_map::Entry; -// FIXME: this is a correctness problem but there's no suitable -// warn-by-default category. declare_clippy_lint! { /// ### What it does - /// Checks for declaration of `const` items which is interior - /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.). + /// Checks for the declaration of named constant which contain interior mutability. /// /// ### Why is this bad? - /// Consts are copied everywhere they are referenced, i.e., - /// every time you refer to the const a fresh instance of the `Cell` or `Mutex` - /// or `AtomicXxxx` will be created, which defeats the whole purpose of using - /// these types in the first place. + /// Named constants are copied at every use site which means any change to their value + /// will be lost after the newly created value is dropped. e.g. /// - /// The `const` should better be replaced by a `static` item if a global - /// variable is wanted, or replaced by a `const fn` if a constructor is wanted. + /// ```rust + /// use core::sync::atomic::{AtomicUsize, Ordering}; + /// const ATOMIC: AtomicUsize = AtomicUsize::new(0); + /// fn add_one() -> usize { + /// // This will always return `0` since `ATOMIC` is copied before it's used. + /// ATOMIC.fetch_add(1, Ordering::AcqRel) + /// } + /// ``` /// - /// ### Known problems - /// A "non-constant" const item is a legacy way to supply an - /// initialized value to downstream `static` items (e.g., the - /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, - /// and this lint should be suppressed. + /// If shared modification of the value is desired, a `static` item is needed instead. + /// If that is not desired, a `const fn` constructor should be used to make it obvious + /// at the use site that a new value is created. /// - /// Even though the lint avoids triggering on a constant whose type has enums that have variants - /// with interior mutability, and its value uses non interior mutable variants (see - /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and - /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples); - /// it complains about associated constants without default values only based on its types; - /// which might not be preferable. - /// There're other enums plus associated constants cases that the lint cannot handle. + /// ### Known problems + /// Prior to `const fn` stabilization this was the only way to provide a value which + /// could initialize a `static` item (e.g. the `std::sync::ONCE_INIT` constant). In + /// this case the use of `const` is required and this lint should be suppressed. /// - /// Types that have underlying or potential interior mutability trigger the lint whether - /// the interior mutable field is used or not. See issue - /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) + /// There also exists types which contain private fields with interior mutability, but + /// no way to both create a value as a constant and modify any mutable field using the + /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to + /// scan a crate's interface to see if this is the case, all such types will be linted. + /// If this happens use the `ignore-interior-mutability` configuration option to allow + /// the type. /// /// ### Example /// ```no_run @@ -74,20 +96,44 @@ declare_clippy_lint! { "declaring `const` with interior mutability" } -// FIXME: this is a correctness problem but there's no suitable -// warn-by-default category. declare_clippy_lint! { /// ### What it does - /// Checks if `const` items which is interior mutable (e.g., - /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly. + /// Checks for a borrow of a named constant with interior mutability. /// /// ### Why is this bad? - /// Consts are copied everywhere they are referenced, i.e., - /// every time you refer to the const a fresh instance of the `Cell` or `Mutex` - /// or `AtomicXxxx` will be created, which defeats the whole purpose of using - /// these types in the first place. + /// Named constants are copied at every use site which means any change to their value + /// will be lost after the newly created value is dropped. e.g. + /// + /// ```rust + /// use core::sync::atomic::{AtomicUsize, Ordering}; + /// const ATOMIC: AtomicUsize = AtomicUsize::new(0); + /// fn add_one() -> usize { + /// // This will always return `0` since `ATOMIC` is copied before it's borrowed + /// // for use by `fetch_add`. + /// ATOMIC.fetch_add(1, Ordering::AcqRel) + /// } + /// ``` /// - /// The `const` value should be stored inside a `static` item. + /// ### Known problems + /// This lint does not, and cannot in general, determine if the borrow of the constant + /// is used in a way which causes a mutation. e.g. + /// + /// ```rust + /// use core::cell::Cell; + /// const CELL: Cell = Cell::new(0); + /// fn get_cell() -> Cell { + /// // This is fine. It borrows a copy of `CELL`, but never mutates it through the + /// // borrow. + /// CELL.clone() + /// } + /// ``` + /// + /// There also exists types which contain private fields with interior mutability, but + /// no way to both create a value as a constant and modify any mutable field using the + /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to + /// scan a crate's interface to see if this is the case, all such types will be linted. + /// If this happens use the `ignore-interior-mutability` configuration option to allow + /// the type. /// /// ### Example /// ```no_run @@ -113,60 +159,101 @@ declare_clippy_lint! { "referencing `const` with interior mutability" } -#[derive(Copy, Clone)] -enum Source<'tcx> { - Item { item: Span, ty: Ty<'tcx> }, - Assoc { item: Span }, - Expr { expr: Span }, +#[derive(Clone, Copy)] +enum IsFreeze { + /// The type and all possible values are `Freeze` + Yes, + /// The type itself is non-`Freeze`, but not all values are. + Maybe, + /// The type and all possible values are non-`Freeze` + No, } +impl IsFreeze { + /// Merges the variants of a sum type (i.e. an enum). + fn from_variants(iter: impl Iterator) -> Self { + iter.fold(Self::Yes, |x, y| match (x, y) { + (Self::Maybe, _) | (_, Self::Maybe) | (Self::No, Self::Yes) | (Self::Yes, Self::No) => Self::Maybe, + (Self::No, Self::No) => Self::No, + (Self::Yes, Self::Yes) => Self::Yes, + }) + } -impl Source<'_> { - #[must_use] - fn lint(&self) -> (&'static Lint, &'static str, Span) { - match self { - Self::Item { item, .. } | Self::Assoc { item, .. } => ( - DECLARE_INTERIOR_MUTABLE_CONST, - "a `const` item should not be interior mutable", - *item, - ), - Self::Expr { expr } => ( - BORROW_INTERIOR_MUTABLE_CONST, - "a `const` item with interior mutability should not be borrowed", - *expr, - ), - } + /// Merges the fields of a product type (e.g. a struct or tuple). + fn from_fields(mut iter: impl Iterator) -> Self { + iter.try_fold(Self::Yes, |x, y| match (x, y) { + (Self::No, _) | (_, Self::No) => None, + (Self::Maybe, _) | (_, Self::Maybe) => Some(Self::Maybe), + (Self::Yes, Self::Yes) => Some(Self::Yes), + }) + .unwrap_or(Self::No) + } + + /// Checks if this is definitely `Freeze`. + fn is_freeze(self) -> bool { + matches!(self, Self::Yes) + } + + /// Checks if this is definitely not `Freeze`. + fn is_not_freeze(self) -> bool { + matches!(self, Self::No) } } -fn lint<'tcx>(cx: &LateContext<'tcx>, source: Source<'tcx>) { - let (lint, msg, span) = source.lint(); - span_lint_and_then(cx, lint, span, msg, |diag| { - if span.from_expansion() { - return; // Don't give suggestions into macros. - } - match source { - Source::Item { ty, .. } => { - let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else { - return; - }; - if implements_trait(cx, ty, sync_trait, &[]) { - diag.help("consider making this a static item"); - } else { - diag.help( - "consider making this `Sync` so that it can go in a static item or using a `thread_local`", - ); - } - }, - Source::Assoc { .. } => (), - Source::Expr { .. } => { - diag.help("assign this const to a local or static variable, and use the variable here"); +/// What operation caused a borrow to occur. +#[derive(Clone, Copy)] +enum BorrowCause { + Borrow, + Deref, + Index, + AutoDeref, + AutoBorrow, + AutoDerefField, +} +impl BorrowCause { + fn note(self) -> Option<&'static str> { + match self { + Self::Borrow => None, + Self::Deref => Some("this deref expression is a call to `Deref::deref`"), + Self::Index => Some("this index expression is a call to `Index::index`"), + Self::AutoDeref => Some("there is a compiler inserted call to `Deref::deref` here"), + Self::AutoBorrow => Some("there is a compiler inserted borrow here"), + Self::AutoDerefField => { + Some("there is a compiler inserted call to `Deref::deref` when accessing this field") }, } - }); + } +} + +/// The source of a borrow. Both what caused it and where. +struct BorrowSource<'tcx> { + expr: &'tcx Expr<'tcx>, + cause: BorrowCause, +} +impl<'tcx> BorrowSource<'tcx> { + fn new(tcx: TyCtxt<'tcx>, expr: &'tcx Expr<'tcx>, cause: BorrowCause) -> Self { + // Custom deref and index impls will always have an auto-borrow inserted since we + // never work with reference types. + let (expr, cause) = if matches!(cause, BorrowCause::AutoBorrow) + && let Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) + { + match parent.kind { + ExprKind::Unary(UnOp::Deref, _) => (parent, BorrowCause::Deref), + ExprKind::Index(..) => (parent, BorrowCause::Index), + ExprKind::Field(..) => (parent, BorrowCause::AutoDerefField), + _ => (expr, cause), + } + } else { + (expr, cause) + }; + Self { expr, cause } + } } pub struct NonCopyConst<'tcx> { - interior_mut: InteriorMut<'tcx>, + ignore_tys: DefIdSet, + // Cache checked types. We can recurse through a type multiple times so this + // can be hit quite frequently. + freeze_tys: FxHashMap, IsFreeze>, } impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); @@ -174,332 +261,629 @@ impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTE impl<'tcx> NonCopyConst<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { Self { - interior_mut: InteriorMut::without_pointers(tcx, &conf.ignore_interior_mutability), + ignore_tys: conf + .ignore_interior_mutability + .iter() + .flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty)) + .collect(), + freeze_tys: FxHashMap::default(), } } - fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool { - // No branch that we check (yet) should continue if val isn't a branch - let Some(branched_val) = val.try_to_branch() else { - return false; - }; - match *ty.kind() { - // the fact that we have to dig into every structs to search enums - // leads us to the point checking `UnsafeCell` directly is the only option. - ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true, - // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the - // contained value. - ty::Adt(def, ..) if def.is_union() => false, - ty::Array(ty, _) => branched_val - .iter() - .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Adt(def, args) if def.is_enum() => { - let Some((&variant_valtree, fields)) = branched_val.split_first() else { - return false; - }; - let variant_index = variant_valtree.unwrap_leaf(); - let variant_index = VariantIdx::from_u32(variant_index.to_u32()); - fields - .iter() - .copied() - .zip( - def.variants()[variant_index] + /// Checks if a value of the given type is `Freeze`, or may be depending on the value. + fn is_ty_freeze(&mut self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, ty: Ty<'tcx>) -> IsFreeze { + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match self.freeze_tys.entry(ty) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => { + let e = e.insert(IsFreeze::Yes); + if ty.is_freeze(tcx, typing_env) { + return IsFreeze::Yes; + } + let is_freeze = match *ty.kind() { + ty::Adt(adt, _) if adt.is_unsafe_cell() => { + *e = IsFreeze::No; + return IsFreeze::No; + }, + ty::Adt(adt, _) if self.ignore_tys.contains(&adt.did()) => return IsFreeze::Yes, + ty::Adt(adt, args) if adt.is_enum() => IsFreeze::from_variants(adt.variants().iter().map(|v| { + IsFreeze::from_fields( + v.fields + .iter() + .map(|f| self.is_ty_freeze(tcx, typing_env, f.ty(tcx, args))), + ) + })), + // Workaround for `ManuallyDrop`-like unions. + ty::Adt(adt, args) + if adt.is_union() + && adt.non_enum_variant().fields.iter().any(|f| { + tcx.layout_of(typing_env.as_query_input(f.ty(tcx, args))) + .is_ok_and(|l| l.layout.size().bytes() == 0) + }) => + { + return IsFreeze::Yes; + }, + // Rust doesn't have the concept of an active union field so we have + // to treat all fields as active. + ty::Adt(adt, args) => IsFreeze::from_fields( + adt.non_enum_variant() .fields .iter() - .map(|field| field.ty(cx.tcx, args)), - ) - .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty)) - }, - ty::Adt(def, args) => branched_val - .iter() - .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))) - .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Tuple(tys) => branched_val - .iter() - .zip(tys) - .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), - ty::Alias(ty::Projection, _) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) { - Ok(normalized_ty) if ty != normalized_ty => Self::is_value_unfrozen_raw_inner(cx, val, normalized_ty), - _ => false, + .map(|f| self.is_ty_freeze(tcx, typing_env, f.ty(tcx, args))), + ), + ty::Array(ty, _) | ty::Pat(ty, _) => self.is_ty_freeze(tcx, typing_env, ty), + ty::Tuple(tys) => { + IsFreeze::from_fields(tys.iter().map(|ty| self.is_ty_freeze(tcx, typing_env, ty))) + }, + // Treat type parameters as though they were `Freeze`. + ty::Param(_) | ty::Alias(..) => return IsFreeze::Yes, + // TODO: check other types. + _ => { + *e = IsFreeze::No; + return IsFreeze::No; + }, + }; + if !is_freeze.is_freeze() { + self.freeze_tys.insert(ty, is_freeze); + } + is_freeze }, - _ => false, } } - fn is_value_unfrozen_raw( - cx: &LateContext<'tcx>, - result: Result, Ty<'tcx>>, ErrorHandled>, + /// Checks if the given constant value is `Freeze`. Returns `Err` if the constant + /// cannot be read, but the result depends on the value. + fn is_value_freeze( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, ty: Ty<'tcx>, - ) -> bool { - result.map_or_else( - |err| { - // Consider `TooGeneric` cases as being unfrozen. - // This causes a false positive where an assoc const whose type is unfrozen - // have a value that is a frozen variant with a generic param (an example is - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`). - // However, it prevents a number of false negatives that is, I think, important: - // 1. assoc consts in trait defs referring to consts of themselves (an example is - // `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`). - // 2. a path expr referring to assoc consts whose type is doesn't have any frozen variants in trait - // defs (i.e. without substitute for `Self`). (e.g. borrowing - // `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`) - // 3. similar to the false positive above; but the value is an unfrozen variant, or the type has no - // enums. (An example is - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` and - // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`). - // One might be able to prevent these FNs correctly, and replace this with `false`; - // e.g. implementing `has_frozen_variant` described above, and not running this function - // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd - // case (that actually removes another suboptimal behavior (I won't say 'false positive') where, - // similar to 2., but with a frozen variant) (e.g. borrowing - // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`). - // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). - matches!(err, ErrorHandled::TooGeneric(..)) + val: ConstValue<'tcx>, + ) -> Result { + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match self.is_ty_freeze(tcx, typing_env, ty) { + IsFreeze::Yes => Ok(true), + IsFreeze::Maybe if matches!(ty.kind(), ty::Adt(..) | ty::Array(..) | ty::Tuple(..)) => { + for &(val, ty) in tcx + .try_destructure_mir_constant_for_user_output(val, ty) + .ok_or(())? + .fields + { + if !self.is_value_freeze(tcx, typing_env, ty, val)? { + return Ok(false); + } + } + Ok(true) }, - |val| val.map_or(true, |val| Self::is_value_unfrozen_raw_inner(cx, val, ty)), - ) - } - - fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { - let def_id = body_id.hir_id.owner.to_def_id(); - let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id); - let instance = ty::Instance::new(def_id, args); - let cid = GlobalId { - instance, - promoted: None, - }; - let typing_env = ty::TypingEnv::post_analysis(cx.tcx, def_id); - let result = cx.tcx.const_eval_global_id_for_typeck(typing_env, cid, DUMMY_SP); - Self::is_value_unfrozen_raw(cx, result, ty) - } - - fn is_value_unfrozen_expr(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { - let args = cx.typeck_results().node_args(hir_id); - - let result = Self::const_eval_resolve( - cx.tcx, - cx.typing_env(), - ty::UnevaluatedConst::new(def_id, args), - DUMMY_SP, - ); - Self::is_value_unfrozen_raw(cx, result, ty) + IsFreeze::Maybe | IsFreeze::No => Ok(false), + } } - pub fn const_eval_resolve( + /// Checks if the given expression creates a value which is `Freeze`. + /// + /// This will return `true` if the type is maybe `Freeze`, but it cannot be + /// determined for certain from the value. + /// + /// `typing_env` and `gen_args` are from the constant's use site. + /// `typeck` and `e` are from the constant's definition site. + fn is_init_expr_freeze( + &mut self, tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ct: ty::UnevaluatedConst<'tcx>, - span: Span, - ) -> EvalToValTreeResult<'tcx> { - match ty::Instance::try_resolve(tcx, typing_env, ct.def, ct.args) { - Ok(Some(instance)) => { - let cid = GlobalId { - instance, - promoted: None, - }; - tcx.const_eval_global_id_for_typeck(typing_env, cid, span) + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + gen_args: GenericArgsRef<'tcx>, + e: &'tcx Expr<'tcx>, + ) -> bool { + // Make sure to instantiate all types coming from `typeck` with `gen_args`. + let ty = EarlyBinder::bind(typeck.expr_ty(e)).instantiate(tcx, gen_args); + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + match self.is_ty_freeze(tcx, typing_env, ty) { + IsFreeze::Yes => true, + IsFreeze::No => false, + IsFreeze::Maybe => match e.kind { + ExprKind::Block(b, _) + if !b.targeted_by_break + && b.stmts.is_empty() + && let Some(e) = b.expr => + { + self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e) + }, + ExprKind::Path(ref p) => { + let res = typeck.qpath_res(p, e.hir_id); + let gen_args = EarlyBinder::bind(typeck.node_args(e.hir_id)).instantiate(tcx, gen_args); + match res { + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Ok(val) = + tcx.const_eval_resolve(typing_env, UnevaluatedConst::new(did, gen_args), DUMMY_SP) + && let Ok(is_freeze) = self.is_value_freeze(tcx, typing_env, ty, val) => + { + is_freeze + }, + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Some((typeck, init)) = get_const_hir_value(tcx, typing_env, did, gen_args) => + { + self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, init) + }, + // Either this is a unit constructor, or some unknown value. + // In either case we consider the value to be `Freeze`. + _ => true, + } + }, + ExprKind::Call(callee, args) + if let ExprKind::Path(p) = &callee.kind + && let res = typeck.qpath_res(p, callee.hir_id) + && matches!(res, Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(_)) => + { + args.iter() + .all(|e| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e)) + }, + ExprKind::Struct(_, fields, StructTailExpr::None) => fields + .iter() + .all(|f| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, f.expr)), + ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs + .iter() + .all(|e| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e)), + ExprKind::Repeat(e, _) => self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e), + _ => true, }, - Ok(None) => Err(ErrorHandled::TooGeneric(span)), - Err(err) => Err(ErrorHandled::Reported( - ReportedErrorInfo::non_const_eval_error(err), - span, - )), } } -} -impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { - fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { - if let ItemKind::Const(.., body_id) = it.kind { - let ty = cx.tcx.type_of(it.owner_id).instantiate_identity(); - if !ignored_macro(cx, it) - && self.interior_mut.is_interior_mut_ty(cx, ty) - && Self::is_value_unfrozen_poly(cx, body_id, ty) - { - lint(cx, Source::Item { item: it.span, ty }); + /// Checks if the given expression (or a local projection of it) is both borrowed and + /// definitely a non-`Freeze` type. + fn is_non_freeze_expr_borrowed( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut src_expr: &'tcx Expr<'tcx>, + ) -> Option> { + let mut parents = tcx.hir_parent_iter(src_expr.hir_id); + loop { + let ty = typeck.expr_ty(src_expr); + // Normalized as we need to check if this is an array later. + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + let is_freeze = self.is_ty_freeze(tcx, typing_env, ty); + if is_freeze.is_freeze() { + return None; } - } - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Const(_, body_id_opt) = &trait_item.kind { - let ty = cx.tcx.type_of(trait_item.owner_id).instantiate_identity(); - - // Normalize assoc types because ones originated from generic params - // bounded other traits could have their bound. - let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty); - if self.interior_mut.is_interior_mut_ty(cx, normalized) - // When there's no default value, lint it only according to its type; - // in other words, lint consts whose value *could* be unfrozen, not definitely is. - // This feels inconsistent with how the lint treats generic types, - // which avoids linting types which potentially become unfrozen. - // One could check whether an unfrozen type have a *frozen variant* - // (like `body_id_opt.map_or_else(|| !has_frozen_variant(...), ...)`), - // and do the same as the case of generic types at impl items. - // Note that it isn't sufficient to check if it has an enum - // since all of that enum's variants can be unfrozen: - // i.e. having an enum doesn't necessary mean a type has a frozen variant. - // And, implementing it isn't a trivial task; it'll probably end up - // re-implementing the trait predicate evaluation specific to `Freeze`. - && body_id_opt.is_none_or(|body_id| Self::is_value_unfrozen_poly(cx, body_id, normalized)) - { - lint(cx, Source::Assoc { item: trait_item.span }); + if let [adjust, ..] = typeck.expr_adjustments(src_expr) { + return does_adjust_borrow(adjust) + .filter(|_| is_freeze.is_not_freeze()) + .map(|cause| BorrowSource::new(tcx, src_expr, cause)); + } + let Some((_, Node::Expr(use_expr))) = parents.next() else { + return None; + }; + match use_expr.kind { + ExprKind::Field(..) => {}, + ExprKind::Index(..) if ty.is_array() => {}, + ExprKind::AddrOf(..) if is_freeze.is_not_freeze() => { + return Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow)); + }, + // All other expressions use the value. + _ => return None, } + src_expr = use_expr; } } - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Const(_, body_id) = &impl_item.kind { - let item_def_id = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; - let item = cx.tcx.hir().expect_item(item_def_id); - - match &item.kind { - ItemKind::Impl(Impl { - of_trait: Some(of_trait_ref), - .. - }) => { - if let Some(of_trait_def_id) = of_trait_ref.trait_def_id() - // Lint a trait impl item only when the definition is a generic type, - // assuming an assoc const is not meant to be an interior mutable type. - && let Some(of_assoc_item) = cx - .tcx - .associated_item(impl_item.owner_id) - .trait_item_def_id - && cx - .tcx - .layout_of(ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id).as_query_input( - // Normalize assoc types because ones originated from generic params - // bounded other traits could have their bound at the trait defs; - // and, in that case, the definition is *not* generic. - cx.tcx.normalize_erasing_regions( - ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id), - cx.tcx.type_of(of_assoc_item).instantiate_identity(), - ), - )) - .is_err() - // If there were a function like `has_frozen_variant` described above, - // we should use here as a frozen variant is a potential to be frozen - // similar to unknown layouts. - // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` - && let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity() - && let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty) - && self.interior_mut.is_interior_mut_ty(cx, normalized) - && Self::is_value_unfrozen_poly(cx, *body_id, normalized) - { - lint(cx, Source::Assoc { item: impl_item.span }); + /// Checks if the given value (or a local projection of it) is both borrowed and + /// definitely non-`Freeze`. Returns `Err` if the constant cannot be read, but the + /// result depends on the value. + fn is_non_freeze_val_borrowed( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut src_expr: &'tcx Expr<'tcx>, + mut val: ConstValue<'tcx>, + ) -> Result>, ()> { + let mut parents = tcx.hir_parent_iter(src_expr.hir_id); + let mut ty = typeck.expr_ty(src_expr); + loop { + // Normalized as we need to check if this is an array later. + ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + if let [adjust, ..] = typeck.expr_adjustments(src_expr) { + let res = if let Some(cause) = does_adjust_borrow(adjust) + && !self.is_value_freeze(tcx, typing_env, ty, val)? + { + Some(BorrowSource::new(tcx, src_expr, cause)) + } else { + None + }; + return Ok(res); + } + // Check only the type here as the result gets cached for each type. + if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() { + return Ok(None); + } + let Some((_, Node::Expr(use_expr))) = parents.next() else { + return Ok(None); + }; + let next_val = match use_expr.kind { + ExprKind::Field(_, name) => { + if let Some(idx) = get_field_idx_by_name(ty, name.name) { + tcx.try_destructure_mir_constant_for_user_output(val, ty) + .ok_or(())? + .fields + .get(idx) + } else { + return Ok(None); } }, - ItemKind::Impl(Impl { of_trait: None, .. }) => { - let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); - // Normalize assoc types originated from generic params. - let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty); - - if self.interior_mut.is_interior_mut_ty(cx, normalized) - && Self::is_value_unfrozen_poly(cx, *body_id, normalized) - { - lint(cx, Source::Assoc { item: impl_item.span }); + ExprKind::Index(_, idx, _) if ty.is_array() => { + let val = tcx.try_destructure_mir_constant_for_user_output(val, ty).ok_or(())?; + if let Some(Constant::Int(idx)) = ConstEvalCtxt::with_env(tcx, typing_env, typeck).eval(idx) { + val.fields.get(idx as usize) + } else { + // It's some value in the array so check all of them. + for &(val, _) in val.fields { + if let Some(src) = + self.is_non_freeze_val_borrowed(tcx, typing_env, typeck, use_expr, val)? + { + return Ok(Some(src)); + } + } + return Ok(None); } }, - _ => (), + ExprKind::AddrOf(..) if !self.is_value_freeze(tcx, typing_env, ty, val)? => { + return Ok(Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow))); + }, + // All other expressions use the value. + _ => return Ok(None), + }; + src_expr = use_expr; + if let Some(&(next_val, next_ty)) = next_val { + ty = next_ty; + val = next_val; + } else { + return Ok(None); } } } - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Path(qpath) = &expr.kind { - // Only lint if we use the const item inside a function. - if is_in_const_context(cx) { - return; + /// Checks if the given value (or a local projection of it) is both borrowed and + /// definitely non-`Freeze`. + /// + /// `typing_env` and `init_args` are from the constant's use site. + /// `init_typeck` and `init_expr` are from the constant's definition site. + #[expect(clippy::too_many_arguments, clippy::too_many_lines)] + fn is_non_freeze_init_borrowed( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + mut src_expr: &'tcx Expr<'tcx>, + mut init_typeck: &'tcx TypeckResults<'tcx>, + mut init_args: GenericArgsRef<'tcx>, + mut init_expr: &'tcx Expr<'tcx>, + ) -> Option> { + // Make sure to instantiate all types coming from `init_typeck` with `init_args`. + let mut parents = tcx.hir_parent_iter(src_expr.hir_id); + loop { + // First handle any adjustments since they are cheap to check. + if let [adjust, ..] = typeck.expr_adjustments(src_expr) { + return does_adjust_borrow(adjust) + .filter(|_| !self.is_init_expr_freeze(tcx, typing_env, init_typeck, init_args, init_expr)) + .map(|cause| BorrowSource::new(tcx, src_expr, cause)); } - // Make sure it is a const item. - let Res::Def(DefKind::Const | DefKind::AssocConst, item_def_id) = cx.qpath_res(qpath, expr.hir_id) else { - return; - }; - - // Climb up to resolve any field access and explicit referencing. - let mut cur_expr = expr; - let mut dereferenced_expr = expr; - let mut needs_check_adjustment = true; + // Then read through constants and blocks on the init expression before + // applying the next use expression. loop { - let parent_id = cx.tcx.parent_hir_id(cur_expr.hir_id); - if parent_id == cur_expr.hir_id { - break; + match init_expr.kind { + ExprKind::Block(b, _) + if !b.targeted_by_break + && b.stmts.is_empty() + && let Some(next_init) = b.expr => + { + init_expr = next_init; + }, + ExprKind::Path(ref init_path) => { + let next_init_args = + EarlyBinder::bind(init_typeck.node_args(init_expr.hir_id)).instantiate(tcx, init_args); + match init_typeck.qpath_res(init_path, init_expr.hir_id) { + Res::Def(DefKind::Ctor(..), _) => return None, + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Ok(val) = tcx.const_eval_resolve( + typing_env, + UnevaluatedConst::new(did, next_init_args), + DUMMY_SP, + ) && let Ok(res) = + self.is_non_freeze_val_borrowed(tcx, typing_env, init_typeck, src_expr, val) => + { + return res; + }, + Res::Def(DefKind::Const | DefKind::AssocConst, did) + if let Some((next_typeck, value)) = + get_const_hir_value(tcx, typing_env, did, next_init_args) => + { + init_typeck = next_typeck; + init_args = next_init_args; + init_expr = value; + }, + // There's no more that we can read from the init expression. Switch to a + // type based check. + _ => { + return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, src_expr); + }, + } + }, + _ => break, } - if let Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) { - match &parent_expr.kind { - ExprKind::AddrOf(..) => { - // `&e` => `e` must be referenced. - needs_check_adjustment = false; - }, - ExprKind::Field(..) => { - 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; - } + // Then a type check. Note we only check the type here as the result + // gets cached. + let ty = EarlyBinder::bind(typeck.expr_ty(src_expr)).instantiate(tcx, init_args); + // Normalized as we need to check if this is an array later. + let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); + if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() { + return None; + } - dereferenced_expr = parent_expr; - }, - ExprKind::Index(e, _, _) if ptr::eq(&**e, cur_expr) => { - // `e[i]` => desugared to `*Index::index(&e, i)`, - // meaning `e` must be referenced. - // no need to go further up since a method call is involved now. - needs_check_adjustment = false; - break; - }, - ExprKind::Unary(UnOp::Deref, _) => { - // `*e` => desugared to `*Deref::deref(&e)`, - // meaning `e` must be referenced. - // no need to go further up since a method call is involved now. - needs_check_adjustment = false; - break; + // Finally reduce the init expression using the next use expression. + let Some((_, Node::Expr(use_expr))) = parents.next() else { + return None; + }; + init_expr = match &use_expr.kind { + ExprKind::Field(_, name) => match init_expr.kind { + ExprKind::Struct(_, fields, _) + if let Some(field) = fields.iter().find(|f| f.ident.name == name.name) => + { + field.expr + }, + ExprKind::Tup(fields) + if let Ok(idx) = name.as_str().parse::() + && let Some(field) = fields.get(idx) => + { + field + }, + ExprKind::Call(callee, args) + if let ExprKind::Path(callee_path) = &callee.kind + && matches!( + init_typeck.qpath_res(callee_path, callee.hir_id), + Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(_) + ) + && let Ok(idx) = name.as_str().parse::() + && let Some(arg) = args.get(idx) => + { + arg + }, + // Revert to a type based check as we don't know the field's value. + _ => return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, use_expr), + }, + ExprKind::Index(_, idx, _) if ty.is_array() => match init_expr.kind { + ExprKind::Array(fields) => { + if let Some(Constant::Int(idx)) = ConstEvalCtxt::with_env(tcx, typing_env, typeck).eval(idx) { + // If the index is out of bounds it means the code + // unconditionally panics. In that case there is no borrow. + fields.get(idx as usize)? + } else { + // Unknown index, just run the check for all values. + return fields.iter().find_map(|f| { + self.is_non_freeze_init_borrowed( + tcx, + typing_env, + typeck, + use_expr, + init_typeck, + init_args, + f, + ) + }); + } + }, + // Just assume the index expression doesn't panic here. + ExprKind::Repeat(field, _) => field, + _ => return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, use_expr), + }, + ExprKind::AddrOf(..) + if !self.is_init_expr_freeze(tcx, typing_env, init_typeck, init_args, init_expr) => + { + return Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow)); + }, + // All other expressions use the value. + _ => return None, + }; + src_expr = use_expr; + } + } +} + +impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if let ItemKind::Const(ident, .., body_id) = item.kind + && !ident.is_special() + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { + IsFreeze::No => true, + IsFreeze::Yes => false, + IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { + Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze, + _ => !self.is_init_expr_freeze( + cx.tcx, + cx.typing_env(), + cx.tcx.typeck(item.owner_id), + GenericArgs::identity_for_item(cx.tcx, item.owner_id), + cx.tcx.hir_body(body_id).value, + ), + }, + } + && !item.span.in_external_macro(cx.sess().source_map()) + // Only needed when compiling `std` + && !is_thread_local(cx, item) + { + span_lint_and_then( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + ident.span, + "named constant with interior mutability", + |diag| { + let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else { + return; + }; + if implements_trait(cx, ty, sync_trait, &[]) { + diag.help("did you mean to make this a `static` item"); + } else { + diag.help("did you mean to make this a `thread_local!` item"); + } + }, + ); + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Const(_, body_id_opt) = item.kind + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { + IsFreeze::No => true, + IsFreeze::Maybe if let Some(body_id) = body_id_opt => { + match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { + Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => { + !is_freeze }, - _ => break, + _ => !self.is_init_expr_freeze( + cx.tcx, + cx.typing_env(), + cx.tcx.typeck(item.owner_id), + GenericArgs::identity_for_item(cx.tcx, item.owner_id), + cx.tcx.hir_body(body_id).value, + ), } - cur_expr = parent_expr; - } else { - break; - } + }, + IsFreeze::Yes | IsFreeze::Maybe => false, } + && !item.span.in_external_macro(cx.sess().source_map()) + { + span_lint( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + item.ident.span, + "named constant with interior mutability", + ); + } + } - let ty = if needs_check_adjustment { - let adjustments = cx.typeck_results().expr_adjustments(dereferenced_expr); - if let Some(i) = adjustments - .iter() - .position(|adj| matches!(adj.kind, Adjust::Borrow(_) | Adjust::Deref(_))) - { - if i == 0 { - cx.typeck_results().expr_ty(dereferenced_expr) + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Const(_, body_id) = item.kind + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) { + IsFreeze::Yes => false, + IsFreeze::No => { + // If this is a trait impl, check if the trait definition is the source + // of the cell. + if let Node::Item(parent_item) = cx.tcx.parent_hir_node(item.hir_id()) + && let ItemKind::Impl(impl_block) = parent_item.kind + && let Some(of_trait) = impl_block.of_trait + && let Some(trait_id) = of_trait.trait_def_id() + { + // Replace all instances of `::AssocType` with the + // unit type and check again. If the result is the same then the + // trait definition is the cause. + let ty = (ReplaceAssocFolder { + tcx: cx.tcx, + trait_id, + self_ty: cx.tcx.type_of(parent_item.owner_id).instantiate_identity(), + }) + .fold_ty(cx.tcx.type_of(item.owner_id).instantiate_identity()); + // `ty` may not be normalizable, but that should be fine. + !self.is_ty_freeze(cx.tcx, cx.typing_env(), ty).is_not_freeze() } else { - adjustments[i - 1].target + true } + }, + // Even if this is from a trait, there are values which don't have + // interior mutability. + IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { + Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze, + _ => !self.is_init_expr_freeze( + cx.tcx, + cx.typing_env(), + cx.tcx.typeck(item.owner_id), + GenericArgs::identity_for_item(cx.tcx, item.owner_id), + cx.tcx.hir_body(body_id).value, + ), + }, + } + && !item.span.in_external_macro(cx.sess().source_map()) + { + span_lint( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + item.ident.span, + "named constant with interior mutability", + ); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Path(qpath) = &e.kind + && let typeck = cx.typeck_results() + && let Res::Def(DefKind::Const | DefKind::AssocConst, did) = typeck.qpath_res(qpath, e.hir_id) + // As of `1.80` constant contexts can't borrow any type with interior mutability + && !is_in_const_context(cx) + && !self.is_ty_freeze(cx.tcx, cx.typing_env(), typeck.expr_ty(e)).is_freeze() + && let Some(borrow_src) = { + // The extra block helps formatting a lot. + if let Ok(val) = cx.tcx.const_eval_resolve( + cx.typing_env(), + UnevaluatedConst::new(did, typeck.node_args(e.hir_id)), + DUMMY_SP, + ) && let Ok(src) = self.is_non_freeze_val_borrowed(cx.tcx, cx.typing_env(), typeck, e, val) + { + src + } else if let init_args = typeck.node_args(e.hir_id) + && let Some((init_typeck, init)) = get_const_hir_value(cx.tcx, cx.typing_env(), did, init_args) + { + self.is_non_freeze_init_borrowed(cx.tcx, cx.typing_env(), typeck, e, init_typeck, init_args, init) } else { - // No borrow adjustments means the entire const is moved. - return; + self.is_non_freeze_expr_borrowed(cx.tcx, cx.typing_env(), typeck, e) } - } else { - cx.typeck_results().expr_ty(dereferenced_expr) - }; - - if self.interior_mut.is_interior_mut_ty(cx, ty) - && Self::is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) - { - lint(cx, Source::Expr { expr: expr.span }); } + && !borrow_src.expr.span.in_external_macro(cx.sess().source_map()) + { + span_lint_and_then( + cx, + BORROW_INTERIOR_MUTABLE_CONST, + borrow_src.expr.span, + "borrow of a named constant with interior mutability", + |diag| { + if let Some(note) = borrow_src.cause.note() { + diag.note(note); + } + diag.help("this lint can be silenced by assigning the value to a local variable before borrowing"); + }, + ); + } + } +} + +struct ReplaceAssocFolder<'tcx> { + tcx: TyCtxt<'tcx>, + trait_id: DefId, + self_ty: Ty<'tcx>, +} +impl<'tcx> TypeFolder> for ReplaceAssocFolder<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Alias(AliasTyKind::Projection, ty) = ty.kind() + && ty.trait_def_id(self.tcx) == self.trait_id + && ty.self_ty() == self.self_ty + { + self.tcx.types.unit + } else { + ty.super_fold_with(self) } } } -fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool { +fn is_thread_local(cx: &LateContext<'_>, it: &Item<'_>) -> bool { macro_backtrace(it.span).any(|macro_call| { matches!( cx.tcx.get_diagnostic_name(macro_call.def_id), @@ -507,3 +891,42 @@ fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool { ) }) } + +/// Checks if the adjustment causes a borrow of the original value. Returns +/// `None` if the value is consumed instead of borrowed. +fn does_adjust_borrow(adjust: &Adjustment<'_>) -> Option { + match adjust.kind { + Adjust::Borrow(_) => Some(BorrowCause::AutoBorrow), + // Custom deref calls `::deref(&x)` resulting in a borrow. + Adjust::Deref(Some(_)) => Some(BorrowCause::AutoDeref), + // All other adjustments read the value. + _ => None, + } +} + +/// Attempts to get the value of a constant as a HIR expression. Also gets the +/// `TypeckResults` associated with the constant's body. +fn get_const_hir_value<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + did: DefId, + args: GenericArgsRef<'tcx>, +) -> Option<(&'tcx TypeckResults<'tcx>, &'tcx Expr<'tcx>)> { + let did = did.as_local()?; + let (did, body_id) = match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) { + Node::Item(item) if let ItemKind::Const(.., body_id) = item.kind => (did, body_id), + Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id), + Node::TraitItem(_) + if let Ok(Some(inst)) = Instance::try_resolve(tcx, typing_env, did.into(), args) + && let Some(did) = inst.def_id().as_local() => + { + match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) { + Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id), + Node::TraitItem(item) if let TraitItemKind::Const(.., Some(body_id)) = item.kind => (did, body_id), + _ => return None, + } + }, + _ => return None, + }; + Some((tcx.typeck(did), tcx.hir_body(body_id).value)) +} diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index 852c3885f5689..23a1622f30fff 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -43,12 +43,12 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { match &expr.kind { ExprKind::MethodCall(path, func, [param], _) => { if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def() - && ((path.ident.name.as_str() == "mode" + && ((path.ident.name == sym::mode && matches!( cx.tcx.get_diagnostic_name(adt.did()), Some(sym::FsOpenOptions | sym::DirBuilder) )) - || (path.ident.name.as_str() == "set_mode" + || (path.ident.name == sym::set_mode && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()))) && let ExprKind::Lit(_) = param.kind && param.span.eq_ctxt(expr.span) diff --git a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs index a82365f943181..f66b9519317be 100644 --- a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs +++ b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs @@ -1,13 +1,14 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::paths::{self, PathNS, find_crates, lookup_path_str}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{def_path_def_ids, fn_def_id, is_no_std_crate, path_def_id}; +use clippy_utils::{fn_def_id, is_no_std_crate, path_def_id, sym}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_hir::{self as hir, BodyId, Expr, ExprKind, Item, ItemKind}; +use rustc_hir::{self as hir, BodyId, Expr, ExprKind, HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -37,7 +38,7 @@ declare_clippy_lint! { /// static FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "FOO".to_lowercase()); /// static BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "BAR".to_lowercase()); /// ``` - #[clippy::version = "1.81.0"] + #[clippy::version = "1.86.0"] pub NON_STD_LAZY_STATICS, pedantic, "lazy static that could be replaced by `std::sync::LazyLock`" @@ -62,10 +63,7 @@ static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[ pub struct NonStdLazyStatic { msrv: Msrv, - lazy_static_lazy_static: Vec, - once_cell_crate: Vec, - once_cell_sync_lazy: Vec, - once_cell_sync_lazy_new: Vec, + once_cell_crates: Vec, sugg_map: FxIndexMap>, lazy_type_defs: FxIndexMap, uses_other_once_cell_types: bool, @@ -76,10 +74,7 @@ impl NonStdLazyStatic { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv, - lazy_static_lazy_static: Vec::new(), - once_cell_crate: Vec::new(), - once_cell_sync_lazy: Vec::new(), - once_cell_sync_lazy_new: Vec::new(), + once_cell_crates: Vec::new(), sugg_map: FxIndexMap::default(), lazy_type_defs: FxIndexMap::default(), uses_other_once_cell_types: false, @@ -95,17 +90,15 @@ fn can_use_lazy_cell(cx: &LateContext<'_>, msrv: Msrv) -> bool { impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { fn check_crate(&mut self, cx: &LateContext<'hir>) { - // Fetch def_ids for external paths - self.lazy_static_lazy_static = def_path_def_ids(cx.tcx, &["lazy_static", "lazy_static"]).collect(); - self.once_cell_sync_lazy = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy"]).collect(); - self.once_cell_sync_lazy_new = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy", "new"]).collect(); - // And CrateNums for `once_cell` crate - self.once_cell_crate = self.once_cell_sync_lazy.iter().map(|d| d.krate).collect(); + // Add CrateNums for `once_cell` crate + self.once_cell_crates = find_crates(cx.tcx, sym::once_cell) + .iter() + .map(|def_id| def_id.krate) + .collect(); // Convert hardcoded fn replacement list into a map with def_id for (path, sugg) in FUNCTION_REPLACEMENTS { - let path_vec: Vec<&str> = path.split("::").collect(); - for did in def_path_def_ids(cx.tcx, &path_vec) { + for did in lookup_path_str(cx.tcx, PathNS::Value, path) { self.sugg_map.insert(did, sugg.map(ToOwned::to_owned)); } } @@ -114,14 +107,14 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) { if let ItemKind::Static(..) = item.kind && let Some(macro_call) = clippy_utils::macros::root_macro_call(item.span) - && self.lazy_static_lazy_static.contains(¯o_call.def_id) + && paths::LAZY_STATIC.matches(cx, macro_call.def_id) && can_use_lazy_cell(cx, self.msrv) { span_lint( cx, NON_STD_LAZY_STATICS, macro_call.span, - "this macro has been superceded by `std::sync::LazyLock`", + "this macro has been superseded by `std::sync::LazyLock`", ); return; } @@ -130,7 +123,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { return; } - if let Some(lazy_info) = LazyInfo::from_item(self, cx, item) + if let Some(lazy_info) = LazyInfo::from_item(cx, item) && can_use_lazy_cell(cx, self.msrv) { self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_info); @@ -155,9 +148,9 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { if let rustc_hir::TyKind::Path(qpath) = ty.peel_refs().kind && let Some(ty_def_id) = cx.qpath_res(&qpath, ty.hir_id).opt_def_id() // Is from `once_cell` crate - && self.once_cell_crate.contains(&ty_def_id.krate) + && self.once_cell_crates.contains(&ty_def_id.krate) // And is NOT `once_cell::sync::Lazy` - && !self.once_cell_sync_lazy.contains(&ty_def_id) + && !paths::ONCE_CELL_SYNC_LAZY.matches(cx, ty_def_id) { self.uses_other_once_cell_types = true; } @@ -180,6 +173,8 @@ struct LazyInfo { /// // ^^^^ /// ``` ty_span_no_args: Span, + /// Item on which the lint must be generated. + item_hir_id: HirId, /// `Span` and `DefId` of calls on `Lazy` type. /// i.e.: /// ```ignore @@ -190,12 +185,12 @@ struct LazyInfo { } impl LazyInfo { - fn from_item(state: &NonStdLazyStatic, cx: &LateContext<'_>, item: &Item<'_>) -> Option { + fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Option { // Check if item is a `once_cell:sync::Lazy` static. - if let ItemKind::Static(ty, _, body_id) = item.kind + if let ItemKind::Static(_, ty, _, body_id) = item.kind && let Some(path_def_id) = path_def_id(cx, ty) && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && state.once_cell_sync_lazy.contains(&path_def_id) + && paths::ONCE_CELL_SYNC_LAZY.matches(cx, path_def_id) { let ty_span_no_args = path_span_without_args(path); let body = cx.tcx.hir_body(body_id); @@ -204,7 +199,7 @@ impl LazyInfo { let mut new_fn_calls = FxIndexMap::default(); for_each_expr::<(), ()>(cx, body, |ex| { if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id) - && state.once_cell_sync_lazy_new.contains(&fn_did) + && paths::ONCE_CELL_SYNC_LAZY_NEW.matches(cx, fn_did) { new_fn_calls.insert(call_span, fn_did); } @@ -213,6 +208,7 @@ impl LazyInfo { Some(LazyInfo { ty_span_no_args, + item_hir_id: item.hir_id(), calls_span_and_id: new_fn_calls, }) } else { @@ -236,11 +232,12 @@ impl LazyInfo { } } - span_lint_and_then( + span_lint_hir_and_then( cx, NON_STD_LAZY_STATICS, + self.item_hir_id, self.ty_span_no_args, - "this type has been superceded by `LazyLock` in the standard library", + "this type has been superseded by `LazyLock` in the standard library", |diag| { diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, appl); }, diff --git a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs index 16c4391c0fbea..1b8ab1bdedf8a 100644 --- a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs +++ b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::sym; use rustc_ast::ast::BinOpKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -72,7 +72,7 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit && let ExprKind::Path(qpath) = &func.kind && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && let ExprKind::MethodCall(rcv_path, receiver, [], _) = &arg.kind - && rcv_path.ident.name.as_str() == "get" + && rcv_path.ident.name == sym::get { let fn_name = cx.tcx.item_name(def_id); let target_ty = cx.typeck_results().expr_ty(expr); @@ -82,11 +82,10 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit if let ty::Adt(adt_def, _) = receiver_ty.kind() && adt_def.is_struct() && cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero) + && let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { - if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { - let arg_snippet = get_arg_snippet(cx, arg, rcv_path); - suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability); - } + let arg_snippet = get_arg_snippet(cx, arg, rcv_path); + suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability); } } } diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index 594101427f5a9..a78a342d4fe39 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -335,9 +335,12 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { return; } match &expr.kind { - hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => { + hir::ExprKind::Binary(op, lhs, rhs) => { self.manage_bin_ops(cx, expr, op.node, lhs, rhs); }, + hir::ExprKind::AssignOp(op, lhs, rhs) => { + self.manage_bin_ops(cx, expr, op.node.into(), lhs, rhs); + }, hir::ExprKind::MethodCall(ps, receiver, args, _) => { self.manage_method_call(args, cx, expr, ps, receiver); }, @@ -354,7 +357,7 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { let body_owner_kind = cx.tcx.hir_body_owner_kind(body_owner_def_id); if let hir::BodyOwnerKind::Const { .. } | hir::BodyOwnerKind::Static(_) = body_owner_kind { - let body_span = cx.tcx.hir().span_with_body(body_owner); + let body_span = cx.tcx.hir_span_with_body(body_owner); if let Some(span) = self.const_span && span.contains(body_span) { @@ -366,7 +369,7 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { let body_owner = cx.tcx.hir_body_owner(body.id()); - let body_span = cx.tcx.hir().span(body_owner); + let body_span = cx.tcx.hir_span(body_owner); if let Some(span) = self.const_span && span.contains(body_span) { diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index 5737a91031db2..4be42267b14b3 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>( let rty = cx.typeck_results().expr_ty(rhs); if let Some((_, lang_item)) = binop_traits(op.node) && let Some(trait_id) = cx.tcx.lang_items().get(lang_item) - && let parent_fn = cx.tcx.hir_get_parent_item(e.hir_id).def_id + && let parent_fn = cx.tcx.hir_get_parent_item(e.hir_id) && trait_ref_of_method(cx, parent_fn).is_none_or(|t| t.path.res.def_id() != trait_id) && implements_trait(cx, ty, trait_id, &[rty.into()]) { @@ -112,6 +112,7 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet { } fn consume(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {} + fn use_cloned(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {} fn mutate(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {} fn fake_read(&mut self, _: &PlaceWithHirId<'_>, _: FakeReadCause, _: HirId) {} fn copy(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {} @@ -137,6 +138,7 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet { } fn consume(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {} + fn use_cloned(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {} fn mutate(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {} fn fake_read(&mut self, _: &PlaceWithHirId<'_>, _: FakeReadCause, _: HirId) {} fn copy(&mut self, _: &PlaceWithHirId<'_>, _: HirId) {} diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs index cf6b8992973a7..9b2cfd91b8535 100644 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs +++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs @@ -98,7 +98,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) let arg_snip = snippet(cx, arg_span, ".."); let expr_snip; let eq_impl; - if with_deref.is_implemented() { + if with_deref.is_implemented() && !arg_ty.peel_refs().is_str() { expr_snip = format!("*{arg_snip}"); eq_impl = with_deref; } else { diff --git a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs index e3029f8438e5f..6c9be7c5e90b3 100644 --- a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs +++ b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs @@ -1,11 +1,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::sym; use super::DURATION_SUBSEC; @@ -21,9 +21,9 @@ pub(crate) fn check<'tcx>( && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval(right) { - let suggested_fn = match (method_path.ident.as_str(), divisor) { - ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis", - ("subsec_nanos", 1_000) => "subsec_micros", + let suggested_fn = match (method_path.ident.name, divisor) { + (sym::subsec_micros, 1_000) | (sym::subsec_nanos, 1_000_000) => "subsec_millis", + (sym::subsec_nanos, 1_000) => "subsec_micros", _ => return, }; let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs index 1421893274f5a..d79101a687df4 100644 --- a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs @@ -1,20 +1,18 @@ use clippy_utils::ast_utils::is_useless_with_eq_exprs; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; -use clippy_utils::{eq_expr_value, is_in_test_function}; +use clippy_utils::{eq_expr_value, is_in_test_function, sym}; use rustc_hir::{BinOpKind, Expr}; use rustc_lint::LateContext; use super::EQ_OP; pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| { - let name = cx.tcx.item_name(macro_call.def_id); + if let Some(macro_call) = first_node_macro_backtrace(cx, e).find(|macro_call| { matches!( - name.as_str(), - "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne" + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro) ) - .then(|| (macro_call, name)) }) && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) && eq_expr_value(cx, lhs, rhs) && macro_call.is_local() @@ -24,7 +22,10 @@ pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { cx, EQ_OP, lhs.span.to(rhs.span), - format!("identical args used in this `{macro_name}!` macro call"), + format!( + "identical args used in this `{}!` macro call", + cx.tcx.item_name(macro_call.def_id) + ), ); } } diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs index 01dc6a27c33e3..ded161c8576a1 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_item_name; use clippy_utils::sugg::Sugg; +use clippy_utils::{parent_item_name, sym}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; @@ -34,7 +34,7 @@ pub(crate) fn check<'tcx>( return; } - if let Some(name) = get_item_name(cx, expr) { + if let Some(name) = parent_item_name(cx, expr) { let name = name.as_str(); if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") { return; @@ -106,7 +106,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } if let ExprKind::MethodCall(method_name, self_arg, [], _) = expr.kind - && method_name.ident.name.as_str() == "signum" + && method_name.ident.name == sym::signum // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) { diff --git a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs index 74e0a6333db0f..047a5a0159cb0 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs @@ -50,7 +50,7 @@ pub(crate) fn check<'tcx>( // format the suggestion let suggestion = format!( "{}.abs()", - sugg::make_assoc(AssocOp::Binary(BinOpKind::Sub), &sug_l, &sug_r).maybe_par() + sugg::make_assoc(AssocOp::Binary(BinOpKind::Sub), &sug_l, &sug_r).maybe_paren() ); // spans the lint span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs index 0358232282786..e1fd09549a4b8 100644 --- a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs @@ -103,7 +103,7 @@ enum Parens { /// /// e.g. `-(x + y + 0)` cannot be reduced to `-x + y`, as the behavior changes silently. /// e.g. `1u64 + ((x + y + 0i32) as u64)` cannot be reduced to `1u64 + x + y as u64`, since -/// the the cast expression will not apply to the same expression. +/// the cast expression will not apply to the same expression. /// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced /// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be /// interpreted as a statement. The same behavior happens for `match`, `loop`, diff --git a/src/tools/clippy/clippy_lints/src/operators/integer_division.rs b/src/tools/clippy/clippy_lints/src/operators/integer_division.rs index 76eba7327cff6..7b98afa9b40bd 100644 --- a/src/tools/clippy/clippy_lints/src/operators/integer_division.rs +++ b/src/tools/clippy/clippy_lints/src/operators/integer_division.rs @@ -1,6 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::symbol::sym; use super::INTEGER_DIVISION; @@ -13,7 +15,8 @@ pub(crate) fn check<'tcx>( ) { if op == hir::BinOpKind::Div && cx.typeck_results().expr_ty(left).is_integral() - && cx.typeck_results().expr_ty(right).is_integral() + && let right_ty = cx.typeck_results().expr_ty(right) + && (right_ty.is_integral() || is_type_diagnostic_item(cx, right_ty, sym::NonZero)) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| { diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index 80459945094ed..d32c062cf56a7 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -18,7 +18,6 @@ mod modulo_one; mod needless_bitwise_bool; mod numeric_arithmetic; mod op_ref; -mod ptr_eq; mod self_assignment; mod verbose_bit_mask; @@ -768,35 +767,6 @@ declare_clippy_lint! { "Boolean expressions that use bitwise rather than lazy operators" } -declare_clippy_lint! { - /// ### What it does - /// Use `std::ptr::eq` when applicable - /// - /// ### Why is this bad? - /// `ptr::eq` can be used to compare `&T` references - /// (which coerce to `*const T` implicitly) by their address rather than - /// comparing the values they point to. - /// - /// ### Example - /// ```no_run - /// let a = &[1, 2, 3]; - /// let b = &[1, 2, 3]; - /// - /// assert!(a as *const _ as usize == b as *const _ as usize); - /// ``` - /// Use instead: - /// ```no_run - /// let a = &[1, 2, 3]; - /// let b = &[1, 2, 3]; - /// - /// assert!(std::ptr::eq(a, b)); - /// ``` - #[clippy::version = "1.49.0"] - pub PTR_EQ, - style, - "use `std::ptr::eq` when comparing raw pointers" -} - declare_clippy_lint! { /// ### What it does /// Checks for explicit self-assignments. @@ -902,7 +872,6 @@ impl_lint_pass!(Operators => [ MODULO_ONE, MODULO_ARITHMETIC, NEEDLESS_BITWISE_BOOL, - PTR_EQ, SELF_ASSIGNMENT, MANUAL_MIDPOINT, ]); @@ -921,7 +890,6 @@ impl<'tcx> LateLintPass<'tcx> for Operators { erasing_op::check(cx, e, op.node, lhs, rhs); identity_op::check(cx, e, op.node, lhs, rhs); needless_bitwise_bool::check(cx, e, op.node, lhs, rhs); - ptr_eq::check(cx, e, op.node, lhs, rhs); manual_midpoint::check(cx, e, op.node, lhs, rhs, self.msrv); } self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs); @@ -945,9 +913,10 @@ impl<'tcx> LateLintPass<'tcx> for Operators { ); }, ExprKind::AssignOp(op, lhs, rhs) => { - self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs); - misrefactored_assign_op::check(cx, e, op.node, lhs, rhs); - modulo_arithmetic::check(cx, e, op.node, lhs, rhs, false); + let bin_op = op.node.into(); + self.arithmetic_context.check_binary(cx, e, bin_op, lhs, rhs); + misrefactored_assign_op::check(cx, e, bin_op, lhs, rhs); + modulo_arithmetic::check(cx, e, bin_op, lhs, rhs, false); }, ExprKind::Assign(lhs, rhs, _) => { assign_op_pattern::check(cx, e, lhs, rhs); diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs index fc5565e821edd..2e6a071eb1848 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs @@ -12,15 +12,15 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, right: span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); } - if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() { - if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) { - span_lint( - cx, - MODULO_ONE, - expr.span, - "any number modulo -1 will panic/overflow or result in 0", - ); - } + if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() + && is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) + { + span_lint( + cx, + MODULO_ONE, + expr.span, + "any number modulo -1 will panic/overflow or result in 0", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs index c261fd9bd9cb0..e6be536ca0f4e 100644 --- a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs @@ -73,12 +73,12 @@ impl Context { match cx.tcx.hir_body_owner_kind(body_owner_def_id) { hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const { .. } => { - let body_span = cx.tcx.hir().span_with_body(body_owner); + let body_span = cx.tcx.hir_span_with_body(body_owner); - if let Some(span) = self.const_span { - if span.contains(body_span) { - return; - } + if let Some(span) = self.const_span + && span.contains(body_span) + { + return; } self.const_span = Some(body_span); }, @@ -88,12 +88,12 @@ impl Context { pub fn body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { let body_owner = cx.tcx.hir_body_owner(body.id()); - let body_span = cx.tcx.hir().span_with_body(body_owner); + let body_span = cx.tcx.hir_span_with_body(body_owner); - if let Some(span) = self.const_span { - if span.contains(body_span) { - return; - } + if let Some(span) = self.const_span + && span.contains(body_span) + { + return; } self.const_span = None; } diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs index c3c09946c27d1..0faa7b9e64665 100644 --- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs +++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs @@ -47,12 +47,11 @@ pub(crate) fn check<'tcx>( let rty = cx.typeck_results().expr_ty(r); let lcpy = is_copy(cx, lty); let rcpy = is_copy(cx, rty); - if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) { - if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty)) - || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty)) - { - return; // Don't lint - } + if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) + && ((are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty)) + || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))) + { + return; // Don't lint } // either operator autorefs or both args are copyable if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) { @@ -86,7 +85,7 @@ pub(crate) fn check<'tcx>( left.span, "use the left value directly", lsnip, - Applicability::MaybeIncorrect, // FIXME #2597 + Applicability::MachineApplicable, ); }, ); @@ -105,7 +104,7 @@ pub(crate) fn check<'tcx>( right.span, "use the right value directly", rsnip, - Applicability::MaybeIncorrect, // FIXME #2597 + Applicability::MachineApplicable, ); }, ); @@ -137,7 +136,7 @@ pub(crate) fn check<'tcx>( left.span, "use the left value directly", lsnip, - Applicability::MaybeIncorrect, // FIXME #2597 + Applicability::MachineApplicable, ); }, ); @@ -164,7 +163,7 @@ pub(crate) fn check<'tcx>( right.span, "use the right value directly", rsnip, - Applicability::MaybeIncorrect, // FIXME #2597 + Applicability::MachineApplicable, ); }); } @@ -181,7 +180,7 @@ fn in_impl<'tcx>( ) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> { if let Some(block) = get_enclosing_block(cx, e.hir_id) && let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id()) - && let item = cx.tcx.hir().expect_item(impl_def_id.expect_local()) + && let item = cx.tcx.hir_expect_item(impl_def_id.expect_local()) && let ItemKind::Impl(item) = &item.kind && let Some(of_trait) = &item.of_trait && let Some(seg) = of_trait.path.segments.last() @@ -200,7 +199,7 @@ fn in_impl<'tcx>( fn are_equal(cx: &LateContext<'_>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool { if let ty::Adt(adt_def, _) = middle_ty.kind() && let Some(local_did) = adt_def.did().as_local() - && let item = cx.tcx.hir().expect_item(local_did) + && let item = cx.tcx.hir_expect_item(local_did) && let middle_ty_id = item.owner_id.to_def_id() && let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind && let Res::Def(_, hir_ty_id) = path.res diff --git a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs deleted file mode 100644 index 8118ad59bb71c..0000000000000 --- a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs +++ /dev/null @@ -1,62 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; -use clippy_utils::std_or_core; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::LateContext; - -use super::PTR_EQ; - -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - op: BinOpKind, - left: &'tcx Expr<'_>, - right: &'tcx Expr<'_>, -) { - if BinOpKind::Eq == op { - let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { - (Some(lhs), Some(rhs)) => (lhs, rhs), - _ => (left, right), - }; - - if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left) - && let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right) - && let Some(left_snip) = left_var.span.get_source_text(cx) - && let Some(right_snip) = right_var.span.get_source_text(cx) - { - let Some(top_crate) = std_or_core(cx) else { return }; - span_lint_and_sugg( - cx, - PTR_EQ, - expr.span, - format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), - "try", - format!("{top_crate}::ptr::eq({left_snip}, {right_snip})"), - Applicability::MachineApplicable, - ); - } - } -} - -// If the given expression is a cast to a usize, return the lhs of the cast -// E.g., `foo as *const _ as usize` returns `foo as *const _`. -fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize { - if let ExprKind::Cast(expr, _) = cast_expr.kind { - return Some(expr); - } - } - None -} - -// If the given expression is a cast to a `*const` pointer, return the lhs of the cast -// E.g., `foo as *const _` returns `foo`. -fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if cx.typeck_results().expr_ty(cast_expr).is_raw_ptr() { - if let ExprKind::Cast(expr, _) = cast_expr.kind { - return Some(expr); - } - } - None -} diff --git a/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs index a6aba33e431a4..1477378914120 100644 --- a/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs +++ b/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( e.span, "bit mask could be simplified with a call to `trailing_zeros`", |diag| { - let sugg = Sugg::hir(cx, left1, "...").maybe_par(); + let sugg = Sugg::hir(cx, left1, "...").maybe_paren(); diag.span_suggestion( e.span, "try", diff --git a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs index d16f5f8e112c3..64ad92b1ebb50 100644 --- a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs @@ -37,7 +37,7 @@ impl EarlyLintPass for OptionEnvUnwrap { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind && matches!(seg.ident.name, sym::expect | sym::unwrap) - && is_direct_expn_of(receiver.span, "option_env").is_some() + && is_direct_expn_of(receiver.span, sym::option_env).is_some() { span_lint_and_help( 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 de9f055863cb2..9487cec87efb8 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 @@ -1,16 +1,23 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_copy; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context, - is_res_lang_ctor, peel_blocks, peel_hir_expr_while, + CaptureKind, can_move_expr_to_closure, eager_or_lazy, expr_requires_coercion, higher, is_else_clause, + is_in_const_context, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, }; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::Res; +use rustc_hir::intravisit::{Visitor, walk_expr, walk_path}; use rustc_hir::{ - Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, UnOp, + Arm, BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatExpr, PatExprKind, PatKind, Path, + QPath, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; use rustc_session::declare_lint_pass; use rustc_span::SyntaxContext; @@ -99,7 +106,7 @@ struct OptionOccurrence { fn format_option_in_sugg(cond_sugg: Sugg<'_>, as_ref: bool, as_mut: bool) -> String { format!( "{}{}", - cond_sugg.maybe_par(), + cond_sugg.maybe_paren(), if as_mut { ".as_mut()" } else if as_ref { @@ -110,11 +117,12 @@ fn format_option_in_sugg(cond_sugg: Sugg<'_>, as_ref: bool, as_mut: bool) -> Str ) } +#[expect(clippy::too_many_lines)] fn try_get_option_occurrence<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, pat: &Pat<'tcx>, - expr: &Expr<'_>, + expr: &'tcx Expr<'_>, if_then: &'tcx Expr<'_>, if_else: &'tcx Expr<'_>, ) -> Option { @@ -177,11 +185,40 @@ fn try_get_option_occurrence<'tcx>( .then_some(()) .and_then(|()| none_captures.get(local_id)) }) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, + Some(CaptureKind::Value | CaptureKind::Use | CaptureKind::Ref(Mutability::Mut)) => return None, Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None, Some(CaptureKind::Ref(Mutability::Not)) | None => (), } } + } else if !is_copy(cx, cx.typeck_results().expr_ty(expr)) + // TODO: Cover more match cases + && matches!( + expr.kind, + ExprKind::Field(_, _) | ExprKind::Path(_) | ExprKind::Index(_, _, _) + ) + { + let mut condition_visitor = ConditionVisitor { + cx, + identifiers: FxHashSet::default(), + }; + condition_visitor.visit_expr(cond_expr); + + let mut reference_visitor = ReferenceVisitor { + cx, + identifiers: condition_visitor.identifiers, + }; + if reference_visitor.visit_expr(none_body).is_break() { + return None; + } + } + + let some_body_ty = cx.typeck_results().expr_ty(some_body); + let none_body_ty = cx.typeck_results().expr_ty(none_body); + // Check if coercion is needed for the `None` arm. If so, we cannot suggest because it will + // introduce a type mismatch. A special case is when both arms have the same type, then + // coercion is fine. + if some_body_ty != none_body_ty && expr_requires_coercion(cx, none_body) { + return None; } let mut app = Applicability::Unspecified; @@ -219,6 +256,60 @@ fn try_get_option_occurrence<'tcx>( None } +/// This visitor looks for bindings in the block that mention a local variable. Then gets the +/// identifiers. The list of identifiers will then be used to check if the block mentions the +/// same local. See [`ReferenceVisitor`] for more. +struct ConditionVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + identifiers: FxHashSet, +} + +impl<'tcx> Visitor<'tcx> for ConditionVisitor<'_, 'tcx> { + type NestedFilter = nested_filter::All; + + fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) { + if let Res::Local(local_id) = path.res + && let Node::Pat(pat) = self.cx.tcx.hir_node(local_id) + && let PatKind::Binding(_, local_id, ..) = pat.kind + { + self.identifiers.insert(local_id); + } + walk_path(self, path); + } + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.cx.tcx + } +} + +/// This visitor checks if the block contains references to the local variables that are +/// used in the block. See [`ConditionVisitor`] for more. +struct ReferenceVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + identifiers: FxHashSet, +} + +impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> { + type NestedFilter = nested_filter::All; + type Result = ControlFlow<()>; + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> ControlFlow<()> { + if let ExprKind::Path(ref path) = expr.kind + && let QPath::Resolved(_, path) = path + && let Res::Local(local_id) = path.res + && let Node::Pat(pat) = self.cx.tcx.hir_node(local_id) + && let PatKind::Binding(_, local_id, ..) = pat.kind + && self.identifiers.contains(&local_id) + { + return ControlFlow::Break(()); + } + walk_expr(self, expr) + } + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.cx.tcx + } +} + fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> { if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { let res = cx.qpath_res(qpath, pat.hir_id); diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs index eebc62e2a5a96..ee1d59490ce9f 100644 --- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs +++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{is_inside_always_const_context, return_ty}; @@ -69,10 +69,11 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir return ControlFlow::Continue(Descend::Yes); }; if !is_inside_always_const_context(cx.tcx, e.hir_id) - && matches!( - cx.tcx.item_name(macro_call.def_id).as_str(), - "panic" | "assert" | "assert_eq" | "assert_ne" - ) + && (is_panic(cx, macro_call.def_id) + || matches!( + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro) + )) { panics.push(macro_call.span); ControlFlow::Continue(Descend::No) diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index 39ae9967e0166..8962f36db1e69 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -113,8 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { ); return; } - match cx.tcx.item_name(macro_call.def_id).as_str() { - "todo" => { + match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::todo_macro) => { span_lint( cx, TODO, @@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { "`todo` should not be present in production code", ); }, - "unimplemented" => { + Some(sym::unimplemented_macro) => { span_lint( cx, UNIMPLEMENTED, @@ -130,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { "`unimplemented` should not be present in production code", ); }, - "unreachable" => { + Some(sym::unreachable_macro) => { span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro"); }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs index 267e2067e101a..cda752d003fa0 100644 --- a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs +++ b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs @@ -41,7 +41,7 @@ declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]); impl EarlyLintPass for PartialPubFields { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - let ItemKind::Struct(ref st, _) = item.kind else { + let ItemKind::Struct(_, ref st, _) = item.kind else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs index 55676522419c6..8eaf65e63065e 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs @@ -19,8 +19,8 @@ declare_clippy_lint! { /// struct Foo; /// /// impl PartialEq for Foo { - /// fn eq(&self, other: &Foo) -> bool { true } - /// fn ne(&self, other: &Foo) -> bool { !(self == other) } + /// fn eq(&self, other: &Foo) -> bool { true } + /// fn ne(&self, other: &Foo) -> bool { !(self == other) } /// } /// ``` #[clippy::version = "pre 1.29.0"] @@ -38,7 +38,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { items: impl_items, .. }) = item.kind - && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) && let Some(eq_trait) = cx.tcx.lang_items().eq_trait() && trait_ref.path.res.def_id() == eq_trait { diff --git a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs index 6d4216970cc4d..9b9024c810575 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { let sugg = format!( "{}.{}", sugg::Sugg::hir_with_applicability(cx, peel_ref_operators(cx, scrutinee), "..", &mut applicability) - .maybe_par(), + .maybe_paren(), if is_eq { "is_none()" } else { "is_some()" } ); diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index 49bc560834682..5d30b66def2c8 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -141,7 +141,7 @@ impl PassByRefOrValue { // Gather all the lifetimes found in the output type which may affect whether // `TRIVIALLY_COPY_PASS_BY_REF` should be linted. let mut output_regions = FxHashSet::default(); - for_each_top_level_late_bound_region(fn_sig.skip_binder().output(), |region| -> ControlFlow { + let _ = for_each_top_level_late_bound_region(fn_sig.skip_binder().output(), |region| -> ControlFlow { output_regions.insert(region); ControlFlow::Continue(()) }); @@ -178,19 +178,18 @@ impl PassByRefOrValue { && size <= self.ref_min_size && let hir::TyKind::Ref(_, MutTy { ty: decl_ty, .. }) = input.kind { - if let Some(typeck) = cx.maybe_typeck_results() { + if let Some(typeck) = cx.maybe_typeck_results() // Don't lint if a raw pointer is created. // TODO: Limit the check only to raw pointers to the argument (or part of the argument) // which escape the current function. - if typeck.node_types().items().any(|(_, &ty)| ty.is_raw_ptr()) + && (typeck.node_types().items().any(|(_, &ty)| ty.is_raw_ptr()) || typeck .adjustments() .items() .flat_map(|(_, a)| a) - .any(|a| matches!(a.kind, Adjust::Pointer(PointerCoercion::UnsafeFnPointer))) - { - continue; - } + .any(|a| matches!(a.kind, Adjust::Pointer(PointerCoercion::UnsafeFnPointer)))) + { + continue; } let value_type = if fn_body.and_then(|body| body.params.get(index)).is_some_and(is_self) { "self".into() @@ -280,14 +279,13 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { if header.abi != ExternAbi::Rust { return; } - let attrs = cx.tcx.hir().attrs(hir_id); + let attrs = cx.tcx.hir_attrs(hir_id); for a in attrs { - 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; - } + if let Some(meta_items) = a.meta_item_list() + && (a.has_name(sym::proc_macro_derive) + || (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always))) + { + return; } } }, @@ -296,13 +294,13 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } // Exclude non-inherent impls - if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - if matches!( + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + && matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) - ) { - return; - } + ) + { + return; } self.check_poly_fn(cx, def_id, decl, Some(span)); diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs index b653b459b04c8..35caac855cf61 100644 --- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -173,16 +173,15 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let Some(mut searcher) = self.searcher.take() { - if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind - && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) - && name.ident.as_str() == "push" - { - searcher.err_span = searcher.err_span.to(stmt.span); - searcher.arg = Some(*arg_expr); - searcher.display_err(cx); - } + if let Some(mut searcher) = self.searcher.take() + && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind + && path_to_local_id(self_arg, searcher.local_id) + && name.ident.as_str() == "push" + { + searcher.err_span = searcher.err_span.to(stmt.span); + searcher.arg = Some(*arg_expr); + searcher.display_err(cx); } } 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 8f1a1ee76c6a6..19d9acfc9305a 100644 --- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs @@ -177,17 +177,16 @@ fn find_first_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<(Span, Mut PatKind::Or([p, ..]) => p, _ => p, }; - if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) { - if let [first, ..] = **adjustments { - if let ty::Ref(.., mutability) = *first.kind() { - let level = if p.hir_id == pat.hir_id { - Level::Top - } else { - Level::Lower - }; - result = Some((p.span, mutability, level)); - } - } + if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) + && let [first, ..] = **adjustments + && let ty::Ref(.., mutability) = *first.source.kind() + { + let level = if p.hir_id == pat.hir_id { + Level::Top + } else { + Level::Lower + }; + result = Some((p.span, mutability, level)); } result.is_none() }); diff --git a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs index dc142b6e15771..da56a785007c4 100644 --- a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs +++ b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -33,7 +33,7 @@ impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind && let ExprKind::Lit(lit) = &arg.kind && LitKind::Bool(false) == lit.node - && path.ident.name.as_str() == "set_readonly" + && path.ident.name == sym::set_readonly && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) { span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index dae0709a5404a..9149406642d67 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -3,7 +3,7 @@ use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core}; -use hir::LifetimeName; +use hir::LifetimeKind; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::hir_id::{HirId, HirIdMap}; @@ -127,28 +127,34 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// This lint checks for invalid usages of `ptr::null`. + /// Use `std::ptr::eq` when applicable /// /// ### Why is this bad? - /// This causes undefined behavior. + /// `ptr::eq` can be used to compare `&T` references + /// (which coerce to `*const T` implicitly) by their address rather than + /// comparing the values they point to. /// /// ### Example - /// ```ignore - /// // Undefined behavior - /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); } - /// ``` + /// ```no_run + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; /// + /// assert!(a as *const _ as usize == b as *const _ as usize); + /// ``` /// Use instead: - /// ```ignore - /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); } + /// ```no_run + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(std::ptr::eq(a, b)); /// ``` - #[clippy::version = "1.53.0"] - pub INVALID_NULL_PTR_USAGE, - correctness, - "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead" + #[clippy::version = "1.49.0"] + pub PTR_EQ, + style, + "use `std::ptr::eq` when comparing raw pointers" } -declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]); +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, PTR_EQ]); impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { @@ -253,10 +259,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { if let ExprKind::Binary(op, l, r) = expr.kind && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) { - let non_null_path_snippet = match (is_null_path(cx, l), is_null_path(cx, r)) { - (true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_par(), - (false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_par(), - _ => return, + let non_null_path_snippet = match ( + is_lint_allowed(cx, CMP_NULL, expr.hir_id), + is_null_path(cx, l), + is_null_path(cx, r), + ) { + (false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_paren(), + (false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_paren(), + _ => return check_ptr_eq(cx, expr, op.node, l, r), }; span_lint_and_sugg( @@ -268,54 +278,6 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { format!("{non_null_path_snippet}.is_null()"), Applicability::MachineApplicable, ); - } else { - check_invalid_ptr_usage(cx, expr); - } - } -} - -fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Call(fun, args) = expr.kind - && let ExprKind::Path(ref qpath) = fun.kind - && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() - && let Some(name) = cx.tcx.get_diagnostic_name(fun_def_id) - { - // TODO: `ptr_slice_from_raw_parts` and its mutable variant should probably still be linted - // conditionally based on how the return value is used, but not universally like the other - // functions since there are valid uses for null slice pointers. - // - // See: https://github.com/rust-lang/rust-clippy/pull/13452/files#r1773772034 - - // `arg` positions where null would cause U.B. - let arg_indices: &[_] = match name { - sym::ptr_read - | sym::ptr_read_unaligned - | sym::ptr_read_volatile - | sym::ptr_replace - | sym::ptr_write - | sym::ptr_write_bytes - | sym::ptr_write_unaligned - | sym::ptr_write_volatile - | sym::slice_from_raw_parts - | sym::slice_from_raw_parts_mut => &[0], - sym::ptr_copy | sym::ptr_copy_nonoverlapping | sym::ptr_swap | sym::ptr_swap_nonoverlapping => &[0, 1], - _ => return, - }; - - for &arg_idx in arg_indices { - if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) - && let Some(std_or_core) = std_or_core(cx) - { - span_lint_and_sugg( - cx, - INVALID_NULL_PTR_USAGE, - arg.span, - "pointer must be non-null", - "change this to", - format!("{std_or_core}::ptr::NonNull::dangling().as_ptr()"), - Applicability::MachineApplicable, - ); - } } } } @@ -470,7 +432,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( } None }) { - if let LifetimeName::Param(param_def_id) = lifetime.res + if let LifetimeKind::Param(param_def_id) = lifetime.kind && !lifetime.is_anonymous() && fn_sig .output() @@ -536,29 +498,33 @@ fn check_fn_args<'cx, 'tcx: 'cx>( } fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&Body<'tcx>>) { - if let FnRetTy::Return(ty) = sig.decl.output - && let Some((out, Mutability::Mut, _)) = get_ref_lm(ty) - { + let FnRetTy::Return(ty) = sig.decl.output else { return }; + for (out, mutability, out_span) in get_lifetimes(ty) { + if mutability != Some(Mutability::Mut) { + continue; + } let out_region = cx.tcx.named_bound_var(out.hir_id); - let args: Option> = sig + // `None` if one of the types contains `&'a mut T` or `T<'a>`. + // Else, contains all the locations of `&'a T` types. + let args_immut_refs: Option> = sig .decl .inputs .iter() - .filter_map(get_ref_lm) + .flat_map(get_lifetimes) .filter(|&(lt, _, _)| cx.tcx.named_bound_var(lt.hir_id) == out_region) - .map(|(_, mutability, span)| (mutability == Mutability::Not).then_some(span)) + .map(|(_, mutability, span)| (mutability == Some(Mutability::Not)).then_some(span)) .collect(); - if let Some(args) = args - && !args.is_empty() + if let Some(args_immut_refs) = args_immut_refs + && !args_immut_refs.is_empty() && body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value)) { span_lint_and_then( cx, MUT_FROM_REF, - ty.span, + out_span, "mutable borrow from immutable input(s)", |diag| { - let ms = MultiSpan::from_spans(args); + let ms = MultiSpan::from_spans(args_immut_refs); diag.span_note(ms, "immutable borrow here"); }, ); @@ -724,14 +690,38 @@ fn matches_preds<'tcx>( }) } -fn get_ref_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { - if let TyKind::Ref(lt, ref m) = ty.kind { - Some((lt, m.mutbl, ty.span)) - } else { - None +struct LifetimeVisitor<'tcx> { + result: Vec<(&'tcx Lifetime, Option, Span)>, +} + +impl<'tcx> Visitor<'tcx> for LifetimeVisitor<'tcx> { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { + if let TyKind::Ref(lt, ref m) = ty.kind { + self.result.push((lt, Some(m.mutbl), ty.span)); + } + hir::intravisit::walk_ty(self, ty); + } + + fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) { + if let GenericArg::Lifetime(lt) = generic_arg { + self.result.push((lt, None, generic_arg.span())); + } + hir::intravisit::walk_generic_arg(self, generic_arg); } } +/// Visit `ty` and collect the all the lifetimes appearing in it, implicit or not. +/// +/// The second field of the vector's elements indicate if the lifetime is attached to a +/// shared reference, a mutable reference, or neither. +fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option, Span)> { + use hir::intravisit::VisitorExt as _; + + let mut visitor = LifetimeVisitor { result: Vec::new() }; + visitor.visit_ty_unambig(ty); + visitor.result +} + fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let ExprKind::Call(pathexp, []) = expr.kind { path_def_id(cx, pathexp) @@ -740,3 +730,80 @@ fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } } + +fn check_ptr_eq<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op: BinOpKind, + left: &'tcx Expr<'_>, + right: &'tcx Expr<'_>, +) { + if expr.span.from_expansion() { + return; + } + + // Remove one level of usize conversion if any + let (left, right, usize_peeled) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { + (Some(lhs), Some(rhs)) => (lhs, rhs, true), + _ => (left, right, false), + }; + + // This lint concerns raw pointers + let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right)); + if !left_ty.is_raw_ptr() || !right_ty.is_raw_ptr() { + return; + } + + let ((left_var, left_casts_peeled), (right_var, right_casts_peeled)) = + (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty)); + + if !(usize_peeled || left_casts_peeled || right_casts_peeled) { + return; + } + + let mut app = Applicability::MachineApplicable; + let left_snip = Sugg::hir_with_context(cx, left_var, expr.span.ctxt(), "_", &mut app); + let right_snip = Sugg::hir_with_context(cx, right_var, expr.span.ctxt(), "_", &mut app); + { + let Some(top_crate) = std_or_core(cx) else { return }; + let invert = if op == BinOpKind::Eq { "" } else { "!" }; + span_lint_and_sugg( + cx, + PTR_EQ, + expr.span, + format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), + "try", + format!("{invert}{top_crate}::ptr::eq({left_snip}, {right_snip})"), + app, + ); + } +} + +// If the given expression is a cast to a usize, return the lhs of the cast +// E.g., `foo as *const _ as usize` returns `foo as *const _`. +fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if !cast_expr.span.from_expansion() + && cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize + && let ExprKind::Cast(expr, _) = cast_expr.kind + { + Some(expr) + } else { + None + } +} + +// Peel raw casts if the remaining expression can be coerced to it, and whether casts have been +// peeled or not. +fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> (&'tcx Expr<'tcx>, bool) { + if !expr.span.from_expansion() + && let ExprKind::Cast(inner, _) = expr.kind + && let ty::RawPtr(target_ty, _) = expr_ty.kind() + && let inner_ty = cx.typeck_results().expr_ty(inner) + && let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind() + && target_ty == inner_target_ty + { + (peel_raw_casts(cx, inner, inner_ty).0, true) + } else { + (expr, false) + } +} diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs index 68ae575c9063f..d8d813f9846d5 100644 --- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs +++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; use std::fmt; declare_clippy_lint! { @@ -77,10 +77,10 @@ impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { // If the given expression is a cast from a usize, return the lhs of the cast fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind { - if is_expr_ty_usize(cx, cast_lhs_expr) { - return Some(cast_lhs_expr); - } + if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind + && is_expr_ty_usize(cx, cast_lhs_expr) + { + return Some(cast_lhs_expr); } None } @@ -91,14 +91,14 @@ fn expr_as_ptr_offset_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind { - if is_expr_ty_raw_ptr(cx, arg_0) { - if path_segment.ident.name == sym::offset { - return Some((arg_0, arg_1, Method::Offset)); - } - if path_segment.ident.name.as_str() == "wrapping_offset" { - return Some((arg_0, arg_1, Method::WrappingOffset)); - } + if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind + && is_expr_ty_raw_ptr(cx, arg_0) + { + if path_segment.ident.name == sym::offset { + return Some((arg_0, arg_1, Method::Offset)); + } + if path_segment.ident.name == sym::wrapping_offset { + return Some((arg_0, arg_1, Method::WrappingOffset)); } } None diff --git a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs index db03657c9af43..e4a9bf7a84812 100644 --- a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs +++ b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs @@ -58,7 +58,7 @@ impl PubUnderscoreFields { impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { // This lint only pertains to structs. - let ItemKind::Struct(variant_data, _) = &item.kind else { + let ItemKind::Struct(_, variant_data, _) = &item.kind else { return; }; @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { // Only pertains to fields that start with an underscore, and are public. if field.ident.as_str().starts_with('_') && is_visible(field) // We ignore fields that have `#[doc(hidden)]`. - && !is_doc_hidden(cx.tcx.hir().attrs(field.hir_id)) + && !is_doc_hidden(cx.tcx.hir_attrs(field.hir_id)) // We ignore fields that are `PhantomData`. && !is_path_lang_item(cx, field.ty, LangItem::PhantomData) { diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 4f5f3eb6c15a0..c02e5e0621c9f 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -3,13 +3,14 @@ use crate::question_mark_used::QUESTION_MARK_USED; use clippy_config::Conf; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::msrvs::Msrv; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, - span_contains_cfg, span_contains_comment, + span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -21,15 +22,14 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::sym; use rustc_span::symbol::Symbol; declare_clippy_lint! { /// ### What it does - /// Checks for expressions that could be replaced by the question mark operator. + /// Checks for expressions that could be replaced by the `?` operator. /// /// ### Why is this bad? - /// Question mark usage is more idiomatic. + /// Using the `?` operator is shorter and more idiomatic. /// /// ### Example /// ```ignore @@ -46,7 +46,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub QUESTION_MARK, style, - "checks for expressions that could be replaced by the question mark operator" + "checks for expressions that could be replaced by the `?` operator" } pub struct QuestionMark { @@ -144,9 +144,48 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { && !span_contains_comment(cx.tcx.sess.source_map(), els.span) { let mut applicability = Applicability::MaybeIncorrect; - let init_expr_str = snippet_with_applicability(cx, init_expr.span, "..", &mut applicability); - let receiver_str = snippet_with_applicability(cx, inner_pat.span, "..", &mut applicability); - let sugg = format!("let {receiver_str} = {init_expr_str}?;",); + let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren(); + // Take care when binding is `ref` + let sugg = if let PatKind::Binding( + BindingMode(ByRef::Yes(ref_mutability), binding_mutability), + _hir_id, + ident, + subpattern, + ) = inner_pat.kind + { + let (from_method, replace_to) = match ref_mutability { + Mutability::Mut => (".as_mut()", "&mut "), + Mutability::Not => (".as_ref()", "&"), + }; + + let mutability_str = match binding_mutability { + Mutability::Mut => "mut ", + Mutability::Not => "", + }; + + // Handle subpattern (@ subpattern) + let maybe_subpattern = match subpattern { + Some(Pat { + kind: PatKind::Binding(BindingMode(ByRef::Yes(_), _), _, subident, None), + .. + }) => { + // avoid `&ref` + // note that, because you can't have aliased, mutable references, we don't have to worry about + // the outer and inner mutability being different + format!(" @ {subident}") + }, + Some(subpattern) => { + let substr = snippet_with_applicability(cx, subpattern.span, "..", &mut applicability); + format!(" @ {replace_to}{substr}") + }, + None => String::new(), + }; + + format!("let {mutability_str}{ident}{maybe_subpattern} = {init_expr_str}{from_method}?;") + } else { + let receiver_str = snippet_with_applicability(cx, inner_pat.span, "..", &mut applicability); + format!("let {receiver_str} = {init_expr_str}?;") + }; span_lint_and_sugg( cx, QUESTION_MARK, @@ -167,8 +206,8 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ is_type_diagnostic_item(cx, caller_ty, smbl) && expr_return_none_or_err(smbl, cx, if_then, caller, None) && match smbl { - sym::Option => call_sym.as_str() == "is_none", - sym::Result => call_sym.as_str() == "is_err", + sym::Option => call_sym == sym::is_none, + sym::Result => call_sym == sym::is_err, _ => false, } }, @@ -230,7 +269,7 @@ fn expr_return_none_or_err( /// /// ```ignore /// if option.is_none() { -/// return None; +/// return None; /// } /// ``` /// @@ -240,7 +279,7 @@ fn expr_return_none_or_err( /// } /// ``` /// -/// If it matches, it will suggest to use the question mark operator instead +/// If it matches, it will suggest to use the `?` operator instead fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) && !is_else_clause(cx.tcx, expr) @@ -485,7 +524,8 @@ fn is_inferred_ret_closure(expr: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) { + if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) || !self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) + { return; } @@ -501,7 +541,10 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { return; } - if !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) + if !self.inside_try_block() + && !is_in_const_context(cx) + && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) + && self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) { check_is_none_or_err_and_early_return(cx, expr); check_if_let_some_or_err_and_early_return(cx, expr); diff --git a/src/tools/clippy/clippy_lints/src/question_mark_used.rs b/src/tools/clippy/clippy_lints/src/question_mark_used.rs index 0a974bf9d2f71..96ea485d76936 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark_used.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark_used.rs @@ -7,10 +7,10 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for expressions that use the question mark operator and rejects them. + /// Checks for expressions that use the `?` operator and rejects them. /// /// ### Why restrict this? - /// Sometimes code wants to avoid the question mark operator because for instance a local + /// Sometimes code wants to avoid the `?` operator because for instance a local /// block requires a macro to re-throw errors to attach additional information to the /// error. /// @@ -27,7 +27,7 @@ declare_clippy_lint! { #[clippy::version = "1.69.0"] pub QUESTION_MARK_USED, restriction, - "complains if the question mark operator is used" + "checks if the `?` operator is used" } declare_lint_pass!(QuestionMarkUsed => [QUESTION_MARK_USED]); @@ -40,15 +40,9 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed { } #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - QUESTION_MARK_USED, - expr.span, - "question mark operator was used", - |diag| { - diag.help("consider using a custom macro or match expression"); - }, - ); + span_lint_and_then(cx, QUESTION_MARK_USED, expr.span, "the `?` operator was used", |diag| { + diag.help("consider using a custom macro or match expression"); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index cc423eca74fbe..d292ed86ea4c6 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -179,10 +179,10 @@ impl_lint_pass!(Ranges => [ impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, l, r) = expr.kind { - if self.msrv.meets(cx, msrvs::RANGE_CONTAINS) { - check_possible_range_contains(cx, op.node, l, r, expr, expr.span); - } + if let ExprKind::Binary(ref op, l, r) = expr.kind + && self.msrv.meets(cx, msrvs::RANGE_CONTAINS) + { + check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } check_exclusive_range_plus_one(cx, expr); @@ -327,18 +327,18 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> inc: inclusive, }); } - } else if let Some(id) = path_to_local(r) { - if let Some(c) = ConstEvalCtxt::new(cx).eval(l) { - return Some(RangeBounds { - val: c, - expr: l, - id, - name_span: r.span, - val_span: l.span, - ord: ordering.reverse(), - inc: inclusive, - }); - } + } else if let Some(id) = path_to_local(r) + && let Some(c) = ConstEvalCtxt::new(cx).eval(l) + { + return Some(RangeBounds { + val: c, + expr: l, + id, + name_span: r.span, + val_span: l.span, + ord: ordering.reverse(), + inc: inclusive, + }); } } None @@ -361,8 +361,8 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { span, "an inclusive range would be more readable", |diag| { - let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); - let end = Sugg::hir(cx, y, "y").maybe_par(); + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_paren().to_string()); + let end = Sugg::hir(cx, y, "y").maybe_paren(); match span.with_source_text(cx, |src| src.starts_with('(') && src.ends_with(')')) { Some(true) => { diag.span_suggestion(span, "use", format!("({start}..={end})"), Applicability::MaybeIncorrect); @@ -398,8 +398,8 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "an exclusive range would be more readable", |diag| { - let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); - let end = Sugg::hir(cx, y, "y").maybe_par(); + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_paren().to_string()); + let end = Sugg::hir(cx, y, "y").maybe_paren(); diag.span_suggestion( expr.span, "use", diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index c6e6e782f9d45..6a79cae32a596 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs @@ -138,7 +138,7 @@ impl RawStrings { ); }, ); - if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) { + if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS).level, rustc_lint::Allow) { return; } } diff --git a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs index 6bb7650a7e1cf..689a2ac4c6aeb 100644 --- a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; -use clippy_utils::last_path_segment; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::{indent_of, snippet}; +use clippy_utils::{last_path_segment, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -135,7 +135,7 @@ fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, Span)> { if let ty::Adt(adt, _) = *cx.typeck_results().expr_ty(expr).kind() && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::RcWeak | sym::ArcWeak)) { - return Some((Symbol::intern("Weak"), func.span)); + return Some((sym::Weak, func.span)); } } diff --git a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs index 6bd68dd4109d4..49b522994fbf7 100644 --- a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs +++ b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs @@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { }, ), VecInitKind::WithExprCapacity(hir_id) => { - let e = cx.tcx.hir().expect_expr(hir_id); + let e = cx.tcx.hir_expect_expr(hir_id); span_lint_hir_and_then( cx, READ_ZERO_BYTE_VEC, diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs index bc5e8fd2c2584..d2442ad0f373a 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs @@ -1,14 +1,9 @@ -use std::ops::ControlFlow; - use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::peel_blocks; use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::ty::implements_trait; -use clippy_utils::visitors::for_each_expr_without_closures; +use clippy_utils::{desugar_await, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{ - Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, MatchSource, -}; +use rustc_hir::{Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::UpvarCapture; use rustc_session::declare_lint_pass; @@ -23,7 +18,7 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// let f = async { - /// 1 + 2 + /// 1 + 2 /// }; /// let fut = async { /// f.await @@ -32,7 +27,7 @@ declare_clippy_lint! { /// Use instead: /// ```no_run /// let f = async { - /// 1 + 2 + /// 1 + 2 /// }; /// let fut = f; /// ``` @@ -99,20 +94,3 @@ fn desugar_async_block<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Op None } } - -/// If `expr` is a desugared `.await`, return the original expression if it does not come from a -/// macro expansion. -fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind - && let ExprKind::Call(_, [into_future_arg]) = match_value.kind - && let ctxt = expr.span.ctxt() - && for_each_expr_without_closures(into_future_arg, |e| { - walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(())) - }) - .is_none() - { - Some(into_future_arg) - } else { - None - } -} diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index cfa622aea582f..e57b8cc2d84e3 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -109,10 +109,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } - if let ty::Adt(def, _) = arg_ty.kind() { - if def.is_manually_drop() { - continue; - } + if let ty::Adt(def, _) = arg_ty.kind() + && def.is_manually_drop() + { + continue; } // `{ arg = &cloned; clone(move arg); }` or `{ arg = &cloned; to_path_buf(arg); }` @@ -182,20 +182,25 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let clone_usage = if local == ret_local { CloneUsage { - cloned_used: false, + cloned_use_loc: None.into(), cloned_consume_or_mutate_loc: None, clone_consumed_or_mutated: true, } } else { let clone_usage = visit_clone_usage(local, ret_local, mir, bb); - if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated { + if clone_usage.cloned_use_loc.maybe_used() && clone_usage.clone_consumed_or_mutated { // cloned value is used, and the clone is modified or moved continue; - } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc { + } else if let MirLocalUsage::Used(loc) = clone_usage.cloned_use_loc + && possible_borrower.local_is_alive_at(ret_local, loc) + { + // cloned value is used, and the clone is alive. + continue; + } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc // cloned value is mutated, and the clone is alive. - if possible_borrower.local_is_alive_at(ret_local, loc) { - continue; - } + && possible_borrower.local_is_alive_at(ret_local, loc) + { + continue; } clone_usage }; @@ -216,19 +221,18 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let call_snip = &snip[dot + 1..]; // Machine applicable when `call_snip` looks like `foobar()` - if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { - if call_snip + if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) + && call_snip .as_bytes() .iter() .all(|b| b.is_ascii_alphabetic() || *b == b'_') - { - app = Applicability::MachineApplicable; - } + { + app = Applicability::MachineApplicable; } span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { diag.span_suggestion(sugg_span, "remove this", "", app); - if clone_usage.cloned_used { + if clone_usage.cloned_use_loc.maybe_used() { diag.span_note(span, "cloned value is neither consumed nor mutated"); } else { diag.span_note( @@ -329,10 +333,33 @@ fn base_local_and_movability<'tcx>( (place.local, deref || field || slice) } -#[derive(Default)] +#[derive(Debug, Default)] +enum MirLocalUsage { + /// The local maybe used, but we are not sure how. + Unknown, + /// The local is not used. + #[default] + Unused, + /// The local is used at a specific location. + Used(mir::Location), +} + +impl MirLocalUsage { + fn maybe_used(&self) -> bool { + matches!(self, MirLocalUsage::Unknown | MirLocalUsage::Used(_)) + } +} + +impl From> for MirLocalUsage { + fn from(loc: Option) -> Self { + loc.map_or(MirLocalUsage::Unused, MirLocalUsage::Used) + } +} + +#[derive(Debug, Default)] struct CloneUsage { - /// Whether the cloned value is used after the clone. - cloned_used: bool, + /// The first location where the cloned value is used, if any. + cloned_use_loc: MirLocalUsage, /// The first location where the cloned value is consumed or mutated, if any. cloned_consume_or_mutate_loc: Option, /// Whether the clone value is mutated. @@ -360,7 +387,7 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, .map(|mut vec| (vec.remove(0), vec.remove(0))) { CloneUsage { - cloned_used: !cloned_use_locs.is_empty(), + cloned_use_loc: cloned_use_locs.first().copied().into(), cloned_consume_or_mutate_loc: cloned_consume_or_mutate_locs.first().copied(), // Consider non-temporary clones consumed. // TODO: Actually check for mutation of non-temporaries. @@ -369,7 +396,7 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, } } else { CloneUsage { - cloned_used: true, + cloned_use_loc: MirLocalUsage::Unknown, cloned_consume_or_mutate_loc: None, clone_consumed_or_mutated: true, } diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index 1498a49a7a4a9..84597269a58fa 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { // avoid clippy::double_parens if !is_in_fn_call_arg { - hint = hint.maybe_par(); + hint = hint.maybe_paren(); } diag.span_suggestion(full_expr.span, "try doing something like", hint, applicability); diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index defb6684cffbe..8f33a47e29089 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// let a = a; /// /// fn foo(b: i32) { - /// let b = b; + /// let b = b; /// } /// ``` /// Use instead: diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index 6a17b83b3d01b..7b381fac5f118 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -52,7 +52,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { && is_not_macro_export(item) && !item.span.in_external_macro(cx.sess().source_map()) { - let span = item.span.with_hi(item.ident.span.hi()); + let span = item + .kind + .ident() + .map_or(item.span, |ident| item.span.with_hi(ident.span.hi())); let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index 7038b19d27596..1117dea703c2a 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -135,25 +135,24 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { }; diag.span_suggestion(expr.span, help_msg, sugg, app); }); - } else if let Some(target_id) = cx.tcx.lang_items().deref_target() { - if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( + } else if let Some(target_id) = cx.tcx.lang_items().deref_target() + && let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( cx.typing_env(), Ty::new_projection_from_args(cx.tcx, target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])), - ) { - if deref_ty == expr_ty { - let (lint, msg) = DEREF_BY_SLICING_LINT; - span_lint_and_then(cx, lint, expr.span, msg, |diag| { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; - let sugg = if needs_parens_for_prefix { - format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) - } else { - format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) - }; - diag.span_suggestion(expr.span, "dereference the original value instead", sugg, app); - }); - } - } + ) + && deref_ty == expr_ty + { + let (lint, msg) = DEREF_BY_SLICING_LINT; + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; + let sugg = if needs_parens_for_prefix { + format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) + } else { + format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) + }; + diag.span_suggestion(expr.span, "dereference the original value instead", sugg, app); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs b/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs new file mode 100644 index 0000000000000..84276e3216573 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs @@ -0,0 +1,161 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_test_function; +use clippy_utils::visitors::for_each_expr; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{self as hir, Body, ExprKind, FnDecl}; +use rustc_lexer::is_ident; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::def_id::LocalDefId; +use rustc_span::{Span, Symbol, edition}; +use std::borrow::Cow; +use std::ops::ControlFlow; + +declare_clippy_lint! { + /// ### What it does + /// Checks for test functions (functions annotated with `#[test]`) that are prefixed + /// with `test_` which is redundant. + /// + /// ### Why is this bad? + /// This is redundant because test functions are already annotated with `#[test]`. + /// Moreover, it clutters the output of `cargo test` since test functions are expanded as + /// `module::tests::test_use_case` in the output. Without the redundant prefix, the output + /// becomes `module::tests::use_case`, which is more readable. + /// + /// ### Example + /// ```no_run + /// #[cfg(test)] + /// mod tests { + /// use super::*; + /// + /// #[test] + /// fn test_use_case() { + /// // test code + /// } + /// } + /// ``` + /// Use instead: + /// ```no_run + /// #[cfg(test)] + /// mod tests { + /// use super::*; + /// + /// #[test] + /// fn use_case() { + /// // test code + /// } + /// } + /// ``` + #[clippy::version = "1.88.0"] + pub REDUNDANT_TEST_PREFIX, + restriction, + "redundant `test_` prefix in test function name" +} + +declare_lint_pass!(RedundantTestPrefix => [REDUNDANT_TEST_PREFIX]); + +impl<'tcx> LateLintPass<'tcx> for RedundantTestPrefix { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'_>, + _decl: &FnDecl<'_>, + body: &'tcx Body<'_>, + _span: Span, + fn_def_id: LocalDefId, + ) { + // Ignore methods and closures. + let FnKind::ItemFn(ref ident, ..) = kind else { + return; + }; + + // Skip the lint if the function is within a macro expansion. + if ident.span.from_expansion() { + return; + } + + // Skip if the function name does not start with `test_`. + if !ident.as_str().starts_with("test_") { + return; + } + + // If the function is not a test function, skip the lint. + if !is_test_function(cx.tcx, fn_def_id) { + return; + } + + span_lint_and_then( + cx, + REDUNDANT_TEST_PREFIX, + ident.span, + "redundant `test_` prefix in test function name", + |diag| { + let non_prefixed = Symbol::intern(ident.as_str().trim_start_matches("test_")); + if is_invalid_ident(non_prefixed) { + // If the prefix-trimmed name is not a valid function name, do not provide an + // automatic fix, just suggest renaming the function. + diag.help( + "consider function renaming (just removing `test_` prefix will produce invalid function name)", + ); + } else { + let (sugg, msg): (Cow<'_, str>, _) = if name_conflicts(cx, body, non_prefixed) { + // If `non_prefixed` conflicts with another function in the same module/scope, + // do not provide an automatic fix, but still emit a fix suggestion. + ( + format!("{non_prefixed}_works").into(), + "consider function renaming (just removing `test_` prefix will cause a name conflict)", + ) + } else { + // If `non_prefixed` is a valid identifier and does not conflict with another function, + // so we can suggest an auto-fix. + (non_prefixed.as_str().into(), "consider removing the `test_` prefix") + }; + diag.span_suggestion(ident.span, msg, sugg, Applicability::MaybeIncorrect); + } + }, + ); + } +} + +/// Checks whether removal of the `_test` prefix from the function name will cause a name conflict. +/// +/// There should be no other function with the same name in the same module/scope. Also, there +/// should not be any function call with the same name within the body of the function, to avoid +/// recursion. +fn name_conflicts<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, fn_name: Symbol) -> bool { + let tcx = cx.tcx; + let id = body.id().hir_id; + + // Iterate over items in the same module/scope + let (module, _module_span, _module_hir) = tcx.hir_get_module(tcx.parent_module(id)); + if module + .item_ids + .iter() + .any(|item| matches!(tcx.hir_item(*item).kind, hir::ItemKind::Fn { ident, .. } if ident.name == fn_name)) + { + // Name conflict found + return true; + } + + // Also check that within the body of the function there is also no function call + // with the same name (since it will result in recursion) + for_each_expr(cx, body, |expr| { + if let ExprKind::Path(qpath) = &expr.kind + && let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id() + && let Some(name) = tcx.opt_item_name(def_id) + && name == fn_name + { + // Function call with the same name found + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} + +fn is_invalid_ident(ident: Symbol) -> bool { + // The identifier is either a reserved keyword, or starts with an invalid sequence. + ident.is_reserved(|| edition::LATEST_STABLE_EDITION) || !is_ident(ident.as_str()) +} diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 9443dca154e33..89d945161f629 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -2,8 +2,9 @@ use std::fmt::Display; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::paths::PathLookup; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths}; +use clippy_utils::{path_def_id, paths}; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; @@ -76,7 +77,7 @@ declare_clippy_lint! { /// This is documented as an antipattern [on the regex documentation](https://docs.rs/regex/latest/regex/#avoid-re-compiling-regexes-especially-in-a-loop) /// /// ### Example - /// ```no_run + /// ```rust,ignore /// # let haystacks = [""]; /// # const MY_REGEX: &str = "a.b"; /// for haystack in haystacks { @@ -87,7 +88,7 @@ declare_clippy_lint! { /// } /// ``` /// can be replaced with - /// ```no_run + /// ```rust,ignore /// # let haystacks = [""]; /// # const MY_REGEX: &str = "a.b"; /// let regex = regex::Regex::new(MY_REGEX).unwrap(); @@ -121,17 +122,9 @@ impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX, REGEX_CREATION_IN_LOOPS] impl<'tcx> LateLintPass<'tcx> for Regex { fn check_crate(&mut self, cx: &LateContext<'tcx>) { - // We don't use `match_def_path` here because that relies on matching the exact path, which changed - // between regex 1.8 and 1.9 - // - // `def_path_res_with_base` will resolve through re-exports but is relatively heavy, so we only - // perform the operation once and store the results - let regex_crates = find_crates(cx.tcx, sym!(regex)); - let mut resolve = |path: &[&str], kind: RegexKind| { - for res in def_path_res_with_base(cx.tcx, regex_crates.clone(), &path[1..]) { - if let Some(id) = res.opt_def_id() { - self.definitions.insert(id, kind); - } + let mut resolve = |path: &PathLookup, kind: RegexKind| { + for &id in path.get(cx) { + self.definitions.insert(id, kind); } }; diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs index 5a25483c397c6..07ae92fa98439 100644 --- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs +++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs @@ -74,7 +74,7 @@ fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, spa // We only show this warning for public exported methods. && cx.effective_visibilities.is_exported(fn_def) // We don't want to emit this lint if the `#[must_use]` attribute is already there. - && !cx.tcx.hir().attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use)) + && !cx.tcx.hir_attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use)) && cx.tcx.visibility(fn_def.to_def_id()).is_public() && let ret_ty = return_ty(cx, owner_id) && let self_arg = nth_arg(cx, owner_id, 0) diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 3ba6d6284592b..ab9b0f88f936c 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -5,7 +5,7 @@ use clippy_utils::visitors::for_each_expr; use clippy_utils::{ binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg, - span_find_starting_semi, + span_find_starting_semi, sym, }; use core::ops::ControlFlow; use rustc_ast::MetaItemInner; @@ -22,7 +22,7 @@ use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::edition::Edition; -use rustc_span::{BytePos, Pos, Span, sym}; +use rustc_span::{BytePos, Pos, Span}; use std::borrow::Cow; use std::fmt::Display; @@ -231,7 +231,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { && let Some(stmt) = block.stmts.iter().last() && let StmtKind::Let(local) = &stmt.kind && local.ty.is_none() - && cx.tcx.hir().attrs(local.hir_id).is_empty() + && cx.tcx.hir_attrs(local.hir_id).is_empty() && let Some(initexpr) = &local.init && let PatKind::Binding(_, local_id, _, _) = local.pat.kind && path_to_local_id(retexpr, local_id) @@ -401,18 +401,18 @@ fn check_final_expr<'tcx>( // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) // `#[expect(clippy::needless_return)]` needs to be handled separately to // actually fulfill the expectation (clippy::#12998) - match cx.tcx.hir().attrs(expr.hir_id) { + match cx.tcx.hir_attrs(expr.hir_id) { [] => {}, [attr] => { - if matches!(Level::from_attr(attr), Some(Level::Expect(_))) + if matches!(Level::from_attr(attr), Some((Level::Expect, _))) && let metas = attr.meta_item_list() && let Some(lst) = metas && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() && let [tool, lint_name] = meta_item.path.segments.as_slice() && tool.ident.name == sym::clippy && matches!( - lint_name.ident.name.as_str(), - "needless_return" | "style" | "all" | "warnings" + lint_name.ident.name, + sym::needless_return | sym::style | sym::all | sym::warnings ) { // This is an expectation of the `needless_return` lint @@ -423,7 +423,14 @@ fn check_final_expr<'tcx>( _ => return, } - emit_return_lint(cx, ret_span, semi_spans, &replacement, expr.hir_id); + emit_return_lint( + cx, + peeled_drop_expr.span, + ret_span, + semi_spans, + &replacement, + expr.hir_id, + ); }, ExprKind::If(_, then, else_clause_opt) => { check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); @@ -448,6 +455,7 @@ fn check_final_expr<'tcx>( fn emit_return_lint( cx: &LateContext<'_>, + lint_span: Span, ret_span: Span, semi_spans: Vec, replacement: &RetReplacement<'_>, @@ -457,7 +465,7 @@ fn emit_return_lint( cx, NEEDLESS_RETURN, at, - ret_span, + lint_span, "unneeded `return` statement", |diag| { let suggestions = std::iter::once((ret_span, replacement.to_string())) diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs index 552135b15fd8f..226e8ff6adbf5 100644 --- a/src/tools/clippy/clippy_lints/src/same_name_method.rs +++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs @@ -3,7 +3,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{HirId, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::AssocKind; +use rustc_middle::ty::AssocItem; use rustc_session::declare_lint_pass; use rustc_span::Span; use rustc_span::symbol::Symbol; @@ -85,8 +85,8 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { cx.tcx .associated_items(did) .in_definition_order() - .filter(|assoc_item| matches!(assoc_item.kind, AssocKind::Fn)) - .map(|assoc_item| assoc_item.name) + .filter(|assoc_item| assoc_item.is_fn()) + .map(AssocItem::name) .collect() } else { BTreeSet::new() diff --git a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs index fc02c3a51716e..534ba3a50c6b4 100644 --- a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs +++ b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { } let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; - let item = cx.tcx.hir().expect_item(parent); + let item = cx.tcx.hir_expect_item(parent); let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let ret_ty = return_ty(cx, impl_item.owner_id); @@ -73,7 +73,8 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { if let Some(self_def) = self_ty.ty_adt_def() && let Some(self_local_did) = self_def.did().as_local() && let Node::Item(x) = cx.tcx.hir_node_by_def_id(self_local_did) - && let type_name = x.ident.name.as_str().to_lowercase() + && let Some(type_ident) = x.kind.ident() + && let type_name = type_ident.name.as_str().to_lowercase() && (impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace('_', "") == type_name) { diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs index 6a0dfde2d9c9c..a64b9b223786b 100644 --- a/src/tools/clippy/clippy_lints/src/serde_api.rs +++ b/src/tools/clippy/clippy_lints/src/serde_api.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{get_trait_def_id, paths}; +use clippy_utils::paths; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -32,28 +32,26 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi { }) = item.kind { let did = trait_ref.path.res.def_id(); - if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR) { - if did == visit_did { - let mut seen_str = None; - let mut seen_string = None; - for item in *items { - match item.ident.as_str() { - "visit_str" => seen_str = Some(item.span), - "visit_string" => seen_string = Some(item.span), - _ => {}, - } - } - if let Some(span) = seen_string { - if seen_str.is_none() { - span_lint( - cx, - SERDE_API_MISUSE, - span, - "you should not implement `visit_string` without also implementing `visit_str`", - ); - } + if paths::SERDE_DE_VISITOR.matches(cx, did) { + let mut seen_str = None; + let mut seen_string = None; + for item in *items { + match item.ident.as_str() { + "visit_str" => seen_str = Some(item.span), + "visit_string" => seen_string = Some(item.span), + _ => {}, } } + if let Some(span) = seen_string + && seen_str.is_none() + { + span_lint( + cx, + SERDE_API_MISUSE, + span, + "you should not implement `visit_string` without also implementing `visit_str`", + ); + } } } } diff --git a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs index 1185d67b1258b..ff6e6ef214b5b 100644 --- a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs +++ b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs @@ -3,12 +3,12 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while}; +use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while, sym}; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Span; use rustc_span::symbol::Symbol; -use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for SetContainsOrInsert { then: then_expr, .. }) = higher::If::hir(expr) - && let Some((contains_expr, sym)) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr) + && let Some((contains_expr, sym)) = try_parse_op_call(cx, cond_expr, sym::contains)//try_parse_contains(cx, cond_expr) && let Some(insert_expr) = find_insert_calls(cx, &contains_expr, then_expr) { span_lint( @@ -118,7 +118,7 @@ fn find_insert_calls<'tcx>( expr: &'tcx Expr<'_>, ) -> Option> { for_each_expr(cx, expr, |e| { - if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym!(insert)) + if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym::insert) && SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver) && SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value) { diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index ee282ee1dfb71..14399867f3181 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -8,7 +8,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::hir_id::ItemLocalId; -use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, LetExpr, Node, Pat, PatKind, QPath, UnOp}; +use rustc_hir::{ + Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, LetExpr, LocalSource, Node, Pat, PatKind, QPath, UnOp, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{Span, Symbol}; @@ -65,7 +67,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub SHADOW_REUSE, restriction, - "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`" + "rebinding a name to an expression that reuses the original value, e.g., `let x = x + 1`" } declare_clippy_lint! { @@ -125,6 +127,17 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { return; } + // Desugaring of a destructuring assignment may reuse the same identifier internally. + // Peel `Pat` and `PatField` nodes and check if we reach a desugared `Let` assignment. + if let Some((_, Node::LetStmt(let_stmt))) = cx + .tcx + .hir_parent_iter(pat.hir_id) + .find(|(_, node)| !matches!(node, Node::Pat(_) | Node::PatField(_))) + && let LocalSource::AssignDesugar(_) = let_stmt.source + { + return; + } + let HirId { owner, local_id } = id; // get (or insert) the list of items for this owner and symbol let (ref mut data, scope_owner) = *self.bindings.last_mut().unwrap(); @@ -167,10 +180,10 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool { let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id()); - if let Some(first_scope) = scope_tree.var_scope(first) { - if let Some(second_scope) = scope_tree.var_scope(second) { - return scope_tree.is_subscope_of(second_scope, first_scope); - } + if let Some(first_scope) = scope_tree.var_scope(first) + && let Some(second_scope) = scope_tree.var_scope(second) + { + return scope_tree.is_subscope_of(second_scope, first_scope); } false @@ -218,7 +231,7 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) }, }; span_lint_and_then(cx, lint, span, msg, |diag| { - diag.span_note(cx.tcx.hir().span(shadowed), "previous binding is here"); + diag.span_note(cx.tcx.hir_span(shadowed), "previous binding is here"); }); } diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index e9db7c9d031a9..f3fea3add592d 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{indent_of, snippet}; -use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary}; +use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{GenericArgKind, Ty}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Ident; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, Span}; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -79,10 +79,11 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { if apa.counter <= 1 || !apa.has_expensive_expr_after_last_attr { continue; } + let first_bind_ident = apa.first_bind_ident.unwrap(); span_lint_and_then( cx, SIGNIFICANT_DROP_TIGHTENING, - apa.first_bind_ident.span, + first_bind_ident.span, "temporary with significant `Drop` can be early dropped", |diag| { match apa.counter { @@ -91,13 +92,13 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { let indent = " ".repeat(indent_of(cx, apa.last_stmt_span).unwrap_or(0)); let init_method = snippet(cx, apa.first_method_span, ".."); let usage_method = snippet(cx, apa.last_method_span, ".."); - let stmt = if apa.last_bind_ident == Ident::empty() { - format!("\n{indent}{init_method}.{usage_method};") - } else { + let stmt = if let Some(last_bind_ident) = apa.last_bind_ident { format!( "\n{indent}let {} = {init_method}.{usage_method};", - snippet(cx, apa.last_bind_ident.span, ".."), + snippet(cx, last_bind_ident.span, ".."), ) + } else { + format!("\n{indent}{init_method}.{usage_method};") }; diag.multipart_suggestion_verbose( @@ -113,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { format!( "\n{}drop({});", " ".repeat(indent_of(cx, apa.last_stmt_span).unwrap_or(0)), - apa.first_bind_ident + first_bind_ident ), Applicability::MaybeIncorrect, ); @@ -123,8 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { diag.span_label( apa.first_block_span, format!( - "temporary `{}` is currently being dropped at the end of its contained scope", - apa.first_bind_ident + "temporary `{first_bind_ident}` is currently being dropped at the end of its contained scope" ), ); }, @@ -144,7 +144,10 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { Self { cx, type_cache } } - fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { + fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>, depth: usize) -> bool { + if !self.cx.tcx.recursion_limit().value_within_limit(depth) { + return false; + } let ty = self .cx .tcx @@ -156,17 +159,17 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { e.insert(false); }, } - let value = self.has_sig_drop_attr_uncached(ty); + let value = self.has_sig_drop_attr_uncached(ty, depth + 1); self.type_cache.insert(ty, value); value } - fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>) -> bool { + fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>, depth: usize) -> bool { if let Some(adt) = ty.ty_adt_def() { let mut iter = get_attr( self.cx.sess(), self.cx.tcx.get_attrs_unchecked(adt.did()), - "has_significant_drop", + sym::has_significant_drop, ); if iter.next().is_some() { return true; @@ -176,15 +179,15 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { rustc_middle::ty::Adt(a, b) => { for f in a.all_fields() { let ty = f.ty(self.cx.tcx, b); - if self.has_sig_drop_attr(ty) { + if self.has_sig_drop_attr(ty, depth) { return true; } } for generic_arg in *b { - if let GenericArgKind::Type(ty) = generic_arg.unpack() { - if self.has_sig_drop_attr(ty) { - return true; - } + if let GenericArgKind::Type(ty) = generic_arg.unpack() + && self.has_sig_drop_attr(ty, depth) + { + return true; } } false @@ -192,7 +195,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { rustc_middle::ty::Array(ty, _) | rustc_middle::ty::RawPtr(ty, _) | rustc_middle::ty::Ref(_, ty, _) - | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(*ty), + | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(*ty, depth), _ => false, } } @@ -268,7 +271,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { apa.has_expensive_expr_after_last_attr = false; }; let mut ac = AttrChecker::new(self.cx, self.type_cache); - if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr)) { + if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr), 0) { if let hir::StmtKind::Let(local) = self.ap.curr_stmt.kind && let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind && !self.ap.apas.contains_key(&hir_id) @@ -283,7 +286,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { let mut apa = AuxParamsAttr { first_block_hir_id: self.ap.curr_block_hir_id, first_block_span: self.ap.curr_block_span, - first_bind_ident: ident, + first_bind_ident: Some(ident), first_method_span: { let expr_or_init = expr_or_init(self.cx, expr); if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr_or_init.kind { @@ -307,7 +310,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { match self.ap.curr_stmt.kind { hir::StmtKind::Let(local) => { if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind { - apa.last_bind_ident = ident; + apa.last_bind_ident = Some(ident); } if let Some(local_init) = local.init && let hir::ExprKind::MethodCall(_, _, _, span) = local_init.kind @@ -316,7 +319,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { } }, hir::StmtKind::Semi(semi_expr) => { - if has_drop(semi_expr, &apa.first_bind_ident, self.cx) { + if has_drop(semi_expr, apa.first_bind_ident, self.cx) { apa.has_expensive_expr_after_last_attr = false; apa.last_stmt_span = DUMMY_SP; return; @@ -373,7 +376,7 @@ struct AuxParamsAttr { first_block_span: Span, /// The binding or variable that references the initial construction of the type marked with /// `#[has_significant_drop]`. - first_bind_ident: Ident, + first_bind_ident: Option, /// Similar to `init_bind_ident` but encompasses the right-hand method call. first_method_span: Span, /// Similar to `init_bind_ident` but encompasses the whole contained statement. @@ -381,7 +384,7 @@ struct AuxParamsAttr { /// The last visited binding or variable span within a block that had any referenced inner type /// marked with `#[has_significant_drop]`. - last_bind_ident: Ident, + last_bind_ident: Option, /// Similar to `last_bind_span` but encompasses the right-hand method call. last_method_span: Span, /// Similar to `last_bind_span` but encompasses the whole contained statement. @@ -395,10 +398,10 @@ impl Default for AuxParamsAttr { has_expensive_expr_after_last_attr: false, first_block_hir_id: HirId::INVALID, first_block_span: DUMMY_SP, - first_bind_ident: Ident::empty(), + first_bind_ident: None, first_method_span: DUMMY_SP, first_stmt_span: DUMMY_SP, - last_bind_ident: Ident::empty(), + last_bind_ident: None, last_method_span: DUMMY_SP, last_stmt_span: DUMMY_SP, } @@ -413,7 +416,7 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> { } } -fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_>) -> bool { +fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: Option, lcx: &LateContext<'_>) -> bool { if let hir::ExprKind::Call(fun, [first_arg]) = expr.kind && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind && let Res::Def(DefKind::Fn, did) = fun_path.res @@ -422,7 +425,8 @@ fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_ let has_ident = |local_expr: &hir::Expr<'_>| { if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind && let [first_arg_ps, ..] = arg_path.segments - && &first_arg_ps.ident == first_bind_ident + && let Some(first_bind_ident) = first_bind_ident + && first_arg_ps.ident == first_bind_ident { true } else { diff --git a/src/tools/clippy/clippy_lints/src/single_call_fn.rs b/src/tools/clippy/clippy_lints/src/single_call_fn.rs index 1a2fb77acc15a..64891743dc636 100644 --- a/src/tools/clippy/clippy_lints/src/single_call_fn.rs +++ b/src/tools/clippy/clippy_lints/src/single_call_fn.rs @@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for SingleCallFn { for (&def_id, usage) in &self.def_id_to_usage { if let CallState::Once { call_site } = *usage && let fn_hir_id = cx.tcx.local_def_id_to_hir_id(def_id) - && let fn_span = cx.tcx.hir().span_with_body(fn_hir_id) + && let fn_span = cx.tcx.hir_span_with_body(fn_hir_id) && !self.is_function_allowed(cx, def_id, fn_hir_id, fn_span) { span_lint_hir_and_then( diff --git a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs index 50a6ee316c8a6..8c34da0d14a4d 100644 --- a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs +++ b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs @@ -45,19 +45,20 @@ impl EarlyLintPass for SingleCharLifetimeNames { return; } - if let GenericParamKind::Lifetime = param.kind { - if !param.is_placeholder && param.ident.as_str().len() <= 2 { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - ctx, - SINGLE_CHAR_LIFETIME_NAMES, - param.ident.span, - "single-character lifetime names are likely uninformative", - |diag| { - diag.help("use a more informative name"); - }, - ); - } + if let GenericParamKind::Lifetime = param.kind + && !param.is_placeholder + && param.ident.as_str().len() <= 2 + { + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( + ctx, + SINGLE_CHAR_LIFETIME_NAMES, + param.ident.span, + "single-character lifetime names are likely uninformative", + |diag| { + diag.help("use a more informative name"); + }, + ); } } } 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 fa08245350429..62939912304ba 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 @@ -174,11 +174,11 @@ impl SingleComponentPathImports { } match &item.kind { - ItemKind::Mod(_, ModKind::Loaded(items, ..)) => { + ItemKind::Mod(_, _, ModKind::Loaded(items, ..)) => { self.check_mod(items); }, - ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { - macros.push(item.ident.name); + ItemKind::MacroDef(ident, MacroDef { macro_rules: true, .. }) => { + macros.push(ident.name); }, ItemKind::Use(use_tree) => { let segments = &use_tree.prefix.segments; @@ -204,17 +204,17 @@ impl SingleComponentPathImports { if let UseTreeKind::Nested { items, .. } = &use_tree.kind { for tree in items { let segments = &tree.0.prefix.segments; - if segments.len() == 1 { - if let UseTreeKind::Simple(None) = tree.0.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push(SingleUse { - name, - span: tree.0.span, - item_id: item.id, - can_suggest: false, - }); - } + if segments.len() == 1 + && let UseTreeKind::Simple(None) = tree.0.kind + { + let name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: tree.0.span, + item_id: item.id, + can_suggest: false, + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/single_option_map.rs b/src/tools/clippy/clippy_lints/src/single_option_map.rs index 1fb54950612a5..cc497c97a4725 100644 --- a/src/tools/clippy/clippy_lints/src/single_option_map.rs +++ b/src/tools/clippy/clippy_lints/src/single_option_map.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// param * 2 /// } /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] pub SINGLE_OPTION_MAP, nursery, "Checks for functions with method calls to `.map(_)` on an arg of type `Option` as the outermost expression." diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs index 2d989b1cf0ba0..54d09ff9ee402 100644 --- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs @@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, is_no_std_crate}; +use clippy_utils::{is_no_std_crate, paths}; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr}; @@ -100,7 +100,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { && let Some(start_snippet) = start.span.get_source_text(cx) && let Some(end_snippet) = end.span.get_source_text(cx) { - let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"]) + let should_emit_every_value = if let Some(step_def_id) = paths::ITER_STEP.only(cx) && implements_trait(cx, ty, step_def_id, &[]) { true diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs index dc19236011bd4..3623039aece15 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::sym; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::sym; +use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -18,7 +19,6 @@ declare_clippy_lint! { /// ### Example /// ```rust,no_run /// # use std::ptr::copy_nonoverlapping; - /// # use std::mem::size_of; /// const SIZE: usize = 128; /// let x = [2u8; SIZE]; /// let mut y = [2u8; SIZE]; @@ -63,17 +63,17 @@ fn get_pointee_ty_and_count_expr<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { - const METHODS: [&str; 10] = [ - "copy_to", - "copy_from", - "copy_to_nonoverlapping", - "copy_from_nonoverlapping", - "add", - "wrapping_add", - "sub", - "wrapping_sub", - "offset", - "wrapping_offset", + const METHODS: [Symbol; 10] = [ + sym::copy_to, + sym::copy_from, + sym::copy_to_nonoverlapping, + sym::copy_from_nonoverlapping, + sym::add, + sym::wrapping_add, + sym::sub, + sym::wrapping_sub, + sym::offset, + sym::wrapping_offset, ]; if let ExprKind::Call(func, [.., count]) = expr.kind @@ -98,7 +98,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( } if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind // Find calls to copy_{from,to}{,_nonoverlapping} - && let method_ident = method_path.ident.as_str() + && let method_ident = method_path.ident.name && METHODS.contains(&method_ident) // Get the pointee type diff --git a/src/tools/clippy/clippy_lints/src/size_of_ref.rs b/src/tools/clippy/clippy_lints/src/size_of_ref.rs index b3d32a6d7d84c..60d923bcd77e7 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_ref.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_ref.rs @@ -8,7 +8,7 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does /// - /// Checks for calls to `std::mem::size_of_val()` where the argument is + /// Checks for calls to `size_of_val()` where the argument is /// a reference to a reference. /// /// ### Why is this bad? @@ -29,7 +29,7 @@ declare_clippy_lint! { /// // is already a reference, `&self` is a double-reference. /// // The return value of `size_of_val()` therefore is the /// // size of the reference-type, not the size of `self`. - /// std::mem::size_of_val(&self) + /// size_of_val(&self) /// } /// } /// ``` @@ -42,14 +42,14 @@ declare_clippy_lint! { /// impl Foo { /// fn size(&self) -> usize { /// // Correct - /// std::mem::size_of_val(self) + /// size_of_val(self) /// } /// } /// ``` #[clippy::version = "1.68.0"] pub SIZE_OF_REF, suspicious, - "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended" + "Argument to `size_of_val()` is a double-reference, which is almost certainly unintended" } declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]); @@ -65,9 +65,9 @@ impl LateLintPass<'_> for SizeOfRef { cx, SIZE_OF_REF, expr.span, - "argument to `std::mem::size_of_val()` is a reference to a reference", + "argument to `size_of_val()` is a reference to a reference", None, - "dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type", + "dereference the argument to `size_of_val()` to get the size of the value instead of the size of the reference-type", ); } } diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index d26288adb3919..f497d0700b8eb 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -3,14 +3,13 @@ use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, - span_contains_comment, + span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -248,7 +247,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind && path_to_local_id(self_arg, self.vec_alloc.local_id) - && path.ident.name.as_str() == "extend" + && path.ident.name == sym::extend && self.is_repeat_take(extend_arg) { self.slow_expression = Some(InitializationType::Extend(expr)); @@ -260,14 +259,14 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind && path_to_local_id(self_arg, self.vec_alloc.local_id) - && path.ident.name.as_str() == "resize" + && path.ident.name == sym::resize // Check that is filled with 0 && is_integer_literal(fill_arg, 0) { let is_matching_resize = if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { // If we have a size expression, check that it is equal to what's passed to `resize` SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) - || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity") + || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.name == sym::capacity) } else { self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); true @@ -282,14 +281,14 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { /// Returns `true` if give expression is `repeat(0).take(...)` fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool { if let ExprKind::MethodCall(take_path, recv, [len_arg], _) = expr.kind - && take_path.ident.name.as_str() == "take" + && take_path.ident.name == sym::take // Check that take is applied to `repeat(0)` && self.is_repeat_zero(recv) { if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { // Check that len expression is equals to `with_capacity` expression return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) - || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity"); + || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.name == sym::capacity); } self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index d68ac8bab1286..3d39386ecf909 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::Msrv; -use rustc_attr_parsing::{StabilityLevel, StableSince}; +use rustc_attr_data_structures::{StabilityLevel, StableSince}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index 5c95dfe834730..f63e6b3087b9e 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -5,9 +5,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::path_to_local_id; use clippy_utils::source::{snippet, str_literal_to_char_literal}; use clippy_utils::visitors::{Descend, for_each_expr}; +use clippy_utils::{path_to_local_id, sym}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; @@ -15,7 +15,7 @@ use rustc_hir::{Expr, ExprKind, PatExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -83,29 +83,29 @@ impl StringPatterns { impl_lint_pass!(StringPatterns => [MANUAL_PATTERN_CHAR_COMPARISON, SINGLE_CHAR_PATTERN]); -const PATTERN_METHODS: [(&str, usize); 22] = [ - ("contains", 0), - ("starts_with", 0), - ("ends_with", 0), - ("find", 0), - ("rfind", 0), - ("split", 0), - ("split_inclusive", 0), - ("rsplit", 0), - ("split_terminator", 0), - ("rsplit_terminator", 0), - ("splitn", 1), - ("rsplitn", 1), - ("split_once", 0), - ("rsplit_once", 0), - ("matches", 0), - ("rmatches", 0), - ("match_indices", 0), - ("rmatch_indices", 0), - ("trim_start_matches", 0), - ("trim_end_matches", 0), - ("replace", 0), - ("replacen", 0), +const PATTERN_METHODS: [(Symbol, usize); 22] = [ + (sym::contains, 0), + (sym::starts_with, 0), + (sym::ends_with, 0), + (sym::find, 0), + (sym::rfind, 0), + (sym::split, 0), + (sym::split_inclusive, 0), + (sym::rsplit, 0), + (sym::split_terminator, 0), + (sym::rsplit_terminator, 0), + (sym::splitn, 1), + (sym::rsplitn, 1), + (sym::split_once, 0), + (sym::rsplit_once, 0), + (sym::matches, 0), + (sym::rmatches, 0), + (sym::match_indices, 0), + (sym::rmatch_indices, 0), + (sym::trim_start_matches, 0), + (sym::trim_end_matches, 0), + (sym::replace, 0), + (sym::replacen, 0), ]; fn check_single_char_pattern_lint(cx: &LateContext<'_>, arg: &Expr<'_>) { @@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for StringPatterns { && let ExprKind::MethodCall(method, receiver, args, _) = expr.kind && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() && ty.is_str() - && let method_name = method.ident.name.as_str() + && let method_name = method.ident.name && let Some(&(_, pos)) = PATTERN_METHODS .iter() .find(|(array_method_name, _)| *array_method_name == method_name) diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 4a5f143a2d344..73a9fe71e0018 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -3,7 +3,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{ SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, path_def_id, - peel_blocks, + peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -12,7 +12,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::sym; + +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -160,13 +161,12 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { if is_string(cx, left) { if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) { let parent = get_parent_expr(cx, e); - if let Some(p) = parent { - if let ExprKind::Assign(target, _, _) = p.kind { + if let Some(p) = parent + && let ExprKind::Assign(target, _, _) = p.kind // avoid duplicate matches - if SpanlessEq::new(cx).eq_expr(target, left) { - return; - } - } + && SpanlessEq::new(cx).eq_expr(target, left) + { + return; } } span_lint( @@ -261,7 +261,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = bytes_arg.kind && let ExprKind::Index(left, right, _) = args.kind && let (method_names, expressions, _) = method_calls(left, 1) - && method_names == [sym!(as_bytes)] + && method_names == [sym::as_bytes] && expressions.len() == 1 && expressions[0].1.is_empty() @@ -286,7 +286,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if !e.span.in_external_macro(cx.sess().source_map()) && let ExprKind::MethodCall(path, receiver, ..) = &e.kind - && path.ident.name.as_str() == "as_bytes" + && path.ident.name == sym::as_bytes && let ExprKind::Lit(lit) = &receiver.kind && let LitKind::Str(lit_content, _) = &lit.node { @@ -332,9 +332,9 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { } if let ExprKind::MethodCall(path, recv, [], _) = &e.kind - && path.ident.name.as_str() == "into_bytes" + && path.ident.name == sym::into_bytes && let ExprKind::MethodCall(path, recv, [], _) = &recv.kind - && matches!(path.ident.name.as_str(), "to_owned" | "to_string") + && matches!(path.ident.name, sym::to_owned | sym::to_string) && let ExprKind::Lit(lit) = &recv.kind && let LitKind::Str(lit_content, _) = &lit.node && lit_content.as_str().is_ascii() @@ -438,27 +438,94 @@ declare_clippy_lint! { declare_lint_pass!(StringToString => [STRING_TO_STRING]); +fn is_parent_map_like(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if let Some(parent_expr) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(name, _, _, parent_span) = parent_expr.kind + && name.ident.name == sym::map + && let Some(caller_def_id) = cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) + && (clippy_utils::is_diag_item_method(cx, caller_def_id, sym::Result) + || clippy_utils::is_diag_item_method(cx, caller_def_id, sym::Option) + || clippy_utils::is_diag_trait_item(cx, caller_def_id, sym::Iterator)) + { + Some(parent_span) + } else { + None + } +} + +fn is_called_from_map_like(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + // Look for a closure as parent of `expr`, discarding simple blocks + let parent_closure = cx + .tcx + .hir_parent_iter(expr.hir_id) + .try_fold(expr.hir_id, |child_hir_id, (_, node)| match node { + // Check that the child expression is the only expression in the block + Node::Block(block) if block.stmts.is_empty() && block.expr.map(|e| e.hir_id) == Some(child_hir_id) => { + ControlFlow::Continue(block.hir_id) + }, + Node::Expr(expr) if matches!(expr.kind, ExprKind::Block(..)) => ControlFlow::Continue(expr.hir_id), + Node::Expr(expr) if matches!(expr.kind, ExprKind::Closure(_)) => ControlFlow::Break(Some(expr)), + _ => ControlFlow::Break(None), + }) + .break_value()?; + is_parent_map_like(cx, parent_closure?) +} + +fn suggest_cloned_string_to_string(cx: &LateContext<'_>, span: rustc_span::Span) { + span_lint_and_sugg( + cx, + STRING_TO_STRING, + span, + "`to_string()` called on a `String`", + "try", + "cloned()".to_string(), + Applicability::MachineApplicable, + ); +} + impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if expr.span.from_expansion() { return; } - if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind - && path.ident.name == sym::to_string - && let ty = cx.typeck_results().expr_ty(self_arg) - && is_type_lang_item(cx, ty, LangItem::String) - { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - STRING_TO_STRING, - expr.span, - "`to_string()` called on a `String`", - |diag| { - diag.help("consider using `.clone()`"); - }, - ); + match &expr.kind { + ExprKind::MethodCall(path, self_arg, [], _) => { + if path.ident.name == sym::to_string + && let ty = cx.typeck_results().expr_ty(self_arg) + && is_type_lang_item(cx, ty.peel_refs(), LangItem::String) + { + if let Some(parent_span) = is_called_from_map_like(cx, expr) { + suggest_cloned_string_to_string(cx, parent_span); + } else { + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( + cx, + STRING_TO_STRING, + expr.span, + "`to_string()` called on a `String`", + |diag| { + diag.help("consider using `.clone()`"); + }, + ); + } + } + }, + ExprKind::Path(QPath::TypeRelative(ty, segment)) => { + if segment.ident.name == sym::to_string + && let rustc_hir::TyKind::Path(QPath::Resolved(_, path)) = ty.peel_refs().kind + && let rustc_hir::def::Res::Def(_, def_id) = path.res + && cx + .tcx + .lang_items() + .get(LangItem::String) + .is_some_and(|lang_id| lang_id == def_id) + && let Some(parent_span) = is_parent_map_like(cx, expr) + { + suggest_cloned_string_to_string(cx, parent_span); + } + }, + _ => {}, } } } @@ -489,7 +556,7 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { let tyckres = cx.typeck_results(); if let ExprKind::MethodCall(path, split_recv, [], split_ws_span) = expr.kind - && path.ident.name.as_str() == "split_whitespace" + && path.ident.name == sym::split_whitespace && let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id) && cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id) && let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind diff --git a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs index 1ada7094dc550..33856c750d7e9 100644 --- a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs +++ b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::match_libc_symbol; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::is_expr_unsafe; +use clippy_utils::{match_libc_symbol, sym}; use rustc_errors::Applicability; use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, LangItem, Node, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -44,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { && let ExprKind::Call(func, [recv]) = expr.kind && let ExprKind::Path(path) = &func.kind && let Some(did) = cx.qpath_res(path, func.hir_id).opt_def_id() - && match_libc_symbol(cx, did, "strlen") + && match_libc_symbol(cx, did, sym::strlen) && let ExprKind::MethodCall(path, self_arg, [], _) = recv.kind && !recv.span.from_expansion() && path.ident.name == sym::as_ptr 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 fb426e91bf01d..edb7600b7c067 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -5,6 +5,7 @@ use core::ops::ControlFlow; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -56,8 +57,20 @@ declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind - && let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node) + match expr.kind { + hir::ExprKind::Binary(op, _, _) => { + check_expr_inner(cx, expr, op.node, op.span); + }, + hir::ExprKind::AssignOp(op, _, _) => { + check_expr_inner(cx, expr, op.node.into(), op.span); + }, + _ => {}, + } + } +} + +fn check_expr_inner<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, binop: hir::BinOpKind, span: Span) { + if let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop) && let Some(binop_trait_id) = cx.tcx.lang_items().get(binop_trait_lang) && let Some(op_assign_trait_id) = cx.tcx.lang_items().get(op_assign_trait_lang) @@ -67,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { && let hir::Node::ImplItem(impl_item) = cx.tcx.hir_node_by_def_id(parent_fn) && let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind && let body = cx.tcx.hir_body(body_id) - && let parent_fn = cx.tcx.hir_get_parent_item(expr.hir_id).def_id + && let parent_fn = cx.tcx.hir_get_parent_item(expr.hir_id) && let Some(trait_ref) = trait_ref_of_method(cx, parent_fn) && let trait_id = trait_ref.path.res.def_id() && ![binop_trait_id, op_assign_trait_id].contains(&trait_id) @@ -78,18 +91,17 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { .iter() .find(|&(ts, _)| ts.iter().any(|&t| Some(trait_id) == cx.tcx.lang_items().get(t))) && count_binops(body.value) == 1 - { - span_lint( - cx, - lint, - binop.span, - format!( - "suspicious use of `{}` in `{}` impl", - binop.node.as_str(), - cx.tcx.item_name(trait_id) - ), - ); - } + { + span_lint( + cx, + lint, + span, + format!( + "suspicious use of `{}` in `{}` impl", + binop.as_str(), + cx.tcx.item_name(trait_id) + ), + ); } } diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 7176d533b6164..e3ecd6508bf9a 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -10,7 +10,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{AssignOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::declare_lint_pass; @@ -133,7 +133,7 @@ fn generate_swap_warning<'tcx>( applicability: &mut applicability, } .snippet_index_bindings(&[idx1, idx2, rhs1, rhs2]), - slice.maybe_par(), + slice.maybe_paren(), snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0, snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0, ), @@ -269,12 +269,11 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr< if let ExprKind::Assign(lhs, rhs, _) = expr.kind { return Some((ExprOrIdent::Expr(lhs), rhs)); } - } else if let StmtKind::Let(expr) = stmt.kind { - if let Some(rhs) = expr.init { - if let PatKind::Binding(_, _, ident_l, _) = expr.pat.kind { - return Some((ExprOrIdent::Ident(ident_l), rhs)); - } - } + } else if let StmtKind::Let(expr) = stmt.kind + && let Some(rhs) = expr.init + && let PatKind::Binding(_, _, ident_l, _) = expr.pat.kind + { + return Some((ExprOrIdent::Ident(ident_l), rhs)); } None } @@ -307,7 +306,7 @@ fn extract_sides_of_xor_assign<'a, 'hir>( if let StmtKind::Semi(expr) = stmt.kind && let ExprKind::AssignOp( Spanned { - node: BinOpKind::BitXor, + node: AssignOpKind::BitXorAssign, .. }, lhs, 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 9993e6ae18b9d..7d7d74f27b3c2 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 @@ -1,11 +1,12 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::match_def_path; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{is_in_const_context, paths, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does @@ -33,34 +34,35 @@ declare_clippy_lint! { "`char.is_digit()` is clearer" } -declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); +impl_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]); + +pub(crate) struct ToDigitIsSome { + msrv: Msrv, +} + +impl ToDigitIsSome { + pub(crate) fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind - && is_some_path.ident.name.as_str() == "is_some" + && is_some_path.ident.name == sym::is_some { - let match_result = match &to_digit_expr.kind { + let match_result = match to_digit_expr.kind { hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => { - if to_digits_path.ident.name.as_str() == "to_digit" - && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg) - && *char_arg_ty.kind() == ty::Char + if to_digits_path.ident.name == sym::to_digit + && cx.typeck_results().expr_ty_adjusted(char_arg).is_char() { - Some((true, *char_arg, radix_arg)) + Some((true, char_arg, radix_arg)) } else { None } }, hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => { - if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind - && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id) - && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id() - && match_def_path( - cx, - to_digits_def_id, - &["core", "char", "methods", "", "to_digit"], - ) - { + if paths::CHAR_TO_DIGIT.matches_path(cx, to_digits_call) { Some((false, char_arg, radix_arg)) } else { None @@ -69,7 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { _ => None, }; - if let Some((is_method_call, char_arg, radix_arg)) = match_result { + if let Some((is_method_call, char_arg, radix_arg)) = match_result + && (!is_in_const_context(cx) || self.msrv.meets(cx, msrvs::CONST_CHAR_IS_DIGIT)) + { let mut applicability = Applicability::MachineApplicable; let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability); let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs index 82cc5155380ea..20bf3a0bff1c3 100644 --- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs +++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs @@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { } fn is_struct_with_trailing_zero_sized_array<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool { - if let ItemKind::Struct(data, _) = &item.kind + if let ItemKind::Struct(_, data, _) = &item.kind && let Some(last_field) = data.fields().last() && let field_ty = cx.tcx.normalize_erasing_regions( cx.typing_env(), diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index f961e1c4d1a36..45e54302e32c9 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -15,7 +15,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{BytePos, Span}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { // special handling for self trait bounds as these are not considered generics // ie. trait Foo: Display {} if let Item { - kind: ItemKind::Trait(_, _, _, bounds, ..), + kind: ItemKind::Trait(_, _, _, _, bounds, ..), .. } = item { @@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { .. }) = segments.first() && let Some(Node::Item(Item { - kind: ItemKind::Trait(_, _, _, self_bounds, _), + kind: ItemKind::Trait(_, _, _, _, self_bounds, _), .. })) = cx.tcx.hir_get_if_local(*def_id) { @@ -151,20 +151,19 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { .iter() .filter_map(get_trait_info_from_bound) .for_each(|(trait_item_res, trait_item_segments, span)| { - if let Some(self_segments) = self_bounds_map.get(&trait_item_res) { - if SpanlessEq::new(cx) + if let Some(self_segments) = self_bounds_map.get(&trait_item_res) + && SpanlessEq::new(cx) .paths_by_resolution() .eq_path_segments(self_segments, trait_item_segments) - { - span_lint_and_help( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - span, - "this trait bound is already specified in trait declaration", - None, - "consider removing this trait bound", - ); - } + { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + span, + "this trait bound is already specified in trait declaration", + None, + "consider removing this trait bound", + ); } }); } @@ -283,18 +282,18 @@ impl TraitBounds { .iter() .copied() .chain(p.bounds.iter()) - .filter_map(get_trait_info_from_bound) - .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability)) + .map(|bound| snippet_with_applicability(cx, bound.span(), "_", &mut applicability)) .join(" + "); let hint_string = format!( "consider combining the bounds: `{}: {trait_bounds}`", snippet(cx, p.bounded_ty.span, "_"), ); + let ty_name = snippet(cx, p.bounded_ty.span, "_"); span_lint_and_help( cx, TYPE_REPETITION_IN_BOUNDS, bound.span, - "this type has already been used as a bound predicate", + format!("type `{ty_name}` has already been used as a bound predicate"), None, hint_string, ); @@ -396,15 +395,7 @@ impl Hash for ComparableTraitRef<'_, '_> { fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> { if let GenericBound::Trait(t) = bound { let trait_path = t.trait_ref.path; - let trait_span = { - let path_span = trait_path.span; - if let BoundPolarity::Maybe(_) = t.modifiers.polarity { - path_span.with_lo(path_span.lo() - BytePos(1)) // include the `?` - } else { - path_span - } - }; - Some((trait_path.res, trait_path.segments, trait_span)) + Some((trait_path.res, trait_path.segments, t.span)) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs index 81c0a57083e80..535c044f49e68 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_normalizable; -use clippy_utils::{eq_expr_value, path_to_local}; +use clippy_utils::{eq_expr_value, path_to_local, sym}; use rustc_abi::WrappingRange; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node}; @@ -43,7 +42,7 @@ fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_ binops_with_local(cx, local_expr, lhs) || binops_with_local(cx, local_expr, rhs) }, ExprKind::MethodCall(path, receiver, [arg], _) - if path.ident.name.as_str() == "contains" + if path.ident.name == sym::contains // ... `contains` called on some kind of range && let Some(receiver_adt) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def() && let lang_items = cx.tcx.lang_items() @@ -81,11 +80,9 @@ pub(super) fn check<'tcx>( if let Some(then_some_call) = peel_parent_unsafe_blocks(cx, expr) && let ExprKind::MethodCall(path, receiver, [arg], _) = then_some_call.kind && cx.typeck_results().expr_ty(receiver).is_bool() - && path.ident.name.as_str() == "then_some" + && path.ident.name == sym::then_some && is_local_with_projections(transmutable) && binops_with_local(cx, transmutable, receiver) - && is_normalizable(cx, cx.param_env, from_ty) - && is_normalizable(cx, cx.param_env, to_ty) // we only want to lint if the target type has a niche that is larger than the one of the source type // e.g. `u8` to `NonZero` should lint, but `NonZero` to `u8` should not && let Ok(from_layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(from_ty)) diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index f2da8d35cb4fc..d5112e2c3f97a 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -1,13 +1,9 @@ mod crosspointer_transmute; mod eager_transmute; mod missing_transmute_annotations; -mod transmute_float_to_int; mod transmute_int_to_bool; -mod transmute_int_to_char; -mod transmute_int_to_float; mod transmute_int_to_non_zero; mod transmute_null_to_fn; -mod transmute_num_to_bytes; mod transmute_ptr_to_ptr; mod transmute_ptr_to_ref; mod transmute_ref_to_ref; @@ -141,40 +137,6 @@ declare_clippy_lint! { "transmutes from a pointer to a reference type" } -declare_clippy_lint! { - /// ### What it does - /// Checks for transmutes from an integer to a `char`. - /// - /// ### Why is this bad? - /// Not every integer is a Unicode scalar value. - /// - /// ### Known problems - /// - [`from_u32`] which this lint suggests using is slower than `transmute` - /// as it needs to validate the input. - /// If you are certain that the input is always a valid Unicode scalar value, - /// use [`from_u32_unchecked`] which is as fast as `transmute` - /// but has a semantically meaningful name. - /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`. - /// - /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html - /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html - /// - /// ### Example - /// ```no_run - /// let x = 1_u32; - /// unsafe { - /// let _: char = std::mem::transmute(x); // where x: u32 - /// } - /// - /// // should be: - /// let _ = std::char::from_u32(x).unwrap(); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub TRANSMUTE_INT_TO_CHAR, - complexity, - "transmutes from an integer to a `char`" -} - declare_clippy_lint! { /// ### What it does /// Checks for transmutes from a `&[u8]` to a `&str`. @@ -232,29 +194,6 @@ declare_clippy_lint! { "transmutes from an integer to a `bool`" } -declare_clippy_lint! { - /// ### What it does - /// Checks for transmutes from an integer to a float. - /// - /// ### Why is this bad? - /// Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive - /// and safe. - /// - /// ### Example - /// ```no_run - /// unsafe { - /// let _: f32 = std::mem::transmute(1_u32); // where x: u32 - /// } - /// - /// // should be: - /// let _: f32 = f32::from_bits(1_u32); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub TRANSMUTE_INT_TO_FLOAT, - complexity, - "transmutes from an integer to a float" -} - declare_clippy_lint! { /// ### What it does /// Checks for transmutes from `T` to `NonZero`, and suggests the `new_unchecked` @@ -280,52 +219,6 @@ declare_clippy_lint! { "transmutes from an integer to a non-zero wrapper" } -declare_clippy_lint! { - /// ### What it does - /// Checks for transmutes from a float to an integer. - /// - /// ### Why is this bad? - /// Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive - /// and safe. - /// - /// ### Example - /// ```no_run - /// unsafe { - /// let _: u32 = std::mem::transmute(1f32); - /// } - /// - /// // should be: - /// let _: u32 = 1f32.to_bits(); - /// ``` - #[clippy::version = "1.41.0"] - pub TRANSMUTE_FLOAT_TO_INT, - complexity, - "transmutes from a float to an integer" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for transmutes from a number to an array of `u8` - /// - /// ### Why this is bad? - /// Transmutes are dangerous and error-prone, whereas `to_ne_bytes` - /// is intuitive and safe. - /// - /// ### Example - /// ```no_run - /// unsafe { - /// let x: [u8; 8] = std::mem::transmute(1i64); - /// } - /// - /// // should be - /// let x: [u8; 8] = 0i64.to_ne_bytes(); - /// ``` - #[clippy::version = "1.58.0"] - pub TRANSMUTE_NUM_TO_BYTES, - complexity, - "transmutes from a number to an array of `u8`" -} - declare_clippy_lint! { /// ### What it does /// Checks for transmutes from a pointer to a pointer, or @@ -581,13 +474,9 @@ impl_lint_pass!(Transmute => [ TRANSMUTE_PTR_TO_PTR, USELESS_TRANSMUTE, WRONG_TRANSMUTE, - TRANSMUTE_INT_TO_CHAR, TRANSMUTE_BYTES_TO_STR, TRANSMUTE_INT_TO_BOOL, - TRANSMUTE_INT_TO_FLOAT, TRANSMUTE_INT_TO_NON_ZERO, - TRANSMUTE_FLOAT_TO_INT, - TRANSMUTE_NUM_TO_BYTES, UNSOUND_COLLECTION_TRANSMUTE, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTE_UNDEFINED_REPR, @@ -632,14 +521,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | transmute_null_to_fn::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) | missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id) - | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) - | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv) | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) - | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv) - | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv) | (unsound_collection_transmute::check(cx, e, from_ty, to_ty) || transmute_undefined_repr::check(cx, e, from_ty, to_ty)) | (eager_transmute::check(cx, e, arg, from_ty, to_ty)); diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs deleted file mode 100644 index f2c757952af38..0000000000000 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs +++ /dev/null @@ -1,66 +0,0 @@ -use super::TRANSMUTE_FLOAT_TO_INT; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::sugg; -use rustc_ast as ast; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, UnOp}; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; - -/// Checks for `transmute_float_to_int` lint. -/// Returns `true` if it's triggered, otherwise returns `false`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - mut arg: &'tcx Expr<'_>, - const_context: bool, - msrv: Msrv, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) - if !const_context || msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) => - { - span_lint_and_then( - cx, - TRANSMUTE_FLOAT_TO_INT, - e.span, - format!("transmute from a `{from_ty}` to a `{to_ty}`"), - |diag| { - let mut sugg = sugg::Sugg::hir(cx, arg, ".."); - - if let ExprKind::Unary(UnOp::Neg, inner_expr) = &arg.kind { - arg = inner_expr; - } - - if let ExprKind::Lit(lit) = &arg.kind - // if the expression is a float literal and it is unsuffixed then - // add a suffix so the suggestion is valid and unambiguous - && let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node - { - let op = format!("{sugg}{}", float_ty.name_str()).into(); - match sugg { - sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op), - _ => sugg = sugg::Sugg::NonParen(op), - } - } - - sugg = sugg::Sugg::NonParen(format!("{}.to_bits()", sugg.maybe_par()).into()); - - // cast the result of `to_bits` if `to_ty` is signed - sugg = if let ty::Int(int_ty) = to_ty.kind() { - sugg.as_ty(int_ty.name_str().to_string()) - } else { - sugg - }; - - diag.span_suggestion(e.span, "consider using", sugg, Applicability::Unspecified); - }, - ); - true - }, - _ => false, - } -} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs deleted file mode 100644 index 81d10a7d5bde4..0000000000000 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs +++ /dev/null @@ -1,47 +0,0 @@ -use super::TRANSMUTE_INT_TO_CHAR; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{std_or_core, sugg}; -use rustc_ast as ast; -use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; - -/// Checks for `transmute_int_to_char` lint. -/// Returns `true` if it's triggered, otherwise returns `false`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - arg: &'tcx Expr<'_>, - const_context: bool, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) if !const_context => { - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_CHAR, - e.span, - format!("transmute from a `{from_ty}` to a `char`"), - |diag| { - let Some(top_crate) = std_or_core(cx) else { return }; - let arg = sugg::Sugg::hir(cx, arg, ".."); - let arg = if let ty::Int(_) = from_ty.kind() { - arg.as_ty(ast::UintTy::U32.name_str()) - } else { - arg - }; - diag.span_suggestion( - e.span, - "consider using", - format!("{top_crate}::char::from_u32({arg}).unwrap()"), - Applicability::Unspecified, - ); - }, - ); - true - }, - _ => false, - } -} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs deleted file mode 100644 index aaa95396b4b4b..0000000000000 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::TRANSMUTE_INT_TO_FLOAT; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::sugg; -use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; - -/// Checks for `transmute_int_to_float` lint. -/// Returns `true` if it's triggered, otherwise returns `false`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - arg: &'tcx Expr<'_>, - const_context: bool, - msrv: Msrv, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context || msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) => { - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_FLOAT, - e.span, - format!("transmute from a `{from_ty}` to a `{to_ty}`"), - |diag| { - let arg = sugg::Sugg::hir(cx, arg, ".."); - 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()) - )) - } else { - arg - }; - diag.span_suggestion( - e.span, - "consider using", - format!("{to_ty}::from_bits({arg})"), - Applicability::Unspecified, - ); - }, - ); - true - }, - _ => false, - } -} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs deleted file mode 100644 index d72be270b731f..0000000000000 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::TRANSMUTE_NUM_TO_BYTES; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::sugg; -use rustc_errors::Applicability; -use rustc_hir::Expr; -use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty, UintTy}; - -/// Checks for `transmute_int_to_float` lint. -/// Returns `true` if it's triggered, otherwise returns `false`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - arg: &'tcx Expr<'_>, - const_context: bool, - msrv: Msrv, -) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(_) | ty::Uint(_) | ty::Float(_), ty::Array(arr_ty, _)) => { - if !matches!(arr_ty.kind(), ty::Uint(UintTy::U8)) { - return false; - } - if matches!(from_ty.kind(), ty::Float(_)) && const_context && !msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) - { - return false; - } - - span_lint_and_then( - cx, - TRANSMUTE_NUM_TO_BYTES, - e.span, - format!("transmute from a `{from_ty}` to a `{to_ty}`"), - |diag| { - let arg = sugg::Sugg::hir(cx, arg, ".."); - diag.span_suggestion( - e.span, - "consider using `to_ne_bytes()`", - format!("{arg}.to_ne_bytes()"), - Applicability::Unspecified, - ); - }, - ); - true - }, - _ => false, - } -} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs index fcc763763bd2f..933e25fe98c65 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion_verbose( e.span, "use `pointer::cast` instead", - format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_par()), + format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_paren()), Applicability::MaybeIncorrect, ); } else if from_pointee_ty == to_pointee_ty @@ -48,7 +48,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion_verbose( e.span, format!("use `pointer::{method}` instead"), - format!("{}.{method}()", arg.maybe_par()), + format!("{}.{method}()", arg.maybe_paren()), Applicability::MaybeIncorrect, ); } else { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 45ee83c78ab67..e58212fae15cf 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); if msrv.meets(cx, msrvs::POINTER_CAST) { - format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par()) + format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_paren()) } else if from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() } else { @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( } else if *from_ptr_ty == *to_ref_ty { if from_ptr_ty.has_erased_regions() { if msrv.meets(cx, msrvs::POINTER_CAST) { - format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par()) + format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) } else { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) .to_string() diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 151a6f67437c0..c1c7cc5165652 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -61,10 +61,6 @@ declare_clippy_lint! { /// `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. /// - /// ### Known problems - /// Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530), - /// 1st comment). - /// /// ### Example /// ```no_run /// struct X { @@ -389,7 +385,7 @@ declare_clippy_lint! { /// ```no_run /// let right: std::borrow::Cow<'_, [u8]>; /// ``` - #[clippy::version = "1.85.0"] + #[clippy::version = "1.87.0"] pub OWNED_COW, style, "needlessly owned Cow type" @@ -451,7 +447,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id); match item.kind { - ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty( + ItemKind::Static(_, ty, _, _) | ItemKind::Const(_, ty, _, _) => self.check_ty( cx, ty, CheckTyContext { @@ -595,26 +591,26 @@ impl Types { TyKind::Path(ref qpath) if !context.in_body => { let hir_id = hir_ty.hir_id; let res = cx.qpath_res(qpath, hir_id); - if let Some(def_id) = res.opt_def_id() { - if self.is_type_change_allowed(context) { - // All lints that are being checked in this block are guarded by - // the `avoid_breaking_exported_api` configuration. When adding a - // new lint, please also add the name to the configuration documentation - // in `clippy_config::conf` - - let mut triggered = false; - triggered |= box_collection::check(cx, hir_ty, qpath, def_id); - triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id); - triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id); - triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold); - triggered |= option_option::check(cx, hir_ty, qpath, def_id); - triggered |= linked_list::check(cx, hir_ty, def_id); - triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id); - triggered |= owned_cow::check(cx, qpath, def_id); - - if triggered { - return; - } + if let Some(def_id) = res.opt_def_id() + && self.is_type_change_allowed(context) + { + // All lints that are being checked in this block are guarded by + // the `avoid_breaking_exported_api` configuration. When adding a + // new lint, please also add the name to the configuration documentation + // in `clippy_config::conf` + + let mut triggered = false; + triggered |= box_collection::check(cx, hir_ty, qpath, def_id); + triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id); + triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id); + triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold); + triggered |= option_option::check(cx, hir_ty, qpath, def_id); + triggered |= linked_list::check(cx, hir_ty, def_id); + triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id); + triggered |= owned_cow::check(cx, qpath, def_id); + + if triggered { + return; } } match *qpath { diff --git a/src/tools/clippy/clippy_lints/src/types/vec_box.rs b/src/tools/clippy/clippy_lints/src/types/vec_box.rs index 769244c675e1b..f13042a6fa6bf 100644 --- a/src/tools/clippy/clippy_lints/src/types/vec_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/vec_box.rs @@ -19,61 +19,58 @@ pub(super) fn check<'tcx>( def_id: DefId, box_size_threshold: u64, ) -> bool { - if cx.tcx.is_diagnostic_item(sym::Vec, def_id) { - if let Some(last) = last_path_segment(qpath).args - // Get the _ part of Vec<_> - && let Some(GenericArg::Type(ty)) = last.args.first() - // extract allocator from the Vec for later - && let vec_alloc_ty = last.args.get(1) - // ty is now _ at this point - && let TyKind::Path(ref ty_qpath) = ty.kind - && let res = cx.qpath_res(ty_qpath, ty.hir_id) - && let Some(def_id) = res.opt_def_id() - && Some(def_id) == cx.tcx.lang_items().owned_box() - // At this point, we know ty is Box, now get T - && let Some(last) = last_path_segment(ty_qpath).args - && let Some(GenericArg::Type(boxed_ty)) = last.args.first() - // extract allocator from the Box for later - && let boxed_alloc_ty = last.args.get(1) - // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay - && let ty_ty = lower_ty(cx.tcx, boxed_ty.as_unambig_ty()) - && !ty_ty.has_escaping_bound_vars() - && ty_ty.is_sized(cx.tcx, cx.typing_env()) - && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()) - && ty_ty_size < box_size_threshold - // https://github.com/rust-lang/rust-clippy/issues/7114 - && match (vec_alloc_ty, boxed_alloc_ty) { - (None, None) => true, - // this is in the event that we have something like - // Vec<_, Global>, in which case is equivalent to - // Vec<_> - (None, Some(GenericArg::Type(inner))) | (Some(GenericArg::Type(inner)), None) => { - if let TyKind::Path(path) = inner.kind - && let Some(did) = cx.qpath_res(&path, inner.hir_id).opt_def_id() { - cx.tcx.lang_items().get(LangItem::GlobalAlloc) == Some(did) - } else { - false - } - }, - (Some(GenericArg::Type(l)), Some(GenericArg::Type(r))) => - // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay - lower_ty(cx.tcx, l.as_unambig_ty()) == lower_ty(cx.tcx, r.as_unambig_ty()), - _ => false - } - { - span_lint_and_sugg( - cx, - VEC_BOX, - hir_ty.span, - "`Vec` is already on the heap, the boxing is unnecessary", - "try", - format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), - Applicability::Unspecified, - ); - true - } else { - false + if cx.tcx.is_diagnostic_item(sym::Vec, def_id) + && let Some(last) = last_path_segment(qpath).args + // Get the _ part of Vec<_> + && let Some(GenericArg::Type(ty)) = last.args.first() + // extract allocator from the Vec for later + && let vec_alloc_ty = last.args.get(1) + // ty is now _ at this point + && let TyKind::Path(ref ty_qpath) = ty.kind + && let res = cx.qpath_res(ty_qpath, ty.hir_id) + && let Some(def_id) = res.opt_def_id() + && Some(def_id) == cx.tcx.lang_items().owned_box() + // At this point, we know ty is Box, now get T + && let Some(last) = last_path_segment(ty_qpath).args + && let Some(GenericArg::Type(boxed_ty)) = last.args.first() + // extract allocator from the Box for later + && let boxed_alloc_ty = last.args.get(1) + // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay + && let ty_ty = lower_ty(cx.tcx, boxed_ty.as_unambig_ty()) + && !ty_ty.has_escaping_bound_vars() + && ty_ty.is_sized(cx.tcx, cx.typing_env()) + && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()) + && ty_ty_size < box_size_threshold + // https://github.com/rust-lang/rust-clippy/issues/7114 + && match (vec_alloc_ty, boxed_alloc_ty) { + (None, None) => true, + // this is in the event that we have something like + // Vec<_, Global>, in which case is equivalent to + // Vec<_> + (None, Some(GenericArg::Type(inner))) | (Some(GenericArg::Type(inner)), None) => { + if let TyKind::Path(path) = inner.kind + && let Some(did) = cx.qpath_res(&path, inner.hir_id).opt_def_id() { + cx.tcx.lang_items().get(LangItem::GlobalAlloc) == Some(did) + } else { + false + } + }, + (Some(GenericArg::Type(l)), Some(GenericArg::Type(r))) => + // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay + lower_ty(cx.tcx, l.as_unambig_ty()) == lower_ty(cx.tcx, r.as_unambig_ty()), + _ => false } + { + span_lint_and_sugg( + cx, + VEC_BOX, + hir_ty.span, + "`Vec` is already on the heap, the boxing is unnecessary", + "try", + format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), + Applicability::Unspecified, + ); + true } else { false } diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index a443043bef902..d321c48f6aff8 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -10,7 +10,7 @@ use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Node, QPath use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::symbol::{Ident, kw}; use rustc_span::{Span, sym}; @@ -23,8 +23,8 @@ declare_clippy_lint! { /// implementations. /// /// ### Why is this bad? - /// This is a hard to find infinite recursion that will crash any code - /// using it. + /// Infinite recursion in trait implementation will either cause crashes + /// or result in an infinite loop, and it is hard to detect. /// /// ### Example /// ```no_run @@ -39,9 +39,31 @@ declare_clippy_lint! { /// } /// } /// ``` + /// /// Use instead: /// - /// In such cases, either use `#[derive(PartialEq)]` or don't implement it. + /// ```no_run + /// #[derive(PartialEq)] + /// enum Foo { + /// A, + /// B, + /// } + /// ``` + /// + /// As an alternative, rewrite the logic without recursion: + /// + /// ```no_run + /// enum Foo { + /// A, + /// B, + /// } + /// + /// impl PartialEq for Foo { + /// fn eq(&self, other: &Self) -> bool { + /// matches!((self, other), (Foo::A, Foo::A) | (Foo::B, Foo::B)) + /// } + /// } + /// ``` #[clippy::version = "1.77.0"] pub UNCONDITIONAL_RECURSION, suspicious, @@ -113,7 +135,7 @@ fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Opt }), )) = cx.tcx.hir_parent_iter(hir_id).next() // We exclude `impl` blocks generated from rustc's proc macros. - && !cx.tcx.has_attr(*owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(owner_id.to_def_id()) // It is a implementation of a trait. && let Some(trait_) = impl_.of_trait { @@ -218,7 +240,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local }), )) = cx.tcx.hir_parent_iter(hir_id).next() // We exclude `impl` blocks generated from rustc's proc macros. - && !cx.tcx.has_attr(*owner_id, sym::automatically_derived) + && !cx.tcx.is_automatically_derived(owner_id.to_def_id()) // It is a implementation of a trait. && let Some(trait_) = impl_.of_trait && let Some(trait_def_id) = trait_.trait_def_id() @@ -315,14 +337,14 @@ impl UnconditionalRecursion { for (ty, impl_def_ids) in impls.non_blanket_impls() { let Some(self_def_id) = ty.def() else { continue }; for impl_def_id in impl_def_ids { - if !cx.tcx.has_attr(*impl_def_id, sym::automatically_derived) && + if !cx.tcx.is_automatically_derived(*impl_def_id) && let Some(assoc_item) = cx .tcx .associated_items(impl_def_id) .in_definition_order() // We're not interested in foreign implementations of the `Default` trait. .find(|item| { - item.kind == AssocKind::Fn && item.def_id.is_local() && item.name == kw::Default + item.is_fn() && item.def_id.is_local() && item.name() == kw::Default }) && let Some(body_node) = cx.tcx.hir_get_if_local(assoc_item.def_id) && let Some(body_id) = body_node.body_id() @@ -381,7 +403,7 @@ impl UnconditionalRecursion { implemented_ty_id, method_span, }; - walk_body(&mut c, body); + let _ = walk_body(&mut c, body); } } } diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 746bf018bcc31..a2938c86c76a9 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -312,6 +312,25 @@ fn expr_has_unnecessary_safety_comment<'tcx>( }, _, ) => ControlFlow::Break(()), + // `_ = foo()` is desugared to `{ let _ = foo(); }` + hir::ExprKind::Block( + Block { + rules: BlockCheckMode::DefaultBlock, + stmts: + [ + hir::Stmt { + kind: + hir::StmtKind::Let(hir::LetStmt { + source: hir::LocalSource::AssignDesugar(_), + .. + }), + .. + }, + ], + .. + }, + _, + ) => ControlFlow::Continue(Descend::Yes), // statements will be handled by check_stmt itself again hir::ExprKind::Block(..) => ControlFlow::Continue(Descend::No), _ => ControlFlow::Continue(Descend::Yes), @@ -339,6 +358,33 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { .is_none_or(|src| !src.starts_with("unsafe")) } +fn find_unsafe_block_parent_in_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, +) -> Option<(Span, HirId)> { + match cx.tcx.parent_hir_node(expr.hir_id) { + Node::LetStmt(hir::LetStmt { span, hir_id, .. }) + | Node::Expr(hir::Expr { + hir_id, + kind: hir::ExprKind::Assign(_, _, span), + .. + }) => Some((*span, *hir_id)), + Node::Expr(expr) => find_unsafe_block_parent_in_expr(cx, expr), + node if let Some((span, hir_id)) = span_and_hid_of_item_alike_node(&node) + && is_const_or_static(&node) => + { + Some((span, hir_id)) + }, + + _ => { + if is_branchy(expr) { + return None; + } + Some((expr.span, expr.hir_id)) + }, + } +} + // Checks if any parent {expression, statement, block, local, const, static} // has a safety comment fn block_parents_have_safety_comment( @@ -348,21 +394,7 @@ fn block_parents_have_safety_comment( id: HirId, ) -> bool { let (span, hir_id) = match cx.tcx.parent_hir_node(id) { - Node::Expr(expr) => match cx.tcx.parent_hir_node(expr.hir_id) { - Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), - Node::Item(hir::Item { - kind: ItemKind::Const(..) | ItemKind::Static(..), - span, - owner_id, - .. - }) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)), - _ => { - if is_branchy(expr) { - return false; - } - (expr.span, expr.hir_id) - }, - }, + Node::Expr(expr) if let Some(inner) = find_unsafe_block_parent_in_expr(cx, expr) => inner, Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(hir::LetStmt { span, hir_id, .. }) @@ -371,12 +403,13 @@ fn block_parents_have_safety_comment( .. }) | Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), - Node::Item(hir::Item { - kind: ItemKind::Const(..) | ItemKind::Static(..), - span, - owner_id, - .. - }) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)), + + node if let Some((span, hir_id)) = span_and_hid_of_item_alike_node(&node) + && is_const_or_static(&node) => + { + (span, hir_id) + }, + _ => return false, }; // if unsafe block is part of a let/const/static statement, @@ -427,12 +460,12 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { } fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span { - span.to(cx - .tcx - .hir() - .attrs(hir_id) - .iter() - .fold(span, |acc, attr| acc.to(attr.span()))) + span.to(cx.tcx.hir_attrs(hir_id).iter().fold(span, |acc, attr| { + if attr.is_doc_comment() { + return acc; + } + acc.to(attr.span()) + })) } enum HasSafetyComment { @@ -455,7 +488,7 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf let comment_start = match cx.tcx.parent_hir_node(item.hir_id()) { Node::Crate(parent_mod) => comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item), Node::Item(parent_item) => { - if let ItemKind::Mod(parent_mod) = &parent_item.kind { + if let ItemKind::Mod(_, parent_mod) = &parent_item.kind { comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item) } else { // Doesn't support impls in this position. Pretend a comment was found. @@ -604,31 +637,35 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span fn get_body_search_span(cx: &LateContext<'_>) -> Option { let body = cx.enclosing_body?; - let mut span = cx.tcx.hir_body(body).value.span; - let mut maybe_global_var = false; - for (_, node) in cx.tcx.hir_parent_iter(body.hir_id) { - match node { - Node::Expr(e) => span = e.span, - Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::LetStmt(_) => (), - Node::Item(hir::Item { - kind: ItemKind::Const(..) | ItemKind::Static(..), - .. - }) => maybe_global_var = true, + let mut maybe_mod_item = None; + + for (_, parent_node) in cx.tcx.hir_parent_iter(body.hir_id) { + match parent_node { + Node::Crate(mod_) => return Some(mod_.spans.inner_span), Node::Item(hir::Item { - kind: ItemKind::Mod(_), - span: item_span, + kind: ItemKind::Mod(_, mod_), + span, .. }) => { - span = *item_span; - break; + return maybe_mod_item + .and_then(|item| comment_start_before_item_in_mod(cx, mod_, *span, &item)) + .map(|comment_start| mod_.spans.inner_span.with_lo(comment_start)) + .or(Some(*span)); }, - Node::Crate(mod_) if maybe_global_var => { - span = mod_.spans.inner_span; + node if let Some((span, _)) = span_and_hid_of_item_alike_node(&node) + && !is_const_or_static(&node) => + { + return Some(span); + }, + Node::Item(item) => { + maybe_mod_item = Some(*item); + }, + _ => { + maybe_mod_item = None; }, - _ => break, } } - Some(span) + None } fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { @@ -717,3 +754,28 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos } } } + +fn span_and_hid_of_item_alike_node(node: &Node<'_>) -> Option<(Span, HirId)> { + match node { + Node::Item(item) => Some((item.span, item.owner_id.into())), + Node::TraitItem(ti) => Some((ti.span, ti.owner_id.into())), + Node::ImplItem(ii) => Some((ii.span, ii.owner_id.into())), + _ => None, + } +} + +fn is_const_or_static(node: &Node<'_>) -> bool { + matches!( + node, + Node::Item(hir::Item { + kind: ItemKind::Const(..) | ItemKind::Static(..), + .. + }) | Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Const(..), + .. + }) | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Const(..), + .. + }) + ) +} diff --git a/src/tools/clippy/clippy_lints/src/unicode.rs b/src/tools/clippy/clippy_lints/src/unicode.rs index e1fc644e4ceeb..79571b0409d21 100644 --- a/src/tools/clippy/clippy_lints/src/unicode.rs +++ b/src/tools/clippy/clippy_lints/src/unicode.rs @@ -76,10 +76,10 @@ declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_ impl LateLintPass<'_> for Unicode { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let ExprKind::Lit(lit) = expr.kind { - if let LitKind::Str(_, _) | LitKind::Char(_) = lit.node { - check_str(cx, lit.span, expr.hir_id); - } + if let ExprKind::Lit(lit) = expr.kind + && let LitKind::Str(_, _) | LitKind::Char(_) = lit.node + { + check_str(cx, lit.span, expr.hir_id); } } } diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs index 7803d5115c971..cee4a53f03cbe 100644 --- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs +++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; -use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while}; +use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; // TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std declare_clippy_lint! { @@ -187,7 +187,7 @@ fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_>) -> bool { is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::Vec) - && path.ident.name.as_str() == "reserve" + && path.ident.name == sym::reserve } /// Returns self if the expression is `Vec::set_len()` @@ -209,7 +209,7 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt ExprKind::MethodCall(path, self_expr, [arg], _) => { let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs(); if is_type_diagnostic_item(cx, self_type, sym::Vec) - && path.ident.name.as_str() == "set_len" + && path.ident.name == sym::set_len && !is_integer_literal(arg, 0) { Some((self_expr, expr.span)) diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs index 6dcc1195a7051..48b532968cb57 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::sym; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -7,11 +8,12 @@ use super::UNIT_CMP; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { - if let Some(macro_call) = root_macro_call_first_node(cx, expr) { - let macro_name = cx.tcx.item_name(macro_call.def_id); - let result = match macro_name.as_str() { - "assert_eq" | "debug_assert_eq" => "succeed", - "assert_ne" | "debug_assert_ne" => "fail", + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) + { + let result = match diag_name { + sym::assert_eq_macro | sym::debug_assert_eq_macro => "succeed", + sym::assert_ne_macro | sym::debug_assert_ne_macro => "fail", _ => return, }; let Some((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { @@ -24,7 +26,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, UNIT_CMP, macro_call.span, - format!("`{macro_name}` of unit values detected. This will always {result}"), + format!( + "`{}` of unit values detected. This will always {result}", + cx.tcx.item_name(macro_call.def_id) + ), ); } return; diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs index 4158050f969a8..2b7d3dc0c90a9 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs @@ -130,9 +130,9 @@ impl LateLintPass<'_> for UnnecessaryBoxReturns { } fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - let ItemKind::Fn { sig, .. } = &item.kind else { + let ItemKind::Fn { ident, sig, .. } = &item.kind else { return; }; - self.check_fn_item(cx, sig.decl, item.owner_id.def_id, item.ident.name); + self.check_fn_item(cx, sig.decl, item.owner_id.def_id, ident.name); } } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs b/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs index e5267620c4fb9..f1d1a76d0c2df 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// ```no_run /// # let a: u32 = 42; /// if a > 10 { - /// println!("a is greater than 10"); + /// println!("a is greater than 10"); /// } /// ``` #[clippy::version = "1.86.0"] diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index 937e35dea96d9..54a7efc090ade 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; @@ -78,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { fn_kind: FnKind<'tcx>, fn_decl: &FnDecl<'tcx>, body: &Body<'tcx>, - span: Span, + _span: Span, def_id: LocalDefId, ) { // Abort if public function/method or closure. @@ -93,13 +95,13 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { // Abort if the method is implementing a trait or of it a trait method. let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { - if matches!( + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) + && matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) - ) { - return; - } + ) + { + return; } // Get the wrapper and inner types, if can't, abort. @@ -147,19 +149,22 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { "remove the return type...".to_string(), // FIXME: we should instead get the span including the `->` and suggest an // empty string for this case. - "()".to_string(), - "...and then remove returned values", + Cow::Borrowed("()"), + Cow::Borrowed("...and then remove returned values"), ) } else { + let wrapper = if lang_item == OptionSome { "Some" } else { "Ok" }; ( format!("this function's return value is unnecessarily wrapped by `{return_type_label}`"), format!("remove `{return_type_label}` from the return type..."), - inner_type.to_string(), - "...and then change returning expressions", + Cow::Owned(inner_type.to_string()), + Cow::Owned(format!( + "...and then remove the surrounding `{wrapper}()` from returning expressions" + )), ) }; - span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg, |diag| { + span_lint_and_then(cx, UNNECESSARY_WRAPS, cx.tcx.def_span(def_id), lint_msg, |diag| { diag.span_suggestion( fn_decl.output.span(), return_type_sugg_msg, diff --git a/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs b/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs index a74eab8b6ae50..3326dea8c5dfa 100644 --- a/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs @@ -51,7 +51,7 @@ impl LateLintPass<'_> for UnneededStructPattern { let variant = cx.tcx.adt_def(enum_did).variant_with_id(did); let has_only_fields_brackets = variant.ctor.is_some() && variant.fields.is_empty(); - let non_exhaustive_activated = !variant.def_id.is_local() && variant.is_field_list_non_exhaustive(); + let non_exhaustive_activated = variant.field_list_has_applicable_non_exhaustive(); if !has_only_fields_brackets || non_exhaustive_activated { return; } 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 f43715d6752e3..9ad184450de43 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -69,10 +69,10 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if self.msrv.meets(msrvs::OR_PATTERNS) { - if let ast::ExprKind::Let(pat, _, _, _) = &e.kind { - lint_unnested_or_patterns(cx, pat); - } + if self.msrv.meets(msrvs::OR_PATTERNS) + && let ast::ExprKind::Let(pat, _, _, _) = &e.kind + { + lint_unnested_or_patterns(cx, pat); } } @@ -120,18 +120,25 @@ fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { /// Remove all `(p)` patterns in `pat`. fn remove_all_parens(pat: &mut P) { - struct Visitor; + #[derive(Default)] + struct Visitor { + /// If is not in the outer most pattern. This is needed to avoid removing the outermost + /// parens because top-level or-patterns are not allowed in let statements. + is_inner: bool, + } + impl MutVisitor for Visitor { fn visit_pat(&mut self, pat: &mut P) { + let is_inner = mem::replace(&mut self.is_inner, true); walk_pat(self, pat); let inner = match &mut pat.kind { - Paren(i) => mem::replace(&mut i.kind, Wild), + Paren(i) if is_inner => mem::replace(&mut i.kind, Wild), _ => return, }; pat.kind = inner; } } - Visitor.visit_pat(pat); + Visitor::default().visit_pat(pat); } /// Insert parens where necessary according to Rust's precedence rules for patterns. @@ -224,6 +231,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: us // We're trying to find whatever kind (~"constructor") we found in `alternatives[start..]`. let changed = match &mut focus_kind { + Missing => unreachable!(), // These pattern forms are "leafs" and do not have sub-patterns. // Therefore they are not some form of constructor `C`, // with which a pattern `C(p_0)` may be formed, diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs index 1c1c841e96412..8ceaa3dc58ec0 100644 --- a/src/tools/clippy/clippy_lints/src/unused_async.rs +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_def_id_trait_method; use rustc_hir::def::DefKind; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Node, YieldSource}; +use rustc_hir::{Body, Defaultness, Expr, ExprKind, FnDecl, HirId, Node, TraitItem, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_session::impl_lint_pass; @@ -116,7 +116,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { span: Span, def_id: LocalDefId, ) { - if !span.from_expansion() && fn_kind.asyncness().is_async() && !is_def_id_trait_method(cx, def_id) { + if !span.from_expansion() + && fn_kind.asyncness().is_async() + && !is_def_id_trait_method(cx, def_id) + && !is_default_trait_impl(cx, def_id) + { let mut visitor = AsyncFnVisitor { cx, found_await: false, @@ -189,3 +193,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { } } } + +fn is_default_trait_impl(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + matches!( + cx.tcx.hir_node_by_def_id(def_id), + Node::TraitItem(TraitItem { + defaultness: Defaultness::Default { .. }, + .. + }) + ) +} 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 0687fc319af68..5e1cb9e54f57a 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, paths, peel_blocks}; +use clippy_utils::{is_res_lang_ctor, paths, peel_blocks}; use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -93,14 +93,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { return; } - let async_paths: [&[&str]; 4] = [ + let async_paths = [ &paths::TOKIO_IO_ASYNCREADEXT, &paths::TOKIO_IO_ASYNCWRITEEXT, &paths::FUTURES_IO_ASYNCREADEXT, &paths::FUTURES_IO_ASYNCWRITEEXT, ]; - if async_paths.into_iter().any(|path| match_def_path(cx, trait_id, path)) { + if async_paths.into_iter().any(|path| path.matches(cx, trait_id)) { return; } } @@ -226,7 +226,7 @@ fn is_unreachable_or_panic(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { if is_panic(cx, macro_call.def_id) { return !cx.tcx.hir_is_inside_const_context(expr.hir_id); } - matches!(cx.tcx.item_name(macro_call.def_id).as_str(), "unreachable") + cx.tcx.is_diagnostic_item(sym::unreachable_macro, macro_call.def_id) } fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { @@ -265,15 +265,14 @@ fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { /// If `expr` is an (e).await, return the inner expression "e" that's being /// waited on. Otherwise return None. fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { - if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { - if let ExprKind::Call(func, [arg_0]) = expr.kind { - if matches!( - func.kind, - ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) - ) { - return arg_0; - } - } + if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind + && let ExprKind::Call(func, [arg_0]) = expr.kind + && matches!( + func.kind, + ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) + ) + { + return arg_0; } expr } @@ -292,19 +291,28 @@ fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option { }, }; - match ( - is_trait_method(cx, call, sym::IoRead), - is_trait_method(cx, call, sym::IoWrite), - match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) - || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT), - match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) - || match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT), - ) { - (true, _, _, _) => Some(IoOp::SyncRead(vectorized)), - (_, true, _, _) => Some(IoOp::SyncWrite(vectorized)), - (_, _, true, _) => Some(IoOp::AsyncRead(vectorized)), - (_, _, _, true) => Some(IoOp::AsyncWrite(vectorized)), - _ => None, + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(call.hir_id) + && let Some(trait_def_id) = cx.tcx.trait_of_item(method_def_id) + { + if let Some(diag_name) = cx.tcx.get_diagnostic_name(trait_def_id) { + match diag_name { + sym::IoRead => Some(IoOp::SyncRead(vectorized)), + sym::IoWrite => Some(IoOp::SyncWrite(vectorized)), + _ => None, + } + } else if paths::FUTURES_IO_ASYNCREADEXT.matches(cx, trait_def_id) + || paths::TOKIO_IO_ASYNCREADEXT.matches(cx, trait_def_id) + { + Some(IoOp::AsyncRead(vectorized)) + } else if paths::TOKIO_IO_ASYNCWRITEEXT.matches(cx, trait_def_id) + || paths::FUTURES_IO_ASYNCWRITEEXT.matches(cx, trait_def_id) + { + Some(IoOp::AsyncWrite(vectorized)) + } else { + None + } + } else { + None } } diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs index 7487e273caa7e..1f5351e32aacf 100644 --- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs +++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators}; +use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_session::declare_lint_pass; -use rustc_span::sym; use std::ops::ControlFlow; declare_clippy_lint! { @@ -150,10 +149,10 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { remaining_args, _, ) => { - let method_name = method_name_ident.name.as_str(); + let method_name = method_name_ident.name; // `Peekable` methods - if matches!(method_name, "peek" | "peek_mut" | "next_if" | "next_if_eq") + if matches!(method_name, sym::peek | sym::peek_mut | sym::next_if | sym::next_if_eq) && arg_is_mut_peekable(self.cx, self_arg) { return ControlFlow::Break(()); @@ -167,7 +166,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { } // foo.by_ref(), keep checking for `peek` - if method_name == "by_ref" { + if method_name == sym::by_ref { continue; } diff --git a/src/tools/clippy/clippy_lints/src/unused_rounding.rs b/src/tools/clippy/clippy_lints/src/unused_rounding.rs index 3e5afec541c49..c21004b5362dd 100644 --- a/src/tools/clippy/clippy_lints/src/unused_rounding.rs +++ b/src/tools/clippy/clippy_lints/src/unused_rounding.rs @@ -1,9 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::sym; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -30,19 +32,20 @@ declare_clippy_lint! { } declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); -fn is_useless_rounding<'a>(cx: &EarlyContext<'_>, expr: &'a Expr) -> Option<(&'a str, String)> { +fn is_useless_rounding(cx: &EarlyContext<'_>, expr: &Expr) -> Option<(Symbol, String)> { if let ExprKind::MethodCall(box MethodCall { seg: name_ident, receiver, .. }) = &expr.kind - && let method_name = name_ident.ident.name.as_str() - && (method_name == "ceil" || method_name == "round" || method_name == "floor") + && let method_name = name_ident.ident.name + && matches!(method_name, sym::ceil | sym::floor | sym::round) && let ExprKind::Lit(token_lit) = &receiver.kind && token_lit.is_semantic_float() && let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::() + && f.fract() == 0.0 { - (f.fract() == 0.0).then(|| (method_name, snippet(cx, receiver.span, "..").to_string())) + Some((method_name, snippet(cx, receiver.span, "..").into())) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs index 2c6c75693166f..12da891a71b11 100644 --- a/src/tools/clippy/clippy_lints/src/unused_self.rs +++ b/src/tools/clippy/clippy_lints/src/unused_self.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::sym; use clippy_utils::visitors::is_local_used; use rustc_hir::{Body, Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -57,16 +58,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { return; } let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; - let parent_item = cx.tcx.hir().expect_item(parent); + let parent_item = cx.tcx.hir_expect_item(parent); let assoc_item = cx.tcx.associated_item(impl_item.owner_id); let contains_todo = |cx, body: &'_ Body<'_>| -> bool { clippy_utils::visitors::for_each_expr_without_closures(body.value, |e| { - if let Some(macro_call) = root_macro_call_first_node(cx, e) { - if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } + if let Some(macro_call) = root_macro_call_first_node(cx, e) + && cx.tcx.is_diagnostic_item(sym::todo_macro, macro_call.def_id) + { + ControlFlow::Break(()) } else { ControlFlow::Continue(()) } @@ -74,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { .is_some() }; if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind - && assoc_item.fn_has_self_parameter + && assoc_item.is_method() && let ImplItemKind::Fn(.., body_id) = &impl_item.kind && (!cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) || !self.avoid_breaking_exported_api) && let body = cx.tcx.hir_body(*body_id) diff --git a/src/tools/clippy/clippy_lints/src/unused_trait_names.rs b/src/tools/clippy/clippy_lints/src/unused_trait_names.rs index 2577f1ceaa2c0..14ac65cf4dfe1 100644 --- a/src/tools/clippy/clippy_lints/src/unused_trait_names.rs +++ b/src/tools/clippy/clippy_lints/src/unused_trait_names.rs @@ -60,9 +60,9 @@ impl_lint_pass!(UnusedTraitNames => [UNUSED_TRAIT_NAMES]); impl<'tcx> LateLintPass<'tcx> for UnusedTraitNames { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if !item.span.in_external_macro(cx.sess().source_map()) - && let ItemKind::Use(path, UseKind::Single) = item.kind + && let ItemKind::Use(path, UseKind::Single(ident)) = item.kind // Ignore imports that already use Underscore - && item.ident.name != kw::Underscore + && ident.name != kw::Underscore // Only check traits && let Some(Res::Def(DefKind::Trait, _)) = path.res.first() && cx.tcx.maybe_unused_trait_imports(()).contains(&item.owner_id.def_id) @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedTraitNames { && self.msrv.meets(cx, msrvs::UNDERSCORE_IMPORTS) && !is_from_proc_macro(cx, &last_segment.ident) { - let complete_span = last_segment.ident.span.to(item.ident.span); + let complete_span = last_segment.ident.span.to(ident.span); span_lint_and_sugg( cx, UNUSED_TRAIT_NAMES, diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs index d5309aade7aac..9859ddfdf7bde 100644 --- a/src/tools/clippy/clippy_lints/src/unused_unit.rs +++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs @@ -1,11 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, position_before_rarrow}; -use rustc_ast::visit::FnKind; -use rustc_ast::{ClosureBinder, ast}; +use clippy_utils::{is_never_expr, is_unit_expr}; +use rustc_ast::{Block, StmtKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{ + AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, Node, PolyTraitRef, Term, + Ty, TyKind, +}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{BytePos, Span}; +use rustc_span::edition::Edition; +use rustc_span::{BytePos, Span, sym}; declare_clippy_lint! { /// ### What it does @@ -34,27 +41,89 @@ declare_clippy_lint! { declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]); -impl EarlyLintPass for UnusedUnit { - fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - if let ast::FnRetTy::Ty(ref ty) = kind.decl().output - && let ast::TyKind::Tup(ref vals) = ty.kind - && vals.is_empty() - && !ty.span.from_expansion() - && get_def(span) == get_def(ty.span) +impl<'tcx> LateLintPass<'tcx> for UnusedUnit { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + span: Span, + def_id: LocalDefId, + ) { + if let FnRetTy::Return(hir_ty) = decl.output + && is_unit_ty(hir_ty) + && !hir_ty.span.from_expansion() + && get_def(span) == get_def(hir_ty.span) { // implicit types in closure signatures are forbidden when `for<...>` is present - if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { + if let FnKind::Closure = kind + && let Node::Expr(expr) = cx.tcx.hir_node_by_def_id(def_id) + && let ExprKind::Closure(closure) = expr.kind + && !closure.bound_generic_params.is_empty() + { + return; + } + + // unit never type fallback is no longer supported since Rust 2024. For more information, + // see + if cx.tcx.sess.edition() >= Edition::Edition2024 + && let ExprKind::Block(block, _) = body.value.kind + && let Some(expr) = block.expr + && is_never_expr(cx, expr).is_some() + { return; } - lint_unneeded_unit_return(cx, ty, span); + lint_unneeded_unit_return(cx, hir_ty.span, span); } } - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - if let Some(stmt) = block.stmts.last() - && let ast::StmtKind::Expr(ref expr) = stmt.kind + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Ret(Some(expr)) | ExprKind::Break(_, Some(expr)) = expr.kind && 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: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { + let segments = &poly.trait_ref.path.segments; + + if segments.len() == 1 + && ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str()) + && let Some(args) = segments[0].args + && args.parenthesized == GenericArgsParentheses::ParenSugar + && let constraints = &args.constraints + && constraints.len() == 1 + && constraints[0].ident.name == sym::Output + && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraints[0].kind + && args.span_ext.hi() != poly.span.hi() + && !hir_ty.span.from_expansion() + && is_unit_ty(hir_ty) + { + lint_unneeded_unit_return(cx, hir_ty.span, poly.span); + } + } +} + +impl EarlyLintPass for UnusedUnit { + /// Check for unit expressions in blocks. This is left in the early pass because some macros + /// expand its inputs as-is, making it invisible to the late pass. See #4076. + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { + if let Some(stmt) = block.stmts.last() + && let StmtKind::Expr(expr) = &stmt.kind + && let rustc_ast::ExprKind::Tup(inner) = &expr.kind + && inner.is_empty() && let ctxt = block.span.ctxt() && stmt.span.ctxt() == ctxt && expr.span.ctxt() == ctxt @@ -72,39 +141,10 @@ impl EarlyLintPass for UnusedUnit { ); } } +} - 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) { - let segments = &poly.trait_ref.path.segments; - - if segments.len() == 1 - && ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str()) - && let Some(args) = &segments[0].args - && let ast::GenericArgs::Parenthesized(generic_args) = &**args - && let ast::FnRetTy::Ty(ty) = &generic_args.output - && ty.kind.is_unit() - { - lint_unneeded_unit_return(cx, ty, generic_args.span); - } - } +fn is_unit_ty(ty: &Ty<'_>) -> bool { + matches!(ty.kind, TyKind::Tup([])) } // get the def site @@ -117,24 +157,15 @@ fn get_def(span: Span) -> Option { } } -// 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) { +fn lint_unneeded_unit_return(cx: &LateContext<'_>, ty_span: Span, span: Span) { let (ret_span, appl) = - span.with_hi(ty.span.hi()) + span.with_hi(ty_span.hi()) .get_source_text(cx) - .map_or((ty.span, Applicability::MaybeIncorrect), |src| { - position_before_rarrow(&src).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + .map_or((ty_span, Applicability::MaybeIncorrect), |src| { + position_before_rarrow(&src).map_or((ty_span, Applicability::MaybeIncorrect), |rpos| { ( #[expect(clippy::cast_possible_truncation)] - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + ty_span.with_lo(BytePos(span.lo().0 + rpos as u32)), Applicability::MachineApplicable, ) }) diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index 76b9bbbd32fde..c641d4e55b940 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::is_potentially_local_place; -use clippy_utils::{higher, path_to_local}; +use clippy_utils::{higher, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; -use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, UnOp}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; @@ -12,7 +12,7 @@ use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -111,7 +111,7 @@ struct UnwrapInfo<'tcx> { /// The check, like `x.is_ok()` check: &'tcx Expr<'tcx>, /// The check's name, like `is_ok` - check_name: &'tcx PathSegment<'tcx>, + check_name: Symbol, /// The branch where the check takes place, like `if x.is_ok() { .. }` branch: &'tcx Expr<'tcx>, /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`). @@ -133,12 +133,12 @@ fn collect_unwrap_info<'tcx>( invert: bool, is_entire_condition: bool, ) -> Vec> { - fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { - is_type_diagnostic_item(cx, ty, sym::Option) && ["is_some", "is_none"].contains(&method_name) + fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { + is_type_diagnostic_item(cx, ty, sym::Option) && matches!(method_name, sym::is_none | sym::is_some) } - fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { - is_type_diagnostic_item(cx, ty, sym::Result) && ["is_ok", "is_err"].contains(&method_name) + fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { + is_type_diagnostic_item(cx, ty, sym::Result) && matches!(method_name, sym::is_err | sym::is_ok) } if let ExprKind::Binary(op, left, right) = &expr.kind { @@ -155,14 +155,10 @@ fn collect_unwrap_info<'tcx>( } else if let ExprKind::MethodCall(method_name, receiver, [], _) = &expr.kind && let Some(local_id) = path_to_local(receiver) && let ty = cx.typeck_results().expr_ty(receiver) - && let name = method_name.ident.as_str() + && let name = method_name.ident.name && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name)) { - let unwrappable = match name { - "is_some" | "is_ok" => true, - "is_err" | "is_none" => false, - _ => unreachable!(), - }; + let unwrappable = matches!(name, sym::is_some | sym::is_ok); let safe_to_unwrap = unwrappable != invert; let kind = if is_type_diagnostic_item(cx, ty, sym::Option) { UnwrappableKind::Option @@ -174,7 +170,7 @@ fn collect_unwrap_info<'tcx>( local_id, if_expr, check: expr, - check_name: method_name, + check_name: name, branch, safe_to_unwrap, kind, @@ -184,12 +180,12 @@ fn collect_unwrap_info<'tcx>( Vec::new() } -/// A HIR visitor delegate that checks if a local variable of type `Option<_>` is mutated, -/// *except* for if `Option::as_mut` is called. +/// A HIR visitor delegate that checks if a local variable of type `Option` or `Result` is mutated, +/// *except* for if `.as_mut()` is called. /// The reason for why we allow that one specifically is that `.as_mut()` cannot change -/// the option to `None`, and that is important because this lint relies on the fact that +/// the variant, and that is important because this lint relies on the fact that /// `is_some` + `unwrap` is equivalent to `if let Some(..) = ..`, which it would not be if -/// the option is changed to None between `is_some` and `unwrap`. +/// the option is changed to None between `is_some` and `unwrap`, ditto for `Result`. /// (And also `.as_mut()` is a somewhat common method that is still worth linting on.) struct MutationVisitor<'tcx> { is_mutated: bool, @@ -198,17 +194,17 @@ struct MutationVisitor<'tcx> { } /// Checks if the parent of the expression pointed at by the given `HirId` is a call to -/// `Option::as_mut`. +/// `.as_mut()`. /// /// Used by the mutation visitor to specifically allow `.as_mut()` calls. /// In particular, the `HirId` that the visitor receives is the id of the local expression /// (i.e. the `x` in `x.as_mut()`), and that is the reason for why we care about its parent /// expression: that will be where the actual method call is. -fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { +fn is_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { if let Node::Expr(mutating_expr) = tcx.parent_hir_node(expr_id) && let ExprKind::MethodCall(path, _, [], _) = mutating_expr.kind { - path.ident.name.as_str() == "as_mut" + path.ident.name == sym::as_mut } else { false } @@ -218,18 +214,22 @@ impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> { fn borrow(&mut self, cat: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { if let ty::BorrowKind::Mutable = bk && is_potentially_local_place(self.local_id, &cat.place) - && !is_option_as_mut_use(self.tcx, diag_expr_id) + && !is_as_mut_use(self.tcx, diag_expr_id) { self.is_mutated = true; } } - fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) { - self.is_mutated = true; + fn mutate(&mut self, cat: &PlaceWithHirId<'tcx>, _: HirId) { + if is_potentially_local_place(self.local_id, &cat.place) { + self.is_mutated = true; + } } fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } @@ -274,12 +274,10 @@ enum AsRefKind { /// If it isn't, the expression itself is returned. fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Option) { if let ExprKind::MethodCall(path, recv, [], _) = expr.kind { - if path.ident.name == sym::as_ref { - (recv, Some(AsRefKind::AsRef)) - } else if path.ident.name.as_str() == "as_mut" { - (recv, Some(AsRefKind::AsMut)) - } else { - (expr, None) + match path.ident.name { + sym::as_ref => (recv, Some(AsRefKind::AsRef)), + sym::as_mut => (recv, Some(AsRefKind::AsMut)), + _ => (expr, None), } } else { (expr, None) @@ -294,6 +292,10 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { if expr.span.in_external_macro(self.cx.tcx.sess.source_map()) { return; } + // Skip checking inside closures since they are visited through `Unwrap::check_fn()` already. + if matches!(expr.kind, ExprKind::Closure(_)) { + return; + } if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) { walk_expr(self, cond); self.visit_branch(expr, cond, then, false); @@ -305,8 +307,8 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind && let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg) && let Some(id) = path_to_local(self_arg) - && [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name) - && let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name) + && matches!(method_name.ident.name, sym::unwrap | sym::expect | sym::unwrap_err) + && let call_to_unwrap = matches!(method_name.ident.name, sym::unwrap | sym::expect) && let Some(unwrappable) = self.unwrappables.iter() .find(|u| u.local_id == id) // Span contexts should not differ with the conditional branch @@ -316,7 +318,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { { if call_to_unwrap == unwrappable.safe_to_unwrap { let is_entire_condition = unwrappable.is_entire_condition; - let unwrappable_variable_name = self.cx.tcx.hir().name(unwrappable.local_id); + let unwrappable_variable_name = self.cx.tcx.hir_name(unwrappable.local_id); let suggested_pattern = if call_to_unwrap { unwrappable.kind.success_variant_pattern() } else { @@ -330,8 +332,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { expr.span, format!( "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", - method_name.ident.name, - unwrappable.check_name.ident.as_str(), + method_name.ident.name, unwrappable.check_name, ), |diag| { if is_entire_condition { diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs index f870eb71e19b9..7bec212a23ca0 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs @@ -79,7 +79,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc let mut result = Vec::new(); let _: Option = for_each_expr(cx, body.value, |e| { // check for `expect` - if let Some(arglists) = method_chain_args(e, &["expect"]) { + if let Some(arglists) = method_chain_args(e, &[sym::expect]) { let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); if is_type_diagnostic_item(cx, receiver_ty, sym::Option) || is_type_diagnostic_item(cx, receiver_ty, sym::Result) @@ -89,7 +89,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc } // check for `unwrap` - if let Some(arglists) = method_chain_args(e, &["unwrap"]) { + if let Some(arglists) = method_chain_args(e, &[sym::unwrap]) { let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); if is_type_diagnostic_item(cx, receiver_ty, sym::Option) || is_type_diagnostic_item(cx, receiver_ty, sym::Result) diff --git a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs index 3449468ef4804..8922478e71830 100644 --- a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs +++ b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs @@ -131,11 +131,11 @@ impl LateLintPass<'_> for UpperCaseAcronyms { return; } match it.kind { - ItemKind::TyAlias(..) | ItemKind::Struct(..) | ItemKind::Trait(..) => { - check_ident(cx, &it.ident, it.hir_id(), self.upper_case_acronyms_aggressive); + ItemKind::TyAlias(ident, ..) | ItemKind::Struct(ident, ..) | ItemKind::Trait(_, _, ident, ..) => { + check_ident(cx, &ident, it.hir_id(), self.upper_case_acronyms_aggressive); }, - ItemKind::Enum(ref enumdef, _) => { - check_ident(cx, &it.ident, it.hir_id(), self.upper_case_acronyms_aggressive); + ItemKind::Enum(ident, ref enumdef, _) => { + check_ident(cx, &ident, it.hir_id(), self.upper_case_acronyms_aggressive); // check enum variants separately because again we only want to lint on private enums and // the fn check_variant does not know about the vis of the enum of its variants enumdef.variants.iter().for_each(|variant| { diff --git a/src/tools/clippy/clippy_lints/src/useless_concat.rs b/src/tools/clippy/clippy_lints/src/useless_concat.rs new file mode 100644 index 0000000000000..1ed1fbb3b9c69 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/useless_concat.rs @@ -0,0 +1,104 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::macros::macro_backtrace; +use clippy_utils::paths::CONCAT; +use clippy_utils::source::snippet_opt; +use clippy_utils::tokenize_with_text; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lexer::TokenKind; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks that the `concat!` macro has at least two arguments. + /// + /// ### Why is this bad? + /// If there are less than 2 arguments, then calling the macro is doing nothing. + /// + /// ### Example + /// ```no_run + /// let x = concat!("a"); + /// ``` + /// Use instead: + /// ```no_run + /// let x = "a"; + /// ``` + #[clippy::version = "1.89.0"] + pub USELESS_CONCAT, + complexity, + "checks that the `concat` macro has at least two arguments" +} + +declare_lint_pass!(UselessConcat => [USELESS_CONCAT]); + +impl LateLintPass<'_> for UselessConcat { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + // Check that the expression is generated by a macro. + if expr.span.from_expansion() + // Check that it's a string literal. + && let ExprKind::Lit(lit) = expr.kind + && let LitKind::Str(lit_s, _) = lit.node + // Get the direct parent of the expression. + && let Some(macro_call) = macro_backtrace(expr.span).next() + // Check if the `concat` macro from the `core` library. + && CONCAT.matches(cx, macro_call.def_id) + // We get the original code to parse it. + && let Some(original_code) = snippet_opt(cx, macro_call.span) + // This check allows us to ensure that the code snippet: + // 1. Doesn't come from proc-macro expansion. + // 2. Doesn't come from foreign macro expansion. + // + // It works as follows: if the snippet we get doesn't contain `concat!(`, then it + // means it's not code written in the current crate so we shouldn't lint. + && let mut parts = original_code.split('!') + && parts.next().is_some_and(|p| p.trim() == "concat") + && parts.next().is_some_and(|p| p.trim().starts_with('(')) + { + let mut literal = None; + let mut nb_commas = 0; + let mut nb_idents = 0; + for (token_kind, token_s, _) in tokenize_with_text(&original_code) { + match token_kind { + TokenKind::Eof => break, + TokenKind::Literal { .. } => { + if literal.is_some() { + return; + } + literal = Some(token_s); + }, + TokenKind::Ident => { + if token_s == "true" || token_s == "false" { + literal = Some(token_s); + } else { + nb_idents += 1; + } + }, + TokenKind::Comma => { + nb_commas += 1; + if nb_commas > 1 { + return; + } + }, + // We're inside a macro definition and we are manipulating something we likely + // shouldn't, so aborting. + TokenKind::Dollar => return, + _ => {}, + } + } + // There should always be the ident of the `concat` macro. + if nb_idents == 1 { + span_lint_and_sugg( + cx, + USELESS_CONCAT, + macro_call.span, + "unneeded use of `concat!` macro", + "replace with", + format!("{lit_s:?}"), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 5e452c6d2ac09..380ddea4e1e83 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -1,21 +1,21 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{ - get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, + get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; +use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; declare_clippy_lint! { @@ -92,36 +92,36 @@ fn into_iter_bound<'tcx>( let mut into_iter_span = None; for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates { - if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() { - if tr.self_ty().is_param(param_index) { - if tr.def_id() == into_iter_did { - into_iter_span = Some(*span); - } else { - let tr = cx.tcx.erase_regions(tr); - if tr.has_escaping_bound_vars() { - return None; - } - - // Substitute generics in the predicate and replace the IntoIterator type parameter with the - // `.into_iter()` receiver to see if the bound also holds for that type. - let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { - if i == param_index as usize { - GenericArg::from(into_iter_receiver) - } else { - arg - } - })); + if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() + && tr.self_ty().is_param(param_index) + { + if tr.def_id() == into_iter_did { + into_iter_span = Some(*span); + } else { + let tr = cx.tcx.erase_regions(tr); + if tr.has_escaping_bound_vars() { + return None; + } - let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); - let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); - if !cx - .tcx - .infer_ctxt() - .build(cx.typing_mode()) - .predicate_must_hold_modulo_regions(&obligation) - { - return None; + // Substitute generics in the predicate and replace the IntoIterator type parameter with the + // `.into_iter()` receiver to see if the bound also holds for that type. + let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { + if i == param_index as usize { + GenericArg::from(into_iter_receiver) + } else { + arg } + })); + + let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); + if !cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .predicate_must_hold_modulo_regions(&obligation) + { + return None; } } } @@ -177,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(name, recv, [], _) => { - if is_trait_method(cx, e, sym::Into) && name.ident.as_str() == "into" { + if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if same_type_and_consts(a, b) { @@ -298,6 +298,33 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // implements Copy, in which case .into_iter() returns a copy of the receiver and // cannot be safely omitted. if same_type_and_consts(a, b) && !is_copy(cx, b) { + // Below we check if the parent method call meets the following conditions: + // 1. First parameter is `&mut self` (requires mutable reference) + // 2. Second parameter implements the `FnMut` trait (e.g., Iterator::any) + // For methods satisfying these conditions (like any), .into_iter() must be preserved. + if let Some(parent) = get_parent_expr(cx, e) + && let ExprKind::MethodCall(_, recv, _, _) = parent.kind + && recv.hir_id == e.hir_id + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) + && let sig = cx.tcx.fn_sig(def_id).skip_binder().skip_binder() + && let inputs = sig.inputs() + && inputs.len() >= 2 + && let Some(self_ty) = inputs.first() + && let ty::Ref(_, _, Mutability::Mut) = self_ty.kind() + && let Some(second_ty) = inputs.get(1) + && let predicates = cx.tcx.param_env(def_id).caller_bounds() + && predicates.iter().any(|pred| { + if let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() { + trait_pred.self_ty() == *second_ty + && cx.tcx.lang_items().fn_mut_trait() == Some(trait_pred.def_id()) + } else { + false + } + }) + { + return; + } + let sugg = snippet(cx, recv.span, "").into_owned(); span_lint_and_sugg( cx, @@ -356,7 +383,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if cx.tcx.is_diagnostic_item(sym::from_fn, def_id) && same_type_and_consts(a, b) { let mut app = Applicability::MachineApplicable; - let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_par(); + let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_paren(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); span_lint_and_sugg( cx, @@ -412,24 +439,14 @@ pub fn check_function_application(cx: &LateContext<'_>, expr: &Expr<'_>, recv: & } fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) -> bool { - let recv_ty = cx.typeck_results().expr_ty(recv); - if is_inherent_method_call(cx, expr) - && let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did) - { - if let Some(diag_name) = cx.tcx.get_diagnostic_name(recv_ty_defid) - && matches!(diag_name, sym::Option | sym::Result) - { - return true; - } - - if cx.tcx.is_diagnostic_item(sym::ControlFlow, recv_ty_defid) { - return true; - } - } - if is_trait_method(cx, expr, sym::Iterator) { - return true; + if is_inherent_method_call(cx, expr) { + matches!( + get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)), + Some(sym::Option | sym::Result | sym::ControlFlow) + ) + } else { + is_trait_method(cx, expr, sym::Iterator) } - false } fn adjustments(cx: &LateContext<'_>, expr: &Expr<'_>) -> String { diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 5fc166438e84a..3a08531cf1c9c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -1,16 +1,18 @@ -use clippy_utils::{get_attr, higher}; +use clippy_utils::{MaybePath, get_attr, higher, path_def_id, sym}; +use itertools::Itertools; use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; use rustc_hir::{ self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, - FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind, + FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::symbol::{Ident, Symbol}; use std::cell::Cell; -use std::fmt::{Display, Formatter, Write as _}; +use std::fmt::{Display, Formatter}; declare_lint_pass!( /// ### What it does @@ -148,6 +150,15 @@ fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, } } +fn paths_static_name(cx: &LateContext<'_>, id: DefId) -> String { + cx.get_def_path(id) + .iter() + .map(Symbol::as_str) + .filter(|s| !s.starts_with('<')) + .join("_") + .to_uppercase() +} + struct Binding { name: String, value: T, @@ -257,11 +268,44 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } - fn qpath(&self, qpath: &Binding<&QPath<'_>>) { + fn qpath<'p>(&self, qpath: &Binding<&QPath<'_>>, has_hir_id: &Binding<&impl MaybePath<'p>>) { if let QPath::LangItem(lang_item, ..) = *qpath.value { chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); - } else if let Ok(path) = path_to_string(qpath.value) { - chain!(self, "match_qpath({qpath}, &[{}])", path); + } else if let Some(def_id) = self.cx.qpath_res(qpath.value, has_hir_id.value.hir_id()).opt_def_id() + && !def_id.is_local() + { + bind!(self, def_id); + chain!( + self, + "let Some({def_id}) = cx.qpath_res({qpath}, {has_hir_id}.hir_id).opt_def_id()" + ); + if let Some(name) = self.cx.tcx.get_diagnostic_name(def_id.value) { + chain!(self, "cx.tcx.is_diagnostic_item(sym::{name}, {def_id})"); + } else { + chain!( + self, + "paths::{}.matches(cx, {def_id}) // Add the path to `clippy_utils::paths` if needed", + paths_static_name(self.cx, def_id.value) + ); + } + } + } + + fn maybe_path<'p>(&self, path: &Binding<&impl MaybePath<'p>>) { + if let Some(id) = path_def_id(self.cx, path.value) + && !id.is_local() + { + if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { + chain!(self, "is_path_lang_item(cx, {path}, LangItem::{}", lang.name()); + } else if let Some(name) = self.cx.tcx.get_diagnostic_name(id) { + chain!(self, "is_path_diagnostic_item(cx, {path}, sym::{name})"); + } else { + chain!( + self, + "paths::{}.matches_path(cx, {path}) // Add the path to `clippy_utils::paths` if needed", + paths_static_name(self.cx, id) + ); + } } } @@ -270,7 +314,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ConstArgKind::Path(ref qpath) => { bind!(self, qpath); chain!(self, "let ConstArgKind::Path(ref {qpath}) = {const_arg}.kind"); - self.qpath(qpath); }, ConstArgKind::Anon(anon_const) => { bind!(self, anon_const); @@ -394,12 +437,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, let_expr); kind!("Let({let_expr})"); self.pat(field!(let_expr.pat)); - // Does what ExprKind::Cast does, only adds a clause for the type - // if it's a path - if let Some(TyKind::Path(qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { - bind!(self, qpath); - chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); - self.qpath(qpath); + if let Some(ty) = let_expr.value.ty { + bind!(self, ty); + chain!(self, "let Some({ty}) = {let_expr}.ty"); + self.maybe_path(ty); } self.expr(field!(let_expr.init)); }, @@ -426,6 +467,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Tup({elements})"); self.slice(elements, |e| self.expr(e)); }, + ExprKind::Use(expr, _) => { + bind!(self, expr); + kind!("Use({expr})"); + self.expr(expr); + }, ExprKind::Binary(op, left, right) => { bind!(self, op, left, right); kind!("Binary({op}, {left}, {right})"); @@ -446,11 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::Cast(expr, cast_ty) => { bind!(self, expr, cast_ty); kind!("Cast({expr}, {cast_ty})"); - if let TyKind::Path(ref qpath) = cast_ty.value.kind { - bind!(self, qpath); - chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind"); - self.qpath(qpath); - } + self.maybe_path(cast_ty); self.expr(expr); }, ExprKind::Type(expr, _ty) => { @@ -488,6 +530,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { }) => { let capture_clause = match capture_clause { CaptureBy::Value { .. } => "Value { .. }", + CaptureBy::Use { .. } => "Use { .. }", CaptureBy::Ref => "Ref", }; @@ -555,10 +598,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(object); self.expr(index); }, - ExprKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); + ExprKind::Path(_) => { + self.maybe_path(expr); }, ExprKind::AddrOf(kind, mutability, inner) => { bind!(self, inner); @@ -602,7 +643,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { StructTailExpr::None | StructTailExpr::DefaultFields(_) => None, }); kind!("Struct({qpath}, {fields}, {base})"); - self.qpath(qpath); + self.qpath(qpath, expr); self.slice(fields, |field| { self.ident(field!(field.ident)); self.expr(field!(field.expr)); @@ -642,7 +683,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(expr); } - fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) { + fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>, pat: &Binding<&hir::Pat<'_>>) { let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); @@ -651,15 +692,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatExprKind::Lit { lit, negated } => { bind!(self, lit); bind!(self, negated); - kind!("Lit{{ref {lit}, {negated} }}"); + kind!("Lit {{ ref {lit}, {negated} }}"); self.lit(lit); }, PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"), - PatExprKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); - }, + PatExprKind::Path(_) => self.maybe_path(pat), } } @@ -670,6 +707,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } match pat.value.kind { + PatKind::Missing => unreachable!(), PatKind::Wild => kind!("Wild"), PatKind::Never => kind!("Never"), PatKind::Binding(ann, _, name, sub) => { @@ -690,7 +728,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::Struct(ref qpath, fields, ignore) => { bind!(self, qpath, fields); kind!("Struct(ref {qpath}, {fields}, {ignore})"); - self.qpath(qpath); + self.qpath(qpath, pat); self.slice(fields, |field| { self.ident(field!(field.ident)); self.pat(field!(field.pat)); @@ -704,7 +742,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::TupleStruct(ref qpath, fields, skip_pos) => { bind!(self, qpath, fields); kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})"); - self.qpath(qpath); + self.qpath(qpath, pat); self.slice(fields, |pat| self.pat(pat)); }, PatKind::Tuple(fields, skip_pos) => { @@ -736,13 +774,13 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::Expr(lit_expr) => { bind!(self, lit_expr); kind!("Expr({lit_expr})"); - self.pat_expr(lit_expr); + self.pat_expr(lit_expr, pat); }, PatKind::Range(start, end, end_kind) => { opt_bind!(self, start, end); kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); - start.if_some(|e| self.pat_expr(e)); - end.if_some(|e| self.pat_expr(e)); + start.if_some(|e| self.pat_expr(e, pat)); + end.if_some(|e| self.pat_expr(e, pat)); }, PatKind::Slice(start, middle, end) => { bind!(self, start, end); @@ -787,35 +825,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { - let attrs = cx.tcx.hir().attrs(hir_id); - get_attr(cx.sess(), attrs, "author").count() > 0 -} - -fn path_to_string(path: &QPath<'_>) -> Result { - fn inner(s: &mut String, path: &QPath<'_>) -> Result<(), ()> { - match *path { - QPath::Resolved(_, path) => { - for (i, segment) in path.segments.iter().enumerate() { - if i > 0 { - *s += ", "; - } - write!(s, "{:?}", segment.ident.as_str()).unwrap(); - } - }, - QPath::TypeRelative(ty, segment) => match &ty.kind { - TyKind::Path(inner_path) => { - inner(s, inner_path)?; - *s += ", "; - write!(s, "{:?}", segment.ident.as_str()).unwrap(); - }, - other => write!(s, "/* unimplemented: {other:?}*/").unwrap(), - }, - QPath::LangItem(..) => return Err(()), - } - - Ok(()) - } - let mut s = String::new(); - inner(&mut s, path)?; - Ok(s) + let attrs = cx.tcx.hir_attrs(hir_id); + get_attr(cx.sess(), attrs, sym::author).count() > 0 } diff --git a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs index b108951978f30..d6cf07fdaf3f8 100644 --- a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs +++ b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs @@ -1,4 +1,4 @@ -use clippy_utils::get_attr; +use clippy_utils::{get_attr, sym}; use hir::TraitItem; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -59,6 +59,6 @@ impl<'tcx> LateLintPass<'tcx> for DumpHir { } fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { - let attrs = cx.tcx.hir().attrs(hir_id); - get_attr(cx.sess(), attrs, "dump").count() > 0 + let attrs = cx.tcx.hir_attrs(hir_id); + get_attr(cx.sess(), attrs, sym::dump).count() > 0 } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs deleted file mode 100644 index deb983b6971dc..0000000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod almost_standard_lint_formulation; -pub mod collapsible_calls; -pub mod interning_defined_symbol; -pub mod invalid_paths; -pub mod lint_without_lint_pass; -pub mod msrv_attr_impl; -pub mod outer_expn_data_pass; -pub mod produce_ice; -pub mod slow_symbol_comparisons; -pub mod unnecessary_def_path; -pub mod unsorted_clippy_utils_paths; diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs deleted file mode 100644 index e454427adde1b..0000000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ /dev/null @@ -1,241 +0,0 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::ty::match_type; -use clippy_utils::{def_path_def_ids, is_expn_of, match_def_path, paths}; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::ConstValue; -use rustc_middle::ty; -use rustc_session::impl_lint_pass; -use rustc_span::sym; -use rustc_span::symbol::Symbol; - -use std::borrow::Cow; - -declare_clippy_lint! { - /// ### What it does - /// Checks for interning symbols that have already been pre-interned and defined as constants. - /// - /// ### Why is this bad? - /// It's faster and easier to use the symbol constant. - /// - /// ### Example - /// ```rust,ignore - /// let _ = sym!(f32); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// let _ = sym::f32; - /// ``` - pub INTERNING_DEFINED_SYMBOL, - internal, - "interning a symbol that is pre-interned and defined as a constant" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for unnecessary conversion from Symbol to a string. - /// - /// ### Why is this bad? - /// It's faster use symbols directly instead of strings. - /// - /// ### Example - /// ```rust,ignore - /// symbol.as_str() == "clippy"; - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// symbol == sym::clippy; - /// ``` - pub UNNECESSARY_SYMBOL_STR, - internal, - "unnecessary conversion between Symbol and string" -} - -#[derive(Default)] -pub struct InterningDefinedSymbol { - // Maps the symbol value to the constant DefId. - symbol_map: FxHashMap, -} - -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); - -impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { - fn check_crate(&mut self, cx: &LateContext<'_>) { - if !self.symbol_map.is_empty() { - return; - } - - for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - for def_id in def_path_def_ids(cx.tcx, module) { - for item in cx.tcx.module_children(def_id) { - if let Res::Def(DefKind::Const, item_def_id) = item.res - && let ty = cx.tcx.type_of(item_def_id).instantiate_identity() - && match_type(cx, ty, &paths::SYMBOL) - && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id) - && let Some(value) = value.to_u32().discard_err() - { - self.symbol_map.insert(value, item_def_id); - } - } - } - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Call(func, [arg]) = &expr.kind - && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() - && cx.tcx.is_diagnostic_item(sym::SymbolIntern, *def_id) - && let Some(Constant::Str(arg)) = ConstEvalCtxt::new(cx).eval_simple(arg) - && let value = Symbol::intern(&arg).as_u32() - && let Some(&def_id) = self.symbol_map.get(&value) - { - span_lint_and_sugg( - cx, - INTERNING_DEFINED_SYMBOL, - is_expn_of(expr.span, "sym").unwrap_or(expr.span), - "interning a defined symbol", - "try", - cx.tcx.def_path_str(def_id), - Applicability::MachineApplicable, - ); - } - if let ExprKind::Binary(op, left, right) = expr.kind { - if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { - let data = [ - (left, self.symbol_str_expr(left, cx)), - (right, self.symbol_str_expr(right, cx)), - ]; - match data { - // both operands are a symbol string - [(_, Some(left)), (_, Some(right))] => { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary `Symbol` to string conversion", - "try", - format!( - "{} {} {}", - left.as_symbol_snippet(cx), - op.node.as_str(), - right.as_symbol_snippet(cx), - ), - Applicability::MachineApplicable, - ); - }, - // one of the operands is a symbol string - [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { - // creating an owned string for comparison - if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary string allocation", - "try", - format!("{}.as_str()", symbol.as_symbol_snippet(cx)), - Applicability::MachineApplicable, - ); - } - }, - // nothing found - [(_, None), (_, None)] => {}, - } - } - } - } -} - -impl InterningDefinedSymbol { - fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { - static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR]; - static SYMBOL_STR_PATHS: &[&[&str]] = &[&paths::SYMBOL_AS_STR, &paths::SYMBOL_TO_IDENT_STRING]; - let call = if let ExprKind::AddrOf(_, _, e) = expr.kind - && let ExprKind::Unary(UnOp::Deref, e) = e.kind - { - e - } else { - expr - }; - if let ExprKind::MethodCall(_, item, [], _) = call.kind - // is a method call - && let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id) - && let ty = cx.typeck_results().expr_ty(item) - // ...on either an Ident or a Symbol - && let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { - Some(false) - } else if match_type(cx, ty, &paths::IDENT) { - Some(true) - } else { - None - } - // ...which converts it to a string - && let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS } - && let Some(is_to_owned) = paths - .iter() - .find_map(|path| if match_def_path(cx, did, path) { - Some(path == &paths::SYMBOL_TO_IDENT_STRING) - } else { - None - }) - .or_else(|| if cx.tcx.is_diagnostic_item(sym::to_string_method, did) { - Some(true) - } else { - None - }) - { - return Some(SymbolStrExpr::Expr { - item, - is_ident, - is_to_owned, - }); - } - // is a string constant - if let Some(Constant::Str(s)) = ConstEvalCtxt::new(cx).eval_simple(expr) { - let value = Symbol::intern(&s).as_u32(); - // ...which matches a symbol constant - if let Some(&def_id) = self.symbol_map.get(&value) { - return Some(SymbolStrExpr::Const(def_id)); - } - } - None - } -} - -enum SymbolStrExpr<'tcx> { - /// a string constant with a corresponding symbol constant - Const(DefId), - /// a "symbol to string" expression like `symbol.as_str()` - Expr { - /// part that evaluates to `Symbol` or `Ident` - item: &'tcx Expr<'tcx>, - is_ident: bool, - /// whether an owned `String` is created like `to_ident_string()` - is_to_owned: bool, - }, -} - -impl<'tcx> SymbolStrExpr<'tcx> { - /// Returns a snippet that evaluates to a `Symbol` and is const if possible - fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { - match *self { - Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), - Self::Expr { item, is_ident, .. } => { - let mut snip = snippet(cx, item.span.source_callsite(), ".."); - if is_ident { - // get `Ident.name` - snip.to_mut().push_str(".name"); - } - snip - }, - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs deleted file mode 100644 index 252ac5e676822..0000000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ /dev/null @@ -1,106 +0,0 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::def_path_res; -use clippy_utils::diagnostics::span_lint; -use rustc_hir as hir; -use rustc_hir::Item; -use rustc_hir::def::DefKind; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, FloatTy}; -use rustc_session::declare_lint_pass; -use rustc_span::symbol::Symbol; - -declare_clippy_lint! { - /// ### What it does - /// Checks the paths module for invalid paths. - /// - /// ### Why is this bad? - /// It indicates a bug in the code. - /// - /// ### Example - /// None. - pub INVALID_PATHS, - internal, - "invalid path" -} - -declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); - -impl<'tcx> LateLintPass<'tcx> for InvalidPaths { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let local_def_id = &cx.tcx.parent_module(item.hir_id()); - let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); - if mod_name.as_str() == "paths" - && let hir::ItemKind::Const(.., body_id) = item.kind - && let Some(Constant::Vec(path)) = ConstEvalCtxt::with_env( - cx.tcx, - ty::TypingEnv::post_analysis(cx.tcx, item.owner_id), - cx.tcx.typeck(item.owner_id), - ) - .eval_simple(cx.tcx.hir_body(body_id).value) - && let Some(path) = path - .iter() - .map(|x| { - if let Constant::Str(s) = x { - Some(s.as_str()) - } else { - None - } - }) - .collect::>>() - && !check_path(cx, &path[..]) - { - span_lint(cx, INVALID_PATHS, item.span, "invalid path"); - } - } -} - -// This is not a complete resolver for paths. It works on all the paths currently used in the paths -// module. That's all it does and all it needs to do. -pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if !def_path_res(cx.tcx, path).is_empty() { - return true; - } - - // Some implementations can't be found by `path_to_res`, particularly inherent - // implementations of native types. Check lang items. - let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); - let lang_items = cx.tcx.lang_items(); - // This list isn't complete, but good enough for our current list of paths. - let incoherent_impls = [ - SimplifiedType::Float(FloatTy::F32), - SimplifiedType::Float(FloatTy::F64), - SimplifiedType::Slice, - SimplifiedType::Str, - SimplifiedType::Bool, - SimplifiedType::Char, - ] - .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter()) - .copied(); - for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) { - let lang_item_path = cx.get_def_path(item_def_id); - if path_syms.starts_with(&lang_item_path) { - if let [item] = &path_syms[lang_item_path.len()..] { - if matches!( - cx.tcx.def_kind(item_def_id), - DefKind::Mod | DefKind::Enum | DefKind::Trait - ) { - for child in cx.tcx.module_children(item_def_id) { - if child.ident.name == *item { - return true; - } - } - } else { - for child in cx.tcx.associated_item_def_ids(item_def_id) { - if cx.tcx.item_name(*child) == *item { - return true; - } - } - } - } - } - } - - false -} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/produce_ice.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/produce_ice.rs deleted file mode 100644 index 9169e2968eb7d..0000000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/produce_ice.rs +++ /dev/null @@ -1,37 +0,0 @@ -use rustc_ast::ast::NodeId; -use rustc_ast::visit::FnKind; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::declare_lint_pass; -use rustc_span::Span; - -declare_clippy_lint! { - /// ### What it does - /// Not an actual lint. This lint is only meant for testing our customized internal compiler - /// error message by calling `panic`. - /// - /// ### Why is this bad? - /// ICE in large quantities can damage your teeth - /// - /// ### Example - /// ```rust,ignore - /// 🍦🍦🍦🍦🍦 - /// ``` - pub PRODUCE_ICE, - internal, - "this message should not appear anywhere as we ICE before and don't emit the lint" -} - -declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); - -impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { - assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?"); - } -} - -fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { - match fn_kind { - FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", - FnKind::Closure(..) => false, - } -} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs deleted file mode 100644 index b8bcb9b375601..0000000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs +++ /dev/null @@ -1,74 +0,0 @@ -use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::paths; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::match_type; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; - -declare_clippy_lint! { - /// ### What it does - /// - /// Detects symbol comparison using `Symbol::intern`. - /// - /// ### Why is this bad? - /// - /// Comparison via `Symbol::as_str()` is faster if the interned symbols are not reused. - /// - /// ### Example - /// - /// None, see suggestion. - pub SLOW_SYMBOL_COMPARISONS, - internal, - "detects slow comparisons of symbol" -} - -declare_lint_pass!(SlowSymbolComparisons => [SLOW_SYMBOL_COMPARISONS]); - -fn check_slow_comparison<'tcx>( - cx: &LateContext<'tcx>, - op1: &'tcx Expr<'tcx>, - op2: &'tcx Expr<'tcx>, -) -> Option<(Span, String)> { - if match_type(cx, cx.typeck_results().expr_ty(op1), &paths::SYMBOL) - && let ExprKind::Call(fun, args) = op2.kind - && let ExprKind::Path(ref qpath) = fun.kind - && cx - .tcx - .is_diagnostic_item(sym::SymbolIntern, cx.qpath_res(qpath, fun.hir_id).opt_def_id()?) - && let [symbol_name_expr] = args - && let Some(Constant::Str(symbol_name)) = ConstEvalCtxt::new(cx).eval_simple(symbol_name_expr) - { - Some((op1.span, symbol_name)) - } else { - None - } -} - -impl<'tcx> LateLintPass<'tcx> for SlowSymbolComparisons { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if let ExprKind::Binary(op, left, right) = expr.kind - && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) - && let Some((symbol_span, symbol_name)) = - check_slow_comparison(cx, left, right).or_else(|| check_slow_comparison(cx, right, left)) - { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - SLOW_SYMBOL_COMPARISONS, - expr.span, - "comparing `Symbol` via `Symbol::intern`", - "use `Symbol::as_str` and check the string instead", - format!( - "{}.as_str() {} \"{symbol_name}\"", - snippet_with_applicability(cx, symbol_span, "symbol", &mut applicability), - op.node.as_str() - ), - applicability, - ); - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs deleted file mode 100644 index 76b0a52621be4..0000000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ /dev/null @@ -1,301 +0,0 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; -use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind, LetStmt, Mutability, Node}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::ConstValue; -use rustc_middle::mir::interpret::{Allocation, GlobalAlloc}; -use rustc_middle::ty::{self, Ty}; -use rustc_session::impl_lint_pass; -use rustc_span::Span; -use rustc_span::symbol::Symbol; - -use std::str; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of def paths when a diagnostic item or a `LangItem` could be used. - /// - /// ### Why is this bad? - /// The path for an item is subject to change and is less efficient to look up than a - /// diagnostic item or a `LangItem`. - /// - /// ### Example - /// ```rust,ignore - /// utils::match_type(cx, ty, &paths::VEC) - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) - /// ``` - pub UNNECESSARY_DEF_PATH, - internal, - "using a def path when a diagnostic item or a `LangItem` is available" -} - -impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); - -#[derive(Default)] -pub struct UnnecessaryDefPath { - array_def_ids: FxIndexSet<(DefId, Span)>, - linted_def_ids: FxHashSet, -} - -impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { - return; - } - - match expr.kind { - ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span), - ExprKind::Array(elements) => self.check_array(cx, elements, expr.span), - _ => {}, - } - } - - fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - for &(def_id, span) in &self.array_def_ids { - if self.linted_def_ids.contains(&def_id) { - continue; - } - - let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) { - ("diagnostic item", format!("sym::{sym}")) - } else if let Some(sym) = get_lang_item_name(cx, def_id) { - ("language item", format!("LangItem::{sym}")) - } else { - continue; - }; - - span_lint_and_help( - cx, - UNNECESSARY_DEF_PATH, - span, - format!("hardcoded path to a {msg}"), - None, - format!("convert all references to use `{sugg}`"), - ); - } - } -} - -impl UnnecessaryDefPath { - #[allow(clippy::too_many_lines)] - fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) { - enum Item { - LangItem(&'static str), - DiagnosticItem(Symbol), - } - static PATHS: &[&[&str]] = &[ - &["clippy_utils", "match_def_path"], - &["clippy_utils", "match_trait_method"], - &["clippy_utils", "ty", "match_type"], - &["clippy_utils", "is_expr_path_def_path"], - ]; - - if let [cx_arg, def_arg, args @ ..] = args - && let ExprKind::Path(path) = &func.kind - && let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id() - && let Some(which_path) = match_any_def_paths(cx, id, PATHS) - && let item_arg = if which_path == 4 { &args[1] } else { &args[0] } - // Extract the path to the matched type - && let Some(segments) = path_to_matched_type(cx, item_arg) - && let segments = segments.iter().map(|sym| &**sym).collect::>() - && let Some(def_id) = def_path_def_ids(cx.tcx, &segments[..]).next() - { - // Check if the target item is a diagnostic item or LangItem. - #[rustfmt::skip] - let (msg, item) = if let Some(item_name) - = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) - { - ( - "use of a def path to a diagnostic item", - Item::DiagnosticItem(*item_name), - ) - } else if let Some(item_name) = get_lang_item_name(cx, def_id) { - ( - "use of a def path to a `LangItem`", - Item::LangItem(item_name), - ) - } else { - return; - }; - - let has_ctor = match cx.tcx.def_kind(def_id) { - DefKind::Struct => { - let variant = cx.tcx.adt_def(def_id).non_enum_variant(); - variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - }, - DefKind::Variant => { - let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); - variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - }, - _ => false, - }; - - let mut app = Applicability::MachineApplicable; - let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); - let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); - let (sugg, with_note) = match (which_path, item) { - // match_def_path - (0, Item::DiagnosticItem(item)) => ( - format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), - has_ctor, - ), - (0, Item::LangItem(item)) => ( - format!("{cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some({def_snip})"), - has_ctor, - ), - // match_trait_method - (1, Item::DiagnosticItem(item)) => { - (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) - }, - // match_type - (2, Item::DiagnosticItem(item)) => ( - format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), - false, - ), - (2, Item::LangItem(item)) => ( - format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), - false, - ), - // is_expr_path_def_path - (3, Item::DiagnosticItem(item)) if has_ctor => ( - format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), - false, - ), - (3, Item::LangItem(item)) if has_ctor => ( - format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), - false, - ), - (3, Item::DiagnosticItem(item)) => ( - format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), - false, - ), - (3, Item::LangItem(item)) => ( - format!( - "path_res({cx_snip}, {def_snip}).opt_def_id()\ - .map_or(false, |id| {cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some(id))", - ), - false, - ), - _ => return, - }; - - span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { - diag.span_suggestion(span, "try", sugg, app); - if with_note { - diag.help( - "if this `DefId` came from a constructor expression or pattern then the \ - parent `DefId` should be used instead", - ); - } - }); - - self.linted_def_ids.insert(def_id); - } - } - - fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { - let Some(path) = path_from_array(elements) else { return }; - - for def_id in def_path_def_ids(cx.tcx, &path.iter().map(AsRef::as_ref).collect::>()) { - self.array_def_ids.insert((def_id, span)); - } - } -} - -fn path_to_matched_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option> { - match peel_hir_expr_refs(expr).0.kind { - ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { - Res::Local(hir_id) => { - if let Node::LetStmt(LetStmt { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) { - path_to_matched_type(cx, init) - } else { - None - } - }, - Res::Def(DefKind::Static { .. }, def_id) => read_mir_alloc_def_path( - cx, - cx.tcx.eval_static_initializer(def_id).ok()?.inner(), - cx.tcx.type_of(def_id).instantiate_identity(), - ), - Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { - ConstValue::Indirect { alloc_id, offset } if offset.bytes() == 0 => { - let alloc = cx.tcx.global_alloc(alloc_id).unwrap_memory(); - read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity()) - }, - _ => None, - }, - _ => None, - }, - ExprKind::Array(exprs) => path_from_array(exprs), - _ => None, - } -} - -fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { - let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { - let &alloc = alloc.provenance().ptrs().values().next()?; - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) { - (alloc.inner(), ty) - } else { - return None; - } - } else { - (alloc, ty) - }; - - if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() - && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() - && ty.is_str() - { - alloc - .provenance() - .ptrs() - .values() - .map(|&alloc| { - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) { - let alloc = alloc.inner(); - str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) - .ok() - .map(ToOwned::to_owned) - } else { - None - } - }) - .collect() - } else { - None - } -} - -fn path_from_array(exprs: &[Expr<'_>]) -> Option> { - exprs - .iter() - .map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some((*sym.as_str()).to_owned()); - } - } - - None - }) - .collect() -} - -fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { - if let Some((lang_item, _)) = cx.tcx.lang_items().iter().find(|(_, id)| *id == def_id) { - Some(lang_item.variant_name()) - } else { - None - } -} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs deleted file mode 100644 index a5c4bf474f7aa..0000000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unsorted_clippy_utils_paths.rs +++ /dev/null @@ -1,49 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast::{Crate, ItemKind, ModKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::declare_lint_pass; - -declare_clippy_lint! { - /// ### What it does - /// Checks that [`clippy_utils::paths`] is sorted lexically - /// - /// ### Why is this bad? - /// We like to pretend we're an example of tidy code. - /// - /// ### Example - /// Wrong ordering of the util::paths constants. - pub UNSORTED_CLIPPY_UTILS_PATHS, - internal, - "various things that will negatively affect your clippy experience" -} - -declare_lint_pass!(UnsortedClippyUtilsPaths => [UNSORTED_CLIPPY_UTILS_PATHS]); - -impl EarlyLintPass for UnsortedClippyUtilsPaths { - fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { - if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { - if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { - let mut last_name: Option<&str> = None; - for item in items { - let name = item.ident.as_str(); - if let Some(last_name) = last_name { - if *last_name > *name { - span_lint( - cx, - UNSORTED_CLIPPY_UTILS_PATHS, - item.span, - "this constant should be before the previous constant due to lexical \ - ordering", - ); - } - } - last_name = Some(name); - } - } - } - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index 4476cd1005e7e..16066dd96c0ab 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -2,6 +2,3 @@ pub mod attr_collector; pub mod author; pub mod dump_hir; pub mod format_args_collector; - -#[cfg(feature = "internal")] -pub mod internal_lints; diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 3346b15dae9ca..7b6a25123e85e 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -8,14 +8,14 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment}; +use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; -use rustc_span::{DesugaringKind, Span, sym}; +use rustc_span::{DesugaringKind, Span}; pub struct UselessVec { too_large_for_stack: u64, @@ -249,10 +249,8 @@ fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// that also exists on slices. If this returns true, it means that /// this expression does not actually require a `Vec` and could just work with an array. pub fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - const ALLOWED_METHOD_NAMES: &[&str] = &["len", "as_ptr", "is_empty"]; - if let ExprKind::MethodCall(path, _, [], _) = e.kind { - ALLOWED_METHOD_NAMES.contains(&path.ident.name.as_str()) + matches!(path.ident.name, sym::as_ptr | sym::is_empty | sym::len) } else { is_trait_method(cx, e, sym::IntoIterator) } diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index 405310512dff3..45a5dbabeb4ef 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -68,6 +68,8 @@ declare_clippy_lint! { /// (including the standard library) provide modules named "prelude" specifically designed /// for wildcard import. /// + /// Wildcard imports reexported through `pub use` are also allowed. + /// /// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name. /// /// These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag. @@ -121,7 +123,9 @@ impl LateLintPass<'_> for WildcardImports { } let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id); - if cx.tcx.visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) { + if cx.tcx.visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) + && !self.warn_on_all + { return; } if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind @@ -150,7 +154,7 @@ impl LateLintPass<'_> for WildcardImports { (span, false) }; - let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(); + let mut imports: Vec<_> = used_imports.iter().map(ToString::to_string).collect(); let imports_string = if imports.len() == 1 { imports.pop().unwrap() } else if braced_glob { diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 11c14c1477764..a8758b65c9192 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::is_in_test; use clippy_utils::macros::{FormatArgsStorage, MacroCall, format_arg_removal_span, root_macro_call_first_node}; use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma}; +use clippy_utils::{is_in_test, sym}; use rustc_ast::token::LitKind; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, @@ -12,7 +12,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -359,7 +359,7 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { } fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { - let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { + let Some(&FormatArgsPiece::Literal(last)) = format_args.template.last() else { return; }; @@ -401,7 +401,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma return; }; - if format_args.template.len() == 1 && last.as_str() == "\n" { + if format_args.template.len() == 1 && last == sym::LF { // print!("\n"), write!(f, "\n") diag.multipart_suggestion( @@ -427,9 +427,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma } fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { - if let [FormatArgsPiece::Literal(literal)] = &format_args.template[..] - && literal.as_str() == "\n" - { + if let [FormatArgsPiece::Literal(sym::LF)] = &format_args.template[..] { let mut span = format_args.span; let lint = if name == "writeln" { @@ -500,9 +498,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { None => return, }, LitKind::Char => ( - match lit.symbol.as_str() { - "\"" => "\\\"", - "\\'" => "'", + match lit.symbol { + sym::DOUBLE_QUOTE => "\\\"", + sym::BACKSLASH_SINGLE_QUOTE => "'", _ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) { Some(stripped) => stripped, None => return, diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs index f6948be7f67aa..1550872bca2b9 100644 --- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs +++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item, ty_from_hir_ty}; +use clippy_utils::ty::{is_type_diagnostic_item, ty_from_hir_ty}; use rustc_hir::{self as hir, AmbigArg, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf as _; @@ -51,11 +51,11 @@ impl LateLintPass<'_> for ZeroSizedMapValues { && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) && let ty::Adt(_, args) = ty.kind() && let ty = args.type_at(1) - // Fixes https://github.com/rust-lang/rust-clippy/issues/7447 because of - // https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/sty.rs#L968 - && !ty.has_escaping_bound_vars() - // Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`. - && is_normalizable(cx, cx.param_env, ty) + // Ensure that no type information is missing, to avoid a delayed bug in the compiler if this is not the case. + // This might happen when computing a reference/pointer metadata on a type for which we + // cannot check if it is `Sized` or not, such as an incomplete associated type in a + // type alias. See an example in `issue14822()` of `tests/ui/zero_sized_hashmap_values.rs`. + && !ty.has_non_region_param() && let Ok(layout) = cx.layout_of(ty) && layout.is_zst() { @@ -74,10 +74,10 @@ impl LateLintPass<'_> for ZeroSizedMapValues { fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool { let parent_id = cx.tcx.hir_get_parent_item(hir_id); let second_parent_id = cx.tcx.hir_get_parent_item(parent_id.into()).def_id; - if let Node::Item(item) = cx.tcx.hir_node_by_def_id(second_parent_id) { - if let ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind { - return true; - } + if let Node::Item(item) = cx.tcx.hir_node_by_def_id(second_parent_id) + && let ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind + { + return true; } false } diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs index 7667db469689e..09f1084fe7004 100644 --- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs +++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs @@ -4,6 +4,7 @@ use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; use rustc_errors::Applicability; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local}; use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -68,6 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { let mut vis = WaitFinder { cx, local_id, + body_id: cx.tcx.hir_enclosing_body_owner(expr.hir_id), state: VisitorState::WalkUpToLocal, early_return: None, missing_wait_branch: None, @@ -129,9 +131,10 @@ struct MaybeWait(Span); struct WaitFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, local_id: HirId, + body_id: LocalDefId, state: VisitorState, early_return: Option, - // When joining two if branches where one of them doesn't call `wait()`, stores its span for more targetted help + // When joining two if branches where one of them doesn't call `wait()`, stores its span for more targeted help // messages missing_wait_branch: Option, } @@ -186,7 +189,7 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { } } else { match ex.kind { - ExprKind::Ret(e) => { + ExprKind::Ret(e) if self.cx.tcx.hir_enclosing_body_owner(ex.hir_id) == self.body_id => { visit_opt!(self, visit_expr, e); if self.early_return.is_none() { self.early_return = Some(ex.span); diff --git a/src/tools/clippy/clippy_lints_internal/Cargo.toml b/src/tools/clippy/clippy_lints_internal/Cargo.toml new file mode 100644 index 0000000000000..a8293a1ad395d --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "clippy_lints_internal" +version = "0.0.1" +edition = "2024" + +[dependencies] +clippy_config = { path = "../clippy_config" } +clippy_utils = { path = "../clippy_utils" } +regex = { version = "1.5" } +rustc-semver = "1.1" + +[package.metadata.rust-analyzer] +# This crate uses #[feature(rustc_private)] +rustc_private = true diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs b/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs similarity index 88% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs rename to src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs index bfcce81c498a8..311f76fee6b8b 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs +++ b/src/tools/clippy/clippy_lints_internal/src/almost_standard_lint_formulation.rs @@ -1,11 +1,11 @@ -use crate::utils::internal_lints::lint_without_lint_pass::is_lint_ref_type; +use crate::lint_without_lint_pass::is_lint_ref_type; use clippy_utils::diagnostics::span_lint_and_help; use regex::Regex; use rustc_hir::{Attribute, Item, ItemKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::impl_lint_pass; +use rustc_session::{declare_tool_lint, impl_lint_pass}; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks if lint formulations have a standardized format. /// @@ -14,9 +14,10 @@ declare_clippy_lint! { /// /// ### Example /// `Checks for use...` can be written as `Checks for usage...` . - pub ALMOST_STANDARD_LINT_FORMULATION, - internal, - "lint formulations must have a standardized format." + pub clippy::ALMOST_STANDARD_LINT_FORMULATION, + Warn, + "lint formulations must have a standardized format.", + report_in_external_macro: true } impl_lint_pass!(AlmostStandardFormulation => [ALMOST_STANDARD_LINT_FORMULATION]); @@ -44,11 +45,10 @@ impl AlmostStandardFormulation { impl<'tcx> LateLintPass<'tcx> for AlmostStandardFormulation { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let mut check_next = false; - if let ItemKind::Static(ty, Mutability::Not, _) = item.kind { + if let ItemKind::Static(_, ty, Mutability::Not, _) = item.kind { let lines = cx .tcx - .hir() - .attrs(item.hir_id()) + .hir_attrs(item.hir_id()) .iter() .filter_map(|attr| Attribute::doc_str(attr).map(|sym| (sym, attr))); if is_lint_ref_type(cx, ty) { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs similarity index 95% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs rename to src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs index 2e6fb7c4ce4d5..7c9e7286925ef 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs +++ b/src/tools/clippy/clippy_lints_internal/src/collapsible_calls.rs @@ -1,15 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{SpanlessEq, is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt}; +use clippy_utils::{SpanlessEq, is_lint_allowed, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; use std::borrow::{Borrow, Cow}; -declare_clippy_lint! { +use crate::internal_paths; + +declare_tool_lint! { /// ### What it does /// Lints `span_lint_and_then` function calls, where the /// closure argument has only one statement and that statement is a method @@ -64,9 +66,10 @@ declare_clippy_lint! { /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); /// ``` - pub COLLAPSIBLE_SPAN_LINT_CALLS, - internal, - "found collapsible `span_lint_and_then` calls" + pub clippy::COLLAPSIBLE_SPAN_LINT_CALLS, + Warn, + "found collapsible `span_lint_and_then` calls", + report_in_external_macro: true } declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); @@ -78,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { } if let ExprKind::Call(func, [call_cx, call_lint, call_sp, call_msg, call_f]) = expr.kind - && is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]) + && internal_paths::SPAN_LINT_AND_THEN.matches_path(cx, func) && let ExprKind::Closure(&Closure { body, .. }) = call_f.kind && let body = cx.tcx.hir_body(body) && let only_expr = peel_blocks_with_stmt(body.value) diff --git a/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs b/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs new file mode 100644 index 0000000000000..a1dacd359a06c --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs @@ -0,0 +1,164 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::paths; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::{AttrStyle, DelimArgs}; +use rustc_hir::def::Res; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{ + AttrArgs, AttrItem, AttrPath, Attribute, HirId, Impl, Item, ItemKind, Path, QPath, TraitRef, Ty, TyKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; +use rustc_middle::ty::TyCtxt; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +declare_tool_lint! { + /// ### What it does + /// Checks for structs or enums that derive `serde::Deserialize` and that + /// do not have a `#[serde(deny_unknown_fields)]` attribute. + /// + /// ### Why is this bad? + /// If the struct or enum is used in [`clippy_config::conf::Conf`] and a + /// user inserts an unknown field by mistake, the user's error will be + /// silently ignored. + /// + /// ### Example + /// ```rust + /// #[derive(serde::Deserialize)] + /// pub struct DisallowedPath { + /// path: String, + /// reason: Option, + /// replacement: Option, + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// #[derive(serde::Deserialize)] + /// #[serde(deny_unknown_fields)] + /// pub struct DisallowedPath { + /// path: String, + /// reason: Option, + /// replacement: Option, + /// } + /// ``` + pub clippy::DERIVE_DESERIALIZE_ALLOWING_UNKNOWN, + Allow, + "`#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]`", + report_in_external_macro: true +} + +declare_lint_pass!(DeriveDeserializeAllowingUnknown => [DERIVE_DESERIALIZE_ALLOWING_UNKNOWN]); + +impl<'tcx> LateLintPass<'tcx> for DeriveDeserializeAllowingUnknown { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + // Is this an `impl` (of a certain form)? + let ItemKind::Impl(Impl { + of_trait: + Some(TraitRef { + path: + Path { + res: Res::Def(_, trait_def_id), + .. + }, + .. + }), + self_ty: + Ty { + kind: + TyKind::Path(QPath::Resolved( + None, + Path { + res: Res::Def(_, self_ty_def_id), + .. + }, + )), + .. + }, + .. + }) = item.kind + else { + return; + }; + + // Is it an `impl` of the trait `serde::Deserialize`? + if !paths::SERDE_DESERIALIZE.get(cx).contains(trait_def_id) { + return; + } + + // Is it derived? + if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) { + return; + } + + // Is `self_ty` local? + let Some(local_def_id) = self_ty_def_id.as_local() else { + return; + }; + + // Does `self_ty` have a variant with named fields? + if !has_variant_with_named_fields(cx.tcx, local_def_id) { + return; + } + + let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); + + // Does `self_ty` have `#[serde(deny_unknown_fields)]`? + if let Some(tokens) = find_serde_attr_item(cx.tcx, hir_id) + && tokens.iter().any(is_deny_unknown_fields_token) + { + return; + } + + span_lint( + cx, + DERIVE_DESERIALIZE_ALLOWING_UNKNOWN, + item.span, + "`#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]`", + ); + } +} + +// Determines whether `def_id` corresponds to an ADT with at least one variant with named fields. A +// variant has named fields if its `ctor` field is `None`. +fn has_variant_with_named_fields(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + let ty = tcx.type_of(def_id).skip_binder(); + + let rustc_middle::ty::Adt(adt_def, _) = ty.kind() else { + return false; + }; + + adt_def.variants().iter().any(|variant_def| variant_def.ctor.is_none()) +} + +fn find_serde_attr_item(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&TokenStream> { + tcx.hir_attrs(hir_id).iter().find_map(|attribute| { + if let Attribute::Unparsed(attr_item) = attribute + && let AttrItem { + path: AttrPath { segments, .. }, + args: AttrArgs::Delimited(DelimArgs { tokens, .. }), + style: AttrStyle::Outer, + .. + } = &**attr_item + && segments.len() == 1 + && segments[0].as_str() == "serde" + { + Some(tokens) + } else { + None + } + }) +} + +fn is_deny_unknown_fields_token(tt: &TokenTree) -> bool { + if let TokenTree::Token(token, _) = tt + && token + .ident() + .is_some_and(|(token, _)| token.as_str() == "deny_unknown_fields") + { + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints_internal/src/internal_paths.rs b/src/tools/clippy/clippy_lints_internal/src/internal_paths.rs new file mode 100644 index 0000000000000..dc1e30ab2bdd3 --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/internal_paths.rs @@ -0,0 +1,17 @@ +use clippy_utils::paths::{PathLookup, PathNS}; +use clippy_utils::{sym, type_path, value_path}; + +// Paths inside rustc +pub static EARLY_LINT_PASS: PathLookup = type_path!(rustc_lint::passes::EarlyLintPass); +pub static KW_MODULE: PathLookup = type_path!(rustc_span::symbol::kw); +pub static LINT: PathLookup = type_path!(rustc_lint_defs::Lint); +pub static SYMBOL: PathLookup = type_path!(rustc_span::symbol::Symbol); +pub static SYMBOL_AS_STR: PathLookup = value_path!(rustc_span::symbol::Symbol::as_str); +pub static SYM_MODULE: PathLookup = type_path!(rustc_span::symbol::sym); +pub static SYNTAX_CONTEXT: PathLookup = type_path!(rustc_span::hygiene::SyntaxContext); + +// Paths in clippy itself +pub static CLIPPY_SYM_MODULE: PathLookup = type_path!(clippy_utils::sym); +pub static MSRV_STACK: PathLookup = type_path!(clippy_utils::msrvs::MsrvStack); +pub static PATH_LOOKUP_NEW: PathLookup = value_path!(clippy_utils::paths::PathLookup::new); +pub static SPAN_LINT_AND_THEN: PathLookup = value_path!(clippy_utils::diagnostics::span_lint_and_then); diff --git a/src/tools/clippy/clippy_lints_internal/src/lib.rs b/src/tools/clippy/clippy_lints_internal/src/lib.rs new file mode 100644 index 0000000000000..0c94d100c4172 --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/lib.rs @@ -0,0 +1,78 @@ +#![feature(rustc_private)] +#![allow( + clippy::missing_docs_in_private_items, + clippy::must_use_candidate, + clippy::symbol_as_str, + rustc::diagnostic_outside_of_impl, + rustc::untranslatable_diagnostic +)] +#![warn( + trivial_casts, + trivial_numeric_casts, + rust_2018_idioms, + unused_lifetimes, + unused_qualifications, + rustc::internal +)] +// Disable this rustc lint for now, as it was also done in rustc +#![allow(rustc::potential_query_instability)] +// None of these lints need a version. +#![allow(clippy::missing_clippy_version_attribute)] + +extern crate rustc_ast; +extern crate rustc_attr_data_structures; +extern crate rustc_attr_parsing; +extern crate rustc_data_structures; +extern crate rustc_errors; +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_lint_defs; +extern crate rustc_middle; +extern crate rustc_session; +extern crate rustc_span; + +mod almost_standard_lint_formulation; +mod collapsible_calls; +mod derive_deserialize_allowing_unknown; +mod internal_paths; +mod lint_without_lint_pass; +mod msrv_attr_impl; +mod outer_expn_data_pass; +mod produce_ice; +mod symbols; +mod unnecessary_def_path; +mod unsorted_clippy_utils_paths; + +use rustc_lint::{Lint, LintStore}; + +static LINTS: &[&Lint] = &[ + almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION, + collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, + derive_deserialize_allowing_unknown::DERIVE_DESERIALIZE_ALLOWING_UNKNOWN, + lint_without_lint_pass::DEFAULT_LINT, + lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, + lint_without_lint_pass::LINT_WITHOUT_LINT_PASS, + lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE, + msrv_attr_impl::MISSING_MSRV_ATTR_IMPL, + outer_expn_data_pass::OUTER_EXPN_EXPN_DATA, + produce_ice::PRODUCE_ICE, + symbols::INTERNING_LITERALS, + symbols::SYMBOL_AS_STR, + unnecessary_def_path::UNNECESSARY_DEF_PATH, + unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS, +]; + +pub fn register_lints(store: &mut LintStore) { + store.register_lints(LINTS); + + store.register_early_pass(|| Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths)); + store.register_early_pass(|| Box::new(produce_ice::ProduceIce)); + store.register_late_pass(|_| Box::new(collapsible_calls::CollapsibleCalls)); + store.register_late_pass(|_| Box::new(derive_deserialize_allowing_unknown::DeriveDeserializeAllowingUnknown)); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath)); + store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass)); + store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl)); + store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new())); +} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs similarity index 86% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs rename to src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs index 89b4c48b8b116..0edeef3ab8559 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs @@ -1,6 +1,7 @@ +use crate::internal_paths; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::is_lint_allowed; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::{is_lint_allowed, match_def_path, paths}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir as hir; @@ -10,12 +11,12 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_session::impl_lint_pass; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{Span, sym}; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Ensures every lint is associated to a `LintPass`. /// @@ -37,12 +38,14 @@ declare_clippy_lint! { /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); /// // missing FORGOTTEN_LINT /// ``` - pub LINT_WITHOUT_LINT_PASS, - internal, - "declaring a lint without associating it in a LintPass" + pub clippy::LINT_WITHOUT_LINT_PASS, + Warn, + "declaring a lint without associating it in a LintPass", + report_in_external_macro: true + } -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for cases of an auto-generated lint without an updated description, /// i.e. `default lint description`. @@ -59,30 +62,32 @@ declare_clippy_lint! { /// ```rust,ignore /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } /// ``` - pub DEFAULT_LINT, - internal, - "found 'default lint description' in a lint declaration" + pub clippy::DEFAULT_LINT, + Warn, + "found 'default lint description' in a lint declaration", + report_in_external_macro: true } -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for invalid `clippy::version` attributes. /// /// Valid values are: /// * "pre 1.29.0" /// * any valid semantic version - pub INVALID_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found an invalid `clippy::version` attribute" + pub clippy::INVALID_CLIPPY_VERSION_ATTRIBUTE, + Warn, + "found an invalid `clippy::version` attribute", + report_in_external_macro: true } -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for declared clippy lints without the `clippy::version` attribute. - /// - pub MISSING_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found clippy lint without `clippy::version` attribute" + pub clippy::MISSING_CLIPPY_VERSION_ATTRIBUTE, + Warn, + "found clippy lint without `clippy::version` attribute", + report_in_external_macro: true } #[derive(Clone, Debug, Default)] @@ -100,11 +105,7 @@ impl_lint_pass!(LintWithoutLintPass => [ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) { - return; - } - - if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { + if let hir::ItemKind::Static(ident, ty, Mutability::Not, body_id) = item.kind { if is_lint_ref_type(cx, ty) { check_invalid_clippy_version_attribute(cx, item); @@ -133,10 +134,10 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { cx, DEFAULT_LINT, item.span, - format!("the lint `{}` has the default lint description", item.ident.name), + format!("the lint `{}` has the default lint description", ident.name), ); } - self.declared_lints.insert(item.ident.name, item.span); + self.declared_lints.insert(ident.name, item.span); } } } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { @@ -205,15 +206,13 @@ pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { mutbl: Mutability::Not, }, ) = ty.kind + && let TyKind::Path(ref path) = inner.kind + && let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - if let TyKind::Path(ref path) = inner.kind { - if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - return match_def_path(cx, def_id, &paths::LINT); - } - } + internal_paths::LINT.matches(cx, def_id) + } else { + false } - - false } fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { @@ -247,7 +246,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<' /// This function extracts the version value of a `clippy::version` attribute if the given value has /// one pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { - let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attrs = cx.tcx.hir_attrs(item.hir_id()); attrs.iter().find_map(|attr| { if let hir::Attribute::Unparsed(attr_kind) = &attr // Identify attribute diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs similarity index 83% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs rename to src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs index 558acacb97245..441c688485236 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/src/tools/clippy/clippy_lints_internal/src/msrv_attr_impl.rs @@ -1,20 +1,19 @@ +use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::match_type; -use clippy_utils::{match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, EarlyBinder, GenericArgKind}; -use rustc_session::declare_lint_pass; +use rustc_session::{declare_lint_pass, declare_tool_lint}; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. - /// - pub MISSING_MSRV_ATTR_IMPL, - internal, - "checking if all necessary steps were taken when adding a MSRV to a lint" + pub clippy::MISSING_MSRV_ATTR_IMPL, + Warn, + "checking if all necessary steps were taken when adding a MSRV to a lint", + report_in_external_macro: true } declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); @@ -30,7 +29,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .tcx .impl_trait_ref(item.owner_id) .map(EarlyBinder::instantiate_identity) - && match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS) + && internal_paths::EARLY_LINT_PASS.matches(cx, trait_ref.def_id) && let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind() && self_ty_def.is_struct() && self_ty_def.all_fields().any(|f| { @@ -39,7 +38,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .instantiate_identity() .walk() .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) - .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV_STACK)) + .any(|t| internal_paths::MSRV_STACK.matches_ty(cx, t.expect_ty())) }) && !items.iter().any(|item| item.ident.name.as_str() == "check_attributes") { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs b/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs similarity index 78% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs rename to src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs index 326e172146130..5cdc66ae90536 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs +++ b/src/tools/clippy/clippy_lints_internal/src/outer_expn_data_pass.rs @@ -1,13 +1,13 @@ +use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::match_type; -use clippy_utils::{is_lint_allowed, method_calls, paths}; +use clippy_utils::{is_lint_allowed, method_calls, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::declare_tool_lint; use rustc_session::declare_lint_pass; -use rustc_span::symbol::Symbol; -declare_clippy_lint! { +declare_tool_lint! { /// ### What it does /// Checks for calls to `cx.outer().expn_data()` and suggests to use /// the `cx.outer_expn_data()` @@ -24,9 +24,10 @@ declare_clippy_lint! { /// ```rust,ignore /// expr.span.ctxt().outer_expn_data() /// ``` - pub OUTER_EXPN_EXPN_DATA, - internal, - "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" + pub clippy::OUTER_EXPN_EXPN_DATA, + Warn, + "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`", + report_in_external_macro: true } declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); @@ -38,12 +39,11 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { } let (method_names, arg_lists, spans) = method_calls(expr, 2); - let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); - if let ["expn_data", "outer_expn"] = method_names.as_slice() + if let [sym::expn_data, sym::outer_expn] = method_names.as_slice() && let (self_arg, args) = arg_lists[1] && args.is_empty() && let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs() - && match_type(cx, self_ty, &paths::SYNTAX_CONTEXT) + && internal_paths::SYNTAX_CONTEXT.matches_ty(cx, self_ty) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs b/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs new file mode 100644 index 0000000000000..3a813b4b9a223 --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/produce_ice.rs @@ -0,0 +1,42 @@ +use rustc_ast::ast::NodeId; +use rustc_ast::visit::FnKind; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_tool_lint! { + /// ### What it does + /// Not an actual lint. This lint is only meant for testing our customized internal compiler + /// error message by calling `panic`. + /// + /// ### Why is this bad? + /// ICE in large quantities can damage your teeth + /// + /// ### Example + /// ```rust,ignore + /// 🍦🍦🍦🍦🍦 + /// ``` + pub clippy::PRODUCE_ICE, + Warn, + "this message should not appear anywhere as we ICE before and don't emit the lint", + report_in_external_macro: true +} + +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); + +impl EarlyLintPass for ProduceIce { + fn check_fn(&mut self, ctx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + if is_trigger_fn(fn_kind) { + ctx.sess() + .dcx() + .span_delayed_bug(span, "Would you like some help with that?"); + } + } +} + +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { + match fn_kind { + FnKind::Fn(_, _, func) => func.ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", + FnKind::Closure(..) => false, + } +} diff --git a/src/tools/clippy/clippy_lints_internal/src/symbols.rs b/src/tools/clippy/clippy_lints_internal/src/symbols.rs new file mode 100644 index 0000000000000..7b5d58824c38f --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/symbols.rs @@ -0,0 +1,206 @@ +use crate::internal_paths; +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::LitKind; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Expr, ExprKind, Lit, Node, Pat, PatExprKind, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::ConstValue; +use rustc_middle::ty; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::Symbol; +use rustc_span::{Span, sym}; + +declare_tool_lint! { + /// ### What it does + /// Checks for interning string literals as symbols + /// + /// ### Why is this bad? + /// It's faster and easier to use the symbol constant. If one doesn't exist it can be added to `clippy_utils/src/sym.rs` + /// + /// ### Example + /// ```rust,ignore + /// let _ = Symbol::intern("f32"); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// let _ = sym::f32; + /// ``` + pub clippy::INTERNING_LITERALS, + Warn, + "interning a symbol that is a literal", + report_in_external_macro: true +} + +declare_tool_lint! { + /// ### What it does + /// Checks for calls to `Symbol::as_str` + /// + /// ### Why is this bad? + /// It's faster and easier to use the symbol constant. If one doesn't exist it can be added to `clippy_utils/src/sym.rs` + /// + /// ### Example + /// ```rust,ignore + /// symbol.as_str() == "foo" + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// symbol == sym::foo + /// ``` + pub clippy::SYMBOL_AS_STR, + Warn, + "calls to `Symbol::as_str`", + report_in_external_macro: true +} + +#[derive(Default)] +pub struct Symbols { + // Maps the symbol to the import path + symbol_map: FxHashMap, +} + +impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]); + +impl Symbols { + fn lit_suggestion(&self, lit: &Lit) -> Option<(Span, String)> { + if let LitKind::Str(name, _) = lit.node { + let sugg = if let Some((prefix, name)) = self.symbol_map.get(&name.as_u32()) { + format!("{prefix}::{name}") + } else { + format!("sym::{}", name.as_str().replace(|ch: char| !ch.is_alphanumeric(), "_")) + }; + Some((lit.span, sugg)) + } else { + None + } + } + + fn expr_suggestion(&self, expr: &Expr<'_>) -> Option<(Span, String)> { + if let ExprKind::Lit(lit) = expr.kind { + self.lit_suggestion(lit) + } else { + None + } + } + + fn pat_suggestions(&self, pat: &Pat<'_>, suggestions: &mut Vec<(Span, String)>) { + pat.walk_always(|pat| { + if let PatKind::Expr(pat_expr) = pat.kind + && let PatExprKind::Lit { lit, .. } = pat_expr.kind + { + suggestions.extend(self.lit_suggestion(lit)); + } + }); + } +} + +impl<'tcx> LateLintPass<'tcx> for Symbols { + fn check_crate(&mut self, cx: &LateContext<'_>) { + let modules = [ + ("kw", &internal_paths::KW_MODULE), + ("sym", &internal_paths::SYM_MODULE), + ("sym", &internal_paths::CLIPPY_SYM_MODULE), + ]; + for (prefix, module) in modules { + for def_id in module.get(cx) { + // When linting `clippy_utils` itself we can't use `module_children` as it's a local def id. It will + // still lint but the suggestion may suggest the incorrect name for symbols such as `sym::CRLF` + if def_id.is_local() { + continue; + } + + for item in cx.tcx.module_children(def_id) { + if let Res::Def(DefKind::Const, item_def_id) = item.res + && let ty = cx.tcx.type_of(item_def_id).instantiate_identity() + && internal_paths::SYMBOL.matches_ty(cx, ty) + && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id) + && let Some(value) = value.to_u32().discard_err() + { + self.symbol_map.insert(value, (prefix, item.ident.name)); + } + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Call(func, [arg]) = &expr.kind + && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() + && cx.tcx.is_diagnostic_item(sym::SymbolIntern, *def_id) + && let Some((_, sugg)) = self.expr_suggestion(arg) + { + span_lint_and_then( + cx, + INTERNING_LITERALS, + expr.span, + "interning a string literal", + |diag| { + diag.span_suggestion_verbose( + expr.span, + "use a preinterned symbol instead", + sugg, + Applicability::MaybeIncorrect, + ); + diag.help("add the symbol to `clippy_utils/src/sym.rs` if needed"); + }, + ); + } + + if let Some(as_str) = as_str_span(cx, expr) + && let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) + { + let mut suggestions = Vec::new(); + + match parent.kind { + ExprKind::Binary(_, lhs, rhs) => { + suggestions.extend(self.expr_suggestion(lhs)); + suggestions.extend(self.expr_suggestion(rhs)); + }, + ExprKind::Match(_, arms, _) => { + for arm in arms { + self.pat_suggestions(arm.pat, &mut suggestions); + } + }, + _ => {}, + } + + if suggestions.is_empty() { + return; + } + + span_lint_and_then( + cx, + SYMBOL_AS_STR, + expr.span, + "converting a Symbol to a string", + |diag| { + suggestions.push((as_str, String::new())); + diag.multipart_suggestion( + "use preinterned symbols instead", + suggestions, + Applicability::MaybeIncorrect, + ); + diag.help("add the symbols to `clippy_utils/src/sym.rs` if needed"); + }, + ); + } + } +} + +/// ```ignore +/// symbol.as_str() +/// // ^^^^^^^^ +/// ``` +fn as_str_span(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if let ExprKind::MethodCall(_, recv, [], _) = expr.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && internal_paths::SYMBOL_AS_STR.matches(cx, method_def_id) + { + Some(recv.span.shrink_to_hi().to(expr.span.shrink_to_hi())) + } else { + None + } +} diff --git a/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs new file mode 100644 index 0000000000000..8877f1faf0ee8 --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/unnecessary_def_path.rs @@ -0,0 +1,104 @@ +use crate::internal_paths; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::{PathNS, lookup_path}; +use clippy_utils::{path_def_id, peel_ref_operators}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::{declare_lint_pass, declare_tool_lint}; +use rustc_middle::mir::ConstValue; +use rustc_span::symbol::Symbol; + +declare_tool_lint! { + /// ### What it does + /// Checks for usage of def paths when a diagnostic item or a `LangItem` could be used. + /// + /// ### Why is this bad? + /// The path for an item is subject to change and is less efficient to look up than a + /// diagnostic item or a `LangItem`. + /// + /// ### Example + /// ```rust,ignore + /// pub static VEC: PathLookup = path!(alloc::vec::Vec); + /// + /// VEC.contains_ty(cx, ty) + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// is_type_diagnostic_item(cx, ty, sym::Vec) + /// ``` + pub clippy::UNNECESSARY_DEF_PATH, + Warn, + "using a def path when a diagnostic item or a `LangItem` is available", + report_in_external_macro: true +} + +declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Call(ctor, [_, path]) = expr.kind + && internal_paths::PATH_LOOKUP_NEW.matches_path(cx, ctor) + && let ExprKind::Array(segments) = peel_ref_operators(cx, path).kind + && let Some(macro_id) = expr.span.ctxt().outer_expn_data().macro_def_id + { + let ns = match cx.tcx.item_name(macro_id).as_str() { + "type_path" => PathNS::Type, + "value_path" => PathNS::Value, + "macro_path" => PathNS::Macro, + _ => unreachable!(), + }; + + let path: Vec = segments + .iter() + .map(|segment| { + if let Some(const_def_id) = path_def_id(cx, segment) + && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(const_def_id) + && let Some(value) = value.to_u32().discard_err() + { + Symbol::new(value) + } else { + panic!("failed to resolve path {:?}", expr.span); + } + }) + .collect(); + + for def_id in lookup_path(cx.tcx, ns, &path) { + if let Some(name) = cx.tcx.get_diagnostic_name(def_id) { + span_lint_and_then( + cx, + UNNECESSARY_DEF_PATH, + expr.span.source_callsite(), + format!("a diagnostic name exists for this path: sym::{name}"), + |diag| { + diag.help( + "remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead", + ); + diag.help("see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils"); + }, + ); + } else if let Some(item_name) = get_lang_item_name(cx, def_id) { + span_lint_and_then( + cx, + UNNECESSARY_DEF_PATH, + expr.span.source_callsite(), + format!("a language item exists for this path: LangItem::{item_name}"), + |diag| { + diag.help("remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead"); + diag.help("see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils"); + }, + ); + } + } + } + } +} + +fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { + if let Some((lang_item, _)) = cx.tcx.lang_items().iter().find(|(_, id)| *id == def_id) { + Some(lang_item.variant_name()) + } else { + None + } +} diff --git a/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs b/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs new file mode 100644 index 0000000000000..9ca4ae31d455c --- /dev/null +++ b/src/tools/clippy/clippy_lints_internal/src/unsorted_clippy_utils_paths.rs @@ -0,0 +1,54 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::sym; +use rustc_ast::ast::{Crate, ItemKind, ModKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_tool_lint! { + /// ### What it does + /// Checks that [`clippy_utils::paths`] is sorted lexically + /// + /// ### Why is this bad? + /// We like to pretend we're an example of tidy code. + /// + /// ### Example + /// Wrong ordering of the util::paths constants. + pub clippy::UNSORTED_CLIPPY_UTILS_PATHS, + Warn, + "various things that will negatively affect your clippy experience", + report_in_external_macro: true +} + +declare_lint_pass!(UnsortedClippyUtilsPaths => [UNSORTED_CLIPPY_UTILS_PATHS]); + +impl EarlyLintPass for UnsortedClippyUtilsPaths { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + if let Some(utils) = krate + .items + .iter() + .find(|item| item.kind.ident().is_some_and(|i| i.name == sym::utils)) + && let ItemKind::Mod(_, _, ModKind::Loaded(ref items, ..)) = utils.kind + && let Some(paths) = items + .iter() + .find(|item| item.kind.ident().is_some_and(|i| i.name == sym::paths)) + && let ItemKind::Mod(_, _, ModKind::Loaded(ref items, ..)) = paths.kind + { + let mut last_name: Option = None; + for item in items { + let name = item.kind.ident().expect("const items have idents").to_string(); + if let Some(last_name) = last_name + && *last_name > *name + { + span_lint( + cx, + UNSORTED_CLIPPY_UTILS_PATHS, + item.span, + "this constant should be before the previous constant due to lexical \ + ordering", + ); + } + last_name = Some(name); + } + } + } +} diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index ba4bb1d177c57..615c0995e8b17 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy_utils" -# begin autogenerated version -version = "0.1.87" -# end autogenerated version +version = "0.1.89" edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 5dd31b52f8800..c9083f654c4cb 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-02-27 +nightly-2025-05-21 ``` @@ -30,7 +30,7 @@ Function signatures can change or be removed without replacement without any pri -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 <[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index ab5f97199ce33..8996b694ed8f7 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -33,6 +33,7 @@ pub fn eq_id(l: Ident, r: Ident) -> bool { pub fn eq_pat(l: &Pat, r: &Pat) -> bool { use PatKind::*; match (&l.kind, &r.kind) { + (Missing, _) | (_, Missing) => unreachable!(), (Paren(l), _) => eq_pat(l, r), (_, Paren(r)) => eq_pat(l, r), (Wild, Wild) | (Rest, Rest) => true, @@ -201,7 +202,8 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lt, rt), (Block(lb, ll), Block(rb, rl)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lb, rb), (TryBlock(l), TryBlock(r)) => eq_block(l, r), - (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l.as_ref(), r.as_ref()), + (Yield(l), Yield(r)) => eq_expr_opt(l.expr(), r.expr()) && l.same_kind(r), + (Ret(l), Ret(r)) => eq_expr_opt(l.as_ref(), r.as_ref()), (Break(ll, le), Break(rl, re)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_expr_opt(le.as_ref(), re.as_ref()), (Continue(ll), Continue(rl)) => eq_label(ll.as_ref(), rl.as_ref()), (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2, _), Index(r1, r2, _)) => { @@ -320,67 +322,87 @@ pub fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool { } pub fn eq_item(l: &Item, r: &Item, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool { - eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) + over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) } #[expect(clippy::similar_names, clippy::too_many_lines)] // Just a big match statement pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { use ItemKind::*; match (l, r) { - (ExternCrate(l), ExternCrate(r)) => l == r, + (ExternCrate(ls, li), ExternCrate(rs, ri)) => ls == rs && eq_id(*li, *ri), (Use(l), Use(r)) => eq_use_tree(l, r), ( Static(box StaticItem { + ident: li, ty: lt, mutability: lm, expr: le, safety: ls, + define_opaque: _, }), Static(box StaticItem { + ident: ri, ty: rt, mutability: rm, expr: re, safety: rs, + define_opaque: _, }), - ) => lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), + ) => eq_id(*li, *ri) && lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), ( Const(box ConstItem { defaultness: ld, + ident: li, generics: lg, ty: lt, expr: le, + define_opaque: _, }), Const(box ConstItem { defaultness: rd, + ident: ri, generics: rg, ty: rt, expr: re, + define_opaque: _, }), - ) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), + ) => { + eq_defaultness(*ld, *rd) + && eq_id(*li, *ri) + && eq_generics(lg, rg) + && eq_ty(lt, rt) + && eq_expr_opt(le.as_ref(), re.as_ref()) + }, ( Fn(box ast::Fn { defaultness: ld, sig: lf, + ident: li, generics: lg, contract: lc, body: lb, + define_opaque: _, }), Fn(box ast::Fn { defaultness: rd, sig: rf, + ident: ri, generics: rg, contract: rc, body: rb, + define_opaque: _, }), ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) + && eq_id(*li, *ri) && eq_generics(lg, rg) && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) }, - (Mod(lu, lmk), Mod(ru, rmk)) => { - lu == ru + (Mod(ls, li, lmk), Mod(rs, ri, rmk)) => { + ls == rs + && eq_id(*li, *ri) && match (lmk, rmk) { (ModKind::Loaded(litems, linline, _, _), ModKind::Loaded(ritems, rinline, _, _)) => { linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind)) @@ -414,33 +436,40 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) }, - (Enum(le, lg), Enum(re, rg)) => over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg), - (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => { - eq_variant_data(lv, rv) && eq_generics(lg, rg) + (Enum(li, le, lg), Enum(ri, re, rg)) => { + eq_id(*li, *ri) && over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg) + }, + (Struct(li, lv, lg), Struct(ri, rv, rg)) | (Union(li, lv, lg), Union(ri, rv, rg)) => { + eq_id(*li, *ri) && eq_variant_data(lv, rv) && eq_generics(lg, rg) }, ( Trait(box ast::Trait { is_auto: la, safety: lu, + ident: li, generics: lg, bounds: lb, - items: li, + items: lis, }), Trait(box ast::Trait { is_auto: ra, safety: ru, + ident: ri, generics: rg, bounds: rb, - items: ri, + items: ris, }), ) => { la == ra && matches!(lu, Safety::Default) == matches!(ru, Safety::Default) + && eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) - && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind)) + && over(lis, ris, |l, r| eq_item(l, r, eq_assoc_item_kind)) + }, + (TraitAlias(li, lg, lb), TraitAlias(ri, rg, rb)) => { + eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) }, - (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, eq_generic_bound), ( Impl(box ast::Impl { safety: lu, @@ -473,7 +502,9 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind)) }, (MacCall(l), MacCall(r)) => eq_mac_call(l, r), - (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_delim_args(&l.body, &r.body), + (MacroDef(li, ld), MacroDef(ri, rd)) => { + eq_id(*li, *ri) && ld.macro_rules == rd.macro_rules && eq_delim_args(&ld.body, &rd.body) + }, _ => false, } } @@ -483,36 +514,45 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { match (l, r) { ( Static(box StaticItem { + ident: li, ty: lt, mutability: lm, expr: le, safety: ls, + define_opaque: _, }), Static(box StaticItem { + ident: ri, ty: rt, mutability: rm, expr: re, safety: rs, + define_opaque: _, }), - ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()) && ls == rs, + ) => eq_id(*li, *ri) && eq_ty(lt, rt) && lm == rm && eq_expr_opt(le.as_ref(), re.as_ref()) && ls == rs, ( Fn(box ast::Fn { defaultness: ld, sig: lf, + ident: li, generics: lg, contract: lc, body: lb, + define_opaque: _, }), Fn(box ast::Fn { defaultness: rd, sig: rf, + ident: ri, generics: rg, contract: rc, body: rb, + define_opaque: _, }), ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) + && eq_id(*li, *ri) && eq_generics(lg, rg) && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) @@ -520,20 +560,23 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { ( TyAlias(box ast::TyAlias { defaultness: ld, + ident: li, generics: lg, + where_clauses: _, bounds: lb, ty: lt, - .. }), TyAlias(box ast::TyAlias { defaultness: rd, + ident: ri, generics: rg, + where_clauses: _, bounds: rb, ty: rt, - .. }), ) => { eq_defaultness(*ld, *rd) + && eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) @@ -549,35 +592,50 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { ( Const(box ConstItem { defaultness: ld, + ident: li, generics: lg, ty: lt, expr: le, + define_opaque: _, }), Const(box ConstItem { defaultness: rd, + ident: ri, generics: rg, ty: rt, expr: re, + define_opaque: _, }), - ) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()), + ) => { + eq_defaultness(*ld, *rd) + && eq_id(*li, *ri) + && eq_generics(lg, rg) + && eq_ty(lt, rt) + && eq_expr_opt(le.as_ref(), re.as_ref()) + }, ( Fn(box ast::Fn { defaultness: ld, sig: lf, + ident: li, generics: lg, contract: lc, body: lb, + define_opaque: _, }), Fn(box ast::Fn { defaultness: rd, sig: rf, + ident: ri, generics: rg, contract: rc, body: rb, + define_opaque: _, }), ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) + && eq_id(*li, *ri) && eq_generics(lg, rg) && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) @@ -585,20 +643,23 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { ( Type(box TyAlias { defaultness: ld, + ident: li, generics: lg, + where_clauses: _, bounds: lb, ty: lt, - .. }), Type(box TyAlias { defaultness: rd, + ident: ri, generics: rg, + where_clauses: _, bounds: rb, ty: rt, - .. }), ) => { eq_defaultness(*ld, *rd) + && eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) @@ -682,19 +743,20 @@ pub fn eq_generics(l: &Generics, r: &Generics) -> bool { pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool { use WherePredicateKind::*; - match (&l.kind, &r.kind) { - (BoundPredicate(l), BoundPredicate(r)) => { - over(&l.bound_generic_params, &r.bound_generic_params, |l, r| { - eq_generic_param(l, r) - }) && eq_ty(&l.bounded_ty, &r.bounded_ty) - && over(&l.bounds, &r.bounds, eq_generic_bound) - }, - (RegionPredicate(l), RegionPredicate(r)) => { - eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, eq_generic_bound) - }, - (EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty), - _ => false, - } + over(&l.attrs, &r.attrs, eq_attr) + && match (&l.kind, &r.kind) { + (BoundPredicate(l), BoundPredicate(r)) => { + over(&l.bound_generic_params, &r.bound_generic_params, |l, r| { + eq_generic_param(l, r) + }) && eq_ty(&l.bounded_ty, &r.bounded_ty) + && over(&l.bounds, &r.bounds, eq_generic_bound) + }, + (RegionPredicate(l), RegionPredicate(r)) => { + eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, eq_generic_bound) + }, + (EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty), + _ => false, + } } pub fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool { diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 09de5c055379f..8a0ff5323c98d 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -5,11 +5,11 @@ use rustc_lexer::TokenKind; use rustc_lint::LateContext; use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; use std::str::FromStr; use crate::source::SpanRangeExt; -use crate::tokenize_with_text; +use crate::{sym, tokenize_with_text}; /// Deprecation status of attributes known by Clippy. pub enum DeprecationStatus { @@ -21,17 +21,17 @@ pub enum DeprecationStatus { } #[rustfmt::skip] -pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[ - ("author", DeprecationStatus::None), - ("version", DeprecationStatus::None), - ("cognitive_complexity", DeprecationStatus::None), - ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")), - ("dump", DeprecationStatus::None), - ("msrv", DeprecationStatus::None), +pub const BUILTIN_ATTRIBUTES: &[(Symbol, DeprecationStatus)] = &[ + (sym::author, DeprecationStatus::None), + (sym::version, DeprecationStatus::None), + (sym::cognitive_complexity, DeprecationStatus::None), + (sym::cyclomatic_complexity, DeprecationStatus::Replaced("cognitive_complexity")), + (sym::dump, DeprecationStatus::None), + (sym::msrv, DeprecationStatus::None), // The following attributes are for the 3rd party crate authors. // See book/src/attribs.md - ("has_significant_drop", DeprecationStatus::None), - ("format_args", DeprecationStatus::None), + (sym::has_significant_drop, DeprecationStatus::None), + (sym::format_args, DeprecationStatus::None), ]; pub struct LimitStack { @@ -52,11 +52,11 @@ impl LimitStack { pub fn limit(&self) -> u64 { *self.stack.last().expect("there should always be a value in the stack") } - pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: &'static str) { + pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) { let stack = &mut self.stack; parse_attrs(sess, attrs, name, |val| stack.push(val)); } - pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: &'static str) { + pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) { let stack = &mut self.stack; parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val))); } @@ -65,7 +65,7 @@ impl LimitStack { pub fn get_attr<'a, A: AttributeExt + 'a>( sess: &'a Session, attrs: &'a [A], - name: &'static str, + name: Symbol, ) -> impl Iterator { attrs.iter().filter(move |attr| { let Some(attr_segments) = attr.ident_path() else { @@ -75,8 +75,8 @@ pub fn get_attr<'a, A: AttributeExt + 'a>( if attr_segments.len() == 2 && attr_segments[0].name == sym::clippy { BUILTIN_ATTRIBUTES .iter() - .find_map(|&(builtin_name, ref deprecation_status)| { - if attr_segments[1].name.as_str() == builtin_name { + .find_map(|(builtin_name, deprecation_status)| { + if attr_segments[1].name == *builtin_name { Some(deprecation_status) } else { None @@ -108,7 +108,7 @@ pub fn get_attr<'a, A: AttributeExt + 'a>( }, DeprecationStatus::None => { diag.cancel(); - attr_segments[1].as_str() == name + attr_segments[1].name == name }, } }, @@ -119,9 +119,9 @@ pub fn get_attr<'a, A: AttributeExt + 'a>( }) } -fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: &'static str, mut f: F) { +fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: Symbol, mut f: F) { for attr in get_attr(sess, attrs, name) { - if let Some(ref value) = attr.value_str() { + if let Some(value) = attr.value_str() { if let Ok(value) = FromStr::from_str(value.as_str()) { f(value); } else { @@ -133,7 +133,7 @@ fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: } } -pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: &'static str) -> Option<&'a A> { +pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: Symbol) -> Option<&'a A> { let mut unique_attr: Option<&A> = None; for attr in get_attr(sess, attrs, name) { if let Some(duplicate) = unique_attr { diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 4f48fb3b8a969..004c840c3310b 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -242,14 +242,14 @@ fn fn_header_search_pat(header: FnHeader) -> Pat { fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) { let (start_pat, end_pat) = match &item.kind { - ItemKind::ExternCrate(_) => (Pat::Str("extern"), Pat::Str(";")), + ItemKind::ExternCrate(..) => (Pat::Str("extern"), Pat::Str(";")), ItemKind::Static(..) => (Pat::Str("static"), Pat::Str(";")), ItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")), ItemKind::Fn { sig, .. } => (fn_header_search_pat(sig.header), Pat::Str("")), ItemKind::ForeignMod { .. } => (Pat::Str("extern"), Pat::Str("}")), ItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")), ItemKind::Enum(..) => (Pat::Str("enum"), Pat::Str("}")), - ItemKind::Struct(VariantData::Struct { .. }, _) => (Pat::Str("struct"), Pat::Str("}")), + ItemKind::Struct(_, VariantData::Struct { .. }, _) => (Pat::Str("struct"), Pat::Str("}")), ItemKind::Struct(..) => (Pat::Str("struct"), Pat::Str(";")), ItemKind::Union(..) => (Pat::Str("union"), Pat::Str("}")), ItemKind::Trait(_, Safety::Unsafe, ..) diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index dd149c4a29b9f..6f5b0ec54cd85 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -15,7 +15,7 @@ use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, + BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, }; use rustc_lexer::tokenize; use rustc_lint::LateContext; @@ -43,14 +43,16 @@ pub enum Constant<'tcx> { Char(char), /// An integer's bit representation. Int(u128), - /// An `f16`. - F16(f16), + /// An `f16` bitcast to a `u16`. + // FIXME(f16_f128): use `f16` once builtins are available on all host tools platforms. + F16(u16), /// An `f32`. F32(f32), /// An `f64`. F64(f64), - /// An `f128`. - F128(f128), + /// An `f128` bitcast to a `u128`. + // FIXME(f16_f128): use `f128` once builtins are available on all host tools platforms. + F128(u128), /// `true` or `false`. Bool(bool), /// An array of constants. @@ -177,7 +179,7 @@ impl Hash for Constant<'_> { }, Self::F16(f) => { // FIXME(f16_f128): once conversions to/from `f128` are available on all platforms, - f.to_bits().hash(state); + f.hash(state); }, Self::F32(f) => { f64::from(f).to_bits().hash(state); @@ -186,7 +188,7 @@ impl Hash for Constant<'_> { f.to_bits().hash(state); }, Self::F128(f) => { - f.to_bits().hash(state); + f.hash(state); }, Self::Bool(b) => { b.hash(state); @@ -292,12 +294,12 @@ impl Constant<'_> { fn parse_f16(s: &str) -> Self { let f: Half = s.parse().unwrap(); - Self::F16(f16::from_bits(f.to_bits().try_into().unwrap())) + Self::F16(f.to_bits().try_into().unwrap()) } fn parse_f128(s: &str) -> Self { let f: Quad = s.parse().unwrap(); - Self::F128(f128::from_bits(f.to_bits())) + Self::F128(f.to_bits()) } } @@ -485,7 +487,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id), ExprKind::Block(block, _) => self.block(block), ExprKind::Lit(lit) => { - if is_direct_expn_of(e.span, "cfg").is_some() { + if is_direct_expn_of(e.span, sym::cfg).is_some() { None } else { Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) @@ -506,7 +508,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), - ExprKind::Binary(op, left, right) => self.binop(op, left, right), + ExprKind::Binary(op, left, right) => self.binop(op.node, left, right), ExprKind::Call(callee, []) => { // We only handle a few const functions for now. if let ExprKind::Path(qpath) = &callee.kind @@ -563,7 +565,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { }) }, ExprKind::Lit(lit) => { - if is_direct_expn_of(e.span, "cfg").is_some() { + if is_direct_expn_of(e.span, sym::cfg).is_some() { None } else { match &lit.node { @@ -652,7 +654,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { span, .. }) = self.tcx.hir_node(body_id.hir_id) - && is_direct_expn_of(*span, "cfg").is_some() + && is_direct_expn_of(*span, sym::cfg).is_some() { return None; } @@ -744,7 +746,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn binop(&self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option> { + fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option> { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { @@ -757,7 +759,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { // Using / or %, where the left-hand argument is the smallest integer of a signed integer type and // the right-hand argument is -1 always panics, even with overflow-checks disabled - if let BinOpKind::Div | BinOpKind::Rem = op.node + if let BinOpKind::Div | BinOpKind::Rem = op && l == ty_min_value && r == -1 { @@ -765,7 +767,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity)); - match op.node { + match op { // When +, * or binary - create a value greater than the maximum value, or less than // the minimum value that can be stored, it panics. BinOpKind::Add => l.checked_add(r).and_then(|n| ity.ensure_fits(n)).map(zext), @@ -792,7 +794,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { ty::Uint(ity) => { let bits = ity.bits(); - match op.node { + match op { BinOpKind::Add => l.checked_add(r).and_then(|n| ity.ensure_fits(n)).map(Constant::Int), BinOpKind::Sub => l.checked_sub(r).and_then(|n| ity.ensure_fits(n)).map(Constant::Int), BinOpKind::Mul => l.checked_mul(r).and_then(|n| ity.ensure_fits(n)).map(Constant::Int), @@ -815,7 +817,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { _ => None, }, // FIXME(f16_f128): add these types when binary operations are available on all platforms - (Constant::F32(l), Some(Constant::F32(r))) => match op.node { + (Constant::F32(l), Some(Constant::F32(r))) => match op { BinOpKind::Add => Some(Constant::F32(l + r)), BinOpKind::Sub => Some(Constant::F32(l - r)), BinOpKind::Mul => Some(Constant::F32(l * r)), @@ -829,7 +831,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { BinOpKind::Gt => Some(Constant::Bool(l > r)), _ => None, }, - (Constant::F64(l), Some(Constant::F64(r))) => match op.node { + (Constant::F64(l), Some(Constant::F64(r))) => match op { BinOpKind::Add => Some(Constant::F64(l + r)), BinOpKind::Sub => Some(Constant::F64(l - r)), BinOpKind::Mul => Some(Constant::F64(l * r)), @@ -843,7 +845,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { BinOpKind::Gt => Some(Constant::Bool(l > r)), _ => None, }, - (l, r) => match (op.node, l, r) { + (l, r) => match (op, l, r) { (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)), (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)), (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => { @@ -868,10 +870,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))), - ty::Float(FloatTy::F16) => Some(Constant::F16(f16::from_bits(int.into()))), + ty::Float(FloatTy::F16) => Some(Constant::F16(int.into())), ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(int.into()))), ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(int.into()))), - ty::Float(FloatTy::F128) => Some(Constant::F128(f128::from_bits(int.into()))), + ty::Float(FloatTy::F128) => Some(Constant::F128(int.into())), ty::RawPtr(_, _) => Some(Constant::RawPtr(int.to_bits(int.size()))), _ => None, }, @@ -892,10 +894,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option let range = alloc_range(offset + size * idx, size); let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?; res.push(match flt { - FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().discard_err()?)), + FloatTy::F16 => Constant::F16(val.to_u16().discard_err()?), FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().discard_err()?)), FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().discard_err()?)), - FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().discard_err()?)), + FloatTy::F128 => Constant::F128(val.to_u128().discard_err()?), }); } Some(Constant::Vec(res)) diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index ddb7a6635e063..cd2098a89891d 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -17,16 +17,16 @@ use rustc_span::Span; use std::env; fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) { - if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { - if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { - diag.help(format!( - "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", - &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { - // extract just major + minor version and ignore patch versions - format!("rust-{}", n.rsplit_once('.').unwrap().1) - }) - )); - } + if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() + && let Some(lint) = lint.name_lower().strip_prefix("clippy::") + { + diag.help(format!( + "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", + &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { + // extract just major + minor version and ignore patch versions + format!("rust-{}", n.rsplit_once('.').unwrap().1) + }) + )); } } @@ -85,7 +85,7 @@ fn validate_diag(diag: &Diag<'_, impl EmissionGuarantee>) { /// This is needed for `#[allow]` and `#[expect]` attributes to work on the node /// highlighted in the displayed warning. /// -/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir`] instead. /// @@ -128,7 +128,7 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( /// This is needed for `#[allow]` and `#[expect]` attributes to work on the node /// highlighted in the displayed warning. /// -/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. /// @@ -241,7 +241,7 @@ pub fn span_lint_and_note( /// This is needed for `#[allow]` and `#[expect]` attributes to work on the node /// highlighted in the displayed warning. /// -/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. pub fn span_lint_and_then(cx: &C, lint: &'static Lint, sp: S, msg: M, f: F) @@ -358,7 +358,7 @@ pub fn span_lint_hir_and_then( /// This is needed for `#[allow]` and `#[expect]` attributes to work on the node /// highlighted in the displayed warning. /// -/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. /// diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index aaea8d71efbef..9d38672efada9 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -10,6 +10,7 @@ //! - option-if-let-else use crate::consts::{ConstEvalCtxt, FullInt}; +use crate::sym; use crate::ty::{all_predicates_of, is_copy}; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; @@ -19,7 +20,7 @@ use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use std::{cmp, ops}; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -49,14 +50,13 @@ impl ops::BitOrAssign for EagernessSuggestion { /// Determine the eagerness of the given function call. fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion { use EagernessSuggestion::{Eager, Lazy, NoChange}; - let name = name.as_str(); let ty = match cx.tcx.impl_of_method(fn_id) { Some(id) => cx.tcx.type_of(id).instantiate_identity(), None => return Lazy, }; - if (name.starts_with("as_") || name == "len" || name == "is_empty") && have_one_arg { + if (matches!(name, sym::is_empty | sym::len) || name.as_str().starts_with("as_")) && have_one_arg { if matches!( cx.tcx.crate_name(fn_id.krate), sym::std | sym::core | sym::alloc | sym::proc_macro @@ -291,6 +291,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS ExprKind::ConstBlock(_) | ExprKind::Array(_) | ExprKind::Tup(_) + | ExprKind::Use(..) | ExprKind::Lit(_) | ExprKind::Cast(..) | ExprKind::Type(..) diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index c4d00002292c9..6971b488013c4 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -3,14 +3,14 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::consts::{ConstEvalCtxt, Constant}; -use crate::is_expn_of; use crate::ty::is_type_diagnostic_item; +use crate::{is_expn_of, sym}; use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StructTailExpr}; use rustc_lint::LateContext; -use rustc_span::{Span, sym, symbol}; +use rustc_span::{Span, symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Returns `(pat, arg, body, span)`. @@ -118,18 +118,17 @@ impl<'hir> IfLet<'hir> { ) = expr.kind { let mut iter = cx.tcx.hir_parent_iter(expr.hir_id); - if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() { - if let Some(( + if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() + && let Some(( _, Node::Expr(Expr { kind: ExprKind::Loop(_, _, LoopSource::While, _), .. }), )) = iter.next() - { - // while loop desugar - return None; - } + { + // while loop desugar + return None; } return Some(Self { let_pat, @@ -176,6 +175,12 @@ impl<'hir> IfLetOrMatch<'hir> { ), } } + + pub fn scrutinee(&self) -> &'hir Expr<'hir> { + match self { + Self::Match(scrutinee, _, _) | Self::IfLet(scrutinee, _, _, _, _) => scrutinee, + } + } } /// An `if` or `if let` expression @@ -294,7 +299,7 @@ impl<'a> VecArgs<'a> { pub fn hir(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option> { if let ExprKind::Call(fun, args) = expr.kind && let ExprKind::Path(ref qpath) = fun.kind - && is_expn_of(fun.span, "vec").is_some() + && is_expn_of(fun.span, sym::vec).is_some() && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() { return if cx.tcx.is_diagnostic_item(sym::vec_from_elem, fun_def_id) && args.len() == 2 { @@ -469,7 +474,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::New); } else if name.ident.name == symbol::kw::Default { return Some(VecInitKind::Default); - } else if name.ident.name.as_str() == "with_capacity" { + } else if name.ident.name == sym::with_capacity { let arg = args.first()?; return match ConstEvalCtxt::new(cx).eval_simple(arg) { Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 4bd86a2533518..c37231d093129 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -8,7 +8,7 @@ use rustc_hir::MatchSource::TryDesugar; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, - ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, + ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeKind, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind, }; @@ -148,7 +148,7 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> { impl HirEqInterExpr<'_, '_, '_> { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { - (&StmtKind::Let(l), &StmtKind::Let(r)) => { + (StmtKind::Let(l), StmtKind::Let(r)) => { // This additional check ensures that the type of the locals are equivalent even if the init // expression or type have some inferred parts. if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { @@ -166,7 +166,7 @@ impl HirEqInterExpr<'_, '_, '_> { && both(l.els.as_ref(), r.els.as_ref(), |l, r| self.eq_block(l, r)) && self.eq_pat(l.pat, r.pat) }, - (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r), + (StmtKind::Expr(l), StmtKind::Expr(r)) | (StmtKind::Semi(l), StmtKind::Semi(r)) => self.eq_expr(l, r), _ => false, } } @@ -260,7 +260,7 @@ impl HirEqInterExpr<'_, '_, '_> { fn should_ignore(&mut self, expr: &Expr<'_>) -> bool { macro_backtrace(expr.span).last().is_some_and(|macro_call| { matches!( - &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id), + self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::todo_macro | sym::unimplemented_macro) ) }) @@ -301,58 +301,58 @@ impl HirEqInterExpr<'_, '_, '_> { reduce_exprkind(self.inner.cx, &left.kind), reduce_exprkind(self.inner.cx, &right.kind), ) { - (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => { + (ExprKind::AddrOf(lb, l_mut, le), ExprKind::AddrOf(rb, r_mut, re)) => { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, - (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r), - (&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => { + (ExprKind::Array(l), ExprKind::Array(r)) => self.eq_exprs(l, r), + (ExprKind::Assign(ll, lr, _), ExprKind::Assign(rl, rr, _)) => { self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, - (&ExprKind::AssignOp(ref lo, ll, lr), &ExprKind::AssignOp(ref ro, rl, rr)) => { + (ExprKind::AssignOp(lo, ll, lr), ExprKind::AssignOp(ro, rl, rr)) => { self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, - (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r), - (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => { + (ExprKind::Block(l, _), ExprKind::Block(r, _)) => self.eq_block(l, r), + (ExprKind::Binary(l_op, ll, lr), ExprKind::Binary(r_op, rl, rr)) => { l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) || swap_binop(l_op.node, ll, lr).is_some_and(|(l_op, ll, lr)| { l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }) }, - (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => { + (ExprKind::Break(li, le), ExprKind::Break(ri, re)) => { both(li.label.as_ref(), ri.label.as_ref(), |l, r| l.ident.name == r.ident.name) && both(le.as_ref(), re.as_ref(), |l, r| self.eq_expr(l, r)) }, - (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { + (ExprKind::Call(l_fun, l_args), ExprKind::Call(r_fun, r_args)) => { self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) }, - (&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) => { + (ExprKind::Cast(lx, lt), ExprKind::Cast(rx, rt)) => { self.eq_expr(lx, rx) && self.eq_ty(lt, rt) }, - (&ExprKind::Closure(_l), &ExprKind::Closure(_r)) => false, - (&ExprKind::ConstBlock(lb), &ExprKind::ConstBlock(rb)) => self.eq_body(lb.body, rb.body), - (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => { + (ExprKind::Closure(_l), ExprKind::Closure(_r)) => false, + (ExprKind::ConstBlock(lb), ExprKind::ConstBlock(rb)) => self.eq_body(lb.body, rb.body), + (ExprKind::Continue(li), ExprKind::Continue(ri)) => { both(li.label.as_ref(), ri.label.as_ref(), |l, r| l.ident.name == r.ident.name) }, - (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re), - (&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => { + (ExprKind::DropTemps(le), ExprKind::DropTemps(re)) => self.eq_expr(le, re), + (ExprKind::Field(l_f_exp, l_f_ident), ExprKind::Field(r_f_exp, r_f_ident)) => { l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp) }, - (&ExprKind::Index(la, li, _), &ExprKind::Index(ra, ri, _)) => self.eq_expr(la, ra) && self.eq_expr(li, ri), - (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => { + (ExprKind::Index(la, li, _), ExprKind::Index(ra, ri, _)) => self.eq_expr(la, ra) && self.eq_expr(li, ri), + (ExprKind::If(lc, lt, le), ExprKind::If(rc, rt, re)) => { self.eq_expr(lc, rc) && self.eq_expr(lt, rt) && both(le.as_ref(), re.as_ref(), |l, r| self.eq_expr(l, r)) }, - (&ExprKind::Let(l), &ExprKind::Let(r)) => { + (ExprKind::Let(l), ExprKind::Let(r)) => { self.eq_pat(l.pat, r.pat) && both(l.ty.as_ref(), r.ty.as_ref(), |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) }, (ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node, - (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => { + (ExprKind::Loop(lb, ll, lls, _), ExprKind::Loop(rb, rl, rls, _)) => { lls == rls && self.eq_block(lb, rb) && both(ll.as_ref(), rl.as_ref(), |l, r| l.ident.name == r.ident.name) }, - (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => { + (ExprKind::Match(le, la, ls), ExprKind::Match(re, ra, rs)) => { (ls == rs || (matches!((ls, rs), (TryDesugar(_), TryDesugar(_))))) && self.eq_expr(le, re) && over(la, ra, |l, r| { @@ -362,27 +362,27 @@ impl HirEqInterExpr<'_, '_, '_> { }) }, ( - &ExprKind::MethodCall(l_path, l_receiver, l_args, _), - &ExprKind::MethodCall(r_path, r_receiver, r_args, _), + ExprKind::MethodCall(l_path, l_receiver, l_args, _), + ExprKind::MethodCall(r_path, r_receiver, r_args, _), ) => { self.inner.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_expr(l_receiver, r_receiver) && self.eq_exprs(l_args, r_args) }, - (&ExprKind::UnsafeBinderCast(lkind, le, None), &ExprKind::UnsafeBinderCast(rkind, re, None)) => + (ExprKind::UnsafeBinderCast(lkind, le, None), ExprKind::UnsafeBinderCast(rkind, re, None)) => lkind == rkind && self.eq_expr(le, re), - (&ExprKind::UnsafeBinderCast(lkind, le, Some(lt)), &ExprKind::UnsafeBinderCast(rkind, re, Some(rt))) => + (ExprKind::UnsafeBinderCast(lkind, le, Some(lt)), ExprKind::UnsafeBinderCast(rkind, re, Some(rt))) => lkind == rkind && self.eq_expr(le, re) && self.eq_ty(lt, rt), - (&ExprKind::OffsetOf(l_container, l_fields), &ExprKind::OffsetOf(r_container, r_fields)) => { + (ExprKind::OffsetOf(l_container, l_fields), ExprKind::OffsetOf(r_container, r_fields)) => { self.eq_ty(l_container, r_container) && over(l_fields, r_fields, |l, r| l.name == r.name) }, (ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r), - (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => { + (ExprKind::Repeat(le, ll), ExprKind::Repeat(re, rl)) => { self.eq_expr(le, re) && self.eq_const_arg(ll, rl) }, (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l.as_ref(), r.as_ref(), |l, r| self.eq_expr(l, r)), - (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => { + (ExprKind::Struct(l_path, lf, lo), ExprKind::Struct(r_path, rf, ro)) => { self.eq_qpath(l_path, r_path) && match (lo, ro) { (StructTailExpr::Base(l),StructTailExpr::Base(r)) => self.eq_expr(l, r), @@ -392,56 +392,58 @@ impl HirEqInterExpr<'_, '_, '_> { } && over(lf, rf, |l, r| self.eq_expr_field(l, r)) }, - (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup), - (&ExprKind::Type(le, lt), &ExprKind::Type(re, rt)) => self.eq_expr(le, re) && self.eq_ty(lt, rt), - (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re), - (&ExprKind::Yield(le, _), &ExprKind::Yield(re, _)) => return self.eq_expr(le, re), + (ExprKind::Tup(l_tup), ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup), + (ExprKind::Use(l_expr, _), ExprKind::Use(r_expr, _)) => self.eq_expr(l_expr, r_expr), + (ExprKind::Type(le, lt), ExprKind::Type(re, rt)) => self.eq_expr(le, re) && self.eq_ty(lt, rt), + (ExprKind::Unary(l_op, le), ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re), + (ExprKind::Yield(le, _), ExprKind::Yield(re, _)) => return self.eq_expr(le, re), ( // Else branches for branches above, grouped as per `match_same_arms`. - | &ExprKind::AddrOf(..) - | &ExprKind::Array(..) - | &ExprKind::Assign(..) - | &ExprKind::AssignOp(..) - | &ExprKind::Binary(..) - | &ExprKind::Become(..) - | &ExprKind::Block(..) - | &ExprKind::Break(..) - | &ExprKind::Call(..) - | &ExprKind::Cast(..) - | &ExprKind::ConstBlock(..) - | &ExprKind::Continue(..) - | &ExprKind::DropTemps(..) - | &ExprKind::Field(..) - | &ExprKind::Index(..) - | &ExprKind::If(..) - | &ExprKind::Let(..) - | &ExprKind::Lit(..) - | &ExprKind::Loop(..) - | &ExprKind::Match(..) - | &ExprKind::MethodCall(..) - | &ExprKind::OffsetOf(..) - | &ExprKind::Path(..) - | &ExprKind::Repeat(..) - | &ExprKind::Ret(..) - | &ExprKind::Struct(..) - | &ExprKind::Tup(..) - | &ExprKind::Type(..) - | &ExprKind::Unary(..) - | &ExprKind::Yield(..) - | &ExprKind::UnsafeBinderCast(..) + | ExprKind::AddrOf(..) + | ExprKind::Array(..) + | ExprKind::Assign(..) + | ExprKind::AssignOp(..) + | ExprKind::Binary(..) + | ExprKind::Become(..) + | ExprKind::Block(..) + | ExprKind::Break(..) + | ExprKind::Call(..) + | ExprKind::Cast(..) + | ExprKind::ConstBlock(..) + | ExprKind::Continue(..) + | ExprKind::DropTemps(..) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::If(..) + | ExprKind::Let(..) + | ExprKind::Lit(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::MethodCall(..) + | ExprKind::OffsetOf(..) + | ExprKind::Path(..) + | ExprKind::Repeat(..) + | ExprKind::Ret(..) + | ExprKind::Struct(..) + | ExprKind::Tup(..) + | ExprKind::Use(..) + | ExprKind::Type(..) + | ExprKind::Unary(..) + | ExprKind::Yield(..) + | ExprKind::UnsafeBinderCast(..) // --- Special cases that do not have a positive branch. // `Err` represents an invalid expression, so let's never assume that // an invalid expressions is equal to anything. - | &ExprKind::Err(..) + | ExprKind::Err(..) // For the time being, we always consider that two closures are unequal. // This behavior may change in the future. - | &ExprKind::Closure(..) + | ExprKind::Closure(..) // For the time being, we always consider that two instances of InlineAsm are different. // This behavior may change in the future. - | &ExprKind::InlineAsm(_) + | ExprKind::InlineAsm(_) , _ ) => false, }; @@ -481,7 +483,7 @@ impl HirEqInterExpr<'_, '_, '_> { } fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool { - left.res == right.res + left.kind == right.kind } fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool { @@ -492,11 +494,11 @@ impl HirEqInterExpr<'_, '_, '_> { fn eq_pat_expr(&mut self, left: &PatExpr<'_>, right: &PatExpr<'_>) -> bool { match (&left.kind, &right.kind) { ( - &PatExprKind::Lit { + PatExprKind::Lit { lit: left, negated: left_neg, }, - &PatExprKind::Lit { + PatExprKind::Lit { lit: right, negated: right_neg, }, @@ -510,47 +512,47 @@ impl HirEqInterExpr<'_, '_, '_> { /// Checks whether two patterns are the same. fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { - (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r), - (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => { + (PatKind::Box(l), PatKind::Box(r)) => self.eq_pat(l, r), + (PatKind::Struct(lp, la, ..), PatKind::Struct(rp, ra, ..)) => { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r)) }, - (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => { + (PatKind::TupleStruct(lp, la, ls), PatKind::TupleStruct(rp, ra, rs)) => { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs }, - (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => { + (PatKind::Binding(lb, li, _, lp), PatKind::Binding(rb, ri, _, rp)) => { let eq = lb == rb && both(lp.as_ref(), rp.as_ref(), |l, r| self.eq_pat(l, r)); if eq { - self.locals.insert(li, ri); + self.locals.insert(*li, *ri); } eq }, - (&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r), - (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), - (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { + (PatKind::Expr(l), PatKind::Expr(r)) => self.eq_pat_expr(l, r), + (PatKind::Tuple(l, ls), PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), + (PatKind::Range(ls, le, li), PatKind::Range(rs, re, ri)) => { both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b)) && both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b)) && (li == ri) }, - (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re), - (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => { + (PatKind::Ref(le, lm), PatKind::Ref(re, rm)) => lm == rm && self.eq_pat(le, re), + (PatKind::Slice(ls, li, le), PatKind::Slice(rs, ri, re)) => { over(ls, rs, |l, r| self.eq_pat(l, r)) && over(le, re, |l, r| self.eq_pat(l, r)) && both(li.as_ref(), ri.as_ref(), |l, r| self.eq_pat(l, r)) }, - (&PatKind::Wild, &PatKind::Wild) => true, + (PatKind::Wild, PatKind::Wild) => true, _ => false, } } fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool { match (left, right) { - (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => { + (QPath::Resolved(lty, lpath), QPath::Resolved(rty, rpath)) => { both(lty.as_ref(), rty.as_ref(), |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath) }, - (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => { + (QPath::TypeRelative(lty, lseg), QPath::TypeRelative(rty, rseg)) => { self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg) }, - (&QPath::LangItem(llang_item, ..), &QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item, + (QPath::LangItem(llang_item, ..), QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item, _ => false, } } @@ -609,15 +611,15 @@ impl HirEqInterExpr<'_, '_, '_> { pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { match (&left.kind, &right.kind) { - (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), - (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_const_arg(ll, rl), + (TyKind::Slice(l_vec), TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), + (TyKind::Array(lt, ll), TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_const_arg(ll, rl), (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty), (TyKind::Ref(_, l_rmut), TyKind::Ref(_, r_rmut)) => { l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty) }, (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r), - (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), - (&TyKind::Infer(()), &TyKind::Infer(())) => true, + (TyKind::Tup(l), TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), + (TyKind::Infer(()), TyKind::Infer(())) => true, _ => false, } } @@ -851,9 +853,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { std::mem::discriminant(&e.kind).hash(&mut self.s); - match e.kind { + match &e.kind { ExprKind::AddrOf(kind, m, e) => { - std::mem::discriminant(&kind).hash(&mut self.s); + std::mem::discriminant(kind).hash(&mut self.s); m.hash(&mut self.s); self.hash_expr(e); }, @@ -869,7 +871,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(l); self.hash_expr(r); }, - ExprKind::AssignOp(ref o, l, r) => { + ExprKind::AssignOp(o, l, r) => { std::mem::discriminant(&o.node).hash(&mut self.s); self.hash_expr(l); self.hash_expr(r); @@ -885,11 +887,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(l); self.hash_expr(r); }, - ExprKind::Break(i, ref j) => { + ExprKind::Break(i, j) => { if let Some(i) = i.label { self.hash_name(i.ident.name); } - if let Some(j) = *j { + if let Some(j) = j { self.hash_expr(j); } }, @@ -901,20 +903,20 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(e); self.hash_ty(ty); }, - ExprKind::Closure(&Closure { + ExprKind::Closure(Closure { capture_clause, body, .. }) => { - std::mem::discriminant(&capture_clause).hash(&mut self.s); + std::mem::discriminant(capture_clause).hash(&mut self.s); // closures inherit TypeckResults - self.hash_expr(self.cx.tcx.hir_body(body).value); + self.hash_expr(self.cx.tcx.hir_body(*body).value); }, - ExprKind::ConstBlock(ref l_id) => { + ExprKind::ConstBlock(l_id) => { self.hash_body(l_id.body); }, ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { self.hash_expr(e); }, - ExprKind::Field(e, ref f) => { + ExprKind::Field(e, f) => { self.hash_expr(e); self.hash_name(f.name); }, @@ -989,23 +991,23 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { ExprKind::Lit(l) => { l.node.hash(&mut self.s); }, - ExprKind::Loop(b, ref i, ..) => { + ExprKind::Loop(b, i, ..) => { self.hash_block(b); - if let Some(i) = *i { + if let Some(i) = i { self.hash_name(i.ident.name); } }, - ExprKind::If(cond, then, ref else_opt) => { + ExprKind::If(cond, then, else_opt) => { self.hash_expr(cond); self.hash_expr(then); - if let Some(e) = *else_opt { + if let Some(e) = else_opt { self.hash_expr(e); } }, - ExprKind::Match(e, arms, ref s) => { + ExprKind::Match(e, arms, s) => { self.hash_expr(e); - for arm in arms { + for arm in *arms { self.hash_pat(arm.pat); if let Some(e) = arm.guard { self.hash_expr(e); @@ -1015,50 +1017,53 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { s.hash(&mut self.s); }, - ExprKind::MethodCall(path, receiver, args, ref _fn_span) => { + ExprKind::MethodCall(path, receiver, args, _fn_span) => { self.hash_name(path.ident.name); self.hash_expr(receiver); self.hash_exprs(args); }, ExprKind::OffsetOf(container, fields) => { self.hash_ty(container); - for field in fields { + for field in *fields { self.hash_name(field.name); } }, - ExprKind::Path(ref qpath) => { + ExprKind::Path(qpath) => { self.hash_qpath(qpath); }, ExprKind::Repeat(e, len) => { self.hash_expr(e); self.hash_const_arg(len); }, - ExprKind::Ret(ref e) => { - if let Some(e) = *e { + ExprKind::Ret(e) => { + if let Some(e) = e { self.hash_expr(e); } }, - ExprKind::Struct(path, fields, ref expr) => { + ExprKind::Struct(path, fields, expr) => { self.hash_qpath(path); - for f in fields { + for f in *fields { self.hash_name(f.ident.name); self.hash_expr(f.expr); } - if let StructTailExpr::Base(e) = *expr { + if let StructTailExpr::Base(e) = expr { self.hash_expr(e); } }, ExprKind::Tup(tup) => { self.hash_exprs(tup); }, + ExprKind::Use(expr, _) => { + self.hash_expr(expr); + }, ExprKind::Unary(lop, le) => { - std::mem::discriminant(&lop).hash(&mut self.s); + std::mem::discriminant(lop).hash(&mut self.s); self.hash_expr(le); }, ExprKind::UnsafeBinderCast(kind, expr, ty) => { - std::mem::discriminant(&kind).hash(&mut self.s); + std::mem::discriminant(kind).hash(&mut self.s); self.hash_expr(expr); if let Some(ty) = ty { self.hash_ty(ty); @@ -1079,7 +1084,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } pub fn hash_qpath(&mut self, p: &QPath<'_>) { - match *p { + match p { QPath::Resolved(_, path) => { self.hash_path(path); }, @@ -1087,7 +1092,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_name(path.ident.name); }, QPath::LangItem(lang_item, ..) => { - std::mem::discriminant(&lang_item).hash(&mut self.s); + std::mem::discriminant(lang_item).hash(&mut self.s); }, } // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); @@ -1108,14 +1113,14 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_ty_pat(&mut self, pat: &TyPat<'_>) { std::mem::discriminant(&pat.kind).hash(&mut self.s); match pat.kind { - TyPatKind::Range(s, e, i) => { - if let Some(s) = s { - self.hash_const_arg(s); - } - if let Some(e) = e { - self.hash_const_arg(e); + TyPatKind::Range(s, e) => { + self.hash_const_arg(s); + self.hash_const_arg(e); + }, + TyPatKind::Or(variants) => { + for variant in variants { + self.hash_ty_pat(variant); } - std::mem::discriminant(&i).hash(&mut self.s); }, TyPatKind::Err(_) => {}, } @@ -1123,10 +1128,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_pat(&mut self, pat: &Pat<'_>) { std::mem::discriminant(&pat.kind).hash(&mut self.s); - match pat.kind { + match &pat.kind { + PatKind::Missing => unreachable!(), PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => { - std::mem::discriminant(&by_ref).hash(&mut self.s); - std::mem::discriminant(&mutability).hash(&mut self.s); + std::mem::discriminant(by_ref).hash(&mut self.s); + std::mem::discriminant(mutability).hash(&mut self.s); if let Some(pat) = pat { self.hash_pat(pat); } @@ -1134,7 +1140,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat), PatKind::Expr(expr) => self.hash_pat_expr(expr), PatKind::Or(pats) => { - for pat in pats { + for pat in *pats { self.hash_pat(pat); } }, @@ -1145,44 +1151,44 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { if let Some(e) = e { self.hash_pat_expr(e); } - std::mem::discriminant(&i).hash(&mut self.s); + std::mem::discriminant(i).hash(&mut self.s); }, PatKind::Ref(pat, mu) => { self.hash_pat(pat); - std::mem::discriminant(&mu).hash(&mut self.s); + std::mem::discriminant(mu).hash(&mut self.s); }, PatKind::Guard(pat, guard) => { self.hash_pat(pat); self.hash_expr(guard); }, PatKind::Slice(l, m, r) => { - for pat in l { + for pat in *l { self.hash_pat(pat); } if let Some(pat) = m { self.hash_pat(pat); } - for pat in r { + for pat in *r { self.hash_pat(pat); } }, - PatKind::Struct(ref qpath, fields, e) => { + PatKind::Struct(qpath, fields, e) => { self.hash_qpath(qpath); - for f in fields { + for f in *fields { self.hash_name(f.ident.name); self.hash_pat(f.pat); } e.hash(&mut self.s); }, PatKind::Tuple(pats, e) => { - for pat in pats { + for pat in *pats { self.hash_pat(pat); } e.hash(&mut self.s); }, - PatKind::TupleStruct(ref qpath, pats, e) => { + PatKind::TupleStruct(qpath, pats, e) => { self.hash_qpath(qpath); - for pat in pats { + for pat in *pats { self.hash_pat(pat); } e.hash(&mut self.s); @@ -1244,8 +1250,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_lifetime(&mut self, lifetime: &Lifetime) { lifetime.ident.name.hash(&mut self.s); - std::mem::discriminant(&lifetime.res).hash(&mut self.s); - if let LifetimeName::Param(param_id) = lifetime.res { + std::mem::discriminant(&lifetime.kind).hash(&mut self.s); + if let LifetimeKind::Param(param_id) = lifetime.kind { param_id.hash(&mut self.s); } } @@ -1260,7 +1266,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { TyKind::Slice(ty) => { self.hash_ty(ty); }, - &TyKind::Array(ty, len) => { + TyKind::Array(ty, len) => { self.hash_ty(ty); self.hash_const_arg(len); }, @@ -1333,11 +1339,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) { for arg in arg_list { - match *arg { + match arg { GenericArg::Lifetime(l) => self.hash_lifetime(l), GenericArg::Type(ty) => self.hash_ty(ty.as_unambig_ty()), GenericArg::Const(ca) => self.hash_const_arg(ca.as_unambig_ct()), - GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()), + GenericArg::Infer(inf) => self.hash_ty(&inf.to_ty()), } } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index d850cc410008d..3a31917657103 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1,10 +1,6 @@ -#![feature(array_chunks)] #![feature(box_patterns)] -#![feature(f128)] -#![feature(f16)] #![feature(if_let_guard)] -#![feature(macro_metavar_expr_concat)] -#![feature(let_chains)] +#![feature(macro_metavar_expr)] #![feature(never_type)] #![feature(rustc_private)] #![feature(assert_matches)] @@ -29,8 +25,10 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) +extern crate indexmap; extern crate rustc_abi; extern crate rustc_ast; +extern crate rustc_attr_data_structures; extern crate rustc_attr_parsing; extern crate rustc_const_eval; extern crate rustc_data_structures; @@ -52,9 +50,6 @@ extern crate rustc_span; extern crate rustc_trait_selection; extern crate smallvec; -#[macro_use] -pub mod sym_helper; - pub mod ast_utils; pub mod attrs; mod check_proc_macro; @@ -74,6 +69,7 @@ pub mod qualify_min_const_fn; pub mod source; pub mod str_utils; pub mod sugg; +pub mod sym; pub mod ty; pub mod usage; pub mod visitors; @@ -87,52 +83,52 @@ pub use self::hir_utils::{ use core::mem; use core::ops::ControlFlow; use std::collections::hash_map::Entry; -use std::hash::BuildHasherDefault; use std::iter::{once, repeat_n}; use std::sync::{Mutex, MutexGuard, OnceLock}; use itertools::Itertools; use rustc_abi::Integer; use rustc_ast::ast::{self, LitKind, RangeLimits}; -use rustc_attr_parsing::{AttributeKind, find_attr}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; -use rustc_data_structures::unhash::UnhashMap; +use rustc_data_structures::unhash::UnindexMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ - self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, - Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, - ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, - PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, - TraitItemRef, TraitRef, TyKind, UnOp, def, + self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring, + CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, + ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, + Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, + TraitItemKind, TraitRef, TyKind, UnOp, def, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; +use rustc_middle::hir::nested_filter; use rustc_middle::hir::place::PlaceBase; +use rustc_middle::lint::LevelAndSource; use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; -use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty, - TyCtxt, TypeFlags, TypeVisitableExt, UintTy, UpvarCapture, + self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt, + TypeFlags, TypeVisitableExt, UintTy, UpvarCapture, }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; -use rustc_span::{InnerSpan, Span, sym}; +use rustc_span::{InnerSpan, Span}; +use source::walk_span_to_context; use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; -use rustc_middle::hir::nested_filter; #[macro_export] macro_rules! extract_msrv_attr { @@ -240,7 +236,7 @@ pub fn is_in_const_context(cx: &LateContext<'_>) -> bool { /// * const blocks (or inline consts) /// * associated constants pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { - use ConstContext::{Const, ConstFn, Static}; + use rustc_hir::ConstContext::{Const, ConstFn, Static}; let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else { return false; }; @@ -348,14 +344,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { } } -/// Checks if the method call given in `expr` belongs to the given trait. -/// This is a deprecated function, consider using [`is_trait_method`]. -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(); - let trt_id = cx.tcx.trait_of_item(def_id); - trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path)) -} - /// Checks if the given method call expression calls an inherent method. pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { @@ -367,10 +355,10 @@ pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks if a method is defined in an impl of a diagnostic item pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { - if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { - return cx.tcx.is_diagnostic_item(diag_item, adt.did()); - } + if let Some(impl_did) = cx.tcx.impl_of_method(def_id) + && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() + { + return cx.tcx.is_diagnostic_item(diag_item, adt.did()); } false } @@ -434,49 +422,11 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator Some(ty.as_unambig_ty()), + GenericArg::Type(ty) => Some(ty.as_unambig_ty()), _ => None, }) } -/// THIS METHOD IS DEPRECATED. Matches a `QPath` against a slice of segment string literals. -/// -/// This method is deprecated and will eventually be removed since it does not match against the -/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from -/// `QPath::Resolved.1.res.opt_def_id()`. -/// -/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a -/// `rustc_hir::QPath`. -/// -/// # Examples -/// ```rust,ignore -/// match_qpath(path, &["std", "rt", "begin_unwind"]) -/// ``` -pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { - match *path { - QPath::Resolved(_, path) => match_path(path, segments), - QPath::TypeRelative(ty, segment) => match ty.kind { - TyKind::Path(ref inner_path) => { - if let [prefix @ .., end] = segments { - if match_qpath(inner_path, prefix) { - return segment.ident.name.as_str() == *end; - } - } - false - }, - _ => false, - }, - QPath::LangItem(..) => false, - } -} - -/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path. -/// -/// Please use `is_path_diagnostic_item` if the target is a diagnostic item. -pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool { - path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments)) -} - /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if /// it matches the given lang item. pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool { @@ -493,40 +443,12 @@ pub fn is_path_diagnostic_item<'tcx>( path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id)) } -/// THIS METHOD IS DEPRECATED. Matches a `Path` against a slice of segment string literals. -/// -/// This method is deprecated and will eventually be removed since it does not match against the -/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from -/// `QPath::Resolved.1.res.opt_def_id()`. -/// -/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a -/// `rustc_hir::Path`. -/// -/// # Examples -/// -/// ```rust,ignore -/// if match_path(&trait_ref.path, &paths::HASH) { -/// // This is the `std::hash::Hash` trait. -/// } -/// -/// if match_path(ty_path, &["rustc", "lint", "Lint"]) { -/// // This is a `rustc_middle::lint::Lint`. -/// } -/// ``` -pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool { - path.segments - .iter() - .rev() - .zip(segments.iter().rev()) - .all(|(a, b)| a.ident.name.as_str() == *b) -} - /// If the expression is a path to a local, returns the canonical `HirId` of the local. pub fn path_to_local(expr: &Expr<'_>) -> Option { - if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind { - if let Res::Local(id) = path.res { - return Some(id); - } + if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind + && let Res::Local(id) = path.res + { + return Some(id); } None } @@ -587,198 +509,6 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> path_res(cx, maybe_path).opt_def_id() } -fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { - let ty = match name { - "bool" => SimplifiedType::Bool, - "char" => SimplifiedType::Char, - "str" => SimplifiedType::Str, - "array" => SimplifiedType::Array, - "slice" => SimplifiedType::Slice, - // FIXME: rustdoc documents these two using just `pointer`. - // - // Maybe this is something we should do here too. - "const_ptr" => SimplifiedType::Ptr(Mutability::Not), - "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut), - "isize" => SimplifiedType::Int(IntTy::Isize), - "i8" => SimplifiedType::Int(IntTy::I8), - "i16" => SimplifiedType::Int(IntTy::I16), - "i32" => SimplifiedType::Int(IntTy::I32), - "i64" => SimplifiedType::Int(IntTy::I64), - "i128" => SimplifiedType::Int(IntTy::I128), - "usize" => SimplifiedType::Uint(UintTy::Usize), - "u8" => SimplifiedType::Uint(UintTy::U8), - "u16" => SimplifiedType::Uint(UintTy::U16), - "u32" => SimplifiedType::Uint(UintTy::U32), - "u64" => SimplifiedType::Uint(UintTy::U64), - "u128" => SimplifiedType::Uint(UintTy::U128), - "f32" => SimplifiedType::Float(FloatTy::F32), - "f64" => SimplifiedType::Float(FloatTy::F64), - _ => { - return [].iter().copied(); - }, - }; - - tcx.incoherent_impls(ty).iter().copied() -} - -fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { - match tcx.def_kind(def_id) { - DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx - .module_children(def_id) - .iter() - .filter(|item| item.ident.name == name) - .map(|child| child.res.expect_non_local()) - .collect(), - DefKind::Impl { .. } => tcx - .associated_item_def_ids(def_id) - .iter() - .copied() - .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name) - .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)) - .collect(), - _ => Vec::new(), - } -} - -fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec { - let root_mod; - let item_kind = match tcx.hir_node_by_def_id(local_id) { - Node::Crate(r#mod) => { - root_mod = ItemKind::Mod(r#mod); - &root_mod - }, - Node::Item(item) => &item.kind, - _ => return Vec::new(), - }; - - let res = |ident: Ident, owner_id: OwnerId| { - if ident.name == name { - let def_id = owner_id.to_def_id(); - Some(Res::Def(tcx.def_kind(def_id), def_id)) - } else { - None - } - }; - - match item_kind { - ItemKind::Mod(r#mod) => r#mod - .item_ids - .iter() - .filter_map(|&item_id| res(tcx.hir_item(item_id).ident, item_id.owner_id)) - .collect(), - ItemKind::Impl(r#impl) => r#impl - .items - .iter() - .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)) - .collect(), - ItemKind::Trait(.., trait_item_refs) => trait_item_refs - .iter() - .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)) - .collect(), - _ => Vec::new(), - } -} - -fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { - if let Some(local_id) = def_id.as_local() { - local_item_children_by_name(tcx, local_id, name) - } else { - non_local_item_children_by_name(tcx, def_id, name) - } -} - -/// Finds the crates called `name`, may be multiple due to multiple major versions. -pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec { - tcx.crates(()) - .iter() - .copied() - .filter(move |&num| tcx.crate_name(num) == name) - .map(CrateNum::as_def_id) - .map(|id| Res::Def(tcx.def_kind(id), id)) - .collect() -} - -/// Resolves a def path like `std::vec::Vec`. -/// -/// Can return multiple resolutions when there are multiple versions of the same crate, e.g. -/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0. -/// -/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec` -/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. -/// -/// This function is expensive and should be used sparingly. -pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec { - let (base, path) = match path { - [primitive] => { - return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; - }, - [base, path @ ..] => (base, path), - _ => return Vec::new(), - }; - - let base_sym = Symbol::intern(base); - - let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym { - Some(LOCAL_CRATE.as_def_id()) - } else { - None - }; - - let crates = find_primitive_impls(tcx, base) - .chain(local_crate) - .map(|id| Res::Def(tcx.def_kind(id), id)) - .chain(find_crates(tcx, base_sym)) - .collect(); - - def_path_res_with_base(tcx, crates, path) -} - -/// Resolves a def path like `vec::Vec` with the base `std`. -/// -/// This is lighter than [`def_path_res`], and should be called with [`find_crates`] looking up -/// items from the same crate repeatedly, although should still be used sparingly. -pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec, mut path: &[&str]) -> Vec { - while let [segment, rest @ ..] = path { - path = rest; - let segment = Symbol::intern(segment); - - base = base - .into_iter() - .filter_map(|res| res.opt_def_id()) - .flat_map(|def_id| { - // When the current def_id is e.g. `struct S`, check the impl items in - // `impl S { ... }` - let inherent_impl_children = tcx - .inherent_impls(def_id) - .iter() - .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)); - - let direct_children = item_children_by_name(tcx, def_id, segment); - - inherent_impl_children.chain(direct_children) - }) - .collect(); - } - - base -} - -/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`]. -pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator + use<> { - def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id()) -} - -/// Convenience function to get the `DefId` of a trait by path. -/// It could be a trait or trait alias. -/// -/// This function is expensive and should be used sparingly. -pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option { - def_path_res(tcx, path).into_iter().find_map(|res| match res { - Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), - _ => None, - }) -} - /// Gets the `hir::TraitRef` of the trait the given method is implemented for. /// /// Use this if you want to find the `TraitRef` of the `Add` trait in this example: @@ -794,12 +524,8 @@ pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option { /// } /// } /// ``` -pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> { - // Get the implemented trait for the current function - let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - let parent_impl = cx.tcx.hir_get_parent_item(hir_id); - if parent_impl != hir::CRATE_OWNER_ID - && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_impl.def_id) +pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> { + if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner)) && let ItemKind::Impl(impl_) = &item.kind { return impl_.of_trait.as_ref(); @@ -890,16 +616,14 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< sym::BinaryHeap, ]; - if let QPath::TypeRelative(_, method) = path { - if method.ident.name == sym::new { - if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { - if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { - return std_types_symbols.iter().any(|&symbol| { - cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string() - }); - } - } - } + if let QPath::TypeRelative(_, method) = path + && method.ident.name == sym::new + && let Some(impl_did) = cx.tcx.impl_of_method(def_id) + && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() + { + return std_types_symbols.iter().any(|&symbol| { + cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string() + }); } false } @@ -1024,6 +748,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg), ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), + ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)), _ => false, } } @@ -1129,6 +854,7 @@ pub fn can_move_expr_to_closure_no_visit<'tcx>( #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CaptureKind { Value, + Use, Ref(Mutability), } impl CaptureKind { @@ -1141,6 +867,7 @@ impl std::ops::BitOr for CaptureKind { fn bitor(self, rhs: Self) -> Self::Output { match (self, rhs) { (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value, + (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use, (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_)) | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut), (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not), @@ -1198,12 +925,10 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { .adjustments() .get(child_id) .map_or(&[][..], |x| &**x) - { - if let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) = + && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) = *adjust.last().map_or(target, |a| a.target).kind() - { - return CaptureKind::Ref(mutability); - } + { + return CaptureKind::Ref(mutability); } match parent { @@ -1220,7 +945,7 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { }, ExprKind::Let(let_expr) => { let mutability = match pat_capture_kind(cx, let_expr.pat) { - CaptureKind::Value => Mutability::Not, + CaptureKind::Value | CaptureKind::Use => Mutability::Not, CaptureKind::Ref(m) => m, }; return CaptureKind::Ref(mutability); @@ -1229,7 +954,7 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { let mut mutability = Mutability::Not; for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) { match capture { - CaptureKind::Value => break, + CaptureKind::Value | CaptureKind::Use => break, CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut, CaptureKind::Ref(Mutability::Not) => (), } @@ -1239,7 +964,7 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { _ => break, }, Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) { - CaptureKind::Value => break, + CaptureKind::Value | CaptureKind::Use => break, capture @ CaptureKind::Ref(_) => return capture, }, _ => break, @@ -1294,6 +1019,7 @@ pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<' if !self.locals.contains(&local_id) { let capture = match capture.info.capture_kind { UpvarCapture::ByValue => CaptureKind::Value, + UpvarCapture::ByUse => CaptureKind::Use, UpvarCapture::ByRef(kind) => match kind { BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not), BorrowKind::UniqueImmutable | BorrowKind::Mutable => { @@ -1372,13 +1098,13 @@ pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec(expr: &'a Expr<'_>, methods: &[&str]) -> Option, &'a [Expr<'a>])>> { +pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option, &'a [Expr<'a>])>> { let mut current = expr; let mut matched = Vec::with_capacity(methods.len()); for method_name in methods.iter().rev() { // method chains are stored last -> first if let ExprKind::MethodCall(path, receiver, args, _) = current.kind { - if path.ident.name.as_str() == *method_name { + if path.ident.name == *method_name { if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) { return None; } @@ -1410,12 +1136,11 @@ pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> 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 parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id; match cx.tcx.hir_node_by_def_id(parent_id) { - Node::Item(Item { ident, .. }) - | Node::TraitItem(TraitItem { ident, .. }) - | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name), + Node::Item(item) => item.kind.ident().map(|ident| ident.name), + Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name), _ => None, } } @@ -1731,10 +1456,10 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool /// Checks whether the given expression is a constant literal of the given value. pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool { // FIXME: use constant folding - if let ExprKind::Lit(spanned) = expr.kind { - if let LitKind::Int(v, _) = spanned.node { - return v == value; - } + if let ExprKind::Lit(spanned) = expr.kind + && let LitKind::Int(v, _) = spanned.node + { + return v == value; } false } @@ -1765,16 +1490,16 @@ pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// macro `name`. /// See also [`is_direct_expn_of`]. #[must_use] -pub fn is_expn_of(mut span: Span, name: &str) -> Option { +pub fn is_expn_of(mut span: Span, name: Symbol) -> Option { loop { if span.from_expansion() { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; - if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind { - if mac_name.as_str() == name { - return Some(new_span); - } + if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind + && mac_name == name + { + return Some(new_span); } span = new_span; @@ -1795,15 +1520,15 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option { /// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only /// from `bar!` by `is_direct_expn_of`. #[must_use] -pub fn is_direct_expn_of(span: Span, name: &str) -> Option { +pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option { if span.from_expansion() { let data = span.ctxt().outer_expn_data(); let new_span = data.call_site; - if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind { - if mac_name.as_str() == name { - return Some(new_span); - } + if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind + && mac_name == name + { + return Some(new_span); } } @@ -1824,15 +1549,15 @@ pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> /// 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 { - if let ExprKind::Call(fun, _) = expr.kind { - if let ExprKind::Path(ref qp) = fun.kind { - let res = cx.qpath_res(qp, fun.hir_id); - return match res { - Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, - Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), - _ => false, - }; - } + if let ExprKind::Call(fun, _) = expr.kind + && let ExprKind::Path(ref qp) = fun.kind + { + let res = cx.qpath_res(qp, fun.hir_id); + return match res { + Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, + Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), + _ => false, + }; } false } @@ -1852,6 +1577,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { } match pat.kind { + PatKind::Missing => unreachable!(), PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable. PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)), PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat), @@ -1905,10 +1631,10 @@ pub fn is_self(slf: &Param<'_>) -> bool { } pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool { - if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind { - if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res { - return true; - } + if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind + && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res + { + return true; } false } @@ -1971,14 +1697,14 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I let mut suppress_lint = false; for id in ids { - let (level, _) = cx.tcx.lint_level_at_node(lint, id); - if let Some(expectation) = level.get_expectation_id() { + let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id); + if let Some(expectation) = lint_id { cx.fulfill_expectation(expectation); } match level { - Level::Allow | Level::Expect(_) => suppress_lint = true, - Level::Warn | Level::ForceWarn(_) | Level::Deny | Level::Forbid => {}, + Level::Allow | Level::Expect => suppress_lint = true, + Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {}, } } @@ -1993,7 +1719,7 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I /// make sure to use `span_lint_hir` functions to emit the lint. This ensures that /// expectations at the checked nodes will be fulfilled. pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool { - cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow + cx.tcx.lint_level_at_node(lint, id).level == Level::Allow } pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> { @@ -2033,15 +1759,14 @@ pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool { } pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { - find_attr!(cx.tcx.hir().attrs(hir_id), AttributeKind::Repr(..)) + find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr(..)) } pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool { - let map = &tcx.hir(); let mut prev_enclosing_node = None; let mut enclosing_node = node; while Some(enclosing_node) != prev_enclosing_node { - if has_attr(map.attrs(enclosing_node), symbol) { + if has_attr(tcx.hir_attrs(enclosing_node), symbol) { return true; } prev_enclosing_node = Some(enclosing_node); @@ -2058,36 +1783,18 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool { .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_)))) .any(|(id, _)| { has_attr( - tcx.hir().attrs(tcx.local_def_id_to_hir_id(id.def_id)), + tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)), sym::automatically_derived, ) }) } -/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if -/// any. -/// -/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items. -pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option { - let search_path = cx.get_def_path(did); - paths - .iter() - .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied())) -} - -/// Checks if the given `DefId` matches the path. -pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool { - // We should probably move to Symbols in Clippy as well rather than interning every time. - let path = cx.get_def_path(did); - syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied()) -} - /// Checks if the given `DefId` matches the `libc` item. -pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool { +pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool { let path = cx.get_def_path(did); // libc is meant to be used as a flat list of names, but they're all actually defined in different // modules based on the target platform. Ignore everything but crate name and the item name. - path.first().is_some_and(|s| s.as_str() == "libc") && path.last().is_some_and(|s| s.as_str() == name) + path.first().is_some_and(|s| *s == sym::libc) && path.last().copied() == Some(name) } /// Returns the list of condition expressions and the list of blocks in a @@ -2114,10 +1821,10 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, } // final `else {..}` - if !blocks.is_empty() { - if let ExprKind::Block(block, _) = expr.kind { - blocks.push(block); - } + if !blocks.is_empty() + && let ExprKind::Block(block, _) = expr.kind + { + blocks.push(block); } (conds, blocks) @@ -2132,26 +1839,34 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool { } } -/// Peels away all the compiler generated code surrounding the body of an async function, -pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind { - if let ExprKind::Block( +/// Peels away all the compiler generated code surrounding the body of an async closure. +pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Closure(&Closure { + body, + kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)), + .. + }) = expr.kind + && let ExprKind::Block( Block { - stmts: [], expr: Some(Expr { - kind: ExprKind::DropTemps(expr), + kind: ExprKind::DropTemps(inner_expr), .. }), .. }, _, ) = tcx.hir_body(body).value.kind - { - return Some(expr); - } + { + Some(inner_expr) + } else { + None } - None +} + +/// Peels away all the compiler generated code surrounding the body of an async function, +pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { + get_async_closure_expr(tcx, body.value) } // check if expr is calling method or function with #[must_use] attribute @@ -2329,6 +2044,18 @@ pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..)) } +/// Checks if the expression is a temporary value. +// This logic is the same as the one used in rustc's `check_named_place_expr function`. +// https://github.com/rust-lang/rust/blob/3ed2a10d173d6c2e0232776af338ca7d080b1cd4/compiler/rustc_hir_typeck/src/expr.rs#L482-L499 +pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + !expr.is_place_expr(|base| { + cx.typeck_results() + .adjustments() + .get(base.hir_id) + .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) + }) +} + pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> { if !is_no_std_crate(cx) { Some("std") @@ -2341,18 +2068,16 @@ pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> { pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool { cx.tcx - .hir() - .attrs(hir::CRATE_HIR_ID) + .hir_attrs(hir::CRATE_HIR_ID) .iter() - .any(|attr| attr.name_or_empty() == sym::no_std) + .any(|attr| attr.has_name(sym::no_std)) } pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { cx.tcx - .hir() - .attrs(hir::CRATE_HIR_ID) + .hir_attrs(hir::CRATE_HIR_ID) .iter() - .any(|attr| attr.name_or_empty() == sym::no_core) + .any(|attr| attr.has_name(sym::no_core)) } /// Check if parent of a hir node is a trait implementation block. @@ -2468,45 +2193,46 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<(&T, &T)> +pub fn search_same(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec> where Hash: FnMut(&T) -> u64, Eq: FnMut(&T, &T) -> bool, { match exprs { - [a, b] if eq(a, b) => return vec![(a, b)], + [a, b] if eq(a, b) => return vec![vec![a, b]], _ if exprs.len() <= 2 => return vec![], _ => {}, } - let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); - - let mut map: UnhashMap> = - UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default()); + let mut buckets: UnindexMap>> = UnindexMap::default(); for expr in exprs { - match map.entry(hash(expr)) { - Entry::Occupied(mut o) => { - for o in o.get() { - if eq(o, expr) { - match_expr_list.push((o, expr)); - } + match buckets.entry(hash(expr)) { + indexmap::map::Entry::Occupied(mut o) => { + let bucket = o.get_mut(); + match bucket.iter_mut().find(|group| eq(expr, group[0])) { + Some(group) => group.push(expr), + None => bucket.push(vec![expr]), } - o.get_mut().push(expr); }, - Entry::Vacant(v) => { - v.insert(vec![expr]); + indexmap::map::Entry::Vacant(v) => { + v.insert(vec![vec![expr]]); }, } } - match_expr_list + buckets + .into_values() + .flatten() + .filter(|group| group.len() > 1) + .collect() } /// Peels off all references on the pattern. Returns the underlying pattern and the number of @@ -2613,17 +2339,19 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir> } pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { - if let Res::Def(_, def_id) = path.res { - return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr); - } + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind + && let Res::Def(_, def_id) = path.res + { + return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr); } false } static TEST_ITEM_NAMES_CACHE: OnceLock>>> = OnceLock::new(); -fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { +/// Apply `f()` to the set of test item names. +/// The names are sorted using the default `Symbol` ordering. +fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool { let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default())); let mut map: MutexGuard<'_, FxHashMap>> = cache.lock().unwrap(); let value = map.entry(module); @@ -2634,20 +2362,17 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Sym for id in tcx.hir_module_free_items(module) { if matches!(tcx.def_kind(id.owner_id), DefKind::Const) && let item = tcx.hir_item(id) - && let ItemKind::Const(ty, _generics, _body) = item.kind - { - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + && let ItemKind::Const(ident, ty, _generics, _body) = item.kind + && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind // We could also check for the type name `test::TestDescAndFn` - if let Res::Def(DefKind::Struct, _) = path.res { - let has_test_marker = tcx - .hir() - .attrs(item.hir_id()) - .iter() - .any(|a| a.has_name(sym::rustc_test_marker)); - if has_test_marker { - names.push(item.ident.name); - } - } + && let Res::Def(DefKind::Struct, _) = path.res + { + let has_test_marker = tcx + .hir_attrs(item.hir_id()) + .iter() + .any(|a| a.has_name(sym::rustc_test_marker)); + if has_test_marker { + names.push(ident.name); } } } @@ -2668,25 +2393,44 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool { // Since you can nest functions we need to collect all until we leave // function scope .any(|(_id, node)| { - if let Node::Item(item) = node { - if let ItemKind::Fn { .. } = item.kind { - // Note that we have sorted the item names in the visitor, - // so the binary_search gets the same as `contains`, but faster. - return names.binary_search(&item.ident.name).is_ok(); - } + if let Node::Item(item) = node + && let ItemKind::Fn { ident, .. } = item.kind + { + // Note that we have sorted the item names in the visitor, + // so the binary_search gets the same as `contains`, but faster. + return names.binary_search(&ident.name).is_ok(); } false }) }) } +/// Checks if `fn_def_id` has a `#[test]` attribute applied +/// +/// This only checks directly applied attributes. To see if a node has a parent function marked with +/// `#[test]` use [`is_in_test_function`]. +/// +/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function +pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool { + let id = tcx.local_def_id_to_hir_id(fn_def_id); + if let Node::Item(item) = tcx.hir_node(id) + && let ItemKind::Fn { ident, .. } = item.kind + { + with_test_item_names(tcx, tcx.parent_module(id), |names| { + names.binary_search(&ident.name).is_ok() + }) + } else { + false + } +} + /// Checks if `id` has a `#[cfg(test)]` attribute applied /// /// This only checks directly applied attributes, to see if a node is inside a `#[cfg(test)]` parent /// use [`is_in_cfg_test`] pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool { - tcx.hir().attrs(id).iter().any(|attr| { - if attr.has_name(sym::cfg) + tcx.hir_attrs(id).iter().any(|attr| { + if attr.has_name(sym::cfg_trace) && let Some(items) = attr.meta_item_list() && let [item] = &*items && item.has_name(sym::test) @@ -2710,13 +2454,11 @@ pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { /// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied. pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - let hir = tcx.hir(); - - tcx.has_attr(def_id, sym::cfg) + tcx.has_attr(def_id, sym::cfg_trace) || tcx .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id)) - .flat_map(|(parent_id, _)| hir.attrs(parent_id)) - .any(|attr| attr.has_name(sym::cfg)) + .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)) + .any(|attr| attr.has_name(sym::cfg_trace)) } /// Walks up the HIR tree from the given expression in an attempt to find where the value is @@ -2979,7 +2721,7 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU { adjustments = cx.typeck_results().expr_adjustments(e); } - same_ctxt &= cx.tcx.hir().span(parent_id).ctxt() == ctxt; + same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt; if let Node::Expr(e) = parent { match e.kind { ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => { @@ -3066,7 +2808,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { sm.span_take_while(span, |&ch| ch == ' ' || ch == ';') } -/// Returns whether the given let pattern and else body can be turned into a question mark +/// Returns whether the given let pattern and else body can be turned into the `?` operator /// /// For this example: /// ```ignore @@ -3089,8 +2831,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { /// ``` /// /// We output `Some(a)` in the first instance, and `Some(FooBar { a, b })` in the second, because -/// the question mark operator is applicable here. Callers have to check whether we are in a -/// constant or not. +/// the `?` operator is applicable here. Callers have to check whether we are in a constant or not. pub fn pat_and_expr_can_be_question_mark<'a, 'hir>( cx: &LateContext<'_>, pat: &'a Pat<'hir>, @@ -3548,7 +3289,7 @@ pub fn is_block_like(expr: &Expr<'_>) -> bool { pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool { fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool { match expr.kind { - ExprKind::Binary(_, lhs, _) => contains_block(lhs, true), + ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true), _ if is_block_like(expr) => is_operand, _ => false, } @@ -3695,3 +3436,38 @@ pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { true } } + +/// Peel `Option<…>` from `hir_ty` as long as the HIR name is `Option` and it corresponds to the +/// `core::Option<_>` type. +pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { + let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else { + return hir_ty; + }; + while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind + && let Some(segment) = path.segments.last() + && segment.ident.name == sym::Option + && let Res::Def(DefKind::Enum, def_id) = segment.res + && def_id == option_def_id + && let [GenericArg::Type(arg_ty)] = segment.args().args + { + hir_ty = arg_ty.as_unambig_ty(); + } + hir_ty +} + +/// If `expr` is a desugared `.await`, return the original expression if it does not come from a +/// macro expansion. +pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind + && let ExprKind::Call(_, [into_future_arg]) = match_value.kind + && let ctxt = expr.span.ctxt() + && for_each_expr_without_closures(into_future_arg, |e| { + walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(())) + }) + .is_none() + { + Some(into_future_arg) + } else { + None + } +} diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index 3ce2cdfebe96b..ba126fcd05de9 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -1,19 +1,18 @@ #![allow(clippy::similar_names)] // `expr` and `expn` -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; -use crate::get_unique_attr; use crate::visitors::{Descend, for_each_expr_without_closures}; +use crate::{get_unique_attr, sym}; use arrayvec::ArrayVec; use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::OnceLock; use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; -use rustc_span::{BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol, sym}; +use rustc_span::{BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol}; use std::ops::ControlFlow; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ @@ -43,7 +42,7 @@ pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool { } else { // Allow users to tag any macro as being format!-like // TODO: consider deleting FORMAT_MACRO_DIAG_ITEMS and using just this method - get_unique_attr(cx.sess(), cx.tcx.get_attrs_unchecked(macro_def_id), "format_args").is_some() + get_unique_attr(cx.sess(), cx.tcx.get_attrs_unchecked(macro_def_id), sym::format_args).is_some() } } @@ -179,7 +178,6 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option< // get the parent node, possibly skipping over a statement // if the parent is not found, it is sensible to return `Some(root)` - let hir = cx.tcx.hir(); let mut parent_iter = cx.tcx.hir_parent_iter(node.hir_id()); let (parent_id, _) = match parent_iter.next() { None => return Some(ExpnId::root()), @@ -191,7 +189,7 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option< }; // get the macro expansion of the parent node - let parent_span = hir.span(parent_id); + let parent_span = cx.tcx.hir_span(parent_id); let Some(parent_macro_call) = macro_backtrace(parent_span).next() else { // the parent node is not in a macro return Some(ExpnId::root()); @@ -250,10 +248,10 @@ impl<'a> PanicExpn<'a> { let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None; }; - let name = path.segments.last().unwrap().ident.as_str(); + let name = path.segments.last().unwrap().ident.name; // This has no argument - if name == "panic_cold_explicit" { + if name == sym::panic_cold_explicit { return Some(Self::Empty); } @@ -261,18 +259,18 @@ impl<'a> PanicExpn<'a> { return None; }; let result = match name { - "panic" if arg.span.eq_ctxt(expr.span) => Self::Empty, - "panic" | "panic_str" => Self::Str(arg), - "panic_display" | "panic_cold_display" => { + sym::panic if arg.span.eq_ctxt(expr.span) => Self::Empty, + sym::panic | sym::panic_str => Self::Str(arg), + sym::panic_display | sym::panic_cold_display => { let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None; }; Self::Display(e) }, - "panic_fmt" => Self::Format(arg), + sym::panic_fmt => Self::Format(arg), // Since Rust 1.52, `assert_{eq,ne}` macros expand to use: // `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));` - "assert_failed" => { + sym::assert_failed => { // It should have 4 arguments in total (we already matched with the first argument, // so we're just checking for 3) if rest.len() != 3 { diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs index ffcfcd240ea5a..9ba644fdd20ec 100644 --- a/src/tools/clippy/clippy_utils/src/mir/mod.rs +++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs @@ -76,7 +76,7 @@ impl<'tcx> Visitor<'tcx> for V<'_> { } if matches!( ctx, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move | NonMutatingUseContext::Inspect) | PlaceContext::MutatingUse(MutatingUseContext::Borrow) ) { self.results[i].local_consume_or_mutate_locs.push(loc); diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs index 605764cef89fe..152b4272c26ca 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs @@ -6,8 +6,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_lint::LateContext; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{self, Mutability}; -use rustc_middle::ty::visit::TypeVisitor; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitor}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::{Analysis, ResultsCursor}; use std::borrow::Cow; diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 5bb2b12988a6c..a5e66ad463bbb 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -1,10 +1,11 @@ +use crate::sym; use rustc_ast::Attribute; use rustc_ast::attr::AttributeExt; - -use rustc_attr_parsing::{RustcVersion, parse_version}; +use rustc_attr_data_structures::RustcVersion; +use rustc_attr_parsing::parse_version; use rustc_lint::LateContext; use rustc_session::Session; -use rustc_span::{Symbol, sym}; +use rustc_span::Symbol; use serde::Deserialize; use smallvec::SmallVec; use std::iter::once; @@ -22,24 +23,26 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT } - 1,85,0 { UINT_FLOAT_MIDPOINT } - 1,84,0 { CONST_OPTION_AS_SLICE } + 1,88,0 { LET_CHAINS } + 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT } + 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } + 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } - 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION } + 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION, DURATION_ABS_DIFF } 1,80,0 { BOX_INTO_ITER, LAZY_CELL } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } 1,75,0 { OPTION_AS_SLICE } 1,74,0 { REPR_RUST, IO_ERROR_OTHER } - 1,73,0 { MANUAL_DIV_CEIL } + 1,73,0 { DIV_CEIL } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } - 1,63,0 { CLONE_INTO } + 1,63,0 { CLONE_INTO, CONST_SLICE_FROM_REF } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN } + 1,60,0 { ABS_DIFF } 1,59,0 { THREAD_LOCAL_CONST_INIT } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF } 1,57,0 { MAP_WHILE } @@ -63,10 +66,11 @@ msrv_aliases! { 1,35,0 { OPTION_COPIED, RANGE_CONTAINS } 1,34,0 { TRY_FROM } 1,33,0 { UNDERSCORE_IMPORTS } + 1,32,0 { CONST_IS_POWER_OF_TWO } 1,31,0 { OPTION_REPLACE } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } - 1,28,0 { FROM_BOOL, REPEAT_WITH } + 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } 1,27,0 { ITERATOR_TRY_FOLD } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } 1,24,0 { IS_ASCII_DIGIT } @@ -74,6 +78,7 @@ msrv_aliases! { 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } 1,15,0 { MAYBE_BOUND_IN_WHERE } + 1,13,0 { QUESTION_MARK_OPERATOR } } /// `#[clippy::msrv]` attributes are rarely used outside of Clippy's test suite, as a basic @@ -108,7 +113,7 @@ impl Msrv { let start = cx.last_node_with_lint_attrs; if let Some(msrv_attr) = once(start) .chain(cx.tcx.hir_parent_id_iter(start)) - .find_map(|id| parse_attrs(cx.tcx.sess, cx.tcx.hir().attrs(id))) + .find_map(|id| parse_attrs(cx.tcx.sess, cx.tcx.hir_attrs(id))) { return Some(msrv_attr); } @@ -181,8 +186,7 @@ impl MsrvStack { } fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt]) -> Option { - let sym_msrv = Symbol::intern("msrv"); - let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym_msrv])); + let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym::msrv])); if let Some(msrv_attr) = msrv_attrs.next() { if let Some(duplicate) = msrv_attrs.next_back() { diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 51d06ad9b1aa5..9d7f3086b05fa 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -4,60 +4,344 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. -// Paths inside rustc -pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; -pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ - ["rustc_lint_defs", "Applicability", "Unspecified"], - ["rustc_lint_defs", "Applicability", "HasPlaceholders"], - ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], - ["rustc_lint_defs", "Applicability", "MachineApplicable"], -]; -pub const DIAG: [&str; 2] = ["rustc_errors", "Diag"]; -pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; -pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"]; -pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; -pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; -pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; -pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; -pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; -pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; -pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; -pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; -pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; -pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; +use crate::{MaybePath, path_def_id, sym}; +use rustc_ast::Mutability; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; +use rustc_hir::{ImplItemRef, ItemKind, Node, OwnerId, TraitItemRef, UseKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy}; +use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol}; +use std::sync::OnceLock; -// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items. -pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "", "is_ascii"]; -pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"]; -pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"]; +/// Specifies whether to resolve a path in the [`TypeNS`], [`ValueNS`], [`MacroNS`] or in an +/// arbitrary namespace +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum PathNS { + Type, + Value, + Macro, + + /// Resolves to the name in the first available namespace, e.g. for `std::vec` this would return + /// either the macro or the module but **not** both + /// + /// Must only be used when the specific resolution is unimportant such as in + /// `missing_enforced_import_renames` + Arbitrary, +} + +impl PathNS { + fn matches(self, ns: Option) -> bool { + let required = match self { + PathNS::Type => TypeNS, + PathNS::Value => ValueNS, + PathNS::Macro => MacroNS, + PathNS::Arbitrary => return true, + }; + + ns == Some(required) + } +} + +/// Lazily resolves a path into a list of [`DefId`]s using [`lookup_path`]. +/// +/// Typically it will contain one [`DefId`] or none, but in some situations there can be multiple: +/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0 +/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls +/// ([1], [2], [3]) +/// +/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast +/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1 +/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2 +pub struct PathLookup { + ns: PathNS, + path: &'static [Symbol], + once: OnceLock>, +} + +impl PathLookup { + /// Only exported for tests and `clippy_lints_internal` + #[doc(hidden)] + pub const fn new(ns: PathNS, path: &'static [Symbol]) -> Self { + Self { + ns, + path, + once: OnceLock::new(), + } + } + + /// Returns the list of [`DefId`]s that the path resolves to + pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] { + self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path)) + } -// Paths in clippy itself -pub const MSRV_STACK: [&str; 3] = ["clippy_utils", "msrvs", "MsrvStack"]; + /// Returns the single [`DefId`] that the path resolves to, this can only be used for paths into + /// stdlib crates to avoid the issue of multiple [`DefId`]s being returned + /// + /// May return [`None`] in `no_std`/`no_core` environments + pub fn only(&self, cx: &LateContext<'_>) -> Option { + let ids = self.get(cx); + debug_assert!(STDLIB_STABLE_CRATES.contains(&self.path[0])); + debug_assert!(ids.len() <= 1, "{ids:?}"); + ids.first().copied() + } + + /// Checks if the path resolves to the given `def_id` + pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool { + self.get(cx).contains(&def_id) + } + + /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it + pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> bool { + path_def_id(cx, maybe_path).is_some_and(|def_id| self.matches(cx, def_id)) + } + + /// Checks if the path resolves to `ty`'s definition, must be an `Adt` + pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did())) + } +} + +macro_rules! path_macros { + ($($name:ident: $ns:expr,)*) => { + $( + /// Only exported for tests and `clippy_lints_internal` + #[doc(hidden)] + #[macro_export] + macro_rules! $name { + ($$($$seg:ident $$(::)?)*) => { + PathLookup::new($ns, &[$$(sym::$$seg,)*]) + }; + } + )* + }; +} + +path_macros! { + type_path: PathNS::Type, + value_path: PathNS::Value, + macro_path: PathNS::Macro, +} + +// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items. +pub static ALIGN_OF: PathLookup = value_path!(core::mem::align_of); +pub static CHAR_TO_DIGIT: PathLookup = value_path!(char::to_digit); +pub static CONCAT: PathLookup = macro_path!(core::concat); +pub static IO_ERROR_NEW: PathLookup = value_path!(std::io::Error::new); +pub static IO_ERRORKIND_OTHER_CTOR: PathLookup = value_path!(std::io::ErrorKind::Other); +pub static ITER_STEP: PathLookup = type_path!(core::iter::Step); +pub static SLICE_FROM_REF: PathLookup = value_path!(core::slice::from_ref); // Paths in external crates -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; -pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; -pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"]; -pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"]; -pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"]; -pub const REGEX_BUILDER_NEW: [&str; 3] = ["regex", "RegexBuilder", "new"]; -pub const REGEX_BYTES_BUILDER_NEW: [&str; 4] = ["regex", "bytes", "RegexBuilder", "new"]; -pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "bytes", "Regex", "new"]; -pub const REGEX_BYTES_SET_NEW: [&str; 4] = ["regex", "bytes", "RegexSet", "new"]; -pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"]; -pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"]; -pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; -pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_OPEN_OPTIONS: [&str; 4] = ["tokio", "fs", "open_options", "OpenOptions"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const TOKIO_IO_OPEN_OPTIONS_NEW: [&str; 5] = ["tokio", "fs", "open_options", "OpenOptions", "new"]; +pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt); +pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt); +pub static ITERTOOLS_NEXT_TUPLE: PathLookup = value_path!(itertools::Itertools::next_tuple); +pub static PARKING_LOT_GUARDS: [PathLookup; 3] = [ + type_path!(lock_api::mutex::MutexGuard), + type_path!(lock_api::rwlock::RwLockReadGuard), + type_path!(lock_api::rwlock::RwLockWriteGuard), +]; +pub static REGEX_BUILDER_NEW: PathLookup = value_path!(regex::RegexBuilder::new); +pub static REGEX_BYTES_BUILDER_NEW: PathLookup = value_path!(regex::bytes::RegexBuilder::new); +pub static REGEX_BYTES_NEW: PathLookup = value_path!(regex::bytes::Regex::new); +pub static REGEX_BYTES_SET_NEW: PathLookup = value_path!(regex::bytes::RegexSet::new); +pub static REGEX_NEW: PathLookup = value_path!(regex::Regex::new); +pub static REGEX_SET_NEW: PathLookup = value_path!(regex::RegexSet::new); +pub static SERDE_DESERIALIZE: PathLookup = type_path!(serde::de::Deserialize); +pub static SERDE_DE_VISITOR: PathLookup = type_path!(serde::de::Visitor); +pub static TOKIO_FILE_OPTIONS: PathLookup = value_path!(tokio::fs::File::options); +pub static TOKIO_IO_ASYNCREADEXT: PathLookup = type_path!(tokio::io::AsyncReadExt); +pub static TOKIO_IO_ASYNCWRITEEXT: PathLookup = type_path!(tokio::io::AsyncWriteExt); +pub static TOKIO_IO_OPEN_OPTIONS: PathLookup = type_path!(tokio::fs::OpenOptions); +pub static TOKIO_IO_OPEN_OPTIONS_NEW: PathLookup = value_path!(tokio::fs::OpenOptions::new); +pub static LAZY_STATIC: PathLookup = macro_path!(lazy_static::lazy_static); +pub static ONCE_CELL_SYNC_LAZY: PathLookup = type_path!(once_cell::sync::Lazy); +pub static ONCE_CELL_SYNC_LAZY_NEW: PathLookup = value_path!(once_cell::sync::Lazy::new); + +// Paths for internal lints go in `clippy_lints_internal/src/internal_paths.rs` + +/// Equivalent to a [`lookup_path`] after splitting the input string on `::` +/// +/// This function is expensive and should be used sparingly. +pub fn lookup_path_str(tcx: TyCtxt<'_>, ns: PathNS, path: &str) -> Vec { + let path: Vec = path.split("::").map(Symbol::intern).collect(); + lookup_path(tcx, ns, &path) +} + +/// Resolves a def path like `std::vec::Vec`. +/// +/// Typically it will return one [`DefId`] or none, but in some situations there can be multiple: +/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0 +/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls +/// ([1], [2], [3]) +/// +/// This function is expensive and should be used sparingly. +/// +/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast +/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1 +/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2 +pub fn lookup_path(tcx: TyCtxt<'_>, ns: PathNS, path: &[Symbol]) -> Vec { + let (root, rest) = match *path { + [] | [_] => return Vec::new(), + [root, ref rest @ ..] => (root, rest), + }; + + let mut out = Vec::new(); + for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) { + lookup_with_base(tcx, base, ns, rest, &mut out); + } + out +} + +/// Finds the crates called `name`, may be multiple due to multiple major versions. +pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> &'static [DefId] { + static BY_NAME: OnceLock>> = OnceLock::new(); + let map = BY_NAME.get_or_init(|| { + let mut map = FxHashMap::default(); + map.insert(tcx.crate_name(LOCAL_CRATE), vec![LOCAL_CRATE.as_def_id()]); + for &num in tcx.crates(()) { + map.entry(tcx.crate_name(num)).or_default().push(num.as_def_id()); + } + map + }); + match map.get(&name) { + Some(def_ids) => def_ids, + None => &[], + } +} + +fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] { + let ty = match name { + sym::bool => SimplifiedType::Bool, + sym::char => SimplifiedType::Char, + sym::str => SimplifiedType::Str, + sym::array => SimplifiedType::Array, + sym::slice => SimplifiedType::Slice, + // FIXME: rustdoc documents these two using just `pointer`. + // + // Maybe this is something we should do here too. + sym::const_ptr => SimplifiedType::Ptr(Mutability::Not), + sym::mut_ptr => SimplifiedType::Ptr(Mutability::Mut), + sym::isize => SimplifiedType::Int(IntTy::Isize), + sym::i8 => SimplifiedType::Int(IntTy::I8), + sym::i16 => SimplifiedType::Int(IntTy::I16), + sym::i32 => SimplifiedType::Int(IntTy::I32), + sym::i64 => SimplifiedType::Int(IntTy::I64), + sym::i128 => SimplifiedType::Int(IntTy::I128), + sym::usize => SimplifiedType::Uint(UintTy::Usize), + sym::u8 => SimplifiedType::Uint(UintTy::U8), + sym::u16 => SimplifiedType::Uint(UintTy::U16), + sym::u32 => SimplifiedType::Uint(UintTy::U32), + sym::u64 => SimplifiedType::Uint(UintTy::U64), + sym::u128 => SimplifiedType::Uint(UintTy::U128), + sym::f32 => SimplifiedType::Float(FloatTy::F32), + sym::f64 => SimplifiedType::Float(FloatTy::F64), + _ => return &[], + }; + + tcx.incoherent_impls(ty) +} + +/// Resolves a def path like `vec::Vec` with the base `std`. +fn lookup_with_base(tcx: TyCtxt<'_>, mut base: DefId, ns: PathNS, mut path: &[Symbol], out: &mut Vec) { + loop { + match *path { + [segment] => { + out.extend(item_child_by_name(tcx, base, ns, segment)); + + // When the current def_id is e.g. `struct S`, check the impl items in + // `impl S { ... }` + let inherent_impl_children = tcx + .inherent_impls(base) + .iter() + .filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment)); + out.extend(inherent_impl_children); + + return; + }, + [segment, ref rest @ ..] => { + path = rest; + let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else { + return; + }; + base = child; + }, + [] => unreachable!(), + } + } +} + +fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { + if let Some(local_id) = def_id.as_local() { + local_item_child_by_name(tcx, local_id, ns, name) + } else { + non_local_item_child_by_name(tcx, def_id, ns, name) + } +} + +fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, name: Symbol) -> Option { + let root_mod; + let item_kind = match tcx.hir_node_by_def_id(local_id) { + Node::Crate(r#mod) => { + root_mod = ItemKind::Mod(Ident::dummy(), r#mod); + &root_mod + }, + Node::Item(item) => &item.kind, + _ => return None, + }; + + let res = |ident: Ident, owner_id: OwnerId| { + if ident.name == name && ns.matches(tcx.def_kind(owner_id).ns()) { + Some(owner_id.to_def_id()) + } else { + None + } + }; + + match item_kind { + ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| { + let item = tcx.hir_item(item_id); + if let ItemKind::Use(path, UseKind::Single(ident)) = item.kind { + if ident.name == name { + path.res + .iter() + .find(|res| ns.matches(res.ns())) + .and_then(Res::opt_def_id) + } else { + None + } + } else { + res(item.kind.ident()?, item_id.owner_id) + } + }), + ItemKind::Impl(r#impl) => r#impl + .items + .iter() + .find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)), + ItemKind::Trait(.., trait_item_refs) => trait_item_refs + .iter() + .find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)), + _ => None, + } +} + +fn non_local_item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { + match tcx.def_kind(def_id) { + DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx.module_children(def_id).iter().find_map(|child| { + if child.ident.name == name && ns.matches(child.res.ns()) { + child.res.opt_def_id() + } else { + None + } + }), + DefKind::Impl { .. } => tcx + .associated_item_def_ids(def_id) + .iter() + .copied() + .find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_utils/src/ptr.rs b/src/tools/clippy/clippy_utils/src/ptr.rs index 360c6251a57c2..5847e916e340f 100644 --- a/src/tools/clippy/clippy_utils/src/ptr.rs +++ b/src/tools/clippy/clippy_utils/src/ptr.rs @@ -1,17 +1,17 @@ use crate::source::snippet; use crate::visitors::{Descend, for_each_expr_without_closures}; -use crate::{path_to_local_id, strip_pat_refs}; +use crate::{path_to_local_id, strip_pat_refs, sym}; use core::ops::ControlFlow; use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; pub fn get_spans( cx: &LateContext<'_>, opt_body_id: Option, idx: usize, - replacements: &[(&'static str, &'static str)], + replacements: &[(Symbol, &'static str)], ) -> Option)>> { if let Some(body) = opt_body_id.map(|id| cx.tcx.hir_body(id)) { if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind { @@ -27,7 +27,7 @@ pub fn get_spans( fn extract_clone_suggestions<'tcx>( cx: &LateContext<'tcx>, id: HirId, - replace: &[(&'static str, &'static str)], + replace: &[(Symbol, &'static str)], body: &'tcx Body<'_>, ) -> Option)>> { let mut spans = Vec::new(); @@ -35,11 +35,11 @@ fn extract_clone_suggestions<'tcx>( if let ExprKind::MethodCall(seg, recv, [], _) = e.kind && path_to_local_id(recv, id) { - if seg.ident.as_str() == "capacity" { + if seg.ident.name == sym::capacity { return ControlFlow::Break(()); } for &(fn_name, suffix) in replace { - if seg.ident.as_str() == fn_name { + if seg.ident.name == fn_name { spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); return ControlFlow::Continue(Descend::No); } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 8e6f4d4a317eb..45da266fd8a9a 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -5,7 +5,7 @@ use crate::msrvs::{self, Msrv}; use hir::LangItem; -use rustc_attr_parsing::{RustcVersion, StableSince}; +use rustc_attr_data_structures::{RustcVersion, StableSince}; use rustc_const_eval::check_consts::ConstCx; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -395,24 +395,32 @@ fn check_terminator<'tcx>( fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool { cx.tcx.is_const_fn(def_id) - && cx.tcx.lookup_const_stability(def_id).is_none_or(|const_stab| { - if let rustc_attr_parsing::StabilityLevel::Stable { since, .. } = const_stab.level { - // Checking MSRV is manually necessary because `rustc` has no such concept. This entire - // function could be removed if `rustc` provided a MSRV-aware version of `is_stable_const_fn`. - // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. + && cx + .tcx + .lookup_const_stability(def_id) + .or_else(|| { + cx.tcx + .trait_of_item(def_id) + .and_then(|trait_def_id| cx.tcx.lookup_const_stability(trait_def_id)) + }) + .is_none_or(|const_stab| { + if let rustc_attr_data_structures::StabilityLevel::Stable { since, .. } = const_stab.level { + // Checking MSRV is manually necessary because `rustc` has no such concept. This entire + // function could be removed if `rustc` provided a MSRV-aware version of `is_stable_const_fn`. + // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. - let const_stab_rust_version = match since { - StableSince::Version(version) => version, - StableSince::Current => RustcVersion::CURRENT, - StableSince::Err => return false, - }; + let const_stab_rust_version = match since { + StableSince::Version(version) => version, + StableSince::Current => RustcVersion::CURRENT, + StableSince::Err => return false, + }; - msrv.meets(cx, const_stab_rust_version) - } else { - // Unstable const fn, check if the feature is enabled. - cx.tcx.features().enabled(const_stab.feature) && msrv.current(cx).is_none() - } - }) + msrv.meets(cx, const_stab_rust_version) + } else { + // Unstable const fn, check if the feature is enabled. + cx.tcx.features().enabled(const_stab.feature) && msrv.current(cx).is_none() + } + }) } fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool { diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 80066e9702d34..7f2bf99daff20 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -7,13 +7,14 @@ use std::sync::Arc; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; +use rustc_lexer::{LiteralKind, TokenKind, tokenize}; use rustc_lint::{EarlyContext, LateContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::source_map::{SourceMap, original_sp}; use rustc_span::{ - BytePos, DUMMY_SP, FileNameDisplayPreference, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, - hygiene, + BytePos, DUMMY_SP, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, Span, SpanData, + SyntaxContext, hygiene, }; use std::borrow::Cow; use std::fmt; @@ -137,12 +138,25 @@ pub trait SpanRangeExt: SpanRange { fn map_range( self, cx: &impl HasSession, - f: impl for<'a> FnOnce(&'a str, Range) -> Option>, + f: impl for<'a> FnOnce(&'a SourceFile, &'a str, Range) -> Option>, ) -> Option> { map_range(cx.sess().source_map(), self.into_range(), f) } + #[allow(rustdoc::invalid_rust_codeblocks, reason = "The codeblock is intentionally broken")] /// Extends the range to include all preceding whitespace characters. + /// + /// The range will not be expanded if it would cross a line boundary, the line the range would + /// be extended to ends with a line comment and the text after the range contains a + /// non-whitespace character on the same line. e.g. + /// + /// ```ignore + /// ( // Some comment + /// foo) + /// ``` + /// + /// When the range points to `foo`, suggesting to remove the range after it's been extended will + /// cause the `)` to be placed inside the line comment as `( // Some comment)`. fn with_leading_whitespace(self, cx: &impl HasSession) -> Range { with_leading_whitespace(cx.sess().source_map(), self.into_range()) } @@ -241,11 +255,11 @@ fn with_source_text_and_range( fn map_range( sm: &SourceMap, sp: Range, - f: impl for<'a> FnOnce(&'a str, Range) -> Option>, + f: impl for<'a> FnOnce(&'a SourceFile, &'a str, Range) -> Option>, ) -> Option> { if let Some(src) = get_source_range(sm, sp.clone()) && let Some(text) = &src.sf.src - && let Some(range) = f(text, src.range.clone()) + && let Some(range) = f(&src.sf, text, src.range.clone()) { debug_assert!( range.start <= text.len() && range.end <= text.len(), @@ -262,15 +276,57 @@ fn map_range( } } +fn ends_with_line_comment_or_broken(text: &str) -> bool { + let Some(last) = tokenize(text).last() else { + return false; + }; + match last.kind { + // Will give the wrong result on text like `" // "` where the first quote ends a string + // started earlier. The only workaround is to lex the whole file which we don't really want + // to do. + TokenKind::LineComment { .. } | TokenKind::BlockComment { terminated: false, .. } => true, + TokenKind::Literal { kind, .. } => matches!( + kind, + LiteralKind::Byte { terminated: false } + | LiteralKind::ByteStr { terminated: false } + | LiteralKind::CStr { terminated: false } + | LiteralKind::Char { terminated: false } + | LiteralKind::RawByteStr { n_hashes: None } + | LiteralKind::RawCStr { n_hashes: None } + | LiteralKind::RawStr { n_hashes: None } + ), + _ => false, + } +} + +fn with_leading_whitespace_inner(lines: &[RelativeBytePos], src: &str, range: Range) -> Option { + debug_assert!(lines.is_empty() || lines[0].to_u32() == 0); + + let start = src.get(..range.start)?.trim_end(); + let next_line = lines.partition_point(|&pos| pos.to_usize() <= start.len()); + if let Some(line_end) = lines.get(next_line) + && line_end.to_usize() <= range.start + && let prev_start = lines.get(next_line - 1).map_or(0, |&x| x.to_usize()) + && ends_with_line_comment_or_broken(&start[prev_start..]) + && let next_line = lines.partition_point(|&pos| pos.to_usize() < range.end) + && let next_start = lines.get(next_line).map_or(src.len(), |&x| x.to_usize()) + && tokenize(src.get(range.end..next_start)?).any(|t| !matches!(t.kind, TokenKind::Whitespace)) + { + Some(range.start) + } else { + Some(start.len()) + } +} + fn with_leading_whitespace(sm: &SourceMap, sp: Range) -> Range { - map_range(sm, sp.clone(), |src, range| { - Some(src.get(..range.start)?.trim_end().len()..range.end) + map_range(sm, sp.clone(), |sf, src, range| { + Some(with_leading_whitespace_inner(sf.lines(), src, range.clone())?..range.end) }) .unwrap_or(sp) } fn trim_start(sm: &SourceMap, sp: Range) -> Range { - map_range(sm, sp.clone(), |src, range| { + map_range(sm, sp.clone(), |_, src, range| { let src = src.get(range.clone())?; Some(range.start + (src.len() - src.trim_start().len())..range.end) }) @@ -384,10 +440,10 @@ pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { // For some reason these attributes don't have any expansion info on them, so // we have to check it this way until there is a better way. pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool { - if let Some(snippet) = snippet_opt(sess, span) { - if snippet.is_empty() { - return false; - } + if let Some(snippet) = snippet_opt(sess, span) + && snippet.is_empty() + { + return false; } true } @@ -408,11 +464,11 @@ pub fn position_before_rarrow(s: &str) -> Option { let mut rpos = rpos; let chars: Vec = s.chars().collect(); while rpos > 1 { - if let Some(c) = chars.get(rpos - 1) { - if c.is_whitespace() { - rpos -= 1; - continue; - } + if let Some(c) = chars.get(rpos - 1) + && c.is_whitespace() + { + rpos -= 1; + continue; } break; } diff --git a/src/tools/clippy/clippy_utils/src/str_utils.rs b/src/tools/clippy/clippy_utils/src/str_utils.rs index 421b25a77fe8b..f0f82c8dddcf5 100644 --- a/src/tools/clippy/clippy_utils/src/str_utils.rs +++ b/src/tools/clippy/clippy_utils/src/str_utils.rs @@ -1,4 +1,4 @@ -/// Dealing with sting indices can be hard, this struct ensures that both the +/// Dealing with string indices can be hard, this struct ensures that both the /// character and byte index are provided for correct indexing. #[derive(Debug, Default, PartialEq, Eq)] pub struct StrIndex { @@ -165,7 +165,7 @@ pub fn camel_case_split(s: &str) -> Vec<&str> { offsets.windows(2).map(|w| &s[w[0]..w[1]]).collect() } -/// Dealing with sting comparison can be complicated, this struct ensures that both the +/// Dealing with string comparison can be complicated, this struct ensures that both the /// character and byte count are provided for correct indexing. #[derive(Debug, Default, PartialEq, Eq)] pub struct StrCount { diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 4a9ab17d4a609..93dec113d31a5 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -4,8 +4,8 @@ use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_context}; use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; -use rustc_ast::util::parser::AssocOp; use rustc_ast::ast; +use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; @@ -147,6 +147,7 @@ impl<'a> Sugg<'a> { | ExprKind::Become(..) | ExprKind::Struct(..) | ExprKind::Tup(..) + | ExprKind::Use(..) | ExprKind::Err(_) | ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(get_snippet(expr.span)), ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet), @@ -217,6 +218,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Try(..) | ast::ExprKind::TryBlock(..) | ast::ExprKind::Tup(..) + | ast::ExprKind::Use(..) | ast::ExprKind::Array(..) | ast::ExprKind::While(..) | ast::ExprKind::Await(..) @@ -324,7 +326,7 @@ impl<'a> Sugg<'a> { /// `self` argument of a method call /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`). #[must_use] - pub fn maybe_par(self) -> Self { + pub fn maybe_paren(self) -> Self { match self { Sugg::NonParen(..) => self, // `(x)` and `(x).y()` both don't need additional parens. @@ -355,7 +357,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { match op { AssocOp::Binary(op) => format!("{lhs} {} {rhs}", op.as_str()), AssocOp::Assign => format!("{lhs} = {rhs}"), - AssocOp::AssignOp(op) => format!("{lhs} {}= {rhs}", op.as_str()), + AssocOp::AssignOp(op) => format!("{lhs} {} {rhs}", op.as_str()), AssocOp::Cast => format!("{lhs} as {rhs}"), AssocOp::Range(limits) => format!("{lhs}{}{rhs}", limits.as_str()), } @@ -442,7 +444,7 @@ impl<'a> Not for Sugg<'a> { type Output = Sugg<'a>; fn not(self) -> Sugg<'a> { use AssocOp::Binary; - use ast::BinOpKind::{Eq, Gt, Ge, Lt, Le, Ne}; + use ast::BinOpKind::{Eq, Ge, Gt, Le, Lt, Ne}; if let Sugg::BinOp(op, lhs, rhs) = self { let to_op = match op { @@ -492,7 +494,7 @@ impl Display for ParenHelper { /// operators have the same /// precedence. pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> { - Sugg::MaybeParen(format!("{op}{}", expr.maybe_par()).into()) + Sugg::MaybeParen(format!("{op}{}", expr.maybe_paren()).into()) } /// Builds the string for ` ` adding parenthesis when necessary. @@ -513,10 +515,10 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> op, AssocOp::Binary( ast::BinOpKind::Add - | ast::BinOpKind::Sub - | ast::BinOpKind::Mul - | ast::BinOpKind::Div - | ast::BinOpKind::Rem + | ast::BinOpKind::Sub + | ast::BinOpKind::Mul + | ast::BinOpKind::Div + | ast::BinOpKind::Rem ) ) } @@ -576,10 +578,8 @@ enum Associativity { /// associative. #[must_use] fn associativity(op: AssocOp) -> Associativity { + use ast::BinOpKind::{Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub}; use rustc_ast::util::parser::AssocOp::{Assign, AssignOp, Binary, Cast, Range}; - use ast::BinOpKind::{ - Add, BitAnd, BitOr, BitXor, Div, Eq, Gt, Ge, And, Or, Lt, Le, Rem, Mul, Ne, Shl, Shr, Sub, - }; match op { Assign | AssignOp(_) => Associativity::Right, @@ -835,15 +835,16 @@ impl<'tcx> DerefDelegate<'_, 'tcx> { impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) { if let PlaceBase::Local(id) = cmt.place.base { - let map = self.cx.tcx.hir(); - let span = map.span(cmt.hir_id); + let span = self.cx.tcx.hir_span(cmt.hir_id); let start_span = Span::new(self.next_pos, span.lo(), span.ctxt(), None); let mut start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability); // identifier referring to the variable currently triggered (i.e.: `fp`) - let ident_str = map.name(id).to_string(); + let ident_str = self.cx.tcx.hir_name(id).to_string(); // full identifier that includes projection (i.e.: `fp.field`) let ident_str_with_proj = snippet(self.cx, span, "..").to_string(); @@ -872,7 +873,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // item is used in a call // i.e.: `Call`: `|x| please(x)` or `MethodCall`: `|x| [1, 2, 3].contains(x)` ExprKind::Call(_, call_args) | ExprKind::MethodCall(_, _, call_args, _) => { - let expr = self.cx.tcx.hir().expect_expr(cmt.hir_id); + let expr = self.cx.tcx.hir_expect_expr(cmt.hir_id); let arg_ty_kind = self.cx.typeck_results().expr_ty(expr).kind(); if matches!(arg_ty_kind, ty::Ref(_, _, Mutability::Not)) { @@ -945,10 +946,9 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // some items do not need explicit deref, such as array accesses, // so we mark them as already processed // i.e.: don't suggest `*sub[1..4].len()` for `|sub| sub[1..4].len() == 3` - if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() { - if matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) { - projections_handled = true; - } + if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() + && matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) { + projections_handled = true; } }, } @@ -990,6 +990,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { mod test { use super::Sugg; + use rustc_ast as ast; use rustc_ast::util::parser::AssocOp; use std::borrow::Cow; @@ -1006,16 +1007,16 @@ mod test { } #[test] - fn binop_maybe_par() { - let sugg = Sugg::BinOp(AssocOp::Add, "1".into(), "1".into()); - assert_eq!("(1 + 1)", sugg.maybe_par().to_string()); + fn binop_maybe_paren() { + let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "1".into(), "1".into()); + assert_eq!("(1 + 1)", sugg.maybe_paren().to_string()); - let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1)".into(), "(1 + 1)".into()); - assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string()); + let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "(1 + 1)".into(), "(1 + 1)".into()); + assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_paren().to_string()); } #[test] fn not_op() { - use AssocOp::{Add, Equal, Greater, GreaterEqual, LAnd, LOr, Less, LessEqual, NotEqual}; + use ast::BinOpKind::{Add, And, Eq, Ge, Gt, Le, Lt, Ne, Or}; fn test_not(op: AssocOp, correct: &str) { let sugg = Sugg::BinOp(op, "x".into(), "y".into()); @@ -1023,16 +1024,16 @@ mod test { } // Invert the comparison operator. - test_not(Equal, "x != y"); - test_not(NotEqual, "x == y"); - test_not(Less, "x >= y"); - test_not(LessEqual, "x > y"); - test_not(Greater, "x <= y"); - test_not(GreaterEqual, "x < y"); + test_not(AssocOp::Binary(Eq), "x != y"); + test_not(AssocOp::Binary(Ne), "x == y"); + test_not(AssocOp::Binary(Lt), "x >= y"); + test_not(AssocOp::Binary(Le), "x > y"); + test_not(AssocOp::Binary(Gt), "x <= y"); + test_not(AssocOp::Binary(Ge), "x < y"); // Other operators are inverted like !(..). - test_not(Add, "!(x + y)"); - test_not(LAnd, "!(x && y)"); - test_not(LOr, "!(x || y)"); + test_not(AssocOp::Binary(Add), "!(x + y)"); + test_not(AssocOp::Binary(And), "!(x && y)"); + test_not(AssocOp::Binary(Or), "!(x || y)"); } } diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs new file mode 100644 index 0000000000000..f417530be3672 --- /dev/null +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -0,0 +1,358 @@ +#![allow(non_upper_case_globals)] + +use rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT; + +#[doc(no_inline)] +pub use rustc_span::sym::*; + +macro_rules! val { + ($name:ident) => { + stringify!($name) + }; + ($name:ident $value:literal) => { + $value + }; +} + +macro_rules! generate { + ($($name:ident $(: $value:literal)? ,)*) => { + /// To be supplied to `rustc_interface::Config` + pub const EXTRA_SYMBOLS: &[&str] = &[ + $( + val!($name $($value)?), + )* + ]; + + $( + pub const $name: rustc_span::Symbol = rustc_span::Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()}); + )* + }; +} + +// List of extra symbols to be included in Clippy (for example, as `sym::ambiguous_glob_reexports`). +// An alternative content can be specified using a colon after the symbol name. +// +// `cargo dev fmt` ensures that the content of the `generate!()` macro call stays sorted. +generate! { + AsyncReadExt, + AsyncWriteExt, + BACKSLASH_SINGLE_QUOTE: r"\'", + Binary, + CLIPPY_ARGS, + CLIPPY_CONF_DIR, + CRLF: "\r\n", + Cargo_toml: "Cargo.toml", + Current, + DOUBLE_QUOTE: "\"", + Deserialize, + EarlyLintPass, + ErrorKind, + IntoIter, + Itertools, + LF: "\n", + Lazy, + Lint, + LowerExp, + LowerHex, + MAX, + MIN, + MsrvStack, + Octal, + OpenOptions, + Other, + PathLookup, + Regex, + RegexBuilder, + RegexSet, + Start, + Step, + Symbol, + SyntaxContext, + TBD, + UpperExp, + UpperHex, + V4, + V6, + Visitor, + Weak, + abs, + align_of, + ambiguous_glob_reexports, + append, + arg, + as_bytes, + as_deref, + as_deref_mut, + as_mut, + assert_failed, + author, + borrow, + borrow_mut, + build_hasher, + by_ref, + bytes, + capacity, + cargo_clippy: "cargo-clippy", + cast, + cast_const, + cast_mut, + ceil, + ceil_char_boundary, + chain, + chars, + checked_abs, + checked_add, + checked_isqrt, + checked_mul, + checked_pow, + checked_rem_euclid, + checked_sub, + clamp, + clippy_utils, + clone_into, + cloned, + cognitive_complexity, + collect, + const_ptr, + contains, + copied, + copy_from, + copy_from_nonoverlapping, + copy_to, + copy_to_nonoverlapping, + count_ones, + cycle, + cyclomatic_complexity, + de, + diagnostics, + disallowed_types, + drain, + dump, + ends_with, + enum_glob_use, + enumerate, + err, + error, + exp, + expect_err, + expn_data, + extend, + filter, + filter_map, + find, + find_map, + finish, + finish_non_exhaustive, + first, + flat_map, + flatten, + floor, + floor_char_boundary, + fold, + for_each, + from_bytes_with_nul, + from_bytes_with_nul_unchecked, + from_ptr, + from_raw, + from_ref, + from_str, + from_str_radix, + fs, + fuse, + futures_util, + get, + get_mut, + get_or_insert_with, + get_unchecked, + get_unchecked_mut, + has_significant_drop, + hidden_glob_reexports, + hygiene, + if_chain, + insert, + inspect, + int_roundings, + into, + into_bytes, + into_ok, + into_owned, + io, + is_ascii, + is_char_boundary, + is_digit, + is_empty, + is_err, + is_file, + is_none, + is_ok, + is_some, + isqrt, + itertools, + join, + kw, + last, + lazy_static, + ln, + lock, + lock_api, + log, + log10, + log2, + macro_use_imports, + map_break, + map_continue, + map_or, + map_or_else, + match_indices, + matches, + max, + max_by, + max_by_key, + max_value, + maximum, + mem, + min, + min_by, + min_by_key, + min_value, + minimum, + mode, + module_name_repetitions, + msrv, + msrvs, + mut_ptr, + mutex, + needless_return, + next_back, + next_if, + next_if_eq, + next_tuple, + nth, + ok, + ok_or, + once_cell, + open, + or_default, + or_else, + or_insert, + or_insert_with, + outer_expn, + panic_cold_display, + panic_cold_explicit, + panic_display, + panic_str, + parse, + partition, + paths, + peek, + peek_mut, + peekable, + pow, + powf, + powi, + product, + push, + read_line, + read_to_end, + read_to_string, + redundant_pub_crate, + regex, + rem_euclid, + repeat, + replace, + replacen, + reserve, + resize, + restriction, + rev, + rfind, + rmatch_indices, + rmatches, + round, + rposition, + rsplit, + rsplit_once, + rsplit_terminator, + rsplitn, + rsplitn_mut, + rustc_lint, + rustc_lint_defs, + rustc_span, + rustfmt_skip, + rwlock, + saturating_abs, + saturating_pow, + scan, + seek, + serde, + set_len, + set_mode, + set_readonly, + signum, + single_component_path_imports, + skip_while, + slice_mut_unchecked, + slice_unchecked, + sort, + sort_by, + sort_unstable_by, + span_lint_and_then, + split, + split_at, + split_at_checked, + split_at_mut, + split_at_mut_checked, + split_inclusive, + split_once, + split_terminator, + split_whitespace, + splitn, + splitn_mut, + sqrt, + starts_with, + step_by, + strlen, + style, + subsec_micros, + subsec_nanos, + sum, + symbol, + take, + take_while, + then, + then_some, + to_ascii_lowercase, + to_ascii_uppercase, + to_digit, + to_lowercase, + to_os_string, + to_owned, + to_path_buf, + to_uppercase, + tokio, + trim, + trim_end_matches, + trim_start_matches, + unreachable_pub, + unsafe_removed_from_name, + unused, + unused_braces, + unused_extern_crates, + unused_import_braces, + unused_trait_names, + unwrap_err, + unwrap_err_unchecked, + unwrap_or_default, + unwrap_or_else, + unwrap_unchecked, + unzip, + utils, + wake, + warnings, + wildcard_imports, + with_capacity, + wrapping_offset, + write, + writeln, + zip, +} diff --git a/src/tools/clippy/clippy_utils/src/sym_helper.rs b/src/tools/clippy/clippy_utils/src/sym_helper.rs deleted file mode 100644 index f47dc80ebade8..0000000000000 --- a/src/tools/clippy/clippy_utils/src/sym_helper.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[macro_export] -/// Convenience wrapper around rustc's `Symbol::intern` -macro_rules! sym { - ($tt:tt) => { - rustc_span::symbol::Symbol::intern(stringify!($tt)) - }; -} diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 6fdf4c244f8d8..c50ad17bfad1e 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -19,8 +19,8 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocItem, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, - GenericArgsRef, GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, + self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, + GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; @@ -32,7 +32,8 @@ use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; use std::iter; -use crate::{def_path_def_ids, match_def_path, path_res}; +use crate::path_res; +use crate::paths::{PathNS, lookup_path_str}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -128,10 +129,10 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' // For `impl Trait`, it will register a predicate of `::Assoc = U`, // so we check the term for `U`. ty::ClauseKind::Projection(projection_predicate) => { - if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() { - if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) { - return true; - } + if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() + && contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) + { + return true; } }, _ => (), @@ -156,7 +157,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { cx.tcx .get_diagnostic_item(sym::Iterator) - .and_then(|iter_did| cx.get_associated_type(ty, iter_did, "Item")) + .and_then(|iter_did| cx.get_associated_type(ty, iter_did, sym::Item)) } /// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type @@ -229,9 +230,7 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option< /// Checks whether a type implements a trait. /// The function returns false in case the type contains an inference variable. /// -/// See: -/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`]. -/// * [Common tools for writing lints] for an example how to use this function and other options. +/// See [Common tools for writing lints] for an example how to use this function and other options. /// /// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait pub fn implements_trait<'tcx>( @@ -337,20 +336,20 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)), ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => { for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() { - if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() { - if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { - return true; - } + if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) + { + return true; } } false }, ty::Dynamic(binder, _, _) => { for predicate in *binder { - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { - if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) { - return true; - } + if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() + && cx.tcx.has_attr(trait_ref.def_id, sym::must_use) + { + return true; } } false @@ -359,56 +358,6 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } -// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize -// this function can be removed once the `normalize` method does not panic when normalization does -// not succeed -/// Checks if `Ty` is normalizable. This function is useful -/// to avoid crashes on `layout_of`. -pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { - is_normalizable_helper(cx, param_env, ty, 0, &mut FxHashMap::default()) -} - -fn is_normalizable_helper<'tcx>( - cx: &LateContext<'tcx>, - param_env: ParamEnv<'tcx>, - ty: Ty<'tcx>, - depth: usize, - cache: &mut FxHashMap, bool>, -) -> bool { - if let Some(&cached_result) = cache.get(&ty) { - return cached_result; - } - if !cx.tcx.recursion_limit().value_within_limit(depth) { - return false; - } - // Prevent recursive loops by answering `true` to recursive requests with the same - // type. This will be adjusted when the outermost call analyzes all the type - // components. - cache.insert(ty, true); - let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); - let cause = ObligationCause::dummy(); - let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() { - match ty.kind() { - ty::Adt(def, args) => def.variants().iter().all(|variant| { - variant - .fields - .iter() - .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, args), depth + 1, cache)) - }), - _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { - GenericArgKind::Type(inner_ty) if inner_ty != ty => { - is_normalizable_helper(cx, param_env, inner_ty, depth + 1, cache) - }, - _ => true, // if inner_ty == ty, we've already checked it - }), - } - } else { - false - }; - cache.insert(ty, result); - result -} - /// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any /// integer or floating-point number type). /// @@ -474,17 +423,6 @@ pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) } -/// Checks if type is struct, enum or union type with the given def path. -/// -/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead. -/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` -pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { - match ty.kind() { - ty::Adt(adt, _) => match_def_path(cx, adt.did(), path), - _ => false, - } -} - /// Checks if the drop order for a type matters. /// /// Some std types implement drop solely to deallocate memory. For these types, and composites @@ -915,7 +853,7 @@ pub fn for_each_top_level_late_bound_region( ControlFlow::Continue(()) } } - fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result { + fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result { self.index += 1; let res = t.super_visit_with(self); self.index -= 1; @@ -993,9 +931,6 @@ pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option< /// account the layout of type parameters. pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { use rustc_middle::ty::layout::LayoutOf; - if !is_normalizable(cx, cx.param_env, ty) { - return 0; - } match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) { (Ok(size), _) => size, (Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(), @@ -1109,10 +1044,10 @@ pub fn make_projection<'tcx>( assoc_ty: Symbol, args: GenericArgsRef<'tcx>, ) -> Option> { - let Some(assoc_item) = tcx.associated_items(container_id).find_by_name_and_kind( + let Some(assoc_item) = tcx.associated_items(container_id).find_by_ident_and_kind( tcx, Ident::with_dummy_span(assoc_ty), - AssocKind::Type, + AssocTag::Type, container_id, ) else { debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`"); @@ -1184,10 +1119,7 @@ impl<'tcx> InteriorMut<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, ignore_interior_mutability: &[String]) -> Self { let ignored_def_ids = ignore_interior_mutability .iter() - .flat_map(|ignored_ty| { - let path: Vec<&str> = ignored_ty.split("::").collect(); - def_path_def_ids(tcx, path.as_slice()) - }) + .flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty)) .collect(); Self { @@ -1345,14 +1277,14 @@ pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_n .associated_items(did) .filter_by_name_unhygienic(method_name) .next() - .filter(|item| item.kind == AssocKind::Fn) + .filter(|item| item.as_tag() == AssocTag::Fn) }) } else { None } } -/// Get's the type of a field by name. +/// Gets the type of a field by name. pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> Option> { match *ty.kind() { ty::Adt(def, args) if def.is_union() || def.is_struct() => def @@ -1376,3 +1308,67 @@ pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option None, } } + +/// Check if a Ty<'_> of `Iterator` contains any mutable access to non-owning types by checking if +/// it contains fields of mutable references or pointers, or references/pointers to non-`Freeze` +/// types, or `PhantomData` types containing any of the previous. This can be used to check whether +/// skipping iterating over an iterator will change its behavior. +pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>) -> bool { + fn normalize_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty) + } + + /// Check if `ty` contains mutable references or equivalent, which includes: + /// - A mutable reference/pointer. + /// - A reference/pointer to a non-`Freeze` type. + /// - A `PhantomData` type containing any of the previous. + fn has_non_owning_mutable_access_inner<'tcx>( + cx: &LateContext<'tcx>, + phantoms: &mut FxHashSet>, + ty: Ty<'tcx>, + ) -> bool { + match ty.kind() { + ty::Adt(adt_def, args) if adt_def.is_phantom_data() => { + phantoms.insert(ty) + && args + .types() + .any(|arg_ty| has_non_owning_mutable_access_inner(cx, phantoms, arg_ty)) + }, + ty::Adt(adt_def, args) => adt_def.all_fields().any(|field| { + has_non_owning_mutable_access_inner(cx, phantoms, normalize_ty(cx, field.ty(cx.tcx, args))) + }), + ty::Array(elem_ty, _) | ty::Slice(elem_ty) => has_non_owning_mutable_access_inner(cx, phantoms, *elem_ty), + ty::RawPtr(pointee_ty, mutability) | ty::Ref(_, pointee_ty, mutability) => { + mutability.is_mut() || !pointee_ty.is_freeze(cx.tcx, cx.typing_env()) + }, + ty::Closure(_, closure_args) => { + matches!(closure_args.types().next_back(), Some(captures) if has_non_owning_mutable_access_inner(cx, phantoms, captures)) + }, + ty::Tuple(tuple_args) => tuple_args + .iter() + .any(|arg_ty| has_non_owning_mutable_access_inner(cx, phantoms, arg_ty)), + _ => false, + } + } + + let mut phantoms = FxHashSet::default(); + has_non_owning_mutable_access_inner(cx, &mut phantoms, iter_ty) +} + +/// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec`. +pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_slice() + || ty.is_array() + || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())) +} + +/// Gets the index of a field by name. +pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { + match *ty.kind() { + ty::Adt(def, _) if def.is_union() || def.is_struct() => { + def.non_enum_variant().fields.iter().position(|f| f.name == name) + }, + ty::Tuple(_) => name.as_str().parse::().ok(), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index 3398ff8af2f5a..6e3586623277f 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -11,14 +11,14 @@ //! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should //! be considered a bug. -use crate::def_path_res; +use crate::paths::{PathNS, lookup_path}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_qpath, walk_ty}; use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; mod certainty; use certainty::{Certainty, Meet, join, meet}; @@ -194,7 +194,7 @@ fn path_segment_certainty( path_segment: &PathSegment<'_>, resolves_to_type: bool, ) -> Certainty { - let certainty = match update_res(cx, parent_certainty, path_segment).unwrap_or(path_segment.res) { + let certainty = match update_res(cx, parent_certainty, path_segment, resolves_to_type).unwrap_or(path_segment.res) { // A definition's type is certain if it refers to something without generics (e.g., a crate or module, or // an unparameterized type), or the generics are instantiated with arguments that are certain. // @@ -267,17 +267,24 @@ fn path_segment_certainty( /// For at least some `QPath::TypeRelative`, the path segment's `res` can be `Res::Err`. /// `update_res` tries to fix the resolution when `parent_certainty` is `Certain(Some(..))`. -fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: &PathSegment<'_>) -> Option { +fn update_res( + cx: &LateContext<'_>, + parent_certainty: Certainty, + path_segment: &PathSegment<'_>, + resolves_to_type: bool, +) -> Option { if path_segment.res == Res::Err && let Some(def_id) = parent_certainty.to_def_id() { let mut def_path = cx.get_def_path(def_id); def_path.push(path_segment.ident.name); - let reses = def_path_res(cx.tcx, &def_path.iter().map(Symbol::as_str).collect::>()); - if let [res] = reses.as_slice() { Some(*res) } else { None } - } else { - None + let ns = if resolves_to_type { PathNS::Type } else { PathNS::Value }; + if let &[id] = lookup_path(cx.tcx, ns, &def_path).as_slice() { + return Some(Res::Def(cx.tcx.def_kind(id), id)); + } } + + None } #[allow(clippy::cast_possible_truncation)] diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index 3bf518f7fe706..1b049b6d12c4c 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -66,6 +66,8 @@ impl MutVarsDelegate { impl<'tcx> Delegate<'tcx> for MutVarsDelegate { fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) { if bk == ty::BorrowKind::Mutable { self.update(cmt); @@ -124,10 +126,10 @@ impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) -> Self::Result { - if let Res::Local(id) = path.res { - if self.binding_ids.contains(&id) { - return ControlFlow::Break(()); - } + if let Res::Local(id) = path.res + && self.binding_ids.contains(&id) + { + return ControlFlow::Break(()); } ControlFlow::Continue(()) diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 2ac0efd7e392d..fc6e30a980476 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -297,10 +297,10 @@ where /// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { for_each_expr(cx, cx.tcx.hir_body(body).value, |e| { - if let ExprKind::Path(p) = &e.kind { - if cx.qpath_res(p, e.hir_id) == res { - return ControlFlow::Break(()); - } + if let ExprKind::Path(p) = &e.kind + && cx.qpath_res(p, e.hir_id) == res + { + return ControlFlow::Break(()); } ControlFlow::Continue(()) }) @@ -648,6 +648,9 @@ pub fn for_each_unconsumed_temporary<'tcx, B>( helper(typeck, true, arg, f)?; } }, + ExprKind::Use(expr, _) => { + helper(typeck, true, expr, f)?; + }, ExprKind::Index(borrowed, consumed, _) | ExprKind::Assign(borrowed, consumed, _) | ExprKind::AssignOp(_, borrowed, consumed) => { diff --git a/src/tools/clippy/lintcheck/ci-config/clippy.toml b/src/tools/clippy/lintcheck/ci-config/clippy.toml new file mode 100644 index 0000000000000..9853465c83f00 --- /dev/null +++ b/src/tools/clippy/lintcheck/ci-config/clippy.toml @@ -0,0 +1,7 @@ +# Configuration applied when running lintcheck from the CI +# +# The CI will set the `CLIPPY_CONF_DIR` environment variable +# to `$PWD/lintcheck/ci-config`. + +avoid-breaking-exported-api = false +lint-commented-code = false diff --git a/src/tools/clippy/lintcheck/src/json.rs b/src/tools/clippy/lintcheck/src/json.rs index 3a68f2c924356..8ea0a41ed368a 100644 --- a/src/tools/clippy/lintcheck/src/json.rs +++ b/src/tools/clippy/lintcheck/src/json.rs @@ -1,3 +1,9 @@ +//! JSON output and comparison functionality for Clippy warnings. +//! +//! This module handles serialization of Clippy warnings to JSON format, +//! loading warnings from JSON files, and generating human-readable diffs +//! between different linting runs. + use std::fs; use std::path::Path; @@ -8,8 +14,10 @@ use crate::ClippyWarning; /// This is the total number. 300 warnings results in 100 messages per section. const DEFAULT_LIMIT_PER_LINT: usize = 300; +/// Target for total warnings to display across all lints when truncating output. const TRUNCATION_TOTAL_TARGET: usize = 1000; +/// Representation of a single Clippy warning for JSON serialization. #[derive(Debug, Deserialize, Serialize)] struct LintJson { /// The lint name e.g. `clippy::bytes_nth` @@ -21,10 +29,12 @@ struct LintJson { } impl LintJson { + /// Returns a tuple of name and `file_line` for sorting and comparison. fn key(&self) -> impl Ord + '_ { (self.name.as_str(), self.file_line.as_str()) } + /// Formats the warning information with an action verb for display. fn info_text(&self, action: &str) -> String { format!("{action} `{}` at [`{}`]({})", self.name, self.file_line, self.file_url) } @@ -53,12 +63,17 @@ pub(crate) fn output(clippy_warnings: Vec) -> String { serde_json::to_string(&lints).unwrap() } +/// Loads lint warnings from a JSON file at the given path. fn load_warnings(path: &Path) -> Vec { let file = fs::read(path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display())); serde_json::from_slice(&file).unwrap_or_else(|e| panic!("failed to deserialize {}: {e}", path.display())) } +/// Generates and prints a diff between two sets of lint warnings. +/// +/// Compares warnings from `old_path` and `new_path`, then displays a summary table +/// and detailed information about added, removed, and changed warnings. pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { let old_warnings = load_warnings(old_path); let new_warnings = load_warnings(new_path); @@ -116,6 +131,7 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { } } +/// Container for grouped lint warnings organized by status (added/removed/changed). #[derive(Debug)] struct LintWarnings { name: String, @@ -124,6 +140,7 @@ struct LintWarnings { changed: Vec<(LintJson, LintJson)>, } +/// Prints a formatted report for a single lint type with its warnings. fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { let name = &lint.name; let html_id = to_html_id(name); @@ -145,6 +162,7 @@ fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { print_changed_diff(&lint.changed, truncate_after / 3); } +/// Prints a summary table of all lints with counts of added, removed, and changed warnings. fn print_summary_table(lints: &[LintWarnings]) { println!("| Lint | Added | Removed | Changed |"); println!("| ------------------------------------------ | ------: | ------: | ------: |"); @@ -160,6 +178,7 @@ fn print_summary_table(lints: &[LintWarnings]) { } } +/// Prints a section of warnings with a header and formatted code blocks. fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) { if warnings.is_empty() { return; @@ -180,6 +199,7 @@ fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) { } } +/// Prints a section of changed warnings with unified diff format. fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) { if changed.is_empty() { return; @@ -213,6 +233,7 @@ fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) { } } +/// Truncates a list to a maximum number of items and prints a message about truncation. fn truncate(list: &[T], truncate_after: usize) -> &[T] { if list.len() > truncate_after { println!( @@ -227,6 +248,7 @@ fn truncate(list: &[T], truncate_after: usize) -> &[T] { } } +/// Prints a level 3 heading with an appropriate HTML ID for linking. fn print_h3(lint: &str, title: &str) { let html_id = to_html_id(lint); // We have to use HTML here to be able to manually add an id. diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index 8d0d41ab9450f..d4bf6cd48a152 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -6,7 +6,6 @@ // positives. #![feature(iter_collect_into)] -#![feature(let_chains)] #![warn( trivial_casts, trivial_numeric_casts, @@ -120,14 +119,17 @@ impl Crate { if config.perf { cmd = Command::new("perf"); + let perf_data_filename = get_perf_data_filename(&self.path); cmd.args(&[ "record", "-e", "instructions", // Only count instructions "-g", // Enable call-graph, useful for flamegraphs and produces richer reports "--quiet", // Do not tamper with lintcheck's normal output + "--compression-level=22", + "--freq=3000", // Slow down program to capture all events "-o", - "perf.data", + &perf_data_filename, "--", "cargo", ]); @@ -165,7 +167,7 @@ impl Crate { return Vec::new(); } - if !config.fix { + if !config.fix && !config.perf { cmd.arg("--message-format=json"); } @@ -203,6 +205,11 @@ impl Crate { return Vec::new(); } + // We don't want to keep target directories if benchmarking + if config.perf { + let _ = fs::remove_dir_all(&shared_target_dir); + } + // get all clippy warnings and ICEs let mut entries: Vec = Message::parse_stream(stdout.as_bytes()) .filter_map(|msg| match msg { @@ -441,6 +448,35 @@ fn lintcheck(config: LintcheckConfig) { fs::write(&config.lintcheck_results_path, text).unwrap(); } +/// Traverse a directory looking for `perf.data.` files, and adds one +/// to the most recent of those files, returning the new most recent `perf.data` +/// file name. +fn get_perf_data_filename(source_path: &Path) -> String { + if source_path.join("perf.data").exists() { + let mut max_number = 0; + fs::read_dir(source_path) + .unwrap() + .filter_map(Result::ok) + .filter(|path| { + path.file_name() + .as_os_str() + .to_string_lossy() // We don't care about data loss, as we're checking for equality + .starts_with("perf.data") + }) + .for_each(|path| { + let file_name = path.file_name(); + let file_name = file_name.as_os_str().to_str().unwrap().split('.').next_back().unwrap(); + if let Ok(parsed_file_name) = file_name.parse::() + && parsed_file_name >= max_number + { + max_number = parsed_file_name + 1; + } + }); + return format!("perf.data.{max_number}"); + } + String::from("perf.data") +} + /// Returns the path to the Clippy project directory #[must_use] fn clippy_project_root() -> &'static Path { diff --git a/src/tools/clippy/lintcheck/src/recursive.rs b/src/tools/clippy/lintcheck/src/recursive.rs index 57073f523648e..6406b2dcb643b 100644 --- a/src/tools/clippy/lintcheck/src/recursive.rs +++ b/src/tools/clippy/lintcheck/src/recursive.rs @@ -64,7 +64,7 @@ fn process_stream( // It's 99% likely that dependencies compiled with recursive mode are on crates.io // and therefore on docs.rs. This links to the sources directly, do avoid invalid - // links due to remaped paths. See rust-lang/docs.rs#2551 for more details. + // links due to remapped paths. See rust-lang/docs.rs#2551 for more details. let base_url = format!( "https://docs.rs/crate/{}/{}/source/src/{{file}}#{{line}}", driver_info.package_name, driver_info.version diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain deleted file mode 100644 index a4931499c8028..0000000000000 --- a/src/tools/clippy/rust-toolchain +++ /dev/null @@ -1,6 +0,0 @@ -[toolchain] -# begin autogenerated nightly -channel = "nightly-2025-02-27" -# end autogenerated nightly -components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] -profile = "minimal" diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml new file mode 100644 index 0000000000000..0b9ebe554f5b4 --- /dev/null +++ b/src/tools/clippy/rust-toolchain.toml @@ -0,0 +1,6 @@ +[toolchain] +# begin autogenerated nightly +channel = "nightly-2025-05-21" +# end autogenerated nightly +components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] +profile = "minimal" diff --git a/src/tools/clippy/rustc_tools_util/README.md b/src/tools/clippy/rustc_tools_util/README.md index ff4ca6f830e6e..f47a4c69c2c32 100644 --- a/src/tools/clippy/rustc_tools_util/README.md +++ b/src/tools/clippy/rustc_tools_util/README.md @@ -51,7 +51,7 @@ The changelog for `rustc_tools_util` is available under: -Copyright 2014-2024 The Rust Project Developers +Copyright 2014-2025 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/src/tools/clippy/rustc_tools_util/src/lib.rs b/src/tools/clippy/rustc_tools_util/src/lib.rs index 423154a69fa0b..b45edf2345585 100644 --- a/src/tools/clippy/rustc_tools_util/src/lib.rs +++ b/src/tools/clippy/rustc_tools_util/src/lib.rs @@ -121,7 +121,7 @@ fn get_output(cmd: &str, args: &[&str]) -> Option { pub fn rerun_if_git_changes() -> Option<()> { // Make sure we get rerun when the git commit changes. // We want to watch two files: HEAD, which tracks which branch we are on, - // and the file for that branch that tracks which commit is is on. + // and the file for that branch that tracks which commit is checked out. // First, find the `HEAD` file. This should work even with worktrees. let git_head_file = PathBuf::from(get_output("git", &["rev-parse", "--git-path", "HEAD"])?); diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index e4092bcd10564..37adb14169a3f 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -1,7 +1,6 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![feature(rustc_private)] -#![feature(let_chains)] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] // warn on rustc internal lints @@ -14,6 +13,7 @@ extern crate rustc_interface; extern crate rustc_session; extern crate rustc_span; +use clippy_utils::sym; use rustc_interface::interface; use rustc_session::EarlyDiagCtxt; use rustc_session::config::ErrorOutputType; @@ -78,7 +78,7 @@ fn track_clippy_args(psess: &mut ParseSess, args_env_var: Option<&str>) { psess .env_depinfo .get_mut() - .insert((Symbol::intern("CLIPPY_ARGS"), args_env_var.map(Symbol::intern))); + .insert((sym::CLIPPY_ARGS, args_env_var.map(Symbol::intern))); } /// Track files that may be accessed at runtime in `file_depinfo` so that cargo will re-run clippy @@ -89,23 +89,18 @@ fn track_files(psess: &mut ParseSess) { // Used by `clippy::cargo` lints and to determine the MSRV. `cargo clippy` executes `clippy-driver` // with the current directory set to `CARGO_MANIFEST_DIR` so a relative path is fine if Path::new("Cargo.toml").exists() { - file_depinfo.insert(Symbol::intern("Cargo.toml")); + file_depinfo.insert(sym::Cargo_toml); } // `clippy.toml` will be automatically tracked as it's loaded with `sess.source_map().load_file()` // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever // it is rebuilt - #[expect( - clippy::collapsible_if, - reason = "Due to a bug in let_chains this if statement can't be collapsed" - )] - if cfg!(debug_assertions) { - if let Ok(current_exe) = env::current_exe() - && let Some(current_exe) = current_exe.to_str() - { - file_depinfo.insert(Symbol::intern(current_exe)); - } + if cfg!(debug_assertions) + && let Ok(current_exe) = env::current_exe() + && let Some(current_exe) = current_exe.to_str() + { + file_depinfo.insert(Symbol::intern(current_exe)); } } @@ -145,7 +140,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { // Trigger a rebuild if CLIPPY_CONF_DIR changes. The value must be a valid string so // changes between dirs that are invalid UTF-8 will not trigger rebuilds psess.env_depinfo.get_mut().insert(( - Symbol::intern("CLIPPY_CONF_DIR"), + sym::CLIPPY_CONF_DIR, env::var("CLIPPY_CONF_DIR").ok().map(|dir| Symbol::intern(&dir)), )); })); @@ -158,8 +153,10 @@ impl rustc_driver::Callbacks for ClippyCallbacks { let conf = clippy_config::Conf::read(sess, &conf_path); clippy_lints::register_lints(lint_store, conf); - clippy_lints::register_pre_expansion_lints(lint_store, conf); + #[cfg(feature = "internal")] + clippy_lints_internal::register_lints(lint_store); })); + config.extra_symbols = sym::EXTRA_SYMBOLS.into(); // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be // run on the unoptimized MIR. On the other hand this results in some false negatives. If @@ -207,12 +204,12 @@ pub fn main() { // Beside checking for existence of `--sysroot` on the command line, we need to // check for the arg files that are prefixed with @ as well to be consistent with rustc for arg in args.iter() { - if let Some(arg_file_path) = arg.strip_prefix('@') { - if let Ok(arg_file) = read_to_string(arg_file_path) { - let split_arg_file: Vec = arg_file.lines().map(ToString::to_string).collect(); - if has_arg(&split_arg_file, "--sysroot") { - return true; - } + if let Some(arg_file_path) = arg.strip_prefix('@') + && let Ok(arg_file) = read_to_string(arg_file_path) + { + let split_arg_file: Vec = arg_file.lines().map(ToString::to_string).collect(); + if has_arg(&split_arg_file, "--sysroot") { + return true; } } } @@ -221,10 +218,10 @@ pub fn main() { let sys_root_env = std::env::var("SYSROOT").ok(); let pass_sysroot_env_if_given = |args: &mut Vec, sys_root_env| { - if let Some(sys_root) = sys_root_env { - if !has_sysroot_arg(args) { - args.extend(vec!["--sysroot".into(), sys_root]); - } + if let Some(sys_root) = sys_root_env + && !has_sysroot_arg(args) + { + args.extend(vec!["--sysroot".into(), sys_root]); } }; diff --git a/src/tools/clippy/tests/clippy.toml b/src/tools/clippy/tests/clippy.toml index 5eb7ac0354198..91a2e55180b97 100644 --- a/src/tools/clippy/tests/clippy.toml +++ b/src/tools/clippy/tests/clippy.toml @@ -1 +1,2 @@ # default config for tests, overrides clippy.toml at the project root +lint-commented-code = false diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index f44cf7a7c25a0..78b27e2f61399 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -1,7 +1,9 @@ -#![feature(rustc_private, let_chains)] +#![feature(rustc_private)] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] +use askama::Template; +use askama::filters::Safe; use cargo_metadata::Message; use cargo_metadata::diagnostic::{Applicability, Diagnostic}; use clippy_config::ClippyConfiguration; @@ -9,14 +11,13 @@ use clippy_lints::LintInfo; use clippy_lints::declared_lints::LINTS; use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED}; use pulldown_cmark::{Options, Parser, html}; -use rinja::Template; -use rinja::filters::Safe; use serde::Deserialize; use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::custom_flags::Flag; +use ui_test::custom_flags::edition::Edition; use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::spanned::Spanned; -use ui_test::{Args, CommandBuilder, Config, Match, OutputConflictHandling, status_emitter}; +use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict, status_emitter}; use std::collections::{BTreeMap, HashMap}; use std::env::{self, set_var, var_os}; @@ -86,13 +87,13 @@ fn extern_flags() -> Vec { let name = name.strip_prefix("lib").unwrap_or(name); Some((name, path_str)) }; - if let Some((name, path)) = parse_name_path() { - if TEST_DEPENDENCIES.contains(&name) { - // A dependency may be listed twice if it is available in sysroot, - // and the sysroot dependencies are listed first. As of the writing, - // this only seems to apply to if_chain. - crates.insert(name, path); - } + if let Some((name, path)) = parse_name_path() + && TEST_DEPENDENCIES.contains(&name) + { + // A dependency may be listed twice if it is available in sysroot, + // and the sysroot dependencies are listed first. As of the writing, + // this only seems to apply to if_chain. + crates.insert(name, path); } } let not_found: Vec<&str> = TEST_DEPENDENCIES @@ -142,16 +143,21 @@ impl TestContext { fn base_config(&self, test_dir: &str, mandatory_annotations: bool) -> Config { let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())); let mut config = Config { - output_conflict_handling: OutputConflictHandling::Error, + output_conflict_handling: error_on_output_conflict, filter_files: env::var("TESTNAME") .map(|filters| filters.split(',').map(str::to_string).collect()) .unwrap_or_default(), target: None, - bless_command: Some("cargo uibless".into()), + bless_command: Some(if IS_RUSTC_TEST_SUITE { + "./x test src/tools/clippy --bless".into() + } else { + "cargo uibless".into() + }), out_dir: target_dir.join("ui_test"), ..Config::rustc(Path::new("tests").join(test_dir)) }; let defaults = config.comment_defaults.base(); + defaults.set_custom("edition", Edition("2024".into())); defaults.exit_status = None.into(); if mandatory_annotations { defaults.require_annotations = Some(Spanned::dummy(true)).into(); @@ -220,7 +226,7 @@ fn run_internal_tests(cx: &TestContext) { if !RUN_INTERNAL_TESTS { return; } - let mut config = cx.base_config("ui-internal", false); + let mut config = cx.base_config("ui-internal", true); config.bless_command = Some("cargo uitest --features internal -- -- --bless".into()); ui_test::run_tests_generic( diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index 858be389a9e6e..4ac2bd532851e 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -36,6 +36,7 @@ fn dogfood() { for package in [ "./", "clippy_dev", + "clippy_lints_internal", "clippy_lints", "clippy_utils", "clippy_config", @@ -43,8 +44,8 @@ fn dogfood() { "rustc_tools_util", ] { println!("linting {package}"); - if !run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]) { - failed_packages.push(if package.is_empty() { "root" } else { package }); + if !run_clippy_for_package(package) { + failed_packages.push(package); } } @@ -56,7 +57,7 @@ fn dogfood() { } #[must_use] -fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { +fn run_clippy_for_package(project: &str) -> bool { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); @@ -78,17 +79,17 @@ fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { } } - command.arg("--").args(args); + command.arg("--"); command.arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - - if cfg!(feature = "internal") { - // internal lints only exist if we build with the internal feature - command.args(["-D", "clippy::internal"]); - } else { + command.args(["-D", "clippy::all", "-D", "clippy::pedantic", "-D", "clippy::dbg_macro"]); + if !cfg!(feature = "internal") { // running a clippy built without internal lints on the clippy source - // that contains e.g. `allow(clippy::invalid_paths)` + // that contains e.g. `allow(clippy::symbol_as_str)` command.args(["-A", "unknown_lints"]); } + // Workaround for not being a workspace, add the crate's directory back to the path + command.args(["--remap-path-prefix", &format!("={project}")]); + command.status().unwrap().success() } diff --git a/src/tools/clippy/tests/integration.rs b/src/tools/clippy/tests/integration.rs index 13cf36823c5e4..cb7d61eee24ad 100644 --- a/src/tools/clippy/tests/integration.rs +++ b/src/tools/clippy/tests/integration.rs @@ -30,7 +30,7 @@ fn integration_test() { let repo_dir = tempfile::tempdir() .expect("couldn't create temp dir") - .into_path() + .keep() .join(crate_name); let st = Command::new("git") diff --git a/src/tools/clippy/tests/lint_message_convention.rs b/src/tools/clippy/tests/lint_message_convention.rs index 7ed1f485c1cfd..9229e2e8c496b 100644 --- a/src/tools/clippy/tests/lint_message_convention.rs +++ b/src/tools/clippy/tests/lint_message_convention.rs @@ -44,6 +44,7 @@ impl Message { ".*AT&T x86 assembly syntax used", "note: Clippy version: .*", "the compiler unexpectedly panicked. this is a bug.", + "internal compiler error:", ]) .unwrap() }); diff --git a/src/tools/clippy/tests/ui-internal/auxiliary/paths.rs b/src/tools/clippy/tests/ui-internal/auxiliary/paths.rs deleted file mode 100644 index 52fcaec4df32e..0000000000000 --- a/src/tools/clippy/tests/ui-internal/auxiliary/paths.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub static OPTION: [&str; 3] = ["core", "option", "Option"]; -pub const RESULT: &[&str] = &["core", "result", "Result"]; diff --git a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs index 31acac89cc635..897002949e67e 100644 --- a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs +++ b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs @@ -1,5 +1,5 @@ -#![deny(clippy::internal)] #![feature(rustc_private)] +#![deny(clippy::invalid_clippy_version_attribute, clippy::missing_clippy_version_attribute)] #[macro_use] extern crate rustc_middle; @@ -38,6 +38,7 @@ declare_tool_lint! { // Invalid attributes /////////////////////// declare_tool_lint! { +//~^ invalid_clippy_version_attribute #[clippy::version = "1.2.3.4.5.6"] pub clippy::INVALID_ONE, Warn, @@ -46,6 +47,7 @@ declare_tool_lint! { } declare_tool_lint! { +//~^ invalid_clippy_version_attribute #[clippy::version = "I'm a string"] pub clippy::INVALID_TWO, Warn, @@ -57,6 +59,7 @@ declare_tool_lint! { // Missing attribute test /////////////////////// declare_tool_lint! { +//~^ missing_clippy_version_attribute #[clippy::version] pub clippy::MISSING_ATTRIBUTE_ONE, Warn, @@ -65,6 +68,7 @@ declare_tool_lint! { } declare_tool_lint! { +//~^ missing_clippy_version_attribute pub clippy::MISSING_ATTRIBUTE_TWO, Warn, "Two", @@ -82,6 +86,15 @@ mod internal_clippy_lints { } use crate::internal_clippy_lints::ALLOW_MISSING_ATTRIBUTE_ONE; -declare_lint_pass!(Pass2 => [VALID_ONE, VALID_TWO, VALID_THREE, INVALID_ONE, INVALID_TWO, MISSING_ATTRIBUTE_ONE, MISSING_ATTRIBUTE_TWO, ALLOW_MISSING_ATTRIBUTE_ONE]); +declare_lint_pass!(Pass2 => [ + VALID_ONE, + VALID_TWO, + VALID_THREE, + INVALID_ONE, + INVALID_TWO, + MISSING_ATTRIBUTE_ONE, + MISSING_ATTRIBUTE_TWO, + ALLOW_MISSING_ATTRIBUTE_ONE, +]); fn main() {} diff --git a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr index 631c292f5249e..952bc94403033 100644 --- a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr +++ b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr @@ -2,31 +2,30 @@ error: this item has an invalid `clippy::version` attribute --> tests/ui-internal/check_clippy_version_attribute.rs:40:1 | LL | / declare_tool_lint! { +LL | | LL | | #[clippy::version = "1.2.3.4.5.6"] LL | | pub clippy::INVALID_ONE, -LL | | Warn, -LL | | "One", +... | LL | | report_in_external_macro: true LL | | } | |_^ | = help: please use a valid semantic version, see `doc/adding_lints.md` note: the lint level is defined here - --> tests/ui-internal/check_clippy_version_attribute.rs:1:9 + --> tests/ui-internal/check_clippy_version_attribute.rs:2:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::invalid_clippy_version_attribute, clippy::missing_clippy_version_attribute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this item has an invalid `clippy::version` attribute - --> tests/ui-internal/check_clippy_version_attribute.rs:48:1 + --> tests/ui-internal/check_clippy_version_attribute.rs:49:1 | LL | / declare_tool_lint! { +LL | | LL | | #[clippy::version = "I'm a string"] LL | | pub clippy::INVALID_TWO, -LL | | Warn, -LL | | "Two", +... | LL | | report_in_external_macro: true LL | | } | |_^ @@ -35,25 +34,30 @@ LL | | } = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value - --> tests/ui-internal/check_clippy_version_attribute.rs:59:1 + --> tests/ui-internal/check_clippy_version_attribute.rs:61:1 | LL | / declare_tool_lint! { +LL | | LL | | #[clippy::version] LL | | pub clippy::MISSING_ATTRIBUTE_ONE, -LL | | Warn, -LL | | "Two", +... | LL | | report_in_external_macro: true LL | | } | |_^ | = help: please use a `clippy::version` attribute, see `doc/adding_lints.md` - = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` +note: the lint level is defined here + --> tests/ui-internal/check_clippy_version_attribute.rs:2:51 + | +LL | #![deny(clippy::invalid_clippy_version_attribute, clippy::missing_clippy_version_attribute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value - --> tests/ui-internal/check_clippy_version_attribute.rs:67:1 + --> tests/ui-internal/check_clippy_version_attribute.rs:70:1 | LL | / declare_tool_lint! { +LL | | LL | | pub clippy::MISSING_ATTRIBUTE_TWO, LL | | Warn, LL | | "Two", diff --git a/src/tools/clippy/tests/ui-internal/check_formulation.rs b/src/tools/clippy/tests/ui-internal/check_formulation.rs index 43fc996033eac..bcbb0d783198e 100644 --- a/src/tools/clippy/tests/ui-internal/check_formulation.rs +++ b/src/tools/clippy/tests/ui-internal/check_formulation.rs @@ -1,4 +1,5 @@ -#![warn(clippy::almost_standard_lint_formulation)] +#![deny(clippy::almost_standard_lint_formulation)] +#![allow(clippy::lint_without_lint_pass)] #![feature(rustc_private)] #[macro_use] @@ -21,6 +22,7 @@ declare_tool_lint! { declare_tool_lint! { /// # What it does /// Check for lint formulations that are correct + //~^ almost_standard_lint_formulation #[clippy::version = "pre 1.29.0"] pub clippy::INVALID1, Warn, @@ -31,6 +33,7 @@ declare_tool_lint! { declare_tool_lint! { /// # What it does /// Detects uses of incorrect formulations + //~^ almost_standard_lint_formulation #[clippy::version = "pre 1.29.0"] pub clippy::INVALID2, Warn, diff --git a/src/tools/clippy/tests/ui-internal/check_formulation.stderr b/src/tools/clippy/tests/ui-internal/check_formulation.stderr index 12514370e6ded..9aeb9e1f2d49c 100644 --- a/src/tools/clippy/tests/ui-internal/check_formulation.stderr +++ b/src/tools/clippy/tests/ui-internal/check_formulation.stderr @@ -1,15 +1,18 @@ error: non-standard lint formulation - --> tests/ui-internal/check_formulation.rs:23:5 + --> tests/ui-internal/check_formulation.rs:24:5 | LL | /// Check for lint formulations that are correct | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using `Checks for` - = note: `-D clippy::almost-standard-lint-formulation` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]` +note: the lint level is defined here + --> tests/ui-internal/check_formulation.rs:1:9 + | +LL | #![deny(clippy::almost_standard_lint_formulation)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-standard lint formulation - --> tests/ui-internal/check_formulation.rs:33:5 + --> tests/ui-internal/check_formulation.rs:35:5 | LL | /// Detects uses of incorrect formulations | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed index 918e33345a779..76f68686ee2a8 100644 --- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed +++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::collapsible_span_lint_calls)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs index 1baf6142b3499..214c8783a6690 100644 --- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs +++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::collapsible_span_lint_calls)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] @@ -33,18 +33,23 @@ impl EarlyLintPass for Pass { let predicate = true; span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); }); span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.span_help(expr.span, help_msg); }); span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.help(help_msg); }); span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.span_note(expr.span, note_msg); }); span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + //~^ collapsible_span_lint_calls db.note(note_msg); }); diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr index 104995918de2e..9c83538947cab 100644 --- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr +++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -2,6 +2,7 @@ error: this call is collapsible --> tests/ui-internal/collapsible_span_lint_calls.rs:35:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); LL | | }); | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)` @@ -9,38 +10,41 @@ LL | | }); note: the lint level is defined here --> tests/ui-internal/collapsible_span_lint_calls.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::collapsible_span_lint_calls)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this call is collapsible - --> tests/ui-internal/collapsible_span_lint_calls.rs:38:9 + --> tests/ui-internal/collapsible_span_lint_calls.rs:39:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.span_help(expr.span, help_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)` error: this call is collapsible - --> tests/ui-internal/collapsible_span_lint_calls.rs:41:9 + --> tests/ui-internal/collapsible_span_lint_calls.rs:43:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.help(help_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` error: this call is collapsible - --> tests/ui-internal/collapsible_span_lint_calls.rs:44:9 + --> tests/ui-internal/collapsible_span_lint_calls.rs:47:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.span_note(expr.span, note_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` error: this call is collapsible - --> tests/ui-internal/collapsible_span_lint_calls.rs:47:9 + --> tests/ui-internal/collapsible_span_lint_calls.rs:51:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | LL | | db.note(note_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)` diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs index 9b0db660c9975..c7e92b1bf164f 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs @@ -6,9 +6,10 @@ //@normalize-stderr-test: "rustc 1\.\d+.* running on .*" -> "rustc running on " //@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> "" -#![deny(clippy::internal)] +#![deny(clippy::produce_ice)] #![allow(clippy::missing_clippy_version_attribute)] fn it_looks_like_you_are_trying_to_kill_clippy() {} +//~^ ice: Would you like some help with that? fn main() {} diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index 801b0f340de93..884d3d035a29d 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -1,9 +1,18 @@ - -thread '' panicked at clippy_lints/src/utils/internal_lints/produce_ice.rs: -Would you like some help with that? -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace - -error: the compiler unexpectedly panicked. this is a bug. +note: no errors encountered even though delayed bugs were created + +note: those delayed bugs will now be shown as internal compiler errors + +error: internal compiler error: Would you like some help with that? + --> tests/ui-internal/custom_ice_message.rs:12:1 + | +LL | fn it_looks_like_you_are_trying_to_kill_clippy() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: delayed at clippy_lints_internal/src/produce_ice.rs - disabled backtrace + --> tests/ui-internal/custom_ice_message.rs:12:1 + | +LL | fn it_looks_like_you_are_trying_to_kill_clippy() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml @@ -13,9 +22,5 @@ note: rustc running on note: compiler flags: -Z ui-testing -Z deduplicate-diagnostics=no -query stack during panic: -#0 [early_lint_checks] perform lints prior to AST lowering -#1 [hir_crate] getting the crate HIR -... and 3 other queries... use `env RUST_BACKTRACE=1` to see the full query stack note: Clippy version: foo diff --git a/src/tools/clippy/tests/ui-internal/default_lint.rs b/src/tools/clippy/tests/ui-internal/default_lint.rs index da29aedb2a3ae..809f2c4d080dc 100644 --- a/src/tools/clippy/tests/ui-internal/default_lint.rs +++ b/src/tools/clippy/tests/ui-internal/default_lint.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::default_lint)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] @@ -16,6 +16,7 @@ declare_tool_lint! { } declare_tool_lint! { +//~^ default_lint pub clippy::TEST_LINT_DEFAULT, Warn, "default lint description", diff --git a/src/tools/clippy/tests/ui-internal/default_lint.stderr b/src/tools/clippy/tests/ui-internal/default_lint.stderr index c939125e875c4..2c700ec82dcd4 100644 --- a/src/tools/clippy/tests/ui-internal/default_lint.stderr +++ b/src/tools/clippy/tests/ui-internal/default_lint.stderr @@ -2,6 +2,7 @@ error: the lint `TEST_LINT_DEFAULT` has the default lint description --> tests/ui-internal/default_lint.rs:18:1 | LL | / declare_tool_lint! { +LL | | LL | | pub clippy::TEST_LINT_DEFAULT, LL | | Warn, LL | | "default lint description", @@ -12,9 +13,8 @@ LL | | } note: the lint level is defined here --> tests/ui-internal/default_lint.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::default_lint)] + | ^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui-internal/derive_deserialize_allowing_unknown.rs b/src/tools/clippy/tests/ui-internal/derive_deserialize_allowing_unknown.rs new file mode 100644 index 0000000000000..9dc8e9e8f4c16 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/derive_deserialize_allowing_unknown.rs @@ -0,0 +1,60 @@ +#![deny(clippy::derive_deserialize_allowing_unknown)] + +use serde::{Deserialize, Deserializer}; + +#[derive(Deserialize)] //~ derive_deserialize_allowing_unknown +struct Struct { + flag: bool, + limit: u64, +} + +#[derive(Deserialize)] //~ derive_deserialize_allowing_unknown +enum Enum { + A(bool), + B { limit: u64 }, +} + +// negative tests + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct StructWithDenyUnknownFields { + flag: bool, + limit: u64, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +enum EnumWithDenyUnknownFields { + A(bool), + B { limit: u64 }, +} + +#[derive(Deserialize)] +#[serde(untagged, deny_unknown_fields)] +enum MultipleSerdeAttributes { + A(bool), + B { limit: u64 }, +} + +#[derive(Deserialize)] +struct TupleStruct(u64, bool); + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +enum EnumWithOnlyTupleVariants { + A(bool), + B(u64), +} + +struct ManualSerdeImplementation; + +impl<'de> Deserialize<'de> for ManualSerdeImplementation { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let () = <() as Deserialize>::deserialize(deserializer)?; + Ok(ManualSerdeImplementation) + } +} diff --git a/src/tools/clippy/tests/ui-internal/derive_deserialize_allowing_unknown.stderr b/src/tools/clippy/tests/ui-internal/derive_deserialize_allowing_unknown.stderr new file mode 100644 index 0000000000000..93d64826c9935 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/derive_deserialize_allowing_unknown.stderr @@ -0,0 +1,23 @@ +error: `#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]` + --> tests/ui-internal/derive_deserialize_allowing_unknown.rs:5:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui-internal/derive_deserialize_allowing_unknown.rs:1:9 + | +LL | #![deny(clippy::derive_deserialize_allowing_unknown)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `#[derive(serde::Deserialize)]` without `#[serde(deny_unknown_fields)]` + --> tests/ui-internal/derive_deserialize_allowing_unknown.rs:11:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs b/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs index ca71dddcc24f1..36e4158f6e688 100644 --- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs +++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs @@ -1,4 +1,5 @@ #![feature(rustc_private)] +#![deny(clippy::disallowed_methods)] extern crate rustc_errors; extern crate rustc_hir; @@ -12,12 +13,14 @@ use rustc_middle::ty::TyCtxt; pub fn a(cx: impl LintContext, lint: &'static Lint, span: impl Into, msg: impl Into) { cx.span_lint(lint, span, |lint| { + //~^ disallowed_methods lint.primary_message(msg); }); } pub fn b(tcx: TyCtxt<'_>, lint: &'static Lint, hir_id: HirId, span: impl Into, msg: impl Into) { tcx.node_span_lint(lint, hir_id, span, |lint| { + //~^ disallowed_methods lint.primary_message(msg); }); } diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr index 16e1487f2bbca..f03a745963e00 100644 --- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr +++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr @@ -1,15 +1,18 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` - --> tests/ui-internal/disallow_span_lint.rs:14:8 + --> tests/ui-internal/disallow_span_lint.rs:15:8 | LL | cx.span_lint(lint, span, |lint| { | ^^^^^^^^^ | = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead - = note: `-D clippy::disallowed-methods` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` +note: the lint level is defined here + --> tests/ui-internal/disallow_span_lint.rs:2:9 + | +LL | #![deny(clippy::disallowed_methods)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint` - --> tests/ui-internal/disallow_span_lint.rs:20:9 + --> tests/ui-internal/disallow_span_lint.rs:22:9 | LL | tcx.node_span_lint(lint, hir_id, span, |lint| { | ^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed deleted file mode 100644 index 3bcabb4ab2d36..0000000000000 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed +++ /dev/null @@ -1,36 +0,0 @@ -#![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] -#![feature(rustc_private)] - -extern crate rustc_span; - -use rustc_span::symbol::Symbol; - -macro_rules! sym { - ($tt:tt) => { - rustc_span::symbol::Symbol::intern(stringify!($tt)) - }; -} - -fn main() { - // Direct use of Symbol::intern - let _ = rustc_span::sym::f32; - - // Using a sym macro - let _ = rustc_span::sym::f32; - - // Correct suggestion when symbol isn't stringified constant name - let _ = rustc_span::sym::proc_dash_macro; - - // interning a keyword - let _ = rustc_span::kw::SelfLower; - - // Interning a symbol that is not defined - let _ = Symbol::intern("xyz123"); - let _ = sym!(xyz123); - - // Using a different `intern` function - let _ = intern("f32"); -} - -fn intern(_: &str) {} diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs deleted file mode 100644 index 92e92d4fbc16a..0000000000000 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] -#![feature(rustc_private)] - -extern crate rustc_span; - -use rustc_span::symbol::Symbol; - -macro_rules! sym { - ($tt:tt) => { - rustc_span::symbol::Symbol::intern(stringify!($tt)) - }; -} - -fn main() { - // Direct use of Symbol::intern - let _ = Symbol::intern("f32"); - - // Using a sym macro - let _ = sym!(f32); - - // Correct suggestion when symbol isn't stringified constant name - let _ = Symbol::intern("proc-macro"); - - // interning a keyword - let _ = Symbol::intern("self"); - - // Interning a symbol that is not defined - let _ = Symbol::intern("xyz123"); - let _ = sym!(xyz123); - - // Using a different `intern` function - let _ = intern("f32"); -} - -fn intern(_: &str) {} diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr deleted file mode 100644 index c4d0308979f64..0000000000000 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:17:13 - | -LL | let _ = Symbol::intern("f32"); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32` - | -note: the lint level is defined here - --> tests/ui-internal/interning_defined_symbol.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]` - -error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:20:13 - | -LL | let _ = sym!(f32); - | ^^^^^^^^^ help: try: `rustc_span::sym::f32` - -error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:23:13 - | -LL | let _ = Symbol::intern("proc-macro"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro` - -error: interning a defined symbol - --> tests/ui-internal/interning_defined_symbol.rs:26:13 - | -LL | let _ = Symbol::intern("self"); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::kw::SelfLower` - -error: aborting due to 4 previous errors - diff --git a/src/tools/clippy/tests/ui-internal/interning_literals.fixed b/src/tools/clippy/tests/ui-internal/interning_literals.fixed new file mode 100644 index 0000000000000..03e97768b996f --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_literals.fixed @@ -0,0 +1,31 @@ +#![allow(clippy::let_unit_value)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn main() { + let _ = sym::f32; + //~^ interning_literals + + // Correct suggestion when symbol isn't stringified constant name + let _ = sym::proc_dash_macro; + //~^ interning_literals + + // Interning a keyword + let _ = kw::SelfLower; + //~^ interning_literals + + // Defined in clippy_utils + let _ = sym::msrv; + //~^ interning_literals + let _ = sym::Cargo_toml; + //~^ interning_literals + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/src/tools/clippy/tests/ui-internal/interning_literals.rs b/src/tools/clippy/tests/ui-internal/interning_literals.rs new file mode 100644 index 0000000000000..561fd5702a59f --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_literals.rs @@ -0,0 +1,31 @@ +#![allow(clippy::let_unit_value)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn main() { + let _ = Symbol::intern("f32"); + //~^ interning_literals + + // Correct suggestion when symbol isn't stringified constant name + let _ = Symbol::intern("proc-macro"); + //~^ interning_literals + + // Interning a keyword + let _ = Symbol::intern("self"); + //~^ interning_literals + + // Defined in clippy_utils + let _ = Symbol::intern("msrv"); + //~^ interning_literals + let _ = Symbol::intern("Cargo.toml"); + //~^ interning_literals + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/src/tools/clippy/tests/ui-internal/interning_literals.stderr b/src/tools/clippy/tests/ui-internal/interning_literals.stderr new file mode 100644 index 0000000000000..9ff4194e542bb --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_literals.stderr @@ -0,0 +1,69 @@ +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:10:13 + | +LL | let _ = Symbol::intern("f32"); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed + = note: `-D clippy::interning-literals` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::interning_literals)]` +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("f32"); +LL + let _ = sym::f32; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:14:13 + | +LL | let _ = Symbol::intern("proc-macro"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("proc-macro"); +LL + let _ = sym::proc_dash_macro; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:18:13 + | +LL | let _ = Symbol::intern("self"); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("self"); +LL + let _ = kw::SelfLower; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:22:13 + | +LL | let _ = Symbol::intern("msrv"); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("msrv"); +LL + let _ = sym::msrv; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals.rs:24:13 + | +LL | let _ = Symbol::intern("Cargo.toml"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("Cargo.toml"); +LL + let _ = sym::Cargo_toml; + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.rs b/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.rs new file mode 100644 index 0000000000000..43872e95a5854 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.rs @@ -0,0 +1,16 @@ +//@no-rustfix: paths that don't exist yet +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::Symbol; + +fn main() { + // Not yet defined + let _ = Symbol::intern("xyz123"); + //~^ interning_literals + let _ = Symbol::intern("with-dash"); + //~^ interning_literals + let _ = Symbol::intern("with.dot"); + //~^ interning_literals +} diff --git a/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.stderr b/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.stderr new file mode 100644 index 0000000000000..879d9e633c23d --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/interning_literals_unfixable.stderr @@ -0,0 +1,43 @@ +error: interning a string literal + --> tests/ui-internal/interning_literals_unfixable.rs:10:13 + | +LL | let _ = Symbol::intern("xyz123"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed + = note: `-D clippy::interning-literals` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::interning_literals)]` +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("xyz123"); +LL + let _ = sym::xyz123; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals_unfixable.rs:12:13 + | +LL | let _ = Symbol::intern("with-dash"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("with-dash"); +LL + let _ = sym::with_dash; + | + +error: interning a string literal + --> tests/ui-internal/interning_literals_unfixable.rs:14:13 + | +LL | let _ = Symbol::intern("with.dot"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add the symbol to `clippy_utils/src/sym.rs` if needed +help: use a preinterned symbol instead + | +LL - let _ = Symbol::intern("with.dot"); +LL + let _ = sym::with_dot; + | + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed index 7011ef518f20f..238ef9ae6d0ac 100644 --- a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed +++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::missing_msrv_attr_impl)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] @@ -27,6 +27,7 @@ impl_lint_pass!(Pass => [TEST_LINT]); impl EarlyLintPass for Pass { extract_msrv_attr!(); + //~^ missing_msrv_attr_impl fn check_expr(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Expr) {} } diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs index 323061decd23c..7753dcaad7139 100644 --- a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs +++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::missing_msrv_attr_impl)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] @@ -26,6 +26,7 @@ struct Pass { impl_lint_pass!(Pass => [TEST_LINT]); impl EarlyLintPass for Pass { + //~^ missing_msrv_attr_impl fn check_expr(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Expr) {} } diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.stderr b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.stderr index 0a7636313eff2..d5928d8c0c2de 100644 --- a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.stderr +++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.stderr @@ -7,9 +7,8 @@ LL | impl EarlyLintPass for Pass { note: the lint level is defined here --> tests/ui-internal/invalid_msrv_attr_impl.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::missing_msrv_attr_impl)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::missing_msrv_attr_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `extract_msrv_attr!()` to the `EarlyLintPass` implementation | LL ~ impl EarlyLintPass for Pass { diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.rs b/src/tools/clippy/tests/ui-internal/invalid_paths.rs deleted file mode 100644 index 9a9790a4bae51..0000000000000 --- a/src/tools/clippy/tests/ui-internal/invalid_paths.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![warn(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)] - -mod paths { - // Good path - pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; - - // Path to method on inherent impl of a primitive type - pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; - - // Path to method on inherent impl - pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; - - // Path with empty segment - pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; - - // Path with bad crate - pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; - - // Path with bad module - pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; - - // Path to method on an enum inherent impl - pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"]; -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr deleted file mode 100644 index fc530a2efa378..0000000000000 --- a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: invalid path - --> tests/ui-internal/invalid_paths.rs:15:5 - | -LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::invalid-paths` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::invalid_paths)]` - -error: invalid path - --> tests/ui-internal/invalid_paths.rs:18:5 - | -LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: invalid path - --> tests/ui-internal/invalid_paths.rs:21:5 - | -LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs index d59e9cbbb6173..6b649132aca31 100644 --- a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs +++ b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::lint_without_lint_pass)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] @@ -10,6 +10,7 @@ extern crate rustc_lint; use rustc_lint::{LintPass, LintVec}; declare_tool_lint! { +//~^ lint_without_lint_pass pub clippy::TEST_LINT, Warn, "", diff --git a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr index 187bba97fd419..3798293f4c111 100644 --- a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr +++ b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr @@ -2,6 +2,7 @@ error: the lint `TEST_LINT` is not added to any `LintPass` --> tests/ui-internal/lint_without_lint_pass.rs:12:1 | LL | / declare_tool_lint! { +LL | | LL | | pub clippy::TEST_LINT, LL | | Warn, LL | | "", @@ -12,9 +13,8 @@ LL | | } note: the lint level is defined here --> tests/ui-internal/lint_without_lint_pass.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::lint_without_lint_pass)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed b/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed index cef16cf6ca5bf..900ca5b2ab9d8 100644 --- a/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed +++ b/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::outer_expn_expn_data)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] @@ -21,6 +21,7 @@ declare_lint_pass!(Pass => [TEST_LINT]); impl<'tcx> LateLintPass<'tcx> for Pass { fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { let _ = expr.span.ctxt().outer_expn_data(); + //~^ outer_expn_expn_data } } diff --git a/src/tools/clippy/tests/ui-internal/outer_expn_data.rs b/src/tools/clippy/tests/ui-internal/outer_expn_data.rs index fb453be661c8b..bcfc42aa2ac75 100644 --- a/src/tools/clippy/tests/ui-internal/outer_expn_data.rs +++ b/src/tools/clippy/tests/ui-internal/outer_expn_data.rs @@ -1,4 +1,4 @@ -#![deny(clippy::internal)] +#![deny(clippy::outer_expn_expn_data)] #![allow(clippy::missing_clippy_version_attribute)] #![feature(rustc_private)] @@ -21,6 +21,7 @@ declare_lint_pass!(Pass => [TEST_LINT]); impl<'tcx> LateLintPass<'tcx> for Pass { fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { let _ = expr.span.ctxt().outer_expn().expn_data(); + //~^ outer_expn_expn_data } } diff --git a/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr b/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr index 33ac91e4fb0de..b86138a5d45d2 100644 --- a/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr +++ b/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr @@ -7,9 +7,8 @@ LL | let _ = expr.span.ctxt().outer_expn().expn_data(); note: the lint level is defined here --> tests/ui-internal/outer_expn_data.rs:1:9 | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::outer_expn_expn_data)]` implied by `#[deny(clippy::internal)]` +LL | #![deny(clippy::outer_expn_expn_data)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed deleted file mode 100644 index 2cbd646a0fd5d..0000000000000 --- a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed +++ /dev/null @@ -1,24 +0,0 @@ -#![feature(rustc_private)] -#![warn(clippy::slow_symbol_comparisons)] - -extern crate rustc_span; - -use clippy_utils::sym; -use rustc_span::Symbol; - -fn main() { - let symbol = sym!(example); - let other_symbol = sym!(other_example); - - // Should lint - let slow_comparison = symbol.as_str() == "example"; - //~^ error: comparing `Symbol` via `Symbol::intern` - let slow_comparison_macro = symbol.as_str() == "example"; - //~^ error: comparing `Symbol` via `Symbol::intern` - let slow_comparison_backwards = symbol.as_str() == "example"; - //~^ error: comparing `Symbol` via `Symbol::intern` - - // Should not lint - let faster_comparison = symbol.as_str() == "other_example"; - let preinterned_comparison = symbol == other_symbol; -} diff --git a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs deleted file mode 100644 index 0cea3c3fcff9f..0000000000000 --- a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![feature(rustc_private)] -#![warn(clippy::slow_symbol_comparisons)] - -extern crate rustc_span; - -use clippy_utils::sym; -use rustc_span::Symbol; - -fn main() { - let symbol = sym!(example); - let other_symbol = sym!(other_example); - - // Should lint - let slow_comparison = symbol == Symbol::intern("example"); - //~^ error: comparing `Symbol` via `Symbol::intern` - let slow_comparison_macro = symbol == sym!(example); - //~^ error: comparing `Symbol` via `Symbol::intern` - let slow_comparison_backwards = sym!(example) == symbol; - //~^ error: comparing `Symbol` via `Symbol::intern` - - // Should not lint - let faster_comparison = symbol.as_str() == "other_example"; - let preinterned_comparison = symbol == other_symbol; -} diff --git a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr deleted file mode 100644 index 72cb20a7fed90..0000000000000 --- a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: comparing `Symbol` via `Symbol::intern` - --> tests/ui-internal/slow_symbol_comparisons.rs:14:27 - | -LL | let slow_comparison = symbol == Symbol::intern("example"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"` - | - = note: `-D clippy::slow-symbol-comparisons` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::slow_symbol_comparisons)]` - -error: comparing `Symbol` via `Symbol::intern` - --> tests/ui-internal/slow_symbol_comparisons.rs:16:33 - | -LL | let slow_comparison_macro = symbol == sym!(example); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"` - -error: comparing `Symbol` via `Symbol::intern` - --> tests/ui-internal/slow_symbol_comparisons.rs:18:37 - | -LL | let slow_comparison_backwards = sym!(example) == symbol; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"` - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str.fixed b/src/tools/clippy/tests/ui-internal/symbol_as_str.fixed new file mode 100644 index 0000000000000..6a71b16c60494 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str.fixed @@ -0,0 +1,28 @@ +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn f(s: Symbol) { + s == sym::f32; + //~^ symbol_as_str + s == sym::proc_dash_macro; + //~^ symbol_as_str + s == kw::SelfLower; + //~^ symbol_as_str + s == sym::msrv; + //~^ symbol_as_str + s == sym::Cargo_toml; + //~^ symbol_as_str + sym::get == s; + //~^ symbol_as_str + + let _ = match s { + //~^ symbol_as_str + sym::unwrap_err => 1, + sym::unwrap_or_default | sym::unwrap_or_else => 2, + _ => 3, + }; +} diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str.rs b/src/tools/clippy/tests/ui-internal/symbol_as_str.rs new file mode 100644 index 0000000000000..43136504bf1af --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str.rs @@ -0,0 +1,28 @@ +#![feature(rustc_private)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::{Symbol, kw}; + +fn f(s: Symbol) { + s.as_str() == "f32"; + //~^ symbol_as_str + s.as_str() == "proc-macro"; + //~^ symbol_as_str + s.as_str() == "self"; + //~^ symbol_as_str + s.as_str() == "msrv"; + //~^ symbol_as_str + s.as_str() == "Cargo.toml"; + //~^ symbol_as_str + "get" == s.as_str(); + //~^ symbol_as_str + + let _ = match s.as_str() { + //~^ symbol_as_str + "unwrap_err" => 1, + "unwrap_or_default" | "unwrap_or_else" => 2, + _ => 3, + }; +} diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str.stderr b/src/tools/clippy/tests/ui-internal/symbol_as_str.stderr new file mode 100644 index 0000000000000..3eeead4aa8c14 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str.stderr @@ -0,0 +1,97 @@ +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:9:5 + | +LL | s.as_str() == "f32"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed + = note: `-D clippy::symbol-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::symbol_as_str)]` +help: use preinterned symbols instead + | +LL - s.as_str() == "f32"; +LL + s == sym::f32; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:11:5 + | +LL | s.as_str() == "proc-macro"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - s.as_str() == "proc-macro"; +LL + s == sym::proc_dash_macro; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:13:5 + | +LL | s.as_str() == "self"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - s.as_str() == "self"; +LL + s == kw::SelfLower; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:15:5 + | +LL | s.as_str() == "msrv"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - s.as_str() == "msrv"; +LL + s == sym::msrv; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:17:5 + | +LL | s.as_str() == "Cargo.toml"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - s.as_str() == "Cargo.toml"; +LL + s == sym::Cargo_toml; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:19:14 + | +LL | "get" == s.as_str(); + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - "get" == s.as_str(); +LL + sym::get == s; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str.rs:22:19 + | +LL | let _ = match s.as_str() { + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL ~ let _ = match s { +LL | +LL ~ sym::unwrap_err => 1, +LL ~ sym::unwrap_or_default | sym::unwrap_or_else => 2, + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.rs b/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.rs new file mode 100644 index 0000000000000..635f28007e9af --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.rs @@ -0,0 +1,15 @@ +//@no-rustfix: paths that don't exist yet +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::Symbol; + +fn f(s: Symbol) { + s.as_str() == "xyz123"; + //~^ symbol_as_str + s.as_str() == "with-dash"; + //~^ symbol_as_str + s.as_str() == "with.dot"; + //~^ symbol_as_str +} diff --git a/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.stderr b/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.stderr new file mode 100644 index 0000000000000..65664ebb451ae --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/symbol_as_str_unfixable.stderr @@ -0,0 +1,43 @@ +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str_unfixable.rs:9:5 + | +LL | s.as_str() == "xyz123"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed + = note: `-D clippy::symbol-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::symbol_as_str)]` +help: use preinterned symbols instead + | +LL - s.as_str() == "xyz123"; +LL + s == sym::xyz123; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str_unfixable.rs:11:5 + | +LL | s.as_str() == "with-dash"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - s.as_str() == "with-dash"; +LL + s == sym::with_dash; + | + +error: converting a Symbol to a string + --> tests/ui-internal/symbol_as_str_unfixable.rs:13:5 + | +LL | s.as_str() == "with.dot"; + | ^^^^^^^^^^ + | + = help: add the symbols to `clippy_utils/src/sym.rs` if needed +help: use preinterned symbols instead + | +LL - s.as_str() == "with.dot"; +LL + s == sym::with_dot; + | + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed deleted file mode 100644 index d3fab60f9e3ed..0000000000000 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed +++ /dev/null @@ -1,62 +0,0 @@ -//@aux-build:paths.rs -#![deny(clippy::internal)] -#![feature(rustc_private)] -#![allow(clippy::unnecessary_map_or)] - -extern crate clippy_utils; -extern crate paths; -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; -extern crate rustc_span; - -#[allow(unused)] -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; -#[allow(unused)] -use clippy_utils::{ - is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method, - match_def_path, match_trait_method, path_res, -}; - -#[allow(unused)] -use rustc_hir::LangItem; -#[allow(unused)] -use rustc_span::sym; - -use rustc_hir::Expr; -use rustc_hir::def_id::DefId; -use rustc_lint::LateContext; -use rustc_middle::ty::Ty; - -#[allow(unused, clippy::unnecessary_def_path)] -static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused, clippy::unnecessary_def_path)] -const RESULT: &[&str] = &["core", "result", "Result"]; - -fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { - let _ = is_type_diagnostic_item(cx, ty, sym::Option); - let _ = is_type_diagnostic_item(cx, ty, sym::Result); - let _ = is_type_diagnostic_item(cx, ty, sym::Result); - - #[allow(unused, clippy::unnecessary_def_path)] - let rc_path = &["alloc", "rc", "Rc"]; - let _ = is_type_diagnostic_item(cx, ty, sym::Rc); - - let _ = is_type_diagnostic_item(cx, ty, sym::Option); - let _ = is_type_diagnostic_item(cx, ty, sym::Result); - - let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox); - let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit); - - let _ = cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did); - let _ = cx.tcx.is_diagnostic_item(sym::Option, did); - let _ = cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did); - - let _ = is_trait_method(cx, expr, sym::AsRef); - - let _ = is_path_diagnostic_item(cx, expr, sym::Option); - let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id)); - let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome); -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs index 1b36f6b09e9c4..5cd3254188d9a 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs @@ -1,62 +1,20 @@ -//@aux-build:paths.rs -#![deny(clippy::internal)] #![feature(rustc_private)] -#![allow(clippy::unnecessary_map_or)] -extern crate clippy_utils; -extern crate paths; -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; -extern crate rustc_span; +use clippy_utils::paths::{PathLookup, PathNS}; +use clippy_utils::{macro_path, sym, type_path, value_path}; -#[allow(unused)] -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; -#[allow(unused)] -use clippy_utils::{ - is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method, - match_def_path, match_trait_method, path_res, -}; +static OPTION: PathLookup = type_path!(core::option::Option); +//~^ unnecessary_def_path +static SOME: PathLookup = type_path!(core::option::Option::Some); +//~^ unnecessary_def_path -#[allow(unused)] -use rustc_hir::LangItem; -#[allow(unused)] -use rustc_span::sym; +static RESULT: PathLookup = type_path!(core::result::Result); +//~^ unnecessary_def_path +static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result); +//~^ unnecessary_def_path -use rustc_hir::Expr; -use rustc_hir::def_id::DefId; -use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new); +//~^ unnecessary_def_path -#[allow(unused, clippy::unnecessary_def_path)] -static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused, clippy::unnecessary_def_path)] -const RESULT: &[&str] = &["core", "result", "Result"]; - -fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { - let _ = match_type(cx, ty, &OPTION); - let _ = match_type(cx, ty, RESULT); - let _ = match_type(cx, ty, &["core", "result", "Result"]); - - #[allow(unused, clippy::unnecessary_def_path)] - let rc_path = &["alloc", "rc", "Rc"]; - let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - - let _ = match_type(cx, ty, &paths::OPTION); - let _ = match_type(cx, ty, paths::RESULT); - - let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); - let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); - - let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); - let _ = match_def_path(cx, did, &["core", "option", "Option"]); - let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); - - let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); - - let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); - let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); - let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); -} - -fn main() {} +static VEC_MACRO: PathLookup = macro_path!(std::vec); +//~^ unnecessary_def_path diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr index 79521c5037a80..4abb1be7406c1 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr @@ -1,101 +1,58 @@ -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:37:13 +error: a diagnostic name exists for this path: sym::Option + --> tests/ui-internal/unnecessary_def_path.rs:6:29 | -LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` +LL | static OPTION: PathLookup = type_path!(core::option::Option); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui-internal/unnecessary_def_path.rs:2:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:38:13 - | -LL | let _ = match_type(cx, ty, RESULT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils + = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:39:13 +error: a language item exists for this path: LangItem::OptionSome + --> tests/ui-internal/unnecessary_def_path.rs:8:27 | -LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:43:13 +LL | static SOME: PathLookup = type_path!(core::option::Option::Some); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:45:13 +error: a diagnostic name exists for this path: sym::Result + --> tests/ui-internal/unnecessary_def_path.rs:11:29 | -LL | let _ = match_type(cx, ty, &paths::OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:46:13 +LL | static RESULT: PathLookup = type_path!(core::result::Result); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_type(cx, ty, paths::RESULT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:48:13 +error: a diagnostic name exists for this path: sym::Result + --> tests/ui-internal/unnecessary_def_path.rs:13:37 | -LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:49:13 +LL | static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:51:13 +error: a diagnostic name exists for this path: sym::vec_new + --> tests/ui-internal/unnecessary_def_path.rs:16:30 | -LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:52:13 +LL | static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:53:13 - | -LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)` +error: a diagnostic name exists for this path: sym::vec_macro + --> tests/ui-internal/unnecessary_def_path.rs:19:32 | - = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:55:13 - | -LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)` - -error: use of a def path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path.rs:57:13 - | -LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:58:13 - | -LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))` - -error: use of a def path to a `LangItem` - --> tests/ui-internal/unnecessary_def_path.rs:59:13 +LL | static VEC_MACRO: PathLookup = macro_path!(std::vec); + | ^^^^^^^^^^^^^^^^^^^^^ | -LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)` + = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead + = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils -error: aborting due to 15 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs deleted file mode 100644 index f6abb3cc3d713..0000000000000 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![feature(rustc_private)] -#![allow(unused)] -#![warn(clippy::unnecessary_def_path)] - -extern crate rustc_hir; - -use rustc_hir::LangItem; - -fn main() { - const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; - const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; - - // Don't lint, not a diagnostic or language item - const OPS_MOD: [&str; 2] = ["core", "ops"]; -} diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr deleted file mode 100644 index 9d7c00088fe8f..0000000000000 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: hardcoded path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:10:36 - | -LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `sym::Deref` - = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` - -error: hardcoded path to a language item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:11:40 - | -LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `LangItem::DerefMut` - -error: hardcoded path to a diagnostic item - --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:43 - | -LL | const OPS_MOD: [&str; 5] = ["core", "ops"]; - | ^^^^^^^^^^^^^^^ - | - = help: convert all references to use `sym::deref_method` - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed deleted file mode 100644 index 3d9deb705ace8..0000000000000 --- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed +++ /dev/null @@ -1,21 +0,0 @@ -#![feature(rustc_private)] -#![deny(clippy::internal)] -#![allow( - clippy::slow_symbol_comparisons, - clippy::borrow_deref_ref, - clippy::unnecessary_operation, - unused_must_use, - clippy::missing_clippy_version_attribute -)] - -extern crate rustc_span; - -use rustc_span::symbol::{Ident, Symbol}; - -fn main() { - Symbol::intern("foo") == rustc_span::sym::clippy; - Symbol::intern("foo") == rustc_span::kw::SelfLower; - Symbol::intern("foo") != rustc_span::kw::SelfUpper; - Ident::empty().name == rustc_span::sym::clippy; - rustc_span::sym::clippy == Ident::empty().name; -} diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs deleted file mode 100644 index 9aeeb9aaf3aa3..0000000000000 --- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![feature(rustc_private)] -#![deny(clippy::internal)] -#![allow( - clippy::slow_symbol_comparisons, - clippy::borrow_deref_ref, - clippy::unnecessary_operation, - unused_must_use, - clippy::missing_clippy_version_attribute -)] - -extern crate rustc_span; - -use rustc_span::symbol::{Ident, Symbol}; - -fn main() { - Symbol::intern("foo").as_str() == "clippy"; - Symbol::intern("foo").to_string() == "self"; - Symbol::intern("foo").to_ident_string() != "Self"; - &*Ident::empty().as_str() == "clippy"; - "clippy" == Ident::empty().to_string(); -} diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr deleted file mode 100644 index 1742603eff6d9..0000000000000 --- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:16:5 - | -LL | Symbol::intern("foo").as_str() == "clippy"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy` - | -note: the lint level is defined here - --> tests/ui-internal/unnecessary_symbol_str.rs:2:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` - -error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:17:5 - | -LL | Symbol::intern("foo").to_string() == "self"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::kw::SelfLower` - -error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:18:5 - | -LL | Symbol::intern("foo").to_ident_string() != "Self"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::kw::SelfUpper` - -error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:19:5 - | -LL | &*Ident::empty().as_str() == "clippy"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` - -error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:20:5 - | -LL | "clippy" == Ident::empty().to_string(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` - -error: aborting due to 5 previous errors - diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml new file mode 100644 index 0000000000000..baf7041138f92 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = true diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5/clippy.toml new file mode 100644 index 0000000000000..1fa80c730126a --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = ["madules"] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6/clippy.toml new file mode 100644 index 0000000000000..cee857a155b9c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = ["entirely garbled"] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml index ddca5cfa57796..af3aa1cc62a49 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml @@ -9,4 +9,4 @@ module-item-order-groupings = [ ["PascalCase", ["ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl"]], ["lower_snake_case", ["fn"]] ] - +module-items-ordered-within-groupings = "none" diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ord_in_2/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ord_in_2/clippy.toml new file mode 100644 index 0000000000000..fd961c82d6f21 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ord_in_2/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = ["PascalCase"] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ord_in_3/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ord_in_3/clippy.toml new file mode 100644 index 0000000000000..de2a7307eca6e --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ord_in_3/clippy.toml @@ -0,0 +1,2 @@ +source-item-ordering = ["module"] +module-items-ordered-within-groupings = ["PascalCase"] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ord_within/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ord_within/clippy.toml new file mode 100644 index 0000000000000..e7640efde10ca --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ord_within/clippy.toml @@ -0,0 +1 @@ +module-items-ordered-within-groupings = "all" diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr new file mode 100644 index 0000000000000..c38c54ffeb0d5 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_4.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: data did not match any variant of untagged enum StringOrVecOfString The available options for configuring an ordering within module item groups are: "all", "none", or a list of module item group names (as configured with the `module-item-order-groupings` configuration option). + --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4/clippy.toml:1:41 + | +LL | module-items-ordered-within-groupings = true + | ^^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr new file mode 100644 index 0000000000000..7b1dafb6d0d5b --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_5.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file: unknown ordering group: `madules` was not specified in `module-items-ordered-within-groupings`, perhaps you meant `modules`? expected one of: `modules`, `use`, `macros`, `global_asm`, `UPPER_SNAKE_CASE`, `PascalCase`, `lower_snake_case` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr new file mode 100644 index 0000000000000..996cabeeed4a7 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_6.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file: unknown ordering group: `entirely garbled` was not specified in `module-items-ordered-within-groupings`, expected one of: `modules`, `use`, `macros`, `global_asm`, `UPPER_SNAKE_CASE`, `PascalCase`, `lower_snake_case` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs index 05eb40506db49..694ef45c75b08 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs @@ -1,15 +1,22 @@ //@aux-build:../../ui/auxiliary/proc_macros.rs -//@revisions: default default_exp bad_conf_1 bad_conf_2 bad_conf_3 +//@revisions: default default_exp bad_conf_1 bad_conf_2 bad_conf_3 bad_conf_4 bad_conf_5 bad_conf_6 //@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default //@[default_exp] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default_exp //@[bad_conf_1] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1 //@[bad_conf_2] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2 //@[bad_conf_3] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3 +//@[bad_conf_4] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_4 +//@[bad_conf_5] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_5 +//@[bad_conf_6] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_6 //@[default] check-pass //@[default_exp] check-pass //@[bad_conf_1] error-in-other-file: //@[bad_conf_2] error-in-other-file: //@[bad_conf_3] error-in-other-file: +//@[bad_conf_4] error-in-other-file: +//@[bad_conf_5] error-in-other-file: +//@[bad_conf_6] error-in-other-file: +//@compile-flags: --test #![allow(dead_code)] #![warn(clippy::arbitrary_source_item_ordering)] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr index 3605952bddc9b..a3c35a31c3314 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr @@ -1,223 +1,184 @@ -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:22:14 +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:26:14 | LL | use std::rc::Weak; | ^^^^ | note: should be placed before `SNAKE_CASE` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:20:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:23:7 | LL | const SNAKE_CASE: &str = "zzzzzzzz"; | ^^^^^^^^^^ = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:67:1 - | -LL | / impl CloneSelf for StructOrdered { -LL | | -LL | | fn clone_self(&self) -> Self { -LL | | Self { -... | -LL | | } - | |_^ - | -note: should be placed before the following item - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:1 - | -LL | / impl Default for StructOrdered { -LL | | fn default() -> Self { -LL | | Self { -LL | | a: true, -... | -LL | | } - | |_^ - -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:145:7 +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:165:7 | LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `TraitUnorderedItemKinds` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:132:7 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:7 | LL | trait TraitUnorderedItemKinds { | ^^^^^^^^^^^^^^^^^^^^^^^ -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:163:1 - | -LL | impl BasicEmptyTrait for StructOrdered {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: should be placed before the following item - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:148:1 - | -LL | / impl TraitUnordered for StructUnordered { -LL | | const A: bool = false; -LL | | const C: bool = false; -LL | | const B: bool = false; -... | -LL | | } - | |_^ - -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:184:5 +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:204:5 | LL | mod this_is_in_the_wrong_position { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `main` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:179:4 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:199:4 | LL | fn main() { | ^^^^ -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:194:7 +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:214:7 | LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `ZisShouldBeBeforeZeMainFn` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:192:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:212:8 | LL | struct ZisShouldBeBeforeZeMainFn; | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:12:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:44:5 | -LL | const AFTER: i8 = 0; - | ^^^^^ +LL | B, + | ^ | -note: should be placed before `BEFORE` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:10:11 +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:43:5 | -LL | const BEFORE: i8 = 0; - | ^^^^^^ +LL | C, + | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:40:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 | -LL | B, +LL | g: u8, | ^ | -note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:39:5 +note: should be placed before `r` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:56:5 | -LL | C, +LL | r: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:59:5 + | +LL | b: u8, + | ^ + | +note: should be placed before `g` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:92:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:112:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:91:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:111:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:101:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:5 | LL | b: bool, | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:100:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:5 | LL | c: bool, | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:141:11 | LL | const B: bool; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:140:11 | LL | const C: bool; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:128:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:148:8 | LL | fn b(); | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:127:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:147:8 | LL | fn c(); | ^ error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:135:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:5 | LL | const A: bool; | ^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:133:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:153:5 | LL | type SomeType; | ^^^^^^^^^^^^^^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:151:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11 | LL | const B: bool = false; | ^ | note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:150:11 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:170:11 | LL | const C: bool = false; | ^ error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:158:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:178:8 | LL | fn b() {} | ^ | note: should be placed before `c` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:157:8 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:177:8 | LL | fn c() {} | ^ error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:169:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:189:5 | LL | const A: bool = false; | ^^^^^^^^^^^^^^^^^^^^^^ | note: should be placed before `SomeType` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:167:5 + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:5 | LL | type SomeType = (); | ^^^^^^^^^^^^^^^^^^^ -error: incorrect ordering of items (must be alphabetically ordered) - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:11 - | -LL | const A: i8 = 1; - | ^ - | -note: should be placed before `C` - --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:186:11 - | -LL | const C: i8 = 0; - | ^ - -error: aborting due to 17 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr new file mode 100644 index 0000000000000..a3c35a31c3314 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr @@ -0,0 +1,184 @@ +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:26:14 + | +LL | use std::rc::Weak; + | ^^^^ + | +note: should be placed before `SNAKE_CASE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:23:7 + | +LL | const SNAKE_CASE: &str = "zzzzzzzz"; + | ^^^^^^^^^^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:165:7 + | +LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `TraitUnorderedItemKinds` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:7 + | +LL | trait TraitUnorderedItemKinds { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:204:5 + | +LL | mod this_is_in_the_wrong_position { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `main` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:199:4 + | +LL | fn main() { + | ^^^^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:214:7 + | +LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `ZisShouldBeBeforeZeMainFn` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:212:8 + | +LL | struct ZisShouldBeBeforeZeMainFn; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:44:5 + | +LL | B, + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:43:5 + | +LL | C, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + | +note: should be placed before `r` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:56:5 + | +LL | r: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:59:5 + | +LL | b: u8, + | ^ + | +note: should be placed before `g` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:112:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:111:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:141:11 + | +LL | const B: bool; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:140:11 + | +LL | const C: bool; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:148:8 + | +LL | fn b(); + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:147:8 + | +LL | fn c(); + | ^ + +error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:5 + | +LL | const A: bool; + | ^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:153:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11 + | +LL | const B: bool = false; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:170:11 + | +LL | const C: bool = false; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:178:8 + | +LL | fn b() {} + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:177:8 + | +LL | fn c() {} + | ^ + +error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:189:5 + | +LL | const A: bool = false; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:5 + | +LL | type SomeType = (); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr new file mode 100644 index 0000000000000..3fdd706fc627f --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr @@ -0,0 +1,271 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:23:7 + | +LL | const SNAKE_CASE: &str = "zzzzzzzz"; + | ^^^^^^^^^^ + | +note: should be placed before `ZNAKE_CASE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:22:7 + | +LL | const ZNAKE_CASE: &str = "123"; + | ^^^^^^^^^^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:26:14 + | +LL | use std::rc::Weak; + | ^^^^ + | +note: should be placed before `SNAKE_CASE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:23:7 + | +LL | const SNAKE_CASE: &str = "zzzzzzzz"; + | ^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:64:8 + | +LL | struct EnumWithExternButAtWrongPosition { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `EnumWithoutExtern` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:55:8 + | +LL | struct EnumWithoutExtern { + | ^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:87:1 + | +LL | / impl CloneSelf for StructOrdered { +LL | | +LL | | fn clone_self(&self) -> Self { +LL | | Self { +... | +LL | | } + | |_^ + | +note: should be placed before the following item + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:77:1 + | +LL | / impl Default for StructOrdered { +LL | | fn default() -> Self { +LL | | Self { +LL | | a: true, +... | +LL | | } + | |_^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:165:7 + | +LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `TraitUnorderedItemKinds` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:152:7 + | +LL | trait TraitUnorderedItemKinds { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:183:1 + | +LL | impl BasicEmptyTrait for StructOrdered {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before the following item + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:168:1 + | +LL | / impl TraitUnordered for StructUnordered { +LL | | const A: bool = false; +LL | | const C: bool = false; +LL | | const B: bool = false; +... | +LL | | } + | |_^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:204:5 + | +LL | mod this_is_in_the_wrong_position { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `main` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:199:4 + | +LL | fn main() { + | ^^^^ + +error: incorrect ordering of items (module item groupings specify another order) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:214:7 + | +LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `ZisShouldBeBeforeZeMainFn` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:212:8 + | +LL | struct ZisShouldBeBeforeZeMainFn; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:14:11 + | +LL | const AFTER: i8 = 0; + | ^^^^^ + | +note: should be placed before `BEFORE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:12:11 + | +LL | const BEFORE: i8 = 0; + | ^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:44:5 + | +LL | B, + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:43:5 + | +LL | C, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + | +note: should be placed before `r` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:56:5 + | +LL | r: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:59:5 + | +LL | b: u8, + | ^ + | +note: should be placed before `g` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:57:5 + | +LL | g: u8, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:112:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:111:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:141:11 + | +LL | const B: bool; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:140:11 + | +LL | const C: bool; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:148:8 + | +LL | fn b(); + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:147:8 + | +LL | fn c(); + | ^ + +error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:5 + | +LL | const A: bool; + | ^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:153:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11 + | +LL | const B: bool = false; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:170:11 + | +LL | const C: bool = false; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:178:8 + | +LL | fn b() {} + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:177:8 + | +LL | fn c() {} + | ^ + +error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:189:5 + | +LL | const A: bool = false; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:5 + | +LL | type SomeType = (); + | ^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:207:11 + | +LL | const A: i8 = 1; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:206:11 + | +LL | const C: i8 = 0; + | ^ + +error: aborting due to 21 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs index 9e65a9cca0da1..1cfed9790c120 100644 --- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs @@ -1,6 +1,8 @@ //@aux-build:../../ui/auxiliary/proc_macros.rs -//@revisions: default +//@revisions: default default_exp ord_within //@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default +//@[default_exp] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default_exp +//@[ord_within] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_within #![allow(dead_code)] #![warn(clippy::arbitrary_source_item_ordering)] @@ -10,14 +12,16 @@ mod i_am_just_right { const BEFORE: i8 = 0; const AFTER: i8 = 0; - //~^ arbitrary_source_item_ordering + //~[ord_within]^ arbitrary_source_item_ordering } // Use statements should not be linted internally - this is normally auto-sorted using rustfmt. use std::rc::Rc; use std::sync::{Arc, Barrier, RwLock}; +const ZNAKE_CASE: &str = "123"; const SNAKE_CASE: &str = "zzzzzzzz"; +//~[ord_within]^ arbitrary_source_item_ordering use std::rc::Weak; //~^ arbitrary_source_item_ordering @@ -48,6 +52,22 @@ enum EnumUnorderedAllowed { B, } +struct EnumWithoutExtern { + r: u8, + g: u8, + //~^ arbitrary_source_item_ordering + b: u8, + //~^ arbitrary_source_item_ordering +} + +#[repr(C)] +struct EnumWithExternButAtWrongPosition { + //~[ord_within]^ arbitrary_source_item_ordering + r: u8, + g: u8, + b: u8, +} + struct StructOrdered { a: bool, b: bool, @@ -65,7 +85,7 @@ impl Default for StructOrdered { } impl CloneSelf for StructOrdered { - //~^ arbitrary_source_item_ordering + //~[ord_within]^ arbitrary_source_item_ordering fn clone_self(&self) -> Self { Self { a: true, @@ -161,7 +181,7 @@ impl TraitUnordered for StructUnordered { // Trait impls should be located just after the type they implement it for. impl BasicEmptyTrait for StructOrdered {} -//~^ arbitrary_source_item_ordering +//~[ord_within]^ arbitrary_source_item_ordering impl TraitUnorderedItemKinds for StructUnordered { type SomeType = (); @@ -185,7 +205,7 @@ mod this_is_in_the_wrong_position { //~^ arbitrary_source_item_ordering const C: i8 = 0; const A: i8 = 1; - //~^ arbitrary_source_item_ordering + //~[ord_within]^ arbitrary_source_item_ordering } #[derive(Default, std::clone::Clone)] diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr new file mode 100644 index 0000000000000..fcd7864c6677d --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.default.stderr @@ -0,0 +1,19 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:36:5 + | +LL | a: bool, + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 + | +LL | b: bool, + | ^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:33:8 + | +LL | #[deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr new file mode 100644 index 0000000000000..81c35ff778b7f --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_2.stderr @@ -0,0 +1,36 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:25:8 + | +LL | struct OrderedChecked { + | ^^^^^^^^^^^^^^ + | +note: should be placed before `Unordered` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:19:8 + | +LL | struct Unordered { + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:10:9 + | +LL | #![deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:36:5 + | +LL | a: bool, + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 + | +LL | b: bool, + | ^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:33:8 + | +LL | #[deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr new file mode 100644 index 0000000000000..09ede57f295e8 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_in_3.stderr @@ -0,0 +1,19 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:25:8 + | +LL | struct OrderedChecked { + | ^^^^^^^^^^^^^^ + | +note: should be placed before `Unordered` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:19:8 + | +LL | struct Unordered { + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:10:9 + | +LL | #![deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr new file mode 100644 index 0000000000000..7c515f050c127 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.ord_within.stderr @@ -0,0 +1,60 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:25:8 + | +LL | struct OrderedChecked { + | ^^^^^^^^^^^^^^ + | +note: should be placed before `Unordered` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:19:8 + | +LL | struct Unordered { + | ^^^^^^^^^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:10:9 + | +LL | #![deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:46:4 + | +LL | fn before_main() {} + | ^^^^^^^^^^^ + | +note: should be placed before `main` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:42:4 + | +LL | fn main() { + | ^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:36:5 + | +LL | a: bool, + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:35:5 + | +LL | b: bool, + | ^ +note: the lint level is defined here + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:33:8 + | +LL | #[deny(clippy::arbitrary_source_item_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:52:11 + | +LL | const A: i8 = 0; + | ^ + | +note: should be placed before `B` + --> tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs:51:11 + | +LL | const B: i8 = 1; + | ^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs new file mode 100644 index 0000000000000..cb6d0170b8f97 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/selective_ordering.rs @@ -0,0 +1,54 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: default ord_within ord_in_2 ord_in_3 +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default +//@[ord_within] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_within +//@[ord_in_2] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_in_2 +//@[ord_in_3] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/ord_in_3 +//@compile-flags: --test + +#![allow(dead_code)] +#![deny(clippy::arbitrary_source_item_ordering)] + +#[allow(clippy::arbitrary_source_item_ordering)] +struct Ordered { + a: bool, + b: bool, +} + +#[allow(clippy::arbitrary_source_item_ordering)] +struct Unordered { + b: bool, + a: bool, +} + +#[deny(clippy::arbitrary_source_item_ordering)] +struct OrderedChecked { + //~[ord_within]^ arbitrary_source_item_ordering + //~[ord_in_2]| arbitrary_source_item_ordering + //~[ord_in_3]| arbitrary_source_item_ordering + a: bool, + b: bool, +} + +#[deny(clippy::arbitrary_source_item_ordering)] +struct UnorderedChecked { + b: bool, + a: bool, + //~[ord_within]^ arbitrary_source_item_ordering + //~[default]| arbitrary_source_item_ordering + //~[ord_in_2]| arbitrary_source_item_ordering +} + +fn main() { + // test code goes here +} + +fn before_main() {} +//~[ord_within]^ arbitrary_source_item_ordering + +#[cfg(test)] +mod test { + const B: i8 = 1; + const A: i8 = 0; + //~[ord_within]^ arbitrary_source_item_ordering +} diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr index 86e30409af068..d0fce3614a145 100644 --- a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr +++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type_with_replacement/await_holding_invalid_type.stderr @@ -1,11 +1,8 @@ error: error reading Clippy's configuration file: replacement not allowed for this configuration - --> $DIR/tests/ui-toml/await_holding_invalid_type_with_replacement/clippy.toml:1:31 + --> $DIR/tests/ui-toml/await_holding_invalid_type_with_replacement/clippy.toml:2:5 | -LL | await-holding-invalid-types = [ - | _______________________________^ -LL | | { path = "std::string::String", replacement = "std::net::Ipv4Addr" }, -LL | | ] - | |_^ +LL | { path = "std::string::String", replacement = "std::net::Ipv4Addr" }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/clippy.toml b/src/tools/clippy/tests/ui-toml/collapsible_if/clippy.toml new file mode 100644 index 0000000000000..592cea90cff5c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/clippy.toml @@ -0,0 +1 @@ +lint-commented-code = true diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed new file mode 100644 index 0000000000000..f695f9804d59c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.fixed @@ -0,0 +1,34 @@ +#![allow(clippy::eq_op, clippy::nonminimal_bool)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let (x, y) = ("hello", "world"); + + if x == "hello" + // Comment must be kept + && y == "world" { + println!("Hello world!"); + } + //~^^^^^^ collapsible_if + + // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 + if x == "hello" // Inner comment + && y == "world" { + println!("Hello world!"); + } + //~^^^^^ collapsible_if + + if x == "hello" + /* Inner comment */ + && y == "world" { + println!("Hello world!"); + } + //~^^^^^^ collapsible_if + + if x == "hello" /* Inner comment */ + && y == "world" { + println!("Hello world!"); + } + //~^^^^^ collapsible_if +} diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.rs b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.rs new file mode 100644 index 0000000000000..868b4adcde502 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.rs @@ -0,0 +1,38 @@ +#![allow(clippy::eq_op, clippy::nonminimal_bool)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let (x, y) = ("hello", "world"); + + if x == "hello" { + // Comment must be kept + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^^ collapsible_if + + // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 + if x == "hello" { // Inner comment + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^ collapsible_if + + if x == "hello" { + /* Inner comment */ + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^^ collapsible_if + + if x == "hello" { /* Inner comment */ + if y == "world" { + println!("Hello world!"); + } + } + //~^^^^^ collapsible_if +} diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr new file mode 100644 index 0000000000000..a12c2112f5877 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if.stderr @@ -0,0 +1,80 @@ +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if.rs:8:5 + | +LL | / if x == "hello" { +LL | | // Comment must be kept +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` +help: collapse nested if block + | +LL ~ if x == "hello" +LL | // Comment must be kept +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if.rs:17:5 + | +LL | / if x == "hello" { // Inner comment +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if x == "hello" // Inner comment +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if.rs:24:5 + | +LL | / if x == "hello" { +LL | | /* Inner comment */ +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if x == "hello" +LL | /* Inner comment */ +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if.rs:32:5 + | +LL | / if x == "hello" { /* Inner comment */ +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if x == "hello" /* Inner comment */ +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } + | + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed new file mode 100644 index 0000000000000..5e189471b0005 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.fixed @@ -0,0 +1,24 @@ +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) + // with comment + && let Some(b) = Some(4) { + let _ = a + b; + } + //~^^^^^^ collapsible_if + + if let Some(a) = Some(3) + // with comment + && a + 1 == 4 { + let _ = a; + } + //~^^^^^^ collapsible_if + + if Some(3) == Some(4).map(|x| x - 1) + // with comment + && let Some(b) = Some(4) { + let _ = b; + } + //~^^^^^^ collapsible_if +} diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs new file mode 100644 index 0000000000000..525eebf632a0c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs @@ -0,0 +1,27 @@ +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) { + // with comment + if let Some(b) = Some(4) { + let _ = a + b; + } + } + //~^^^^^^ collapsible_if + + if let Some(a) = Some(3) { + // with comment + if a + 1 == 4 { + let _ = a; + } + } + //~^^^^^^ collapsible_if + + if Some(3) == Some(4).map(|x| x - 1) { + // with comment + if let Some(b) = Some(4) { + let _ = b; + } + } + //~^^^^^^ collapsible_if +} diff --git a/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr new file mode 100644 index 0000000000000..c9de166a969ad --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_if_let_chains.stderr @@ -0,0 +1,64 @@ +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:4:5 + | +LL | / if let Some(a) = Some(3) { +LL | | // with comment +LL | | if let Some(b) = Some(4) { +LL | | let _ = a + b; +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL | // with comment +LL ~ && let Some(b) = Some(4) { +LL | let _ = a + b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:12:5 + | +LL | / if let Some(a) = Some(3) { +LL | | // with comment +LL | | if a + 1 == 4 { +LL | | let _ = a; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL | // with comment +LL ~ && a + 1 == 4 { +LL | let _ = a; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui-toml/collapsible_if/collapsible_if_let_chains.rs:20:5 + | +LL | / if Some(3) == Some(4).map(|x| x - 1) { +LL | | // with comment +LL | | if let Some(b) = Some(4) { +LL | | let _ = b; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if Some(3) == Some(4).map(|x| x - 1) +LL | // with comment +LL ~ && let Some(b) = Some(4) { +LL | let _ = b; +LL ~ } + | + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs index e953a2a4e90bc..2a6097fb57953 100644 --- a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs +++ b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs @@ -11,9 +11,9 @@ fn issue10272() { // should trigger warning let x = Cell::new(true); if x.get() { + //~^ ifs_same_cond } else if !x.take() { } else if x.get() { - //~^ ifs_same_cond } else { } } diff --git a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr index d67e7fca6565b..adc44358c4c7b 100644 --- a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr +++ b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr @@ -1,14 +1,12 @@ -error: this `if` has the same condition as a previous `if` - --> tests/ui-toml/ifs_same_cond/ifs_same_cond.rs:15:15 - | -LL | } else if x.get() { - | ^^^^^^^ - | -note: same as this +error: these `if` branches have the same condition --> tests/ui-toml/ifs_same_cond/ifs_same_cond.rs:13:8 | LL | if x.get() { | ^^^^^^^ +... +LL | } else if x.get() { + | ^^^^^^^ + | = note: `-D clippy::ifs-same-cond` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]` diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml new file mode 100644 index 0000000000000..3ed7cedbd147d --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/clippy.toml @@ -0,0 +1 @@ +allow-exact-repetitions = false diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs new file mode 100644 index 0000000000000..20603766624c7 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs @@ -0,0 +1,13 @@ +#![warn(clippy::module_name_repetitions)] +#![allow(dead_code)] + +pub mod foo { + // this line should produce a warning: + pub fn foo() {} + //~^ module_name_repetitions + + // but this line shouldn't + pub fn to_foo() {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr new file mode 100644 index 0000000000000..8e6f726d02c01 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.stderr @@ -0,0 +1,11 @@ +error: item name is the same as its containing module's name + --> tests/ui-toml/item_name_repetitions/allow_exact_repetitions/item_name_repetitions.rs:6:12 + | +LL | pub fn foo() {} + | ^^^ + | + = note: `-D clippy::module-name-repetitions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::module_name_repetitions)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs index 2465fe45645f1..d3d5b0c103e7f 100644 --- a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs +++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs @@ -251,6 +251,16 @@ pub mod issue13219 { } } +#[macro_export] +macro_rules! issue14488 { + ($e:expr) => { + #[expect(clippy::macro_metavars_in_unsafe)] + unsafe { + $e + } + }; +} + fn main() { allow_works!(1); simple!(1); @@ -271,4 +281,10 @@ fn main() { multiple_unsafe_blocks!(1, 1, 1); unsafe_from_root_ctxt!(unsafe { 1 }); nested_macros!(1, 1); + + // These two invocations lead to two expanded unsafe blocks, each with an `#[expect]` on it. + // Only of them gets a warning, which used to result in an unfulfilled expectation for the other + // expanded unsafe block. + issue14488!(1); + issue14488!(2); } diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed index 36540bf1dcf73..2877871d0bf4c 100644 --- a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed +++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed @@ -1,3 +1,4 @@ +#![allow(clippy::uninlined_format_args)] #![deny(clippy::index_refutable_slice)] fn below_limit() { diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs index da76bb20fd961..f958b92a102a3 100644 --- a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs +++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs @@ -1,3 +1,4 @@ +#![allow(clippy::uninlined_format_args)] #![deny(clippy::index_refutable_slice)] fn below_limit() { diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr index 022deb330e6e3..e1a8941e102f5 100644 --- a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr +++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr @@ -1,11 +1,11 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:5:17 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:6:17 | LL | if let Some(slice) = slice { | ^^^^^ | note: the lint level is defined here - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:1:9 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:2:9 | LL | #![deny(clippy::index_refutable_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/clippy.toml b/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/clippy.toml new file mode 100644 index 0000000000000..2fe64b2755b69 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/clippy.toml @@ -0,0 +1 @@ +missing-docs-allow-unused = true diff --git a/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs b/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs new file mode 100644 index 0000000000000..155f680c7b13a --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs @@ -0,0 +1,26 @@ +//! Test file for missing_docs_in_private_items lint with allow_unused configuration +#![warn(clippy::missing_docs_in_private_items)] +#![allow(dead_code)] + +/// A struct with some documented and undocumented fields +struct Test { + /// This field is documented + field1: i32, + _unused: i32, // This should not trigger a warning because it starts with an underscore + field3: i32, //~ missing_docs_in_private_items +} + +struct Test2 { + //~^ missing_docs_in_private_items + _field1: i32, // This should not trigger a warning + _field2: i32, // This should not trigger a warning +} + +struct Test3 { + //~^ missing_docs_in_private_items + /// This field is documented although this is not mandatory + _unused: i32, // This should not trigger a warning because it starts with an underscore + field2: i32, //~ missing_docs_in_private_items +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr b/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr new file mode 100644 index 0000000000000..8f511883e9005 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.stderr @@ -0,0 +1,38 @@ +error: missing documentation for a struct field + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:10:5 + | +LL | field3: i32, + | ^^^^^^^^^^^ + | + = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]` + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:13:1 + | +LL | / struct Test2 { +LL | | +LL | | _field1: i32, // This should not trigger a warning +LL | | _field2: i32, // This should not trigger a warning +LL | | } + | |_^ + +error: missing documentation for a struct + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:19:1 + | +LL | / struct Test3 { +LL | | +LL | | /// This field is documented although this is not mandatory +LL | | _unused: i32, // This should not trigger a warning because it starts with an underscore +LL | | field2: i32, +LL | | } + | |_^ + +error: missing documentation for a struct field + --> tests/ui-toml/missing_docs_allow_unused/missing_docs_allow_unused.rs:23:5 + | +LL | field2: i32, + | ^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs b/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs index 08a8e1186d5cb..13e19e9fe14bf 100644 --- a/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs +++ b/src/tools/clippy/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs @@ -29,7 +29,7 @@ unsafe impl Send for MyOption {} //~^ non_send_fields_in_send_ty // All fields are disallowed when raw pointer heuristic is off -extern "C" { +unsafe extern "C" { type NonSend; } diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml index 41dbd5068479b..c7a326f282951 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -14,4 +14,7 @@ disallowed-methods = [ "conf_disallowed_methods::Struct::method", "conf_disallowed_methods::Trait::provided_method", "conf_disallowed_methods::Trait::implemented_method", + # re-exports + "conf_disallowed_methods::identity", + "conf_disallowed_methods::renamed", ] diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index dd170d6baf857..2dac01649a0fd 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -8,6 +8,9 @@ extern crate regex; use futures::stream::{empty, select_all}; use regex::Regex; +use std::convert::identity; +use std::hint::black_box as renamed; + fn local_fn() {} struct Struct; @@ -71,4 +74,9 @@ fn main() { //~^ disallowed_methods s.implemented_method(); //~^ disallowed_methods + + identity(()); + //~^ disallowed_methods + renamed(1); + //~^ disallowed_methods } diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index f7dda81eb936e..20474ad6e9270 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -1,5 +1,5 @@ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:33:14 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:36:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); | ^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let re = Regex::new(r"ab.*c").unwrap(); = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` error: use of a disallowed method `regex::Regex::is_match` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:35:8 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:38:8 | LL | re.is_match("abc"); | ^^^^^^^^ @@ -16,76 +16,88 @@ LL | re.is_match("abc"); = note: no matching allowed error: use of a disallowed method `std::iter::Iterator::sum` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:39:14 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:42:14 | LL | a.iter().sum::(); | ^^^ error: use of a disallowed method `slice::sort_unstable` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:42:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:45:7 | LL | a.sort_unstable(); | ^^^^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:46:20 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:49:20 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:50:61 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:53:61 | LL | let indirect: fn(&str) -> Result = Regex::new; | ^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:54:28 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:57:28 | LL | let in_call = Box::new(f32::clamp); | ^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:56:53 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:59:53 | LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); | ^^^^^^^^^^ error: use of a disallowed method `futures::stream::select_all` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:60:31 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:63:31 | LL | let same_name_as_module = select_all(vec![empty::<()>()]); | ^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::local_fn` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:63:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:66:5 | LL | local_fn(); | ^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::local_mod::f` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:65:5 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:68:5 | LL | local_mod::f(); | ^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Struct::method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:68:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:71:7 | LL | s.method(); | ^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:70:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:73:7 | LL | s.provided_method(); | ^^^^^^^^^^^^^^^ error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method` - --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:72:7 + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:75:7 | LL | s.implemented_method(); | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: use of a disallowed method `conf_disallowed_methods::identity` + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:78:5 + | +LL | identity(()); + | ^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::renamed` + --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:80:5 + | +LL | renamed(1); + | ^^^^^^^ + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml index 6cb9e2ef95467..08e35017f782d 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml @@ -6,7 +6,7 @@ disallowed-types = [ "std::thread::Thread", "std::time::Instant", "std::io::Read", - "std::primitive::usize", + "usize", "bool", # can give path and reason with an inline table { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" }, diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr index 18bc36ca1e33b..061cdc7649ad2 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr @@ -37,7 +37,7 @@ error: use of a disallowed type `std::io::Read` LL | fn trait_obj(_: &dyn std::io::Read) {} | ^^^^^^^^^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:26:33 | LL | fn full_and_single_path_prim(_: usize, _: bool) {} @@ -49,13 +49,13 @@ error: use of a disallowed type `bool` LL | fn full_and_single_path_prim(_: usize, _: bool) {} | ^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:30:28 | LL | fn const_generics() {} | ^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:33:24 | LL | struct GenArg([u8; U]); @@ -123,7 +123,7 @@ error: use of a disallowed type `proc_macro2::Ident` LL | let _ = syn::Ident::new("", todo!()); | ^^^^^^^^^^ -error: use of a disallowed type `std::primitive::usize` +error: use of a disallowed type `usize` --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:61:12 | LL | let _: usize = 64_usize; diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml index f43c9d97e825d..3cb8523562a8f 100644 --- a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml @@ -1 +1 @@ -lint-inconsistent-struct-field-initializers = true +check-inconsistent-struct-field-initializers = true diff --git a/src/tools/clippy/tests/ui-toml/toml_invalid_path/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_invalid_path/clippy.toml new file mode 100644 index 0000000000000..997ed47b71cb6 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_invalid_path/clippy.toml @@ -0,0 +1,17 @@ +[[disallowed-macros]] +path = "bool" + +[[disallowed-methods]] +path = "std::process::current_exe" + +[[disallowed-methods]] +path = "" + +[[disallowed-types]] +path = "std::result::Result::Err" + +# negative test + +[[disallowed-methods]] +path = "std::current_exe" +allow-invalid = true diff --git a/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs b/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs new file mode 100644 index 0000000000000..ff4eada390071 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs @@ -0,0 +1,6 @@ +//@error-in-other-file: expected a macro, found a primitive type +//@error-in-other-file: `std::process::current_exe` does not refer to a reachable function +//@error-in-other-file: `` does not refer to a reachable function +//@error-in-other-file: expected a type, found a variant + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr b/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr new file mode 100644 index 0000000000000..59a427dc99cae --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr @@ -0,0 +1,38 @@ +warning: expected a macro, found a primitive type + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1 + | +LL | / [[disallowed-macros]] +LL | | path = "bool" + | |_____________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: `std::process::current_exe` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1 + | +LL | / [[disallowed-methods]] +LL | | path = "std::process::current_exe" + | |__________________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: `` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1 + | +LL | / [[disallowed-methods]] +LL | | path = "" + | |_________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: expected a type, found a variant + --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:10:1 + | +LL | / [[disallowed-types]] +LL | | path = "std::result::Result::Err" + | |_________________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: 4 warnings emitted + diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml new file mode 100644 index 0000000000000..82560cfd5e2a4 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml @@ -0,0 +1,4 @@ +# In the following configuration, "recommendation" should be "reason" or "replacement". +disallowed-macros = [ + { path = "std::panic", recommendation = "return a `std::result::Result::Error` instead" }, +] diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs b/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs new file mode 100644 index 0000000000000..9c770c31f6f8d --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.rs @@ -0,0 +1,5 @@ +#[rustfmt::skip] +//@error-in-other-file: error reading Clippy's configuration file: data did not match any variant of untagged enum DisallowedPathEnum +fn main() { + panic!(); +} diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr new file mode 100644 index 0000000000000..b564709721d5c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_config_struct_field/toml_unknown_config_struct_field.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: data did not match any variant of untagged enum DisallowedPathEnum + --> $DIR/tests/ui-toml/toml_unknown_config_struct_field/clippy.toml:3:5 + | +LL | { path = "std::panic", recommendation = "return a `std::result::Result::Error` instead" }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index acfe739277cc7..6ee77ebd8ece2 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -5,6 +5,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect accept-comment-above-statement allow-comparison-to-zero allow-dbg-in-tests + allow-exact-repetitions allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests @@ -29,12 +30,11 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect array-size-threshold avoid-breaking-exported-api await-holding-invalid-types - blacklisted-names cargo-ignore-publish check-incompatible-msrv-in-tests + check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold - cyclomatic-complexity-threshold disallowed-macros disallowed-methods disallowed-names @@ -49,7 +49,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect future-size-threshold ignore-interior-mutability large-error-threshold - lint-inconsistent-struct-field-initializers + lint-commented-code literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -58,8 +58,10 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect max-suggested-slice-pattern-length max-trait-bounds min-ident-chars-threshold + missing-docs-allow-unused missing-docs-in-crate-items module-item-order-groupings + module-items-ordered-within-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior @@ -97,6 +99,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect accept-comment-above-statement allow-comparison-to-zero allow-dbg-in-tests + allow-exact-repetitions allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests @@ -121,12 +124,11 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect array-size-threshold avoid-breaking-exported-api await-holding-invalid-types - blacklisted-names cargo-ignore-publish check-incompatible-msrv-in-tests + check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold - cyclomatic-complexity-threshold disallowed-macros disallowed-methods disallowed-names @@ -141,7 +143,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect future-size-threshold ignore-interior-mutability large-error-threshold - lint-inconsistent-struct-field-initializers + lint-commented-code literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -150,8 +152,10 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect max-suggested-slice-pattern-length max-trait-bounds min-ident-chars-threshold + missing-docs-allow-unused missing-docs-in-crate-items module-item-order-groupings + module-items-ordered-within-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior @@ -189,6 +193,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni accept-comment-above-statement allow-comparison-to-zero allow-dbg-in-tests + allow-exact-repetitions allow-expect-in-consts allow-expect-in-tests allow-indexing-slicing-in-tests @@ -213,12 +218,11 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni array-size-threshold avoid-breaking-exported-api await-holding-invalid-types - blacklisted-names cargo-ignore-publish check-incompatible-msrv-in-tests + check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold - cyclomatic-complexity-threshold disallowed-macros disallowed-methods disallowed-names @@ -233,7 +237,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni future-size-threshold ignore-interior-mutability large-error-threshold - lint-inconsistent-struct-field-initializers + lint-commented-code literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -242,8 +246,10 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni max-suggested-slice-pattern-length max-trait-bounds min-ident-chars-threshold + missing-docs-allow-unused missing-docs-in-crate-items module-item-order-groupings + module-items-ordered-within-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior diff --git a/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/clippy.toml new file mode 100644 index 0000000000000..e664256d2a2fd --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/clippy.toml @@ -0,0 +1,10 @@ +# The first two `disallowed-methods` paths should generate warnings, but the third should not. + +[[disallowed-methods]] +path = "regex::Regex::new_" + +[[disallowed-methods]] +path = "regex::Regex_::new" + +[[disallowed-methods]] +path = "regex_::Regex::new" diff --git a/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs b/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs new file mode 100644 index 0000000000000..14f15e733111c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs @@ -0,0 +1,6 @@ +//@error-in-other-file: `regex::Regex::new_` does not refer to a reachable function +//@error-in-other-file: `regex::Regex_::new` does not refer to a reachable function + +extern crate regex; + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr b/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr new file mode 100644 index 0000000000000..e5fd548b26df4 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr @@ -0,0 +1,20 @@ +warning: `regex::Regex::new_` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:3:1 + | +LL | / [[disallowed-methods]] +LL | | path = "regex::Regex::new_" + | |___________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: `regex::Regex_::new` does not refer to a reachable function + --> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:6:1 + | +LL | / [[disallowed-methods]] +LL | | path = "regex::Regex_::new" + | |___________________________^ + | + = help: add `allow-invalid = true` to the entry to suppress this warning + +warning: 2 warnings emitted + diff --git a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs index 7f93d2071c9db..b60cb7632e201 100644 --- a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs +++ b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs @@ -12,7 +12,7 @@ fn f2() where T: Copy + Clone + Sync + Send + ?Sized, T: Unpin + PartialEq, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { } diff --git a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr index c5102c39d1cff..ba0f41167a00f 100644 --- a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr +++ b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr @@ -1,4 +1,4 @@ -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui-toml/type_repetition_in_bounds/main.rs:14:5 | LL | T: Unpin + PartialEq, diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr index 32ed78151d237..8a2f201009a92 100644 --- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr @@ -310,5 +310,49 @@ LL | let bar = unsafe {}; | = help: consider adding a safety comment on the preceding line -error: aborting due to 35 previous errors +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:638:52 + | +LL | const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:647:41 + | +LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:657:42 + | +LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:662:40 + | +LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: statement has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:719:5 + | +LL | _ = bar(); + | ^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:718:5 + | +LL | // SAFETY: unnecessary_safety_comment triggers here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 40 previous errors diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index 83a6986addf26..e9c5e5f9f1146 100644 --- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -390,5 +390,65 @@ LL | unsafe {} | = help: consider adding a safety comment on the preceding line -error: aborting due to 45 previous errors +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:638:52 + | +LL | const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:647:41 + | +LL | const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:657:42 + | +LL | const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:662:40 + | +LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 }; + | ^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:673:9 + | +LL | unsafe { here_is_another_variable_with_long_name }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:702:9 + | +LL | unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian_day_just_make_this_line_longer(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: statement has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:719:5 + | +LL | _ = bar(); + | ^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:718:5 + | +LL | // SAFETY: unnecessary_safety_comment triggers here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 52 previous errors diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index 6a3fda3df5c38..91a02bc3d7c65 100644 --- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -632,4 +632,95 @@ mod issue_11246 { // Safety: Another safety comment const FOO: () = unsafe {}; +// trait items and impl items +mod issue_11709 { + trait MyTrait { + const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 0 }; + //~^ ERROR: unsafe block missing a safety comment + + // SAFETY: safe + const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + + // SAFETY: unrelated + unsafe fn unsafe_fn() {} + + const NO_SAFETY_IN_TRAIT: i32 = unsafe { 1 }; + //~^ ERROR: unsafe block missing a safety comment + } + + struct UnsafeStruct; + + impl MyTrait for UnsafeStruct { + // SAFETY: safe in this impl + const NO_SAFETY_IN_TRAIT_BUT_IN_IMPL: u8 = unsafe { 2 }; + + const HAS_SAFETY_IN_TRAIT: i32 = unsafe { 3 }; + //~^ ERROR: unsafe block missing a safety comment + } + + impl UnsafeStruct { + const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 }; + //~^ ERROR: unsafe block missing a safety comment + } +} + +fn issue_13024() { + let mut just_a_simple_variable_with_a_very_long_name_that_has_seventy_eight_characters = 0; + let here_is_another_variable_with_long_name = 100; + + // SAFETY: fail ONLY if `accept-comment-above-statement = false` + just_a_simple_variable_with_a_very_long_name_that_has_seventy_eight_characters = + unsafe { here_is_another_variable_with_long_name }; + //~[disabled]^ undocumented_unsafe_blocks +} + +// https://docs.rs/time/0.3.36/src/time/offset_date_time.rs.html#35 +mod issue_11709_regression { + use std::num::NonZeroI32; + + struct Date { + value: NonZeroI32, + } + + impl Date { + const unsafe fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self { + Self { + // Safety: The caller must guarantee that `ordinal` is not zero. + value: unsafe { NonZeroI32::new_unchecked((year << 9) | ordinal as i32) }, + } + } + + const fn into_julian_day_just_make_this_line_longer(self) -> i32 { + 42 + } + } + + /// The Julian day of the Unix epoch. + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` + #[allow(unsafe_code)] + const UNIX_EPOCH_JULIAN_DAY: i32 = + unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian_day_just_make_this_line_longer(); + //~[disabled]^ undocumented_unsafe_blocks +} + +fn issue_13039() { + unsafe fn foo() -> usize { + 1234 + } + + fn bar() -> usize { + 1234 + } + + // SAFETY: unnecessary_safety_comment should not trigger here + _ = unsafe { foo() }; + + // SAFETY: unnecessary_safety_comment triggers here + _ = bar(); + //~^ unnecessary_safety_comment + + // SAFETY: unnecessary_safety_comment should not trigger here + _ = unsafe { foo() } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed index af72d6be0e096..20511cbed165e 100644 --- a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed +++ b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed @@ -15,7 +15,7 @@ mod my_crate { } } -use utils::{BAR, print}; +pub use utils::{BAR, print}; //~^ ERROR: usage of wildcard import use my_crate::utils::my_util_fn; //~^ ERROR: usage of wildcard import diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs index 91009dd8835f8..8d05910f471ba 100644 --- a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs +++ b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs @@ -15,7 +15,7 @@ mod my_crate { } } -use utils::*; +pub use utils::*; //~^ ERROR: usage of wildcard import use my_crate::utils::*; //~^ ERROR: usage of wildcard import diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr index 3d3be965aa411..5e624dd6c3cdc 100644 --- a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr +++ b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr @@ -1,8 +1,8 @@ error: usage of wildcard import - --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:18:5 + --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:18:9 | -LL | use utils::*; - | ^^^^^^^^ help: try: `utils::{BAR, print}` +LL | pub use utils::*; + | ^^^^^^^^ help: try: `utils::{BAR, print}` | = note: `-D clippy::wildcard-imports` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::wildcard_imports)]` diff --git a/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs b/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs index edcd5247f18ce..361bc2033934a 100644 --- a/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs +++ b/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs @@ -8,9 +8,11 @@ mod dont_warn { use std::arch::{asm, global_asm}; pub(super) unsafe fn use_asm() { - asm!(""); - asm!("", options()); - asm!("", options(nostack)); + unsafe { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + } } global_asm!(""); diff --git a/src/tools/clippy/tests/ui/asm_syntax_x86.rs b/src/tools/clippy/tests/ui/asm_syntax_x86.rs index 4e91f27cd3189..30401c9a0448a 100644 --- a/src/tools/clippy/tests/ui/asm_syntax_x86.rs +++ b/src/tools/clippy/tests/ui/asm_syntax_x86.rs @@ -5,17 +5,19 @@ mod warn_intel { use std::arch::{asm, global_asm}; pub(super) unsafe fn use_asm() { - asm!(""); - //~^ inline_asm_x86_intel_syntax + unsafe { + asm!(""); + //~^ inline_asm_x86_intel_syntax - asm!("", options()); - //~^ inline_asm_x86_intel_syntax + asm!("", options()); + //~^ inline_asm_x86_intel_syntax - asm!("", options(nostack)); - //~^ inline_asm_x86_intel_syntax + asm!("", options(nostack)); + //~^ inline_asm_x86_intel_syntax - asm!("", options(att_syntax)); - asm!("", options(nostack, att_syntax)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } } global_asm!(""); @@ -32,14 +34,16 @@ mod warn_att { use std::arch::{asm, global_asm}; pub(super) unsafe fn use_asm() { - asm!(""); - asm!("", options()); - asm!("", options(nostack)); - asm!("", options(att_syntax)); - //~^ inline_asm_x86_att_syntax - - asm!("", options(nostack, att_syntax)); - //~^ inline_asm_x86_att_syntax + unsafe { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + //~^ inline_asm_x86_att_syntax + + asm!("", options(nostack, att_syntax)); + //~^ inline_asm_x86_att_syntax + } } global_asm!(""); diff --git a/src/tools/clippy/tests/ui/asm_syntax_x86.stderr b/src/tools/clippy/tests/ui/asm_syntax_x86.stderr index 2dcd955f03479..8e068cf2349cd 100644 --- a/src/tools/clippy/tests/ui/asm_syntax_x86.stderr +++ b/src/tools/clippy/tests/ui/asm_syntax_x86.stderr @@ -1,31 +1,31 @@ error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:8:9 + --> tests/ui/asm_syntax_x86.rs:9:13 | -LL | asm!(""); - | ^^^^^^^^ +LL | asm!(""); + | ^^^^^^^^ | = help: use AT&T x86 assembly syntax = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_intel_syntax)]` error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:11:9 + --> tests/ui/asm_syntax_x86.rs:12:13 | -LL | asm!("", options()); - | ^^^^^^^^^^^^^^^^^^^ +LL | asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^ | = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:14:9 + --> tests/ui/asm_syntax_x86.rs:15:13 | -LL | asm!("", options(nostack)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | asm!("", options(nostack)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:21:5 + --> tests/ui/asm_syntax_x86.rs:23:5 | LL | global_asm!(""); | ^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | global_asm!(""); = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:24:5 + --> tests/ui/asm_syntax_x86.rs:26:5 | LL | global_asm!("", options()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,25 +41,25 @@ LL | global_asm!("", options()); = help: use AT&T x86 assembly syntax error: AT&T x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:38:9 + --> tests/ui/asm_syntax_x86.rs:41:13 | -LL | asm!("", options(att_syntax)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use Intel x86 assembly syntax = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_att_syntax)]` error: AT&T x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:41:9 + --> tests/ui/asm_syntax_x86.rs:44:13 | -LL | asm!("", options(nostack, att_syntax)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | asm!("", options(nostack, att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use Intel x86 assembly syntax error: AT&T x86 assembly syntax used - --> tests/ui/asm_syntax_x86.rs:47:5 + --> tests/ui/asm_syntax_x86.rs:51:5 | LL | global_asm!("", options(att_syntax)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/assign_ops.fixed b/src/tools/clippy/tests/ui/assign_ops.fixed index 18f0e04a8807e..429c20f95e919 100644 --- a/src/tools/clippy/tests/ui/assign_ops.fixed +++ b/src/tools/clippy/tests/ui/assign_ops.fixed @@ -1,7 +1,9 @@ +#![allow(clippy::useless_vec)] +#![warn(clippy::assign_op_pattern)] + use core::num::Wrapping; +use std::ops::{Mul, MulAssign}; -#[allow(dead_code, unused_assignments, clippy::useless_vec)] -#[warn(clippy::assign_op_pattern)] fn main() { let mut a = 5; a += 1; @@ -39,3 +41,35 @@ fn main() { v[0] = v[0] + v[1]; let _ = || v[0] = v[0] + v[1]; } + +fn cow_add_assign() { + use std::borrow::Cow; + let mut buf = Cow::Owned(String::from("bar")); + let cows = Cow::Borrowed("foo"); + + // this can be linted + buf += cows.clone(); + //~^ assign_op_pattern + + // this should not as cow Add is not commutative + buf = cows + buf; +} + +// check that we don't lint on op assign impls, because that's just the way to impl them + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Wrap(i64); + +impl Mul for Wrap { + type Output = Self; + + fn mul(self, rhs: i64) -> Self { + Wrap(self.0 * rhs) + } +} + +impl MulAssign for Wrap { + fn mul_assign(&mut self, rhs: i64) { + *self = *self * rhs + } +} diff --git a/src/tools/clippy/tests/ui/assign_ops.rs b/src/tools/clippy/tests/ui/assign_ops.rs index 8b05c74d86040..480ff07f150ea 100644 --- a/src/tools/clippy/tests/ui/assign_ops.rs +++ b/src/tools/clippy/tests/ui/assign_ops.rs @@ -1,7 +1,9 @@ +#![allow(clippy::useless_vec)] +#![warn(clippy::assign_op_pattern)] + use core::num::Wrapping; +use std::ops::{Mul, MulAssign}; -#[allow(dead_code, unused_assignments, clippy::useless_vec)] -#[warn(clippy::assign_op_pattern)] fn main() { let mut a = 5; a = a + 1; @@ -39,3 +41,35 @@ fn main() { v[0] = v[0] + v[1]; let _ = || v[0] = v[0] + v[1]; } + +fn cow_add_assign() { + use std::borrow::Cow; + let mut buf = Cow::Owned(String::from("bar")); + let cows = Cow::Borrowed("foo"); + + // this can be linted + buf = buf + cows.clone(); + //~^ assign_op_pattern + + // this should not as cow Add is not commutative + buf = cows + buf; +} + +// check that we don't lint on op assign impls, because that's just the way to impl them + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Wrap(i64); + +impl Mul for Wrap { + type Output = Self; + + fn mul(self, rhs: i64) -> Self { + Wrap(self.0 * rhs) + } +} + +impl MulAssign for Wrap { + fn mul_assign(&mut self, rhs: i64) { + *self = *self * rhs + } +} diff --git a/src/tools/clippy/tests/ui/assign_ops.stderr b/src/tools/clippy/tests/ui/assign_ops.stderr index 17f216ee4a071..881a333fbe4b9 100644 --- a/src/tools/clippy/tests/ui/assign_ops.stderr +++ b/src/tools/clippy/tests/ui/assign_ops.stderr @@ -1,5 +1,5 @@ error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:7:5 + --> tests/ui/assign_ops.rs:9:5 | LL | a = a + 1; | ^^^^^^^^^ help: replace it with: `a += 1` @@ -8,64 +8,70 @@ LL | a = a + 1; = help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:9:5 + --> tests/ui/assign_ops.rs:11:5 | LL | a = 1 + a; | ^^^^^^^^^ help: replace it with: `a += 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:11:5 + --> tests/ui/assign_ops.rs:13:5 | LL | a = a - 1; | ^^^^^^^^^ help: replace it with: `a -= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:13:5 + --> tests/ui/assign_ops.rs:15:5 | LL | a = a * 99; | ^^^^^^^^^^ help: replace it with: `a *= 99` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:15:5 + --> tests/ui/assign_ops.rs:17:5 | LL | a = 42 * a; | ^^^^^^^^^^ help: replace it with: `a *= 42` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:17:5 + --> tests/ui/assign_ops.rs:19:5 | LL | a = a / 2; | ^^^^^^^^^ help: replace it with: `a /= 2` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:19:5 + --> tests/ui/assign_ops.rs:21:5 | LL | a = a % 5; | ^^^^^^^^^ help: replace it with: `a %= 5` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:21:5 + --> tests/ui/assign_ops.rs:23:5 | LL | a = a & 1; | ^^^^^^^^^ help: replace it with: `a &= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:28:5 + --> tests/ui/assign_ops.rs:30:5 | LL | s = s + "bla"; | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:33:5 + --> tests/ui/assign_ops.rs:35:5 | LL | a = a + Wrapping(1u32); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:36:5 + --> tests/ui/assign_ops.rs:38:5 | LL | v[0] = v[0] + v[1]; | ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]` -error: aborting due to 11 previous errors +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:51:5 + | +LL | buf = buf + cows.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/assign_ops2.rs b/src/tools/clippy/tests/ui/assign_ops2.rs deleted file mode 100644 index 51867fa6962cf..0000000000000 --- a/src/tools/clippy/tests/ui/assign_ops2.rs +++ /dev/null @@ -1,77 +0,0 @@ -//@no-rustfix: overlapping suggestions -#![allow(clippy::uninlined_format_args)] - -#[allow(unused_assignments)] -#[warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] -fn main() { - let mut a = 5; - a += a + 1; - //~^ misrefactored_assign_op - - a += 1 + a; - //~^ misrefactored_assign_op - - a -= a - 1; - //~^ misrefactored_assign_op - - a *= a * 99; - //~^ misrefactored_assign_op - - a *= 42 * a; - //~^ misrefactored_assign_op - - a /= a / 2; - //~^ misrefactored_assign_op - - a %= a % 5; - //~^ misrefactored_assign_op - - a &= a & 1; - //~^ misrefactored_assign_op - - a *= a * a; - //~^ misrefactored_assign_op - - a = a * a * a; - a = a * 42 * a; - a = a * 2 + a; - a -= 1 - a; - a /= 5 / a; - a %= 42 % a; - a <<= 6 << a; -} - -// check that we don't lint on op assign impls, because that's just the way to impl them - -use std::ops::{Mul, MulAssign}; - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Wrap(i64); - -impl Mul for Wrap { - type Output = Self; - - fn mul(self, rhs: i64) -> Self { - Wrap(self.0 * rhs) - } -} - -impl MulAssign for Wrap { - fn mul_assign(&mut self, rhs: i64) { - *self = *self * rhs - } -} - -fn cow_add_assign() { - use std::borrow::Cow; - let mut buf = Cow::Owned(String::from("bar")); - let cows = Cow::Borrowed("foo"); - - // this can be linted - buf = buf + cows.clone(); - //~^ assign_op_pattern - - // this should not as cow Add is not commutative - buf = cows + buf; - println!("{}", buf); -} diff --git a/src/tools/clippy/tests/ui/assign_ops2.stderr b/src/tools/clippy/tests/ui/assign_ops2.stderr deleted file mode 100644 index d9ecd3f8b230c..0000000000000 --- a/src/tools/clippy/tests/ui/assign_ops2.stderr +++ /dev/null @@ -1,166 +0,0 @@ -error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:8:5 - | -LL | a += a + 1; - | ^^^^^^^^^^ - | - = note: `-D clippy::misrefactored-assign-op` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::misrefactored_assign_op)]` -help: did you mean `a = a + 1` or `a = a + a + 1`? Consider replacing it with - | -LL - a += a + 1; -LL + a += 1; - | -help: or - | -LL - a += a + 1; -LL + a = a + a + 1; - | - -error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:11:5 - | -LL | a += 1 + a; - | ^^^^^^^^^^ - | -help: did you mean `a = a + 1` or `a = a + 1 + a`? Consider replacing it with - | -LL - a += 1 + a; -LL + a += 1; - | -help: or - | -LL - a += 1 + a; -LL + a = a + 1 + a; - | - -error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:14:5 - | -LL | a -= a - 1; - | ^^^^^^^^^^ - | -help: did you mean `a = a - 1` or `a = a - (a - 1)`? Consider replacing it with - | -LL - a -= a - 1; -LL + a -= 1; - | -help: or - | -LL - a -= a - 1; -LL + a = a - (a - 1); - | - -error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:17:5 - | -LL | a *= a * 99; - | ^^^^^^^^^^^ - | -help: did you mean `a = a * 99` or `a = a * a * 99`? Consider replacing it with - | -LL - a *= a * 99; -LL + a *= 99; - | -help: or - | -LL - a *= a * 99; -LL + a = a * a * 99; - | - -error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:20:5 - | -LL | a *= 42 * a; - | ^^^^^^^^^^^ - | -help: did you mean `a = a * 42` or `a = a * 42 * a`? Consider replacing it with - | -LL - a *= 42 * a; -LL + a *= 42; - | -help: or - | -LL - a *= 42 * a; -LL + a = a * 42 * a; - | - -error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:23:5 - | -LL | a /= a / 2; - | ^^^^^^^^^^ - | -help: did you mean `a = a / 2` or `a = a / (a / 2)`? Consider replacing it with - | -LL - a /= a / 2; -LL + a /= 2; - | -help: or - | -LL - a /= a / 2; -LL + a = a / (a / 2); - | - -error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:26:5 - | -LL | a %= a % 5; - | ^^^^^^^^^^ - | -help: did you mean `a = a % 5` or `a = a % (a % 5)`? Consider replacing it with - | -LL - a %= a % 5; -LL + a %= 5; - | -help: or - | -LL - a %= a % 5; -LL + a = a % (a % 5); - | - -error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:29:5 - | -LL | a &= a & 1; - | ^^^^^^^^^^ - | -help: did you mean `a = a & 1` or `a = a & a & 1`? Consider replacing it with - | -LL - a &= a & 1; -LL + a &= 1; - | -help: or - | -LL - a &= a & 1; -LL + a = a & a & 1; - | - -error: variable appears on both sides of an assignment operation - --> tests/ui/assign_ops2.rs:32:5 - | -LL | a *= a * a; - | ^^^^^^^^^^ - | -help: did you mean `a = a * a` or `a = a * a * a`? Consider replacing it with - | -LL - a *= a * a; -LL + a *= a; - | -help: or - | -LL - a *= a * a; -LL + a = a * a * a; - | - -error: manual implementation of an assign operation - --> tests/ui/assign_ops2.rs:71:5 - | -LL | buf = buf + cows.clone(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` - | - = note: `-D clippy::assign-op-pattern` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]` - -error: aborting due to 10 previous errors - diff --git a/src/tools/clippy/tests/ui/author.stdout b/src/tools/clippy/tests/ui/author.stdout index eed704e82fe1b..88a275302387c 100644 --- a/src/tools/clippy/tests/ui/author.stdout +++ b/src/tools/clippy/tests/ui/author.stdout @@ -1,8 +1,6 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Cast(expr, cast_ty) = init.kind - && let TyKind::Path(ref qpath) = cast_ty.kind - && match_qpath(qpath, &["char"]) && let ExprKind::Lit(ref lit) = expr.kind && let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout index 54325f9776c52..e453299edbcfc 100644 --- a/src/tools/clippy/tests/ui/author/blocks.stdout +++ b/src/tools/clippy/tests/ui/author/blocks.stdout @@ -14,8 +14,6 @@ if let ExprKind::Block(block, None) = expr.kind && name1.as_str() == "_t" && let StmtKind::Semi(e) = block.stmts[2].kind && let ExprKind::Unary(UnOp::Neg, inner) = e.kind - && let ExprKind::Path(ref qpath) = inner.kind - && match_qpath(qpath, &["x"]) && block.expr.is_none() { // report your lint here @@ -25,18 +23,14 @@ if let ExprKind::Block(block, None) = expr.kind && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["String", "new"]) + && is_path_diagnostic_item(cx, func, sym::string_new) && args.is_empty() && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind && name.as_str() == "expr" && let Some(trailing_expr) = block.expr && let ExprKind::Call(func1, args1) = trailing_expr.kind - && let ExprKind::Path(ref qpath1) = func1.kind - && match_qpath(qpath1, &["drop"]) + && is_path_diagnostic_item(cx, func1, sym::mem_drop) && args1.len() == 1 - && let ExprKind::Path(ref qpath2) = args1[0].kind - && match_qpath(qpath2, &["expr"]) { // report your lint here } diff --git a/src/tools/clippy/tests/ui/author/call.stdout b/src/tools/clippy/tests/ui/author/call.stdout index 59d4da490fe54..2b179d45112ee 100644 --- a/src/tools/clippy/tests/ui/author/call.stdout +++ b/src/tools/clippy/tests/ui/author/call.stdout @@ -1,8 +1,7 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]) + && is_path_diagnostic_item(cx, func, sym::cmp_min) && args.len() == 2 && let ExprKind::Lit(ref lit) = args[0].kind && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node diff --git a/src/tools/clippy/tests/ui/author/if.rs b/src/tools/clippy/tests/ui/author/if.rs index 59bc9f5bfa5c2..abefc34cf6b3e 100644 --- a/src/tools/clippy/tests/ui/author/if.rs +++ b/src/tools/clippy/tests/ui/author/if.rs @@ -1,6 +1,6 @@ //@ check-pass -#[allow(clippy::all)] +#![allow(clippy::all)] fn main() { #[clippy::author] diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout index 8ffdf8862027b..da359866bffc6 100644 --- a/src/tools/clippy/tests/ui/author/if.stdout +++ b/src/tools/clippy/tests/ui/author/if.stdout @@ -31,10 +31,8 @@ if let StmtKind::Let(local) = stmt.kind if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind && let ExprKind::Let(let_expr) = cond.kind && let PatKind::Expr(lit_expr) = let_expr.pat.kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node - && let ExprKind::Path(ref qpath) = let_expr.init.kind - && match_qpath(qpath, &["a"]) && let ExprKind::Block(block, None) = then.kind && block.stmts.is_empty() && block.expr.is_none() diff --git a/src/tools/clippy/tests/ui/author/issue_3849.stdout b/src/tools/clippy/tests/ui/author/issue_3849.stdout index a5a8c0304ee40..f02ea5bf075f7 100644 --- a/src/tools/clippy/tests/ui/author/issue_3849.stdout +++ b/src/tools/clippy/tests/ui/author/issue_3849.stdout @@ -1,11 +1,8 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["std", "mem", "transmute"]) + && is_path_diagnostic_item(cx, func, sym::transmute) && args.len() == 1 - && let ExprKind::Path(ref qpath1) = args[0].kind - && match_qpath(qpath1, &["ZPTR"]) && let PatKind::Wild = local.pat.kind { // report your lint here diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout index c94eb171f52b9..79794cec92698 100644 --- a/src/tools/clippy/tests/ui/author/loop.stdout +++ b/src/tools/clippy/tests/ui/author/loop.stdout @@ -14,8 +14,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && block.stmts.len() == 1 && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init - && let ExprKind::Path(ref qpath1) = init.kind - && match_qpath(qpath1, &["y"]) && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind && name1.as_str() == "z" && block.expr.is_none() @@ -64,8 +62,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo // report your lint here } if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr) - && let ExprKind::Path(ref qpath) = condition.kind - && match_qpath(qpath, &["a"]) && let ExprKind::Block(block, None) = body.kind && block.stmts.len() == 1 && let StmtKind::Semi(e) = block.stmts[0].kind @@ -77,10 +73,8 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While: } if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) && let PatKind::Expr(lit_expr) = let_pat.kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node - && let ExprKind::Path(ref qpath) = let_expr.kind - && match_qpath(qpath, &["a"]) && let ExprKind::Block(block, None) = if_then.kind && block.stmts.len() == 1 && let StmtKind::Semi(e) = block.stmts[0].kind diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.rs b/src/tools/clippy/tests/ui/author/macro_in_closure.rs index 8a02f38fad87b..373f0148d475a 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_closure.rs +++ b/src/tools/clippy/tests/ui/author/macro_in_closure.rs @@ -1,5 +1,7 @@ //@ check-pass +#![allow(clippy::uninlined_format_args)] + fn main() { #[clippy::author] let print_text = |x| println!("{}", x); diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout index 3186d0cbc276c..5f8a4ce236302 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout @@ -7,12 +7,10 @@ if let StmtKind::Let(local) = stmt.kind && block.stmts.len() == 1 && let StmtKind::Semi(e) = block.stmts[0].kind && let ExprKind::Call(func, args) = e.kind - && let ExprKind::Path(ref qpath) = func.kind - && match_qpath(qpath, &["$crate", "io", "_print"]) + && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Call(func1, args1) = args[0].kind - && let ExprKind::Path(ref qpath1) = func1.kind - && match_qpath(qpath1, &["format_arguments", "new_v1"]) + && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed && args1.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind && let ExprKind::Array(elements) = inner.kind @@ -27,12 +25,9 @@ if let StmtKind::Let(local) = stmt.kind && let ExprKind::Array(elements1) = inner1.kind && elements1.len() == 1 && let ExprKind::Call(func2, args2) = elements1[0].kind - && let ExprKind::Path(ref qpath2) = func2.kind - && match_qpath(qpath2, &["format_argument", "new_display"]) + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 1 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind - && let ExprKind::Path(ref qpath3) = inner2.kind - && match_qpath(qpath3, &["x"]) && block.expr.is_none() && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind && name.as_str() == "print_text" diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.rs b/src/tools/clippy/tests/ui/author/macro_in_loop.rs index 84ffe416e839b..f68275fefaaa3 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_loop.rs +++ b/src/tools/clippy/tests/ui/author/macro_in_loop.rs @@ -1,6 +1,7 @@ //@ check-pass #![feature(stmt_expr_attributes)] +#![allow(clippy::uninlined_format_args)] fn main() { #[clippy::author] diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout index 3f9be297c33c5..ecc252543117a 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout @@ -17,12 +17,10 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && block1.stmts.len() == 1 && let StmtKind::Semi(e1) = block1.stmts[0].kind && let ExprKind::Call(func, args) = e1.kind - && let ExprKind::Path(ref qpath1) = func.kind - && match_qpath(qpath1, &["$crate", "io", "_print"]) + && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed && args.len() == 1 && let ExprKind::Call(func1, args1) = args[0].kind - && let ExprKind::Path(ref qpath2) = func1.kind - && match_qpath(qpath2, &["format_arguments", "new_v1"]) + && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed && args1.len() == 2 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind && let ExprKind::Array(elements) = inner.kind @@ -37,12 +35,9 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && let ExprKind::Array(elements1) = inner1.kind && elements1.len() == 1 && let ExprKind::Call(func2, args2) = elements1[0].kind - && let ExprKind::Path(ref qpath3) = func2.kind - && match_qpath(qpath3, &["format_argument", "new_display"]) + && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 1 && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind - && let ExprKind::Path(ref qpath4) = inner2.kind - && match_qpath(qpath4, &["i"]) && block1.expr.is_none() && block.expr.is_none() { diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout index acb3b140dfa13..9752d7a9f99d2 100644 --- a/src/tools/clippy/tests/ui/author/matches.stdout +++ b/src/tools/clippy/tests/ui/author/matches.stdout @@ -5,13 +5,13 @@ if let StmtKind::Let(local) = stmt.kind && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node && arms.len() == 3 && let PatKind::Expr(lit_expr) = arms[0].pat.kind - && let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit1, negated } = lit_expr.kind && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node && arms[0].guard.is_none() && let ExprKind::Lit(ref lit2) = arms[0].body.kind && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node && let PatKind::Expr(lit_expr1) = arms[1].pat.kind - && let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind + && let PatExprKind::Lit { ref lit3, negated1 } = lit_expr1.kind && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node && arms[1].guard.is_none() && let ExprKind::Block(block, None) = arms[1].body.kind @@ -23,8 +23,6 @@ if let StmtKind::Let(local) = stmt.kind && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind && name.as_str() == "x" && let Some(trailing_expr) = block.expr - && let ExprKind::Path(ref qpath) = trailing_expr.kind - && match_qpath(qpath, &["x"]) && let PatKind::Wild = arms[2].pat.kind && arms[2].guard.is_none() && let ExprKind::Lit(ref lit5) = arms[2].body.kind diff --git a/src/tools/clippy/tests/ui/author/struct.stdout b/src/tools/clippy/tests/ui/author/struct.stdout index b66bbccb3cf1b..1e8fbafd30c5c 100644 --- a/src/tools/clippy/tests/ui/author/struct.stdout +++ b/src/tools/clippy/tests/ui/author/struct.stdout @@ -1,5 +1,4 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind - && match_qpath(qpath, &["Test"]) && fields.len() == 1 && fields[0].ident.as_str() == "field" && let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind @@ -20,11 +19,10 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind // report your lint here } if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind - && match_qpath(qpath, &["Test"]) && fields.len() == 1 && fields[0].ident.as_str() == "field" && let PatKind::Expr(lit_expr) = fields[0].pat.kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind @@ -34,10 +32,9 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind // report your lint here } if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind - && match_qpath(qpath, &["TestTuple"]) && fields.len() == 1 && let PatKind::Expr(lit_expr) = fields[0].kind - && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind + && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind @@ -48,8 +45,6 @@ if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind } if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind && method_name.ident.as_str() == "test" - && let ExprKind::Path(ref qpath) = receiver.kind - && match_qpath(qpath, &["test_method_call"]) && args.is_empty() { // report your lint here diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs b/src/tools/clippy/tests/ui/auxiliary/interior_mutable_const.rs similarity index 100% rename from src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs rename to src/tools/clippy/tests/ui/auxiliary/interior_mutable_const.rs diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs index e72d6b6ceadfd..4c61c5accd397 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs @@ -51,14 +51,14 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> { let arg = sig.inputs.first_mut()?; - if let FnArg::Typed(PatType { pat, .. }) = arg { - if let Pat::Ident(PatIdent { ident, .. }) = &**pat { - if ident == "self" { - return Some(arg); - } - } + if let FnArg::Typed(PatType { pat, .. }) = arg + && let Pat::Ident(PatIdent { ident, .. }) = &**pat + && ident == "self" + { + Some(arg) + } else { + None } - None } let mut elided = 0; @@ -66,30 +66,29 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea // Look for methods having arbitrary self type taken by &mut ref for inner in &mut item.items { - if let ImplItem::Fn(method) = inner { - if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) { - if let box Type::Reference(reference) = &mut pat_type.ty { - // Target only unnamed lifetimes - let name = match &reference.lifetime { - Some(lt) if lt.ident == "_" => make_name(elided), - None => make_name(elided), - _ => continue, - }; - elided += 1; - - // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. - // In order to avoid adding the dependency, get a default span from a nonexistent token. - // A default span is needed to mark the code as coming from expansion. - let span = Star::default().span(); - - // Replace old lifetime with the named one - let lifetime = Lifetime::new(&name, span); - reference.lifetime = Some(parse_quote!(#lifetime)); - - // Add lifetime to the generics of the method - method.sig.generics.params.push(parse_quote!(#lifetime)); - } - } + if let ImplItem::Fn(method) = inner + && let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) + && let box Type::Reference(reference) = &mut pat_type.ty + { + // Target only unnamed lifetimes + let name = match &reference.lifetime { + Some(lt) if lt.ident == "_" => make_name(elided), + None => make_name(elided), + _ => continue, + }; + elided += 1; + + // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. + // In order to avoid adding the dependency, get a default span from a nonexistent token. + // A default span is needed to mark the code as coming from expansion. + let span = Star::default().span(); + + // Replace old lifetime with the named one + let lifetime = Lifetime::new(&name, span); + reference.lifetime = Some(parse_quote!(#lifetime)); + + // Add lifetime to the generics of the method + method.sig.generics.params.push(parse_quote!(#lifetime)); } } @@ -129,15 +128,15 @@ pub fn fake_desugar_await(_args: TokenStream, input: TokenStream) -> TokenStream let mut async_fn = parse_macro_input!(input as syn::ItemFn); for stmt in &mut async_fn.block.stmts { - if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt { - if let syn::Expr::Await(syn::ExprAwait { base, await_token, .. }) = scrutinee.as_mut() { - let blc = quote_spanned!( await_token.span => { - #[allow(clippy::let_and_return)] - let __pinned = #base; - __pinned - }); - *scrutinee = parse_quote!(#blc); - } + if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt + && let syn::Expr::Await(syn::ExprAwait { base, await_token, .. }) = scrutinee.as_mut() + { + let blc = quote_spanned!( await_token.span => { + #[allow(clippy::let_and_return)] + let __pinned = #base; + __pinned + }); + *scrutinee = parse_quote!(#blc); } } diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs index 1a2a4ec231143..bb55539617fc5 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![feature(proc_macro_span)] #![allow(clippy::needless_if, dead_code)] @@ -131,12 +130,12 @@ fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Resul pub fn make_it_big(input: TokenStream) -> TokenStream { let mut expr_repeat = syn::parse_macro_input!(input as syn::ExprRepeat); let len_span = expr_repeat.len.span(); - if let syn::Expr::Lit(expr_lit) = &mut *expr_repeat.len { - if let syn::Lit::Int(lit_int) = &expr_lit.lit { - let orig_val = lit_int.base10_parse::().expect("not a valid length parameter"); - let new_val = orig_val.saturating_mul(10); - expr_lit.lit = syn::parse_quote_spanned!( len_span => #new_val); - } + if let syn::Expr::Lit(expr_lit) = &mut *expr_repeat.len + && let syn::Lit::Int(lit_int) = &expr_lit.lit + { + let orig_val = lit_int.base10_parse::().expect("not a valid length parameter"); + let new_val = orig_val.saturating_mul(10); + expr_lit.lit = syn::parse_quote_spanned!( len_span => #new_val); } quote::quote!(#expr_repeat).into() } diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed index df375e370573c..6ae5b0cb2f041 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed @@ -3,7 +3,7 @@ #![warn(clippy::blocks_in_conditions)] #![allow( unused, - clippy::let_and_return, + unnecessary_transmutes, clippy::needless_if, clippy::missing_transmute_annotations )] @@ -71,28 +71,6 @@ fn block_in_assert() { ); } -// issue #11814 -fn block_in_match_expr(num: i32) -> i32 { - let res = { - //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - let opt = Some(2); - opt - }; match res { - Some(0) => 1, - Some(n) => num * 2, - None => 0, - }; - - match unsafe { - let hearty_hearty_hearty = vec![240, 159, 146, 150]; - String::from_utf8_unchecked(hearty_hearty_hearty).as_str() - } { - "💖" => 1, - "what" => 2, - _ => 3, - } -} - // issue #12162 macro_rules! timed { ($name:expr, $body:expr $(,)?) => {{ @@ -118,6 +96,13 @@ mod issue_12016 { } } +fn issue_9911() { + if { return } {} + + let a = 1; + if { if a == 1 { return } else { true } } {} +} + fn in_closure() { let v = vec![1, 2, 3]; if v.into_iter() diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_conditions.rs index 1d9c9dd424603..3fd060620728f 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.rs +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.rs @@ -3,7 +3,7 @@ #![warn(clippy::blocks_in_conditions)] #![allow( unused, - clippy::let_and_return, + unnecessary_transmutes, clippy::needless_if, clippy::missing_transmute_annotations )] @@ -71,28 +71,6 @@ fn block_in_assert() { ); } -// issue #11814 -fn block_in_match_expr(num: i32) -> i32 { - match { - //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - let opt = Some(2); - opt - } { - Some(0) => 1, - Some(n) => num * 2, - None => 0, - }; - - match unsafe { - let hearty_hearty_hearty = vec![240, 159, 146, 150]; - String::from_utf8_unchecked(hearty_hearty_hearty).as_str() - } { - "💖" => 1, - "what" => 2, - _ => 3, - } -} - // issue #12162 macro_rules! timed { ($name:expr, $body:expr $(,)?) => {{ @@ -118,6 +96,13 @@ mod issue_12016 { } } +fn issue_9911() { + if { return } {} + + let a = 1; + if { if a == 1 { return } else { true } } {} +} + fn in_closure() { let v = vec![1, 2, 3]; if v.into_iter() diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr index da21344a84289..282c42a98bfc2 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.stderr +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr @@ -34,24 +34,5 @@ LL | if true && x == 3 { 6 } else { 10 } = note: `-D clippy::nonminimal-bool` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` -error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions.rs:76:5 - | -LL | / match { -LL | | -LL | | let opt = Some(2); -LL | | opt -LL | | } { - | |_____^ - | -help: try - | -LL ~ let res = { -LL + -LL + let opt = Some(2); -LL + opt -LL ~ }; match res { - | - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions_2021.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions_2021.fixed new file mode 100644 index 0000000000000..c7cc643dba679 --- /dev/null +++ b/src/tools/clippy/tests/ui/blocks_in_conditions_2021.fixed @@ -0,0 +1,25 @@ +//@edition: 2021 + +#![allow(clippy::let_and_return)] + +// issue #11814 +fn block_in_match_expr(num: i32) -> i32 { + let res = { + //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + let opt = Some(2); + opt + }; match res { + Some(0) => 1, + Some(n) => num * 2, + None => 0, + }; + + match unsafe { + let hearty_hearty_hearty = vec![240, 159, 146, 150]; + String::from_utf8_unchecked(hearty_hearty_hearty).as_str() + } { + "💖" => 1, + "what" => 2, + _ => 3, + } +} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions_2021.rs b/src/tools/clippy/tests/ui/blocks_in_conditions_2021.rs new file mode 100644 index 0000000000000..a911237f5f795 --- /dev/null +++ b/src/tools/clippy/tests/ui/blocks_in_conditions_2021.rs @@ -0,0 +1,25 @@ +//@edition: 2021 + +#![allow(clippy::let_and_return)] + +// issue #11814 +fn block_in_match_expr(num: i32) -> i32 { + match { + //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + let opt = Some(2); + opt + } { + Some(0) => 1, + Some(n) => num * 2, + None => 0, + }; + + match unsafe { + let hearty_hearty_hearty = vec![240, 159, 146, 150]; + String::from_utf8_unchecked(hearty_hearty_hearty).as_str() + } { + "💖" => 1, + "what" => 2, + _ => 3, + } +} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions_2021.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions_2021.stderr new file mode 100644 index 0000000000000..497ee9d679dde --- /dev/null +++ b/src/tools/clippy/tests/ui/blocks_in_conditions_2021.stderr @@ -0,0 +1,23 @@ +error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> tests/ui/blocks_in_conditions_2021.rs:7:5 + | +LL | / match { +LL | | +LL | | let opt = Some(2); +LL | | opt +LL | | } { + | |_____^ + | + = note: `-D clippy::blocks-in-conditions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::blocks_in_conditions)]` +help: try + | +LL ~ let res = { +LL + +LL + let opt = Some(2); +LL + opt +LL ~ }; match res { + | + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed b/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed index 0080801d46b78..7fa7c016f9351 100644 --- a/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed +++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.fixed @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::bool_to_int_with_if)] #![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] @@ -117,3 +116,27 @@ fn if_let(a: Enum, b: Enum) { 0 }; } + +fn issue14628() { + macro_rules! mac { + (if $cond:expr, $then:expr, $else:expr) => { + if $cond { $then } else { $else } + }; + (zero) => { + 0 + }; + (one) => { + 1 + }; + } + + let _ = i32::from(dbg!(4 > 0)); + //~^ bool_to_int_with_if + + let _ = dbg!(i32::from(4 > 0)); + //~^ bool_to_int_with_if + + let _ = mac!(if 4 > 0, 1, 0); + let _ = if 4 > 0 { mac!(one) } else { 0 }; + let _ = if 4 > 0 { 1 } else { mac!(zero) }; +} diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.rs b/src/tools/clippy/tests/ui/bool_to_int_with_if.rs index 72c7e2c71c560..2295d6f1362d2 100644 --- a/src/tools/clippy/tests/ui/bool_to_int_with_if.rs +++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.rs @@ -1,4 +1,3 @@ -#![feature(let_chains)] #![warn(clippy::bool_to_int_with_if)] #![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] @@ -157,3 +156,27 @@ fn if_let(a: Enum, b: Enum) { 0 }; } + +fn issue14628() { + macro_rules! mac { + (if $cond:expr, $then:expr, $else:expr) => { + if $cond { $then } else { $else } + }; + (zero) => { + 0 + }; + (one) => { + 1 + }; + } + + let _ = if dbg!(4 > 0) { 1 } else { 0 }; + //~^ bool_to_int_with_if + + let _ = dbg!(if 4 > 0 { 1 } else { 0 }); + //~^ bool_to_int_with_if + + let _ = mac!(if 4 > 0, 1, 0); + let _ = if 4 > 0 { mac!(one) } else { 0 }; + let _ = if 4 > 0 { 1 } else { mac!(zero) }; +} diff --git a/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr b/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr index 415e80f8d73d1..e4ae57304141a 100644 --- a/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr +++ b/src/tools/clippy/tests/ui/bool_to_int_with_if.stderr @@ -1,5 +1,5 @@ error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:14:5 + --> tests/ui/bool_to_int_with_if.rs:13:5 | LL | / if a { LL | | @@ -14,7 +14,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::bool_to_int_with_if)]` error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:20:5 + --> tests/ui/bool_to_int_with_if.rs:19:5 | LL | / if a { LL | | @@ -27,7 +27,7 @@ LL | | }; = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:26:5 + --> tests/ui/bool_to_int_with_if.rs:25:5 | LL | / if !a { LL | | @@ -40,7 +40,7 @@ LL | | }; = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:32:5 + --> tests/ui/bool_to_int_with_if.rs:31:5 | LL | / if a || b { LL | | @@ -53,7 +53,7 @@ LL | | }; = note: `(a || b) as i32` or `(a || b).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:38:5 + --> tests/ui/bool_to_int_with_if.rs:37:5 | LL | / if cond(a, b) { LL | | @@ -66,7 +66,7 @@ LL | | }; = note: `cond(a, b) as i32` or `cond(a, b).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:44:5 + --> tests/ui/bool_to_int_with_if.rs:43:5 | LL | / if x + y < 4 { LL | | @@ -79,7 +79,7 @@ LL | | }; = note: `(x + y < 4) as i32` or `(x + y < 4).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:54:12 + --> tests/ui/bool_to_int_with_if.rs:53:12 | LL | } else if b { | ____________^ @@ -93,7 +93,7 @@ LL | | }; = note: `b as i32` or `b.into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:64:12 + --> tests/ui/bool_to_int_with_if.rs:63:12 | LL | } else if b { | ____________^ @@ -107,12 +107,28 @@ LL | | }; = note: `!b as i32` or `(!b).into()` can also be valid options error: boolean to int conversion using if - --> tests/ui/bool_to_int_with_if.rs:130:5 + --> tests/ui/bool_to_int_with_if.rs:129:5 | LL | if a { 1 } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)` | = note: `a as u8` or `a.into()` can also be valid options -error: aborting due to 9 previous errors +error: boolean to int conversion using if + --> tests/ui/bool_to_int_with_if.rs:173:13 + | +LL | let _ = if dbg!(4 > 0) { 1 } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `i32::from(dbg!(4 > 0))` + | + = note: `dbg!(4 > 0) as i32` or `dbg!(4 > 0).into()` can also be valid options + +error: boolean to int conversion using if + --> tests/ui/bool_to_int_with_if.rs:176:18 + | +LL | let _ = dbg!(if 4 > 0 { 1 } else { 0 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `i32::from(4 > 0)` + | + = note: `(4 > 0) as i32` or `(4 > 0).into()` can also be valid options + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed index 3dca06fce4b8d..3ba2eea59f0b0 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed @@ -29,3 +29,21 @@ fn issue_13882() { let _raw = (&raw mut x[1]).wrapping_offset(-1); //~^ borrow_as_ptr } + +fn implicit_cast() { + let val = 1; + let p: *const i32 = &raw const val; + //~^ borrow_as_ptr + + let mut val = 1; + let p: *mut i32 = &raw mut val; + //~^ borrow_as_ptr + + let mut val = 1; + // Only lint the leftmost argument, the rightmost is ref to a temporary + core::ptr::eq(&raw const val, &1); + //~^ borrow_as_ptr + + // Do not lint references to temporaries + core::ptr::eq(&0i32, &1i32); +} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs index 3559dc23d0185..8cdd0512da5f7 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.rs +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs @@ -29,3 +29,21 @@ fn issue_13882() { let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); //~^ borrow_as_ptr } + +fn implicit_cast() { + let val = 1; + let p: *const i32 = &val; + //~^ borrow_as_ptr + + let mut val = 1; + let p: *mut i32 = &mut val; + //~^ borrow_as_ptr + + let mut val = 1; + // Only lint the leftmost argument, the rightmost is ref to a temporary + core::ptr::eq(&val, &1); + //~^ borrow_as_ptr + + // Do not lint references to temporaries + core::ptr::eq(&0i32, &1i32); +} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr index 4a9f2ed4aa003..b1fcce49403c8 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr @@ -25,5 +25,38 @@ error: borrow as raw pointer LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `&raw mut x[1]` -error: aborting due to 4 previous errors +error: implicit borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:35:25 + | +LL | let p: *const i32 = &val; + | ^^^^ + | +help: use a raw pointer instead + | +LL | let p: *const i32 = &raw const val; + | +++++++++ + +error: implicit borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:39:23 + | +LL | let p: *mut i32 = &mut val; + | ^^^^^^^^ + | +help: use a raw pointer instead + | +LL | let p: *mut i32 = &raw mut val; + | +++ + +error: implicit borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:44:19 + | +LL | core::ptr::eq(&val, &1); + | ^^^^ + | +help: use a raw pointer instead + | +LL | core::ptr::eq(&raw const val, &1); + | +++++++++ + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed index 17c224f10bfea..765dd75fceb92 100644 --- a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed +++ b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed @@ -81,3 +81,46 @@ fn issue_13584() { let p = &raw const *s; let _ = p as *const i8; } + +mod issue_9905 { + use std::{fs, io}; + + pub enum File { + Stdio, + File(fs::File), + } + + impl io::Read for &'_ File { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + File::Stdio => io::stdin().read(buf), + File::File(file) => (&*file).read(buf), + } + } + } +} + +mod issue_11346 { + struct Struct; + + impl Struct { + fn foo(self: &mut &Self) {} + } + + trait Trait { + fn bar(&mut self) {} + } + + impl Trait for &Struct {} + + fn bar() { + let s = &Struct; + (&*s).foo(); + (&*s).bar(); + + let mut s = &Struct; + s.foo(); // To avoid a warning about `s` not needing to be mutable + s.foo(); + //~^ borrow_deref_ref + } +} diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.rs b/src/tools/clippy/tests/ui/borrow_deref_ref.rs index 130ed2903dc61..8ee66bfa881ab 100644 --- a/src/tools/clippy/tests/ui/borrow_deref_ref.rs +++ b/src/tools/clippy/tests/ui/borrow_deref_ref.rs @@ -81,3 +81,46 @@ fn issue_13584() { let p = &raw const *s; let _ = p as *const i8; } + +mod issue_9905 { + use std::{fs, io}; + + pub enum File { + Stdio, + File(fs::File), + } + + impl io::Read for &'_ File { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + File::Stdio => io::stdin().read(buf), + File::File(file) => (&*file).read(buf), + } + } + } +} + +mod issue_11346 { + struct Struct; + + impl Struct { + fn foo(self: &mut &Self) {} + } + + trait Trait { + fn bar(&mut self) {} + } + + impl Trait for &Struct {} + + fn bar() { + let s = &Struct; + (&*s).foo(); + (&*s).bar(); + + let mut s = &Struct; + s.foo(); // To avoid a warning about `s` not needing to be mutable + (&*s).foo(); + //~^ borrow_deref_ref + } +} diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.stderr b/src/tools/clippy/tests/ui/borrow_deref_ref.stderr index f5868aa874900..3d55da25b9b20 100644 --- a/src/tools/clippy/tests/ui/borrow_deref_ref.stderr +++ b/src/tools/clippy/tests/ui/borrow_deref_ref.stderr @@ -19,5 +19,11 @@ error: deref on an immutable reference LL | let addr_y = &&*x as *const _ as usize; // assert ok | ^^^ help: if you would like to reborrow, try removing `&*`: `x` -error: aborting due to 3 previous errors +error: deref on an immutable reference + --> tests/ui/borrow_deref_ref.rs:123:9 + | +LL | (&*s).foo(); + | ^^^^^ help: if you would like to reborrow, try removing `&*`: `s` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs new file mode 100644 index 0000000000000..0f439f7891507 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs @@ -0,0 +1,221 @@ +//@aux-build:interior_mutable_const.rs + +#![deny(clippy::borrow_interior_mutable_const)] +#![allow( + clippy::declare_interior_mutable_const, + clippy::out_of_bounds_indexing, + const_item_mutation, + unconditional_panic +)] + +use core::cell::{Cell, UnsafeCell}; +use core::ops::{Deref, Index}; + +trait ConstDefault { + const DEFAULT: Self; +} +impl ConstDefault for u32 { + const DEFAULT: Self = 0; +} +impl ConstDefault for Cell { + const DEFAULT: Self = Cell::new(T::DEFAULT); +} + +fn main() { + { + const C: String = String::new(); + let _ = C; + let _ = &C; + let _ = C.len(); + let _ = &*C; + } + { + const C: UnsafeCell = UnsafeCell::new(0); + let _ = C; + let _ = &C; //~ borrow_interior_mutable_const + let _ = C.into_inner(); + let _ = C.get(); //~ borrow_interior_mutable_const + } + { + const C: Cell = Cell::new(0); + let _ = C; + let _ = &C; //~ borrow_interior_mutable_const + let _ = &mut C; //~ borrow_interior_mutable_const + let _ = C.into_inner(); + + let local = C; + C.swap(&local) //~ borrow_interior_mutable_const + } + { + const C: [(Cell,); 1] = [(Cell::new(0),)]; + let _ = C; + let _ = &C; //~ borrow_interior_mutable_const + let _ = &C[0]; //~ borrow_interior_mutable_const + let _ = &C[0].0; //~ borrow_interior_mutable_const + C[0].0.set(1); //~ borrow_interior_mutable_const + } + { + struct S(Cell); + impl S { + const C: Self = Self(Cell::new(0)); + } + impl Deref for S { + type Target = Cell; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + let _ = S::C; + let _ = S::C.0; + let _ = &S::C; //~ borrow_interior_mutable_const + let _ = &S::C.0; //~ borrow_interior_mutable_const + S::C.set(1); //~ borrow_interior_mutable_const + let _ = &*S::C; //~ borrow_interior_mutable_const + (*S::C).set(1); //~ borrow_interior_mutable_const + } + { + enum E { + Cell(Cell), + Other, + } + const CELL: E = E::Cell(Cell::new(0)); + const OTHER: E = E::Other; + + let _ = CELL; + let _ = &CELL; //~ borrow_interior_mutable_const + let E::Cell(_) = CELL else { + return; + }; + + let _ = OTHER; + let _ = &OTHER; + let E::Cell(ref _x) = OTHER else { + return; + }; + } + { + struct S { + cell: (Cell, u32), + other: Option, + } + impl S { + const C: Self = Self { + cell: (Cell::::DEFAULT, 0), + other: Some(T::DEFAULT), + }; + + fn f() { + let _ = Self::C; + let _ = &Self::C; //~ borrow_interior_mutable_const + let _ = Self::C.other; + let _ = &Self::C.other; + let _ = &Self::C.cell; //~ borrow_interior_mutable_const + let _ = &Self::C.cell.0; //~ borrow_interior_mutable_const + Self::C.cell.0.set(T::DEFAULT); //~ borrow_interior_mutable_const + let _ = &Self::C.cell.1; + } + } + } + { + trait T { + const VALUE: Option> = Some(Cell::new(0)); + } + impl T for u32 {} + impl T for i32 { + const VALUE: Option> = None; + } + + let _ = &u32::VALUE; //~ borrow_interior_mutable_const + let _ = &i32::VALUE; + } + { + trait Trait { + type T: ConstDefault; + const VALUE: Option> = Some(Self::T::::DEFAULT); + } + impl Trait for u32 { + type T = Cell; + } + impl Trait for i32 { + type T = Cell; + const VALUE: Option> = None; + } + + fn f() { + let _ = &>::VALUE; //~ borrow_interior_mutable_const + let _ = &>::VALUE; + } + } + { + trait Trait { + const UNFROZEN: Option> = Some(Cell::new(0)); + const FROZEN: Option> = None; + const NON_FREEZE: u32 = 0; + } + fn f() { + // None of these are guaranteed to be frozen, so don't lint. + let _ = &T::UNFROZEN; + let _ = &T::FROZEN; + let _ = &T::NON_FREEZE; + } + } + { + struct S([Option>; 2]); + impl Index for S { + type Output = Option>; + fn index(&self, idx: usize) -> &Self::Output { + &self.0[idx] + } + } + + const C: S = S([Some(Cell::new(0)), None]); + let _ = &C; //~ borrow_interior_mutable_const + let _ = &C[0]; //~ borrow_interior_mutable_const + let _ = &C.0[0]; //~ borrow_interior_mutable_const + let _ = &C.0[1]; + } + { + const C: [Option>; 2] = [None, None]; + let _ = &C[0]; + let _ = &C[1]; + let _ = &C[2]; + + fn f(i: usize) { + let _ = &C[i]; + } + } + { + const C: [Option>; 2] = [None, Some(Cell::new(0))]; + let _ = &C[0]; + let _ = &C[1]; //~ borrow_interior_mutable_const + let _ = &C[2]; + + fn f(i: usize) { + let _ = &C[i]; //~ borrow_interior_mutable_const + } + } + { + let _ = &interior_mutable_const::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ borrow_interior_mutable_const + let _ = &interior_mutable_const::WRAPPED_PRIVATE_FROZEN_VARIANT; + } + { + type Cell2 = Cell; + type MyCell = Cell2; + struct S(Option); + trait T { + type Assoc; + } + struct S2(T, T, u32); + impl T for S { + type Assoc = S2; + } + type Assoc = ::Assoc; + impl S { + const VALUE: Assoc = S2(Self(None), Self(Some(Cell::new(0))), 0); + } + let _ = &S::VALUE; //~ borrow_interior_mutable_const + let _ = &S::VALUE.0; + let _ = &S::VALUE.1; //~ borrow_interior_mutable_const + let _ = &S::VALUE.2; + } +} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr new file mode 100644 index 0000000000000..e7c3f879b05b0 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr @@ -0,0 +1,247 @@ +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:35:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing +note: the lint level is defined here + --> tests/ui/borrow_interior_mutable_const.rs:3:9 + | +LL | #![deny(clippy::borrow_interior_mutable_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:37:17 + | +LL | let _ = C.get(); + | ^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:42:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:43:17 + | +LL | let _ = &mut C; + | ^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:47:9 + | +LL | C.swap(&local) + | ^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:52:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:53:17 + | +LL | let _ = &C[0]; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:54:17 + | +LL | let _ = &C[0].0; + | ^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:55:9 + | +LL | C[0].0.set(1); + | ^^^^^^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:70:17 + | +LL | let _ = &S::C; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:71:17 + | +LL | let _ = &S::C.0; + | ^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:72:9 + | +LL | S::C.set(1); + | ^^^^ + | + = note: there is a compiler inserted call to `Deref::deref` here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:73:18 + | +LL | let _ = &*S::C; + | ^^^^^ + | + = note: this deref expression is a call to `Deref::deref` + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:74:9 + | +LL | (*S::C).set(1); + | ^^^^^^^ + | + = note: this deref expression is a call to `Deref::deref` + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:85:17 + | +LL | let _ = &CELL; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:109:25 + | +LL | let _ = &Self::C; + | ^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:112:25 + | +LL | let _ = &Self::C.cell; + | ^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:113:25 + | +LL | let _ = &Self::C.cell.0; + | ^^^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:114:17 + | +LL | Self::C.cell.0.set(T::DEFAULT); + | ^^^^^^^^^^^^^^ + | + = note: there is a compiler inserted borrow here + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:128:17 + | +LL | let _ = &u32::VALUE; + | ^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:145:21 + | +LL | let _ = &>::VALUE; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:172:17 + | +LL | let _ = &C; + | ^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:173:18 + | +LL | let _ = &C[0]; + | ^^^^ + | + = note: this index expression is a call to `Index::index` + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:174:17 + | +LL | let _ = &C.0[0]; + | ^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:190:17 + | +LL | let _ = &C[1]; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:194:21 + | +LL | let _ = &C[i]; + | ^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:198:17 + | +LL | let _ = &interior_mutable_const::WRAPPED_PRIVATE_UNFROZEN_VARIANT; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:216:17 + | +LL | let _ = &S::VALUE; + | ^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: borrow of a named constant with interior mutability + --> tests/ui/borrow_interior_mutable_const.rs:218:17 + | +LL | let _ = &S::VALUE.1; + | ^^^^^^^^^^^ + | + = help: this lint can be silenced by assigning the value to a local variable before borrowing + +error: aborting due to 29 previous errors + diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs deleted file mode 100644 index da940a4cfb50b..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.rs +++ /dev/null @@ -1,101 +0,0 @@ -//@aux-build:helper.rs - -#![deny(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const)] - -// this file (mostly) replicates its `declare` counterpart. Please see it for more discussions. - -extern crate helper; - -use std::cell::Cell; -use std::sync::atomic::AtomicUsize; - -enum OptionalCell { - Unfrozen(Cell), - Frozen, -} - -const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); -const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - -fn borrow_optional_cell() { - let _ = &UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &FROZEN_VARIANT; -} - -trait AssocConsts { - const TO_BE_UNFROZEN_VARIANT: OptionalCell; - const TO_BE_FROZEN_VARIANT: OptionalCell; - - const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - - fn function() { - // This is the "suboptimal behavior" mentioned in `is_value_unfrozen` - // caused by a similar reason to unfrozen types without any default values - // get linted even if it has frozen variants'. - let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR: interior mutability - - // The lint ignores default values because an impl of this trait can set - // an unfrozen variant to `DEFAULTED_ON_FROZEN_VARIANT` and use the default impl for `function`. - let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR: interior mutability - } -} - -impl AssocConsts for u64 { - const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - - fn function() { - let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &::TO_BE_FROZEN_VARIANT; - let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; - } -} - -trait AssocTypes { - type ToBeUnfrozen; - - const TO_BE_UNFROZEN_VARIANT: Option; - const TO_BE_FROZEN_VARIANT: Option; - - // there's no need to test here because it's the exactly same as `trait::AssocTypes` - fn function(); -} - -impl AssocTypes for u64 { - type ToBeUnfrozen = AtomicUsize; - - const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); - const TO_BE_FROZEN_VARIANT: Option = None; - - fn function() { - let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &::TO_BE_FROZEN_VARIANT; - } -} - -enum BothOfCellAndGeneric { - Unfrozen(Cell<*const T>), - Generic(*const T), - Frozen(usize), -} - -impl BothOfCellAndGeneric { - const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); - - fn function() { - let _ = &Self::UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &Self::GENERIC_VARIANT; //~ ERROR: interior mutability - let _ = &Self::FROZEN_VARIANT; - } -} - -fn main() { - // constants defined in foreign crates - let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR: interior mutability - let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT; -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr deleted file mode 100644 index 43850384b9036..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/enums.stderr +++ /dev/null @@ -1,79 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:22:14 - | -LL | let _ = &UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/enums.rs:3:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:37:18 - | -LL | let _ = &Self::TO_BE_FROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/enums.rs:41:18 - | -LL | let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/enums.rs:50:18 - | -LL | let _ = &::TO_BE_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/enums.rs:52:18 - | -LL | let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/enums.rs:74:18 - | -LL | let _ = &::TO_BE_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/enums.rs:91:18 - | -LL | let _ = &Self::UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/enums.rs:92:18 - | -LL | let _ = &Self::GENERIC_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/enums.rs:99:14 - | -LL | let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 9 previous errors - diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs deleted file mode 100644 index fa729b62d7f51..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs +++ /dev/null @@ -1,128 +0,0 @@ -#![deny(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const, clippy::needless_borrow)] -#![allow(const_item_mutation)] - -use std::borrow::Cow; -use std::cell::{Cell, UnsafeCell}; -use std::fmt::Display; -use std::sync::Once; -use std::sync::atomic::{AtomicUsize, Ordering}; - -const ATOMIC: AtomicUsize = AtomicUsize::new(5); -const CELL: Cell = Cell::new(6); -const ATOMIC_TUPLE: ([AtomicUsize; 1], Option>, u8) = ([ATOMIC], None, 7); -const INTEGER: u8 = 8; -const STRING: String = String::new(); -const STR: &str = "012345"; -const COW: Cow = Cow::Borrowed("abcdef"); -const NO_ANN: &dyn Display = &70; -static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); -const ONCE_INIT: Once = Once::new(); - -// This is just a pointer that can be safely dereferenced, -// 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) -> &T { - unsafe { &*self.ptr } - } -} - -// ICE regression test -mod issue12979 { - use std::cell::UnsafeCell; - - const ATOMIC_TUPLE: (Vec>, ()) = (Vec::new(), ()); - - fn main() { - let _x = &ATOMIC_TUPLE.0; - } -} - -// 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); - //~^ borrow_interior_mutable_const - assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); - //~^ borrow_interior_mutable_const - - let _once = ONCE_INIT; - let _once_ref = &ONCE_INIT; - //~^ borrow_interior_mutable_const - let _once_ref_2 = &&ONCE_INIT; - //~^ borrow_interior_mutable_const - let _once_ref_4 = &&&&ONCE_INIT; - //~^ borrow_interior_mutable_const - let _once_mut = &mut ONCE_INIT; - //~^ borrow_interior_mutable_const - let _atomic_into_inner = ATOMIC.into_inner(); - // these should be all fine. - let _twice = (ONCE_INIT, ONCE_INIT); - let _ref_twice = &(ONCE_INIT, ONCE_INIT); - let _ref_once = &(ONCE_INIT, ONCE_INIT).0; - let _array_twice = [ONCE_INIT, ONCE_INIT]; - let _ref_array_twice = &[ONCE_INIT, ONCE_INIT]; - let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0]; - - // referencing projection is still bad. - let _ = &ATOMIC_TUPLE; - //~^ borrow_interior_mutable_const - let _ = &ATOMIC_TUPLE.0; - //~^ borrow_interior_mutable_const - let _ = &(&&&&ATOMIC_TUPLE).0; - //~^ borrow_interior_mutable_const - let _ = &ATOMIC_TUPLE.0[0]; - //~^ borrow_interior_mutable_const - let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); - //~^ borrow_interior_mutable_const - let _ = &ATOMIC_TUPLE.2; - let _ = (&&&&ATOMIC_TUPLE).0; - let _ = (&&&&ATOMIC_TUPLE).2; - let _ = ATOMIC_TUPLE.0; - let _ = ATOMIC_TUPLE.0[0]; - //~^ borrow_interior_mutable_const - let _ = ATOMIC_TUPLE.1.into_iter(); - let _ = ATOMIC_TUPLE.2; - let _ = &{ ATOMIC_TUPLE }; - - CELL.set(2); - //~^ borrow_interior_mutable_const - assert_eq!(CELL.get(), 6); - //~^ borrow_interior_mutable_const - - assert_eq!(INTEGER, 8); - assert!(STRING.is_empty()); - - let a = ATOMIC; - a.store(4, Ordering::SeqCst); - assert_eq!(a.load(Ordering::SeqCst), 4); - - STATIC_TUPLE.0.store(3, Ordering::SeqCst); - assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3); - assert!(STATIC_TUPLE.1.is_empty()); - - 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/others.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr deleted file mode 100644 index decea153f7173..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr +++ /dev/null @@ -1,119 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:65:5 - | -LL | ATOMIC.store(1, Ordering::SeqCst); - | ^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/others.rs:1:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:67:16 - | -LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); - | ^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/others.rs:71:22 - | -LL | let _once_ref = &ONCE_INIT; - | ^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/others.rs:73:25 - | -LL | let _once_ref_2 = &&ONCE_INIT; - | ^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/others.rs:75:27 - | -LL | let _once_ref_4 = &&&&ONCE_INIT; - | ^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/others.rs:77:26 - | -LL | let _once_mut = &mut ONCE_INIT; - | ^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/others.rs:89:14 - | -LL | let _ = &ATOMIC_TUPLE; - | ^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/others.rs:91:14 - | -LL | let _ = &ATOMIC_TUPLE.0; - | ^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/others.rs:93:19 - | -LL | let _ = &(&&&&ATOMIC_TUPLE).0; - | ^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/others.rs:95:14 - | -LL | let _ = &ATOMIC_TUPLE.0[0]; - | ^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/others.rs:97:13 - | -LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); - | ^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/others.rs:103:13 - | -LL | let _ = ATOMIC_TUPLE.0[0]; - | ^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/others.rs:109:5 - | -LL | CELL.set(2); - | ^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/others.rs:111:16 - | -LL | assert_eq!(CELL.get(), 6); - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 14 previous errors - diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.rs deleted file mode 100644 index bbe5538fbe123..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![deny(clippy::borrow_interior_mutable_const)] -#![deny(clippy::declare_interior_mutable_const)] - -// Inspired by https://github.com/rust-lang/rust/pull/130543#issuecomment-2364828139 - -use std::cell::UnsafeCell; - -trait Trait { - type Assoc; -} - -type Assoc = ::Assoc; - -impl Trait for u8 { - type Assoc = UnsafeCell; -} - -impl Trait for () { - type Assoc = (); -} - -enum MaybeMutable { - Mutable(Assoc), - Immutable(Assoc<()>), -} - -const CELL: Assoc = UnsafeCell::new(0); //~ ERROR: interior mutable -const UNIT: Assoc<()> = (); -const MUTABLE: MaybeMutable = MaybeMutable::Mutable(CELL); //~ ERROR: interior mutable -const IMMUTABLE: MaybeMutable = MaybeMutable::Immutable(UNIT); - -fn print_ref(t: &T) { - let p: *const T = t; - println!("{p:p}") -} - -fn main() { - print_ref(&CELL); //~ ERROR: interior mutability - print_ref(&UNIT); - print_ref(&MUTABLE); //~ ERROR: interior mutability - print_ref(&IMMUTABLE); -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.stderr deleted file mode 100644 index eabaf66560ada..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/projections.stderr +++ /dev/null @@ -1,44 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/borrow_interior_mutable_const/projections.rs:27:1 - | -LL | const CELL: Assoc = UnsafeCell::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/projections.rs:2:9 - | -LL | #![deny(clippy::declare_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/borrow_interior_mutable_const/projections.rs:29:1 - | -LL | const MUTABLE: MaybeMutable = MaybeMutable::Mutable(CELL); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/projections.rs:38:16 - | -LL | print_ref(&CELL); - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/projections.rs:1:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/projections.rs:40:16 - | -LL | print_ref(&MUTABLE); - | ^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 4 previous errors - diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs deleted file mode 100644 index c4878dbe57b29..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.rs +++ /dev/null @@ -1,219 +0,0 @@ -#![deny(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const)] - -// this file replicates its `declare` counterpart. Please see it for more discussions. - -use std::borrow::Cow; -use std::cell::Cell; -use std::sync::atomic::{AtomicUsize, Ordering}; - -trait ConcreteTypes { - const ATOMIC: AtomicUsize; - const STRING: String; - - fn function() { - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const - let _ = &Self::STRING; - } -} - -impl ConcreteTypes for u64 { - const ATOMIC: AtomicUsize = AtomicUsize::new(9); - const STRING: String = String::new(); - - fn function() { - // Lint this again since implementers can choose not to borrow it. - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const - let _ = &Self::STRING; - } -} - -// a helper trait used below -trait ConstDefault { - const DEFAULT: Self; -} - -trait GenericTypes { - const TO_REMAIN_GENERIC: T; - const TO_BE_CONCRETE: U; - - fn function() { - let _ = &Self::TO_REMAIN_GENERIC; - } -} - -impl GenericTypes for Vec { - const TO_REMAIN_GENERIC: T = T::DEFAULT; - const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); - - fn function() { - let _ = &Self::TO_REMAIN_GENERIC; - let _ = &Self::TO_BE_CONCRETE; - //~^ borrow_interior_mutable_const - } -} - -// a helper type used below -pub struct Wrapper(T); - -trait AssocTypes { - type ToBeFrozen; - type ToBeUnfrozen; - type ToBeGenericParam; - - const TO_BE_FROZEN: Self::ToBeFrozen; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen; - const WRAPPED_TO_BE_UNFROZEN: Wrapper; - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper; - - fn function() { - let _ = &Self::TO_BE_FROZEN; - let _ = &Self::WRAPPED_TO_BE_UNFROZEN; - } -} - -impl AssocTypes for Vec { - type ToBeFrozen = u16; - type ToBeUnfrozen = AtomicUsize; - type ToBeGenericParam = T; - - const TO_BE_FROZEN: Self::ToBeFrozen = 12; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); - const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper = Wrapper(T::DEFAULT); - - fn function() { - let _ = &Self::TO_BE_FROZEN; - let _ = &Self::TO_BE_UNFROZEN; - //~^ borrow_interior_mutable_const - let _ = &Self::WRAPPED_TO_BE_UNFROZEN; - //~^ borrow_interior_mutable_const - let _ = &Self::WRAPPED_TO_BE_GENERIC_PARAM; - } -} - -// a helper trait used below -trait AssocTypesHelper { - type NotToBeBounded; - type ToBeBounded; - - const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; -} - -trait AssocTypesFromGenericParam -where - T: AssocTypesHelper, -{ - const NOT_BOUNDED: T::NotToBeBounded; - const BOUNDED: T::ToBeBounded; - - fn function() { - let _ = &Self::NOT_BOUNDED; - let _ = &Self::BOUNDED; - //~^ borrow_interior_mutable_const - } -} - -impl AssocTypesFromGenericParam for Vec -where - T: AssocTypesHelper, -{ - const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); - - fn function() { - let _ = &Self::NOT_BOUNDED; - let _ = &Self::BOUNDED; - //~^ borrow_interior_mutable_const - } -} - -trait SelfType: Sized { - const SELF: Self; - const WRAPPED_SELF: Option; - - fn function() { - let _ = &Self::SELF; - let _ = &Self::WRAPPED_SELF; - } -} - -impl SelfType for u64 { - const SELF: Self = 16; - const WRAPPED_SELF: Option = Some(20); - - fn function() { - let _ = &Self::SELF; - let _ = &Self::WRAPPED_SELF; - } -} - -impl SelfType for AtomicUsize { - const SELF: Self = AtomicUsize::new(17); - const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); - - fn function() { - let _ = &Self::SELF; - //~^ borrow_interior_mutable_const - let _ = &Self::WRAPPED_SELF; - //~^ borrow_interior_mutable_const - } -} - -trait BothOfCellAndGeneric { - const DIRECT: Cell; - const INDIRECT: Cell<*const T>; - - fn function() { - let _ = &Self::DIRECT; - //~^ borrow_interior_mutable_const - let _ = &Self::INDIRECT; - //~^ borrow_interior_mutable_const - } -} - -impl BothOfCellAndGeneric for Vec { - const DIRECT: Cell = Cell::new(T::DEFAULT); - const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); - - fn function() { - let _ = &Self::DIRECT; - //~^ borrow_interior_mutable_const - let _ = &Self::INDIRECT; - //~^ borrow_interior_mutable_const - } -} - -struct Local(T); - -impl Local -where - T: ConstDefault + AssocTypesHelper, -{ - const ATOMIC: AtomicUsize = AtomicUsize::new(18); - const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - - const GENERIC_TYPE: T = T::DEFAULT; - - const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - - fn function() { - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const - let _ = &Self::COW; - let _ = &Self::GENERIC_TYPE; - let _ = &Self::ASSOC_TYPE; - let _ = &Self::BOUNDED_ASSOC_TYPE; - //~^ borrow_interior_mutable_const - } -} - -fn main() { - u64::ATOMIC.store(5, Ordering::SeqCst); - //~^ borrow_interior_mutable_const - assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); - //~^ borrow_interior_mutable_const -} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr deleted file mode 100644 index cad68ca9260cb..0000000000000 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/traits.stderr +++ /dev/null @@ -1,143 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:15:18 - | -LL | let _ = &Self::ATOMIC; - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here -note: the lint level is defined here - --> tests/ui/borrow_interior_mutable_const/traits.rs:1:9 - | -LL | #![deny(clippy::borrow_interior_mutable_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:27:18 - | -LL | let _ = &Self::ATOMIC; - | ^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:53:18 - | -LL | let _ = &Self::TO_BE_CONCRETE; - | ^^^^^^^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:89:18 - | -LL | let _ = &Self::TO_BE_UNFROZEN; - | ^^^^^^^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:91:18 - | -LL | let _ = &Self::WRAPPED_TO_BE_UNFROZEN; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:114:18 - | -LL | let _ = &Self::BOUNDED; - | ^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:128:18 - | -LL | let _ = &Self::BOUNDED; - | ^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:158:18 - | -LL | let _ = &Self::SELF; - | ^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:160:18 - | -LL | let _ = &Self::WRAPPED_SELF; - | ^^^^^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:170:18 - | -LL | let _ = &Self::DIRECT; - | ^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:172:18 - | -LL | let _ = &Self::INDIRECT; - | ^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:182:18 - | -LL | let _ = &Self::DIRECT; - | ^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:184:18 - | -LL | let _ = &Self::INDIRECT; - | ^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:204:18 - | -LL | let _ = &Self::ATOMIC; - | ^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:209:18 - | -LL | let _ = &Self::BOUNDED_ASSOC_TYPE; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:215:5 - | -LL | u64::ATOMIC.store(5, Ordering::SeqCst); - | ^^^^^^^^^^^ - | - = 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 - --> tests/ui/borrow_interior_mutable_const/traits.rs:217:16 - | -LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); - | ^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 17 previous errors - diff --git a/src/tools/clippy/tests/ui/box_collection.rs b/src/tools/clippy/tests/ui/box_collection.rs index 0f7d3c74ddd07..7ae5446924fa0 100644 --- a/src/tools/clippy/tests/ui/box_collection.rs +++ b/src/tools/clippy/tests/ui/box_collection.rs @@ -1,4 +1,3 @@ -#![warn(clippy::all)] #![allow( clippy::boxed_local, clippy::needless_pass_by_value, diff --git a/src/tools/clippy/tests/ui/box_collection.stderr b/src/tools/clippy/tests/ui/box_collection.stderr index ebbc3d92b57f7..d730e2dcc1145 100644 --- a/src/tools/clippy/tests/ui/box_collection.stderr +++ b/src/tools/clippy/tests/ui/box_collection.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `Box>`. Consider using just `Vec<..>` - --> tests/ui/box_collection.rs:21:15 + --> tests/ui/box_collection.rs:20:15 | LL | fn test1(foo: Box>) {} | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | fn test1(foo: Box>) {} = help: to override `-D warnings` add `#[allow(clippy::box_collection)]` error: you seem to be trying to use `Box`. Consider using just `String` - --> tests/ui/box_collection.rs:29:15 + --> tests/ui/box_collection.rs:28:15 | LL | fn test3(foo: Box) {} | ^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | fn test3(foo: Box) {} = help: `String` is already on the heap, `Box` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `HashMap<..>` - --> tests/ui/box_collection.rs:32:15 + --> tests/ui/box_collection.rs:31:15 | LL | fn test4(foo: Box>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | fn test4(foo: Box>) {} = help: `HashMap<..>` is already on the heap, `Box>` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `HashSet<..>` - --> tests/ui/box_collection.rs:35:15 + --> tests/ui/box_collection.rs:34:15 | LL | fn test5(foo: Box>) {} | ^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | fn test5(foo: Box>) {} = help: `HashSet<..>` is already on the heap, `Box>` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `VecDeque<..>` - --> tests/ui/box_collection.rs:38:15 + --> tests/ui/box_collection.rs:37:15 | LL | fn test6(foo: Box>) {} | ^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | fn test6(foo: Box>) {} = help: `VecDeque<..>` is already on the heap, `Box>` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `LinkedList<..>` - --> tests/ui/box_collection.rs:41:15 + --> tests/ui/box_collection.rs:40:15 | LL | fn test7(foo: Box>) {} | ^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | fn test7(foo: Box>) {} = help: `LinkedList<..>` is already on the heap, `Box>` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `BTreeMap<..>` - --> tests/ui/box_collection.rs:44:15 + --> tests/ui/box_collection.rs:43:15 | LL | fn test8(foo: Box>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | fn test8(foo: Box>) {} = help: `BTreeMap<..>` is already on the heap, `Box>` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `BTreeSet<..>` - --> tests/ui/box_collection.rs:47:15 + --> tests/ui/box_collection.rs:46:15 | LL | fn test9(foo: Box>) {} | ^^^^^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | fn test9(foo: Box>) {} = help: `BTreeSet<..>` is already on the heap, `Box>` makes an extra allocation error: you seem to be trying to use `Box>`. Consider using just `BinaryHeap<..>` - --> tests/ui/box_collection.rs:50:16 + --> tests/ui/box_collection.rs:49:16 | LL | fn test10(foo: Box>) {} | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed index bf7635fdf09bf..0c9d21243546d 100644 --- a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed +++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed @@ -1,5 +1,4 @@ #![warn(clippy::case_sensitive_file_extension_comparisons)] -#![allow(clippy::unnecessary_map_or)] use std::string::String; @@ -13,7 +12,7 @@ impl TestStruct { fn is_rust_file(filename: &str) -> bool { std::path::Path::new(filename) .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) + .is_some_and(|ext| ext.eq_ignore_ascii_case("rs")) //~^ case_sensitive_file_extension_comparisons } @@ -21,18 +20,18 @@ fn main() { // std::string::String and &str should trigger the lint failure with .ext12 let _ = std::path::Path::new(&String::new()) .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); //~^ case_sensitive_file_extension_comparisons let _ = std::path::Path::new("str") .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); //~^ case_sensitive_file_extension_comparisons // The fixup should preserve the indentation level { let _ = std::path::Path::new("str") .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); //~^ case_sensitive_file_extension_comparisons } @@ -42,11 +41,11 @@ fn main() { // std::string::String and &str should trigger the lint failure with .EXT12 let _ = std::path::Path::new(&String::new()) .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("EXT12")); //~^ case_sensitive_file_extension_comparisons let _ = std::path::Path::new("str") .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + .is_some_and(|ext| ext.eq_ignore_ascii_case("EXT12")); //~^ case_sensitive_file_extension_comparisons // Should not trigger the lint failure because of the calls to to_lowercase and to_uppercase @@ -76,3 +75,11 @@ fn main() { let _ = "str".ends_with(".123"); TestStruct {}.ends_with(".123"); } + +#[clippy::msrv = "1.69"] +fn msrv_check() { + let _ = std::path::Path::new(&String::new()) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + //~^ case_sensitive_file_extension_comparisons +} diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs index 0c4070a42d4b0..f8a947aa827b9 100644 --- a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -1,5 +1,4 @@ #![warn(clippy::case_sensitive_file_extension_comparisons)] -#![allow(clippy::unnecessary_map_or)] use std::string::String; @@ -64,3 +63,9 @@ fn main() { let _ = "str".ends_with(".123"); TestStruct {}.ends_with(".123"); } + +#[clippy::msrv = "1.69"] +fn msrv_check() { + let _ = String::new().ends_with(".ext12"); + //~^ case_sensitive_file_extension_comparisons +} diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr index e035534d26996..93bee8e766719 100644 --- a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr +++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr @@ -1,5 +1,5 @@ error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:14:5 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:13:5 | LL | filename.ends_with(".rs") | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,11 +11,11 @@ help: use std::path::Path | LL ~ std::path::Path::new(filename) LL + .extension() -LL + .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) +LL + .is_some_and(|ext| ext.eq_ignore_ascii_case("rs")) | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:20:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:19:13 | LL | let _ = String::new().ends_with(".ext12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,11 +25,11 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new(&String::new()) LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:22:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:21:13 | LL | let _ = "str".ends_with(".ext12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,11 +39,11 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new("str") LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:27:17 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:26:17 | LL | let _ = "str".ends_with(".ext12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,11 +53,11 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new("str") LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:35:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:34:13 | LL | let _ = String::new().ends_with(".EXT12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -67,11 +67,11 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new(&String::new()) LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("EXT12")); | error: case-sensitive file extension comparison - --> tests/ui/case_sensitive_file_extension_comparisons.rs:37:13 + --> tests/ui/case_sensitive_file_extension_comparisons.rs:36:13 | LL | let _ = "str".ends_with(".EXT12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -81,8 +81,22 @@ help: use std::path::Path | LL ~ let _ = std::path::Path::new("str") LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); +LL ~ .is_some_and(|ext| ext.eq_ignore_ascii_case("EXT12")); + | + +error: case-sensitive file extension comparison + --> tests/ui/case_sensitive_file_extension_comparisons.rs:69:13 + | +LL | let _ = String::new().ends_with(".ext12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new(&String::new()) +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); | -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs index 88c2549f4dc50..77329cf5455de 100644 --- a/src/tools/clippy/tests/ui/cast.rs +++ b/src/tools/clippy/tests/ui/cast.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: only some diagnostics have suggestions #![feature(repr128)] #![allow(incomplete_features)] diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs index e924018088674..e5bef2a99d59e 100644 --- a/src/tools/clippy/tests/ui/cast_size.rs +++ b/src/tools/clippy/tests/ui/cast_size.rs @@ -1,7 +1,7 @@ //@revisions: 32bit 64bit //@[32bit]ignore-bitwidth: 64 //@[64bit]ignore-bitwidth: 32 -//@no-rustfix +//@no-rustfix: only some diagnostics have suggestions #![warn( clippy::cast_precision_loss, diff --git a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed new file mode 100644 index 0000000000000..04c8f6782c51e --- /dev/null +++ b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed @@ -0,0 +1,65 @@ +#![feature(round_char_boundary)] +#![warn(clippy::char_indices_as_byte_indices)] + +trait StrExt { + fn use_index(&self, _: usize); +} +impl StrExt for str { + fn use_index(&self, _: usize) {} +} + +fn bad(prim: &str, string: String) { + for (idx, _) in prim.char_indices() { + let _ = prim[..idx]; + //~^ char_indices_as_byte_indices + prim.split_at(idx); + //~^ char_indices_as_byte_indices + + // This won't panic, but it can still return a wrong substring + let _ = prim[..prim.floor_char_boundary(idx)]; + //~^ char_indices_as_byte_indices + + // can't use #[expect] here because the .fixed file will still have the attribute and create an + // unfulfilled expectation, but make sure lint level attributes work on the use expression: + #[allow(clippy::char_indices_as_byte_indices)] + let _ = prim[..idx]; + } + + for c in prim.char_indices() { + let _ = prim[..c.0]; + //~^ char_indices_as_byte_indices + prim.split_at(c.0); + //~^ char_indices_as_byte_indices + } + + for (idx, _) in string.char_indices() { + let _ = string[..idx]; + //~^ char_indices_as_byte_indices + string.split_at(idx); + //~^ char_indices_as_byte_indices + } +} + +fn good(prim: &str, prim2: &str) { + for (idx, _) in prim.chars().enumerate() { + // Indexing into a different string + let _ = prim2[..idx]; + + // Unknown use + std::hint::black_box(idx); + + // Method call to user defined extension trait + prim.use_index(idx); + + // str method taking a usize that doesn't represent a byte index + prim.splitn(idx, prim2); + } + + let mut string = "äa".to_owned(); + for (idx, _) in string.clone().chars().enumerate() { + // Even though the receiver is the same expression, it should not be treated as the same value. + string.clone().remove(idx); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs new file mode 100644 index 0000000000000..773a4fc65f12f --- /dev/null +++ b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs @@ -0,0 +1,65 @@ +#![feature(round_char_boundary)] +#![warn(clippy::char_indices_as_byte_indices)] + +trait StrExt { + fn use_index(&self, _: usize); +} +impl StrExt for str { + fn use_index(&self, _: usize) {} +} + +fn bad(prim: &str, string: String) { + for (idx, _) in prim.chars().enumerate() { + let _ = prim[..idx]; + //~^ char_indices_as_byte_indices + prim.split_at(idx); + //~^ char_indices_as_byte_indices + + // This won't panic, but it can still return a wrong substring + let _ = prim[..prim.floor_char_boundary(idx)]; + //~^ char_indices_as_byte_indices + + // can't use #[expect] here because the .fixed file will still have the attribute and create an + // unfulfilled expectation, but make sure lint level attributes work on the use expression: + #[allow(clippy::char_indices_as_byte_indices)] + let _ = prim[..idx]; + } + + for c in prim.chars().enumerate() { + let _ = prim[..c.0]; + //~^ char_indices_as_byte_indices + prim.split_at(c.0); + //~^ char_indices_as_byte_indices + } + + for (idx, _) in string.chars().enumerate() { + let _ = string[..idx]; + //~^ char_indices_as_byte_indices + string.split_at(idx); + //~^ char_indices_as_byte_indices + } +} + +fn good(prim: &str, prim2: &str) { + for (idx, _) in prim.chars().enumerate() { + // Indexing into a different string + let _ = prim2[..idx]; + + // Unknown use + std::hint::black_box(idx); + + // Method call to user defined extension trait + prim.use_index(idx); + + // str method taking a usize that doesn't represent a byte index + prim.splitn(idx, prim2); + } + + let mut string = "äa".to_owned(); + for (idx, _) in string.clone().chars().enumerate() { + // Even though the receiver is the same expression, it should not be treated as the same value. + string.clone().remove(idx); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr new file mode 100644 index 0000000000000..e2b4c1db78cf4 --- /dev/null +++ b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr @@ -0,0 +1,130 @@ +error: indexing into a string with a character position where a byte index is expected + --> tests/ui/char_indices_as_byte_indices.rs:13:24 + | +LL | let _ = prim[..idx]; + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:12:10 + | +LL | for (idx, _) in prim.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ + = note: `-D clippy::char-indices-as-byte-indices` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::char_indices_as_byte_indices)]` +help: consider using `.char_indices()` instead + | +LL - for (idx, _) in prim.chars().enumerate() { +LL + for (idx, _) in prim.char_indices() { + | + +error: passing a character position to a method that expects a byte index + --> tests/ui/char_indices_as_byte_indices.rs:15:23 + | +LL | prim.split_at(idx); + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:12:10 + | +LL | for (idx, _) in prim.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL - for (idx, _) in prim.chars().enumerate() { +LL + for (idx, _) in prim.char_indices() { + | + +error: passing a character position to a method that expects a byte index + --> tests/ui/char_indices_as_byte_indices.rs:19:49 + | +LL | let _ = prim[..prim.floor_char_boundary(idx)]; + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:12:10 + | +LL | for (idx, _) in prim.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL - for (idx, _) in prim.chars().enumerate() { +LL + for (idx, _) in prim.char_indices() { + | + +error: indexing into a string with a character position where a byte index is expected + --> tests/ui/char_indices_as_byte_indices.rs:29:24 + | +LL | let _ = prim[..c.0]; + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:28:9 + | +LL | for c in prim.chars().enumerate() { + | ^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL - for c in prim.chars().enumerate() { +LL + for c in prim.char_indices() { + | + +error: passing a character position to a method that expects a byte index + --> tests/ui/char_indices_as_byte_indices.rs:31:23 + | +LL | prim.split_at(c.0); + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:28:9 + | +LL | for c in prim.chars().enumerate() { + | ^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL - for c in prim.chars().enumerate() { +LL + for c in prim.char_indices() { + | + +error: indexing into a string with a character position where a byte index is expected + --> tests/ui/char_indices_as_byte_indices.rs:36:26 + | +LL | let _ = string[..idx]; + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:35:10 + | +LL | for (idx, _) in string.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL - for (idx, _) in string.chars().enumerate() { +LL + for (idx, _) in string.char_indices() { + | + +error: passing a character position to a method that expects a byte index + --> tests/ui/char_indices_as_byte_indices.rs:38:25 + | +LL | string.split_at(idx); + | ^^^ + | + = note: a character can take up more than one byte, so they are not interchangeable +note: position comes from the enumerate iterator + --> tests/ui/char_indices_as_byte_indices.rs:35:10 + | +LL | for (idx, _) in string.chars().enumerate() { + | ^^^ ^^^^^^^^^^^ +help: consider using `.char_indices()` instead + | +LL - for (idx, _) in string.chars().enumerate() { +LL + for (idx, _) in string.char_indices() { + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs index 145885702a9bc..7635f848cb349 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs @@ -4,7 +4,7 @@ clippy::branches_sharing_code, clippy::unnecessary_literal_unwrap )] -//@no-rustfix +//@no-rustfix: has placeholders fn test_nested() { fn nested() { let x = Some(()); diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs index 4101897d38004..785b2473c0530 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs @@ -1,4 +1,4 @@ -//@no-rustfix: overlapping suggestions +//@no-rustfix: has placeholders #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] #![allow( clippy::if_same_then_else, @@ -188,6 +188,91 @@ fn issue11371() { } } +fn gen_option() -> Option<()> { + Some(()) + // Or None +} + +fn gen_result() -> Result<(), ()> { + Ok(()) + // Or Err(()) +} + +fn issue14725() { + let option = Some(()); + + if option.is_some() { + let _ = option.as_ref().unwrap(); + //~^ unnecessary_unwrap + } else { + let _ = option.as_ref().unwrap(); + //~^ panicking_unwrap + } + + let result = Ok::<(), ()>(()); + + if result.is_ok() { + let _y = 1; + result.as_ref().unwrap(); + //~^ unnecessary_unwrap + } else { + let _y = 1; + result.as_ref().unwrap(); + //~^ panicking_unwrap + } + + let mut option = Some(()); + if option.is_some() { + option = gen_option(); + option.as_mut().unwrap(); + } else { + option = gen_option(); + option.as_mut().unwrap(); + } + + let mut result = Ok::<(), ()>(()); + if result.is_ok() { + result = gen_result(); + result.as_mut().unwrap(); + } else { + result = gen_result(); + result.as_mut().unwrap(); + } +} + +fn issue14763(x: Option, r: Result<(), ()>) { + _ = || { + if x.is_some() { + _ = x.unwrap(); + //~^ unnecessary_unwrap + } else { + _ = x.unwrap(); + //~^ panicking_unwrap + } + }; + _ = || { + if r.is_ok() { + _ = r.as_ref().unwrap(); + //~^ unnecessary_unwrap + } else { + _ = r.as_ref().unwrap(); + //~^ panicking_unwrap + } + }; +} + +const ISSUE14763: fn(Option) = |x| { + _ = || { + if x.is_some() { + _ = x.unwrap(); + //~^ unnecessary_unwrap + } else { + _ = x.unwrap(); + //~^ panicking_unwrap + } + } +}; + fn check_expect() { let x = Some(()); 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 c17eaef2326b3..a4bf00992445e 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -236,7 +236,93 @@ LL | if result.is_ok() { LL | result.as_mut().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: creating a shared reference to mutable static is discouraged +error: called `unwrap` on `option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:205:17 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some() = &option` +LL | let _ = option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:208:17 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | let _ = option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:216:9 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok() = &result` +LL | let _y = 1; +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:220:9 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `x` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:246:17 + | +LL | if x.is_some() { + | -------------- help: try: `if let Some() = x` +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:249:17 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: called `unwrap` on `r` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:255:17 + | +LL | if r.is_ok() { + | ------------ help: try: `if let Ok() = &r` +LL | _ = r.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:258:17 + | +LL | if r.is_ok() { + | --------- because of this check +... +LL | _ = r.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `x` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:267:17 + | +LL | if x.is_some() { + | -------------- help: try: `if let Some() = x` +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:270:17 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | _ = x.unwrap(); + | ^^^^^^^^^^ + +error: creating a shared reference to mutable static --> tests/ui/checked_unwrap/simple_conditionals.rs:183:12 | LL | if X.is_some() { @@ -244,8 +330,7 @@ LL | if X.is_some() { | = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = note: `-D static-mut-refs` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(static_mut_refs)]` + = note: `#[deny(static_mut_refs)]` on by default -error: aborting due to 26 previous errors +error: aborting due to 36 previous errors diff --git a/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.fixed b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.fixed new file mode 100644 index 0000000000000..818c6e23259e3 --- /dev/null +++ b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.fixed @@ -0,0 +1,64 @@ +#![warn(clippy::cloned_ref_to_slice_refs)] + +#[derive(Clone)] +struct Data; + +fn main() { + { + let data = Data; + let data_ref = &data; + let _ = std::slice::from_ref(data_ref); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + let _ = std::slice::from_ref(&Data); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + #[derive(Clone)] + struct Point(i32, i32); + + let _ = std::slice::from_ref(&Point(0, 0)); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + // the string was cloned with the intention to not mutate + { + struct BetterString(String); + + let mut message = String::from("good"); + let sender = BetterString(message.clone()); + + message.push_str("bye!"); + + println!("{} {}", message, sender.0) + } + + // the string was cloned with the intention to not mutate + { + let mut x = String::from("Hello"); + let r = &[x.clone()]; + x.push('!'); + println!("r = `{}', x = `{x}'", r[0]); + } + + // mutable borrows may have the intention to clone + { + let data = Data; + let data_ref = &data; + let _ = &mut [data_ref.clone()]; + } + + // `T::clone` is used to denote a clone with side effects + { + use std::sync::Arc; + let data = Arc::new(Data); + let _ = &[Arc::clone(&data)]; + } + + // slices with multiple members can only be made from a singular reference + { + let data_1 = Data; + let data_2 = Data; + let _ = &[data_1.clone(), data_2.clone()]; + } +} diff --git a/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.rs b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.rs new file mode 100644 index 0000000000000..9517dbfd1569c --- /dev/null +++ b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.rs @@ -0,0 +1,64 @@ +#![warn(clippy::cloned_ref_to_slice_refs)] + +#[derive(Clone)] +struct Data; + +fn main() { + { + let data = Data; + let data_ref = &data; + let _ = &[data_ref.clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + let _ = &[Data.clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + { + #[derive(Clone)] + struct Point(i32, i32); + + let _ = &[Point(0, 0).clone()]; //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref` + } + + // the string was cloned with the intention to not mutate + { + struct BetterString(String); + + let mut message = String::from("good"); + let sender = BetterString(message.clone()); + + message.push_str("bye!"); + + println!("{} {}", message, sender.0) + } + + // the string was cloned with the intention to not mutate + { + let mut x = String::from("Hello"); + let r = &[x.clone()]; + x.push('!'); + println!("r = `{}', x = `{x}'", r[0]); + } + + // mutable borrows may have the intention to clone + { + let data = Data; + let data_ref = &data; + let _ = &mut [data_ref.clone()]; + } + + // `T::clone` is used to denote a clone with side effects + { + use std::sync::Arc; + let data = Arc::new(Data); + let _ = &[Arc::clone(&data)]; + } + + // slices with multiple members can only be made from a singular reference + { + let data_1 = Data; + let data_2 = Data; + let _ = &[data_1.clone(), data_2.clone()]; + } +} diff --git a/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.stderr b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.stderr new file mode 100644 index 0000000000000..6a31d87823955 --- /dev/null +++ b/src/tools/clippy/tests/ui/cloned_ref_to_slice_refs.stderr @@ -0,0 +1,23 @@ +error: this call to `clone` can be replaced with `std::slice::from_ref` + --> tests/ui/cloned_ref_to_slice_refs.rs:10:17 + | +LL | let _ = &[data_ref.clone()]; + | ^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(data_ref)` + | + = note: `-D clippy::cloned-ref-to-slice-refs` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cloned_ref_to_slice_refs)]` + +error: this call to `clone` can be replaced with `std::slice::from_ref` + --> tests/ui/cloned_ref_to_slice_refs.rs:14:17 + | +LL | let _ = &[Data.clone()]; + | ^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&Data)` + +error: this call to `clone` can be replaced with `std::slice::from_ref` + --> tests/ui/cloned_ref_to_slice_refs.rs:21:17 + | +LL | let _ = &[Point(0, 0).clone()]; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&Point(0, 0))` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed index eb01633a25fd5..85d0991bef05d 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed @@ -74,3 +74,12 @@ impl ToOwned for Baz { Baz } } + +fn issue_8103() { + let foo1 = String::from("foo"); + let _ = foo1 == "foo"; + //~^ cmp_owned + let foo2 = "foo"; + let _ = foo1 == foo2; + //~^ cmp_owned +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs index 82409f27b129c..2393757d76f2b 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs @@ -74,3 +74,12 @@ impl ToOwned for Baz { Baz } } + +fn issue_8103() { + let foo1 = String::from("foo"); + let _ = foo1 == "foo".to_owned(); + //~^ cmp_owned + let foo2 = "foo"; + let _ = foo1 == foo2.to_owned(); + //~^ cmp_owned +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr index ca2ab44847274..dd9ffa70897ab 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr @@ -37,5 +37,17 @@ error: this creates an owned instance just for comparison LL | "abc".chars().filter(|c| c.to_owned() != 'X'); | ^^^^^^^^^^^^ help: try: `*c` -error: aborting due to 6 previous errors +error: this creates an owned instance just for comparison + --> tests/ui/cmp_owned/with_suggestion.rs:80:21 + | +LL | let _ = foo1 == "foo".to_owned(); + | ^^^^^^^^^^^^^^^^ help: try: `"foo"` + +error: this creates an owned instance just for comparison + --> tests/ui/cmp_owned/with_suggestion.rs:83:21 + | +LL | let _ = foo1 == foo2.to_owned(); + | ^^^^^^^^^^^^^^^ help: try: `foo2` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/cognitive_complexity.rs b/src/tools/clippy/tests/ui/cognitive_complexity.rs index 2dbec955f63fe..8080c6775e0be 100644 --- a/src/tools/clippy/tests/ui/cognitive_complexity.rs +++ b/src/tools/clippy/tests/ui/cognitive_complexity.rs @@ -1,6 +1,11 @@ -#![allow(clippy::all)] #![warn(clippy::cognitive_complexity)] -#![allow(unused, unused_crate_dependencies)] +#![allow( + clippy::eq_op, + clippy::needless_borrows_for_generic_args, + clippy::needless_return, + clippy::nonminimal_bool, + clippy::uninlined_format_args +)] #[rustfmt::skip] fn main() { @@ -448,3 +453,22 @@ mod issue9300 { } } } + +#[clippy::cognitive_complexity = "1"] +mod issue14422 { + fn foo() { + //~^ cognitive_complexity + for _ in 0..10 { + println!("hello there"); + } + } + + fn bar() { + //~^ cognitive_complexity + for _ in 0..10 { + println!("hello there"); + } + return; + return; + } +} diff --git a/src/tools/clippy/tests/ui/cognitive_complexity.stderr b/src/tools/clippy/tests/ui/cognitive_complexity.stderr index 52607b87c60ef..67ef4e5655bd6 100644 --- a/src/tools/clippy/tests/ui/cognitive_complexity.stderr +++ b/src/tools/clippy/tests/ui/cognitive_complexity.stderr @@ -1,5 +1,5 @@ error: the function has a cognitive complexity of (28/25) - --> tests/ui/cognitive_complexity.rs:6:4 + --> tests/ui/cognitive_complexity.rs:11:4 | LL | fn main() { | ^^^^ @@ -9,7 +9,7 @@ LL | fn main() { = help: to override `-D warnings` add `#[allow(clippy::cognitive_complexity)]` error: the function has a cognitive complexity of (7/1) - --> tests/ui/cognitive_complexity.rs:93:4 + --> tests/ui/cognitive_complexity.rs:98:4 | LL | fn kaboom() { | ^^^^^^ @@ -17,7 +17,7 @@ LL | fn kaboom() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:153:4 + --> tests/ui/cognitive_complexity.rs:158:4 | LL | fn baa() { | ^^^ @@ -25,7 +25,7 @@ LL | fn baa() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:156:13 + --> tests/ui/cognitive_complexity.rs:161:13 | LL | let x = || match 99 { | ^^ @@ -33,7 +33,7 @@ LL | let x = || match 99 { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:174:4 + --> tests/ui/cognitive_complexity.rs:179:4 | LL | fn bar() { | ^^^ @@ -41,7 +41,7 @@ LL | fn bar() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:187:4 + --> tests/ui/cognitive_complexity.rs:192:4 | LL | fn dont_warn_on_tests() { | ^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | fn dont_warn_on_tests() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:197:4 + --> tests/ui/cognitive_complexity.rs:202:4 | LL | fn barr() { | ^^^^ @@ -57,7 +57,7 @@ LL | fn barr() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (3/1) - --> tests/ui/cognitive_complexity.rs:209:4 + --> tests/ui/cognitive_complexity.rs:214:4 | LL | fn barr2() { | ^^^^^ @@ -65,7 +65,7 @@ LL | fn barr2() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:227:4 + --> tests/ui/cognitive_complexity.rs:232:4 | LL | fn barrr() { | ^^^^^ @@ -73,7 +73,7 @@ LL | fn barrr() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (3/1) - --> tests/ui/cognitive_complexity.rs:239:4 + --> tests/ui/cognitive_complexity.rs:244:4 | LL | fn barrr2() { | ^^^^^^ @@ -81,7 +81,7 @@ LL | fn barrr2() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:257:4 + --> tests/ui/cognitive_complexity.rs:262:4 | LL | fn barrrr() { | ^^^^^^ @@ -89,7 +89,7 @@ LL | fn barrrr() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (3/1) - --> tests/ui/cognitive_complexity.rs:269:4 + --> tests/ui/cognitive_complexity.rs:274:4 | LL | fn barrrr2() { | ^^^^^^^ @@ -97,7 +97,7 @@ LL | fn barrrr2() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:287:4 + --> tests/ui/cognitive_complexity.rs:292:4 | LL | fn cake() { | ^^^^ @@ -105,7 +105,7 @@ LL | fn cake() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (4/1) - --> tests/ui/cognitive_complexity.rs:299:8 + --> tests/ui/cognitive_complexity.rs:304:8 | LL | pub fn read_file(input_path: &str) -> String { | ^^^^^^^^^ @@ -113,7 +113,7 @@ LL | pub fn read_file(input_path: &str) -> String { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:332:4 + --> tests/ui/cognitive_complexity.rs:337:4 | LL | fn void(void: Void) { | ^^^^ @@ -121,7 +121,7 @@ LL | fn void(void: Void) { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (8/1) - --> tests/ui/cognitive_complexity.rs:385:4 + --> tests/ui/cognitive_complexity.rs:390:4 | LL | fn early_ret() -> i32 { | ^^^^^^^^^ @@ -129,7 +129,7 @@ LL | fn early_ret() -> i32 { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:408:13 + --> tests/ui/cognitive_complexity.rs:413:13 | LL | let x = |a: i32, b: i32| -> i32 { | ^^^^^^^^^^^^^^^^ @@ -137,7 +137,7 @@ LL | let x = |a: i32, b: i32| -> i32 { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:423:8 + --> tests/ui/cognitive_complexity.rs:428:8 | LL | fn moo(&self) { | ^^^ @@ -145,7 +145,7 @@ LL | fn moo(&self) { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:434:14 + --> tests/ui/cognitive_complexity.rs:439:14 | LL | async fn a() { | ^ @@ -153,12 +153,28 @@ LL | async fn a() { = help: you could split it up into multiple smaller functions error: the function has a cognitive complexity of (2/1) - --> tests/ui/cognitive_complexity.rs:443:22 + --> tests/ui/cognitive_complexity.rs:448:22 | LL | pub async fn async_method() { | ^^^^^^^^^^^^ | = help: you could split it up into multiple smaller functions -error: aborting due to 20 previous errors +error: the function has a cognitive complexity of (2/1) + --> tests/ui/cognitive_complexity.rs:459:8 + | +LL | fn foo() { + | ^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> tests/ui/cognitive_complexity.rs:466:8 + | +LL | fn bar() { + | ^^^ + | + = help: you could split it up into multiple smaller functions + +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/collapsible_if.fixed b/src/tools/clippy/tests/ui/collapsible_if.fixed index 6e994018aef01..b553182a44543 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.fixed +++ b/src/tools/clippy/tests/ui/collapsible_if.fixed @@ -12,34 +12,40 @@ fn main() { let x = "hello"; let y = "world"; - if x == "hello" && y == "world" { - println!("Hello world!"); - } + if x == "hello" + && y == "world" { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if (x == "hello" || x == "world") && (y == "world" || y == "hello") { - println!("Hello world!"); - } + if (x == "hello" || x == "world") + && (y == "world" || y == "hello") { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if x == "hello" && x == "world" && (y == "world" || y == "hello") { - println!("Hello world!"); - } + if x == "hello" && x == "world" + && (y == "world" || y == "hello") { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if (x == "hello" || x == "world") && y == "world" && y == "hello" { - println!("Hello world!"); - } + if (x == "hello" || x == "world") + && y == "world" && y == "hello" { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if x == "hello" && x == "world" && y == "world" && y == "hello" { - println!("Hello world!"); - } + if x == "hello" && x == "world" + && y == "world" && y == "hello" { + println!("Hello world!"); + } //~^^^^^ collapsible_if - if 42 == 1337 && 'a' != 'A' { - println!("world!") - } + if 42 == 1337 + && 'a' != 'A' { + println!("world!") + } //~^^^^^ collapsible_if // Works because any if with an else statement cannot be collapsed. @@ -71,37 +77,17 @@ fn main() { assert!(true); // assert! is just an `if` } - - // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 - if x == "hello" {// Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { // Not collapsible - if y == "world" { + if x == "hello" + && y == "world" { // Collapsible println!("Hello world!"); } - } - - if x == "hello" { - // Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" && y == "world" { // Collapsible - println!("Hello world!"); - } //~^^^^^ collapsible_if if x == "hello" { print!("Hello "); } else { // Not collapsible - if y == "world" { + if let Some(42) = Some(42) { println!("world!") } } @@ -110,51 +96,21 @@ fn main() { print!("Hello "); } else { // Not collapsible - if let Some(42) = Some(42) { - println!("world!") - } - } - - if x == "hello" { - /* Not collapsible */ - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { /* Not collapsible */ if y == "world" { - println!("Hello world!"); + println!("world!") } } - // Test behavior wrt. `let_chains`. - // None of the cases below should be collapsed. fn truth() -> bool { true } - // Prefix: - if let 0 = 1 { - if truth() {} - } - - // Suffix: - if truth() { - if let 0 = 1 {} - } - - // Midfix: - if truth() { - if let 0 = 1 { - if truth() {} - } - } - // Fix #5962 - if matches!(true, true) && matches!(true, true) {} + if matches!(true, true) + && matches!(true, true) {} //~^^^ collapsible_if // Issue #9375 - if matches!(true, true) && truth() && matches!(true, true) {} + if matches!(true, true) && truth() + && matches!(true, true) {} //~^^^ collapsible_if if true { @@ -163,4 +119,38 @@ fn main() { println!("Hello world!"); } } + + if true + && true { + println!("No comment, linted"); + } + //~^^^^^ collapsible_if + + if true { + // Do not collapse because of this comment + if true { + println!("Hello world!"); + } + } +} + +#[rustfmt::skip] +fn layout_check() -> u32 { + if true + && true { + } + // This is a comment, do not collapse code to it + ; 3 + //~^^^^^ collapsible_if +} + +fn issue14722() { + let x = if true { + Some(1) + } else { + if true { + println!("Some debug information"); + }; + None + }; } diff --git a/src/tools/clippy/tests/ui/collapsible_if.rs b/src/tools/clippy/tests/ui/collapsible_if.rs index 5cf591a658c7a..f5998457ca6cf 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.rs +++ b/src/tools/clippy/tests/ui/collapsible_if.rs @@ -83,27 +83,6 @@ fn main() { assert!(true); // assert! is just an `if` } - - // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 - if x == "hello" {// Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { // Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { - // Not collapsible - if y == "world" { - println!("Hello world!"); - } - } - if x == "hello" { if y == "world" { // Collapsible println!("Hello world!"); @@ -115,7 +94,7 @@ fn main() { print!("Hello "); } else { // Not collapsible - if y == "world" { + if let Some(42) = Some(42) { println!("world!") } } @@ -124,45 +103,13 @@ fn main() { print!("Hello "); } else { // Not collapsible - if let Some(42) = Some(42) { - println!("world!") - } - } - - if x == "hello" { - /* Not collapsible */ - if y == "world" { - println!("Hello world!"); - } - } - - if x == "hello" { /* Not collapsible */ if y == "world" { - println!("Hello world!"); + println!("world!") } } - // Test behavior wrt. `let_chains`. - // None of the cases below should be collapsed. fn truth() -> bool { true } - // Prefix: - if let 0 = 1 { - if truth() {} - } - - // Suffix: - if truth() { - if let 0 = 1 {} - } - - // Midfix: - if truth() { - if let 0 = 1 { - if truth() {} - } - } - // Fix #5962 if matches!(true, true) { if matches!(true, true) {} @@ -181,4 +128,39 @@ fn main() { println!("Hello world!"); } } + + if true { + if true { + println!("No comment, linted"); + } + } + //~^^^^^ collapsible_if + + if true { + // Do not collapse because of this comment + if true { + println!("Hello world!"); + } + } +} + +#[rustfmt::skip] +fn layout_check() -> u32 { + if true { + if true { + } + // This is a comment, do not collapse code to it + }; 3 + //~^^^^^ collapsible_if +} + +fn issue14722() { + let x = if true { + Some(1) + } else { + if true { + println!("Some debug information"); + }; + None + }; } diff --git a/src/tools/clippy/tests/ui/collapsible_if.stderr b/src/tools/clippy/tests/ui/collapsible_if.stderr index 3cc3fe5534f25..32c6b0194030a 100644 --- a/src/tools/clippy/tests/ui/collapsible_if.stderr +++ b/src/tools/clippy/tests/ui/collapsible_if.stderr @@ -12,9 +12,10 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` help: collapse nested if block | -LL ~ if x == "hello" && y == "world" { -LL + println!("Hello world!"); -LL + } +LL ~ if x == "hello" +LL ~ && y == "world" { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -29,9 +30,10 @@ LL | | } | help: collapse nested if block | -LL ~ if (x == "hello" || x == "world") && (y == "world" || y == "hello") { -LL + println!("Hello world!"); -LL + } +LL ~ if (x == "hello" || x == "world") { +LL ~ && (y == "world" || y == "hello") { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -46,9 +48,10 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" && x == "world" && (y == "world" || y == "hello") { -LL + println!("Hello world!"); -LL + } +LL ~ if x == "hello" && x == "world" +LL ~ && (y == "world" || y == "hello") { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -63,9 +66,10 @@ LL | | } | help: collapse nested if block | -LL ~ if (x == "hello" || x == "world") && y == "world" && y == "hello" { -LL + println!("Hello world!"); -LL + } +LL ~ if (x == "hello" || x == "world") { +LL ~ && y == "world" && y == "hello" { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -80,9 +84,10 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" && x == "world" && y == "world" && y == "hello" { -LL + println!("Hello world!"); -LL + } +LL ~ if x == "hello" && x == "world" +LL ~ && y == "world" && y == "hello" { +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed @@ -97,13 +102,14 @@ LL | | } | help: collapse nested if block | -LL ~ if 42 == 1337 && 'a' != 'A' { -LL + println!("world!") -LL + } +LL ~ if 42 == 1337 +LL ~ && 'a' != 'A' { +LL | println!("world!") +LL ~ } | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:107:5 + --> tests/ui/collapsible_if.rs:86:5 | LL | / if x == "hello" { LL | | if y == "world" { // Collapsible @@ -114,26 +120,75 @@ LL | | } | help: collapse nested if block | -LL ~ if x == "hello" && y == "world" { // Collapsible -LL + println!("Hello world!"); -LL + } +LL ~ if x == "hello" +LL ~ && y == "world" { // Collapsible +LL | println!("Hello world!"); +LL ~ } | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:167:5 + --> tests/ui/collapsible_if.rs:114:5 | LL | / if matches!(true, true) { LL | | if matches!(true, true) {} LL | | } - | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}` + | |_____^ + | +help: collapse nested if block + | +LL ~ if matches!(true, true) +LL ~ && matches!(true, true) {} + | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:173:5 + --> tests/ui/collapsible_if.rs:120:5 | LL | / if matches!(true, true) && truth() { LL | | if matches!(true, true) {} LL | | } - | |_____^ help: collapse nested if block: `if matches!(true, true) && truth() && matches!(true, true) {}` + | |_____^ + | +help: collapse nested if block + | +LL ~ if matches!(true, true) && truth() +LL ~ && matches!(true, true) {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if.rs:132:5 + | +LL | / if true { +LL | | if true { +LL | | println!("No comment, linted"); +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if true +LL ~ && true { +LL | println!("No comment, linted"); +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if.rs:149:5 + | +LL | / if true { +LL | | if true { +... | +LL | | }; 3 + | |_____^ + | +help: collapse nested if block + | +LL ~ if true +LL ~ && true { +LL | } +LL | // This is a comment, do not collapse code to it +LL ~ ; 3 + | -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/collapsible_if_let_chains.edition2024.fixed b/src/tools/clippy/tests/ui/collapsible_if_let_chains.edition2024.fixed new file mode 100644 index 0000000000000..ad08c21ba9ede --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if_let_chains.edition2024.fixed @@ -0,0 +1,68 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2021] check-pass + +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) { + // with comment, so do not lint + if let Some(b) = Some(4) { + let _ = a + b; + } + } + + //~[edition2024]v collapsible_if + if let Some(a) = Some(3) + && let Some(b) = Some(4) { + let _ = a + b; + } + + //~[edition2024]v collapsible_if + if let Some(a) = Some(3) + && a + 1 == 4 { + let _ = a; + } + + //~[edition2024]v collapsible_if + if Some(3) == Some(4).map(|x| x - 1) + && let Some(b) = Some(4) { + let _ = b; + } + + fn truth() -> bool { + true + } + + // Prefix: + //~[edition2024]v collapsible_if + if let 0 = 1 + && truth() {} + + // Suffix: + //~[edition2024]v collapsible_if + if truth() + && let 0 = 1 {} + + // Midfix: + //~[edition2024]vvv collapsible_if + //~[edition2024]v collapsible_if + if truth() + && let 0 = 1 + && truth() {} +} + +#[clippy::msrv = "1.87.0"] +fn msrv_1_87() { + if let 0 = 1 { + if true {} + } +} + +#[clippy::msrv = "1.88.0"] +fn msrv_1_88() { + //~[edition2024]v collapsible_if + if let 0 = 1 + && true {} +} diff --git a/src/tools/clippy/tests/ui/collapsible_if_let_chains.edition2024.stderr b/src/tools/clippy/tests/ui/collapsible_if_let_chains.edition2024.stderr new file mode 100644 index 0000000000000..b0aa3cecadb60 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if_let_chains.edition2024.stderr @@ -0,0 +1,132 @@ +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:17:5 + | +LL | / if let Some(a) = Some(3) { +LL | | if let Some(b) = Some(4) { +LL | | let _ = a + b; +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_if)]` +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL ~ && let Some(b) = Some(4) { +LL | let _ = a + b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:24:5 + | +LL | / if let Some(a) = Some(3) { +LL | | if a + 1 == 4 { +LL | | let _ = a; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let Some(a) = Some(3) +LL ~ && a + 1 == 4 { +LL | let _ = a; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:31:5 + | +LL | / if Some(3) == Some(4).map(|x| x - 1) { +LL | | if let Some(b) = Some(4) { +LL | | let _ = b; +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if Some(3) == Some(4).map(|x| x - 1) +LL ~ && let Some(b) = Some(4) { +LL | let _ = b; +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:43:5 + | +LL | / if let 0 = 1 { +LL | | if truth() {} +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let 0 = 1 +LL ~ && truth() {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:49:5 + | +LL | / if truth() { +LL | | if let 0 = 1 {} +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if truth() +LL ~ && let 0 = 1 {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:56:5 + | +LL | / if truth() { +LL | | if let 0 = 1 { +LL | | if truth() {} +LL | | } +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if truth() +LL ~ && let 0 = 1 { +LL | if truth() {} +LL ~ } + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:57:9 + | +LL | / if let 0 = 1 { +LL | | if truth() {} +LL | | } + | |_________^ + | +help: collapse nested if block + | +LL ~ if let 0 = 1 +LL ~ && truth() {} + | + +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if_let_chains.rs:73:5 + | +LL | / if let 0 = 1 { +LL | | if true {} +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if let 0 = 1 +LL ~ && true {} + | + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/collapsible_if_let_chains.rs b/src/tools/clippy/tests/ui/collapsible_if_let_chains.rs new file mode 100644 index 0000000000000..b2e88b1a55689 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if_let_chains.rs @@ -0,0 +1,76 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2021] check-pass + +#![warn(clippy::collapsible_if)] + +fn main() { + if let Some(a) = Some(3) { + // with comment, so do not lint + if let Some(b) = Some(4) { + let _ = a + b; + } + } + + //~[edition2024]v collapsible_if + if let Some(a) = Some(3) { + if let Some(b) = Some(4) { + let _ = a + b; + } + } + + //~[edition2024]v collapsible_if + if let Some(a) = Some(3) { + if a + 1 == 4 { + let _ = a; + } + } + + //~[edition2024]v collapsible_if + if Some(3) == Some(4).map(|x| x - 1) { + if let Some(b) = Some(4) { + let _ = b; + } + } + + fn truth() -> bool { + true + } + + // Prefix: + //~[edition2024]v collapsible_if + if let 0 = 1 { + if truth() {} + } + + // Suffix: + //~[edition2024]v collapsible_if + if truth() { + if let 0 = 1 {} + } + + // Midfix: + //~[edition2024]vvv collapsible_if + //~[edition2024]v collapsible_if + if truth() { + if let 0 = 1 { + if truth() {} + } + } +} + +#[clippy::msrv = "1.87.0"] +fn msrv_1_87() { + if let 0 = 1 { + if true {} + } +} + +#[clippy::msrv = "1.88.0"] +fn msrv_1_88() { + //~[edition2024]v collapsible_if + if let 0 = 1 { + if true {} + } +} diff --git a/src/tools/clippy/tests/ui/collapsible_match.rs b/src/tools/clippy/tests/ui/collapsible_match.rs index 796cabd4b669a..71b82040ff62a 100644 --- a/src/tools/clippy/tests/ui/collapsible_match.rs +++ b/src/tools/clippy/tests/ui/collapsible_match.rs @@ -1,5 +1,6 @@ #![warn(clippy::collapsible_match)] #![allow( + clippy::collapsible_if, clippy::equatable_if_let, clippy::needless_return, clippy::no_effect, @@ -303,6 +304,18 @@ pub fn test_2(x: Issue9647) { } } +// https://github.com/rust-lang/rust-clippy/issues/14281 +fn lint_emitted_at_right_node(opt: Option>) { + let n = match opt { + #[expect(clippy::collapsible_match)] + Some(n) => match n { + Ok(n) => n, + _ => return, + }, + None => return, + }; +} + fn make() -> T { unimplemented!() } diff --git a/src/tools/clippy/tests/ui/collapsible_match.stderr b/src/tools/clippy/tests/ui/collapsible_match.stderr index 5294a9d6975db..c290d84ec2976 100644 --- a/src/tools/clippy/tests/ui/collapsible_match.stderr +++ b/src/tools/clippy/tests/ui/collapsible_match.stderr @@ -1,5 +1,5 @@ error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:14:20 + --> tests/ui/collapsible_match.rs:15:20 | LL | Ok(val) => match val { | ____________________^ @@ -10,7 +10,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:14:12 + --> tests/ui/collapsible_match.rs:15:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -21,7 +21,7 @@ LL | Some(n) => foo(n), = help: to override `-D warnings` add `#[allow(clippy::collapsible_match)]` error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:24:20 + --> tests/ui/collapsible_match.rs:25:20 | LL | Ok(val) => match val { | ____________________^ @@ -32,7 +32,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:24:12 + --> tests/ui/collapsible_match.rs:25:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -41,7 +41,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:34:9 + --> tests/ui/collapsible_match.rs:35:9 | LL | / if let Some(n) = val { LL | | @@ -51,7 +51,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:33:15 + --> tests/ui/collapsible_match.rs:34:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -59,7 +59,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:43:9 + --> tests/ui/collapsible_match.rs:44:9 | LL | / if let Some(n) = val { LL | | @@ -71,7 +71,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:42:15 + --> tests/ui/collapsible_match.rs:43:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -79,7 +79,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:56:9 + --> tests/ui/collapsible_match.rs:57:9 | LL | / match val { LL | | @@ -89,7 +89,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:55:15 + --> tests/ui/collapsible_match.rs:56:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -98,7 +98,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:66:13 + --> tests/ui/collapsible_match.rs:67:13 | LL | / if let Some(n) = val { LL | | @@ -108,7 +108,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:65:12 + --> tests/ui/collapsible_match.rs:66:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -116,7 +116,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:77:9 + --> tests/ui/collapsible_match.rs:78:9 | LL | / match val { LL | | @@ -126,7 +126,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:76:15 + --> tests/ui/collapsible_match.rs:77:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -135,7 +135,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:89:13 + --> tests/ui/collapsible_match.rs:90:13 | LL | / if let Some(n) = val { LL | | @@ -147,7 +147,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:88:12 + --> tests/ui/collapsible_match.rs:89:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -155,7 +155,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:102:20 + --> tests/ui/collapsible_match.rs:103:20 | LL | Ok(val) => match val { | ____________________^ @@ -166,7 +166,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:102:12 + --> tests/ui/collapsible_match.rs:103:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -175,7 +175,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:112:22 + --> tests/ui/collapsible_match.rs:113:22 | LL | Some(val) => match val { | ______________________^ @@ -186,7 +186,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:112:14 + --> tests/ui/collapsible_match.rs:113:14 | LL | Some(val) => match val { | ^^^ replace this binding @@ -195,7 +195,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> tests/ui/collapsible_match.rs:256:22 + --> tests/ui/collapsible_match.rs:257:22 | LL | Some(val) => match val { | ______________________^ @@ -206,7 +206,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:256:14 + --> tests/ui/collapsible_match.rs:257:14 | LL | Some(val) => match val { | ^^^ replace this binding @@ -215,7 +215,7 @@ LL | E::A(val) | E::B(val) => foo(val), | ^^^^^^^^^^^^^^^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:288:9 + --> tests/ui/collapsible_match.rs:289:9 | LL | / if let Some(u) = a { LL | | @@ -225,7 +225,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:287:27 + --> tests/ui/collapsible_match.rs:288:27 | LL | if let Issue9647::A { a, .. } = x { | ^ replace this binding @@ -233,7 +233,7 @@ LL | if let Some(u) = a { | ^^^^^^^ with this pattern, prefixed by `a`: error: this `if let` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:298:9 + --> tests/ui/collapsible_match.rs:299:9 | LL | / if let Some(u) = a { LL | | @@ -243,7 +243,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:297:35 + --> tests/ui/collapsible_match.rs:298:35 | LL | if let Issue9647::A { a: Some(a), .. } = x { | ^ replace this binding diff --git a/src/tools/clippy/tests/ui/comparison_chain.rs b/src/tools/clippy/tests/ui/comparison_chain.rs index 669690a4d42ce..6db75a4f364d7 100644 --- a/src/tools/clippy/tests/ui/comparison_chain.rs +++ b/src/tools/clippy/tests/ui/comparison_chain.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: has placeholders #![allow(dead_code)] #![warn(clippy::comparison_chain)] @@ -12,11 +12,10 @@ fn f(x: u8, y: u8, z: u8) { a() } - if x > y { - //~^ comparison_chain - + // Ignored: Not all cases are covered + if x < y { a() - } else if x < y { + } else if x > y { b() } @@ -123,9 +122,8 @@ fn g(x: f64, y: f64, z: f64) { } fn h(x: T, y: T, z: T) { + // Ignored: Not all cases are covered if x > y { - //~^ comparison_chain - a() } else if x < y { b() diff --git a/src/tools/clippy/tests/ui/comparison_chain.stderr b/src/tools/clippy/tests/ui/comparison_chain.stderr index 0256573d0d906..ce580bd54a16a 100644 --- a/src/tools/clippy/tests/ui/comparison_chain.stderr +++ b/src/tools/clippy/tests/ui/comparison_chain.stderr @@ -1,12 +1,12 @@ error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:15:5 + --> tests/ui/comparison_chain.rs:29:5 | LL | / if x > y { LL | | LL | | LL | | a() -LL | | } else if x < y { -LL | | b() +... | +LL | | c() LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` | @@ -14,19 +14,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::comparison_chain)]` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:30:5 - | -LL | / if x > y { -LL | | -LL | | -LL | | a() -... | -LL | | c() -LL | | } - | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` - -error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:40:5 + --> tests/ui/comparison_chain.rs:39:5 | LL | / if x > y { LL | | @@ -38,7 +26,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:50:5 + --> tests/ui/comparison_chain.rs:49:5 | LL | / if x > 1 { LL | | @@ -50,19 +38,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&1) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:126:5 - | -LL | / if x > y { -LL | | -LL | | -LL | | a() -LL | | } else if x < y { -LL | | b() -LL | | } - | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` - -error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:134:5 + --> tests/ui/comparison_chain.rs:132:5 | LL | / if x > y { LL | | @@ -74,7 +50,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:144:5 + --> tests/ui/comparison_chain.rs:142:5 | LL | / if x > y { LL | | @@ -86,7 +62,7 @@ LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match x.cmp(&y) {...}` error: `if` chain can be rewritten with `match` - --> tests/ui/comparison_chain.rs:251:5 + --> tests/ui/comparison_chain.rs:249:5 | LL | / if x + 1 > y * 2 { LL | | @@ -97,5 +73,5 @@ LL | | "cc" LL | | } | |_____^ help: consider rewriting the `if` chain with `match`: `match (x + 1).cmp(&(y * 2)) {...}` -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.fixed b/src/tools/clippy/tests/ui/comparison_to_empty.fixed index dfbb616838402..7a71829dd62cc 100644 --- a/src/tools/clippy/tests/ui/comparison_to_empty.fixed +++ b/src/tools/clippy/tests/ui/comparison_to_empty.fixed @@ -1,6 +1,5 @@ #![warn(clippy::comparison_to_empty)] #![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] -#![feature(let_chains)] fn main() { // Disallow comparisons to empty diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.rs b/src/tools/clippy/tests/ui/comparison_to_empty.rs index 61cdb2bbe9f81..5d213a09e8126 100644 --- a/src/tools/clippy/tests/ui/comparison_to_empty.rs +++ b/src/tools/clippy/tests/ui/comparison_to_empty.rs @@ -1,6 +1,5 @@ #![warn(clippy::comparison_to_empty)] #![allow(clippy::borrow_deref_ref, clippy::needless_if, clippy::useless_vec)] -#![feature(let_chains)] fn main() { // Disallow comparisons to empty diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.stderr b/src/tools/clippy/tests/ui/comparison_to_empty.stderr index 00a50430a3eea..deb3e93887846 100644 --- a/src/tools/clippy/tests/ui/comparison_to_empty.stderr +++ b/src/tools/clippy/tests/ui/comparison_to_empty.stderr @@ -1,5 +1,5 @@ error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:8:13 + --> tests/ui/comparison_to_empty.rs:7:13 | LL | let _ = s == ""; | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` @@ -8,73 +8,73 @@ LL | let _ = s == ""; = help: to override `-D warnings` add `#[allow(clippy::comparison_to_empty)]` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:10:13 + --> tests/ui/comparison_to_empty.rs:9:13 | LL | let _ = s != ""; | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:14:13 + --> tests/ui/comparison_to_empty.rs:13:13 | LL | let _ = v == []; | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:16:13 + --> tests/ui/comparison_to_empty.rs:15:13 | LL | let _ = v != []; | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:18:8 + --> tests/ui/comparison_to_empty.rs:17:8 | LL | if let [] = &*v {} | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(*v).is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:21:8 + --> tests/ui/comparison_to_empty.rs:20:8 | LL | if let [] = s {} | ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:23:8 + --> tests/ui/comparison_to_empty.rs:22:8 | LL | if let [] = &*s {} | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice using `if let` - --> tests/ui/comparison_to_empty.rs:25:8 + --> tests/ui/comparison_to_empty.rs:24:8 | LL | if let [] = &*s | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:27:12 + --> tests/ui/comparison_to_empty.rs:26:12 | LL | && s == [] | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:48:13 + --> tests/ui/comparison_to_empty.rs:47:13 | LL | let _ = s.eq(""); | ^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:50:13 + --> tests/ui/comparison_to_empty.rs:49:13 | LL | let _ = s.ne(""); | ^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:53:13 + --> tests/ui/comparison_to_empty.rs:52:13 | LL | let _ = v.eq(&[]); | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` error: comparison to empty slice - --> tests/ui/comparison_to_empty.rs:55:13 + --> tests/ui/comparison_to_empty.rs:54:13 | LL | let _ = v.ne(&[]); | ^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` diff --git a/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.fixed b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.fixed new file mode 100644 index 0000000000000..e698b99edd5c0 --- /dev/null +++ b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.fixed @@ -0,0 +1,14 @@ +#![feature(float_minimum_maximum)] +#![warn(clippy::confusing_method_to_numeric_cast)] + +fn main() { + let _ = u16::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = u16::MIN as usize; //~ confusing_method_to_numeric_cast + let _ = u16::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = u16::MIN as usize; //~ confusing_method_to_numeric_cast + + let _ = f32::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = f32::MAX as usize; //~ confusing_method_to_numeric_cast + let _ = f32::MIN as usize; //~ confusing_method_to_numeric_cast + let _ = f32::MIN as usize; //~ confusing_method_to_numeric_cast +} diff --git a/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.rs b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.rs new file mode 100644 index 0000000000000..ef65c21563d98 --- /dev/null +++ b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.rs @@ -0,0 +1,14 @@ +#![feature(float_minimum_maximum)] +#![warn(clippy::confusing_method_to_numeric_cast)] + +fn main() { + let _ = u16::max as usize; //~ confusing_method_to_numeric_cast + let _ = u16::min as usize; //~ confusing_method_to_numeric_cast + let _ = u16::max_value as usize; //~ confusing_method_to_numeric_cast + let _ = u16::min_value as usize; //~ confusing_method_to_numeric_cast + + let _ = f32::maximum as usize; //~ confusing_method_to_numeric_cast + let _ = f32::max as usize; //~ confusing_method_to_numeric_cast + let _ = f32::minimum as usize; //~ confusing_method_to_numeric_cast + let _ = f32::min as usize; //~ confusing_method_to_numeric_cast +} diff --git a/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.stderr b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.stderr new file mode 100644 index 0000000000000..ba90df2059af6 --- /dev/null +++ b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.stderr @@ -0,0 +1,100 @@ +error: casting function pointer `u16::max` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:5:13 + | +LL | let _ = u16::max as usize; + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::confusing-method-to-numeric-cast` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::confusing_method_to_numeric_cast)]` +help: did you mean to use the associated constant? + | +LL - let _ = u16::max as usize; +LL + let _ = u16::MAX as usize; + | + +error: casting function pointer `u16::min` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:6:13 + | +LL | let _ = u16::min as usize; + | ^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = u16::min as usize; +LL + let _ = u16::MIN as usize; + | + +error: casting function pointer `u16::max_value` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:7:13 + | +LL | let _ = u16::max_value as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = u16::max_value as usize; +LL + let _ = u16::MAX as usize; + | + +error: casting function pointer `u16::min_value` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:8:13 + | +LL | let _ = u16::min_value as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = u16::min_value as usize; +LL + let _ = u16::MIN as usize; + | + +error: casting function pointer `f32::maximum` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:10:13 + | +LL | let _ = f32::maximum as usize; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::maximum as usize; +LL + let _ = f32::MAX as usize; + | + +error: casting function pointer `f32::max` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:11:13 + | +LL | let _ = f32::max as usize; + | ^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::max as usize; +LL + let _ = f32::MAX as usize; + | + +error: casting function pointer `f32::minimum` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:12:13 + | +LL | let _ = f32::minimum as usize; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::minimum as usize; +LL + let _ = f32::MIN as usize; + | + +error: casting function pointer `f32::min` to `usize` + --> tests/ui/confusing_method_to_numeric_cast.rs:13:13 + | +LL | let _ = f32::min as usize; + | ^^^^^^^^^^^^^^^^^ + | +help: did you mean to use the associated constant? + | +LL - let _ = f32::min as usize; +LL + let _ = f32::MIN as usize; + | + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs b/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs index bbcd599f6d0b8..3352e822ef848 100644 --- a/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs +++ b/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs @@ -1,8 +1,5 @@ //@ check-pass -#![deny(clippy::all)] -#![allow(unused_imports)] - use std::*; fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-10972-tait.rs b/src/tools/clippy/tests/ui/crashes/ice-10972-tait.rs deleted file mode 100644 index 11ddbfc3a04b9..0000000000000 --- a/src/tools/clippy/tests/ui/crashes/ice-10972-tait.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ check-pass -// ICE: #10972 -// asked to assemble constituent types of unexpected type: Binder(Foo, []) -#![feature(type_alias_impl_trait)] - -use std::fmt::Debug; -type Foo = impl Debug; -const FOO2: Foo = 22_u32; - -pub fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.fixed b/src/tools/clippy/tests/ui/crashes/ice-11230.fixed index 181e1ebbe5a3a..c49a419f0d4ba 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11230.fixed +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.fixed @@ -12,7 +12,7 @@ fn main() { // needless_collect trait Helper<'a>: Iterator {} +// Should not be linted because we have no idea whether the iterator has side effects fn x(w: &mut dyn for<'a> Helper<'a>) { - w.next().is_none(); - //~^ needless_collect + w.collect::>().is_empty(); } diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.rs b/src/tools/clippy/tests/ui/crashes/ice-11230.rs index fb05dc781bc0d..f66b7e961c889 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11230.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.rs @@ -12,7 +12,7 @@ fn main() { // needless_collect trait Helper<'a>: Iterator {} +// Should not be linted because we have no idea whether the iterator has side effects fn x(w: &mut dyn for<'a> Helper<'a>) { w.collect::>().is_empty(); - //~^ needless_collect } diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.stderr b/src/tools/clippy/tests/ui/crashes/ice-11230.stderr index b4a3f67081aec..91d59121ac4ed 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11230.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.stderr @@ -7,14 +7,5 @@ LL | for v in A.iter() {} = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::explicit_iter_loop)]` -error: avoid using `collect()` when not needed - --> tests/ui/crashes/ice-11230.rs:16:7 - | -LL | w.collect::>().is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` - | - = note: `-D clippy::needless-collect` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_collect)]` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/crashes/ice-12491.fixed b/src/tools/clippy/tests/ui/crashes/ice-12491.fixed index ab9c61463e92d..8a31eb9550b23 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-12491.fixed +++ b/src/tools/clippy/tests/ui/crashes/ice-12491.fixed @@ -3,6 +3,6 @@ fn main() { if (true) { // anything一些中文 - //~^^ needless_return + //~^ needless_return } } diff --git a/src/tools/clippy/tests/ui/crashes/ice-12491.rs b/src/tools/clippy/tests/ui/crashes/ice-12491.rs index b774bd3788a77..013aadadce516 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-12491.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-12491.rs @@ -4,6 +4,6 @@ fn main() { if (true) { // anything一些中文 return; - //~^^ needless_return + //~^ needless_return } } diff --git a/src/tools/clippy/tests/ui/crashes/ice-12491.stderr b/src/tools/clippy/tests/ui/crashes/ice-12491.stderr index 7cc418898e881..4b77299dd5e6c 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-12491.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-12491.stderr @@ -1,10 +1,8 @@ error: unneeded `return` statement - --> tests/ui/crashes/ice-12491.rs:5:24 + --> tests/ui/crashes/ice-12491.rs:6:9 | -LL | // anything一些中文 - | ____________________________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | = note: `-D clippy::needless-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_return)]` diff --git a/src/tools/clippy/tests/ui/crashes/ice-12979.1.fixed b/src/tools/clippy/tests/ui/crashes/ice-12979.1.fixed new file mode 100644 index 0000000000000..e68f1c20a8e7c --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12979.1.fixed @@ -0,0 +1,2 @@ +#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr +const FOO: u8 = 0; diff --git a/src/tools/clippy/tests/ui/crashes/ice-12979.2.fixed b/src/tools/clippy/tests/ui/crashes/ice-12979.2.fixed new file mode 100644 index 0000000000000..e89fa636d4b24 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12979.2.fixed @@ -0,0 +1,3 @@ +#![deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr + +const FOO: u8 = 0; diff --git a/src/tools/clippy/tests/ui/crashes/ice-12979.rs b/src/tools/clippy/tests/ui/crashes/ice-12979.rs new file mode 100644 index 0000000000000..a2787291d9e15 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12979.rs @@ -0,0 +1,3 @@ +#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr + +const FOO: u8 = 0; diff --git a/src/tools/clippy/tests/ui/crashes/ice-12979.stderr b/src/tools/clippy/tests/ui/crashes/ice-12979.stderr new file mode 100644 index 0000000000000..5e76081616461 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12979.stderr @@ -0,0 +1,19 @@ +error: empty line after outer attribute + --> tests/ui/crashes/ice-12979.rs:1:1 + | +LL | / #[deny(clippy::declare_interior_mutable_const)] +LL | | + | |_^ +LL | const FOO: u8 = 0; + | --------- the attribute applies to this constant item + | + = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]` + = help: if the empty line is unintentional, remove it +help: if the attribute should apply to the crate use an inner attribute + | +LL | #![deny(clippy::declare_interior_mutable_const)] + | + + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-13544-original.rs b/src/tools/clippy/tests/ui/crashes/ice-13544-original.rs new file mode 100644 index 0000000000000..1709eaeb365e8 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-13544-original.rs @@ -0,0 +1,45 @@ +//@ check-pass +#![warn(clippy::significant_drop_tightening)] + +use std::mem::ManuallyDrop; +use std::ops::{Deref, DerefMut}; + +trait Scopable: Sized { + type SubType: Scopable; +} + +struct Subtree(ManuallyDrop>>); + +impl Drop for Subtree { + fn drop(&mut self) { + // SAFETY: The field cannot be used after we drop + unsafe { ManuallyDrop::drop(&mut self.0) } + } +} + +impl Deref for Subtree { + type Target = Tree; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Subtree { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +enum Tree { + Group(Vec>), + Subtree(Subtree), + Leaf(T), +} + +impl Tree { + fn foo(self) -> Self { + self + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-13544-reduced.rs b/src/tools/clippy/tests/ui/crashes/ice-13544-reduced.rs new file mode 100644 index 0000000000000..9266e71f5d0e0 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-13544-reduced.rs @@ -0,0 +1,16 @@ +//@ check-pass +#![warn(clippy::significant_drop_tightening)] +#![allow(unused, clippy::no_effect)] + +use std::marker::PhantomData; + +trait Trait { + type Assoc: Trait; +} +struct S(*const S, PhantomData); + +fn f(x: &mut S) { + &mut x.0; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-1588.rs b/src/tools/clippy/tests/ui/crashes/ice-1588.rs index 3ccd33052cd6c..422c29b66cf68 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-1588.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-1588.rs @@ -1,6 +1,6 @@ //@ check-pass -#![allow(clippy::all)] +#![expect(clippy::no_effect)] // Test for https://github.com/rust-lang/rust-clippy/issues/1588 diff --git a/src/tools/clippy/tests/ui/crashes/ice-1782.rs b/src/tools/clippy/tests/ui/crashes/ice-1782.rs index fefdc405cce2a..776b0a93bf7c7 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-1782.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-1782.rs @@ -1,6 +1,6 @@ //@ check-pass -#![allow(dead_code, unused_variables)] +#![allow(dead_code, unused_variables, invalid_null_arguments, unnecessary_transmutes)] #![allow(clippy::unnecessary_cast, clippy::missing_transmute_annotations)] /// Should not trigger an ICE in `SpanlessEq` / `consts::constant` diff --git a/src/tools/clippy/tests/ui/crashes/ice-1969.rs b/src/tools/clippy/tests/ui/crashes/ice-1969.rs index 34ff725d71176..813972a046f75 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-1969.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-1969.rs @@ -1,7 +1,5 @@ //@ check-pass -#![allow(clippy::all)] - // Test for https://github.com/rust-lang/rust-clippy/issues/1969 fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3462.rs b/src/tools/clippy/tests/ui/crashes/ice-3462.rs index 4ce484917ae2f..e06eccdf142ca 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-3462.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-3462.rs @@ -1,8 +1,6 @@ //@ check-pass -#![warn(clippy::all)] -#![allow(clippy::disallowed_names, clippy::equatable_if_let, clippy::needless_if)] -#![allow(unused)] +#![expect(clippy::disallowed_names)] // Test for https://github.com/rust-lang/rust-clippy/issues/3462 diff --git a/src/tools/clippy/tests/ui/crashes/ice-6840.rs b/src/tools/clippy/tests/ui/crashes/ice-6840.rs index 94481f2489987..f30d83e1eeecd 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6840.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-6840.rs @@ -2,6 +2,7 @@ //! This is a reproducer for the ICE 6840: https://github.com/rust-lang/rust-clippy/issues/6840. //! The ICE is caused by `TyCtxt::layout_of` and `is_normalizable` not being strict enough #![allow(dead_code)] +#![deny(clippy::zero_sized_map_values)] // For ICE 14822 use std::collections::HashMap; pub trait Rule { diff --git a/src/tools/clippy/tests/ui/crashes/ice-700.rs b/src/tools/clippy/tests/ui/crashes/ice-700.rs index aa3bf493c201c..ca82f638b0bba 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-700.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-700.rs @@ -1,7 +1,5 @@ //@ check-pass -#![deny(clippy::all)] - // Test for https://github.com/rust-lang/rust-clippy/issues/700 fn core() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-7012.rs b/src/tools/clippy/tests/ui/crashes/ice-7012.rs index d76995adadf1a..48c1c5a98d40a 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-7012.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-7012.rs @@ -1,6 +1,6 @@ //@ check-pass -#![allow(clippy::all)] +#![expect(clippy::single_match)] enum _MyOption { None, diff --git a/src/tools/clippy/tests/ui/crashes/ice-7423.rs b/src/tools/clippy/tests/ui/crashes/ice-7423.rs index a03981842fcc4..fbf5d6520ed64 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-7423.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-7423.rs @@ -6,7 +6,7 @@ pub trait Trait { impl Trait for usize { fn f() { - extern "C" { + unsafe extern "C" { fn g() -> usize; } } diff --git a/src/tools/clippy/tests/ui/crashes/ice-9445.rs b/src/tools/clippy/tests/ui/crashes/ice-9445.rs deleted file mode 100644 index 232b8e4a7959d..0000000000000 --- a/src/tools/clippy/tests/ui/crashes/ice-9445.rs +++ /dev/null @@ -1,4 +0,0 @@ -const UNINIT: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(); -//~^ declare_interior_mutable_const - -fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-9445.stderr b/src/tools/clippy/tests/ui/crashes/ice-9445.stderr deleted file mode 100644 index 76689cd6f5c22..0000000000000 --- a/src/tools/clippy/tests/ui/crashes/ice-9445.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/crashes/ice-9445.rs:1:1 - | -LL | const UNINIT: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: aborting due to 1 previous error - diff --git a/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs b/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs index cb4685e78e22f..0aa55cd0fac61 100644 --- a/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs +++ b/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs @@ -1,10 +1,7 @@ //@ check-pass -#![deny(clippy::all)] - // Test for https://github.com/rust-lang/rust-clippy/issues/1336 -#[allow(dead_code)] struct Foo; impl Iterator for Foo { diff --git a/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs b/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs index 58ee751948b52..8a27b3c6d47db 100644 --- a/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs +++ b/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs @@ -1,6 +1,5 @@ //@ check-pass -#![allow(clippy::comparison_chain)] #![deny(clippy::if_same_then_else)] // Test for https://github.com/rust-lang/rust-clippy/issues/2426 diff --git a/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.fixed b/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.fixed new file mode 100644 index 0000000000000..9c85c4b846489 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.fixed @@ -0,0 +1,13 @@ +//@compile-flags: -Z validate-mir +#![warn(clippy::missing_const_for_fn)] + +static BLOCK_FN_DEF: fn(usize) -> usize = { + //~v missing_const_for_fn + const fn foo(a: usize) -> usize { + a + 10 + } + foo +}; +struct X; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.rs b/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.rs new file mode 100644 index 0000000000000..6519be61256e6 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.rs @@ -0,0 +1,13 @@ +//@compile-flags: -Z validate-mir +#![warn(clippy::missing_const_for_fn)] + +static BLOCK_FN_DEF: fn(usize) -> usize = { + //~v missing_const_for_fn + fn foo(a: usize) -> usize { + a + 10 + } + foo +}; +struct X; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.stderr b/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.stderr new file mode 100644 index 0000000000000..a407376d0b9d9 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/missing_const_for_fn_14774.stderr @@ -0,0 +1,17 @@ +error: this could be a `const fn` + --> tests/ui/crashes/missing_const_for_fn_14774.rs:6:5 + | +LL | / fn foo(a: usize) -> usize { +LL | | a + 10 +LL | | } + | |_____^ + | + = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_const_for_fn)]` +help: make the function `const` + | +LL | const fn foo(a: usize) -> usize { + | +++++ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs b/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs index 68e39531682a0..1b3b0290d909e 100644 --- a/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs +++ b/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs @@ -1,6 +1,5 @@ //@ check-pass -#[deny(clippy::all)] #[derive(Debug)] pub enum Error { Type(&'static str), diff --git a/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr b/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr index 2847957000692..b4fb1222539a2 100644 --- a/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr +++ b/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr @@ -2,7 +2,7 @@ error: this argument is passed by value, but not consumed in the function body --> tests/ui/crashes/needless_pass_by_value-w-late-bound.rs:7:12 | LL | fn test(x: Foo<'_>) {} - | ^^^^^^^ help: consider taking a reference instead: `&Foo<'_>` + | ^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/crashes/needless_pass_by_value-w-late-bound.rs:5:1 @@ -11,6 +11,10 @@ LL | struct Foo<'a>(&'a [(); 100]); | ^^^^^^^^^^^^^^ = note: `-D clippy::needless-pass-by-value` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_value)]` +help: consider taking a reference instead + | +LL | fn test(x: &Foo<'_>) {} + | + error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.fixed b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.fixed index e09a913ef06cd..9d977e9eddc84 100644 --- a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.fixed +++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.fixed @@ -3,7 +3,6 @@ use core::panic::PanicInfo; -#[warn(clippy::all)] pub fn main() { let mut a = 42; let mut b = 1337; diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs index 66ca97690c175..0967efe2ed8dc 100644 --- a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs +++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs @@ -3,7 +3,6 @@ use core::panic::PanicInfo; -#[warn(clippy::all)] pub fn main() { let mut a = 42; let mut b = 1337; diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr index 3e37bd95ef340..e16b53b51a810 100644 --- a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr +++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr @@ -1,5 +1,5 @@ error: this looks like you are trying to swap `a` and `b` - --> tests/ui/crate_level_checks/no_std_swap.rs:11:5 + --> tests/ui/crate_level_checks/no_std_swap.rs:10:5 | LL | / a = b; ... | @@ -7,8 +7,7 @@ LL | | b = a; | |_________^ help: try: `core::mem::swap(&mut a, &mut b)` | = note: or maybe you should use `core::mem::replace`? - = note: `-D clippy::almost-swapped` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::almost_swapped)]` + = note: `#[deny(clippy::almost_swapped)]` on by default error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed index fd1a0d8934b3c..3b9dee81898ab 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed @@ -1,5 +1,10 @@ +#![allow( + clippy::no_effect, + clippy::uninlined_format_args, + clippy::unit_arg, + clippy::unnecessary_operation +)] #![warn(clippy::dbg_macro)] -#![allow(clippy::unnecessary_operation, clippy::no_effect, clippy::unit_arg)] fn foo(n: u32) -> u32 { if let Some(n) = n.checked_sub(4) { n } else { n } diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs index c96e2c7251c29..1dbbc6fe98456 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs @@ -1,5 +1,10 @@ +#![allow( + clippy::no_effect, + clippy::uninlined_format_args, + clippy::unit_arg, + clippy::unnecessary_operation +)] #![warn(clippy::dbg_macro)] -#![allow(clippy::unnecessary_operation, clippy::no_effect, clippy::unit_arg)] fn foo(n: u32) -> u32 { if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr index cd6dce584a2fa..f1412023cc897 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr @@ -1,5 +1,5 @@ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:5:22 + --> tests/ui/dbg_macro/dbg_macro.rs:10:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + if let Some(n) = n.checked_sub(4) { n } else { n } | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:11:8 + --> tests/ui/dbg_macro/dbg_macro.rs:16:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + if n <= 1 { | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:14:9 + --> tests/ui/dbg_macro/dbg_macro.rs:19:9 | LL | dbg!(1) | ^^^^^^^ @@ -37,7 +37,7 @@ LL + 1 | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:17:9 + --> tests/ui/dbg_macro/dbg_macro.rs:22:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + n * factorial(n - 1) | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:23:5 + --> tests/ui/dbg_macro/dbg_macro.rs:28:5 | LL | dbg!(42); | ^^^^^^^^ @@ -61,7 +61,7 @@ LL + 42; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:26:14 + --> tests/ui/dbg_macro/dbg_macro.rs:31:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + foo(3) + factorial(4); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:29:5 + --> tests/ui/dbg_macro/dbg_macro.rs:34:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + (1, 2, 3, 4, 5); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:51:5 + --> tests/ui/dbg_macro/dbg_macro.rs:56:5 | LL | dbg!(); | ^^^^^^ @@ -96,7 +96,7 @@ LL - dbg!(); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:55:13 + --> tests/ui/dbg_macro/dbg_macro.rs:60:13 | LL | let _ = dbg!(); | ^^^^^^ @@ -108,7 +108,7 @@ LL + let _ = (); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:58:9 + --> tests/ui/dbg_macro/dbg_macro.rs:63:9 | LL | bar(dbg!()); | ^^^^^^ @@ -120,7 +120,7 @@ LL + bar(()); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:61:10 + --> tests/ui/dbg_macro/dbg_macro.rs:66:10 | LL | foo!(dbg!()); | ^^^^^^ @@ -132,7 +132,7 @@ LL + foo!(()); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:64:16 + --> tests/ui/dbg_macro/dbg_macro.rs:69:16 | LL | foo2!(foo!(dbg!())); | ^^^^^^ @@ -144,7 +144,7 @@ LL + foo2!(foo!(())); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:46:13 + --> tests/ui/dbg_macro/dbg_macro.rs:51:13 | LL | dbg!(); | ^^^^^^ @@ -159,7 +159,7 @@ LL - dbg!(); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:87:9 + --> tests/ui/dbg_macro/dbg_macro.rs:92:9 | LL | dbg!(2); | ^^^^^^^ @@ -171,7 +171,7 @@ LL + 2; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:94:5 + --> tests/ui/dbg_macro/dbg_macro.rs:99:5 | LL | dbg!(1); | ^^^^^^^ @@ -183,7 +183,7 @@ LL + 1; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:100:5 + --> tests/ui/dbg_macro/dbg_macro.rs:105:5 | LL | dbg!(1); | ^^^^^^^ @@ -195,7 +195,7 @@ LL + 1; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:107:9 + --> tests/ui/dbg_macro/dbg_macro.rs:112:9 | LL | dbg!(1); | ^^^^^^^ @@ -207,7 +207,7 @@ LL + 1; | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:114:31 + --> tests/ui/dbg_macro/dbg_macro.rs:119:31 | LL | println!("dbg: {:?}", dbg!(s)); | ^^^^^^^ @@ -219,7 +219,7 @@ LL + println!("dbg: {:?}", s); | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:117:22 + --> tests/ui/dbg_macro/dbg_macro.rs:122:22 | LL | print!("{}", dbg!(s)); | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs index 1a5119651b539..96b35c9a20c4c 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: overlapping suggestions //@error-in-other-file: #![warn(clippy::dbg_macro)] diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs new file mode 100644 index 0000000000000..c65df275038dc --- /dev/null +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs @@ -0,0 +1,200 @@ +#![deny(clippy::declare_interior_mutable_const)] +#![allow(clippy::missing_const_for_thread_local)] + +use core::cell::{Cell, RefCell, UnsafeCell}; +use core::mem::{ManuallyDrop, MaybeUninit}; +use core::ptr; +use core::sync::atomic::AtomicUsize; + +fn main() {} + +const _: Cell = Cell::new(0); +const UNSAFE_CELL: UnsafeCell = UnsafeCell::new(0); //~ declare_interior_mutable_const +const REF_CELL: RefCell = RefCell::new(0); //~ declare_interior_mutable_const +const CELL: Cell = Cell::new(0); //~ declare_interior_mutable_const + +// Constants can't contain pointers or references to type with interior mutability. +const fn make_ptr() -> *const Cell { + ptr::null() +} +const PTR: *const Cell = make_ptr(); + +const fn casted_to_cell_ptr() -> *const Cell { + const VALUE: u32 = 0; + &VALUE as *const _ as *const Cell +} +const TRANSMUTED_PTR: *const Cell = casted_to_cell_ptr(); + +const CELL_TUPLE: (bool, Cell) = (true, Cell::new(0)); //~ declare_interior_mutable_const +const CELL_ARRAY: [Cell; 2] = [Cell::new(0), Cell::new(0)]; //~ declare_interior_mutable_const + +const UNINIT_CELL: MaybeUninit> = MaybeUninit::uninit(); + +struct CellStruct { + x: u32, + cell: Cell, +} +//~v declare_interior_mutable_const +const CELL_STRUCT: CellStruct = CellStruct { + x: 0, + cell: Cell::new(0), +}; + +enum CellEnum { + Cell(Cell), +} +const CELL_ENUM: CellEnum = CellEnum::Cell(Cell::new(0)); //~ declare_interior_mutable_const + +const NONE_CELL: Option> = None; +const SOME_CELL: Option> = Some(Cell::new(0)); //~ declare_interior_mutable_const + +struct NestedCell([(Option>,); 1]); +const NONE_NESTED_CELL: NestedCell = NestedCell([(None,)]); +const SOME_NESTED_CELL: NestedCell = NestedCell([(Some(Cell::new(0)),)]); //~ declare_interior_mutable_const + +union UnionCell { + cell: ManuallyDrop>, + x: u32, +} +//~v declare_interior_mutable_const +const UNION_CELL: UnionCell = UnionCell { + cell: ManuallyDrop::new(Cell::new(0)), +}; +// Access to either union field is valid so we have to be conservative here. +const UNION_U32: UnionCell = UnionCell { x: 0 }; //~ declare_interior_mutable_const + +struct Assoc; +impl Assoc { + const SELF: Self = Self; + const CELL: Cell = Cell::new(0); //~ declare_interior_mutable_const +} + +struct AssocCell(Cell); +impl AssocCell { + const SELF: Self = Self(Cell::new(0)); //~ declare_interior_mutable_const + const NONE_SELF: Option = None; + const SOME_SELF: Option = Some(Self(Cell::new(0))); //~ declare_interior_mutable_const +} + +trait ConstDefault { + // May or may not be `Freeze` + const DEFAULT: Self; +} +impl ConstDefault for u32 { + const DEFAULT: Self = 0; +} +impl ConstDefault for Cell { + // Interior mutability is forced by the trait. + const DEFAULT: Self = Cell::new(T::DEFAULT); +} +impl ConstDefault for Option> { + // Could have been `None` + const DEFAULT: Self = Some(Cell::new(T::DEFAULT)); //~ declare_interior_mutable_const +} + +enum GenericEnumCell { + Cell(Cell), + Other(T), +} +impl ConstDefault for GenericEnumCell { + const DEFAULT: Self = Self::Cell(Cell::new(T::DEFAULT)); //~ declare_interior_mutable_const +} +impl GenericEnumCell { + const CELL: Self = Self::DEFAULT; //~ declare_interior_mutable_const + const CELL_BY_DEFAULT: Self = Self::Cell(Cell::DEFAULT); //~ declare_interior_mutable_const + const OTHER: Self = Self::Other(T::DEFAULT); + const FROM_OTHER: Self = Self::OTHER; +} + +enum GenericNestedEnumCell { + GenericEnumCell(GenericEnumCell), + EnumCell(GenericEnumCell), + Other(T), +} +impl GenericNestedEnumCell { + const GENERIC_OTHER: Self = Self::GenericEnumCell(GenericEnumCell::::FROM_OTHER); + const GENERIC_CELL: Self = Self::GenericEnumCell(GenericEnumCell::::CELL); //~ declare_interior_mutable_const + const ENUM_OTHER: Self = Self::EnumCell(GenericEnumCell::::FROM_OTHER); + const ENUM_CELL: Self = Self::EnumCell(GenericEnumCell::::CELL); //~ declare_interior_mutable_const +} + +trait CellTrait: ConstDefault + Sized { + // Must be non-`Freeze` due to the type + const CELL: Cell; //~ declare_interior_mutable_const + // May be non-`Freeze`, but may not be + const OPTION_CELL: Option>; + // May get redefined by the impl, but the default is non-`Freeze`. + const SOME_CELL: Option> = Some(Cell::new(Self::DEFAULT)); //~ declare_interior_mutable_const + // May get redefined by the impl, but the default is `Freeze`. + const NONE_CELL: Option> = None; +} + +trait CellWithAssoc { + type T; + const DEFAULT: Self::T; + // Must be non-`Freeze` due to the type + const CELL: Cell; //~ declare_interior_mutable_const + // May be non-`Freeze`, but may not be + const OPTION_CELL: Option>; + // May get redefined by the impl, but the default is non-`Freeze`. + const SOME_CELL: Option> = Some(Cell::new(Self::DEFAULT)); //~ declare_interior_mutable_const + // May get redefined by the impl, but the default is `Freeze`. + const NONE_CELL: Option> = None; +} + +impl CellWithAssoc for () { + type T = u32; + const DEFAULT: Self::T = 0; + const CELL: Cell = Cell::new(0); + const OPTION_CELL: Option> = None; +} + +trait WithAssoc { + type T; + const VALUE: Self::T; +} + +impl WithAssoc for u32 { + type T = Cell; + // The cell comes from the impl block, not the trait. + const VALUE: Self::T = Cell::new(0); //~ declare_interior_mutable_const +} + +trait WithLayeredAssoc { + type T: WithAssoc; + const VALUE: ::T; +} + +impl WithLayeredAssoc for u32 { + type T = u32; + // The cell comes from the impl block, not the trait. + const VALUE: ::T = Cell::new(0); //~ declare_interior_mutable_const +} + +trait WithGenericAssoc { + type T; + const VALUE: Self::T; +} + +impl WithGenericAssoc for u32 { + type T = Cell; + const VALUE: Self::T = Cell::new(0); //~ declare_interior_mutable_const +} + +trait WithGenericAssocCell { + type T; + const VALUE: Self::T>; +} + +impl WithGenericAssocCell for u32 { + type T = Option; + const VALUE: Self::T> = None; +} + +impl WithGenericAssocCell for i32 { + type T = Option; + const VALUE: Self::T> = Some(Cell::new(0)); //~ declare_interior_mutable_const +} + +thread_local!(static THREAD_LOCAL_CELL: Cell = const { Cell::new(0) }); +thread_local!(static THREAD_LOCAL_CELL2: Cell = Cell::new(0)); diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr new file mode 100644 index 0000000000000..9742c17486c5f --- /dev/null +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr @@ -0,0 +1,197 @@ +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:12:7 + | +LL | const UNSAFE_CELL: UnsafeCell = UnsafeCell::new(0); + | ^^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item +note: the lint level is defined here + --> tests/ui/declare_interior_mutable_const.rs:1:9 + | +LL | #![deny(clippy::declare_interior_mutable_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:13:7 + | +LL | const REF_CELL: RefCell = RefCell::new(0); + | ^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:14:7 + | +LL | const CELL: Cell = Cell::new(0); + | ^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:28:7 + | +LL | const CELL_TUPLE: (bool, Cell) = (true, Cell::new(0)); + | ^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:29:7 + | +LL | const CELL_ARRAY: [Cell; 2] = [Cell::new(0), Cell::new(0)]; + | ^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:38:7 + | +LL | const CELL_STRUCT: CellStruct = CellStruct { + | ^^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:46:7 + | +LL | const CELL_ENUM: CellEnum = CellEnum::Cell(Cell::new(0)); + | ^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:49:7 + | +LL | const SOME_CELL: Option> = Some(Cell::new(0)); + | ^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:53:7 + | +LL | const SOME_NESTED_CELL: NestedCell = NestedCell([(Some(Cell::new(0)),)]); + | ^^^^^^^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:60:7 + | +LL | const UNION_CELL: UnionCell = UnionCell { + | ^^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:64:7 + | +LL | const UNION_U32: UnionCell = UnionCell { x: 0 }; + | ^^^^^^^^^ + | + = help: did you mean to make this a `thread_local!` item + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:69:11 + | +LL | const CELL: Cell = Cell::new(0); + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:74:11 + | +LL | const SELF: Self = Self(Cell::new(0)); + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:76:11 + | +LL | const SOME_SELF: Option = Some(Self(Cell::new(0))); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:92:11 + | +LL | const DEFAULT: Self = Some(Cell::new(T::DEFAULT)); + | ^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:100:11 + | +LL | const DEFAULT: Self = Self::Cell(Cell::new(T::DEFAULT)); + | ^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:103:11 + | +LL | const CELL: Self = Self::DEFAULT; + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:104:11 + | +LL | const CELL_BY_DEFAULT: Self = Self::Cell(Cell::DEFAULT); + | ^^^^^^^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:116:11 + | +LL | const GENERIC_CELL: Self = Self::GenericEnumCell(GenericEnumCell::::CELL); + | ^^^^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:118:11 + | +LL | const ENUM_CELL: Self = Self::EnumCell(GenericEnumCell::::CELL); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:123:11 + | +LL | const CELL: Cell; + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:127:11 + | +LL | const SOME_CELL: Option> = Some(Cell::new(Self::DEFAULT)); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:136:11 + | +LL | const CELL: Cell; + | ^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:140:11 + | +LL | const SOME_CELL: Option> = Some(Cell::new(Self::DEFAULT)); + | ^^^^^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:160:11 + | +LL | const VALUE: Self::T = Cell::new(0); + | ^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:171:11 + | +LL | const VALUE: ::T = Cell::new(0); + | ^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:181:11 + | +LL | const VALUE: Self::T = Cell::new(0); + | ^^^^^ + +error: named constant with interior mutability + --> tests/ui/declare_interior_mutable_const.rs:196:11 + | +LL | const VALUE: Self::T> = Some(Cell::new(0)); + | ^^^^^ + +error: aborting due to 28 previous errors + diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs deleted file mode 100644 index c87468277fb31..0000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.rs +++ /dev/null @@ -1,135 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::cell::Cell; -use std::sync::atomic::AtomicUsize; - -enum OptionalCell { - Unfrozen(Cell), - Frozen, -} - -// a constant with enums should be linted only when the used variant is unfrozen (#3962). -const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); -//~^ declare_interior_mutable_const -const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - -const fn unfrozen_variant() -> OptionalCell { - OptionalCell::Unfrozen(Cell::new(false)) -} - -const fn frozen_variant() -> OptionalCell { - OptionalCell::Frozen -} - -const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); -//~^ declare_interior_mutable_const -const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant(); - -enum NestedInnermost { - Unfrozen(AtomicUsize), - Frozen, -} - -struct NestedInner { - inner: NestedInnermost, -} - -enum NestedOuter { - NestedInner(NestedInner), - NotNested(usize), -} - -struct NestedOutermost { - outer: NestedOuter, -} - -// a constant with enums should be linted according to its value, no matter how structs involve. -const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { - //~^ declare_interior_mutable_const - outer: NestedOuter::NestedInner(NestedInner { - inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), - }), -}; -const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost { - outer: NestedOuter::NestedInner(NestedInner { - inner: NestedInnermost::Frozen, - }), -}; - -trait AssocConsts { - // When there's no default value, lint it only according to its type. - // Further details are on the corresponding code (`NonCopyConst::check_trait_item`). - const TO_BE_UNFROZEN_VARIANT: OptionalCell; - //~^ declare_interior_mutable_const - const TO_BE_FROZEN_VARIANT: OptionalCell; - //~^ declare_interior_mutable_const - - // Lint default values accordingly. - const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - //~^ declare_interior_mutable_const - const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; -} - -// The lint doesn't trigger for an assoc constant in a trait impl with an unfrozen type even if it -// has enums. Further details are on the corresponding code in 'NonCopyConst::check_impl_item'. -impl AssocConsts for u64 { - const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; - - // even if this sets an unfrozen variant, the lint ignores it. - const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); -} - -// At first, I thought I'd need to check every patterns in `trait.rs`; but, what matters -// here are values; and I think substituted generics at definitions won't appear in MIR. -trait AssocTypes { - type ToBeUnfrozen; - - const TO_BE_UNFROZEN_VARIANT: Option; - const TO_BE_FROZEN_VARIANT: Option; -} - -impl AssocTypes for u64 { - type ToBeUnfrozen = AtomicUsize; - - const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); - //~^ declare_interior_mutable_const - const TO_BE_FROZEN_VARIANT: Option = None; -} - -// Use raw pointers since direct generics have a false negative at the type level. -enum BothOfCellAndGeneric { - Unfrozen(Cell<*const T>), - Generic(*const T), - Frozen(usize), -} - -impl BothOfCellAndGeneric { - const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - //~^ declare_interior_mutable_const - - // This is a false positive. The argument about this is on `is_value_unfrozen_raw` - const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - //~^ declare_interior_mutable_const - - const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); - - // This is what is likely to be a false negative when one tries to fix - // the `GENERIC_VARIANT` false positive. - const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); - //~^ declare_interior_mutable_const -} - -// associated types here is basically the same as the one above. -trait BothOfCellAndGenericWithAssocType { - type AssocType; - - const UNFROZEN_VARIANT: BothOfCellAndGeneric = - //~^ declare_interior_mutable_const - BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - //~^ declare_interior_mutable_const - const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr deleted file mode 100644 index 32839d14f0ed3..0000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr +++ /dev/null @@ -1,89 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:12:1 - | -LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:24:1 - | -LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:47:1 - | -LL | / const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { -LL | | -LL | | outer: NestedOuter::NestedInner(NestedInner { -LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), -LL | | }), -LL | | }; - | |__^ - | - = help: consider making this a static item - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:62:5 - | -LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:64:5 - | -LL | const TO_BE_FROZEN_VARIANT: OptionalCell; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:68:5 - | -LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:95:5 - | -LL | const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:108:5 - | -LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:112:5 - | -LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:119:5 - | -LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:127:5 - | -LL | / const UNFROZEN_VARIANT: BothOfCellAndGeneric = -LL | | -LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - | |____________________________________________________________________^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:130:5 - | -LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 12 previous errors - diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs deleted file mode 100644 index 7ce04a3f2c340..0000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs +++ /dev/null @@ -1,76 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::borrow::Cow; -use std::cell::Cell; -use std::fmt::Display; -use std::ptr; -use std::sync::Once; -use std::sync::atomic::AtomicUsize; - -const ATOMIC: AtomicUsize = AtomicUsize::new(5); -//~^ declare_interior_mutable_const -const CELL: Cell = Cell::new(6); -//~^ declare_interior_mutable_const -const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); -//~^ declare_interior_mutable_const - -macro_rules! declare_const { - ($name:ident: $ty:ty = $e:expr) => { - const $name: $ty = $e; - //~^ declare_interior_mutable_const - }; -} -declare_const!(_ONCE: Once = Once::new()); - -// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492. - -const INTEGER: u8 = 8; -const STRING: String = String::new(); -const STR: &str = "012345"; -const COW: Cow = Cow::Borrowed("abcdef"); -// note: a const item of Cow is used in the `postgres` package. - -const NO_ANN: &dyn Display = &70; - -static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); -// there should be no lints on the line above line - -mod issue_8493 { - use std::cell::Cell; - - thread_local! { - static _BAR: Cell = const { Cell::new(0) }; - } - - macro_rules! issue_8493 { - () => { - const _BAZ: Cell = Cell::new(0); - //~^ declare_interior_mutable_const - static _FOOBAR: () = { - thread_local! { - static _VAR: Cell = const { Cell::new(0) }; - } - }; - }; - } - - issue_8493!(); -} - -#[repr(C, align(8))] -struct NoAtomic(usize); -#[repr(C, align(8))] -struct WithAtomic(AtomicUsize); - -const fn with_non_null() -> *const WithAtomic { - const NO_ATOMIC: NoAtomic = NoAtomic(0); - (&NO_ATOMIC as *const NoAtomic).cast() -} -const WITH_ATOMIC: *const WithAtomic = with_non_null(); - -struct Generic(T); -impl Generic { - const RAW_POINTER: *const Cell = ptr::null(); -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr deleted file mode 100644 index 09299b290416d..0000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr +++ /dev/null @@ -1,50 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:10:1 - | -LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this a static item - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:12:1 - | -LL | const CELL: Cell = Cell::new(6); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:14:1 - | -LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider making this a static item - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:19:9 - | -LL | const $name: $ty = $e; - | ^^^^^^^^^^^^^^^^^^^^^^ -... -LL | declare_const!(_ONCE: Once = Once::new()); - | ----------------------------------------- in this macro invocation - | - = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:47:13 - | -LL | const _BAZ: Cell = Cell::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | issue_8493!(); - | ------------- in this macro invocation - | - = note: this error originates in the macro `issue_8493` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 5 previous errors - diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs deleted file mode 100644 index d3139be6859f3..0000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.rs +++ /dev/null @@ -1,162 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::borrow::Cow; -use std::cell::Cell; -use std::sync::atomic::AtomicUsize; - -macro_rules! declare_const { - ($name:ident: $ty:ty = $e:expr) => { - const $name: $ty = $e; - //~^ declare_interior_mutable_const - }; -} - -// a constant whose type is a concrete type should be linted at the definition site. -trait ConcreteTypes { - const ATOMIC: AtomicUsize; - //~^ declare_interior_mutable_const - const INTEGER: u64; - const STRING: String; - declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); -} - -impl ConcreteTypes for u64 { - const ATOMIC: AtomicUsize = AtomicUsize::new(9); - const INTEGER: u64 = 10; - const STRING: String = String::new(); -} - -// a helper trait used below -trait ConstDefault { - const DEFAULT: Self; -} - -// a constant whose type is a generic type should be linted at the implementation site. -trait GenericTypes { - const TO_REMAIN_GENERIC: T; - const TO_BE_CONCRETE: U; - - const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC; - declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC); -} - -impl GenericTypes for u64 { - const TO_REMAIN_GENERIC: T = T::DEFAULT; - const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); - //~^ declare_interior_mutable_const -} - -// a helper type used below -struct Wrapper(T); - -// a constant whose type is an associated type should be linted at the implementation site, too. -trait AssocTypes { - type ToBeFrozen; - type ToBeUnfrozen; - type ToBeGenericParam; - - const TO_BE_FROZEN: Self::ToBeFrozen; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen; - const WRAPPED_TO_BE_UNFROZEN: Wrapper; - // to ensure it can handle things when a generic type remains after normalization. - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper; -} - -impl AssocTypes for Vec { - type ToBeFrozen = u16; - type ToBeUnfrozen = AtomicUsize; - type ToBeGenericParam = T; - - const TO_BE_FROZEN: Self::ToBeFrozen = 12; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); - //~^ declare_interior_mutable_const - const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); - //~^ declare_interior_mutable_const - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper = Wrapper(T::DEFAULT); -} - -// a helper trait used below -trait AssocTypesHelper { - type NotToBeBounded; - type ToBeBounded; - - const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; -} - -// a constant whose type is an assoc type originated from a generic param bounded at the definition -// site should be linted at there. -trait AssocTypesFromGenericParam -where - T: AssocTypesHelper, -{ - const NOT_BOUNDED: T::NotToBeBounded; - const BOUNDED: T::ToBeBounded; - //~^ declare_interior_mutable_const -} - -impl AssocTypesFromGenericParam for u64 -where - T: AssocTypesHelper, -{ - // an associated type could remain unknown in a trait impl. - const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); -} - -// a constant whose type is `Self` should be linted at the implementation site as well. -// (`Option` requires `Sized` bound.) -trait SelfType: Sized { - const SELF: Self; - // this was the one in the original issue (#5050). - const WRAPPED_SELF: Option; -} - -impl SelfType for u64 { - const SELF: Self = 16; - const WRAPPED_SELF: Option = Some(20); -} - -impl SelfType for AtomicUsize { - // this (interior mutable `Self` const) exists in `parking_lot`. - // `const_trait_impl` will replace it in the future, hopefully. - const SELF: Self = AtomicUsize::new(17); - //~^ declare_interior_mutable_const - const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); - //~^ declare_interior_mutable_const -} - -// Even though a constant contains a generic type, if it also have an interior mutable type, -// it should be linted at the definition site. -trait BothOfCellAndGeneric { - const DIRECT: Cell; - //~^ declare_interior_mutable_const - const INDIRECT: Cell<*const T>; - //~^ declare_interior_mutable_const -} - -impl BothOfCellAndGeneric for u64 { - const DIRECT: Cell = Cell::new(T::DEFAULT); - //~^ declare_interior_mutable_const - const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); -} - -struct Local(T); - -// a constant in an inherent impl are essentially the same as a normal const item -// except there can be a generic or associated type. -impl Local -where - T: ConstDefault + AssocTypesHelper, -{ - const ATOMIC: AtomicUsize = AtomicUsize::new(18); - //~^ declare_interior_mutable_const - const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - - const GENERIC_TYPE: T = T::DEFAULT; - - const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - //~^ declare_interior_mutable_const -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr deleted file mode 100644 index b03dd7a084033..0000000000000 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr +++ /dev/null @@ -1,88 +0,0 @@ -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:16:5 - | -LL | const ATOMIC: AtomicUsize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:9:9 - | -LL | const $name: $ty = $e; - | ^^^^^^^^^^^^^^^^^^^^^^ -... -LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); - | ---------------------------------------------------------- in this macro invocation - | - = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:45:5 - | -LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:71:5 - | -LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:73:5 - | -LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:93:5 - | -LL | const BOUNDED: T::ToBeBounded; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:122:5 - | -LL | const SELF: Self = AtomicUsize::new(17); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:124:5 - | -LL | const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:131:5 - | -LL | const DIRECT: Cell; - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:133:5 - | -LL | const INDIRECT: Cell<*const T>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:138:5 - | -LL | const DIRECT: Cell = Cell::new(T::DEFAULT); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:151:5 - | -LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:158:5 - | -LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 13 previous errors - diff --git a/src/tools/clippy/tests/ui/def_id_nocore.rs b/src/tools/clippy/tests/ui/def_id_nocore.rs index 03f5ca31f5f07..40f40f7ea0960 100644 --- a/src/tools/clippy/tests/ui/def_id_nocore.rs +++ b/src/tools/clippy/tests/ui/def_id_nocore.rs @@ -5,7 +5,7 @@ #![allow(clippy::missing_safety_doc)] #[link(name = "c")] -extern "C" {} +unsafe extern "C" {} #[lang = "sized"] pub trait Sized {} diff --git a/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed b/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed index fa4d55177823e..1ca9be0ceddc7 100644 --- a/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed +++ b/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed @@ -161,3 +161,17 @@ fn main() { let _ = ::default(); } + +fn issue12654() { + #[derive(Default)] + struct G; + + fn f(_g: G) {} + + f(<_>::default()); + f(G); + //~^ default_constructed_unit_structs + + // No lint because `as Default` hides the singleton + f(::default()); +} diff --git a/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs b/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs index 291cd89da0b79..99eb8913fc3ce 100644 --- a/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs +++ b/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs @@ -161,3 +161,17 @@ fn main() { let _ = ::default(); } + +fn issue12654() { + #[derive(Default)] + struct G; + + fn f(_g: G) {} + + f(<_>::default()); + f(::default()); + //~^ default_constructed_unit_structs + + // No lint because `as Default` hides the singleton + f(::default()); +} diff --git a/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr b/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr index 6d4e1bdc2cc81..97fad792e4f7a 100644 --- a/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr +++ b/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr @@ -1,41 +1,65 @@ error: use of `default` to create a unit struct - --> tests/ui/default_constructed_unit_structs.rs:11:13 + --> tests/ui/default_constructed_unit_structs.rs:11:9 | LL | Self::default() - | ^^^^^^^^^^^ help: remove this call to `default` + | ^^^^----------- + | | + | help: remove this call to `default` | = note: `-D clippy::default-constructed-unit-structs` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::default_constructed_unit_structs)]` error: use of `default` to create a unit struct - --> tests/ui/default_constructed_unit_structs.rs:54:31 + --> tests/ui/default_constructed_unit_structs.rs:54:20 | LL | inner: PhantomData::default(), - | ^^^^^^^^^^^ help: remove this call to `default` + | ^^^^^^^^^^^----------- + | | + | help: remove this call to `default` error: use of `default` to create a unit struct - --> tests/ui/default_constructed_unit_structs.rs:128:33 + --> tests/ui/default_constructed_unit_structs.rs:128:13 | LL | let _ = PhantomData::::default(); - | ^^^^^^^^^^^ help: remove this call to `default` + | ^^^^^^^^^^^^^^^^^^^^----------- + | | + | help: remove this call to `default` error: use of `default` to create a unit struct - --> tests/ui/default_constructed_unit_structs.rs:130:42 + --> tests/ui/default_constructed_unit_structs.rs:130:31 | LL | let _: PhantomData = PhantomData::default(); - | ^^^^^^^^^^^ help: remove this call to `default` + | ^^^^^^^^^^^----------- + | | + | help: remove this call to `default` error: use of `default` to create a unit struct - --> tests/ui/default_constructed_unit_structs.rs:132:55 + --> tests/ui/default_constructed_unit_structs.rs:132:31 | LL | let _: PhantomData = std::marker::PhantomData::default(); - | ^^^^^^^^^^^ help: remove this call to `default` + | ^^^^^^^^^^^^^^^^^^^^^^^^----------- + | | + | help: remove this call to `default` error: use of `default` to create a unit struct - --> tests/ui/default_constructed_unit_structs.rs:134:23 + --> tests/ui/default_constructed_unit_structs.rs:134:13 | LL | let _ = UnitStruct::default(); - | ^^^^^^^^^^^ help: remove this call to `default` + | ^^^^^^^^^^----------- + | | + | help: remove this call to `default` -error: aborting due to 6 previous errors +error: use of `default` to create a unit struct + --> tests/ui/default_constructed_unit_structs.rs:172:7 + | +LL | f(::default()); + | ^^^^^^^^^^^^^^ + | +help: remove this call to `default` + | +LL - f(::default()); +LL + f(G); + | + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs index 35646e1c23919..6b69bdd29ceac 100644 --- a/src/tools/clippy/tests/ui/deprecated.rs +++ b/src/tools/clippy/tests/ui/deprecated.rs @@ -2,19 +2,20 @@ // Use that command to update this file and do not edit by hand. // Manual edits will be overwritten. -#![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq` +#![warn(clippy::assign_ops)] //~ ERROR: lint `clippy::assign_ops` #![warn(clippy::extend_from_slice)] //~ ERROR: lint `clippy::extend_from_slice` -#![warn(clippy::range_step_by_zero)] //~ ERROR: lint `clippy::range_step_by_zero` -#![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice` -#![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice` +#![warn(clippy::match_on_vec_items)] //~ ERROR: lint `clippy::match_on_vec_items` #![warn(clippy::misaligned_transmute)] //~ ERROR: lint `clippy::misaligned_transmute` -#![warn(clippy::assign_ops)] //~ ERROR: lint `clippy::assign_ops` +#![warn(clippy::option_map_or_err_ok)] //~ ERROR: lint `clippy::option_map_or_err_ok` +#![warn(clippy::pub_enum_variant_names)] //~ ERROR: lint `clippy::pub_enum_variant_names` +#![warn(clippy::range_step_by_zero)] //~ ERROR: lint `clippy::range_step_by_zero` +#![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro` +#![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts` +#![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq` #![warn(clippy::unsafe_vector_initialization)] //~ ERROR: lint `clippy::unsafe_vector_initialization` +#![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice` +#![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice` #![warn(clippy::unused_collect)] //~ ERROR: lint `clippy::unused_collect` -#![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts` -#![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro` -#![warn(clippy::pub_enum_variant_names)] //~ ERROR: lint `clippy::pub_enum_variant_names` #![warn(clippy::wrong_pub_self_convention)] //~ ERROR: lint `clippy::wrong_pub_self_convention` -#![warn(clippy::option_map_or_err_ok)] //~ ERROR: lint `clippy::option_map_or_err_ok` fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr index d7be1e583b08b..07e59d33d608b 100644 --- a/src/tools/clippy/tests/ui/deprecated.stderr +++ b/src/tools/clippy/tests/ui/deprecated.stderr @@ -1,8 +1,8 @@ -error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can +error: lint `clippy::assign_ops` has been removed: compound operators are harmless and linting on them is not in scope for clippy --> tests/ui/deprecated.rs:5:9 | -LL | #![warn(clippy::should_assert_eq)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::assign_ops)] + | ^^^^^^^^^^^^^^^^^^ | = note: `-D renamed-and-removed-lints` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` @@ -13,77 +13,83 @@ error: lint `clippy::extend_from_slice` has been removed: `Vec::extend_from_slic LL | #![warn(clippy::extend_from_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::range_step_by_zero` has been removed: `Iterator::step_by(0)` now panics and is no longer an infinite iterator +error: lint `clippy::match_on_vec_items` has been removed: `clippy::indexing_slicing` covers indexing and slicing on `Vec<_>` --> tests/ui/deprecated.rs:7:9 | -LL | #![warn(clippy::range_step_by_zero)] +LL | #![warn(clippy::match_on_vec_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable +error: lint `clippy::misaligned_transmute` has been removed: split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr` --> tests/ui/deprecated.rs:8:9 | -LL | #![warn(clippy::unstable_as_slice)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::misaligned_transmute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable +error: lint `clippy::option_map_or_err_ok` has been removed: `clippy::manual_ok_or` covers this case --> tests/ui/deprecated.rs:9:9 | -LL | #![warn(clippy::unstable_as_mut_slice)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::option_map_or_err_ok)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::misaligned_transmute` has been removed: split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr` +error: lint `clippy::pub_enum_variant_names` has been removed: `clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config --> tests/ui/deprecated.rs:10:9 | -LL | #![warn(clippy::misaligned_transmute)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::pub_enum_variant_names)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::assign_ops` has been removed: compound operators are harmless and linting on them is not in scope for clippy +error: lint `clippy::range_step_by_zero` has been removed: `Iterator::step_by(0)` now panics and is no longer an infinite iterator --> tests/ui/deprecated.rs:11:9 | -LL | #![warn(clippy::assign_ops)] - | ^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::range_step_by_zero)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower +error: lint `clippy::regex_macro` has been removed: the `regex!` macro was removed from the regex crate in 2018 --> tests/ui/deprecated.rs:12:9 | -LL | #![warn(clippy::unsafe_vector_initialization)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::regex_macro)] + | ^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]` +error: lint `clippy::replace_consts` has been removed: `min_value` and `max_value` are now deprecated --> tests/ui/deprecated.rs:13:9 | -LL | #![warn(clippy::unused_collect)] +LL | #![warn(clippy::replace_consts)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::replace_consts` has been removed: `min_value` and `max_value` are now deprecated +error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can --> tests/ui/deprecated.rs:14:9 | -LL | #![warn(clippy::replace_consts)] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::should_assert_eq)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::regex_macro` has been removed: the `regex!` macro was removed from the regex crate in 2018 +error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower --> tests/ui/deprecated.rs:15:9 | -LL | #![warn(clippy::regex_macro)] - | ^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::unsafe_vector_initialization)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::pub_enum_variant_names` has been removed: `clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config +error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable --> tests/ui/deprecated.rs:16:9 | -LL | #![warn(clippy::pub_enum_variant_names)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::unstable_as_mut_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config +error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable --> tests/ui/deprecated.rs:17:9 | -LL | #![warn(clippy::wrong_pub_self_convention)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::option_map_or_err_ok` has been removed: `clippy::manual_ok_or` covers this case +error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]` --> tests/ui/deprecated.rs:18:9 | -LL | #![warn(clippy::option_map_or_err_ok)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![warn(clippy::unused_collect)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config + --> tests/ui/deprecated.rs:19:9 + | +LL | #![warn(clippy::wrong_pub_self_convention)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/derive.rs b/src/tools/clippy/tests/ui/derive.rs index 707a9ff058576..e334203c7b2a7 100644 --- a/src/tools/clippy/tests/ui/derive.rs +++ b/src/tools/clippy/tests/ui/derive.rs @@ -6,6 +6,8 @@ dead_code )] #![warn(clippy::expl_impl_clone_on_copy)] +#![expect(incomplete_features)] // `unsafe_fields` is incomplete for the time being +#![feature(unsafe_fields)] // `clone()` cannot be derived automatically on unsafe fields #[derive(Copy)] @@ -113,4 +115,19 @@ impl Clone for Packed { } } +fn issue14558() { + pub struct Valid { + pub unsafe actual: (), + } + + unsafe impl Copy for Valid {} + + impl Clone for Valid { + #[inline] + fn clone(&self) -> Self { + *self + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/derive.stderr b/src/tools/clippy/tests/ui/derive.stderr index 20278d4f4e4a4..9004ced6849e5 100644 --- a/src/tools/clippy/tests/ui/derive.stderr +++ b/src/tools/clippy/tests/ui/derive.stderr @@ -1,5 +1,5 @@ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:14:1 + --> tests/ui/derive.rs:16:1 | LL | / impl Clone for Qux { LL | | @@ -10,7 +10,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:14:1 + --> tests/ui/derive.rs:16:1 | LL | / impl Clone for Qux { LL | | @@ -23,7 +23,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:40:1 + --> tests/ui/derive.rs:42:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -34,7 +34,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:40:1 + --> tests/ui/derive.rs:42:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -45,7 +45,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:53:1 + --> tests/ui/derive.rs:55:1 | LL | / impl Clone for BigArray { LL | | @@ -56,7 +56,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:53:1 + --> tests/ui/derive.rs:55:1 | LL | / impl Clone for BigArray { LL | | @@ -67,7 +67,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:66:1 + --> tests/ui/derive.rs:68:1 | LL | / impl Clone for FnPtr { LL | | @@ -78,7 +78,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:66:1 + --> tests/ui/derive.rs:68:1 | LL | / impl Clone for FnPtr { LL | | @@ -89,7 +89,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:88:1 + --> tests/ui/derive.rs:90:1 | LL | / impl Clone for Generic2 { LL | | @@ -100,7 +100,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:88:1 + --> tests/ui/derive.rs:90:1 | LL | / impl Clone for Generic2 { LL | | 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 index 6bbe54eeaa6a0..76dca3c79470a 100644 --- a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.stderr +++ b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.stderr @@ -11,7 +11,6 @@ LL | impl PartialOrd for DeriveOrd { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::derive-ord-xor-partial-ord` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::derive_ord_xor_partial_ord)]` - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are deriving `Ord` but have implemented `PartialOrd` explicitly --> tests/ui/derive_ord_xor_partial_ord.rs:33:10 @@ -24,7 +23,6 @@ note: `PartialOrd` implemented here | LL | impl PartialOrd for DeriveOrdWithExplicitTypeVariable { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Ord` explicitly but have derived `PartialOrd` --> tests/ui/derive_ord_xor_partial_ord.rs:47:1 @@ -42,7 +40,6 @@ note: `PartialOrd` implemented here | LL | #[derive(PartialOrd, PartialEq, Eq)] | ^^^^^^^^^^ - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Ord` explicitly but have derived `PartialOrd` --> tests/ui/derive_ord_xor_partial_ord.rs:69:5 @@ -60,7 +57,6 @@ note: `PartialOrd` implemented here | LL | #[derive(PartialOrd, PartialEq, Eq)] | ^^^^^^^^^^ - = note: this error originates in the derive macro `PartialOrd` (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/derived_hash_with_manual_eq.stderr b/src/tools/clippy/tests/ui/derived_hash_with_manual_eq.stderr index 19a24e752810d..55740780c8a50 100644 --- a/src/tools/clippy/tests/ui/derived_hash_with_manual_eq.stderr +++ b/src/tools/clippy/tests/ui/derived_hash_with_manual_eq.stderr @@ -10,7 +10,6 @@ note: `PartialEq` implemented here LL | impl PartialEq for Bar { | ^^^^^^^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::derived_hash_with_manual_eq)]` on by default - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are deriving `Hash` but have implemented `PartialEq` explicitly --> tests/ui/derived_hash_with_manual_eq.rs:23:10 @@ -23,7 +22,6 @@ note: `PartialEq` implemented here | LL | impl PartialEq for Baz { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/diverging_sub_expression.stderr b/src/tools/clippy/tests/ui/diverging_sub_expression.stderr index 3e417aa39cd85..ba08eb4e5f4b5 100644 --- a/src/tools/clippy/tests/ui/diverging_sub_expression.stderr +++ b/src/tools/clippy/tests/ui/diverging_sub_expression.stderr @@ -36,8 +36,6 @@ error: sub-expression diverges | LL | _ => true || panic!("boo"), | ^^^^^^^^^^^^^ - | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: sub-expression diverges --> tests/ui/diverging_sub_expression.rs:52:29 diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index 5f2b697f88b02..8cf20d8b1a11c 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -1,4 +1,3 @@ - //! This file tests for the `DOC_MARKDOWN` lint. #![allow(dead_code, incomplete_features)] @@ -272,7 +271,7 @@ fn parenthesized_word() {} /// UXes fn plural_acronym_test() {} -extern "C" { +unsafe extern "C" { /// `foo()` //~^ doc_markdown fn in_extern(); diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index ed3925694c67e..5b6f2bd8330c5 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -1,4 +1,3 @@ - //! This file tests for the `DOC_MARKDOWN` lint. #![allow(dead_code, incomplete_features)] @@ -272,7 +271,7 @@ fn parenthesized_word() {} /// UXes fn plural_acronym_test() {} -extern "C" { +unsafe extern "C" { /// foo() //~^ doc_markdown fn in_extern(); diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr index d67da75a230ca..98c26e6bec2eb 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr @@ -1,5 +1,5 @@ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:9:9 + --> tests/ui/doc/doc-fixable.rs:8:9 | LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) | ^^^^^^^ @@ -13,7 +13,7 @@ LL + /// The `foo_bar` function does _nothing_. See also foo::bar. (note the dot | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:9:51 + --> tests/ui/doc/doc-fixable.rs:8:51 | LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) | ^^^^^^^^ @@ -25,7 +25,7 @@ LL + /// The foo_bar function does _nothing_. See also `foo::bar`. (note the dot | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:12:83 + --> tests/ui/doc/doc-fixable.rs:11:83 | LL | /// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun | ^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + /// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. B | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:15:13 + --> tests/ui/doc/doc-fixable.rs:14:13 | LL | /// Here be ::a::global:path, and _::another::global::path_. :: is not a path though. | ^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + /// Here be `::a::global:path`, and _::another::global::path_. :: is not a | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:15:36 + --> tests/ui/doc/doc-fixable.rs:14:36 | LL | /// Here be ::a::global:path, and _::another::global::path_. :: is not a path though. | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + /// Here be ::a::global:path, and _`::another::global::path`_. :: is not a | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:18:25 + --> tests/ui/doc/doc-fixable.rs:17:25 | LL | /// Import an item from ::awesome::global::blob:: (Intended postfix) | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + /// Import an item from `::awesome::global::blob::` (Intended postfix) | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:20:31 + --> tests/ui/doc/doc-fixable.rs:19:31 | LL | /// These are the options for ::Cat: (Intended trailing single colon, shouldn't be linted) | ^^^^^ @@ -85,7 +85,7 @@ LL + /// These are the options for `::Cat`: (Intended trailing single colon, sho | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:22:22 + --> tests/ui/doc/doc-fixable.rs:21:22 | LL | /// That's not code ~NotInCodeBlock~. | ^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL + /// That's not code ~`NotInCodeBlock`~. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:24:5 + --> tests/ui/doc/doc-fixable.rs:23:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:39:5 + --> tests/ui/doc/doc-fixable.rs:38:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:47:5 + --> tests/ui/doc/doc-fixable.rs:46:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:62:5 + --> tests/ui/doc/doc-fixable.rs:61:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:91:5 + --> tests/ui/doc/doc-fixable.rs:90:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:109:5 + --> tests/ui/doc/doc-fixable.rs:108:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:118:8 + --> tests/ui/doc/doc-fixable.rs:117:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + /// ## `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:122:7 + --> tests/ui/doc/doc-fixable.rs:121:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + /// # `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:125:22 + --> tests/ui/doc/doc-fixable.rs:124:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ @@ -205,7 +205,7 @@ LL + /// Not a title #897 `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:127:5 + --> tests/ui/doc/doc-fixable.rs:126:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:135:5 + --> tests/ui/doc/doc-fixable.rs:134:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:149:5 + --> tests/ui/doc/doc-fixable.rs:148:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:161:43 + --> tests/ui/doc/doc-fixable.rs:160:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -253,7 +253,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:166:5 + --> tests/ui/doc/doc-fixable.rs:165:5 | LL | And BarQuz too. | ^^^^^^ @@ -265,7 +265,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:167:1 + --> tests/ui/doc/doc-fixable.rs:166:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -277,7 +277,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:175:43 + --> tests/ui/doc/doc-fixable.rs:174:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -289,7 +289,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:180:5 + --> tests/ui/doc/doc-fixable.rs:179:5 | LL | And BarQuz too. | ^^^^^^ @@ -301,7 +301,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:181:1 + --> tests/ui/doc/doc-fixable.rs:180:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -313,7 +313,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:195:5 + --> tests/ui/doc/doc-fixable.rs:194:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -325,7 +325,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:215:22 + --> tests/ui/doc/doc-fixable.rs:214:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ @@ -337,7 +337,7 @@ LL + /// An iterator over `mycrate::Collection`'s values. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:240:34 + --> tests/ui/doc/doc-fixable.rs:239:34 | LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint | ^^^^^^^^^^^^^^^ @@ -349,7 +349,7 @@ LL + /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:264:22 + --> tests/ui/doc/doc-fixable.rs:263:22 | LL | /// There is no try (do() or do_not()). | ^^^^ @@ -361,7 +361,7 @@ LL + /// There is no try (`do()` or do_not()). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:264:30 + --> tests/ui/doc/doc-fixable.rs:263:30 | LL | /// There is no try (do() or do_not()). | ^^^^^^^^ @@ -373,7 +373,7 @@ LL + /// There is no try (do() or `do_not()`). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:269:5 + --> tests/ui/doc/doc-fixable.rs:268:5 | LL | /// ABes | ^^^^ @@ -385,7 +385,7 @@ LL + /// `ABes` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:276:9 + --> tests/ui/doc/doc-fixable.rs:275:9 | LL | /// foo() | ^^^^^ @@ -397,7 +397,7 @@ LL + /// `foo()` | error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> tests/ui/doc/doc-fixable.rs:281:5 + --> tests/ui/doc/doc-fixable.rs:280:5 | LL | /// https://github.com/rust-lang/rust-clippy/pull/12836 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `` diff --git a/src/tools/clippy/tests/ui/doc/doc_comment_double_space_linebreaks.fixed b/src/tools/clippy/tests/ui/doc/doc_comment_double_space_linebreaks.fixed new file mode 100644 index 0000000000000..6a616b2c7e180 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_comment_double_space_linebreaks.fixed @@ -0,0 +1,98 @@ +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +#![warn(clippy::doc_comment_double_space_linebreaks)] +#![allow(unused, clippy::empty_docs)] + +//~v doc_comment_double_space_linebreaks +//! Should warn on double space linebreaks\ +//! in file/module doc comment + +/// Should not warn on single-line doc comments +fn single_line() {} + +/// Should not warn on single-line doc comments +/// split across multiple lines +fn single_line_split() {} + +// Should not warn on normal comments + +// note: cargo fmt can remove double spaces from normal and block comments +// Should not warn on normal comments +// with double spaces at the end of a line + +#[doc = "This is a doc attribute, which should not be linted"] +fn normal_comment() { + /* + Should not warn on block comments + */ + + /* + Should not warn on block comments + with double space at the end of a line + */ +} + +//~v doc_comment_double_space_linebreaks +/// Should warn when doc comment uses double space\ +/// as a line-break, even when there are multiple\ +/// in a row +fn double_space_doc_comment() {} + +/// Should not warn when back-slash is used \ +/// as a line-break +fn back_slash_doc_comment() {} + +//~v doc_comment_double_space_linebreaks +/// 🌹 are 🟥\ +/// 🌷 are 🟦\ +/// 📎 is 😎\ +/// and so are 🫵\ +/// (hopefully no formatting weirdness linting this) +fn multi_byte_chars_tada() {} + +macro_rules! macro_that_makes_function { + () => { + /// Shouldn't lint on this! + /// (hopefully) + fn my_macro_created_function() {} + } +} + +macro_that_makes_function!(); + +// dont lint when its alone on a line +/// +fn alone() {} + +/// | First column | Second column | +/// | ------------ | ------------- | +/// | Not a line | break when | +/// | after a line | in a table | +fn table() {} + +/// ```text +/// It's also not a hard line break if +/// there's two spaces at the end of a +/// line in a block code. +/// ``` +fn codeblock() {} + +/// It's also not a hard line break `if +/// there's` two spaces in the middle of inline code. +fn inline() {} + +/// It's also not a hard line break [when]( +/// https://example.com) in a URL. +fn url() {} + +//~v doc_comment_double_space_linebreaks +/// here we mix\ +/// double spaces\ +/// and also\ +/// adding backslash\ +/// to some of them\ +/// to see how that looks +fn mixed() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_comment_double_space_linebreaks.rs b/src/tools/clippy/tests/ui/doc/doc_comment_double_space_linebreaks.rs new file mode 100644 index 0000000000000..e36cf7dea2363 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_comment_double_space_linebreaks.rs @@ -0,0 +1,98 @@ +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +#![warn(clippy::doc_comment_double_space_linebreaks)] +#![allow(unused, clippy::empty_docs)] + +//~v doc_comment_double_space_linebreaks +//! Should warn on double space linebreaks +//! in file/module doc comment + +/// Should not warn on single-line doc comments +fn single_line() {} + +/// Should not warn on single-line doc comments +/// split across multiple lines +fn single_line_split() {} + +// Should not warn on normal comments + +// note: cargo fmt can remove double spaces from normal and block comments +// Should not warn on normal comments +// with double spaces at the end of a line + +#[doc = "This is a doc attribute, which should not be linted"] +fn normal_comment() { + /* + Should not warn on block comments + */ + + /* + Should not warn on block comments + with double space at the end of a line + */ +} + +//~v doc_comment_double_space_linebreaks +/// Should warn when doc comment uses double space +/// as a line-break, even when there are multiple +/// in a row +fn double_space_doc_comment() {} + +/// Should not warn when back-slash is used \ +/// as a line-break +fn back_slash_doc_comment() {} + +//~v doc_comment_double_space_linebreaks +/// 🌹 are 🟥 +/// 🌷 are 🟦 +/// 📎 is 😎 +/// and so are 🫵 +/// (hopefully no formatting weirdness linting this) +fn multi_byte_chars_tada() {} + +macro_rules! macro_that_makes_function { + () => { + /// Shouldn't lint on this! + /// (hopefully) + fn my_macro_created_function() {} + } +} + +macro_that_makes_function!(); + +// dont lint when its alone on a line +/// +fn alone() {} + +/// | First column | Second column | +/// | ------------ | ------------- | +/// | Not a line | break when | +/// | after a line | in a table | +fn table() {} + +/// ```text +/// It's also not a hard line break if +/// there's two spaces at the end of a +/// line in a block code. +/// ``` +fn codeblock() {} + +/// It's also not a hard line break `if +/// there's` two spaces in the middle of inline code. +fn inline() {} + +/// It's also not a hard line break [when]( +/// https://example.com) in a URL. +fn url() {} + +//~v doc_comment_double_space_linebreaks +/// here we mix +/// double spaces\ +/// and also +/// adding backslash\ +/// to some of them +/// to see how that looks +fn mixed() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_comment_double_space_linebreaks.stderr b/src/tools/clippy/tests/ui/doc/doc_comment_double_space_linebreaks.stderr new file mode 100644 index 0000000000000..08c6956c3d003 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_comment_double_space_linebreaks.stderr @@ -0,0 +1,50 @@ +error: doc comment uses two spaces for a hard line break + --> tests/ui/doc/doc_comment_double_space_linebreaks.rs:8:43 + | +LL | //! Should warn on double space linebreaks + | ^^ + | + = help: replace this double space with a backslash: `\` + = note: `-D clippy::doc-comment-double-space-linebreaks` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_comment_double_space_linebreaks)]` + +error: doc comment uses two spaces for a hard line break + --> tests/ui/doc/doc_comment_double_space_linebreaks.rs:37:51 + | +LL | /// Should warn when doc comment uses double space + | ^^ +LL | /// as a line-break, even when there are multiple + | ^^ + | + = help: replace this double space with a backslash: `\` + +error: doc comment uses two spaces for a hard line break + --> tests/ui/doc/doc_comment_double_space_linebreaks.rs:47:12 + | +LL | /// 🌹 are 🟥 + | ^^ +LL | /// 🌷 are 🟦 + | ^^ +LL | /// 📎 is 😎 + | ^^ +LL | /// and so are 🫵 + | ^^ + | + = help: replace this double space with a backslash: `\` + +error: doc comment uses two spaces for a hard line break + --> tests/ui/doc/doc_comment_double_space_linebreaks.rs:90:16 + | +LL | /// here we mix + | ^^ +LL | /// double spaces\ +LL | /// and also + | ^^ +LL | /// adding backslash\ +LL | /// to some of them + | ^^ + | + = help: replace this double space with a backslash: `\` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/doc_unsafe.rs b/src/tools/clippy/tests/ui/doc_unsafe.rs index 1bdf01e4e22e9..7146fd7941ab0 100644 --- a/src/tools/clippy/tests/ui/doc_unsafe.rs +++ b/src/tools/clippy/tests/ui/doc_unsafe.rs @@ -103,7 +103,7 @@ macro_rules! very_unsafe { /// /// Please keep the seat belt fastened pub unsafe fn drive() { - whee() + unsafe { whee() } } }; } diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed b/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed index 17d0d71a88545..be31ee5fb4862 100644 --- a/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed @@ -52,31 +52,51 @@ fn main() { let _ = CustomLast.last(); } +// Should not be linted because applying the lint would move the original iterator. This can only be +// linted if the iterator is used thereafter. fn issue_14139() { let mut index = [true, true, false, false, false, true].iter(); - let mut subindex = index.by_ref().take(3); - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let subindex = index.by_ref().take(3); + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); let subindex = &mut subindex; - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); let subindex = &mut subindex; - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); - let (mut subindex, _) = (index.by_ref().take(3), 42); - let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let (subindex, _) = (index.by_ref().take(3), 42); + let _ = subindex.last(); + let _ = index.next(); } fn drop_order() { + struct DropDeIterator(std::vec::IntoIter); + impl Iterator for DropDeIterator { + type Item = S; + fn next(&mut self) -> Option { + self.0.next() + } + } + impl DoubleEndedIterator for DropDeIterator { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + } + struct S(&'static str); impl std::ops::Drop for S { fn drop(&mut self) { @@ -85,8 +105,19 @@ fn drop_order() { } let v = vec![S("one"), S("two"), S("three")]; - let mut v = v.into_iter(); + let mut v = DropDeIterator(v.into_iter()); println!("Last element is {}", v.next_back().unwrap().0); //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator` println!("Done"); } + +fn issue_14444() { + let mut squares = vec![]; + let last_square = [1, 2, 3] + .into_iter() + .map(|x| { + squares.push(x * x); + Some(x * x) + }) + .last(); +} diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.rs b/src/tools/clippy/tests/ui/double_ended_iterator_last.rs index 41bc669b1719f..30864e15bce7e 100644 --- a/src/tools/clippy/tests/ui/double_ended_iterator_last.rs +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.rs @@ -52,31 +52,51 @@ fn main() { let _ = CustomLast.last(); } +// Should not be linted because applying the lint would move the original iterator. This can only be +// linted if the iterator is used thereafter. fn issue_14139() { let mut index = [true, true, false, false, false, true].iter(); let subindex = index.by_ref().take(3); - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); let subindex = &mut subindex; - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let mut subindex = index.by_ref().take(3); let subindex = &mut subindex; - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); let mut index = [true, true, false, false, false, true].iter(); let (subindex, _) = (index.by_ref().take(3), 42); - let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.last(); + let _ = index.next(); } fn drop_order() { + struct DropDeIterator(std::vec::IntoIter); + impl Iterator for DropDeIterator { + type Item = S; + fn next(&mut self) -> Option { + self.0.next() + } + } + impl DoubleEndedIterator for DropDeIterator { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + } + struct S(&'static str); impl std::ops::Drop for S { fn drop(&mut self) { @@ -85,8 +105,19 @@ fn drop_order() { } let v = vec![S("one"), S("two"), S("three")]; - let v = v.into_iter(); + let v = DropDeIterator(v.into_iter()); println!("Last element is {}", v.last().unwrap().0); //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator` println!("Done"); } + +fn issue_14444() { + let mut squares = vec![]; + let last_square = [1, 2, 3] + .into_iter() + .map(|x| { + squares.push(x * x); + Some(x * x) + }) + .last(); +} diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr b/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr index 1702a24d7a055..72a6ead47a931 100644 --- a/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr @@ -18,55 +18,7 @@ LL | let _ = DeIterator.last(); | help: try: `next_back()` error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:58:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^^^^^^^ - | -help: try - | -LL ~ let mut subindex = index.by_ref().take(3); -LL ~ let _ = subindex.next_back(); - | - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:62:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^------ - | | - | help: try: `next_back()` - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:67:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^------ - | | - | help: try: `next_back()` - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:72:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^------ - | | - | help: try: `next_back()` - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:76:13 - | -LL | let _ = subindex.last(); - | ^^^^^^^^^^^^^^^ - | -help: try - | -LL ~ let (mut subindex, _) = (index.by_ref().take(3), 42); -LL ~ let _ = subindex.next_back(); - | - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last.rs:89:36 + --> tests/ui/double_ended_iterator_last.rs:109:36 | LL | println!("Last element is {}", v.last().unwrap().0); | ^^^^^^^^ @@ -74,9 +26,9 @@ LL | println!("Last element is {}", v.last().unwrap().0); = note: this change will alter drop order which may be undesirable help: try | -LL ~ let mut v = v.into_iter(); +LL ~ let mut v = DropDeIterator(v.into_iter()); LL ~ println!("Last element is {}", v.next_back().unwrap().0); | -error: aborting due to 8 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs index 3f125c7f20c1e..73f62ac124698 100644 --- a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs @@ -1,13 +1,29 @@ -//@no-rustfix +//@no-rustfix: requires manual changes #![warn(clippy::double_ended_iterator_last)] +// Should not be linted because applying the lint would move the original iterator. This can only be +// linted if the iterator is used thereafter. fn main() { let mut index = [true, true, false, false, false, true].iter(); let subindex = (index.by_ref().take(3), 42); - let _ = subindex.0.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator` + let _ = subindex.0.last(); + let _ = index.next(); } fn drop_order() { + struct DropDeIterator(std::vec::IntoIter); + impl Iterator for DropDeIterator { + type Item = S; + fn next(&mut self) -> Option { + self.0.next() + } + } + impl DoubleEndedIterator for DropDeIterator { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + } + struct S(&'static str); impl std::ops::Drop for S { fn drop(&mut self) { @@ -16,7 +32,7 @@ fn drop_order() { } let v = vec![S("one"), S("two"), S("three")]; - let v = (v.into_iter(), 42); + let v = (DropDeIterator(v.into_iter()), 42); println!("Last element is {}", v.0.last().unwrap().0); //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator` println!("Done"); diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.stderr b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.stderr index f4be757d00d29..e330a22a35489 100644 --- a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.stderr +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.stderr @@ -1,21 +1,5 @@ error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last_unfixable.rs:7:13 - | -LL | let _ = subindex.0.last(); - | ^^^^^^^^^^^------ - | | - | help: try: `next_back()` - | -note: this must be made mutable to use `.next_back()` - --> tests/ui/double_ended_iterator_last_unfixable.rs:7:13 - | -LL | let _ = subindex.0.last(); - | ^^^^^^^^^^ - = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]` - -error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator - --> tests/ui/double_ended_iterator_last_unfixable.rs:20:36 + --> tests/ui/double_ended_iterator_last_unfixable.rs:36:36 | LL | println!("Last element is {}", v.0.last().unwrap().0); | ^^^^------ @@ -24,10 +8,12 @@ LL | println!("Last element is {}", v.0.last().unwrap().0); | = note: this change will alter drop order which may be undesirable note: this must be made mutable to use `.next_back()` - --> tests/ui/double_ended_iterator_last_unfixable.rs:20:36 + --> tests/ui/double_ended_iterator_last_unfixable.rs:36:36 | LL | println!("Last element is {}", v.0.last().unwrap().0); | ^^^ + = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/eager_transmute.fixed b/src/tools/clippy/tests/ui/eager_transmute.fixed index 14cbb6113e62f..47a32ec836cc9 100644 --- a/src/tools/clippy/tests/ui/eager_transmute.fixed +++ b/src/tools/clippy/tests/ui/eager_transmute.fixed @@ -71,8 +71,10 @@ fn f(op: u8, op2: Data, unrelated: u8) { } unsafe fn f2(op: u8) { - (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); - //~^ eager_transmute + unsafe { + (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); + //~^ eager_transmute + } } #[rustc_layout_scalar_valid_range_end(254)] diff --git a/src/tools/clippy/tests/ui/eager_transmute.rs b/src/tools/clippy/tests/ui/eager_transmute.rs index 48d7d50cdaef8..906cd7bccc86f 100644 --- a/src/tools/clippy/tests/ui/eager_transmute.rs +++ b/src/tools/clippy/tests/ui/eager_transmute.rs @@ -71,8 +71,10 @@ fn f(op: u8, op2: Data, unrelated: u8) { } unsafe fn f2(op: u8) { - (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); - //~^ eager_transmute + unsafe { + (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); + //~^ eager_transmute + } } #[rustc_layout_scalar_valid_range_end(254)] diff --git a/src/tools/clippy/tests/ui/eager_transmute.stderr b/src/tools/clippy/tests/ui/eager_transmute.stderr index 54850d110eb26..c719ca8adc12e 100644 --- a/src/tools/clippy/tests/ui/eager_transmute.stderr +++ b/src/tools/clippy/tests/ui/eager_transmute.stderr @@ -157,19 +157,19 @@ LL + let _: Option = (..=3).contains(&op).then(|| unsafe { std::mem: | error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:74:24 + --> tests/ui/eager_transmute.rs:75:28 | -LL | (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider using `bool::then` to only transmute if the condition holds | -LL - (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); -LL + (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); +LL - (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); +LL + (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); | error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:104:62 + --> tests/ui/eager_transmute.rs:106:62 | LL | let _: Option> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) }); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let _: Option> = (v1 > 0).then(|| unsafe { std::mem::transm | error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:111:86 + --> tests/ui/eager_transmute.rs:113:86 | LL | let _: Option = (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + let _: Option = (v2 < NonZero::new(255u8).unwrap()).then(|| u | error: this transmute is always evaluated eagerly, even if the condition is false - --> tests/ui/eager_transmute.rs:118:93 + --> tests/ui/eager_transmute.rs:120:93 | LL | let _: Option = (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/empty_docs.rs b/src/tools/clippy/tests/ui/empty_docs.rs index d7768e07901ae..57f8976cd6a79 100644 --- a/src/tools/clippy/tests/ui/empty_docs.rs +++ b/src/tools/clippy/tests/ui/empty_docs.rs @@ -84,7 +84,7 @@ mod issue_12377 { use proc_macro_attr::with_empty_docs; #[with_empty_docs] - extern "C" { + unsafe extern "C" { type Test; } diff --git a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.fixed b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.fixed index 885f6a50025e9..abdf6ca5cb61e 100644 --- a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.fixed +++ b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.fixed @@ -6,8 +6,7 @@ pub enum PublicTestEnum { NonEmptyParentheses(i32, i32), // No error EmptyBraces, //~^ empty_enum_variants_with_brackets - EmptyParentheses, - //~^ empty_enum_variants_with_brackets + EmptyParentheses(), // No error as enum is pub } enum TestEnum { @@ -20,6 +19,67 @@ enum TestEnum { AnotherEnum, // No error } +mod issue12551 { + enum EvenOdd { + // Used as functions -> no error + Even(), + Odd(), + // Not used as a function + Unknown, + //~^ empty_enum_variants_with_brackets + } + + fn even_odd(x: i32) -> EvenOdd { + (x % 2 == 0).then(EvenOdd::Even).unwrap_or_else(EvenOdd::Odd) + } + + fn natural_number(x: i32) -> NaturalOrNot { + (x > 0) + .then(NaturalOrNot::Natural) + .unwrap_or_else(NaturalOrNot::NotNatural) + } + + enum NaturalOrNot { + // Used as functions -> no error + Natural(), + NotNatural(), + // Not used as a function + Unknown, + //~^ empty_enum_variants_with_brackets + } + + enum RedundantParenthesesFunctionCall { + // Used as a function call but with redundant parentheses + Parentheses, + //~^ empty_enum_variants_with_brackets + // Not used as a function + NoParentheses, + } + + #[allow(clippy::no_effect)] + fn redundant_parentheses_function_call() { + // The parentheses in the below line are redundant. + RedundantParenthesesFunctionCall::Parentheses; + RedundantParenthesesFunctionCall::NoParentheses; + } + + // Same test as above but with usage of the enum occurring before the definition. + #[allow(clippy::no_effect)] + fn redundant_parentheses_function_call_2() { + // The parentheses in the below line are redundant. + RedundantParenthesesFunctionCall2::Parentheses; + RedundantParenthesesFunctionCall2::NoParentheses; + } + + enum RedundantParenthesesFunctionCall2 { + // Used as a function call but with redundant parentheses + Parentheses, + //~^ empty_enum_variants_with_brackets + // Not used as a function + NoParentheses, + } +} + enum TestEnumWithFeatures { NonEmptyBraces { #[cfg(feature = "thisisneverenabled")] @@ -28,4 +88,18 @@ enum TestEnumWithFeatures { NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), // No error } +#[derive(Clone)] +enum Foo { + Variant1(i32), + Variant2, + Variant3, //~ ERROR: enum variant has empty brackets +} + +#[derive(Clone)] +pub enum PubFoo { + Variant1(i32), + Variant2, + Variant3(), +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.rs b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.rs index 092712ee2ead4..63a5a8e9143e6 100644 --- a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.rs +++ b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.rs @@ -6,8 +6,7 @@ pub enum PublicTestEnum { NonEmptyParentheses(i32, i32), // No error EmptyBraces {}, //~^ empty_enum_variants_with_brackets - EmptyParentheses(), - //~^ empty_enum_variants_with_brackets + EmptyParentheses(), // No error as enum is pub } enum TestEnum { @@ -20,6 +19,67 @@ enum TestEnum { AnotherEnum, // No error } +mod issue12551 { + enum EvenOdd { + // Used as functions -> no error + Even(), + Odd(), + // Not used as a function + Unknown(), + //~^ empty_enum_variants_with_brackets + } + + fn even_odd(x: i32) -> EvenOdd { + (x % 2 == 0).then(EvenOdd::Even).unwrap_or_else(EvenOdd::Odd) + } + + fn natural_number(x: i32) -> NaturalOrNot { + (x > 0) + .then(NaturalOrNot::Natural) + .unwrap_or_else(NaturalOrNot::NotNatural) + } + + enum NaturalOrNot { + // Used as functions -> no error + Natural(), + NotNatural(), + // Not used as a function + Unknown(), + //~^ empty_enum_variants_with_brackets + } + + enum RedundantParenthesesFunctionCall { + // Used as a function call but with redundant parentheses + Parentheses(), + //~^ empty_enum_variants_with_brackets + // Not used as a function + NoParentheses, + } + + #[allow(clippy::no_effect)] + fn redundant_parentheses_function_call() { + // The parentheses in the below line are redundant. + RedundantParenthesesFunctionCall::Parentheses(); + RedundantParenthesesFunctionCall::NoParentheses; + } + + // Same test as above but with usage of the enum occurring before the definition. + #[allow(clippy::no_effect)] + fn redundant_parentheses_function_call_2() { + // The parentheses in the below line are redundant. + RedundantParenthesesFunctionCall2::Parentheses(); + RedundantParenthesesFunctionCall2::NoParentheses; + } + + enum RedundantParenthesesFunctionCall2 { + // Used as a function call but with redundant parentheses + Parentheses(), + //~^ empty_enum_variants_with_brackets + // Not used as a function + NoParentheses, + } +} + enum TestEnumWithFeatures { NonEmptyBraces { #[cfg(feature = "thisisneverenabled")] @@ -28,4 +88,18 @@ enum TestEnumWithFeatures { NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), // No error } +#[derive(Clone)] +enum Foo { + Variant1(i32), + Variant2, + Variant3(), //~ ERROR: enum variant has empty brackets +} + +#[derive(Clone)] +pub enum PubFoo { + Variant1(i32), + Variant2, + Variant3(), +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.stderr b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.stderr index a9ae3b476dd68..7fe85e829a351 100644 --- a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.stderr +++ b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.stderr @@ -9,7 +9,15 @@ LL | EmptyBraces {}, = help: remove the brackets error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:9:21 + --> tests/ui/empty_enum_variants_with_brackets.rs:15:16 + | +LL | EmptyBraces {}, + | ^^^ + | + = help: remove the brackets + +error: enum variant has empty brackets + --> tests/ui/empty_enum_variants_with_brackets.rs:17:21 | LL | EmptyParentheses(), | ^^ @@ -17,20 +25,58 @@ LL | EmptyParentheses(), = help: remove the brackets error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:16:16 + --> tests/ui/empty_enum_variants_with_brackets.rs:28:16 | -LL | EmptyBraces {}, - | ^^^ +LL | Unknown(), + | ^^ | = help: remove the brackets error: enum variant has empty brackets - --> tests/ui/empty_enum_variants_with_brackets.rs:18:21 + --> tests/ui/empty_enum_variants_with_brackets.rs:47:16 | -LL | EmptyParentheses(), - | ^^ +LL | Unknown(), + | ^^ + | + = help: remove the brackets + +error: enum variant has empty brackets + --> tests/ui/empty_enum_variants_with_brackets.rs:53:20 + | +LL | Parentheses(), + | ^^ + | +help: remove the brackets + | +LL ~ Parentheses, +LL | +... +LL | // The parentheses in the below line are redundant. +LL ~ RedundantParenthesesFunctionCall::Parentheses; + | + +error: enum variant has empty brackets + --> tests/ui/empty_enum_variants_with_brackets.rs:76:20 + | +LL | Parentheses(), + | ^^ + | +help: remove the brackets + | +LL ~ RedundantParenthesesFunctionCall2::Parentheses; +LL | RedundantParenthesesFunctionCall2::NoParentheses; +... +LL | // Used as a function call but with redundant parentheses +LL ~ Parentheses, + | + +error: enum variant has empty brackets + --> tests/ui/empty_enum_variants_with_brackets.rs:95:13 + | +LL | Variant3(), + | ^^ | = help: remove the brackets -error: aborting due to 4 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed index e4ba09ea1d478..70ab235b694f5 100644 --- a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed +++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed @@ -142,4 +142,9 @@ impl Foo for LineComment { fn bar() {} } +//~v empty_line_after_doc_comments +/// Docs for this item. +// fn some_item() {} +impl LineComment {} // or any other nameless item kind + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed index a20f9bc20eb56..87c636c6ad2c7 100644 --- a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed +++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed @@ -152,4 +152,10 @@ impl Foo for LineComment { fn bar() {} } +//~v empty_line_after_doc_comments +// /// Docs for this item. +// fn some_item() {} + +impl LineComment {} // or any other nameless item kind + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs index 9e3ddfd5abe11..91e9c1ac0b6d3 100644 --- a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs +++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs @@ -155,4 +155,10 @@ impl Foo for LineComment { fn bar() {} } +//~v empty_line_after_doc_comments +/// Docs for this item. +// fn some_item() {} + +impl LineComment {} // or any other nameless item kind + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr index fe25ba9afcb90..ae8cb91ba12f7 100644 --- a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr +++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr @@ -87,7 +87,7 @@ LL | fn new_code() {} | ----------- the comment documents this function | = help: if the empty line is unintentional, remove it -help: if the doc comment should not document `new_code` comment it out +help: if the doc comment should not document function `new_code` then comment it out | LL | // /// docs for `old_code` | ++ @@ -107,7 +107,7 @@ LL | struct Multiple; | --------------- the comment documents this struct | = help: if the empty lines are unintentional, remove them -help: if the doc comment should not document `Multiple` comment it out +help: if the doc comment should not document struct `Multiple` then comment it out | LL ~ // /// Docs LL ~ // /// for OldA @@ -149,7 +149,7 @@ LL | fn new_code() {} | ----------- the comment documents this function | = help: if the empty line is unintentional, remove it -help: if the doc comment should not document `new_code` comment it out +help: if the doc comment should not document function `new_code` then comment it out | LL - /** LL + /* @@ -167,7 +167,7 @@ LL | fn new_code2() {} | ------------ the comment documents this function | = help: if the empty line is unintentional, remove it -help: if the doc comment should not document `new_code2` comment it out +help: if the doc comment should not document function `new_code2` then comment it out | LL | // /// Docs for `old_code2` | ++ @@ -183,10 +183,26 @@ LL | fn bar() {} | ------ the comment documents this function | = help: if the empty line is unintentional, remove it -help: if the doc comment should not document `bar` comment it out +help: if the doc comment should not document function `bar` then comment it out | LL | // /// comment on assoc item | ++ -error: aborting due to 11 previous errors +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:159:1 + | +LL | / /// Docs for this item. +LL | | // fn some_item() {} +LL | | + | |_^ +LL | impl LineComment {} // or any other nameless item kind + | - the comment documents this implementation + | + = help: if the empty line is unintentional, remove it +help: if the doc comment should not document the following item then comment it out + | +LL | // /// Docs for this item. + | ++ + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed b/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed index b1600862a8f60..419cf2354f89b 100644 --- a/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed +++ b/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed @@ -23,4 +23,12 @@ struct MyTupleStruct(usize, String); // should not trigger lint struct MySingleTupleStruct(usize); // should not trigger lint struct MyUnitLikeStruct; // should not trigger lint +macro_rules! empty_struct { + ($s:ident) => { + struct $s {} + }; +} + +empty_struct!(FromMacro); + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs b/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs index 1f69c4be9ec77..90c415c122063 100644 --- a/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs +++ b/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs @@ -23,4 +23,12 @@ struct MyTupleStruct(usize, String); // should not trigger lint struct MySingleTupleStruct(usize); // should not trigger lint struct MyUnitLikeStruct; // should not trigger lint +macro_rules! empty_struct { + ($s:ident) => { + struct $s {} + }; +} + +empty_struct!(FromMacro); + fn main() {} diff --git a/src/tools/clippy/tests/ui/entry.fixed b/src/tools/clippy/tests/ui/entry.fixed index 69452a8d9a671..f2df9f0204ea6 100644 --- a/src/tools/clippy/tests/ui/entry.fixed +++ b/src/tools/clippy/tests/ui/entry.fixed @@ -226,4 +226,26 @@ fn issue11976() { } } +mod issue14449 { + use std::collections::BTreeMap; + + pub struct Meow { + map: BTreeMap, + } + + impl Meow { + fn pet(&self, _key: &str, _v: u32) -> u32 { + 42 + } + } + + pub fn f(meow: &Meow, x: String) { + if meow.map.contains_key(&x) { + let _ = meow.pet(&x, 1); + } else { + let _ = meow.pet(&x, 0); + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/entry.rs b/src/tools/clippy/tests/ui/entry.rs index 3578324f01c58..166eea417ac23 100644 --- a/src/tools/clippy/tests/ui/entry.rs +++ b/src/tools/clippy/tests/ui/entry.rs @@ -232,4 +232,26 @@ fn issue11976() { } } +mod issue14449 { + use std::collections::BTreeMap; + + pub struct Meow { + map: BTreeMap, + } + + impl Meow { + fn pet(&self, _key: &str, _v: u32) -> u32 { + 42 + } + } + + pub fn f(meow: &Meow, x: String) { + if meow.map.contains_key(&x) { + let _ = meow.pet(&x, 1); + } else { + let _ = meow.pet(&x, 0); + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/entry_unfixable.rs b/src/tools/clippy/tests/ui/entry_unfixable.rs new file mode 100644 index 0000000000000..c4c055572086e --- /dev/null +++ b/src/tools/clippy/tests/ui/entry_unfixable.rs @@ -0,0 +1,93 @@ +#![allow(clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] + +use std::collections::HashMap; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +macro_rules! insert { + ($map:expr, $key:expr, $val:expr) => { + $map.insert($key, $val) + }; +} + +mod issue13306 { + use std::collections::HashMap; + + struct Env { + enclosing: Option>, + values: HashMap, + } + + impl Env { + fn assign(&mut self, name: String, value: usize) -> bool { + if !self.values.contains_key(&name) { + //~^ map_entry + self.values.insert(name, value); + true + } else if let Some(enclosing) = &mut self.enclosing { + enclosing.assign(name, value) + } else { + false + } + } + } +} + +fn issue9925(mut hm: HashMap) { + let key = "hello".to_string(); + if hm.contains_key(&key) { + //~^ map_entry + let bval = hm.get_mut(&key).unwrap(); + *bval = false; + } else { + hm.insert(key, true); + } +} + +mod issue9470 { + use std::collections::HashMap; + use std::sync::Mutex; + + struct Interner(i32); + + impl Interner { + const fn new() -> Self { + Interner(0) + } + + fn resolve(&self, name: String) -> Option { + todo!() + } + } + + static INTERNER: Mutex = Mutex::new(Interner::new()); + + struct VM { + stack: Vec, + globals: HashMap, + } + + impl VM { + fn stack_top(&self) -> &i32 { + self.stack.last().unwrap() + } + + fn resolve(&mut self, name: String, value: i32) -> Result<(), String> { + if self.globals.contains_key(&name) { + //~^ map_entry + self.globals.insert(name, value); + } else { + let interner = INTERNER.lock().unwrap(); + return Err(interner.resolve(name).unwrap().to_owned()); + } + + Ok(()) + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/entry_unfixable.stderr b/src/tools/clippy/tests/ui/entry_unfixable.stderr new file mode 100644 index 0000000000000..0197d2ab4cf9c --- /dev/null +++ b/src/tools/clippy/tests/ui/entry_unfixable.stderr @@ -0,0 +1,41 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> tests/ui/entry_unfixable.rs:27:13 + | +LL | / if !self.values.contains_key(&name) { +LL | | +LL | | self.values.insert(name, value); +LL | | true +... | +LL | | false +LL | | } + | |_____________^ + | + = note: `-D clippy::map-entry` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::map_entry)]` + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> tests/ui/entry_unfixable.rs:42:5 + | +LL | / if hm.contains_key(&key) { +LL | | +LL | | let bval = hm.get_mut(&key).unwrap(); +LL | | *bval = false; +LL | | } else { +LL | | hm.insert(key, true); +LL | | } + | |_____^ + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> tests/ui/entry_unfixable.rs:80:13 + | +LL | / if self.globals.contains_key(&name) { +LL | | +LL | | self.globals.insert(name, value); +LL | | } else { +LL | | let interner = INTERNER.lock().unwrap(); +LL | | return Err(interner.resolve(name).unwrap().to_owned()); +LL | | } + | |_____________^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/enum_variants.rs b/src/tools/clippy/tests/ui/enum_variants.rs index f7bbf83654f11..18c80c7aba43d 100644 --- a/src/tools/clippy/tests/ui/enum_variants.rs +++ b/src/tools/clippy/tests/ui/enum_variants.rs @@ -220,4 +220,19 @@ mod issue11494 { } } +mod encapsulated { + mod types { + pub struct FooError; + pub struct BarError; + pub struct BazError; + } + + enum Error { + FooError(types::FooError), + BarError(types::BarError), + BazError(types::BazError), + Other, + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/equatable_if_let.fixed b/src/tools/clippy/tests/ui/equatable_if_let.fixed index 166b1387ba265..ce8b67f9ca7b0 100644 --- a/src/tools/clippy/tests/ui/equatable_if_let.fixed +++ b/src/tools/clippy/tests/ui/equatable_if_let.fixed @@ -103,3 +103,39 @@ fn main() { external!({ if let 2 = $a {} }); } + +mod issue8710 { + fn str_ref(cs: &[char]) { + if matches!(cs.iter().next(), Some('i')) { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn i32_ref(cs: &[i32]) { + if matches!(cs.iter().next(), Some(1)) { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn enum_ref() { + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + enum MyEnum { + A(i32), + B, + } + + fn get_enum() -> Option<&'static MyEnum> { + todo!() + } + + if matches!(get_enum(), Some(MyEnum::B)) { + //~^ equatable_if_let + } else { + todo!(); + } + } +} diff --git a/src/tools/clippy/tests/ui/equatable_if_let.rs b/src/tools/clippy/tests/ui/equatable_if_let.rs index 09c2483ae6d43..ff09533f26519 100644 --- a/src/tools/clippy/tests/ui/equatable_if_let.rs +++ b/src/tools/clippy/tests/ui/equatable_if_let.rs @@ -103,3 +103,39 @@ fn main() { external!({ if let 2 = $a {} }); } + +mod issue8710 { + fn str_ref(cs: &[char]) { + if let Some('i') = cs.iter().next() { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn i32_ref(cs: &[i32]) { + if let Some(1) = cs.iter().next() { + //~^ equatable_if_let + } else { + todo!(); + } + } + + fn enum_ref() { + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] + enum MyEnum { + A(i32), + B, + } + + fn get_enum() -> Option<&'static MyEnum> { + todo!() + } + + if let Some(MyEnum::B) = get_enum() { + //~^ equatable_if_let + } else { + todo!(); + } + } +} diff --git a/src/tools/clippy/tests/ui/equatable_if_let.stderr b/src/tools/clippy/tests/ui/equatable_if_let.stderr index 81e0e15a5c747..dd1832ad68b28 100644 --- a/src/tools/clippy/tests/ui/equatable_if_let.stderr +++ b/src/tools/clippy/tests/ui/equatable_if_let.stderr @@ -85,5 +85,23 @@ error: this pattern matching can be expressed using equality LL | if let inline!("abc") = "abc" { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"abc" == inline!("abc")` -error: aborting due to 14 previous errors +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:109:12 + | +LL | if let Some('i') = cs.iter().next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some('i'))` + +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:117:12 + | +LL | if let Some(1) = cs.iter().next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(cs.iter().next(), Some(1))` + +error: this pattern matching can be expressed using `matches!` + --> tests/ui/equatable_if_let.rs:135:12 + | +LL | if let Some(MyEnum::B) = get_enum() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(get_enum(), Some(MyEnum::B))` + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/excessive_precision.fixed b/src/tools/clippy/tests/ui/excessive_precision.fixed index 99d09774d16d3..8a8c2e1939c83 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.fixed +++ b/src/tools/clippy/tests/ui/excessive_precision.fixed @@ -79,6 +79,9 @@ fn main() { // issue #2840 let num = 0.000_000_000_01e-10f64; + // issue #6341 + let exponential: f64 = 4.886506780521244E-03; + // issue #7744 let _ = 2.225_073_858_507_201e-308_f64; //~^ excessive_precision diff --git a/src/tools/clippy/tests/ui/excessive_precision.rs b/src/tools/clippy/tests/ui/excessive_precision.rs index a542fb2e7e3cd..5dcf55cb92735 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.rs +++ b/src/tools/clippy/tests/ui/excessive_precision.rs @@ -79,6 +79,9 @@ fn main() { // issue #2840 let num = 0.000_000_000_01e-10f64; + // issue #6341 + let exponential: f64 = 4.886506780521244E-03; + // issue #7744 let _ = 2.225_073_858_507_201_1e-308_f64; //~^ excessive_precision diff --git a/src/tools/clippy/tests/ui/excessive_precision.stderr b/src/tools/clippy/tests/ui/excessive_precision.stderr index 934a367e10659..f5eeadf0c8cb0 100644 --- a/src/tools/clippy/tests/ui/excessive_precision.stderr +++ b/src/tools/clippy/tests/ui/excessive_precision.stderr @@ -157,7 +157,7 @@ LL + let bad_bige32: f32 = 1.123_456_8E-10; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:83:13 + --> tests/ui/excessive_precision.rs:86:13 | LL | let _ = 2.225_073_858_507_201_1e-308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + let _ = 2.225_073_858_507_201e-308_f64; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:87:13 + --> tests/ui/excessive_precision.rs:90:13 | LL | let _ = 1.000_000_000_000_001e-324_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let _ = 0_f64; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:98:20 + --> tests/ui/excessive_precision.rs:101:20 | LL | const _: f64 = 3.0000000000000000e+00; | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed index 7235f7d5b82af..ec6bed152e797 100644 --- a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed +++ b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed @@ -59,7 +59,7 @@ fn f_str_t(_: &str, _: T) {} fn f_box_t(_: &Box) {} -extern "C" { +unsafe extern "C" { fn var(_: u32, ...); } diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.rs b/src/tools/clippy/tests/ui/explicit_auto_deref.rs index c4d2b28ff4b5f..ca58c650d9ce9 100644 --- a/src/tools/clippy/tests/ui/explicit_auto_deref.rs +++ b/src/tools/clippy/tests/ui/explicit_auto_deref.rs @@ -59,7 +59,7 @@ fn f_str_t(_: &str, _: T) {} fn f_box_t(_: &Box) {} -extern "C" { +unsafe extern "C" { fn var(_: u32, ...); } diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.rs b/src/tools/clippy/tests/ui/explicit_counter_loop.rs index 8340d99ace226..13934785d7b15 100644 --- a/src/tools/clippy/tests/ui/explicit_counter_loop.rs +++ b/src/tools/clippy/tests/ui/explicit_counter_loop.rs @@ -1,6 +1,6 @@ #![warn(clippy::explicit_counter_loop)] #![allow(clippy::uninlined_format_args, clippy::useless_vec)] -//@no-rustfix +//@no-rustfix: suggestion does not remove the `+= 1` fn main() { let mut vec = vec![1, 2, 3, 4]; let mut _index = 0; diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed index 0d1a5f80f3d5a..619329a6ade0c 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed @@ -8,7 +8,8 @@ clippy::needless_borrow, clippy::no_effect, clippy::uninlined_format_args, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::deref_addrof )] use std::ops::{Deref, DerefMut}; @@ -87,9 +88,6 @@ fn main() { let b = &*opt_a.unwrap(); //~^ explicit_deref_methods - // make sure `Aaa::deref` instead of `aaa.deref()` is not linted, as well as fully qualified - // syntax - Aaa::deref(&Aaa); Aaa::deref_mut(&mut Aaa); ::deref(&Aaa); @@ -139,4 +137,9 @@ fn main() { let no_lint = NoLint(42); let b = no_lint.deref(); let b = no_lint.deref_mut(); + + let _ = &*&"foo"; //~ explicit_deref_methods + let mut x = String::new(); + let _ = &&mut **&mut x; //~ explicit_deref_methods + let _ = &&mut ***(&mut &mut x); //~ explicit_deref_methods } diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.rs b/src/tools/clippy/tests/ui/explicit_deref_methods.rs index 8d4a899cd2607..9f2d513283c9b 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.rs +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.rs @@ -8,7 +8,8 @@ clippy::needless_borrow, clippy::no_effect, clippy::uninlined_format_args, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::deref_addrof )] use std::ops::{Deref, DerefMut}; @@ -87,9 +88,6 @@ fn main() { let b = opt_a.unwrap().deref(); //~^ explicit_deref_methods - // make sure `Aaa::deref` instead of `aaa.deref()` is not linted, as well as fully qualified - // syntax - Aaa::deref(&Aaa); Aaa::deref_mut(&mut Aaa); ::deref(&Aaa); @@ -139,4 +137,9 @@ fn main() { let no_lint = NoLint(42); let b = no_lint.deref(); let b = no_lint.deref_mut(); + + let _ = &Deref::deref(&"foo"); //~ explicit_deref_methods + let mut x = String::new(); + let _ = &DerefMut::deref_mut(&mut x); //~ explicit_deref_methods + let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); //~ explicit_deref_methods } diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr index 2ca376cba00bd..a81e2f60317b3 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:54:19 + --> tests/ui/explicit_deref_methods.rs:55:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try: `&*a` @@ -8,70 +8,88 @@ LL | let b: &str = a.deref(); = help: to override `-D warnings` add `#[allow(clippy::explicit_deref_methods)]` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:57:23 + --> tests/ui/explicit_deref_methods.rs:58:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try: `&mut **a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:61:39 + --> tests/ui/explicit_deref_methods.rs:62:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:61:50 + --> tests/ui/explicit_deref_methods.rs:62:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:65:20 + --> tests/ui/explicit_deref_methods.rs:66:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:69:11 + --> tests/ui/explicit_deref_methods.rs:70:11 | LL | match a.deref() { | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:74:28 + --> tests/ui/explicit_deref_methods.rs:75:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:77:13 + --> tests/ui/explicit_deref_methods.rs:78:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:80:28 + --> tests/ui/explicit_deref_methods.rs:81:28 | LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:83:19 + --> tests/ui/explicit_deref_methods.rs:84:19 | LL | let b: &str = a.deref().deref(); | ^^^^^^^^^^^^^^^^^ help: try: `&**a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:87:13 + --> tests/ui/explicit_deref_methods.rs:88:13 | LL | let b = opt_a.unwrap().deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*opt_a.unwrap()` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:125:31 + --> tests/ui/explicit_deref_methods.rs:123:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try: `&*a` -error: aborting due to 12 previous errors +error: explicit `deref` method call + --> tests/ui/explicit_deref_methods.rs:141:14 + | +LL | let _ = &Deref::deref(&"foo"); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `*&"foo"` + +error: explicit `deref_mut` method call + --> tests/ui/explicit_deref_methods.rs:143:14 + | +LL | let _ = &DerefMut::deref_mut(&mut x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut **&mut x` + +error: explicit `deref_mut` method call + --> tests/ui/explicit_deref_methods.rs:144:14 + | +LL | let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut ***(&mut &mut x)` + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed b/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed index 2b68906ae39fa..c1b3c478eeb91 100644 --- a/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed +++ b/src/tools/clippy/tests/ui/explicit_into_iter_loop.fixed @@ -73,3 +73,16 @@ fn main() { for _ in S.into_iter::() {} } + +fn issue14630() { + macro_rules! mac { + (into_iter $e:expr) => { + $e.into_iter() + }; + } + + for _ in dbg!([1, 2]) {} + //~^ explicit_into_iter_loop + + for _ in mac!(into_iter [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs b/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs index ca335b62d9068..581e0dadcecb3 100644 --- a/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs +++ b/src/tools/clippy/tests/ui/explicit_into_iter_loop.rs @@ -73,3 +73,16 @@ fn main() { for _ in S.into_iter::() {} } + +fn issue14630() { + macro_rules! mac { + (into_iter $e:expr) => { + $e.into_iter() + }; + } + + for _ in dbg!([1, 2]).into_iter() {} + //~^ explicit_into_iter_loop + + for _ in mac!(into_iter [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr b/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr index 1c3156755d4ee..26fb11e004822 100644 --- a/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr +++ b/src/tools/clippy/tests/ui/explicit_into_iter_loop.stderr @@ -37,5 +37,11 @@ error: it is more concise to loop over containers instead of using explicit iter LL | for _ in mr.into_iter() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *mr` -error: aborting due to 6 previous errors +error: it is more concise to loop over containers instead of using explicit iteration methods + --> tests/ui/explicit_into_iter_loop.rs:84:14 + | +LL | for _ in dbg!([1, 2]).into_iter() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `dbg!([1, 2])` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed index cd0898dfc3672..f246ec61800e1 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed +++ b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed @@ -183,3 +183,16 @@ pub fn issue_13184() { let rvalues = &values; for _ in rvalues.iter() {} } + +fn issue14630() { + macro_rules! mac { + (iter $e:expr) => { + $e.into_iter() + }; + } + + for _ in &dbg!([1, 2]) {} + //~^ explicit_iter_loop + + for _ in mac!(iter [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.rs b/src/tools/clippy/tests/ui/explicit_iter_loop.rs index 02405280ce423..35f4fb7097d89 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.rs +++ b/src/tools/clippy/tests/ui/explicit_iter_loop.rs @@ -183,3 +183,16 @@ pub fn issue_13184() { let rvalues = &values; for _ in rvalues.iter() {} } + +fn issue14630() { + macro_rules! mac { + (iter $e:expr) => { + $e.into_iter() + }; + } + + for _ in dbg!([1, 2]).iter() {} + //~^ explicit_iter_loop + + for _ in mac!(iter [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.stderr b/src/tools/clippy/tests/ui/explicit_iter_loop.stderr index 3816bb4db98b3..575dbe7813d75 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.stderr +++ b/src/tools/clippy/tests/ui/explicit_iter_loop.stderr @@ -112,5 +112,11 @@ error: it is more concise to loop over references to containers instead of using LL | for _ in r.iter() {} | ^^^^^^^^ help: to write this more concisely, try: `r` -error: aborting due to 18 previous errors +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> tests/ui/explicit_iter_loop.rs:194:14 + | +LL | for _ in dbg!([1, 2]).iter() {} + | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&dbg!([1, 2])` + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/fallible_impl_from.stderr b/src/tools/clippy/tests/ui/fallible_impl_from.stderr index d773fc3a6d7c7..402494b39f30b 100644 --- a/src/tools/clippy/tests/ui/fallible_impl_from.stderr +++ b/src/tools/clippy/tests/ui/fallible_impl_from.stderr @@ -38,7 +38,6 @@ note: potential failure(s) | LL | panic!(); | ^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: consider implementing `TryFrom` instead --> tests/ui/fallible_impl_from.rs:40:1 @@ -64,7 +63,6 @@ LL | } else if s.parse::().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^ LL | panic!("{:?}", s); | ^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: consider implementing `TryFrom` instead --> tests/ui/fallible_impl_from.rs:60:1 @@ -85,7 +83,6 @@ LL | if s.parse::().ok().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | panic!("{:?}", s); | ^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (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/filter_map_bool_then_unfixable.rs b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs new file mode 100644 index 0000000000000..5d29e0317bb2f --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.rs @@ -0,0 +1,62 @@ +#![allow(clippy::question_mark)] +#![warn(clippy::filter_map_bool_then)] + +fn issue11617() { + let mut x: Vec = vec![0; 10]; + let _ = (0..x.len()).zip(x.clone().iter()).filter_map(|(i, v)| { + //~^ filter_map_bool_then + (x[i] != *v).then(|| { + x[i] = i; + i + }) + }); +} + +mod issue14368 { + + fn do_something(_: ()) -> bool { + true + } + + fn option_with_early_return(x: &[Option]) { + let _ = x.iter().filter_map(|&x| x?.then(|| do_something(()))); + //~^ filter_map_bool_then + let _ = x + .iter() + .filter_map(|&x| if let Some(x) = x { x } else { return None }.then(|| do_something(()))); + //~^ filter_map_bool_then + let _ = x.iter().filter_map(|&x| { + //~^ filter_map_bool_then + match x { + Some(x) => x, + None => return None, + } + .then(|| do_something(())) + }); + } + + #[derive(Copy, Clone)] + enum Foo { + One(bool), + Two, + Three(Option), + } + + fn nested_type_with_early_return(x: &[Foo]) { + let _ = x.iter().filter_map(|&x| { + //~^ filter_map_bool_then + match x { + Foo::One(x) => x, + Foo::Two => return None, + Foo::Three(inner) => { + if inner? == 0 { + return Some(false); + } else { + true + } + }, + } + .then(|| do_something(())) + }); + } +} diff --git a/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr new file mode 100644 index 0000000000000..2990423973e10 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_map_bool_then_unfixable.stderr @@ -0,0 +1,65 @@ +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:6:48 + | +LL | let _ = (0..x.len()).zip(x.clone().iter()).filter_map(|(i, v)| { + | ________________________________________________^ +LL | | +LL | | (x[i] != *v).then(|| { +LL | | x[i] = i; +LL | | i +LL | | }) +LL | | }); + | |______^ + | + = help: consider using `filter` then `map` instead + = note: `-D clippy::filter-map-bool-then` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::filter_map_bool_then)]` + +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:22:26 + | +LL | let _ = x.iter().filter_map(|&x| x?.then(|| do_something(()))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `filter` then `map` instead + +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:26:14 + | +LL | .filter_map(|&x| if let Some(x) = x { x } else { return None }.then(|| do_something(()))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `filter` then `map` instead + +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:28:26 + | +LL | let _ = x.iter().filter_map(|&x| { + | __________________________^ +LL | | +LL | | match x { +LL | | Some(x) => x, +... | +LL | | .then(|| do_something(())) +LL | | }); + | |__________^ + | + = help: consider using `filter` then `map` instead + +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then_unfixable.rs:46:26 + | +LL | let _ = x.iter().filter_map(|&x| { + | __________________________^ +LL | | +LL | | match x { +LL | | Foo::One(x) => x, +... | +LL | | .then(|| do_something(())) +LL | | }); + | |__________^ + | + = help: consider using `filter` then `map` instead + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/filter_map_next.rs b/src/tools/clippy/tests/ui/filter_map_next.rs index 2a2237ed16cf2..5414e01c87006 100644 --- a/src/tools/clippy/tests/ui/filter_map_next.rs +++ b/src/tools/clippy/tests/ui/filter_map_next.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::pedantic)] +#![warn(clippy::filter_map_next)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed b/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed index 285863ef340db..09c416041a4e9 100644 --- a/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed +++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed @@ -1,5 +1,4 @@ -#![warn(clippy::all, clippy::pedantic)] -#![allow(unused)] +#![warn(clippy::filter_map_next)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.rs b/src/tools/clippy/tests/ui/filter_map_next_fixable.rs index af911689b7c72..3d686ef41d917 100644 --- a/src/tools/clippy/tests/ui/filter_map_next_fixable.rs +++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.rs @@ -1,5 +1,4 @@ -#![warn(clippy::all, clippy::pedantic)] -#![allow(unused)] +#![warn(clippy::filter_map_next)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr b/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr index 707dec8687b1f..1002837732b86 100644 --- a/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr +++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr @@ -1,5 +1,5 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> tests/ui/filter_map_next_fixable.rs:7:32 + --> tests/ui/filter_map_next_fixable.rs:6:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.iter().find_map(|s| s.parse().ok())` @@ -8,7 +8,7 @@ LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next = help: to override `-D warnings` add `#[allow(clippy::filter_map_next)]` error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> tests/ui/filter_map_next_fixable.rs:21:26 + --> tests/ui/filter_map_next_fixable.rs:20:26 | LL | let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.iter().find_map(|s| s.parse().ok())` diff --git a/src/tools/clippy/tests/ui/find_map.rs b/src/tools/clippy/tests/ui/find_map.rs index aba1f2cbe581e..a9a8508d5b7db 100644 --- a/src/tools/clippy/tests/ui/find_map.rs +++ b/src/tools/clippy/tests/ui/find_map.rs @@ -1,6 +1,5 @@ //@ check-pass -#![warn(clippy::all, clippy::pedantic)] #![allow(clippy::useless_vec)] #[derive(Debug, Copy, Clone)] diff --git a/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs b/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs index cc18708d25faf..25d25663d1e4d 100644 --- a/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs +++ b/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs @@ -1,7 +1,7 @@ #![warn(clippy::fn_params_excessive_bools)] #![allow(clippy::too_many_arguments)] -extern "C" { +unsafe extern "C" { // Should not lint, most of the time users have no control over extern function signatures fn f(_: bool, _: bool, _: bool, _: bool); } @@ -14,8 +14,8 @@ macro_rules! foo { foo!(); -#[no_mangle] -extern "C" fn k(_: bool, _: bool, _: bool, _: bool) {} +#[unsafe(no_mangle)] +unsafe extern "C" fn k(_: bool, _: bool, _: bool, _: bool) {} fn g(_: bool, _: bool, _: bool, _: bool) {} //~^ ERROR: more than 3 bools in function parameters fn h(_: bool, _: bool, _: bool) {} @@ -39,8 +39,8 @@ impl S { fn f(&self, _: bool, _: bool, _: bool, _: bool) {} //~^ ERROR: more than 3 bools in function parameters fn g(&self, _: bool, _: bool, _: bool) {} - #[no_mangle] - extern "C" fn h(_: bool, _: bool, _: bool, _: bool) {} + #[unsafe(no_mangle)] + unsafe extern "C" fn h(_: bool, _: bool, _: bool, _: bool) {} } impl Trait for S { diff --git a/src/tools/clippy/tests/ui/formatting.rs b/src/tools/clippy/tests/ui/formatting.rs index 4e84dcf7d5b70..009815633d759 100644 --- a/src/tools/clippy/tests/ui/formatting.rs +++ b/src/tools/clippy/tests/ui/formatting.rs @@ -1,6 +1,3 @@ -#![warn(clippy::all)] -#![allow(unused_variables)] -#![allow(unused_assignments)] #![allow(clippy::if_same_then_else)] #![allow(clippy::deref_addrof)] #![allow(clippy::nonminimal_bool)] diff --git a/src/tools/clippy/tests/ui/formatting.stderr b/src/tools/clippy/tests/ui/formatting.stderr index 972bd3a6a2e65..d9dc2a55f5b62 100644 --- a/src/tools/clippy/tests/ui/formatting.stderr +++ b/src/tools/clippy/tests/ui/formatting.stderr @@ -1,5 +1,5 @@ error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)` - --> tests/ui/formatting.rs:16:6 + --> tests/ui/formatting.rs:13:6 | LL | a =- 35; | ^^^^ @@ -9,7 +9,7 @@ LL | a =- 35; = help: to override `-D warnings` add `#[allow(clippy::suspicious_assignment_formatting)]` error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)` - --> tests/ui/formatting.rs:20:6 + --> tests/ui/formatting.rs:17:6 | LL | a =* &191; | ^^^^ @@ -17,7 +17,7 @@ LL | a =* &191; = note: to remove this lint, use either `*=` or `= *` error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)` - --> tests/ui/formatting.rs:26:6 + --> tests/ui/formatting.rs:23:6 | LL | b =! false; | ^^^^ @@ -25,17 +25,16 @@ LL | b =! false; = note: to remove this lint, use either `!=` or `= !` error: possibly missing a comma here - --> tests/ui/formatting.rs:38:19 + --> tests/ui/formatting.rs:35:19 | LL | -1, -2, -3 // <= no comma here | ^ | = note: to remove this lint, add a comma or write the expr in a single line - = note: `-D clippy::possible-missing-comma` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::possible_missing_comma)]` + = note: `#[deny(clippy::possible_missing_comma)]` on by default error: possibly missing a comma here - --> tests/ui/formatting.rs:45:19 + --> tests/ui/formatting.rs:42:19 | LL | -1, -2, -3 // <= no comma here | ^ @@ -43,7 +42,7 @@ LL | -1, -2, -3 // <= no comma here = note: to remove this lint, add a comma or write the expr in a single line error: possibly missing a comma here - --> tests/ui/formatting.rs:85:11 + --> tests/ui/formatting.rs:82:11 | LL | -1 | ^ diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed index 8618004efb89f..be98b22779584 100644 --- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed +++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed @@ -73,3 +73,46 @@ fn main() { for _i in [1, 2, 3].iter().collect::>() {} //~^ from_iter_instead_of_collect } + +fn issue14581() { + let nums = [0, 1, 2]; + let _ = &nums.iter().map(|&num| char::from_u32(num).unwrap()).collect::(); + //~^ from_iter_instead_of_collect +} + +fn test_implicit_generic_args(iter: impl Iterator + Copy) { + struct S<'l, T = i32, const A: usize = 3, const B: usize = 3> { + a: [&'l T; A], + b: [&'l T; B], + } + + impl<'l, T, const A: usize, const B: usize> FromIterator<&'l T> for S<'l, T, A, B> { + fn from_iter>(_: I) -> Self { + todo!() + } + } + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::>(); + //~^ from_iter_instead_of_collect + + let _ = iter.collect::(); + //~^ from_iter_instead_of_collect +} diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs index c46397e8ff560..ce20fef2ac337 100644 --- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs +++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs @@ -73,3 +73,46 @@ fn main() { for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {} //~^ from_iter_instead_of_collect } + +fn issue14581() { + let nums = [0, 1, 2]; + let _ = &String::from_iter(nums.iter().map(|&num| char::from_u32(num).unwrap())); + //~^ from_iter_instead_of_collect +} + +fn test_implicit_generic_args(iter: impl Iterator + Copy) { + struct S<'l, T = i32, const A: usize = 3, const B: usize = 3> { + a: [&'l T; A], + b: [&'l T; B], + } + + impl<'l, T, const A: usize, const B: usize> FromIterator<&'l T> for S<'l, T, A, B> { + fn from_iter>(_: I) -> Self { + todo!() + } + } + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = >::from_iter(iter); + //~^ from_iter_instead_of_collect + + let _ = ::from_iter(iter); + //~^ from_iter_instead_of_collect +} diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr index b46d97af152f6..ec11a375c0d87 100644 --- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr +++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr @@ -91,5 +91,59 @@ error: usage of `FromIterator::from_iter` LL | for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::>()` -error: aborting due to 15 previous errors +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:79:14 + | +LL | let _ = &String::from_iter(nums.iter().map(|&num| char::from_u32(num).unwrap())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `nums.iter().map(|&num| char::from_u32(num).unwrap()).collect::()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:95:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:98:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:101:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:104:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:107:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:110:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:113:13 + | +LL | let _ = >::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::>()` + +error: usage of `FromIterator::from_iter` + --> tests/ui/from_iter_instead_of_collect.rs:116:13 + | +LL | let _ = ::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.collect::()` + +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/from_over_into.fixed b/src/tools/clippy/tests/ui/from_over_into.fixed index 7d6780a0e02c8..7229e5a2d3589 100644 --- a/src/tools/clippy/tests/ui/from_over_into.fixed +++ b/src/tools/clippy/tests/ui/from_over_into.fixed @@ -105,4 +105,15 @@ fn issue_12138() { } } +fn issue_112502() { + struct MyInt(i64); + + impl From for i64 { + //~^ from_over_into + fn from(val: MyInt) -> Self { + val.0 + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/from_over_into.rs b/src/tools/clippy/tests/ui/from_over_into.rs index 387ddde359c16..9c75969c5c134 100644 --- a/src/tools/clippy/tests/ui/from_over_into.rs +++ b/src/tools/clippy/tests/ui/from_over_into.rs @@ -105,4 +105,15 @@ fn issue_12138() { } } +fn issue_112502() { + struct MyInt(i64); + + impl Into for MyInt { + //~^ from_over_into + fn into(self: MyInt) -> i64 { + self.0 + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/from_over_into.stderr b/src/tools/clippy/tests/ui/from_over_into.stderr index a564bccbaf71f..fe779544dd578 100644 --- a/src/tools/clippy/tests/ui/from_over_into.stderr +++ b/src/tools/clippy/tests/ui/from_over_into.stderr @@ -107,5 +107,21 @@ LL | LL ~ fn from(val: Hello) {} | -error: aborting due to 7 previous errors +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> tests/ui/from_over_into.rs:111:5 + | +LL | impl Into for MyInt { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence +help: replace the `Into` implementation with `From` + | +LL ~ impl From for i64 { +LL | +LL ~ fn from(val: MyInt) -> Self { +LL ~ val.0 + | + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/functions.rs b/src/tools/clippy/tests/ui/functions.rs index 9c1ca8bf93009..ceaba392dc200 100644 --- a/src/tools/clippy/tests/ui/functions.rs +++ b/src/tools/clippy/tests/ui/functions.rs @@ -1,5 +1,3 @@ -#![warn(clippy::all)] -#![allow(dead_code, unused_unsafe)] #![allow(clippy::missing_safety_doc, clippy::uninlined_format_args)] // TOO_MANY_ARGUMENTS diff --git a/src/tools/clippy/tests/ui/functions.stderr b/src/tools/clippy/tests/ui/functions.stderr index c8770023f77a0..65cc627cc44c1 100644 --- a/src/tools/clippy/tests/ui/functions.stderr +++ b/src/tools/clippy/tests/ui/functions.stderr @@ -1,5 +1,5 @@ error: this function has too many arguments (8/7) - --> tests/ui/functions.rs:8:1 + --> tests/ui/functions.rs:6:1 | LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f = help: to override `-D warnings` add `#[allow(clippy::too_many_arguments)]` error: this function has too many arguments (8/7) - --> tests/ui/functions.rs:12:1 + --> tests/ui/functions.rs:10:1 | LL | / fn bad_multiline( LL | | @@ -20,88 +20,87 @@ LL | | ) { | |_^ error: this function has too many arguments (8/7) - --> tests/ui/functions.rs:48:5 + --> tests/ui/functions.rs:46:5 | LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this function has too many arguments (8/7) - --> tests/ui/functions.rs:58:5 + --> tests/ui/functions.rs:56:5 | LL | fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:68:34 + --> tests/ui/functions.rs:66:34 | LL | println!("{}", unsafe { *p }); | ^ | - = note: `-D clippy::not-unsafe-ptr-arg-deref` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::not_unsafe_ptr_arg_deref)]` + = note: `#[deny(clippy::not_unsafe_ptr_arg_deref)]` on by default error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:71:35 + --> tests/ui/functions.rs:69:35 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:74:33 + --> tests/ui/functions.rs:72:33 | LL | unsafe { std::ptr::read(p) }; | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:86:30 + --> tests/ui/functions.rs:84:30 | LL | println!("{}", unsafe { *p }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:89:31 + --> tests/ui/functions.rs:87:31 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:92:29 + --> tests/ui/functions.rs:90:29 | LL | unsafe { std::ptr::read(p) }; | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:99:30 + --> tests/ui/functions.rs:97:30 | LL | println!("{}", unsafe { *p }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:102:31 + --> tests/ui/functions.rs:100:31 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:105:29 + --> tests/ui/functions.rs:103:29 | LL | unsafe { std::ptr::read(p) }; | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:115:34 + --> tests/ui/functions.rs:113:34 | LL | println!("{}", unsafe { *p }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:118:35 + --> tests/ui/functions.rs:116:35 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ error: this public function might dereference a raw pointer but is not marked `unsafe` - --> tests/ui/functions.rs:121:33 + --> tests/ui/functions.rs:119:33 | LL | unsafe { std::ptr::read(p) }; | ^ diff --git a/src/tools/clippy/tests/ui/if_not_else.fixed b/src/tools/clippy/tests/ui/if_not_else.fixed index d26a15156cd89..4e6f43e5671e8 100644 --- a/src/tools/clippy/tests/ui/if_not_else.fixed +++ b/src/tools/clippy/tests/ui/if_not_else.fixed @@ -1,4 +1,3 @@ -#![warn(clippy::all)] #![warn(clippy::if_not_else)] fn foo() -> bool { diff --git a/src/tools/clippy/tests/ui/if_not_else.rs b/src/tools/clippy/tests/ui/if_not_else.rs index 6171cf1164955..6cd2e3bd63fe9 100644 --- a/src/tools/clippy/tests/ui/if_not_else.rs +++ b/src/tools/clippy/tests/ui/if_not_else.rs @@ -1,4 +1,3 @@ -#![warn(clippy::all)] #![warn(clippy::if_not_else)] fn foo() -> bool { diff --git a/src/tools/clippy/tests/ui/if_not_else.stderr b/src/tools/clippy/tests/ui/if_not_else.stderr index f44dd0aabc863..824837bd52bb1 100644 --- a/src/tools/clippy/tests/ui/if_not_else.stderr +++ b/src/tools/clippy/tests/ui/if_not_else.stderr @@ -1,5 +1,5 @@ error: unnecessary boolean `not` operation - --> tests/ui/if_not_else.rs:12:5 + --> tests/ui/if_not_else.rs:11:5 | LL | / if !bla() { LL | | @@ -24,7 +24,7 @@ LL + } | error: unnecessary `!=` operation - --> tests/ui/if_not_else.rs:19:5 + --> tests/ui/if_not_else.rs:18:5 | LL | / if 4 != 5 { LL | | @@ -47,7 +47,7 @@ LL + } | error: unnecessary boolean `not` operation - --> tests/ui/if_not_else.rs:34:5 + --> tests/ui/if_not_else.rs:33:5 | LL | / if !(foo() && bla()) { LL | | @@ -79,7 +79,7 @@ LL + } | error: unnecessary boolean `not` operation - --> tests/ui/if_not_else.rs:53:5 + --> tests/ui/if_not_else.rs:52:5 | LL | / if !foo() { LL | | @@ -102,7 +102,7 @@ LL + } | error: unnecessary boolean `not` operation - --> tests/ui/if_not_else.rs:61:5 + --> tests/ui/if_not_else.rs:60:5 | LL | / if !bla() { LL | | @@ -125,7 +125,7 @@ LL + } | error: unnecessary boolean `not` operation - --> tests/ui/if_not_else.rs:72:5 + --> tests/ui/if_not_else.rs:71:5 | LL | / if !foo() { LL | | diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.rs b/src/tools/clippy/tests/ui/ifs_same_cond.rs index 6ecb7cb1eba9d..7067434953d6e 100644 --- a/src/tools/clippy/tests/ui/ifs_same_cond.rs +++ b/src/tools/clippy/tests/ui/ifs_same_cond.rs @@ -1,29 +1,30 @@ #![warn(clippy::ifs_same_cond)] -#![allow( - clippy::if_same_then_else, - clippy::comparison_chain, - clippy::needless_if, - clippy::needless_else -)] // all empty blocks +#![allow(clippy::if_same_then_else, clippy::needless_if, clippy::needless_else)] // all empty blocks fn ifs_same_cond() { let a = 0; let b = false; if b { + //~^ ifs_same_cond } else if b { + } + + if b { //~^ ifs_same_cond + } else if b { + } else if b { } if a == 1 { - } else if a == 1 { //~^ ifs_same_cond + } else if a == 1 { } if 2 * a == 1 { + //~^ ifs_same_cond } else if 2 * a == 2 { } else if 2 * a == 1 { - //~^ ifs_same_cond } else if a == 1 { } @@ -55,8 +56,8 @@ fn ifs_same_cond() { fn issue10272() { let a = String::from("ha"); if a.contains("ah") { - } else if a.contains("ah") { //~^ ifs_same_cond + } else if a.contains("ah") { // Trigger this lint } else if a.contains("ha") { diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.stderr b/src/tools/clippy/tests/ui/ifs_same_cond.stderr index 81fbb921e8463..7acbc1a6399b6 100644 --- a/src/tools/clippy/tests/ui/ifs_same_cond.stderr +++ b/src/tools/clippy/tests/ui/ifs_same_cond.stderr @@ -1,52 +1,52 @@ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:14:15 +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:8:8 | +LL | if b { + | ^ +LL | LL | } else if b { | ^ | -note: same as this + = note: `-D clippy::ifs-same-cond` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]` + +error: these `if` branches have the same condition --> tests/ui/ifs_same_cond.rs:13:8 | LL | if b { | ^ - = note: `-D clippy::ifs-same-cond` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]` +LL | +LL | } else if b { + | ^ +LL | } else if b { + | ^ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:19:15 - | -LL | } else if a == 1 { - | ^^^^^^ - | -note: same as this - --> tests/ui/ifs_same_cond.rs:18:8 +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:19:8 | LL | if a == 1 { | ^^^^^^ +LL | +LL | } else if a == 1 { + | ^^^^^^ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:25:15 - | -LL | } else if 2 * a == 1 { - | ^^^^^^^^^^ - | -note: same as this - --> tests/ui/ifs_same_cond.rs:23:8 +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:24:8 | LL | if 2 * a == 1 { | ^^^^^^^^^^ +... +LL | } else if 2 * a == 1 { + | ^^^^^^^^^^ -error: this `if` has the same condition as a previous `if` - --> tests/ui/ifs_same_cond.rs:58:15 - | -LL | } else if a.contains("ah") { - | ^^^^^^^^^^^^^^^^ - | -note: same as this - --> tests/ui/ifs_same_cond.rs:57:8 +error: these `if` branches have the same condition + --> tests/ui/ifs_same_cond.rs:58:8 | LL | if a.contains("ah") { | ^^^^^^^^^^^^^^^^ +LL | +LL | } else if a.contains("ah") { + | ^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/ignore_without_reason.rs b/src/tools/clippy/tests/ui/ignore_without_reason.rs new file mode 100644 index 0000000000000..53ac34c27248e --- /dev/null +++ b/src/tools/clippy/tests/ui/ignore_without_reason.rs @@ -0,0 +1,14 @@ +#![warn(clippy::ignore_without_reason)] + +fn main() {} + +#[test] +fn unignored_test() {} + +#[test] +#[ignore = "Some good reason"] +fn ignored_with_reason() {} + +#[test] +#[ignore] //~ ignore_without_reason +fn ignored_without_reason() {} diff --git a/src/tools/clippy/tests/ui/ignore_without_reason.stderr b/src/tools/clippy/tests/ui/ignore_without_reason.stderr new file mode 100644 index 0000000000000..4c0210c2bbc08 --- /dev/null +++ b/src/tools/clippy/tests/ui/ignore_without_reason.stderr @@ -0,0 +1,12 @@ +error: `#[ignore]` without reason + --> tests/ui/ignore_without_reason.rs:13:1 + | +LL | #[ignore] + | ^^^^^^^^^ + | + = help: add a reason with `= ".."` + = note: `-D clippy::ignore-without-reason` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ignore_without_reason)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/impl_hash_with_borrow_str_and_bytes.stderr b/src/tools/clippy/tests/ui/impl_hash_with_borrow_str_and_bytes.stderr index 7328f563ce13d..c327ce3f7ca12 100644 --- a/src/tools/clippy/tests/ui/impl_hash_with_borrow_str_and_bytes.stderr +++ b/src/tools/clippy/tests/ui/impl_hash_with_borrow_str_and_bytes.stderr @@ -23,7 +23,6 @@ LL | #[derive(Hash)] = note: ... as (`hash("abc") != hash("abc".as_bytes())` = help: consider either removing one of the `Borrow` implementations (`Borrow` or `Borrow<[u8]>`) ... = help: ... or not implementing `Hash` for this type - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: the semantics of `Borrow` around `Hash` can't be satisfied when both `Borrow` and `Borrow<[u8]>` are implemented --> tests/ui/impl_hash_with_borrow_str_and_bytes.rs:117:6 diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.rs b/src/tools/clippy/tests/ui/impl_trait_in_params.rs index 2039f6339a878..72e3e068c9c79 100644 --- a/src/tools/clippy/tests/ui/impl_trait_in_params.rs +++ b/src/tools/clippy/tests/ui/impl_trait_in_params.rs @@ -1,7 +1,7 @@ #![allow(unused)] #![warn(clippy::impl_trait_in_params)] -//@no-rustfix +//@no-rustfix: has placeholders pub trait Trait {} pub trait AnotherTrait {} diff --git a/src/tools/clippy/tests/ui/implicit_return.fixed b/src/tools/clippy/tests/ui/implicit_return.fixed index 1cb639b60a9af..728c6e015c155 100644 --- a/src/tools/clippy/tests/ui/implicit_return.fixed +++ b/src/tools/clippy/tests/ui/implicit_return.fixed @@ -165,3 +165,46 @@ with_span!( x } ); + +fn desugared_closure_14446() { + let _ = async || return 0; + //~^ implicit_return + #[rustfmt::skip] + let _ = async || -> i32 { return 0 }; + //~^ implicit_return + let _ = async |a: i32| return a; + //~^ implicit_return + #[rustfmt::skip] + let _ = async |a: i32| { return a }; + //~^ implicit_return + + let _ = async || return 0; + let _ = async || -> i32 { return 0 }; + let _ = async |a: i32| return a; + #[rustfmt::skip] + let _ = async |a: i32| { return a; }; + + let _ = async || return foo().await; + //~^ implicit_return + let _ = async || { + foo().await; + return foo().await + }; + //~^^ implicit_return + #[rustfmt::skip] + let _ = async || { return foo().await }; + //~^ implicit_return + let _ = async || -> bool { return foo().await }; + //~^ implicit_return + + let _ = async || return foo().await; + let _ = async || { + foo().await; + return foo().await; + }; + #[rustfmt::skip] + let _ = async || { return foo().await; }; + let _ = async || -> bool { + return foo().await; + }; +} diff --git a/src/tools/clippy/tests/ui/implicit_return.rs b/src/tools/clippy/tests/ui/implicit_return.rs index 99d75e4987e47..3381fffb6e450 100644 --- a/src/tools/clippy/tests/ui/implicit_return.rs +++ b/src/tools/clippy/tests/ui/implicit_return.rs @@ -165,3 +165,46 @@ with_span!( x } ); + +fn desugared_closure_14446() { + let _ = async || 0; + //~^ implicit_return + #[rustfmt::skip] + let _ = async || -> i32 { 0 }; + //~^ implicit_return + let _ = async |a: i32| a; + //~^ implicit_return + #[rustfmt::skip] + let _ = async |a: i32| { a }; + //~^ implicit_return + + let _ = async || return 0; + let _ = async || -> i32 { return 0 }; + let _ = async |a: i32| return a; + #[rustfmt::skip] + let _ = async |a: i32| { return a; }; + + let _ = async || foo().await; + //~^ implicit_return + let _ = async || { + foo().await; + foo().await + }; + //~^^ implicit_return + #[rustfmt::skip] + let _ = async || { foo().await }; + //~^ implicit_return + let _ = async || -> bool { foo().await }; + //~^ implicit_return + + let _ = async || return foo().await; + let _ = async || { + foo().await; + return foo().await; + }; + #[rustfmt::skip] + let _ = async || { return foo().await; }; + let _ = async || -> bool { + return foo().await; + }; +} diff --git a/src/tools/clippy/tests/ui/implicit_return.stderr b/src/tools/clippy/tests/ui/implicit_return.stderr index 02044df47ac3c..05cd7f62583b1 100644 --- a/src/tools/clippy/tests/ui/implicit_return.stderr +++ b/src/tools/clippy/tests/ui/implicit_return.stderr @@ -183,5 +183,93 @@ help: add `return` as shown LL | return true | ++++++ -error: aborting due to 16 previous errors +error: missing `return` statement + --> tests/ui/implicit_return.rs:170:22 + | +LL | let _ = async || 0; + | ^ + | +help: add `return` as shown + | +LL | let _ = async || return 0; + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:173:31 + | +LL | let _ = async || -> i32 { 0 }; + | ^ + | +help: add `return` as shown + | +LL | let _ = async || -> i32 { return 0 }; + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:175:28 + | +LL | let _ = async |a: i32| a; + | ^ + | +help: add `return` as shown + | +LL | let _ = async |a: i32| return a; + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:178:30 + | +LL | let _ = async |a: i32| { a }; + | ^ + | +help: add `return` as shown + | +LL | let _ = async |a: i32| { return a }; + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:187:22 + | +LL | let _ = async || foo().await; + | ^^^^^ + | +help: add `return` as shown + | +LL | let _ = async || return foo().await; + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:191:9 + | +LL | foo().await + | ^^^^^ + | +help: add `return` as shown + | +LL | return foo().await + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:195:24 + | +LL | let _ = async || { foo().await }; + | ^^^^^ + | +help: add `return` as shown + | +LL | let _ = async || { return foo().await }; + | ++++++ + +error: missing `return` statement + --> tests/ui/implicit_return.rs:197:32 + | +LL | let _ = async || -> bool { foo().await }; + | ^^^^^ + | +help: add `return` as shown + | +LL | let _ = async || -> bool { return foo().await }; + | ++++++ + +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed index 136238f9ecacc..1aab6c54407e6 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed @@ -228,3 +228,27 @@ fn regression_13524(a: usize, b: usize, c: bool) -> usize { 123 } else { b.saturating_sub(a) } } + +fn with_side_effect(a: u64) -> u64 { + println!("a = {a}"); + a +} + +fn arbitrary_expression() { + let (a, b) = (15u64, 20u64); + + let _ = (a * 2).saturating_sub(b); + //~^ implicit_saturating_sub + + let _ = a.saturating_sub(b * 2); + //~^ implicit_saturating_sub + + let _ = a.saturating_sub(b * 2); + //~^ implicit_saturating_sub + + let _ = if with_side_effect(a) > a { + with_side_effect(a) - a + } else { + 0 + }; +} diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs index 140cf0338602d..7ca57a2902db8 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs @@ -302,3 +302,27 @@ fn regression_13524(a: usize, b: usize, c: bool) -> usize { b - a } } + +fn with_side_effect(a: u64) -> u64 { + println!("a = {a}"); + a +} + +fn arbitrary_expression() { + let (a, b) = (15u64, 20u64); + + let _ = if a * 2 > b { a * 2 - b } else { 0 }; + //~^ implicit_saturating_sub + + let _ = if a > b * 2 { a - b * 2 } else { 0 }; + //~^ implicit_saturating_sub + + let _ = if a < b * 2 { 0 } else { a - b * 2 }; + //~^ implicit_saturating_sub + + let _ = if with_side_effect(a) > a { + with_side_effect(a) - a + } else { + 0 + }; +} diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr index f9c94d3ad8416..0c225856fd07c 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr @@ -220,5 +220,23 @@ LL | | b - a LL | | } | |_____^ help: replace it with: `{ b.saturating_sub(a) }` -error: aborting due to 24 previous errors +error: manual arithmetic check found + --> tests/ui/implicit_saturating_sub.rs:314:13 + | +LL | let _ = if a * 2 > b { a * 2 - b } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `(a * 2).saturating_sub(b)` + +error: manual arithmetic check found + --> tests/ui/implicit_saturating_sub.rs:317:13 + | +LL | let _ = if a > b * 2 { a - b * 2 } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b * 2)` + +error: manual arithmetic check found + --> tests/ui/implicit_saturating_sub.rs:320:13 + | +LL | let _ = if a < b * 2 { 0 } else { a - b * 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b * 2)` + +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed b/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed index bac7af59491f1..4fe3fa4eab516 100644 --- a/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed +++ b/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed @@ -192,6 +192,7 @@ impl Atpit for () { type Tait = impl DerefMut; //~^ implied_bounds_in_impls +#[define_opaque(Tait)] fn define() -> Tait { &mut [] as &mut [()] } diff --git a/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs b/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs index 2014cd46ada7b..6cc824db11072 100644 --- a/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs +++ b/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs @@ -192,6 +192,7 @@ impl Atpit for () { type Tait = impl Deref + DerefMut; //~^ implied_bounds_in_impls +#[define_opaque(Tait)] fn define() -> Tait { &mut [] as &mut [()] } diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.rs b/src/tools/clippy/tests/ui/incompatible_msrv.rs index b4fea4cae5edf..99101b2bb8f26 100644 --- a/src/tools/clippy/tests/ui/incompatible_msrv.rs +++ b/src/tools/clippy/tests/ui/incompatible_msrv.rs @@ -1,5 +1,6 @@ #![warn(clippy::incompatible_msrv)] #![feature(custom_inner_attributes)] +#![feature(panic_internals)] #![clippy::msrv = "1.3.0"] use std::collections::HashMap; @@ -34,4 +35,42 @@ async fn issue12273(v: impl Future) { v.await; } +fn core_special_treatment(p: bool) { + // Do not lint code coming from `core` macros expanding into `core` function calls + if p { + panic!("foo"); // Do not lint + } + + // But still lint code calling `core` functions directly + if p { + core::panicking::panic("foo"); + //~^ ERROR: is `1.3.0` but this item is stable since `1.6.0` + } + + // Lint code calling `core` from non-`core` macros + macro_rules! my_panic { + ($msg:expr) => { + core::panicking::panic($msg) + }; //~^ ERROR: is `1.3.0` but this item is stable since `1.6.0` + } + my_panic!("foo"); + + // Lint even when the macro comes from `core` and calls `core` functions + assert!(core::panicking::panic("out of luck")); + //~^ ERROR: is `1.3.0` but this item is stable since `1.6.0` +} + +#[clippy::msrv = "1.26.0"] +fn lang_items() { + // Do not lint lang items. `..=` will expand into `RangeInclusive::new()`, which was introduced + // in Rust 1.27.0. + let _ = 1..=3; +} + +#[clippy::msrv = "1.80.0"] +fn issue14212() { + let _ = std::iter::repeat_n((), 5); + //~^ ERROR: is `1.80.0` but this item is stable since `1.82.0` +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.stderr b/src/tools/clippy/tests/ui/incompatible_msrv.stderr index 56c9eae5aafa7..5ea2bb9cc58b1 100644 --- a/src/tools/clippy/tests/ui/incompatible_msrv.stderr +++ b/src/tools/clippy/tests/ui/incompatible_msrv.stderr @@ -1,5 +1,5 @@ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.10.0` - --> tests/ui/incompatible_msrv.rs:13:39 + --> tests/ui/incompatible_msrv.rs:14:39 | LL | assert_eq!(map.entry("poneyland").key(), &"poneyland"); | ^^^^^ @@ -8,16 +8,45 @@ LL | assert_eq!(map.entry("poneyland").key(), &"poneyland"); = help: to override `-D warnings` add `#[allow(clippy::incompatible_msrv)]` error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.12.0` - --> tests/ui/incompatible_msrv.rs:17:11 + --> tests/ui/incompatible_msrv.rs:18:11 | LL | v.into_key(); | ^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.4.0` - --> tests/ui/incompatible_msrv.rs:21:5 + --> tests/ui/incompatible_msrv.rs:22:5 | LL | sleep(Duration::new(1, 0)); | ^^^^^ -error: aborting due to 3 previous errors +error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.6.0` + --> tests/ui/incompatible_msrv.rs:46:9 + | +LL | core::panicking::panic("foo"); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.6.0` + --> tests/ui/incompatible_msrv.rs:53:13 + | +LL | core::panicking::panic($msg) + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | my_panic!("foo"); + | ---------------- in this macro invocation + | + = note: this error originates in the macro `my_panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.6.0` + --> tests/ui/incompatible_msrv.rs:59:13 + | +LL | assert!(core::panicking::panic("out of luck")); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: current MSRV (Minimum Supported Rust Version) is `1.80.0` but this item is stable since `1.82.0` + --> tests/ui/incompatible_msrv.rs:72:13 + | +LL | let _ = std::iter::repeat_n((), 5); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed index e19aa4acb4c12..050cdfcba966e 100644 --- a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed +++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.fixed @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)] +#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs index 2903935685541..91429bfea2762 100644 --- a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes)] +#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), diff --git a/src/tools/clippy/tests/ui/infinite_loop.rs b/src/tools/clippy/tests/ui/infinite_loop.rs index 4a0968918bfbe..8ff7f3b0c18d8 100644 --- a/src/tools/clippy/tests/ui/infinite_loop.rs +++ b/src/tools/clippy/tests/ui/infinite_loop.rs @@ -1,5 +1,3 @@ -//@no-rustfix - fn fn_val(i: i32) -> i32 { unimplemented!() } diff --git a/src/tools/clippy/tests/ui/infinite_loop.stderr b/src/tools/clippy/tests/ui/infinite_loop.stderr index 7ba1374d64f46..04da9776c3025 100644 --- a/src/tools/clippy/tests/ui/infinite_loop.stderr +++ b/src/tools/clippy/tests/ui/infinite_loop.stderr @@ -1,5 +1,5 @@ error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:22:11 + --> tests/ui/infinite_loop.rs:20:11 | LL | while y < 10 { | ^^^^^^ @@ -8,7 +8,7 @@ LL | while y < 10 { = note: `#[deny(clippy::while_immutable_condition)]` on by default error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:29:11 + --> tests/ui/infinite_loop.rs:27:11 | LL | while y < 10 && x < 3 { | ^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | while y < 10 && x < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:38:11 + --> tests/ui/infinite_loop.rs:36:11 | LL | while !cond { | ^^^^^ @@ -24,7 +24,7 @@ LL | while !cond { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:84:11 + --> tests/ui/infinite_loop.rs:82:11 | LL | while i < 3 { | ^^^^^ @@ -32,7 +32,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:91:11 + --> tests/ui/infinite_loop.rs:89:11 | LL | while i < 3 && j > 0 { | ^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | while i < 3 && j > 0 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:97:11 + --> tests/ui/infinite_loop.rs:95:11 | LL | while i < 3 { | ^^^^^ @@ -48,7 +48,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:114:11 + --> tests/ui/infinite_loop.rs:112:11 | LL | while i < 3 { | ^^^^^ @@ -56,7 +56,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:121:11 + --> tests/ui/infinite_loop.rs:119:11 | LL | while i < 3 { | ^^^^^ @@ -64,7 +64,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:189:15 + --> tests/ui/infinite_loop.rs:187:15 | LL | while self.count < n { | ^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | while self.count < n { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:199:11 + --> tests/ui/infinite_loop.rs:197:11 | LL | while y < 10 { | ^^^^^^ @@ -82,7 +82,7 @@ LL | while y < 10 { = help: rewrite it as `if cond { loop { } }` error: variables in the condition are not mutated in the loop body - --> tests/ui/infinite_loop.rs:208:11 + --> tests/ui/infinite_loop.rs:206:11 | LL | while y < 10 { | ^^^^^^ diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs index eaa8d00880682..fcd1f795fff09 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.rs +++ b/src/tools/clippy/tests/ui/infinite_loops.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: multiple suggestions add `-> !` to the same fn //@aux-build:proc_macros.rs #![allow(clippy::never_loop)] diff --git a/src/tools/clippy/tests/ui/integer_division.rs b/src/tools/clippy/tests/ui/integer_division.rs index 632fedc9e8fe3..a40956e2672e3 100644 --- a/src/tools/clippy/tests/ui/integer_division.rs +++ b/src/tools/clippy/tests/ui/integer_division.rs @@ -1,5 +1,9 @@ #![warn(clippy::integer_division)] +use std::num::NonZeroU32; + +const TWO: NonZeroU32 = NonZeroU32::new(2).unwrap(); + fn main() { let two = 2; let n = 1 / 2; @@ -12,4 +16,8 @@ fn main() { //~^ integer_division let x = 1. / 2.0; + + let a = 1; + let s = a / TWO; + //~^ integer_division } diff --git a/src/tools/clippy/tests/ui/integer_division.stderr b/src/tools/clippy/tests/ui/integer_division.stderr index 0fe2021a1a9ca..c0e34a562f652 100644 --- a/src/tools/clippy/tests/ui/integer_division.stderr +++ b/src/tools/clippy/tests/ui/integer_division.stderr @@ -1,5 +1,5 @@ error: integer division - --> tests/ui/integer_division.rs:5:13 + --> tests/ui/integer_division.rs:9:13 | LL | let n = 1 / 2; | ^^^^^ @@ -9,7 +9,7 @@ LL | let n = 1 / 2; = help: to override `-D warnings` add `#[allow(clippy::integer_division)]` error: integer division - --> tests/ui/integer_division.rs:8:13 + --> tests/ui/integer_division.rs:12:13 | LL | let o = 1 / two; | ^^^^^^^ @@ -17,12 +17,20 @@ LL | let o = 1 / two; = help: division of integers may cause loss of precision. consider using floats error: integer division - --> tests/ui/integer_division.rs:11:13 + --> tests/ui/integer_division.rs:15:13 | LL | let p = two / 4; | ^^^^^^^ | = help: division of integers may cause loss of precision. consider using floats -error: aborting due to 3 previous errors +error: integer division + --> tests/ui/integer_division.rs:21:13 + | +LL | let s = a / TWO; + | ^^^^^^^ + | + = help: division of integers may cause loss of precision. consider using floats + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/into_iter_without_iter.rs b/src/tools/clippy/tests/ui/into_iter_without_iter.rs index 45e34b3930ad0..f0b86e5620e9b 100644 --- a/src/tools/clippy/tests/ui/into_iter_without_iter.rs +++ b/src/tools/clippy/tests/ui/into_iter_without_iter.rs @@ -1,4 +1,4 @@ -//@no-rustfix +//@no-rustfix: suggestions reference out of scope lifetimes/types //@aux-build:proc_macros.rs #![warn(clippy::into_iter_without_iter)] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed deleted file mode 100644 index ce78e89ee829c..0000000000000 --- a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed +++ /dev/null @@ -1,66 +0,0 @@ -fn main() { - unsafe { - let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - struct A; // zero sized struct - assert_eq!(std::mem::size_of::(), 0); - - let _a: A = std::ptr::read(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::read_unaligned(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read_unaligned(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::read_volatile(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read_volatile(std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::replace(std::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); // shouldn't lint - let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); - - std::ptr::swap::(std::ptr::NonNull::dangling().as_ptr(), &mut A); - //~^ invalid_null_ptr_usage - std::ptr::swap::(&mut A, std::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - std::ptr::swap_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), &mut A, 0); - //~^ invalid_null_ptr_usage - std::ptr::swap_nonoverlapping::(&mut A, std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::write(std::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_unaligned(std::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_volatile(std::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_bytes::(std::ptr::NonNull::dangling().as_ptr(), 42, 0); - //~^ invalid_null_ptr_usage - } -} diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs deleted file mode 100644 index 361865fbd9601..0000000000000 --- a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs +++ /dev/null @@ -1,66 +0,0 @@ -fn main() { - unsafe { - let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); - //~^ invalid_null_ptr_usage - let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::copy::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::copy_nonoverlapping::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - struct A; // zero sized struct - assert_eq!(std::mem::size_of::(), 0); - - let _a: A = std::ptr::read(std::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read(std::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::read_unaligned(std::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::read_volatile(std::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = std::ptr::replace(std::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); // shouldn't lint - let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); - - std::ptr::swap::(std::ptr::null_mut(), &mut A); - //~^ invalid_null_ptr_usage - std::ptr::swap::(&mut A, std::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); - //~^ invalid_null_ptr_usage - std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - std::ptr::write(std::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_unaligned(std::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_volatile(std::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); - //~^ invalid_null_ptr_usage - } -} diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr deleted file mode 100644 index 3f9d15b904018..0000000000000 --- a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr +++ /dev/null @@ -1,136 +0,0 @@ -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:3:59 - | -LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - | - = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:5:59 - | -LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:8:63 - | -LL | let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:11:33 - | -LL | std::ptr::copy::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:13:73 - | -LL | std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:16:48 - | -LL | std::ptr::copy_nonoverlapping::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:18:88 - | -LL | std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:24:36 - | -LL | let _a: A = std::ptr::read(std::ptr::null()); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:26:36 - | -LL | let _a: A = std::ptr::read(std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:29:46 - | -LL | let _a: A = std::ptr::read_unaligned(std::ptr::null()); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:31:46 - | -LL | let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:34:45 - | -LL | let _a: A = std::ptr::read_volatile(std::ptr::null()); - | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:36:45 - | -LL | let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:39:39 - | -LL | let _a: A = std::ptr::replace(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:44:29 - | -LL | std::ptr::swap::(std::ptr::null_mut(), &mut A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:46:37 - | -LL | std::ptr::swap::(&mut A, std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:49:44 - | -LL | std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:51:52 - | -LL | std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:54:25 - | -LL | std::ptr::write(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:57:35 - | -LL | std::ptr::write_unaligned(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:60:34 - | -LL | std::ptr::write_volatile(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage.rs:63:40 - | -LL | std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` - -error: aborting due to 22 previous errors - diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.fixed b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.fixed deleted file mode 100644 index df7ab166187de..0000000000000 --- a/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.fixed +++ /dev/null @@ -1,79 +0,0 @@ -#![no_std] -#![feature(lang_items)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn main() { - unsafe { - let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - core::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - core::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - struct A; // zero sized struct - assert_eq!(core::mem::size_of::(), 0); - - let _a: A = core::ptr::read(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - let _slice: *const [usize] = core::ptr::slice_from_raw_parts(core::ptr::null_mut(), 0); // shouldn't lint - let _slice: *const [usize] = core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0); - - core::ptr::swap::(core::ptr::NonNull::dangling().as_ptr(), &mut A); - //~^ invalid_null_ptr_usage - core::ptr::swap::(&mut A, core::ptr::NonNull::dangling().as_ptr()); - //~^ invalid_null_ptr_usage - - core::ptr::swap_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0); - //~^ invalid_null_ptr_usage - core::ptr::swap_nonoverlapping::(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_bytes::(core::ptr::NonNull::dangling().as_ptr(), 42, 0); - //~^ invalid_null_ptr_usage - } -} diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.rs b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.rs deleted file mode 100644 index 38ddfff055353..0000000000000 --- a/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.rs +++ /dev/null @@ -1,79 +0,0 @@ -#![no_std] -#![feature(lang_items)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn main() { - unsafe { - let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null(), 0); - //~^ invalid_null_ptr_usage - let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::copy::(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - core::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::copy_nonoverlapping::(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); - //~^ invalid_null_ptr_usage - core::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - struct A; // zero sized struct - assert_eq!(core::mem::size_of::(), 0); - - let _a: A = core::ptr::read(core::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read(core::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::read_unaligned(core::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read_unaligned(core::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::read_volatile(core::ptr::null()); - //~^ invalid_null_ptr_usage - let _a: A = core::ptr::read_volatile(core::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - let _a: A = core::ptr::replace(core::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - let _slice: *const [usize] = core::ptr::slice_from_raw_parts(core::ptr::null_mut(), 0); // shouldn't lint - let _slice: *const [usize] = core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0); - - core::ptr::swap::(core::ptr::null_mut(), &mut A); - //~^ invalid_null_ptr_usage - core::ptr::swap::(&mut A, core::ptr::null_mut()); - //~^ invalid_null_ptr_usage - - core::ptr::swap_nonoverlapping::(core::ptr::null_mut(), &mut A, 0); - //~^ invalid_null_ptr_usage - core::ptr::swap_nonoverlapping::(&mut A, core::ptr::null_mut(), 0); - //~^ invalid_null_ptr_usage - - core::ptr::write(core::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_unaligned(core::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_volatile(core::ptr::null_mut(), A); - //~^ invalid_null_ptr_usage - - core::ptr::write_bytes::(core::ptr::null_mut(), 42, 0); - //~^ invalid_null_ptr_usage - } -} diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.stderr b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.stderr deleted file mode 100644 index b5dd21ce6248f..0000000000000 --- a/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.stderr +++ /dev/null @@ -1,136 +0,0 @@ -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:16:60 - | -LL | let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null(), 0); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - | - = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:18:60 - | -LL | let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:21:64 - | -LL | let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:24:34 - | -LL | core::ptr::copy::(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:26:75 - | -LL | core::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:29:49 - | -LL | core::ptr::copy_nonoverlapping::(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:31:90 - | -LL | core::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:37:37 - | -LL | let _a: A = core::ptr::read(core::ptr::null()); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:39:37 - | -LL | let _a: A = core::ptr::read(core::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:42:47 - | -LL | let _a: A = core::ptr::read_unaligned(core::ptr::null()); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:44:47 - | -LL | let _a: A = core::ptr::read_unaligned(core::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:47:46 - | -LL | let _a: A = core::ptr::read_volatile(core::ptr::null()); - | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:49:46 - | -LL | let _a: A = core::ptr::read_volatile(core::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:52:40 - | -LL | let _a: A = core::ptr::replace(core::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:57:30 - | -LL | core::ptr::swap::(core::ptr::null_mut(), &mut A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:59:38 - | -LL | core::ptr::swap::(&mut A, core::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:62:45 - | -LL | core::ptr::swap_nonoverlapping::(core::ptr::null_mut(), &mut A, 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:64:53 - | -LL | core::ptr::swap_nonoverlapping::(&mut A, core::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:67:26 - | -LL | core::ptr::write(core::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:70:36 - | -LL | core::ptr::write_unaligned(core::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:73:35 - | -LL | core::ptr::write_volatile(core::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: pointer must be non-null - --> tests/ui/invalid_null_ptr_usage_no_std.rs:76:41 - | -LL | core::ptr::write_bytes::(core::ptr::null_mut(), 42, 0); - | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` - -error: aborting due to 22 previous errors - diff --git a/src/tools/clippy/tests/ui/io_other_error.fixed b/src/tools/clippy/tests/ui/io_other_error.fixed index 0054c56fb6282..ce7e8ef281f70 100644 --- a/src/tools/clippy/tests/ui/io_other_error.fixed +++ b/src/tools/clippy/tests/ui/io_other_error.fixed @@ -53,3 +53,8 @@ mod paths { fn under_msrv() { let _err = std::io::Error::new(std::io::ErrorKind::Other, E); } + +pub fn issue14346(x: i32) -> std::io::Error { + std::io::Error::other(format!("{x}")) + //~^ ERROR: this can be `std::io::Error::other(_)` +} diff --git a/src/tools/clippy/tests/ui/io_other_error.rs b/src/tools/clippy/tests/ui/io_other_error.rs index 8529fb9a77f98..b66e7f88ab143 100644 --- a/src/tools/clippy/tests/ui/io_other_error.rs +++ b/src/tools/clippy/tests/ui/io_other_error.rs @@ -53,3 +53,8 @@ mod paths { fn under_msrv() { let _err = std::io::Error::new(std::io::ErrorKind::Other, E); } + +pub fn issue14346(x: i32) -> std::io::Error { + std::io::Error::new(std::io::ErrorKind::Other, format!("{x}")) + //~^ ERROR: this can be `std::io::Error::other(_)` +} diff --git a/src/tools/clippy/tests/ui/io_other_error.stderr b/src/tools/clippy/tests/ui/io_other_error.stderr index e79e05ecd406b..b37e3d15064f5 100644 --- a/src/tools/clippy/tests/ui/io_other_error.stderr +++ b/src/tools/clippy/tests/ui/io_other_error.stderr @@ -48,5 +48,17 @@ LL - let _err = io::Error::new(io::ErrorKind::Other, super::E); LL + let _err = io::Error::other(super::E); | -error: aborting due to 4 previous errors +error: this can be `std::io::Error::other(_)` + --> tests/ui/io_other_error.rs:58:5 + | +LL | std::io::Error::new(std::io::ErrorKind::Other, format!("{x}")) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `std::io::Error::other` + | +LL - std::io::Error::new(std::io::ErrorKind::Other, format!("{x}")) +LL + std::io::Error::other(format!("{x}")) + | + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/issue-7447.stderr b/src/tools/clippy/tests/ui/issue-7447.stderr index 5e28c1423840f..09a75c6b8d299 100644 --- a/src/tools/clippy/tests/ui/issue-7447.stderr +++ b/src/tools/clippy/tests/ui/issue-7447.stderr @@ -6,15 +6,12 @@ LL | byte_view(panic!()); | = note: `-D clippy::diverging-sub-expression` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::diverging_sub_expression)]` - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: sub-expression diverges --> tests/ui/issue-7447.rs:29:19 | LL | group_entries(panic!()); | ^^^^^^^^ - | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed b/src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed index f036b368a6676..c00d6440f1c6a 100644 --- a/src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed +++ b/src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::items_after_test_module)] fn main() {} diff --git a/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs b/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs index de0cbb120330e..23d191e3b13b0 100644 --- a/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs +++ b/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::items_after_test_module)] fn main() {} diff --git a/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr b/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr index bed8d4bd5a00c..952489ff5ef9a 100644 --- a/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr +++ b/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr @@ -1,5 +1,5 @@ error: items after a test module - --> tests/ui/items_after_test_module/root_module.rs:12:1 + --> tests/ui/items_after_test_module/root_module.rs:11:1 | LL | mod tests { | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.fixed b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed index e9fb44e89598e..231fac7cdde7d 100644 --- a/src/tools/clippy/tests/ui/iter_cloned_collect.fixed +++ b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed @@ -29,3 +29,30 @@ fn main() { let _: Vec = v.to_vec(); //~^ iter_cloned_collect } + +mod issue9119 { + + use std::iter; + + #[derive(Clone)] + struct Example(u16); + + impl iter::FromIterator for Vec { + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + iter.into_iter().flat_map(|e| e.0.to_le_bytes()).collect() + } + } + + fn foo() { + let examples = [Example(1), Example(0x1234)]; + let encoded: Vec = examples.iter().cloned().collect(); + assert_eq!(encoded, vec![0x01, 0x00, 0x34, 0x12]); + + let a = [&&String::new()]; + let v: Vec<&&String> = a.to_vec(); + //~^ iter_cloned_collect + } +} diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.rs b/src/tools/clippy/tests/ui/iter_cloned_collect.rs index c9b8abcc9a0d0..e73b6ecae8021 100644 --- a/src/tools/clippy/tests/ui/iter_cloned_collect.rs +++ b/src/tools/clippy/tests/ui/iter_cloned_collect.rs @@ -33,3 +33,30 @@ fn main() { let _: Vec = v.iter().copied().collect(); //~^ iter_cloned_collect } + +mod issue9119 { + + use std::iter; + + #[derive(Clone)] + struct Example(u16); + + impl iter::FromIterator for Vec { + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + iter.into_iter().flat_map(|e| e.0.to_le_bytes()).collect() + } + } + + fn foo() { + let examples = [Example(1), Example(0x1234)]; + let encoded: Vec = examples.iter().cloned().collect(); + assert_eq!(encoded, vec![0x01, 0x00, 0x34, 0x12]); + + let a = [&&String::new()]; + let v: Vec<&&String> = a.iter().cloned().collect(); + //~^ iter_cloned_collect + } +} diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.stderr b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr index 119698cb46343..f8a507943270d 100644 --- a/src/tools/clippy/tests/ui/iter_cloned_collect.stderr +++ b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr @@ -36,5 +36,11 @@ error: called `iter().copied().collect()` on a slice to create a `Vec`. Calling LL | let _: Vec = v.iter().copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` -error: aborting due to 5 previous errors +error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable + --> tests/ui/iter_cloned_collect.rs:59:33 + | +LL | let v: Vec<&&String> = a.iter().cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/iter_kv_map.fixed b/src/tools/clippy/tests/ui/iter_kv_map.fixed index 7fcab6592e26c..874f749b33d02 100644 --- a/src/tools/clippy/tests/ui/iter_kv_map.fixed +++ b/src/tools/clippy/tests/ui/iter_kv_map.fixed @@ -166,3 +166,18 @@ fn msrv_1_54() { let _ = map.values().map(|v| v + 2).collect::>(); //~^ iter_kv_map } + +fn issue14595() { + pub struct Foo(BTreeMap); + + impl AsRef> for Foo { + fn as_ref(&self) -> &BTreeMap { + &self.0 + } + } + + let map = Foo(BTreeMap::default()); + + let _ = map.as_ref().values().copied().collect::>(); + //~^ iter_kv_map +} diff --git a/src/tools/clippy/tests/ui/iter_kv_map.rs b/src/tools/clippy/tests/ui/iter_kv_map.rs index b590aef7b8031..f570e3c32cb67 100644 --- a/src/tools/clippy/tests/ui/iter_kv_map.rs +++ b/src/tools/clippy/tests/ui/iter_kv_map.rs @@ -170,3 +170,18 @@ fn msrv_1_54() { let _ = map.iter().map(|(_, v)| v + 2).collect::>(); //~^ iter_kv_map } + +fn issue14595() { + pub struct Foo(BTreeMap); + + impl AsRef> for Foo { + fn as_ref(&self) -> &BTreeMap { + &self.0 + } + } + + let map = Foo(BTreeMap::default()); + + let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::>(); + //~^ iter_kv_map +} diff --git a/src/tools/clippy/tests/ui/iter_kv_map.stderr b/src/tools/clippy/tests/ui/iter_kv_map.stderr index 00d566ed14a28..31ee76c25b7a5 100644 --- a/src/tools/clippy/tests/ui/iter_kv_map.stderr +++ b/src/tools/clippy/tests/ui/iter_kv_map.stderr @@ -263,5 +263,11 @@ error: iterating on a map's values LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` -error: aborting due to 38 previous errors +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:185:13 + | +LL | let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.as_ref().values()` + +error: aborting due to 39 previous errors diff --git a/src/tools/clippy/tests/ui/iter_next_loop.rs b/src/tools/clippy/tests/ui/iter_next_loop.rs index 32711c7ef6239..8e62ed963b900 100644 --- a/src/tools/clippy/tests/ui/iter_next_loop.rs +++ b/src/tools/clippy/tests/ui/iter_next_loop.rs @@ -15,3 +15,16 @@ fn main() { let u = Unrelated(&[0]); for _v in u.next() {} // no error } + +fn issue14630() { + macro_rules! mac { + (next $e:expr) => { + $e.iter().next() + }; + } + + for _ in dbg!([1, 2].iter()).next() {} + //~^ iter_next_loop + + for _ in mac!(next [1, 2]) {} +} diff --git a/src/tools/clippy/tests/ui/iter_next_loop.stderr b/src/tools/clippy/tests/ui/iter_next_loop.stderr index acc55031c3b29..c076e86db93bb 100644 --- a/src/tools/clippy/tests/ui/iter_next_loop.stderr +++ b/src/tools/clippy/tests/ui/iter_next_loop.stderr @@ -7,5 +7,11 @@ LL | for _ in x.iter().next() {} = note: `-D clippy::iter-next-loop` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::iter_next_loop)]` -error: aborting due to 1 previous error +error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want + --> tests/ui/iter_next_loop.rs:26:14 + | +LL | for _ in dbg!([1, 2].iter()).next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/iter_out_of_bounds.rs b/src/tools/clippy/tests/ui/iter_out_of_bounds.rs index b34e4ad782409..6458b1342dcd5 100644 --- a/src/tools/clippy/tests/ui/iter_out_of_bounds.rs +++ b/src/tools/clippy/tests/ui/iter_out_of_bounds.rs @@ -1,5 +1,3 @@ -//@no-rustfix - #![deny(clippy::iter_out_of_bounds)] #![allow(clippy::useless_vec)] diff --git a/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr b/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr index 19ac60b9d0ac0..1b3a99e1e9457 100644 --- a/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr +++ b/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr @@ -1,18 +1,18 @@ error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:12:14 + --> tests/ui/iter_out_of_bounds.rs:10:14 | LL | for _ in [1, 2, 3].iter().skip(4) { | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this operation is useless and will create an empty iterator note: the lint level is defined here - --> tests/ui/iter_out_of_bounds.rs:3:9 + --> tests/ui/iter_out_of_bounds.rs:1:9 | LL | #![deny(clippy::iter_out_of_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:17:19 + --> tests/ui/iter_out_of_bounds.rs:15:19 | LL | for (i, _) in [1, 2, 3].iter().take(4).enumerate() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | for (i, _) in [1, 2, 3].iter().take(4).enumerate() { = note: this operation is useless and the returned iterator will simply yield the same items error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:24:14 + --> tests/ui/iter_out_of_bounds.rs:22:14 | LL | for _ in (&&&&&&[1, 2, 3]).iter().take(4) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | for _ in (&&&&&&[1, 2, 3]).iter().take(4) {} = note: this operation is useless and the returned iterator will simply yield the same items error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:27:14 + --> tests/ui/iter_out_of_bounds.rs:25:14 | LL | for _ in [1, 2, 3].iter().skip(4) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | for _ in [1, 2, 3].iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:30:14 + --> tests/ui/iter_out_of_bounds.rs:28:14 | LL | for _ in [1; 3].iter().skip(4) {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | for _ in [1; 3].iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:36:14 + --> tests/ui/iter_out_of_bounds.rs:34:14 | LL | for _ in vec![1, 2, 3].iter().skip(4) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | for _ in vec![1, 2, 3].iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:39:14 + --> tests/ui/iter_out_of_bounds.rs:37:14 | LL | for _ in vec![1; 3].iter().skip(4) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | for _ in vec![1; 3].iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:43:14 + --> tests/ui/iter_out_of_bounds.rs:41:14 | LL | for _ in x.iter().skip(4) {} | ^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | for _ in x.iter().skip(4) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:47:14 + --> tests/ui/iter_out_of_bounds.rs:45:14 | LL | for _ in x.iter().skip(n) {} | ^^^^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | for _ in x.iter().skip(n) {} = note: this operation is useless and will create an empty iterator error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:52:14 + --> tests/ui/iter_out_of_bounds.rs:50:14 | LL | for _ in empty().skip(1) {} | ^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL | for _ in empty().skip(1) {} = note: this operation is useless and will create an empty iterator error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:55:14 + --> tests/ui/iter_out_of_bounds.rs:53:14 | LL | for _ in empty().take(1) {} | ^^^^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL | for _ in empty().take(1) {} = note: this operation is useless and the returned iterator will simply yield the same items error: this `.skip()` call skips more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:58:14 + --> tests/ui/iter_out_of_bounds.rs:56:14 | LL | for _ in std::iter::once(1).skip(2) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | for _ in std::iter::once(1).skip(2) {} = note: this operation is useless and will create an empty iterator error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:61:14 + --> tests/ui/iter_out_of_bounds.rs:59:14 | LL | for _ in std::iter::once(1).take(2) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL | for _ in std::iter::once(1).take(2) {} = note: this operation is useless and the returned iterator will simply yield the same items error: this `.take()` call takes more items than the iterator will produce - --> tests/ui/iter_out_of_bounds.rs:64:14 + --> tests/ui/iter_out_of_bounds.rs:62:14 | LL | for x in [].iter().take(1) { | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed index 9999126902903..b0e548f179093 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed @@ -59,7 +59,7 @@ fn main() { iter: impl Iterator + 'a, target: String, ) -> impl Iterator + 'a { - iter.filter(move |&(&a, b)| a == 1 && b == &target).cloned() + iter.filter(move |&&(&a, ref b)| a == 1 && b == &target).cloned() //~^ iter_overeager_cloned } diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs index 6a860dad5afd8..cedf62a6b4730 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs @@ -60,7 +60,7 @@ fn main() { iter: impl Iterator + 'a, target: String, ) -> impl Iterator + 'a { - iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) + iter.cloned().filter(move |&(&a, ref b)| a == 1 && b == &target) //~^ iter_overeager_cloned } diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr index f3239b59582e3..1616dec95b792 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr @@ -120,10 +120,10 @@ LL | let _ = vec.iter().cloned().find(f); error: unnecessarily eager cloning of iterator items --> tests/ui/iter_overeager_cloned.rs:63:9 | -LL | iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) - | ^^^^------------------------------------------------------- +LL | iter.cloned().filter(move |&(&a, ref b)| a == 1 && b == &target) + | ^^^^------------------------------------------------------------ | | - | help: try: `.filter(move |&(&a, b)| a == 1 && b == &target).cloned()` + | help: try: `.filter(move |&&(&a, ref b)| a == 1 && b == &target).cloned()` error: unnecessarily eager cloning of iterator items --> tests/ui/iter_overeager_cloned.rs:75:13 diff --git a/src/tools/clippy/tests/ui/large_futures.fixed b/src/tools/clippy/tests/ui/large_futures.fixed index c2159c58de1ec..4c7215f0abeb0 100644 --- a/src/tools/clippy/tests/ui/large_futures.fixed +++ b/src/tools/clippy/tests/ui/large_futures.fixed @@ -1,7 +1,10 @@ +#![allow( + clippy::future_not_send, + clippy::manual_async_fn, + clippy::never_loop, + clippy::uninlined_format_args +)] #![warn(clippy::large_futures)] -#![allow(clippy::never_loop)] -#![allow(clippy::future_not_send)] -#![allow(clippy::manual_async_fn)] async fn big_fut(_arg: [u8; 1024 * 16]) {} diff --git a/src/tools/clippy/tests/ui/large_futures.rs b/src/tools/clippy/tests/ui/large_futures.rs index 567f6344afeac..2b5860583f5ec 100644 --- a/src/tools/clippy/tests/ui/large_futures.rs +++ b/src/tools/clippy/tests/ui/large_futures.rs @@ -1,7 +1,10 @@ +#![allow( + clippy::future_not_send, + clippy::manual_async_fn, + clippy::never_loop, + clippy::uninlined_format_args +)] #![warn(clippy::large_futures)] -#![allow(clippy::never_loop)] -#![allow(clippy::future_not_send)] -#![allow(clippy::manual_async_fn)] async fn big_fut(_arg: [u8; 1024 * 16]) {} diff --git a/src/tools/clippy/tests/ui/large_futures.stderr b/src/tools/clippy/tests/ui/large_futures.stderr index fd6ba4e3563de..4280c9e2af284 100644 --- a/src/tools/clippy/tests/ui/large_futures.stderr +++ b/src/tools/clippy/tests/ui/large_futures.stderr @@ -1,5 +1,5 @@ error: large future with a size of 16385 bytes - --> tests/ui/large_futures.rs:10:9 + --> tests/ui/large_futures.rs:13:9 | LL | big_fut([0u8; 1024 * 16]).await; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(big_fut([0u8; 1024 * 16]))` @@ -8,37 +8,37 @@ LL | big_fut([0u8; 1024 * 16]).await; = help: to override `-D warnings` add `#[allow(clippy::large_futures)]` error: large future with a size of 16386 bytes - --> tests/ui/large_futures.rs:13:5 + --> tests/ui/large_futures.rs:16:5 | LL | f.await | ^ help: consider `Box::pin` on it: `Box::pin(f)` error: large future with a size of 16387 bytes - --> tests/ui/large_futures.rs:18:9 + --> tests/ui/large_futures.rs:21:9 | LL | wait().await; | ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())` error: large future with a size of 16387 bytes - --> tests/ui/large_futures.rs:24:13 + --> tests/ui/large_futures.rs:27:13 | LL | wait().await; | ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())` error: large future with a size of 65540 bytes - --> tests/ui/large_futures.rs:32:5 + --> tests/ui/large_futures.rs:35:5 | LL | foo().await; | ^^^^^ help: consider `Box::pin` on it: `Box::pin(foo())` error: large future with a size of 49159 bytes - --> tests/ui/large_futures.rs:35:5 + --> tests/ui/large_futures.rs:38:5 | LL | calls_fut(fut).await; | ^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(calls_fut(fut))` error: large future with a size of 65540 bytes - --> tests/ui/large_futures.rs:48:5 + --> tests/ui/large_futures.rs:51:5 | LL | / async { LL | | @@ -61,7 +61,7 @@ LL + }) | error: large future with a size of 65540 bytes - --> tests/ui/large_futures.rs:61:13 + --> tests/ui/large_futures.rs:64:13 | LL | / async { LL | | diff --git a/src/tools/clippy/tests/ui/len_without_is_empty_expect.rs b/src/tools/clippy/tests/ui/len_without_is_empty_expect.rs new file mode 100644 index 0000000000000..9d1245e2d02ad --- /dev/null +++ b/src/tools/clippy/tests/ui/len_without_is_empty_expect.rs @@ -0,0 +1,28 @@ +//@no-rustfix +#![allow(clippy::len_without_is_empty)] + +// Check that the lint expectation is fulfilled even if the lint is allowed at the type level. +pub struct Empty; + +impl Empty { + #[expect(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + 0 + } +} + +// Check that the lint expectation is not triggered if it should not +pub struct Empty2; + +impl Empty2 { + #[expect(clippy::len_without_is_empty)] //~ ERROR: this lint expectation is unfulfilled + pub fn len(&self) -> usize { + 0 + } + + pub fn is_empty(&self) -> bool { + false + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/len_without_is_empty_expect.stderr b/src/tools/clippy/tests/ui/len_without_is_empty_expect.stderr new file mode 100644 index 0000000000000..e96870f054e43 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_without_is_empty_expect.stderr @@ -0,0 +1,11 @@ +error: this lint expectation is unfulfilled + --> tests/ui/len_without_is_empty_expect.rs:18:14 + | +LL | #[expect(clippy::len_without_is_empty)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D unfulfilled-lint-expectations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unfulfilled_lint_expectations)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/let_underscore_untyped.rs b/src/tools/clippy/tests/ui/let_underscore_untyped.rs index 26ba8682dc2d5..7f74ab035bda8 100644 --- a/src/tools/clippy/tests/ui/let_underscore_untyped.rs +++ b/src/tools/clippy/tests/ui/let_underscore_untyped.rs @@ -6,7 +6,6 @@ extern crate proc_macros; use proc_macros::with_span; -use clippy_utils::is_from_proc_macro; use std::boxed::Box; use std::fmt::Display; use std::future::Future; diff --git a/src/tools/clippy/tests/ui/let_underscore_untyped.stderr b/src/tools/clippy/tests/ui/let_underscore_untyped.stderr index 86cdd5c662cc1..54b955ac3a561 100644 --- a/src/tools/clippy/tests/ui/let_underscore_untyped.stderr +++ b/src/tools/clippy/tests/ui/let_underscore_untyped.stderr @@ -1,11 +1,11 @@ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:51:5 + --> tests/ui/let_underscore_untyped.rs:50:5 | LL | let _ = a(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:51:10 + --> tests/ui/let_underscore_untyped.rs:50:10 | LL | let _ = a(); | ^ @@ -13,49 +13,49 @@ LL | let _ = a(); = help: to override `-D warnings` add `#[allow(clippy::let_underscore_untyped)]` error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:53:5 + --> tests/ui/let_underscore_untyped.rs:52:5 | LL | let _ = b(1); | ^^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:53:10 + --> tests/ui/let_underscore_untyped.rs:52:10 | LL | let _ = b(1); | ^ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:56:5 + --> tests/ui/let_underscore_untyped.rs:55:5 | LL | let _ = d(&1); | ^^^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:56:10 + --> tests/ui/let_underscore_untyped.rs:55:10 | LL | let _ = d(&1); | ^ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:58:5 + --> tests/ui/let_underscore_untyped.rs:57:5 | LL | let _ = e(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:58:10 + --> tests/ui/let_underscore_untyped.rs:57:10 | LL | let _ = e(); | ^ error: non-binding `let` without a type annotation - --> tests/ui/let_underscore_untyped.rs:60:5 + --> tests/ui/let_underscore_untyped.rs:59:5 | LL | let _ = f(); | ^^^^^^^^^^^^ | help: consider adding a type annotation - --> tests/ui/let_underscore_untyped.rs:60:10 + --> tests/ui/let_underscore_untyped.rs:59:10 | LL | let _ = f(); | ^ diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.fixed b/src/tools/clippy/tests/ui/let_with_type_underscore.fixed new file mode 100644 index 0000000000000..7a4af4e3d1e7f --- /dev/null +++ b/src/tools/clippy/tests/ui/let_with_type_underscore.fixed @@ -0,0 +1,47 @@ +//@aux-build: proc_macros.rs +#![allow(unused)] +#![warn(clippy::let_with_type_underscore)] +#![allow(clippy::let_unit_value, clippy::needless_late_init)] + +extern crate proc_macros; + +fn func() -> &'static str { + "" +} + +#[rustfmt::skip] +fn main() { + // Will lint + let x = 1; + //~^ let_with_type_underscore + let _ = 2; + //~^ let_with_type_underscore + let x = func(); + //~^ let_with_type_underscore + let x; + //~^ let_with_type_underscore + x = (); + + let x = 1; // Will not lint, Rust infers this to an integer before Clippy + let x = func(); + let x: Vec<_> = Vec::::new(); + let x: [_; 1] = [1]; + let x = 1; + //~^ let_with_type_underscore + + // Do not lint from procedural macros + proc_macros::with_span! { + span + let x: _ = (); + // Late initialization + let x: _; + x = (); + // Ensure weird formatting will not break it (hopefully) + let x : _ = 1; + let x +: _ = 1; + let x : + _; + x = (); + }; +} diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.stderr b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr index 2284d1fe2e48a..9179f99220718 100644 --- a/src/tools/clippy/tests/ui/let_with_type_underscore.stderr +++ b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr @@ -4,13 +4,13 @@ error: variable declared with type underscore LL | let x: _ = 1; | ^^^^^^^^^^^^^ | -help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:15:10 - | -LL | let x: _ = 1; - | ^^^ = note: `-D clippy::let-with-type-underscore` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_with_type_underscore)]` +help: remove the explicit type `_` declaration + | +LL - let x: _ = 1; +LL + let x = 1; + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:17:5 @@ -19,10 +19,10 @@ LL | let _: _ = 2; | ^^^^^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:17:10 | -LL | let _: _ = 2; - | ^^^ +LL - let _: _ = 2; +LL + let _ = 2; + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:19:5 @@ -31,10 +31,10 @@ LL | let x: _ = func(); | ^^^^^^^^^^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:19:10 | -LL | let x: _ = func(); - | ^^^ +LL - let x: _ = func(); +LL + let x = func(); + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:21:5 @@ -43,10 +43,10 @@ LL | let x: _; | ^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:21:10 | -LL | let x: _; - | ^^^ +LL - let x: _; +LL + let x; + | error: variable declared with type underscore --> tests/ui/let_with_type_underscore.rs:29:5 @@ -55,10 +55,10 @@ LL | let x : _ = 1; | ^^^^^^^^^^^^^^ | help: remove the explicit type `_` declaration - --> tests/ui/let_with_type_underscore.rs:29:10 | -LL | let x : _ = 1; - | ^^^^ +LL - let x : _ = 1; +LL + let x = 1; + | error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/literals.rs b/src/tools/clippy/tests/ui/literals.rs index d21d49310a077..ba343a8f62f82 100644 --- a/src/tools/clippy/tests/ui/literals.rs +++ b/src/tools/clippy/tests/ui/literals.rs @@ -30,6 +30,10 @@ fn main() { //~^ separated_literal_suffix //~| mixed_case_hex_literals + let fail2 = 0xab_CD_isize; + //~^ separated_literal_suffix + //~| mixed_case_hex_literals + let fail_multi_zero = 000_123usize; //~^ unseparated_literal_suffix //~| zero_prefixed_literal diff --git a/src/tools/clippy/tests/ui/literals.stderr b/src/tools/clippy/tests/ui/literals.stderr index d65cd3c89ebbe..6bfeb91625b32 100644 --- a/src/tools/clippy/tests/ui/literals.stderr +++ b/src/tools/clippy/tests/ui/literals.stderr @@ -25,6 +25,7 @@ error: inconsistent casing in hexadecimal literal LL | let fail1 = 0xabCD; | ^^^^^^ | + = help: consider using `0xabcd` or `0xABCD` = note: `-D clippy::mixed-case-hex-literals` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mixed_case_hex_literals)]` @@ -39,6 +40,8 @@ error: inconsistent casing in hexadecimal literal | LL | let fail2 = 0xabCD_u32; | ^^^^^^^^^^ + | + = help: consider using `0xabcd_u32` or `0xABCD_u32` error: integer type suffix should not be separated by an underscore --> tests/ui/literals.rs:29:17 @@ -51,9 +54,25 @@ error: inconsistent casing in hexadecimal literal | LL | let fail2 = 0xabCD_isize; | ^^^^^^^^^^^^ + | + = help: consider using `0xabcd_isize` or `0xABCD_isize` + +error: integer type suffix should not be separated by an underscore + --> tests/ui/literals.rs:33:17 + | +LL | let fail2 = 0xab_CD_isize; + | ^^^^^^^^^^^^^ help: remove the underscore: `0xab_CDisize` + +error: inconsistent casing in hexadecimal literal + --> tests/ui/literals.rs:33:17 + | +LL | let fail2 = 0xab_CD_isize; + | ^^^^^^^^^^^^^ + | + = help: consider using `0xab_cd_isize` or `0xAB_CD_isize` error: integer type suffix should be separated by an underscore - --> tests/ui/literals.rs:33:27 + --> tests/ui/literals.rs:37:27 | LL | let fail_multi_zero = 000_123usize; | ^^^^^^^^^^^^ help: add an underscore: `000_123_usize` @@ -62,7 +81,7 @@ LL | let fail_multi_zero = 000_123usize; = help: to override `-D warnings` add `#[allow(clippy::unseparated_literal_suffix)]` error: this is a decimal constant - --> tests/ui/literals.rs:33:27 + --> tests/ui/literals.rs:37:27 | LL | let fail_multi_zero = 000_123usize; | ^^^^^^^^^^^^ @@ -81,13 +100,13 @@ LL + let fail_multi_zero = 0o123usize; | error: integer type suffix should not be separated by an underscore - --> tests/ui/literals.rs:38:16 + --> tests/ui/literals.rs:42:16 | LL | let ok10 = 0_i64; | ^^^^^ help: remove the underscore: `0i64` error: this is a decimal constant - --> tests/ui/literals.rs:41:17 + --> tests/ui/literals.rs:45:17 | LL | let fail8 = 0123; | ^^^^ @@ -103,13 +122,13 @@ LL | let fail8 = 0o123; | + error: integer type suffix should not be separated by an underscore - --> tests/ui/literals.rs:51:16 + --> tests/ui/literals.rs:55:16 | LL | let ok17 = 0x123_4567_8901_usize; | ^^^^^^^^^^^^^^^^^^^^^ help: remove the underscore: `0x123_4567_8901usize` error: digits grouped inconsistently by underscores - --> tests/ui/literals.rs:56:18 + --> tests/ui/literals.rs:60:18 | LL | let fail19 = 12_3456_21; | ^^^^^^^^^^ help: consider: `12_345_621` @@ -118,19 +137,19 @@ LL | let fail19 = 12_3456_21; = help: to override `-D warnings` add `#[allow(clippy::inconsistent_digit_grouping)]` error: digits grouped inconsistently by underscores - --> tests/ui/literals.rs:59:18 + --> tests/ui/literals.rs:63:18 | LL | let fail22 = 3__4___23; | ^^^^^^^^^ help: consider: `3_423` error: digits grouped inconsistently by underscores - --> tests/ui/literals.rs:62:18 + --> tests/ui/literals.rs:66:18 | LL | let fail23 = 3__16___23; | ^^^^^^^^^^ help: consider: `31_623` error: digits of hex, binary or octal literal not in groups of equal size - --> tests/ui/literals.rs:65:18 + --> tests/ui/literals.rs:69:18 | LL | let fail24 = 0xAB_ABC_AB; | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB` @@ -139,7 +158,7 @@ LL | let fail24 = 0xAB_ABC_AB; = help: to override `-D warnings` add `#[allow(clippy::unusual_byte_groupings)]` error: this is a decimal constant - --> tests/ui/literals.rs:75:13 + --> tests/ui/literals.rs:79:13 | LL | let _ = 08; | ^^ @@ -151,7 +170,7 @@ LL + let _ = 8; | error: this is a decimal constant - --> tests/ui/literals.rs:78:13 + --> tests/ui/literals.rs:82:13 | LL | let _ = 09; | ^^ @@ -163,7 +182,7 @@ LL + let _ = 9; | error: this is a decimal constant - --> tests/ui/literals.rs:81:13 + --> tests/ui/literals.rs:85:13 | LL | let _ = 089; | ^^^ @@ -174,5 +193,5 @@ LL - let _ = 089; LL + let _ = 89; | -error: aborting due to 20 previous errors +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.fixed b/src/tools/clippy/tests/ui/manual_abs_diff.fixed new file mode 100644 index 0000000000000..f1b1278ea6d22 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_abs_diff.fixed @@ -0,0 +1,106 @@ +#![warn(clippy::manual_abs_diff)] + +use std::time::Duration; + +fn main() { + let a: usize = 5; + let b: usize = 3; + let c: usize = 8; + let d: usize = 11; + + let _ = a.abs_diff(b); + //~^ manual_abs_diff + let _ = b.abs_diff(a); + //~^ manual_abs_diff + + let _ = b.abs_diff(5); + //~^ manual_abs_diff + let _ = b.abs_diff(5); + //~^ manual_abs_diff + + let _ = a.abs_diff(b); + //~^ manual_abs_diff + let _ = b.abs_diff(a); + //~^ manual_abs_diff + + #[allow(arithmetic_overflow)] + { + let _ = if a > b { b - a } else { a - b }; + let _ = if a < b { a - b } else { b - a }; + } + + let _ = (a + b).abs_diff(c + d); + let _ = (c + d).abs_diff(a + b); + + const A: usize = 5; + const B: usize = 3; + // check const context + const _: usize = A.abs_diff(B); + //~^ manual_abs_diff + + let a = Duration::from_secs(3); + let b = Duration::from_secs(5); + let _ = a.abs_diff(b); + //~^ manual_abs_diff + + let a: i32 = 3; + let b: i32 = -5; + let _ = if a > b { a - b } else { b - a }; + let _ = a.abs_diff(b); + //~^ manual_abs_diff +} + +// FIXME: bunch of patterns that should be linted +fn fixme() { + let a: usize = 5; + let b: usize = 3; + let c: usize = 8; + let d: usize = 11; + + { + let out; + if a > b { + out = a - b; + } else { + out = b - a; + } + } + + { + let mut out = 0; + if a > b { + out = a - b; + } else if a < b { + out = b - a; + } + } + + #[allow(clippy::implicit_saturating_sub)] + let _ = if a > b { + a - b + } else if a < b { + b - a + } else { + 0 + }; + + let a: i32 = 3; + let b: i32 = 5; + let _: u32 = if a > b { a - b } else { b - a } as u32; +} + +fn non_primitive_ty() { + #[derive(Eq, PartialEq, PartialOrd)] + struct S(i32); + + impl std::ops::Sub for S { + type Output = S; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } + } + + let (a, b) = (S(10), S(20)); + let _ = if a < b { b - a } else { a - b }; +} diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.rs b/src/tools/clippy/tests/ui/manual_abs_diff.rs new file mode 100644 index 0000000000000..60ef819c12d30 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_abs_diff.rs @@ -0,0 +1,116 @@ +#![warn(clippy::manual_abs_diff)] + +use std::time::Duration; + +fn main() { + let a: usize = 5; + let b: usize = 3; + let c: usize = 8; + let d: usize = 11; + + let _ = if a > b { a - b } else { b - a }; + //~^ manual_abs_diff + let _ = if a < b { b - a } else { a - b }; + //~^ manual_abs_diff + + let _ = if 5 > b { 5 - b } else { b - 5 }; + //~^ manual_abs_diff + let _ = if b > 5 { b - 5 } else { 5 - b }; + //~^ manual_abs_diff + + let _ = if a >= b { a - b } else { b - a }; + //~^ manual_abs_diff + let _ = if a <= b { b - a } else { a - b }; + //~^ manual_abs_diff + + #[allow(arithmetic_overflow)] + { + let _ = if a > b { b - a } else { a - b }; + let _ = if a < b { a - b } else { b - a }; + } + + let _ = if (a + b) > (c + d) { + //~^ manual_abs_diff + (a + b) - (c + d) + } else { + (c + d) - (a + b) + }; + let _ = if (a + b) < (c + d) { + //~^ manual_abs_diff + (c + d) - (a + b) + } else { + (a + b) - (c + d) + }; + + const A: usize = 5; + const B: usize = 3; + // check const context + const _: usize = if A > B { A - B } else { B - A }; + //~^ manual_abs_diff + + let a = Duration::from_secs(3); + let b = Duration::from_secs(5); + let _ = if a > b { a - b } else { b - a }; + //~^ manual_abs_diff + + let a: i32 = 3; + let b: i32 = -5; + let _ = if a > b { a - b } else { b - a }; + let _ = if a > b { (a - b) as u32 } else { (b - a) as u32 }; + //~^ manual_abs_diff +} + +// FIXME: bunch of patterns that should be linted +fn fixme() { + let a: usize = 5; + let b: usize = 3; + let c: usize = 8; + let d: usize = 11; + + { + let out; + if a > b { + out = a - b; + } else { + out = b - a; + } + } + + { + let mut out = 0; + if a > b { + out = a - b; + } else if a < b { + out = b - a; + } + } + + #[allow(clippy::implicit_saturating_sub)] + let _ = if a > b { + a - b + } else if a < b { + b - a + } else { + 0 + }; + + let a: i32 = 3; + let b: i32 = 5; + let _: u32 = if a > b { a - b } else { b - a } as u32; +} + +fn non_primitive_ty() { + #[derive(Eq, PartialEq, PartialOrd)] + struct S(i32); + + impl std::ops::Sub for S { + type Output = S; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } + } + + let (a, b) = (S(10), S(20)); + let _ = if a < b { b - a } else { a - b }; +} diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.stderr b/src/tools/clippy/tests/ui/manual_abs_diff.stderr new file mode 100644 index 0000000000000..c14c1dc830fbd --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_abs_diff.stderr @@ -0,0 +1,83 @@ +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:11:13 + | +LL | let _ = if a > b { a - b } else { b - a }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `a.abs_diff(b)` + | + = note: `-D clippy::manual-abs-diff` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_abs_diff)]` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:13:13 + | +LL | let _ = if a < b { b - a } else { a - b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `b.abs_diff(a)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:16:13 + | +LL | let _ = if 5 > b { 5 - b } else { b - 5 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `b.abs_diff(5)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:18:13 + | +LL | let _ = if b > 5 { b - 5 } else { 5 - b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `b.abs_diff(5)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:21:13 + | +LL | let _ = if a >= b { a - b } else { b - a }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `a.abs_diff(b)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:23:13 + | +LL | let _ = if a <= b { b - a } else { a - b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `b.abs_diff(a)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:32:13 + | +LL | let _ = if (a + b) > (c + d) { + | _____________^ +LL | | +LL | | (a + b) - (c + d) +LL | | } else { +LL | | (c + d) - (a + b) +LL | | }; + | |_____^ help: replace with `abs_diff`: `(a + b).abs_diff(c + d)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:38:13 + | +LL | let _ = if (a + b) < (c + d) { + | _____________^ +LL | | +LL | | (c + d) - (a + b) +LL | | } else { +LL | | (a + b) - (c + d) +LL | | }; + | |_____^ help: replace with `abs_diff`: `(c + d).abs_diff(a + b)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:48:22 + | +LL | const _: usize = if A > B { A - B } else { B - A }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `A.abs_diff(B)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:53:13 + | +LL | let _ = if a > b { a - b } else { b - a }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `a.abs_diff(b)` + +error: manual absolute difference pattern without using `abs_diff` + --> tests/ui/manual_abs_diff.rs:59:13 + | +LL | let _ = if a > b { (a - b) as u32 } else { (b - a) as u32 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `a.abs_diff(b)` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_arithmetic_check-2.rs b/src/tools/clippy/tests/ui/manual_arithmetic_check-2.rs index 749d15f1cbd81..b094b2021b32c 100644 --- a/src/tools/clippy/tests/ui/manual_arithmetic_check-2.rs +++ b/src/tools/clippy/tests/ui/manual_arithmetic_check-2.rs @@ -24,6 +24,15 @@ fn main() { let result = if b <= a { 0 } else { a - b }; //~^ inverted_saturating_sub + let result = if b * 2 <= a { 0 } else { a - b * 2 }; + //~^ inverted_saturating_sub + + let result = if b <= a * 2 { 0 } else { a * 2 - b }; + //~^ inverted_saturating_sub + + let result = if b + 3 <= a + 2 { 0 } else { (a + 2) - (b + 3) }; + //~^ inverted_saturating_sub + let af = 12f32; let bf = 13f32; // Should not lint! diff --git a/src/tools/clippy/tests/ui/manual_arithmetic_check-2.stderr b/src/tools/clippy/tests/ui/manual_arithmetic_check-2.stderr index 8841210befda7..fb0f0da772c9f 100644 --- a/src/tools/clippy/tests/ui/manual_arithmetic_check-2.stderr +++ b/src/tools/clippy/tests/ui/manual_arithmetic_check-2.stderr @@ -71,5 +71,41 @@ note: this subtraction underflows when `a < b` LL | let result = if b <= a { 0 } else { a - b }; | ^^^^^ -error: aborting due to 6 previous errors +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:27:27 + | +LL | let result = if b * 2 <= a { 0 } else { a - b * 2 }; + | ^^ --------- help: try replacing it with: `b * 2 - a` + | +note: this subtraction underflows when `a < b * 2` + --> tests/ui/manual_arithmetic_check-2.rs:27:45 + | +LL | let result = if b * 2 <= a { 0 } else { a - b * 2 }; + | ^^^^^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:30:23 + | +LL | let result = if b <= a * 2 { 0 } else { a * 2 - b }; + | ^^ --------- help: try replacing it with: `b - a * 2` + | +note: this subtraction underflows when `a * 2 < b` + --> tests/ui/manual_arithmetic_check-2.rs:30:45 + | +LL | let result = if b <= a * 2 { 0 } else { a * 2 - b }; + | ^^^^^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:33:27 + | +LL | let result = if b + 3 <= a + 2 { 0 } else { (a + 2) - (b + 3) }; + | ^^ ----------------- help: try replacing it with: `b + 3 - (a + 2)` + | +note: this subtraction underflows when `a + 2 < b + 3` + --> tests/ui/manual_arithmetic_check-2.rs:33:49 + | +LL | let result = if b + 3 <= a + 2 { 0 } else { (a + 2) - (b + 3) }; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 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 ad0266d39e982..a284ca9f62530 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.fixed +++ b/src/tools/clippy/tests/ui/manual_async_fn.fixed @@ -75,7 +75,7 @@ impl S { async fn elided(_: &i32) -> i32 { 42 } // should be ignored -fn elided_not_bound(_: &i32) -> impl Future { +fn elided_not_bound(_: &i32) -> impl Future + use<> { async { 42 } } @@ -84,7 +84,7 @@ 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 { +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + use<> { async { 42 } } @@ -94,7 +94,7 @@ mod issue_5765 { struct A; impl A { - fn f(&self) -> impl Future { + fn f(&self) -> impl Future + use<> { async {} } } diff --git a/src/tools/clippy/tests/ui/manual_async_fn.rs b/src/tools/clippy/tests/ui/manual_async_fn.rs index fe367b4bc7b9b..188f8a4982c36 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.rs +++ b/src/tools/clippy/tests/ui/manual_async_fn.rs @@ -102,7 +102,7 @@ fn elided(_: &i32) -> impl Future + '_ { } // should be ignored -fn elided_not_bound(_: &i32) -> impl Future { +fn elided_not_bound(_: &i32) -> impl Future + use<> { async { 42 } } @@ -114,7 +114,7 @@ fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + // should be ignored #[allow(clippy::needless_lifetimes)] -fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + use<> { async { 42 } } @@ -124,7 +124,7 @@ mod issue_5765 { struct A; impl A { - fn f(&self) -> impl Future { + fn f(&self) -> impl Future + use<> { async {} } } diff --git a/src/tools/clippy/tests/ui/manual_bits.stderr b/src/tools/clippy/tests/ui/manual_bits.stderr index a50975ed474ec..44c4cf9239c8f 100644 --- a/src/tools/clippy/tests/ui/manual_bits.stderr +++ b/src/tools/clippy/tests/ui/manual_bits.stderr @@ -1,4 +1,4 @@ -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:14:5 | LL | size_of::() * 8; @@ -7,169 +7,169 @@ LL | size_of::() * 8; = note: `-D clippy::manual-bits` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_bits)]` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:16:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:18:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:20:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:22:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:24:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:27:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:29:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:31:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:33:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:35:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:37:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:40:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:42:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:44:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:46:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:48:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:50:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:53:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:55:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:57:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:59:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:61:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:63:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:74:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:79:18 | LL | let _: u32 = (size_of::() * 8) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:81:18 | LL | let _: u32 = (size_of::() * 8).try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:83:13 | LL | let _ = (size_of::() * 8).pow(5); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits +error: usage of `size_of::()` to obtain the size of `T` in bits --> tests/ui/manual_bits.rs:85:14 | LL | let _ = &(size_of::() * 8); diff --git a/src/tools/clippy/tests/ui/manual_dangling_ptr.fixed b/src/tools/clippy/tests/ui/manual_dangling_ptr.fixed new file mode 100644 index 0000000000000..b6afe7898906c --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_dangling_ptr.fixed @@ -0,0 +1,44 @@ +#![warn(clippy::manual_dangling_ptr)] +use std::mem; + +pub fn foo(_const: *const f32, _mut: *mut i32) {} + +fn main() { + let _: *const u8 = std::ptr::dangling(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling_mut::(); + //~^ manual_dangling_ptr + + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + + foo(std::ptr::dangling(), std::ptr::dangling_mut()); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} + +fn should_not_lint() { + let _ = 0x10 as *mut i32; + let _ = mem::align_of::() as *const u8; + + foo(0 as _, 0 as _); +} + +#[clippy::msrv = "1.83"] +fn _msrv_1_83() { + // `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this + foo(4 as *const _, 4 as *mut _); +} + +#[clippy::msrv = "1.84"] +fn _msrv_1_84() { + foo(std::ptr::dangling(), std::ptr::dangling_mut()); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} diff --git a/src/tools/clippy/tests/ui/manual_dangling_ptr.rs b/src/tools/clippy/tests/ui/manual_dangling_ptr.rs new file mode 100644 index 0000000000000..581ad50113e28 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_dangling_ptr.rs @@ -0,0 +1,44 @@ +#![warn(clippy::manual_dangling_ptr)] +use std::mem; + +pub fn foo(_const: *const f32, _mut: *mut i32) {} + +fn main() { + let _: *const u8 = 1 as *const _; + //~^ manual_dangling_ptr + let _ = 2 as *const u32; + //~^ manual_dangling_ptr + let _ = 4 as *mut f32; + //~^ manual_dangling_ptr + + let _ = mem::align_of::() as *const u8; + //~^ manual_dangling_ptr + let _ = mem::align_of::() as *const u32; + //~^ manual_dangling_ptr + let _ = mem::align_of::() as *const usize; + //~^ manual_dangling_ptr + + foo(4 as *const _, 4 as *mut _); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} + +fn should_not_lint() { + let _ = 0x10 as *mut i32; + let _ = mem::align_of::() as *const u8; + + foo(0 as _, 0 as _); +} + +#[clippy::msrv = "1.83"] +fn _msrv_1_83() { + // `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this + foo(4 as *const _, 4 as *mut _); +} + +#[clippy::msrv = "1.84"] +fn _msrv_1_84() { + foo(4 as *const _, 4 as *mut _); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} diff --git a/src/tools/clippy/tests/ui/manual_dangling_ptr.stderr b/src/tools/clippy/tests/ui/manual_dangling_ptr.stderr new file mode 100644 index 0000000000000..e3bc9b16b0d93 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_dangling_ptr.stderr @@ -0,0 +1,65 @@ +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:7:24 + | +LL | let _: *const u8 = 1 as *const _; + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` + | + = note: `-D clippy::manual-dangling-ptr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_dangling_ptr)]` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:9:13 + | +LL | let _ = 2 as *const u32; + | ^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:11:13 + | +LL | let _ = 4 as *mut f32; + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling_mut::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:14:13 + | +LL | let _ = mem::align_of::() as *const u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:16:13 + | +LL | let _ = mem::align_of::() as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:18:13 + | +LL | let _ = mem::align_of::() as *const usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:21:9 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:21:24 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:41:9 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:41:24 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.fixed b/src/tools/clippy/tests/ui/manual_div_ceil.fixed index 57fe8917afe88..58ee6978fc125 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil.fixed @@ -1,5 +1,21 @@ #![warn(clippy::manual_div_ceil)] +macro_rules! y { + () => { + let x = 33u32; + let _ = x.div_ceil(8); + //~^ manual_div_ceil + let _ = x.div_ceil(8); + //~^ manual_div_ceil + }; +} + +macro_rules! eight { + () => { + 8 + }; +} + fn main() { let x = 7_u32; let y = 4_u32; @@ -32,6 +48,13 @@ fn main() { let _ = (z as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; + + // Test lint with macro + y!(); + + // Also test if RHS should be result of macro expansion + let _ = 33u32.div_ceil(eight!()); + //~^ manual_div_ceil } fn issue_13843() { diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.rs b/src/tools/clippy/tests/ui/manual_div_ceil.rs index ec343513e5ce3..aa0d81b22a0e2 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil.rs @@ -1,5 +1,21 @@ #![warn(clippy::manual_div_ceil)] +macro_rules! y { + () => { + let x = 33u32; + let _ = (x + 7) / 8; + //~^ manual_div_ceil + let _ = (7 + x) / 8; + //~^ manual_div_ceil + }; +} + +macro_rules! eight { + () => { + 8 + }; +} + fn main() { let x = 7_u32; let y = 4_u32; @@ -32,6 +48,13 @@ fn main() { let _ = (z as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; + + // Test lint with macro + y!(); + + // Also test if RHS should be result of macro expansion + let _ = (33u32 + 7) / eight!(); + //~^ manual_div_ceil } fn issue_13843() { diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.stderr b/src/tools/clippy/tests/ui/manual_div_ceil.stderr index 8e14ab274269a..9be5a19bf391f 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil.stderr @@ -1,5 +1,5 @@ error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:9:13 + --> tests/ui/manual_div_ceil.rs:25:13 | LL | let _ = (x + (y - 1)) / y; | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` @@ -8,94 +8,122 @@ LL | let _ = (x + (y - 1)) / y; = help: to override `-D warnings` add `#[allow(clippy::manual_div_ceil)]` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:11:13 + --> tests/ui/manual_div_ceil.rs:27:13 | LL | let _ = ((y - 1) + x) / y; | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:13:13 + --> tests/ui/manual_div_ceil.rs:29:13 | LL | let _ = (x + y - 1) / y; | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:16:13 + --> tests/ui/manual_div_ceil.rs:32:13 | LL | let _ = (7_u32 + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `7_u32.div_ceil(4)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:18:13 + --> tests/ui/manual_div_ceil.rs:34:13 | LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:39:13 + --> tests/ui/manual_div_ceil.rs:6:17 + | +LL | let _ = (x + 7) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` +... +LL | y!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:8:17 + | +LL | let _ = (7 + x) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` +... +LL | y!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:56:13 + | +LL | let _ = (33u32 + 7) / eight!(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `33u32.div_ceil(eight!())` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:62:13 | LL | let _ = (2048 + x - 1) / x; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:43:13 + --> tests/ui/manual_div_ceil.rs:66:13 | LL | let _ = (2048usize + x - 1) / x; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048usize.div_ceil(x)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:47:13 + --> tests/ui/manual_div_ceil.rs:70:13 | LL | let _ = (2048_usize + x - 1) / x; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:51:13 + --> tests/ui/manual_div_ceil.rs:74:13 | LL | let _ = (x + 4 - 1) / 4; | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(4)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:54:18 + --> tests/ui/manual_div_ceil.rs:77:18 | LL | let _: u32 = (2048 + 6 - 1) / 6; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:56:20 + --> tests/ui/manual_div_ceil.rs:79:20 | LL | let _: usize = (2048 + 6 - 1) / 6; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(6)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:58:18 + --> tests/ui/manual_div_ceil.rs:81:18 | LL | let _: u32 = (0x2048 + 0x6 - 1) / 0x6; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `0x2048_u32.div_ceil(0x6)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:61:13 + --> tests/ui/manual_div_ceil.rs:84:13 | LL | let _ = (2048 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6u32)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:64:13 + --> tests/ui/manual_div_ceil.rs:87:13 | LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:70:13 + --> tests/ui/manual_div_ceil.rs:93:13 | LL | let _ = (x + 7) / 8; | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` error: manually reimplementing `div_ceil` - --> tests/ui/manual_div_ceil.rs:72:13 + --> tests/ui/manual_div_ceil.rs:95:13 | LL | let _ = (7 + x) / 8; | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` -error: aborting due to 16 previous errors +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/manual_find.rs b/src/tools/clippy/tests/ui/manual_find.rs index 20b557f21d141..7b9846cfe429d 100644 --- a/src/tools/clippy/tests/ui/manual_find.rs +++ b/src/tools/clippy/tests/ui/manual_find.rs @@ -23,4 +23,32 @@ fn tuple(arr: Vec<(String, i32)>) -> Option { None } +mod issue9521 { + fn condition(x: u32, y: u32) -> Result { + todo!() + } + + fn find_with_early_return(v: Vec) -> Option { + for x in v { + if condition(x, 10).ok()? { + return Some(x); + } + } + None + } + + fn find_with_early_break(v: Vec) -> Option { + for x in v { + if if x < 3 { + break; + } else { + x < 10 + } { + return Some(x); + } + } + None + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed index c1c929585cfd3..cd7adc20b127e 100644 --- a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed +++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed @@ -1,5 +1,11 @@ -#![allow(clippy::all)] -#![deny(clippy::manual_ignore_case_cmp)] +#![warn(clippy::manual_ignore_case_cmp)] +#![allow( + clippy::deref_addrof, + clippy::op_ref, + clippy::ptr_arg, + clippy::short_circuit_statement, + clippy::unnecessary_operation +)] use std::ffi::{OsStr, OsString}; diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs index ca401e595fe97..85f6719827c93 100644 --- a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs +++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs @@ -1,5 +1,11 @@ -#![allow(clippy::all)] -#![deny(clippy::manual_ignore_case_cmp)] +#![warn(clippy::manual_ignore_case_cmp)] +#![allow( + clippy::deref_addrof, + clippy::op_ref, + clippy::ptr_arg, + clippy::short_circuit_statement, + clippy::unnecessary_operation +)] use std::ffi::{OsStr, OsString}; diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr index 47378a65799fc..fa7fadd910760 100644 --- a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr +++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr @@ -1,14 +1,11 @@ error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:9:8 + --> tests/ui/manual_ignore_case_cmp.rs:15:8 | LL | if a.to_ascii_lowercase() == b.to_ascii_lowercase() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui/manual_ignore_case_cmp.rs:2:9 - | -LL | #![deny(clippy::manual_ignore_case_cmp)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::manual-ignore-case-cmp` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_ignore_case_cmp)]` help: consider using `.eq_ignore_ascii_case()` instead | LL - if a.to_ascii_lowercase() == b.to_ascii_lowercase() { @@ -16,7 +13,7 @@ LL + if a.eq_ignore_ascii_case(b) { | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:13:8 + --> tests/ui/manual_ignore_case_cmp.rs:19:8 | LL | if a.to_ascii_uppercase() == b.to_ascii_uppercase() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +25,7 @@ LL + if a.eq_ignore_ascii_case(b) { | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:17:13 + --> tests/ui/manual_ignore_case_cmp.rs:23:13 | LL | let r = a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +37,7 @@ LL + let r = a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:19:18 + --> tests/ui/manual_ignore_case_cmp.rs:25:18 | LL | let r = r || a.to_ascii_uppercase() == b.to_ascii_uppercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +49,7 @@ LL + let r = r || a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:21:10 + --> tests/ui/manual_ignore_case_cmp.rs:27:10 | LL | r && a.to_ascii_lowercase() == b.to_uppercase().to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +61,7 @@ LL + r && a.eq_ignore_ascii_case(&b.to_uppercase()); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:24:8 + --> tests/ui/manual_ignore_case_cmp.rs:30:8 | LL | if a.to_ascii_lowercase() != b.to_ascii_lowercase() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,7 +73,7 @@ LL + if !a.eq_ignore_ascii_case(b) { | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:28:8 + --> tests/ui/manual_ignore_case_cmp.rs:34:8 | LL | if a.to_ascii_uppercase() != b.to_ascii_uppercase() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +85,7 @@ LL + if !a.eq_ignore_ascii_case(b) { | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:32:13 + --> tests/ui/manual_ignore_case_cmp.rs:38:13 | LL | let r = a.to_ascii_lowercase() != b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +97,7 @@ LL + let r = !a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:34:18 + --> tests/ui/manual_ignore_case_cmp.rs:40:18 | LL | let r = r || a.to_ascii_uppercase() != b.to_ascii_uppercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +109,7 @@ LL + let r = r || !a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:36:10 + --> tests/ui/manual_ignore_case_cmp.rs:42:10 | LL | r && a.to_ascii_lowercase() != b.to_uppercase().to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -124,7 +121,7 @@ LL + r && !a.eq_ignore_ascii_case(&b.to_uppercase()); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:48:5 + --> tests/ui/manual_ignore_case_cmp.rs:54:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +133,7 @@ LL + a.eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:52:5 + --> tests/ui/manual_ignore_case_cmp.rs:58:5 | LL | a.to_ascii_lowercase() == 'a'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -148,7 +145,7 @@ LL + a.eq_ignore_ascii_case(&'a'); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:54:5 + --> tests/ui/manual_ignore_case_cmp.rs:60:5 | LL | 'a' == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -160,7 +157,7 @@ LL + 'a'.eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:58:5 + --> tests/ui/manual_ignore_case_cmp.rs:64:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -172,7 +169,7 @@ LL + a.eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:60:5 + --> tests/ui/manual_ignore_case_cmp.rs:66:5 | LL | a.to_ascii_lowercase() == b'a'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -184,7 +181,7 @@ LL + a.eq_ignore_ascii_case(&b'a'); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:62:5 + --> tests/ui/manual_ignore_case_cmp.rs:68:5 | LL | b'a' == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -196,7 +193,7 @@ LL + b'a'.eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:66:5 + --> tests/ui/manual_ignore_case_cmp.rs:72:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -208,7 +205,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:68:5 + --> tests/ui/manual_ignore_case_cmp.rs:74:5 | LL | a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -220,7 +217,7 @@ LL + a.to_uppercase().eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:70:5 + --> tests/ui/manual_ignore_case_cmp.rs:76:5 | LL | a.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -232,7 +229,7 @@ LL + a.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:72:5 + --> tests/ui/manual_ignore_case_cmp.rs:78:5 | LL | "a" == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -244,7 +241,7 @@ LL + "a".eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:76:5 + --> tests/ui/manual_ignore_case_cmp.rs:82:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -256,7 +253,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:78:5 + --> tests/ui/manual_ignore_case_cmp.rs:84:5 | LL | a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -268,7 +265,7 @@ LL + a.to_uppercase().eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:80:5 + --> tests/ui/manual_ignore_case_cmp.rs:86:5 | LL | a.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -280,7 +277,7 @@ LL + a.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:82:5 + --> tests/ui/manual_ignore_case_cmp.rs:88:5 | LL | "a" == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -292,7 +289,7 @@ LL + "a".eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:86:5 + --> tests/ui/manual_ignore_case_cmp.rs:92:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -304,7 +301,7 @@ LL + a.eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:88:5 + --> tests/ui/manual_ignore_case_cmp.rs:94:5 | LL | a.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -316,7 +313,7 @@ LL + a.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:90:5 + --> tests/ui/manual_ignore_case_cmp.rs:96:5 | LL | "a" == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -328,7 +325,7 @@ LL + "a".eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:94:5 + --> tests/ui/manual_ignore_case_cmp.rs:100:5 | LL | a.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -340,7 +337,7 @@ LL + a.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:96:5 + --> tests/ui/manual_ignore_case_cmp.rs:102:5 | LL | "a" == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -352,7 +349,7 @@ LL + "a".eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:100:5 + --> tests/ui/manual_ignore_case_cmp.rs:106:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -364,7 +361,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:102:5 + --> tests/ui/manual_ignore_case_cmp.rs:108:5 | LL | a.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -376,7 +373,7 @@ LL + a.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:104:5 + --> tests/ui/manual_ignore_case_cmp.rs:110:5 | LL | "a" == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -388,7 +385,7 @@ LL + "a".eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:107:5 + --> tests/ui/manual_ignore_case_cmp.rs:113:5 | LL | b.to_ascii_lowercase() == a.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -400,7 +397,7 @@ LL + b.eq_ignore_ascii_case(&a); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:109:5 + --> tests/ui/manual_ignore_case_cmp.rs:115:5 | LL | b.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -412,7 +409,7 @@ LL + b.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:111:5 + --> tests/ui/manual_ignore_case_cmp.rs:117:5 | LL | "a" == a.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -424,7 +421,7 @@ LL + "a".eq_ignore_ascii_case(&a); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:115:5 + --> tests/ui/manual_ignore_case_cmp.rs:121:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -436,7 +433,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:117:5 + --> tests/ui/manual_ignore_case_cmp.rs:123:5 | LL | a.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -448,7 +445,7 @@ LL + a.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:119:5 + --> tests/ui/manual_ignore_case_cmp.rs:125:5 | LL | "a" == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -460,7 +457,7 @@ LL + "a".eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:122:5 + --> tests/ui/manual_ignore_case_cmp.rs:128:5 | LL | b.to_ascii_lowercase() == a.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -472,7 +469,7 @@ LL + b.eq_ignore_ascii_case(&a); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:124:5 + --> tests/ui/manual_ignore_case_cmp.rs:130:5 | LL | b.to_ascii_lowercase() == "a"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -484,7 +481,7 @@ LL + b.eq_ignore_ascii_case("a"); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:126:5 + --> tests/ui/manual_ignore_case_cmp.rs:132:5 | LL | "a" == a.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -496,7 +493,7 @@ LL + "a".eq_ignore_ascii_case(&a); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:130:5 + --> tests/ui/manual_ignore_case_cmp.rs:136:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -508,7 +505,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:134:5 + --> tests/ui/manual_ignore_case_cmp.rs:140:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -520,7 +517,7 @@ LL + a.eq_ignore_ascii_case(&b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:138:5 + --> tests/ui/manual_ignore_case_cmp.rs:144:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -532,7 +529,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:140:5 + --> tests/ui/manual_ignore_case_cmp.rs:146:5 | LL | b.to_ascii_lowercase() == a.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -544,7 +541,7 @@ LL + b.eq_ignore_ascii_case(&a); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:144:5 + --> tests/ui/manual_ignore_case_cmp.rs:150:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -556,7 +553,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:148:5 + --> tests/ui/manual_ignore_case_cmp.rs:154:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -568,7 +565,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:152:5 + --> tests/ui/manual_ignore_case_cmp.rs:158:5 | LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -580,7 +577,7 @@ LL + a.eq_ignore_ascii_case(b); | error: manual case-insensitive ASCII comparison - --> tests/ui/manual_ignore_case_cmp.rs:154:5 + --> tests/ui/manual_ignore_case_cmp.rs:160:5 | LL | b.to_ascii_lowercase() == a.to_ascii_lowercase(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_inspect.fixed b/src/tools/clippy/tests/ui/manual_inspect.fixed index 44f15d61f8563..9b768dbad700c 100644 --- a/src/tools/clippy/tests/ui/manual_inspect.fixed +++ b/src/tools/clippy/tests/ui/manual_inspect.fixed @@ -1,5 +1,5 @@ +#![allow(clippy::no_effect, clippy::op_ref, clippy::uninlined_format_args)] #![warn(clippy::manual_inspect)] -#![allow(clippy::no_effect, clippy::op_ref)] fn main() { let _ = Some(0).inspect(|&x| { @@ -185,3 +185,12 @@ fn main() { }); } } + +#[rustfmt::skip] +fn layout_check() { + if let Some(x) = Some(1).inspect(|&x| { println!("{x}"); //~ manual_inspect + // Do not collapse code into this comment + }) { + println!("{x}"); + } +} diff --git a/src/tools/clippy/tests/ui/manual_inspect.rs b/src/tools/clippy/tests/ui/manual_inspect.rs index d34f2abce6ae1..e679636201e6a 100644 --- a/src/tools/clippy/tests/ui/manual_inspect.rs +++ b/src/tools/clippy/tests/ui/manual_inspect.rs @@ -1,5 +1,5 @@ +#![allow(clippy::no_effect, clippy::op_ref, clippy::uninlined_format_args)] #![warn(clippy::manual_inspect)] -#![allow(clippy::no_effect, clippy::op_ref)] fn main() { let _ = Some(0).map(|x| { @@ -197,3 +197,12 @@ fn main() { }); } } + +#[rustfmt::skip] +fn layout_check() { + if let Some(x) = Some(1).map(|x| { println!("{x}"); //~ manual_inspect + // Do not collapse code into this comment + x }) { + println!("{x}"); + } +} diff --git a/src/tools/clippy/tests/ui/manual_inspect.stderr b/src/tools/clippy/tests/ui/manual_inspect.stderr index 510325d2baaa9..78b085fdfca30 100644 --- a/src/tools/clippy/tests/ui/manual_inspect.stderr +++ b/src/tools/clippy/tests/ui/manual_inspect.stderr @@ -187,5 +187,18 @@ LL | LL ~ println!("{}", x); | -error: aborting due to 13 previous errors +error: using `map` over `inspect` + --> tests/ui/manual_inspect.rs:203:30 + | +LL | if let Some(x) = Some(1).map(|x| { println!("{x}"); + | ^^^ + | +help: try + | +LL ~ if let Some(x) = Some(1).inspect(|&x| { println!("{x}"); +LL | // Do not collapse code into this comment +LL ~ }) { + | + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed b/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed index 6f29d76bd2109..8a1ab785dfbfd 100644 --- a/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed +++ b/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed @@ -1,4 +1,17 @@ #![warn(clippy::manual_is_power_of_two)] +#![allow(clippy::precedence)] + +macro_rules! binop { + ($a: expr, equal, $b: expr) => { + $a == $b + }; + ($a: expr, and, $b: expr) => { + $a & $b + }; + ($a: expr, minus, $b: expr) => { + $a - $b + }; +} fn main() { let a = 16_u64; @@ -7,6 +20,8 @@ fn main() { //~^ manual_is_power_of_two let _ = a.is_power_of_two(); //~^ manual_is_power_of_two + let _ = a.is_power_of_two(); + //~^ manual_is_power_of_two // Test different orders of expression let _ = a.is_power_of_two(); @@ -23,4 +38,23 @@ fn main() { // is_power_of_two only works for unsigned integers let _ = b.count_ones() == 1; let _ = b & (b - 1) == 0; + + let i: i32 = 3; + let _ = (i as u32).is_power_of_two(); + //~^ manual_is_power_of_two + + let _ = binop!(a.count_ones(), equal, 1); + let _ = binop!(a, and, a - 1) == 0; + let _ = a & binop!(a, minus, 1) == 0; +} + +#[clippy::msrv = "1.31"] +const fn low_msrv(a: u32) -> bool { + a & (a - 1) == 0 +} + +#[clippy::msrv = "1.32"] +const fn high_msrv(a: u32) -> bool { + a.is_power_of_two() + //~^ manual_is_power_of_two } diff --git a/src/tools/clippy/tests/ui/manual_is_power_of_two.rs b/src/tools/clippy/tests/ui/manual_is_power_of_two.rs index 0c44d7a660b43..57a3b05e0336a 100644 --- a/src/tools/clippy/tests/ui/manual_is_power_of_two.rs +++ b/src/tools/clippy/tests/ui/manual_is_power_of_two.rs @@ -1,10 +1,25 @@ #![warn(clippy::manual_is_power_of_two)] +#![allow(clippy::precedence)] + +macro_rules! binop { + ($a: expr, equal, $b: expr) => { + $a == $b + }; + ($a: expr, and, $b: expr) => { + $a & $b + }; + ($a: expr, minus, $b: expr) => { + $a - $b + }; +} fn main() { let a = 16_u64; let _ = a.count_ones() == 1; //~^ manual_is_power_of_two + let _ = u64::count_ones(a) == 1; + //~^ manual_is_power_of_two let _ = a & (a - 1) == 0; //~^ manual_is_power_of_two @@ -23,4 +38,23 @@ fn main() { // is_power_of_two only works for unsigned integers let _ = b.count_ones() == 1; let _ = b & (b - 1) == 0; + + let i: i32 = 3; + let _ = i as u32 & (i as u32 - 1) == 0; + //~^ manual_is_power_of_two + + let _ = binop!(a.count_ones(), equal, 1); + let _ = binop!(a, and, a - 1) == 0; + let _ = a & binop!(a, minus, 1) == 0; +} + +#[clippy::msrv = "1.31"] +const fn low_msrv(a: u32) -> bool { + a & (a - 1) == 0 +} + +#[clippy::msrv = "1.32"] +const fn high_msrv(a: u32) -> bool { + a & (a - 1) == 0 + //~^ manual_is_power_of_two } diff --git a/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr b/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr index ad12ee10565f6..5781a093d5f2b 100644 --- a/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr +++ b/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr @@ -1,5 +1,5 @@ error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:6:13 + --> tests/ui/manual_is_power_of_two.rs:19:13 | LL | let _ = a.count_ones() == 1; | ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` @@ -8,34 +8,52 @@ LL | let _ = a.count_ones() == 1; = help: to override `-D warnings` add `#[allow(clippy::manual_is_power_of_two)]` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:8:13 + --> tests/ui/manual_is_power_of_two.rs:21:13 + | +LL | let _ = u64::count_ones(a) == 1; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:23:13 | LL | let _ = a & (a - 1) == 0; | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:12:13 + --> tests/ui/manual_is_power_of_two.rs:27:13 | LL | let _ = 1 == a.count_ones(); | ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:14:13 + --> tests/ui/manual_is_power_of_two.rs:29:13 | LL | let _ = (a - 1) & a == 0; | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:16:13 + --> tests/ui/manual_is_power_of_two.rs:31:13 | LL | let _ = 0 == a & (a - 1); | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` error: manually reimplementing `is_power_of_two` - --> tests/ui/manual_is_power_of_two.rs:18:13 + --> tests/ui/manual_is_power_of_two.rs:33:13 | LL | let _ = 0 == (a - 1) & a; | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` -error: aborting due to 6 previous errors +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:43:13 + | +LL | let _ = i as u32 & (i as u32 - 1) == 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `(i as u32).is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:58:5 + | +LL | a & (a - 1) == 0 + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/manual_let_else.rs b/src/tools/clippy/tests/ui/manual_let_else.rs index d2350d97ed003..3781ba1676f5e 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.rs +++ b/src/tools/clippy/tests/ui/manual_let_else.rs @@ -480,3 +480,69 @@ fn issue12337() { //~^ manual_let_else }; } + +mod issue13768 { + enum Foo { + Str(String), + None, + } + + fn foo(value: Foo) { + let signature = match value { + //~^ manual_let_else + Foo::Str(ref val) => val, + _ => { + println!("No signature found"); + return; + }, + }; + } + + enum Bar { + Str { inner: String }, + None, + } + + fn bar(mut value: Bar) { + let signature = match value { + //~^ manual_let_else + Bar::Str { ref mut inner } => inner, + _ => { + println!("No signature found"); + return; + }, + }; + } +} + +mod issue14598 { + fn bar() -> Result { + let value = match foo() { + //~^ manual_let_else + Err(_) => return Err("abc"), + Ok(value) => value, + }; + + let w = Some(0); + let v = match w { + //~^ manual_let_else + None => return Err("abc"), + Some(x) => x, + }; + + enum Foo { + Foo(T), + } + + let v = match Foo::Foo(Some(())) { + Foo::Foo(Some(_)) => return Err("abc"), + Foo::Foo(v) => v, + }; + + Ok(value == 42) + } + + fn foo() -> Result { + todo!() + } +} diff --git a/src/tools/clippy/tests/ui/manual_let_else.stderr b/src/tools/clippy/tests/ui/manual_let_else.stderr index 8f5cba64d545c..a1eea04192919 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else.stderr @@ -489,5 +489,65 @@ error: this could be rewritten as `let...else` LL | let v = if let Some(v_some) = g() { v_some } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` -error: aborting due to 31 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:491:9 + | +LL | / let signature = match value { +LL | | +LL | | Foo::Str(ref val) => val, +LL | | _ => { +... | +LL | | }, +LL | | }; + | |__________^ + | +help: consider writing + | +LL ~ let Foo::Str(ref signature) = value else { +LL + println!("No signature found"); +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:507:9 + | +LL | / let signature = match value { +LL | | +LL | | Bar::Str { ref mut inner } => inner, +LL | | _ => { +... | +LL | | }, +LL | | }; + | |__________^ + | +help: consider writing + | +LL ~ let Bar::Str { inner: ref mut signature } = value else { +LL + println!("No signature found"); +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:520:9 + | +LL | / let value = match foo() { +LL | | +LL | | Err(_) => return Err("abc"), +LL | | Ok(value) => value, +LL | | }; + | |__________^ help: consider writing: `let Ok(value) = foo() else { return Err("abc") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:527:9 + | +LL | / let v = match w { +LL | | +LL | | None => return Err("abc"), +LL | | Some(x) => x, +LL | | }; + | |__________^ help: consider writing: `let Some(v) = w else { return Err("abc") };` + +error: aborting due to 35 previous errors diff --git a/src/tools/clippy/tests/ui/manual_map_option.rs b/src/tools/clippy/tests/ui/manual_map_option.rs index 9477d0d795d2d..40133748d4599 100644 --- a/src/tools/clippy/tests/ui/manual_map_option.rs +++ b/src/tools/clippy/tests/ui/manual_map_option.rs @@ -101,7 +101,7 @@ fn main() { match &mut Some(String::new()) { //~^ manual_map - Some(ref x) => Some(x.len()), + &mut Some(ref x) => Some(x.len()), None => None, }; diff --git a/src/tools/clippy/tests/ui/manual_map_option.stderr b/src/tools/clippy/tests/ui/manual_map_option.stderr index 8f9bce4c265c9..486379c1e5f33 100644 --- a/src/tools/clippy/tests/ui/manual_map_option.stderr +++ b/src/tools/clippy/tests/ui/manual_map_option.stderr @@ -127,7 +127,7 @@ error: manual implementation of `Option::map` | LL | / match &mut Some(String::new()) { LL | | -LL | | Some(ref x) => Some(x.len()), +LL | | &mut Some(ref x) => Some(x.len()), LL | | None => None, LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.len())` diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.fixed b/src/tools/clippy/tests/ui/manual_map_option_2.fixed index d698cc74ea65a..206c6d5d07763 100644 --- a/src/tools/clippy/tests/ui/manual_map_option_2.fixed +++ b/src/tools/clippy/tests/ui/manual_map_option_2.fixed @@ -115,7 +115,7 @@ mod with_type_coercion { fn with_fn_ret(s: &Option) -> Option<(String, &str)> { // Don't lint, `map` doesn't work as the return type is adjusted. match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, } } @@ -124,7 +124,7 @@ mod with_type_coercion { if true { // Don't lint, `map` doesn't work as the return type is adjusted. return match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, }; } @@ -136,7 +136,7 @@ mod with_type_coercion { let x: Option<(String, &'a str)>; x = { match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, } }; diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.rs b/src/tools/clippy/tests/ui/manual_map_option_2.rs index 069c2381f6db1..a47dc950760e2 100644 --- a/src/tools/clippy/tests/ui/manual_map_option_2.rs +++ b/src/tools/clippy/tests/ui/manual_map_option_2.rs @@ -143,7 +143,7 @@ mod with_type_coercion { fn with_fn_ret(s: &Option) -> Option<(String, &str)> { // Don't lint, `map` doesn't work as the return type is adjusted. match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, } } @@ -152,7 +152,7 @@ mod with_type_coercion { if true { // Don't lint, `map` doesn't work as the return type is adjusted. return match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, }; } @@ -164,7 +164,7 @@ mod with_type_coercion { let x: Option<(String, &'a str)>; x = { match s { - Some(x) => Some({ if let Some(ref s) = s { (x.clone(), s) } else { panic!() } }), + Some(x) => Some({ if let Some(s) = s { (x.clone(), s) } else { panic!() } }), None => None, } }; diff --git a/src/tools/clippy/tests/ui/manual_ok_err.fixed b/src/tools/clippy/tests/ui/manual_ok_err.fixed index bc169b64be9fd..e6f799aa58d61 100644 --- a/src/tools/clippy/tests/ui/manual_ok_err.fixed +++ b/src/tools/clippy/tests/ui/manual_ok_err.fixed @@ -80,6 +80,11 @@ fn no_lint() { Ok(3) => None, Ok(v) => Some(v), }; + + let _ = match funcall() { + Ok(v @ 1..) => Some(v), + _ => None, + }; } const fn cf(x: Result) -> Option { diff --git a/src/tools/clippy/tests/ui/manual_ok_err.rs b/src/tools/clippy/tests/ui/manual_ok_err.rs index 03c730d4b4e46..972b2c41ee7aa 100644 --- a/src/tools/clippy/tests/ui/manual_ok_err.rs +++ b/src/tools/clippy/tests/ui/manual_ok_err.rs @@ -116,6 +116,11 @@ fn no_lint() { Ok(3) => None, Ok(v) => Some(v), }; + + let _ = match funcall() { + Ok(v @ 1..) => Some(v), + _ => None, + }; } const fn cf(x: Result) -> Option { diff --git a/src/tools/clippy/tests/ui/manual_ok_err.stderr b/src/tools/clippy/tests/ui/manual_ok_err.stderr index 13fceacda1074..040e170f397e2 100644 --- a/src/tools/clippy/tests/ui/manual_ok_err.stderr +++ b/src/tools/clippy/tests/ui/manual_ok_err.stderr @@ -94,7 +94,7 @@ LL | | }; | |_____^ help: replace with: `(-S).ok()` error: manual implementation of `ok` - --> tests/ui/manual_ok_err.rs:132:12 + --> tests/ui/manual_ok_err.rs:137:12 | LL | } else if let Ok(n) = "1".parse::() { | ____________^ diff --git a/src/tools/clippy/tests/ui/manual_retain.fixed b/src/tools/clippy/tests/ui/manual_retain.fixed index ca8491131c06c..016f520e216c0 100644 --- a/src/tools/clippy/tests/ui/manual_retain.fixed +++ b/src/tools/clippy/tests/ui/manual_retain.fixed @@ -1,5 +1,5 @@ #![warn(clippy::manual_retain)] -#![allow(unused, clippy::redundant_clone)] +#![allow(unused, clippy::needless_borrowed_reference, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; fn main() { @@ -31,7 +31,7 @@ fn binary_heap_retain() { // Do lint, because we use pattern matching let mut tuples = BinaryHeap::from([(0, 1), (1, 2), (2, 3)]); - tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|&(ref x, ref y)| *x == 0); //~^ manual_retain tuples.retain(|(x, y)| *x == 0); //~^ manual_retain @@ -99,7 +99,7 @@ fn btree_set_retain() { // Do lint, because we use pattern matching let mut tuples = BTreeSet::from([(0, 1), (1, 2), (2, 3)]); - tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|&(ref x, ref y)| *x == 0); //~^ manual_retain tuples.retain(|(x, y)| *x == 0); //~^ manual_retain @@ -166,7 +166,7 @@ fn hash_set_retain() { // Do lint, because we use pattern matching let mut tuples = HashSet::from([(0, 1), (1, 2), (2, 3)]); - tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|&(ref x, ref y)| *x == 0); //~^ manual_retain tuples.retain(|(x, y)| *x == 0); //~^ manual_retain @@ -220,7 +220,7 @@ fn vec_retain() { // Do lint, because we use pattern matching let mut tuples = vec![(0, 1), (1, 2), (2, 3)]; - tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|&(ref x, ref y)| *x == 0); //~^ manual_retain tuples.retain(|(x, y)| *x == 0); //~^ manual_retain diff --git a/src/tools/clippy/tests/ui/manual_retain.rs b/src/tools/clippy/tests/ui/manual_retain.rs index cd05a41f3f25a..62f9b7b0595d0 100644 --- a/src/tools/clippy/tests/ui/manual_retain.rs +++ b/src/tools/clippy/tests/ui/manual_retain.rs @@ -1,5 +1,5 @@ #![warn(clippy::manual_retain)] -#![allow(unused, clippy::redundant_clone)] +#![allow(unused, clippy::needless_borrowed_reference, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; fn main() { @@ -31,7 +31,7 @@ fn binary_heap_retain() { // Do lint, because we use pattern matching let mut tuples = BinaryHeap::from([(0, 1), (1, 2), (2, 3)]); - tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); //~^ manual_retain tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); //~^ manual_retain @@ -103,7 +103,7 @@ fn btree_set_retain() { // Do lint, because we use pattern matching let mut tuples = BTreeSet::from([(0, 1), (1, 2), (2, 3)]); - tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); //~^ manual_retain tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); //~^ manual_retain @@ -174,7 +174,7 @@ fn hash_set_retain() { // Do lint, because we use pattern matching let mut tuples = HashSet::from([(0, 1), (1, 2), (2, 3)]); - tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); //~^ manual_retain tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); //~^ manual_retain @@ -228,7 +228,7 @@ fn vec_retain() { // Do lint, because we use pattern matching let mut tuples = vec![(0, 1), (1, 2), (2, 3)]; - tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); //~^ manual_retain tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); //~^ manual_retain diff --git a/src/tools/clippy/tests/ui/manual_retain.stderr b/src/tools/clippy/tests/ui/manual_retain.stderr index 2f81647dd8b7b..e7d3e34b5d7d4 100644 --- a/src/tools/clippy/tests/ui/manual_retain.stderr +++ b/src/tools/clippy/tests/ui/manual_retain.stderr @@ -22,8 +22,8 @@ LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).cloned().colle error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:34:5 | -LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` +LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:36:5 @@ -74,8 +74,8 @@ LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:106:5 | -LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` +LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:108:5 @@ -126,8 +126,8 @@ LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:177:5 | -LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` +LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:179:5 @@ -162,8 +162,8 @@ LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:231:5 | -LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` +LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` --> tests/ui/manual_retain.rs:233:5 diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed index 3f73d6e5a1a67..304be05f6c4cf 100644 --- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed @@ -1,7 +1,5 @@ #![allow(clippy::legacy_numeric_constants, unused_imports)] -use std::{i32, i128, u32, u128}; - fn main() { let _ = 1u32.saturating_add(1); //~^ manual_saturating_arithmetic diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs index 98246a5cd96ca..c2b570e974ac8 100644 --- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs @@ -1,7 +1,5 @@ #![allow(clippy::legacy_numeric_constants, unused_imports)] -use std::{i32, i128, u32, u128}; - fn main() { let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); //~^ manual_saturating_arithmetic diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr index 9d133d8a073b4..2f006a3ae1702 100644 --- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr @@ -1,5 +1,5 @@ error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:6:13 + --> tests/ui/manual_saturating_arithmetic.rs:4:13 | LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)` @@ -8,19 +8,19 @@ LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); = help: to override `-D warnings` add `#[allow(clippy::manual_saturating_arithmetic)]` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:8:13 + --> tests/ui/manual_saturating_arithmetic.rs:6:13 | LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:10:13 + --> tests/ui/manual_saturating_arithmetic.rs:8:13 | LL | let _ = 1u8.checked_add(1).unwrap_or(255); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u8.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:12:13 + --> tests/ui/manual_saturating_arithmetic.rs:10:13 | LL | let _ = 1u128 | _____________^ @@ -30,49 +30,49 @@ LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455); | |_______________________________________________________________________^ help: consider using `saturating_add`: `1u128.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:18:13 + --> tests/ui/manual_saturating_arithmetic.rs:16:13 | LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_mul`: `1u32.saturating_mul(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:21:13 + --> tests/ui/manual_saturating_arithmetic.rs:19:13 | LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:23:13 + --> tests/ui/manual_saturating_arithmetic.rs:21:13 | LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:25:13 + --> tests/ui/manual_saturating_arithmetic.rs:23:13 | LL | let _ = 1u8.checked_sub(1).unwrap_or(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u8.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:30:13 + --> tests/ui/manual_saturating_arithmetic.rs:28:13 | LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:32:13 + --> tests/ui/manual_saturating_arithmetic.rs:30:13 | LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:34:13 + --> tests/ui/manual_saturating_arithmetic.rs:32:13 | LL | let _ = 1i8.checked_add(1).unwrap_or(127); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:36:13 + --> tests/ui/manual_saturating_arithmetic.rs:34:13 | LL | let _ = 1i128 | _____________^ @@ -82,25 +82,25 @@ LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); | |_______________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:40:13 + --> tests/ui/manual_saturating_arithmetic.rs:38:13 | LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:42:13 + --> tests/ui/manual_saturating_arithmetic.rs:40:13 | LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:44:13 + --> tests/ui/manual_saturating_arithmetic.rs:42:13 | LL | let _ = 1i8.checked_add(-1).unwrap_or(-128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:46:13 + --> tests/ui/manual_saturating_arithmetic.rs:44:13 | LL | let _ = 1i128 | _____________^ @@ -110,25 +110,25 @@ LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); | |________________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:54:13 + --> tests/ui/manual_saturating_arithmetic.rs:52:13 | LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:56:13 + --> tests/ui/manual_saturating_arithmetic.rs:54:13 | LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:58:13 + --> tests/ui/manual_saturating_arithmetic.rs:56:13 | LL | let _ = 1i8.checked_sub(1).unwrap_or(-128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:60:13 + --> tests/ui/manual_saturating_arithmetic.rs:58:13 | LL | let _ = 1i128 | _____________^ @@ -138,25 +138,25 @@ LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); | |________________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:64:13 + --> tests/ui/manual_saturating_arithmetic.rs:62:13 | LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:66:13 + --> tests/ui/manual_saturating_arithmetic.rs:64:13 | LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:68:13 + --> tests/ui/manual_saturating_arithmetic.rs:66:13 | LL | let _ = 1i8.checked_sub(-1).unwrap_or(127); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(-1)` error: manual saturating arithmetic - --> tests/ui/manual_saturating_arithmetic.rs:70:13 + --> tests/ui/manual_saturating_arithmetic.rs:68:13 | LL | let _ = 1i128 | _____________^ diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.fixed b/src/tools/clippy/tests/ui/manual_slice_fill.fixed index bba863247f5d3..d07d1d60e2c16 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.fixed +++ b/src/tools/clippy/tests/ui/manual_slice_fill.fixed @@ -123,3 +123,40 @@ fn issue14189() { *b = !*b; } } + +mod issue14685 { + use std::ops::{Index, IndexMut}; + + #[derive(Clone)] + struct ZipList(T); + + impl ZipList { + fn len(&self) -> usize { + todo!() + } + + fn is_empty(&self) -> bool { + todo!() + } + } + + impl Index for ZipList { + type Output = T; + + fn index(&self, _: usize) -> &Self::Output { + todo!() + } + } + + impl IndexMut for ZipList { + fn index_mut(&mut self, _: usize) -> &mut Self::Output { + todo!() + } + } + + fn index_mut(mut zl: ZipList) { + for i in 0..zl.len() { + zl[i] = 6; + } + } +} diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.rs b/src/tools/clippy/tests/ui/manual_slice_fill.rs index 44c60dc40f074..c74ab2225c0a4 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.rs +++ b/src/tools/clippy/tests/ui/manual_slice_fill.rs @@ -136,3 +136,40 @@ fn issue14189() { *b = !*b; } } + +mod issue14685 { + use std::ops::{Index, IndexMut}; + + #[derive(Clone)] + struct ZipList(T); + + impl ZipList { + fn len(&self) -> usize { + todo!() + } + + fn is_empty(&self) -> bool { + todo!() + } + } + + impl Index for ZipList { + type Output = T; + + fn index(&self, _: usize) -> &Self::Output { + todo!() + } + } + + impl IndexMut for ZipList { + fn index_mut(&mut self, _: usize) -> &mut Self::Output { + todo!() + } + } + + fn index_mut(mut zl: ZipList) { + for i in 0..zl.len() { + zl[i] = 6; + } + } +} diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed b/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed index 294d1e1506dec..090f0fd30c555 100644 --- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed +++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed @@ -60,7 +60,26 @@ fn main() { let _ = size_of::() * 5 * s_i32.len(); // Ok (MISSED OPPORTUNITY) } -const fn _const(s_i32: &[i32]) { - // True negative: - let _ = s_i32.len() * size_of::(); // Ok, can't use size_of_val in const +#[clippy::msrv = "1.85"] +const fn const_ok(s_i32: &[i32]) { + let _ = std::mem::size_of_val(s_i32); + //~^ manual_slice_size_calculation +} + +#[clippy::msrv = "1.84"] +const fn const_before_msrv(s_i32: &[i32]) { + let _ = s_i32.len() * size_of::(); +} + +fn issue_14802() { + struct IcedSlice { + dst: [u8], + } + + impl IcedSlice { + fn get_len(&self) -> usize { + std::mem::size_of_val(&self.dst) + //~^ manual_slice_size_calculation + } + } } diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs index ae5225663139b..3c19a0eb5cea1 100644 --- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs +++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs @@ -60,7 +60,26 @@ fn main() { let _ = size_of::() * 5 * s_i32.len(); // Ok (MISSED OPPORTUNITY) } -const fn _const(s_i32: &[i32]) { - // True negative: - let _ = s_i32.len() * size_of::(); // Ok, can't use size_of_val in const +#[clippy::msrv = "1.85"] +const fn const_ok(s_i32: &[i32]) { + let _ = s_i32.len() * size_of::(); + //~^ manual_slice_size_calculation +} + +#[clippy::msrv = "1.84"] +const fn const_before_msrv(s_i32: &[i32]) { + let _ = s_i32.len() * size_of::(); +} + +fn issue_14802() { + struct IcedSlice { + dst: [u8], + } + + impl IcedSlice { + fn get_len(&self) -> usize { + self.dst.len() * size_of::() + //~^ manual_slice_size_calculation + } + } } diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr index f07e97a1c8638..8e9b49e4bf295 100644 --- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr +++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr @@ -55,5 +55,17 @@ error: manual slice size calculation LL | let _ = external!(&[1u64][..]).len() * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(external!(&[1u64][..]))` -error: aborting due to 9 previous errors +error: manual slice size calculation + --> tests/ui/manual_slice_size_calculation.rs:65:13 + | +LL | let _ = s_i32.len() * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)` + +error: manual slice size calculation + --> tests/ui/manual_slice_size_calculation.rs:81:13 + | +LL | self.dst.len() * size_of::() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(&self.dst)` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/manual_strip_fixable.fixed b/src/tools/clippy/tests/ui/manual_strip_fixable.fixed index 75a3f1645de33..b59e3719d951d 100644 --- a/src/tools/clippy/tests/ui/manual_strip_fixable.fixed +++ b/src/tools/clippy/tests/ui/manual_strip_fixable.fixed @@ -1,4 +1,5 @@ #![warn(clippy::manual_strip)] +#![allow(clippy::uninlined_format_args)] fn main() { let s = "abc"; diff --git a/src/tools/clippy/tests/ui/manual_strip_fixable.rs b/src/tools/clippy/tests/ui/manual_strip_fixable.rs index 5080068449e20..4fb3a9bf007f6 100644 --- a/src/tools/clippy/tests/ui/manual_strip_fixable.rs +++ b/src/tools/clippy/tests/ui/manual_strip_fixable.rs @@ -1,4 +1,5 @@ #![warn(clippy::manual_strip)] +#![allow(clippy::uninlined_format_args)] fn main() { let s = "abc"; diff --git a/src/tools/clippy/tests/ui/manual_strip_fixable.stderr b/src/tools/clippy/tests/ui/manual_strip_fixable.stderr index 1c276e5d8fdfe..da8b0cd08f893 100644 --- a/src/tools/clippy/tests/ui/manual_strip_fixable.stderr +++ b/src/tools/clippy/tests/ui/manual_strip_fixable.stderr @@ -1,11 +1,11 @@ error: stripping a prefix manually - --> tests/ui/manual_strip_fixable.rs:7:24 + --> tests/ui/manual_strip_fixable.rs:8:24 | LL | let stripped = &s["ab".len()..]; | ^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> tests/ui/manual_strip_fixable.rs:6:5 + --> tests/ui/manual_strip_fixable.rs:7:5 | LL | if s.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,13 +19,13 @@ LL ~ println!("{stripped}{}", stripped); | error: stripping a suffix manually - --> tests/ui/manual_strip_fixable.rs:13:24 + --> tests/ui/manual_strip_fixable.rs:14:24 | LL | let stripped = &s[..s.len() - "bc".len()]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the suffix was tested here - --> tests/ui/manual_strip_fixable.rs:12:5 + --> tests/ui/manual_strip_fixable.rs:13:5 | LL | if s.ends_with("bc") { | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed index 07e4bdd483a8c..e12287a709395 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed @@ -18,11 +18,9 @@ fn option_unwrap_or() { // multiline case #[rustfmt::skip] - Some(1).unwrap_or({ - 42 + 42 - + 42 + 42 + 42 - + 42 + 42 + 42 - }); + Some(1).unwrap_or(42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42); // string case Some("Bob").unwrap_or("Alice"); @@ -125,11 +123,9 @@ fn result_unwrap_or() { // multiline case #[rustfmt::skip] - Ok::(1).unwrap_or({ - 42 + 42 - + 42 + 42 + 42 - + 42 + 42 + 42 - }); + Ok::(1).unwrap_or(42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42); // string case Ok::<&str, &str>("Bob").unwrap_or("Alice"); @@ -159,11 +155,7 @@ fn result_unwrap_or() { Ok(s) => s, Err(s) => s, }; - // could lint, but unused_variables takes care of it - match Ok::<&str, &str>("Alice") { - Ok(s) => s, - Err(s) => "Bob", - }; + Ok::<&str, &str>("Alice").unwrap_or("Bob"); Ok::(1).unwrap_or(42); @@ -250,4 +242,12 @@ mod issue_13018 { } } +fn implicit_deref(v: Vec) { + let _ = if let Some(s) = v.first() { s } else { "" }; +} + +fn allowed_manual_unwrap_or_zero() -> u32 { + Some(42).unwrap_or(0) +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.rs b/src/tools/clippy/tests/ui/manual_unwrap_or.rs index c88b6f95da68e..53cffcab5b56c 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.rs @@ -216,8 +216,8 @@ fn result_unwrap_or() { Ok(s) => s, Err(s) => s, }; - // could lint, but unused_variables takes care of it match Ok::<&str, &str>("Alice") { + //~^ manual_unwrap_or Ok(s) => s, Err(s) => "Bob", }; @@ -316,4 +316,17 @@ mod issue_13018 { } } +fn implicit_deref(v: Vec) { + let _ = if let Some(s) = v.first() { s } else { "" }; +} + +fn allowed_manual_unwrap_or_zero() -> u32 { + if let Some(x) = Some(42) { + //~^ manual_unwrap_or + x + } else { + 0 + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or.stderr index a5deb55786e96..320e895fb8237 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.stderr +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.stderr @@ -44,11 +44,9 @@ LL | | }; | help: replace with | -LL ~ Some(1).unwrap_or({ -LL + 42 + 42 -LL + + 42 + 42 + 42 -LL + + 42 + 42 + 42 -LL ~ }); +LL ~ Some(1).unwrap_or(42 + 42 +LL + + 42 + 42 + 42 +LL ~ + 42 + 42 + 42); | error: this pattern reimplements `Option::unwrap_or` @@ -145,11 +143,9 @@ LL | | }; | help: replace with | -LL ~ Ok::(1).unwrap_or({ -LL + 42 + 42 -LL + + 42 + 42 + 42 -LL + + 42 + 42 + 42 -LL ~ }); +LL ~ Ok::(1).unwrap_or(42 + 42 +LL + + 42 + 42 + 42 +LL ~ + 42 + 42 + 42); | error: this pattern reimplements `Result::unwrap_or` @@ -162,6 +158,16 @@ LL | | Err(_) => "Alice", LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` +error: this pattern reimplements `Result::unwrap_or` + --> tests/ui/manual_unwrap_or.rs:219:5 + | +LL | / match Ok::<&str, &str>("Alice") { +LL | | +LL | | Ok(s) => s, +LL | | Err(s) => "Bob", +LL | | }; + | |_____^ help: replace with: `Ok::<&str, &str>("Alice").unwrap_or("Bob")` + error: this pattern reimplements `Result::unwrap_or` --> tests/ui/manual_unwrap_or.rs:225:5 | @@ -184,5 +190,16 @@ LL | | None => 0, LL | | }; | |_________^ help: replace with: `some_macro!().unwrap_or(0)` -error: aborting due to 16 previous errors +error: this pattern reimplements `Option::unwrap_or` + --> tests/ui/manual_unwrap_or.rs:324:5 + | +LL | / if let Some(x) = Some(42) { +LL | | +LL | | x +LL | | } else { +LL | | 0 +LL | | } + | |_____^ help: replace with: `Some(42).unwrap_or(0)` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed index 832376fa5af15..41ca44ceef4e6 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed @@ -1,5 +1,5 @@ #![warn(clippy::manual_unwrap_or_default)] -#![allow(clippy::unnecessary_literal_unwrap, clippy::manual_unwrap_or)] +#![allow(clippy::unnecessary_literal_unwrap)] fn main() { let x: Option> = None; @@ -36,10 +36,12 @@ fn main() { // Issue #12531 unsafe fn no_deref_ptr(a: Option, b: *const Option) -> i32 { - match a { - // `*b` being correct depends on `a == Some(_)` - Some(_) => (*b).unwrap_or_default(), - _ => 0, + unsafe { + match a { + // `*b` being correct depends on `a == Some(_)` + Some(_) => (*b).unwrap_or_default(), + _ => 0, + } } } @@ -99,3 +101,21 @@ fn issue_12928() { let y = if let Some(Y(a, _)) = x { a } else { 0 }; let y = if let Some(Y(a, ..)) = x { a } else { 0 }; } + +// For symetry with `manual_unwrap_or` test +fn allowed_manual_unwrap_or_zero() -> u32 { + Some(42).unwrap_or_default() +} + +mod issue14716 { + struct Foo { + name: Option, + } + + fn bar(project: &Foo) { + let _name = match project.name { + Some(ref x) => x, + None => "", + }; + } +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs index bedb3f0af0f3e..343fbc4879ce6 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs @@ -1,5 +1,5 @@ #![warn(clippy::manual_unwrap_or_default)] -#![allow(clippy::unnecessary_literal_unwrap, clippy::manual_unwrap_or)] +#![allow(clippy::unnecessary_literal_unwrap)] fn main() { let x: Option> = None; @@ -68,14 +68,16 @@ fn main() { // Issue #12531 unsafe fn no_deref_ptr(a: Option, b: *const Option) -> i32 { - match a { - // `*b` being correct depends on `a == Some(_)` - Some(_) => match *b { - //~^ manual_unwrap_or_default - Some(v) => v, + unsafe { + match a { + // `*b` being correct depends on `a == Some(_)` + Some(_) => match *b { + //~^ manual_unwrap_or_default + Some(v) => v, + _ => 0, + }, _ => 0, - }, - _ => 0, + } } } @@ -135,3 +137,26 @@ fn issue_12928() { let y = if let Some(Y(a, _)) = x { a } else { 0 }; let y = if let Some(Y(a, ..)) = x { a } else { 0 }; } + +// For symetry with `manual_unwrap_or` test +fn allowed_manual_unwrap_or_zero() -> u32 { + if let Some(x) = Some(42) { + //~^ manual_unwrap_or_default + x + } else { + 0 + } +} + +mod issue14716 { + struct Foo { + name: Option, + } + + fn bar(project: &Foo) { + let _name = match project.name { + Some(ref x) => x, + None => "", + }; + } +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr index ca9aa159152e3..e8f38a2e3899e 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr @@ -76,15 +76,26 @@ LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` error: match can be simplified with `.unwrap_or_default()` - --> tests/ui/manual_unwrap_or_default.rs:73:20 + --> tests/ui/manual_unwrap_or_default.rs:74:24 | -LL | Some(_) => match *b { - | ____________________^ +LL | Some(_) => match *b { + | ________________________^ LL | | -LL | | Some(v) => v, -LL | | _ => 0, -LL | | }, - | |_________^ help: replace it with: `(*b).unwrap_or_default()` +LL | | Some(v) => v, +LL | | _ => 0, +LL | | }, + | |_____________^ help: replace it with: `(*b).unwrap_or_default()` -error: aborting due to 8 previous errors +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:143:5 + | +LL | / if let Some(x) = Some(42) { +LL | | +LL | | x +LL | | } else { +LL | | 0 +LL | | } + | |_____^ help: replace it with: `Some(42).unwrap_or_default()` + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.fixed b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed index 948fec970d869..f8379ed23c5b2 100644 --- a/src/tools/clippy/tests/ui/map_flatten_fixable.fixed +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed @@ -1,10 +1,11 @@ -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::let_underscore_untyped)] -#![allow(clippy::missing_docs_in_private_items)] -#![allow(clippy::map_identity)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::unnecessary_wraps)] #![feature(result_flattening)] +#![allow( + clippy::let_underscore_untyped, + clippy::missing_docs_in_private_items, + clippy::map_identity, + clippy::redundant_closure, + clippy::unnecessary_wraps +)] fn main() { // mapping to Option on Iterator diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.rs b/src/tools/clippy/tests/ui/map_flatten_fixable.rs index 67a91ab94147e..040a9ca85f647 100644 --- a/src/tools/clippy/tests/ui/map_flatten_fixable.rs +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.rs @@ -1,10 +1,11 @@ -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::let_underscore_untyped)] -#![allow(clippy::missing_docs_in_private_items)] -#![allow(clippy::map_identity)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::unnecessary_wraps)] #![feature(result_flattening)] +#![allow( + clippy::let_underscore_untyped, + clippy::missing_docs_in_private_items, + clippy::map_identity, + clippy::redundant_closure, + clippy::unnecessary_wraps +)] fn main() { // mapping to Option on Iterator diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.stderr b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr index 05d4d9a6ad85c..fe68eb7e4ab44 100644 --- a/src/tools/clippy/tests/ui/map_flatten_fixable.stderr +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr @@ -1,5 +1,5 @@ error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:16:47 + --> tests/ui/map_flatten_fixable.rs:17:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id)` @@ -8,43 +8,43 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll = help: to override `-D warnings` add `#[allow(clippy::map_flatten)]` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:18:47 + --> tests/ui/map_flatten_fixable.rs:19:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_ref)` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:20:47 + --> tests/ui/map_flatten_fixable.rs:21:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_closure)` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:22:47 + --> tests/ui/map_flatten_fixable.rs:23:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(|x| x.checked_add(1))` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:26:47 + --> tests/ui/map_flatten_fixable.rs:27:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| 0..x)` error: called `map(..).flatten()` on `Option` - --> tests/ui/map_flatten_fixable.rs:30:40 + --> tests/ui/map_flatten_fixable.rs:31:40 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)` error: called `map(..).flatten()` on `Result` - --> tests/ui/map_flatten_fixable.rs:34:42 + --> tests/ui/map_flatten_fixable.rs:35:42 | LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)` error: called `map(..).flatten()` on `Iterator` - --> tests/ui/map_flatten_fixable.rs:44:10 + --> tests/ui/map_flatten_fixable.rs:45:10 | LL | .map(|n| match n { | __________^ @@ -74,7 +74,7 @@ LL ~ }); | error: called `map(..).flatten()` on `Option` - --> tests/ui/map_flatten_fixable.rs:65:10 + --> tests/ui/map_flatten_fixable.rs:66:10 | LL | .map(|_| { | __________^ diff --git a/src/tools/clippy/tests/ui/match_on_vec_items.rs b/src/tools/clippy/tests/ui/match_on_vec_items.rs deleted file mode 100644 index f3174ec9734df..0000000000000 --- a/src/tools/clippy/tests/ui/match_on_vec_items.rs +++ /dev/null @@ -1,161 +0,0 @@ -#![warn(clippy::match_on_vec_items)] -#![allow(clippy::redundant_at_rest_pattern, clippy::useless_vec)] -//@no-rustfix -fn match_with_wildcard() { - let arr = vec![0, 1, 2, 3]; - let range = 1..3; - let idx = 1; - - // Lint, may panic - match arr[idx] { - //~^ match_on_vec_items - 0 => println!("0"), - 1 => println!("1"), - _ => {}, - } - - // Lint, may panic - match arr[range] { - //~^ match_on_vec_items - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - _ => {}, - } -} - -fn match_without_wildcard() { - let arr = vec![0, 1, 2, 3]; - let range = 1..3; - let idx = 2; - - // Lint, may panic - match arr[idx] { - //~^ match_on_vec_items - 0 => println!("0"), - 1 => println!("1"), - num => {}, - } - - // Lint, may panic - match arr[range] { - //~^ match_on_vec_items - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - [ref sub @ ..] => {}, - } -} - -fn match_wildcard_and_action() { - let arr = vec![0, 1, 2, 3]; - let range = 1..3; - let idx = 3; - - // Lint, may panic - match arr[idx] { - //~^ match_on_vec_items - 0 => println!("0"), - 1 => println!("1"), - _ => println!("Hello, World!"), - } - - // Lint, may panic - match arr[range] { - //~^ match_on_vec_items - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - _ => println!("Hello, World!"), - } -} - -fn match_vec_ref() { - let arr = &vec![0, 1, 2, 3]; - let range = 1..3; - let idx = 3; - - // Lint, may panic - match arr[idx] { - //~^ match_on_vec_items - 0 => println!("0"), - 1 => println!("1"), - _ => {}, - } - - // Lint, may panic - match arr[range] { - //~^ match_on_vec_items - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - _ => {}, - } -} - -fn match_with_get() { - let arr = vec![0, 1, 2, 3]; - let range = 1..3; - let idx = 3; - - // Ok - match arr.get(idx) { - Some(0) => println!("0"), - Some(1) => println!("1"), - _ => {}, - } - - // Ok - match arr.get(range) { - Some(&[0, 1]) => println!("0 1"), - Some(&[1, 2]) => println!("1 2"), - _ => {}, - } -} - -fn match_with_array() { - let arr = [0, 1, 2, 3]; - let range = 1..3; - let idx = 3; - - // Ok - match arr[idx] { - 0 => println!("0"), - 1 => println!("1"), - _ => {}, - } - - // Ok - match arr[range] { - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - _ => {}, - } -} - -fn match_with_endless_range() { - let arr = vec![0, 1, 2, 3]; - let range = ..; - - // Ok - match arr[range] { - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - [0, 1, 2, 3] => println!("0, 1, 2, 3"), - _ => {}, - } - - // Ok - match arr[..] { - [0, 1] => println!("0 1"), - [1, 2] => println!("1 2"), - [0, 1, 2, 3] => println!("0, 1, 2, 3"), - _ => {}, - } -} - -fn main() { - match_with_wildcard(); - match_without_wildcard(); - match_wildcard_and_action(); - match_vec_ref(); - match_with_get(); - match_with_array(); - match_with_endless_range(); -} diff --git a/src/tools/clippy/tests/ui/match_on_vec_items.stderr b/src/tools/clippy/tests/ui/match_on_vec_items.stderr deleted file mode 100644 index ae79e1305f7fd..0000000000000 --- a/src/tools/clippy/tests/ui/match_on_vec_items.stderr +++ /dev/null @@ -1,53 +0,0 @@ -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:10:11 - | -LL | match arr[idx] { - | ^^^^^^^^ help: try: `arr.get(idx)` - | - = note: `-D clippy::match-on-vec-items` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::match_on_vec_items)]` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:18:11 - | -LL | match arr[range] { - | ^^^^^^^^^^ help: try: `arr.get(range)` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:32:11 - | -LL | match arr[idx] { - | ^^^^^^^^ help: try: `arr.get(idx)` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:40:11 - | -LL | match arr[range] { - | ^^^^^^^^^^ help: try: `arr.get(range)` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:54:11 - | -LL | match arr[idx] { - | ^^^^^^^^ help: try: `arr.get(idx)` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:62:11 - | -LL | match arr[range] { - | ^^^^^^^^^^ help: try: `arr.get(range)` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:76:11 - | -LL | match arr[idx] { - | ^^^^^^^^ help: try: `arr.get(idx)` - -error: indexing into a vector may panic - --> tests/ui/match_on_vec_items.rs:84:11 - | -LL | match arr[range] { - | ^^^^^^^^^^ help: try: `arr.get(range)` - -error: aborting due to 8 previous errors - diff --git a/src/tools/clippy/tests/ui/match_same_arms.fixed b/src/tools/clippy/tests/ui/match_same_arms.fixed new file mode 100644 index 0000000000000..31684a5759fe9 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms.fixed @@ -0,0 +1,142 @@ +#![allow(clippy::manual_range_patterns)] +#![warn(clippy::match_same_arms)] + +pub enum Abc { + A, + B, + C, +} + +fn match_same_arms() { + let _ = match Abc::A { + Abc::B => 1, + _ => 0, + //~^ match_same_arms + }; + + match 0 { + 1 => 'a', + _ => 'b', + //~^ match_same_arms + }; + + match (1, 2, 3) { + (1, .., 3) | (.., 3) => 42, + //~^ match_same_arms + _ => 0, + }; + + let _ = match 42 { + //~^ match_same_arms + 42 | 51 => 1, + 41 | 52 => 2, + //~^ match_same_arms + _ => 0, + }; + + let _ = match 42 { + //~^ match_same_arms + 1 | 2 | 3 => 2, + 4 => 3, + _ => 0, + }; +} + +mod issue4244 { + #[derive(PartialEq, PartialOrd, Eq, Ord)] + pub enum CommandInfo { + BuiltIn { name: String, about: Option }, + External { name: String, path: std::path::PathBuf }, + } + + impl CommandInfo { + pub fn name(&self) -> String { + match self { + //~^ match_same_arms + CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. } => name.to_string(), + } + } + } +} + +macro_rules! m { + (foo) => {}; + (bar) => {}; +} +macro_rules! foo { + () => { + 1 + }; +} +macro_rules! bar { + () => { + 1 + }; +} + +fn main() { + let x = 0; + let _ = match 0 { + 0 => { + m!(foo); + x + }, + 1 => { + m!(bar); + x + }, + _ => 1, + }; + + let _ = match 0 { + 0 => { + m!(foo); + 0 + }, + 1 => { + m!(bar); + 0 + }, + _ => 1, + }; + + let _ = match 0 { + 0 => { + let mut x = 0; + #[cfg(not_enabled)] + { + x = 5; + } + #[cfg(not(not_enabled))] + { + x = 6; + } + x + }, + 1 => { + let mut x = 0; + #[cfg(also_not_enabled)] + { + x = 5; + } + #[cfg(not(also_not_enabled))] + { + x = 6; + } + x + }, + _ => 0, + }; + + let _ = match 0 { + 0 => foo!(), + 1 => bar!(), + _ => 1, + }; + + let _ = match 0 { + 0 => cfg!(not_enabled), + 1 => cfg!(also_not_enabled), + _ => false, + }; +} diff --git a/src/tools/clippy/tests/ui/match_same_arms.rs b/src/tools/clippy/tests/ui/match_same_arms.rs index 55441948e91b6..39bee01bac22b 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.rs +++ b/src/tools/clippy/tests/ui/match_same_arms.rs @@ -1,4 +1,4 @@ -//@no-rustfix: overlapping suggestions +#![allow(clippy::manual_range_patterns)] #![warn(clippy::match_same_arms)] pub enum Abc { @@ -10,9 +10,17 @@ pub enum Abc { fn match_same_arms() { let _ = match Abc::A { Abc::A => 0, - //~^ match_same_arms Abc::B => 1, _ => 0, + //~^ match_same_arms + }; + + match 0 { + 1 => 'a', + 2 => 'b', + 3 => 'b', + _ => 'b', + //~^ match_same_arms }; match (1, 2, 3) { @@ -24,8 +32,8 @@ fn match_same_arms() { let _ = match 42 { 42 => 1, - 51 => 1, //~^ match_same_arms + 51 => 1, 41 => 2, //~^ match_same_arms 52 => 2, @@ -34,11 +42,9 @@ fn match_same_arms() { let _ = match 42 { 1 => 2, - 2 => 2, //~^ match_same_arms - //~| match_same_arms + 2 => 2, 3 => 2, - //~^ match_same_arms 4 => 3, _ => 0, }; @@ -55,8 +61,8 @@ mod issue4244 { pub fn name(&self) -> String { match self { CommandInfo::BuiltIn { name, .. } => name.to_string(), - CommandInfo::External { name, .. } => name.to_string(), //~^ match_same_arms + CommandInfo::External { name, .. } => name.to_string(), } } } diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr index 3744b83d89cc8..8aa60f8357663 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms.stderr @@ -1,118 +1,121 @@ -error: this match arm has an identical body to the `_` wildcard arm +error: these match arms have identical bodies --> tests/ui/match_same_arms.rs:12:9 | -LL | / Abc::A => 0, -LL | | - | |________^ help: try removing the arm - | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms.rs:15:9 - | +LL | Abc::A => 0, + | ^^^^^^^^^^^ +LL | Abc::B => 1, LL | _ => 0, - | ^^^^^^ + | ^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` +help: otherwise remove the non-wildcard arm + | +LL - Abc::A => 0, + | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:19:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:20:9 + | +LL | 2 => 'b', + | ^^^^^^^^ +LL | 3 => 'b', + | ^^^^^^^^ +LL | _ => 'b', + | ^^^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values +help: otherwise remove the non-wildcard arms + | +LL - 2 => 'b', +LL - 3 => 'b', +LL + _ => 'b', + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:27:9 | LL | (1, .., 3) => 42, | ^^^^^^^^^^^^^^^^ +LL | +LL | (.., 3) => 42, + | ^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ (1, .., 3) | (.., 3) => 42, LL | LL ~ _ => 0, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:27:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:34:9 | +LL | 42 => 1, + | ^^^^^^^ +LL | LL | 51 => 1, | ^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 42 => 1, -LL - 51 => 1, -LL + 51 | 42 => 1, +LL ~ +LL ~ 42 | 51 => 1, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:29:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:37:9 | LL | 41 => 2, | ^^^^^^^ +LL | +LL | 52 => 2, + | ^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ 41 | 52 => 2, LL | LL ~ _ => 0, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:37:9 - | -LL | 2 => 2, - | ^^^^^^ - | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm - | -LL - 1 => 2, -LL - 2 => 2, -LL + 2 | 1 => 2, - | - -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:40:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:44:9 | -LL | 3 => 2, +LL | 1 => 2, | ^^^^^^ - | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm - | -LL ~ 2 => 2, -LL | LL | -LL ~ 3 | 1 => 2, - | - -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:37:9 - | LL | 2 => 2, | ^^^^^^ +LL | 3 => 2, + | ^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL ~ 2 | 3 => 2, -LL | -LL | LL ~ +LL ~ 1 | 2 | 3 => 2, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms.rs:58:17 +error: these match arms have identical bodies + --> tests/ui/match_same_arms.rs:63:17 | +LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | CommandInfo::External { name, .. } => name.to_string(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - CommandInfo::BuiltIn { name, .. } => name.to_string(), -LL - CommandInfo::External { name, .. } => name.to_string(), -LL + CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. } => name.to_string(), +LL ~ +LL ~ CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. } => name.to_string(), | -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms2.fixed b/src/tools/clippy/tests/ui/match_same_arms2.fixed index 0d93e2c728d75..cb860cef1e688 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.fixed +++ b/src/tools/clippy/tests/ui/match_same_arms2.fixed @@ -14,7 +14,7 @@ fn foo() -> bool { fn match_same_arms() { let _ = match 42 { - //~^^^^^^^^^ match_same_arms + //~v match_same_arms _ => { foo(); let mut a = 42 + [23].len() as i32; @@ -27,14 +27,14 @@ fn match_same_arms() { }; let _ = match 42 { - 51 | 42 => foo(), //~^ match_same_arms + 42 | 51 => foo(), _ => true, }; let _ = match Some(42) { - None | Some(_) => 24, //~^ match_same_arms + Some(_) | None => 24, }; let _ = match Some(42) { @@ -55,8 +55,8 @@ fn match_same_arms() { }; match (Some(42), Some(42)) { - (None, Some(a)) | (Some(a), None) => bar(a), //~^ match_same_arms + (Some(a), None) | (None, Some(a)) => bar(a), _ => (), } @@ -69,8 +69,8 @@ fn match_same_arms() { }; let _ = match (Some(42), Some(42)) { - (None, Some(a)) | (Some(a), None) if a == 42 => a, //~^ match_same_arms + (Some(a), None) | (None, Some(a)) if a == 42 => a, _ => 0, }; @@ -124,8 +124,8 @@ fn match_same_arms() { // False negative #2251. match x { Ok(_tmp) => println!("ok"), - Ok(_) | Ok(3) => println!("ok"), //~^ match_same_arms + Ok(3) | Ok(_) => println!("ok"), Err(_) => { unreachable!(); }, @@ -149,10 +149,10 @@ fn match_same_arms() { // still lint if the tokens are the same match 0 { - 1 | 0 => { + //~^^^ match_same_arms + 0 | 1 => { empty!(0); }, - //~^^^ match_same_arms x => { empty!(x); }, @@ -208,9 +208,9 @@ fn main() { // Suggest moving `Foo::X(0)` down. let _ = match Foo::X(0) { - Foo::Y(_) | Foo::Z(0) => 2, - Foo::Z(_) | Foo::X(0) => 1, //~^ match_same_arms + Foo::Y(_) | Foo::Z(0) => 2, + Foo::X(0) | Foo::Z(_) => 1, _ => 0, }; @@ -230,10 +230,10 @@ fn main() { // Lint. let _ = match None { + //~^ match_same_arms Some(Bar { y: 10, z: 0, .. }) => 2, None => 50, - Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, - //~^ match_same_arms + Some(Bar { x: 0, y: 5, .. }) | Some(Bar { y: 0, x: 5, .. }) => 1, _ => 200, }; @@ -246,8 +246,8 @@ fn main() { }; let _ = match 0 { - 1 | 0 => cfg!(not_enable), //~^ match_same_arms + 0 | 1 => cfg!(not_enable), _ => false, }; } @@ -262,9 +262,34 @@ mod with_lifetime { impl<'a> MaybeStaticStr<'a> { fn get(&self) -> &'a str { match *self { - MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, //~^ match_same_arms + MaybeStaticStr::Static(s) | MaybeStaticStr::Borrowed(s) => s, } } } } + +fn lint_levels() { + match 1 { + 0 => "a", + 1 => "b", + #[expect(clippy::match_same_arms)] + _ => "b", + }; + + match 2 { + 0 => "a", + 1 | 2 => "b", + //~^ match_same_arms + #[allow(clippy::match_same_arms)] + _ => "b", + }; + + match 3 { + 0 => "a", + 1 | 2 => "b", + //~^ match_same_arms + #[expect(clippy::match_same_arms)] + _ => "b", + }; +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs index b0ebc4784f331..0fd5d76e7d3e7 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.rs +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -23,7 +23,7 @@ fn match_same_arms() { a = -31 - a; a }, - //~^^^^^^^^^ match_same_arms + //~v match_same_arms _ => { foo(); let mut a = 42 + [23].len() as i32; @@ -37,15 +37,15 @@ fn match_same_arms() { let _ = match 42 { 42 => foo(), - 51 => foo(), //~^ match_same_arms + 51 => foo(), _ => true, }; let _ = match Some(42) { Some(_) => 24, - None => 24, //~^ match_same_arms + None => 24, }; let _ = match Some(42) { @@ -67,8 +67,8 @@ fn match_same_arms() { match (Some(42), Some(42)) { (Some(a), None) => bar(a), - (None, Some(a)) => bar(a), //~^ match_same_arms + (None, Some(a)) => bar(a), _ => (), } @@ -82,8 +82,8 @@ fn match_same_arms() { let _ = match (Some(42), Some(42)) { (Some(a), None) if a == 42 => a, - (None, Some(a)) if a == 42 => a, //~^ match_same_arms + (None, Some(a)) if a == 42 => a, _ => 0, }; @@ -140,8 +140,8 @@ fn match_same_arms() { match x { Ok(_tmp) => println!("ok"), Ok(3) => println!("ok"), - Ok(_) => println!("ok"), //~^ match_same_arms + Ok(_) => println!("ok"), Err(_) => { unreachable!(); }, @@ -168,10 +168,10 @@ fn match_same_arms() { 0 => { empty!(0); }, + //~^^^ match_same_arms 1 => { empty!(0); }, - //~^^^ match_same_arms x => { empty!(x); }, @@ -229,9 +229,9 @@ fn main() { // Suggest moving `Foo::X(0)` down. let _ = match Foo::X(0) { Foo::X(0) => 1, + //~^ match_same_arms Foo::Y(_) | Foo::Z(0) => 2, Foo::Z(_) => 1, - //~^ match_same_arms _ => 0, }; @@ -252,10 +252,10 @@ fn main() { // Lint. let _ = match None { Some(Bar { x: 0, y: 5, .. }) => 1, + //~^ match_same_arms Some(Bar { y: 10, z: 0, .. }) => 2, None => 50, Some(Bar { y: 0, x: 5, .. }) => 1, - //~^ match_same_arms _ => 200, }; @@ -269,8 +269,8 @@ fn main() { let _ = match 0 { 0 => cfg!(not_enable), - 1 => cfg!(not_enable), //~^ match_same_arms + 1 => cfg!(not_enable), _ => false, }; } @@ -286,9 +286,36 @@ mod with_lifetime { fn get(&self) -> &'a str { match *self { MaybeStaticStr::Static(s) => s, - MaybeStaticStr::Borrowed(s) => s, //~^ match_same_arms + MaybeStaticStr::Borrowed(s) => s, } } } } + +fn lint_levels() { + match 1 { + 0 => "a", + 1 => "b", + #[expect(clippy::match_same_arms)] + _ => "b", + }; + + match 2 { + 0 => "a", + 1 => "b", + //~^ match_same_arms + 2 => "b", + #[allow(clippy::match_same_arms)] + _ => "b", + }; + + match 3 { + 0 => "a", + 1 => "b", + //~^ match_same_arms + 2 => "b", + #[expect(clippy::match_same_arms)] + _ => "b", + }; +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr index 21a8743cc3242..f3031619cce56 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -1,4 +1,4 @@ -error: this match arm has an identical body to the `_` wildcard arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:17:9 | LL | / 42 => { @@ -6,14 +6,10 @@ LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; LL | | if true { ... | +LL | | a LL | | }, -LL | | - | |________^ help: try removing the arm - | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms2.rs:27:9 - | + | |_________^ +LL | LL | / _ => { LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; @@ -21,134 +17,169 @@ LL | | if true { ... | LL | | a LL | | }, - | |_________^ + | |_________^ the wildcard arm + | + = help: if this is unintentional make the arms return different values = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` +help: otherwise remove the non-wildcard arm + | +LL - 42 => { +LL - foo(); +LL - let mut a = 42 + [23].len() as i32; +LL - if true { +LL - a += 7; +LL - } +LL - a = -31 - a; +LL - a +LL - }, + | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:40:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:39:9 | +LL | 42 => foo(), + | ^^^^^^^^^^^ +LL | LL | 51 => foo(), | ^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 42 => foo(), -LL - 51 => foo(), -LL + 51 | 42 => foo(), +LL ~ +LL ~ 42 | 51 => foo(), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:47:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:46:9 | +LL | Some(_) => 24, + | ^^^^^^^^^^^^^ +LL | LL | None => 24, | ^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - Some(_) => 24, -LL - None => 24, -LL + None | Some(_) => 24, +LL ~ +LL ~ Some(_) | None => 24, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:70:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:69:9 | +LL | (Some(a), None) => bar(a), + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | (None, Some(a)) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - (Some(a), None) => bar(a), -LL - (None, Some(a)) => bar(a), -LL + (None, Some(a)) | (Some(a), None) => bar(a), +LL ~ +LL ~ (Some(a), None) | (None, Some(a)) => bar(a), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:85:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:84:9 | +LL | (Some(a), None) if a == 42 => a, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | (None, Some(a)) if a == 42 => a, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - (Some(a), None) if a == 42 => a, -LL - (None, Some(a)) if a == 42 => a, -LL + (None, Some(a)) | (Some(a), None) if a == 42 => a, +LL ~ +LL ~ (Some(a), None) | (None, Some(a)) if a == 42 => a, | -error: this match arm has an identical body to another arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:91:9 | LL | (Some(a), ..) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | (.., Some(a)) => bar(a), + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ (Some(a), ..) | (.., Some(a)) => bar(a), LL | LL ~ _ => (), | -error: this match arm has an identical body to another arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:126:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | (Ok(_), Some(x)) => println!("ok {}", x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), LL | LL ~ _ => println!("err"), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:143:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:142:9 | +LL | Ok(3) => println!("ok"), + | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | Ok(_) => println!("ok"), | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - Ok(3) => println!("ok"), -LL - Ok(_) => println!("ok"), -LL + Ok(_) | Ok(3) => println!("ok"), +LL ~ +LL ~ Ok(3) | Ok(_) => println!("ok"), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:171:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:168:9 | +LL | / 0 => { +LL | | empty!(0); +LL | | }, + | |_________^ +LL | LL | / 1 => { LL | | empty!(0); LL | | }, | |_________^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 0 => { -LL - empty!(0); -LL - }, -LL - 1 => { -LL + 1 | 0 => { +LL ~ +LL ~ 0 | 1 => { | -error: this match arm has an identical body to another arm +error: these match arms have identical bodies --> tests/ui/match_same_arms2.rs:222:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ +... +LL | Foo::Z(_) => 1, + | ^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | LL ~ Foo::X(0) | Foo::Z(_) => 1, LL | @@ -156,60 +187,106 @@ LL | Foo::X(_) | Foo::Y(_) => 2, LL ~ _ => 0, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:233:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:231:9 | +LL | Foo::X(0) => 1, + | ^^^^^^^^^^^^^^ +... LL | Foo::Z(_) => 1, | ^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL ~ Foo::Y(_) | Foo::Z(0) => 2, -LL ~ Foo::Z(_) | Foo::X(0) => 1, +LL ~ +LL | Foo::Y(_) | Foo::Z(0) => 2, +LL ~ Foo::X(0) | Foo::Z(_) => 1, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:257:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:254:9 | +LL | Some(Bar { x: 0, y: 5, .. }) => 1, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... LL | Some(Bar { y: 0, x: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL ~ Some(Bar { y: 10, z: 0, .. }) => 2, +LL ~ +LL | Some(Bar { y: 10, z: 0, .. }) => 2, LL | None => 50, -LL ~ Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, +LL ~ Some(Bar { x: 0, y: 5, .. }) | Some(Bar { y: 0, x: 5, .. }) => 1, | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:272:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:271:9 | +LL | 0 => cfg!(not_enable), + | ^^^^^^^^^^^^^^^^^^^^^ +LL | LL | 1 => cfg!(not_enable), | ^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | -LL - 0 => cfg!(not_enable), -LL - 1 => cfg!(not_enable), -LL + 1 | 0 => cfg!(not_enable), +LL ~ +LL ~ 0 | 1 => cfg!(not_enable), | -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:289:17 +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:288:17 | +LL | MaybeStaticStr::Static(s) => s, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | MaybeStaticStr::Borrowed(s) => s, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try changing either arm body -help: or try merging the arm patterns and removing the obsolete arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ +LL ~ MaybeStaticStr::Static(s) | MaybeStaticStr::Borrowed(s) => s, | -LL - MaybeStaticStr::Static(s) => s, -LL - MaybeStaticStr::Borrowed(s) => s, -LL + MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, + +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:306:9 + | +LL | 1 => "b", + | ^^^^^^^^ +LL | +LL | 2 => "b", + | ^^^^^^^^ + | + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ 1 | 2 => "b", +LL | +LL ~ #[allow(clippy::match_same_arms)] + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms2.rs:315:9 + | +LL | 1 => "b", + | ^^^^^^^^ +LL | +LL | 2 => "b", + | ^^^^^^^^ + | + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ 1 | 2 => "b", +LL | +LL ~ #[expect(clippy::match_same_arms)] | -error: aborting due to 14 previous errors +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed index 0c9398933b80e..61a5bd0323a88 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed @@ -7,14 +7,22 @@ fn repeat() -> ! { panic!() } +#[deny(non_exhaustive_omitted_patterns)] pub fn f(x: Ordering) { - #[deny(non_exhaustive_omitted_patterns)] match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => repeat(), - _ => repeat(), + //~^ match_same_arms + Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + } + + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + //~^ match_same_arms + Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), } } @@ -28,21 +36,21 @@ mod f { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - Ordering::AcqRel | Ordering::SeqCst => repeat(), - _ => repeat(), + //~^ match_same_arms + Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), } } } -// Below should still lint +// Below can still suggest removing the other patterns pub fn g(x: Ordering) { match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } @@ -54,8 +62,8 @@ mod g { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } } diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs index 304a9e5c28e76..66f65eb39d0a9 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs @@ -7,15 +7,25 @@ fn repeat() -> ! { panic!() } +#[deny(non_exhaustive_omitted_patterns)] pub fn f(x: Ordering) { - #[deny(non_exhaustive_omitted_patterns)] match x { Ordering::Relaxed => println!("relaxed"), Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), + //~^ match_same_arms _ => repeat(), } + + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + Ordering::AcqRel => repeat(), + //~^ match_same_arms + Ordering::SeqCst | _ => repeat(), + } } mod f { @@ -29,12 +39,13 @@ mod f { Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), + //~^ match_same_arms _ => repeat(), } } } -// Below should still lint +// Below can still suggest removing the other patterns pub fn g(x: Ordering) { match x { @@ -42,8 +53,8 @@ pub fn g(x: Ordering) { Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } @@ -56,8 +67,8 @@ mod g { Ordering::Release => println!("release"), Ordering::Acquire => println!("acquire"), Ordering::AcqRel | Ordering::SeqCst => repeat(), - //~^ match_same_arms _ => repeat(), + //~^ match_same_arms } } } diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr index aa7f8c95dce84..03252f346c6cc 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr @@ -1,32 +1,80 @@ -error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:44:9 - | -LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), -LL | | - | |________^ help: try removing the arm - | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:46:9 +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:16:9 | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | _ => repeat(), | ^^^^^^^^^^^^^ + | + = help: if this is unintentional make the arms return different values = note: `-D clippy::match-same-arms` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` +help: otherwise merge the patterns into a single arm + | +LL ~ +LL ~ Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + | -error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:58:13 +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:25:9 + | +LL | Ordering::AcqRel => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | Ordering::SeqCst | _ => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), -LL | | - | |____________^ help: try removing the arm + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm | - = help: or try changing either arm body -note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:60:13 +LL ~ +LL ~ Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:41:13 + | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | LL | _ => repeat(), | ^^^^^^^^^^^^^ + | + = help: if this is unintentional make the arms return different values +help: otherwise merge the patterns into a single arm + | +LL ~ +LL ~ Ordering::AcqRel | Ordering::SeqCst | _ => repeat(), + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:55:9 + | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | _ => repeat(), + | ^^^^^^^^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values +help: otherwise remove the non-wildcard arm + | +LL - Ordering::AcqRel | Ordering::SeqCst => repeat(), + | + +error: these match arms have identical bodies + --> tests/ui/match_same_arms_non_exhaustive.rs:69:13 + | +LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | _ => repeat(), + | ^^^^^^^^^^^^^ the wildcard arm + | + = help: if this is unintentional make the arms return different values +help: otherwise remove the non-wildcard arm + | +LL - Ordering::AcqRel | Ordering::SeqCst => repeat(), + | -error: aborting due to 2 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed index 3a3eee4c958b4..bdf39796ebfcb 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.fixed +++ b/src/tools/clippy/tests/ui/match_single_binding.fixed @@ -171,3 +171,20 @@ fn issue_10447() -> usize { 2 } + +fn issue14634() { + macro_rules! id { + ($i:ident) => { + $i + }; + } + dbg!(3); + println!("here"); + //~^^^ match_single_binding + let id!(a) = dbg!(3); + println!("found {a}"); + //~^^^ match_single_binding + let id!(b) = dbg!(3); + let id!(_a) = dbg!(b + 1); + //~^^^ match_single_binding +} diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs index ada51254c6cdf..419ff95d873b0 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.rs +++ b/src/tools/clippy/tests/ui/match_single_binding.rs @@ -229,3 +229,23 @@ fn issue_10447() -> usize { 2 } + +fn issue14634() { + macro_rules! id { + ($i:ident) => { + $i + }; + } + match dbg!(3) { + _ => println!("here"), + } + //~^^^ match_single_binding + match dbg!(3) { + id!(a) => println!("found {a}"), + } + //~^^^ match_single_binding + let id!(_a) = match dbg!(3) { + id!(b) => dbg!(b + 1), + }; + //~^^^ match_single_binding +} diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr index 7e1ec32dac2ff..bdd0134a5f1c9 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.stderr +++ b/src/tools/clippy/tests/ui/match_single_binding.stderr @@ -336,5 +336,47 @@ LL | | _ => println!("1"), LL | | }, | |_________^ help: consider using the match body instead: `println!("1")` -error: aborting due to 24 previous errors +error: this match could be replaced by its scrutinee and body + --> tests/ui/match_single_binding.rs:239:5 + | +LL | / match dbg!(3) { +LL | | _ => println!("here"), +LL | | } + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ dbg!(3); +LL + println!("here"); + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:243:5 + | +LL | / match dbg!(3) { +LL | | id!(a) => println!("found {a}"), +LL | | } + | |_____^ + | +help: consider using a `let` statement + | +LL ~ let id!(a) = dbg!(3); +LL + println!("found {a}"); + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:247:5 + | +LL | / let id!(_a) = match dbg!(3) { +LL | | id!(b) => dbg!(b + 1), +LL | | }; + | |______^ + | +help: consider using a `let` statement + | +LL ~ let id!(b) = dbg!(3); +LL + let id!(_a) = dbg!(b + 1); + | + +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs index 76b0d131dd41d..2f4004181f6a8 100644 --- a/src/tools/clippy/tests/ui/methods.rs +++ b/src/tools/clippy/tests/ui/methods.rs @@ -1,6 +1,5 @@ //@aux-build:option_helpers.rs -#![warn(clippy::all, clippy::pedantic)] #![allow( clippy::disallowed_names, clippy::default_trait_access, @@ -19,8 +18,7 @@ clippy::wrong_self_convention, clippy::unused_async, clippy::unused_self, - clippy::useless_vec, - unused + clippy::useless_vec )] #[macro_use] diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr index 353b999d7da0f..b226ce7c65dab 100644 --- a/src/tools/clippy/tests/ui/methods.stderr +++ b/src/tools/clippy/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> tests/ui/methods.rs:104:5 + --> tests/ui/methods.rs:102:5 | LL | / fn new() -> i32 { LL | | @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::new_ret_no_self)]` error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods.rs:126:13 + --> tests/ui/methods.rs:124:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ diff --git a/src/tools/clippy/tests/ui/min_max.rs b/src/tools/clippy/tests/ui/min_max.rs index f3eeb85f20ed6..ee19d3ff71421 100644 --- a/src/tools/clippy/tests/ui/min_max.rs +++ b/src/tools/clippy/tests/ui/min_max.rs @@ -1,4 +1,3 @@ -#![warn(clippy::all)] #![allow(clippy::manual_clamp)] use std::cmp::{max as my_max, max, min as my_min, min}; diff --git a/src/tools/clippy/tests/ui/min_max.stderr b/src/tools/clippy/tests/ui/min_max.stderr index 84b4d37545529..87510a465a08b 100644 --- a/src/tools/clippy/tests/ui/min_max.stderr +++ b/src/tools/clippy/tests/ui/min_max.stderr @@ -1,80 +1,79 @@ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:22:5 + --> tests/ui/min_max.rs:21:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ | - = note: `-D clippy::min-max` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::min_max)]` + = note: `#[deny(clippy::min_max)]` on by default error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:25:5 + --> tests/ui/min_max.rs:24:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:28:5 + --> tests/ui/min_max.rs:27:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:31:5 + --> tests/ui/min_max.rs:30:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:34:5 + --> tests/ui/min_max.rs:33:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:45:5 + --> tests/ui/min_max.rs:44:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:48:5 + --> tests/ui/min_max.rs:47:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:54:5 + --> tests/ui/min_max.rs:53:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:57:5 + --> tests/ui/min_max.rs:56:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:60:5 + --> tests/ui/min_max.rs:59:5 | LL | f.max(3f32).min(1f32); | ^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:67:5 + --> tests/ui/min_max.rs:66:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:72:5 + --> tests/ui/min_max.rs:71:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:75:5 + --> tests/ui/min_max.rs:74:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/misnamed_getters.fixed b/src/tools/clippy/tests/ui/misnamed_getters.fixed index cada5307b1c8e..bc123d1a40ba2 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.fixed +++ b/src/tools/clippy/tests/ui/misnamed_getters.fixed @@ -54,63 +54,63 @@ impl B { unsafe fn a(&self) -> &u8 { //~^ misnamed_getters - &self.a + unsafe { &self.a } } unsafe fn a_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.a + unsafe { &mut self.a } } unsafe fn b(self) -> u8 { //~^ misnamed_getters - self.b + unsafe { self.b } } unsafe fn b_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.b + unsafe { &mut self.b } } unsafe fn c(&self) -> &u8 { - &self.b + unsafe { &self.b } } unsafe fn c_mut(&mut self) -> &mut u8 { - &mut self.a + unsafe { &mut self.a } } unsafe fn a_unchecked(&self) -> &u8 { //~^ misnamed_getters - &self.a + unsafe { &self.a } } unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.a + unsafe { &mut self.a } } unsafe fn b_unchecked(self) -> u8 { //~^ misnamed_getters - self.b + unsafe { self.b } } unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.b + unsafe { &mut self.b } } unsafe fn c_unchecked(&self) -> &u8 { - &self.b + unsafe { &self.b } } unsafe fn c_unchecked_mut(&mut self) -> &mut u8 { - &mut self.a + unsafe { &mut self.a } } } diff --git a/src/tools/clippy/tests/ui/misnamed_getters.rs b/src/tools/clippy/tests/ui/misnamed_getters.rs index f529c56b4717b..6590101157c3f 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.rs +++ b/src/tools/clippy/tests/ui/misnamed_getters.rs @@ -54,63 +54,63 @@ impl B { unsafe fn a(&self) -> &u8 { //~^ misnamed_getters - &self.b + unsafe { &self.b } } unsafe fn a_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.b + unsafe { &mut self.b } } unsafe fn b(self) -> u8 { //~^ misnamed_getters - self.a + unsafe { self.a } } unsafe fn b_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.a + unsafe { &mut self.a } } unsafe fn c(&self) -> &u8 { - &self.b + unsafe { &self.b } } unsafe fn c_mut(&mut self) -> &mut u8 { - &mut self.a + unsafe { &mut self.a } } unsafe fn a_unchecked(&self) -> &u8 { //~^ misnamed_getters - &self.b + unsafe { &self.b } } unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.b + unsafe { &mut self.b } } unsafe fn b_unchecked(self) -> u8 { //~^ misnamed_getters - self.a + unsafe { self.a } } unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { //~^ misnamed_getters - &mut self.a + unsafe { &mut self.a } } unsafe fn c_unchecked(&self) -> &u8 { - &self.b + unsafe { &self.b } } unsafe fn c_unchecked_mut(&mut self) -> &mut u8 { - &mut self.a + unsafe { &mut self.a } } } diff --git a/src/tools/clippy/tests/ui/misnamed_getters.stderr b/src/tools/clippy/tests/ui/misnamed_getters.stderr index 5dd1d75bcf6f1..aaf21cecb9255 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.stderr +++ b/src/tools/clippy/tests/ui/misnamed_getters.stderr @@ -73,8 +73,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn a(&self) -> &u8 { LL | | LL | | -LL | | &self.b - | | ------- help: consider using: `&self.a` +LL | | unsafe { &self.b } + | | ------- help: consider using: `&self.a` LL | | } | |_____^ @@ -84,8 +84,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn a_mut(&mut self) -> &mut u8 { LL | | LL | | -LL | | &mut self.b - | | ----------- help: consider using: `&mut self.a` +LL | | unsafe { &mut self.b } + | | ----------- help: consider using: `&mut self.a` LL | | } | |_____^ @@ -95,8 +95,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn b(self) -> u8 { LL | | LL | | -LL | | self.a - | | ------ help: consider using: `self.b` +LL | | unsafe { self.a } + | | ------ help: consider using: `self.b` LL | | } | |_____^ @@ -106,8 +106,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn b_mut(&mut self) -> &mut u8 { LL | | LL | | -LL | | &mut self.a - | | ----------- help: consider using: `&mut self.b` +LL | | unsafe { &mut self.a } + | | ----------- help: consider using: `&mut self.b` LL | | } | |_____^ @@ -117,8 +117,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn a_unchecked(&self) -> &u8 { LL | | LL | | -LL | | &self.b - | | ------- help: consider using: `&self.a` +LL | | unsafe { &self.b } + | | ------- help: consider using: `&self.a` LL | | } | |_____^ @@ -128,8 +128,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { LL | | LL | | -LL | | &mut self.b - | | ----------- help: consider using: `&mut self.a` +LL | | unsafe { &mut self.b } + | | ----------- help: consider using: `&mut self.a` LL | | } | |_____^ @@ -139,8 +139,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn b_unchecked(self) -> u8 { LL | | LL | | -LL | | self.a - | | ------ help: consider using: `self.b` +LL | | unsafe { self.a } + | | ------ help: consider using: `self.b` LL | | } | |_____^ @@ -150,8 +150,8 @@ error: getter function appears to return the wrong field LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { LL | | LL | | -LL | | &mut self.a - | | ----------- help: consider using: `&mut self.b` +LL | | unsafe { &mut self.a } + | | ----------- help: consider using: `&mut self.b` LL | | } | |_____^ diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed b/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed new file mode 100644 index 0000000000000..7112719a9f284 --- /dev/null +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed @@ -0,0 +1,24 @@ +//@edition: 2021 +#![allow(unused)] +#![allow(clippy::struct_field_names)] +#![warn(clippy::misnamed_getters)] + +// Edition 2021 specific check, where `unsafe` blocks are not required +// inside `unsafe fn`. + +union B { + a: u8, + b: u8, +} + +impl B { + unsafe fn a(&self) -> &u8 { + //~^ misnamed_getters + + &self.a + } +} + +fn main() { + // test code goes here +} diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.rs b/src/tools/clippy/tests/ui/misnamed_getters_2021.rs new file mode 100644 index 0000000000000..19b5d086041f4 --- /dev/null +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.rs @@ -0,0 +1,24 @@ +//@edition: 2021 +#![allow(unused)] +#![allow(clippy::struct_field_names)] +#![warn(clippy::misnamed_getters)] + +// Edition 2021 specific check, where `unsafe` blocks are not required +// inside `unsafe fn`. + +union B { + a: u8, + b: u8, +} + +impl B { + unsafe fn a(&self) -> &u8 { + //~^ misnamed_getters + + &self.b + } +} + +fn main() { + // test code goes here +} diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr b/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr new file mode 100644 index 0000000000000..5495e2e3733f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr @@ -0,0 +1,16 @@ +error: getter function appears to return the wrong field + --> tests/ui/misnamed_getters_2021.rs:15:5 + | +LL | / unsafe fn a(&self) -> &u8 { +LL | | +LL | | +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ + | + = note: `-D clippy::misnamed-getters` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::misnamed_getters)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed b/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed new file mode 100644 index 0000000000000..882ff6bf8944a --- /dev/null +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed @@ -0,0 +1,40 @@ +#![allow(clippy::eq_op)] +#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] + +fn main() { + let mut a = 5; + a += 1; + //~^ misrefactored_assign_op + + a += 1; + //~^ misrefactored_assign_op + + a -= 1; + //~^ misrefactored_assign_op + + a *= 99; + //~^ misrefactored_assign_op + + a *= 42; + //~^ misrefactored_assign_op + + a /= 2; + //~^ misrefactored_assign_op + + a %= 5; + //~^ misrefactored_assign_op + + a &= 1; + //~^ misrefactored_assign_op + + a *= a; + //~^ misrefactored_assign_op + + a = a * a * a; + a = a * 42 * a; + a = a * 2 + a; + a -= 1 - a; + a /= 5 / a; + a %= 42 % a; + a <<= 6 << a; +} diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed b/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed new file mode 100644 index 0000000000000..de3a0f1710d24 --- /dev/null +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed @@ -0,0 +1,40 @@ +#![allow(clippy::eq_op)] +#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] + +fn main() { + let mut a = 5; + a = a + a + 1; + //~^ misrefactored_assign_op + + a = a + 1 + a; + //~^ misrefactored_assign_op + + a = a - (a - 1); + //~^ misrefactored_assign_op + + a = a * a * 99; + //~^ misrefactored_assign_op + + a = a * 42 * a; + //~^ misrefactored_assign_op + + a = a / (a / 2); + //~^ misrefactored_assign_op + + a = a % (a % 5); + //~^ misrefactored_assign_op + + a = a & a & 1; + //~^ misrefactored_assign_op + + a = a * a * a; + //~^ misrefactored_assign_op + + a = a * a * a; + a = a * 42 * a; + a = a * 2 + a; + a -= 1 - a; + a /= 5 / a; + a %= 42 % a; + a <<= 6 << a; +} diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.rs b/src/tools/clippy/tests/ui/misrefactored_assign_op.rs new file mode 100644 index 0000000000000..62d83d1619c1e --- /dev/null +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.rs @@ -0,0 +1,40 @@ +#![allow(clippy::eq_op)] +#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] + +fn main() { + let mut a = 5; + a += a + 1; + //~^ misrefactored_assign_op + + a += 1 + a; + //~^ misrefactored_assign_op + + a -= a - 1; + //~^ misrefactored_assign_op + + a *= a * 99; + //~^ misrefactored_assign_op + + a *= 42 * a; + //~^ misrefactored_assign_op + + a /= a / 2; + //~^ misrefactored_assign_op + + a %= a % 5; + //~^ misrefactored_assign_op + + a &= a & 1; + //~^ misrefactored_assign_op + + a *= a * a; + //~^ misrefactored_assign_op + + a = a * a * a; + a = a * 42 * a; + a = a * 2 + a; + a -= 1 - a; + a /= 5 / a; + a %= 42 % a; + a <<= 6 << a; +} diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr b/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr new file mode 100644 index 0000000000000..63f3a3e28f12c --- /dev/null +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr @@ -0,0 +1,157 @@ +error: variable appears on both sides of an assignment operation + --> tests/ui/misrefactored_assign_op.rs:6:5 + | +LL | a += a + 1; + | ^^^^^^^^^^ + | + = note: `-D clippy::misrefactored-assign-op` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::misrefactored_assign_op)]` +help: did you mean `a = a + 1` or `a = a + a + 1`? Consider replacing it with + | +LL - a += a + 1; +LL + a += 1; + | +help: or + | +LL - a += a + 1; +LL + a = a + a + 1; + | + +error: variable appears on both sides of an assignment operation + --> tests/ui/misrefactored_assign_op.rs:9:5 + | +LL | a += 1 + a; + | ^^^^^^^^^^ + | +help: did you mean `a = a + 1` or `a = a + 1 + a`? Consider replacing it with + | +LL - a += 1 + a; +LL + a += 1; + | +help: or + | +LL - a += 1 + a; +LL + a = a + 1 + a; + | + +error: variable appears on both sides of an assignment operation + --> tests/ui/misrefactored_assign_op.rs:12:5 + | +LL | a -= a - 1; + | ^^^^^^^^^^ + | +help: did you mean `a = a - 1` or `a = a - (a - 1)`? Consider replacing it with + | +LL - a -= a - 1; +LL + a -= 1; + | +help: or + | +LL - a -= a - 1; +LL + a = a - (a - 1); + | + +error: variable appears on both sides of an assignment operation + --> tests/ui/misrefactored_assign_op.rs:15:5 + | +LL | a *= a * 99; + | ^^^^^^^^^^^ + | +help: did you mean `a = a * 99` or `a = a * a * 99`? Consider replacing it with + | +LL - a *= a * 99; +LL + a *= 99; + | +help: or + | +LL - a *= a * 99; +LL + a = a * a * 99; + | + +error: variable appears on both sides of an assignment operation + --> tests/ui/misrefactored_assign_op.rs:18:5 + | +LL | a *= 42 * a; + | ^^^^^^^^^^^ + | +help: did you mean `a = a * 42` or `a = a * 42 * a`? Consider replacing it with + | +LL - a *= 42 * a; +LL + a *= 42; + | +help: or + | +LL - a *= 42 * a; +LL + a = a * 42 * a; + | + +error: variable appears on both sides of an assignment operation + --> tests/ui/misrefactored_assign_op.rs:21:5 + | +LL | a /= a / 2; + | ^^^^^^^^^^ + | +help: did you mean `a = a / 2` or `a = a / (a / 2)`? Consider replacing it with + | +LL - a /= a / 2; +LL + a /= 2; + | +help: or + | +LL - a /= a / 2; +LL + a = a / (a / 2); + | + +error: variable appears on both sides of an assignment operation + --> tests/ui/misrefactored_assign_op.rs:24:5 + | +LL | a %= a % 5; + | ^^^^^^^^^^ + | +help: did you mean `a = a % 5` or `a = a % (a % 5)`? Consider replacing it with + | +LL - a %= a % 5; +LL + a %= 5; + | +help: or + | +LL - a %= a % 5; +LL + a = a % (a % 5); + | + +error: variable appears on both sides of an assignment operation + --> tests/ui/misrefactored_assign_op.rs:27:5 + | +LL | a &= a & 1; + | ^^^^^^^^^^ + | +help: did you mean `a = a & 1` or `a = a & a & 1`? Consider replacing it with + | +LL - a &= a & 1; +LL + a &= 1; + | +help: or + | +LL - a &= a & 1; +LL + a = a & a & 1; + | + +error: variable appears on both sides of an assignment operation + --> tests/ui/misrefactored_assign_op.rs:30:5 + | +LL | a *= a * a; + | ^^^^^^^^^^ + | +help: did you mean `a = a * a` or `a = a * a * a`? Consider replacing it with + | +LL - a *= a * a; +LL + a *= a; + | +help: or + | +LL - a *= a * a; +LL + a = a * a * a; + | + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed index 3bbafe0bba3fe..9018f38100efd 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed @@ -139,4 +139,31 @@ fn issue11835(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { let _ = v4[0] + v4[1] + v4[2]; } +// ok +fn same_index_multiple_times(v1: &[u8]) { + let _ = v1[0] + v1[0]; +} + +// ok +fn highest_index_first(v1: &[u8]) { + let _ = v1[2] + v1[1] + v1[0]; +} + +fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { + assert!(v1.len() == 3); + assert_eq!(v2.len(), 4); + assert!(v3.len() == 3); + assert_eq!(4, v4.len()); + + let _ = v1[0] + v1[1] + v1[2]; + //~^ missing_asserts_for_indexing + + let _ = v2[0] + v2[1] + v2[2]; + + let _ = v3[0] + v3[1] + v3[2]; + //~^ missing_asserts_for_indexing + + let _ = v4[0] + v4[1] + v4[2]; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs index f8ea0173c13fc..44c5eddf3d8b9 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs @@ -139,4 +139,31 @@ fn issue11835(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { let _ = v4[0] + v4[1] + v4[2]; } +// ok +fn same_index_multiple_times(v1: &[u8]) { + let _ = v1[0] + v1[0]; +} + +// ok +fn highest_index_first(v1: &[u8]) { + let _ = v1[2] + v1[1] + v1[0]; +} + +fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { + assert_eq!(v1.len(), 2); + assert_eq!(v2.len(), 4); + assert_eq!(2, v3.len()); + assert_eq!(4, v4.len()); + + let _ = v1[0] + v1[1] + v1[2]; + //~^ missing_asserts_for_indexing + + let _ = v2[0] + v2[1] + v2[2]; + + let _ = v3[0] + v3[1] + v3[2]; + //~^ missing_asserts_for_indexing + + let _ = v4[0] + v4[1] + v4[2]; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr index 5d30920ccf521..b610de94b5308 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr @@ -301,5 +301,57 @@ LL | let _ = v3[0] + v3[1] + v3[2]; | ^^^^^ = note: asserting the length before indexing will elide bounds checks -error: aborting due to 11 previous errors +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> tests/ui/missing_asserts_for_indexing.rs:158:13 + | +LL | assert_eq!(v1.len(), 2); + | ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() == 3)` +... +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:158:13 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:158:21 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:158:29 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> tests/ui/missing_asserts_for_indexing.rs:163:13 + | +LL | assert_eq!(2, v3.len()); + | ----------------------- help: provide the highest index that is indexed with: `assert!(v3.len() == 3)` +... +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:163:13 + | +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:163:21 + | +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:163:29 + | +LL | let _ = v3[0] + v3[1] + v3[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs index a520151a2dd94..eb98969efa47f 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs @@ -73,4 +73,17 @@ pub fn issue11856(values: &[i32]) -> usize { ascending.len() } +fn assert_after_indexing(v1: &[u8]) { + let _ = v1[1] + v1[2]; + //~^ ERROR: indexing into a slice multiple times without an `assert` + assert!(v1.len() > 2); +} + +fn issue14255(v1: &[u8]) { + assert_ne!(v1.len(), 2); + + let _ = v1[0] + v1[1] + v1[2]; + //~^ missing_asserts_for_indexing +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr index 24109b052a8af..a17ad02321386 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr @@ -180,5 +180,48 @@ LL | let _ = x[0] + x[1]; | ^^^^ = note: asserting the length before indexing will elide bounds checks -error: aborting due to 8 previous errors +error: indexing into a slice multiple times without an `assert` + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13 + | +LL | let _ = v1[1] + v1[2]; + | ^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v1.len() > 2);` +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13 + | +LL | let _ = v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:21 + | +LL | let _ = v1[1] + v1[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times without an `assert` + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v1.len() > 2);` +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:21 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:29 + | +LL | let _ = v1[0] + v1[1] + v1[2]; + | ^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs index aef5eb5b890a8..40e7a5d745cce 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -207,6 +207,7 @@ mod msrv { mod with_ty_alias { type Foo = impl std::fmt::Debug; + #[define_opaque(Foo)] fn foo(_: Foo) { let _: Foo = 1; } @@ -217,3 +218,91 @@ mod with_ty_alias { fn mut_add(x: &mut i32) { *x += 1; } + +#[clippy::msrv = "1.87"] +mod issue14020 { + use std::ops::Add; + + fn f(a: T, b: T) -> ::Output { + a + b + } +} + +#[clippy::msrv = "1.87"] +mod issue14290 { + use std::ops::{Deref, DerefMut}; + + struct Wrapper { + t: T, + } + + impl Deref for Wrapper { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.t + } + } + impl DerefMut for Wrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.t + } + } + + struct Example(bool); + + fn do_something(mut a: Wrapper) { + a.0 = !a.0; + } + + pub struct Stream(Vec); + + impl Stream { + pub fn bytes(&self) -> &[u8] { + &self.0 + } + } +} + +#[clippy::msrv = "1.87"] +mod issue14091 { + use std::mem::ManuallyDrop; + + struct BucketSlotGuard<'a> { + id: u32, + free_list: &'a mut Vec, + } + + impl BucketSlotGuard<'_> { + fn into_inner(self) -> u32 { + let this = ManuallyDrop::new(self); + this.id + } + } + + use std::ops::{Deref, DerefMut}; + + struct Wrap(T); + + impl Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + + impl DerefMut for Wrap { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } + } + + fn smart_two_field(v: &mut Wrap<(i32, i32)>) { + let _a = &mut v.0; + let _b = &mut v.1; + } + + fn smart_destructure(v: &mut Wrap<(i32, i32)>) { + let (ref mut _head, ref mut _tail) = **v; + } +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed index 10df44e73b85f..65eb2d5938b6b 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -144,7 +144,7 @@ mod msrv { #[clippy::msrv = "1.62"] mod with_extern { - const extern "C" fn c() {} + const unsafe extern "C" fn c() {} //~^ missing_const_for_fn #[rustfmt::skip] @@ -153,7 +153,7 @@ mod msrv { //~^ missing_const_for_fn // any item functions in extern block won't trigger this lint - extern "C" { + unsafe extern "C" { fn c_in_block(); } } diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index bc44b34daef7e..3690d2f799ff4 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -144,7 +144,7 @@ mod msrv { #[clippy::msrv = "1.62"] mod with_extern { - extern "C" fn c() {} + unsafe extern "C" fn c() {} //~^ missing_const_for_fn #[rustfmt::skip] @@ -153,7 +153,7 @@ mod msrv { //~^ missing_const_for_fn // any item functions in extern block won't trigger this lint - extern "C" { + unsafe extern "C" { fn c_in_block(); } } diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr index 5df5a54ff5216..10e07d12f5a4c 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -212,12 +212,12 @@ LL | const fn union_access_can_be_const() { error: this could be a `const fn` --> tests/ui/missing_const_for_fn/could_be_const.rs:147:9 | -LL | extern "C" fn c() {} - | ^^^^^^^^^^^^^^^^^^^^ +LL | unsafe extern "C" fn c() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `const` | -LL | const extern "C" fn c() {} +LL | const unsafe extern "C" fn c() {} | +++++ error: this could be a `const fn` diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs index 95e361c5d5556..ffdae8504f72e 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.rs +++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs @@ -151,6 +151,45 @@ pub fn debug_assertions() { debug_assert_ne!(1, 2); } +pub fn partially_const(n: usize) { + //~^ missing_panics_doc + + const { + assert!(N > 5); + } + + assert!(N > n); +} + +pub fn expect_allow(i: Option) { + #[expect(clippy::missing_panics_doc)] + i.unwrap(); + + #[allow(clippy::missing_panics_doc)] + i.unwrap(); +} + +pub fn expect_allow_with_error(i: Option) { + //~^ missing_panics_doc + + #[expect(clippy::missing_panics_doc)] + i.unwrap(); + + #[allow(clippy::missing_panics_doc)] + i.unwrap(); + + i.unwrap(); +} + +pub fn expect_after_error(x: Option, y: Option) { + //~^ missing_panics_doc + + let x = x.unwrap(); + + #[expect(clippy::missing_panics_doc)] + let y = y.unwrap(); +} + // all function must be triggered the lint. // `pub` is required, because the lint does not consider unreachable items pub mod issue10240 { diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.stderr b/src/tools/clippy/tests/ui/missing_panics_doc.stderr index a83e2fa367dd3..7f0acf8de9b77 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.stderr +++ b/src/tools/clippy/tests/ui/missing_panics_doc.stderr @@ -73,76 +73,112 @@ LL | assert_ne!(x, 0); | ^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:157:5 + --> tests/ui/missing_panics_doc.rs:154:1 + | +LL | pub fn partially_const(n: usize) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:161:5 + | +LL | assert!(N > n); + | ^^^^^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:172:1 + | +LL | pub fn expect_allow_with_error(i: Option) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:181:5 + | +LL | i.unwrap(); + | ^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:184:1 + | +LL | pub fn expect_after_error(x: Option, y: Option) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:187:13 + | +LL | let x = x.unwrap(); + | ^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:196:5 | LL | pub fn option_unwrap(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:160:9 + --> tests/ui/missing_panics_doc.rs:199:9 | LL | o.unwrap() | ^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:163:5 + --> tests/ui/missing_panics_doc.rs:202:5 | LL | pub fn option_expect(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:166:9 + --> tests/ui/missing_panics_doc.rs:205:9 | LL | o.expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:169:5 + --> tests/ui/missing_panics_doc.rs:208:5 | LL | pub fn result_unwrap(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:172:9 + --> tests/ui/missing_panics_doc.rs:211:9 | LL | res.unwrap() | ^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:175:5 + --> tests/ui/missing_panics_doc.rs:214:5 | LL | pub fn result_expect(v: &[T]) -> &T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:178:9 + --> tests/ui/missing_panics_doc.rs:217:9 | LL | res.expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:181:5 + --> tests/ui/missing_panics_doc.rs:220:5 | LL | pub fn last_unwrap(v: &[u32]) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:183:10 + --> tests/ui/missing_panics_doc.rs:222:10 | LL | *v.last().unwrap() | ^^^^^^^^^^^^^^^^^ error: docs for function which may panic missing `# Panics` section - --> tests/ui/missing_panics_doc.rs:186:5 + --> tests/ui/missing_panics_doc.rs:225:5 | LL | pub fn last_expect(v: &[u32]) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here - --> tests/ui/missing_panics_doc.rs:188:10 + --> tests/ui/missing_panics_doc.rs:227:10 | LL | *v.last().expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed b/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed index a3c94ab139ec6..58faeaee09d46 100644 --- a/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed @@ -18,8 +18,10 @@ fn bar(x: i32) -> i32 { } unsafe fn foo1() -> i32 { - // Should not warn! - std::mem::transmute([1u16, 2u16]) + unsafe { + // Should not warn! + std::mem::transmute([1u16, 2u16]) + } } // Should not warn! @@ -31,33 +33,35 @@ enum Foo { } unsafe fn foo2() -> i32 { - let mut i: i32 = 0; - i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations + unsafe { + let mut i: i32 = 0; + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations - let x: i32 = bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); - //~^ ERROR: transmute used without annotations - bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); - //~^ ERROR: transmute used without annotations + let x: i32 = bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations + bar(std::mem::transmute::<[u16; 2], i32>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations - i = local_bad_transmute!([1u16, 2u16]); + i = local_bad_transmute!([1u16, 2u16]); - // Should not warn. - i = bad_transmute!([1u16, 2u16]); + // Should not warn. + i = bad_transmute!([1u16, 2u16]); - i = std::mem::transmute::<[i16; 2], i32>([0i16, 0i16]); - //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[i16; 2], i32>([0i16, 0i16]); + //~^ ERROR: transmute used without annotations - i = std::mem::transmute::(Foo::A); - //~^ ERROR: transmute used without annotations + i = std::mem::transmute::(Foo::A); + //~^ ERROR: transmute used without annotations - i + i + } } fn main() { diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.rs b/src/tools/clippy/tests/ui/missing_transmute_annotations.rs index c12e1b0f8d220..c9a4c5fa83b2b 100644 --- a/src/tools/clippy/tests/ui/missing_transmute_annotations.rs +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.rs @@ -18,8 +18,10 @@ fn bar(x: i32) -> i32 { } unsafe fn foo1() -> i32 { - // Should not warn! - std::mem::transmute([1u16, 2u16]) + unsafe { + // Should not warn! + std::mem::transmute([1u16, 2u16]) + } } // Should not warn! @@ -31,33 +33,35 @@ enum Foo { } unsafe fn foo2() -> i32 { - let mut i: i32 = 0; - i = std::mem::transmute([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<_, _>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<_, i32>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations - i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); - //~^ ERROR: transmute used without annotations + unsafe { + let mut i: i32 = 0; + i = std::mem::transmute([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<_, _>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<_, i32>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations + i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); + //~^ ERROR: transmute used without annotations - let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); - //~^ ERROR: transmute used without annotations - bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); - //~^ ERROR: transmute used without annotations + let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations + bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + //~^ ERROR: transmute used without annotations - i = local_bad_transmute!([1u16, 2u16]); + i = local_bad_transmute!([1u16, 2u16]); - // Should not warn. - i = bad_transmute!([1u16, 2u16]); + // Should not warn. + i = bad_transmute!([1u16, 2u16]); - i = std::mem::transmute([0i16, 0i16]); - //~^ ERROR: transmute used without annotations + i = std::mem::transmute([0i16, 0i16]); + //~^ ERROR: transmute used without annotations - i = std::mem::transmute(Foo::A); - //~^ ERROR: transmute used without annotations + i = std::mem::transmute(Foo::A); + //~^ ERROR: transmute used without annotations - i + i + } } fn main() { diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.stderr b/src/tools/clippy/tests/ui/missing_transmute_annotations.stderr index 5903ed488ef18..63f7e28ee7dc6 100644 --- a/src/tools/clippy/tests/ui/missing_transmute_annotations.stderr +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.stderr @@ -1,41 +1,41 @@ error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:35:19 + --> tests/ui/missing_transmute_annotations.rs:38:23 | -LL | i = std::mem::transmute([1u16, 2u16]); - | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | i = std::mem::transmute([1u16, 2u16]); + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` | = note: `-D clippy::missing-transmute-annotations` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_transmute_annotations)]` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:37:19 + --> tests/ui/missing_transmute_annotations.rs:40:23 | -LL | i = std::mem::transmute::<_, _>([1u16, 2u16]); - | ^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | i = std::mem::transmute::<_, _>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:39:19 + --> tests/ui/missing_transmute_annotations.rs:42:23 | -LL | i = std::mem::transmute::<_, i32>([1u16, 2u16]); - | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | i = std::mem::transmute::<_, i32>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:41:19 + --> tests/ui/missing_transmute_annotations.rs:44:23 | -LL | i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | i = std::mem::transmute::<[u16; 2], _>([1u16, 2u16]); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:44:32 + --> tests/ui/missing_transmute_annotations.rs:47:36 | -LL | let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | let x: i32 = bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:46:19 + --> tests/ui/missing_transmute_annotations.rs:49:23 | -LL | bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` +LL | bar(std::mem::transmute::<[u16; 2], _>([1u16, 2u16])); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations --> tests/ui/missing_transmute_annotations.rs:11:19 @@ -43,31 +43,31 @@ error: transmute used without annotations LL | std::mem::transmute($e) | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` ... -LL | i = local_bad_transmute!([1u16, 2u16]); - | ---------------------------------- in this macro invocation +LL | i = local_bad_transmute!([1u16, 2u16]); + | ---------------------------------- in this macro invocation | = note: this error originates in the macro `local_bad_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:54:19 + --> tests/ui/missing_transmute_annotations.rs:57:23 | -LL | i = std::mem::transmute([0i16, 0i16]); - | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[i16; 2], i32>` +LL | i = std::mem::transmute([0i16, 0i16]); + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::<[i16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:57:19 + --> tests/ui/missing_transmute_annotations.rs:60:23 | -LL | i = std::mem::transmute(Foo::A); - | ^^^^^^^^^ help: consider adding missing annotations: `transmute::` +LL | i = std::mem::transmute(Foo::A); + | ^^^^^^^^^ help: consider adding missing annotations: `transmute::` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:64:35 + --> tests/ui/missing_transmute_annotations.rs:68:35 | LL | let x: _ = unsafe { std::mem::transmute::<_, i32>([1u16, 2u16]) }; | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` error: transmute used without annotations - --> tests/ui/missing_transmute_annotations.rs:67:30 + --> tests/ui/missing_transmute_annotations.rs:71:30 | LL | let x: _ = std::mem::transmute::<_, i32>([1u16, 2u16]); | ^^^^^^^^^^^^^^^^^^^ help: consider adding missing annotations: `transmute::<[u16; 2], i32>` diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed index b5d356a502170..4c1d6b1ccb596 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.fixed +++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed @@ -88,11 +88,13 @@ static mut COUNTER: usize = 0; /// /// Don't ever call this from multiple threads pub unsafe fn mutates_static() -> usize { - COUNTER += 1; - COUNTER + unsafe { + COUNTER += 1; + COUNTER + } } -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn unmangled(i: bool) -> bool { !i } diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs index 14ea16662fdb4..71d546718ae79 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.rs +++ b/src/tools/clippy/tests/ui/must_use_candidates.rs @@ -88,11 +88,13 @@ static mut COUNTER: usize = 0; /// /// Don't ever call this from multiple threads pub unsafe fn mutates_static() -> usize { - COUNTER += 1; - COUNTER + unsafe { + COUNTER += 1; + COUNTER + } } -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn unmangled(i: bool) -> bool { !i } diff --git a/src/tools/clippy/tests/ui/mut_from_ref.rs b/src/tools/clippy/tests/ui/mut_from_ref.rs index b8c10f3eeb8f9..1b0b351518cbb 100644 --- a/src/tools/clippy/tests/ui/mut_from_ref.rs +++ b/src/tools/clippy/tests/ui/mut_from_ref.rs @@ -1,4 +1,10 @@ -#![allow(unused, clippy::needless_lifetimes, clippy::needless_pass_by_ref_mut)] +#![allow( + unused, + clippy::needless_lifetimes, + clippy::needless_pass_by_ref_mut, + clippy::redundant_allocation, + clippy::boxed_local +)] #![warn(clippy::mut_from_ref)] struct Foo; @@ -40,6 +46,18 @@ fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { unsafe { unimplemented!() } } +fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { + //~^ mut_from_ref + + unsafe { unimplemented!() } +} + +fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { + //~^ mut_from_ref + + unsafe { unimplemented!() } +} + // this is OK, because the result borrows y fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 { unsafe { unimplemented!() } @@ -50,6 +68,20 @@ fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 { unsafe { unimplemented!() } } +fn works_tuples<'a>(x: (&'a u32, &'a mut u32)) -> &'a mut u32 { + unsafe { unimplemented!() } +} + +fn works_box<'a>(x: &'a u32, y: Box<&'a mut u32>) -> &'a mut u32 { + unsafe { unimplemented!() } +} + +struct RefMut<'a>(&'a mut u32); + +fn works_parameter<'a>(x: &'a u32, y: RefMut<'a>) -> &'a mut u32 { + unsafe { unimplemented!() } +} + unsafe fn also_broken(x: &u32) -> &mut u32 { //~^ mut_from_ref diff --git a/src/tools/clippy/tests/ui/mut_from_ref.stderr b/src/tools/clippy/tests/ui/mut_from_ref.stderr index 8c3c8e0c3d851..0974268734653 100644 --- a/src/tools/clippy/tests/ui/mut_from_ref.stderr +++ b/src/tools/clippy/tests/ui/mut_from_ref.stderr @@ -1,11 +1,11 @@ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:7:39 + --> tests/ui/mut_from_ref.rs:13:39 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:7:29 + --> tests/ui/mut_from_ref.rs:13:29 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^ @@ -13,64 +13,88 @@ LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { = help: to override `-D warnings` add `#[allow(clippy::mut_from_ref)]` error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:15:25 + --> tests/ui/mut_from_ref.rs:21:25 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:15:16 + --> tests/ui/mut_from_ref.rs:21:16 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:25:21 + --> tests/ui/mut_from_ref.rs:31:21 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:25:12 + --> tests/ui/mut_from_ref.rs:31:12 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:31:50 + --> tests/ui/mut_from_ref.rs:37:50 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:31:25 + --> tests/ui/mut_from_ref.rs:37:25 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:37:67 + --> tests/ui/mut_from_ref.rs:43:67 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:37:27 + --> tests/ui/mut_from_ref.rs:43:27 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:53:35 + --> tests/ui/mut_from_ref.rs:49:46 + | +LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { + | ^^^^^^^^^^^ + | +note: immutable borrow here + --> tests/ui/mut_from_ref.rs:49:24 + | +LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { + | ^^^^^^^ ^^^^^^^ + +error: mutable borrow from immutable input(s) + --> tests/ui/mut_from_ref.rs:55:37 + | +LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { + | ^^^^^^^^^^^ + | +note: immutable borrow here + --> tests/ui/mut_from_ref.rs:55:24 + | +LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { + | ^^^^^^^ + +error: mutable borrow from immutable input(s) + --> tests/ui/mut_from_ref.rs:85:35 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:53:26 + --> tests/ui/mut_from_ref.rs:85:26 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^ -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs index 80a712a9286a4..7db5c9f274f6e 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.rs +++ b/src/tools/clippy/tests/ui/mutex_atomic.rs @@ -1,4 +1,3 @@ -#![warn(clippy::all)] #![warn(clippy::mutex_integer)] #![warn(clippy::mutex_atomic)] #![allow(clippy::borrow_as_ptr)] diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr index 838fc1d7c36ee..a6d5d60fbf05b 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.stderr +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -1,5 +1,5 @@ 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<()>` - --> tests/ui/mutex_atomic.rs:8:5 + --> tests/ui/mutex_atomic.rs:7:5 | LL | Mutex::new(true); | ^^^^^^^^^^^^^^^^ @@ -8,31 +8,31 @@ LL | Mutex::new(true); = help: to override `-D warnings` add `#[allow(clippy::mutex_atomic)]` 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<()>` - --> tests/ui/mutex_atomic.rs:11:5 + --> tests/ui/mutex_atomic.rs:10: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<()>` - --> tests/ui/mutex_atomic.rs:14:5 + --> tests/ui/mutex_atomic.rs:13: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<()>` - --> tests/ui/mutex_atomic.rs:18:5 + --> tests/ui/mutex_atomic.rs:17: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<()>` - --> tests/ui/mutex_atomic.rs:21:5 + --> tests/ui/mutex_atomic.rs:20:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicU32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:24:5 + --> tests/ui/mutex_atomic.rs:23:5 | LL | Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ @@ -41,31 +41,31 @@ LL | Mutex::new(0u32); = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` error: consider using an `AtomicI32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:27:5 + --> tests/ui/mutex_atomic.rs:26:5 | LL | Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ error: consider using an `AtomicU8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:31:5 + --> tests/ui/mutex_atomic.rs:30:5 | LL | Mutex::new(0u8); | ^^^^^^^^^^^^^^^ error: consider using an `AtomicI16` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:34:5 + --> tests/ui/mutex_atomic.rs:33:5 | LL | Mutex::new(0i16); | ^^^^^^^^^^^^^^^^ error: consider using an `AtomicI8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:37:25 + --> tests/ui/mutex_atomic.rs:36:25 | LL | let _x: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ error: consider using an `AtomicI64` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> tests/ui/mutex_atomic.rs:41:5 + --> tests/ui/mutex_atomic.rs:40:5 | LL | Mutex::new(X); | ^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed index e4504bc2784cc..84924cac62d52 100644 --- a/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed +++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed @@ -89,7 +89,7 @@ fn should_not_lint( tuple_struct: TupleStruct, s: Struct, ) { - if let [ref a] = slice {} + if let [a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.rs b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs index 7edfda60b9790..280cef43340cc 100644 --- a/src/tools/clippy/tests/ui/needless_borrowed_ref.rs +++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs @@ -89,7 +89,7 @@ fn should_not_lint( tuple_struct: TupleStruct, s: Struct, ) { - if let [ref a] = slice {} + if let [a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} diff --git a/src/tools/clippy/tests/ui/needless_collect.fixed b/src/tools/clippy/tests/ui/needless_collect.fixed index bad8c307586cf..b09efe9888f50 100644 --- a/src/tools/clippy/tests/ui/needless_collect.fixed +++ b/src/tools/clippy/tests/ui/needless_collect.fixed @@ -98,8 +98,115 @@ fn main() { let mut out = vec![]; values.iter().cloned().map(|x| out.push(x)).collect::>(); let _y = values.iter().cloned().map(|x| out.push(x)).collect::>(); // this is fine + + // Don't write a warning if we call `clone()` on the iterator + // https://github.com/rust-lang/rust-clippy/issues/13430 + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let _cloned = my_collection.into_iter().clone(); + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let my_iter = my_collection.into_iter(); + let _cloned = my_iter.clone(); + // Same for `as_slice()`, for same reason. + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let _sliced = my_collection.into_iter().as_slice(); + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let my_iter = my_collection.into_iter(); + let _sliced = my_iter.as_slice(); + // Assignment outside of main scope + { + let x; + { + let xxx: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + x = xxx.into_iter(); + for i in x.as_slice() {} + } + } } fn foo(_: impl IntoIterator) {} fn bar>(_: Vec, _: I) {} fn baz>(_: I, _: (), _: impl IntoIterator) {} + +mod issue9191 { + use std::cell::Cell; + use std::collections::HashSet; + use std::hash::Hash; + use std::marker::PhantomData; + use std::ops::Deref; + + fn captures_ref_mut(xs: Vec, mut ys: HashSet) { + if xs.iter().map(|x| ys.remove(x)).collect::>().contains(&true) { + todo!() + } + } + + #[derive(Debug, Clone)] + struct MyRef<'a>(PhantomData<&'a mut Cell>>, *mut Cell>); + + impl MyRef<'_> { + fn new(target: &mut Cell>) -> Self { + MyRef(PhantomData, target) + } + + fn get(&mut self) -> &mut Cell> { + unsafe { &mut *self.1 } + } + } + + fn captures_phantom(xs: Vec, mut ys: Cell>) { + let mut ys_ref = MyRef::new(&mut ys); + if xs + .iter() + .map({ + let mut ys_ref = ys_ref.clone(); + move |x| ys_ref.get().get_mut().remove(x) + }) + .collect::>() + .contains(&true) + { + todo!() + } + } +} + +pub fn issue8055(v: impl IntoIterator) -> Result, usize> { + let mut zeros = 0; + + let res: Vec<_> = v + .into_iter() + .filter(|i| { + if *i == 0 { + zeros += 1 + }; + *i != 0 + }) + .collect(); + + if zeros != 0 { + return Err(zeros); + } + Ok(res.into_iter()) +} + +mod issue8055_regression { + struct Foo { + inner: T, + marker: core::marker::PhantomData, + } + + impl Iterator for Foo { + type Item = T::Item; + fn next(&mut self) -> Option { + self.inner.next() + } + } + + fn foo() { + Foo { + inner: [].iter(), + marker: core::marker::PhantomData, + } + .collect::>() + .len(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_collect.rs b/src/tools/clippy/tests/ui/needless_collect.rs index 3dfb5194f4048..da4182966bb17 100644 --- a/src/tools/clippy/tests/ui/needless_collect.rs +++ b/src/tools/clippy/tests/ui/needless_collect.rs @@ -98,8 +98,115 @@ fn main() { let mut out = vec![]; values.iter().cloned().map(|x| out.push(x)).collect::>(); let _y = values.iter().cloned().map(|x| out.push(x)).collect::>(); // this is fine + + // Don't write a warning if we call `clone()` on the iterator + // https://github.com/rust-lang/rust-clippy/issues/13430 + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let _cloned = my_collection.into_iter().clone(); + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let my_iter = my_collection.into_iter(); + let _cloned = my_iter.clone(); + // Same for `as_slice()`, for same reason. + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let _sliced = my_collection.into_iter().as_slice(); + let my_collection: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + let my_iter = my_collection.into_iter(); + let _sliced = my_iter.as_slice(); + // Assignment outside of main scope + { + let x; + { + let xxx: Vec<()> = vec![()].into_iter().map(|()| {}).collect(); + x = xxx.into_iter(); + for i in x.as_slice() {} + } + } } fn foo(_: impl IntoIterator) {} fn bar>(_: Vec, _: I) {} fn baz>(_: I, _: (), _: impl IntoIterator) {} + +mod issue9191 { + use std::cell::Cell; + use std::collections::HashSet; + use std::hash::Hash; + use std::marker::PhantomData; + use std::ops::Deref; + + fn captures_ref_mut(xs: Vec, mut ys: HashSet) { + if xs.iter().map(|x| ys.remove(x)).collect::>().contains(&true) { + todo!() + } + } + + #[derive(Debug, Clone)] + struct MyRef<'a>(PhantomData<&'a mut Cell>>, *mut Cell>); + + impl MyRef<'_> { + fn new(target: &mut Cell>) -> Self { + MyRef(PhantomData, target) + } + + fn get(&mut self) -> &mut Cell> { + unsafe { &mut *self.1 } + } + } + + fn captures_phantom(xs: Vec, mut ys: Cell>) { + let mut ys_ref = MyRef::new(&mut ys); + if xs + .iter() + .map({ + let mut ys_ref = ys_ref.clone(); + move |x| ys_ref.get().get_mut().remove(x) + }) + .collect::>() + .contains(&true) + { + todo!() + } + } +} + +pub fn issue8055(v: impl IntoIterator) -> Result, usize> { + let mut zeros = 0; + + let res: Vec<_> = v + .into_iter() + .filter(|i| { + if *i == 0 { + zeros += 1 + }; + *i != 0 + }) + .collect(); + + if zeros != 0 { + return Err(zeros); + } + Ok(res.into_iter()) +} + +mod issue8055_regression { + struct Foo { + inner: T, + marker: core::marker::PhantomData, + } + + impl Iterator for Foo { + type Item = T::Item; + fn next(&mut self) -> Option { + self.inner.next() + } + } + + fn foo() { + Foo { + inner: [].iter(), + marker: core::marker::PhantomData, + } + .collect::>() + .len(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_if.fixed b/src/tools/clippy/tests/ui/needless_if.fixed index 6208ca19b82b4..c839156bed9bd 100644 --- a/src/tools/clippy/tests/ui/needless_if.fixed +++ b/src/tools/clippy/tests/ui/needless_if.fixed @@ -1,5 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] #![allow( clippy::blocks_in_conditions, clippy::if_same_then_else, @@ -46,9 +45,7 @@ fn main() { if let true = true && true {} - if true - && let true = true - {} + if true && let true = true {} // Can lint nested `if let`s ({ //~^ needless_if diff --git a/src/tools/clippy/tests/ui/needless_if.rs b/src/tools/clippy/tests/ui/needless_if.rs index b459ff877be61..11103af5c5598 100644 --- a/src/tools/clippy/tests/ui/needless_if.rs +++ b/src/tools/clippy/tests/ui/needless_if.rs @@ -1,5 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] #![allow( clippy::blocks_in_conditions, clippy::if_same_then_else, @@ -46,9 +45,7 @@ fn main() { if let true = true && true {} - if true - && let true = true - {} + if true && let true = true {} // Can lint nested `if let`s if { //~^ needless_if diff --git a/src/tools/clippy/tests/ui/needless_if.stderr b/src/tools/clippy/tests/ui/needless_if.stderr index eeb8d044526d3..4b56843bd5229 100644 --- a/src/tools/clippy/tests/ui/needless_if.stderr +++ b/src/tools/clippy/tests/ui/needless_if.stderr @@ -1,5 +1,5 @@ error: this `if` branch is empty - --> tests/ui/needless_if.rs:27:5 + --> tests/ui/needless_if.rs:26:5 | LL | if (true) {} | ^^^^^^^^^^^^ help: you can remove it @@ -8,13 +8,13 @@ LL | if (true) {} = help: to override `-D warnings` add `#[allow(clippy::needless_if)]` error: this `if` branch is empty - --> tests/ui/needless_if.rs:30:5 + --> tests/ui/needless_if.rs:29:5 | LL | if maybe_side_effect() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `maybe_side_effect();` error: this `if` branch is empty - --> tests/ui/needless_if.rs:36:5 + --> tests/ui/needless_if.rs:35:5 | LL | / if { LL | | @@ -31,7 +31,7 @@ LL + }); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:53:5 + --> tests/ui/needless_if.rs:50:5 | LL | / if { LL | | @@ -57,19 +57,19 @@ LL + } && true); | error: this `if` branch is empty - --> tests/ui/needless_if.rs:98:5 + --> tests/ui/needless_if.rs:95:5 | LL | if { maybe_side_effect() } {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() });` error: this `if` branch is empty - --> tests/ui/needless_if.rs:101:5 + --> tests/ui/needless_if.rs:98:5 | LL | if { maybe_side_effect() } && true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can remove it: `({ maybe_side_effect() } && true);` error: this `if` branch is empty - --> tests/ui/needless_if.rs:106:5 + --> tests/ui/needless_if.rs:103:5 | LL | if true {} | ^^^^^^^^^^ help: you can remove it: `true;` diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed index eb67e6cc82821..b686a8e9f1a06 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.fixed +++ b/src/tools/clippy/tests/ui/needless_late_init.fixed @@ -1,6 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] -#![allow(unused)] #![allow( clippy::assign_op_pattern, clippy::blocks_in_conditions, @@ -246,9 +244,7 @@ fn does_not_lint() { } let x; - if true - && let Some(n) = Some("let chains too") - { + if true && let Some(n) = Some("let chains too") { x = 1; } else { x = 2; @@ -297,3 +293,9 @@ fn issue13776() { let x; issue13776_mac!(x, 10); // should not lint } + +fn issue9895() { + + //~^ needless_late_init + let r = 5; +} diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs index 7de2202dc3234..23772ff702931 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.rs +++ b/src/tools/clippy/tests/ui/needless_late_init.rs @@ -1,6 +1,4 @@ //@aux-build:proc_macros.rs -#![feature(let_chains)] -#![allow(unused)] #![allow( clippy::assign_op_pattern, clippy::blocks_in_conditions, @@ -246,9 +244,7 @@ fn does_not_lint() { } let x; - if true - && let Some(n) = Some("let chains too") - { + if true && let Some(n) = Some("let chains too") { x = 1; } else { x = 2; @@ -297,3 +293,9 @@ fn issue13776() { let x; issue13776_mac!(x, 10); // should not lint } + +fn issue9895() { + let r; + //~^ needless_late_init + (r = 5); +} diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr index 02fd2811da58b..e3e25cdc8d769 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.stderr +++ b/src/tools/clippy/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ error: unneeded late initialization - --> tests/ui/needless_late_init.rs:27:5 + --> tests/ui/needless_late_init.rs:25:5 | LL | let a; | ^^^^^^ created here @@ -17,7 +17,7 @@ LL ~ let a = "zero"; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:31:5 + --> tests/ui/needless_late_init.rs:29:5 | LL | let b; | ^^^^^^ created here @@ -35,7 +35,7 @@ LL ~ let b = 1; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:33:5 + --> tests/ui/needless_late_init.rs:31:5 | LL | let c; | ^^^^^^ created here @@ -52,7 +52,7 @@ LL ~ let c = 2; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:38:5 + --> tests/ui/needless_late_init.rs:36:5 | LL | let d: usize; | ^^^^^^^^^^^^^ created here @@ -68,7 +68,7 @@ LL ~ let d: usize = 1; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:42:5 + --> tests/ui/needless_late_init.rs:40:5 | LL | let e; | ^^^^^^ created here @@ -84,7 +84,7 @@ LL ~ let e = format!("{}", d); | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:48:5 + --> tests/ui/needless_late_init.rs:46:5 | LL | let a; | ^^^^^^ @@ -103,7 +103,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:58:5 + --> tests/ui/needless_late_init.rs:56:5 | LL | let b; | ^^^^^^ @@ -120,7 +120,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:66:5 + --> tests/ui/needless_late_init.rs:64:5 | LL | let d; | ^^^^^^ @@ -138,7 +138,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:75:5 + --> tests/ui/needless_late_init.rs:73:5 | LL | let e; | ^^^^^^ @@ -155,7 +155,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:83:5 + --> tests/ui/needless_late_init.rs:81:5 | LL | let f; | ^^^^^^ @@ -169,7 +169,7 @@ LL ~ 1 => "three", | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:90:5 + --> tests/ui/needless_late_init.rs:88:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -186,7 +186,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:99:5 + --> tests/ui/needless_late_init.rs:97:5 | LL | let x; | ^^^^^^ created here @@ -203,7 +203,7 @@ LL ~ let x = 1; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:104:5 + --> tests/ui/needless_late_init.rs:102:5 | LL | let x; | ^^^^^^ created here @@ -220,7 +220,7 @@ LL ~ let x = SignificantDrop; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:109:5 + --> tests/ui/needless_late_init.rs:107:5 | LL | let x; | ^^^^^^ created here @@ -238,7 +238,7 @@ LL ~ let x = SignificantDrop; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:129:5 + --> tests/ui/needless_late_init.rs:127:5 | LL | let a; | ^^^^^^ @@ -257,7 +257,7 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:147:5 + --> tests/ui/needless_late_init.rs:145:5 | LL | let a; | ^^^^^^ @@ -275,5 +275,21 @@ LL | }, LL ~ }; | -error: aborting due to 16 previous errors +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:298:5 + | +LL | let r; + | ^^^^^^ created here +LL | +LL | (r = 5); + | ^^^^^^^ initialised here + | +help: move the declaration `r` here + | +LL ~ +LL | +LL ~ let r = 5; + | + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed index d59393fb3f3c6..e9d811986aa49 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed +++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed @@ -534,4 +534,11 @@ mod issue13749bis { impl<'a, T: 'a> Generic {} } +pub fn issue14607<'s>(x: &'s u8) { + #[expect(clippy::redundant_closure_call)] + (|| { + let _: &'s u8 = x; + })(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index e24907ab5fcdf..0b6eb9755b932 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -534,4 +534,11 @@ mod issue13749bis { impl<'a, T: 'a> Generic {} } +pub fn issue14607<'s>(x: &'s u8) { + #[expect(clippy::redundant_closure_call)] + (|| { + let _: &'s u8 = x; + })(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.fixed b/src/tools/clippy/tests/ui/needless_match.fixed index b2c2bbfaa3613..41acf44023f63 100644 --- a/src/tools/clippy/tests/ui/needless_match.fixed +++ b/src/tools/clippy/tests/ui/needless_match.fixed @@ -301,4 +301,16 @@ pub fn issue13574() -> Option<()> { None } +fn issue14754(t: Result) -> Result { + let _ = match t { + Ok(v) => Ok::<_, &'static str>(v), + err @ Err(_) => return err, + }; + println!("Still here"); + let x = t; + //~^^^^ needless_match + println!("Still here"); + x +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.rs b/src/tools/clippy/tests/ui/needless_match.rs index 1cb670edc60fb..936653b961bbb 100644 --- a/src/tools/clippy/tests/ui/needless_match.rs +++ b/src/tools/clippy/tests/ui/needless_match.rs @@ -364,4 +364,19 @@ pub fn issue13574() -> Option<()> { None } +fn issue14754(t: Result) -> Result { + let _ = match t { + Ok(v) => Ok::<_, &'static str>(v), + err @ Err(_) => return err, + }; + println!("Still here"); + let x = match t { + Ok(v) => Ok::<_, &'static str>(v), + err @ Err(_) => err, + }; + //~^^^^ needless_match + println!("Still here"); + x +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.stderr b/src/tools/clippy/tests/ui/needless_match.stderr index 719b0ef884696..5195ecdfa555e 100644 --- a/src/tools/clippy/tests/ui/needless_match.stderr +++ b/src/tools/clippy/tests/ui/needless_match.stderr @@ -151,5 +151,15 @@ LL | | None LL | | } | |_________^ help: replace it with: `A` -error: aborting due to 14 previous errors +error: this match expression is unnecessary + --> tests/ui/needless_match.rs:373:13 + | +LL | let x = match t { + | _____________^ +LL | | Ok(v) => Ok::<_, &'static str>(v), +LL | | err @ Err(_) => err, +LL | | }; + | |_____^ help: replace it with: `t` + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs index f0c5a716ac991..bdad3e3d5b008 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs @@ -1,8 +1,9 @@ #![allow( clippy::if_same_then_else, clippy::no_effect, + clippy::ptr_arg, clippy::redundant_closure_call, - clippy::ptr_arg + clippy::uninlined_format_args )] #![warn(clippy::needless_pass_by_ref_mut)] //@no-rustfix @@ -300,7 +301,7 @@ struct Data { } // Unsafe functions should not warn. unsafe fn get_mut_unchecked(ptr: &mut NonNull>) -> &mut T { - &mut (*ptr.as_ptr()).value + unsafe { &mut (*ptr.as_ptr()).value } } // Unsafe blocks should not warn. fn get_mut_unchecked2(ptr: &mut NonNull>) -> &mut T { diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr index 6637a255b5f51..94d98f0e9b12d 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr @@ -1,5 +1,5 @@ error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:11:11 + --> tests/ui/needless_pass_by_ref_mut.rs:12:11 | LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` @@ -8,79 +8,79 @@ LL | fn foo(s: &mut Vec, b: &u32, x: &mut u32) { = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:37:12 + --> tests/ui/needless_pass_by_ref_mut.rs:38:12 | LL | fn foo6(s: &mut Vec) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:48:12 + --> tests/ui/needless_pass_by_ref_mut.rs:49:12 | LL | fn bar(&mut self) {} | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:51:29 + --> tests/ui/needless_pass_by_ref_mut.rs:52:29 | LL | fn mushroom(&self, vec: &mut Vec) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:129:16 + --> tests/ui/needless_pass_by_ref_mut.rs:130:16 | LL | async fn a1(x: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:134:16 + --> tests/ui/needless_pass_by_ref_mut.rs:135:16 | LL | async fn a2(x: &mut i32, y: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:139:16 + --> tests/ui/needless_pass_by_ref_mut.rs:140:16 | LL | async fn a3(x: &mut i32, y: String, z: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:144:16 + --> tests/ui/needless_pass_by_ref_mut.rs:145:16 | LL | async fn a4(x: &mut i32, y: i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:149:24 + --> tests/ui/needless_pass_by_ref_mut.rs:150:24 | LL | async fn a5(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:154:24 + --> tests/ui/needless_pass_by_ref_mut.rs:155:24 | LL | async fn a6(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:159:32 + --> tests/ui/needless_pass_by_ref_mut.rs:160:32 | LL | async fn a7(x: i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:164:24 + --> tests/ui/needless_pass_by_ref_mut.rs:165:24 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:164:45 + --> tests/ui/needless_pass_by_ref_mut.rs:165:45 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:200:16 + --> tests/ui/needless_pass_by_ref_mut.rs:201:16 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -88,7 +88,7 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:205:20 + --> tests/ui/needless_pass_by_ref_mut.rs:206:20 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -96,115 +96,115 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:218:39 + --> tests/ui/needless_pass_by_ref_mut.rs:219:39 | LL | async fn inner_async2(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:227:26 + --> tests/ui/needless_pass_by_ref_mut.rs:228:26 | LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:247:30 + --> tests/ui/needless_pass_by_ref_mut.rs:248:30 | LL | async fn call_in_closure1(n: &mut str) { | ^^^^^^^^ help: consider changing to: `&str` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:267:16 + --> tests/ui/needless_pass_by_ref_mut.rs:268:16 | LL | fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { | ^^^^^^^^^^ help: consider changing to: `&usize` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:279:22 + --> tests/ui/needless_pass_by_ref_mut.rs:280:22 | LL | async fn closure4(n: &mut usize) { | ^^^^^^^^^^ help: consider changing to: `&usize` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:334:12 + --> tests/ui/needless_pass_by_ref_mut.rs:335:12 | LL | fn bar(&mut self) {} | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:337:18 + --> tests/ui/needless_pass_by_ref_mut.rs:338:18 | LL | async fn foo(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^^ help: consider changing to: `&self` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:337:45 + --> tests/ui/needless_pass_by_ref_mut.rs:338:45 | LL | async fn foo(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:346:46 + --> tests/ui/needless_pass_by_ref_mut.rs:347:46 | LL | async fn foo2(&mut self, u: &mut i32, v: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:363:18 + --> tests/ui/needless_pass_by_ref_mut.rs:364:18 | LL | fn _empty_tup(x: &mut (())) {} | ^^^^^^^^^ help: consider changing to: `&()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:365:19 + --> tests/ui/needless_pass_by_ref_mut.rs:366:19 | LL | fn _single_tup(x: &mut ((i32,))) {} | ^^^^^^^^^^^^^ help: consider changing to: `&(i32,)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:367:18 + --> tests/ui/needless_pass_by_ref_mut.rs:368:18 | LL | fn _multi_tup(x: &mut ((i32, u32))) {} | ^^^^^^^^^^^^^^^^^ help: consider changing to: `&(i32, u32)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:369:11 + --> tests/ui/needless_pass_by_ref_mut.rs:370:11 | LL | fn _fn(x: &mut (fn())) {} | ^^^^^^^^^^^ help: consider changing to: `&fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:372:23 + --> tests/ui/needless_pass_by_ref_mut.rs:373:23 | LL | fn _extern_rust_fn(x: &mut extern "Rust" fn()) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "Rust" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:374:20 + --> tests/ui/needless_pass_by_ref_mut.rs:375:20 | LL | fn _extern_c_fn(x: &mut extern "C" fn()) {} | ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "C" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:376:18 + --> tests/ui/needless_pass_by_ref_mut.rs:377:18 | LL | fn _unsafe_fn(x: &mut unsafe fn()) {} | ^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:378:25 + --> tests/ui/needless_pass_by_ref_mut.rs:379:25 | LL | fn _unsafe_extern_fn(x: &mut unsafe extern "C" fn()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn()` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:380:20 + --> tests/ui/needless_pass_by_ref_mut.rs:381:20 | LL | fn _fn_with_arg(x: &mut unsafe extern "C" fn(i32)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn(i32)` error: this argument is a mutable reference, but not used mutably - --> tests/ui/needless_pass_by_ref_mut.rs:382:20 + --> tests/ui/needless_pass_by_ref_mut.rs:383:20 | LL | fn _fn_with_ret(x: &mut unsafe extern "C" fn() -> (i32)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn() -> (i32)` diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut_2021.rs b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut_2021.rs new file mode 100644 index 0000000000000..994eba9cae3d7 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut_2021.rs @@ -0,0 +1,12 @@ +//@edition: 2021 +//@check-pass +#![warn(clippy::needless_pass_by_ref_mut)] + +struct Data { + value: T, +} + +// Unsafe functions should not warn. +unsafe fn get_mut_unchecked(ptr: &mut std::ptr::NonNull>) -> &mut T { + &mut (*ptr.as_ptr()).value +} diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.rs b/src/tools/clippy/tests/ui/needless_pass_by_value.rs index 885fb409417b1..aef7fff287058 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_value.rs +++ b/src/tools/clippy/tests/ui/needless_pass_by_value.rs @@ -189,6 +189,42 @@ struct Obj(String); fn prefix_test(_unused_with_prefix: Obj) {} +// Regression test for . +// It's more idiomatic to write `Option<&T>` rather than `&Option`. +fn option_inner_ref(x: Option) { + //~^ ERROR: this argument is passed by value, but not consumed in the function body + assert!(x.is_some()); +} + +mod non_standard { + #[derive(Debug)] + pub struct Option(T); +} + +fn non_standard_option(x: non_standard::Option) { + //~^ needless_pass_by_value + dbg!(&x); +} + +fn option_by_name(x: Option>>>) { + //~^ needless_pass_by_value + dbg!(&x); +} + +type OptStr = Option; + +fn non_option(x: OptStr) { + //~^ needless_pass_by_value + dbg!(&x); +} + +type Opt = Option; + +fn non_option_either(x: Opt) { + //~^ needless_pass_by_value + dbg!(&x); +} + fn main() { // This should not cause an ICE either // https://github.com/rust-lang/rust-clippy/issues/3144 diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.stderr b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr index 4ac4fdce972d2..e4381d1db53ad 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_value.stderr +++ b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr @@ -17,43 +17,78 @@ error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:35:22 | LL | fn bar(x: String, y: Wrapper) { - | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + | ^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn bar(x: String, y: &Wrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:44:71 | LL | fn test_borrow_trait, U: AsRef, V>(t: T, u: U, v: V) { - | ^ help: consider taking a reference instead: `&V` + | ^ + | +help: consider taking a reference instead + | +LL | fn test_borrow_trait, U: AsRef, V>(t: T, u: U, v: &V) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:58:18 | LL | fn test_match(x: Option>, y: Option>) { - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option>` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn test_match(x: Option>, y: Option>) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:73:24 | LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { - | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + | ^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn test_destructure(x: &Wrapper, y: Wrapper, z: Wrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:73:36 | LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { - | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + | ^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn test_destructure(x: Wrapper, y: &Wrapper, z: Wrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:92:49 | LL | fn test_blanket_ref(vals: T, serializable: S) {} - | ^ help: consider taking a reference instead: `&T` + | ^ + | +help: consider taking a reference instead + | +LL | fn test_blanket_ref(vals: &T, serializable: S) {} + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:95:18 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { - | ^^^^^^ help: consider taking a reference instead: `&String` + | ^^^^^^ + | +help: consider taking a reference instead + | +LL | fn issue_2114(s: &String, t: String, u: Vec, v: Vec) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:95:29 @@ -76,7 +111,12 @@ error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:95:40 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { - | ^^^^^^^^ help: consider taking a reference instead: `&Vec` + | ^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn issue_2114(s: String, t: String, u: &Vec, v: Vec) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:95:53 @@ -105,79 +145,175 @@ error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:115:12 | LL | t: String, - | ^^^^^^ help: consider taking a reference instead: `&String` + | ^^^^^^ + | +help: consider taking a reference instead + | +LL | t: &String, + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:125:23 | LL | fn baz(&self, uu: U, ss: Self) {} - | ^ help: consider taking a reference instead: `&U` + | ^ + | +help: consider taking a reference instead + | +LL | fn baz(&self, uu: &U, ss: Self) {} + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:125:30 | LL | fn baz(&self, uu: U, ss: Self) {} - | ^^^^ help: consider taking a reference instead: `&Self` + | ^^^^ + | +help: consider taking a reference instead + | +LL | fn baz(&self, uu: U, ss: &Self) {} + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:149:24 | LL | fn bar_copy(x: u32, y: CopyWrapper) { - | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | ^^^^^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:147:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ +help: consider taking a reference instead + | +LL | fn bar_copy(x: u32, y: &CopyWrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:157:29 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { - | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | ^^^^^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:147:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ +help: consider taking a reference instead + | +LL | fn test_destructure_copy(x: &CopyWrapper, y: CopyWrapper, z: CopyWrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:157:45 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { - | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | ^^^^^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:147:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ +help: consider taking a reference instead + | +LL | fn test_destructure_copy(x: CopyWrapper, y: &CopyWrapper, z: CopyWrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:157:61 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { - | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | ^^^^^^^^^^^ | help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:147:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ +help: consider taking a reference instead + | +LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: &CopyWrapper) { + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:173:40 | LL | fn some_fun<'b, S: Bar<'b, ()>>(items: S) {} - | ^ help: consider taking a reference instead: `&S` + | ^ + | +help: consider taking a reference instead + | +LL | fn some_fun<'b, S: Bar<'b, ()>>(items: &S) {} + | + error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:179:20 | LL | fn more_fun(items: impl Club<'static, i32>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn more_fun(items: &impl Club<'static, i32>) {} + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:194:24 + | +LL | fn option_inner_ref(x: Option) { + | ^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn option_inner_ref(x: Option<&String>) { + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:204:27 + | +LL | fn non_standard_option(x: non_standard::Option) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn non_standard_option(x: &non_standard::Option) { + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:209:22 + | +LL | fn option_by_name(x: Option>>>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn option_by_name(x: Option>>>) { + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:216:18 + | +LL | fn non_option(x: OptStr) { + | ^^^^^^ + | +help: consider taking a reference instead + | +LL | fn non_option(x: &OptStr) { + | + + +error: this argument is passed by value, but not consumed in the function body + --> tests/ui/needless_pass_by_value.rs:223:25 + | +LL | fn non_option_either(x: Opt) { + | ^^^^^^^^^^^ + | +help: consider taking a reference instead + | +LL | fn non_option_either(x: &Opt) { + | + -error: aborting due to 22 previous errors +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/needless_question_mark.stderr b/src/tools/clippy/tests/ui/needless_question_mark.stderr index 55da4f28976c1..8516cee48e679 100644 --- a/src/tools/clippy/tests/ui/needless_question_mark.stderr +++ b/src/tools/clippy/tests/ui/needless_question_mark.stderr @@ -1,100 +1,188 @@ -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:20:12 | LL | return Some(to.magic?); - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-question-mark` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_question_mark)]` +help: remove the enclosing `Some` and `?` operator + | +LL - return Some(to.magic?); +LL + return to.magic; + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:29:12 | LL | return Some(to.magic?) - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - return Some(to.magic?) +LL + return to.magic + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:35:5 | LL | Some(to.magic?) - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - Some(to.magic?) +LL + to.magic + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:41:21 | LL | to.and_then(|t| Some(t.magic?)) - | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` + | ^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - to.and_then(|t| Some(t.magic?)) +LL + to.and_then(|t| t.magic) + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:51:9 | LL | Some(t.magic?) - | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` + | ^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - Some(t.magic?) +LL + t.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:57:12 | LL | return Ok(tr.magic?); - | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` + | ^^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - return Ok(tr.magic?); +LL + return tr.magic; + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:65:12 | LL | return Ok(tr.magic?) - | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` + | ^^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - return Ok(tr.magic?) +LL + return tr.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:70:5 | LL | Ok(tr.magic?) - | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` + | ^^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - Ok(tr.magic?) +LL + tr.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:75:21 | LL | tr.and_then(|t| Ok(t.magic?)) - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - tr.and_then(|t| Ok(t.magic?)) +LL + tr.and_then(|t| t.magic) + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:84:9 | LL | Ok(t.magic?) - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - Ok(t.magic?) +LL + t.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:92:16 | LL | return Ok(t.magic?); - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - return Ok(t.magic?); +LL + return t.magic; + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:128:27 | LL | || -> Option<_> { Some(Some($expr)?) }() - | ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)` + | ^^^^^^^^^^^^^^^^^^ ... LL | let _x = some_and_qmark_in_macro!(x?); | ---------------------------- in this macro invocation | = note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) +help: remove the enclosing `Some` and `?` operator + | +LL - || -> Option<_> { Some(Some($expr)?) }() +LL + || -> Option<_> { Some($expr) }() + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:140:5 | LL | Some(to.magic?) - | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + | ^^^^^^^^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - Some(to.magic?) +LL + to.magic + | -error: question mark operator is useless here +error: enclosing `Ok` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:149:5 | LL | Ok(s.magic?) - | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic` + | ^^^^^^^^^^^^ + | +help: remove the enclosing `Ok` and `?` operator + | +LL - Ok(s.magic?) +LL + s.magic + | -error: question mark operator is useless here +error: enclosing `Some` and `?` operator are unneeded --> tests/ui/needless_question_mark.rs:154:7 | LL | { Some(a?) } - | ^^^^^^^^ help: try removing question mark and `Some()`: `a` + | ^^^^^^^^ + | +help: remove the enclosing `Some` and `?` operator + | +LL - { Some(a?) } +LL + { a } + | error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index efc073ebe8747..17d3862cd8668 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -6,7 +6,8 @@ clippy::single_match, clippy::needless_bool, clippy::equatable_if_let, - clippy::needless_else + clippy::needless_else, + clippy::missing_safety_doc )] #![warn(clippy::needless_return)] @@ -83,14 +84,14 @@ fn test_macro_call() -> i32 { } fn test_void_fun() { - //~^^ needless_return + //~^ needless_return } fn test_void_if_fun(b: bool) { if b { - //~^^ needless_return + //~^ needless_return } else { - //~^^ needless_return + //~^ needless_return } } @@ -107,7 +108,7 @@ fn test_nested_match(x: u32) { 0 => (), 1 => { let _ = 42; - //~^^ needless_return + //~^ needless_return }, _ => (), //~^ needless_return @@ -155,7 +156,7 @@ mod issue6501 { fn test_closure() { let _ = || { - //~^^ needless_return + //~^ needless_return }; let _ = || {}; //~^ needless_return @@ -219,14 +220,14 @@ async fn async_test_macro_call() -> i32 { } async fn async_test_void_fun() { - //~^^ needless_return + //~^ needless_return } async fn async_test_void_if_fun(b: bool) { if b { - //~^^ needless_return + //~^ needless_return } else { - //~^^ needless_return + //~^ needless_return } } @@ -353,7 +354,7 @@ fn issue9503(x: usize) -> isize { mod issue9416 { pub fn with_newline() { let _ = 42; - //~^^ needless_return + //~^ needless_return } #[rustfmt::skip] @@ -442,3 +443,12 @@ fn b(x: Option) -> Option { }, } } + +unsafe fn todo() -> *const u8 { + todo!() +} + +pub unsafe fn issue_12157() -> *const i32 { + (unsafe { todo() } as *const i32) + //~^ needless_return +} diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index 283d86f25fe55..1c6e7ffa1ee8c 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -6,7 +6,8 @@ clippy::single_match, clippy::needless_bool, clippy::equatable_if_let, - clippy::needless_else + clippy::needless_else, + clippy::missing_safety_doc )] #![warn(clippy::needless_return)] @@ -84,16 +85,16 @@ fn test_macro_call() -> i32 { fn test_void_fun() { return; - //~^^ needless_return + //~^ needless_return } fn test_void_if_fun(b: bool) { if b { return; - //~^^ needless_return + //~^ needless_return } else { return; - //~^^ needless_return + //~^ needless_return } } @@ -111,7 +112,7 @@ fn test_nested_match(x: u32) { 1 => { let _ = 42; return; - //~^^ needless_return + //~^ needless_return }, _ => return, //~^ needless_return @@ -160,7 +161,7 @@ mod issue6501 { fn test_closure() { let _ = || { return; - //~^^ needless_return + //~^ needless_return }; let _ = || return; //~^ needless_return @@ -225,16 +226,16 @@ async fn async_test_macro_call() -> i32 { async fn async_test_void_fun() { return; - //~^^ needless_return + //~^ needless_return } async fn async_test_void_if_fun(b: bool) { if b { return; - //~^^ needless_return + //~^ needless_return } else { return; - //~^^ needless_return + //~^ needless_return } } @@ -362,7 +363,7 @@ mod issue9416 { pub fn with_newline() { let _ = 42; return; - //~^^ needless_return + //~^ needless_return } #[rustfmt::skip] @@ -451,3 +452,12 @@ fn b(x: Option) -> Option { }, } } + +unsafe fn todo() -> *const u8 { + todo!() +} + +pub unsafe fn issue_12157() -> *const i32 { + return unsafe { todo() } as *const i32; + //~^ needless_return +} diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr index 04c97a41d676b..26dd265379be0 100644 --- a/src/tools/clippy/tests/ui/needless_return.stderr +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement - --> tests/ui/needless_return.rs:29:5 + --> tests/ui/needless_return.rs:30:5 | LL | return true; | ^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:34:5 + --> tests/ui/needless_return.rs:35:5 | LL | return true; | ^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:40:5 + --> tests/ui/needless_return.rs:41:5 | LL | return true;;; | ^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:46:5 + --> tests/ui/needless_return.rs:47:5 | LL | return true;; ; ; | ^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:52:9 + --> tests/ui/needless_return.rs:53:9 | LL | return true; | ^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:55:9 + --> tests/ui/needless_return.rs:56:9 | LL | return false; | ^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + false | error: unneeded `return` statement - --> tests/ui/needless_return.rs:62:17 + --> tests/ui/needless_return.rs:63:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + true => false, | error: unneeded `return` statement - --> tests/ui/needless_return.rs:65:13 + --> tests/ui/needless_return.rs:66:13 | LL | return true; | ^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:73:9 + --> tests/ui/needless_return.rs:74:9 | LL | return true; | ^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:76:16 + --> tests/ui/needless_return.rs:77:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL + let _ = || true; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:81:5 + --> tests/ui/needless_return.rs:82:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -133,12 +133,10 @@ LL + the_answer!() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:85:21 + --> tests/ui/needless_return.rs:87:5 | -LL | fn test_void_fun() { - | _____________________^ -LL | | return; - | |__________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -148,12 +146,10 @@ LL + fn test_void_fun() { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:91:11 + --> tests/ui/needless_return.rs:93:9 | -LL | if b { - | ___________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -163,12 +159,10 @@ LL + if b { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:94:13 + --> tests/ui/needless_return.rs:96:9 | -LL | } else { - | _____________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -178,7 +172,7 @@ LL + } else { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:103:14 + --> tests/ui/needless_return.rs:104:14 | LL | _ => return, | ^^^^^^ @@ -190,12 +184,10 @@ LL + _ => (), | error: unneeded `return` statement - --> tests/ui/needless_return.rs:112:24 + --> tests/ui/needless_return.rs:114:13 | -LL | let _ = 42; - | ________________________^ -LL | | return; - | |__________________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -205,7 +197,7 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:116:14 + --> tests/ui/needless_return.rs:117:14 | LL | _ => return, | ^^^^^^ @@ -217,7 +209,7 @@ LL + _ => (), | error: unneeded `return` statement - --> tests/ui/needless_return.rs:130:9 + --> tests/ui/needless_return.rs:131:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -229,7 +221,7 @@ LL + String::from("test") | error: unneeded `return` statement - --> tests/ui/needless_return.rs:133:9 + --> tests/ui/needless_return.rs:134:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +233,7 @@ LL + String::new() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:156:32 + --> tests/ui/needless_return.rs:157:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ @@ -253,12 +245,10 @@ LL + bar.unwrap_or_else(|_| {}) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:161:21 + --> tests/ui/needless_return.rs:163:13 | -LL | let _ = || { - | _____________________^ -LL | | return; - | |__________________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -268,7 +258,7 @@ LL + let _ = || { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:165:20 + --> tests/ui/needless_return.rs:166:20 | LL | let _ = || return; | ^^^^^^ @@ -280,7 +270,7 @@ LL + let _ = || {}; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:172:32 + --> tests/ui/needless_return.rs:173:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ @@ -292,7 +282,7 @@ LL + res.unwrap_or_else(|_| Foo) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:182:5 + --> tests/ui/needless_return.rs:183:5 | LL | return true; | ^^^^^^^^^^^ @@ -304,7 +294,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:187:5 + --> tests/ui/needless_return.rs:188:5 | LL | return true; | ^^^^^^^^^^^ @@ -316,7 +306,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:193:9 + --> tests/ui/needless_return.rs:194:9 | LL | return true; | ^^^^^^^^^^^ @@ -328,7 +318,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:196:9 + --> tests/ui/needless_return.rs:197:9 | LL | return false; | ^^^^^^^^^^^^ @@ -340,7 +330,7 @@ LL + false | error: unneeded `return` statement - --> tests/ui/needless_return.rs:203:17 + --> tests/ui/needless_return.rs:204:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -352,7 +342,7 @@ LL + true => false, | error: unneeded `return` statement - --> tests/ui/needless_return.rs:206:13 + --> tests/ui/needless_return.rs:207:13 | LL | return true; | ^^^^^^^^^^^ @@ -364,7 +354,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:214:9 + --> tests/ui/needless_return.rs:215:9 | LL | return true; | ^^^^^^^^^^^ @@ -376,7 +366,7 @@ LL + true | error: unneeded `return` statement - --> tests/ui/needless_return.rs:217:16 + --> tests/ui/needless_return.rs:218:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -388,7 +378,7 @@ LL + let _ = || true; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:222:5 + --> tests/ui/needless_return.rs:223:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -400,12 +390,10 @@ LL + the_answer!() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:226:33 + --> tests/ui/needless_return.rs:228:5 | -LL | async fn async_test_void_fun() { - | _________________________________^ -LL | | return; - | |__________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -415,12 +403,10 @@ LL + async fn async_test_void_fun() { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:232:11 + --> tests/ui/needless_return.rs:234:9 | -LL | if b { - | ___________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -430,12 +416,10 @@ LL + if b { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:235:13 + --> tests/ui/needless_return.rs:237:9 | -LL | } else { - | _____________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -445,7 +429,7 @@ LL + } else { | error: unneeded `return` statement - --> tests/ui/needless_return.rs:244:14 + --> tests/ui/needless_return.rs:245:14 | LL | _ => return, | ^^^^^^ @@ -457,7 +441,7 @@ LL + _ => (), | error: unneeded `return` statement - --> tests/ui/needless_return.rs:258:9 + --> tests/ui/needless_return.rs:259:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -469,7 +453,7 @@ LL + String::from("test") | error: unneeded `return` statement - --> tests/ui/needless_return.rs:261:9 + --> tests/ui/needless_return.rs:262:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -481,7 +465,7 @@ LL + String::new() | error: unneeded `return` statement - --> tests/ui/needless_return.rs:278:5 + --> tests/ui/needless_return.rs:279:5 | LL | return format!("Hello {}", "world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -493,7 +477,7 @@ LL + format!("Hello {}", "world!") | error: unneeded `return` statement - --> tests/ui/needless_return.rs:320:9 + --> tests/ui/needless_return.rs:321:9 | LL | return true; | ^^^^^^^^^^^ @@ -508,7 +492,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:323:9 + --> tests/ui/needless_return.rs:324:9 | LL | return false; | ^^^^^^^^^^^^ @@ -521,7 +505,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:331:13 + --> tests/ui/needless_return.rs:332:13 | LL | return 10; | ^^^^^^^^^ @@ -536,7 +520,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:335:13 + --> tests/ui/needless_return.rs:336:13 | LL | return 100; | ^^^^^^^^^^ @@ -550,7 +534,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:344:9 + --> tests/ui/needless_return.rs:345:9 | LL | return 0; | ^^^^^^^^ @@ -563,7 +547,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:352:13 + --> tests/ui/needless_return.rs:353:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -579,7 +563,7 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:355:13 + --> tests/ui/needless_return.rs:356:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -593,12 +577,10 @@ LL ~ } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:363:20 + --> tests/ui/needless_return.rs:365:9 | -LL | let _ = 42; - | ____________________^ -LL | | return; - | |______________^ +LL | return; + | ^^^^^^ | help: remove `return` | @@ -608,10 +590,10 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:370:20 + --> tests/ui/needless_return.rs:371:21 | LL | let _ = 42; return; - | ^^^^^^^ + | ^^^^^^ | help: remove `return` | @@ -620,7 +602,7 @@ LL + let _ = 42; | error: unneeded `return` statement - --> tests/ui/needless_return.rs:383:9 + --> tests/ui/needless_return.rs:384:9 | LL | return Ok(format!("ok!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -632,7 +614,7 @@ LL + Ok(format!("ok!")) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:386:9 + --> tests/ui/needless_return.rs:387:9 | LL | return Err(format!("err!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -644,7 +626,7 @@ LL + Err(format!("err!")) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:393:9 + --> tests/ui/needless_return.rs:394:9 | LL | return if true { 1 } else { 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -656,7 +638,7 @@ LL + if true { 1 } else { 2 } | error: unneeded `return` statement - --> tests/ui/needless_return.rs:398:9 + --> tests/ui/needless_return.rs:399:9 | LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -668,7 +650,7 @@ LL + (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else | error: unneeded `return` statement - --> tests/ui/needless_return.rs:420:5 + --> tests/ui/needless_return.rs:421:5 | LL | return { "a".to_string() } + "b" + { "c" }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -680,7 +662,7 @@ LL + ({ "a".to_string() } + "b" + { "c" }) | error: unneeded `return` statement - --> tests/ui/needless_return.rs:425:5 + --> tests/ui/needless_return.rs:426:5 | LL | return "".split("").next().unwrap().to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -691,5 +673,17 @@ LL - return "".split("").next().unwrap().to_string(); LL + "".split("").next().unwrap().to_string() | -error: aborting due to 54 previous errors +error: unneeded `return` statement + --> tests/ui/needless_return.rs:461:5 + | +LL | return unsafe { todo() } as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove `return` and wrap the sequence with parentheses + | +LL - return unsafe { todo() } as *const i32; +LL + (unsafe { todo() } as *const i32) + | + +error: aborting due to 55 previous errors diff --git a/src/tools/clippy/tests/ui/neg_multiply.fixed b/src/tools/clippy/tests/ui/neg_multiply.fixed index 995470493bfb7..ff6e08300e298 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.fixed +++ b/src/tools/clippy/tests/ui/neg_multiply.fixed @@ -53,3 +53,32 @@ fn main() { X * -1; // should be ok -1 * X; // should also be ok } + +fn float() { + let x = 0.0; + + -x; + //~^ neg_multiply + + -x; + //~^ neg_multiply + + 100.0 + -x; + //~^ neg_multiply + + -(100.0 + x); + //~^ neg_multiply + + -17.0; + //~^ neg_multiply + + 0.0 + -0.0; + //~^ neg_multiply + + -(3.0_f32 as f64); + //~^ neg_multiply + -(3.0_f32 as f64); + //~^ neg_multiply + + -1.0 * -1.0; // should be ok +} diff --git a/src/tools/clippy/tests/ui/neg_multiply.rs b/src/tools/clippy/tests/ui/neg_multiply.rs index 95b94e29517fa..b0f4e85c78e5d 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.rs +++ b/src/tools/clippy/tests/ui/neg_multiply.rs @@ -53,3 +53,32 @@ fn main() { X * -1; // should be ok -1 * X; // should also be ok } + +fn float() { + let x = 0.0; + + x * -1.0; + //~^ neg_multiply + + -1.0 * x; + //~^ neg_multiply + + 100.0 + x * -1.0; + //~^ neg_multiply + + (100.0 + x) * -1.0; + //~^ neg_multiply + + -1.0 * 17.0; + //~^ neg_multiply + + 0.0 + 0.0 * -1.0; + //~^ neg_multiply + + 3.0_f32 as f64 * -1.0; + //~^ neg_multiply + (3.0_f32 as f64) * -1.0; + //~^ neg_multiply + + -1.0 * -1.0; // should be ok +} diff --git a/src/tools/clippy/tests/ui/neg_multiply.stderr b/src/tools/clippy/tests/ui/neg_multiply.stderr index 9efa5d3ba1f1d..2ef7e32ce05e1 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.stderr +++ b/src/tools/clippy/tests/ui/neg_multiply.stderr @@ -49,5 +49,53 @@ error: this multiplication by -1 can be written more succinctly LL | (3_usize as i32) * -1; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3_usize as i32)` -error: aborting due to 8 previous errors +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:60:5 + | +LL | x * -1.0; + | ^^^^^^^^ help: consider using: `-x` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:63:5 + | +LL | -1.0 * x; + | ^^^^^^^^ help: consider using: `-x` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:66:13 + | +LL | 100.0 + x * -1.0; + | ^^^^^^^^ help: consider using: `-x` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:69:5 + | +LL | (100.0 + x) * -1.0; + | ^^^^^^^^^^^^^^^^^^ help: consider using: `-(100.0 + x)` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:72:5 + | +LL | -1.0 * 17.0; + | ^^^^^^^^^^^ help: consider using: `-17.0` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:75:11 + | +LL | 0.0 + 0.0 * -1.0; + | ^^^^^^^^^^ help: consider using: `-0.0` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:78:5 + | +LL | 3.0_f32 as f64 * -1.0; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3.0_f32 as f64)` + +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:80:5 + | +LL | (3.0_f32 as f64) * -1.0; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3.0_f32 as f64)` + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs index 2d8e04c192e40..e0f54ef899b05 100644 --- a/src/tools/clippy/tests/ui/never_loop.rs +++ b/src/tools/clippy/tests/ui/never_loop.rs @@ -422,6 +422,34 @@ pub fn issue12205() -> Option<()> { } } +fn stmt_after_return() { + for v in 0..10 { + //~^ never_loop + break; + println!("{v}"); + } +} + +fn loop_label() { + 'outer: for v in 0..10 { + //~^ never_loop + loop { + //~^ never_loop + break 'outer; + } + return; + } + + for v in 0..10 { + //~^ never_loop + 'inner: loop { + //~^ never_loop + break 'inner; + } + return; + } +} + fn main() { test1(); test2(); diff --git a/src/tools/clippy/tests/ui/never_loop.stderr b/src/tools/clippy/tests/ui/never_loop.stderr index f6d6d109542b5..bc9a7ec48b487 100644 --- a/src/tools/clippy/tests/ui/never_loop.stderr +++ b/src/tools/clippy/tests/ui/never_loop.stderr @@ -164,5 +164,73 @@ LL | | unimplemented!("not yet"); LL | | } | |_____^ -error: aborting due to 16 previous errors +error: this loop never actually loops + --> tests/ui/never_loop.rs:426:5 + | +LL | / for v in 0..10 { +LL | | +LL | | break; +LL | | println!("{v}"); +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL - for v in 0..10 { +LL + if let Some(v) = (0..10).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:434:5 + | +LL | / 'outer: for v in 0..10 { +LL | | +LL | | loop { +... | +LL | | return; +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL - 'outer: for v in 0..10 { +LL + if let Some(v) = (0..10).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:436:9 + | +LL | / loop { +LL | | +LL | | break 'outer; +LL | | } + | |_________^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:443:5 + | +LL | / for v in 0..10 { +LL | | +LL | | 'inner: loop { +... | +LL | | return; +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL - for v in 0..10 { +LL + if let Some(v) = (0..10).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:445:9 + | +LL | / 'inner: loop { +LL | | +LL | | break 'inner; +LL | | } + | |_________^ + +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/never_loop_fixable.fixed b/src/tools/clippy/tests/ui/never_loop_fixable.fixed new file mode 100644 index 0000000000000..5bc9ff1bb4df0 --- /dev/null +++ b/src/tools/clippy/tests/ui/never_loop_fixable.fixed @@ -0,0 +1,20 @@ +#![allow(clippy::iter_next_slice, clippy::needless_return)] + +fn no_break_or_continue_loop() { + if let Some(i) = [1, 2, 3].iter().next() { + //~^ never_loop + return; + } +} + +fn no_break_or_continue_loop_outer() { + if let Some(i) = [1, 2, 3].iter().next() { + //~^ never_loop + return; + loop { + if true { + continue; + } + } + } +} diff --git a/src/tools/clippy/tests/ui/never_loop_fixable.rs b/src/tools/clippy/tests/ui/never_loop_fixable.rs new file mode 100644 index 0000000000000..9782bc107e9a6 --- /dev/null +++ b/src/tools/clippy/tests/ui/never_loop_fixable.rs @@ -0,0 +1,20 @@ +#![allow(clippy::iter_next_slice, clippy::needless_return)] + +fn no_break_or_continue_loop() { + for i in [1, 2, 3].iter() { + //~^ never_loop + return; + } +} + +fn no_break_or_continue_loop_outer() { + for i in [1, 2, 3].iter() { + //~^ never_loop + return; + loop { + if true { + continue; + } + } + } +} diff --git a/src/tools/clippy/tests/ui/never_loop_fixable.stderr b/src/tools/clippy/tests/ui/never_loop_fixable.stderr new file mode 100644 index 0000000000000..ee02d9a429760 --- /dev/null +++ b/src/tools/clippy/tests/ui/never_loop_fixable.stderr @@ -0,0 +1,35 @@ +error: this loop never actually loops + --> tests/ui/never_loop_fixable.rs:4:5 + | +LL | / for i in [1, 2, 3].iter() { +LL | | +LL | | return; +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default +help: if you need the first element of the iterator, try writing + | +LL - for i in [1, 2, 3].iter() { +LL + if let Some(i) = [1, 2, 3].iter().next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop_fixable.rs:11:5 + | +LL | / for i in [1, 2, 3].iter() { +LL | | +LL | | return; +LL | | loop { +... | +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL - for i in [1, 2, 3].iter() { +LL + if let Some(i) = [1, 2, 3].iter().next() { + | + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/new_ret_no_self_overflow.rs b/src/tools/clippy/tests/ui/new_ret_no_self_overflow.rs index 8a85c56622763..f317674bc1ae4 100644 --- a/src/tools/clippy/tests/ui/new_ret_no_self_overflow.rs +++ b/src/tools/clippy/tests/ui/new_ret_no_self_overflow.rs @@ -17,6 +17,7 @@ mod issue10041 { struct Bomb2; impl Bomb2 { + #[define_opaque(X)] pub fn new() -> X { //~^ ERROR: overflow evaluating the requirement 0i32 diff --git a/src/tools/clippy/tests/ui/new_ret_no_self_overflow.stderr b/src/tools/clippy/tests/ui/new_ret_no_self_overflow.stderr index 77c1b64ebc8e3..8ecd0437e7dbd 100644 --- a/src/tools/clippy/tests/ui/new_ret_no_self_overflow.stderr +++ b/src/tools/clippy/tests/ui/new_ret_no_self_overflow.stderr @@ -1,5 +1,5 @@ error[E0275]: overflow evaluating the requirement `::Output == issue10041::X` - --> tests/ui/new_ret_no_self_overflow.rs:20:25 + --> tests/ui/new_ret_no_self_overflow.rs:21:25 | LL | pub fn new() -> X { | ^ diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs index 703c2a3d98479..4ab5bc9acdeeb 100644 --- a/src/tools/clippy/tests/ui/no_effect.rs +++ b/src/tools/clippy/tests/ui/no_effect.rs @@ -221,3 +221,56 @@ fn main() { Cout << 142; -Cout; } + +fn issue14592() { + struct MyStruct { + _inner: MyInner, + } + struct MyInner {} + + impl Drop for MyInner { + fn drop(&mut self) { + println!("dropping"); + } + } + + let x = MyStruct { _inner: MyInner {} }; + + let closure = || { + // Do not lint: dropping the assignment or assigning to `_` would + // change the output. + let _x = x; + }; + + println!("1"); + closure(); + println!("2"); + + struct Innocuous { + a: i32, + } + + // Do not lint: one of the fields has a side effect. + let x = MyInner {}; + let closure = || { + let _x = Innocuous { + a: { + x; + 10 + }, + }; + }; + + // Do not lint: the base has a side effect. + let x = MyInner {}; + let closure = || { + let _x = Innocuous { + ..Innocuous { + a: { + x; + 10 + }, + } + }; + }; +} diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs index 0d09b3ceecde7..f4248ffc0f4d0 100644 --- a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs +++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs @@ -43,7 +43,7 @@ extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {} extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {} -extern "C" { +unsafe extern "C" { fn c_abi_in_block(arg_one: u32, arg_two: usize); } diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed index 8774c666db11f..23dbee5a08488 100644 --- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed +++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed @@ -162,3 +162,36 @@ impl PartialOrd for I { return Some(self.cmp(other)); } } + +// #13640, do not lint + +#[derive(Eq, PartialEq)] +struct J(u32); + +impl Ord for J { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for J { + fn partial_cmp(&self, other: &Self) -> Option { + self.cmp(other).into() + } +} + +// #13640, check that a simple `.into()` does not obliterate the lint + +#[derive(Eq, PartialEq)] +struct K(u32); + +impl Ord for K { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for K { + //~^ non_canonical_partial_ord_impl + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } +} diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs index 568b97c8fff7b..12f055a542b89 100644 --- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs +++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs @@ -166,3 +166,38 @@ impl PartialOrd for I { return Some(self.cmp(other)); } } + +// #13640, do not lint + +#[derive(Eq, PartialEq)] +struct J(u32); + +impl Ord for J { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for J { + fn partial_cmp(&self, other: &Self) -> Option { + self.cmp(other).into() + } +} + +// #13640, check that a simple `.into()` does not obliterate the lint + +#[derive(Eq, PartialEq)] +struct K(u32); + +impl Ord for K { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } +} + +impl PartialOrd for K { + //~^ non_canonical_partial_ord_impl + fn partial_cmp(&self, other: &Self) -> Option { + Ordering::Greater.into() + } +} diff --git a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr index 86845df4ea906..c7de968588f8b 100644 --- a/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr +++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr @@ -31,5 +31,18 @@ LL - fn partial_cmp(&self, _: &Self) -> Option { LL + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } | -error: aborting due to 2 previous errors +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> tests/ui/non_canonical_partial_ord_impl.rs:198:1 + | +LL | / impl PartialOrd for K { +LL | | +LL | | fn partial_cmp(&self, other: &Self) -> Option { + | | _____________________________________________________________- +LL | || Ordering::Greater.into() +LL | || } + | ||_____- help: change this to: `{ Some(self.cmp(other)) }` +LL | | } + | |__^ + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/non_expressive_names.rs b/src/tools/clippy/tests/ui/non_expressive_names.rs index b772c754f8b76..3f34dff563d26 100644 --- a/src/tools/clippy/tests/ui/non_expressive_names.rs +++ b/src/tools/clippy/tests/ui/non_expressive_names.rs @@ -1,5 +1,4 @@ -#![warn(clippy::all)] -#![allow(unused, clippy::println_empty_string, non_snake_case, clippy::let_unit_value)] +#![allow(clippy::println_empty_string, non_snake_case, clippy::let_unit_value)] #[derive(Clone, Debug)] enum MaybeInst { diff --git a/src/tools/clippy/tests/ui/non_expressive_names.stderr b/src/tools/clippy/tests/ui/non_expressive_names.stderr index 3bd77a730fe78..11b12d2c5f103 100644 --- a/src/tools/clippy/tests/ui/non_expressive_names.stderr +++ b/src/tools/clippy/tests/ui/non_expressive_names.stderr @@ -1,5 +1,5 @@ error: consider choosing a more descriptive name - --> tests/ui/non_expressive_names.rs:28:9 + --> tests/ui/non_expressive_names.rs:27:9 | LL | let _1 = 1; | ^^ @@ -8,31 +8,31 @@ LL | let _1 = 1; = help: to override `-D warnings` add `#[allow(clippy::just_underscores_and_digits)]` error: consider choosing a more descriptive name - --> tests/ui/non_expressive_names.rs:30:9 + --> tests/ui/non_expressive_names.rs:29:9 | LL | let ____1 = 1; | ^^^^^ error: consider choosing a more descriptive name - --> tests/ui/non_expressive_names.rs:32:9 + --> tests/ui/non_expressive_names.rs:31:9 | LL | let __1___2 = 12; | ^^^^^^^ error: consider choosing a more descriptive name - --> tests/ui/non_expressive_names.rs:54:13 + --> tests/ui/non_expressive_names.rs:53:13 | LL | let _1 = 1; | ^^ error: consider choosing a more descriptive name - --> tests/ui/non_expressive_names.rs:56:13 + --> tests/ui/non_expressive_names.rs:55:13 | LL | let ____1 = 1; | ^^^^^ error: consider choosing a more descriptive name - --> tests/ui/non_expressive_names.rs:58:13 + --> tests/ui/non_expressive_names.rs:57:13 | LL | let __1___2 = 12; | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs index 046ea70b08f16..31778f7450989 100644 --- a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs +++ b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs @@ -35,7 +35,7 @@ unsafe impl Send for ArcGuard {} //~^ ERROR: some fields in `ArcGuard` are not safe to be sent to another thread // rusb / RUSTSEC-2020-0098 -extern "C" { +unsafe extern "C" { type libusb_device_handle; } @@ -90,7 +90,7 @@ unsafe impl Send for MultiParam {} //~^ ERROR: some fields in `MultiParam` are not safe to be sent to another thread // Tests for raw pointer heuristic -extern "C" { +unsafe extern "C" { type NonSend; } diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed index f7c56b6fffe81..d6c35d8097c51 100644 --- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed @@ -9,16 +9,16 @@ use once_cell::sync::Lazy; fn main() {} static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); -//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +//~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| { - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library let x = "bar"; x.to_uppercase() }); static LAZY_BAZ: std::sync::LazyLock = { std::sync::LazyLock::new(|| "baz".to_uppercase()) }; -//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +//~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_QUX: std::sync::LazyLock = { - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library if "qux".len() == 3 { std::sync::LazyLock::new(|| "qux".to_uppercase()) } else if "qux".is_ascii() { @@ -39,11 +39,11 @@ mod once_cell_lazy_with_fns { use once_cell::sync::Lazy; static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static mut LAZY_BAZ: std::sync::LazyLock = std::sync::LazyLock::new(|| "baz".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library fn calling_replaceable_fns() { let _ = std::sync::LazyLock::force(&LAZY_FOO); @@ -70,3 +70,10 @@ mod external_macros { once_cell::external!(); lazy_static::external!(); } + +mod issue14729 { + use once_cell::sync::Lazy; + + #[expect(clippy::non_std_lazy_statics)] + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs index 90bc428137cea..996ef050d6915 100644 --- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs @@ -9,16 +9,16 @@ use once_cell::sync::Lazy; fn main() {} static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); -//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +//~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_BAR: Lazy = Lazy::new(|| { - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library let x = "bar"; x.to_uppercase() }); static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; -//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +//~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_QUX: Lazy = { - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library if "qux".len() == 3 { Lazy::new(|| "qux".to_uppercase()) } else if "qux".is_ascii() { @@ -39,11 +39,11 @@ mod once_cell_lazy_with_fns { use once_cell::sync::Lazy; static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library fn calling_replaceable_fns() { let _ = Lazy::force(&LAZY_FOO); @@ -70,3 +70,10 @@ mod external_macros { once_cell::external!(); lazy_static::external!(); } + +mod issue14729 { + use once_cell::sync::Lazy; + + #[expect(clippy::non_std_lazy_statics)] + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr index 333052ae1c110..bb80cd11c7199 100644 --- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr @@ -1,4 +1,4 @@ -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:11:18 | LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); @@ -12,7 +12,7 @@ LL - static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); LL + static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:13:18 | LL | static LAZY_BAR: Lazy = Lazy::new(|| { @@ -24,7 +24,7 @@ LL - static LAZY_BAR: Lazy = Lazy::new(|| { LL + static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| { | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:18:18 | LL | static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; @@ -36,7 +36,7 @@ LL - static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; LL + static LAZY_BAZ: std::sync::LazyLock = { std::sync::LazyLock::new(|| "baz".to_uppercase()) }; | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:20:18 | LL | static LAZY_QUX: Lazy = { @@ -54,7 +54,7 @@ LL | } else { LL ~ std::sync::LazyLock::new(|| "qux".to_string()) | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:41:22 | LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); @@ -69,7 +69,7 @@ LL | fn calling_replaceable_fns() { LL ~ let _ = std::sync::LazyLock::force(&LAZY_FOO); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:43:22 | LL | static LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); @@ -84,7 +84,7 @@ LL | let _ = Lazy::force(&LAZY_FOO); LL ~ let _ = std::sync::LazyLock::force(&LAZY_BAR); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:45:26 | LL | static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs index 34f8dd1ccb2ea..acc8c04678f50 100644 --- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs @@ -9,11 +9,11 @@ mod once_cell_lazy { use once_cell::sync::Lazy; static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); - //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + //~^ ERROR: this type has been superseded by `LazyLock` in the standard library fn calling_irreplaceable_fns() { let _ = Lazy::get(&LAZY_FOO); @@ -31,13 +31,13 @@ mod lazy_static_lazy_static { lazy_static! { static ref LAZY_FOO: String = "foo".to_uppercase(); } - //~^^^ ERROR: this macro has been superceded by `std::sync::LazyLock` + //~^^^ ERROR: this macro has been superseded by `std::sync::LazyLock` lazy_static! { static ref LAZY_BAR: String = "bar".to_uppercase(); static ref LAZY_BAZ: String = "baz".to_uppercase(); } - //~^^^^ ERROR: this macro has been superceded by `std::sync::LazyLock` - //~| ERROR: this macro has been superceded by `std::sync::LazyLock` + //~^^^^ ERROR: this macro has been superseded by `std::sync::LazyLock` + //~| ERROR: this macro has been superseded by `std::sync::LazyLock` } fn main() {} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr index 216190ae4ca31..2c35cad6237ab 100644 --- a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr @@ -1,4 +1,4 @@ -error: this macro has been superceded by `std::sync::LazyLock` +error: this macro has been superseded by `std::sync::LazyLock` --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:31:5 | LL | / lazy_static! { @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::non-std-lazy-statics` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::non_std_lazy_statics)]` -error: this macro has been superceded by `std::sync::LazyLock` +error: this macro has been superseded by `std::sync::LazyLock` --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:35:5 | LL | / lazy_static! { @@ -18,7 +18,7 @@ LL | | static ref LAZY_BAZ: String = "baz".to_uppercase(); LL | | } | |_____^ -error: this macro has been superceded by `std::sync::LazyLock` +error: this macro has been superseded by `std::sync::LazyLock` --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:35:5 | LL | / lazy_static! { @@ -29,7 +29,7 @@ LL | | } | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:11:22 | LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); @@ -41,7 +41,7 @@ LL - static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); LL + static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:13:26 | LL | static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); @@ -53,7 +53,7 @@ LL - static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); LL + static mut LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); | -error: this type has been superceded by `LazyLock` in the standard library +error: this type has been superseded by `LazyLock` in the standard library --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:15:26 | LL | static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs index a155ff3508be0..1eecc3dee3dc5 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool.rs +++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs @@ -216,3 +216,23 @@ fn issue14184(a: f32, b: bool) { println!("Hi"); } } + +mod issue14404 { + enum TyKind { + Ref(i32, i32, i32), + Other, + } + + struct Expr; + + fn is_mutable(expr: &Expr) -> bool { + todo!() + } + + fn should_not_give_macro(ty: TyKind, expr: Expr) { + if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) { + //~^ nonminimal_bool + todo!() + } + } +} diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr index 336cce40abf0d..0e3e4cf7988e2 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool.stderr +++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr @@ -227,7 +227,13 @@ error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:214:8 | LL | if !(a < 2.0 && !b) { - | ^^^^^^^^^^^^^^^^ help: try: `!(a < 2.0) || b` + | ^^^^^^^^^^^^^^^^ help: try: `a >= 2.0 || b` -error: aborting due to 30 previous errors +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool.rs:233:12 + | +LL | if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!matches!(ty, TyKind::Ref(_, _, _)) || is_mutable(&expr)` + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.fixed b/src/tools/clippy/tests/ui/obfuscated_if_else.fixed index 66f5070787b09..70ae090626b96 100644 --- a/src/tools/clippy/tests/ui/obfuscated_if_else.fixed +++ b/src/tools/clippy/tests/ui/obfuscated_if_else.fixed @@ -46,6 +46,18 @@ fn main() { let partial = true.then_some(1); partial.unwrap_or_else(|| n * 2); // not lint + + if true { () } else { Default::default() }; + //~^ obfuscated_if_else + + if true { () } else { Default::default() }; + //~^ obfuscated_if_else + + if true { 1 } else { Default::default() }; + //~^ obfuscated_if_else + + if true { 1 } else { Default::default() }; + //~^ obfuscated_if_else } fn issue11141() { diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.rs b/src/tools/clippy/tests/ui/obfuscated_if_else.rs index 4efd740eb60bb..8e1f57ca2c026 100644 --- a/src/tools/clippy/tests/ui/obfuscated_if_else.rs +++ b/src/tools/clippy/tests/ui/obfuscated_if_else.rs @@ -46,6 +46,18 @@ fn main() { let partial = true.then_some(1); partial.unwrap_or_else(|| n * 2); // not lint + + true.then_some(()).unwrap_or_default(); + //~^ obfuscated_if_else + + true.then(|| ()).unwrap_or_default(); + //~^ obfuscated_if_else + + true.then_some(1).unwrap_or_default(); + //~^ obfuscated_if_else + + true.then(|| 1).unwrap_or_default(); + //~^ obfuscated_if_else } fn issue11141() { diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.stderr b/src/tools/clippy/tests/ui/obfuscated_if_else.stderr index d676c25669570..0de7259d8bb82 100644 --- a/src/tools/clippy/tests/ui/obfuscated_if_else.stderr +++ b/src/tools/clippy/tests/ui/obfuscated_if_else.stderr @@ -68,52 +68,76 @@ LL | true.then_some(1).unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 1 } else { Default::default() }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:53:13 + --> tests/ui/obfuscated_if_else.rs:50:5 + | +LL | true.then_some(()).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { () } else { Default::default() }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:53:5 + | +LL | true.then(|| ()).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { () } else { Default::default() }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:56:5 + | +LL | true.then_some(1).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 1 } else { Default::default() }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:59:5 + | +LL | true.then(|| 1).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 1 } else { Default::default() }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:65:13 | LL | let _ = true.then_some(40).unwrap_or(17) | 2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(if true { 40 } else { 17 })` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:57:13 + --> tests/ui/obfuscated_if_else.rs:69:13 | LL | let _ = true.then_some(30).unwrap_or(17) | true.then_some(2).unwrap_or(3) | true.then_some(10).unwrap_or(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(if true { 30 } else { 17 })` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:57:48 + --> tests/ui/obfuscated_if_else.rs:69:48 | LL | let _ = true.then_some(30).unwrap_or(17) | true.then_some(2).unwrap_or(3) | true.then_some(10).unwrap_or(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 2 } else { 3 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:57:81 + --> tests/ui/obfuscated_if_else.rs:69:81 | LL | let _ = true.then_some(30).unwrap_or(17) | true.then_some(2).unwrap_or(3) | true.then_some(10).unwrap_or(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 10 } else { 1 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:63:17 + --> tests/ui/obfuscated_if_else.rs:75:17 | LL | let _ = 2 | true.then_some(40).unwrap_or(17); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 40 } else { 17 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:67:13 + --> tests/ui/obfuscated_if_else.rs:79:13 | LL | let _ = true.then_some(42).unwrap_or(17) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { 42 } else { 17 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:71:14 + --> tests/ui/obfuscated_if_else.rs:83:14 | LL | let _ = *true.then_some(&42).unwrap_or(&17); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { &42 } else { &17 }` error: this method chain can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:75:14 + --> tests/ui/obfuscated_if_else.rs:87:14 | LL | let _ = *true.then_some(&42).unwrap_or(&17) as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { &42 } else { &17 }` -error: aborting due to 19 previous errors +error: aborting due to 23 previous errors diff --git a/src/tools/clippy/tests/ui/op_ref.fixed b/src/tools/clippy/tests/ui/op_ref.fixed index 46a59e419cce0..f412190b9fd9e 100644 --- a/src/tools/clippy/tests/ui/op_ref.fixed +++ b/src/tools/clippy/tests/ui/op_ref.fixed @@ -98,3 +98,15 @@ impl Mul for A { self * &rhs } } + +mod issue_2597 { + fn ex1() { + let a: &str = "abc"; + let b: String = "abc".to_owned(); + println!("{}", a > &b); + } + + pub fn ex2(array: &[T], val: &T, idx: usize) -> bool { + &array[idx] < val + } +} diff --git a/src/tools/clippy/tests/ui/op_ref.rs b/src/tools/clippy/tests/ui/op_ref.rs index e10840ff4b97b..a4bbd86c7e95b 100644 --- a/src/tools/clippy/tests/ui/op_ref.rs +++ b/src/tools/clippy/tests/ui/op_ref.rs @@ -98,3 +98,15 @@ impl Mul for A { self * &rhs } } + +mod issue_2597 { + fn ex1() { + let a: &str = "abc"; + let b: String = "abc".to_owned(); + println!("{}", a > &b); + } + + pub fn ex2(array: &[T], val: &T, idx: usize) -> bool { + &array[idx] < val + } +} diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed index f5a869cf28318..fe3ac9e8f92c3 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.fixed +++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed @@ -268,3 +268,37 @@ fn issue11893() { panic!("Haven't thought about this condition."); } } + +mod issue13964 { + #[derive(Debug)] + struct A(Option); + + fn foo(a: A) { + let _ = match a.0 { + Some(x) => x, + None => panic!("{a:?} is invalid."), + }; + } + + fn bar(a: A) { + let _ = if let Some(x) = a.0 { + x + } else { + panic!("{a:?} is invalid.") + }; + } +} + +mod issue11059 { + use std::fmt::Debug; + + fn box_coercion_unsize(o: Option) -> Box { + if let Some(o) = o { Box::new(o) } else { Box::new("foo") } + } + + static S: String = String::new(); + + fn deref_with_overload(o: Option<&str>) -> &str { + if let Some(o) = o { o } else { &S } + } +} diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs index d48272e618acc..5b7498bc8e23b 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.rs +++ b/src/tools/clippy/tests/ui/option_if_let_else.rs @@ -331,3 +331,37 @@ fn issue11893() { panic!("Haven't thought about this condition."); } } + +mod issue13964 { + #[derive(Debug)] + struct A(Option); + + fn foo(a: A) { + let _ = match a.0 { + Some(x) => x, + None => panic!("{a:?} is invalid."), + }; + } + + fn bar(a: A) { + let _ = if let Some(x) = a.0 { + x + } else { + panic!("{a:?} is invalid.") + }; + } +} + +mod issue11059 { + use std::fmt::Debug; + + fn box_coercion_unsize(o: Option) -> Box { + if let Some(o) = o { Box::new(o) } else { Box::new("foo") } + } + + static S: String = String::new(); + + fn deref_with_overload(o: Option<&str>) -> &str { + if let Some(o) = o { o } else { &S } + } +} diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index 1794ac57fe5b1..a1119d75c231b 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -179,16 +179,20 @@ fn f() -> Option<()> { mod issue6675 { unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T { - #[allow(unused)] - let x = vec![0; 1000]; // future-proofing, make this function expensive. - &*p + unsafe { + #[allow(unused)] + let x = vec![0; 1000]; // future-proofing, make this function expensive. + &*p + } } unsafe fn foo() { - let s = "test".to_owned(); - let s = &s as *const _; - None.unwrap_or_else(|| ptr_to_ref(s)); - //~^ or_fun_call + unsafe { + let s = "test".to_owned(); + let s = &s as *const _; + None.unwrap_or_else(|| ptr_to_ref(s)); + //~^ or_fun_call + } } fn bar() { diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index 256db343c0573..a7cd632bf166f 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -179,16 +179,20 @@ fn f() -> Option<()> { mod issue6675 { unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T { - #[allow(unused)] - let x = vec![0; 1000]; // future-proofing, make this function expensive. - &*p + unsafe { + #[allow(unused)] + let x = vec![0; 1000]; // future-proofing, make this function expensive. + &*p + } } unsafe fn foo() { - let s = "test".to_owned(); - let s = &s as *const _; - None.unwrap_or(ptr_to_ref(s)); - //~^ or_fun_call + unsafe { + let s = "test".to_owned(); + let s = &s as *const _; + None.unwrap_or(ptr_to_ref(s)); + //~^ or_fun_call + } } fn bar() { diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index 93c87b2f12cde..35bda7e4d3314 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -125,91 +125,91 @@ LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:190:14 + --> tests/ui/or_fun_call.rs:193:18 | -LL | None.unwrap_or(ptr_to_ref(s)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` +LL | None.unwrap_or(ptr_to_ref(s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:197:14 + --> tests/ui/or_fun_call.rs:201:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:200:14 + --> tests/ui/or_fun_call.rs:204:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:276:25 + --> tests/ui/or_fun_call.rs:280:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:278:25 + --> tests/ui/or_fun_call.rs:282:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:310:18 + --> tests/ui/or_fun_call.rs:314:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:314:28 + --> tests/ui/or_fun_call.rs:318:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:318:27 + --> tests/ui/or_fun_call.rs:322:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:322:22 + --> tests/ui/or_fun_call.rs:326:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:326:23 + --> tests/ui/or_fun_call.rs:330:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:330:25 + --> tests/ui/or_fun_call.rs:334:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:334:25 + --> tests/ui/or_fun_call.rs:338:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:376:17 + --> tests/ui/or_fun_call.rs:380:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:381:17 + --> tests/ui/or_fun_call.rs:385:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:386:17 + --> tests/ui/or_fun_call.rs:390:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -229,19 +229,19 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:392:17 + --> tests/ui/or_fun_call.rs:396:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:397:17 + --> tests/ui/or_fun_call.rs:401:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:404:21 + --> tests/ui/or_fun_call.rs:408:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs index bdac3764bf167..643d8fedda984 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs @@ -1,5 +1,5 @@ -#![allow(clippy::all)] #![warn(clippy::pattern_type_mismatch)] +#![allow(clippy::single_match)] fn main() {} diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs index 3c789f570b038..a1c447d258346 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs @@ -1,4 +1,3 @@ -#![allow(clippy::all)] #![warn(clippy::pattern_type_mismatch)] fn main() {} diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr index 763f688ea8975..b3ae63ec031a4 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr @@ -1,5 +1,5 @@ error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_alternatives.rs:15:12 + --> tests/ui/pattern_type_mismatch/pattern_alternatives.rs:14:12 | LL | if let Value::B | Value::A(_) = ref_value {} | ^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | if let Value::B | Value::A(_) = ref_value {} = help: to override `-D warnings` add `#[allow(clippy::pattern_type_mismatch)]` error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_alternatives.rs:18:34 + --> tests/ui/pattern_type_mismatch/pattern_alternatives.rs:17:34 | LL | if let &Value::B | &Value::A(Some(_)) = ref_value {} | ^^^^^^^ @@ -17,7 +17,7 @@ LL | if let &Value::B | &Value::A(Some(_)) = ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_alternatives.rs:21:32 + --> tests/ui/pattern_type_mismatch/pattern_alternatives.rs:20:32 | LL | if let Value::B | Value::A(Some(_)) = *ref_value {} | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs index 7fc53d591a917..c5e395c4084f2 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs @@ -1,4 +1,3 @@ -#![allow(clippy::all)] #![warn(clippy::pattern_type_mismatch)] fn main() {} diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr index 70f7bdc389061..e18a88c2bf510 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr @@ -1,5 +1,5 @@ error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:13:9 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:12:9 | LL | let Struct { .. } = ref_value; | ^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let Struct { .. } = ref_value; = help: to override `-D warnings` add `#[allow(clippy::pattern_type_mismatch)]` error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:16:33 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:15:33 | LL | if let &Struct { ref_inner: Some(_) } = ref_value {} | ^^^^^^^ @@ -17,7 +17,7 @@ LL | if let &Struct { ref_inner: Some(_) } = ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:19:32 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:18:32 | LL | if let Struct { ref_inner: Some(_) } = *ref_value {} | ^^^^^^^ @@ -25,7 +25,7 @@ LL | if let Struct { ref_inner: Some(_) } = *ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:37:12 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:36:12 | LL | if let StructEnum::Var { .. } = ref_value {} | ^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | if let StructEnum::Var { .. } = ref_value {} = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:40:12 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:39:12 | LL | if let StructEnum::Var { inner_ref: Some(_) } = ref_value {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | if let StructEnum::Var { inner_ref: Some(_) } = ref_value {} = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:43:42 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:42:42 | LL | if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {} | ^^^^^^^ @@ -49,7 +49,7 @@ LL | if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:46:41 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:45:41 | LL | if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {} | ^^^^^^^ @@ -57,7 +57,7 @@ LL | if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_structs.rs:49:12 + --> tests/ui/pattern_type_mismatch/pattern_structs.rs:48:12 | LL | if let StructEnum::Empty = ref_value {} | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs index ecd95d9ae2b3b..8bec5abc88f17 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs @@ -1,4 +1,3 @@ -#![allow(clippy::all)] #![warn(clippy::pattern_type_mismatch)] fn main() {} diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr index d47c5d509c3fa..ee307be63c1a5 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr @@ -1,5 +1,5 @@ error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:11:9 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:10:9 | LL | let TupleStruct(_) = ref_value; | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let TupleStruct(_) = ref_value; = help: to override `-D warnings` add `#[allow(clippy::pattern_type_mismatch)]` error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:14:25 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:13:25 | LL | if let &TupleStruct(Some(_)) = ref_value {} | ^^^^^^^ @@ -17,7 +17,7 @@ LL | if let &TupleStruct(Some(_)) = ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:17:24 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:16:24 | LL | if let TupleStruct(Some(_)) = *ref_value {} | ^^^^^^^ @@ -25,7 +25,7 @@ LL | if let TupleStruct(Some(_)) = *ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:35:12 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:34:12 | LL | if let TupleEnum::Var(_) = ref_value {} | ^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | if let TupleEnum::Var(_) = ref_value {} = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:38:28 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:37:28 | LL | if let &TupleEnum::Var(Some(_)) = ref_value {} | ^^^^^^^ @@ -41,7 +41,7 @@ LL | if let &TupleEnum::Var(Some(_)) = ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:41:27 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:40:27 | LL | if let TupleEnum::Var(Some(_)) = *ref_value {} | ^^^^^^^ @@ -49,7 +49,7 @@ LL | if let TupleEnum::Var(Some(_)) = *ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:44:12 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:43:12 | LL | if let TupleEnum::Empty = ref_value {} | ^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | if let TupleEnum::Empty = ref_value {} = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:60:9 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:59:9 | LL | let (_a, _b) = ref_value; | ^^^^^^^^ @@ -65,7 +65,7 @@ LL | let (_a, _b) = ref_value; = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:63:18 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:62:18 | LL | if let &(_a, Some(_)) = ref_value {} | ^^^^^^^ @@ -73,7 +73,7 @@ LL | if let &(_a, Some(_)) = ref_value {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:66:17 + --> tests/ui/pattern_type_mismatch/pattern_tuples.rs:65:17 | LL | if let (_a, Some(_)) = *ref_value {} | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs index 0bbc26a0c27c5..49ea1d3f7a67c 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs @@ -1,5 +1,10 @@ -#![allow(clippy::all)] #![warn(clippy::pattern_type_mismatch)] +#![allow( + clippy::match_ref_pats, + clippy::never_loop, + clippy::redundant_pattern_matching, + clippy::single_match +)] fn main() {} diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr index 3f6b5feb9b07b..cd604d604c12c 100644 --- a/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr +++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr @@ -1,5 +1,5 @@ error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:11:9 + --> tests/ui/pattern_type_mismatch/syntax.rs:16:9 | LL | Some(_) => (), | ^^^^^^^ @@ -9,7 +9,7 @@ LL | Some(_) => (), = help: to override `-D warnings` add `#[allow(clippy::pattern_type_mismatch)]` error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:31:12 + --> tests/ui/pattern_type_mismatch/syntax.rs:36:12 | LL | if let Some(_) = ref_value {} | ^^^^^^^ @@ -17,7 +17,7 @@ LL | if let Some(_) = ref_value {} = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:43:15 + --> tests/ui/pattern_type_mismatch/syntax.rs:48:15 | LL | while let Some(_) = ref_value { | ^^^^^^^ @@ -25,7 +25,7 @@ LL | while let Some(_) = ref_value { = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:63:9 + --> tests/ui/pattern_type_mismatch/syntax.rs:68:9 | LL | for (_a, _b) in slice.iter() {} | ^^^^^^^^ @@ -33,7 +33,7 @@ LL | for (_a, _b) in slice.iter() {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:74:9 + --> tests/ui/pattern_type_mismatch/syntax.rs:79:9 | LL | let (_n, _m) = ref_value; | ^^^^^^^^ @@ -41,7 +41,7 @@ LL | let (_n, _m) = ref_value; = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:84:12 + --> tests/ui/pattern_type_mismatch/syntax.rs:89:12 | LL | fn foo((_a, _b): &(i32, i32)) {} | ^^^^^^^^ @@ -49,7 +49,7 @@ LL | fn foo((_a, _b): &(i32, i32)) {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:99:10 + --> tests/ui/pattern_type_mismatch/syntax.rs:104:10 | LL | foo(|(_a, _b)| ()); | ^^^^^^^^ @@ -57,7 +57,7 @@ LL | foo(|(_a, _b)| ()); = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:116:9 + --> tests/ui/pattern_type_mismatch/syntax.rs:121:9 | LL | Some(_) => (), | ^^^^^^^ @@ -65,7 +65,7 @@ LL | Some(_) => (), = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:137:17 + --> tests/ui/pattern_type_mismatch/syntax.rs:142:17 | LL | Some(_) => (), | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/patterns.fixed b/src/tools/clippy/tests/ui/patterns.fixed index bcb8ecfc38d25..a6dd5fd63a9f6 100644 --- a/src/tools/clippy/tests/ui/patterns.fixed +++ b/src/tools/clippy/tests/ui/patterns.fixed @@ -1,6 +1,4 @@ //@aux-build:proc_macros.rs -#![warn(clippy::all)] -#![allow(unused)] #![allow(clippy::uninlined_format_args, clippy::single_match)] #[macro_use] diff --git a/src/tools/clippy/tests/ui/patterns.rs b/src/tools/clippy/tests/ui/patterns.rs index 19639ebd13d60..64bfbdecdac2b 100644 --- a/src/tools/clippy/tests/ui/patterns.rs +++ b/src/tools/clippy/tests/ui/patterns.rs @@ -1,6 +1,4 @@ //@aux-build:proc_macros.rs -#![warn(clippy::all)] -#![allow(unused)] #![allow(clippy::uninlined_format_args, clippy::single_match)] #[macro_use] diff --git a/src/tools/clippy/tests/ui/patterns.stderr b/src/tools/clippy/tests/ui/patterns.stderr index b9950fe181cc7..ff5e1a8de90a4 100644 --- a/src/tools/clippy/tests/ui/patterns.stderr +++ b/src/tools/clippy/tests/ui/patterns.stderr @@ -1,5 +1,5 @@ error: the `y @ _` pattern can be written as just `y` - --> tests/ui/patterns.rs:14:9 + --> tests/ui/patterns.rs:12:9 | LL | y @ _ => (), | ^^^^^ help: try: `y` @@ -8,13 +8,13 @@ LL | y @ _ => (), = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern)]` error: the `x @ _` pattern can be written as just `x` - --> tests/ui/patterns.rs:30:9 + --> tests/ui/patterns.rs:28:9 | LL | ref mut x @ _ => { | ^^^^^^^^^^^^^ help: try: `ref mut x` error: the `x @ _` pattern can be written as just `x` - --> tests/ui/patterns.rs:39:9 + --> tests/ui/patterns.rs:37:9 | LL | ref x @ _ => println!("vec: {:?}", x), | ^^^^^^^^^ help: try: `ref x` diff --git a/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs index 171716be26024..7f69c61b0289c 100644 --- a/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs +++ b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs @@ -6,29 +6,37 @@ use core::arch::asm; unsafe fn nomem_bad(p: &i32) { - asm!( - "asdf {p1}, {p2}, {p3}", - p1 = in(reg) p, - //~^ pointers_in_nomem_asm_block + unsafe { + asm!( + "asdf {p1}, {p2}, {p3}", + p1 = in(reg) p, + //~^ pointers_in_nomem_asm_block - p2 = in(reg) p as *const _ as usize, - p3 = in(reg) p, - options(nomem, nostack, preserves_flags) - ); + p2 = in(reg) p as *const _ as usize, + p3 = in(reg) p, + options(nomem, nostack, preserves_flags) + ); + } } unsafe fn nomem_good(p: &i32) { - asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); - let p = p as *const i32 as usize; - asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + unsafe { + asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); + let p = p as *const i32 as usize; + asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + } } unsafe fn nomem_bad2(p: &mut i32) { - asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); - //~^ pointers_in_nomem_asm_block + unsafe { + asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + //~^ pointers_in_nomem_asm_block + } } unsafe fn nomem_fn(p: extern "C" fn()) { - asm!("call {p}", p = in(reg) p, options(nomem)); - //~^ pointers_in_nomem_asm_block + unsafe { + asm!("call {p}", p = in(reg) p, options(nomem)); + //~^ pointers_in_nomem_asm_block + } } diff --git a/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr index ca24e34f63c0f..eabac2444eccc 100644 --- a/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr +++ b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr @@ -1,11 +1,11 @@ error: passing pointers to nomem asm block - --> tests/ui/pointers_in_nomem_asm_block.rs:11:9 + --> tests/ui/pointers_in_nomem_asm_block.rs:12:13 | -LL | p1 = in(reg) p, - | ^^^^^^^^^^^^^^ +LL | p1 = in(reg) p, + | ^^^^^^^^^^^^^^ ... -LL | p3 = in(reg) p, - | ^^^^^^^^^^^^^^ +LL | p3 = in(reg) p, + | ^^^^^^^^^^^^^^ | = note: `nomem` means that no memory write or read happens inside the asm! block = note: if this is intentional and no pointers are read or written to, consider allowing the lint @@ -13,19 +13,19 @@ LL | p3 = in(reg) p, = help: to override `-D warnings` add `#[allow(clippy::pointers_in_nomem_asm_block)]` error: passing pointers to nomem asm block - --> tests/ui/pointers_in_nomem_asm_block.rs:27:22 + --> tests/ui/pointers_in_nomem_asm_block.rs:32:26 | -LL | asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); - | ^^^^^^^^^^^^^ +LL | asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + | ^^^^^^^^^^^^^ | = note: `nomem` means that no memory write or read happens inside the asm! block = note: if this is intentional and no pointers are read or written to, consider allowing the lint error: passing pointers to nomem asm block - --> tests/ui/pointers_in_nomem_asm_block.rs:32:22 + --> tests/ui/pointers_in_nomem_asm_block.rs:39:26 | -LL | asm!("call {p}", p = in(reg) p, options(nomem)); - | ^^^^^^^^^^^^^ +LL | asm!("call {p}", p = in(reg) p, options(nomem)); + | ^^^^^^^^^^^^^ | = note: `nomem` means that no memory write or read happens inside the asm! block = note: if this is intentional and no pointers are read or written to, consider allowing the lint diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed index 6dded72d3e191..79bfae1f7ebb4 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed @@ -12,11 +12,13 @@ extern crate proc_macros; use proc_macros::{external, inline_macros}; unsafe fn ptr_to_ref(p: *const T, om: *mut U) { - let _: &mut T = std::mem::transmute(p.cast_mut()); - //~^ ptr_cast_constness - let _ = &mut *p.cast_mut(); - //~^ ptr_cast_constness - let _: &T = &*(om as *const T); + unsafe { + let _: &mut T = std::mem::transmute(p.cast_mut()); + //~^ ptr_cast_constness + let _ = &mut *p.cast_mut(); + //~^ ptr_cast_constness + let _: &T = &*(om as *const T); + } } #[inline_macros] @@ -98,3 +100,9 @@ fn null_pointers() { let _ = external!(ptr::null::() as *mut u32); let _ = external!(ptr::null::().cast_mut()); } + +fn issue14621() { + let mut local = 4; + let _ = std::ptr::addr_of_mut!(local).cast_const(); + //~^ ptr_cast_constness +} diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.rs b/src/tools/clippy/tests/ui/ptr_cast_constness.rs index e9629f5290ec1..f6590dabd5b84 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.rs +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.rs @@ -12,11 +12,13 @@ extern crate proc_macros; use proc_macros::{external, inline_macros}; unsafe fn ptr_to_ref(p: *const T, om: *mut U) { - let _: &mut T = std::mem::transmute(p as *mut T); - //~^ ptr_cast_constness - let _ = &mut *(p as *mut T); - //~^ ptr_cast_constness - let _: &T = &*(om as *const T); + unsafe { + let _: &mut T = std::mem::transmute(p as *mut T); + //~^ ptr_cast_constness + let _ = &mut *(p as *mut T); + //~^ ptr_cast_constness + let _: &T = &*(om as *const T); + } } #[inline_macros] @@ -98,3 +100,9 @@ fn null_pointers() { let _ = external!(ptr::null::() as *mut u32); let _ = external!(ptr::null::().cast_mut()); } + +fn issue14621() { + let mut local = 4; + let _ = std::ptr::addr_of_mut!(local) as *const _; + //~^ ptr_cast_constness +} diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr index 1eeeef7470138..0b1644168ff51 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr @@ -1,74 +1,74 @@ error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:15:41 + --> tests/ui/ptr_cast_constness.rs:16:45 | -LL | let _: &mut T = std::mem::transmute(p as *mut T); - | ^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` +LL | let _: &mut T = std::mem::transmute(p as *mut T); + | ^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` | = note: `-D clippy::ptr-cast-constness` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ptr_cast_constness)]` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:17:19 + --> tests/ui/ptr_cast_constness.rs:18:23 | -LL | let _ = &mut *(p as *mut T); - | ^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` +LL | let _ = &mut *(p as *mut T); + | ^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `p.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:33:17 + --> tests/ui/ptr_cast_constness.rs:35:17 | LL | let _ = *ptr_ptr as *mut u32; | ^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `(*ptr_ptr).cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:37:13 + --> tests/ui/ptr_cast_constness.rs:39:13 | LL | let _ = ptr as *mut u32; | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:39:13 + --> tests/ui/ptr_cast_constness.rs:41:13 | LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:73:13 + --> tests/ui/ptr_cast_constness.rs:75:13 | LL | let _ = ptr as *mut u32; | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` error: `as` casting between raw pointers while changing only its constness - --> tests/ui/ptr_cast_constness.rs:75:13 + --> tests/ui/ptr_cast_constness.rs:77:13 | LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` error: `as` casting to make a const null pointer into a mutable null pointer - --> tests/ui/ptr_cast_constness.rs:82:13 + --> tests/ui/ptr_cast_constness.rs:84:13 | LL | let _ = ptr::null::() as *mut String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` error: `as` casting to make a mutable null pointer into a const null pointer - --> tests/ui/ptr_cast_constness.rs:84:13 + --> tests/ui/ptr_cast_constness.rs:86:13 | LL | let _ = ptr::null_mut::() as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` error: changing constness of a null pointer - --> tests/ui/ptr_cast_constness.rs:86:13 + --> tests/ui/ptr_cast_constness.rs:88:13 | LL | let _ = ptr::null::().cast_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` error: changing constness of a null pointer - --> tests/ui/ptr_cast_constness.rs:88:13 + --> tests/ui/ptr_cast_constness.rs:90:13 | LL | let _ = ptr::null_mut::().cast_const(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` error: `as` casting to make a const null pointer into a mutable null pointer - --> tests/ui/ptr_cast_constness.rs:92:21 + --> tests/ui/ptr_cast_constness.rs:94:21 | LL | let _ = inline!(ptr::null::() as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` @@ -76,12 +76,18 @@ LL | let _ = inline!(ptr::null::() as *mut u32); = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) error: changing constness of a null pointer - --> tests/ui/ptr_cast_constness.rs:94:21 + --> tests/ui/ptr_cast_constness.rs:96:21 | LL | let _ = inline!(ptr::null::().cast_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` | = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 13 previous errors +error: `as` casting between raw pointers while changing only its constness + --> tests/ui/ptr_cast_constness.rs:106:13 + | +LL | let _ = std::ptr::addr_of_mut!(local) as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `std::ptr::addr_of_mut!(local).cast_const()` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_eq.fixed b/src/tools/clippy/tests/ui/ptr_eq.fixed index 1ccd2c2237dee..9629b3eea5870 100644 --- a/src/tools/clippy/tests/ui/ptr_eq.fixed +++ b/src/tools/clippy/tests/ui/ptr_eq.fixed @@ -4,6 +4,9 @@ macro_rules! mac { ($a:expr, $b:expr) => { $a as *const _ as usize == $b as *const _ as usize }; + (cast $a:expr) => { + $a as *const [i32; 3] + }; } macro_rules! another_mac { @@ -20,20 +23,45 @@ fn main() { //~^ ptr_eq let _ = std::ptr::eq(a, b); //~^ ptr_eq + + // Do not lint: the rhs conversion is needed let _ = a.as_ptr() == b as *const _; + + // Do not lint: we have two raw pointers already let _ = a.as_ptr() == b.as_ptr(); // Do not lint - let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; + // Do not lint: the rhs conversion is needed let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + + // Do not lint: we have two raw pointers already let _ = a.as_mut_ptr() == b.as_mut_ptr(); let _ = a == b; let _ = core::ptr::eq(a, b); + + let (x, y) = (&0u32, &mut 1u32); + let _ = std::ptr::eq(x, y); + //~^ ptr_eq + + let _ = !std::ptr::eq(x, y); + //~^ ptr_eq + + #[expect(clippy::eq_op)] + // Do not lint: casts are needed to not change type + let _issue14337 = main as *const () == main as *const (); + + // Do not peel the content of macros + let _ = std::ptr::eq(mac!(cast a), mac!(cast b)); + //~^ ptr_eq + + // Do not peel the content of macros + let _ = std::ptr::eq(mac!(cast a), mac!(cast b)); + //~^ ptr_eq } diff --git a/src/tools/clippy/tests/ui/ptr_eq.rs b/src/tools/clippy/tests/ui/ptr_eq.rs index 0bc58a57fa53d..2b741d8df4684 100644 --- a/src/tools/clippy/tests/ui/ptr_eq.rs +++ b/src/tools/clippy/tests/ui/ptr_eq.rs @@ -4,6 +4,9 @@ macro_rules! mac { ($a:expr, $b:expr) => { $a as *const _ as usize == $b as *const _ as usize }; + (cast $a:expr) => { + $a as *const [i32; 3] + }; } macro_rules! another_mac { @@ -20,20 +23,45 @@ fn main() { //~^ ptr_eq let _ = a as *const _ == b as *const _; //~^ ptr_eq + + // Do not lint: the rhs conversion is needed let _ = a.as_ptr() == b as *const _; + + // Do not lint: we have two raw pointers already let _ = a.as_ptr() == b.as_ptr(); // Do not lint - let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; + // Do not lint: the rhs conversion is needed let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + + // Do not lint: we have two raw pointers already let _ = a.as_mut_ptr() == b.as_mut_ptr(); let _ = a == b; let _ = core::ptr::eq(a, b); + + let (x, y) = (&0u32, &mut 1u32); + let _ = x as *const u32 == y as *mut u32 as *const u32; + //~^ ptr_eq + + let _ = x as *const u32 != y as *mut u32 as *const u32; + //~^ ptr_eq + + #[expect(clippy::eq_op)] + // Do not lint: casts are needed to not change type + let _issue14337 = main as *const () == main as *const (); + + // Do not peel the content of macros + let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; + //~^ ptr_eq + + // Do not peel the content of macros + let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; + //~^ ptr_eq } diff --git a/src/tools/clippy/tests/ui/ptr_eq.stderr b/src/tools/clippy/tests/ui/ptr_eq.stderr index 8e8b34f26ff77..e7340624b5950 100644 --- a/src/tools/clippy/tests/ui/ptr_eq.stderr +++ b/src/tools/clippy/tests/ui/ptr_eq.stderr @@ -1,5 +1,5 @@ error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:19:13 + --> tests/ui/ptr_eq.rs:22:13 | LL | let _ = a as *const _ as usize == b as *const _ as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` @@ -8,10 +8,34 @@ LL | let _ = a as *const _ as usize == b as *const _ as usize; = help: to override `-D warnings` add `#[allow(clippy::ptr_eq)]` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:21:13 + --> tests/ui/ptr_eq.rs:24:13 | LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` -error: aborting due to 2 previous errors +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:50:13 + | +LL | let _ = x as *const u32 == y as *mut u32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(x, y)` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:53:13 + | +LL | let _ = x as *const u32 != y as *mut u32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!std::ptr::eq(x, y)` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:61:13 + | +LL | let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(mac!(cast a), mac!(cast b))` + +error: use `std::ptr::eq` when comparing raw pointers + --> tests/ui/ptr_eq.rs:65:13 + | +LL | let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(mac!(cast a), mac!(cast b))` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_eq_no_std.fixed b/src/tools/clippy/tests/ui/ptr_eq_no_std.fixed index b3e82fae38f30..48cbad62e1a36 100644 --- a/src/tools/clippy/tests/ui/ptr_eq_no_std.fixed +++ b/src/tools/clippy/tests/ui/ptr_eq_no_std.fixed @@ -32,18 +32,24 @@ fn main() { //~^ ptr_eq let _ = core::ptr::eq(a, b); //~^ ptr_eq + + // Do not lint: the rhs conversion is needed let _ = a.as_ptr() == b as *const _; + + // Do not lint: we have two raw pointers already let _ = a.as_ptr() == b.as_ptr(); // Do not lint - let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; + // Do not lint: the rhs conversion is needed let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + + // Do not lint: we have two raw pointers already let _ = a.as_mut_ptr() == b.as_mut_ptr(); let _ = a == b; diff --git a/src/tools/clippy/tests/ui/ptr_eq_no_std.rs b/src/tools/clippy/tests/ui/ptr_eq_no_std.rs index ba78f5ee5f84f..3827178640eea 100644 --- a/src/tools/clippy/tests/ui/ptr_eq_no_std.rs +++ b/src/tools/clippy/tests/ui/ptr_eq_no_std.rs @@ -32,18 +32,24 @@ fn main() { //~^ ptr_eq let _ = a as *const _ == b as *const _; //~^ ptr_eq + + // Do not lint: the rhs conversion is needed let _ = a.as_ptr() == b as *const _; + + // Do not lint: we have two raw pointers already let _ = a.as_ptr() == b.as_ptr(); // Do not lint - let _ = mac!(a, b); let _ = another_mac!(a, b); let a = &mut [1, 2, 3]; let b = &mut [1, 2, 3]; + // Do not lint: the rhs conversion is needed let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + + // Do not lint: we have two raw pointers already let _ = a.as_mut_ptr() == b.as_mut_ptr(); let _ = a == b; diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed index 8dfef3202be9c..507bc2b29d862 100644 --- a/src/tools/clippy/tests/ui/question_mark.fixed +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -3,6 +3,8 @@ #![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] +use std::sync::MutexGuard; + fn some_func(a: Option) -> Option { a?; @@ -299,6 +301,11 @@ fn pattern() -> Result<(), PatternedError> { res } +fn expect_expr(a: Option) -> Option { + #[expect(clippy::needless_question_mark)] + Some(a?) +} + fn main() {} // `?` is not the same as `return None;` if inside of a try block @@ -375,3 +382,64 @@ fn issue12412(foo: &Foo, bar: &Bar) -> Option<()> { //~^^^ question_mark Some(()) } + +struct StructWithOptionString { + opt_x: Option, +} + +struct WrapperStructWithString(String); + +#[allow(clippy::disallowed_names)] +fn issue_13417(foo: &mut StructWithOptionString) -> Option { + let x = foo.opt_x.as_ref()?; + //~^^^ question_mark + let opt_y = Some(x.clone()); + std::mem::replace(&mut foo.opt_x, opt_y) +} + +#[allow(clippy::disallowed_names)] +fn issue_13417_mut(foo: &mut StructWithOptionString) -> Option { + let x = foo.opt_x.as_mut()?; + //~^^^ question_mark + let opt_y = Some(x.clone()); + std::mem::replace(&mut foo.opt_x, opt_y) +} + +#[allow(clippy::disallowed_names)] +#[allow(unused)] +fn issue_13417_weirder(foo: &mut StructWithOptionString, mut bar: Option) -> Option<()> { + let x @ y = foo.opt_x.as_ref()?; + //~^^^ question_mark + let x @ &WrapperStructWithString(_) = bar.as_ref()?; + //~^^^ question_mark + let x @ &mut WrapperStructWithString(_) = bar.as_mut()?; + //~^^^ question_mark + Some(()) +} + +#[clippy::msrv = "1.12"] +fn msrv_1_12(arg: Option) -> Option { + if arg.is_none() { + return None; + } + let val = match arg { + Some(val) => val, + None => return None, + }; + println!("{}", val); + Some(val) +} + +#[clippy::msrv = "1.13"] +fn msrv_1_13(arg: Option) -> Option { + arg?; + let val = arg?; + println!("{}", val); + Some(val) +} + +fn issue_14615(a: MutexGuard>) -> Option { + let a = (*a)?; + //~^^^ question_mark + Some(format!("{a}")) +} diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs index fffaa803f39c2..64b51b849ede0 100644 --- a/src/tools/clippy/tests/ui/question_mark.rs +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -3,6 +3,8 @@ #![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] +use std::sync::MutexGuard; + fn some_func(a: Option) -> Option { if a.is_none() { //~^ question_mark @@ -369,6 +371,11 @@ fn pattern() -> Result<(), PatternedError> { res } +fn expect_expr(a: Option) -> Option { + #[expect(clippy::needless_question_mark)] + Some(a?) +} + fn main() {} // `?` is not the same as `return None;` if inside of a try block @@ -452,3 +459,83 @@ fn issue12412(foo: &Foo, bar: &Bar) -> Option<()> { //~^^^ question_mark Some(()) } + +struct StructWithOptionString { + opt_x: Option, +} + +struct WrapperStructWithString(String); + +#[allow(clippy::disallowed_names)] +fn issue_13417(foo: &mut StructWithOptionString) -> Option { + let Some(ref x) = foo.opt_x else { + return None; + }; + //~^^^ question_mark + let opt_y = Some(x.clone()); + std::mem::replace(&mut foo.opt_x, opt_y) +} + +#[allow(clippy::disallowed_names)] +fn issue_13417_mut(foo: &mut StructWithOptionString) -> Option { + let Some(ref mut x) = foo.opt_x else { + return None; + }; + //~^^^ question_mark + let opt_y = Some(x.clone()); + std::mem::replace(&mut foo.opt_x, opt_y) +} + +#[allow(clippy::disallowed_names)] +#[allow(unused)] +fn issue_13417_weirder(foo: &mut StructWithOptionString, mut bar: Option) -> Option<()> { + let Some(ref x @ ref y) = foo.opt_x else { + return None; + }; + //~^^^ question_mark + let Some(ref x @ WrapperStructWithString(_)) = bar else { + return None; + }; + //~^^^ question_mark + let Some(ref mut x @ WrapperStructWithString(_)) = bar else { + return None; + }; + //~^^^ question_mark + Some(()) +} + +#[clippy::msrv = "1.12"] +fn msrv_1_12(arg: Option) -> Option { + if arg.is_none() { + return None; + } + let val = match arg { + Some(val) => val, + None => return None, + }; + println!("{}", val); + Some(val) +} + +#[clippy::msrv = "1.13"] +fn msrv_1_13(arg: Option) -> Option { + if arg.is_none() { + //~^ question_mark + return None; + } + let val = match arg { + //~^ question_mark + Some(val) => val, + None => return None, + }; + println!("{}", val); + Some(val) +} + +fn issue_14615(a: MutexGuard>) -> Option { + let Some(a) = *a else { + return None; + }; + //~^^^ question_mark + Some(format!("{a}")) +} diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr index c4db0fbc30229..d8ce4420aeeb6 100644 --- a/src/tools/clippy/tests/ui/question_mark.stderr +++ b/src/tools/clippy/tests/ui/question_mark.stderr @@ -1,5 +1,5 @@ error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:7:5 + --> tests/ui/question_mark.rs:9:5 | LL | / if a.is_none() { LL | | @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::question_mark)]` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:53:9 + --> tests/ui/question_mark.rs:55:9 | LL | / if (self.opt).is_none() { LL | | @@ -20,7 +20,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:58:9 + --> tests/ui/question_mark.rs:60:9 | LL | / if self.opt.is_none() { LL | | @@ -29,7 +29,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:63:17 + --> tests/ui/question_mark.rs:65:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -41,7 +41,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:70:17 + --> tests/ui/question_mark.rs:72:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -53,7 +53,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:88:9 + --> tests/ui/question_mark.rs:90:9 | LL | / if self.opt.is_none() { LL | | @@ -62,7 +62,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:97:9 + --> tests/ui/question_mark.rs:99:9 | LL | / if self.opt.is_none() { LL | | @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:106:9 + --> tests/ui/question_mark.rs:108:9 | LL | / if self.opt.is_none() { LL | | @@ -80,7 +80,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:114:26 + --> tests/ui/question_mark.rs:116:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -92,7 +92,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:125:17 + --> tests/ui/question_mark.rs:127:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:147:5 + --> tests/ui/question_mark.rs:149:5 | LL | / if f().is_none() { LL | | @@ -113,7 +113,7 @@ LL | | } | |_____^ help: replace it with: `f()?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:152:16 + --> tests/ui/question_mark.rs:154:16 | LL | let _val = match f() { | ________________^ @@ -124,7 +124,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:163:5 + --> tests/ui/question_mark.rs:165:5 | LL | / match f() { LL | | @@ -134,7 +134,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:169:5 + --> tests/ui/question_mark.rs:171:5 | LL | / match opt_none!() { LL | | @@ -144,13 +144,13 @@ LL | | }; | |_____^ help: try instead: `opt_none!()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:196:13 + --> tests/ui/question_mark.rs:198:13 | LL | let _ = if let Ok(x) = x { x } else { return x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:199:5 + --> tests/ui/question_mark.rs:201:5 | LL | / if x.is_err() { LL | | @@ -159,7 +159,7 @@ LL | | } | |_____^ help: replace it with: `x?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:204:16 + --> tests/ui/question_mark.rs:206:16 | LL | let _val = match func_returning_result() { | ________________^ @@ -170,7 +170,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:210:5 + --> tests/ui/question_mark.rs:212:5 | LL | / match func_returning_result() { LL | | @@ -180,7 +180,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:302:5 + --> tests/ui/question_mark.rs:304:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:310:5 + --> tests/ui/question_mark.rs:312:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -198,7 +198,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:388:13 + --> tests/ui/question_mark.rs:395:13 | LL | / if a.is_none() { LL | | @@ -208,12 +208,80 @@ LL | | } | |_____________^ help: replace it with: `a?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:449:5 + --> tests/ui/question_mark.rs:456:5 | LL | / let Some(v) = bar.foo.owned.clone() else { LL | | return None; LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` -error: aborting due to 22 previous errors +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:471:5 + | +LL | / let Some(ref x) = foo.opt_x else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:481:5 + | +LL | / let Some(ref mut x) = foo.opt_x else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:492:5 + | +LL | / let Some(ref x @ ref y) = foo.opt_x else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:496:5 + | +LL | / let Some(ref x @ WrapperStructWithString(_)) = bar else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:500:5 + | +LL | / let Some(ref mut x @ WrapperStructWithString(_)) = bar else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;` + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:522:5 + | +LL | / if arg.is_none() { +LL | | +LL | | return None; +LL | | } + | |_____^ help: replace it with: `arg?;` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:526:15 + | +LL | let val = match arg { + | _______________^ +LL | | +LL | | Some(val) => val, +LL | | None => return None, +LL | | }; + | |_____^ help: try instead: `arg?` + +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:536:5 + | +LL | / let Some(a) = *a else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let a = (*a)?;` + +error: aborting due to 30 previous errors diff --git a/src/tools/clippy/tests/ui/question_mark_used.stderr b/src/tools/clippy/tests/ui/question_mark_used.stderr index 53cb59c021667..82f0d32504077 100644 --- a/src/tools/clippy/tests/ui/question_mark_used.stderr +++ b/src/tools/clippy/tests/ui/question_mark_used.stderr @@ -1,4 +1,4 @@ -error: question mark operator was used +error: the `?` operator was used --> tests/ui/question_mark_used.rs:11:5 | LL | other_function()?; diff --git a/src/tools/clippy/tests/ui/redundant_allocation.rs b/src/tools/clippy/tests/ui/redundant_allocation.rs index 0562f7dcc7614..832f147c6ed53 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation.rs +++ b/src/tools/clippy/tests/ui/redundant_allocation.rs @@ -1,4 +1,3 @@ -#![warn(clippy::all)] #![allow(clippy::boxed_local, clippy::disallowed_names)] pub struct MyStruct; diff --git a/src/tools/clippy/tests/ui/redundant_allocation.stderr b/src/tools/clippy/tests/ui/redundant_allocation.stderr index 44d30f95d7bc3..886ed2088c67b 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation.stderr +++ b/src/tools/clippy/tests/ui/redundant_allocation.stderr @@ -1,5 +1,5 @@ error: usage of `Box>` - --> tests/ui/redundant_allocation.rs:16:30 + --> tests/ui/redundant_allocation.rs:15:30 | LL | pub fn box_test6(foo: Box>) {} | ^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | pub fn box_test6(foo: Box>) {} = help: to override `-D warnings` add `#[allow(clippy::redundant_allocation)]` error: usage of `Box>` - --> tests/ui/redundant_allocation.rs:19:30 + --> tests/ui/redundant_allocation.rs:18:30 | LL | pub fn box_test7(foo: Box>) {} | ^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | pub fn box_test7(foo: Box>) {} = help: consider using just `Box` or `Arc` error: usage of `Box>>` - --> tests/ui/redundant_allocation.rs:22:27 + --> tests/ui/redundant_allocation.rs:21:27 | LL | pub fn box_test8() -> Box>> { | ^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | pub fn box_test8() -> Box>> { = help: consider using just `Box>` or `Rc>` error: usage of `Box>` - --> tests/ui/redundant_allocation.rs:28:30 + --> tests/ui/redundant_allocation.rs:27:30 | LL | pub fn box_test9(foo: Box>) -> Box>> { | ^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | pub fn box_test9(foo: Box>) -> Box>> { = help: consider using just `Box` or `Arc` error: usage of `Box>>` - --> tests/ui/redundant_allocation.rs:28:46 + --> tests/ui/redundant_allocation.rs:27:46 | LL | pub fn box_test9(foo: Box>) -> Box>> { | ^^^^^^^^^^^^^^^^^ @@ -46,7 +46,7 @@ LL | pub fn box_test9(foo: Box>) -> Box>> { = help: consider using just `Box>` or `Arc>` error: usage of `Rc>` - --> tests/ui/redundant_allocation.rs:42:24 + --> tests/ui/redundant_allocation.rs:41:24 | LL | pub fn rc_test5(a: Rc>) {} | ^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | pub fn rc_test5(a: Rc>) {} = help: consider using just `Rc` or `Box` error: usage of `Rc>` - --> tests/ui/redundant_allocation.rs:45:24 + --> tests/ui/redundant_allocation.rs:44:24 | LL | pub fn rc_test7(a: Rc>) {} | ^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | pub fn rc_test7(a: Rc>) {} = help: consider using just `Rc` or `Arc` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:48:26 + --> tests/ui/redundant_allocation.rs:47:26 | LL | pub fn rc_test8() -> Rc>> { | ^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | pub fn rc_test8() -> Rc>> { = help: consider using just `Rc>` or `Box>` error: usage of `Rc>` - --> tests/ui/redundant_allocation.rs:54:29 + --> tests/ui/redundant_allocation.rs:53:29 | LL | pub fn rc_test9(foo: Rc>) -> Rc>> { | ^^^^^^^^^^ @@ -82,7 +82,7 @@ LL | pub fn rc_test9(foo: Rc>) -> Rc>> { = help: consider using just `Rc` or `Arc` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:54:44 + --> tests/ui/redundant_allocation.rs:53:44 | LL | pub fn rc_test9(foo: Rc>) -> Rc>> { | ^^^^^^^^^^^^^^^^ @@ -91,7 +91,7 @@ LL | pub fn rc_test9(foo: Rc>) -> Rc>> { = help: consider using just `Rc>` or `Arc>` error: usage of `Arc>` - --> tests/ui/redundant_allocation.rs:68:25 + --> tests/ui/redundant_allocation.rs:67:25 | LL | pub fn arc_test5(a: Arc>) {} | ^^^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | pub fn arc_test5(a: Arc>) {} = help: consider using just `Arc` or `Box` error: usage of `Arc>` - --> tests/ui/redundant_allocation.rs:71:25 + --> tests/ui/redundant_allocation.rs:70:25 | LL | pub fn arc_test6(a: Arc>) {} | ^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | pub fn arc_test6(a: Arc>) {} = help: consider using just `Arc` or `Rc` error: usage of `Arc>>` - --> tests/ui/redundant_allocation.rs:74:27 + --> tests/ui/redundant_allocation.rs:73:27 | LL | pub fn arc_test8() -> Arc>> { | ^^^^^^^^^^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | pub fn arc_test8() -> Arc>> { = help: consider using just `Arc>` or `Box>` error: usage of `Arc>` - --> tests/ui/redundant_allocation.rs:80:30 + --> tests/ui/redundant_allocation.rs:79:30 | LL | pub fn arc_test9(foo: Arc>) -> Arc>> { | ^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | pub fn arc_test9(foo: Arc>) -> Arc>> { = help: consider using just `Arc` or `Rc` error: usage of `Arc>>` - --> tests/ui/redundant_allocation.rs:80:45 + --> tests/ui/redundant_allocation.rs:79:45 | LL | pub fn arc_test9(foo: Arc>) -> Arc>> { | ^^^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | pub fn arc_test9(foo: Arc>) -> Arc>> { = help: consider using just `Arc>` or `Rc>` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:105:27 + --> tests/ui/redundant_allocation.rs:104:27 | LL | pub fn test_rc_box(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | pub fn test_rc_box(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:138:31 + --> tests/ui/redundant_allocation.rs:137:31 | LL | pub fn test_rc_box_str(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | pub fn test_rc_box_str(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:141:33 + --> tests/ui/redundant_allocation.rs:140:33 | LL | pub fn test_rc_box_slice(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -163,7 +163,7 @@ LL | pub fn test_rc_box_slice(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:144:32 + --> tests/ui/redundant_allocation.rs:143:32 | LL | pub fn test_rc_box_path(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^^ @@ -172,7 +172,7 @@ LL | pub fn test_rc_box_path(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> tests/ui/redundant_allocation.rs:147:34 + --> tests/ui/redundant_allocation.rs:146:34 | LL | pub fn test_rc_box_custom(_: Rc>>) {} | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed b/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed index 7773ba11f973e..dbc6c0794d1a7 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed +++ b/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed @@ -1,7 +1,5 @@ -#![warn(clippy::all)] #![allow(clippy::boxed_local, clippy::needless_pass_by_value)] -#![allow(clippy::disallowed_names, unused_variables, dead_code)] -#![allow(unused_imports)] +#![allow(clippy::disallowed_names)] pub struct MyStruct; diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs b/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs index fb86ed2b3cfdf..05b6429492ce7 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs +++ b/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs @@ -1,7 +1,5 @@ -#![warn(clippy::all)] #![allow(clippy::boxed_local, clippy::needless_pass_by_value)] -#![allow(clippy::disallowed_names, unused_variables, dead_code)] -#![allow(unused_imports)] +#![allow(clippy::disallowed_names)] pub struct MyStruct; diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr b/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr index ed8282cc82ce9..4073766887174 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr +++ b/src/tools/clippy/tests/ui/redundant_allocation_fixable.stderr @@ -1,5 +1,5 @@ error: usage of `Box<&T>` - --> tests/ui/redundant_allocation_fixable.rs:23:30 + --> tests/ui/redundant_allocation_fixable.rs:21:30 | LL | pub fn box_test1(foo: Box<&T>) {} | ^^^^^^^ help: try: `&T` @@ -9,7 +9,7 @@ LL | pub fn box_test1(foo: Box<&T>) {} = help: to override `-D warnings` add `#[allow(clippy::redundant_allocation)]` error: usage of `Box<&MyStruct>` - --> tests/ui/redundant_allocation_fixable.rs:26:27 + --> tests/ui/redundant_allocation_fixable.rs:24:27 | LL | pub fn box_test2(foo: Box<&MyStruct>) {} | ^^^^^^^^^^^^^^ help: try: `&MyStruct` @@ -17,7 +17,7 @@ LL | pub fn box_test2(foo: Box<&MyStruct>) {} = note: `&MyStruct` is already a pointer, `Box<&MyStruct>` allocates a pointer on the heap error: usage of `Box<&MyEnum>` - --> tests/ui/redundant_allocation_fixable.rs:29:27 + --> tests/ui/redundant_allocation_fixable.rs:27:27 | LL | pub fn box_test3(foo: Box<&MyEnum>) {} | ^^^^^^^^^^^^ help: try: `&MyEnum` @@ -25,7 +25,7 @@ LL | pub fn box_test3(foo: Box<&MyEnum>) {} = note: `&MyEnum` is already a pointer, `Box<&MyEnum>` allocates a pointer on the heap error: usage of `Box>` - --> tests/ui/redundant_allocation_fixable.rs:34:30 + --> tests/ui/redundant_allocation_fixable.rs:32:30 | LL | pub fn box_test5(foo: Box>) {} | ^^^^^^^^^^^ help: try: `Box` @@ -33,7 +33,7 @@ LL | pub fn box_test5(foo: Box>) {} = note: `Box` is already on the heap, `Box>` makes an extra allocation error: usage of `Rc<&T>` - --> tests/ui/redundant_allocation_fixable.rs:44:29 + --> tests/ui/redundant_allocation_fixable.rs:42:29 | LL | pub fn rc_test1(foo: Rc<&T>) {} | ^^^^^^ help: try: `&T` @@ -41,7 +41,7 @@ LL | pub fn rc_test1(foo: Rc<&T>) {} = note: `&T` is already a pointer, `Rc<&T>` allocates a pointer on the heap error: usage of `Rc<&MyStruct>` - --> tests/ui/redundant_allocation_fixable.rs:47:26 + --> tests/ui/redundant_allocation_fixable.rs:45:26 | LL | pub fn rc_test2(foo: Rc<&MyStruct>) {} | ^^^^^^^^^^^^^ help: try: `&MyStruct` @@ -49,7 +49,7 @@ LL | pub fn rc_test2(foo: Rc<&MyStruct>) {} = note: `&MyStruct` is already a pointer, `Rc<&MyStruct>` allocates a pointer on the heap error: usage of `Rc<&MyEnum>` - --> tests/ui/redundant_allocation_fixable.rs:50:26 + --> tests/ui/redundant_allocation_fixable.rs:48:26 | LL | pub fn rc_test3(foo: Rc<&MyEnum>) {} | ^^^^^^^^^^^ help: try: `&MyEnum` @@ -57,7 +57,7 @@ LL | pub fn rc_test3(foo: Rc<&MyEnum>) {} = note: `&MyEnum` is already a pointer, `Rc<&MyEnum>` allocates a pointer on the heap error: usage of `Rc>` - --> tests/ui/redundant_allocation_fixable.rs:55:24 + --> tests/ui/redundant_allocation_fixable.rs:53:24 | LL | pub fn rc_test6(a: Rc>) {} | ^^^^^^^^^^^^ help: try: `Rc` @@ -65,7 +65,7 @@ LL | pub fn rc_test6(a: Rc>) {} = note: `Rc` is already on the heap, `Rc>` makes an extra allocation error: usage of `Arc<&T>` - --> tests/ui/redundant_allocation_fixable.rs:65:30 + --> tests/ui/redundant_allocation_fixable.rs:63:30 | LL | pub fn arc_test1(foo: Arc<&T>) {} | ^^^^^^^ help: try: `&T` @@ -73,7 +73,7 @@ LL | pub fn arc_test1(foo: Arc<&T>) {} = note: `&T` is already a pointer, `Arc<&T>` allocates a pointer on the heap error: usage of `Arc<&MyStruct>` - --> tests/ui/redundant_allocation_fixable.rs:68:27 + --> tests/ui/redundant_allocation_fixable.rs:66:27 | LL | pub fn arc_test2(foo: Arc<&MyStruct>) {} | ^^^^^^^^^^^^^^ help: try: `&MyStruct` @@ -81,7 +81,7 @@ LL | pub fn arc_test2(foo: Arc<&MyStruct>) {} = note: `&MyStruct` is already a pointer, `Arc<&MyStruct>` allocates a pointer on the heap error: usage of `Arc<&MyEnum>` - --> tests/ui/redundant_allocation_fixable.rs:71:27 + --> tests/ui/redundant_allocation_fixable.rs:69:27 | LL | pub fn arc_test3(foo: Arc<&MyEnum>) {} | ^^^^^^^^^^^^ help: try: `&MyEnum` @@ -89,7 +89,7 @@ LL | pub fn arc_test3(foo: Arc<&MyEnum>) {} = note: `&MyEnum` is already a pointer, `Arc<&MyEnum>` allocates a pointer on the heap error: usage of `Arc>` - --> tests/ui/redundant_allocation_fixable.rs:76:25 + --> tests/ui/redundant_allocation_fixable.rs:74:25 | LL | pub fn arc_test7(a: Arc>) {} | ^^^^^^^^^^^^^^ help: try: `Arc` diff --git a/src/tools/clippy/tests/ui/redundant_clone.fixed b/src/tools/clippy/tests/ui/redundant_clone.fixed index 23c00b34a00a8..c1c389f7c4ed2 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.fixed +++ b/src/tools/clippy/tests/ui/redundant_clone.fixed @@ -259,3 +259,35 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } + +mod issue10074 { + #[derive(Debug, Clone)] + enum MyEnum { + A = 1, + } + + fn false_positive_on_as() { + let e = MyEnum::A; + let v = e.clone() as u16; + + println!("{e:?}"); + println!("{v}"); + } +} + +mod issue13900 { + use std::fmt::Display; + + fn do_something(f: impl Display + Clone) -> String { + let g = f.clone(); + format!("{} + {}", f, g) + } + + fn regression() { + let mut a = String::new(); + let mut b = String::new(); + for _ in 1..10 { + b = a.clone(); + } + } +} diff --git a/src/tools/clippy/tests/ui/redundant_clone.rs b/src/tools/clippy/tests/ui/redundant_clone.rs index f9fe8ba0236d0..78d98762efc86 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.rs +++ b/src/tools/clippy/tests/ui/redundant_clone.rs @@ -259,3 +259,35 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } + +mod issue10074 { + #[derive(Debug, Clone)] + enum MyEnum { + A = 1, + } + + fn false_positive_on_as() { + let e = MyEnum::A; + let v = e.clone() as u16; + + println!("{e:?}"); + println!("{v}"); + } +} + +mod issue13900 { + use std::fmt::Display; + + fn do_something(f: impl Display + Clone) -> String { + let g = f.clone(); + format!("{} + {}", f, g) + } + + fn regression() { + let mut a = String::new(); + let mut b = String::new(); + for _ in 1..10 { + b = a.clone(); + } + } +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed index 549c97d9534ad..1cec19ab8c99d 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.fixed @@ -1,5 +1,4 @@ -#![warn(clippy::all, clippy::redundant_pattern_matching)] -#![allow(unused_must_use)] +#![warn(clippy::redundant_pattern_matching)] #![allow( clippy::match_like_matches_macro, clippy::needless_bool, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs index decb1396d56dd..123573a8602b6 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.rs @@ -1,5 +1,4 @@ -#![warn(clippy::all, clippy::redundant_pattern_matching)] -#![allow(unused_must_use)] +#![warn(clippy::redundant_pattern_matching)] #![allow( clippy::match_like_matches_macro, clippy::needless_bool, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr index 66d2cecdc0c91..3be7cf81afe95 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_ipaddr.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:15:12 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:14:12 | LL | if let V4(_) = &ipaddr {} | -------^^^^^---------- help: try: `if ipaddr.is_ipv4()` @@ -8,43 +8,43 @@ LL | if let V4(_) = &ipaddr {} = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:18:12 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:17:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:21:12 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:20:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:25:8 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:24:8 | LL | if matches!(V4(Ipv4Addr::LOCALHOST), V4(_)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:29:8 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:28:8 | LL | if matches!(V6(Ipv6Addr::LOCALHOST), V6(_)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:32:15 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:31:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:35:15 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:34:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:46:5 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:45:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | @@ -54,7 +54,7 @@ LL | | }; | |_____^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:52:5 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:51:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | @@ -64,7 +64,7 @@ LL | | }; | |_____^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:58:5 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:57:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | @@ -74,7 +74,7 @@ LL | | }; | |_____^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:64:5 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:63:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | @@ -84,49 +84,49 @@ LL | | }; | |_____^ help: try: `V6(Ipv6Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:70:20 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:69:20 | LL | let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { | -------^^^^^-------------------------- help: try: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:79:20 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:78:20 | LL | let _ = if let V4(_) = gen_ipaddr() { | -------^^^^^--------------- help: try: `if gen_ipaddr().is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:82:19 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:81:19 | LL | } else if let V6(_) = gen_ipaddr() { | -------^^^^^--------------- help: try: `if gen_ipaddr().is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:95:12 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:94:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:98:12 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:97:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:101:15 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:100:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:104:15 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:103:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:107:5 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:106:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | @@ -136,7 +136,7 @@ LL | | }; | |_____^ help: try: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> tests/ui/redundant_pattern_matching_ipaddr.rs:113:5 + --> tests/ui/redundant_pattern_matching_ipaddr.rs:112:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed index 5585006dc362b..dc9d6491691f3 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed @@ -1,14 +1,12 @@ -#![warn(clippy::all)] +#![feature(if_let_guard)] #![warn(clippy::redundant_pattern_matching)] #![allow( - unused_must_use, clippy::needless_bool, clippy::needless_if, clippy::match_like_matches_macro, clippy::equatable_if_let, clippy::if_same_then_else )] -#![feature(let_chains, if_let_guard)] fn issue_11174(boolean: bool, maybe_some: Option) -> bool { maybe_some.is_none() && (!boolean) diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs index 581a432f38e11..2e9714ad8e75f 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs @@ -1,14 +1,12 @@ -#![warn(clippy::all)] +#![feature(if_let_guard)] #![warn(clippy::redundant_pattern_matching)] #![allow( - unused_must_use, clippy::needless_bool, clippy::needless_if, clippy::match_like_matches_macro, clippy::equatable_if_let, clippy::if_same_then_else )] -#![feature(let_chains, if_let_guard)] fn issue_11174(boolean: bool, maybe_some: Option) -> bool { matches!(maybe_some, None if !boolean) diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr index 681602567d2f2..e5a6598898aa1 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:14:5 + --> tests/ui/redundant_pattern_matching_option.rs:12:5 | LL | matches!(maybe_some, None if !boolean) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `maybe_some.is_none() && (!boolean)` @@ -8,55 +8,55 @@ LL | matches!(maybe_some, None if !boolean) = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:19:13 + --> tests/ui/redundant_pattern_matching_option.rs:17:13 | LL | let _ = matches!(maybe_some, None if boolean || boolean2); // guard needs parentheses | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `maybe_some.is_none() && (boolean || boolean2)` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:35:12 + --> tests/ui/redundant_pattern_matching_option.rs:33:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:38:12 + --> tests/ui/redundant_pattern_matching_option.rs:36:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:41:12 + --> tests/ui/redundant_pattern_matching_option.rs:39:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:48:15 + --> tests/ui/redundant_pattern_matching_option.rs:46:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:51:15 + --> tests/ui/redundant_pattern_matching_option.rs:49:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:54:15 + --> tests/ui/redundant_pattern_matching_option.rs:52:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:58:15 + --> tests/ui/redundant_pattern_matching_option.rs:56:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:67:5 + --> tests/ui/redundant_pattern_matching_option.rs:65:5 | LL | / match Some(42) { LL | | @@ -66,7 +66,7 @@ LL | | }; | |_____^ help: try: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:73:5 + --> tests/ui/redundant_pattern_matching_option.rs:71:5 | LL | / match None::<()> { LL | | @@ -76,7 +76,7 @@ LL | | }; | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:79:13 + --> tests/ui/redundant_pattern_matching_option.rs:77:13 | LL | let _ = match None::<()> { | _____________^ @@ -87,55 +87,55 @@ LL | | }; | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:86:20 + --> tests/ui/redundant_pattern_matching_option.rs:84:20 | LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:93:20 + --> tests/ui/redundant_pattern_matching_option.rs:91:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:96:19 + --> tests/ui/redundant_pattern_matching_option.rs:94:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:103:12 + --> tests/ui/redundant_pattern_matching_option.rs:101:12 | LL | if let Some(..) = gen_opt() {} | -------^^^^^^^^------------ help: try: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:119:12 + --> tests/ui/redundant_pattern_matching_option.rs:117:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:122:12 + --> tests/ui/redundant_pattern_matching_option.rs:120:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:125:15 + --> tests/ui/redundant_pattern_matching_option.rs:123:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:128:15 + --> tests/ui/redundant_pattern_matching_option.rs:126:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:131:5 + --> tests/ui/redundant_pattern_matching_option.rs:129:5 | LL | / match Some(42) { LL | | @@ -145,7 +145,7 @@ LL | | }; | |_____^ help: try: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:137:5 + --> tests/ui/redundant_pattern_matching_option.rs:135:5 | LL | / match None::<()> { LL | | @@ -155,19 +155,19 @@ LL | | }; | |_____^ help: try: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:146:12 + --> tests/ui/redundant_pattern_matching_option.rs:144:12 | LL | if let None = *(&None::<()>) {} | -------^^^^----------------- help: try: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:148:12 + --> tests/ui/redundant_pattern_matching_option.rs:146:12 | LL | if let None = *&None::<()> {} | -------^^^^--------------- help: try: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:155:5 + --> tests/ui/redundant_pattern_matching_option.rs:153:5 | LL | / match x { LL | | @@ -177,7 +177,7 @@ LL | | }; | |_____^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:161:5 + --> tests/ui/redundant_pattern_matching_option.rs:159:5 | LL | / match x { LL | | @@ -187,7 +187,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:167:5 + --> tests/ui/redundant_pattern_matching_option.rs:165:5 | LL | / match x { LL | | @@ -197,7 +197,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:173:5 + --> tests/ui/redundant_pattern_matching_option.rs:171:5 | LL | / match x { LL | | @@ -207,19 +207,19 @@ LL | | }; | |_____^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_option.rs:189:13 + --> tests/ui/redundant_pattern_matching_option.rs:187:13 | LL | let _ = matches!(x, Some(_)); | ^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_some()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:192:13 + --> tests/ui/redundant_pattern_matching_option.rs:190:13 | LL | let _ = matches!(x, None); | ^^^^^^^^^^^^^^^^^ help: try: `x.is_none()` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/redundant_pattern_matching_option.rs:203:17 + --> tests/ui/redundant_pattern_matching_option.rs:201:17 | LL | let _ = matches!(*p, None); | ^^^^^^^^^^^^^^^^^^ help: try: `(*p).is_none()` diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed index c8e18e8676f21..800889b5fda0a 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed @@ -1,7 +1,5 @@ -#![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] #![allow( - unused_must_use, clippy::needless_bool, clippy::needless_if, clippy::match_like_matches_macro, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs index 727503d21a54a..1668c2ff2bbac 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs @@ -1,7 +1,5 @@ -#![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] #![allow( - unused_must_use, clippy::needless_bool, clippy::needless_if, clippy::match_like_matches_macro, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr index 5f659184f7b38..5cd9d9636e466 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:15:12 + --> tests/ui/redundant_pattern_matching_poll.rs:13:12 | LL | if let Pending = Pending::<()> {} | -------^^^^^^^---------------- help: try: `if Pending::<()>.is_pending()` @@ -8,49 +8,49 @@ LL | if let Pending = Pending::<()> {} = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:18:12 + --> tests/ui/redundant_pattern_matching_poll.rs:16:12 | LL | if let Ready(_) = Ready(42) {} | -------^^^^^^^^------------ help: try: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:21:12 + --> tests/ui/redundant_pattern_matching_poll.rs:19:12 | LL | if let Ready(_) = Ready(42) { | -------^^^^^^^^------------ help: try: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:29:8 + --> tests/ui/redundant_pattern_matching_poll.rs:27:8 | LL | if matches!(Ready(42), Ready(_)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:33:8 + --> tests/ui/redundant_pattern_matching_poll.rs:31:8 | LL | if matches!(Pending::<()>, Pending) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:36:15 + --> tests/ui/redundant_pattern_matching_poll.rs:34:15 | LL | while let Ready(_) = Ready(42) {} | ----------^^^^^^^^------------ help: try: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:39:15 + --> tests/ui/redundant_pattern_matching_poll.rs:37:15 | LL | while let Pending = Ready(42) {} | ----------^^^^^^^------------ help: try: `while Ready(42).is_pending()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:42:15 + --> tests/ui/redundant_pattern_matching_poll.rs:40:15 | LL | while let Pending = Pending::<()> {} | ----------^^^^^^^---------------- help: try: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:49:5 + --> tests/ui/redundant_pattern_matching_poll.rs:47:5 | LL | / match Ready(42) { LL | | @@ -60,7 +60,7 @@ LL | | }; | |_____^ help: try: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:55:5 + --> tests/ui/redundant_pattern_matching_poll.rs:53:5 | LL | / match Pending::<()> { LL | | @@ -70,7 +70,7 @@ LL | | }; | |_____^ help: try: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:61:13 + --> tests/ui/redundant_pattern_matching_poll.rs:59:13 | LL | let _ = match Pending::<()> { | _____________^ @@ -81,49 +81,49 @@ LL | | }; | |_____^ help: try: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:68:20 + --> tests/ui/redundant_pattern_matching_poll.rs:66:20 | LL | let _ = if let Ready(_) = poll { true } else { false }; | -------^^^^^^^^------- help: try: `if poll.is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:73:20 + --> tests/ui/redundant_pattern_matching_poll.rs:71:20 | LL | let _ = if let Ready(_) = gen_poll() { | -------^^^^^^^^------------- help: try: `if gen_poll().is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:76:19 + --> tests/ui/redundant_pattern_matching_poll.rs:74:19 | LL | } else if let Pending = gen_poll() { | -------^^^^^^^------------- help: try: `if gen_poll().is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:93:12 + --> tests/ui/redundant_pattern_matching_poll.rs:91:12 | LL | if let Ready(_) = Ready(42) {} | -------^^^^^^^^------------ help: try: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:96:12 + --> tests/ui/redundant_pattern_matching_poll.rs:94:12 | LL | if let Pending = Pending::<()> {} | -------^^^^^^^---------------- help: try: `if Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:99:15 + --> tests/ui/redundant_pattern_matching_poll.rs:97:15 | LL | while let Ready(_) = Ready(42) {} | ----------^^^^^^^^------------ help: try: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:102:15 + --> tests/ui/redundant_pattern_matching_poll.rs:100:15 | LL | while let Pending = Pending::<()> {} | ----------^^^^^^^---------------- help: try: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> tests/ui/redundant_pattern_matching_poll.rs:105:5 + --> tests/ui/redundant_pattern_matching_poll.rs:103:5 | LL | / match Ready(42) { LL | | @@ -133,7 +133,7 @@ LL | | }; | |_____^ help: try: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> tests/ui/redundant_pattern_matching_poll.rs:111:5 + --> tests/ui/redundant_pattern_matching_poll.rs:109:5 | LL | / match Pending::<()> { LL | | diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed index 1158796083147..dab816716d598 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed @@ -1,6 +1,5 @@ -#![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(deprecated, unused_must_use)] +#![allow(deprecated)] #![allow( clippy::if_same_then_else, clippy::match_like_matches_macro, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs index 35f8f91b31527..3fd70515d0847 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs @@ -1,6 +1,5 @@ -#![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(deprecated, unused_must_use)] +#![allow(deprecated)] #![allow( clippy::if_same_then_else, clippy::match_like_matches_macro, diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr index 4f78b95356c21..7e7d27d07a7f6 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:15:12 + --> tests/ui/redundant_pattern_matching_result.rs:14:12 | LL | if let Ok(_) = &result {} | -------^^^^^---------- help: try: `if result.is_ok()` @@ -8,31 +8,31 @@ LL | if let Ok(_) = &result {} = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:18:12 + --> tests/ui/redundant_pattern_matching_result.rs:17:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:21:12 + --> tests/ui/redundant_pattern_matching_result.rs:20:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:24:15 + --> tests/ui/redundant_pattern_matching_result.rs:23:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:27:15 + --> tests/ui/redundant_pattern_matching_result.rs:26:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:38:5 + --> tests/ui/redundant_pattern_matching_result.rs:37:5 | LL | / match Ok::(42) { LL | | @@ -42,7 +42,7 @@ LL | | }; | |_____^ help: try: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:44:5 + --> tests/ui/redundant_pattern_matching_result.rs:43:5 | LL | / match Ok::(42) { LL | | @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: try: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:50:5 + --> tests/ui/redundant_pattern_matching_result.rs:49:5 | LL | / match Err::(42) { LL | | @@ -62,7 +62,7 @@ LL | | }; | |_____^ help: try: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:56:5 + --> tests/ui/redundant_pattern_matching_result.rs:55:5 | LL | / match Err::(42) { LL | | @@ -72,73 +72,73 @@ LL | | }; | |_____^ help: try: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:62:20 + --> tests/ui/redundant_pattern_matching_result.rs:61:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:71:20 + --> tests/ui/redundant_pattern_matching_result.rs:70:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:74:19 + --> tests/ui/redundant_pattern_matching_result.rs:73:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_result.rs:98:19 + --> tests/ui/redundant_pattern_matching_result.rs:97:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_result.rs:100:16 + --> tests/ui/redundant_pattern_matching_result.rs:99:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_result.rs:107:12 + --> tests/ui/redundant_pattern_matching_result.rs:106:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/redundant_pattern_matching_result.rs:109:15 + --> tests/ui/redundant_pattern_matching_result.rs:108:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:128:12 + --> tests/ui/redundant_pattern_matching_result.rs:127:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:131:12 + --> tests/ui/redundant_pattern_matching_result.rs:130:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:134:15 + --> tests/ui/redundant_pattern_matching_result.rs:133:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:137:15 + --> tests/ui/redundant_pattern_matching_result.rs:136:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:140:5 + --> tests/ui/redundant_pattern_matching_result.rs:139:5 | LL | / match Ok::(42) { LL | | @@ -148,7 +148,7 @@ LL | | }; | |_____^ help: try: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:146:5 + --> tests/ui/redundant_pattern_matching_result.rs:145:5 | LL | / match Err::(42) { LL | | @@ -158,7 +158,7 @@ LL | | }; | |_____^ help: try: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:157:5 + --> tests/ui/redundant_pattern_matching_result.rs:156:5 | LL | / match x { LL | | @@ -168,7 +168,7 @@ LL | | }; | |_____^ help: try: `x.is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:163:5 + --> tests/ui/redundant_pattern_matching_result.rs:162:5 | LL | / match x { LL | | @@ -178,7 +178,7 @@ LL | | }; | |_____^ help: try: `x.is_err()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:169:5 + --> tests/ui/redundant_pattern_matching_result.rs:168:5 | LL | / match x { LL | | @@ -188,7 +188,7 @@ LL | | }; | |_____^ help: try: `x.is_err()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:175:5 + --> tests/ui/redundant_pattern_matching_result.rs:174:5 | LL | / match x { LL | | @@ -198,13 +198,13 @@ LL | | }; | |_____^ help: try: `x.is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> tests/ui/redundant_pattern_matching_result.rs:197:13 + --> tests/ui/redundant_pattern_matching_result.rs:196:13 | LL | let _ = matches!(x, Ok(_)); | ^^^^^^^^^^^^^^^^^^ help: try: `x.is_ok()` error: redundant pattern matching, consider using `is_err()` - --> tests/ui/redundant_pattern_matching_result.rs:200:13 + --> tests/ui/redundant_pattern_matching_result.rs:199:13 | LL | let _ = matches!(x, Err(_)); | ^^^^^^^^^^^^^^^^^^^ help: try: `x.is_err()` diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed index a6450123f4c9d..8a30fedede4a4 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed @@ -131,6 +131,14 @@ mod m4 { } } +mod m5 { + pub mod m5_1 {} + // Test that the primary span isn't butchered for item kinds that don't have an ident. + pub use m5_1::*; //~ redundant_pub_crate + #[rustfmt::skip] + pub use m5_1::{*}; //~ redundant_pub_crate +} + pub use m4::*; mod issue_8732 { diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.rs b/src/tools/clippy/tests/ui/redundant_pub_crate.rs index 7415d34d50cc7..45ba13a63b2e2 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.rs +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.rs @@ -131,6 +131,14 @@ mod m4 { } } +mod m5 { + pub mod m5_1 {} + // Test that the primary span isn't butchered for item kinds that don't have an ident. + pub(crate) use m5_1::*; //~ redundant_pub_crate + #[rustfmt::skip] + pub(crate) use m5_1::{*}; //~ redundant_pub_crate +} + pub use m4::*; mod issue_8732 { diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr index 95909ea8b0663..4a47a321028d1 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr @@ -129,5 +129,21 @@ LL | pub(crate) fn g() {} // private due to m4_2 | | | help: consider using: `pub` -error: aborting due to 16 previous errors +error: pub(crate) import inside private module + --> tests/ui/redundant_pub_crate.rs:137:5 + | +LL | pub(crate) use m5_1::*; + | ----------^^^^^^^^^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) import inside private module + --> tests/ui/redundant_pub_crate.rs:139:27 + | +LL | pub(crate) use m5_1::{*}; + | ---------- ^ + | | + | help: consider using: `pub` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_test_prefix.fixed b/src/tools/clippy/tests/ui/redundant_test_prefix.fixed new file mode 100644 index 0000000000000..b99771f0640ca --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_test_prefix.fixed @@ -0,0 +1,158 @@ +#![allow(dead_code)] +#![warn(clippy::redundant_test_prefix)] + +fn main() { + // Normal function, no redundant prefix. +} + +fn f1() { + // Normal function, no redundant prefix. +} + +fn test_f2() { + // Has prefix, but no `#[test]` attribute, ignore. +} + +#[test] +fn f3() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute. Not within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +#[cfg(test)] +#[test] +fn f4() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +mod m1 { + pub fn f5() {} +} + +#[cfg(test)] +#[test] +fn f6() { + //~^ redundant_test_prefix + + use m1::f5; + + f5(); + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision, has function call, but it will not result in recursion. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn foo() { + //~^ redundant_test_prefix + } + + #[test] + fn foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn f1() { + //~^ redundant_test_prefix + } + + #[test] + fn f2() { + //~^ redundant_test_prefix + } + + #[test] + fn f3() { + //~^ redundant_test_prefix + } + + #[test] + fn f4() { + //~^ redundant_test_prefix + } + + #[test] + fn f5() { + //~^ redundant_test_prefix + } + + #[test] + fn f6() { + //~^ redundant_test_prefix + } +} + +mod tests_no_annotations { + use super::*; + + #[test] + fn foo() { + //~^ redundant_test_prefix + } + + #[test] + fn foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn f1() { + //~^ redundant_test_prefix + } + + #[test] + fn f2() { + //~^ redundant_test_prefix + } + + #[test] + fn f3() { + //~^ redundant_test_prefix + } + + #[test] + fn f4() { + //~^ redundant_test_prefix + } + + #[test] + fn f5() { + //~^ redundant_test_prefix + } + + #[test] + fn f6() { + //~^ redundant_test_prefix + } +} + +// This test is inspired by real test in `clippy_utils/src/sugg.rs`. +// The `is_in_test_function()` checks whether any identifier within a given node's parents is +// marked with `#[test]` attribute. Thus flagging false positives when nested functions are +// prefixed with `test_`. Therefore `is_test_function()` has been defined in `clippy_utils`, +// allowing to select only functions that are immediately marked with `#[test]` annotation. +// +// This test case ensures that for such nested functions no error is emitted. +#[test] +fn not_op() { + fn test_not(foo: bool) { + assert!(foo); + } + + // Use helper function + test_not(true); + test_not(false); +} diff --git a/src/tools/clippy/tests/ui/redundant_test_prefix.rs b/src/tools/clippy/tests/ui/redundant_test_prefix.rs new file mode 100644 index 0000000000000..3aec577cffa16 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_test_prefix.rs @@ -0,0 +1,158 @@ +#![allow(dead_code)] +#![warn(clippy::redundant_test_prefix)] + +fn main() { + // Normal function, no redundant prefix. +} + +fn f1() { + // Normal function, no redundant prefix. +} + +fn test_f2() { + // Has prefix, but no `#[test]` attribute, ignore. +} + +#[test] +fn test_f3() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute. Not within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +#[cfg(test)] +#[test] +fn test_f4() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +mod m1 { + pub fn f5() {} +} + +#[cfg(test)] +#[test] +fn test_f6() { + //~^ redundant_test_prefix + + use m1::f5; + + f5(); + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision, has function call, but it will not result in recursion. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_foo() { + //~^ redundant_test_prefix + } + + #[test] + fn test_foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn test_f1() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f2() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f3() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f4() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f5() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f6() { + //~^ redundant_test_prefix + } +} + +mod tests_no_annotations { + use super::*; + + #[test] + fn test_foo() { + //~^ redundant_test_prefix + } + + #[test] + fn test_foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn test_f1() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f2() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f3() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f4() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f5() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f6() { + //~^ redundant_test_prefix + } +} + +// This test is inspired by real test in `clippy_utils/src/sugg.rs`. +// The `is_in_test_function()` checks whether any identifier within a given node's parents is +// marked with `#[test]` attribute. Thus flagging false positives when nested functions are +// prefixed with `test_`. Therefore `is_test_function()` has been defined in `clippy_utils`, +// allowing to select only functions that are immediately marked with `#[test]` annotation. +// +// This test case ensures that for such nested functions no error is emitted. +#[test] +fn not_op() { + fn test_not(foo: bool) { + assert!(foo); + } + + // Use helper function + test_not(true); + test_not(false); +} diff --git a/src/tools/clippy/tests/ui/redundant_test_prefix.stderr b/src/tools/clippy/tests/ui/redundant_test_prefix.stderr new file mode 100644 index 0000000000000..d156af586df3f --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_test_prefix.stderr @@ -0,0 +1,119 @@ +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:17:4 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + | + = note: `-D clippy::redundant-test-prefix` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_test_prefix)]` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:26:4 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:39:4 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:54:8 + | +LL | fn test_foo() { + | ^^^^^^^^ help: consider removing the `test_` prefix: `foo` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:59:8 + | +LL | fn test_foo_with_call() { + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `test_` prefix: `foo_with_call` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:66:8 + | +LL | fn test_f1() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f1` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:71:8 + | +LL | fn test_f2() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:76:8 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:81:8 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:86:8 + | +LL | fn test_f5() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f5` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:91:8 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:100:8 + | +LL | fn test_foo() { + | ^^^^^^^^ help: consider removing the `test_` prefix: `foo` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:105:8 + | +LL | fn test_foo_with_call() { + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `test_` prefix: `foo_with_call` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:112:8 + | +LL | fn test_f1() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f1` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:117:8 + | +LL | fn test_f2() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:122:8 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:127:8 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:132:8 + | +LL | fn test_f5() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f5` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix.rs:137:8 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: aborting due to 19 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_test_prefix_noautofix.rs b/src/tools/clippy/tests/ui/redundant_test_prefix_noautofix.rs new file mode 100644 index 0000000000000..6ad5d011d8b71 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_test_prefix_noautofix.rs @@ -0,0 +1,288 @@ +//@no-rustfix: name conflicts + +#![allow(dead_code)] +#![warn(clippy::redundant_test_prefix)] + +fn main() { + // Normal function, no redundant prefix. +} + +fn f1() { + // Normal function, no redundant prefix. +} + +fn test_f2() { + // Has prefix, but no `#[test]` attribute, ignore. +} + +#[test] +fn test_f3() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute. Not within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +#[cfg(test)] +#[test] +fn test_f4() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. +} + +fn f5() {} + +#[cfg(test)] +#[test] +fn test_f5() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // Collision with existing function. +} + +mod m1 { + pub fn f6() {} + pub fn f7() {} +} + +#[cfg(test)] +#[test] +fn test_f6() { + //~^ redundant_test_prefix + + use m1::f6; + + f6(); + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision, but has a function call that will result in recursion. +} + +#[cfg(test)] +#[test] +fn test_f8() { + //~^ redundant_test_prefix + + use m1::f7; + + f7(); + // Has prefix, has `#[test]` attribute, within a `#[cfg(test)]`. + // No collision, has function call, but it will not result in recursion. +} + +// Although there's no direct call of `f` in the test, name collision still exists, +// since all `m3` functions are imported and then `map` is used to call `f`. +mod m2 { + mod m3 { + pub fn f(_: i32) -> i32 { + 0 + } + } + + use m3::*; + + #[cfg(test)] + #[test] + fn test_f() { + //~^ redundant_test_prefix + let a = Some(3); + let _ = a.map(f); + } +} + +mod m3 { + fn test_m3_1() { + // Has prefix, but no `#[test]` attribute, ignore. + } + + #[test] + fn test_m3_2() { + //~^ redundant_test_prefix + + // Has prefix, has `#[test]` attribute. Not within a `#[cfg(test)]`. + // No collision with other functions, should emit warning. + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_foo() { + //~^ redundant_test_prefix + } + + #[test] + fn test_foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn test_f1() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f2() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f3() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f4() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f5() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f6() { + //~^ redundant_test_prefix + } + + #[test] + fn test_1() { + //~^ redundant_test_prefix + + // `1` is invalid function name, so suggestion to rename is emitted + } + + #[test] + fn test_const() { + //~^ redundant_test_prefix + + // `const` is reserved keyword, so suggestion to rename is emitted + } + + #[test] + fn test_async() { + //~^ redundant_test_prefix + + // `async` is reserved keyword, so suggestion to rename is emitted + } + + #[test] + fn test_yield() { + //~^ redundant_test_prefix + + // `yield` is reserved keyword for future use, so suggestion to rename is emitted + } + + #[test] + fn test_() { + //~^ redundant_test_prefix + + // `` is invalid function name, so suggestion to rename is emitted + } +} + +mod tests_no_annotations { + use super::*; + + #[test] + fn test_foo() { + //~^ redundant_test_prefix + } + + #[test] + fn test_foo_with_call() { + //~^ redundant_test_prefix + + main(); + } + + #[test] + fn test_f1() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f2() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f3() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f4() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f5() { + //~^ redundant_test_prefix + } + + #[test] + fn test_f6() { + //~^ redundant_test_prefix + } + + #[test] + fn test_1() { + //~^ redundant_test_prefix + + // `1` is invalid function name, so suggestion to rename is emitted + } + + #[test] + fn test_const() { + //~^ redundant_test_prefix + + // `const` is reserved keyword, so suggestion to rename is emitted + } + + #[test] + fn test_async() { + //~^ redundant_test_prefix + + // `async` is reserved keyword, so suggestion to rename is emitted + } + + #[test] + fn test_yield() { + //~^ redundant_test_prefix + + // `yield` is reserved keyword for future use, so suggestion to rename is emitted + } + + #[test] + fn test_() { + //~^ redundant_test_prefix + + // `` is invalid function name, so suggestion to rename is emitted + } +} + +// This test is inspired by real test in `clippy_utils/src/sugg.rs`. +// The `is_in_test_function()` checks whether any identifier within a given node's parents is +// marked with `#[test]` attribute. Thus flagging false positives when nested functions are +// prefixed with `test_`. Therefore `is_test_function()` has been defined in `clippy_utils`, +// allowing to select only functions that are immediately marked with `#[test]` annotation. +// +// This test case ensures that for such nested functions no error is emitted. +#[test] +fn not_op() { + fn test_not(foo: bool) { + assert!(foo); + } + + // Use helper function + test_not(true); + test_not(false); +} diff --git a/src/tools/clippy/tests/ui/redundant_test_prefix_noautofix.stderr b/src/tools/clippy/tests/ui/redundant_test_prefix_noautofix.stderr new file mode 100644 index 0000000000000..6440faf1b3c83 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_test_prefix_noautofix.stderr @@ -0,0 +1,241 @@ +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:19:4 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + | + = note: `-D clippy::redundant-test-prefix` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_test_prefix)]` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:28:4 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:39:4 + | +LL | fn test_f5() { + | ^^^^^^^ + | +help: consider function renaming (just removing `test_` prefix will cause a name conflict) + | +LL - fn test_f5() { +LL + fn f5_works() { + | + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:53:4 + | +LL | fn test_f6() { + | ^^^^^^^ + | +help: consider function renaming (just removing `test_` prefix will cause a name conflict) + | +LL - fn test_f6() { +LL + fn f6_works() { + | + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:65:4 + | +LL | fn test_f8() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f8` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:88:8 + | +LL | fn test_f() { + | ^^^^^^ + | +help: consider function renaming (just removing `test_` prefix will cause a name conflict) + | +LL - fn test_f() { +LL + fn f_works() { + | + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:101:8 + | +LL | fn test_m3_2() { + | ^^^^^^^^^ help: consider removing the `test_` prefix: `m3_2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:114:8 + | +LL | fn test_foo() { + | ^^^^^^^^ help: consider removing the `test_` prefix: `foo` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:119:8 + | +LL | fn test_foo_with_call() { + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `test_` prefix: `foo_with_call` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:126:8 + | +LL | fn test_f1() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f1` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:131:8 + | +LL | fn test_f2() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:136:8 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:141:8 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:146:8 + | +LL | fn test_f5() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f5` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:151:8 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:156:8 + | +LL | fn test_1() { + | ^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:163:8 + | +LL | fn test_const() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:170:8 + | +LL | fn test_async() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:177:8 + | +LL | fn test_yield() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:184:8 + | +LL | fn test_() { + | ^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:195:8 + | +LL | fn test_foo() { + | ^^^^^^^^ help: consider removing the `test_` prefix: `foo` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:200:8 + | +LL | fn test_foo_with_call() { + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `test_` prefix: `foo_with_call` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:207:8 + | +LL | fn test_f1() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f1` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:212:8 + | +LL | fn test_f2() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f2` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:217:8 + | +LL | fn test_f3() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f3` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:222:8 + | +LL | fn test_f4() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f4` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:227:8 + | +LL | fn test_f5() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f5` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:232:8 + | +LL | fn test_f6() { + | ^^^^^^^ help: consider removing the `test_` prefix: `f6` + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:237:8 + | +LL | fn test_1() { + | ^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:244:8 + | +LL | fn test_const() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:251:8 + | +LL | fn test_async() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:258:8 + | +LL | fn test_yield() { + | ^^^^^^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: redundant `test_` prefix in test function name + --> tests/ui/redundant_test_prefix_noautofix.rs:265:8 + | +LL | fn test_() { + | ^^^^^ + | + = help: consider function renaming (just removing `test_` prefix will produce invalid function name) + +error: aborting due to 33 previous errors + diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.all.stderr b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.all.stderr index 030a9a28ec684..886bf2b034989 100644 --- a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.all.stderr +++ b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:10:5 + --> tests/ui/ref_option/ref_option_traits.rs:9:5 | LL | fn pub_trait_opt(&self, a: &Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ @@ -10,7 +10,7 @@ LL | fn pub_trait_opt(&self, a: &Option>); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:12:5 + --> tests/ui/ref_option/ref_option_traits.rs:11:5 | LL | fn pub_trait_ret(&self) -> &Option>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ @@ -18,7 +18,7 @@ LL | fn pub_trait_ret(&self) -> &Option>; | help: change this to: `Option<&Vec>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:17:5 + --> tests/ui/ref_option/ref_option_traits.rs:16:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -26,7 +26,7 @@ LL | fn trait_opt(&self, a: &Option); | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:19:5 + --> tests/ui/ref_option/ref_option_traits.rs:18:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.private.stderr b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.private.stderr index 2837ee80fb2ef..cfab7fa5734c3 100644 --- a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.private.stderr +++ b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:17:5 + --> tests/ui/ref_option/ref_option_traits.rs:16:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -10,7 +10,7 @@ LL | fn trait_opt(&self, a: &Option); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:19:5 + --> tests/ui/ref_option/ref_option_traits.rs:18:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.rs b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.rs index 811da2eb4d500..4c773e84f8da8 100644 --- a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.rs +++ b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.rs @@ -3,7 +3,6 @@ //@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private //@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all -#![allow(unused, clippy::all)] #![warn(clippy::ref_option)] pub trait PubTrait { diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 501811fa491b3..ff81c64260274 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -7,82 +7,107 @@ #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_conditions)] #![allow(clippy::box_collection)] +#![allow(invalid_reference_casting)] +#![allow(suspicious_double_ref_op)] +#![allow(invalid_nan_comparisons)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] #![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] +#![allow(double_negations)] +#![allow(drop_bounds)] +#![allow(dropping_copy_types)] +#![allow(dropping_references)] #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::manual_filter_map)] #![allow(clippy::manual_find_map)] +#![allow(unpredictable_function_pointer_comparisons)] +#![allow(useless_ptr_null_checks)] +#![allow(for_loops_over_fallibles)] +#![allow(forgetting_copy_types)] +#![allow(forgetting_references)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] #![allow(clippy::non_canonical_clone_impl)] #![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] +#![allow(array_into_iter)] +#![allow(invalid_atomic_ordering)] +#![allow(invalid_null_arguments)] +#![allow(invalid_value)] +#![allow(invalid_from_utf8_unchecked)] +#![allow(let_underscore_drop)] #![allow(clippy::overly_complex_bool_expr)] +#![allow(unexpected_cfgs)] +#![allow(enum_intrinsics_non_enums)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::expect_used)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::unwrap_used)] #![allow(clippy::panicking_overflow_checks)] +#![allow(non_fmt_panics)] +#![allow(named_arguments_used_positionally)] #![allow(clippy::needless_borrow)] #![allow(clippy::reversed_empty_ranges)] #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] +#![allow(dangling_pointers_from_temporaries)] #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] -#![allow(clippy::unwrap_or_default)] -#![allow(clippy::invisible_characters)] -#![allow(invalid_reference_casting)] -#![allow(suspicious_double_ref_op)] -#![allow(invalid_nan_comparisons)] -#![allow(double_negations)] -#![allow(drop_bounds)] -#![allow(dropping_copy_types)] -#![allow(dropping_references)] -#![allow(unpredictable_function_pointer_comparisons)] -#![allow(useless_ptr_null_checks)] -#![allow(for_loops_over_fallibles)] -#![allow(forgetting_copy_types)] -#![allow(forgetting_references)] -#![allow(array_into_iter)] -#![allow(invalid_atomic_ordering)] -#![allow(invalid_value)] -#![allow(invalid_from_utf8_unchecked)] -#![allow(let_underscore_drop)] -#![allow(unexpected_cfgs)] -#![allow(enum_intrinsics_non_enums)] -#![allow(non_fmt_panics)] -#![allow(named_arguments_used_positionally)] -#![allow(dangling_pointers_from_temporaries)] +#![allow(unnecessary_transmutes)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![allow(clippy::unwrap_or_default)] #![allow(ambiguous_wide_pointer_comparisons)] +#![allow(clippy::invisible_characters)] #![warn(clippy::almost_complete_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::disallowed_names)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_expr` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_stmt` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions` #![warn(clippy::box_collection)] //~ ERROR: lint `clippy::box_vec` +#![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut` +#![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref` +#![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan` #![warn(clippy::redundant_static_lifetimes)] //~ ERROR: lint `clippy::const_static_lifetime` #![warn(clippy::cognitive_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity` #![warn(clippy::derived_hash_with_manual_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq` #![warn(clippy::disallowed_methods)] //~ ERROR: lint `clippy::disallowed_method` #![warn(clippy::disallowed_types)] //~ ERROR: lint `clippy::disallowed_type` +#![warn(double_negations)] //~ ERROR: lint `clippy::double_neg` +#![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` +#![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` +#![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` #![warn(clippy::mixed_read_write_in_expression)] //~ ERROR: lint `clippy::eval_order_dependence` #![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map` #![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` +#![warn(unpredictable_function_pointer_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` +#![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` +#![warn(forgetting_copy_types)] //~ ERROR: lint `clippy::forget_copy` +#![warn(forgetting_references)] //~ ERROR: lint `clippy::forget_ref` #![warn(clippy::useless_conversion)] //~ ERROR: lint `clippy::identity_conversion` #![warn(clippy::redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` #![warn(clippy::match_result_ok)] //~ ERROR: lint `clippy::if_let_some_result` #![warn(clippy::non_canonical_clone_impl)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type` #![warn(clippy::non_canonical_partial_ord_impl)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type` #![warn(clippy::arithmetic_side_effects)] //~ ERROR: lint `clippy::integer_arithmetic` +#![warn(array_into_iter)] //~ ERROR: lint `clippy::into_iter_on_array` +#![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` +#![warn(invalid_null_arguments)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` +#![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` +#![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(clippy::overly_complex_bool_expr)] //~ ERROR: lint `clippy::logic_bug` +#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` +#![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` +#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` #![warn(clippy::new_without_default)] //~ ERROR: lint `clippy::new_without_default_derive` #![warn(clippy::bind_instead_of_map)] //~ ERROR: lint `clippy::option_and_then_some` #![warn(clippy::expect_used)] //~ ERROR: lint `clippy::option_expect_used` @@ -90,6 +115,8 @@ #![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or_else` #![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used` #![warn(clippy::panicking_overflow_checks)] //~ ERROR: lint `clippy::overflow_check_conditional` +#![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params` +#![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters` #![warn(clippy::needless_borrow)] //~ ERROR: lint `clippy::ref_in_deref` #![warn(clippy::expect_used)] //~ ERROR: lint `clippy::result_expect_used` #![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` @@ -97,38 +124,18 @@ #![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` #![warn(clippy::single_char_add_str)] //~ ERROR: lint `clippy::single_char_push_str` #![warn(clippy::module_name_repetitions)] //~ ERROR: lint `clippy::stutter` +#![warn(dangling_pointers_from_temporaries)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` #![warn(clippy::missing_const_for_thread_local)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` #![warn(clippy::recursive_format_impl)] //~ ERROR: lint `clippy::to_string_in_display` -#![warn(clippy::unwrap_or_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` -#![warn(clippy::invisible_characters)] //~ ERROR: lint `clippy::zero_width_space` -#![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut` -#![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref` -#![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan` -#![warn(double_negations)] //~ ERROR: lint `clippy::double_neg` -#![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` -#![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` -#![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` -#![warn(unpredictable_function_pointer_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` -#![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check` -#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option` -#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result` -#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` -#![warn(forgetting_copy_types)] //~ ERROR: lint `clippy::forget_copy` -#![warn(forgetting_references)] //~ ERROR: lint `clippy::forget_ref` -#![warn(array_into_iter)] //~ ERROR: lint `clippy::into_iter_on_array` -#![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` -#![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` -#![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` -#![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` -#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` -#![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` -#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` -#![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params` -#![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters` -#![warn(dangling_pointers_from_temporaries)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_float_to_int` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_char` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_float` +#![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` #![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` +#![warn(clippy::unwrap_or_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` #![warn(ambiguous_wide_pointer_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` +#![warn(clippy::invisible_characters)] //~ ERROR: lint `clippy::zero_width_space` fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 7f4b8062e1b4b..b5d5d07e639a0 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -7,82 +7,107 @@ #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_conditions)] #![allow(clippy::box_collection)] +#![allow(invalid_reference_casting)] +#![allow(suspicious_double_ref_op)] +#![allow(invalid_nan_comparisons)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] #![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] +#![allow(double_negations)] +#![allow(drop_bounds)] +#![allow(dropping_copy_types)] +#![allow(dropping_references)] #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::manual_filter_map)] #![allow(clippy::manual_find_map)] +#![allow(unpredictable_function_pointer_comparisons)] +#![allow(useless_ptr_null_checks)] +#![allow(for_loops_over_fallibles)] +#![allow(forgetting_copy_types)] +#![allow(forgetting_references)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] #![allow(clippy::non_canonical_clone_impl)] #![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] +#![allow(array_into_iter)] +#![allow(invalid_atomic_ordering)] +#![allow(invalid_null_arguments)] +#![allow(invalid_value)] +#![allow(invalid_from_utf8_unchecked)] +#![allow(let_underscore_drop)] #![allow(clippy::overly_complex_bool_expr)] +#![allow(unexpected_cfgs)] +#![allow(enum_intrinsics_non_enums)] #![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::expect_used)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::unwrap_used)] #![allow(clippy::panicking_overflow_checks)] +#![allow(non_fmt_panics)] +#![allow(named_arguments_used_positionally)] #![allow(clippy::needless_borrow)] #![allow(clippy::reversed_empty_ranges)] #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] +#![allow(dangling_pointers_from_temporaries)] #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] -#![allow(clippy::unwrap_or_default)] -#![allow(clippy::invisible_characters)] -#![allow(invalid_reference_casting)] -#![allow(suspicious_double_ref_op)] -#![allow(invalid_nan_comparisons)] -#![allow(double_negations)] -#![allow(drop_bounds)] -#![allow(dropping_copy_types)] -#![allow(dropping_references)] -#![allow(unpredictable_function_pointer_comparisons)] -#![allow(useless_ptr_null_checks)] -#![allow(for_loops_over_fallibles)] -#![allow(forgetting_copy_types)] -#![allow(forgetting_references)] -#![allow(array_into_iter)] -#![allow(invalid_atomic_ordering)] -#![allow(invalid_value)] -#![allow(invalid_from_utf8_unchecked)] -#![allow(let_underscore_drop)] -#![allow(unexpected_cfgs)] -#![allow(enum_intrinsics_non_enums)] -#![allow(non_fmt_panics)] -#![allow(named_arguments_used_positionally)] -#![allow(dangling_pointers_from_temporaries)] +#![allow(unnecessary_transmutes)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![allow(clippy::unwrap_or_default)] #![allow(ambiguous_wide_pointer_comparisons)] +#![allow(clippy::invisible_characters)] #![warn(clippy::almost_complete_letter_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::blacklisted_name)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::block_in_if_condition_expr)] //~ ERROR: lint `clippy::block_in_if_condition_expr` #![warn(clippy::block_in_if_condition_stmt)] //~ ERROR: lint `clippy::block_in_if_condition_stmt` #![warn(clippy::blocks_in_if_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions` #![warn(clippy::box_vec)] //~ ERROR: lint `clippy::box_vec` +#![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut` +#![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref` +#![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan` #![warn(clippy::const_static_lifetime)] //~ ERROR: lint `clippy::const_static_lifetime` #![warn(clippy::cyclomatic_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity` #![warn(clippy::derive_hash_xor_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq` #![warn(clippy::disallowed_method)] //~ ERROR: lint `clippy::disallowed_method` #![warn(clippy::disallowed_type)] //~ ERROR: lint `clippy::disallowed_type` +#![warn(clippy::double_neg)] //~ ERROR: lint `clippy::double_neg` +#![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` +#![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` +#![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` #![warn(clippy::eval_order_dependence)] //~ ERROR: lint `clippy::eval_order_dependence` #![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map` #![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` +#![warn(clippy::fn_address_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` +#![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check` +#![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option` +#![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result` +#![warn(clippy::for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` +#![warn(clippy::forget_copy)] //~ ERROR: lint `clippy::forget_copy` +#![warn(clippy::forget_ref)] //~ ERROR: lint `clippy::forget_ref` #![warn(clippy::identity_conversion)] //~ ERROR: lint `clippy::identity_conversion` #![warn(clippy::if_let_redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` #![warn(clippy::if_let_some_result)] //~ ERROR: lint `clippy::if_let_some_result` #![warn(clippy::incorrect_clone_impl_on_copy_type)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type` #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type` #![warn(clippy::integer_arithmetic)] //~ ERROR: lint `clippy::integer_arithmetic` +#![warn(clippy::into_iter_on_array)] //~ ERROR: lint `clippy::into_iter_on_array` +#![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` +#![warn(clippy::invalid_null_ptr_usage)] //~ ERROR: lint `clippy::invalid_null_ptr_usage` +#![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` +#![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` #![warn(clippy::logic_bug)] //~ ERROR: lint `clippy::logic_bug` +#![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` +#![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` +#![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os` #![warn(clippy::new_without_default_derive)] //~ ERROR: lint `clippy::new_without_default_derive` #![warn(clippy::option_and_then_some)] //~ ERROR: lint `clippy::option_and_then_some` #![warn(clippy::option_expect_used)] //~ ERROR: lint `clippy::option_expect_used` @@ -90,6 +115,8 @@ #![warn(clippy::option_map_unwrap_or_else)] //~ ERROR: lint `clippy::option_map_unwrap_or_else` #![warn(clippy::option_unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used` #![warn(clippy::overflow_check_conditional)] //~ ERROR: lint `clippy::overflow_check_conditional` +#![warn(clippy::panic_params)] //~ ERROR: lint `clippy::panic_params` +#![warn(clippy::positional_named_format_parameters)] //~ ERROR: lint `clippy::positional_named_format_parameters` #![warn(clippy::ref_in_deref)] //~ ERROR: lint `clippy::ref_in_deref` #![warn(clippy::result_expect_used)] //~ ERROR: lint `clippy::result_expect_used` #![warn(clippy::result_map_unwrap_or_else)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` @@ -97,38 +124,18 @@ #![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` #![warn(clippy::single_char_push_str)] //~ ERROR: lint `clippy::single_char_push_str` #![warn(clippy::stutter)] //~ ERROR: lint `clippy::stutter` +#![warn(clippy::temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` #![warn(clippy::thread_local_initializer_can_be_made_const)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` #![warn(clippy::to_string_in_display)] //~ ERROR: lint `clippy::to_string_in_display` -#![warn(clippy::unwrap_or_else_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` -#![warn(clippy::zero_width_space)] //~ ERROR: lint `clippy::zero_width_space` -#![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut` -#![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref` -#![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan` -#![warn(clippy::double_neg)] //~ ERROR: lint `clippy::double_neg` -#![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` -#![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` -#![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` -#![warn(clippy::fn_address_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` -#![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check` -#![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option` -#![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result` -#![warn(clippy::for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` -#![warn(clippy::forget_copy)] //~ ERROR: lint `clippy::forget_copy` -#![warn(clippy::forget_ref)] //~ ERROR: lint `clippy::forget_ref` -#![warn(clippy::into_iter_on_array)] //~ ERROR: lint `clippy::into_iter_on_array` -#![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` -#![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` -#![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` -#![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` -#![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` -#![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` -#![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os` -#![warn(clippy::panic_params)] //~ ERROR: lint `clippy::panic_params` -#![warn(clippy::positional_named_format_parameters)] //~ ERROR: lint `clippy::positional_named_format_parameters` -#![warn(clippy::temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(clippy::transmute_float_to_int)] //~ ERROR: lint `clippy::transmute_float_to_int` +#![warn(clippy::transmute_int_to_char)] //~ ERROR: lint `clippy::transmute_int_to_char` +#![warn(clippy::transmute_int_to_float)] //~ ERROR: lint `clippy::transmute_int_to_float` +#![warn(clippy::transmute_num_to_bytes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` #![warn(clippy::undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` +#![warn(clippy::unwrap_or_else_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` #![warn(clippy::vtable_address_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` +#![warn(clippy::zero_width_space)] //~ ERROR: lint `clippy::zero_width_space` fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index f24eaec3917ad..2487dfc8eba44 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:65:9 + --> tests/ui/rename.rs:67:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,406 +8,436 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:66:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` +error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` + --> tests/ui/rename.rs:73:9 + | +LL | #![warn(clippy::cast_ref_to_mut)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` + +error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` + --> tests/ui/rename.rs:74:9 + | +LL | #![warn(clippy::clone_double_ref)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` + +error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` + --> tests/ui/rename.rs:75:9 + | +LL | #![warn(clippy::cmp_nan)] + | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` + error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` -error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:76:9 - | -LL | #![warn(clippy::eval_order_dependence)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` - -error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:77:9 - | -LL | #![warn(clippy::filter_map)] - | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` - -error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:78:9 - | -LL | #![warn(clippy::find_map)] - | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` - -error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:79:9 - | -LL | #![warn(clippy::identity_conversion)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` - -error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` - --> tests/ui/rename.rs:80:9 - | -LL | #![warn(clippy::if_let_redundant_pattern_matching)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` - -error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` +error: lint `clippy::double_neg` has been renamed to `double_negations` --> tests/ui/rename.rs:81:9 | -LL | #![warn(clippy::if_let_some_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` +LL | #![warn(clippy::double_neg)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` -error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` +error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` --> tests/ui/rename.rs:82:9 | -LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` +LL | #![warn(clippy::drop_bounds)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` +error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` --> tests/ui/rename.rs:83:9 | -LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` +LL | #![warn(clippy::drop_copy)] + | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` -error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` +error: lint `clippy::drop_ref` has been renamed to `dropping_references` --> tests/ui/rename.rs:84:9 | -LL | #![warn(clippy::integer_arithmetic)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` +LL | #![warn(clippy::drop_ref)] + | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` -error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` +error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` --> tests/ui/rename.rs:85:9 | -LL | #![warn(clippy::logic_bug)] - | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` +LL | #![warn(clippy::eval_order_dependence)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` -error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` +error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` --> tests/ui/rename.rs:86:9 | -LL | #![warn(clippy::new_without_default_derive)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` +LL | #![warn(clippy::filter_map)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` -error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` +error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` --> tests/ui/rename.rs:87:9 | -LL | #![warn(clippy::option_and_then_some)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` +LL | #![warn(clippy::find_map)] + | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` -error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` +error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` --> tests/ui/rename.rs:88:9 | -LL | #![warn(clippy::option_expect_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` +LL | #![warn(clippy::fn_address_comparisons)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` -error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` --> tests/ui/rename.rs:89:9 | -LL | #![warn(clippy::option_map_unwrap_or)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::fn_null_check)] + | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` -error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` --> tests/ui/rename.rs:90:9 | -LL | #![warn(clippy::option_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::for_loop_over_option)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` +error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` --> tests/ui/rename.rs:91:9 | -LL | #![warn(clippy::option_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` +LL | #![warn(clippy::for_loop_over_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` +error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` --> tests/ui/rename.rs:92:9 | -LL | #![warn(clippy::overflow_check_conditional)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` +LL | #![warn(clippy::for_loops_over_fallibles)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` +error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` --> tests/ui/rename.rs:93:9 | -LL | #![warn(clippy::ref_in_deref)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` +LL | #![warn(clippy::forget_copy)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` -error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` +error: lint `clippy::forget_ref` has been renamed to `forgetting_references` --> tests/ui/rename.rs:94:9 | -LL | #![warn(clippy::result_expect_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` +LL | #![warn(clippy::forget_ref)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` -error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` --> tests/ui/rename.rs:95:9 | -LL | #![warn(clippy::result_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::identity_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` -error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` +error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` --> tests/ui/rename.rs:96:9 | -LL | #![warn(clippy::result_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` +LL | #![warn(clippy::if_let_redundant_pattern_matching)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` -error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` +error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` --> tests/ui/rename.rs:97:9 | -LL | #![warn(clippy::reverse_range_loop)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` +LL | #![warn(clippy::if_let_some_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` -error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` +error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` --> tests/ui/rename.rs:98:9 | -LL | #![warn(clippy::single_char_push_str)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` +LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` -error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` +error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` --> tests/ui/rename.rs:99:9 | -LL | #![warn(clippy::stutter)] - | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` +LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` -error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` +error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` --> tests/ui/rename.rs:100:9 | -LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` +LL | #![warn(clippy::integer_arithmetic)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` -error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` --> tests/ui/rename.rs:101:9 | -LL | #![warn(clippy::to_string_in_display)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` +LL | #![warn(clippy::into_iter_on_array)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` -error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` +error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` --> tests/ui/rename.rs:102:9 | -LL | #![warn(clippy::unwrap_or_else_default)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` +LL | #![warn(clippy::invalid_atomic_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` -error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` +error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` --> tests/ui/rename.rs:103:9 | -LL | #![warn(clippy::zero_width_space)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` +LL | #![warn(clippy::invalid_null_ptr_usage)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` -error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` +error: lint `clippy::invalid_ref` has been renamed to `invalid_value` --> tests/ui/rename.rs:104:9 | -LL | #![warn(clippy::cast_ref_to_mut)] - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` +LL | #![warn(clippy::invalid_ref)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` -error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` +error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` --> tests/ui/rename.rs:105:9 | -LL | #![warn(clippy::clone_double_ref)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` +LL | #![warn(clippy::invalid_utf8_in_unchecked)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` -error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` +error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` --> tests/ui/rename.rs:106:9 | -LL | #![warn(clippy::cmp_nan)] - | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` +LL | #![warn(clippy::let_underscore_drop)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` -error: lint `clippy::double_neg` has been renamed to `double_negations` +error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` --> tests/ui/rename.rs:107:9 | -LL | #![warn(clippy::double_neg)] - | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` +LL | #![warn(clippy::logic_bug)] + | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` -error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` +error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` --> tests/ui/rename.rs:108:9 | -LL | #![warn(clippy::drop_bounds)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` +LL | #![warn(clippy::maybe_misused_cfg)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` -error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` +error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` --> tests/ui/rename.rs:109:9 | -LL | #![warn(clippy::drop_copy)] - | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` +LL | #![warn(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` -error: lint `clippy::drop_ref` has been renamed to `dropping_references` +error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` --> tests/ui/rename.rs:110:9 | -LL | #![warn(clippy::drop_ref)] - | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` +LL | #![warn(clippy::mismatched_target_os)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` -error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` +error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` --> tests/ui/rename.rs:111:9 | -LL | #![warn(clippy::fn_address_comparisons)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` +LL | #![warn(clippy::new_without_default_derive)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` -error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` +error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` --> tests/ui/rename.rs:112:9 | -LL | #![warn(clippy::fn_null_check)] - | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` +LL | #![warn(clippy::option_and_then_some)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` -error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` +error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` --> tests/ui/rename.rs:113:9 | -LL | #![warn(clippy::for_loop_over_option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` +LL | #![warn(clippy::option_expect_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` -error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` +error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` --> tests/ui/rename.rs:114:9 | -LL | #![warn(clippy::for_loop_over_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` +LL | #![warn(clippy::option_map_unwrap_or)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` +error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` --> tests/ui/rename.rs:115:9 | -LL | #![warn(clippy::for_loops_over_fallibles)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` +LL | #![warn(clippy::option_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` +error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` --> tests/ui/rename.rs:116:9 | -LL | #![warn(clippy::forget_copy)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` +LL | #![warn(clippy::option_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` -error: lint `clippy::forget_ref` has been renamed to `forgetting_references` +error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` --> tests/ui/rename.rs:117:9 | -LL | #![warn(clippy::forget_ref)] - | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` +LL | #![warn(clippy::overflow_check_conditional)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` -error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` +error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` --> tests/ui/rename.rs:118:9 | -LL | #![warn(clippy::into_iter_on_array)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` +LL | #![warn(clippy::panic_params)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` -error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` +error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` --> tests/ui/rename.rs:119:9 | -LL | #![warn(clippy::invalid_atomic_ordering)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` +LL | #![warn(clippy::positional_named_format_parameters)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` -error: lint `clippy::invalid_ref` has been renamed to `invalid_value` +error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` --> tests/ui/rename.rs:120:9 | -LL | #![warn(clippy::invalid_ref)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` +LL | #![warn(clippy::ref_in_deref)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` -error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` +error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` --> tests/ui/rename.rs:121:9 | -LL | #![warn(clippy::invalid_utf8_in_unchecked)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` +LL | #![warn(clippy::result_expect_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` -error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` +error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` --> tests/ui/rename.rs:122:9 | -LL | #![warn(clippy::let_underscore_drop)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` +LL | #![warn(clippy::result_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` +error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` --> tests/ui/rename.rs:123:9 | -LL | #![warn(clippy::maybe_misused_cfg)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` +LL | #![warn(clippy::result_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` -error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` +error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` --> tests/ui/rename.rs:124:9 | -LL | #![warn(clippy::mem_discriminant_non_enum)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` +LL | #![warn(clippy::reverse_range_loop)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` -error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` +error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` --> tests/ui/rename.rs:125:9 | -LL | #![warn(clippy::mismatched_target_os)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` +LL | #![warn(clippy::single_char_push_str)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` -error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` +error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` --> tests/ui/rename.rs:126:9 | -LL | #![warn(clippy::panic_params)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` +LL | #![warn(clippy::stutter)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` -error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` --> tests/ui/rename.rs:127:9 | -LL | #![warn(clippy::positional_named_format_parameters)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` +LL | #![warn(clippy::temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` -error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` +error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` --> tests/ui/rename.rs:128:9 | -LL | #![warn(clippy::temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` +LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` -error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` +error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` --> tests/ui/rename.rs:129:9 | +LL | #![warn(clippy::to_string_in_display)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` + +error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:130:9 + | +LL | #![warn(clippy::transmute_float_to_int)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:131:9 + | +LL | #![warn(clippy::transmute_int_to_char)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:132:9 + | +LL | #![warn(clippy::transmute_int_to_float)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` + --> tests/ui/rename.rs:133:9 + | +LL | #![warn(clippy::transmute_num_to_bytes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` + +error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` + --> tests/ui/rename.rs:134:9 + | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:135:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:136:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` +error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` + --> tests/ui/rename.rs:137:9 + | +LL | #![warn(clippy::unwrap_or_else_default)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` + error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:138:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` -error: aborting due to 68 previous errors +error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` + --> tests/ui/rename.rs:139:9 + | +LL | #![warn(clippy::zero_width_space)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` + +error: aborting due to 73 previous errors diff --git a/src/tools/clippy/tests/ui/repr_packed_without_abi.stderr b/src/tools/clippy/tests/ui/repr_packed_without_abi.stderr index d1078b3e8e48e..f688e4bc744ae 100644 --- a/src/tools/clippy/tests/ui/repr_packed_without_abi.stderr +++ b/src/tools/clippy/tests/ui/repr_packed_without_abi.stderr @@ -11,7 +11,7 @@ LL | | } | |_^ | = warning: unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI - = help: qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` + = help: qualify the desired ABI explicitly via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` note: the lint level is defined here --> tests/ui/repr_packed_without_abi.rs:1:9 | @@ -31,7 +31,7 @@ LL | | } | |_^ | = warning: unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI - = help: qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` + = help: qualify the desired ABI explicitly via `#[repr(C, packed)]` or `#[repr(Rust, packed)]` error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/result_unit_error_no_std.rs b/src/tools/clippy/tests/ui/result_unit_error_no_std.rs index 8a1849b8490ab..a64e8414d78fe 100644 --- a/src/tools/clippy/tests/ui/result_unit_error_no_std.rs +++ b/src/tools/clippy/tests/ui/result_unit_error_no_std.rs @@ -14,7 +14,7 @@ pub fn returns_unit_error_lint() -> Result { Err(()) } -#[no_mangle] +#[unsafe(no_mangle)] extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { 0 } diff --git a/src/tools/clippy/tests/ui/return_and_then.fixed b/src/tools/clippy/tests/ui/return_and_then.fixed index 74efa14eeec81..8d9481d159512 100644 --- a/src/tools/clippy/tests/ui/return_and_then.fixed +++ b/src/tools/clippy/tests/ui/return_and_then.fixed @@ -67,8 +67,60 @@ fn main() { .first() // creates temporary reference .and_then(|x| test_opt_block(Some(*x))) } + + fn in_closure() -> bool { + let _ = || { + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + //~^ return_and_then + }; + true + } + + fn with_return(shortcut: bool) -> Option { + if shortcut { + return { + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + }; + //~^ return_and_then + }; + None + } + + fn with_return_multiline(shortcut: bool) -> Option { + if shortcut { + return { + let mut x = Some("")?; + let x = format!("{x}."); + if x.len() > 2 { Some(3) } else { None } + }; + //~^^^^ return_and_then + }; + None + } } fn gen_option(n: i32) -> Option { Some(n) } + +mod issue14781 { + fn foo(_: &str, _: (u32, u32)) -> Result<(u32, u32), ()> { + Ok((1, 1)) + } + + fn bug(_: Option<&str>) -> Result<(), ()> { + let year: Option<&str> = None; + let month: Option<&str> = None; + let day: Option<&str> = None; + + let _day = if let (Some(year), Some(month)) = (year, month) { + day.and_then(|day| foo(day, (1, 31)).ok()) + } else { + None + }; + + Ok(()) + } +} diff --git a/src/tools/clippy/tests/ui/return_and_then.rs b/src/tools/clippy/tests/ui/return_and_then.rs index 188dc57e588cf..beada921a9187 100644 --- a/src/tools/clippy/tests/ui/return_and_then.rs +++ b/src/tools/clippy/tests/ui/return_and_then.rs @@ -63,8 +63,55 @@ fn main() { .first() // creates temporary reference .and_then(|x| test_opt_block(Some(*x))) } + + fn in_closure() -> bool { + let _ = || { + Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) + //~^ return_and_then + }; + true + } + + fn with_return(shortcut: bool) -> Option { + if shortcut { + return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }); + //~^ return_and_then + }; + None + } + + fn with_return_multiline(shortcut: bool) -> Option { + if shortcut { + return Some("").and_then(|mut x| { + let x = format!("{x}."); + if x.len() > 2 { Some(3) } else { None } + }); + //~^^^^ return_and_then + }; + None + } } fn gen_option(n: i32) -> Option { Some(n) } + +mod issue14781 { + fn foo(_: &str, _: (u32, u32)) -> Result<(u32, u32), ()> { + Ok((1, 1)) + } + + fn bug(_: Option<&str>) -> Result<(), ()> { + let year: Option<&str> = None; + let month: Option<&str> = None; + let day: Option<&str> = None; + + let _day = if let (Some(year), Some(month)) = (year, month) { + day.and_then(|day| foo(day, (1, 31)).ok()) + } else { + None + }; + + Ok(()) + } +} diff --git a/src/tools/clippy/tests/ui/return_and_then.stderr b/src/tools/clippy/tests/ui/return_and_then.stderr index cc611c3dba679..5feca88286057 100644 --- a/src/tools/clippy/tests/ui/return_and_then.stderr +++ b/src/tools/clippy/tests/ui/return_and_then.stderr @@ -1,4 +1,4 @@ -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:5:9 | LL | / opt.and_then(|n| { @@ -20,7 +20,7 @@ LL + ret += n; LL + if n > 1 { Some(ret) } else { None } | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:14:9 | LL | opt.and_then(|n| test_opt_block(Some(n))) @@ -32,7 +32,7 @@ LL ~ let n = opt?; LL + test_opt_block(Some(n)) | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:19:9 | LL | gen_option(1).and_then(|n| test_opt_block(Some(n))) @@ -44,7 +44,7 @@ LL ~ let n = gen_option(1)?; LL + test_opt_block(Some(n)) | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:24:9 | LL | opt.and_then(|n| if n > 1 { Ok(n + 1) } else { Err(n) }) @@ -56,7 +56,7 @@ LL ~ let n = opt?; LL + if n > 1 { Ok(n + 1) } else { Err(n) } | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:29:9 | LL | opt.and_then(|n| test_res_block(Ok(n))) @@ -68,7 +68,7 @@ LL ~ let n = opt?; LL + test_res_block(Ok(n)) | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:35:9 | LL | Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) @@ -80,7 +80,7 @@ LL ~ let x = Some("")?; LL + if x.len() > 2 { Some(3) } else { None } | -error: use the question mark operator instead of an `and_then` call +error: use the `?` operator instead of an `and_then` call --> tests/ui/return_and_then.rs:41:9 | LL | / Some(match (vec![1, 2, 3], vec![1, 2, 4]) { @@ -101,5 +101,50 @@ LL + })?; LL + if x.len() > 2 { Some(3) } else { None } | -error: aborting due to 7 previous errors +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:69:13 + | +LL | Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:77:20 + | +LL | return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ return { +LL + let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } +LL ~ }; + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:85:20 + | +LL | return Some("").and_then(|mut x| { + | ____________________^ +LL | | let x = format!("{x}."); +LL | | if x.len() > 2 { Some(3) } else { None } +LL | | }); + | |______________^ + | +help: try + | +LL ~ return { +LL + let mut x = Some("")?; +LL + let x = format!("{x}."); +LL + if x.len() > 2 { Some(3) } else { None } +LL ~ }; + | + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs index b7ed3aab00434..3f205b322ab08 100644 --- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs @@ -3,12 +3,7 @@ // ifs_same_cond warning is different from `ifs_same_cond`. // clippy::if_same_then_else, clippy::comparison_chain -- all empty blocks #![allow(incomplete_features)] -#![allow( - clippy::comparison_chain, - clippy::if_same_then_else, - clippy::ifs_same_cond, - clippy::uninlined_format_args -)] +#![allow(clippy::if_same_then_else, clippy::ifs_same_cond, clippy::uninlined_format_args)] use std::marker::ConstParamTy; @@ -36,34 +31,34 @@ fn ifs_same_cond_fn() { let obj = Struct; if function() { - } else if function() { //~^ same_functions_in_if_condition + } else if function() { } if fn_arg(a) { - } else if fn_arg(a) { //~^ same_functions_in_if_condition + } else if fn_arg(a) { } if obj.method() { - } else if obj.method() { //~^ same_functions_in_if_condition + } else if obj.method() { } if obj.method_arg(a) { - } else if obj.method_arg(a) { //~^ same_functions_in_if_condition + } else if obj.method_arg(a) { } let mut v = vec![1]; if v.pop().is_none() { - } else if v.pop().is_none() { //~^ same_functions_in_if_condition + } else if v.pop().is_none() { } if v.len() == 42 { - } else if v.len() == 42 { //~^ same_functions_in_if_condition + } else if v.len() == 42 { } if v.len() == 1 { diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr index 6cd4f96c13e32..59f4511757d61 100644 --- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr @@ -1,79 +1,62 @@ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:39:15 +error: these `if` branches have the same function call + --> tests/ui/same_functions_in_if_condition.rs:33:8 | +LL | if function() { + | ^^^^^^^^^^ +LL | LL | } else if function() { | ^^^^^^^^^^ | -note: same as this - --> tests/ui/same_functions_in_if_condition.rs:38:8 - | -LL | if function() { - | ^^^^^^^^^^ note: the lint level is defined here --> tests/ui/same_functions_in_if_condition.rs:2:9 | LL | #![deny(clippy::same_functions_in_if_condition)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:44:15 - | -LL | } else if fn_arg(a) { - | ^^^^^^^^^ - | -note: same as this - --> tests/ui/same_functions_in_if_condition.rs:43:8 +error: these `if` branches have the same function call + --> tests/ui/same_functions_in_if_condition.rs:38:8 | LL | if fn_arg(a) { | ^^^^^^^^^ +LL | +LL | } else if fn_arg(a) { + | ^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:49:15 - | -LL | } else if obj.method() { - | ^^^^^^^^^^^^ - | -note: same as this - --> tests/ui/same_functions_in_if_condition.rs:48:8 +error: these `if` branches have the same function call + --> tests/ui/same_functions_in_if_condition.rs:43:8 | LL | if obj.method() { | ^^^^^^^^^^^^ +LL | +LL | } else if obj.method() { + | ^^^^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:54:15 - | -LL | } else if obj.method_arg(a) { - | ^^^^^^^^^^^^^^^^^ - | -note: same as this - --> tests/ui/same_functions_in_if_condition.rs:53:8 +error: these `if` branches have the same function call + --> tests/ui/same_functions_in_if_condition.rs:48:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ - -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:60:15 - | -LL | } else if v.pop().is_none() { +LL | +LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ - | -note: same as this - --> tests/ui/same_functions_in_if_condition.rs:59:8 + +error: these `if` branches have the same function call + --> tests/ui/same_functions_in_if_condition.rs:54:8 | LL | if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ +LL | +LL | } else if v.pop().is_none() { + | ^^^^^^^^^^^^^^^^^ -error: this `if` has the same function call as a previous `if` - --> tests/ui/same_functions_in_if_condition.rs:65:15 - | -LL | } else if v.len() == 42 { - | ^^^^^^^^^^^^^ - | -note: same as this - --> tests/ui/same_functions_in_if_condition.rs:64:8 +error: these `if` branches have the same function call + --> tests/ui/same_functions_in_if_condition.rs:59:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ +LL | +LL | } else if v.len() == 42 { + | ^^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/same_name_method.stderr b/src/tools/clippy/tests/ui/same_name_method.stderr index fefdb5c9c23d0..b2624ac4d2644 100644 --- a/src/tools/clippy/tests/ui/same_name_method.stderr +++ b/src/tools/clippy/tests/ui/same_name_method.stderr @@ -23,7 +23,6 @@ note: existing `clone` defined here | LL | #[derive(Clone)] | ^^^^^ - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: method's name is the same as an existing method in a trait --> tests/ui/same_name_method.rs:46:13 diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed index 847e5140d3e65..cc4dbc919d81d 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed @@ -214,10 +214,9 @@ mod issue7392 { } fn ref_bindings() { - let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); - //~^ search_is_some - let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); - //~^ search_is_some + let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)] + //~^ search_is_some + .iter().any(|&&(&x, ref y)| x == *y); } fn test_string_1(s: &str) -> bool { diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs index e976d12600cc1..fa31a9ddedc66 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs @@ -221,10 +221,11 @@ mod issue7392 { } fn ref_bindings() { - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); - //~^ search_is_some - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); - //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + //~^ search_is_some + .iter() + .find(|&&&(&x, ref y)| x == *y) + .is_none(); } fn test_string_1(s: &str) -> bool { diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr index ccc17025222d9..b079cf7ea361b 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr @@ -248,116 +248,122 @@ LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none(); error: called `is_none()` after searching an `Iterator` with `find` --> tests/ui/search_is_some_fixable_none.rs:224:17 | -LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` - -error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:226:17 +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + | _________________^ +LL | | +LL | | .iter() +LL | | .find(|&&&(&x, ref y)| x == *y) +LL | | .is_none(); + | |______________________^ + | +help: consider using + | +LL ~ let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)] +LL + +LL ~ .iter().any(|&&(&x, ref y)| x == *y); | -LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:246:17 + --> tests/ui/search_is_some_fixable_none.rs:247:17 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| s[0].is_empty())` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:248:17 + --> tests/ui/search_is_some_fixable_none.rs:249:17 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| test_string_1(&s[0]))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:258:17 + --> tests/ui/search_is_some_fixable_none.rs:259:17 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| fp.field.is_power_of_two())` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:260:17 + --> tests/ui/search_is_some_fixable_none.rs:261:17 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_1(fp.field))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:262:17 + --> tests/ui/search_is_some_fixable_none.rs:263:17 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_2(*fp.field))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:279:17 + --> tests/ui/search_is_some_fixable_none.rs:280:17 | LL | let _ = v.iter().find(|x| **x == 42).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 42)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:281:17 + --> tests/ui/search_is_some_fixable_none.rs:282:17 | LL | Foo.bar(v.iter().find(|x| **x == 42).is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 42)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:287:9 + --> tests/ui/search_is_some_fixable_none.rs:288:9 | LL | v.iter().find(|x| **x == 42).is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!v.iter().any(|x| *x == 42))` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_none.rs:293:9 + --> tests/ui/search_is_some_fixable_none.rs:294:9 | LL | v.iter().find(|x| **x == 42).is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!v.iter().any(|x| *x == 42))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:299:17 + --> tests/ui/search_is_some_fixable_none.rs:300:17 | LL | let _ = s.find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:301:17 + --> tests/ui/search_is_some_fixable_none.rs:302:17 | LL | Foo.bar(s.find("world").is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:304:17 + --> tests/ui/search_is_some_fixable_none.rs:305:17 | LL | let _ = s.find("world").is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:306:17 + --> tests/ui/search_is_some_fixable_none.rs:307:17 | LL | Foo.bar(s.find("world").is_none()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s.contains("world")` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:312:17 + --> tests/ui/search_is_some_fixable_none.rs:313:17 | LL | let _ = s.find("world").is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:315:17 + --> tests/ui/search_is_some_fixable_none.rs:316:17 | LL | let _ = s.find("world").is_none().then(computations); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:321:17 + --> tests/ui/search_is_some_fixable_none.rs:322:17 | LL | let _ = s.find("world").is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` error: called `is_none()` after calling `find()` on a string - --> tests/ui/search_is_some_fixable_none.rs:324:17 + --> tests/ui/search_is_some_fixable_none.rs:325:17 | LL | let _ = s.find("world").is_none().then_some(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(!s.contains("world"))` -error: aborting due to 55 previous errors +error: aborting due to 54 previous errors diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.fixed new file mode 100644 index 0000000000000..6e15244901c28 --- /dev/null +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.fixed @@ -0,0 +1,14 @@ +//@edition: 2021 +#![warn(clippy::search_is_some)] + +fn main() { + fn ref_bindings() { + let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); + //~^ search_is_some + let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); + //~^ search_is_some + let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)] + //~^ search_is_some + .iter().any(|&&(&x, ref y)| x == *y); + } +} diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.rs new file mode 100644 index 0000000000000..4b1db3f9fc328 --- /dev/null +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.rs @@ -0,0 +1,16 @@ +//@edition: 2021 +#![warn(clippy::search_is_some)] + +fn main() { + fn ref_bindings() { + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); + //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); + //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + //~^ search_is_some + .iter() + .find(|&&&(&x, ref y)| x == *y) + .is_none(); + } +} diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.stderr new file mode 100644 index 0000000000000..af93be1a70719 --- /dev/null +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none_2021.stderr @@ -0,0 +1,35 @@ +error: called `is_none()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_none_2021.rs:6:17 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` + | + = note: `-D clippy::search-is-some` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` + +error: called `is_none()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_none_2021.rs:8:17 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` + +error: called `is_none()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_none_2021.rs:10:17 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + | _________________^ +LL | | +LL | | .iter() +LL | | .find(|&&&(&x, ref y)| x == *y) +LL | | .is_none(); + | |______________________^ + | +help: consider using + | +LL ~ let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)] +LL + +LL ~ .iter().any(|&&(&x, ref y)| x == *y); + | + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed index 05e88b8528f15..42b39b33b575c 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed @@ -214,10 +214,9 @@ mod issue7392 { } fn ref_bindings() { - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); - //~^ search_is_some - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); - //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + .iter() + .any(|&&(&x, ref y)| x == *y); } fn test_string_1(s: &str) -> bool { diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs index caab816f24361..ca4f4d941cb2f 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs @@ -220,10 +220,11 @@ mod issue7392 { } fn ref_bindings() { - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); - //~^ search_is_some - let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); - //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)] + .iter() + .find(|&&&(&x, ref y)| x == *y) + //~^ search_is_some + .is_some(); } fn test_string_1(s: &str) -> bool { diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr index af719b78831a1..8291f48d43c4d 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr @@ -227,70 +227,67 @@ LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.by_ref(&v.bar))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:223:55 + --> tests/ui/search_is_some_fixable_some.rs:225:14 | -LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` - -error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:225:55 - | -LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` +LL | .find(|&&&(&x, ref y)| x == *y) + | ______________^ +LL | | +LL | | .is_some(); + | |______________________^ help: consider using: `any(|&&(&x, ref y)| x == *y)` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:245:26 + --> tests/ui/search_is_some_fixable_some.rs:246:26 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s[0].is_empty())` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:247:26 + --> tests/ui/search_is_some_fixable_some.rs:248:26 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| test_string_1(&s[0]))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:257:26 + --> tests/ui/search_is_some_fixable_some.rs:258:26 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| fp.field.is_power_of_two())` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:259:26 + --> tests/ui/search_is_some_fixable_some.rs:260:26 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_1(fp.field))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:261:26 + --> tests/ui/search_is_some_fixable_some.rs:262:26 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_2(*fp.field))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:277:18 + --> tests/ui/search_is_some_fixable_some.rs:278:18 | LL | v.iter().find(|x: &&u32| func(x)).is_some() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| func(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:287:26 + --> tests/ui/search_is_some_fixable_some.rs:288:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_impl(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:291:26 + --> tests/ui/search_is_some_fixable_some.rs:292:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_dyn(&x))` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:295:26 + --> tests/ui/search_is_some_fixable_some.rs:296:26 | LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| (*arg_no_deref_dyn)(&x))` -error: aborting due to 47 previous errors +error: aborting due to 46 previous errors diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.fixed new file mode 100644 index 0000000000000..d2b05db562a0b --- /dev/null +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.fixed @@ -0,0 +1,11 @@ +//@edition: 2021 +#![warn(clippy::search_is_some)] + +fn main() { + fn ref_bindings() { + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); + //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y); + //~^ search_is_some + } +} diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.rs new file mode 100644 index 0000000000000..c3f5ef769dab7 --- /dev/null +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.rs @@ -0,0 +1,11 @@ +//@edition: 2021 +#![warn(clippy::search_is_some)] + +fn main() { + fn ref_bindings() { + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); + //~^ search_is_some + let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); + //~^ search_is_some + } +} diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.stderr new file mode 100644 index 0000000000000..91d9540e6fcf3 --- /dev/null +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some_2021.stderr @@ -0,0 +1,17 @@ +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_some_2021.rs:6:55 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` + | + = note: `-D clippy::search-is-some` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` + +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_some_2021.rs:8:55 + | +LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/shadow.rs b/src/tools/clippy/tests/ui/shadow.rs index 7d503a1cf6c17..05009b2ddd416 100644 --- a/src/tools/clippy/tests/ui/shadow.rs +++ b/src/tools/clippy/tests/ui/shadow.rs @@ -167,4 +167,19 @@ fn issue13795(value: Issue13795) { //~^ shadow_same } +fn issue14377() { + let a; + let b; + (a, b) = (0, 1); + + struct S { + c: i32, + d: i32, + } + + let c; + let d; + S { c, d } = S { c: 1, d: 2 }; +} + fn main() {} 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 index 4ec0f02d66451..53704f59cb999 100644 --- a/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs +++ b/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs @@ -1,6 +1,5 @@ //@ check-pass -#![warn(clippy::all, clippy::pedantic)] #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, 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 index 87b3a7d2fa0cf..e8de0e04c0c4c 100644 --- 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 @@ -1,4 +1,3 @@ -#![warn(clippy::all, clippy::pedantic)] #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, 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 index 8738b61192a3c..5609d6a21a360 100644 --- 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 @@ -1,5 +1,5 @@ error: method `add` can be confused for the standard trait method `std::ops::Add::add` - --> tests/ui/should_impl_trait/method_list_1.rs:25:5 + --> tests/ui/should_impl_trait/method_list_1.rs:24:5 | LL | / pub fn add(self, other: T) -> T { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:31:5 + --> tests/ui/should_impl_trait/method_list_1.rs:30:5 | LL | / pub fn as_mut(&mut self) -> &mut T { LL | | @@ -25,7 +25,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_1.rs:37:5 + --> tests/ui/should_impl_trait/method_list_1.rs:36:5 | LL | / pub fn as_ref(&self) -> &T { LL | | @@ -37,7 +37,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_1.rs:43:5 + --> tests/ui/should_impl_trait/method_list_1.rs:42:5 | LL | / pub fn bitand(self, rhs: T) -> T { LL | | @@ -49,7 +49,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_1.rs:49:5 + --> tests/ui/should_impl_trait/method_list_1.rs:48:5 | LL | / pub fn bitor(self, rhs: Self) -> Self { LL | | @@ -61,7 +61,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_1.rs:55:5 + --> tests/ui/should_impl_trait/method_list_1.rs:54:5 | LL | / pub fn bitxor(self, rhs: Self) -> Self { LL | | @@ -73,7 +73,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_1.rs:61:5 + --> tests/ui/should_impl_trait/method_list_1.rs:60:5 | LL | / pub fn borrow(&self) -> &str { LL | | @@ -85,7 +85,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_1.rs:67:5 + --> tests/ui/should_impl_trait/method_list_1.rs:66:5 | LL | / pub fn borrow_mut(&mut self) -> &mut str { LL | | @@ -97,7 +97,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_1.rs:73:5 + --> tests/ui/should_impl_trait/method_list_1.rs:72:5 | LL | / pub fn clone(&self) -> Self { LL | | @@ -109,7 +109,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_1.rs:79:5 + --> tests/ui/should_impl_trait/method_list_1.rs:78:5 | LL | / pub fn cmp(&self, other: &Self) -> Self { LL | | @@ -121,7 +121,7 @@ LL | | } = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name error: method `default` can be confused for the standard trait method `std::default::Default::default` - --> tests/ui/should_impl_trait/method_list_1.rs:85:5 + --> tests/ui/should_impl_trait/method_list_1.rs:84:5 | LL | / pub fn default() -> Self { LL | | @@ -133,7 +133,7 @@ LL | | } = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` - --> tests/ui/should_impl_trait/method_list_1.rs:91:5 + --> tests/ui/should_impl_trait/method_list_1.rs:90:5 | LL | / pub fn deref(&self) -> &Self { LL | | @@ -145,7 +145,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_1.rs:97:5 + --> tests/ui/should_impl_trait/method_list_1.rs:96:5 | LL | / pub fn deref_mut(&mut self) -> &mut Self { LL | | @@ -157,7 +157,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_1.rs:103:5 + --> tests/ui/should_impl_trait/method_list_1.rs:102:5 | LL | / pub fn div(self, rhs: Self) -> Self { LL | | @@ -169,7 +169,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_1.rs:109:5 + --> tests/ui/should_impl_trait/method_list_1.rs:108:5 | LL | / pub fn drop(&mut self) { LL | | 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 index f0c4d4f15cb63..1f25ab3938a3d 100644 --- 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 @@ -1,4 +1,3 @@ -#![warn(clippy::all, clippy::pedantic)] #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, 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 index 85de74337020d..0f5818507779f 100644 --- 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 @@ -1,5 +1,5 @@ error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` - --> tests/ui/should_impl_trait/method_list_2.rs:26:5 + --> tests/ui/should_impl_trait/method_list_2.rs:25:5 | LL | / pub fn eq(&self, other: &Self) -> bool { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter` - --> tests/ui/should_impl_trait/method_list_2.rs:32:5 + --> tests/ui/should_impl_trait/method_list_2.rs:31:5 | LL | / pub fn from_iter(iter: T) -> Self { LL | | @@ -25,7 +25,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:38:5 + --> tests/ui/should_impl_trait/method_list_2.rs:37:5 | LL | / pub fn from_str(s: &str) -> Result { LL | | @@ -37,7 +37,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:44:5 + --> tests/ui/should_impl_trait/method_list_2.rs:43:5 | LL | / pub fn hash(&self, state: &mut T) { LL | | @@ -49,7 +49,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:50:5 + --> tests/ui/should_impl_trait/method_list_2.rs:49:5 | LL | / pub fn index(&self, index: usize) -> &Self { LL | | @@ -61,7 +61,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:56:5 + --> tests/ui/should_impl_trait/method_list_2.rs:55:5 | LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { LL | | @@ -73,7 +73,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:62:5 + --> tests/ui/should_impl_trait/method_list_2.rs:61:5 | LL | / pub fn into_iter(self) -> Self { LL | | @@ -85,7 +85,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:68:5 + --> tests/ui/should_impl_trait/method_list_2.rs:67:5 | LL | / pub fn mul(self, rhs: Self) -> Self { LL | | @@ -97,7 +97,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:74:5 + --> tests/ui/should_impl_trait/method_list_2.rs:73:5 | LL | / pub fn neg(self) -> Self { LL | | @@ -109,7 +109,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:80:5 + --> tests/ui/should_impl_trait/method_list_2.rs:79:5 | LL | / pub fn next(&mut self) -> Option { LL | | @@ -121,7 +121,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:86:5 + --> tests/ui/should_impl_trait/method_list_2.rs:85:5 | LL | / pub fn not(self) -> Self { LL | | @@ -133,7 +133,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:92:5 + --> tests/ui/should_impl_trait/method_list_2.rs:91:5 | LL | / pub fn rem(self, rhs: Self) -> Self { LL | | @@ -145,7 +145,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:98:5 + --> tests/ui/should_impl_trait/method_list_2.rs:97:5 | LL | / pub fn shl(self, rhs: Self) -> Self { LL | | @@ -157,7 +157,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:104:5 + --> tests/ui/should_impl_trait/method_list_2.rs:103:5 | LL | / pub fn shr(self, rhs: Self) -> Self { LL | | @@ -169,7 +169,7 @@ 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` - --> tests/ui/should_impl_trait/method_list_2.rs:110:5 + --> tests/ui/should_impl_trait/method_list_2.rs:109:5 | LL | / pub fn sub(self, rhs: Self) -> Self { LL | | diff --git a/src/tools/clippy/tests/ui/single_call_fn.rs b/src/tools/clippy/tests/ui/single_call_fn.rs index c1cc4032bec99..a1ecd7bc166cf 100644 --- a/src/tools/clippy/tests/ui/single_call_fn.rs +++ b/src/tools/clippy/tests/ui/single_call_fn.rs @@ -94,7 +94,7 @@ trait Trait { //~^ single_call_fn fn foo(&self); } -extern "C" { +unsafe extern "C" { // test some kind of foreign item fn rand() -> std::ffi::c_int; } diff --git a/src/tools/clippy/tests/ui/single_match.fixed b/src/tools/clippy/tests/ui/single_match.fixed index c6ffe93eb7ab3..db5107600ee6d 100644 --- a/src/tools/clippy/tests/ui/single_match.fixed +++ b/src/tools/clippy/tests/ui/single_match.fixed @@ -1,3 +1,4 @@ +//@require-annotations-for-level: WARN #![warn(clippy::single_match)] #![allow( unused, @@ -18,13 +19,9 @@ fn single_match() { //~^^^^^^ single_match let x = Some(1u8); - match x { - // Note the missing block braces. - // We suggest `if let Some(y) = x { .. }` because the macro - // is expanded before we can do anything. - Some(y) => println!("{:?}", y), - _ => (), - } + if let Some(y) = x { println!("{:?}", y) } + //~^^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` let z = (1u8, 1u8); if let (2..=3, 7..=9) = z { dummy() }; @@ -358,21 +355,50 @@ fn irrefutable_match() { let mut x = vec![1i8]; - // Should not lint. - match x.pop() { + if let Some(u) = x.pop() { println!("{u}") } + //~^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` + + if let Some(u) = x.pop() { // bla - Some(u) => println!("{u}"), - // more comments! - None => {}, + println!("{u}"); } - // Should not lint. - match x.pop() { - // bla - Some(u) => { - // bla - println!("{u}"); - }, - // bla - None => {}, + //~^^^^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` +} + +fn issue_14493() { + macro_rules! mac { + (some) => { + Some(42) + }; + (any) => { + _ + }; + (str) => { + "foo" + }; + } + + if let Some(u) = mac!(some) { println!("{u}") } + //~^^^^ single_match + + // When scrutinee comes from macro, do not tell that arm will always match + // and suggest an equality check instead. + if mac!(str) == "foo" { println!("eq") } + //~^^^^ ERROR: for an equality check + + // Do not lint if any match arm come from expansion + match Some(0) { + mac!(some) => println!("eq"), + mac!(any) => println!("neq"), + } + match Some(0) { + Some(42) => println!("eq"), + mac!(any) => println!("neq"), + } + match Some(0) { + mac!(some) => println!("eq"), + _ => println!("neq"), } } diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs index dc758fa4281c8..a367b94c4ca6b 100644 --- a/src/tools/clippy/tests/ui/single_match.rs +++ b/src/tools/clippy/tests/ui/single_match.rs @@ -1,3 +1,4 @@ +//@require-annotations-for-level: WARN #![warn(clippy::single_match)] #![allow( unused, @@ -28,6 +29,8 @@ fn single_match() { Some(y) => println!("{:?}", y), _ => (), } + //~^^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` let z = (1u8, 1u8); match z { @@ -437,14 +440,15 @@ fn irrefutable_match() { let mut x = vec![1i8]; - // Should not lint. match x.pop() { // bla Some(u) => println!("{u}"), // more comments! None => {}, } - // Should not lint. + //~^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` + match x.pop() { // bla Some(u) => { @@ -454,4 +458,48 @@ fn irrefutable_match() { // bla None => {}, } + //~^^^^^^^^^ single_match + //~| NOTE: you might want to preserve the comments from inside the `match` +} + +fn issue_14493() { + macro_rules! mac { + (some) => { + Some(42) + }; + (any) => { + _ + }; + (str) => { + "foo" + }; + } + + match mac!(some) { + Some(u) => println!("{u}"), + _ => (), + } + //~^^^^ single_match + + // When scrutinee comes from macro, do not tell that arm will always match + // and suggest an equality check instead. + match mac!(str) { + "foo" => println!("eq"), + _ => (), + } + //~^^^^ ERROR: for an equality check + + // Do not lint if any match arm come from expansion + match Some(0) { + mac!(some) => println!("eq"), + mac!(any) => println!("neq"), + } + match Some(0) { + Some(42) => println!("eq"), + mac!(any) => println!("neq"), + } + match Some(0) { + mac!(some) => println!("eq"), + _ => println!("neq"), + } } diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr index c882969594892..1a4edc45c928d 100644 --- a/src/tools/clippy/tests/ui/single_match.stderr +++ b/src/tools/clippy/tests/ui/single_match.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:15:5 + --> tests/ui/single_match.rs:16:5 | LL | / match x { LL | | Some(y) => { @@ -19,7 +19,18 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:33:5 + --> tests/ui/single_match.rs:25:5 + | +LL | / match x { +... | +LL | | _ => (), +LL | | } + | |_____^ help: try: `if let Some(y) = x { println!("{:?}", y) }` + | + = note: you might want to preserve the comments from inside the `match` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:36:5 | LL | / match z { LL | | (2..=3, 7..=9) => dummy(), @@ -28,7 +39,7 @@ LL | | }; | |_____^ help: try: `if let (2..=3, 7..=9) = z { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:63:5 + --> tests/ui/single_match.rs:66:5 | LL | / match x { LL | | Some(y) => dummy(), @@ -37,7 +48,7 @@ LL | | }; | |_____^ help: try: `if let Some(y) = x { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:69:5 + --> tests/ui/single_match.rs:72:5 | LL | / match y { LL | | Ok(y) => dummy(), @@ -46,7 +57,7 @@ LL | | }; | |_____^ help: try: `if let Ok(y) = y { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:77:5 + --> tests/ui/single_match.rs:80:5 | LL | / match c { LL | | Cow::Borrowed(..) => dummy(), @@ -55,7 +66,7 @@ LL | | }; | |_____^ help: try: `if let Cow::Borrowed(..) = c { dummy() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:99:5 + --> tests/ui/single_match.rs:102:5 | LL | / match x { LL | | "test" => println!(), @@ -64,7 +75,7 @@ LL | | } | |_____^ help: try: `if x == "test" { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:113:5 + --> tests/ui/single_match.rs:116:5 | LL | / match x { LL | | Foo::A => println!(), @@ -73,7 +84,7 @@ LL | | } | |_____^ help: try: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:120:5 + --> tests/ui/single_match.rs:123:5 | LL | / match x { LL | | FOO_C => println!(), @@ -82,7 +93,7 @@ LL | | } | |_____^ help: try: `if x == FOO_C { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:126:5 + --> tests/ui/single_match.rs:129:5 | LL | / match &&x { LL | | Foo::A => println!(), @@ -91,7 +102,7 @@ LL | | } | |_____^ help: try: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:133:5 + --> tests/ui/single_match.rs:136:5 | LL | / match &x { LL | | Foo::A => println!(), @@ -100,7 +111,7 @@ LL | | } | |_____^ help: try: `if x == &Foo::A { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:151:5 + --> tests/ui/single_match.rs:154:5 | LL | / match x { LL | | Bar::A => println!(), @@ -109,7 +120,7 @@ LL | | } | |_____^ help: try: `if let Bar::A = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:160:5 + --> tests/ui/single_match.rs:163:5 | LL | / match x { LL | | None => println!(), @@ -118,7 +129,7 @@ LL | | }; | |_____^ help: try: `if let None = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:183:5 + --> tests/ui/single_match.rs:186:5 | LL | / match x { LL | | (Some(_), _) => {}, @@ -127,7 +138,7 @@ LL | | } | |_____^ help: try: `if let (Some(_), _) = x {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:190:5 + --> tests/ui/single_match.rs:193:5 | LL | / match x { LL | | (Some(E::V), _) => todo!(), @@ -136,7 +147,7 @@ LL | | } | |_____^ help: try: `if let (Some(E::V), _) = x { todo!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:197:5 + --> tests/ui/single_match.rs:200:5 | LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, @@ -145,7 +156,7 @@ LL | | } | |_____^ help: try: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:270:5 + --> tests/ui/single_match.rs:273:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -165,7 +176,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:279:5 + --> tests/ui/single_match.rs:282:5 | LL | / match bar { LL | | #[rustfmt::skip] @@ -187,7 +198,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:360:5 + --> tests/ui/single_match.rs:363:5 | LL | / match Ok::<_, u32>(Some(A)) { LL | | Ok(Some(A)) => println!(), @@ -196,7 +207,7 @@ LL | | } | |_____^ help: try: `if let Ok(Some(A)) = Ok::<_, u32>(Some(A)) { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:376:5 + --> tests/ui/single_match.rs:379:5 | LL | / match &Some(A) { LL | | Some(A | B) => println!(), @@ -205,7 +216,7 @@ LL | | } | |_____^ help: try: `if let Some(A | B) = &Some(A) { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:384:5 + --> tests/ui/single_match.rs:387:5 | LL | / match &s[0..3] { LL | | b"foo" => println!(), @@ -214,7 +225,7 @@ LL | | } | |_____^ help: try: `if &s[0..3] == b"foo" { println!() }` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:398:5 + --> tests/ui/single_match.rs:401:5 | LL | / match DATA { LL | | DATA => println!(), @@ -223,7 +234,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:404:5 + --> tests/ui/single_match.rs:407:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -232,7 +243,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:411:5 + --> tests/ui/single_match.rs:414:5 | LL | / match i { LL | | i => { @@ -252,7 +263,7 @@ LL + } | error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:420:5 + --> tests/ui/single_match.rs:423:5 | LL | / match i { LL | | i => {}, @@ -261,7 +272,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:426:5 + --> tests/ui/single_match.rs:429:5 | LL | / match i { LL | | i => (), @@ -270,7 +281,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:432:5 + --> tests/ui/single_match.rs:435:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -278,5 +289,55 @@ LL | | _ => {}, LL | | } | |_____^ help: try: `println!();` -error: aborting due to 26 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:443:5 + | +LL | / match x.pop() { +LL | | // bla +LL | | Some(u) => println!("{u}"), +... | +LL | | } + | |_____^ help: try: `if let Some(u) = x.pop() { println!("{u}") }` + | + = note: you might want to preserve the comments from inside the `match` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:452:5 + | +LL | / match x.pop() { +LL | | // bla +LL | | Some(u) => { +... | +LL | | None => {}, +LL | | } + | |_____^ + | + = note: you might want to preserve the comments from inside the `match` +help: try + | +LL ~ if let Some(u) = x.pop() { +LL + // bla +LL + println!("{u}"); +LL + } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:478:5 + | +LL | / match mac!(some) { +LL | | Some(u) => println!("{u}"), +LL | | _ => (), +LL | | } + | |_____^ help: try: `if let Some(u) = mac!(some) { println!("{u}") }` + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match.rs:486:5 + | +LL | / match mac!(str) { +LL | | "foo" => println!("eq"), +LL | | _ => (), +LL | | } + | |_____^ help: try: `if mac!(str) == "foo" { println!("eq") }` + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/single_match_else.fixed b/src/tools/clippy/tests/ui/single_match_else.fixed index 64782bf62a782..fde13fb90dbb2 100644 --- a/src/tools/clippy/tests/ui/single_match_else.fixed +++ b/src/tools/clippy/tests/ui/single_match_else.fixed @@ -1,4 +1,5 @@ //@aux-build: proc_macros.rs +//@require-annotations-for-level: WARN #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] @@ -90,6 +91,13 @@ fn main() { } //~^^^^^^^ single_match_else + if let Some(a) = Some(1) { println!("${:?}", a) } else { + println!("else block"); + return; + } + //~^^^^^^^^ single_match_else + //~| NOTE: you might want to preserve the comments from inside the `match` + // lint here use std::convert::Infallible; if let Ok(a) = Result::::Ok(1) { println!("${:?}", a) } else { diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs index 3f86f4d51803f..ca282200067ce 100644 --- a/src/tools/clippy/tests/ui/single_match_else.rs +++ b/src/tools/clippy/tests/ui/single_match_else.rs @@ -1,4 +1,5 @@ //@aux-build: proc_macros.rs +//@require-annotations-for-level: WARN #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] @@ -99,6 +100,17 @@ fn main() { } //~^^^^^^^ single_match_else + match Some(1) { + Some(a) => println!("${:?}", a), + // This is an inner comment + None => { + println!("else block"); + return; + }, + } + //~^^^^^^^^ single_match_else + //~| NOTE: you might want to preserve the comments from inside the `match` + // lint here use std::convert::Infallible; match Result::::Ok(1) { diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr index 7d4ba5fb75ef8..570480f9a3f0a 100644 --- a/src/tools/clippy/tests/ui/single_match_else.stderr +++ b/src/tools/clippy/tests/ui/single_match_else.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:17:13 + --> tests/ui/single_match_else.rs:18:13 | LL | let _ = match ExprNode::Butterflies { | _____________^ @@ -22,7 +22,7 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:83:5 + --> tests/ui/single_match_else.rs:84:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -42,7 +42,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:93:5 + --> tests/ui/single_match_else.rs:94:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -62,7 +62,28 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:104:5 + --> tests/ui/single_match_else.rs:103:5 + | +LL | / match Some(1) { +LL | | Some(a) => println!("${:?}", a), +LL | | // This is an inner comment +LL | | None => { +... | +LL | | }, +LL | | } + | |_____^ + | + = note: you might want to preserve the comments from inside the `match` +help: try + | +LL ~ if let Some(a) = Some(1) { println!("${:?}", a) } else { +LL + println!("else block"); +LL + return; +LL + } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match_else.rs:116:5 | LL | / match Result::::Ok(1) { LL | | Ok(a) => println!("${:?}", a), @@ -81,7 +102,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:114:5 + --> tests/ui/single_match_else.rs:126:5 | LL | / match Cow::from("moo") { LL | | Cow::Owned(a) => println!("${:?}", a), @@ -100,7 +121,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:125:5 + --> tests/ui/single_match_else.rs:137:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -123,7 +144,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:137:5 + --> tests/ui/single_match_else.rs:149:5 | LL | / match bar { LL | | Some(v) => { @@ -147,7 +168,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:150:5 + --> tests/ui/single_match_else.rs:162:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -171,7 +192,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:163:5 + --> tests/ui/single_match_else.rs:175:5 | LL | / match bar { LL | | #[rustfmt::skip] @@ -196,7 +217,7 @@ LL + } | error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match_else.rs:213:5 + --> tests/ui/single_match_else.rs:225:5 | LL | / match ExprNode::Butterflies { LL | | ExprNode::Butterflies => Some(&NODE), @@ -207,5 +228,5 @@ LL | | }, LL | | } | |_____^ help: try: `Some(&NODE)` -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/single_range_in_vec_init.rs b/src/tools/clippy/tests/ui/single_range_in_vec_init.rs index c6c0cb347dc68..25884450b0842 100644 --- a/src/tools/clippy/tests/ui/single_range_in_vec_init.rs +++ b/src/tools/clippy/tests/ui/single_range_in_vec_init.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macros.rs //@no-rustfix: overlapping suggestions -#![allow(clippy::no_effect, clippy::useless_vec, unused)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)] #![warn(clippy::single_range_in_vec_init)] #![feature(generic_arg_infer)] diff --git a/src/tools/clippy/tests/ui/size_of_ref.stderr b/src/tools/clippy/tests/ui/size_of_ref.stderr index 6ac0b0dd2f065..46af9f55deaf6 100644 --- a/src/tools/clippy/tests/ui/size_of_ref.stderr +++ b/src/tools/clippy/tests/ui/size_of_ref.stderr @@ -1,28 +1,28 @@ -error: argument to `std::mem::size_of_val()` is a reference to a reference +error: argument to `size_of_val()` is a reference to a reference --> tests/ui/size_of_ref.rs:13:5 | LL | size_of_val(&&x); | ^^^^^^^^^^^^^^^^ | - = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = help: dereference the argument to `size_of_val()` to get the size of the value instead of the size of the reference-type = note: `-D clippy::size-of-ref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::size_of_ref)]` -error: argument to `std::mem::size_of_val()` is a reference to a reference +error: argument to `size_of_val()` is a reference to a reference --> tests/ui/size_of_ref.rs:16:5 | LL | size_of_val(&y); | ^^^^^^^^^^^^^^^ | - = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = help: dereference the argument to `size_of_val()` to get the size of the value instead of the size of the reference-type -error: argument to `std::mem::size_of_val()` is a reference to a reference +error: argument to `size_of_val()` is a reference to a reference --> tests/ui/size_of_ref.rs:28:9 | LL | std::mem::size_of_val(&self) + (std::mem::size_of::() * self.data.capacity()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = help: dereference the argument to `size_of_val()` to get the size of the value instead of the size of the reference-type error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.fixed b/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.fixed similarity index 100% rename from src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.fixed rename to src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.fixed diff --git a/src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.rs b/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs similarity index 100% rename from src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.rs rename to src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs diff --git a/src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.stderr b/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.stderr similarity index 80% rename from src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.stderr rename to src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.stderr index e334ca5241eba..4998b9bd2cc40 100644 --- a/src/tools/clippy/tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.stderr +++ b/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.stderr @@ -1,5 +1,5 @@ error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `)` - --> tests/ui/syntax-error-recovery/non_expressive_names_error_recovery.rs:6:19 + --> tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs:6:19 | LL | fn aa(a: Aa tests/ui/string_to_string.rs:14:9 + | +LL | x.to_string() + | ^^^^^^^^^^^^^ + | + = help: consider using `.clone()` + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string.rs:19:33 + | +LL | let _ = x.unwrap_or_else(|| v.to_string()); + | ^^^^^^^^^^^^^ + | + = help: consider using `.clone()` + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/string_to_string_in_map.fixed b/src/tools/clippy/tests/ui/string_to_string_in_map.fixed new file mode 100644 index 0000000000000..efc085539f154 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_to_string_in_map.fixed @@ -0,0 +1,20 @@ +#![deny(clippy::string_to_string)] +#![allow(clippy::unnecessary_literal_unwrap, clippy::useless_vec, clippy::iter_cloned_collect)] + +fn main() { + let variable1 = String::new(); + let v = &variable1; + let variable2 = Some(v); + let _ = variable2.cloned(); + //~^ string_to_string + let _ = variable2.cloned(); + //~^ string_to_string + #[rustfmt::skip] + let _ = variable2.cloned(); + //~^ string_to_string + + let _ = vec![String::new()].iter().cloned().collect::>(); + //~^ string_to_string + let _ = vec![String::new()].iter().cloned().collect::>(); + //~^ string_to_string +} diff --git a/src/tools/clippy/tests/ui/string_to_string_in_map.rs b/src/tools/clippy/tests/ui/string_to_string_in_map.rs new file mode 100644 index 0000000000000..5bf1d7ba5a2e6 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_to_string_in_map.rs @@ -0,0 +1,20 @@ +#![deny(clippy::string_to_string)] +#![allow(clippy::unnecessary_literal_unwrap, clippy::useless_vec, clippy::iter_cloned_collect)] + +fn main() { + let variable1 = String::new(); + let v = &variable1; + let variable2 = Some(v); + let _ = variable2.map(String::to_string); + //~^ string_to_string + let _ = variable2.map(|x| x.to_string()); + //~^ string_to_string + #[rustfmt::skip] + let _ = variable2.map(|x| { x.to_string() }); + //~^ string_to_string + + let _ = vec![String::new()].iter().map(String::to_string).collect::>(); + //~^ string_to_string + let _ = vec![String::new()].iter().map(|x| x.to_string()).collect::>(); + //~^ string_to_string +} diff --git a/src/tools/clippy/tests/ui/string_to_string_in_map.stderr b/src/tools/clippy/tests/ui/string_to_string_in_map.stderr new file mode 100644 index 0000000000000..35aeed656eed0 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_to_string_in_map.stderr @@ -0,0 +1,38 @@ +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:8:23 + | +LL | let _ = variable2.map(String::to_string); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + | +note: the lint level is defined here + --> tests/ui/string_to_string_in_map.rs:1:9 + | +LL | #![deny(clippy::string_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:10:23 + | +LL | let _ = variable2.map(|x| x.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:13:23 + | +LL | let _ = variable2.map(|x| { x.to_string() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:16:40 + | +LL | let _ = vec![String::new()].iter().map(String::to_string).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + +error: `to_string()` called on a `String` + --> tests/ui/string_to_string_in_map.rs:18:40 + | +LL | let _ = vec![String::new()].iter().map(|x| x.to_string()).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/struct_fields.rs b/src/tools/clippy/tests/ui/struct_fields.rs index 3dce530efffaa..e7ff2e6573b2a 100644 --- a/src/tools/clippy/tests/ui/struct_fields.rs +++ b/src/tools/clippy/tests/ui/struct_fields.rs @@ -344,4 +344,31 @@ struct Use { //~^ struct_field_names } +// should lint on private fields of public structs (renaming them is not breaking-exported-api) +pub struct PubStructFieldNamedAfterStruct { + pub_struct_field_named_after_struct: bool, + //~^ ERROR: field name starts with the struct's name + other1: bool, + other2: bool, +} +pub struct PubStructFieldPrefix { + //~^ ERROR: all fields have the same prefix: `field` + field_foo: u8, + field_bar: u8, + field_baz: u8, +} +// ...but should not lint on structs with public fields. +pub struct PubStructPubAndPrivateFields { + /// One could argue that this field should be linted, but currently, any public field stops all + /// checking. + pub_struct_pub_and_private_fields_1: bool, + pub pub_struct_pub_and_private_fields_2: bool, +} +// nor on common prefixes if one of the involved fields is public +pub struct PubStructPubAndPrivateFieldPrefix { + pub field_foo: u8, + field_bar: u8, + field_baz: u8, +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/struct_fields.stderr b/src/tools/clippy/tests/ui/struct_fields.stderr index 79186cc1cfd8f..a5ff1b1259074 100644 --- a/src/tools/clippy/tests/ui/struct_fields.stderr +++ b/src/tools/clippy/tests/ui/struct_fields.stderr @@ -281,5 +281,24 @@ error: field name starts with the struct's name LL | use_baz: bool, | ^^^^^^^^^^^^^ -error: aborting due to 24 previous errors +error: field name starts with the struct's name + --> tests/ui/struct_fields.rs:349:5 + | +LL | pub_struct_field_named_after_struct: bool, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: all fields have the same prefix: `field` + --> tests/ui/struct_fields.rs:354:1 + | +LL | / pub struct PubStructFieldPrefix { +LL | | +LL | | field_foo: u8, +LL | | field_bar: u8, +LL | | field_baz: u8, +LL | | } + | |_^ + | + = help: remove the prefixes + +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed b/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed index 3696b0e066d28..3faa4b21ee414 100644 --- a/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed +++ b/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed @@ -87,4 +87,8 @@ pub mod useless_outer_doc { use std::mem; } +// Do not lint, this is not a `///!` +#[doc = "! here's some docs !"] +fn issue14265() {} + fn main() {} diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments.rs b/src/tools/clippy/tests/ui/suspicious_doc_comments.rs index 4107f5526d132..4af6ed850c2bb 100644 --- a/src/tools/clippy/tests/ui/suspicious_doc_comments.rs +++ b/src/tools/clippy/tests/ui/suspicious_doc_comments.rs @@ -87,4 +87,8 @@ pub mod useless_outer_doc { use std::mem; } +// Do not lint, this is not a `///!` +#[doc = "! here's some docs !"] +fn issue14265() {} + fn main() {} diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed index 888665a17ad16..6a64e64e98fa2 100644 --- a/src/tools/clippy/tests/ui/swap.fixed +++ b/src/tools/clippy/tests/ui/swap.fixed @@ -1,14 +1,9 @@ //@aux-build: macro_rules.rs -#![warn(clippy::all)] #![allow( clippy::disallowed_names, clippy::no_effect, clippy::redundant_clone, - redundant_semicolons, - dead_code, - unused_assignments, - unused_variables, clippy::let_and_return, clippy::useless_vec, clippy::redundant_locals diff --git a/src/tools/clippy/tests/ui/swap.rs b/src/tools/clippy/tests/ui/swap.rs index 51af55ecd27c8..e2d89c47382da 100644 --- a/src/tools/clippy/tests/ui/swap.rs +++ b/src/tools/clippy/tests/ui/swap.rs @@ -1,14 +1,9 @@ //@aux-build: macro_rules.rs -#![warn(clippy::all)] #![allow( clippy::disallowed_names, clippy::no_effect, clippy::redundant_clone, - redundant_semicolons, - dead_code, - unused_assignments, - unused_variables, clippy::let_and_return, clippy::useless_vec, clippy::redundant_locals diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr index 15f7566d58960..195b888187e6d 100644 --- a/src/tools/clippy/tests/ui/swap.stderr +++ b/src/tools/clippy/tests/ui/swap.stderr @@ -1,5 +1,5 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually - --> tests/ui/swap.rs:28:5 + --> tests/ui/swap.rs:23:5 | LL | / let temp = bar.a; LL | | @@ -12,7 +12,7 @@ LL | | bar.b = temp; = help: to override `-D warnings` add `#[allow(clippy::manual_swap)]` error: this looks like you are swapping elements of `foo` manually - --> tests/ui/swap.rs:41:5 + --> tests/ui/swap.rs:36:5 | LL | / let temp = foo[0]; LL | | @@ -21,7 +21,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually - --> tests/ui/swap.rs:51:5 + --> tests/ui/swap.rs:46:5 | LL | / let temp = foo[0]; LL | | @@ -30,7 +30,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually - --> tests/ui/swap.rs:71:5 + --> tests/ui/swap.rs:66:5 | LL | / let temp = foo[0]; LL | | @@ -39,7 +39,7 @@ LL | | foo[1] = temp; | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `a` and `b` manually - --> tests/ui/swap.rs:83:5 + --> tests/ui/swap.rs:78:5 | LL | / a ^= b; LL | | @@ -48,7 +48,7 @@ LL | | a ^= b; | |___________^ help: try: `std::mem::swap(&mut a, &mut b);` error: this looks like you are swapping `bar.a` and `bar.b` manually - --> tests/ui/swap.rs:92:5 + --> tests/ui/swap.rs:87:5 | LL | / bar.a ^= bar.b; LL | | @@ -57,7 +57,7 @@ LL | | bar.a ^= bar.b; | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);` error: this looks like you are swapping elements of `foo` manually - --> tests/ui/swap.rs:101:5 + --> tests/ui/swap.rs:96:5 | LL | / foo[0] ^= foo[1]; LL | | @@ -66,7 +66,7 @@ LL | | foo[0] ^= foo[1]; | |_____________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually - --> tests/ui/swap.rs:131:5 + --> tests/ui/swap.rs:126:5 | LL | / let temp = foo[0][1]; LL | | @@ -77,7 +77,7 @@ LL | | bar[1][0] = temp; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `a` and `b` manually - --> tests/ui/swap.rs:147:7 + --> tests/ui/swap.rs:142:7 | LL | ; let t = a; | _______^ @@ -89,7 +89,7 @@ LL | | b = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `c.0` and `a` manually - --> tests/ui/swap.rs:158:7 + --> tests/ui/swap.rs:153:7 | LL | ; let t = c.0; | _______^ @@ -101,7 +101,7 @@ LL | | a = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `b` and `a` manually - --> tests/ui/swap.rs:188:5 + --> tests/ui/swap.rs:183:5 | LL | / let t = b; LL | | @@ -112,7 +112,7 @@ LL | | a = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> tests/ui/swap.rs:143:5 + --> tests/ui/swap.rs:138:5 | LL | / a = b; LL | | @@ -120,11 +120,10 @@ LL | | b = a; | |_________^ help: try: `std::mem::swap(&mut a, &mut b)` | = note: or maybe you should use `std::mem::replace`? - = note: `-D clippy::almost-swapped` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::almost_swapped)]` + = note: `#[deny(clippy::almost_swapped)]` on by default error: this looks like you are trying to swap `c.0` and `a` - --> tests/ui/swap.rs:154:5 + --> tests/ui/swap.rs:149:5 | LL | / c.0 = a; LL | | @@ -134,7 +133,7 @@ LL | | a = c.0; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> tests/ui/swap.rs:163:5 + --> tests/ui/swap.rs:158:5 | LL | / let a = b; LL | | @@ -144,7 +143,7 @@ LL | | let b = a; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `d` and `c` - --> tests/ui/swap.rs:169:5 + --> tests/ui/swap.rs:164:5 | LL | / d = c; LL | | @@ -154,7 +153,7 @@ LL | | c = d; = note: or maybe you should use `std::mem::replace`? error: this looks like you are trying to swap `a` and `b` - --> tests/ui/swap.rs:174:5 + --> tests/ui/swap.rs:169:5 | LL | / let a = b; LL | | @@ -164,7 +163,7 @@ LL | | b = a; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `s.0.x` and `s.0.y` manually - --> tests/ui/swap.rs:224:5 + --> tests/ui/swap.rs:219:5 | LL | / let t = s.0.x; LL | | diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.fixed b/src/tools/clippy/tests/ui/swap_with_temporary.fixed new file mode 100644 index 0000000000000..4007d998ba068 --- /dev/null +++ b/src/tools/clippy/tests/ui/swap_with_temporary.fixed @@ -0,0 +1,74 @@ +#![warn(clippy::swap_with_temporary)] + +use std::mem::swap; + +fn func() -> String { + String::from("func") +} + +fn func_returning_refmut(s: &mut String) -> &mut String { + s +} + +fn main() { + let mut x = String::from("x"); + let mut y = String::from("y"); + let mut zz = String::from("zz"); + let z = &mut zz; + + // No lint + swap(&mut x, &mut y); + + y = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + x = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + *z = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + // No lint + swap(z, func_returning_refmut(&mut x)); + + swap(&mut y, z); + + *z = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + macro_rules! mac { + (refmut $x:expr) => { + &mut $x + }; + (funcall $f:ident) => { + $f() + }; + (wholeexpr) => { + swap(&mut 42, &mut 0) + }; + (ident $v:ident) => { + $v + }; + } + *z = mac!(funcall func); + //~^ ERROR: swapping with a temporary value is inefficient + *mac!(ident z) = mac!(funcall func); + //~^ ERROR: swapping with a temporary value is inefficient + *mac!(ident z) = mac!(funcall func); + //~^ ERROR: swapping with a temporary value is inefficient + *mac!(refmut y) = func(); + //~^ ERROR: swapping with a temporary value is inefficient + + // No lint if it comes from a macro as it may depend on the arguments + mac!(wholeexpr); +} + +struct S { + t: String, +} + +fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) { + swap(&mut s.t, &mut v[0]); + swap(&mut s.t, v.get_mut(0).unwrap()); + swap(w.unwrap(), &mut s.t); +} diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.rs b/src/tools/clippy/tests/ui/swap_with_temporary.rs new file mode 100644 index 0000000000000..d403c086c0f4f --- /dev/null +++ b/src/tools/clippy/tests/ui/swap_with_temporary.rs @@ -0,0 +1,74 @@ +#![warn(clippy::swap_with_temporary)] + +use std::mem::swap; + +fn func() -> String { + String::from("func") +} + +fn func_returning_refmut(s: &mut String) -> &mut String { + s +} + +fn main() { + let mut x = String::from("x"); + let mut y = String::from("y"); + let mut zz = String::from("zz"); + let z = &mut zz; + + // No lint + swap(&mut x, &mut y); + + swap(&mut func(), &mut y); + //~^ ERROR: swapping with a temporary value is inefficient + + swap(&mut x, &mut func()); + //~^ ERROR: swapping with a temporary value is inefficient + + swap(z, &mut func()); + //~^ ERROR: swapping with a temporary value is inefficient + + // No lint + swap(z, func_returning_refmut(&mut x)); + + swap(&mut y, z); + + swap(&mut func(), z); + //~^ ERROR: swapping with a temporary value is inefficient + + macro_rules! mac { + (refmut $x:expr) => { + &mut $x + }; + (funcall $f:ident) => { + $f() + }; + (wholeexpr) => { + swap(&mut 42, &mut 0) + }; + (ident $v:ident) => { + $v + }; + } + swap(&mut mac!(funcall func), z); + //~^ ERROR: swapping with a temporary value is inefficient + swap(&mut mac!(funcall func), mac!(ident z)); + //~^ ERROR: swapping with a temporary value is inefficient + swap(mac!(ident z), &mut mac!(funcall func)); + //~^ ERROR: swapping with a temporary value is inefficient + swap(mac!(refmut y), &mut func()); + //~^ ERROR: swapping with a temporary value is inefficient + + // No lint if it comes from a macro as it may depend on the arguments + mac!(wholeexpr); +} + +struct S { + t: String, +} + +fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) { + swap(&mut s.t, &mut v[0]); + swap(&mut s.t, v.get_mut(0).unwrap()); + swap(w.unwrap(), &mut s.t); +} diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.stderr b/src/tools/clippy/tests/ui/swap_with_temporary.stderr new file mode 100644 index 0000000000000..59355771a9648 --- /dev/null +++ b/src/tools/clippy/tests/ui/swap_with_temporary.stderr @@ -0,0 +1,100 @@ +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:22:5 + | +LL | swap(&mut func(), &mut y); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `y = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:22:15 + | +LL | swap(&mut func(), &mut y); + | ^^^^^^ + = note: `-D clippy::swap-with-temporary` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::swap_with_temporary)]` + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:25:5 + | +LL | swap(&mut x, &mut func()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `x = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:25:23 + | +LL | swap(&mut x, &mut func()); + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:28:5 + | +LL | swap(z, &mut func()); + | ^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*z = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:28:18 + | +LL | swap(z, &mut func()); + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:36:5 + | +LL | swap(&mut func(), z); + | ^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*z = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:36:15 + | +LL | swap(&mut func(), z); + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:53:5 + | +LL | swap(&mut mac!(funcall func), z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*z = mac!(funcall func)` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:53:15 + | +LL | swap(&mut mac!(funcall func), z); + | ^^^^^^^^^^^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:55:5 + | +LL | swap(&mut mac!(funcall func), mac!(ident z)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*mac!(ident z) = mac!(funcall func)` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:55:15 + | +LL | swap(&mut mac!(funcall func), mac!(ident z)); + | ^^^^^^^^^^^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:57:5 + | +LL | swap(mac!(ident z), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*mac!(ident z) = mac!(funcall func)` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:57:30 + | +LL | swap(mac!(ident z), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:59:5 + | +LL | swap(mac!(refmut y), &mut func()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*mac!(refmut y) = func()` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:59:31 + | +LL | swap(mac!(refmut y), &mut func()); + | ^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/swap_with_temporary_unfixable.rs b/src/tools/clippy/tests/ui/swap_with_temporary_unfixable.rs new file mode 100644 index 0000000000000..a974ca82abf26 --- /dev/null +++ b/src/tools/clippy/tests/ui/swap_with_temporary_unfixable.rs @@ -0,0 +1,62 @@ +//@no-rustfix +#![warn(clippy::swap_with_temporary)] + +use std::mem::swap; + +fn func() -> String { + String::from("func") +} + +fn func_returning_refmut(s: &mut String) -> &mut String { + s +} + +fn main() { + let mut x = String::from("x"); + let mut y = String::from("y"); + let mut zz = String::from("zz"); + let z = &mut zz; + + swap(&mut func(), &mut func()); + //~^ ERROR: swapping temporary values has no effect + + if matches!(swap(&mut func(), &mut func()), ()) { + //~^ ERROR: swapping temporary values has no effect + println!("Yeah"); + } + + if matches!(swap(z, &mut func()), ()) { + //~^ ERROR: swapping with a temporary value is inefficient + println!("Yeah"); + } + + macro_rules! mac { + (refmut $x:expr) => { + &mut $x + }; + (refmut) => { + mac!(refmut String::new()) + }; + (funcall $f:ident) => { + $f() + }; + } + + swap(mac!(refmut func()), z); + //~^ ERROR: swapping with a temporary value is inefficient + swap(&mut mac!(funcall func), &mut mac!(funcall func)); + //~^ ERROR: swapping temporary values has no effect + swap(mac!(refmut), mac!(refmut)); + //~^ ERROR: swapping temporary values has no effect + swap(mac!(refmut y), mac!(refmut)); + //~^ ERROR: swapping with a temporary value is inefficient +} + +fn bug(v1: &mut [i32], v2: &mut [i32]) { + // Incorrect: swapping temporary references (`&mut &mut` passed to swap) + std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + //~^ ERROR: swapping temporary values has no effect + + // Correct + std::mem::swap(v1.last_mut().unwrap(), v2.last_mut().unwrap()); +} diff --git a/src/tools/clippy/tests/ui/swap_with_temporary_unfixable.stderr b/src/tools/clippy/tests/ui/swap_with_temporary_unfixable.stderr new file mode 100644 index 0000000000000..856c5415d676c --- /dev/null +++ b/src/tools/clippy/tests/ui/swap_with_temporary_unfixable.stderr @@ -0,0 +1,125 @@ +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:20:5 + | +LL | swap(&mut func(), &mut func()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:20:15 + | +LL | swap(&mut func(), &mut func()); + | ^^^^^^ +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:20:28 + | +LL | swap(&mut func(), &mut func()); + | ^^^^^^ + = note: `-D clippy::swap-with-temporary` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::swap_with_temporary)]` + +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:23:17 + | +LL | if matches!(swap(&mut func(), &mut func()), ()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:23:27 + | +LL | if matches!(swap(&mut func(), &mut func()), ()) { + | ^^^^^^ +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:23:40 + | +LL | if matches!(swap(&mut func(), &mut func()), ()) { + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary_unfixable.rs:28:17 + | +LL | if matches!(swap(z, &mut func()), ()) { + | ^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:28:30 + | +LL | if matches!(swap(z, &mut func()), ()) { + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary_unfixable.rs:45:5 + | +LL | swap(mac!(refmut func()), z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this is a mutable reference to a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:45:10 + | +LL | swap(mac!(refmut func()), z); + | ^^^^^^^^^^^^^^^^^^^ + +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:47:5 + | +LL | swap(&mut mac!(funcall func), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:47:15 + | +LL | swap(&mut mac!(funcall func), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^ +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:47:40 + | +LL | swap(&mut mac!(funcall func), &mut mac!(funcall func)); + | ^^^^^^^^^^^^^^^^^^ + +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:49:5 + | +LL | swap(mac!(refmut), mac!(refmut)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this is a mutable reference to a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:49:10 + | +LL | swap(mac!(refmut), mac!(refmut)); + | ^^^^^^^^^^^^ +note: this is a mutable reference to a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:49:24 + | +LL | swap(mac!(refmut), mac!(refmut)); + | ^^^^^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary_unfixable.rs:51:5 + | +LL | swap(mac!(refmut y), mac!(refmut)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this is a mutable reference to a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:51:26 + | +LL | swap(mac!(refmut y), mac!(refmut)); + | ^^^^^^^^^^^^ + +error: swapping temporary values has no effect + --> tests/ui/swap_with_temporary_unfixable.rs:57:5 + | +LL | std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:57:25 + | +LL | std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^ +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary_unfixable.rs:57:54 + | +LL | std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.fixed b/src/tools/clippy/tests/ui/to_digit_is_some.fixed index 627d54c5f7384..ff6b32e6bd182 100644 --- a/src/tools/clippy/tests/ui/to_digit_is_some.fixed +++ b/src/tools/clippy/tests/ui/to_digit_is_some.fixed @@ -9,3 +9,20 @@ fn main() { let _ = char::is_digit(c, 8); //~^ to_digit_is_some } + +#[clippy::msrv = "1.86"] +mod cannot_lint_in_const_context { + fn without_const(c: char) -> bool { + c.is_digit(8) + //~^ to_digit_is_some + } + const fn with_const(c: char) -> bool { + c.to_digit(8).is_some() + } +} + +#[clippy::msrv = "1.87"] +const fn with_const(c: char) -> bool { + c.is_digit(8) + //~^ to_digit_is_some +} diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.rs b/src/tools/clippy/tests/ui/to_digit_is_some.rs index d4eccc9931f17..5ba0861743318 100644 --- a/src/tools/clippy/tests/ui/to_digit_is_some.rs +++ b/src/tools/clippy/tests/ui/to_digit_is_some.rs @@ -9,3 +9,20 @@ fn main() { let _ = char::to_digit(c, 8).is_some(); //~^ to_digit_is_some } + +#[clippy::msrv = "1.86"] +mod cannot_lint_in_const_context { + fn without_const(c: char) -> bool { + c.to_digit(8).is_some() + //~^ to_digit_is_some + } + const fn with_const(c: char) -> bool { + c.to_digit(8).is_some() + } +} + +#[clippy::msrv = "1.87"] +const fn with_const(c: char) -> bool { + c.to_digit(8).is_some() + //~^ to_digit_is_some +} diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.stderr b/src/tools/clippy/tests/ui/to_digit_is_some.stderr index f41382a60d53f..5ffedb4683f17 100644 --- a/src/tools/clippy/tests/ui/to_digit_is_some.stderr +++ b/src/tools/clippy/tests/ui/to_digit_is_some.stderr @@ -13,5 +13,17 @@ error: use of `.to_digit(..).is_some()` LL | let _ = char::to_digit(c, 8).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `char::is_digit(c, 8)` -error: aborting due to 2 previous errors +error: use of `.to_digit(..).is_some()` + --> tests/ui/to_digit_is_some.rs:16:9 + | +LL | c.to_digit(8).is_some() + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_digit(8)` + +error: use of `.to_digit(..).is_some()` + --> tests/ui/to_digit_is_some.rs:26:5 + | +LL | c.to_digit(8).is_some() + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_digit(8)` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index 3aecde398dc3f..e968e7a59244d 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -3,6 +3,7 @@ #![allow( dead_code, clippy::borrow_as_ptr, + unnecessary_transmutes, clippy::needless_lifetimes, clippy::missing_transmute_annotations )] @@ -23,19 +24,21 @@ fn my_vec() -> MyVec { #[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)] #[warn(clippy::useless_transmute)] unsafe fn _generic<'a, T, U: 'a>(t: &'a T) { - // FIXME: should lint - // let _: &'a T = core::mem::transmute(t); + unsafe { + // FIXME: should lint + // let _: &'a T = core::mem::transmute(t); - let _: &'a U = core::mem::transmute(t); + let _: &'a U = core::mem::transmute(t); - let _: *const T = core::mem::transmute(t); - //~^ useless_transmute + let _: *const T = core::mem::transmute(t); + //~^ useless_transmute - let _: *mut T = core::mem::transmute(t); - //~^ useless_transmute + let _: *mut T = core::mem::transmute(t); + //~^ useless_transmute - let _: *const U = core::mem::transmute(t); - //~^ useless_transmute + let _: *const U = core::mem::transmute(t); + //~^ useless_transmute + } } #[warn(clippy::useless_transmute)] @@ -59,7 +62,7 @@ fn useless() { let _: *const usize = std::mem::transmute(5_isize); //~^ useless_transmute - let _ = 5_isize as *const usize; + let _ = std::ptr::dangling::(); let _: *const usize = std::mem::transmute(1 + 1usize); //~^ useless_transmute @@ -68,19 +71,19 @@ fn useless() { } unsafe fn _f<'a, 'b>(x: &'a u32) -> &'b u32 { - std::mem::transmute(x) + unsafe { std::mem::transmute(x) } } unsafe fn _f2<'a, 'b>(x: *const (dyn Iterator + 'a)) -> *const (dyn Iterator + 'b) { - std::mem::transmute(x) + unsafe { std::mem::transmute(x) } } unsafe fn _f3<'a, 'b>(x: fn(&'a u32)) -> fn(&'b u32) { - std::mem::transmute(x) + unsafe { std::mem::transmute(x) } } unsafe fn _f4<'a, 'b>(x: std::borrow::Cow<'a, str>) -> std::borrow::Cow<'b, str> { - std::mem::transmute(x) + unsafe { std::mem::transmute(x) } } } @@ -113,138 +116,6 @@ fn int_to_bool() { //~^ transmute_int_to_bool } -#[warn(clippy::transmute_int_to_float)] -mod int_to_float { - fn test() { - let _: f16 = unsafe { std::mem::transmute(0_u16) }; - //~^ transmute_int_to_float - - let _: f16 = unsafe { std::mem::transmute(0_i16) }; - //~^ transmute_int_to_float - - let _: f32 = unsafe { std::mem::transmute(0_u32) }; - //~^ transmute_int_to_float - - let _: f32 = unsafe { std::mem::transmute(0_i32) }; - //~^ transmute_int_to_float - - let _: f64 = unsafe { std::mem::transmute(0_u64) }; - //~^ transmute_int_to_float - - let _: f64 = unsafe { std::mem::transmute(0_i64) }; - //~^ transmute_int_to_float - - let _: f128 = unsafe { std::mem::transmute(0_u128) }; - //~^ transmute_int_to_float - - let _: f128 = unsafe { std::mem::transmute(0_i128) }; - //~^ transmute_int_to_float - } - - mod issue_5747 { - const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; - //~^ transmute_int_to_float - - const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; - //~^ transmute_int_to_float - - const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; - //~^ transmute_int_to_float - - const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; - //~^ transmute_int_to_float - - const fn from_bits_16(v: i16) -> f16 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - - const fn from_bits_32(v: i32) -> f32 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - - const fn from_bits_64(v: u64) -> f64 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - - const fn from_bits_128(v: u128) -> f128 { - unsafe { std::mem::transmute(v) } - //~^ transmute_int_to_float - } - } -} - -mod num_to_bytes { - fn test() { - unsafe { - let _: [u8; 1] = std::mem::transmute(0u8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0u32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0u128); - //~^ transmute_num_to_bytes - - let _: [u8; 1] = std::mem::transmute(0i8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0i32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0i128); - //~^ transmute_num_to_bytes - - let _: [u8; 2] = std::mem::transmute(0.0f16); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0.0f32); - //~^ transmute_num_to_bytes - - let _: [u8; 8] = std::mem::transmute(0.0f64); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0.0f128); - //~^ transmute_num_to_bytes - } - } - const fn test_const() { - unsafe { - let _: [u8; 1] = std::mem::transmute(0u8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0u32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0u128); - //~^ transmute_num_to_bytes - - let _: [u8; 1] = std::mem::transmute(0i8); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0i32); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0i128); - //~^ transmute_num_to_bytes - - let _: [u8; 2] = std::mem::transmute(0.0f16); - //~^ transmute_num_to_bytes - - let _: [u8; 4] = std::mem::transmute(0.0f32); - //~^ transmute_num_to_bytes - - let _: [u8; 8] = std::mem::transmute(0.0f64); - //~^ transmute_num_to_bytes - - let _: [u8; 16] = std::mem::transmute(0.0f128); - //~^ transmute_num_to_bytes - } - } -} - fn bytes_to_str(mb: &mut [u8]) { const B: &[u8] = b""; diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr index e0d28437aafc8..79528ec06f1bf 100644 --- a/src/tools/clippy/tests/ui/transmute.stderr +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -1,68 +1,68 @@ error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:31:23 + --> tests/ui/transmute.rs:33:27 | -LL | let _: *const T = core::mem::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` +LL | let _: *const T = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` | = note: `-D clippy::useless-transmute` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:34:21 + --> tests/ui/transmute.rs:36:25 | -LL | let _: *mut T = core::mem::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` +LL | let _: *mut T = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:37:23 + --> tests/ui/transmute.rs:39:27 | -LL | let _: *const U = core::mem::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` +LL | let _: *const U = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:44:27 + --> tests/ui/transmute.rs:47:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:47:27 + --> tests/ui/transmute.rs:50:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:50:27 + --> tests/ui/transmute.rs:53:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:53:27 + --> tests/ui/transmute.rs:56:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:56:27 + --> tests/ui/transmute.rs:59:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:59:31 + --> tests/ui/transmute.rs:62: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 - --> tests/ui/transmute.rs:64:31 + --> tests/ui/transmute.rs:67: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`) - --> tests/ui/transmute.rs:96:24 + --> tests/ui/transmute.rs:99:24 | LL | let _: Usize = core::mem::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,25 +71,25 @@ LL | let _: Usize = core::mem::transmute(int_const_ptr); = help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:99:24 + --> tests/ui/transmute.rs:102:24 | LL | let _: Usize = core::mem::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> tests/ui/transmute.rs:102:31 + --> tests/ui/transmute.rs:105:31 | LL | let _: *const Usize = core::mem::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> tests/ui/transmute.rs:105:29 + --> tests/ui/transmute.rs:108:29 | LL | let _: *mut Usize = core::mem::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u8` to a `bool` - --> tests/ui/transmute.rs:112:28 + --> tests/ui/transmute.rs:115:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -97,230 +97,8 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_bool)]` -error: transmute from a `u16` to a `f16` - --> tests/ui/transmute.rs:119:31 - | -LL | let _: f16 = unsafe { std::mem::transmute(0_u16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` - | - = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_float)]` - -error: transmute from a `i16` to a `f16` - --> tests/ui/transmute.rs:122:31 - | -LL | let _: f16 = unsafe { std::mem::transmute(0_i16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_i16 as u16)` - -error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:125:31 - | -LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` - -error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:128: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` - --> tests/ui/transmute.rs:131: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` - --> tests/ui/transmute.rs:134:31 - | -LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` - -error: transmute from a `u128` to a `f128` - --> tests/ui/transmute.rs:137:32 - | -LL | let _: f128 = unsafe { std::mem::transmute(0_u128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_u128)` - -error: transmute from a `i128` to a `f128` - --> tests/ui/transmute.rs:140:32 - | -LL | let _: f128 = unsafe { std::mem::transmute(0_i128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` - -error: transmute from a `u16` to a `f16` - --> tests/ui/transmute.rs:145:39 - | -LL | const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` - -error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:148:39 - | -LL | const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` - -error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:151:39 - | -LL | const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` - -error: transmute from a `i128` to a `f128` - --> tests/ui/transmute.rs:154:41 - | -LL | const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` - -error: transmute from a `i16` to a `f16` - --> tests/ui/transmute.rs:158:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(v as u16)` - -error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:163:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(v as u32)` - -error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:168:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(v)` - -error: transmute from a `u128` to a `f128` - --> tests/ui/transmute.rs:173:22 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(v)` - -error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:182:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0u8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` - | - = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_num_to_bytes)]` - -error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:185:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` - -error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:188:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0u128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` - -error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:191:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0i8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` - -error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:194:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` - -error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:197:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0i128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` - -error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:200:30 - | -LL | let _: [u8; 2] = std::mem::transmute(0.0f16); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` - -error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:203:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` - -error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:206:30 - | -LL | let _: [u8; 8] = std::mem::transmute(0.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` - -error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:209:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0.0f128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` - -error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:215:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0u8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` - -error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:218:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` - -error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:221:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0u128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` - -error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:224:30 - | -LL | let _: [u8; 1] = std::mem::transmute(0i8); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` - -error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:227:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` - -error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:230:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0i128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` - -error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:233:30 - | -LL | let _: [u8; 2] = std::mem::transmute(0.0f16); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` - -error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:236:30 - | -LL | let _: [u8; 4] = std::mem::transmute(0.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` - -error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:239:30 - | -LL | let _: [u8; 8] = std::mem::transmute(0.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` - -error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:242:31 - | -LL | let _: [u8; 16] = std::mem::transmute(0.0f128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` - error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:251:28 + --> tests/ui/transmute.rs:122:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -329,16 +107,16 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]` error: transmute from a `&mut [u8]` to a `&mut str` - --> tests/ui/transmute.rs:254:32 + --> tests/ui/transmute.rs:125:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:257:30 + --> tests/ui/transmute.rs:128:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 54 previous errors +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed deleted file mode 100644 index 1f97b997eaa0e..0000000000000 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::transmute_float_to_int)] -#![allow(clippy::missing_transmute_annotations)] -#![feature(f128)] -#![feature(f16)] - -fn float_to_int() { - let _: u32 = unsafe { 1f32.to_bits() }; - //~^ transmute_float_to_int - - let _: i32 = unsafe { 1f32.to_bits() as i32 }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { 1f64.to_bits() }; - //~^ transmute_float_to_int - - let _: i64 = unsafe { 1f64.to_bits() as i64 }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { 1.0f64.to_bits() }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { (-1.0f64).to_bits() }; - //~^ transmute_float_to_int -} - -mod issue_5747 { - const VALUE16: i16 = unsafe { 1f16.to_bits() as i16 }; - //~^ transmute_float_to_int - - const VALUE32: i32 = unsafe { 1f32.to_bits() as i32 }; - //~^ transmute_float_to_int - - const VALUE64: u64 = unsafe { 1f64.to_bits() }; - //~^ transmute_float_to_int - - const VALUE128: u128 = unsafe { 1f128.to_bits() }; - //~^ transmute_float_to_int - - const fn to_bits_16(v: f16) -> u16 { - unsafe { v.to_bits() } - //~^ transmute_float_to_int - } - - const fn to_bits_32(v: f32) -> u32 { - unsafe { v.to_bits() } - //~^ transmute_float_to_int - } - - const fn to_bits_64(v: f64) -> i64 { - unsafe { v.to_bits() as i64 } - //~^ transmute_float_to_int - } - - const fn to_bits_128(v: f128) -> i128 { - unsafe { v.to_bits() as i128 } - //~^ transmute_float_to_int - } -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.rs b/src/tools/clippy/tests/ui/transmute_float_to_int.rs deleted file mode 100644 index 788a7e1026c67..0000000000000 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.rs +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::transmute_float_to_int)] -#![allow(clippy::missing_transmute_annotations)] -#![feature(f128)] -#![feature(f16)] - -fn float_to_int() { - let _: u32 = unsafe { std::mem::transmute(1f32) }; - //~^ transmute_float_to_int - - let _: i32 = unsafe { std::mem::transmute(1f32) }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { std::mem::transmute(1f64) }; - //~^ transmute_float_to_int - - let _: i64 = unsafe { std::mem::transmute(1f64) }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { std::mem::transmute(1.0) }; - //~^ transmute_float_to_int - - let _: u64 = unsafe { std::mem::transmute(-1.0) }; - //~^ transmute_float_to_int -} - -mod issue_5747 { - const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; - //~^ transmute_float_to_int - - const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; - //~^ transmute_float_to_int - - const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; - //~^ transmute_float_to_int - - const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; - //~^ transmute_float_to_int - - const fn to_bits_16(v: f16) -> u16 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } - - const fn to_bits_32(v: f32) -> u32 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } - - const fn to_bits_64(v: f64) -> i64 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } - - const fn to_bits_128(v: f128) -> i128 { - unsafe { std::mem::transmute(v) } - //~^ transmute_float_to_int - } -} - -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 deleted file mode 100644 index 223cbc4e90c07..0000000000000 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.stderr +++ /dev/null @@ -1,89 +0,0 @@ -error: transmute from a `f32` to a `u32` - --> tests/ui/transmute_float_to_int.rs:7:27 - | -LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` - | - = note: `-D clippy::transmute-float-to-int` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_float_to_int)]` - -error: transmute from a `f32` to a `i32` - --> tests/ui/transmute_float_to_int.rs:10: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` - --> tests/ui/transmute_float_to_int.rs:13:27 - | -LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` - -error: transmute from a `f64` to a `i64` - --> tests/ui/transmute_float_to_int.rs:16: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` - --> tests/ui/transmute_float_to_int.rs:19: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` - --> tests/ui/transmute_float_to_int.rs:22:27 - | -LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` - -error: transmute from a `f16` to a `i16` - --> tests/ui/transmute_float_to_int.rs:27:35 - | -LL | const VALUE16: i16 = unsafe { std::mem::transmute(1f16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f16.to_bits() as i16` - -error: transmute from a `f32` to a `i32` - --> tests/ui/transmute_float_to_int.rs:30:35 - | -LL | const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` - -error: transmute from a `f64` to a `u64` - --> tests/ui/transmute_float_to_int.rs:33:35 - | -LL | const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` - -error: transmute from a `f128` to a `u128` - --> tests/ui/transmute_float_to_int.rs:36:37 - | -LL | const VALUE128: u128 = unsafe { std::mem::transmute(1f128) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f128.to_bits()` - -error: transmute from a `f16` to a `u16` - --> tests/ui/transmute_float_to_int.rs:40:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits()` - -error: transmute from a `f32` to a `u32` - --> tests/ui/transmute_float_to_int.rs:45:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits()` - -error: transmute from a `f64` to a `i64` - --> tests/ui/transmute_float_to_int.rs:50:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits() as i64` - -error: transmute from a `f128` to a `i128` - --> tests/ui/transmute_float_to_int.rs:55:18 - | -LL | unsafe { std::mem::transmute(v) } - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `v.to_bits() as i128` - -error: aborting due to 14 previous errors - diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.fixed b/src/tools/clippy/tests/ui/transmute_int_to_char.fixed deleted file mode 100644 index b5425a2e9e854..0000000000000 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.fixed +++ /dev/null @@ -1,16 +0,0 @@ -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] - -fn int_to_char() { - let _: char = unsafe { std::char::from_u32(0_u32).unwrap() }; - //~^ transmute_int_to_char - - let _: char = unsafe { std::char::from_u32(0_i32 as u32).unwrap() }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { std::mem::transmute(0_u32) }; - const _: char = unsafe { std::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.rs b/src/tools/clippy/tests/ui/transmute_int_to_char.rs deleted file mode 100644 index b24bb177c9fc0..0000000000000 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] - -fn int_to_char() { - let _: char = unsafe { std::mem::transmute(0_u32) }; - //~^ transmute_int_to_char - - let _: char = unsafe { std::mem::transmute(0_i32) }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { std::mem::transmute(0_u32) }; - const _: char = unsafe { std::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.stderr b/src/tools/clippy/tests/ui/transmute_int_to_char.stderr deleted file mode 100644 index e3a3620f28b75..0000000000000 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: transmute from a `u32` to a `char` - --> tests/ui/transmute_int_to_char.rs:5:28 - | -LL | let _: char = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` - | - = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` - -error: transmute from a `i32` to a `char` - --> tests/ui/transmute_int_to_char.rs:8:28 - | -LL | let _: char = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` - -error: aborting due to 2 previous errors - diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed deleted file mode 100644 index e525751e306ea..0000000000000 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed +++ /dev/null @@ -1,28 +0,0 @@ -#![no_std] -#![feature(lang_items)] -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn int_to_char() { - let _: char = unsafe { core::char::from_u32(0_u32).unwrap() }; - //~^ transmute_int_to_char - - let _: char = unsafe { core::char::from_u32(0_i32 as u32).unwrap() }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { core::mem::transmute(0_u32) }; - const _: char = unsafe { core::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs deleted file mode 100644 index 7cb508ceaf3bc..0000000000000 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![no_std] -#![feature(lang_items)] -#![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] - -use core::panic::PanicInfo; - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - loop {} -} - -fn int_to_char() { - let _: char = unsafe { core::mem::transmute(0_u32) }; - //~^ transmute_int_to_char - - let _: char = unsafe { core::mem::transmute(0_i32) }; - //~^ transmute_int_to_char - - // These shouldn't warn - const _: char = unsafe { core::mem::transmute(0_u32) }; - const _: char = unsafe { core::mem::transmute(0_i32) }; -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr deleted file mode 100644 index d94580a84d7a4..0000000000000 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: transmute from a `u32` to a `char` - --> tests/ui/transmute_int_to_char_no_std.rs:17:28 - | -LL | let _: char = unsafe { core::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_u32).unwrap()` - | - = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` - -error: transmute from a `i32` to a `char` - --> tests/ui/transmute_int_to_char_no_std.rs:20:28 - | -LL | let _: char = unsafe { core::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_i32 as u32).unwrap()` - -error: aborting due to 2 previous errors - diff --git a/src/tools/clippy/tests/ui/transmute_null_to_fn.rs b/src/tools/clippy/tests/ui/transmute_null_to_fn.rs index e88f05bb662e2..4712374af934f 100644 --- a/src/tools/clippy/tests/ui/transmute_null_to_fn.rs +++ b/src/tools/clippy/tests/ui/transmute_null_to_fn.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] #![warn(clippy::transmute_null_to_fn)] #![allow(clippy::zero_ptr, clippy::missing_transmute_annotations)] +#![allow(clippy::manual_dangling_ptr)] // Easy to lint because these only span one line. fn one_liners() { diff --git a/src/tools/clippy/tests/ui/transmute_null_to_fn.stderr b/src/tools/clippy/tests/ui/transmute_null_to_fn.stderr index f7d80147445d8..b5b0d4ecc7c03 100644 --- a/src/tools/clippy/tests/ui/transmute_null_to_fn.stderr +++ b/src/tools/clippy/tests/ui/transmute_null_to_fn.stderr @@ -1,5 +1,5 @@ error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:8:23 + --> tests/ui/transmute_null_to_fn.rs:9:23 | LL | let _: fn() = std::mem::transmute(0 as *const ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -9,7 +9,7 @@ LL | let _: fn() = std::mem::transmute(0 as *const ()); = help: to override `-D warnings` add `#[allow(clippy::transmute_null_to_fn)]` error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:11:23 + --> tests/ui/transmute_null_to_fn.rs:12:23 | LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -17,7 +17,7 @@ LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>()); = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:22:23 + --> tests/ui/transmute_null_to_fn.rs:23:23 | LL | let _: fn() = std::mem::transmute(ZPTR); | ^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -25,7 +25,7 @@ LL | let _: fn() = std::mem::transmute(ZPTR); = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:32:23 + --> tests/ui/transmute_null_to_fn.rs:33:23 | LL | let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -33,7 +33,7 @@ LL | let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:35:23 + --> tests/ui/transmute_null_to_fn.rs:36:23 | LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -41,7 +41,7 @@ LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:38:23 + --> tests/ui/transmute_null_to_fn.rs:39:23 | LL | let _: fn() = std::mem::transmute(ZPTR as *const u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed index 3a67be5f45d0b..476e7e35a1f61 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.fixed @@ -8,12 +8,12 @@ use std::mem::transmute; // Make sure we can do static lifetime transmutes unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T { - transmute::<&'a T, &'static T>(t) + unsafe { transmute::<&'a T, &'static T>(t) } } // Make sure we can do non-static lifetime transmutes unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T { - transmute::<&'a T, &'b T>(t) + unsafe { transmute::<&'a T, &'b T>(t) } } struct LifetimeParam<'a> { 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 01ad3a3296b17..7356668bcab5a 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs @@ -8,12 +8,12 @@ use std::mem::transmute; // Make sure we can do static lifetime transmutes unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T { - transmute::<&'a T, &'static T>(t) + unsafe { transmute::<&'a T, &'static T>(t) } } // Make sure we can do non-static lifetime transmutes unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T { - transmute::<&'a T, &'b T>(t) + unsafe { transmute::<&'a T, &'b T>(t) } } struct LifetimeParam<'a> { diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed index 1bd45bc10a39b..61e3ac2fe88e3 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed @@ -6,33 +6,35 @@ )] unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { - let _: &T = &*p; - //~^ transmute_ptr_to_ref - let _: &T = &*p; + unsafe { + let _: &T = &*p; + //~^ transmute_ptr_to_ref + let _: &T = &*p; - let _: &mut T = &mut *m; - //~^ transmute_ptr_to_ref - let _: &mut T = &mut *m; + let _: &mut T = &mut *m; + //~^ transmute_ptr_to_ref + let _: &mut T = &mut *m; - let _: &T = &*m; - //~^ transmute_ptr_to_ref - let _: &T = &*m; + let _: &T = &*m; + //~^ transmute_ptr_to_ref + let _: &T = &*m; - let _: &mut T = &mut *(p as *mut T); - //~^ transmute_ptr_to_ref - let _ = &mut *(p as *mut T); + let _: &mut T = &mut *(p as *mut T); + //~^ transmute_ptr_to_ref + let _ = &mut *(p as *mut T); - let _: &T = &*(o as *const T); - //~^ transmute_ptr_to_ref - let _: &T = &*(o as *const T); + let _: &T = &*(o as *const T); + //~^ transmute_ptr_to_ref + let _: &T = &*(o as *const T); - let _: &mut T = &mut *(om as *mut T); - //~^ transmute_ptr_to_ref - let _: &mut T = &mut *(om as *mut T); + let _: &mut T = &mut *(om as *mut T); + //~^ transmute_ptr_to_ref + let _: &mut T = &mut *(om as *mut T); - let _: &T = &*(om as *const T); - //~^ transmute_ptr_to_ref - let _: &T = &*(om as *const T); + let _: &T = &*(om as *const T); + //~^ transmute_ptr_to_ref + let _: &T = &*(om as *const T); + } } fn _issue1231() { @@ -54,47 +56,53 @@ fn _issue1231() { } unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { - match 0 { - 0 => &*x.cast::<&u32>(), - //~^ transmute_ptr_to_ref - 1 => &*y.cast::<&u32>(), - //~^ transmute_ptr_to_ref - 2 => &*x.cast::<&'b u32>(), - //~^ transmute_ptr_to_ref - _ => &*y.cast::<&'b u32>(), - //~^ transmute_ptr_to_ref + unsafe { + match 0 { + 0 => &*x.cast::<&u32>(), + //~^ transmute_ptr_to_ref + 1 => &*y.cast::<&u32>(), + //~^ transmute_ptr_to_ref + 2 => &*x.cast::<&'b u32>(), + //~^ transmute_ptr_to_ref + _ => &*y.cast::<&'b u32>(), + //~^ transmute_ptr_to_ref + } } } #[clippy::msrv = "1.38"] unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - let a = 0u32; - let a = &a as *const u32; - let _: &u32 = &*a; - //~^ transmute_ptr_to_ref - let _: &u32 = &*a.cast::(); - //~^ transmute_ptr_to_ref - match 0 { - 0 => &*x.cast::<&u32>(), + unsafe { + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = &*a; //~^ transmute_ptr_to_ref - _ => &*x.cast::<&'b u32>(), + let _: &u32 = &*a.cast::(); //~^ transmute_ptr_to_ref + match 0 { + 0 => &*x.cast::<&u32>(), + //~^ transmute_ptr_to_ref + _ => &*x.cast::<&'b u32>(), + //~^ transmute_ptr_to_ref + } } } #[clippy::msrv = "1.37"] unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - let a = 0u32; - let a = &a as *const u32; - let _: &u32 = &*a; - //~^ transmute_ptr_to_ref - let _: &u32 = &*(a as *const u32); - //~^ transmute_ptr_to_ref - match 0 { - 0 => &*(x as *const () as *const &u32), + unsafe { + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = &*a; //~^ transmute_ptr_to_ref - _ => &*(x as *const () as *const &'b u32), + let _: &u32 = &*(a as *const u32); //~^ transmute_ptr_to_ref + match 0 { + 0 => &*(x as *const () as *const &u32), + //~^ transmute_ptr_to_ref + _ => &*(x as *const () as *const &'b u32), + //~^ transmute_ptr_to_ref + } } } diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs index cbe64bf1ea6bc..48e2f527b554c 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs @@ -6,33 +6,35 @@ )] unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { - let _: &T = std::mem::transmute(p); - //~^ transmute_ptr_to_ref - let _: &T = &*p; + unsafe { + let _: &T = std::mem::transmute(p); + //~^ transmute_ptr_to_ref + let _: &T = &*p; - let _: &mut T = std::mem::transmute(m); - //~^ transmute_ptr_to_ref - let _: &mut T = &mut *m; + let _: &mut T = std::mem::transmute(m); + //~^ transmute_ptr_to_ref + let _: &mut T = &mut *m; - let _: &T = std::mem::transmute(m); - //~^ transmute_ptr_to_ref - let _: &T = &*m; + let _: &T = std::mem::transmute(m); + //~^ transmute_ptr_to_ref + let _: &T = &*m; - let _: &mut T = std::mem::transmute(p as *mut T); - //~^ transmute_ptr_to_ref - let _ = &mut *(p as *mut T); + let _: &mut T = std::mem::transmute(p as *mut T); + //~^ transmute_ptr_to_ref + let _ = &mut *(p as *mut T); - let _: &T = std::mem::transmute(o); - //~^ transmute_ptr_to_ref - let _: &T = &*(o as *const T); + let _: &T = std::mem::transmute(o); + //~^ transmute_ptr_to_ref + let _: &T = &*(o as *const T); - let _: &mut T = std::mem::transmute(om); - //~^ transmute_ptr_to_ref - let _: &mut T = &mut *(om as *mut T); + let _: &mut T = std::mem::transmute(om); + //~^ transmute_ptr_to_ref + let _: &mut T = &mut *(om as *mut T); - let _: &T = std::mem::transmute(om); - //~^ transmute_ptr_to_ref - let _: &T = &*(om as *const T); + let _: &T = std::mem::transmute(om); + //~^ transmute_ptr_to_ref + let _: &T = &*(om as *const T); + } } fn _issue1231() { @@ -54,47 +56,53 @@ fn _issue1231() { } unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { - match 0 { - 0 => std::mem::transmute(x), - //~^ transmute_ptr_to_ref - 1 => std::mem::transmute(y), - //~^ transmute_ptr_to_ref - 2 => std::mem::transmute::<_, &&'b u32>(x), - //~^ transmute_ptr_to_ref - _ => std::mem::transmute::<_, &&'b u32>(y), - //~^ transmute_ptr_to_ref + unsafe { + match 0 { + 0 => std::mem::transmute(x), + //~^ transmute_ptr_to_ref + 1 => std::mem::transmute(y), + //~^ transmute_ptr_to_ref + 2 => std::mem::transmute::<_, &&'b u32>(x), + //~^ transmute_ptr_to_ref + _ => std::mem::transmute::<_, &&'b u32>(y), + //~^ transmute_ptr_to_ref + } } } #[clippy::msrv = "1.38"] unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - let a = 0u32; - let a = &a as *const u32; - let _: &u32 = std::mem::transmute(a); - //~^ transmute_ptr_to_ref - let _: &u32 = std::mem::transmute::<_, &u32>(a); - //~^ transmute_ptr_to_ref - match 0 { - 0 => std::mem::transmute(x), + unsafe { + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = std::mem::transmute(a); //~^ transmute_ptr_to_ref - _ => std::mem::transmute::<_, &&'b u32>(x), + let _: &u32 = std::mem::transmute::<_, &u32>(a); //~^ transmute_ptr_to_ref + match 0 { + 0 => std::mem::transmute(x), + //~^ transmute_ptr_to_ref + _ => std::mem::transmute::<_, &&'b u32>(x), + //~^ transmute_ptr_to_ref + } } } #[clippy::msrv = "1.37"] unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - let a = 0u32; - let a = &a as *const u32; - let _: &u32 = std::mem::transmute(a); - //~^ transmute_ptr_to_ref - let _: &u32 = std::mem::transmute::<_, &u32>(a); - //~^ transmute_ptr_to_ref - match 0 { - 0 => std::mem::transmute(x), + unsafe { + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = std::mem::transmute(a); //~^ transmute_ptr_to_ref - _ => std::mem::transmute::<_, &&'b u32>(x), + let _: &u32 = std::mem::transmute::<_, &u32>(a); //~^ transmute_ptr_to_ref + match 0 { + 0 => std::mem::transmute(x), + //~^ transmute_ptr_to_ref + _ => std::mem::transmute::<_, &&'b u32>(x), + //~^ transmute_ptr_to_ref + } } } diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr index 7fad9b4065a56..7685c345c8619 100644 --- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr @@ -1,137 +1,137 @@ error: transmute from a pointer type (`*const T`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:9:17 + --> tests/ui/transmute_ptr_to_ref.rs:10:21 | -LL | let _: &T = std::mem::transmute(p); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` +LL | let _: &T = std::mem::transmute(p); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` | = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::transmute_ptr_to_ref)]` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> tests/ui/transmute_ptr_to_ref.rs:13:21 + --> tests/ui/transmute_ptr_to_ref.rs:14:25 | -LL | let _: &mut T = std::mem::transmute(m); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m` +LL | let _: &mut T = std::mem::transmute(m); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m` error: transmute from a pointer type (`*mut T`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:17:17 + --> tests/ui/transmute_ptr_to_ref.rs:18:21 | -LL | let _: &T = std::mem::transmute(m); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` +LL | let _: &T = std::mem::transmute(m); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> tests/ui/transmute_ptr_to_ref.rs:21:21 + --> tests/ui/transmute_ptr_to_ref.rs:22:25 | -LL | let _: &mut T = std::mem::transmute(p as *mut T); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)` +LL | let _: &mut T = std::mem::transmute(p as *mut T); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)` error: transmute from a pointer type (`*const U`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:25:17 + --> tests/ui/transmute_ptr_to_ref.rs:26:21 | -LL | let _: &T = std::mem::transmute(o); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)` +LL | let _: &T = std::mem::transmute(o); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`) - --> tests/ui/transmute_ptr_to_ref.rs:29:21 + --> tests/ui/transmute_ptr_to_ref.rs:30:25 | -LL | let _: &mut T = std::mem::transmute(om); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)` +LL | let _: &mut T = std::mem::transmute(om); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) - --> tests/ui/transmute_ptr_to_ref.rs:33:17 + --> tests/ui/transmute_ptr_to_ref.rs:34:21 | -LL | let _: &T = std::mem::transmute(om); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` +LL | let _: &T = std::mem::transmute(om); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`) - --> tests/ui/transmute_ptr_to_ref.rs:44:32 + --> tests/ui/transmute_ptr_to_ref.rs:46:32 | LL | let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`) - --> tests/ui/transmute_ptr_to_ref.rs:47:33 + --> tests/ui/transmute_ptr_to_ref.rs:49:33 | LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`) - --> tests/ui/transmute_ptr_to_ref.rs:52:14 + --> tests/ui/transmute_ptr_to_ref.rs:54:14 | LL | unsafe { std::mem::transmute::<_, Bar>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:58:14 + --> tests/ui/transmute_ptr_to_ref.rs:61:18 | -LL | 0 => std::mem::transmute(x), - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` +LL | 0 => std::mem::transmute(x), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:60:14 + --> tests/ui/transmute_ptr_to_ref.rs:63:18 | -LL | 1 => std::mem::transmute(y), - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()` +LL | 1 => std::mem::transmute(y), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:62:14 + --> tests/ui/transmute_ptr_to_ref.rs:65:18 | -LL | 2 => std::mem::transmute::<_, &&'b u32>(x), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` +LL | 2 => std::mem::transmute::<_, &&'b u32>(x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:64:14 + --> tests/ui/transmute_ptr_to_ref.rs:67:18 | -LL | _ => std::mem::transmute::<_, &&'b u32>(y), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()` +LL | _ => std::mem::transmute::<_, &&'b u32>(y), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:73:19 + --> tests/ui/transmute_ptr_to_ref.rs:78:23 | -LL | let _: &u32 = std::mem::transmute(a); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` +LL | let _: &u32 = std::mem::transmute(a); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:75:19 + --> tests/ui/transmute_ptr_to_ref.rs:80:23 | -LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::()` +LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:78:14 + --> tests/ui/transmute_ptr_to_ref.rs:83:18 | -LL | 0 => std::mem::transmute(x), - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` +LL | 0 => std::mem::transmute(x), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:80:14 + --> tests/ui/transmute_ptr_to_ref.rs:85:18 | -LL | _ => std::mem::transmute::<_, &&'b u32>(x), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` +LL | _ => std::mem::transmute::<_, &&'b u32>(x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:89:19 + --> tests/ui/transmute_ptr_to_ref.rs:96:23 | -LL | let _: &u32 = std::mem::transmute(a); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` +LL | let _: &u32 = std::mem::transmute(a); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:91:19 + --> tests/ui/transmute_ptr_to_ref.rs:98:23 | -LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)` +LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:94:14 + --> tests/ui/transmute_ptr_to_ref.rs:101:18 | -LL | 0 => std::mem::transmute(x), - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)` +LL | 0 => std::mem::transmute(x), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> tests/ui/transmute_ptr_to_ref.rs:96:14 + --> tests/ui/transmute_ptr_to_ref.rs:103:18 | -LL | _ => std::mem::transmute::<_, &&'b u32>(x), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` +LL | _ => std::mem::transmute::<_, &&'b u32>(x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/transmuting_null.rs b/src/tools/clippy/tests/ui/transmuting_null.rs index bcd35bbd4e72a..f3eb5060cd0d3 100644 --- a/src/tools/clippy/tests/ui/transmuting_null.rs +++ b/src/tools/clippy/tests/ui/transmuting_null.rs @@ -3,6 +3,7 @@ #![allow(clippy::zero_ptr)] #![allow(clippy::transmute_ptr_to_ref)] #![allow(clippy::eq_op, clippy::missing_transmute_annotations)] +#![allow(clippy::manual_dangling_ptr)] // Easy to lint because these only span one line. fn one_liners() { diff --git a/src/tools/clippy/tests/ui/transmuting_null.stderr b/src/tools/clippy/tests/ui/transmuting_null.stderr index 84e6e374d5253..c68e4102e405b 100644 --- a/src/tools/clippy/tests/ui/transmuting_null.stderr +++ b/src/tools/clippy/tests/ui/transmuting_null.stderr @@ -1,5 +1,5 @@ error: transmuting a known null pointer into a reference - --> tests/ui/transmuting_null.rs:10:23 + --> tests/ui/transmuting_null.rs:11:23 | LL | let _: &u64 = std::mem::transmute(0 as *const u64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,13 +8,13 @@ LL | let _: &u64 = std::mem::transmute(0 as *const u64); = help: to override `-D warnings` add `#[allow(clippy::transmuting_null)]` error: transmuting a known null pointer into a reference - --> tests/ui/transmuting_null.rs:13:23 + --> tests/ui/transmuting_null.rs:14:23 | LL | let _: &u64 = std::mem::transmute(std::ptr::null::()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmuting a known null pointer into a reference - --> tests/ui/transmuting_null.rs:24:23 + --> tests/ui/transmuting_null.rs:25:23 | LL | let _: &u64 = std::mem::transmute(ZPTR); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/type_complexity.rs b/src/tools/clippy/tests/ui/type_complexity.rs index 89c4955c9f6f0..9d145516d6107 100644 --- a/src/tools/clippy/tests/ui/type_complexity.rs +++ b/src/tools/clippy/tests/ui/type_complexity.rs @@ -1,6 +1,5 @@ -#![warn(clippy::all)] -#![allow(unused, clippy::needless_pass_by_value, clippy::vec_box, clippy::useless_vec)] #![feature(associated_type_defaults)] +#![allow(clippy::needless_pass_by_value, clippy::vec_box, clippy::useless_vec)] type Alias = Vec>>; // no warning here diff --git a/src/tools/clippy/tests/ui/type_complexity.stderr b/src/tools/clippy/tests/ui/type_complexity.stderr index 181e04d38e9a4..a7f6a074a4a4d 100644 --- a/src/tools/clippy/tests/ui/type_complexity.stderr +++ b/src/tools/clippy/tests/ui/type_complexity.stderr @@ -1,5 +1,5 @@ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:7:12 + --> tests/ui/type_complexity.rs:6:12 | LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,85 +8,85 @@ LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); = help: to override `-D warnings` add `#[allow(clippy::type_complexity)]` error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:10:12 + --> tests/ui/type_complexity.rs:9:12 | LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:14:8 + --> tests/ui/type_complexity.rs:13:8 | LL | f: Vec>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:18:11 + --> tests/ui/type_complexity.rs:17:11 | LL | struct Ts(Vec>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:22:11 + --> tests/ui/type_complexity.rs:21:11 | LL | Tuple(Vec>>), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:24:17 + --> tests/ui/type_complexity.rs:23:17 | LL | Struct { f: Vec>> }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:29:14 + --> tests/ui/type_complexity.rs:28:14 | LL | const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:32:30 + --> tests/ui/type_complexity.rs:31:30 | LL | fn impl_method(&self, p: Vec>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:37:14 + --> tests/ui/type_complexity.rs:36:14 | LL | const A: Vec>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:40:14 + --> tests/ui/type_complexity.rs:39:14 | LL | type B = Vec>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:43:25 + --> tests/ui/type_complexity.rs:42:25 | LL | fn method(&self, p: Vec>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:46:29 + --> tests/ui/type_complexity.rs:45:29 | LL | fn def_method(&self, p: Vec>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:59:15 + --> tests/ui/type_complexity.rs:58:15 | LL | fn test1() -> Vec>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:65:14 + --> tests/ui/type_complexity.rs:64:14 | LL | fn test2(_x: Vec>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions - --> tests/ui/type_complexity.rs:69:13 + --> tests/ui/type_complexity.rs:68:13 | LL | let _y: Vec>> = vec![]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs index d325887bfba3f..e75678d5fd93e 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs @@ -12,7 +12,7 @@ pub fn foo(_t: T) where T: Copy, T: Clone, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { unimplemented!(); } @@ -30,7 +30,7 @@ trait LintBounds where Self: Clone, Self: Copy + Default + Ord, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds Self: Add + AddAssign + Sub + SubAssign, Self: Mul + MulAssign + Div + DivAssign, { @@ -105,13 +105,13 @@ where pub fn f() where T: Clone, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { } pub fn g() where T: ?Sized, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds { } @@ -137,10 +137,27 @@ mod issue8772_pass { pub fn f(arg: usize) where T: Trait, Box<[String]>, bool> + 'static, - //~^ ERROR: this type has already been used as a bound predicate + //~^ type_repetition_in_bounds U: Clone + Sync + 'static, { } } +struct Issue14744<'a, K: 'a> +where + K: Clone, +{ + phantom: std::marker::PhantomData<&'a K>, +} +//~^^^^ type_repetition_in_bounds + +struct ComplexType +where + Vec: Clone, + Vec: Clone, +{ + t: T, +} +//~^^^^ type_repetition_in_bounds + fn main() {} diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr index 77944c9504579..de1b14da1985f 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr @@ -1,4 +1,4 @@ -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:14:5 | LL | T: Clone, @@ -11,7 +11,7 @@ note: the lint level is defined here LL | #![deny(clippy::type_repetition_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this type has already been used as a bound predicate +error: type `Self` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:32:5 | LL | Self: Copy + Default + Ord, @@ -19,7 +19,7 @@ LL | Self: Copy + Default + Ord, | = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:107:5 | LL | T: Clone, @@ -27,7 +27,7 @@ LL | T: Clone, | = help: consider combining the bounds: `T: ?Sized + Clone` -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:113:5 | LL | T: ?Sized, @@ -35,13 +35,29 @@ LL | T: ?Sized, | = help: consider combining the bounds: `T: Clone + ?Sized` -error: this type has already been used as a bound predicate +error: type `T` has already been used as a bound predicate --> tests/ui/type_repetition_in_bounds.rs:139:9 | LL | T: Trait, Box<[String]>, bool> + 'static, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider combining the bounds: `T: ?Sized + Trait, Box<[String]>, bool>` + = help: consider combining the bounds: `T: ?Sized + Trait, Box<[String]>, bool> + 'static` -error: aborting due to 5 previous errors +error: type `K` has already been used as a bound predicate + --> tests/ui/type_repetition_in_bounds.rs:148:5 + | +LL | K: Clone, + | ^^^^^^^^ + | + = help: consider combining the bounds: `K: 'a + Clone` + +error: type `Vec` has already been used as a bound predicate + --> tests/ui/type_repetition_in_bounds.rs:157:5 + | +LL | Vec: Clone, + | ^^^^^^^^^^^^^ + | + = help: consider combining the bounds: `Vec: Clone + Clone` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs index a48397137992f..eeb281322da92 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.rs +++ b/src/tools/clippy/tests/ui/uninit_vec.rs @@ -15,9 +15,17 @@ union MyOwnMaybeUninit { // https://github.com/rust-lang/rust/issues/119620 unsafe fn requires_paramenv() { - let mut vec = Vec::>::with_capacity(1); + unsafe { + let mut vec = Vec::>::with_capacity(1); + //~^ uninit_vec + vec.set_len(1); + } + + let mut vec = Vec::>::with_capacity(2); //~^ uninit_vec - vec.set_len(1); + unsafe { + vec.set_len(2); + } } fn main() { diff --git a/src/tools/clippy/tests/ui/uninit_vec.stderr b/src/tools/clippy/tests/ui/uninit_vec.stderr index 7ff6140a2c3ec..1b821ef004e6f 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.stderr +++ b/src/tools/clippy/tests/ui/uninit_vec.stderr @@ -1,18 +1,29 @@ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:18:5 + --> tests/ui/uninit_vec.rs:24:5 | -LL | let mut vec = Vec::>::with_capacity(1); +LL | let mut vec = Vec::>::with_capacity(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | vec.set_len(1); - | ^^^^^^^^^^^^^^ +... +LL | vec.set_len(2); + | ^^^^^^^^^^^^^^ | = help: initialize the buffer or wrap the content in `MaybeUninit` = note: `-D clippy::uninit-vec` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::uninit_vec)]` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:25:5 + --> tests/ui/uninit_vec.rs:19:9 + | +LL | let mut vec = Vec::>::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | vec.set_len(1); + | ^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:33:5 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +34,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:33:5 + --> tests/ui/uninit_vec.rs:41:5 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -34,7 +45,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:41:5 + --> tests/ui/uninit_vec.rs:49:5 | LL | let mut vec: Vec = Vec::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -43,7 +54,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:49:5 + --> tests/ui/uninit_vec.rs:57:5 | LL | let mut vec: Vec = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +63,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:56:5 + --> tests/ui/uninit_vec.rs:64:5 | LL | let mut vec: Vec = Vec::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +72,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:76:5 + --> tests/ui/uninit_vec.rs:84:5 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +83,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:87:5 + --> tests/ui/uninit_vec.rs:95:5 | LL | my_vec.vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,7 +94,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:94:5 + --> tests/ui/uninit_vec.rs:102:5 | LL | my_vec.vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,7 +105,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:65:9 + --> tests/ui/uninit_vec.rs:73:9 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +116,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:70:9 + --> tests/ui/uninit_vec.rs:78:9 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -116,7 +127,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:150:9 + --> tests/ui/uninit_vec.rs:158:9 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -127,7 +138,7 @@ LL | vec.set_len(10); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:178:9 + --> tests/ui/uninit_vec.rs:186:9 | LL | let mut vec: Vec> = Vec::with_capacity(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -138,7 +149,7 @@ LL | vec.set_len(1); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:192:9 + --> tests/ui/uninit_vec.rs:200:9 | LL | let mut vec: Vec> = Vec::with_capacity(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -148,5 +159,5 @@ LL | vec.set_len(1); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.fixed b/src/tools/clippy/tests/ui/unnecessary_cast.fixed index ba167e79a308b..91ff4b9ee7713 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_cast.fixed @@ -266,7 +266,21 @@ mod fixable { // Issue #11968: The suggestion for this lint removes the parentheses and leave the code as // `*x.pow(2)` which tries to dereference the return value rather than `x`. fn issue_11968(x: &usize) -> usize { - { *x }.pow(2) + (*x).pow(2) + //~^ unnecessary_cast + } + + #[allow(clippy::cast_lossless)] + fn issue_14640() { + let x = 5usize; + let vec: Vec = vec![1, 2, 3, 4, 5]; + assert_eq!(vec.len(), x); + //~^ unnecessary_cast + + let _ = (5i32 as i64).abs(); + //~^ unnecessary_cast + + let _ = 5i32 as i64; //~^ unnecessary_cast } } diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs index 0f90a8b05965a..5444a914db167 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs @@ -269,4 +269,18 @@ mod fixable { (*x as usize).pow(2) //~^ unnecessary_cast } + + #[allow(clippy::cast_lossless)] + fn issue_14640() { + let x = 5usize; + let vec: Vec = vec![1, 2, 3, 4, 5]; + assert_eq!(vec.len(), x as usize); + //~^ unnecessary_cast + + let _ = (5i32 as i64 as i64).abs(); + //~^ unnecessary_cast + + let _ = 5i32 as i64 as i64; + //~^ unnecessary_cast + } } diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.stderr b/src/tools/clippy/tests/ui/unnecessary_cast.stderr index c83770c1a2992..3e3c5eb81c105 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_cast.stderr @@ -245,7 +245,25 @@ error: casting to the same type is unnecessary (`usize` -> `usize`) --> tests/ui/unnecessary_cast.rs:269:9 | LL | (*x as usize).pow(2) - | ^^^^^^^^^^^^^ help: try: `{ *x }` + | ^^^^^^^^^^^^^ help: try: `(*x)` -error: aborting due to 41 previous errors +error: casting to the same type is unnecessary (`usize` -> `usize`) + --> tests/ui/unnecessary_cast.rs:277:31 + | +LL | assert_eq!(vec.len(), x as usize); + | ^^^^^^^^^^ help: try: `x` + +error: casting to the same type is unnecessary (`i64` -> `i64`) + --> tests/ui/unnecessary_cast.rs:280:17 + | +LL | let _ = (5i32 as i64 as i64).abs(); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `(5i32 as i64)` + +error: casting to the same type is unnecessary (`i64` -> `i64`) + --> tests/ui/unnecessary_cast.rs:283:17 + | +LL | let _ = 5i32 as i64 as i64; + | ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64` + +error: aborting due to 44 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.rs b/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.rs index 2bb64c3e80e34..4b1f4f76cc45e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.rs @@ -17,8 +17,10 @@ mod issue11113 { impl TearOff { unsafe fn query(&self) { - ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() - //~^ unnecessary_cast + unsafe { + ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() + //~^ unnecessary_cast + } } } } diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.stderr index 6ba1c78730667..6b26bea9de2a7 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_cast_unfixable.stderr @@ -8,10 +8,10 @@ LL | let _ = std::ptr::null() as *const u8; = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` error: casting raw pointers to the same type and constness is unnecessary (`*mut issue11113::Vtbl` -> `*mut issue11113::Vtbl`) - --> tests/ui/unnecessary_cast_unfixable.rs:20:16 + --> tests/ui/unnecessary_cast_unfixable.rs:21:20 | -LL | ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `*(self.object as *mut *mut _)` +LL | ((*(*(self.object as *mut *mut _) as *mut Vtbl)).query)() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `*(self.object as *mut *mut _)` error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.rs b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs index c4f1b6bc7e3d7..85582c399ce5f 100644 --- a/src/tools/clippy/tests/ui/unnecessary_filter_map.rs +++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs @@ -1,5 +1,4 @@ -//@no-rustfix -#![allow(dead_code)] +#![allow(clippy::redundant_closure)] fn main() { let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); @@ -27,9 +26,7 @@ fn main() { let _ = (0..4).filter_map(Some); let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - //~^ redundant_closure - //~| unnecessary_filter_map - //~| unnecessary_filter_map + //~^ unnecessary_filter_map } fn filter_map_none_changes_item_type() -> impl Iterator { diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr index 6683444b72730..a879633e10f2a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr @@ -1,14 +1,14 @@ -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:5:13 +error: this `.filter_map(..)` can be written more simply using `.filter(..)` + --> tests/ui/unnecessary_filter_map.rs:4:13 | LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `filter` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_filter_map)]` -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:8:13 +error: this `.filter_map(..)` can be written more simply using `.filter(..)` + --> tests/ui/unnecessary_filter_map.rs:7:13 | LL | let _ = (0..4).filter_map(|x| { | _____________^ @@ -18,10 +18,10 @@ LL | | if x > 1 { ... | LL | | None LL | | }); - | |______^ help: try instead: `filter` + | |______^ -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:16:13 +error: this `.filter_map(..)` can be written more simply using `.filter(..)` + --> tests/ui/unnecessary_filter_map.rs:15:13 | LL | let _ = (0..4).filter_map(|x| match x { | _____________^ @@ -29,40 +29,25 @@ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), LL | | }); - | |______^ help: try instead: `filter` + | |______^ -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:22:13 +error: this `.filter_map(..)` can be written more simply using `.map(..)` + --> tests/ui/unnecessary_filter_map.rs:21:13 | LL | let _ = (0..4).filter_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: redundant closure - --> tests/ui/unnecessary_filter_map.rs:29:57 +error: this call to `.filter_map(..)` is unnecessary + --> tests/ui/unnecessary_filter_map.rs:28:61 | LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - | ^^^^^^^^^^^ help: replace the closure with the function itself: `Some` - | - = note: `-D clippy::redundant-closure` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` - -error: filter_map is unnecessary - --> tests/ui/unnecessary_filter_map.rs:29:61 - | -LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - | ^^^^ help: try removing the filter_map - -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:29:13 - | -LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map` + | ^^^^ -error: this `.filter_map` can be written more simply - --> tests/ui/unnecessary_filter_map.rs:169:14 +error: this `.filter_map(..)` can be written more simply using `.filter(..)` + --> tests/ui/unnecessary_filter_map.rs:166:14 | LL | let _x = std::iter::once(1).filter_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `filter` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_find_map.rs b/src/tools/clippy/tests/ui/unnecessary_find_map.rs index 8c8a3799f0216..33ba7074d623b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_find_map.rs +++ b/src/tools/clippy/tests/ui/unnecessary_find_map.rs @@ -1,4 +1,3 @@ -//@no-rustfix #![allow(dead_code)] fn main() { diff --git a/src/tools/clippy/tests/ui/unnecessary_find_map.stderr b/src/tools/clippy/tests/ui/unnecessary_find_map.stderr index 94e320773a6fe..3754a3d99538e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_find_map.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_find_map.stderr @@ -1,14 +1,14 @@ -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:5:13 +error: this `.find_map(..)` can be written more simply using `.find(..)` + --> tests/ui/unnecessary_find_map.rs:4:13 | LL | let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `find` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-find-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_find_map)]` -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:8:13 +error: this `.find_map(..)` can be written more simply using `.find(..)` + --> tests/ui/unnecessary_find_map.rs:7:13 | LL | let _ = (0..4).find_map(|x| { | _____________^ @@ -18,10 +18,10 @@ LL | | if x > 1 { ... | LL | | None LL | | }); - | |______^ help: try instead: `find` + | |______^ -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:16:13 +error: this `.find_map(..)` can be written more simply using `.find(..)` + --> tests/ui/unnecessary_find_map.rs:15:13 | LL | let _ = (0..4).find_map(|x| match x { | _____________^ @@ -29,19 +29,19 @@ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), LL | | }); - | |______^ help: try instead: `find` + | |______^ -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:22:13 +error: this `.find_map(..)` can be written more simply using `.map(..).next()` + --> tests/ui/unnecessary_find_map.rs:21:13 | LL | let _ = (0..4).find_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map(..).next()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this `.find_map` can be written more simply - --> tests/ui/unnecessary_find_map.rs:34:14 +error: this `.find_map(..)` can be written more simply using `.find(..)` + --> tests/ui/unnecessary_find_map.rs:33:14 | LL | let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `find` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed index aed2dbe1f1ce4..61f2e3745ad05 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed @@ -1,4 +1,4 @@ -#![allow(unused_assignments)] +#![allow(unused_assignments, clippy::uninlined_format_args)] #![warn(clippy::unnecessary_to_owned)] #[allow(dead_code)] diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs index 12fdd150e4233..b90ca00a5fece 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs @@ -1,4 +1,4 @@ -#![allow(unused_assignments)] +#![allow(unused_assignments, clippy::uninlined_format_args)] #![warn(clippy::unnecessary_to_owned)] #[allow(dead_code)] diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed index 9a32908163897..409a8efbfeb95 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed @@ -321,3 +321,7 @@ fn panicky_arithmetic_ops(x: usize, y: isize) { let _x = false.then_some(f1 + f2); //~^ unnecessary_lazy_evaluations } + +fn issue14578() { + let _: Box> = Box::new(true.then(async || 42).unwrap()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs index 2d05ef5c29175..54735023a935d 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs @@ -321,3 +321,7 @@ fn panicky_arithmetic_ops(x: usize, y: isize) { let _x = false.then(|| f1 + f2); //~^ unnecessary_lazy_evaluations } + +fn issue14578() { + let _: Box> = Box::new(true.then(async || 42).unwrap()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed index 05dfb72f48d2d..645b56fe95e74 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed @@ -1,9 +1,10 @@ #![allow( clippy::deref_addrof, - dead_code, - unused, clippy::no_effect, - clippy::unnecessary_struct_initialization + clippy::uninlined_format_args, + clippy::unnecessary_struct_initialization, + dead_code, + unused )] #![warn(clippy::unnecessary_operation)] diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs index 6ef74c3eb1c1e..97e90269c5c0c 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.rs +++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs @@ -1,9 +1,10 @@ #![allow( clippy::deref_addrof, - dead_code, - unused, clippy::no_effect, - clippy::unnecessary_struct_initialization + clippy::uninlined_format_args, + clippy::unnecessary_struct_initialization, + dead_code, + unused )] #![warn(clippy::unnecessary_operation)] diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr index eb98af09e7a3d..0fda1dfde1903 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr @@ -1,5 +1,5 @@ error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:70:5 + --> tests/ui/unnecessary_operation.rs:71:5 | LL | Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` @@ -8,103 +8,103 @@ LL | Tuple(get_number()); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_operation)]` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:72:5 + --> tests/ui/unnecessary_operation.rs:73:5 | LL | Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:74:5 + --> tests/ui/unnecessary_operation.rs:75:5 | LL | Struct { ..get_struct() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:76:5 + --> tests/ui/unnecessary_operation.rs:77:5 | LL | Enum::Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:78:5 + --> tests/ui/unnecessary_operation.rs:79:5 | LL | Enum::Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:80:5 + --> tests/ui/unnecessary_operation.rs:81:5 | LL | 5 + get_number(); | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:82:5 + --> tests/ui/unnecessary_operation.rs:83:5 | LL | *&get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:84:5 + --> tests/ui/unnecessary_operation.rs:85:5 | LL | &get_number(); | ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:86:5 + --> tests/ui/unnecessary_operation.rs:87:5 | LL | (5, 6, get_number()); | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:88:5 + --> tests/ui/unnecessary_operation.rs:89:5 | LL | get_number()..; | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:90:5 + --> tests/ui/unnecessary_operation.rs:91:5 | LL | ..get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:92:5 + --> tests/ui/unnecessary_operation.rs:93:5 | LL | 5..get_number(); | ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:94:5 + --> tests/ui/unnecessary_operation.rs:95:5 | LL | [42, get_number()]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:96:5 + --> tests/ui/unnecessary_operation.rs:97:5 | LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:98:5 + --> tests/ui/unnecessary_operation.rs:99:5 | LL | (42, get_number()).1; | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:100:5 + --> tests/ui/unnecessary_operation.rs:101:5 | LL | [get_number(); 55]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:102:5 + --> tests/ui/unnecessary_operation.rs:103:5 | LL | [42; 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:104:5 + --> tests/ui/unnecessary_operation.rs:105:5 | LL | / { LL | | @@ -113,7 +113,7 @@ LL | | }; | |______^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:108:5 + --> tests/ui/unnecessary_operation.rs:109:5 | LL | / FooString { LL | | @@ -122,7 +122,7 @@ LL | | }; | |______^ help: statement can be reduced to: `String::from("blah");` error: unnecessary operation - --> tests/ui/unnecessary_operation.rs:149:5 + --> tests/ui/unnecessary_operation.rs:150:5 | LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` diff --git a/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.rs b/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.rs index 12663ec9a528f..6652efd9ae1d8 100644 --- a/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.rs +++ b/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.rs @@ -1,4 +1,5 @@ #![warn(clippy::unnecessary_debug_formatting)] +#![allow(clippy::uninlined_format_args)] use std::ffi::{OsStr, OsString}; diff --git a/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.stderr b/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.stderr index 001309ab817a1..382e59b046193 100644 --- a/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.stderr @@ -1,5 +1,5 @@ error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:14:22 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:15:22 | LL | println!("{:?}", os_str); | ^^^^^^ @@ -10,7 +10,7 @@ LL | println!("{:?}", os_str); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_debug_formatting)]` error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:15:22 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:16:22 | LL | println!("{:?}", os_string); | ^^^^^^^^^ @@ -19,7 +19,7 @@ LL | println!("{:?}", os_string); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:17:16 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:18:16 | LL | println!("{os_str:?}"); | ^^^^^^ @@ -28,7 +28,7 @@ LL | println!("{os_str:?}"); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:18:16 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:19:16 | LL | println!("{os_string:?}"); | ^^^^^^^^^ @@ -37,7 +37,7 @@ LL | println!("{os_string:?}"); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `format!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:20:37 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:21:37 | LL | let _: String = format!("{:?}", os_str); | ^^^^^^ @@ -46,7 +46,7 @@ LL | let _: String = format!("{:?}", os_str); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `format!` args - --> tests/ui/unnecessary_os_str_debug_formatting.rs:21:37 + --> tests/ui/unnecessary_os_str_debug_formatting.rs:22:37 | LL | let _: String = format!("{:?}", os_string); | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.rs b/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.rs index 02adeece2809b..215e0d5d7802e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.rs +++ b/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.rs @@ -1,4 +1,5 @@ #![warn(clippy::unnecessary_debug_formatting)] +#![allow(clippy::uninlined_format_args)] use std::ffi::{OsStr, OsString}; use std::ops::Deref; @@ -41,3 +42,9 @@ fn main() { let deref_path = DerefPath { path }; println!("{:?}", &*deref_path); //~ unnecessary_debug_formatting } + +#[test] +fn issue_14345() { + let input = std::path::Path::new("/foo/bar"); + assert!(input.ends_with("baz"), "{input:?}"); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.stderr b/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.stderr index f12fa72c84b35..d244b9ad6716a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_path_debug_formatting.stderr @@ -1,5 +1,5 @@ error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:29:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:30:22 | LL | println!("{:?}", os_str); | ^^^^^^ @@ -10,7 +10,7 @@ LL | println!("{:?}", os_str); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_debug_formatting)]` error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:30:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:31:22 | LL | println!("{:?}", os_string); | ^^^^^^^^^ @@ -19,7 +19,7 @@ LL | println!("{:?}", os_string); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:32:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:33:22 | LL | println!("{:?}", path); | ^^^^ @@ -28,7 +28,7 @@ LL | println!("{:?}", path); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:33:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:34:22 | LL | println!("{:?}", path_buf); | ^^^^^^^^ @@ -37,7 +37,7 @@ LL | println!("{:?}", path_buf); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:35:16 + --> tests/ui/unnecessary_path_debug_formatting.rs:36:16 | LL | println!("{path:?}"); | ^^^^ @@ -46,7 +46,7 @@ LL | println!("{path:?}"); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:36:16 + --> tests/ui/unnecessary_path_debug_formatting.rs:37:16 | LL | println!("{path_buf:?}"); | ^^^^^^^^ @@ -55,7 +55,7 @@ LL | println!("{path_buf:?}"); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `format!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:38:37 + --> tests/ui/unnecessary_path_debug_formatting.rs:39:37 | LL | let _: String = format!("{:?}", path); | ^^^^ @@ -64,7 +64,7 @@ LL | let _: String = format!("{:?}", path); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `format!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:39:37 + --> tests/ui/unnecessary_path_debug_formatting.rs:40:37 | LL | let _: String = format!("{:?}", path_buf); | ^^^^^^^^ @@ -73,7 +73,7 @@ LL | let _: String = format!("{:?}", path_buf); = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed error: unnecessary `Debug` formatting in `println!` args - --> tests/ui/unnecessary_path_debug_formatting.rs:42:22 + --> tests/ui/unnecessary_path_debug_formatting.rs:43:22 | LL | println!("{:?}", &*deref_path); | ^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed index bf271aef763bb..316eac0b58b79 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed @@ -1,10 +1,11 @@ #![allow( + clippy::manual_async_fn, clippy::needless_borrow, clippy::needless_borrows_for_generic_args, - clippy::ptr_arg, - clippy::manual_async_fn, clippy::needless_lifetimes, - clippy::owned_cow + clippy::owned_cow, + clippy::ptr_arg, + clippy::uninlined_format_args )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] @@ -195,19 +196,11 @@ fn main() { //~^ unnecessary_to_owned let _ = slice.iter().copied(); //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - //~^ unnecessary_to_owned let _ = slice.iter().copied(); //~^ unnecessary_to_owned let _ = slice.iter().copied(); //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].iter().cloned(); - //~^ unnecessary_to_owned let _ = check_files(&[FileType::Account]); @@ -317,19 +310,6 @@ fn get_file_path(_file_type: &FileType) -> Result impl IntoIterator { cow.into_owned().into_iter() } + +mod issue_14242 { + use std::rc::Rc; + + #[derive(Copy, Clone)] + struct Foo; + + fn rc_slice_provider() -> Rc<[Foo]> { + Rc::from([Foo]) + } + + fn iterator_provider() -> impl Iterator { + rc_slice_provider().to_vec().into_iter() + } +} + +fn issue14833() { + use std::collections::HashSet; + let mut s = HashSet::<&String>::new(); + s.remove(&"hello".to_owned()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index 95b95ab6bd222..f2dbd1db3c9f6 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -1,10 +1,11 @@ #![allow( + clippy::manual_async_fn, clippy::needless_borrow, clippy::needless_borrows_for_generic_args, - clippy::ptr_arg, - clippy::manual_async_fn, clippy::needless_lifetimes, - clippy::owned_cow + clippy::owned_cow, + clippy::ptr_arg, + clippy::uninlined_format_args )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] @@ -195,19 +196,11 @@ fn main() { //~^ unnecessary_to_owned let _ = slice.to_owned().into_iter(); //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); - //~^ unnecessary_to_owned - let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); - //~^ unnecessary_to_owned let _ = IntoIterator::into_iter(slice.to_vec()); //~^ unnecessary_to_owned let _ = IntoIterator::into_iter(slice.to_owned()); //~^ unnecessary_to_owned - let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); - //~^ unnecessary_to_owned - let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); - //~^ unnecessary_to_owned let _ = check_files(&[FileType::Account]); @@ -317,19 +310,6 @@ fn get_file_path(_file_type: &FileType) -> Result impl IntoIterator { cow.into_owned().into_iter() } + +mod issue_14242 { + use std::rc::Rc; + + #[derive(Copy, Clone)] + struct Foo; + + fn rc_slice_provider() -> Rc<[Foo]> { + Rc::from([Foo]) + } + + fn iterator_provider() -> impl Iterator { + rc_slice_provider().to_vec().into_iter() + } +} + +fn issue14833() { + use std::collections::HashSet; + let mut s = HashSet::<&String>::new(); + s.remove(&"hello".to_owned()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr index 4daa3876e60eb..6c52be8393010 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr @@ -1,11 +1,11 @@ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:225:64 + --> tests/ui/unnecessary_to_owned.rs:218:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:225:20 + --> tests/ui/unnecessary_to_owned.rs:218:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,55 +13,55 @@ LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()) = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:227:40 + --> tests/ui/unnecessary_to_owned.rs:220:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:227:21 + --> tests/ui/unnecessary_to_owned.rs:220:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:229:48 + --> tests/ui/unnecessary_to_owned.rs:222:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:229:19 + --> tests/ui/unnecessary_to_owned.rs:222:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:231:35 + --> tests/ui/unnecessary_to_owned.rs:224:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:231:18 + --> tests/ui/unnecessary_to_owned.rs:224:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> tests/ui/unnecessary_to_owned.rs:233:39 + --> tests/ui/unnecessary_to_owned.rs:226:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> tests/ui/unnecessary_to_owned.rs:233:20 + --> tests/ui/unnecessary_to_owned.rs:226:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:65:36 + --> tests/ui/unnecessary_to_owned.rs:66:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -70,415 +70,391 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:67:19 + --> tests/ui/unnecessary_to_owned.rs:68:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> tests/ui/unnecessary_to_owned.rs:70:20 + --> tests/ui/unnecessary_to_owned.rs:71:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:72:38 + --> tests/ui/unnecessary_to_owned.rs:73:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:74:20 + --> tests/ui/unnecessary_to_owned.rs:75:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> tests/ui/unnecessary_to_owned.rs:77:18 + --> tests/ui/unnecessary_to_owned.rs:78:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:79:34 + --> tests/ui/unnecessary_to_owned.rs:80:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:81:18 + --> tests/ui/unnecessary_to_owned.rs:82:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:84:17 + --> tests/ui/unnecessary_to_owned.rs:85:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:86:30 + --> tests/ui/unnecessary_to_owned.rs:87:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:88:17 + --> tests/ui/unnecessary_to_owned.rs:89:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:90:17 + --> tests/ui/unnecessary_to_owned.rs:91:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:93:19 + --> tests/ui/unnecessary_to_owned.rs:94:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:95:36 + --> tests/ui/unnecessary_to_owned.rs:96:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:97:19 + --> tests/ui/unnecessary_to_owned.rs:98:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:99:19 + --> tests/ui/unnecessary_to_owned.rs:100:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:101:19 + --> tests/ui/unnecessary_to_owned.rs:102:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> tests/ui/unnecessary_to_owned.rs:105:42 + --> tests/ui/unnecessary_to_owned.rs:106:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:109:25 + --> tests/ui/unnecessary_to_owned.rs:110:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:111:26 + --> tests/ui/unnecessary_to_owned.rs:112:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:113:24 + --> tests/ui/unnecessary_to_owned.rs:114:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:115:23 + --> tests/ui/unnecessary_to_owned.rs:116:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:117:25 + --> tests/ui/unnecessary_to_owned.rs:118:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:120:30 + --> tests/ui/unnecessary_to_owned.rs:121:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:122:31 + --> tests/ui/unnecessary_to_owned.rs:123:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:124:29 + --> tests/ui/unnecessary_to_owned.rs:125:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:126:28 + --> tests/ui/unnecessary_to_owned.rs:127:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:128:30 + --> tests/ui/unnecessary_to_owned.rs:129:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:131:29 + --> tests/ui/unnecessary_to_owned.rs:132:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:131:43 + --> tests/ui/unnecessary_to_owned.rs:132:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:134:29 + --> tests/ui/unnecessary_to_owned.rs:135:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:134:47 + --> tests/ui/unnecessary_to_owned.rs:135:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:138:26 + --> tests/ui/unnecessary_to_owned.rs:139:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:140:27 + --> tests/ui/unnecessary_to_owned.rs:141:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:142:25 + --> tests/ui/unnecessary_to_owned.rs:143:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:144:24 + --> tests/ui/unnecessary_to_owned.rs:145:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:146:24 + --> tests/ui/unnecessary_to_owned.rs:147:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:148:26 + --> tests/ui/unnecessary_to_owned.rs:149:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:150:26 + --> tests/ui/unnecessary_to_owned.rs:151:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:152:26 + --> tests/ui/unnecessary_to_owned.rs:153:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:155:31 + --> tests/ui/unnecessary_to_owned.rs:156:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:157:32 + --> tests/ui/unnecessary_to_owned.rs:158:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:159:30 + --> tests/ui/unnecessary_to_owned.rs:160:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:161:29 + --> tests/ui/unnecessary_to_owned.rs:162:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:163:29 + --> tests/ui/unnecessary_to_owned.rs:164:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:165:31 + --> tests/ui/unnecessary_to_owned.rs:166:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:167:31 + --> tests/ui/unnecessary_to_owned.rs:168:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:169:31 + --> tests/ui/unnecessary_to_owned.rs:170:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:172:30 + --> tests/ui/unnecessary_to_owned.rs:173:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:172:44 + --> tests/ui/unnecessary_to_owned.rs:173:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:175:30 + --> tests/ui/unnecessary_to_owned.rs:176:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:175:44 + --> tests/ui/unnecessary_to_owned.rs:176:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:178:30 + --> tests/ui/unnecessary_to_owned.rs:179:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:178:44 + --> tests/ui/unnecessary_to_owned.rs:179:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:181:30 + --> tests/ui/unnecessary_to_owned.rs:182:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:181:48 + --> tests/ui/unnecessary_to_owned.rs:182:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:184:30 + --> tests/ui/unnecessary_to_owned.rs:185:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:184:52 + --> tests/ui/unnecessary_to_owned.rs:185:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:187:30 + --> tests/ui/unnecessary_to_owned.rs:188:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:187:48 + --> tests/ui/unnecessary_to_owned.rs:188:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:191:20 + --> tests/ui/unnecessary_to_owned.rs:192:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:194:13 + --> tests/ui/unnecessary_to_owned.rs:195:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:196:13 + --> tests/ui/unnecessary_to_owned.rs:197:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:198:13 - | -LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` - -error: unnecessary use of `to_owned` --> tests/ui/unnecessary_to_owned.rs:200:13 | -LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` - -error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:203:13 - | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:205:13 + --> tests/ui/unnecessary_to_owned.rs:202:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` -error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:207:13 - | -LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` - -error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:209:13 - | -LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` - error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:237:26 + --> tests/ui/unnecessary_to_owned.rs:230:26 | LL | let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -490,7 +466,7 @@ LL + let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8"); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:239:26 + --> tests/ui/unnecessary_to_owned.rs:232:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -502,7 +478,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo").unwrap(); | error: allocating a new `String` only to create a temporary `&str` from it - --> tests/ui/unnecessary_to_owned.rs:241:26 + --> tests/ui/unnecessary_to_owned.rs:234:26 | LL | let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -514,7 +490,7 @@ LL + let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap(); | error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:299:14 + --> tests/ui/unnecessary_to_owned.rs:292:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -526,65 +502,53 @@ LL | LL ~ let path = match get_file_path(t) { | -error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:323:14 - | -LL | let _ = &["x"][..].to_vec().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` - -error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:329:14 - | -LL | let _ = &["x"][..].to_vec().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` - error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:378:24 + --> tests/ui/unnecessary_to_owned.rs:358:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:488:12 + --> tests/ui/unnecessary_to_owned.rs:468:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:632:37 + --> tests/ui/unnecessary_to_owned.rs:612:37 | LL | IntoFuture::into_future(foo([].to_vec(), &0)); | ^^^^^^^^^^^ help: use: `[]` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:643:18 + --> tests/ui/unnecessary_to_owned.rs:623:18 | LL | s.remove(&a.to_vec()); | ^^^^^^^^^^^ help: replace it with: `a` error: unnecessary use of `to_owned` - --> tests/ui/unnecessary_to_owned.rs:648:14 + --> tests/ui/unnecessary_to_owned.rs:628:14 | LL | s.remove(&"b".to_owned()); | ^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_string` - --> tests/ui/unnecessary_to_owned.rs:650:14 + --> tests/ui/unnecessary_to_owned.rs:630:14 | LL | s.remove(&"b".to_string()); | ^^^^^^^^^^^^^^^^ help: replace it with: `"b"` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:656:14 + --> tests/ui/unnecessary_to_owned.rs:636:14 | LL | s.remove(&["b"].to_vec()); | ^^^^^^^^^^^^^^^ help: replace it with: `["b"].as_slice()` error: unnecessary use of `to_vec` - --> tests/ui/unnecessary_to_owned.rs:658:14 + --> tests/ui/unnecessary_to_owned.rs:638:14 | LL | s.remove(&(&["b"]).to_vec()); | ^^^^^^^^^^^^^^^^^^ help: replace it with: `(&["b"]).as_slice()` -error: aborting due to 88 previous errors +error: aborting due to 82 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr index ba562f5a50bec..13d71271e211c 100644 --- a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr @@ -1,13 +1,8 @@ error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:9:1 | -LL | / fn func1(a: bool, b: bool) -> Option { -LL | | -LL | | -LL | | if a && b { -... | -LL | | } - | |_^ +LL | fn func1(a: bool, b: bool) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-wraps` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_wraps)]` @@ -16,7 +11,7 @@ help: remove `Option` from the return type... LL - fn func1(a: bool, b: bool) -> Option { LL + fn func1(a: bool, b: bool) -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL ~ return 42; LL | } @@ -30,21 +25,15 @@ LL ~ return 1337; error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:24:1 | -LL | / fn func2(a: bool, b: bool) -> Option { -LL | | -LL | | -LL | | if a && b { -... | -LL | | if a { Some(20) } else { Some(30) } -LL | | } - | |_^ +LL | fn func2(a: bool, b: bool) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Option` from the return type... | LL - fn func2(a: bool, b: bool) -> Option { LL + fn func2(a: bool, b: bool) -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL ~ return 10; LL | } @@ -54,19 +43,15 @@ LL ~ if a { 20 } else { 30 } error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:44:1 | -LL | / fn func5() -> Option { -LL | | -LL | | -LL | | Some(1) -LL | | } - | |_^ +LL | fn func5() -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Option` from the return type... | LL - fn func5() -> Option { LL + fn func5() -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL - Some(1) LL + 1 @@ -75,19 +60,15 @@ LL + 1 error: this function's return value is unnecessarily wrapped by `Result` --> tests/ui/unnecessary_wraps.rs:56:1 | -LL | / fn func7() -> Result { -LL | | -LL | | -LL | | Ok(1) -LL | | } - | |_^ +LL | fn func7() -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Result` from the return type... | LL - fn func7() -> Result { LL + fn func7() -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Ok()` from returning expressions | LL - Ok(1) LL + 1 @@ -96,19 +77,15 @@ LL + 1 error: this function's return value is unnecessarily wrapped by `Option` --> tests/ui/unnecessary_wraps.rs:86:5 | -LL | / fn func12() -> Option { -LL | | -LL | | -LL | | Some(1) -LL | | } - | |_____^ +LL | fn func12() -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove `Option` from the return type... | LL - fn func12() -> Option { LL + fn func12() -> i32 { | -help: ...and then change returning expressions +help: ...and then remove the surrounding `Some()` from returning expressions | LL - Some(1) LL + 1 @@ -117,13 +94,8 @@ LL + 1 error: this function's return value is unnecessary --> tests/ui/unnecessary_wraps.rs:115:1 | -LL | / fn issue_6640_1(a: bool, b: bool) -> Option<()> { -LL | | -LL | | -LL | | if a && b { -... | -LL | | } - | |_^ +LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the return type... | @@ -144,13 +116,8 @@ LL ~ return ; error: this function's return value is unnecessary --> tests/ui/unnecessary_wraps.rs:130:1 | -LL | / fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { -LL | | -LL | | -LL | | if a && b { -... | -LL | | } - | |_^ +LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the return type... | diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed index 791b2fa131f23..2081772d06b36 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed @@ -64,3 +64,16 @@ fn msrv_1_53() { if let [1 | 53] = [0] {} //~^ unnested_or_patterns } + +mod issue9952 { + fn or_in_local() { + let (0 | 1 | _) = 0; + //~^ unnested_or_patterns + + if let (0 | 1 | _) = 0 {} + //~^ unnested_or_patterns + } + + fn or_in_param((x | x | x): i32) {} + //~^ unnested_or_patterns +} diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.rs b/src/tools/clippy/tests/ui/unnested_or_patterns.rs index e7e7c7cd2e494..6bf8fce36616c 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.rs +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.rs @@ -64,3 +64,16 @@ fn msrv_1_53() { if let [1] | [53] = [0] {} //~^ unnested_or_patterns } + +mod issue9952 { + fn or_in_local() { + let (0 | (1 | _)) = 0; + //~^ unnested_or_patterns + + if let (0 | (1 | _)) = 0 {} + //~^ unnested_or_patterns + } + + fn or_in_param((x | (x | x)): i32) {} + //~^ unnested_or_patterns +} diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr index ec5eb983c5a01..c805dc992b1c2 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr @@ -204,5 +204,41 @@ LL - if let [1] | [53] = [0] {} LL + if let [1 | 53] = [0] {} | -error: aborting due to 17 previous errors +error: unnested or-patterns + --> tests/ui/unnested_or_patterns.rs:70:13 + | +LL | let (0 | (1 | _)) = 0; + | ^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL - let (0 | (1 | _)) = 0; +LL + let (0 | 1 | _) = 0; + | + +error: unnested or-patterns + --> tests/ui/unnested_or_patterns.rs:73:16 + | +LL | if let (0 | (1 | _)) = 0 {} + | ^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL - if let (0 | (1 | _)) = 0 {} +LL + if let (0 | 1 | _) = 0 {} + | + +error: unnested or-patterns + --> tests/ui/unnested_or_patterns.rs:77:20 + | +LL | fn or_in_param((x | (x | x)): i32) {} + | ^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL - fn or_in_param((x | (x | x)): i32) {} +LL + fn or_in_param((x | x | x): i32) {} + | + +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/unused_async.rs b/src/tools/clippy/tests/ui/unused_async.rs index 5aaf7b9f5b598..433459253dd76 100644 --- a/src/tools/clippy/tests/ui/unused_async.rs +++ b/src/tools/clippy/tests/ui/unused_async.rs @@ -119,3 +119,11 @@ fn main() { foo(); bar(); } + +mod issue14704 { + use std::sync::Arc; + + trait Action { + async fn cancel(self: Arc) {} + } +} diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed b/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed new file mode 100644 index 0000000000000..93dd58b8e9d7b --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed @@ -0,0 +1,146 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +// The output for humans should just highlight the whole span without showing +// the suggested replacement, but we also want to test that suggested +// replacement only removes one set of parentheses, rather than naïvely +// stripping away any starting or ending parenthesis characters—hence this +// test of the JSON error format. + +#![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] +#![rustfmt::skip] + +#![deny(clippy::unused_unit)] +#![allow(dead_code)] +#![allow(clippy::from_over_into)] + +struct Unitter; +impl Unitter { + #[allow(clippy::no_effect)] + pub fn get_unit(&self, f: F, _g: G) + //~^ unused_unit + //~| unused_unit + where G: Fn() { + //~^ unused_unit + let _y: &dyn Fn() = &f; + //~^ unused_unit + (); // this should not lint, as it's not in return type position + } +} + +impl Into<()> for Unitter { + #[rustfmt::skip] + fn into(self) { + //~^ unused_unit + + //~^ unused_unit + } +} + +trait Trait { + fn redundant(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn(); + //~^ unused_unit +} + +impl Trait for Unitter { + fn redundant(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn() {} + //~^ unused_unit +} + +fn return_unit() { } +//~^ unused_unit +//~| unused_unit + +#[allow(clippy::needless_return)] +#[allow(clippy::never_loop)] +#[allow(clippy::unit_cmp)] +fn main() { + let u = Unitter; + assert_eq!(u.get_unit(|| {}, return_unit), u.into()); + return_unit(); + loop { + break; + //~^ unused_unit + } + return; + //~^ unused_unit +} + +// https://github.com/rust-lang/rust-clippy/issues/4076 +fn foo() { + macro_rules! foo { + (recv($r:expr) -> $res:pat => $body:expr) => { + $body + } + } + + foo! { + recv(rx) -> _x => () + } +} + +#[rustfmt::skip] +fn test(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test2(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test3(){} +//~^ unused_unit + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} + +mod issue9949 { + fn main() { + #[doc = "documentation"] + () + } +} + +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run(f: impl FnOnce() -> R) { + f(); + } + + #[allow(dependency_on_unit_never_type_fallback)] + fn bar() { + run(|| { todo!() }); + //~[edition2021]^ unused_unit + } + + struct UnitStruct; + impl UnitStruct { + fn apply Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr b/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr new file mode 100644 index 0000000000000..13cc20d4d7adc --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr @@ -0,0 +1,128 @@ +error: unneeded unit expression + --> tests/ui/unused_unit.rs:37:9 + | +LL | () + | ^^ help: remove the final `()` + | +note: the lint level is defined here + --> tests/ui/unused_unit.rs:15:9 + | +LL | #![deny(clippy::unused_unit)] + | ^^^^^^^^^^^^^^^^^^^ + +error: unneeded unit expression + --> tests/ui/unused_unit.rs:62:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:28 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:25:18 + | +LL | where G: Fn() -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:58 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:27:26 + | +LL | let _y: &dyn Fn() -> () = &f; + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:35:18 + | +LL | fn into(self) -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:43:29 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:46:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:48:16 + | +LL | H: Fn() -> (); + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:53:29 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:56:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:58:16 + | +LL | H: Fn() -> () {} + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:62:17 + | +LL | fn return_unit() -> () { () } + | ^^^^^^ help: remove the `-> ()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:74:14 + | +LL | break(); + | ^^ help: remove the `()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:77:11 + | +LL | return(); + | ^^ help: remove the `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:95:10 + | +LL | fn test()->(){} + | ^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:99:11 + | +LL | fn test2() ->(){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:103:11 + | +LL | fn test3()-> (){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:136:15 + | +LL | run(|| -> () { todo!() }); + | ^^^^^^ help: remove the `-> ()` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed b/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed new file mode 100644 index 0000000000000..987d901b97df7 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed @@ -0,0 +1,146 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +// The output for humans should just highlight the whole span without showing +// the suggested replacement, but we also want to test that suggested +// replacement only removes one set of parentheses, rather than naïvely +// stripping away any starting or ending parenthesis characters—hence this +// test of the JSON error format. + +#![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] +#![rustfmt::skip] + +#![deny(clippy::unused_unit)] +#![allow(dead_code)] +#![allow(clippy::from_over_into)] + +struct Unitter; +impl Unitter { + #[allow(clippy::no_effect)] + pub fn get_unit(&self, f: F, _g: G) + //~^ unused_unit + //~| unused_unit + where G: Fn() { + //~^ unused_unit + let _y: &dyn Fn() = &f; + //~^ unused_unit + (); // this should not lint, as it's not in return type position + } +} + +impl Into<()> for Unitter { + #[rustfmt::skip] + fn into(self) { + //~^ unused_unit + + //~^ unused_unit + } +} + +trait Trait { + fn redundant(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn(); + //~^ unused_unit +} + +impl Trait for Unitter { + fn redundant(&self, _f: F, _g: G, _h: H) + //~^ unused_unit + where + G: FnMut(), + //~^ unused_unit + H: Fn() {} + //~^ unused_unit +} + +fn return_unit() { } +//~^ unused_unit +//~| unused_unit + +#[allow(clippy::needless_return)] +#[allow(clippy::never_loop)] +#[allow(clippy::unit_cmp)] +fn main() { + let u = Unitter; + assert_eq!(u.get_unit(|| {}, return_unit), u.into()); + return_unit(); + loop { + break; + //~^ unused_unit + } + return; + //~^ unused_unit +} + +// https://github.com/rust-lang/rust-clippy/issues/4076 +fn foo() { + macro_rules! foo { + (recv($r:expr) -> $res:pat => $body:expr) => { + $body + } + } + + foo! { + recv(rx) -> _x => () + } +} + +#[rustfmt::skip] +fn test(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test2(){} +//~^ unused_unit + +#[rustfmt::skip] +fn test3(){} +//~^ unused_unit + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} + +mod issue9949 { + fn main() { + #[doc = "documentation"] + () + } +} + +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run(f: impl FnOnce() -> R) { + f(); + } + + #[allow(dependency_on_unit_never_type_fallback)] + fn bar() { + run(|| -> () { todo!() }); + //~[edition2021]^ unused_unit + } + + struct UnitStruct; + impl UnitStruct { + fn apply Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2024.stderr b/src/tools/clippy/tests/ui/unused_unit.edition2024.stderr new file mode 100644 index 0000000000000..a79e70e066bd3 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.edition2024.stderr @@ -0,0 +1,122 @@ +error: unneeded unit expression + --> tests/ui/unused_unit.rs:37:9 + | +LL | () + | ^^ help: remove the final `()` + | +note: the lint level is defined here + --> tests/ui/unused_unit.rs:15:9 + | +LL | #![deny(clippy::unused_unit)] + | ^^^^^^^^^^^^^^^^^^^ + +error: unneeded unit expression + --> tests/ui/unused_unit.rs:62:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:28 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:25:18 + | +LL | where G: Fn() -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:22:58 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:27:26 + | +LL | let _y: &dyn Fn() -> () = &f; + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:35:18 + | +LL | fn into(self) -> () { + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:43:29 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:46:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:48:16 + | +LL | H: Fn() -> (); + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:53:29 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:56:19 + | +LL | G: FnMut() -> (), + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:58:16 + | +LL | H: Fn() -> () {} + | ^^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:62:17 + | +LL | fn return_unit() -> () { () } + | ^^^^^^ help: remove the `-> ()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:74:14 + | +LL | break(); + | ^^ help: remove the `()` + +error: unneeded `()` + --> tests/ui/unused_unit.rs:77:11 + | +LL | return(); + | ^^ help: remove the `()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:95:10 + | +LL | fn test()->(){} + | ^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:99:11 + | +LL | fn test2() ->(){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> tests/ui/unused_unit.rs:103:11 + | +LL | fn test3()-> (){} + | ^^^^^ help: remove the `-> ()` + +error: aborting due to 19 previous errors + diff --git a/src/tools/clippy/tests/ui/unused_unit.fixed b/src/tools/clippy/tests/ui/unused_unit.fixed index e3c02681c9fd7..6668bf90c0924 100644 --- a/src/tools/clippy/tests/ui/unused_unit.fixed +++ b/src/tools/clippy/tests/ui/unused_unit.fixed @@ -120,3 +120,24 @@ mod issue9949 { () } } + +#[clippy::msrv = "1.85"] +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run(f: impl FnOnce() -> R) { + f(); + } + + fn bar() { + run(|| -> () { todo!() }); + } + + struct UnitStruct; + impl UnitStruct { + fn apply Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/src/tools/clippy/tests/ui/unused_unit.rs b/src/tools/clippy/tests/ui/unused_unit.rs index 4353026c594c1..b7645f7b6a263 100644 --- a/src/tools/clippy/tests/ui/unused_unit.rs +++ b/src/tools/clippy/tests/ui/unused_unit.rs @@ -1,4 +1,6 @@ - +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 // The output for humans should just highlight the whole span without showing // the suggested replacement, but we also want to test that suggested @@ -120,3 +122,25 @@ mod issue9949 { () } } + +mod issue14577 { + trait Unit {} + impl Unit for () {} + + fn run(f: impl FnOnce() -> R) { + f(); + } + + #[allow(dependency_on_unit_never_type_fallback)] + fn bar() { + run(|| -> () { todo!() }); + //~[edition2021]^ unused_unit + } + + struct UnitStruct; + impl UnitStruct { + fn apply Fn(&'c mut Self)>(&mut self, f: F) { + todo!() + } + } +} \ No newline at end of file diff --git a/src/tools/clippy/tests/ui/unused_unit.stderr b/src/tools/clippy/tests/ui/unused_unit.stderr index 172fe06550281..366f2142095ff 100644 --- a/src/tools/clippy/tests/ui/unused_unit.stderr +++ b/src/tools/clippy/tests/ui/unused_unit.stderr @@ -1,8 +1,8 @@ -error: unneeded unit return type - --> tests/ui/unused_unit.rs:20:58 +error: unneeded unit expression + --> tests/ui/unused_unit.rs:35:9 | -LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () - | ^^^^^^ help: remove the `-> ()` +LL | () + | ^^ help: remove the final `()` | note: the lint level is defined here --> tests/ui/unused_unit.rs:13:9 @@ -10,6 +10,12 @@ note: the lint level is defined here LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ +error: unneeded unit expression + --> tests/ui/unused_unit.rs:60:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + error: unneeded unit return type --> tests/ui/unused_unit.rs:20:28 | @@ -22,6 +28,12 @@ error: unneeded unit return type LL | where G: Fn() -> () { | ^^^^^^ help: remove the `-> ()` +error: unneeded unit return type + --> tests/ui/unused_unit.rs:20:58 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` + error: unneeded unit return type --> tests/ui/unused_unit.rs:25:26 | @@ -34,12 +46,6 @@ error: unneeded unit return type LL | fn into(self) -> () { | ^^^^^^ help: remove the `-> ()` -error: unneeded unit expression - --> tests/ui/unused_unit.rs:35:9 - | -LL | () - | ^^ help: remove the final `()` - error: unneeded unit return type --> tests/ui/unused_unit.rs:41:29 | @@ -82,12 +88,6 @@ error: unneeded unit return type LL | fn return_unit() -> () { () } | ^^^^^^ help: remove the `-> ()` -error: unneeded unit expression - --> tests/ui/unused_unit.rs:60:26 - | -LL | fn return_unit() -> () { () } - | ^^ help: remove the final `()` - error: unneeded `()` --> tests/ui/unused_unit.rs:72:14 | diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.rs b/src/tools/clippy/tests/ui/unwrap_expect_used.rs index d0bb571273b5a..b429f3a8a0bb9 100644 --- a/src/tools/clippy/tests/ui/unwrap_expect_used.rs +++ b/src/tools/clippy/tests/ui/unwrap_expect_used.rs @@ -66,3 +66,20 @@ fn main() { SOME.expect("Still not three?"); } } + +mod with_expansion { + macro_rules! open { + ($file:expr) => { + std::fs::File::open($file) + }; + } + + fn test(file: &str) { + use std::io::Read; + let mut s = String::new(); + let _ = open!(file).unwrap(); //~ unwrap_used + let _ = open!(file).expect("can open"); //~ expect_used + let _ = open!(file).unwrap_err(); //~ unwrap_used + let _ = open!(file).expect_err("can open"); //~ expect_used + } +} diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.stderr b/src/tools/clippy/tests/ui/unwrap_expect_used.stderr index 79eac3f58ccb2..6fd1b84d81231 100644 --- a/src/tools/clippy/tests/ui/unwrap_expect_used.stderr +++ b/src/tools/clippy/tests/ui/unwrap_expect_used.stderr @@ -50,5 +50,37 @@ LL | a.expect_err("Hello error!"); | = note: if this value is an `Ok`, it will panic -error: aborting due to 6 previous errors +error: used `unwrap()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:80:17 + | +LL | let _ = open!(file).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + +error: used `expect()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:81:17 + | +LL | let _ = open!(file).expect("can open"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + +error: used `unwrap_err()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:82:17 + | +LL | let _ = open!(file).unwrap_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Ok`, it will panic + +error: used `expect_err()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:83:17 + | +LL | let _ = open!(file).expect_err("can open"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Ok`, it will panic + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/unwrap_or.fixed b/src/tools/clippy/tests/ui/unwrap_or.fixed index c794ed577032d..e550484b5d9f0 100644 --- a/src/tools/clippy/tests/ui/unwrap_or.fixed +++ b/src/tools/clippy/tests/ui/unwrap_or.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::or_fun_call)] +#![warn(clippy::or_fun_call)] #![allow(clippy::unnecessary_literal_unwrap)] fn main() { diff --git a/src/tools/clippy/tests/ui/unwrap_or.rs b/src/tools/clippy/tests/ui/unwrap_or.rs index 11a6883b7403f..cdd61ac898e6d 100644 --- a/src/tools/clippy/tests/ui/unwrap_or.rs +++ b/src/tools/clippy/tests/ui/unwrap_or.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all, clippy::or_fun_call)] +#![warn(clippy::or_fun_call)] #![allow(clippy::unnecessary_literal_unwrap)] fn main() { diff --git a/src/tools/clippy/tests/ui/used_underscore_items.rs b/src/tools/clippy/tests/ui/used_underscore_items.rs index 3401df6ae7438..7e8289f1406ba 100644 --- a/src/tools/clippy/tests/ui/used_underscore_items.rs +++ b/src/tools/clippy/tests/ui/used_underscore_items.rs @@ -73,7 +73,7 @@ fn external_item_call() { // should not lint foreign functions. // issue #14156 -extern "C" { +unsafe extern "C" { pub fn _exit(code: i32) -> !; } diff --git a/src/tools/clippy/tests/ui/useless_asref.fixed b/src/tools/clippy/tests/ui/useless_asref.fixed index 8c1f948fb5806..3c3ea5a736d43 100644 --- a/src/tools/clippy/tests/ui/useless_asref.fixed +++ b/src/tools/clippy/tests/ui/useless_asref.fixed @@ -248,6 +248,16 @@ impl Issue12357 { } } +fn issue_14828() { + pub trait T { + fn as_ref(&self) {} + } + + impl T for () {} + + ().as_ref(); +} + fn main() { not_ok(); ok(); diff --git a/src/tools/clippy/tests/ui/useless_asref.rs b/src/tools/clippy/tests/ui/useless_asref.rs index d9db2d4f5596a..c173dd677152d 100644 --- a/src/tools/clippy/tests/ui/useless_asref.rs +++ b/src/tools/clippy/tests/ui/useless_asref.rs @@ -248,6 +248,16 @@ impl Issue12357 { } } +fn issue_14828() { + pub trait T { + fn as_ref(&self) {} + } + + impl T for () {} + + ().as_ref(); +} + fn main() { not_ok(); ok(); diff --git a/src/tools/clippy/tests/ui/useless_concat.fixed b/src/tools/clippy/tests/ui/useless_concat.fixed new file mode 100644 index 0000000000000..360b6f6ce82ee --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_concat.fixed @@ -0,0 +1,41 @@ +//@aux-build:proc_macros.rs + +#![warn(clippy::useless_concat)] +#![allow(clippy::print_literal)] + +extern crate proc_macros; +use proc_macros::{external, with_span}; + +macro_rules! my_concat { + ($fmt:literal $(, $e:expr)*) => { + println!(concat!("ERROR: ", $fmt), $($e,)*); + } +} + +fn main() { + let x = ""; //~ useless_concat + let x = "c"; //~ useless_concat + let x = "\""; //~ useless_concat + let x = "true"; //~ useless_concat + let x = "1"; //~ useless_concat + let x = "1.0000"; //~ useless_concat + let x = "1"; //~ useless_concat + let x = "1"; //~ useless_concat + let x = "1.0000"; //~ useless_concat + let x = "1.0000"; //~ useless_concat + let x = "a😀\n"; //~ useless_concat + let x = "a"; //~ useless_concat + let x = "1"; //~ useless_concat + println!("b: {}", "a"); //~ useless_concat + // Should not lint. + let x = concat!("a", "b"); + let local_i32 = 1; + my_concat!("{}", local_i32); + let x = concat!(file!(), "#L", line!()); + + external! { concat!(); } + with_span! { + span + concat!(); + } +} diff --git a/src/tools/clippy/tests/ui/useless_concat.rs b/src/tools/clippy/tests/ui/useless_concat.rs new file mode 100644 index 0000000000000..338d20a48ae96 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_concat.rs @@ -0,0 +1,41 @@ +//@aux-build:proc_macros.rs + +#![warn(clippy::useless_concat)] +#![allow(clippy::print_literal)] + +extern crate proc_macros; +use proc_macros::{external, with_span}; + +macro_rules! my_concat { + ($fmt:literal $(, $e:expr)*) => { + println!(concat!("ERROR: ", $fmt), $($e,)*); + } +} + +fn main() { + let x = concat!(); //~ useless_concat + let x = concat!('c'); //~ useless_concat + let x = concat!('"'); //~ useless_concat + let x = concat!(true); //~ useless_concat + let x = concat!(1f32); //~ useless_concat + let x = concat!(1.0000f32); //~ useless_concat + let x = concat!(1_f32); //~ useless_concat + let x = concat!(1_); //~ useless_concat + let x = concat!(1.0000_f32); //~ useless_concat + let x = concat!(1.0000_); //~ useless_concat + let x = concat!("a\u{1f600}\n"); //~ useless_concat + let x = concat!(r##"a"##); //~ useless_concat + let x = concat!(1); //~ useless_concat + println!("b: {}", concat!("a")); //~ useless_concat + // Should not lint. + let x = concat!("a", "b"); + let local_i32 = 1; + my_concat!("{}", local_i32); + let x = concat!(file!(), "#L", line!()); + + external! { concat!(); } + with_span! { + span + concat!(); + } +} diff --git a/src/tools/clippy/tests/ui/useless_concat.stderr b/src/tools/clippy/tests/ui/useless_concat.stderr new file mode 100644 index 0000000000000..43d6d9ff5799d --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_concat.stderr @@ -0,0 +1,89 @@ +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:16:13 + | +LL | let x = concat!(); + | ^^^^^^^^^ help: replace with: `""` + | + = note: `-D clippy::useless-concat` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_concat)]` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:17:13 + | +LL | let x = concat!('c'); + | ^^^^^^^^^^^^ help: replace with: `"c"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:18:13 + | +LL | let x = concat!('"'); + | ^^^^^^^^^^^^ help: replace with: `"\""` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:19:13 + | +LL | let x = concat!(true); + | ^^^^^^^^^^^^^ help: replace with: `"true"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:20:13 + | +LL | let x = concat!(1f32); + | ^^^^^^^^^^^^^ help: replace with: `"1"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:21:13 + | +LL | let x = concat!(1.0000f32); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `"1.0000"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:22:13 + | +LL | let x = concat!(1_f32); + | ^^^^^^^^^^^^^^ help: replace with: `"1"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:23:13 + | +LL | let x = concat!(1_); + | ^^^^^^^^^^^ help: replace with: `"1"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:24:13 + | +LL | let x = concat!(1.0000_f32); + | ^^^^^^^^^^^^^^^^^^^ help: replace with: `"1.0000"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:25:13 + | +LL | let x = concat!(1.0000_); + | ^^^^^^^^^^^^^^^^ help: replace with: `"1.0000"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:26:13 + | +LL | let x = concat!("a\u{1f600}\n"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `"a😀\n"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:27:13 + | +LL | let x = concat!(r##"a"##); + | ^^^^^^^^^^^^^^^^^ help: replace with: `"a"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:28:13 + | +LL | let x = concat!(1); + | ^^^^^^^^^^ help: replace with: `"1"` + +error: unneeded use of `concat!` macro + --> tests/ui/useless_concat.rs:29:23 + | +LL | println!("b: {}", concat!("a")); + | ^^^^^^^^^^^^ help: replace with: `"a"` + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index 489caacf21235..ad30c94f34781 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -427,3 +427,18 @@ mod issue11819 { } } } + +fn issue14739() { + use std::ops::Range; + + const R: Range = 2..7; + + R.into_iter().all(|_x| true); // no lint + + R.into_iter().any(|_x| true); // no lint + + R.for_each(|_x| {}); + //~^ useless_conversion + let _ = R.map(|_x| 0); + //~^ useless_conversion +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index 4f3a3b00ea206..505afb340009a 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -427,3 +427,18 @@ mod issue11819 { } } } + +fn issue14739() { + use std::ops::Range; + + const R: Range = 2..7; + + R.into_iter().all(|_x| true); // no lint + + R.into_iter().any(|_x| true); // no lint + + R.into_iter().for_each(|_x| {}); + //~^ useless_conversion + let _ = R.into_iter().map(|_x| 0); + //~^ useless_conversion +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index 3cde2a786e466..3bfaf1411c2c6 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -377,5 +377,17 @@ LL - takes_into_iter(self.my_field.into_iter()); LL + takes_into_iter(&mut *self.my_field); | -error: aborting due to 41 previous errors +error: useless conversion to the same type: `std::ops::Range` + --> tests/ui/useless_conversion.rs:440:5 + | +LL | R.into_iter().for_each(|_x| {}); + | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` + +error: useless conversion to the same type: `std::ops::Range` + --> tests/ui/useless_conversion.rs:442:13 + | +LL | let _ = R.into_iter().map(|_x| 0); + | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` + +error: aborting due to 43 previous errors diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed index a26b4a34190cc..17510683f03e4 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed @@ -16,7 +16,7 @@ use crate::fn_mod::foo; //~^ wildcard_imports use crate::mod_mod::inner_mod; //~^ wildcard_imports -use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}; //~^ wildcard_imports #[macro_use] use crate::struct_mod::{A, inner_struct_mod}; @@ -26,7 +26,7 @@ use crate::struct_mod::{A, inner_struct_mod}; use wildcard_imports_helper::inner::inner_for_self_import; use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; //~^ wildcard_imports -use wildcard_imports_helper::{ExternA, extern_foo}; +use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports use std::io::prelude::*; @@ -138,7 +138,7 @@ mod in_fn_test { fn test_extern() { use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; //~^ wildcard_imports - use wildcard_imports_helper::{ExternA, extern_foo}; + use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports inner_for_self_import::inner_extern_foo(); @@ -160,7 +160,7 @@ mod in_fn_test { } fn test_extern_reexported() { - use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + use wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}; //~^ wildcard_imports extern_exported(); @@ -190,7 +190,7 @@ mod in_fn_test { } fn test_reexported() { - use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + use crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}; //~^ wildcard_imports exported(); diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr index f774126102bce..26434656a509e 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr @@ -17,7 +17,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:19:5 | LL | use crate::multi_fn_mod::*; - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:22:5 @@ -35,7 +35,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:29:5 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:100:13 @@ -59,7 +59,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:141:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:154:20 @@ -77,13 +77,13 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:163:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:193:9 | LL | use crate::in_fn_test::*; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:203:9 diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed index a3d1aebba8af4..f97b883ea231f 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed @@ -14,7 +14,7 @@ use crate::fn_mod::foo; //~^ wildcard_imports use crate::mod_mod::inner_mod; //~^ wildcard_imports -use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}; //~^ wildcard_imports use crate::struct_mod::{A, inner_struct_mod}; //~^ wildcard_imports @@ -23,7 +23,7 @@ use crate::struct_mod::{A, inner_struct_mod}; use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; //~^ wildcard_imports use wildcard_imports_helper::prelude::v1::*; -use wildcard_imports_helper::{ExternA, extern_foo}; +use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports use std::io::prelude::*; @@ -132,7 +132,7 @@ mod in_fn_test { fn test_extern() { use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; //~^ wildcard_imports - use wildcard_imports_helper::{ExternA, extern_foo}; + use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports inner_for_self_import::inner_extern_foo(); @@ -154,7 +154,7 @@ mod in_fn_test { } fn test_extern_reexported() { - use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + use wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}; //~^ wildcard_imports extern_exported(); @@ -184,7 +184,7 @@ mod in_fn_test { } fn test_reexported() { - use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + use crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}; //~^ wildcard_imports exported(); diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr index a1b557f39f0d2..873ce41b04f49 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr @@ -17,7 +17,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:17:5 | LL | use crate::multi_fn_mod::*; - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:19:5 @@ -35,7 +35,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:26:5 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:95:13 @@ -59,7 +59,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:135:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:148:20 @@ -77,13 +77,13 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:157:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:187:9 | LL | use crate::in_fn_test::*; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:197:9 diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed index a3d1aebba8af4..f97b883ea231f 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed @@ -14,7 +14,7 @@ use crate::fn_mod::foo; //~^ wildcard_imports use crate::mod_mod::inner_mod; //~^ wildcard_imports -use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}; //~^ wildcard_imports use crate::struct_mod::{A, inner_struct_mod}; //~^ wildcard_imports @@ -23,7 +23,7 @@ use crate::struct_mod::{A, inner_struct_mod}; use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; //~^ wildcard_imports use wildcard_imports_helper::prelude::v1::*; -use wildcard_imports_helper::{ExternA, extern_foo}; +use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports use std::io::prelude::*; @@ -132,7 +132,7 @@ mod in_fn_test { fn test_extern() { use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; //~^ wildcard_imports - use wildcard_imports_helper::{ExternA, extern_foo}; + use wildcard_imports_helper::{extern_foo, ExternA}; //~^ wildcard_imports inner_for_self_import::inner_extern_foo(); @@ -154,7 +154,7 @@ mod in_fn_test { } fn test_extern_reexported() { - use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + use wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}; //~^ wildcard_imports extern_exported(); @@ -184,7 +184,7 @@ mod in_fn_test { } fn test_reexported() { - use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + use crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}; //~^ wildcard_imports exported(); diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr index a1b557f39f0d2..873ce41b04f49 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr @@ -17,7 +17,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:17:5 | LL | use crate::multi_fn_mod::*; - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:19:5 @@ -35,7 +35,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:26:5 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:95:13 @@ -59,7 +59,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:135:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:148:20 @@ -77,13 +77,13 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:157:13 | LL | use wildcard_imports_helper::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:187:9 | LL | use crate::in_fn_test::*; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:197:9 diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs index 4beeef421f30e..dcbfd16843de4 100644 --- a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs +++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs @@ -71,6 +71,27 @@ fn test2(map: HashMap, key: &str) -> HashMap { todo!(); } +fn issue14822() { + trait Trait { + type T; + } + struct S(T::T); + + // The `delay_bug` happens when evaluating the pointer metadata of `S` which depends on + // whether `T::T` is `Sized`. Since the type alias doesn't have a trait bound of `T: Trait` + // evaluating `T::T: Sized` ultimately fails with `NoSolution`. + type A = HashMap>; + type B = HashMap>; + + enum E {} + impl Trait for E { + type T = (); + } + type C = HashMap>; + type D = HashMap>; + //~^ zero_sized_map_values +} + fn main() { let _: HashMap = HashMap::new(); //~^ zero_sized_map_values diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr index ed8536acfe8fa..d29491fa05c76 100644 --- a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr +++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr @@ -81,7 +81,15 @@ LL | fn test(map: HashMap, key: &str) -> HashMap { = help: consider using a set instead error: map with zero-sized value type - --> tests/ui/zero_sized_hashmap_values.rs:75:34 + --> tests/ui/zero_sized_hashmap_values.rs:91:14 + | +LL | type D = HashMap>; + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> tests/ui/zero_sized_hashmap_values.rs:96:34 | LL | let _: HashMap = HashMap::new(); | ^^^^^^^ @@ -89,7 +97,7 @@ LL | let _: HashMap = HashMap::new(); = help: consider using a set instead error: map with zero-sized value type - --> tests/ui/zero_sized_hashmap_values.rs:75:12 + --> tests/ui/zero_sized_hashmap_values.rs:96:12 | LL | let _: HashMap = HashMap::new(); | ^^^^^^^^^^^^^^^^^^^ @@ -97,12 +105,12 @@ LL | let _: HashMap = HashMap::new(); = help: consider using a set instead error: map with zero-sized value type - --> tests/ui/zero_sized_hashmap_values.rs:81:12 + --> tests/ui/zero_sized_hashmap_values.rs:102:12 | LL | let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect(); | ^^^^^^^^^^^^^ | = help: consider using a set instead -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/zombie_processes.rs b/src/tools/clippy/tests/ui/zombie_processes.rs index 25bbc02ffb762..395f9dd2defb5 100644 --- a/src/tools/clippy/tests/ui/zombie_processes.rs +++ b/src/tools/clippy/tests/ui/zombie_processes.rs @@ -176,3 +176,25 @@ fn return_wait() -> ExitStatus { let mut x = Command::new("").spawn().unwrap(); return x.wait().unwrap(); } + +mod issue14677 { + use std::io; + use std::process::Command; + + fn do_something Result<(), ()>>(f: F) { + todo!() + } + + fn foo() { + let mut child = Command::new("true").spawn().unwrap(); + let some_condition = true; + do_something(|| { + if some_condition { + return Err(()); + } + Ok(()) + }); + child.kill().unwrap(); + child.wait().unwrap(); + } +} diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 3d35116ebc178..389f22c6a2ca3 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -1,7 +1,7 @@ [relabel] allow-unauthenticated = [ "A-*", "C-*", "E-*", "I-*", "L-*", "P-*", "S-*", "T-*", - "good-first-issue", "beta-nominated" + "good first issue", "beta-nominated" ] # Allows shortcuts like `@rustbot ready` @@ -9,11 +9,34 @@ allow-unauthenticated = [ # See https://forge.rust-lang.org/triagebot/shortcuts.html [shortcut] +[merge-conflicts] + +[note] + +[close] + +[issue-links] + +# Prevents mentions in commits to avoid users being spammed +[no-mentions] + # Have rustbot inform users about the *No Merge Policy* [no-merges] exclude_titles = ["Rustup"] # exclude syncs from rust-lang/rust labels = ["has-merge-commits", "S-waiting-on-author"] +[review-requested] +# Those labels are removed when PR author requests a review from an assignee +remove_labels = ["S-waiting-on-author"] +# Those labels are added when PR author requests a review from an assignee +add_labels = ["S-waiting-on-review"] + +[review-submitted] +# These labels are removed when a review is submitted. +review_labels = ["S-waiting-on-review"] +# This label is added when a review is submitted. +reviewed_label = "S-waiting-on-author" + [autolabel."S-waiting-on-review"] new_pr = true @@ -21,10 +44,12 @@ new_pr = true contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", + "Manishearth", ] [assign.owners] "/.github" = ["@flip1995"] +"/triagebot.toml" = ["@flip1995"] "/book" = ["@flip1995"] "*" = [ "@Manishearth", @@ -34,5 +59,5 @@ users_on_vacation = [ "@Jarcho", "@blyxyas", "@y21", - "@Centri3", + "@samueltardieu", ] diff --git a/src/tools/clippy/util/etc/pre-commit.sh b/src/tools/clippy/util/etc/pre-commit.sh index 5dd2ba3d5f53b..528f8953b25d8 100755 --- a/src/tools/clippy/util/etc/pre-commit.sh +++ b/src/tools/clippy/util/etc/pre-commit.sh @@ -6,7 +6,6 @@ set -e # Update lints cargo dev update_lints git add clippy_lints/src/lib.rs -git add clippy_lints/src/lib.*.rs # Formatting: # Git will not automatically add the formatted code to the staged changes once diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html index a9b6462800307..19dc1ec0b0cc6 100644 --- a/src/tools/clippy/util/gh-pages/index_template.html +++ b/src/tools/clippy/util/gh-pages/index_template.html @@ -19,10 +19,10 @@ {# #} - {# #} - {# #} - {# #} - {# #} + {# #} + {# #} + {# #} + {# #} {# #} {# #} {# #} diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 4f8e475e7625f..3b544d8b82817 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -1,31 +1,34 @@ [package] name = "compiletest" version = "0.0.0" -edition = "2021" +edition = "2024" [lib] doctest = false [dependencies] +# tidy-alphabetical-start anstyle-svg = "0.1.3" +build_helper = { path = "../../build_helper" } +camino = "1" colored = "2" diff = "0.1.10" -unified-diff = "0.2.1" getopts = "0.2" +glob = "0.3.0" +home = "0.5.5" indexmap = "2.0.0" miropt-test-tools = { path = "../miropt-test-tools" } -build_helper = { path = "../../build_helper" } -tracing = "0.1" -tracing-subscriber = { version = "0.3.3", default-features = false, features = ["ansi", "env-filter", "fmt", "parking_lot", "smallvec"] } +rayon = "1.10.0" regex = "1.0" +rustfix = "0.8.1" semver = { version = "1.0.23", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -rustfix = "0.8.1" +tracing = "0.1" +tracing-subscriber = { version = "0.3.3", default-features = false, features = ["ansi", "env-filter", "fmt", "parking_lot", "smallvec"] } +unified-diff = "0.2.1" walkdir = "2" -glob = "0.3.0" -anyhow = "1" -home = "0.5.5" +# tidy-alphabetical-end [target.'cfg(unix)'.dependencies] libc = "0.2" @@ -34,7 +37,7 @@ libc = "0.2" miow = "0.6" [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_System_Diagnostics_Debug", diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index b302c6a49f58e..4f93b49874134 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -1,18 +1,17 @@ use std::collections::{BTreeSet, HashMap, HashSet}; -use std::ffi::OsString; -use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; use std::sync::OnceLock; use std::{fmt, iter}; use build_helper::git::GitConfig; +use camino::{Utf8Path, Utf8PathBuf}; use semver::Version; use serde::de::{Deserialize, Deserializer, Error as _}; -use test::{ColorConfig, OutputFormat}; pub use self::Mode::*; -use crate::util::{PathBufExt, add_dylib_path}; +use crate::executor::{ColorConfig, OutputFormat}; +use crate::util::{Utf8PathBufExt, add_dylib_path}; macro_rules! string_enum { ($(#[$meta:meta])* $vis:vis enum $name:ident { $($variant:ident => $repr:expr,)* }) => { @@ -178,23 +177,30 @@ pub struct Config { /// `true` to overwrite stderr/stdout files instead of complaining about changes in output. pub bless: bool, + /// Stop as soon as possible after any test fails. + /// May run a few more tests before stopping, due to threading. + pub fail_fast: bool, + /// The library paths required for running the compiler. - pub compile_lib_path: PathBuf, + pub compile_lib_path: Utf8PathBuf, /// The library paths required for running compiled programs. - pub run_lib_path: PathBuf, + pub run_lib_path: Utf8PathBuf, /// The rustc executable. - pub rustc_path: PathBuf, + pub rustc_path: Utf8PathBuf, /// The cargo executable. - pub cargo_path: Option, + pub cargo_path: Option, + + /// Rustc executable used to compile run-make recipes. + pub stage0_rustc_path: Option, /// The rustdoc executable. - pub rustdoc_path: Option, + pub rustdoc_path: Option, /// The coverage-dump executable. - pub coverage_dump_path: Option, + pub coverage_dump_path: Option, /// The Python executable to use for LLDB and htmldocck. pub python: String, @@ -206,27 +212,27 @@ pub struct Config { pub jsondoclint_path: Option, /// The LLVM `FileCheck` binary path. - pub llvm_filecheck: Option, + pub llvm_filecheck: Option, /// Path to LLVM's bin directory. - pub llvm_bin_dir: Option, + pub llvm_bin_dir: Option, /// The path to the Clang executable to run Clang-based tests with. If /// `None` then these tests will be ignored. pub run_clang_based_tests_with: Option, /// The directory containing the sources. - pub src_root: PathBuf, + pub src_root: Utf8PathBuf, /// The directory containing the test suite sources. Must be a subdirectory of `src_root`. - pub src_test_suite_root: PathBuf, + pub src_test_suite_root: Utf8PathBuf, /// Root build directory (e.g. `build/`). - pub build_root: PathBuf, + pub build_root: Utf8PathBuf, /// Test suite specific build directory (e.g. `build/host/test/ui/`). - pub build_test_suite_root: PathBuf, + pub build_test_suite_root: Utf8PathBuf, /// The directory containing the compiler sysroot - pub sysroot_base: PathBuf, + pub sysroot_base: Utf8PathBuf, /// The number of the stage under test. pub stage: u32, @@ -268,9 +274,6 @@ pub struct Config { /// Explicitly enable or disable running. pub run: Option, - /// Write out a parseable log of tests that were run - pub logfile: Option, - /// A command line to prefix program execution with, /// for running under valgrind for example. /// @@ -297,7 +300,7 @@ pub struct Config { pub host: String, /// Path to / name of the Microsoft Console Debugger (CDB) executable - pub cdb: Option, + pub cdb: Option, /// Version of CDB pub cdb_version: Option<[u16; 4]>, @@ -318,7 +321,7 @@ pub struct Config { pub system_llvm: bool, /// Path to the android tools - pub android_cross_path: PathBuf, + pub android_cross_path: Utf8PathBuf, /// Extra parameter to run adb on arm-linux-androideabi pub adb_path: String, @@ -342,7 +345,7 @@ pub struct Config { pub color: ColorConfig, /// where to find the remote test client process, if we're using it - pub remote_test_client: Option, + pub remote_test_client: Option, /// mode describing what file the actual ui output will be compared to pub compare_mode: Option, @@ -391,11 +394,11 @@ pub struct Config { pub target_cfgs: OnceLock, pub builtin_cfg_names: OnceLock>, + pub supported_crate_types: OnceLock>, pub nocapture: bool, // Needed both to construct build_helper::git::GitConfig - pub git_repository: String, pub nightly_branch: String, pub git_merge_commit_email: String, @@ -409,7 +412,7 @@ pub struct Config { /// Path to minicore aux library, used for `no_core` tests that need `core` stubs in /// cross-compilation scenarios that do not otherwise want/need to `-Zbuild-std`. Used in e.g. /// ABI tests. - pub minicore_path: PathBuf, + pub minicore_path: Utf8PathBuf, } impl Config { @@ -468,6 +471,11 @@ impl Config { self.builtin_cfg_names.get_or_init(|| builtin_cfg_names(self)) } + /// Get the list of crate types that the target platform supports. + pub fn supported_crate_types(&self) -> &HashSet { + self.supported_crate_types.get_or_init(|| supported_crate_types(self)) + } + pub fn has_threads(&self) -> bool { // Wasm targets don't have threads unless `-threads` is in the target // name, such as `wasm32-wasip1-threads`. @@ -478,9 +486,17 @@ impl Config { } pub fn has_asm_support(&self) -> bool { + // This should match the stable list in `LoweringContext::lower_inline_asm`. static ASM_SUPPORTED_ARCHS: &[&str] = &[ - "x86", "x86_64", "arm", "aarch64", "riscv32", + "x86", + "x86_64", + "arm", + "aarch64", + "arm64ec", + "riscv32", "riscv64", + "loongarch64", + "s390x", // These targets require an additional asm_experimental_arch feature. // "nvptx64", "hexagon", "mips", "mips64", "spirv", "wasm32", ]; @@ -489,7 +505,6 @@ impl Config { pub fn git_config(&self) -> GitConfig<'_> { GitConfig { - git_repository: &self.git_repository, nightly_branch: &self.nightly_branch, git_merge_commit_email: &self.git_merge_commit_email, } @@ -733,6 +748,31 @@ fn builtin_cfg_names(config: &Config) -> HashSet { .collect() } +pub const KNOWN_CRATE_TYPES: &[&str] = + &["bin", "cdylib", "dylib", "lib", "proc-macro", "rlib", "staticlib"]; + +fn supported_crate_types(config: &Config) -> HashSet { + let crate_types: HashSet<_> = rustc_output( + config, + &["--target", &config.target, "--print=supported-crate-types", "-Zunstable-options"], + Default::default(), + ) + .lines() + .map(|l| l.to_string()) + .collect(); + + for crate_type in crate_types.iter() { + assert!( + KNOWN_CRATE_TYPES.contains(&crate_type.as_str()), + "unexpected crate type `{}`: known crate types are {:?}", + crate_type, + KNOWN_CRATE_TYPES + ); + } + + crate_types +} + fn rustc_output(config: &Config, args: &[&str], envs: HashMap) -> String { let mut command = Command::new(&config.rustc_path); add_dylib_path(&mut command, iter::once(&config.compile_lib_path)); @@ -761,8 +801,8 @@ fn serde_parse_u32<'de, D: Deserializer<'de>>(deserializer: D) -> Result, compare_mode: &Option, kind: &str, -) -> PathBuf { +) -> Utf8PathBuf { assert!(UI_EXTENSIONS.contains(&kind)); let mut parts = Vec::new(); @@ -822,7 +862,7 @@ pub const UI_COVERAGE_MAP: &str = "cov-map"; /// ``` /// /// This is created early when tests are collected to avoid race conditions. -pub fn output_relative_path(config: &Config, relative_dir: &Path) -> PathBuf { +pub fn output_relative_path(config: &Config, relative_dir: &Utf8Path) -> Utf8PathBuf { config.build_test_suite_root.join(relative_dir) } @@ -831,10 +871,10 @@ pub fn output_testname_unique( config: &Config, testpaths: &TestPaths, revision: Option<&str>, -) -> PathBuf { +) -> Utf8PathBuf { let mode = config.compare_mode.as_ref().map_or("", |m| m.to_str()); let debugger = config.debugger.as_ref().map_or("", |m| m.to_str()); - PathBuf::from(&testpaths.file.file_stem().unwrap()) + Utf8PathBuf::from(&testpaths.file.file_stem().unwrap()) .with_extra_extension(config.mode.output_dir_disambiguator()) .with_extra_extension(revision.unwrap_or("")) .with_extra_extension(mode) @@ -844,7 +884,11 @@ pub fn output_testname_unique( /// Absolute path to the directory where all output for the given /// test/revision should reside. Example: /// /path/to/build/host-tuple/test/ui/relative/testname.revision.mode/ -pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { +pub fn output_base_dir( + config: &Config, + testpaths: &TestPaths, + revision: Option<&str>, +) -> Utf8PathBuf { output_relative_path(config, &testpaths.relative_dir) .join(output_testname_unique(config, testpaths, revision)) } @@ -852,12 +896,20 @@ pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option< /// Absolute path to the base filename used as output for the given /// test/revision. Example: /// /path/to/build/host-tuple/test/ui/relative/testname.revision.mode/testname -pub fn output_base_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { +pub fn output_base_name( + config: &Config, + testpaths: &TestPaths, + revision: Option<&str>, +) -> Utf8PathBuf { output_base_dir(config, testpaths, revision).join(testpaths.file.file_stem().unwrap()) } /// Absolute path to the directory to use for incremental compilation. Example: /// /path/to/build/host-tuple/test/ui/relative/testname.mode/testname.inc -pub fn incremental_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { +pub fn incremental_dir( + config: &Config, + testpaths: &TestPaths, + revision: Option<&str>, +) -> Utf8PathBuf { output_base_name(config, testpaths, revision).with_extension("inc") } diff --git a/src/tools/compiletest/src/compute_diff.rs b/src/tools/compiletest/src/compute_diff.rs index 4c942c51bae13..509e7e117039c 100644 --- a/src/tools/compiletest/src/compute_diff.rs +++ b/src/tools/compiletest/src/compute_diff.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; use std::fs::{File, FileType}; -use std::path::Path; + +use camino::Utf8Path; #[derive(Debug, PartialEq)] pub enum DiffLine { @@ -112,8 +113,8 @@ pub(crate) fn write_diff(expected: &str, actual: &str, context_size: usize) -> S /// Returns whether any data was actually written. pub(crate) fn write_filtered_diff( diff_filename: &str, - out_dir: &Path, - compare_dir: &Path, + out_dir: &Utf8Path, + compare_dir: &Utf8Path, verbose: bool, filter: Filter, ) -> bool @@ -123,19 +124,21 @@ where use std::io::{Read, Write}; let mut diff_output = File::create(diff_filename).unwrap(); let mut wrote_data = false; - for entry in walkdir::WalkDir::new(out_dir) { + for entry in walkdir::WalkDir::new(out_dir.as_std_path()) { let entry = entry.expect("failed to read file"); let extension = entry.path().extension().and_then(|p| p.to_str()); if filter(entry.file_type(), extension) { - let expected_path = compare_dir.join(entry.path().strip_prefix(&out_dir).unwrap()); + let expected_path = compare_dir + .as_std_path() + .join(entry.path().strip_prefix(&out_dir.as_std_path()).unwrap()); let expected = if let Ok(s) = std::fs::read(&expected_path) { s } else { continue }; let actual_path = entry.path(); let actual = std::fs::read(&actual_path).unwrap(); let diff = unified_diff::diff( &expected, - &expected_path.to_string_lossy(), + &expected_path.to_str().unwrap(), &actual, - &actual_path.to_string_lossy(), + &actual_path.to_str().unwrap(), 3, ); wrote_data |= !diff.is_empty(); diff --git a/src/tools/compiletest/src/debuggers.rs b/src/tools/compiletest/src/debuggers.rs index 20e3c8dfb9ee7..c133d7fd4fbd0 100644 --- a/src/tools/compiletest/src/debuggers.rs +++ b/src/tools/compiletest/src/debuggers.rs @@ -1,9 +1,9 @@ use std::env; -use std::ffi::OsString; -use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::Arc; +use camino::{Utf8Path, Utf8PathBuf}; + use crate::common::{Config, Debugger}; pub(crate) fn configure_cdb(config: &Config) -> Option> { @@ -40,7 +40,9 @@ pub(crate) fn configure_gdb(config: &Config) -> Option> { // // we should figure out how to lift this restriction! (run them all // on different ports allocated dynamically). - env::set_var("RUST_TEST_THREADS", "1"); + // + // SAFETY: at this point we are still single-threaded. + unsafe { env::set_var("RUST_TEST_THREADS", "1") }; } Some(Arc::new(Config { debugger: Some(Debugger::Gdb), ..config.clone() })) @@ -76,12 +78,15 @@ fn is_pc_windows_msvc_target(target: &str) -> bool { target.ends_with("-pc-windows-msvc") } -fn find_cdb(target: &str) -> 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_else(|| env::var_os("ProgramFiles"))?; + let pf86 = Utf8PathBuf::from_path_buf( + env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?.into(), + ) + .unwrap(); let cdb_arch = if cfg!(target_arch = "x86") { "x86" } else if cfg!(target_arch = "x86_64") { @@ -94,8 +99,7 @@ fn find_cdb(target: &str) -> Option { return None; // No compatible CDB.exe in the Windows 10 SDK }; - let mut path = PathBuf::new(); - path.push(pf86); + let mut path = pf86; path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too? path.push(cdb_arch); path.push(r"cdb.exe"); @@ -104,15 +108,15 @@ fn find_cdb(target: &str) -> Option { return None; } - Some(path.into_os_string()) + Some(path) } /// Returns Path to CDB pub(crate) fn analyze_cdb( cdb: Option, target: &str, -) -> (Option, Option<[u16; 4]>) { - let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target)); +) -> (Option, Option<[u16; 4]>) { + let cdb = cdb.map(Utf8PathBuf::from).or_else(|| find_cdb(target)); let mut version = None; if let Some(cdb) = cdb.as_ref() { @@ -141,7 +145,7 @@ pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> { pub(crate) fn analyze_gdb( gdb: Option, target: &str, - android_cross_path: &Path, + android_cross_path: &Utf8Path, ) -> (Option, Option) { #[cfg(not(windows))] const GDB_FALLBACK: &str = "gdb"; @@ -150,10 +154,7 @@ pub(crate) fn analyze_gdb( let fallback_gdb = || { if is_android_gdb_target(target) { - let mut gdb_path = match android_cross_path.to_str() { - Some(x) => x.to_owned(), - None => panic!("cannot find android cross path"), - }; + let mut gdb_path = android_cross_path.to_string(); gdb_path.push_str("/bin/gdb"); gdb_path } else { diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs index 8c909bcb19527..5757e422ae21e 100644 --- a/src/tools/compiletest/src/directive-list.rs +++ b/src/tools/compiletest/src/directive-list.rs @@ -22,6 +22,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "dont-check-compiler-stderr", "dont-check-compiler-stdout", "dont-check-failure-status", + "dont-require-annotations", "edition", "error-pattern", "exact-llvm-major-version", @@ -34,6 +35,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-32bit", "ignore-64bit", "ignore-aarch64", + "ignore-aarch64-pc-windows-msvc", "ignore-aarch64-unknown-linux-gnu", "ignore-aix", "ignore-android", @@ -43,6 +45,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-arm-unknown-linux-gnueabihf", "ignore-arm-unknown-linux-musleabi", "ignore-arm-unknown-linux-musleabihf", + "ignore-auxiliary", "ignore-avr", "ignore-beta", "ignore-cdb", @@ -52,6 +55,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-coverage-run", "ignore-cross-compile", "ignore-eabi", + "ignore-elf", "ignore-emscripten", "ignore-endian-big", "ignore-enzyme", @@ -131,6 +135,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "min-llvm-version", "min-system-llvm-version", "needs-asm-support", + "needs-crate-type", "needs-deterministic-layouts", "needs-dlltool", "needs-dynamic-linking", @@ -174,6 +179,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-32bit", "only-64bit", "only-aarch64", + "only-aarch64-apple-darwin", "only-aarch64-unknown-linux-gnu", "only-apple", "only-arm", @@ -182,10 +188,12 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-bpf", "only-cdb", "only-dist", + "only-elf", "only-emscripten", "only-gnu", "only-i686-pc-windows-gnu", "only-i686-pc-windows-msvc", + "only-i686-unknown-linux-gnu", "only-ios", "only-linux", "only-loongarch64", @@ -217,6 +225,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-windows-msvc", "only-x86", "only-x86_64", + "only-x86_64-apple-darwin", "only-x86_64-fortanix-unknown-sgx", "only-x86_64-pc-windows-gnu", "only-x86_64-pc-windows-msvc", diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs index 37ef63ae42ea9..b5a2b7feac9d6 100644 --- a/src/tools/compiletest/src/errors.rs +++ b/src/tools/compiletest/src/errors.rs @@ -2,36 +2,49 @@ use std::fmt; use std::fs::File; use std::io::BufReader; use std::io::prelude::*; -use std::path::Path; -use std::str::FromStr; use std::sync::OnceLock; +use camino::Utf8Path; use regex::Regex; use tracing::*; -use self::WhichLine::*; - -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum ErrorKind { Help, Error, Note, Suggestion, Warning, + Raw, } -impl FromStr for ErrorKind { - type Err = (); - fn from_str(s: &str) -> Result { - let s = s.to_uppercase(); - let part0: &str = s.split(':').next().unwrap(); - match part0 { - "HELP" => Ok(ErrorKind::Help), - "ERROR" => Ok(ErrorKind::Error), - "NOTE" => Ok(ErrorKind::Note), - "SUGGESTION" => Ok(ErrorKind::Suggestion), - "WARN" | "WARNING" => Ok(ErrorKind::Warning), - _ => Err(()), +impl ErrorKind { + pub fn from_compiler_str(s: &str) -> ErrorKind { + match s { + "help" => ErrorKind::Help, + "error" | "error: internal compiler error" => ErrorKind::Error, + "note" | "failure-note" => ErrorKind::Note, + "warning" => ErrorKind::Warning, + _ => panic!("unexpected compiler diagnostic kind `{s}`"), + } + } + + /// Either the canonical uppercase string, or some additional versions for compatibility. + /// FIXME: consider keeping only the canonical versions here. + pub fn from_user_str(s: &str) -> ErrorKind { + match s { + "HELP" | "help" => ErrorKind::Help, + "ERROR" | "error" => ErrorKind::Error, + // `MONO_ITEM` makes annotations in `codegen-units` tests syntactically correct, + // but those tests never use the error kind later on. + "NOTE" | "note" | "MONO_ITEM" => ErrorKind::Note, + "SUGGESTION" => ErrorKind::Suggestion, + "WARN" | "WARNING" | "warn" | "warning" => ErrorKind::Warning, + "RAW" => ErrorKind::Raw, + _ => panic!( + "unexpected diagnostic kind `{s}`, expected \ + `ERROR`, `WARN`, `NOTE`, `HELP` or `SUGGESTION`" + ), } } } @@ -39,41 +52,37 @@ impl FromStr for ErrorKind { impl fmt::Display for ErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - ErrorKind::Help => write!(f, "help message"), - ErrorKind::Error => write!(f, "error"), - ErrorKind::Note => write!(f, "note"), - ErrorKind::Suggestion => write!(f, "suggestion"), - ErrorKind::Warning => write!(f, "warning"), + ErrorKind::Help => write!(f, "HELP"), + ErrorKind::Error => write!(f, "ERROR"), + ErrorKind::Note => write!(f, "NOTE"), + ErrorKind::Suggestion => write!(f, "SUGGESTION"), + ErrorKind::Warning => write!(f, "WARN"), + ErrorKind::Raw => write!(f, "RAW"), } } } #[derive(Debug)] pub struct Error { - pub line_num: usize, + pub line_num: Option, /// What kind of message we expect (e.g., warning, error, suggestion). - /// `None` if not specified or unknown message kind. - pub kind: Option, + pub kind: ErrorKind, pub msg: String, + /// For some `Error`s, like secondary lines of multi-line diagnostics, line annotations + /// are not mandatory, even if they would otherwise be mandatory for primary errors. + /// Only makes sense for "actual" errors, not for "expected" errors. + pub require_annotation: bool, } impl Error { pub fn render_for_expected(&self) -> String { use colored::Colorize; - format!( - "{: <10}line {: >3}: {}", - self.kind.map(|kind| kind.to_string()).unwrap_or_default().to_uppercase(), - self.line_num, - self.msg.cyan(), - ) + format!("{: <10}line {: >3}: {}", self.kind, self.line_num_str(), self.msg.cyan()) } -} -#[derive(PartialEq, Debug)] -enum WhichLine { - ThisLine, - FollowPrevious(usize), - AdjustBackward(usize), + pub fn line_num_str(&self) -> String { + self.line_num.map_or("?".to_string(), |line_num| line_num.to_string()) + } } /// Looks for either "//~| KIND MESSAGE" or "//~^^... KIND MESSAGE" @@ -86,8 +95,8 @@ enum WhichLine { /// /// If revision is not None, then we look /// for `//[X]~` instead, where `X` is the current revision. -pub fn load_errors(testfile: &Path, revision: Option<&str>) -> Vec { - let rdr = BufReader::new(File::open(testfile).unwrap()); +pub fn load_errors(testfile: &Utf8Path, revision: Option<&str>) -> Vec { + let rdr = BufReader::new(File::open(testfile.as_std_path()).unwrap()); // `last_nonfollow_error` tracks the most recently seen // line with an error template that did not use the @@ -105,12 +114,10 @@ pub fn load_errors(testfile: &Path, revision: Option<&str>) -> Vec { .filter(|(_, line)| line.is_ok()) .filter_map(|(line_num, line)| { parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), revision).map( - |(which, error)| { - match which { - FollowPrevious(_) => {} - _ => last_nonfollow_error = Some(error.line_num), + |(follow_prev, error)| { + if !follow_prev { + last_nonfollow_error = error.line_num; } - error }, ) @@ -123,18 +130,23 @@ fn parse_expected( line_num: usize, line: &str, test_revision: Option<&str>, -) -> Option<(WhichLine, Error)> { +) -> Option<(bool, Error)> { // Matches comments like: // //~ // //~| // //~^ // //~^^^^^ + // //~v + // //~vvvvv + // //~? // //[rev1]~ // //[rev1,rev2]~^^ static RE: OnceLock = OnceLock::new(); let captures = RE - .get_or_init(|| Regex::new(r"//(?:\[(?P[\w\-,]+)])?~(?P\||\^*)").unwrap()) + .get_or_init(|| { + Regex::new(r"//(?:\[(?P[\w\-,]+)])?~(?P\?|\||[v\^]*)").unwrap() + }) .captures(line)?; match (test_revision, captures.name("revs")) { @@ -151,47 +163,35 @@ fn parse_expected( (Some(_), None) | (None, None) => {} } - let (follow, adjusts) = match &captures["adjust"] { - "|" => (true, 0), - circumflexes => (false, circumflexes.len()), - }; - // Get the part of the comment after the sigil (e.g. `~^^` or ~|). - let whole_match = captures.get(0).unwrap(); - let (_, mut msg) = line.split_at(whole_match.end()); - - let first_word = msg.split_whitespace().next().expect("Encountered unexpected empty comment"); - - // If we find `//~ ERROR foo` or something like that, skip the first word. - let kind = first_word.parse::().ok(); - if kind.is_some() { - msg = &msg.trim_start().split_at(first_word.len()).1; - } - - let msg = msg.trim().to_owned(); - - let (which, line_num) = if follow { - assert_eq!(adjusts, 0, "use either //~| or //~^, not both."); - let line_num = last_nonfollow_error.expect( - "encountered //~| without \ - preceding //~^ line.", - ); - (FollowPrevious(line_num), line_num) + let tag = captures.get(0).unwrap(); + let rest = line[tag.end()..].trim_start(); + let (kind_str, _) = + rest.split_once(|c: char| c != '_' && !c.is_ascii_alphabetic()).unwrap_or((rest, "")); + let kind = ErrorKind::from_user_str(kind_str); + let untrimmed_msg = &rest[kind_str.len()..]; + let msg = untrimmed_msg.strip_prefix(':').unwrap_or(untrimmed_msg).trim().to_owned(); + + let line_num_adjust = &captures["adjust"]; + let (follow_prev, line_num) = if line_num_adjust == "|" { + (true, Some(last_nonfollow_error.expect("encountered //~| without preceding //~^ line"))) + } else if line_num_adjust == "?" { + (false, None) + } else if line_num_adjust.starts_with('v') { + (false, Some(line_num + line_num_adjust.len())) } else { - let which = if adjusts > 0 { AdjustBackward(adjusts) } else { ThisLine }; - let line_num = line_num - adjusts; - (which, line_num) + (false, Some(line_num - line_num_adjust.len())) }; debug!( - "line={} tag={:?} which={:?} kind={:?} msg={:?}", + "line={:?} tag={:?} follow_prev={:?} kind={:?} msg={:?}", line_num, - whole_match.as_str(), - which, + tag.as_str(), + follow_prev, kind, msg ); - Some((which, Error { line_num, kind, msg })) + Some((follow_prev, Error { line_num, kind, msg, require_annotation: true })) } #[cfg(test)] diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs new file mode 100644 index 0000000000000..e774c5e2047c8 --- /dev/null +++ b/src/tools/compiletest/src/executor.rs @@ -0,0 +1,294 @@ +//! This module contains a reimplementation of the subset of libtest +//! functionality needed by compiletest. + +use std::borrow::Cow; +use std::collections::HashMap; +use std::hash::{BuildHasherDefault, DefaultHasher}; +use std::num::NonZero; +use std::sync::{Arc, Mutex, mpsc}; +use std::{env, hint, io, mem, panic, thread}; + +use crate::common::{Config, TestPaths}; + +mod deadline; +mod json; + +pub(crate) fn run_tests(config: &Config, tests: Vec) -> bool { + let tests_len = tests.len(); + let filtered = filter_tests(config, tests); + // Iterator yielding tests that haven't been started yet. + let mut fresh_tests = (0..).map(TestId).zip(&filtered); + + let concurrency = get_concurrency(); + assert!(concurrency > 0); + let concurrent_capacity = concurrency.min(filtered.len()); + + let mut listener = json::Listener::new(); + let mut running_tests = HashMap::with_capacity_and_hasher( + concurrent_capacity, + BuildHasherDefault::::new(), + ); + let mut deadline_queue = deadline::DeadlineQueue::with_capacity(concurrent_capacity); + + let num_filtered_out = tests_len - filtered.len(); + listener.suite_started(filtered.len(), num_filtered_out); + + // Channel used by test threads to report the test outcome when done. + let (completion_tx, completion_rx) = mpsc::channel::(); + + // Unlike libtest, we don't have a separate code path for concurrency=1. + // In that case, the tests will effectively be run serially anyway. + loop { + // Spawn new test threads, up to the concurrency limit. + // FIXME(let_chains): Use a let-chain here when stable in bootstrap. + 'spawn: while running_tests.len() < concurrency { + let Some((id, test)) = fresh_tests.next() else { break 'spawn }; + listener.test_started(test); + deadline_queue.push(id, test); + let join_handle = spawn_test_thread(id, test, completion_tx.clone()); + running_tests.insert(id, RunningTest { test, join_handle }); + } + + // If all running tests have finished, and there weren't any unstarted + // tests to spawn, then we're done. + if running_tests.is_empty() { + break; + } + + let completion = deadline_queue + .read_channel_while_checking_deadlines( + &completion_rx, + |id| running_tests.contains_key(&id), + |_id, test| listener.test_timed_out(test), + ) + .expect("receive channel should never be closed early"); + + let RunningTest { test, join_handle } = running_tests.remove(&completion.id).unwrap(); + if let Some(join_handle) = join_handle { + join_handle.join().unwrap_or_else(|_| { + panic!("thread for `{}` panicked after reporting completion", test.desc.name) + }); + } + + listener.test_finished(test, &completion); + + if completion.outcome.is_failed() && config.fail_fast { + // Prevent any other in-flight threads from panicking when they + // write to the completion channel. + mem::forget(completion_rx); + break; + } + } + + let suite_passed = listener.suite_finished(); + suite_passed +} + +/// Spawns a thread to run a single test, and returns the thread's join handle. +/// +/// Returns `None` if the test was ignored, so no thread was spawned. +fn spawn_test_thread( + id: TestId, + test: &CollectedTest, + completion_tx: mpsc::Sender, +) -> Option> { + if test.desc.ignore && !test.config.run_ignored { + completion_tx + .send(TestCompletion { id, outcome: TestOutcome::Ignored, stdout: None }) + .unwrap(); + return None; + } + + let runnable_test = RunnableTest::new(test); + let should_panic = test.desc.should_panic; + let run_test = move || run_test_inner(id, should_panic, runnable_test, completion_tx); + + let thread_builder = thread::Builder::new().name(test.desc.name.clone()); + let join_handle = thread_builder.spawn(run_test).unwrap(); + Some(join_handle) +} + +/// Runs a single test, within the dedicated thread spawned by the caller. +fn run_test_inner( + id: TestId, + should_panic: ShouldPanic, + runnable_test: RunnableTest, + completion_sender: mpsc::Sender, +) { + let is_capture = !runnable_test.config.nocapture; + let capture_buf = is_capture.then(|| Arc::new(Mutex::new(vec![]))); + + if let Some(capture_buf) = &capture_buf { + io::set_output_capture(Some(Arc::clone(capture_buf))); + } + + let panic_payload = panic::catch_unwind(move || runnable_test.run()).err(); + + if is_capture { + io::set_output_capture(None); + } + + let outcome = match (should_panic, panic_payload) { + (ShouldPanic::No, None) | (ShouldPanic::Yes, Some(_)) => TestOutcome::Succeeded, + (ShouldPanic::No, Some(_)) => TestOutcome::Failed { message: None }, + (ShouldPanic::Yes, None) => { + TestOutcome::Failed { message: Some("test did not panic as expected") } + } + }; + let stdout = capture_buf.map(|mutex| mutex.lock().unwrap_or_else(|e| e.into_inner()).to_vec()); + + completion_sender.send(TestCompletion { id, outcome, stdout }).unwrap(); +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +struct TestId(usize); + +struct RunnableTest { + config: Arc, + testpaths: TestPaths, + revision: Option, +} + +impl RunnableTest { + fn new(test: &CollectedTest) -> Self { + let config = Arc::clone(&test.config); + let testpaths = test.testpaths.clone(); + let revision = test.revision.clone(); + Self { config, testpaths, revision } + } + + fn run(&self) { + __rust_begin_short_backtrace(|| { + crate::runtest::run( + Arc::clone(&self.config), + &self.testpaths, + self.revision.as_deref(), + ); + }); + } +} + +/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. +#[inline(never)] +fn __rust_begin_short_backtrace T>(f: F) -> T { + let result = f(); + + // prevent this frame from being tail-call optimised away + hint::black_box(result) +} + +struct RunningTest<'a> { + test: &'a CollectedTest, + join_handle: Option>, +} + +/// Test completion message sent by individual test threads when their test +/// finishes (successfully or unsuccessfully). +struct TestCompletion { + id: TestId, + outcome: TestOutcome, + stdout: Option>, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +enum TestOutcome { + Succeeded, + Failed { message: Option<&'static str> }, + Ignored, +} + +impl TestOutcome { + fn is_failed(&self) -> bool { + matches!(self, Self::Failed { .. }) + } +} + +/// Applies command-line arguments for filtering/skipping tests by name. +/// +/// Adapted from `filter_tests` in libtest. +/// +/// FIXME(#139660): After the libtest dependency is removed, redesign the whole +/// filtering system to do a better job of understanding and filtering _paths_, +/// instead of being tied to libtest's substring/exact matching behaviour. +fn filter_tests(opts: &Config, tests: Vec) -> Vec { + let mut filtered = tests; + + let matches_filter = |test: &CollectedTest, filter_str: &str| { + let test_name = &test.desc.name; + if opts.filter_exact { test_name == filter_str } else { test_name.contains(filter_str) } + }; + + // Remove tests that don't match the test filter + if !opts.filters.is_empty() { + filtered.retain(|test| opts.filters.iter().any(|filter| matches_filter(test, filter))); + } + + // Skip tests that match any of the skip filters + if !opts.skip.is_empty() { + filtered.retain(|test| !opts.skip.iter().any(|sf| matches_filter(test, sf))); + } + + filtered +} + +/// Determines the number of tests to run concurrently. +/// +/// Copied from `get_concurrency` in libtest. +/// +/// FIXME(#139660): After the libtest dependency is removed, consider making +/// bootstrap specify the number of threads on the command-line, instead of +/// propagating the `RUST_TEST_THREADS` environment variable. +fn get_concurrency() -> usize { + if let Ok(value) = env::var("RUST_TEST_THREADS") { + match value.parse::>().ok() { + Some(n) => n.get(), + _ => panic!("RUST_TEST_THREADS is `{value}`, should be a positive integer."), + } + } else { + thread::available_parallelism().map(|n| n.get()).unwrap_or(1) + } +} + +/// Information needed to create a `test::TestDescAndFn`. +pub(crate) struct CollectedTest { + pub(crate) desc: CollectedTestDesc, + pub(crate) config: Arc, + pub(crate) testpaths: TestPaths, + pub(crate) revision: Option, +} + +/// Information needed to create a `test::TestDesc`. +pub(crate) struct CollectedTestDesc { + pub(crate) name: String, + pub(crate) ignore: bool, + pub(crate) ignore_message: Option>, + pub(crate) should_panic: ShouldPanic, +} + +/// Whether console output should be colored or not. +#[derive(Copy, Clone, Default, Debug)] +pub enum ColorConfig { + #[default] + AutoColor, + AlwaysColor, + NeverColor, +} + +/// Format of the test results output. +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum OutputFormat { + /// Verbose output + Pretty, + /// Quiet output + #[default] + Terse, + /// JSON output + Json, +} + +/// Whether test is expected to panic or not. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub(crate) enum ShouldPanic { + No, + Yes, +} diff --git a/src/tools/compiletest/src/executor/deadline.rs b/src/tools/compiletest/src/executor/deadline.rs new file mode 100644 index 0000000000000..3536eff2fd80d --- /dev/null +++ b/src/tools/compiletest/src/executor/deadline.rs @@ -0,0 +1,102 @@ +use std::collections::VecDeque; +use std::sync::mpsc::{self, RecvError, RecvTimeoutError}; +use std::time::{Duration, Instant}; + +use crate::executor::{CollectedTest, TestId}; + +const TEST_WARN_TIMEOUT_S: u64 = 60; + +struct DeadlineEntry<'a> { + id: TestId, + test: &'a CollectedTest, + deadline: Instant, +} + +pub(crate) struct DeadlineQueue<'a> { + queue: VecDeque>, +} + +impl<'a> DeadlineQueue<'a> { + pub(crate) fn with_capacity(capacity: usize) -> Self { + Self { queue: VecDeque::with_capacity(capacity) } + } + + /// All calls to [`Instant::now`] go through this wrapper method. + /// This makes it easier to find all places that read the current time. + fn now(&self) -> Instant { + Instant::now() + } + + pub(crate) fn push(&mut self, id: TestId, test: &'a CollectedTest) { + let deadline = self.now() + Duration::from_secs(TEST_WARN_TIMEOUT_S); + if let Some(back) = self.queue.back() { + assert!(back.deadline <= deadline); + } + self.queue.push_back(DeadlineEntry { id, test, deadline }); + } + + /// Equivalent to `rx.recv()`, except that if a test exceeds its deadline + /// during the wait, the given callback will also be called for that test. + pub(crate) fn read_channel_while_checking_deadlines( + &mut self, + rx: &mpsc::Receiver, + is_running: impl Fn(TestId) -> bool, + mut on_deadline_passed: impl FnMut(TestId, &CollectedTest), + ) -> Result { + loop { + let Some(next_deadline) = self.next_deadline() else { + // All currently-running tests have already exceeded their + // deadline, so do a normal receive. + return rx.recv(); + }; + let next_deadline_timeout = next_deadline.saturating_duration_since(self.now()); + + let recv_result = rx.recv_timeout(next_deadline_timeout); + // Process deadlines after every receive attempt, regardless of + // outcome, so that we don't build up an unbounded backlog of stale + // entries due to a constant stream of tests finishing. + self.for_each_entry_past_deadline(&is_running, &mut on_deadline_passed); + + match recv_result { + Ok(value) => return Ok(value), + // Deadlines have already been processed, so loop and do another receive. + Err(RecvTimeoutError::Timeout) => {} + Err(RecvTimeoutError::Disconnected) => return Err(RecvError), + } + } + } + + fn next_deadline(&self) -> Option { + Some(self.queue.front()?.deadline) + } + + fn for_each_entry_past_deadline( + &mut self, + is_running: impl Fn(TestId) -> bool, + mut on_deadline_passed: impl FnMut(TestId, &CollectedTest), + ) { + let now = self.now(); + + // Clear out entries that are past their deadline, but only invoke the + // callback for tests that are still considered running. + while let Some(entry) = pop_front_if(&mut self.queue, |entry| entry.deadline <= now) { + if is_running(entry.id) { + on_deadline_passed(entry.id, entry.test); + } + } + + // Also clear out any leading entries that are no longer running, even + // if their deadline hasn't been reached. + while let Some(_) = pop_front_if(&mut self.queue, |entry| !is_running(entry.id)) {} + + if let Some(front) = self.queue.front() { + assert!(now < front.deadline); + } + } +} + +/// FIXME(vec_deque_pop_if): Use `VecDeque::pop_front_if` when it is stable in bootstrap. +fn pop_front_if(queue: &mut VecDeque, predicate: impl FnOnce(&T) -> bool) -> Option { + let first = queue.front()?; + if predicate(first) { queue.pop_front() } else { None } +} diff --git a/src/tools/compiletest/src/executor/json.rs b/src/tools/compiletest/src/executor/json.rs new file mode 100644 index 0000000000000..c74ed81a36b85 --- /dev/null +++ b/src/tools/compiletest/src/executor/json.rs @@ -0,0 +1,111 @@ +//! Collects statistics and emits suite/test events as JSON messages, using +//! the same JSON format as libtest's JSON formatter. +//! +//! These messages are then parsed by bootstrap, which replaces them with +//! user-friendly terminal output. + +use std::time::Instant; + +use serde_json::json; + +use crate::executor::{CollectedTest, TestCompletion, TestOutcome}; + +pub(crate) struct Listener { + suite_start: Option, + passed: usize, + failed: usize, + ignored: usize, + filtered_out: usize, +} + +impl Listener { + pub(crate) fn new() -> Self { + Self { suite_start: None, passed: 0, failed: 0, ignored: 0, filtered_out: 0 } + } + + fn print_message(&self, message: &serde_json::Value) { + println!("{message}"); + } + + fn now(&self) -> Instant { + Instant::now() + } + + pub(crate) fn suite_started(&mut self, test_count: usize, filtered_out: usize) { + self.suite_start = Some(self.now()); + self.filtered_out = filtered_out; + let message = json!({ "type": "suite", "event": "started", "test_count": test_count }); + self.print_message(&message); + } + + pub(crate) fn test_started(&mut self, test: &CollectedTest) { + let name = test.desc.name.as_str(); + let message = json!({ "type": "test", "event": "started", "name": name }); + self.print_message(&message); + } + + pub(crate) fn test_timed_out(&mut self, test: &CollectedTest) { + let name = test.desc.name.as_str(); + let message = json!({ "type": "test", "event": "timeout", "name": name }); + self.print_message(&message); + } + + pub(crate) fn test_finished(&mut self, test: &CollectedTest, completion: &TestCompletion) { + let event; + let name = test.desc.name.as_str(); + let mut maybe_message = None; + let maybe_stdout = completion.stdout.as_deref().map(String::from_utf8_lossy); + + match completion.outcome { + TestOutcome::Succeeded => { + self.passed += 1; + event = "ok"; + } + TestOutcome::Failed { message } => { + self.failed += 1; + maybe_message = message; + event = "failed"; + } + TestOutcome::Ignored => { + self.ignored += 1; + maybe_message = test.desc.ignore_message.as_deref(); + event = "ignored"; + } + }; + + // This emits optional fields as `null`, instead of omitting them + // completely as libtest does, but bootstrap can parse the result + // either way. + let json = json!({ + "type": "test", + "event": event, + "name": name, + "message": maybe_message, + "stdout": maybe_stdout, + }); + + self.print_message(&json); + } + + pub(crate) fn suite_finished(&mut self) -> bool { + let exec_time = self.suite_start.map(|start| (self.now() - start).as_secs_f64()); + let suite_passed = self.failed == 0; + + let event = if suite_passed { "ok" } else { "failed" }; + let message = json!({ + "type": "suite", + "event": event, + "passed": self.passed, + "failed": self.failed, + "ignored": self.ignored, + // Compiletest doesn't run any benchmarks, but we still need to set this + // field to 0 so that bootstrap's JSON parser can read our message. + "measured": 0, + "filtered_out": self.filtered_out, + "exec_time": exec_time, + }); + + self.print_message(&message); + suite_passed + } +} diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 53ee901b8bc48..8bee9caacc949 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -3,14 +3,16 @@ use std::env; use std::fs::File; use std::io::BufReader; use std::io::prelude::*; -use std::path::{Path, PathBuf}; use std::process::Command; +use camino::{Utf8Path, Utf8PathBuf}; use semver::Version; use tracing::*; use crate::common::{Config, Debugger, FailMode, Mode, PassMode}; use crate::debuggers::{extract_cdb_version, extract_gdb_version}; +use crate::errors::ErrorKind; +use crate::executor::{CollectedTestDesc, ShouldPanic}; use crate::header::auxiliary::{AuxProps, parse_and_update_aux}; use crate::header::needs::CachedNeedsConditions; use crate::util::static_regex; @@ -43,12 +45,12 @@ pub struct EarlyProps { } impl EarlyProps { - pub fn from_file(config: &Config, testfile: &Path) -> Self { - let file = File::open(testfile).expect("open test file to parse earlyprops"); + pub fn from_file(config: &Config, testfile: &Utf8Path) -> Self { + let file = File::open(testfile.as_std_path()).expect("open test file to parse earlyprops"); Self::from_reader(config, testfile, file) } - pub fn from_reader(config: &Config, testfile: &Path, rdr: R) -> Self { + pub fn from_reader(config: &Config, testfile: &Utf8Path, rdr: R) -> Self { let mut props = EarlyProps::default(); let mut poisoned = false; iter_header( @@ -64,7 +66,7 @@ impl EarlyProps { ); if poisoned { - eprintln!("errors encountered during EarlyProps parsing: {}", testfile.display()); + eprintln!("errors encountered during EarlyProps parsing: {}", testfile); panic!("errors encountered during EarlyProps parsing"); } @@ -86,7 +88,7 @@ pub struct TestProps { pub doc_flags: Vec, // If present, the name of a file that this test should match when // pretty-printed - pub pp_exact: Option, + pub pp_exact: Option, /// Auxiliary crates that should be built and made available to this test. pub(crate) aux: AuxProps, // Environment settings to use for compiling @@ -132,7 +134,7 @@ pub struct TestProps { // not set by end-users; rather it is set by the incremental // testing harness and used when generating compilation // arguments. (In particular, it propagates to the aux-builds.) - pub incremental_dir: Option, + pub incremental_dir: Option, // If `true`, this test will use incremental compilation. // // This can be set manually with the `incremental` header, or implicitly @@ -195,6 +197,8 @@ pub struct TestProps { /// Build and use `minicore` as `core` stub for `no_core` tests in cross-compilation scenarios /// that don't otherwise want/need `-Z build-std`. pub add_core_stubs: bool, + /// Whether line annotatins are required for the given error kind. + pub dont_require_annotations: HashSet, } mod directives { @@ -211,6 +215,7 @@ mod directives { pub const CHECK_RUN_RESULTS: &'static str = "check-run-results"; pub const DONT_CHECK_COMPILER_STDOUT: &'static str = "dont-check-compiler-stdout"; pub const DONT_CHECK_COMPILER_STDERR: &'static str = "dont-check-compiler-stderr"; + pub const DONT_REQUIRE_ANNOTATIONS: &'static str = "dont-require-annotations"; pub const NO_PREFER_DYNAMIC: &'static str = "no-prefer-dynamic"; pub const PRETTY_MODE: &'static str = "pretty-mode"; pub const PRETTY_COMPARE_ONLY: &'static str = "pretty-compare-only"; @@ -296,10 +301,16 @@ impl TestProps { no_auto_check_cfg: false, has_enzyme: false, add_core_stubs: false, + dont_require_annotations: Default::default(), } } - pub fn from_aux_file(&self, testfile: &Path, revision: Option<&str>, config: &Config) -> Self { + pub fn from_aux_file( + &self, + testfile: &Utf8Path, + revision: Option<&str>, + config: &Config, + ) -> Self { let mut props = TestProps::new(); // copy over select properties to the aux build: @@ -310,10 +321,10 @@ impl TestProps { props } - pub fn from_file(testfile: &Path, revision: Option<&str>, config: &Config) -> Self { + pub fn from_file(testfile: &Utf8Path, revision: Option<&str>, config: &Config) -> Self { let mut props = TestProps::new(); props.load_from(testfile, revision, config); - props.exec_env.push(("RUSTC".to_string(), config.rustc_path.display().to_string())); + props.exec_env.push(("RUSTC".to_string(), config.rustc_path.to_string())); match (props.pass_mode, props.fail_mode) { (None, None) if config.mode == Mode::Ui => props.fail_mode = Some(FailMode::Check), @@ -328,10 +339,10 @@ impl TestProps { /// tied to a particular revision `foo` (indicated by writing /// `//@[foo]`), then the property is ignored unless `test_revision` is /// `Some("foo")`. - fn load_from(&mut self, testfile: &Path, test_revision: Option<&str>, config: &Config) { + fn load_from(&mut self, testfile: &Utf8Path, test_revision: Option<&str>, config: &Config) { let mut has_edition = false; if !testfile.is_dir() { - let file = File::open(testfile).unwrap(); + let file = File::open(testfile.as_std_path()).unwrap(); let mut poisoned = false; @@ -377,14 +388,22 @@ impl TestProps { } if let Some(flags) = config.parse_name_value_directive(ln, COMPILE_FLAGS) { - self.compile_flags.extend(split_flags(&flags)); + let flags = split_flags(&flags); + for flag in &flags { + if flag == "--edition" || flag.starts_with("--edition=") { + panic!("you must use `//@ edition` to configure the edition"); + } + } + self.compile_flags.extend(flags); } if config.parse_name_value_directive(ln, INCORRECT_COMPILER_FLAGS).is_some() { panic!("`compiler-flags` directive should be spelled `compile-flags`"); } if let Some(edition) = config.parse_edition(ln) { - self.compile_flags.push(format!("--edition={}", edition.trim())); + // The edition is added at the start, since flags from //@compile-flags must + // be passed to rustc last. + self.compile_flags.insert(0, format!("--edition={}", edition.trim())); has_edition = true; } @@ -440,7 +459,7 @@ impl TestProps { ln, UNSET_EXEC_ENV, &mut self.unset_exec_env, - |r| r, + |r| r.trim().to_owned(), ); config.push_name_value_directive( ln, @@ -452,7 +471,7 @@ impl TestProps { ln, UNSET_RUSTC_ENV, &mut self.unset_rustc_env, - |r| r, + |r| r.trim().to_owned(), ); config.push_name_value_directive( ln, @@ -569,11 +588,18 @@ impl TestProps { config.set_name_directive(ln, NO_AUTO_CHECK_CFG, &mut self.no_auto_check_cfg); self.update_add_core_stubs(ln, config); + + if let Some(err_kind) = + config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS) + { + self.dont_require_annotations + .insert(ErrorKind::from_user_str(err_kind.trim())); + } }, ); if poisoned { - eprintln!("errors encountered during TestProps parsing: {}", testfile.display()); + eprintln!("errors encountered during TestProps parsing: {}", testfile); panic!("errors encountered during TestProps parsing"); } } @@ -605,7 +631,9 @@ impl TestProps { } if let (Some(edition), false) = (&config.edition, has_edition) { - self.compile_flags.push(format!("--edition={}", edition)); + // The edition is added at the start, since flags from //@compile-flags must be passed + // to rustc last. + self.compile_flags.insert(0, format!("--edition={}", edition)); } } @@ -709,11 +737,11 @@ impl TestProps { /// returns a struct containing various parts of the directive. fn line_directive<'line>( line_number: usize, - comment: &str, original_line: &'line str, ) -> Option> { // Ignore lines that don't start with the comment prefix. - let after_comment = original_line.trim_start().strip_prefix(comment)?.trim_start(); + let after_comment = + original_line.trim_start().strip_prefix(COMPILETEST_DIRECTIVE_PREFIX)?.trim_start(); let revision; let raw_directive; @@ -722,7 +750,7 @@ fn line_directive<'line>( // A comment like `//@[foo]` only applies to revision `foo`. let Some((line_revision, after_close_bracket)) = after_open_bracket.split_once(']') else { panic!( - "malformed condition directive: expected `{comment}[foo]`, found `{original_line}`" + "malformed condition directive: expected `{COMPILETEST_DIRECTIVE_PREFIX}[foo]`, found `{original_line}`" ) }; @@ -836,11 +864,13 @@ pub(crate) fn check_directive<'a>( CheckDirectiveResult { is_known_directive: is_known(&directive_name), trailing_directive } } +const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@"; + fn iter_header( mode: Mode, _suite: &str, poisoned: &mut bool, - testfile: &Path, + testfile: &Utf8Path, rdr: impl Read, it: &mut dyn FnMut(DirectiveLine<'_>), ) { @@ -849,8 +879,7 @@ fn iter_header( } // Coverage tests in coverage-run mode always have these extra directives, without needing to - // specify them manually in every test file. (Some of the comments below have been copied over - // from the old `tests/run-make/coverage-reports/Makefile`, which no longer exists.) + // specify them manually in every test file. // // FIXME(jieyouxu): I feel like there's a better way to do this, leaving for later. if mode == Mode::CoverageRun { @@ -867,9 +896,6 @@ fn iter_header( } } - // NOTE(jieyouxu): once we get rid of `Makefile`s we can unconditionally check for `//@`. - let comment = if testfile.extension().is_some_and(|e| e == "rs") { "//@" } else { "#" }; - let mut rdr = BufReader::with_capacity(1024, rdr); let mut ln = String::new(); let mut line_number = 0; @@ -882,7 +908,7 @@ fn iter_header( } let ln = ln.trim(); - let Some(directive_line) = line_directive(line_number, comment, ln) else { + let Some(directive_line) = line_directive(line_number, ln) else { continue; }; @@ -896,9 +922,7 @@ fn iter_header( eprintln!( "error: detected unknown compiletest test directive `{}` in {}:{}", - directive_line.raw_directive, - testfile.display(), - line_number, + directive_line.raw_directive, testfile, line_number, ); return; @@ -910,10 +934,7 @@ fn iter_header( eprintln!( "error: detected trailing compiletest test directive `{}` in {}:{}\n \ help: put the trailing directive in it's own line: `//@ {}`", - trailing_directive, - testfile.display(), - line_number, - trailing_directive, + trailing_directive, testfile, line_number, trailing_directive, ); return; @@ -925,58 +946,72 @@ fn iter_header( } impl Config { - fn parse_and_update_revisions(&self, testfile: &Path, line: &str, existing: &mut Vec) { - const FORBIDDEN_REVISION_NAMES: [&str; 9] = + fn parse_and_update_revisions( + &self, + testfile: &Utf8Path, + line: &str, + existing: &mut Vec, + ) { + const FORBIDDEN_REVISION_NAMES: [&str; 2] = [ + // `//@ revisions: true false` Implying `--cfg=true` and `--cfg=false` makes it very + // weird for the test, since if the test writer wants a cfg of the same revision name + // they'd have to use `cfg(r#true)` and `cfg(r#false)`. + "true", "false", + ]; + + const FILECHECK_FORBIDDEN_REVISION_NAMES: [&str; 9] = ["CHECK", "COM", "NEXT", "SAME", "EMPTY", "NOT", "COUNT", "DAG", "LABEL"]; if let Some(raw) = self.parse_name_value_directive(line, "revisions") { if self.mode == Mode::RunMake { - panic!("`run-make` tests do not support revisions: {}", testfile.display()); + panic!("`run-make` tests do not support revisions: {}", testfile); } let mut duplicates: HashSet<_> = existing.iter().cloned().collect(); - for revision in raw.split_whitespace().map(|r| r.to_string()) { - if !duplicates.insert(revision.clone()) { + for revision in raw.split_whitespace() { + if !duplicates.insert(revision.to_string()) { + panic!("duplicate revision: `{}` in line `{}`: {}", revision, raw, testfile); + } + + if FORBIDDEN_REVISION_NAMES.contains(&revision) { panic!( - "duplicate revision: `{}` in line `{}`: {}", - revision, - raw, - testfile.display() + "revision name `{revision}` is not permitted: `{}` in line `{}`: {}", + revision, raw, testfile ); - } else if matches!(self.mode, Mode::Assembly | Mode::Codegen | Mode::MirOpt) - && FORBIDDEN_REVISION_NAMES.contains(&revision.as_str()) + } + + if matches!(self.mode, Mode::Assembly | Mode::Codegen | Mode::MirOpt) + && FILECHECK_FORBIDDEN_REVISION_NAMES.contains(&revision) { panic!( - "revision name `{revision}` is not permitted in a test suite that uses `FileCheck` annotations\n\ - as it is confusing when used as custom `FileCheck` prefix: `{revision}` in line `{}`: {}", - raw, - testfile.display() + "revision name `{revision}` is not permitted in a test suite that uses \ + `FileCheck` annotations as it is confusing when used as custom `FileCheck` \ + prefix: `{revision}` in line `{}`: {}", + raw, testfile ); } - existing.push(revision); + + existing.push(revision.to_string()); } } } fn parse_env(nv: String) -> (String, String) { // nv is either FOO or FOO=BAR - let mut strs: Vec = nv.splitn(2, '=').map(str::to_owned).collect(); - - match strs.len() { - 1 => (strs.pop().unwrap(), String::new()), - 2 => { - let end = strs.pop().unwrap(); - (strs.pop().unwrap(), end) - } - n => panic!("Expected 1 or 2 strings, not {}", n), - } + // FIXME(Zalathar): The form without `=` seems to be unused; should + // we drop support for it? + let (name, value) = nv.split_once('=').unwrap_or((&nv, "")); + // Trim whitespace from the name, so that `//@ exec-env: FOO=BAR` + // sees the name as `FOO` and not ` FOO`. + let name = name.trim(); + (name.to_owned(), value.to_owned()) } - fn parse_pp_exact(&self, line: &str, testfile: &Path) -> Option { + fn parse_pp_exact(&self, line: &str, testfile: &Utf8Path) -> Option { if let Some(s) = self.parse_name_value_directive(line, "pp-exact") { - Some(PathBuf::from(&s)) + Some(Utf8PathBuf::from(&s)) } else if self.parse_name_directive(line, "pp-exact") { - testfile.file_name().map(PathBuf::from) + testfile.file_name().map(Utf8PathBuf::from) } else { None } @@ -1082,20 +1117,19 @@ fn expand_variables(mut value: String, config: &Config) -> String { if value.contains(CWD) { let cwd = env::current_dir().unwrap(); - value = value.replace(CWD, &cwd.to_string_lossy()); + value = value.replace(CWD, &cwd.to_str().unwrap()); } if value.contains(SRC_BASE) { - value = value.replace(SRC_BASE, &config.src_test_suite_root.to_str().unwrap()); + value = value.replace(SRC_BASE, &config.src_test_suite_root.as_str()); } if value.contains(TEST_SUITE_BUILD_BASE) { - value = - value.replace(TEST_SUITE_BUILD_BASE, &config.build_test_suite_root.to_str().unwrap()); + value = value.replace(TEST_SUITE_BUILD_BASE, &config.build_test_suite_root.as_str()); } if value.contains(SYSROOT_BASE) { - value = value.replace(SYSROOT_BASE, &config.sysroot_base.to_str().unwrap()); + value = value.replace(SYSROOT_BASE, &config.sysroot_base.as_str()); } if value.contains(TARGET_LINKER) { @@ -1108,9 +1142,9 @@ fn expand_variables(mut value: String, config: &Config) -> String { if value.contains(RUST_SRC_BASE) { let src_base = config.sysroot_base.join("lib/rustlib/src/rust"); - src_base.try_exists().expect(&*format!("{} should exists", src_base.display())); - let src_base = src_base.read_link().unwrap_or(src_base); - value = value.replace(RUST_SRC_BASE, &src_base.to_string_lossy()); + src_base.try_exists().expect(&*format!("{} should exists", src_base)); + let src_base = src_base.read_link_utf8().unwrap_or(src_base); + value = value.replace(RUST_SRC_BASE, &src_base.as_str()); } value @@ -1213,14 +1247,14 @@ pub fn llvm_has_libzstd(config: &Config) -> bool { // contains a path to that static lib, and that it exists. // // See compiler/rustc_llvm/build.rs for more details and similar expectations. - fn is_zstd_in_config(llvm_bin_dir: &Path) -> Option<()> { + fn is_zstd_in_config(llvm_bin_dir: &Utf8Path) -> Option<()> { let llvm_config_path = llvm_bin_dir.join("llvm-config"); let output = Command::new(llvm_config_path).arg("--system-libs").output().ok()?; assert!(output.status.success(), "running llvm-config --system-libs failed"); let libs = String::from_utf8(output.stdout).ok()?; for lib in libs.split_whitespace() { - if lib.ends_with("libzstd.a") && Path::new(lib).exists() { + if lib.ends_with("libzstd.a") && Utf8Path::new(lib).exists() { return Some(()); } } @@ -1238,7 +1272,7 @@ pub fn llvm_has_libzstd(config: &Config) -> bool { // `lld` supports it. If not, an error will be emitted: "LLVM was not built with // LLVM_ENABLE_ZSTD or did not find zstd at build time". #[cfg(unix)] - fn is_lld_built_with_zstd(llvm_bin_dir: &Path) -> Option<()> { + fn is_lld_built_with_zstd(llvm_bin_dir: &Utf8Path) -> Option<()> { let lld_path = llvm_bin_dir.join("lld"); if lld_path.exists() { // We can't call `lld` as-is, it expects to be invoked by a compiler driver using a @@ -1274,7 +1308,7 @@ pub fn llvm_has_libzstd(config: &Config) -> bool { } #[cfg(not(unix))] - fn is_lld_built_with_zstd(_llvm_bin_dir: &Path) -> Option<()> { + fn is_lld_built_with_zstd(_llvm_bin_dir: &Utf8Path) -> Option<()> { None } @@ -1337,15 +1371,15 @@ where Some((min, max)) } -pub fn make_test_description( +pub(crate) fn make_test_description( config: &Config, cache: &HeadersCache, - name: test::TestName, - path: &Path, + name: String, + path: &Utf8Path, src: R, test_revision: Option<&str>, poisoned: &mut bool, -) -> test::TestDesc { +) -> CollectedTestDesc { let mut ignore = false; let mut ignore_message = None; let mut should_fail = false; @@ -1369,13 +1403,10 @@ pub fn make_test_description( match $e { IgnoreDecision::Ignore { reason } => { ignore = true; - // The ignore reason must be a &'static str, so we have to leak memory to - // create it. This is fine, as the header is parsed only at the start of - // compiletest so it won't grow indefinitely. - ignore_message = Some(&*Box::leak(Box::::from(reason))); + ignore_message = Some(reason.into()); } IgnoreDecision::Error { message } => { - eprintln!("error: {}:{line_number}: {message}", path.display()); + eprintln!("error: {}:{line_number}: {message}", path); *poisoned = true; return; } @@ -1387,7 +1418,7 @@ pub fn make_test_description( decision!(cfg::handle_ignore(config, ln)); decision!(cfg::handle_only(config, ln)); decision!(needs::handle_needs(&cache.needs, config, ln)); - decision!(ignore_llvm(config, ln)); + decision!(ignore_llvm(config, path, ln)); decision!(ignore_cdb(config, ln)); decision!(ignore_gdb(config, ln)); decision!(ignore_lldb(config, ln)); @@ -1405,7 +1436,7 @@ pub fn make_test_description( ); if local_poisoned { - eprintln!("errors encountered when trying to make test description: {}", path.display()); + eprintln!("errors encountered when trying to make test description: {}", path); panic!("errors encountered when trying to make test description"); } @@ -1413,25 +1444,12 @@ pub fn make_test_description( // since we run the pretty printer across all tests by default. // If desired, we could add a `should-fail-pretty` annotation. let should_panic = match config.mode { - crate::common::Pretty => test::ShouldPanic::No, - _ if should_fail => test::ShouldPanic::Yes, - _ => test::ShouldPanic::No, + crate::common::Pretty => ShouldPanic::No, + _ if should_fail => ShouldPanic::Yes, + _ => ShouldPanic::No, }; - test::TestDesc { - name, - ignore, - ignore_message, - source_file: "", - start_line: 0, - start_col: 0, - end_line: 0, - end_col: 0, - should_panic, - compile_fail: false, - no_run: false, - test_type: test::TestType::Unknown, - } + CollectedTestDesc { name, ignore, ignore_message, should_panic } } fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision { @@ -1527,7 +1545,7 @@ fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision { IgnoreDecision::Continue } -fn ignore_llvm(config: &Config, line: &str) -> IgnoreDecision { +fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision { if let Some(needed_components) = config.parse_name_value_directive(line, "needs-llvm-components") { @@ -1538,8 +1556,8 @@ fn ignore_llvm(config: &Config, line: &str) -> IgnoreDecision { { if env::var_os("COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS").is_some() { panic!( - "missing LLVM component {}, and COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS is set", - missing_component + "missing LLVM component {}, and COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS is set: {}", + missing_component, path ); } return IgnoreDecision::Ignore { diff --git a/src/tools/compiletest/src/header/cfg.rs b/src/tools/compiletest/src/header/cfg.rs index 72a3b9d85c8cf..f1f1384afb971 100644 --- a/src/tools/compiletest/src/header/cfg.rs +++ b/src/tools/compiletest/src/header/cfg.rs @@ -100,6 +100,10 @@ fn parse_cfg_name_directive<'a>( name: "test", message: "always" } + condition! { + name: "auxiliary", + message: "used by another main test file" + } condition! { name: &config.target, allowed_names: &target_cfgs.all_targets, @@ -166,6 +170,16 @@ fn parse_cfg_name_directive<'a>( message: "when the target vendor is Apple" } + condition! { + name: "elf", + condition: !config.target.contains("windows") + && !config.target.contains("wasm") + && !config.target.contains("apple") + && !config.target.contains("aix") + && !config.target.contains("uefi"), + message: "when the target binary format is ELF" + } + condition! { name: "enzyme", condition: config.has_enzyme, diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs index 12f0790fb1040..2ace40c490bf3 100644 --- a/src/tools/compiletest/src/header/needs.rs +++ b/src/tools/compiletest/src/header/needs.rs @@ -1,4 +1,4 @@ -use crate::common::{Config, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer}; +use crate::common::{Config, KNOWN_CRATE_TYPES, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer}; use crate::header::{IgnoreDecision, llvm_has_libzstd}; pub(super) fn handle_needs( @@ -6,7 +6,7 @@ pub(super) fn handle_needs( config: &Config, ln: &str, ) -> IgnoreDecision { - // Note thet we intentionally still put the needs- prefix here to make the file show up when + // Note that we intentionally still put the needs- prefix here to make the file show up when // grepping for a directive name, even though we could technically strip that. let needs = &[ Need { @@ -224,6 +224,50 @@ pub(super) fn handle_needs( } } + // FIXME(jieyouxu): share multi-value directive logic with `needs-target-has-atomic` above. + if name == "needs-crate-type" { + let Some(rest) = rest else { + return IgnoreDecision::Error { + message: + "expected `needs-crate-type` to have a comma-separated list of crate types" + .to_string(), + }; + }; + + // Expect directive value to be a list of comma-separated crate-types. + let specified_crate_types = rest + .split(',') + .map(|crate_type| crate_type.trim()) + .map(ToString::to_string) + .collect::>(); + + for crate_type in &specified_crate_types { + if !KNOWN_CRATE_TYPES.contains(&crate_type.as_str()) { + return IgnoreDecision::Error { + message: format!( + "unknown crate type specified in `needs-crate-type`: `{crate_type}` is not \ + a known crate type, known values are `{:?}`", + KNOWN_CRATE_TYPES + ), + }; + } + } + + let satisfies_all_crate_types = specified_crate_types + .iter() + .all(|specified| config.supported_crate_types().contains(specified)); + if satisfies_all_crate_types { + return IgnoreDecision::Continue; + } else { + return IgnoreDecision::Ignore { + reason: format!( + "skipping test as target does not support all of the crate types `{:?}`", + specified_crate_types + ), + }; + } + } + if !name.starts_with("needs-") { return IgnoreDecision::Continue; } diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index d7079fdeee6c3..e7e5ff0ab0093 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -1,6 +1,6 @@ use std::io::Read; -use std::path::Path; +use camino::Utf8Path; use semver::Version; use super::{ @@ -8,14 +8,15 @@ use super::{ parse_normalize_rule, }; use crate::common::{Config, Debugger, Mode}; +use crate::executor::{CollectedTestDesc, ShouldPanic}; fn make_test_description( config: &Config, - name: test::TestName, - path: &Path, + name: String, + path: &Utf8Path, src: R, revision: Option<&str>, -) -> test::TestDesc { +) -> CollectedTestDesc { let cache = HeadersCache::load(config); let mut poisoned = false; let test = crate::header::make_test_description( @@ -174,7 +175,6 @@ impl ConfigBuilder { self.host.as_deref().unwrap_or("x86_64-unknown-linux-gnu"), "--target", self.target.as_deref().unwrap_or("x86_64-unknown-linux-gnu"), - "--git-repository=", "--nightly-branch=", "--git-merge-commit-email=", "--minicore-path=", @@ -229,31 +229,26 @@ fn cfg() -> ConfigBuilder { fn parse_rs(config: &Config, contents: &str) -> EarlyProps { let bytes = contents.as_bytes(); - EarlyProps::from_reader(config, Path::new("a.rs"), bytes) + EarlyProps::from_reader(config, Utf8Path::new("a.rs"), bytes) } fn check_ignore(config: &Config, contents: &str) -> bool { - let tn = test::DynTestName(String::new()); - let p = Path::new("a.rs"); + let tn = String::new(); + let p = Utf8Path::new("a.rs"); let d = make_test_description(&config, tn, p, std::io::Cursor::new(contents), None); d.ignore } -fn parse_makefile(config: &Config, contents: &str) -> EarlyProps { - let bytes = contents.as_bytes(); - EarlyProps::from_reader(config, Path::new("Makefile"), bytes) -} - #[test] fn should_fail() { let config: Config = cfg().build(); - let tn = test::DynTestName(String::new()); - let p = Path::new("a.rs"); + let tn = String::new(); + let p = Utf8Path::new("a.rs"); let d = make_test_description(&config, tn.clone(), p, std::io::Cursor::new(""), None); - assert_eq!(d.should_panic, test::ShouldPanic::No); + assert_eq!(d.should_panic, ShouldPanic::No); let d = make_test_description(&config, tn, p, std::io::Cursor::new("//@ should-fail"), None); - assert_eq!(d.should_panic, test::ShouldPanic::Yes); + assert_eq!(d.should_panic, ShouldPanic::Yes); } #[test] @@ -261,10 +256,6 @@ fn revisions() { let config: Config = cfg().build(); assert_eq!(parse_rs(&config, "//@ revisions: a b c").revisions, vec!["a", "b", "c"],); - assert_eq!( - parse_makefile(&config, "# revisions: hello there").revisions, - vec!["hello", "there"], - ); } #[test] @@ -467,9 +458,6 @@ fn profiler_runtime() { #[test] fn asm_support() { let asms = [ - #[cfg(bootstrap)] - ("avr-unknown-gnu-atmega328", false), - #[cfg(not(bootstrap))] ("avr-none", false), ("i686-unknown-netbsd", true), ("riscv32gc-unknown-linux-gnu", true), @@ -576,6 +564,13 @@ fn test_assembly_mode_forbidden_revisions() { parse_rs(&config, "//@ revisions: CHECK"); } +#[test] +#[should_panic(expected = "revision name `true` is not permitted")] +fn test_forbidden_revisions() { + let config = cfg().mode("ui").build(); + parse_rs(&config, "//@ revisions: true"); +} + #[test] #[should_panic( expected = "revision name `CHECK` is not permitted in a test suite that uses `FileCheck` annotations" @@ -788,7 +783,7 @@ fn threads_support() { } } -fn run_path(poisoned: &mut bool, path: &Path, buf: &[u8]) { +fn run_path(poisoned: &mut bool, path: &Utf8Path, buf: &[u8]) { let rdr = std::io::Cursor::new(&buf); iter_header(Mode::Ui, "ui", poisoned, path, rdr, &mut |_| {}); } @@ -798,7 +793,7 @@ fn test_unknown_directive_check() { let mut poisoned = false; run_path( &mut poisoned, - Path::new("a.rs"), + Utf8Path::new("a.rs"), include_bytes!("./test-auxillary/unknown_directive.rs"), ); assert!(poisoned); @@ -809,7 +804,7 @@ fn test_known_directive_check_no_error() { let mut poisoned = false; run_path( &mut poisoned, - Path::new("a.rs"), + Utf8Path::new("a.rs"), include_bytes!("./test-auxillary/known_directive.rs"), ); assert!(!poisoned); @@ -820,7 +815,7 @@ fn test_error_annotation_no_error() { let mut poisoned = false; run_path( &mut poisoned, - Path::new("a.rs"), + Utf8Path::new("a.rs"), include_bytes!("./test-auxillary/error_annotation.rs"), ); assert!(!poisoned); @@ -831,7 +826,7 @@ fn test_non_rs_unknown_directive_not_checked() { let mut poisoned = false; run_path( &mut poisoned, - Path::new("a.Makefile"), + Utf8Path::new("a.Makefile"), include_bytes!("./test-auxillary/not_rs.Makefile"), ); assert!(!poisoned); @@ -840,21 +835,21 @@ fn test_non_rs_unknown_directive_not_checked() { #[test] fn test_trailing_directive() { let mut poisoned = false; - run_path(&mut poisoned, Path::new("a.rs"), b"//@ only-x86 only-arm"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ only-x86 only-arm"); assert!(poisoned); } #[test] fn test_trailing_directive_with_comment() { let mut poisoned = false; - run_path(&mut poisoned, Path::new("a.rs"), b"//@ only-x86 only-arm with comment"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ only-x86 only-arm with comment"); assert!(poisoned); } #[test] fn test_not_trailing_directive() { let mut poisoned = false; - run_path(&mut poisoned, Path::new("a.rs"), b"//@ revisions: incremental"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ revisions: incremental"); assert!(!poisoned); } @@ -906,3 +901,47 @@ fn test_rustc_abi() { assert!(!check_ignore(&config, "//@ ignore-rustc_abi-x86-sse2")); assert!(check_ignore(&config, "//@ only-rustc_abi-x86-sse2")); } + +#[test] +fn test_supported_crate_types() { + // Basic assumptions check on under-test compiler's `--print=supported-crate-types` output based + // on knowledge about the cherry-picked `x86_64-unknown-linux-gnu` and `wasm32-unknown-unknown` + // targets. Also smoke tests the `needs-crate-type` directive itself. + + use std::collections::HashSet; + + let config = cfg().target("x86_64-unknown-linux-gnu").build(); + assert_eq!( + config.supported_crate_types().iter().map(String::as_str).collect::>(), + HashSet::from(["bin", "cdylib", "dylib", "lib", "proc-macro", "rlib", "staticlib"]), + ); + assert!(!check_ignore(&config, "//@ needs-crate-type: rlib")); + assert!(!check_ignore(&config, "//@ needs-crate-type: dylib")); + assert!(!check_ignore( + &config, + "//@ needs-crate-type: bin, cdylib, dylib, lib, proc-macro, rlib, staticlib" + )); + + let config = cfg().target("wasm32-unknown-unknown").build(); + assert_eq!( + config.supported_crate_types().iter().map(String::as_str).collect::>(), + HashSet::from(["bin", "cdylib", "lib", "rlib", "staticlib"]), + ); + + // rlib is supported + assert!(!check_ignore(&config, "//@ needs-crate-type: rlib")); + // dylib is not + assert!(check_ignore(&config, "//@ needs-crate-type: dylib")); + // If multiple crate types are specified, then all specified crate types need to be supported. + assert!(check_ignore(&config, "//@ needs-crate-type: cdylib, dylib")); + assert!(check_ignore( + &config, + "//@ needs-crate-type: bin, cdylib, dylib, lib, proc-macro, rlib, staticlib" + )); +} + +#[test] +fn test_ignore_auxiliary() { + let config = cfg().build(); + assert!(check_ignore(&config, "//@ ignore-auxiliary")); +} diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs index 0da93dcafa20b..6ed2b52c66d21 100644 --- a/src/tools/compiletest/src/json.rs +++ b/src/tools/compiletest/src/json.rs @@ -1,12 +1,12 @@ //! These structs are a subset of the ones found in `rustc_errors::json`. use std::path::{Path, PathBuf}; -use std::str::FromStr; +use std::sync::OnceLock; +use regex::Regex; use serde::Deserialize; use crate::errors::{Error, ErrorKind}; -use crate::runtest::ProcRes; #[derive(Deserialize)] struct Diagnostic { @@ -139,44 +139,26 @@ pub fn extract_rendered(output: &str) -> String { .collect() } -pub fn parse_output(file_name: &str, output: &str, proc_res: &ProcRes) -> Vec { - output.lines().flat_map(|line| parse_line(file_name, line, output, proc_res)).collect() -} - -fn parse_line(file_name: &str, line: &str, output: &str, proc_res: &ProcRes) -> Vec { - // The compiler sometimes intermingles non-JSON stuff into the - // output. This hack just skips over such lines. Yuck. - if line.starts_with('{') { +pub fn parse_output(file_name: &str, output: &str) -> Vec { + let mut errors = Vec::new(); + for line in output.lines() { + // Compiler can emit non-json lines in non-`--error-format=json` modes, + // and in some situations even in json mode. match serde_json::from_str::(line) { - Ok(diagnostic) => { - let mut expected_errors = vec![]; - push_expected_errors(&mut expected_errors, &diagnostic, &[], file_name); - expected_errors - } - Err(error) => { - // Ignore the future compat report message - this is handled - // by `extract_rendered` - if serde_json::from_str::(line).is_ok() { - vec![] - } else { - proc_res.fatal( - Some(&format!( - "failed to decode compiler output as json: \ - `{}`\nline: {}\noutput: {}", - error, line, output - )), - || (), - ); - } - } + Ok(diagnostic) => push_actual_errors(&mut errors, &diagnostic, &[], file_name), + Err(_) => errors.push(Error { + line_num: None, + kind: ErrorKind::Raw, + msg: line.to_string(), + require_annotation: false, + }), } - } else { - vec![] } + errors } -fn push_expected_errors( - expected_errors: &mut Vec, +fn push_actual_errors( + errors: &mut Vec, diagnostic: &Diagnostic, default_spans: &[&DiagnosticSpan], file_name: &str, @@ -189,8 +171,6 @@ fn push_expected_errors( .filter(|(_, span)| Path::new(&span.file_name) == Path::new(&file_name)) .collect(); - let spans_in_this_file: Vec<_> = spans_info_in_this_file.iter().map(|(_, span)| span).collect(); - let primary_spans: Vec<_> = spans_info_in_this_file .iter() .filter(|(is_primary, _)| *is_primary) @@ -213,57 +193,70 @@ fn push_expected_errors( // also ensure that `//~ ERROR E123` *always* works. The // assumption is that these multi-line error messages are on their // way out anyhow. - let with_code = |span: &DiagnosticSpan, text: &str| { - match diagnostic.code { - Some(ref code) => - // FIXME(#33000) -- it'd be better to use a dedicated - // UI harness than to include the line/col number like - // this, but some current tests rely on it. - // - // Note: Do NOT include the filename. These can easily - // cause false matches where the expected message - // appears in the filename, and hence the message - // changes but the test still passes. - { - format!( - "{}:{}: {}:{}: {} [{}]", - span.line_start, - span.column_start, - span.line_end, - span.column_end, - text, - code.code.clone() - ) - } - None => - // FIXME(#33000) -- it'd be better to use a dedicated UI harness - { - format!( - "{}:{}: {}:{}: {}", - span.line_start, span.column_start, span.line_end, span.column_end, text - ) + let with_code = |span: Option<&DiagnosticSpan>, text: &str| { + // FIXME(#33000) -- it'd be better to use a dedicated + // UI harness than to include the line/col number like + // this, but some current tests rely on it. + // + // Note: Do NOT include the filename. These can easily + // cause false matches where the expected message + // appears in the filename, and hence the message + // changes but the test still passes. + let span_str = match span { + Some(DiagnosticSpan { line_start, column_start, line_end, column_end, .. }) => { + format!("{line_start}:{column_start}: {line_end}:{column_end}") } + None => format!("?:?: ?:?"), + }; + match &diagnostic.code { + Some(code) => format!("{span_str}: {text} [{}]", code.code), + None => format!("{span_str}: {text}"), } }; - // Convert multi-line messages into multiple expected - // errors. We expect to replace these with something - // more structured shortly anyhow. + // Convert multi-line messages into multiple errors. + // We expect to replace these with something more structured anyhow. let mut message_lines = diagnostic.message.lines(); - if let Some(first_line) = message_lines.next() { + let kind = ErrorKind::from_compiler_str(&diagnostic.level); + let first_line = message_lines.next().unwrap_or(&diagnostic.message); + if primary_spans.is_empty() { + static RE: OnceLock = OnceLock::new(); + let re_init = + || Regex::new(r"aborting due to \d+ previous errors?|\d+ warnings? emitted").unwrap(); + errors.push(Error { + line_num: None, + kind, + msg: with_code(None, first_line), + require_annotation: diagnostic.level != "failure-note" + && !RE.get_or_init(re_init).is_match(first_line), + }); + } else { for span in primary_spans { - let msg = with_code(span, first_line); - let kind = ErrorKind::from_str(&diagnostic.level).ok(); - expected_errors.push(Error { line_num: span.line_start, kind, msg }); + errors.push(Error { + line_num: Some(span.line_start), + kind, + msg: with_code(Some(span), first_line), + require_annotation: true, + }); } } for next_line in message_lines { - for span in primary_spans { - expected_errors.push(Error { - line_num: span.line_start, - kind: None, - msg: with_code(span, next_line), + if primary_spans.is_empty() { + errors.push(Error { + line_num: None, + kind, + msg: with_code(None, next_line), + require_annotation: false, }); + } else { + for span in primary_spans { + errors.push(Error { + line_num: Some(span.line_start), + kind, + msg: with_code(Some(span), next_line), + require_annotation: false, + }); + } } } @@ -271,10 +264,13 @@ fn push_expected_errors( for span in primary_spans { if let Some(ref suggested_replacement) = span.suggested_replacement { for (index, line) in suggested_replacement.lines().enumerate() { - expected_errors.push(Error { - line_num: span.line_start + index, - kind: Some(ErrorKind::Suggestion), + errors.push(Error { + line_num: Some(span.line_start + index), + kind: ErrorKind::Suggestion, msg: line.to_string(), + // Empty suggestions (suggestions to remove something) are common + // and annotating them in source is not useful. + require_annotation: !line.is_empty(), }); } } @@ -283,39 +279,44 @@ fn push_expected_errors( // Add notes for the backtrace for span in primary_spans { if let Some(frame) = &span.expansion { - push_backtrace(expected_errors, frame, file_name); + push_backtrace(errors, frame, file_name); } } // Add notes for any labels that appear in the message. - for span in spans_in_this_file.iter().filter(|span| span.label.is_some()) { - expected_errors.push(Error { - line_num: span.line_start, - kind: Some(ErrorKind::Note), - msg: span.label.clone().unwrap(), - }); + for (_, span) in spans_info_in_this_file { + if let Some(label) = &span.label { + errors.push(Error { + line_num: Some(span.line_start), + kind: ErrorKind::Note, + msg: label.clone(), + // Empty labels (only underlining spans) are common and do not need annotations. + require_annotation: !label.is_empty(), + }); + } } // Flatten out the children. for child in &diagnostic.children { - push_expected_errors(expected_errors, child, primary_spans, file_name); + push_actual_errors(errors, child, primary_spans, file_name); } } fn push_backtrace( - expected_errors: &mut Vec, + errors: &mut Vec, expansion: &DiagnosticSpanMacroExpansion, file_name: &str, ) { if Path::new(&expansion.span.file_name) == Path::new(&file_name) { - expected_errors.push(Error { - line_num: expansion.span.line_start, - kind: Some(ErrorKind::Note), + errors.push(Error { + line_num: Some(expansion.span.line_start), + kind: ErrorKind::Note, msg: format!("in this expansion of {}", expansion.macro_decl_name), + require_annotation: true, }); } if let Some(previous_expansion) = &expansion.span.expansion { - push_backtrace(expected_errors, previous_expansion, file_name); + push_backtrace(errors, previous_expansion, file_name); } } diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 9dff7047bc4ab..0db4d3f6a4100 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -1,9 +1,9 @@ #![crate_name = "compiletest"] -// The `test` crate is the only unstable feature -// allowed here, just to share similar code. -#![feature(test)] - -extern crate test; +// Needed by the "new" test executor that does not depend on libtest. +// FIXME(Zalathar): We should be able to get rid of `internal_output_capture`, +// by having `runtest` manually capture all of its println-like output instead. +// That would result in compiletest being written entirely in stable Rust! +#![feature(internal_output_capture)] #[cfg(test)] mod tests; @@ -12,6 +12,7 @@ pub mod common; pub mod compute_diff; mod debuggers; pub mod errors; +mod executor; pub mod header; mod json; mod raise_fd_limit; @@ -21,17 +22,17 @@ pub mod util; use core::panic; use std::collections::HashSet; -use std::ffi::{OsStr, OsString}; +use std::fmt::Write; use std::io::{self, ErrorKind}; -use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::sync::{Arc, OnceLock}; use std::time::SystemTime; use std::{env, fs, vec}; use build_helper::git::{get_git_modified_files, get_git_untracked_files}; +use camino::{Utf8Path, Utf8PathBuf}; use getopts::Options; -use test::ColorConfig; +use rayon::iter::{ParallelBridge, ParallelIterator}; use tracing::*; use walkdir::WalkDir; @@ -40,6 +41,7 @@ use crate::common::{ CompareMode, Config, Debugger, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path, output_base_dir, output_relative_path, }; +use crate::executor::{CollectedTest, ColorConfig, OutputFormat}; use crate::header::HeadersCache; use crate::util::logv; @@ -49,11 +51,23 @@ use crate::util::logv; /// some code here that inspects environment variables or even runs executables /// (e.g. when discovering debugger versions). pub fn parse_config(args: Vec) -> Config { + if env::var("RUST_TEST_NOCAPTURE").is_ok() { + eprintln!( + "WARNING: RUST_TEST_NOCAPTURE is not supported. Use the `--no-capture` flag instead." + ); + } + let mut opts = Options::new(); opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH") .reqopt("", "run-lib-path", "path to target shared libraries", "PATH") .reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH") .optopt("", "cargo-path", "path to cargo to use for compiling", "PATH") + .optopt( + "", + "stage0-rustc-path", + "path to rustc to use for compiling run-make recipes", + "PATH", + ) .optopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH") .optopt("", "coverage-dump-path", "path to coverage-dump to use in tests", "PATH") .reqopt("", "python", "path to python to use for doc tests", "PATH") @@ -121,10 +135,10 @@ pub fn parse_config(args: Vec) -> Config { "bless", "overwrite stderr/stdout files instead of complaining about a mismatch", ) + .optflag("", "fail-fast", "stop as soon as possible after any test fails") .optflag("", "quiet", "print one character per test instead of one line") .optopt("", "color", "coloring: auto, always, never", "WHEN") .optflag("", "json", "emit json output instead of plaintext output") - .optopt("", "logfile", "file to log test execution to", "FILE") .optopt("", "target", "the target to build for", "TARGET") .optopt("", "host", "the host to build for", "HOST") .optopt("", "cdb", "path to CDB to use for CDB debuginfo tests", "PATH") @@ -174,7 +188,6 @@ pub fn parse_config(args: Vec) -> Config { "run tests which rely on commit version being compiled into the binaries", ) .optopt("", "edition", "default Rust edition", "EDITION") - .reqopt("", "git-repository", "name of the git repository", "ORG/REPO") .reqopt("", "nightly-branch", "name of the git branch for nightly", "BRANCH") .reqopt( "", @@ -189,6 +202,7 @@ pub fn parse_config(args: Vec) -> Config { "COMMAND", ) .reqopt("", "minicore-path", "path to minicore aux library", "PATH") + .optflag("N", "no-new-executor", "disables the new test executor, and uses libtest instead") .optopt( "", "debugger", @@ -216,15 +230,19 @@ pub fn parse_config(args: Vec) -> Config { panic!() } - fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf { - match m.opt_str(nm) { - Some(s) => PathBuf::from(&s), - None => panic!("no option (=path) found for {}", nm), + fn make_absolute(path: Utf8PathBuf) -> Utf8PathBuf { + if path.is_relative() { + Utf8PathBuf::try_from(env::current_dir().unwrap()).unwrap().join(path) + } else { + path } } - fn make_absolute(path: PathBuf) -> PathBuf { - if path.is_relative() { env::current_dir().unwrap().join(path) } else { path } + fn opt_path(m: &getopts::Matches, nm: &str) -> Utf8PathBuf { + match m.opt_str(nm) { + Some(s) => Utf8PathBuf::from(&s), + None => panic!("no option (=path) found for {}", nm), + } } let target = opt_str2(matches.opt_str("target")); @@ -265,16 +283,12 @@ pub fn parse_config(args: Vec) -> Config { .free .iter() .map(|f| { - let path = Path::new(f); + let path = Utf8Path::new(f); let mut iter = path.iter().skip(1); - // We skip the test folder and check if the user passed `rmake.rs` or `Makefile`. - if iter - .next() - .is_some_and(|s| s == OsStr::new("rmake.rs") || s == OsStr::new("Makefile")) - && iter.next().is_none() - { - path.parent().unwrap().to_str().unwrap().to_string() + // We skip the test folder and check if the user passed `rmake.rs`. + if iter.next().is_some_and(|s| s == "rmake.rs") && iter.next().is_none() { + path.parent().unwrap().to_string() } else { f.to_string() } @@ -306,8 +320,8 @@ pub fn parse_config(args: Vec) -> Config { assert!( src_test_suite_root.starts_with(&src_root), "`src-root` must be a parent of `src-test-suite-root`: `src-root`=`{}`, `src-test-suite-root` = `{}`", - src_root.display(), - src_test_suite_root.display() + src_root, + src_test_suite_root ); let build_root = opt_path(matches, "build-root"); @@ -316,18 +330,22 @@ pub fn parse_config(args: Vec) -> Config { Config { bless: matches.opt_present("bless"), + fail_fast: matches.opt_present("fail-fast") + || env::var_os("RUSTC_TEST_FAIL_FAST").is_some(), + compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), run_lib_path: make_absolute(opt_path(matches, "run-lib-path")), rustc_path: opt_path(matches, "rustc-path"), - cargo_path: matches.opt_str("cargo-path").map(PathBuf::from), - rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from), - coverage_dump_path: matches.opt_str("coverage-dump-path").map(PathBuf::from), + cargo_path: matches.opt_str("cargo-path").map(Utf8PathBuf::from), + stage0_rustc_path: matches.opt_str("stage0-rustc-path").map(Utf8PathBuf::from), + rustdoc_path: matches.opt_str("rustdoc-path").map(Utf8PathBuf::from), + coverage_dump_path: matches.opt_str("coverage-dump-path").map(Utf8PathBuf::from), python: matches.opt_str("python").unwrap(), jsondocck_path: matches.opt_str("jsondocck-path"), jsondoclint_path: matches.opt_str("jsondoclint-path"), run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"), - llvm_filecheck: matches.opt_str("llvm-filecheck").map(PathBuf::from), - llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(PathBuf::from), + llvm_filecheck: matches.opt_str("llvm-filecheck").map(Utf8PathBuf::from), + llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(Utf8PathBuf::from), src_root, src_test_suite_root, @@ -363,7 +381,6 @@ pub fn parse_config(args: Vec) -> Config { "never" => Some(false), _ => panic!("unknown `--run` option `{}` given", mode), }), - logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)), runner: matches.opt_str("runner"), host_rustcflags: matches.opt_strs("host-rustcflags"), target_rustcflags: matches.opt_strs("target-rustcflags"), @@ -388,13 +405,13 @@ pub fn parse_config(args: Vec) -> Config { verbose: matches.opt_present("verbose"), format: match (matches.opt_present("quiet"), matches.opt_present("json")) { (true, true) => panic!("--quiet and --json are incompatible"), - (true, false) => test::OutputFormat::Terse, - (false, true) => test::OutputFormat::Json, - (false, false) => test::OutputFormat::Pretty, + (true, false) => OutputFormat::Terse, + (false, true) => OutputFormat::Json, + (false, false) => OutputFormat::Pretty, }, only_modified: matches.opt_present("only-modified"), color, - remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from), + remote_test_client: matches.opt_str("remote-test-client").map(Utf8PathBuf::from), compare_mode, rustfix_coverage: matches.opt_present("rustfix-coverage"), has_html_tidy, @@ -418,10 +435,10 @@ pub fn parse_config(args: Vec) -> Config { target_cfgs: OnceLock::new(), builtin_cfg_names: OnceLock::new(), + supported_crate_types: OnceLock::new(), nocapture: matches.opt_present("no-capture"), - git_repository: matches.opt_str("git-repository").unwrap(), nightly_branch: matches.opt_str("nightly-branch").unwrap(), git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(), @@ -436,19 +453,19 @@ pub fn parse_config(args: Vec) -> Config { pub fn log_config(config: &Config) { let c = config; logv(c, "configuration:".to_string()); - logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path)); - logv(c, format!("run_lib_path: {:?}", config.run_lib_path)); - logv(c, format!("rustc_path: {:?}", config.rustc_path.display())); + logv(c, format!("compile_lib_path: {}", config.compile_lib_path)); + logv(c, format!("run_lib_path: {}", config.run_lib_path)); + logv(c, format!("rustc_path: {}", config.rustc_path)); logv(c, format!("cargo_path: {:?}", config.cargo_path)); logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path)); - logv(c, format!("src_root: {}", config.src_root.display())); - logv(c, format!("src_test_suite_root: {}", config.src_test_suite_root.display())); + logv(c, format!("src_root: {}", config.src_root)); + logv(c, format!("src_test_suite_root: {}", config.src_test_suite_root)); - logv(c, format!("build_root: {}", config.build_root.display())); - logv(c, format!("build_test_suite_root: {}", config.build_test_suite_root.display())); + logv(c, format!("build_root: {}", config.build_root)); + logv(c, format!("build_test_suite_root: {}", config.build_test_suite_root)); - logv(c, format!("sysroot_base: {}", config.sysroot_base.display())); + logv(c, format!("sysroot_base: {}", config.sysroot_base)); logv(c, format!("stage: {}", config.stage)); logv(c, format!("stage_id: {}", config.stage_id)); @@ -466,16 +483,16 @@ pub fn log_config(config: &Config) { logv(c, format!("target-rustcflags: {:?}", config.target_rustcflags)); logv(c, format!("target: {}", config.target)); logv(c, format!("host: {}", config.host)); - logv(c, format!("android-cross-path: {:?}", config.android_cross_path.display())); - logv(c, format!("adb_path: {:?}", config.adb_path)); - logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir)); + logv(c, format!("android-cross-path: {}", config.android_cross_path)); + logv(c, format!("adb_path: {}", config.adb_path)); + logv(c, format!("adb_test_dir: {}", config.adb_test_dir)); logv(c, format!("adb_device_status: {}", config.adb_device_status)); logv(c, format!("ar: {}", config.ar)); logv(c, format!("target-linker: {:?}", config.target_linker)); logv(c, format!("host-linker: {:?}", config.host_linker)); logv(c, format!("verbose: {}", config.verbose)); logv(c, format!("format: {:?}", config.format)); - logv(c, format!("minicore_path: {:?}", config.minicore_path.display())); + logv(c, format!("minicore_path: {}", config.minicore_path)); logv(c, "\n".to_string()); } @@ -503,7 +520,7 @@ pub fn run_tests(config: Arc) { coverage_file_path.push("rustfix_missing_coverage.txt"); if coverage_file_path.exists() { if let Err(e) = fs::remove_file(&coverage_file_path) { - panic!("Could not delete {} due to {}", coverage_file_path.display(), e) + panic!("Could not delete {} due to {}", coverage_file_path, e) } } } @@ -516,12 +533,14 @@ pub fn run_tests(config: Arc) { } // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary - env::set_var("__COMPAT_LAYER", "RunAsInvoker"); - - // Let tests know which target they're running as - env::set_var("TARGET", &config.target); + // + // SAFETY: at this point we're still single-threaded. + unsafe { env::set_var("__COMPAT_LAYER", "RunAsInvoker") }; - let opts = test_opts(&config); + // Let tests know which target they're running as. + // + // SAFETY: at this point we're still single-threaded. + unsafe { env::set_var("TARGET", &config.target) }; let mut configs = Vec::new(); if let Mode::DebugInfo = config.mode { @@ -549,12 +568,15 @@ pub fn run_tests(config: Arc) { tests.extend(collect_and_make_tests(c)); } - tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice())); + tests.sort_by(|a, b| Ord::cmp(&a.desc.name, &b.desc.name)); - // Delegate to libtest to filter and run the big list of structures created - // during test discovery. When libtest decides to run a test, it will invoke - // the corresponding closure created by `make_test_closure`. - let res = test::run_tests_console(&opts, tests); + // Delegate to the executor to filter and run the big list of test structures + // created during test discovery. When the executor decides to run a test, + // it will return control to the rest of compiletest by calling `runtest::run`. + // FIXME(Zalathar): Once we're confident that we won't need to revert the + // removal of the libtest-based executor, remove this Result and other + // remnants of the old executor. + let res: io::Result = Ok(executor::run_tests(&config, tests)); // Check the outcome reported by libtest. match res { @@ -567,18 +589,22 @@ pub fn run_tests(config: Arc) { // easy to miss which tests failed, and as such fail to reproduce // the failure locally. - println!( - "Some tests failed in compiletest suite={}{} mode={} host={} target={}", - config.suite, - config - .compare_mode - .as_ref() - .map(|c| format!(" compare_mode={:?}", c)) - .unwrap_or_default(), - config.mode, - config.host, - config.target - ); + let mut msg = String::from("Some tests failed in compiletest"); + write!(msg, " suite={}", config.suite).unwrap(); + + if let Some(compare_mode) = config.compare_mode.as_ref() { + write!(msg, " compare_mode={}", compare_mode).unwrap(); + } + + if let Some(pass_mode) = config.force_pass_mode.as_ref() { + write!(msg, " pass_mode={}", pass_mode).unwrap(); + } + + write!(msg, " mode={}", config.mode).unwrap(); + write!(msg, " host={}", config.host).unwrap(); + write!(msg, " target={}", config.target).unwrap(); + + println!("{msg}"); std::process::exit(1); } @@ -594,80 +620,54 @@ pub fn run_tests(config: Arc) { } } -pub fn test_opts(config: &Config) -> test::TestOpts { - if env::var("RUST_TEST_NOCAPTURE").is_ok() { - eprintln!( - "WARNING: RUST_TEST_NOCAPTURE is no longer used. \ - Use the `--nocapture` flag instead." - ); - } - - test::TestOpts { - exclude_should_panic: false, - filters: config.filters.clone(), - filter_exact: config.filter_exact, - run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No }, - format: config.format, - logfile: config.logfile.clone(), - run_tests: true, - bench_benchmarks: true, - nocapture: config.nocapture, - color: config.color, - shuffle: false, - shuffle_seed: None, - test_threads: None, - skip: config.skip.clone(), - list: false, - options: test::Options::new(), - time_options: None, - force_run_in_process: false, - fail_fast: std::env::var_os("RUSTC_TEST_FAIL_FAST").is_some(), - } -} - /// Read-only context data used during test collection. struct TestCollectorCx { config: Arc, cache: HeadersCache, common_inputs_stamp: Stamp, - modified_tests: Vec, + modified_tests: Vec, } /// Mutable state used during test collection. struct TestCollector { - tests: Vec, - found_path_stems: HashSet, + tests: Vec, + found_path_stems: HashSet, poisoned: bool, } -/// Creates libtest structures for every test/revision in the test suite directory. +impl TestCollector { + fn new() -> Self { + TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false } + } + + fn merge(&mut self, mut other: Self) { + self.tests.append(&mut other.tests); + self.found_path_stems.extend(other.found_path_stems); + self.poisoned |= other.poisoned; + } +} + +/// Creates test structures for every test/revision in the test suite directory. /// /// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests), /// regardless of whether any filters/tests were specified on the command-line, /// because filtering is handled later by libtest. -pub fn collect_and_make_tests(config: Arc) -> Vec { - debug!("making tests from {}", config.src_test_suite_root.display()); +pub(crate) fn collect_and_make_tests(config: Arc) -> Vec { + debug!("making tests from {}", config.src_test_suite_root); let common_inputs_stamp = common_inputs_stamp(&config); let modified_tests = modified_tests(&config, &config.src_test_suite_root).unwrap_or_else(|err| { panic!( "modified_tests got error from dir: {}, error: {}", - config.src_test_suite_root.display(), - err + config.src_test_suite_root, err ) }); let cache = HeadersCache::load(&config); let cx = TestCollectorCx { config, cache, common_inputs_stamp, modified_tests }; - let mut collector = - TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false }; - - collect_tests_from_dir(&cx, &mut collector, &cx.config.src_test_suite_root, Path::new("")) + let collector = collect_tests_from_dir(&cx, &cx.config.src_test_suite_root, Utf8Path::new("")) .unwrap_or_else(|reason| { - panic!( - "Could not read tests from {}: {reason}", - cx.config.src_test_suite_root.display() - ) + panic!("Could not read tests from {}: {reason}", cx.config.src_test_suite_root) }); let TestCollector { tests, found_path_stems, poisoned } = collector; @@ -736,25 +736,29 @@ fn common_inputs_stamp(config: &Config) -> Stamp { /// the `--only-modified` flag is in use. /// /// (Might be inaccurate in some cases.) -fn modified_tests(config: &Config, dir: &Path) -> Result, String> { +fn modified_tests(config: &Config, dir: &Utf8Path) -> Result, String> { // If `--only-modified` wasn't passed, the list of modified tests won't be // used for anything, so avoid some work and just return an empty list. if !config.only_modified { return Ok(vec![]); } - let files = - get_git_modified_files(&config.git_config(), Some(dir), &vec!["rs", "stderr", "fixed"])? - .unwrap_or(vec![]); + let files = get_git_modified_files( + &config.git_config(), + Some(dir.as_std_path()), + &vec!["rs", "stderr", "fixed"], + )?; // Add new test cases to the list, it will be convenient in daily development. - let untracked_files = get_git_untracked_files(&config.git_config(), None)?.unwrap_or(vec![]); + let untracked_files = get_git_untracked_files(Some(dir.as_std_path()))?.unwrap_or(vec![]); let all_paths = [&files[..], &untracked_files[..]].concat(); let full_paths = { - let mut full_paths: Vec = all_paths + let mut full_paths: Vec = all_paths .into_iter() - .map(|f| PathBuf::from(f).with_extension("").with_extension("rs")) - .filter_map(|f| if Path::new(&f).exists() { f.canonicalize().ok() } else { None }) + .map(|f| Utf8PathBuf::from(f).with_extension("").with_extension("rs")) + .filter_map( + |f| if Utf8Path::new(&f).exists() { f.canonicalize_utf8().ok() } else { None }, + ) .collect(); full_paths.dedup(); full_paths.sort_unstable(); @@ -767,32 +771,25 @@ fn modified_tests(config: &Config, dir: &Path) -> Result, String> { /// that will be handed over to libtest. fn collect_tests_from_dir( cx: &TestCollectorCx, - collector: &mut TestCollector, - dir: &Path, - relative_dir_path: &Path, -) -> io::Result<()> { + dir: &Utf8Path, + relative_dir_path: &Utf8Path, +) -> io::Result { // Ignore directories that contain a file named `compiletest-ignore-dir`. if dir.join("compiletest-ignore-dir").exists() { - return Ok(()); + return Ok(TestCollector::new()); } - // For run-make tests, a "test file" is actually a directory that contains - // an `rmake.rs` or `Makefile`" + // For run-make tests, a "test file" is actually a directory that contains an `rmake.rs`. if cx.config.mode == Mode::RunMake { - if dir.join("Makefile").exists() && dir.join("rmake.rs").exists() { - return Err(io::Error::other( - "run-make tests cannot have both `Makefile` and `rmake.rs`", - )); - } - - if dir.join("Makefile").exists() || dir.join("rmake.rs").exists() { + let mut collector = TestCollector::new(); + if dir.join("rmake.rs").exists() { let paths = TestPaths { file: dir.to_path_buf(), relative_dir: relative_dir_path.parent().unwrap().to_path_buf(), }; - make_test(cx, collector, &paths); + make_test(cx, &mut collector, &paths); // This directory is a test, so don't try to find other tests inside it. - return Ok(()); + return Ok(collector); } } @@ -809,42 +806,51 @@ fn collect_tests_from_dir( // subdirectories we find, except for `auxiliary` directories. // FIXME: this walks full tests tree, even if we have something to ignore // use walkdir/ignore like in tidy? - for file in fs::read_dir(dir)? { - let file = file?; - let file_path = file.path(); - let file_name = file.file_name(); - - if is_test(&file_name) - && (!cx.config.only_modified || cx.modified_tests.contains(&file_path)) - { - // We found a test file, so create the corresponding libtest structures. - debug!("found test file: {:?}", file_path.display()); - - // Record the stem of the test file, to check for overlaps later. - let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap()); - collector.found_path_stems.insert(rel_test_path); - - let paths = - TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; - make_test(cx, collector, &paths); - } else if file_path.is_dir() { - // Recurse to find more tests in a subdirectory. - let relative_file_path = relative_dir_path.join(file.file_name()); - if &file_name != "auxiliary" { - debug!("found directory: {:?}", file_path.display()); - collect_tests_from_dir(cx, collector, &file_path, &relative_file_path)?; + fs::read_dir(dir.as_std_path())? + .par_bridge() + .map(|file| { + let mut collector = TestCollector::new(); + let file = file?; + let file_path = Utf8PathBuf::try_from(file.path()).unwrap(); + let file_name = file_path.file_name().unwrap(); + + if is_test(file_name) + && (!cx.config.only_modified || cx.modified_tests.contains(&file_path)) + { + // We found a test file, so create the corresponding libtest structures. + debug!(%file_path, "found test file"); + + // Record the stem of the test file, to check for overlaps later. + let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap()); + collector.found_path_stems.insert(rel_test_path); + + let paths = + TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; + make_test(cx, &mut collector, &paths); + } else if file_path.is_dir() { + // Recurse to find more tests in a subdirectory. + let relative_file_path = relative_dir_path.join(file_name); + if file_name != "auxiliary" { + debug!(%file_path, "found directory"); + collector.merge(collect_tests_from_dir(cx, &file_path, &relative_file_path)?); + } + } else { + debug!(%file_path, "found other file/directory"); } - } else { - debug!("found other file/directory: {:?}", file_path.display()); - } - } - Ok(()) + Ok(collector) + }) + .reduce( + || Ok(TestCollector::new()), + |a, b| { + let mut a = a?; + a.merge(b?); + Ok(a) + }, + ) } /// Returns true if `file_name` looks like a proper test file name. -pub fn is_test(file_name: &OsString) -> bool { - let file_name = file_name.to_str().unwrap(); - +pub fn is_test(file_name: &str) -> bool { if !file_name.ends_with(".rs") { return false; } @@ -854,26 +860,16 @@ pub fn is_test(file_name: &OsString) -> bool { !invalid_prefixes.iter().any(|p| file_name.starts_with(p)) } -/// For a single test file, creates one or more test structures (one per revision) -/// that can be handed over to libtest to run, possibly in parallel. +/// For a single test file, creates one or more test structures (one per revision) that can be +/// handed over to libtest to run, possibly in parallel. fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &TestPaths) { - // For run-make tests, each "test file" is actually a _directory_ containing - // an `rmake.rs` or `Makefile`. But for the purposes of directive parsing, - // we want to look at that recipe file, not the directory itself. + // For run-make tests, each "test file" is actually a _directory_ containing an `rmake.rs`. But + // for the purposes of directive parsing, we want to look at that recipe file, not the directory + // itself. let test_path = if cx.config.mode == Mode::RunMake { - if testpaths.file.join("rmake.rs").exists() && testpaths.file.join("Makefile").exists() { - panic!("run-make tests cannot have both `rmake.rs` and `Makefile`"); - } - - if testpaths.file.join("rmake.rs").exists() { - // Parse directives in rmake.rs. - testpaths.file.join("rmake.rs") - } else { - // Parse directives in the Makefile. - testpaths.file.join("Makefile") - } + testpaths.file.join("rmake.rs") } else { - PathBuf::from(&testpaths.file) + testpaths.file.clone() }; // Scan the test file to discover its revisions, if any. @@ -892,7 +888,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te }; // For each revision (or the sole dummy revision), create and append a - // `test::TestDescAndFn` that can be handed over to libtest. + // `CollectedTest` that can be handed over to the test executor. collector.tests.extend(revisions.into_iter().map(|revision| { // Create a test name and description to hand over to libtest. let src_file = fs::File::open(&test_path).expect("open test file to parse ignores"); @@ -915,19 +911,20 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te if !cx.config.force_rerun && is_up_to_date(cx, testpaths, &early_props, revision) { desc.ignore = true; // Keep this in sync with the "up-to-date" message detected by bootstrap. - desc.ignore_message = Some("up-to-date"); + desc.ignore_message = Some("up-to-date".into()); } - // Create the callback that will run this test/revision when libtest calls it. - let testfn = make_test_closure(Arc::clone(&cx.config), testpaths, revision); + let config = Arc::clone(&cx.config); + let testpaths = testpaths.clone(); + let revision = revision.map(str::to_owned); - test::TestDescAndFn { desc, testfn } + CollectedTest { desc, config, testpaths, revision } })); } /// The path of the `stamp` file that gets created or updated whenever a /// particular test completes successfully. -fn stamp_file_path(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { +fn stamp_file_path(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> Utf8PathBuf { output_base_dir(config, testpaths, revision).join("stamp") } @@ -940,7 +937,7 @@ fn files_related_to_test( testpaths: &TestPaths, props: &EarlyProps, revision: Option<&str>, -) -> Vec { +) -> Vec { let mut related = vec![]; if testpaths.file.is_dir() { @@ -948,7 +945,7 @@ fn files_related_to_test( for entry in WalkDir::new(&testpaths.file) { let path = entry.unwrap().into_path(); if path.is_file() { - related.push(path); + related.push(Utf8PathBuf::try_from(path).unwrap()); } } } else { @@ -1019,7 +1016,7 @@ struct Stamp { impl Stamp { /// Creates a timestamp holding the last-modified time of the specified file. - fn from_path(path: &Path) -> Self { + fn from_path(path: &Utf8Path) -> Self { let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH }; stamp.add_path(path); stamp @@ -1027,8 +1024,8 @@ impl Stamp { /// Updates this timestamp to the last-modified time of the specified file, /// if it is later than the currently-stored timestamp. - fn add_path(&mut self, path: &Path) { - let modified = fs::metadata(path) + fn add_path(&mut self, path: &Utf8Path) { + let modified = fs::metadata(path.as_std_path()) .and_then(|metadata| metadata.modified()) .unwrap_or(SystemTime::UNIX_EPOCH); self.time = self.time.max(modified); @@ -1037,7 +1034,8 @@ impl Stamp { /// Updates this timestamp to the most recent last-modified time of all files /// recursively contained in the given directory, if it is later than the /// currently-stored timestamp. - fn add_dir(&mut self, path: &Path) { + fn add_dir(&mut self, path: &Utf8Path) { + let path = path.as_std_path(); for entry in WalkDir::new(path) { let entry = entry.unwrap(); if entry.file_type().is_file() { @@ -1053,11 +1051,7 @@ impl Stamp { } /// Creates a name for this test/revision that can be handed over to libtest. -fn make_test_name( - config: &Config, - testpaths: &TestPaths, - revision: Option<&str>, -) -> test::TestName { +fn make_test_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> String { // Print the name of the file, relative to the sources root. let path = testpaths.file.strip_prefix(&config.src_root).unwrap(); let debugger = match config.debugger { @@ -1069,32 +1063,14 @@ fn make_test_name( None => String::new(), }; - test::DynTestName(format!( + format!( "[{}{}{}] {}{}", config.mode, debugger, mode_suffix, - path.display(), + path, revision.map_or("".to_string(), |rev| format!("#{}", rev)) - )) -} - -/// Creates a callback for this test/revision that libtest will call when it -/// decides to actually run the underlying test. -fn make_test_closure( - config: Arc, - testpaths: &TestPaths, - revision: Option<&str>, -) -> test::TestFn { - let testpaths = testpaths.clone(); - let revision = revision.map(str::to_owned); - - // This callback is the link between compiletest's test discovery code, - // and the parts of compiletest that know how to run an individual test. - test::DynTestFn(Box::new(move || { - runtest::run(config, &testpaths, revision.as_deref()); - Ok(()) - })) + ) } /// Checks that test discovery didn't find any tests whose name stem is a prefix @@ -1114,7 +1090,7 @@ fn make_test_closure( /// To avoid problems, we forbid test names from overlapping in this way. /// /// See for more context. -fn check_for_overlapping_test_paths(found_path_stems: &HashSet) { +fn check_for_overlapping_test_paths(found_path_stems: &HashSet) { let mut collisions = Vec::new(); for path in found_path_stems { for ancestor in path.ancestors().skip(1) { @@ -1127,7 +1103,7 @@ fn check_for_overlapping_test_paths(found_path_stems: &HashSet) { collisions.sort(); let collisions: String = collisions .into_iter() - .map(|(path, check_parent)| format!("test {path:?} clashes with {check_parent:?}\n")) + .map(|(path, check_parent)| format!("test {path} clashes with {check_parent}\n")) .collect(); panic!( "{collisions}\n\ diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 2f0c7d8ddc516..b9ae583581ef2 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -27,7 +27,7 @@ fn main() { eprintln!( r#" WARNING: profiler runtime is not available, so `.coverage` files won't be {actioned} -help: try setting `profiler = true` in the `[build]` section of `config.toml`"# +help: try setting `profiler = true` in the `[build]` section of `bootstrap.toml`"# ); } diff --git a/src/tools/compiletest/src/raise_fd_limit.rs b/src/tools/compiletest/src/raise_fd_limit.rs index 4445ecb7ce857..653b125a6b413 100644 --- a/src/tools/compiletest/src/raise_fd_limit.rs +++ b/src/tools/compiletest/src/raise_fd_limit.rs @@ -6,8 +6,8 @@ /// This fixes issue #7772. #[cfg(target_vendor = "apple")] #[allow(non_camel_case_types)] +// FIXME(#139616): document caller contract. pub unsafe fn raise_fd_limit() { - use std::mem::size_of_val; use std::ptr::null_mut; use std::{cmp, io}; @@ -22,8 +22,10 @@ pub unsafe fn raise_fd_limit() { let mut mib: [libc::c_int; 2] = [CTL_KERN, KERN_MAXFILESPERPROC]; let mut maxfiles: libc::c_int = 0; let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t; - if libc::sysctl(&mut mib[0], 2, &mut maxfiles as *mut _ as *mut _, &mut size, null_mut(), 0) - != 0 + // FIXME(#139616): justify why this is sound. + if unsafe { + libc::sysctl(&mut mib[0], 2, &mut maxfiles as *mut _ as *mut _, &mut size, null_mut(), 0) + } != 0 { let err = io::Error::last_os_error(); panic!("raise_fd_limit: error calling sysctl: {}", err); @@ -31,7 +33,8 @@ pub unsafe fn raise_fd_limit() { // Fetch the current resource limits let mut rlim = libc::rlimit { rlim_cur: 0, rlim_max: 0 }; - if libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) != 0 { + // FIXME(#139616): justify why this is sound. + if unsafe { libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) } != 0 { let err = io::Error::last_os_error(); panic!("raise_fd_limit: error calling getrlimit: {}", err); } @@ -42,7 +45,8 @@ pub unsafe fn raise_fd_limit() { rlim.rlim_cur = cmp::min(maxfiles as libc::rlim_t, rlim.rlim_max); // Set our newly-increased resource limit. - if libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) != 0 { + // FIXME(#139616): justify why this is sound. + if unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) } != 0 { let err = io::Error::last_os_error(); panic!("raise_fd_limit: error calling setrlimit: {}", err); } diff --git a/src/tools/compiletest/src/read2.rs b/src/tools/compiletest/src/read2.rs index 28ca5589992a2..2213dd07160a7 100644 --- a/src/tools/compiletest/src/read2.rs +++ b/src/tools/compiletest/src/read2.rs @@ -165,6 +165,7 @@ mod imp { mut err_pipe: ChildStderr, data: &mut dyn FnMut(bool, &mut Vec, bool), ) -> io::Result<()> { + // FIXME(#139616): justify why this is sound. unsafe { libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK); libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK); @@ -175,6 +176,7 @@ mod imp { let mut out = Vec::new(); let mut err = Vec::new(); + // FIXME(#139616): justify why this is sound. let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; fds[0].fd = out_pipe.as_raw_fd(); fds[0].events = libc::POLLIN; @@ -185,6 +187,7 @@ mod imp { while nfds > 0 { // wait for either pipe to become readable using `select` + // FIXME(#139616): justify why this is sound. let r = unsafe { libc::poll(fds.as_mut_ptr(), nfds, -1) }; if r == -1 { let err = io::Error::last_os_error(); @@ -256,6 +259,7 @@ mod imp { port.add_handle(0, &out_pipe)?; port.add_handle(1, &err_pipe)?; + // FIXME(#139616): justify why this is sound. unsafe { let mut out_pipe = Pipe::new(out_pipe, &mut out); let mut err_pipe = Pipe::new(err_pipe, &mut err); @@ -284,18 +288,23 @@ mod imp { } impl<'a> Pipe<'a> { + // FIXME(#139616): document caller contract. unsafe fn new(p: P, dst: &'a mut Vec) -> Pipe<'a> { Pipe { dst, - pipe: NamedPipe::from_raw_handle(p.into_raw_handle()), + // FIXME(#139616): justify why this is sound. + pipe: unsafe { NamedPipe::from_raw_handle(p.into_raw_handle()) }, overlapped: Overlapped::zero(), done: false, } } + // FIXME(#139616): document caller contract. unsafe fn read(&mut self) -> io::Result<()> { - let dst = slice_to_end(self.dst); - match self.pipe.read_overlapped(dst, self.overlapped.raw()) { + // FIXME(#139616): justify why this is sound. + let dst = unsafe { slice_to_end(self.dst) }; + // FIXME(#139616): justify why this is sound. + match unsafe { self.pipe.read_overlapped(dst, self.overlapped.raw()) } { Ok(_) => Ok(()), Err(e) => { if e.raw_os_error() == Some(ERROR_BROKEN_PIPE.0 as i32) { @@ -308,15 +317,18 @@ mod imp { } } + // FIXME(#139616): document caller contract. unsafe fn complete(&mut self, status: &CompletionStatus) { let prev = self.dst.len(); - self.dst.set_len(prev + status.bytes_transferred() as usize); + // FIXME(#139616): justify why this is sound. + unsafe { self.dst.set_len(prev + status.bytes_transferred() as usize) }; if status.bytes_transferred() == 0 { self.done = true; } } } + // FIXME(#139616): document caller contract. unsafe fn slice_to_end(v: &mut Vec) -> &mut [u8] { if v.capacity() == 0 { v.reserve(16); @@ -324,6 +336,12 @@ mod imp { if v.capacity() == v.len() { v.reserve(1); } - slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize), v.capacity() - v.len()) + // FIXME(#139616): justify why this is sound. + unsafe { + slice::from_raw_parts_mut( + v.as_mut_ptr().offset(v.len() as isize), + v.capacity() - v.len(), + ) + } } } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index f36d43b211fff..75f24adb70fa5 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1,16 +1,16 @@ use std::borrow::Cow; use std::collections::{HashMap, HashSet}; -use std::ffi::{OsStr, OsString}; +use std::ffi::OsString; use std::fs::{self, File, create_dir_all}; use std::hash::{DefaultHasher, Hash, Hasher}; use std::io::prelude::*; use std::io::{self, BufReader}; -use std::path::{Path, PathBuf}; use std::process::{Child, Command, ExitStatus, Output, Stdio}; use std::sync::Arc; use std::{env, iter, str}; -use anyhow::Context; +use build_helper::fs::remove_and_create_dir_all; +use camino::{Utf8Path, Utf8PathBuf}; use colored::Colorize; use regex::{Captures, Regex}; use tracing::*; @@ -23,10 +23,10 @@ use crate::common::{ output_base_dir, output_base_name, output_testname_unique, }; use crate::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff}; -use crate::errors::{self, Error, ErrorKind}; +use crate::errors::{Error, ErrorKind, load_errors}; use crate::header::TestProps; use crate::read2::{Truncated, read2_abbreviated}; -use crate::util::{PathBufExt, add_dylib_path, logv, static_regex}; +use crate::util::{Utf8PathBufExt, add_dylib_path, logv, static_regex}; use crate::{ColorConfig, json, stamp_file_path}; mod debugger; @@ -132,7 +132,7 @@ pub fn run(config: Arc, testpaths: &TestPaths, revision: Option<&str>) { // We're going to be dumping a lot of info. Start on a new line. print!("\n\n"); } - debug!("running {:?}", testpaths.file.display()); + debug!("running {}", testpaths.file); let mut props = TestProps::from_file(&testpaths.file, revision, &config); // For non-incremental (i.e. regular UI) tests, the incremental directory @@ -143,11 +143,11 @@ pub fn run(config: Arc, testpaths: &TestPaths, revision: Option<&str>) { } let cx = TestCx { config: &config, props: &props, testpaths, revision }; - create_dir_all(&cx.output_base_dir()) - .with_context(|| { - format!("failed to create output base directory {}", cx.output_base_dir().display()) - }) - .unwrap(); + + if let Err(e) = create_dir_all(&cx.output_base_dir()) { + panic!("failed to create output base directory {}: {e}", cx.output_base_dir()); + } + if props.incremental { cx.init_incremental_test(); } @@ -178,6 +178,7 @@ pub fn compute_stamp_hash(config: &Config) -> String { let mut hash = DefaultHasher::new(); config.stage_id.hash(&mut hash); config.run.hash(&mut hash); + config.edition.hash(&mut hash); match config.debugger { Some(Debugger::Cdb) => { @@ -207,11 +208,6 @@ pub fn compute_stamp_hash(config: &Config) -> String { format!("{:x}", hash.finish()) } -fn remove_and_create_dir_all(path: &Path) { - let _ = fs::remove_dir_all(path); - fs::create_dir_all(path).unwrap(); -} - #[derive(Copy, Clone, Debug)] struct TestCx<'test> { config: &'test Config, @@ -423,7 +419,7 @@ impl<'test> TestCx<'test> { let aux_dir = self.aux_output_dir_name(); let input: &str = match read_from { ReadFrom::Stdin(_) => "-", - ReadFrom::Path => self.testpaths.file.to_str().unwrap(), + ReadFrom::Path => self.testpaths.file.as_str(), }; let mut rustc = Command::new(&self.config.rustc_path); @@ -446,8 +442,8 @@ impl<'test> TestCx<'test> { self.compose_and_run( rustc, - self.config.compile_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), + self.config.compile_lib_path.as_path(), + Some(aux_dir.as_path()), src, ) } @@ -522,7 +518,9 @@ impl<'test> TestCx<'test> { let mut rustc = Command::new(&self.config.rustc_path); let out_dir = self.output_base_name().with_extension("pretty-out"); - remove_and_create_dir_all(&out_dir); + remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| { + panic!("failed to remove and recreate output directory `{out_dir}`: {e}") + }); let target = if self.props.force_host { &*self.config.host } else { &*self.config.target }; @@ -579,26 +577,9 @@ impl<'test> TestCx<'test> { } } - fn check_all_error_patterns( - &self, - output_to_check: &str, - proc_res: &ProcRes, - pm: Option, - ) { - if self.props.error_patterns.is_empty() && self.props.regex_error_patterns.is_empty() { - if pm.is_some() { - // FIXME(#65865) - return; - } else { - self.fatal(&format!( - "no error pattern specified in {:?}", - self.testpaths.file.display() - )); - } - } - + /// Check `error-pattern` and `regex-error-pattern` directives. + fn check_all_error_patterns(&self, output_to_check: &str, proc_res: &ProcRes) { let mut missing_patterns: Vec = Vec::new(); - self.check_error_patterns(output_to_check, &mut missing_patterns); self.check_regex_error_patterns(output_to_check, proc_res, &mut missing_patterns); @@ -675,14 +656,14 @@ impl<'test> TestCx<'test> { } } - fn check_expected_errors(&self, expected_errors: Vec, proc_res: &ProcRes) { + /// Check `//~ KIND message` annotations. + fn check_expected_errors(&self, proc_res: &ProcRes) { + let expected_errors = load_errors(&self.testpaths.file, self.revision); debug!( "check_expected_errors: expected_errors={:?} proc_res.status={:?}", expected_errors, proc_res.status ); - if proc_res.status.success() - && expected_errors.iter().any(|x| x.kind == Some(ErrorKind::Error)) - { + if proc_res.status.success() && expected_errors.iter().any(|x| x.kind == ErrorKind::Error) { self.fatal_proc_rec("process did not return an error status", proc_res); } @@ -697,39 +678,51 @@ impl<'test> TestCx<'test> { } // On Windows, translate all '\' path separators to '/' - let file_name = format!("{}", self.testpaths.file.display()).replace(r"\", "/"); + let file_name = self.testpaths.file.to_string().replace(r"\", "/"); // On Windows, keep all '\' path separators to match the paths reported in the JSON output // from the compiler let diagnostic_file_name = if self.props.remap_src_base { - let mut p = PathBuf::from(FAKE_SRC_BASE); + let mut p = Utf8PathBuf::from(FAKE_SRC_BASE); p.push(&self.testpaths.relative_dir); p.push(self.testpaths.file.file_name().unwrap()); - p.display().to_string() + p.to_string() } else { - self.testpaths.file.display().to_string() + self.testpaths.file.to_string() }; - // If the testcase being checked contains at least one expected "help" - // message, then we'll ensure that all "help" messages are expected. - // Otherwise, all "help" messages reported by the compiler will be ignored. - // This logic also applies to "note" messages. - let expect_help = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Help)); - let expect_note = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Note)); + // Errors and warnings are always expected, other diagnostics are only expected + // if one of them actually occurs in the test. + let expected_kinds: HashSet<_> = [ErrorKind::Error, ErrorKind::Warning] + .into_iter() + .chain(expected_errors.iter().map(|e| e.kind)) + .collect(); // Parse the JSON output from the compiler and extract out the messages. - let actual_errors = json::parse_output(&diagnostic_file_name, &proc_res.stderr, proc_res); + let actual_errors = json::parse_output(&diagnostic_file_name, &self.get_output(proc_res)) + .into_iter() + .map(|e| Error { msg: self.normalize_output(&e.msg, &[]), ..e }); + let mut unexpected = Vec::new(); let mut found = vec![false; expected_errors.len()]; - for mut actual_error in actual_errors { - actual_error.msg = self.normalize_output(&actual_error.msg, &[]); + for actual_error in actual_errors { + for pattern in &self.props.error_patterns { + let pattern = pattern.trim(); + if actual_error.msg.contains(pattern) { + let q = if actual_error.line_num.is_none() { "?" } else { "" }; + self.fatal(&format!( + "error pattern '{pattern}' is found in structured \ + diagnostics, use `//~{q} {} {pattern}` instead", + actual_error.kind, + )); + } + } let opt_index = expected_errors.iter().enumerate().position(|(index, expected_error)| { !found[index] && actual_error.line_num == expected_error.line_num - && (expected_error.kind.is_none() - || actual_error.kind == expected_error.kind) + && actual_error.kind == expected_error.kind && actual_error.msg.contains(&expected_error.msg) }); @@ -741,17 +734,15 @@ impl<'test> TestCx<'test> { } None => { - // If the test is a known bug, don't require that the error is annotated - if self.is_unexpected_compiler_message(&actual_error, expect_help, expect_note) + if actual_error.require_annotation + && expected_kinds.contains(&actual_error.kind) + && !self.props.dont_require_annotations.contains(&actual_error.kind) { self.error(&format!( "{}:{}: unexpected {}: '{}'", file_name, - actual_error.line_num, - actual_error - .kind - .as_ref() - .map_or(String::from("message"), |k| k.to_string()), + actual_error.line_num_str(), + actual_error.kind, actual_error.msg )); unexpected.push(actual_error); @@ -767,8 +758,8 @@ impl<'test> TestCx<'test> { self.error(&format!( "{}:{}: expected {} not found: {}", file_name, - expected_error.line_num, - expected_error.kind.as_ref().map_or("message".into(), |k| k.to_string()), + expected_error.line_num_str(), + expected_error.kind, expected_error.msg )); not_found.push(expected_error); @@ -800,25 +791,6 @@ impl<'test> TestCx<'test> { } } - /// Returns `true` if we should report an error about `actual_error`, - /// which did not match any of the expected error. We always require - /// errors/warnings to be explicitly listed, but only require - /// helps/notes if there are explicit helps/notes given. - fn is_unexpected_compiler_message( - &self, - actual_error: &Error, - expect_help: bool, - expect_note: bool, - ) -> bool { - !actual_error.msg.is_empty() - && match actual_error.kind { - Some(ErrorKind::Help) => expect_help, - Some(ErrorKind::Note) => expect_note, - Some(ErrorKind::Error) | Some(ErrorKind::Warning) => true, - Some(ErrorKind::Suggestion) | None => false, - } - } - fn should_emit_metadata(&self, pm: Option) -> Emit { match (pm, self.props.fail_mode, self.config.mode) { (Some(PassMode::Check), ..) | (_, Some(FailMode::Check), Ui) => Emit::Metadata, @@ -889,7 +861,7 @@ impl<'test> TestCx<'test> { /// `root_out_dir` and `root_testpaths` refer to the parameters of the actual test being run. /// Auxiliaries, no matter how deep, have the same root_out_dir and root_testpaths. - fn document(&self, root_out_dir: &Path, root_testpaths: &TestPaths) -> ProcRes { + fn document(&self, root_out_dir: &Utf8Path, root_testpaths: &TestPaths) -> ProcRes { if self.props.build_aux_docs { for rel_ab in &self.props.aux.builds { let aux_testpaths = self.compute_aux_test_paths(root_testpaths, rel_ab); @@ -918,13 +890,13 @@ impl<'test> TestCx<'test> { // actual --out-dir given to the auxiliary or test, as opposed to the root out dir for the entire // test - let out_dir: Cow<'_, Path> = if self.props.unique_doc_out_dir { + let out_dir: Cow<'_, Utf8Path> = if self.props.unique_doc_out_dir { let file_name = self.testpaths.file.file_stem().expect("file name should not be empty"); - let out_dir = PathBuf::from_iter([ + let out_dir = Utf8PathBuf::from_iter([ root_out_dir, - Path::new("docs"), - Path::new(file_name), - Path::new("doc"), + Utf8Path::new("docs"), + Utf8Path::new(file_name), + Utf8Path::new("doc"), ]); create_dir_all(&out_dir).unwrap(); Cow::Owned(out_dir) @@ -937,7 +909,7 @@ impl<'test> TestCx<'test> { rustdoc.current_dir(current_dir); rustdoc .arg("-L") - .arg(self.config.run_lib_path.to_str().unwrap()) + .arg(self.config.run_lib_path.as_path()) .arg("-L") .arg(aux_dir) .arg("-o") @@ -971,16 +943,16 @@ impl<'test> TestCx<'test> { delete_after_success: bool, ) -> ProcRes { let prepare_env = |cmd: &mut Command| { - for key in &self.props.unset_exec_env { - cmd.env_remove(key); - } - for (key, val) in &self.props.exec_env { cmd.env(key, val); } for (key, val) in env_extra { cmd.env(key, val); } + + for key in &self.props.unset_exec_env { + cmd.env_remove(key); + } }; let proc_res = match &*self.config.target { @@ -1023,8 +995,8 @@ impl<'test> TestCx<'test> { self.compose_and_run( test_client, - self.config.run_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), + self.config.run_lib_path.as_path(), + Some(aux_dir.as_path()), None, ) } @@ -1038,8 +1010,8 @@ impl<'test> TestCx<'test> { self.compose_and_run( wr_run, - self.config.run_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), + self.config.run_lib_path.as_path(), + Some(aux_dir.as_path()), None, ) } @@ -1053,8 +1025,8 @@ impl<'test> TestCx<'test> { self.compose_and_run( program, - self.config.run_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), + self.config.run_lib_path.as_path(), + Some(aux_dir.as_path()), None, ) } @@ -1075,7 +1047,7 @@ impl<'test> TestCx<'test> { let test_ab = of.file.parent().expect("test file path has no parent").join("auxiliary").join(rel_ab); if !test_ab.exists() { - self.fatal(&format!("aux-build `{}` source not found", test_ab.display())) + self.fatal(&format!("aux-build `{}` source not found", test_ab)) } TestPaths { @@ -1112,23 +1084,29 @@ impl<'test> TestCx<'test> { || !self.props.aux.proc_macros.is_empty() } - fn aux_output_dir(&self) -> PathBuf { + fn aux_output_dir(&self) -> Utf8PathBuf { let aux_dir = self.aux_output_dir_name(); if !self.props.aux.builds.is_empty() { - remove_and_create_dir_all(&aux_dir); + remove_and_create_dir_all(&aux_dir).unwrap_or_else(|e| { + panic!("failed to remove and recreate output directory `{aux_dir}`: {e}") + }); } if !self.props.aux.bins.is_empty() { let aux_bin_dir = self.aux_bin_output_dir_name(); - remove_and_create_dir_all(&aux_dir); - remove_and_create_dir_all(&aux_bin_dir); + remove_and_create_dir_all(&aux_dir).unwrap_or_else(|e| { + panic!("failed to remove and recreate output directory `{aux_dir}`: {e}") + }); + remove_and_create_dir_all(&aux_bin_dir).unwrap_or_else(|e| { + panic!("failed to remove and recreate output directory `{aux_bin_dir}`: {e}") + }); } aux_dir } - fn build_all_auxiliary(&self, of: &TestPaths, aux_dir: &Path, rustc: &mut Command) { + fn build_all_auxiliary(&self, of: &TestPaths, aux_dir: &Utf8Path, rustc: &mut Command) { for rel_ab in &self.props.aux.builds { self.build_auxiliary(of, rel_ab, &aux_dir, None); } @@ -1148,12 +1126,7 @@ impl<'test> TestCx<'test> { |rustc: &mut Command, aux_name: &str, aux_path: &str, aux_type: AuxType| { let lib_name = get_lib_name(&path_to_crate_name(aux_path), aux_type); if let Some(lib_name) = lib_name { - rustc.arg("--extern").arg(format!( - "{}={}/{}", - aux_name, - aux_dir.display(), - lib_name - )); + rustc.arg("--extern").arg(format!("{}={}/{}", aux_name, aux_dir, lib_name)); } }; @@ -1174,7 +1147,7 @@ impl<'test> TestCx<'test> { let aux_type = self.build_auxiliary(of, aux_file, aux_dir, None); if let Some(lib_name) = get_lib_name(aux_file.trim_end_matches(".rs"), aux_type) { let lib_path = aux_dir.join(&lib_name); - rustc.arg(format!("-Zcodegen-backend={}", lib_path.display())); + rustc.arg(format!("-Zcodegen-backend={}", lib_path)); } } } @@ -1190,7 +1163,7 @@ impl<'test> TestCx<'test> { if self.props.add_core_stubs { let minicore_path = self.build_minicore(); rustc.arg("--extern"); - rustc.arg(&format!("minicore={}", minicore_path.to_str().unwrap())); + rustc.arg(&format!("minicore={}", minicore_path)); } let aux_dir = self.aux_output_dir(); @@ -1200,15 +1173,15 @@ impl<'test> TestCx<'test> { self.props.unset_rustc_env.iter().fold(&mut rustc, Command::env_remove); self.compose_and_run( rustc, - self.config.compile_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), + self.config.compile_lib_path.as_path(), + Some(aux_dir.as_path()), input, ) } /// Builds `minicore`. Returns the path to the minicore rlib within the base test output /// directory. - fn build_minicore(&self) -> PathBuf { + fn build_minicore(&self) -> Utf8PathBuf { let output_file_path = self.output_base_dir().join("libminicore.rlib"); let mut rustc = self.make_compile_args( &self.config.minicore_path, @@ -1222,14 +1195,10 @@ impl<'test> TestCx<'test> { rustc.args(&["--crate-type", "rlib"]); rustc.arg("-Cpanic=abort"); - let res = - self.compose_and_run(rustc, self.config.compile_lib_path.to_str().unwrap(), None, None); + let res = self.compose_and_run(rustc, self.config.compile_lib_path.as_path(), None, None); if !res.status.success() { self.fatal_proc_rec( - &format!( - "auxiliary build of {:?} failed to compile: ", - self.config.minicore_path.display() - ), + &format!("auxiliary build of {} failed to compile: ", self.config.minicore_path), &res, ); } @@ -1244,7 +1213,7 @@ impl<'test> TestCx<'test> { &self, of: &TestPaths, source_path: &str, - aux_dir: &Path, + aux_dir: &Utf8Path, aux_type: Option, ) -> AuxType { let aux_testpaths = self.compute_aux_test_paths(of, source_path); @@ -1335,16 +1304,13 @@ impl<'test> TestCx<'test> { let auxres = aux_cx.compose_and_run( aux_rustc, - aux_cx.config.compile_lib_path.to_str().unwrap(), - Some(aux_dir.to_str().unwrap()), + aux_cx.config.compile_lib_path.as_path(), + Some(aux_dir.as_path()), None, ); if !auxres.status.success() { self.fatal_proc_rec( - &format!( - "auxiliary build of {:?} failed to compile: ", - aux_testpaths.file.display() - ), + &format!("auxiliary build of {} failed to compile: ", aux_testpaths.file), &auxres, ); } @@ -1353,8 +1319,8 @@ impl<'test> TestCx<'test> { fn read2_abbreviated(&self, child: Child) -> (Output, Truncated) { let mut filter_paths_from_len = Vec::new(); - let mut add_path = |path: &Path| { - let path = path.display().to_string(); + let mut add_path = |path: &Utf8Path| { + let path = path.to_string(); let windows = path.replace("\\", "\\\\"); if windows != path { filter_paths_from_len.push(windows); @@ -1376,8 +1342,8 @@ impl<'test> TestCx<'test> { fn compose_and_run( &self, mut command: Command, - lib_path: &str, - aux_path: Option<&str>, + lib_path: &Utf8Path, + aux_path: Option<&Utf8Path>, input: Option, ) -> ProcRes { let cmdline = { @@ -1422,17 +1388,9 @@ impl<'test> TestCx<'test> { matches!(self.config.suite.as_str(), "rustdoc-ui" | "rustdoc-js" | "rustdoc-json") } - fn get_mir_dump_dir(&self) -> PathBuf { - let mut mir_dump_dir = self.config.build_test_suite_root.clone(); - debug!("input_file: {:?}", self.testpaths.file); - mir_dump_dir.push(&self.testpaths.relative_dir); - mir_dump_dir.push(self.testpaths.file.file_stem().unwrap()); - mir_dump_dir - } - fn make_compile_args( &self, - input_file: &Path, + input_file: &Utf8Path, output_file: TargetLocation, emit: Emit, allow_unused: AllowUnused, @@ -1473,7 +1431,7 @@ impl<'test> TestCx<'test> { // Similarly, vendored sources shouldn't be shown when running from a dist tarball. rustc.arg("-Z").arg(format!( "ignore-directory-in-diagnostics-source-blocks={}", - self.config.src_root.join("vendor").to_str().unwrap(), + self.config.src_root.join("vendor"), )); // Optionally prevent default --sysroot if specified in test compile-flags. @@ -1497,7 +1455,7 @@ impl<'test> TestCx<'test> { if !is_rustdoc { if let Some(ref incremental_dir) = self.props.incremental_dir { - rustc.args(&["-C", &format!("incremental={}", incremental_dir.display())]); + rustc.args(&["-C", &format!("incremental={}", incremental_dir)]); rustc.args(&["-Z", "incremental-verify-ich"]); } @@ -1538,10 +1496,9 @@ impl<'test> TestCx<'test> { } let set_mir_dump_dir = |rustc: &mut Command| { - let mir_dump_dir = self.get_mir_dump_dir(); - remove_and_create_dir_all(&mir_dump_dir); + let mir_dump_dir = self.output_base_dir(); let mut dir_opt = "-Zdump-mir-dir=".to_string(); - dir_opt.push_str(mir_dump_dir.to_str().unwrap()); + dir_opt.push_str(mir_dump_dir.as_str()); debug!("dir_opt: {:?}", dir_opt); rustc.arg(dir_opt); }; @@ -1626,7 +1583,10 @@ impl<'test> TestCx<'test> { Crashes => { set_mir_dump_dir(&mut rustc); } - Pretty | DebugInfo | Rustdoc | RustdocJson | RunMake | CodegenUnits | RustdocJs => { + CodegenUnits => { + rustc.arg("-Zprint-mono-items"); + } + Pretty | DebugInfo | Rustdoc | RustdocJson | RunMake | RustdocJs => { // do not use JSON output } } @@ -1634,8 +1594,7 @@ impl<'test> TestCx<'test> { if self.props.remap_src_base { rustc.arg(format!( "--remap-path-prefix={}={}", - self.config.src_test_suite_root.to_str().unwrap(), - FAKE_SRC_BASE, + self.config.src_test_suite_root, FAKE_SRC_BASE, )); } @@ -1747,18 +1706,22 @@ impl<'test> TestCx<'test> { rustc.args(&self.props.compile_flags); // FIXME(jieyouxu): we should report a fatal error or warning if user wrote `-Cpanic=` with - // something that's not `abort`, however, by moving this last we should override previous - // `-Cpanic=`s + // something that's not `abort` and `-Cforce-unwind-tables` with a value that is not `yes`, + // however, by moving this last we should override previous `-Cpanic`s and + // `-Cforce-unwind-tables`s. Note that checking here is very fragile, because we'd have to + // account for all possible compile flag splittings (they have some... intricacies and are + // not yet normalized). // // `minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics. if self.props.add_core_stubs { rustc.arg("-Cpanic=abort"); + rustc.arg("-Cforce-unwind-tables=yes"); } rustc } - fn make_exe_name(&self) -> PathBuf { + fn make_exe_name(&self) -> Utf8PathBuf { // Using a single letter here to keep the path length down for // Windows. Some test names get very long. rustc creates `rcgu` // files with the module name appended to it which can more than @@ -1809,7 +1772,7 @@ impl<'test> TestCx<'test> { } } - fn make_cmdline(&self, command: &Command, libpath: &str) -> String { + fn make_cmdline(&self, command: &Command, libpath: &Utf8Path) -> String { use crate::util; // Linux and mac don't require adjusting the library search path @@ -1822,7 +1785,7 @@ impl<'test> TestCx<'test> { format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path)) } - format!("{} {:?}", lib_path_cmd_prefix(libpath), command) + format!("{} {:?}", lib_path_cmd_prefix(libpath.as_str()), command) } } @@ -1836,20 +1799,19 @@ impl<'test> TestCx<'test> { return; } - let path = Path::new(proc_name); + let path = Utf8Path::new(proc_name); let proc_name = if path.file_stem().is_some_and(|p| p == "rmake") { - OsString::from_iter( + String::from_iter( path.parent() .unwrap() .file_name() .into_iter() - .chain(Some(OsStr::new("/"))) + .chain(Some("/")) .chain(path.file_name()), ) } else { path.file_name().unwrap().into() }; - let proc_name = proc_name.to_string_lossy(); println!("------{proc_name} stdout------------------------------"); println!("{}", out); println!("------{proc_name} stderr------------------------------"); @@ -1859,18 +1821,18 @@ impl<'test> TestCx<'test> { fn dump_output_file(&self, out: &str, extension: &str) { let outfile = self.make_out_name(extension); - fs::write(&outfile, out).unwrap(); + fs::write(outfile.as_std_path(), out).unwrap(); } /// Creates a filename for output with the given extension. /// E.g., `/.../testname.revision.mode/testname.extension`. - fn make_out_name(&self, extension: &str) -> PathBuf { + fn make_out_name(&self, extension: &str) -> Utf8PathBuf { self.output_base_name().with_extension(extension) } /// Gets the directory where auxiliary files are written. /// E.g., `/.../testname.revision.mode/auxiliary/`. - fn aux_output_dir_name(&self) -> PathBuf { + fn aux_output_dir_name(&self) -> Utf8PathBuf { self.output_base_dir() .join("auxiliary") .with_extra_extension(self.config.mode.aux_dir_disambiguator()) @@ -1878,12 +1840,12 @@ impl<'test> TestCx<'test> { /// Gets the directory where auxiliary binaries are written. /// E.g., `/.../testname.revision.mode/auxiliary/bin`. - fn aux_bin_output_dir_name(&self) -> PathBuf { + fn aux_bin_output_dir_name(&self) -> Utf8PathBuf { self.aux_output_dir_name().join("bin") } /// Generates a unique name for the test, such as `testname.revision.mode`. - fn output_testname_unique(&self) -> PathBuf { + fn output_testname_unique(&self) -> Utf8PathBuf { output_testname_unique(self.config, self.testpaths, self.safe_revision()) } @@ -1896,14 +1858,14 @@ impl<'test> TestCx<'test> { /// Gets the absolute path to the directory where all output for the given /// test/revision should reside. /// E.g., `/path/to/build/host-tuple/test/ui/relative/testname.revision.mode/`. - fn output_base_dir(&self) -> PathBuf { + fn output_base_dir(&self) -> Utf8PathBuf { output_base_dir(self.config, self.testpaths, self.safe_revision()) } /// Gets the absolute path to the base filename used as output for the given /// test/revision. /// E.g., `/.../relative/testname.revision.mode/testname`. - fn output_base_name(&self) -> PathBuf { + fn output_base_name(&self) -> Utf8PathBuf { output_base_name(self.config, self.testpaths, self.safe_revision()) } @@ -1938,7 +1900,7 @@ impl<'test> TestCx<'test> { // codegen tests (using FileCheck) - fn compile_test_and_save_ir(&self) -> (ProcRes, PathBuf) { + fn compile_test_and_save_ir(&self) -> (ProcRes, Utf8PathBuf) { let output_path = self.output_base_name().with_extension("ll"); let input_file = &self.testpaths.file; let rustc = self.make_compile_args( @@ -1954,7 +1916,7 @@ impl<'test> TestCx<'test> { (proc_res, output_path) } - fn verify_with_filecheck(&self, output: &Path) -> ProcRes { + fn verify_with_filecheck(&self, output: &Utf8Path) -> ProcRes { let mut filecheck = Command::new(self.config.llvm_filecheck.as_ref().unwrap()); filecheck.arg("--input-file").arg(output).arg(&self.testpaths.file); @@ -1983,7 +1945,8 @@ impl<'test> TestCx<'test> { // Add custom flags supplied by the `filecheck-flags:` test header. filecheck.args(&self.props.filecheck_flags); - self.compose_and_run(filecheck, "", None, None) + // FIXME(jieyouxu): don't pass an empty Path + self.compose_and_run(filecheck, Utf8Path::new(""), None, None) } fn charset() -> &'static str { @@ -1991,7 +1954,7 @@ impl<'test> TestCx<'test> { if cfg!(target_os = "freebsd") { "ISO-8859-1" } else { "UTF-8" } } - fn compare_to_default_rustdoc(&mut self, out_dir: &Path) { + fn compare_to_default_rustdoc(&mut self, out_dir: &Utf8Path) { if !self.config.has_html_tidy { return; } @@ -2000,7 +1963,9 @@ impl<'test> TestCx<'test> { let suffix = self.safe_revision().map_or("nightly".into(), |path| path.to_owned() + "-nightly"); let compare_dir = output_base_dir(self.config, self.testpaths, Some(&suffix)); - remove_and_create_dir_all(&compare_dir); + remove_and_create_dir_all(&compare_dir).unwrap_or_else(|e| { + panic!("failed to remove and recreate output directory `{compare_dir}`: {e}") + }); // We need to create a new struct for the lifetimes on `config` to work. let new_rustdoc = TestCx { @@ -2143,12 +2108,8 @@ impl<'test> TestCx<'test> { }; } - fn get_lines>( - &self, - path: &P, - mut other_files: Option<&mut Vec>, - ) -> Vec { - let content = fs::read_to_string(&path).unwrap(); + fn get_lines(&self, path: &Utf8Path, mut other_files: Option<&mut Vec>) -> Vec { + let content = fs::read_to_string(path.as_std_path()).unwrap(); let mut ignore = false; content .lines() @@ -2194,8 +2155,8 @@ impl<'test> TestCx<'test> { for other_file in other_files { let mut path = self.testpaths.file.clone(); path.set_file_name(&format!("{}.rs", other_file)); - let path = fs::canonicalize(path).expect("failed to canonicalize"); - let normalized = path.to_str().unwrap().replace('\\', "/"); + let path = path.canonicalize_utf8().expect("failed to canonicalize"); + let normalized = path.as_str().replace('\\', "/"); files.insert(normalized, self.get_lines(&path, None)); } @@ -2379,26 +2340,24 @@ impl<'test> TestCx<'test> { let mut normalized = output.to_string(); - let mut normalize_path = |from: &Path, to: &str| { - let mut from = from.display().to_string(); - if json { - from = from.replace("\\", "\\\\"); - } - normalized = normalized.replace(&from, to); + let mut normalize_path = |from: &Utf8Path, to: &str| { + let from = if json { &from.as_str().replace("\\", "\\\\") } else { from.as_str() }; + + normalized = normalized.replace(from, to); }; let parent_dir = self.testpaths.file.parent().unwrap(); normalize_path(parent_dir, "$DIR"); if self.props.remap_src_base { - let mut remapped_parent_dir = PathBuf::from(FAKE_SRC_BASE); - if self.testpaths.relative_dir != Path::new("") { + let mut remapped_parent_dir = Utf8PathBuf::from(FAKE_SRC_BASE); + if self.testpaths.relative_dir != Utf8Path::new("") { remapped_parent_dir.push(&self.testpaths.relative_dir); } normalize_path(&remapped_parent_dir, "$DIR"); } - let base_dir = Path::new("/rustc/FAKE_PREFIX"); + let base_dir = Utf8Path::new("/rustc/FAKE_PREFIX"); // Fake paths into the libstd/libcore normalize_path(&base_dir.join("library"), "$SRC_DIR"); // `ui-fulldeps` tests can show paths to the compiler source when testing macros from @@ -2408,18 +2367,26 @@ impl<'test> TestCx<'test> { // Real paths into the libstd/libcore let rust_src_dir = &self.config.sysroot_base.join("lib/rustlib/src/rust"); - rust_src_dir.try_exists().expect(&*format!("{} should exists", rust_src_dir.display())); - let rust_src_dir = rust_src_dir.read_link().unwrap_or(rust_src_dir.to_path_buf()); + rust_src_dir.try_exists().expect(&*format!("{} should exists", rust_src_dir)); + let rust_src_dir = rust_src_dir.read_link_utf8().unwrap_or(rust_src_dir.to_path_buf()); normalize_path(&rust_src_dir.join("library"), "$SRC_DIR_REAL"); - // eg. /home/user/rust/build/x86_64-unknown-linux-gnu/test/ui - normalize_path(&self.config.build_test_suite_root, "$TEST_BUILD_DIR"); + // eg. + // /home/user/rust/build/x86_64-unknown-linux-gnu/test/ui//$name.$revision.$mode/ + normalize_path(&self.output_base_dir(), "$TEST_BUILD_DIR"); + // Same as above, but with a canonicalized path. + // This is required because some tests print canonical paths inside test build directory, + // so if the build directory is a symlink, normalization doesn't help. + // + // NOTE: There are also tests which print the non-canonical name, so we need both this and + // the above normalizations. + normalize_path(&self.output_base_dir().canonicalize_utf8().unwrap(), "$TEST_BUILD_DIR"); // eg. /home/user/rust/build normalize_path(&self.config.build_root, "$BUILD_DIR"); if json { // escaped newlines in json strings should be readable - // in the stderr files. There's no point int being correct, + // in the stderr files. There's no point in being correct, // since only humans process the stderr files. // Thus we just turn escaped newlines back into newlines. normalized = normalized.replace("\\n", "\n"); @@ -2434,6 +2401,18 @@ impl<'test> TestCx<'test> { .into_owned(); normalized = Self::normalize_platform_differences(&normalized); + + // Normalize long type name hash. + normalized = + static_regex!(r"\$TEST_BUILD_DIR/(?P[^\.]+).long-type-(?P\d+).txt") + .replace_all(&normalized, |caps: &Captures<'_>| { + format!( + "$TEST_BUILD_DIR/{filename}.long-type-$LONG_TYPE_HASH.txt", + filename = &caps["filename"] + ) + }) + .into_owned(); + normalized = normalized.replace("\t", "\\t"); // makes tabs visible // Remove test annotations like `//~ ERROR text` from the output, @@ -2536,7 +2515,7 @@ impl<'test> TestCx<'test> { .replace("\r\n", "\n") } - fn expected_output_path(&self, kind: &str) -> PathBuf { + fn expected_output_path(&self, kind: &str) -> Utf8PathBuf { let mut path = expected_output_path(&self.testpaths, self.revision, &self.config.compare_mode, kind); @@ -2565,19 +2544,18 @@ impl<'test> TestCx<'test> { } } - fn load_expected_output_from_path(&self, path: &Path) -> Result { - fs::read_to_string(path).map_err(|err| { - format!("failed to load expected output from `{}`: {}", path.display(), err) - }) + fn load_expected_output_from_path(&self, path: &Utf8Path) -> Result { + fs::read_to_string(path) + .map_err(|err| format!("failed to load expected output from `{}`: {}", path, err)) } - fn delete_file(&self, file: &Path) { + fn delete_file(&self, file: &Utf8Path) { if !file.exists() { // Deleting a nonexistent file would error. return; } - if let Err(e) = fs::remove_file(file) { - self.fatal(&format!("failed to delete `{}`: {}", file.display(), e,)); + if let Err(e) = fs::remove_file(file.as_std_path()) { + self.fatal(&format!("failed to delete `{}`: {}", file, e,)); } } @@ -2631,18 +2609,19 @@ impl<'test> TestCx<'test> { (expected, actual) }; - // Write the actual output to a file in build/ - let test_name = self.config.compare_mode.as_ref().map_or("", |m| m.to_str()); + // Write the actual output to a file in build directory. let actual_path = self .output_base_name() .with_extra_extension(self.revision.unwrap_or("")) - .with_extra_extension(test_name) + .with_extra_extension( + self.config.compare_mode.as_ref().map(|cm| cm.to_str()).unwrap_or(""), + ) .with_extra_extension(stream); if let Err(err) = fs::write(&actual_path, &actual) { - self.fatal(&format!("failed to write {stream} to `{actual_path:?}`: {err}",)); + self.fatal(&format!("failed to write {stream} to `{actual_path}`: {err}",)); } - println!("Saved the actual {stream} to {actual_path:?}"); + println!("Saved the actual {stream} to `{actual_path}`"); if !self.config.bless { if expected.is_empty() { @@ -2668,13 +2647,16 @@ impl<'test> TestCx<'test> { if !actual.is_empty() { if let Err(err) = fs::write(&expected_path, &actual) { - self.fatal(&format!("failed to write {stream} to `{expected_path:?}`: {err}")); + self.fatal(&format!("failed to write {stream} to `{expected_path}`: {err}")); } - println!("Blessing the {stream} of {test_name} in {expected_path:?}"); + println!( + "Blessing the {stream} of `{test_name}` as `{expected_path}`", + test_name = self.testpaths.file + ); } } - println!("\nThe actual {0} differed from the expected {0}.", stream); + println!("\nThe actual {stream} differed from the expected {stream}"); if self.config.bless { CompareOutcome::Blessed } else { CompareOutcome::Differed } } @@ -2683,8 +2665,8 @@ impl<'test> TestCx<'test> { fn show_diff( &self, stream: &str, - expected_path: &Path, - actual_path: &Path, + expected_path: &Utf8Path, + actual_path: &Utf8Path, expected: &str, actual: &str, actual_unnormalized: &str, @@ -2823,7 +2805,7 @@ impl<'test> TestCx<'test> { fs::create_dir_all(&incremental_dir).unwrap(); if self.config.verbose { - println!("init_incremental_test: incremental_dir={}", incremental_dir.display()); + println!("init_incremental_test: incremental_dir={incremental_dir}"); } } } @@ -2881,8 +2863,8 @@ impl ProcRes { #[derive(Debug)] enum TargetLocation { - ThisFile(PathBuf), - ThisDirectory(PathBuf), + ThisFile(Utf8PathBuf), + ThisDirectory(Utf8PathBuf), } enum AllowUnused { diff --git a/src/tools/compiletest/src/runtest/assembly.rs b/src/tools/compiletest/src/runtest/assembly.rs index 89d7de58c203c..91d4f620f7194 100644 --- a/src/tools/compiletest/src/runtest/assembly.rs +++ b/src/tools/compiletest/src/runtest/assembly.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use camino::Utf8PathBuf; use super::{AllowUnused, Emit, LinkToAux, ProcRes, TargetLocation, TestCx}; @@ -19,7 +19,7 @@ impl TestCx<'_> { } } - fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) { + fn compile_test_and_save_assembly(&self) -> (ProcRes, Utf8PathBuf) { // This works with both `--emit asm` (as default output name for the assembly) // and `ptx-linker` because the latter can write output at requested location. let output_path = self.output_base_name().with_extension("s"); diff --git a/src/tools/compiletest/src/runtest/codegen_units.rs b/src/tools/compiletest/src/runtest/codegen_units.rs index 6c866cbef21ab..8dfa8d18d1a0b 100644 --- a/src/tools/compiletest/src/runtest/codegen_units.rs +++ b/src/tools/compiletest/src/runtest/codegen_units.rs @@ -26,9 +26,7 @@ impl TestCx<'_> { .stdout .lines() .filter(|line| line.starts_with(PREFIX)) - .map(|line| { - line.replace(&self.testpaths.file.display().to_string(), "TEST_PATH").to_string() - }) + .map(|line| line.replace(&self.testpaths.file.as_str(), "TEST_PATH").to_string()) .map(|line| str_to_mono_item(&line, true)) .collect(); diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs index 56fc5baf5f248..41cfeaee35ffb 100644 --- a/src/tools/compiletest/src/runtest/coverage.rs +++ b/src/tools/compiletest/src/runtest/coverage.rs @@ -1,9 +1,9 @@ //! Code specific to the coverage test suites. use std::ffi::OsStr; -use std::path::{Path, PathBuf}; use std::process::Command; +use camino::{Utf8Path, Utf8PathBuf}; use glob::glob; use crate::common::{UI_COVERAGE, UI_COVERAGE_MAP}; @@ -11,7 +11,7 @@ use crate::runtest::{Emit, ProcRes, TestCx, WillExecute}; use crate::util::static_regex; impl<'test> TestCx<'test> { - fn coverage_dump_path(&self) -> &Path { + fn coverage_dump_path(&self) -> &Utf8Path { self.config .coverage_dump_path .as_deref() @@ -79,10 +79,8 @@ impl<'test> TestCx<'test> { std::fs::remove_file(&profdata_path).unwrap(); } - let proc_res = self.exec_compiled_test_general( - &[("LLVM_PROFILE_FILE", &profraw_path.to_str().unwrap())], - false, - ); + let proc_res = + self.exec_compiled_test_general(&[("LLVM_PROFILE_FILE", profraw_path.as_str())], false); if self.props.failure_status.is_some() { self.check_correct_failure_status(&proc_res); } else if !proc_res.status.success() { @@ -158,8 +156,8 @@ impl<'test> TestCx<'test> { /// `.profraw` files and doctest executables to the given vectors. fn run_doctests_for_coverage( &self, - profraw_paths: &mut Vec, - bin_paths: &mut Vec, + profraw_paths: &mut Vec, + bin_paths: &mut Vec, ) { // Put .profraw files and doctest executables in dedicated directories, // to make it easier to glob them all later. @@ -204,10 +202,9 @@ impl<'test> TestCx<'test> { self.fatal_proc_rec("rustdoc --test failed!", &proc_res) } - fn glob_iter(path: impl AsRef) -> impl Iterator { - let path_str = path.as_ref().to_str().unwrap(); - let iter = glob(path_str).unwrap(); - iter.map(Result::unwrap) + fn glob_iter(path: impl AsRef) -> impl Iterator { + let iter = glob(path.as_ref().as_str()).unwrap(); + iter.map(Result::unwrap).map(Utf8PathBuf::try_from).map(Result::unwrap) } // Find all profraw files in the profraw directory. diff --git a/src/tools/compiletest/src/runtest/crashes.rs b/src/tools/compiletest/src/runtest/crashes.rs index 885ed3b08faca..da1e74b4a56bc 100644 --- a/src/tools/compiletest/src/runtest/crashes.rs +++ b/src/tools/compiletest/src/runtest/crashes.rs @@ -15,7 +15,7 @@ impl TestCx<'_> { // if a test does not crash, consider it an error if proc_res.status.success() || matches!(proc_res.status.code(), Some(1 | 0)) { self.fatal(&format!( - "crashtest no longer crashes/triggers ICE, horray! Please give it a meaningful \ + "crashtest no longer crashes/triggers ICE, hooray! Please give it a meaningful \ name, add a doc-comment to the start of the test explaining why it exists and \ move it to tests/ui or wherever you see fit. Adding 'Fixes #' to your PR \ description ensures that the corresponding ticket is auto-closed upon merge. \ diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs index d9e5c3fa0d8fa..a4103c5b4a9a4 100644 --- a/src/tools/compiletest/src/runtest/debugger.rs +++ b/src/tools/compiletest/src/runtest/debugger.rs @@ -1,7 +1,8 @@ use std::fmt::Write; use std::fs::File; use std::io::{BufRead, BufReader}; -use std::path::{Path, PathBuf}; + +use camino::{Utf8Path, Utf8PathBuf}; use crate::common::Config; use crate::runtest::ProcRes; @@ -15,11 +16,15 @@ pub(super) struct DebuggerCommands { /// Contains the source line number to check and the line itself check_lines: Vec<(usize, String)>, /// Source file name - file: PathBuf, + file: Utf8PathBuf, } impl DebuggerCommands { - pub fn parse_from(file: &Path, config: &Config, debugger_prefix: &str) -> Result { + pub fn parse_from( + file: &Utf8Path, + config: &Config, + debugger_prefix: &str, + ) -> Result { let command_directive = format!("{debugger_prefix}-command"); let check_directive = format!("{debugger_prefix}-check"); @@ -27,7 +32,7 @@ impl DebuggerCommands { let mut commands = vec![]; let mut check_lines = vec![]; let mut counter = 0; - let reader = BufReader::new(File::open(file).unwrap()); + let reader = BufReader::new(File::open(file.as_std_path()).unwrap()); for (line_no, line) in reader.lines().enumerate() { counter += 1; let line = line.map_err(|e| format!("Error while parsing debugger commands: {}", e))?; @@ -50,7 +55,7 @@ impl DebuggerCommands { } } - Ok(Self { commands, breakpoint_lines, check_lines, file: file.to_owned() }) + Ok(Self { commands, breakpoint_lines, check_lines, file: file.to_path_buf() }) } /// Given debugger output and lines to check, ensure that every line is @@ -81,10 +86,10 @@ impl DebuggerCommands { if missing.is_empty() { Ok(()) } else { - let fname = self.file.file_name().unwrap().to_string_lossy(); + let fname = self.file.file_name().unwrap(); let mut msg = format!( "check directive(s) from `{}` not found in debugger output. errors:", - self.file.display() + self.file ); for (src_lineno, err_line) in missing { diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs index 170b8a8099687..31240dff9a196 100644 --- a/src/tools/compiletest/src/runtest/debuginfo.rs +++ b/src/tools/compiletest/src/runtest/debuginfo.rs @@ -1,9 +1,9 @@ use std::ffi::{OsStr, OsString}; use std::fs::File; use std::io::{BufRead, BufReader, Read}; -use std::path::Path; use std::process::{Command, Output, Stdio}; +use camino::Utf8Path; use tracing::debug; use super::debugger::DebuggerCommands; @@ -73,11 +73,11 @@ impl TestCx<'_> { let mut js_extension = self.testpaths.file.clone(); js_extension.set_extension("cdb.js"); if js_extension.exists() { - script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension.to_string_lossy())); + script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension)); } // Set breakpoints on every line that contains the string "#break" - let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy(); + let source_file_name = self.testpaths.file.file_name().unwrap(); for line in &dbg_cmds.breakpoint_lines { script_str.push_str(&format!("bp `{}:{}`\n", source_file_name, line)); } @@ -104,7 +104,7 @@ impl TestCx<'_> { let debugger_run_result = self.compose_and_run( cdb, - self.config.run_lib_path.to_str().unwrap(), + self.config.run_lib_path.as_path(), None, // aux_path None, // input ); @@ -151,16 +151,11 @@ impl TestCx<'_> { if is_android_gdb_target(&self.config.target) { cmds = cmds.replace("run", "continue"); - let tool_path = match self.config.android_cross_path.to_str() { - Some(x) => x.to_owned(), - None => self.fatal("cannot find android cross path"), - }; - // write debugger script let mut script_str = String::with_capacity(2048); script_str.push_str(&format!("set charset {}\n", Self::charset())); - script_str.push_str(&format!("set sysroot {}\n", tool_path)); - script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap())); + script_str.push_str(&format!("set sysroot {}\n", &self.config.android_cross_path)); + script_str.push_str(&format!("file {}\n", exe_file)); script_str.push_str("target remote :5039\n"); script_str.push_str(&format!( "set solib-search-path \ @@ -169,12 +164,8 @@ impl TestCx<'_> { )); for line in &dbg_cmds.breakpoint_lines { script_str.push_str( - format!( - "break {:?}:{}\n", - self.testpaths.file.file_name().unwrap().to_string_lossy(), - *line - ) - .as_str(), + format!("break {}:{}\n", self.testpaths.file.file_name().unwrap(), *line) + .as_str(), ); } script_str.push_str(&cmds); @@ -203,7 +194,7 @@ impl TestCx<'_> { self.config.adb_test_dir.clone(), if self.config.target.contains("aarch64") { "64" } else { "" }, self.config.adb_test_dir.clone(), - exe_file.file_name().unwrap().to_str().unwrap() + exe_file.file_name().unwrap() ); debug!("adb arg: {}", adb_arg); @@ -241,7 +232,8 @@ impl TestCx<'_> { let cmdline = { let mut gdb = Command::new(&format!("{}-gdb", self.config.target)); gdb.args(debugger_opts); - let cmdline = self.make_cmdline(&gdb, ""); + // FIXME(jieyouxu): don't pass an empty Path + let cmdline = self.make_cmdline(&gdb, Utf8Path::new("")); logv(self.config, format!("executing {}", cmdline)); cmdline }; @@ -258,7 +250,6 @@ impl TestCx<'_> { } } else { let rust_pp_module_abs_path = self.config.src_root.join("src").join("etc"); - let rust_pp_module_abs_path = rust_pp_module_abs_path.to_str().unwrap(); // write debugger script let mut script_str = String::with_capacity(2048); script_str.push_str(&format!("set charset {}\n", Self::charset())); @@ -273,17 +264,15 @@ impl TestCx<'_> { // GDB's script auto loading safe path script_str.push_str(&format!( "add-auto-load-safe-path {}\n", - rust_pp_module_abs_path.replace(r"\", r"\\") + rust_pp_module_abs_path.as_str().replace(r"\", r"\\") )); - let output_base_dir = self.output_base_dir().to_str().unwrap().to_owned(); - // Add the directory containing the output binary to // include embedded pretty printers to GDB's script // auto loading safe path script_str.push_str(&format!( "add-auto-load-safe-path {}\n", - output_base_dir.replace(r"\", r"\\") + self.output_base_dir().as_str().replace(r"\", r"\\") )); } } @@ -300,12 +289,13 @@ impl TestCx<'_> { script_str.push_str("set print pretty off\n"); // Add the pretty printer directory to GDB's source-file search path - script_str - .push_str(&format!("directory {}\n", rust_pp_module_abs_path.replace(r"\", r"\\"))); + script_str.push_str(&format!( + "directory {}\n", + rust_pp_module_abs_path.as_str().replace(r"\", r"\\") + )); // Load the target executable - script_str - .push_str(&format!("file {}\n", exe_file.to_str().unwrap().replace(r"\", r"\\"))); + script_str.push_str(&format!("file {}\n", exe_file.as_str().replace(r"\", r"\\"))); // Force GDB to print values in the Rust format. script_str.push_str("set language rust\n"); @@ -314,7 +304,7 @@ impl TestCx<'_> { for line in &dbg_cmds.breakpoint_lines { script_str.push_str(&format!( "break '{}':{}\n", - self.testpaths.file.file_name().unwrap().to_string_lossy(), + self.testpaths.file.file_name().unwrap(), *line )); } @@ -340,7 +330,7 @@ impl TestCx<'_> { gdb.args(debugger_opts).env("PYTHONPATH", pythonpath); debugger_run_result = - self.compose_and_run(gdb, self.config.run_lib_path.to_str().unwrap(), None, None); + self.compose_and_run(gdb, self.config.run_lib_path.as_path(), None, None); } if !debugger_run_result.status.success() { @@ -409,14 +399,14 @@ impl TestCx<'_> { script_str.push_str(&format!( "command script import {}/lldb_lookup.py\n", - rust_pp_module_abs_path.to_str().unwrap() + rust_pp_module_abs_path )); File::open(rust_pp_module_abs_path.join("lldb_commands")) .and_then(|mut file| file.read_to_string(&mut script_str)) .expect("Failed to read lldb_commands"); // Set breakpoints on every line that contains the string "#break" - let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy(); + let source_file_name = self.testpaths.file.file_name().unwrap(); for line in &dbg_cmds.breakpoint_lines { script_str.push_str(&format!( "breakpoint set --file '{}' --line {}\n", @@ -450,7 +440,7 @@ impl TestCx<'_> { } } - fn run_lldb(&self, test_executable: &Path, debugger_script: &Path) -> ProcRes { + fn run_lldb(&self, test_executable: &Utf8Path, debugger_script: &Utf8Path) -> ProcRes { // Prepare the lldb_batchmode which executes the debugger script let lldb_script_path = self.config.src_root.join("src/etc/lldb_batchmode.py"); let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") { diff --git a/src/tools/compiletest/src/runtest/incremental.rs b/src/tools/compiletest/src/runtest/incremental.rs index 591aff0defeb0..90cff6bab4dc6 100644 --- a/src/tools/compiletest/src/runtest/incremental.rs +++ b/src/tools/compiletest/src/runtest/incremental.rs @@ -65,7 +65,7 @@ impl TestCx<'_> { // FIXME(#41968): Move this check to tidy? if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() { - self.fatal("compile-pass tests with expected warnings should be moved to ui/"); + self.fatal("build-pass tests with expected warnings should be moved to ui/"); } } @@ -100,16 +100,8 @@ impl TestCx<'_> { self.check_no_compiler_crash(&proc_res, self.props.should_ice); let output_to_check = self.get_output(&proc_res); - let expected_errors = errors::load_errors(&self.testpaths.file, self.revision); - if !expected_errors.is_empty() { - if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty() - { - self.fatal("both error pattern and expected errors specified"); - } - self.check_expected_errors(expected_errors, &proc_res); - } else { - self.check_all_error_patterns(&output_to_check, &proc_res, pm); - } + self.check_expected_errors(&proc_res); + self.check_all_error_patterns(&output_to_check, &proc_res); if self.props.should_ice { match proc_res.status.code() { Some(101) => (), @@ -137,6 +129,6 @@ impl TestCx<'_> { let output_to_check = self.get_output(&proc_res); self.check_correct_failure_status(&proc_res); - self.check_all_error_patterns(&output_to_check, &proc_res, pm); + self.check_all_error_patterns(&output_to_check, &proc_res); } } diff --git a/src/tools/compiletest/src/runtest/js_doc.rs b/src/tools/compiletest/src/runtest/js_doc.rs index d630affbec104..fd53f01ca1746 100644 --- a/src/tools/compiletest/src/runtest/js_doc.rs +++ b/src/tools/compiletest/src/runtest/js_doc.rs @@ -9,8 +9,7 @@ impl TestCx<'_> { self.document(&out_dir, &self.testpaths); - let file_stem = - self.testpaths.file.file_stem().and_then(|f| f.to_str()).expect("no file stem"); + let file_stem = self.testpaths.file.file_stem().expect("no file stem"); let res = self.run_command_to_procres( Command::new(&nodejs) .arg(self.config.src_root.join("src/tools/rustdoc-js/tester.js")) diff --git a/src/tools/compiletest/src/runtest/mir_opt.rs b/src/tools/compiletest/src/runtest/mir_opt.rs index d1ec00357449d..efdb131bf14a8 100644 --- a/src/tools/compiletest/src/runtest/mir_opt.rs +++ b/src/tools/compiletest/src/runtest/mir_opt.rs @@ -1,6 +1,6 @@ use std::fs; -use std::path::{Path, PathBuf}; +use camino::{Utf8Path, Utf8PathBuf}; use glob::glob; use miropt_test_tools::{MiroptTest, MiroptTestFile, files_for_miropt_test}; use tracing::debug; @@ -14,7 +14,7 @@ impl TestCx<'_> { let should_run = self.should_run(pm); let mut test_info = files_for_miropt_test( - &self.testpaths.file, + &self.testpaths.file.as_std_path(), self.config.get_pointer_width(), self.config.target_cfg().panic.for_miropt_test_tools(), ); @@ -38,20 +38,15 @@ impl TestCx<'_> { fn check_mir_dump(&self, test_info: MiroptTest) { let test_dir = self.testpaths.file.parent().unwrap(); - let test_crate = - self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace('-', "_"); + let test_crate = self.testpaths.file.file_stem().unwrap().replace('-', "_"); let MiroptTest { run_filecheck, suffix, files, passes: _ } = test_info; if self.config.bless { - for e in - glob(&format!("{}/{}.*{}.mir", test_dir.display(), test_crate, suffix)).unwrap() - { + for e in glob(&format!("{}/{}.*{}.mir", test_dir, test_crate, suffix)).unwrap() { fs::remove_file(e.unwrap()).unwrap(); } - for e in - glob(&format!("{}/{}.*{}.diff", test_dir.display(), test_crate, suffix)).unwrap() - { + for e in glob(&format!("{}/{}.*{}.diff", test_dir, test_crate, suffix)).unwrap() { fs::remove_file(e.unwrap()).unwrap(); } } @@ -60,19 +55,15 @@ impl TestCx<'_> { 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()); + let mut output_file = Utf8PathBuf::new(); + output_file.push(self.output_base_dir()); output_file.push(&from_file); - debug!( - "comparing the contents of: {} with {}", - output_file.display(), - expected_file.display() - ); + debug!("comparing the contents of: {} with {:?}", output_file, expected_file); if !output_file.exists() { panic!( "Output file `{}` from test does not exist, available files are in `{}`", - output_file.display(), - output_file.parent().unwrap().display() + output_file, + output_file.parent().unwrap() ); } self.check_mir_test_timestamp(&from_file, &output_file); @@ -107,21 +98,20 @@ impl TestCx<'_> { } } - 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); + fn diff_mir_files(&self, before: Utf8PathBuf, after: Utf8PathBuf) -> String { + let to_full_path = |path: Utf8PathBuf| { + let full = self.output_base_dir().join(&path); if !full.exists() { panic!( "the mir dump file for {} does not exist (requested in {})", - path.display(), - self.testpaths.file.display(), + path, self.testpaths.file, ); } full }; let before = to_full_path(before); let after = to_full_path(after); - debug!("comparing the contents of: {} with {}", before.display(), after.display()); + debug!("comparing the contents of: {} with {}", before, after); let before = fs::read_to_string(before).unwrap(); let after = fs::read_to_string(after).unwrap(); let before = self.normalize_output(&before, &[]); @@ -138,8 +128,8 @@ impl TestCx<'_> { dumped_string } - fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) { - let t = |file| fs::metadata(file).unwrap().modified().unwrap(); + fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Utf8Path) { + let t = |file: &Utf8Path| fs::metadata(file.as_std_path()).unwrap().modified().unwrap(); let source_file = &self.testpaths.file; let output_time = t(output_file); let source_time = t(source_file); @@ -147,8 +137,7 @@ impl TestCx<'_> { debug!("source file time: {:?} output file time: {:?}", source_time, output_time); panic!( "test source file `{}` is newer than potentially stale output file `{}`.", - source_file.display(), - test_name + source_file, test_name ); } } diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index 9bb3993223e21..a5ce929f9b8e4 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -1,179 +1,18 @@ -use std::path::Path; use std::process::{Command, Output, Stdio}; use std::{env, fs}; use build_helper::fs::{ignore_not_found, recursive_remove}; +use camino::{Utf8Path, Utf8PathBuf}; use super::{ProcRes, TestCx, disable_error_reporting}; use crate::util::{copy_dir_all, dylib_env_var}; impl TestCx<'_> { pub(super) fn run_rmake_test(&self) { - let test_dir = &self.testpaths.file; - if test_dir.join("rmake.rs").exists() { - self.run_rmake_v2_test(); - } else if test_dir.join("Makefile").exists() { - self.run_rmake_legacy_test(); - } else { - self.fatal("failed to find either `rmake.rs` or `Makefile`") - } - } - - fn run_rmake_legacy_test(&self) { - let cwd = env::current_dir().unwrap(); - - // FIXME(Zalathar): This should probably be `output_base_dir` to avoid - // an unnecessary extra subdirectory, but since legacy Makefile tests - // are hopefully going away, it seems safer to leave this perilous code - // as-is until it can all be deleted. - let tmpdir = cwd.join(self.output_base_name()); - ignore_not_found(|| recursive_remove(&tmpdir)).unwrap(); - - fs::create_dir_all(&tmpdir).unwrap(); - - let host = &self.config.host; - let make = if host.contains("dragonfly") - || host.contains("freebsd") - || host.contains("netbsd") - || host.contains("openbsd") - || host.contains("aix") - { - "gmake" - } else { - "make" - }; - - let mut cmd = Command::new(make); - cmd.current_dir(&self.testpaths.file) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .env("TARGET", &self.config.target) - .env("PYTHON", &self.config.python) - .env("S", &self.config.src_root) - .env("RUST_BUILD_STAGE", &self.config.stage_id) - .env("RUSTC", cwd.join(&self.config.rustc_path)) - .env("TMPDIR", &tmpdir) - .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) - .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) - .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) - .env("LLVM_COMPONENTS", &self.config.llvm_components) - // We for sure don't want these tests to run in parallel, so make - // sure they don't have access to these vars if we run via `make` - // at the top level - .env_remove("MAKEFLAGS") - .env_remove("MFLAGS") - .env_remove("CARGO_MAKEFLAGS"); - - if let Some(ref cargo) = self.config.cargo_path { - cmd.env("CARGO", cwd.join(cargo)); - } - - if let Some(ref rustdoc) = self.config.rustdoc_path { - cmd.env("RUSTDOC", cwd.join(rustdoc)); - } - - if let Some(ref node) = self.config.nodejs { - cmd.env("NODE", node); - } - - if let Some(ref linker) = self.config.target_linker { - cmd.env("RUSTC_LINKER", linker); - } - - if let Some(ref clang) = self.config.run_clang_based_tests_with { - cmd.env("CLANG", clang); - } - - if let Some(ref filecheck) = self.config.llvm_filecheck { - cmd.env("LLVM_FILECHECK", filecheck); - } - - if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir { - cmd.env("LLVM_BIN_DIR", llvm_bin_dir); - } - - if let Some(ref remote_test_client) = self.config.remote_test_client { - cmd.env("REMOTE_TEST_CLIENT", remote_test_client); - } - - // We don't want RUSTFLAGS set from the outside to interfere with - // compiler flags set in the test cases: - cmd.env_remove("RUSTFLAGS"); - - // Use dynamic musl for tests because static doesn't allow creating dylibs - if self.config.host.contains("musl") { - 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.is_empty() { - // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` - // and that `lib.exe` lives next to it. - let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); - - // MSYS doesn't like passing flags of the form `/foo` as it thinks it's - // a path and instead passes `C:\msys64\foo`, so convert all - // `/`-arguments to MSVC here to `-` arguments. - let cflags = self - .config - .cflags - .split(' ') - .map(|s| s.replace("/", "-")) - .collect::>() - .join(" "); - let cxxflags = self - .config - .cxxflags - .split(' ') - .map(|s| s.replace("/", "-")) - .collect::>() - .join(" "); - - cmd.env("IS_MSVC", "1") - .env("IS_WINDOWS", "1") - .env("MSVC_LIB", format!("'{}' -nologo", lib.display())) - .env("MSVC_LIB_PATH", format!("{}", lib.display())) - .env("CC", format!("'{}' {}", self.config.cc, cflags)) - .env("CXX", format!("'{}' {}", &self.config.cxx, cxxflags)); - } else { - cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags)) - .env("CXX", format!("{} {}", self.config.cxx, self.config.cxxflags)) - .env("AR", &self.config.ar); - - if self.config.target.contains("windows") { - cmd.env("IS_WINDOWS", "1"); - } - } - - let (output, truncated) = - self.read2_abbreviated(cmd.spawn().expect("failed to spawn `make`")); - if !output.status.success() { - let res = ProcRes { - status: output.status, - stdout: String::from_utf8_lossy(&output.stdout).into_owned(), - stderr: String::from_utf8_lossy(&output.stderr).into_owned(), - truncated, - cmdline: format!("{:?}", cmd), - }; - self.fatal_proc_rec("make failed", &res); - } - } - - fn run_rmake_v2_test(&self) { // For `run-make` V2, we need to perform 2 steps to build and run a `run-make` V2 recipe // (`rmake.rs`) to run the actual tests. The support library is already built as a tool rust - // library and is available under `build/$TARGET/stageN-tools-bin/librun_make_support.rlib`. + // library and is available under + // `build/$HOST/stage0-bootstrap-tools/$TARGET/release/librun_make_support.rlib`. // // 1. We need to build the recipe `rmake.rs` as a binary and link in the `run_make_support` // library. @@ -191,8 +30,6 @@ impl TestCx<'_> { // recipes to `remove_dir_all($TMPDIR)` without running into issues related trying to remove // a currently running executable because the recipe executable is not under the // `rmake_out/` directory. - // - // This setup intentionally diverges from legacy Makefile run-make tests. let base_dir = self.output_base_dir(); ignore_not_found(|| recursive_remove(&base_dir)).unwrap(); @@ -202,14 +39,16 @@ impl TestCx<'_> { // Copy all input files (apart from rmake.rs) to the temporary directory, // so that the input directory structure from `tests/run-make/` is mirrored // to the `rmake_out` directory. - for path in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) { - let path = path.unwrap().path().to_path_buf(); + for entry in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) { + let entry = entry.unwrap(); + let path = entry.path(); + let path = <&Utf8Path>::try_from(path).unwrap(); if path.file_name().is_some_and(|s| s != "rmake.rs") { let target = rmake_out_dir.join(path.strip_prefix(&self.testpaths.file).unwrap()); if path.is_dir() { - copy_dir_all(&path, target).unwrap(); + copy_dir_all(&path, &target).unwrap(); } else { - fs::copy(&path, target).unwrap(); + fs::copy(path.as_std_path(), target).unwrap(); } } } @@ -224,25 +63,21 @@ impl TestCx<'_> { // // ``` // build// - // ├── stageN-tools-bin/ - // │ └── librun_make_support.rlib // <- support rlib itself - // ├── stageN-tools/ - // │ ├── release/deps/ // <- deps of deps - // │ └── /release/deps/ // <- deps + // ├── stage0-bootstrap-tools/ + // │ ├── /release/librun_make_support.rlib // <- support rlib itself + // │ ├── /release/deps/ // <- deps + // │ └── release/deps/ // <- deps of deps // ``` // // FIXME(jieyouxu): there almost certainly is a better way to do this (specifically how the - // support lib and its deps are organized, can't we copy them to the tools-bin dir as - // well?), but this seems to work for now. - - let stage_number = self.config.stage; + // support lib and its deps are organized), but this seems to work for now. - let stage_tools_bin = host_build_root.join(format!("stage{stage_number}-tools-bin")); - let support_lib_path = stage_tools_bin.join("librun_make_support.rlib"); + let tools_bin = host_build_root.join("stage0-bootstrap-tools"); + let support_host_path = tools_bin.join(&self.config.host).join("release"); + let support_lib_path = support_host_path.join("librun_make_support.rlib"); - let stage_tools = host_build_root.join(format!("stage{stage_number}-tools")); - let support_lib_deps = stage_tools.join(&self.config.host).join("release").join("deps"); - let support_lib_deps_deps = stage_tools.join("release").join("deps"); + let support_lib_deps = support_host_path.join("deps"); + let support_lib_deps_deps = tools_bin.join("release").join("deps"); // To compile the recipe with rustc, we need to provide suitable dynamic library search // paths to rustc. This includes both: @@ -250,14 +85,10 @@ impl TestCx<'_> { // on some linux distros. // 2. Specific library paths in `self.config.compile_lib_path` needed for running rustc. - let base_dylib_search_paths = - Vec::from_iter(env::split_paths(&env::var(dylib_env_var()).unwrap())); - - let host_dylib_search_paths = { - let mut paths = vec![self.config.compile_lib_path.clone()]; - paths.extend(base_dylib_search_paths.iter().cloned()); - paths - }; + let base_dylib_search_paths = Vec::from_iter( + env::split_paths(&env::var(dylib_env_var()).unwrap()) + .map(|p| Utf8PathBuf::try_from(p).expect("dylib env var contains non-UTF8 paths")), + ); // Calculate the paths of the recipe binary. As previously discussed, this is placed at // `/` with `bin_name` being `rmake` or `rmake.exe` depending on @@ -268,49 +99,39 @@ impl TestCx<'_> { p }; - let mut rustc = Command::new(&self.config.rustc_path); + // run-make-support and run-make tests are compiled using the stage0 compiler + // If the stage is 0, then the compiler that we test (either bootstrap or an explicitly + // set compiler) is the one that actually compiled run-make-support. + let stage0_rustc = self + .config + .stage0_rustc_path + .as_ref() + .expect("stage0 rustc is required to run run-make tests"); + let mut rustc = Command::new(&stage0_rustc); rustc + // `rmake.rs` **must** be buildable by a stable compiler, it may not use *any* unstable + // library or compiler features. Here, we force the stage 0 rustc to consider itself as + // a stable-channel compiler via `RUSTC_BOOTSTRAP=-1` to prevent *any* unstable + // library/compiler usages, even if stage 0 rustc is *actually* a nightly rustc. + .env("RUSTC_BOOTSTRAP", "-1") .arg("-o") .arg(&recipe_bin) // Specify library search paths for `run_make_support`. - .arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap().to_string_lossy())) - .arg(format!("-Ldependency={}", &support_lib_deps.to_string_lossy())) - .arg(format!("-Ldependency={}", &support_lib_deps_deps.to_string_lossy())) + .arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap())) + .arg(format!("-Ldependency={}", &support_lib_deps)) + .arg(format!("-Ldependency={}", &support_lib_deps_deps)) // Provide `run_make_support` as extern prelude, so test writers don't need to write // `extern run_make_support;`. .arg("--extern") - .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy())) + .arg(format!("run_make_support={}", &support_lib_path)) .arg("--edition=2021") .arg(&self.testpaths.file.join("rmake.rs")) - .arg("-Cprefer-dynamic") - // Provide necessary library search paths for rustc. - .env(dylib_env_var(), &env::join_paths(host_dylib_search_paths).unwrap()); + .arg("-Cprefer-dynamic"); // In test code we want to be very pedantic about values being silently discarded that are // annotated with `#[must_use]`. rustc.arg("-Dunused_must_use"); - // > `cg_clif` uses `COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0` for running the rustc - // > test suite. With the introduction of rmake.rs this broke. `librun_make_support.rlib` is - // > compiled using the bootstrap rustc wrapper which sets `--sysroot - // > build/aarch64-unknown-linux-gnu/stage0-sysroot`, but then compiletest will compile - // > `rmake.rs` using the sysroot of the bootstrap compiler causing it to not find the - // > `libstd.rlib` against which `librun_make_support.rlib` is compiled. - // - // The gist here is that we have to pass the proper stage0 sysroot if we want - // - // ``` - // $ COMPILETEST_FORCE_STAGE0=1 ./x test run-make --stage 0 - // ``` - // - // to work correctly. - // - // See for more background. - let stage0_sysroot = host_build_root.join("stage0-sysroot"); - if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { - rustc.arg("--sysroot").arg(&stage0_sysroot); - } - // Now run rustc to build the recipe. let res = self.run_command_to_procres(&mut rustc); if !res.status.success() { @@ -320,35 +141,24 @@ impl TestCx<'_> { // To actually run the recipe, we have to provide the recipe with a bunch of information // provided through env vars. - // Compute stage-specific standard library paths. - let stage_std_path = host_build_root.join(format!("stage{stage_number}")).join("lib"); - // Compute dynamic library search paths for recipes. + // These dylib directories are needed to **execute the recipe**. let recipe_dylib_search_paths = { let mut paths = base_dylib_search_paths.clone(); - - // For stage 0, we need to explicitly include the stage0-sysroot libstd dylib. - // See . - if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { - paths.push( - stage0_sysroot.join("lib").join("rustlib").join(&self.config.host).join("lib"), - ); - } - - paths.push(support_lib_path.parent().unwrap().to_path_buf()); - paths.push(stage_std_path.join("rustlib").join(&self.config.host).join("lib")); - paths - }; - - // Compute runtime library search paths for recipes. This is target-specific. - let target_runtime_dylib_search_paths = { - let mut paths = vec![rmake_out_dir.clone()]; - paths.extend(base_dylib_search_paths.iter().cloned()); + paths.push( + stage0_rustc + .parent() + .unwrap() + .parent() + .unwrap() + .join("lib") + .join("rustlib") + .join(&self.config.host) + .join("lib"), + ); paths }; - // FIXME(jieyouxu): please rename `TARGET_RPATH_ENV`, `HOST_RPATH_DIR` and - // `TARGET_RPATH_DIR`, it is **extremely** confusing! let mut cmd = Command::new(&recipe_bin); cmd.current_dir(&rmake_out_dir) .stdout(Stdio::piped()) @@ -357,9 +167,14 @@ impl TestCx<'_> { // example, this could be `LD_LIBRARY_PATH` on some linux distros but `PATH` on Windows. .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) // Provide the dylib search paths. + // This is required to run the **recipe** itself. .env(dylib_env_var(), &env::join_paths(recipe_dylib_search_paths).unwrap()) - // Provide runtime dylib search paths. - .env("TARGET_RPATH_ENV", &env::join_paths(target_runtime_dylib_search_paths).unwrap()) + // Provide the directory to libraries that are needed to run the *compiler* invoked + // by the recipe. + .env("HOST_RUSTC_DYLIB_PATH", &self.config.compile_lib_path) + // Provide the directory to libraries that might be needed to run binaries created + // by a compiler invoked by the recipe. + .env("TARGET_EXE_DYLIB_PATH", &self.config.run_lib_path) // Provide the target. .env("TARGET", &self.config.target) // Some tests unfortunately still need Python, so provide path to a Python interpreter. @@ -370,13 +185,6 @@ impl TestCx<'_> { .env("BUILD_ROOT", &host_build_root) // Provide path to stage-corresponding rustc. .env("RUSTC", &self.config.rustc_path) - // Provide the directory to libraries that are needed to run the *compiler*. This is not - // to be confused with `TARGET_RPATH_ENV` or `TARGET_RPATH_DIR`. This is needed if the - // recipe wants to invoke rustc. - .env("HOST_RPATH_DIR", &self.config.compile_lib_path) - // Provide the directory to libraries that might be needed to run compiled binaries - // (further compiled by the recipe!). - .env("TARGET_RPATH_DIR", &self.config.run_lib_path) // Provide which LLVM components are available (e.g. which LLVM components are provided // through a specific CI runner). .env("LLVM_COMPONENTS", &self.config.llvm_components); @@ -436,7 +244,7 @@ impl TestCx<'_> { if self.config.target.contains("msvc") && !self.config.cc.is_empty() { // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` // and that `lib.exe` lives next to it. - let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); + let lib = Utf8Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); // MSYS doesn't like passing flags of the form `/foo` as it thinks it's // a path and instead passes `C:\msys64\foo`, so convert all @@ -458,8 +266,8 @@ impl TestCx<'_> { cmd.env("IS_MSVC", "1") .env("IS_WINDOWS", "1") - .env("MSVC_LIB", format!("'{}' -nologo", lib.display())) - .env("MSVC_LIB_PATH", format!("{}", lib.display())) + .env("MSVC_LIB", format!("'{}' -nologo", lib)) + .env("MSVC_LIB_PATH", &lib) // Note: we diverge from legacy run_make and don't lump `CC` the compiler and // default flags together. .env("CC_DEFAULT_FLAGS", &cflags) diff --git a/src/tools/compiletest/src/runtest/rustdoc.rs b/src/tools/compiletest/src/runtest/rustdoc.rs index 2583ae96a6788..637ea833357a2 100644 --- a/src/tools/compiletest/src/runtest/rustdoc.rs +++ b/src/tools/compiletest/src/runtest/rustdoc.rs @@ -7,7 +7,9 @@ impl TestCx<'_> { assert!(self.revision.is_none(), "revisions not relevant here"); let out_dir = self.output_base_dir(); - remove_and_create_dir_all(&out_dir); + remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| { + panic!("failed to remove and recreate output directory `{out_dir}`: {e}") + }); let proc_res = self.document(&out_dir, &self.testpaths); if !proc_res.status.success() { diff --git a/src/tools/compiletest/src/runtest/rustdoc_json.rs b/src/tools/compiletest/src/runtest/rustdoc_json.rs index bf7eb2e109a46..9f88faca89268 100644 --- a/src/tools/compiletest/src/runtest/rustdoc_json.rs +++ b/src/tools/compiletest/src/runtest/rustdoc_json.rs @@ -9,7 +9,9 @@ impl TestCx<'_> { assert!(self.revision.is_none(), "revisions not relevant here"); let out_dir = self.output_base_dir(); - remove_and_create_dir_all(&out_dir); + remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| { + panic!("failed to remove and recreate output directory `{out_dir}`: {e}") + }); let proc_res = self.document(&out_dir, &self.testpaths); if !proc_res.status.success() { diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs index 3329e10745f8d..cc50a918f757a 100644 --- a/src/tools/compiletest/src/runtest/ui.rs +++ b/src/tools/compiletest/src/runtest/ui.rs @@ -6,10 +6,10 @@ use rustfix::{Filter, apply_suggestions, get_suggestions_from_json}; use tracing::debug; use super::{ - AllowUnused, Emit, ErrorKind, FailMode, LinkToAux, PassMode, TargetLocation, TestCx, - TestOutput, Truncated, UI_FIXED, WillExecute, + AllowUnused, Emit, FailMode, LinkToAux, PassMode, TargetLocation, TestCx, TestOutput, + Truncated, UI_FIXED, WillExecute, }; -use crate::{errors, json}; +use crate::json; impl TestCx<'_> { pub(super) fn run_ui_test(&self) { @@ -68,7 +68,7 @@ impl TestCx<'_> { { let mut coverage_file_path = self.config.build_test_suite_root.clone(); coverage_file_path.push("rustfix_missing_coverage.txt"); - debug!("coverage_file_path: {}", coverage_file_path.display()); + debug!("coverage_file_path: {}", coverage_file_path); let mut file = OpenOptions::new() .create(true) @@ -76,8 +76,8 @@ impl TestCx<'_> { .open(coverage_file_path.as_path()) .expect("could not create or open file"); - if let Err(e) = writeln!(file, "{}", self.testpaths.file.display()) { - panic!("couldn't write to {}: {e:?}", coverage_file_path.display()); + if let Err(e) = writeln!(file, "{}", self.testpaths.file) { + panic!("couldn't write to {}: {e:?}", coverage_file_path); } } } else if self.props.run_rustfix { @@ -119,7 +119,7 @@ impl TestCx<'_> { self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap()); println!( "To only update this specific test, also pass `--test-args {}`", - relative_path_to_file.display(), + relative_path_to_file, ); self.fatal_proc_rec( &format!("{} errors occurred comparing output.", errors), @@ -127,9 +127,7 @@ impl TestCx<'_> { ); } - let expected_errors = errors::load_errors(&self.testpaths.file, self.revision); - - if let WillExecute::Yes = should_run { + let output_to_check = if let WillExecute::Yes = should_run { let proc_res = self.exec_compiled_test(); let run_output_errors = if self.props.check_run_results { self.load_compare_outputs(&proc_res, TestOutput::Run, explicit) @@ -150,48 +148,19 @@ impl TestCx<'_> { self.fatal_proc_rec("test run succeeded!", &proc_res); } - let output_to_check = self.get_output(&proc_res); - if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty() - { - // "// error-pattern" comments - self.check_all_error_patterns(&output_to_check, &proc_res, pm); - } - self.check_forbid_output(&output_to_check, &proc_res) - } + self.get_output(&proc_res) + } else { + self.get_output(&proc_res) + }; debug!( - "run_ui_test: explicit={:?} config.compare_mode={:?} expected_errors={:?} \ + "run_ui_test: explicit={:?} config.compare_mode={:?} \ proc_res.status={:?} props.error_patterns={:?}", - explicit, - self.config.compare_mode, - expected_errors, - proc_res.status, - self.props.error_patterns + explicit, self.config.compare_mode, proc_res.status, self.props.error_patterns ); - let check_patterns = should_run == WillExecute::No - && (!self.props.error_patterns.is_empty() - || !self.props.regex_error_patterns.is_empty()); - if !explicit && self.config.compare_mode.is_none() { - let check_annotations = !check_patterns || !expected_errors.is_empty(); - - if check_annotations { - // "//~ERROR comments" - self.check_expected_errors(expected_errors, &proc_res); - } - } else if explicit && !expected_errors.is_empty() { - let msg = format!( - "line {}: cannot combine `--error-format` with {} annotations; use `error-pattern` instead", - expected_errors[0].line_num, - expected_errors[0].kind.unwrap_or(ErrorKind::Error), - ); - self.fatal(&msg); - } - let output_to_check = self.get_output(&proc_res); - if check_patterns { - // "// error-pattern" comments - self.check_all_error_patterns(&output_to_check, &proc_res, pm); - } + self.check_expected_errors(&proc_res); + self.check_all_error_patterns(&output_to_check, &proc_res); self.check_forbid_output(&output_to_check, &proc_res); if self.props.run_rustfix && self.config.compare_mode.is_none() { @@ -215,8 +184,6 @@ impl TestCx<'_> { let crate_name = self.testpaths.file.file_stem().expect("test must have a file stem"); // crate name must be alphanumeric or `_`. - let crate_name = - crate_name.to_str().expect("crate name implies file name must be valid UTF-8"); // replace `a.foo` -> `a__foo` for crate name purposes. // replace `revision-name-with-dashes` -> `revision_name_with_underscore` let crate_name = crate_name.replace('.', "__"); diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index 43c6dc0a67e89..e3e4a81755d09 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -1,5 +1,3 @@ -use std::ffi::OsString; - use crate::debuggers::{extract_gdb_version, extract_lldb_version}; use crate::is_test; @@ -60,11 +58,11 @@ fn test_extract_lldb_version() { #[test] fn is_test_test() { - assert!(is_test(&OsString::from("a_test.rs"))); - assert!(!is_test(&OsString::from(".a_test.rs"))); - assert!(!is_test(&OsString::from("a_cat.gif"))); - assert!(!is_test(&OsString::from("#a_dog_gif"))); - assert!(!is_test(&OsString::from("~a_temp_file"))); + assert!(is_test("a_test.rs")); + assert!(!is_test(".a_test.rs")); + assert!(!is_test("a_cat.gif")); + assert!(!is_test("#a_dog_gif")); + assert!(!is_test("~a_temp_file")); } #[test] diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index bff02f1db9f02..81f5679aead77 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -1,8 +1,7 @@ use std::env; -use std::ffi::OsStr; -use std::path::{Path, PathBuf}; use std::process::Command; +use camino::{Utf8Path, Utf8PathBuf}; use tracing::*; use crate::common::Config; @@ -34,21 +33,21 @@ pub fn logv(config: &Config, s: String) { } } -pub trait PathBufExt { +pub trait Utf8PathBufExt { /// Append an extension to the path, even if it already has one. - fn with_extra_extension>(&self, extension: S) -> PathBuf; + fn with_extra_extension(&self, extension: &str) -> Utf8PathBuf; } -impl PathBufExt for PathBuf { - fn with_extra_extension>(&self, extension: S) -> PathBuf { - if extension.as_ref().is_empty() { +impl Utf8PathBufExt for Utf8PathBuf { + fn with_extra_extension(&self, extension: &str) -> Utf8PathBuf { + if extension.is_empty() { self.clone() } else { - let mut fname = self.file_name().unwrap().to_os_string(); - if !extension.as_ref().to_str().unwrap().starts_with('.') { - fname.push("."); + let mut fname = self.file_name().unwrap().to_string(); + if !extension.starts_with('.') { + fname.push_str("."); } - fname.push(extension); + fname.push_str(extension); self.with_file_name(fname) } } @@ -71,22 +70,27 @@ pub fn dylib_env_var() -> &'static str { /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path. /// If the dylib_path_var is already set for this cmd, the old value will be overwritten! -pub fn add_dylib_path(cmd: &mut Command, paths: impl Iterator>) { +pub fn add_dylib_path( + cmd: &mut Command, + paths: impl Iterator>, +) { let path_env = env::var_os(dylib_env_var()); let old_paths = path_env.as_ref().map(env::split_paths); let new_paths = paths.map(Into::into).chain(old_paths.into_iter().flatten()); cmd.env(dylib_env_var(), env::join_paths(new_paths).unwrap()); } -pub fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> std::io::Result<()> { - std::fs::create_dir_all(&dst)?; - for entry in std::fs::read_dir(src)? { +pub fn copy_dir_all(src: &Utf8Path, dst: &Utf8Path) -> std::io::Result<()> { + std::fs::create_dir_all(dst.as_std_path())?; + for entry in std::fs::read_dir(src.as_std_path())? { let entry = entry?; + let path = Utf8PathBuf::try_from(entry.path()).unwrap(); + let file_name = path.file_name().unwrap(); let ty = entry.file_type()?; if ty.is_dir() { - copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; + copy_dir_all(&path, &dst.join(file_name))?; } else { - std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + std::fs::copy(path.as_std_path(), dst.join(file_name).as_std_path())?; } } Ok(()) diff --git a/src/tools/compiletest/src/util/tests.rs b/src/tools/compiletest/src/util/tests.rs index b09a183b14e6a..5bcae0dcee146 100644 --- a/src/tools/compiletest/src/util/tests.rs +++ b/src/tools/compiletest/src/util/tests.rs @@ -3,12 +3,12 @@ use super::*; #[test] fn path_buf_with_extra_extension_test() { assert_eq!( - PathBuf::from("foo.rs.stderr"), - PathBuf::from("foo.rs").with_extra_extension("stderr") + Utf8PathBuf::from("foo.rs.stderr"), + Utf8PathBuf::from("foo.rs").with_extra_extension("stderr") ); assert_eq!( - PathBuf::from("foo.rs.stderr"), - PathBuf::from("foo.rs").with_extra_extension(".stderr") + Utf8PathBuf::from("foo.rs.stderr"), + Utf8PathBuf::from("foo.rs").with_extra_extension(".stderr") ); - assert_eq!(PathBuf::from("foo.rs"), PathBuf::from("foo.rs").with_extra_extension("")); + assert_eq!(Utf8PathBuf::from("foo.rs"), Utf8PathBuf::from("foo.rs").with_extra_extension("")); } diff --git a/src/tools/coverage-dump/Cargo.toml b/src/tools/coverage-dump/Cargo.toml index 7f14286b5d0c4..36a66f16030ec 100644 --- a/src/tools/coverage-dump/Cargo.toml +++ b/src/tools/coverage-dump/Cargo.toml @@ -7,8 +7,9 @@ edition = "2021" [dependencies] anyhow = "1.0.71" +itertools = "0.12" leb128 = "0.2.5" md5 = { package = "md-5" , version = "0.10.5" } -miniz_oxide = "0.7.1" +miniz_oxide = "0.8.8" regex = "1.8.4" rustc-demangle = "0.1.23" diff --git a/src/tools/coverage-dump/src/covfun.rs b/src/tools/coverage-dump/src/covfun.rs index 82ebd33d0d1ca..1cc9f4dc5d6a3 100644 --- a/src/tools/coverage-dump/src/covfun.rs +++ b/src/tools/coverage-dump/src/covfun.rs @@ -1,23 +1,33 @@ use std::collections::HashMap; use std::fmt::{self, Debug, Write as _}; -use std::sync::OnceLock; +use std::sync::LazyLock; -use anyhow::{Context, anyhow}; +use anyhow::{Context, anyhow, bail, ensure}; +use itertools::Itertools; use regex::Regex; -use crate::parser::{Parser, unescape_llvm_string_contents}; +use crate::covmap::FilenameTables; +use crate::llvm_utils::unescape_llvm_string_contents; +use crate::parser::Parser; + +#[cfg(test)] +mod tests; pub(crate) fn dump_covfun_mappings( llvm_ir: &str, + filename_tables: &FilenameTables, function_names: &HashMap, ) -> anyhow::Result<()> { // Extract function coverage entries from the LLVM IR assembly, and associate // each entry with its (demangled) name. let mut covfun_entries = llvm_ir .lines() - .filter_map(covfun_line_data) - .map(|line_data| (function_names.get(&line_data.name_hash).map(String::as_str), line_data)) - .collect::>(); + .filter(|line| is_covfun_line(line)) + .map(parse_covfun_line) + .map_ok(|line_data| { + (function_names.get(&line_data.name_hash).map(String::as_str), line_data) + }) + .collect::, _>>()?; covfun_entries.sort_by(|a, b| { // Sort entries primarily by name, to help make the order consistent // across platforms and relatively insensitive to changes. @@ -41,8 +51,12 @@ pub(crate) fn dump_covfun_mappings( println!("Number of files: {num_files}"); for i in 0..num_files { - let global_file_id = parser.read_uleb128_u32()?; - println!("- file {i} => global file {global_file_id}"); + let global_file_id = parser.read_uleb128_usize()?; + let &CovfunLineData { filenames_hash, .. } = line_data; + let Some(filename) = filename_tables.lookup(filenames_hash, global_file_id) else { + bail!("couldn't resolve global file: {filenames_hash}, {global_file_id}"); + }; + println!("- file {i} => {filename}"); } let num_expressions = parser.read_uleb128_u32()?; @@ -107,36 +121,50 @@ pub(crate) fn dump_covfun_mappings( Ok(()) } +#[derive(Debug, PartialEq, Eq)] struct CovfunLineData { - name_hash: u64, is_used: bool, + name_hash: u64, + filenames_hash: u64, payload: Vec, } -/// Checks a line of LLVM IR assembly to see if it contains an `__llvm_covfun` -/// entry, and if so extracts relevant data in a `CovfunLineData`. -fn covfun_line_data(line: &str) -> Option { - let re = { - // We cheat a little bit and match variable names `@__covrec_[HASH]u` - // rather than the section name, because the section name is harder to - // extract and differs across Linux/Windows/macOS. We also extract the - // symbol name hash from the variable name rather than the data, since - // it's easier and both should match. - static RE: OnceLock = OnceLock::new(); - RE.get_or_init(|| { - Regex::new( - r#"^@__covrec_(?[0-9A-Z]+)(?u)? = .*\[[0-9]+ x i8\] c"(?[^"]*)".*$"#, - ) - .unwrap() - }) - }; +fn is_covfun_line(line: &str) -> bool { + line.starts_with("@__covrec_") +} - let captures = re.captures(line)?; - let name_hash = u64::from_str_radix(&captures["name_hash"], 16).unwrap(); +/// Given a line of LLVM IR assembly that should contain an `__llvm_covfun` +/// entry, parses it to extract relevant data in a `CovfunLineData`. +fn parse_covfun_line(line: &str) -> anyhow::Result { + ensure!(is_covfun_line(line)); + + // We cheat a little bit and match variable names `@__covrec_[HASH]u` + // rather than the section name, because the section name is harder to + // extract and differs across Linux/Windows/macOS. + const RE_STRING: &str = r#"(?x)^ + @__covrec_[0-9A-Z]+(?u)? + \ = \ # (trailing space) + .* + <\{ + \ i64 \ (? -? [0-9]+), + \ i32 \ -? [0-9]+, # (length of payload; currently unused) + \ i64 \ -? [0-9]+, # (source hash; currently unused) + \ i64 \ (? -? [0-9]+), + \ \[ [0-9]+ \ x \ i8 \] \ c"(?[^"]*)" + \ # (trailing space) + }> + .*$ + "#; + static RE: LazyLock = LazyLock::new(|| Regex::new(RE_STRING).unwrap()); + + let captures = + RE.captures(line).with_context(|| format!("couldn't parse covfun line: {line:?}"))?; let is_used = captures.name("is_used").is_some(); + let name_hash = i64::from_str_radix(&captures["name_hash"], 10).unwrap() as u64; + let filenames_hash = i64::from_str_radix(&captures["filenames_hash"], 10).unwrap() as u64; let payload = unescape_llvm_string_contents(&captures["payload"]); - Some(CovfunLineData { name_hash, is_used, payload }) + Ok(CovfunLineData { is_used, name_hash, filenames_hash, payload }) } // Extra parser methods only needed when parsing `covfun` payloads. diff --git a/src/tools/coverage-dump/src/covfun/tests.rs b/src/tools/coverage-dump/src/covfun/tests.rs new file mode 100644 index 0000000000000..1ce833784bd45 --- /dev/null +++ b/src/tools/coverage-dump/src/covfun/tests.rs @@ -0,0 +1,53 @@ +use super::{CovfunLineData, parse_covfun_line}; + +/// Integers in LLVM IR are not inherently signed/unsigned, and the text format tends +/// to emit them in signed form, so this helper function converts `i64` to `u64`. +fn as_u64(x: i64) -> u64 { + x as u64 +} + +#[test] +fn parse_covfun_line_data() { + struct Case { + line: &'static str, + expected: CovfunLineData, + } + let cases = &[ + // Copied from `trivial.ll`: + Case { + line: r#"@__covrec_49A9BAAE5F896E81u = linkonce_odr hidden constant <{ i64, i32, i64, i64, [9 x i8] }> <{ i64 5307978893922758273, i32 9, i64 445092354169400020, i64 6343436898695299756, [9 x i8] c"\01\01\00\01\01\03\01\00\0D" }>, section "__LLVM_COV,__llvm_covfun", align 8"#, + expected: CovfunLineData { + is_used: true, + name_hash: as_u64(5307978893922758273), + filenames_hash: as_u64(6343436898695299756), + payload: b"\x01\x01\x00\x01\x01\x03\x01\x00\x0D".to_vec(), + }, + }, + // Copied from `on-off-sandwich.ll`: + Case { + line: r#"@__covrec_D0CE53C5E64F319Au = linkonce_odr hidden constant <{ i64, i32, i64, i64, [14 x i8] }> <{ i64 -3400688559180533350, i32 14, i64 7307957714577672185, i64 892196767019953100, [14 x i8] c"\01\01\00\02\01\10\05\02\10\01\07\05\00\06" }>, section "__LLVM_COV,__llvm_covfun", align 8"#, + expected: CovfunLineData { + is_used: true, + name_hash: as_u64(-3400688559180533350), + filenames_hash: as_u64(892196767019953100), + payload: b"\x01\x01\x00\x02\x01\x10\x05\x02\x10\x01\x07\x05\x00\x06".to_vec(), + }, + }, + // Copied from `no-core.ll`: + Case { + line: r#"@__covrec_F8016FC82D46106u = linkonce_odr hidden constant <{ i64, i32, i64, i64, [9 x i8] }> <{ i64 1116917981370409222, i32 9, i64 -8857254680411629915, i64 -3625186110715410276, [9 x i8] c"\01\01\00\01\01\0C\01\00\0D" }>, section "__LLVM_COV,__llvm_covfun", align 8"#, + expected: CovfunLineData { + is_used: true, + name_hash: as_u64(1116917981370409222), + filenames_hash: as_u64(-3625186110715410276), + payload: b"\x01\x01\x00\x01\x01\x0C\x01\x00\x0D".to_vec(), + }, + }, + ]; + + for &Case { line, ref expected } in cases { + println!("- {line}"); + let line_data = parse_covfun_line(line).map_err(|e| e.to_string()); + assert_eq!(line_data.as_ref(), Ok(expected)); + } +} diff --git a/src/tools/coverage-dump/src/covmap.rs b/src/tools/coverage-dump/src/covmap.rs new file mode 100644 index 0000000000000..2246ca2d57574 --- /dev/null +++ b/src/tools/coverage-dump/src/covmap.rs @@ -0,0 +1,75 @@ +use std::collections::HashMap; +use std::sync::LazyLock; + +use anyhow::{Context, ensure}; +use regex::Regex; + +use crate::llvm_utils::{truncated_md5, unescape_llvm_string_contents}; +use crate::parser::Parser; + +#[derive(Debug, Default)] +pub(crate) struct FilenameTables { + map: HashMap>, +} + +impl FilenameTables { + pub(crate) fn lookup(&self, filenames_hash: u64, global_file_id: usize) -> Option<&str> { + let table = self.map.get(&filenames_hash)?; + let filename = table.get(global_file_id)?; + Some(filename) + } +} + +struct CovmapLineData { + payload: Vec, +} + +pub(crate) fn make_filename_tables(llvm_ir: &str) -> anyhow::Result { + let mut map = HashMap::default(); + + for line in llvm_ir.lines().filter(|line| is_covmap_line(line)) { + let CovmapLineData { payload } = parse_covmap_line(line)?; + + let mut parser = Parser::new(&payload); + let n_filenames = parser.read_uleb128_usize()?; + let uncompressed_bytes = parser.read_chunk_to_uncompressed_bytes()?; + parser.ensure_empty()?; + + let mut filenames_table = vec![]; + + let mut parser = Parser::new(&uncompressed_bytes); + for _ in 0..n_filenames { + let len = parser.read_uleb128_usize()?; + let bytes = parser.read_n_bytes(len)?; + let filename = str::from_utf8(bytes)?; + filenames_table.push(filename.to_owned()); + } + + let filenames_hash = truncated_md5(&payload); + map.insert(filenames_hash, filenames_table); + } + + Ok(FilenameTables { map }) +} + +fn is_covmap_line(line: &str) -> bool { + line.starts_with("@__llvm_coverage_mapping ") +} + +fn parse_covmap_line(line: &str) -> anyhow::Result { + ensure!(is_covmap_line(line)); + + const RE_STRING: &str = r#"(?x)^ + @__llvm_coverage_mapping \ = + .* + \[ [0-9]+ \ x \ i8 \] \ c"(?[^"]*)" + .*$ + "#; + static RE: LazyLock = LazyLock::new(|| Regex::new(RE_STRING).unwrap()); + + let captures = + RE.captures(line).with_context(|| format!("couldn't parse covmap line: {line:?}"))?; + let payload = unescape_llvm_string_contents(&captures["payload"]); + + Ok(CovmapLineData { payload }) +} diff --git a/src/tools/coverage-dump/src/llvm_utils.rs b/src/tools/coverage-dump/src/llvm_utils.rs new file mode 100644 index 0000000000000..92322b256a828 --- /dev/null +++ b/src/tools/coverage-dump/src/llvm_utils.rs @@ -0,0 +1,85 @@ +use std::borrow::Cow; +use std::sync::OnceLock; + +use anyhow::{anyhow, ensure}; +use regex::bytes; + +use crate::parser::Parser; + +#[cfg(test)] +mod tests; + +/// Given the raw contents of a string literal in LLVM IR assembly, decodes any +/// backslash escapes and returns a vector containing the resulting byte string. +pub(crate) fn unescape_llvm_string_contents(contents: &str) -> Vec { + let escape_re = { + static RE: OnceLock = OnceLock::new(); + // LLVM IR supports two string escapes: `\\` and `\xx`. + RE.get_or_init(|| bytes::Regex::new(r"\\\\|\\([0-9A-Za-z]{2})").unwrap()) + }; + + fn u8_from_hex_digits(digits: &[u8]) -> u8 { + // We know that the input contains exactly 2 hex digits, so these calls + // should never fail. + assert_eq!(digits.len(), 2); + let digits = std::str::from_utf8(digits).unwrap(); + u8::from_str_radix(digits, 16).unwrap() + } + + escape_re + .replace_all(contents.as_bytes(), |captures: &bytes::Captures<'_>| { + let byte = match captures.get(1) { + None => b'\\', + Some(hex_digits) => u8_from_hex_digits(hex_digits.as_bytes()), + }; + [byte] + }) + .into_owned() +} + +/// LLVM's profiler/coverage metadata often uses an MD5 hash truncated to +/// 64 bits as a way to associate data stored in different tables/sections. +pub(crate) fn truncated_md5(bytes: &[u8]) -> u64 { + use md5::{Digest, Md5}; + let mut hasher = Md5::new(); + hasher.update(bytes); + let hash: [u8; 8] = hasher.finalize().as_slice()[..8].try_into().unwrap(); + // The truncated hash is explicitly little-endian, regardless of host + // or target platform. (See `MD5Result::low` in LLVM's `MD5.h`.) + u64::from_le_bytes(hash) +} + +impl<'a> Parser<'a> { + /// Reads a sequence of: + /// - Length of uncompressed data in bytes, as ULEB128 + /// - Length of compressed data in bytes (or 0), as ULEB128 + /// - The indicated number of compressed or uncompressed bytes + /// + /// If the number of compressed bytes is 0, the subsequent bytes are + /// uncompressed. Otherwise, the subsequent bytes are compressed, and will + /// be decompressed. + /// + /// Returns the uncompressed bytes that were read directly or decompressed. + pub(crate) fn read_chunk_to_uncompressed_bytes(&mut self) -> anyhow::Result> { + let uncompressed_len = self.read_uleb128_usize()?; + let compressed_len = self.read_uleb128_usize()?; + + if compressed_len == 0 { + // The bytes are uncompressed, so read them directly. + let uncompressed_bytes = self.read_n_bytes(uncompressed_len)?; + Ok(Cow::Borrowed(uncompressed_bytes)) + } else { + // The bytes are compressed, so read and decompress them. + let compressed_bytes = self.read_n_bytes(compressed_len)?; + + let uncompressed_bytes = miniz_oxide::inflate::decompress_to_vec_zlib_with_limit( + compressed_bytes, + uncompressed_len, + ) + .map_err(|e| anyhow!("{e:?}"))?; + ensure!(uncompressed_bytes.len() == uncompressed_len); + + Ok(Cow::Owned(uncompressed_bytes)) + } + } +} diff --git a/src/tools/coverage-dump/src/llvm_utils/tests.rs b/src/tools/coverage-dump/src/llvm_utils/tests.rs new file mode 100644 index 0000000000000..506b0a6200bb1 --- /dev/null +++ b/src/tools/coverage-dump/src/llvm_utils/tests.rs @@ -0,0 +1,34 @@ +use super::unescape_llvm_string_contents; + +// Tests for `unescape_llvm_string_contents`: + +#[test] +fn unescape_empty() { + assert_eq!(unescape_llvm_string_contents(""), &[]); +} + +#[test] +fn unescape_noop() { + let input = "The quick brown fox jumps over the lazy dog."; + assert_eq!(unescape_llvm_string_contents(input), input.as_bytes()); +} + +#[test] +fn unescape_backslash() { + let input = r"\\Hello\\world\\"; + assert_eq!(unescape_llvm_string_contents(input), r"\Hello\world\".as_bytes()); +} + +#[test] +fn unescape_hex() { + let input = r"\01\02\03\04\0a\0b\0C\0D\fd\fE\FF"; + let expected: &[u8] = &[0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d, 0xfd, 0xfe, 0xff]; + assert_eq!(unescape_llvm_string_contents(input), expected); +} + +#[test] +fn unescape_mixed() { + let input = r"\\01.\5c\5c"; + let expected: &[u8] = br"\01.\\"; + assert_eq!(unescape_llvm_string_contents(input), expected); +} diff --git a/src/tools/coverage-dump/src/main.rs b/src/tools/coverage-dump/src/main.rs index b21e3e292f2b4..2c76d2f246022 100644 --- a/src/tools/coverage-dump/src/main.rs +++ b/src/tools/coverage-dump/src/main.rs @@ -1,4 +1,6 @@ mod covfun; +mod covmap; +mod llvm_utils; mod parser; mod prf_names; @@ -17,8 +19,9 @@ fn main() -> anyhow::Result<()> { let llvm_ir_path = args.get(1).context("LLVM IR file not specified")?; let llvm_ir = std::fs::read_to_string(llvm_ir_path).context("couldn't read LLVM IR file")?; + let filename_tables = covmap::make_filename_tables(&llvm_ir)?; let function_names = crate::prf_names::make_function_names_table(&llvm_ir)?; - crate::covfun::dump_covfun_mappings(&llvm_ir, &function_names)?; + crate::covfun::dump_covfun_mappings(&llvm_ir, &filename_tables, &function_names)?; Ok(()) } diff --git a/src/tools/coverage-dump/src/parser.rs b/src/tools/coverage-dump/src/parser.rs index 0bd4abdae3ef2..f26a57b43b331 100644 --- a/src/tools/coverage-dump/src/parser.rs +++ b/src/tools/coverage-dump/src/parser.rs @@ -1,38 +1,4 @@ -#[cfg(test)] -mod tests; - -use std::sync::OnceLock; - use anyhow::ensure; -use regex::bytes; - -/// Given the raw contents of a string literal in LLVM IR assembly, decodes any -/// backslash escapes and returns a vector containing the resulting byte string. -pub(crate) fn unescape_llvm_string_contents(contents: &str) -> Vec { - let escape_re = { - static RE: OnceLock = OnceLock::new(); - // LLVM IR supports two string escapes: `\\` and `\xx`. - RE.get_or_init(|| bytes::Regex::new(r"\\\\|\\([0-9A-Za-z]{2})").unwrap()) - }; - - fn u8_from_hex_digits(digits: &[u8]) -> u8 { - // We know that the input contains exactly 2 hex digits, so these calls - // should never fail. - assert_eq!(digits.len(), 2); - let digits = std::str::from_utf8(digits).unwrap(); - u8::from_str_radix(digits, 16).unwrap() - } - - escape_re - .replace_all(contents.as_bytes(), |captures: &bytes::Captures<'_>| { - let byte = match captures.get(1) { - None => b'\\', - Some(hex_digits) => u8_from_hex_digits(hex_digits.as_bytes()), - }; - [byte] - }) - .into_owned() -} pub(crate) struct Parser<'a> { rest: &'a [u8], diff --git a/src/tools/coverage-dump/src/parser/tests.rs b/src/tools/coverage-dump/src/parser/tests.rs deleted file mode 100644 index a673606b9c4c8..0000000000000 --- a/src/tools/coverage-dump/src/parser/tests.rs +++ /dev/null @@ -1,38 +0,0 @@ -use super::unescape_llvm_string_contents; - -// WARNING: These tests don't necessarily run in CI, and were mainly used to -// help track down problems when originally developing this tool. -// (The tool is still tested indirectly by snapshot tests that rely on it.) - -// Tests for `unescape_llvm_string_contents`: - -#[test] -fn unescape_empty() { - assert_eq!(unescape_llvm_string_contents(""), &[]); -} - -#[test] -fn unescape_noop() { - let input = "The quick brown fox jumps over the lazy dog."; - assert_eq!(unescape_llvm_string_contents(input), input.as_bytes()); -} - -#[test] -fn unescape_backslash() { - let input = r"\\Hello\\world\\"; - assert_eq!(unescape_llvm_string_contents(input), r"\Hello\world\".as_bytes()); -} - -#[test] -fn unescape_hex() { - let input = r"\01\02\03\04\0a\0b\0C\0D\fd\fE\FF"; - let expected: &[u8] = &[0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d, 0xfd, 0xfe, 0xff]; - assert_eq!(unescape_llvm_string_contents(input), expected); -} - -#[test] -fn unescape_mixed() { - let input = r"\\01.\5c\5c"; - let expected: &[u8] = br"\01.\\"; - assert_eq!(unescape_llvm_string_contents(input), expected); -} diff --git a/src/tools/coverage-dump/src/prf_names.rs b/src/tools/coverage-dump/src/prf_names.rs index 96d097c79a315..f9ab35deba505 100644 --- a/src/tools/coverage-dump/src/prf_names.rs +++ b/src/tools/coverage-dump/src/prf_names.rs @@ -1,10 +1,10 @@ use std::collections::HashMap; use std::sync::OnceLock; -use anyhow::{anyhow, ensure}; use regex::Regex; -use crate::parser::{Parser, unescape_llvm_string_contents}; +use crate::llvm_utils::{truncated_md5, unescape_llvm_string_contents}; +use crate::parser::Parser; /// Scans through the contents of an LLVM IR assembly file to find `__llvm_prf_names` /// entries, decodes them, and creates a table that maps name hash values to @@ -25,18 +25,6 @@ pub(crate) fn make_function_names_table(llvm_ir: &str) -> anyhow::Result u64 { - use md5::{Digest, Md5}; - let mut hasher = Md5::new(); - hasher.update(bytes); - let hash: [u8; 8] = hasher.finalize().as_slice()[..8].try_into().unwrap(); - // The truncated hash is explicitly little-endian, regardless of host - // or target platform. (See `MD5Result::low` in LLVM's `MD5.h`.) - u64::from_le_bytes(hash) - } - fn demangle_if_able(symbol_name_bytes: &[u8]) -> anyhow::Result { // In practice, raw symbol names should always be ASCII. let symbol_name_str = std::str::from_utf8(symbol_name_bytes)?; @@ -54,26 +42,8 @@ pub(crate) fn make_function_names_table(llvm_ir: &str) -> anyhow::Result anyhow::Result Vec<&Value> { - jsonpath_lib::select(&self.value, path).unwrap() + jsonpath_rust::query::js_path_vals(path, &self.value).unwrap() } } diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 54249fbd9ae71..65ad38da98b08 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -154,9 +154,10 @@ impl CommandKind { static LINE_PATTERN: LazyLock = LazyLock::new(|| { RegexBuilder::new( r#" + ^\s* //@\s+ (?P!?) - (?P[A-Za-z]+(?:-[A-Za-z]+)*) + (?P[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*) (?P.*)$ "#, ) diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 791b231c27a06..8c9e4c8bb3a60 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -326,6 +326,7 @@ impl<'a> Validator<'a> { self.check_type(o); } } + GenericArgs::ReturnTypeNotation => {} } } diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs index 28deb7e7ceef6..dd0b4ac5601a7 100644 --- a/src/tools/jsondoclint/src/validator/tests.rs +++ b/src/tools/jsondoclint/src/validator/tests.rs @@ -42,6 +42,7 @@ fn errors_on_missing_links() { )]), paths: FxHashMap::default(), external_crates: FxHashMap::default(), + target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] }, format_version: rustdoc_json_types::FORMAT_VERSION, }; @@ -112,6 +113,7 @@ fn errors_on_local_in_paths_and_not_index() { }, )]), external_crates: FxHashMap::default(), + target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] }, format_version: rustdoc_json_types::FORMAT_VERSION, }; @@ -216,6 +218,7 @@ fn errors_on_missing_path() { ItemSummary { crate_id: 0, path: vec!["foo".to_owned()], kind: ItemKind::Module }, )]), external_crates: FxHashMap::default(), + target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] }, format_version: rustdoc_json_types::FORMAT_VERSION, }; @@ -259,6 +262,7 @@ fn checks_local_crate_id_is_correct() { )]), paths: FxHashMap::default(), external_crates: FxHashMap::default(), + target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] }, format_version: FORMAT_VERSION, }; check(&krate, &[]); diff --git a/src/tools/linkchecker/Cargo.toml b/src/tools/linkchecker/Cargo.toml index f1be6e9e749d4..7123d43eb564c 100644 --- a/src/tools/linkchecker/Cargo.toml +++ b/src/tools/linkchecker/Cargo.toml @@ -9,4 +9,4 @@ path = "main.rs" [dependencies] regex = "1" -html5ever = "0.27.0" +html5ever = "0.29.0" diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 19340b5d07a11..84cba3f8c4473 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -16,7 +16,7 @@ //! A few exceptions are allowed as there's known bugs in rustdoc, but this //! should catch the majority of "broken link" cases. -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::collections::{HashMap, HashSet}; use std::io::ErrorKind; use std::path::{Component, Path, PathBuf}; @@ -544,7 +544,7 @@ fn parse_html(source: &str, sink: Sink) -> Sink { let mut input = BufferQueue::default(); input.push_back(tendril.try_reinterpret().unwrap()); - let mut tok = Tokenizer::new(sink, TokenizerOpts::default()); + let tok = Tokenizer::new(sink, TokenizerOpts::default()); let _ = tok.feed(&mut input); assert!(input.is_empty()); tok.end(); @@ -554,8 +554,8 @@ fn parse_html(source: &str, sink: Sink) -> Sink { #[derive(Default)] struct AttrCollector { attr_name: &'static [u8], - base: Option, - found_attrs: Vec<(u64, String)>, + base: Cell>, + found_attrs: RefCell>, /// Tracks whether or not it is inside a pub struct Bar; + +//~? ERROR unopened HTML tag `script` diff --git a/tests/rustdoc-ui/remap-path-prefix-macro.rs b/tests/rustdoc-ui/remap-path-prefix-macro.rs new file mode 100644 index 0000000000000..1be22694b8cdc --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-macro.rs @@ -0,0 +1,9 @@ +// Regression test for "attempted to remap an already remapped filename" ICE in rustdoc +// when using --remap-path-prefix with macro rendering. +// + +//@ compile-flags:-Z unstable-options --remap-path-prefix={{src-base}}=remapped_path +//@ rustc-env:RUST_BACKTRACE=0 +//@ build-pass + +macro_rules! f(() => {}); diff --git a/tests/rustdoc-ui/scrape-examples/scrape-examples-fail-if-type-error.rs b/tests/rustdoc-ui/scrape-examples/scrape-examples-fail-if-type-error.rs index 4fb5c9ab36fa3..c8e82d3a20b58 100644 --- a/tests/rustdoc-ui/scrape-examples/scrape-examples-fail-if-type-error.rs +++ b/tests/rustdoc-ui/scrape-examples/scrape-examples-fail-if-type-error.rs @@ -5,3 +5,5 @@ pub fn foo() { INVALID_FUNC(); //~^ ERROR could not resolve path } + +//~? ERROR Compilation failed, aborting rustdoc diff --git a/tests/rustdoc-ui/scrape-examples/scrape-examples-wrong-options-1.rs b/tests/rustdoc-ui/scrape-examples/scrape-examples-wrong-options-1.rs index df7b41e20f6f1..3db7924dbe14b 100644 --- a/tests/rustdoc-ui/scrape-examples/scrape-examples-wrong-options-1.rs +++ b/tests/rustdoc-ui/scrape-examples/scrape-examples-wrong-options-1.rs @@ -1 +1,3 @@ //@ compile-flags: -Z unstable-options --scrape-examples-target-crate foobar + +//~? ERROR must use --scrape-examples-output-path and --scrape-examples-target-crate together diff --git a/tests/rustdoc-ui/scrape-examples/scrape-examples-wrong-options-2.rs b/tests/rustdoc-ui/scrape-examples/scrape-examples-wrong-options-2.rs index ef270a08f48bf..7b6a30291ce83 100644 --- a/tests/rustdoc-ui/scrape-examples/scrape-examples-wrong-options-2.rs +++ b/tests/rustdoc-ui/scrape-examples/scrape-examples-wrong-options-2.rs @@ -1 +1,3 @@ //@ compile-flags: -Z unstable-options --scrape-examples-output-path ex.calls + +//~? ERROR must use --scrape-examples-output-path and --scrape-examples-target-crate together diff --git a/tests/rustdoc-ui/track-diagnostics.rs b/tests/rustdoc-ui/track-diagnostics.rs index 5c950a11082f8..d18d26bf79432 100644 --- a/tests/rustdoc-ui/track-diagnostics.rs +++ b/tests/rustdoc-ui/track-diagnostics.rs @@ -7,4 +7,5 @@ struct A; struct B; -const S: A = B; + +pub const S: A = B; //~ ERROR mismatched types diff --git a/tests/rustdoc-ui/track-diagnostics.stderr b/tests/rustdoc-ui/track-diagnostics.stderr index 3d17570a7a2df..fb0d7b8664491 100644 --- a/tests/rustdoc-ui/track-diagnostics.stderr +++ b/tests/rustdoc-ui/track-diagnostics.stderr @@ -1,8 +1,8 @@ error[E0308]: mismatched types --> $DIR/track-diagnostics.rs:LL:CC | -LL | const S: A = B; - | ^ expected `A`, found `B` +LL | pub const S: A = B; + | ^ expected `A`, found `B` -Ztrack-diagnostics: created at compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs:LL:CC error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/unportable-markdown.rs b/tests/rustdoc-ui/unportable-markdown.rs deleted file mode 100644 index 105fc1e59d53a..0000000000000 --- a/tests/rustdoc-ui/unportable-markdown.rs +++ /dev/null @@ -1,62 +0,0 @@ -// https://internals.rust-lang.org/t/proposal-migrate-the-syntax-of-rustdoc-markdown-footnotes-to-be-compatible-with-the-syntax-used-in-github/18929 -// -// A series of test cases for CommonMark corner cases that pulldown-cmark 0.11 fixes. -// -// This version of the lint is targeted at two especially-common cases where docs got broken. -// Other differences in parsing should not warn. -#![allow(rustdoc::broken_intra_doc_links)] -#![deny(rustdoc::unportable_markdown)] - -/// -/// -/// Test footnote [^foot]. -/// -/// [^foot]: This is nested within the footnote now, but didn't used to be. -/// -/// This is a multi-paragraph footnote. -pub struct GfmFootnotes; - -/// -/// -/// test [^foo][^bar] -/// -/// [^foo]: test -/// [^bar]: test2 -pub struct FootnoteSmashedName; - -/// -/// -/// - _t -/// # test -/// t_ -pub struct NestingCornerCase; - -/// -/// -/// *~~__emphasis strike strong__~~* ~~*__strike emphasis strong__*~~ -pub struct Emphasis1; - -/// -/// -/// | -/// | -pub struct NotEnoughTable; - -/// -/// -/// foo -/// >bar -//~^ ERROR unportable markdown -pub struct BlockQuoteNoSpace; - -/// Negative test. -/// -/// foo -/// > bar -pub struct BlockQuoteSpace; - -/// Negative test. -/// -/// >bar -/// baz -pub struct BlockQuoteNoSpaceStart; diff --git a/tests/rustdoc-ui/unportable-markdown.stderr b/tests/rustdoc-ui/unportable-markdown.stderr deleted file mode 100644 index 952ae4bb6eec6..0000000000000 --- a/tests/rustdoc-ui/unportable-markdown.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: unportable markdown - --> $DIR/unportable-markdown.rs:48:5 - | -LL | /// >bar - | ^ - | - = help: confusing block quote with no space after the `>` marker -note: the lint level is defined here - --> $DIR/unportable-markdown.rs:8:9 - | -LL | #![deny(rustdoc::unportable_markdown)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: if the quote is intended, add a space - | -LL | /// > bar - | + -help: if it should not be a quote, escape it - | -LL | /// \>bar - | + - -error: aborting due to 1 previous error - diff --git a/tests/rustdoc-ui/use_both_out_dir_and_output_options.rs b/tests/rustdoc-ui/use_both_out_dir_and_output_options.rs index 62d3d955855e2..42847203d4890 100644 --- a/tests/rustdoc-ui/use_both_out_dir_and_output_options.rs +++ b/tests/rustdoc-ui/use_both_out_dir_and_output_options.rs @@ -1 +1,3 @@ //@ compile-flags: --output ./foo + +//~? ERROR cannot use both 'out-dir' and 'output' at once diff --git a/tests/rustdoc/anchor-id-duplicate-method-name-25001.rs b/tests/rustdoc/anchors/anchor-id-duplicate-method-name-25001.rs similarity index 100% rename from tests/rustdoc/anchor-id-duplicate-method-name-25001.rs rename to tests/rustdoc/anchors/anchor-id-duplicate-method-name-25001.rs diff --git a/tests/rustdoc/anchor-id-trait-method-15169.rs b/tests/rustdoc/anchors/anchor-id-trait-method-15169.rs similarity index 100% rename from tests/rustdoc/anchor-id-trait-method-15169.rs rename to tests/rustdoc/anchors/anchor-id-trait-method-15169.rs diff --git a/tests/rustdoc/anchor-id-trait-tymethod-28478.rs b/tests/rustdoc/anchors/anchor-id-trait-tymethod-28478.rs similarity index 100% rename from tests/rustdoc/anchor-id-trait-tymethod-28478.rs rename to tests/rustdoc/anchors/anchor-id-trait-tymethod-28478.rs diff --git a/tests/rustdoc/anchors.no_const_anchor.html b/tests/rustdoc/anchors/anchors.no_const_anchor.html similarity index 100% rename from tests/rustdoc/anchors.no_const_anchor.html rename to tests/rustdoc/anchors/anchors.no_const_anchor.html diff --git a/tests/rustdoc/anchors.no_const_anchor2.html b/tests/rustdoc/anchors/anchors.no_const_anchor2.html similarity index 100% rename from tests/rustdoc/anchors.no_const_anchor2.html rename to tests/rustdoc/anchors/anchors.no_const_anchor2.html diff --git a/tests/rustdoc/anchors.no_method_anchor.html b/tests/rustdoc/anchors/anchors.no_method_anchor.html similarity index 100% rename from tests/rustdoc/anchors.no_method_anchor.html rename to tests/rustdoc/anchors/anchors.no_method_anchor.html diff --git a/tests/rustdoc/anchors.no_trait_method_anchor.html b/tests/rustdoc/anchors/anchors.no_trait_method_anchor.html similarity index 100% rename from tests/rustdoc/anchors.no_trait_method_anchor.html rename to tests/rustdoc/anchors/anchors.no_trait_method_anchor.html diff --git a/tests/rustdoc/anchors.no_tymethod_anchor.html b/tests/rustdoc/anchors/anchors.no_tymethod_anchor.html similarity index 100% rename from tests/rustdoc/anchors.no_tymethod_anchor.html rename to tests/rustdoc/anchors/anchors.no_tymethod_anchor.html diff --git a/tests/rustdoc/anchors.no_type_anchor.html b/tests/rustdoc/anchors/anchors.no_type_anchor.html similarity index 100% rename from tests/rustdoc/anchors.no_type_anchor.html rename to tests/rustdoc/anchors/anchors.no_type_anchor.html diff --git a/tests/rustdoc/anchors.no_type_anchor2.html b/tests/rustdoc/anchors/anchors.no_type_anchor2.html similarity index 100% rename from tests/rustdoc/anchors.no_type_anchor2.html rename to tests/rustdoc/anchors/anchors.no_type_anchor2.html diff --git a/tests/rustdoc/anchors.rs b/tests/rustdoc/anchors/anchors.rs similarity index 100% rename from tests/rustdoc/anchors.rs rename to tests/rustdoc/anchors/anchors.rs diff --git a/tests/rustdoc/auxiliary/issue-86620-1.rs b/tests/rustdoc/anchors/auxiliary/issue-86620-1.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-86620-1.rs rename to tests/rustdoc/anchors/auxiliary/issue-86620-1.rs diff --git a/tests/rustdoc/disambiguate-anchors-32890.rs b/tests/rustdoc/anchors/disambiguate-anchors-32890.rs similarity index 100% rename from tests/rustdoc/disambiguate-anchors-32890.rs rename to tests/rustdoc/anchors/disambiguate-anchors-32890.rs diff --git a/tests/rustdoc/disambiguate-anchors-header-29449.rs b/tests/rustdoc/anchors/disambiguate-anchors-header-29449.rs similarity index 100% rename from tests/rustdoc/disambiguate-anchors-header-29449.rs rename to tests/rustdoc/anchors/disambiguate-anchors-header-29449.rs diff --git a/tests/rustdoc/method-anchor-in-blanket-impl-86620.rs b/tests/rustdoc/anchors/method-anchor-in-blanket-impl-86620.rs similarity index 100% rename from tests/rustdoc/method-anchor-in-blanket-impl-86620.rs rename to tests/rustdoc/anchors/method-anchor-in-blanket-impl-86620.rs diff --git a/tests/rustdoc/trait-impl-items-links-and-anchors.rs b/tests/rustdoc/anchors/trait-impl-items-links-and-anchors.rs similarity index 100% rename from tests/rustdoc/trait-impl-items-links-and-anchors.rs rename to tests/rustdoc/anchors/trait-impl-items-links-and-anchors.rs diff --git a/tests/rustdoc/anon-fn-params.rs b/tests/rustdoc/anon-fn-params.rs new file mode 100644 index 0000000000000..9af1af3d3fa3f --- /dev/null +++ b/tests/rustdoc/anon-fn-params.rs @@ -0,0 +1,25 @@ +// Test that we render the deprecated anonymous trait function parameters from Rust 2015 as +// underscores in order not to perpetuate it and for legibility. + +//@ edition: 2015 +#![expect(anonymous_parameters)] + +// Check the "local case" (HIR cleaning) // + +//@ has anon_fn_params/trait.Trait.html +pub trait Trait { + //@ has - '//*[@id="tymethod.required"]' 'fn required(_: Option, _: impl Fn(&str) -> bool)' + fn required(Option, impl Fn(&str) -> bool); + //@ has - '//*[@id="method.provided"]' 'fn provided(_: [i32; 2])' + fn provided([i32; 2]) {} +} + +// Check the "extern case" (middle cleaning) // + +//@ aux-build: ext-anon-fn-params.rs +extern crate ext_anon_fn_params; + +//@ has anon_fn_params/trait.ExtTrait.html +//@ has - '//*[@id="tymethod.required"]' 'fn required(_: Option, _: impl Fn(&str) -> bool)' +//@ has - '//*[@id="method.provided"]' 'fn provided(_: [i32; 2])' +pub use ext_anon_fn_params::Trait as ExtTrait; diff --git a/tests/rustdoc/assoc/assoc-fns.rs b/tests/rustdoc/assoc/assoc-fns.rs new file mode 100644 index 0000000000000..6ffbebc3d27ef --- /dev/null +++ b/tests/rustdoc/assoc/assoc-fns.rs @@ -0,0 +1,13 @@ +// Basic testing for associated functions (in traits, trait impls & inherent impls). + +//@ has assoc_fns/trait.Trait.html +pub trait Trait { + //@ has - '//*[@id="tymethod.required"]' 'fn required(first: i32, second: &str)' + fn required(first: i32, second: &str); + + //@ has - '//*[@id="method.provided"]' 'fn provided(only: ())' + fn provided(only: ()) {} + + //@ has - '//*[@id="tymethod.params_are_unnamed"]' 'fn params_are_unnamed(_: i32, _: u32)' + fn params_are_unnamed(_: i32, _: u32); +} diff --git a/tests/rustdoc/assoc-item-cast.rs b/tests/rustdoc/assoc/assoc-item-cast.rs similarity index 100% rename from tests/rustdoc/assoc-item-cast.rs rename to tests/rustdoc/assoc/assoc-item-cast.rs diff --git a/tests/rustdoc/assoc-type-bindings-20646.rs b/tests/rustdoc/assoc/assoc-type-bindings-20646.rs similarity index 100% rename from tests/rustdoc/assoc-type-bindings-20646.rs rename to tests/rustdoc/assoc/assoc-type-bindings-20646.rs diff --git a/tests/rustdoc/assoc-types.rs b/tests/rustdoc/assoc/assoc-types.rs similarity index 100% rename from tests/rustdoc/assoc-types.rs rename to tests/rustdoc/assoc/assoc-types.rs diff --git a/tests/rustdoc/auxiliary/cross-crate-hidden-assoc-trait-items.rs b/tests/rustdoc/assoc/auxiliary/cross-crate-hidden-assoc-trait-items.rs similarity index 100% rename from tests/rustdoc/auxiliary/cross-crate-hidden-assoc-trait-items.rs rename to tests/rustdoc/assoc/auxiliary/cross-crate-hidden-assoc-trait-items.rs diff --git a/tests/rustdoc/auxiliary/issue-20646.rs b/tests/rustdoc/assoc/auxiliary/issue-20646.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-20646.rs rename to tests/rustdoc/assoc/auxiliary/issue-20646.rs diff --git a/tests/rustdoc/auxiliary/issue-20727.rs b/tests/rustdoc/assoc/auxiliary/issue-20727.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-20727.rs rename to tests/rustdoc/assoc/auxiliary/issue-20727.rs diff --git a/tests/rustdoc/auxiliary/normalize-assoc-item.rs b/tests/rustdoc/assoc/auxiliary/normalize-assoc-item.rs similarity index 100% rename from tests/rustdoc/auxiliary/normalize-assoc-item.rs rename to tests/rustdoc/assoc/auxiliary/normalize-assoc-item.rs diff --git a/tests/rustdoc/cross-crate-hidden-assoc-trait-items.rs b/tests/rustdoc/assoc/cross-crate-hidden-assoc-trait-items.rs similarity index 100% rename from tests/rustdoc/cross-crate-hidden-assoc-trait-items.rs rename to tests/rustdoc/assoc/cross-crate-hidden-assoc-trait-items.rs diff --git a/tests/rustdoc/doc-assoc-item.rs b/tests/rustdoc/assoc/doc-assoc-item.rs similarity index 100% rename from tests/rustdoc/doc-assoc-item.rs rename to tests/rustdoc/assoc/doc-assoc-item.rs diff --git a/tests/rustdoc/inline-assoc-type-20727-bindings.rs b/tests/rustdoc/assoc/inline-assoc-type-20727-bindings.rs similarity index 100% rename from tests/rustdoc/inline-assoc-type-20727-bindings.rs rename to tests/rustdoc/assoc/inline-assoc-type-20727-bindings.rs diff --git a/tests/rustdoc/inline-assoc-type-20727-bounds-deref.rs b/tests/rustdoc/assoc/inline-assoc-type-20727-bounds-deref.rs similarity index 100% rename from tests/rustdoc/inline-assoc-type-20727-bounds-deref.rs rename to tests/rustdoc/assoc/inline-assoc-type-20727-bounds-deref.rs diff --git a/tests/rustdoc/inline-assoc-type-20727-bounds-index.rs b/tests/rustdoc/assoc/inline-assoc-type-20727-bounds-index.rs similarity index 100% rename from tests/rustdoc/inline-assoc-type-20727-bounds-index.rs rename to tests/rustdoc/assoc/inline-assoc-type-20727-bounds-index.rs diff --git a/tests/rustdoc/inline-assoc-type-20727-bounds.rs b/tests/rustdoc/assoc/inline-assoc-type-20727-bounds.rs similarity index 100% rename from tests/rustdoc/inline-assoc-type-20727-bounds.rs rename to tests/rustdoc/assoc/inline-assoc-type-20727-bounds.rs diff --git a/tests/rustdoc/normalize-assoc-item.rs b/tests/rustdoc/assoc/normalize-assoc-item.rs similarity index 100% rename from tests/rustdoc/normalize-assoc-item.rs rename to tests/rustdoc/assoc/normalize-assoc-item.rs diff --git a/tests/rustdoc/async-fn-opaque-item.rs b/tests/rustdoc/async/async-fn-opaque-item.rs similarity index 100% rename from tests/rustdoc/async-fn-opaque-item.rs rename to tests/rustdoc/async/async-fn-opaque-item.rs diff --git a/tests/rustdoc/async-fn.rs b/tests/rustdoc/async/async-fn.rs similarity index 100% rename from tests/rustdoc/async-fn.rs rename to tests/rustdoc/async/async-fn.rs diff --git a/tests/rustdoc/async-move-doctest.rs b/tests/rustdoc/async/async-move-doctest.rs similarity index 100% rename from tests/rustdoc/async-move-doctest.rs rename to tests/rustdoc/async/async-move-doctest.rs diff --git a/tests/rustdoc/async-trait-sig.rs b/tests/rustdoc/async/async-trait-sig.rs similarity index 100% rename from tests/rustdoc/async-trait-sig.rs rename to tests/rustdoc/async/async-trait-sig.rs diff --git a/tests/rustdoc/async-trait.rs b/tests/rustdoc/async/async-trait.rs similarity index 100% rename from tests/rustdoc/async-trait.rs rename to tests/rustdoc/async/async-trait.rs diff --git a/tests/rustdoc/auxiliary/async-trait-dep.rs b/tests/rustdoc/async/auxiliary/async-trait-dep.rs similarity index 100% rename from tests/rustdoc/auxiliary/async-trait-dep.rs rename to tests/rustdoc/async/auxiliary/async-trait-dep.rs diff --git a/tests/rustdoc/auto-impl-for-trait.rs b/tests/rustdoc/auto/auto-impl-for-trait.rs similarity index 100% rename from tests/rustdoc/auto-impl-for-trait.rs rename to tests/rustdoc/auto/auto-impl-for-trait.rs diff --git a/tests/rustdoc/auto-impl-primitive.rs b/tests/rustdoc/auto/auto-impl-primitive.rs similarity index 100% rename from tests/rustdoc/auto-impl-primitive.rs rename to tests/rustdoc/auto/auto-impl-primitive.rs diff --git a/tests/rustdoc/auto-trait-bounds-by-associated-type-50159.rs b/tests/rustdoc/auto/auto-trait-bounds-by-associated-type-50159.rs similarity index 100% rename from tests/rustdoc/auto-trait-bounds-by-associated-type-50159.rs rename to tests/rustdoc/auto/auto-trait-bounds-by-associated-type-50159.rs diff --git a/tests/rustdoc/auto-trait-bounds-inference-variables-54705.rs b/tests/rustdoc/auto/auto-trait-bounds-inference-variables-54705.rs similarity index 100% rename from tests/rustdoc/auto-trait-bounds-inference-variables-54705.rs rename to tests/rustdoc/auto/auto-trait-bounds-inference-variables-54705.rs diff --git a/tests/rustdoc/auto-trait-bounds-where-51236.rs b/tests/rustdoc/auto/auto-trait-bounds-where-51236.rs similarity index 100% rename from tests/rustdoc/auto-trait-bounds-where-51236.rs rename to tests/rustdoc/auto/auto-trait-bounds-where-51236.rs diff --git a/tests/rustdoc/auto-trait-negative-impl-55321.rs b/tests/rustdoc/auto/auto-trait-negative-impl-55321.rs similarity index 100% rename from tests/rustdoc/auto-trait-negative-impl-55321.rs rename to tests/rustdoc/auto/auto-trait-negative-impl-55321.rs diff --git a/tests/rustdoc/auto-trait-not-send.rs b/tests/rustdoc/auto/auto-trait-not-send.rs similarity index 100% rename from tests/rustdoc/auto-trait-not-send.rs rename to tests/rustdoc/auto/auto-trait-not-send.rs diff --git a/tests/rustdoc/auto-traits.rs b/tests/rustdoc/auto/auto-traits.rs similarity index 100% rename from tests/rustdoc/auto-traits.rs rename to tests/rustdoc/auto/auto-traits.rs diff --git a/tests/rustdoc/auto_aliases.rs b/tests/rustdoc/auto/auto_aliases.rs similarity index 100% rename from tests/rustdoc/auto_aliases.rs rename to tests/rustdoc/auto/auto_aliases.rs diff --git a/tests/rustdoc/auxiliary/auto-traits.rs b/tests/rustdoc/auto/auxiliary/auto-traits.rs similarity index 100% rename from tests/rustdoc/auxiliary/auto-traits.rs rename to tests/rustdoc/auto/auxiliary/auto-traits.rs diff --git a/tests/rustdoc/auxiliary/ext-anon-fn-params.rs b/tests/rustdoc/auxiliary/ext-anon-fn-params.rs new file mode 100644 index 0000000000000..1acb919ca64f5 --- /dev/null +++ b/tests/rustdoc/auxiliary/ext-anon-fn-params.rs @@ -0,0 +1,7 @@ +//@ edition: 2015 +#![expect(anonymous_parameters)] + +pub trait Trait { + fn required(Option, impl Fn(&str) -> bool); + fn provided([i32; 2]) {} +} diff --git a/tests/rustdoc/auxiliary/ext-trait-aliases.rs b/tests/rustdoc/auxiliary/ext-trait-aliases.rs new file mode 100644 index 0000000000000..8454c04063c12 --- /dev/null +++ b/tests/rustdoc/auxiliary/ext-trait-aliases.rs @@ -0,0 +1,13 @@ +#![feature(trait_alias)] + +pub trait ExtAlias0 = Copy + Iterator; + +pub trait ExtAlias1<'a, T: 'a + Clone, const N: usize> = From<[&'a T; N]>; + +pub trait ExtAlias2 = where T: From, String: Into; + +pub trait ExtAlias3 = Sized; + +pub trait ExtAlias4 = where Self: Sized; + +pub trait ExtAlias5 = ; diff --git a/tests/rustdoc/auxiliary/primitive-doc.rs b/tests/rustdoc/auxiliary/primitive-doc.rs deleted file mode 100644 index a7253ed2450b8..0000000000000 --- a/tests/rustdoc/auxiliary/primitive-doc.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ compile-flags: --crate-type lib --edition 2018 - -#![feature(rustc_attrs)] -#![feature(no_core)] -#![no_core] - -#[rustc_doc_primitive = "usize"] -/// This is the built-in type `usize`. -mod usize { -} diff --git a/tests/rustdoc/auxiliary/primitive-reexport.rs b/tests/rustdoc/auxiliary/primitive-reexport.rs deleted file mode 100644 index 18b57037634d2..0000000000000 --- a/tests/rustdoc/auxiliary/primitive-reexport.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ compile-flags: --emit metadata --crate-type lib --edition 2018 - -#![crate_name = "foo"] - -pub mod bar { - pub use bool; - pub use char as my_char; -} diff --git a/tests/rustdoc/auxiliary/trait-alias-mention.rs b/tests/rustdoc/auxiliary/trait-alias-mention.rs deleted file mode 100644 index 6df06c87a09d5..0000000000000 --- a/tests/rustdoc/auxiliary/trait-alias-mention.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![feature(trait_alias)] - -pub trait SomeAlias = std::fmt::Debug + std::marker::Copy; diff --git a/tests/rustdoc/assoc-consts-underscore.rs b/tests/rustdoc/constant/assoc-consts-underscore.rs similarity index 100% rename from tests/rustdoc/assoc-consts-underscore.rs rename to tests/rustdoc/constant/assoc-consts-underscore.rs diff --git a/tests/rustdoc/assoc-consts-version.rs b/tests/rustdoc/constant/assoc-consts-version.rs similarity index 100% rename from tests/rustdoc/assoc-consts-version.rs rename to tests/rustdoc/constant/assoc-consts-version.rs diff --git a/tests/rustdoc/assoc-consts.rs b/tests/rustdoc/constant/assoc-consts.rs similarity index 100% rename from tests/rustdoc/assoc-consts.rs rename to tests/rustdoc/constant/assoc-consts.rs diff --git a/tests/rustdoc/associated-consts.rs b/tests/rustdoc/constant/associated-consts.rs similarity index 100% rename from tests/rustdoc/associated-consts.rs rename to tests/rustdoc/constant/associated-consts.rs diff --git a/tests/rustdoc/const-display.rs b/tests/rustdoc/constant/const-display.rs similarity index 100% rename from tests/rustdoc/const-display.rs rename to tests/rustdoc/constant/const-display.rs diff --git a/tests/rustdoc/const-doc.rs b/tests/rustdoc/constant/const-doc.rs similarity index 100% rename from tests/rustdoc/const-doc.rs rename to tests/rustdoc/constant/const-doc.rs diff --git a/tests/rustdoc/const-effect-param.rs b/tests/rustdoc/constant/const-effect-param.rs similarity index 100% rename from tests/rustdoc/const-effect-param.rs rename to tests/rustdoc/constant/const-effect-param.rs diff --git a/tests/rustdoc/const-underscore.rs b/tests/rustdoc/constant/const-underscore.rs similarity index 100% rename from tests/rustdoc/const-underscore.rs rename to tests/rustdoc/constant/const-underscore.rs diff --git a/tests/rustdoc/const-value-display.rs b/tests/rustdoc/constant/const-value-display.rs similarity index 100% rename from tests/rustdoc/const-value-display.rs rename to tests/rustdoc/constant/const-value-display.rs diff --git a/tests/rustdoc/const.rs b/tests/rustdoc/constant/const.rs similarity index 100% rename from tests/rustdoc/const.rs rename to tests/rustdoc/constant/const.rs diff --git a/tests/rustdoc/document-item-with-associated-const-in-where-clause.rs b/tests/rustdoc/constant/document-item-with-associated-const-in-where-clause.rs similarity index 100% rename from tests/rustdoc/document-item-with-associated-const-in-where-clause.rs rename to tests/rustdoc/constant/document-item-with-associated-const-in-where-clause.rs diff --git a/tests/rustdoc/generic-const-items.rs b/tests/rustdoc/constant/generic-const-items.rs similarity index 100% rename from tests/rustdoc/generic-const-items.rs rename to tests/rustdoc/constant/generic-const-items.rs diff --git a/tests/rustdoc/generic_const_exprs.rs b/tests/rustdoc/constant/generic_const_exprs.rs similarity index 100% rename from tests/rustdoc/generic_const_exprs.rs rename to tests/rustdoc/constant/generic_const_exprs.rs diff --git a/tests/rustdoc/glob-shadowing-const.rs b/tests/rustdoc/constant/glob-shadowing-const.rs similarity index 100% rename from tests/rustdoc/glob-shadowing-const.rs rename to tests/rustdoc/constant/glob-shadowing-const.rs diff --git a/tests/rustdoc/hide-complex-unevaluated-const-arguments.rs b/tests/rustdoc/constant/hide-complex-unevaluated-const-arguments.rs similarity index 100% rename from tests/rustdoc/hide-complex-unevaluated-const-arguments.rs rename to tests/rustdoc/constant/hide-complex-unevaluated-const-arguments.rs diff --git a/tests/rustdoc/hide-complex-unevaluated-consts.rs b/tests/rustdoc/constant/hide-complex-unevaluated-consts.rs similarity index 100% rename from tests/rustdoc/hide-complex-unevaluated-consts.rs rename to tests/rustdoc/constant/hide-complex-unevaluated-consts.rs diff --git a/tests/rustdoc/ice-associated-const-equality-105952.rs b/tests/rustdoc/constant/ice-associated-const-equality-105952.rs similarity index 100% rename from tests/rustdoc/ice-associated-const-equality-105952.rs rename to tests/rustdoc/constant/ice-associated-const-equality-105952.rs diff --git a/tests/rustdoc/legacy-const-generic.rs b/tests/rustdoc/constant/legacy-const-generic.rs similarity index 100% rename from tests/rustdoc/legacy-const-generic.rs rename to tests/rustdoc/constant/legacy-const-generic.rs diff --git a/tests/rustdoc/link-assoc-const.rs b/tests/rustdoc/constant/link-assoc-const.rs similarity index 100% rename from tests/rustdoc/link-assoc-const.rs rename to tests/rustdoc/constant/link-assoc-const.rs diff --git a/tests/rustdoc/redirect-const.rs b/tests/rustdoc/constant/redirect-const.rs similarity index 100% rename from tests/rustdoc/redirect-const.rs rename to tests/rustdoc/constant/redirect-const.rs diff --git a/tests/rustdoc/rfc-2632-const-trait-impl.rs b/tests/rustdoc/constant/rfc-2632-const-trait-impl.rs similarity index 100% rename from tests/rustdoc/rfc-2632-const-trait-impl.rs rename to tests/rustdoc/constant/rfc-2632-const-trait-impl.rs diff --git a/tests/rustdoc/show-const-contents.rs b/tests/rustdoc/constant/show-const-contents.rs similarity index 100% rename from tests/rustdoc/show-const-contents.rs rename to tests/rustdoc/constant/show-const-contents.rs diff --git a/tests/rustdoc/deref/deref-methods-24686-target.rs b/tests/rustdoc/deref/deref-methods-24686-target.rs new file mode 100644 index 0000000000000..e019488ca8023 --- /dev/null +++ b/tests/rustdoc/deref/deref-methods-24686-target.rs @@ -0,0 +1,27 @@ +#![crate_name = "foo"] + +// test for https://github.com/rust-lang/rust/issues/24686 +use std::ops::Deref; + +pub struct Foo(T); +impl Foo { + pub fn get_i32(&self) -> i32 { self.0 } +} +impl Foo { + pub fn get_u32(&self) -> u32 { self.0 } +} + +// Note that the same href is used both on the method itself, +// and on the sidebar items. +//@ has foo/struct.Bar.html +//@ has - '//a[@href="#method.get_i32"]' 'get_i32' +//@ !has - '//a[@href="#method.get_u32"]' 'get_u32' +//@ count - '//ul[@class="block deref-methods"]//a' 1 +//@ count - '//a[@href="#method.get_i32"]' 2 +pub struct Bar(Foo); +impl Deref for Bar { + type Target = Foo; + fn deref(&self) -> &Foo { + &self.0 + } +} diff --git a/tests/rustdoc/doc-cfg-hide.rs b/tests/rustdoc/doc-cfg/doc-cfg-hide.rs similarity index 100% rename from tests/rustdoc/doc-cfg-hide.rs rename to tests/rustdoc/doc-cfg/doc-cfg-hide.rs diff --git a/tests/rustdoc/doc-cfg-implicit-gate.rs b/tests/rustdoc/doc-cfg/doc-cfg-implicit-gate.rs similarity index 100% rename from tests/rustdoc/doc-cfg-implicit-gate.rs rename to tests/rustdoc/doc-cfg/doc-cfg-implicit-gate.rs diff --git a/tests/rustdoc/doc-cfg-implicit.rs b/tests/rustdoc/doc-cfg/doc-cfg-implicit.rs similarity index 100% rename from tests/rustdoc/doc-cfg-implicit.rs rename to tests/rustdoc/doc-cfg/doc-cfg-implicit.rs diff --git a/tests/rustdoc/doc-cfg-inherit-from-module-79201.rs b/tests/rustdoc/doc-cfg/doc-cfg-inherit-from-module-79201.rs similarity index 100% rename from tests/rustdoc/doc-cfg-inherit-from-module-79201.rs rename to tests/rustdoc/doc-cfg/doc-cfg-inherit-from-module-79201.rs diff --git a/tests/rustdoc/doc-cfg-simplification.rs b/tests/rustdoc/doc-cfg/doc-cfg-simplification.rs similarity index 100% rename from tests/rustdoc/doc-cfg-simplification.rs rename to tests/rustdoc/doc-cfg/doc-cfg-simplification.rs diff --git a/tests/rustdoc/doc-cfg-target-feature.rs b/tests/rustdoc/doc-cfg/doc-cfg-target-feature.rs similarity index 100% rename from tests/rustdoc/doc-cfg-target-feature.rs rename to tests/rustdoc/doc-cfg/doc-cfg-target-feature.rs diff --git a/tests/rustdoc/doc-cfg-traits.rs b/tests/rustdoc/doc-cfg/doc-cfg-traits.rs similarity index 100% rename from tests/rustdoc/doc-cfg-traits.rs rename to tests/rustdoc/doc-cfg/doc-cfg-traits.rs diff --git a/tests/rustdoc/doc-cfg.rs b/tests/rustdoc/doc-cfg/doc-cfg.rs similarity index 100% rename from tests/rustdoc/doc-cfg.rs rename to tests/rustdoc/doc-cfg/doc-cfg.rs diff --git a/tests/rustdoc/doctest/auxiliary/doctest-runtool.rs b/tests/rustdoc/doctest/auxiliary/doctest-runtool.rs new file mode 100644 index 0000000000000..a21ae0664fe63 --- /dev/null +++ b/tests/rustdoc/doctest/auxiliary/doctest-runtool.rs @@ -0,0 +1,21 @@ +// For some reason on Windows, the PATH to the libstd dylib doesn't seem to +// carry over to running the runtool. +//@ no-prefer-dynamic + +use std::path::Path; +use std::process::Command; + +fn main() { + let args: Vec<_> = std::env::args().collect(); + eprintln!("{args:#?}"); + assert_eq!(args.len(), 4); + assert_eq!(args[1], "arg1"); + assert_eq!(args[2], "arg2 with space"); + let path = Path::new(&args[3]); + let output = Command::new(path).output().unwrap(); + // Should fail without env var. + assert!(!output.status.success()); + let output = Command::new(path).env("DOCTEST_RUNTOOL_CHECK", "xyz").output().unwrap(); + // Should pass with env var. + assert!(output.status.success()); +} diff --git a/tests/rustdoc/doctest/doctest-runtool.rs b/tests/rustdoc/doctest/doctest-runtool.rs new file mode 100644 index 0000000000000..c4fb02e5228cc --- /dev/null +++ b/tests/rustdoc/doctest/doctest-runtool.rs @@ -0,0 +1,13 @@ +// Tests that the --test-runtool argument works. + +//@ ignore-cross-compile +//@ aux-bin: doctest-runtool.rs +//@ compile-flags: --test +//@ compile-flags: --test-runtool=auxiliary/bin/doctest-runtool +//@ compile-flags: --test-runtool-arg=arg1 --test-runtool-arg +//@ compile-flags: 'arg2 with space' + +/// ``` +/// assert_eq!(std::env::var("DOCTEST_RUNTOOL_CHECK"), Ok("xyz".to_string())); +/// ``` +pub fn main() {} diff --git a/tests/rustdoc/auxiliary/enum-variant.rs b/tests/rustdoc/enum/auxiliary/enum-variant.rs similarity index 100% rename from tests/rustdoc/auxiliary/enum-variant.rs rename to tests/rustdoc/enum/auxiliary/enum-variant.rs diff --git a/tests/rustdoc/auxiliary/variant-struct.rs b/tests/rustdoc/enum/auxiliary/variant-struct.rs similarity index 100% rename from tests/rustdoc/auxiliary/variant-struct.rs rename to tests/rustdoc/enum/auxiliary/variant-struct.rs diff --git a/tests/rustdoc/enum-headings.rs b/tests/rustdoc/enum/enum-headings.rs similarity index 100% rename from tests/rustdoc/enum-headings.rs rename to tests/rustdoc/enum/enum-headings.rs diff --git a/tests/rustdoc/enum-non-exhaustive-108925.rs b/tests/rustdoc/enum/enum-non-exhaustive-108925.rs similarity index 100% rename from tests/rustdoc/enum-non-exhaustive-108925.rs rename to tests/rustdoc/enum/enum-non-exhaustive-108925.rs diff --git a/tests/rustdoc/enum-variant-doc-hidden-field-88600.rs b/tests/rustdoc/enum/enum-variant-doc-hidden-field-88600.rs similarity index 100% rename from tests/rustdoc/enum-variant-doc-hidden-field-88600.rs rename to tests/rustdoc/enum/enum-variant-doc-hidden-field-88600.rs diff --git a/tests/rustdoc/enum-variant-fields-heading.rs b/tests/rustdoc/enum/enum-variant-fields-heading.rs similarity index 100% rename from tests/rustdoc/enum-variant-fields-heading.rs rename to tests/rustdoc/enum/enum-variant-fields-heading.rs diff --git a/tests/rustdoc/enum-variant-fields-heading.variants.html b/tests/rustdoc/enum/enum-variant-fields-heading.variants.html similarity index 100% rename from tests/rustdoc/enum-variant-fields-heading.variants.html rename to tests/rustdoc/enum/enum-variant-fields-heading.variants.html diff --git a/tests/rustdoc/enum-variant-value.rs b/tests/rustdoc/enum/enum-variant-value.rs similarity index 100% rename from tests/rustdoc/enum-variant-value.rs rename to tests/rustdoc/enum/enum-variant-value.rs diff --git a/tests/rustdoc/render-enum-variant-structlike-32395.rs b/tests/rustdoc/enum/render-enum-variant-structlike-32395.rs similarity index 100% rename from tests/rustdoc/render-enum-variant-structlike-32395.rs rename to tests/rustdoc/enum/render-enum-variant-structlike-32395.rs diff --git a/tests/rustdoc/strip-enum-variant.no-not-shown.html b/tests/rustdoc/enum/strip-enum-variant.no-not-shown.html similarity index 100% rename from tests/rustdoc/strip-enum-variant.no-not-shown.html rename to tests/rustdoc/enum/strip-enum-variant.no-not-shown.html diff --git a/tests/rustdoc/strip-enum-variant.rs b/tests/rustdoc/enum/strip-enum-variant.rs similarity index 100% rename from tests/rustdoc/strip-enum-variant.rs rename to tests/rustdoc/enum/strip-enum-variant.rs diff --git a/tests/rustdoc/extern/auxiliary/empty.rs b/tests/rustdoc/extern/auxiliary/empty.rs new file mode 100644 index 0000000000000..d11c69f812a8d --- /dev/null +++ b/tests/rustdoc/extern/auxiliary/empty.rs @@ -0,0 +1 @@ +// intentionally empty diff --git a/tests/rustdoc/auxiliary/extern-links.rs b/tests/rustdoc/extern/auxiliary/extern-links.rs similarity index 100% rename from tests/rustdoc/auxiliary/extern-links.rs rename to tests/rustdoc/extern/auxiliary/extern-links.rs diff --git a/tests/rustdoc/auxiliary/external-cross-doc.md b/tests/rustdoc/extern/auxiliary/external-cross-doc.md similarity index 100% rename from tests/rustdoc/auxiliary/external-cross-doc.md rename to tests/rustdoc/extern/auxiliary/external-cross-doc.md diff --git a/tests/rustdoc/auxiliary/external-cross.rs b/tests/rustdoc/extern/auxiliary/external-cross.rs similarity index 100% rename from tests/rustdoc/auxiliary/external-cross.rs rename to tests/rustdoc/extern/auxiliary/external-cross.rs diff --git a/tests/rustdoc/auxiliary/external-doc.md b/tests/rustdoc/extern/auxiliary/external-doc.md similarity index 100% rename from tests/rustdoc/auxiliary/external-doc.md rename to tests/rustdoc/extern/auxiliary/external-doc.md diff --git a/tests/rustdoc/auxiliary/html_root.rs b/tests/rustdoc/extern/auxiliary/html_root.rs similarity index 100% rename from tests/rustdoc/auxiliary/html_root.rs rename to tests/rustdoc/extern/auxiliary/html_root.rs diff --git a/tests/rustdoc/auxiliary/issue-30109-1.rs b/tests/rustdoc/extern/auxiliary/issue-30109-1.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-30109-1.rs rename to tests/rustdoc/extern/auxiliary/issue-30109-1.rs diff --git a/tests/rustdoc/auxiliary/no_html_root.rs b/tests/rustdoc/extern/auxiliary/no_html_root.rs similarity index 100% rename from tests/rustdoc/auxiliary/no_html_root.rs rename to tests/rustdoc/extern/auxiliary/no_html_root.rs diff --git a/tests/rustdoc/auxiliary/panic-item.rs b/tests/rustdoc/extern/auxiliary/panic-item.rs similarity index 100% rename from tests/rustdoc/auxiliary/panic-item.rs rename to tests/rustdoc/extern/auxiliary/panic-item.rs diff --git a/tests/rustdoc/auxiliary/pub-extern-crate.rs b/tests/rustdoc/extern/auxiliary/pub-extern-crate.rs similarity index 100% rename from tests/rustdoc/auxiliary/pub-extern-crate.rs rename to tests/rustdoc/extern/auxiliary/pub-extern-crate.rs diff --git a/tests/rustdoc/auxiliary/rustdoc-extern-default-method.rs b/tests/rustdoc/extern/auxiliary/rustdoc-extern-default-method.rs similarity index 100% rename from tests/rustdoc/auxiliary/rustdoc-extern-default-method.rs rename to tests/rustdoc/extern/auxiliary/rustdoc-extern-default-method.rs diff --git a/tests/rustdoc/auxiliary/rustdoc-extern-method.rs b/tests/rustdoc/extern/auxiliary/rustdoc-extern-method.rs similarity index 100% rename from tests/rustdoc/auxiliary/rustdoc-extern-method.rs rename to tests/rustdoc/extern/auxiliary/rustdoc-extern-method.rs diff --git a/tests/rustdoc/extern/auxiliary/variant-struct.rs b/tests/rustdoc/extern/auxiliary/variant-struct.rs new file mode 100644 index 0000000000000..0f3d2e5f1b7a0 --- /dev/null +++ b/tests/rustdoc/extern/auxiliary/variant-struct.rs @@ -0,0 +1,5 @@ +pub enum Foo { + Bar { + qux: (), + } +} diff --git a/tests/rustdoc/extern-default-method.no_href_on_anchor.html b/tests/rustdoc/extern/extern-default-method.no_href_on_anchor.html similarity index 100% rename from tests/rustdoc/extern-default-method.no_href_on_anchor.html rename to tests/rustdoc/extern/extern-default-method.no_href_on_anchor.html diff --git a/tests/rustdoc/extern-default-method.rs b/tests/rustdoc/extern/extern-default-method.rs similarity index 100% rename from tests/rustdoc/extern-default-method.rs rename to tests/rustdoc/extern/extern-default-method.rs diff --git a/tests/rustdoc/extern-fn-22038.rs b/tests/rustdoc/extern/extern-fn-22038.rs similarity index 100% rename from tests/rustdoc/extern-fn-22038.rs rename to tests/rustdoc/extern/extern-fn-22038.rs diff --git a/tests/rustdoc/extern-html-root-url-precedence.rs b/tests/rustdoc/extern/extern-html-root-url-precedence.rs similarity index 100% rename from tests/rustdoc/extern-html-root-url-precedence.rs rename to tests/rustdoc/extern/extern-html-root-url-precedence.rs diff --git a/tests/rustdoc/extern-html-root-url.rs b/tests/rustdoc/extern/extern-html-root-url.rs similarity index 100% rename from tests/rustdoc/extern-html-root-url.rs rename to tests/rustdoc/extern/extern-html-root-url.rs diff --git a/tests/rustdoc/extern-links.rs b/tests/rustdoc/extern/extern-links.rs similarity index 100% rename from tests/rustdoc/extern-links.rs rename to tests/rustdoc/extern/extern-links.rs diff --git a/tests/rustdoc/extern-method.rs b/tests/rustdoc/extern/extern-method.rs similarity index 100% rename from tests/rustdoc/extern-method.rs rename to tests/rustdoc/extern/extern-method.rs diff --git a/tests/rustdoc/external-cross.rs b/tests/rustdoc/extern/external-cross.rs similarity index 100% rename from tests/rustdoc/external-cross.rs rename to tests/rustdoc/extern/external-cross.rs diff --git a/tests/rustdoc/external-doc.rs b/tests/rustdoc/extern/external-doc.rs similarity index 100% rename from tests/rustdoc/external-doc.rs rename to tests/rustdoc/extern/external-doc.rs diff --git a/tests/rustdoc/hidden-extern-34025.rs b/tests/rustdoc/extern/hidden-extern-34025.rs similarity index 100% rename from tests/rustdoc/hidden-extern-34025.rs rename to tests/rustdoc/extern/hidden-extern-34025.rs diff --git a/tests/rustdoc/link-extern-crate-33178.rs b/tests/rustdoc/extern/link-extern-crate-33178.rs similarity index 100% rename from tests/rustdoc/link-extern-crate-33178.rs rename to tests/rustdoc/extern/link-extern-crate-33178.rs diff --git a/tests/rustdoc/link-extern-crate-item-30109.rs b/tests/rustdoc/extern/link-extern-crate-item-30109.rs similarity index 100% rename from tests/rustdoc/link-extern-crate-item-30109.rs rename to tests/rustdoc/extern/link-extern-crate-item-30109.rs diff --git a/tests/rustdoc/link-extern-crate-title-33178.rs b/tests/rustdoc/extern/link-extern-crate-title-33178.rs similarity index 100% rename from tests/rustdoc/link-extern-crate-title-33178.rs rename to tests/rustdoc/extern/link-extern-crate-title-33178.rs diff --git a/tests/rustdoc/pub-extern-crate.rs b/tests/rustdoc/extern/pub-extern-crate.rs similarity index 100% rename from tests/rustdoc/pub-extern-crate.rs rename to tests/rustdoc/extern/pub-extern-crate.rs diff --git a/tests/rustdoc/unsafe-extern-blocks.rs b/tests/rustdoc/extern/unsafe-extern-blocks.rs similarity index 100% rename from tests/rustdoc/unsafe-extern-blocks.rs rename to tests/rustdoc/extern/unsafe-extern-blocks.rs diff --git a/tests/rustdoc/unused-extern-crate.rs b/tests/rustdoc/extern/unused-extern-crate.rs similarity index 100% rename from tests/rustdoc/unused-extern-crate.rs rename to tests/rustdoc/extern/unused-extern-crate.rs diff --git a/tests/rustdoc/ffi.rs b/tests/rustdoc/ffi.rs index 5ba7cdba91096..524fb0edefb6e 100644 --- a/tests/rustdoc/ffi.rs +++ b/tests/rustdoc/ffi.rs @@ -9,4 +9,8 @@ pub use lib::foreigner; extern "C" { //@ has ffi/fn.another.html //pre 'pub unsafe extern "C" fn another(cold_as_ice: u32)' pub fn another(cold_as_ice: u32); + + //@ has ffi/fn.params_are_unnamed.html //pre \ + // 'pub unsafe extern "C" fn params_are_unnamed(_: i32, _: u32)' + pub fn params_are_unnamed(_: i32, _: u32); } diff --git a/tests/rustdoc/auxiliary/cross-crate-hidden-impl-parameter.rs b/tests/rustdoc/impl/auxiliary/cross-crate-hidden-impl-parameter.rs similarity index 100% rename from tests/rustdoc/auxiliary/cross-crate-hidden-impl-parameter.rs rename to tests/rustdoc/impl/auxiliary/cross-crate-hidden-impl-parameter.rs diff --git a/tests/rustdoc/auxiliary/extern-impl-trait.rs b/tests/rustdoc/impl/auxiliary/extern-impl-trait.rs similarity index 100% rename from tests/rustdoc/auxiliary/extern-impl-trait.rs rename to tests/rustdoc/impl/auxiliary/extern-impl-trait.rs diff --git a/tests/rustdoc/auxiliary/incoherent-impl-types.rs b/tests/rustdoc/impl/auxiliary/incoherent-impl-types.rs similarity index 100% rename from tests/rustdoc/auxiliary/incoherent-impl-types.rs rename to tests/rustdoc/impl/auxiliary/incoherent-impl-types.rs diff --git a/tests/rustdoc/auxiliary/issue-100204-aux.rs b/tests/rustdoc/impl/auxiliary/issue-100204-aux.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-100204-aux.rs rename to tests/rustdoc/impl/auxiliary/issue-100204-aux.rs diff --git a/tests/rustdoc/auxiliary/issue-17476.rs b/tests/rustdoc/impl/auxiliary/issue-17476.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-17476.rs rename to tests/rustdoc/impl/auxiliary/issue-17476.rs diff --git a/tests/rustdoc/auxiliary/issue-21092.rs b/tests/rustdoc/impl/auxiliary/issue-21092.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-21092.rs rename to tests/rustdoc/impl/auxiliary/issue-21092.rs diff --git a/tests/rustdoc/auxiliary/issue-22025.rs b/tests/rustdoc/impl/auxiliary/issue-22025.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-22025.rs rename to tests/rustdoc/impl/auxiliary/issue-22025.rs diff --git a/tests/rustdoc/auxiliary/issue-53689.rs b/tests/rustdoc/impl/auxiliary/issue-53689.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-53689.rs rename to tests/rustdoc/impl/auxiliary/issue-53689.rs diff --git a/tests/rustdoc/auxiliary/precise-capturing.rs b/tests/rustdoc/impl/auxiliary/precise-capturing.rs similarity index 100% rename from tests/rustdoc/auxiliary/precise-capturing.rs rename to tests/rustdoc/impl/auxiliary/precise-capturing.rs diff --git a/tests/rustdoc/auxiliary/real_gimli.rs b/tests/rustdoc/impl/auxiliary/real_gimli.rs similarity index 100% rename from tests/rustdoc/auxiliary/real_gimli.rs rename to tests/rustdoc/impl/auxiliary/real_gimli.rs diff --git a/tests/rustdoc/auxiliary/realcore.rs b/tests/rustdoc/impl/auxiliary/realcore.rs similarity index 100% rename from tests/rustdoc/auxiliary/realcore.rs rename to tests/rustdoc/impl/auxiliary/realcore.rs diff --git a/tests/rustdoc/auxiliary/rustdoc-default-impl.rs b/tests/rustdoc/impl/auxiliary/rustdoc-default-impl.rs similarity index 100% rename from tests/rustdoc/auxiliary/rustdoc-default-impl.rs rename to tests/rustdoc/impl/auxiliary/rustdoc-default-impl.rs diff --git a/tests/rustdoc/auxiliary/rustdoc-impl-parts-crosscrate.rs b/tests/rustdoc/impl/auxiliary/rustdoc-impl-parts-crosscrate.rs similarity index 100% rename from tests/rustdoc/auxiliary/rustdoc-impl-parts-crosscrate.rs rename to tests/rustdoc/impl/auxiliary/rustdoc-impl-parts-crosscrate.rs diff --git a/tests/rustdoc/blanket-impl-29503.rs b/tests/rustdoc/impl/blanket-impl-29503.rs similarity index 100% rename from tests/rustdoc/blanket-impl-29503.rs rename to tests/rustdoc/impl/blanket-impl-29503.rs diff --git a/tests/rustdoc/blanket-impl-78673.rs b/tests/rustdoc/impl/blanket-impl-78673.rs similarity index 100% rename from tests/rustdoc/blanket-impl-78673.rs rename to tests/rustdoc/impl/blanket-impl-78673.rs diff --git a/tests/rustdoc/cross-crate-hidden-impl-parameter.rs b/tests/rustdoc/impl/cross-crate-hidden-impl-parameter.rs similarity index 100% rename from tests/rustdoc/cross-crate-hidden-impl-parameter.rs rename to tests/rustdoc/impl/cross-crate-hidden-impl-parameter.rs diff --git a/tests/rustdoc/deduplicate-glob-import-impl-21474.rs b/tests/rustdoc/impl/deduplicate-glob-import-impl-21474.rs similarity index 100% rename from tests/rustdoc/deduplicate-glob-import-impl-21474.rs rename to tests/rustdoc/impl/deduplicate-glob-import-impl-21474.rs diff --git a/tests/rustdoc/deduplicate-trait-impl-22025.rs b/tests/rustdoc/impl/deduplicate-trait-impl-22025.rs similarity index 100% rename from tests/rustdoc/deduplicate-trait-impl-22025.rs rename to tests/rustdoc/impl/deduplicate-trait-impl-22025.rs diff --git a/tests/rustdoc/default-impl.rs b/tests/rustdoc/impl/default-impl.rs similarity index 100% rename from tests/rustdoc/default-impl.rs rename to tests/rustdoc/impl/default-impl.rs diff --git a/tests/rustdoc/deprecated-impls.rs b/tests/rustdoc/impl/deprecated-impls.rs similarity index 100% rename from tests/rustdoc/deprecated-impls.rs rename to tests/rustdoc/impl/deprecated-impls.rs diff --git a/tests/rustdoc/doc-hidden-trait-implementors-33069.rs b/tests/rustdoc/impl/doc-hidden-trait-implementors-33069.rs similarity index 100% rename from tests/rustdoc/doc-hidden-trait-implementors-33069.rs rename to tests/rustdoc/impl/doc-hidden-trait-implementors-33069.rs diff --git a/tests/rustdoc/doc_auto_cfg_nested_impl.rs b/tests/rustdoc/impl/doc_auto_cfg_nested_impl.rs similarity index 100% rename from tests/rustdoc/doc_auto_cfg_nested_impl.rs rename to tests/rustdoc/impl/doc_auto_cfg_nested_impl.rs diff --git a/tests/rustdoc/duplicated_impl.rs b/tests/rustdoc/impl/duplicated_impl.rs similarity index 100% rename from tests/rustdoc/duplicated_impl.rs rename to tests/rustdoc/impl/duplicated_impl.rs diff --git a/tests/rustdoc/empty-impl-block.rs b/tests/rustdoc/impl/empty-impl-block.rs similarity index 100% rename from tests/rustdoc/empty-impl-block.rs rename to tests/rustdoc/impl/empty-impl-block.rs diff --git a/tests/rustdoc/empty-impls.rs b/tests/rustdoc/impl/empty-impls.rs similarity index 100% rename from tests/rustdoc/empty-impls.rs rename to tests/rustdoc/impl/empty-impls.rs diff --git a/tests/rustdoc/extern-impl-trait.rs b/tests/rustdoc/impl/extern-impl-trait.rs similarity index 100% rename from tests/rustdoc/extern-impl-trait.rs rename to tests/rustdoc/impl/extern-impl-trait.rs diff --git a/tests/rustdoc/extern-impl.rs b/tests/rustdoc/impl/extern-impl.rs similarity index 100% rename from tests/rustdoc/extern-impl.rs rename to tests/rustdoc/impl/extern-impl.rs diff --git a/tests/rustdoc/foreign-implementors-js-43701.rs b/tests/rustdoc/impl/foreign-implementors-js-43701.rs similarity index 100% rename from tests/rustdoc/foreign-implementors-js-43701.rs rename to tests/rustdoc/impl/foreign-implementors-js-43701.rs diff --git a/tests/rustdoc/generic-impl.rs b/tests/rustdoc/impl/generic-impl.rs similarity index 100% rename from tests/rustdoc/generic-impl.rs rename to tests/rustdoc/impl/generic-impl.rs diff --git a/tests/rustdoc/hidden-implementors-90781.rs b/tests/rustdoc/impl/hidden-implementors-90781.rs similarity index 100% rename from tests/rustdoc/hidden-implementors-90781.rs rename to tests/rustdoc/impl/hidden-implementors-90781.rs diff --git a/tests/rustdoc/hidden-impls.rs b/tests/rustdoc/impl/hidden-impls.rs similarity index 100% rename from tests/rustdoc/hidden-impls.rs rename to tests/rustdoc/impl/hidden-impls.rs diff --git a/tests/rustdoc/hidden-trait-struct-impls.rs b/tests/rustdoc/impl/hidden-trait-struct-impls.rs similarity index 100% rename from tests/rustdoc/hidden-trait-struct-impls.rs rename to tests/rustdoc/impl/hidden-trait-struct-impls.rs diff --git a/tests/rustdoc/hide-mut-methods-if-no-derefmut-impl-74083.rs b/tests/rustdoc/impl/hide-mut-methods-if-no-derefmut-impl-74083.rs similarity index 100% rename from tests/rustdoc/hide-mut-methods-if-no-derefmut-impl-74083.rs rename to tests/rustdoc/impl/hide-mut-methods-if-no-derefmut-impl-74083.rs diff --git a/tests/rustdoc/impl-alias-substituted.rs b/tests/rustdoc/impl/impl-alias-substituted.rs similarity index 100% rename from tests/rustdoc/impl-alias-substituted.rs rename to tests/rustdoc/impl/impl-alias-substituted.rs diff --git a/tests/rustdoc/impl-assoc-type-21092.rs b/tests/rustdoc/impl/impl-assoc-type-21092.rs similarity index 100% rename from tests/rustdoc/impl-assoc-type-21092.rs rename to tests/rustdoc/impl/impl-assoc-type-21092.rs diff --git a/tests/rustdoc/impl-associated-items-order.rs b/tests/rustdoc/impl/impl-associated-items-order.rs similarity index 100% rename from tests/rustdoc/impl-associated-items-order.rs rename to tests/rustdoc/impl/impl-associated-items-order.rs diff --git a/tests/rustdoc/impl-associated-items-sidebar.rs b/tests/rustdoc/impl/impl-associated-items-sidebar.rs similarity index 100% rename from tests/rustdoc/impl-associated-items-sidebar.rs rename to tests/rustdoc/impl/impl-associated-items-sidebar.rs diff --git a/tests/rustdoc/impl-blanket-53689.rs b/tests/rustdoc/impl/impl-blanket-53689.rs similarity index 100% rename from tests/rustdoc/impl-blanket-53689.rs rename to tests/rustdoc/impl/impl-blanket-53689.rs diff --git a/tests/rustdoc/impl-box.rs b/tests/rustdoc/impl/impl-box.rs similarity index 100% rename from tests/rustdoc/impl-box.rs rename to tests/rustdoc/impl/impl-box.rs diff --git a/tests/rustdoc/impl-disambiguation.rs b/tests/rustdoc/impl/impl-disambiguation.rs similarity index 100% rename from tests/rustdoc/impl-disambiguation.rs rename to tests/rustdoc/impl/impl-disambiguation.rs diff --git a/tests/rustdoc/impl-everywhere.rs b/tests/rustdoc/impl/impl-everywhere.rs similarity index 100% rename from tests/rustdoc/impl-everywhere.rs rename to tests/rustdoc/impl/impl-everywhere.rs diff --git a/tests/rustdoc/impl-in-const-block.rs b/tests/rustdoc/impl/impl-in-const-block.rs similarity index 100% rename from tests/rustdoc/impl-in-const-block.rs rename to tests/rustdoc/impl/impl-in-const-block.rs diff --git a/tests/rustdoc/impl-on-ty-alias-issue-119015.rs b/tests/rustdoc/impl/impl-on-ty-alias-issue-119015.rs similarity index 100% rename from tests/rustdoc/impl-on-ty-alias-issue-119015.rs rename to tests/rustdoc/impl/impl-on-ty-alias-issue-119015.rs diff --git a/tests/rustdoc/impl-parts-crosscrate.rs b/tests/rustdoc/impl/impl-parts-crosscrate.rs similarity index 93% rename from tests/rustdoc/impl-parts-crosscrate.rs rename to tests/rustdoc/impl/impl-parts-crosscrate.rs index 49752ab75d512..631c8bb3eb318 100644 --- a/tests/rustdoc/impl-parts-crosscrate.rs +++ b/tests/rustdoc/impl/impl-parts-crosscrate.rs @@ -5,7 +5,7 @@ extern crate rustdoc_impl_parts_crosscrate; -pub struct Bar { t: T } +pub struct Bar { t: T } // The output file is html embedded in javascript, so the html tags // aren't stripped by the processing script and we can't check for the diff --git a/tests/rustdoc/impl-parts.rs b/tests/rustdoc/impl/impl-parts.rs similarity index 90% rename from tests/rustdoc/impl-parts.rs rename to tests/rustdoc/impl/impl-parts.rs index 820f51008a40c..4f281bfd63c83 100644 --- a/tests/rustdoc/impl-parts.rs +++ b/tests/rustdoc/impl/impl-parts.rs @@ -3,7 +3,7 @@ pub auto trait AnAutoTrait {} -pub struct Foo { field: T } +pub struct Foo { field: T } //@ has impl_parts/struct.Foo.html '//*[@class="impl"]//h3[@class="code-header"]' \ // "impl !AnAutoTrait for Foowhere T: Sync + Clone," diff --git a/tests/rustdoc/impl-ref-20175.rs b/tests/rustdoc/impl/impl-ref-20175.rs similarity index 100% rename from tests/rustdoc/impl-ref-20175.rs rename to tests/rustdoc/impl/impl-ref-20175.rs diff --git a/tests/rustdoc/impl-trait-43869.rs b/tests/rustdoc/impl/impl-trait-43869.rs similarity index 100% rename from tests/rustdoc/impl-trait-43869.rs rename to tests/rustdoc/impl/impl-trait-43869.rs diff --git a/tests/rustdoc/impl-trait-alias.rs b/tests/rustdoc/impl/impl-trait-alias.rs similarity index 100% rename from tests/rustdoc/impl-trait-alias.rs rename to tests/rustdoc/impl/impl-trait-alias.rs diff --git a/tests/rustdoc/impl-trait-precise-capturing.rs b/tests/rustdoc/impl/impl-trait-precise-capturing.rs similarity index 100% rename from tests/rustdoc/impl-trait-precise-capturing.rs rename to tests/rustdoc/impl/impl-trait-precise-capturing.rs diff --git a/tests/rustdoc/impl-type-parameter-33592.rs b/tests/rustdoc/impl/impl-type-parameter-33592.rs similarity index 100% rename from tests/rustdoc/impl-type-parameter-33592.rs rename to tests/rustdoc/impl/impl-type-parameter-33592.rs diff --git a/tests/rustdoc/implementor-stable-version.rs b/tests/rustdoc/impl/implementor-stable-version.rs similarity index 100% rename from tests/rustdoc/implementor-stable-version.rs rename to tests/rustdoc/impl/implementor-stable-version.rs diff --git a/tests/rustdoc/implementors-unstable-75588.rs b/tests/rustdoc/impl/implementors-unstable-75588.rs similarity index 100% rename from tests/rustdoc/implementors-unstable-75588.rs rename to tests/rustdoc/impl/implementors-unstable-75588.rs diff --git a/tests/rustdoc/inline-impl-through-glob-import-100204.rs b/tests/rustdoc/impl/inline-impl-through-glob-import-100204.rs similarity index 100% rename from tests/rustdoc/inline-impl-through-glob-import-100204.rs rename to tests/rustdoc/impl/inline-impl-through-glob-import-100204.rs diff --git a/tests/rustdoc/manual_impl.rs b/tests/rustdoc/impl/manual_impl.rs similarity index 100% rename from tests/rustdoc/manual_impl.rs rename to tests/rustdoc/impl/manual_impl.rs diff --git a/tests/rustdoc/method-link-foreign-trait-impl-17476.rs b/tests/rustdoc/impl/method-link-foreign-trait-impl-17476.rs similarity index 100% rename from tests/rustdoc/method-link-foreign-trait-impl-17476.rs rename to tests/rustdoc/impl/method-link-foreign-trait-impl-17476.rs diff --git a/tests/rustdoc/module-impls.rs b/tests/rustdoc/impl/module-impls.rs similarity index 100% rename from tests/rustdoc/module-impls.rs rename to tests/rustdoc/impl/module-impls.rs diff --git a/tests/rustdoc/must_implement_one_of.rs b/tests/rustdoc/impl/must_implement_one_of.rs similarity index 100% rename from tests/rustdoc/must_implement_one_of.rs rename to tests/rustdoc/impl/must_implement_one_of.rs diff --git a/tests/rustdoc/negative-impl-no-items.rs b/tests/rustdoc/impl/negative-impl-no-items.rs similarity index 100% rename from tests/rustdoc/negative-impl-no-items.rs rename to tests/rustdoc/impl/negative-impl-no-items.rs diff --git a/tests/rustdoc/negative-impl-sidebar.rs b/tests/rustdoc/impl/negative-impl-sidebar.rs similarity index 100% rename from tests/rustdoc/negative-impl-sidebar.rs rename to tests/rustdoc/impl/negative-impl-sidebar.rs diff --git a/tests/rustdoc/negative-impl.rs b/tests/rustdoc/impl/negative-impl.rs similarity index 100% rename from tests/rustdoc/negative-impl.rs rename to tests/rustdoc/impl/negative-impl.rs diff --git a/tests/rustdoc/return-impl-trait.rs b/tests/rustdoc/impl/return-impl-trait.rs similarity index 100% rename from tests/rustdoc/return-impl-trait.rs rename to tests/rustdoc/impl/return-impl-trait.rs diff --git a/tests/rustdoc/rustc-incoherent-impls.rs b/tests/rustdoc/impl/rustc-incoherent-impls.rs similarity index 100% rename from tests/rustdoc/rustc-incoherent-impls.rs rename to tests/rustdoc/impl/rustc-incoherent-impls.rs diff --git a/tests/rustdoc/same-crate-hidden-impl-parameter.rs b/tests/rustdoc/impl/same-crate-hidden-impl-parameter.rs similarity index 100% rename from tests/rustdoc/same-crate-hidden-impl-parameter.rs rename to tests/rustdoc/impl/same-crate-hidden-impl-parameter.rs diff --git a/tests/rustdoc/sidebar-trait-impl-disambiguate-78701.rs b/tests/rustdoc/impl/sidebar-trait-impl-disambiguate-78701.rs similarity index 100% rename from tests/rustdoc/sidebar-trait-impl-disambiguate-78701.rs rename to tests/rustdoc/impl/sidebar-trait-impl-disambiguate-78701.rs diff --git a/tests/rustdoc/struct-implementations-title.rs b/tests/rustdoc/impl/struct-implementations-title.rs similarity index 100% rename from tests/rustdoc/struct-implementations-title.rs rename to tests/rustdoc/impl/struct-implementations-title.rs diff --git a/tests/rustdoc/trait-impl.rs b/tests/rustdoc/impl/trait-impl.rs similarity index 100% rename from tests/rustdoc/trait-impl.rs rename to tests/rustdoc/impl/trait-impl.rs diff --git a/tests/rustdoc/trait-implementations-duplicate-self-45584.rs b/tests/rustdoc/impl/trait-implementations-duplicate-self-45584.rs similarity index 100% rename from tests/rustdoc/trait-implementations-duplicate-self-45584.rs rename to tests/rustdoc/impl/trait-implementations-duplicate-self-45584.rs diff --git a/tests/rustdoc/underscore-type-in-trait-impl-96381.rs b/tests/rustdoc/impl/underscore-type-in-trait-impl-96381.rs similarity index 100% rename from tests/rustdoc/underscore-type-in-trait-impl-96381.rs rename to tests/rustdoc/impl/underscore-type-in-trait-impl-96381.rs diff --git a/tests/rustdoc/universal-impl-trait.rs b/tests/rustdoc/impl/universal-impl-trait.rs similarity index 100% rename from tests/rustdoc/universal-impl-trait.rs rename to tests/rustdoc/impl/universal-impl-trait.rs diff --git a/tests/rustdoc/unneeded-trait-implementations-title.rs b/tests/rustdoc/impl/unneeded-trait-implementations-title.rs similarity index 100% rename from tests/rustdoc/unneeded-trait-implementations-title.rs rename to tests/rustdoc/impl/unneeded-trait-implementations-title.rs diff --git a/tests/rustdoc/inline_cross/auxiliary/fn-type.rs b/tests/rustdoc/inline_cross/auxiliary/fn-ptr-ty.rs similarity index 100% rename from tests/rustdoc/inline_cross/auxiliary/fn-type.rs rename to tests/rustdoc/inline_cross/auxiliary/fn-ptr-ty.rs diff --git a/tests/rustdoc/inline_cross/default-generic-args.rs b/tests/rustdoc/inline_cross/default-generic-args.rs index 0469221b3d858..5124fbdf8daeb 100644 --- a/tests/rustdoc/inline_cross/default-generic-args.rs +++ b/tests/rustdoc/inline_cross/default-generic-args.rs @@ -53,17 +53,17 @@ pub use default_generic_args::R2; //@ has user/type.H0.html // Check that we handle higher-ranked regions correctly: -//@ has - '//*[@class="rust item-decl"]//code' "fn(_: for<'a> fn(_: Re<'a>))" +//@ has - '//*[@class="rust item-decl"]//code' "fn(for<'a> fn(Re<'a>))" pub use default_generic_args::H0; //@ has user/type.H1.html // Check that we don't conflate distinct universially quantified regions (#1): -//@ has - '//*[@class="rust item-decl"]//code' "for<'b> fn(_: for<'a> fn(_: Re<'a, &'b ()>))" +//@ has - '//*[@class="rust item-decl"]//code' "for<'b> fn(for<'a> fn(Re<'a, &'b ()>))" pub use default_generic_args::H1; //@ has user/type.H2.html // Check that we don't conflate distinct universially quantified regions (#2): -//@ has - '//*[@class="rust item-decl"]//code' "for<'a> fn(_: for<'b> fn(_: Re<'a, &'b ()>))" +//@ has - '//*[@class="rust item-decl"]//code' "for<'a> fn(for<'b> fn(Re<'a, &'b ()>))" pub use default_generic_args::H2; //@ has user/type.P0.html @@ -86,7 +86,7 @@ pub use default_generic_args::A0; // Demonstrates that we currently don't elide generic arguments that are alpha-equivalent to their // respective generic parameter (after instantiation) for perf reasons (it would require us to // create an inference context). -//@ has - '//*[@class="rust item-decl"]//code' "Alpha fn(_: &'arbitrary ())>" +//@ has - '//*[@class="rust item-decl"]//code' "Alpha fn(&'arbitrary ())>" pub use default_generic_args::A1; //@ has user/type.M0.html diff --git a/tests/rustdoc/inline_cross/fn-ptr-ty.rs b/tests/rustdoc/inline_cross/fn-ptr-ty.rs new file mode 100644 index 0000000000000..0105962252172 --- /dev/null +++ b/tests/rustdoc/inline_cross/fn-ptr-ty.rs @@ -0,0 +1,12 @@ +// Make sure that we print the higher-ranked parameters of cross-crate function pointer types. +// They should be rendered exactly as the user wrote it, i.e., in source order and with unused +// parameters present, not stripped. + +//@ aux-crate:fn_ptr_ty=fn-ptr-ty.rs +//@ edition: 2021 +#![crate_name = "user"] + +//@ has user/type.F.html +//@ has - '//*[@class="rust item-decl"]//code' \ +// "for<'z, 'a, '_unused> fn(&'z for<'b> fn(&'b str), &'a ()) -> &'a ();" +pub use fn_ptr_ty::F; diff --git a/tests/rustdoc/inline_cross/fn-type.rs b/tests/rustdoc/inline_cross/fn-type.rs deleted file mode 100644 index 8db6f65f421fb..0000000000000 --- a/tests/rustdoc/inline_cross/fn-type.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Make sure that we print the higher-ranked parameters of cross-crate function pointer types. -// They should be rendered exactly as the user wrote it, i.e., in source order and with unused -// parameters present, not stripped. - -//@ aux-crate:fn_type=fn-type.rs -//@ edition: 2021 -#![crate_name = "user"] - -//@ has user/type.F.html -//@ has - '//*[@class="rust item-decl"]//code' \ -// "for<'z, 'a, '_unused> fn(_: &'z for<'b> fn(_: &'b str), _: &'a ()) -> &'a ();" -pub use fn_type::F; diff --git a/tests/rustdoc/inline_cross/impl_trait.rs b/tests/rustdoc/inline_cross/impl_trait.rs index e6baf33660acb..468ac0830619b 100644 --- a/tests/rustdoc/inline_cross/impl_trait.rs +++ b/tests/rustdoc/inline_cross/impl_trait.rs @@ -29,7 +29,7 @@ pub use impl_trait_aux::func4; //@ has impl_trait/fn.func5.html //@ has - '//pre[@class="rust item-decl"]' "func5(" //@ has - '//pre[@class="rust item-decl"]' "_f: impl for<'any> Fn(&'any str, &'any str) -> bool + for<'r> Other = ()>," -//@ has - '//pre[@class="rust item-decl"]' "_a: impl for<'beta, 'alpha, '_gamma> Auxiliary<'alpha, Item<'beta> = fn(_: &'beta ())>" +//@ has - '//pre[@class="rust item-decl"]' "_a: impl for<'beta, 'alpha, '_gamma> Auxiliary<'alpha, Item<'beta> = fn(&'beta ())>" //@ !has - '//pre[@class="rust item-decl"]' 'where' pub use impl_trait_aux::func5; diff --git a/tests/rustdoc/inline_cross/inline_hidden.rs b/tests/rustdoc/inline_cross/inline_hidden.rs index 49ca2db6a2245..f6727de2cc67f 100644 --- a/tests/rustdoc/inline_cross/inline_hidden.rs +++ b/tests/rustdoc/inline_cross/inline_hidden.rs @@ -6,7 +6,7 @@ extern crate rustdoc_hidden; //@ has inline_hidden/index.html // Ensures this item is not inlined. -//@ has - '//*[@id="reexport.Foo"]/code' 'pub use rustdoc_hidden::Foo;' +//@ !has - '//*[@id="reexport.Foo"]/code' 'pub use rustdoc_hidden::Foo;' #[doc(no_inline)] pub use rustdoc_hidden::Foo; @@ -16,7 +16,7 @@ pub use rustdoc_hidden::Foo; pub use rustdoc_hidden::Foo as Inlined; // Even with this import, we should not see `Foo`. -//@ count - '//dt' 4 +//@ count - '//dt' 3 //@ has - '//dt/a[@class="struct"]' 'Bar' //@ has - '//dt/a[@class="fn"]' 'foo' pub use rustdoc_hidden::*; diff --git a/tests/rustdoc/intra-doc/extern-crate-only-used-in-link.rs b/tests/rustdoc/intra-doc/extern-crate-only-used-in-link.rs index 7cec30c8b7469..30834bd72104b 100644 --- a/tests/rustdoc/intra-doc/extern-crate-only-used-in-link.rs +++ b/tests/rustdoc/intra-doc/extern-crate-only-used-in-link.rs @@ -6,7 +6,8 @@ //@ aux-build:empty2.rs //@ aux-crate:priv:empty2=empty2.rs //@ build-aux-docs -//@ compile-flags:-Z unstable-options --edition 2018 +//@ compile-flags:-Z unstable-options +//@ edition: 2018 //@ has extern_crate_only_used_in_link/index.html //@ has - '//a[@href="../issue_66159_1/struct.Something.html"]' 'issue_66159_1::Something' diff --git a/tests/rustdoc/ice-intra-doc-links-107995.rs b/tests/rustdoc/intra-doc/ice-intra-doc-links-107995.rs similarity index 100% rename from tests/rustdoc/ice-intra-doc-links-107995.rs rename to tests/rustdoc/intra-doc/ice-intra-doc-links-107995.rs diff --git a/tests/rustdoc/intra-doc-link-method-trait-impl-72340.rs b/tests/rustdoc/intra-doc/intra-doc-link-method-trait-impl-72340.rs similarity index 100% rename from tests/rustdoc/intra-doc-link-method-trait-impl-72340.rs rename to tests/rustdoc/intra-doc/intra-doc-link-method-trait-impl-72340.rs diff --git a/tests/rustdoc/auxiliary/jump-to-def-macro.rs b/tests/rustdoc/jump-to-def/auxiliary/jump-to-def-macro.rs similarity index 100% rename from tests/rustdoc/auxiliary/jump-to-def-macro.rs rename to tests/rustdoc/jump-to-def/auxiliary/jump-to-def-macro.rs diff --git a/tests/rustdoc/jump-to-def-doc-links-calls.rs b/tests/rustdoc/jump-to-def/jump-to-def-doc-links-calls.rs similarity index 100% rename from tests/rustdoc/jump-to-def-doc-links-calls.rs rename to tests/rustdoc/jump-to-def/jump-to-def-doc-links-calls.rs diff --git a/tests/rustdoc/jump-to-def-doc-links.rs b/tests/rustdoc/jump-to-def/jump-to-def-doc-links.rs similarity index 100% rename from tests/rustdoc/jump-to-def-doc-links.rs rename to tests/rustdoc/jump-to-def/jump-to-def-doc-links.rs diff --git a/tests/rustdoc/jump-to-def-macro.rs b/tests/rustdoc/jump-to-def/jump-to-def-macro.rs similarity index 100% rename from tests/rustdoc/jump-to-def-macro.rs rename to tests/rustdoc/jump-to-def/jump-to-def-macro.rs diff --git a/tests/rustdoc/jump-to-def-pats.rs b/tests/rustdoc/jump-to-def/jump-to-def-pats.rs similarity index 100% rename from tests/rustdoc/jump-to-def-pats.rs rename to tests/rustdoc/jump-to-def/jump-to-def-pats.rs diff --git a/tests/rustdoc/jump-to-def-prelude-types.rs b/tests/rustdoc/jump-to-def/jump-to-def-prelude-types.rs similarity index 100% rename from tests/rustdoc/jump-to-def-prelude-types.rs rename to tests/rustdoc/jump-to-def/jump-to-def-prelude-types.rs diff --git a/tests/rustdoc/jump-to-non-local-method.rs b/tests/rustdoc/jump-to-def/jump-to-non-local-method.rs similarity index 100% rename from tests/rustdoc/jump-to-non-local-method.rs rename to tests/rustdoc/jump-to-def/jump-to-non-local-method.rs diff --git a/tests/rustdoc/auxiliary/external-macro-src.rs b/tests/rustdoc/macro/auxiliary/external-macro-src.rs similarity index 100% rename from tests/rustdoc/auxiliary/external-macro-src.rs rename to tests/rustdoc/macro/auxiliary/external-macro-src.rs diff --git a/tests/rustdoc/macro/auxiliary/issue-99221-aux.rs b/tests/rustdoc/macro/auxiliary/issue-99221-aux.rs new file mode 100644 index 0000000000000..e061e42b29db8 --- /dev/null +++ b/tests/rustdoc/macro/auxiliary/issue-99221-aux.rs @@ -0,0 +1,20 @@ +pub struct Option; +impl Option { + pub fn unwrap(self) {} +} + +mod macros { + use crate::Option; + /// [`Option::unwrap`] + #[macro_export] + macro_rules! print { + () => () + } +} + +mod structs { + use crate::Option; + /// [`Option::unwrap`] + pub struct Print; +} +pub use structs::Print; diff --git a/tests/rustdoc/auxiliary/macro_pub_in_module.rs b/tests/rustdoc/macro/auxiliary/macro_pub_in_module.rs similarity index 100% rename from tests/rustdoc/auxiliary/macro_pub_in_module.rs rename to tests/rustdoc/macro/auxiliary/macro_pub_in_module.rs diff --git a/tests/rustdoc/auxiliary/pub-use-extern-macros.rs b/tests/rustdoc/macro/auxiliary/pub-use-extern-macros.rs similarity index 100% rename from tests/rustdoc/auxiliary/pub-use-extern-macros.rs rename to tests/rustdoc/macro/auxiliary/pub-use-extern-macros.rs diff --git a/tests/rustdoc/compiler-derive-proc-macro.rs b/tests/rustdoc/macro/compiler-derive-proc-macro.rs similarity index 100% rename from tests/rustdoc/compiler-derive-proc-macro.rs rename to tests/rustdoc/macro/compiler-derive-proc-macro.rs diff --git a/tests/rustdoc/const-rendering-macros-33302.rs b/tests/rustdoc/macro/const-rendering-macros-33302.rs similarity index 100% rename from tests/rustdoc/const-rendering-macros-33302.rs rename to tests/rustdoc/macro/const-rendering-macros-33302.rs diff --git a/tests/rustdoc/decl_macro.rs b/tests/rustdoc/macro/decl_macro.rs similarity index 100% rename from tests/rustdoc/decl_macro.rs rename to tests/rustdoc/macro/decl_macro.rs diff --git a/tests/rustdoc/decl_macro_priv.rs b/tests/rustdoc/macro/decl_macro_priv.rs similarity index 100% rename from tests/rustdoc/decl_macro_priv.rs rename to tests/rustdoc/macro/decl_macro_priv.rs diff --git a/tests/rustdoc/doc-proc-macro.rs b/tests/rustdoc/macro/doc-proc-macro.rs similarity index 100% rename from tests/rustdoc/doc-proc-macro.rs rename to tests/rustdoc/macro/doc-proc-macro.rs diff --git a/tests/rustdoc/external-macro-src.rs b/tests/rustdoc/macro/external-macro-src.rs similarity index 100% rename from tests/rustdoc/external-macro-src.rs rename to tests/rustdoc/macro/external-macro-src.rs diff --git a/tests/rustdoc/macro-const-display-115295.rs b/tests/rustdoc/macro/macro-const-display-115295.rs similarity index 100% rename from tests/rustdoc/macro-const-display-115295.rs rename to tests/rustdoc/macro/macro-const-display-115295.rs diff --git a/tests/rustdoc/macro-doc-comment-23812.rs b/tests/rustdoc/macro/macro-doc-comment-23812.rs similarity index 100% rename from tests/rustdoc/macro-doc-comment-23812.rs rename to tests/rustdoc/macro/macro-doc-comment-23812.rs diff --git a/tests/rustdoc/macro-export-crate-root-108231.rs b/tests/rustdoc/macro/macro-export-crate-root-108231.rs similarity index 100% rename from tests/rustdoc/macro-export-crate-root-108231.rs rename to tests/rustdoc/macro/macro-export-crate-root-108231.rs diff --git a/tests/rustdoc/macro-generated-macro.macro_linebreak_pre.html b/tests/rustdoc/macro/macro-generated-macro.macro_linebreak_pre.html similarity index 100% rename from tests/rustdoc/macro-generated-macro.macro_linebreak_pre.html rename to tests/rustdoc/macro/macro-generated-macro.macro_linebreak_pre.html diff --git a/tests/rustdoc/macro-generated-macro.macro_morestuff_pre.html b/tests/rustdoc/macro/macro-generated-macro.macro_morestuff_pre.html similarity index 100% rename from tests/rustdoc/macro-generated-macro.macro_morestuff_pre.html rename to tests/rustdoc/macro/macro-generated-macro.macro_morestuff_pre.html diff --git a/tests/rustdoc/macro-generated-macro.rs b/tests/rustdoc/macro/macro-generated-macro.rs similarity index 100% rename from tests/rustdoc/macro-generated-macro.rs rename to tests/rustdoc/macro/macro-generated-macro.rs diff --git a/tests/rustdoc/macro-higher-kinded-function.rs b/tests/rustdoc/macro/macro-higher-kinded-function.rs similarity index 100% rename from tests/rustdoc/macro-higher-kinded-function.rs rename to tests/rustdoc/macro/macro-higher-kinded-function.rs diff --git a/tests/rustdoc/macro-ice-16019.rs b/tests/rustdoc/macro/macro-ice-16019.rs similarity index 100% rename from tests/rustdoc/macro-ice-16019.rs rename to tests/rustdoc/macro/macro-ice-16019.rs diff --git a/tests/rustdoc/macro-in-async-block.rs b/tests/rustdoc/macro/macro-in-async-block.rs similarity index 100% rename from tests/rustdoc/macro-in-async-block.rs rename to tests/rustdoc/macro/macro-in-async-block.rs diff --git a/tests/rustdoc/macro-in-closure.rs b/tests/rustdoc/macro/macro-in-closure.rs similarity index 100% rename from tests/rustdoc/macro-in-closure.rs rename to tests/rustdoc/macro/macro-in-closure.rs diff --git a/tests/rustdoc/macro-indirect-use.rs b/tests/rustdoc/macro/macro-indirect-use.rs similarity index 100% rename from tests/rustdoc/macro-indirect-use.rs rename to tests/rustdoc/macro/macro-indirect-use.rs diff --git a/tests/rustdoc/macro_pub_in_module.rs b/tests/rustdoc/macro/macro_pub_in_module.rs similarity index 100% rename from tests/rustdoc/macro_pub_in_module.rs rename to tests/rustdoc/macro/macro_pub_in_module.rs diff --git a/tests/rustdoc/macro_rules-matchers.rs b/tests/rustdoc/macro/macro_rules-matchers.rs similarity index 100% rename from tests/rustdoc/macro_rules-matchers.rs rename to tests/rustdoc/macro/macro_rules-matchers.rs diff --git a/tests/rustdoc/macros.rs b/tests/rustdoc/macro/macros.rs similarity index 100% rename from tests/rustdoc/macros.rs rename to tests/rustdoc/macro/macros.rs diff --git a/tests/rustdoc/multiple-macro-rules-w-same-name-99221.rs b/tests/rustdoc/macro/multiple-macro-rules-w-same-name-99221.rs similarity index 100% rename from tests/rustdoc/multiple-macro-rules-w-same-name-99221.rs rename to tests/rustdoc/macro/multiple-macro-rules-w-same-name-99221.rs diff --git a/tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs b/tests/rustdoc/macro/multiple-macro-rules-w-same-name-submodule-99221.rs similarity index 100% rename from tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs rename to tests/rustdoc/macro/multiple-macro-rules-w-same-name-submodule-99221.rs diff --git a/tests/rustdoc/proc-macro.rs b/tests/rustdoc/macro/proc-macro.rs similarity index 100% rename from tests/rustdoc/proc-macro.rs rename to tests/rustdoc/macro/proc-macro.rs diff --git a/tests/rustdoc/pub-use-extern-macros.rs b/tests/rustdoc/macro/pub-use-extern-macros.rs similarity index 100% rename from tests/rustdoc/pub-use-extern-macros.rs rename to tests/rustdoc/macro/pub-use-extern-macros.rs diff --git a/tests/rustdoc/rustc-macro-crate.rs b/tests/rustdoc/macro/rustc-macro-crate.rs similarity index 100% rename from tests/rustdoc/rustc-macro-crate.rs rename to tests/rustdoc/macro/rustc-macro-crate.rs diff --git a/tests/rustdoc/playground.rs b/tests/rustdoc/playground.rs index db2d1669df604..65dad2a5195d6 100644 --- a/tests/rustdoc/playground.rs +++ b/tests/rustdoc/playground.rs @@ -24,4 +24,4 @@ //@ matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn+main()+%7B%0A++++println!(%22Hello,+world!%22);%0A%7D&edition=2015"]' "" //@ matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn+main()+%7B%0A++++println!(%22Hello,+world!%22);%0A%7D&edition=2015"]' "" -//@ matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0A%23!%5Bfeature(something)%5D%0A%0Afn+main()+%7B%0A++++println!(%22Hello,+world!%22);%0A%7D&version=nightly&edition=2015"]' "" +//@ matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0A%23!%5Bfeature(something)%5D%0A%0A%0Afn+main()+%7B%0A++++println!(%22Hello,+world!%22);%0A%7D&version=nightly&edition=2015"]' "" diff --git a/tests/rustdoc/primitive-reexport.rs b/tests/rustdoc/primitive-reexport.rs deleted file mode 100644 index eb255745392ae..0000000000000 --- a/tests/rustdoc/primitive-reexport.rs +++ /dev/null @@ -1,28 +0,0 @@ -//@ aux-build: primitive-reexport.rs -//@ compile-flags:--extern foo --edition 2018 - -#![crate_name = "bar"] - -//@ has bar/p/index.html -//@ has - '//code' 'pub use bool;' -//@ has - '//code/a[@href="{{channel}}/std/primitive.bool.html"]' 'bool' -//@ has - '//code' 'pub use char as my_char;' -//@ has - '//code/a[@href="{{channel}}/std/primitive.char.html"]' 'char' -pub mod p { - pub use foo::bar::*; -} - -//@ has bar/baz/index.html -//@ has - '//code' 'pub use bool;' -//@ has - '//code/a[@href="{{channel}}/std/primitive.bool.html"]' 'bool' -//@ has - '//code' 'pub use char as my_char;' -//@ has - '//code/a[@href="{{channel}}/std/primitive.char.html"]' 'char' -pub use foo::bar as baz; - -//@ has bar/index.html -//@ has - '//code' 'pub use str;' -//@ has - '//code/a[@href="{{channel}}/std/primitive.str.html"]' 'str' -//@ has - '//code' 'pub use i32 as my_i32;' -//@ has - '//code/a[@href="{{channel}}/std/primitive.i32.html"]' 'i32' -pub use str; -pub use i32 as my_i32; diff --git a/tests/rustdoc/auxiliary/issue-15318.rs b/tests/rustdoc/primitive/auxiliary/issue-15318.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-15318.rs rename to tests/rustdoc/primitive/auxiliary/issue-15318.rs diff --git a/tests/rustdoc/primitive/auxiliary/primitive-doc.rs b/tests/rustdoc/primitive/auxiliary/primitive-doc.rs new file mode 100644 index 0000000000000..859716c38e462 --- /dev/null +++ b/tests/rustdoc/primitive/auxiliary/primitive-doc.rs @@ -0,0 +1,11 @@ +//@ compile-flags: --crate-type lib +//@ edition: 2018 + +#![feature(rustc_attrs)] +#![feature(no_core)] +#![no_core] + +#[rustc_doc_primitive = "usize"] +/// This is the built-in type `usize`. +mod usize { +} diff --git a/tests/rustdoc/cross-crate-primitive-doc.rs b/tests/rustdoc/primitive/cross-crate-primitive-doc.rs similarity index 100% rename from tests/rustdoc/cross-crate-primitive-doc.rs rename to tests/rustdoc/primitive/cross-crate-primitive-doc.rs diff --git a/tests/rustdoc/no_std-primitive.rs b/tests/rustdoc/primitive/no_std-primitive.rs similarity index 100% rename from tests/rustdoc/no_std-primitive.rs rename to tests/rustdoc/primitive/no_std-primitive.rs diff --git a/tests/rustdoc/primitive-link.rs b/tests/rustdoc/primitive/primitive-link.rs similarity index 100% rename from tests/rustdoc/primitive-link.rs rename to tests/rustdoc/primitive/primitive-link.rs diff --git a/tests/rustdoc/primitive-raw-pointer-dox-15318-3.rs b/tests/rustdoc/primitive/primitive-raw-pointer-dox-15318-3.rs similarity index 100% rename from tests/rustdoc/primitive-raw-pointer-dox-15318-3.rs rename to tests/rustdoc/primitive/primitive-raw-pointer-dox-15318-3.rs diff --git a/tests/rustdoc/primitive-raw-pointer-link-15318.rs b/tests/rustdoc/primitive/primitive-raw-pointer-link-15318.rs similarity index 100% rename from tests/rustdoc/primitive-raw-pointer-link-15318.rs rename to tests/rustdoc/primitive/primitive-raw-pointer-link-15318.rs diff --git a/tests/rustdoc/primitive-raw-pointer-link-no-inlined-15318-2.rs b/tests/rustdoc/primitive/primitive-raw-pointer-link-no-inlined-15318-2.rs similarity index 100% rename from tests/rustdoc/primitive-raw-pointer-link-no-inlined-15318-2.rs rename to tests/rustdoc/primitive/primitive-raw-pointer-link-no-inlined-15318-2.rs diff --git a/tests/rustdoc/primitive-reference.rs b/tests/rustdoc/primitive/primitive-reference.rs similarity index 100% rename from tests/rustdoc/primitive-reference.rs rename to tests/rustdoc/primitive/primitive-reference.rs diff --git a/tests/rustdoc/primitive-slice-auto-trait.rs b/tests/rustdoc/primitive/primitive-slice-auto-trait.rs similarity index 90% rename from tests/rustdoc/primitive-slice-auto-trait.rs rename to tests/rustdoc/primitive/primitive-slice-auto-trait.rs index e78d1d9461421..647c1cca94810 100644 --- a/tests/rustdoc/primitive-slice-auto-trait.rs +++ b/tests/rustdoc/primitive/primitive-slice-auto-trait.rs @@ -1,4 +1,5 @@ -//@ compile-flags: --crate-type lib --edition 2018 +//@ compile-flags: --crate-type lib +//@ edition: 2018 #![crate_name = "foo"] #![feature(rustc_attrs)] diff --git a/tests/rustdoc/primitive-tuple-auto-trait.rs b/tests/rustdoc/primitive/primitive-tuple-auto-trait.rs similarity index 94% rename from tests/rustdoc/primitive-tuple-auto-trait.rs rename to tests/rustdoc/primitive/primitive-tuple-auto-trait.rs index 045478e6b4f8c..51300bd6b2fc6 100644 --- a/tests/rustdoc/primitive-tuple-auto-trait.rs +++ b/tests/rustdoc/primitive/primitive-tuple-auto-trait.rs @@ -1,4 +1,5 @@ -//@ compile-flags: --crate-type lib --edition 2018 +//@ compile-flags: --crate-type lib +//@ edition: 2018 #![crate_name = "foo"] #![feature(rustc_attrs)] diff --git a/tests/rustdoc/primitive-tuple-variadic.rs b/tests/rustdoc/primitive/primitive-tuple-variadic.rs similarity index 97% rename from tests/rustdoc/primitive-tuple-variadic.rs rename to tests/rustdoc/primitive/primitive-tuple-variadic.rs index d142729d2a8bc..bab5eaae9a236 100644 --- a/tests/rustdoc/primitive-tuple-variadic.rs +++ b/tests/rustdoc/primitive/primitive-tuple-variadic.rs @@ -1,4 +1,5 @@ -//@ compile-flags: --crate-type lib --edition 2018 +//@ compile-flags: --crate-type lib +//@ edition: 2018 #![crate_name = "foo"] #![feature(rustdoc_internals)] diff --git a/tests/rustdoc/primitive-unit-auto-trait.rs b/tests/rustdoc/primitive/primitive-unit-auto-trait.rs similarity index 90% rename from tests/rustdoc/primitive-unit-auto-trait.rs rename to tests/rustdoc/primitive/primitive-unit-auto-trait.rs index 6cae094c21c81..7dada1f9832e6 100644 --- a/tests/rustdoc/primitive-unit-auto-trait.rs +++ b/tests/rustdoc/primitive/primitive-unit-auto-trait.rs @@ -1,4 +1,5 @@ -//@ compile-flags: --crate-type lib --edition 2018 +//@ compile-flags: --crate-type lib +//@ edition: 2018 #![crate_name = "foo"] #![feature(rustc_attrs)] diff --git a/tests/rustdoc/search-index-primitive-inherent-method-23511.rs b/tests/rustdoc/primitive/search-index-primitive-inherent-method-23511.rs similarity index 100% rename from tests/rustdoc/search-index-primitive-inherent-method-23511.rs rename to tests/rustdoc/primitive/search-index-primitive-inherent-method-23511.rs diff --git a/tests/rustdoc/doc-hidden-private-67851-both.rs b/tests/rustdoc/private/doc-hidden-private-67851-both.rs similarity index 100% rename from tests/rustdoc/doc-hidden-private-67851-both.rs rename to tests/rustdoc/private/doc-hidden-private-67851-both.rs diff --git a/tests/rustdoc/doc-hidden-private-67851-hidden.rs b/tests/rustdoc/private/doc-hidden-private-67851-hidden.rs similarity index 100% rename from tests/rustdoc/doc-hidden-private-67851-hidden.rs rename to tests/rustdoc/private/doc-hidden-private-67851-hidden.rs diff --git a/tests/rustdoc/doc-hidden-private-67851-neither.rs b/tests/rustdoc/private/doc-hidden-private-67851-neither.rs similarity index 100% rename from tests/rustdoc/doc-hidden-private-67851-neither.rs rename to tests/rustdoc/private/doc-hidden-private-67851-neither.rs diff --git a/tests/rustdoc/doc-hidden-private-67851-private.rs b/tests/rustdoc/private/doc-hidden-private-67851-private.rs similarity index 100% rename from tests/rustdoc/doc-hidden-private-67851-private.rs rename to tests/rustdoc/private/doc-hidden-private-67851-private.rs diff --git a/tests/rustdoc/empty-impl-block-private-with-doc.rs b/tests/rustdoc/private/empty-impl-block-private-with-doc.rs similarity index 100% rename from tests/rustdoc/empty-impl-block-private-with-doc.rs rename to tests/rustdoc/private/empty-impl-block-private-with-doc.rs diff --git a/tests/rustdoc/empty-impl-block-private.rs b/tests/rustdoc/private/empty-impl-block-private.rs similarity index 100% rename from tests/rustdoc/empty-impl-block-private.rs rename to tests/rustdoc/private/empty-impl-block-private.rs diff --git a/tests/rustdoc/empty-mod-private.rs b/tests/rustdoc/private/empty-mod-private.rs similarity index 100% rename from tests/rustdoc/empty-mod-private.rs rename to tests/rustdoc/private/empty-mod-private.rs diff --git a/tests/rustdoc/enum-variant-private-46767.rs b/tests/rustdoc/private/enum-variant-private-46767.rs similarity index 100% rename from tests/rustdoc/enum-variant-private-46767.rs rename to tests/rustdoc/private/enum-variant-private-46767.rs diff --git a/tests/rustdoc/files-creation-private.rs b/tests/rustdoc/private/files-creation-private.rs similarity index 100% rename from tests/rustdoc/files-creation-private.rs rename to tests/rustdoc/private/files-creation-private.rs diff --git a/tests/rustdoc/hidden-private.rs b/tests/rustdoc/private/hidden-private.rs similarity index 100% rename from tests/rustdoc/hidden-private.rs rename to tests/rustdoc/private/hidden-private.rs diff --git a/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs b/tests/rustdoc/private/inline-private-with-intermediate-doc-hidden.rs similarity index 100% rename from tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs rename to tests/rustdoc/private/inline-private-with-intermediate-doc-hidden.rs diff --git a/tests/rustdoc/inner-private-110422.rs b/tests/rustdoc/private/inner-private-110422.rs similarity index 100% rename from tests/rustdoc/inner-private-110422.rs rename to tests/rustdoc/private/inner-private-110422.rs diff --git a/tests/rustdoc/macro-document-private-duplicate.rs b/tests/rustdoc/private/macro-document-private-duplicate.rs similarity index 100% rename from tests/rustdoc/macro-document-private-duplicate.rs rename to tests/rustdoc/private/macro-document-private-duplicate.rs diff --git a/tests/rustdoc/macro-document-private.rs b/tests/rustdoc/private/macro-document-private.rs similarity index 100% rename from tests/rustdoc/macro-document-private.rs rename to tests/rustdoc/private/macro-document-private.rs diff --git a/tests/rustdoc/macro-private-not-documented.rs b/tests/rustdoc/private/macro-private-not-documented.rs similarity index 100% rename from tests/rustdoc/macro-private-not-documented.rs rename to tests/rustdoc/private/macro-private-not-documented.rs diff --git a/tests/rustdoc/missing-private-inlining-109258.rs b/tests/rustdoc/private/missing-private-inlining-109258.rs similarity index 100% rename from tests/rustdoc/missing-private-inlining-109258.rs rename to tests/rustdoc/private/missing-private-inlining-109258.rs diff --git a/tests/rustdoc/private-fields-tuple-struct.rs b/tests/rustdoc/private/private-fields-tuple-struct.rs similarity index 100% rename from tests/rustdoc/private-fields-tuple-struct.rs rename to tests/rustdoc/private/private-fields-tuple-struct.rs diff --git a/tests/rustdoc/private-non-local-fields-2.rs b/tests/rustdoc/private/private-non-local-fields-2.rs similarity index 100% rename from tests/rustdoc/private-non-local-fields-2.rs rename to tests/rustdoc/private/private-non-local-fields-2.rs diff --git a/tests/rustdoc/private-non-local-fields.rs b/tests/rustdoc/private/private-non-local-fields.rs similarity index 100% rename from tests/rustdoc/private-non-local-fields.rs rename to tests/rustdoc/private/private-non-local-fields.rs diff --git a/tests/rustdoc/private-type-alias.rs b/tests/rustdoc/private/private-type-alias.rs similarity index 100% rename from tests/rustdoc/private-type-alias.rs rename to tests/rustdoc/private/private-type-alias.rs diff --git a/tests/rustdoc/private-type-cycle-110629.rs b/tests/rustdoc/private/private-type-cycle-110629.rs similarity index 100% rename from tests/rustdoc/private-type-cycle-110629.rs rename to tests/rustdoc/private/private-type-cycle-110629.rs diff --git a/tests/rustdoc/private-use-decl-macro-47038.rs b/tests/rustdoc/private/private-use-decl-macro-47038.rs similarity index 100% rename from tests/rustdoc/private-use-decl-macro-47038.rs rename to tests/rustdoc/private/private-use-decl-macro-47038.rs diff --git a/tests/rustdoc/private-use.rs b/tests/rustdoc/private/private-use.rs similarity index 100% rename from tests/rustdoc/private-use.rs rename to tests/rustdoc/private/private-use.rs diff --git a/tests/rustdoc/public-impl-mention-private-generic-46380-2.rs b/tests/rustdoc/private/public-impl-mention-private-generic-46380-2.rs similarity index 100% rename from tests/rustdoc/public-impl-mention-private-generic-46380-2.rs rename to tests/rustdoc/private/public-impl-mention-private-generic-46380-2.rs diff --git a/tests/rustdoc/traits-in-bodies-private.rs b/tests/rustdoc/private/traits-in-bodies-private.rs similarity index 100% rename from tests/rustdoc/traits-in-bodies-private.rs rename to tests/rustdoc/private/traits-in-bodies-private.rs diff --git a/tests/rustdoc/reexport-doc-hidden-inside-private.rs b/tests/rustdoc/reexport-doc-hidden-inside-private.rs deleted file mode 100644 index 8e194ef74fb82..0000000000000 --- a/tests/rustdoc/reexport-doc-hidden-inside-private.rs +++ /dev/null @@ -1,16 +0,0 @@ -// This test ensures that a re-export of `#[doc(hidden)]` item inside a private -// module will still be displayed (the re-export, not the item). - -#![crate_name = "foo"] - -mod private_module { - #[doc(hidden)] - pub struct Public; -} - -//@ has 'foo/index.html' -//@ has - '//*[@id="reexport.Foo"]/code' 'pub use crate::private_module::Public as Foo;' -pub use crate::private_module::Public as Foo; -// Glob re-exports with no visible items should not be displayed. -//@ count - '//*[@class="item-table reexports"]/dt' 1 -pub use crate::private_module::*; diff --git a/tests/rustdoc/reexport-of-doc-hidden.rs b/tests/rustdoc/reexport-of-doc-hidden.rs deleted file mode 100644 index 21511bc2aea99..0000000000000 --- a/tests/rustdoc/reexport-of-doc-hidden.rs +++ /dev/null @@ -1,42 +0,0 @@ -// This test ensures that all re-exports of doc hidden elements are displayed. - -#![crate_name = "foo"] - -#[doc(hidden)] -pub struct Bar; - -#[macro_export] -#[doc(hidden)] -macro_rules! foo { - () => {}; -} - -//@ has 'foo/index.html' -//@ has - '//*[@id="reexport.Macro"]/code' 'pub use crate::foo as Macro;' -pub use crate::foo as Macro; -//@ has - '//*[@id="reexport.Macro2"]/code' 'pub use crate::foo as Macro2;' -pub use crate::foo as Macro2; -//@ has - '//*[@id="reexport.Boo"]/code' 'pub use crate::Bar as Boo;' -pub use crate::Bar as Boo; -//@ has - '//*[@id="reexport.Boo2"]/code' 'pub use crate::Bar as Boo2;' -pub use crate::Bar as Boo2; - -pub fn fofo() {} - -//@ has - '//*[@id="reexport.f1"]/code' 'pub use crate::fofo as f1;' -pub use crate::fofo as f1; -//@ has - '//*[@id="reexport.f2"]/code' 'pub use crate::fofo as f2;' -pub use crate::fofo as f2; - -pub mod sub { - //@ has 'foo/sub/index.html' - //@ has - '//*[@id="reexport.Macro"]/code' 'pub use crate::foo as Macro;' - pub use crate::foo as Macro; - //@ has - '//*[@id="reexport.Macro2"]/code' 'pub use crate::foo as Macro2;' - pub use crate::foo as Macro2; - - //@ has - '//*[@id="reexport.f1"]/code' 'pub use crate::fofo as f1;' - pub use crate::fofo as f1; - //@ has - '//*[@id="reexport.f2"]/code' 'pub use crate::fofo as f2;' - pub use crate::fofo as f2; -} diff --git a/tests/rustdoc/alias-reexport.rs b/tests/rustdoc/reexport/alias-reexport.rs similarity index 100% rename from tests/rustdoc/alias-reexport.rs rename to tests/rustdoc/reexport/alias-reexport.rs diff --git a/tests/rustdoc/alias-reexport2.rs b/tests/rustdoc/reexport/alias-reexport2.rs similarity index 100% rename from tests/rustdoc/alias-reexport2.rs rename to tests/rustdoc/reexport/alias-reexport2.rs diff --git a/tests/rustdoc/anonymous-reexport-108931.rs b/tests/rustdoc/reexport/anonymous-reexport-108931.rs similarity index 100% rename from tests/rustdoc/anonymous-reexport-108931.rs rename to tests/rustdoc/reexport/anonymous-reexport-108931.rs diff --git a/tests/rustdoc/anonymous-reexport.rs b/tests/rustdoc/reexport/anonymous-reexport.rs similarity index 100% rename from tests/rustdoc/anonymous-reexport.rs rename to tests/rustdoc/reexport/anonymous-reexport.rs diff --git a/tests/rustdoc/auxiliary/alias-reexport.rs b/tests/rustdoc/reexport/auxiliary/alias-reexport.rs similarity index 100% rename from tests/rustdoc/auxiliary/alias-reexport.rs rename to tests/rustdoc/reexport/auxiliary/alias-reexport.rs diff --git a/tests/rustdoc/auxiliary/alias-reexport2.rs b/tests/rustdoc/reexport/auxiliary/alias-reexport2.rs similarity index 100% rename from tests/rustdoc/auxiliary/alias-reexport2.rs rename to tests/rustdoc/reexport/auxiliary/alias-reexport2.rs diff --git a/tests/rustdoc/reexport/auxiliary/all-item-types.rs b/tests/rustdoc/reexport/auxiliary/all-item-types.rs new file mode 100644 index 0000000000000..f94bd998717a0 --- /dev/null +++ b/tests/rustdoc/reexport/auxiliary/all-item-types.rs @@ -0,0 +1,22 @@ +#![feature(extern_types)] + +pub mod foo_mod {} +extern "C" { + pub fn foo_ffn(); + pub static FOO_FSTATIC: FooStruct; + pub type FooFType; +} +pub fn foo_fn() {} +pub trait FooTrait {} +pub struct FooStruct; +pub enum FooEnum {} +pub union FooUnion { + x: (), +} +pub type FooType = FooStruct; +pub static FOO_STATIC: FooStruct = FooStruct; +pub const FOO_CONSTANT: FooStruct = FooStruct; +#[macro_export] +macro_rules! foo_macro { + () => (); +} diff --git a/tests/rustdoc/auxiliary/issue-113982-doc_auto_cfg-reexport-foreign.rs b/tests/rustdoc/reexport/auxiliary/issue-113982-doc_auto_cfg-reexport-foreign.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-113982-doc_auto_cfg-reexport-foreign.rs rename to tests/rustdoc/reexport/auxiliary/issue-113982-doc_auto_cfg-reexport-foreign.rs diff --git a/tests/rustdoc/auxiliary/issue-28927-1.rs b/tests/rustdoc/reexport/auxiliary/issue-28927-1.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-28927-1.rs rename to tests/rustdoc/reexport/auxiliary/issue-28927-1.rs diff --git a/tests/rustdoc/auxiliary/issue-28927-2.rs b/tests/rustdoc/reexport/auxiliary/issue-28927-2.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-28927-2.rs rename to tests/rustdoc/reexport/auxiliary/issue-28927-2.rs diff --git a/tests/rustdoc/reexport/auxiliary/primitive-reexport.rs b/tests/rustdoc/reexport/auxiliary/primitive-reexport.rs new file mode 100644 index 0000000000000..7c85038674b66 --- /dev/null +++ b/tests/rustdoc/reexport/auxiliary/primitive-reexport.rs @@ -0,0 +1,9 @@ +//@ compile-flags: --emit metadata --crate-type lib +//@ edition: 2018 + +#![crate_name = "foo"] + +pub mod bar { + pub use bool; + pub use char as my_char; +} diff --git a/tests/rustdoc/auxiliary/reexport-check.rs b/tests/rustdoc/reexport/auxiliary/reexport-check.rs similarity index 100% rename from tests/rustdoc/auxiliary/reexport-check.rs rename to tests/rustdoc/reexport/auxiliary/reexport-check.rs diff --git a/tests/rustdoc/auxiliary/reexport-doc-aux.rs b/tests/rustdoc/reexport/auxiliary/reexport-doc-aux.rs similarity index 100% rename from tests/rustdoc/auxiliary/reexport-doc-aux.rs rename to tests/rustdoc/reexport/auxiliary/reexport-doc-aux.rs diff --git a/tests/rustdoc/auxiliary/reexports.rs b/tests/rustdoc/reexport/auxiliary/reexports.rs similarity index 100% rename from tests/rustdoc/auxiliary/reexports.rs rename to tests/rustdoc/reexport/auxiliary/reexports.rs diff --git a/tests/rustdoc/blanket-reexport-item.rs b/tests/rustdoc/reexport/blanket-reexport-item.rs similarity index 100% rename from tests/rustdoc/blanket-reexport-item.rs rename to tests/rustdoc/reexport/blanket-reexport-item.rs diff --git a/tests/rustdoc/cfg_doc_reexport.rs b/tests/rustdoc/reexport/cfg_doc_reexport.rs similarity index 100% rename from tests/rustdoc/cfg_doc_reexport.rs rename to tests/rustdoc/reexport/cfg_doc_reexport.rs diff --git a/tests/rustdoc/doc-hidden-reexports-109449.rs b/tests/rustdoc/reexport/doc-hidden-reexports-109449.rs similarity index 88% rename from tests/rustdoc/doc-hidden-reexports-109449.rs rename to tests/rustdoc/reexport/doc-hidden-reexports-109449.rs index 8f195544120af..78b9214300a95 100644 --- a/tests/rustdoc/doc-hidden-reexports-109449.rs +++ b/tests/rustdoc/reexport/doc-hidden-reexports-109449.rs @@ -26,21 +26,21 @@ pub mod single_reexport { //@ has 'foo/single_reexport/index.html' // First we check that we have 4 type aliases. - //@ count - '//*[@id="main-content"]/*[@class="item-table reexports"]//code' 4 + //@ count - '//*[@id="main-content"]/*[@class="item-table reexports"]//code' 0 // Then we check that we have the correct link for each re-export. //@ !has - '//*[@href="struct.Foo.html"]' 'Foo' - //@ has - '//*[@id="reexport.Foo"]/code' 'pub use crate::private_module::Public as Foo;' + //@ !has - '//*[@id="reexport.Foo"]/code' 'pub use crate::private_module::Public as Foo;' pub use crate::private_module::Public as Foo; //@ !has - '//*[@href="type.Foo2.html"]' 'Foo2' - //@ has - '//*[@id="reexport.Foo2"]/code' 'pub use crate::private_module::Bar as Foo2;' + //@ !has - '//*[@id="reexport.Foo2"]/code' 'pub use crate::private_module::Bar as Foo2;' pub use crate::private_module::Bar as Foo2; //@ !has - '//*[@href="type.Yo.html"]' 'Yo' - //@ has - '//*[@id="reexport.Yo"]/code' 'pub use crate::Bar3 as Yo;' + //@ !has - '//*[@id="reexport.Yo"]/code' 'pub use crate::Bar3 as Yo;' pub use crate::Bar3 as Yo; //@ !has - '//*[@href="struct.Yo2.html"]' 'Yo2' - //@ has - '//*[@id="reexport.Yo2"]/code' 'pub use crate::FooFoo as Yo2;' + //@ !has - '//*[@id="reexport.Yo2"]/code' 'pub use crate::FooFoo as Yo2;' pub use crate::FooFoo as Yo2; // Checking that each file is also created as expected. @@ -70,19 +70,19 @@ pub mod single_reexport_no_inline { //@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Re-exports' // Now we check that we don't have links to the items, just `pub use`. - //@ has - '//*[@id="main-content"]//*' 'pub use crate::private_module::Public as XFoo;' + //@ !has - '//*[@id="main-content"]//*' 'pub use crate::private_module::Public as XFoo;' //@ !has - '//*[@id="main-content"]//a' 'XFoo' #[doc(no_inline)] pub use crate::private_module::Public as XFoo; - //@ has - '//*[@id="main-content"]//*' 'pub use crate::private_module::Bar as Foo2;' + //@ !has - '//*[@id="main-content"]//*' 'pub use crate::private_module::Bar as Foo2;' //@ !has - '//*[@id="main-content"]//a' 'Foo2' #[doc(no_inline)] pub use crate::private_module::Bar as Foo2; - //@ has - '//*[@id="main-content"]//*' 'pub use crate::Bar3 as Yo;' + //@ !has - '//*[@id="main-content"]//*' 'pub use crate::Bar3 as Yo;' //@ !has - '//*[@id="main-content"]//a' 'Yo' #[doc(no_inline)] pub use crate::Bar3 as Yo; - //@ has - '//*[@id="main-content"]//*' 'pub use crate::FooFoo as Yo2;' + //@ !has - '//*[@id="main-content"]//*' 'pub use crate::FooFoo as Yo2;' //@ !has - '//*[@id="main-content"]//a' 'Yo2' #[doc(no_inline)] pub use crate::FooFoo as Yo2; diff --git a/tests/rustdoc/doc_auto_cfg-reexport-foreign-113982.rs b/tests/rustdoc/reexport/doc_auto_cfg-reexport-foreign-113982.rs similarity index 100% rename from tests/rustdoc/doc_auto_cfg-reexport-foreign-113982.rs rename to tests/rustdoc/reexport/doc_auto_cfg-reexport-foreign-113982.rs diff --git a/tests/rustdoc/duplicated-glob-reexport-60522.rs b/tests/rustdoc/reexport/duplicated-glob-reexport-60522.rs similarity index 100% rename from tests/rustdoc/duplicated-glob-reexport-60522.rs rename to tests/rustdoc/reexport/duplicated-glob-reexport-60522.rs diff --git a/tests/rustdoc/enum-variant-reexport-35488.rs b/tests/rustdoc/reexport/enum-variant-reexport-35488.rs similarity index 100% rename from tests/rustdoc/enum-variant-reexport-35488.rs rename to tests/rustdoc/reexport/enum-variant-reexport-35488.rs diff --git a/tests/rustdoc/foreigntype-reexport.rs b/tests/rustdoc/reexport/foreigntype-reexport.rs similarity index 100% rename from tests/rustdoc/foreigntype-reexport.rs rename to tests/rustdoc/reexport/foreigntype-reexport.rs diff --git a/tests/rustdoc/glob-reexport-attribute-merge-120487.rs b/tests/rustdoc/reexport/glob-reexport-attribute-merge-120487.rs similarity index 100% rename from tests/rustdoc/glob-reexport-attribute-merge-120487.rs rename to tests/rustdoc/reexport/glob-reexport-attribute-merge-120487.rs diff --git a/tests/rustdoc/glob-reexport-attribute-merge-doc-auto-cfg.rs b/tests/rustdoc/reexport/glob-reexport-attribute-merge-doc-auto-cfg.rs similarity index 100% rename from tests/rustdoc/glob-reexport-attribute-merge-doc-auto-cfg.rs rename to tests/rustdoc/reexport/glob-reexport-attribute-merge-doc-auto-cfg.rs diff --git a/tests/rustdoc/ice-reexport-crate-root-28927.rs b/tests/rustdoc/reexport/ice-reexport-crate-root-28927.rs similarity index 100% rename from tests/rustdoc/ice-reexport-crate-root-28927.rs rename to tests/rustdoc/reexport/ice-reexport-crate-root-28927.rs diff --git a/tests/rustdoc/local-reexport-doc.rs b/tests/rustdoc/reexport/local-reexport-doc.rs similarity index 100% rename from tests/rustdoc/local-reexport-doc.rs rename to tests/rustdoc/reexport/local-reexport-doc.rs diff --git a/tests/rustdoc/no-compiler-reexport.rs b/tests/rustdoc/reexport/no-compiler-reexport.rs similarity index 100% rename from tests/rustdoc/no-compiler-reexport.rs rename to tests/rustdoc/reexport/no-compiler-reexport.rs diff --git a/tests/rustdoc/overlapping-reexport-105735-2.rs b/tests/rustdoc/reexport/overlapping-reexport-105735-2.rs similarity index 100% rename from tests/rustdoc/overlapping-reexport-105735-2.rs rename to tests/rustdoc/reexport/overlapping-reexport-105735-2.rs diff --git a/tests/rustdoc/overlapping-reexport-105735.rs b/tests/rustdoc/reexport/overlapping-reexport-105735.rs similarity index 100% rename from tests/rustdoc/overlapping-reexport-105735.rs rename to tests/rustdoc/reexport/overlapping-reexport-105735.rs diff --git a/tests/rustdoc/reexport/primitive-reexport.rs b/tests/rustdoc/reexport/primitive-reexport.rs new file mode 100644 index 0000000000000..9b23b24fc93a7 --- /dev/null +++ b/tests/rustdoc/reexport/primitive-reexport.rs @@ -0,0 +1,29 @@ +//@ aux-build: primitive-reexport.rs +//@ compile-flags: --extern foo +//@ edition: 2018 + +#![crate_name = "bar"] + +//@ has bar/p/index.html +//@ has - '//code' 'pub use bool;' +//@ has - '//code/a[@href="{{channel}}/std/primitive.bool.html"]' 'bool' +//@ has - '//code' 'pub use char as my_char;' +//@ has - '//code/a[@href="{{channel}}/std/primitive.char.html"]' 'char' +pub mod p { + pub use foo::bar::*; +} + +//@ has bar/baz/index.html +//@ has - '//code' 'pub use bool;' +//@ has - '//code/a[@href="{{channel}}/std/primitive.bool.html"]' 'bool' +//@ has - '//code' 'pub use char as my_char;' +//@ has - '//code/a[@href="{{channel}}/std/primitive.char.html"]' 'char' +pub use foo::bar as baz; + +//@ has bar/index.html +//@ has - '//code' 'pub use str;' +//@ has - '//code/a[@href="{{channel}}/std/primitive.str.html"]' 'str' +//@ has - '//code' 'pub use i32 as my_i32;' +//@ has - '//code/a[@href="{{channel}}/std/primitive.i32.html"]' 'i32' +pub use str; +pub use i32 as my_i32; diff --git a/tests/rustdoc/pub-reexport-of-pub-reexport-46506.rs b/tests/rustdoc/reexport/pub-reexport-of-pub-reexport-46506.rs similarity index 100% rename from tests/rustdoc/pub-reexport-of-pub-reexport-46506.rs rename to tests/rustdoc/reexport/pub-reexport-of-pub-reexport-46506.rs diff --git a/tests/rustdoc/reexport-attr-merge.rs b/tests/rustdoc/reexport/reexport-attr-merge.rs similarity index 90% rename from tests/rustdoc/reexport-attr-merge.rs rename to tests/rustdoc/reexport/reexport-attr-merge.rs index e4a406c3845ad..aef302eb0b29a 100644 --- a/tests/rustdoc/reexport-attr-merge.rs +++ b/tests/rustdoc/reexport/reexport-attr-merge.rs @@ -18,7 +18,7 @@ pub use Foo1 as Foo2; // First we ensure that only the reexport `Bar2` and the inlined struct `Bar` // are inlined. -//@ count - '//a[@class="struct"]' 2 +//@ count - '//a[@class="struct"]' 1 // Then we check that `cfg` is displayed for base item, but not for intermediate re-exports. //@ has - '//*[@class="stab portability"]' 'foo' //@ !has - '//*[@class="stab portability"]' 'bar' @@ -29,5 +29,5 @@ pub use Foo2 as Bar; // This one should appear but `Bar2` won't be linked because there is no // `#[doc(inline)]`. -//@ has - '//*[@id="reexport.Bar2"]' 'pub use Foo2 as Bar2;' +//@ !has - '//*[@id="reexport.Bar2"]' 'pub use Foo2 as Bar2;' pub use Foo2 as Bar2; diff --git a/tests/rustdoc/reexport-cfg.rs b/tests/rustdoc/reexport/reexport-cfg.rs similarity index 100% rename from tests/rustdoc/reexport-cfg.rs rename to tests/rustdoc/reexport/reexport-cfg.rs diff --git a/tests/rustdoc/reexport-check.rs b/tests/rustdoc/reexport/reexport-check.rs similarity index 100% rename from tests/rustdoc/reexport-check.rs rename to tests/rustdoc/reexport/reexport-check.rs diff --git a/tests/rustdoc/reexport-dep-foreign-fn.rs b/tests/rustdoc/reexport/reexport-dep-foreign-fn.rs similarity index 100% rename from tests/rustdoc/reexport-dep-foreign-fn.rs rename to tests/rustdoc/reexport/reexport-dep-foreign-fn.rs diff --git a/tests/rustdoc/reexport/reexport-doc-hidden-inside-private.rs b/tests/rustdoc/reexport/reexport-doc-hidden-inside-private.rs new file mode 100644 index 0000000000000..bae2aa78ec7a3 --- /dev/null +++ b/tests/rustdoc/reexport/reexport-doc-hidden-inside-private.rs @@ -0,0 +1,16 @@ +// This test ensures that a re-export of `#[doc(hidden)]` item inside a private +// module will still be displayed (the re-export, not the item). + +#![crate_name = "foo"] + +mod private_module { + #[doc(hidden)] + pub struct Public; +} + +//@ has 'foo/index.html' +//@ !has - '//*[@id="reexport.Foo"]/code' 'pub use crate::private_module::Public as Foo;' +pub use crate::private_module::Public as Foo; +// Glob re-exports with no visible items should not be displayed. +//@ count - '//*[@class="item-table reexports"]/dt' 0 +pub use crate::private_module::*; diff --git a/tests/rustdoc/reexport-doc-hidden.rs b/tests/rustdoc/reexport/reexport-doc-hidden.rs similarity index 75% rename from tests/rustdoc/reexport-doc-hidden.rs rename to tests/rustdoc/reexport/reexport-doc-hidden.rs index b912362f2987a..1468e9ad957f7 100644 --- a/tests/rustdoc/reexport-doc-hidden.rs +++ b/tests/rustdoc/reexport/reexport-doc-hidden.rs @@ -8,7 +8,7 @@ pub type Type = u32; //@ has 'foo/index.html' -//@ has - '//*[@id="reexport.Type2"]/code' 'pub use crate::Type as Type2;' +//@ !has - '//*[@id="reexport.Type2"]/code' 'pub use crate::Type as Type2;' pub use crate::Type as Type2; //@ count - '//*[@id="reexport.Type3"]' 0 @@ -21,5 +21,5 @@ macro_rules! foo { () => {}; } -//@ has - '//*[@id="reexport.Macro"]/code' 'pub use crate::foo as Macro;' +//@ !has - '//*[@id="reexport.Macro"]/code' 'pub use crate::foo as Macro;' pub use crate::foo as Macro; diff --git a/tests/rustdoc/reexport-doc.rs b/tests/rustdoc/reexport/reexport-doc.rs similarity index 100% rename from tests/rustdoc/reexport-doc.rs rename to tests/rustdoc/reexport/reexport-doc.rs diff --git a/tests/rustdoc/reexport-hidden-macro.rs b/tests/rustdoc/reexport/reexport-hidden-macro.rs similarity index 86% rename from tests/rustdoc/reexport-hidden-macro.rs rename to tests/rustdoc/reexport/reexport-hidden-macro.rs index 9b83bca3906d2..7345149c64522 100644 --- a/tests/rustdoc/reexport-hidden-macro.rs +++ b/tests/rustdoc/reexport/reexport-hidden-macro.rs @@ -5,7 +5,7 @@ //@ has 'foo/index.html' //@ has - '//*[@id="main-content"]//a[@href="macro.Macro2.html"]' 'Macro2' -//@ has - '//*[@id="reexport.Macro"]/code' 'pub use crate::foo as Macro;' +//@ !has - '//*[@id="reexport.Macro"]/code' 'pub use crate::foo as Macro;' //@ has 'foo/macro.Macro2.html' //@ has - '//*[@class="docblock"]' 'Displayed' diff --git a/tests/rustdoc/reexport-macro.rs b/tests/rustdoc/reexport/reexport-macro.rs similarity index 100% rename from tests/rustdoc/reexport-macro.rs rename to tests/rustdoc/reexport/reexport-macro.rs diff --git a/tests/rustdoc/reexport/reexport-of-doc-hidden.rs b/tests/rustdoc/reexport/reexport-of-doc-hidden.rs new file mode 100644 index 0000000000000..e901d0ff8a2bc --- /dev/null +++ b/tests/rustdoc/reexport/reexport-of-doc-hidden.rs @@ -0,0 +1,42 @@ +// This test ensures that all re-exports of doc hidden elements are displayed. + +#![crate_name = "foo"] + +#[doc(hidden)] +pub struct Bar; + +#[macro_export] +#[doc(hidden)] +macro_rules! foo { + () => {}; +} + +//@ has 'foo/index.html' +//@ !has - '//*[@id="reexport.Macro"]/code' 'pub use crate::foo as Macro;' +pub use crate::foo as Macro; +//@ !has - '//*[@id="reexport.Macro2"]/code' 'pub use crate::foo as Macro2;' +pub use crate::foo as Macro2; +//@ !has - '//*[@id="reexport.Boo"]/code' 'pub use crate::Bar as Boo;' +pub use crate::Bar as Boo; +//@ !has - '//*[@id="reexport.Boo2"]/code' 'pub use crate::Bar as Boo2;' +pub use crate::Bar as Boo2; + +pub fn fofo() {} + +//@ has - '//*[@id="reexport.f1"]/code' 'pub use crate::fofo as f1;' +pub use crate::fofo as f1; +//@ has - '//*[@id="reexport.f2"]/code' 'pub use crate::fofo as f2;' +pub use crate::fofo as f2; + +pub mod sub { + //@ has 'foo/sub/index.html' + //@ !has - '//*[@id="reexport.Macro"]/code' 'pub use crate::foo as Macro;' + pub use crate::foo as Macro; + //@ !has - '//*[@id="reexport.Macro2"]/code' 'pub use crate::foo as Macro2;' + pub use crate::foo as Macro2; + + //@ has - '//*[@id="reexport.f1"]/code' 'pub use crate::fofo as f1;' + pub use crate::fofo as f1; + //@ has - '//*[@id="reexport.f2"]/code' 'pub use crate::fofo as f2;' + pub use crate::fofo as f2; +} diff --git a/tests/rustdoc/reexport-of-reexport-108679.rs b/tests/rustdoc/reexport/reexport-of-reexport-108679.rs similarity index 100% rename from tests/rustdoc/reexport-of-reexport-108679.rs rename to tests/rustdoc/reexport/reexport-of-reexport-108679.rs diff --git a/tests/rustdoc/reexport-stability-tags-deprecated-and-portability.rs b/tests/rustdoc/reexport/reexport-stability-tags-deprecated-and-portability.rs similarity index 100% rename from tests/rustdoc/reexport-stability-tags-deprecated-and-portability.rs rename to tests/rustdoc/reexport/reexport-stability-tags-deprecated-and-portability.rs diff --git a/tests/rustdoc/reexport-stability-tags-unstable-and-portability.rs b/tests/rustdoc/reexport/reexport-stability-tags-unstable-and-portability.rs similarity index 100% rename from tests/rustdoc/reexport-stability-tags-unstable-and-portability.rs rename to tests/rustdoc/reexport/reexport-stability-tags-unstable-and-portability.rs diff --git a/tests/rustdoc/reexport-trait-from-hidden-111064-2.rs b/tests/rustdoc/reexport/reexport-trait-from-hidden-111064-2.rs similarity index 100% rename from tests/rustdoc/reexport-trait-from-hidden-111064-2.rs rename to tests/rustdoc/reexport/reexport-trait-from-hidden-111064-2.rs diff --git a/tests/rustdoc/reexport-trait-from-hidden-111064.rs b/tests/rustdoc/reexport/reexport-trait-from-hidden-111064.rs similarity index 100% rename from tests/rustdoc/reexport-trait-from-hidden-111064.rs rename to tests/rustdoc/reexport/reexport-trait-from-hidden-111064.rs diff --git a/tests/rustdoc/reexports-of-same-name.rs b/tests/rustdoc/reexport/reexports-of-same-name.rs similarity index 100% rename from tests/rustdoc/reexports-of-same-name.rs rename to tests/rustdoc/reexport/reexports-of-same-name.rs diff --git a/tests/rustdoc/reexports-priv.rs b/tests/rustdoc/reexport/reexports-priv.rs similarity index 100% rename from tests/rustdoc/reexports-priv.rs rename to tests/rustdoc/reexport/reexports-priv.rs diff --git a/tests/rustdoc/reexports.rs b/tests/rustdoc/reexport/reexports.rs similarity index 100% rename from tests/rustdoc/reexports.rs rename to tests/rustdoc/reexport/reexports.rs diff --git a/tests/rustdoc/return-type-notation.rs b/tests/rustdoc/return-type-notation.rs new file mode 100644 index 0000000000000..405e98eb28d57 --- /dev/null +++ b/tests/rustdoc/return-type-notation.rs @@ -0,0 +1,18 @@ +//@ edition: 2021 + +#![crate_type = "lib"] +#![feature(return_type_notation)] + +pub trait Foo { + async fn bar(); +} + +//@ has "return_type_notation/fn.foo.html" +//@ has - '//pre[@class="rust item-decl"]' "pub fn foo>()" +//@ has - '//pre[@class="rust item-decl"]' "where ::bar(..): 'static, T::bar(..): Sync" +pub fn foo>() +where + ::bar(..): 'static, + T::bar(..): Sync, +{ +} diff --git a/tests/rustdoc/assoc-type-source-link.rs b/tests/rustdoc/source-code-pages/assoc-type-source-link.rs similarity index 100% rename from tests/rustdoc/assoc-type-source-link.rs rename to tests/rustdoc/source-code-pages/assoc-type-source-link.rs diff --git a/tests/rustdoc/auxiliary/issue-26606-macro.rs b/tests/rustdoc/source-code-pages/auxiliary/issue-26606-macro.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-26606-macro.rs rename to tests/rustdoc/source-code-pages/auxiliary/issue-26606-macro.rs diff --git a/tests/rustdoc/auxiliary/issue-34274.rs b/tests/rustdoc/source-code-pages/auxiliary/issue-34274.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-34274.rs rename to tests/rustdoc/source-code-pages/auxiliary/issue-34274.rs diff --git a/tests/rustdoc/auxiliary/source-code-bar.rs b/tests/rustdoc/source-code-pages/auxiliary/source-code-bar.rs similarity index 100% rename from tests/rustdoc/auxiliary/source-code-bar.rs rename to tests/rustdoc/source-code-pages/auxiliary/source-code-bar.rs diff --git a/tests/rustdoc/auxiliary/source_code.rs b/tests/rustdoc/source-code-pages/auxiliary/source_code.rs similarity index 100% rename from tests/rustdoc/auxiliary/source_code.rs rename to tests/rustdoc/source-code-pages/auxiliary/source_code.rs diff --git a/tests/rustdoc/auxiliary/src-links-external.rs b/tests/rustdoc/source-code-pages/auxiliary/src-links-external.rs similarity index 100% rename from tests/rustdoc/auxiliary/src-links-external.rs rename to tests/rustdoc/source-code-pages/auxiliary/src-links-external.rs diff --git a/tests/rustdoc/check-source-code-urls-to-def-std.rs b/tests/rustdoc/source-code-pages/check-source-code-urls-to-def-std.rs similarity index 100% rename from tests/rustdoc/check-source-code-urls-to-def-std.rs rename to tests/rustdoc/source-code-pages/check-source-code-urls-to-def-std.rs diff --git a/tests/rustdoc/check-source-code-urls-to-def.rs b/tests/rustdoc/source-code-pages/check-source-code-urls-to-def.rs similarity index 100% rename from tests/rustdoc/check-source-code-urls-to-def.rs rename to tests/rustdoc/source-code-pages/check-source-code-urls-to-def.rs diff --git a/tests/rustdoc/source-code-pages/doc-hidden-source.rs b/tests/rustdoc/source-code-pages/doc-hidden-source.rs new file mode 100644 index 0000000000000..b6bc622dd584f --- /dev/null +++ b/tests/rustdoc/source-code-pages/doc-hidden-source.rs @@ -0,0 +1,16 @@ +// Test for . + +#![crate_name = "foo"] + +//@ has 'foo/index.html' +//@ !has - '//*[@id="main-content"]//*[@class="struct"]' 'Bar' +#[doc(hidden)] +pub struct Bar; + +//@ !has - '//*' 'pub use crate::Bar as A;' +pub use crate::Bar as A; +//@ !has - '//*' 'pub use crate::A as B;' +pub use crate::A as B; +//@ has - '//dt/a[@class="struct"]' 'C' +#[doc(inline)] +pub use crate::Bar as C; diff --git a/tests/rustdoc/html-no-source.rs b/tests/rustdoc/source-code-pages/html-no-source.rs similarity index 100% rename from tests/rustdoc/html-no-source.rs rename to tests/rustdoc/source-code-pages/html-no-source.rs diff --git a/tests/rustdoc/source-code-highlight.rs b/tests/rustdoc/source-code-pages/source-code-highlight.rs similarity index 100% rename from tests/rustdoc/source-code-highlight.rs rename to tests/rustdoc/source-code-pages/source-code-highlight.rs diff --git a/tests/rustdoc/source-file.rs b/tests/rustdoc/source-code-pages/source-file.rs similarity index 100% rename from tests/rustdoc/source-file.rs rename to tests/rustdoc/source-code-pages/source-file.rs diff --git a/tests/rustdoc/source-line-numbers.rs b/tests/rustdoc/source-code-pages/source-line-numbers.rs similarity index 100% rename from tests/rustdoc/source-line-numbers.rs rename to tests/rustdoc/source-code-pages/source-line-numbers.rs diff --git a/tests/rustdoc/source-version-separator.rs b/tests/rustdoc/source-code-pages/source-version-separator.rs similarity index 100% rename from tests/rustdoc/source-version-separator.rs rename to tests/rustdoc/source-code-pages/source-version-separator.rs diff --git a/tests/rustdoc/src-link-external-macro-26606.rs b/tests/rustdoc/source-code-pages/src-link-external-macro-26606.rs similarity index 100% rename from tests/rustdoc/src-link-external-macro-26606.rs rename to tests/rustdoc/source-code-pages/src-link-external-macro-26606.rs diff --git a/tests/rustdoc/src-links-auto-impls.rs b/tests/rustdoc/source-code-pages/src-links-auto-impls.rs similarity index 100% rename from tests/rustdoc/src-links-auto-impls.rs rename to tests/rustdoc/source-code-pages/src-links-auto-impls.rs diff --git a/tests/rustdoc/src-links-external.rs b/tests/rustdoc/source-code-pages/src-links-external.rs similarity index 100% rename from tests/rustdoc/src-links-external.rs rename to tests/rustdoc/source-code-pages/src-links-external.rs diff --git a/tests/rustdoc/src-links-implementor-43893.rs b/tests/rustdoc/source-code-pages/src-links-implementor-43893.rs similarity index 100% rename from tests/rustdoc/src-links-implementor-43893.rs rename to tests/rustdoc/source-code-pages/src-links-implementor-43893.rs diff --git a/tests/rustdoc/src-links-inlined-34274.rs b/tests/rustdoc/source-code-pages/src-links-inlined-34274.rs similarity index 100% rename from tests/rustdoc/src-links-inlined-34274.rs rename to tests/rustdoc/source-code-pages/src-links-inlined-34274.rs diff --git a/tests/rustdoc/src-links.rs b/tests/rustdoc/source-code-pages/src-links.rs similarity index 100% rename from tests/rustdoc/src-links.rs rename to tests/rustdoc/source-code-pages/src-links.rs diff --git a/tests/rustdoc/src-links/compiletest-ignore-dir b/tests/rustdoc/source-code-pages/src-links/compiletest-ignore-dir similarity index 100% rename from tests/rustdoc/src-links/compiletest-ignore-dir rename to tests/rustdoc/source-code-pages/src-links/compiletest-ignore-dir diff --git a/tests/rustdoc/src-links/fizz.rs b/tests/rustdoc/source-code-pages/src-links/fizz.rs similarity index 100% rename from tests/rustdoc/src-links/fizz.rs rename to tests/rustdoc/source-code-pages/src-links/fizz.rs diff --git a/tests/rustdoc/src-links/mod.rs b/tests/rustdoc/source-code-pages/src-links/mod.rs similarity index 100% rename from tests/rustdoc/src-links/mod.rs rename to tests/rustdoc/source-code-pages/src-links/mod.rs diff --git a/tests/rustdoc/src-mod-path-absolute-26995.rs b/tests/rustdoc/source-code-pages/src-mod-path-absolute-26995.rs similarity index 100% rename from tests/rustdoc/src-mod-path-absolute-26995.rs rename to tests/rustdoc/source-code-pages/src-mod-path-absolute-26995.rs diff --git a/tests/rustdoc/version-separator-without-source.rs b/tests/rustdoc/source-code-pages/version-separator-without-source.rs similarity index 100% rename from tests/rustdoc/version-separator-without-source.rs rename to tests/rustdoc/source-code-pages/version-separator-without-source.rs diff --git a/tests/rustdoc/trait-alias-mention.rs b/tests/rustdoc/trait-alias-mention.rs deleted file mode 100644 index b6ef926e644e5..0000000000000 --- a/tests/rustdoc/trait-alias-mention.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ aux-build:trait-alias-mention.rs -//@ build-aux-docs - -#![crate_name = "foo"] - -extern crate trait_alias_mention; - -//@ has foo/fn.mention_alias_in_bounds.html '//a[@href="../trait_alias_mention/traitalias.SomeAlias.html"]' 'SomeAlias' -pub fn mention_alias_in_bounds() { -} diff --git a/tests/rustdoc/trait-aliases.rs b/tests/rustdoc/trait-aliases.rs new file mode 100644 index 0000000000000..1be93f72042c2 --- /dev/null +++ b/tests/rustdoc/trait-aliases.rs @@ -0,0 +1,82 @@ +// Basic testing for trait aliases. +#![feature(trait_alias)] +#![crate_name = "it"] + +// Check the "local case" (HIR cleaning) // + +//@ has it/all.html '//a[@href="traitalias.Alias0.html"]' 'Alias0' +//@ has it/index.html '//h2[@id="trait-aliases"]' 'Trait Aliases' +//@ has it/index.html '//a[@class="traitalias"]' 'Alias0' +//@ has it/traitalias.Alias0.html +//@ has - '//*[@class="rust item-decl"]//code' 'trait Alias0 = Copy + Iterator;' +pub trait Alias0 = Copy + Iterator; + +//@ has it/traitalias.Alias1.html +//@ has - '//pre[@class="rust item-decl"]' \ +// "trait Alias1<'a, T: 'a + Clone, const N: usize> = From<[&'a T; N]>;" +pub trait Alias1<'a, T: 'a + Clone, const N: usize> = From<[&'a T; N]>; + +//@ has it/traitalias.Alias2.html +//@ has - '//pre[@class="rust item-decl"]' \ +// 'trait Alias2 = where T: From, String: Into;' +pub trait Alias2 = where T: From, String: Into; + +//@ has it/traitalias.Alias3.html +//@ has - '//pre[@class="rust item-decl"]' 'trait Alias3 = ;' +pub trait Alias3 =; + +//@ has it/traitalias.Alias4.html +//@ has - '//pre[@class="rust item-decl"]' 'trait Alias4 = ;' +pub trait Alias4 = where; + +//@ has it/fn.usage0.html +//@ has - '//pre[@class="rust item-decl"]' "pub fn usage0(_: impl Alias0)" +//@ has - '//a[@href="traitalias.Alias0.html"]' 'Alias0' +pub fn usage0(_: impl Alias0) {} + +// FIXME: One can only "disambiguate" intra-doc links to trait aliases with `type@` but not with +// `trait@` (fails to resolve) or `traitalias@` (doesn't exist). We should make at least one of +// the latter two work, right? + +//@ has it/link0/index.html +//@ has - '//a/@href' 'traitalias.Alias0.html' +//@ has - '//a/@href' 'traitalias.Alias1.html' +/// [Alias0], [type@Alias1] +pub mod link0 {} + +// Check the "extern case" (middle cleaning) // + +//@ aux-build: ext-trait-aliases.rs +extern crate ext_trait_aliases as ext; + +//@ has it/traitalias.ExtAlias0.html +//@ has - '//pre[@class="rust item-decl"]' 'trait ExtAlias0 = Copy + Iterator;' +pub use ext::ExtAlias0; + +//@ has it/traitalias.ExtAlias1.html +//@ has - '//pre[@class="rust item-decl"]' \ +// "trait ExtAlias1<'a, T, const N: usize> = From<[&'a T; N]> where T: 'a + Clone;" +pub use ext::ExtAlias1; + +//@ has it/traitalias.ExtAlias2.html +//@ has - '//pre[@class="rust item-decl"]' \ +// 'trait ExtAlias2 = where T: From, String: Into;' +pub use ext::ExtAlias2; + +//@ has it/traitalias.ExtAlias3.html +//@ has - '//pre[@class="rust item-decl"]' 'trait ExtAlias3 = Sized;' +pub use ext::ExtAlias3; + +// NOTE: Middle cleaning can't discern `= Sized` and `= where Self: Sized` and that's okay. +//@ has it/traitalias.ExtAlias4.html +//@ has - '//pre[@class="rust item-decl"]' 'trait ExtAlias4 = Sized;' +pub use ext::ExtAlias4; + +//@ has it/traitalias.ExtAlias5.html +//@ has - '//pre[@class="rust item-decl"]' 'trait ExtAlias5 = ;' +pub use ext::ExtAlias5; + +//@ has it/fn.usage1.html +//@ has - '//pre[@class="rust item-decl"]' "pub fn usage1(_: impl ExtAlias0)" +//@ has - '//a[@href="traitalias.ExtAlias0.html"]' 'ExtAlias0' +pub fn usage1(_: impl ExtAlias0) {} diff --git a/tests/rustdoc/trait_alias.rs b/tests/rustdoc/trait_alias.rs deleted file mode 100644 index bfdb9d40e2d2e..0000000000000 --- a/tests/rustdoc/trait_alias.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![feature(trait_alias)] - -#![crate_name = "foo"] - -use std::fmt::Debug; - -//@ has foo/all.html '//a[@href="traitalias.CopyAlias.html"]' 'CopyAlias' -//@ has foo/all.html '//a[@href="traitalias.Alias2.html"]' 'Alias2' -//@ has foo/all.html '//a[@href="traitalias.Foo.html"]' 'Foo' - -//@ has foo/index.html '//h2[@id="trait-aliases"]' 'Trait Aliases' -//@ has foo/index.html '//a[@class="traitalias"]' 'CopyAlias' -//@ has foo/index.html '//a[@class="traitalias"]' 'Alias2' -//@ has foo/index.html '//a[@class="traitalias"]' 'Foo' - -//@ has foo/traitalias.CopyAlias.html -//@ has - '//section[@id="main-content"]/pre[@class="rust item-decl"]' 'trait CopyAlias = Copy;' -pub trait CopyAlias = Copy; -//@ has foo/traitalias.Alias2.html -//@ has - '//section[@id="main-content"]/pre[@class="rust item-decl"]' 'trait Alias2 = Copy + Debug;' -pub trait Alias2 = Copy + Debug; -//@ has foo/traitalias.Foo.html -//@ has - '//section[@id="main-content"]/pre[@class="rust item-decl"]' 'trait Foo = Into + Debug;' -pub trait Foo = Into + Debug; -//@ has foo/fn.bar.html '//a[@href="traitalias.Alias2.html"]' 'Alias2' -pub fn bar() where T: Alias2 {} diff --git a/tests/ui-fulldeps/auxiliary/parser.rs b/tests/ui-fulldeps/auxiliary/parser.rs index 4ea0d814b1faa..be51bd29008aa 100644 --- a/tests/ui-fulldeps/auxiliary/parser.rs +++ b/tests/ui-fulldeps/auxiliary/parser.rs @@ -39,8 +39,6 @@ pub fn parse_expr(psess: &ParseSess, source_code: &str) -> Option> { struct Normalize; impl MutVisitor for Normalize { - const VISIT_TOKENS: bool = true; - fn visit_id(&mut self, id: &mut NodeId) { *id = DUMMY_NODE_ID; } diff --git a/tests/ui-fulldeps/codegen-backend/hotplug.bindep.stdout b/tests/ui-fulldeps/codegen-backend/hotplug.bindep.stdout index 4d58fd503d0a6..5cf39ca3165d7 100644 --- a/tests/ui-fulldeps/codegen-backend/hotplug.bindep.stdout +++ b/tests/ui-fulldeps/codegen-backend/hotplug.bindep.stdout @@ -1,4 +1,4 @@ -$TEST_BUILD_DIR/codegen-backend/hotplug.bindep/libhotplug.rlib: $DIR/hotplug.rs $TEST_BUILD_DIR/codegen-backend/hotplug.bindep/auxiliary/libthe_backend.so +$TEST_BUILD_DIR/libhotplug.rlib: $DIR/hotplug.rs $TEST_BUILD_DIR/auxiliary/libthe_backend.so $DIR/hotplug.rs: -$TEST_BUILD_DIR/codegen-backend/hotplug.bindep/auxiliary/libthe_backend.so: +$TEST_BUILD_DIR/auxiliary/libthe_backend.so: diff --git a/tests/ui-fulldeps/codegen-backend/hotplug.dep.stdout b/tests/ui-fulldeps/codegen-backend/hotplug.dep.stdout index 48b7534d8faf7..717439f14e1d6 100644 --- a/tests/ui-fulldeps/codegen-backend/hotplug.dep.stdout +++ b/tests/ui-fulldeps/codegen-backend/hotplug.dep.stdout @@ -1,3 +1,3 @@ -$TEST_BUILD_DIR/codegen-backend/hotplug.dep/libhotplug.rlib: $DIR/hotplug.rs +$TEST_BUILD_DIR/libhotplug.rlib: $DIR/hotplug.rs $DIR/hotplug.rs: diff --git a/tests/ui-fulldeps/hash-stable-is-unstable.rs b/tests/ui-fulldeps/hash-stable-is-unstable.rs index dc77869528730..7f62b60441040 100644 --- a/tests/ui-fulldeps/hash-stable-is-unstable.rs +++ b/tests/ui-fulldeps/hash-stable-is-unstable.rs @@ -1,25 +1,24 @@ -//@ ignore-stage1 FIXME: this line can be removed once these new error messages are in stage 0 rustc //@ compile-flags: -Zdeduplicate-diagnostics=yes extern crate rustc_data_structures; -//~^ use of unstable library feature `rustc_private` +//~^ ERROR use of unstable library feature `rustc_private` //~| NOTE: issue #27812 for more information //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date extern crate rustc_macros; -//~^ use of unstable library feature `rustc_private` +//~^ ERROR use of unstable library feature `rustc_private` //~| NOTE: see issue #27812 for more information //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date extern crate rustc_query_system; -//~^ use of unstable library feature `rustc_private` +//~^ ERROR use of unstable library feature `rustc_private` //~| NOTE: see issue #27812 for more information //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date use rustc_macros::HashStable; -//~^ use of unstable library feature `rustc_private` +//~^ ERROR use of unstable library feature `rustc_private` //~| NOTE: see issue #27812 for more information //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date #[derive(HashStable)] -//~^ use of unstable library feature `rustc_private` +//~^ ERROR use of unstable library feature `rustc_private` //~| NOTE: in this expansion of #[derive(HashStable)] //~| NOTE: in this expansion of #[derive(HashStable)] //~| NOTE: in this expansion of #[derive(HashStable)] diff --git a/tests/ui-fulldeps/hash-stable-is-unstable.stderr b/tests/ui-fulldeps/hash-stable-is-unstable.stderr index 8d80917587531..e7740d744b4f8 100644 --- a/tests/ui-fulldeps/hash-stable-is-unstable.stderr +++ b/tests/ui-fulldeps/hash-stable-is-unstable.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `rustc_private`: this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? - --> $DIR/hash-stable-is-unstable.rs:3:1 + --> $DIR/hash-stable-is-unstable.rs:2:1 | LL | extern crate rustc_data_structures; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | extern crate rustc_data_structures; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `rustc_private`: this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? - --> $DIR/hash-stable-is-unstable.rs:7:1 + --> $DIR/hash-stable-is-unstable.rs:6:1 | LL | extern crate rustc_macros; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | extern crate rustc_macros; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `rustc_private`: this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? - --> $DIR/hash-stable-is-unstable.rs:11:1 + --> $DIR/hash-stable-is-unstable.rs:10:1 | LL | extern crate rustc_query_system; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | extern crate rustc_query_system; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `rustc_private`: this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? - --> $DIR/hash-stable-is-unstable.rs:16:5 + --> $DIR/hash-stable-is-unstable.rs:15:5 | LL | use rustc_macros::HashStable; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | use rustc_macros::HashStable; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `rustc_private`: this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? - --> $DIR/hash-stable-is-unstable.rs:21:10 + --> $DIR/hash-stable-is-unstable.rs:20:10 | LL | #[derive(HashStable)] | ^^^^^^^^^^ diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.rs b/tests/ui-fulldeps/internal-lints/diagnostics.rs index 442f9d72c3f19..1238fefd5bc03 100644 --- a/tests/ui-fulldeps/internal-lints/diagnostics.rs +++ b/tests/ui-fulldeps/internal-lints/diagnostics.rs @@ -15,7 +15,7 @@ extern crate rustc_span; use rustc_errors::{ Diag, DiagCtxtHandle, DiagInner, DiagMessage, Diagnostic, EmissionGuarantee, Level, - LintDiagnostic, SubdiagMessage, SubdiagMessageOp, Subdiagnostic, + LintDiagnostic, SubdiagMessage, Subdiagnostic, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::Span; @@ -56,10 +56,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for TranslatableInDiagnostic { pub struct UntranslatableInAddtoDiag; impl Subdiagnostic for UntranslatableInAddtoDiag { - fn add_to_diag_with>( + fn add_to_diag( self, diag: &mut Diag<'_, G>, - f: &F, ) { diag.note("untranslatable diagnostic"); //~^ ERROR diagnostics should be created using translatable messages @@ -69,10 +68,9 @@ impl Subdiagnostic for UntranslatableInAddtoDiag { pub struct TranslatableInAddtoDiag; impl Subdiagnostic for TranslatableInAddtoDiag { - fn add_to_diag_with>( + fn add_to_diag( self, diag: &mut Diag<'_, G>, - f: &F, ) { diag.note(crate::fluent_generated::no_crate_note); } diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.stderr b/tests/ui-fulldeps/internal-lints/diagnostics.stderr index 36dd3cf4be798..b260c4b7afefb 100644 --- a/tests/ui-fulldeps/internal-lints/diagnostics.stderr +++ b/tests/ui-fulldeps/internal-lints/diagnostics.stderr @@ -11,19 +11,19 @@ LL | #![deny(rustc::untranslatable_diagnostic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:64:19 + --> $DIR/diagnostics.rs:63:19 | LL | diag.note("untranslatable diagnostic"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:85:19 + --> $DIR/diagnostics.rs:83:19 | LL | diag.note("untranslatable diagnostic"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls - --> $DIR/diagnostics.rs:99:21 + --> $DIR/diagnostics.rs:97:21 | LL | let _diag = dcx.struct_err(crate::fluent_generated::no_crate_example); | ^^^^^^^^^^ @@ -35,37 +35,37 @@ LL | #![deny(rustc::diagnostic_outside_of_impl)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls - --> $DIR/diagnostics.rs:102:21 + --> $DIR/diagnostics.rs:100:21 | LL | let _diag = dcx.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:102:32 + --> $DIR/diagnostics.rs:100:32 | LL | let _diag = dcx.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:120:7 + --> $DIR/diagnostics.rs:118:7 | LL | f("untranslatable diagnostic", crate::fluent_generated::no_crate_example); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:122:50 + --> $DIR/diagnostics.rs:120:50 | LL | f(crate::fluent_generated::no_crate_example, "untranslatable diagnostic"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:124:7 + --> $DIR/diagnostics.rs:122:7 | LL | f("untranslatable diagnostic", "untranslatable diagnostic"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:124:36 + --> $DIR/diagnostics.rs:122:36 | LL | f("untranslatable diagnostic", "untranslatable diagnostic"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.rs b/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.rs new file mode 100644 index 0000000000000..3fdd65d6c87f1 --- /dev/null +++ b/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -Z unstable-options +//@ ignore-stage1 + +#![feature(rustc_private)] +#![deny(rustc::usage_of_type_ir_traits)] + +extern crate rustc_type_ir; + +use rustc_type_ir::Interner; + +fn foo(cx: I, did: I::DefId) { + let _ = cx.trait_is_unsafe(did); + //~^ ERROR do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver +} + +fn main() {} diff --git a/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.stderr b/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.stderr new file mode 100644 index 0000000000000..df29a4945582b --- /dev/null +++ b/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.stderr @@ -0,0 +1,15 @@ +error: do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver + --> $DIR/import-of-type-ir-traits.rs:12:13 + | +LL | let _ = cx.trait_is_unsafe(did); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the method or struct you're looking for is likely defined somewhere else downstream in the compiler +note: the lint level is defined here + --> $DIR/import-of-type-ir-traits.rs:5:9 + | +LL | #![deny(rustc::usage_of_type_ir_traits)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-fulldeps/missing-rustc-driver-error.rs b/tests/ui-fulldeps/missing-rustc-driver-error.rs index 03ab5ce7b2539..c6dff2d99dde0 100644 --- a/tests/ui-fulldeps/missing-rustc-driver-error.rs +++ b/tests/ui-fulldeps/missing-rustc-driver-error.rs @@ -1,11 +1,15 @@ // Test that we get the following hint when trying to use a compiler crate without rustc_driver. -//@ error-pattern: try adding `extern crate rustc_driver;` at the top level of this crate //@ compile-flags: --emit link //@ normalize-stderr: ".*crate .* required.*\n\n" -> "" //@ normalize-stderr: "aborting due to [0-9]+" -> "aborting due to NUMBER" +//@ dont-require-annotations: ERROR #![feature(rustc_private)] extern crate rustc_serialize; fn main() {} + +//~? HELP try adding `extern crate rustc_driver;` at the top level of this crate +//~? HELP try adding `extern crate rustc_driver;` at the top level of this crate +//~? HELP try adding `extern crate rustc_driver;` at the top level of this crate diff --git a/tests/ui-fulldeps/pathless-extern-unstable.rs b/tests/ui-fulldeps/pathless-extern-unstable.rs index 27272135696aa..b5ba4082e9df3 100644 --- a/tests/ui-fulldeps/pathless-extern-unstable.rs +++ b/tests/ui-fulldeps/pathless-extern-unstable.rs @@ -1,4 +1,3 @@ -//@ ignore-stage1 FIXME: this line can be removed once these new error messages are in stage 0 rustc //@ edition:2018 //@ compile-flags:--extern rustc_middle diff --git a/tests/ui-fulldeps/pathless-extern-unstable.stderr b/tests/ui-fulldeps/pathless-extern-unstable.stderr index a78b69f4d20f5..779c3773a9b90 100644 --- a/tests/ui-fulldeps/pathless-extern-unstable.stderr +++ b/tests/ui-fulldeps/pathless-extern-unstable.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `rustc_private`: this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? - --> $DIR/pathless-extern-unstable.rs:7:9 + --> $DIR/pathless-extern-unstable.rs:6:9 | LL | pub use rustc_middle; | ^^^^^^^^^^^^ diff --git a/tests/ui-fulldeps/pprust-expr-roundtrip.rs b/tests/ui-fulldeps/pprust-expr-roundtrip.rs index 37e328a315f1e..4a866560e798a 100644 --- a/tests/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/tests/ui-fulldeps/pprust-expr-roundtrip.rs @@ -114,7 +114,6 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { rules: BlockCheckMode::Default, span: DUMMY_SP, tokens: None, - could_be_bare_literal: false, }); iter_exprs(depth - 1, &mut |e| g(ExprKind::If(e, block.clone(), None))); } diff --git a/tests/ui-fulldeps/run-compiler-twice.rs b/tests/ui-fulldeps/run-compiler-twice.rs index f414c961627dd..fa651baa7bc8f 100644 --- a/tests/ui-fulldeps/run-compiler-twice.rs +++ b/tests/ui-fulldeps/run-compiler-twice.rs @@ -46,7 +46,7 @@ fn main() { fn compile(code: String, output: PathBuf, sysroot: PathBuf, linker: Option<&Path>) { let mut opts = Options::default(); opts.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); - opts.maybe_sysroot = Some(sysroot); + opts.sysroot = sysroot; if let Some(linker) = linker { opts.cg.linker = Some(linker.to_owned()); @@ -70,6 +70,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf, linker: Option<&Path hash_untracked_state: None, register_lints: None, override_queries: None, + extra_symbols: Vec::new(), make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), using_internal_features: &rustc_driver::USING_INTERNAL_FEATURES, diff --git a/tests/ui-fulldeps/stable-mir/check_abi.rs b/tests/ui-fulldeps/stable-mir/check_abi.rs index ef2d5b4854bef..71edf813a7be2 100644 --- a/tests/ui-fulldeps/stable-mir/check_abi.rs +++ b/tests/ui-fulldeps/stable-mir/check_abi.rs @@ -17,7 +17,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; use stable_mir::abi::{ ArgAbi, CallConvention, FieldsShape, IntegerLength, PassMode, Primitive, Scalar, ValueAbi, VariantsShape, @@ -146,7 +145,7 @@ fn get_item<'a>( fn main() { let path = "alloc_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_allocation.rs b/tests/ui-fulldeps/stable-mir/check_allocation.rs index c102f86a2286b..692c24f054451 100644 --- a/tests/ui-fulldeps/stable-mir/check_allocation.rs +++ b/tests/ui-fulldeps/stable-mir/check_allocation.rs @@ -19,7 +19,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; use stable_mir::crate_def::CrateDef; use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::{Instance, InstanceKind, StaticDef}; @@ -220,7 +219,7 @@ fn get_item<'a>( fn main() { let path = "alloc_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--edition=2021".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_assoc_items.rs b/tests/ui-fulldeps/stable-mir/check_assoc_items.rs new file mode 100644 index 0000000000000..bb95bedf97337 --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_assoc_items.rs @@ -0,0 +1,144 @@ +//@ run-pass +//! Test that users are able to retrieve all associated items from a definition. +//! definition. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ edition: 2021 + +#![feature(rustc_private)] +#![feature(assert_matches)] + +extern crate rustc_middle; +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use std::collections::HashSet; +use std::io::Write; +use std::ops::ControlFlow; + +use stable_mir::ty::*; +use stable_mir::{CrateDef, *}; + +const CRATE_NAME: &str = "crate_assoc_items"; + +/// This function uses the Stable MIR APIs to get information about the test crate. +fn test_assoc_items() -> ControlFlow<()> { + let local_crate = stable_mir::local_crate(); + check_items( + &local_crate.fn_defs(), + &[ + "AStruct::new", + "::assoc_fn_no_self", + "::assoc_fn_has_self", + "ATrait::rpitit", + "ATrait::assoc_fn_has_self", + "ATrait::assoc_fn_no_self", + "::rpitit", + ], + ); + + let local_impls = local_crate.trait_impls(); + let local_traits = local_crate.trait_decls(); + + let trait_assoc_item_defs: Vec = + local_traits[0].associated_items().iter().map(|assoc_item| assoc_item.def_id).collect(); + check_items( + &trait_assoc_item_defs, + &[ + "ATrait::rpitit::{anon_assoc#0}", + "ATrait::rpitit", + "ATrait::Assoc", + "ATrait::assoc_fn_no_self", + "ATrait::assoc_fn_has_self", + ], + ); + + let impl_assoc_item_defs: Vec = + local_impls[0].associated_items().iter().map(|assoc_item| assoc_item.def_id).collect(); + check_items( + &impl_assoc_item_defs, + &[ + "::rpitit::{anon_assoc#0}", + "::rpitit", + "::Assoc", + "::assoc_fn_no_self", + "::assoc_fn_has_self", + ], + ); + + ControlFlow::Continue(()) +} + +/// Check if the list of definitions matches the expected list. +/// Note that order doesn't matter. +fn check_items(items: &[T], expected: &[&str]) { + let expected: HashSet<_> = expected.iter().map(|s| s.to_string()).collect(); + let item_names: HashSet<_> = items.iter().map(|item| item.name()).collect(); + assert_eq!(item_names, expected); +} + +fn main() { + let path = "assoc_items.rs"; + generate_input(&path).unwrap(); + let args = &[ + "rustc".to_string(), + "--crate-type=lib".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, test_assoc_items).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + #![allow(dead_code, unused_variables)] + struct AStruct; + + impl AStruct {{ + const ASSOC_CONST: &str = "Nina"; + + fn new() -> Self {{ + AStruct{{}} + }} + }} + + trait ATrait {{ + type Assoc; + + fn assoc_fn_no_self() {{ + }} + + fn assoc_fn_has_self(&self) {{ + }} + + fn rpitit(&self) -> impl std::fmt::Debug {{ + "ciallo" + }} + }} + + impl ATrait for AStruct {{ + type Assoc = u32; + + fn assoc_fn_no_self() {{ + }} + + fn assoc_fn_has_self(&self) {{ + }} + + fn rpitit(&self) -> impl std::fmt::Debug {{ + "ciallo~" + }} + }} + "# + )?; + Ok(()) +} diff --git a/tests/ui-fulldeps/stable-mir/check_attribute.rs b/tests/ui-fulldeps/stable-mir/check_attribute.rs index de5ba15f6ea4e..e4cc7b104b60e 100644 --- a/tests/ui-fulldeps/stable-mir/check_attribute.rs +++ b/tests/ui-fulldeps/stable-mir/check_attribute.rs @@ -15,7 +15,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; use stable_mir::{CrateDef, CrateItems}; use std::io::Write; use std::ops::ControlFlow; @@ -36,12 +35,12 @@ fn test_stable_mir() -> ControlFlow<()> { fn test_tool(items: &CrateItems) { let rustfmt_fn = *get_item(&items, "do_not_format").unwrap(); let rustfmt_attrs = rustfmt_fn.tool_attrs(&["rustfmt".to_string(), "skip".to_string()]); - assert_eq!(rustfmt_attrs[0].as_str(), "#[rustfmt::skip]"); + assert_eq!(rustfmt_attrs[0].as_str(), "#[rustfmt::skip]\n"); let clippy_fn = *get_item(&items, "complex_fn").unwrap(); let clippy_attrs = clippy_fn.tool_attrs(&["clippy".to_string(), "cyclomatic_complexity".to_string()]); - assert_eq!(clippy_attrs[0].as_str(), "#[clippy::cyclomatic_complexity = \"100\"]"); + assert_eq!(clippy_attrs[0].as_str(), "#[clippy::cyclomatic_complexity = \"100\"]\n"); } fn get_item<'a>( @@ -58,7 +57,7 @@ fn get_item<'a>( fn main() { let path = "attribute_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_binop.rs b/tests/ui-fulldeps/stable-mir/check_binop.rs index 65b3ffd27ab05..f9559d9958d28 100644 --- a/tests/ui-fulldeps/stable-mir/check_binop.rs +++ b/tests/ui-fulldeps/stable-mir/check_binop.rs @@ -15,7 +15,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; use stable_mir::mir::visit::{Location, MirVisitor}; use stable_mir::mir::{LocalDecl, Rvalue, Statement, StatementKind, Terminator, TerminatorKind}; @@ -82,7 +81,7 @@ impl<'a> MirVisitor for Visitor<'a> { fn main() { let path = "binop_input.rs"; generate_input(&path).unwrap(); - let args = vec!["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; + let args = &["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; run!(args, test_binops).unwrap(); } diff --git a/tests/ui-fulldeps/stable-mir/check_crate_defs.rs b/tests/ui-fulldeps/stable-mir/check_crate_defs.rs index 71cca94c34f62..6863242f22571 100644 --- a/tests/ui-fulldeps/stable-mir/check_crate_defs.rs +++ b/tests/ui-fulldeps/stable-mir/check_crate_defs.rs @@ -16,7 +16,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; use stable_mir::CrateDef; use std::collections::HashSet; use std::io::Write; @@ -85,7 +84,7 @@ fn contains(items: &[T], expected: &[&str]) { fn main() { let path = "crate_definitions.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_def_ty.rs b/tests/ui-fulldeps/stable-mir/check_def_ty.rs index 37b9a83e33e76..f86a8e0ae6189 100644 --- a/tests/ui-fulldeps/stable-mir/check_def_ty.rs +++ b/tests/ui-fulldeps/stable-mir/check_def_ty.rs @@ -17,7 +17,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; use stable_mir::ty::{Ty, ForeignItemKind}; use stable_mir::*; use std::io::Write; @@ -77,7 +76,7 @@ fn check_fn_def(ty: Ty) { fn main() { let path = "defs_ty_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_defs.rs b/tests/ui-fulldeps/stable-mir/check_defs.rs index cd3d76d876012..ab741378bb713 100644 --- a/tests/ui-fulldeps/stable-mir/check_defs.rs +++ b/tests/ui-fulldeps/stable-mir/check_defs.rs @@ -19,7 +19,6 @@ extern crate stable_mir; use std::assert_matches::assert_matches; use mir::{mono::Instance, TerminatorKind::*}; use stable_mir::mir::mono::InstanceKind; -use rustc_smir::rustc_internal; use stable_mir::ty::{RigidTy, TyKind, Ty, UintTy}; use stable_mir::*; use std::io::Write; @@ -113,7 +112,7 @@ fn get_instances(body: mir::Body) -> Vec { fn main() { let path = "defs_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_foreign.rs b/tests/ui-fulldeps/stable-mir/check_foreign.rs index bc3956b309088..398024c4ff082 100644 --- a/tests/ui-fulldeps/stable-mir/check_foreign.rs +++ b/tests/ui-fulldeps/stable-mir/check_foreign.rs @@ -17,7 +17,6 @@ extern crate rustc_interface; extern crate rustc_span; extern crate stable_mir; -use rustc_smir::rustc_internal; use stable_mir::{ ty::{Abi, ForeignItemKind}, *, @@ -59,7 +58,7 @@ fn test_foreign() -> ControlFlow<()> { fn main() { let path = "foreign_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-type=lib".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_instance.rs b/tests/ui-fulldeps/stable-mir/check_instance.rs index 72a138f907e72..b19e5b033c469 100644 --- a/tests/ui-fulldeps/stable-mir/check_instance.rs +++ b/tests/ui-fulldeps/stable-mir/check_instance.rs @@ -21,7 +21,6 @@ use std::ops::ControlFlow; use mir::mono::Instance; use mir::TerminatorKind::*; -use rustc_smir::rustc_internal; use stable_mir::ty::{RigidTy, TyKind}; use stable_mir::*; @@ -88,7 +87,7 @@ fn test_body(body: mir::Body) { fn main() { let path = "instance_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-type=lib".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_intrinsics.rs b/tests/ui-fulldeps/stable-mir/check_intrinsics.rs index 07a2a62e06687..52424857dc196 100644 --- a/tests/ui-fulldeps/stable-mir/check_intrinsics.rs +++ b/tests/ui-fulldeps/stable-mir/check_intrinsics.rs @@ -20,7 +20,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; use stable_mir::mir::mono::{Instance, InstanceKind}; use stable_mir::mir::visit::{Location, MirVisitor}; use stable_mir::mir::{LocalDecl, Terminator, TerminatorKind}; @@ -116,7 +115,7 @@ impl<'a> MirVisitor for CallsVisitor<'a> { fn main() { let path = "binop_input.rs"; generate_input(&path).unwrap(); - let args = vec!["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; + let args = &["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; run!(args, test_intrinsics).unwrap(); } diff --git a/tests/ui-fulldeps/stable-mir/check_item_kind.rs b/tests/ui-fulldeps/stable-mir/check_item_kind.rs index 647ce534589e1..d1124c75a8997 100644 --- a/tests/ui-fulldeps/stable-mir/check_item_kind.rs +++ b/tests/ui-fulldeps/stable-mir/check_item_kind.rs @@ -16,7 +16,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; use stable_mir::*; use std::io::Write; use std::ops::ControlFlow; @@ -48,7 +47,7 @@ fn test_item_kind() -> ControlFlow<()> { fn main() { let path = "item_kind_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-type=lib".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_normalization.rs b/tests/ui-fulldeps/stable-mir/check_normalization.rs index de14202adb9c0..16e8c0339ed47 100644 --- a/tests/ui-fulldeps/stable-mir/check_normalization.rs +++ b/tests/ui-fulldeps/stable-mir/check_normalization.rs @@ -17,7 +17,6 @@ extern crate stable_mir; use mir::mono::Instance; use ty::{Ty, TyKind, RigidTy}; -use rustc_smir::rustc_internal; use stable_mir::*; use std::io::Write; use std::ops::ControlFlow; @@ -62,7 +61,7 @@ fn check_ty(ty: Ty) { fn main() { let path = "normalization_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-type=lib".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_trait_queries.rs b/tests/ui-fulldeps/stable-mir/check_trait_queries.rs index 23c2844d3f166..fcf04a1fc3a3f 100644 --- a/tests/ui-fulldeps/stable-mir/check_trait_queries.rs +++ b/tests/ui-fulldeps/stable-mir/check_trait_queries.rs @@ -16,7 +16,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; use stable_mir::CrateDef; use std::collections::HashSet; use std::io::Write; @@ -73,7 +72,7 @@ fn assert_impl(impl_names: &HashSet, target: &str) { fn main() { let path = "trait_queries.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_transform.rs b/tests/ui-fulldeps/stable-mir/check_transform.rs index d9fc924933fa8..9087c1cf45027 100644 --- a/tests/ui-fulldeps/stable-mir/check_transform.rs +++ b/tests/ui-fulldeps/stable-mir/check_transform.rs @@ -17,7 +17,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::Instance; use stable_mir::mir::{Body, ConstOperand, Operand, Rvalue, StatementKind, TerminatorKind}; @@ -121,7 +120,7 @@ fn get_item<'a>( fn main() { let path = "transform_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/check_ty_fold.rs b/tests/ui-fulldeps/stable-mir/check_ty_fold.rs index 9fa4929d68e27..18b9e32e4e809 100644 --- a/tests/ui-fulldeps/stable-mir/check_ty_fold.rs +++ b/tests/ui-fulldeps/stable-mir/check_ty_fold.rs @@ -17,10 +17,11 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; -use stable_mir::ty::{RigidTy, TyKind, Ty, }; -use stable_mir::mir::{Body, MirVisitor, FieldIdx, Place, ProjectionElem, visit::{Location, - PlaceContext}}; +use stable_mir::mir::{ + Body, FieldIdx, MirVisitor, Place, ProjectionElem, + visit::{Location, PlaceContext}, +}; +use stable_mir::ty::{RigidTy, Ty, TyKind}; use std::io::Write; use std::ops::ControlFlow; @@ -29,8 +30,8 @@ const CRATE_NAME: &str = "input"; /// This function uses the Stable MIR APIs to get information about the test crate. fn test_stable_mir() -> ControlFlow<()> { let main_fn = stable_mir::entry_fn(); - let body = main_fn.unwrap().body(); - let mut visitor = PlaceVisitor{ body: &body, tested: false}; + let body = main_fn.unwrap().expect_body(); + let mut visitor = PlaceVisitor { body: &body, tested: false }; visitor.visit_body(&body); assert!(visitor.tested); ControlFlow::Continue(()) @@ -77,7 +78,7 @@ impl<'a> MirVisitor for PlaceVisitor<'a> { fn main() { let path = "ty_fold_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/compilation-result.rs b/tests/ui-fulldeps/stable-mir/compilation-result.rs index b8a9e720e5465..19b9c8b7de508 100644 --- a/tests/ui-fulldeps/stable-mir/compilation-result.rs +++ b/tests/ui-fulldeps/stable-mir/compilation-result.rs @@ -16,7 +16,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; use std::io::Write; /// This test will generate and analyze a dummy crate using the stable mir. @@ -26,40 +25,42 @@ use std::io::Write; fn main() { let path = "input_compilation_result_test.rs"; generate_input(&path).unwrap(); - let args = vec!["rustc".to_string(), path.to_string()]; - test_continue(args.clone()); - test_break(args.clone()); - test_failed(args.clone()); - test_skipped(args.clone()); + let args = &["rustc".to_string(), path.to_string()]; + test_continue(args); + test_break(args); + test_failed(args); + test_skipped(args); test_captured(args) } -fn test_continue(args: Vec) { +fn test_continue(args: &[String]) { let result = run!(args, || ControlFlow::Continue::<(), bool>(true)); assert_eq!(result, Ok(true)); } -fn test_break(args: Vec) { +fn test_break(args: &[String]) { let result = run!(args, || ControlFlow::Break::(false)); assert_eq!(result, Err(stable_mir::CompilerError::Interrupted(false))); } #[allow(unreachable_code)] -fn test_skipped(mut args: Vec) { +fn test_skipped(args: &[String]) { + let mut args = args.to_vec(); args.push("--version".to_string()); - let result = run!(args, || unreachable!() as ControlFlow<()>); + let result = run!(&args, || unreachable!() as ControlFlow<()>); assert_eq!(result, Err(stable_mir::CompilerError::Skipped)); } #[allow(unreachable_code)] -fn test_failed(mut args: Vec) { +fn test_failed(args: &[String]) { + let mut args = args.to_vec(); args.push("--cfg=broken".to_string()); - let result = run!(args, || unreachable!() as ControlFlow<()>); + let result = run!(&args, || unreachable!() as ControlFlow<()>); assert_eq!(result, Err(stable_mir::CompilerError::Failed)); } /// Test that we are able to pass a closure and set the return according to the captured value. -fn test_captured(args: Vec) { +fn test_captured(args: &[String]) { let captured = "10".to_string(); let result = run!(args, || ControlFlow::Continue::<(), usize>(captured.len())); assert_eq!(result, Ok(captured.len())); diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs index 7f0e9b50335d3..7fc4edafb9338 100644 --- a/tests/ui-fulldeps/stable-mir/crate-info.rs +++ b/tests/ui-fulldeps/stable-mir/crate-info.rs @@ -18,7 +18,6 @@ extern crate rustc_interface; extern crate stable_mir; use rustc_hir::def::DefKind; -use rustc_smir::rustc_internal; use stable_mir::ItemKind; use stable_mir::crate_def::CrateDef; use stable_mir::mir::mono::Instance; @@ -45,7 +44,7 @@ fn test_stable_mir() -> ControlFlow<()> { assert!(stable_mir::find_crates("std").len() == 1); let bar = get_item(&items, (DefKind::Fn, "bar")).unwrap(); - let body = bar.body(); + let body = bar.expect_body(); assert_eq!(body.locals().len(), 2); assert_eq!(body.blocks.len(), 1); let block = &body.blocks[0]; @@ -60,7 +59,7 @@ fn test_stable_mir() -> ControlFlow<()> { } let foo_bar = get_item(&items, (DefKind::Fn, "foo_bar")).unwrap(); - let body = foo_bar.body(); + let body = foo_bar.expect_body(); assert_eq!(body.locals().len(), 5); assert_eq!(body.blocks.len(), 4); let block = &body.blocks[0]; @@ -70,7 +69,7 @@ fn test_stable_mir() -> ControlFlow<()> { } let types = get_item(&items, (DefKind::Fn, "types")).unwrap(); - let body = types.body(); + let body = types.expect_body(); assert_eq!(body.locals().len(), 6); assert_matches!( body.locals()[0].ty.kind(), @@ -100,7 +99,7 @@ fn test_stable_mir() -> ControlFlow<()> { ); let drop = get_item(&items, (DefKind::Fn, "drop")).unwrap(); - let body = drop.body(); + let body = drop.expect_body(); assert_eq!(body.blocks.len(), 2); let block = &body.blocks[0]; match &block.terminator.kind { @@ -109,7 +108,7 @@ fn test_stable_mir() -> ControlFlow<()> { } let assert = get_item(&items, (DefKind::Fn, "assert")).unwrap(); - let body = assert.body(); + let body = assert.expect_body(); assert_eq!(body.blocks.len(), 2); let block = &body.blocks[0]; match &block.terminator.kind { @@ -123,7 +122,8 @@ fn test_stable_mir() -> ControlFlow<()> { match &block.terminator.kind { stable_mir::mir::TerminatorKind::Call { func, .. } => { let TyKind::RigidTy(ty) = func.ty(&body.locals()).unwrap().kind() else { - unreachable!() }; + unreachable!() + }; let RigidTy::FnDef(def, args) = ty else { unreachable!() }; let next_func = Instance::resolve(def, &args).unwrap(); match next_func.body().unwrap().locals()[1].ty.kind() { @@ -138,10 +138,10 @@ fn test_stable_mir() -> ControlFlow<()> { let foo_const = get_item(&items, (DefKind::Const, "FOO")).unwrap(); // Ensure we don't panic trying to get the body of a constant. - foo_const.body(); + foo_const.expect_body(); let locals_fn = get_item(&items, (DefKind::Fn, "locals")).unwrap(); - let body = locals_fn.body(); + let body = locals_fn.expect_body(); assert_eq!(body.locals().len(), 4); assert_matches!( body.ret_local().ty.kind(), @@ -172,8 +172,10 @@ fn get_item<'a>( item: (DefKind, &str), ) -> Option<&'a stable_mir::CrateItem> { items.iter().find(|crate_item| { - matches!((item.0, crate_item.kind()), (DefKind::Fn, ItemKind::Fn) | (DefKind::Const, - ItemKind::Const)) && crate_item.name() == item.1 + matches!( + (item.0, crate_item.kind()), + (DefKind::Fn, ItemKind::Fn) | (DefKind::Const, ItemKind::Const) + ) && crate_item.name() == item.1 }) } @@ -184,7 +186,7 @@ fn get_item<'a>( fn main() { let path = "input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/projections.rs b/tests/ui-fulldeps/stable-mir/projections.rs index 3cc71247e6744..103c97bc48e17 100644 --- a/tests/ui-fulldeps/stable-mir/projections.rs +++ b/tests/ui-fulldeps/stable-mir/projections.rs @@ -17,11 +17,10 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use rustc_smir::rustc_internal; +use stable_mir::ItemKind; use stable_mir::crate_def::CrateDef; use stable_mir::mir::{ProjectionElem, Rvalue, StatementKind}; use stable_mir::ty::{RigidTy, TyKind, UintTy}; -use stable_mir::ItemKind; use std::assert_matches::assert_matches; use std::io::Write; use std::ops::ControlFlow; @@ -31,7 +30,7 @@ const CRATE_NAME: &str = "input"; /// Tests projections within Place objects fn test_place_projections() -> ControlFlow<()> { let items = stable_mir::all_local_items(); - let body = get_item(&items, (ItemKind::Fn, "projections")).unwrap().body(); + let body = get_item(&items, (ItemKind::Fn, "projections")).unwrap().expect_body(); assert_eq!(body.blocks.len(), 4); // The first statement assigns `&s.c` to a local. The projections include a deref for `s`, since // `s` is passed as a reference argument, and a field access for field `c`. @@ -53,7 +52,7 @@ fn test_place_projections() -> ControlFlow<()> { ); let ty = place.ty(body.locals()).unwrap(); assert_matches!(ty.kind().rigid(), Some(RigidTy::Ref(..))); - }, + } other => panic!( "Unable to match against expected rvalue projection. Expected the projection \ for `s.c`, which is a Deref and u8 Field. Got: {:?}", @@ -137,9 +136,7 @@ fn get_item<'a>( items: &'a stable_mir::CrateItems, item: (ItemKind, &str), ) -> Option<&'a stable_mir::CrateItem> { - items.iter().find(|crate_item| { - crate_item.kind() == item.0 && crate_item.name() == item.1 - }) + items.iter().find(|crate_item| crate_item.kind() == item.0 && crate_item.name() == item.1) } /// This test will generate and analyze a dummy crate using the stable mir. @@ -149,7 +146,7 @@ fn get_item<'a>( fn main() { let path = "input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-type=lib".to_string(), "--crate-name".to_string(), diff --git a/tests/ui-fulldeps/stable-mir/smir_internal.rs b/tests/ui-fulldeps/stable-mir/smir_internal.rs index 453e5372de4f8..0519b9de68050 100644 --- a/tests/ui-fulldeps/stable-mir/smir_internal.rs +++ b/tests/ui-fulldeps/stable-mir/smir_internal.rs @@ -26,7 +26,7 @@ const CRATE_NAME: &str = "input"; fn test_translation(tcx: TyCtxt<'_>) -> ControlFlow<()> { let main_fn = stable_mir::entry_fn().unwrap(); - let body = main_fn.body(); + let body = main_fn.expect_body(); let orig_ty = body.locals()[0].ty; let rustc_ty = rustc_internal::internal(tcx, &orig_ty); assert!(rustc_ty.is_unit()); @@ -40,7 +40,7 @@ fn test_translation(tcx: TyCtxt<'_>) -> ControlFlow<()> { fn main() { let path = "internal_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-name".to_string(), CRATE_NAME.to_string(), diff --git a/tests/ui-fulldeps/stable-mir/smir_serde.rs b/tests/ui-fulldeps/stable-mir/smir_serde.rs index 2c74276d550cf..0b39ec050024e 100644 --- a/tests/ui-fulldeps/stable-mir/smir_serde.rs +++ b/tests/ui-fulldeps/stable-mir/smir_serde.rs @@ -14,33 +14,28 @@ extern crate rustc_smir; extern crate rustc_driver; extern crate rustc_interface; extern crate rustc_middle; -extern crate stable_mir; extern crate serde; extern crate serde_json; +extern crate stable_mir; use rustc_middle::ty::TyCtxt; -use rustc_smir::rustc_internal; +use serde_json::to_string; use stable_mir::mir::Body; -use std::io::{Write, BufWriter}; +use std::io::{BufWriter, Write}; use std::ops::ControlFlow; -use serde_json::to_string; - const CRATE_NAME: &str = "input"; fn serialize_to_json(_tcx: TyCtxt<'_>) -> ControlFlow<()> { let path = "output.json"; - let mut writer = BufWriter::new(std::fs::File::create(path) - .expect("Failed to create path")); + let mut writer = BufWriter::new(std::fs::File::create(path).expect("Failed to create path")); let local_crate = stable_mir::local_crate(); - let items: Vec = stable_mir::all_local_items() - .iter() - .map(|item| { item.body() }) - .collect(); - let crate_data = ( local_crate.name, items ); - writer.write_all(to_string(&crate_data) - .expect("serde_json failed") - .as_bytes()).expect("JSON serialization failed"); + let items: Vec = + stable_mir::all_local_items().iter().map(|item| item.expect_body()).collect(); + let crate_data = (local_crate.name, items); + writer + .write_all(to_string(&crate_data).expect("serde_json failed").as_bytes()) + .expect("JSON serialization failed"); ControlFlow::Continue(()) } @@ -51,7 +46,7 @@ fn serialize_to_json(_tcx: TyCtxt<'_>) -> ControlFlow<()> { fn main() { let path = "internal_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "--crate-name".to_string(), CRATE_NAME.to_string(), diff --git a/tests/ui-fulldeps/stable-mir/smir_visitor.rs b/tests/ui-fulldeps/stable-mir/smir_visitor.rs index 0a6415d490e4b..caf71de2556c4 100644 --- a/tests/ui-fulldeps/stable-mir/smir_visitor.rs +++ b/tests/ui-fulldeps/stable-mir/smir_visitor.rs @@ -16,10 +16,10 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use std::collections::HashSet; -use rustc_smir::rustc_internal; -use stable_mir::*; use stable_mir::mir::MirVisitor; +use stable_mir::mir::MutMirVisitor; +use stable_mir::*; +use std::collections::HashSet; use std::io::Write; use std::ops::ControlFlow; @@ -27,7 +27,7 @@ const CRATE_NAME: &str = "input"; fn test_visitor() -> ControlFlow<()> { let main_fn = stable_mir::entry_fn(); - let main_body = main_fn.unwrap().body(); + let main_body = main_fn.unwrap().expect_body(); let main_visitor = TestVisitor::collect(&main_body); assert!(main_visitor.ret_val.is_some()); assert!(main_visitor.args.is_empty()); @@ -51,7 +51,7 @@ struct TestVisitor<'a> { pub tys: HashSet, pub ret_val: Option, pub args: Vec, - pub calls: Vec + pub calls: Vec, } impl<'a> TestVisitor<'a> { @@ -90,8 +90,85 @@ impl<'a> mir::MirVisitor for TestVisitor<'a> { fn visit_terminator(&mut self, term: &mir::Terminator, location: mir::visit::Location) { if let mir::TerminatorKind::Call { func, .. } = &term.kind { let ty::TyKind::RigidTy(ty) = func.ty(self.body.locals()).unwrap().kind() else { - unreachable! - () }; + unreachable!() + }; + let ty::RigidTy::FnDef(def, args) = ty else { unreachable!() }; + self.calls.push(mir::mono::Instance::resolve(def, &args).unwrap()); + } + self.super_terminator(term, location); + } +} + +fn test_mut_visitor() -> ControlFlow<()> { + let main_fn = stable_mir::entry_fn(); + let mut main_body = main_fn.unwrap().expect_body(); + let locals = main_body.locals().to_vec(); + let mut main_visitor = TestMutVisitor::collect(locals); + main_visitor.visit_body(&mut main_body); + assert!(main_visitor.ret_val.is_some()); + assert!(main_visitor.args.is_empty()); + assert!(main_visitor.tys.contains(&main_visitor.ret_val.unwrap().ty)); + assert!(!main_visitor.calls.is_empty()); + + let exit_fn = main_visitor.calls.last().unwrap(); + assert!(exit_fn.mangled_name().contains("exit_fn"), "Unexpected last function: {exit_fn:?}"); + + let mut exit_body = exit_fn.body().unwrap(); + let locals = exit_body.locals().to_vec(); + let mut exit_visitor = TestMutVisitor::collect(locals); + exit_visitor.visit_body(&mut exit_body); + assert!(exit_visitor.ret_val.is_some()); + assert_eq!(exit_visitor.args.len(), 1); + assert!(exit_visitor.tys.contains(&exit_visitor.ret_val.unwrap().ty)); + assert!(exit_visitor.tys.contains(&exit_visitor.args[0].ty)); + ControlFlow::Continue(()) +} + +struct TestMutVisitor { + locals: Vec, + pub tys: HashSet, + pub ret_val: Option, + pub args: Vec, + pub calls: Vec, +} + +impl TestMutVisitor { + fn collect(locals: Vec) -> TestMutVisitor { + let visitor = TestMutVisitor { + locals: locals, + tys: Default::default(), + ret_val: None, + args: vec![], + calls: vec![], + }; + visitor + } +} + +impl mir::MutMirVisitor for TestMutVisitor { + fn visit_ty(&mut self, ty: &mut ty::Ty, _location: mir::visit::Location) { + self.tys.insert(*ty); + self.super_ty(ty) + } + + fn visit_ret_decl(&mut self, local: mir::Local, decl: &mut mir::LocalDecl) { + assert!(local == mir::RETURN_LOCAL); + assert!(self.ret_val.is_none()); + self.ret_val = Some(decl.clone()); + self.super_ret_decl(local, decl); + } + + fn visit_arg_decl(&mut self, local: mir::Local, decl: &mut mir::LocalDecl) { + self.args.push(decl.clone()); + assert_eq!(local, self.args.len()); + self.super_arg_decl(local, decl); + } + + fn visit_terminator(&mut self, term: &mut mir::Terminator, location: mir::visit::Location) { + if let mir::TerminatorKind::Call { func, .. } = &mut term.kind { + let ty::TyKind::RigidTy(ty) = func.ty(&self.locals).unwrap().kind() else { + unreachable!() + }; let ty::RigidTy::FnDef(def, args) = ty else { unreachable!() }; self.calls.push(mir::mono::Instance::resolve(def, &args).unwrap()); } @@ -106,7 +183,7 @@ impl<'a> mir::MirVisitor for TestVisitor<'a> { fn main() { let path = "sim_visitor_input.rs"; generate_input(&path).unwrap(); - let args = vec![ + let args = &[ "rustc".to_string(), "-Cpanic=abort".to_string(), "--crate-name".to_string(), @@ -114,6 +191,7 @@ fn main() { path.to_string(), ]; run!(args, test_visitor).unwrap(); + run!(args, test_mut_visitor).unwrap(); } fn generate_input(path: &str) -> std::io::Result<()> { diff --git a/tests/ui-fulldeps/try-from-u32/errors.rs b/tests/ui-fulldeps/try-from-u32/errors.rs index 0470063312cb2..a25069c0a53cb 100644 --- a/tests/ui-fulldeps/try-from-u32/errors.rs +++ b/tests/ui-fulldeps/try-from-u32/errors.rs @@ -8,17 +8,17 @@ extern crate rustc_macros; use rustc_macros::TryFromU32; #[derive(TryFromU32)] -struct MyStruct {} //~ type is not an enum +struct MyStruct {} //~ ERROR type is not an enum #[derive(TryFromU32)] enum NonTrivial { A, B(), C {}, - D(bool), //~ enum variant cannot have fields - E(bool, bool), //~ enum variant cannot have fields - F { x: bool }, //~ enum variant cannot have fields - G { x: bool, y: bool }, //~ enum variant cannot have fields + D(bool), //~ ERROR enum variant cannot have fields + E(bool, bool), //~ ERROR enum variant cannot have fields + F { x: bool }, //~ ERROR enum variant cannot have fields + G { x: bool, y: bool }, //~ ERROR enum variant cannot have fields } fn main() {} diff --git a/tests/ui/README.md b/tests/ui/README.md deleted file mode 100644 index aa36481ae06eb..0000000000000 --- a/tests/ui/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# UI Tests - -This folder contains `rustc`'s -[UI tests](https://rustc-dev-guide.rust-lang.org/tests/ui.html). - -## Test Directives (Headers) - -Typically, a UI test will have some test directives / headers which are -special comments that tell compiletest how to build and interpret a test. - -As part of an ongoing effort to rewrite compiletest -(see ), a major -change proposal to change legacy compiletest-style headers `// ` -to [`ui_test`](https://github.com/oli-obk/ui_test)-style headers -`//@ ` was accepted (see -. - -An example directive is `ignore-test`. In legacy compiletest style, the header -would be written as - -```rs -// ignore-test -``` - -but in `ui_test` style, the header would be written as - -```rs -//@ ignore-test -``` - -compiletest is changed to accept only `//@` directives for UI tests -(currently), and will reject and report an error if it encounters any -comments `// ` that may be parsed as a legacy compiletest-style -test header. To fix this, you should migrate to the `ui_test`-style header -`//@ `. diff --git a/tests/ui/abi/abi-typo-unstable.feature_disabled.stderr b/tests/ui/abi/abi-typo-unstable.feature_disabled.stderr new file mode 100644 index 0000000000000..1934b483c47a0 --- /dev/null +++ b/tests/ui/abi/abi-typo-unstable.feature_disabled.stderr @@ -0,0 +1,11 @@ +error[E0703]: invalid ABI: found `rust-cull` + --> $DIR/abi-typo-unstable.rs:5:8 + | +LL | extern "rust-cull" fn rust_call(_: ()) {} + | ^^^^^^^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0703`. diff --git a/tests/ui/abi/abi-typo-unstable.feature_enabled.stderr b/tests/ui/abi/abi-typo-unstable.feature_enabled.stderr new file mode 100644 index 0000000000000..868b9509830b0 --- /dev/null +++ b/tests/ui/abi/abi-typo-unstable.feature_enabled.stderr @@ -0,0 +1,16 @@ +error[E0703]: invalid ABI: found `rust-cull` + --> $DIR/abi-typo-unstable.rs:5:8 + | +LL | extern "rust-cull" fn rust_call(_: ()) {} + | ^^^^^^^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions +help: there's a similarly named valid ABI `rust-call` + | +LL - extern "rust-cull" fn rust_call(_: ()) {} +LL + extern "rust-call" fn rust_call(_: ()) {} + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0703`. diff --git a/tests/ui/abi/abi-typo-unstable.rs b/tests/ui/abi/abi-typo-unstable.rs index 94991a5eb17e8..75366217fa26f 100644 --- a/tests/ui/abi/abi-typo-unstable.rs +++ b/tests/ui/abi/abi-typo-unstable.rs @@ -1,6 +1,11 @@ -// rust-intrinsic is unstable and not enabled, so it should not be suggested as a fix -extern "rust-intrinsec" fn rust_intrinsic() {} //~ ERROR invalid ABI +//@ revisions: feature_disabled feature_enabled +#![cfg_attr(feature_enabled, feature(unboxed_closures))] + +// rust-call is unstable and not enabled, so it should not be suggested as a fix +extern "rust-cull" fn rust_call(_: ()) {} +//~^ ERROR invalid ABI +//[feature_enabled]~| HELP there's a similarly named valid ABI fn main() { - rust_intrinsic(); + rust_call(()); } diff --git a/tests/ui/abi/abi-typo-unstable.stderr b/tests/ui/abi/abi-typo-unstable.stderr deleted file mode 100644 index 9ba67ad7dbe4a..0000000000000 --- a/tests/ui/abi/abi-typo-unstable.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0703]: invalid ABI: found `rust-intrinsec` - --> $DIR/abi-typo-unstable.rs:2:8 - | -LL | extern "rust-intrinsec" fn rust_intrinsic() {} - | ^^^^^^^^^^^^^^^^ invalid ABI - | - = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0703`. diff --git a/tests/ui/abi/c-zst.aarch64-darwin.stderr b/tests/ui/abi/c-zst.aarch64-darwin.stderr index 57cc48aa9cf47..48fa2bf29bc40 100644 --- a/tests/ui/abi/c-zst.aarch64-darwin.stderr +++ b/tests/ui/abi/c-zst.aarch64-darwin.stderr @@ -9,7 +9,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -38,7 +38,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/abi/c-zst.powerpc-linux.stderr b/tests/ui/abi/c-zst.powerpc-linux.stderr index 6738017673003..bfdf94c99007c 100644 --- a/tests/ui/abi/c-zst.powerpc-linux.stderr +++ b/tests/ui/abi/c-zst.powerpc-linux.stderr @@ -9,7 +9,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -49,7 +49,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/abi/c-zst.s390x-linux.stderr b/tests/ui/abi/c-zst.s390x-linux.stderr index 6738017673003..bfdf94c99007c 100644 --- a/tests/ui/abi/c-zst.s390x-linux.stderr +++ b/tests/ui/abi/c-zst.s390x-linux.stderr @@ -9,7 +9,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -49,7 +49,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/abi/c-zst.sparc64-linux.stderr b/tests/ui/abi/c-zst.sparc64-linux.stderr index 6738017673003..bfdf94c99007c 100644 --- a/tests/ui/abi/c-zst.sparc64-linux.stderr +++ b/tests/ui/abi/c-zst.sparc64-linux.stderr @@ -9,7 +9,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -49,7 +49,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/abi/c-zst.x86_64-linux.stderr b/tests/ui/abi/c-zst.x86_64-linux.stderr index 57cc48aa9cf47..48fa2bf29bc40 100644 --- a/tests/ui/abi/c-zst.x86_64-linux.stderr +++ b/tests/ui/abi/c-zst.x86_64-linux.stderr @@ -9,7 +9,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -38,7 +38,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr index 6738017673003..bfdf94c99007c 100644 --- a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr +++ b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr @@ -9,7 +9,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -49,7 +49,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index 01d90717107bb..68706f1e821ac 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -61,7 +61,6 @@ //@[nvptx64] needs-llvm-components: nvptx #![feature(no_core, rustc_attrs, lang_items)] #![feature(unsized_fn_params, transparent_unions)] -#![no_std] #![no_core] #![allow(unused, improper_ctypes_definitions, internal_features)] diff --git a/tests/ui/abi/debug.rs b/tests/ui/abi/debug.rs index 6dbc316146412..c0d8de05fda5e 100644 --- a/tests/ui/abi/debug.rs +++ b/tests/ui/abi/debug.rs @@ -52,3 +52,6 @@ type TestAbiNeSign = (fn(i32), fn(u32)); //~ ERROR: ABIs are not compatible #[rustc_abi(assert_eq)] type TestAbiEqNonsense = (fn((str, str)), fn((str, str))); //~ ERROR: cannot be known at compilation time + +#[rustc_abi("assert_eq")] //~ ERROR unrecognized argument +type Bad = u32; diff --git a/tests/ui/abi/debug.stderr b/tests/ui/abi/debug.stderr index 5f73ff7d6bd58..480f3f04215e7 100644 --- a/tests/ui/abi/debug.stderr +++ b/tests/ui/abi/debug.stderr @@ -9,7 +9,7 @@ error: fn_abi_of(test) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -48,7 +48,7 @@ error: fn_abi_of(test) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -107,7 +107,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -155,7 +155,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -205,7 +205,7 @@ error: fn_abi_of(test_generic) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Pointer( AddressSpace( @@ -245,7 +245,7 @@ error: fn_abi_of(test_generic) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -292,7 +292,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -331,7 +331,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -366,7 +366,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -405,7 +405,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -446,7 +446,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Array { @@ -486,7 +486,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -521,7 +521,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Array { @@ -561,7 +561,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -602,7 +602,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Float( F32, @@ -640,7 +640,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -675,7 +675,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -714,7 +714,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -755,7 +755,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -794,7 +794,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -829,7 +829,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -868,7 +868,7 @@ error: ABIs are not compatible abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -906,6 +906,12 @@ LL | type TestAbiEqNonsense = (fn((str, str)), fn((str, str))); = help: the trait `Sized` is not implemented for `str` = note: only the last element of a tuple may have a dynamically sized type +error: unrecognized argument + --> $DIR/debug.rs:56:13 + | +LL | #[rustc_abi("assert_eq")] + | ^^^^^^^^^^^ + error: `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions --> $DIR/debug.rs:29:5 | @@ -923,7 +929,7 @@ error: fn_abi_of(assoc_test) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Pointer( AddressSpace( @@ -975,7 +981,7 @@ error: fn_abi_of(assoc_test) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -1004,6 +1010,6 @@ error: fn_abi_of(assoc_test) = FnAbi { LL | fn assoc_test(&self) { } | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/abi/fixed_x18.rs b/tests/ui/abi/fixed_x18.rs index f1ff3e1d53418..d64b845e5bd1c 100644 --- a/tests/ui/abi/fixed_x18.rs +++ b/tests/ui/abi/fixed_x18.rs @@ -2,7 +2,6 @@ // Behavior on aarch64 is tested by tests/codegen/fixed-x18.rs. // //@ revisions: x64 i686 arm riscv32 riscv64 -//@ error-pattern: the `-Zfixed-x18` flag is not supported //@ dont-check-compiler-stderr // //@ compile-flags: -Zfixed-x18 @@ -23,3 +22,5 @@ #[lang = "sized"] trait Sized {} + +//~? ERROR the `-Zfixed-x18` flag is not supported on the ` diff --git a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs index 22b9b029a4049..2c78b794a8d74 100644 --- a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs +++ b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs @@ -7,8 +7,6 @@ //@ run-pass //@ needs-subprocess -#![feature(avx512_target_feature)] - #![allow(overflowing_literals)] #![allow(unused_variables)] diff --git a/tests/ui/abi/large-byval-align.rs b/tests/ui/abi/large-byval-align.rs index ddd579f264e67..c1de841178fcd 100644 --- a/tests/ui/abi/large-byval-align.rs +++ b/tests/ui/abi/large-byval-align.rs @@ -1,6 +1,5 @@ //@ compile-flags: -Copt-level=0 //@ only-x86_64 -//@ min-llvm-version: 19 //@ build-pass #[repr(align(536870912))] diff --git a/tests/ui/abi/lib-defaults.rs b/tests/ui/abi/lib-defaults.rs deleted file mode 100644 index 2c2cad4f82dca..0000000000000 --- a/tests/ui/abi/lib-defaults.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-pass -//@ dont-check-compiler-stderr (rust-lang/rust#54222) - -//@ compile-flags: -lrust_test_helpers - -#[link(name = "rust_test_helpers", kind = "static")] -extern "C" { - pub fn rust_dbg_extern_identity_u32(x: u32) -> u32; -} - -fn main() { - unsafe { - rust_dbg_extern_identity_u32(42); - } -} diff --git a/tests/ui/abi/rust-cold-works-with-rustic-args.rs b/tests/ui/abi/rust-cold-works-with-rustic-args.rs new file mode 100644 index 0000000000000..5702736469965 --- /dev/null +++ b/tests/ui/abi/rust-cold-works-with-rustic-args.rs @@ -0,0 +1,6 @@ +//@build-pass +//@compile-flags: -Clink-dead-code=true --crate-type lib +// We used to not handle all "rustic" ABIs in a (relatively) uniform way, +// so we failed to fix up arguments for actually passing through the ABI... +#![feature(rust_cold_cc)] +pub extern "rust-cold" fn foo(_: [usize; 3]) {} diff --git a/tests/ui/abi/shadow-call-stack-without-fixed-x18.rs b/tests/ui/abi/shadow-call-stack-without-fixed-x18.rs index d758c903087b6..74882fb5c55ce 100644 --- a/tests/ui/abi/shadow-call-stack-without-fixed-x18.rs +++ b/tests/ui/abi/shadow-call-stack-without-fixed-x18.rs @@ -1,5 +1,4 @@ //@ compile-flags: --target aarch64-unknown-none -Zsanitizer=shadow-call-stack -//@ error-pattern: shadow-call-stack sanitizer is not supported for this target //@ dont-check-compiler-stderr //@ needs-llvm-components: aarch64 @@ -13,3 +12,5 @@ trait Sized {} #[no_mangle] pub fn foo() {} + +//~? ERROR shadow-call-stack sanitizer is not supported for this target diff --git a/tests/ui/abi/simd-abi-checks-avx.rs b/tests/ui/abi/simd-abi-checks-avx.rs index acab74300b8f6..7432381d15b72 100644 --- a/tests/ui/abi/simd-abi-checks-avx.rs +++ b/tests/ui/abi/simd-abi-checks-avx.rs @@ -1,8 +1,7 @@ //@ only-x86_64 -//@ build-pass -//@ ignore-pass (test emits codegen-time warnings) +//@ build-fail +//@ compile-flags: -C target-feature=-avx -#![feature(avx512_target_feature)] #![feature(portable_simd)] #![feature(simd_ffi)] #![allow(improper_ctypes_definitions)] @@ -13,20 +12,17 @@ use std::arch::x86_64::*; struct Wrapper(__m256); unsafe extern "C" fn w(_: Wrapper) { - //~^ requires the `avx` target feature, which is not enabled - //~| WARNING this was previously accepted by the compiler + //~^ ERROR: requires the `avx` target feature, which is not enabled todo!() } unsafe extern "C" fn f(_: __m256) { - //~^ requires the `avx` target feature, which is not enabled - //~| WARNING this was previously accepted by the compiler + //~^ ERROR: requires the `avx` target feature, which is not enabled todo!() } unsafe extern "C" fn g() -> __m256 { - //~^ requires the `avx` target feature, which is not enabled - //~| WARNING this was previously accepted by the compiler + //~^ ERROR: requires the `avx` target feature, which is not enabled todo!() } @@ -55,25 +51,20 @@ unsafe fn test() { unsafe fn in_closure() -> impl FnOnce() -> __m256 { #[inline(always)] // this disables target-feature inheritance || g() - //~^ WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING this was previously accepted by the compiler + //~^ ERROR requires the `avx` target feature, which is not enabled in the caller } fn main() { unsafe { f(g()); - //~^ WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING this was previously accepted by the compiler - //~| WARNING this was previously accepted by the compiler + //~^ ERROR requires the `avx` target feature, which is not enabled in the caller + //~| ERROR requires the `avx` target feature, which is not enabled in the caller } unsafe { gavx(favx()); - //~^ WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING this was previously accepted by the compiler - //~| WARNING this was previously accepted by the compiler + //~^ ERROR requires the `avx` target feature, which is not enabled in the caller + //~| ERROR requires the `avx` target feature, which is not enabled in the caller } unsafe { @@ -82,10 +73,8 @@ fn main() { unsafe { w(Wrapper(g())); - //~^ WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING this was previously accepted by the compiler - //~| WARNING this was previously accepted by the compiler + //~^ ERROR requires the `avx` target feature, which is not enabled in the caller + //~| ERROR requires the `avx` target feature, which is not enabled in the caller } unsafe { @@ -98,8 +87,7 @@ fn main() { fn some_extern() -> __m256; } some_extern(); - //~^ WARNING requires the `avx` target feature, which is not enabled in the caller - //~| WARNING this was previously accepted by the compiler + //~^ ERROR requires the `avx` target feature, which is not enabled in the caller } } diff --git a/tests/ui/abi/simd-abi-checks-avx.stderr b/tests/ui/abi/simd-abi-checks-avx.stderr index 0dddc7dfa1c1b..7489ca0194600 100644 --- a/tests/ui/abi/simd-abi-checks-avx.stderr +++ b/tests/ui/abi/simd-abi-checks-avx.stderr @@ -1,245 +1,96 @@ -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:64:11 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:59:11 | LL | f(g()); | ^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:64:9 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:59:9 | LL | f(g()); | ^^^^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:72:14 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:65:14 | LL | gavx(favx()); | ^^^^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:72:9 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:65:9 | LL | gavx(favx()); | ^^^^^^^^^^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:84:19 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:75:19 | LL | w(Wrapper(g())); | ^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function call uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:84:9 +error: this function call uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:75:9 | LL | w(Wrapper(g())); | ^^^^^^^^^^^^^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:100:9 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:89:9 | LL | some_extern(); | ^^^^^^^^^^^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:27:1 +error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled + --> $DIR/simd-abi-checks-avx.rs:24:1 | LL | unsafe extern "C" fn g() -> __m256 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:21:1 +error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled + --> $DIR/simd-abi-checks-avx.rs:19:1 | LL | unsafe extern "C" fn f(_: __m256) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:15:1 +error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled + --> $DIR/simd-abi-checks-avx.rs:14:1 | LL | unsafe extern "C" fn w(_: Wrapper) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:57:8 +error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller + --> $DIR/simd-abi-checks-avx.rs:53:8 | LL | || g() | ^^^ function called here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) -warning: 11 warnings emitted - -Future incompatibility report: Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:64:11 - | -LL | f(g()); - | ^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:64:9 - | -LL | f(g()); - | ^^^^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:72:14 - | -LL | gavx(favx()); - | ^^^^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:72:9 - | -LL | gavx(favx()); - | ^^^^^^^^^^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:84:19 - | -LL | w(Wrapper(g())); - | ^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function call uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:84:9 - | -LL | w(Wrapper(g())); - | ^^^^^^^^^^^^^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:100:9 - | -LL | some_extern(); - | ^^^^^^^^^^^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:27:1 - | -LL | unsafe extern "C" fn g() -> __m256 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:21:1 - | -LL | unsafe extern "C" fn f(_: __m256) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -Future breakage diagnostic: -warning: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled - --> $DIR/simd-abi-checks-avx.rs:15:1 - | -LL | unsafe extern "C" fn w(_: Wrapper) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here +note: the above error was encountered while instantiating `fn in_closure::{closure#0}` + --> $DIR/simd-abi-checks-avx.rs:81:9 | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default +LL | in_closure()(); + | ^^^^^^^^^^^^^^ -Future breakage diagnostic: -warning: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller - --> $DIR/simd-abi-checks-avx.rs:57:8 - | -LL | || g() - | ^^^ function called here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default +error: aborting due to 11 previous errors diff --git a/tests/ui/abi/simd-abi-checks-empty-list.rs b/tests/ui/abi/simd-abi-checks-empty-list.rs index ca0889364fc32..d00445b29e055 100644 --- a/tests/ui/abi/simd-abi-checks-empty-list.rs +++ b/tests/ui/abi/simd-abi-checks-empty-list.rs @@ -1,8 +1,9 @@ +//! At the time of writing, the list of "which target feature enables which vector size" is empty +//! for SPARC. Ensure that this leads to all vector sizes causing an error. //@ add-core-stubs //@ needs-llvm-components: sparc //@ compile-flags: --target=sparc-unknown-none-elf --crate-type=rlib -//@ build-pass -//@ ignore-pass (test emits codegen-time warnings) +//@ build-fail #![no_core] #![feature(no_core, repr_simd)] #![allow(improper_ctypes_definitions)] @@ -14,5 +15,4 @@ use minicore::*; pub struct SimdVec([i32; 4]); pub extern "C" fn pass_by_vec(_: SimdVec) {} -//~^ this function definition uses SIMD vector type `SimdVec` which is not currently supported with the chosen ABI -//~| WARNING this was previously accepted by the compiler +//~^ ERROR: this function definition uses SIMD vector type `SimdVec` which is not currently supported with the chosen ABI diff --git a/tests/ui/abi/simd-abi-checks-empty-list.stderr b/tests/ui/abi/simd-abi-checks-empty-list.stderr index 111dda42f33f2..780afb3844fc5 100644 --- a/tests/ui/abi/simd-abi-checks-empty-list.stderr +++ b/tests/ui/abi/simd-abi-checks-empty-list.stderr @@ -1,23 +1,8 @@ -warning: this function definition uses SIMD vector type `SimdVec` which is not currently supported with the chosen ABI - --> $DIR/simd-abi-checks-empty-list.rs:16:1 +error: this function definition uses SIMD vector type `SimdVec` which is not currently supported with the chosen ABI + --> $DIR/simd-abi-checks-empty-list.rs:17:1 | LL | pub extern "C" fn pass_by_vec(_: SimdVec) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = note: `#[warn(abi_unsupported_vector_types)]` on by default - -warning: 1 warning emitted -Future incompatibility report: Future breakage diagnostic: -warning: this function definition uses SIMD vector type `SimdVec` which is not currently supported with the chosen ABI - --> $DIR/simd-abi-checks-empty-list.rs:16:1 - | -LL | pub extern "C" fn pass_by_vec(_: SimdVec) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = note: `#[warn(abi_unsupported_vector_types)]` on by default +error: aborting due to 1 previous error diff --git a/tests/ui/abi/simd-abi-checks-s390x.rs b/tests/ui/abi/simd-abi-checks-s390x.rs index 424ac00edcfc5..2d4eb7a350f25 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.rs +++ b/tests/ui/abi/simd-abi-checks-s390x.rs @@ -1,7 +1,7 @@ //@ add-core-stubs //@ revisions: z10 z13_no_vector z13_soft_float //@ build-fail -//@[z10] compile-flags: --target s390x-unknown-linux-gnu +//@[z10] compile-flags: --target s390x-unknown-linux-gnu -C target-cpu=z10 //@[z10] needs-llvm-components: systemz //@[z13_no_vector] compile-flags: --target s390x-unknown-linux-gnu -C target-cpu=z13 -C target-feature=-vector //@[z13_no_vector] needs-llvm-components: systemz @@ -13,7 +13,6 @@ #![no_core] #![crate_type = "lib"] #![allow(non_camel_case_types, improper_ctypes_definitions)] -#![deny(abi_unsupported_vector_types)] extern crate minicore; use minicore::*; @@ -38,13 +37,11 @@ impl Copy for TransparentWrapper {} #[no_mangle] extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted *x } #[no_mangle] extern "C" fn vector_ret(x: &i8x16) -> i8x16 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted *x } #[no_mangle] @@ -93,7 +90,6 @@ extern "C" fn vector_transparent_wrapper_ret_small( x: &TransparentWrapper, ) -> TransparentWrapper { //~^^^ ERROR requires the `vector` target feature, which is not enabled - //~^^^^ WARN this was previously accepted *x } #[no_mangle] @@ -101,7 +97,6 @@ extern "C" fn vector_transparent_wrapper_ret( x: &TransparentWrapper, ) -> TransparentWrapper { //~^^^ ERROR requires the `vector` target feature, which is not enabled - //~^^^^ WARN this was previously accepted *x } #[no_mangle] @@ -115,13 +110,11 @@ extern "C" fn vector_transparent_wrapper_ret_large( #[no_mangle] extern "C" fn vector_arg_small(x: i8x8) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted unsafe { *(&x as *const i8x8 as *const i64) } } #[no_mangle] extern "C" fn vector_arg(x: i8x16) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted unsafe { *(&x as *const i8x16 as *const i64) } } #[no_mangle] @@ -133,13 +126,11 @@ extern "C" fn vector_arg_large(x: i8x32) -> i64 { #[no_mangle] extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted unsafe { *(&x as *const Wrapper as *const i64) } } #[no_mangle] extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted unsafe { *(&x as *const Wrapper as *const i64) } } #[no_mangle] @@ -151,13 +142,11 @@ extern "C" fn vector_wrapper_arg_large(x: Wrapper) -> i64 { #[no_mangle] extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted unsafe { *(&x as *const TransparentWrapper as *const i64) } } #[no_mangle] extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - //~^^ WARN this was previously accepted unsafe { *(&x as *const TransparentWrapper as *const i64) } } #[no_mangle] diff --git a/tests/ui/abi/simd-abi-checks-s390x.z10.stderr b/tests/ui/abi/simd-abi-checks-s390x.z10.stderr index d2f7abb7c3221..c1c4e90f3cf89 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.z10.stderr +++ b/tests/ui/abi/simd-abi-checks-s390x.z10.stderr @@ -1,275 +1,86 @@ error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:39:1 + --> $DIR/simd-abi-checks-s390x.rs:38:1 | LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:45:1 + --> $DIR/simd-abi-checks-s390x.rs:43:1 | LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:92:1 + --> $DIR/simd-abi-checks-s390x.rs:89:1 | LL | / extern "C" fn vector_transparent_wrapper_ret_small( LL | | x: &TransparentWrapper, LL | | ) -> TransparentWrapper { | |_____________________________^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:100:1 + --> $DIR/simd-abi-checks-s390x.rs:96:1 | LL | / extern "C" fn vector_transparent_wrapper_ret( LL | | x: &TransparentWrapper, LL | | ) -> TransparentWrapper { | |______________________________^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 + --> $DIR/simd-abi-checks-s390x.rs:111:1 | LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:122:1 + --> $DIR/simd-abi-checks-s390x.rs:116:1 | LL | extern "C" fn vector_arg(x: i8x16) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:134:1 + --> $DIR/simd-abi-checks-s390x.rs:127:1 | LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:140:1 + --> $DIR/simd-abi-checks-s390x.rs:132:1 | LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:152:1 + --> $DIR/simd-abi-checks-s390x.rs:143:1 | LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:158:1 + --> $DIR/simd-abi-checks-s390x.rs:148:1 | LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: aborting due to 10 previous errors -Future incompatibility report: Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:39:1 - | -LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:45:1 - | -LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:92:1 - | -LL | / extern "C" fn vector_transparent_wrapper_ret_small( -LL | | x: &TransparentWrapper, -LL | | ) -> TransparentWrapper { - | |_____________________________^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:100:1 - | -LL | / extern "C" fn vector_transparent_wrapper_ret( -LL | | x: &TransparentWrapper, -LL | | ) -> TransparentWrapper { - | |______________________________^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 - | -LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:122:1 - | -LL | extern "C" fn vector_arg(x: i8x16) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:134:1 - | -LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:140:1 - | -LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:152:1 - | -LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:158:1 - | -LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - diff --git a/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr b/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr index d2f7abb7c3221..c1c4e90f3cf89 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr +++ b/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr @@ -1,275 +1,86 @@ error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:39:1 + --> $DIR/simd-abi-checks-s390x.rs:38:1 | LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:45:1 + --> $DIR/simd-abi-checks-s390x.rs:43:1 | LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:92:1 + --> $DIR/simd-abi-checks-s390x.rs:89:1 | LL | / extern "C" fn vector_transparent_wrapper_ret_small( LL | | x: &TransparentWrapper, LL | | ) -> TransparentWrapper { | |_____________________________^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:100:1 + --> $DIR/simd-abi-checks-s390x.rs:96:1 | LL | / extern "C" fn vector_transparent_wrapper_ret( LL | | x: &TransparentWrapper, LL | | ) -> TransparentWrapper { | |______________________________^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 + --> $DIR/simd-abi-checks-s390x.rs:111:1 | LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:122:1 + --> $DIR/simd-abi-checks-s390x.rs:116:1 | LL | extern "C" fn vector_arg(x: i8x16) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:134:1 + --> $DIR/simd-abi-checks-s390x.rs:127:1 | LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:140:1 + --> $DIR/simd-abi-checks-s390x.rs:132:1 | LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:152:1 + --> $DIR/simd-abi-checks-s390x.rs:143:1 | LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:158:1 + --> $DIR/simd-abi-checks-s390x.rs:148:1 | LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: aborting due to 10 previous errors -Future incompatibility report: Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:39:1 - | -LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:45:1 - | -LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:92:1 - | -LL | / extern "C" fn vector_transparent_wrapper_ret_small( -LL | | x: &TransparentWrapper, -LL | | ) -> TransparentWrapper { - | |_____________________________^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:100:1 - | -LL | / extern "C" fn vector_transparent_wrapper_ret( -LL | | x: &TransparentWrapper, -LL | | ) -> TransparentWrapper { - | |______________________________^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 - | -LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:122:1 - | -LL | extern "C" fn vector_arg(x: i8x16) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:134:1 - | -LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:140:1 - | -LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:152:1 - | -LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:158:1 - | -LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - diff --git a/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr b/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr index d2f7abb7c3221..c1c4e90f3cf89 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr +++ b/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr @@ -1,275 +1,86 @@ error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:39:1 + --> $DIR/simd-abi-checks-s390x.rs:38:1 | LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:45:1 + --> $DIR/simd-abi-checks-s390x.rs:43:1 | LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:92:1 + --> $DIR/simd-abi-checks-s390x.rs:89:1 | LL | / extern "C" fn vector_transparent_wrapper_ret_small( LL | | x: &TransparentWrapper, LL | | ) -> TransparentWrapper { | |_____________________________^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:100:1 + --> $DIR/simd-abi-checks-s390x.rs:96:1 | LL | / extern "C" fn vector_transparent_wrapper_ret( LL | | x: &TransparentWrapper, LL | | ) -> TransparentWrapper { | |______________________________^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 + --> $DIR/simd-abi-checks-s390x.rs:111:1 | LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:122:1 + --> $DIR/simd-abi-checks-s390x.rs:116:1 | LL | extern "C" fn vector_arg(x: i8x16) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:134:1 + --> $DIR/simd-abi-checks-s390x.rs:127:1 | LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:140:1 + --> $DIR/simd-abi-checks-s390x.rs:132:1 | LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:152:1 + --> $DIR/simd-abi-checks-s390x.rs:143:1 | LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:158:1 + --> $DIR/simd-abi-checks-s390x.rs:148:1 | LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: aborting due to 10 previous errors -Future incompatibility report: Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:39:1 - | -LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:45:1 - | -LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:92:1 - | -LL | / extern "C" fn vector_transparent_wrapper_ret_small( -LL | | x: &TransparentWrapper, -LL | | ) -> TransparentWrapper { - | |_____________________________^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:100:1 - | -LL | / extern "C" fn vector_transparent_wrapper_ret( -LL | | x: &TransparentWrapper, -LL | | ) -> TransparentWrapper { - | |______________________________^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 - | -LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:122:1 - | -LL | extern "C" fn vector_arg(x: i8x16) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:134:1 - | -LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:140:1 - | -LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:152:1 - | -LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:158:1 - | -LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -note: the lint level is defined here - --> $DIR/simd-abi-checks-s390x.rs:16:9 - | -LL | #![deny(abi_unsupported_vector_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - diff --git a/tests/ui/abi/simd-abi-checks-sse.rs b/tests/ui/abi/simd-abi-checks-sse.rs index cb708bea3cae9..817f9b6d13bc6 100644 --- a/tests/ui/abi/simd-abi-checks-sse.rs +++ b/tests/ui/abi/simd-abi-checks-sse.rs @@ -3,8 +3,7 @@ //@ compile-flags: --crate-type=rlib --target=i586-unknown-linux-gnu //@ compile-flags: -Ctarget-cpu=pentium4 -C target-feature=-sse,-sse2 //@ add-core-stubs -//@ build-pass -//@ ignore-pass (test emits codegen-time warnings) +//@ build-fail //@ needs-llvm-components: x86 #![feature(no_core, repr_simd)] #![no_core] @@ -18,6 +17,5 @@ pub struct SseVector([i64; 2]); #[no_mangle] pub unsafe extern "C" fn f(_: SseVector) { - //~^ this function definition uses SIMD vector type `SseVector` which (with the chosen ABI) requires the `sse` target feature, which is not enabled - //~| WARNING this was previously accepted by the compiler + //~^ ERROR: this function definition uses SIMD vector type `SseVector` which (with the chosen ABI) requires the `sse` target feature, which is not enabled } diff --git a/tests/ui/abi/simd-abi-checks-sse.stderr b/tests/ui/abi/simd-abi-checks-sse.stderr index c0f2e6e1e1b14..a5a2ba808c660 100644 --- a/tests/ui/abi/simd-abi-checks-sse.stderr +++ b/tests/ui/abi/simd-abi-checks-sse.stderr @@ -1,25 +1,10 @@ -warning: this function definition uses SIMD vector type `SseVector` which (with the chosen ABI) requires the `sse` target feature, which is not enabled - --> $DIR/simd-abi-checks-sse.rs:20:1 +error: this function definition uses SIMD vector type `SseVector` which (with the chosen ABI) requires the `sse` target feature, which is not enabled + --> $DIR/simd-abi-checks-sse.rs:19:1 | LL | pub unsafe extern "C" fn f(_: SseVector) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | - = 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 #116558 = help: consider enabling it globally (`-C target-feature=+sse`) or locally (`#[target_feature(enable="sse")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default -warning: 1 warning emitted - -Future incompatibility report: Future breakage diagnostic: -warning: this function definition uses SIMD vector type `SseVector` which (with the chosen ABI) requires the `sse` target feature, which is not enabled - --> $DIR/simd-abi-checks-sse.rs:20:1 - | -LL | pub unsafe extern "C" fn f(_: SseVector) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here - | - = 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 #116558 - = help: consider enabling it globally (`-C target-feature=+sse`) or locally (`#[target_feature(enable="sse")]`) - = note: `#[warn(abi_unsupported_vector_types)]` on by default +error: aborting due to 1 previous error diff --git a/tests/ui/abi/sysv64-zst.stderr b/tests/ui/abi/sysv64-zst.stderr index ec85030c10686..59d7b00441790 100644 --- a/tests/ui/abi/sysv64-zst.stderr +++ b/tests/ui/abi/sysv64-zst.stderr @@ -9,7 +9,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -38,7 +38,7 @@ error: fn_abi_of(pass_zst) = FnAbi { abi: $SOME_ALIGN, pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/abi/unsized-args-in-c-abi-issues-94223-115845.rs b/tests/ui/abi/unsized-args-in-c-abi-issues-94223-115845.rs index 376630b8d33f7..7d21307e1b2d9 100644 --- a/tests/ui/abi/unsized-args-in-c-abi-issues-94223-115845.rs +++ b/tests/ui/abi/unsized-args-in-c-abi-issues-94223-115845.rs @@ -1,9 +1,10 @@ //@ check-pass #![allow(improper_ctypes_definitions)] -#![feature(unsized_tuple_coercion)] #![feature(unsized_fn_params)] #![crate_type = "lib"] +struct Fat(T); + // Check that computing the fn abi for `bad`, with a external ABI fn ptr that is not FFI-safe, does // not ICE. @@ -19,12 +20,12 @@ pub extern "C" fn declare_bad(_x: str) {} pub extern "system" fn declare_more_bad(f: dyn FnOnce()) { } -fn make_bad() -> extern "C" fn(([u8],)) { +fn make_bad() -> extern "C" fn(Fat<[u8]>) { todo!() } pub fn call_bad() { let f = make_bad(); - let slice: Box<([u8],)> = Box::new(([1; 8],)); + let slice: Box> = Box::new(Fat([1; 8])); f(*slice); } diff --git a/tests/ui/abi/unsupported.aarch64.stderr b/tests/ui/abi/unsupported.aarch64.stderr index 01f071f1f310f..c11cc4e2d52e5 100644 --- a/tests/ui/abi/unsupported.aarch64.stderr +++ b/tests/ui/abi/unsupported.aarch64.stderr @@ -206,3 +206,113 @@ LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} error: aborting due to 19 previous errors; 10 warnings emitted For more information about this error, try `rustc --explain E0570`. +Future incompatibility report: Future breakage diagnostic: +warning: the calling convention "ptx-kernel" is not supported on this target + --> $DIR/unsupported.rs:35:15 + | +LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "aapcs" is not supported on this target + --> $DIR/unsupported.rs:51:17 + | +LL | fn aapcs_ptr(f: extern "aapcs" fn()) { + | ^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "msp430-interrupt" is not supported on this target + --> $DIR/unsupported.rs:73:18 + | +LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "avr-interrupt" is not supported on this target + --> $DIR/unsupported.rs:83:15 + | +LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "riscv-interrupt-m" is not supported on this target + --> $DIR/unsupported.rs:96:17 + | +LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "x86-interrupt" is not supported on this target + --> $DIR/unsupported.rs:118:15 + | +LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "thiscall" is not supported on this target + --> $DIR/unsupported.rs:141:20 + | +LL | fn thiscall_ptr(f: extern "thiscall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "stdcall" is not supported on this target + --> $DIR/unsupported.rs:167:19 + | +LL | fn stdcall_ptr(f: extern "stdcall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target + --> $DIR/unsupported.rs:187:21 + | +LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target + --> $DIR/unsupported.rs:195:22 + | +LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + diff --git a/tests/ui/abi/unsupported.arm.stderr b/tests/ui/abi/unsupported.arm.stderr index dfb4b573349bc..b2f24381336a6 100644 --- a/tests/ui/abi/unsupported.arm.stderr +++ b/tests/ui/abi/unsupported.arm.stderr @@ -185,3 +185,102 @@ LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} error: aborting due to 17 previous errors; 9 warnings emitted For more information about this error, try `rustc --explain E0570`. +Future incompatibility report: Future breakage diagnostic: +warning: the calling convention "ptx-kernel" is not supported on this target + --> $DIR/unsupported.rs:35:15 + | +LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "msp430-interrupt" is not supported on this target + --> $DIR/unsupported.rs:73:18 + | +LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "avr-interrupt" is not supported on this target + --> $DIR/unsupported.rs:83:15 + | +LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "riscv-interrupt-m" is not supported on this target + --> $DIR/unsupported.rs:96:17 + | +LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "x86-interrupt" is not supported on this target + --> $DIR/unsupported.rs:118:15 + | +LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "thiscall" is not supported on this target + --> $DIR/unsupported.rs:141:20 + | +LL | fn thiscall_ptr(f: extern "thiscall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "stdcall" is not supported on this target + --> $DIR/unsupported.rs:167:19 + | +LL | fn stdcall_ptr(f: extern "stdcall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target + --> $DIR/unsupported.rs:187:21 + | +LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target + --> $DIR/unsupported.rs:195:22 + | +LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + diff --git a/tests/ui/abi/unsupported.i686.stderr b/tests/ui/abi/unsupported.i686.stderr index d7b76a4730b2b..94bd9b8af90d2 100644 --- a/tests/ui/abi/unsupported.i686.stderr +++ b/tests/ui/abi/unsupported.i686.stderr @@ -143,3 +143,80 @@ LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} error: aborting due to 13 previous errors; 7 warnings emitted For more information about this error, try `rustc --explain E0570`. +Future incompatibility report: Future breakage diagnostic: +warning: the calling convention "ptx-kernel" is not supported on this target + --> $DIR/unsupported.rs:35:15 + | +LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "aapcs" is not supported on this target + --> $DIR/unsupported.rs:51:17 + | +LL | fn aapcs_ptr(f: extern "aapcs" fn()) { + | ^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "msp430-interrupt" is not supported on this target + --> $DIR/unsupported.rs:73:18 + | +LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "avr-interrupt" is not supported on this target + --> $DIR/unsupported.rs:83:15 + | +LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "riscv-interrupt-m" is not supported on this target + --> $DIR/unsupported.rs:96:17 + | +LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target + --> $DIR/unsupported.rs:187:21 + | +LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target + --> $DIR/unsupported.rs:195:22 + | +LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + diff --git a/tests/ui/abi/unsupported.riscv32.stderr b/tests/ui/abi/unsupported.riscv32.stderr index eef2ead4f0f3f..c6ff47283c6ec 100644 --- a/tests/ui/abi/unsupported.riscv32.stderr +++ b/tests/ui/abi/unsupported.riscv32.stderr @@ -185,3 +185,102 @@ LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} error: aborting due to 17 previous errors; 9 warnings emitted For more information about this error, try `rustc --explain E0570`. +Future incompatibility report: Future breakage diagnostic: +warning: the calling convention "ptx-kernel" is not supported on this target + --> $DIR/unsupported.rs:35:15 + | +LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "aapcs" is not supported on this target + --> $DIR/unsupported.rs:51:17 + | +LL | fn aapcs_ptr(f: extern "aapcs" fn()) { + | ^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "msp430-interrupt" is not supported on this target + --> $DIR/unsupported.rs:73:18 + | +LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "avr-interrupt" is not supported on this target + --> $DIR/unsupported.rs:83:15 + | +LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "x86-interrupt" is not supported on this target + --> $DIR/unsupported.rs:118:15 + | +LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "thiscall" is not supported on this target + --> $DIR/unsupported.rs:141:20 + | +LL | fn thiscall_ptr(f: extern "thiscall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "stdcall" is not supported on this target + --> $DIR/unsupported.rs:167:19 + | +LL | fn stdcall_ptr(f: extern "stdcall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target + --> $DIR/unsupported.rs:187:21 + | +LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target + --> $DIR/unsupported.rs:195:22 + | +LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + diff --git a/tests/ui/abi/unsupported.riscv64.stderr b/tests/ui/abi/unsupported.riscv64.stderr index eef2ead4f0f3f..c6ff47283c6ec 100644 --- a/tests/ui/abi/unsupported.riscv64.stderr +++ b/tests/ui/abi/unsupported.riscv64.stderr @@ -185,3 +185,102 @@ LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} error: aborting due to 17 previous errors; 9 warnings emitted For more information about this error, try `rustc --explain E0570`. +Future incompatibility report: Future breakage diagnostic: +warning: the calling convention "ptx-kernel" is not supported on this target + --> $DIR/unsupported.rs:35:15 + | +LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "aapcs" is not supported on this target + --> $DIR/unsupported.rs:51:17 + | +LL | fn aapcs_ptr(f: extern "aapcs" fn()) { + | ^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "msp430-interrupt" is not supported on this target + --> $DIR/unsupported.rs:73:18 + | +LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "avr-interrupt" is not supported on this target + --> $DIR/unsupported.rs:83:15 + | +LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "x86-interrupt" is not supported on this target + --> $DIR/unsupported.rs:118:15 + | +LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "thiscall" is not supported on this target + --> $DIR/unsupported.rs:141:20 + | +LL | fn thiscall_ptr(f: extern "thiscall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "stdcall" is not supported on this target + --> $DIR/unsupported.rs:167:19 + | +LL | fn stdcall_ptr(f: extern "stdcall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target + --> $DIR/unsupported.rs:187:21 + | +LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target + --> $DIR/unsupported.rs:195:22 + | +LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + diff --git a/tests/ui/abi/unsupported.x64.stderr b/tests/ui/abi/unsupported.x64.stderr index 80613d83e588c..0a9f9a69123c0 100644 --- a/tests/ui/abi/unsupported.x64.stderr +++ b/tests/ui/abi/unsupported.x64.stderr @@ -185,3 +185,102 @@ LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} error: aborting due to 17 previous errors; 9 warnings emitted For more information about this error, try `rustc --explain E0570`. +Future incompatibility report: Future breakage diagnostic: +warning: the calling convention "ptx-kernel" is not supported on this target + --> $DIR/unsupported.rs:35:15 + | +LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "aapcs" is not supported on this target + --> $DIR/unsupported.rs:51:17 + | +LL | fn aapcs_ptr(f: extern "aapcs" fn()) { + | ^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "msp430-interrupt" is not supported on this target + --> $DIR/unsupported.rs:73:18 + | +LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "avr-interrupt" is not supported on this target + --> $DIR/unsupported.rs:83:15 + | +LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "riscv-interrupt-m" is not supported on this target + --> $DIR/unsupported.rs:96:17 + | +LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "thiscall" is not supported on this target + --> $DIR/unsupported.rs:141:20 + | +LL | fn thiscall_ptr(f: extern "thiscall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "stdcall" is not supported on this target + --> $DIR/unsupported.rs:167:19 + | +LL | fn stdcall_ptr(f: extern "stdcall" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target + --> $DIR/unsupported.rs:187:21 + | +LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +Future breakage diagnostic: +warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target + --> $DIR/unsupported.rs:195:22 + | +LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + diff --git a/tests/ui/abi/vectorcall-abi-checks.rs b/tests/ui/abi/vectorcall-abi-checks.rs index d83bbffa745f8..f3f399e0e3139 100644 --- a/tests/ui/abi/vectorcall-abi-checks.rs +++ b/tests/ui/abi/vectorcall-abi-checks.rs @@ -11,11 +11,11 @@ use minicore::*; #[no_mangle] pub extern "vectorcall" fn f() { - //~^ ABI "vectorcall" which requires the `sse2` target feature + //~^ ERROR ABI "vectorcall" which requires the `sse2` target feature } #[no_mangle] pub fn call_site() { f(); - //~^ ABI "vectorcall" which requires the `sse2` target feature + //~^ ERROR ABI "vectorcall" which requires the `sse2` target feature } diff --git a/tests/ui/alloc-error/alloc-error-handler-bad-signature-1.stderr b/tests/ui/alloc-error/alloc-error-handler-bad-signature-1.stderr index 80ff10e13d889..15314fac37b2c 100644 --- a/tests/ui/alloc-error/alloc-error-handler-bad-signature-1.stderr +++ b/tests/ui/alloc-error/alloc-error-handler-bad-signature-1.stderr @@ -18,7 +18,6 @@ LL | fn oom( | ^^^ LL | info: &Layout, | ------------- - = note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/alloc-error-handler-bad-signature-1.rs:10:1 @@ -35,7 +34,6 @@ LL | | } | = note: expected type `!` found unit type `()` - = note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/alloc-error/alloc-error-handler-bad-signature-2.stderr b/tests/ui/alloc-error/alloc-error-handler-bad-signature-2.stderr index 7a495380f2ba1..2ab4263841128 100644 --- a/tests/ui/alloc-error/alloc-error-handler-bad-signature-2.stderr +++ b/tests/ui/alloc-error/alloc-error-handler-bad-signature-2.stderr @@ -26,7 +26,6 @@ LL | fn oom( | ^^^ LL | info: Layout, | ------------ - = note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/alloc-error-handler-bad-signature-2.rs:10:1 @@ -43,7 +42,6 @@ LL | | } | = note: expected type `!` found unit type `()` - = note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/alloc-error/alloc-error-handler-bad-signature-3.stderr b/tests/ui/alloc-error/alloc-error-handler-bad-signature-3.stderr index 3be219bdb0646..3a410174f548e 100644 --- a/tests/ui/alloc-error/alloc-error-handler-bad-signature-3.stderr +++ b/tests/ui/alloc-error/alloc-error-handler-bad-signature-3.stderr @@ -14,7 +14,6 @@ note: function defined here | LL | fn oom() -> ! { | ^^^ - = note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/allocator/not-an-allocator.stderr b/tests/ui/allocator/not-an-allocator.stderr index e1967b700133e..079bf9334ebc3 100644 --- a/tests/ui/allocator/not-an-allocator.stderr +++ b/tests/ui/allocator/not-an-allocator.stderr @@ -7,7 +7,6 @@ LL | static A: usize = 0; | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize` | = help: the trait `GlobalAlloc` is implemented for `System` - = note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied --> $DIR/not-an-allocator.rs:2:11 @@ -19,7 +18,6 @@ LL | static A: usize = 0; | = help: the trait `GlobalAlloc` is implemented for `System` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied --> $DIR/not-an-allocator.rs:2:11 @@ -31,7 +29,6 @@ LL | static A: usize = 0; | = help: the trait `GlobalAlloc` is implemented for `System` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied --> $DIR/not-an-allocator.rs:2:11 @@ -43,7 +40,6 @@ LL | static A: usize = 0; | = help: the trait `GlobalAlloc` is implemented for `System` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/allocator/two-allocators.stderr b/tests/ui/allocator/two-allocators.stderr index 1eecbc0a97b1f..5308232a20b59 100644 --- a/tests/ui/allocator/two-allocators.stderr +++ b/tests/ui/allocator/two-allocators.stderr @@ -7,8 +7,6 @@ LL | #[global_allocator] | ------------------- in this procedural macro expansion LL | static B: System = System; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot define a new global allocator - | - = note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/allocator/two-allocators2.rs b/tests/ui/allocator/two-allocators2.rs index b3bb4598c778b..6efb297054bca 100644 --- a/tests/ui/allocator/two-allocators2.rs +++ b/tests/ui/allocator/two-allocators2.rs @@ -1,6 +1,5 @@ //@ aux-build:system-allocator.rs //@ no-prefer-dynamic -//@ error-pattern: the `#[global_allocator]` in extern crate system_allocator; @@ -10,3 +9,5 @@ use std::alloc::System; static A: System = System; fn main() {} + +//~? ERROR the `#[global_allocator]` in this crate conflicts with global allocator in: system_allocator diff --git a/tests/ui/allocator/two-allocators3.rs b/tests/ui/allocator/two-allocators3.rs index 0cb3879666da3..3aba96068de9c 100644 --- a/tests/ui/allocator/two-allocators3.rs +++ b/tests/ui/allocator/two-allocators3.rs @@ -1,10 +1,10 @@ //@ aux-build:system-allocator.rs //@ aux-build:system-allocator2.rs //@ no-prefer-dynamic -//@ error-pattern: the `#[global_allocator]` in - extern crate system_allocator; extern crate system_allocator2; fn main() {} + +//~? ERROR the `#[global_allocator]` in system_allocator conflicts with global allocator in: system_allocator2 diff --git a/tests/ui/amdgpu-require-explicit-cpu.rs b/tests/ui/amdgpu-require-explicit-cpu.rs deleted file mode 100644 index 46778a1094fab..0000000000000 --- a/tests/ui/amdgpu-require-explicit-cpu.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ revisions: nocpu cpu -//@ no-prefer-dynamic -//@ compile-flags: --crate-type=cdylib --target=amdgcn-amd-amdhsa -//@ needs-llvm-components: amdgpu -//@ needs-rust-lld -//@[nocpu] error-pattern: target requires explicitly specifying a cpu -//@[nocpu] build-fail -//@[cpu] compile-flags: -Ctarget-cpu=gfx900 -//@[cpu] build-pass - -#![feature(no_core, lang_items)] -#![no_core] - -#[lang="sized"] -trait Sized {} - -pub fn foo() {} diff --git a/tests/ui/annotate-snippet/missing-type.rs b/tests/ui/annotate-snippet/missing-type.rs index ea1c2521103e8..bfe8ab2f5ffeb 100644 --- a/tests/ui/annotate-snippet/missing-type.rs +++ b/tests/ui/annotate-snippet/missing-type.rs @@ -1,6 +1,7 @@ //@ compile-flags: --error-format human-annotate-rs -Z unstable-options -//@ error-pattern:cannot find type `Iter` in this scope pub fn main() { let x: Iter; } + +//~? RAW cannot find type `Iter` in this scope diff --git a/tests/ui/annotate-snippet/missing-type.stderr b/tests/ui/annotate-snippet/missing-type.stderr index 89ce19c182f52..c16f022a77fa3 100644 --- a/tests/ui/annotate-snippet/missing-type.stderr +++ b/tests/ui/annotate-snippet/missing-type.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `Iter` in this scope - --> $DIR/missing-type.rs:5:12 + --> $DIR/missing-type.rs:4:12 | LL | let x: Iter; | ^^^^ not found in this scope diff --git a/tests/ui/annotate-snippet/multispan.rs b/tests/ui/annotate-snippet/multispan.rs index b7cf22eebcbff..c2054f62d24c5 100644 --- a/tests/ui/annotate-snippet/multispan.rs +++ b/tests/ui/annotate-snippet/multispan.rs @@ -1,5 +1,4 @@ //@ proc-macro: multispan.rs -//@ error-pattern:hello to you, too! //@ compile-flags: --error-format human-annotate-rs -Z unstable-options #![feature(proc_macro_hygiene)] @@ -27,3 +26,5 @@ fn main() { hello!(whoah. hi di hi di ho); hello!(hi good hi and good bye); } + +//~? RAW hello to you, too! diff --git a/tests/ui/annotate-snippet/multispan.stderr b/tests/ui/annotate-snippet/multispan.stderr index 833b67730325e..baed54c59a4e9 100644 --- a/tests/ui/annotate-snippet/multispan.stderr +++ b/tests/ui/annotate-snippet/multispan.stderr @@ -1,41 +1,41 @@ error: hello to you, too! - --> $DIR/multispan.rs:16:5 + --> $DIR/multispan.rs:15:5 | LL | hello!(hi); | ^^^^^^^^^^ | error: hello to you, too! - --> $DIR/multispan.rs:19:5 + --> $DIR/multispan.rs:18:5 | LL | hello!(hi hi); | ^^^^^^^^^^^^^ | error: hello to you, too! - --> $DIR/multispan.rs:22:5 + --> $DIR/multispan.rs:21:5 | LL | hello!(hi hi hi); | ^^^^^^^^^^^^^^^^ | error: hello to you, too! - --> $DIR/multispan.rs:25:5 + --> $DIR/multispan.rs:24:5 | LL | hello!(hi hey hi yo hi beep beep hi hi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | error: hello to you, too! - --> $DIR/multispan.rs:26:5 + --> $DIR/multispan.rs:25:5 | LL | hello!(hi there, hi how are you? hi... hi.); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | error: hello to you, too! - --> $DIR/multispan.rs:27:5 + --> $DIR/multispan.rs:26:5 | LL | hello!(whoah. hi di hi di ho); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | error: hello to you, too! - --> $DIR/multispan.rs:28:5 + --> $DIR/multispan.rs:27:5 | LL | hello!(hi good hi and good bye); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/anon-params/anon-params-denied-2018.rs b/tests/ui/anon-params/anon-params-denied-2018.rs index 3602b401f857b..d9d4363a68166 100644 --- a/tests/ui/anon-params/anon-params-denied-2018.rs +++ b/tests/ui/anon-params/anon-params-denied-2018.rs @@ -3,7 +3,7 @@ //@ edition:2018 trait T { - fn foo(i32); //~ expected one of `:`, `@`, or `|`, found `)` + fn foo(i32); //~ ERROR expected one of `:`, `@`, or `|`, found `)` // Also checks with `&` fn foo_with_ref(&mut i32); diff --git a/tests/ui/argument-suggestions/exotic-calls.rs b/tests/ui/argument-suggestions/exotic-calls.rs index 569a39a2b450d..765b4bc536c3a 100644 --- a/tests/ui/argument-suggestions/exotic-calls.rs +++ b/tests/ui/argument-suggestions/exotic-calls.rs @@ -1,8 +1,18 @@ +//! Checks variations of E0057, which is the incorrect number of agruments passed into a closure + +//@ check-fail + fn foo(t: T) { t(1i32); //~^ ERROR function takes 0 arguments but 1 argument was supplied } +/// Regression test for +fn foo2(f: T) { + |t| f(t); + //~^ ERROR function takes 0 arguments but 1 argument was supplied +} + fn bar(t: impl Fn()) { t(1i32); //~^ ERROR function takes 0 arguments but 1 argument was supplied diff --git a/tests/ui/argument-suggestions/exotic-calls.stderr b/tests/ui/argument-suggestions/exotic-calls.stderr index aca3b8a343343..e78871c19cb97 100644 --- a/tests/ui/argument-suggestions/exotic-calls.stderr +++ b/tests/ui/argument-suggestions/exotic-calls.stderr @@ -1,11 +1,11 @@ error[E0057]: this function takes 0 arguments but 1 argument was supplied - --> $DIR/exotic-calls.rs:2:5 + --> $DIR/exotic-calls.rs:6:5 | LL | t(1i32); | ^ ---- unexpected argument of type `i32` | note: callable defined here - --> $DIR/exotic-calls.rs:1:11 + --> $DIR/exotic-calls.rs:5:11 | LL | fn foo(t: T) { | ^^^^ @@ -16,13 +16,30 @@ LL + t(); | error[E0057]: this function takes 0 arguments but 1 argument was supplied - --> $DIR/exotic-calls.rs:7:5 + --> $DIR/exotic-calls.rs:12:9 + | +LL | |t| f(t); + | ^ - unexpected argument + | +note: callable defined here + --> $DIR/exotic-calls.rs:11:12 + | +LL | fn foo2(f: T) { + | ^^^^ +help: remove the extra argument + | +LL - |t| f(t); +LL + |t| f(); + | + +error[E0057]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/exotic-calls.rs:17:5 | LL | t(1i32); | ^ ---- unexpected argument of type `i32` | note: type parameter defined here - --> $DIR/exotic-calls.rs:6:11 + --> $DIR/exotic-calls.rs:16:11 | LL | fn bar(t: impl Fn()) { | ^^^^^^^^^ @@ -33,13 +50,13 @@ LL + t(); | error[E0057]: this function takes 0 arguments but 1 argument was supplied - --> $DIR/exotic-calls.rs:16:5 + --> $DIR/exotic-calls.rs:26:5 | LL | baz()(1i32) | ^^^^^ ---- unexpected argument of type `i32` | note: opaque type defined here - --> $DIR/exotic-calls.rs:11:13 + --> $DIR/exotic-calls.rs:21:13 | LL | fn baz() -> impl Fn() { | ^^^^^^^^^ @@ -50,13 +67,13 @@ LL + baz()() | error[E0057]: this function takes 0 arguments but 1 argument was supplied - --> $DIR/exotic-calls.rs:22:5 + --> $DIR/exotic-calls.rs:32:5 | LL | x(1i32); | ^ ---- unexpected argument of type `i32` | note: closure defined here - --> $DIR/exotic-calls.rs:21:13 + --> $DIR/exotic-calls.rs:31:13 | LL | let x = || {}; | ^^ @@ -66,6 +83,6 @@ LL - x(1i32); LL + x(); | -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0057`. diff --git a/tests/ui/argument-suggestions/issue-100478.rs b/tests/ui/argument-suggestions/issue-100478.rs index fb50fa115376b..b0a9703112e31 100644 --- a/tests/ui/argument-suggestions/issue-100478.rs +++ b/tests/ui/argument-suggestions/issue-100478.rs @@ -45,7 +45,7 @@ fn main() { let p8 = Arc::default(); foo( - //~^ 47:5: 47:8: this function takes 8 arguments but 7 arguments were supplied [E0061] + //~^ ERROR this function takes 8 arguments but 7 arguments were supplied [E0061] p1, //p2, p3, p4, p5, p6, p7, p8, ); diff --git a/tests/ui/argument-suggestions/issue-101097.rs b/tests/ui/argument-suggestions/issue-101097.rs index 25f7f58379923..9b1565fef6ff3 100644 --- a/tests/ui/argument-suggestions/issue-101097.rs +++ b/tests/ui/argument-suggestions/issue-101097.rs @@ -15,7 +15,7 @@ fn f( fn main() { f(C, A, A, A, B, B, C); //~ ERROR function takes 6 arguments but 7 arguments were supplied [E0061] f(C, C, A, A, B, B); //~ ERROR arguments to this function are incorrect [E0308] - f(A, A, D, D, B, B); //~ arguments to this function are incorrect [E0308] - f(C, C, B, B, A, A); //~ arguments to this function are incorrect [E0308] - f(C, C, A, B, A, A); //~ arguments to this function are incorrect [E0308] + f(A, A, D, D, B, B); //~ ERROR arguments to this function are incorrect [E0308] + f(C, C, B, B, A, A); //~ ERROR arguments to this function are incorrect [E0308] + f(C, C, A, B, A, A); //~ ERROR arguments to this function are incorrect [E0308] } diff --git a/tests/ui/array-slice-vec/array-not-vector.rs b/tests/ui/array-slice-vec/array-not-vector.rs index d8b5b10d59148..7345f7219186a 100644 --- a/tests/ui/array-slice-vec/array-not-vector.rs +++ b/tests/ui/array-slice-vec/array-not-vector.rs @@ -1,12 +1,14 @@ +//@ dont-require-annotations: NOTE + fn main() { let _x: i32 = [1, 2, 3]; //~^ ERROR mismatched types - //~| expected `i32`, found `[{integer}; 3]` + //~| NOTE expected `i32`, found `[{integer}; 3]` let x: &[i32] = &[1, 2, 3]; let _y: &i32 = x; //~^ ERROR mismatched types - //~| expected reference `&i32` - //~| found reference `&[i32]` - //~| expected `&i32`, found `&[i32]` + //~| NOTE expected reference `&i32` + //~| NOTE found reference `&[i32]` + //~| NOTE expected `&i32`, found `&[i32]` } diff --git a/tests/ui/array-slice-vec/array-not-vector.stderr b/tests/ui/array-slice-vec/array-not-vector.stderr index f20d99524dccd..686c5dfe7877b 100644 --- a/tests/ui/array-slice-vec/array-not-vector.stderr +++ b/tests/ui/array-slice-vec/array-not-vector.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/array-not-vector.rs:2:19 + --> $DIR/array-not-vector.rs:4:19 | LL | let _x: i32 = [1, 2, 3]; | --- ^^^^^^^^^ expected `i32`, found `[{integer}; 3]` @@ -7,7 +7,7 @@ LL | let _x: i32 = [1, 2, 3]; | expected due to this error[E0308]: mismatched types - --> $DIR/array-not-vector.rs:7:20 + --> $DIR/array-not-vector.rs:9:20 | LL | let _y: &i32 = x; | ---- ^ expected `&i32`, found `&[i32]` diff --git a/tests/ui/array-slice-vec/array_const_index-0.rs b/tests/ui/array-slice-vec/array_const_index-0.rs index 96755802ec7f1..f4fe89a50c252 100644 --- a/tests/ui/array-slice-vec/array_const_index-0.rs +++ b/tests/ui/array-slice-vec/array_const_index-0.rs @@ -1,6 +1,6 @@ const A: &'static [i32] = &[]; const B: i32 = (&A)[1]; -//~^ index out of bounds: the length is 0 but the index is 1 +//~^ NOTE index out of bounds: the length is 0 but the index is 1 //~| ERROR evaluation of constant value failed fn main() { diff --git a/tests/ui/array-slice-vec/array_const_index-1.rs b/tests/ui/array-slice-vec/array_const_index-1.rs index 625bf06a745e9..0d4de137a6e0d 100644 --- a/tests/ui/array-slice-vec/array_const_index-1.rs +++ b/tests/ui/array-slice-vec/array_const_index-1.rs @@ -1,6 +1,6 @@ const A: [i32; 0] = []; const B: i32 = A[1]; -//~^ index out of bounds: the length is 0 but the index is 1 +//~^ NOTE index out of bounds: the length is 0 but the index is 1 //~| ERROR evaluation of constant value failed fn main() { diff --git a/tests/ui/array-slice-vec/slice-mut.rs b/tests/ui/array-slice-vec/slice-mut.rs index e9989f0f48131..baa05c36a9db0 100644 --- a/tests/ui/array-slice-vec/slice-mut.rs +++ b/tests/ui/array-slice-vec/slice-mut.rs @@ -6,7 +6,8 @@ fn main() { let y: &mut[_] = &x[2..4]; //~^ ERROR mismatched types - //~| expected mutable reference `&mut [_]` - //~| found reference `&[isize]` - //~| types differ in mutability + //~| NOTE expected mutable reference `&mut [_]` + //~| NOTE found reference `&[isize]` + //~| NOTE types differ in mutability + //~| NOTE expected due to this } diff --git a/tests/ui/asm/aarch64/parse-error.rs b/tests/ui/asm/aarch64/parse-error.rs index aa731c35dda88..622f99aa1b1e8 100644 --- a/tests/ui/asm/aarch64/parse-error.rs +++ b/tests/ui/asm/aarch64/parse-error.rs @@ -1,57 +1,11 @@ //@ only-aarch64 -use std::arch::{asm, global_asm}; +use std::arch::asm; fn main() { let mut foo = 0; let mut bar = 0; unsafe { - asm!(); - //~^ ERROR requires at least a template string argument - asm!(foo); - //~^ ERROR asm template must be a string literal - asm!("{}" foo); - //~^ ERROR expected token: `,` - asm!("{}", foo); - //~^ ERROR expected operand, clobber_abi, options, or additional template string - asm!("{}", in foo); - //~^ ERROR expected `(`, found `foo` - asm!("{}", in(reg foo)); - //~^ ERROR expected `)`, found `foo` - asm!("{}", in(reg)); - //~^ ERROR expected expression, found end of macro arguments - asm!("{}", inout(=) foo => bar); - //~^ ERROR expected register class or explicit register - asm!("{}", inout(reg) foo =>); - //~^ ERROR expected expression, found end of macro arguments - asm!("{}", in(reg) foo => bar); - //~^ ERROR expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` - asm!("{}", sym foo + bar); - //~^ ERROR expected a path for argument to `sym` - asm!("", options(foo)); - //~^ ERROR expected one of - asm!("", options(nomem foo)); - //~^ ERROR expected one of - asm!("", options(nomem, foo)); - //~^ ERROR expected one of - asm!("{}", options(), const foo); - //~^ ERROR attempt to use a non-constant value in a constant - asm!("", clobber_abi(foo)); - //~^ ERROR expected string literal - asm!("", clobber_abi("C" foo)); - //~^ ERROR expected one of `)` or `,`, found `foo` - asm!("", clobber_abi("C", foo)); - //~^ ERROR expected string literal - asm!("{}", clobber_abi("C"), const foo); - //~^ ERROR attempt to use a non-constant value in a constant - asm!("", options(), clobber_abi("C")); - asm!("{}", options(), clobber_abi("C"), const foo); - //~^ ERROR attempt to use a non-constant value in a constant - asm!("{a}", a = const foo, a = const bar); - //~^ ERROR duplicate argument named `a` - //~^^ ERROR argument never used - //~^^^ ERROR attempt to use a non-constant value in a constant - //~^^^^ ERROR attempt to use a non-constant value in a constant asm!("", a = in("x0") foo); //~^ ERROR explicit register arguments cannot have names asm!("{a}", in("x0") foo, a = const bar); @@ -61,66 +15,5 @@ fn main() { asm!("{1}", in("x0") foo, const bar); //~^ ERROR positional arguments cannot follow named arguments or explicit register arguments //~^^ ERROR attempt to use a non-constant value in a constant - asm!("", options(), ""); - //~^ ERROR expected one of - asm!("{}", in(reg) foo, "{}", out(reg) foo); - //~^ ERROR expected one of - asm!(format!("{{{}}}", 0), in(reg) foo); - //~^ ERROR asm template must be a string literal - asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); - //~^ ERROR asm template must be a string literal - asm!("{}", in(reg) _); - //~^ ERROR _ cannot be used for input operands - asm!("{}", inout(reg) _); - //~^ ERROR _ cannot be used for input operands - asm!("{}", inlateout(reg) _); - //~^ ERROR _ cannot be used for input operands } } - -const FOO: i32 = 1; -const BAR: i32 = 2; -global_asm!(); -//~^ ERROR requires at least a template string argument -global_asm!(FOO); -//~^ ERROR asm template must be a string literal -global_asm!("{}" FOO); -//~^ ERROR expected token: `,` -global_asm!("{}", FOO); -//~^ ERROR expected operand, options, or additional template string -global_asm!("{}", const); -//~^ ERROR expected expression, found end of macro arguments -global_asm!("{}", const(reg) FOO); -//~^ ERROR expected one of -global_asm!("", options(FOO)); -//~^ ERROR expected one of -global_asm!("", options(nomem FOO)); -//~^ ERROR expected one of -//~| ERROR the `nomem` option cannot be used with `global_asm!` -global_asm!("", options(nomem, FOO)); -//~^ ERROR expected one of -//~| ERROR the `nomem` option cannot be used with `global_asm!` -global_asm!("{}", options(), const FOO); -global_asm!("", clobber_abi(FOO)); -//~^ ERROR expected string literal -global_asm!("", clobber_abi("C" FOO)); -//~^ ERROR expected one of `)` or `,`, found `FOO` -global_asm!("", clobber_abi("C", FOO)); -//~^ ERROR expected string literal -global_asm!("{}", clobber_abi("C"), const FOO); -//~^ ERROR `clobber_abi` cannot be used with `global_asm!` -global_asm!("", options(), clobber_abi("C")); -//~^ ERROR `clobber_abi` cannot be used with `global_asm!` -global_asm!("{}", options(), clobber_abi("C"), const FOO); -//~^ ERROR `clobber_abi` cannot be used with `global_asm!` -global_asm!("{a}", a = const FOO, a = const BAR); -//~^ ERROR duplicate argument named `a` -//~^^ ERROR argument never used -global_asm!("", options(), ""); -//~^ ERROR expected one of -global_asm!("{}", const FOO, "{}", const FOO); -//~^ ERROR expected one of -global_asm!(format!("{{{}}}", 0), const FOO); -//~^ ERROR asm template must be a string literal -global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); -//~^ ERROR asm template must be a string literal diff --git a/tests/ui/asm/aarch64/parse-error.stderr b/tests/ui/asm/aarch64/parse-error.stderr index b5e1169e5f6b4..ca21311f87f96 100644 --- a/tests/ui/asm/aarch64/parse-error.stderr +++ b/tests/ui/asm/aarch64/parse-error.stderr @@ -1,389 +1,19 @@ -error: requires at least a template string argument - --> $DIR/parse-error.rs:9:9 - | -LL | asm!(); - | ^^^^^^ - -error: asm template must be a string literal - --> $DIR/parse-error.rs:11:14 - | -LL | asm!(foo); - | ^^^ - -error: expected token: `,` - --> $DIR/parse-error.rs:13:19 - | -LL | asm!("{}" foo); - | ^^^ expected `,` - -error: expected operand, clobber_abi, options, or additional template string - --> $DIR/parse-error.rs:15:20 - | -LL | asm!("{}", foo); - | ^^^ expected operand, clobber_abi, options, or additional template string - -error: expected `(`, found `foo` - --> $DIR/parse-error.rs:17:23 - | -LL | asm!("{}", in foo); - | ^^^ expected `(` - -error: expected `)`, found `foo` - --> $DIR/parse-error.rs:19:27 - | -LL | asm!("{}", in(reg foo)); - | ^^^ expected `)` - -error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:21:27 - | -LL | asm!("{}", in(reg)); - | ^ expected expression - -error: expected register class or explicit register - --> $DIR/parse-error.rs:23:26 - | -LL | asm!("{}", inout(=) foo => bar); - | ^ - -error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:25:37 - | -LL | asm!("{}", inout(reg) foo =>); - | ^ expected expression - -error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` - --> $DIR/parse-error.rs:27:32 - | -LL | asm!("{}", in(reg) foo => bar); - | ^^ expected one of 7 possible tokens - -error: expected a path for argument to `sym` - --> $DIR/parse-error.rs:29:24 - | -LL | asm!("{}", sym foo + bar); - | ^^^^^^^^^ - -error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo` - --> $DIR/parse-error.rs:31:26 - | -LL | asm!("", options(foo)); - | ^^^ expected one of 10 possible tokens - -error: expected one of `)` or `,`, found `foo` - --> $DIR/parse-error.rs:33:32 - | -LL | asm!("", options(nomem foo)); - | ^^^ expected one of `)` or `,` - -error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo` - --> $DIR/parse-error.rs:35:33 - | -LL | asm!("", options(nomem, foo)); - | ^^^ expected one of 10 possible tokens - -error: expected string literal - --> $DIR/parse-error.rs:39:30 - | -LL | asm!("", clobber_abi(foo)); - | ^^^ not a string literal - -error: expected one of `)` or `,`, found `foo` - --> $DIR/parse-error.rs:41:34 - | -LL | asm!("", clobber_abi("C" foo)); - | ^^^ expected one of `)` or `,` - -error: expected string literal - --> $DIR/parse-error.rs:43:35 - | -LL | asm!("", clobber_abi("C", foo)); - | ^^^ not a string literal - -error: duplicate argument named `a` - --> $DIR/parse-error.rs:50:36 - | -LL | asm!("{a}", a = const foo, a = const bar); - | ------------- ^^^^^^^^^^^^^ duplicate argument - | | - | previously here - -error: argument never used - --> $DIR/parse-error.rs:50:36 - | -LL | asm!("{a}", a = const foo, a = const bar); - | ^^^^^^^^^^^^^ argument never used - | - = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` - error: explicit register arguments cannot have names - --> $DIR/parse-error.rs:55:18 + --> $DIR/parse-error.rs:9:18 | LL | asm!("", a = in("x0") foo); | ^^^^^^^^^^^^^^^^ error: positional arguments cannot follow named arguments or explicit register arguments - --> $DIR/parse-error.rs:61:35 + --> $DIR/parse-error.rs:15:35 | LL | asm!("{1}", in("x0") foo, const bar); | ------------ ^^^^^^^^^ positional argument | | | explicit register argument -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""` - --> $DIR/parse-error.rs:64:29 - | -LL | asm!("", options(), ""); - | ^^ expected one of 10 possible tokens - -error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:66:33 - | -LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); - | ^^^^ expected one of 10 possible tokens - -error: asm template must be a string literal - --> $DIR/parse-error.rs:68:14 - | -LL | asm!(format!("{{{}}}", 0), in(reg) foo); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: asm template must be a string literal - --> $DIR/parse-error.rs:70:21 - | -LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: _ cannot be used for input operands - --> $DIR/parse-error.rs:72:28 - | -LL | asm!("{}", in(reg) _); - | ^ - -error: _ cannot be used for input operands - --> $DIR/parse-error.rs:74:31 - | -LL | asm!("{}", inout(reg) _); - | ^ - -error: _ cannot be used for input operands - --> $DIR/parse-error.rs:76:35 - | -LL | asm!("{}", inlateout(reg) _); - | ^ - -error: requires at least a template string argument - --> $DIR/parse-error.rs:83:1 - | -LL | global_asm!(); - | ^^^^^^^^^^^^^ - -error: asm template must be a string literal - --> $DIR/parse-error.rs:85:13 - | -LL | global_asm!(FOO); - | ^^^ - -error: expected token: `,` - --> $DIR/parse-error.rs:87:18 - | -LL | global_asm!("{}" FOO); - | ^^^ expected `,` - -error: expected operand, options, or additional template string - --> $DIR/parse-error.rs:89:19 - | -LL | global_asm!("{}", FOO); - | ^^^ expected operand, options, or additional template string - -error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:91:24 - | -LL | global_asm!("{}", const); - | ^ expected expression - -error: expected one of `,`, `.`, `?`, or an operator, found `FOO` - --> $DIR/parse-error.rs:93:30 - | -LL | global_asm!("{}", const(reg) FOO); - | ^^^ expected one of `,`, `.`, `?`, or an operator - -error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:95:25 - | -LL | global_asm!("", options(FOO)); - | ^^^ expected one of `)`, `att_syntax`, or `raw` - -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:97:25 - | -LL | global_asm!("", options(nomem FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - -error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:97:31 - | -LL | global_asm!("", options(nomem FOO)); - | ^^^ expected one of `)` or `,` - -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:100:25 - | -LL | global_asm!("", options(nomem, FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - -error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:100:32 - | -LL | global_asm!("", options(nomem, FOO)); - | ^^^ expected one of `)`, `att_syntax`, or `raw` - -error: expected string literal - --> $DIR/parse-error.rs:104:29 - | -LL | global_asm!("", clobber_abi(FOO)); - | ^^^ not a string literal - -error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:106:33 - | -LL | global_asm!("", clobber_abi("C" FOO)); - | ^^^ expected one of `)` or `,` - -error: expected string literal - --> $DIR/parse-error.rs:108:34 - | -LL | global_asm!("", clobber_abi("C", FOO)); - | ^^^ not a string literal - -error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:110:19 - | -LL | global_asm!("{}", clobber_abi("C"), const FOO); - | ^^^^^^^^^^^^^^^^ - -error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:112:28 - | -LL | global_asm!("", options(), clobber_abi("C")); - | ^^^^^^^^^^^^^^^^ - -error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:114:30 - | -LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); - | ^^^^^^^^^^^^^^^^ - -error: duplicate argument named `a` - --> $DIR/parse-error.rs:116:35 - | -LL | global_asm!("{a}", a = const FOO, a = const BAR); - | ------------- ^^^^^^^^^^^^^ duplicate argument - | | - | previously here - -error: argument never used - --> $DIR/parse-error.rs:116:35 - | -LL | global_asm!("{a}", a = const FOO, a = const BAR); - | ^^^^^^^^^^^^^ argument never used - | - = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` - -error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:119:28 - | -LL | global_asm!("", options(), ""); - | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` - -error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:121:30 - | -LL | global_asm!("{}", const FOO, "{}", const FOO); - | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` - -error: asm template must be a string literal - --> $DIR/parse-error.rs:123:13 - | -LL | global_asm!(format!("{{{}}}", 0), const FOO); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: asm template must be a string literal - --> $DIR/parse-error.rs:125:20 - | -LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:37:37 - | -LL | asm!("{}", options(), const foo); - | ^^^ non-constant value - | -help: consider using `const` instead of `let` - | -LL - let mut foo = 0; -LL + const foo: /* Type */ = 0; - | - -error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:45:44 - | -LL | asm!("{}", clobber_abi("C"), const foo); - | ^^^ non-constant value - | -help: consider using `const` instead of `let` - | -LL - let mut foo = 0; -LL + const foo: /* Type */ = 0; - | - -error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:48:55 - | -LL | asm!("{}", options(), clobber_abi("C"), const foo); - | ^^^ non-constant value - | -help: consider using `const` instead of `let` - | -LL - let mut foo = 0; -LL + const foo: /* Type */ = 0; - | - -error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:50:31 - | -LL | asm!("{a}", a = const foo, a = const bar); - | ^^^ non-constant value - | -help: consider using `const` instead of `let` - | -LL - let mut foo = 0; -LL + const foo: /* Type */ = 0; - | - -error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:50:46 - | -LL | asm!("{a}", a = const foo, a = const bar); - | ^^^ non-constant value - | -help: consider using `const` instead of `let` - | -LL - let mut bar = 0; -LL + const bar: /* Type */ = 0; - | - error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:57:45 + --> $DIR/parse-error.rs:11:45 | LL | asm!("{a}", in("x0") foo, a = const bar); | ^^^ non-constant value @@ -395,7 +25,7 @@ LL + const bar: /* Type */ = 0; | error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:59:45 + --> $DIR/parse-error.rs:13:45 | LL | asm!("{a}", in("x0") foo, a = const bar); | ^^^ non-constant value @@ -407,7 +37,7 @@ LL + const bar: /* Type */ = 0; | error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:61:41 + --> $DIR/parse-error.rs:15:41 | LL | asm!("{1}", in("x0") foo, const bar); | ^^^ non-constant value @@ -418,6 +48,6 @@ LL - let mut bar = 0; LL + const bar: /* Type */ = 0; | -error: aborting due to 59 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/asm/aarch64/srcloc.new.stderr b/tests/ui/asm/aarch64/srcloc.new.stderr deleted file mode 100644 index b92a07e5fb131..0000000000000 --- a/tests/ui/asm/aarch64/srcloc.new.stderr +++ /dev/null @@ -1,320 +0,0 @@ -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:15:15 - | -LL | asm!("invalid_instruction"); - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:19:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :2:13 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:24:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :2:13 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:30:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :3:13 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:37:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :3:13 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:42:14 - | -LL | asm!(concat!("invalid", "_", "instruction")); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:46:14 - | -LL | "invalid_instruction", - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:52:14 - | -LL | "invalid_instruction", - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:59:14 - | -LL | "invalid_instruction", - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:66:13 - | -LL | concat!("invalid", "_", "instruction"), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:73:13 - | -LL | concat!("invalid", "_", "instruction"), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:80:14 - | -LL | "invalid_instruction1", - | ^^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction1 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:81:14 - | -LL | "invalid_instruction2", - | ^^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction2 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:87:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction1", "\n", -LL | | "invalid", "_", "instruction2", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction1 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:87:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction1", "\n", -LL | | "invalid", "_", "instruction2", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction2 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:96:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction1", "\n", -LL | | "invalid", "_", "instruction2", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction1 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:96:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction1", "\n", -LL | | "invalid", "_", "instruction2", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction2 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:100:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction3", "\n", -LL | | "invalid", "_", "instruction4", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction3 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:100:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction3", "\n", -LL | | "invalid", "_", "instruction4", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :4:1 - | -LL | invalid_instruction4 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:111:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction1", "\n", -LL | | "invalid", "_", "instruction2", "\n", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction1 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:111:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction1", "\n", -LL | | "invalid", "_", "instruction2", "\n", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction2 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:115:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction3", "\n", -LL | | "invalid", "_", "instruction4", "\n", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :4:1 - | -LL | invalid_instruction3 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:115:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction3", "\n", -LL | | "invalid", "_", "instruction4", "\n", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :5:1 - | -LL | invalid_instruction4 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:128:14 - | -LL | "invalid_instruction" - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :4:1 - | -LL | invalid_instruction - | ^ - -error: aborting due to 24 previous errors - diff --git a/tests/ui/asm/aarch64/srcloc.old.stderr b/tests/ui/asm/aarch64/srcloc.old.stderr deleted file mode 100644 index 2a15e48f0256d..0000000000000 --- a/tests/ui/asm/aarch64/srcloc.old.stderr +++ /dev/null @@ -1,290 +0,0 @@ -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:15:15 - | -LL | asm!("invalid_instruction"); - | ^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:19:13 - | -LL | invalid_instruction - | ^ - | -note: instantiated into assembly here - --> :2:13 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:24:13 - | -LL | invalid_instruction - | ^ - | -note: instantiated into assembly here - --> :2:13 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:30:13 - | -LL | invalid_instruction - | ^ - | -note: instantiated into assembly here - --> :3:13 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:37:13 - | -LL | invalid_instruction - | ^ - | -note: instantiated into assembly here - --> :3:13 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:42:14 - | -LL | asm!(concat!("invalid", "_", "instruction")); - | ^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:46:14 - | -LL | "invalid_instruction", - | ^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:52:14 - | -LL | "invalid_instruction", - | ^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:59:14 - | -LL | "invalid_instruction", - | ^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:66:13 - | -LL | concat!("invalid", "_", "instruction"), - | ^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:73:13 - | -LL | concat!("invalid", "_", "instruction"), - | ^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:80:14 - | -LL | "invalid_instruction1", - | ^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction1 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:81:14 - | -LL | "invalid_instruction2", - | ^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction2 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:87:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction1 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:87:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction2 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:96:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction1 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:96:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction2 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:100:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction3 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:100:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :4:1 - | -LL | invalid_instruction4 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:111:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | invalid_instruction1 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:111:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :2:1 - | -LL | invalid_instruction2 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:115:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :4:1 - | -LL | invalid_instruction3 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:115:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :5:1 - | -LL | invalid_instruction4 - | ^ - -error: unrecognized instruction mnemonic - --> $DIR/srcloc.rs:128:14 - | -LL | "invalid_instruction" - | ^ - | -note: instantiated into assembly here - --> :4:1 - | -LL | invalid_instruction - | ^ - -error: aborting due to 24 previous errors - diff --git a/tests/ui/asm/aarch64/srcloc.rs b/tests/ui/asm/aarch64/srcloc.rs index 9b92dfef056d0..c635fa6ba700a 100644 --- a/tests/ui/asm/aarch64/srcloc.rs +++ b/tests/ui/asm/aarch64/srcloc.rs @@ -1,10 +1,7 @@ -//@ revisions: old new //@ only-aarch64 //@ build-fail //@ needs-asm-support //@ compile-flags: -Ccodegen-units=1 -//@[old] ignore-llvm-version: 19 - 99 -//@[new] min-llvm-version: 19 use std::arch::asm; diff --git a/tests/ui/asm/aarch64/srcloc.stderr b/tests/ui/asm/aarch64/srcloc.stderr new file mode 100644 index 0000000000000..b47f19bea6148 --- /dev/null +++ b/tests/ui/asm/aarch64/srcloc.stderr @@ -0,0 +1,320 @@ +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:12:15 + | +LL | asm!("invalid_instruction"); + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:2 + | +LL | invalid_instruction + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:16:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :2:13 + | +LL | invalid_instruction + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:21:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :2:13 + | +LL | invalid_instruction + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:27:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :3:13 + | +LL | invalid_instruction + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:34:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :3:13 + | +LL | invalid_instruction + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:39:14 + | +LL | asm!(concat!("invalid", "_", "instruction")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:2 + | +LL | invalid_instruction + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:43:14 + | +LL | "invalid_instruction", + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:2 + | +LL | invalid_instruction + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:49:14 + | +LL | "invalid_instruction", + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :2:1 + | +LL | invalid_instruction + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:56:14 + | +LL | "invalid_instruction", + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:63:13 + | +LL | concat!("invalid", "_", "instruction"), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :2:1 + | +LL | invalid_instruction + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:70:13 + | +LL | concat!("invalid", "_", "instruction"), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :2:1 + | +LL | invalid_instruction + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:77:14 + | +LL | "invalid_instruction1", + | ^^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:2 + | +LL | invalid_instruction1 + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:78:14 + | +LL | "invalid_instruction2", + | ^^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :2:1 + | +LL | invalid_instruction2 + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:84:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction1", "\n", +LL | | "invalid", "_", "instruction2", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :1:2 + | +LL | invalid_instruction1 + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:84:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction1", "\n", +LL | | "invalid", "_", "instruction2", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :2:1 + | +LL | invalid_instruction2 + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:93:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction1", "\n", +LL | | "invalid", "_", "instruction2", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :1:2 + | +LL | invalid_instruction1 + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:93:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction1", "\n", +LL | | "invalid", "_", "instruction2", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :2:1 + | +LL | invalid_instruction2 + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:97:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction3", "\n", +LL | | "invalid", "_", "instruction4", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction3 + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:97:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction3", "\n", +LL | | "invalid", "_", "instruction4", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :4:1 + | +LL | invalid_instruction4 + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:108:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction1", "\n", +LL | | "invalid", "_", "instruction2", "\n", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :1:2 + | +LL | invalid_instruction1 + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:108:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction1", "\n", +LL | | "invalid", "_", "instruction2", "\n", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :2:1 + | +LL | invalid_instruction2 + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:112:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction3", "\n", +LL | | "invalid", "_", "instruction4", "\n", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :4:1 + | +LL | invalid_instruction3 + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:112:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction3", "\n", +LL | | "invalid", "_", "instruction4", "\n", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :5:1 + | +LL | invalid_instruction4 + | ^ + +error: unrecognized instruction mnemonic + --> $DIR/srcloc.rs:125:14 + | +LL | "invalid_instruction" + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :4:1 + | +LL | invalid_instruction + | ^ + +error: aborting due to 24 previous errors + diff --git a/tests/ui/asm/global-asm-isnt-really-a-mir-body.rs b/tests/ui/asm/global-asm-isnt-really-a-mir-body.rs new file mode 100644 index 0000000000000..b7636d116ec6c --- /dev/null +++ b/tests/ui/asm/global-asm-isnt-really-a-mir-body.rs @@ -0,0 +1,26 @@ +//@ revisions: emit_mir instrument cfi + +// Make sure we don't try to emit MIR for it. +//@[emit_mir] compile-flags: --emit=mir + +// Make sure we don't try to instrument it. +//@[instrument] compile-flags: -Cinstrument-coverage -Zno-profiler-runtime +//@[instrument] only-linux + +// Make sure we don't try to CFI encode it. +//@[cfi] compile-flags: -Zsanitizer=cfi -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Clink-dead-code=true +//@[cfi] needs-sanitizer-cfi +//@[cfi] no-prefer-dynamic +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@[cfi] only-linux + +//@ build-pass +//@ needs-asm-support + +use std::arch::global_asm; + +fn foo() {} + +global_asm!("/* {} */", sym foo); + +fn main() {} diff --git a/tests/ui/asm/global-asm-mono-sym-fn.rs b/tests/ui/asm/global-asm-mono-sym-fn.rs new file mode 100644 index 0000000000000..e584a98badb0d --- /dev/null +++ b/tests/ui/asm/global-asm-mono-sym-fn.rs @@ -0,0 +1,27 @@ +// Test that we're properly monomorphizing sym args in global asm blocks +// that point to associated items. + +//@ edition: 2021 +//@ needs-asm-support +//@ only-x86_64-unknown-linux-gnu +//@ build-pass + +#![no_main] + +use std::arch::global_asm; + +fn foo() { + loop {} +} + +trait Foo { + fn bar(); +} + +impl Foo for i32 { + fn bar() { + loop {} + } +} + +global_asm!(".global main", "main:", "call {}", sym ::bar); diff --git a/tests/ui/asm/global-asm-with-error.rs b/tests/ui/asm/global-asm-with-error.rs new file mode 100644 index 0000000000000..c2253e3cb8747 --- /dev/null +++ b/tests/ui/asm/global-asm-with-error.rs @@ -0,0 +1,11 @@ +// Ensure that we don't ICE when constructing the fake MIR body for a global +// asm when the body has errors. See #137470. + +//@ needs-asm-support + +use std::arch::global_asm; + +global_asm!("/* {} */", sym a); +//~^ ERROR cannot find value `a` in this scope + +fn main() {} diff --git a/tests/ui/asm/global-asm-with-error.stderr b/tests/ui/asm/global-asm-with-error.stderr new file mode 100644 index 0000000000000..3b76bfd19485c --- /dev/null +++ b/tests/ui/asm/global-asm-with-error.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find value `a` in this scope + --> $DIR/global-asm-with-error.rs:8:29 + | +LL | global_asm!("/* {} */", sym a); + | ^ not found in this scope + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/asm/ice-bad-err-span-in-template-129503.stderr b/tests/ui/asm/ice-bad-err-span-in-template-129503.stderr index ffa9362ed9538..066959a052d99 100644 --- a/tests/ui/asm/ice-bad-err-span-in-template-129503.stderr +++ b/tests/ui/asm/ice-bad-err-span-in-template-129503.stderr @@ -5,7 +5,6 @@ LL | asm!(concat!(r#"lJ𐏿Æ�.𐏿�"#, "r} {}")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unmatched `}` in asm template string | = note: if you intended to print `}`, you can escape it using `}}` - = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) error: invalid asm template string: unmatched `}` found --> $DIR/ice-bad-err-span-in-template-129503.rs:18:10 @@ -14,7 +13,6 @@ LL | asm!(concat!("abc", "r} {}")); | ^^^^^^^^^^^^^^^^^^^^^^^ unmatched `}` in asm template string | = note: if you intended to print `}`, you can escape it using `}}` - = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) error: invalid asm template string: unmatched `}` found --> $DIR/ice-bad-err-span-in-template-129503.rs:24:19 diff --git a/tests/ui/asm/inline-syntax.arm.stderr b/tests/ui/asm/inline-syntax.arm.stderr index 4003a10f37452..5b4eb3cc1409c 100644 --- a/tests/ui/asm/inline-syntax.arm.stderr +++ b/tests/ui/asm/inline-syntax.arm.stderr @@ -15,7 +15,7 @@ LL | .intel_syntax noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:26:15 + --> $DIR/inline-syntax.rs:21:15 | LL | asm!(".intel_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | .intel_syntax noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:30:15 + --> $DIR/inline-syntax.rs:24:15 | LL | asm!(".intel_syntax aaa noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | .intel_syntax aaa noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:34:15 + --> $DIR/inline-syntax.rs:27:15 | LL | asm!(".att_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^ @@ -51,7 +51,7 @@ LL | .att_syntax noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:38:15 + --> $DIR/inline-syntax.rs:30:15 | LL | asm!(".att_syntax bbb noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | .att_syntax bbb noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:42:15 + --> $DIR/inline-syntax.rs:33:15 | LL | asm!(".intel_syntax noprefix; nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,7 +75,7 @@ LL | .intel_syntax noprefix; nop | ^ error: unknown directive - --> $DIR/inline-syntax.rs:49:13 + --> $DIR/inline-syntax.rs:39:13 | LL | .intel_syntax noprefix | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/inline-syntax.arm_llvm_18.stderr b/tests/ui/asm/inline-syntax.arm_llvm_18.stderr deleted file mode 100644 index a03861c78a3e6..0000000000000 --- a/tests/ui/asm/inline-syntax.arm_llvm_18.stderr +++ /dev/null @@ -1,90 +0,0 @@ -error: unknown directive - | -note: instantiated into assembly here - --> :1:1 - | -LL | .intel_syntax noprefix - | ^ - -error: unknown directive - | -note: instantiated into assembly here - --> :1:1 - | -LL | .intel_syntax noprefix - | ^ - -error: unknown directive - --> $DIR/inline-syntax.rs:26:15 - | -LL | asm!(".intel_syntax noprefix", "nop"); - | ^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | .intel_syntax noprefix - | ^ - -error: unknown directive - --> $DIR/inline-syntax.rs:30:15 - | -LL | asm!(".intel_syntax aaa noprefix", "nop"); - | ^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | .intel_syntax aaa noprefix - | ^ - -error: unknown directive - --> $DIR/inline-syntax.rs:34:15 - | -LL | asm!(".att_syntax noprefix", "nop"); - | ^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | .att_syntax noprefix - | ^ - -error: unknown directive - --> $DIR/inline-syntax.rs:38:15 - | -LL | asm!(".att_syntax bbb noprefix", "nop"); - | ^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | .att_syntax bbb noprefix - | ^ - -error: unknown directive - --> $DIR/inline-syntax.rs:42:15 - | -LL | asm!(".intel_syntax noprefix; nop"); - | ^ - | -note: instantiated into assembly here - --> :1:2 - | -LL | .intel_syntax noprefix; nop - | ^ - -error: unknown directive - --> $DIR/inline-syntax.rs:49:13 - | -LL | .intel_syntax noprefix - | ^ - | -note: instantiated into assembly here - --> :2:13 - | -LL | .intel_syntax noprefix - | ^ - -error: aborting due to 8 previous errors - diff --git a/tests/ui/asm/inline-syntax.rs b/tests/ui/asm/inline-syntax.rs index adbda369b6a76..5f2f4e357f9ce 100644 --- a/tests/ui/asm/inline-syntax.rs +++ b/tests/ui/asm/inline-syntax.rs @@ -1,17 +1,12 @@ //@ add-core-stubs -//@ revisions: x86_64 arm_llvm_18 arm +//@ revisions: x86_64 arm //@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu //@[x86_64] check-pass //@[x86_64] needs-llvm-components: x86 -//@[arm_llvm_18] compile-flags: --target armv7-unknown-linux-gnueabihf -//@[arm_llvm_18] build-fail -//@[arm_llvm_18] needs-llvm-components: arm -//@[arm_llvm_18] ignore-llvm-version: 19 - 99 // LLVM 19+ has full support for 64-bit cookies. //@[arm] compile-flags: --target armv7-unknown-linux-gnueabihf //@[arm] build-fail //@[arm] needs-llvm-components: arm -//@[arm] min-llvm-version: 19 //@ needs-asm-support #![feature(no_core)] @@ -26,23 +21,18 @@ pub fn main() { asm!(".intel_syntax noprefix", "nop"); //[x86_64]~^ WARN avoid using `.intel_syntax` //[arm]~^^ ERROR unknown directive - //[arm_llvm_18]~^^^ ERROR unknown directive asm!(".intel_syntax aaa noprefix", "nop"); //[x86_64]~^ WARN avoid using `.intel_syntax` //[arm]~^^ ERROR unknown directive - //[arm_llvm_18]~^^^ ERROR unknown directive asm!(".att_syntax noprefix", "nop"); //[x86_64]~^ WARN avoid using `.att_syntax` //[arm]~^^ ERROR unknown directive - //[arm_llvm_18]~^^^ ERROR unknown directive asm!(".att_syntax bbb noprefix", "nop"); //[x86_64]~^ WARN avoid using `.att_syntax` //[arm]~^^ ERROR unknown directive - //[arm_llvm_18]~^^^ ERROR unknown directive asm!(".intel_syntax noprefix; nop"); //[x86_64]~^ WARN avoid using `.intel_syntax` //[arm]~^^ ERROR unknown directive - //[arm_llvm_18]~^^^ ERROR unknown directive asm!( r" @@ -51,10 +41,12 @@ pub fn main() { ); //[x86_64]~^^^ WARN avoid using `.intel_syntax` //[arm]~^^^^ ERROR unknown directive - //[arm_llvm_18]~^^^^^ ERROR unknown directive } } global_asm!(".intel_syntax noprefix", "nop"); //[x86_64]~^ WARN avoid using `.intel_syntax` // Global assembly errors don't have line numbers, so no error on ARM. + +//[arm]~? ERROR unknown directive +//[arm]~? ERROR unknown directive diff --git a/tests/ui/asm/inline-syntax.x86_64.stderr b/tests/ui/asm/inline-syntax.x86_64.stderr index 369f7b66ae45a..2d8091c204411 100644 --- a/tests/ui/asm/inline-syntax.x86_64.stderr +++ b/tests/ui/asm/inline-syntax.x86_64.stderr @@ -1,5 +1,5 @@ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:58:14 + --> $DIR/inline-syntax.rs:47:14 | LL | global_asm!(".intel_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -7,37 +7,37 @@ LL | global_asm!(".intel_syntax noprefix", "nop"); = note: `#[warn(bad_asm_style)]` on by default warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:26:15 + --> $DIR/inline-syntax.rs:21:15 | LL | asm!(".intel_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:30:15 + --> $DIR/inline-syntax.rs:24:15 | LL | asm!(".intel_syntax aaa noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead - --> $DIR/inline-syntax.rs:34:15 + --> $DIR/inline-syntax.rs:27:15 | LL | asm!(".att_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead - --> $DIR/inline-syntax.rs:38:15 + --> $DIR/inline-syntax.rs:30:15 | LL | asm!(".att_syntax bbb noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:42:15 + --> $DIR/inline-syntax.rs:33:15 | LL | asm!(".intel_syntax noprefix; nop"); | ^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:49:13 + --> $DIR/inline-syntax.rs:39:13 | LL | .intel_syntax noprefix | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/invalid-const-operand.rs b/tests/ui/asm/invalid-const-operand.rs index a688f5042db5c..bbf4001752a4b 100644 --- a/tests/ui/asm/invalid-const-operand.rs +++ b/tests/ui/asm/invalid-const-operand.rs @@ -14,7 +14,7 @@ global_asm!("{}", const 0f32); global_asm!("{}", const 0 as *mut u8); //~^ ERROR invalid type for `const` operand -fn main() { +fn test1() { unsafe { // Const operands must be integers and must be constants. @@ -27,7 +27,11 @@ fn main() { //~^ ERROR invalid type for `const` operand asm!("{}", const &0); //~^ ERROR invalid type for `const` operand + } +} +fn test2() { + unsafe { // Constants must be... constant let x = 0; @@ -47,3 +51,5 @@ fn main() { //~^ ERROR attempt to use a non-constant value in a constant } } + +fn main() {} diff --git a/tests/ui/asm/invalid-const-operand.stderr b/tests/ui/asm/invalid-const-operand.stderr index 1cedabeef2892..01aa843c6fb19 100644 --- a/tests/ui/asm/invalid-const-operand.stderr +++ b/tests/ui/asm/invalid-const-operand.stderr @@ -1,5 +1,5 @@ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/invalid-const-operand.rs:40:26 + --> $DIR/invalid-const-operand.rs:44:26 | LL | asm!("{}", const x); | ^ non-constant value @@ -11,7 +11,7 @@ LL + const x: /* Type */ = 0; | error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/invalid-const-operand.rs:43:36 + --> $DIR/invalid-const-operand.rs:47:36 | LL | asm!("{}", const const_foo(x)); | ^ non-constant value @@ -23,7 +23,7 @@ LL + const x: /* Type */ = 0; | error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/invalid-const-operand.rs:46:36 + --> $DIR/invalid-const-operand.rs:50:36 | LL | asm!("{}", const const_bar(x)); | ^ non-constant value @@ -80,7 +80,7 @@ error: invalid type for `const` operand LL | asm!("{}", const &0); | ^^^^^^-- | | - | is a `&{integer}` + | is a `&i32` | = help: `const` operands must be of an integer type diff --git a/tests/ui/asm/issue-85247.rs b/tests/ui/asm/issue-85247.rs index 47bfda14092d7..f54c868dd5610 100644 --- a/tests/ui/asm/issue-85247.rs +++ b/tests/ui/asm/issue-85247.rs @@ -18,6 +18,6 @@ use minicore::*; fn main() { unsafe { asm!("", out("r9") _); - //[rwpi]~^ cannot use register `r9` + //[rwpi]~^ ERROR cannot use register `r9` } } diff --git a/tests/ui/asm/issue-99071.rs b/tests/ui/asm/issue-99071.rs index 6a00fce7de46e..522ac1fe8877c 100644 --- a/tests/ui/asm/issue-99071.rs +++ b/tests/ui/asm/issue-99071.rs @@ -13,6 +13,6 @@ use minicore::*; pub fn foo() { unsafe { asm!("", in("r8") 0); - //~^ cannot use register `r8`: high registers (r8+) can only be used as clobbers in Thumb-1 code + //~^ ERROR cannot use register `r8`: high registers (r8+) can only be used as clobbers in Thumb-1 code } } diff --git a/tests/ui/asm/naked-asm-mono-sym-fn.rs b/tests/ui/asm/naked-asm-mono-sym-fn.rs new file mode 100644 index 0000000000000..948c290c0b4fb --- /dev/null +++ b/tests/ui/asm/naked-asm-mono-sym-fn.rs @@ -0,0 +1,35 @@ +// Regression test for . +// Test that we're properly monomorphizing sym args in naked asm blocks +// that point to associated items. + +//@ edition: 2021 +//@ needs-asm-support +//@ only-x86_64 +//@ build-pass + +trait Tr { + extern "C" fn t(); +} + +enum E {} + +impl Tr for E { + extern "C" fn t() { + println!("Const generic: {}", C); + } +} + +#[unsafe(naked)] +extern "C" fn foo() { + core::arch::naked_asm!( + "push rax", + "call {fn}", + "pop rax", + "ret", + fn = sym ::t, + ); +} + +fn main() { + foo::>(); +} diff --git a/tests/ui/asm/naked-asm-outside-naked-fn.rs b/tests/ui/asm/naked-asm-outside-naked-fn.rs index 1696008f3397d..0a15b21f4d014 100644 --- a/tests/ui/asm/naked-asm-outside-naked-fn.rs +++ b/tests/ui/asm/naked-asm-outside-naked-fn.rs @@ -3,7 +3,6 @@ //@ ignore-nvptx64 //@ ignore-spirv -#![feature(naked_functions)] #![crate_type = "lib"] use std::arch::naked_asm; @@ -12,24 +11,24 @@ fn main() { test1(); } -#[naked] +#[unsafe(naked)] extern "C" fn test1() { - unsafe { naked_asm!("") } + naked_asm!("") } extern "C" fn test2() { - unsafe { naked_asm!("") } - //~^ ERROR the `naked_asm!` macro can only be used in functions marked with `#[naked]` + naked_asm!("") + //~^ ERROR the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]` } extern "C" fn test3() { - unsafe { (|| naked_asm!(""))() } - //~^ ERROR the `naked_asm!` macro can only be used in functions marked with `#[naked]` + (|| naked_asm!(""))() + //~^ ERROR the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]` } fn test4() { async move { - unsafe { naked_asm!("") } ; - //~^ ERROR the `naked_asm!` macro can only be used in functions marked with `#[naked]` + naked_asm!(""); + //~^ ERROR the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]` }; } diff --git a/tests/ui/asm/naked-asm-outside-naked-fn.stderr b/tests/ui/asm/naked-asm-outside-naked-fn.stderr index 6e91359669ca2..2cebaa9ea285d 100644 --- a/tests/ui/asm/naked-asm-outside-naked-fn.stderr +++ b/tests/ui/asm/naked-asm-outside-naked-fn.stderr @@ -1,20 +1,20 @@ -error: the `naked_asm!` macro can only be used in functions marked with `#[naked]` - --> $DIR/naked-asm-outside-naked-fn.rs:21:14 +error: the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]` + --> $DIR/naked-asm-outside-naked-fn.rs:20:5 | -LL | unsafe { naked_asm!("") } - | ^^^^^^^^^^^^^^ +LL | naked_asm!("") + | ^^^^^^^^^^^^^^ -error: the `naked_asm!` macro can only be used in functions marked with `#[naked]` - --> $DIR/naked-asm-outside-naked-fn.rs:26:18 +error: the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]` + --> $DIR/naked-asm-outside-naked-fn.rs:25:9 | -LL | unsafe { (|| naked_asm!(""))() } - | ^^^^^^^^^^^^^^ +LL | (|| naked_asm!(""))() + | ^^^^^^^^^^^^^^ -error: the `naked_asm!` macro can only be used in functions marked with `#[naked]` - --> $DIR/naked-asm-outside-naked-fn.rs:32:19 +error: the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]` + --> $DIR/naked-asm-outside-naked-fn.rs:31:9 | -LL | unsafe { naked_asm!("") } ; - | ^^^^^^^^^^^^^^ +LL | naked_asm!(""); + | ^^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/asm/naked-functions-ffi.rs b/tests/ui/asm/naked-functions-ffi.rs index b78d1e6a0d1c6..565c440022db3 100644 --- a/tests/ui/asm/naked-functions-ffi.rs +++ b/tests/ui/asm/naked-functions-ffi.rs @@ -1,15 +1,12 @@ //@ check-pass //@ needs-asm-support -#![feature(naked_functions)] #![crate_type = "lib"] use std::arch::naked_asm; -#[naked] +#[unsafe(naked)] pub extern "C" fn naked(p: char) -> u128 { //~^ WARN uses type `char` //~| WARN uses type `u128` - unsafe { - naked_asm!(""); - } + naked_asm!("") } diff --git a/tests/ui/asm/naked-functions-ffi.stderr b/tests/ui/asm/naked-functions-ffi.stderr index 908881b194999..9df6185498ed6 100644 --- a/tests/ui/asm/naked-functions-ffi.stderr +++ b/tests/ui/asm/naked-functions-ffi.stderr @@ -1,5 +1,5 @@ warning: `extern` fn uses type `char`, which is not FFI-safe - --> $DIR/naked-functions-ffi.rs:9:28 + --> $DIR/naked-functions-ffi.rs:8:28 | LL | pub extern "C" fn naked(p: char) -> u128 { | ^^^^ not FFI-safe @@ -9,7 +9,7 @@ LL | pub extern "C" fn naked(p: char) -> u128 { = note: `#[warn(improper_ctypes_definitions)]` on by default warning: `extern` fn uses type `u128`, which is not FFI-safe - --> $DIR/naked-functions-ffi.rs:9:37 + --> $DIR/naked-functions-ffi.rs:8:37 | LL | pub extern "C" fn naked(p: char) -> u128 { | ^^^^ not FFI-safe diff --git a/tests/ui/asm/naked-functions-inline.rs b/tests/ui/asm/naked-functions-inline.rs index 74049e8ecbc7c..93741f26275bd 100644 --- a/tests/ui/asm/naked-functions-inline.rs +++ b/tests/ui/asm/naked-functions-inline.rs @@ -1,38 +1,37 @@ //@ needs-asm-support -#![feature(naked_functions)] #![crate_type = "lib"] use std::arch::naked_asm; -#[naked] -pub unsafe extern "C" fn inline_none() { +#[unsafe(naked)] +pub extern "C" fn inline_none() { naked_asm!(""); } -#[naked] +#[unsafe(naked)] #[inline] //~^ ERROR [E0736] -pub unsafe extern "C" fn inline_hint() { +pub extern "C" fn inline_hint() { naked_asm!(""); } -#[naked] +#[unsafe(naked)] #[inline(always)] //~^ ERROR [E0736] -pub unsafe extern "C" fn inline_always() { +pub extern "C" fn inline_always() { naked_asm!(""); } -#[naked] +#[unsafe(naked)] #[inline(never)] //~^ ERROR [E0736] -pub unsafe extern "C" fn inline_never() { +pub extern "C" fn inline_never() { naked_asm!(""); } -#[naked] +#[unsafe(naked)] #[cfg_attr(all(), inline(never))] //~^ ERROR [E0736] -pub unsafe extern "C" fn conditional_inline_never() { +pub extern "C" fn conditional_inline_never() { naked_asm!(""); } diff --git a/tests/ui/asm/naked-functions-inline.stderr b/tests/ui/asm/naked-functions-inline.stderr index 84a688f6f5382..07d5f3bc49a94 100644 --- a/tests/ui/asm/naked-functions-inline.stderr +++ b/tests/ui/asm/naked-functions-inline.stderr @@ -1,34 +1,34 @@ -error[E0736]: attribute incompatible with `#[naked]` - --> $DIR/naked-functions-inline.rs:13:1 +error[E0736]: attribute incompatible with `#[unsafe(naked)]` + --> $DIR/naked-functions-inline.rs:12:1 | -LL | #[naked] - | -------- function marked with `#[naked]` here +LL | #[unsafe(naked)] + | ---------------- function marked with `#[unsafe(naked)]` here LL | #[inline] - | ^^^^^^^^^ the `inline` attribute is incompatible with `#[naked]` + | ^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` -error[E0736]: attribute incompatible with `#[naked]` - --> $DIR/naked-functions-inline.rs:20:1 +error[E0736]: attribute incompatible with `#[unsafe(naked)]` + --> $DIR/naked-functions-inline.rs:19:1 | -LL | #[naked] - | -------- function marked with `#[naked]` here +LL | #[unsafe(naked)] + | ---------------- function marked with `#[unsafe(naked)]` here LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[naked]` + | ^^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` -error[E0736]: attribute incompatible with `#[naked]` - --> $DIR/naked-functions-inline.rs:27:1 +error[E0736]: attribute incompatible with `#[unsafe(naked)]` + --> $DIR/naked-functions-inline.rs:26:1 | -LL | #[naked] - | -------- function marked with `#[naked]` here +LL | #[unsafe(naked)] + | ---------------- function marked with `#[unsafe(naked)]` here LL | #[inline(never)] - | ^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[naked]` + | ^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` -error[E0736]: attribute incompatible with `#[naked]` - --> $DIR/naked-functions-inline.rs:34:19 +error[E0736]: attribute incompatible with `#[unsafe(naked)]` + --> $DIR/naked-functions-inline.rs:33:19 | -LL | #[naked] - | -------- function marked with `#[naked]` here +LL | #[unsafe(naked)] + | ---------------- function marked with `#[unsafe(naked)]` here LL | #[cfg_attr(all(), inline(never))] - | ^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[naked]` + | ^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]` error: aborting due to 4 previous errors diff --git a/tests/ui/asm/naked-functions-instruction-set.rs b/tests/ui/asm/naked-functions-instruction-set.rs index 28241badf5f87..69927a56aabb3 100644 --- a/tests/ui/asm/naked-functions-instruction-set.rs +++ b/tests/ui/asm/naked-functions-instruction-set.rs @@ -5,22 +5,22 @@ //@ build-pass #![crate_type = "lib"] -#![feature(no_core, naked_functions)] +#![feature(no_core)] #![no_core] extern crate minicore; use minicore::*; #[no_mangle] -#[naked] +#[unsafe(naked)] #[instruction_set(arm::t32)] -unsafe extern "C" fn test_thumb() { +extern "C" fn test_thumb() { naked_asm!("bx lr"); } #[no_mangle] -#[naked] +#[unsafe(naked)] #[instruction_set(arm::a32)] -unsafe extern "C" fn test_arm() { +extern "C" fn test_arm() { naked_asm!("bx lr"); } diff --git a/tests/ui/asm/naked-functions-rustic-abi.rs b/tests/ui/asm/naked-functions-rustic-abi.rs new file mode 100644 index 0000000000000..d9c1147482881 --- /dev/null +++ b/tests/ui/asm/naked-functions-rustic-abi.rs @@ -0,0 +1,27 @@ +//@ revisions: x86_64 aarch64 +// +//@[aarch64] only-aarch64 +//@[x86_64] only-x86_64 +// +//@ build-pass +//@ needs-asm-support + +#![feature(naked_functions_rustic_abi, rust_cold_cc)] +#![crate_type = "lib"] + +use std::arch::{asm, naked_asm}; + +#[unsafe(naked)] +pub fn rust_implicit() { + naked_asm!("ret"); +} + +#[unsafe(naked)] +pub extern "Rust" fn rust_explicit() { + naked_asm!("ret"); +} + +#[unsafe(naked)] +pub extern "rust-cold" fn rust_cold() { + naked_asm!("ret"); +} diff --git a/tests/ui/asm/naked-functions-target-feature.rs b/tests/ui/asm/naked-functions-target-feature.rs new file mode 100644 index 0000000000000..57ad79b1c315c --- /dev/null +++ b/tests/ui/asm/naked-functions-target-feature.rs @@ -0,0 +1,21 @@ +//@ build-pass +//@ needs-asm-support + +#![feature(naked_functions_target_feature)] +#![crate_type = "lib"] + +use std::arch::{asm, naked_asm}; + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "sse2")] +#[unsafe(naked)] +pub extern "C" fn compatible_target_feature() { + naked_asm!("ret"); +} + +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "neon")] +#[unsafe(naked)] +pub extern "C" fn compatible_target_feature() { + naked_asm!("ret"); +} diff --git a/tests/ui/asm/naked-functions-testattrs.rs b/tests/ui/asm/naked-functions-testattrs.rs index 7e373270e9fc6..6dc14a6840ecb 100644 --- a/tests/ui/asm/naked-functions-testattrs.rs +++ b/tests/ui/asm/naked-functions-testattrs.rs @@ -1,39 +1,37 @@ //@ needs-asm-support //@ compile-flags: --test -#![allow(undefined_naked_function_abi)] -#![feature(naked_functions)] #![feature(test)] #![crate_type = "lib"] use std::arch::naked_asm; #[test] -#[naked] +#[unsafe(naked)] //~^ ERROR [E0736] -fn test_naked() { - unsafe { naked_asm!("") }; +extern "C" fn test_naked() { + naked_asm!("") } #[should_panic] #[test] -#[naked] +#[unsafe(naked)] //~^ ERROR [E0736] -fn test_naked_should_panic() { - unsafe { naked_asm!("") }; +extern "C" fn test_naked_should_panic() { + naked_asm!("") } #[ignore] #[test] -#[naked] +#[unsafe(naked)] //~^ ERROR [E0736] -fn test_naked_ignore() { - unsafe { naked_asm!("") }; +extern "C" fn test_naked_ignore() { + naked_asm!("") } #[bench] -#[naked] +#[unsafe(naked)] //~^ ERROR [E0736] -fn bench_naked() { - unsafe { naked_asm!("") }; +extern "C" fn bench_naked() { + naked_asm!("") } diff --git a/tests/ui/asm/naked-functions-testattrs.stderr b/tests/ui/asm/naked-functions-testattrs.stderr index 4dabe41964a57..8aab2f04ee29e 100644 --- a/tests/ui/asm/naked-functions-testattrs.stderr +++ b/tests/ui/asm/naked-functions-testattrs.stderr @@ -1,34 +1,34 @@ -error[E0736]: cannot use `#[naked]` with testing attributes - --> $DIR/naked-functions-testattrs.rs:12:1 +error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes + --> $DIR/naked-functions-testattrs.rs:10:1 | LL | #[test] | ------- function marked with testing attribute here -LL | #[naked] - | ^^^^^^^^ `#[naked]` is incompatible with testing attributes +LL | #[unsafe(naked)] + | ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes -error[E0736]: cannot use `#[naked]` with testing attributes - --> $DIR/naked-functions-testattrs.rs:20:1 +error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes + --> $DIR/naked-functions-testattrs.rs:18:1 | LL | #[test] | ------- function marked with testing attribute here -LL | #[naked] - | ^^^^^^^^ `#[naked]` is incompatible with testing attributes +LL | #[unsafe(naked)] + | ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes -error[E0736]: cannot use `#[naked]` with testing attributes - --> $DIR/naked-functions-testattrs.rs:28:1 +error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes + --> $DIR/naked-functions-testattrs.rs:26:1 | LL | #[test] | ------- function marked with testing attribute here -LL | #[naked] - | ^^^^^^^^ `#[naked]` is incompatible with testing attributes +LL | #[unsafe(naked)] + | ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes -error[E0736]: cannot use `#[naked]` with testing attributes - --> $DIR/naked-functions-testattrs.rs:35:1 +error[E0736]: cannot use `#[unsafe(naked)]` with testing attributes + --> $DIR/naked-functions-testattrs.rs:33:1 | LL | #[bench] | -------- function marked with testing attribute here -LL | #[naked] - | ^^^^^^^^ `#[naked]` is incompatible with testing attributes +LL | #[unsafe(naked)] + | ^^^^^^^^^^^^^^^^ `#[unsafe(naked)]` is incompatible with testing attributes error: aborting due to 4 previous errors diff --git a/tests/ui/asm/naked-functions-unused.aarch64.stderr b/tests/ui/asm/naked-functions-unused.aarch64.stderr index ea63ced1aab04..bfb2923b0b8d6 100644 --- a/tests/ui/asm/naked-functions-unused.aarch64.stderr +++ b/tests/ui/asm/naked-functions-unused.aarch64.stderr @@ -1,5 +1,5 @@ error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:17:32 + --> $DIR/naked-functions-unused.rs:16:32 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` @@ -12,55 +12,55 @@ LL | #![deny(unused)] = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:17:42 + --> $DIR/naked-functions-unused.rs:16:42 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:28:38 + --> $DIR/naked-functions-unused.rs:27:38 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:28:48 + --> $DIR/naked-functions-unused.rs:27:48 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:36:41 + --> $DIR/naked-functions-unused.rs:35:41 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:36:51 + --> $DIR/naked-functions-unused.rs:35:51 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:46:40 + --> $DIR/naked-functions-unused.rs:45:40 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:46:50 + --> $DIR/naked-functions-unused.rs:45:50 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:54:43 + --> $DIR/naked-functions-unused.rs:53:43 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:54:53 + --> $DIR/naked-functions-unused.rs:53:53 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` diff --git a/tests/ui/asm/naked-functions-unused.rs b/tests/ui/asm/naked-functions-unused.rs index c27037819a44f..945ab1a40ad0c 100644 --- a/tests/ui/asm/naked-functions-unused.rs +++ b/tests/ui/asm/naked-functions-unused.rs @@ -3,7 +3,6 @@ //@[x86_64] only-x86_64 //@[aarch64] only-aarch64 #![deny(unused)] -#![feature(naked_functions)] #![crate_type = "lib"] pub trait Trait { @@ -64,44 +63,34 @@ pub mod normal { pub mod naked { use std::arch::naked_asm; - #[naked] + #[unsafe(naked)] pub extern "C" fn function(a: usize, b: usize) -> usize { - unsafe { - naked_asm!(""); - } + naked_asm!("") } pub struct Naked; impl Naked { - #[naked] + #[unsafe(naked)] pub extern "C" fn associated(a: usize, b: usize) -> usize { - unsafe { - naked_asm!(""); - } + naked_asm!("") } - #[naked] + #[unsafe(naked)] pub extern "C" fn method(&self, a: usize, b: usize) -> usize { - unsafe { - naked_asm!(""); - } + naked_asm!("") } } impl super::Trait for Naked { - #[naked] + #[unsafe(naked)] extern "C" fn trait_associated(a: usize, b: usize) -> usize { - unsafe { - naked_asm!(""); - } + naked_asm!("") } - #[naked] + #[unsafe(naked)] extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { - unsafe { - naked_asm!(""); - } + naked_asm!("") } } } diff --git a/tests/ui/asm/naked-functions-unused.x86_64.stderr b/tests/ui/asm/naked-functions-unused.x86_64.stderr index ea63ced1aab04..bfb2923b0b8d6 100644 --- a/tests/ui/asm/naked-functions-unused.x86_64.stderr +++ b/tests/ui/asm/naked-functions-unused.x86_64.stderr @@ -1,5 +1,5 @@ error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:17:32 + --> $DIR/naked-functions-unused.rs:16:32 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` @@ -12,55 +12,55 @@ LL | #![deny(unused)] = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:17:42 + --> $DIR/naked-functions-unused.rs:16:42 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:28:38 + --> $DIR/naked-functions-unused.rs:27:38 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:28:48 + --> $DIR/naked-functions-unused.rs:27:48 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:36:41 + --> $DIR/naked-functions-unused.rs:35:41 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:36:51 + --> $DIR/naked-functions-unused.rs:35:51 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:46:40 + --> $DIR/naked-functions-unused.rs:45:40 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:46:50 + --> $DIR/naked-functions-unused.rs:45:50 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:54:43 + --> $DIR/naked-functions-unused.rs:53:43 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:54:53 + --> $DIR/naked-functions-unused.rs:53:53 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index e7e5d84f2a5da..a6f41698b4117 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -2,15 +2,14 @@ //@ ignore-nvptx64 //@ ignore-spirv -#![feature(naked_functions)] -#![feature(asm_unwind, linkage)] +#![feature(asm_unwind, linkage, rustc_attrs)] #![crate_type = "lib"] use std::arch::{asm, naked_asm}; -#[naked] -pub unsafe extern "C" fn inline_asm_macro() { - asm!("", options(raw)); +#[unsafe(naked)] +pub extern "C" fn inline_asm_macro() { + unsafe { asm!("", options(raw)) }; //~^ERROR the `asm!` macro is not allowed in naked functions } @@ -20,8 +19,8 @@ pub struct P { y: u16, } -#[naked] -pub unsafe extern "C" fn patterns( +#[unsafe(naked)] +pub extern "C" fn patterns( mut a: u32, //~^ ERROR patterns not allowed in naked function parameters &b: &i32, @@ -34,28 +33,28 @@ pub unsafe extern "C" fn patterns( naked_asm!("") } -#[naked] -pub unsafe extern "C" fn inc(a: u32) -> u32 { +#[unsafe(naked)] +pub extern "C" fn inc(a: u32) -> u32 { //~^ ERROR naked functions must contain a single `naked_asm!` invocation a + 1 //~^ ERROR referencing function parameters is not allowed in naked functions } -#[naked] +#[unsafe(naked)] #[allow(asm_sub_register)] -pub unsafe extern "C" fn inc_asm(a: u32) -> u32 { +pub extern "C" fn inc_asm(a: u32) -> u32 { naked_asm!("/* {0} */", in(reg) a) //~^ ERROR the `in` operand cannot be used with `naked_asm!` } -#[naked] -pub unsafe extern "C" fn inc_closure(a: u32) -> u32 { +#[unsafe(naked)] +pub extern "C" fn inc_closure(a: u32) -> u32 { //~^ ERROR naked functions must contain a single `naked_asm!` invocation (|| a + 1)() } -#[naked] -pub unsafe extern "C" fn unsupported_operands() { +#[unsafe(naked)] +pub extern "C" fn unsupported_operands() { //~^ ERROR naked functions must contain a single `naked_asm!` invocation let mut a = 0usize; let mut b = 0usize; @@ -76,23 +75,22 @@ pub unsafe extern "C" fn unsupported_operands() { ); } -#[naked] +#[unsafe(naked)] pub extern "C" fn missing_assembly() { //~^ ERROR naked functions must contain a single `naked_asm!` invocation } -#[naked] +#[unsafe(naked)] pub extern "C" fn too_many_asm_blocks() { //~^ ERROR naked functions must contain a single `naked_asm!` invocation - unsafe { - naked_asm!("", options(noreturn)); - //~^ ERROR the `noreturn` option cannot be used with `naked_asm!` - naked_asm!(""); - } + + naked_asm!("", options(noreturn)); + //~^ ERROR the `noreturn` option cannot be used with `naked_asm!` + naked_asm!(""); } pub fn outer(x: u32) -> extern "C" fn(usize) -> usize { - #[naked] + #[unsafe(naked)] pub extern "C" fn inner(y: usize) -> usize { //~^ ERROR naked functions must contain a single `naked_asm!` invocation *&y @@ -101,14 +99,14 @@ pub fn outer(x: u32) -> extern "C" fn(usize) -> usize { inner } -#[naked] +#[unsafe(naked)] unsafe extern "C" fn invalid_options() { naked_asm!("", options(nomem, preserves_flags)); //~^ ERROR the `nomem` option cannot be used with `naked_asm!` //~| ERROR the `preserves_flags` option cannot be used with `naked_asm!` } -#[naked] +#[unsafe(naked)] unsafe extern "C" fn invalid_options_continued() { naked_asm!("", options(readonly, nostack), options(pure)); //~^ ERROR the `readonly` option cannot be used with `naked_asm!` @@ -116,77 +114,60 @@ unsafe extern "C" fn invalid_options_continued() { //~| ERROR the `pure` option cannot be used with `naked_asm!` } -#[naked] +#[unsafe(naked)] unsafe extern "C" fn invalid_may_unwind() { naked_asm!("", options(may_unwind)); //~^ ERROR the `may_unwind` option cannot be used with `naked_asm!` } -#[naked] -pub unsafe fn default_abi() { - //~^ WARN Rust ABI is unsupported in naked functions - naked_asm!(""); -} - -#[naked] -pub unsafe fn rust_abi() { - //~^ WARN Rust ABI is unsupported in naked functions - naked_asm!(""); -} - -#[naked] +#[unsafe(naked)] pub extern "C" fn valid_a() -> T { - unsafe { - naked_asm!(""); - } + naked_asm!(""); } -#[naked] +#[unsafe(naked)] pub extern "C" fn valid_b() { - unsafe { + { { - { - naked_asm!(""); - }; + naked_asm!(""); }; - } + }; } -#[naked] -pub unsafe extern "C" fn valid_c() { +#[unsafe(naked)] +pub extern "C" fn valid_c() { naked_asm!(""); } #[cfg(target_arch = "x86_64")] -#[naked] -pub unsafe extern "C" fn valid_att_syntax() { +#[unsafe(naked)] +pub extern "C" fn valid_att_syntax() { naked_asm!("", options(att_syntax)); } -#[naked] -#[naked] -pub unsafe extern "C" fn allow_compile_error(a: u32) -> u32 { +#[unsafe(naked)] +pub extern "C" fn allow_compile_error(a: u32) -> u32 { compile_error!("this is a user specified error") //~^ ERROR this is a user specified error } -#[naked] -pub unsafe extern "C" fn allow_compile_error_and_asm(a: u32) -> u32 { +#[unsafe(naked)] +pub extern "C" fn allow_compile_error_and_asm(a: u32) -> u32 { compile_error!("this is a user specified error"); //~^ ERROR this is a user specified error naked_asm!("") } -#[naked] -pub unsafe extern "C" fn invalid_asm_syntax(a: u32) -> u32 { +#[unsafe(naked)] +pub extern "C" fn invalid_asm_syntax(a: u32) -> u32 { naked_asm!(invalid_syntax) //~^ ERROR asm template must be a string literal } #[cfg(target_arch = "x86_64")] #[cfg_attr(target_pointer_width = "64", no_mangle)] -#[naked] -pub unsafe extern "C" fn compatible_cfg_attributes() { +#[unsafe(naked)] +pub extern "C" fn compatible_cfg_attributes() { naked_asm!("", options(att_syntax)); } @@ -194,21 +175,21 @@ pub unsafe extern "C" fn compatible_cfg_attributes() { #[warn(dead_code)] #[deny(dead_code)] #[forbid(dead_code)] -#[naked] -pub unsafe extern "C" fn compatible_diagnostic_attributes() { +#[unsafe(naked)] +pub extern "C" fn compatible_diagnostic_attributes() { naked_asm!("", options(raw)); } #[deprecated = "test"] -#[naked] -pub unsafe extern "C" fn compatible_deprecated_attributes() { +#[unsafe(naked)] +pub extern "C" fn compatible_deprecated_attributes() { naked_asm!("", options(raw)); } #[cfg(target_arch = "x86_64")] #[must_use] -#[naked] -pub unsafe extern "C" fn compatible_must_use_attributes() -> u64 { +#[unsafe(naked)] +pub extern "C" fn compatible_must_use_attributes() -> u64 { naked_asm!( " mov rax, 42 @@ -219,35 +200,40 @@ pub unsafe extern "C" fn compatible_must_use_attributes() -> u64 { #[export_name = "exported_function_name"] #[link_section = ".custom_section"] -#[naked] -pub unsafe extern "C" fn compatible_ffi_attributes_1() { +#[unsafe(naked)] +pub extern "C" fn compatible_ffi_attributes_1() { naked_asm!("", options(raw)); } #[cold] -#[naked] -pub unsafe extern "C" fn compatible_codegen_attributes() { +#[unsafe(naked)] +pub extern "C" fn compatible_codegen_attributes() { naked_asm!("", options(raw)); } -#[cfg(target_arch = "x86_64")] -#[target_feature(enable = "sse2")] -#[naked] -pub unsafe extern "C" fn compatible_target_feature() { - naked_asm!(""); -} - #[doc = "foo bar baz"] /// a doc comment // a normal comment #[doc(alias = "ADocAlias")] -#[naked] -pub unsafe extern "C" fn compatible_doc_attributes() { +#[unsafe(naked)] +pub extern "C" fn compatible_doc_attributes() { naked_asm!("", options(raw)); } #[linkage = "external"] -#[naked] -pub unsafe extern "C" fn compatible_linkage() { +#[unsafe(naked)] +pub extern "C" fn compatible_linkage() { + naked_asm!("", options(raw)); +} + +#[rustc_std_internal_symbol] +#[unsafe(naked)] +pub extern "C" fn rustc_std_internal_symbol() { + naked_asm!("", options(raw)); +} + +#[rustfmt::skip] +#[unsafe(naked)] +pub extern "C" fn rustfmt_skip() { naked_asm!("", options(raw)); } diff --git a/tests/ui/asm/naked-functions.stderr b/tests/ui/asm/naked-functions.stderr index 0898f3620f24f..b94a09bb92ed9 100644 --- a/tests/ui/asm/naked-functions.stderr +++ b/tests/ui/asm/naked-functions.stderr @@ -1,107 +1,107 @@ error: the `in` operand cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:47:29 + --> $DIR/naked-functions.rs:46:29 | LL | naked_asm!("/* {0} */", in(reg) a) | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `in` operand cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:68:10 + --> $DIR/naked-functions.rs:67:10 | LL | in(reg) a, | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `noreturn` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:88:32 + --> $DIR/naked-functions.rs:87:28 | -LL | naked_asm!("", options(noreturn)); - | ^^^^^^^^ the `noreturn` option is not meaningful for global-scoped inline assembly +LL | naked_asm!("", options(noreturn)); + | ^^^^^^^^ the `noreturn` option is not meaningful for global-scoped inline assembly error: the `nomem` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:106:28 + --> $DIR/naked-functions.rs:104:28 | LL | naked_asm!("", options(nomem, preserves_flags)); | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly error: the `preserves_flags` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:106:35 + --> $DIR/naked-functions.rs:104:35 | LL | naked_asm!("", options(nomem, preserves_flags)); | ^^^^^^^^^^^^^^^ the `preserves_flags` option is not meaningful for global-scoped inline assembly error: the `readonly` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:113:28 + --> $DIR/naked-functions.rs:111:28 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^^ the `readonly` option is not meaningful for global-scoped inline assembly error: the `nostack` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:113:38 + --> $DIR/naked-functions.rs:111:38 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^ the `nostack` option is not meaningful for global-scoped inline assembly error: the `pure` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:113:56 + --> $DIR/naked-functions.rs:111:56 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^ the `pure` option is not meaningful for global-scoped inline assembly error: the `may_unwind` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:121:28 + --> $DIR/naked-functions.rs:119:28 | LL | naked_asm!("", options(may_unwind)); | ^^^^^^^^^^ the `may_unwind` option is not meaningful for global-scoped inline assembly error: this is a user specified error - --> $DIR/naked-functions.rs:169:5 + --> $DIR/naked-functions.rs:150:5 | LL | compile_error!("this is a user specified error") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this is a user specified error - --> $DIR/naked-functions.rs:175:5 + --> $DIR/naked-functions.rs:156:5 | LL | compile_error!("this is a user specified error"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: asm template must be a string literal - --> $DIR/naked-functions.rs:182:16 + --> $DIR/naked-functions.rs:163:16 | LL | naked_asm!(invalid_syntax) | ^^^^^^^^^^^^^^ error[E0787]: the `asm!` macro is not allowed in naked functions - --> $DIR/naked-functions.rs:13:5 + --> $DIR/naked-functions.rs:12:14 | -LL | asm!("", options(raw)); - | ^^^^^^^^^^^^^^^^^^^^^^ consider using the `naked_asm!` macro instead +LL | unsafe { asm!("", options(raw)) }; + | ^^^^^^^^^^^^^^^^^^^^^^ consider using the `naked_asm!` macro instead error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:25:5 + --> $DIR/naked-functions.rs:24:5 | LL | mut a: u32, | ^^^^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:27:5 + --> $DIR/naked-functions.rs:26:5 | LL | &b: &i32, | ^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:29:6 + --> $DIR/naked-functions.rs:28:6 | LL | (None | Some(_)): Option>, | ^^^^^^^^^^^^^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:31:5 + --> $DIR/naked-functions.rs:30:5 | LL | P { x, y }: P, | ^^^^^^^^^^ error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:40:5 + --> $DIR/naked-functions.rs:39:5 | LL | a + 1 | ^ @@ -109,28 +109,28 @@ LL | a + 1 = help: follow the calling convention in asm block to use parameters error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:38:1 + --> $DIR/naked-functions.rs:37:1 | -LL | pub unsafe extern "C" fn inc(a: u32) -> u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub extern "C" fn inc(a: u32) -> u32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | a + 1 | ----- not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:52:1 + --> $DIR/naked-functions.rs:51:1 | -LL | pub unsafe extern "C" fn inc_closure(a: u32) -> u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub extern "C" fn inc_closure(a: u32) -> u32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | (|| a + 1)() | ------------ not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:58:1 + --> $DIR/naked-functions.rs:57:1 | -LL | pub unsafe extern "C" fn unsupported_operands() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub extern "C" fn unsupported_operands() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | let mut a = 0usize; | ------------------- not allowed in naked functions @@ -144,22 +144,22 @@ LL | let mut e = 0usize; | ------------------- not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:80:1 + --> $DIR/naked-functions.rs:79:1 | LL | pub extern "C" fn missing_assembly() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:85:1 + --> $DIR/naked-functions.rs:84:1 | LL | pub extern "C" fn too_many_asm_blocks() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... -LL | naked_asm!(""); - | -------------- multiple `naked_asm!` invocations are not allowed in naked functions +LL | naked_asm!(""); + | -------------- multiple `naked_asm!` invocations are not allowed in naked functions error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:98:11 + --> $DIR/naked-functions.rs:96:11 | LL | *&y | ^ @@ -167,7 +167,7 @@ LL | *&y = help: follow the calling convention in asm block to use parameters error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:96:5 + --> $DIR/naked-functions.rs:94:5 | LL | pub extern "C" fn inner(y: usize) -> usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -175,20 +175,6 @@ LL | LL | *&y | --- not allowed in naked functions -warning: Rust ABI is unsupported in naked functions - --> $DIR/naked-functions.rs:126:1 - | -LL | pub unsafe fn default_abi() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(undefined_naked_function_abi)]` on by default - -warning: Rust ABI is unsupported in naked functions - --> $DIR/naked-functions.rs:132:1 - | -LL | pub unsafe fn rust_abi() { - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 25 previous errors; 2 warnings emitted +error: aborting due to 25 previous errors For more information about this error, try `rustc --explain E0787`. diff --git a/tests/ui/asm/naked-invalid-attr.rs b/tests/ui/asm/naked-invalid-attr.rs index 4053c58fb5136..6ac9cb9e3a9c5 100644 --- a/tests/ui/asm/naked-invalid-attr.rs +++ b/tests/ui/asm/naked-invalid-attr.rs @@ -1,53 +1,62 @@ -// Checks that #[naked] attribute can be placed on function definitions only. +// Checks that the #[unsafe(naked)] attribute can be placed on function definitions only. // //@ needs-asm-support -#![feature(naked_functions)] -#![naked] //~ ERROR should be applied to a function definition +#![unsafe(naked)] //~ ERROR should be applied to a function definition use std::arch::naked_asm; extern "C" { - #[naked] //~ ERROR should be applied to a function definition + #[unsafe(naked)] //~ ERROR should be applied to a function definition fn f(); } -#[naked] //~ ERROR should be applied to a function definition +#[unsafe(naked)] //~ ERROR should be applied to a function definition #[repr(C)] struct S { + #[unsafe(naked)] //~ ERROR should be applied to a function definition a: u32, b: u32, } trait Invoke { - #[naked] //~ ERROR should be applied to a function definition + #[unsafe(naked)] //~ ERROR should be applied to a function definition extern "C" fn invoke(&self); } impl Invoke for S { - #[naked] + #[unsafe(naked)] extern "C" fn invoke(&self) { - unsafe { naked_asm!("") } + naked_asm!("") } } -#[naked] +#[unsafe(naked)] extern "C" fn ok() { - unsafe { naked_asm!("") } + naked_asm!("") } impl S { - #[naked] + #[unsafe(naked)] extern "C" fn g() { - unsafe { naked_asm!("") } + naked_asm!("") } - #[naked] + #[unsafe(naked)] extern "C" fn h(&self) { - unsafe { naked_asm!("") } + naked_asm!("") } } fn main() { - #[naked] //~ ERROR should be applied to a function definition + #[unsafe(naked)] //~ ERROR should be applied to a function definition || {}; } + +// Check that the path of an attribute without a name is printed correctly (issue #140082) +#[::a] +//~^ ERROR attribute incompatible with `#[unsafe(naked)]` +//~| ERROR failed to resolve: use of unresolved module or unlinked crate `a` +#[unsafe(naked)] +extern "C" fn issue_140082() { + naked_asm!("") +} diff --git a/tests/ui/asm/naked-invalid-attr.stderr b/tests/ui/asm/naked-invalid-attr.stderr index 640f9d9510d15..ef389e7d921b9 100644 --- a/tests/ui/asm/naked-invalid-attr.stderr +++ b/tests/ui/asm/naked-invalid-attr.stderr @@ -1,44 +1,70 @@ +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `a` + --> $DIR/naked-invalid-attr.rs:56:5 + | +LL | #[::a] + | ^ use of unresolved module or unlinked crate `a` + error: attribute should be applied to a function definition - --> $DIR/naked-invalid-attr.rs:14:1 + --> $DIR/naked-invalid-attr.rs:13:1 | -LL | #[naked] - | ^^^^^^^^ +LL | #[unsafe(naked)] + | ^^^^^^^^^^^^^^^^ LL | #[repr(C)] LL | / struct S { +LL | | #[unsafe(naked)] LL | | a: u32, LL | | b: u32, LL | | } | |_- not a function definition +error: attribute should be applied to a function definition + --> $DIR/naked-invalid-attr.rs:16:5 + | +LL | #[unsafe(naked)] + | ^^^^^^^^^^^^^^^^ +LL | a: u32, + | ------ not a function definition + error: attribute should be applied to a function definition --> $DIR/naked-invalid-attr.rs:51:5 | -LL | #[naked] - | ^^^^^^^^ +LL | #[unsafe(naked)] + | ^^^^^^^^^^^^^^^^ LL | || {}; | ----- not a function definition +error[E0736]: attribute incompatible with `#[unsafe(naked)]` + --> $DIR/naked-invalid-attr.rs:56:1 + | +LL | #[::a] + | ^^^^^^ the `{{root}}::a` attribute is incompatible with `#[unsafe(naked)]` +... +LL | #[unsafe(naked)] + | ---------------- function marked with `#[unsafe(naked)]` here + error: attribute should be applied to a function definition --> $DIR/naked-invalid-attr.rs:22:5 | -LL | #[naked] - | ^^^^^^^^ +LL | #[unsafe(naked)] + | ^^^^^^^^^^^^^^^^ LL | extern "C" fn invoke(&self); | ---------------------------- not a function definition error: attribute should be applied to a function definition - --> $DIR/naked-invalid-attr.rs:10:5 + --> $DIR/naked-invalid-attr.rs:9:5 | -LL | #[naked] - | ^^^^^^^^ +LL | #[unsafe(naked)] + | ^^^^^^^^^^^^^^^^ LL | fn f(); | ------- not a function definition error: attribute should be applied to a function definition - --> $DIR/naked-invalid-attr.rs:5:1 + --> $DIR/naked-invalid-attr.rs:4:1 | -LL | #![naked] - | ^^^^^^^^^ cannot be applied to crates +LL | #![unsafe(naked)] + | ^^^^^^^^^^^^^^^^^ cannot be applied to crates -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors +Some errors have detailed explanations: E0433, E0736. +For more information about an error, try `rustc --explain E0433`. diff --git a/tests/ui/asm/naked-with-invalid-repr-attr.rs b/tests/ui/asm/naked-with-invalid-repr-attr.rs index 18b9c1014c3fa..96eed70dc5504 100644 --- a/tests/ui/asm/naked-with-invalid-repr-attr.rs +++ b/tests/ui/asm/naked-with-invalid-repr-attr.rs @@ -1,48 +1,47 @@ //@ needs-asm-support -#![feature(naked_functions)] #![feature(fn_align)] #![crate_type = "lib"] use std::arch::naked_asm; #[repr(C)] //~^ ERROR attribute should be applied to a struct, enum, or union [E0517] -#[naked] +#[unsafe(naked)] extern "C" fn example1() { //~^ NOTE not a struct, enum, or union - unsafe { naked_asm!("") } + naked_asm!("") } #[repr(transparent)] //~^ ERROR attribute should be applied to a struct, enum, or union [E0517] -#[naked] +#[unsafe(naked)] extern "C" fn example2() { //~^ NOTE not a struct, enum, or union - unsafe { naked_asm!("") } + naked_asm!("") } #[repr(align(16), C)] //~^ ERROR attribute should be applied to a struct, enum, or union [E0517] -#[naked] +#[unsafe(naked)] extern "C" fn example3() { //~^ NOTE not a struct, enum, or union - unsafe { naked_asm!("") } + naked_asm!("") } // note: two errors because of packed and C #[repr(C, packed)] //~^ ERROR attribute should be applied to a struct or union [E0517] //~| ERROR attribute should be applied to a struct, enum, or union [E0517] -#[naked] +#[unsafe(naked)] extern "C" fn example4() { //~^ NOTE not a struct, enum, or union //~| NOTE not a struct or union - unsafe { naked_asm!("") } + naked_asm!("") } #[repr(u8)] //~^ ERROR attribute should be applied to an enum [E0517] -#[naked] +#[unsafe(naked)] extern "C" fn example5() { //~^ NOTE not an enum - unsafe { naked_asm!("") } + naked_asm!("") } diff --git a/tests/ui/asm/naked-with-invalid-repr-attr.stderr b/tests/ui/asm/naked-with-invalid-repr-attr.stderr index 8248a8c165791..f173a39e5bf6d 100644 --- a/tests/ui/asm/naked-with-invalid-repr-attr.stderr +++ b/tests/ui/asm/naked-with-invalid-repr-attr.stderr @@ -1,41 +1,41 @@ error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/naked-with-invalid-repr-attr.rs:7:8 + --> $DIR/naked-with-invalid-repr-attr.rs:6:8 | LL | #[repr(C)] | ^ ... LL | / extern "C" fn example1() { LL | | -LL | | unsafe { naked_asm!("") } +LL | | naked_asm!("") LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/naked-with-invalid-repr-attr.rs:15:8 + --> $DIR/naked-with-invalid-repr-attr.rs:14:8 | LL | #[repr(transparent)] | ^^^^^^^^^^^ ... LL | / extern "C" fn example2() { LL | | -LL | | unsafe { naked_asm!("") } +LL | | naked_asm!("") LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/naked-with-invalid-repr-attr.rs:23:19 + --> $DIR/naked-with-invalid-repr-attr.rs:22:19 | LL | #[repr(align(16), C)] | ^ ... LL | / extern "C" fn example3() { LL | | -LL | | unsafe { naked_asm!("") } +LL | | naked_asm!("") LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/naked-with-invalid-repr-attr.rs:32:8 + --> $DIR/naked-with-invalid-repr-attr.rs:31:8 | LL | #[repr(C, packed)] | ^ @@ -43,12 +43,12 @@ LL | #[repr(C, packed)] LL | / extern "C" fn example4() { LL | | LL | | -LL | | unsafe { naked_asm!("") } +LL | | naked_asm!("") LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct or union - --> $DIR/naked-with-invalid-repr-attr.rs:32:11 + --> $DIR/naked-with-invalid-repr-attr.rs:31:11 | LL | #[repr(C, packed)] | ^^^^^^ @@ -56,19 +56,19 @@ LL | #[repr(C, packed)] LL | / extern "C" fn example4() { LL | | LL | | -LL | | unsafe { naked_asm!("") } +LL | | naked_asm!("") LL | | } | |_- not a struct or union error[E0517]: attribute should be applied to an enum - --> $DIR/naked-with-invalid-repr-attr.rs:42:8 + --> $DIR/naked-with-invalid-repr-attr.rs:41:8 | LL | #[repr(u8)] | ^^ ... LL | / extern "C" fn example5() { LL | | -LL | | unsafe { naked_asm!("") } +LL | | naked_asm!("") LL | | } | |_- not an enum diff --git a/tests/ui/asm/named-asm-labels.rs b/tests/ui/asm/named-asm-labels.rs index 77831e679ed42..996fb82a944f2 100644 --- a/tests/ui/asm/named-asm-labels.rs +++ b/tests/ui/asm/named-asm-labels.rs @@ -10,8 +10,6 @@ // which causes less readable LLVM errors and in the worst cases causes ICEs // or segfaults based on system dependent behavior and codegen flags. -#![feature(naked_functions)] - use std::arch::{asm, global_asm, naked_asm}; #[no_mangle] @@ -175,9 +173,9 @@ fn main() { // Trigger on naked fns too, even though they can't be inlined, reusing a // label or LTO can cause labels to break -#[naked] +#[unsafe(naked)] pub extern "C" fn foo() -> i32 { - unsafe { naked_asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1) } + naked_asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1) //~^ ERROR avoid using named labels } @@ -188,21 +186,21 @@ pub extern "C" fn bar() { //~^ ERROR avoid using named labels } -#[naked] +#[unsafe(naked)] pub extern "C" fn aaa() { fn _local() {} - unsafe { naked_asm!(".Laaa: nop; ret;") } //~ ERROR avoid using named labels + naked_asm!(".Laaa: nop; ret;") //~ ERROR avoid using named labels } pub fn normal() { fn _local1() {} - #[naked] + #[unsafe(naked)] pub extern "C" fn bbb() { fn _very_local() {} - unsafe { naked_asm!(".Lbbb: nop; ret;") } //~ ERROR avoid using named labels + naked_asm!(".Lbbb: nop; ret;") //~ ERROR avoid using named labels } fn _local2() {} @@ -219,8 +217,8 @@ fn closures() { }; || { - #[naked] - unsafe extern "C" fn _nested() { + #[unsafe(naked)] + extern "C" fn _nested() { naked_asm!("ret;"); } diff --git a/tests/ui/asm/named-asm-labels.stderr b/tests/ui/asm/named-asm-labels.stderr index 44ce358c62bdb..cd7e7a08c1d29 100644 --- a/tests/ui/asm/named-asm-labels.stderr +++ b/tests/ui/asm/named-asm-labels.stderr @@ -1,5 +1,5 @@ error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:23:15 + --> $DIR/named-asm-labels.rs:21:15 | LL | asm!("bar: nop"); | ^^^ @@ -9,7 +9,7 @@ LL | asm!("bar: nop"); = note: `#[deny(named_asm_labels)]` on by default error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:26:15 + --> $DIR/named-asm-labels.rs:24:15 | LL | asm!("abcd:"); | ^^^^ @@ -18,7 +18,7 @@ LL | asm!("abcd:"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:29:15 + --> $DIR/named-asm-labels.rs:27:15 | LL | asm!("foo: bar1: nop"); | ^^^ @@ -27,7 +27,7 @@ LL | asm!("foo: bar1: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:29:20 + --> $DIR/named-asm-labels.rs:27:20 | LL | asm!("foo: bar1: nop"); | ^^^^ @@ -36,7 +36,7 @@ LL | asm!("foo: bar1: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:34:15 + --> $DIR/named-asm-labels.rs:32:15 | LL | asm!("foo1: nop", "nop"); | ^^^^ @@ -45,7 +45,7 @@ LL | asm!("foo1: nop", "nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:35:15 + --> $DIR/named-asm-labels.rs:33:15 | LL | asm!("foo2: foo3: nop", "nop"); | ^^^^ @@ -54,7 +54,7 @@ LL | asm!("foo2: foo3: nop", "nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:35:21 + --> $DIR/named-asm-labels.rs:33:21 | LL | asm!("foo2: foo3: nop", "nop"); | ^^^^ @@ -63,7 +63,7 @@ LL | asm!("foo2: foo3: nop", "nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:38:22 + --> $DIR/named-asm-labels.rs:36:22 | LL | asm!("nop", "foo4: nop"); | ^^^^ @@ -72,7 +72,7 @@ LL | asm!("nop", "foo4: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:39:15 + --> $DIR/named-asm-labels.rs:37:15 | LL | asm!("foo5: nop", "foo6: nop"); | ^^^^ @@ -81,7 +81,7 @@ LL | asm!("foo5: nop", "foo6: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:39:28 + --> $DIR/named-asm-labels.rs:37:28 | LL | asm!("foo5: nop", "foo6: nop"); | ^^^^ @@ -90,7 +90,7 @@ LL | asm!("foo5: nop", "foo6: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:44:15 + --> $DIR/named-asm-labels.rs:42:15 | LL | asm!("foo7: nop; foo8: nop"); | ^^^^ @@ -99,7 +99,7 @@ LL | asm!("foo7: nop; foo8: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:44:26 + --> $DIR/named-asm-labels.rs:42:26 | LL | asm!("foo7: nop; foo8: nop"); | ^^^^ @@ -108,7 +108,7 @@ LL | asm!("foo7: nop; foo8: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:47:15 + --> $DIR/named-asm-labels.rs:45:15 | LL | asm!("foo9: nop; nop"); | ^^^^ @@ -117,7 +117,7 @@ LL | asm!("foo9: nop; nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:48:20 + --> $DIR/named-asm-labels.rs:46:20 | LL | asm!("nop; foo10: nop"); | ^^^^^ @@ -126,7 +126,7 @@ LL | asm!("nop; foo10: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:51:15 + --> $DIR/named-asm-labels.rs:49:15 | LL | asm!("bar2: nop\n bar3: nop"); | ^^^^ @@ -135,7 +135,7 @@ LL | asm!("bar2: nop\n bar3: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:51:27 + --> $DIR/named-asm-labels.rs:49:27 | LL | asm!("bar2: nop\n bar3: nop"); | ^^^^ @@ -144,7 +144,7 @@ LL | asm!("bar2: nop\n bar3: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:54:15 + --> $DIR/named-asm-labels.rs:52:15 | LL | asm!("bar4: nop\n nop"); | ^^^^ @@ -153,7 +153,7 @@ LL | asm!("bar4: nop\n nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:55:21 + --> $DIR/named-asm-labels.rs:53:21 | LL | asm!("nop\n bar5: nop"); | ^^^^ @@ -162,7 +162,7 @@ LL | asm!("nop\n bar5: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:56:21 + --> $DIR/named-asm-labels.rs:54:21 | LL | asm!("nop\n bar6: bar7: nop"); | ^^^^ @@ -171,7 +171,7 @@ LL | asm!("nop\n bar6: bar7: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:56:27 + --> $DIR/named-asm-labels.rs:54:27 | LL | asm!("nop\n bar6: bar7: nop"); | ^^^^ @@ -180,7 +180,7 @@ LL | asm!("nop\n bar6: bar7: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:63:13 + --> $DIR/named-asm-labels.rs:61:13 | LL | blah2: nop | ^^^^^ @@ -189,7 +189,7 @@ LL | blah2: nop = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:64:13 + --> $DIR/named-asm-labels.rs:62:13 | LL | blah3: nop | ^^^^^ @@ -198,7 +198,7 @@ LL | blah3: nop = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:73:19 + --> $DIR/named-asm-labels.rs:71:19 | LL | nop ; blah4: nop | ^^^^^ @@ -207,7 +207,7 @@ LL | nop ; blah4: nop = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:87:15 + --> $DIR/named-asm-labels.rs:85:15 | LL | asm!("blah1: 2bar: nop"); | ^^^^^ @@ -216,7 +216,7 @@ LL | asm!("blah1: 2bar: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:90:15 + --> $DIR/named-asm-labels.rs:88:15 | LL | asm!("def: def: nop"); | ^^^ @@ -225,7 +225,7 @@ LL | asm!("def: def: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:90:15 + --> $DIR/named-asm-labels.rs:88:15 | LL | asm!("def: def: nop"); | ^^^ @@ -235,7 +235,7 @@ LL | asm!("def: def: nop"); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:93:15 + --> $DIR/named-asm-labels.rs:91:15 | LL | asm!("def: nop\ndef: nop"); | ^^^ @@ -244,7 +244,7 @@ LL | asm!("def: nop\ndef: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:93:15 + --> $DIR/named-asm-labels.rs:91:15 | LL | asm!("def: nop\ndef: nop"); | ^^^ @@ -254,7 +254,7 @@ LL | asm!("def: nop\ndef: nop"); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:96:15 + --> $DIR/named-asm-labels.rs:94:15 | LL | asm!("def: nop; def: nop"); | ^^^ @@ -263,7 +263,7 @@ LL | asm!("def: nop; def: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:96:15 + --> $DIR/named-asm-labels.rs:94:15 | LL | asm!("def: nop; def: nop"); | ^^^ @@ -273,7 +273,7 @@ LL | asm!("def: nop; def: nop"); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:106:15 + --> $DIR/named-asm-labels.rs:104:15 | LL | asm!("fooo\u{003A} nop"); | ^^^^^^^^^^^^^^^^ @@ -282,7 +282,7 @@ LL | asm!("fooo\u{003A} nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:107:15 + --> $DIR/named-asm-labels.rs:105:15 | LL | asm!("foooo\x3A nop"); | ^^^^^^^^^^^^^ @@ -291,7 +291,7 @@ LL | asm!("foooo\x3A nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:110:15 + --> $DIR/named-asm-labels.rs:108:15 | LL | asm!("fooooo:\u{000A} nop"); | ^^^^^^ @@ -300,7 +300,7 @@ LL | asm!("fooooo:\u{000A} nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:111:15 + --> $DIR/named-asm-labels.rs:109:15 | LL | asm!("foooooo:\x0A nop"); | ^^^^^^^ @@ -309,7 +309,7 @@ LL | asm!("foooooo:\x0A nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:115:14 + --> $DIR/named-asm-labels.rs:113:14 | LL | asm!("\x41\x42\x43\x3A\x20\x6E\x6F\x70"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -319,7 +319,7 @@ LL | asm!("\x41\x42\x43\x3A\x20\x6E\x6F\x70"); = note: the label may be declared in the expansion of a macro error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:123:13 + --> $DIR/named-asm-labels.rs:121:13 | LL | ab: nop // ab: does foo | ^^ @@ -328,7 +328,7 @@ LL | ab: nop // ab: does foo = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:144:19 + --> $DIR/named-asm-labels.rs:142:19 | LL | asm!("test_{}: nop", in(reg) 10); | ^^^^^^^ @@ -338,7 +338,7 @@ LL | asm!("test_{}: nop", in(reg) 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:146:15 + --> $DIR/named-asm-labels.rs:144:15 | LL | asm!("test_{}: nop", const 10); | ^^^^^^^ @@ -348,7 +348,7 @@ LL | asm!("test_{}: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:147:15 + --> $DIR/named-asm-labels.rs:145:15 | LL | asm!("test_{}: nop", sym main); | ^^^^^^^ @@ -358,7 +358,7 @@ LL | asm!("test_{}: nop", sym main); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:148:15 + --> $DIR/named-asm-labels.rs:146:15 | LL | asm!("{}_test: nop", const 10); | ^^^^^^^ @@ -368,7 +368,7 @@ LL | asm!("{}_test: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:149:15 + --> $DIR/named-asm-labels.rs:147:15 | LL | asm!("test_{}_test: nop", const 10); | ^^^^^^^^^^^^ @@ -378,7 +378,7 @@ LL | asm!("test_{}_test: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:150:15 + --> $DIR/named-asm-labels.rs:148:15 | LL | asm!("{}: nop", const 10); | ^^ @@ -388,7 +388,7 @@ LL | asm!("{}: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:152:15 + --> $DIR/named-asm-labels.rs:150:15 | LL | asm!("{uwu}: nop", uwu = const 10); | ^^^^^ @@ -398,7 +398,7 @@ LL | asm!("{uwu}: nop", uwu = const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:153:15 + --> $DIR/named-asm-labels.rs:151:15 | LL | asm!("{0}: nop", const 10); | ^^^ @@ -408,7 +408,7 @@ LL | asm!("{0}: nop", const 10); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:154:15 + --> $DIR/named-asm-labels.rs:152:15 | LL | asm!("{1}: nop", "/* {0} */", const 10, const 20); | ^^^ @@ -418,7 +418,7 @@ LL | asm!("{1}: nop", "/* {0} */", const 10, const 20); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:157:14 + --> $DIR/named-asm-labels.rs:155:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -428,7 +428,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: the label may be declared in the expansion of a macro error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:157:14 + --> $DIR/named-asm-labels.rs:155:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -439,7 +439,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:157:14 + --> $DIR/named-asm-labels.rs:155:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -450,7 +450,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:157:14 + --> $DIR/named-asm-labels.rs:155:14 | LL | asm!(include_str!("named-asm-labels.s")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -461,7 +461,7 @@ LL | asm!(include_str!("named-asm-labels.s")); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:171:19 + --> $DIR/named-asm-labels.rs:169:19 | LL | asm!("warned: nop"); | ^^^^^^ @@ -469,22 +469,22 @@ LL | asm!("warned: nop"); = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information note: the lint level is defined here - --> $DIR/named-asm-labels.rs:169:16 + --> $DIR/named-asm-labels.rs:167:16 | LL | #[warn(named_asm_labels)] | ^^^^^^^^^^^^^^^^ error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:180:26 + --> $DIR/named-asm-labels.rs:178:17 | -LL | unsafe { naked_asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1) } - | ^^^^^ +LL | naked_asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1) + | ^^^^^ | = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:187:20 + --> $DIR/named-asm-labels.rs:185:20 | LL | unsafe { asm!(".Lbar: mov rax, {}; ret;", "nop", const 1, options(noreturn)) } | ^^^^^ @@ -493,25 +493,25 @@ LL | unsafe { asm!(".Lbar: mov rax, {}; ret;", "nop", const 1, options(noret = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:195:26 + --> $DIR/named-asm-labels.rs:193:17 | -LL | unsafe { naked_asm!(".Laaa: nop; ret;") } - | ^^^^^ +LL | naked_asm!(".Laaa: nop; ret;") + | ^^^^^ | = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:205:30 + --> $DIR/named-asm-labels.rs:203:21 | -LL | unsafe { naked_asm!(".Lbbb: nop; ret;") } - | ^^^^^ +LL | naked_asm!(".Lbbb: nop; ret;") + | ^^^^^ | = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:214:15 + --> $DIR/named-asm-labels.rs:212:15 | LL | asm!("closure1: nop"); | ^^^^^^^^ @@ -520,7 +520,7 @@ LL | asm!("closure1: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:218:15 + --> $DIR/named-asm-labels.rs:216:15 | LL | asm!("closure2: nop"); | ^^^^^^^^ @@ -529,7 +529,7 @@ LL | asm!("closure2: nop"); = note: see the asm section of Rust By Example for more information error: avoid using named labels in inline assembly - --> $DIR/named-asm-labels.rs:228:19 + --> $DIR/named-asm-labels.rs:226:19 | LL | asm!("closure3: nop"); | ^^^^^^^^ diff --git a/tests/ui/asm/named_const_simd_vec_len.rs b/tests/ui/asm/named_const_simd_vec_len.rs index 7df4d922d5c91..7fedeb7d4d122 100644 --- a/tests/ui/asm/named_const_simd_vec_len.rs +++ b/tests/ui/asm/named_const_simd_vec_len.rs @@ -3,6 +3,9 @@ //@ only-x86_64 //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver #![feature(repr_simd)] diff --git a/tests/ui/asm/normalizable-asm-ty.rs b/tests/ui/asm/normalizable-asm-ty.rs new file mode 100644 index 0000000000000..c1f3f3ecd6168 --- /dev/null +++ b/tests/ui/asm/normalizable-asm-ty.rs @@ -0,0 +1,16 @@ +//@ check-pass +//@ needs-asm-support +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +fn invoke(pc_section: &[usize]) { + unsafe { + std::arch::asm!( + "/* {} */", + in(reg) pc_section[0] + ); + } +} + +fn main() {} diff --git a/tests/ui/asm/parse-error.rs b/tests/ui/asm/parse-error.rs index 4d7b522f5fc5b..d135ccae12804 100644 --- a/tests/ui/asm/parse-error.rs +++ b/tests/ui/asm/parse-error.rs @@ -113,11 +113,9 @@ global_asm!("", options(FOO)); global_asm!("", options(FOO,)); //~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO` global_asm!("", options(nomem FOO)); -//~^ ERROR the `nomem` option cannot be used with `global_asm!` -//~| ERROR expected one of `)` or `,`, found `FOO` +//~^ ERROR expected one of `)` or `,`, found `FOO` global_asm!("", options(nomem, FOO)); -//~^ ERROR the `nomem` option cannot be used with `global_asm!` -//~| ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO` +//~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO` global_asm!("{}", options(), const FOO); global_asm!("", clobber_abi(FOO)); //~^ ERROR expected string literal diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index 74647372a3557..0bba1fd8d9b64 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -270,74 +270,62 @@ error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` LL | global_asm!("", options(FOO,)); | ^^^ expected one of `)`, `att_syntax`, or `raw` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:115:25 - | -LL | global_asm!("", options(nomem FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)` or `,`, found `FOO` --> $DIR/parse-error.rs:115:31 | LL | global_asm!("", options(nomem FOO)); | ^^^ expected one of `)` or `,` -error: the `nomem` option cannot be used with `global_asm!` - --> $DIR/parse-error.rs:118:25 - | -LL | global_asm!("", options(nomem, FOO)); - | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly - error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:118:32 + --> $DIR/parse-error.rs:117:32 | LL | global_asm!("", options(nomem, FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: expected string literal - --> $DIR/parse-error.rs:122:29 + --> $DIR/parse-error.rs:120:29 | LL | global_asm!("", clobber_abi(FOO)); | ^^^ not a string literal error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:124:33 + --> $DIR/parse-error.rs:122:33 | LL | global_asm!("", clobber_abi("C" FOO)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:126:34 + --> $DIR/parse-error.rs:124:34 | LL | global_asm!("", clobber_abi("C", FOO)); | ^^^ not a string literal error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:128:19 + --> $DIR/parse-error.rs:126:19 | LL | global_asm!("{}", clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:130:28 + --> $DIR/parse-error.rs:128:28 | LL | global_asm!("", options(), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:132:30 + --> $DIR/parse-error.rs:130:30 | LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:134:17 + --> $DIR/parse-error.rs:132:17 | LL | global_asm!("", clobber_abi("C"), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ error: duplicate argument named `a` - --> $DIR/parse-error.rs:136:35 + --> $DIR/parse-error.rs:134:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -345,7 +333,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | previously here error: argument never used - --> $DIR/parse-error.rs:136:35 + --> $DIR/parse-error.rs:134:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ^^^^^^^^^^^^^ argument never used @@ -353,19 +341,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:139:28 + --> $DIR/parse-error.rs:137:28 | LL | global_asm!("", options(), ""); | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:141:30 + --> $DIR/parse-error.rs:139:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal - --> $DIR/parse-error.rs:143:13 + --> $DIR/parse-error.rs:141:13 | LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ @@ -373,7 +361,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:145:20 + --> $DIR/parse-error.rs:143:20 | LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ @@ -381,37 +369,37 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: the `in` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:148:19 + --> $DIR/parse-error.rs:146:19 | LL | global_asm!("{}", in(reg)); | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `out` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:150:19 + --> $DIR/parse-error.rs:148:19 | LL | global_asm!("{}", out(reg)); | ^^^ the `out` operand is not meaningful for global-scoped inline assembly, remove it error: the `lateout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:152:19 + --> $DIR/parse-error.rs:150:19 | LL | global_asm!("{}", lateout(reg)); | ^^^^^^^ the `lateout` operand is not meaningful for global-scoped inline assembly, remove it error: the `inout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:154:19 + --> $DIR/parse-error.rs:152:19 | LL | global_asm!("{}", inout(reg)); | ^^^^^ the `inout` operand is not meaningful for global-scoped inline assembly, remove it error: the `inlateout` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:156:19 + --> $DIR/parse-error.rs:154:19 | LL | global_asm!("{}", inlateout(reg)); | ^^^^^^^^^ the `inlateout` operand is not meaningful for global-scoped inline assembly, remove it error: the `label` operand cannot be used with `global_asm!` - --> $DIR/parse-error.rs:158:19 + --> $DIR/parse-error.rs:156:19 | LL | global_asm!("{}", label(reg)); | ^^^^^ the `label` operand is not meaningful for global-scoped inline assembly, remove it @@ -476,6 +464,6 @@ LL - let mut bar = 0; LL + const bar: /* Type */ = 0; | -error: aborting due to 72 previous errors +error: aborting due to 70 previous errors For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/asm/riscv/riscv32e-registers.riscv32e.stderr b/tests/ui/asm/riscv/riscv32e-registers.riscv32e.stderr index 07c1bf211839b..5d527cd70b81a 100644 --- a/tests/ui/asm/riscv/riscv32e-registers.riscv32e.stderr +++ b/tests/ui/asm/riscv/riscv32e-registers.riscv32e.stderr @@ -1,5 +1,5 @@ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:54:11 + --> $DIR/riscv32e-registers.rs:42:11 | LL | asm!("li x16, 0"); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | li x16, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:57:11 + --> $DIR/riscv32e-registers.rs:45:11 | LL | asm!("li x17, 0"); | ^^^^^^^^^ @@ -23,7 +23,7 @@ LL | li x17, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:60:11 + --> $DIR/riscv32e-registers.rs:48:11 | LL | asm!("li x18, 0"); | ^^^^^^^^^ @@ -35,7 +35,7 @@ LL | li x18, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:63:11 + --> $DIR/riscv32e-registers.rs:51:11 | LL | asm!("li x19, 0"); | ^^^^^^^^^ @@ -47,7 +47,7 @@ LL | li x19, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:66:11 + --> $DIR/riscv32e-registers.rs:54:11 | LL | asm!("li x20, 0"); | ^^^^^^^^^ @@ -59,7 +59,7 @@ LL | li x20, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:69:11 + --> $DIR/riscv32e-registers.rs:57:11 | LL | asm!("li x21, 0"); | ^^^^^^^^^ @@ -71,7 +71,7 @@ LL | li x21, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:72:11 + --> $DIR/riscv32e-registers.rs:60:11 | LL | asm!("li x22, 0"); | ^^^^^^^^^ @@ -83,7 +83,7 @@ LL | li x22, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:75:11 + --> $DIR/riscv32e-registers.rs:63:11 | LL | asm!("li x23, 0"); | ^^^^^^^^^ @@ -95,7 +95,7 @@ LL | li x23, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:78:11 + --> $DIR/riscv32e-registers.rs:66:11 | LL | asm!("li x24, 0"); | ^^^^^^^^^ @@ -107,7 +107,7 @@ LL | li x24, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:81:11 + --> $DIR/riscv32e-registers.rs:69:11 | LL | asm!("li x25, 0"); | ^^^^^^^^^ @@ -119,7 +119,7 @@ LL | li x25, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:84:11 + --> $DIR/riscv32e-registers.rs:72:11 | LL | asm!("li x26, 0"); | ^^^^^^^^^ @@ -131,7 +131,7 @@ LL | li x26, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:87:11 + --> $DIR/riscv32e-registers.rs:75:11 | LL | asm!("li x27, 0"); | ^^^^^^^^^ @@ -143,7 +143,7 @@ LL | li x27, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:90:11 + --> $DIR/riscv32e-registers.rs:78:11 | LL | asm!("li x28, 0"); | ^^^^^^^^^ @@ -155,7 +155,7 @@ LL | li x28, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:93:11 + --> $DIR/riscv32e-registers.rs:81:11 | LL | asm!("li x29, 0"); | ^^^^^^^^^ @@ -167,7 +167,7 @@ LL | li x29, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:96:11 + --> $DIR/riscv32e-registers.rs:84:11 | LL | asm!("li x30, 0"); | ^^^^^^^^^ @@ -179,7 +179,7 @@ LL | li x30, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:99:11 + --> $DIR/riscv32e-registers.rs:87:11 | LL | asm!("li x31, 0"); | ^^^^^^^^^ diff --git a/tests/ui/asm/riscv/riscv32e-registers.riscv32e_llvm_18.stderr b/tests/ui/asm/riscv/riscv32e-registers.riscv32e_llvm_18.stderr deleted file mode 100644 index 59009b8c3525c..0000000000000 --- a/tests/ui/asm/riscv/riscv32e-registers.riscv32e_llvm_18.stderr +++ /dev/null @@ -1,194 +0,0 @@ -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:54:11 - | -LL | asm!("li x16, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x16, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:57:11 - | -LL | asm!("li x17, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x17, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:60:11 - | -LL | asm!("li x18, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x18, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:63:11 - | -LL | asm!("li x19, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x19, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:66:11 - | -LL | asm!("li x20, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x20, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:69:11 - | -LL | asm!("li x21, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x21, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:72:11 - | -LL | asm!("li x22, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x22, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:75:11 - | -LL | asm!("li x23, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x23, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:78:11 - | -LL | asm!("li x24, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x24, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:81:11 - | -LL | asm!("li x25, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x25, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:84:11 - | -LL | asm!("li x26, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x26, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:87:11 - | -LL | asm!("li x27, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x27, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:90:11 - | -LL | asm!("li x28, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x28, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:93:11 - | -LL | asm!("li x29, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x29, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:96:11 - | -LL | asm!("li x30, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x30, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:99:11 - | -LL | asm!("li x31, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x31, 0 - | ^ - -error: aborting due to 16 previous errors - diff --git a/tests/ui/asm/riscv/riscv32e-registers.riscv32em.stderr b/tests/ui/asm/riscv/riscv32e-registers.riscv32em.stderr index 07c1bf211839b..5d527cd70b81a 100644 --- a/tests/ui/asm/riscv/riscv32e-registers.riscv32em.stderr +++ b/tests/ui/asm/riscv/riscv32e-registers.riscv32em.stderr @@ -1,5 +1,5 @@ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:54:11 + --> $DIR/riscv32e-registers.rs:42:11 | LL | asm!("li x16, 0"); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | li x16, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:57:11 + --> $DIR/riscv32e-registers.rs:45:11 | LL | asm!("li x17, 0"); | ^^^^^^^^^ @@ -23,7 +23,7 @@ LL | li x17, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:60:11 + --> $DIR/riscv32e-registers.rs:48:11 | LL | asm!("li x18, 0"); | ^^^^^^^^^ @@ -35,7 +35,7 @@ LL | li x18, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:63:11 + --> $DIR/riscv32e-registers.rs:51:11 | LL | asm!("li x19, 0"); | ^^^^^^^^^ @@ -47,7 +47,7 @@ LL | li x19, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:66:11 + --> $DIR/riscv32e-registers.rs:54:11 | LL | asm!("li x20, 0"); | ^^^^^^^^^ @@ -59,7 +59,7 @@ LL | li x20, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:69:11 + --> $DIR/riscv32e-registers.rs:57:11 | LL | asm!("li x21, 0"); | ^^^^^^^^^ @@ -71,7 +71,7 @@ LL | li x21, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:72:11 + --> $DIR/riscv32e-registers.rs:60:11 | LL | asm!("li x22, 0"); | ^^^^^^^^^ @@ -83,7 +83,7 @@ LL | li x22, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:75:11 + --> $DIR/riscv32e-registers.rs:63:11 | LL | asm!("li x23, 0"); | ^^^^^^^^^ @@ -95,7 +95,7 @@ LL | li x23, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:78:11 + --> $DIR/riscv32e-registers.rs:66:11 | LL | asm!("li x24, 0"); | ^^^^^^^^^ @@ -107,7 +107,7 @@ LL | li x24, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:81:11 + --> $DIR/riscv32e-registers.rs:69:11 | LL | asm!("li x25, 0"); | ^^^^^^^^^ @@ -119,7 +119,7 @@ LL | li x25, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:84:11 + --> $DIR/riscv32e-registers.rs:72:11 | LL | asm!("li x26, 0"); | ^^^^^^^^^ @@ -131,7 +131,7 @@ LL | li x26, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:87:11 + --> $DIR/riscv32e-registers.rs:75:11 | LL | asm!("li x27, 0"); | ^^^^^^^^^ @@ -143,7 +143,7 @@ LL | li x27, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:90:11 + --> $DIR/riscv32e-registers.rs:78:11 | LL | asm!("li x28, 0"); | ^^^^^^^^^ @@ -155,7 +155,7 @@ LL | li x28, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:93:11 + --> $DIR/riscv32e-registers.rs:81:11 | LL | asm!("li x29, 0"); | ^^^^^^^^^ @@ -167,7 +167,7 @@ LL | li x29, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:96:11 + --> $DIR/riscv32e-registers.rs:84:11 | LL | asm!("li x30, 0"); | ^^^^^^^^^ @@ -179,7 +179,7 @@ LL | li x30, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:99:11 + --> $DIR/riscv32e-registers.rs:87:11 | LL | asm!("li x31, 0"); | ^^^^^^^^^ diff --git a/tests/ui/asm/riscv/riscv32e-registers.riscv32em_llvm_18.stderr b/tests/ui/asm/riscv/riscv32e-registers.riscv32em_llvm_18.stderr deleted file mode 100644 index 59009b8c3525c..0000000000000 --- a/tests/ui/asm/riscv/riscv32e-registers.riscv32em_llvm_18.stderr +++ /dev/null @@ -1,194 +0,0 @@ -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:54:11 - | -LL | asm!("li x16, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x16, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:57:11 - | -LL | asm!("li x17, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x17, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:60:11 - | -LL | asm!("li x18, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x18, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:63:11 - | -LL | asm!("li x19, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x19, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:66:11 - | -LL | asm!("li x20, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x20, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:69:11 - | -LL | asm!("li x21, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x21, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:72:11 - | -LL | asm!("li x22, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x22, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:75:11 - | -LL | asm!("li x23, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x23, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:78:11 - | -LL | asm!("li x24, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x24, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:81:11 - | -LL | asm!("li x25, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x25, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:84:11 - | -LL | asm!("li x26, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x26, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:87:11 - | -LL | asm!("li x27, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x27, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:90:11 - | -LL | asm!("li x28, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x28, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:93:11 - | -LL | asm!("li x29, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x29, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:96:11 - | -LL | asm!("li x30, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x30, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:99:11 - | -LL | asm!("li x31, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x31, 0 - | ^ - -error: aborting due to 16 previous errors - diff --git a/tests/ui/asm/riscv/riscv32e-registers.riscv32emc.stderr b/tests/ui/asm/riscv/riscv32e-registers.riscv32emc.stderr index 07c1bf211839b..5d527cd70b81a 100644 --- a/tests/ui/asm/riscv/riscv32e-registers.riscv32emc.stderr +++ b/tests/ui/asm/riscv/riscv32e-registers.riscv32emc.stderr @@ -1,5 +1,5 @@ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:54:11 + --> $DIR/riscv32e-registers.rs:42:11 | LL | asm!("li x16, 0"); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | li x16, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:57:11 + --> $DIR/riscv32e-registers.rs:45:11 | LL | asm!("li x17, 0"); | ^^^^^^^^^ @@ -23,7 +23,7 @@ LL | li x17, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:60:11 + --> $DIR/riscv32e-registers.rs:48:11 | LL | asm!("li x18, 0"); | ^^^^^^^^^ @@ -35,7 +35,7 @@ LL | li x18, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:63:11 + --> $DIR/riscv32e-registers.rs:51:11 | LL | asm!("li x19, 0"); | ^^^^^^^^^ @@ -47,7 +47,7 @@ LL | li x19, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:66:11 + --> $DIR/riscv32e-registers.rs:54:11 | LL | asm!("li x20, 0"); | ^^^^^^^^^ @@ -59,7 +59,7 @@ LL | li x20, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:69:11 + --> $DIR/riscv32e-registers.rs:57:11 | LL | asm!("li x21, 0"); | ^^^^^^^^^ @@ -71,7 +71,7 @@ LL | li x21, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:72:11 + --> $DIR/riscv32e-registers.rs:60:11 | LL | asm!("li x22, 0"); | ^^^^^^^^^ @@ -83,7 +83,7 @@ LL | li x22, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:75:11 + --> $DIR/riscv32e-registers.rs:63:11 | LL | asm!("li x23, 0"); | ^^^^^^^^^ @@ -95,7 +95,7 @@ LL | li x23, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:78:11 + --> $DIR/riscv32e-registers.rs:66:11 | LL | asm!("li x24, 0"); | ^^^^^^^^^ @@ -107,7 +107,7 @@ LL | li x24, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:81:11 + --> $DIR/riscv32e-registers.rs:69:11 | LL | asm!("li x25, 0"); | ^^^^^^^^^ @@ -119,7 +119,7 @@ LL | li x25, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:84:11 + --> $DIR/riscv32e-registers.rs:72:11 | LL | asm!("li x26, 0"); | ^^^^^^^^^ @@ -131,7 +131,7 @@ LL | li x26, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:87:11 + --> $DIR/riscv32e-registers.rs:75:11 | LL | asm!("li x27, 0"); | ^^^^^^^^^ @@ -143,7 +143,7 @@ LL | li x27, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:90:11 + --> $DIR/riscv32e-registers.rs:78:11 | LL | asm!("li x28, 0"); | ^^^^^^^^^ @@ -155,7 +155,7 @@ LL | li x28, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:93:11 + --> $DIR/riscv32e-registers.rs:81:11 | LL | asm!("li x29, 0"); | ^^^^^^^^^ @@ -167,7 +167,7 @@ LL | li x29, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:96:11 + --> $DIR/riscv32e-registers.rs:84:11 | LL | asm!("li x30, 0"); | ^^^^^^^^^ @@ -179,7 +179,7 @@ LL | li x30, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:99:11 + --> $DIR/riscv32e-registers.rs:87:11 | LL | asm!("li x31, 0"); | ^^^^^^^^^ diff --git a/tests/ui/asm/riscv/riscv32e-registers.riscv32emc_llvm_18.stderr b/tests/ui/asm/riscv/riscv32e-registers.riscv32emc_llvm_18.stderr deleted file mode 100644 index 59009b8c3525c..0000000000000 --- a/tests/ui/asm/riscv/riscv32e-registers.riscv32emc_llvm_18.stderr +++ /dev/null @@ -1,194 +0,0 @@ -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:54:11 - | -LL | asm!("li x16, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x16, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:57:11 - | -LL | asm!("li x17, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x17, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:60:11 - | -LL | asm!("li x18, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x18, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:63:11 - | -LL | asm!("li x19, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x19, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:66:11 - | -LL | asm!("li x20, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x20, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:69:11 - | -LL | asm!("li x21, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x21, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:72:11 - | -LL | asm!("li x22, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x22, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:75:11 - | -LL | asm!("li x23, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x23, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:78:11 - | -LL | asm!("li x24, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x24, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:81:11 - | -LL | asm!("li x25, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x25, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:84:11 - | -LL | asm!("li x26, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x26, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:87:11 - | -LL | asm!("li x27, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x27, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:90:11 - | -LL | asm!("li x28, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x28, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:93:11 - | -LL | asm!("li x29, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x29, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:96:11 - | -LL | asm!("li x30, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x30, 0 - | ^ - -error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:99:11 - | -LL | asm!("li x31, 0"); - | ^ - | -note: instantiated into assembly here - --> :1:5 - | -LL | li x31, 0 - | ^ - -error: aborting due to 16 previous errors - diff --git a/tests/ui/asm/riscv/riscv32e-registers.rs b/tests/ui/asm/riscv/riscv32e-registers.rs index 99cbdf5ead3f8..96bfe2af06448 100644 --- a/tests/ui/asm/riscv/riscv32e-registers.rs +++ b/tests/ui/asm/riscv/riscv32e-registers.rs @@ -2,27 +2,15 @@ // //@ add-core-stubs //@ build-fail -//@ revisions: riscv32e riscv32em riscv32emc riscv32e_llvm_18 riscv32em_llvm_18 riscv32emc_llvm_18 +//@ revisions: riscv32e riscv32em riscv32emc // //@ compile-flags: --crate-type=rlib //@ [riscv32e] needs-llvm-components: riscv //@ [riscv32e] compile-flags: --target=riscv32e-unknown-none-elf -//@ [riscv32e] min-llvm-version: 19 //@ [riscv32em] needs-llvm-components: riscv //@ [riscv32em] compile-flags: --target=riscv32em-unknown-none-elf -//@ [riscv32em] min-llvm-version: 19 //@ [riscv32emc] needs-llvm-components: riscv //@ [riscv32emc] compile-flags: --target=riscv32emc-unknown-none-elf -//@ [riscv32emc] min-llvm-version: 19 -//@ [riscv32e_llvm_18] needs-llvm-components: riscv -//@ [riscv32e_llvm_18] compile-flags: --target=riscv32e-unknown-none-elf -//@ [riscv32e_llvm_18] ignore-llvm-version: 19 - 99 -//@ [riscv32em_llvm_18] needs-llvm-components: riscv -//@ [riscv32em_llvm_18] compile-flags: --target=riscv32em-unknown-none-elf -//@ [riscv32em_llvm_18] ignore-llvm-version: 19 - 99 -//@ [riscv32emc_llvm_18] needs-llvm-components: riscv -//@ [riscv32emc_llvm_18] compile-flags: --target=riscv32emc-unknown-none-elf -//@ [riscv32emc_llvm_18] ignore-llvm-version: 19 - 99 // Unlike bad-reg.rs, this tests if the assembler can reject invalid registers // usage in assembly code. diff --git a/tests/ui/asm/simple_global_asm.rs b/tests/ui/asm/simple_global_asm.rs index 9b193b3e44ce6..68b0b83858e18 100644 --- a/tests/ui/asm/simple_global_asm.rs +++ b/tests/ui/asm/simple_global_asm.rs @@ -1,7 +1,6 @@ //@ run-pass //@ needs-asm-support -#![feature(naked_functions)] #![allow(dead_code)] #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] diff --git a/tests/ui/asm/tainting-on-error.rs b/tests/ui/asm/tainting-on-error.rs new file mode 100644 index 0000000000000..a8e9af911580d --- /dev/null +++ b/tests/ui/asm/tainting-on-error.rs @@ -0,0 +1,13 @@ +//@ needs-asm-support + +use std::arch::asm; + +fn main() { + unsafe { + asm!( + "/* {} */", + sym None::<()>, + //~^ ERROR invalid `sym` operand + ); + } +} diff --git a/tests/ui/asm/tainting-on-error.stderr b/tests/ui/asm/tainting-on-error.stderr new file mode 100644 index 0000000000000..bd706d1f310bf --- /dev/null +++ b/tests/ui/asm/tainting-on-error.stderr @@ -0,0 +1,10 @@ +error: invalid `sym` operand + --> $DIR/tainting-on-error.rs:9:13 + | +LL | sym None::<()>, + | ^^^^^^^^^^^^^^ is an `Option<()>` + | + = help: `sym` operands must refer to either a function or a static + +error: aborting due to 1 previous error + diff --git a/tests/ui/asm/type-check-4.rs b/tests/ui/asm/type-check-4.rs index a5b5e29294bb8..97b1ab74a7ecf 100644 --- a/tests/ui/asm/type-check-4.rs +++ b/tests/ui/asm/type-check-4.rs @@ -11,7 +11,7 @@ fn main() { let mut a = 0isize; let p = &a; asm!("{}", out(reg) a); - //~^ cannot assign to `a` because it is borrowed + //~^ ERROR cannot assign to `a` because it is borrowed println!("{}", p); // Can't read from mutable borrowed values. @@ -19,7 +19,7 @@ fn main() { let mut a = 0isize; let p = &mut a; asm!("{}", in(reg) a); - //~^ cannot use `a` because it was mutably borrowed + //~^ ERROR cannot use `a` because it was mutably borrowed println!("{}", p); } } diff --git a/tests/ui/asm/unpretty-expanded.rs b/tests/ui/asm/unpretty-expanded.rs index 1da2c7704f41f..3eb46fe4889c6 100644 --- a/tests/ui/asm/unpretty-expanded.rs +++ b/tests/ui/asm/unpretty-expanded.rs @@ -1,4 +1,5 @@ //@ needs-asm-support //@ check-pass //@ compile-flags: -Zunpretty=expanded +//@ edition: 2015 core::arch::global_asm!("x: .byte 42"); diff --git a/tests/ui/asm/unpretty-expanded.stdout b/tests/ui/asm/unpretty-expanded.stdout index 80ccd127d506d..7ba1702dfed8e 100644 --- a/tests/ui/asm/unpretty-expanded.stdout +++ b/tests/ui/asm/unpretty-expanded.stdout @@ -7,4 +7,5 @@ extern crate std; //@ needs-asm-support //@ check-pass //@ compile-flags: -Zunpretty=expanded +//@ edition: 2015 global_asm! ("x: .byte 42"); diff --git a/tests/ui/asm/x86_64/bad-options.rs b/tests/ui/asm/x86_64/bad-options.rs index 6424a1b1d421b..123febc06fc95 100644 --- a/tests/ui/asm/x86_64/bad-options.rs +++ b/tests/ui/asm/x86_64/bad-options.rs @@ -1,6 +1,6 @@ //@ only-x86_64 -#![feature(asm_unwind, asm_goto)] +#![feature(asm_unwind)] use std::arch::{asm, global_asm}; diff --git a/tests/ui/asm/x86_64/evex512-implicit-feature.rs b/tests/ui/asm/x86_64/evex512-implicit-feature.rs index ea2acd424e2c6..ec5da7c7fa488 100644 --- a/tests/ui/asm/x86_64/evex512-implicit-feature.rs +++ b/tests/ui/asm/x86_64/evex512-implicit-feature.rs @@ -2,7 +2,6 @@ //@ only-x86_64 //@ compile-flags: --crate-type=lib -C target-cpu=skylake -#![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] use std::arch::x86_64::*; diff --git a/tests/ui/asm/x86_64/goto-block-safe.rs b/tests/ui/asm/x86_64/goto-block-safe.rs index ee833a48a4b19..b739e9f9ced8d 100644 --- a/tests/ui/asm/x86_64/goto-block-safe.rs +++ b/tests/ui/asm/x86_64/goto-block-safe.rs @@ -2,7 +2,6 @@ //@ needs-asm-support #![deny(unreachable_code)] -#![feature(asm_goto)] use std::arch::asm; diff --git a/tests/ui/asm/x86_64/goto-block-safe.stderr b/tests/ui/asm/x86_64/goto-block-safe.stderr index 49818db7484e5..ee7313bc8be3f 100644 --- a/tests/ui/asm/x86_64/goto-block-safe.stderr +++ b/tests/ui/asm/x86_64/goto-block-safe.stderr @@ -1,5 +1,5 @@ error[E0133]: call to unsafe function `unreachable_unchecked` is unsafe and requires unsafe function or block - --> $DIR/goto-block-safe.rs:14:17 + --> $DIR/goto-block-safe.rs:13:17 | LL | unsafe { | ------ items do not inherit unsafety from separate enclosing items diff --git a/tests/ui/asm/x86_64/goto.rs b/tests/ui/asm/x86_64/goto.rs index 50e7441509ac2..00a8e588f9673 100644 --- a/tests/ui/asm/x86_64/goto.rs +++ b/tests/ui/asm/x86_64/goto.rs @@ -3,7 +3,7 @@ //@ needs-asm-support #![deny(unreachable_code)] -#![feature(asm_goto, asm_goto_with_outputs)] +#![feature(asm_goto_with_outputs)] use std::arch::asm; diff --git a/tests/ui/asm/x86_64/goto.stderr b/tests/ui/asm/x86_64/goto.stderr index 8f89e2b129081..78b726b3f3d31 100644 --- a/tests/ui/asm/x86_64/goto.stderr +++ b/tests/ui/asm/x86_64/goto.stderr @@ -17,7 +17,6 @@ note: the lint level is defined here | LL | #[warn(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this warning originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) warning: 1 warning emitted diff --git a/tests/ui/asm/x86_64/issue-82869.rs b/tests/ui/asm/x86_64/issue-82869.rs index 5d3f417f7337b..448ebd9c99f45 100644 --- a/tests/ui/asm/x86_64/issue-82869.rs +++ b/tests/ui/asm/x86_64/issue-82869.rs @@ -12,9 +12,9 @@ pub unsafe fn aarch64(a: f64, b: f64) -> f64 { || {}; b }); - //~^^^^ invalid register class - //~^^^^^ invalid register class - //~^^^^^^ invalid register + //~^^^^ ERROR invalid register class + //~^^^^^ ERROR invalid register class + //~^^^^^^ ERROR invalid register c } diff --git a/tests/ui/asm/x86_64/srcloc.new.stderr b/tests/ui/asm/x86_64/srcloc.new.stderr deleted file mode 100644 index 7211f1ab69dc0..0000000000000 --- a/tests/ui/asm/x86_64/srcloc.new.stderr +++ /dev/null @@ -1,332 +0,0 @@ -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:14:15 - | -LL | asm!("invalid_instruction"); - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:18:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :3:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:23:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :3:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:29:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :4:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:36:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :4:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:41:14 - | -LL | asm!(concat!("invalid", "_", "instruction")); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -warning: scale factor without index register is ignored - --> $DIR/srcloc.rs:44:15 - | -LL | asm!("movaps %xmm3, (%esi, 2)", options(att_syntax)); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :1:23 - | -LL | movaps %xmm3, (%esi, 2) - | ^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:48:14 - | -LL | "invalid_instruction", - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:54:14 - | -LL | "invalid_instruction", - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:61:14 - | -LL | "invalid_instruction", - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :4:1 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:68:13 - | -LL | concat!("invalid", "_", "instruction"), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:75:13 - | -LL | concat!("invalid", "_", "instruction"), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction1' - --> $DIR/srcloc.rs:82:14 - | -LL | "invalid_instruction1", - | ^^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction1 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction2' - --> $DIR/srcloc.rs:83:14 - | -LL | "invalid_instruction2", - | ^^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction2 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction1' - --> $DIR/srcloc.rs:89:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction1", "\n", -LL | | "invalid", "_", "instruction2", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction1 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction2' - --> $DIR/srcloc.rs:89:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction1", "\n", -LL | | "invalid", "_", "instruction2", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction2 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction1' - --> $DIR/srcloc.rs:98:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction1", "\n", -LL | | "invalid", "_", "instruction2", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction1 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction2' - --> $DIR/srcloc.rs:98:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction1", "\n", -LL | | "invalid", "_", "instruction2", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction2 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction3' - --> $DIR/srcloc.rs:102:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction3", "\n", -LL | | "invalid", "_", "instruction4", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :4:1 - | -LL | invalid_instruction3 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction4' - --> $DIR/srcloc.rs:102:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction3", "\n", -LL | | "invalid", "_", "instruction4", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :5:1 - | -LL | invalid_instruction4 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction1' - --> $DIR/srcloc.rs:113:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction1", "\n", -LL | | "invalid", "_", "instruction2", "\n", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction1 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction2' - --> $DIR/srcloc.rs:113:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction1", "\n", -LL | | "invalid", "_", "instruction2", "\n", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction2 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction3' - --> $DIR/srcloc.rs:117:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction3", "\n", -LL | | "invalid", "_", "instruction4", "\n", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :5:1 - | -LL | invalid_instruction3 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction4' - --> $DIR/srcloc.rs:117:13 - | -LL | / concat!( -LL | | "invalid", "_", "instruction3", "\n", -LL | | "invalid", "_", "instruction4", "\n", -LL | | ), - | |_____________^ - | -note: instantiated into assembly here - --> :6:1 - | -LL | invalid_instruction4 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:130:14 - | -LL | "invalid_instruction" - | ^^^^^^^^^^^^^^^^^^^ - | -note: instantiated into assembly here - --> :5:1 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 24 previous errors; 1 warning emitted - diff --git a/tests/ui/asm/x86_64/srcloc.old.stderr b/tests/ui/asm/x86_64/srcloc.old.stderr deleted file mode 100644 index edb9ee46812f8..0000000000000 --- a/tests/ui/asm/x86_64/srcloc.old.stderr +++ /dev/null @@ -1,302 +0,0 @@ -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:14:15 - | -LL | asm!("invalid_instruction"); - | ^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:18:13 - | -LL | invalid_instruction - | ^ - | -note: instantiated into assembly here - --> :3:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:23:13 - | -LL | invalid_instruction - | ^ - | -note: instantiated into assembly here - --> :3:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:29:13 - | -LL | invalid_instruction - | ^ - | -note: instantiated into assembly here - --> :4:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:36:13 - | -LL | invalid_instruction - | ^ - | -note: instantiated into assembly here - --> :4:13 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:41:14 - | -LL | asm!(concat!("invalid", "_", "instruction")); - | ^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -warning: scale factor without index register is ignored - --> $DIR/srcloc.rs:44:15 - | -LL | asm!("movaps %xmm3, (%esi, 2)", options(att_syntax)); - | ^ - | -note: instantiated into assembly here - --> :1:23 - | -LL | movaps %xmm3, (%esi, 2) - | ^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:48:14 - | -LL | "invalid_instruction", - | ^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:54:14 - | -LL | "invalid_instruction", - | ^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:61:14 - | -LL | "invalid_instruction", - | ^ - | -note: instantiated into assembly here - --> :4:1 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:68:13 - | -LL | concat!("invalid", "_", "instruction"), - | ^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:75:13 - | -LL | concat!("invalid", "_", "instruction"), - | ^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction1' - --> $DIR/srcloc.rs:82:14 - | -LL | "invalid_instruction1", - | ^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction1 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction2' - --> $DIR/srcloc.rs:83:14 - | -LL | "invalid_instruction2", - | ^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction2 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction1' - --> $DIR/srcloc.rs:89:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction1 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction2' - --> $DIR/srcloc.rs:89:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction2 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction1' - --> $DIR/srcloc.rs:98:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction1 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction2' - --> $DIR/srcloc.rs:98:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction2 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction3' - --> $DIR/srcloc.rs:102:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :4:1 - | -LL | invalid_instruction3 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction4' - --> $DIR/srcloc.rs:102:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :5:1 - | -LL | invalid_instruction4 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction1' - --> $DIR/srcloc.rs:113:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :2:2 - | -LL | invalid_instruction1 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction2' - --> $DIR/srcloc.rs:113:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :3:1 - | -LL | invalid_instruction2 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction3' - --> $DIR/srcloc.rs:117:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :5:1 - | -LL | invalid_instruction3 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction4' - --> $DIR/srcloc.rs:117:13 - | -LL | concat!( - | ^ - | -note: instantiated into assembly here - --> :6:1 - | -LL | invalid_instruction4 - | ^^^^^^^^^^^^^^^^^^^^ - -error: invalid instruction mnemonic 'invalid_instruction' - --> $DIR/srcloc.rs:130:14 - | -LL | "invalid_instruction" - | ^ - | -note: instantiated into assembly here - --> :5:1 - | -LL | invalid_instruction - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 24 previous errors; 1 warning emitted - diff --git a/tests/ui/asm/x86_64/srcloc.rs b/tests/ui/asm/x86_64/srcloc.rs index 40fc66cbc929c..2938bafe5e70c 100644 --- a/tests/ui/asm/x86_64/srcloc.rs +++ b/tests/ui/asm/x86_64/srcloc.rs @@ -1,9 +1,6 @@ -//@ revisions: old new //@ only-x86_64 //@ build-fail //@ compile-flags: -Ccodegen-units=1 -//@[old] ignore-llvm-version: 19 - 99 -//@[new] min-llvm-version: 19 use std::arch::asm; diff --git a/tests/ui/asm/x86_64/srcloc.stderr b/tests/ui/asm/x86_64/srcloc.stderr new file mode 100644 index 0000000000000..bb4e855163d77 --- /dev/null +++ b/tests/ui/asm/x86_64/srcloc.stderr @@ -0,0 +1,332 @@ +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:11:15 + | +LL | asm!("invalid_instruction"); + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:15:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :3:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:20:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :3:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:26:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :4:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:33:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :4:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:38:14 + | +LL | asm!(concat!("invalid", "_", "instruction")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +warning: scale factor without index register is ignored + --> $DIR/srcloc.rs:41:15 + | +LL | asm!("movaps %xmm3, (%esi, 2)", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:23 + | +LL | movaps %xmm3, (%esi, 2) + | ^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:45:14 + | +LL | "invalid_instruction", + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:51:14 + | +LL | "invalid_instruction", + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:58:14 + | +LL | "invalid_instruction", + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :4:1 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:65:13 + | +LL | concat!("invalid", "_", "instruction"), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:72:13 + | +LL | concat!("invalid", "_", "instruction"), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction1' + --> $DIR/srcloc.rs:79:14 + | +LL | "invalid_instruction1", + | ^^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction1 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction2' + --> $DIR/srcloc.rs:80:14 + | +LL | "invalid_instruction2", + | ^^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction2 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction1' + --> $DIR/srcloc.rs:86:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction1", "\n", +LL | | "invalid", "_", "instruction2", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction1 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction2' + --> $DIR/srcloc.rs:86:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction1", "\n", +LL | | "invalid", "_", "instruction2", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction2 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction1' + --> $DIR/srcloc.rs:95:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction1", "\n", +LL | | "invalid", "_", "instruction2", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction1 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction2' + --> $DIR/srcloc.rs:95:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction1", "\n", +LL | | "invalid", "_", "instruction2", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction2 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction3' + --> $DIR/srcloc.rs:99:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction3", "\n", +LL | | "invalid", "_", "instruction4", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :4:1 + | +LL | invalid_instruction3 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction4' + --> $DIR/srcloc.rs:99:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction3", "\n", +LL | | "invalid", "_", "instruction4", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :5:1 + | +LL | invalid_instruction4 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction1' + --> $DIR/srcloc.rs:110:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction1", "\n", +LL | | "invalid", "_", "instruction2", "\n", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction1 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction2' + --> $DIR/srcloc.rs:110:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction1", "\n", +LL | | "invalid", "_", "instruction2", "\n", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction2 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction3' + --> $DIR/srcloc.rs:114:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction3", "\n", +LL | | "invalid", "_", "instruction4", "\n", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :5:1 + | +LL | invalid_instruction3 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction4' + --> $DIR/srcloc.rs:114:13 + | +LL | / concat!( +LL | | "invalid", "_", "instruction3", "\n", +LL | | "invalid", "_", "instruction4", "\n", +LL | | ), + | |_____________^ + | +note: instantiated into assembly here + --> :6:1 + | +LL | invalid_instruction4 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:127:14 + | +LL | "invalid_instruction" + | ^^^^^^^^^^^^^^^^^^^ + | +note: instantiated into assembly here + --> :5:1 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 24 previous errors; 1 warning emitted + diff --git a/tests/ui/asm/x86_64/target-feature-attr.rs b/tests/ui/asm/x86_64/target-feature-attr.rs index 6bb277ac16598..2193117caeb6d 100644 --- a/tests/ui/asm/x86_64/target-feature-attr.rs +++ b/tests/ui/asm/x86_64/target-feature-attr.rs @@ -2,8 +2,6 @@ // Set the base cpu explicitly, in case the default has been changed. //@ compile-flags: -C target-cpu=x86-64 -#![feature(avx512_target_feature)] - use std::arch::asm; #[target_feature(enable = "avx")] diff --git a/tests/ui/asm/x86_64/target-feature-attr.stderr b/tests/ui/asm/x86_64/target-feature-attr.stderr index 0cd571ac8cce8..c852726ee7ff8 100644 --- a/tests/ui/asm/x86_64/target-feature-attr.stderr +++ b/tests/ui/asm/x86_64/target-feature-attr.stderr @@ -1,23 +1,23 @@ error: register class `ymm_reg` requires the `avx` target feature - --> $DIR/target-feature-attr.rs:20:40 + --> $DIR/target-feature-attr.rs:18:40 | LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x); | ^^^^^^^^^^^^^ error: register class `ymm_reg` requires the `avx` target feature - --> $DIR/target-feature-attr.rs:20:55 + --> $DIR/target-feature-attr.rs:18:55 | LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x); | ^^^^^^^^^^^^^ error: register class `ymm_reg` requires the `avx` target feature - --> $DIR/target-feature-attr.rs:20:70 + --> $DIR/target-feature-attr.rs:18:70 | LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x); | ^^^^^^^^^^^^^^^^^^ error: register class `kreg` requires at least one of the following target features: avx512bw, avx512f - --> $DIR/target-feature-attr.rs:35:23 + --> $DIR/target-feature-attr.rs:33:23 | LL | asm!("/* {0} */", in(kreg) x); | ^^^^^^^^^^ diff --git a/tests/ui/asm/x86_64/type-check-2.rs b/tests/ui/asm/x86_64/type-check-2.rs index 1650c595faebb..c63042298da30 100644 --- a/tests/ui/asm/x86_64/type-check-2.rs +++ b/tests/ui/asm/x86_64/type-check-2.rs @@ -7,7 +7,7 @@ use std::arch::{asm, global_asm}; #[repr(simd)] struct SimdNonCopy([f32; 4]); -fn main() { +fn test1() { unsafe { // Inputs must be initialized @@ -26,7 +26,11 @@ fn main() { asm!("{}", in(reg) v[0]); asm!("{}", out(reg) v[0]); asm!("{}", inout(reg) v[0]); + } +} +fn test2() { + unsafe { // Register operands must be Copy asm!("{}", in(xmm_reg) SimdNonCopy([0.0, 0.0, 0.0, 0.0])); @@ -68,3 +72,5 @@ fn main() { asm!("{}", in(reg) u); } } + +fn main() {} diff --git a/tests/ui/asm/x86_64/type-check-2.stderr b/tests/ui/asm/x86_64/type-check-2.stderr index 8b1bfa85fa2c4..d5c5a3ff1f843 100644 --- a/tests/ui/asm/x86_64/type-check-2.stderr +++ b/tests/ui/asm/x86_64/type-check-2.stderr @@ -1,13 +1,13 @@ error: arguments for inline assembly must be copyable - --> $DIR/type-check-2.rs:32:32 + --> $DIR/type-check-2.rs:36:32 | LL | asm!("{}", in(xmm_reg) SimdNonCopy([0.0, 0.0, 0.0, 0.0])); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `SimdNonCopy` does not implement the Copy trait -error: cannot use value of type `{closure@$DIR/type-check-2.rs:44:28: 44:36}` for inline assembly - --> $DIR/type-check-2.rs:44:28 +error: cannot use value of type `{closure@$DIR/type-check-2.rs:48:28: 48:36}` for inline assembly + --> $DIR/type-check-2.rs:48:28 | LL | asm!("{}", in(reg) |x: i32| x); | ^^^^^^^^^^ @@ -15,7 +15,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 `Vec` for inline assembly - --> $DIR/type-check-2.rs:46:28 + --> $DIR/type-check-2.rs:50:28 | LL | asm!("{}", in(reg) vec![0]); | ^^^^^^^ @@ -24,7 +24,7 @@ LL | asm!("{}", in(reg) vec![0]); = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot use value of type `(i32, i32, i32)` for inline assembly - --> $DIR/type-check-2.rs:48:28 + --> $DIR/type-check-2.rs:52:28 | LL | asm!("{}", in(reg) (1, 2, 3)); | ^^^^^^^^^ @@ -32,7 +32,7 @@ LL | asm!("{}", in(reg) (1, 2, 3)); = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly error: cannot use value of type `[i32; 3]` for inline assembly - --> $DIR/type-check-2.rs:50:28 + --> $DIR/type-check-2.rs:54:28 | LL | asm!("{}", in(reg) [1, 2, 3]); | ^^^^^^^^^ @@ -40,7 +40,7 @@ LL | asm!("{}", in(reg) [1, 2, 3]); = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly error: cannot use value of type `fn() {main}` for inline assembly - --> $DIR/type-check-2.rs:58:31 + --> $DIR/type-check-2.rs:62:31 | LL | asm!("{}", inout(reg) f); | ^ @@ -48,7 +48,7 @@ LL | asm!("{}", inout(reg) f); = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly error: cannot use value of type `&mut i32` for inline assembly - --> $DIR/type-check-2.rs:61:31 + --> $DIR/type-check-2.rs:65:31 | LL | asm!("{}", inout(reg) r); | ^ diff --git a/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr b/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr index a8256f775a68c..19b63a041d616 100644 --- a/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr +++ b/tests/ui/associated-consts/associated-const-type-parameter-pattern.stderr @@ -27,7 +27,7 @@ LL | B::X => println!("B::X"), | ^^^^ `const` depends on a generic parameter error[E0158]: constant pattern cannot depend on generic parameters - --> $DIR/associated-const-type-parameter-pattern.rs:30:9 + --> $DIR/associated-const-type-parameter-pattern.rs:28:48 | LL | pub trait Foo { | ------------- @@ -35,13 +35,12 @@ LL | const X: EFoo; | ------------- constant defined here ... LL | pub fn test_let_pat(arg: EFoo, A::X: EFoo) { - | - constant depends on this generic parameter -LL | -LL | let A::X = arg; - | ^^^^ `const` depends on a generic parameter + | - ^^^^ `const` depends on a generic parameter + | | + | constant depends on this generic parameter error[E0158]: constant pattern cannot depend on generic parameters - --> $DIR/associated-const-type-parameter-pattern.rs:28:48 + --> $DIR/associated-const-type-parameter-pattern.rs:30:9 | LL | pub trait Foo { | ------------- @@ -49,9 +48,10 @@ LL | const X: EFoo; | ------------- constant defined here ... LL | pub fn test_let_pat(arg: EFoo, A::X: EFoo) { - | - ^^^^ `const` depends on a generic parameter - | | - | constant depends on this generic parameter + | - constant depends on this generic parameter +LL | +LL | let A::X = arg; + | ^^^^ `const` depends on a generic parameter error: aborting due to 4 previous errors diff --git a/tests/ui/associated-consts/defaults-cyclic-fail.rs b/tests/ui/associated-consts/defaults-cyclic-fail.rs index b868ef310041e..cc3b60b30e5d8 100644 --- a/tests/ui/associated-consts/defaults-cyclic-fail.rs +++ b/tests/ui/associated-consts/defaults-cyclic-fail.rs @@ -3,7 +3,7 @@ // Cyclic assoc. const defaults don't error unless *used* trait Tr { const A: u8 = Self::B; - //~^ cycle detected + //~^ ERROR cycle detected const B: u8 = Self::A; } diff --git a/tests/ui/associated-consts/defaults-not-assumed-fail.rs b/tests/ui/associated-consts/defaults-not-assumed-fail.rs index 3dc709cf633a7..830fd4ab0e975 100644 --- a/tests/ui/associated-consts/defaults-not-assumed-fail.rs +++ b/tests/ui/associated-consts/defaults-not-assumed-fail.rs @@ -1,4 +1,5 @@ //@ build-fail +//@ dont-require-annotations: NOTE trait Tr { const A: u8 = 255; @@ -31,7 +32,7 @@ impl Tr for u32 { fn main() { assert_eq!(<() as Tr>::A, 255); assert_eq!(<() as Tr>::B, 0); // causes the error above - //~^ constant + //~^ NOTE constant assert_eq!(::A, 254); assert_eq!(::B, 255); diff --git a/tests/ui/associated-consts/defaults-not-assumed-fail.stderr b/tests/ui/associated-consts/defaults-not-assumed-fail.stderr index 4b53603cfe851..3386e81dc98f4 100644 --- a/tests/ui/associated-consts/defaults-not-assumed-fail.stderr +++ b/tests/ui/associated-consts/defaults-not-assumed-fail.stderr @@ -1,17 +1,17 @@ error[E0080]: evaluation of `<() as Tr>::B` failed - --> $DIR/defaults-not-assumed-fail.rs:8:19 + --> $DIR/defaults-not-assumed-fail.rs:9:19 | LL | const B: u8 = Self::A + 1; | ^^^^^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow note: erroneous constant encountered - --> $DIR/defaults-not-assumed-fail.rs:33:16 + --> $DIR/defaults-not-assumed-fail.rs:34:16 | LL | assert_eq!(<() as Tr>::B, 0); // causes the error above | ^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/defaults-not-assumed-fail.rs:33:16 + --> $DIR/defaults-not-assumed-fail.rs:34:16 | LL | assert_eq!(<() as Tr>::B, 0); // causes the error above | ^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | assert_eq!(<() as Tr>::B, 0); // causes the error above = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: erroneous constant encountered - --> $DIR/defaults-not-assumed-fail.rs:33:5 + --> $DIR/defaults-not-assumed-fail.rs:34:5 | LL | assert_eq!(<() as Tr>::B, 0); // causes the error above | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | assert_eq!(<() as Tr>::B, 0); // causes the error above = note: this note originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/defaults-not-assumed-fail.rs:33:5 + --> $DIR/defaults-not-assumed-fail.rs:34:5 | LL | assert_eq!(<() as Tr>::B, 0); // causes the error above | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/associated-consts/issue-93835.rs b/tests/ui/associated-consts/issue-93835.rs index 048681f047789..d6c2acaa9edce 100644 --- a/tests/ui/associated-consts/issue-93835.rs +++ b/tests/ui/associated-consts/issue-93835.rs @@ -3,11 +3,10 @@ fn e() { type_ascribe!(p, a>); //~^ ERROR cannot find type `a` in this scope - //~| ERROR path separator must be a double colon //~| ERROR cannot find value //~| ERROR associated const equality + //~| ERROR cannot find trait `p` in this scope //~| ERROR associated const equality - //~| ERROR failed to resolve: use of unresolved module or unlinked crate `p` } fn main() {} diff --git a/tests/ui/associated-consts/issue-93835.stderr b/tests/ui/associated-consts/issue-93835.stderr index e154ae25de26c..551b50d0eb6b5 100644 --- a/tests/ui/associated-consts/issue-93835.stderr +++ b/tests/ui/associated-consts/issue-93835.stderr @@ -1,15 +1,3 @@ -error: path separator must be a double colon - --> $DIR/issue-93835.rs:4:25 - | -LL | type_ascribe!(p, a>); - | ^ - | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 -help: use a double colon instead - | -LL | type_ascribe!(p, a>); - | + - error[E0425]: cannot find value `p` in this scope --> $DIR/issue-93835.rs:4:19 | @@ -22,6 +10,12 @@ error[E0412]: cannot find type `a` in this scope LL | type_ascribe!(p, a>); | ^ not found in this scope +error[E0405]: cannot find trait `p` in this scope + --> $DIR/issue-93835.rs:4:26 + | +LL | type_ascribe!(p, a>); + | ^ not found in this scope + error[E0658]: associated const equality is incomplete --> $DIR/issue-93835.rs:4:28 | @@ -43,15 +37,7 @@ LL | type_ascribe!(p, a>); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0433]: failed to resolve: use of unresolved module or unlinked crate `p` - --> $DIR/issue-93835.rs:4:24 - | -LL | type_ascribe!(p, a>); - | ^ use of unresolved module or unlinked crate `p` - | - = help: you might be missing a crate named `p` - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors -Some errors have detailed explanations: E0412, E0425, E0433, E0658. -For more information about an error, try `rustc --explain E0412`. +Some errors have detailed explanations: E0405, E0412, E0425, E0658. +For more information about an error, try `rustc --explain E0405`. diff --git a/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr deleted file mode 100644 index 81ace4ebb6daf..0000000000000 --- a/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0277]: the size for values of type `[u32]` cannot be known at compilation time - --> $DIR/wf-check-skipped.rs:17:25 - | -LL | fn main() -> Foo::Bar::> {} - | ^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `[u32]` -note: required by an implicit `Sized` bound in `Vec` - --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs index 52df4efd13e91..2949007d770ae 100644 --- a/tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs +++ b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs @@ -1,8 +1,8 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) -//@[current] known-bug: #100041 -//@[current] check-pass +//@ known-bug: #100041 +//@ check-pass // FIXME(inherent_associated_types): This should fail. #![feature(inherent_associated_types)] @@ -15,4 +15,3 @@ impl Foo { } fn main() -> Foo::Bar::> {} -//[next]~^ ERROR the size for values of type `[u32]` cannot be known at compilation time diff --git a/tests/ui/associated-inherent-types/constrain_opaque_types_during_projection.rs b/tests/ui/associated-inherent-types/constrain_opaque_types_during_projection.rs index 292733cd49239..929fa354cd09d 100644 --- a/tests/ui/associated-inherent-types/constrain_opaque_types_during_projection.rs +++ b/tests/ui/associated-inherent-types/constrain_opaque_types_during_projection.rs @@ -11,7 +11,8 @@ impl Foo { type Tait = impl Sized; -fn bar(_: Tait) { +#[define_opaque(Tait)] +fn bar() { let x: Foo::Assoc = 42; } diff --git a/tests/ui/associated-inherent-types/generic-associated-types-bad.rs b/tests/ui/associated-inherent-types/generic-associated-types-bad.rs index fdc2a0f64e491..fd3281cefeef0 100644 --- a/tests/ui/associated-inherent-types/generic-associated-types-bad.rs +++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.rs @@ -13,8 +13,8 @@ impl Ty { } #[cfg(item)] -const _: Ty::Pr = String::new(); //[item]~ the trait bound `String: Copy` is not satisfied -//[item]~^ the trait bound `String: Copy` is not satisfied +const _: Ty::Pr = String::new(); //[item]~ ERROR the trait bound `String: Copy` is not satisfied +//[item]~^ ERROR the trait bound `String: Copy` is not satisfied fn main() { #[cfg(local)] diff --git a/tests/ui/associated-inherent-types/issue-109299-1.stderr b/tests/ui/associated-inherent-types/issue-109299-1.stderr index 07a00b6b9a95f..940ccd7e400b5 100644 --- a/tests/ui/associated-inherent-types/issue-109299-1.stderr +++ b/tests/ui/associated-inherent-types/issue-109299-1.stderr @@ -2,7 +2,7 @@ error[E0220]: associated type `Cursor` not found for `Lexer` in the current s --> $DIR/issue-109299-1.rs:10:40 | LL | struct Lexer(T); - | --------------- associated item `Cursor` not found for this struct + | --------------- associated type `Cursor` not found for this struct ... LL | type X = impl for Fn() -> Lexer::Cursor; | ^^^^^^ associated item not found in `Lexer` @@ -14,7 +14,7 @@ error[E0220]: associated type `Cursor` not found for `Lexer` in the current s --> $DIR/issue-109299-1.rs:10:40 | LL | struct Lexer(T); - | --------------- associated item `Cursor` not found for this struct + | --------------- associated type `Cursor` not found for this struct ... LL | type X = impl for Fn() -> Lexer::Cursor; | ^^^^^^ associated item not found in `Lexer` @@ -29,7 +29,7 @@ error: unconstrained opaque type LL | type X = impl for Fn() -> Lexer::Cursor; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `X` must be used in combination with a concrete type within the same module + = note: `X` must be used in combination with a concrete type within the same crate error: aborting due to 3 previous errors diff --git a/tests/ui/associated-inherent-types/issue-109789.rs b/tests/ui/associated-inherent-types/issue-109789.rs index 46dd4590141d0..e3c490b2dc842 100644 --- a/tests/ui/associated-inherent-types/issue-109789.rs +++ b/tests/ui/associated-inherent-types/issue-109789.rs @@ -20,5 +20,6 @@ fn bar(_: Foo fn(&'a ())>::Assoc) {} //~| ERROR mismatched types //~| ERROR higher-ranked subtype error //~| ERROR higher-ranked subtype error +//~| ERROR higher-ranked subtype error fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-109789.stderr b/tests/ui/associated-inherent-types/issue-109789.stderr index c6ea6c5541d23..db860a64826d6 100644 --- a/tests/ui/associated-inherent-types/issue-109789.stderr +++ b/tests/ui/associated-inherent-types/issue-109789.stderr @@ -31,6 +31,14 @@ LL | fn bar(_: Foo fn(&'a ())>::Assoc) {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 4 previous errors +error: higher-ranked subtype error + --> $DIR/issue-109789.rs:18:1 + | +LL | fn bar(_: Foo fn(&'a ())>::Assoc) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-inherent-types/issue-111404-1.rs b/tests/ui/associated-inherent-types/issue-111404-1.rs index 3255bf20ebd1b..cad6d48b1c5af 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.rs +++ b/tests/ui/associated-inherent-types/issue-111404-1.rs @@ -12,5 +12,6 @@ fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} //~| ERROR mismatched types [E0308] //~| ERROR higher-ranked subtype error //~| ERROR higher-ranked subtype error +//~| ERROR higher-ranked subtype error fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-111404-1.stderr b/tests/ui/associated-inherent-types/issue-111404-1.stderr index 5074c877a8ee8..ce49126c316c2 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.stderr +++ b/tests/ui/associated-inherent-types/issue-111404-1.stderr @@ -31,6 +31,14 @@ LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 4 previous errors +error: higher-ranked subtype error + --> $DIR/issue-111404-1.rs:10:1 + | +LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr index 9bd5a842fdc7c..a247d6d5b45bd 100644 --- a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr @@ -2,7 +2,7 @@ error[E0220]: associated type `Pr` not found for `S` in the current scope --> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:23 | LL | struct S(T); - | ----------- associated item `Pr` not found for this struct + | ----------- associated type `Pr` not found for this struct ... LL | let _: S::::Pr = (); | ^^ associated item not found in `S` diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs.stderr index 1871407c64fe1..6194c6ff4d70b 100644 --- a/tests/ui/associated-inherent-types/not-found-self-type-differs.stderr +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs.stderr @@ -2,7 +2,7 @@ error[E0220]: associated type `Proj` not found for `Family>` in the c --> $DIR/not-found-self-type-differs.rs:15:32 | LL | struct Family(T); - | ---------------- associated item `Proj` not found for this struct + | ---------------- associated type `Proj` not found for this struct ... LL | let _: Family>::Proj; | ^^^^ associated item not found in `Family>` @@ -15,7 +15,7 @@ error[E0220]: associated type `Proj` not found for `Family` in the curr --> $DIR/not-found-self-type-differs.rs:16:40 | LL | struct Family(T); - | ---------------- associated item `Proj` not found for this struct + | ---------------- associated type `Proj` not found for this struct ... LL | let _: Family::Proj = (); | ^^^^ associated item not found in `Family` diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr index 736579067615a..dd51192e06ff1 100644 --- a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr @@ -2,7 +2,7 @@ error: the associated type `Yield` exists for `Container<[u8]>`, but its trait b --> $DIR/not-found-unsatisfied-bounds-0.rs:19:29 | LL | struct Container(T); - | --------------------------- associated item `Yield` not found for this struct + | --------------------------- associated type `Yield` not found for this struct ... LL | let _: Container<[u8]>::Yield = 1; | ^^^^^ associated type cannot be referenced on `Container<[u8]>` due to unsatisfied trait bounds @@ -14,7 +14,7 @@ error: the associated type `Combination` exists for `Duple>`, bu --> $DIR/not-found-unsatisfied-bounds-0.rs:20:45 | LL | struct Duple(T, U); - | ------------------ associated item `Combination` not found for this struct + | ------------------ associated type `Combination` not found for this struct ... LL | let _: Duple>::Combination; | ^^^^^^^^^^^ associated type cannot be referenced on `Duple>` due to unsatisfied trait bounds diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr index 0d5f781dc6334..c27e1f49faabd 100644 --- a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr @@ -5,7 +5,7 @@ LL | let _: Container::Proj = String::new(); | ^^^^ associated type cannot be referenced on `Container` due to unsatisfied trait bounds ... LL | struct Container(T); - | ------------------- associated item `Proj` not found for this struct + | ------------------- associated type `Proj` not found for this struct | = note: the following trait bounds were not satisfied: `T: Clone` diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr index 1613af6b8b152..db6c71a626ff7 100644 --- a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr @@ -2,7 +2,7 @@ error: the associated type `X` exists for `S`, but its --> $DIR/not-found-unsatisfied-bounds-in-multiple-impls.rs:19:43 | LL | struct S(A, B); - | -------------- associated item `X` not found for this struct + | -------------- associated type `X` not found for this struct LL | struct Featureless; | ------------------ doesn't satisfy `Featureless: One` or `Featureless: Two` ... diff --git a/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.current.stderr b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.current.stderr new file mode 100644 index 0000000000000..64304be9d6b15 --- /dev/null +++ b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.current.stderr @@ -0,0 +1,20 @@ +error[E0283]: type annotations needed + --> $DIR/dedup-normalized-2-higher-ranked.rs:28:5 + | +LL | impls(rigid); + | ^^^^^ cannot infer type of the type parameter `U` declared on the function `impls` + | + = note: cannot satisfy `for<'b>

    ::Rigid: Bound<'b, _>` +note: required by a bound in `impls` + --> $DIR/dedup-normalized-2-higher-ranked.rs:25:13 + | +LL | fn impls Bound<'b, U>, U>(_: T) {} + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `impls` +help: consider specifying the generic arguments + | +LL | impls::<

    ::Rigid, U>(rigid); + | ++++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.rs b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.rs index 9224d47d30fd6..32b8c689248fb 100644 --- a/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.rs +++ b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.rs @@ -1,3 +1,8 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + // We try to prove `for<'b> T::Rigid: Bound<'b, ?0>` and have 2 candidates from where-clauses: // // - `for<'a> Bound<'a, String>` @@ -21,7 +26,7 @@ fn impls Bound<'b, U>, U>(_: T) {} fn test(rigid: P::Rigid) { impls(rigid); - //~^ ERROR type annotations needed + //[current]~^ ERROR type annotations needed } fn main() {} diff --git a/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.stderr b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.stderr deleted file mode 100644 index 372d379de5a4f..0000000000000 --- a/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error[E0283]: type annotations needed - --> $DIR/dedup-normalized-2-higher-ranked.rs:23:5 - | -LL | impls(rigid); - | ^^^^^ cannot infer type of the type parameter `U` declared on the function `impls` - | - = note: cannot satisfy `for<'b>

    ::Rigid: Bound<'b, _>` -note: required by a bound in `impls` - --> $DIR/dedup-normalized-2-higher-ranked.rs:20:13 - | -LL | fn impls Bound<'b, U>, U>(_: T) {} - | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `impls` -help: consider specifying the generic arguments - | -LL | impls::<

    ::Rigid, U>(rigid); - | ++++++++++++++++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/associated-type-bounds/dont-imply-atb-in-closure-inference.rs b/tests/ui/associated-type-bounds/dont-imply-atb-in-closure-inference.rs index fecb3b153386b..1cfbefb9daa65 100644 --- a/tests/ui/associated-type-bounds/dont-imply-atb-in-closure-inference.rs +++ b/tests/ui/associated-type-bounds/dont-imply-atb-in-closure-inference.rs @@ -11,10 +11,8 @@ impl IsPtr for T { type Tait = impl IsPtr + Fn(u32); -fn hello() -where - Tait:, -{ +#[define_opaque(Tait)] +fn hello() { let _: Tait = |x| {}; } diff --git a/tests/ui/associated-type-bounds/duplicate.rs b/tests/ui/associated-type-bounds/duplicate.rs index 2b4a01376d779..e9d94787e9829 100644 --- a/tests/ui/associated-type-bounds/duplicate.rs +++ b/tests/ui/associated-type-bounds/duplicate.rs @@ -179,19 +179,25 @@ where type ETAI1> = impl Copy; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] +//~| ERROR unconstrained opaque type type ETAI2> = impl Copy; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] +//~| ERROR unconstrained opaque type type ETAI3> = impl Copy; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] +//~| ERROR unconstrained opaque type type ETAI4 = impl Iterator; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] +//~| ERROR unconstrained opaque type type ETAI5 = impl Iterator; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] +//~| ERROR unconstrained opaque type type ETAI6 = impl Iterator; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] +//~| ERROR unconstrained opaque type trait TRI1> {} //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] diff --git a/tests/ui/associated-type-bounds/duplicate.stderr b/tests/ui/associated-type-bounds/duplicate.stderr index 0dabcbdce1bbf..1ce212a9ff3eb 100644 --- a/tests/ui/associated-type-bounds/duplicate.stderr +++ b/tests/ui/associated-type-bounds/duplicate.stderr @@ -325,7 +325,7 @@ LL | type ETAI1> = impl Copy; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:182:36 + --> $DIR/duplicate.rs:183:36 | LL | type ETAI2> = impl Copy; | ---------- ^^^^^^^^^^ re-bound here @@ -333,7 +333,7 @@ LL | type ETAI2> = impl Copy; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:184:39 + --> $DIR/duplicate.rs:186:39 | LL | type ETAI3> = impl Copy; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -341,7 +341,7 @@ LL | type ETAI3> = impl Copy; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:186:40 + --> $DIR/duplicate.rs:189:40 | LL | type ETAI4 = impl Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -349,7 +349,7 @@ LL | type ETAI4 = impl Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:186:40 + --> $DIR/duplicate.rs:189:40 | LL | type ETAI4 = impl Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -359,7 +359,7 @@ LL | type ETAI4 = impl Iterator; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:189:40 + --> $DIR/duplicate.rs:193:40 | LL | type ETAI5 = impl Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -367,7 +367,7 @@ LL | type ETAI5 = impl Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:189:40 + --> $DIR/duplicate.rs:193:40 | LL | type ETAI5 = impl Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -377,7 +377,7 @@ LL | type ETAI5 = impl Iterator; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:192:43 + --> $DIR/duplicate.rs:197:43 | LL | type ETAI6 = impl Iterator; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -385,7 +385,7 @@ LL | type ETAI6 = impl Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:192:43 + --> $DIR/duplicate.rs:197:43 | LL | type ETAI6 = impl Iterator; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -395,7 +395,7 @@ LL | type ETAI6 = impl Iterator; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:196:36 + --> $DIR/duplicate.rs:202:36 | LL | trait TRI1> {} | ---------- ^^^^^^^^^^ re-bound here @@ -403,7 +403,7 @@ LL | trait TRI1> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:198:36 + --> $DIR/duplicate.rs:204:36 | LL | trait TRI2> {} | ---------- ^^^^^^^^^^ re-bound here @@ -411,7 +411,7 @@ LL | trait TRI2> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:200:39 + --> $DIR/duplicate.rs:206:39 | LL | trait TRI3> {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -419,7 +419,7 @@ LL | trait TRI3> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:202:34 + --> $DIR/duplicate.rs:208:34 | LL | trait TRS1: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -427,7 +427,7 @@ LL | trait TRS1: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:202:34 + --> $DIR/duplicate.rs:208:34 | LL | trait TRS1: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -437,7 +437,7 @@ LL | trait TRS1: Iterator {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:202:34 + --> $DIR/duplicate.rs:208:34 | LL | trait TRS1: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -447,7 +447,7 @@ LL | trait TRS1: Iterator {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:206:34 + --> $DIR/duplicate.rs:212:34 | LL | trait TRS2: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -455,7 +455,7 @@ LL | trait TRS2: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:206:34 + --> $DIR/duplicate.rs:212:34 | LL | trait TRS2: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -465,7 +465,7 @@ LL | trait TRS2: Iterator {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:206:34 + --> $DIR/duplicate.rs:212:34 | LL | trait TRS2: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -475,7 +475,7 @@ LL | trait TRS2: Iterator {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:210:37 + --> $DIR/duplicate.rs:216:37 | LL | trait TRS3: Iterator {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -483,7 +483,7 @@ LL | trait TRS3: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:210:37 + --> $DIR/duplicate.rs:216:37 | LL | trait TRS3: Iterator {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -493,7 +493,7 @@ LL | trait TRS3: Iterator {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:210:37 + --> $DIR/duplicate.rs:216:37 | LL | trait TRS3: Iterator {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -503,7 +503,7 @@ LL | trait TRS3: Iterator {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:216:29 + --> $DIR/duplicate.rs:222:29 | LL | T: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -511,7 +511,7 @@ LL | T: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:222:29 + --> $DIR/duplicate.rs:228:29 | LL | T: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -519,7 +519,7 @@ LL | T: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:228:32 + --> $DIR/duplicate.rs:234:32 | LL | T: Iterator, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -527,7 +527,7 @@ LL | T: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:234:32 + --> $DIR/duplicate.rs:240:32 | LL | Self: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -535,7 +535,7 @@ LL | Self: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:234:32 + --> $DIR/duplicate.rs:240:32 | LL | Self: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -545,7 +545,7 @@ LL | Self: Iterator, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:234:32 + --> $DIR/duplicate.rs:240:32 | LL | Self: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -555,7 +555,7 @@ LL | Self: Iterator, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:242:32 + --> $DIR/duplicate.rs:248:32 | LL | Self: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -563,7 +563,7 @@ LL | Self: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:242:32 + --> $DIR/duplicate.rs:248:32 | LL | Self: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -573,7 +573,7 @@ LL | Self: Iterator, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:242:32 + --> $DIR/duplicate.rs:248:32 | LL | Self: Iterator, | ---------- ^^^^^^^^^^ re-bound here @@ -583,7 +583,7 @@ LL | Self: Iterator, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:250:35 + --> $DIR/duplicate.rs:256:35 | LL | Self: Iterator, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -591,7 +591,7 @@ LL | Self: Iterator, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:250:35 + --> $DIR/duplicate.rs:256:35 | LL | Self: Iterator, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -601,7 +601,7 @@ LL | Self: Iterator, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:250:35 + --> $DIR/duplicate.rs:256:35 | LL | Self: Iterator, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -611,7 +611,7 @@ LL | Self: Iterator, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:257:34 + --> $DIR/duplicate.rs:263:34 | LL | type A: Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -619,7 +619,7 @@ LL | type A: Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:257:34 + --> $DIR/duplicate.rs:263:34 | LL | type A: Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -629,7 +629,7 @@ LL | type A: Iterator; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:262:34 + --> $DIR/duplicate.rs:268:34 | LL | type A: Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -637,7 +637,7 @@ LL | type A: Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:262:34 + --> $DIR/duplicate.rs:268:34 | LL | type A: Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -647,7 +647,7 @@ LL | type A: Iterator; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:267:37 + --> $DIR/duplicate.rs:273:37 | LL | type A: Iterator; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -655,7 +655,7 @@ LL | type A: Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:267:37 + --> $DIR/duplicate.rs:273:37 | LL | type A: Iterator; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -697,7 +697,55 @@ help: consider specifying the generic argument LL | iter::empty::() | +++++ -error: aborting due to 81 previous errors +error: unconstrained opaque type + --> $DIR/duplicate.rs:180:51 + | +LL | type ETAI1> = impl Copy; + | ^^^^^^^^^ + | + = note: `ETAI1` must be used in combination with a concrete type within the same crate + +error: unconstrained opaque type + --> $DIR/duplicate.rs:183:51 + | +LL | type ETAI2> = impl Copy; + | ^^^^^^^^^ + | + = note: `ETAI2` must be used in combination with a concrete type within the same crate + +error: unconstrained opaque type + --> $DIR/duplicate.rs:186:57 + | +LL | type ETAI3> = impl Copy; + | ^^^^^^^^^ + | + = note: `ETAI3` must be used in combination with a concrete type within the same crate + +error: unconstrained opaque type + --> $DIR/duplicate.rs:189:14 + | +LL | type ETAI4 = impl Iterator; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `ETAI4` must be used in combination with a concrete type within the same crate + +error: unconstrained opaque type + --> $DIR/duplicate.rs:193:14 + | +LL | type ETAI5 = impl Iterator; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `ETAI5` must be used in combination with a concrete type within the same crate + +error: unconstrained opaque type + --> $DIR/duplicate.rs:197:14 + | +LL | type ETAI6 = impl Iterator; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `ETAI6` must be used in combination with a concrete type within the same crate + +error: aborting due to 87 previous errors Some errors have detailed explanations: E0282, E0719. For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/associated-type-bounds/hrtb.rs b/tests/ui/associated-type-bounds/hrtb.rs index 1bf574f2e6518..8ff7faec3a0f0 100644 --- a/tests/ui/associated-type-bounds/hrtb.rs +++ b/tests/ui/associated-type-bounds/hrtb.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) trait A<'a> {} trait B<'b> {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.fixed b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.fixed new file mode 100644 index 0000000000000..7af986267aa67 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.fixed @@ -0,0 +1,40 @@ +//@ edition: 2021 +//@ run-rustfix +#![feature(return_type_notation)] +#![allow(dead_code)] + +trait Trait { + async fn method() {} +} + +fn foo>() {} +//~^ ERROR argument types not allowed with return type notation + +fn bar>() {} +//~^ ERROR return type not allowed with return type notation + +fn baz>() {} +//~^ ERROR return type notation arguments must be elided with `..` + +fn foo_path() where T::method(..): Send {} +//~^ ERROR argument types not allowed with return type notation + +fn bar_path() where T::method(..): Send {} +//~^ ERROR return type not allowed with return type notation + +fn bay_path() where T::method(..): Send {} +//~^ ERROR return type not allowed with return type notation + +fn baz_path() where T::method(..): Send {} +//~^ ERROR return type notation arguments must be elided with `..` + +fn foo_qualified() where T::method(..): Send {} +//~^ ERROR expected associated type + +fn bar_qualified() where T::method(..): Send {} +//~^ ERROR expected associated type + +fn baz_qualified() where T::method(..): Send {} +//~^ ERROR expected associated type + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs index 90fd2c2b4f326..983836e6433f0 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs +++ b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs @@ -1,6 +1,7 @@ //@ edition: 2021 - +//@ run-rustfix #![feature(return_type_notation)] +#![allow(dead_code)] trait Trait { async fn method() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr index bd02b7d6534a9..e1245a04c7f7d 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr @@ -1,5 +1,5 @@ error: return type not allowed with return type notation - --> $DIR/bad-inputs-and-output.rs:24:45 + --> $DIR/bad-inputs-and-output.rs:25:45 | LL | fn bay_path() where T::method(..) -> (): Send {} | ^^^^^ @@ -11,25 +11,43 @@ LL + fn bay_path() where T::method(..): Send {} | error[E0575]: expected associated type, found associated function `Trait::method` - --> $DIR/bad-inputs-and-output.rs:30:36 + --> $DIR/bad-inputs-and-output.rs:31:36 | LL | fn foo_qualified() where ::method(i32): Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ not a associated type + | +help: you might have meant to use the return type notation syntax + | +LL - fn foo_qualified() where ::method(i32): Send {} +LL + fn foo_qualified() where T::method(..): Send {} + | error[E0575]: expected associated type, found associated function `Trait::method` - --> $DIR/bad-inputs-and-output.rs:33:36 + --> $DIR/bad-inputs-and-output.rs:34:36 | LL | fn bar_qualified() where ::method() -> (): Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a associated type + | +help: you might have meant to use the return type notation syntax + | +LL - fn bar_qualified() where ::method() -> (): Send {} +LL + fn bar_qualified() where T::method(..): Send {} + | error[E0575]: expected associated type, found associated function `Trait::method` - --> $DIR/bad-inputs-and-output.rs:36:36 + --> $DIR/bad-inputs-and-output.rs:37:36 | LL | fn baz_qualified() where ::method(): Send {} | ^^^^^^^^^^^^^^^^^^^^^^ not a associated type + | +help: you might have meant to use the return type notation syntax + | +LL - fn baz_qualified() where ::method(): Send {} +LL + fn baz_qualified() where T::method(..): Send {} + | error: argument types not allowed with return type notation - --> $DIR/bad-inputs-and-output.rs:9:23 + --> $DIR/bad-inputs-and-output.rs:10:23 | LL | fn foo>() {} | ^^^^^ @@ -41,7 +59,7 @@ LL + fn foo>() {} | error: return type not allowed with return type notation - --> $DIR/bad-inputs-and-output.rs:12:25 + --> $DIR/bad-inputs-and-output.rs:13:25 | LL | fn bar (): Send>>() {} | ^^^^^^ @@ -53,7 +71,7 @@ LL + fn bar>() {} | error: return type notation arguments must be elided with `..` - --> $DIR/bad-inputs-and-output.rs:15:23 + --> $DIR/bad-inputs-and-output.rs:16:23 | LL | fn baz>() {} | ^^ @@ -64,7 +82,7 @@ LL | fn baz>() {} | ++ error: argument types not allowed with return type notation - --> $DIR/bad-inputs-and-output.rs:18:40 + --> $DIR/bad-inputs-and-output.rs:19:40 | LL | fn foo_path() where T::method(i32): Send {} | ^^^^^ @@ -76,7 +94,7 @@ LL + fn foo_path() where T::method(..): Send {} | error: return type not allowed with return type notation - --> $DIR/bad-inputs-and-output.rs:21:42 + --> $DIR/bad-inputs-and-output.rs:22:42 | LL | fn bar_path() where T::method() -> (): Send {} | ^^^^^^ @@ -88,7 +106,7 @@ LL + fn bar_path() where T::method(..): Send {} | error: return type notation arguments must be elided with `..` - --> $DIR/bad-inputs-and-output.rs:27:40 + --> $DIR/bad-inputs-and-output.rs:28:40 | LL | fn baz_path() where T::method(): Send {} | ^^ diff --git a/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr b/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr index 0a31cc6753340..459f3ea164202 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr @@ -15,6 +15,10 @@ note: required by a bound in `is_send` | LL | fn is_send(_: impl Send) {} | ^^^^ required by this bound in `is_send` +help: consider further restricting the associated type + | +LL | >() where ::method(..): Send { + | ++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/associated-type-bounds/return-type-notation/display.stderr b/tests/ui/associated-type-bounds/return-type-notation/display.stderr index b895d79695272..a614089ce407a 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/display.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/display.stderr @@ -11,6 +11,10 @@ note: required by a bound in `needs_trait` | LL | fn needs_trait(_: impl Trait) {} | ^^^^^ required by this bound in `needs_trait` +help: consider further restricting the associated type + | +LL | fn foo(t: T) where ::method(..): Trait { + | +++++++++++++++++++++++++++++++++++++ error[E0277]: the trait bound `impl Sized { ::method_with_lt(..) }: Trait` is not satisfied --> $DIR/display.rs:16:17 @@ -25,6 +29,10 @@ note: required by a bound in `needs_trait` | LL | fn needs_trait(_: impl Trait) {} | ^^^^^ required by this bound in `needs_trait` +help: consider further restricting the associated type + | +LL | fn foo(t: T) where ::method_with_lt(..): Trait { + | +++++++++++++++++++++++++++++++++++++++++++++ error[E0277]: the trait bound `impl Sized: Trait` is not satisfied --> $DIR/display.rs:18:17 diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-missing.rs b/tests/ui/associated-type-bounds/return-type-notation/path-missing.rs index 8cab48bd0c454..1ad02f754db53 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/path-missing.rs +++ b/tests/ui/associated-type-bounds/return-type-notation/path-missing.rs @@ -17,7 +17,7 @@ where fn type_dependent() where T::method(..): Send, - //~^ associated function `method` not found for `T` + //~^ ERROR associated function `method` not found for `T` { } diff --git a/tests/ui/associated-type-bounds/return-type-notation/rendering.fixed b/tests/ui/associated-type-bounds/return-type-notation/rendering.fixed new file mode 100644 index 0000000000000..72c174a0ca020 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/rendering.fixed @@ -0,0 +1,15 @@ +//@ run-rustfix + +#![allow(unused)] +#![feature(return_type_notation)] + +trait Foo { + fn missing() -> impl Sized; +} + +impl Foo for () { + //~^ ERROR not all trait items implemented, missing: `missing` +fn missing() -> impl Sized { todo!() } +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/rendering.rs b/tests/ui/associated-type-bounds/return-type-notation/rendering.rs new file mode 100644 index 0000000000000..4c9948d4c0604 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/rendering.rs @@ -0,0 +1,14 @@ +//@ run-rustfix + +#![allow(unused)] +#![feature(return_type_notation)] + +trait Foo { + fn missing() -> impl Sized; +} + +impl Foo for () { + //~^ ERROR not all trait items implemented, missing: `missing` +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/rendering.stderr b/tests/ui/associated-type-bounds/return-type-notation/rendering.stderr new file mode 100644 index 0000000000000..62fdeb059dd84 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/rendering.stderr @@ -0,0 +1,12 @@ +error[E0046]: not all trait items implemented, missing: `missing` + --> $DIR/rendering.rs:10:1 + | +LL | fn missing() -> impl Sized; + | --------------------------- `missing` from trait +... +LL | impl Foo for () { + | ^^^^^^^^^^^^^^^ missing `missing` in implementation + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0046`. diff --git a/tests/ui/associated-type-bounds/supertrait-defines-ty.rs b/tests/ui/associated-type-bounds/supertrait-defines-ty.rs index ed1c1fa6f039a..1c09ff3d3fb86 100644 --- a/tests/ui/associated-type-bounds/supertrait-defines-ty.rs +++ b/tests/ui/associated-type-bounds/supertrait-defines-ty.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) // Make sure that we don't look into associated type bounds when looking for // supertraits that define an associated type. Fixes #76593. diff --git a/tests/ui/associated-type-bounds/trait-alias-impl-trait.rs b/tests/ui/associated-type-bounds/trait-alias-impl-trait.rs index fb6a4fcbe9768..540c083e309fa 100644 --- a/tests/ui/associated-type-bounds/trait-alias-impl-trait.rs +++ b/tests/ui/associated-type-bounds/trait-alias-impl-trait.rs @@ -31,6 +31,7 @@ impl Tr1 for S1 { } type Et1 = impl Tr1; +#[define_opaque(Et1)] fn def_et1() -> Et1 { S1 } @@ -39,6 +40,7 @@ pub fn use_et1() { } type Et2 = impl Tr1; +#[define_opaque(Et2)] fn def_et2() -> Et2 { S1 } @@ -47,6 +49,7 @@ pub fn use_et2() { } type Et3 = impl Tr1>>>; +#[define_opaque(Et3)] fn def_et3() -> Et3 { struct A; impl Tr1 for A { @@ -68,6 +71,7 @@ pub fn use_et3() { } type Et4 = impl Tr1 Tr2<'a>>; +#[define_opaque(Et4)] fn def_et4() -> Et4 { #[derive(Copy, Clone)] struct A; diff --git a/tests/ui/associated-types/associated-type-macro.rs b/tests/ui/associated-types/associated-type-macro.rs index 22b5bca40103d..8586dc172764a 100644 --- a/tests/ui/associated-types/associated-type-macro.rs +++ b/tests/ui/associated-types/associated-type-macro.rs @@ -1,4 +1,4 @@ fn main() { - #[cfg(FALSE)] + #[cfg(false)] <() as module>::mac!(); //~ ERROR macros cannot use qualified paths } diff --git a/tests/ui/associated-types/associated-types-eq-3.rs b/tests/ui/associated-types/associated-types-eq-3.rs index 380d0e95c13f3..082a73254668c 100644 --- a/tests/ui/associated-types/associated-types-eq-3.rs +++ b/tests/ui/associated-types/associated-types-eq-3.rs @@ -1,6 +1,8 @@ // Test equality constraints on associated types. Check we get type errors // where we should. +//@ dont-require-annotations: NOTE + pub trait Foo { type A; fn boo(&self) -> ::A; @@ -22,9 +24,9 @@ fn foo1>(x: I) { fn foo2(x: I) { let _: Bar = x.boo(); //~^ ERROR mismatched types - //~| found associated type `::A` - //~| expected `Bar`, found - //~| expected struct `Bar` + //~| NOTE found associated type `::A` + //~| NOTE expected `Bar`, found + //~| NOTE expected struct `Bar` } diff --git a/tests/ui/associated-types/associated-types-eq-3.stderr b/tests/ui/associated-types/associated-types-eq-3.stderr index c3377eed20a52..5fef2a020b890 100644 --- a/tests/ui/associated-types/associated-types-eq-3.stderr +++ b/tests/ui/associated-types/associated-types-eq-3.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/associated-types-eq-3.rs:23:18 + --> $DIR/associated-types-eq-3.rs:25:18 | LL | let _: Bar = x.boo(); | --- ^^^^^^^ expected `Bar`, found associated type @@ -14,7 +14,7 @@ LL | fn foo2>(x: I) { | +++++++++ error[E0271]: type mismatch resolving `::A == Bar` - --> $DIR/associated-types-eq-3.rs:38:10 + --> $DIR/associated-types-eq-3.rs:40:10 | LL | foo1(a); | ---- ^ type mismatch resolving `::A == Bar` @@ -22,24 +22,24 @@ LL | foo1(a); | required by a bound introduced by this call | note: expected this to be `Bar` - --> $DIR/associated-types-eq-3.rs:12:14 + --> $DIR/associated-types-eq-3.rs:14:14 | LL | type A = usize; | ^^^^^ note: required by a bound in `foo1` - --> $DIR/associated-types-eq-3.rs:18:16 + --> $DIR/associated-types-eq-3.rs:20:16 | LL | fn foo1>(x: I) { | ^^^^^ required by this bound in `foo1` error[E0271]: type mismatch resolving `::A == Bar` - --> $DIR/associated-types-eq-3.rs:40:9 + --> $DIR/associated-types-eq-3.rs:42:9 | LL | baz(&a); | ^^ type mismatch resolving `::A == Bar` | note: expected this to be `Bar` - --> $DIR/associated-types-eq-3.rs:12:14 + --> $DIR/associated-types-eq-3.rs:14:14 | LL | type A = usize; | ^^^^^ diff --git a/tests/ui/associated-types/associated-types-in-ambiguous-context.stderr b/tests/ui/associated-types/associated-types-in-ambiguous-context.stderr index d7d2161e7ad97..1be8db5ddf44f 100644 --- a/tests/ui/associated-types/associated-types-in-ambiguous-context.stderr +++ b/tests/ui/associated-types/associated-types-in-ambiguous-context.stderr @@ -10,24 +10,6 @@ LL - fn get(x: T, y: U) -> Get::Value {} LL + fn get(x: T, y: U) -> ::Value {} | -error[E0223]: ambiguous associated type - --> $DIR/associated-types-in-ambiguous-context.rs:13:23 - | -LL | fn grab(&self) -> Grab::Value; - | ^^^^^^^^^^^ help: use fully-qualified syntax: `::Value` - -error[E0223]: ambiguous associated type - --> $DIR/associated-types-in-ambiguous-context.rs:16:22 - | -LL | fn get(&self) -> Get::Value; - | ^^^^^^^^^^ - | -help: if there were a type named `Example` that implemented `Get`, you could use the fully-qualified path - | -LL - fn get(&self) -> Get::Value; -LL + fn get(&self) -> ::Value; - | - error[E0223]: ambiguous associated type --> $DIR/associated-types-in-ambiguous-context.rs:22:17 | @@ -56,6 +38,24 @@ LL + type X = as Deref>::Target; | and N other candidates +error[E0223]: ambiguous associated type + --> $DIR/associated-types-in-ambiguous-context.rs:13:23 + | +LL | fn grab(&self) -> Grab::Value; + | ^^^^^^^^^^^ help: use fully-qualified syntax: `::Value` + +error[E0223]: ambiguous associated type + --> $DIR/associated-types-in-ambiguous-context.rs:16:22 + | +LL | fn get(&self) -> Get::Value; + | ^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Get`, you could use the fully-qualified path + | +LL - fn get(&self) -> Get::Value; +LL + fn get(&self) -> ::Value; + | + error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/associated-types/associated-types-path-2.rs b/tests/ui/associated-types/associated-types-path-2.rs index c993e1d27202d..b81aa990bcd44 100644 --- a/tests/ui/associated-types/associated-types-path-2.rs +++ b/tests/ui/associated-types/associated-types-path-2.rs @@ -1,5 +1,7 @@ // Test type checking of uses of associated types via sugary paths. +//@ dont-require-annotations: NOTE + pub trait Foo { type A; @@ -18,7 +20,7 @@ pub fn f2(a: T) -> T::A { pub fn f1_int_int() { f1(2i32, 4i32); //~^ ERROR mismatched types - //~| expected `u32`, found `i32` + //~| NOTE expected `u32`, found `i32` } pub fn f1_int_uint() { @@ -40,7 +42,7 @@ pub fn f1_uint_int() { pub fn f2_int() { let _: i32 = f2(2i32); //~^ ERROR mismatched types - //~| expected `i32`, found `u32` + //~| NOTE expected `i32`, found `u32` } pub fn main() { } diff --git a/tests/ui/associated-types/associated-types-path-2.stderr b/tests/ui/associated-types/associated-types-path-2.stderr index 897eb75e3e3d2..46e34b0809fb8 100644 --- a/tests/ui/associated-types/associated-types-path-2.stderr +++ b/tests/ui/associated-types/associated-types-path-2.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/associated-types-path-2.rs:19:14 + --> $DIR/associated-types-path-2.rs:21:14 | LL | f1(2i32, 4i32); | -- ^^^^ expected `u32`, found `i32` @@ -7,7 +7,7 @@ LL | f1(2i32, 4i32); | arguments to this function are incorrect | note: function defined here - --> $DIR/associated-types-path-2.rs:13:8 + --> $DIR/associated-types-path-2.rs:15:8 | LL | pub fn f1(a: T, x: T::A) {} | ^^ ------- @@ -18,7 +18,7 @@ LL + f1(2i32, 4u32); | error[E0277]: the trait bound `u32: Foo` is not satisfied - --> $DIR/associated-types-path-2.rs:29:8 + --> $DIR/associated-types-path-2.rs:31:8 | LL | f1(2u32, 4u32); | -- ^^^^ the trait `Foo` is not implemented for `u32` @@ -27,13 +27,13 @@ LL | f1(2u32, 4u32); | = help: the trait `Foo` is implemented for `i32` note: required by a bound in `f1` - --> $DIR/associated-types-path-2.rs:13:14 + --> $DIR/associated-types-path-2.rs:15:14 | LL | pub fn f1(a: T, x: T::A) {} | ^^^ required by this bound in `f1` error[E0277]: the trait bound `u32: Foo` is not satisfied - --> $DIR/associated-types-path-2.rs:29:14 + --> $DIR/associated-types-path-2.rs:31:14 | LL | f1(2u32, 4u32); | ^^^^ the trait `Foo` is not implemented for `u32` @@ -41,7 +41,7 @@ LL | f1(2u32, 4u32); = help: the trait `Foo` is implemented for `i32` error[E0277]: the trait bound `u32: Foo` is not satisfied - --> $DIR/associated-types-path-2.rs:35:8 + --> $DIR/associated-types-path-2.rs:37:8 | LL | f1(2u32, 4i32); | -- ^^^^ the trait `Foo` is not implemented for `u32` @@ -50,13 +50,13 @@ LL | f1(2u32, 4i32); | = help: the trait `Foo` is implemented for `i32` note: required by a bound in `f1` - --> $DIR/associated-types-path-2.rs:13:14 + --> $DIR/associated-types-path-2.rs:15:14 | LL | pub fn f1(a: T, x: T::A) {} | ^^^ required by this bound in `f1` error[E0277]: the trait bound `u32: Foo` is not satisfied - --> $DIR/associated-types-path-2.rs:35:14 + --> $DIR/associated-types-path-2.rs:37:14 | LL | f1(2u32, 4i32); | ^^^^ the trait `Foo` is not implemented for `u32` @@ -64,7 +64,7 @@ LL | f1(2u32, 4i32); = help: the trait `Foo` is implemented for `i32` error[E0308]: mismatched types - --> $DIR/associated-types-path-2.rs:41:18 + --> $DIR/associated-types-path-2.rs:43:18 | LL | let _: i32 = f2(2i32); | --- ^^^^^^^^ expected `i32`, found `u32` diff --git a/tests/ui/associated-types/bound-lifetime-constrained.ok.stderr b/tests/ui/associated-types/bound-lifetime-constrained.ok.stderr deleted file mode 100644 index 9082044fe068b..0000000000000 --- a/tests/ui/associated-types/bound-lifetime-constrained.ok.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fatal error triggered by #[rustc_error] - --> $DIR/bound-lifetime-constrained.rs:48:1 - | -LL | fn main() { } - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/associated-types/bound-lifetime-constrained.rs b/tests/ui/associated-types/bound-lifetime-constrained.rs index 1dc3b2f5c2b77..3a5a77f57f23d 100644 --- a/tests/ui/associated-types/bound-lifetime-constrained.rs +++ b/tests/ui/associated-types/bound-lifetime-constrained.rs @@ -1,7 +1,7 @@ //@ revisions: func object clause ok +//@[ok] check-pass #![allow(dead_code)] -#![feature(rustc_attrs)] trait Foo<'a> { type Item; @@ -44,5 +44,4 @@ fn clause2() where T: for<'a> Fn() -> <() as Foo<'a>>::Item { //[clause]~^ ERROR `Output` references lifetime `'a` } -#[rustc_error] -fn main() { } //[ok]~ ERROR fatal error triggered by #[rustc_error] +fn main() { } diff --git a/tests/ui/associated-types/bound-lifetime-in-binding-only.ok.stderr b/tests/ui/associated-types/bound-lifetime-in-binding-only.ok.stderr deleted file mode 100644 index 435e224bd8922..0000000000000 --- a/tests/ui/associated-types/bound-lifetime-in-binding-only.ok.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fatal error triggered by #[rustc_error] - --> $DIR/bound-lifetime-in-binding-only.rs:71:1 - | -LL | fn main() { } - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/associated-types/bound-lifetime-in-binding-only.rs b/tests/ui/associated-types/bound-lifetime-in-binding-only.rs index e973e58b629fd..2401fe0ef1f7b 100644 --- a/tests/ui/associated-types/bound-lifetime-in-binding-only.rs +++ b/tests/ui/associated-types/bound-lifetime-in-binding-only.rs @@ -1,7 +1,7 @@ //@ revisions: angle paren ok elision +//@[ok] check-pass #![allow(dead_code)] -#![feature(rustc_attrs)] #![feature(unboxed_closures)] trait Foo { @@ -67,5 +67,4 @@ fn ok2 Fn<(&'b Parameterized<'a>,), Output=&'a i32>>() { fn ok3() where for<'a> Parameterized<'a>: Foo { } -#[rustc_error] -fn main() { } //[ok]~ ERROR fatal error triggered by #[rustc_error] +fn main() { } diff --git a/tests/ui/associated-types/bound-lifetime-in-return-only.ok.stderr b/tests/ui/associated-types/bound-lifetime-in-return-only.ok.stderr deleted file mode 100644 index 1815a7be7ee81..0000000000000 --- a/tests/ui/associated-types/bound-lifetime-in-return-only.ok.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fatal error triggered by #[rustc_error] - --> $DIR/bound-lifetime-in-return-only.rs:49:1 - | -LL | fn main() { } - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/associated-types/bound-lifetime-in-return-only.rs b/tests/ui/associated-types/bound-lifetime-in-return-only.rs index bf3aa6149cce3..8a28f5b778610 100644 --- a/tests/ui/associated-types/bound-lifetime-in-return-only.rs +++ b/tests/ui/associated-types/bound-lifetime-in-return-only.rs @@ -1,7 +1,7 @@ //@ revisions: sig local structure ok elision +//@[ok] check-pass #![allow(dead_code)] -#![feature(rustc_attrs)] #![feature(unboxed_closures)] trait Foo { @@ -45,5 +45,4 @@ fn ok1(_: &dyn for<'a> Fn(&Parameterized<'a>) -> &'a i32) { fn ok2(_: &dyn for<'a,'b> Fn<(&'b Parameterized<'a>,), Output=&'a i32>) { } -#[rustc_error] -fn main() { } //[ok]~ ERROR fatal error triggered by #[rustc_error] +fn main() { } diff --git a/tests/ui/associated-types/hr-associated-type-bound-2.rs b/tests/ui/associated-types/hr-associated-type-bound-2.rs index a89f61a81a57c..467400f4b800c 100644 --- a/tests/ui/associated-types/hr-associated-type-bound-2.rs +++ b/tests/ui/associated-types/hr-associated-type-bound-2.rs @@ -8,7 +8,7 @@ where } } -impl X<'_> for u32 //~ overflow evaluating the requirement `for<'b> u32: X<'b>` +impl X<'_> for u32 //~ ERROR overflow evaluating the requirement `for<'b> u32: X<'b>` where for<'b> >::U: Clone, { diff --git a/tests/ui/associated-types/hr-associated-type-bound-param-2.rs b/tests/ui/associated-types/hr-associated-type-bound-param-2.rs index 673f02c7cd0e5..d6546c24dc50b 100644 --- a/tests/ui/associated-types/hr-associated-type-bound-param-2.rs +++ b/tests/ui/associated-types/hr-associated-type-bound-param-2.rs @@ -1,16 +1,16 @@ trait Z<'a, T: ?Sized> where T: Z<'a, u16>, - //~^ the trait bound `str: Clone` is not satisfied - //~| the trait bound `str: Clone` is not satisfied + //~^ ERROR the trait bound `str: Clone` is not satisfied + //~| ERROR the trait bound `str: Clone` is not satisfied for<'b> >::W: Clone, { type W: ?Sized; fn h(&self, x: &T::W) { ::clone(x); - //~^ the trait bound `str: Clone` is not satisfied - //~| the trait bound `str: Clone` is not satisfied - //~| the trait bound `str: Clone` is not satisfied + //~^ ERROR the trait bound `str: Clone` is not satisfied + //~| ERROR the trait bound `str: Clone` is not satisfied + //~| ERROR the trait bound `str: Clone` is not satisfied } } diff --git a/tests/ui/associated-types/hr-associated-type-projection-1.rs b/tests/ui/associated-types/hr-associated-type-projection-1.rs index d7fc5d122c3ec..1d272a6604469 100644 --- a/tests/ui/associated-types/hr-associated-type-projection-1.rs +++ b/tests/ui/associated-types/hr-associated-type-projection-1.rs @@ -12,10 +12,10 @@ where impl UnsafeCopy<'_, T> for T { type Item = T; - //~^ type mismatch resolving `::Target == T` + //~^ ERROR type mismatch resolving `::Target == T` } pub fn main() { <&'static str>::bug(&""); - //~^ type mismatch resolving `<&str as Deref>::Target == &str` + //~^ ERROR type mismatch resolving `<&str as Deref>::Target == &str` } diff --git a/tests/ui/associated-types/ident-from-macro-expansion.rs b/tests/ui/associated-types/ident-from-macro-expansion.rs new file mode 100644 index 0000000000000..6aabe4571406c --- /dev/null +++ b/tests/ui/associated-types/ident-from-macro-expansion.rs @@ -0,0 +1,23 @@ +trait Trait {} +impl Trait for () {} + +macro_rules! fully_qualified { + ($id:ident) => { + <() as Trait>::$id + } +} + +macro_rules! type_dependent { + ($t:ident, $id:ident) => { + T::$id + } +} + +fn t() { + let x: fully_qualified!(Assoc); + //~^ ERROR cannot find associated type `Assoc` in trait `Trait` + let x: type_dependent!(T, Assoc); + //~^ ERROR associated type `Assoc` not found for `T` +} + +fn main() {} diff --git a/tests/ui/associated-types/ident-from-macro-expansion.stderr b/tests/ui/associated-types/ident-from-macro-expansion.stderr new file mode 100644 index 0000000000000..dabf13c2c1726 --- /dev/null +++ b/tests/ui/associated-types/ident-from-macro-expansion.stderr @@ -0,0 +1,22 @@ +error[E0576]: cannot find associated type `Assoc` in trait `Trait` + --> $DIR/ident-from-macro-expansion.rs:17:29 + | +LL | <() as Trait>::$id + | --- due to this macro variable +... +LL | let x: fully_qualified!(Assoc); + | ^^^^^ not found in `Trait` + +error[E0220]: associated type `Assoc` not found for `T` + --> $DIR/ident-from-macro-expansion.rs:19:31 + | +LL | T::$id + | --- due to this macro variable +... +LL | let x: type_dependent!(T, Assoc); + | ^^^^^ associated type `Assoc` not found + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0220, E0576. +For more information about an error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-types/impl-wf-cycle-4.rs b/tests/ui/associated-types/impl-wf-cycle-4.rs index bfa8adc71a11d..1c1b3991d32a9 100644 --- a/tests/ui/associated-types/impl-wf-cycle-4.rs +++ b/tests/ui/associated-types/impl-wf-cycle-4.rs @@ -2,7 +2,7 @@ trait Filter { type ToMatch; } -impl Filter for T //~ ERROR overflow evaluating the requirement +impl Filter for T //~ ERROR cycle detected when where T: Fn(Self::ToMatch), { diff --git a/tests/ui/associated-types/impl-wf-cycle-4.stderr b/tests/ui/associated-types/impl-wf-cycle-4.stderr index cdbac267d34dc..c966579aecf16 100644 --- a/tests/ui/associated-types/impl-wf-cycle-4.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-4.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow evaluating the requirement `::ToMatch == ::ToMatch` +error[E0391]: cycle detected when computing normalized predicates of `` --> $DIR/impl-wf-cycle-4.rs:5:1 | LL | / impl Filter for T @@ -6,20 +6,23 @@ LL | | where LL | | T: Fn(Self::ToMatch), | |_________________________^ | -note: required for `T` to implement `Filter` - --> $DIR/impl-wf-cycle-4.rs:5:9 +note: ...which requires computing whether `` has a guaranteed unsized self type... + --> $DIR/impl-wf-cycle-4.rs:5:1 | -LL | impl Filter for T - | ^^^^^^ ^ -LL | where -LL | T: Fn(Self::ToMatch), - | ----------------- unsatisfied trait bound introduced here -note: associated types for the current `impl` cannot be restricted in `where` clauses - --> $DIR/impl-wf-cycle-4.rs:7:11 +LL | / impl Filter for T +LL | | where +LL | | T: Fn(Self::ToMatch), + | |_________________________^ + = note: ...which again requires computing normalized predicates of ``, completing the cycle +note: cycle used when checking that `` is well-formed + --> $DIR/impl-wf-cycle-4.rs:5:1 | -LL | T: Fn(Self::ToMatch), - | ^^^^^^^^^^^^^ +LL | / impl Filter for T +LL | | where +LL | | T: Fn(Self::ToMatch), + | |_________________________^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/associated-types/issue-36499.rs b/tests/ui/associated-types/issue-36499.rs index 25f4060fa6f58..941b1aaa0d546 100644 --- a/tests/ui/associated-types/issue-36499.rs +++ b/tests/ui/associated-types/issue-36499.rs @@ -1,5 +1,3 @@ -//@ error-pattern: aborting due to 1 previous error - fn main() { - 2 + +2; + 2 + +2; //~ ERROR leading `+` is not supported } diff --git a/tests/ui/associated-types/issue-36499.stderr b/tests/ui/associated-types/issue-36499.stderr index dd91bac815861..aebf0faae2dcb 100644 --- a/tests/ui/associated-types/issue-36499.stderr +++ b/tests/ui/associated-types/issue-36499.stderr @@ -1,5 +1,5 @@ error: leading `+` is not supported - --> $DIR/issue-36499.rs:4:9 + --> $DIR/issue-36499.rs:2:9 | LL | 2 + +2; | ^ unexpected `+` diff --git a/tests/ui/associated-types/issue-38821.stderr b/tests/ui/associated-types/issue-38821.stderr index dc919299710bc..8a19142b73056 100644 --- a/tests/ui/associated-types/issue-38821.stderr +++ b/tests/ui/associated-types/issue-38821.stderr @@ -53,7 +53,6 @@ LL | impl IntoNullable for T { | ------- ^^^^^^^^^^^^ ^ | | | unsatisfied trait bound introduced here - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting the associated type | LL | Expr: Expression::Nullable>, ::SqlType: NotNull, @@ -73,7 +72,6 @@ LL | impl IntoNullable for T { | | | unsatisfied trait bound introduced here = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting the associated type | LL | Expr: Expression::Nullable>, ::SqlType: NotNull, @@ -92,7 +90,6 @@ LL | impl IntoNullable for T { | ------- ^^^^^^^^^^^^ ^ | | | unsatisfied trait bound introduced here - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied --> $DIR/issue-38821.rs:23:10 @@ -108,7 +105,6 @@ LL | impl IntoNullable for T { | | | unsatisfied trait bound introduced here = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied --> $DIR/issue-38821.rs:23:17 @@ -123,7 +119,6 @@ LL | impl IntoNullable for T { | ------- ^^^^^^^^^^^^ ^ | | | unsatisfied trait bound introduced here - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting the associated type | LL | Expr: Expression::Nullable>, ::SqlType: NotNull, @@ -143,7 +138,6 @@ LL | impl IntoNullable for T { | | | unsatisfied trait bound introduced here = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting the associated type | LL | Expr: Expression::Nullable>, ::SqlType: NotNull, @@ -162,7 +156,6 @@ LL | impl IntoNullable for T { | ------- ^^^^^^^^^^^^ ^ | | | unsatisfied trait bound introduced here - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting the associated type | LL | Expr: Expression::Nullable>, ::SqlType: NotNull, @@ -182,7 +175,6 @@ LL | impl IntoNullable for T { | | | unsatisfied trait bound introduced here = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting the associated type | LL | Expr: Expression::Nullable>, ::SqlType: NotNull, @@ -201,7 +193,6 @@ LL | impl IntoNullable for T { | ------- ^^^^^^^^^^^^ ^ | | | unsatisfied trait bound introduced here - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied --> $DIR/issue-38821.rs:23:23 @@ -217,7 +208,6 @@ LL | impl IntoNullable for T { | | | unsatisfied trait bound introduced here = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied --> $DIR/issue-38821.rs:23:10 @@ -233,7 +223,6 @@ LL | impl IntoNullable for T { | | | unsatisfied trait bound introduced here = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied --> $DIR/issue-38821.rs:23:10 @@ -249,7 +238,6 @@ LL | impl IntoNullable for T { | | | unsatisfied trait bound introduced here = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied --> $DIR/issue-38821.rs:23:23 @@ -265,7 +253,6 @@ LL | impl IntoNullable for T { | | | unsatisfied trait bound introduced here = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied --> $DIR/issue-38821.rs:23:23 @@ -281,7 +268,6 @@ LL | impl IntoNullable for T { | | | unsatisfied trait bound introduced here = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied --> $DIR/issue-38821.rs:23:10 @@ -297,7 +283,6 @@ LL | impl IntoNullable for T { | | | unsatisfied trait bound introduced here = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied --> $DIR/issue-38821.rs:23:23 @@ -313,7 +298,6 @@ LL | impl IntoNullable for T { | | | unsatisfied trait bound introduced here = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 18 previous errors diff --git a/tests/ui/associated-types/issue-59324.rs b/tests/ui/associated-types/issue-59324.rs index 7421e08c89888..3abe84730526d 100644 --- a/tests/ui/associated-types/issue-59324.rs +++ b/tests/ui/associated-types/issue-59324.rs @@ -15,6 +15,7 @@ pub trait ThriftService: { fn get_service( //~^ ERROR the trait bound `Bug: Foo` is not satisfied + //~| ERROR the trait bound `Bug: Foo` is not satisfied &self, ) -> Self::AssocType; //~^ ERROR the trait bound `Bug: Foo` is not satisfied diff --git a/tests/ui/associated-types/issue-59324.stderr b/tests/ui/associated-types/issue-59324.stderr index dc8f9cfe895b5..f79afc89d10fb 100644 --- a/tests/ui/associated-types/issue-59324.stderr +++ b/tests/ui/associated-types/issue-59324.stderr @@ -32,17 +32,13 @@ error[E0277]: the trait bound `Bug: Foo` is not satisfied | LL | / fn get_service( LL | | +LL | | LL | | &self, LL | | ) -> Self::AssocType; | |_________________________^ the trait `Foo` is not implemented for `Bug` - | -help: consider further restricting type parameter `Bug` with trait `Foo` - | -LL | pub trait ThriftService: - | +++++ error[E0277]: the trait bound `(): Foo` is not satisfied - --> $DIR/issue-59324.rs:23:29 + --> $DIR/issue-59324.rs:24:29 | LL | fn with_factory(factory: dyn ThriftService<()>) {} | ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()` @@ -54,7 +50,22 @@ LL | pub trait Foo: NotFoo { | ^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `Bug: Foo` is not satisfied - --> $DIR/issue-59324.rs:19:10 + --> $DIR/issue-59324.rs:16:5 + | +LL | / fn get_service( +LL | | +LL | | +LL | | &self, +LL | | ) -> Self::AssocType; + | |_________________________^ the trait `Foo` is not implemented for `Bug` + | +help: consider further restricting type parameter `Bug` with trait `Foo` + | +LL | pub trait ThriftService: + | +++++ + +error[E0277]: the trait bound `Bug: Foo` is not satisfied + --> $DIR/issue-59324.rs:20:10 | LL | ) -> Self::AssocType; | ^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bug` @@ -65,7 +76,7 @@ LL | pub trait ThriftService: | +++++ error[E0277]: the trait bound `(): Foo` is not satisfied - --> $DIR/issue-59324.rs:23:29 + --> $DIR/issue-59324.rs:24:29 | LL | fn with_factory(factory: dyn ThriftService<()>) {} | ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()` @@ -78,7 +89,7 @@ LL | pub trait Foo: NotFoo { = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the size for values of type `(dyn ThriftService<(), AssocType = _> + 'static)` cannot be known at compilation time - --> $DIR/issue-59324.rs:23:29 + --> $DIR/issue-59324.rs:24:29 | LL | fn with_factory(factory: dyn ThriftService<()>) {} | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -95,6 +106,6 @@ help: function arguments must have a statically known size, borrowed types alway LL | fn with_factory(factory: &dyn ThriftService<()>) {} | + -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-types/issue-64855-2.rs b/tests/ui/associated-types/issue-64855-2.rs index 30cb37b5198e2..20b8ff17e9e5d 100644 --- a/tests/ui/associated-types/issue-64855-2.rs +++ b/tests/ui/associated-types/issue-64855-2.rs @@ -1,5 +1,8 @@ -//@ check-pass +// This was originally a test for a `ReEmpty` ICE, but became an unintentional test of +// the coinductiveness of WF predicates. That behavior was removed, and thus this is +// also inadvertently a test for the (non-)co-inductiveness of WF predicates. pub struct Bar<'a>(&'a Self) where Self: ; +//~^ ERROR overflow evaluating the requirement `Bar<'a> well-formed` fn main() {} diff --git a/tests/ui/associated-types/issue-64855-2.stderr b/tests/ui/associated-types/issue-64855-2.stderr new file mode 100644 index 0000000000000..22292a8721a16 --- /dev/null +++ b/tests/ui/associated-types/issue-64855-2.stderr @@ -0,0 +1,15 @@ +error[E0275]: overflow evaluating the requirement `Bar<'a> well-formed` + --> $DIR/issue-64855-2.rs:5:36 + | +LL | pub struct Bar<'a>(&'a Self) where Self: ; + | ^^^^ + | +note: required by a bound in `Bar` + --> $DIR/issue-64855-2.rs:5:36 + | +LL | pub struct Bar<'a>(&'a Self) where Self: ; + | ^^^^ required by this bound in `Bar` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/associated-types/issue-64855.rs b/tests/ui/associated-types/issue-64855.rs index 81cf3ae6e83b6..5d325b981a2c6 100644 --- a/tests/ui/associated-types/issue-64855.rs +++ b/tests/ui/associated-types/issue-64855.rs @@ -1,8 +1,13 @@ +// This was originally a test for a `ReEmpty` ICE, but became an unintentional test of +// the coinductiveness of WF predicates. That behavior was removed, and thus this is +// also inadvertently a test for the (non-)co-inductiveness of WF predicates. + pub trait Foo { type Type; } pub struct Bar(::Type) where Self: ; //~^ ERROR the trait bound `Bar: Foo` is not satisfied +//~| ERROR overflow evaluating the requirement `Bar well-formed` fn main() {} diff --git a/tests/ui/associated-types/issue-64855.stderr b/tests/ui/associated-types/issue-64855.stderr index 7c09abdb3b6f3..d8ba1a9d07ee9 100644 --- a/tests/ui/associated-types/issue-64855.stderr +++ b/tests/ui/associated-types/issue-64855.stderr @@ -1,15 +1,28 @@ error[E0277]: the trait bound `Bar: Foo` is not satisfied - --> $DIR/issue-64855.rs:5:19 + --> $DIR/issue-64855.rs:9:19 | LL | pub struct Bar(::Type) where Self: ; | ^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bar` | help: this trait has no implementations, consider adding one - --> $DIR/issue-64855.rs:1:1 + --> $DIR/issue-64855.rs:5:1 | LL | pub trait Foo { | ^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error[E0275]: overflow evaluating the requirement `Bar well-formed` + --> $DIR/issue-64855.rs:9:46 + | +LL | pub struct Bar(::Type) where Self: ; + | ^^^^ + | +note: required by a bound in `Bar` + --> $DIR/issue-64855.rs:9:46 + | +LL | pub struct Bar(::Type) where Self: ; + | ^^^^ required by this bound in `Bar` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0275, E0277. +For more information about an error, try `rustc --explain E0275`. diff --git a/tests/ui/associated-types/issue-85103-layout-debug.rs b/tests/ui/associated-types/issue-85103-layout-debug.rs index 77c9876ffa57f..29a59924ef082 100644 --- a/tests/ui/associated-types/issue-85103-layout-debug.rs +++ b/tests/ui/associated-types/issue-85103-layout-debug.rs @@ -4,6 +4,6 @@ use std::borrow::Cow; #[rustc_layout(debug)] type Edges<'a, E> = Cow<'a, [E]>; -//~^ the trait bound `[E]: ToOwned` is not satisfied +//~^ ERROR the trait bound `[E]: ToOwned` is not satisfied fn main() {} diff --git a/tests/ui/associated-types/mismatch-two-relevant-impls.rs b/tests/ui/associated-types/mismatch-two-relevant-impls.rs new file mode 100644 index 0000000000000..58fd567c27800 --- /dev/null +++ b/tests/ui/associated-types/mismatch-two-relevant-impls.rs @@ -0,0 +1,20 @@ +trait Tr { + type Assoc; +} + +struct W(T); + +impl Tr for W { + type Assoc = u32; +} + +impl Tr for W { + type Assoc = i32; +} + +fn needs_unit>() {} + +fn main() { + needs_unit::>(); + //~^ ERROR type mismatch resolving ` as Tr>::Assoc == ()` +} diff --git a/tests/ui/associated-types/mismatch-two-relevant-impls.stderr b/tests/ui/associated-types/mismatch-two-relevant-impls.stderr new file mode 100644 index 0000000000000..2a1f3ef23ca7b --- /dev/null +++ b/tests/ui/associated-types/mismatch-two-relevant-impls.stderr @@ -0,0 +1,20 @@ +error[E0271]: type mismatch resolving ` as Tr>::Assoc == ()` + --> $DIR/mismatch-two-relevant-impls.rs:18:18 + | +LL | needs_unit::>(); + | ^^^^^^ type mismatch resolving ` as Tr>::Assoc == ()` + | +note: expected this to be `()` + --> $DIR/mismatch-two-relevant-impls.rs:8:18 + | +LL | type Assoc = u32; + | ^^^ +note: required by a bound in `needs_unit` + --> $DIR/mismatch-two-relevant-impls.rs:15:21 + | +LL | fn needs_unit>() {} + | ^^^^^^^^^^ required by this bound in `needs_unit` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs b/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs index 4dfeab9e8c3f0..70685504cccc3 100644 --- a/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs +++ b/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs @@ -13,7 +13,7 @@ impl Foo for Baz { async fn bar(&mut self, _func: F) -> () where F: FnMut() + Send, - //~^ impl has stricter requirements than trait + //~^ ERROR impl has stricter requirements than trait { () } diff --git a/tests/ui/associated-types/substs-ppaux.normal.stderr b/tests/ui/associated-types/substs-ppaux.normal.stderr index 1ea8ab235564e..aa2aca7426e1a 100644 --- a/tests/ui/associated-types/substs-ppaux.normal.stderr +++ b/tests/ui/associated-types/substs-ppaux.normal.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/substs-ppaux.rs:23:17 + --> $DIR/substs-ppaux.rs:22:17 | LL | / fn bar<'a, T>() LL | | where @@ -19,7 +19,7 @@ LL | let x: () = >::bar::<'static, char>(); | ++ error[E0308]: mismatched types - --> $DIR/substs-ppaux.rs:31:17 + --> $DIR/substs-ppaux.rs:30:17 | LL | / fn bar<'a, T>() LL | | where @@ -39,7 +39,7 @@ LL | let x: () = >::bar::<'static, char>(); | ++ error[E0308]: mismatched types - --> $DIR/substs-ppaux.rs:39:17 + --> $DIR/substs-ppaux.rs:38:17 | LL | fn baz() {} | -------- associated function `baz` defined here @@ -57,7 +57,7 @@ LL | let x: () = >::baz(); | ++ error[E0308]: mismatched types - --> $DIR/substs-ppaux.rs:47:17 + --> $DIR/substs-ppaux.rs:46:17 | LL | / fn foo<'z>() LL | | where @@ -77,13 +77,13 @@ LL | let x: () = foo::<'static>(); | ++ error[E0277]: the trait bound `str: Foo<'_, '_, u8>` is not satisfied - --> $DIR/substs-ppaux.rs:55:6 + --> $DIR/substs-ppaux.rs:54:6 | LL | >::bar; | ^^^ the trait `Sized` is not implemented for `str` | note: required for `str` to implement `Foo<'_, '_, u8>` - --> $DIR/substs-ppaux.rs:15:20 + --> $DIR/substs-ppaux.rs:14:20 | LL | impl<'a, 'b, T, S> Foo<'a, 'b, S> for T {} | - ^^^^^^^^^^^^^^ ^ diff --git a/tests/ui/associated-types/substs-ppaux.rs b/tests/ui/associated-types/substs-ppaux.rs index 302a6b345e49b..ceec9978b1c7e 100644 --- a/tests/ui/associated-types/substs-ppaux.rs +++ b/tests/ui/associated-types/substs-ppaux.rs @@ -1,7 +1,6 @@ -// //@ revisions: verbose normal -// //@[verbose] compile-flags: -Z verbose-internals +//@ dont-require-annotations: NOTE trait Foo<'b, 'c, S = u32> { fn bar<'a, T>() @@ -22,35 +21,35 @@ where { let x: () = >::bar::<'static, char>; //[verbose]~^ ERROR mismatched types - //[verbose]~| expected unit type `()` - //[verbose]~| found fn item `fn() {>::bar::<'static, char>}` + //[verbose]~| NOTE expected unit type `()` + //[verbose]~| NOTE found fn item `fn() {>::bar::<'static, char>}` //[normal]~^^^^ ERROR mismatched types - //[normal]~| expected unit type `()` - //[normal]~| found fn item `fn() {>::bar::<'static, char>}` + //[normal]~| NOTE expected unit type `()` + //[normal]~| NOTE found fn item `fn() {>::bar::<'static, char>}` let x: () = >::bar::<'static, char>; //[verbose]~^ ERROR mismatched types - //[verbose]~| expected unit type `()` - //[verbose]~| found fn item `fn() {>::bar::<'static, char>}` + //[verbose]~| NOTE expected unit type `()` + //[verbose]~| NOTE found fn item `fn() {>::bar::<'static, char>}` //[normal]~^^^^ ERROR mismatched types - //[normal]~| expected unit type `()` - //[normal]~| found fn item `fn() {>::bar::<'static, char>}` + //[normal]~| NOTE expected unit type `()` + //[normal]~| NOTE found fn item `fn() {>::bar::<'static, char>}` let x: () = >::baz; //[verbose]~^ ERROR mismatched types - //[verbose]~| expected unit type `()` - //[verbose]~| found fn item `fn() {>::baz}` + //[verbose]~| NOTE expected unit type `()` + //[verbose]~| NOTE found fn item `fn() {>::baz}` //[normal]~^^^^ ERROR mismatched types - //[normal]~| expected unit type `()` - //[normal]~| found fn item `fn() {>::baz}` + //[normal]~| NOTE expected unit type `()` + //[normal]~| NOTE found fn item `fn() {>::baz}` let x: () = foo::<'static>; //[verbose]~^ ERROR mismatched types - //[verbose]~| expected unit type `()` - //[verbose]~| found fn item `fn() {foo::<'static>}` + //[verbose]~| NOTE expected unit type `()` + //[verbose]~| NOTE found fn item `fn() {foo::<'static>}` //[normal]~^^^^ ERROR mismatched types - //[normal]~| expected unit type `()` - //[normal]~| found fn item `fn() {foo::<'static>}` + //[normal]~| NOTE expected unit type `()` + //[normal]~| NOTE found fn item `fn() {foo::<'static>}` >::bar; //[verbose]~^ ERROR the trait bound `str: Foo<'?0, '?1, u8>` is not satisfied diff --git a/tests/ui/associated-types/substs-ppaux.verbose.stderr b/tests/ui/associated-types/substs-ppaux.verbose.stderr index 05cd971c8822e..23cf222a1d37e 100644 --- a/tests/ui/associated-types/substs-ppaux.verbose.stderr +++ b/tests/ui/associated-types/substs-ppaux.verbose.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/substs-ppaux.rs:23:17 + --> $DIR/substs-ppaux.rs:22:17 | LL | / fn bar<'a, T>() LL | | where @@ -19,7 +19,7 @@ LL | let x: () = >::bar::<'static, char>(); | ++ error[E0308]: mismatched types - --> $DIR/substs-ppaux.rs:31:17 + --> $DIR/substs-ppaux.rs:30:17 | LL | / fn bar<'a, T>() LL | | where @@ -39,7 +39,7 @@ LL | let x: () = >::bar::<'static, char>(); | ++ error[E0308]: mismatched types - --> $DIR/substs-ppaux.rs:39:17 + --> $DIR/substs-ppaux.rs:38:17 | LL | fn baz() {} | -------- associated function `baz` defined here @@ -57,7 +57,7 @@ LL | let x: () = >::baz(); | ++ error[E0308]: mismatched types - --> $DIR/substs-ppaux.rs:47:17 + --> $DIR/substs-ppaux.rs:46:17 | LL | / fn foo<'z>() LL | | where @@ -77,13 +77,13 @@ LL | let x: () = foo::<'static>(); | ++ error[E0277]: the trait bound `str: Foo<'?0, '?1, u8>` is not satisfied - --> $DIR/substs-ppaux.rs:55:6 + --> $DIR/substs-ppaux.rs:54:6 | LL | >::bar; | ^^^ the trait `Sized` is not implemented for `str` | note: required for `str` to implement `Foo<'?0, '?1, u8>` - --> $DIR/substs-ppaux.rs:15:20 + --> $DIR/substs-ppaux.rs:14:20 | LL | impl<'a, 'b, T, S> Foo<'a, 'b, S> for T {} | - ^^^^^^^^^^^^^^ ^ diff --git a/tests/ui/async-await/async-closures/by-move-body-inlined-attrs.rs b/tests/ui/async-await/async-closures/by-move-body-inlined-attrs.rs new file mode 100644 index 0000000000000..ecfc06d2bad0c --- /dev/null +++ b/tests/ui/async-await/async-closures/by-move-body-inlined-attrs.rs @@ -0,0 +1,28 @@ +//@ check-pass +//@ compile-flags: -Zinline-mir -Zvalidate-mir +//@ edition: 2024 + +// See comment below. + +use std::future::Future; +use std::pin::pin; +use std::task::{Context, Waker}; + +fn call_once(f: impl FnOnce() -> T) -> T { f() } + +fn main() { + let x = async || {}; + // We first inline `call_once<{async closure}>`. + // + // This gives us a future whose type is the "FnOnce" flavor of the async closure's + // child coroutine. The body of this coroutine is synthetic, which we synthesize in + // the by-move body query. + let fut = pin!(call_once(x)); + // We then try to inline that body in this poll call. + // + // The inliner does some inlinability checks; one of these checks involves checking + // the body for the `#[rustc_no_mir_inline]` attribute. Since the synthetic body had + // no HIR synthesized, but it's still a local def id, we end up ICEing in the + // `local_def_id_to_hir_id` call when trying to read its attrs. + fut.poll(&mut Context::from_waker(Waker::noop())); +} diff --git a/tests/ui/async-await/async-closures/closure-shim-borrowck-error.rs b/tests/ui/async-await/async-closures/closure-shim-borrowck-error.rs index 069744a3282df..12dca587e07d5 100644 --- a/tests/ui/async-await/async-closures/closure-shim-borrowck-error.rs +++ b/tests/ui/async-await/async-closures/closure-shim-borrowck-error.rs @@ -1,4 +1,5 @@ -//@ compile-flags: -Zvalidate-mir --edition=2018 --crate-type=lib -Copt-level=3 +//@ compile-flags: -Zvalidate-mir --crate-type=lib -Copt-level=3 +//@ edition: 2018 fn main() {} diff --git a/tests/ui/async-await/async-closures/closure-shim-borrowck-error.stderr b/tests/ui/async-await/async-closures/closure-shim-borrowck-error.stderr index 52697bac50985..03fa220b0bfae 100644 --- a/tests/ui/async-await/async-closures/closure-shim-borrowck-error.stderr +++ b/tests/ui/async-await/async-closures/closure-shim-borrowck-error.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of `x` which is behind a mutable reference - --> $DIR/closure-shim-borrowck-error.rs:10:18 + --> $DIR/closure-shim-borrowck-error.rs:11:18 | LL | needs_fn_mut(async || { | ^^^^^^^^ `x` is moved here @@ -11,7 +11,7 @@ LL | x.hello(); | move occurs because `x` has type `Ty`, which does not implement the `Copy` trait | note: if `Ty` implemented `Clone`, you could clone the value - --> $DIR/closure-shim-borrowck-error.rs:16:1 + --> $DIR/closure-shim-borrowck-error.rs:17:1 | LL | x.hello(); | - you could clone this value diff --git a/tests/ui/async-await/async-closures/def-path.stderr b/tests/ui/async-await/async-closures/def-path.stderr index 13ebaf67e54ad..b50e353b6988e 100644 --- a/tests/ui/async-await/async-closures/def-path.stderr +++ b/tests/ui/async-await/async-closures/def-path.stderr @@ -5,11 +5,11 @@ LL | let x = async || {}; | -- the expected `async` closure body LL | LL | let () = x(); - | ^^ --- this expression has type `{static main::{closure#0}::{closure#0} upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?6t}` + | ^^ --- this expression has type `{static main::{closure#0}::{closure#0} upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?5t}` | | | expected `async` closure body, found `()` | - = note: expected `async` closure body `{static main::{closure#0}::{closure#0} upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?6t}` + = note: expected `async` closure body `{static main::{closure#0}::{closure#0} upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?5t}` found unit type `()` error: aborting due to 1 previous error diff --git a/tests/ui/async-await/async-closures/imm-deref-lending.rs b/tests/ui/async-await/async-closures/imm-deref-lending.rs new file mode 100644 index 0000000000000..59f8d434d9cc0 --- /dev/null +++ b/tests/ui/async-await/async-closures/imm-deref-lending.rs @@ -0,0 +1,46 @@ +//@ edition: 2021 +//@ check-pass + +#![feature(impl_trait_in_bindings)] + +struct FooS { + precise: i32, +} + +fn ref_inside_mut(f: &mut &FooS) { + let x: impl AsyncFn() = async move || { + let y = &f.precise; + }; +} + +fn mut_inside_ref(f: &&mut FooS) { + let x: impl AsyncFn() = async move || { + let y = &f.precise; + }; +} + +fn mut_ref_inside_mut(f: &mut &mut FooS) { + let x: impl AsyncFn() = async move || { + let y = &f.precise; + }; +} + +fn ref_inside_box(f: Box<&FooS>) { + let x: impl AsyncFn() = async move || { + let y = &f.precise; + }; +} + +fn box_inside_ref(f: &Box) { + let x: impl AsyncFn() = async move || { + let y = &f.precise; + }; +} + +fn box_inside_box(f: Box>) { + let x: impl AsyncFn() = async move || { + let y = &f.precise; + }; +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/imm-deref-not-lending.rs b/tests/ui/async-await/async-closures/imm-deref-not-lending.rs new file mode 100644 index 0000000000000..bd1197cc63654 --- /dev/null +++ b/tests/ui/async-await/async-closures/imm-deref-not-lending.rs @@ -0,0 +1,49 @@ +//@ edition: 2021 + +#![feature(impl_trait_in_bindings)] + +struct FooS { + precise: i32, +} + +fn ref_inside_mut(f: &mut &FooS) { + let x: impl Fn() -> _ = async move || { + let y = &f.precise; + }; +} + +fn mut_inside_ref(f: &&mut FooS) { + let x: impl Fn() -> _ = async move || { + let y = &f.precise; + }; +} + +// Expected to fail, no immutable reference here. +fn mut_ref_inside_mut(f: &mut &mut FooS) { + let x: impl Fn() -> _ = async move || { + //~^ ERROR async closure does not implement `Fn` + let y = &f.precise; + }; +} + +fn ref_inside_box(f: Box<&FooS>) { + let x: impl Fn() -> _ = async move || { + let y = &f.precise; + }; +} + +fn box_inside_ref(f: &Box) { + let x: impl Fn() -> _ = async move || { + let y = &f.precise; + }; +} + +// Expected to fail, no immutable reference here. +fn box_inside_box(f: Box>) { + let x: impl Fn() -> _ = async move || { + //~^ ERROR async closure does not implement `Fn` + let y = &f.precise; + }; +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/imm-deref-not-lending.stderr b/tests/ui/async-await/async-closures/imm-deref-not-lending.stderr new file mode 100644 index 0000000000000..cd3ff55e458ab --- /dev/null +++ b/tests/ui/async-await/async-closures/imm-deref-not-lending.stderr @@ -0,0 +1,14 @@ +error: async closure does not implement `Fn` because it captures state from its environment + --> $DIR/imm-deref-not-lending.rs:23:29 + | +LL | let x: impl Fn() -> _ = async move || { + | ^^^^^^^^^^^^^ + +error: async closure does not implement `Fn` because it captures state from its environment + --> $DIR/imm-deref-not-lending.rs:43:29 + | +LL | let x: impl Fn() -> _ = async move || { + | ^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/async-await/async-closures/is-not-fn.next.stderr b/tests/ui/async-await/async-closures/is-not-fn.next.stderr index 0fab1c15f27df..970970a915114 100644 --- a/tests/ui/async-await/async-closures/is-not-fn.next.stderr +++ b/tests/ui/async-await/async-closures/is-not-fn.next.stderr @@ -1,13 +1,11 @@ -error[E0271]: expected `{async closure@is-not-fn.rs:8:14}` to return `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}` +error[E0271]: type mismatch resolving `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25} == ()` --> $DIR/is-not-fn.rs:8:14 | LL | needs_fn(async || {}); - | -------- ^^^^^^^^^^^ expected `()`, found `async` closure body + | -------- ^^^^^^^^^^^ types differ | | | required by a bound introduced by this call | - = note: expected unit type `()` - found `async` closure body `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}` note: required by a bound in `needs_fn` --> $DIR/is-not-fn.rs:7:25 | diff --git a/tests/ui/async-await/async-closures/is-not-fn.rs b/tests/ui/async-await/async-closures/is-not-fn.rs index e5ab4742daba1..c09ccb3fc2b09 100644 --- a/tests/ui/async-await/async-closures/is-not-fn.rs +++ b/tests/ui/async-await/async-closures/is-not-fn.rs @@ -6,5 +6,6 @@ fn main() { fn needs_fn(x: impl FnOnce()) {} needs_fn(async || {}); - //~^ ERROR expected `{async closure@is-not-fn.rs:8:14}` to return `()` + //[current]~^ ERROR expected `{async closure@is-not-fn.rs:8:14}` to return `()` + //[next]~^^ ERROR type mismatch resolving `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25} == ()` } diff --git a/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs new file mode 100644 index 0000000000000..650fb10d94be0 --- /dev/null +++ b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs @@ -0,0 +1,22 @@ +//@ edition: 2024 + +// Regression test for . + +struct Test; + +impl Test { + async fn an_async_fn(&mut self) { + todo!() + } + + pub async fn uses_takes_asyncfn(&mut self) { + takes_asyncfn(Box::new(async || self.an_async_fn().await)); + //~^ ERROR expected a closure that implements the `AsyncFn` trait, but this closure only implements `AsyncFnMut` + } +} + +async fn takes_asyncfn(_: impl AsyncFn()) { + todo!() +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr new file mode 100644 index 0000000000000..e79f95c197b63 --- /dev/null +++ b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr @@ -0,0 +1,21 @@ +error[E0525]: expected a closure that implements the `AsyncFn` trait, but this closure only implements `AsyncFnMut` + --> $DIR/kind-due-to-arg-with-box-wrap.rs:13:32 + | +LL | takes_asyncfn(Box::new(async || self.an_async_fn().await)); + | ------------- ---------^^^^^^^^-------------------------- + | | | | | + | | | | closure is `AsyncFnMut` because it mutates the variable `*self` here + | | | this closure implements `AsyncFnMut`, not `AsyncFn` + | | the requirement to implement `AsyncFn` derives from here + | required by a bound introduced by this call + | + = note: required for `Box<{async closure@$DIR/kind-due-to-arg-with-box-wrap.rs:13:32: 13:40}>` to implement `AsyncFn()` +note: required by a bound in `takes_asyncfn` + --> $DIR/kind-due-to-arg-with-box-wrap.rs:18:32 + | +LL | async fn takes_asyncfn(_: impl AsyncFn()) { + | ^^^^^^^^^ required by this bound in `takes_asyncfn` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0525`. diff --git a/tests/ui/async-await/async-closures/kind-due-to-rpit.rs b/tests/ui/async-await/async-closures/kind-due-to-rpit.rs new file mode 100644 index 0000000000000..ad19d93b93a3c --- /dev/null +++ b/tests/ui/async-await/async-closures/kind-due-to-rpit.rs @@ -0,0 +1,14 @@ +//@ edition: 2024 + +// Make sure the error message is understandable when an `AsyncFn` goal is not satisfied +// (due to closure kind), and that goal originates from an RPIT. + +fn repro(foo: impl Into) -> impl AsyncFn() { + let inner_fn = async move || { + //~^ ERROR expected a closure that implements the `AsyncFn` trait + let _ = foo.into(); + }; + inner_fn +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/kind-due-to-rpit.stderr b/tests/ui/async-await/async-closures/kind-due-to-rpit.stderr new file mode 100644 index 0000000000000..982cc50e14f97 --- /dev/null +++ b/tests/ui/async-await/async-closures/kind-due-to-rpit.stderr @@ -0,0 +1,17 @@ +error[E0525]: expected a closure that implements the `AsyncFn` trait, but this closure only implements `AsyncFnOnce` + --> $DIR/kind-due-to-rpit.rs:7:20 + | +LL | fn repro(foo: impl Into) -> impl AsyncFn() { + | -------------- the requirement to implement `AsyncFn` derives from here +LL | let inner_fn = async move || { + | ^^^^^^^^^^^^^ this closure implements `AsyncFnOnce`, not `AsyncFn` +LL | +LL | let _ = foo.into(); + | --- closure is `AsyncFnOnce` because it moves the variable `foo` out of its environment +LL | }; +LL | inner_fn + | -------- return type was inferred to be `{async closure@$DIR/kind-due-to-rpit.rs:7:20: 7:33}` here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0525`. diff --git a/tests/ui/async-await/async-drop.rs b/tests/ui/async-await/async-drop.rs deleted file mode 100644 index b1af81423cea5..0000000000000 --- a/tests/ui/async-await/async-drop.rs +++ /dev/null @@ -1,222 +0,0 @@ -//@ run-pass -//@ check-run-results - -// WARNING: If you would ever want to modify this test, -// please consider modifying miri's async drop test at -// `src/tools/miri/tests/pass/async-drop.rs`. - -#![feature(async_drop, impl_trait_in_assoc_type)] -#![allow(incomplete_features, dead_code)] - -//@ edition: 2021 - -// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests -use core::future::{async_drop_in_place, AsyncDrop, Future}; -use core::hint::black_box; -use core::mem::{self, ManuallyDrop}; -use core::pin::{pin, Pin}; -use core::task::{Context, Poll, Waker}; - -async fn test_async_drop(x: T, _size: usize) { - let mut x = mem::MaybeUninit::new(x); - let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) }); - - // FIXME(zetanumbers): This check fully depends on the layout of - // the coroutine state, since async destructor combinators are just - // async functions. - #[cfg(target_pointer_width = "64")] - assert_eq!( - mem::size_of_val(&*dtor), - _size, - "sizes did not match for async destructor of type {}", - core::any::type_name::(), - ); - - test_idempotency(dtor).await; -} - -fn test_idempotency(mut x: Pin<&mut T>) -> impl Future + '_ -where - T: Future, -{ - core::future::poll_fn(move |cx| { - assert_eq!(x.as_mut().poll(cx), Poll::Ready(())); - assert_eq!(x.as_mut().poll(cx), Poll::Ready(())); - Poll::Ready(()) - }) -} - -fn main() { - let waker = Waker::noop(); - let mut cx = Context::from_waker(&waker); - - let i = 13; - let fut = pin!(async { - test_async_drop(Int(0), 0).await; - // FIXME(#63818): niches in coroutines are disabled. - // Some of these sizes should be smaller, as indicated in comments. - test_async_drop(AsyncInt(0), /*104*/ 112).await; - test_async_drop([AsyncInt(1), AsyncInt(2)], /*152*/ 168).await; - test_async_drop((AsyncInt(3), AsyncInt(4)), /*488*/ 528).await; - test_async_drop(5, 0).await; - let j = 42; - test_async_drop(&i, 0).await; - test_async_drop(&j, 0).await; - test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }, /*1688*/ 1792).await; - test_async_drop(ManuallyDrop::new(AsyncInt(9)), 0).await; - - let foo = AsyncInt(10); - test_async_drop(AsyncReference { foo: &foo }, /*104*/ 112).await; - - let foo = AsyncInt(11); - test_async_drop( - || { - black_box(foo); - let foo = AsyncInt(10); - foo - }, - /*120*/ 136, - ) - .await; - - test_async_drop(AsyncEnum::A(AsyncInt(12)), /*680*/ 736).await; - test_async_drop(AsyncEnum::B(SyncInt(13)), /*680*/ 736).await; - - test_async_drop(SyncInt(14), /*16*/ 24).await; - test_async_drop( - SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) }, - /*3064*/ 3296, - ) - .await; - - let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19))); - test_idempotency(async_drop_fut).await; - - let foo = AsyncInt(20); - test_async_drop( - async || { - black_box(foo); - let foo = AsyncInt(19); - // Await point there, but this is async closure so it's fine - black_box(core::future::ready(())).await; - foo - }, - /*120*/ 136, - ) - .await; - - test_async_drop(AsyncUnion { signed: 21 }, /*32*/ 40).await; - }); - let res = fut.poll(&mut cx); - assert_eq!(res, Poll::Ready(())); -} - -struct AsyncInt(i32); - -impl AsyncDrop for AsyncInt { - type Dropper<'a> = impl Future; - - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncInt::Dropper::poll: {}", self.0); - } - } -} - -struct SyncInt(i32); - -impl Drop for SyncInt { - fn drop(&mut self) { - println!("SyncInt::drop: {}", self.0); - } -} - -struct SyncThenAsync { - i: i32, - a: AsyncInt, - b: SyncInt, - c: AsyncInt, -} - -impl Drop for SyncThenAsync { - fn drop(&mut self) { - println!("SyncThenAsync::drop: {}", self.i); - } -} - -struct AsyncReference<'a> { - foo: &'a AsyncInt, -} - -impl AsyncDrop for AsyncReference<'_> { - type Dropper<'a> = impl Future where Self: 'a; - - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncReference::Dropper::poll: {}", self.foo.0); - } - } -} - -struct Int(i32); - -struct AsyncStruct { - i: i32, - a: AsyncInt, - b: AsyncInt, -} - -impl AsyncDrop for AsyncStruct { - type Dropper<'a> = impl Future; - - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncStruct::Dropper::poll: {}", self.i); - } - } -} - -enum AsyncEnum { - A(AsyncInt), - B(SyncInt), -} - -impl AsyncDrop for AsyncEnum { - type Dropper<'a> = impl Future; - - fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - let new_self = match &*self { - AsyncEnum::A(foo) => { - println!("AsyncEnum(A)::Dropper::poll: {}", foo.0); - AsyncEnum::B(SyncInt(foo.0)) - } - AsyncEnum::B(foo) => { - println!("AsyncEnum(B)::Dropper::poll: {}", foo.0); - AsyncEnum::A(AsyncInt(foo.0)) - } - }; - mem::forget(mem::replace(&mut *self, new_self)); - } - } -} - -// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions -union AsyncUnion { - signed: i32, - unsigned: u32, -} - -impl AsyncDrop for AsyncUnion { - type Dropper<'a> = impl Future; - - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!( - "AsyncUnion::Dropper::poll: {}, {}", - unsafe { self.signed }, - unsafe { self.unsigned }, - ); - } - } -} diff --git a/tests/ui/async-await/async-drop/async-drop-future-from-future.rs b/tests/ui/async-await/async-drop/async-drop-future-from-future.rs new file mode 100644 index 0000000000000..44dcbd14f4886 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-from-future.rs @@ -0,0 +1,101 @@ +//@ run-pass +//@ check-run-results +// Future `bar` with internal async drop `Foo` will have async drop itself. +// And we trying to drop this future in sync context (`block_on` func) + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + println!("Foo::new({})", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + println!("Foo::drop({})", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + println!("Foo::async drop({})", self.my_resource_handle); + } +} + +fn main() { + block_on(bar(10)); + println!("done") +} + +async fn baz(ident_base: usize) { + let mut _first = Foo::new(ident_base); +} + +async fn bar(ident_base: usize) { + let mut _first = Foo::new(ident_base); + baz(ident_base + 1).await; +} + +fn block_on(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-future-from-future.run.stdout b/tests/ui/async-await/async-drop/async-drop-future-from-future.run.stdout new file mode 100644 index 0000000000000..c2663b3f23806 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-from-future.run.stdout @@ -0,0 +1,5 @@ +Foo::new(10) +Foo::new(11) +Foo::async drop(11) +Foo::async drop(10) +done diff --git a/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs new file mode 100644 index 0000000000000..417dce62dba90 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs @@ -0,0 +1,82 @@ +//@ run-pass +//@ check-run-results +// Future `bar` with internal async drop `Foo` will have async drop itself. +// And we trying to drop this future in sync context (`block_on` func) + +#![feature(async_drop)] +#![allow(incomplete_features)] + +//@ edition: 2021 + +use std::{ + future::{Future, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + println!("Foo::new({})", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + println!("Foo::drop({})", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + println!("Foo::async drop({})", self.my_resource_handle); + } +} + +fn main() { + block_on(bar(10)); + println!("done") +} + +async fn bar(ident_base: usize) { + let mut _first = Foo::new(ident_base); +} + +fn block_on(fut: F) -> F::Output +where + F: Future, +{ + let mut fut = pin!(fut); + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + } +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stdout b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stdout new file mode 100644 index 0000000000000..ad0b083ca0910 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stdout @@ -0,0 +1,3 @@ +Foo::new(10) +Foo::async drop(10) +done diff --git a/tests/ui/async-await/async-drop/async-drop-glue-array.rs b/tests/ui/async-await/async-drop/async-drop-glue-array.rs new file mode 100644 index 0000000000000..21c08da2337ea --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue-array.rs @@ -0,0 +1,112 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. +// Struct `Complex` contains three `Foo` fields and has complex async drop glue. + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +#[inline(never)] +fn myprintln(msg: &str, my_resource_handle: usize) { + println!("{} : {}", msg, my_resource_handle); +} + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + myprintln("Foo::new()", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + myprintln("Foo::drop()", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + myprintln("Foo::async drop()", self.my_resource_handle); + } +} + +fn main() { + { + let _ = Foo::new(7); + } + println!("Middle"); + { + block_on(bar(10)); + } + println!("Done") +} + +async fn bar(ident_base: usize) { + let _vec: [Foo; 4] = [ + Foo::new(ident_base), + Foo::new(ident_base + 1), + Foo::new(ident_base + 2), + Foo::new(ident_base + 3) + ]; +} + +fn block_on(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-glue-array.run.stdout b/tests/ui/async-await/async-drop/async-drop-glue-array.run.stdout new file mode 100644 index 0000000000000..56b61103cc4e0 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue-array.run.stdout @@ -0,0 +1,12 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Foo::new() : 10 +Foo::new() : 11 +Foo::new() : 12 +Foo::new() : 13 +Foo::async drop() : 10 +Foo::async drop() : 11 +Foo::async drop() : 12 +Foo::async drop() : 13 +Done diff --git a/tests/ui/async-await/async-drop/async-drop-glue-generic.rs b/tests/ui/async-await/async-drop/async-drop-glue-generic.rs new file mode 100644 index 0000000000000..9e8ae13032423 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue-generic.rs @@ -0,0 +1,111 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +#[inline(never)] +fn myprintln(msg: &str, my_resource_handle: usize) { + println!("{} : {}", msg, my_resource_handle); +} + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + myprintln("Foo::new()", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + myprintln("Foo::drop()", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + myprintln("Foo::async drop()", self.my_resource_handle); + } +} + +fn main() { + { + let _ = Foo::new(7); + } + println!("Middle"); + { + block_on(bar(6, 10)); + } + println!("Done") +} + +async fn bar(_arg: T, ident_base: usize) { + let _vec: [Foo; 4] = [ + Foo::new(ident_base), + Foo::new(ident_base + 1), + Foo::new(ident_base + 2), + Foo::new(ident_base + 3) + ]; +} + +fn block_on(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-glue-generic.run.stdout b/tests/ui/async-await/async-drop/async-drop-glue-generic.run.stdout new file mode 100644 index 0000000000000..56b61103cc4e0 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue-generic.run.stdout @@ -0,0 +1,12 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Foo::new() : 10 +Foo::new() : 11 +Foo::new() : 12 +Foo::new() : 13 +Foo::async drop() : 10 +Foo::async drop() : 11 +Foo::async drop() : 12 +Foo::async drop() : 13 +Done diff --git a/tests/ui/async-await/async-drop/async-drop-glue.rs b/tests/ui/async-await/async-drop/async-drop-glue.rs new file mode 100644 index 0000000000000..dc4bb527a51ca --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue.rs @@ -0,0 +1,124 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. +// Struct `Complex` contains three `Foo` fields and has complex async drop glue. + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +#[inline(never)] +fn myprintln(msg: &str, my_resource_handle: usize) { + println!("{} : {}", msg, my_resource_handle); +} + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +#[allow(dead_code)] +struct Complex { + field1: Foo, + field2: Foo, + field3: Foo, +} + +impl Complex { + fn new(my_resource_handle: usize) -> Self { + myprintln("Complex::new()", my_resource_handle); + let field1 = Foo::new(my_resource_handle); + let field2 = Foo::new(my_resource_handle + 1); + let field3 = Foo::new(my_resource_handle + 2); + Complex { field1, field2, field3 } + } +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + myprintln("Foo::new()", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + myprintln("Foo::drop()", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + myprintln("Foo::async drop()", self.my_resource_handle); + } +} + +fn main() { + { + let _ = Foo::new(7); + } + println!("Middle"); + { + block_on(bar(10)); + } + println!("Done") +} + +async fn bar(ident_base: usize) { + let _complex = Complex::new(ident_base); +} + +fn block_on(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-glue.run.stdout b/tests/ui/async-await/async-drop/async-drop-glue.run.stdout new file mode 100644 index 0000000000000..e21a9dd34fa9b --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue.run.stdout @@ -0,0 +1,11 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Complex::new() : 10 +Foo::new() : 10 +Foo::new() : 11 +Foo::new() : 12 +Foo::async drop() : 10 +Foo::async drop() : 11 +Foo::async drop() : 12 +Done diff --git a/tests/ui/async-await/async-drop/async-drop-initial.rs b/tests/ui/async-await/async-drop/async-drop-initial.rs new file mode 100644 index 0000000000000..263b70699f5eb --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-initial.rs @@ -0,0 +1,246 @@ +//@ run-pass +//@ check-run-results + +// WARNING: If you would ever want to modify this test, +// please consider modifying miri's async drop test at +// `src/tools/miri/tests/pass/async-drop.rs`. + +#![feature(async_drop, impl_trait_in_assoc_type)] +#![allow(incomplete_features, dead_code)] + +//@ edition: 2021 + +// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests +use core::future::{async_drop_in_place, AsyncDrop, Future}; +use core::hint::black_box; +use core::mem::{self, ManuallyDrop}; +use core::pin::{pin, Pin}; +use core::task::{Context, Poll, Waker}; + +async fn test_async_drop(x: T, _size: usize) { + let mut x = mem::MaybeUninit::new(x); + let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) }); + + // FIXME(zetanumbers): This check fully depends on the layout of + // the coroutine state, since async destructor combinators are just + // async functions. + #[cfg(target_pointer_width = "64")] + assert_eq!( + mem::size_of_val(&*dtor), + _size, + "sizes did not match for async destructor of type {}", + core::any::type_name::(), + ); + + test_idempotency(dtor).await; +} + +fn test_idempotency(mut x: Pin<&mut T>) -> impl Future + '_ +where + T: Future, +{ + core::future::poll_fn(move |cx| { + assert_eq!(x.as_mut().poll(cx), Poll::Ready(())); + assert_eq!(x.as_mut().poll(cx), Poll::Ready(())); + Poll::Ready(()) + }) +} + +fn main() { + let waker = Waker::noop(); + let mut cx = Context::from_waker(&waker); + + let i = 13; + let fut = pin!(async { + test_async_drop(Int(0), 16).await; + test_async_drop(AsyncInt(0), 32).await; + test_async_drop([AsyncInt(1), AsyncInt(2)], 104).await; + test_async_drop((AsyncInt(3), AsyncInt(4)), 120).await; + test_async_drop(5, 16).await; + let j = 42; + test_async_drop(&i, 16).await; + test_async_drop(&j, 16).await; + test_async_drop( + AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }, + if cfg!(panic = "unwind") { 168 } else { 136 }, + ).await; + test_async_drop(ManuallyDrop::new(AsyncInt(9)), 16).await; + + let foo = AsyncInt(10); + test_async_drop(AsyncReference { foo: &foo }, 32).await; + let _ = ManuallyDrop::new(foo); + + let foo = AsyncInt(11); + test_async_drop( + || { + black_box(foo); + let foo = AsyncInt(10); + foo + }, + 48, + ) + .await; + + test_async_drop(AsyncEnum::A(AsyncInt(12)), 104).await; + test_async_drop(AsyncEnum::B(SyncInt(13)), 104).await; + + test_async_drop(SyncInt(14), 16).await; + test_async_drop( + SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) }, + 120, + ) + .await; + + let mut ptr19 = mem::MaybeUninit::new(AsyncInt(19)); + let async_drop_fut = pin!(unsafe { async_drop_in_place(ptr19.as_mut_ptr()) }); + test_idempotency(async_drop_fut).await; + + let foo = AsyncInt(20); + test_async_drop( + async || { + black_box(foo); + let foo = AsyncInt(19); + // Await point there, but this is async closure so it's fine + black_box(core::future::ready(())).await; + foo + }, + 48, + ) + .await; + + test_async_drop(AsyncUnion { signed: 21 }, 32).await; + }); + let res = fut.poll(&mut cx); + assert_eq!(res, Poll::Ready(())); +} + +struct AsyncInt(i32); + +impl Drop for AsyncInt { + fn drop(&mut self) { + println!("AsyncInt::drop: {}", self.0); + } +} +impl AsyncDrop for AsyncInt { + async fn drop(self: Pin<&mut Self>) { + println!("AsyncInt::Dropper::poll: {}", self.0); + } +} + +struct SyncInt(i32); + +impl Drop for SyncInt { + fn drop(&mut self) { + println!("SyncInt::drop: {}", self.0); + } +} + +struct SyncThenAsync { + i: i32, + a: AsyncInt, + b: SyncInt, + c: AsyncInt, +} + +impl Drop for SyncThenAsync { + fn drop(&mut self) { + println!("SyncThenAsync::drop: {}", self.i); + } +} + +struct AsyncReference<'a> { + foo: &'a AsyncInt, +} + +impl Drop for AsyncReference<'_> { + fn drop(&mut self) { + println!("AsyncReference::drop: {}", self.foo.0); + } +} + +impl AsyncDrop for AsyncReference<'_> { + async fn drop(self: Pin<&mut Self>) { + println!("AsyncReference::Dropper::poll: {}", self.foo.0); + } +} + +struct Int(i32); + +struct AsyncStruct { + i: i32, + a: AsyncInt, + b: AsyncInt, +} + +impl Drop for AsyncStruct { + fn drop(&mut self) { + println!("AsyncStruct::drop: {}", self.i); + } +} + +impl AsyncDrop for AsyncStruct { + async fn drop(self: Pin<&mut Self>) { + println!("AsyncStruct::Dropper::poll: {}", self.i); + } +} + +enum AsyncEnum { + A(AsyncInt), + B(SyncInt), +} + +impl Drop for AsyncEnum { + fn drop(&mut self) { + let new_self = match self { + AsyncEnum::A(foo) => { + println!("AsyncEnum(A)::drop: {}", foo.0); + AsyncEnum::B(SyncInt(foo.0)) + } + AsyncEnum::B(foo) => { + println!("AsyncEnum(B)::drop: {}", foo.0); + AsyncEnum::A(AsyncInt(foo.0)) + } + }; + mem::forget(mem::replace(&mut *self, new_self)); + } +} +impl AsyncDrop for AsyncEnum { + async fn drop(mut self: Pin<&mut Self>) { + let new_self = match &*self { + AsyncEnum::A(foo) => { + println!("AsyncEnum(A)::Dropper::poll: {}", foo.0); + AsyncEnum::B(SyncInt(foo.0)) + } + AsyncEnum::B(foo) => { + println!("AsyncEnum(B)::Dropper::poll: {}", foo.0); + AsyncEnum::A(AsyncInt(foo.0)) + } + }; + mem::forget(mem::replace(&mut *self, new_self)); + } +} + +// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions +union AsyncUnion { + signed: i32, + unsigned: u32, +} + +impl Drop for AsyncUnion { + fn drop(&mut self) { + println!( + "AsyncUnion::drop: {}, {}", + unsafe { self.signed }, + unsafe { self.unsigned }, + ); + } +} +impl AsyncDrop for AsyncUnion { + async fn drop(self: Pin<&mut Self>) { + println!( + "AsyncUnion::Dropper::poll: {}, {}", + unsafe { self.signed }, + unsafe { self.unsigned }, + ); + } +} diff --git a/tests/ui/async-await/async-drop.run.stdout b/tests/ui/async-await/async-drop/async-drop-initial.run.stdout similarity index 100% rename from tests/ui/async-await/async-drop.run.stdout rename to tests/ui/async-await/async-drop/async-drop-initial.run.stdout diff --git a/tests/ui/async-await/async-drop/async-drop-middle-drop.rs b/tests/ui/async-await/async-drop/async-drop-middle-drop.rs new file mode 100644 index 0000000000000..772a853fe1eff --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-middle-drop.rs @@ -0,0 +1,110 @@ +//@ run-pass +//@ check-run-results +// Test async drop of coroutine `bar` (with internal async drop), +// stopped at the middle of execution, with AsyncDrop object Foo active. + +#![feature(async_drop)] +#![allow(incomplete_features)] + +//@ edition: 2021 + +use std::mem::ManuallyDrop; + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + println!("Foo::new({})", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + println!("Foo::drop({})", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + println!("Foo::async drop({})", self.my_resource_handle); + } +} + +fn main() { + block_on_and_drop_in_the_middle(bar(10)); + println!("done") +} + +pub struct MiddleFuture { + first_call: bool, +} +impl Future for MiddleFuture { + type Output = (); + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + if self.first_call { + println!("MiddleFuture first poll"); + self.first_call = false; + Poll::Pending + } else { + println!("MiddleFuture Ready"); + Poll::Ready(()) + } + } +} + +async fn bar(ident_base: usize) { + let middle = MiddleFuture { first_call: true }; + let mut _first = Foo::new(ident_base); + middle.await; // Hanging `bar` future before Foo drop +} + +fn block_on_and_drop_in_the_middle(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let poll1 = fut.as_mut().poll(&mut context); + assert!(poll1.is_pending()); + + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-middle-drop.run.stdout b/tests/ui/async-await/async-drop/async-drop-middle-drop.run.stdout new file mode 100644 index 0000000000000..60df7674d0a9d --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-middle-drop.run.stdout @@ -0,0 +1,4 @@ +Foo::new(10) +MiddleFuture first poll +Foo::async drop(10) +done diff --git a/tests/ui/async-await/async-drop/async-drop-open.rs b/tests/ui/async-await/async-drop/async-drop-open.rs new file mode 100644 index 0000000000000..d14eff874aeac --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-open.rs @@ -0,0 +1,127 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. +// Struct `Complex` contains three `Foo` fields and one of them is moved out. + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +#[inline(never)] +fn myprintln(msg: &str, my_resource_handle: usize) { + println!("{} : {}", msg, my_resource_handle); +} + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +#[allow(dead_code)] +struct Complex { + field1: Foo, + field2: Foo, + field3: Foo, +} + +impl Complex { + fn new(my_resource_handle: usize) -> Self { + myprintln("Complex::new()", my_resource_handle); + let field1 = Foo::new(my_resource_handle); + let field2 = Foo::new(my_resource_handle + 1); + let field3 = Foo::new(my_resource_handle + 2); + Complex { field1, field2, field3 } + } +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + myprintln("Foo::new()", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + myprintln("Foo::drop()", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + myprintln("Foo::async drop()", self.my_resource_handle); + } +} + +fn main() { + { + let _ = Foo::new(7); + } + println!("Middle"); + // Inside field1 and field3 of Complex must be dropped (as async drop) + // field2 must be dropped here (as sync drop) + { + let _field2 = block_on(bar(10)); + } + println!("Done") +} + +async fn bar(ident_base: usize) -> Foo { + let complex = Complex::new(ident_base); + complex.field2 +} + +fn block_on(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-open.run.stdout b/tests/ui/async-await/async-drop/async-drop-open.run.stdout new file mode 100644 index 0000000000000..e72dfd8a74390 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-open.run.stdout @@ -0,0 +1,11 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Complex::new() : 10 +Foo::new() : 10 +Foo::new() : 11 +Foo::new() : 12 +Foo::async drop() : 10 +Foo::async drop() : 12 +Foo::drop() : 11 +Done diff --git a/tests/ui/async-await/async-drop/async-drop.rs b/tests/ui/async-await/async-drop/async-drop.rs new file mode 100644 index 0000000000000..2d9c5934be5d7 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop.rs @@ -0,0 +1,105 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. +// Sync version is called in sync context, async version is called in async function. + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +#[inline(never)] +fn myprintln(msg: &str, my_resource_handle: usize) { + println!("{} : {}", msg, my_resource_handle); +} + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + myprintln("Foo::new()", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + myprintln("Foo::drop()", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + myprintln("Foo::async drop()", self.my_resource_handle); + } +} + +fn main() { + { + let _ = Foo::new(7); + } + println!("Middle"); + block_on(bar(10)); + println!("Done") +} + +async fn bar(ident_base: usize) { + let mut _first = Foo::new(ident_base); +} + +fn block_on(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop.run.stdout b/tests/ui/async-await/async-drop/async-drop.run.stdout new file mode 100644 index 0000000000000..cb7d0b0fea59d --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop.run.stdout @@ -0,0 +1,6 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Foo::new() : 10 +Foo::async drop() : 10 +Done diff --git a/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs b/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs new file mode 100644 index 0000000000000..1729599f7b3f3 --- /dev/null +++ b/tests/ui/async-await/async-drop/auxiliary/async-drop-dep.rs @@ -0,0 +1,28 @@ +//@ edition:2021 + +#![feature(async_drop)] +#![allow(incomplete_features)] + +pub struct HasDrop; +impl Drop for HasDrop{ + fn drop(&mut self) { + println!("Sync drop"); + } +} + +pub struct MongoDrop; +impl MongoDrop { + pub async fn new() -> Result { + Ok(Self) + } +} +impl Drop for MongoDrop{ + fn drop(&mut self) { + println!("Sync drop"); + } +} +impl std::future::AsyncDrop for MongoDrop { + async fn drop(self: std::pin::Pin<&mut Self>) { + println!("Async drop"); + } +} diff --git a/tests/ui/async-await/async-drop/dependency-dropped.rs b/tests/ui/async-await/async-drop/dependency-dropped.rs new file mode 100644 index 0000000000000..d7f415e19aadf --- /dev/null +++ b/tests/ui/async-await/async-drop/dependency-dropped.rs @@ -0,0 +1,37 @@ +//@ run-pass +//@ check-run-results +//@ revisions: with_feature without_feature +//@ aux-build:async-drop-dep.rs +//@ edition:2021 + +#![cfg_attr(with_feature, feature(async_drop))] +//[without_feature]~^ WARN found async drop types in dependency `async_drop_dep`, but async_drop feature is disabled for `dependency_dropped` + +#![allow(incomplete_features)] + +extern crate async_drop_dep; + +use async_drop_dep::MongoDrop; +use std::pin::pin; +use std::task::{Context, Poll, Waker}; +use std::future::Future; + +async fn asyncdrop() { + let _ = MongoDrop::new().await; +} + +pub fn block_on(fut: impl Future) -> T { + let mut fut = pin!(fut); + let ctx = &mut Context::from_waker(Waker::noop()); + + loop { + match fut.as_mut().poll(ctx) { + Poll::Pending => {} + Poll::Ready(t) => break t, + } + } +} + +fn main() { + let _ = block_on(asyncdrop()); +} diff --git a/tests/ui/async-await/async-drop/dependency-dropped.with_feature.run.stdout b/tests/ui/async-await/async-drop/dependency-dropped.with_feature.run.stdout new file mode 100644 index 0000000000000..7aaf70c12d608 --- /dev/null +++ b/tests/ui/async-await/async-drop/dependency-dropped.with_feature.run.stdout @@ -0,0 +1 @@ +Async drop diff --git a/tests/ui/async-await/async-drop/dependency-dropped.without_feature.run.stdout b/tests/ui/async-await/async-drop/dependency-dropped.without_feature.run.stdout new file mode 100644 index 0000000000000..80eeeefc2220f --- /dev/null +++ b/tests/ui/async-await/async-drop/dependency-dropped.without_feature.run.stdout @@ -0,0 +1 @@ +Sync drop diff --git a/tests/ui/async-await/async-drop/dependency-dropped.without_feature.stderr b/tests/ui/async-await/async-drop/dependency-dropped.without_feature.stderr new file mode 100644 index 0000000000000..96a4572055c60 --- /dev/null +++ b/tests/ui/async-await/async-drop/dependency-dropped.without_feature.stderr @@ -0,0 +1,10 @@ +warning: found async drop types in dependency `async_drop_dep`, but async_drop feature is disabled for `dependency_dropped` + --> $DIR/dependency-dropped.rs:7:1 + | +LL | #![cfg_attr(with_feature, feature(async_drop))] + | ^ + | + = help: if async drop type will be dropped in a crate without `feature(async_drop)`, sync Drop will be used + +warning: 1 warning emitted + diff --git a/tests/ui/async-await/async-drop/deref-later-projection.rs b/tests/ui/async-await/async-drop/deref-later-projection.rs new file mode 100644 index 0000000000000..baf81daf766b6 --- /dev/null +++ b/tests/ui/async-await/async-drop/deref-later-projection.rs @@ -0,0 +1,26 @@ +// Ex-ICE: #140975 +//@ compile-flags: -Zvalidate-mir +//@ build-pass +//@ edition:2021 +#![crate_type = "lib"] +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::{future::AsyncDrop, pin::Pin}; + +struct HasAsyncDrop ; +impl Drop for HasAsyncDrop { + fn drop(&mut self) {} +} +impl AsyncDrop for HasAsyncDrop { + async fn drop(self: Pin<&mut Self>) {} +} + +struct Holder { + inner: HasAsyncDrop, +} +async fn bar() { + Holder { + inner: HasAsyncDrop + }; +} diff --git a/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.rs b/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.rs new file mode 100644 index 0000000000000..bd0a95eb1e497 --- /dev/null +++ b/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.rs @@ -0,0 +1,17 @@ +//@ edition: 2024 +// Ex-ICE: #140974 +#![crate_type = "lib"] +#![allow(incomplete_features)] +#![feature(async_drop)] +use core::future::AsyncDrop; + +async fn fun(_: HasIncompleteAsyncDrop) {} + +struct HasIncompleteAsyncDrop; +impl Drop for HasIncompleteAsyncDrop { + fn drop(&mut self) {} +} +impl AsyncDrop for HasIncompleteAsyncDrop { + //~^ ERROR: not all trait items implemented, missing: `drop` [E0046] + // not implemented yet.. +} diff --git a/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.stderr b/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.stderr new file mode 100644 index 0000000000000..d8582398c797e --- /dev/null +++ b/tests/ui/async-await/async-drop/elaborate-index-out-of-bounds.stderr @@ -0,0 +1,11 @@ +error[E0046]: not all trait items implemented, missing: `drop` + --> $DIR/elaborate-index-out-of-bounds.rs:14:1 + | +LL | impl AsyncDrop for HasIncompleteAsyncDrop { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation + | + = help: implement the missing item: `async fn drop(self: Pin<&mut Self>) { todo!() }` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0046`. diff --git a/tests/ui/async-await/async-drop/ex-ice-132103.rs b/tests/ui/async-await/async-drop/ex-ice-132103.rs new file mode 100644 index 0000000000000..3d32cb14fb061 --- /dev/null +++ b/tests/ui/async-await/async-drop/ex-ice-132103.rs @@ -0,0 +1,27 @@ +//@ run-pass +//! This test used to ICE: rust-lang/rust#132103 +//! Fixed when re-work async drop to shim drop glue coroutine scheme. +//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes +//@ edition: 2018 +#![feature(async_drop)] +#![allow(incomplete_features)] + +use core::future::{async_drop_in_place, Future}; +use core::mem::{self}; +use core::pin::pin; +use core::task::{Context, Waker}; + +async fn test_async_drop(x: T) { + let mut x = mem::MaybeUninit::new(x); + pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) }); +} + +fn main() { + let waker = Waker::noop(); + let mut cx = Context::from_waker(&waker); + + let fut = pin!(async { + test_async_drop(test_async_drop(0)).await; + }); + let _ = fut.poll(&mut cx); +} diff --git a/tests/ui/async-await/async-drop/ex-ice1.rs b/tests/ui/async-await/async-drop/ex-ice1.rs new file mode 100644 index 0000000000000..f514c57097a3a --- /dev/null +++ b/tests/ui/async-await/async-drop/ex-ice1.rs @@ -0,0 +1,13 @@ +//! This test used to ICE: rust-lang/rust#128695 +//! Fixed when re-work async drop to shim drop glue coroutine scheme. +//@ edition: 2021 + +use core::pin::{pin, Pin}; + +fn main() { + let fut = pin!(async { + let async_drop_fut = pin!(core::future::async_drop(async {})); //~ ERROR: expected function, found module `core::future::async_drop` + //~^ ERROR: module `async_drop` is private + (async_drop_fut).await; + }); +} diff --git a/tests/ui/async-await/async-drop/ex-ice1.stderr b/tests/ui/async-await/async-drop/ex-ice1.stderr new file mode 100644 index 0000000000000..68533edeb086c --- /dev/null +++ b/tests/ui/async-await/async-drop/ex-ice1.stderr @@ -0,0 +1,19 @@ +error[E0423]: expected function, found module `core::future::async_drop` + --> $DIR/ex-ice1.rs:9:35 + | +LL | let async_drop_fut = pin!(core::future::async_drop(async {})); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not a function + +error[E0603]: module `async_drop` is private + --> $DIR/ex-ice1.rs:9:49 + | +LL | let async_drop_fut = pin!(core::future::async_drop(async {})); + | ^^^^^^^^^^ private module + | +note: the module `async_drop` is defined here + --> $SRC_DIR/core/src/future/mod.rs:LL:COL + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0423, E0603. +For more information about an error, try `rustc --explain E0423`. diff --git a/tests/ui/async-await/async-drop/open-drop-error.rs b/tests/ui/async-await/async-drop/open-drop-error.rs new file mode 100644 index 0000000000000..1d97eee521094 --- /dev/null +++ b/tests/ui/async-await/async-drop/open-drop-error.rs @@ -0,0 +1,21 @@ +//@ compile-flags: -Zmir-enable-passes=+DataflowConstProp +//@ edition: 2021 +//@ build-pass +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; +use std::{ + future::async_drop_in_place, + pin::{pin, Pin}, +}; +fn main() { + a(b) +} +fn b() {} +fn a(d: C) { + let e = pin!(ManuallyDrop::new(d)); + let f = unsafe { Pin::map_unchecked_mut(e, |g| &mut **g) }; + let h = unsafe { async_drop_in_place(f.get_unchecked_mut()) }; + h; +} diff --git a/tests/ui/async-await/async-drop/partly-dropped-tuple.rs b/tests/ui/async-await/async-drop/partly-dropped-tuple.rs new file mode 100644 index 0000000000000..147caaf4cfd1d --- /dev/null +++ b/tests/ui/async-await/async-drop/partly-dropped-tuple.rs @@ -0,0 +1,11 @@ +//@ edition: 2024 +//@ build-pass +#![crate_type = "lib"] +#![allow(incomplete_features)] +#![feature(async_drop)] +async fn move_part_await_return_rest_tuple() -> Vec { + let x = (vec![3], vec![4, 4]); + drop(x.1); + + x.0 +} diff --git a/tests/ui/async-await/async-fn/impl-trait.rs b/tests/ui/async-await/async-fn/impl-trait.rs index f284de8981ad8..89c41ceb2da51 100644 --- a/tests/ui/async-await/async-fn/impl-trait.rs +++ b/tests/ui/async-await/async-fn/impl-trait.rs @@ -4,11 +4,14 @@ #![feature(type_alias_impl_trait)] type Tait = impl AsyncFn(); +#[define_opaque(Tait)] fn tait() -> Tait { || async {} } -fn foo(x: impl AsyncFn()) -> impl AsyncFn() { x } +fn foo(x: impl AsyncFn()) -> impl AsyncFn() { + x +} fn param() {} diff --git a/tests/ui/async-await/async-gen-move-suggestion.fixed b/tests/ui/async-await/async-gen-move-suggestion.fixed new file mode 100644 index 0000000000000..d802076552895 --- /dev/null +++ b/tests/ui/async-await/async-gen-move-suggestion.fixed @@ -0,0 +1,35 @@ +// This is a regression test for . +// It ensures that the "add `move` keyword" suggestion is valid. + +//@ run-rustfix +//@ edition:2024 + +#![feature(coroutines)] +#![feature(gen_blocks)] +#![feature(async_iterator)] + +use std::async_iter::AsyncIterator; + +#[allow(dead_code)] +fn moved() -> impl AsyncIterator { + let mut x = "foo".to_string(); + + async gen move { //~ ERROR + x.clear(); + for x in 3..6 { yield x } + } +} + +#[allow(dead_code)] +fn check_with_whitespace_chars() -> impl AsyncIterator { + let mut x = "foo".to_string(); + + async // Just to check that whitespace characters are correctly handled + gen move { //~^ ERROR + x.clear(); + for x in 3..6 { yield x } + } +} + +fn main() { +} diff --git a/tests/ui/async-await/async-gen-move-suggestion.rs b/tests/ui/async-await/async-gen-move-suggestion.rs new file mode 100644 index 0000000000000..825fb0fd1898c --- /dev/null +++ b/tests/ui/async-await/async-gen-move-suggestion.rs @@ -0,0 +1,35 @@ +// This is a regression test for . +// It ensures that the "add `move` keyword" suggestion is valid. + +//@ run-rustfix +//@ edition:2024 + +#![feature(coroutines)] +#![feature(gen_blocks)] +#![feature(async_iterator)] + +use std::async_iter::AsyncIterator; + +#[allow(dead_code)] +fn moved() -> impl AsyncIterator { + let mut x = "foo".to_string(); + + async gen { //~ ERROR + x.clear(); + for x in 3..6 { yield x } + } +} + +#[allow(dead_code)] +fn check_with_whitespace_chars() -> impl AsyncIterator { + let mut x = "foo".to_string(); + + async // Just to check that whitespace characters are correctly handled + gen { //~^ ERROR + x.clear(); + for x in 3..6 { yield x } + } +} + +fn main() { +} diff --git a/tests/ui/async-await/async-gen-move-suggestion.stderr b/tests/ui/async-await/async-gen-move-suggestion.stderr new file mode 100644 index 0000000000000..b8cdb8be7a4ae --- /dev/null +++ b/tests/ui/async-await/async-gen-move-suggestion.stderr @@ -0,0 +1,47 @@ +error[E0373]: async gen block may outlive the current function, but it borrows `x`, which is owned by the current function + --> $DIR/async-gen-move-suggestion.rs:17:5 + | +LL | async gen { + | ^^^^^^^^^ may outlive borrowed value `x` +LL | x.clear(); + | - `x` is borrowed here + | +note: async gen block is returned here + --> $DIR/async-gen-move-suggestion.rs:17:5 + | +LL | / async gen { +LL | | x.clear(); +LL | | for x in 3..6 { yield x } +LL | | } + | |_____^ +help: to force the async gen block to take ownership of `x` (and any other referenced variables), use the `move` keyword + | +LL | async gen move { + | ++++ + +error[E0373]: async gen block may outlive the current function, but it borrows `x`, which is owned by the current function + --> $DIR/async-gen-move-suggestion.rs:27:5 + | +LL | / async // Just to check that whitespace characters are correctly handled +LL | | gen { + | |_______^ may outlive borrowed value `x` +LL | x.clear(); + | - `x` is borrowed here + | +note: async gen block is returned here + --> $DIR/async-gen-move-suggestion.rs:27:5 + | +LL | / async // Just to check that whitespace characters are correctly handled +LL | | gen { +LL | | x.clear(); +LL | | for x in 3..6 { yield x } +LL | | } + | |_____^ +help: to force the async gen block to take ownership of `x` (and any other referenced variables), use the `move` keyword + | +LL | gen move { + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0373`. diff --git a/tests/ui/async-await/drop-track-field-assign-nonsend.rs b/tests/ui/async-await/drop-track-field-assign-nonsend.rs index 7002836ee47ff..2b93f90137671 100644 --- a/tests/ui/async-await/drop-track-field-assign-nonsend.rs +++ b/tests/ui/async-await/drop-track-field-assign-nonsend.rs @@ -40,5 +40,5 @@ fn main() { let agent = Agent { info_result: InfoResult { node: None } }; // FIXME: It would be nice for this to work. See #94067. assert_send(agent.handle()); - //~^ cannot be sent between threads safely + //~^ ERROR cannot be sent between threads safely } diff --git a/tests/ui/async-await/dyn/mut-is-pointer-like.rs b/tests/ui/async-await/dyn/mut-is-pointer-like.rs index 93e8281164ce9..a82567e372e16 100644 --- a/tests/ui/async-await/dyn/mut-is-pointer-like.rs +++ b/tests/ui/async-await/dyn/mut-is-pointer-like.rs @@ -1,11 +1,9 @@ //@ aux-build:block-on.rs //@ edition: 2021 -//@ run-pass -//@ check-run-results +//@ known-bug: #133119 #![allow(refining_impl_trait)] #![feature(async_fn_in_dyn_trait)] -//~^ WARN the feature `async_fn_in_dyn_trait` is incomplete extern crate block_on; diff --git a/tests/ui/async-await/dyn/mut-is-pointer-like.stderr b/tests/ui/async-await/dyn/mut-is-pointer-like.stderr index 7c72ce43cf05a..bf20473924bc5 100644 --- a/tests/ui/async-await/dyn/mut-is-pointer-like.stderr +++ b/tests/ui/async-await/dyn/mut-is-pointer-like.stderr @@ -1,5 +1,5 @@ warning: the feature `async_fn_in_dyn_trait` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/mut-is-pointer-like.rs:7:12 + --> $DIR/mut-is-pointer-like.rs:6:12 | LL | #![feature(async_fn_in_dyn_trait)] | ^^^^^^^^^^^^^^^^^^^^^ @@ -7,5 +7,65 @@ LL | #![feature(async_fn_in_dyn_trait)] = note: see issue #133119 for more information = note: `#[warn(incomplete_features)]` on by default -warning: 1 warning emitted +error[E0038]: the trait `AsyncTrait` is not dyn compatible + --> $DIR/mut-is-pointer-like.rs:35:16 + | +LL | let x: Pin<&mut dyn AsyncTrait> = f; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/mut-is-pointer-like.rs:16:14 + | +LL | trait AsyncTrait { + | ---------- this trait is not dyn compatible... +... +LL | async fn async_dispatch(self: Pin<&mut Self>) -> Self::Output; + | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` + = help: consider moving `async_dispatch` to another trait + +error[E0038]: the trait `AsyncTrait` is not dyn compatible + --> $DIR/mut-is-pointer-like.rs:35:56 + | +LL | let x: Pin<&mut dyn AsyncTrait> = f; + | ^ `AsyncTrait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/mut-is-pointer-like.rs:16:14 + | +LL | trait AsyncTrait { + | ---------- this trait is not dyn compatible... +... +LL | async fn async_dispatch(self: Pin<&mut Self>) -> Self::Output; + | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` + = help: consider moving `async_dispatch` to another trait + = note: required for the cast from `Pin<&mut {async block@$DIR/mut-is-pointer-like.rs:32:32: 32:37}>` to `Pin<&mut dyn AsyncTrait>` + +error[E0277]: the trait bound `dyn AsyncTrait: AsyncTrait` is not satisfied + --> $DIR/mut-is-pointer-like.rs:36:11 + | +LL | x.async_dispatch().await; + | ^^^^^^^^^^^^^^ the trait `AsyncTrait` is not implemented for `dyn AsyncTrait` + +error[E0038]: the trait `AsyncTrait` is not dyn compatible + --> $DIR/mut-is-pointer-like.rs:36:9 + | +LL | x.async_dispatch().await; + | ^^^^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/mut-is-pointer-like.rs:16:14 + | +LL | trait AsyncTrait { + | ---------- this trait is not dyn compatible... +... +LL | async fn async_dispatch(self: Pin<&mut Self>) -> Self::Output; + | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` + = help: consider moving `async_dispatch` to another trait + +error: aborting due to 4 previous errors; 1 warning emitted +Some errors have detailed explanations: E0038, E0277. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/async-await/dyn/works.rs b/tests/ui/async-await/dyn/works.rs index 0732a3ee2f263..f406a7b593f09 100644 --- a/tests/ui/async-await/dyn/works.rs +++ b/tests/ui/async-await/dyn/works.rs @@ -1,11 +1,9 @@ //@ aux-build:block-on.rs //@ edition: 2021 -//@ run-pass -//@ check-run-results +//@ known-bug: #133119 #![allow(refining_impl_trait)] #![feature(async_fn_in_dyn_trait)] -//~^ WARN the feature `async_fn_in_dyn_trait` is incomplete extern crate block_on; diff --git a/tests/ui/async-await/dyn/works.stderr b/tests/ui/async-await/dyn/works.stderr index 2c7db7c32f59f..47abeab5aacb5 100644 --- a/tests/ui/async-await/dyn/works.stderr +++ b/tests/ui/async-await/dyn/works.stderr @@ -1,5 +1,5 @@ warning: the feature `async_fn_in_dyn_trait` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/works.rs:7:12 + --> $DIR/works.rs:6:12 | LL | #![feature(async_fn_in_dyn_trait)] | ^^^^^^^^^^^^^^^^^^^^^ @@ -7,5 +7,75 @@ LL | #![feature(async_fn_in_dyn_trait)] = note: see issue #133119 for more information = note: `#[warn(incomplete_features)]` on by default -warning: 1 warning emitted +error[E0038]: the trait `AsyncTrait` is not dyn compatible + --> $DIR/works.rs:27:34 + | +LL | let x: &dyn AsyncTrait = &"hello, world!"; + | ^^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/works.rs:14:14 + | +LL | trait AsyncTrait { + | ---------- this trait is not dyn compatible... +LL | async fn async_dispatch(&self); + | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` + = help: consider moving `async_dispatch` to another trait + = help: only type `&'static str` implements `AsyncTrait`; consider using it directly instead. + = note: required for the cast from `&&'static str` to `&dyn AsyncTrait` + +error[E0038]: the trait `AsyncTrait` is not dyn compatible + --> $DIR/works.rs:27:16 + | +LL | let x: &dyn AsyncTrait = &"hello, world!"; + | ^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/works.rs:14:14 + | +LL | trait AsyncTrait { + | ---------- this trait is not dyn compatible... +LL | async fn async_dispatch(&self); + | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` + = help: consider moving `async_dispatch` to another trait + = help: only type `&'static str` implements `AsyncTrait`; consider using it directly instead. + +error[E0038]: the trait `AsyncTrait` is not dyn compatible + --> $DIR/works.rs:28:11 + | +LL | x.async_dispatch().await; + | ^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/works.rs:14:14 + | +LL | trait AsyncTrait { + | ---------- this trait is not dyn compatible... +LL | async fn async_dispatch(&self); + | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` + = help: consider moving `async_dispatch` to another trait + = help: only type `&'static str` implements `AsyncTrait`; consider using it directly instead. + +error[E0038]: the trait `AsyncTrait` is not dyn compatible + --> $DIR/works.rs:28:9 + | +LL | x.async_dispatch().await; + | ^^^^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/works.rs:14:14 + | +LL | trait AsyncTrait { + | ---------- this trait is not dyn compatible... +LL | async fn async_dispatch(&self); + | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` + = help: consider moving `async_dispatch` to another trait + = help: only type `&'static str` implements `AsyncTrait`; consider using it directly instead. + +error: aborting due to 4 previous errors; 1 warning emitted +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/async-await/dyn/wrong-size.rs b/tests/ui/async-await/dyn/wrong-size.rs index ac15dd2606767..f5fce3648ac88 100644 --- a/tests/ui/async-await/dyn/wrong-size.rs +++ b/tests/ui/async-await/dyn/wrong-size.rs @@ -1,7 +1,7 @@ //@ edition: 2021 +//@ known-bug: #133119 #![feature(async_fn_in_dyn_trait)] -//~^ WARN the feature `async_fn_in_dyn_trait` is incomplete use std::future::Future; @@ -19,5 +19,5 @@ impl AsyncTrait for &'static str { fn main() { let x: &dyn AsyncTrait = &"hello, world!"; - //~^ ERROR `impl Future` needs to have the same ABI as a pointer + // FIXME ~^ ERROR `impl Future` needs to have the same ABI as a pointer } diff --git a/tests/ui/async-await/dyn/wrong-size.stderr b/tests/ui/async-await/dyn/wrong-size.stderr index 0202b5f240977..b4684f4fc174a 100644 --- a/tests/ui/async-await/dyn/wrong-size.stderr +++ b/tests/ui/async-await/dyn/wrong-size.stderr @@ -1,5 +1,5 @@ warning: the feature `async_fn_in_dyn_trait` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/wrong-size.rs:3:12 + --> $DIR/wrong-size.rs:4:12 | LL | #![feature(async_fn_in_dyn_trait)] | ^^^^^^^^^^^^^^^^^^^^^ @@ -7,15 +7,41 @@ LL | #![feature(async_fn_in_dyn_trait)] = note: see issue #133119 for more information = note: `#[warn(incomplete_features)]` on by default -error[E0277]: `impl Future` needs to have the same ABI as a pointer +error[E0038]: the trait `AsyncTrait` is not dyn compatible --> $DIR/wrong-size.rs:21:30 | LL | let x: &dyn AsyncTrait = &"hello, world!"; - | ^^^^^^^^^^^^^^^^ `impl Future` needs to be a pointer-like type + | ^^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible | - = help: the trait `for<'a> PointerLike` is not implemented for `impl Future` +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/wrong-size.rs:9:14 + | +LL | trait AsyncTrait { + | ---------- this trait is not dyn compatible... +LL | async fn async_dispatch(&self); + | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` + = help: consider moving `async_dispatch` to another trait + = help: only type `&'static str` implements `AsyncTrait`; consider using it directly instead. = note: required for the cast from `&&'static str` to `&dyn AsyncTrait` -error: aborting due to 1 previous error; 1 warning emitted +error[E0038]: the trait `AsyncTrait` is not dyn compatible + --> $DIR/wrong-size.rs:21:12 + | +LL | let x: &dyn AsyncTrait = &"hello, world!"; + | ^^^^^^^^^^^^^^^ `AsyncTrait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/wrong-size.rs:9:14 + | +LL | trait AsyncTrait { + | ---------- this trait is not dyn compatible... +LL | async fn async_dispatch(&self); + | ^^^^^^^^^^^^^^ ...because method `async_dispatch` is `async` + = help: consider moving `async_dispatch` to another trait + = help: only type `&'static str` implements `AsyncTrait`; consider using it directly instead. + +error: aborting due to 2 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/async-await/feature-async-for-loop.rs b/tests/ui/async-await/feature-async-for-loop.rs index 67817cbfa5f3f..22d32907e0e7c 100644 --- a/tests/ui/async-await/feature-async-for-loop.rs +++ b/tests/ui/async-await/feature-async-for-loop.rs @@ -11,7 +11,7 @@ fn f() { }; } -#[cfg(FALSE)] +#[cfg(false)] fn g() { let _ = async { for await _i in core::async_iter::from_iter(0..3) { diff --git a/tests/ui/async-await/field-assign-nonsend.rs b/tests/ui/async-await/field-assign-nonsend.rs index 7002836ee47ff..2b93f90137671 100644 --- a/tests/ui/async-await/field-assign-nonsend.rs +++ b/tests/ui/async-await/field-assign-nonsend.rs @@ -40,5 +40,5 @@ fn main() { let agent = Agent { info_result: InfoResult { node: None } }; // FIXME: It would be nice for this to work. See #94067. assert_send(agent.handle()); - //~^ cannot be sent between threads safely + //~^ ERROR cannot be sent between threads safely } diff --git a/tests/ui/async-await/format-await-send.rs b/tests/ui/async-await/format-await-send.rs new file mode 100644 index 0000000000000..13ae7233fd680 --- /dev/null +++ b/tests/ui/async-await/format-await-send.rs @@ -0,0 +1,24 @@ +// regression test for +// assert that Future which has format!() with an async function is Send + +#![allow(unused)] + +//@ check-pass +//@ edition: 2018 + +use core::future::Future; +use core::pin::Pin; + +fn build_string() -> Pin + Send>> { + Box::pin(async move { + let mut string_builder = String::new(); + string_builder += &format!("Hello {}", helper().await); + string_builder + }) +} + +async fn helper() -> String { + "World".to_string() +} + +fn main() {} diff --git a/tests/ui/async-await/in-trait/cycle-if-impl-doesnt-apply.rs b/tests/ui/async-await/in-trait/cycle-if-impl-doesnt-apply.rs new file mode 100644 index 0000000000000..54992c986558c --- /dev/null +++ b/tests/ui/async-await/in-trait/cycle-if-impl-doesnt-apply.rs @@ -0,0 +1,39 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ edition: 2024 + +// Regression test for . +// Avoid unnecessarily computing the RPITIT type of the first impl when checking the WF of the +// second impl, since the first impl relies on the hidden type of the second impl. + +use std::future::Future; + +trait Handler {} + +struct W(T); + +trait SendTarget { + fn call(self) -> impl Future + Send; +} + +impl SendTarget for W +where + T: Handler + Send, +{ + async fn call(self) { + todo!() + } +} + +impl SendTarget for T +where + T: Handler + Send, +{ + async fn call(self) { + W(self).call().await + } +} + +fn main() {} diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr index 4ca6ef8981984..aa22a453744c0 100644 --- a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr +++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr @@ -3,6 +3,9 @@ error[E0733]: recursion in an async fn requires boxing | LL | async fn second(self) { | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | self.first().await.second().await; + | --------------------------------- recursive call here | = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future diff --git a/tests/ui/async-await/issue-60709.rs b/tests/ui/async-await/issue-60709.rs index 8634d6f7768ba..a3f54d70316c7 100644 --- a/tests/ui/async-await/issue-60709.rs +++ b/tests/ui/async-await/issue-60709.rs @@ -1,6 +1,7 @@ // This used to compile the future down to ud2, due to uninhabited types being // handled incorrectly in coroutines. -//@ compile-flags: -Copt-level=z -Cdebuginfo=2 --edition=2018 +//@ compile-flags: -Copt-level=z -Cdebuginfo=2 +//@ edition: 2018 //@ run-pass diff --git a/tests/ui/async-await/issue-70818.rs b/tests/ui/async-await/issue-70818.rs index 36295a84e7ad7..bc181de8d925d 100644 --- a/tests/ui/async-await/issue-70818.rs +++ b/tests/ui/async-await/issue-70818.rs @@ -2,7 +2,7 @@ use std::future::Future; fn foo(ty: T, ty1: U) -> impl Future + Send { - //~^ Error future cannot be sent between threads safely + //~^ ERROR future cannot be sent between threads safely async { (ty, ty1) } } diff --git a/tests/ui/async-await/issue-71137.rs b/tests/ui/async-await/issue-71137.rs index 551cf85047cae..6fbf17ccf0d04 100644 --- a/tests/ui/async-await/issue-71137.rs +++ b/tests/ui/async-await/issue-71137.rs @@ -19,5 +19,5 @@ async fn wrong_mutex() { } fn main() { - fake_spawn(wrong_mutex()); //~ Error future cannot be sent between threads safely + fake_spawn(wrong_mutex()); //~ ERROR future cannot be sent between threads safely } diff --git a/tests/ui/async-await/issues/issue-59972.rs b/tests/ui/async-await/issues/issue-59972.rs index c30477fcd30ce..e64a856fab30b 100644 --- a/tests/ui/async-await/issues/issue-59972.rs +++ b/tests/ui/async-await/issues/issue-59972.rs @@ -4,7 +4,8 @@ //@ run-pass -//@ compile-flags: --edition=2018 -Aunused +//@ compile-flags: -Aunused +//@ edition: 2018 pub enum Uninhabited { } diff --git a/tests/ui/async-await/issues/issue-60655-latebound-regions.rs b/tests/ui/async-await/issues/issue-60655-latebound-regions.rs index 4a8b5af5769bd..f9eeb361b1396 100644 --- a/tests/ui/async-await/issues/issue-60655-latebound-regions.rs +++ b/tests/ui/async-await/issues/issue-60655-latebound-regions.rs @@ -11,15 +11,17 @@ pub type Func = impl Sized; // Late bound region should be allowed to escape the function, since it's bound // in the type. +#[define_opaque(Func)] fn null_function_ptr() -> Func { None:: fn(&'a ())> } async fn async_nop(_: &u8) {} -pub type ServeFut = impl Future; +pub type ServeFut = impl Future; // Late bound regions occur in the coroutine witness type here. +#[define_opaque(ServeFut)] fn serve() -> ServeFut { async move { let x = 5; diff --git a/tests/ui/async-await/issues/issue-63388-2.rs b/tests/ui/async-await/issues/issue-63388-2.rs index 8bb5cfa4a594a..d9dc602bd1c6d 100644 --- a/tests/ui/async-await/issues/issue-63388-2.rs +++ b/tests/ui/async-await/issues/issue-63388-2.rs @@ -12,7 +12,6 @@ impl Xyz { ) -> &dyn Foo //~ ERROR missing lifetime specifier { foo - //~^ ERROR explicit lifetime required in the type of `foo` [E0621] } } diff --git a/tests/ui/async-await/issues/issue-63388-2.stderr b/tests/ui/async-await/issues/issue-63388-2.stderr index 7e3c0a1227de8..45843bc72cd81 100644 --- a/tests/ui/async-await/issues/issue-63388-2.stderr +++ b/tests/ui/async-await/issues/issue-63388-2.stderr @@ -12,16 +12,6 @@ help: consider using the `'a` lifetime LL | ) -> &'a dyn Foo | ++ -error[E0621]: explicit lifetime required in the type of `foo` - --> $DIR/issue-63388-2.rs:14:9 - | -LL | foo: &dyn Foo, bar: &'a dyn Foo - | -------- help: add explicit lifetime `'a` to the type of `foo`: `&'a (dyn Foo + 'a)` -... -LL | foo - | ^^^ lifetime `'a` required - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0106, E0621. -For more information about an error, try `rustc --explain E0106`. +For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/async-await/no-unsafe-async.rs b/tests/ui/async-await/no-unsafe-async.rs index e58d878c3db04..cc7e89e16cb2b 100644 --- a/tests/ui/async-await/no-unsafe-async.rs +++ b/tests/ui/async-await/no-unsafe-async.rs @@ -3,11 +3,11 @@ struct S; impl S { - #[cfg(FALSE)] + #[cfg(false)] unsafe async fn g() {} //~ ERROR expected one of `extern` or `fn`, found keyword `async` } -#[cfg(FALSE)] +#[cfg(false)] unsafe async fn f() {} //~ ERROR expected one of `extern` or `fn`, found keyword `async` fn main() {} diff --git a/tests/ui/async-await/normalize-output-in-signature-deduction.rs b/tests/ui/async-await/normalize-output-in-signature-deduction.rs index 19d70c2c6eeb5..e2238d85093dd 100644 --- a/tests/ui/async-await/normalize-output-in-signature-deduction.rs +++ b/tests/ui/async-await/normalize-output-in-signature-deduction.rs @@ -13,6 +13,7 @@ pub trait Trait {} pub type TAIT = impl Trait; +#[define_opaque(TAIT)] async fn foo() -> TAIT { Foo } diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-once.stderr b/tests/ui/async-await/pin-ergonomics/reborrow-once.stderr index a1ea2b4a57a70..dc8e424ad2a42 100644 --- a/tests/ui/async-await/pin-ergonomics/reborrow-once.stderr +++ b/tests/ui/async-await/pin-ergonomics/reborrow-once.stderr @@ -1,4 +1,4 @@ -error[E0499]: cannot borrow `*x.__pointer` as mutable more than once at a time +error[E0499]: cannot borrow `*x.pointer` as mutable more than once at a time --> $DIR/reborrow-once.rs:12:14 | LL | twice(x, x); diff --git a/tests/ui/async-await/pin-ergonomics/sugar-self.rs b/tests/ui/async-await/pin-ergonomics/sugar-self.rs new file mode 100644 index 0000000000000..3d71b54b1aec3 --- /dev/null +++ b/tests/ui/async-await/pin-ergonomics/sugar-self.rs @@ -0,0 +1,46 @@ +//@ check-pass + +#![feature(pin_ergonomics)] +#![allow(dead_code, incomplete_features)] + +// Makes sure we can handle `&pin mut self` and `&pin const self` as sugar for +// `self: Pin<&mut Self>` and `self: Pin<&Self>`. + +use std::pin::Pin; + +struct Foo; + +impl Foo { + fn baz(&pin mut self) {} + + fn baz_const(&pin const self) {} + + fn baz_lt<'a>(&'a pin mut self) {} + + fn baz_const_lt(&'_ pin const self) {} +} + +fn foo(_: &pin mut Foo) {} + +fn foo_const(_: &pin const Foo) {} + +fn bar(x: &pin mut Foo) { + // For the calls below to work we need to automatically reborrow, + // as if the user had written `foo(x.as_mut())`. + foo(x); + foo(x); + + Foo::baz(x); + Foo::baz(x); + + // make sure we can reborrow &mut as &. + foo_const(x); + Foo::baz_const(x); + + let x: &pin const _ = Pin::new(&Foo); + + foo_const(x); // make sure reborrowing from & to & works. + foo_const(x); +} + +fn main() {} diff --git a/tests/ui/async-await/post-cleanup-phase-validation.rs b/tests/ui/async-await/post-cleanup-phase-validation.rs new file mode 100644 index 0000000000000..a347e35c26db3 --- /dev/null +++ b/tests/ui/async-await/post-cleanup-phase-validation.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -Zvalidate-mir +//@ edition: 2024 +//@ build-pass + +// Regression test that we don't ICE when encountering a transmute in a coroutine's +// drop shim body, which is conceptually in the Runtime phase but wasn't having the +// phase updated b/c the pass manager neither optimizes nor updates the phase for +// drop shim bodies. + +struct HasDrop; +impl Drop for HasDrop { + fn drop(&mut self) {} +} + +fn main() { + async { + vec![async { HasDrop }.await]; + }; +} diff --git a/tests/ui/async-await/suggest-missing-await.rs b/tests/ui/async-await/suggest-missing-await.rs index 0bd67cec335bd..de0b2ce52b691 100644 --- a/tests/ui/async-await/suggest-missing-await.rs +++ b/tests/ui/async-await/suggest-missing-await.rs @@ -1,4 +1,5 @@ //@ edition:2018 +//@ dont-require-annotations: SUGGESTION fn take_u32(_x: u32) {} @@ -43,7 +44,7 @@ async fn suggest_await_on_previous_match_arms() { 0 => dummy(), //~ HELP consider `await`ing on the `Future` 1 => dummy(), 2 => dummy().await, - //~^ `match` arms have incompatible types [E0308] + //~^ ERROR `match` arms have incompatible types [E0308] }; } diff --git a/tests/ui/async-await/suggest-missing-await.stderr b/tests/ui/async-await/suggest-missing-await.stderr index f9db86ea40a93..9db7eb980effe 100644 --- a/tests/ui/async-await/suggest-missing-await.stderr +++ b/tests/ui/async-await/suggest-missing-await.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/suggest-missing-await.rs:12:14 + --> $DIR/suggest-missing-await.rs:13:14 | LL | take_u32(x) | -------- ^ expected `u32`, found future @@ -7,12 +7,12 @@ LL | take_u32(x) | arguments to this function are incorrect | note: calling an async function returns a future - --> $DIR/suggest-missing-await.rs:12:14 + --> $DIR/suggest-missing-await.rs:13:14 | LL | take_u32(x) | ^ note: function defined here - --> $DIR/suggest-missing-await.rs:3:4 + --> $DIR/suggest-missing-await.rs:4:4 | LL | fn take_u32(_x: u32) {} | ^^^^^^^^ ------- @@ -22,13 +22,13 @@ LL | take_u32(x.await) | ++++++ error[E0308]: mismatched types - --> $DIR/suggest-missing-await.rs:22:5 + --> $DIR/suggest-missing-await.rs:23:5 | LL | dummy() | ^^^^^^^ expected `()`, found future | note: calling an async function returns a future - --> $DIR/suggest-missing-await.rs:22:5 + --> $DIR/suggest-missing-await.rs:23:5 | LL | dummy() | ^^^^^^^ @@ -42,7 +42,7 @@ LL | dummy(); | + error[E0308]: `if` and `else` have incompatible types - --> $DIR/suggest-missing-await.rs:35:9 + --> $DIR/suggest-missing-await.rs:36:9 | LL | let _x = if true { | ______________- @@ -64,7 +64,7 @@ LL | dummy().await | ++++++ error[E0308]: `match` arms have incompatible types - --> $DIR/suggest-missing-await.rs:45:14 + --> $DIR/suggest-missing-await.rs:46:14 | LL | let _x = match 0usize { | ______________- @@ -87,7 +87,7 @@ LL ~ 1 => dummy().await, | error[E0308]: mismatched types - --> $DIR/suggest-missing-await.rs:53:9 + --> $DIR/suggest-missing-await.rs:54:9 | LL | let _x = match dummy() { | ------- this expression has type `impl Future` @@ -102,7 +102,7 @@ LL | let _x = match dummy().await { | ++++++ error[E0308]: mismatched types - --> $DIR/suggest-missing-await.rs:67:9 + --> $DIR/suggest-missing-await.rs:68:9 | LL | match dummy_result() { | -------------- this expression has type `impl Future>` @@ -118,7 +118,7 @@ LL | match dummy_result().await { | ++++++ error[E0308]: mismatched types - --> $DIR/suggest-missing-await.rs:69:9 + --> $DIR/suggest-missing-await.rs:70:9 | LL | match dummy_result() { | -------------- this expression has type `impl Future>` @@ -134,7 +134,7 @@ LL | match dummy_result().await { | ++++++ error[E0308]: mismatched types - --> $DIR/suggest-missing-await.rs:77:27 + --> $DIR/suggest-missing-await.rs:78:27 | LL | Some(do_async()).map(|()| {}); | ^^ diff --git a/tests/ui/attributes/attr-bad-crate-attr.rs b/tests/ui/attributes/attr-bad-crate-attr.rs index 9de0abca9a75e..d37d349026a02 100644 --- a/tests/ui/attributes/attr-bad-crate-attr.rs +++ b/tests/ui/attributes/attr-bad-crate-attr.rs @@ -3,7 +3,6 @@ //! //! See . -//@ error-pattern: expected item - #![attr = "val"] #[attr = "val"] // Unterminated +//~^ ERROR expected item after attributes diff --git a/tests/ui/attributes/attr-bad-crate-attr.stderr b/tests/ui/attributes/attr-bad-crate-attr.stderr index 69eabd32230f4..22522896bd1a9 100644 --- a/tests/ui/attributes/attr-bad-crate-attr.stderr +++ b/tests/ui/attributes/attr-bad-crate-attr.stderr @@ -1,5 +1,5 @@ error: expected item after attributes - --> $DIR/attr-bad-crate-attr.rs:9:1 + --> $DIR/attr-bad-crate-attr.rs:7:1 | LL | #[attr = "val"] // Unterminated | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/attributes/auxiliary/use-doc-alias-name-extern.rs b/tests/ui/attributes/auxiliary/use-doc-alias-name-extern.rs new file mode 100644 index 0000000000000..4c06d0fdfe390 --- /dev/null +++ b/tests/ui/attributes/auxiliary/use-doc-alias-name-extern.rs @@ -0,0 +1,24 @@ +#[doc(alias="DocAliasS1")] +pub struct S1; + +#[doc(alias="DocAliasS2")] +#[doc(alias("DocAliasS3", "DocAliasS4"))] +pub struct S2; + +#[doc(alias("doc_alias_f1", "doc_alias_f2"))] +pub fn f() {} + +pub mod m { + #[doc(alias="DocAliasS5")] + pub struct S5; + + pub mod n { + #[doc(alias("DocAliasX"))] + pub mod x { + pub mod y { + #[doc(alias="DocAliasS6")] + pub struct S6; + } + } + } +} diff --git a/tests/ui/attributes/auxiliary/used_pre_main_constructor.rs b/tests/ui/attributes/auxiliary/used_pre_main_constructor.rs index d94572ef5d6cd..8ea7a9cb4b5ac 100644 --- a/tests/ui/attributes/auxiliary/used_pre_main_constructor.rs +++ b/tests/ui/attributes/auxiliary/used_pre_main_constructor.rs @@ -19,6 +19,7 @@ target_os = "netbsd", target_os = "nto", target_os = "openbsd", + target_os = "fuchsia", ), link_section = ".init_array" )] diff --git a/tests/ui/attributes/check-builtin-attr-ice.rs b/tests/ui/attributes/check-builtin-attr-ice.rs index 9ef5890601f6b..7745849acd0b1 100644 --- a/tests/ui/attributes/check-builtin-attr-ice.rs +++ b/tests/ui/attributes/check-builtin-attr-ice.rs @@ -39,13 +39,17 @@ // Notably, `should_panic` is a `AttributeType::Normal` attribute that is checked separately. +#![deny(unused_attributes)] + struct Foo { #[should_panic::skip] //~^ ERROR failed to resolve + //~| ERROR `#[should_panic::skip]` only has an effect on functions pub field: u8, #[should_panic::a::b::c] //~^ ERROR failed to resolve + //~| ERROR `#[should_panic::a::b::c]` only has an effect on functions pub field2: u8, } diff --git a/tests/ui/attributes/check-builtin-attr-ice.stderr b/tests/ui/attributes/check-builtin-attr-ice.stderr index 06a4769b2b421..4f26f71efb7e7 100644 --- a/tests/ui/attributes/check-builtin-attr-ice.stderr +++ b/tests/ui/attributes/check-builtin-attr-ice.stderr @@ -1,21 +1,39 @@ error[E0433]: failed to resolve: use of unresolved module or unlinked crate `should_panic` - --> $DIR/check-builtin-attr-ice.rs:43:7 + --> $DIR/check-builtin-attr-ice.rs:45:7 | LL | #[should_panic::skip] | ^^^^^^^^^^^^ use of unresolved module or unlinked crate `should_panic` error[E0433]: failed to resolve: use of unresolved module or unlinked crate `should_panic` - --> $DIR/check-builtin-attr-ice.rs:47:7 + --> $DIR/check-builtin-attr-ice.rs:50:7 | LL | #[should_panic::a::b::c] | ^^^^^^^^^^^^ use of unresolved module or unlinked crate `should_panic` error[E0433]: failed to resolve: use of unresolved module or unlinked crate `deny` - --> $DIR/check-builtin-attr-ice.rs:55:7 + --> $DIR/check-builtin-attr-ice.rs:59:7 | LL | #[deny::skip] | ^^^^ use of unresolved module or unlinked crate `deny` -error: aborting due to 3 previous errors +error: `#[should_panic::skip]` only has an effect on functions + --> $DIR/check-builtin-attr-ice.rs:45:5 + | +LL | #[should_panic::skip] + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/check-builtin-attr-ice.rs:42:9 + | +LL | #![deny(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + +error: `#[should_panic::a::b::c]` only has an effect on functions + --> $DIR/check-builtin-attr-ice.rs:50:5 + | +LL | #[should_panic::a::b::c] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/attributes/collapse-debuginfo-invalid.rs b/tests/ui/attributes/collapse-debuginfo-invalid.rs index d6b3554a5a812..ccf11df2eb0d7 100644 --- a/tests/ui/attributes/collapse-debuginfo-invalid.rs +++ b/tests/ui/attributes/collapse-debuginfo-invalid.rs @@ -24,15 +24,15 @@ const BAR: u32 = 3; //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions fn foo() { let _ = #[collapse_debuginfo(yes)] || { }; -//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions + //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions #[collapse_debuginfo(yes)] -//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions + //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions let _ = 3; let _ = #[collapse_debuginfo(yes)] 3; -//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions + //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions match (3, 4) { #[collapse_debuginfo(yes)] -//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions + //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions _ => (), } } @@ -50,7 +50,7 @@ type Map = HashMap; //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions enum Foo { #[collapse_debuginfo(yes)] -//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions + //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions Variant, } @@ -58,7 +58,7 @@ enum Foo { //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions struct Bar { #[collapse_debuginfo(yes)] -//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions + //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions field: u32, } @@ -73,7 +73,7 @@ union Qux { //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions trait Foobar { #[collapse_debuginfo(yes)] -//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions + //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions type Bar; } @@ -85,6 +85,7 @@ impl Foobar for Bar { type Bar = u32; } +#[define_opaque(AFoobar)] fn constraining() -> AFoobar { Bar { field: 3 } } @@ -93,11 +94,11 @@ fn constraining() -> AFoobar { //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions impl Bar { #[collapse_debuginfo(yes)] -//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions + //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions const FOO: u32 = 3; #[collapse_debuginfo(yes)] -//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions + //~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions fn bar(&self) {} } diff --git a/tests/ui/attributes/collapse-debuginfo-invalid.stderr b/tests/ui/attributes/collapse-debuginfo-invalid.stderr index 70376f985cb12..081e4445a8695 100644 --- a/tests/ui/attributes/collapse-debuginfo-invalid.stderr +++ b/tests/ui/attributes/collapse-debuginfo-invalid.stderr @@ -176,7 +176,7 @@ LL | type AFoobar = impl Foobar; | --------------------------- not a macro definition error: `collapse_debuginfo` attribute should be applied to macro definitions - --> $DIR/collapse-debuginfo-invalid.rs:92:1 + --> $DIR/collapse-debuginfo-invalid.rs:93:1 | LL | #[collapse_debuginfo(yes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -200,7 +200,7 @@ LL | type Bar; | --------- not a macro definition error: `collapse_debuginfo` attribute should be applied to macro definitions - --> $DIR/collapse-debuginfo-invalid.rs:95:5 + --> $DIR/collapse-debuginfo-invalid.rs:96:5 | LL | #[collapse_debuginfo(yes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,7 +209,7 @@ LL | const FOO: u32 = 3; | ------------------- not a macro definition error: `collapse_debuginfo` attribute should be applied to macro definitions - --> $DIR/collapse-debuginfo-invalid.rs:99:5 + --> $DIR/collapse-debuginfo-invalid.rs:100:5 | LL | #[collapse_debuginfo(yes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/attributes/crate-name-empty.rs b/tests/ui/attributes/crate-name-empty.rs new file mode 100644 index 0000000000000..dfba77a52def7 --- /dev/null +++ b/tests/ui/attributes/crate-name-empty.rs @@ -0,0 +1,5 @@ +// Ensure we reject `#![crate_name = ""]`. + +#![crate_name = ""] //~ ERROR crate name must not be empty + +fn main() {} diff --git a/tests/ui/attributes/crate-name-empty.stderr b/tests/ui/attributes/crate-name-empty.stderr new file mode 100644 index 0000000000000..509a42d05f711 --- /dev/null +++ b/tests/ui/attributes/crate-name-empty.stderr @@ -0,0 +1,8 @@ +error: crate name must not be empty + --> $DIR/crate-name-empty.rs:3:1 + | +LL | #![crate_name = ""] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/attributes/decl_macro_ty_in_attr_macro.rs b/tests/ui/attributes/decl_macro_ty_in_attr_macro.rs new file mode 100644 index 0000000000000..e633c08be3ad2 --- /dev/null +++ b/tests/ui/attributes/decl_macro_ty_in_attr_macro.rs @@ -0,0 +1,20 @@ +// tests for #137662: using a ty or (or most other) fragment inside an attr macro wouldn't work +// because of a missing code path. With $repr: tt it did work. +//@ check-pass + +macro_rules! foo { + { + $repr:ty + } => { + #[repr($repr)] + pub enum Foo { + Bar = 0i32, + } + } +} + +foo! { + i32 +} + +fn main() {} diff --git a/tests/ui/attributes/dont-dup-expr-attrs.rs b/tests/ui/attributes/dont-dup-expr-attrs.rs new file mode 100644 index 0000000000000..21da6820a538a --- /dev/null +++ b/tests/ui/attributes/dont-dup-expr-attrs.rs @@ -0,0 +1,133 @@ +//@ check-pass +// +// During development of #124141 at one point expression on attributes were +// being duplicated and `m1` caused an exponential blowup that caused OOM. +// The number of recursive calls depends on the number of doc comments on the +// expr block. On each recursive call, the `#[allow(deprecated)]` attribute(s) on +// the `0` somehow get duplicated, resulting in 1, 2, 4, 8, ... identical +// attributes. +// +// After the fix, the code compiles quickly and normally. + +macro_rules! m1 { + ($(#[$meta:meta])* { $e:expr }) => { + m1! { expr: { $e }, unprocessed: [$(#[$meta])*] } + }; + + (expr: { $e:expr }, unprocessed: [ #[$meta:meta] $($metas:tt)* ]) => { + m1! { expr: { $e }, unprocessed: [ $($metas)* ] } + }; + + (expr: { $e:expr }, unprocessed: []) => { + { $e } + } +} + +macro_rules! m2 { + ($(#[$meta:meta])* { $e:stmt }) => { + m2! { stmt: { $e }, unprocessed: [$(#[$meta])*] } + }; + + (stmt: { $e:stmt }, unprocessed: [ #[$meta:meta] $($metas:tt)* ]) => { + m2! { stmt: { $e }, unprocessed: [ $($metas)* ] } + }; + + (stmt: { $e:stmt }, unprocessed: []) => { + { $e } + } +} + +macro_rules! m3 { + ($(#[$meta:meta])* { $e:item }) => { + m3! { item: { $e }, unprocessed: [$(#[$meta])*] } + }; + + (item: { $e:item }, unprocessed: [ #[$meta:meta] $($metas:tt)* ]) => { + m3! { item: { $e }, unprocessed: [ $($metas)* ] } + }; + + (item: { $e:item }, unprocessed: []) => { + { $e } + } +} + +fn main() { + // Each additional doc comment line doubles the compile time. + m1!( + /// a1 + /// a2 + /// a3 + /// a4 + /// a5 + /// a6 + /// a7 + /// a8 + /// a9 + /// a10 + /// a11 + /// a12 + /// a13 + /// a14 + /// a15 + /// a16 + /// a17 + /// a18 + /// a19 + /// a20 + { + #[allow(deprecated)] 0 + } + ); + + m2!( + /// a1 + /// a2 + /// a3 + /// a4 + /// a5 + /// a6 + /// a7 + /// a8 + /// a9 + /// a10 + /// a11 + /// a12 + /// a13 + /// a14 + /// a15 + /// a16 + /// a17 + /// a18 + /// a19 + /// a20 + { + #[allow(deprecated)] let x = 5 + } + ); + + m3!( + /// a1 + /// a2 + /// a3 + /// a4 + /// a5 + /// a6 + /// a7 + /// a8 + /// a9 + /// a10 + /// a11 + /// a12 + /// a13 + /// a14 + /// a15 + /// a16 + /// a17 + /// a18 + /// a19 + /// a20 + { + #[allow(deprecated)] struct S; + } + ); +} diff --git a/tests/ui/attributes/export/crate-type-2.rs b/tests/ui/attributes/export/crate-type-2.rs new file mode 100644 index 0000000000000..f0379f6d797c2 --- /dev/null +++ b/tests/ui/attributes/export/crate-type-2.rs @@ -0,0 +1,2 @@ +//@ compile-flags: --crate-type=sdylib +//~^ ERROR `sdylib` crate type is unstable diff --git a/tests/ui/attributes/export/crate-type-2.stderr b/tests/ui/attributes/export/crate-type-2.stderr new file mode 100644 index 0000000000000..7ce6a500113fb --- /dev/null +++ b/tests/ui/attributes/export/crate-type-2.stderr @@ -0,0 +1,9 @@ +error[E0658]: `sdylib` crate type is unstable + | + = note: see issue #139939 for more information + = help: add `#![feature(export_stable)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/attributes/export/crate-type.rs b/tests/ui/attributes/export/crate-type.rs new file mode 100644 index 0000000000000..bd092bbb1a1ab --- /dev/null +++ b/tests/ui/attributes/export/crate-type.rs @@ -0,0 +1,2 @@ +#![crate_type = "sdylib"] +//~^ ERROR `sdylib` crate type is unstable diff --git a/tests/ui/attributes/export/crate-type.stderr b/tests/ui/attributes/export/crate-type.stderr new file mode 100644 index 0000000000000..7ce6a500113fb --- /dev/null +++ b/tests/ui/attributes/export/crate-type.stderr @@ -0,0 +1,9 @@ +error[E0658]: `sdylib` crate type is unstable + | + = note: see issue #139939 for more information + = help: add `#![feature(export_stable)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/attributes/export/exportable.rs b/tests/ui/attributes/export/exportable.rs new file mode 100644 index 0000000000000..f592fce88cd49 --- /dev/null +++ b/tests/ui/attributes/export/exportable.rs @@ -0,0 +1,139 @@ +//@ compile-flags: -Zunstable-options -Csymbol-mangling-version=v0 + +#![crate_type = "sdylib"] +#![allow(incomplete_features, improper_ctypes_definitions)] +#![feature(export_stable)] +#![feature(inherent_associated_types)] + +mod m { + #[export_stable] + pub struct S; + //~^ ERROR private items are not exportable + + pub fn foo() -> i32 { 0 } + //~^ ERROR only functions with "C" ABI are exportable +} + +#[export_stable] +pub use m::foo; + +#[export_stable] +pub mod m1 { + #[repr(C)] + pub struct S1; // OK, public type with stable repr + + struct S2; + + pub struct S3; + //~^ ERROR types with unstable layout are not exportable +} + +pub mod fn_sig { + #[export_stable] + pub fn foo1() {} + //~^ ERROR only functions with "C" ABI are exportable + + #[export_stable] + #[repr(C)] + pub struct S; + + #[export_stable] + pub extern "C" fn foo2(x: S) -> i32 { 0 } + + #[export_stable] + pub extern "C" fn foo3(x: Box) -> i32 { 0 } + //~^ ERROR function with `#[export_stable]` attribute uses type `Box`, which is not exportable +} + +pub mod impl_item { + pub struct S; + + impl S { + #[export_stable] + pub extern "C" fn foo1(&self) -> i32 { 0 } + //~^ ERROR method with `#[export_stable]` attribute uses type `&impl_item::S`, which is not exportable + + #[export_stable] + pub extern "C" fn foo2(self) -> i32 { 0 } + //~^ ERROR method with `#[export_stable]` attribute uses type `impl_item::S`, which is not exportable + } + + pub struct S2(T); + + impl S2 { + #[export_stable] + pub extern "C" fn foo1(&self) {} + //~^ ERROR generic functions are not exportable + } +} + +pub mod tys { + pub trait Trait { + type Type; + } + pub struct S; + + impl Trait for S { + type Type = (u32,); + } + + #[export_stable] + pub extern "C" fn foo1(x: ::Type) -> u32 { x.0 } + //~^ ERROR function with `#[export_stable]` attribute uses type `(u32,)`, which is not exportable + + #[export_stable] + pub type Type = [i32; 4]; + + #[export_stable] + pub extern "C" fn foo2(_x: Type) {} + //~^ ERROR function with `#[export_stable]` attribute uses type `[i32; 4]`, which is not exportable + + impl S { + #[export_stable] + pub type Type = extern "C" fn(); + } + + #[export_stable] + pub extern "C" fn foo3(_x: S::Type) {} + //~^ ERROR function with `#[export_stable]` attribute uses type `extern "C" fn()`, which is not exportable + + #[export_stable] + pub extern "C" fn foo4() -> impl Copy { + //~^ ERROR function with `#[export_stable]` attribute uses type `impl Copy`, which is not exportable + 0 + } +} + +pub mod privacy { + #[export_stable] + #[repr(C)] + pub struct S1 { + pub x: i32 + } + + #[export_stable] + #[repr(C)] + pub struct S2 { + //~^ ERROR ADT types with private fields are not exportable + x: i32 + } + + #[export_stable] + #[repr(i32)] + enum E { + //~^ ERROR private items are not exportable + Variant1 { x: i32 } + } +} + +pub mod use_site { + #[export_stable] + pub trait Trait {} + //~^ ERROR trait's are not exportable + + #[export_stable] + pub const C: i32 = 0; + //~^ ERROR constant's are not exportable +} + +fn main() {} diff --git a/tests/ui/attributes/export/exportable.stderr b/tests/ui/attributes/export/exportable.stderr new file mode 100644 index 0000000000000..0f6469d35c3b7 --- /dev/null +++ b/tests/ui/attributes/export/exportable.stderr @@ -0,0 +1,130 @@ +error: private items are not exportable + --> $DIR/exportable.rs:10:5 + | +LL | pub struct S; + | ^^^^^^^^^^^^ + | +note: is only usable at visibility `pub(crate)` + --> $DIR/exportable.rs:10:5 + | +LL | pub struct S; + | ^^^^^^^^^^^^ + +error: private items are not exportable + --> $DIR/exportable.rs:123:5 + | +LL | enum E { + | ^^^^^^ + | +note: is only usable at visibility `pub(self)` + --> $DIR/exportable.rs:123:5 + | +LL | enum E { + | ^^^^^^ + +error: trait's are not exportable + --> $DIR/exportable.rs:131:5 + | +LL | pub trait Trait {} + | ^^^^^^^^^^^^^^^ + +error: constant's are not exportable + --> $DIR/exportable.rs:135:5 + | +LL | pub const C: i32 = 0; + | ^^^^^^^^^^^^^^^^ + +error: only functions with "C" ABI are exportable + --> $DIR/exportable.rs:13:5 + | +LL | pub fn foo() -> i32 { 0 } + | ^^^^^^^^^^^^^^^^^^^ + +error: types with unstable layout are not exportable + --> $DIR/exportable.rs:27:5 + | +LL | pub struct S3; + | ^^^^^^^^^^^^^ + +error: only functions with "C" ABI are exportable + --> $DIR/exportable.rs:33:5 + | +LL | pub fn foo1() {} + | ^^^^^^^^^^^^^ + +error: function with `#[export_stable]` attribute uses type `Box`, which is not exportable + --> $DIR/exportable.rs:44:5 + | +LL | pub extern "C" fn foo3(x: Box) -> i32 { 0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^------^^^^^^^^ + | | + | not exportable + +error: method with `#[export_stable]` attribute uses type `&impl_item::S`, which is not exportable + --> $DIR/exportable.rs:53:9 + | +LL | pub extern "C" fn foo1(&self) -> i32 { 0 } + | ^^^^^^^^^^^^^^^^^^^^^^^-----^^^^^^^^ + | | + | not exportable + +error: method with `#[export_stable]` attribute uses type `impl_item::S`, which is not exportable + --> $DIR/exportable.rs:57:9 + | +LL | pub extern "C" fn foo2(self) -> i32 { 0 } + | ^^^^^^^^^^^^^^^^^^^^^^^----^^^^^^^^ + | | + | not exportable + +error: generic functions are not exportable + --> $DIR/exportable.rs:65:9 + | +LL | pub extern "C" fn foo1(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: function with `#[export_stable]` attribute uses type `(u32,)`, which is not exportable + --> $DIR/exportable.rs:81:5 + | +LL | pub extern "C" fn foo1(x: ::Type) -> u32 { x.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^------------------^^^^^^^^ + | | + | not exportable + +error: function with `#[export_stable]` attribute uses type `[i32; 4]`, which is not exportable + --> $DIR/exportable.rs:88:5 + | +LL | pub extern "C" fn foo2(_x: Type) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----^ + | | + | not exportable + +error: function with `#[export_stable]` attribute uses type `extern "C" fn()`, which is not exportable + --> $DIR/exportable.rs:97:5 + | +LL | pub extern "C" fn foo3(_x: S::Type) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^ + | | + | not exportable + +error: function with `#[export_stable]` attribute uses type `impl Copy`, which is not exportable + --> $DIR/exportable.rs:101:5 + | +LL | pub extern "C" fn foo4() -> impl Copy { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^--------- + | | + | not exportable + +error: ADT types with private fields are not exportable + --> $DIR/exportable.rs:116:5 + | +LL | pub struct S2 { + | ^^^^^^^^^^^^^ + | +note: `x` is private + --> $DIR/exportable.rs:118:9 + | +LL | x: i32 + | ^^^^^^ + +error: aborting due to 16 previous errors + diff --git a/tests/ui/attributes/export/lang-item.rs b/tests/ui/attributes/export/lang-item.rs new file mode 100644 index 0000000000000..b923b41a95759 --- /dev/null +++ b/tests/ui/attributes/export/lang-item.rs @@ -0,0 +1,8 @@ +#![feature(no_core, lang_items, export_stable)] +#![allow(incomplete_features)] +#![crate_type = "sdylib"] +#![no_core] + +#[lang = "sized"] +//~^ ERROR lang items are not allowed in stable dylibs +trait Sized {} diff --git a/tests/ui/attributes/export/lang-item.stderr b/tests/ui/attributes/export/lang-item.stderr new file mode 100644 index 0000000000000..8c0741bdb6f2d --- /dev/null +++ b/tests/ui/attributes/export/lang-item.stderr @@ -0,0 +1,8 @@ +error: lang items are not allowed in stable dylibs + --> $DIR/lang-item.rs:6:1 + | +LL | #[lang = "sized"] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/attributes/extented-attribute-macro-error.stderr b/tests/ui/attributes/extented-attribute-macro-error.stderr index 884f3c7b16657..7b93b98f64cd4 100644 --- a/tests/ui/attributes/extented-attribute-macro-error.stderr +++ b/tests/ui/attributes/extented-attribute-macro-error.stderr @@ -3,8 +3,6 @@ error: couldn't read the file | LL | #![doc = include_str!("../not_existing_file.md")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/attributes/inner-attr-metavar.rs b/tests/ui/attributes/inner-attr-metavar.rs new file mode 100644 index 0000000000000..345121c252e82 --- /dev/null +++ b/tests/ui/attributes/inner-attr-metavar.rs @@ -0,0 +1,14 @@ +//@ check-pass +// +// During `Nonterminal` removal (#124141) there was at one point a problem with +// calling from_ast on expressions with inner attributes within metavars -- the +// inner attributes were being inserted in the wrong place in `from_ast`. This +// test covers that case. + +macro_rules! m3 { ($e:expr) => {} } +macro_rules! m2 { ($e:expr) => { m3!($e); } } +macro_rules! m1 { ($e:expr) => { m2!($e); } } + +m1!({ #![allow(unused)] 0 }); + +fn main() {} diff --git a/tests/ui/attributes/invalid-reprs.rs b/tests/ui/attributes/invalid-reprs.rs new file mode 100644 index 0000000000000..95ed14b549163 --- /dev/null +++ b/tests/ui/attributes/invalid-reprs.rs @@ -0,0 +1,6 @@ +fn main() { + let y = #[repr(uwu(4))] + //~^ ERROR attributes on expressions are experimental + //~| ERROR unrecognized representation hint + (&id(5)); //~ ERROR: cannot find function `id` in this scope +} diff --git a/tests/ui/attributes/invalid-reprs.stderr b/tests/ui/attributes/invalid-reprs.stderr new file mode 100644 index 0000000000000..415b969b2440a --- /dev/null +++ b/tests/ui/attributes/invalid-reprs.stderr @@ -0,0 +1,33 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/invalid-reprs.rs:2:13 + | +LL | let y = #[repr(uwu(4))] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0425]: cannot find function `id` in this scope + --> $DIR/invalid-reprs.rs:5:7 + | +LL | (&id(5)); + | ^^ not found in this scope + | +help: consider importing this function + | +LL + use std::process::id; + | + +error[E0552]: unrecognized representation hint + --> $DIR/invalid-reprs.rs:2:20 + | +LL | let y = #[repr(uwu(4))] + | ^^^^^^ + | + = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0425, E0552, E0658. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/attributes/invalid_macro_export_argument.deny.stderr b/tests/ui/attributes/invalid_macro_export_argument.deny.stderr index 644acc27b58e2..9d44bd162c7b7 100644 --- a/tests/ui/attributes/invalid_macro_export_argument.deny.stderr +++ b/tests/ui/attributes/invalid_macro_export_argument.deny.stderr @@ -10,11 +10,17 @@ note: the lint level is defined here LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `not_local_inner_macros` isn't a valid `#[macro_export]` argument +error: invalid `#[macro_export]` argument --> $DIR/invalid_macro_export_argument.rs:13:16 | LL | #[macro_export(not_local_inner_macros)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: invalid `#[macro_export]` argument + --> $DIR/invalid_macro_export_argument.rs:33:16 + | +LL | #[macro_export("blah")] + | ^^^^^^ + +error: aborting due to 3 previous errors diff --git a/tests/ui/attributes/invalid_macro_export_argument.rs b/tests/ui/attributes/invalid_macro_export_argument.rs index 96f66991e0414..c5fe39d062a4e 100644 --- a/tests/ui/attributes/invalid_macro_export_argument.rs +++ b/tests/ui/attributes/invalid_macro_export_argument.rs @@ -11,7 +11,7 @@ macro_rules! a { } #[macro_export(not_local_inner_macros)] -//[deny]~^ ERROR `not_local_inner_macros` isn't a valid `#[macro_export]` argument +//[deny]~^ ERROR invalid `#[macro_export]` argument macro_rules! b { () => () } @@ -30,4 +30,10 @@ macro_rules! e { () => () } +#[macro_export("blah")] +//[deny]~^ ERROR invalid `#[macro_export]` argument +macro_rules! f { + () => () +} + fn main() {} diff --git a/tests/ui/attributes/issue-90873.rs b/tests/ui/attributes/issue-90873.rs index 53339ce7e28fb..50336e04c888a 100644 --- a/tests/ui/attributes/issue-90873.rs +++ b/tests/ui/attributes/issue-90873.rs @@ -1,7 +1,7 @@ #![u=||{static d=||1;}] -//~^ attribute value must be a literal -//~| cannot find attribute `u` in this scope -//~| missing type for `static` item +//~^ ERROR attribute value must be a literal +//~| ERROR cannot find attribute `u` in this scope +//~| ERROR missing type for `static` item #![a={impl std::ops::Neg for i8 {}}] //~^ ERROR attribute value must be a literal diff --git a/tests/ui/attributes/no-sanitize.rs b/tests/ui/attributes/no-sanitize.rs index 8c79866d5aa31..ddf909be63a8a 100644 --- a/tests/ui/attributes/no-sanitize.rs +++ b/tests/ui/attributes/no-sanitize.rs @@ -38,3 +38,8 @@ fn valid() {} #[no_sanitize(address)] static VALID : i32 = 0; + +#[no_sanitize("address")] +//~^ ERROR `#[no_sanitize(...)]` should be applied to a function +//~| ERROR invalid argument for `no_sanitize` +static VALID2 : i32 = 0; diff --git a/tests/ui/attributes/no-sanitize.stderr b/tests/ui/attributes/no-sanitize.stderr index 9b0b76e3f4eba..8d5fbb109eadb 100644 --- a/tests/ui/attributes/no-sanitize.stderr +++ b/tests/ui/attributes/no-sanitize.stderr @@ -59,5 +59,22 @@ LL | #[no_sanitize(address, memory)] LL | static INVALID : i32 = 0; | ------------------------- not a function -error: aborting due to 7 previous errors +error: `#[no_sanitize(...)]` should be applied to a function + --> $DIR/no-sanitize.rs:42:15 + | +LL | #[no_sanitize("address")] + | ^^^^^^^^^ +... +LL | static VALID2 : i32 = 0; + | ------------------------ not a function + +error: invalid argument for `no_sanitize` + --> $DIR/no-sanitize.rs:42:15 + | +LL | #[no_sanitize("address")] + | ^^^^^^^^^ + | + = note: expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` + +error: aborting due to 9 previous errors diff --git a/tests/ui/attributes/no_link/auxiliary/empty-crate-1.rs b/tests/ui/attributes/no_link/auxiliary/empty-crate-1.rs new file mode 100644 index 0000000000000..8bd2b3353b8f3 --- /dev/null +++ b/tests/ui/attributes/no_link/auxiliary/empty-crate-1.rs @@ -0,0 +1 @@ +#![crate_type = "dylib"] diff --git a/tests/ui/auxiliary/issue-13560-2.rs b/tests/ui/attributes/no_link/auxiliary/empty-crate-2.rs similarity index 100% rename from tests/ui/auxiliary/issue-13560-2.rs rename to tests/ui/attributes/no_link/auxiliary/empty-crate-2.rs diff --git a/tests/ui/attributes/no_link/auxiliary/no_link-crate.rs b/tests/ui/attributes/no_link/auxiliary/no_link-crate.rs new file mode 100644 index 0000000000000..1c3af5431cc6e --- /dev/null +++ b/tests/ui/attributes/no_link/auxiliary/no_link-crate.rs @@ -0,0 +1,6 @@ +//@ no-prefer-dynamic + +#![crate_type = "rlib"] + +#[macro_use] #[no_link] extern crate empty_crate_1 as t1; +#[macro_use] extern crate empty_crate_2 as t2; diff --git a/tests/ui/attributes/no_link/multiple-crates-and-no_link.rs b/tests/ui/attributes/no_link/multiple-crates-and-no_link.rs new file mode 100644 index 0000000000000..0e6f1deb21729 --- /dev/null +++ b/tests/ui/attributes/no_link/multiple-crates-and-no_link.rs @@ -0,0 +1,17 @@ +//! Regression test for #13560. Previously, it was possible to +//! trigger an assert in crate numbering if a series of crates +//! being loaded included a "syntax-only" extern crate. +//! But it appears we don't mess with crate numbering for +//! `#[no_link]` crates anymore, so this test doesn't seem +//! to test anything now. + +//@ run-pass +//@ needs-crate-type: dylib +//@ aux-build:empty-crate-1.rs +//@ aux-build:empty-crate-2.rs +//@ aux-build:no_link-crate.rs + +extern crate empty_crate_2 as t2; +extern crate no_link_crate as t3; + +fn main() {} diff --git a/tests/ui/attributes/no_link/no-link-unknown-crate.rs b/tests/ui/attributes/no_link/no-link-unknown-crate.rs new file mode 100644 index 0000000000000..3a91fa27ee3ba --- /dev/null +++ b/tests/ui/attributes/no_link/no-link-unknown-crate.rs @@ -0,0 +1,19 @@ +//! Unfortunately the development of `#[phase]` and `#[no_link]` +//! predates Zulip, and thus has been lost in the sands of time. +//! Understanding the true nature of this test has been left as +//! an exercise for the reader. +//! +//! But we guess from the git history that originally this +//! test was supposed to check that we error if we can't find +//! an extern crate annotated with `#[phase(syntax)]`, +//! see `macro-crate-unknown-crate.rs` in +//! . Later, we changed +//! `#[phase]` to `#![feature(plugin)]` and added a `#[no_link]`. +//! +//! I suppose that this now tests that we still error if we can't +//! find a `#[no_link]` extern crate? + +#[no_link] +extern crate doesnt_exist; //~ ERROR can't find crate + +fn main() {} diff --git a/tests/ui/no-link-unknown-crate.stderr b/tests/ui/attributes/no_link/no-link-unknown-crate.stderr similarity index 85% rename from tests/ui/no-link-unknown-crate.stderr rename to tests/ui/attributes/no_link/no-link-unknown-crate.stderr index edc248db09eac..999b013866cd6 100644 --- a/tests/ui/no-link-unknown-crate.stderr +++ b/tests/ui/attributes/no_link/no-link-unknown-crate.stderr @@ -1,5 +1,5 @@ error[E0463]: can't find crate for `doesnt_exist` - --> $DIR/no-link-unknown-crate.rs:2:1 + --> $DIR/no-link-unknown-crate.rs:17:1 | LL | extern crate doesnt_exist; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ can't find crate diff --git a/tests/ui/attributes/nonterminal-expansion.rs b/tests/ui/attributes/nonterminal-expansion.rs index 83c8f00999a72..004a8a23fd61f 100644 --- a/tests/ui/attributes/nonterminal-expansion.rs +++ b/tests/ui/attributes/nonterminal-expansion.rs @@ -5,8 +5,7 @@ macro_rules! pass_nonterminal { ($n:expr) => { #[repr(align($n))] - //~^ ERROR expected unsuffixed literal, found expression `n!()` - //~^^ ERROR incorrect `repr(align)` attribute format: `align` expects a literal integer as argument [E0693] + //~^ ERROR expected unsuffixed literal, found `expr` metavariable struct S; }; } @@ -16,5 +15,6 @@ macro_rules! n { } pass_nonterminal!(n!()); +//~^ ERROR incorrect `repr(align)` attribute format: `align` expects a literal integer as argument [E0693] fn main() {} diff --git a/tests/ui/attributes/nonterminal-expansion.stderr b/tests/ui/attributes/nonterminal-expansion.stderr index 8a85731bd5a13..9c6cb98f61965 100644 --- a/tests/ui/attributes/nonterminal-expansion.stderr +++ b/tests/ui/attributes/nonterminal-expansion.stderr @@ -1,4 +1,4 @@ -error: expected unsuffixed literal, found expression `n!()` +error: expected unsuffixed literal, found `expr` metavariable --> $DIR/nonterminal-expansion.rs:7:22 | LL | #[repr(align($n))] @@ -10,15 +10,10 @@ LL | pass_nonterminal!(n!()); = note: this error originates in the macro `pass_nonterminal` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0693]: incorrect `repr(align)` attribute format: `align` expects a literal integer as argument - --> $DIR/nonterminal-expansion.rs:7:22 + --> $DIR/nonterminal-expansion.rs:17:19 | -LL | #[repr(align($n))] - | ^^ -... LL | pass_nonterminal!(n!()); - | ----------------------- in this macro invocation - | - = note: this error originates in the macro `pass_nonterminal` (in Nightly builds, run with -Z macro-backtrace for more info) + | ^ error: aborting due to 2 previous errors diff --git a/tests/ui/attributes/outer-mod-attr-applies-only-to-first.rs b/tests/ui/attributes/outer-mod-attr-applies-only-to-first.rs new file mode 100644 index 0000000000000..ec521c1afb518 --- /dev/null +++ b/tests/ui/attributes/outer-mod-attr-applies-only-to-first.rs @@ -0,0 +1,16 @@ +//! Regression test to check that outer attributes applied to the first module item is applied to +//! its attached module item only, and not also to other subsequent module items +//! +//! Commit: + +//@ check-pass +//@ compile-flags: --cfg=first +//@ no-auto-check-cfg + +#[cfg(first)] +mod hello {} + +#[cfg(not_set)] +mod hello {} + +fn main() {} diff --git a/tests/ui/attributes/positions/used.rs b/tests/ui/attributes/positions/used.rs new file mode 100644 index 0000000000000..7950fa773a109 --- /dev/null +++ b/tests/ui/attributes/positions/used.rs @@ -0,0 +1,23 @@ +//! Checks that `#[used]` cannot be used on invalid positions. +#![crate_type = "lib"] + +#[used] +static FOO: u32 = 0; // OK + +#[used] //~ ERROR attribute must be applied to a `static` variable +fn foo() {} + +#[used] //~ ERROR attribute must be applied to a `static` variable +struct Foo {} + +#[used] //~ ERROR attribute must be applied to a `static` variable +trait Bar {} + +#[used] //~ ERROR attribute must be applied to a `static` variable +impl Bar for Foo {} + +// Regression test for . +extern "C" { + #[used] //~ ERROR attribute must be applied to a `static` variable + static BAR: i32; +} diff --git a/tests/ui/attributes/positions/used.stderr b/tests/ui/attributes/positions/used.stderr new file mode 100644 index 0000000000000..96dd43a3a937f --- /dev/null +++ b/tests/ui/attributes/positions/used.stderr @@ -0,0 +1,42 @@ +error: attribute must be applied to a `static` variable + --> $DIR/used.rs:7:1 + | +LL | #[used] + | ^^^^^^^ +LL | fn foo() {} + | ----------- but this is a function + +error: attribute must be applied to a `static` variable + --> $DIR/used.rs:10:1 + | +LL | #[used] + | ^^^^^^^ +LL | struct Foo {} + | ------------- but this is a struct + +error: attribute must be applied to a `static` variable + --> $DIR/used.rs:13:1 + | +LL | #[used] + | ^^^^^^^ +LL | trait Bar {} + | ------------ but this is a trait + +error: attribute must be applied to a `static` variable + --> $DIR/used.rs:16:1 + | +LL | #[used] + | ^^^^^^^ +LL | impl Bar for Foo {} + | ------------------- but this is a implementation block + +error: attribute must be applied to a `static` variable + --> $DIR/used.rs:21:5 + | +LL | #[used] + | ^^^^^^^ +LL | static BAR: i32; + | ---------------- but this is a foreign static item + +error: aborting due to 5 previous errors + diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs index eaf8706369a5a..2f17d9620b411 100644 --- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs +++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs @@ -30,6 +30,8 @@ pub fn e() {} //~| ERROR: expected identifier, found keyword `unsafe` //~| ERROR: malformed lint attribute input //~| ERROR: malformed lint attribute input +//~| ERROR: malformed lint attribute input +//~| ERROR: malformed lint attribute input pub fn f() {} fn main() {} diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr index 9c5751c82e4c4..25b83a26e1700 100644 --- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr +++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr @@ -114,6 +114,22 @@ LL | #[unsafe(allow(unsafe(dead_code)))] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 15 previous errors +error[E0452]: malformed lint attribute input + --> $DIR/proc-unsafe-attributes.rs:26:16 + | +LL | #[unsafe(allow(unsafe(dead_code)))] + | ^^^^^^^^^^^^^^^^^ bad attribute argument + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0452]: malformed lint attribute input + --> $DIR/proc-unsafe-attributes.rs:26:16 + | +LL | #[unsafe(allow(unsafe(dead_code)))] + | ^^^^^^^^^^^^^^^^^ bad attribute argument + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 17 previous errors For more information about this error, try `rustc --explain E0452`. diff --git a/tests/ui/attributes/use-doc-alias-name.rs b/tests/ui/attributes/use-doc-alias-name.rs new file mode 100644 index 0000000000000..1fc9199b6e382 --- /dev/null +++ b/tests/ui/attributes/use-doc-alias-name.rs @@ -0,0 +1,67 @@ +//@ aux-build: use-doc-alias-name-extern.rs + +// issue#124273 + +extern crate use_doc_alias_name_extern; + +use use_doc_alias_name_extern::*; + +#[doc(alias="LocalDocAliasS")] +struct S; + +fn main() { + LocalDocAliasS; // don't show help in local crate + //~^ ERROR: cannot find value `LocalDocAliasS` in this scope + + DocAliasS1; + //~^ ERROR: cannot find value `DocAliasS1` in this scope + //~| HELP: `S1` has a name defined in the doc alias attribute as `DocAliasS1` + + DocAliasS2; + //~^ ERROR: cannot find value `DocAliasS2` in this scope + //~| HELP: `S2` has a name defined in the doc alias attribute as `DocAliasS2` + + DocAliasS3; + //~^ ERROR: cannot find value `DocAliasS3` in this scope + //~| HELP: `S2` has a name defined in the doc alias attribute as `DocAliasS3` + + DocAliasS4; + //~^ ERROR: cannot find value `DocAliasS4` in this scope + //~| HELP: `S2` has a name defined in the doc alias attribute as `DocAliasS4` + + doc_alias_f1(); + //~^ ERROR: cannot find function `doc_alias_f1` in this scope + //~| HELP: `f` has a name defined in the doc alias attribute as `doc_alias_f1` + + doc_alias_f2(); + //~^ ERROR: cannot find function `doc_alias_f2` in this scope + //~| HELP: `f` has a name defined in the doc alias attribute as `doc_alias_f2` + + m::DocAliasS5; + //~^ ERROR: cannot find value `DocAliasS5` in module `m` + //~| HELP: `S5` has a name defined in the doc alias attribute as `DocAliasS5` + + not_exist_module::DocAliasS1; + //~^ ERROR: use of unresolved module or unlinked crate `not_exist_module` + //~| HELP: you might be missing a crate named `not_exist_module` + + use_doc_alias_name_extern::DocAliasS1; + //~^ ERROR: cannot find value `DocAliasS1` in crate `use_doc_alias_name_extern + //~| HELP: `S1` has a name defined in the doc alias attribute as `DocAliasS1` + + m::n::DocAliasX::y::S6; + //~^ ERROR: could not find `DocAliasX` in `n` + //~| HELP: `x` has a name defined in the doc alias attribute as `DocAliasX` + + m::n::x::y::DocAliasS6; + //~^ ERROR: cannot find value `DocAliasS6` in module `m::n::x::y` + //~| HELP: `S6` has a name defined in the doc alias attribute as `DocAliasS6` +} + +trait T { + fn f() { + DocAliasS1; + //~^ ERROR: cannot find value `DocAliasS1` in this scope + //~| HELP: `S1` has a name defined in the doc alias attribute as `DocAliasS1` + } +} diff --git a/tests/ui/attributes/use-doc-alias-name.stderr b/tests/ui/attributes/use-doc-alias-name.stderr new file mode 100644 index 0000000000000..07f4865e41520 --- /dev/null +++ b/tests/ui/attributes/use-doc-alias-name.stderr @@ -0,0 +1,150 @@ +error[E0433]: failed to resolve: could not find `DocAliasX` in `n` + --> $DIR/use-doc-alias-name.rs:52:11 + | +LL | m::n::DocAliasX::y::S6; + | ^^^^^^^^^ could not find `DocAliasX` in `n` + | +help: `x` has a name defined in the doc alias attribute as `DocAliasX` + | +LL - m::n::DocAliasX::y::S6; +LL + m::n::x::y::S6; + | + +error[E0425]: cannot find value `LocalDocAliasS` in this scope + --> $DIR/use-doc-alias-name.rs:13:5 + | +LL | LocalDocAliasS; // don't show help in local crate + | ^^^^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find value `DocAliasS1` in this scope + --> $DIR/use-doc-alias-name.rs:16:5 + | +LL | DocAliasS1; + | ^^^^^^^^^^ + | +help: `S1` has a name defined in the doc alias attribute as `DocAliasS1` + | +LL - DocAliasS1; +LL + S1; + | + +error[E0425]: cannot find value `DocAliasS2` in this scope + --> $DIR/use-doc-alias-name.rs:20:5 + | +LL | DocAliasS2; + | ^^^^^^^^^^ + | +help: `S2` has a name defined in the doc alias attribute as `DocAliasS2` + | +LL - DocAliasS2; +LL + S2; + | + +error[E0425]: cannot find value `DocAliasS3` in this scope + --> $DIR/use-doc-alias-name.rs:24:5 + | +LL | DocAliasS3; + | ^^^^^^^^^^ + | +help: `S2` has a name defined in the doc alias attribute as `DocAliasS3` + | +LL - DocAliasS3; +LL + S2; + | + +error[E0425]: cannot find value `DocAliasS4` in this scope + --> $DIR/use-doc-alias-name.rs:28:5 + | +LL | DocAliasS4; + | ^^^^^^^^^^ + | +help: `S2` has a name defined in the doc alias attribute as `DocAliasS4` + | +LL - DocAliasS4; +LL + S2; + | + +error[E0425]: cannot find value `DocAliasS5` in module `m` + --> $DIR/use-doc-alias-name.rs:40:8 + | +LL | m::DocAliasS5; + | ^^^^^^^^^^ + | +help: `S5` has a name defined in the doc alias attribute as `DocAliasS5` + | +LL - m::DocAliasS5; +LL + m::S5; + | + +error[E0425]: cannot find value `DocAliasS1` in crate `use_doc_alias_name_extern` + --> $DIR/use-doc-alias-name.rs:48:32 + | +LL | use_doc_alias_name_extern::DocAliasS1; + | ^^^^^^^^^^ + | +help: `S1` has a name defined in the doc alias attribute as `DocAliasS1` + | +LL - use_doc_alias_name_extern::DocAliasS1; +LL + use_doc_alias_name_extern::S1; + | + +error[E0425]: cannot find value `DocAliasS6` in module `m::n::x::y` + --> $DIR/use-doc-alias-name.rs:56:17 + | +LL | m::n::x::y::DocAliasS6; + | ^^^^^^^^^^ + | +help: `S6` has a name defined in the doc alias attribute as `DocAliasS6` + | +LL - m::n::x::y::DocAliasS6; +LL + m::n::x::y::S6; + | + +error[E0425]: cannot find value `DocAliasS1` in this scope + --> $DIR/use-doc-alias-name.rs:63:9 + | +LL | DocAliasS1; + | ^^^^^^^^^^ + | +help: `S1` has a name defined in the doc alias attribute as `DocAliasS1` + | +LL - DocAliasS1; +LL + S1; + | + +error[E0425]: cannot find function `doc_alias_f1` in this scope + --> $DIR/use-doc-alias-name.rs:32:5 + | +LL | doc_alias_f1(); + | ^^^^^^^^^^^^ + | +help: `f` has a name defined in the doc alias attribute as `doc_alias_f1` + | +LL - doc_alias_f1(); +LL + f(); + | + +error[E0425]: cannot find function `doc_alias_f2` in this scope + --> $DIR/use-doc-alias-name.rs:36:5 + | +LL | doc_alias_f2(); + | ^^^^^^^^^^^^ + | +help: `f` has a name defined in the doc alias attribute as `doc_alias_f2` + | +LL - doc_alias_f2(); +LL + f(); + | + +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `not_exist_module` + --> $DIR/use-doc-alias-name.rs:44:5 + | +LL | not_exist_module::DocAliasS1; + | ^^^^^^^^^^^^^^^^ use of unresolved module or unlinked crate `not_exist_module` + | + = help: you might be missing a crate named `not_exist_module` + +error: aborting due to 13 previous errors + +Some errors have detailed explanations: E0425, E0433. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/attributes/used-issue-126789.rs b/tests/ui/attributes/used-issue-126789.rs deleted file mode 100644 index 90a1aa8d5cc3d..0000000000000 --- a/tests/ui/attributes/used-issue-126789.rs +++ /dev/null @@ -1,6 +0,0 @@ -extern "C" { - #[used] //~ ERROR attribute must be applied to a `static` variable - static FOO: i32; -} - -fn main() {} diff --git a/tests/ui/attributes/used-issue-126789.stderr b/tests/ui/attributes/used-issue-126789.stderr deleted file mode 100644 index 6014f7af95c55..0000000000000 --- a/tests/ui/attributes/used-issue-126789.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: attribute must be applied to a `static` variable - --> $DIR/used-issue-126789.rs:2:5 - | -LL | #[used] - | ^^^^^^^ -LL | static FOO: i32; - | ---------------- but this is a foreign static item - -error: aborting due to 1 previous error - diff --git a/tests/ui/attributes/used/used-not-dead-code-lint.rs b/tests/ui/attributes/used/used-not-dead-code-lint.rs new file mode 100644 index 0000000000000..ece40ed219d4a --- /dev/null +++ b/tests/ui/attributes/used/used-not-dead-code-lint.rs @@ -0,0 +1,10 @@ +//! Checks that the `dead_code` lint does not consider `#[used]` items unused. +//! Regression test for . + +//@ check-pass +#![deny(dead_code)] + +#[used] +static FOO: u32 = 0; + +fn main() {} diff --git a/tests/ui/attributes/used_with_multi_args.rs b/tests/ui/attributes/used_with_multi_args.rs index 2e17fcfd7a493..d3109cc64442e 100644 --- a/tests/ui/attributes/used_with_multi_args.rs +++ b/tests/ui/attributes/used_with_multi_args.rs @@ -1,6 +1,6 @@ #![feature(used_with_arg)] -#[used(compiler, linker)] //~ expected `used`, `used(compiler)` or `used(linker)` +#[used(compiler, linker)] //~ ERROR expected `used`, `used(compiler)` or `used(linker)` static mut USED_COMPILER_LINKER: [usize; 1] = [0]; fn main() {} diff --git a/tests/ui/attributes/z-crate-attr.rs b/tests/ui/attributes/z-crate-attr/basic.rs similarity index 100% rename from tests/ui/attributes/z-crate-attr.rs rename to tests/ui/attributes/z-crate-attr/basic.rs diff --git a/tests/ui/attributes/z-crate-attr/cfg-false.rs b/tests/ui/attributes/z-crate-attr/cfg-false.rs new file mode 100644 index 0000000000000..5e5662c74385f --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/cfg-false.rs @@ -0,0 +1,7 @@ +// Ensure that `-Z crate-attr=cfg(false)` can comment out the whole crate +//@ compile-flags: --crate-type=lib -Zcrate-attr=cfg(false) +//@ check-pass + +// NOTE: duplicate items are load-bearing +fn foo() {} +fn foo() {} diff --git a/tests/ui/attributes/z-crate-attr/comments.rs b/tests/ui/attributes/z-crate-attr/comments.rs new file mode 100644 index 0000000000000..c1ab041f34477 --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/comments.rs @@ -0,0 +1,5 @@ +//@ check-pass +//@ compile-flags: -Zcrate-attr=/*hi-there*/feature(rustc_attrs) + +#[rustc_dummy] +fn main() {} diff --git a/tests/ui/attributes/z-crate-attr/crate-name.rs b/tests/ui/attributes/z-crate-attr/crate-name.rs new file mode 100644 index 0000000000000..d49830390e2ec --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/crate-name.rs @@ -0,0 +1,6 @@ +// Ensure that `crate_name` and `crate_type` can be set through `-Z crate-attr`. +//@ check-pass +//@ compile-flags: -Zcrate-attr=crate_name="override" +fn main() { + assert_eq!(module_path!(), "r#override"); +} diff --git a/tests/ui/attributes/z-crate-attr/crate-type.rs b/tests/ui/attributes/z-crate-attr/crate-type.rs new file mode 100644 index 0000000000000..0e7411865af9c --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/crate-type.rs @@ -0,0 +1,3 @@ +//@ check-pass +//@ compile-flags: -Zcrate-attr=crate_type="lib" +// notice the lack of `main` is load-bearing diff --git a/tests/ui/attributes/z-crate-attr/garbage.rs b/tests/ui/attributes/z-crate-attr/garbage.rs new file mode 100644 index 0000000000000..69444e1935a2e --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/garbage.rs @@ -0,0 +1,7 @@ +// Show diagnostics for invalid tokens +//@ compile-flags: -Zcrate-attr=`%~@$# + +fn main() {} + +//~? ERROR unknown start of token: ` +//~? ERROR expected identifier, found `%` diff --git a/tests/ui/attributes/z-crate-attr/garbage.stderr b/tests/ui/attributes/z-crate-attr/garbage.stderr new file mode 100644 index 0000000000000..12d18b0845fc0 --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/garbage.stderr @@ -0,0 +1,20 @@ +error: unknown start of token: ` + --> :1:4 + | +LL | #![`%~@$#] + | ^ + | +help: Unicode character '`' (Grave Accent) looks like ''' (Single Quote), but it is not + | +LL - #![`%~@$#] +LL + #!['%~@$#] + | + +error: expected identifier, found `%` + --> :1:5 + | +LL | #![`%~@$#] + | ^ expected identifier + +error: aborting due to 2 previous errors + diff --git a/tests/ui/attributes/z-crate-attr/injection.rs b/tests/ui/attributes/z-crate-attr/injection.rs new file mode 100644 index 0000000000000..ee7a27c7490ce --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/injection.rs @@ -0,0 +1,3 @@ +//@ compile-flags: '-Zcrate-attr=feature(yeet_expr)]fn main(){}#[inline' +//~? ERROR unexpected token +fn foo() {} //~ ERROR `main` function not found diff --git a/tests/ui/attributes/z-crate-attr/injection.stderr b/tests/ui/attributes/z-crate-attr/injection.stderr new file mode 100644 index 0000000000000..899dad07e6048 --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/injection.stderr @@ -0,0 +1,15 @@ +error: unexpected token: keyword `fn` + --> :1:23 + | +LL | #![feature(yeet_expr)]fn main(){}#[inline] + | ^^ unexpected token after this + +error[E0601]: `main` function not found in crate `injection` + --> $DIR/injection.rs:3:12 + | +LL | fn foo() {} + | ^ consider adding a `main` function to `$DIR/injection.rs` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0601`. diff --git a/tests/ui/attributes/z-crate-attr/injection2.rs b/tests/ui/attributes/z-crate-attr/injection2.rs new file mode 100644 index 0000000000000..67ae3d17f3e78 --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/injection2.rs @@ -0,0 +1,3 @@ +//@ compile-flags: -Zcrate-attr=feature(yeet_expr)]#![allow(warnings) +//~? ERROR unexpected token +fn foo() {} //~ ERROR `main` function not found diff --git a/tests/ui/attributes/z-crate-attr/injection2.stderr b/tests/ui/attributes/z-crate-attr/injection2.stderr new file mode 100644 index 0000000000000..51f54f900a192 --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/injection2.stderr @@ -0,0 +1,15 @@ +error: unexpected token: `#` + --> :1:23 + | +LL | #![feature(yeet_expr)]#![allow(warnings)] + | ^ unexpected token after this + +error[E0601]: `main` function not found in crate `injection2` + --> $DIR/injection2.rs:3:12 + | +LL | fn foo() {} + | ^ consider adding a `main` function to `$DIR/injection2.rs` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0601`. diff --git a/tests/ui/attributes/z-crate-attr/inner-attr.rs b/tests/ui/attributes/z-crate-attr/inner-attr.rs new file mode 100644 index 0000000000000..47ecad700624f --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/inner-attr.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -Zcrate-attr=#![feature(foo)] + +fn main() {} + +//~? ERROR expected identifier, found `#` diff --git a/tests/ui/attributes/z-crate-attr/inner-attr.stderr b/tests/ui/attributes/z-crate-attr/inner-attr.stderr new file mode 100644 index 0000000000000..1acb8c2e750b3 --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/inner-attr.stderr @@ -0,0 +1,8 @@ +error: expected identifier, found `#` + --> :1:4 + | +LL | #![#![feature(foo)]] + | ^ expected identifier + +error: aborting due to 1 previous error + diff --git a/tests/ui/attributes/z-crate-attr/multiple.rs b/tests/ui/attributes/z-crate-attr/multiple.rs new file mode 100644 index 0000000000000..8c60ea64feca6 --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/multiple.rs @@ -0,0 +1,3 @@ +//@ compile-flags: -Zcrate-attr=feature(foo),feature(bar) +//~? ERROR expected `]` +fn main() {} diff --git a/tests/ui/attributes/z-crate-attr/multiple.stderr b/tests/ui/attributes/z-crate-attr/multiple.stderr new file mode 100644 index 0000000000000..b95c95dcd7393 --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/multiple.stderr @@ -0,0 +1,8 @@ +error: expected `]`, found `,` + --> :1:16 + | +LL | #![feature(foo),feature(bar)] + | ^ expected `]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/attributes/z-crate-attr/respect-existing-attrs.rs b/tests/ui/attributes/z-crate-attr/respect-existing-attrs.rs new file mode 100644 index 0000000000000..71f2559998fba --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/respect-existing-attrs.rs @@ -0,0 +1,9 @@ +// Make sure that existing root attributes are still respected even when `-Zcrate-attr` is present. +//@ run-pass +//@ compile-flags: -Zcrate-attr=feature(rustc_attrs) +#![crate_name = "override"] + +#[rustc_dummy] +fn main() { + assert_eq!(module_path!(), "r#override"); +} diff --git a/tests/ui/attributes/z-crate-attr/shebang.rs b/tests/ui/attributes/z-crate-attr/shebang.rs new file mode 100644 index 0000000000000..195393acaf5ec --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/shebang.rs @@ -0,0 +1,6 @@ +#!/usr/bin/env -S cargo +nightly -Zscript +// Make sure that shebangs are still allowed even when `-Zcrate-attr` is present. +//@ check-pass +//@ compile-flags: -Zcrate-attr=feature(rustc_attrs) +#[rustc_dummy] +fn main() {} diff --git a/tests/ui/attributes/z-crate-attr/unbalanced-paren.rs b/tests/ui/attributes/z-crate-attr/unbalanced-paren.rs new file mode 100644 index 0000000000000..5ef0a75a3a87b --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/unbalanced-paren.rs @@ -0,0 +1,4 @@ +// Show diagnostics for unbalanced parens. +//@ compile-flags: -Zcrate-attr=( +//~? ERROR mismatched closing delimiter +fn main() {} diff --git a/tests/ui/attributes/z-crate-attr/unbalanced-paren.stderr b/tests/ui/attributes/z-crate-attr/unbalanced-paren.stderr new file mode 100644 index 0000000000000..f6545d1db8bc9 --- /dev/null +++ b/tests/ui/attributes/z-crate-attr/unbalanced-paren.stderr @@ -0,0 +1,11 @@ +error: mismatched closing delimiter: `]` + --> :1:4 + | +LL | #![(] + | -^^ mismatched closing delimiter + | || + | |unclosed delimiter + | closing delimiter possibly meant for this + +error: aborting due to 1 previous error + diff --git a/tests/ui/augmented-assignments-feature-gate-cross.rs b/tests/ui/augmented-assignments-feature-gate-cross.rs deleted file mode 100644 index d402d20061779..0000000000000 --- a/tests/ui/augmented-assignments-feature-gate-cross.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ run-pass -//@ aux-build:augmented_assignments.rs - -extern crate augmented_assignments; - -use augmented_assignments::Int; - -fn main() { - let mut x = Int(0); - x += 1; -} diff --git a/tests/ui/augmented-assignments.rs b/tests/ui/augmented-assignments.rs deleted file mode 100644 index 440a4a7fd6500..0000000000000 --- a/tests/ui/augmented-assignments.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::ops::AddAssign; - -#[derive(Clone)] -struct Int(i32); - -impl AddAssign for Int { - fn add_assign(&mut self, _: Int) { - unimplemented!() - } -} - -fn main() { - let mut x = Int(1); //~ NOTE binding `x` declared here - x - //~^ NOTE borrow of `x` occurs here - += - x; - //~^ ERROR cannot move out of `x` because it is borrowed - //~| move out of `x` occurs here - - let y = Int(2); - //~^ HELP consider changing this to be mutable - //~| SUGGESTION mut - y //~ ERROR cannot borrow `y` as mutable, as it is not declared as mutable - //~| cannot borrow as mutable - += - Int(1); -} diff --git a/tests/ui/auto-instantiate.rs b/tests/ui/auto-instantiate.rs deleted file mode 100644 index 73ad5d701e182..0000000000000 --- a/tests/ui/auto-instantiate.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ run-pass - -#![allow(dead_code)] -#[derive(Debug)] -struct Pair { a: T, b: U } -struct Triple { x: isize, y: isize, z: isize } - -fn f(x: T, y: U) -> Pair { return Pair {a: x, b: y}; } - -pub fn main() { - println!("{}", f(Triple {x: 3, y: 4, z: 5}, 4).a.x); - println!("{}", f(5, 6).a); -} diff --git a/tests/ui/auto-traits/auto-trait-validation.fixed b/tests/ui/auto-traits/auto-trait-validation.fixed index f65952e00f588..8a445448c8581 100644 --- a/tests/ui/auto-traits/auto-trait-validation.fixed +++ b/tests/ui/auto-traits/auto-trait-validation.fixed @@ -4,11 +4,11 @@ //@ run-rustfix auto trait Generic {} -//~^ auto traits cannot have generic parameters [E0567] +//~^ ERROR auto traits cannot have generic parameters [E0567] auto trait Bound {} -//~^ auto traits cannot have super traits or lifetime bounds [E0568] +//~^ ERROR auto traits cannot have super traits or lifetime bounds [E0568] auto trait LifetimeBound {} -//~^ auto traits cannot have super traits or lifetime bounds [E0568] +//~^ ERROR auto traits cannot have super traits or lifetime bounds [E0568] auto trait MyTrait { } -//~^ auto traits cannot have associated items [E0380] +//~^ ERROR auto traits cannot have associated items [E0380] fn main() {} diff --git a/tests/ui/auto-traits/auto-trait-validation.rs b/tests/ui/auto-traits/auto-trait-validation.rs index c83d7426e47ce..b5e7505d86a24 100644 --- a/tests/ui/auto-traits/auto-trait-validation.rs +++ b/tests/ui/auto-traits/auto-trait-validation.rs @@ -4,11 +4,11 @@ //@ run-rustfix auto trait Generic {} -//~^ auto traits cannot have generic parameters [E0567] +//~^ ERROR auto traits cannot have generic parameters [E0567] auto trait Bound : Copy {} -//~^ auto traits cannot have super traits or lifetime bounds [E0568] +//~^ ERROR auto traits cannot have super traits or lifetime bounds [E0568] auto trait LifetimeBound : 'static {} -//~^ auto traits cannot have super traits or lifetime bounds [E0568] +//~^ ERROR auto traits cannot have super traits or lifetime bounds [E0568] auto trait MyTrait { fn foo() {} } -//~^ auto traits cannot have associated items [E0380] +//~^ ERROR auto traits cannot have associated items [E0380] fn main() {} diff --git a/tests/ui/auto-traits/pre-cfg.rs b/tests/ui/auto-traits/pre-cfg.rs index e806686f965c3..4820a53535803 100644 --- a/tests/ui/auto-traits/pre-cfg.rs +++ b/tests/ui/auto-traits/pre-cfg.rs @@ -1,6 +1,6 @@ //@ check-pass -#[cfg(FALSE)] +#[cfg(false)] auto trait Foo {} //~^ WARN `auto` traits are unstable //~| WARN unstable syntax can change at any point in the future, causing a hard error! diff --git a/tests/ui/auto-traits/ungated-impl.rs b/tests/ui/auto-traits/ungated-impl.rs new file mode 100644 index 0000000000000..d46b4b01af9c7 --- /dev/null +++ b/tests/ui/auto-traits/ungated-impl.rs @@ -0,0 +1,7 @@ +auto trait MyTrait {} +//~^ ERROR auto traits are experimental and possibly buggy + +impl !MyTrait for *mut T {} +//~^ ERROR negative trait bounds are not fully implemented + +fn main() {} diff --git a/tests/ui/auto-traits/ungated-impl.stderr b/tests/ui/auto-traits/ungated-impl.stderr new file mode 100644 index 0000000000000..9d10d46a90283 --- /dev/null +++ b/tests/ui/auto-traits/ungated-impl.stderr @@ -0,0 +1,23 @@ +error[E0658]: auto traits are experimental and possibly buggy + --> $DIR/ungated-impl.rs:1:1 + | +LL | auto trait MyTrait {} + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #13231 for more information + = help: add `#![feature(auto_traits)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: negative trait bounds are not fully implemented; use marker types for now + --> $DIR/ungated-impl.rs:4:9 + | +LL | impl !MyTrait for *mut T {} + | ^^^^^^^^ + | + = note: see issue #68318 for more information + = help: add `#![feature(negative_impls)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/autodiff/autodiff_illegal.rs b/tests/ui/autodiff/autodiff_illegal.rs index c0548d2bbb8ff..a916bd8b857b9 100644 --- a/tests/ui/autodiff/autodiff_illegal.rs +++ b/tests/ui/autodiff/autodiff_illegal.rs @@ -12,41 +12,40 @@ use std::autodiff::autodiff; // We can't use Duplicated on scalars #[autodiff(df1, Reverse, Duplicated)] pub fn f1(x: f64) { -//~^ ERROR Duplicated can not be used for this type + //~^ ERROR Duplicated can not be used for this type unimplemented!() } // Too many activities #[autodiff(df3, Reverse, Duplicated, Const)] pub fn f3(x: f64) { -//~^^ ERROR expected 1 activities, but found 2 + //~^^ ERROR expected 1 activities, but found 2 unimplemented!() } // To few activities #[autodiff(df4, Reverse)] pub fn f4(x: f64) { -//~^^ ERROR expected 1 activities, but found 0 + //~^^ ERROR expected 1 activities, but found 0 unimplemented!() } // We can't use Dual in Reverse mode #[autodiff(df5, Reverse, Dual)] pub fn f5(x: f64) { -//~^^ ERROR Dual can not be used in Reverse Mode + //~^^ ERROR Dual can not be used in Reverse Mode unimplemented!() } // We can't use Duplicated in Forward mode #[autodiff(df6, Forward, Duplicated)] pub fn f6(x: f64) { -//~^^ ERROR Duplicated can not be used in Forward Mode -//~^^ ERROR Duplicated can not be used for this type + //~^^ ERROR Duplicated can not be used in Forward Mode + //~^^ ERROR Duplicated can not be used for this type unimplemented!() } fn dummy() { - #[autodiff(df7, Forward, Dual)] let mut x = 5; //~^ ERROR autodiff must be applied to function @@ -64,21 +63,21 @@ fn dummy() { // Malformed, where args? #[autodiff] pub fn f7(x: f64) { -//~^ ERROR autodiff must be applied to function + //~^ ERROR autodiff requires at least a name and mode unimplemented!() } // Malformed, where args? #[autodiff()] pub fn f8(x: f64) { -//~^ ERROR autodiff requires at least a name and mode + //~^ ERROR autodiff requires at least a name and mode unimplemented!() } // Invalid attribute syntax #[autodiff = ""] pub fn f9(x: f64) { -//~^ ERROR autodiff must be applied to function + //~^ ERROR autodiff requires at least a name and mode unimplemented!() } @@ -87,21 +86,21 @@ fn fn_exists() {} // We colide with an already existing function #[autodiff(fn_exists, Reverse, Active)] pub fn f10(x: f64) { -//~^^ ERROR the name `fn_exists` is defined multiple times [E0428] + //~^^ ERROR the name `fn_exists` is defined multiple times [E0428] unimplemented!() } // Malformed, missing a mode #[autodiff(df11)] pub fn f11() { -//~^ ERROR autodiff requires at least a name and mode + //~^ ERROR autodiff requires at least a name and mode unimplemented!() } // Invalid Mode #[autodiff(df12, Debug)] pub fn f12() { -//~^^ ERROR unknown Mode: `Debug`. Use `Forward` or `Reverse` + //~^^ ERROR unknown Mode: `Debug`. Use `Forward` or `Reverse` unimplemented!() } @@ -109,7 +108,7 @@ pub fn f12() { // or use two autodiff macros. #[autodiff(df13, Forward, Reverse)] pub fn f13() { -//~^^ ERROR did not recognize Activity: `Reverse` + //~^^ ERROR did not recognize Activity: `Reverse` unimplemented!() } @@ -130,7 +129,7 @@ type MyFloat = f32; // like THIR which has type information available. #[autodiff(df15, Reverse, Active, Active)] fn f15(x: MyFloat) -> f32 { -//~^^ ERROR failed to resolve: use of undeclared type `MyFloat` [E0433] + //~^^ ERROR failed to resolve: use of undeclared type `MyFloat` [E0433] unimplemented!() } @@ -141,7 +140,9 @@ fn f16(x: f32) -> MyFloat { } #[repr(transparent)] -struct F64Trans { inner: f64 } +struct F64Trans { + inner: f64, +} // We would like to support `#[repr(transparent)]` f32/f64 wrapper in return type in the future #[autodiff(df17, Reverse, Active, Active)] @@ -156,5 +157,31 @@ fn f18(x: F64Trans) -> f64 { unimplemented!() } +// Invalid return activity +#[autodiff(df19, Forward, Dual, Active)] +fn f19(x: f32) -> f32 { + //~^^ ERROR invalid return activity Active in Forward Mode + unimplemented!() +} + +#[autodiff(df20, Reverse, Active, Dual)] +fn f20(x: f32) -> f32 { + //~^^ ERROR invalid return activity Dual in Reverse Mode + unimplemented!() +} + +// Duplicated cannot be used as return activity +#[autodiff(df21, Reverse, Active, Duplicated)] +fn f21(x: f32) -> f32 { + //~^^ ERROR invalid return activity Duplicated in Reverse Mode + unimplemented!() +} + +struct DoesNotImplDefault; +#[autodiff(df22, Forward, Dual)] +pub fn f22() -> DoesNotImplDefault { + //~^^ ERROR the function or associated item `default` exists for tuple `(DoesNotImplDefault, DoesNotImplDefault)`, but its trait bounds were not satisfied + unimplemented!() +} fn main() {} diff --git a/tests/ui/autodiff/autodiff_illegal.stderr b/tests/ui/autodiff/autodiff_illegal.stderr index 3a7242b2f5d95..b119f61b8ae66 100644 --- a/tests/ui/autodiff/autodiff_illegal.stderr +++ b/tests/ui/autodiff/autodiff_illegal.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/autodiff_illegal.rs:54:5 + --> $DIR/autodiff_illegal.rs:53:5 | LL | #[autodiff(df7, Forward, Dual)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,32 +19,24 @@ error: expected 1 activities, but found 2 | LL | #[autodiff(df3, Reverse, Duplicated, Const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error: expected 1 activities, but found 0 --> $DIR/autodiff_illegal.rs:27:1 | LL | #[autodiff(df4, Reverse)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error: Dual can not be used in Reverse Mode --> $DIR/autodiff_illegal.rs:34:1 | LL | #[autodiff(df5, Reverse, Dual)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error: Duplicated can not be used in Forward Mode --> $DIR/autodiff_illegal.rs:41:1 | LL | #[autodiff(df6, Forward, Duplicated)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error: Duplicated can not be used for this type --> $DIR/autodiff_illegal.rs:42:14 @@ -53,25 +45,25 @@ LL | pub fn f6(x: f64) { | ^^^ error: autodiff must be applied to function - --> $DIR/autodiff_illegal.rs:51:5 + --> $DIR/autodiff_illegal.rs:50:5 | LL | let mut x = 5; | ^^^^^^^^^^^^^^ error: autodiff must be applied to function - --> $DIR/autodiff_illegal.rs:55:5 + --> $DIR/autodiff_illegal.rs:54:5 | LL | x = x + 3; | ^ error: autodiff must be applied to function - --> $DIR/autodiff_illegal.rs:60:5 + --> $DIR/autodiff_illegal.rs:59:5 | LL | let add_one_v2 = |x: u32| -> u32 { x + 1 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: autodiff must be applied to function - --> $DIR/autodiff_illegal.rs:66:1 +error: autodiff requires at least a name and mode + --> $DIR/autodiff_illegal.rs:65:1 | LL | / pub fn f7(x: f64) { LL | | @@ -80,7 +72,7 @@ LL | | } | |_^ error: autodiff requires at least a name and mode - --> $DIR/autodiff_illegal.rs:73:1 + --> $DIR/autodiff_illegal.rs:72:1 | LL | / pub fn f8(x: f64) { LL | | @@ -88,8 +80,8 @@ LL | | unimplemented!() LL | | } | |_^ -error: autodiff must be applied to function - --> $DIR/autodiff_illegal.rs:80:1 +error: autodiff requires at least a name and mode + --> $DIR/autodiff_illegal.rs:79:1 | LL | / pub fn f9(x: f64) { LL | | @@ -98,7 +90,7 @@ LL | | } | |_^ error[E0428]: the name `fn_exists` is defined multiple times - --> $DIR/autodiff_illegal.rs:88:1 + --> $DIR/autodiff_illegal.rs:87:1 | LL | fn fn_exists() {} | -------------- previous definition of the value `fn_exists` here @@ -107,10 +99,9 @@ LL | #[autodiff(fn_exists, Reverse, Active)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `fn_exists` redefined here | = note: `fn_exists` must be defined only once in the value namespace of this module - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error: autodiff requires at least a name and mode - --> $DIR/autodiff_illegal.rs:96:1 + --> $DIR/autodiff_illegal.rs:95:1 | LL | / pub fn f11() { LL | | @@ -119,34 +110,65 @@ LL | | } | |_^ error: unknown Mode: `Debug`. Use `Forward` or `Reverse` - --> $DIR/autodiff_illegal.rs:102:18 + --> $DIR/autodiff_illegal.rs:101:18 | LL | #[autodiff(df12, Debug)] | ^^^^^ error: did not recognize Activity: `Reverse` - --> $DIR/autodiff_illegal.rs:110:27 + --> $DIR/autodiff_illegal.rs:109:27 | LL | #[autodiff(df13, Forward, Reverse)] | ^^^^^^^ +error: invalid return activity Active in Forward Mode + --> $DIR/autodiff_illegal.rs:161:1 + | +LL | #[autodiff(df19, Forward, Dual, Active)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: invalid return activity Dual in Reverse Mode + --> $DIR/autodiff_illegal.rs:167:1 + | +LL | #[autodiff(df20, Reverse, Active, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: invalid return activity Duplicated in Reverse Mode + --> $DIR/autodiff_illegal.rs:174:1 + | +LL | #[autodiff(df21, Reverse, Active, Duplicated)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0433]: failed to resolve: use of undeclared type `MyFloat` - --> $DIR/autodiff_illegal.rs:131:1 + --> $DIR/autodiff_illegal.rs:130:1 | LL | #[autodiff(df15, Reverse, Active, Active)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `MyFloat` - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0433]: failed to resolve: use of undeclared type `F64Trans` - --> $DIR/autodiff_illegal.rs:153:1 + --> $DIR/autodiff_illegal.rs:154:1 | LL | #[autodiff(df18, Reverse, Active, Active)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `F64Trans` + +error[E0599]: the function or associated item `default` exists for tuple `(DoesNotImplDefault, DoesNotImplDefault)`, but its trait bounds were not satisfied + --> $DIR/autodiff_illegal.rs:181:1 + | +LL | struct DoesNotImplDefault; + | ------------------------- doesn't satisfy `DoesNotImplDefault: Default` +LL | #[autodiff(df22, Forward, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item cannot be called on `(DoesNotImplDefault, DoesNotImplDefault)` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `DoesNotImplDefault: Default` + which is required by `(DoesNotImplDefault, DoesNotImplDefault): Default` +help: consider annotating `DoesNotImplDefault` with `#[derive(Default)]` + | +LL + #[derive(Default)] +LL | struct DoesNotImplDefault; | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 19 previous errors +error: aborting due to 23 previous errors -Some errors have detailed explanations: E0428, E0433, E0658. +Some errors have detailed explanations: E0428, E0433, E0599, E0658. For more information about an error, try `rustc --explain E0428`. diff --git a/tests/ui/auxiliary/default-ty-param-cross-crate-crate.rs b/tests/ui/auxiliary/default-ty-param-cross-crate-crate.rs deleted file mode 100644 index d722b78768a47..0000000000000 --- a/tests/ui/auxiliary/default-ty-param-cross-crate-crate.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![crate_type = "lib"] -#![crate_name = "default_param_test"] -#![feature(default_type_parameter_fallback)] - -use std::marker::PhantomData; - -pub struct Foo(PhantomData<(A, B)>); - -pub fn bleh() -> Foo { Foo(PhantomData) } diff --git a/tests/ui/auxiliary/edition-kw-macro-2015.rs b/tests/ui/auxiliary/edition-kw-macro-2015.rs deleted file mode 100644 index 7f479fa93708a..0000000000000 --- a/tests/ui/auxiliary/edition-kw-macro-2015.rs +++ /dev/null @@ -1,26 +0,0 @@ -//@ edition:2015 - -#[macro_export] -macro_rules! produces_async { - () => (pub fn async() {}) -} - -#[macro_export] -macro_rules! produces_async_raw { - () => (pub fn r#async() {}) -} - -#[macro_export] -macro_rules! consumes_async { - (async) => (1) -} - -#[macro_export] -macro_rules! consumes_async_raw { - (r#async) => (1) -} - -#[macro_export] -macro_rules! passes_ident { - ($i: ident) => ($i) -} diff --git a/tests/ui/auxiliary/edition-kw-macro-2018.rs b/tests/ui/auxiliary/edition-kw-macro-2018.rs deleted file mode 100644 index ba8ecc4d83b3f..0000000000000 --- a/tests/ui/auxiliary/edition-kw-macro-2018.rs +++ /dev/null @@ -1,26 +0,0 @@ -//@ edition:2018 - -#[macro_export] -macro_rules! produces_async { - () => (pub fn async() {}) -} - -#[macro_export] -macro_rules! produces_async_raw { - () => (pub fn r#async() {}) -} - -#[macro_export] -macro_rules! consumes_async { - (async) => (1) -} - -#[macro_export] -macro_rules! consumes_async_raw { - (r#async) => (1) -} - -#[macro_export] -macro_rules! passes_ident { - ($i: ident) => ($i) -} diff --git a/tests/ui/auxiliary/inner_static.rs b/tests/ui/auxiliary/inner_static.rs deleted file mode 100644 index 42dcd379d41e4..0000000000000 --- a/tests/ui/auxiliary/inner_static.rs +++ /dev/null @@ -1,51 +0,0 @@ -pub struct A { pub v: T } -pub struct B { pub v: T } - -pub mod test { - pub struct A { pub v: T } - - impl A { - pub fn foo(&self) -> isize { - static a: isize = 5; - return a - } - - pub fn bar(&self) -> isize { - static a: isize = 6; - return a; - } - } -} - -impl A { - pub fn foo(&self) -> isize { - static a: isize = 1; - return a - } - - pub fn bar(&self) -> isize { - static a: isize = 2; - return a; - } -} - -impl B { - pub fn foo(&self) -> isize { - static a: isize = 3; - return a - } - - pub fn bar(&self) -> isize { - static a: isize = 4; - return a; - } -} - -pub fn foo() -> isize { - let a = A { v: () }; - let b = B { v: () }; - let c = test::A { v: () }; - return a.foo() + a.bar() + - b.foo() + b.bar() + - c.foo() + c.bar(); -} diff --git a/tests/ui/auxiliary/issue-13560-1.rs b/tests/ui/auxiliary/issue-13560-1.rs deleted file mode 100644 index baca1567e1b08..0000000000000 --- a/tests/ui/auxiliary/issue-13560-1.rs +++ /dev/null @@ -1,3 +0,0 @@ -//@ no-prefer-dynamic - -#![crate_type = "dylib"] diff --git a/tests/ui/auxiliary/issue-13560-3.rs b/tests/ui/auxiliary/issue-13560-3.rs deleted file mode 100644 index 4aab2ddc73a01..0000000000000 --- a/tests/ui/auxiliary/issue-13560-3.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ no-prefer-dynamic - -#![crate_type = "rlib"] - -#[macro_use] #[no_link] extern crate issue_13560_1 as t1; -#[macro_use] extern crate issue_13560_2 as t2; diff --git a/tests/ui/auxiliary/removing-extern-crate.rs b/tests/ui/auxiliary/removing-extern-crate.rs deleted file mode 100644 index 65e2cc340450f..0000000000000 --- a/tests/ui/auxiliary/removing-extern-crate.rs +++ /dev/null @@ -1 +0,0 @@ -// intentionally blank diff --git a/tests/ui/augmented-assignments-rpass.rs b/tests/ui/binop/augmented-assignment.rs similarity index 100% rename from tests/ui/augmented-assignments-rpass.rs rename to tests/ui/binop/augmented-assignment.rs diff --git a/tests/ui/binop/augmented-assignments-cross-crate.rs b/tests/ui/binop/augmented-assignments-cross-crate.rs new file mode 100644 index 0000000000000..6dbb03509884d --- /dev/null +++ b/tests/ui/binop/augmented-assignments-cross-crate.rs @@ -0,0 +1,13 @@ +//! Smoke test for overloaded compound assignments cross-crate. + +//@ run-pass +//@ aux-build:augmented_assignments.rs + +extern crate augmented_assignments; + +use augmented_assignments::Int; + +fn main() { + let mut x = Int(0); + x += 1; +} diff --git a/tests/ui/auxiliary/augmented_assignments.rs b/tests/ui/binop/auxiliary/augmented_assignments.rs similarity index 100% rename from tests/ui/auxiliary/augmented_assignments.rs rename to tests/ui/binop/auxiliary/augmented_assignments.rs diff --git a/tests/ui/binop/binop-bitxor-str.rs b/tests/ui/binop/binop-bitxor-str.rs index d59e46167fe03..3072fa6ae90df 100644 --- a/tests/ui/binop/binop-bitxor-str.rs +++ b/tests/ui/binop/binop-bitxor-str.rs @@ -1,3 +1,2 @@ -//@ error-pattern:no implementation for `String ^ String` - fn main() { let x = "a".to_string() ^ "b".to_string(); } +//~^ ERROR no implementation for `String ^ String` diff --git a/tests/ui/binop/binop-bitxor-str.stderr b/tests/ui/binop/binop-bitxor-str.stderr index 9d9ec6c5af6be..d4bb0c17bfa78 100644 --- a/tests/ui/binop/binop-bitxor-str.stderr +++ b/tests/ui/binop/binop-bitxor-str.stderr @@ -1,5 +1,5 @@ error[E0369]: no implementation for `String ^ String` - --> $DIR/binop-bitxor-str.rs:3:37 + --> $DIR/binop-bitxor-str.rs:1:37 | LL | fn main() { let x = "a".to_string() ^ "b".to_string(); } | --------------- ^ --------------- String diff --git a/tests/ui/binop/binop-mul-bool.rs b/tests/ui/binop/binop-mul-bool.rs index 0b4ed21a12d00..706195d215ccc 100644 --- a/tests/ui/binop/binop-mul-bool.rs +++ b/tests/ui/binop/binop-mul-bool.rs @@ -1,3 +1 @@ -//@ error-pattern:cannot multiply `bool` by `bool` - -fn main() { let x = true * false; } +fn main() { let x = true * false; } //~ ERROR cannot multiply `bool` by `bool` diff --git a/tests/ui/binop/binop-mul-bool.stderr b/tests/ui/binop/binop-mul-bool.stderr index 82d066f45a4e2..7fa70f65f56a0 100644 --- a/tests/ui/binop/binop-mul-bool.stderr +++ b/tests/ui/binop/binop-mul-bool.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot multiply `bool` by `bool` - --> $DIR/binop-mul-bool.rs:3:26 + --> $DIR/binop-mul-bool.rs:1:26 | LL | fn main() { let x = true * false; } | ---- ^ ----- bool diff --git a/tests/ui/binop/shift-various-bad-types.rs b/tests/ui/binop/shift-various-bad-types.rs index 31224bbca1efc..025cd922d7882 100644 --- a/tests/ui/binop/shift-various-bad-types.rs +++ b/tests/ui/binop/shift-various-bad-types.rs @@ -1,5 +1,7 @@ // Test that we can do shifts by any integral type. +//@ dont-require-annotations: NOTE + struct Panolpy { char: char, str: &'static str, @@ -24,7 +26,7 @@ fn foo(p: &Panolpy) { // Type of the result follows the LHS, not the RHS: let _: i32 = 22_i64 >> 1_i32; //~^ ERROR mismatched types - //~| expected `i32`, found `i64` + //~| NOTE expected `i32`, found `i64` } fn main() { diff --git a/tests/ui/binop/shift-various-bad-types.stderr b/tests/ui/binop/shift-various-bad-types.stderr index d7c9eb5f9df32..5d16ed7e50b44 100644 --- a/tests/ui/binop/shift-various-bad-types.stderr +++ b/tests/ui/binop/shift-various-bad-types.stderr @@ -1,5 +1,5 @@ error[E0277]: no implementation for `{integer} >> char` - --> $DIR/shift-various-bad-types.rs:9:8 + --> $DIR/shift-various-bad-types.rs:11:8 | LL | 22 >> p.char; | ^^ no implementation for `{integer} >> char` @@ -17,7 +17,7 @@ LL | 22 >> p.char; and 568 others error[E0277]: no implementation for `{integer} >> &str` - --> $DIR/shift-various-bad-types.rs:12:8 + --> $DIR/shift-various-bad-types.rs:14:8 | LL | 22 >> p.str; | ^^ no implementation for `{integer} >> &str` @@ -35,7 +35,7 @@ LL | 22 >> p.str; and 568 others error[E0277]: no implementation for `{integer} >> &Panolpy` - --> $DIR/shift-various-bad-types.rs:15:8 + --> $DIR/shift-various-bad-types.rs:17:8 | LL | 22 >> p; | ^^ no implementation for `{integer} >> &Panolpy` @@ -53,7 +53,7 @@ LL | 22 >> p; and 568 others error[E0308]: mismatched types - --> $DIR/shift-various-bad-types.rs:25:18 + --> $DIR/shift-various-bad-types.rs:27:18 | LL | let _: i32 = 22_i64 >> 1_i32; | --- ^^^^^^^^^^^^^^^ expected `i32`, found `i64` diff --git a/tests/ui/blind/blind-item-item-shadow.rs b/tests/ui/blind/blind-item-item-shadow.rs index 82d07ea70919b..4384156941776 100644 --- a/tests/ui/blind/blind-item-item-shadow.rs +++ b/tests/ui/blind/blind-item-item-shadow.rs @@ -1,7 +1,9 @@ +//@ dont-require-annotations: NOTE + mod foo { pub mod foo { } } use foo::foo; //~^ ERROR the name `foo` is defined multiple times -//~| `foo` reimported here +//~| NOTE `foo` reimported here fn main() {} diff --git a/tests/ui/blind/blind-item-item-shadow.stderr b/tests/ui/blind/blind-item-item-shadow.stderr index 08f5f5b47ed49..7d13fbea37ba5 100644 --- a/tests/ui/blind/blind-item-item-shadow.stderr +++ b/tests/ui/blind/blind-item-item-shadow.stderr @@ -1,5 +1,5 @@ error[E0255]: the name `foo` is defined multiple times - --> $DIR/blind-item-item-shadow.rs:3:5 + --> $DIR/blind-item-item-shadow.rs:5:5 | LL | mod foo { pub mod foo { } } | ------- previous definition of the module `foo` here diff --git a/tests/ui/block-result/block-must-not-have-result-while.rs b/tests/ui/block-result/block-must-not-have-result-while.rs index 418059bf280ea..ada4e1778a562 100644 --- a/tests/ui/block-result/block-must-not-have-result-while.rs +++ b/tests/ui/block-result/block-must-not-have-result-while.rs @@ -1,6 +1,8 @@ +//@ dont-require-annotations: NOTE + fn main() { while true { //~ WARN denote infinite loops with true //~ ERROR mismatched types - //~| expected `()`, found `bool` + //~| NOTE expected `()`, found `bool` } } diff --git a/tests/ui/block-result/block-must-not-have-result-while.stderr b/tests/ui/block-result/block-must-not-have-result-while.stderr index 94a4b33da5dda..0b9941ea96efb 100644 --- a/tests/ui/block-result/block-must-not-have-result-while.stderr +++ b/tests/ui/block-result/block-must-not-have-result-while.stderr @@ -1,5 +1,5 @@ warning: denote infinite loops with `loop { ... }` - --> $DIR/block-must-not-have-result-while.rs:2:5 + --> $DIR/block-must-not-have-result-while.rs:4:5 | LL | while true { | ^^^^^^^^^^ help: use `loop` @@ -7,7 +7,7 @@ LL | while true { = note: `#[warn(while_true)]` on by default error[E0308]: mismatched types - --> $DIR/block-must-not-have-result-while.rs:3:9 + --> $DIR/block-must-not-have-result-while.rs:5:9 | LL | / while true { LL | | true diff --git a/tests/ui/block-result/issue-13624.rs b/tests/ui/block-result/issue-13624.rs index 8f93e5a356fa6..f9f7c33e4119a 100644 --- a/tests/ui/block-result/issue-13624.rs +++ b/tests/ui/block-result/issue-13624.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + mod a { pub enum Enum { EnumStructVariant { x: u8, y: u8, z: u8 } @@ -6,7 +8,7 @@ mod a { pub fn get_enum_struct_variant() -> () { Enum::EnumStructVariant { x: 1, y: 2, z: 3 } //~^ ERROR mismatched types - //~| expected `()`, found `Enum` + //~| NOTE expected `()`, found `Enum` } } @@ -19,7 +21,7 @@ mod b { match enum_struct_variant { a::Enum::EnumStructVariant { x, y, z } => { //~^ ERROR mismatched types - //~| expected `()`, found `Enum` + //~| NOTE expected `()`, found `Enum` } } } diff --git a/tests/ui/block-result/issue-13624.stderr b/tests/ui/block-result/issue-13624.stderr index d41bd057f8246..16f5233a0410b 100644 --- a/tests/ui/block-result/issue-13624.stderr +++ b/tests/ui/block-result/issue-13624.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-13624.rs:7:5 + --> $DIR/issue-13624.rs:9:5 | LL | pub fn get_enum_struct_variant() -> () { | -- expected `()` because of return type @@ -7,7 +7,7 @@ LL | Enum::EnumStructVariant { x: 1, y: 2, z: 3 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `Enum` error[E0308]: mismatched types - --> $DIR/issue-13624.rs:20:9 + --> $DIR/issue-13624.rs:22:9 | LL | match enum_struct_variant { | ------------------- this expression has type `()` diff --git a/tests/ui/block-result/issue-5500.rs b/tests/ui/block-result/issue-5500.rs index de7fd39a20caa..cbad6a27c1dc3 100644 --- a/tests/ui/block-result/issue-5500.rs +++ b/tests/ui/block-result/issue-5500.rs @@ -1,7 +1,7 @@ -fn main() { +fn main() { //~ NOTE expected `()` because of default return type &panic!() //~^ ERROR mismatched types - //~| expected unit type `()` - //~| found reference `&_` - //~| expected `()`, found `&_` + //~| NOTE expected unit type `()` + //~| NOTE found reference `&_` + //~| NOTE expected `()`, found `&_` } diff --git a/tests/ui/bootstrap/rustc_bootstrap.rs b/tests/ui/bootstrap/rustc_bootstrap.rs index daa28e0cdf29e..fb72bba95a158 100644 --- a/tests/ui/bootstrap/rustc_bootstrap.rs +++ b/tests/ui/bootstrap/rustc_bootstrap.rs @@ -38,10 +38,11 @@ // also affected by `RUSTC_BOOTSTRAP`. //@[force_stable] rustc-env:RUSTC_BOOTSTRAP=-1 //@[force_stable] compile-flags: -Z unstable-options -//@[force_stable] regex-error-pattern: error: the option `Z` is only accepted on the nightly compiler #![crate_type = "lib"] // Note: `rustc_attrs` is a perma-unstable internal feature that is unlikely to change, which is // used as a proxy to check `RUSTC_BOOTSTRAP` versus stability checking logic. #![feature(rustc_attrs)] + +//[force_stable]~? RAW the option `Z` is only accepted on the nightly compiler diff --git a/tests/ui/bootstrap/self-test/a.rs b/tests/ui/bootstrap/self-test/a.rs index 64d2d6f11bbdd..0818665af9f23 100644 --- a/tests/ui/bootstrap/self-test/a.rs +++ b/tests/ui/bootstrap/self-test/a.rs @@ -1,2 +1 @@ //! Not used by compiler, this is used by bootstrap cli self-test. -//@ ignore-test diff --git a/tests/ui/bootstrap/self-test/b.rs b/tests/ui/bootstrap/self-test/b.rs index 91f92f67910b7..aeb4688830dba 100644 --- a/tests/ui/bootstrap/self-test/b.rs +++ b/tests/ui/bootstrap/self-test/b.rs @@ -1,2 +1 @@ //! Not used by compiler, used by bootstrap cli self-test. -//@ ignore-test diff --git a/src/tools/miri/test-cargo-miri/test.stdout-empty.ref b/tests/ui/bootstrap/self-test/compiletest-ignore-dir similarity index 100% rename from src/tools/miri/test-cargo-miri/test.stdout-empty.ref rename to tests/ui/bootstrap/self-test/compiletest-ignore-dir diff --git a/tests/ui/borrowck/augmented-assignments.rs b/tests/ui/borrowck/augmented-assignments.rs new file mode 100644 index 0000000000000..d717dcc7935ed --- /dev/null +++ b/tests/ui/borrowck/augmented-assignments.rs @@ -0,0 +1,31 @@ +//! Check that overloaded compound assignment operators respect usual borrowck rules and emit +//! reasonable diagnostics. + +use std::ops::AddAssign; + +#[derive(Clone)] +struct Int(i32); + +impl AddAssign for Int { + fn add_assign(&mut self, _: Int) { + unimplemented!() + } +} + +fn main() { + let mut x = Int(1); //~ NOTE binding `x` declared here + x + //~^ NOTE borrow of `x` occurs here + += + x; + //~^ ERROR cannot move out of `x` because it is borrowed + //~| NOTE move out of `x` occurs here + + let y = Int(2); + //~^ HELP consider changing this to be mutable + //~| SUGGESTION mut + y //~ ERROR cannot borrow `y` as mutable, as it is not declared as mutable + //~| NOTE cannot borrow as mutable + += + Int(1); +} diff --git a/tests/ui/augmented-assignments.stderr b/tests/ui/borrowck/augmented-assignments.stderr similarity index 88% rename from tests/ui/augmented-assignments.stderr rename to tests/ui/borrowck/augmented-assignments.stderr index a4b75cbf6e8fc..4b945cd998a14 100644 --- a/tests/ui/augmented-assignments.stderr +++ b/tests/ui/borrowck/augmented-assignments.stderr @@ -1,5 +1,5 @@ error[E0505]: cannot move out of `x` because it is borrowed - --> $DIR/augmented-assignments.rs:17:5 + --> $DIR/augmented-assignments.rs:20:5 | LL | let mut x = Int(1); | ----- binding `x` declared here @@ -10,7 +10,7 @@ LL | x; | ^ move out of `x` occurs here error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable - --> $DIR/augmented-assignments.rs:24:5 + --> $DIR/augmented-assignments.rs:27:5 | LL | y | ^ cannot borrow as mutable diff --git a/tests/ui/borrowck/borrowck-fn-in-const-c.rs b/tests/ui/borrowck/borrowck-fn-in-const-c.rs index c638cd08bc9ae..3cabedfe994c4 100644 --- a/tests/ui/borrowck/borrowck-fn-in-const-c.rs +++ b/tests/ui/borrowck/borrowck-fn-in-const-c.rs @@ -14,7 +14,7 @@ impl Drop for DropString { const LOCAL_REF: fn() -> &'static str = { fn broken() -> &'static str { let local = DropString { inner: format!("Some local string") }; - return &local.inner; //~ borrow may still be in use when destructor runs + return &local.inner; //~ ERROR borrow may still be in use when destructor runs } broken }; diff --git a/tests/ui/borrowck/borrowck-report-with-custom-diagnostic.rs b/tests/ui/borrowck/borrowck-report-with-custom-diagnostic.rs index 6f323d9122753..caa312edd7168 100644 --- a/tests/ui/borrowck/borrowck-report-with-custom-diagnostic.rs +++ b/tests/ui/borrowck/borrowck-report-with-custom-diagnostic.rs @@ -1,12 +1,13 @@ -#![feature(rustc_attrs)] +//@ dont-require-annotations: NOTE + #![allow(dead_code)] -fn main() { #![rustc_error] // rust-lang/rust#49855 +fn main() { // Original borrow ends at end of function let mut x = 1; let y = &mut x; - //~^ mutable borrow occurs here + //~^ NOTE mutable borrow occurs here let z = &x; //~ ERROR cannot borrow - //~^ immutable borrow occurs here + //~^ NOTE immutable borrow occurs here z.use_ref(); y.use_mut(); } @@ -17,9 +18,9 @@ fn foo() { // Original borrow ends at end of match arm let mut x = 1; let y = &x; - //~^ immutable borrow occurs here + //~^ NOTE immutable borrow occurs here let z = &mut x; //~ ERROR cannot borrow - //~^ mutable borrow occurs here + //~^ NOTE mutable borrow occurs here z.use_mut(); y.use_ref(); } @@ -32,9 +33,9 @@ fn bar() { || { let mut x = 1; let y = &mut x; - //~^ first mutable borrow occurs here + //~^ NOTE first mutable borrow occurs here let z = &mut x; //~ ERROR cannot borrow - //~^ second mutable borrow occurs here + //~^ NOTE second mutable borrow occurs here z.use_mut(); y.use_mut(); }; diff --git a/tests/ui/borrowck/borrowck-report-with-custom-diagnostic.stderr b/tests/ui/borrowck/borrowck-report-with-custom-diagnostic.stderr index db73d4c04acc8..e2474737aad4d 100644 --- a/tests/ui/borrowck/borrowck-report-with-custom-diagnostic.stderr +++ b/tests/ui/borrowck/borrowck-report-with-custom-diagnostic.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-report-with-custom-diagnostic.rs:8:13 + --> $DIR/borrowck-report-with-custom-diagnostic.rs:9:13 | LL | let y = &mut x; | ------ mutable borrow occurs here @@ -11,7 +11,7 @@ LL | y.use_mut(); | - mutable borrow later used here error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-report-with-custom-diagnostic.rs:21:21 + --> $DIR/borrowck-report-with-custom-diagnostic.rs:22:21 | LL | let y = &x; | -- immutable borrow occurs here @@ -23,7 +23,7 @@ LL | y.use_ref(); | - immutable borrow later used here error[E0499]: cannot borrow `x` as mutable more than once at a time - --> $DIR/borrowck-report-with-custom-diagnostic.rs:36:17 + --> $DIR/borrowck-report-with-custom-diagnostic.rs:37:17 | LL | let y = &mut x; | ------ first mutable borrow occurs here diff --git a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs index b09c96ada8ab6..5106ba6c86cd4 100644 --- a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs +++ b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs @@ -17,7 +17,7 @@ impl Foo { fn main() { unsafe { let sfoo: *mut Foo = &mut SFOO; - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] + //~^ WARN mutable reference to mutable static [static_mut_refs] let x = (*sfoo).x(); (*sfoo).x[1] += 1; *x += 1; diff --git a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr index a392177ffe2f3..4e19fd81735e1 100644 --- a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr +++ b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr @@ -1,4 +1,4 @@ -warning: creating a mutable reference to mutable static is discouraged +warning: creating a mutable reference to mutable static --> $DIR/borrowck-unsafe-static-mutable-borrows.rs:19:30 | LL | let sfoo: *mut Foo = &mut SFOO; diff --git a/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs b/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs index ec074d2cf1c9a..400e5f010ecb2 100644 --- a/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs +++ b/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs @@ -19,7 +19,7 @@ fn b() { let vec: &mut [Box] = &mut vec; match vec { &mut [ref _b @ ..] => { - //~^ `vec[_]` is borrowed here + //~^ NOTE `vec[_]` is borrowed here vec[0] = Box::new(4); //~ ERROR cannot assign //~^ NOTE `vec[_]` is assigned to here _b.use_ref(); diff --git a/tests/ui/borrowck/ice-mutability-error-slicing-121807.stderr b/tests/ui/borrowck/ice-mutability-error-slicing-121807.stderr index 3a6b8008fceb3..02d5231f7134d 100644 --- a/tests/ui/borrowck/ice-mutability-error-slicing-121807.stderr +++ b/tests/ui/borrowck/ice-mutability-error-slicing-121807.stderr @@ -23,12 +23,6 @@ LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; = note: for more information, see issue #41686 = note: `#[warn(anonymous_parameters)]` on by default -error[E0220]: associated type `Assoc` not found for `Self` - --> $DIR/ice-mutability-error-slicing-121807.rs:7:36 - | -LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; - | ^^^^^ associated type `Assoc` not found - error[E0185]: method `read_dword` has a `&self` declaration in the impl, but not in the trait --> $DIR/ice-mutability-error-slicing-121807.rs:17:5 | @@ -47,6 +41,12 @@ LL | extern "C" fn read_word(&mut self) -> u8; LL | impl MemoryUnit for ROM { | ^^^^^^^^^^^^^^^^^^^^^^^ missing `read_word` in implementation +error[E0220]: associated type `Assoc` not found for `Self` + --> $DIR/ice-mutability-error-slicing-121807.rs:7:36 + | +LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + | ^^^^^ associated type `Assoc` not found + error: aborting due to 4 previous errors; 1 warning emitted Some errors have detailed explanations: E0046, E0185, E0220, E0261. diff --git a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr b/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr index 3ee89d9734a0f..30f292f71a280 100644 --- a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr +++ b/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr @@ -11,7 +11,6 @@ LL | bar(x); | = note: the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used = note: to learn more, visit - = note: this error originates in the macro `format_args` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0716]: temporary value dropped while borrowed --> $DIR/issue-114374-invalid-help-fmt-args.rs:10:15 @@ -26,7 +25,6 @@ LL | bar(foo); | = note: the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used = note: to learn more, visit - = note: this error originates in the macro `format_args` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/issue-36082.fixed b/tests/ui/borrowck/issue-36082.fixed index 2209c56048e45..9ac89b016071d 100644 --- a/tests/ui/borrowck/issue-36082.fixed +++ b/tests/ui/borrowck/issue-36082.fixed @@ -13,5 +13,5 @@ fn main() { //~| NOTE creates a temporary value which is freed while still in use //~| HELP consider using a `let` binding to create a longer lived value println!("{}", val); - //~^ borrow later used here + //~^ NOTE borrow later used here } diff --git a/tests/ui/borrowck/issue-36082.rs b/tests/ui/borrowck/issue-36082.rs index da8b0068882b5..f2f769ea1c3ae 100644 --- a/tests/ui/borrowck/issue-36082.rs +++ b/tests/ui/borrowck/issue-36082.rs @@ -12,5 +12,5 @@ fn main() { //~| NOTE creates a temporary value which is freed while still in use //~| HELP consider using a `let` binding to create a longer lived value println!("{}", val); - //~^ borrow later used here + //~^ NOTE borrow later used here } diff --git a/tests/ui/borrowck/issue-81899.rs b/tests/ui/borrowck/issue-81899.rs index 380c03751f504..11755620d864e 100644 --- a/tests/ui/borrowck/issue-81899.rs +++ b/tests/ui/borrowck/issue-81899.rs @@ -1,14 +1,16 @@ // Regression test for #81899. // The `panic!()` below is important to trigger the fixed ICE. +//@ dont-require-annotations: NOTE + const _CONST: &[u8] = &f(&[], |_| {}); //~ ERROR evaluation of constant value failed -//~^ constant +//~^ NOTE constant const fn f(_: &[u8], _: F) -> &[u8] where F: FnMut(&u8), { - panic!() //~ inside `f + panic!() //~ NOTE inside `f } fn main() {} diff --git a/tests/ui/borrowck/issue-81899.stderr b/tests/ui/borrowck/issue-81899.stderr index 2e6e7511ec916..97d463cb6a774 100644 --- a/tests/ui/borrowck/issue-81899.stderr +++ b/tests/ui/borrowck/issue-81899.stderr @@ -1,18 +1,17 @@ error[E0080]: evaluation of constant value failed - --> $DIR/issue-81899.rs:4:24 + --> $DIR/issue-81899.rs:6:24 | LL | const _CONST: &[u8] = &f(&[], |_| {}); | ^^^^^^^^^^^^^^ evaluation panicked: explicit panic | -note: inside `f::<{closure@$DIR/issue-81899.rs:4:31: 4:34}>` - --> $DIR/issue-81899.rs:11:5 +note: inside `f::<{closure@$DIR/issue-81899.rs:6:31: 6:34}>` + --> $DIR/issue-81899.rs:13:5 | LL | panic!() | ^^^^^^^^ the failure occurred here - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/issue-81899.rs:4:23 + --> $DIR/issue-81899.rs:6:23 | LL | const _CONST: &[u8] = &f(&[], |_| {}); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/borrowck/issue-88434-minimal-example.rs b/tests/ui/borrowck/issue-88434-minimal-example.rs index ebaa9a92273b7..7482b3fd612c1 100644 --- a/tests/ui/borrowck/issue-88434-minimal-example.rs +++ b/tests/ui/borrowck/issue-88434-minimal-example.rs @@ -1,13 +1,15 @@ // Regression test related to issue 88434 +//@ dont-require-annotations: NOTE + const _CONST: &() = &f(&|_| {}); //~ ERROR evaluation of constant value failed -//~^ constant +//~^ NOTE constant const fn f(_: &F) where F: FnMut(&u8), { - panic!() //~ inside `f + panic!() //~ NOTE inside `f } fn main() { } diff --git a/tests/ui/borrowck/issue-88434-minimal-example.stderr b/tests/ui/borrowck/issue-88434-minimal-example.stderr index c8ac13a3473d2..4c525b9ea2c7a 100644 --- a/tests/ui/borrowck/issue-88434-minimal-example.stderr +++ b/tests/ui/borrowck/issue-88434-minimal-example.stderr @@ -1,18 +1,17 @@ error[E0080]: evaluation of constant value failed - --> $DIR/issue-88434-minimal-example.rs:3:22 + --> $DIR/issue-88434-minimal-example.rs:5:22 | LL | const _CONST: &() = &f(&|_| {}); | ^^^^^^^^^^ evaluation panicked: explicit panic | -note: inside `f::<{closure@$DIR/issue-88434-minimal-example.rs:3:25: 3:28}>` - --> $DIR/issue-88434-minimal-example.rs:10:5 +note: inside `f::<{closure@$DIR/issue-88434-minimal-example.rs:5:25: 5:28}>` + --> $DIR/issue-88434-minimal-example.rs:12:5 | LL | panic!() | ^^^^^^^^ the failure occurred here - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/issue-88434-minimal-example.rs:3:21 + --> $DIR/issue-88434-minimal-example.rs:5:21 | LL | const _CONST: &() = &f(&|_| {}); | ^^^^^^^^^^^ diff --git a/tests/ui/borrowck/issue-88434-removal-index-should-be-less.rs b/tests/ui/borrowck/issue-88434-removal-index-should-be-less.rs index 8d042630424b8..09b1f59c449ca 100644 --- a/tests/ui/borrowck/issue-88434-removal-index-should-be-less.rs +++ b/tests/ui/borrowck/issue-88434-removal-index-should-be-less.rs @@ -1,13 +1,15 @@ // Regression test for issue 88434 +//@ dont-require-annotations: NOTE + const _CONST: &[u8] = &f(&[], |_| {}); //~ ERROR evaluation of constant value failed -//~^ constant +//~^ NOTE constant const fn f(_: &[u8], _: F) -> &[u8] where F: FnMut(&u8), { - panic!() //~ inside `f + panic!() //~ NOTE inside `f } fn main() { } diff --git a/tests/ui/borrowck/issue-88434-removal-index-should-be-less.stderr b/tests/ui/borrowck/issue-88434-removal-index-should-be-less.stderr index 0041759609ca5..a22621c9c1bb7 100644 --- a/tests/ui/borrowck/issue-88434-removal-index-should-be-less.stderr +++ b/tests/ui/borrowck/issue-88434-removal-index-should-be-less.stderr @@ -1,18 +1,17 @@ error[E0080]: evaluation of constant value failed - --> $DIR/issue-88434-removal-index-should-be-less.rs:3:24 + --> $DIR/issue-88434-removal-index-should-be-less.rs:5:24 | LL | const _CONST: &[u8] = &f(&[], |_| {}); | ^^^^^^^^^^^^^^ evaluation panicked: explicit panic | -note: inside `f::<{closure@$DIR/issue-88434-removal-index-should-be-less.rs:3:31: 3:34}>` - --> $DIR/issue-88434-removal-index-should-be-less.rs:10:5 +note: inside `f::<{closure@$DIR/issue-88434-removal-index-should-be-less.rs:5:31: 5:34}>` + --> $DIR/issue-88434-removal-index-should-be-less.rs:12:5 | LL | panic!() | ^^^^^^^^ the failure occurred here - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/issue-88434-removal-index-should-be-less.rs:3:23 + --> $DIR/issue-88434-removal-index-should-be-less.rs:5:23 | LL | const _CONST: &[u8] = &f(&[], |_| {}); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/borrowck/move-error-snippets-ext.rs b/tests/ui/borrowck/move-error-snippets-ext.rs index f8103228cf81c..6dd68438f179c 100644 --- a/tests/ui/borrowck/move-error-snippets-ext.rs +++ b/tests/ui/borrowck/move-error-snippets-ext.rs @@ -1,4 +1,4 @@ -//@ ignore-test (auxiliary, used by other tests) +//@ ignore-auxiliary (used by `./move-error-snippets.rs`) macro_rules! aaa { ($c:ident) => {{ diff --git a/tests/ui/borrowck/mut-borrow-in-loop-2.stderr b/tests/ui/borrowck/mut-borrow-in-loop-2.stderr index 4f32df1eb24e2..46e06bc91b91c 100644 --- a/tests/ui/borrowck/mut-borrow-in-loop-2.stderr +++ b/tests/ui/borrowck/mut-borrow-in-loop-2.stderr @@ -12,10 +12,6 @@ help: consider creating a fresh reborrow of `value` here | LL | Other::handle(&mut *value); | ++++++ -help: consider creating a fresh reborrow of `value` here - | -LL | Other::handle(&mut *value); - | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/mut-borrow-outside-loop.rs b/tests/ui/borrowck/mut-borrow-outside-loop.rs index c02bfbf873907..3cc362def38f3 100644 --- a/tests/ui/borrowck/mut-borrow-outside-loop.rs +++ b/tests/ui/borrowck/mut-borrow-outside-loop.rs @@ -1,6 +1,6 @@ // ensure borrowck messages are correct outside special case -#![feature(rustc_attrs)] -fn main() { #![rustc_error] // rust-lang/rust#49855 + +fn main() { let mut void = (); let first = &mut void; diff --git a/tests/ui/borrowck/overwrite-anon-late-param-regions.rs b/tests/ui/borrowck/overwrite-anon-late-param-regions.rs index 7b0f784068f60..8e0e005cd781d 100644 --- a/tests/ui/borrowck/overwrite-anon-late-param-regions.rs +++ b/tests/ui/borrowck/overwrite-anon-late-param-regions.rs @@ -6,6 +6,7 @@ #![feature(type_alias_impl_trait)] type Opaque2<'a> = impl Sized + 'a; +#[define_opaque(Opaque2)] fn test2() -> impl for<'a, 'b> Fn((&'a str, &'b str)) -> (Opaque2<'a>, Opaque2<'a>) { |x| x //~^ ERROR lifetime may not live long enough diff --git a/tests/ui/borrowck/overwrite-anon-late-param-regions.stderr b/tests/ui/borrowck/overwrite-anon-late-param-regions.stderr index c5b7284271e46..96b3ebf1eb490 100644 --- a/tests/ui/borrowck/overwrite-anon-late-param-regions.stderr +++ b/tests/ui/borrowck/overwrite-anon-late-param-regions.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/overwrite-anon-late-param-regions.rs:10:9 + --> $DIR/overwrite-anon-late-param-regions.rs:11:9 | LL | |x| x | - ^ closure was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` @@ -8,7 +8,7 @@ LL | |x| x | has type `(&'2 str, &str)` error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/overwrite-anon-late-param-regions.rs:10:5 + --> $DIR/overwrite-anon-late-param-regions.rs:11:5 | LL | type Opaque2<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter diff --git a/tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs b/tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs index 141ad5bd2c482..7c0378e068be4 100644 --- a/tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs +++ b/tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs @@ -17,11 +17,11 @@ pub trait Foo<'a, 't> { impl<'a, 't> Foo<'a, 't> for &'a isize { fn no_bound<'b:'a>(self, b: Inv<'b>) { - //~^ ERROR lifetime parameters or bounds on method `no_bound` do not match + //~^ ERROR lifetime parameters do not match the trait definition } fn has_bound<'b>(self, b: Inv<'b>) { - //~^ ERROR lifetime parameters or bounds on method `has_bound` do not match + //~^ ERROR lifetime parameters do not match the trait definition } fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) { @@ -40,7 +40,7 @@ impl<'a, 't> Foo<'a, 't> for &'a isize { } fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) { - //~^ ERROR lifetime parameters or bounds on method `wrong_bound2` do not match the trait + //~^ ERROR lifetime parameters do not match the trait definition } fn okay_bound<'b,'c,'e:'b+'c>(self, b: Inv<'b>, c: Inv<'c>, e: Inv<'e>) { diff --git a/tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr b/tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr index 5f0347bdb4d49..207ca57af38b5 100644 --- a/tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr +++ b/tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr @@ -1,20 +1,48 @@ -error[E0195]: lifetime parameters or bounds on method `no_bound` do not match the trait declaration - --> $DIR/regions-bound-missing-bound-in-impl.rs:19:16 +error[E0195]: lifetime parameters do not match the trait definition + --> $DIR/regions-bound-missing-bound-in-impl.rs:19:17 | +LL | fn no_bound<'b:'a>(self, b: Inv<'b>) { + | ^^ + | + = note: lifetime parameters differ in whether they are early- or late-bound +note: `'b` differs between the trait and impl + --> $DIR/regions-bound-missing-bound-in-impl.rs:10:17 + | +LL | pub trait Foo<'a, 't> { + | --------------------- in this trait... LL | fn no_bound<'b>(self, b: Inv<'b>); - | ---- lifetimes in impl do not match this method in trait + | ^^ `'b` is late-bound ... +LL | impl<'a, 't> Foo<'a, 't> for &'a isize { + | -------------------------------------- in this impl... LL | fn no_bound<'b:'a>(self, b: Inv<'b>) { - | ^^^^^^^ lifetimes do not match method in trait + | ^^ -- this lifetime bound makes `'b` early-bound + | | + | `'b` is early-bound -error[E0195]: lifetime parameters or bounds on method `has_bound` do not match the trait declaration - --> $DIR/regions-bound-missing-bound-in-impl.rs:23:17 +error[E0195]: lifetime parameters do not match the trait definition + --> $DIR/regions-bound-missing-bound-in-impl.rs:23:18 + | +LL | fn has_bound<'b>(self, b: Inv<'b>) { + | ^^ | + = note: lifetime parameters differ in whether they are early- or late-bound +note: `'b` differs between the trait and impl + --> $DIR/regions-bound-missing-bound-in-impl.rs:11:18 + | +LL | pub trait Foo<'a, 't> { + | --------------------- in this trait... +LL | fn no_bound<'b>(self, b: Inv<'b>); LL | fn has_bound<'b:'a>(self, b: Inv<'b>); - | ------- lifetimes in impl do not match this method in trait + | ^^ -- this lifetime bound makes `'b` early-bound + | | + | `'b` is early-bound +... +LL | impl<'a, 't> Foo<'a, 't> for &'a isize { + | -------------------------------------- in this impl... ... LL | fn has_bound<'b>(self, b: Inv<'b>) { - | ^^^^ lifetimes do not match method in trait + | ^^ `'b` is late-bound error[E0308]: method not compatible with trait --> $DIR/regions-bound-missing-bound-in-impl.rs:27:5 @@ -54,14 +82,45 @@ note: ...does not necessarily outlive the lifetime `'c` as defined here LL | fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) { | ^^ -error[E0195]: lifetime parameters or bounds on method `wrong_bound2` do not match the trait declaration - --> $DIR/regions-bound-missing-bound-in-impl.rs:42:20 +error[E0195]: lifetime parameters do not match the trait definition + --> $DIR/regions-bound-missing-bound-in-impl.rs:42:30 + | +LL | fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) { + | ^^^ ^^^ | + = note: lifetime parameters differ in whether they are early- or late-bound +note: `'_` differs between the trait and impl + --> $DIR/regions-bound-missing-bound-in-impl.rs:13:21 + | +LL | pub trait Foo<'a, 't> { + | --------------------- in this trait... +... LL | fn wrong_bound2<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>); - | ---------------- lifetimes in impl do not match this method in trait + | ^^ -- this lifetime bound makes `'b` early-bound + | | + | `'b` is early-bound +... +LL | impl<'a, 't> Foo<'a, 't> for &'a isize { + | -------------------------------------- in this impl... +... +LL | fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) { + | ^^^ `'_` is late-bound +note: `'_` differs between the trait and impl + --> $DIR/regions-bound-missing-bound-in-impl.rs:13:27 + | +LL | pub trait Foo<'a, 't> { + | --------------------- in this trait... +... +LL | fn wrong_bound2<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>); + | ^^ -- this lifetime bound makes `'d` early-bound + | | + | `'d` is early-bound +... +LL | impl<'a, 't> Foo<'a, 't> for &'a isize { + | -------------------------------------- in this impl... ... LL | fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) { - | ^ lifetimes do not match method in trait + | ^^^ `'_` is late-bound error[E0276]: impl has stricter requirements than trait --> $DIR/regions-bound-missing-bound-in-impl.rs:49:26 diff --git a/tests/ui/borrowck/static-trait-bound-lost.rs b/tests/ui/borrowck/static-trait-bound-lost.rs new file mode 100644 index 0000000000000..0288acea0f604 --- /dev/null +++ b/tests/ui/borrowck/static-trait-bound-lost.rs @@ -0,0 +1,54 @@ +// This test is a reduced version of a bug introduced during work on type-tests for Polonius. +// The underlying problem is that the 'static bound is lost for a type parameter that is +// threaded deeply enough, causing an error. +// The bug was first observed in exr-1.4.1/src/image/read/mod.rs:124:5 during perf test. + +//@ check-pass + +use std::marker::PhantomData; + +struct ReadAllLayers { + px: PhantomData, +} + +trait ReadLayers<'s> {} + +impl<'s, C> ReadLayers<'s> for ReadAllLayers where C: ReadChannels<'s> {} + +fn make_builder( + _: Set, +) -> ReadAllLayers> +where + Set: Fn(&mut Pixels), +{ + todo!() +} + +struct CollectPixels { + px: PhantomData<(SetPixel, Pixel, PixelStorage)>, +} + +impl<'s, PixelStorage, SetPixel: 's> ReadChannels<'s> + for CollectPixels +where + SetPixel: Fn(&mut PixelStorage), +{ +} + +trait ReadChannels<'s> {} + +fn from_file(_: L) +where + for<'s> L: ReadLayers<'s>, +{ +} + +pub fn read_all_rgba_layers_from_file( + set_pixel: Set, +) where + Set: Fn(&mut Pixels), +{ + from_file(make_builder(set_pixel)); // Error triggered. +} + +pub fn main() {} diff --git a/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr b/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr index 190ddeaa8f285..a656bb67bcba0 100644 --- a/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr +++ b/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr @@ -8,12 +8,6 @@ LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; = note: for more information, see issue #41686 = note: `#[warn(anonymous_parameters)]` on by default -error[E0220]: associated type `Assoc` not found for `Self` - --> $DIR/trait-impl-argument-difference-ice.rs:4:36 - | -LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; - | ^^^^^ associated type `Assoc` not found - error[E0185]: method `read_dword` has a `&self` declaration in the impl, but not in the trait --> $DIR/trait-impl-argument-difference-ice.rs:14:5 | @@ -32,6 +26,12 @@ LL | extern "C" fn read_word(&mut self) -> u8; LL | impl MemoryUnit for ROM { | ^^^^^^^^^^^^^^^^^^^^^^^ missing `read_word` in implementation +error[E0220]: associated type `Assoc` not found for `Self` + --> $DIR/trait-impl-argument-difference-ice.rs:4:36 + | +LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + | ^^^^^ associated type `Assoc` not found + error[E0596]: cannot borrow `*self` as mutable, as it is behind a `&` reference --> $DIR/trait-impl-argument-difference-ice.rs:16:19 | diff --git a/tests/ui/borrowck/two-phase-multi-mut.rs b/tests/ui/borrowck/two-phase-multi-mut.rs index bb646d7caf1e2..8ff924ccc2a89 100644 --- a/tests/ui/borrowck/two-phase-multi-mut.rs +++ b/tests/ui/borrowck/two-phase-multi-mut.rs @@ -9,6 +9,6 @@ impl Foo { fn main() { let mut foo = Foo { }; foo.method(&mut foo); - //~^ cannot borrow `foo` as mutable more than once at a time - //~^^ cannot borrow `foo` as mutable more than once at a time + //~^ ERROR cannot borrow `foo` as mutable more than once at a time + //~^^ ERROR cannot borrow `foo` as mutable more than once at a time } diff --git a/tests/ui/writing-to-immutable-vec.rs b/tests/ui/borrowck/writing-to-immutable-vec.rs similarity index 100% rename from tests/ui/writing-to-immutable-vec.rs rename to tests/ui/borrowck/writing-to-immutable-vec.rs diff --git a/tests/ui/writing-to-immutable-vec.stderr b/tests/ui/borrowck/writing-to-immutable-vec.stderr similarity index 100% rename from tests/ui/writing-to-immutable-vec.stderr rename to tests/ui/borrowck/writing-to-immutable-vec.stderr diff --git a/tests/ui/box/suggest-box-for-expr-field-issue-139631.rs b/tests/ui/box/suggest-box-for-expr-field-issue-139631.rs new file mode 100644 index 0000000000000..8d040da1ef7cc --- /dev/null +++ b/tests/ui/box/suggest-box-for-expr-field-issue-139631.rs @@ -0,0 +1,14 @@ +struct X { + a: Box, +} + +struct Y { + y: Box, +} + +fn main() { + let a = 8; + let v2 = X { a }; //~ ERROR mismatched types [E0308] + let v3 = Y { y: a }; //~ ERROR mismatched types [E0308] + let v4 = Y { a }; //~ ERROR struct `Y` has no field named `a` [E0560] +} diff --git a/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr b/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr new file mode 100644 index 0000000000000..01bd0523a162d --- /dev/null +++ b/tests/ui/box/suggest-box-for-expr-field-issue-139631.stderr @@ -0,0 +1,44 @@ +error[E0308]: mismatched types + --> $DIR/suggest-box-for-expr-field-issue-139631.rs:11:18 + | +LL | let v2 = X { a }; + | ^ expected `Box`, found integer + | + = note: expected struct `Box` + 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 +help: store this in the heap by calling `Box::new` + | +LL | let v2 = X { a: Box::new(a) }; + | ++++++++++++ + + +error[E0308]: mismatched types + --> $DIR/suggest-box-for-expr-field-issue-139631.rs:12:21 + | +LL | let v3 = Y { y: a }; + | ^ expected `Box`, found integer + | + = note: expected struct `Box` + 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 +help: store this in the heap by calling `Box::new` + | +LL | let v3 = Y { y: Box::new(a) }; + | +++++++++ + + +error[E0560]: struct `Y` has no field named `a` + --> $DIR/suggest-box-for-expr-field-issue-139631.rs:13:18 + | +LL | let v4 = Y { a }; + | ^ unknown field + | +help: a field with a similar name exists + | +LL - let v4 = Y { a }; +LL + let v4 = Y { y }; + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0560. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/c-variadic/issue-86053-1.rs b/tests/ui/c-variadic/issue-86053-1.rs index f952235be9811..8eeb99a4cc19c 100644 --- a/tests/ui/c-variadic/issue-86053-1.rs +++ b/tests/ui/c-variadic/issue-86053-1.rs @@ -1,12 +1,18 @@ // Regression test for the ICE described in issue #86053. -//@ error-pattern:unexpected `self` parameter in function -//@ error-pattern:`...` must be the last argument of a C-variadic function -//@ error-pattern:cannot find type `F` in this scope - #![feature(c_variadic)] #![crate_type="lib"] fn ordering4 < 'a , 'b > ( a : , self , self , self , + //~^ ERROR expected type, found `,` + //~| ERROR unexpected `self` parameter in function + //~| ERROR unexpected `self` parameter in function + //~| ERROR unexpected `self` parameter in function self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { + //~^ ERROR unexpected `self` parameter in function + //~| ERROR unexpected `self` parameter in function + //~| ERROR unexpected `self` parameter in function + //~| ERROR `...` must be the last argument of a C-variadic function + //~| ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg + //~| ERROR cannot find type `F` in this scope } diff --git a/tests/ui/c-variadic/issue-86053-1.stderr b/tests/ui/c-variadic/issue-86053-1.stderr index ce31f0d300f1f..dc323f9a234d6 100644 --- a/tests/ui/c-variadic/issue-86053-1.stderr +++ b/tests/ui/c-variadic/issue-86053-1.stderr @@ -1,23 +1,23 @@ error: expected type, found `,` - --> $DIR/issue-86053-1.rs:10:47 + --> $DIR/issue-86053-1.rs:6:47 | LL | fn ordering4 < 'a , 'b > ( a : , self , self , self , | ^ expected type error: unexpected `self` parameter in function - --> $DIR/issue-86053-1.rs:10:51 + --> $DIR/issue-86053-1.rs:6:51 | LL | fn ordering4 < 'a , 'b > ( a : , self , self , self , | ^^^^ must be the first parameter of an associated function error: unexpected `self` parameter in function - --> $DIR/issue-86053-1.rs:10:58 + --> $DIR/issue-86053-1.rs:6:58 | LL | fn ordering4 < 'a , 'b > ( a : , self , self , self , | ^^^^ must be the first parameter of an associated function error: unexpected `self` parameter in function - --> $DIR/issue-86053-1.rs:10:67 + --> $DIR/issue-86053-1.rs:6:67 | LL | fn ordering4 < 'a , 'b > ( a : , self , self , self , | ^^^^ must be the first parameter of an associated function diff --git a/tests/ui/c-variadic/variadic-ffi-2.rs b/tests/ui/c-variadic/variadic-ffi-2.rs index 99f83f22d17c0..da7bb76fc14bf 100644 --- a/tests/ui/c-variadic/variadic-ffi-2.rs +++ b/tests/ui/c-variadic/variadic-ffi-2.rs @@ -1,8 +1,6 @@ -//@ ignore-arm stdcall isn't supported #![feature(extended_varargs_abi_support)] -#[allow(unsupported_fn_ptr_calling_conventions)] -fn baz(f: extern "stdcall" fn(usize, ...)) { +fn baz(f: extern "Rust" fn(usize, ...)) { //~^ ERROR: C-variadic function must have a compatible calling convention, // like C, cdecl, system, aapcs, win64, sysv64 or efiapi f(22, 44); diff --git a/tests/ui/c-variadic/variadic-ffi-2.stderr b/tests/ui/c-variadic/variadic-ffi-2.stderr index e52de93a92646..9f8dcefdb0338 100644 --- a/tests/ui/c-variadic/variadic-ffi-2.stderr +++ b/tests/ui/c-variadic/variadic-ffi-2.stderr @@ -1,8 +1,8 @@ error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi` - --> $DIR/variadic-ffi-2.rs:5:11 + --> $DIR/variadic-ffi-2.rs:3:11 | -LL | fn baz(f: extern "stdcall" fn(usize, ...)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention +LL | fn baz(f: extern "Rust" fn(usize, ...)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention error: aborting due to 1 previous error diff --git a/tests/ui/capture1.rs b/tests/ui/capture1.rs deleted file mode 100644 index 9bf6532a7d382..0000000000000 --- a/tests/ui/capture1.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ error-pattern: can't capture dynamic environment in a fn item - -fn main() { - let bar: isize = 5; - fn foo() -> isize { return bar; } -} diff --git a/tests/ui/capture1.stderr b/tests/ui/capture1.stderr deleted file mode 100644 index 067b85ba61913..0000000000000 --- a/tests/ui/capture1.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0434]: can't capture dynamic environment in a fn item - --> $DIR/capture1.rs:5:32 - | -LL | fn foo() -> isize { return bar; } - | ^^^ - | - = help: use the `|| { ... }` closure form instead - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0434`. diff --git a/tests/ui/cast/cast-alias-of-array-to-element.rs b/tests/ui/cast/cast-alias-of-array-to-element.rs new file mode 100644 index 0000000000000..124d0e0346f07 --- /dev/null +++ b/tests/ui/cast/cast-alias-of-array-to-element.rs @@ -0,0 +1,22 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for . +// Test that we structually normalize in the hacky `&[T; N] -> *const T` in cast. + +trait Mirror { + type Assoc: ?Sized; +} +impl Mirror for T { + type Assoc = T; +} + +struct W<'a>(&'a <[f32; 0] as Mirror>::Assoc); + +fn foo(x: W<'_>) -> *const f32 { + x.0 as *const f32 +} + +fn main() {} diff --git a/tests/ui/cast/cast-array-issue-138836.rs b/tests/ui/cast/cast-array-issue-138836.rs new file mode 100644 index 0000000000000..3f8098e76fd29 --- /dev/null +++ b/tests/ui/cast/cast-array-issue-138836.rs @@ -0,0 +1,5 @@ +fn main() { + let a: [u8; 3] = [1,2,3]; + let b = &a; + let c = b as *const [u32; 3]; //~ ERROR casting `&[u8; 3]` as `*const [u32; 3]` is invalid +} diff --git a/tests/ui/cast/cast-array-issue-138836.stderr b/tests/ui/cast/cast-array-issue-138836.stderr new file mode 100644 index 0000000000000..309474c29f933 --- /dev/null +++ b/tests/ui/cast/cast-array-issue-138836.stderr @@ -0,0 +1,9 @@ +error[E0606]: casting `&[u8; 3]` as `*const [u32; 3]` is invalid + --> $DIR/cast-array-issue-138836.rs:4:13 + | +LL | let c = b as *const [u32; 3]; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0606`. diff --git a/tests/ui/cast/cast-as-bool.rs b/tests/ui/cast/cast-as-bool.rs index 511a02718fe21..fa9664e572c03 100644 --- a/tests/ui/cast/cast-as-bool.rs +++ b/tests/ui/cast/cast-as-bool.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: SUGGESTION + fn main() { let u = 5 as bool; //~ ERROR cannot cast `i32` as `bool` //~| HELP compare with zero instead diff --git a/tests/ui/cast/cast-as-bool.stderr b/tests/ui/cast/cast-as-bool.stderr index b2c9ae5c6ad61..25377ebebe2b2 100644 --- a/tests/ui/cast/cast-as-bool.stderr +++ b/tests/ui/cast/cast-as-bool.stderr @@ -1,5 +1,5 @@ error[E0054]: cannot cast `i32` as `bool` - --> $DIR/cast-as-bool.rs:2:13 + --> $DIR/cast-as-bool.rs:4:13 | LL | let u = 5 as bool; | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL + let u = 5 != 0; | error[E0054]: cannot cast `i32` as `bool` - --> $DIR/cast-as-bool.rs:6:13 + --> $DIR/cast-as-bool.rs:8:13 | LL | let t = (1 + 2) as bool; | ^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL + let t = (1 + 2) != 0; | error[E0054]: cannot cast `u32` as `bool` - --> $DIR/cast-as-bool.rs:10:13 + --> $DIR/cast-as-bool.rs:12:13 | LL | let _ = 5_u32 as bool; | ^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL + let _ = 5_u32 != 0; | error[E0054]: cannot cast `f64` as `bool` - --> $DIR/cast-as-bool.rs:13:13 + --> $DIR/cast-as-bool.rs:15:13 | LL | let _ = 64.0_f64 as bool; | ^^^^^^^^^^^^^^^^ @@ -47,43 +47,43 @@ LL + let _ = 64.0_f64 != 0; | error[E0054]: cannot cast `IntEnum` as `bool` - --> $DIR/cast-as-bool.rs:24:13 + --> $DIR/cast-as-bool.rs:26:13 | LL | let _ = IntEnum::One as bool; | ^^^^^^^^^^^^^^^^^^^^ unsupported cast error[E0054]: cannot cast `fn(u8) -> String {uwu}` as `bool` - --> $DIR/cast-as-bool.rs:33:13 + --> $DIR/cast-as-bool.rs:35:13 | LL | let _ = uwu as bool; | ^^^^^^^^^^^ unsupported cast error[E0054]: cannot cast `unsafe fn() {owo}` as `bool` - --> $DIR/cast-as-bool.rs:35:13 + --> $DIR/cast-as-bool.rs:37:13 | LL | let _ = owo as bool; | ^^^^^^^^^^^ unsupported cast error[E0054]: cannot cast `fn(u8) -> String` as `bool` - --> $DIR/cast-as-bool.rs:38:13 + --> $DIR/cast-as-bool.rs:40:13 | LL | let _ = uwu as fn(u8) -> String as bool; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported cast error[E0054]: cannot cast `char` as `bool` - --> $DIR/cast-as-bool.rs:40:13 + --> $DIR/cast-as-bool.rs:42:13 | LL | let _ = 'x' as bool; | ^^^^^^^^^^^ unsupported cast error[E0054]: cannot cast `*const ()` as `bool` - --> $DIR/cast-as-bool.rs:44:13 + --> $DIR/cast-as-bool.rs:46:13 | LL | let _ = ptr as bool; | ^^^^^^^^^^^ unsupported cast error[E0606]: casting `&'static str` as `bool` is invalid - --> $DIR/cast-as-bool.rs:46:13 + --> $DIR/cast-as-bool.rs:48:13 | LL | let v = "hello" as bool; | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/cast/cast-from-nil.rs b/tests/ui/cast/cast-from-nil.rs index 8a677603aa9fd..65e1eb31b21b8 100644 --- a/tests/ui/cast/cast-from-nil.rs +++ b/tests/ui/cast/cast-from-nil.rs @@ -1,2 +1 @@ -//@ error-pattern: non-primitive cast: `()` as `u32` -fn main() { let u = (assert!(true) as u32); } +fn main() { let u = (assert!(true) as u32); } //~ ERROR non-primitive cast: `()` as `u32` diff --git a/tests/ui/cast/cast-from-nil.stderr b/tests/ui/cast/cast-from-nil.stderr index 991ff93a8bd0d..82c53d71e78a0 100644 --- a/tests/ui/cast/cast-from-nil.stderr +++ b/tests/ui/cast/cast-from-nil.stderr @@ -1,5 +1,5 @@ error[E0605]: non-primitive cast: `()` as `u32` - --> $DIR/cast-from-nil.rs:2:21 + --> $DIR/cast-from-nil.rs:1:21 | LL | fn main() { let u = (assert!(true) as u32); } | ^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object diff --git a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs index 5704a33cc870b..78f6dca181852 100644 --- a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs +++ b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs @@ -2,8 +2,6 @@ // Check that you can cast between different pointers to trait objects // whose vtable have the same kind (both lengths, or both trait pointers). -#![feature(unsized_tuple_coercion)] - trait Bar { //~ WARN trait `Bar` is never used fn bar(&self) { println!("Bar!"); } } @@ -19,10 +17,6 @@ fn foo_to_bar(u: *const FooS) -> *const BarS { u as *const BarS } -fn tuple_i32_to_u32(u: *const (i32, T)) -> *const (u32, T) { - u as *const (u32, T) -} - fn main() { let s = FooS([0,1,2]); @@ -31,14 +25,4 @@ fn main() { let bar_ref : *const BarS<[u32]> = foo_to_bar(u); let z : &BarS<[u32]> = unsafe{&*bar_ref}; assert_eq!(&z.0, &[0,1,2]); - - // this assumes that tuple reprs for (i32, _) and (u32, _) are - // the same. - let s = (0i32, [0, 1, 2]); - let u: &(i32, [u8]) = &s; - let u: *const (i32, [u8]) = u; - let u_u32 : *const (u32, [u8]) = tuple_i32_to_u32(u); - unsafe { - assert_eq!(&(*u_u32).1, &[0, 1, 2]); - } } diff --git a/tests/ui/cast/cast-rfc0401-vtable-kinds.stderr b/tests/ui/cast/cast-rfc0401-vtable-kinds.stderr index 4f57e2e7df75e..01277fd632e10 100644 --- a/tests/ui/cast/cast-rfc0401-vtable-kinds.stderr +++ b/tests/ui/cast/cast-rfc0401-vtable-kinds.stderr @@ -1,5 +1,5 @@ warning: trait `Bar` is never used - --> $DIR/cast-rfc0401-vtable-kinds.rs:7:7 + --> $DIR/cast-rfc0401-vtable-kinds.rs:5:7 | LL | trait Bar { | ^^^ diff --git a/tests/ui/cast/cast-to-nil.rs b/tests/ui/cast/cast-to-nil.rs index d91f9a16a07f5..7cd864471dde5 100644 --- a/tests/ui/cast/cast-to-nil.rs +++ b/tests/ui/cast/cast-to-nil.rs @@ -1,2 +1 @@ -//@ error-pattern: non-primitive cast: `u32` as `()` -fn main() { let u = 0u32 as (); } +fn main() { let u = 0u32 as (); } //~ ERROR non-primitive cast: `u32` as `()` diff --git a/tests/ui/cast/cast-to-nil.stderr b/tests/ui/cast/cast-to-nil.stderr index 14c75983b94c2..d7121a7adba7a 100644 --- a/tests/ui/cast/cast-to-nil.stderr +++ b/tests/ui/cast/cast-to-nil.stderr @@ -1,5 +1,5 @@ error[E0605]: non-primitive cast: `u32` as `()` - --> $DIR/cast-to-nil.rs:2:21 + --> $DIR/cast-to-nil.rs:1:21 | LL | fn main() { let u = 0u32 as (); } | ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object diff --git a/tests/ui/cast/cast_lit_suffix-issue-138392.fixed b/tests/ui/cast/cast_lit_suffix-issue-138392.fixed new file mode 100644 index 0000000000000..c6fbd09f89c51 --- /dev/null +++ b/tests/ui/cast/cast_lit_suffix-issue-138392.fixed @@ -0,0 +1,6 @@ +//@ run-rustfix +#![allow(unused_parens)] +fn main() { + let _x: u8 = (4u8); //~ ERROR: mismatched types + let _y: u8 = (4u8); //~ ERROR: mismatched types +} diff --git a/tests/ui/cast/cast_lit_suffix-issue-138392.rs b/tests/ui/cast/cast_lit_suffix-issue-138392.rs new file mode 100644 index 0000000000000..86dbbbbf12628 --- /dev/null +++ b/tests/ui/cast/cast_lit_suffix-issue-138392.rs @@ -0,0 +1,6 @@ +//@ run-rustfix +#![allow(unused_parens)] +fn main() { + let _x: u8 = (4i32); //~ ERROR: mismatched types + let _y: u8 = (4.0f32); //~ ERROR: mismatched types +} diff --git a/tests/ui/cast/cast_lit_suffix-issue-138392.stderr b/tests/ui/cast/cast_lit_suffix-issue-138392.stderr new file mode 100644 index 0000000000000..998fcfc36dc3f --- /dev/null +++ b/tests/ui/cast/cast_lit_suffix-issue-138392.stderr @@ -0,0 +1,31 @@ +error[E0308]: mismatched types + --> $DIR/cast_lit_suffix-issue-138392.rs:4:18 + | +LL | let _x: u8 = (4i32); + | -- ^^^^^^ expected `u8`, found `i32` + | | + | expected due to this + | +help: change the type of the numeric literal from `i32` to `u8` + | +LL - let _x: u8 = (4i32); +LL + let _x: u8 = (4u8); + | + +error[E0308]: mismatched types + --> $DIR/cast_lit_suffix-issue-138392.rs:5:18 + | +LL | let _y: u8 = (4.0f32); + | -- ^^^^^^^^ expected `u8`, found `f32` + | | + | expected due to this + | +help: change the type of the numeric literal from `f32` to `u8` + | +LL - let _y: u8 = (4.0f32); +LL + let _y: u8 = (4u8); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs index 46e72ea08779c..3a1e667d03a4f 100644 --- a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs @@ -1,18 +1,20 @@ -//@ check-pass - trait Trait<'a> {} fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) { x as _ - //~^ warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on - //~| warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~^ ERROR cannot add auto trait `Send` to dyn bound via pointer cast + //~| NOTE unsupported cast + //~| NOTE this could allow UB elsewhere + //~| HELP use `transmute` if you're sure this is sound } // (to test diagnostic list formatting) fn add_multiple_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send + Sync + Unpin) { x as _ - //~^ warning: adding auto traits `Send`, `Sync`, and `Unpin` to a trait object in a pointer cast may cause UB later on - //~| warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + //~^ ERROR cannot add auto traits `Send`, `Sync`, and `Unpin` to dyn bound via pointer cast + //~| NOTE unsupported cast + //~| NOTE this could allow UB elsewhere + //~| HELP use `transmute` if you're sure this is sound } fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr index e5ef8bf76b447..b64ca9b32bf5d 100644 --- a/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr @@ -1,43 +1,21 @@ -warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on - --> $DIR/ptr-to-trait-obj-add-auto.rs:6:5 +error[E0804]: cannot add auto trait `Send` to dyn bound via pointer cast + --> $DIR/ptr-to-trait-obj-add-auto.rs:4:5 | LL | x as _ - | ^^^^^^ + | ^^^^^^ unsupported cast | - = 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 #127323 - = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default + = note: this could allow UB elsewhere + = help: use `transmute` if you're sure this is sound -warning: adding auto traits `Send`, `Sync`, and `Unpin` to a trait object in a pointer cast may cause UB later on +error[E0804]: cannot add auto traits `Send`, `Sync`, and `Unpin` to dyn bound via pointer cast --> $DIR/ptr-to-trait-obj-add-auto.rs:13:5 | LL | x as _ - | ^^^^^^ + | ^^^^^^ unsupported cast | - = 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 #127323 + = note: this could allow UB elsewhere + = help: use `transmute` if you're sure this is sound -warning: 2 warnings emitted - -Future incompatibility report: Future breakage diagnostic: -warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on - --> $DIR/ptr-to-trait-obj-add-auto.rs:6:5 - | -LL | x as _ - | ^^^^^^ - | - = 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 #127323 - = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default - -Future breakage diagnostic: -warning: adding auto traits `Send`, `Sync`, and `Unpin` to a trait object in a pointer cast may cause UB later on - --> $DIR/ptr-to-trait-obj-add-auto.rs:13:5 - | -LL | x as _ - | ^^^^^^ - | - = 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 #127323 - = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0804`. diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs index f8910c944c61e..ce94ba657b86f 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs @@ -1,4 +1,5 @@ //@ check-fail +//@ dont-require-annotations: NOTE trait Trait<'a> {} @@ -18,7 +19,7 @@ fn change_lt_ba<'a, 'b: 'a>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { fn change_lt_hr<'a>(x: *mut dyn Trait<'a>) -> *mut dyn for<'b> Trait<'b> { x as _ //~ error: lifetime may not live long enough //~^ error: mismatched types - //~| one type is more general than the other + //~| NOTE one type is more general than the other } trait Assocked { diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr index faaa6325f3495..88a89dc4ac1b1 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:6:5 + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:7:5 | LL | fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { | -- -- lifetime `'b` defined here @@ -14,7 +14,7 @@ LL | x as _ = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:6:5 + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:7:5 | LL | fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { | -- -- lifetime `'b` defined here @@ -31,7 +31,7 @@ LL | x as _ help: `'b` and `'a` must be the same: replace one with the other error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:11:5 + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:12:5 | LL | fn change_lt_ab<'a: 'b, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { | -- -- lifetime `'b` defined here @@ -46,7 +46,7 @@ LL | x as _ = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:15:5 + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:16:5 | LL | fn change_lt_ba<'a, 'b: 'a>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { | -- -- lifetime `'b` defined here @@ -61,7 +61,7 @@ LL | x as _ = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:19:5 + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:20:5 | LL | fn change_lt_hr<'a>(x: *mut dyn Trait<'a>) -> *mut dyn for<'b> Trait<'b> { | -- lifetime `'a` defined here @@ -74,7 +74,7 @@ LL | fn change_lt_hr<'a>(x: *mut dyn Trait<'a>) -> *mut dyn for<'b> Trait<'b> + | ++++ error[E0308]: mismatched types - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:19:5 + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:20:5 | LL | x as _ | ^^^^^^ one type is more general than the other @@ -83,7 +83,7 @@ LL | x as _ found trait object `dyn Trait<'_>` error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:31:5 + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:32:5 | LL | fn change_assoc_0<'a, 'b>( | -- -- lifetime `'b` defined here @@ -99,7 +99,7 @@ LL | x as _ = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:31:5 + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:32:5 | LL | fn change_assoc_0<'a, 'b>( | -- -- lifetime `'b` defined here @@ -119,7 +119,7 @@ help: `'b` and `'a` must be the same: replace one with the other = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:38:5 + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:39:5 | LL | fn change_assoc_1<'a, 'b>( | -- -- lifetime `'b` defined here @@ -135,7 +135,7 @@ LL | x as _ = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:38:5 + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:39:5 | LL | fn change_assoc_1<'a, 'b>( | -- -- lifetime `'b` defined here @@ -155,7 +155,7 @@ help: `'b` and `'a` must be the same: replace one with the other = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0521]: borrowed data escapes outside of function - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:45:5 + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:46:5 | LL | fn extend_to_static<'a>(ptr: *const dyn Trait<'a>) { | -- --- diff --git a/tests/ui/catch-unwind-bang.rs b/tests/ui/catch-unwind-bang.rs deleted file mode 100644 index c874c649f3330..0000000000000 --- a/tests/ui/catch-unwind-bang.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass -//@ needs-unwind - -fn worker() -> ! { - panic!() -} - -fn main() { - std::panic::catch_unwind(worker).unwrap_err(); -} diff --git a/tests/ui/cfg/auxiliary/cfg_false_lib.rs b/tests/ui/cfg/auxiliary/cfg_false_lib.rs index 6c2dbb44d2a40..d1768e69b0d57 100644 --- a/tests/ui/cfg/auxiliary/cfg_false_lib.rs +++ b/tests/ui/cfg/auxiliary/cfg_false_lib.rs @@ -1,4 +1,4 @@ -// `#![no_std]` on a fully unconfigured crate is respected if it's placed before `cfg(FALSE)`. +// `#![no_std]` on a fully unconfigured crate is respected if it's placed before `cfg(false)`. // This crate has no such attribute, therefore this crate does link to libstd. -#![cfg(FALSE)] +#![cfg(false)] diff --git a/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_after.rs b/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_after.rs index 3cfa6c510d020..cd3170f3fb33d 100644 --- a/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_after.rs +++ b/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_after.rs @@ -1,5 +1,5 @@ -// `#![no_std]` on a fully unconfigured crate is respected if it's placed before `cfg(FALSE)`. +// `#![no_std]` on a fully unconfigured crate is respected if it's placed before `cfg(false)`. // Therefore this crate does link to libstd. -#![cfg(FALSE)] +#![cfg(false)] #![no_std] diff --git a/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_before.rs b/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_before.rs index a5c14be4c29de..ce4e1690996b5 100644 --- a/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_before.rs +++ b/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_before.rs @@ -1,8 +1,8 @@ -// `#![no_std]` on a fully unconfigured crate is respected if it's placed before `cfg(FALSE)`. +// `#![no_std]` on a fully unconfigured crate is respected if it's placed before `cfg(false)`. // Therefore this crate doesn't link to libstd. //@ no-prefer-dynamic #![no_std] #![crate_type = "lib"] -#![cfg(FALSE)] +#![cfg(false)] diff --git a/tests/ui/cfg/auxiliary/cfged_out.rs b/tests/ui/cfg/auxiliary/cfged_out.rs index f6a9089cf29de..564280b24f598 100644 --- a/tests/ui/cfg/auxiliary/cfged_out.rs +++ b/tests/ui/cfg/auxiliary/cfged_out.rs @@ -1,8 +1,8 @@ pub mod inner { - #[cfg(FALSE)] + #[cfg(false)] pub fn uwu() {} - #[cfg(FALSE)] + #[cfg(false)] pub mod doesnt_exist { pub fn hello() {} } diff --git a/tests/ui/cfg/both-true-false.rs b/tests/ui/cfg/both-true-false.rs new file mode 100644 index 0000000000000..5fca8f654ad8e --- /dev/null +++ b/tests/ui/cfg/both-true-false.rs @@ -0,0 +1,14 @@ +/// Test that placing a `cfg(true)` and `cfg(false)` on the same item result in +//. it being disabled.` + +#[cfg(false)] +#[cfg(true)] +fn foo() {} + +#[cfg(true)] +#[cfg(false)] +fn foo() {} + +fn main() { + foo(); //~ ERROR cannot find function `foo` in this scope +} diff --git a/tests/ui/cfg/both-true-false.stderr b/tests/ui/cfg/both-true-false.stderr new file mode 100644 index 0000000000000..1526cc2b707b4 --- /dev/null +++ b/tests/ui/cfg/both-true-false.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find function `foo` in this scope + --> $DIR/both-true-false.rs:13:5 + | +LL | foo(); + | ^^^ not found in this scope + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/cfg-false-feature.rs b/tests/ui/cfg/cfg-false-feature.rs index 716b18492c739..f66e4722440c5 100644 --- a/tests/ui/cfg/cfg-false-feature.rs +++ b/tests/ui/cfg/cfg-false-feature.rs @@ -1,10 +1,10 @@ -// Features above `cfg(FALSE)` are in effect in a fully unconfigured crate (issue #104633). +// Features above `cfg(false)` are in effect in a fully unconfigured crate (issue #104633). //@ check-pass //@ compile-flags: --crate-type lib #![feature(decl_macro)] -#![cfg(FALSE)] +#![cfg(false)] #![feature(box_patterns)] macro mac() {} // OK diff --git a/tests/ui/cfg/cfg-macros-notfoo.rs b/tests/ui/cfg/cfg-macros-notfoo.rs index 9feb06be73e90..c25cf1c39bf54 100644 --- a/tests/ui/cfg/cfg-macros-notfoo.rs +++ b/tests/ui/cfg/cfg-macros-notfoo.rs @@ -3,7 +3,7 @@ // check that cfg correctly chooses between the macro impls (see also // cfg-macros-foo.rs) -#[cfg(FALSE)] +#[cfg(false)] #[macro_use] mod foo { macro_rules! bar { diff --git a/tests/ui/cfg/cfg-match-arm.rs b/tests/ui/cfg/cfg-match-arm.rs index f6cd52c475cfc..cb5bf0ab06539 100644 --- a/tests/ui/cfg/cfg-match-arm.rs +++ b/tests/ui/cfg/cfg-match-arm.rs @@ -11,7 +11,7 @@ fn foo(f: Foo) { Foo::Bar => {}, #[cfg(not(FALSE))] Foo::Baz => {}, - #[cfg(FALSE)] + #[cfg(false)] Basdfwe => {} } } diff --git a/tests/ui/cfg/cfg-stmt-recovery.rs b/tests/ui/cfg/cfg-stmt-recovery.rs index 2e0839d2a1535..f0f9a649165b5 100644 --- a/tests/ui/cfg/cfg-stmt-recovery.rs +++ b/tests/ui/cfg/cfg-stmt-recovery.rs @@ -6,7 +6,7 @@ #[cfg_eval] fn main() { #[cfg_eval] - let _ = #[cfg(FALSE)] 0; + let _ = #[cfg(false)] 0; //~^ ERROR removing an expression is not supported in this position //~| ERROR expected expression, found `;` //~| ERROR removing an expression is not supported in this position diff --git a/tests/ui/cfg/cfg-stmt-recovery.stderr b/tests/ui/cfg/cfg-stmt-recovery.stderr index cb15e21fac698..e34da72afd934 100644 --- a/tests/ui/cfg/cfg-stmt-recovery.stderr +++ b/tests/ui/cfg/cfg-stmt-recovery.stderr @@ -1,19 +1,19 @@ error: removing an expression is not supported in this position --> $DIR/cfg-stmt-recovery.rs:9:13 | -LL | let _ = #[cfg(FALSE)] 0; +LL | let _ = #[cfg(false)] 0; | ^^^^^^^^^^^^^ error: expected expression, found `;` --> $DIR/cfg-stmt-recovery.rs:9:28 | -LL | let _ = #[cfg(FALSE)] 0; +LL | let _ = #[cfg(false)] 0; | ^ expected expression error: removing an expression is not supported in this position --> $DIR/cfg-stmt-recovery.rs:9:13 | -LL | let _ = #[cfg(FALSE)] 0; +LL | let _ = #[cfg(false)] 0; | ^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/cfg/cfg_false_no_std-2.rs b/tests/ui/cfg/cfg_false_no_std-2.rs index cd33756587210..666c90deaf0f4 100644 --- a/tests/ui/cfg/cfg_false_no_std-2.rs +++ b/tests/ui/cfg/cfg_false_no_std-2.rs @@ -1,7 +1,11 @@ // Error, the linked empty library is `no_std` and doesn't provide a panic handler. //@ dont-check-compiler-stderr -//@ error-pattern: `#[panic_handler]` function required, but not found + +// NOTE: fix a panic strategy to prevent differing errors subject to target's default panic strategy +// which changes between targets. The specific panic strategy doesn't matter for test intention. +//@ compile-flags: -Cpanic=abort + //@ aux-build: cfg_false_lib_no_std_before.rs #![no_std] @@ -9,3 +13,5 @@ extern crate cfg_false_lib_no_std_before as _; fn main() {} + +//~? ERROR `#[panic_handler]` function required, but not found diff --git a/tests/ui/cfg/cfg_stmt_expr.rs b/tests/ui/cfg/cfg_stmt_expr.rs index 9245f6d97576f..361b159a354fd 100644 --- a/tests/ui/cfg/cfg_stmt_expr.rs +++ b/tests/ui/cfg/cfg_stmt_expr.rs @@ -7,47 +7,47 @@ fn main() { let a = 413; - #[cfg(FALSE)] + #[cfg(false)] let a = (); assert_eq!(a, 413); let mut b = 612; - #[cfg(FALSE)] + #[cfg(false)] { b = 1111; } assert_eq!(b, 612); - #[cfg(FALSE)] + #[cfg(false)] undefined_fn(); - #[cfg(FALSE)] + #[cfg(false)] undefined_macro!(); - #[cfg(FALSE)] + #[cfg(false)] undefined_macro![]; - #[cfg(FALSE)] + #[cfg(false)] undefined_macro!{}; // pretty printer bug... - // #[cfg(FALSE)] + // #[cfg(false)] // undefined_macro!{} - let () = (#[cfg(FALSE)] 341,); // Should this also work on parens? - let t = (1, #[cfg(FALSE)] 3, 4); + let () = (#[cfg(false)] 341,); // Should this also work on parens? + let t = (1, #[cfg(false)] 3, 4); assert_eq!(t, (1, 4)); let f = |_: u32, _: u32| (); - f(2, 1, #[cfg(FALSE)] 6); + f(2, 1, #[cfg(false)] 6); - let _: u32 = a.clone(#[cfg(FALSE)] undefined); + let _: u32 = a.clone(#[cfg(false)] undefined); - let _: [(); 0] = [#[cfg(FALSE)] 126]; - let t = [#[cfg(FALSE)] 1, 2, 6]; + let _: [(); 0] = [#[cfg(false)] 126]; + let t = [#[cfg(false)] 1, 2, 6]; assert_eq!(t, [2, 6]); { let r; - #[cfg(FALSE)] + #[cfg(false)] (r = 5); #[cfg(not(FALSE))] (r = 10); @@ -75,7 +75,7 @@ fn main() { 612 }); - assert_eq!((#[cfg(FALSE)] 1, #[cfg(not(FALSE))] 2), (2,)); + assert_eq!((#[cfg(false)] 1, #[cfg(not(FALSE))] 2), (2,)); assert_eq!(n, 612); // check that lints work diff --git a/tests/ui/cfg/cmdline-false.rs b/tests/ui/cfg/cmdline-false.rs new file mode 100644 index 0000000000000..d4b7d3bbfdca2 --- /dev/null +++ b/tests/ui/cfg/cmdline-false.rs @@ -0,0 +1,9 @@ +/// Test that `--cfg false` doesn't cause `cfg(false)` to evaluate to `true` +//@ compile-flags: --cfg false + +#[cfg(false)] +fn foo() {} + +fn main() { + foo(); //~ ERROR cannot find function `foo` in this scope +} diff --git a/tests/ui/cfg/cmdline-false.stderr b/tests/ui/cfg/cmdline-false.stderr new file mode 100644 index 0000000000000..5f57c754c403b --- /dev/null +++ b/tests/ui/cfg/cmdline-false.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find function `foo` in this scope + --> $DIR/cmdline-false.rs:8:5 + | +LL | foo(); + | ^^^ not found in this scope + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/conditional-compile.rs b/tests/ui/cfg/conditional-compile.rs index dff280054d659..0739e877bfd14 100644 --- a/tests/ui/cfg/conditional-compile.rs +++ b/tests/ui/cfg/conditional-compile.rs @@ -6,16 +6,16 @@ // Crate use statements -#[cfg(FALSE)] +#[cfg(false)] use flippity; -#[cfg(FALSE)] +#[cfg(false)] static b: bool = false; static b: bool = true; mod rustrt { - #[cfg(FALSE)] + #[cfg(false)] extern "C" { // This symbol doesn't exist and would be a link error if this // module was codegened @@ -25,12 +25,12 @@ mod rustrt { extern "C" {} } -#[cfg(FALSE)] +#[cfg(false)] type t = isize; type t = bool; -#[cfg(FALSE)] +#[cfg(false)] enum tg { foo, } @@ -39,12 +39,12 @@ enum tg { bar, } -#[cfg(FALSE)] +#[cfg(false)] struct r { i: isize, } -#[cfg(FALSE)] +#[cfg(false)] fn r(i: isize) -> r { r { i: i } } @@ -57,7 +57,7 @@ fn r(i: isize) -> r { r { i: i } } -#[cfg(FALSE)] +#[cfg(false)] mod m { // This needs to parse but would fail in typeck. Since it's not in // the current config it should not be typechecked. @@ -69,7 +69,7 @@ mod m { mod m { // Submodules have slightly different code paths than the top-level // module, so let's make sure this jazz works here as well - #[cfg(FALSE)] + #[cfg(false)] pub fn f() {} pub fn f() {} @@ -77,7 +77,7 @@ mod m { // Since the FALSE configuration isn't defined main will just be // parsed, but nothing further will be done with it -#[cfg(FALSE)] +#[cfg(false)] pub fn main() { panic!() } @@ -93,14 +93,14 @@ pub fn main() { } fn test_in_fn_ctxt() { - #[cfg(FALSE)] + #[cfg(false)] fn f() { panic!() } fn f() {} f(); - #[cfg(FALSE)] + #[cfg(false)] static i: isize = 0; static i: isize = 1; assert_eq!(i, 1); @@ -109,7 +109,7 @@ fn test_in_fn_ctxt() { mod test_foreign_items { pub mod rustrt { extern "C" { - #[cfg(FALSE)] + #[cfg(false)] pub fn write() -> String; pub fn write() -> String; } @@ -117,7 +117,7 @@ mod test_foreign_items { } mod test_use_statements { - #[cfg(FALSE)] + #[cfg(false)] use flippity_foo; } @@ -127,24 +127,24 @@ mod test_methods { } impl Fooable for Foo { - #[cfg(FALSE)] + #[cfg(false)] fn what(&self) {} fn what(&self) {} - #[cfg(FALSE)] + #[cfg(false)] fn the(&self) {} fn the(&self) {} } trait Fooable { - #[cfg(FALSE)] + #[cfg(false)] fn what(&self); fn what(&self); - #[cfg(FALSE)] + #[cfg(false)] fn the(&self); fn the(&self); diff --git a/tests/ui/cfg/diagnostics-cross-crate.stderr b/tests/ui/cfg/diagnostics-cross-crate.stderr index 07ad4e3272d1b..3e32a856e9540 100644 --- a/tests/ui/cfg/diagnostics-cross-crate.stderr +++ b/tests/ui/cfg/diagnostics-cross-crate.stderr @@ -12,7 +12,7 @@ LL | pub mod doesnt_exist { note: the item is gated here --> $DIR/auxiliary/cfged_out.rs:5:5 | -LL | #[cfg(FALSE)] +LL | #[cfg(false)] | ^^^^^^^^^^^^^ error[E0425]: cannot find function `uwu` in crate `cfged_out` @@ -35,7 +35,7 @@ LL | pub fn uwu() {} note: the item is gated here --> $DIR/auxiliary/cfged_out.rs:2:5 | -LL | #[cfg(FALSE)] +LL | #[cfg(false)] | ^^^^^^^^^^^^^ error[E0425]: cannot find function `meow` in module `cfged_out::inner::right` diff --git a/tests/ui/cfg/diagnostics-reexport.rs b/tests/ui/cfg/diagnostics-reexport.rs index 9ae7d931fcb8f..56fac56223827 100644 --- a/tests/ui/cfg/diagnostics-reexport.rs +++ b/tests/ui/cfg/diagnostics-reexport.rs @@ -1,10 +1,10 @@ pub mod inner { - #[cfg(FALSE)] + #[cfg(false)] mod gone { pub fn uwu() {} } - #[cfg(FALSE)] //~ NOTE the item is gated here + #[cfg(false)] //~ NOTE the item is gated here pub use super::uwu; //~^ NOTE found an item that was configured out } @@ -14,7 +14,7 @@ pub use a::x; //~| NOTE no `x` in `a` mod a { - #[cfg(FALSE)] //~ NOTE the item is gated here + #[cfg(false)] //~ NOTE the item is gated here pub fn x() {} //~^ NOTE found an item that was configured out } @@ -25,10 +25,10 @@ pub use b::{x, y}; //~| NOTE no `y` in `b` mod b { - #[cfg(FALSE)] //~ NOTE the item is gated here + #[cfg(false)] //~ NOTE the item is gated here pub fn x() {} //~^ NOTE found an item that was configured out - #[cfg(FALSE)] //~ NOTE the item is gated here + #[cfg(false)] //~ NOTE the item is gated here pub fn y() {} //~^ NOTE found an item that was configured out } diff --git a/tests/ui/cfg/diagnostics-reexport.stderr b/tests/ui/cfg/diagnostics-reexport.stderr index 737202fdf9ad9..95dc4fac945e2 100644 --- a/tests/ui/cfg/diagnostics-reexport.stderr +++ b/tests/ui/cfg/diagnostics-reexport.stderr @@ -12,7 +12,7 @@ LL | pub fn x() {} note: the item is gated here --> $DIR/diagnostics-reexport.rs:17:5 | -LL | #[cfg(FALSE)] +LL | #[cfg(false)] | ^^^^^^^^^^^^^ error[E0432]: unresolved imports `b::x`, `b::y` @@ -31,7 +31,7 @@ LL | pub fn x() {} note: the item is gated here --> $DIR/diagnostics-reexport.rs:28:5 | -LL | #[cfg(FALSE)] +LL | #[cfg(false)] | ^^^^^^^^^^^^^ note: found an item that was configured out --> $DIR/diagnostics-reexport.rs:32:12 @@ -41,7 +41,7 @@ LL | pub fn y() {} note: the item is gated here --> $DIR/diagnostics-reexport.rs:31:5 | -LL | #[cfg(FALSE)] +LL | #[cfg(false)] | ^^^^^^^^^^^^^ error[E0425]: cannot find function `uwu` in module `inner` @@ -58,7 +58,7 @@ LL | pub use super::uwu; note: the item is gated here --> $DIR/diagnostics-reexport.rs:7:5 | -LL | #[cfg(FALSE)] +LL | #[cfg(false)] | ^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/cfg/diagnostics-same-crate.rs b/tests/ui/cfg/diagnostics-same-crate.rs index d6f8dd21a9221..9153f20b29649 100644 --- a/tests/ui/cfg/diagnostics-same-crate.rs +++ b/tests/ui/cfg/diagnostics-same-crate.rs @@ -1,11 +1,11 @@ #![allow(unexpected_cfgs)] // since we want to recognize them as unexpected pub mod inner { - #[cfg(FALSE)] //~ NOTE the item is gated here + #[cfg(false)] //~ NOTE the item is gated here pub fn uwu() {} //~^ NOTE found an item that was configured out - #[cfg(FALSE)] //~ NOTE the item is gated here + #[cfg(false)] //~ NOTE the item is gated here //~^ NOTE the item is gated here //~| NOTE the item is gated here pub mod doesnt_exist { diff --git a/tests/ui/cfg/diagnostics-same-crate.stderr b/tests/ui/cfg/diagnostics-same-crate.stderr index dd0d10c6567ea..75a1bc39a013c 100644 --- a/tests/ui/cfg/diagnostics-same-crate.stderr +++ b/tests/ui/cfg/diagnostics-same-crate.stderr @@ -12,7 +12,7 @@ LL | pub mod doesnt_exist { note: the item is gated here --> $DIR/diagnostics-same-crate.rs:8:5 | -LL | #[cfg(FALSE)] +LL | #[cfg(false)] | ^^^^^^^^^^^^^ error[E0432]: unresolved import `super::inner::doesnt_exist` @@ -29,7 +29,7 @@ LL | pub mod doesnt_exist { note: the item is gated here --> $DIR/diagnostics-same-crate.rs:8:5 | -LL | #[cfg(FALSE)] +LL | #[cfg(false)] | ^^^^^^^^^^^^^ error[E0433]: failed to resolve: could not find `doesnt_exist` in `inner` @@ -46,7 +46,7 @@ LL | pub mod doesnt_exist { note: the item is gated here --> $DIR/diagnostics-same-crate.rs:8:5 | -LL | #[cfg(FALSE)] +LL | #[cfg(false)] | ^^^^^^^^^^^^^ error[E0425]: cannot find function `uwu` in module `inner` @@ -63,7 +63,7 @@ LL | pub fn uwu() {} note: the item is gated here --> $DIR/diagnostics-same-crate.rs:4:5 | -LL | #[cfg(FALSE)] +LL | #[cfg(false)] | ^^^^^^^^^^^^^ error[E0425]: cannot find function `meow` in module `inner::right` diff --git a/tests/ui/cfg/disallowed-cli-cfgs.reliable_f128_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.reliable_f128_.stderr new file mode 100644 index 0000000000000..1b6e05060017e --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.reliable_f128_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_has_reliable_f128` flag + | + = note: config `target_has_reliable_f128` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.reliable_f128_math_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.reliable_f128_math_.stderr new file mode 100644 index 0000000000000..86e7342b8fc1e --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.reliable_f128_math_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_has_reliable_f128_math` flag + | + = note: config `target_has_reliable_f128_math` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.reliable_f16_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.reliable_f16_.stderr new file mode 100644 index 0000000000000..cf5000ecf2741 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.reliable_f16_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_has_reliable_f16` flag + | + = note: config `target_has_reliable_f16` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.reliable_f16_math_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.reliable_f16_math_.stderr new file mode 100644 index 0000000000000..079e5627e4c2b --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.reliable_f16_math_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_has_reliable_f16_math` flag + | + = note: config `target_has_reliable_f16_math` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.rs b/tests/ui/cfg/disallowed-cli-cfgs.rs index cae9c65cb45ac..f7f9d2b5cd7f9 100644 --- a/tests/ui/cfg/disallowed-cli-cfgs.rs +++ b/tests/ui/cfg/disallowed-cli-cfgs.rs @@ -8,6 +8,7 @@ //@ revisions: target_thread_local_ relocation_model_ //@ revisions: fmt_debug_ //@ revisions: emscripten_wasm_eh_ +//@ revisions: reliable_f16_ reliable_f16_math_ reliable_f128_ reliable_f128_math_ //@ [overflow_checks_]compile-flags: --cfg overflow_checks //@ [debug_assertions_]compile-flags: --cfg debug_assertions @@ -35,5 +36,11 @@ //@ [relocation_model_]compile-flags: --cfg relocation_model="a" //@ [fmt_debug_]compile-flags: --cfg fmt_debug="shallow" //@ [emscripten_wasm_eh_]compile-flags: --cfg emscripten_wasm_eh +//@ [reliable_f16_]compile-flags: --cfg target_has_reliable_f16 +//@ [reliable_f16_math_]compile-flags: --cfg target_has_reliable_f16_math +//@ [reliable_f128_]compile-flags: --cfg target_has_reliable_f128 +//@ [reliable_f128_math_]compile-flags: --cfg target_has_reliable_f128_math fn main() {} + +//~? ERROR unexpected `--cfg diff --git a/tests/ui/cfg/raw-true-false.rs b/tests/ui/cfg/raw-true-false.rs index 4cb8bb71c924b..c92672fc144e3 100644 --- a/tests/ui/cfg/raw-true-false.rs +++ b/tests/ui/cfg/raw-true-false.rs @@ -1,19 +1,11 @@ //@ check-pass -//@ compile-flags: --cfg false --check-cfg=cfg(r#false) - -#![deny(warnings)] - -#[expect(unexpected_cfgs)] -mod a { - #[cfg(r#true)] - pub fn foo() {} -} - -mod b { - #[cfg(r#false)] - pub fn bar() {} -} - +//@ revisions: r0x0 r0x1 r1x0 r1x1 +//@[r0x0] compile-flags: --cfg false --check-cfg=cfg(false) +//@[r0x1] compile-flags: --cfg false --check-cfg=cfg(r#false) +//@[r1x0] compile-flags: --cfg r#false --check-cfg=cfg(false) +//@[r1x1] compile-flags: --cfg r#false --check-cfg=cfg(r#false) +#![deny(unexpected_cfgs)] fn main() { - b::bar() + #[cfg(not(r#false))] + compile_error!(""); } diff --git a/tests/ui/cfg/true-false.rs b/tests/ui/cfg/true-false.rs index 03d96fbafecbe..0bd1cf427fafd 100644 --- a/tests/ui/cfg/true-false.rs +++ b/tests/ui/cfg/true-false.rs @@ -1,7 +1,6 @@ //@ run-pass #![feature(link_cfg)] -#![feature(cfg_boolean_literals)] #[cfg(true)] fn foo() -> bool { diff --git a/tests/ui/check-cfg/allow-same-level.rs b/tests/ui/check-cfg/allow-same-level.rs index 5eef50e08e21b..3f673cb884472 100644 --- a/tests/ui/check-cfg/allow-same-level.rs +++ b/tests/ui/check-cfg/allow-same-level.rs @@ -3,7 +3,7 @@ // // It should work, but due to interactions between how #[cfg]s are // expanded, the lint machinery and the check-cfg impl, we -// miss the #[allow], althrough we probably shoudln't. +// miss the #[allow], althrough we probably shouldn't. // // cf. https://github.com/rust-lang/rust/issues/124735 // @@ -12,7 +12,7 @@ //@ compile-flags: --check-cfg=cfg() --cfg=unknown_but_active_cfg #[allow(unexpected_cfgs)] -#[cfg(FALSE)] +#[cfg(unknown_and_inactive_cfg)] //~^ WARNING unexpected `cfg` condition name fn bar() {} diff --git a/tests/ui/check-cfg/allow-same-level.stderr b/tests/ui/check-cfg/allow-same-level.stderr index a705cd4e5f01e..cfff03048b53c 100644 --- a/tests/ui/check-cfg/allow-same-level.stderr +++ b/tests/ui/check-cfg/allow-same-level.stderr @@ -1,10 +1,10 @@ -warning: unexpected `cfg` condition name: `FALSE` +warning: unexpected `cfg` condition name: `unknown_and_inactive_cfg` --> $DIR/allow-same-level.rs:15:7 | -LL | #[cfg(FALSE)] - | ^^^^^ +LL | #[cfg(unknown_and_inactive_cfg)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: to expect this configuration use `--check-cfg=cfg(FALSE)` + = help: to expect this configuration use `--check-cfg=cfg(unknown_and_inactive_cfg)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/allow-top-level.rs b/tests/ui/check-cfg/allow-top-level.rs index cf94ed5da428f..7ccecd2360e86 100644 --- a/tests/ui/check-cfg/allow-top-level.rs +++ b/tests/ui/check-cfg/allow-top-level.rs @@ -6,7 +6,7 @@ #![allow(unexpected_cfgs)] -#[cfg(FALSE)] +#[cfg(false)] fn bar() {} fn foo() { diff --git a/tests/ui/check-cfg/allow-upper-level.rs b/tests/ui/check-cfg/allow-upper-level.rs index 2e6664c30d395..657a4768f952c 100644 --- a/tests/ui/check-cfg/allow-upper-level.rs +++ b/tests/ui/check-cfg/allow-upper-level.rs @@ -6,7 +6,7 @@ #[allow(unexpected_cfgs)] mod aa { - #[cfg(FALSE)] + #[cfg(false)] fn bar() {} } diff --git a/tests/ui/check-cfg/and-more-diagnostic.rs b/tests/ui/check-cfg/and-more-diagnostic.rs index 977f55e8a6d10..5422829c5b313 100644 --- a/tests/ui/check-cfg/and-more-diagnostic.rs +++ b/tests/ui/check-cfg/and-more-diagnostic.rs @@ -5,7 +5,7 @@ //@ no-auto-check-cfg //@ compile-flags: --check-cfg=cfg() //@ normalize-stderr: "and \d+ more" -> "and X more" -//@ normalize-stderr: "`[a-zA-Z0-9_-]+`" -> "`xxx`" +//@ normalize-stderr: "`[a-zA-Z0-9_\.-]+`" -> "`xxx`" fn main() { cfg!(target_feature = "zebra"); diff --git a/tests/ui/check-cfg/cargo-feature.rs b/tests/ui/check-cfg/cargo-feature.rs index a9380ddae1a90..a02b0437057f9 100644 --- a/tests/ui/check-cfg/cargo-feature.rs +++ b/tests/ui/check-cfg/cargo-feature.rs @@ -10,7 +10,7 @@ //@ [none]compile-flags: --check-cfg=cfg(feature,values()) //@ [some]compile-flags: --check-cfg=cfg(feature,values("bitcode")) //@ [some]compile-flags: --check-cfg=cfg(CONFIG_NVME,values("y")) -//@ [none]error-pattern:Cargo.toml +//@ dont-require-annotations: HELP #[cfg(feature = "serde")] //~^ WARNING unexpected `cfg` condition value @@ -27,6 +27,7 @@ fn tokio() {} #[cfg(CONFIG_NVME = "m")] //[none]~^ WARNING unexpected `cfg` condition name //[some]~^^ WARNING unexpected `cfg` condition value +//[none]~| HELP Cargo.toml fn tokio() {} fn main() {} diff --git a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr index e7b8f35505761..b07d630e5f5dc 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr @@ -14,7 +14,7 @@ warning: unexpected `cfg` condition value: `value` LL | #[cfg(target_vendor = "value")] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `amd`, `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `amd`, `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `openwrt`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `feature` diff --git a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr index 95af0a9092993..80f8f36c23f78 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr @@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value: `value` LL | #[cfg(target_vendor = "value")] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `amd`, `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `amd`, `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `openwrt`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `unk` diff --git a/tests/ui/check-cfg/exhaustive-names-values.full.stderr b/tests/ui/check-cfg/exhaustive-names-values.full.stderr index 95af0a9092993..80f8f36c23f78 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.full.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.full.stderr @@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value: `value` LL | #[cfg(target_vendor = "value")] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `amd`, `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `amd`, `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `openwrt`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `unk` diff --git a/tests/ui/check-cfg/invalid-arguments.boolean.stderr b/tests/ui/check-cfg/invalid-arguments.boolean.stderr deleted file mode 100644 index 18734de9dac03..0000000000000 --- a/tests/ui/check-cfg/invalid-arguments.boolean.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: invalid `--check-cfg` argument: `cfg(true)` - | - = note: `true` is a boolean literal - = note: `cfg()` arguments must be simple identifiers, `any()` or `values(...)` - = note: visit for more details - diff --git a/tests/ui/check-cfg/invalid-arguments.boolean_after_values.stderr b/tests/ui/check-cfg/invalid-arguments.boolean_after_values.stderr new file mode 100644 index 0000000000000..3aa2205d3b18c --- /dev/null +++ b/tests/ui/check-cfg/invalid-arguments.boolean_after_values.stderr @@ -0,0 +1,5 @@ +error: invalid `--check-cfg` argument: `cfg(values(),true)` + | + = note: `values()` cannot be specified before the names + = note: visit for more details + diff --git a/tests/ui/check-cfg/invalid-arguments.rs b/tests/ui/check-cfg/invalid-arguments.rs index c6b1218ce27c9..279b13b697290 100644 --- a/tests/ui/check-cfg/invalid-arguments.rs +++ b/tests/ui/check-cfg/invalid-arguments.rs @@ -2,7 +2,7 @@ // //@ check-fail //@ no-auto-check-cfg -//@ revisions: anything_else boolean +//@ revisions: anything_else boolean_after_values //@ revisions: string_for_name_1 string_for_name_2 multiple_any multiple_values //@ revisions: multiple_values_any not_empty_any not_empty_values_any //@ revisions: values_any_missing_values values_any_before_ident ident_in_values_1 @@ -11,7 +11,7 @@ //@ revisions: none_not_empty cfg_none unsafe_attr // //@ [anything_else]compile-flags: --check-cfg=anything_else(...) -//@ [boolean]compile-flags: --check-cfg=cfg(true) +//@ [boolean_after_values]compile-flags: --check-cfg=cfg(values(),true) //@ [string_for_name_1]compile-flags: --check-cfg=cfg("NOT_IDENT") //@ [string_for_name_2]compile-flags: --check-cfg=cfg(foo,"NOT_IDENT",bar) //@ [multiple_any]compile-flags: --check-cfg=cfg(any(),any()) @@ -36,3 +36,5 @@ //@ [unsafe_attr]compile-flags: --check-cfg=unsafe(cfg(foo)) fn main() {} + +//~? ERROR invalid `--check-cfg` argument diff --git a/tests/ui/check-cfg/raw-keywords.edition2015.stderr b/tests/ui/check-cfg/raw-keywords.edition2015.stderr index 8ca33e088fc94..29c1a71c0b7a6 100644 --- a/tests/ui/check-cfg/raw-keywords.edition2015.stderr +++ b/tests/ui/check-cfg/raw-keywords.edition2015.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition name: `tru` - --> $DIR/raw-keywords.rs:14:7 + --> $DIR/raw-keywords.rs:15:7 | LL | #[cfg(tru)] | ^^^ help: there is a config with a similar name: `r#true` @@ -9,7 +9,7 @@ LL | #[cfg(tru)] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition name: `r#false` - --> $DIR/raw-keywords.rs:19:7 + --> $DIR/raw-keywords.rs:20:7 | LL | #[cfg(r#false)] | ^^^^^^^ @@ -19,7 +19,7 @@ LL | #[cfg(r#false)] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `await` - --> $DIR/raw-keywords.rs:27:29 + --> $DIR/raw-keywords.rs:28:29 | LL | #[cfg_attr(edition2015, cfg(await))] | ^^^^^ @@ -28,7 +28,7 @@ LL | #[cfg_attr(edition2015, cfg(await))] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `raw` - --> $DIR/raw-keywords.rs:33:7 + --> $DIR/raw-keywords.rs:34:7 | LL | #[cfg(r#raw)] | ^^^^^ diff --git a/tests/ui/check-cfg/raw-keywords.edition2021.stderr b/tests/ui/check-cfg/raw-keywords.edition2021.stderr index cce55720bdd18..cc3702685fd2c 100644 --- a/tests/ui/check-cfg/raw-keywords.edition2021.stderr +++ b/tests/ui/check-cfg/raw-keywords.edition2021.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition name: `tru` - --> $DIR/raw-keywords.rs:14:7 + --> $DIR/raw-keywords.rs:15:7 | LL | #[cfg(tru)] | ^^^ help: there is a config with a similar name: `r#true` @@ -9,7 +9,7 @@ LL | #[cfg(tru)] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition name: `r#false` - --> $DIR/raw-keywords.rs:19:7 + --> $DIR/raw-keywords.rs:20:7 | LL | #[cfg(r#false)] | ^^^^^^^ @@ -19,7 +19,7 @@ LL | #[cfg(r#false)] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `r#await` - --> $DIR/raw-keywords.rs:28:29 + --> $DIR/raw-keywords.rs:29:29 | LL | #[cfg_attr(edition2021, cfg(r#await))] | ^^^^^^^ @@ -28,7 +28,7 @@ LL | #[cfg_attr(edition2021, cfg(r#await))] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `raw` - --> $DIR/raw-keywords.rs:33:7 + --> $DIR/raw-keywords.rs:34:7 | LL | #[cfg(r#raw)] | ^^^^^ diff --git a/tests/ui/check-cfg/raw-keywords.rs b/tests/ui/check-cfg/raw-keywords.rs index 5de13240d7ed3..b82eb5a64e9a1 100644 --- a/tests/ui/check-cfg/raw-keywords.rs +++ b/tests/ui/check-cfg/raw-keywords.rs @@ -6,7 +6,8 @@ //@ compile-flags: --cfg=true --cfg=async --check-cfg=cfg(r#true,r#async,edition2015,edition2021) // //@ revisions: edition2015 edition2021 -//@ [edition2021] compile-flags: --edition 2021 +//@ [edition2015] edition: 2015 +//@ [edition2021] edition: 2021 #[cfg(r#true)] fn foo() {} diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 5b82d3f539fe1..eb66633f9dd7c 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -17,13 +17,21 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `aes` `altivec` `alu32` +`amx-avx512` `amx-bf16` `amx-complex` `amx-fp16` +`amx-fp8` `amx-int8` +`amx-movrs` +`amx-tf32` `amx-tile` +`amx-transpose` +`apxf` `atomics` `avx` +`avx10.1` +`avx10.2` `avx2` `avx512bf16` `avx512bitalg` @@ -44,6 +52,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `avxvnni` `avxvnniint16` `avxvnniint8` +`b` `backchain` `bf16` `bmi1` @@ -60,6 +69,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `d32` `deflate-conversion` `dit` +`div32` `doloop` `dotprod` `dpb` @@ -133,8 +143,11 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `jsconv` `kl` `lahfsahf` +`lam-bh` +`lamcas` `lasx` `lbt` +`ld-seq-sa` `leoncasa` `lor` `lse` @@ -148,6 +161,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `mclass` `mops` `movbe` +`movrs` `mp` `mp1e2` `msa` @@ -190,6 +204,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `reserve-x18` `rtm` `sb` +`scq` `sha` `sha2` `sha3` @@ -240,6 +255,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `trustzone` `ual` `unaligned-scalar-mem` +`unaligned-vector-mem` `v` `v5te` `v6` @@ -306,12 +322,33 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `zbkc` `zbkx` `zbs` +`zca` +`zcb` +`zcmop` `zdinx` +`zfa` +`zfbfmin` `zfh` `zfhmin` `zfinx` `zhinx` `zhinxmin` +`zic64b` +`zicbom` +`zicbop` +`zicboz` +`ziccamoa` +`ziccif` +`zicclsm` +`ziccrse` +`zicntr` +`zicond` +`zicsr` +`zifencei` +`zihintntl` +`zihintpause` +`zihpm` +`zimop` `zk` `zkn` `zknd` @@ -320,7 +357,45 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `zkr` `zks` `zksed` -`zksh`, and `zkt` +`zksh` +`zkt` +`ztso` +`zvbb` +`zvbc` +`zve32f` +`zve32x` +`zve64d` +`zve64f` +`zve64x` +`zvfbfmin` +`zvfbfwma` +`zvfh` +`zvfhmin` +`zvkb` +`zvkg` +`zvkn` +`zvknc` +`zvkned` +`zvkng` +`zvknha` +`zvknhb` +`zvks` +`zvksc` +`zvksed` +`zvksg` +`zvksh` +`zvkt` +`zvl1024b` +`zvl128b` +`zvl16384b` +`zvl2048b` +`zvl256b` +`zvl32768b` +`zvl32b` +`zvl4096b` +`zvl512b` +`zvl64b` +`zvl65536b`, and `zvl8192b` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index ba1900fcddb2d..7cda6c2eaa529 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -201,7 +201,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_os = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -230,7 +230,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_vendor = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `amd`, `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `amd`, `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `openwrt`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -274,7 +274,7 @@ LL | #[cfg(target_os = "linuz")] // testing that we suggest `linux` | | | help: there is a expected value with a similar name: `"linux"` | - = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `lynxos178`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: 28 warnings emitted diff --git a/tests/ui/closures/2229_closure_analysis/by_value.rs b/tests/ui/closures/2229_closure_analysis/by_value.rs index 2c9202fd61721..605b8ea35e51f 100644 --- a/tests/ui/closures/2229_closure_analysis/by_value.rs +++ b/tests/ui/closures/2229_closure_analysis/by_value.rs @@ -20,8 +20,8 @@ fn big_box() { //~| NOTE: see issue #15701 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: let p = t.0.0; //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue //~| NOTE: Min Capture t[(0, 0)] -> ByValue diff --git a/tests/ui/closures/2229_closure_analysis/capture-analysis-1.rs b/tests/ui/closures/2229_closure_analysis/capture-analysis-1.rs index 0c42e66a2faf4..3eb5cef30056d 100644 --- a/tests/ui/closures/2229_closure_analysis/capture-analysis-1.rs +++ b/tests/ui/closures/2229_closure_analysis/capture-analysis-1.rs @@ -17,8 +17,8 @@ fn main() { //~| NOTE: see issue #15701 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: println!("{:?}", p); //~^ NOTE: Capturing p[] -> Immutable //~| NOTE: Min Capture p[] -> Immutable diff --git a/tests/ui/closures/2229_closure_analysis/capture-analysis-2.rs b/tests/ui/closures/2229_closure_analysis/capture-analysis-2.rs index adb618d1771e7..e6cda82480937 100644 --- a/tests/ui/closures/2229_closure_analysis/capture-analysis-2.rs +++ b/tests/ui/closures/2229_closure_analysis/capture-analysis-2.rs @@ -16,8 +16,8 @@ fn main() { //~| NOTE: see issue #15701 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: let _x = p.x; //~^ NOTE: Capturing p[(0, 0)] -> ByValue //~| NOTE: p[] captured as ByValue here diff --git a/tests/ui/closures/2229_closure_analysis/capture-analysis-3.rs b/tests/ui/closures/2229_closure_analysis/capture-analysis-3.rs index 0a21eaaaa1214..b25b613b61c02 100644 --- a/tests/ui/closures/2229_closure_analysis/capture-analysis-3.rs +++ b/tests/ui/closures/2229_closure_analysis/capture-analysis-3.rs @@ -21,8 +21,8 @@ fn main() { //~| NOTE: see issue #15701 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: let _x = a.b.c; //~^ NOTE: Capturing a[(0, 0),(0, 0)] -> ByValue //~| NOTE: a[(0, 0)] captured as ByValue here diff --git a/tests/ui/closures/2229_closure_analysis/capture-analysis-4.rs b/tests/ui/closures/2229_closure_analysis/capture-analysis-4.rs index 790dad0710bc8..355e36c1463be 100644 --- a/tests/ui/closures/2229_closure_analysis/capture-analysis-4.rs +++ b/tests/ui/closures/2229_closure_analysis/capture-analysis-4.rs @@ -21,8 +21,8 @@ fn main() { //~| NOTE: see issue #15701 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: let _x = a.b; //~^ NOTE: Capturing a[(0, 0)] -> ByValue //~| NOTE: Min Capture a[(0, 0)] -> ByValue diff --git a/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs index af12e0b259d9a..52f0dcba6bee9 100644 --- a/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs +++ b/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs @@ -15,8 +15,8 @@ fn main() { //~| NOTE: see issue #15701 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: println!("{}", p.x); //~^ NOTE: Capturing p[(0, 0)] -> Immutable //~| NOTE: Min Capture p[(0, 0)] -> Immutable diff --git a/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs index ccd260492643e..bac79ad2860f7 100644 --- a/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs +++ b/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -10,8 +10,8 @@ fn main() { //~| NOTE: see issue #15701 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: println!("{}", t.0); //~^ NOTE: Capturing t[(0, 0)] -> Immutable //~| NOTE: Min Capture t[(0, 0)] -> Immutable diff --git a/tests/ui/closures/2229_closure_analysis/capture-enums.rs b/tests/ui/closures/2229_closure_analysis/capture-enums.rs index b1e21bd0f8d61..d9c06a68c95b9 100644 --- a/tests/ui/closures/2229_closure_analysis/capture-enums.rs +++ b/tests/ui/closures/2229_closure_analysis/capture-enums.rs @@ -18,8 +18,8 @@ fn multi_variant_enum() { //~| NOTE: see issue #15701 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: if let Info::Point(_, _, str) = point { //~^ NOTE: Capturing point[] -> Immutable //~| NOTE: Capturing point[(2, 0)] -> ByValue @@ -50,8 +50,8 @@ fn single_variant_enum() { //~| NOTE: see issue #15701 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: let SingleVariant::Point(_, _, str) = point; //~^ NOTE: Capturing point[(2, 0)] -> ByValue //~| NOTE: Min Capture point[(2, 0)] -> ByValue diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/union.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/union.rs index 647005bc1c970..c036712381ce0 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/union.rs +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/union.rs @@ -11,15 +11,15 @@ union A { fn main() { let mut a = A { y: 1 }; let mut c = || { - //~^ `a.y` is borrowed here + //~^ NOTE `a.y` is borrowed here let _ = unsafe { &a.y }; let _ = &mut a; - //~^ borrow occurs due to use in closure + //~^ NOTE borrow occurs due to use in closure let _ = unsafe { &mut a.y }; }; a.y = 1; - //~^ cannot assign to `a.y` because it is borrowed [E0506] - //~| `a.y` is assigned to here + //~^ ERROR cannot assign to `a.y` because it is borrowed [E0506] + //~| NOTE `a.y` is assigned to here c(); - //~^ borrow later used here + //~^ NOTE borrow later used here } diff --git a/tests/ui/closures/2229_closure_analysis/issue-89606.rs b/tests/ui/closures/2229_closure_analysis/issue-89606.rs index 8c88a4b82261f..5494686356d53 100644 --- a/tests/ui/closures/2229_closure_analysis/issue-89606.rs +++ b/tests/ui/closures/2229_closure_analysis/issue-89606.rs @@ -2,8 +2,8 @@ // //@ check-pass //@ revisions: twenty_eighteen twenty_twentyone -//@ [twenty_eighteen]compile-flags: --edition 2018 -//@ [twenty_twentyone]compile-flags: --edition 2021 +//@ [twenty_eighteen] edition: 2018 +//@ [twenty_twentyone] edition: 2021 struct S<'a>(Option<&'a mut i32>); diff --git a/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs index 9c7b70457d98e..40330af4088c2 100644 --- a/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs +++ b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs @@ -10,8 +10,8 @@ fn test_1_should_capture() { let variant = Some(2229); let c = #[rustc_capture_analysis] || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: match variant { //~^ NOTE: Capturing variant[] -> Immutable //~| NOTE: Min Capture variant[] -> Immutable @@ -28,7 +28,7 @@ fn test_2_should_not_capture() { let variant = Some(2229); let c = #[rustc_capture_analysis] || { - //~^ First Pass analysis includes: + //~^ ERROR First Pass analysis includes: match variant { _ => {} } @@ -47,7 +47,7 @@ fn test_3_should_not_capture_single_variant() { let variant = SingleVariant::Points(1); let c = #[rustc_capture_analysis] || { - //~^ First Pass analysis includes: + //~^ ERROR First Pass analysis includes: match variant { SingleVariant::Points(_) => {} } @@ -61,8 +61,8 @@ fn test_6_should_capture_single_variant() { let variant = SingleVariant::Points(1); let c = #[rustc_capture_analysis] || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: match variant { //~^ NOTE: Capturing variant[] -> Immutable //~| NOTE: Capturing variant[(0, 0)] -> Immutable @@ -81,7 +81,7 @@ fn test_4_should_not_capture_array() { let array: [i32; 3] = [0; 3]; let c = #[rustc_capture_analysis] || { - //~^ First Pass analysis includes: + //~^ ERROR First Pass analysis includes: match array { [_,_,_] => {} } @@ -93,7 +93,7 @@ fn test_4_should_not_capture_array() { let array: &[i32; 3] = &[0; 3]; let c = #[rustc_capture_analysis] || { - //~^ First Pass analysis includes: + //~^ ERROR First Pass analysis includes: match array { [_, _, _] => {} } @@ -106,7 +106,7 @@ fn test_4_should_not_capture_array() { let f = &Foo(&[10; 3]); let c = #[rustc_capture_analysis] || { - //~^ First Pass analysis includes: + //~^ ERROR First Pass analysis includes: match f { Foo([_, _, _]) => () } @@ -128,8 +128,8 @@ fn test_5_should_capture_multi_variant() { let variant = MVariant::A; let c = #[rustc_capture_analysis] || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: match variant { //~^ NOTE: Capturing variant[] -> Immutable //~| NOTE: Min Capture variant[] -> Immutable @@ -146,8 +146,8 @@ fn test_7_should_capture_slice_len() { let slice: &[i32] = &[1, 2, 3]; let c = #[rustc_capture_analysis] || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: match slice { //~^ NOTE: Capturing slice[] -> Immutable //~| NOTE: Min Capture slice[] -> Immutable @@ -158,8 +158,8 @@ fn test_7_should_capture_slice_len() { c(); let c = #[rustc_capture_analysis] || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: match slice { //~^ NOTE: Capturing slice[] -> Immutable //~| NOTE: Min Capture slice[] -> Immutable @@ -170,8 +170,8 @@ fn test_7_should_capture_slice_len() { c(); let c = #[rustc_capture_analysis] || { - //~^ First Pass analysis includes: - //~| Min Capture analysis includes: + //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: match slice { //~^ NOTE: Capturing slice[] -> Immutable //~| NOTE: Min Capture slice[] -> Immutable @@ -187,7 +187,7 @@ fn test_8_capture_slice_wild() { let slice: &[i32] = &[1, 2, 3]; let c = #[rustc_capture_analysis] || { - //~^ First Pass analysis includes: + //~^ ERROR First Pass analysis includes: match slice { [..] => {}, _ => {} diff --git a/tests/ui/closures/2229_closure_analysis/move_closure.rs b/tests/ui/closures/2229_closure_analysis/move_closure.rs index b6690d060118e..c681559f61904 100644 --- a/tests/ui/closures/2229_closure_analysis/move_closure.rs +++ b/tests/ui/closures/2229_closure_analysis/move_closure.rs @@ -181,9 +181,9 @@ fn box_mut_1() { //~^ ERROR: attributes on expressions are experimental //~| NOTE: see issue #15701 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - //~| First Pass analysis includes: + //~| ERROR First Pass analysis includes: //~| NOTE: Capturing box_p_foo[Deref,Deref,(0, 0)] -> Mutable - //~| Min Capture analysis includes: + //~| ERROR Min Capture analysis includes: //~| NOTE: Min Capture box_p_foo[] -> ByValue } @@ -199,9 +199,9 @@ fn box_mut_2() { //~^ ERROR: attributes on expressions are experimental //~| NOTE: see issue #15701 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - //~| First Pass analysis includes: + //~| ERROR First Pass analysis includes: //~| NOTE: Capturing p_foo[Deref,Deref,(0, 0)] -> Mutable - //~| Min Capture analysis includes: + //~| ERROR Min Capture analysis includes: //~| NOTE: Min Capture p_foo[] -> ByValue } @@ -213,9 +213,9 @@ fn returned_closure_owns_copy_type_data() -> impl Fn() -> i32 { //~^ ERROR: attributes on expressions are experimental //~| NOTE: see issue #15701 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - //~| First Pass analysis includes: + //~| ERROR First Pass analysis includes: //~| NOTE: Capturing x[] -> Immutable - //~| Min Capture analysis includes: + //~| ERROR Min Capture analysis includes: //~| NOTE: Min Capture x[] -> ByValue c diff --git a/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs b/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs index 4fc2e6c903a93..a771b815702ad 100644 --- a/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs +++ b/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs @@ -1,8 +1,8 @@ //@ run-pass //@ check-run-results //@ revisions: twenty_eighteen twenty_twentyone -//@ [twenty_eighteen]compile-flags: --edition 2018 -//@ [twenty_twentyone]compile-flags: --edition 2021 +//@ [twenty_eighteen] edition: 2018 +//@ [twenty_twentyone] edition: 2021 #[derive(Debug)] struct Dropable(&'static str); diff --git a/tests/ui/closures/binder/implicit-return.rs b/tests/ui/closures/binder/implicit-return.rs index d34e5721d919b..9889d055a3661 100644 --- a/tests/ui/closures/binder/implicit-return.rs +++ b/tests/ui/closures/binder/implicit-return.rs @@ -2,5 +2,5 @@ fn main() { let _f = for<'a> |_: &'a ()| {}; - //~^ implicit types in closure signatures are forbidden when `for<...>` is present + //~^ ERROR implicit types in closure signatures are forbidden when `for<...>` is present } diff --git a/tests/ui/closures/closure-return-type-mismatch.stderr b/tests/ui/closures/closure-return-type-mismatch.stderr index 052bbbb5ed579..f9587d8dad1de 100644 --- a/tests/ui/closures/closure-return-type-mismatch.stderr +++ b/tests/ui/closures/closure-return-type-mismatch.stderr @@ -1,11 +1,3 @@ -error[E0308]: mismatched types - --> $DIR/closure-return-type-mismatch.rs:20:41 - | -LL | static FOO: fn() -> bool = || -> bool { 1 }; - | ---- ^ expected `bool`, found integer - | | - | expected `bool` because of return type - error[E0308]: mismatched types --> $DIR/closure-return-type-mismatch.rs:7:9 | @@ -27,6 +19,14 @@ LL | if false { LL | return "hello" | ^^^^^^^ expected `bool`, found `&str` +error[E0308]: mismatched types + --> $DIR/closure-return-type-mismatch.rs:20:41 + | +LL | static FOO: fn() -> bool = || -> bool { 1 }; + | ---- ^ expected `bool`, found integer + | | + | expected `bool` because of return type + error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.rs b/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.rs index deb888ec28673..f8e3b14f92769 100644 --- a/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.rs +++ b/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.rs @@ -16,7 +16,7 @@ impl<'a, T> Foo<'a> for Wrap where T: Fn(&'a i32) {} fn main() { needs_foo(|x| { - //[current]~^ implementation of `Foo` is not general enough + //[current]~^ ERROR implementation of `Foo` is not general enough //[next]~^^ ERROR type annotations needed x.to_string(); }); diff --git a/tests/ui/closures/issue-78720.rs b/tests/ui/closures/issue-78720.rs index 81af030fe5568..a615cdf269021 100644 --- a/tests/ui/closures/issue-78720.rs +++ b/tests/ui/closures/issue-78720.rs @@ -1,5 +1,6 @@ fn server() -> impl { //~^ ERROR at least one trait must be specified + //~^^ ERROR type annotations needed ().map2(|| "") } diff --git a/tests/ui/closures/issue-78720.stderr b/tests/ui/closures/issue-78720.stderr index 90672cd83d7cf..3e95fab441ade 100644 --- a/tests/ui/closures/issue-78720.stderr +++ b/tests/ui/closures/issue-78720.stderr @@ -5,7 +5,7 @@ LL | fn server() -> impl { | ^^^^ error[E0412]: cannot find type `F` in this scope - --> $DIR/issue-78720.rs:13:12 + --> $DIR/issue-78720.rs:14:12 | LL | _func: F, | ^ @@ -22,8 +22,14 @@ help: you might be missing a type parameter LL | struct Map2 { | +++ +error[E0282]: type annotations needed + --> $DIR/issue-78720.rs:1:16 + | +LL | fn server() -> impl { + | ^^^^ cannot infer type + error[E0308]: mismatched types - --> $DIR/issue-78720.rs:7:39 + --> $DIR/issue-78720.rs:8:39 | LL | fn map2(self, f: F) -> Map2 {} | ^^ expected `Map2`, found `()` @@ -32,7 +38,7 @@ LL | fn map2(self, f: F) -> Map2 {} found unit type `()` error[E0277]: the size for values of type `Self` cannot be known at compilation time - --> $DIR/issue-78720.rs:7:16 + --> $DIR/issue-78720.rs:8:16 | LL | fn map2(self, f: F) -> Map2 {} | ^^^^ doesn't have a size known at compile-time @@ -47,7 +53,7 @@ help: function arguments must have a statically known size, borrowed types alway LL | fn map2(&self, f: F) -> Map2 {} | + -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -Some errors have detailed explanations: E0277, E0308, E0412. +Some errors have detailed explanations: E0277, E0282, E0308, E0412. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/closures/opaque-upvar.rs b/tests/ui/closures/opaque-upvar.rs new file mode 100644 index 0000000000000..90e7c9ccb465f --- /dev/null +++ b/tests/ui/closures/opaque-upvar.rs @@ -0,0 +1,19 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for . +// This is only an issue in the new solver, but I'm testing it in both solvers for now. +// This has to do with the fact that the recursive `walk_dir` is a revealing use, which has not +// yet been constrained from the defining use by the time that closure signature inference is +// performed. We don't really care, though, since anywhere we structurally match on a type in +// upvar analysis, we already call `structurally_resolve_type` right before `.kind()`. + +fn walk_dir(cb: &()) -> impl Sized { + || { + let fut = walk_dir(cb); + }; +} + +fn main() {} diff --git a/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr b/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr index b71cfc503339c..bcf5759a194f0 100644 --- a/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr +++ b/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr @@ -9,7 +9,7 @@ LL | let c1 : () = c; | expected due to this | = note: expected unit type `()` - found closure `{mod1::f::{closure#0} closure_kind_ty=?8t closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=?7t}` + found closure `{mod1::f::{closure#0} closure_kind_ty=?7t closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=?6t}` help: use parentheses to call this closure | LL | let c1 : () = c(); diff --git a/tests/ui/closures/print/closure-print-generic-verbose-2.stderr b/tests/ui/closures/print/closure-print-generic-verbose-2.stderr index 88f4dc9b92ab4..a8d2b07a00dcb 100644 --- a/tests/ui/closures/print/closure-print-generic-verbose-2.stderr +++ b/tests/ui/closures/print/closure-print-generic-verbose-2.stderr @@ -9,7 +9,7 @@ LL | let c1 : () = c; | expected due to this | = note: expected unit type `()` - found closure `{f::{closure#0} closure_kind_ty=?8t closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=?7t}` + found closure `{f::{closure#0} closure_kind_ty=?7t closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=?6t}` help: use parentheses to call this closure | LL | let c1 : () = c(); diff --git a/tests/ui/closures/upvar-or-pattern-issue-138958.rs b/tests/ui/closures/upvar-or-pattern-issue-138958.rs new file mode 100644 index 0000000000000..fe9ebc0a7e69c --- /dev/null +++ b/tests/ui/closures/upvar-or-pattern-issue-138958.rs @@ -0,0 +1,11 @@ +//@ edition:2024 +//@ check-pass + +pub fn f(x: (u32, u32)) { + let _ = || { + let ((0, a) | (a, _)) = x; + a + }; +} + +fn main() {} diff --git a/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs b/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs index ce575697cf684..c72d19e84816d 100644 --- a/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs +++ b/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs @@ -21,9 +21,9 @@ fn func(_f: impl Fn()) -> usize { fn test_func(s: &S) -> usize { let mut x = (); s.assoc_func(|| x = ()); - //~^ cannot assign to `x`, as it is a captured variable in a `Fn` closure + //~^ ERROR cannot assign to `x`, as it is a captured variable in a `Fn` closure func(|| x = ()) - //~^ cannot assign to `x`, as it is a captured variable in a `Fn` closure + //~^ ERROR cannot assign to `x`, as it is a captured variable in a `Fn` closure } fn main() {} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr index 63260b5c78fdc..beb0ab70cc7e5 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr @@ -11,3 +11,13 @@ LL | core::mem::transmute:: $DIR/gate_test.rs:5:39 + | +LL | core::mem::transmute:: i32>( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + diff --git a/tests/ui/cfguard-run.rs b/tests/ui/codegen/cfguard-run.rs similarity index 100% rename from tests/ui/cfguard-run.rs rename to tests/ui/codegen/cfguard-run.rs diff --git a/tests/ui/codegen/duplicated-path-in-error.rs b/tests/ui/codegen/duplicated-path-in-error.rs index cff20dd9bd639..a446395de208f 100644 --- a/tests/ui/codegen/duplicated-path-in-error.rs +++ b/tests/ui/codegen/duplicated-path-in-error.rs @@ -5,3 +5,5 @@ // the path of the dylib. fn main() {} + +//~? ERROR couldn't load codegen backend /non-existing-one.so diff --git a/tests/ui/codegen/empty-static-libs-issue-108825.rs b/tests/ui/codegen/empty-static-libs-issue-108825.rs new file mode 100644 index 0000000000000..4c644be0954e3 --- /dev/null +++ b/tests/ui/codegen/empty-static-libs-issue-108825.rs @@ -0,0 +1,18 @@ +//! Test that linking a no_std application still outputs the +//! `native-static-libs: ` note, even though it is empty. + +//@ compile-flags: -Cpanic=abort --print=native-static-libs +//@ build-pass +//@ dont-check-compiler-stderr (libcore links `/defaultlib:msvcrt` or `/defaultlib:libcmt` on MSVC) +//@ ignore-pass (the note is emitted later in the compilation pipeline, needs build) + +#![crate_type = "staticlib"] +#![no_std] + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +//~? NOTE native-static-libs: +//~? NOTE Link against the following native artifacts when linking against this static library diff --git a/tests/ui/codegen/equal-pointers-unequal/as-cast/basic.rs b/tests/ui/codegen/equal-pointers-unequal/as-cast/basic.rs deleted file mode 100644 index e2a00ce173d14..0000000000000 --- a/tests/ui/codegen/equal-pointers-unequal/as-cast/basic.rs +++ /dev/null @@ -1,21 +0,0 @@ -//@ known-bug: #107975 -//@ compile-flags: -Copt-level=2 -//@ run-pass - -fn main() { - let a: usize = { - let v = 0u8; - &v as *const _ as usize - }; - let b: usize = { - let v = 0u8; - &v as *const _ as usize - }; - - // `a` and `b` are not equal. - assert_ne!(a, b); - // But they are the same number. - assert_eq!(format!("{a}"), format!("{b}")); - // And they are equal. - assert_eq!(a, b); -} diff --git a/tests/ui/codegen/equal-pointers-unequal/as-cast/function.rs b/tests/ui/codegen/equal-pointers-unequal/as-cast/function.rs deleted file mode 100644 index 15434de50f76c..0000000000000 --- a/tests/ui/codegen/equal-pointers-unequal/as-cast/function.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ known-bug: #107975 -//@ compile-flags: -Copt-level=2 -//@ run-pass - -// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1434203908 - -fn f() -> usize { - let v = 0; - &v as *const _ as usize -} - -fn main() { - let a = f(); - let b = f(); - - // `a` and `b` are not equal. - assert_ne!(a, b); - // But they are the same number. - assert_eq!(format!("{a}"), format!("{b}")); - // And they are equal. - assert_eq!(a, b); -} diff --git a/tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs b/tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs index 9a1ace86e4db8..5ec3c7cbdf55b 100644 --- a/tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs +++ b/tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs @@ -23,7 +23,7 @@ fn main() { let v = 0; &v as *const _ as usize }; - assert_eq!(a.to_string(), b.to_string()); + assert_eq!(format!("{a}"), format!("{b}")); assert_eq!(format!("{}", a == b), "true"); assert_eq!(format!("{}", cmp_in(a, b)), "true"); assert_eq!(format!("{}", cmp(a, b)), "true"); diff --git a/tests/ui/codegen/equal-pointers-unequal/as-cast/print.rs b/tests/ui/codegen/equal-pointers-unequal/as-cast/print.rs deleted file mode 100644 index f33a9e511b61a..0000000000000 --- a/tests/ui/codegen/equal-pointers-unequal/as-cast/print.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ known-bug: #107975 -//@ compile-flags: -Copt-level=2 -//@ run-pass - -// https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499 - -fn main() { - let a = { - let v = 0; - &v as *const _ as usize - }; - let b = { - let v = 0; - &v as *const _ as usize - }; - - assert_ne!(a, b); - println!("{a}"); // or b - assert_eq!(a, b); -} diff --git a/tests/ui/codegen/equal-pointers-unequal/as-cast/print3.rs b/tests/ui/codegen/equal-pointers-unequal/as-cast/print3.rs deleted file mode 100644 index eda83e999a528..0000000000000 --- a/tests/ui/codegen/equal-pointers-unequal/as-cast/print3.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@ known-bug: #107975 -//@ compile-flags: -Copt-level=2 -//@ run-pass - -// https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499 - -fn main() { - let a = { - let v = 0; - &v as *const _ as usize - }; - let b = { - let v = 0; - &v as *const _ as usize - }; - - assert_ne!(a, b); - assert_ne!(a, b); - let c = a; - assert_eq!(format!("{} {} {}", a == b, a == c, b == c), "false true false"); - println!("{a} {b}"); - assert_eq!(format!("{} {} {}", a == b, a == c, b == c), "true true true"); -} diff --git a/tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs b/tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs index d1aa95a9a569d..731c5b67882bd 100644 --- a/tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs +++ b/tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs @@ -21,7 +21,7 @@ fn main() { // It's not zero, which means `a` and `b` are not equal. assert_ne!(i, 0); // But it looks like zero... - assert_eq!(i.to_string(), "0"); + assert_eq!(format!("{i}"), "0"); // ...and now it *is* zero? assert_eq!(i, 0); // So `a` and `b` are equal after all? diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs deleted file mode 100644 index b2b4934aa5f18..0000000000000 --- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@ known-bug: #107975 -//@ compile-flags: -Copt-level=2 -//@ run-pass - -use std::ptr; - -fn main() { - let a: usize = { - let v = 0u8; - ptr::from_ref(&v).expose_provenance() - }; - let b: usize = { - let v = 0u8; - ptr::from_ref(&v).expose_provenance() - }; - - // `a` and `b` are not equal. - assert_ne!(a, b); - // But they are the same number. - assert_eq!(format!("{a}"), format!("{b}")); - // And they are equal. - assert_eq!(a, b); -} diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs deleted file mode 100644 index bf130c9f75982..0000000000000 --- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ known-bug: #107975 -//@ compile-flags: -Copt-level=2 -//@ run-pass - -// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1434203908 - -use std::ptr; - -fn f() -> usize { - let v = 0; - ptr::from_ref(&v).expose_provenance() -} - -fn main() { - let a = f(); - let b = f(); - - // `a` and `b` are not equal. - assert_ne!(a, b); - // But they are the same number. - assert_eq!(format!("{a}"), format!("{b}")); - // And they are equal. - assert_eq!(a, b); -} diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs index f128e1bb0841a..94739708ab8bd 100644 --- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs +++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs @@ -25,7 +25,7 @@ fn main() { let v = 0; ptr::from_ref(&v).expose_provenance() }; - assert_eq!(a.to_string(), b.to_string()); + assert_eq!(format!("{a}"), format!("{b}")); assert_eq!(format!("{}", a == b), "true"); assert_eq!(format!("{}", cmp_in(a, b)), "true"); assert_eq!(format!("{}", cmp(a, b)), "true"); diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs deleted file mode 100644 index 0baf8886395e8..0000000000000 --- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ known-bug: #107975 -//@ compile-flags: -Copt-level=2 -//@ run-pass - -// https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499 - -use std::ptr; - -fn main() { - let a: usize = { - let v = 0; - ptr::from_ref(&v).expose_provenance() - }; - let b: usize = { - let v = 0; - ptr::from_ref(&v).expose_provenance() - }; - - assert_ne!(a, b); - println!("{a}"); // or b - assert_eq!(a, b); -} diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print3.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print3.rs deleted file mode 100644 index c7f46318aaef7..0000000000000 --- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print3.rs +++ /dev/null @@ -1,25 +0,0 @@ -//@ known-bug: #107975 -//@ compile-flags: -Copt-level=2 -//@ run-pass - -// https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499 - -use std::ptr; - -fn main() { - let a: usize = { - let v = 0; - ptr::from_ref(&v).expose_provenance() - }; - let b: usize = { - let v = 0; - ptr::from_ref(&v).expose_provenance() - }; - - assert_ne!(a, b); - assert_ne!(a, b); - let c = a; - assert_eq!(format!("{} {} {}", a == b, a == c, b == c), "false true false"); - println!("{a} {b}"); - assert_eq!(format!("{} {} {}", a == b, a == c, b == c), "true true true"); -} diff --git a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs index 7ccff8d0848e9..b7824f53d77f8 100644 --- a/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs +++ b/tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs @@ -23,7 +23,7 @@ fn main() { // It's not zero, which means `a` and `b` are not equal. assert_ne!(i, 0); // But it looks like zero... - assert_eq!(i.to_string(), "0"); + assert_eq!(format!("{i}"), "0"); // ...and now it *is* zero? assert_eq!(i, 0); // So `a` and `b` are equal after all? diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs deleted file mode 100644 index 4602ec654d107..0000000000000 --- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@ known-bug: #107975 -//@ compile-flags: -Copt-level=2 -//@ run-pass - -use std::ptr; - -fn main() { - let a: usize = { - let v = 0u8; - ptr::from_ref(&v).addr() - }; - let b: usize = { - let v = 0u8; - ptr::from_ref(&v).addr() - }; - - // `a` and `b` are not equal. - assert_ne!(a, b); - // But they are the same number. - assert_eq!(format!("{a}"), format!("{b}")); - // And they are equal. - assert_eq!(a, b); -} diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs deleted file mode 100644 index 789a78c15b4e2..0000000000000 --- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ known-bug: #107975 -//@ compile-flags: -Copt-level=2 -//@ run-pass - -// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1434203908 - -use std::ptr; - -fn f() -> usize { - let v = 0; - ptr::from_ref(&v).addr() -} - -fn main() { - let a = f(); - let b = f(); - - // `a` and `b` are not equal. - assert_ne!(a, b); - // But they are the same number. - assert_eq!(format!("{a}"), format!("{b}")); - // And they are equal. - assert_eq!(a, b); -} diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs index 0414879804a5a..0f838af1fb190 100644 --- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs +++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs @@ -25,7 +25,7 @@ fn main() { let v = 0; ptr::from_ref(&v).addr() }; - assert_eq!(a.to_string(), b.to_string()); + assert_eq!(format!("{a}"), format!("{b}")); assert_eq!(format!("{}", a == b), "true"); assert_eq!(format!("{}", cmp_in(a, b)), "true"); assert_eq!(format!("{}", cmp(a, b)), "true"); diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs deleted file mode 100644 index b7165ce1e5ce3..0000000000000 --- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ known-bug: #107975 -//@ compile-flags: -Copt-level=2 -//@ run-pass - -// https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499 - -use std::ptr; - -fn main() { - let a: usize = { - let v = 0; - ptr::from_ref(&v).addr() - }; - let b: usize = { - let v = 0; - ptr::from_ref(&v).addr() - }; - - assert_ne!(a, b); - println!("{a}"); // or b - assert_eq!(a, b); -} diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print3.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print3.rs deleted file mode 100644 index a02ff30918daf..0000000000000 --- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/print3.rs +++ /dev/null @@ -1,25 +0,0 @@ -//@ known-bug: #107975 -//@ compile-flags: -Copt-level=2 -//@ run-pass - -// https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499 - -use std::ptr; - -fn main() { - let a: usize = { - let v = 0; - ptr::from_ref(&v).addr() - }; - let b: usize = { - let v = 0; - ptr::from_ref(&v).addr() - }; - - assert_ne!(a, b); - assert_ne!(a, b); - let c = a; - assert_eq!(format!("{} {} {}", a == b, a == c, b == c), "false true false"); - println!("{a} {b}"); - assert_eq!(format!("{} {} {}", a == b, a == c, b == c), "true true true"); -} diff --git a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs index d963e45e4cdf2..20ed991ed3d66 100644 --- a/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs +++ b/tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs @@ -23,7 +23,7 @@ fn main() { // It's not zero, which means `a` and `b` are not equal. assert_ne!(i, 0); // But it looks like zero... - assert_eq!(i.to_string(), "0"); + assert_eq!(format!("{i}"), "0"); // ...and now it *is* zero? assert_eq!(i, 0); // So `a` and `b` are equal after all? diff --git a/tests/ui/codegen/mismatched-data-layouts.rs b/tests/ui/codegen/mismatched-data-layouts.rs index 955f917ee3328..194bcaa307f24 100644 --- a/tests/ui/codegen/mismatched-data-layouts.rs +++ b/tests/ui/codegen/mismatched-data-layouts.rs @@ -3,7 +3,6 @@ //@ build-fail //@ needs-llvm-components: x86 //@ compile-flags: --crate-type=lib --target={{src-base}}/codegen/mismatched-data-layout.json -Z unstable-options -//@ error-pattern: differs from LLVM target's //@ normalize-stderr: "`, `[A-Za-z0-9-:]*`" -> "`, `normalized data layout`" //@ normalize-stderr: "layout, `[A-Za-z0-9-:]*`" -> "layout, `normalized data layout`" @@ -12,3 +11,5 @@ #[lang = "sized"] trait Sized {} + +//~? ERROR differs from LLVM target's diff --git a/tests/ui/codegen/mono-impossible-drop.rs b/tests/ui/codegen/mono-impossible-drop.rs index dec013cfe54b3..c8a9554da4366 100644 --- a/tests/ui/codegen/mono-impossible-drop.rs +++ b/tests/ui/codegen/mono-impossible-drop.rs @@ -1,3 +1,6 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver //@ compile-flags: -Clink-dead-code=on --crate-type=lib //@ build-pass diff --git a/tests/ui/codegen/no-mangle-on-internal-lang-items.rs b/tests/ui/codegen/no-mangle-on-internal-lang-items.rs new file mode 100644 index 0000000000000..37766936410ed --- /dev/null +++ b/tests/ui/codegen/no-mangle-on-internal-lang-items.rs @@ -0,0 +1,14 @@ +// Issue a error when the user uses #[no_mangle] on internal language items +//@ edition:2024 + +#![feature(rustc_attrs)] + +#[rustc_std_internal_symbol] +#[unsafe(no_mangle)] //~ERROR `#[no_mangle]` cannot be used on internal language items +fn internal_lang_function () { + +} + +fn main() { + +} diff --git a/tests/ui/codegen/no-mangle-on-internal-lang-items.stderr b/tests/ui/codegen/no-mangle-on-internal-lang-items.stderr new file mode 100644 index 0000000000000..12461a6abb964 --- /dev/null +++ b/tests/ui/codegen/no-mangle-on-internal-lang-items.stderr @@ -0,0 +1,12 @@ +error: `#[no_mangle]` cannot be used on internal language items + --> $DIR/no-mangle-on-internal-lang-items.rs:7:1 + | +LL | #[unsafe(no_mangle)] + | ^^^^^^^^^^^^^^^^^^^^ +LL | fn internal_lang_function () { + | ---------------------------- should be the internal language item + | + = note: Rustc requires this item to have a specific mangled name. + +error: aborting due to 1 previous error + diff --git a/tests/ui/codegen/no-mangle-on-panic-handler.rs b/tests/ui/codegen/no-mangle-on-panic-handler.rs new file mode 100644 index 0000000000000..1dc0cce0a2ece --- /dev/null +++ b/tests/ui/codegen/no-mangle-on-panic-handler.rs @@ -0,0 +1,14 @@ +// Issue an error when the user uses #[no_mangle] on the panic handler +//@ edition:2024 + +#![crate_type="lib"] +#![no_std] +#![no_main] + +use core::panic::PanicInfo; + +#[unsafe(no_mangle)] //~ ERROR `#[no_mangle]` cannot be used on internal language items +#[panic_handler] +pub unsafe fn panic_fmt(pi: &PanicInfo) -> ! { + loop {} +} diff --git a/tests/ui/codegen/no-mangle-on-panic-handler.stderr b/tests/ui/codegen/no-mangle-on-panic-handler.stderr new file mode 100644 index 0000000000000..dc88b66d1b5d7 --- /dev/null +++ b/tests/ui/codegen/no-mangle-on-panic-handler.stderr @@ -0,0 +1,16 @@ +error: `#[no_mangle]` cannot be used on internal language items + --> $DIR/no-mangle-on-panic-handler.rs:10:1 + | +LL | #[unsafe(no_mangle)] + | ^^^^^^^^^^^^^^^^^^^^ +LL | #[panic_handler] +LL | pub unsafe fn panic_fmt(pi: &PanicInfo) -> ! { + | -------------------------------------------- should be the internal language item + | + = note: Rustc requires this item to have a specific mangled name. + = note: If you are trying to prevent mangling to ease debugging, many + = note: debuggers support a command such as `rbreak rust_begin_unwind` to + = note: match `.*rust_begin_unwind.*` instead of `break rust_begin_unwind` on a specific name + +error: aborting due to 1 previous error + diff --git a/tests/ui/codegen/overflow-during-mono.rs b/tests/ui/codegen/overflow-during-mono.rs index 83a8b6b3ef6a2..a9045840173e7 100644 --- a/tests/ui/codegen/overflow-during-mono.rs +++ b/tests/ui/codegen/overflow-during-mono.rs @@ -1,4 +1,4 @@ -//~ ERROR overflow evaluating the requirement `{closure@$DIR/overflow-during-mono.rs:13:41: 13:44}: Sized` +//~ ERROR overflow evaluating the requirement `for<'a> {closure@$DIR/overflow-during-mono.rs:13:41: 13:44}: FnMut(&'a _)` //@ build-fail #![recursion_limit = "32"] diff --git a/tests/ui/codegen/overflow-during-mono.stderr b/tests/ui/codegen/overflow-during-mono.stderr index f7a3e2df3dbaa..74d98fde285bb 100644 --- a/tests/ui/codegen/overflow-during-mono.stderr +++ b/tests/ui/codegen/overflow-during-mono.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow evaluating the requirement `{closure@$DIR/overflow-during-mono.rs:13:41: 13:44}: Sized` +error[E0275]: overflow evaluating the requirement `for<'a> {closure@$DIR/overflow-during-mono.rs:13:41: 13:44}: FnMut(&'a _)` | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "64"]` attribute to your crate (`overflow_during_mono`) = note: required for `Filter, {closure@$DIR/overflow-during-mono.rs:13:41: 13:44}>` to implement `Iterator` diff --git a/tests/ui/codegen/ref-dyn-trait-in-structs-and-enums.rs b/tests/ui/codegen/ref-dyn-trait-in-structs-and-enums.rs new file mode 100644 index 0000000000000..04548817773e3 --- /dev/null +++ b/tests/ui/codegen/ref-dyn-trait-in-structs-and-enums.rs @@ -0,0 +1,54 @@ +//! Regression test for an LLVM assertion that used to be hit when: +//! +//! - There's a generic enum contained within a tuple struct +//! - When the tuple struct is parameterized by some lifetime `'a` +//! - The enum is concretized with its type argument being a reference to a trait object (of +//! lifetime `'a`) +//! +//! Issue: + +//@ build-pass + +// Dummy trait implemented for `isize` to use in the test cases +pub trait MyTrait { + fn dummy(&self) {} +} +impl MyTrait for isize {} + +// `&dyn MyTrait` contained in enum variant +pub struct EnumRefDynTrait<'a>(Enum<&'a (dyn MyTrait + 'a)>); +pub enum Enum { + Variant(T), +} + +fn enum_dyn_trait() { + let x: isize = 42; + let y = EnumRefDynTrait(Enum::Variant(&x as &dyn MyTrait)); + let _ = y; +} + +// `&dyn MyTrait` contained behind `Option` in named field of struct +struct RefDynTraitNamed<'a> { + x: Option<&'a (dyn MyTrait + 'a)>, +} + +fn named_option_dyn_trait() { + let x: isize = 42; + let y = RefDynTraitNamed { x: Some(&x as &dyn MyTrait) }; + let _ = y; +} + +// `&dyn MyTrait` contained behind `Option` in unnamed field of struct +pub struct RefDynTraitUnnamed<'a>(Option<&'a (dyn MyTrait + 'a)>); + +fn unnamed_option_dyn_trait() { + let x: isize = 42; + let y = RefDynTraitUnnamed(Some(&x as &dyn MyTrait)); + let _ = y; +} + +pub fn main() { + enum_dyn_trait(); + named_option_dyn_trait(); + unnamed_option_dyn_trait(); +} diff --git a/tests/ui/codemap_tests/issue-11715.rs b/tests/ui/codemap_tests/issue-11715.rs index 617d57ff75a48..d0daaf444b34d 100644 --- a/tests/ui/codemap_tests/issue-11715.rs +++ b/tests/ui/codemap_tests/issue-11715.rs @@ -1,5 +1,4 @@ -#![feature(rustc_attrs)] -fn main() { #![rustc_error] // rust-lang/rust#49855 +fn main() { let mut x = "foo"; let y = &mut x; let z = &mut x; //~ ERROR cannot borrow diff --git a/tests/ui/codemap_tests/issue-11715.stderr b/tests/ui/codemap_tests/issue-11715.stderr index 5d0cf718761c4..6b330560adb90 100644 --- a/tests/ui/codemap_tests/issue-11715.stderr +++ b/tests/ui/codemap_tests/issue-11715.stderr @@ -1,5 +1,5 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time - --> $DIR/issue-11715.rs:5:13 + --> $DIR/issue-11715.rs:4:13 | LL | let y = &mut x; | ------ first mutable borrow occurs here diff --git a/tests/ui/codemap_tests/issue-28308.stderr b/tests/ui/codemap_tests/issue-28308.stderr index efd8fa22fa5ce..7bc9e05dfc024 100644 --- a/tests/ui/codemap_tests/issue-28308.stderr +++ b/tests/ui/codemap_tests/issue-28308.stderr @@ -3,8 +3,6 @@ error[E0600]: cannot apply unary operator `!` to type `&'static str` | LL | assert!("foo"); | ^^^^^^^^^^^^^^ cannot apply unary operator `!` - | - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/codemap_tests/tab_2.stderr b/tests/ui/codemap_tests/tab_2.stderr index b22c7b4266517..4f9a937155dde 100644 --- a/tests/ui/codemap_tests/tab_2.stderr +++ b/tests/ui/codemap_tests/tab_2.stderr @@ -4,7 +4,7 @@ error[E0765]: unterminated double quote string LL | """; | ___________________^ LL | | } - | |_^ + | |__^ error: aborting due to 1 previous error diff --git a/tests/ui/codemap_tests/two_files_data.rs b/tests/ui/codemap_tests/two_files_data.rs index a4e4cf7e896ed..82852f6cfbdff 100644 --- a/tests/ui/codemap_tests/two_files_data.rs +++ b/tests/ui/codemap_tests/two_files_data.rs @@ -1,4 +1,4 @@ -//@ ignore-test (auxiliary, used by other tests) +//@ ignore-auxiliary (used by `./two_files.rs`) trait Foo { } diff --git a/tests/ui/codemap_tests/unicode.expanded.stdout b/tests/ui/codemap_tests/unicode.expanded.stdout index eb53d12e94f3c..c88035de0449f 100644 --- a/tests/ui/codemap_tests/unicode.expanded.stdout +++ b/tests/ui/codemap_tests/unicode.expanded.stdout @@ -7,6 +7,7 @@ extern crate std; //@ revisions: normal expanded //@[expanded] check-pass //@[expanded]compile-flags: -Zunpretty=expanded +//@ edition: 2015 extern "路濫狼á́́" fn foo() {} diff --git a/tests/ui/codemap_tests/unicode.normal.stderr b/tests/ui/codemap_tests/unicode.normal.stderr index 0f254e0246f41..10cb34f48326e 100644 --- a/tests/ui/codemap_tests/unicode.normal.stderr +++ b/tests/ui/codemap_tests/unicode.normal.stderr @@ -1,5 +1,5 @@ error[E0703]: invalid ABI: found `路濫狼á́́` - --> $DIR/unicode.rs:5:8 + --> $DIR/unicode.rs:6:8 | LL | extern "路濫狼á́́" fn foo() {} | ^^^^^^^^^ invalid ABI diff --git a/tests/ui/codemap_tests/unicode.rs b/tests/ui/codemap_tests/unicode.rs index 73023e3c669d3..4b93ef0940697 100644 --- a/tests/ui/codemap_tests/unicode.rs +++ b/tests/ui/codemap_tests/unicode.rs @@ -1,6 +1,7 @@ //@ revisions: normal expanded //@[expanded] check-pass //@[expanded]compile-flags: -Zunpretty=expanded +//@ edition: 2015 extern "路濫狼á́́" fn foo() {} //[normal]~ ERROR invalid ABI diff --git a/tests/ui/utf8-bom.rs b/tests/ui/codemap_tests/utf8-bom.rs similarity index 100% rename from tests/ui/utf8-bom.rs rename to tests/ui/codemap_tests/utf8-bom.rs diff --git a/tests/ui/coercion/codegen-smart-pointer-with-alias.rs b/tests/ui/coercion/codegen-smart-pointer-with-alias.rs new file mode 100644 index 0000000000000..a68952bb70aa2 --- /dev/null +++ b/tests/ui/coercion/codegen-smart-pointer-with-alias.rs @@ -0,0 +1,32 @@ +//@ build-pass + +// Regression test for . + +// Make sure that the unsize coercion we collect in mono for `Signal -> Signal` +// doesn't choke on the fact that the inner unsized field of `Signal` is a (trivial) alias. +// This exercises a normalize call that is necessary since we're getting a type from the type +// system, which isn't guaranteed to be normalized after substitution. + +#![feature(coerce_unsized)] + +use std::ops::CoerceUnsized; + +trait Mirror { + type Assoc: ?Sized; +} +impl Mirror for T { + type Assoc = T; +} + +trait Any {} +impl Any for T {} + +struct Signal<'a, T: ?Sized>(<&'a T as Mirror>::Assoc); + +// This `CoerceUnsized` impl isn't special; it's a bit more restricted than we'd see in the wild, +// but this ICE also reproduces if we were to make it general over `Signal -> Signal`. +impl<'a> CoerceUnsized> for Signal<'a, i32> {} + +fn main() { + Signal(&1i32) as Signal; +} diff --git a/tests/ui/coercion/coerce-loop-issue-122561.stderr b/tests/ui/coercion/coerce-loop-issue-122561.stderr index 3af7e7cddb31d..6415fd554cba9 100644 --- a/tests/ui/coercion/coerce-loop-issue-122561.stderr +++ b/tests/ui/coercion/coerce-loop-issue-122561.stderr @@ -24,21 +24,6 @@ help: consider returning a value here LL | fn for_in_arg(a: &[(); for x in 0..2 {} /* `usize` value */]) -> bool { | +++++++++++++++++++ -error[E0308]: mismatched types - --> $DIR/coerce-loop-issue-122561.rs:85:5 - | -LL | / for i in 0.. { -LL | | -LL | | } - | |_____^ expected `i32`, found `()` - | - = note: `for` loops evaluate to unit type `()` -help: consider returning a value here - | -LL ~ } -LL + /* `i32` value */ - | - error[E0308]: mismatched types --> $DIR/coerce-loop-issue-122561.rs:4:5 | @@ -202,6 +187,21 @@ LL ~ } LL + /* `loop {}` or `panic!("...")` */ | +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:85:5 + | +LL | / for i in 0.. { +LL | | +LL | | } + | |_____^ expected `i32`, found `()` + | + = note: `for` loops evaluate to unit type `()` +help: consider returning a value here + | +LL ~ } +LL + /* `i32` value */ + | + error[E0308]: mismatched types --> $DIR/coerce-loop-issue-122561.rs:92:9 | diff --git a/tests/ui/coercion/coerce-mut.rs b/tests/ui/coercion/coerce-mut.rs index 43f0b55856d3c..d3ed0a52303c4 100644 --- a/tests/ui/coercion/coerce-mut.rs +++ b/tests/ui/coercion/coerce-mut.rs @@ -1,10 +1,12 @@ +//@ dont-require-annotations: NOTE + fn f(x: &mut i32) {} fn main() { let x = 0; f(&x); //~^ ERROR mismatched types - //~| expected mutable reference `&mut i32` - //~| found reference `&{integer}` - //~| types differ in mutability + //~| NOTE expected mutable reference `&mut i32` + //~| NOTE found reference `&{integer}` + //~| NOTE types differ in mutability } diff --git a/tests/ui/coercion/coerce-mut.stderr b/tests/ui/coercion/coerce-mut.stderr index 9bbfcc29e61ab..0e2a2d044670f 100644 --- a/tests/ui/coercion/coerce-mut.stderr +++ b/tests/ui/coercion/coerce-mut.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/coerce-mut.rs:5:7 + --> $DIR/coerce-mut.rs:7:7 | LL | f(&x); | - ^^ types differ in mutability @@ -9,7 +9,7 @@ LL | f(&x); = note: expected mutable reference `&mut i32` found reference `&{integer}` note: function defined here - --> $DIR/coerce-mut.rs:1:4 + --> $DIR/coerce-mut.rs:3:4 | LL | fn f(x: &mut i32) {} | ^ ----------- diff --git a/tests/ui/coercion/coerce-reborrow-multi-arg-fail.rs b/tests/ui/coercion/coerce-reborrow-multi-arg-fail.rs index 48be2d3146b81..0a58f0897aae5 100644 --- a/tests/ui/coercion/coerce-reborrow-multi-arg-fail.rs +++ b/tests/ui/coercion/coerce-reborrow-multi-arg-fail.rs @@ -2,5 +2,5 @@ fn test(_a: T, _b: T) {} fn main() { test(&mut 7, &7); - //~^ mismatched types + //~^ ERROR mismatched types } diff --git a/tests/ui/coercion/coercion-slice.rs b/tests/ui/coercion/coercion-slice.rs index b99235dd37f89..a0795e8316f29 100644 --- a/tests/ui/coercion/coercion-slice.rs +++ b/tests/ui/coercion/coercion-slice.rs @@ -3,5 +3,6 @@ fn main() { let _: &[i32] = [0]; //~^ ERROR mismatched types - //~| expected `&[i32]`, found `[{integer}; 1]` + //~| NOTE expected `&[i32]`, found `[{integer}; 1]` + //~| NOTE expected due to this } diff --git a/tests/ui/coercion/issue-73886.stderr b/tests/ui/coercion/issue-73886.stderr index a6f8ba65ab51a..0d4c90017cf11 100644 --- a/tests/ui/coercion/issue-73886.stderr +++ b/tests/ui/coercion/issue-73886.stderr @@ -8,9 +8,14 @@ error[E0605]: non-primitive cast: `u32` as `Option<_>` --> $DIR/issue-73886.rs:4:13 | LL | let _ = 7u32 as Option<_>; - | ^^^^^^^^^^^^^^^^^ help: consider using the `From` trait instead: `Option<_>::from(7u32)` + | ^^^^^^^^^^^^^^^^^ | = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +help: consider using the `From` trait instead + | +LL - let _ = 7u32 as Option<_>; +LL + let _ = Option::<_>::from(7u32); + | error: aborting due to 2 previous errors diff --git a/tests/ui/coercion/mut-mut-wont-coerce.rs b/tests/ui/coercion/mut-mut-wont-coerce.rs index e99566461a24b..6b00bfd583d83 100644 --- a/tests/ui/coercion/mut-mut-wont-coerce.rs +++ b/tests/ui/coercion/mut-mut-wont-coerce.rs @@ -2,6 +2,9 @@ // Making this compile was a feature request in rust-lang/rust#34117 but this is currently // "working as intended". Allowing "deep pointer coercion" seems footgun-prone, and would // require proceeding carefully. + +//@ dont-require-annotations: NOTE + use std::ops::{Deref, DerefMut}; struct Foo(i32); @@ -33,8 +36,8 @@ fn make_foo(_: *mut *mut Foo) { fn main() { let mut result: SmartPtr = SmartPtr(std::ptr::null_mut()); - make_foo(&mut &mut *result); //~ mismatched types - //~^ expected `*mut *mut Foo`, found `&mut &mut Foo` + make_foo(&mut &mut *result); //~ ERROR mismatched types + //~^ NOTE expected `*mut *mut Foo`, found `&mut &mut Foo` make_foo(out(&mut result)); // works, but makes one wonder why above coercion cannot happen } diff --git a/tests/ui/coercion/mut-mut-wont-coerce.stderr b/tests/ui/coercion/mut-mut-wont-coerce.stderr index 5daf9cbd3d360..c37ea6af5a8c9 100644 --- a/tests/ui/coercion/mut-mut-wont-coerce.stderr +++ b/tests/ui/coercion/mut-mut-wont-coerce.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/mut-mut-wont-coerce.rs:36:14 + --> $DIR/mut-mut-wont-coerce.rs:39:14 | LL | make_foo(&mut &mut *result); | -------- ^^^^^^^^^^^^^^^^^ expected `*mut *mut Foo`, found `&mut &mut Foo` @@ -9,7 +9,7 @@ LL | make_foo(&mut &mut *result); = note: expected raw pointer `*mut *mut Foo` found mutable reference `&mut &mut Foo` note: function defined here - --> $DIR/mut-mut-wont-coerce.rs:30:4 + --> $DIR/mut-mut-wont-coerce.rs:33:4 | LL | fn make_foo(_: *mut *mut Foo) { | ^^^^^^^^ ---------------- diff --git a/tests/ui/coercion/non-primitive-cast-135412.fixed b/tests/ui/coercion/non-primitive-cast-135412.fixed new file mode 100644 index 0000000000000..5cadc9368d52e --- /dev/null +++ b/tests/ui/coercion/non-primitive-cast-135412.fixed @@ -0,0 +1,10 @@ +//@ run-rustfix + +use std::sync::Arc; + +fn main() { + let _ = Option::<_>::from(7u32); + //~^ ERROR non-primitive cast: `u32` as `Option<_>` + let _ = Arc::::from("String"); + //~^ ERROR non-primitive cast: `&'static str` as `Arc` +} diff --git a/tests/ui/coercion/non-primitive-cast-135412.rs b/tests/ui/coercion/non-primitive-cast-135412.rs new file mode 100644 index 0000000000000..67a3ef340d2f1 --- /dev/null +++ b/tests/ui/coercion/non-primitive-cast-135412.rs @@ -0,0 +1,10 @@ +//@ run-rustfix + +use std::sync::Arc; + +fn main() { + let _ = 7u32 as Option<_>; + //~^ ERROR non-primitive cast: `u32` as `Option<_>` + let _ = "String" as Arc; + //~^ ERROR non-primitive cast: `&'static str` as `Arc` +} diff --git a/tests/ui/coercion/non-primitive-cast-135412.stderr b/tests/ui/coercion/non-primitive-cast-135412.stderr new file mode 100644 index 0000000000000..7e5861f83e9c1 --- /dev/null +++ b/tests/ui/coercion/non-primitive-cast-135412.stderr @@ -0,0 +1,29 @@ +error[E0605]: non-primitive cast: `u32` as `Option<_>` + --> $DIR/non-primitive-cast-135412.rs:6:13 + | +LL | let _ = 7u32 as Option<_>; + | ^^^^^^^^^^^^^^^^^ + | + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +help: consider using the `From` trait instead + | +LL - let _ = 7u32 as Option<_>; +LL + let _ = Option::<_>::from(7u32); + | + +error[E0605]: non-primitive cast: `&'static str` as `Arc` + --> $DIR/non-primitive-cast-135412.rs:8:13 + | +LL | let _ = "String" as Arc; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +help: consider using the `From` trait instead + | +LL - let _ = "String" as Arc; +LL + let _ = Arc::::from("String"); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0605`. diff --git a/tests/ui/coercion/struct-coerce-vec-to-slice.rs b/tests/ui/coercion/struct-coerce-vec-to-slice.rs new file mode 100644 index 0000000000000..9ef20ac4ea630 --- /dev/null +++ b/tests/ui/coercion/struct-coerce-vec-to-slice.rs @@ -0,0 +1,20 @@ +//! Regression test that ensures struct field literals can be coerced into slice and `Box` types + +//@ check-pass + +struct Thing1<'a> { + baz: &'a [Box], + bar: Box, +} + +struct Thing2<'a> { + baz: &'a [Box], + bar: u64, +} + +pub fn main() { + let _a = Thing1 { baz: &[], bar: Box::new(32) }; + let _b = Thing1 { baz: &Vec::new(), bar: Box::new(32) }; + let _c = Thing2 { baz: &[], bar: 32 }; + let _d = Thing2 { baz: &Vec::new(), bar: 32 }; +} diff --git a/tests/ui/coercion/struct-literal-field-type-coercion-to-expected-type.rs b/tests/ui/coercion/struct-literal-field-type-coercion-to-expected-type.rs new file mode 100644 index 0000000000000..0b8ec7dc07a73 --- /dev/null +++ b/tests/ui/coercion/struct-literal-field-type-coercion-to-expected-type.rs @@ -0,0 +1,16 @@ +//! Regression test to check that literal expressions in a struct field can be coerced to the +//! expected field type, including block expressions. +//! +//! Issue: + +//@ check-pass + +pub struct Struct { + pub field: K, +} + +static STRUCT: Struct<&'static [u8]> = Struct { field: { &[1] } }; + +static STRUCT2: Struct<&'static [u8]> = Struct { field: &[1] }; + +fn main() {} diff --git a/tests/ui/coherence/coherence-conflicting-negative-trait-impl.rs b/tests/ui/coherence/coherence-conflicting-negative-trait-impl.rs index 24b878927530c..15ca0577e4acb 100644 --- a/tests/ui/coherence/coherence-conflicting-negative-trait-impl.rs +++ b/tests/ui/coherence/coherence-conflicting-negative-trait-impl.rs @@ -8,10 +8,14 @@ struct TestType(::std::marker::PhantomData); unsafe impl Send for TestType {} -impl !Send for TestType {} //~ ERROR found both positive and negative implementation +impl !Send for TestType {} +//~^ ERROR found both positive and negative implementation +//~| ERROR `!Send` impl requires `T: MyTrait` but the struct it is implemented for does not -unsafe impl Send for TestType {} //~ ERROR conflicting implementations +unsafe impl Send for TestType {} +//~^ ERROR conflicting implementations impl !Send for TestType {} +//~^ ERROR `!Send` impls cannot be specialized fn main() {} diff --git a/tests/ui/coherence/coherence-conflicting-negative-trait-impl.stderr b/tests/ui/coherence/coherence-conflicting-negative-trait-impl.stderr index 2463f38a92251..c6aed150201d8 100644 --- a/tests/ui/coherence/coherence-conflicting-negative-trait-impl.stderr +++ b/tests/ui/coherence/coherence-conflicting-negative-trait-impl.stderr @@ -8,7 +8,7 @@ LL | impl !Send for TestType {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here error[E0119]: conflicting implementations of trait `Send` for type `TestType<_>` - --> $DIR/coherence-conflicting-negative-trait-impl.rs:13:1 + --> $DIR/coherence-conflicting-negative-trait-impl.rs:15:1 | LL | unsafe impl Send for TestType {} | ------------------------------------------------------ first implementation here @@ -16,7 +16,32 @@ LL | unsafe impl Send for TestType {} LL | unsafe impl Send for TestType {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `TestType<_>` -error: aborting due to 2 previous errors +error[E0367]: `!Send` impl requires `T: MyTrait` but the struct it is implemented for does not + --> $DIR/coherence-conflicting-negative-trait-impl.rs:11:9 + | +LL | impl !Send for TestType {} + | ^^^^^^^ + | +note: the implementor must specify the same requirement + --> $DIR/coherence-conflicting-negative-trait-impl.rs:7:1 + | +LL | struct TestType(::std::marker::PhantomData); + | ^^^^^^^^^^^^^^^^^^ + +error[E0366]: `!Send` impls cannot be specialized + --> $DIR/coherence-conflicting-negative-trait-impl.rs:18:1 + | +LL | impl !Send for TestType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `i32` is not a generic parameter +note: use the same sequence of generic lifetime, type and const parameters as the struct definition + --> $DIR/coherence-conflicting-negative-trait-impl.rs:7:1 + | +LL | struct TestType(::std::marker::PhantomData); + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0119, E0751. +Some errors have detailed explanations: E0119, E0366, E0367, E0751. For more information about an error, try `rustc --explain E0119`. diff --git a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr index 77d1bdee5acf1..bc81dd6f286dd 100644 --- a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr +++ b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr @@ -1,9 +1,3 @@ -error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1` - --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1 - | -LL | impl !Marker1 for dyn Object + Marker2 {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1` - error[E0321]: traits with a default impl, like `Marker1`, cannot be implemented for trait object `(dyn Object + Marker2 + 'static)` --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1 | @@ -12,11 +6,11 @@ LL | impl !Marker1 for dyn Object + Marker2 {} | = note: a trait object implements `Marker1` if and only if `Marker1` is one of the trait object's trait bounds -error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2` - --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:18:1 +error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1` + --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1 | -LL | impl !Marker2 for dyn Object + Marker2 {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2` +LL | impl !Marker1 for dyn Object + Marker2 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1` error[E0321]: traits with a default impl, like `Marker2`, cannot be implemented for trait object `(dyn Object + Marker2 + 'static)` --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:18:1 @@ -26,6 +20,12 @@ LL | impl !Marker2 for dyn Object + Marker2 {} | = note: a trait object implements `Marker2` if and only if `Marker2` is one of the trait object's trait bounds +error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2` + --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:18:1 + | +LL | impl !Marker2 for dyn Object + Marker2 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2` + error[E0321]: traits with a default impl, like `Marker2`, cannot be implemented for trait object `(dyn Object + 'static)` --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:26:1 | diff --git a/tests/ui/coherence/coherence-orphan.rs b/tests/ui/coherence/coherence-orphan.rs index aee6647a78819..665fa15c56e53 100644 --- a/tests/ui/coherence/coherence-orphan.rs +++ b/tests/ui/coherence/coherence-orphan.rs @@ -8,12 +8,13 @@ use lib::TheTrait; struct TheType; impl TheTrait for isize {} -//~^ ERROR E0117 +//~^ ERROR only traits defined in the current crate can be implemented for primitive types impl TheTrait for isize {} impl TheTrait for TheType {} -impl !Send for Vec {} //~ ERROR E0117 +impl !Send for Vec {} +//~^ ERROR only traits defined in the current crate can be implemented for types defined outside of the crate fn main() {} diff --git a/tests/ui/coherence/coherence-overlap-negative-impls.rs b/tests/ui/coherence/coherence-overlap-negative-impls.rs deleted file mode 100644 index ffcd56817e5c2..0000000000000 --- a/tests/ui/coherence/coherence-overlap-negative-impls.rs +++ /dev/null @@ -1,44 +0,0 @@ -//@ check-pass -//@ known-bug: #74629 - -// Should fail. The `0` and `1` impls overlap, violating coherence. Eg, with -// `T = Test, F = ()`, all bounds are true, making both impls applicable. -// `Test: Fold`, `Test: Fold<()>` are true because of `2`. -// `Is: NotNil` is true because of `auto trait` and lack of negative impl. - -#![feature(negative_impls)] -#![feature(auto_traits)] - -struct Nil; -struct Cons(H); -struct Test; - -trait Fold {} - -impl Fold for Cons -// 0 -where - T: Fold, -{ -} - -impl Fold for Cons -// 1 -where - T: Fold, - private::Is: private::NotNil, -{ -} - -impl Fold for Test {} // 2 - -mod private { - use crate::Nil; - - pub struct Is(T); - pub auto trait NotNil {} - - impl !NotNil for Is {} -} - -fn main() {} diff --git a/tests/ui/coherence/coherence-with-closure.rs b/tests/ui/coherence/coherence-with-closure.rs index 5b6a62b24d4b3..ac2c55843929e 100644 --- a/tests/ui/coherence/coherence-with-closure.rs +++ b/tests/ui/coherence/coherence-with-closure.rs @@ -1,6 +1,7 @@ // Test that encountering closures during coherence does not cause issues. #![feature(type_alias_impl_trait)] type OpaqueClosure = impl Sized; +#[define_opaque(OpaqueClosure)] fn defining_use() -> OpaqueClosure { || () } diff --git a/tests/ui/coherence/coherence-with-closure.stderr b/tests/ui/coherence/coherence-with-closure.stderr index 501279ffe6a72..37f393ceeb70a 100644 --- a/tests/ui/coherence/coherence-with-closure.stderr +++ b/tests/ui/coherence/coherence-with-closure.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Trait` for type `Wrapper` - --> $DIR/coherence-with-closure.rs:11:1 + --> $DIR/coherence-with-closure.rs:12:1 | LL | impl Trait for Wrapper {} | ------------------------------------- first implementation here diff --git a/tests/ui/coherence/coherence-with-coroutine.rs b/tests/ui/coherence/coherence-with-coroutine.rs index 6b0617e950b7d..0e65a6c1da031 100644 --- a/tests/ui/coherence/coherence-with-coroutine.rs +++ b/tests/ui/coherence/coherence-with-coroutine.rs @@ -7,6 +7,7 @@ //@ [specialized]check-pass type OpaqueCoroutine = impl Sized; +#[define_opaque(OpaqueCoroutine)] fn defining_use() -> OpaqueCoroutine { #[coroutine] || { diff --git a/tests/ui/coherence/coherence-with-coroutine.stock.stderr b/tests/ui/coherence/coherence-with-coroutine.stock.stderr index 5f58b3088f14d..c7af384df6d30 100644 --- a/tests/ui/coherence/coherence-with-coroutine.stock.stderr +++ b/tests/ui/coherence/coherence-with-coroutine.stock.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Trait` for type `Wrapper` - --> $DIR/coherence-with-coroutine.rs:22:1 + --> $DIR/coherence-with-coroutine.rs:23:1 | LL | impl Trait for Wrapper {} | --------------------------------------- first implementation here diff --git a/tests/ui/coherence/const-errs-dont-conflict-103369.stderr b/tests/ui/coherence/const-errs-dont-conflict-103369.stderr index 4acaaf22ae8fd..b2104299f65e1 100644 --- a/tests/ui/coherence/const-errs-dont-conflict-103369.stderr +++ b/tests/ui/coherence/const-errs-dont-conflict-103369.stderr @@ -9,7 +9,6 @@ note: inside `my_fn` | LL | panic!("Some error occurred"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the failure occurred here - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const-errs-dont-conflict-103369.rs:7:25 @@ -22,7 +21,6 @@ note: inside `my_fn` | LL | panic!("Some error occurred"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the failure occurred here - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/coherence/issue-99663-2.rs b/tests/ui/coherence/issue-99663-2.rs index 675e9fdfdba7a..9827df1d5e708 100644 --- a/tests/ui/coherence/issue-99663-2.rs +++ b/tests/ui/coherence/issue-99663-2.rs @@ -8,6 +8,7 @@ struct Outer { type InnerSend = impl Send; +#[define_opaque(InnerSend)] fn constrain() -> InnerSend { () } diff --git a/tests/ui/coherence/issue-99663.rs b/tests/ui/coherence/issue-99663.rs index 00d15977d8f09..49156952990d9 100644 --- a/tests/ui/coherence/issue-99663.rs +++ b/tests/ui/coherence/issue-99663.rs @@ -8,6 +8,7 @@ struct Send { type InnerSend = impl Sized; +#[define_opaque(InnerSend)] fn constrain() -> InnerSend { () } diff --git a/tests/ui/coherence/occurs-check/opaques.current.stderr b/tests/ui/coherence/occurs-check/opaques.current.stderr index f3fc22027c26a..d3850df521820 100644 --- a/tests/ui/coherence/occurs-check/opaques.current.stderr +++ b/tests/ui/coherence/occurs-check/opaques.current.stderr @@ -1,11 +1,11 @@ error[E0119]: conflicting implementations of trait `Trait<_>` - --> $DIR/opaques.rs:28:1 + --> $DIR/opaques.rs:27:1 | LL | impl Trait for T { | ---------------------- first implementation here ... -LL | impl Trait for defining_scope::Alias { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation +LL | impl Trait for Alias { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation error: aborting due to 1 previous error diff --git a/tests/ui/coherence/occurs-check/opaques.next.stderr b/tests/ui/coherence/occurs-check/opaques.next.stderr index 04fd139f901ba..508e6f4023426 100644 --- a/tests/ui/coherence/occurs-check/opaques.next.stderr +++ b/tests/ui/coherence/occurs-check/opaques.next.stderr @@ -1,17 +1,17 @@ error[E0119]: conflicting implementations of trait `Trait<_>` - --> $DIR/opaques.rs:28:1 + --> $DIR/opaques.rs:27:1 | LL | impl Trait for T { | ---------------------- first implementation here ... -LL | impl Trait for defining_scope::Alias { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation +LL | impl Trait for Alias { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation error[E0282]: type annotations needed - --> $DIR/opaques.rs:11:23 + --> $DIR/opaques.rs:11:19 | -LL | pub fn cast(x: Container, T>) -> Container { - | ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type +LL | pub fn cast(x: Container, T>) -> Container { + | ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type error: aborting due to 2 previous errors diff --git a/tests/ui/coherence/occurs-check/opaques.rs b/tests/ui/coherence/occurs-check/opaques.rs index e197256c78c77..5ea654189a943 100644 --- a/tests/ui/coherence/occurs-check/opaques.rs +++ b/tests/ui/coherence/occurs-check/opaques.rs @@ -4,14 +4,13 @@ // A regression test for #105787 #![feature(type_alias_impl_trait)] -mod defining_scope { - use super::*; - pub type Alias = impl Sized; - pub fn cast(x: Container, T>) -> Container { - //[next]~^ ERROR type annotations needed - x - } +pub type Alias = impl Sized; + +#[define_opaque(Alias)] +pub fn cast(x: Container, T>) -> Container { + //[next]~^ ERROR type annotations needed + x } struct Container, U> { @@ -25,12 +24,12 @@ trait Trait { impl Trait for T { type Assoc = Box; } -impl Trait for defining_scope::Alias { +impl Trait for Alias { //~^ ERROR conflicting implementations of trait type Assoc = usize; } fn main() { - let x: Box = defining_scope::cast::<()>(Container { x: 0 }).x; + let x: Box = cast::<()>(Container { x: 0 }).x; println!("{}", *x); } diff --git a/tests/ui/coherence/orphan-check-error-reporting-ty-var.rs b/tests/ui/coherence/orphan-check-error-reporting-ty-var.rs new file mode 100644 index 0000000000000..99a8345335448 --- /dev/null +++ b/tests/ui/coherence/orphan-check-error-reporting-ty-var.rs @@ -0,0 +1,17 @@ +// Regression test for #132826. + +// Make sure we don't try to resolve the variable `K1` in the generics of the impl +// (which only has `K2`). + +pub trait MyTrait { + type Item; +} + +impl MyTrait for Vec { + type Item = Vec; +} + +impl From> for as MyTrait>::Item {} +//~^ ERROR only traits defined in the current crate can be implemented for arbitrary types + +fn main() {} diff --git a/tests/ui/coherence/orphan-check-error-reporting-ty-var.stderr b/tests/ui/coherence/orphan-check-error-reporting-ty-var.stderr new file mode 100644 index 0000000000000..f229f8b2e385e --- /dev/null +++ b/tests/ui/coherence/orphan-check-error-reporting-ty-var.stderr @@ -0,0 +1,16 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/orphan-check-error-reporting-ty-var.rs:14:1 + | +LL | impl From> for as MyTrait>::Item {} + | ^^^^^^^^^-------------^^^^^-------------------------- + | | | + | | `Vec` is not defined in the current crate + | `Vec` is not defined in the current crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/coherence/orphan-check-opaque-types-not-covering.rs b/tests/ui/coherence/orphan-check-opaque-types-not-covering.rs index 02e9eb65570c0..ea91933e6949e 100644 --- a/tests/ui/coherence/orphan-check-opaque-types-not-covering.rs +++ b/tests/ui/coherence/orphan-check-opaque-types-not-covering.rs @@ -7,6 +7,7 @@ type Identity = impl Sized; +#[define_opaque(Identity)] fn define_identity(x: T) -> Identity { x } @@ -16,6 +17,7 @@ impl foreign::Trait0 for Identity {} type Opaque = impl Sized; +#[define_opaque(Opaque)] fn define_local() -> Opaque { Local } diff --git a/tests/ui/coherence/orphan-check-opaque-types-not-covering.stderr b/tests/ui/coherence/orphan-check-opaque-types-not-covering.stderr index 57f5bbd227875..6203742b47c0a 100644 --- a/tests/ui/coherence/orphan-check-opaque-types-not-covering.stderr +++ b/tests/ui/coherence/orphan-check-opaque-types-not-covering.stderr @@ -1,5 +1,5 @@ error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) - --> $DIR/orphan-check-opaque-types-not-covering.rs:14:6 + --> $DIR/orphan-check-opaque-types-not-covering.rs:15:6 | LL | impl foreign::Trait0 for Identity {} | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) @@ -8,7 +8,7 @@ LL | impl foreign::Trait0 for Identity {} = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) - --> $DIR/orphan-check-opaque-types-not-covering.rs:23:6 + --> $DIR/orphan-check-opaque-types-not-covering.rs:25:6 | LL | impl foreign::Trait1 for Opaque {} | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) diff --git a/tests/ui/coherence/warn-when-cycle-is-error-in-coherence.stderr b/tests/ui/coherence/warn-when-cycle-is-error-in-coherence.stderr index af4dbfcad0e20..a9a99fb28d844 100644 --- a/tests/ui/coherence/warn-when-cycle-is-error-in-coherence.stderr +++ b/tests/ui/coherence/warn-when-cycle-is-error-in-coherence.stderr @@ -9,8 +9,6 @@ LL | | where LL | | T: Borrow, LL | | Q: ?Sized + PartialOrd, | |___________________________- first implementation here - | - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/compiletest-self-test/compile-flags-last.rs b/tests/ui/compiletest-self-test/compile-flags-last.rs index b78a64394b87c..e0743d2aea585 100644 --- a/tests/ui/compiletest-self-test/compile-flags-last.rs +++ b/tests/ui/compiletest-self-test/compile-flags-last.rs @@ -4,4 +4,5 @@ // next flag as the argument of this flag. // //@ compile-flags: --cap-lints -//@ error-pattern: Argument to option 'cap-lints' missing + +//~? RAW Argument to option 'cap-lints' missing diff --git a/tests/ui/compiletest-self-test/compile-flags-last.stderr b/tests/ui/compiletest-self-test/compile-flags-last.stderr index 72d92206e2bb4..5a48361cfc0df 100644 --- a/tests/ui/compiletest-self-test/compile-flags-last.stderr +++ b/tests/ui/compiletest-self-test/compile-flags-last.stderr @@ -1,5 +1,5 @@ error: Argument to option 'cap-lints' missing Usage: - --cap-lints LEVEL Set the most restrictive lint level. More restrictive + --cap-lints Set the most restrictive lint level. More restrictive lints are capped at this level diff --git a/tests/ui/compiletest-self-test/trim-env-name.rs b/tests/ui/compiletest-self-test/trim-env-name.rs new file mode 100644 index 0000000000000..0cb6efe9f7658 --- /dev/null +++ b/tests/ui/compiletest-self-test/trim-env-name.rs @@ -0,0 +1,23 @@ +//@ edition: 2024 +//@ revisions: set unset +//@ run-pass +//@ ignore-cross-compile (assume that non-cross targets have working env vars) +//@ rustc-env: MY_RUSTC_ENV = my-rustc-value +//@ exec-env: MY_EXEC_ENV = my-exec-value +//@[unset] unset-rustc-env: MY_RUSTC_ENV +//@[unset] unset-exec-env: MY_EXEC_ENV + +// Check that compiletest trims whitespace from environment variable names +// specified in `rustc-env` and `exec-env` directives, so that +// `//@ exec-env: FOO=bar` sees the name as `FOO` and not ` FOO`. +// +// Values are currently not trimmed. +// +// Since this is a compiletest self-test, only run it on non-cross targets, +// to avoid having to worry about weird targets that don't support env vars. + +fn main() { + let is_set = cfg!(set); + assert_eq!(option_env!("MY_RUSTC_ENV"), is_set.then_some(" my-rustc-value")); + assert_eq!(std::env::var("MY_EXEC_ENV").ok().as_deref(), is_set.then_some(" my-exec-value")); +} diff --git a/tests/ui/conditional-compilation/cfg-arg-invalid-1.rs b/tests/ui/conditional-compilation/cfg-arg-invalid-1.rs index cd181f4a49f67..28d2d266b6233 100644 --- a/tests/ui/conditional-compilation/cfg-arg-invalid-1.rs +++ b/tests/ui/conditional-compilation/cfg-arg-invalid-1.rs @@ -1,3 +1,5 @@ -//@ compile-flags: --error-format=human --cfg a(b=c) -//@ error-pattern: invalid `--cfg` argument: `a(b=c)` (expected `key` or `key="value"`, ensure escaping is appropriate for your shell, try 'key="value"' or key=\"value\") +//@ compile-flags: --cfg a(b=c) + fn main() {} + +//~? ERROR invalid `--cfg` argument: `a(b=c)` (expected `key` or `key="value"`, ensure escaping is appropriate for your shell, try 'key="value"' or key=\"value\") diff --git a/tests/ui/conditional-compilation/cfg-arg-invalid-2.rs b/tests/ui/conditional-compilation/cfg-arg-invalid-2.rs index a0c16bd1f80a1..a46022602e271 100644 --- a/tests/ui/conditional-compilation/cfg-arg-invalid-2.rs +++ b/tests/ui/conditional-compilation/cfg-arg-invalid-2.rs @@ -1,3 +1,5 @@ -//@ compile-flags: --error-format=human --cfg a{b} -//@ error-pattern: invalid `--cfg` argument: `a{b}` (expected `key` or `key="value"`) +//@ compile-flags: --cfg a{b} + fn main() {} + +//~? ERROR invalid `--cfg` argument: `a{b}` (expected `key` or `key="value"`) diff --git a/tests/ui/conditional-compilation/cfg-arg-invalid-3.rs b/tests/ui/conditional-compilation/cfg-arg-invalid-3.rs index c086b8d8c3f42..ba55b1c08b1ed 100644 --- a/tests/ui/conditional-compilation/cfg-arg-invalid-3.rs +++ b/tests/ui/conditional-compilation/cfg-arg-invalid-3.rs @@ -1,3 +1,5 @@ //@ compile-flags: --cfg a::b -//@ error-pattern: invalid `--cfg` argument: `a::b` (argument key must be an identifier) + fn main() {} + +//~? ERROR invalid `--cfg` argument: `a::b` (argument key must be an identifier) diff --git a/tests/ui/conditional-compilation/cfg-arg-invalid-4.rs b/tests/ui/conditional-compilation/cfg-arg-invalid-4.rs index 30402d51852fc..ba34708c171bf 100644 --- a/tests/ui/conditional-compilation/cfg-arg-invalid-4.rs +++ b/tests/ui/conditional-compilation/cfg-arg-invalid-4.rs @@ -1,3 +1,5 @@ -//@ compile-flags: --error-format=human --cfg a(b) -//@ error-pattern: invalid `--cfg` argument: `a(b)` (expected `key` or `key="value"`) +//@ compile-flags: --cfg a(b) + fn main() {} + +//~? ERROR invalid `--cfg` argument: `a(b)` (expected `key` or `key="value"`) diff --git a/tests/ui/conditional-compilation/cfg-arg-invalid-5.rs b/tests/ui/conditional-compilation/cfg-arg-invalid-5.rs index 6f0bf8cf5fee9..d64aeead79cbd 100644 --- a/tests/ui/conditional-compilation/cfg-arg-invalid-5.rs +++ b/tests/ui/conditional-compilation/cfg-arg-invalid-5.rs @@ -1,3 +1,5 @@ //@ compile-flags: --cfg a=10 -//@ error-pattern: invalid `--cfg` argument: `a=10` (argument value must be a string) + fn main() {} + +//~? ERROR invalid `--cfg` argument: `a=10` (argument value must be a string) diff --git a/tests/ui/conditional-compilation/cfg-arg-invalid-6.rs b/tests/ui/conditional-compilation/cfg-arg-invalid-6.rs index e0ce66eab8773..2c2fc105958ab 100644 --- a/tests/ui/conditional-compilation/cfg-arg-invalid-6.rs +++ b/tests/ui/conditional-compilation/cfg-arg-invalid-6.rs @@ -1,3 +1,5 @@ -//@ compile-flags: --error-format=human --cfg a{ -//@ error-pattern: invalid `--cfg` argument: `a{` (expected `key` or `key="value"`) +//@ compile-flags: --cfg a{ + fn main() {} + +//~? ERROR invalid `--cfg` argument: `a{` (expected `key` or `key="value"`) diff --git a/tests/ui/conditional-compilation/cfg-arg-invalid-7.rs b/tests/ui/conditional-compilation/cfg-arg-invalid-7.rs index b4344f1bca5c2..f05adc7bf7aad 100644 --- a/tests/ui/conditional-compilation/cfg-arg-invalid-7.rs +++ b/tests/ui/conditional-compilation/cfg-arg-invalid-7.rs @@ -1,5 +1,6 @@ // Regression test for issue #89358. //@ compile-flags: --cfg a" -//@ error-pattern: unterminated double quote string -//@ error-pattern: this error occurred on the command line + +//~? RAW unterminated double quote string +//~? RAW this error occurred on the command line diff --git a/tests/ui/conditional-compilation/cfg-arg-invalid-8.rs b/tests/ui/conditional-compilation/cfg-arg-invalid-8.rs index 33f8da25830ed..c9185fc7b2591 100644 --- a/tests/ui/conditional-compilation/cfg-arg-invalid-8.rs +++ b/tests/ui/conditional-compilation/cfg-arg-invalid-8.rs @@ -1,3 +1,5 @@ -//@ compile-flags: --error-format=human --cfg ) -//@ error-pattern: invalid `--cfg` argument: `)` (expected `key` or `key="value"`) +//@ compile-flags: --cfg ) + fn main() {} + +//~? ERROR invalid `--cfg` argument: `)` (expected `key` or `key="value"`) diff --git a/tests/ui/conditional-compilation/cfg-arg-invalid-9.rs b/tests/ui/conditional-compilation/cfg-arg-invalid-9.rs index 8ab3b101da799..8d07165dfae40 100644 --- a/tests/ui/conditional-compilation/cfg-arg-invalid-9.rs +++ b/tests/ui/conditional-compilation/cfg-arg-invalid-9.rs @@ -1,4 +1,6 @@ // Test for missing quotes around value, issue #66450. -//@ compile-flags: --error-format=human --cfg key=value -//@ error-pattern: invalid `--cfg` argument: `key=value` (expected `key` or `key="value"`, ensure escaping is appropriate for your shell, try 'key="value"' or key=\"value\") +//@ compile-flags: --cfg key=value + fn main() {} + +//~? ERROR invalid `--cfg` argument: `key=value` (expected `key` or `key="value"`, ensure escaping is appropriate for your shell, try 'key="value"' or key=\"value\") diff --git a/tests/ui/conditional-compilation/cfg-attr-cfg-2.rs b/tests/ui/conditional-compilation/cfg-attr-cfg-2.rs index c801bbccedd6e..80735990015c4 100644 --- a/tests/ui/conditional-compilation/cfg-attr-cfg-2.rs +++ b/tests/ui/conditional-compilation/cfg-attr-cfg-2.rs @@ -1,8 +1,7 @@ -//@ error-pattern: `main` function not found //@ compile-flags: --cfg foo --check-cfg=cfg(foo,bar) // main is conditionally compiled, but the conditional compilation // is conditional too! #[cfg_attr(foo, cfg(bar))] -fn main() { } +fn main() { } //~ ERROR `main` function not found in crate `cfg_attr_cfg_2` diff --git a/tests/ui/conditional-compilation/cfg-attr-cfg-2.stderr b/tests/ui/conditional-compilation/cfg-attr-cfg-2.stderr index 64595241dc729..1febb43086334 100644 --- a/tests/ui/conditional-compilation/cfg-attr-cfg-2.stderr +++ b/tests/ui/conditional-compilation/cfg-attr-cfg-2.stderr @@ -1,5 +1,5 @@ error[E0601]: `main` function not found in crate `cfg_attr_cfg_2` - --> $DIR/cfg-attr-cfg-2.rs:8:14 + --> $DIR/cfg-attr-cfg-2.rs:7:14 | LL | fn main() { } | ^ consider adding a `main` function to `$DIR/cfg-attr-cfg-2.rs` diff --git a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs index 9a041557c7cc9..47418b4e091b2 100644 --- a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs +++ b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs @@ -28,8 +28,7 @@ struct S9; macro_rules! generate_s10 { ($expr: expr) => { #[cfg(feature = $expr)] - //~^ ERROR expected unsuffixed literal, found expression `concat!("nonexistent")` - //~| ERROR expected unsuffixed literal, found expression `concat!("nonexistent")` + //~^ ERROR expected unsuffixed literal, found `expr` metavariable struct S10; } } diff --git a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr index 21a3712d9391a..66ce2ee98589d 100644 --- a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr +++ b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr @@ -54,7 +54,7 @@ LL | #[cfg(a = b"hi")] | | | help: consider removing the prefix -error: expected unsuffixed literal, found expression `concat!("nonexistent")` +error: expected unsuffixed literal, found `expr` metavariable --> $DIR/cfg-attr-syntax-validation.rs:30:25 | LL | #[cfg(feature = $expr)] @@ -65,19 +65,7 @@ LL | generate_s10!(concat!("nonexistent")); | = note: this error originates in the macro `generate_s10` (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected unsuffixed literal, found expression `concat!("nonexistent")` - --> $DIR/cfg-attr-syntax-validation.rs:30:25 - | -LL | #[cfg(feature = $expr)] - | ^^^^^ -... -LL | generate_s10!(concat!("nonexistent")); - | ------------------------------------- in this macro invocation - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the macro `generate_s10` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors Some errors have detailed explanations: E0537, E0565. For more information about an error, try `rustc --explain E0537`. diff --git a/tests/ui/conditional-compilation/cfg-empty-codemap.rs b/tests/ui/conditional-compilation/cfg-empty-codemap.rs index d8fc027775950..9f7ef1bcf9aa9 100644 --- a/tests/ui/conditional-compilation/cfg-empty-codemap.rs +++ b/tests/ui/conditional-compilation/cfg-empty-codemap.rs @@ -1,8 +1,8 @@ // Tests that empty source_maps don't ICE (#23301) -//@ compile-flags: --error-format=human --cfg "" - -//@ error-pattern: invalid `--cfg` argument: `""` (expected `key` or `key="value"`) +//@ compile-flags: --cfg "" pub fn main() { } + +//~? ERROR invalid `--cfg` argument: `""` (expected `key` or `key="value"`) diff --git a/tests/ui/conditional-compilation/cfg-generic-params.rs b/tests/ui/conditional-compilation/cfg-generic-params.rs index 4bb8f8ae94f69..6480a0f24794c 100644 --- a/tests/ui/conditional-compilation/cfg-generic-params.rs +++ b/tests/ui/conditional-compilation/cfg-generic-params.rs @@ -1,18 +1,18 @@ //@ compile-flags:--cfg yes --check-cfg=cfg(yes,no) -fn f_lt<#[cfg(yes)] 'a: 'a, #[cfg(FALSE)] T>() {} -fn f_ty<#[cfg(FALSE)] 'a: 'a, #[cfg(yes)] T>() {} +fn f_lt<#[cfg(yes)] 'a: 'a, #[cfg(false)] T>() {} +fn f_ty<#[cfg(false)] 'a: 'a, #[cfg(yes)] T>() {} -type FnGood = for<#[cfg(yes)] 'a, #[cfg(FALSE)] T> fn(); // OK -type FnBad = for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> fn(); +type FnGood = for<#[cfg(yes)] 'a, #[cfg(false)] T> fn(); // OK +type FnBad = for<#[cfg(false)] 'a, #[cfg(yes)] T> fn(); //~^ ERROR only lifetime parameters can be used in this context -type PolyGood = dyn for<#[cfg(yes)] 'a, #[cfg(FALSE)] T> Copy; // OK -type PolyBad = dyn for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> Copy; +type PolyGood = dyn for<#[cfg(yes)] 'a, #[cfg(false)] T> Copy; // OK +type PolyBad = dyn for<#[cfg(false)] 'a, #[cfg(yes)] T> Copy; //~^ ERROR only lifetime parameters can be used in this context -struct WhereGood where for<#[cfg(yes)] 'a, #[cfg(FALSE)] T> u8: Copy; // OK -struct WhereBad where for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> u8: Copy; +struct WhereGood where for<#[cfg(yes)] 'a, #[cfg(false)] T> u8: Copy; // OK +struct WhereBad where for<#[cfg(false)] 'a, #[cfg(yes)] T> u8: Copy; //~^ ERROR only lifetime parameters can be used in this context fn f_lt_no<#[cfg_attr(FALSE, unknown)] 'a>() {} // OK diff --git a/tests/ui/conditional-compilation/cfg-generic-params.stderr b/tests/ui/conditional-compilation/cfg-generic-params.stderr index 563616be36bc1..bae75dd0deb03 100644 --- a/tests/ui/conditional-compilation/cfg-generic-params.stderr +++ b/tests/ui/conditional-compilation/cfg-generic-params.stderr @@ -31,7 +31,7 @@ LL | struct WhereYes where for<#[cfg_attr(yes, unknown)] 'a> u8: Copy; error[E0658]: only lifetime parameters can be used in this context --> $DIR/cfg-generic-params.rs:7:48 | -LL | type FnBad = for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> fn(); +LL | type FnBad = for<#[cfg(false)] 'a, #[cfg(yes)] T> fn(); | ^ | = note: see issue #108185 for more information @@ -41,7 +41,7 @@ LL | type FnBad = for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> fn(); error[E0658]: only lifetime parameters can be used in this context --> $DIR/cfg-generic-params.rs:11:54 | -LL | type PolyBad = dyn for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> Copy; +LL | type PolyBad = dyn for<#[cfg(false)] 'a, #[cfg(yes)] T> Copy; | ^ | = note: see issue #108185 for more information @@ -51,7 +51,7 @@ LL | type PolyBad = dyn for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> Copy; error[E0658]: only lifetime parameters can be used in this context --> $DIR/cfg-generic-params.rs:15:57 | -LL | struct WhereBad where for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> u8: Copy; +LL | struct WhereBad where for<#[cfg(false)] 'a, #[cfg(yes)] T> u8: Copy; | ^ | = note: see issue #108185 for more information diff --git a/tests/ui/conditional-compilation/cfg-in-crate-1.rs b/tests/ui/conditional-compilation/cfg-in-crate-1.rs index ecd3722bf4c54..b9efa32babefd 100644 --- a/tests/ui/conditional-compilation/cfg-in-crate-1.rs +++ b/tests/ui/conditional-compilation/cfg-in-crate-1.rs @@ -1,3 +1 @@ -//@ error-pattern: `main` function not found - -#![cfg(FALSE)] +#![cfg(false)] //~ ERROR `main` function not found in crate `cfg_in_crate_1` diff --git a/tests/ui/conditional-compilation/cfg-in-crate-1.stderr b/tests/ui/conditional-compilation/cfg-in-crate-1.stderr index 6067a3a921cb1..352baf33091b4 100644 --- a/tests/ui/conditional-compilation/cfg-in-crate-1.stderr +++ b/tests/ui/conditional-compilation/cfg-in-crate-1.stderr @@ -1,7 +1,7 @@ error[E0601]: `main` function not found in crate `cfg_in_crate_1` - --> $DIR/cfg-in-crate-1.rs:3:15 + --> $DIR/cfg-in-crate-1.rs:1:15 | -LL | #![cfg(FALSE)] +LL | #![cfg(false)] | ^ consider adding a `main` function to `$DIR/cfg-in-crate-1.rs` error: aborting due to 1 previous error diff --git a/tests/ui/conditional-compilation/cfg-non-opt-expr.rs b/tests/ui/conditional-compilation/cfg-non-opt-expr.rs index ae85f38e645e2..cae07ae0ced10 100644 --- a/tests/ui/conditional-compilation/cfg-non-opt-expr.rs +++ b/tests/ui/conditional-compilation/cfg-non-opt-expr.rs @@ -2,10 +2,10 @@ #![feature(custom_test_frameworks)] fn main() { - let _ = #[cfg(FALSE)] (); + let _ = #[cfg(false)] (); //~^ ERROR removing an expression is not supported in this position - let _ = 1 + 2 + #[cfg(FALSE)] 3; + let _ = 1 + 2 + #[cfg(false)] 3; //~^ ERROR removing an expression is not supported in this position - let _ = [1, 2, 3][#[cfg(FALSE)] 1]; + let _ = [1, 2, 3][#[cfg(false)] 1]; //~^ ERROR removing an expression is not supported in this position } diff --git a/tests/ui/conditional-compilation/cfg-non-opt-expr.stderr b/tests/ui/conditional-compilation/cfg-non-opt-expr.stderr index 06eaa59efdd7a..bd1bfeb06c7ad 100644 --- a/tests/ui/conditional-compilation/cfg-non-opt-expr.stderr +++ b/tests/ui/conditional-compilation/cfg-non-opt-expr.stderr @@ -1,19 +1,19 @@ error: removing an expression is not supported in this position --> $DIR/cfg-non-opt-expr.rs:5:13 | -LL | let _ = #[cfg(FALSE)] (); +LL | let _ = #[cfg(false)] (); | ^^^^^^^^^^^^^ error: removing an expression is not supported in this position --> $DIR/cfg-non-opt-expr.rs:7:21 | -LL | let _ = 1 + 2 + #[cfg(FALSE)] 3; +LL | let _ = 1 + 2 + #[cfg(false)] 3; | ^^^^^^^^^^^^^ error: removing an expression is not supported in this position --> $DIR/cfg-non-opt-expr.rs:9:23 | -LL | let _ = [1, 2, 3][#[cfg(FALSE)] 1]; +LL | let _ = [1, 2, 3][#[cfg(false)] 1]; | ^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/conditional-compilation/cfg_accessible-not_sure.rs b/tests/ui/conditional-compilation/cfg_accessible-not_sure.rs index 2ac57f356740f..7753b7d64fb33 100644 --- a/tests/ui/conditional-compilation/cfg_accessible-not_sure.rs +++ b/tests/ui/conditional-compilation/cfg_accessible-not_sure.rs @@ -1,6 +1,6 @@ //@ revisions: edition2015 edition2021 -//@ [edition2015]compile-flags: --edition=2015 -//@ [edition2021]compile-flags: --edition=2021 +//@ [edition2015] edition: 2015 +//@ [edition2021] edition: 2021 #![feature(extern_types)] #![feature(cfg_accessible)] diff --git a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs index 794e6fad3fc22..7c42be3ed4d6e 100644 --- a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs +++ b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs @@ -1,11 +1,12 @@ // This was triggering an assertion failure in `NodeRange::new`. +//@ check-pass + #![feature(cfg_eval)] #![feature(stmt_expr_attributes)] fn f() -> u32 { #[cfg_eval] #[cfg(not(FALSE))] 0 - //~^ ERROR removing an expression is not supported in this position } fn main() {} diff --git a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.stderr b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.stderr deleted file mode 100644 index 0699e182bd5f2..0000000000000 --- a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: removing an expression is not supported in this position - --> $DIR/invalid-node-range-issue-129166.rs:7:17 - | -LL | #[cfg_eval] #[cfg(not(FALSE))] 0 - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/conditional-compilation/module_with_cfg.rs b/tests/ui/conditional-compilation/module_with_cfg.rs index 778379fa6ea7a..a96f8a3e6e964 100644 --- a/tests/ui/conditional-compilation/module_with_cfg.rs +++ b/tests/ui/conditional-compilation/module_with_cfg.rs @@ -1,3 +1,3 @@ -//@ ignore-test (auxiliary, used by other tests) +//@ ignore-auxiliary (used by `./inner-cfg-non-inline-mod.rs`) -#![cfg_attr(all(), cfg(FALSE))] +#![cfg_attr(all(), cfg(false))] diff --git a/tests/ui/const-generics/adt_const_params/check-type-in-mir.rs b/tests/ui/const-generics/adt_const_params/check-type-in-mir.rs new file mode 100644 index 0000000000000..bec485b001c04 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/check-type-in-mir.rs @@ -0,0 +1,16 @@ +// Ensure that we actually treat `N`'s type as `Invariant<'static>` in MIR typeck. + +#![feature(adt_const_params)] + +use std::marker::ConstParamTy; +use std::ops::Deref; + +#[derive(ConstParamTy, PartialEq, Eq)] +struct Invariant<'a>(<&'a () as Deref>::Target); + +fn test<'a, const N: Invariant<'static>>() { + let x: Invariant<'a> = N; + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/check-type-in-mir.stderr b/tests/ui/const-generics/adt_const_params/check-type-in-mir.stderr new file mode 100644 index 0000000000000..7e265f33f35bd --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/check-type-in-mir.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/check-type-in-mir.rs:12:28 + | +LL | fn test<'a, const N: Invariant<'static>>() { + | -- lifetime `'a` defined here +LL | let x: Invariant<'a> = N; + | ^ assignment requires that `'a` must outlive `'static` + | + = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Invariant<'a>` is invariant over the parameter `'a` + = help: see for more information about variance + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.stderr index 808a569fcadf8..a4e5736d83429 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.stderr @@ -15,8 +15,6 @@ LL | #[derive(std::marker::UnsizedConstParamTy, Eq, PartialEq)] LL | LL | struct CantParamDerive(NotParam); | -------- this field does not implement `ConstParamTy_` - | - = note: this error originates in the derive macro `std::marker::UnsizedConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs index 761a6387a24e9..e743b78fd134b 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs @@ -9,11 +9,11 @@ struct CantParam(ImplementsConstParamTy); impl std::marker::UnsizedConstParamTy for CantParam {} //~^ error: the type `CantParam` does not `#[derive(PartialEq)]` -//~| the trait bound `CantParam: Eq` is not satisfied +//~| ERROR the trait bound `CantParam: Eq` is not satisfied #[derive(std::marker::UnsizedConstParamTy)] //~^ error: the type `CantParamDerive` does not `#[derive(PartialEq)]` -//~| the trait bound `CantParamDerive: Eq` is not satisfied +//~| ERROR the trait bound `CantParamDerive: Eq` is not satisfied struct CantParamDerive(ImplementsConstParamTy); fn check() {} diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr index 80d9942c5914e..b651cca3216b3 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr @@ -29,7 +29,6 @@ LL | #[derive(std::marker::UnsizedConstParamTy)] | note: required by a bound in `UnsizedConstParamTy` --> $SRC_DIR/core/src/marker.rs:LL:COL - = note: this error originates in the derive macro `std::marker::UnsizedConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `CantParamDerive` with `#[derive(Eq)]` | LL + #[derive(Eq)] @@ -44,7 +43,6 @@ LL | #[derive(std::marker::UnsizedConstParamTy)] | note: required by a bound in `UnsizedConstParamTy` --> $SRC_DIR/core/src/marker.rs:LL:COL - = note: this error originates in the derive macro `std::marker::UnsizedConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.rs b/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.rs index 3a283442a0b01..34ea143d25462 100644 --- a/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.rs +++ b/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.rs @@ -4,18 +4,18 @@ use std::marker::ConstParamTy; #[derive(ConstParamTy)] -//~^ the trait `ConstParamTy_` cannot be implemented for this ty -//~| the trait `ConstParamTy_` cannot be implemented for this ty +//~^ ERROR the trait `ConstParamTy_` cannot be implemented for this ty +//~| ERROR the trait `ConstParamTy_` cannot be implemented for this ty struct Foo([*const u8; 1]); #[derive(ConstParamTy)] -//~^ the trait `ConstParamTy_` cannot be implemented for this ty -//~| the trait `ConstParamTy_` cannot be implemented for this ty +//~^ ERROR the trait `ConstParamTy_` cannot be implemented for this ty +//~| ERROR the trait `ConstParamTy_` cannot be implemented for this ty struct Foo2([*mut u8; 1]); #[derive(ConstParamTy)] -//~^ the trait `ConstParamTy_` cannot be implemented for this ty -//~| the trait `ConstParamTy_` cannot be implemented for this ty +//~^ ERROR the trait `ConstParamTy_` cannot be implemented for this ty +//~| ERROR the trait `ConstParamTy_` cannot be implemented for this ty struct Foo3([fn(); 1]); fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.stderr b/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.stderr index c2520f1d10386..6b8d2394a8618 100644 --- a/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.stderr +++ b/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.stderr @@ -12,7 +12,6 @@ note: the `ConstParamTy_` impl for `[*const u8; 1]` requires that `*const u8: Co | LL | struct Foo([*const u8; 1]); | ^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type --> $DIR/nested_bad_const_param_ty.rs:11:10 @@ -28,7 +27,6 @@ note: the `ConstParamTy_` impl for `[*mut u8; 1]` requires that `*mut u8: ConstP | LL | struct Foo2([*mut u8; 1]); | ^^^^^^^^^^^^ - = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type --> $DIR/nested_bad_const_param_ty.rs:16:10 @@ -44,7 +42,6 @@ note: the `ConstParamTy_` impl for `[fn(); 1]` requires that `fn(): ConstParamTy | LL | struct Foo3([fn(); 1]); | ^^^^^^^^^ - = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type --> $DIR/nested_bad_const_param_ty.rs:6:10 @@ -60,7 +57,6 @@ note: the `ConstParamTy_` impl for `[*const u8; 1]` requires that `*const u8: Un | LL | struct Foo([*const u8; 1]); | ^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type --> $DIR/nested_bad_const_param_ty.rs:11:10 @@ -76,7 +72,6 @@ note: the `ConstParamTy_` impl for `[*mut u8; 1]` requires that `*mut u8: Unsize | LL | struct Foo2([*mut u8; 1]); | ^^^^^^^^^^^^ - = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type --> $DIR/nested_bad_const_param_ty.rs:16:10 @@ -92,7 +87,6 @@ note: the `ConstParamTy_` impl for `[fn(); 1]` requires that `fn(): UnsizedConst | LL | struct Foo3([fn(); 1]); | ^^^^^^^^^ - = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors diff --git a/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr b/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr index 7a4f9b99c6379..3089b30bd76d1 100644 --- a/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr +++ b/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr @@ -6,8 +6,6 @@ LL | #[derive(ConstParamTy, Eq, PartialEq)] LL | LL | struct A([u8]); | ---- this field does not implement `ConstParamTy_` - | - = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type --> $DIR/unsized_field-1.rs:12:10 @@ -17,8 +15,6 @@ LL | #[derive(ConstParamTy, Eq, PartialEq)] LL | LL | struct B(&'static [u8]); | ------------- this field does not implement `ConstParamTy_` - | - = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type --> $DIR/unsized_field-1.rs:16:10 @@ -28,8 +24,6 @@ LL | #[derive(ConstParamTy, Eq, PartialEq)] LL | LL | struct C(unsized_const_param::Foo); | ------------------------ this field does not implement `ConstParamTy_` - | - = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/adt_const_params/unsized_field-2.stderr b/tests/ui/const-generics/adt_const_params/unsized_field-2.stderr index 15acece538fd7..cef70ca0463bd 100644 --- a/tests/ui/const-generics/adt_const_params/unsized_field-2.stderr +++ b/tests/ui/const-generics/adt_const_params/unsized_field-2.stderr @@ -21,7 +21,6 @@ note: the `ConstParamTy_` impl for `GenericNotUnsizedParam<&'static [u8]>` requi | LL | struct A(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `std::marker::ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr index 72f3fd9de9001..992a27c1c0e90 100644 --- a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr +++ b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr @@ -26,8 +26,6 @@ LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] ... LL | nested: &'static Bar, | ----------------------------------------- this field does not implement `ConstParamTy_` - | - = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type --> $DIR/unsizing-wfcheck-issue-126272.rs:8:32 @@ -53,7 +51,6 @@ note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` require | LL | nested: &'static Bar, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 @@ -76,7 +73,6 @@ LL | struct Bar(T); = note: 2 redundant requirements hidden = note: required for `&&'static Bar<(dyn Debug + 'static)>` to implement `Debug` = note: required for the cast from `&&&'static Bar<(dyn Debug + 'static)>` to `&dyn Debug` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0369]: binary operation `==` cannot be applied to type `&Bar` --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 @@ -86,8 +82,6 @@ LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] ... LL | nested: &'static Bar, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `dyn Debug: Eq` is not satisfied --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 @@ -108,7 +102,6 @@ LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] = note: required for `&'static Bar` to implement `Eq` note: required by a bound in `AssertParamIsEq` --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 @@ -132,7 +125,6 @@ LL | struct Bar(T); | ^ - ...if indirection were used here: `Box` | | | this could be changed to `T: ?Sized`... - = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time --> $DIR/unsizing-wfcheck-issue-126272.rs:26:33 diff --git a/tests/ui/const-generics/const-arg-in-const-arg.min.stderr b/tests/ui/const-generics/const-arg-in-const-arg.min.stderr index 06a6a5f59d6da..16512cb69e230 100644 --- a/tests/ui/const-generics/const-arg-in-const-arg.min.stderr +++ b/tests/ui/const-generics/const-arg-in-const-arg.min.stderr @@ -240,41 +240,6 @@ note: the late bound lifetime parameter is introduced here LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ -error[E0747]: unresolved item provided when a constant was expected - --> $DIR/const-arg-in-const-arg.rs:36:24 - | -LL | let _: Foo<{ bar::() }>; - | ^ - | -help: if this generic argument was intended as a const parameter, surround it with braces - | -LL | let _: Foo<{ bar::<{ N }>() }>; - | + + - -error[E0794]: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:38:24 - | -LL | let _: Foo<{ faz::<'a>(&()) }>; - | ^^ - | -note: the late bound lifetime parameter is introduced here - --> $DIR/const-arg-in-const-arg.rs:10:14 - | -LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } - | ^^ - -error[E0794]: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:41:24 - | -LL | let _: Foo<{ faz::<'b>(&()) }>; - | ^^ - | -note: the late bound lifetime parameter is introduced here - --> $DIR/const-arg-in-const-arg.rs:10:14 - | -LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } - | ^^ - error: constant expression depends on a generic parameter --> $DIR/const-arg-in-const-arg.rs:25:17 | @@ -326,6 +291,41 @@ note: the late bound lifetime parameter is introduced here LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ +error[E0747]: unresolved item provided when a constant was expected + --> $DIR/const-arg-in-const-arg.rs:36:24 + | +LL | let _: Foo<{ bar::() }>; + | ^ + | +help: if this generic argument was intended as a const parameter, surround it with braces + | +LL | let _: Foo<{ bar::<{ N }>() }>; + | + + + +error[E0794]: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/const-arg-in-const-arg.rs:38:24 + | +LL | let _: Foo<{ faz::<'a>(&()) }>; + | ^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/const-arg-in-const-arg.rs:10:14 + | +LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } + | ^^ + +error[E0794]: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/const-arg-in-const-arg.rs:41:24 + | +LL | let _: Foo<{ faz::<'b>(&()) }>; + | ^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/const-arg-in-const-arg.rs:10:14 + | +LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } + | ^^ + error[E0747]: unresolved item provided when a constant was expected --> $DIR/const-arg-in-const-arg.rs:45:27 | diff --git a/tests/ui/const-generics/const-param-has-ty-goal-in-error-implies.rs b/tests/ui/const-generics/const-param-has-ty-goal-in-error-implies.rs new file mode 100644 index 0000000000000..231bb5252de1a --- /dev/null +++ b/tests/ui/const-generics/const-param-has-ty-goal-in-error-implies.rs @@ -0,0 +1,41 @@ +// compile-flags: -Znext-solver + +// Test for a weird diagnostics corner case. In the error reporting code, when reporting +// fulfillment errors for goals A and B, we try to see if elaborating A will result in +// another goal that can equate with B. That would signal that B is "implied by" A, +// allowing us to skip reporting it, which is beneficial for cutting down on the number +// of diagnostics we report. In the new trait solver especially, but even in the old trait +// solver through things like defining opaque type usages, this `can_equate` call was not +// properly taking the param-env of the goals, resulting in nested obligations that had +// empty param-envs. If one of these nested obligations was a `ConstParamHasTy` goal, then +// we would ICE, since those goals are particularly strict about the param-env they're +// evaluated in. + +// This is morally a fix for , but that +// repro uses details about how defining usages in the `check_opaque_well_formed` code +// can spring out of type equality, and will likely stop failing soon coincidentally once +// we start using `PostBorrowck` mode in that check. + +trait Foo: Baz<()> {} +trait Baz {} + +trait IdentityWithConstArgGoal { + type Assoc; +} +impl IdentityWithConstArgGoal for T { + type Assoc = T; +} + +fn unsatisfied() +where + T: Foo, + T: Baz<>::Assoc>, +{ +} + +fn test() { + unsatisfied::<(), N>(); + //~^ ERROR the trait bound `(): Foo` is not satisfied +} + +fn main() {} diff --git a/tests/ui/const-generics/const-param-has-ty-goal-in-error-implies.stderr b/tests/ui/const-generics/const-param-has-ty-goal-in-error-implies.stderr new file mode 100644 index 0000000000000..77bba4945523a --- /dev/null +++ b/tests/ui/const-generics/const-param-has-ty-goal-in-error-implies.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `(): Foo` is not satisfied + --> $DIR/const-param-has-ty-goal-in-error-implies.rs:37:19 + | +LL | unsatisfied::<(), N>(); + | ^^ the trait `Foo` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/const-param-has-ty-goal-in-error-implies.rs:19:1 + | +LL | trait Foo: Baz<()> {} + | ^^^^^^^^^^^^^^^^^^ +note: required by a bound in `unsatisfied` + --> $DIR/const-param-has-ty-goal-in-error-implies.rs:31:8 + | +LL | fn unsatisfied() + | ----------- required by a bound in this function +LL | where +LL | T: Foo, + | ^^^ required by this bound in `unsatisfied` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/const-param-type-depends-on-const-param.rs b/tests/ui/const-generics/const-param-type-depends-on-const-param.rs index ee0e1326baa87..3372ea5b85313 100644 --- a/tests/ui/const-generics/const-param-type-depends-on-const-param.rs +++ b/tests/ui/const-generics/const-param-type-depends-on-const-param.rs @@ -9,11 +9,11 @@ // We may want to lift this restriction in the future. pub struct Dependent([(); N]); -//~^ ERROR: the type of const parameters must not depend on other generic parameters -//[min]~^^ ERROR `[u8; N]` is forbidden +//~^ ERROR the type of const parameters must not depend on other generic parameters +//[min]~^^ ERROR `[u8; N]` is forbidden as the type of a const generic parameter pub struct SelfDependent; -//~^ ERROR: the type of const parameters must not depend on other generic parameters -//[min]~^^ ERROR `[u8; N]` is forbidden +//~^ ERROR the type of const parameters must not depend on other generic parameters +//[min]~^^ ERROR `[u8; N]` is forbidden as the type of a const generic parameter fn main() {} diff --git a/tests/ui/const-generics/const-param-type-depends-on-parent-param.rs b/tests/ui/const-generics/const-param-type-depends-on-parent-param.rs new file mode 100644 index 0000000000000..83fe8c139f90d --- /dev/null +++ b/tests/ui/const-generics/const-param-type-depends-on-parent-param.rs @@ -0,0 +1,8 @@ +#![feature(adt_const_params)] + +trait Trait { + fn foo() {} + //~^ ERROR the type of const parameters must not depend on other generic parameters +} + +fn main() {} diff --git a/tests/ui/const-generics/const-param-type-depends-on-parent-param.stderr b/tests/ui/const-generics/const-param-type-depends-on-parent-param.stderr new file mode 100644 index 0000000000000..ed0a4b118d497 --- /dev/null +++ b/tests/ui/const-generics/const-param-type-depends-on-parent-param.stderr @@ -0,0 +1,9 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/const-param-type-depends-on-parent-param.rs:4:26 + | +LL | fn foo() {} + | ^ the type must not depend on the parameter `N` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/const_eval_unchecked_doesnt_fire_patterns.rs b/tests/ui/const-generics/const_eval_unchecked_doesnt_fire_patterns.rs new file mode 100644 index 0000000000000..fae2d16f43029 --- /dev/null +++ b/tests/ui/const-generics/const_eval_unchecked_doesnt_fire_patterns.rs @@ -0,0 +1,23 @@ +//@ check-pass + +// Previously the `CONST_EVALUATABLE_UNCHECKED` FCW would fire on const evaluation of +// associated consts. This is unnecessary as the FCW only needs to apply for repeat expr +// counts which are anon consts with generic parameters provided. #140447 + +pub struct Foo; + +impl Foo { + const UNUSED_PARAM: usize = { + let _: [(); N]; + 3 + }; + + pub fn bar() { + match 1 { + Self::UNUSED_PARAM => (), + _ => (), + } + } +} + +fn main() {} diff --git a/tests/ui/const-generics/defaults/concrete-const-param-type.rs b/tests/ui/const-generics/defaults/concrete-const-param-type.rs new file mode 100644 index 0000000000000..c411f81192bd6 --- /dev/null +++ b/tests/ui/const-generics/defaults/concrete-const-param-type.rs @@ -0,0 +1,13 @@ +#![feature(generic_const_parameter_types, unsized_const_params, adt_const_params)] +//~^ WARN the feature `generic_const_parameter_types` is incomplete +//~| WARN the feature `unsized_const_params` is incomplete +// Make sure that we test the const param type of default const parameters +// if both the type of the default and the type of the parameter are concrete. + +use std::marker::ConstParamTy_; + +struct Foo; //~ ERROR the constant `N` is not of type `u64` +struct Bar(T); // ok +struct Baz(T); // ok + +fn main() {} diff --git a/tests/ui/const-generics/defaults/concrete-const-param-type.stderr b/tests/ui/const-generics/defaults/concrete-const-param-type.stderr new file mode 100644 index 0000000000000..ad077f87e5dfb --- /dev/null +++ b/tests/ui/const-generics/defaults/concrete-const-param-type.stderr @@ -0,0 +1,25 @@ +warning: the feature `generic_const_parameter_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/concrete-const-param-type.rs:1:12 + | +LL | #![feature(generic_const_parameter_types, unsized_const_params, adt_const_params)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #137626 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/concrete-const-param-type.rs:1:43 + | +LL | #![feature(generic_const_parameter_types, unsized_const_params, adt_const_params)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #95174 for more information + +error: the constant `N` is not of type `u64` + --> $DIR/concrete-const-param-type.rs:9:26 + | +LL | struct Foo; + | ^^^^^^^^^^^^^^^^ expected `u64`, found `u32` + +error: aborting due to 1 previous error; 2 warnings emitted + diff --git a/tests/ui/const-generics/defaults/default-const-param-cannot-reference-self.stderr b/tests/ui/const-generics/defaults/default-const-param-cannot-reference-self.stderr index 72d7001fdf1b0..c074e2e897e9a 100644 --- a/tests/ui/const-generics/defaults/default-const-param-cannot-reference-self.stderr +++ b/tests/ui/const-generics/defaults/default-const-param-cannot-reference-self.stderr @@ -2,19 +2,19 @@ error[E0735]: generic parameters cannot use `Self` in their defaults --> $DIR/default-const-param-cannot-reference-self.rs:1:34 | LL | struct Struct; - | ^^^^ `Self` in generic parameter default + | ^^^^ error[E0735]: generic parameters cannot use `Self` in their defaults --> $DIR/default-const-param-cannot-reference-self.rs:4:30 | LL | enum Enum { } - | ^^^^ `Self` in generic parameter default + | ^^^^ error[E0735]: generic parameters cannot use `Self` in their defaults --> $DIR/default-const-param-cannot-reference-self.rs:7:32 | LL | union Union { not_empty: () } - | ^^^^ `Self` in generic parameter default + | ^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/defaults/forward-declared.rs b/tests/ui/const-generics/defaults/forward-declared.rs index ede3d873bdcf6..bd3abd4dcaa6a 100644 --- a/tests/ui/const-generics/defaults/forward-declared.rs +++ b/tests/ui/const-generics/defaults/forward-declared.rs @@ -1,13 +1,13 @@ struct Foo; -//~^ ERROR generic parameters with a default cannot use forward declared identifiers +//~^ ERROR generic parameter defaults cannot reference parameters before they are declared enum Bar {} -//~^ ERROR generic parameters with a default cannot use forward declared identifiers +//~^ ERROR generic parameter defaults cannot reference parameters before they are declared struct Foo2; -//~^ ERROR generic parameters with a default cannot use forward declared identifiers +//~^ ERROR generic parameter defaults cannot reference parameters before they are declared enum Bar2 {} -//~^ ERROR generic parameters with a default cannot use forward declared identifiers +//~^ ERROR generic parameter defaults cannot reference parameters before they are declared fn main() {} diff --git a/tests/ui/const-generics/defaults/forward-declared.stderr b/tests/ui/const-generics/defaults/forward-declared.stderr index 4856c7a1fd2d8..4331996bffcd9 100644 --- a/tests/ui/const-generics/defaults/forward-declared.stderr +++ b/tests/ui/const-generics/defaults/forward-declared.stderr @@ -1,26 +1,26 @@ -error[E0128]: generic parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameter defaults cannot reference parameters before they are declared --> $DIR/forward-declared.rs:1:29 | LL | struct Foo; - | ^ defaulted generic parameters cannot be forward declared + | ^ cannot reference `M` before it is declared -error[E0128]: generic parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameter defaults cannot reference parameters before they are declared --> $DIR/forward-declared.rs:4:27 | LL | enum Bar {} - | ^ defaulted generic parameters cannot be forward declared + | ^ cannot reference `M` before it is declared -error[E0128]: generic parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameter defaults cannot reference parameters before they are declared --> $DIR/forward-declared.rs:7:30 | LL | struct Foo2; - | ^ defaulted generic parameters cannot be forward declared + | ^ cannot reference `N` before it is declared -error[E0128]: generic parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameter defaults cannot reference parameters before they are declared --> $DIR/forward-declared.rs:10:28 | LL | enum Bar2 {} - | ^ defaulted generic parameters cannot be forward declared + | ^ cannot reference `N` before it is declared error: aborting due to 4 previous errors diff --git a/tests/ui/const-generics/defaults/mismatch.rs b/tests/ui/const-generics/defaults/mismatch.rs index ec131505ed755..bbedf247bd0a4 100644 --- a/tests/ui/const-generics/defaults/mismatch.rs +++ b/tests/ui/const-generics/defaults/mismatch.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + pub struct Example; pub struct Example2(T); pub struct Example3(T); @@ -5,18 +7,18 @@ pub struct Example4; fn main() { let e: Example<13> = (); - //~^ Error: mismatched types - //~| expected struct `Example` + //~^ ERROR mismatched types + //~| NOTE expected struct `Example` let e: Example2 = (); - //~^ Error: mismatched types - //~| expected struct `Example2` + //~^ ERROR mismatched types + //~| NOTE expected struct `Example2` let e: Example3<13, u32> = (); - //~^ Error: mismatched types - //~| expected struct `Example3` + //~^ ERROR mismatched types + //~| NOTE expected struct `Example3` let e: Example3<7> = (); - //~^ Error: mismatched types - //~| expected struct `Example3<7>` + //~^ ERROR mismatched types + //~| NOTE expected struct `Example3<7>` let e: Example4<7> = (); - //~^ Error: mismatched types - //~| expected struct `Example4<7>` + //~^ ERROR mismatched types + //~| NOTE expected struct `Example4<7>` } diff --git a/tests/ui/const-generics/defaults/mismatch.stderr b/tests/ui/const-generics/defaults/mismatch.stderr index 9c4f0bc950b2d..8cedd1a28c43b 100644 --- a/tests/ui/const-generics/defaults/mismatch.stderr +++ b/tests/ui/const-generics/defaults/mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/mismatch.rs:7:26 + --> $DIR/mismatch.rs:9:26 | LL | let e: Example<13> = (); | ----------- ^^ expected `Example`, found `()` @@ -10,7 +10,7 @@ LL | let e: Example<13> = (); found unit type `()` error[E0308]: mismatched types - --> $DIR/mismatch.rs:10:32 + --> $DIR/mismatch.rs:12:32 | LL | let e: Example2 = (); | ----------------- ^^ expected `Example2`, found `()` @@ -21,7 +21,7 @@ LL | let e: Example2 = (); found unit type `()` error[E0308]: mismatched types - --> $DIR/mismatch.rs:13:32 + --> $DIR/mismatch.rs:15:32 | LL | let e: Example3<13, u32> = (); | ----------------- ^^ expected `Example3`, found `()` @@ -32,7 +32,7 @@ LL | let e: Example3<13, u32> = (); found unit type `()` error[E0308]: mismatched types - --> $DIR/mismatch.rs:16:26 + --> $DIR/mismatch.rs:18:26 | LL | let e: Example3<7> = (); | ----------- ^^ expected `Example3<7>`, found `()` @@ -43,7 +43,7 @@ LL | let e: Example3<7> = (); found unit type `()` error[E0308]: mismatched types - --> $DIR/mismatch.rs:19:26 + --> $DIR/mismatch.rs:21:26 | LL | let e: Example4<7> = (); | ----------- ^^ expected `Example4<7>`, found `()` diff --git a/tests/ui/const-generics/defaults/pretty-printing-ast.rs b/tests/ui/const-generics/defaults/pretty-printing-ast.rs index 20bf900d9f3e4..f7a166d00d551 100644 --- a/tests/ui/const-generics/defaults/pretty-printing-ast.rs +++ b/tests/ui/const-generics/defaults/pretty-printing-ast.rs @@ -1,6 +1,7 @@ // Test the AST pretty printer correctly handles default values for const generics //@ check-pass //@ compile-flags: -Z unpretty=expanded +//@ edition: 2015 #![crate_type = "lib"] diff --git a/tests/ui/const-generics/defaults/pretty-printing-ast.stdout b/tests/ui/const-generics/defaults/pretty-printing-ast.stdout index f1cd1451700fa..b6cb7fa09c880 100644 --- a/tests/ui/const-generics/defaults/pretty-printing-ast.stdout +++ b/tests/ui/const-generics/defaults/pretty-printing-ast.stdout @@ -3,6 +3,7 @@ // Test the AST pretty printer correctly handles default values for const generics //@ check-pass //@ compile-flags: -Z unpretty=expanded +//@ edition: 2015 #![crate_type = "lib"] #[prelude_import] diff --git a/tests/ui/const-generics/dont-evaluate-array-len-on-err-1.rs b/tests/ui/const-generics/dont-evaluate-array-len-on-err-1.rs index 6c4ee1af210ba..e7f050dae3671 100644 --- a/tests/ui/const-generics/dont-evaluate-array-len-on-err-1.rs +++ b/tests/ui/const-generics/dont-evaluate-array-len-on-err-1.rs @@ -13,7 +13,7 @@ trait Foo { [Adt; std::mem::size_of::()]: , { <[Adt; std::mem::size_of::()] as Foo>::bar() - //~^ Error: the trait bound + //~^ ERROR the trait bound } fn bar() {} diff --git a/tests/ui/const-generics/early/invalid-const-arguments.rs b/tests/ui/const-generics/early/invalid-const-arguments.rs index 6619c97588596..68e6b2ac458e0 100644 --- a/tests/ui/const-generics/early/invalid-const-arguments.rs +++ b/tests/ui/const-generics/early/invalid-const-arguments.rs @@ -4,7 +4,7 @@ struct A; trait Foo {} impl Foo for A {} //~^ ERROR cannot find type -//~| unresolved item provided when a constant +//~| ERROR unresolved item provided when a constant struct B; impl Foo for B {} @@ -13,4 +13,4 @@ impl Foo for B {} struct C; impl Foo for C {} //~^ ERROR cannot find type -//~| unresolved item provided when a constant +//~| ERROR unresolved item provided when a constant diff --git a/tests/ui/const-generics/generic_arg_infer/parend_infer.nogate.stderr b/tests/ui/const-generics/generic_arg_infer/parend_infer.nogate.stderr new file mode 100644 index 0000000000000..d0a5da9676df4 --- /dev/null +++ b/tests/ui/const-generics/generic_arg_infer/parend_infer.nogate.stderr @@ -0,0 +1,53 @@ +error[E0658]: const arguments cannot yet be inferred with `_` + --> $DIR/parend_infer.rs:24:16 + | +LL | let c: Foo<_> = Foo::<1>; + | ^ + | + = note: see issue #85077 for more information + = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: const arguments cannot yet be inferred with `_` + --> $DIR/parend_infer.rs:26:16 + | +LL | let c: Foo<(_)> = Foo::<1>; + | ^^^ + | + = note: see issue #85077 for more information + = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: const arguments cannot yet be inferred with `_` + --> $DIR/parend_infer.rs:28:16 + | +LL | let c: Foo<(((_)))> = Foo::<1>; + | ^^^^^^^ + | + = note: see issue #85077 for more information + = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: using `_` for array lengths is unstable + --> $DIR/parend_infer.rs:17:17 + | +LL | let b: [u8; (_)] = [1; (((((_)))))]; + | ^^^ + | + = note: see issue #85077 for more information + = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: using `_` for array lengths is unstable + --> $DIR/parend_infer.rs:17:28 + | +LL | let b: [u8; (_)] = [1; (((((_)))))]; + | ^^^^^^^^^^^ + | + = note: see issue #85077 for more information + = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/const-generics/generic_arg_infer/parend_infer.rs b/tests/ui/const-generics/generic_arg_infer/parend_infer.rs index 81c42183b3833..3dc27a702de40 100644 --- a/tests/ui/const-generics/generic_arg_infer/parend_infer.rs +++ b/tests/ui/const-generics/generic_arg_infer/parend_infer.rs @@ -1,7 +1,9 @@ -//@ check-pass +//@[gate] check-pass //@ revisions: gate nogate #![cfg_attr(gate, feature(generic_arg_infer))] +struct Foo; + fn main() { // AST Types preserve parens for pretty printing reasons. This means // that this is parsed as a `TyKind::Paren(TyKind::Infer)`. Generic @@ -9,4 +11,20 @@ fn main() { // but `TyKind::Infer` wrapped in arbitrarily many `TyKind::Paren`. let a: Vec<(_)> = vec![1_u8]; let a: Vec<(((((_)))))> = vec![1_u8]; + + // AST Exprs similarly preserve parens for pretty printing reasons. + #[rustfmt::skip] + let b: [u8; (_)] = [1; (((((_)))))]; + //[nogate]~^ error: using `_` for array lengths is unstable + //[nogate]~| error: using `_` for array lengths is unstable + let b: [u8; 2] = b; + + // This is the same case as AST types as the parser doesn't distinguish between const + // and type args when they share syntax + let c: Foo<_> = Foo::<1>; + //[nogate]~^ error: const arguments cannot yet be inferred with `_` + let c: Foo<(_)> = Foo::<1>; + //[nogate]~^ error: const arguments cannot yet be inferred with `_` + let c: Foo<(((_)))> = Foo::<1>; + //[nogate]~^ error: const arguments cannot yet be inferred with `_` } diff --git a/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.rs b/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.rs index 7561ae2febbdf..33872ce7f0f26 100644 --- a/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.rs +++ b/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.rs @@ -15,15 +15,15 @@ where // errors are bad but seems to be pre-existing issue #86198 assert_impl::>(); - //~^ Error: mismatched types - //~^^ Error: unconstrained generic constant + //~^ ERROR mismatched types + //~^^ ERROR unconstrained generic constant assert_impl::>(); - //~^ Error: mismatched types - //~^^ Error: unconstrained generic constant + //~^ ERROR mismatched types + //~^^ ERROR unconstrained generic constant assert_impl::>(); - //~^ Error: mismatched types + //~^ ERROR mismatched types assert_impl::>(); - //~^ Error: mismatched types + //~^ ERROR mismatched types } pub fn use_trait_impl_2() where @@ -33,15 +33,15 @@ where // errors are bad but seems to be pre-existing issue #86198 assert_impl::>(); - //~^ Error: mismatched types - //~^^ Error: unconstrained generic constant + //~^ ERROR mismatched types + //~^^ ERROR unconstrained generic constant assert_impl::>(); - //~^ Error: mismatched types - //~^^ Error: unconstrained generic constant + //~^ ERROR mismatched types + //~^^ ERROR unconstrained generic constant assert_impl::>(); - //~^ Error: mismatched types + //~^ ERROR mismatched types assert_impl::>(); - //~^ Error: mismatched types + //~^ ERROR mismatched types } fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/const-generics-closure.rs b/tests/ui/const-generics/generic_const_exprs/const-generics-closure.rs new file mode 100644 index 0000000000000..aad8cefe5d62f --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/const-generics-closure.rs @@ -0,0 +1,13 @@ +// Regression test for issue #127424 + +fn bar() -> impl Into< + [u8; { + //~^ ERROR mismatched types [E0308] + let _ = for<'a, 'b> |x: &'a &'a Vec<&'b u32>, b: bool| -> &'a Vec<&'b u32> { *x }; + //~^ ERROR `for<...>` binders for closures are experimental [E0658] + }], +> { + [89] +} + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/const-generics-closure.stderr b/tests/ui/const-generics/generic_const_exprs/const-generics-closure.stderr new file mode 100644 index 0000000000000..5410bbdc12536 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/const-generics-closure.stderr @@ -0,0 +1,26 @@ +error[E0658]: `for<...>` binders for closures are experimental + --> $DIR/const-generics-closure.rs:6:17 + | +LL | let _ = for<'a, 'b> |x: &'a &'a Vec<&'b u32>, b: bool| -> &'a Vec<&'b u32> { *x }; + | ^^^^^^^^^^^ + | + = note: see issue #97362 for more information + = help: add `#![feature(closure_lifetime_binder)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider removing `for<...>` + +error[E0308]: mismatched types + --> $DIR/const-generics-closure.rs:4:10 + | +LL | [u8; { + | __________^ +LL | | +LL | | let _ = for<'a, 'b> |x: &'a &'a Vec<&'b u32>, b: bool| -> &'a Vec<&'b u32> { *x }; +LL | | +LL | | }], + | |_____^ expected `usize`, found `()` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr b/tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr index a05aaf2af649e..1aac357d60e43 100644 --- a/tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr +++ b/tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr @@ -39,14 +39,6 @@ error: unconstrained generic constant LL | let _ = const_evaluatable_lib::test1::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: required by a bound in `test1` - --> $DIR/auxiliary/const_evaluatable_lib.rs:5:10 - | -LL | pub fn test1() -> [u8; std::mem::size_of::() - 1] - | ----- required by a bound in this function -LL | where -LL | [u8; std::mem::size_of::() - 1]: Sized, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1` help: try adding a `where` bound | LL | fn user() where [(); std::mem::size_of::() - 1]: { @@ -59,10 +51,13 @@ LL | let _ = const_evaluatable_lib::test1::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: required by a bound in `test1` - --> $DIR/auxiliary/const_evaluatable_lib.rs:3:27 + --> $DIR/auxiliary/const_evaluatable_lib.rs:5:10 | LL | pub fn test1() -> [u8; std::mem::size_of::() - 1] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1` + | ----- required by a bound in this function +LL | where +LL | [u8; std::mem::size_of::() - 1]: Sized, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1` help: try adding a `where` bound | LL | fn user() where [(); std::mem::size_of::() - 1]: { diff --git a/tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr b/tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr index 7e318f8786f39..f6119c17bf47e 100644 --- a/tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr +++ b/tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr @@ -1,27 +1,27 @@ -error: overly complex generic constant - --> $DIR/dependence_lint.rs:21:17 - | -LL | let _: [u8; if true { size_of::() } else { 3 }]; // error on stable, error with gce - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants - | - = help: consider moving this anonymous constant into a `const` function - error: unconstrained generic constant - --> $DIR/dependence_lint.rs:14:12 + --> $DIR/dependence_lint.rs:10:9 | -LL | let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs` + | ^^^^^^^^^^^^^^^^^^^ | help: try adding a `where` bound | LL | fn foo() where [(); size_of::<*mut T>()]: { | ++++++++++++++++++++++++++++++++ +error: overly complex generic constant + --> $DIR/dependence_lint.rs:17:9 + | +LL | [0; if false { size_of::() } else { 3 }]; // lint on stable, error with gce + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants + | + = help: consider moving this anonymous constant into a `const` function + error: unconstrained generic constant - --> $DIR/dependence_lint.rs:10:9 + --> $DIR/dependence_lint.rs:14:12 | -LL | [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs` - | ^^^^^^^^^^^^^^^^^^^ +LL | let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: try adding a `where` bound | @@ -29,10 +29,10 @@ LL | fn foo() where [(); size_of::<*mut T>()]: { | ++++++++++++++++++++++++++++++++ error: overly complex generic constant - --> $DIR/dependence_lint.rs:17:9 + --> $DIR/dependence_lint.rs:21:17 | -LL | [0; if false { size_of::() } else { 3 }]; // lint on stable, error with gce - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants +LL | let _: [u8; if true { size_of::() } else { 3 }]; // error on stable, error with gce + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants | = help: consider moving this anonymous constant into a `const` function diff --git a/tests/ui/const-generics/generic_const_exprs/issue-72787.rs b/tests/ui/const-generics/generic_const_exprs/issue-72787.rs index c3208786708b5..ea65b6d3fdf26 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-72787.rs +++ b/tests/ui/const-generics/generic_const_exprs/issue-72787.rs @@ -9,8 +9,8 @@ pub trait True {} impl True for IsLessOrEqual where Condition<{ LHS <= RHS }>: True -//[min]~^ Error generic parameters may not be used in const operations -//[min]~| Error generic parameters may not be used in const operations +//[min]~^ ERROR generic parameters may not be used in const operations +//[min]~| ERROR generic parameters may not be used in const operations { } impl True for Condition {} @@ -21,8 +21,8 @@ where IsLessOrEqual: True, IsLessOrEqual: True, IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, -//[min]~^ Error generic parameters may not be used in const operations -//[min]~| Error generic parameters may not be used in const operations +//[min]~^ ERROR generic parameters may not be used in const operations +//[min]~| ERROR generic parameters may not be used in const operations // Condition<{ 8 - I <= 8 - J }>: True, { fn print() { diff --git a/tests/ui/const-generics/generic_const_exprs/issue-79518-default_trait_method_normalization.rs b/tests/ui/const-generics/generic_const_exprs/issue-79518-default_trait_method_normalization.rs index 2fa9a71fbb33c..f08b9ceffb966 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-79518-default_trait_method_normalization.rs +++ b/tests/ui/const-generics/generic_const_exprs/issue-79518-default_trait_method_normalization.rs @@ -14,7 +14,7 @@ trait Foo { [(); std::mem::size_of::()]: , { Self::AssocInstance == [(); std::mem::size_of::()]; - //~^ Error: mismatched types + //~^ ERROR mismatched types } } diff --git a/tests/ui/const-generics/generic_const_exprs/opaque_type.rs b/tests/ui/const-generics/generic_const_exprs/opaque_type.rs index 56b8acbf88cde..446112cc617d8 100644 --- a/tests/ui/const-generics/generic_const_exprs/opaque_type.rs +++ b/tests/ui/const-generics/generic_const_exprs/opaque_type.rs @@ -3,14 +3,14 @@ type Foo = impl Sized; -fn with_bound() -> Foo +#[define_opaque(Foo)] +fn with_bound() where [u8; (N / 2) as usize]: Sized, { let _: [u8; (N / 2) as Foo] = [0; (N / 2) as usize]; //~^ ERROR mismatched types //~| ERROR non-primitive cast: `usize` as `Foo` - todo!() } fn main() { diff --git a/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr b/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr index e9fb8c0f403ae..9f48a8563c8ab 100644 --- a/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr +++ b/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/opaque_type.rs:10:17 + --> $DIR/opaque_type.rs:11:17 | LL | type Foo = impl Sized; | ---------- the found opaque type @@ -11,7 +11,7 @@ LL | let _: [u8; (N / 2) as Foo] = [0; (N / 2) as usize]; found opaque type `Foo` error[E0605]: non-primitive cast: `usize` as `Foo` - --> $DIR/opaque_type.rs:10:17 + --> $DIR/opaque_type.rs:11:17 | LL | let _: [u8; (N / 2) as Foo] = [0; (N / 2) as usize]; | ^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object diff --git a/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr b/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr index 30a45ce377e7e..b73611c79b291 100644 --- a/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr +++ b/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr @@ -54,12 +54,6 @@ warning: type `v17` should have an upper camel case name LL | pub struct v17 { | ^^^ help: convert the identifier to upper camel case (notice the capitalization): `V17` -error[E0425]: cannot find function `v6` in this scope - --> $DIR/unevaluated-const-ice-119731.rs:13:35 - | -LL | const v0: [[usize; v4]; v4] = v6(v8); - | ^^ not found in this scope - error: `[[usize; v4]; v4]` is forbidden as the type of a const generic parameter --> $DIR/unevaluated-const-ice-119731.rs:16:48 | @@ -72,6 +66,12 @@ help: add `#![feature(adt_const_params)]` to the crate attributes to enable more LL + #![feature(adt_const_params)] | +error[E0425]: cannot find function `v6` in this scope + --> $DIR/unevaluated-const-ice-119731.rs:13:35 + | +LL | const v0: [[usize; v4]; v4] = v6(v8); + | ^^ not found in this scope + error: maximum number of nodes exceeded in constant v20::v17::::{constant#0} --> $DIR/unevaluated-const-ice-119731.rs:28:37 | diff --git a/tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.rs b/tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.rs new file mode 100644 index 0000000000000..bc54aad59ba96 --- /dev/null +++ b/tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.rs @@ -0,0 +1,10 @@ +struct Foo<'a>(&'a ()); + +// We need a lifetime in scope or else we do not write a user type annotation as a fast-path. +impl<'a> Foo<'a> { + fn bar() { + let V; + //~^ ERROR constant parameters cannot be referenced in patterns + } +} +fn main() {} diff --git a/tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.stderr b/tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.stderr new file mode 100644 index 0000000000000..299ff4165e0d5 --- /dev/null +++ b/tests/ui/const-generics/generic_const_parameter_types/bad-param-in-pat.stderr @@ -0,0 +1,11 @@ +error[E0158]: constant parameters cannot be referenced in patterns + --> $DIR/bad-param-in-pat.rs:6:13 + | +LL | fn bar() { + | ----------- constant defined here +LL | let V; + | ^ can't be used in patterns + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0158`. diff --git a/tests/ui/const-generics/generic_const_parameter_types/check-type-in-mir.rs b/tests/ui/const-generics/generic_const_parameter_types/check-type-in-mir.rs new file mode 100644 index 0000000000000..d3bc544ed6c58 --- /dev/null +++ b/tests/ui/const-generics/generic_const_parameter_types/check-type-in-mir.rs @@ -0,0 +1,12 @@ +// Ensure that we actually treat `N`'s type as `&'a u32` in MIR typeck. + +#![feature(unsized_const_params, adt_const_params, generic_const_parameter_types)] +//~^ WARN the feature `unsized_const_params` is incomplete +//~| WARN the feature `generic_const_parameter_types` is incomplete + +fn foo<'a, const N: &'a u32>() { + let b: &'static u32 = N; + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_parameter_types/check-type-in-mir.stderr b/tests/ui/const-generics/generic_const_parameter_types/check-type-in-mir.stderr new file mode 100644 index 0000000000000..3dc7ce438fa93 --- /dev/null +++ b/tests/ui/const-generics/generic_const_parameter_types/check-type-in-mir.stderr @@ -0,0 +1,27 @@ +warning: the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/check-type-in-mir.rs:3:12 + | +LL | #![feature(unsized_const_params, adt_const_params, generic_const_parameter_types)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #95174 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: the feature `generic_const_parameter_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/check-type-in-mir.rs:3:52 + | +LL | #![feature(unsized_const_params, adt_const_params, generic_const_parameter_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #137626 for more information + +error: lifetime may not live long enough + --> $DIR/check-type-in-mir.rs:8:12 + | +LL | fn foo<'a, const N: &'a u32>() { + | -- lifetime `'a` defined here +LL | let b: &'static u32 = N; + | ^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + +error: aborting due to 1 previous error; 2 warnings emitted + diff --git a/tests/ui/const-generics/generic_const_parameter_types/forward_declared_type.rs b/tests/ui/const-generics/generic_const_parameter_types/forward_declared_type.rs index 91c1c80e07c88..2abf7f44e2030 100644 --- a/tests/ui/const-generics/generic_const_parameter_types/forward_declared_type.rs +++ b/tests/ui/const-generics/generic_const_parameter_types/forward_declared_type.rs @@ -4,8 +4,8 @@ use std::marker::PhantomData; struct UsesConst; -//~^ ERROR: the type of const parameters must not depend on other generic parameters +//~^ ERROR: const parameter types cannot reference parameters before they are declared struct UsesType(PhantomData); -//~^ ERROR: the type of const parameters must not depend on other generic parameters +//~^ ERROR: const parameter types cannot reference parameters before they are declared fn main() {} diff --git a/tests/ui/const-generics/generic_const_parameter_types/forward_declared_type.stderr b/tests/ui/const-generics/generic_const_parameter_types/forward_declared_type.stderr index b0dbdff84136b..5526668b0be91 100644 --- a/tests/ui/const-generics/generic_const_parameter_types/forward_declared_type.stderr +++ b/tests/ui/const-generics/generic_const_parameter_types/forward_declared_type.stderr @@ -1,15 +1,14 @@ -error[E0770]: the type of const parameters must not depend on other generic parameters +error: const parameter types cannot reference parameters before they are declared --> $DIR/forward_declared_type.rs:6:32 | LL | struct UsesConst; - | ^ the type must not depend on the parameter `M` + | ^ const parameter type cannot reference `M` before it is declared -error[E0770]: the type of const parameters must not depend on other generic parameters +error: const parameter types cannot reference parameters before they are declared --> $DIR/forward_declared_type.rs:8:27 | LL | struct UsesType(PhantomData); - | ^ the type must not depend on the parameter `T` + | ^ const parameter type cannot reference `T` before it is declared error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.feat.stderr b/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.feat.stderr new file mode 100644 index 0000000000000..2d47797aef284 --- /dev/null +++ b/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.feat.stderr @@ -0,0 +1,25 @@ +warning: the feature `generic_const_parameter_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/references-parent-generics.rs:3:27 + | +LL | #![cfg_attr(feat, feature(generic_const_parameter_types))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #137626 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: `Self` is forbidden as the type of a const generic parameter + --> $DIR/references-parent-generics.rs:7:25 + | +LL | type Assoc; + | ^^^^ + | + = note: the only supported types are integers, `bool`, and `char` + +error: anonymous constants referencing generics are not yet supported + --> $DIR/references-parent-generics.rs:15:21 + | +LL | let x: T::Assoc<3>; + | ^ + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.nofeat.stderr b/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.nofeat.stderr new file mode 100644 index 0000000000000..68ce17317f69f --- /dev/null +++ b/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.nofeat.stderr @@ -0,0 +1,9 @@ +error[E0770]: the type of const parameters must not depend on other generic parameters + --> $DIR/references-parent-generics.rs:7:25 + | +LL | type Assoc; + | ^^^^ the type must not depend on the parameter `Self` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.rs b/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.rs new file mode 100644 index 0000000000000..b91050d540c90 --- /dev/null +++ b/tests/ui/const-generics/generic_const_parameter_types/references-parent-generics.rs @@ -0,0 +1,19 @@ +//@ revisions: feat nofeat + +#![cfg_attr(feat, feature(generic_const_parameter_types))] +//[feat]~^ WARN the feature `generic_const_parameter_types` is incomplete + +trait Foo { + type Assoc; + //[nofeat]~^ ERROR the type of const parameters must not depend on other generic parameters + //[feat]~^^ ERROR `Self` is forbidden as the type of a const generic parameter +} + +fn foo() { + // We used to end up feeding the type of this anon const to be `T`, but the anon const + // doesn't inherit the generics of `foo`, which led to index oob errors. + let x: T::Assoc<3>; + //[feat]~^ ERROR anonymous constants referencing generics are not yet supported +} + +fn main() {} diff --git a/tests/ui/const-generics/invalid-enum.stderr b/tests/ui/const-generics/invalid-enum.stderr index 7e8a632b34f79..f588ff0bc8eb4 100644 --- a/tests/ui/const-generics/invalid-enum.stderr +++ b/tests/ui/const-generics/invalid-enum.stderr @@ -25,28 +25,6 @@ LL | let _: Example = Example { x: 0 }; | not a type | help: try using the variant's enum: `CompileFlag` -error[E0747]: unresolved item provided when a constant was expected - --> $DIR/invalid-enum.rs:31:18 - | -LL | let _: Example = Example { x: 0 }; - | ^^^^^^^^^^^^^^ - | -help: if this generic argument was intended as a const parameter, surround it with braces - | -LL | let _: Example<{ CompileFlag::A }, _> = Example { x: 0 }; - | + + - -error[E0747]: type provided when a constant was expected - --> $DIR/invalid-enum.rs:35:18 - | -LL | let _: Example = Example { x: 0 }; - | ^^^^^^^^^^^^^^^^^^^ - | -help: if this generic argument was intended as a const parameter, surround it with braces - | -LL | let _: Example<{ Example::ASSOC_FLAG }, _> = Example { x: 0 }; - | + + - error[E0747]: unresolved item provided when a constant was expected --> $DIR/invalid-enum.rs:23:12 | @@ -69,6 +47,28 @@ help: if this generic argument was intended as a const parameter, surround it wi LL | test_2::<_, { CompileFlag::A }>(0); | + + +error[E0747]: unresolved item provided when a constant was expected + --> $DIR/invalid-enum.rs:31:18 + | +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^ + | +help: if this generic argument was intended as a const parameter, surround it with braces + | +LL | let _: Example<{ CompileFlag::A }, _> = Example { x: 0 }; + | + + + +error[E0747]: type provided when a constant was expected + --> $DIR/invalid-enum.rs:35:18 + | +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^^^^^^ + | +help: if this generic argument was intended as a const parameter, surround it with braces + | +LL | let _: Example<{ Example::ASSOC_FLAG }, _> = Example { x: 0 }; + | + + + error: aborting due to 7 previous errors Some errors have detailed explanations: E0573, E0747. diff --git a/tests/ui/const-generics/issues/issue-100313.rs b/tests/ui/const-generics/issues/issue-100313.rs index 7ce2a3b908c59..7d43d7bee3470 100644 --- a/tests/ui/const-generics/issues/issue-100313.rs +++ b/tests/ui/const-generics/issues/issue-100313.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + #![allow(incomplete_features)] #![feature(adt_const_params, unsized_const_params)] @@ -6,7 +8,7 @@ struct T; impl T { const fn set_false(&self) { unsafe { - *(B as *const bool as *mut bool) = false; //~ inside `T + *(B as *const bool as *mut bool) = false; //~ NOTE inside `T } } } diff --git a/tests/ui/const-generics/issues/issue-100313.stderr b/tests/ui/const-generics/issues/issue-100313.stderr index 03a658a83aff9..98c3ec5379b5e 100644 --- a/tests/ui/const-generics/issues/issue-100313.stderr +++ b/tests/ui/const-generics/issues/issue-100313.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/issue-100313.rs:16:5 + --> $DIR/issue-100313.rs:18:5 | LL | x.set_false(); | ^^^^^^^^^^^^^ writing to ALLOC0 which is read-only | note: inside `T::<&true>::set_false` - --> $DIR/issue-100313.rs:9:13 + --> $DIR/issue-100313.rs:11:13 | LL | *(B as *const bool as *mut bool) = false; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the failure occurred here diff --git a/tests/ui/const-generics/issues/issue-68366.rs b/tests/ui/const-generics/issues/issue-68366.rs index d9d5e21857b13..6f745b260b633 100644 --- a/tests/ui/const-generics/issues/issue-68366.rs +++ b/tests/ui/const-generics/issues/issue-68366.rs @@ -11,7 +11,7 @@ struct Collatz>; impl Collatz<{Some(N)}> {} //~^ ERROR the const parameter -//[min]~^^ generic parameters may not be used in const operations +//[min]~^^ ERROR generic parameters may not be used in const operations //[full]~^^^ ERROR overly complex struct Foo; diff --git a/tests/ui/const-generics/issues/issue-74950.rs b/tests/ui/const-generics/issues/issue-74950.rs index f79676ccee8ed..150566e98c04e 100644 --- a/tests/ui/const-generics/issues/issue-74950.rs +++ b/tests/ui/const-generics/issues/issue-74950.rs @@ -17,9 +17,9 @@ struct Inner; // - impl StructuralPartialEq #[derive(PartialEq, Eq)] struct Outer; -//[min]~^ `Inner` is forbidden -//[min]~| `Inner` is forbidden -//[min]~| `Inner` is forbidden -//[min]~| `Inner` is forbidden +//[min]~^ ERROR `Inner` is forbidden +//[min]~| ERROR `Inner` is forbidden +//[min]~| ERROR `Inner` is forbidden +//[min]~| ERROR `Inner` is forbidden fn main() {} diff --git a/tests/ui/const-generics/issues/issue-90318.rs b/tests/ui/const-generics/issues/issue-90318.rs index cebc1ce214208..317ddad49cd4e 100644 --- a/tests/ui/const-generics/issues/issue-90318.rs +++ b/tests/ui/const-generics/issues/issue-90318.rs @@ -12,7 +12,7 @@ impl True for If {} fn consume(_val: T) where If<{ TypeId::of::() != TypeId::of::<()>() }>: True, - //~^ overly complex generic constant + //~^ ERROR overly complex generic constant //~| ERROR: cannot call { } @@ -20,7 +20,7 @@ where fn test() where If<{ TypeId::of::() != TypeId::of::<()>() }>: True, - //~^ overly complex generic constant + //~^ ERROR overly complex generic constant //~| ERROR: cannot call { } diff --git a/tests/ui/const-generics/mgca/ambiguous-assoc-const.rs b/tests/ui/const-generics/mgca/ambiguous-assoc-const.rs new file mode 100644 index 0000000000000..d7df9c22afd69 --- /dev/null +++ b/tests/ui/const-generics/mgca/ambiguous-assoc-const.rs @@ -0,0 +1,15 @@ +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait Tr { + const N: usize; +} + +struct Blah; + +fn foo() -> Blah<{ Tr::N }> { + //~^ ERROR ambiguous associated constant + todo!() +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/ambiguous-assoc-const.stderr b/tests/ui/const-generics/mgca/ambiguous-assoc-const.stderr new file mode 100644 index 0000000000000..11c63bdbcf4be --- /dev/null +++ b/tests/ui/const-generics/mgca/ambiguous-assoc-const.stderr @@ -0,0 +1,15 @@ +error[E0223]: ambiguous associated constant + --> $DIR/ambiguous-assoc-const.rs:10:20 + | +LL | fn foo() -> Blah<{ Tr::N }> { + | ^^^^^ + | +help: if there were a type named `Example` that implemented `Tr`, you could use the fully-qualified path + | +LL - fn foo() -> Blah<{ Tr::N }> { +LL + fn foo() -> Blah<{ ::N }> { + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/const-generics/mgca/assoc-const-without-type_const.rs b/tests/ui/const-generics/mgca/assoc-const-without-type_const.rs new file mode 100644 index 0000000000000..a11314c11aaee --- /dev/null +++ b/tests/ui/const-generics/mgca/assoc-const-without-type_const.rs @@ -0,0 +1,14 @@ +#![feature(min_generic_const_args)] +#![allow(incomplete_features)] + +pub trait Tr { + const SIZE: usize; +} + +fn mk_array(_x: T) -> [(); T::SIZE] { + //~^ ERROR type_const + [(); T::SIZE] + //~^ ERROR type_const +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/assoc-const-without-type_const.stderr b/tests/ui/const-generics/mgca/assoc-const-without-type_const.stderr new file mode 100644 index 0000000000000..7872e09676268 --- /dev/null +++ b/tests/ui/const-generics/mgca/assoc-const-without-type_const.stderr @@ -0,0 +1,18 @@ +error: use of trait associated const without `#[type_const]` + --> $DIR/assoc-const-without-type_const.rs:8:35 + | +LL | fn mk_array(_x: T) -> [(); T::SIZE] { + | ^^^^^^^ + | + = note: the declaration in the trait must be marked with `#[type_const]` + +error: use of trait associated const without `#[type_const]` + --> $DIR/assoc-const-without-type_const.rs:10:10 + | +LL | [(); T::SIZE] + | ^^^^^^^ + | + = note: the declaration in the trait must be marked with `#[type_const]` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/mgca/assoc-const.rs b/tests/ui/const-generics/mgca/assoc-const.rs new file mode 100644 index 0000000000000..fb5b4308a7f89 --- /dev/null +++ b/tests/ui/const-generics/mgca/assoc-const.rs @@ -0,0 +1,15 @@ +//@ check-pass + +#![feature(min_generic_const_args)] +#![allow(incomplete_features)] + +pub trait Tr { + #[type_const] + const SIZE: usize; +} + +fn mk_array>(_x: T) -> [(); >::SIZE] { + [(); T::SIZE] +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/bad-type_const-syntax.rs b/tests/ui/const-generics/mgca/bad-type_const-syntax.rs new file mode 100644 index 0000000000000..1e9673a56b563 --- /dev/null +++ b/tests/ui/const-generics/mgca/bad-type_const-syntax.rs @@ -0,0 +1,17 @@ +trait Tr { + #[type_const()] + //~^ ERROR malformed + //~| ERROR experimental + const N: usize; +} + +struct S; + +impl Tr for S { + #[type_const] + //~^ ERROR must only be applied to trait associated constants + //~| ERROR experimental + const N: usize = 0; +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/bad-type_const-syntax.stderr b/tests/ui/const-generics/mgca/bad-type_const-syntax.stderr new file mode 100644 index 0000000000000..579aff849d6e6 --- /dev/null +++ b/tests/ui/const-generics/mgca/bad-type_const-syntax.stderr @@ -0,0 +1,35 @@ +error: malformed `type_const` attribute input + --> $DIR/bad-type_const-syntax.rs:2:5 + | +LL | #[type_const()] + | ^^^^^^^^^^^^^^^ help: must be of the form: `#[type_const]` + +error[E0658]: the `#[type_const]` attribute is an experimental feature + --> $DIR/bad-type_const-syntax.rs:2:5 + | +LL | #[type_const()] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #132980 for more information + = help: add `#![feature(min_generic_const_args)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the `#[type_const]` attribute is an experimental feature + --> $DIR/bad-type_const-syntax.rs:11:5 + | +LL | #[type_const] + | ^^^^^^^^^^^^^ + | + = note: see issue #132980 for more information + = help: add `#![feature(min_generic_const_args)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: `#[type_const]` must only be applied to trait associated constants + --> $DIR/bad-type_const-syntax.rs:11:5 + | +LL | #[type_const] + | ^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/const-generics/mgca/inherent-const-gating.rs b/tests/ui/const-generics/mgca/inherent-const-gating.rs new file mode 100644 index 0000000000000..c39b8e6f7f64a --- /dev/null +++ b/tests/ui/const-generics/mgca/inherent-const-gating.rs @@ -0,0 +1,13 @@ +#![feature(min_generic_const_args)] +#![allow(incomplete_features)] + +struct S; + +impl S { + const N: usize = 42; +} + +fn main() { + let _x: [(); S::N] = todo!(); + //~^ ERROR inherent associated types are unstable +} diff --git a/tests/ui/const-generics/mgca/inherent-const-gating.stderr b/tests/ui/const-generics/mgca/inherent-const-gating.stderr new file mode 100644 index 0000000000000..71bb11ae700f4 --- /dev/null +++ b/tests/ui/const-generics/mgca/inherent-const-gating.stderr @@ -0,0 +1,13 @@ +error[E0658]: inherent associated types are unstable + --> $DIR/inherent-const-gating.rs:11:18 + | +LL | let _x: [(); S::N] = todo!(); + | ^^^^ + | + = note: see issue #8995 for more information + = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr b/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr index 11a824ba73b2a..d1f3b08dd36de 100644 --- a/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr +++ b/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/invalid-patterns.rs:38:32 + --> $DIR/invalid-patterns.rs:40:32 | LL | get_flag::(); | ^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:41:14 + --> $DIR/invalid-patterns.rs:43:14 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x42, but expected a boolean @@ -16,7 +16,7 @@ LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); } error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:43:14 + --> $DIR/invalid-patterns.rs:45:14 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x42, but expected a boolean @@ -27,31 +27,31 @@ LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } error[E0080]: evaluation of constant value failed - --> $DIR/invalid-patterns.rs:43:58 + --> $DIR/invalid-patterns.rs:45:58 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:29:21 + --> $DIR/invalid-patterns.rs:31:21 | LL | get_flag::(); | ^^^^ expected `char`, found `u8` error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:31:14 + --> $DIR/invalid-patterns.rs:33:14 | LL | get_flag::<7, 'c'>(); | ^ expected `bool`, found integer error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:14 + --> $DIR/invalid-patterns.rs:35:14 | LL | get_flag::<42, 0x5ad>(); | ^^ expected `bool`, found integer error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:18 + --> $DIR/invalid-patterns.rs:35:18 | LL | get_flag::<42, 0x5ad>(); | ^^^^^ expected `char`, found `u8` diff --git a/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr b/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr index 11a824ba73b2a..d1f3b08dd36de 100644 --- a/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr +++ b/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/invalid-patterns.rs:38:32 + --> $DIR/invalid-patterns.rs:40:32 | LL | get_flag::(); | ^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:41:14 + --> $DIR/invalid-patterns.rs:43:14 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x42, but expected a boolean @@ -16,7 +16,7 @@ LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); } error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:43:14 + --> $DIR/invalid-patterns.rs:45:14 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x42, but expected a boolean @@ -27,31 +27,31 @@ LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } error[E0080]: evaluation of constant value failed - --> $DIR/invalid-patterns.rs:43:58 + --> $DIR/invalid-patterns.rs:45:58 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:29:21 + --> $DIR/invalid-patterns.rs:31:21 | LL | get_flag::(); | ^^^^ expected `char`, found `u8` error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:31:14 + --> $DIR/invalid-patterns.rs:33:14 | LL | get_flag::<7, 'c'>(); | ^ expected `bool`, found integer error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:14 + --> $DIR/invalid-patterns.rs:35:14 | LL | get_flag::<42, 0x5ad>(); | ^^ expected `bool`, found integer error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:18 + --> $DIR/invalid-patterns.rs:35:18 | LL | get_flag::<42, 0x5ad>(); | ^^^^^ expected `char`, found `u8` diff --git a/tests/ui/const-generics/min_const_generics/invalid-patterns.rs b/tests/ui/const-generics/min_const_generics/invalid-patterns.rs index a9d2a8a5dd7a4..10b0d742a0a97 100644 --- a/tests/ui/const-generics/min_const_generics/invalid-patterns.rs +++ b/tests/ui/const-generics/min_const_generics/invalid-patterns.rs @@ -1,4 +1,6 @@ //@ stderr-per-bitwidth +//@ dont-require-annotations: NOTE + use std::mem::transmute; fn get_flag() -> Option { @@ -37,11 +39,11 @@ fn main() { get_flag::(); //~^ ERROR evaluation of constant value failed - //~| uninitialized + //~| NOTE uninitialized get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); //~^ ERROR it is undefined behavior get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); //~^ ERROR evaluation of constant value failed - //~| uninitialized + //~| NOTE uninitialized //~| ERROR it is undefined behavior } diff --git a/tests/ui/const-generics/opaque_types.rs b/tests/ui/const-generics/opaque_types.rs index 2c7170c889fb3..788d795199980 100644 --- a/tests/ui/const-generics/opaque_types.rs +++ b/tests/ui/const-generics/opaque_types.rs @@ -1,9 +1,8 @@ #![feature(type_alias_impl_trait)] type Foo = impl Sized; -//~^ ERROR: cycle -//~| ERROR: cycle +#[define_opaque(Foo)] fn foo() {} //~^ ERROR: `Foo` is forbidden as the type of a const generic parameter //~| ERROR: item does not constrain diff --git a/tests/ui/const-generics/opaque_types.stderr b/tests/ui/const-generics/opaque_types.stderr index a060488b3287e..f67e1c8ce6992 100644 --- a/tests/ui/const-generics/opaque_types.stderr +++ b/tests/ui/const-generics/opaque_types.stderr @@ -1,26 +1,26 @@ error: `Foo` is forbidden as the type of a const generic parameter - --> $DIR/opaque_types.rs:7:17 + --> $DIR/opaque_types.rs:6:17 | LL | fn foo() {} | ^^^ | = note: the only supported types are integers, `bool`, and `char` -error: item does not constrain `Foo::{opaque#0}`, but has it in its signature - --> $DIR/opaque_types.rs:7:4 +error: item does not constrain `Foo::{opaque#0}` + --> $DIR/opaque_types.rs:6:4 | LL | fn foo() {} | ^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/opaque_types.rs:3:12 | LL | type Foo = impl Sized; | ^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/opaque_types.rs:12:11 + --> $DIR/opaque_types.rs:11:11 | LL | type Foo = impl Sized; | ---------- the expected opaque type @@ -31,106 +31,6 @@ LL | foo::<42>(); = note: expected opaque type `Foo` found type `{integer}` -error[E0391]: cycle detected when computing type of `Foo::{opaque#0}` - --> $DIR/opaque_types.rs:3:12 - | -LL | type Foo = impl Sized; - | ^^^^^^^^^^ - | -note: ...which requires computing type of opaque `Foo::{opaque#0}`... - --> $DIR/opaque_types.rs:3:12 - | -LL | type Foo = impl Sized; - | ^^^^^^^^^^ -note: ...which requires type-checking `main`... - --> $DIR/opaque_types.rs:11:1 - | -LL | fn main() { - | ^^^^^^^^^ -note: ...which requires evaluating type-level constant... - --> $DIR/opaque_types.rs:12:11 - | -LL | foo::<42>(); - | ^^ -note: ...which requires const-evaluating + checking `main::{constant#0}`... - --> $DIR/opaque_types.rs:12:11 - | -LL | foo::<42>(); - | ^^ -note: ...which requires caching mir of `main::{constant#0}` for CTFE... - --> $DIR/opaque_types.rs:12:11 - | -LL | foo::<42>(); - | ^^ -note: ...which requires elaborating drops for `main::{constant#0}`... - --> $DIR/opaque_types.rs:12:11 - | -LL | foo::<42>(); - | ^^ - = note: ...which requires normalizing `Foo`... - = note: ...which again requires computing type of `Foo::{opaque#0}`, completing the cycle -note: cycle used when checking that `Foo::{opaque#0}` is well-formed - --> $DIR/opaque_types.rs:3:12 - | -LL | type Foo = impl Sized; - | ^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error[E0391]: cycle detected when computing type of opaque `Foo::{opaque#0}` - --> $DIR/opaque_types.rs:3:12 - | -LL | type Foo = impl Sized; - | ^^^^^^^^^^ - | -note: ...which requires type-checking `main`... - --> $DIR/opaque_types.rs:11:1 - | -LL | fn main() { - | ^^^^^^^^^ -note: ...which requires evaluating type-level constant... - --> $DIR/opaque_types.rs:12:11 - | -LL | foo::<42>(); - | ^^ -note: ...which requires const-evaluating + checking `main::{constant#0}`... - --> $DIR/opaque_types.rs:12:11 - | -LL | foo::<42>(); - | ^^ -note: ...which requires caching mir of `main::{constant#0}` for CTFE... - --> $DIR/opaque_types.rs:12:11 - | -LL | foo::<42>(); - | ^^ -note: ...which requires elaborating drops for `main::{constant#0}`... - --> $DIR/opaque_types.rs:12:11 - | -LL | foo::<42>(); - | ^^ -note: ...which requires borrow-checking `main::{constant#0}`... - --> $DIR/opaque_types.rs:12:11 - | -LL | foo::<42>(); - | ^^ -note: ...which requires promoting constants in MIR for `main::{constant#0}`... - --> $DIR/opaque_types.rs:12:11 - | -LL | foo::<42>(); - | ^^ -note: ...which requires const checking `main::{constant#0}`... - --> $DIR/opaque_types.rs:12:11 - | -LL | foo::<42>(); - | ^^ - = note: ...which again requires computing type of opaque `Foo::{opaque#0}`, completing the cycle -note: cycle used when computing type of `Foo::{opaque#0}` - --> $DIR/opaque_types.rs:3:12 - | -LL | type Foo = impl Sized; - | ^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0308, E0391. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/opaque_types2.rs b/tests/ui/const-generics/opaque_types2.rs index fd57438bb6171..69f813cf84ae9 100644 --- a/tests/ui/const-generics/opaque_types2.rs +++ b/tests/ui/const-generics/opaque_types2.rs @@ -4,13 +4,14 @@ type Foo = impl Sized; fn foo() {} -const C: Foo = 42; +#[define_opaque(Foo)] +const fn baz() -> Foo { + 42 +} -fn bar() -where - Foo:, -{ - foo::(); +#[define_opaque(Foo)] +fn bar() { + foo::<{ baz() }>(); //~^ ERROR: mismatched types } diff --git a/tests/ui/const-generics/opaque_types2.stderr b/tests/ui/const-generics/opaque_types2.stderr index 2fb1669b7bfab..98d96c3ccee32 100644 --- a/tests/ui/const-generics/opaque_types2.stderr +++ b/tests/ui/const-generics/opaque_types2.stderr @@ -1,11 +1,11 @@ error[E0308]: mismatched types - --> $DIR/opaque_types2.rs:13:11 + --> $DIR/opaque_types2.rs:14:13 | LL | type Foo = impl Sized; | ---------- the found opaque type ... -LL | foo::(); - | ^ expected `u32`, found opaque type +LL | foo::<{ baz() }>(); + | ^^^^^ expected `u32`, found opaque type | = note: expected type `u32` found opaque type `Foo` diff --git a/tests/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr b/tests/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr index 9f0b2efae96ce..dc28dea6851af 100644 --- a/tests/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr +++ b/tests/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr @@ -4,11 +4,11 @@ error: generic parameters with a default must be trailing LL | struct Bar(T); | ^ -error[E0128]: generic parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameter defaults cannot reference parameters before they are declared --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:21 | LL | struct Bar(T); - | ^ defaulted generic parameters cannot be forward declared + | ^ cannot reference `N` before it is declared error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr b/tests/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr index 320c9c1c84de0..3f0e5e96fc814 100644 --- a/tests/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr +++ b/tests/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr @@ -13,11 +13,11 @@ LL | struct Foo()]>(T, U); = note: type parameters may not be used in const expressions = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions -error[E0128]: generic parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameter defaults cannot reference parameters before they are declared --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:8:21 | LL | struct Bar(T); - | ^ defaulted generic parameters cannot be forward declared + | ^ cannot reference `N` before it is declared error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs b/tests/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs index 2794ff3eaa9cb..d212cc425a065 100644 --- a/tests/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs +++ b/tests/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs @@ -6,7 +6,7 @@ struct Foo()]>(T, U); //[min]~^ ERROR generic parameters may not be used in const operations struct Bar(T); -//~^ ERROR generic parameters with a default cannot use forward declared identifiers +//~^ ERROR generic parameter defaults cannot reference parameters before they are declared //~| ERROR generic parameters with a default fn main() {} diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr index a4e9c972cebbd..c73d2ca938cbe 100644 --- a/tests/ui/const-ptr/forbidden_slices.stderr +++ b/tests/ui/const-ptr/forbidden_slices.stderr @@ -109,13 +109,12 @@ note: inside `from_ptr_range::<'_, ()>` --> $SRC_DIR/core/src/slice/raw.rs:LL:COL note: inside `std::ptr::const_ptr::::offset_from_unsigned` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: could not evaluate static initializer --> $DIR/forbidden_slices.rs:54:25 | LL | from_ptr_range(ptr..ptr.add(2)) // errors inside libcore - | ^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC10 which is only 4 bytes from the end of the allocation + | ^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC10 which is only 4 bytes from the end of the allocation | note: inside `std::ptr::const_ptr::::add` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -170,7 +169,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/forbidden_slices.rs:79:25 | LL | from_ptr_range(ptr..ptr.add(1)) - | ^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC11+0x1 which is only 7 bytes from the end of the allocation + | ^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 8 bytes, but got ALLOC11+0x1 which is only 7 bytes from the end of the allocation | note: inside `std::ptr::const_ptr::::add` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL diff --git a/tests/ui/const-ptr/out_of_bounds_read.rs b/tests/ui/const-ptr/out_of_bounds_read.rs index 312b53432b477..ccf45bf324a0d 100644 --- a/tests/ui/const-ptr/out_of_bounds_read.rs +++ b/tests/ui/const-ptr/out_of_bounds_read.rs @@ -1,5 +1,3 @@ -//@ error-pattern: evaluation of constant value failed - fn main() { use std::ptr; @@ -8,6 +6,9 @@ fn main() { const PAST_END_PTR: *const u32 = unsafe { DATA.as_ptr().add(1) }; const _READ: u32 = unsafe { ptr::read(PAST_END_PTR) }; + //~^ ERROR evaluation of constant value failed const _CONST_READ: u32 = unsafe { PAST_END_PTR.read() }; + //~^ ERROR evaluation of constant value failed const _MUT_READ: u32 = unsafe { (PAST_END_PTR as *mut u32).read() }; + //~^ ERROR evaluation of constant value failed } diff --git a/tests/ui/const-ptr/out_of_bounds_read.stderr b/tests/ui/const-ptr/out_of_bounds_read.stderr index 899e151c9b814..1d625a26b78c6 100644 --- a/tests/ui/const-ptr/out_of_bounds_read.stderr +++ b/tests/ui/const-ptr/out_of_bounds_read.stderr @@ -1,17 +1,17 @@ error[E0080]: evaluation of constant value failed - --> $DIR/out_of_bounds_read.rs:10:33 + --> $DIR/out_of_bounds_read.rs:8:33 | LL | const _READ: u32 = unsafe { ptr::read(PAST_END_PTR) }; - | ^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes + | ^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes | note: inside `std::ptr::read::` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL error[E0080]: evaluation of constant value failed - --> $DIR/out_of_bounds_read.rs:11:39 + --> $DIR/out_of_bounds_read.rs:10:39 | LL | const _CONST_READ: u32 = unsafe { PAST_END_PTR.read() }; - | ^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes + | ^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes | note: inside `std::ptr::const_ptr::::read` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -22,7 +22,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/out_of_bounds_read.rs:12:37 | LL | const _MUT_READ: u32 = unsafe { (PAST_END_PTR as *mut u32).read() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes | note: inside `std::ptr::mut_ptr::::read` --> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL diff --git a/tests/ui/consts/assoc-const-elided-lifetime.stderr b/tests/ui/consts/assoc-const-elided-lifetime.stderr index 0c3e455eb2ded..9582152683578 100644 --- a/tests/ui/consts/assoc-const-elided-lifetime.stderr +++ b/tests/ui/consts/assoc-const-elided-lifetime.stderr @@ -35,8 +35,6 @@ note: cannot automatically infer `'static` because of other lifetimes in scope | LL | impl<'a> Foo<'a> { | ^^ -LL | const FOO: Foo<'_> = Foo { x: PhantomData::<&()> }; - | ^^ help: use the `'static` lifetime | LL | const BAR: &'static () = &(); diff --git a/tests/ui/consts/async-block.rs b/tests/ui/consts/async-block.rs index 40be4d195d4b4..96881bc91340a 100644 --- a/tests/ui/consts/async-block.rs +++ b/tests/ui/consts/async-block.rs @@ -2,18 +2,17 @@ //@ edition:2018 //@ revisions: with_feature without_feature +//@[with_feature] check-pass -#![feature(rustc_attrs)] #![cfg_attr(with_feature, feature(const_async_blocks))] use std::future::Future; // From const _: i32 = { core::mem::ManuallyDrop::new(async { 0 }); 4 }; -//[without_feature]~^ `async` block +//[without_feature]~^ ERROR `async` block static _FUT: &(dyn Future + Sync) = &async {}; -//[without_feature]~^ `async` block +//[without_feature]~^ ERROR `async` block -#[rustc_error] -fn main() {} //[with_feature]~ fatal error triggered by #[rustc_error] +fn main() {} diff --git a/tests/ui/consts/async-block.with_feature.stderr b/tests/ui/consts/async-block.with_feature.stderr deleted file mode 100644 index 8228fa29edf17..0000000000000 --- a/tests/ui/consts/async-block.with_feature.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fatal error triggered by #[rustc_error] - --> $DIR/async-block.rs:19:1 - | -LL | fn main() {} - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/consts/const-array-oob-arith.rs b/tests/ui/consts/const-array-oob-arith.rs index 0f6e76768cd15..8e5c56e0ea82e 100644 --- a/tests/ui/consts/const-array-oob-arith.rs +++ b/tests/ui/consts/const-array-oob-arith.rs @@ -4,10 +4,10 @@ const VAL: i32 = ARR[IDX]; const BONG: [i32; (ARR[0] - 41) as usize] = [5]; const BLUB: [i32; (ARR[0] - 40) as usize] = [5]; //~^ ERROR: mismatched types -//~| expected an array +//~| NOTE expected an array const BOO: [i32; (ARR[0] - 41) as usize] = [5, 99]; //~^ ERROR: mismatched types -//~| expected an array +//~| NOTE expected an array fn main() { let _ = VAL; diff --git a/tests/ui/consts/const-array-oob.rs b/tests/ui/consts/const-array-oob.rs index cf3db077e36ee..4b457d1c23c37 100644 --- a/tests/ui/consts/const-array-oob.rs +++ b/tests/ui/consts/const-array-oob.rs @@ -1,10 +1,11 @@ const FOO: [usize; 3] = [1, 2, 3]; const BAR: usize = FOO[5]; //~^ ERROR: evaluation of constant value failed +//~| NOTE index out of bounds: the length is 3 but the index is 5 const BLUB: [u32; FOO[4]] = [5, 6]; //~^ ERROR evaluation of constant value failed [E0080] -//~| index out of bounds: the length is 3 but the index is 4 +//~| NOTE index out of bounds: the length is 3 but the index is 4 fn main() { let _ = BAR; diff --git a/tests/ui/consts/const-array-oob.stderr b/tests/ui/consts/const-array-oob.stderr index be31f183b9aa4..89427c051e760 100644 --- a/tests/ui/consts/const-array-oob.stderr +++ b/tests/ui/consts/const-array-oob.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation of constant value failed - --> $DIR/const-array-oob.rs:5:19 + --> $DIR/const-array-oob.rs:6:19 | LL | const BLUB: [u32; FOO[4]] = [5, 6]; | ^^^^^^ index out of bounds: the length is 3 but the index is 4 diff --git a/tests/ui/consts/const-block-const-bound.stderr b/tests/ui/consts/const-block-const-bound.stderr index 14c62fb4d2573..624772f5aedc6 100644 --- a/tests/ui/consts/const-block-const-bound.stderr +++ b/tests/ui/consts/const-block-const-bound.stderr @@ -10,7 +10,7 @@ note: required by a bound in `f` --> $DIR/const-block-const-bound.rs:6:15 | LL | const fn f(x: T) {} - | ^^^^^^ required by this bound in `f` + | ^^^^^^^^^^^^^^^ required by this bound in `f` error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-blocks/const-block-in-array-size.rs b/tests/ui/consts/const-blocks/const-block-in-array-size.rs new file mode 100644 index 0000000000000..ecab24322869c --- /dev/null +++ b/tests/ui/consts/const-blocks/const-block-in-array-size.rs @@ -0,0 +1,5 @@ +//@ check-pass + +type A = [u32; const { 2 }]; + +fn main() {} diff --git a/tests/ui/consts/const-blocks/trait-error.stderr b/tests/ui/consts/const-blocks/trait-error.stderr index 068720a53f65f..58ddc047d03a5 100644 --- a/tests/ui/consts/const-blocks/trait-error.stderr +++ b/tests/ui/consts/const-blocks/trait-error.stderr @@ -13,7 +13,6 @@ note: required for `Foo` to implement `Copy` LL | #[derive(Copy, Clone)] | ^^^^ unsatisfied trait bound introduced in this `derive` macro = note: the `Copy` trait is required because this value will be copied for each element of the array - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-cast-wrong-type.rs b/tests/ui/consts/const-cast-wrong-type.rs index 6e055a2bcd340..9936a660936b0 100644 --- a/tests/ui/consts/const-cast-wrong-type.rs +++ b/tests/ui/consts/const-cast-wrong-type.rs @@ -1,5 +1,5 @@ const a: [u8; 3] = ['h' as u8, 'i' as u8, 0 as u8]; -const b: *const i8 = &a as *const i8; //~ ERROR mismatched types +const b: *const i8 = &a as *const i8; //~ ERROR casting `&[u8; 3]` as `*const i8` is invalid fn main() { } diff --git a/tests/ui/consts/const-cast-wrong-type.stderr b/tests/ui/consts/const-cast-wrong-type.stderr index 44361f15d8a98..0730bac22354d 100644 --- a/tests/ui/consts/const-cast-wrong-type.stderr +++ b/tests/ui/consts/const-cast-wrong-type.stderr @@ -1,9 +1,9 @@ -error[E0308]: mismatched types +error[E0606]: casting `&[u8; 3]` as `*const i8` is invalid --> $DIR/const-cast-wrong-type.rs:2:22 | LL | const b: *const i8 = &a as *const i8; - | ^^^^^^^^^^^^^^^ expected `u8`, found `i8` + | ^^^^^^^^^^^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0606`. diff --git a/tests/ui/consts/const-compare-bytes-ub.stderr b/tests/ui/consts/const-compare-bytes-ub.stderr index 9ef5c8ad43a1c..0e77310c6bab9 100644 --- a/tests/ui/consts/const-compare-bytes-ub.stderr +++ b/tests/ui/consts/const-compare-bytes-ub.stderr @@ -2,31 +2,31 @@ error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:9:9 | LL | compare_bytes(0 as *const u8, 2 as *const u8, 1) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 1 byte of memory, but got a null pointer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 1 byte, but got null pointer error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:13:9 | LL | compare_bytes(1 as *const u8, 0 as *const u8, 1) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 1 byte of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 1 byte, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:17:9 | LL | compare_bytes(1 as *const u8, 2 as *const u8, 1) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 1 byte of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 1 byte, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:21:9 | LL | compare_bytes([1, 2, 3].as_ptr(), [1, 2, 3, 4].as_ptr(), 4) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC0 which is only 3 bytes from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0 which is only 3 bytes from the end of the allocation error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:25:9 | LL | compare_bytes([1, 2, 3, 4].as_ptr(), [1, 2, 3].as_ptr(), 4) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC1 which is only 3 bytes from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC1 which is only 3 bytes from the end of the allocation error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:29:9 diff --git a/tests/ui/consts/const-deref-ptr.rs b/tests/ui/consts/const-deref-ptr.rs index 2607d4de22919..c80cb95ea936c 100644 --- a/tests/ui/consts/const-deref-ptr.rs +++ b/tests/ui/consts/const-deref-ptr.rs @@ -3,6 +3,6 @@ fn main() { static C: u64 = unsafe {*(0xdeadbeef as *const u64)}; //~^ ERROR could not evaluate static initializer - //~| dangling pointer + //~| NOTE dangling pointer println!("{}", C); } diff --git a/tests/ui/consts/const-deref-ptr.stderr b/tests/ui/consts/const-deref-ptr.stderr index 070685e0b9d93..37502864947a4 100644 --- a/tests/ui/consts/const-deref-ptr.stderr +++ b/tests/ui/consts/const-deref-ptr.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/const-deref-ptr.rs:4:29 | LL | static C: u64 = unsafe {*(0xdeadbeef as *const u64)}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 8 bytes of memory, but got 0xdeadbeef[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 8 bytes, but got 0xdeadbeef[noalloc] which is a dangling pointer (it has no provenance) error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-err-enum-discriminant.rs b/tests/ui/consts/const-err-enum-discriminant.rs index ebb3e551ba814..42165ff534653 100644 --- a/tests/ui/consts/const-err-enum-discriminant.rs +++ b/tests/ui/consts/const-err-enum-discriminant.rs @@ -7,7 +7,7 @@ union Foo { enum Bar { Boo = [unsafe { Foo { b: () }.a }; 4][3], //~^ ERROR evaluation of constant value failed - //~| uninitialized + //~| NOTE uninitialized } fn main() { diff --git a/tests/ui/consts/const-err-late.rs b/tests/ui/consts/const-err-late.rs index 6dff2c101a089..f8bea388109d3 100644 --- a/tests/ui/consts/const-err-late.rs +++ b/tests/ui/consts/const-err-late.rs @@ -1,5 +1,6 @@ //@ build-fail //@ compile-flags: -C overflow-checks=on +//@ dont-require-annotations: NOTE #![allow(arithmetic_overflow, unconditional_panic)] @@ -16,5 +17,5 @@ impl S { } fn main() { - black_box((S::::FOO, S::::FOO)); //~ constant + black_box((S::::FOO, S::::FOO)); //~ NOTE constant } diff --git a/tests/ui/consts/const-err-late.stderr b/tests/ui/consts/const-err-late.stderr index 53badeafa35b6..0c021e8761ea1 100644 --- a/tests/ui/consts/const-err-late.stderr +++ b/tests/ui/consts/const-err-late.stderr @@ -1,29 +1,29 @@ error[E0080]: evaluation of `S::::FOO` failed - --> $DIR/const-err-late.rs:13:21 + --> $DIR/const-err-late.rs:14:21 | LL | const FOO: u8 = [5u8][1]; | ^^^^^^^^ index out of bounds: the length is 1 but the index is 1 note: erroneous constant encountered - --> $DIR/const-err-late.rs:19:16 + --> $DIR/const-err-late.rs:20:16 | LL | black_box((S::::FOO, S::::FOO)); | ^^^^^^^^^^^^^ error[E0080]: evaluation of `S::::FOO` failed - --> $DIR/const-err-late.rs:13:21 + --> $DIR/const-err-late.rs:14:21 | LL | const FOO: u8 = [5u8][1]; | ^^^^^^^^ index out of bounds: the length is 1 but the index is 1 note: erroneous constant encountered - --> $DIR/const-err-late.rs:19:31 + --> $DIR/const-err-late.rs:20:31 | LL | black_box((S::::FOO, S::::FOO)); | ^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/const-err-late.rs:19:16 + --> $DIR/const-err-late.rs:20:16 | LL | black_box((S::::FOO, S::::FOO)); | ^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | black_box((S::::FOO, S::::FOO)); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: erroneous constant encountered - --> $DIR/const-err-late.rs:19:31 + --> $DIR/const-err-late.rs:20:31 | LL | black_box((S::::FOO, S::::FOO)); | ^^^^^^^^^^^^^ diff --git a/tests/ui/consts/const-err-multi.rs b/tests/ui/consts/const-err-multi.rs index b265bc4c4d84f..f21cc97345c79 100644 --- a/tests/ui/consts/const-err-multi.rs +++ b/tests/ui/consts/const-err-multi.rs @@ -1,11 +1,12 @@ pub const A: i8 = -i8::MIN; //~^ ERROR constant +//~| NOTE attempt to negate `i8::MIN`, which would overflow pub const B: i8 = A; -//~^ constant +//~^ NOTE constant pub const C: u8 = A as u8; -//~^ constant +//~^ NOTE constant pub const D: i8 = 50 - A; -//~^ constant +//~^ NOTE constant fn main() { let _ = (A, B, C, D); diff --git a/tests/ui/consts/const-err-multi.stderr b/tests/ui/consts/const-err-multi.stderr index 2fe0b9bb463a5..c60be59b87d33 100644 --- a/tests/ui/consts/const-err-multi.stderr +++ b/tests/ui/consts/const-err-multi.stderr @@ -5,19 +5,19 @@ LL | pub const A: i8 = -i8::MIN; | ^^^^^^^^ attempt to negate `i8::MIN`, which would overflow note: erroneous constant encountered - --> $DIR/const-err-multi.rs:3:19 + --> $DIR/const-err-multi.rs:4:19 | LL | pub const B: i8 = A; | ^ note: erroneous constant encountered - --> $DIR/const-err-multi.rs:5:19 + --> $DIR/const-err-multi.rs:6:19 | LL | pub const C: u8 = A as u8; | ^ note: erroneous constant encountered - --> $DIR/const-err-multi.rs:7:24 + --> $DIR/const-err-multi.rs:8:24 | LL | pub const D: i8 = 50 - A; | ^ diff --git a/tests/ui/consts/const-eval/assign-to-static-within-other-static.rs b/tests/ui/consts/const-eval/assign-to-static-within-other-static.rs index ecf97223f6ada..30e40bd8be13e 100644 --- a/tests/ui/consts/const-eval/assign-to-static-within-other-static.rs +++ b/tests/ui/consts/const-eval/assign-to-static-within-other-static.rs @@ -6,7 +6,7 @@ use std::cell::UnsafeCell; static mut FOO: u32 = 42; static BOO: () = unsafe { FOO = 5; - //~^ could not evaluate static initializer [E0080] + //~^ ERROR could not evaluate static initializer [E0080] }; fn main() {} diff --git a/tests/ui/consts/const-eval/const-eval-overflow-4b.rs b/tests/ui/consts/const-eval/const-eval-overflow-4b.rs index ce9c980de0dd2..3ad23a515a710 100644 --- a/tests/ui/consts/const-eval/const-eval-overflow-4b.rs +++ b/tests/ui/consts/const-eval/const-eval-overflow-4b.rs @@ -3,12 +3,14 @@ // // This test is checking the count in an array type. +//@ dont-require-annotations: NOTE + #![allow(unused_imports)] const A_I8_T : [u32; (i8::MAX as i8 + 1u8) as usize] //~^ ERROR mismatched types - //~| expected `i8`, found `u8` + //~| NOTE expected `i8`, found `u8` //~| ERROR cannot add `u8` to `i8` = [0; (i8::MAX as usize) + 1]; diff --git a/tests/ui/consts/const-eval/const-eval-overflow-4b.stderr b/tests/ui/consts/const-eval/const-eval-overflow-4b.stderr index 399f21a989429..1a0832b8ba02b 100644 --- a/tests/ui/consts/const-eval/const-eval-overflow-4b.stderr +++ b/tests/ui/consts/const-eval/const-eval-overflow-4b.stderr @@ -1,11 +1,11 @@ error[E0308]: mismatched types - --> $DIR/const-eval-overflow-4b.rs:9:30 + --> $DIR/const-eval-overflow-4b.rs:11:30 | LL | : [u32; (i8::MAX as i8 + 1u8) as usize] | ^^^ expected `i8`, found `u8` error[E0277]: cannot add `u8` to `i8` - --> $DIR/const-eval-overflow-4b.rs:9:28 + --> $DIR/const-eval-overflow-4b.rs:11:28 | LL | : [u32; (i8::MAX as i8 + 1u8) as usize] | ^ no implementation for `i8 + u8` @@ -18,13 +18,13 @@ LL | : [u32; (i8::MAX as i8 + 1u8) as usize] `i8` implements `Add` error[E0604]: only `u8` can be cast as `char`, not `i8` - --> $DIR/const-eval-overflow-4b.rs:22:13 + --> $DIR/const-eval-overflow-4b.rs:24:13 | LL | : [u32; 5i8 as char as usize] | ^^^^^^^^^^^ invalid cast | help: try casting from `u8` instead - --> $DIR/const-eval-overflow-4b.rs:22:13 + --> $DIR/const-eval-overflow-4b.rs:24:13 | LL | : [u32; 5i8 as char as usize] | ^^^^^^^^^^^ diff --git a/tests/ui/consts/const-eval/const-eval-span.rs b/tests/ui/consts/const-eval/const-eval-span.rs index 1667c77d124c5..f1904c76b6c15 100644 --- a/tests/ui/consts/const-eval/const-eval-span.rs +++ b/tests/ui/consts/const-eval/const-eval-span.rs @@ -8,7 +8,7 @@ const CONSTANT: S = S(0); enum E { V = CONSTANT, //~^ ERROR mismatched types - //~| expected `isize`, found `S` + //~| NOTE expected `isize`, found `S` } fn main() {} diff --git a/tests/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr b/tests/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr index f099bc7ef7c35..3eccd596274a1 100644 --- a/tests/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr +++ b/tests/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:26:49 + --> $DIR/const-pointer-values-in-various-types.rs:27:49 | LL | const I32_REF_USIZE_UNION: usize = unsafe { Nonsense { int_32_ref: &3 }.u }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -8,7 +8,7 @@ LL | const I32_REF_USIZE_UNION: usize = unsafe { Nonsense { int_32_ref: &3 } = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:29:43 + --> $DIR/const-pointer-values-in-various-types.rs:30:43 | LL | const I32_REF_U8_UNION: u8 = unsafe { Nonsense { int_32_ref: &3 }.uint_8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -17,7 +17,7 @@ LL | const I32_REF_U8_UNION: u8 = unsafe { Nonsense { int_32_ref: &3 }.uint_ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:32:45 + --> $DIR/const-pointer-values-in-various-types.rs:33:45 | LL | const I32_REF_U16_UNION: u16 = unsafe { Nonsense { int_32_ref: &3 }.uint_16 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -26,7 +26,7 @@ LL | const I32_REF_U16_UNION: u16 = unsafe { Nonsense { int_32_ref: &3 }.uin = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:35:45 + --> $DIR/const-pointer-values-in-various-types.rs:36:45 | LL | const I32_REF_U32_UNION: u32 = unsafe { Nonsense { int_32_ref: &3 }.uint_32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -35,7 +35,7 @@ LL | const I32_REF_U32_UNION: u32 = unsafe { Nonsense { int_32_ref: &3 }.uin = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:38:45 + --> $DIR/const-pointer-values-in-various-types.rs:39:45 | LL | const I32_REF_U64_UNION: u64 = unsafe { Nonsense { int_32_ref: &3 }.uint_64 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -44,13 +44,13 @@ LL | const I32_REF_U64_UNION: u64 = unsafe { Nonsense { int_32_ref: &3 }.uin = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:41:47 + --> $DIR/const-pointer-values-in-various-types.rs:42:47 | LL | const I32_REF_U128_UNION: u128 = unsafe { Nonsense { int_32_ref: &3 }.uint_128 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:45:43 + --> $DIR/const-pointer-values-in-various-types.rs:46:43 | LL | const I32_REF_I8_UNION: i8 = unsafe { Nonsense { int_32_ref: &3 }.int_8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -59,7 +59,7 @@ LL | const I32_REF_I8_UNION: i8 = unsafe { Nonsense { int_32_ref: &3 }.int_8 = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:48:45 + --> $DIR/const-pointer-values-in-various-types.rs:49:45 | LL | const I32_REF_I16_UNION: i16 = unsafe { Nonsense { int_32_ref: &3 }.int_16 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -68,7 +68,7 @@ LL | const I32_REF_I16_UNION: i16 = unsafe { Nonsense { int_32_ref: &3 }.int = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:51:45 + --> $DIR/const-pointer-values-in-various-types.rs:52:45 | LL | const I32_REF_I32_UNION: i32 = unsafe { Nonsense { int_32_ref: &3 }.int_32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -77,7 +77,7 @@ LL | const I32_REF_I32_UNION: i32 = unsafe { Nonsense { int_32_ref: &3 }.int = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:54:45 + --> $DIR/const-pointer-values-in-various-types.rs:55:45 | LL | const I32_REF_I64_UNION: i64 = unsafe { Nonsense { int_32_ref: &3 }.int_64 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -86,13 +86,13 @@ LL | const I32_REF_I64_UNION: i64 = unsafe { Nonsense { int_32_ref: &3 }.int = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:57:47 + --> $DIR/const-pointer-values-in-various-types.rs:58:47 | LL | const I32_REF_I128_UNION: i128 = unsafe { Nonsense { int_32_ref: &3 }.int_128 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:61:45 + --> $DIR/const-pointer-values-in-various-types.rs:62:45 | LL | const I32_REF_F32_UNION: f32 = unsafe { Nonsense { int_32_ref: &3 }.float_32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -101,7 +101,7 @@ LL | const I32_REF_F32_UNION: f32 = unsafe { Nonsense { int_32_ref: &3 }.flo = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:64:45 + --> $DIR/const-pointer-values-in-various-types.rs:65:45 | LL | const I32_REF_F64_UNION: f64 = unsafe { Nonsense { int_32_ref: &3 }.float_64 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -110,7 +110,7 @@ LL | const I32_REF_F64_UNION: f64 = unsafe { Nonsense { int_32_ref: &3 }.flo = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:67:47 + --> $DIR/const-pointer-values-in-various-types.rs:68:47 | LL | const I32_REF_BOOL_UNION: bool = unsafe { Nonsense { int_32_ref: &3 }.truthy_falsey }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -119,7 +119,7 @@ LL | const I32_REF_BOOL_UNION: bool = unsafe { Nonsense { int_32_ref: &3 }.t = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:70:47 + --> $DIR/const-pointer-values-in-various-types.rs:71:47 | LL | const I32_REF_CHAR_UNION: char = unsafe { Nonsense { int_32_ref: &3 }.character }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -128,7 +128,7 @@ LL | const I32_REF_CHAR_UNION: char = unsafe { Nonsense { int_32_ref: &3 }.c = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:73:39 + --> $DIR/const-pointer-values-in-various-types.rs:74:39 | LL | const STR_U8_UNION: u8 = unsafe { Nonsense { stringy: "3" }.uint_8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -137,7 +137,7 @@ LL | const STR_U8_UNION: u8 = unsafe { Nonsense { stringy: "3" }.uint_8 }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:76:41 + --> $DIR/const-pointer-values-in-various-types.rs:77:41 | LL | const STR_U16_UNION: u16 = unsafe { Nonsense { stringy: "3" }.uint_16 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -146,7 +146,7 @@ LL | const STR_U16_UNION: u16 = unsafe { Nonsense { stringy: "3" }.uint_16 } = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:79:41 + --> $DIR/const-pointer-values-in-various-types.rs:80:41 | LL | const STR_U32_UNION: u32 = unsafe { Nonsense { stringy: "3" }.uint_32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -155,7 +155,7 @@ LL | const STR_U32_UNION: u32 = unsafe { Nonsense { stringy: "3" }.uint_32 } = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:82:41 + --> $DIR/const-pointer-values-in-various-types.rs:83:41 | LL | const STR_U64_UNION: u64 = unsafe { Nonsense { stringy: "3" }.uint_64 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -164,7 +164,7 @@ LL | const STR_U64_UNION: u64 = unsafe { Nonsense { stringy: "3" }.uint_64 } = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:85:43 + --> $DIR/const-pointer-values-in-various-types.rs:86:43 | LL | const STR_U128_UNION: u128 = unsafe { Nonsense { stringy: "3" }.uint_128 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -173,7 +173,7 @@ LL | const STR_U128_UNION: u128 = unsafe { Nonsense { stringy: "3" }.uint_12 = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:88:39 + --> $DIR/const-pointer-values-in-various-types.rs:89:39 | LL | const STR_I8_UNION: i8 = unsafe { Nonsense { stringy: "3" }.int_8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -182,7 +182,7 @@ LL | const STR_I8_UNION: i8 = unsafe { Nonsense { stringy: "3" }.int_8 }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:91:41 + --> $DIR/const-pointer-values-in-various-types.rs:92:41 | LL | const STR_I16_UNION: i16 = unsafe { Nonsense { stringy: "3" }.int_16 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -191,7 +191,7 @@ LL | const STR_I16_UNION: i16 = unsafe { Nonsense { stringy: "3" }.int_16 }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:94:41 + --> $DIR/const-pointer-values-in-various-types.rs:95:41 | LL | const STR_I32_UNION: i32 = unsafe { Nonsense { stringy: "3" }.int_32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -200,7 +200,7 @@ LL | const STR_I32_UNION: i32 = unsafe { Nonsense { stringy: "3" }.int_32 }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:97:41 + --> $DIR/const-pointer-values-in-various-types.rs:98:41 | LL | const STR_I64_UNION: i64 = unsafe { Nonsense { stringy: "3" }.int_64 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -209,7 +209,7 @@ LL | const STR_I64_UNION: i64 = unsafe { Nonsense { stringy: "3" }.int_64 }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:100:43 + --> $DIR/const-pointer-values-in-various-types.rs:101:43 | LL | const STR_I128_UNION: i128 = unsafe { Nonsense { stringy: "3" }.int_128 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -218,7 +218,7 @@ LL | const STR_I128_UNION: i128 = unsafe { Nonsense { stringy: "3" }.int_128 = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:103:41 + --> $DIR/const-pointer-values-in-various-types.rs:104:41 | LL | const STR_F32_UNION: f32 = unsafe { Nonsense { stringy: "3" }.float_32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -227,7 +227,7 @@ LL | const STR_F32_UNION: f32 = unsafe { Nonsense { stringy: "3" }.float_32 = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:106:41 + --> $DIR/const-pointer-values-in-various-types.rs:107:41 | LL | const STR_F64_UNION: f64 = unsafe { Nonsense { stringy: "3" }.float_64 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -236,7 +236,7 @@ LL | const STR_F64_UNION: f64 = unsafe { Nonsense { stringy: "3" }.float_64 = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:109:43 + --> $DIR/const-pointer-values-in-various-types.rs:110:43 | LL | const STR_BOOL_UNION: bool = unsafe { Nonsense { stringy: "3" }.truthy_falsey }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -245,7 +245,7 @@ LL | const STR_BOOL_UNION: bool = unsafe { Nonsense { stringy: "3" }.truthy_ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/const-pointer-values-in-various-types.rs:112:43 + --> $DIR/const-pointer-values-in-various-types.rs:113:43 | LL | const STR_CHAR_UNION: char = unsafe { Nonsense { stringy: "3" }.character }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer diff --git a/tests/ui/consts/const-eval/const-pointer-values-in-various-types.rs b/tests/ui/consts/const-eval/const-pointer-values-in-various-types.rs index d6b6d42780743..ce7380cd1553f 100644 --- a/tests/ui/consts/const-eval/const-pointer-values-in-various-types.rs +++ b/tests/ui/consts/const-eval/const-pointer-values-in-various-types.rs @@ -1,5 +1,6 @@ //@ only-x86_64 //@ stderr-per-bitwidth +//@ dont-require-annotations: NOTE #[repr(C)] union Nonsense { @@ -40,7 +41,7 @@ fn main() { const I32_REF_U128_UNION: u128 = unsafe { Nonsense { int_32_ref: &3 }.uint_128 }; //~^ ERROR evaluation of constant value failed - //~| uninitialized + //~| NOTE uninitialized const I32_REF_I8_UNION: i8 = unsafe { Nonsense { int_32_ref: &3 }.int_8 }; //~^ ERROR evaluation of constant value failed @@ -56,7 +57,7 @@ fn main() { const I32_REF_I128_UNION: i128 = unsafe { Nonsense { int_32_ref: &3 }.int_128 }; //~^ ERROR evaluation of constant value failed - //~| uninitialized + //~| NOTE uninitialized const I32_REF_F32_UNION: f32 = unsafe { Nonsense { int_32_ref: &3 }.float_32 }; //~^ ERROR evaluation of constant value failed diff --git a/tests/ui/consts/const-eval/const_fn_ptr.rs b/tests/ui/consts/const-eval/const_fn_ptr.rs index f8a2658f31e65..1d65eedb93d06 100644 --- a/tests/ui/consts/const-eval/const_fn_ptr.rs +++ b/tests/ui/consts/const-eval/const_fn_ptr.rs @@ -34,3 +34,5 @@ fn main() { let z = foo(double, 2); assert_eq!(z, 4); } + +//~? WARN skipping const checks diff --git a/tests/ui/consts/const-eval/const_fn_ptr_fail.rs b/tests/ui/consts/const-eval/const_fn_ptr_fail.rs index a0f804722dbb3..00bf0ed0eba06 100644 --- a/tests/ui/consts/const-eval/const_fn_ptr_fail.rs +++ b/tests/ui/consts/const-eval/const_fn_ptr_fail.rs @@ -10,3 +10,5 @@ const fn bar(x: usize) -> usize { } fn main() {} + +//~? WARN skipping const checks diff --git a/tests/ui/consts/const-eval/const_fn_ptr_fail2.rs b/tests/ui/consts/const-eval/const_fn_ptr_fail2.rs index 7db8c6e81ab51..c6ae3af44270b 100644 --- a/tests/ui/consts/const-eval/const_fn_ptr_fail2.rs +++ b/tests/ui/consts/const-eval/const_fn_ptr_fail2.rs @@ -24,3 +24,5 @@ fn main() { assert_eq!(Y, 4); assert_eq!(Z, 4); } + +//~? WARN skipping const checks diff --git a/tests/ui/consts/const-eval/const_panic-normalize-tabs-115498.stderr b/tests/ui/consts/const-eval/const_panic-normalize-tabs-115498.stderr index 64e227c4f45cd..fae971c09f23f 100644 --- a/tests/ui/consts/const-eval/const_panic-normalize-tabs-115498.stderr +++ b/tests/ui/consts/const-eval/const_panic-normalize-tabs-115498.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of constant value failed | LL | struct Bug([u8; panic!{"\t"}]); | ^^^^^^^^^^^^ evaluation panicked: - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/const_panic.stderr b/tests/ui/consts/const-eval/const_panic.stderr index 0816b04faca05..0874224e5f277 100644 --- a/tests/ui/consts/const-eval/const_panic.stderr +++ b/tests/ui/consts/const-eval/const_panic.stderr @@ -3,24 +3,18 @@ error[E0080]: evaluation of constant value failed | LL | const Z: () = std::panic!("cheese"); | ^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: cheese - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `std::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic.rs:9:16 | LL | const Z2: () = std::panic!(); | ^^^^^^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `std::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic.rs:12:15 | LL | const Y: () = std::unreachable!(); | ^^^^^^^^^^^^^^^^^^^ evaluation panicked: internal error: entered unreachable code - | - = note: this error originates in the macro `$crate::panic::unreachable_2015` which comes from the expansion of the macro `std::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic.rs:15:15 @@ -35,40 +29,30 @@ error[E0080]: evaluation of constant value failed | LL | const W: () = std::panic!(MSG); | ^^^^^^^^^^^^^^^^ evaluation panicked: hello - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `std::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic.rs:21:16 | LL | const W2: () = std::panic!("{}", MSG); | ^^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: hello - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `std::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic.rs:24:20 | LL | const Z_CORE: () = core::panic!("cheese"); | ^^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: cheese - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic.rs:27:21 | LL | const Z2_CORE: () = core::panic!(); | ^^^^^^^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic.rs:30:20 | LL | const Y_CORE: () = core::unreachable!(); | ^^^^^^^^^^^^^^^^^^^^ evaluation panicked: internal error: entered unreachable code - | - = note: this error originates in the macro `$crate::panic::unreachable_2015` which comes from the expansion of the macro `core::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic.rs:33:20 @@ -83,16 +67,12 @@ error[E0080]: evaluation of constant value failed | LL | const W_CORE: () = core::panic!(MSG); | ^^^^^^^^^^^^^^^^^ evaluation panicked: hello - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic.rs:39:21 | LL | const W2_CORE: () = core::panic!("{}", MSG); | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: hello - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 12 previous errors diff --git a/tests/ui/consts/const-eval/const_panic_2021.stderr b/tests/ui/consts/const-eval/const_panic_2021.stderr index 4faa2a1e4cfdb..1496df4445a6d 100644 --- a/tests/ui/consts/const-eval/const_panic_2021.stderr +++ b/tests/ui/consts/const-eval/const_panic_2021.stderr @@ -3,24 +3,18 @@ error[E0080]: evaluation of constant value failed | LL | const A: () = std::panic!("blåhaj"); | ^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: blåhaj - | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `std::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic_2021.rs:9:15 | LL | const B: () = std::panic!(); | ^^^^^^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `std::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic_2021.rs:12:15 | LL | const C: () = std::unreachable!(); | ^^^^^^^^^^^^^^^^^^^ evaluation panicked: internal error: entered unreachable code - | - = note: this error originates in the macro `$crate::panic::unreachable_2021` which comes from the expansion of the macro `std::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic_2021.rs:15:15 @@ -35,32 +29,24 @@ error[E0080]: evaluation of constant value failed | LL | const E: () = std::panic!("{}", MSG); | ^^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: hello - | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `std::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic_2021.rs:21:20 | LL | const A_CORE: () = core::panic!("shark"); | ^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: shark - | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic_2021.rs:24:20 | LL | const B_CORE: () = core::panic!(); | ^^^^^^^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic_2021.rs:27:20 | LL | const C_CORE: () = core::unreachable!(); | ^^^^^^^^^^^^^^^^^^^^ evaluation panicked: internal error: entered unreachable code - | - = note: this error originates in the macro `$crate::panic::unreachable_2021` which comes from the expansion of the macro `core::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic_2021.rs:30:20 @@ -75,8 +61,6 @@ error[E0080]: evaluation of constant value failed | LL | const E_CORE: () = core::panic!("{}", MSG); | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: hello - | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 10 previous errors diff --git a/tests/ui/consts/const-eval/const_panic_libcore_bin.stderr b/tests/ui/consts/const-eval/const_panic_libcore_bin.stderr index 11e70c4849901..2acab71129097 100644 --- a/tests/ui/consts/const-eval/const_panic_libcore_bin.stderr +++ b/tests/ui/consts/const-eval/const_panic_libcore_bin.stderr @@ -3,16 +3,12 @@ error[E0080]: evaluation of constant value failed | LL | const Z: () = panic!("cheese"); | ^^^^^^^^^^^^^^^^ evaluation panicked: cheese - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic_libcore_bin.rs:11:15 | LL | const Y: () = unreachable!(); | ^^^^^^^^^^^^^^ evaluation panicked: internal error: entered unreachable code - | - = note: this error originates in the macro `$crate::panic::unreachable_2015` which comes from the expansion of the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/const_panic_libcore_bin.rs:14:15 diff --git a/tests/ui/consts/const-eval/const_raw_ptr_ops2.rs b/tests/ui/consts/const-eval/const_raw_ptr_ops2.rs index ec5508a1e90c9..0e88aa80c7977 100644 --- a/tests/ui/consts/const-eval/const_raw_ptr_ops2.rs +++ b/tests/ui/consts/const-eval/const_raw_ptr_ops2.rs @@ -5,6 +5,6 @@ const Z: i32 = unsafe { *(&1 as *const i32) }; // bad, will thus error in miri const Z2: i32 = unsafe { *(42 as *const i32) }; //~ ERROR evaluation of constant value failed -//~| is a dangling pointer +//~| NOTE dangling pointer const Z3: i32 = unsafe { *(44 as *const i32) }; //~ ERROR evaluation of constant value failed -//~| is a dangling pointer +//~| NOTE dangling pointer diff --git a/tests/ui/consts/const-eval/const_raw_ptr_ops2.stderr b/tests/ui/consts/const-eval/const_raw_ptr_ops2.stderr index b0c864652e55e..a8a5560ccb9c2 100644 --- a/tests/ui/consts/const-eval/const_raw_ptr_ops2.stderr +++ b/tests/ui/consts/const-eval/const_raw_ptr_ops2.stderr @@ -2,13 +2,13 @@ error[E0080]: evaluation of constant value failed --> $DIR/const_raw_ptr_ops2.rs:7:26 | LL | const Z2: i32 = unsafe { *(42 as *const i32) }; - | ^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got 0x2a[noalloc] which is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/const_raw_ptr_ops2.rs:9:26 | LL | const Z3: i32 = unsafe { *(44 as *const i32) }; - | ^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got 0x2c[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got 0x2c[noalloc] which is a dangling pointer (it has no provenance) error: aborting due to 2 previous errors diff --git a/tests/ui/consts/const-eval/format.rs b/tests/ui/consts/const-eval/format.rs index 1878fc0382767..a8085a786e189 100644 --- a/tests/ui/consts/const-eval/format.rs +++ b/tests/ui/consts/const-eval/format.rs @@ -9,4 +9,9 @@ const fn print() { //~| ERROR cannot call non-const function `_print` in constant functions } +const fn format_args() { + format_args!("{}", 0); + //~^ ERROR cannot call non-const formatting macro in constant functions +} + fn main() {} diff --git a/tests/ui/consts/const-eval/format.stderr b/tests/ui/consts/const-eval/format.stderr index af90acc2a2605..2f202705b7f96 100644 --- a/tests/ui/consts/const-eval/format.stderr +++ b/tests/ui/consts/const-eval/format.stderr @@ -1,17 +1,16 @@ error[E0015]: cannot call non-const formatting macro in constant functions - --> $DIR/format.rs:2:13 + --> $DIR/format.rs:2:5 | LL | panic!("{:?}", 0); - | ^^^^ + | ^^^^^^^^^^^^^^^^^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const formatting macro in constant functions - --> $DIR/format.rs:7:15 + --> $DIR/format.rs:7:5 | LL | println!("{:?}", 0); - | ^^^^ + | ^^^^^^^^^^^^^^^^^^^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -25,6 +24,14 @@ LL | println!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 3 previous errors +error[E0015]: cannot call non-const formatting macro in constant functions + --> $DIR/format.rs:13:5 + | +LL | format_args!("{}", 0); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_errors.rs b/tests/ui/consts/const-eval/heap/alloc_intrinsic_errors.rs index c1a544031c2a5..b47e2b3c1ed01 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_errors.rs +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_errors.rs @@ -1,11 +1,13 @@ +//@ dont-require-annotations: NOTE + #![feature(core_intrinsics)] #![feature(const_heap)] use std::intrinsics; -const FOO: i32 = foo(); //~ error: evaluation of constant value failed +const FOO: i32 = foo(); //~ ERROR evaluation of constant value failed const fn foo() -> i32 { unsafe { - let _ = intrinsics::const_allocate(4, 3) as *mut i32; //~ inside `foo` + let _ = intrinsics::const_allocate(4, 3) as *mut i32; //~ NOTE inside `foo` } 1 } diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_errors.stderr b/tests/ui/consts/const-eval/heap/alloc_intrinsic_errors.stderr index e1cb7a839961c..9f7546df3a2b9 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_errors.stderr +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_errors.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/alloc_intrinsic_errors.rs:5:18 + --> $DIR/alloc_intrinsic_errors.rs:7:18 | LL | const FOO: i32 = foo(); | ^^^^^ invalid align passed to `const_allocate`: 3 is not a power of 2 | note: inside `foo` - --> $DIR/alloc_intrinsic_errors.rs:8:17 + --> $DIR/alloc_intrinsic_errors.rs:10:17 | LL | let _ = intrinsics::const_allocate(4, 3) as *mut i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the failure occurred here diff --git a/tests/ui/consts/const-eval/ice-unhandled-type-122191.rs b/tests/ui/consts/const-eval/ice-unhandled-type-122191.rs index a92b99976e262..75bd6f7a123c2 100644 --- a/tests/ui/consts/const-eval/ice-unhandled-type-122191.rs +++ b/tests/ui/consts/const-eval/ice-unhandled-type-122191.rs @@ -3,14 +3,20 @@ type Foo = impl Send; struct A; -const VALUE: Foo = value(); -//~^ ERROR cannot find function `value` in this scope +#[define_opaque(Foo)] +//~^ ERROR unstable library feature +const fn foo() -> Foo { + value() + //~^ ERROR cannot find function `value` in this scope +} + +const VALUE: Foo = foo(); fn test() { match VALUE { 0 | 0 => {} -//~^ ERROR mismatched types -//~| ERROR mismatched types + //~^ ERROR mismatched types + //~| ERROR mismatched types _ => (), } } diff --git a/tests/ui/consts/const-eval/ice-unhandled-type-122191.stderr b/tests/ui/consts/const-eval/ice-unhandled-type-122191.stderr index daf0ccaa776ca..bcb6a80a8f2b2 100644 --- a/tests/ui/consts/const-eval/ice-unhandled-type-122191.stderr +++ b/tests/ui/consts/const-eval/ice-unhandled-type-122191.stderr @@ -1,3 +1,13 @@ +error[E0658]: use of unstable library feature `type_alias_impl_trait`: `type_alias_impl_trait` has open design concerns + --> $DIR/ice-unhandled-type-122191.rs:6:3 + | +LL | #[define_opaque(Foo)] + | ^^^^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: `impl Trait` in type aliases is unstable --> $DIR/ice-unhandled-type-122191.rs:1:12 | @@ -9,13 +19,16 @@ LL | type Foo = impl Send; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0425]: cannot find function `value` in this scope - --> $DIR/ice-unhandled-type-122191.rs:6:20 + --> $DIR/ice-unhandled-type-122191.rs:9:5 | -LL | const VALUE: Foo = value(); - | ^^^^^ not found in this scope +LL | value() + | ^^^^^ help: a constant with a similar name exists: `VALUE` +... +LL | const VALUE: Foo = foo(); + | ------------------------- similarly named constant `VALUE` defined here error[E0308]: mismatched types - --> $DIR/ice-unhandled-type-122191.rs:11:9 + --> $DIR/ice-unhandled-type-122191.rs:17:9 | LL | type Foo = impl Send; | --------- the expected opaque type @@ -27,14 +40,14 @@ LL | 0 | 0 => {} | = note: expected opaque type `Foo` found type `{integer}` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/ice-unhandled-type-122191.rs:9:4 +note: this item must have a `#[define_opaque(Foo)]` attribute to be able to define hidden types + --> $DIR/ice-unhandled-type-122191.rs:15:4 | LL | fn test() { | ^^^^ error[E0308]: mismatched types - --> $DIR/ice-unhandled-type-122191.rs:11:13 + --> $DIR/ice-unhandled-type-122191.rs:17:13 | LL | type Foo = impl Send; | --------- the expected opaque type @@ -46,13 +59,13 @@ LL | 0 | 0 => {} | = note: expected opaque type `Foo` found type `{integer}` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/ice-unhandled-type-122191.rs:9:4 +note: this item must have a `#[define_opaque(Foo)]` attribute to be able to define hidden types + --> $DIR/ice-unhandled-type-122191.rs:15:4 | LL | fn test() { | ^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0308, E0425, E0658. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/consts/const-eval/issue-44578.rs b/tests/ui/consts/const-eval/issue-44578.rs index 945bf93f8faad..565e1d3825b4d 100644 --- a/tests/ui/consts/const-eval/issue-44578.rs +++ b/tests/ui/consts/const-eval/issue-44578.rs @@ -1,4 +1,5 @@ //@ build-fail +//@ dont-require-annotations: NOTE trait Foo { const AMT: usize; @@ -23,5 +24,5 @@ impl Foo for u16 { fn main() { println!("{}", as Foo>::AMT); - //~^ constant + //~^ NOTE constant } diff --git a/tests/ui/consts/const-eval/issue-44578.stderr b/tests/ui/consts/const-eval/issue-44578.stderr index 7d5cf86d3965d..5093cec81c7ab 100644 --- a/tests/ui/consts/const-eval/issue-44578.stderr +++ b/tests/ui/consts/const-eval/issue-44578.stderr @@ -1,17 +1,17 @@ error[E0080]: evaluation of ` as Foo>::AMT` failed - --> $DIR/issue-44578.rs:13:24 + --> $DIR/issue-44578.rs:14:24 | LL | const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ index out of bounds: the length is 1 but the index is 1 note: erroneous constant encountered - --> $DIR/issue-44578.rs:25:20 + --> $DIR/issue-44578.rs:26:20 | LL | println!("{}", as Foo>::AMT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/issue-44578.rs:25:20 + --> $DIR/issue-44578.rs:26:20 | LL | println!("{}", as Foo>::AMT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | println!("{}", as Foo>::AMT); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: erroneous constant encountered - --> $DIR/issue-44578.rs:25:20 + --> $DIR/issue-44578.rs:26:20 | LL | println!("{}", as Foo>::AMT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | println!("{}", as Foo>::AMT); = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/issue-44578.rs:25:20 + --> $DIR/issue-44578.rs:26:20 | LL | println!("{}", as Foo>::AMT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/const-eval/issue-50814-2.mir-opt.stderr b/tests/ui/consts/const-eval/issue-50814-2.mir-opt.stderr index 2de68d3fee9e0..1f4f217b17560 100644 --- a/tests/ui/consts/const-eval/issue-50814-2.mir-opt.stderr +++ b/tests/ui/consts/const-eval/issue-50814-2.mir-opt.stderr @@ -1,23 +1,23 @@ error[E0080]: evaluation of ` as Foo<()>>::BAR` failed - --> $DIR/issue-50814-2.rs:16:24 + --> $DIR/issue-50814-2.rs:17:24 | LL | const BAR: usize = [5, 6, 7][T::BOO]; | ^^^^^^^^^^^^^^^^^ index out of bounds: the length is 3 but the index is 42 note: erroneous constant encountered - --> $DIR/issue-50814-2.rs:20:6 + --> $DIR/issue-50814-2.rs:21:6 | LL | & as Foo>::BAR | ^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/issue-50814-2.rs:20:5 + --> $DIR/issue-50814-2.rs:21:5 | LL | & as Foo>::BAR | ^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/issue-50814-2.rs:20:5 + --> $DIR/issue-50814-2.rs:21:5 | LL | & as Foo>::BAR | ^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | & as Foo>::BAR = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: erroneous constant encountered - --> $DIR/issue-50814-2.rs:20:5 + --> $DIR/issue-50814-2.rs:21:5 | LL | & as Foo>::BAR | ^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | & as Foo>::BAR = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: erroneous constant encountered - --> $DIR/issue-50814-2.rs:20:5 + --> $DIR/issue-50814-2.rs:21:5 | LL | & as Foo>::BAR | ^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | & as Foo>::BAR = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: erroneous constant encountered - --> $DIR/issue-50814-2.rs:20:6 + --> $DIR/issue-50814-2.rs:21:6 | LL | & as Foo>::BAR | ^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | & as Foo>::BAR = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: erroneous constant encountered - --> $DIR/issue-50814-2.rs:20:6 + --> $DIR/issue-50814-2.rs:21:6 | LL | & as Foo>::BAR | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/const-eval/issue-50814-2.normal.stderr b/tests/ui/consts/const-eval/issue-50814-2.normal.stderr index 4a7dfb1930443..f790862aef158 100644 --- a/tests/ui/consts/const-eval/issue-50814-2.normal.stderr +++ b/tests/ui/consts/const-eval/issue-50814-2.normal.stderr @@ -1,23 +1,23 @@ error[E0080]: evaluation of ` as Foo<()>>::BAR` failed - --> $DIR/issue-50814-2.rs:16:24 + --> $DIR/issue-50814-2.rs:17:24 | LL | const BAR: usize = [5, 6, 7][T::BOO]; | ^^^^^^^^^^^^^^^^^ index out of bounds: the length is 3 but the index is 42 note: erroneous constant encountered - --> $DIR/issue-50814-2.rs:20:6 + --> $DIR/issue-50814-2.rs:21:6 | LL | & as Foo>::BAR | ^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/issue-50814-2.rs:20:5 + --> $DIR/issue-50814-2.rs:21:5 | LL | & as Foo>::BAR | ^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/issue-50814-2.rs:20:6 + --> $DIR/issue-50814-2.rs:21:6 | LL | & as Foo>::BAR | ^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | & as Foo>::BAR = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: the above error was encountered while instantiating `fn foo::<()>` - --> $DIR/issue-50814-2.rs:32:22 + --> $DIR/issue-50814-2.rs:33:22 | LL | println!("{:x}", foo::<()>() as *const usize as usize); | ^^^^^^^^^^^ diff --git a/tests/ui/consts/const-eval/issue-50814-2.rs b/tests/ui/consts/const-eval/issue-50814-2.rs index c2e2de67a6517..261dcd3aa4ca6 100644 --- a/tests/ui/consts/const-eval/issue-50814-2.rs +++ b/tests/ui/consts/const-eval/issue-50814-2.rs @@ -1,6 +1,7 @@ //@ build-fail //@ revisions: normal mir-opt //@ [mir-opt]compile-flags: -Zmir-opt-level=4 +//@ dont-require-annotations: NOTE trait C { const BOO: usize; @@ -17,7 +18,7 @@ impl Foo for A { } fn foo() -> &'static usize { - & as Foo>::BAR //~ constant + & as Foo>::BAR //~ NOTE constant } impl C for () { diff --git a/tests/ui/consts/const-eval/issue-50814.rs b/tests/ui/consts/const-eval/issue-50814.rs index 27b5b39ad7371..5495fb43bba22 100644 --- a/tests/ui/consts/const-eval/issue-50814.rs +++ b/tests/ui/consts/const-eval/issue-50814.rs @@ -1,4 +1,5 @@ //@ build-fail +//@ dont-require-annotations: NOTE trait Unsigned { const MAX: u8; @@ -19,7 +20,7 @@ impl Unsigned for Sum { fn foo(_: T) -> &'static u8 { &Sum::::MAX - //~^ constant + //~^ NOTE constant } fn main() { diff --git a/tests/ui/consts/const-eval/issue-50814.stderr b/tests/ui/consts/const-eval/issue-50814.stderr index fe0e25b820f58..5b23c48e450de 100644 --- a/tests/ui/consts/const-eval/issue-50814.stderr +++ b/tests/ui/consts/const-eval/issue-50814.stderr @@ -1,17 +1,17 @@ error[E0080]: evaluation of ` as Unsigned>::MAX` failed - --> $DIR/issue-50814.rs:15:21 + --> $DIR/issue-50814.rs:16:21 | LL | const MAX: u8 = A::MAX + B::MAX; | ^^^^^^^^^^^^^^^ attempt to compute `u8::MAX + u8::MAX`, which would overflow note: erroneous constant encountered - --> $DIR/issue-50814.rs:21:6 + --> $DIR/issue-50814.rs:22:6 | LL | &Sum::::MAX | ^^^^^^^^^^^^^^^^^^ error[E0080]: evaluation of ` as Unsigned>::MAX` failed - --> $DIR/issue-50814.rs:15:21 + --> $DIR/issue-50814.rs:16:21 | LL | const MAX: u8 = A::MAX + B::MAX; | ^^^^^^^^^^^^^^^ attempt to compute `u8::MAX + u8::MAX`, which would overflow @@ -19,7 +19,7 @@ LL | const MAX: u8 = A::MAX + B::MAX; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: erroneous constant encountered - --> $DIR/issue-50814.rs:21:6 + --> $DIR/issue-50814.rs:22:6 | LL | &Sum::::MAX | ^^^^^^^^^^^^^^^^^^ @@ -27,13 +27,13 @@ LL | &Sum::::MAX = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: erroneous constant encountered - --> $DIR/issue-50814.rs:21:5 + --> $DIR/issue-50814.rs:22:5 | LL | &Sum::::MAX | ^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/issue-50814.rs:21:6 + --> $DIR/issue-50814.rs:22:6 | LL | &Sum::::MAX | ^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | &Sum::::MAX = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: the above error was encountered while instantiating `fn foo::` - --> $DIR/issue-50814.rs:26:5 + --> $DIR/issue-50814.rs:27:5 | LL | foo(0); | ^^^^^^ diff --git a/tests/ui/consts/const-eval/issue-85155.rs b/tests/ui/consts/const-eval/issue-85155.rs index 95253a0b288fa..cb5b3a08375d0 100644 --- a/tests/ui/consts/const-eval/issue-85155.rs +++ b/tests/ui/consts/const-eval/issue-85155.rs @@ -19,3 +19,6 @@ fn main() { post_monomorphization_error::stdarch_intrinsic::<2>(); //~^ NOTE the above error was encountered while instantiating } + +//~? ERROR evaluation of `post_monomorphization_error::ValidateConstImm::<2, 0, 1>::VALID` failed +//~? NOTE erroneous constant encountered diff --git a/tests/ui/consts/const-eval/issue-85907.stderr b/tests/ui/consts/const-eval/issue-85907.stderr index 519227bc69379..fa1bbf2187f77 100644 --- a/tests/ui/consts/const-eval/issue-85907.stderr +++ b/tests/ui/consts/const-eval/issue-85907.stderr @@ -3,8 +3,6 @@ error: argument to `panic!()` in a const context must have type `&str` | LL | panic!(123); | ^^^^^^^^^^^ - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.rs b/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.rs index c6960fa7259db..3a932343ddda4 100644 --- a/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.rs +++ b/tests/ui/consts/const-eval/issue-91827-extern-types-field-offset.rs @@ -37,7 +37,7 @@ const OFFSET: () = unsafe { // fails. let field = &x.a; //~^ ERROR: evaluation of constant value failed - //~| does not have a known offset + //~| NOTE does not have a known offset }; fn main() {} diff --git a/tests/ui/consts/const-eval/nonnull_as_ref_ub.stderr b/tests/ui/consts/const-eval/nonnull_as_ref_ub.stderr index bd6dafb936653..1996cd2721ecf 100644 --- a/tests/ui/consts/const-eval/nonnull_as_ref_ub.stderr +++ b/tests/ui/consts/const-eval/nonnull_as_ref_ub.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/nonnull_as_ref_ub.rs:4:29 | LL | const _: () = assert!(42 == *unsafe { NON_NULL.as_ref() }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 1 byte of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 1 byte, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/panic-assoc-never-type.rs b/tests/ui/consts/const-eval/panic-assoc-never-type.rs index bdaa51494b981..79b5dafaf15fa 100644 --- a/tests/ui/consts/const-eval/panic-assoc-never-type.rs +++ b/tests/ui/consts/const-eval/panic-assoc-never-type.rs @@ -1,4 +1,5 @@ //@ build-fail +//@ dont-require-annotations: NOTE // Regression test for #66975 #![feature(never_type)] @@ -11,5 +12,5 @@ impl PrintName { } fn main() { - let _ = PrintName::VOID; //~ erroneous constant encountered + let _ = PrintName::VOID; //~ NOTE erroneous constant encountered } diff --git a/tests/ui/consts/const-eval/panic-assoc-never-type.stderr b/tests/ui/consts/const-eval/panic-assoc-never-type.stderr index efdbbe5698f0b..03413f46b20ad 100644 --- a/tests/ui/consts/const-eval/panic-assoc-never-type.stderr +++ b/tests/ui/consts/const-eval/panic-assoc-never-type.stderr @@ -1,19 +1,17 @@ error[E0080]: evaluation of constant value failed - --> $DIR/panic-assoc-never-type.rs:9:21 + --> $DIR/panic-assoc-never-type.rs:10:21 | LL | const VOID: ! = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/panic-assoc-never-type.rs:14:13 + --> $DIR/panic-assoc-never-type.rs:15:13 | LL | let _ = PrintName::VOID; | ^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/panic-assoc-never-type.rs:14:13 + --> $DIR/panic-assoc-never-type.rs:15:13 | LL | let _ = PrintName::VOID; | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/const-eval/panic-never-type.stderr b/tests/ui/consts/const-eval/panic-never-type.stderr index 30a320f8db2fa..1d9bba9aea2bf 100644 --- a/tests/ui/consts/const-eval/panic-never-type.stderr +++ b/tests/ui/consts/const-eval/panic-never-type.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of constant value failed | LL | const VOID: ! = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/partial_ptr_overwrite.rs b/tests/ui/consts/const-eval/partial_ptr_overwrite.rs index 1e99d84bba4bc..9438de5e3fe78 100644 --- a/tests/ui/consts/const-eval/partial_ptr_overwrite.rs +++ b/tests/ui/consts/const-eval/partial_ptr_overwrite.rs @@ -5,7 +5,7 @@ const PARTIAL_OVERWRITE: () = { unsafe { let ptr: *mut _ = &mut p; *(ptr as *mut u8) = 123; //~ ERROR constant - //~| unable to overwrite parts of a pointer + //~| NOTE unable to overwrite parts of a pointer } let x = *p; }; diff --git a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr index 27c85cc8ce41a..120a08acc3346 100644 --- a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr +++ b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:21:1 + --> $DIR/raw-bytes.rs:23:1 | LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x00000001, but expected a valid enum tag @@ -10,7 +10,7 @@ LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:29:1 + --> $DIR/raw-bytes.rs:31:1 | LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x00000000, but expected a valid enum tag @@ -21,7 +21,7 @@ LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:43:1 + --> $DIR/raw-bytes.rs:45:1 | LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant @@ -32,7 +32,7 @@ LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:45:1 + --> $DIR/raw-bytes.rs:47:1 | LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant @@ -43,7 +43,7 @@ LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:51:1 + --> $DIR/raw-bytes.rs:53:1 | LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0.1: encountered 0xffffffff, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) @@ -54,7 +54,7 @@ LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::tran } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:56:1 + --> $DIR/raw-bytes.rs:58:1 | LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 @@ -65,7 +65,7 @@ LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:59:1 + --> $DIR/raw-bytes.rs:61:1 | LL | const NULL_U8: NonZero = unsafe { mem::transmute(0u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0, but expected something greater or equal to 1 @@ -76,7 +76,7 @@ LL | const NULL_U8: NonZero = unsafe { mem::transmute(0u8) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:61:1 + --> $DIR/raw-bytes.rs:63:1 | LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0, but expected something greater or equal to 1 @@ -87,7 +87,7 @@ LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:67:1 + --> $DIR/raw-bytes.rs:69:1 | LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 42, but expected something in the range 10..=30 @@ -98,7 +98,7 @@ LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:73:1 + --> $DIR/raw-bytes.rs:75:1 | LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 20, but expected something less or equal to 10, or greater or equal to 30 @@ -109,7 +109,7 @@ LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:76:1 + --> $DIR/raw-bytes.rs:78:1 | LL | const NULL_FAT_PTR: NonNull = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 @@ -120,7 +120,7 @@ LL | const NULL_FAT_PTR: NonNull = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:84:1 + --> $DIR/raw-bytes.rs:86:1 | LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) @@ -131,7 +131,7 @@ LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:88:1 + --> $DIR/raw-bytes.rs:90:1 | LL | const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned box (required 2 byte alignment but found 1) @@ -142,7 +142,7 @@ LL | const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:92:1 + --> $DIR/raw-bytes.rs:94:1 | LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null reference @@ -153,7 +153,7 @@ LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:95:1 + --> $DIR/raw-bytes.rs:97:1 | LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null box @@ -164,7 +164,7 @@ LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:98:1 + --> $DIR/raw-bytes.rs:100:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x539[noalloc] has no provenance) @@ -175,7 +175,7 @@ LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:101:1 + --> $DIR/raw-bytes.rs:103:1 | LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (0x539[noalloc] has no provenance) @@ -186,7 +186,7 @@ LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:104:1 + --> $DIR/raw-bytes.rs:106:1 | LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a function pointer @@ -197,7 +197,7 @@ LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:106:1 + --> $DIR/raw-bytes.rs:108:1 | LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0xd[noalloc], but expected a function pointer @@ -208,7 +208,7 @@ LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:108:1 + --> $DIR/raw-bytes.rs:110:1 | LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC3, but expected a function pointer @@ -219,7 +219,7 @@ LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:114:1 + --> $DIR/raw-bytes.rs:116:1 | LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to uninhabited type Bar @@ -230,7 +230,7 @@ LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:139:1 + --> $DIR/raw-bytes.rs:141:1 | LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (going beyond the bounds of its allocation) @@ -241,7 +241,7 @@ LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:141:1 + --> $DIR/raw-bytes.rs:143:1 | LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered invalid reference metadata: slice is bigger than largest supported object @@ -252,7 +252,7 @@ LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, us } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:143:1 + --> $DIR/raw-bytes.rs:145:1 | LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object @@ -263,7 +263,7 @@ LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize: } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:146:1 + --> $DIR/raw-bytes.rs:148:1 | LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered uninitialized memory, but expected a string @@ -274,7 +274,7 @@ LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit: } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:148:1 + --> $DIR/raw-bytes.rs:150:1 | LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered uninitialized memory, but expected a string @@ -285,7 +285,7 @@ LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUni } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:150:1 + --> $DIR/raw-bytes.rs:152:1 | LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a pointer, but expected a string @@ -298,7 +298,7 @@ LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _> = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:154:1 + --> $DIR/raw-bytes.rs:156:1 | LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (going beyond the bounds of its allocation) @@ -309,7 +309,7 @@ LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:156:1 + --> $DIR/raw-bytes.rs:158:1 | LL | const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, isize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object @@ -320,7 +320,7 @@ LL | const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, is } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:159:1 + --> $DIR/raw-bytes.rs:161:1 | LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (going beyond the bounds of its allocation) @@ -331,7 +331,7 @@ LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999us } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:162:1 + --> $DIR/raw-bytes.rs:164:1 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x03, but expected a boolean @@ -342,13 +342,13 @@ LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; } note: erroneous constant encountered - --> $DIR/raw-bytes.rs:162:40 + --> $DIR/raw-bytes.rs:164:40 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:168:1 + --> $DIR/raw-bytes.rs:170:1 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered 0x03, but expected a boolean @@ -359,13 +359,13 @@ LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3 } note: erroneous constant encountered - --> $DIR/raw-bytes.rs:168:42 + --> $DIR/raw-bytes.rs:170:42 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:172:1 + --> $DIR/raw-bytes.rs:174:1 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..1[0]: encountered 0x03, but expected a boolean @@ -376,13 +376,13 @@ LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::tran } note: erroneous constant encountered - --> $DIR/raw-bytes.rs:172:42 + --> $DIR/raw-bytes.rs:174:42 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:177:1 + --> $DIR/raw-bytes.rs:179:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC17, but expected a vtable pointer @@ -393,7 +393,7 @@ LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W(( } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:181:1 + --> $DIR/raw-bytes.rs:183:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC19, but expected a vtable pointer @@ -404,7 +404,7 @@ LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W(( } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:185:1 + --> $DIR/raw-bytes.rs:187:1 | LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0x4[noalloc], but expected a vtable pointer @@ -415,7 +415,7 @@ LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:188:1 + --> $DIR/raw-bytes.rs:190:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC22, but expected a vtable pointer @@ -426,7 +426,7 @@ LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::trans } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:192:1 + --> $DIR/raw-bytes.rs:194:1 | LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..: encountered 0x03, but expected a boolean @@ -437,7 +437,7 @@ LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:196:1 + --> $DIR/raw-bytes.rs:198:1 | LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a vtable pointer @@ -448,7 +448,7 @@ LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:199:1 + --> $DIR/raw-bytes.rs:201:1 | LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC27, but expected a vtable pointer @@ -459,7 +459,7 @@ LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transm } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:204:1 + --> $DIR/raw-bytes.rs:206:1 | LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to uninhabited type [!; 1] @@ -470,7 +470,7 @@ LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:205:1 + --> $DIR/raw-bytes.rs:207:1 | LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered a value of the never type `!` @@ -481,7 +481,7 @@ LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:206:1 + --> $DIR/raw-bytes.rs:208:1 | LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; | ^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered a value of the never type `!` @@ -492,7 +492,7 @@ LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:210:1 + --> $DIR/raw-bytes.rs:212:1 | LL | pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) }; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered uninitialized memory, but expected an integer @@ -503,7 +503,7 @@ LL | pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) } } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:213:1 + --> $DIR/raw-bytes.rs:215:1 | LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem::size_of::<&u32>()) }; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered a pointer, but expected an integer @@ -516,7 +516,7 @@ LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem: = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:216:1 + --> $DIR/raw-bytes.rs:218:1 | LL | pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) }; | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x11, but expected a boolean @@ -527,7 +527,7 @@ LL | pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:220:1 + --> $DIR/raw-bytes.rs:222:1 | LL | pub static S7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[1]: encountered uninitialized memory, but expected an integer @@ -538,7 +538,7 @@ LL | pub static S7: &[u16] = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:227:1 + --> $DIR/raw-bytes.rs:229:1 | LL | pub static R4: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered uninitialized memory, but expected an integer @@ -549,7 +549,7 @@ LL | pub static R4: &[u8] = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:232:1 + --> $DIR/raw-bytes.rs:234:1 | LL | pub static R5: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered a pointer, but expected an integer @@ -562,7 +562,7 @@ LL | pub static R5: &[u8] = unsafe { = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:237:1 + --> $DIR/raw-bytes.rs:239:1 | LL | pub static R6: &[bool] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x11, but expected a boolean diff --git a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr index 2b0ce99a88173..d54ad7c8546c1 100644 --- a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr +++ b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:21:1 + --> $DIR/raw-bytes.rs:23:1 | LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x0000000000000001, but expected a valid enum tag @@ -10,7 +10,7 @@ LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:29:1 + --> $DIR/raw-bytes.rs:31:1 | LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x0000000000000000, but expected a valid enum tag @@ -21,7 +21,7 @@ LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:43:1 + --> $DIR/raw-bytes.rs:45:1 | LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant @@ -32,7 +32,7 @@ LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:45:1 + --> $DIR/raw-bytes.rs:47:1 | LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant @@ -43,7 +43,7 @@ LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:51:1 + --> $DIR/raw-bytes.rs:53:1 | LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0.1: encountered 0xffffffff, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) @@ -54,7 +54,7 @@ LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::tran } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:56:1 + --> $DIR/raw-bytes.rs:58:1 | LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 @@ -65,7 +65,7 @@ LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:59:1 + --> $DIR/raw-bytes.rs:61:1 | LL | const NULL_U8: NonZero = unsafe { mem::transmute(0u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0, but expected something greater or equal to 1 @@ -76,7 +76,7 @@ LL | const NULL_U8: NonZero = unsafe { mem::transmute(0u8) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:61:1 + --> $DIR/raw-bytes.rs:63:1 | LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0, but expected something greater or equal to 1 @@ -87,7 +87,7 @@ LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:67:1 + --> $DIR/raw-bytes.rs:69:1 | LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 42, but expected something in the range 10..=30 @@ -98,7 +98,7 @@ LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:73:1 + --> $DIR/raw-bytes.rs:75:1 | LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 20, but expected something less or equal to 10, or greater or equal to 30 @@ -109,7 +109,7 @@ LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:76:1 + --> $DIR/raw-bytes.rs:78:1 | LL | const NULL_FAT_PTR: NonNull = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 @@ -120,7 +120,7 @@ LL | const NULL_FAT_PTR: NonNull = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:84:1 + --> $DIR/raw-bytes.rs:86:1 | LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) @@ -131,7 +131,7 @@ LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:88:1 + --> $DIR/raw-bytes.rs:90:1 | LL | const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned box (required 2 byte alignment but found 1) @@ -142,7 +142,7 @@ LL | const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:92:1 + --> $DIR/raw-bytes.rs:94:1 | LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null reference @@ -153,7 +153,7 @@ LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:95:1 + --> $DIR/raw-bytes.rs:97:1 | LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null box @@ -164,7 +164,7 @@ LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:98:1 + --> $DIR/raw-bytes.rs:100:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x539[noalloc] has no provenance) @@ -175,7 +175,7 @@ LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:101:1 + --> $DIR/raw-bytes.rs:103:1 | LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (0x539[noalloc] has no provenance) @@ -186,7 +186,7 @@ LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:104:1 + --> $DIR/raw-bytes.rs:106:1 | LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a function pointer @@ -197,7 +197,7 @@ LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:106:1 + --> $DIR/raw-bytes.rs:108:1 | LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0xd[noalloc], but expected a function pointer @@ -208,7 +208,7 @@ LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:108:1 + --> $DIR/raw-bytes.rs:110:1 | LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC3, but expected a function pointer @@ -219,7 +219,7 @@ LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:114:1 + --> $DIR/raw-bytes.rs:116:1 | LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to uninhabited type Bar @@ -230,7 +230,7 @@ LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:139:1 + --> $DIR/raw-bytes.rs:141:1 | LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (going beyond the bounds of its allocation) @@ -241,7 +241,7 @@ LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:141:1 + --> $DIR/raw-bytes.rs:143:1 | LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered invalid reference metadata: slice is bigger than largest supported object @@ -252,7 +252,7 @@ LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, us } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:143:1 + --> $DIR/raw-bytes.rs:145:1 | LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object @@ -263,7 +263,7 @@ LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize: } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:146:1 + --> $DIR/raw-bytes.rs:148:1 | LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered uninitialized memory, but expected a string @@ -274,7 +274,7 @@ LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit: } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:148:1 + --> $DIR/raw-bytes.rs:150:1 | LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered uninitialized memory, but expected a string @@ -285,7 +285,7 @@ LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUni } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:150:1 + --> $DIR/raw-bytes.rs:152:1 | LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a pointer, but expected a string @@ -298,7 +298,7 @@ LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _> = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:154:1 + --> $DIR/raw-bytes.rs:156:1 | LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (going beyond the bounds of its allocation) @@ -309,7 +309,7 @@ LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:156:1 + --> $DIR/raw-bytes.rs:158:1 | LL | const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, isize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object @@ -320,7 +320,7 @@ LL | const SLICE_TOO_LONG_OVERFLOW: &[u32] = unsafe { mem::transmute((&42u32, is } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:159:1 + --> $DIR/raw-bytes.rs:161:1 | LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (going beyond the bounds of its allocation) @@ -331,7 +331,7 @@ LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999us } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:162:1 + --> $DIR/raw-bytes.rs:164:1 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x03, but expected a boolean @@ -342,13 +342,13 @@ LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; } note: erroneous constant encountered - --> $DIR/raw-bytes.rs:162:40 + --> $DIR/raw-bytes.rs:164:40 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:168:1 + --> $DIR/raw-bytes.rs:170:1 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered 0x03, but expected a boolean @@ -359,13 +359,13 @@ LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3 } note: erroneous constant encountered - --> $DIR/raw-bytes.rs:168:42 + --> $DIR/raw-bytes.rs:170:42 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:172:1 + --> $DIR/raw-bytes.rs:174:1 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..1[0]: encountered 0x03, but expected a boolean @@ -376,13 +376,13 @@ LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::tran } note: erroneous constant encountered - --> $DIR/raw-bytes.rs:172:42 + --> $DIR/raw-bytes.rs:174:42 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:177:1 + --> $DIR/raw-bytes.rs:179:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC17, but expected a vtable pointer @@ -393,7 +393,7 @@ LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W(( } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:181:1 + --> $DIR/raw-bytes.rs:183:1 | LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC19, but expected a vtable pointer @@ -404,7 +404,7 @@ LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W(( } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:185:1 + --> $DIR/raw-bytes.rs:187:1 | LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0x4[noalloc], but expected a vtable pointer @@ -415,7 +415,7 @@ LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:188:1 + --> $DIR/raw-bytes.rs:190:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC22, but expected a vtable pointer @@ -426,7 +426,7 @@ LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::trans } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:192:1 + --> $DIR/raw-bytes.rs:194:1 | LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..: encountered 0x03, but expected a boolean @@ -437,7 +437,7 @@ LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:196:1 + --> $DIR/raw-bytes.rs:198:1 | LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a vtable pointer @@ -448,7 +448,7 @@ LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:199:1 + --> $DIR/raw-bytes.rs:201:1 | LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC27, but expected a vtable pointer @@ -459,7 +459,7 @@ LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transm } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:204:1 + --> $DIR/raw-bytes.rs:206:1 | LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to uninhabited type [!; 1] @@ -470,7 +470,7 @@ LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:205:1 + --> $DIR/raw-bytes.rs:207:1 | LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; | ^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered a value of the never type `!` @@ -481,7 +481,7 @@ LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:206:1 + --> $DIR/raw-bytes.rs:208:1 | LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; | ^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered a value of the never type `!` @@ -492,7 +492,7 @@ LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:210:1 + --> $DIR/raw-bytes.rs:212:1 | LL | pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) }; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered uninitialized memory, but expected an integer @@ -503,7 +503,7 @@ LL | pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) } } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:213:1 + --> $DIR/raw-bytes.rs:215:1 | LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem::size_of::<&u32>()) }; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered a pointer, but expected an integer @@ -516,7 +516,7 @@ LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem: = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:216:1 + --> $DIR/raw-bytes.rs:218:1 | LL | pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) }; | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x11, but expected a boolean @@ -527,7 +527,7 @@ LL | pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:220:1 + --> $DIR/raw-bytes.rs:222:1 | LL | pub static S7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[1]: encountered uninitialized memory, but expected an integer @@ -538,7 +538,7 @@ LL | pub static S7: &[u16] = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:227:1 + --> $DIR/raw-bytes.rs:229:1 | LL | pub static R4: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered uninitialized memory, but expected an integer @@ -549,7 +549,7 @@ LL | pub static R4: &[u8] = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:232:1 + --> $DIR/raw-bytes.rs:234:1 | LL | pub static R5: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered a pointer, but expected an integer @@ -562,7 +562,7 @@ LL | pub static R5: &[u8] = unsafe { = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: it is undefined behavior to use this value - --> $DIR/raw-bytes.rs:237:1 + --> $DIR/raw-bytes.rs:239:1 | LL | pub static R6: &[bool] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x11, but expected a boolean diff --git a/tests/ui/consts/const-eval/raw-bytes.rs b/tests/ui/consts/const-eval/raw-bytes.rs index 9187de5636202..1a585d55a5f02 100644 --- a/tests/ui/consts/const-eval/raw-bytes.rs +++ b/tests/ui/consts/const-eval/raw-bytes.rs @@ -2,7 +2,9 @@ //@ ignore-endian-big // ignore-tidy-linelength //@ normalize-stderr: "╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼" -> "╾ALLOC_ID$1╼" -#![allow(invalid_value)] +//@ dont-require-annotations: NOTE + +#![allow(invalid_value, unnecessary_transmutes)] #![feature(never_type, rustc_attrs, ptr_metadata, slice_from_ptr_range, const_slice_from_ptr_range)] use std::mem; @@ -83,11 +85,11 @@ const NULL_FAT_PTR: NonNull = unsafe { const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; //~^ ERROR it is undefined behavior to use this value -//~| constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) +//~| NOTE constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; //~^ ERROR it is undefined behavior to use this value -//~| constructing invalid value: encountered an unaligned box (required 2 byte alignment but found 1) +//~| NOTE constructing invalid value: encountered an unaligned box (required 2 byte alignment but found 1) const NULL: &u16 = unsafe { mem::transmute(0usize) }; //~^ ERROR it is undefined behavior to use this value @@ -161,44 +163,44 @@ const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) // bad data *inside* the slice const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; //~^ ERROR it is undefined behavior to use this value -//~| constant +//~| NOTE constant // bad: sized field is not okay const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); //~^ ERROR it is undefined behavior to use this value -//~| constant +//~| NOTE constant // bad: unsized part is not okay const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); //~^ ERROR it is undefined behavior to use this value -//~| constant +//~| NOTE constant // bad trait object const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; //~^ ERROR it is undefined behavior to use this value -//~| expected a vtable +//~| NOTE expected a vtable // bad trait object const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; //~^ ERROR it is undefined behavior to use this value -//~| expected a vtable +//~| NOTE expected a vtable // bad trait object const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; //~^ ERROR it is undefined behavior to use this value -//~| expected a vtable +//~| NOTE expected a vtable const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; //~^ ERROR it is undefined behavior to use this value -//~| expected a vtable +//~| NOTE expected a vtable // bad data *inside* the trait object const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; //~^ ERROR it is undefined behavior to use this value -//~| expected a boolean +//~| NOTE expected a boolean const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; //~^ ERROR it is undefined behavior to use this value -//~| null pointer +//~| NOTE null pointer const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; //~^ ERROR it is undefined behavior to use this value -//~| vtable +//~| NOTE vtable // Uninhabited types const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; //~ ERROR undefined behavior diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.rs b/tests/ui/consts/const-eval/raw-pointer-ub.rs index 478e93a910e0c..1383de63109c4 100644 --- a/tests/ui/consts/const-eval/raw-pointer-ub.rs +++ b/tests/ui/consts/const-eval/raw-pointer-ub.rs @@ -19,7 +19,7 @@ const MISALIGNED_COPY: () = unsafe { y.copy_to_nonoverlapping(&mut z, 1); //~^ ERROR evaluation of constant value failed //~| NOTE inside `std::ptr::const_ptr - //~| NOTE inside `copy_nonoverlapping::` + //~| NOTE inside `std::ptr::copy_nonoverlapping::` //~| NOTE accessing memory with alignment 1, but alignment 4 is required // The actual error points into the implementation of `copy_to_nonoverlapping`. }; @@ -39,7 +39,7 @@ const OOB: () = unsafe { let mem = [0u32; 1]; let ptr = mem.as_ptr().cast::(); let _val = *ptr; //~ERROR: evaluation of constant value failed - //~^NOTE: expected a pointer to 8 bytes of memory + //~^NOTE: is only 4 bytes from the end of the allocation }; fn main() {} diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.stderr b/tests/ui/consts/const-eval/raw-pointer-ub.stderr index 4fff293b2eef8..0f3dc33f3a31b 100644 --- a/tests/ui/consts/const-eval/raw-pointer-ub.stderr +++ b/tests/ui/consts/const-eval/raw-pointer-ub.stderr @@ -18,8 +18,8 @@ LL | y.copy_to_nonoverlapping(&mut z, 1); | note: inside `std::ptr::const_ptr::::copy_to_nonoverlapping` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: inside `copy_nonoverlapping::` - --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL +note: inside `std::ptr::copy_nonoverlapping::` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL error[E0080]: evaluation of constant value failed --> $DIR/raw-pointer-ub.rs:34:16 @@ -31,7 +31,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/raw-pointer-ub.rs:41:16 | LL | let _val = *ptr; - | ^^^^ memory access failed: expected a pointer to 8 bytes of memory, but got ALLOC0 which is only 4 bytes from the end of the allocation + | ^^^^ memory access failed: attempting to access 8 bytes, but got ALLOC0 which is only 4 bytes from the end of the allocation error: aborting due to 5 previous errors diff --git a/tests/ui/consts/const-eval/transmute-const-promotion.rs b/tests/ui/consts/const-eval/transmute-const-promotion.rs index 1f0240d4b5ac7..840334f43c09c 100644 --- a/tests/ui/consts/const-eval/transmute-const-promotion.rs +++ b/tests/ui/consts/const-eval/transmute-const-promotion.rs @@ -1,3 +1,4 @@ +#![allow(unnecessary_transmutes)] use std::mem; fn main() { diff --git a/tests/ui/consts/const-eval/transmute-const-promotion.stderr b/tests/ui/consts/const-eval/transmute-const-promotion.stderr index 3603db03bb204..eb2fed091c34a 100644 --- a/tests/ui/consts/const-eval/transmute-const-promotion.stderr +++ b/tests/ui/consts/const-eval/transmute-const-promotion.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/transmute-const-promotion.rs:4:37 + --> $DIR/transmute-const-promotion.rs:5:37 | LL | let x: &'static u32 = unsafe { &mem::transmute(3.0f32) }; | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use diff --git a/tests/ui/consts/const-eval/transmute-const.rs b/tests/ui/consts/const-eval/transmute-const.rs index 1cfad00ca76df..fb6cb77675fa6 100644 --- a/tests/ui/consts/const-eval/transmute-const.rs +++ b/tests/ui/consts/const-eval/transmute-const.rs @@ -1,3 +1,4 @@ +#![allow(unnecessary_transmutes)] use std::mem; static FOO: bool = unsafe { mem::transmute(3u8) }; diff --git a/tests/ui/consts/const-eval/transmute-const.stderr b/tests/ui/consts/const-eval/transmute-const.stderr index d72289487d7bf..35a5cabaa6710 100644 --- a/tests/ui/consts/const-eval/transmute-const.stderr +++ b/tests/ui/consts/const-eval/transmute-const.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/transmute-const.rs:3:1 + --> $DIR/transmute-const.rs:4:1 | LL | static FOO: bool = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x03, but expected a boolean diff --git a/tests/ui/consts/const-eval/ub-enum-overwrite.rs b/tests/ui/consts/const-eval/ub-enum-overwrite.rs index 69f1d01b2f31c..e37c25718f8e8 100644 --- a/tests/ui/consts/const-eval/ub-enum-overwrite.rs +++ b/tests/ui/consts/const-eval/ub-enum-overwrite.rs @@ -10,7 +10,7 @@ const _: u8 = { e = E::B; unsafe { *p } //~^ ERROR evaluation of constant value failed - //~| uninitialized + //~| NOTE uninitialized }; fn main() {} diff --git a/tests/ui/consts/const-eval/ub-enum.rs b/tests/ui/consts/const-eval/ub-enum.rs index 5be444e667a1f..ac543430da9d3 100644 --- a/tests/ui/consts/const-eval/ub-enum.rs +++ b/tests/ui/consts/const-eval/ub-enum.rs @@ -2,8 +2,10 @@ //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" //@ normalize-stderr: "0x0+" -> "0x0" +//@ dont-require-annotations: NOTE + #![feature(never_type)] -#![allow(invalid_value)] +#![allow(invalid_value, unnecessary_transmutes)] use std::mem; @@ -58,7 +60,7 @@ union MaybeUninit { } const BAD_ENUM2_UNDEF : Enum2 = unsafe { MaybeUninit { uninit: () }.init }; //~^ ERROR evaluation of constant value failed -//~| uninitialized +//~| NOTE uninitialized // Pointer value in an enum with a niche that is not just 0. const BAD_ENUM2_OPTION_PTR: Option = unsafe { mem::transmute(&0) }; diff --git a/tests/ui/consts/const-eval/ub-enum.stderr b/tests/ui/consts/const-eval/ub-enum.stderr index cfb7eaf537ac0..faec412b004bc 100644 --- a/tests/ui/consts/const-eval/ub-enum.stderr +++ b/tests/ui/consts/const-eval/ub-enum.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:27:1 + --> $DIR/ub-enum.rs:29:1 | LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x01, but expected a valid enum tag @@ -10,7 +10,7 @@ LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; } error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:30:1 + --> $DIR/ub-enum.rs:32:1 | LL | const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -19,7 +19,7 @@ LL | const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:33:1 + --> $DIR/ub-enum.rs:35:1 | LL | const BAD_ENUM_WRAPPED: Wrap = unsafe { mem::transmute(&1) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -28,7 +28,7 @@ LL | const BAD_ENUM_WRAPPED: Wrap = unsafe { mem::transmute(&1) }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:45:1 + --> $DIR/ub-enum.rs:47:1 | LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x0, but expected a valid enum tag @@ -39,7 +39,7 @@ LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; } error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:47:1 + --> $DIR/ub-enum.rs:49:1 | LL | const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -48,7 +48,7 @@ LL | const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:50:1 + --> $DIR/ub-enum.rs:52:1 | LL | const BAD_ENUM2_WRAPPED: Wrap = unsafe { mem::transmute(&0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -57,13 +57,13 @@ LL | const BAD_ENUM2_WRAPPED: Wrap = unsafe { mem::transmute(&0) }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:59:42 + --> $DIR/ub-enum.rs:61:42 | LL | const BAD_ENUM2_UNDEF : Enum2 = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:64:1 + --> $DIR/ub-enum.rs:66:1 | LL | const BAD_ENUM2_OPTION_PTR: Option = unsafe { mem::transmute(&0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -72,7 +72,7 @@ LL | const BAD_ENUM2_OPTION_PTR: Option = unsafe { mem::transmute(&0) }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:81:1 + --> $DIR/ub-enum.rs:83:1 | LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant @@ -83,7 +83,7 @@ LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:83:1 + --> $DIR/ub-enum.rs:85:1 | LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant @@ -94,7 +94,7 @@ LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:91:1 + --> $DIR/ub-enum.rs:93:1 | LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0.1: encountered 0xffffffff, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) @@ -105,19 +105,19 @@ LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::tran } error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:96:77 + --> $DIR/ub-enum.rs:98:77 | LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:98:77 + --> $DIR/ub-enum.rs:100:77 | LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) }; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:103:14 + --> $DIR/ub-enum.rs:105:14 | LL | unsafe { std::mem::discriminant(&*(&() as *const () as *const Never)); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ read discriminant of an uninhabited enum variant diff --git a/tests/ui/consts/const-eval/ub-incorrect-vtable.rs b/tests/ui/consts/const-eval/ub-incorrect-vtable.rs index 8058f7693a71c..30c0ae3bb5af0 100644 --- a/tests/ui/consts/const-eval/ub-incorrect-vtable.rs +++ b/tests/ui/consts/const-eval/ub-incorrect-vtable.rs @@ -11,19 +11,19 @@ // errors are emitted instead of ICEs. //@ stderr-per-bitwidth - +//@ dont-require-annotations: NOTE trait Trait {} const INVALID_VTABLE_ALIGNMENT: &dyn Trait = unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) }; //~^^ ERROR it is undefined behavior to use this value -//~| vtable +//~| NOTE vtable const INVALID_VTABLE_SIZE: &dyn Trait = unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) }; //~^^ ERROR it is undefined behavior to use this value -//~| vtable +//~| NOTE vtable #[repr(transparent)] struct W(T); @@ -33,18 +33,18 @@ fn drop_me(_: *mut usize) {} const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> = unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), 1usize, 1000usize))) }; //~^^ ERROR it is undefined behavior to use this value -//~| expected a vtable pointer +//~| NOTE expected a vtable pointer const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> = unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), usize::MAX, 1usize))) }; //~^^ ERROR it is undefined behavior to use this value -//~| expected a vtable pointer +//~| NOTE expected a vtable pointer // Even if the vtable has a fn ptr and a reasonable size+align, it still does not work. const INVALID_VTABLE_UB: W<&dyn Trait> = unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), 1usize, 1usize))) }; //~^^ ERROR it is undefined behavior to use this value -//~| expected a vtable pointer +//~| NOTE expected a vtable pointer // Trying to access the data in a vtable does not work, either. @@ -90,7 +90,7 @@ union Transmute { const FOO: &dyn Bar = &Foo { foo: 128, bar: false }; const G: Wide = unsafe { Transmute { t: FOO }.u }; //~^ ERROR it is undefined behavior to use this value -//~| encountered a dangling reference +//~| NOTE encountered a dangling reference // (it is dangling because vtables do not contain memory that can be dereferenced) fn main() {} diff --git a/tests/ui/consts/const-eval/ub-int-array.rs b/tests/ui/consts/const-eval/ub-int-array.rs index cde0749dc5fb5..169ac7f15194d 100644 --- a/tests/ui/consts/const-eval/ub-int-array.rs +++ b/tests/ui/consts/const-eval/ub-int-array.rs @@ -1,6 +1,8 @@ //! Test the "array of int" fast path in validity checking, and in particular whether it //! points at the right array element. +//@ dont-require-annotations: NOTE + use std::mem; #[repr(C)] @@ -17,7 +19,7 @@ impl MaybeUninit { const UNINIT_INT_0: [u32; 3] = unsafe { //~^ ERROR it is undefined behavior to use this value - //~| invalid value at [0] + //~| NOTE invalid value at [0] mem::transmute([ MaybeUninit { uninit: () }, // Constants chosen to achieve endianness-independent hex dump. @@ -27,7 +29,7 @@ const UNINIT_INT_0: [u32; 3] = unsafe { }; const UNINIT_INT_1: [u32; 3] = unsafe { //~^ ERROR it is undefined behavior to use this value - //~| invalid value at [1] + //~| NOTE invalid value at [1] mem::transmute([ MaybeUninit::new(0u8), MaybeUninit::new(0u8), @@ -45,7 +47,7 @@ const UNINIT_INT_1: [u32; 3] = unsafe { }; const UNINIT_INT_2: [u32; 3] = unsafe { //~^ ERROR it is undefined behavior to use this value - //~| invalid value at [2] + //~| NOTE invalid value at [2] mem::transmute([ MaybeUninit::new(0u8), MaybeUninit::new(0u8), diff --git a/tests/ui/consts/const-eval/ub-int-array.stderr b/tests/ui/consts/const-eval/ub-int-array.stderr index c8efd7e1bd35d..d6ef9bcfeba63 100644 --- a/tests/ui/consts/const-eval/ub-int-array.stderr +++ b/tests/ui/consts/const-eval/ub-int-array.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-int-array.rs:18:1 + --> $DIR/ub-int-array.rs:20:1 | LL | const UNINIT_INT_0: [u32; 3] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered uninitialized memory, but expected an integer @@ -10,7 +10,7 @@ LL | const UNINIT_INT_0: [u32; 3] = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-int-array.rs:28:1 + --> $DIR/ub-int-array.rs:30:1 | LL | const UNINIT_INT_1: [u32; 3] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [1]: encountered uninitialized memory, but expected an integer @@ -21,7 +21,7 @@ LL | const UNINIT_INT_1: [u32; 3] = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-int-array.rs:46:1 + --> $DIR/ub-int-array.rs:48:1 | LL | const UNINIT_INT_2: [u32; 3] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [2]: encountered uninitialized memory, but expected an integer diff --git a/tests/ui/consts/const-eval/ub-nonnull.rs b/tests/ui/consts/const-eval/ub-nonnull.rs index b8e312759b4c3..8ae913db1e4c4 100644 --- a/tests/ui/consts/const-eval/ub-nonnull.rs +++ b/tests/ui/consts/const-eval/ub-nonnull.rs @@ -1,6 +1,8 @@ // Strip out raw byte dumps to make comparison platform-independent: //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ dont-require-annotations: NOTE + #![allow(invalid_value)] // make sure we cannot allow away the errors tested here #![feature(rustc_attrs, ptr_metadata)] @@ -33,7 +35,7 @@ union MaybeUninit { } const UNINIT: NonZero = unsafe { MaybeUninit { uninit: () }.init }; //~^ ERROR evaluation of constant value failed -//~| uninitialized +//~| NOTE uninitialized // Also test other uses of rustc_layout_scalar_valid_range_start diff --git a/tests/ui/consts/const-eval/ub-nonnull.stderr b/tests/ui/consts/const-eval/ub-nonnull.stderr index 0e4926eb49edf..75dd0443ca4e4 100644 --- a/tests/ui/consts/const-eval/ub-nonnull.stderr +++ b/tests/ui/consts/const-eval/ub-nonnull.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-nonnull.rs:14:1 + --> $DIR/ub-nonnull.rs:16:1 | LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 @@ -10,13 +10,13 @@ LL | const NULL_PTR: NonNull = unsafe { mem::transmute(0usize) }; } error[E0080]: evaluation of constant value failed - --> $DIR/ub-nonnull.rs:20:29 + --> $DIR/ub-nonnull.rs:22:29 | LL | let out_of_bounds_ptr = &ptr[255]; - | ^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 255 bytes of memory, but got ALLOC1 which is only 1 byte from the end of the allocation + | ^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 255 bytes, but got ALLOC1 which is only 1 byte from the end of the allocation error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-nonnull.rs:24:1 + --> $DIR/ub-nonnull.rs:26:1 | LL | const NULL_U8: NonZero = unsafe { mem::transmute(0u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0, but expected something greater or equal to 1 @@ -27,7 +27,7 @@ LL | const NULL_U8: NonZero = unsafe { mem::transmute(0u8) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-nonnull.rs:26:1 + --> $DIR/ub-nonnull.rs:28:1 | LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0, but expected something greater or equal to 1 @@ -38,13 +38,13 @@ LL | const NULL_USIZE: NonZero = unsafe { mem::transmute(0usize) }; } error[E0080]: evaluation of constant value failed - --> $DIR/ub-nonnull.rs:34:38 + --> $DIR/ub-nonnull.rs:36:38 | LL | const UNINIT: NonZero = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-nonnull.rs:43:1 + --> $DIR/ub-nonnull.rs:45:1 | LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 42, but expected something in the range 10..=30 @@ -55,7 +55,7 @@ LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-nonnull.rs:49:1 + --> $DIR/ub-nonnull.rs:51:1 | LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 20, but expected something less or equal to 10, or greater or equal to 30 @@ -66,7 +66,7 @@ LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-nonnull.rs:52:1 + --> $DIR/ub-nonnull.rs:54:1 | LL | const NULL_FAT_PTR: NonNull = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.rs b/tests/ui/consts/const-eval/ub-ref-ptr.rs index 50e510a3d5467..988e6c62e3465 100644 --- a/tests/ui/consts/const-eval/ub-ref-ptr.rs +++ b/tests/ui/consts/const-eval/ub-ref-ptr.rs @@ -2,6 +2,8 @@ // Strip out raw byte dumps to make comparison platform-independent: //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ dont-require-annotations: NOTE + #![allow(invalid_value)] use std::mem; @@ -14,11 +16,11 @@ union MaybeUninit { const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; //~^ ERROR it is undefined behavior to use this value -//~| constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) +//~| NOTE constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; //~^ ERROR it is undefined behavior to use this value -//~| constructing invalid value: encountered an unaligned box (required 2 byte alignment but found 1) +//~| NOTE constructing invalid value: encountered an unaligned box (required 2 byte alignment but found 1) const NULL: &u16 = unsafe { mem::transmute(0usize) }; //~^ ERROR it is undefined behavior to use this value @@ -47,13 +49,13 @@ const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init }; //~^ ERROR evaluation of constant value failed -//~| uninitialized +//~| NOTE uninitialized const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; //~^ ERROR it is undefined behavior to use this value const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init }; //~^ ERROR evaluation of constant value failed -//~| uninitialized +//~| NOTE uninitialized const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; //~^ ERROR it is undefined behavior to use this value const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.stderr b/tests/ui/consts/const-eval/ub-ref-ptr.stderr index 72a523282e6b4..de5e721c3f716 100644 --- a/tests/ui/consts/const-eval/ub-ref-ptr.stderr +++ b/tests/ui/consts/const-eval/ub-ref-ptr.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:15:1 + --> $DIR/ub-ref-ptr.rs:17:1 | LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) @@ -10,7 +10,7 @@ LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:19:1 + --> $DIR/ub-ref-ptr.rs:21:1 | LL | const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned box (required 2 byte alignment but found 1) @@ -21,7 +21,7 @@ LL | const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:23:1 + --> $DIR/ub-ref-ptr.rs:25:1 | LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null reference @@ -32,7 +32,7 @@ LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:26:1 + --> $DIR/ub-ref-ptr.rs:28:1 | LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null box @@ -43,7 +43,7 @@ LL | const NULL_BOX: Box = unsafe { mem::transmute(0usize) }; } error[E0080]: evaluation of constant value failed - --> $DIR/ub-ref-ptr.rs:33:1 + --> $DIR/ub-ref-ptr.rs:35:1 | LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -52,7 +52,7 @@ LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/ub-ref-ptr.rs:36:39 + --> $DIR/ub-ref-ptr.rs:38:39 | LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -61,13 +61,13 @@ LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported note: erroneous constant encountered - --> $DIR/ub-ref-ptr.rs:36:38 + --> $DIR/ub-ref-ptr.rs:38:38 | LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: evaluation of constant value failed - --> $DIR/ub-ref-ptr.rs:39:86 + --> $DIR/ub-ref-ptr.rs:41:86 | LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) }; | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -76,13 +76,13 @@ LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[us = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported note: erroneous constant encountered - --> $DIR/ub-ref-ptr.rs:39:85 + --> $DIR/ub-ref-ptr.rs:41:85 | LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) }; | ^^^^^^^^^^^^^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:42:1 + --> $DIR/ub-ref-ptr.rs:44:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x539[noalloc] has no provenance) @@ -93,7 +93,7 @@ LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:45:1 + --> $DIR/ub-ref-ptr.rs:47:1 | LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (0x539[noalloc] has no provenance) @@ -104,13 +104,13 @@ LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; } error[E0080]: evaluation of constant value failed - --> $DIR/ub-ref-ptr.rs:48:41 + --> $DIR/ub-ref-ptr.rs:50:41 | LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:52:1 + --> $DIR/ub-ref-ptr.rs:54:1 | LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a function pointer @@ -121,13 +121,13 @@ LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; } error[E0080]: evaluation of constant value failed - --> $DIR/ub-ref-ptr.rs:54:38 + --> $DIR/ub-ref-ptr.rs:56:38 | LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:57:1 + --> $DIR/ub-ref-ptr.rs:59:1 | LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0xd[noalloc], but expected a function pointer @@ -138,7 +138,7 @@ LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:59:1 + --> $DIR/ub-ref-ptr.rs:61:1 | LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC2, but expected a function pointer @@ -149,7 +149,7 @@ LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; } error[E0080]: evaluation of constant value failed - --> $DIR/ub-ref-ptr.rs:66:5 + --> $DIR/ub-ref-ptr.rs:68:5 | LL | ptr.read(); | ^^^^^^^^^^ accessing memory based on pointer with alignment 1, but alignment 4 is required diff --git a/tests/ui/consts/const-eval/ub-uninhabit.rs b/tests/ui/consts/const-eval/ub-uninhabit.rs index d0515a4e6f067..3a5e291d5df4a 100644 --- a/tests/ui/consts/const-eval/ub-uninhabit.rs +++ b/tests/ui/consts/const-eval/ub-uninhabit.rs @@ -1,6 +1,8 @@ // Strip out raw byte dumps to make comparison platform-independent: //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ dont-require-annotations: NOTE + #![feature(core_intrinsics)] #![feature(never_type)] @@ -18,15 +20,15 @@ union MaybeUninit { const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init }; //~^ ERROR evaluation of constant value failed -//~| constructing invalid value +//~| NOTE constructing invalid value const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; //~^ ERROR it is undefined behavior to use this value -//~| constructing invalid value +//~| NOTE constructing invalid value const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init }; //~^ ERROR evaluation of constant value failed -//~| constructing invalid value +//~| NOTE constructing invalid value const READ_NEVER: () = unsafe { @@ -34,7 +36,7 @@ const READ_NEVER: () = unsafe { let ptr = mem.as_ptr().cast::(); let _val = intrinsics::read_via_copy(ptr); //~^ ERROR evaluation of constant value failed - //~| constructing invalid value + //~| NOTE constructing invalid value }; diff --git a/tests/ui/consts/const-eval/ub-uninhabit.stderr b/tests/ui/consts/const-eval/ub-uninhabit.stderr index d26f4e03666e9..9d3f93279e5ac 100644 --- a/tests/ui/consts/const-eval/ub-uninhabit.stderr +++ b/tests/ui/consts/const-eval/ub-uninhabit.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/ub-uninhabit.rs:19:35 + --> $DIR/ub-uninhabit.rs:21:35 | LL | const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a value of uninhabited type `Bar` error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-uninhabit.rs:23:1 + --> $DIR/ub-uninhabit.rs:25:1 | LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to uninhabited type Bar @@ -16,13 +16,13 @@ LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; } error[E0080]: evaluation of constant value failed - --> $DIR/ub-uninhabit.rs:27:42 + --> $DIR/ub-uninhabit.rs:29:42 | LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered a value of uninhabited type `Bar` error[E0080]: evaluation of constant value failed - --> $DIR/ub-uninhabit.rs:35:16 + --> $DIR/ub-uninhabit.rs:37:16 | LL | let _val = intrinsics::read_via_copy(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a value of the never type `!` diff --git a/tests/ui/consts/const-eval/ub-wide-ptr.rs b/tests/ui/consts/const-eval/ub-wide-ptr.rs index a071a44272b4d..4b9bbb8066ded 100644 --- a/tests/ui/consts/const-eval/ub-wide-ptr.rs +++ b/tests/ui/consts/const-eval/ub-wide-ptr.rs @@ -1,5 +1,5 @@ // ignore-tidy-linelength -#![allow(unused)] +#![allow(unused, unnecessary_transmutes)] #![feature(ptr_metadata)] use std::{ptr, mem}; @@ -9,7 +9,7 @@ use std::{ptr, mem}; //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" //@ normalize-stderr: "offset \d+" -> "offset N" //@ normalize-stderr: "size \d+" -> "size N" - +//@ dont-require-annotations: NOTE /// A newtype wrapper to prevent MIR generation from inserting reborrows that would affect the error /// message. @@ -62,7 +62,7 @@ const SLICE_VALID: &[u8] = unsafe { mem::transmute((&42u8, 1usize)) }; // bad slice: length uninit const SLICE_LENGTH_UNINIT: &[u8] = unsafe { //~^ ERROR evaluation of constant value failed -//~| uninitialized +//~| NOTE uninitialized let uninit_len = MaybeUninit:: { uninit: () }; mem::transmute((42, uninit_len)) }; @@ -85,18 +85,18 @@ const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3)) }; // bad data *inside* the slice const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; //~^ ERROR it is undefined behavior to use this value -//~| constant +//~| NOTE constant // good MySliceBool const MYSLICE_GOOD: &MySliceBool = &MySlice(true, [false]); // bad: sized field is not okay const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); //~^ ERROR it is undefined behavior to use this value -//~| constant +//~| NOTE constant // bad: unsized part is not okay const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); //~^ ERROR it is undefined behavior to use this value -//~| constant +//~| NOTE constant // # raw slice const RAW_SLICE_VALID: *const [u8] = unsafe { mem::transmute((&42u8, 1usize)) }; // ok @@ -104,7 +104,7 @@ const RAW_SLICE_TOO_LONG: *const [u8] = unsafe { mem::transmute((&42u8, 999usize const RAW_SLICE_MUCH_TOO_LONG: *const [u8] = unsafe { mem::transmute((&42u8, usize::MAX)) }; // ok because raw const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { //~^ ERROR evaluation of constant value failed -//~| uninitialized +//~| NOTE uninitialized let uninit_len = MaybeUninit:: { uninit: () }; mem::transmute((42, uninit_len)) }; @@ -113,40 +113,40 @@ const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { // bad trait object const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; //~^ ERROR it is undefined behavior to use this value -//~| vtable +//~| NOTE vtable // bad trait object const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; //~^ ERROR it is undefined behavior to use this value -//~| vtable +//~| NOTE vtable // bad trait object const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; //~^ ERROR it is undefined behavior to use this value -//~| vtable +//~| NOTE vtable const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; //~^ ERROR it is undefined behavior to use this value -//~| vtable +//~| NOTE vtable const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; //~^ ERROR it is undefined behavior to use this value -//~| vtable +//~| NOTE vtable const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; //~^ ERROR it is undefined behavior to use this value -//~| vtable +//~| NOTE vtable const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; //~^ ERROR it is undefined behavior to use this value -//~| vtable +//~| NOTE vtable // bad data *inside* the trait object const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; //~^ ERROR it is undefined behavior to use this value -//~| expected a boolean +//~| NOTE expected a boolean // # raw trait object const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; //~^ ERROR it is undefined behavior to use this value -//~| null pointer +//~| NOTE null pointer const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; //~^ ERROR it is undefined behavior to use this value -//~| vtable +//~| NOTE vtable const RAW_TRAIT_OBJ_CONTENT_INVALID: *const dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) } as *const dyn Trait; // ok because raw // Officially blessed way to get the vtable const DYN_METADATA: ptr::DynMetadata = ptr::metadata::(ptr::null::()); @@ -155,12 +155,12 @@ const DYN_METADATA: ptr::DynMetadata = ptr::metadata::(ptr:: static mut RAW_TRAIT_OBJ_VTABLE_NULL_THROUGH_REF: *const dyn Trait = unsafe { mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) //~^^ ERROR it is undefined behavior to use this value - //~| null pointer + //~| NOTE null pointer }; static mut RAW_TRAIT_OBJ_VTABLE_INVALID_THROUGH_REF: *const dyn Trait = unsafe { mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) //~^^ ERROR it is undefined behavior to use this value - //~| vtable + //~| NOTE vtable }; fn main() {} diff --git a/tests/ui/consts/const-eval/ub-write-through-immutable.rs b/tests/ui/consts/const-eval/ub-write-through-immutable.rs index d3ae2d8188486..795ac602a1c65 100644 --- a/tests/ui/consts/const-eval/ub-write-through-immutable.rs +++ b/tests/ui/consts/const-eval/ub-write-through-immutable.rs @@ -8,14 +8,14 @@ const WRITE_AFTER_CAST: () = unsafe { let mut x = 0; let ptr = &x as *const i32 as *mut i32; *ptr = 0; //~ERROR: evaluation of constant value failed - //~| immutable + //~| NOTE immutable }; const WRITE_AFTER_TRANSMUTE: () = unsafe { let mut x = 0; let ptr: *mut i32 = mem::transmute(&x); *ptr = 0; //~ERROR: evaluation of constant value failed - //~| immutable + //~| NOTE immutable }; // it's okay when there is interior mutability; diff --git a/tests/ui/consts/const-eval/union-const-eval-field.rs b/tests/ui/consts/const-eval/union-const-eval-field.rs index c9799989ea835..55e9abe72e091 100644 --- a/tests/ui/consts/const-eval/union-const-eval-field.rs +++ b/tests/ui/consts/const-eval/union-const-eval-field.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + type Field1 = i32; type Field2 = f32; type Field3 = i64; @@ -25,7 +27,7 @@ const fn read_field2() -> Field2 { const fn read_field3() -> Field3 { const FIELD3: Field3 = unsafe { UNION.field3 }; //~^ ERROR evaluation of constant value failed - //~| uninitialized + //~| NOTE uninitialized FIELD3 } diff --git a/tests/ui/consts/const-eval/union-const-eval-field.stderr b/tests/ui/consts/const-eval/union-const-eval-field.stderr index b1de225ec580d..4fd6b80381c32 100644 --- a/tests/ui/consts/const-eval/union-const-eval-field.stderr +++ b/tests/ui/consts/const-eval/union-const-eval-field.stderr @@ -1,17 +1,17 @@ error[E0080]: evaluation of constant value failed - --> $DIR/union-const-eval-field.rs:26:37 + --> $DIR/union-const-eval-field.rs:28:37 | LL | const FIELD3: Field3 = unsafe { UNION.field3 }; | ^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory note: erroneous constant encountered - --> $DIR/union-const-eval-field.rs:29:5 + --> $DIR/union-const-eval-field.rs:31:5 | LL | FIELD3 | ^^^^^^ note: erroneous constant encountered - --> $DIR/union-const-eval-field.rs:29:5 + --> $DIR/union-const-eval-field.rs:31:5 | LL | FIELD3 | ^^^^^^ diff --git a/tests/ui/consts/const-eval/union-ice.rs b/tests/ui/consts/const-eval/union-ice.rs index 1db9470912d33..8ce5f6d89a8f3 100644 --- a/tests/ui/consts/const-eval/union-ice.rs +++ b/tests/ui/consts/const-eval/union-ice.rs @@ -13,13 +13,13 @@ const UNION: DummyUnion = DummyUnion { field1: 1065353216 }; const FIELD3: Field3 = unsafe { UNION.field3 }; //~^ ERROR evaluation of constant value failed -//~| uninitialized +//~| NOTE uninitialized const FIELD_PATH: Struct = Struct { a: 42, b: unsafe { UNION.field3 }, //~^ ERROR evaluation of constant value failed - //~| uninitialized + //~| NOTE uninitialized }; struct Struct { @@ -32,7 +32,7 @@ const FIELD_PATH2: Struct2 = Struct2 { 21, unsafe { UNION.field3 }, //~^ ERROR evaluation of constant value failed - //~| uninitialized + //~| NOTE uninitialized 23, 24, ], diff --git a/tests/ui/consts/const-eval/union-ub.32bit.stderr b/tests/ui/consts/const-eval/union-ub.32bit.stderr index e5c8f88be98a7..38ded4d65cfb6 100644 --- a/tests/ui/consts/const-eval/union-ub.32bit.stderr +++ b/tests/ui/consts/const-eval/union-ub.32bit.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/union-ub.rs:32:1 + --> $DIR/union-ub.rs:33:1 | LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x2a, but expected a boolean @@ -10,7 +10,7 @@ LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; } error[E0080]: evaluation of constant value failed - --> $DIR/union-ub.rs:34:36 + --> $DIR/union-ub.rs:35:36 | LL | const UNINIT_BOOL: bool = unsafe { DummyUnion { unit: () }.bool}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory diff --git a/tests/ui/consts/const-eval/union-ub.64bit.stderr b/tests/ui/consts/const-eval/union-ub.64bit.stderr index e5c8f88be98a7..38ded4d65cfb6 100644 --- a/tests/ui/consts/const-eval/union-ub.64bit.stderr +++ b/tests/ui/consts/const-eval/union-ub.64bit.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/union-ub.rs:32:1 + --> $DIR/union-ub.rs:33:1 | LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x2a, but expected a boolean @@ -10,7 +10,7 @@ LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; } error[E0080]: evaluation of constant value failed - --> $DIR/union-ub.rs:34:36 + --> $DIR/union-ub.rs:35:36 | LL | const UNINIT_BOOL: bool = unsafe { DummyUnion { unit: () }.bool}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory diff --git a/tests/ui/consts/const-eval/union-ub.rs b/tests/ui/consts/const-eval/union-ub.rs index 5eb4ad4b47f2c..7d8fd7446a0c5 100644 --- a/tests/ui/consts/const-eval/union-ub.rs +++ b/tests/ui/consts/const-eval/union-ub.rs @@ -1,4 +1,5 @@ //@ stderr-per-bitwidth +//@ dont-require-annotations: NOTE #[repr(C)] union DummyUnion { @@ -33,7 +34,7 @@ const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; //~^ ERROR it is undefined behavior to use this value const UNINIT_BOOL: bool = unsafe { DummyUnion { unit: () }.bool}; //~^ ERROR evaluation of constant value failed -//~| uninitialized +//~| NOTE uninitialized // The value is not valid for any union variant, but that's fine // unions are just a convenient way to transmute bits around diff --git a/tests/ui/consts/const-eval/union_promotion.rs b/tests/ui/consts/const-eval/union_promotion.rs index 18894c45fd822..3868b0b18b1ee 100644 --- a/tests/ui/consts/const-eval/union_promotion.rs +++ b/tests/ui/consts/const-eval/union_promotion.rs @@ -5,7 +5,7 @@ union Foo { } fn main() { - let x: &'static bool = &unsafe { //~ temporary value dropped while borrowed + let x: &'static bool = &unsafe { //~ ERROR temporary value dropped while borrowed Foo { a: &1 }.b == Foo { a: &2 }.b }; } diff --git a/tests/ui/consts/const-eval/unwind-abort.rs b/tests/ui/consts/const-eval/unwind-abort.rs index fee53f8528d60..b239dba023973 100644 --- a/tests/ui/consts/const-eval/unwind-abort.rs +++ b/tests/ui/consts/const-eval/unwind-abort.rs @@ -1,5 +1,7 @@ +//@ dont-require-annotations: NOTE + const extern "C" fn foo() { - panic!() //~ inside `foo` + panic!() //~ NOTE inside `foo` } const _: () = foo(); //~ ERROR evaluation of constant value failed diff --git a/tests/ui/consts/const-eval/unwind-abort.stderr b/tests/ui/consts/const-eval/unwind-abort.stderr index 96c0dd7c5e951..5ed7467e84b25 100644 --- a/tests/ui/consts/const-eval/unwind-abort.stderr +++ b/tests/ui/consts/const-eval/unwind-abort.stderr @@ -1,15 +1,14 @@ error[E0080]: evaluation of constant value failed - --> $DIR/unwind-abort.rs:5:15 + --> $DIR/unwind-abort.rs:7:15 | LL | const _: () = foo(); | ^^^^^ evaluation panicked: explicit panic | note: inside `foo` - --> $DIR/unwind-abort.rs:2:5 + --> $DIR/unwind-abort.rs:4:5 | LL | panic!() | ^^^^^^^^ the failure occurred here - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/validate_uninhabited_zsts.rs b/tests/ui/consts/const-eval/validate_uninhabited_zsts.rs index c4df93b6239ab..5f1d27b0de001 100644 --- a/tests/ui/consts/const-eval/validate_uninhabited_zsts.rs +++ b/tests/ui/consts/const-eval/validate_uninhabited_zsts.rs @@ -1,5 +1,7 @@ +//@ dont-require-annotations: NOTE + const fn foo() -> ! { - unsafe { std::mem::transmute(()) } //~ inside `foo` + unsafe { std::mem::transmute(()) } //~ NOTE inside `foo` } // Type defined in a submodule, so that it is not "visibly" diff --git a/tests/ui/consts/const-eval/validate_uninhabited_zsts.stderr b/tests/ui/consts/const-eval/validate_uninhabited_zsts.stderr index 29311fdb25a27..0407ae5c323a5 100644 --- a/tests/ui/consts/const-eval/validate_uninhabited_zsts.stderr +++ b/tests/ui/consts/const-eval/validate_uninhabited_zsts.stderr @@ -1,17 +1,17 @@ error[E0080]: evaluation of constant value failed - --> $DIR/validate_uninhabited_zsts.rs:15:33 + --> $DIR/validate_uninhabited_zsts.rs:17:33 | LL | const FOO: [empty::Empty; 3] = [foo(); 3]; | ^^^^^ constructing invalid value: encountered a value of the never type `!` | note: inside `foo` - --> $DIR/validate_uninhabited_zsts.rs:2:14 + --> $DIR/validate_uninhabited_zsts.rs:4:14 | LL | unsafe { std::mem::transmute(()) } | ^^^^^^^^^^^^^^^^^^^^^^^ the failure occurred here error[E0080]: evaluation of constant value failed - --> $DIR/validate_uninhabited_zsts.rs:18:42 + --> $DIR/validate_uninhabited_zsts.rs:20:42 | LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered a value of uninhabited type `Void` diff --git a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs b/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs index a77ee29382076..89a8d1429b4dd 100644 --- a/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs +++ b/tests/ui/consts/const-extern-fn/const-extern-fn-requires-unsafe.rs @@ -8,11 +8,11 @@ const unsafe extern "C-unwind" fn bar() -> usize { fn main() { let a: [u8; foo()]; - //~^ call to unsafe function `foo` is unsafe and requires unsafe function or block + //~^ ERROR call to unsafe function `foo` is unsafe and requires unsafe function or block foo(); //~^ ERROR call to unsafe function `foo` is unsafe and requires unsafe function or block let b: [u8; bar()]; - //~^ call to unsafe function `bar` is unsafe and requires unsafe function or block + //~^ ERROR call to unsafe function `bar` is unsafe and requires unsafe function or block bar(); //~^ ERROR call to unsafe function `bar` is unsafe and requires unsafe function or block } diff --git a/tests/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier-2.rs b/tests/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier-2.rs index 7ced24808bf6e..50728970be2cf 100644 --- a/tests/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier-2.rs +++ b/tests/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier-2.rs @@ -1,6 +1,6 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn container() { const unsafe WhereIsFerris Now() {} //~^ ERROR expected one of `extern` or `fn` diff --git a/tests/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.rs b/tests/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.rs index 6f575d055a29b..20e79ca200bbd 100644 --- a/tests/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.rs +++ b/tests/ui/consts/const-extern-fn/issue-68062-const-extern-fns-dont-need-fn-specifier.rs @@ -1,6 +1,6 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn container() { const extern "Rust" PUT_ANYTHING_YOU_WANT_HERE bug() -> usize { 1 } //~^ ERROR expected `fn` diff --git a/tests/ui/consts/const-integer-bool-ops.rs b/tests/ui/consts/const-integer-bool-ops.rs index 35915a7a606a4..fbbd51adfe885 100644 --- a/tests/ui/consts/const-integer-bool-ops.rs +++ b/tests/ui/consts/const-integer-bool-ops.rs @@ -1,67 +1,67 @@ const X: usize = 42 && 39; //~^ ERROR mismatched types -//~| expected `bool`, found integer +//~| NOTE expected `bool`, found integer //~| ERROR mismatched types -//~| expected `bool`, found integer +//~| NOTE expected `bool`, found integer //~| ERROR mismatched types -//~| expected `usize`, found `bool` +//~| NOTE expected `usize`, found `bool` const ARR: [i32; X] = [99; 34]; const X1: usize = 42 || 39; //~^ ERROR mismatched types -//~| expected `bool`, found integer +//~| NOTE expected `bool`, found integer //~| ERROR mismatched types -//~| expected `bool`, found integer +//~| NOTE expected `bool`, found integer //~| ERROR mismatched types -//~| expected `usize`, found `bool` +//~| NOTE expected `usize`, found `bool` const ARR1: [i32; X1] = [99; 47]; const X2: usize = -42 || -39; //~^ ERROR mismatched types -//~| expected `bool`, found integer +//~| NOTE expected `bool`, found integer //~| ERROR mismatched types -//~| expected `bool`, found integer +//~| NOTE expected `bool`, found integer //~| ERROR mismatched types -//~| expected `usize`, found `bool` +//~| NOTE expected `usize`, found `bool` const ARR2: [i32; X2] = [99; 18446744073709551607]; const X3: usize = -42 && -39; //~^ ERROR mismatched types -//~| expected `bool`, found integer +//~| NOTE expected `bool`, found integer //~| ERROR mismatched types -//~| expected `bool`, found integer +//~| NOTE expected `bool`, found integer //~| ERROR mismatched types -//~| expected `usize`, found `bool` +//~| NOTE expected `usize`, found `bool` const ARR3: [i32; X3] = [99; 6]; const Y: usize = 42.0 == 42.0; //~^ ERROR mismatched types -//~| expected `usize`, found `bool` +//~| NOTE expected `usize`, found `bool` const ARRR: [i32; Y] = [99; 1]; const Y1: usize = 42.0 >= 42.0; //~^ ERROR mismatched types -//~| expected `usize`, found `bool` +//~| NOTE expected `usize`, found `bool` const ARRR1: [i32; Y1] = [99; 1]; const Y2: usize = 42.0 <= 42.0; //~^ ERROR mismatched types -//~| expected `usize`, found `bool` +//~| NOTE expected `usize`, found `bool` const ARRR2: [i32; Y2] = [99; 1]; const Y3: usize = 42.0 > 42.0; //~^ ERROR mismatched types -//~| expected `usize`, found `bool` +//~| NOTE expected `usize`, found `bool` const ARRR3: [i32; Y3] = [99; 0]; const Y4: usize = 42.0 < 42.0; //~^ ERROR mismatched types -//~| expected `usize`, found `bool` +//~| NOTE expected `usize`, found `bool` const ARRR4: [i32; Y4] = [99; 0]; const Y5: usize = 42.0 != 42.0; //~^ ERROR mismatched types -//~| expected `usize`, found `bool` +//~| NOTE expected `usize`, found `bool` const ARRR5: [i32; Y5] = [99; 0]; fn main() { diff --git a/tests/ui/consts/const-len-underflow-separate-spans.next.stderr b/tests/ui/consts/const-len-underflow-separate-spans.next.stderr index bd2a81f514830..aacd74635e9fa 100644 --- a/tests/ui/consts/const-len-underflow-separate-spans.next.stderr +++ b/tests/ui/consts/const-len-underflow-separate-spans.next.stderr @@ -5,7 +5,7 @@ LL | const LEN: usize = ONE - TWO; | ^^^^^^^^^ attempt to compute `1_usize - 2_usize`, which would overflow note: erroneous constant encountered - --> $DIR/const-len-underflow-separate-spans.rs:14:17 + --> $DIR/const-len-underflow-separate-spans.rs:15:17 | LL | let a: [i8; LEN] = unimplemented!(); | ^^^ diff --git a/tests/ui/consts/const-len-underflow-separate-spans.old.stderr b/tests/ui/consts/const-len-underflow-separate-spans.old.stderr index bd2a81f514830..aacd74635e9fa 100644 --- a/tests/ui/consts/const-len-underflow-separate-spans.old.stderr +++ b/tests/ui/consts/const-len-underflow-separate-spans.old.stderr @@ -5,7 +5,7 @@ LL | const LEN: usize = ONE - TWO; | ^^^^^^^^^ attempt to compute `1_usize - 2_usize`, which would overflow note: erroneous constant encountered - --> $DIR/const-len-underflow-separate-spans.rs:14:17 + --> $DIR/const-len-underflow-separate-spans.rs:15:17 | LL | let a: [i8; LEN] = unimplemented!(); | ^^^ diff --git a/tests/ui/consts/const-len-underflow-separate-spans.rs b/tests/ui/consts/const-len-underflow-separate-spans.rs index 42314eed7aad6..14eb88b142713 100644 --- a/tests/ui/consts/const-len-underflow-separate-spans.rs +++ b/tests/ui/consts/const-len-underflow-separate-spans.rs @@ -9,8 +9,9 @@ const ONE: usize = 1; const TWO: usize = 2; const LEN: usize = ONE - TWO; //~^ ERROR constant +//~| NOTE attempt to compute `1_usize - 2_usize`, which would overflow fn main() { let a: [i8; LEN] = unimplemented!(); -//~^ constant +//~^ NOTE constant } diff --git a/tests/ui/consts/const-len-underflow-subspans.rs b/tests/ui/consts/const-len-underflow-subspans.rs index ed77e9078425a..5afb1bf89d091 100644 --- a/tests/ui/consts/const-len-underflow-subspans.rs +++ b/tests/ui/consts/const-len-underflow-subspans.rs @@ -7,5 +7,5 @@ const TWO: usize = 2; fn main() { let a: [i8; ONE - TWO] = unimplemented!(); //~^ ERROR evaluation of constant value failed - //~| attempt to compute `1_usize - 2_usize`, which would overflow + //~| NOTE attempt to compute `1_usize - 2_usize`, which would overflow } diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs b/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs index 283c1224e013d..bc534be6832e5 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs @@ -1,6 +1,7 @@ //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "( 0x[0-9a-f][0-9a-f] │)? ([0-9a-f][0-9a-f] |__ |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> " HEX_DUMP" //@ normalize-stderr: "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP" +//@ dont-require-annotations: NOTE use std::cell::UnsafeCell; use std::mem; @@ -25,7 +26,7 @@ const B4: Option<&mut i32> = helper(&mut 42); //~ ERROR temporary value dropped // Not ok, since it points to read-only memory. const IMMUT_MUT_REF: &mut u16 = unsafe { mem::transmute(&13) }; //~^ ERROR undefined behavior to use this value -//~| pointing to read-only memory +//~| NOTE pointing to read-only memory // Ok, because no references to mutable data exist here, since the `{}` moves // its value and then takes a reference to that. diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr index 4f50ae3231290..1f49f08c40156 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr @@ -1,11 +1,11 @@ error[E0764]: mutable references are not allowed in the final value of constants - --> $DIR/mut_ref_in_final.rs:14:21 + --> $DIR/mut_ref_in_final.rs:15:21 | LL | const B: *mut i32 = &mut 4; | ^^^^^^ error[E0716]: temporary value dropped while borrowed - --> $DIR/mut_ref_in_final.rs:20:40 + --> $DIR/mut_ref_in_final.rs:21:40 | LL | const B3: Option<&mut i32> = Some(&mut 42); | ----------^^- @@ -15,7 +15,7 @@ LL | const B3: Option<&mut i32> = Some(&mut 42); | using this value as a constant requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed - --> $DIR/mut_ref_in_final.rs:23:42 + --> $DIR/mut_ref_in_final.rs:24:42 | LL | const B4: Option<&mut i32> = helper(&mut 42); | ------------^^- @@ -25,7 +25,7 @@ LL | const B4: Option<&mut i32> = helper(&mut 42); | using this value as a constant requires that borrow lasts for `'static` error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final.rs:26:1 + --> $DIR/mut_ref_in_final.rs:27:1 | LL | const IMMUT_MUT_REF: &mut u16 = unsafe { mem::transmute(&13) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory @@ -36,7 +36,7 @@ LL | const IMMUT_MUT_REF: &mut u16 = unsafe { mem::transmute(&13) }; } error[E0716]: temporary value dropped while borrowed - --> $DIR/mut_ref_in_final.rs:50:65 + --> $DIR/mut_ref_in_final.rs:51:65 | LL | const FOO: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | -------------------------------^^-- @@ -46,7 +46,7 @@ LL | const FOO: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | using this value as a constant requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed - --> $DIR/mut_ref_in_final.rs:53:67 + --> $DIR/mut_ref_in_final.rs:54:67 | LL | static FOO2: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | -------------------------------^^-- @@ -56,7 +56,7 @@ LL | static FOO2: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | using this value as a static requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed - --> $DIR/mut_ref_in_final.rs:56:71 + --> $DIR/mut_ref_in_final.rs:57:71 | LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | -------------------------------^^-- @@ -66,25 +66,25 @@ LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | using this value as a static requires that borrow lasts for `'static` error[E0764]: mutable references are not allowed in the final value of statics - --> $DIR/mut_ref_in_final.rs:69:53 + --> $DIR/mut_ref_in_final.rs:70:53 | LL | static RAW_MUT_CAST_S: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; | ^^^^^^^ error[E0764]: mutable references are not allowed in the final value of statics - --> $DIR/mut_ref_in_final.rs:71:54 + --> $DIR/mut_ref_in_final.rs:72:54 | LL | static RAW_MUT_COERCE_S: SyncPtr = SyncPtr { x: &mut 0 }; | ^^^^^^ error[E0764]: mutable references are not allowed in the final value of constants - --> $DIR/mut_ref_in_final.rs:73:52 + --> $DIR/mut_ref_in_final.rs:74:52 | LL | const RAW_MUT_CAST_C: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; | ^^^^^^^ error[E0764]: mutable references are not allowed in the final value of constants - --> $DIR/mut_ref_in_final.rs:75:53 + --> $DIR/mut_ref_in_final.rs:76:53 | LL | const RAW_MUT_COERCE_C: SyncPtr = SyncPtr { x: &mut 0 }; | ^^^^^^ diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs index ac903fca20aed..a58c4437e1572 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs @@ -1,6 +1,7 @@ //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "( 0x[0-9a-f][0-9a-f] │)? ([0-9a-f][0-9a-f] |__ |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> " HEX_DUMP" //@ normalize-stderr: "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP" +//@ dont-require-annotations: NOTE use std::sync::Mutex; @@ -16,16 +17,16 @@ const fn helper() -> Option<&'static mut i32> { unsafe { Some(&mut *std::ptr::addr_of_mut!(BUFFER)) } } const MUT: Option<&mut i32> = helper(); //~ ERROR it is undefined behavior to use this value -//~^ encountered reference to mutable +//~^ NOTE encountered reference to mutable const fn helper_int2ptr() -> Option<&'static mut i32> { unsafe { // Undefined behaviour (integer as pointer), who doesn't love tests like this. Some(&mut *(42 as *mut i32)) } } const INT2PTR: Option<&mut i32> = helper_int2ptr(); //~ ERROR it is undefined behavior to use this value -//~^ encountered a dangling reference +//~^ NOTE encountered a dangling reference static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); //~ ERROR it is undefined behavior to use this value -//~^ encountered a dangling reference +//~^ NOTE encountered a dangling reference const fn helper_dangling() -> Option<&'static mut i32> { unsafe { // Undefined behaviour (dangling pointer), who doesn't love tests like this. diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr index aebac56f8c5cf..4ea6fa62475e8 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final_dynamic_check.rs:18:1 + --> $DIR/mut_ref_in_final_dynamic_check.rs:19:1 | LL | const MUT: Option<&mut i32> = helper(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered reference to mutable memory in `const` @@ -10,7 +10,7 @@ LL | const MUT: Option<&mut i32> = helper(); } error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final_dynamic_check.rs:25:1 + --> $DIR/mut_ref_in_final_dynamic_check.rs:26:1 | LL | const INT2PTR: Option<&mut i32> = helper_int2ptr(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a dangling reference (0x2a[noalloc] has no provenance) @@ -21,7 +21,7 @@ LL | const INT2PTR: Option<&mut i32> = helper_int2ptr(); } error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final_dynamic_check.rs:27:1 + --> $DIR/mut_ref_in_final_dynamic_check.rs:28:1 | LL | static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a dangling reference (0x2a[noalloc] has no provenance) @@ -32,7 +32,7 @@ LL | static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); } error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final_dynamic_check.rs:34:1 + --> $DIR/mut_ref_in_final_dynamic_check.rs:35:1 | LL | const DANGLING: Option<&mut i32> = helper_dangling(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a dangling reference (use-after-free) @@ -43,7 +43,7 @@ LL | const DANGLING: Option<&mut i32> = helper_dangling(); } error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final_dynamic_check.rs:35:1 + --> $DIR/mut_ref_in_final_dynamic_check.rs:36:1 | LL | static DANGLING_STATIC: Option<&mut i32> = helper_dangling(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a dangling reference (use-after-free) diff --git a/tests/ui/consts/const-pattern-irrefutable.rs b/tests/ui/consts/const-pattern-irrefutable.rs index 759d2e8b2edeb..e131fd27d532c 100644 --- a/tests/ui/consts/const-pattern-irrefutable.rs +++ b/tests/ui/consts/const-pattern-irrefutable.rs @@ -1,15 +1,17 @@ +//@ dont-require-annotations: NOTE + mod foo { pub const b: u8 = 2; - //~^ missing patterns are not covered because `b` is interpreted as a constant pattern, not a new variable + //~^ NOTE missing patterns are not covered because `b` is interpreted as a constant pattern, not a new variable pub const d: (u8, u8) = (2, 1); - //~^ missing patterns are not covered because `d` is interpreted as a constant pattern, not a new variable + //~^ NOTE missing patterns are not covered because `d` is interpreted as a constant pattern, not a new variable } use foo::b as c; use foo::d; const a: u8 = 2; -//~^ missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable +//~^ NOTE missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable #[derive(PartialEq)] struct S { @@ -23,19 +25,19 @@ const e: S = S { fn main() { let a = 4; //~^ ERROR refutable pattern in local binding - //~| patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered + //~| NOTE patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered //~| HELP introduce a variable instead let c = 4; //~^ ERROR refutable pattern in local binding - //~| patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered + //~| NOTE patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered //~| HELP introduce a variable instead let d = (4, 4); //~^ ERROR refutable pattern in local binding - //~| patterns `(0_u8..=1_u8, _)` and `(3_u8..=u8::MAX, _)` not covered + //~| NOTE patterns `(0_u8..=1_u8, _)` and `(3_u8..=u8::MAX, _)` not covered //~| HELP introduce a variable instead let e = S { //~^ ERROR refutable pattern in local binding - //~| pattern `S { foo: 1_u8..=u8::MAX }` not covered + //~| NOTE pattern `S { foo: 1_u8..=u8::MAX }` not covered //~| HELP introduce a variable instead foo: 1, }; diff --git a/tests/ui/consts/const-pattern-irrefutable.stderr b/tests/ui/consts/const-pattern-irrefutable.stderr index 06bd01bff79f0..f52ebc313339c 100644 --- a/tests/ui/consts/const-pattern-irrefutable.stderr +++ b/tests/ui/consts/const-pattern-irrefutable.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in local binding - --> $DIR/const-pattern-irrefutable.rs:24:9 + --> $DIR/const-pattern-irrefutable.rs:26:9 | LL | const a: u8 = 2; | ----------- missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable @@ -16,7 +16,7 @@ LL | let a_var = 4; | ++++ error[E0005]: refutable pattern in local binding - --> $DIR/const-pattern-irrefutable.rs:28:9 + --> $DIR/const-pattern-irrefutable.rs:30:9 | LL | pub const b: u8 = 2; | --------------- missing patterns are not covered because `b` is interpreted as a constant pattern, not a new variable @@ -34,7 +34,7 @@ LL + let b_var = 4; | error[E0005]: refutable pattern in local binding - --> $DIR/const-pattern-irrefutable.rs:32:9 + --> $DIR/const-pattern-irrefutable.rs:34:9 | LL | pub const d: (u8, u8) = (2, 1); | --------------------- missing patterns are not covered because `d` is interpreted as a constant pattern, not a new variable @@ -51,7 +51,7 @@ LL | let d_var = (4, 4); | ++++ error[E0005]: refutable pattern in local binding - --> $DIR/const-pattern-irrefutable.rs:36:9 + --> $DIR/const-pattern-irrefutable.rs:38:9 | LL | const e: S = S { | ---------- missing patterns are not covered because `e` is interpreted as a constant pattern, not a new variable @@ -62,7 +62,7 @@ LL | let e = S { = 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/ch19-02-refutability.html note: `S` defined here - --> $DIR/const-pattern-irrefutable.rs:15:8 + --> $DIR/const-pattern-irrefutable.rs:17:8 | LL | struct S { | ^ diff --git a/tests/ui/consts/const-promoted-opaque.atomic.stderr b/tests/ui/consts/const-promoted-opaque.atomic.stderr index b9d5cbf801a80..9c0c969d586ce 100644 --- a/tests/ui/consts/const-promoted-opaque.atomic.stderr +++ b/tests/ui/consts/const-promoted-opaque.atomic.stderr @@ -1,5 +1,5 @@ -error[E0493]: destructor of `helper::Foo` cannot be evaluated at compile-time - --> $DIR/const-promoted-opaque.rs:28:26 +error[E0493]: destructor of `Foo` cannot be evaluated at compile-time + --> $DIR/const-promoted-opaque.rs:32:26 | LL | let _: &'static _ = &FOO; | ^^^ the destructor for this type cannot be evaluated in constants @@ -8,13 +8,13 @@ LL | }; | - value is dropped here error[E0492]: constants cannot refer to interior mutable data - --> $DIR/const-promoted-opaque.rs:32:19 + --> $DIR/const-promoted-opaque.rs:36:19 | LL | const BAZ: &Foo = &FOO; | ^^^^ this borrow of an interior mutable value may end up in the final value error[E0716]: temporary value dropped while borrowed - --> $DIR/const-promoted-opaque.rs:36:26 + --> $DIR/const-promoted-opaque.rs:40:26 | LL | let _: &'static _ = &FOO; | ---------- ^^^ creates a temporary value which is freed while still in use diff --git a/tests/ui/consts/const-promoted-opaque.rs b/tests/ui/consts/const-promoted-opaque.rs index bb33e92778aa9..188dacd100343 100644 --- a/tests/ui/consts/const-promoted-opaque.rs +++ b/tests/ui/consts/const-promoted-opaque.rs @@ -8,25 +8,29 @@ //! hidden type of the opaque type. Thus we ended up relying on the //! result of our analysis to compute the result of our analysis. -//@[unit] check-pass +pub type Foo = impl Sized; -mod helper { - pub type Foo = impl Sized; +#[cfg(string)] +#[define_opaque(Foo)] +const fn foo() -> Foo { + String::new() +} - #[cfg(string)] - pub const FOO: Foo = String::new(); +#[cfg(atomic)] +#[define_opaque(Foo)] +const fn foo() -> Foo { + std::sync::atomic::AtomicU8::new(42) +} - #[cfg(atomic)] - pub const FOO: Foo = std::sync::atomic::AtomicU8::new(42); +#[cfg(unit)] +#[define_opaque(Foo)] +const fn foo() -> Foo {} - #[cfg(unit)] - pub const FOO: Foo = (); -} -use helper::*; +const FOO: Foo = foo(); const BAR: () = { let _: &'static _ = &FOO; - //[string,atomic]~^ ERROR: destructor of `helper::Foo` cannot be evaluated at compile-time + //[string,atomic,unit]~^ ERROR: destructor of `Foo` cannot be evaluated at compile-time }; const BAZ: &Foo = &FOO; @@ -34,5 +38,5 @@ const BAZ: &Foo = &FOO; fn main() { let _: &'static _ = &FOO; - //[string,atomic]~^ ERROR: temporary value dropped while borrowed + //[string,atomic,unit]~^ ERROR: temporary value dropped while borrowed } diff --git a/tests/ui/consts/const-promoted-opaque.string.stderr b/tests/ui/consts/const-promoted-opaque.string.stderr index 33e5f426448ca..847a466f21c5f 100644 --- a/tests/ui/consts/const-promoted-opaque.string.stderr +++ b/tests/ui/consts/const-promoted-opaque.string.stderr @@ -1,5 +1,5 @@ -error[E0493]: destructor of `helper::Foo` cannot be evaluated at compile-time - --> $DIR/const-promoted-opaque.rs:28:26 +error[E0493]: destructor of `Foo` cannot be evaluated at compile-time + --> $DIR/const-promoted-opaque.rs:32:26 | LL | let _: &'static _ = &FOO; | ^^^ the destructor for this type cannot be evaluated in constants @@ -8,7 +8,7 @@ LL | }; | - value is dropped here error[E0716]: temporary value dropped while borrowed - --> $DIR/const-promoted-opaque.rs:36:26 + --> $DIR/const-promoted-opaque.rs:40:26 | LL | let _: &'static _ = &FOO; | ---------- ^^^ creates a temporary value which is freed while still in use diff --git a/tests/ui/consts/const-promoted-opaque.unit.stderr b/tests/ui/consts/const-promoted-opaque.unit.stderr new file mode 100644 index 0000000000000..847a466f21c5f --- /dev/null +++ b/tests/ui/consts/const-promoted-opaque.unit.stderr @@ -0,0 +1,24 @@ +error[E0493]: destructor of `Foo` cannot be evaluated at compile-time + --> $DIR/const-promoted-opaque.rs:32:26 + | +LL | let _: &'static _ = &FOO; + | ^^^ the destructor for this type cannot be evaluated in constants +LL | +LL | }; + | - value is dropped here + +error[E0716]: temporary value dropped while borrowed + --> $DIR/const-promoted-opaque.rs:40:26 + | +LL | let _: &'static _ = &FOO; + | ---------- ^^^ creates a temporary value which is freed while still in use + | | + | type annotation requires that borrow lasts for `'static` +LL | +LL | } + | - temporary value is freed at the end of this statement + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0493, E0716. +For more information about an error, try `rustc --explain E0493`. diff --git a/tests/ui/consts/const-ptr-is-null.stderr b/tests/ui/consts/const-ptr-is-null.stderr index ff2db14a2f513..1a2be3b3cb185 100644 --- a/tests/ui/consts/const-ptr-is-null.stderr +++ b/tests/ui/consts/const-ptr-is-null.stderr @@ -8,7 +8,6 @@ note: inside `std::ptr::const_ptr::::is_null` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `std::ptr::const_ptr::::is_null::compiletime` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - = note: this error originates in the macro `$crate::intrinsics::const_eval_select` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-size_of-cycle.rs b/tests/ui/consts/const-size_of-cycle.rs index cfb2294c44599..f45d96d43f80d 100644 --- a/tests/ui/consts/const-size_of-cycle.rs +++ b/tests/ui/consts/const-size_of-cycle.rs @@ -1,7 +1,6 @@ -//@ error-pattern: cycle detected - struct Foo { bytes: [u8; std::mem::size_of::()] + //~^ ERROR cycle detected when evaluating type-level constant } fn main() {} diff --git a/tests/ui/consts/const-size_of-cycle.stderr b/tests/ui/consts/const-size_of-cycle.stderr index cd0ea55642545..bf17d76a092bf 100644 --- a/tests/ui/consts/const-size_of-cycle.stderr +++ b/tests/ui/consts/const-size_of-cycle.stderr @@ -1,11 +1,11 @@ error[E0391]: cycle detected when evaluating type-level constant - --> $DIR/const-size_of-cycle.rs:4:17 + --> $DIR/const-size_of-cycle.rs:2:17 | LL | bytes: [u8; std::mem::size_of::()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`... - --> $DIR/const-size_of-cycle.rs:4:17 + --> $DIR/const-size_of-cycle.rs:2:17 | LL | bytes: [u8; std::mem::size_of::()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -14,7 +14,7 @@ LL | bytes: [u8; std::mem::size_of::()] = note: ...which requires normalizing `[u8; std::mem::size_of::()]`... = note: ...which again requires evaluating type-level constant, completing the cycle note: cycle used when checking that `Foo` is well-formed - --> $DIR/const-size_of-cycle.rs:3:1 + --> $DIR/const-size_of-cycle.rs:1:1 | LL | struct Foo { | ^^^^^^^^^^ diff --git a/tests/ui/consts/const-slice-array-deref.stderr b/tests/ui/consts/const-slice-array-deref.stderr index 346685380cc6c..b1d069280885d 100644 --- a/tests/ui/consts/const-slice-array-deref.stderr +++ b/tests/ui/consts/const-slice-array-deref.stderr @@ -5,6 +5,7 @@ LL | const ONE: [u16] = [1]; | ^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[u16]` + = note: statics and constants must have a statically known size error[E0308]: mismatched types --> $DIR/const-slice-array-deref.rs:1:20 diff --git a/tests/ui/consts/const-slice-oob.rs b/tests/ui/consts/const-slice-oob.rs index 429b978213214..09202df72c0f0 100644 --- a/tests/ui/consts/const-slice-oob.rs +++ b/tests/ui/consts/const-slice-oob.rs @@ -1,6 +1,6 @@ const FOO: &'static[u32] = &[1, 2, 3]; const BAR: u32 = FOO[5]; -//~^ index out of bounds: the length is 3 but the index is 5 +//~^ NOTE index out of bounds: the length is 3 but the index is 5 //~| ERROR evaluation of constant value failed fn main() { diff --git a/tests/ui/consts/const-suggest-feature.rs b/tests/ui/consts/const-suggest-feature.rs index 0c94036897610..dbb166dd6c5d2 100644 --- a/tests/ui/consts/const-suggest-feature.rs +++ b/tests/ui/consts/const-suggest-feature.rs @@ -1,4 +1,4 @@ -//@compile-flags: --edition 2018 +//@ edition: 2018 use std::cell::Cell; const WRITE: () = unsafe { diff --git a/tests/ui/consts/const-tup-index-span.rs b/tests/ui/consts/const-tup-index-span.rs index e77d392e69402..4cb7143b4351f 100644 --- a/tests/ui/consts/const-tup-index-span.rs +++ b/tests/ui/consts/const-tup-index-span.rs @@ -2,7 +2,8 @@ const TUP: (usize,) = 5usize << 64; //~^ ERROR mismatched types -//~| expected `(usize,)`, found `usize` +//~| NOTE expected `(usize,)`, found `usize` +//~| NOTE expected tuple `(usize,)` const ARR: [i32; TUP.0] = []; fn main() { diff --git a/tests/ui/consts/const-unsized.stderr b/tests/ui/consts/const-unsized.stderr index 7931d7adafdb1..cee364b33f7a0 100644 --- a/tests/ui/consts/const-unsized.stderr +++ b/tests/ui/consts/const-unsized.stderr @@ -5,6 +5,7 @@ LL | const CONST_0: dyn Debug + Sync = *(&0 as &(dyn Debug + Sync)); | ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Debug + Sync + 'static)` + = note: statics and constants must have a statically known size error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/const-unsized.rs:7:18 @@ -13,6 +14,7 @@ LL | const CONST_FOO: str = *"foo"; | ^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` + = note: statics and constants must have a statically known size error[E0277]: the size for values of type `(dyn Debug + Sync + 'static)` cannot be known at compilation time --> $DIR/const-unsized.rs:11:18 @@ -21,6 +23,7 @@ LL | static STATIC_1: dyn Debug + Sync = *(&1 as &(dyn Debug + Sync)); | ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Debug + Sync + 'static)` + = note: statics and constants must have a statically known size error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/const-unsized.rs:15:20 @@ -29,6 +32,7 @@ LL | static STATIC_BAR: str = *"bar"; | ^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` + = note: statics and constants must have a statically known size error[E0507]: cannot move out of a shared reference --> $DIR/const-unsized.rs:3:35 @@ -54,18 +58,18 @@ error[E0507]: cannot move out of a shared reference LL | static STATIC_BAR: str = *"bar"; | ^^^^^^ move occurs because value has type `str`, which does not implement the `Copy` trait -error[E0161]: cannot move a value of type `str` - --> $DIR/const-unsized.rs:20:48 - | -LL | println!("{:?} {:?} {:?} {:?}", &CONST_0, &CONST_FOO, &STATIC_1, &STATIC_BAR); - | ^^^^^^^^^ the size of `str` cannot be statically determined - error[E0161]: cannot move a value of type `dyn Debug + Sync` --> $DIR/const-unsized.rs:20:38 | LL | println!("{:?} {:?} {:?} {:?}", &CONST_0, &CONST_FOO, &STATIC_1, &STATIC_BAR); | ^^^^^^^ the size of `dyn Debug + Sync` cannot be statically determined +error[E0161]: cannot move a value of type `str` + --> $DIR/const-unsized.rs:20:48 + | +LL | println!("{:?} {:?} {:?} {:?}", &CONST_0, &CONST_FOO, &STATIC_1, &STATIC_BAR); + | ^^^^^^^^^ the size of `str` cannot be statically determined + error: aborting due to 10 previous errors Some errors have detailed explanations: E0161, E0277, E0507. diff --git a/tests/ui/consts/const_cmp_type_id.stderr b/tests/ui/consts/const_cmp_type_id.stderr index 2e9a8024eaeba..62f8d42c0e688 100644 --- a/tests/ui/consts/const_cmp_type_id.stderr +++ b/tests/ui/consts/const_cmp_type_id.stderr @@ -27,7 +27,6 @@ LL | let _a = TypeId::of::() < TypeId::of::(); note: impl defined here, but it is not `const` --> $SRC_DIR/core/src/any.rs:LL:COL = note: calls in constants are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/consts/const_in_pattern/arrays-and-slices.rs b/tests/ui/consts/const_in_pattern/arrays-and-slices.rs new file mode 100644 index 0000000000000..bb38490206b2c --- /dev/null +++ b/tests/ui/consts/const_in_pattern/arrays-and-slices.rs @@ -0,0 +1,53 @@ +//! Tests that arrays and slices in constants aren't interchangeable when used as patterns. + +#[derive(PartialEq, Eq)] +struct SomeStruct(T); + +const BSTR_SIZED: &'static [u8; 3] = b"012"; +const BSTR_UNSIZED: &'static [u8] = BSTR_SIZED; +const STRUCT_SIZED: &'static SomeStruct<[u8; 3]> = &SomeStruct(*BSTR_SIZED); +const STRUCT_UNSIZED: &'static SomeStruct<[u8]> = STRUCT_SIZED; + +fn type_mismatches() { + // Test that array consts can't be used where a slice pattern is expected. This helps ensure + // that `const_to_pat` won't produce irrefutable `thir::PatKind::Array` patterns when matching + // on slices, which would result in missing length checks. + // See also `tests/ui/match/pattern-deref-miscompile.rs`, which tests that byte string literal + // patterns check slices' length appropriately when matching on slices. + match BSTR_UNSIZED { + BSTR_SIZED => {} + //~^ ERROR: mismatched types + _ => {} + } + match STRUCT_UNSIZED { + STRUCT_SIZED => {} + //~^ ERROR: mismatched types + _ => {} + } + + // Test that slice consts can't be used where an array pattern is expected. + match BSTR_UNSIZED { + BSTR_SIZED => {} + //~^ ERROR: mismatched types + _ => {} + } + // If the types matched here, this would still error, since unsized structs aren't permitted in + // constant patterns. See the `invalid_patterns` test below. + match STRUCT_UNSIZED { + STRUCT_SIZED => {} + //~^ ERROR: mismatched types + _ => {} + } +} + +fn invalid_patterns() { + // Test that unsized structs containing slices can't be used as patterns. + // See `tests/ui/consts/issue-87046.rs` for an example with `str`. + match STRUCT_UNSIZED { + STRUCT_UNSIZED => {} + //~^ ERROR: cannot use unsized non-slice type `SomeStruct<[u8]>` in constant patterns + _ => {} + } +} + +fn main() {} diff --git a/tests/ui/consts/const_in_pattern/arrays-and-slices.stderr b/tests/ui/consts/const_in_pattern/arrays-and-slices.stderr new file mode 100644 index 0000000000000..412caf60f7d85 --- /dev/null +++ b/tests/ui/consts/const_in_pattern/arrays-and-slices.stderr @@ -0,0 +1,84 @@ +error[E0308]: mismatched types + --> $DIR/arrays-and-slices.rs:18:9 + | +LL | const BSTR_SIZED: &'static [u8; 3] = b"012"; + | ---------------------------------- constant defined here +... +LL | match BSTR_UNSIZED { + | ------------ this expression has type `&[u8]` +LL | BSTR_SIZED => {} + | ^^^^^^^^^^ + | | + | expected `&[u8]`, found `&[u8; 3]` + | `BSTR_SIZED` is interpreted as a constant, not a new binding + | help: introduce a new binding instead: `other_bstr_sized` + | + = note: expected reference `&[u8]` + found reference `&'static [u8; 3]` + +error[E0308]: mismatched types + --> $DIR/arrays-and-slices.rs:23:9 + | +LL | const STRUCT_SIZED: &'static SomeStruct<[u8; 3]> = &SomeStruct(*BSTR_SIZED); + | ------------------------------------------------ constant defined here +... +LL | match STRUCT_UNSIZED { + | -------------- this expression has type `&SomeStruct<[u8]>` +LL | STRUCT_SIZED => {} + | ^^^^^^^^^^^^ + | | + | expected `&SomeStruct<[u8]>`, found `&SomeStruct<[u8; 3]>` + | `STRUCT_SIZED` is interpreted as a constant, not a new binding + | help: introduce a new binding instead: `other_struct_sized` + | + = note: expected reference `&SomeStruct<[u8]>` + found reference `&'static SomeStruct<[u8; 3]>` + +error[E0308]: mismatched types + --> $DIR/arrays-and-slices.rs:30:9 + | +LL | const BSTR_SIZED: &'static [u8; 3] = b"012"; + | ---------------------------------- constant defined here +... +LL | match BSTR_UNSIZED { + | ------------ this expression has type `&[u8]` +LL | BSTR_SIZED => {} + | ^^^^^^^^^^ + | | + | expected `&[u8]`, found `&[u8; 3]` + | `BSTR_SIZED` is interpreted as a constant, not a new binding + | help: introduce a new binding instead: `other_bstr_sized` + | + = note: expected reference `&[u8]` + found reference `&'static [u8; 3]` + +error[E0308]: mismatched types + --> $DIR/arrays-and-slices.rs:37:9 + | +LL | const STRUCT_SIZED: &'static SomeStruct<[u8; 3]> = &SomeStruct(*BSTR_SIZED); + | ------------------------------------------------ constant defined here +... +LL | match STRUCT_UNSIZED { + | -------------- this expression has type `&SomeStruct<[u8]>` +LL | STRUCT_SIZED => {} + | ^^^^^^^^^^^^ + | | + | expected `&SomeStruct<[u8]>`, found `&SomeStruct<[u8; 3]>` + | `STRUCT_SIZED` is interpreted as a constant, not a new binding + | help: introduce a new binding instead: `other_struct_sized` + | + = note: expected reference `&SomeStruct<[u8]>` + found reference `&'static SomeStruct<[u8; 3]>` + +error: cannot use unsized non-slice type `SomeStruct<[u8]>` in constant patterns + --> $DIR/arrays-and-slices.rs:47:9 + | +LL | const STRUCT_UNSIZED: &'static SomeStruct<[u8]> = STRUCT_SIZED; + | ----------------------------------------------- constant defined here +... +LL | STRUCT_UNSIZED => {} + | ^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/consts/const_let_assign2.rs b/tests/ui/consts/const_let_assign2.rs index e8ebba7b20887..5349ae31bb4df 100644 --- a/tests/ui/consts/const_let_assign2.rs +++ b/tests/ui/consts/const_let_assign2.rs @@ -16,7 +16,7 @@ static mut BB: AA = AA::new(); fn main() { let ptr = unsafe { &mut BB }; - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] + //~^ WARN mutable reference to mutable static [static_mut_refs] for a in ptr.data.iter() { println!("{}", a); } diff --git a/tests/ui/consts/const_let_assign2.stderr b/tests/ui/consts/const_let_assign2.stderr index 0d76f142d174d..1bb560437b60e 100644 --- a/tests/ui/consts/const_let_assign2.stderr +++ b/tests/ui/consts/const_let_assign2.stderr @@ -1,4 +1,4 @@ -warning: creating a mutable reference to mutable static is discouraged +warning: creating a mutable reference to mutable static --> $DIR/const_let_assign2.rs:18:24 | LL | let ptr = unsafe { &mut BB }; diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.stderr b/tests/ui/consts/const_refs_to_static-ice-121413.stderr index 8665d9b685223..3980a7e9b93ba 100644 --- a/tests/ui/consts/const_refs_to_static-ice-121413.stderr +++ b/tests/ui/consts/const_refs_to_static-ice-121413.stderr @@ -30,6 +30,7 @@ LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Sync + 'static)` + = note: statics and constants must have a statically known size error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/consts/const_refs_to_static_fail.rs b/tests/ui/consts/const_refs_to_static_fail.rs index 596ed50af3852..e1f2262ba29af 100644 --- a/tests/ui/consts/const_refs_to_static_fail.rs +++ b/tests/ui/consts/const_refs_to_static_fail.rs @@ -1,5 +1,6 @@ //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ dont-require-annotations: NOTE #![feature(sync_unsafe_cell)] @@ -9,14 +10,14 @@ static S: SyncUnsafeCell = SyncUnsafeCell::new(0); static mut S_MUT: i32 = 0; const C1: &SyncUnsafeCell = &S; //~ERROR undefined behavior -//~| encountered reference to mutable memory +//~| NOTE encountered reference to mutable memory const C1_READ: () = unsafe { assert!(*C1.get() == 0); }; const C2: *const i32 = unsafe { std::ptr::addr_of!(S_MUT) }; const C2_READ: () = unsafe { assert!(*C2 == 0); //~ERROR evaluation of constant value failed - //~^ constant accesses mutable global memory + //~^ NOTE constant accesses mutable global memory }; fn main() { diff --git a/tests/ui/consts/const_refs_to_static_fail.stderr b/tests/ui/consts/const_refs_to_static_fail.stderr index 297561dbcf137..245806da5c62b 100644 --- a/tests/ui/consts/const_refs_to_static_fail.stderr +++ b/tests/ui/consts/const_refs_to_static_fail.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refs_to_static_fail.rs:11:1 + --> $DIR/const_refs_to_static_fail.rs:12:1 | LL | const C1: &SyncUnsafeCell = &S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` @@ -10,13 +10,13 @@ LL | const C1: &SyncUnsafeCell = &S; } note: erroneous constant encountered - --> $DIR/const_refs_to_static_fail.rs:14:14 + --> $DIR/const_refs_to_static_fail.rs:15:14 | LL | assert!(*C1.get() == 0); | ^^ error[E0080]: evaluation of constant value failed - --> $DIR/const_refs_to_static_fail.rs:18:13 + --> $DIR/const_refs_to_static_fail.rs:19:13 | LL | assert!(*C2 == 0); | ^^^ constant accesses mutable global memory diff --git a/tests/ui/consts/const_refs_to_static_fail_invalid.rs b/tests/ui/consts/const_refs_to_static_fail_invalid.rs index 3383a208345da..f6ccfbfc52fe9 100644 --- a/tests/ui/consts/const_refs_to_static_fail_invalid.rs +++ b/tests/ui/consts/const_refs_to_static_fail_invalid.rs @@ -1,5 +1,7 @@ //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ dont-require-annotations: NOTE + #![allow(static_mut_refs)] fn invalid() { @@ -7,7 +9,7 @@ fn invalid() { const C: &bool = unsafe { std::mem::transmute(&S) }; //~^ERROR: undefined behavior - //~| expected a boolean + //~| NOTE expected a boolean // This must be rejected here (or earlier), since it's not a valid `&bool`. match &true { @@ -23,7 +25,7 @@ fn extern_() { const C: &i8 = unsafe { &S }; //~^ERROR: undefined behavior - //~| `extern` static + //~| NOTE `extern` static // This must be rejected here (or earlier), since the pattern cannot be read. match &0 { @@ -37,7 +39,7 @@ fn mutable() { const C: &i32 = unsafe { &S_MUT }; //~^ERROR: undefined behavior - //~| encountered reference to mutable memory + //~| NOTE encountered reference to mutable memory // This *must not build*, the constant we are matching against // could change its value! diff --git a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr index c9d5cb60bf779..e0086e07af75d 100644 --- a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr +++ b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refs_to_static_fail_invalid.rs:8:5 + --> $DIR/const_refs_to_static_fail_invalid.rs:10:5 | LL | const C: &bool = unsafe { std::mem::transmute(&S) }; | ^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x0a, but expected a boolean @@ -10,7 +10,7 @@ LL | const C: &bool = unsafe { std::mem::transmute(&S) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refs_to_static_fail_invalid.rs:24:5 + --> $DIR/const_refs_to_static_fail_invalid.rs:26:5 | LL | const C: &i8 = unsafe { &S }; | ^^^^^^^^^^^^ constructing invalid value: encountered reference to `extern` static in `const` @@ -21,7 +21,7 @@ LL | const C: &i8 = unsafe { &S }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refs_to_static_fail_invalid.rs:38:5 + --> $DIR/const_refs_to_static_fail_invalid.rs:40:5 | LL | const C: &i32 = unsafe { &S_MUT }; | ^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` diff --git a/tests/ui/consts/control-flow/assert.stderr b/tests/ui/consts/control-flow/assert.stderr index fc378f57fa43e..3fa98b74bf64c 100644 --- a/tests/ui/consts/control-flow/assert.stderr +++ b/tests/ui/consts/control-flow/assert.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of constant value failed | LL | const _: () = assert!(false); | ^^^^^^^^^^^^^^ evaluation panicked: assertion failed: false - | - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/copy-intrinsic.rs b/tests/ui/consts/copy-intrinsic.rs index 08fbcc107e7ec..d0242f285444a 100644 --- a/tests/ui/consts/copy-intrinsic.rs +++ b/tests/ui/consts/copy-intrinsic.rs @@ -1,22 +1,8 @@ -#![stable(feature = "dummy", since = "1.0.0")] - // ignore-tidy-linelength -#![feature(intrinsics, staged_api, rustc_attrs)] -use std::mem; - -#[stable(feature = "dummy", since = "1.0.0")] -#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.63.0")] -#[rustc_intrinsic] -const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { - unimplemented!() -} +#![feature(core_intrinsics)] -#[stable(feature = "dummy", since = "1.0.0")] -#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.63.0")] -#[rustc_intrinsic] -const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { - unimplemented!() -} +use std::mem; +use std::intrinsics::{copy, copy_nonoverlapping}; const COPY_ZERO: () = unsafe { // Since we are not copying anything, this should be allowed. @@ -32,7 +18,7 @@ const COPY_OOB_1: () = unsafe { copy_nonoverlapping(0x100 as *const i32, dangle, 0); // Non-zero-sized copy is not. copy_nonoverlapping(0x100 as *const i32, dangle, 1); //~ ERROR evaluation of constant value failed [E0080] - //~| got 0x100[noalloc] which is a dangling pointer + //~| NOTE which is a dangling pointer }; const COPY_OOB_2: () = unsafe { let x = 0i32; @@ -41,20 +27,20 @@ const COPY_OOB_2: () = unsafe { copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); // Non-zero-sized copy is not. copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); //~ ERROR evaluation of constant value failed [E0080] - //~| +0x28 which is at or beyond the end of the allocation + //~| NOTE is at or beyond the end of the allocation of size 4 bytes }; const COPY_SIZE_OVERFLOW: () = unsafe { let x = 0; let mut y = 0; copy(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); //~ ERROR evaluation of constant value failed [E0080] - //~| overflow computing total size of `copy` + //~| NOTE overflow computing total size of `copy` }; const COPY_NONOVERLAPPING_SIZE_OVERFLOW: () = unsafe { let x = 0; let mut y = 0; - copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); //~ evaluation of constant value failed [E0080] - //~| overflow computing total size of `copy_nonoverlapping` + copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); //~ ERROR evaluation of constant value failed [E0080] + //~| NOTE overflow computing total size of `copy_nonoverlapping` }; fn main() { diff --git a/tests/ui/consts/copy-intrinsic.stderr b/tests/ui/consts/copy-intrinsic.stderr index 41af3a2cd2dc2..8d586428e56f8 100644 --- a/tests/ui/consts/copy-intrinsic.stderr +++ b/tests/ui/consts/copy-intrinsic.stderr @@ -1,23 +1,23 @@ error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:34:5 + --> $DIR/copy-intrinsic.rs:20:5 | LL | copy_nonoverlapping(0x100 as *const i32, dangle, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got 0x100[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got 0x100[noalloc] which is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:43:5 + --> $DIR/copy-intrinsic.rs:29:5 | LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC0+0x28 which is at or beyond the end of the allocation of size 4 bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: attempting to access 4 bytes, but got ALLOC0+0x28 which is at or beyond the end of the allocation of size 4 bytes error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:50:5 + --> $DIR/copy-intrinsic.rs:36:5 | LL | copy(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy` error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:56:5 + --> $DIR/copy-intrinsic.rs:42:5 | LL | copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy_nonoverlapping` diff --git a/tests/ui/consts/dont-ctfe-unsized-initializer.rs b/tests/ui/consts/dont-ctfe-unsized-initializer.rs new file mode 100644 index 0000000000000..cca38b760dcc8 --- /dev/null +++ b/tests/ui/consts/dont-ctfe-unsized-initializer.rs @@ -0,0 +1,7 @@ +static S: str = todo!(); +//~^ ERROR the size for values of type `str` cannot be known at compilation time + +const C: str = todo!(); +//~^ ERROR the size for values of type `str` cannot be known at compilation time + +fn main() {} diff --git a/tests/ui/consts/dont-ctfe-unsized-initializer.stderr b/tests/ui/consts/dont-ctfe-unsized-initializer.stderr new file mode 100644 index 0000000000000..e69790fc1823c --- /dev/null +++ b/tests/ui/consts/dont-ctfe-unsized-initializer.stderr @@ -0,0 +1,21 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/dont-ctfe-unsized-initializer.rs:1:11 + | +LL | static S: str = todo!(); + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: statics and constants must have a statically known size + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/dont-ctfe-unsized-initializer.rs:4:10 + | +LL | const C: str = todo!(); + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: statics and constants must have a statically known size + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/consts/drop_zst.rs b/tests/ui/consts/drop_zst.rs index 40c66043f9fda..daf8f1b0343d1 100644 --- a/tests/ui/consts/drop_zst.rs +++ b/tests/ui/consts/drop_zst.rs @@ -11,7 +11,7 @@ impl Drop for S { } const fn foo() { - let s = S; //~ destructor + let s = S; //~ ERROR destructor } fn main() {} diff --git a/tests/ui/consts/effect_param.stderr b/tests/ui/consts/effect_param.stderr index c63be8035f30f..a20092dbf047a 100644 --- a/tests/ui/consts/effect_param.stderr +++ b/tests/ui/consts/effect_param.stderr @@ -1,32 +1,32 @@ error[E0107]: method takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/effect_param.rs:11:9 + --> $DIR/effect_param.rs:4:9 | -LL | i8::checked_sub::(42, 43); - | ^^^^^^^^^^^--------- help: remove the unnecessary generics +LL | i8::checked_sub::(42, 43); + | ^^^^^^^^^^^-------- help: remove the unnecessary generics | | | expected 0 generic arguments error[E0107]: method takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/effect_param.rs:13:9 + --> $DIR/effect_param.rs:6:9 | -LL | i8::checked_sub::(42, 43); - | ^^^^^^^^^^^-------- help: remove the unnecessary generics +LL | i8::checked_sub::(42, 43); + | ^^^^^^^^^^^--------- help: remove the unnecessary generics | | | expected 0 generic arguments error[E0107]: method takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/effect_param.rs:4:9 + --> $DIR/effect_param.rs:11:9 | -LL | i8::checked_sub::(42, 43); - | ^^^^^^^^^^^-------- help: remove the unnecessary generics +LL | i8::checked_sub::(42, 43); + | ^^^^^^^^^^^--------- help: remove the unnecessary generics | | | expected 0 generic arguments error[E0107]: method takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/effect_param.rs:6:9 + --> $DIR/effect_param.rs:13:9 | -LL | i8::checked_sub::(42, 43); - | ^^^^^^^^^^^--------- help: remove the unnecessary generics +LL | i8::checked_sub::(42, 43); + | ^^^^^^^^^^^-------- help: remove the unnecessary generics | | | expected 0 generic arguments diff --git a/tests/ui/consts/eval-enum.rs b/tests/ui/consts/eval-enum.rs index 551f10e66e35b..fa9ad6736e297 100644 --- a/tests/ui/consts/eval-enum.rs +++ b/tests/ui/consts/eval-enum.rs @@ -1,9 +1,9 @@ enum Test { DivZero = 1/0, - //~^ attempt to divide `1_isize` by zero + //~^ NOTE attempt to divide `1_isize` by zero //~| ERROR evaluation of constant value failed RemZero = 1%0, - //~^ attempt to calculate the remainder of `1_isize` with a divisor of zero + //~^ NOTE attempt to calculate the remainder of `1_isize` with a divisor of zero //~| ERROR evaluation of constant value failed } diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs index d2b157e03e7cb..c3bd8301d5ce4 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs @@ -2,6 +2,7 @@ //@ [no_flag] check-pass //@ [with_flag] compile-flags: -Zextra-const-ub-checks #![feature(never_type)] +#![allow(unnecessary_transmutes)] use std::mem::transmute; use std::ptr::addr_of; @@ -28,32 +29,32 @@ enum UninhDiscriminant { const INVALID_BOOL: () = unsafe { let _x: bool = transmute(3u8); //[with_flag]~^ ERROR: evaluation of constant value failed - //[with_flag]~| invalid value + //[with_flag]~| NOTE invalid value }; const INVALID_PTR_IN_INT: () = unsafe { let _x: usize = transmute(&3u8); //[with_flag]~^ ERROR: evaluation of constant value failed - //[with_flag]~| invalid value + //[with_flag]~| NOTE invalid value }; const INVALID_PTR_IN_ENUM: () = unsafe { let _x: PtrSizedEnum = transmute(&3u8); //[with_flag]~^ ERROR: evaluation of constant value failed - //[with_flag]~| invalid value + //[with_flag]~| NOTE invalid value }; const INVALID_SLICE_TO_USIZE_TRANSMUTE: () = unsafe { let x: &[u8] = &[0; 32]; let _x: (usize, usize) = transmute(x); //[with_flag]~^ ERROR: evaluation of constant value failed - //[with_flag]~| invalid value + //[with_flag]~| NOTE invalid value }; const UNALIGNED_PTR: () = unsafe { let _x: &u32 = transmute(&[0u8; 4]); //[with_flag]~^ ERROR: evaluation of constant value failed - //[with_flag]~| invalid value + //[with_flag]~| NOTE invalid value }; const UNINHABITED_VARIANT: () = unsafe { @@ -61,7 +62,7 @@ const UNINHABITED_VARIANT: () = unsafe { // Not using transmute, we want to hit the ImmTy code path. let v = *addr_of!(data).cast::(); //[with_flag]~^ ERROR: evaluation of constant value failed - //[with_flag]~| invalid value + //[with_flag]~| NOTE invalid value }; const PARTIAL_POINTER: () = unsafe { @@ -81,7 +82,7 @@ const PARTIAL_POINTER: () = unsafe { let mem = Align { p: mem, align: 0 }; let _val = *(&mem as *const Align as *const [*const u8; 2]); //[with_flag]~^ ERROR: evaluation of constant value failed - //[with_flag]~| invalid value + //[with_flag]~| NOTE invalid value }; // Regression tests for an ICE (related to ). @@ -96,7 +97,7 @@ const OVERSIZED_REF: () = { unsafe { let slice: *const [u8] = transmute((1usize, usize::MAX)); let _val = &*slice; //[with_flag]~^ ERROR: evaluation of constant value failed - //[with_flag]~| slice is bigger than largest supported object + //[with_flag]~| NOTE slice is bigger than largest supported object } }; fn main() {} diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr index 0100aafb6b7c6..ea3b0e70b8285 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:29:20 + --> $DIR/detect-extra-ub.rs:30:20 | LL | let _x: bool = transmute(3u8); | ^^^^^^^^^^^^^^ constructing invalid value: encountered 0x03, but expected a boolean error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:35:21 + --> $DIR/detect-extra-ub.rs:36:21 | LL | let _x: usize = transmute(&3u8); | ^^^^^^^^^^^^^^^ constructing invalid value: encountered a pointer, but expected an integer @@ -14,7 +14,7 @@ LL | let _x: usize = transmute(&3u8); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:41:28 + --> $DIR/detect-extra-ub.rs:42:28 | LL | let _x: PtrSizedEnum = transmute(&3u8); | ^^^^^^^^^^^^^^^ constructing invalid value at .: encountered a pointer, but expected an integer @@ -23,7 +23,7 @@ LL | let _x: PtrSizedEnum = transmute(&3u8); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:48:30 + --> $DIR/detect-extra-ub.rs:49:30 | LL | let _x: (usize, usize) = transmute(x); | ^^^^^^^^^^^^ constructing invalid value at .0: encountered a pointer, but expected an integer @@ -32,19 +32,19 @@ LL | let _x: (usize, usize) = transmute(x); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:54:20 + --> $DIR/detect-extra-ub.rs:55:20 | LL | let _x: &u32 = transmute(&[0u8; 4]); | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 4 byte alignment but found 1) error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:62:13 + --> $DIR/detect-extra-ub.rs:63:13 | LL | let v = *addr_of!(data).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:82:16 + --> $DIR/detect-extra-ub.rs:83:16 | LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered a partial pointer or a mix of pointers @@ -53,7 +53,7 @@ LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:97:16 + --> $DIR/detect-extra-ub.rs:98:16 | LL | let _val = &*slice; | ^^^^^^^ constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object diff --git a/tests/ui/consts/invalid-inline-const-in-match-arm.rs b/tests/ui/consts/invalid-inline-const-in-match-arm.rs deleted file mode 100644 index 4fe4b0d33c886..0000000000000 --- a/tests/ui/consts/invalid-inline-const-in-match-arm.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![feature(inline_const_pat)] - -fn main() { - match () { - const { (|| {})() } => {} - //~^ ERROR cannot call non-const closure in constants - //~| ERROR could not evaluate constant pattern - } -} diff --git a/tests/ui/consts/invalid-inline-const-in-match-arm.stderr b/tests/ui/consts/invalid-inline-const-in-match-arm.stderr deleted file mode 100644 index b22f99f40d351..0000000000000 --- a/tests/ui/consts/invalid-inline-const-in-match-arm.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0015]: cannot call non-const closure in constants - --> $DIR/invalid-inline-const-in-match-arm.rs:5:17 - | -LL | const { (|| {})() } => {} - | ^^^^^^^^^ - | - = note: closures need an RFC before allowed to be called in constants - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error: could not evaluate constant pattern - --> $DIR/invalid-inline-const-in-match-arm.rs:5:9 - | -LL | const { (|| {})() } => {} - | ^^^^^^^^^^^^^^^^^^^ could not evaluate constant - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/consts/issue-17718-const-bad-values.rs b/tests/ui/consts/issue-17718-const-bad-values.rs index c4de7b61f07d7..286972365d5a1 100644 --- a/tests/ui/consts/issue-17718-const-bad-values.rs +++ b/tests/ui/consts/issue-17718-const-bad-values.rs @@ -1,5 +1,6 @@ //@ normalize-stderr: "\(size: \d+, align: \d+\)" -> "(size: $$PTR, align: $$PTR)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ dont-require-annotations: NOTE #![allow(static_mut_refs)] @@ -9,6 +10,6 @@ const C1: &'static mut [usize] = &mut []; static mut S: i32 = 3; const C2: &'static mut i32 = unsafe { &mut S }; //~^ ERROR: it is undefined behavior to use this value -//~| reference to mutable memory +//~| NOTE reference to mutable memory fn main() {} diff --git a/tests/ui/consts/issue-17718-const-bad-values.stderr b/tests/ui/consts/issue-17718-const-bad-values.stderr index 364fa7be5aae4..102491e90bac1 100644 --- a/tests/ui/consts/issue-17718-const-bad-values.stderr +++ b/tests/ui/consts/issue-17718-const-bad-values.stderr @@ -1,11 +1,11 @@ error[E0764]: mutable references are not allowed in the final value of constants - --> $DIR/issue-17718-const-bad-values.rs:6:34 + --> $DIR/issue-17718-const-bad-values.rs:7:34 | LL | const C1: &'static mut [usize] = &mut []; | ^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/issue-17718-const-bad-values.rs:10:1 + --> $DIR/issue-17718-const-bad-values.rs:11:1 | LL | const C2: &'static mut i32 = unsafe { &mut S }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` diff --git a/tests/ui/consts/issue-19244-2.rs b/tests/ui/consts/issue-19244-2.rs index c9a68b05c5bae..87f8c1581a750 100644 --- a/tests/ui/consts/issue-19244-2.rs +++ b/tests/ui/consts/issue-19244-2.rs @@ -3,5 +3,5 @@ const STRUCT: MyStruct = MyStruct { field: 42 }; fn main() { let a: [isize; STRUCT.nonexistent_field]; - //~^ no field `nonexistent_field` on type `MyStruct` + //~^ ERROR no field `nonexistent_field` on type `MyStruct` } diff --git a/tests/ui/consts/issue-32829.stderr b/tests/ui/consts/issue-32829.stderr index 0cbc73cfaa399..542dce1915189 100644 --- a/tests/ui/consts/issue-32829.stderr +++ b/tests/ui/consts/issue-32829.stderr @@ -3,8 +3,6 @@ error[E0080]: could not evaluate static initializer | LL | static S : u64 = { { panic!("foo"); 0 } }; | ^^^^^^^^^^^^^ evaluation panicked: foo - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/issue-39974.rs b/tests/ui/consts/issue-39974.rs index 9cb180014b86b..adc65d9be0d04 100644 --- a/tests/ui/consts/issue-39974.rs +++ b/tests/ui/consts/issue-39974.rs @@ -1,10 +1,11 @@ const LENGTH: f64 = 2; //~^ ERROR mismatched types +//~| NOTE expected `f64`, found integer struct Thing { f: [[f64; 2]; LENGTH], //~^ ERROR mismatched types - //~| expected `usize`, found `f64` + //~| NOTE expected `usize`, found `f64` } fn main() { diff --git a/tests/ui/consts/issue-39974.stderr b/tests/ui/consts/issue-39974.stderr index a371ea5709e1b..1c15debb1199c 100644 --- a/tests/ui/consts/issue-39974.stderr +++ b/tests/ui/consts/issue-39974.stderr @@ -1,3 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/issue-39974.rs:6:19 + | +LL | f: [[f64; 2]; LENGTH], + | ^^^^^^ expected `usize`, found `f64` + error[E0308]: mismatched types --> $DIR/issue-39974.rs:1:21 | @@ -9,12 +15,6 @@ help: use a float literal LL | const LENGTH: f64 = 2.0; | ++ -error[E0308]: mismatched types - --> $DIR/issue-39974.rs:5:19 - | -LL | f: [[f64; 2]; LENGTH], - | ^^^^^^ expected `usize`, found `f64` - error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/consts/issue-66693-panic-in-array-len.stderr b/tests/ui/consts/issue-66693-panic-in-array-len.stderr index 9cf5ad126f330..fdef515b6d41a 100644 --- a/tests/ui/consts/issue-66693-panic-in-array-len.stderr +++ b/tests/ui/consts/issue-66693-panic-in-array-len.stderr @@ -3,16 +3,12 @@ error: argument to `panic!()` in a const context must have type `&str` | LL | let _ = [0i32; panic!(2f32)]; | ^^^^^^^^^^^^ - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/issue-66693-panic-in-array-len.rs:10:21 | LL | let _ = [false; panic!()]; | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/consts/issue-66693.stderr b/tests/ui/consts/issue-66693.stderr index 46f30a8cbab64..ea657ec34bd6b 100644 --- a/tests/ui/consts/issue-66693.stderr +++ b/tests/ui/consts/issue-66693.stderr @@ -3,40 +3,30 @@ error: argument to `panic!()` in a const context must have type `&str` | LL | const _: () = panic!(1); | ^^^^^^^^^ - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: argument to `panic!()` in a const context must have type `&str` --> $DIR/issue-66693.rs:7:19 | LL | static _FOO: () = panic!(true); | ^^^^^^^^^^^^ - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $DIR/issue-66693.rs:16:15 | LL | const _: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: could not evaluate static initializer --> $DIR/issue-66693.rs:18:19 | LL | static _BAR: () = panic!("panic in static"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: panic in static - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: argument to `panic!()` in a const context must have type `&str` --> $DIR/issue-66693.rs:11:5 | LL | panic!(&1); | ^^^^^^^^^^ - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 5 previous errors diff --git a/tests/ui/consts/issue-69532.rs b/tests/ui/consts/issue-69532.rs index 285cfe7213bae..43ab1d6cca743 100644 --- a/tests/ui/consts/issue-69532.rs +++ b/tests/ui/consts/issue-69532.rs @@ -1,8 +1,8 @@ //@ run-pass const fn make_nans() -> (f64, f64, f32, f32) { - let nan1: f64 = unsafe { std::mem::transmute(0x7FF0_0001_0000_0001u64) }; - let nan2: f64 = unsafe { std::mem::transmute(0x7FF0_0000_0000_0001u64) }; + let nan1 = f64::from_bits(0x7FF0_0001_0000_0001); + let nan2 = f64::from_bits(0x7FF0_0000_0000_0001); let nan1_32 = nan1 as f32; let nan2_32 = nan2 as f32; diff --git a/tests/ui/consts/issue-76064.stderr b/tests/ui/consts/issue-76064.stderr index 55059220388af..786d4213f1e56 100644 --- a/tests/ui/consts/issue-76064.stderr +++ b/tests/ui/consts/issue-76064.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of constant value failed | LL | struct Bug([u8; panic!("panic")]); | ^^^^^^^^^^^^^^^ evaluation panicked: panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/issue-94675.rs b/tests/ui/consts/issue-94675.rs index e1c6861c5103b..87c8b04452bac 100644 --- a/tests/ui/consts/issue-94675.rs +++ b/tests/ui/consts/issue-94675.rs @@ -1,4 +1,4 @@ -#![feature(const_trait_impl, const_vec_string_slice)] +#![feature(const_trait_impl)] struct Foo<'a> { bar: &'a mut Vec, diff --git a/tests/ui/consts/large_const_alloc.rs b/tests/ui/consts/large_const_alloc.rs index 14edc1bb69610..3573a018630f6 100644 --- a/tests/ui/consts/large_const_alloc.rs +++ b/tests/ui/consts/large_const_alloc.rs @@ -2,6 +2,9 @@ // on 32bit and 16bit platforms it is plausible that the maximum allocation size will succeed // FIXME (#135952) In some cases on AArch64 Linux the diagnostic does not trigger //@ ignore-aarch64-unknown-linux-gnu +// AIX will allow the allocation to go through, and get SIGKILL when zero initializing +// the overcommitted page. +//@ ignore-aix const FOO: () = { // 128 TiB, unlikely anyone has that much RAM diff --git a/tests/ui/consts/large_const_alloc.stderr b/tests/ui/consts/large_const_alloc.stderr index fa7d5977a9567..f3f3de7af63ac 100644 --- a/tests/ui/consts/large_const_alloc.stderr +++ b/tests/ui/consts/large_const_alloc.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/large_const_alloc.rs:8:13 + --> $DIR/large_const_alloc.rs:11:13 | LL | let x = [0_u8; (1 << 47) - 1]; | ^^^^^^^^^^^^^^^^^^^^^ tried to allocate more memory than available to compiler error[E0080]: could not evaluate static initializer - --> $DIR/large_const_alloc.rs:13:13 + --> $DIR/large_const_alloc.rs:16:13 | LL | let x = [0_u8; (1 << 47) - 1]; | ^^^^^^^^^^^^^^^^^^^^^ tried to allocate more memory than available to compiler diff --git a/tests/ui/consts/min_const_fn/min_const_fn.rs b/tests/ui/consts/min_const_fn/min_const_fn.rs index e6d9d184e041a..38ca10858b199 100644 --- a/tests/ui/consts/min_const_fn/min_const_fn.rs +++ b/tests/ui/consts/min_const_fn/min_const_fn.rs @@ -34,13 +34,13 @@ const fn foo35(a: bool, b: bool) -> bool { a ^ b } struct Foo(T); impl Foo { const fn new(t: T) -> Self { Foo(t) } - const fn into_inner(self) -> T { self.0 } //~ destructor of + const fn into_inner(self) -> T { self.0 } //~ ERROR destructor of const fn get(&self) -> &T { &self.0 } const fn get_mut(&mut self) -> &mut T { &mut self.0 } } impl<'a, T> Foo { const fn new_lt(t: T) -> Self { Foo(t) } - const fn into_inner_lt(self) -> T { self.0 } //~ destructor of + const fn into_inner_lt(self) -> T { self.0 } //~ ERROR destructor of const fn get_lt(&self) -> &T { &self.0 } const fn get_mut_lt(&mut self) -> &mut T { &mut self.0 } } diff --git a/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.rs b/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.rs index d6f07994e8200..8f2bcd82c73fe 100644 --- a/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.rs +++ b/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![unstable(feature = "humans", reason = "who ever let humans program computers, we're apparently really bad at it", diff --git a/tests/ui/consts/miri_unleashed/abi-mismatch.rs b/tests/ui/consts/miri_unleashed/abi-mismatch.rs index ea640ae78d551..6a46079b39b6a 100644 --- a/tests/ui/consts/miri_unleashed/abi-mismatch.rs +++ b/tests/ui/consts/miri_unleashed/abi-mismatch.rs @@ -10,6 +10,8 @@ const fn call_rust_fn(my_fn: extern "Rust" fn()) { static VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); //~^ ERROR could not evaluate static initializer -//~| NOTE calling a function with calling convention C using calling convention Rust +//~| NOTE calling a function with calling convention "C" using calling convention "Rust" fn main() {} + +//~? WARN skipping const checks diff --git a/tests/ui/consts/miri_unleashed/abi-mismatch.stderr b/tests/ui/consts/miri_unleashed/abi-mismatch.stderr index 88623b134b0bf..7d1fdcce52614 100644 --- a/tests/ui/consts/miri_unleashed/abi-mismatch.stderr +++ b/tests/ui/consts/miri_unleashed/abi-mismatch.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/abi-mismatch.rs:11:18 | LL | static VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention C using calling convention Rust + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention "C" using calling convention "Rust" | note: inside `call_rust_fn` --> $DIR/abi-mismatch.rs:7:5 diff --git a/tests/ui/consts/miri_unleashed/assoc_const.rs b/tests/ui/consts/miri_unleashed/assoc_const.rs index 96b47ff4e5e24..812207ee80900 100644 --- a/tests/ui/consts/miri_unleashed/assoc_const.rs +++ b/tests/ui/consts/miri_unleashed/assoc_const.rs @@ -28,3 +28,5 @@ fn main() { // this test only causes errors due to the line below, so post-monomorphization let y = , String>>::F; } + +//~? WARN skipping const checks diff --git a/tests/ui/consts/miri_unleashed/assoc_const.stderr b/tests/ui/consts/miri_unleashed/assoc_const.stderr index f259765f6e57a..758f1a2533990 100644 --- a/tests/ui/consts/miri_unleashed/assoc_const.stderr +++ b/tests/ui/consts/miri_unleashed/assoc_const.stderr @@ -1,12 +1,12 @@ -error[E0080]: evaluation of `std::ptr::drop_in_place::> - shim(Some(Vec))` failed +error[E0080]: evaluation of `drop_in_place::> - shim(Some(Vec))` failed --> $DIR/assoc_const.rs:12:31 | LL | const F: u32 = (U::X, 42).1; | ^ calling non-const function ` as Drop>::drop` | -note: inside `std::ptr::drop_in_place::<(Vec, u32)> - shim(Some((Vec, u32)))` +note: inside `drop_in_place::<(Vec, u32)> - shim(Some((Vec, u32)))` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `std::ptr::drop_in_place::> - shim(Some(Vec))` +note: inside `drop_in_place::> - shim(Some(Vec))` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL note: erroneous constant encountered diff --git a/tests/ui/consts/miri_unleashed/assoc_const_2.rs b/tests/ui/consts/miri_unleashed/assoc_const_2.rs index 5490c0963915b..1d8ed2065a4fe 100644 --- a/tests/ui/consts/miri_unleashed/assoc_const_2.rs +++ b/tests/ui/consts/miri_unleashed/assoc_const_2.rs @@ -1,4 +1,5 @@ //@ build-fail +//@ dont-require-annotations: NOTE // a test demonstrating that const qualification cannot prevent monomorphization time errors @@ -24,5 +25,5 @@ impl Bar for String {} fn main() { let x = <() as Bar<()>>::F; // this test only causes errors due to the line below, so post-monomorphization - let y = >::F; //~ constant + let y = >::F; //~ NOTE constant } diff --git a/tests/ui/consts/miri_unleashed/assoc_const_2.stderr b/tests/ui/consts/miri_unleashed/assoc_const_2.stderr index e923d95b7550c..5503f8e56f09e 100644 --- a/tests/ui/consts/miri_unleashed/assoc_const_2.stderr +++ b/tests/ui/consts/miri_unleashed/assoc_const_2.stderr @@ -1,17 +1,17 @@ error[E0080]: evaluation of `>::F` failed - --> $DIR/assoc_const_2.rs:10:20 + --> $DIR/assoc_const_2.rs:11:20 | LL | const F: u32 = 100 / U::X; | ^^^^^^^^^^ attempt to divide `100_u32` by zero note: erroneous constant encountered - --> $DIR/assoc_const_2.rs:27:13 + --> $DIR/assoc_const_2.rs:28:13 | LL | let y = >::F; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: erroneous constant encountered - --> $DIR/assoc_const_2.rs:27:13 + --> $DIR/assoc_const_2.rs:28:13 | LL | let y = >::F; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/miri_unleashed/box.rs b/tests/ui/consts/miri_unleashed/box.rs index 89df4526b0770..1539083f09c24 100644 --- a/tests/ui/consts/miri_unleashed/box.rs +++ b/tests/ui/consts/miri_unleashed/box.rs @@ -9,3 +9,5 @@ static TEST_BAD: &mut i32 = { //~^ ERROR could not evaluate static initializer //~| NOTE calling non-const function `Box::::new` }; + +//~? WARN skipping const checks diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static.rs b/tests/ui/consts/miri_unleashed/const_refers_to_static.rs index a6691fa2a2f11..c66aaec8c56cf 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static.rs +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static.rs @@ -1,6 +1,7 @@ //@ compile-flags: -Zunleash-the-miri-inside-of-you //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ dont-require-annotations: NOTE use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; @@ -20,7 +21,7 @@ const READ_MUT: u32 = unsafe { MUTABLE }; //~ERROR evaluation of constant value // Evaluating this does not read anything mutable, but validation does, so this should error. const REF_INTERIOR_MUT: &usize = { //~ ERROR undefined behavior - //~| encountered reference to mutable memory + //~| NOTE encountered reference to mutable memory static FOO: AtomicUsize = AtomicUsize::new(0); unsafe { &*(&FOO as *const _ as *const usize) } }; @@ -31,3 +32,5 @@ const REF_IMMUT: &u8 = &MY_STATIC; const READ_IMMUT: u8 = *REF_IMMUT; fn main() {} + +//~? WARN skipping const checks diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr index f8e0606fbd778..f647107094d9d 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr @@ -1,23 +1,23 @@ error[E0080]: evaluation of constant value failed - --> $DIR/const_refers_to_static.rs:10:5 + --> $DIR/const_refers_to_static.rs:11:5 | LL | FOO.fetch_add(1, Ordering::Relaxed) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling non-const function `AtomicUsize::fetch_add` error[E0080]: evaluation of constant value failed - --> $DIR/const_refers_to_static.rs:15:14 + --> $DIR/const_refers_to_static.rs:16:14 | LL | unsafe { *(&FOO as *const _ as *const usize) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses mutable global memory error[E0080]: evaluation of constant value failed - --> $DIR/const_refers_to_static.rs:19:32 + --> $DIR/const_refers_to_static.rs:20:32 | LL | const READ_MUT: u32 = unsafe { MUTABLE }; | ^^^^^^^ constant accesses mutable global memory error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refers_to_static.rs:22:1 + --> $DIR/const_refers_to_static.rs:23:1 | LL | const REF_INTERIOR_MUT: &usize = { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` @@ -30,7 +30,7 @@ LL | const REF_INTERIOR_MUT: &usize = { warning: skipping const checks | help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static.rs:10:5 + --> $DIR/const_refers_to_static.rs:11:5 | LL | FOO.fetch_add(1, Ordering::Relaxed) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs index b33ebfb06be9a..86d23d44bffe7 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs @@ -2,6 +2,8 @@ //@ aux-build:static_cross_crate.rs //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ dont-require-annotations: NOTE + #![feature(half_open_range_patterns_in_slices)] #![allow(static_mut_refs)] @@ -10,25 +12,25 @@ extern crate static_cross_crate; // Sneaky: reference to a mutable static. // Allowing this would be a disaster for pattern matching, we could violate exhaustiveness checking! const SLICE_MUT: &[u8; 1] = { //~ ERROR undefined behavior - //~| encountered reference to mutable memory + //~| NOTE encountered reference to mutable memory unsafe { &static_cross_crate::ZERO } }; const U8_MUT: &u8 = { //~ ERROR undefined behavior - //~| encountered reference to mutable memory + //~| NOTE encountered reference to mutable memory unsafe { &static_cross_crate::ZERO[0] } }; // Also test indirection that reads from other static. const U8_MUT2: &u8 = { //~ ERROR undefined behavior - //~| encountered reference to mutable memory + //~| NOTE encountered reference to mutable memory unsafe { &(*static_cross_crate::ZERO_REF)[0] } }; const U8_MUT3: &u8 = { unsafe { match static_cross_crate::OPT_ZERO { //~^ ERROR evaluation of constant value failed - //~| constant accesses mutable global memory + //~| NOTE constant accesses mutable global memory Some(ref u) => u, None => panic!(), } diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr index 8f8271cce38e9..4e5052ed4704d 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refers_to_static_cross_crate.rs:12:1 + --> $DIR/const_refers_to_static_cross_crate.rs:14:1 | LL | const SLICE_MUT: &[u8; 1] = { | ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` @@ -10,7 +10,7 @@ LL | const SLICE_MUT: &[u8; 1] = { } error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refers_to_static_cross_crate.rs:17:1 + --> $DIR/const_refers_to_static_cross_crate.rs:19:1 | LL | const U8_MUT: &u8 = { | ^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` @@ -21,7 +21,7 @@ LL | const U8_MUT: &u8 = { } error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refers_to_static_cross_crate.rs:23:1 + --> $DIR/const_refers_to_static_cross_crate.rs:25:1 | LL | const U8_MUT2: &u8 = { | ^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` @@ -32,7 +32,7 @@ LL | const U8_MUT2: &u8 = { } error[E0080]: evaluation of constant value failed - --> $DIR/const_refers_to_static_cross_crate.rs:29:15 + --> $DIR/const_refers_to_static_cross_crate.rs:31:15 | LL | match static_cross_crate::OPT_ZERO { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses mutable global memory diff --git a/tests/ui/consts/miri_unleashed/drop.rs b/tests/ui/consts/miri_unleashed/drop.rs index 45ade4906b82f..ff9281358d4b3 100644 --- a/tests/ui/consts/miri_unleashed/drop.rs +++ b/tests/ui/consts/miri_unleashed/drop.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Zunleash-the-miri-inside-of-you -//@ error-pattern: calling non-const function ` as Drop>::drop` use std::mem::ManuallyDrop; @@ -14,4 +13,8 @@ static TEST_OK: () = { // The actual error is tested by the error-pattern above. static TEST_BAD: () = { let _v: Vec = Vec::new(); -}; +}; //~ ERROR could not evaluate static initializer + //~| NOTE calling non-const function ` as Drop>::drop` + //~| NOTE inside `drop_in_place::> - shim(Some(Vec))` + +//~? WARN skipping const checks diff --git a/tests/ui/consts/miri_unleashed/drop.stderr b/tests/ui/consts/miri_unleashed/drop.stderr index 40a29d5a819a1..0286c43127945 100644 --- a/tests/ui/consts/miri_unleashed/drop.stderr +++ b/tests/ui/consts/miri_unleashed/drop.stderr @@ -1,16 +1,16 @@ error[E0080]: could not evaluate static initializer - --> $DIR/drop.rs:17:1 + --> $DIR/drop.rs:16:1 | LL | }; | ^ calling non-const function ` as Drop>::drop` | -note: inside `std::ptr::drop_in_place::> - shim(Some(Vec))` +note: inside `drop_in_place::> - shim(Some(Vec))` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL warning: skipping const checks | help: skipping check that does not even have a feature gate - --> $DIR/drop.rs:16:9 + --> $DIR/drop.rs:15:9 | LL | let _v: Vec = Vec::new(); | ^^ diff --git a/tests/ui/consts/miri_unleashed/extern-static.stderr b/tests/ui/consts/miri_unleashed/extern-static.stderr index 0979a5e4fb197..4dbabbe44a2bb 100644 --- a/tests/ui/consts/miri_unleashed/extern-static.stderr +++ b/tests/ui/consts/miri_unleashed/extern-static.stderr @@ -2,13 +2,13 @@ error[E0080]: could not evaluate static initializer --> $DIR/extern-static.rs:11:25 | LL | unsafe { let _val = DATA; } - | ^^^^ cannot access extern static (DefId(0:4 ~ extern_static[c41e]::{extern#0}::DATA)) + | ^^^^ cannot access extern static `DATA` error[E0080]: could not evaluate static initializer --> $DIR/extern-static.rs:16:14 | LL | unsafe { DATA = 0; } - | ^^^^^^^^ cannot access extern static (DefId(0:4 ~ extern_static[c41e]::{extern#0}::DATA)) + | ^^^^^^^^ cannot access extern static `DATA` error: aborting due to 2 previous errors diff --git a/tests/ui/consts/miri_unleashed/inline_asm.rs b/tests/ui/consts/miri_unleashed/inline_asm.rs index 8627a6bf8870a..f1ce613b16e85 100644 --- a/tests/ui/consts/miri_unleashed/inline_asm.rs +++ b/tests/ui/consts/miri_unleashed/inline_asm.rs @@ -11,3 +11,5 @@ static TEST_BAD: () = { //~^ ERROR could not evaluate static initializer //~| NOTE inline assembly is not supported }; + +//~? WARN skipping const checks diff --git a/tests/ui/consts/miri_unleashed/mutable_references.rs b/tests/ui/consts/miri_unleashed/mutable_references.rs index 039d0fadfcc47..02a35487e8a08 100644 --- a/tests/ui/consts/miri_unleashed/mutable_references.rs +++ b/tests/ui/consts/miri_unleashed/mutable_references.rs @@ -1,6 +1,7 @@ //@ compile-flags: -Zunleash-the-miri-inside-of-you //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ dont-require-annotations: NOTE #![allow(static_mut_refs)] use std::cell::UnsafeCell; @@ -11,10 +12,10 @@ use std::sync::atomic::*; // This requires walking nested statics. static FOO: &&mut u32 = &&mut 42; //~^ ERROR it is undefined behavior to use this value -//~| pointing to read-only memory +//~| NOTE pointing to read-only memory static OH_YES: &mut i32 = &mut 42; //~^ ERROR it is undefined behavior to use this value -//~| pointing to read-only memory +//~| NOTE pointing to read-only memory static BAR: &mut () = &mut (); //~^ ERROR encountered mutable pointer in final value of static @@ -25,11 +26,11 @@ static BOO: &mut Foo<()> = &mut Foo(()); const BLUNT: &mut i32 = &mut 42; //~^ ERROR: it is undefined behavior to use this value -//~| pointing to read-only memory +//~| NOTE pointing to read-only memory const SUBTLE: &mut i32 = unsafe { //~^ ERROR: it is undefined behavior to use this value - //~| constructing invalid value: encountered reference to mutable memory in `const` + //~| NOTE constructing invalid value: encountered reference to mutable memory in `const` static mut STATIC: i32 = 0; &mut STATIC }; @@ -42,13 +43,13 @@ struct Meh { unsafe impl Sync for Meh {} static MEH: Meh = Meh { x: &UnsafeCell::new(42) }; //~^ ERROR it is undefined behavior to use this value -//~| `UnsafeCell` in read-only memory +//~| NOTE `UnsafeCell` in read-only memory // Same with a const: // the following will never be ok! no interior mut behind consts, because // all allocs interned here will be marked immutable. const MUH: Meh = Meh { //~^ ERROR it is undefined behavior to use this value - //~| `UnsafeCell` in read-only memory + //~| NOTE `UnsafeCell` in read-only memory x: &UnsafeCell::new(42), }; @@ -60,30 +61,30 @@ unsafe impl Sync for Synced {} // Make sure we also catch this behind a type-erased `dyn Trait` reference. const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; //~^ ERROR: it is undefined behavior to use this value -//~| `UnsafeCell` in read-only memory +//~| NOTE `UnsafeCell` in read-only memory // # Check for mutable references to read-only memory static READONLY: i32 = 0; static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; //~^ ERROR: it is undefined behavior to use this value -//~| pointing to read-only memory +//~| NOTE pointing to read-only memory // # Check for consts pointing to mutable memory static mut MUTABLE: i32 = 42; -const POINTS_TO_MUTABLE: &i32 = unsafe { &MUTABLE }; //~ERROR: undefined behavior -//~| encountered reference to mutable memory +const POINTS_TO_MUTABLE: &i32 = unsafe { &MUTABLE }; //~ ERROR undefined behavior +//~| NOTE encountered reference to mutable memory static mut MUTABLE_REF: &mut i32 = &mut 42; const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF }; -//~^ ERROR: evaluation of constant value failed -//~| accesses mutable global memory +//~^ ERROR evaluation of constant value failed +//~| NOTE accesses mutable global memory const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _; -//~^ ERROR: mutable pointer in final value +//~^ ERROR mutable pointer in final value const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _; -//~^ ERROR: mutable pointer in final value +//~^ ERROR mutable pointer in final value // This does *not* error since it uses a shared reference, and we have to ignore // those. See . @@ -114,3 +115,5 @@ fn main() { } *OH_YES = 99; //~ ERROR cannot assign to `*OH_YES`, as `OH_YES` is an immutable static item } + +//~? WARN skipping const checks diff --git a/tests/ui/consts/miri_unleashed/mutable_references.stderr b/tests/ui/consts/miri_unleashed/mutable_references.stderr index ce5cedac8bcd3..3049be80adc74 100644 --- a/tests/ui/consts/miri_unleashed/mutable_references.stderr +++ b/tests/ui/consts/miri_unleashed/mutable_references.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:12:1 + --> $DIR/mutable_references.rs:13:1 | LL | static FOO: &&mut u32 = &&mut 42; | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered mutable reference or box pointing to read-only memory @@ -10,7 +10,7 @@ LL | static FOO: &&mut u32 = &&mut 42; } error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:15:1 + --> $DIR/mutable_references.rs:16:1 | LL | static OH_YES: &mut i32 = &mut 42; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory @@ -21,19 +21,19 @@ LL | static OH_YES: &mut i32 = &mut 42; } error: encountered mutable pointer in final value of static - --> $DIR/mutable_references.rs:18:1 + --> $DIR/mutable_references.rs:19:1 | LL | static BAR: &mut () = &mut (); | ^^^^^^^^^^^^^^^^^^^ error: encountered mutable pointer in final value of static - --> $DIR/mutable_references.rs:23:1 + --> $DIR/mutable_references.rs:24:1 | LL | static BOO: &mut Foo<()> = &mut Foo(()); | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:26:1 + --> $DIR/mutable_references.rs:27:1 | LL | const BLUNT: &mut i32 = &mut 42; | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory @@ -44,7 +44,7 @@ LL | const BLUNT: &mut i32 = &mut 42; } error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:30:1 + --> $DIR/mutable_references.rs:31:1 | LL | const SUBTLE: &mut i32 = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` @@ -55,7 +55,7 @@ LL | const SUBTLE: &mut i32 = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:43:1 + --> $DIR/mutable_references.rs:44:1 | LL | static MEH: Meh = Meh { x: &UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^ constructing invalid value at .x.: encountered `UnsafeCell` in read-only memory @@ -66,7 +66,7 @@ LL | static MEH: Meh = Meh { x: &UnsafeCell::new(42) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:49:1 + --> $DIR/mutable_references.rs:50:1 | LL | const MUH: Meh = Meh { | ^^^^^^^^^^^^^^ constructing invalid value at .x.: encountered `UnsafeCell` in read-only memory @@ -77,7 +77,7 @@ LL | const MUH: Meh = Meh { } error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:61:1 + --> $DIR/mutable_references.rs:62:1 | LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ...x: encountered `UnsafeCell` in read-only memory @@ -88,7 +88,7 @@ LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:68:1 + --> $DIR/mutable_references.rs:69:1 | LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory @@ -99,7 +99,7 @@ LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const } error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:75:1 + --> $DIR/mutable_references.rs:76:1 | LL | const POINTS_TO_MUTABLE: &i32 = unsafe { &MUTABLE }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` @@ -110,37 +110,37 @@ LL | const POINTS_TO_MUTABLE: &i32 = unsafe { &MUTABLE }; } error[E0080]: evaluation of constant value failed - --> $DIR/mutable_references.rs:78:43 + --> $DIR/mutable_references.rs:79:43 | LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF }; | ^^^^^^^^^^^^^ constant accesses mutable global memory error: encountered mutable pointer in final value of constant - --> $DIR/mutable_references.rs:82:1 + --> $DIR/mutable_references.rs:83:1 | LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: encountered mutable pointer in final value of constant - --> $DIR/mutable_references.rs:85:1 + --> $DIR/mutable_references.rs:86:1 | LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: encountered mutable pointer in final value of constant - --> $DIR/mutable_references.rs:105:1 + --> $DIR/mutable_references.rs:106:1 | LL | const RAW_MUT_CAST: SyncPtr = SyncPtr { x: &mut 42 as *mut _ as *const _ }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: encountered mutable pointer in final value of constant - --> $DIR/mutable_references.rs:108:1 + --> $DIR/mutable_references.rs:109:1 | LL | const RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0594]: cannot assign to `*OH_YES`, as `OH_YES` is an immutable static item - --> $DIR/mutable_references.rs:115:5 + --> $DIR/mutable_references.rs:116:5 | LL | *OH_YES = 99; | ^^^^^^^^^^^^ cannot assign @@ -148,72 +148,72 @@ LL | *OH_YES = 99; warning: skipping const checks | help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:12:26 + --> $DIR/mutable_references.rs:13:26 | LL | static FOO: &&mut u32 = &&mut 42; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:15:27 + --> $DIR/mutable_references.rs:16:27 | LL | static OH_YES: &mut i32 = &mut 42; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:18:23 + --> $DIR/mutable_references.rs:19:23 | LL | static BAR: &mut () = &mut (); | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:23:28 + --> $DIR/mutable_references.rs:24:28 | LL | static BOO: &mut Foo<()> = &mut Foo(()); | ^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:26:25 + --> $DIR/mutable_references.rs:27:25 | LL | const BLUNT: &mut i32 = &mut 42; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:43:28 + --> $DIR/mutable_references.rs:44:28 | LL | static MEH: Meh = Meh { x: &UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:52:8 + --> $DIR/mutable_references.rs:53:8 | LL | x: &UnsafeCell::new(42), | ^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:61:27 + --> $DIR/mutable_references.rs:62:27 | LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:82:45 + --> $DIR/mutable_references.rs:83:45 | LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:85:46 + --> $DIR/mutable_references.rs:86:46 | LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:90:47 + --> $DIR/mutable_references.rs:91:47 | LL | const INTERIOR_MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; | ^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:102:51 + --> $DIR/mutable_references.rs:103:51 | LL | const RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; | ^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:105:49 + --> $DIR/mutable_references.rs:106:49 | LL | const RAW_MUT_CAST: SyncPtr = SyncPtr { x: &mut 42 as *mut _ as *const _ }; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:108:51 + --> $DIR/mutable_references.rs:109:51 | LL | const RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; | ^^^^^^ diff --git a/tests/ui/consts/miri_unleashed/non_const_fn.rs b/tests/ui/consts/miri_unleashed/non_const_fn.rs index d3ffb61af1185..201647ac8d880 100644 --- a/tests/ui/consts/miri_unleashed/non_const_fn.rs +++ b/tests/ui/consts/miri_unleashed/non_const_fn.rs @@ -9,3 +9,5 @@ static C: () = foo(); //~| NOTE calling non-const function `foo` fn main() {} + +//~? WARN skipping const checks diff --git a/tests/ui/consts/miri_unleashed/ptr_arith.rs b/tests/ui/consts/miri_unleashed/ptr_arith.rs index 6dd8ab11e7cff..408aa5db24d19 100644 --- a/tests/ui/consts/miri_unleashed/ptr_arith.rs +++ b/tests/ui/consts/miri_unleashed/ptr_arith.rs @@ -6,7 +6,7 @@ static PTR_INT_CAST: () = { let x = &0 as *const _ as usize; //~^ ERROR could not evaluate static initializer - //~| exposing pointers + //~| NOTE exposing pointers let _v = x == x; }; @@ -14,10 +14,12 @@ static PTR_INT_TRANSMUTE: () = unsafe { let x: usize = std::mem::transmute(&0); let _v = x + 0; //~^ ERROR could not evaluate static initializer - //~| unable to turn pointer into integer + //~| NOTE unable to turn pointer into integer }; // I'd love to test pointer comparison, but that is not possible since // their `PartialEq` impl is non-`const`. fn main() {} + +//~? WARN skipping const checks diff --git a/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs b/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs index 126d78158fe0d..7fa173d8d9d84 100644 --- a/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs +++ b/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs @@ -38,3 +38,5 @@ static RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; //~^ ERROR mutable pointer in final value fn main() {} + +//~? WARN skipping const checks diff --git a/tests/ui/consts/miri_unleashed/tls.rs b/tests/ui/consts/miri_unleashed/tls.rs index b0c6c088361ce..ab23a52499885 100644 --- a/tests/ui/consts/miri_unleashed/tls.rs +++ b/tests/ui/consts/miri_unleashed/tls.rs @@ -23,3 +23,5 @@ static TEST_BAD_REF: () = { }; fn main() {} + +//~? WARN skipping const checks diff --git a/tests/ui/consts/miri_unleashed/tls.stderr b/tests/ui/consts/miri_unleashed/tls.stderr index a00b7eb13128c..ef83654430318 100644 --- a/tests/ui/consts/miri_unleashed/tls.stderr +++ b/tests/ui/consts/miri_unleashed/tls.stderr @@ -2,13 +2,13 @@ error[E0080]: could not evaluate static initializer --> $DIR/tls.rs:11:25 | LL | unsafe { let _val = A; } - | ^ cannot access thread local static (DefId(0:4 ~ tls[ca29]::A)) + | ^ cannot access thread local static `A` error[E0080]: could not evaluate static initializer --> $DIR/tls.rs:20:26 | LL | unsafe { let _val = &A; } - | ^ cannot access thread local static (DefId(0:4 ~ tls[ca29]::A)) + | ^ cannot access thread local static `A` warning: skipping const checks | diff --git a/tests/ui/consts/missing_span_in_backtrace.rs b/tests/ui/consts/missing_span_in_backtrace.rs index c8c7453daa127..490eb57c24d45 100644 --- a/tests/ui/consts/missing_span_in_backtrace.rs +++ b/tests/ui/consts/missing_span_in_backtrace.rs @@ -1,7 +1,5 @@ //@ compile-flags: -Z ui-testing=no - -#![feature(const_swap_nonoverlapping)] use std::{ mem::{self, MaybeUninit}, ptr, diff --git a/tests/ui/consts/missing_span_in_backtrace.stderr b/tests/ui/consts/missing_span_in_backtrace.stderr index 2f3a65302bd57..7c07710332b80 100644 --- a/tests/ui/consts/missing_span_in_backtrace.stderr +++ b/tests/ui/consts/missing_span_in_backtrace.stderr @@ -1,20 +1,20 @@ error[E0080]: evaluation of constant value failed - --> $DIR/missing_span_in_backtrace.rs:16:9 + --> $DIR/missing_span_in_backtrace.rs:14:9 | -16 | / ptr::swap_nonoverlapping( -17 | | &mut ptr1 as *mut _ as *mut MaybeUninit, -18 | | &mut ptr2 as *mut _ as *mut MaybeUninit, -19 | | mem::size_of::<&i32>(), -20 | | ); +14 | / ptr::swap_nonoverlapping( +15 | | &mut ptr1 as *mut _ as *mut MaybeUninit, +16 | | &mut ptr2 as *mut _ as *mut MaybeUninit, +17 | | mem::size_of::<&i32>(), +18 | | ); | |_________^ unable to copy parts of a pointer from memory at ALLOC0 | note: inside `swap_nonoverlapping::>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL note: inside `swap_nonoverlapping::compiletime::>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `std::ptr::swap_nonoverlapping_simple_untyped::>` +note: inside `std::ptr::swap_nonoverlapping_const::>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `std::ptr::read::>>` +note: inside `std::ptr::copy_nonoverlapping::>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported diff --git a/tests/ui/consts/no-ice-from-static-in-const-issue-52060.rs b/tests/ui/consts/no-ice-from-static-in-const-issue-52060.rs index e0f9e462d32e8..e3cd8ec7d8a6a 100644 --- a/tests/ui/consts/no-ice-from-static-in-const-issue-52060.rs +++ b/tests/ui/consts/no-ice-from-static-in-const-issue-52060.rs @@ -4,6 +4,6 @@ static mut A: &'static [u32] = &[1]; static B: [u32; 1] = [0; unsafe { A.len() }]; //~^ ERROR: evaluation of constant value failed -//~| mutable global memory +//~| NOTE mutable global memory fn main() {} diff --git a/tests/ui/consts/offset_from_ub.rs b/tests/ui/consts/offset_from_ub.rs index 53d9c7a39da8a..47511c0343d6c 100644 --- a/tests/ui/consts/offset_from_ub.rs +++ b/tests/ui/consts/offset_from_ub.rs @@ -1,4 +1,6 @@ //@ normalize-stderr: "\d+ bytes" -> "$$BYTES bytes" +//@ dont-require-annotations: NOTE + #![feature(core_intrinsics)] use std::intrinsics::{ptr_offset_from, ptr_offset_from_unsigned}; @@ -16,7 +18,7 @@ pub const DIFFERENT_ALLOC: usize = { let uninit2 = std::mem::MaybeUninit::::uninit(); let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct; let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed - //~| not both derived from the same allocation + //~| NOTE not both derived from the same allocation offset as usize }; @@ -29,14 +31,14 @@ pub const NOT_MULTIPLE_OF_SIZE: isize = { let base_ptr = data.as_ptr(); let field_ptr = &data[1] as *const u8 as *const u16; unsafe { ptr_offset_from(field_ptr, base_ptr as *const u16) } //~ERROR evaluation of constant value failed - //~| 1_isize cannot be divided by 2_isize without remainder + //~| NOTE 1_isize cannot be divided by 2_isize without remainder }; pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC let ptr1 = 8 as *const u8; let ptr2 = 16 as *const u8; unsafe { ptr_offset_from(ptr2, ptr1) } //~ERROR evaluation of constant value failed - //~| not both derived from the same allocation + //~| NOTE not both derived from the same allocation }; const OUT_OF_BOUNDS_1: isize = { @@ -45,7 +47,7 @@ const OUT_OF_BOUNDS_1: isize = { let end_ptr = (start_ptr).wrapping_add(length); // First ptr is out of bounds unsafe { ptr_offset_from(end_ptr, start_ptr) } //~ERROR evaluation of constant value failed - //~| the memory range between them is not in-bounds of an allocation + //~| NOTE the memory range between them is not in-bounds of an allocation }; const OUT_OF_BOUNDS_2: isize = { @@ -54,7 +56,7 @@ const OUT_OF_BOUNDS_2: isize = { let end_ptr = (start_ptr).wrapping_add(length); // Second ptr is out of bounds unsafe { ptr_offset_from(start_ptr, end_ptr) } //~ERROR evaluation of constant value failed - //~| the memory range between them is not in-bounds of an allocation + //~| NOTE the memory range between them is not in-bounds of an allocation }; pub const DIFFERENT_ALLOC_UNSIGNED: usize = { @@ -63,20 +65,20 @@ pub const DIFFERENT_ALLOC_UNSIGNED: usize = { let uninit2 = std::mem::MaybeUninit::::uninit(); let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct; unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) } //~ERROR evaluation of constant value failed - //~| not both derived from the same allocation + //~| NOTE not both derived from the same allocation }; pub const TOO_FAR_APART1: isize = { let ptr1 = &0u8 as *const u8; let ptr2 = ptr1.wrapping_add(isize::MAX as usize + 42); unsafe { ptr_offset_from(ptr2, ptr1) } //~ERROR evaluation of constant value failed - //~| too far ahead + //~| NOTE too far ahead }; pub const TOO_FAR_APART2: isize = { let ptr1 = &0u8 as *const u8; let ptr2 = ptr1.wrapping_add(isize::MAX as usize + 42); unsafe { ptr_offset_from(ptr1, ptr2) } //~ERROR evaluation of constant value failed - //~| too far before + //~| NOTE too far before }; pub const TOO_FAR_APART3: isize = { let ptr1 = &0u8 as *const u8; @@ -84,21 +86,21 @@ pub const TOO_FAR_APART3: isize = { // The result of this would be `isize::MIN`, which *does* fit in an `isize`, but its // absolute value does not. (Also anyway there cannot be an allocation of that size.) unsafe { ptr_offset_from(ptr1, ptr2) } //~ERROR evaluation of constant value failed - //~| too far before + //~| NOTE too far before }; const WRONG_ORDER_UNSIGNED: usize = { let a = ['a', 'b', 'c']; let p = a.as_ptr(); unsafe { ptr_offset_from_unsigned(p, p.add(2) ) } //~ERROR evaluation of constant value failed - //~| first pointer has smaller offset than second: 0 < 8 + //~| NOTE first pointer has smaller offset than second: 0 < 8 }; pub const TOO_FAR_APART_UNSIGNED: usize = { let ptr1 = &0u8 as *const u8; let ptr2 = ptr1.wrapping_add(isize::MAX as usize + 42); // This would fit into a `usize` but we still don't allow it. unsafe { ptr_offset_from_unsigned(ptr2, ptr1) } //~ERROR evaluation of constant value failed - //~| too far ahead + //~| NOTE too far ahead }; // These do NOT complain that pointers are too far apart; they pass that check (to then fail the diff --git a/tests/ui/consts/offset_from_ub.stderr b/tests/ui/consts/offset_from_ub.stderr index 08e42c9f30ba2..5bfb9a1170cf3 100644 --- a/tests/ui/consts/offset_from_ub.stderr +++ b/tests/ui/consts/offset_from_ub.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:18:27 + --> $DIR/offset_from_ub.rs:20:27 | LL | let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same allocation error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:24:14 + --> $DIR/offset_from_ub.rs:26:14 | LL | unsafe { (42 as *const u8).offset_from(&5u8) as usize } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same allocation @@ -14,67 +14,67 @@ note: inside `std::ptr::const_ptr::::offset_from` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:31:14 + --> $DIR/offset_from_ub.rs:33:14 | LL | unsafe { ptr_offset_from(field_ptr, base_ptr as *const u16) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ exact_div: 1_isize cannot be divided by 2_isize without remainder error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:38:14 + --> $DIR/offset_from_ub.rs:40:14 | LL | unsafe { ptr_offset_from(ptr2, ptr1) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same allocation error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:47:14 + --> $DIR/offset_from_ub.rs:49:14 | LL | unsafe { ptr_offset_from(end_ptr, start_ptr) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers where the memory range between them is not in-bounds of an allocation error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:56:14 + --> $DIR/offset_from_ub.rs:58:14 | LL | unsafe { ptr_offset_from(start_ptr, end_ptr) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers where the memory range between them is not in-bounds of an allocation error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:65:14 + --> $DIR/offset_from_ub.rs:67:14 | LL | unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on two different pointers that are not both derived from the same allocation error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:72:14 + --> $DIR/offset_from_ub.rs:74:14 | LL | unsafe { ptr_offset_from(ptr2, ptr1) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far ahead of second error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:78:14 + --> $DIR/offset_from_ub.rs:80:14 | LL | unsafe { ptr_offset_from(ptr1, ptr2) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far before second error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:86:14 + --> $DIR/offset_from_ub.rs:88:14 | LL | unsafe { ptr_offset_from(ptr1, ptr2) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far before second error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:93:14 + --> $DIR/offset_from_ub.rs:95:14 | LL | unsafe { ptr_offset_from_unsigned(p, p.add(2) ) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: 0 < 8 error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:100:14 + --> $DIR/offset_from_ub.rs:102:14 | LL | unsafe { ptr_offset_from_unsigned(ptr2, ptr1) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer is too far ahead of second error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:109:14 + --> $DIR/offset_from_ub.rs:111:14 | LL | unsafe { ptr2.offset_from(ptr1) } | ^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same allocation @@ -83,7 +83,7 @@ note: inside `std::ptr::const_ptr::::offset_from` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:115:14 + --> $DIR/offset_from_ub.rs:117:14 | LL | unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far before second diff --git a/tests/ui/consts/offset_ub.stderr b/tests/ui/consts/offset_ub.stderr index a247ad254651d..699b63dfd66d8 100644 --- a/tests/ui/consts/offset_ub.stderr +++ b/tests/ui/consts/offset_ub.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:8:46 | LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to the end of 1 byte of memory, but got ALLOC0 which is at the beginning of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC0 which is at the beginning of the allocation | note: inside `std::ptr::const_ptr::::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -11,7 +11,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:9:43 | LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got ALLOC1 which is only 1 byte from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got ALLOC1 which is only 1 byte from the end of the allocation | note: inside `std::ptr::const_ptr::::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -20,7 +20,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:10:45 | LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got ALLOC2 which is only $BYTES bytes from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got ALLOC2 which is only $BYTES bytes from the end of the allocation | note: inside `std::ptr::const_ptr::::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -47,7 +47,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:14:56 | LL | pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got 0xf..f[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got 0xf..f[noalloc] which is a dangling pointer (it has no provenance) | note: inside `std::ptr::const_ptr::::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -56,7 +56,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:15:57 | LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) | note: inside `std::ptr::const_ptr::::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -65,7 +65,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:16:49 | LL | pub const NEGATIVE_OFFSET: *const u8 = unsafe { [0u8; 1].as_ptr().wrapping_offset(-2).offset(-2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got ALLOC3-0x2 which points to before the beginning of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got ALLOC3-0x2 which is only $BYTES bytes from the beginning of the allocation | note: inside `std::ptr::const_ptr::::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -74,7 +74,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:18:50 | LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got ALLOC4 which is at or beyond the end of the allocation of size $BYTES bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by 1 byte, but got ALLOC4 which is at or beyond the end of the allocation of size $BYTES bytes | note: inside `std::ptr::const_ptr::::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -83,7 +83,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:19:42 | LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by $BYTES bytes, but got 0x1[noalloc] which is a dangling pointer (it has no provenance) | note: inside `std::ptr::mut_ptr::::offset` --> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL @@ -92,7 +92,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_ub.rs:22:47 | LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got 0xf..f[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in-bounds pointer arithmetic failed: attempting to offset pointer by -$BYTES bytes, but got 0xf..f[noalloc] which is a dangling pointer (it has no provenance) | note: inside `std::ptr::const_ptr::::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL diff --git a/tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs b/tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs index 53618e2e86acd..e458a6f98adf5 100644 --- a/tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs +++ b/tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs @@ -5,10 +5,13 @@ //@ only-64bit // FIXME (#135952) In some cases on AArch64 Linux the diagnostic does not trigger //@ ignore-aarch64-unknown-linux-gnu +// AIX will allow the allocation to go through, and get SIGKILL when zero initializing +// the overcommitted page. +//@ ignore-aix pub struct Data([u8; (1 << 47) - 1]); const _: &'static Data = &Data([0; (1 << 47) - 1]); //~^ERROR: evaluation of constant value failed -//~| tried to allocate more memory than available to compiler +//~| NOTE tried to allocate more memory than available to compiler fn main() {} diff --git a/tests/ui/consts/promoted_running_out_of_memory_issue-130687.stderr b/tests/ui/consts/promoted_running_out_of_memory_issue-130687.stderr index aac805dbd8c79..02180c1e4c683 100644 --- a/tests/ui/consts/promoted_running_out_of_memory_issue-130687.stderr +++ b/tests/ui/consts/promoted_running_out_of_memory_issue-130687.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation of constant value failed - --> $DIR/promoted_running_out_of_memory_issue-130687.rs:10:32 + --> $DIR/promoted_running_out_of_memory_issue-130687.rs:13:32 | LL | const _: &'static Data = &Data([0; (1 << 47) - 1]); | ^^^^^^^^^^^^^^^^^^ tried to allocate more memory than available to compiler diff --git a/tests/ui/consts/promoted_size_overflow.rs b/tests/ui/consts/promoted_size_overflow.rs index 3d606905e782c..ebd5ab0648717 100644 --- a/tests/ui/consts/promoted_size_overflow.rs +++ b/tests/ui/consts/promoted_size_overflow.rs @@ -2,6 +2,6 @@ pub struct Data([u8; usize::MAX >> 2]); const _: &'static [Data] = &[]; //~^ERROR: evaluation of constant value failed -//~| too big for the target architecture +//~| NOTE too big for the target architecture fn main() {} diff --git a/tests/ui/consts/qualif-indirect-mutation-fail.stderr b/tests/ui/consts/qualif-indirect-mutation-fail.stderr index d3bb01af7547e..dd575e07c2013 100644 --- a/tests/ui/consts/qualif-indirect-mutation-fail.stderr +++ b/tests/ui/consts/qualif-indirect-mutation-fail.stderr @@ -13,11 +13,11 @@ error[E0080]: evaluation of constant value failed LL | }; | ^ calling non-const function ` as Drop>::drop` | -note: inside `std::ptr::drop_in_place::> - shim(Some(Option))` +note: inside `drop_in_place::> - shim(Some(Option))` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `std::ptr::drop_in_place:: - shim(Some(String))` +note: inside `drop_in_place:: - shim(Some(String))` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `std::ptr::drop_in_place::> - shim(Some(Vec))` +note: inside `drop_in_place::> - shim(Some(Vec))` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL error[E0493]: destructor of `Option` cannot be evaluated at compile-time @@ -34,11 +34,11 @@ error[E0080]: evaluation of constant value failed LL | }; | ^ calling non-const function ` as Drop>::drop` | -note: inside `std::ptr::drop_in_place::> - shim(Some(Option))` +note: inside `drop_in_place::> - shim(Some(Option))` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `std::ptr::drop_in_place:: - shim(Some(String))` +note: inside `drop_in_place:: - shim(Some(String))` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `std::ptr::drop_in_place::> - shim(Some(Vec))` +note: inside `drop_in_place::> - shim(Some(Vec))` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL error[E0493]: destructor of `(u32, Option)` cannot be evaluated at compile-time diff --git a/tests/ui/consts/recursive-zst-static.rs b/tests/ui/consts/recursive-zst-static.rs index 53d32254a6843..a52624fada8dd 100644 --- a/tests/ui/consts/recursive-zst-static.rs +++ b/tests/ui/consts/recursive-zst-static.rs @@ -10,7 +10,7 @@ static FOO: () = FOO; //~^ ERROR could not evaluate static initializer -static A: () = B; //~ cycle detected when evaluating initializer of static `A` +static A: () = B; //~ ERROR cycle detected when evaluating initializer of static `A` static B: () = A; fn main() { diff --git a/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr index 08d0b338728f9..6812b3734efb8 100644 --- a/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-called-fn.rs:19:17 diff --git a/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr b/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr index 08d0b338728f9..6812b3734efb8 100644 --- a/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-called-fn.rs:19:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr index 41fe2cf84e495..661aea71604a0 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-closure.rs:17:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr index 41fe2cf84e495..661aea71604a0 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-closure.rs:17:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr index b62b25bd3aa4a..31e6b2f487f32 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-drop.rs:16:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr index b62b25bd3aa4a..31e6b2f487f32 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-drop.rs:16:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr index c8a7cf983c416..32dc1cb304b6c 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:16:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr index c8a7cf983c416..32dc1cb304b6c 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:16:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr index 1e68d66cf5ea0..d22cf579448bf 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-fn-behind-generic.rs:15:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr index 1e68d66cf5ea0..d22cf579448bf 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-fn-behind-generic.rs:15:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr index a9cc56fba4587..91daa2f9bc33d 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `m::Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:19:21 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr index a9cc56fba4587..91daa2f9bc33d 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `m::Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:19:21 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs index 795e021ceb084..f333f3462c751 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs @@ -20,6 +20,7 @@ mod m { } } + #[define_opaque(NotCalledFn)] fn mk_not_called() -> NotCalledFn { not_called:: } diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr index ec549561a1765..d254fc60c0ca2 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-fn.rs:19:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr index ec549561a1765..d254fc60c0ca2 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-fn.rs:19:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr index dfaf69d52b26d..c14837ce4422f 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Late::::FAIL` failed | LL | const FAIL: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-fnptr-in-const.rs:10:28 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr index dfaf69d52b26d..c14837ce4422f 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Late::::FAIL` failed | LL | const FAIL: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-fnptr-in-const.rs:10:28 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr index 7cbd423cdc087..284e1a70a20c0 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-fnptr.rs:18:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr index 7cbd423cdc087..284e1a70a20c0 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-fnptr.rs:18:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr index 58e9d7a2c9a64..8b43c67c085ba 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-move.rs:16:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr index 58e9d7a2c9a64..8b43c67c085ba 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-move.rs:16:17 diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr index 6c78ca79fd6d9..4056f28541d07 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-vtable.rs:22:21 diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr index 6c78ca79fd6d9..4056f28541d07 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-dead-vtable.rs:22:21 diff --git a/tests/ui/consts/required-consts/collect-in-promoted-const.noopt.stderr b/tests/ui/consts/required-consts/collect-in-promoted-const.noopt.stderr index fd231e1101d15..c5f3b0009f541 100644 --- a/tests/ui/consts/required-consts/collect-in-promoted-const.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-promoted-const.noopt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-promoted-const.rs:20:21 diff --git a/tests/ui/consts/required-consts/collect-in-promoted-const.opt.stderr b/tests/ui/consts/required-consts/collect-in-promoted-const.opt.stderr index 0f3f77769adf5..6f36aaf314da2 100644 --- a/tests/ui/consts/required-consts/collect-in-promoted-const.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-promoted-const.opt.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-promoted-const.rs:20:21 @@ -17,8 +15,6 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/collect-in-promoted-const.rs:20:21 diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr index d2145089028ee..f98e2c172024e 100644 --- a/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr @@ -3,11 +3,9 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/interpret-in-const-called-fn.rs:17:9 + --> $DIR/interpret-in-const-called-fn.rs:18:9 | LL | Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr b/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr index d2145089028ee..f98e2c172024e 100644 --- a/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr @@ -3,11 +3,9 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/interpret-in-const-called-fn.rs:17:9 + --> $DIR/interpret-in-const-called-fn.rs:18:9 | LL | Fail::::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs b/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs index f2e83f56f3707..1ed6853f0a49b 100644 --- a/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs @@ -6,6 +6,7 @@ struct Fail(T); impl Fail { const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed + //~| NOTE in this expansion of panic! } #[inline(never)] @@ -14,7 +15,7 @@ const fn no_codegen() { // This bad constant is only used in dead code in a no-codegen function... and yet we still // must make sure that the build fails. // This relies on const-eval evaluating all `required_consts` of `const fn`. - Fail::::C; //~ constant + Fail::::C; //~ NOTE constant } } diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr index 2bd0b92d4c229..f70e262ac4c58 100644 --- a/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/interpret-in-promoted.rs:13:28 + --> $DIR/interpret-in-promoted.rs:15:28 | LL | let _x: &'static () = &ub(); | ^^^^ entering unreachable code | note: inside `ub` - --> $DIR/interpret-in-promoted.rs:7:5 + --> $DIR/interpret-in-promoted.rs:9:5 | LL | std::hint::unreachable_unchecked(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ note: inside `unreachable_unchecked` --> $SRC_DIR/core/src/hint.rs:LL:COL note: erroneous constant encountered - --> $DIR/interpret-in-promoted.rs:13:27 + --> $DIR/interpret-in-promoted.rs:15:27 | LL | let _x: &'static () = &ub(); | ^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr b/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr index 2bd0b92d4c229..f70e262ac4c58 100644 --- a/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/interpret-in-promoted.rs:13:28 + --> $DIR/interpret-in-promoted.rs:15:28 | LL | let _x: &'static () = &ub(); | ^^^^ entering unreachable code | note: inside `ub` - --> $DIR/interpret-in-promoted.rs:7:5 + --> $DIR/interpret-in-promoted.rs:9:5 | LL | std::hint::unreachable_unchecked(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ note: inside `unreachable_unchecked` --> $SRC_DIR/core/src/hint.rs:LL:COL note: erroneous constant encountered - --> $DIR/interpret-in-promoted.rs:13:27 + --> $DIR/interpret-in-promoted.rs:15:27 | LL | let _x: &'static () = &ub(); | ^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.rs b/tests/ui/consts/required-consts/interpret-in-promoted.rs index 2c7b3375054d2..85d2ea3418cdf 100644 --- a/tests/ui/consts/required-consts/interpret-in-promoted.rs +++ b/tests/ui/consts/required-consts/interpret-in-promoted.rs @@ -1,10 +1,12 @@ //@revisions: noopt opt //@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O +//@ dont-require-annotations: NOTE + //! Make sure we evaluate const fn calls even if they get promoted and their result ignored. const unsafe fn ub() { - std::hint::unreachable_unchecked(); //~ inside `ub` + std::hint::unreachable_unchecked(); //~ NOTE inside `ub` } pub const FOO: () = unsafe { diff --git a/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr index f999bf370b850..28daf265af4a0 100644 --- a/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr @@ -3,11 +3,9 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/interpret-in-static.rs:16:9 + --> $DIR/interpret-in-static.rs:17:9 | LL | Fail::::C; | ^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-static.opt.stderr b/tests/ui/consts/required-consts/interpret-in-static.opt.stderr index f999bf370b850..28daf265af4a0 100644 --- a/tests/ui/consts/required-consts/interpret-in-static.opt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-static.opt.stderr @@ -3,11 +3,9 @@ error[E0080]: evaluation of `Fail::::C` failed | LL | const C: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/interpret-in-static.rs:16:9 + --> $DIR/interpret-in-static.rs:17:9 | LL | Fail::::C; | ^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-static.rs b/tests/ui/consts/required-consts/interpret-in-static.rs index 8bacd03044002..7ddf15f912121 100644 --- a/tests/ui/consts/required-consts/interpret-in-static.rs +++ b/tests/ui/consts/required-consts/interpret-in-static.rs @@ -6,6 +6,7 @@ struct Fail(T); impl Fail { const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed + //~| NOTE in this expansion of panic! } pub static FOO: () = { @@ -13,7 +14,7 @@ pub static FOO: () = { // This bad constant is only used in dead code in a static initializer... and yet we still // must make sure that the build fails. // This relies on const-eval evaluating all `required_consts` of the `static` MIR body. - Fail::::C; //~ constant + Fail::::C; //~ NOTE constant } }; diff --git a/tests/ui/consts/slice-index-overflow-issue-130284.rs b/tests/ui/consts/slice-index-overflow-issue-130284.rs index 299009082563a..6877ebe147689 100644 --- a/tests/ui/consts/slice-index-overflow-issue-130284.rs +++ b/tests/ui/consts/slice-index-overflow-issue-130284.rs @@ -6,7 +6,7 @@ const C: () = { // This used to ICE, but it should just report UB. let _ice = (*fat)[usize::MAX - 1]; //~^ERROR: constant value failed - //~| overflow + //~| NOTE overflow } }; diff --git a/tests/ui/consts/static-default-lifetime/elided-lifetime.rs b/tests/ui/consts/static-default-lifetime/elided-lifetime.rs index 95d59f9b894e4..d60fe7d409af9 100644 --- a/tests/ui/consts/static-default-lifetime/elided-lifetime.rs +++ b/tests/ui/consts/static-default-lifetime/elided-lifetime.rs @@ -16,7 +16,7 @@ impl Bar for Foo<'_> { const STATIC: &str = ""; //~^ ERROR `&` without an explicit lifetime name cannot be used here //~| WARN this was previously accepted by the compiler but is being phased out - //~| ERROR const not compatible with trait + //~| ERROR lifetime parameters or bounds on associated const `STATIC` do not match the trait declaration } fn main() {} diff --git a/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr b/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr index ec01225c6bfa1..bb8365b0ae560 100644 --- a/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr +++ b/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr @@ -39,21 +39,15 @@ help: use the `'static` lifetime LL | const STATIC: &'static str = ""; | +++++++ -error[E0308]: const not compatible with trait - --> $DIR/elided-lifetime.rs:16:5 +error[E0195]: lifetime parameters or bounds on associated const `STATIC` do not match the trait declaration + --> $DIR/elided-lifetime.rs:16:17 | +LL | const STATIC: &str; + | - lifetimes in impl do not match this associated const in trait +... LL | const STATIC: &str = ""; - | ^^^^^^^^^^^^^^^^^^ lifetime mismatch - | - = note: expected reference `&'static _` - found reference `&_` -note: the anonymous lifetime as defined here... - --> $DIR/elided-lifetime.rs:16:19 - | -LL | const STATIC: &str = ""; - | ^ - = note: ...does not necessarily outlive the static lifetime + | ^ lifetimes do not match associated const in trait error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0195`. diff --git a/tests/ui/consts/static-default-lifetime/static-trait-impl.rs b/tests/ui/consts/static-default-lifetime/static-trait-impl.rs index 025fda4df5811..85746df146fc7 100644 --- a/tests/ui/consts/static-default-lifetime/static-trait-impl.rs +++ b/tests/ui/consts/static-default-lifetime/static-trait-impl.rs @@ -9,7 +9,7 @@ impl Bar<'_> for A { const STATIC: &str = ""; //~^ ERROR `&` without an explicit lifetime name cannot be used here //~| WARN this was previously accepted by the compiler but is being phased out - //~| ERROR const not compatible with trait + //~| ERROR lifetime parameters or bounds on associated const `STATIC` do not match the trait declaration } struct B; @@ -17,4 +17,17 @@ impl Bar<'static> for B { const STATIC: &str = ""; } +struct C; +impl Bar<'_> for C { + // make ^^ not cause + const STATIC: &'static str = { + struct B; + impl Bar<'static> for B { + const STATIC: &str = ""; + // ^ to emit a future incompat warning + } + "" + }; +} + fn main() {} diff --git a/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr b/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr index b8e2f412b492c..38d24db1317f5 100644 --- a/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr +++ b/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr @@ -21,25 +21,15 @@ help: use the `'static` lifetime LL | const STATIC: &'static str = ""; | +++++++ -error[E0308]: const not compatible with trait - --> $DIR/static-trait-impl.rs:9:5 +error[E0195]: lifetime parameters or bounds on associated const `STATIC` do not match the trait declaration + --> $DIR/static-trait-impl.rs:9:17 | +LL | const STATIC: &'a str; + | - lifetimes in impl do not match this associated const in trait +... LL | const STATIC: &str = ""; - | ^^^^^^^^^^^^^^^^^^ lifetime mismatch - | - = note: expected reference `&_` - found reference `&_` -note: the anonymous lifetime as defined here... - --> $DIR/static-trait-impl.rs:9:19 - | -LL | const STATIC: &str = ""; - | ^ -note: ...does not necessarily outlive the anonymous lifetime as defined here - --> $DIR/static-trait-impl.rs:8:10 - | -LL | impl Bar<'_> for A { - | ^^ + | ^ lifetimes do not match associated const in trait error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0195`. diff --git a/tests/ui/consts/timeout.rs b/tests/ui/consts/timeout.rs index c4fb8bab66360..8dab87ac8e183 100644 --- a/tests/ui/consts/timeout.rs +++ b/tests/ui/consts/timeout.rs @@ -3,7 +3,6 @@ //! ICE. //@ compile-flags: --crate-type=lib -Ztiny-const-eval-limit -//@ error-pattern: constant evaluation is taking a long time static ROOK_ATTACKS_TABLE: () = { 0_u64.count_ones(); @@ -23,3 +22,5 @@ static ROOK_ATTACKS_TABLE: () = { 0_u64.count_ones(); 0_u64.count_ones(); }; + +//~? ERROR constant evaluation is taking a long time diff --git a/tests/ui/consts/timeout.stderr b/tests/ui/consts/timeout.stderr index 6bfa06d86d11e..ecefeff76e967 100644 --- a/tests/ui/consts/timeout.stderr +++ b/tests/ui/consts/timeout.stderr @@ -4,7 +4,7 @@ error: constant evaluation is taking a long time = note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval. If your compilation actually takes a long time, you can safely allow the lint. help: the constant being evaluated - --> $DIR/timeout.rs:8:1 + --> $DIR/timeout.rs:7:1 | LL | static ROOK_ATTACKS_TABLE: () = { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/too_generic_eval_ice.current.stderr b/tests/ui/consts/too_generic_eval_ice.current.stderr new file mode 100644 index 0000000000000..02bcaee80154f --- /dev/null +++ b/tests/ui/consts/too_generic_eval_ice.current.stderr @@ -0,0 +1,45 @@ +error: constant expression depends on a generic parameter + --> $DIR/too_generic_eval_ice.rs:11:13 + | +LL | [5; Self::HOST_SIZE] == [6; 0] + | ^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/too_generic_eval_ice.rs:11:9 + | +LL | [5; Self::HOST_SIZE] == [6; 0] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/too_generic_eval_ice.rs:11: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}; Self::HOST_SIZE]` with `[{integer}; 0]` + --> $DIR/too_generic_eval_ice.rs:11:30 + | +LL | [5; Self::HOST_SIZE] == [6; 0] + | ^^ no implementation for `[{integer}; Self::HOST_SIZE] == [{integer}; 0]` + | + = help: the trait `PartialEq<[{integer}; 0]>` is not implemented for `[{integer}; Self::HOST_SIZE]` + = help: the following other types implement trait `PartialEq`: + `&[T]` implements `PartialEq>` + `&[T]` implements `PartialEq<[U; N]>` + `&[u8; N]` implements `PartialEq` + `&[u8; N]` implements `PartialEq` + `&[u8]` implements `PartialEq` + `&[u8]` implements `PartialEq` + `&mut [T]` implements `PartialEq>` + `&mut [T]` implements `PartialEq<[U; N]>` + and 11 others + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/consts/too_generic_eval_ice.next.stderr b/tests/ui/consts/too_generic_eval_ice.next.stderr new file mode 100644 index 0000000000000..01da33241c810 --- /dev/null +++ b/tests/ui/consts/too_generic_eval_ice.next.stderr @@ -0,0 +1,9 @@ +error[E0284]: type annotations needed: cannot satisfy `the constant `Self::HOST_SIZE` can be evaluated` + --> $DIR/too_generic_eval_ice.rs:11:13 + | +LL | [5; Self::HOST_SIZE] == [6; 0] + | ^^^^^^^^^^^^^^^ cannot satisfy `the constant `Self::HOST_SIZE` can be evaluated` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/consts/too_generic_eval_ice.rs b/tests/ui/consts/too_generic_eval_ice.rs index 0d46a4c8276dc..ff741cdcf20bc 100644 --- a/tests/ui/consts/too_generic_eval_ice.rs +++ b/tests/ui/consts/too_generic_eval_ice.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + pub struct Foo(A, B); impl Foo { @@ -5,10 +9,11 @@ impl Foo { pub fn crash() -> bool { [5; Self::HOST_SIZE] == [6; 0] - //~^ ERROR constant expression depends on a generic parameter - //~| ERROR constant expression depends on a generic parameter - //~| ERROR constant expression depends on a generic parameter - //~| ERROR can't compare `[{integer}; Self::HOST_SIZE]` with `[{integer}; 0]` + //[current]~^ ERROR constant expression depends on a generic parameter + //[current]~| ERROR constant expression depends on a generic parameter + //[current]~| ERROR constant expression depends on a generic parameter + //[current]~| ERROR can't compare `[{integer}; Self::HOST_SIZE]` with `[{integer}; 0]` + //[next]~^^^^^ ERROR type annotations needed } } diff --git a/tests/ui/consts/too_generic_eval_ice.stderr b/tests/ui/consts/too_generic_eval_ice.stderr deleted file mode 100644 index 3cc4377514a21..0000000000000 --- a/tests/ui/consts/too_generic_eval_ice.stderr +++ /dev/null @@ -1,45 +0,0 @@ -error: constant expression depends on a generic parameter - --> $DIR/too_generic_eval_ice.rs:7:13 - | -LL | [5; Self::HOST_SIZE] == [6; 0] - | ^^^^^^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -error: constant expression depends on a generic parameter - --> $DIR/too_generic_eval_ice.rs:7:9 - | -LL | [5; Self::HOST_SIZE] == [6; 0] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this may fail depending on what value the parameter takes - -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}; Self::HOST_SIZE]` with `[{integer}; 0]` - --> $DIR/too_generic_eval_ice.rs:7:30 - | -LL | [5; Self::HOST_SIZE] == [6; 0] - | ^^ no implementation for `[{integer}; Self::HOST_SIZE] == [{integer}; 0]` - | - = help: the trait `PartialEq<[{integer}; 0]>` is not implemented for `[{integer}; Self::HOST_SIZE]` - = help: the following other types implement trait `PartialEq`: - `&[T]` implements `PartialEq>` - `&[T]` implements `PartialEq<[U; N]>` - `&[u8; N]` implements `PartialEq` - `&[u8; N]` implements `PartialEq` - `&[u8]` implements `PartialEq` - `&[u8]` implements `PartialEq` - `&mut [T]` implements `PartialEq>` - `&mut [T]` implements `PartialEq<[U; N]>` - and 11 others - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/consts/uninhabited-const-issue-61744.rs b/tests/ui/consts/uninhabited-const-issue-61744.rs index 743aaf58ef5a1..802d422888bae 100644 --- a/tests/ui/consts/uninhabited-const-issue-61744.rs +++ b/tests/ui/consts/uninhabited-const-issue-61744.rs @@ -1,11 +1,12 @@ //@ build-fail +//@ dont-require-annotations: NOTE pub const unsafe fn fake_type() -> T { - hint_unreachable() //~ inside + hint_unreachable() //~ NOTE inside } pub const unsafe fn hint_unreachable() -> ! { - fake_type() //~ inside + fake_type() //~ NOTE inside } trait Const { diff --git a/tests/ui/consts/uninhabited-const-issue-61744.stderr b/tests/ui/consts/uninhabited-const-issue-61744.stderr index dd175b925934d..d0e90f9a423fa 100644 --- a/tests/ui/consts/uninhabited-const-issue-61744.stderr +++ b/tests/ui/consts/uninhabited-const-issue-61744.stderr @@ -1,641 +1,641 @@ error[E0080]: evaluation of `fake_type::` failed - --> $DIR/uninhabited-const-issue-61744.rs:12:36 + --> $DIR/uninhabited-const-issue-61744.rs:13:36 | LL | const CONSTANT: i32 = unsafe { fake_type() }; | ^^^^^^^^^^^ reached the configured maximum number of stack frames | note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ note: inside `hint_unreachable` - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:9:5 | LL | fake_type() | ^^^^^^^^^^^ note: inside `fake_type::` - --> $DIR/uninhabited-const-issue-61744.rs:4:5 + --> $DIR/uninhabited-const-issue-61744.rs:5:5 | LL | hint_unreachable() | ^^^^^^^^^^^^^^^^^^ the failure occurred here diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr index 4a47671fee191..b6f2e014e0a86 100644 --- a/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr @@ -16,6 +16,7 @@ LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz | | within this `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}` | | this tail expression is of type `{closure@contract-captures-via-closure-noncopy.rs:12:42}` | unsatisfied trait bound + | required by a bound introduced by this call | = help: within `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}`, the trait `std::marker::Copy` is not implemented for `Baz` note: required because it's used within this closure diff --git a/tests/ui/contracts/contract-const-fn.all_pass.stderr b/tests/ui/contracts/contract-const-fn.all_pass.stderr new file mode 100644 index 0000000000000..e5b1df6558235 --- /dev/null +++ b/tests/ui/contracts/contract-const-fn.all_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-const-fn.rs:17:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-const-fn.rs b/tests/ui/contracts/contract-const-fn.rs new file mode 100644 index 0000000000000..733a06ae57090 --- /dev/null +++ b/tests/ui/contracts/contract-const-fn.rs @@ -0,0 +1,56 @@ +//! Check if we can annotate a constant function with contracts. +//! +//! The contract is only checked at runtime, and it will not fail if evaluated statically. +//! This is an existing limitation due to the existing architecture and the lack of constant +//! closures. +//! +//@ revisions: all_pass runtime_fail_pre runtime_fail_post +// +//@ [all_pass] run-pass +// +//@ [runtime_fail_pre] run-fail +//@ [runtime_fail_post] run-fail +// +//@ [all_pass] compile-flags: -Zcontract-checks=yes +//@ [runtime_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [runtime_fail_post] compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +extern crate core; +use core::contracts::*; + +#[requires(x < 100)] +const fn less_than_100(x: u8) -> u8 { + x +} + +// This is wrong on purpose. +#[ensures(|ret| *ret)] +const fn always_true(b: bool) -> bool { + b +} + +const ZERO: u8 = less_than_100(0); +// This is no-op because the contract cannot be checked at compilation time. +const TWO_HUNDRED: u8 = less_than_100(200); + +/// Example from . +#[ensures(move |ret: &u32| *ret > x)] +const fn broken_sum(x: u32, y: u32) -> u32 { + x + y +} + +fn main() { + assert_eq!(ZERO, 0); + assert_eq!(TWO_HUNDRED, 200); + assert_eq!(broken_sum(0, 1), 1); + assert_eq!(always_true(true), true); + + #[cfg(runtime_fail_post)] + let _ok = always_true(false); + + // Runtime check should fail. + #[cfg(runtime_fail_pre)] + let _200 = less_than_100(200); +} diff --git a/tests/ui/contracts/contract-const-fn.runtime_fail_post.stderr b/tests/ui/contracts/contract-const-fn.runtime_fail_post.stderr new file mode 100644 index 0000000000000..e5b1df6558235 --- /dev/null +++ b/tests/ui/contracts/contract-const-fn.runtime_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-const-fn.rs:17:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-const-fn.runtime_fail_pre.stderr b/tests/ui/contracts/contract-const-fn.runtime_fail_pre.stderr new file mode 100644 index 0000000000000..e5b1df6558235 --- /dev/null +++ b/tests/ui/contracts/contract-const-fn.runtime_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-const-fn.rs:17:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs index ae692afd146fe..c62b8cca75ab7 100644 --- a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs +++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs @@ -26,11 +26,11 @@ fn main() { #[cfg(any(default, unchk_pass, chk_fail_requires))] core::intrinsics::contract_check_requires(|| false); - let doubles_to_two = { let old = 2; move |ret| ret + ret == old }; + let doubles_to_two = { let old = 2; move |ret: &u32 | ret + ret == old }; // Always pass - core::intrinsics::contract_check_ensures(&1, doubles_to_two); + core::intrinsics::contract_check_ensures(doubles_to_two, 1); // Fail if enabled #[cfg(any(default, unchk_pass, chk_fail_ensures))] - core::intrinsics::contract_check_ensures(&2, doubles_to_two); + core::intrinsics::contract_check_ensures(doubles_to_two, 2); } diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs index e91bbed294d12..73c5919453101 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.rs +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs @@ -15,14 +15,14 @@ #![feature(contracts)] // to access core::contracts //~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #![feature(contracts_internals)] // to access check_requires lang item - +#![feature(core_intrinsics)] fn foo(x: Baz) -> i32 { let injected_checker = { core::contracts::build_check_ensures(|ret| *ret > 100) }; let ret = x.baz + 50; - injected_checker(ret) + core::intrinsics::contract_check_ensures(injected_checker, ret) } struct Baz { baz: i32 } diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs index 1b76eef6780fe..6e5a7a3f95005 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -6,7 +6,7 @@ fn main() { //~^ ERROR use of unstable library feature `contracts_internals` core::intrinsics::contract_check_requires(|| true); //~^ ERROR use of unstable library feature `contracts_internals` - core::intrinsics::contract_check_ensures(&1, |_|true); + core::intrinsics::contract_check_ensures( |_|true, &1); //~^ ERROR use of unstable library feature `contracts_internals` core::contracts::build_check_ensures(|_: &()| true); diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr index 7302694a7874a..1e39bd62e245b 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -41,7 +41,7 @@ LL | core::intrinsics::contract_check_requires(|| true); error[E0658]: use of unstable library feature `contracts_internals` --> $DIR/internal-feature-gating.rs:9:5 | -LL | core::intrinsics::contract_check_ensures(&1, |_|true); +LL | core::intrinsics::contract_check_ensures( |_|true, &1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #128044 for more information diff --git a/tests/ui/copy-a-resource.rs b/tests/ui/copy-a-resource.rs deleted file mode 100644 index 55f2dd4ee6dde..0000000000000 --- a/tests/ui/copy-a-resource.rs +++ /dev/null @@ -1,21 +0,0 @@ -#[derive(Debug)] -struct Foo { - i: isize, -} - -impl Drop for Foo { - fn drop(&mut self) {} -} - -fn foo(i:isize) -> Foo { - Foo { - i: i - } -} - -fn main() { - let x = foo(10); - let _y = x.clone(); - //~^ ERROR no method named `clone` found - println!("{:?}", x); -} diff --git a/tests/ui/copy-a-resource.stderr b/tests/ui/copy-a-resource.stderr deleted file mode 100644 index ff1e28bf9611d..0000000000000 --- a/tests/ui/copy-a-resource.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0599]: no method named `clone` found for struct `Foo` in the current scope - --> $DIR/copy-a-resource.rs:18:16 - | -LL | struct Foo { - | ---------- method `clone` not found for this struct -... -LL | let _y = x.clone(); - | ^^^^^ method not found in `Foo` - | - = 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: `Clone` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/coroutine/async-gen-deduce-yield.rs b/tests/ui/coroutine/async-gen-deduce-yield.rs index f85e4a52e9b96..a9572ee9b0dd9 100644 --- a/tests/ui/coroutine/async-gen-deduce-yield.rs +++ b/tests/ui/coroutine/async-gen-deduce-yield.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2024 +//@ edition: 2024 //@ check-pass #![feature(async_iterator, gen_blocks)] diff --git a/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs b/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs index 583820c7aa39c..53e3ce77f8a82 100644 --- a/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs +++ b/tests/ui/coroutine/async-gen-yield-ty-is-unit.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2024 +//@ edition: 2024 //@ check-pass #![feature(async_iterator, gen_blocks)] diff --git a/tests/ui/coroutine/auto-trait-regions.rs b/tests/ui/coroutine/auto-trait-regions.rs index 4c239f9ee76cf..1c7f0304ddbec 100644 --- a/tests/ui/coroutine/auto-trait-regions.rs +++ b/tests/ui/coroutine/auto-trait-regions.rs @@ -43,8 +43,8 @@ fn main() { // Disallow impls which relates lifetimes in the coroutine interior let gen = #[coroutine] move || { let a = A(&mut true, &mut true, No); - //~^ temporary value dropped while borrowed - //~| temporary value dropped while borrowed + //~^ ERROR temporary value dropped while borrowed + //~| ERROR temporary value dropped while borrowed yield; assert_foo(a); }; diff --git a/tests/ui/coroutine/clone-rpit.next.stderr b/tests/ui/coroutine/clone-rpit.next.stderr deleted file mode 100644 index c223f1f211ac6..0000000000000 --- a/tests/ui/coroutine/clone-rpit.next.stderr +++ /dev/null @@ -1,47 +0,0 @@ -error[E0391]: cycle detected when type-checking `foo` - --> $DIR/clone-rpit.rs:13:1 - | -LL | pub fn foo<'a, 'b>() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...which requires coroutine witness types for `foo::{closure#0}`... - --> $DIR/clone-rpit.rs:15:5 - | -LL | move |_: ()| { - | ^^^^^^^^^^^^ -note: ...which requires promoting constants in MIR for `foo::{closure#0}`... - --> $DIR/clone-rpit.rs:15:5 - | -LL | move |_: ()| { - | ^^^^^^^^^^^^ -note: ...which requires checking if `foo::{closure#0}` contains FFI-unwind calls... - --> $DIR/clone-rpit.rs:15:5 - | -LL | move |_: ()| { - | ^^^^^^^^^^^^ -note: ...which requires building MIR for `foo::{closure#0}`... - --> $DIR/clone-rpit.rs:15:5 - | -LL | move |_: ()| { - | ^^^^^^^^^^^^ -note: ...which requires match-checking `foo::{closure#0}`... - --> $DIR/clone-rpit.rs:15:5 - | -LL | move |_: ()| { - | ^^^^^^^^^^^^ -note: ...which requires type-checking `foo::{closure#0}`... - --> $DIR/clone-rpit.rs:15:5 - | -LL | move |_: ()| { - | ^^^^^^^^^^^^ - = note: ...which again requires type-checking `foo`, completing the cycle -note: cycle used when computing type of opaque `foo::{opaque#0}` - --> $DIR/clone-rpit.rs:13:25 - | -LL | pub fn foo<'a, 'b>() -> impl Clone { - | ^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/coroutine/clone-rpit.rs b/tests/ui/coroutine/clone-rpit.rs index 66569b4f42741..3882564639b4e 100644 --- a/tests/ui/coroutine/clone-rpit.rs +++ b/tests/ui/coroutine/clone-rpit.rs @@ -1,8 +1,7 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[current] check-pass -//@[next] known-bug: trait-system-refactor-initiative#82 +//@ check-pass #![feature(coroutines, coroutine_trait, coroutine_clone)] diff --git a/tests/ui/coroutine/coroutine-with-nll.rs b/tests/ui/coroutine/coroutine-with-nll.rs index fa77ab4e04958..44ead560cf889 100644 --- a/tests/ui/coroutine/coroutine-with-nll.rs +++ b/tests/ui/coroutine/coroutine-with-nll.rs @@ -6,7 +6,7 @@ fn main() { // The reference in `_a` is a Legal with NLL since it ends before the yield let _a = &mut true; let b = &mut true; - //~^ borrow may still be in use when coroutine yields + //~^ ERROR borrow may still be in use when coroutine yields yield (); println!("{}", b); }; diff --git a/tests/ui/coroutine/delayed-obligations-emit.next.stderr b/tests/ui/coroutine/delayed-obligations-emit.next.stderr new file mode 100644 index 0000000000000..3a3663398c9a7 --- /dev/null +++ b/tests/ui/coroutine/delayed-obligations-emit.next.stderr @@ -0,0 +1,15 @@ +error[E0275]: overflow evaluating the requirement `{async block@$DIR/delayed-obligations-emit.rs:17:11: 17:16}: Send` + --> $DIR/delayed-obligations-emit.rs:17:5 + | +LL | spawn(async { build_dependencies().await }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `spawn` + --> $DIR/delayed-obligations-emit.rs:31:13 + | +LL | fn spawn(_: F) {} + | ^^^^ required by this bound in `spawn` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/coroutine/delayed-obligations-emit.rs b/tests/ui/coroutine/delayed-obligations-emit.rs new file mode 100644 index 0000000000000..6334f29fcb22c --- /dev/null +++ b/tests/ui/coroutine/delayed-obligations-emit.rs @@ -0,0 +1,33 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ edition: 2024 +//@[current] check-pass + +// This previously caused an ICE with the new solver. +// The delayed coroutine obligations were checked with the +// opaque types inferred by borrowck. +// +// One of these delayed obligations failed with overflow in +// borrowck, causing us to taint `type_of` for the opaque. This +// then caused us to also not emit an error when checking the +// coroutine obligations. + +fn build_multiple<'a>() -> impl Sized { + spawn(async { build_dependencies().await }); + //[next]~^ ERROR overflow evaluating the requirement +} + +// Adding an explicit `Send` bound fixes it. +// Proving `build_dependencies(): Send` in `build_multiple` adds +// addiitional defining uses/placeholders. +fn build_dependencies() -> impl Future /* + Send */ { + async { + Box::pin(build_dependencies()).await; + async { build_multiple() }.await; + } +} + +fn spawn(_: F) {} + +fn main() {} diff --git a/tests/ui/coroutine/dont-drop-stalled-generators.rs b/tests/ui/coroutine/dont-drop-stalled-generators.rs new file mode 100644 index 0000000000000..8e0c45a9773b3 --- /dev/null +++ b/tests/ui/coroutine/dont-drop-stalled-generators.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass +//@ edition: 2024 + +// This test previously used the `is_copy_raw` query during +// HIR typeck, dropping the list of generators from the current +// body. This then caused a query cycle. + +struct W(*const T); + +impl Clone for W { + fn clone(&self) -> Self { W(self.0) } +} + +impl Copy for W {} + +fn main() { + let coro = async {}; + let x = W(&raw const coro); + let c = || { + let x = x; + }; +} diff --git a/tests/ui/coroutine/drop-tracking-parent-expression.rs b/tests/ui/coroutine/drop-tracking-parent-expression.rs index 0f4d99c893648..702cbc88ae4b0 100644 --- a/tests/ui/coroutine/drop-tracking-parent-expression.rs +++ b/tests/ui/coroutine/drop-tracking-parent-expression.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + #![feature(coroutines, negative_impls, rustc_attrs, stmt_expr_attributes)] macro_rules! type_combinations { @@ -15,9 +17,9 @@ macro_rules! type_combinations { // dropped *after* the yield. { let g = #[coroutine] move || match drop($name::Client { ..$name::Client::default() }) { - //~^ `significant_drop::Client` which is not `Send` - //~| `insignificant_dtor::Client` which is not `Send` - //~| `derived_drop::Client` which is not `Send` + //~^ NOTE `significant_drop::Client` which is not `Send` + //~| NOTE `insignificant_dtor::Client` which is not `Send` + //~| NOTE `derived_drop::Client` which is not `Send` _ => yield, }; assert_send(g); diff --git a/tests/ui/coroutine/drop-tracking-parent-expression.stderr b/tests/ui/coroutine/drop-tracking-parent-expression.stderr index dc2f9768d234e..2f5fe882f6e77 100644 --- a/tests/ui/coroutine/drop-tracking-parent-expression.stderr +++ b/tests/ui/coroutine/drop-tracking-parent-expression.stderr @@ -1,5 +1,5 @@ error: coroutine cannot be sent between threads safely - --> $DIR/drop-tracking-parent-expression.rs:23:13 + --> $DIR/drop-tracking-parent-expression.rs:25:13 | LL | assert_send(g); | ^^^^^^^^^^^^^^ coroutine is not `Send` @@ -12,9 +12,9 @@ LL | | }; LL | | ); | |_____- in this macro invocation | - = help: within `{coroutine@$DIR/drop-tracking-parent-expression.rs:17:34: 17:41}`, the trait `Send` is not implemented for `derived_drop::Client` + = help: within `{coroutine@$DIR/drop-tracking-parent-expression.rs:19:34: 19:41}`, the trait `Send` is not implemented for `derived_drop::Client` note: coroutine is not `Send` as this value is used across a yield - --> $DIR/drop-tracking-parent-expression.rs:21:22 + --> $DIR/drop-tracking-parent-expression.rs:23:22 | LL | let g = #[coroutine] move || match drop($name::Client { ..$name::Client::default() }) { | ------------------------ has type `derived_drop::Client` which is not `Send` @@ -30,14 +30,14 @@ LL | | }; LL | | ); | |_____- in this macro invocation note: required by a bound in `assert_send` - --> $DIR/drop-tracking-parent-expression.rs:40:19 + --> $DIR/drop-tracking-parent-expression.rs:42:19 | LL | fn assert_send(_thing: T) {} | ^^^^ required by this bound in `assert_send` = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info) error: coroutine cannot be sent between threads safely - --> $DIR/drop-tracking-parent-expression.rs:23:13 + --> $DIR/drop-tracking-parent-expression.rs:25:13 | LL | assert_send(g); | ^^^^^^^^^^^^^^ coroutine is not `Send` @@ -50,9 +50,9 @@ LL | | }; LL | | ); | |_____- in this macro invocation | - = help: within `{coroutine@$DIR/drop-tracking-parent-expression.rs:17:34: 17:41}`, the trait `Send` is not implemented for `significant_drop::Client` + = help: within `{coroutine@$DIR/drop-tracking-parent-expression.rs:19:34: 19:41}`, the trait `Send` is not implemented for `significant_drop::Client` note: coroutine is not `Send` as this value is used across a yield - --> $DIR/drop-tracking-parent-expression.rs:21:22 + --> $DIR/drop-tracking-parent-expression.rs:23:22 | LL | let g = #[coroutine] move || match drop($name::Client { ..$name::Client::default() }) { | ------------------------ has type `significant_drop::Client` which is not `Send` @@ -68,14 +68,14 @@ LL | | }; LL | | ); | |_____- in this macro invocation note: required by a bound in `assert_send` - --> $DIR/drop-tracking-parent-expression.rs:40:19 + --> $DIR/drop-tracking-parent-expression.rs:42:19 | LL | fn assert_send(_thing: T) {} | ^^^^ required by this bound in `assert_send` = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info) error: coroutine cannot be sent between threads safely - --> $DIR/drop-tracking-parent-expression.rs:23:13 + --> $DIR/drop-tracking-parent-expression.rs:25:13 | LL | assert_send(g); | ^^^^^^^^^^^^^^ coroutine is not `Send` @@ -88,9 +88,9 @@ LL | | }; LL | | ); | |_____- in this macro invocation | - = help: within `{coroutine@$DIR/drop-tracking-parent-expression.rs:17:34: 17:41}`, the trait `Send` is not implemented for `insignificant_dtor::Client` + = help: within `{coroutine@$DIR/drop-tracking-parent-expression.rs:19:34: 19:41}`, the trait `Send` is not implemented for `insignificant_dtor::Client` note: coroutine is not `Send` as this value is used across a yield - --> $DIR/drop-tracking-parent-expression.rs:21:22 + --> $DIR/drop-tracking-parent-expression.rs:23:22 | LL | let g = #[coroutine] move || match drop($name::Client { ..$name::Client::default() }) { | ------------------------ has type `insignificant_dtor::Client` which is not `Send` @@ -106,7 +106,7 @@ LL | | }; LL | | ); | |_____- in this macro invocation note: required by a bound in `assert_send` - --> $DIR/drop-tracking-parent-expression.rs:40:19 + --> $DIR/drop-tracking-parent-expression.rs:42:19 | LL | fn assert_send(_thing: T) {} | ^^^^ required by this bound in `assert_send` diff --git a/tests/ui/coroutine/gen_block.e2024.stderr b/tests/ui/coroutine/gen_block.e2024.stderr index 0491bdbc2e17a..347f111e79fdc 100644 --- a/tests/ui/coroutine/gen_block.e2024.stderr +++ b/tests/ui/coroutine/gen_block.e2024.stderr @@ -18,16 +18,6 @@ LL | let _ = #[coroutine] || {}; = help: add `#![feature(coroutines)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: yield syntax is experimental - --> $DIR/gen_block.rs:16:16 - | -LL | let _ = || yield true; - | ^^^^^^^^^^ - | - = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks --> $DIR/gen_block.rs:16:16 | @@ -39,23 +29,13 @@ help: use `#[coroutine]` to make this closure a coroutine LL | let _ = #[coroutine] || yield true; | ++++++++++++ -error[E0658]: yield syntax is experimental - --> $DIR/gen_block.rs:20:29 - | -LL | let _ = #[coroutine] || yield true; - | ^^^^^^^^^^ - | - = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error[E0282]: type annotations needed --> $DIR/gen_block.rs:7:13 | LL | let x = gen {}; | ^^^^^^ cannot infer type -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0282, E0658. For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/coroutine/gen_block.none.stderr b/tests/ui/coroutine/gen_block.none.stderr index 43437793005a5..ed744f2957ac5 100644 --- a/tests/ui/coroutine/gen_block.none.stderr +++ b/tests/ui/coroutine/gen_block.none.stderr @@ -71,7 +71,7 @@ LL | let _ = || yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks @@ -92,7 +92,7 @@ LL | let _ = #[coroutine] || yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 11 previous errors diff --git a/tests/ui/coroutine/gen_block.rs b/tests/ui/coroutine/gen_block.rs index 4494d654eeb62..e3734dd4cdf05 100644 --- a/tests/ui/coroutine/gen_block.rs +++ b/tests/ui/coroutine/gen_block.rs @@ -14,12 +14,12 @@ fn main() { //[none]~^ ERROR: cannot find let _ = || yield true; //[none]~ ERROR yield syntax is experimental - //~^ ERROR yield syntax is experimental + //[none]~^ ERROR yield syntax is experimental //~^^ ERROR `yield` can only be used in let _ = #[coroutine] || yield true; //[none]~ ERROR yield syntax is experimental //~^ ERROR `#[coroutine]` attribute is an experimental feature - //~^^ ERROR yield syntax is experimental + //[none]~^^ ERROR yield syntax is experimental let _ = #[coroutine] || {}; //~^ ERROR `#[coroutine]` attribute is an experimental feature diff --git a/tests/ui/coroutine/higher-ranked-rigid.rs b/tests/ui/coroutine/higher-ranked-rigid.rs new file mode 100644 index 0000000000000..23a7d51300c9f --- /dev/null +++ b/tests/ui/coroutine/higher-ranked-rigid.rs @@ -0,0 +1,41 @@ +//@ edition: 2024 +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Regression test for . +// Coroutines erase all free lifetimes from their interior types, replacing them with higher- +// ranked regions which act as universals, to properly represent the fact that we don't know what +// the value of the region is within the coroutine. +// +// In the future in `from_request`, that means that the `'r` lifetime is being replaced in +// `>::Assoc`, which is in present in the existential bounds of the +// `dyn Future` that it's awaiting. Normalizing this associated type, with its free lifetimes +// replaced, means proving `T: FromRequest<'!0>`, which doesn't hold without constraining the +// `'!0` lifetime, which we don't do today. + +// Proving `T: Trait` holds when `::Assoc` is rigid is not necessary for soundness, +// at least not *yet*, and it's not even necessary for diagnostics since we have other special +// casing for, e.g., AliasRelate goals failing in the BestObligation folder. + +// The old solver unintentioanlly avoids this by never checking that `T: Trait` holds when +// `::Assoc` is rigid. Introducing this additional requirement when projecting rigidly +// in the old solver causes this (and tons of production crates) to fail. See the fallout from the +// crater run at . + +use std::future::Future; +use std::pin::Pin; + +pub trait FromRequest<'r> { + type Assoc; + fn from_request() -> Pin + Send>>; +} + +fn test<'r, T: FromRequest<'r>>() -> Pin + Send>> { + Box::pin(async move { + T::from_request().await; + }) +} + +fn main() {} diff --git a/tests/ui/coroutine/layout-error.rs b/tests/ui/coroutine/layout-error.rs index 3e26cf17d29ec..6cf32134025ca 100644 --- a/tests/ui/coroutine/layout-error.rs +++ b/tests/ui/coroutine/layout-error.rs @@ -16,22 +16,20 @@ impl Task { } } -mod helper { - use super::*; - pub type F = impl Future; - fn foo() - where - F:, - { - async fn cb() { - let a = Foo; //~ ERROR cannot find value `Foo` in this scope - } - - Task::spawn(&POOL, || cb()); +pub type F = impl Future; +#[define_opaque(F)] +fn foo() +where + F:, +{ + async fn cb() { + let a = Foo; //~ ERROR cannot find value `Foo` in this scope } + + Task::spawn(&POOL, || cb()); } // Check that statics are inhabited computes they layout. -static POOL: Task = Task::new(); +static POOL: Task = Task::new(); fn main() {} diff --git a/tests/ui/coroutine/layout-error.stderr b/tests/ui/coroutine/layout-error.stderr index ceadb62c99988..91e352164357d 100644 --- a/tests/ui/coroutine/layout-error.stderr +++ b/tests/ui/coroutine/layout-error.stderr @@ -1,8 +1,8 @@ error[E0425]: cannot find value `Foo` in this scope - --> $DIR/layout-error.rs:27:21 + --> $DIR/layout-error.rs:26:17 | -LL | let a = Foo; - | ^^^ not found in this scope +LL | let a = Foo; + | ^^^ not found in this scope error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/metadata-sufficient-for-layout.rs b/tests/ui/coroutine/metadata-sufficient-for-layout.rs index 9c3a7e4378e39..b7d8575c76177 100644 --- a/tests/ui/coroutine/metadata-sufficient-for-layout.rs +++ b/tests/ui/coroutine/metadata-sufficient-for-layout.rs @@ -15,6 +15,7 @@ mod helper { use std::ops::Coroutine; pub type F = impl Coroutine<(), Yield = (), Return = ()>; + #[define_opaque(F)] fn f() -> F { metadata_sufficient_for_layout::g() } diff --git a/tests/ui/coroutine/parent-expression.rs b/tests/ui/coroutine/parent-expression.rs index 0f4d99c893648..702cbc88ae4b0 100644 --- a/tests/ui/coroutine/parent-expression.rs +++ b/tests/ui/coroutine/parent-expression.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + #![feature(coroutines, negative_impls, rustc_attrs, stmt_expr_attributes)] macro_rules! type_combinations { @@ -15,9 +17,9 @@ macro_rules! type_combinations { // dropped *after* the yield. { let g = #[coroutine] move || match drop($name::Client { ..$name::Client::default() }) { - //~^ `significant_drop::Client` which is not `Send` - //~| `insignificant_dtor::Client` which is not `Send` - //~| `derived_drop::Client` which is not `Send` + //~^ NOTE `significant_drop::Client` which is not `Send` + //~| NOTE `insignificant_dtor::Client` which is not `Send` + //~| NOTE `derived_drop::Client` which is not `Send` _ => yield, }; assert_send(g); diff --git a/tests/ui/coroutine/parent-expression.stderr b/tests/ui/coroutine/parent-expression.stderr index a9125772b5ad1..f14bf05ed0949 100644 --- a/tests/ui/coroutine/parent-expression.stderr +++ b/tests/ui/coroutine/parent-expression.stderr @@ -1,5 +1,5 @@ error: coroutine cannot be sent between threads safely - --> $DIR/parent-expression.rs:23:13 + --> $DIR/parent-expression.rs:25:13 | LL | assert_send(g); | ^^^^^^^^^^^^^^ coroutine is not `Send` @@ -12,9 +12,9 @@ LL | | }; LL | | ); | |_____- in this macro invocation | - = help: within `{coroutine@$DIR/parent-expression.rs:17:34: 17:41}`, the trait `Send` is not implemented for `derived_drop::Client` + = help: within `{coroutine@$DIR/parent-expression.rs:19:34: 19:41}`, the trait `Send` is not implemented for `derived_drop::Client` note: coroutine is not `Send` as this value is used across a yield - --> $DIR/parent-expression.rs:21:22 + --> $DIR/parent-expression.rs:23:22 | LL | let g = #[coroutine] move || match drop($name::Client { ..$name::Client::default() }) { | ------------------------ has type `derived_drop::Client` which is not `Send` @@ -30,14 +30,14 @@ LL | | }; LL | | ); | |_____- in this macro invocation note: required by a bound in `assert_send` - --> $DIR/parent-expression.rs:40:19 + --> $DIR/parent-expression.rs:42:19 | LL | fn assert_send(_thing: T) {} | ^^^^ required by this bound in `assert_send` = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info) error: coroutine cannot be sent between threads safely - --> $DIR/parent-expression.rs:23:13 + --> $DIR/parent-expression.rs:25:13 | LL | assert_send(g); | ^^^^^^^^^^^^^^ coroutine is not `Send` @@ -50,9 +50,9 @@ LL | | }; LL | | ); | |_____- in this macro invocation | - = help: within `{coroutine@$DIR/parent-expression.rs:17:34: 17:41}`, the trait `Send` is not implemented for `significant_drop::Client` + = help: within `{coroutine@$DIR/parent-expression.rs:19:34: 19:41}`, the trait `Send` is not implemented for `significant_drop::Client` note: coroutine is not `Send` as this value is used across a yield - --> $DIR/parent-expression.rs:21:22 + --> $DIR/parent-expression.rs:23:22 | LL | let g = #[coroutine] move || match drop($name::Client { ..$name::Client::default() }) { | ------------------------ has type `significant_drop::Client` which is not `Send` @@ -68,14 +68,14 @@ LL | | }; LL | | ); | |_____- in this macro invocation note: required by a bound in `assert_send` - --> $DIR/parent-expression.rs:40:19 + --> $DIR/parent-expression.rs:42:19 | LL | fn assert_send(_thing: T) {} | ^^^^ required by this bound in `assert_send` = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info) error: coroutine cannot be sent between threads safely - --> $DIR/parent-expression.rs:23:13 + --> $DIR/parent-expression.rs:25:13 | LL | assert_send(g); | ^^^^^^^^^^^^^^ coroutine is not `Send` @@ -88,9 +88,9 @@ LL | | }; LL | | ); | |_____- in this macro invocation | - = help: within `{coroutine@$DIR/parent-expression.rs:17:34: 17:41}`, the trait `Send` is not implemented for `insignificant_dtor::Client` + = help: within `{coroutine@$DIR/parent-expression.rs:19:34: 19:41}`, the trait `Send` is not implemented for `insignificant_dtor::Client` note: coroutine is not `Send` as this value is used across a yield - --> $DIR/parent-expression.rs:21:22 + --> $DIR/parent-expression.rs:23:22 | LL | let g = #[coroutine] move || match drop($name::Client { ..$name::Client::default() }) { | ------------------------ has type `insignificant_dtor::Client` which is not `Send` @@ -106,7 +106,7 @@ LL | | }; LL | | ); | |_____- in this macro invocation note: required by a bound in `assert_send` - --> $DIR/parent-expression.rs:40:19 + --> $DIR/parent-expression.rs:42:19 | LL | fn assert_send(_thing: T) {} | ^^^^ required by this bound in `assert_send` diff --git a/tests/ui/coroutine/postfix-yield.rs b/tests/ui/coroutine/postfix-yield.rs new file mode 100644 index 0000000000000..f2fdcebdaa9a9 --- /dev/null +++ b/tests/ui/coroutine/postfix-yield.rs @@ -0,0 +1,34 @@ +// This demonstrates a proposed alternate or additional option of having yield in postfix position. + +//@ run-pass +//@ edition: 2024 + +#![feature(gen_blocks, coroutines, coroutine_trait, yield_expr, stmt_expr_attributes)] + +use std::ops::{Coroutine, CoroutineState}; +use std::pin::pin; + +fn main() { + // generators (i.e. yield doesn't return anything useful) + let mut gn = gen { + yield 1; + 2.yield; + }; + + assert_eq!(gn.next(), Some(1)); + assert_eq!(gn.next(), Some(2)); + assert_eq!(gn.next(), None); + + //coroutines (i.e. yield returns something useful) + let mut coro = pin!( + #[coroutine] + |_: i32| { + let x = 1.yield; + (x + 2).yield; + } + ); + + assert_eq!(coro.as_mut().resume(0), CoroutineState::Yielded(1)); + assert_eq!(coro.as_mut().resume(2), CoroutineState::Yielded(4)); + assert_eq!(coro.as_mut().resume(3), CoroutineState::Complete(())); +} diff --git a/tests/ui/coroutine/retain-resume-ref.rs b/tests/ui/coroutine/retain-resume-ref.rs index 6e688c33979ac..000e40d47fbe8 100644 --- a/tests/ui/coroutine/retain-resume-ref.rs +++ b/tests/ui/coroutine/retain-resume-ref.rs @@ -22,5 +22,5 @@ fn main() { let mut gen = Pin::new(&mut gen); gen.as_mut().resume(&mut thing); gen.as_mut().resume(&mut thing); - //~^ cannot borrow `thing` as mutable more than once at a time + //~^ ERROR cannot borrow `thing` as mutable more than once at a time } diff --git a/tests/ui/coverage-attr/bad-attr-ice.rs b/tests/ui/coverage-attr/bad-attr-ice.rs index 8d57bbbf49a0b..aeb44075bb6b8 100644 --- a/tests/ui/coverage-attr/bad-attr-ice.rs +++ b/tests/ui/coverage-attr/bad-attr-ice.rs @@ -10,7 +10,7 @@ #[coverage] //~^ ERROR malformed `coverage` attribute input -//[nofeat]~| the `#[coverage]` attribute is an experimental feature +//[nofeat]~| ERROR the `#[coverage]` attribute is an experimental feature fn main() {} // FIXME(#130766): When the `#[coverage(..)]` attribute is stabilized, diff --git a/tests/ui/coverage-attr/name-value.stderr b/tests/ui/coverage-attr/name-value.stderr index 31a635b57e546..f24db78415e39 100644 --- a/tests/ui/coverage-attr/name-value.stderr +++ b/tests/ui/coverage-attr/name-value.stderr @@ -43,6 +43,21 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | +error: malformed `coverage` attribute input + --> $DIR/name-value.rs:26:1 + | +LL | #[coverage = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL - #[coverage = "off"] +LL + #[coverage(off)] + | +LL - #[coverage = "off"] +LL + #[coverage(on)] + | + error: malformed `coverage` attribute input --> $DIR/name-value.rs:29:5 | @@ -59,7 +74,7 @@ LL + #[coverage(on)] | error: malformed `coverage` attribute input - --> $DIR/name-value.rs:26:1 + --> $DIR/name-value.rs:35:1 | LL | #[coverage = "off"] | ^^^^^^^^^^^^^^^^^^^ @@ -104,7 +119,7 @@ LL + #[coverage(on)] | error: malformed `coverage` attribute input - --> $DIR/name-value.rs:35:1 + --> $DIR/name-value.rs:50:1 | LL | #[coverage = "off"] | ^^^^^^^^^^^^^^^^^^^ @@ -148,21 +163,6 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | -error: malformed `coverage` attribute input - --> $DIR/name-value.rs:50:1 - | -LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ - | -help: the following are the possible correct uses - | -LL - #[coverage = "off"] -LL + #[coverage(off)] - | -LL - #[coverage = "off"] -LL + #[coverage(on)] - | - error: malformed `coverage` attribute input --> $DIR/name-value.rs:64:1 | diff --git a/tests/ui/coverage-attr/word-only.stderr b/tests/ui/coverage-attr/word-only.stderr index 612301885dcc8..2773db9c85786 100644 --- a/tests/ui/coverage-attr/word-only.stderr +++ b/tests/ui/coverage-attr/word-only.stderr @@ -37,6 +37,19 @@ LL | #[coverage(off)] LL | #[coverage(on)] | ++++ +error: malformed `coverage` attribute input + --> $DIR/word-only.rs:26:1 + | +LL | #[coverage] + | ^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(off)] + | +++++ +LL | #[coverage(on)] + | ++++ + error: malformed `coverage` attribute input --> $DIR/word-only.rs:29:5 | @@ -51,7 +64,7 @@ LL | #[coverage(on)] | ++++ error: malformed `coverage` attribute input - --> $DIR/word-only.rs:26:1 + --> $DIR/word-only.rs:35:1 | LL | #[coverage] | ^^^^^^^^^^^ @@ -90,7 +103,7 @@ LL | #[coverage(on)] | ++++ error: malformed `coverage` attribute input - --> $DIR/word-only.rs:35:1 + --> $DIR/word-only.rs:50:1 | LL | #[coverage] | ^^^^^^^^^^^ @@ -128,19 +141,6 @@ LL | #[coverage(off)] LL | #[coverage(on)] | ++++ -error: malformed `coverage` attribute input - --> $DIR/word-only.rs:50:1 - | -LL | #[coverage] - | ^^^^^^^^^^^ - | -help: the following are the possible correct uses - | -LL | #[coverage(off)] - | +++++ -LL | #[coverage(on)] - | ++++ - error: malformed `coverage` attribute input --> $DIR/word-only.rs:64:1 | diff --git a/tests/ui/crate-loading/crateresolve1.stderr b/tests/ui/crate-loading/crateresolve1.stderr index 47131a96bf43b..b17330fb9d945 100644 --- a/tests/ui/crate-loading/crateresolve1.stderr +++ b/tests/ui/crate-loading/crateresolve1.stderr @@ -4,9 +4,9 @@ error[E0464]: multiple candidates for `rlib` dependency `crateresolve1` found LL | extern crate crateresolve1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: candidate #1: $TEST_BUILD_DIR/crate-loading/crateresolve1/auxiliary/libcrateresolve1-1.somelib - = note: candidate #2: $TEST_BUILD_DIR/crate-loading/crateresolve1/auxiliary/libcrateresolve1-2.somelib - = note: candidate #3: $TEST_BUILD_DIR/crate-loading/crateresolve1/auxiliary/libcrateresolve1-3.somelib + = note: candidate #1: $TEST_BUILD_DIR/auxiliary/libcrateresolve1-1.somelib + = note: candidate #2: $TEST_BUILD_DIR/auxiliary/libcrateresolve1-2.somelib + = note: candidate #3: $TEST_BUILD_DIR/auxiliary/libcrateresolve1-3.somelib error: aborting due to 1 previous error diff --git a/tests/ui/crate-loading/crateresolve2.stderr b/tests/ui/crate-loading/crateresolve2.stderr index 3dc89dabde6d5..0c488f3fd0700 100644 --- a/tests/ui/crate-loading/crateresolve2.stderr +++ b/tests/ui/crate-loading/crateresolve2.stderr @@ -4,9 +4,9 @@ error[E0464]: multiple candidates for `rmeta` dependency `crateresolve2` found LL | extern crate crateresolve2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: candidate #1: $TEST_BUILD_DIR/crate-loading/crateresolve2/auxiliary/libcrateresolve2-1.rmeta - = note: candidate #2: $TEST_BUILD_DIR/crate-loading/crateresolve2/auxiliary/libcrateresolve2-2.rmeta - = note: candidate #3: $TEST_BUILD_DIR/crate-loading/crateresolve2/auxiliary/libcrateresolve2-3.rmeta + = note: candidate #1: $TEST_BUILD_DIR/auxiliary/libcrateresolve2-1.rmeta + = note: candidate #2: $TEST_BUILD_DIR/auxiliary/libcrateresolve2-2.rmeta + = note: candidate #3: $TEST_BUILD_DIR/auxiliary/libcrateresolve2-3.rmeta error: aborting due to 1 previous error diff --git a/tests/ui/crate_type_flag.rs b/tests/ui/crate_type_flag.rs new file mode 100644 index 0000000000000..03bea3638e1af --- /dev/null +++ b/tests/ui/crate_type_flag.rs @@ -0,0 +1,5 @@ +//@ compile-flags: --crate-type dynlib + +fn main() {} + +//~? ERROR unknown crate type: `dynlib` diff --git a/tests/ui/crate_type_flag.stderr b/tests/ui/crate_type_flag.stderr new file mode 100644 index 0000000000000..26a3e1fbd6829 --- /dev/null +++ b/tests/ui/crate_type_flag.stderr @@ -0,0 +1,2 @@ +error: unknown crate type: `dynlib`, expected one of: `lib`, `rlib`, `staticlib`, `dylib`, `cdylib`, `bin`, `proc-macro` + diff --git a/tests/ui/auxiliary/issue-18502.rs b/tests/ui/cross-crate/auxiliary/inline-cross-crate.rs similarity index 100% rename from tests/ui/auxiliary/issue-18502.rs rename to tests/ui/cross-crate/auxiliary/inline-cross-crate.rs diff --git a/tests/ui/auxiliary/issue-76387.rs b/tests/ui/cross-crate/auxiliary/llvm-miscompile-MarkValue-MaybeLive.rs similarity index 100% rename from tests/ui/auxiliary/issue-76387.rs rename to tests/ui/cross-crate/auxiliary/llvm-miscompile-MarkValue-MaybeLive.rs diff --git a/tests/ui/cross-crate/inline-cross-crate.rs b/tests/ui/cross-crate/inline-cross-crate.rs new file mode 100644 index 0000000000000..273aa8f8f0d81 --- /dev/null +++ b/tests/ui/cross-crate/inline-cross-crate.rs @@ -0,0 +1,12 @@ +//! Dpn't ice on using an inlined function from another crate +//! See and +//! + +//@ run-pass +//@ aux-build:inline-cross-crate.rs + +extern crate inline_cross_crate as fmt; + +fn main() { + ::fmt::baz(); +} diff --git a/tests/ui/cross-crate/llvm-miscompile-MarkValue-MaybeLive.rs b/tests/ui/cross-crate/llvm-miscompile-MarkValue-MaybeLive.rs new file mode 100644 index 0000000000000..95d022ddd96eb --- /dev/null +++ b/tests/ui/cross-crate/llvm-miscompile-MarkValue-MaybeLive.rs @@ -0,0 +1,22 @@ +//! Regression test for +//! Tests that LLVM doesn't miscompile this +//! See upstream fix: . + +//@ compile-flags: -C opt-level=3 +//@ aux-build: llvm-miscompile-MarkValue-MaybeLive.rs +//@ run-pass + +extern crate llvm_miscompile_MarkValue_MaybeLive; + +use llvm_miscompile_MarkValue_MaybeLive::FatPtr; + +fn print(data: &[u8]) { + println!("{:#?}", data); +} + +fn main() { + let ptr = FatPtr::new(20); + let data = unsafe { std::slice::from_raw_parts(ptr.as_ptr(), ptr.len()) }; + + print(data); +} diff --git a/tests/ui/cross/cross-borrow-trait.rs b/tests/ui/cross/cross-borrow-trait.rs index 180a75e3dfc54..88ea78e44b3a4 100644 --- a/tests/ui/cross/cross-borrow-trait.rs +++ b/tests/ui/cross/cross-borrow-trait.rs @@ -1,6 +1,8 @@ // Test that cross-borrowing (implicitly converting from `Box` to `&T`) is // forbidden when `T` is a trait. +//@ dont-require-annotations: NOTE + struct Foo; trait Trait { fn foo(&self) {} } impl Trait for Foo {} @@ -8,6 +10,6 @@ impl Trait for Foo {} pub fn main() { let x: Box = Box::new(Foo); let _y: &dyn Trait = x; //~ ERROR E0308 - //~| expected reference `&dyn Trait` - //~| found struct `Box` + //~| NOTE expected reference `&dyn Trait` + //~| NOTE found struct `Box` } diff --git a/tests/ui/cross/cross-borrow-trait.stderr b/tests/ui/cross/cross-borrow-trait.stderr index b670de39f6dbf..5fe80b5a3eef6 100644 --- a/tests/ui/cross/cross-borrow-trait.stderr +++ b/tests/ui/cross/cross-borrow-trait.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/cross-borrow-trait.rs:10:26 + --> $DIR/cross-borrow-trait.rs:12:26 | LL | let _y: &dyn Trait = x; | ---------- ^ expected `&dyn Trait`, found `Box` diff --git a/tests/ui/cross/cross-file-errors/underscore.rs b/tests/ui/cross/cross-file-errors/underscore.rs index 9d075735393d0..73eb36cec24cb 100644 --- a/tests/ui/cross/cross-file-errors/underscore.rs +++ b/tests/ui/cross/cross-file-errors/underscore.rs @@ -1,4 +1,4 @@ -//@ ignore-test (auxiliary, used by other tests) +//@ ignore-auxiliary (used by `./main.rs`) #![crate_type = "lib"] macro_rules! underscore { diff --git a/tests/ui/custom_test_frameworks/full.rs b/tests/ui/custom_test_frameworks/full.rs index 289767b1f698a..57b55e9437bf3 100644 --- a/tests/ui/custom_test_frameworks/full.rs +++ b/tests/ui/custom_test_frameworks/full.rs @@ -25,4 +25,17 @@ impl example_runner::Testable for IsFoo { const TEST_1: IsFoo = IsFoo("hello"); #[test_case] -const TEST_2: IsFoo = IsFoo("foo"); +static TEST_2: IsFoo = IsFoo("foo"); + +// FIXME: `test_case` is currently ignored on anything other than +// fn/const/static. This should be an error. Compare this with `#[test]` and +// #[bench] whose expanders emit "error: expected a non-associated function, +// found […]" if applied to invalid items. +#[test_case] +struct _S; + +// FIXME: as above. +#[test_case] +impl _S { + fn _f() {} +} diff --git a/tests/ui/custom_test_frameworks/mismatch.stderr b/tests/ui/custom_test_frameworks/mismatch.stderr index dad93cfbba4cb..c7798635fbf17 100644 --- a/tests/ui/custom_test_frameworks/mismatch.stderr +++ b/tests/ui/custom_test_frameworks/mismatch.stderr @@ -7,7 +7,6 @@ LL | fn wrong_kind(){} | ^^^^^^^^^^^^^^^^^ the trait `Testable` is not implemented for `TestDescAndFn` | = note: required for the cast from `&TestDescAndFn` to `&dyn Testable` - = note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-1.rs b/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-1.rs index 3bff447169850..7f85cbf227aef 100644 --- a/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-1.rs +++ b/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-1.rs @@ -1,9 +1,9 @@ +//~ ERROR values of the type `[u8; usize::MAX]` are too big for the target architecture // Make sure the compiler does not ICE when trying to generate the debuginfo name of a type that // causes a layout error. See https://github.com/rust-lang/rust/issues/94961. //@ compile-flags:-C debuginfo=2 //@ build-fail -//@ error-pattern: too big for the target architecture #![crate_type = "rlib"] diff --git a/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-2.rs b/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-2.rs index 6a3f8f4c249ca..35210e78dcd95 100644 --- a/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-2.rs +++ b/tests/ui/debuginfo/debuginfo-type-name-layout-ice-94961-2.rs @@ -3,9 +3,8 @@ // This version of the test already ICE'd before the commit that introduce the ICE described in // https://github.com/rust-lang/rust/issues/94961. -//@ compile-flags:-C debuginfo=2 +//@ compile-flags:-C debuginfo=2 --error-format=human //@ build-fail -//@ error-pattern: too big for the target architecture #![crate_type = "rlib"] @@ -16,3 +15,6 @@ pub enum Foo { pub fn foo() -> usize { std::mem::size_of::>() } + +// FIXME(#140620): the error is reported on different lines on different targets +//~? RAW values of the type `[u8; usize::MAX]` are too big for the target architecture diff --git a/tests/ui/debuginfo/dwarf-versions.rs b/tests/ui/debuginfo/dwarf-versions.rs index 806ade51a997f..8f731f10ead4b 100644 --- a/tests/ui/debuginfo/dwarf-versions.rs +++ b/tests/ui/debuginfo/dwarf-versions.rs @@ -1,28 +1,25 @@ // This test verifies the expected behavior of various options passed to -// `-Zdwarf-version`: 2 - 5 (valid) with all other options being invalid. +// `-Cdwarf-version`: 2 - 5 (valid) with all other options being invalid. //@ revisions: zero one two three four five six -//@[zero] compile-flags: -Zdwarf-version=0 -//@[zero] error-pattern: requested DWARF version 0 is not supported +//@[zero] compile-flags: -Cdwarf-version=0 -//@[one] compile-flags: -Zdwarf-version=1 -//@[one] error-pattern: requested DWARF version 1 is not supported +//@[one] compile-flags: -Cdwarf-version=1 -//@[two] compile-flags: -Zdwarf-version=2 +//@[two] compile-flags: -Cdwarf-version=2 //@[two] check-pass -//@[three] compile-flags: -Zdwarf-version=3 +//@[three] compile-flags: -Cdwarf-version=3 //@[three] check-pass -//@[four] compile-flags: -Zdwarf-version=4 +//@[four] compile-flags: -Cdwarf-version=4 //@[four] check-pass -//@[five] compile-flags: -Zdwarf-version=5 +//@[five] compile-flags: -Cdwarf-version=5 //@[five] check-pass -//@[six] compile-flags: -Zdwarf-version=6 -//@[six] error-pattern: requested DWARF version 6 is not supported +//@[six] compile-flags: -Cdwarf-version=6 //@ compile-flags: -g --target x86_64-unknown-linux-gnu --crate-type cdylib //@ needs-llvm-components: x86 @@ -36,3 +33,7 @@ pub trait Sized {} pub fn foo() {} + +//[zero]~? ERROR requested DWARF version 0 is not supported +//[one]~? ERROR requested DWARF version 1 is not supported +//[six]~? ERROR requested DWARF version 6 is not supported diff --git a/tests/ui/debuginfo/issue-105386-debuginfo-ub.rs b/tests/ui/debuginfo/issue-105386-debuginfo-ub.rs index 7b850f32b4b6b..e926a337659c1 100644 --- a/tests/ui/debuginfo/issue-105386-debuginfo-ub.rs +++ b/tests/ui/debuginfo/issue-105386-debuginfo-ub.rs @@ -1,5 +1,6 @@ //@ run-pass -//@ compile-flags: --edition 2021 -Copt-level=3 -Cdebuginfo=2 -Zmir-opt-level=3 +//@ compile-flags: -Copt-level=3 -Cdebuginfo=2 -Zmir-opt-level=3 +//@ edition: 2021 fn main() { TranslatorI.visit_pre(); diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.rs b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.rs index 896bbac7d8e1e..75b8141cc385f 100644 --- a/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.rs +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.rs @@ -1,6 +1,5 @@ //@ revisions: aarch64_gl i686_g i686_gl i686_uwp_g x86_64_g x86_64_gl x86_64_uwp_g //@ compile-flags: --crate-type cdylib -Csplit-debuginfo=packed -//@ error-pattern: error: `-Csplit-debuginfo=packed` is unstable on this platform //@[aarch64_gl] compile-flags: --target aarch64-pc-windows-gnullvm //@[aarch64_gl] needs-llvm-components: aarch64 @@ -27,3 +26,5 @@ #![no_core] #![no_std] + +//~? ERROR `-Csplit-debuginfo=packed` is unstable on this platform diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.rs b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.rs index 54a88c9121734..3f4da555aadf1 100644 --- a/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.rs +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.rs @@ -1,6 +1,5 @@ //@ revisions: aarch64_gl i686_g i686_gl i686_uwp_g x86_64_g x86_64_gl x86_64_uwp_g //@ compile-flags: --crate-type cdylib -Csplit-debuginfo=unpacked -//@ error-pattern: error: `-Csplit-debuginfo=unpacked` is unstable on this platform //@[aarch64_gl] compile-flags: --target aarch64-pc-windows-gnullvm //@[aarch64_gl] needs-llvm-components: aarch64 @@ -27,3 +26,5 @@ #![no_core] #![no_std] + +//~? ERROR `-Csplit-debuginfo=unpacked` is unstable on this platform diff --git a/tests/ui/deduplicate-diagnostics.duplicate.stderr b/tests/ui/deduplicate-diagnostics.duplicate.stderr index 0544b993278d7..48e2ba7b86aa4 100644 --- a/tests/ui/deduplicate-diagnostics.duplicate.stderr +++ b/tests/ui/deduplicate-diagnostics.duplicate.stderr @@ -26,6 +26,14 @@ LL | #[deny("literal")] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 4 previous errors +error[E0452]: malformed lint attribute input + --> $DIR/deduplicate-diagnostics.rs:8:8 + | +LL | #[deny("literal")] + | ^^^^^^^^^ bad attribute argument + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0452`. diff --git a/tests/ui/deduplicate-diagnostics.rs b/tests/ui/deduplicate-diagnostics.rs index 54bd5dd5098c1..299c1f5f46108 100644 --- a/tests/ui/deduplicate-diagnostics.rs +++ b/tests/ui/deduplicate-diagnostics.rs @@ -7,4 +7,5 @@ struct S; #[deny("literal")] //~ ERROR malformed lint attribute input //[duplicate]~| ERROR malformed lint attribute input + //[duplicate]~| ERROR malformed lint attribute input fn main() {} diff --git a/tests/ui/delegation/explicit-paths-signature-pass.rs b/tests/ui/delegation/explicit-paths-signature-pass.rs index 8c16ad92393c9..11bc8a70db0e3 100644 --- a/tests/ui/delegation/explicit-paths-signature-pass.rs +++ b/tests/ui/delegation/explicit-paths-signature-pass.rs @@ -6,7 +6,7 @@ mod to_reuse { use crate::S; - pub fn foo<'a>(#[cfg(FALSE)] a: u8, _b: &'a S) -> u32 { + pub fn foo<'a>(#[cfg(false)] a: u8, _b: &'a S) -> u32 { 1 } } diff --git a/tests/ui/delegation/fn-header-variadic.rs b/tests/ui/delegation/fn-header-variadic.rs new file mode 100644 index 0000000000000..2c83d64d0b3ff --- /dev/null +++ b/tests/ui/delegation/fn-header-variadic.rs @@ -0,0 +1,25 @@ +//@ aux-crate:fn_header_aux=fn-header-aux.rs + +#![feature(c_variadic)] +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +mod to_reuse { + pub unsafe extern "C" fn variadic_fn(n: usize, mut args: ...) {} +} + +reuse to_reuse::variadic_fn; +//~^ ERROR delegation to C-variadic functions is not allowed +reuse fn_header_aux::variadic_fn_extern; +//~^ ERROR delegation to C-variadic functions is not allowed + +fn main() { + unsafe { + variadic_fn(0); + variadic_fn(0, 1); + variadic_fn_extern(0); + variadic_fn_extern(0, 1); + } + let _: unsafe extern "C" fn(usize, ...) = variadic_fn; + let _: unsafe extern "C" fn(usize, ...) = variadic_fn_extern; +} diff --git a/tests/ui/delegation/fn-header-variadic.stderr b/tests/ui/delegation/fn-header-variadic.stderr new file mode 100644 index 0000000000000..688a965fb4d5c --- /dev/null +++ b/tests/ui/delegation/fn-header-variadic.stderr @@ -0,0 +1,22 @@ +error: delegation to C-variadic functions is not allowed + --> $DIR/fn-header-variadic.rs:11:17 + | +LL | pub unsafe extern "C" fn variadic_fn(n: usize, mut args: ...) {} + | ------------------------------------------------------------- callee defined here +... +LL | reuse to_reuse::variadic_fn; + | ^^^^^^^^^^^ + +error: delegation to C-variadic functions is not allowed + --> $DIR/fn-header-variadic.rs:13:22 + | +LL | reuse fn_header_aux::variadic_fn_extern; + | ^^^^^^^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/fn-header-aux.rs:7:1 + | +LL | pub unsafe extern "C" fn variadic_fn_extern(n: usize, mut args: ...) {} + | -------------------------------------------------------------------- callee defined here + +error: aborting due to 2 previous errors + diff --git a/tests/ui/delegation/fn-header.rs b/tests/ui/delegation/fn-header.rs index db20e1058e061..9de0d549f20ac 100644 --- a/tests/ui/delegation/fn-header.rs +++ b/tests/ui/delegation/fn-header.rs @@ -10,20 +10,17 @@ mod to_reuse { pub unsafe fn unsafe_fn() {} pub extern "C" fn extern_fn() {} - pub unsafe extern "C" fn variadic_fn(n: usize, mut args: ...) {} pub const fn const_fn() {} pub async fn async_fn() {} } reuse to_reuse::unsafe_fn; reuse to_reuse::extern_fn; -reuse to_reuse::variadic_fn; reuse to_reuse::const_fn; reuse to_reuse::async_fn; reuse fn_header_aux::unsafe_fn_extern; reuse fn_header_aux::extern_fn_extern; -reuse fn_header_aux::variadic_fn_extern; reuse fn_header_aux::const_fn_extern; reuse fn_header_aux::async_fn_extern; @@ -46,12 +43,4 @@ fn main() { extern_fn_extern(); let _: extern "C" fn() = extern_fn; let _: extern "C" fn() = extern_fn_extern; - unsafe { - variadic_fn(0); - variadic_fn(0, 1); - variadic_fn_extern(0); - variadic_fn_extern(0, 1); - } - let _: unsafe extern "C" fn(usize, ...) = variadic_fn; - let _: unsafe extern "C" fn(usize, ...) = variadic_fn_extern; } diff --git a/tests/ui/delegation/foreign-fn.rs b/tests/ui/delegation/foreign-fn.rs new file mode 100644 index 0000000000000..1d221da29ceb2 --- /dev/null +++ b/tests/ui/delegation/foreign-fn.rs @@ -0,0 +1,22 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] +#![deny(unsafe_op_in_unsafe_fn)] +#![deny(unused_unsafe)] + +mod to_reuse { + unsafe extern "C" { + pub fn default_unsafe_foo(); + pub unsafe fn unsafe_foo(); + pub safe fn safe_foo(); + } +} + +reuse to_reuse::{default_unsafe_foo, unsafe_foo, safe_foo}; + +fn main() { + let _: extern "C" fn() = default_unsafe_foo; + //~^ ERROR mismatched types + let _: extern "C" fn() = unsafe_foo; + //~^ ERROR mismatched types + let _: extern "C" fn() = safe_foo; +} diff --git a/tests/ui/delegation/foreign-fn.stderr b/tests/ui/delegation/foreign-fn.stderr new file mode 100644 index 0000000000000..f7d3dba4bb1c0 --- /dev/null +++ b/tests/ui/delegation/foreign-fn.stderr @@ -0,0 +1,27 @@ +error[E0308]: mismatched types + --> $DIR/foreign-fn.rs:17:30 + | +LL | let _: extern "C" fn() = default_unsafe_foo; + | --------------- ^^^^^^^^^^^^^^^^^^ expected safe fn, found unsafe fn + | | + | expected due to this + | + = note: expected fn pointer `extern "C" fn()` + found fn item `unsafe extern "C" fn() {default_unsafe_foo}` + = note: unsafe functions cannot be coerced into safe function pointers + +error[E0308]: mismatched types + --> $DIR/foreign-fn.rs:19:30 + | +LL | let _: extern "C" fn() = unsafe_foo; + | --------------- ^^^^^^^^^^ expected safe fn, found unsafe fn + | | + | expected due to this + | + = note: expected fn pointer `extern "C" fn()` + found fn item `unsafe extern "C" fn() {unsafe_foo}` + = note: unsafe functions cannot be coerced into safe function pointers + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/delegation/ice-isssue-128190.rs b/tests/ui/delegation/ice-isssue-128190.rs new file mode 100644 index 0000000000000..dab3bbee663cb --- /dev/null +++ b/tests/ui/delegation/ice-isssue-128190.rs @@ -0,0 +1,9 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +fn a(&self) {} +//~^ ERROR `self` parameter is only allowed in associated functions + +reuse a as b; + +fn main() {} diff --git a/tests/ui/delegation/ice-isssue-128190.stderr b/tests/ui/delegation/ice-isssue-128190.stderr new file mode 100644 index 0000000000000..18f676642c2b9 --- /dev/null +++ b/tests/ui/delegation/ice-isssue-128190.stderr @@ -0,0 +1,10 @@ +error: `self` parameter is only allowed in associated functions + --> $DIR/ice-isssue-128190.rs:4:6 + | +LL | fn a(&self) {} + | ^^^^^ not semantically valid as function parameter + | + = note: associated functions are those in `impl` or `trait` definitions + +error: aborting due to 1 previous error + diff --git a/tests/ui/delegation/ice-issue-124347.rs b/tests/ui/delegation/ice-issue-124347.rs index b2b3c61a722b3..3e0a5b36ddcf3 100644 --- a/tests/ui/delegation/ice-issue-124347.rs +++ b/tests/ui/delegation/ice-issue-124347.rs @@ -4,7 +4,6 @@ // FIXME(fn_delegation): `recursive delegation` error should be emitted here trait Trait { reuse Trait::foo { &self.0 } - //~^ ERROR recursive delegation is not supported yet } reuse foo; diff --git a/tests/ui/delegation/ice-issue-124347.stderr b/tests/ui/delegation/ice-issue-124347.stderr index 74c4b5cd949a3..2955c04420348 100644 --- a/tests/ui/delegation/ice-issue-124347.stderr +++ b/tests/ui/delegation/ice-issue-124347.stderr @@ -1,23 +1,17 @@ -error: recursive delegation is not supported yet - --> $DIR/ice-issue-124347.rs:6:18 - | -LL | reuse Trait::foo { &self.0 } - | ^^^ callee defined here - error[E0391]: cycle detected when computing generics of `foo` - --> $DIR/ice-issue-124347.rs:10:7 + --> $DIR/ice-issue-124347.rs:9:7 | LL | reuse foo; | ^^^ | = note: ...which immediately requires computing generics of `foo` again note: cycle used when checking that `foo` is well-formed - --> $DIR/ice-issue-124347.rs:10:7 + --> $DIR/ice-issue-124347.rs:9:7 | LL | reuse foo; | ^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/delegation/ice-issue-138362.rs b/tests/ui/delegation/ice-issue-138362.rs new file mode 100644 index 0000000000000..c30660098555b --- /dev/null +++ b/tests/ui/delegation/ice-issue-138362.rs @@ -0,0 +1,15 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait HasSelf { + fn method(self); +} +trait NoSelf { + fn method(); +} +impl NoSelf for u8 { + reuse HasSelf::method; + //~^ ERROR this function takes 1 argument but 0 arguments were supplied +} + +fn main() {} diff --git a/tests/ui/delegation/ice-issue-138362.stderr b/tests/ui/delegation/ice-issue-138362.stderr new file mode 100644 index 0000000000000..9feddc9feaec3 --- /dev/null +++ b/tests/ui/delegation/ice-issue-138362.stderr @@ -0,0 +1,19 @@ +error[E0061]: this function takes 1 argument but 0 arguments were supplied + --> $DIR/ice-issue-138362.rs:11:20 + | +LL | reuse HasSelf::method; + | ^^^^^^ argument #1 is missing + | +note: method defined here + --> $DIR/ice-issue-138362.rs:5:8 + | +LL | fn method(self); + | ^^^^^^ ---- +help: provide the argument + | +LL | reuse HasSelf::method(/* value */); + | +++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0061`. diff --git a/tests/ui/delegation/unsupported.stderr b/tests/ui/delegation/unsupported.stderr index 2b0bcf9d84e82..53d05c3db8c4f 100644 --- a/tests/ui/delegation/unsupported.stderr +++ b/tests/ui/delegation/unsupported.stderr @@ -1,4 +1,4 @@ -error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` +error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` --> $DIR/unsupported.rs:22:25 | LL | reuse to_reuse::opaque_ret; @@ -9,7 +9,7 @@ note: ...which requires comparing an impl and trait method signature, inferring | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ - = note: ...which again requires computing type of `opaque::::{synthetic#0}`, completing the cycle + = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle note: cycle used when checking that `opaque::` is well-formed --> $DIR/unsupported.rs:21:5 | @@ -17,7 +17,7 @@ LL | impl ToReuse for u8 { | ^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` +error[E0391]: cycle detected when computing type of `opaque::::opaque_ret::{anon_assoc#0}` --> $DIR/unsupported.rs:25:24 | LL | reuse ToReuse::opaque_ret; @@ -28,7 +28,7 @@ note: ...which requires comparing an impl and trait method signature, inferring | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ - = note: ...which again requires computing type of `opaque::::{synthetic#0}`, completing the cycle + = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle note: cycle used when checking that `opaque::` is well-formed --> $DIR/unsupported.rs:24:5 | diff --git a/tests/ui/dep-graph/dep-graph-dump.rs b/tests/ui/dep-graph/dep-graph-dump.rs index 7aede27d125c4..5dc05045e8281 100644 --- a/tests/ui/dep-graph/dep-graph-dump.rs +++ b/tests/ui/dep-graph/dep-graph-dump.rs @@ -4,3 +4,5 @@ //@ compile-flags: -Z dump-dep-graph fn main() {} + +//~? ERROR can't dump dependency graph without `-Z query-dep-graph` diff --git a/tests/ui/deprecation/deprecated_ar.rs b/tests/ui/deprecation/deprecated_ar.rs index 404d062e6a449..00862d2c00a23 100644 --- a/tests/ui/deprecation/deprecated_ar.rs +++ b/tests/ui/deprecation/deprecated_ar.rs @@ -2,3 +2,5 @@ //@ compile-flags: -Car=foo fn main() {} + +//~? WARN `-C ar`: this option is deprecated and does nothing diff --git a/tests/ui/deprecation/deprecated_inline_threshold.rs b/tests/ui/deprecation/deprecated_inline_threshold.rs index b54fa36397af0..284a6d6798e45 100644 --- a/tests/ui/deprecation/deprecated_inline_threshold.rs +++ b/tests/ui/deprecation/deprecated_inline_threshold.rs @@ -6,3 +6,9 @@ //@[no_val] compile-flags: -Cinline-threshold fn main() {} + +//[good_val]~? WARN `-C inline-threshold`: this option is deprecated and does nothing +//[bad_val]~? WARN `-C inline-threshold`: this option is deprecated and does nothing +//[bad_val]~? ERROR incorrect value `asd` for codegen option `inline-threshold` +//[no_val]~? WARN `-C inline-threshold`: this option is deprecated and does nothing +//[no_val]~? ERROR codegen option `inline-threshold` requires a number diff --git a/tests/ui/deprecation/deprecated_no_stack_check_opt.rs b/tests/ui/deprecation/deprecated_no_stack_check_opt.rs index 62584ec23e336..e014c7e80affc 100644 --- a/tests/ui/deprecation/deprecated_no_stack_check_opt.rs +++ b/tests/ui/deprecation/deprecated_no_stack_check_opt.rs @@ -2,3 +2,5 @@ //@ compile-flags: -Cno-stack-check fn main() {} + +//~? WARN `-C no-stack-check`: this option is deprecated and does nothing diff --git a/tests/ui/deprecation/deprecation-lint-2.rs b/tests/ui/deprecation/deprecation-lint-2.rs index 553b1afe45caa..f22eebb53a024 100644 --- a/tests/ui/deprecation/deprecation-lint-2.rs +++ b/tests/ui/deprecation/deprecation-lint-2.rs @@ -1,5 +1,4 @@ //@ aux-build:deprecation-lint.rs -//@ error-pattern: use of deprecated function #![deny(deprecated)] @@ -9,5 +8,5 @@ extern crate deprecation_lint; use deprecation_lint::*; fn main() { - macro_test!(); + macro_test!(); //~ ERROR use of deprecated function `deprecation_lint::deprecated`: text } diff --git a/tests/ui/deprecation/deprecation-lint-2.stderr b/tests/ui/deprecation/deprecation-lint-2.stderr index 7d411c00445ce..ebc760bfe7bf1 100644 --- a/tests/ui/deprecation/deprecation-lint-2.stderr +++ b/tests/ui/deprecation/deprecation-lint-2.stderr @@ -1,11 +1,11 @@ error: use of deprecated function `deprecation_lint::deprecated`: text - --> $DIR/deprecation-lint-2.rs:12:5 + --> $DIR/deprecation-lint-2.rs:11:5 | LL | macro_test!(); | ^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/deprecation-lint-2.rs:4:9 + --> $DIR/deprecation-lint-2.rs:3:9 | LL | #![deny(deprecated)] | ^^^^^^^^^^ diff --git a/tests/ui/deprecation/deprecation-lint-3.rs b/tests/ui/deprecation/deprecation-lint-3.rs index f01fc9244570a..5a9ca9a2d0a64 100644 --- a/tests/ui/deprecation/deprecation-lint-3.rs +++ b/tests/ui/deprecation/deprecation-lint-3.rs @@ -1,5 +1,4 @@ //@ aux-build:deprecation-lint.rs -//@ error-pattern: use of deprecated function #![deny(deprecated)] #![allow(warnings)] @@ -11,4 +10,5 @@ use deprecation_lint::*; fn main() { macro_test_arg_nested!(deprecated_text); + //~^ ERROR use of deprecated function `deprecation_lint::deprecated_text`: text } diff --git a/tests/ui/deprecation/deprecation-lint-3.stderr b/tests/ui/deprecation/deprecation-lint-3.stderr index 1723b7bbd0535..8bfe08cf6d50c 100644 --- a/tests/ui/deprecation/deprecation-lint-3.stderr +++ b/tests/ui/deprecation/deprecation-lint-3.stderr @@ -1,11 +1,11 @@ error: use of deprecated function `deprecation_lint::deprecated_text`: text - --> $DIR/deprecation-lint-3.rs:13:28 + --> $DIR/deprecation-lint-3.rs:12:28 | LL | macro_test_arg_nested!(deprecated_text); | ^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/deprecation-lint-3.rs:4:9 + --> $DIR/deprecation-lint-3.rs:3:9 | LL | #![deny(deprecated)] | ^^^^^^^^^^ diff --git a/tests/ui/deprecation/try-macro-suggestion.rs b/tests/ui/deprecation/try-macro-suggestion.rs index 1e477ab9c88f9..0775f0011001f 100644 --- a/tests/ui/deprecation/try-macro-suggestion.rs +++ b/tests/ui/deprecation/try-macro-suggestion.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 fn foo() -> Result<(), ()> { Ok(try!()); //~ ERROR use of deprecated `try` macro Ok(try!(Ok(()))) //~ ERROR use of deprecated `try` macro diff --git a/tests/ui/deref-non-pointer.rs b/tests/ui/deref-patterns/deref-non-pointer.rs similarity index 100% rename from tests/ui/deref-non-pointer.rs rename to tests/ui/deref-patterns/deref-non-pointer.rs diff --git a/tests/ui/deref-non-pointer.stderr b/tests/ui/deref-patterns/deref-non-pointer.stderr similarity index 100% rename from tests/ui/deref-non-pointer.stderr rename to tests/ui/deref-patterns/deref-non-pointer.stderr diff --git a/tests/ui/deref-patterns/gate.rs b/tests/ui/deref-patterns/gate.rs index ff50e30dea8c8..835fdf854d2c4 100644 --- a/tests/ui/deref-patterns/gate.rs +++ b/tests/ui/deref-patterns/gate.rs @@ -2,6 +2,6 @@ fn main() { match String::new() { "" | _ => {} - //~^ mismatched types + //~^ ERROR mismatched types } } diff --git a/tests/ui/derives/derives-span-Clone-enum-struct-variant.stderr b/tests/ui/derives/derives-span-Clone-enum-struct-variant.stderr index 2c8d9431646d6..9aeb3a8c03d25 100644 --- a/tests/ui/derives/derives-span-Clone-enum-struct-variant.stderr +++ b/tests/ui/derives/derives-span-Clone-enum-struct-variant.stderr @@ -7,7 +7,6 @@ LL | #[derive(Clone)] LL | x: Error | ^^^^^^^^ the trait `Clone` is not implemented for `Error` | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/derives/derives-span-Clone-enum.stderr b/tests/ui/derives/derives-span-Clone-enum.stderr index b683a8b89441a..6b4f224166318 100644 --- a/tests/ui/derives/derives-span-Clone-enum.stderr +++ b/tests/ui/derives/derives-span-Clone-enum.stderr @@ -7,7 +7,6 @@ LL | #[derive(Clone)] LL | Error | ^^^^^ the trait `Clone` is not implemented for `Error` | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/derives/derives-span-Clone-struct.stderr b/tests/ui/derives/derives-span-Clone-struct.stderr index 305a92752716f..17e3f0eccf92b 100644 --- a/tests/ui/derives/derives-span-Clone-struct.stderr +++ b/tests/ui/derives/derives-span-Clone-struct.stderr @@ -7,7 +7,6 @@ LL | struct Struct { LL | x: Error | ^^^^^^^^ the trait `Clone` is not implemented for `Error` | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/derives/derives-span-Clone-tuple-struct.stderr b/tests/ui/derives/derives-span-Clone-tuple-struct.stderr index b636404ad9ed7..f8380d197b06e 100644 --- a/tests/ui/derives/derives-span-Clone-tuple-struct.stderr +++ b/tests/ui/derives/derives-span-Clone-tuple-struct.stderr @@ -7,7 +7,6 @@ LL | struct Struct( LL | Error | ^^^^^ the trait `Clone` is not implemented for `Error` | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/derives/derives-span-Debug-enum-struct-variant.stderr b/tests/ui/derives/derives-span-Debug-enum-struct-variant.stderr index 3f6c39bf9396e..a7f6d094681a1 100644 --- a/tests/ui/derives/derives-span-Debug-enum-struct-variant.stderr +++ b/tests/ui/derives/derives-span-Debug-enum-struct-variant.stderr @@ -9,7 +9,6 @@ LL | x: Error | = help: the trait `Debug` is not implemented for `Error` = note: add `#[derive(Debug)]` to `Error` or manually `impl Debug for Error` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/derives/derives-span-Debug-enum.stderr b/tests/ui/derives/derives-span-Debug-enum.stderr index eaeffaeb84956..b3a5847815983 100644 --- a/tests/ui/derives/derives-span-Debug-enum.stderr +++ b/tests/ui/derives/derives-span-Debug-enum.stderr @@ -9,7 +9,6 @@ LL | Error | = help: the trait `Debug` is not implemented for `Error` = note: add `#[derive(Debug)]` to `Error` or manually `impl Debug for Error` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/derives/derives-span-Debug-struct.stderr b/tests/ui/derives/derives-span-Debug-struct.stderr index 4a725e260deae..c8ad652716caf 100644 --- a/tests/ui/derives/derives-span-Debug-struct.stderr +++ b/tests/ui/derives/derives-span-Debug-struct.stderr @@ -9,7 +9,6 @@ LL | x: Error | = help: the trait `Debug` is not implemented for `Error` = note: add `#[derive(Debug)]` to `Error` or manually `impl Debug for Error` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/derives/derives-span-Debug-tuple-struct.stderr b/tests/ui/derives/derives-span-Debug-tuple-struct.stderr index 2f816e1c85b25..dbece4d2091b2 100644 --- a/tests/ui/derives/derives-span-Debug-tuple-struct.stderr +++ b/tests/ui/derives/derives-span-Debug-tuple-struct.stderr @@ -9,7 +9,6 @@ LL | Error | = help: the trait `Debug` is not implemented for `Error` = note: add `#[derive(Debug)]` to `Error` or manually `impl Debug for Error` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/derives/derives-span-Default-struct.stderr b/tests/ui/derives/derives-span-Default-struct.stderr index 359b61528e183..442081f78ce7b 100644 --- a/tests/ui/derives/derives-span-Default-struct.stderr +++ b/tests/ui/derives/derives-span-Default-struct.stderr @@ -7,7 +7,6 @@ LL | struct Struct { LL | x: Error | ^^^^^^^^ the trait `Default` is not implemented for `Error` | - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Default)]` | LL + #[derive(Default)] diff --git a/tests/ui/derives/derives-span-Default-tuple-struct.stderr b/tests/ui/derives/derives-span-Default-tuple-struct.stderr index 1ddb4ec3f642e..e786ec1e9dabc 100644 --- a/tests/ui/derives/derives-span-Default-tuple-struct.stderr +++ b/tests/ui/derives/derives-span-Default-tuple-struct.stderr @@ -7,7 +7,6 @@ LL | struct Struct( LL | Error | ^^^^^ the trait `Default` is not implemented for `Error` | - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Default)]` | LL + #[derive(Default)] diff --git a/tests/ui/derives/derives-span-Eq-enum-struct-variant.stderr b/tests/ui/derives/derives-span-Eq-enum-struct-variant.stderr index c9edc89e1bc56..e0cb3c1b43da8 100644 --- a/tests/ui/derives/derives-span-Eq-enum-struct-variant.stderr +++ b/tests/ui/derives/derives-span-Eq-enum-struct-variant.stderr @@ -9,7 +9,6 @@ LL | x: Error | note: required by a bound in `AssertParamIsEq` --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Eq)]` | LL + #[derive(Eq)] diff --git a/tests/ui/derives/derives-span-Eq-enum.stderr b/tests/ui/derives/derives-span-Eq-enum.stderr index 7db13e9711141..2f09b9ea385fa 100644 --- a/tests/ui/derives/derives-span-Eq-enum.stderr +++ b/tests/ui/derives/derives-span-Eq-enum.stderr @@ -9,7 +9,6 @@ LL | Error | note: required by a bound in `AssertParamIsEq` --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Eq)]` | LL + #[derive(Eq)] diff --git a/tests/ui/derives/derives-span-Eq-struct.stderr b/tests/ui/derives/derives-span-Eq-struct.stderr index 36eeb89bde18e..c16d9118e10fe 100644 --- a/tests/ui/derives/derives-span-Eq-struct.stderr +++ b/tests/ui/derives/derives-span-Eq-struct.stderr @@ -9,7 +9,6 @@ LL | x: Error | note: required by a bound in `AssertParamIsEq` --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Eq)]` | LL + #[derive(Eq)] diff --git a/tests/ui/derives/derives-span-Eq-tuple-struct.stderr b/tests/ui/derives/derives-span-Eq-tuple-struct.stderr index 126d105354066..dac295eed9195 100644 --- a/tests/ui/derives/derives-span-Eq-tuple-struct.stderr +++ b/tests/ui/derives/derives-span-Eq-tuple-struct.stderr @@ -9,7 +9,6 @@ LL | Error | note: required by a bound in `AssertParamIsEq` --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Eq)]` | LL + #[derive(Eq)] diff --git a/tests/ui/derives/derives-span-Hash-enum-struct-variant.stderr b/tests/ui/derives/derives-span-Hash-enum-struct-variant.stderr index ae973228cacbc..9854b61d31bb2 100644 --- a/tests/ui/derives/derives-span-Hash-enum-struct-variant.stderr +++ b/tests/ui/derives/derives-span-Hash-enum-struct-variant.stderr @@ -7,7 +7,6 @@ LL | #[derive(Hash)] LL | x: Error | ^^^^^^^^ the trait `Hash` is not implemented for `Error` | - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Hash)]` | LL + #[derive(Hash)] diff --git a/tests/ui/derives/derives-span-Hash-enum.stderr b/tests/ui/derives/derives-span-Hash-enum.stderr index 85e26c84fa7a1..60e7d0cf1ff9e 100644 --- a/tests/ui/derives/derives-span-Hash-enum.stderr +++ b/tests/ui/derives/derives-span-Hash-enum.stderr @@ -7,7 +7,6 @@ LL | #[derive(Hash)] LL | Error | ^^^^^ the trait `Hash` is not implemented for `Error` | - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Hash)]` | LL + #[derive(Hash)] diff --git a/tests/ui/derives/derives-span-Hash-struct.stderr b/tests/ui/derives/derives-span-Hash-struct.stderr index f9a654b2df798..3851cf8ce8b0a 100644 --- a/tests/ui/derives/derives-span-Hash-struct.stderr +++ b/tests/ui/derives/derives-span-Hash-struct.stderr @@ -7,7 +7,6 @@ LL | struct Struct { LL | x: Error | ^^^^^^^^ the trait `Hash` is not implemented for `Error` | - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Hash)]` | LL + #[derive(Hash)] diff --git a/tests/ui/derives/derives-span-Hash-tuple-struct.stderr b/tests/ui/derives/derives-span-Hash-tuple-struct.stderr index 0a5fbe286587b..e5a3e7a556640 100644 --- a/tests/ui/derives/derives-span-Hash-tuple-struct.stderr +++ b/tests/ui/derives/derives-span-Hash-tuple-struct.stderr @@ -7,7 +7,6 @@ LL | struct Struct( LL | Error | ^^^^^ the trait `Hash` is not implemented for `Error` | - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Hash)]` | LL + #[derive(Hash)] diff --git a/tests/ui/derives/derives-span-Ord-enum-struct-variant.stderr b/tests/ui/derives/derives-span-Ord-enum-struct-variant.stderr index 96ef59ca9631b..e253a9173fc55 100644 --- a/tests/ui/derives/derives-span-Ord-enum-struct-variant.stderr +++ b/tests/ui/derives/derives-span-Ord-enum-struct-variant.stderr @@ -7,7 +7,6 @@ LL | #[derive(Ord,Eq,PartialOrd,PartialEq)] LL | x: Error | ^^^^^^^^ the trait `Ord` is not implemented for `Error` | - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Ord)]` | LL + #[derive(Ord)] diff --git a/tests/ui/derives/derives-span-Ord-enum.stderr b/tests/ui/derives/derives-span-Ord-enum.stderr index 7c75ecb6432e2..8a92458b51ca6 100644 --- a/tests/ui/derives/derives-span-Ord-enum.stderr +++ b/tests/ui/derives/derives-span-Ord-enum.stderr @@ -7,7 +7,6 @@ LL | #[derive(Ord,Eq,PartialOrd,PartialEq)] LL | Error | ^^^^^ the trait `Ord` is not implemented for `Error` | - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Ord)]` | LL + #[derive(Ord)] diff --git a/tests/ui/derives/derives-span-Ord-struct.stderr b/tests/ui/derives/derives-span-Ord-struct.stderr index 429e7e06f5d9f..ae110d51196fc 100644 --- a/tests/ui/derives/derives-span-Ord-struct.stderr +++ b/tests/ui/derives/derives-span-Ord-struct.stderr @@ -7,7 +7,6 @@ LL | struct Struct { LL | x: Error | ^^^^^^^^ the trait `Ord` is not implemented for `Error` | - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Ord)]` | LL + #[derive(Ord)] diff --git a/tests/ui/derives/derives-span-Ord-tuple-struct.stderr b/tests/ui/derives/derives-span-Ord-tuple-struct.stderr index a46133834c6ad..e2a6d5266b71e 100644 --- a/tests/ui/derives/derives-span-Ord-tuple-struct.stderr +++ b/tests/ui/derives/derives-span-Ord-tuple-struct.stderr @@ -7,7 +7,6 @@ LL | struct Struct( LL | Error | ^^^^^ the trait `Ord` is not implemented for `Error` | - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(Ord)]` | LL + #[derive(Ord)] diff --git a/tests/ui/derives/derives-span-PartialEq-enum-struct-variant.stderr b/tests/ui/derives/derives-span-PartialEq-enum-struct-variant.stderr index e88a523ef4f17..09104289c64ac 100644 --- a/tests/ui/derives/derives-span-PartialEq-enum-struct-variant.stderr +++ b/tests/ui/derives/derives-span-PartialEq-enum-struct-variant.stderr @@ -12,7 +12,6 @@ note: an implementation of `PartialEq` might be missing for `Error` | LL | struct Error; | ^^^^^^^^^^^^ must implement `PartialEq` - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(PartialEq)]` | LL + #[derive(PartialEq)] diff --git a/tests/ui/derives/derives-span-PartialEq-enum.stderr b/tests/ui/derives/derives-span-PartialEq-enum.stderr index 80b225446b47d..40dca92ef23cf 100644 --- a/tests/ui/derives/derives-span-PartialEq-enum.stderr +++ b/tests/ui/derives/derives-span-PartialEq-enum.stderr @@ -12,7 +12,6 @@ note: an implementation of `PartialEq` might be missing for `Error` | LL | struct Error; | ^^^^^^^^^^^^ must implement `PartialEq` - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(PartialEq)]` | LL + #[derive(PartialEq)] diff --git a/tests/ui/derives/derives-span-PartialEq-struct.stderr b/tests/ui/derives/derives-span-PartialEq-struct.stderr index d7fc3da46e0ee..cfd9fc41d061d 100644 --- a/tests/ui/derives/derives-span-PartialEq-struct.stderr +++ b/tests/ui/derives/derives-span-PartialEq-struct.stderr @@ -12,7 +12,6 @@ note: an implementation of `PartialEq` might be missing for `Error` | LL | struct Error; | ^^^^^^^^^^^^ must implement `PartialEq` - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(PartialEq)]` | LL + #[derive(PartialEq)] diff --git a/tests/ui/derives/derives-span-PartialEq-tuple-struct.stderr b/tests/ui/derives/derives-span-PartialEq-tuple-struct.stderr index ea3920f406ce8..4e35cbb534d94 100644 --- a/tests/ui/derives/derives-span-PartialEq-tuple-struct.stderr +++ b/tests/ui/derives/derives-span-PartialEq-tuple-struct.stderr @@ -12,7 +12,6 @@ note: an implementation of `PartialEq` might be missing for `Error` | LL | struct Error; | ^^^^^^^^^^^^ must implement `PartialEq` - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(PartialEq)]` | LL + #[derive(PartialEq)] diff --git a/tests/ui/derives/derives-span-PartialOrd-enum-struct-variant.stderr b/tests/ui/derives/derives-span-PartialOrd-enum-struct-variant.stderr index 3f83eb56aaf20..7fe09d45cc1bb 100644 --- a/tests/ui/derives/derives-span-PartialOrd-enum-struct-variant.stderr +++ b/tests/ui/derives/derives-span-PartialOrd-enum-struct-variant.stderr @@ -8,7 +8,6 @@ LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` | = help: the trait `PartialOrd` is not implemented for `Error` - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(PartialOrd)]` | LL + #[derive(PartialOrd)] diff --git a/tests/ui/derives/derives-span-PartialOrd-enum.stderr b/tests/ui/derives/derives-span-PartialOrd-enum.stderr index cf5915173c5ef..2a14e6e961870 100644 --- a/tests/ui/derives/derives-span-PartialOrd-enum.stderr +++ b/tests/ui/derives/derives-span-PartialOrd-enum.stderr @@ -8,7 +8,6 @@ LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` | = help: the trait `PartialOrd` is not implemented for `Error` - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(PartialOrd)]` | LL + #[derive(PartialOrd)] diff --git a/tests/ui/derives/derives-span-PartialOrd-struct.stderr b/tests/ui/derives/derives-span-PartialOrd-struct.stderr index de21a903b6ca2..0904e3ace8f0f 100644 --- a/tests/ui/derives/derives-span-PartialOrd-struct.stderr +++ b/tests/ui/derives/derives-span-PartialOrd-struct.stderr @@ -8,7 +8,6 @@ LL | x: Error | ^^^^^^^^ no implementation for `Error < Error` and `Error > Error` | = help: the trait `PartialOrd` is not implemented for `Error` - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(PartialOrd)]` | LL + #[derive(PartialOrd)] diff --git a/tests/ui/derives/derives-span-PartialOrd-tuple-struct.stderr b/tests/ui/derives/derives-span-PartialOrd-tuple-struct.stderr index 3050aeecc0dae..501fea02e8efc 100644 --- a/tests/ui/derives/derives-span-PartialOrd-tuple-struct.stderr +++ b/tests/ui/derives/derives-span-PartialOrd-tuple-struct.stderr @@ -8,7 +8,6 @@ LL | Error | ^^^^^ no implementation for `Error < Error` and `Error > Error` | = help: the trait `PartialOrd` is not implemented for `Error` - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Error` with `#[derive(PartialOrd)]` | LL + #[derive(PartialOrd)] diff --git a/tests/ui/derives/deriving-copyclone.stderr b/tests/ui/derives/deriving-copyclone.stderr index c0c2215c04adb..befff8802804e 100644 --- a/tests/ui/derives/deriving-copyclone.stderr +++ b/tests/ui/derives/deriving-copyclone.stderr @@ -16,7 +16,6 @@ note: required by a bound in `is_copy` | LL | fn is_copy(_: T) {} | ^^^^ required by this bound in `is_copy` - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider borrowing here | LL | is_copy(B { a: 1, b: &C }); @@ -40,7 +39,6 @@ note: required by a bound in `is_clone` | LL | fn is_clone(_: T) {} | ^^^^^ required by this bound in `is_clone` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider borrowing here | LL | is_clone(B { a: 1, b: &C }); @@ -64,7 +62,6 @@ note: required by a bound in `is_copy` | LL | fn is_copy(_: T) {} | ^^^^ required by this bound in `is_copy` - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider borrowing here | LL | is_copy(B { a: 1, b: &D }); diff --git a/tests/ui/derives/deriving-no-inner-impl-error-message.stderr b/tests/ui/derives/deriving-no-inner-impl-error-message.stderr index ab99ba9fab5a6..294f700d9de48 100644 --- a/tests/ui/derives/deriving-no-inner-impl-error-message.stderr +++ b/tests/ui/derives/deriving-no-inner-impl-error-message.stderr @@ -12,7 +12,6 @@ note: an implementation of `PartialEq` might be missing for `NoCloneOrEq` | LL | struct NoCloneOrEq; | ^^^^^^^^^^^^^^^^^^ must implement `PartialEq` - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `NoCloneOrEq` with `#[derive(PartialEq)]` | LL + #[derive(PartialEq)] @@ -28,7 +27,6 @@ LL | struct C { LL | x: NoCloneOrEq | ^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NoCloneOrEq` | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `NoCloneOrEq` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/derives/deriving-with-repr-packed-move-errors.rs b/tests/ui/derives/deriving-with-repr-packed-move-errors.rs index ffeb02d78b848..17aa750332c40 100644 --- a/tests/ui/derives/deriving-with-repr-packed-move-errors.rs +++ b/tests/ui/derives/deriving-with-repr-packed-move-errors.rs @@ -11,15 +11,15 @@ use std::cmp::Ordering; #[repr(packed)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] struct StructA(String); -//~^ ERROR: cannot move out of `self` which is behind a shared reference -//~| ERROR: cannot move out of `self` which is behind a shared reference -//~| ERROR: cannot move out of `other` which is behind a shared reference -//~| ERROR: cannot move out of `self` which is behind a shared reference -//~| ERROR: cannot move out of `other` which is behind a shared reference -//~| ERROR: cannot move out of `self` which is behind a shared reference -//~| ERROR: cannot move out of `other` which is behind a shared reference -//~| ERROR: cannot move out of `self` which is behind a shared reference -//~| ERROR: cannot move out of `self` which is behind a shared reference +//~^ ERROR: cannot move out of a shared reference [E0507] +//~| ERROR: cannot move out of a shared reference [E0507] +//~| ERROR: cannot move out of a shared reference [E0507] +//~| ERROR: cannot move out of a shared reference [E0507] +//~| ERROR: cannot move out of a shared reference [E0507] +//~| ERROR: cannot move out of a shared reference [E0507] +//~| ERROR: cannot move out of a shared reference [E0507] +//~| ERROR: cannot move out of a shared reference [E0507] +//~| ERROR: cannot move out of a shared reference [E0507] // Unrelated impl: additinal diagnostic should NOT be emitted diff --git a/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr b/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr index 2de4ee4eabd58..4b085425033d9 100644 --- a/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr +++ b/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr @@ -1,133 +1,127 @@ -error[E0507]: cannot move out of `self` which is behind a shared reference +error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 | LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] | ----- in this derive macro expansion LL | struct StructA(String); - | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait | = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | LL | struct StructA(String.clone()); | ++++++++ -error[E0507]: cannot move out of `self` which is behind a shared reference +error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 | LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] | --------- in this derive macro expansion LL | struct StructA(String); - | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait | = note: `#[derive(PartialEq)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | LL | struct StructA(String.clone()); | ++++++++ -error[E0507]: cannot move out of `other` which is behind a shared reference +error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 | LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] | --------- in this derive macro expansion LL | struct StructA(String); - | ^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait + | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait | = note: `#[derive(PartialEq)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider cloning the value if the performance cost is acceptable | LL | struct StructA(String.clone()); | ++++++++ -error[E0507]: cannot move out of `self` which is behind a shared reference +error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 | LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] | ---------- in this derive macro expansion LL | struct StructA(String); - | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait | = note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | LL | struct StructA(String.clone()); | ++++++++ -error[E0507]: cannot move out of `other` which is behind a shared reference +error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 | LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] | ---------- in this derive macro expansion LL | struct StructA(String); - | ^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait + | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait | = note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider cloning the value if the performance cost is acceptable | LL | struct StructA(String.clone()); | ++++++++ -error[E0507]: cannot move out of `self` which is behind a shared reference +error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 | LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] | --- in this derive macro expansion LL | struct StructA(String); - | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait | = note: `#[derive(Ord)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | LL | struct StructA(String.clone()); | ++++++++ -error[E0507]: cannot move out of `other` which is behind a shared reference +error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 | LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] | --- in this derive macro expansion LL | struct StructA(String); - | ^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait + | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait | = note: `#[derive(Ord)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider cloning the value if the performance cost is acceptable | LL | struct StructA(String.clone()); | ++++++++ -error[E0507]: cannot move out of `self` which is behind a shared reference +error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 | LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] | ---- in this derive macro expansion LL | struct StructA(String); - | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait | = note: `#[derive(Hash)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | LL | struct StructA(String.clone()); | ++++++++ -error[E0507]: cannot move out of `self` which is behind a shared reference +error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 | LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] | ----- in this derive macro expansion LL | struct StructA(String); - | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait | = note: `#[derive(Clone)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | LL | struct StructA(String.clone()); diff --git a/tests/ui/derives/deriving-with-repr-packed.rs b/tests/ui/derives/deriving-with-repr-packed.rs index d17b52842cefd..cba0413c992e6 100644 --- a/tests/ui/derives/deriving-with-repr-packed.rs +++ b/tests/ui/derives/deriving-with-repr-packed.rs @@ -20,7 +20,7 @@ struct Y(usize); #[derive(Debug, Default)] #[repr(packed)] struct X(Y); -//~^ ERROR cannot move out of `self` which is behind a shared reference +//~^ ERROR cannot move out of a shared reference [E0507] #[derive(Debug)] #[repr(packed)] diff --git a/tests/ui/derives/deriving-with-repr-packed.stderr b/tests/ui/derives/deriving-with-repr-packed.stderr index 321ffb27eeb11..9cfc4abdc0cb5 100644 --- a/tests/ui/derives/deriving-with-repr-packed.stderr +++ b/tests/ui/derives/deriving-with-repr-packed.stderr @@ -1,11 +1,11 @@ -error[E0507]: cannot move out of `self` which is behind a shared reference +error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed.rs:22:10 | LL | #[derive(Debug, Default)] | ----- in this derive macro expansion LL | #[repr(packed)] LL | struct X(Y); - | ^ move occurs because `self.0` has type `Y`, which does not implement the `Copy` trait + | ^ move occurs because value has type `Y`, which does not implement the `Copy` trait | note: if `Y` implemented `Clone`, you could clone the value --> $DIR/deriving-with-repr-packed.rs:16:1 @@ -16,7 +16,6 @@ LL | struct Y(usize); LL | struct X(Y); | - you could clone this value = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0161]: cannot move a value of type `[u8]` --> $DIR/deriving-with-repr-packed.rs:29:5 @@ -24,17 +23,16 @@ error[E0161]: cannot move a value of type `[u8]` LL | data: [u8], | ^^^^^^^^^^ the size of `[u8]` cannot be statically determined -error[E0507]: cannot move out of `self.data` which is behind a shared reference +error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed.rs:29:5 | LL | #[derive(Debug)] | ----- in this derive macro expansion ... LL | data: [u8], - | ^^^^^^^^^^ move occurs because `self.data` has type `[u8]`, which does not implement the `Copy` trait + | ^^^^^^^^^^ move occurs because value has type `[u8]`, which does not implement the `Copy` trait | = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0161]: cannot move a value of type `str` --> $DIR/deriving-with-repr-packed.rs:38:5 @@ -42,17 +40,16 @@ error[E0161]: cannot move a value of type `str` LL | data: str, | ^^^^^^^^^ the size of `str` cannot be statically determined -error[E0507]: cannot move out of `self.data` which is behind a shared reference +error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed.rs:38:5 | LL | #[derive(Debug)] | ----- in this derive macro expansion ... LL | data: str, - | ^^^^^^^^^ move occurs because `self.data` has type `str`, which does not implement the `Copy` trait + | ^^^^^^^^^ move occurs because value has type `str`, which does not implement the `Copy` trait | = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 5 previous errors diff --git a/tests/ui/derives/duplicate-derive-copy-clone-diagnostics.stderr b/tests/ui/derives/duplicate-derive-copy-clone-diagnostics.stderr index f8e1db33f5363..b9eb6194f6c4c 100644 --- a/tests/ui/derives/duplicate-derive-copy-clone-diagnostics.stderr +++ b/tests/ui/derives/duplicate-derive-copy-clone-diagnostics.stderr @@ -5,8 +5,6 @@ LL | #[derive(Copy, Clone)] | ---- first implementation here LL | #[derive(Copy, Clone)] | ^^^^ conflicting implementation for `E` - | - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0119]: conflicting implementations of trait `Clone` for type `E` --> $DIR/duplicate-derive-copy-clone-diagnostics.rs:6:16 @@ -15,8 +13,6 @@ LL | #[derive(Copy, Clone)] | ----- first implementation here LL | #[derive(Copy, Clone)] | ^^^^^ conflicting implementation for `E` - | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/derives/issue-97343.stderr b/tests/ui/derives/issue-97343.stderr index efb2fb70f5a51..27691f5718849 100644 --- a/tests/ui/derives/issue-97343.stderr +++ b/tests/ui/derives/issue-97343.stderr @@ -14,7 +14,6 @@ note: type parameter `Irrelevant` defined here | LL | pub struct Irrelevant { | ^^^^^^^^^^ - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/deriving/auxiliary/malicious-macro.rs b/tests/ui/deriving/auxiliary/malicious-macro.rs index 6665b40a14f2b..c551b3f927c62 100644 --- a/tests/ui/deriving/auxiliary/malicious-macro.rs +++ b/tests/ui/deriving/auxiliary/malicious-macro.rs @@ -1,4 +1,4 @@ -#![feature(let_chains)] +//@ edition: 2024 extern crate proc_macro; diff --git a/tests/ui/deriving/built-in-proc-macro-scope.rs b/tests/ui/deriving/built-in-proc-macro-scope.rs index e67197b7e2051..69128a08b9978 100644 --- a/tests/ui/deriving/built-in-proc-macro-scope.rs +++ b/tests/ui/deriving/built-in-proc-macro-scope.rs @@ -1,6 +1,7 @@ //@ check-pass //@ proc-macro: another-proc-macro.rs //@ compile-flags: -Zunpretty=expanded +//@ edition:2015 #![feature(derive_coerce_pointee)] diff --git a/tests/ui/deriving/built-in-proc-macro-scope.stdout b/tests/ui/deriving/built-in-proc-macro-scope.stdout index fa4e50968f4de..2697618ab0035 100644 --- a/tests/ui/deriving/built-in-proc-macro-scope.stdout +++ b/tests/ui/deriving/built-in-proc-macro-scope.stdout @@ -3,6 +3,7 @@ //@ check-pass //@ proc-macro: another-proc-macro.rs //@ compile-flags: -Zunpretty=expanded +//@ edition:2015 #![feature(derive_coerce_pointee)] #[prelude_import] diff --git a/tests/ui/deriving/deriving-coerce-pointee-expanded.rs b/tests/ui/deriving/deriving-coerce-pointee-expanded.rs index 94be7031fb77f..9394ae4efe5ae 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-expanded.rs +++ b/tests/ui/deriving/deriving-coerce-pointee-expanded.rs @@ -1,5 +1,6 @@ //@ check-pass //@ compile-flags: -Zunpretty=expanded +//@ edition: 2015 #![feature(derive_coerce_pointee)] use std::marker::CoercePointee; diff --git a/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout b/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout index a774efbbe354b..84f8e9a3195a7 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout +++ b/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout @@ -2,6 +2,7 @@ #![no_std] //@ check-pass //@ compile-flags: -Zunpretty=expanded +//@ edition: 2015 #![feature(derive_coerce_pointee)] #[prelude_import] use ::std::prelude::rust_2015::*; diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr index d3d731320781b..6c6e631287528 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr +++ b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr @@ -3,40 +3,30 @@ error[E0802]: `CoercePointee` can only be derived on `struct`s with `#[repr(tran | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ - | - = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0802]: `CoercePointee` can only be derived on `struct`s with at least one field --> $DIR/deriving-coerce-pointee-neg.rs:15:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ - | - = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0802]: `CoercePointee` can only be derived on `struct`s with at least one field --> $DIR/deriving-coerce-pointee-neg.rs:22:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ - | - = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0802]: `CoercePointee` can only be derived on `struct`s that are generic over at least one type --> $DIR/deriving-coerce-pointee-neg.rs:29:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ - | - = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0802]: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits --> $DIR/deriving-coerce-pointee-neg.rs:34:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ - | - = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0802]: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits --> $DIR/deriving-coerce-pointee-neg.rs:43:39 @@ -126,8 +116,6 @@ LL | #[derive(CoercePointee)] ... LL | inner: std::rc::Rc<(i32, Box)>, | --------------------------------- `Rc<(i32, Box)>` must be a pointer, reference, or smart pointer that is allowed to be unsized - | - = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0375]: implementing `CoerceUnsized` does not allow multiple fields to be coerced --> $DIR/deriving-coerce-pointee-neg.rs:153:10 @@ -142,7 +130,6 @@ LL | inner1: Box, | ^^^^^^^^^^^^^^ LL | inner2: Box, | ^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error: for `UsingNonCoercePointeeData` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `NotCoercePointeeData` --> $DIR/deriving-coerce-pointee-neg.rs:164:10 @@ -152,8 +139,6 @@ LL | #[derive(CoercePointee)] LL | LL | struct UsingNonCoercePointeeData(NotCoercePointeeData); | ----------------------- `NotCoercePointeeData` must be a pointer, reference, or smart pointer that is allowed to be unsized - | - = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2 --> $DIR/deriving-coerce-pointee-neg.rs:155:1 diff --git a/tests/ui/deriving/do-not-suggest-calling-fn-in-derive-macro.stderr b/tests/ui/deriving/do-not-suggest-calling-fn-in-derive-macro.stderr index 40464a49c3438..af78f64546310 100644 --- a/tests/ui/deriving/do-not-suggest-calling-fn-in-derive-macro.stderr +++ b/tests/ui/deriving/do-not-suggest-calling-fn-in-derive-macro.stderr @@ -6,8 +6,6 @@ LL | #[derive(PartialEq)] LL | pub struct Function { LL | callback: Rc, | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/deriving/issue-103157.stderr b/tests/ui/deriving/issue-103157.stderr index 9754b0289c07b..51d4d0a897456 100644 --- a/tests/ui/deriving/issue-103157.stderr +++ b/tests/ui/deriving/issue-103157.stderr @@ -20,7 +20,6 @@ LL | Float(Option), = note: required for `Option` to implement `Eq` note: required by a bound in `AssertParamIsEq` --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/deriving/multiple-defaults.stderr b/tests/ui/deriving/multiple-defaults.stderr index 05fb6fecffaec..a662ed248fe6e 100644 --- a/tests/ui/deriving/multiple-defaults.stderr +++ b/tests/ui/deriving/multiple-defaults.stderr @@ -11,7 +11,6 @@ LL | B, | - additional default | = note: only one variant can be default - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error: multiple declared defaults --> $DIR/multiple-defaults.rs:18:10 @@ -26,7 +25,6 @@ LL | A, | - additional default | = note: only one variant can be default - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0428]: the name `A` is defined multiple times --> $DIR/multiple-defaults.rs:23:5 diff --git a/tests/ui/deriving/proc-macro-attribute-mixing.rs b/tests/ui/deriving/proc-macro-attribute-mixing.rs index 2c11c3f72ca59..c9e123d7e8a12 100644 --- a/tests/ui/deriving/proc-macro-attribute-mixing.rs +++ b/tests/ui/deriving/proc-macro-attribute-mixing.rs @@ -7,6 +7,7 @@ //@ check-pass //@ proc-macro: another-proc-macro.rs //@ compile-flags: -Zunpretty=expanded +//@ edition: 2015 #![feature(derive_coerce_pointee)] diff --git a/tests/ui/deriving/proc-macro-attribute-mixing.stdout b/tests/ui/deriving/proc-macro-attribute-mixing.stdout index ad743d013d25d..faa9c0218a33c 100644 --- a/tests/ui/deriving/proc-macro-attribute-mixing.stdout +++ b/tests/ui/deriving/proc-macro-attribute-mixing.stdout @@ -9,6 +9,7 @@ //@ check-pass //@ proc-macro: another-proc-macro.rs //@ compile-flags: -Zunpretty=expanded +//@ edition: 2015 #![feature(derive_coerce_pointee)] #[prelude_import] diff --git a/tests/ui/destructure-trait-ref.rs b/tests/ui/destructure-trait-ref.rs index 50b64aeebf0ef..daa0ca30d6875 100644 --- a/tests/ui/destructure-trait-ref.rs +++ b/tests/ui/destructure-trait-ref.rs @@ -1,6 +1,8 @@ // The regression test for #15031 to make sure destructuring trait // reference work properly. +//@ dont-require-annotations: NOTE + #![feature(box_patterns)] trait T { fn foo(&self) {} } @@ -31,14 +33,14 @@ fn main() { // n > m let &&x = &1isize as &dyn T; //~^ ERROR mismatched types - //~| expected trait object `dyn T` - //~| found reference `&_` + //~| NOTE expected trait object `dyn T` + //~| NOTE found reference `&_` let &&&x = &(&1isize as &dyn T); //~^ ERROR mismatched types - //~| expected trait object `dyn T` - //~| found reference `&_` + //~| NOTE expected trait object `dyn T` + //~| NOTE found reference `&_` let box box x = Box::new(1isize) as Box; //~^ ERROR mismatched types - //~| expected trait object `dyn T` - //~| found struct `Box<_>` + //~| NOTE expected trait object `dyn T` + //~| NOTE found struct `Box<_>` } diff --git a/tests/ui/destructure-trait-ref.stderr b/tests/ui/destructure-trait-ref.stderr index 38d2018882727..0b5ea551a5784 100644 --- a/tests/ui/destructure-trait-ref.stderr +++ b/tests/ui/destructure-trait-ref.stderr @@ -1,23 +1,23 @@ error[E0033]: type `&dyn T` cannot be dereferenced - --> $DIR/destructure-trait-ref.rs:26:9 + --> $DIR/destructure-trait-ref.rs:28:9 | LL | let &x = &1isize as &dyn T; | ^^ type `&dyn T` cannot be dereferenced error[E0033]: type `&dyn T` cannot be dereferenced - --> $DIR/destructure-trait-ref.rs:27:10 + --> $DIR/destructure-trait-ref.rs:29:10 | LL | let &&x = &(&1isize as &dyn T); | ^^ type `&dyn T` cannot be dereferenced error[E0033]: type `Box` cannot be dereferenced - --> $DIR/destructure-trait-ref.rs:28:9 + --> $DIR/destructure-trait-ref.rs:30:9 | LL | let box x = Box::new(1isize) as Box; | ^^^^^ type `Box` cannot be dereferenced error[E0308]: mismatched types - --> $DIR/destructure-trait-ref.rs:32:10 + --> $DIR/destructure-trait-ref.rs:34:10 | LL | let &&x = &1isize as &dyn T; | ^^ ----------------- this expression has type `&dyn T` @@ -33,7 +33,7 @@ LL + let &x = &1isize as &dyn T; | error[E0308]: mismatched types - --> $DIR/destructure-trait-ref.rs:36:11 + --> $DIR/destructure-trait-ref.rs:38:11 | LL | let &&&x = &(&1isize as &dyn T); | ^^ -------------------- this expression has type `&&dyn T` @@ -49,7 +49,7 @@ LL + let &&x = &(&1isize as &dyn T); | error[E0308]: mismatched types - --> $DIR/destructure-trait-ref.rs:40:13 + --> $DIR/destructure-trait-ref.rs:42:13 | LL | let box box x = Box::new(1isize) as Box; | ^^^^^ ------------------------------ this expression has type `Box` diff --git a/tests/ui/diagnostic-flags/allow-non-lint-warnings.rs b/tests/ui/diagnostic-flags/allow-non-lint-warnings.rs index 8980ef9c4220f..f928e1122894d 100644 --- a/tests/ui/diagnostic-flags/allow-non-lint-warnings.rs +++ b/tests/ui/diagnostic-flags/allow-non-lint-warnings.rs @@ -13,14 +13,16 @@ //! - Original impl PR: . //! - RFC 507 "Release channels": //! . -#![feature(rustc_attrs)] //@ revisions: without_flag with_flag +//@ check-pass +//@ compile-flags: -Zunleash-the-miri-inside-of-you //@[with_flag] compile-flags: -Awarnings -//@ check-pass +fn non_constant() {} +const fn constant() { non_constant() } -#[rustc_error(warn)] fn main() {} -//[without_flag]~^ WARN unexpected annotation used with `#[rustc_error(...)]`! + +//[without_flag]~? WARN skipping const checks diff --git a/tests/ui/diagnostic-flags/allow-non-lint-warnings.without_flag.stderr b/tests/ui/diagnostic-flags/allow-non-lint-warnings.without_flag.stderr index 3f33ccbd1c9f9..08ae11fc18a9a 100644 --- a/tests/ui/diagnostic-flags/allow-non-lint-warnings.without_flag.stderr +++ b/tests/ui/diagnostic-flags/allow-non-lint-warnings.without_flag.stderr @@ -1,8 +1,10 @@ -warning: unexpected annotation used with `#[rustc_error(...)]`! - --> $DIR/allow-non-lint-warnings.rs:25:1 +warning: skipping const checks | -LL | fn main() {} - | ^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/allow-non-lint-warnings.rs:24:23 + | +LL | const fn constant() { non_constant() } + | ^^^^^^^^^^^^^^ warning: 1 warning emitted diff --git a/tests/ui/diagnostic-width/E0271.ascii.stderr b/tests/ui/diagnostic-width/E0271.ascii.stderr index 9a9c12a938f68..ad5f53e44beab 100644 --- a/tests/ui/diagnostic-width/E0271.ascii.stderr +++ b/tests/ui/diagnostic-width/E0271.ascii.stderr @@ -1,5 +1,5 @@ error[E0271]: type mismatch resolving ` as Future>::Error == Foo` - --> $DIR/E0271.rs:20:5 + --> $DIR/E0271.rs:19:5 | LL | / Box::new( LL | | Ok::<_, ()>( @@ -10,12 +10,12 @@ LL | | ) | |_____^ type mismatch resolving ` as Future>::Error == Foo` | note: expected this to be `Foo` - --> $DIR/E0271.rs:10:18 + --> $DIR/E0271.rs:9:18 | LL | type Error = E; | ^ = note: required for the cast from `Box>` to `Box<...>` - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/E0271.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/E0271.rs b/tests/ui/diagnostic-width/E0271.rs index 0618772104143..522bfb5b93fee 100644 --- a/tests/ui/diagnostic-width/E0271.rs +++ b/tests/ui/diagnostic-width/E0271.rs @@ -1,7 +1,6 @@ //@ revisions: ascii unicode //@[ascii] compile-flags: --diagnostic-width=40 -Zwrite-long-types-to-disk=yes //@[unicode] compile-flags: -Zunstable-options --error-format=human-unicode --diagnostic-width=40 -Zwrite-long-types-to-disk=yes -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" trait Future { type Error; } diff --git a/tests/ui/diagnostic-width/E0271.unicode.stderr b/tests/ui/diagnostic-width/E0271.unicode.stderr index 9c3deae66608c..91adf83410163 100644 --- a/tests/ui/diagnostic-width/E0271.unicode.stderr +++ b/tests/ui/diagnostic-width/E0271.unicode.stderr @@ -1,5 +1,5 @@ error[E0271]: type mismatch resolving ` as Future>::Error == Foo` - ╭▸ $DIR/E0271.rs:20:5 + ╭▸ $DIR/E0271.rs:19:5 │ LL │ ┏ Box::new( LL │ ┃ Ok::<_, ()>( @@ -10,12 +10,12 @@ LL │ ┃ ) │ ┗━━━━━┛ type mismatch resolving ` as Future>::Error == Foo` ╰╴ note: expected this to be `Foo` - ╭▸ $DIR/E0271.rs:10:18 + ╭▸ $DIR/E0271.rs:9:18 │ LL │ type Error = E; │ ━ ├ note: required for the cast from `Box>` to `Box<...>` - ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/E0271.long-type-$LONG_TYPE_HASH.txt' ╰ note: consider using `--verbose` to print the full type name to the console error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/binop.rs b/tests/ui/diagnostic-width/binop.rs index 60ba40b8047f9..9e4e837f38698 100644 --- a/tests/ui/diagnostic-width/binop.rs +++ b/tests/ui/diagnostic-width/binop.rs @@ -1,6 +1,4 @@ //@ compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" type A = (i32, i32, i32, i32); type B = (A, A, A, A); type C = (B, B, B, B); diff --git a/tests/ui/diagnostic-width/binop.stderr b/tests/ui/diagnostic-width/binop.stderr index fd69129c33e44..92723df5a9b35 100644 --- a/tests/ui/diagnostic-width/binop.stderr +++ b/tests/ui/diagnostic-width/binop.stderr @@ -1,21 +1,21 @@ error[E0369]: cannot add `(..., ..., ..., ...)` to `(..., ..., ..., ...)` - --> $DIR/binop.rs:10:7 + --> $DIR/binop.rs:8:7 | LL | x + x; | - ^ - (..., ..., ..., ...) | | | (..., ..., ..., ...) | - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/binop.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error[E0600]: cannot apply unary operator `!` to type `(..., ..., ..., ...)` - --> $DIR/binop.rs:14:5 + --> $DIR/binop.rs:12:5 | LL | !x; | ^^ cannot apply unary operator `!` | - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/binop.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error: aborting due to 2 previous errors diff --git a/tests/ui/diagnostic-width/flag-json.rs b/tests/ui/diagnostic-width/flag-json.rs index 00778872727b5..edc7d2e2a4255 100644 --- a/tests/ui/diagnostic-width/flag-json.rs +++ b/tests/ui/diagnostic-width/flag-json.rs @@ -1,9 +1,10 @@ //@ compile-flags: --diagnostic-width=20 --error-format=json -//@ error-pattern:expected `()`, found integer // This test checks that `-Z output-width` effects the JSON error output by restricting it to an // arbitrarily low value so that the effect is visible. fn main() { - let _: () = 42; + let _: () = 42; //~ ERROR mismatched types + //~| NOTE expected `()`, found integer + //~| NOTE expected due to this } diff --git a/tests/ui/diagnostic-width/flag-json.stderr b/tests/ui/diagnostic-width/flag-json.stderr index 6a54f86dcee5e..cfc0364be76de 100644 --- a/tests/ui/diagnostic-width/flag-json.stderr +++ b/tests/ui/diagnostic-width/flag-json.stderr @@ -24,10 +24,10 @@ This error occurs when an expression was used in a place where the compiler expected an expression of a different type. It can occur in several cases, the most common being when calling a function and passing an argument which has a different type than the matching type in the function declaration. -"},"level":"error","spans":[{"file_name":"$DIR/flag-json.rs","byte_start":291,"byte_end":293,"line_start":8,"line_end":8,"column_start":17,"column_end":19,"is_primary":true,"text":[{"text":" let _: () = 42;","highlight_start":17,"highlight_end":19}],"label":"expected `()`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/flag-json.rs","byte_start":286,"byte_end":288,"line_start":8,"line_end":8,"column_start":12,"column_end":14,"is_primary":false,"text":[{"text":" let _: () = 42;","highlight_start":12,"highlight_end":14}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"error[E0308]: mismatched types - --> $DIR/flag-json.rs:8:17 +"},"level":"error","spans":[{"file_name":"$DIR/flag-json.rs","byte_start":244,"byte_end":246,"line_start":7,"line_end":7,"column_start":17,"column_end":19,"is_primary":true,"text":[{"text":" let _: () = 42; + --> $DIR/flag-json.rs:7:17 | -LL | ..._: () = 42; +LL | ..._: () = 42; /... | -- ^^ expected `()`, found integer | | | expected due to this diff --git a/tests/ui/diagnostic-width/long-E0308.ascii.stderr b/tests/ui/diagnostic-width/long-E0308.ascii.stderr index 83da558618870..d1fdd6c443352 100644 --- a/tests/ui/diagnostic-width/long-E0308.ascii.stderr +++ b/tests/ui/diagnostic-width/long-E0308.ascii.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/long-E0308.rs:48:9 + --> $DIR/long-E0308.rs:45:9 | LL | let x: Atype< | _____________- @@ -20,11 +20,11 @@ LL | | )))))))))))))))))))))))))))))); | = note: expected struct `Atype, i32>` found enum `Result, _>` - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error[E0308]: mismatched types - --> $DIR/long-E0308.rs:61:26 + --> $DIR/long-E0308.rs:58:26 | LL | ))))))))))))))))) == Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O... | __________________________^ @@ -36,11 +36,11 @@ LL | | )))))))))))))))))))))))); | = note: expected enum `Option, _>>` found enum `Result, _>` - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error[E0308]: mismatched types - --> $DIR/long-E0308.rs:92:9 + --> $DIR/long-E0308.rs:89:9 | LL | let x: Atype< | ____________- @@ -56,11 +56,11 @@ LL | | > = (); | = note: expected struct `Atype, i32>` found unit type `()` - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error[E0308]: mismatched types - --> $DIR/long-E0308.rs:95:17 + --> $DIR/long-E0308.rs:92:17 | LL | let _: () = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O... | ____________--___^ @@ -74,7 +74,7 @@ LL | | )))))))))))))))))))))))); | = note: expected unit type `()` found enum `Result, _>` - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error: aborting due to 4 previous errors diff --git a/tests/ui/diagnostic-width/long-E0308.rs b/tests/ui/diagnostic-width/long-E0308.rs index 26383d9418dd8..94e525e387efb 100644 --- a/tests/ui/diagnostic-width/long-E0308.rs +++ b/tests/ui/diagnostic-width/long-E0308.rs @@ -2,9 +2,6 @@ //@[ascii] compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes //@[unicode] compile-flags: -Zunstable-options --json=diagnostic-unicode --diagnostic-width=60 -Zwrite-long-types-to-disk=yes -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" - mod a { // Force the "short path for unique types" machinery to trip up pub struct Atype; diff --git a/tests/ui/diagnostic-width/long-E0308.unicode.stderr b/tests/ui/diagnostic-width/long-E0308.unicode.stderr index 54abf576dbd07..69e5ca100671e 100644 --- a/tests/ui/diagnostic-width/long-E0308.unicode.stderr +++ b/tests/ui/diagnostic-width/long-E0308.unicode.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - ╭▸ $DIR/long-E0308.rs:48:9 + ╭▸ $DIR/long-E0308.rs:45:9 │ LL │ let x: Atype< │ ┌─────────────┘ @@ -20,11 +20,11 @@ LL │ ┃ )))))))))))))))))))))))))))))); │ ├ note: expected struct `Atype, i32>` │ found enum `Result, _>` - ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' ╰ note: consider using `--verbose` to print the full type name to the console error[E0308]: mismatched types - ╭▸ $DIR/long-E0308.rs:61:26 + ╭▸ $DIR/long-E0308.rs:58:26 │ LL │ ))))))))))))))))) == Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(… │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┛ @@ -36,11 +36,11 @@ LL │ ┃ )))))))))))))))))))))))); │ ├ note: expected enum `Option, _>>` │ found enum `Result, _>` - ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' ╰ note: consider using `--verbose` to print the full type name to the console error[E0308]: mismatched types - ╭▸ $DIR/long-E0308.rs:92:9 + ╭▸ $DIR/long-E0308.rs:89:9 │ LL │ let x: Atype< │ ┌────────────┘ @@ -56,11 +56,11 @@ LL │ │ > = (); │ ├ note: expected struct `Atype, i32>` │ found unit type `()` - ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' ╰ note: consider using `--verbose` to print the full type name to the console error[E0308]: mismatched types - ╭▸ $DIR/long-E0308.rs:95:17 + ╭▸ $DIR/long-E0308.rs:92:17 │ LL │ let _: () = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(… │ ┏━━━━━━━━━━━━┬─━━━┛ @@ -74,7 +74,7 @@ LL │ ┃ )))))))))))))))))))))))); │ ├ note: expected unit type `()` │ found enum `Result, _>` - ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' ╰ note: consider using `--verbose` to print the full type name to the console error: aborting due to 4 previous errors diff --git a/tests/ui/diagnostic-width/long-E0529.rs b/tests/ui/diagnostic-width/long-E0529.rs index 3ebc4f5f8c825..4146d3be40fe0 100644 --- a/tests/ui/diagnostic-width/long-E0529.rs +++ b/tests/ui/diagnostic-width/long-E0529.rs @@ -1,6 +1,6 @@ //@ compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" +//@ dont-require-annotations: NOTE + type A = (i32, i32, i32, i32); type B = (A, A, A, A); type C = (B, B, B, B); @@ -8,7 +8,7 @@ type D = (C, C, C, C); fn foo(x: D) { let [] = x; //~ ERROR expected an array or slice, found `(... - //~^ pattern cannot match with input type `(... + //~^ NOTE pattern cannot match with input type `(... } fn main() {} diff --git a/tests/ui/diagnostic-width/long-E0529.stderr b/tests/ui/diagnostic-width/long-E0529.stderr index da03e5fab2ca2..e5b82b592712f 100644 --- a/tests/ui/diagnostic-width/long-E0529.stderr +++ b/tests/ui/diagnostic-width/long-E0529.stderr @@ -4,7 +4,7 @@ error[E0529]: expected an array or slice, found `(..., ..., ..., ...)` LL | let [] = x; | ^^ pattern cannot match with input type `(..., ..., ..., ...)` | - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0529.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/long-E0609.rs b/tests/ui/diagnostic-width/long-E0609.rs index 39442bdeae03a..a26d16ad12e78 100644 --- a/tests/ui/diagnostic-width/long-E0609.rs +++ b/tests/ui/diagnostic-width/long-E0609.rs @@ -1,6 +1,5 @@ //@ compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" + type A = (i32, i32, i32, i32); type B = (A, A, A, A); type C = (B, B, B, B); diff --git a/tests/ui/diagnostic-width/long-E0609.stderr b/tests/ui/diagnostic-width/long-E0609.stderr index 6815caa6b6ba1..36ef85457468e 100644 --- a/tests/ui/diagnostic-width/long-E0609.stderr +++ b/tests/ui/diagnostic-width/long-E0609.stderr @@ -1,10 +1,10 @@ error[E0609]: no field `field` on type `(..., ..., ..., ...)` - --> $DIR/long-E0609.rs:10:7 + --> $DIR/long-E0609.rs:9:7 | LL | x.field; | ^^^^^ unknown field | - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0609.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/long-E0614.rs b/tests/ui/diagnostic-width/long-E0614.rs index 0b78444a00d24..5e8b3324dd17d 100644 --- a/tests/ui/diagnostic-width/long-E0614.rs +++ b/tests/ui/diagnostic-width/long-E0614.rs @@ -1,6 +1,5 @@ //@ compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" + type A = (i32, i32, i32, i32); type B = (A, A, A, A); type C = (B, B, B, B); diff --git a/tests/ui/diagnostic-width/long-E0614.stderr b/tests/ui/diagnostic-width/long-E0614.stderr index 1c16ff617faa2..18da20da9453e 100644 --- a/tests/ui/diagnostic-width/long-E0614.stderr +++ b/tests/ui/diagnostic-width/long-E0614.stderr @@ -1,10 +1,10 @@ error[E0614]: type `(..., ..., ..., ...)` cannot be dereferenced - --> $DIR/long-E0614.rs:10:5 + --> $DIR/long-E0614.rs:9:5 | LL | *x; | ^^ can't be dereferenced | - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0614.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/long-E0618.rs b/tests/ui/diagnostic-width/long-E0618.rs index f8626ab9455e2..247061d17f836 100644 --- a/tests/ui/diagnostic-width/long-E0618.rs +++ b/tests/ui/diagnostic-width/long-E0618.rs @@ -1,12 +1,12 @@ //@ compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" +//@ dont-require-annotations: NOTE + type A = (i32, i32, i32, i32); type B = (A, A, A, A); type C = (B, B, B, B); type D = (C, C, C, C); -fn foo(x: D) { //~ `x` has type `(... +fn foo(x: D) { //~ NOTE `x` has type `(... x(); //~ ERROR expected function, found `(... } diff --git a/tests/ui/diagnostic-width/long-E0618.stderr b/tests/ui/diagnostic-width/long-E0618.stderr index f0838cbddcc64..7d92b94faf8fe 100644 --- a/tests/ui/diagnostic-width/long-E0618.stderr +++ b/tests/ui/diagnostic-width/long-E0618.stderr @@ -8,7 +8,7 @@ LL | x(); | | | call expression requires function | - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0618.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/long-e0277.rs b/tests/ui/diagnostic-width/long-e0277.rs index 9b3bd8bb72896..369fd8daea78c 100644 --- a/tests/ui/diagnostic-width/long-e0277.rs +++ b/tests/ui/diagnostic-width/long-e0277.rs @@ -1,6 +1,4 @@ //@ compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" type A = (i32, i32, i32, i32); type B = (A, A, A, A); type C = (B, B, B, B); diff --git a/tests/ui/diagnostic-width/long-e0277.stderr b/tests/ui/diagnostic-width/long-e0277.stderr index a57270df7e252..ff8971511653c 100644 --- a/tests/ui/diagnostic-width/long-e0277.stderr +++ b/tests/ui/diagnostic-width/long-e0277.stderr @@ -1,21 +1,21 @@ error[E0277]: the trait bound `(..., ..., ..., ...): Trait` is not satisfied - --> $DIR/long-e0277.rs:14:21 + --> $DIR/long-e0277.rs:12:21 | LL | require_trait::(); | ^ unsatisfied trait bound | = help: the trait `Trait` is not implemented for `(..., ..., ..., ...)` help: this trait has no implementations, consider adding one - --> $DIR/long-e0277.rs:9:1 + --> $DIR/long-e0277.rs:7:1 | LL | trait Trait {} | ^^^^^^^^^^^ note: required by a bound in `require_trait` - --> $DIR/long-e0277.rs:11:21 + --> $DIR/long-e0277.rs:9:21 | LL | fn require_trait() {} | ^^^^^ required by this bound in `require_trait` - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-e0277.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/long-span.long.stderr b/tests/ui/diagnostic-width/long-span.long.stderr new file mode 100644 index 0000000000000..252b17912dee2 --- /dev/null +++ b/tests/ui/diagnostic-width/long-span.long.stderr @@ -0,0 +1,18 @@ +error[E0369]: cannot add `[{integer}; 1680]` to `[{integer}; 1680]` + ╭▸ $DIR/long-span.rs:7:5056 + │ +LL │ …u8 = [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]; + │ ┬───────────────────────────…────────────────────── ━ ────────────────────────────…────────────────────── [{integer}; 1680] + │ │ + ╰╴ [{integer}; 1680] + +error[E0308]: mismatched types + ╭▸ $DIR/long-span.rs:9:15 + │ +LL │ …u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, …, 0, 0, 0, 0, 0, 0, 0]; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━…━━━━━━━━━━━━━━━━━━━━━━ expected `u8`, found `[{integer}; 1680]` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0369. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/long-span.longest.stderr b/tests/ui/diagnostic-width/long-span.longest.stderr new file mode 100644 index 0000000000000..2e77c36892231 --- /dev/null +++ b/tests/ui/diagnostic-width/long-span.longest.stderr @@ -0,0 +1,18 @@ +error[E0369]: cannot add `[{integer}; 1680]` to `[{integer}; 1680]` + --> $DIR/long-span.rs:7:5056 + | +LL | ... = [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, 0, 0, 0, 0, 0, 0, 0, 0...0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + | -----------------------------------------...----------------------------------- ^ -----------------------------------------...----------------------------------- [{integer}; 1680] + | | + | [{integer}; 1680] + +error[E0308]: mismatched types + --> $DIR/long-span.rs:9:15 + | +LL | ... = [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]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `[{integer}; 1680]` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0369. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/long-span.rs b/tests/ui/diagnostic-width/long-span.rs new file mode 100644 index 0000000000000..a3103379e93fd --- /dev/null +++ b/tests/ui/diagnostic-width/long-span.rs @@ -0,0 +1,11 @@ +//@ revisions: shortest short long longest +//@[shortest] compile-flags: --diagnostic-width=4 +//@[short] compile-flags: --diagnostic-width=12 -Zunstable-options --json=diagnostic-unicode +//@[long] compile-flags: --diagnostic-width=80 -Zunstable-options --json=diagnostic-unicode +//@[longest] compile-flags: --diagnostic-width=120 +// ignore-tidy-linelength +const C: u8 = [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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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]; +//~^ ERROR E0369 +const D: u8 = [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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; +//~^ ERROR E0308 +fn main() {} diff --git a/tests/ui/diagnostic-width/long-span.short.stderr b/tests/ui/diagnostic-width/long-span.short.stderr new file mode 100644 index 0000000000000..b4803e31aaab8 --- /dev/null +++ b/tests/ui/diagnostic-width/long-span.short.stderr @@ -0,0 +1,18 @@ +error[E0369]: cannot add `[{integer}; 1680]` to `[{integer}; 1680]` + ╭▸ $DIR/long-span.rs:7:5056 + │ +LL │ …u8 = [0, 0, 0…0] + [0, 0, 0…0]; + │ ┬───────…── ━ ────────…── [{integer}; 1680] + │ │ + ╰╴ [{integer}; 1680] + +error[E0308]: mismatched types + ╭▸ $DIR/long-span.rs:9:15 + │ +LL │ …u8 = [0, 0, 0…0]; + ╰╴ ━━━━━━━━…━━ expected `u8`, found `[{integer}; 1680]` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0369. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/long-span.shortest.stderr b/tests/ui/diagnostic-width/long-span.shortest.stderr new file mode 100644 index 0000000000000..1de1a4acd9291 --- /dev/null +++ b/tests/ui/diagnostic-width/long-span.shortest.stderr @@ -0,0 +1,18 @@ +error[E0369]: cannot add `[{integer}; 1680]` to `[{integer}; 1680]` + --> $DIR/long-span.rs:7:5056 + | +LL | ... = [0, 0, 0...0] + [0, 0, 0...0]; + | --------...-- ^ --------...-- [{integer}; 1680] + | | + | [{integer}; 1680] + +error[E0308]: mismatched types + --> $DIR/long-span.rs:9:15 + | +LL | ... = [0, 0, 0...0]; + | ^^^^^^^^...^^ expected `u8`, found `[{integer}; 1680]` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0369. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr index 4d8afb6f3ad9b..60ce0d9a14839 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr @@ -1,11 +1,41 @@ error[E0369]: cannot add `&str` to `&str` - --> $DIR/non-1-width-unicode-multiline-label.rs:7:260 + --> $DIR/non-1-width-unicode-multiline-label.rs:7:237 | -LL | ...ཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇...࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; - | -------------- ^ -------------- &str - | | | - | | `+` cannot be used to concatenate two `&str` strings - | &str +LL | ...👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; + | -------------- ^ -------------- &str + | | | + | | `+` cannot be used to concatenate two `&str` strings + | &str + | + = note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + | +LL | let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun.to_owned() + " really fun!"; + | +++++++++++ + +error[E0369]: cannot add `&str` to `&str` + --> $DIR/non-1-width-unicode-multiline-label.rs:9:384 + | +LL | ...👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; + | -------------- ^ -------------- &str + | | | + | | `+` cannot be used to concatenate two `&str` strings + | &str + | + = note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + | +LL | let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun.to_owned() + " really fun!"; + | +++++++++++ + +error[E0369]: cannot add `&str` to `&str` + --> $DIR/non-1-width-unicode-multiline-label.rs:11:260 + | +LL | ...࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; + | -------------- ^ -------------- &str + | | | + | | `+` cannot be used to concatenate two `&str` strings + | &str | = note: string concatenation requires an owned `String` on the left help: create an owned `String` from a string reference @@ -13,6 +43,21 @@ help: create an owned `String` from a string reference LL | let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun.to_owned() + " really fun!"; | +++++++++++ -error: aborting due to 1 previous error +error[E0369]: cannot add `&str` to `&str` + --> $DIR/non-1-width-unicode-multiline-label.rs:13:219 + | +LL | ...xxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun + " really fun!"; + | -------------- ^ -------------- &str + | | | + | | `+` cannot be used to concatenate two `&str` strings + | &str + | + = note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + | +LL | let _ = "xxxxxxx👨👩👧👦xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx👨xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun.to_owned() + " really fun!"; + | +++++++++++ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs index e630db8ba42a2..6b9b27f6297fc 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs @@ -4,6 +4,12 @@ fn main() { let unicode_is_fun = "؁‱ஹ௸௵꧄.ဪ꧅⸻𒈙𒐫﷽𒌄𒈟𒍼𒁎𒀱𒌧𒅃 𒈓𒍙𒊎𒄡𒅌𒁏𒀰𒐪𒐩𒈙𒐫𪚥"; + let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; + //[ascii]~^ ERROR cannot add `&str` to `&str` + let _ = "👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦"; let _a = unicode_is_fun + " really fun!"; + //[ascii]~^ ERROR cannot add `&str` to `&str` let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; //[ascii]~^ ERROR cannot add `&str` to `&str` + let _ = "xxxxxxx👨‍👩‍👧‍👦xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx👨xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun + " really fun!"; + //[ascii]~^ ERROR cannot add `&str` to `&str` } diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr index ed8ce770bb7ea..15b5dd9d7e2d7 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr @@ -1,11 +1,41 @@ error[E0369]: cannot add `&str` to `&str` - ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:7:260 + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:7:237 │ -LL │ …ཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉…࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; - │ ┬───────────── ┯ ────────────── &str - │ │ │ - │ │ `+` cannot be used to concatenate two `&str` strings - │ &str +LL │ …👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; + │ ┬───────────── ┯ ────────────── &str + │ │ │ + │ │ `+` cannot be used to concatenate two `&str` strings + │ &str + │ + ╰ note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + ╭╴ +LL │ let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun.to_owned() + " really fun!"; + ╰╴ +++++++++++ + +error[E0369]: cannot add `&str` to `&str` + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:9:384 + │ +LL │ …👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; + │ ┬───────────── ┯ ────────────── &str + │ │ │ + │ │ `+` cannot be used to concatenate two `&str` strings + │ &str + │ + ╰ note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + ╭╴ +LL │ let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun.to_owned() + " really fun!"; + ╰╴ +++++++++++ + +error[E0369]: cannot add `&str` to `&str` + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:11:260 + │ +LL │ …࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; + │ ┬───────────── ┯ ────────────── &str + │ │ │ + │ │ `+` cannot be used to concatenate two `&str` strings + │ &str │ ╰ note: string concatenation requires an owned `String` on the left help: create an owned `String` from a string reference @@ -13,6 +43,21 @@ help: create an owned `String` from a string reference LL │ let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun.to_owned() + " really fun!"; ╰╴ +++++++++++ -error: aborting due to 1 previous error +error[E0369]: cannot add `&str` to `&str` + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:13:219 + │ +LL │ …xxxxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun + " really fun!"; + │ ┬───────────── ┯ ────────────── &str + │ │ │ + │ │ `+` cannot be used to concatenate two `&str` strings + │ &str + │ + ╰ note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + ╭╴ +LL │ let _ = "xxxxxxx👨👩👧👦xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx👨xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun.to_owned() + " really fun!"; + ╰╴ +++++++++++ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/diagnostic-width/non-copy-type-moved.rs b/tests/ui/diagnostic-width/non-copy-type-moved.rs index a220c62775eb8..845457f7e30fb 100644 --- a/tests/ui/diagnostic-width/non-copy-type-moved.rs +++ b/tests/ui/diagnostic-width/non-copy-type-moved.rs @@ -1,6 +1,4 @@ //@ compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" type A = (String, String, String, String); type B = (A, A, A, A); diff --git a/tests/ui/diagnostic-width/non-copy-type-moved.stderr b/tests/ui/diagnostic-width/non-copy-type-moved.stderr index 254542c7b3908..16c01c858b7d0 100644 --- a/tests/ui/diagnostic-width/non-copy-type-moved.stderr +++ b/tests/ui/diagnostic-width/non-copy-type-moved.stderr @@ -1,5 +1,5 @@ error[E0382]: use of moved value: `x` - --> $DIR/non-copy-type-moved.rs:16:14 + --> $DIR/non-copy-type-moved.rs:14:14 | LL | fn foo(x: D) { | - move occurs because `x` has type `(..., ..., ..., ...)`, which does not implement the `Copy` trait @@ -8,7 +8,7 @@ LL | let _a = x; LL | let _b = x; | ^ value used here after move | - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/non-copy-type-moved.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console help: consider cloning the value if the performance cost is acceptable | diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr index da3d8d318922e..5408825d8cd6d 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr @@ -1,10 +1,10 @@ error[E0308]: mismatched types --> $DIR/non-whitespace-trimming-unicode.rs:4:415 | -LL | ...♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4"; let _: () = 42; let _: &str = "🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓ ☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄... - | -- ^^ expected `()`, found integer - | | - | expected due to this +LL | ...♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4"; let _: () = 42; let _: &str = "🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓ ☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼... + | -- ^^ expected `()`, found integer + | | + | expected due to this error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/secondary-label-with-long-type.rs b/tests/ui/diagnostic-width/secondary-label-with-long-type.rs index c8845af318357..13fe967ba5f8c 100644 --- a/tests/ui/diagnostic-width/secondary-label-with-long-type.rs +++ b/tests/ui/diagnostic-width/secondary-label-with-long-type.rs @@ -1,6 +1,4 @@ //@ compile-flags: --diagnostic-width=100 -Zwrite-long-types-to-disk=yes -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" type A = (i32, i32, i32, i32); type B = (A, A, A, A); diff --git a/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr b/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr index a95e17091489e..a99657ca113ff 100644 --- a/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr +++ b/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/secondary-label-with-long-type.rs:11:9 + --> $DIR/secondary-label-with-long-type.rs:9:9 | LL | let () = x; | ^^ - this expression has type `((..., ..., ..., ...), ..., ..., ...)` @@ -8,7 +8,7 @@ LL | let () = x; | = note: expected tuple `((..., ..., ..., ...), ..., ..., ...)` found unit type `()` - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/secondary-label-with-long-type.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/tabs-trimming.stderr b/tests/ui/diagnostic-width/tabs-trimming.stderr index 85103fbf6f591..e0d1c2d95a965 100644 --- a/tests/ui/diagnostic-width/tabs-trimming.stderr +++ b/tests/ui/diagnostic-width/tabs-trimming.stderr @@ -1,22 +1,20 @@ error[E0408]: variable `v` is not bound in all patterns --> $DIR/tabs-trimming.rs:9:16 | -LL | ... v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT... - | - ^ ^ pattern doesn't bind `v` - | | | - | | pattern doesn't bind `v` - | variable not in all patterns +LL | ... v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT... + | - ^ ^ pattern doesn't bind `v` + | | | + | | pattern doesn't bind `v` + | variable not in all patterns error[E0381]: used binding `v` is possibly-uninitialized --> $DIR/tabs-trimming.rs:9:67 | -LL | ... v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT... - | - ^ `v` used here but it is possibly-uninitialized - | | - | binding initialized here in some conditions - | binding declared here but left uninitialized - | - = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) +LL | ... v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT... + | - ^ `v` used here but it is possibly-uninitialized + | | + | binding initialized here in some conditions + | binding declared here but left uninitialized error: aborting due to 2 previous errors diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr index 90bb715a0522f..8178f54b2aab1 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr @@ -1,12 +1,14 @@ -error[E0277]: the trait bound `&str: AsExpression<::SqlType>` is not satisfied +error[E0277]: the trait bound `&str: AsExpression` is not satisfied --> $DIR/as_expression.rs:56:21 | LL | SelectInt.check("bar"); - | ----- ^^^^^ the trait `AsExpression<::SqlType>` is not implemented for `&str` + | ----- ^^^^^ the trait `AsExpression` is not implemented for `&str` | | | required by a bound introduced by this call | - = help: the trait `AsExpression` is implemented for `&str` + = help: the trait `AsExpression` is not implemented for `&str` + but trait `AsExpression` is implemented for it + = help: for that trait implementation, expected `Text`, found `Integer` note: required by a bound in `Foo::check` --> $DIR/as_expression.rs:47:12 | @@ -16,13 +18,16 @@ LL | where LL | T: AsExpression, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check` -error[E0271]: type mismatch resolving `::SqlType == Text` +error[E0277]: the trait bound `&str: AsExpression` is not satisfied --> $DIR/as_expression.rs:56:5 | LL | SelectInt.check("bar"); - | ^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Integer` + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `AsExpression` is not implemented for `&str` + | + = help: the trait `AsExpression` is not implemented for `&str` + but trait `AsExpression` is implemented for it + = help: for that trait implementation, expected `Text`, found `Integer` error: aborting due to 2 previous errors -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs index 73a238ddf50d5..86f39e43484e7 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs @@ -54,7 +54,6 @@ impl Foo for T where T: Expression {} fn main() { SelectInt.check("bar"); - //[current]~^ ERROR the trait bound `&str: AsExpression` is not satisfied - //[next]~^^ the trait bound `&str: AsExpression<::SqlType>` is not satisfied - //[next]~| type mismatch + //~^ ERROR the trait bound `&str: AsExpression` is not satisfied + //[next]~| ERROR the trait bound `&str: AsExpression` is not satisfied } diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr index 7fd51c7527f92..a82a1e78da0cf 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr @@ -1,52 +1,52 @@ warning: unmatched `}` found - --> $DIR/broken_format.rs:2:32 + --> $DIR/broken_format.rs:2:42 | LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ | = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default warning: positional format arguments are not allowed here - --> $DIR/broken_format.rs:7:32 + --> $DIR/broken_format.rs:7:49 | LL | #[diagnostic::on_unimplemented(message = "Test {}")] - | ^^^^^^^^^^^^^^^^^^^ + | ^ | = help: only named format arguments with the name of one of the generic types are allowed in this context warning: positional format arguments are not allowed here - --> $DIR/broken_format.rs:12:32 + --> $DIR/broken_format.rs:12:49 | LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] - | ^^^^^^^^^^^^^^^^^^^^^ + | ^ | = help: only named format arguments with the name of one of the generic types are allowed in this context warning: invalid format specifier - --> $DIR/broken_format.rs:17:32 + --> $DIR/broken_format.rs:17:42 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ | = help: no format specifier are supported in this position warning: expected `}`, found `!` - --> $DIR/broken_format.rs:22:32 + --> $DIR/broken_format.rs:22:42 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ warning: unmatched `}` found - --> $DIR/broken_format.rs:22:32 + --> $DIR/broken_format.rs:22:42 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ warning: unmatched `}` found - --> $DIR/broken_format.rs:2:32 + --> $DIR/broken_format.rs:2:42 | LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` @@ -70,10 +70,10 @@ LL | fn check_1(_: impl ImportantTrait1) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_1` warning: positional format arguments are not allowed here - --> $DIR/broken_format.rs:7:32 + --> $DIR/broken_format.rs:7:49 | LL | #[diagnostic::on_unimplemented(message = "Test {}")] - | ^^^^^^^^^^^^^^^^^^^ + | ^ | = help: only named format arguments with the name of one of the generic types are allowed in this context = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` @@ -98,10 +98,10 @@ LL | fn check_2(_: impl ImportantTrait2) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_2` warning: positional format arguments are not allowed here - --> $DIR/broken_format.rs:12:32 + --> $DIR/broken_format.rs:12:49 | LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] - | ^^^^^^^^^^^^^^^^^^^^^ + | ^ | = help: only named format arguments with the name of one of the generic types are allowed in this context = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` @@ -126,10 +126,10 @@ LL | fn check_3(_: impl ImportantTrait3) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_3` warning: invalid format specifier - --> $DIR/broken_format.rs:17:32 + --> $DIR/broken_format.rs:17:42 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ | = help: no format specifier are supported in this position = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` @@ -154,18 +154,18 @@ LL | fn check_4(_: impl ImportantTrait4) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_4` warning: expected `}`, found `!` - --> $DIR/broken_format.rs:22:32 + --> $DIR/broken_format.rs:22:42 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: unmatched `}` found - --> $DIR/broken_format.rs:22:32 + --> $DIR/broken_format.rs:22:42 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.rs b/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.rs index 1173c939038a4..44a84f40d44be 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.rs @@ -1,5 +1,7 @@ //@ reference: attributes.diagnostic.on_unimplemented.intro //@ reference: attributes.diagnostic.on_unimplemented.keys +//@ dont-require-annotations: NOTE + #[diagnostic::on_unimplemented(message = "my message", label = "my label", note = "my note")] pub trait ProviderLt {} @@ -15,7 +17,7 @@ struct B; fn main() { B.request(); - //~^ my message [E0599] - //~| my label - //~| my note + //~^ ERROR my message [E0599] + //~| NOTE my label + //~| NOTE my note } diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.stderr index 4c1838620b303..1ba2c2e222dd0 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/custom-on-unimplemented-diagnostic.stderr @@ -1,5 +1,5 @@ error[E0599]: my message - --> $DIR/custom-on-unimplemented-diagnostic.rs:17:7 + --> $DIR/custom-on-unimplemented-diagnostic.rs:19:7 | LL | struct B; | -------- method `request` not found for this struct because it doesn't satisfy `B: ProviderExt` or `B: ProviderLt` @@ -8,7 +8,7 @@ LL | B.request(); | ^^^^^^^ my label | note: trait bound `B: ProviderLt` was not satisfied - --> $DIR/custom-on-unimplemented-diagnostic.rs:12:18 + --> $DIR/custom-on-unimplemented-diagnostic.rs:14:18 | LL | impl ProviderExt for T {} | ^^^^^^^^^^ ----------- - @@ -16,13 +16,13 @@ LL | impl ProviderExt for T {} | unsatisfied trait bound introduced here = note: my note note: the trait `ProviderLt` must be implemented - --> $DIR/custom-on-unimplemented-diagnostic.rs:4:1 + --> $DIR/custom-on-unimplemented-diagnostic.rs:6:1 | LL | pub trait ProviderLt {} | ^^^^^^^^^^^^^^^^^^^^ = help: items from traits can only be used if the trait is implemented and in scope note: `ProviderExt` defines an item `request`, perhaps you need to implement it - --> $DIR/custom-on-unimplemented-diagnostic.rs:6:1 + --> $DIR/custom-on-unimplemented-diagnostic.rs:8:1 | LL | pub trait ProviderExt { | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr index bb455d929406d..88816a98dcf00 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr @@ -39,82 +39,82 @@ LL | #[diagnostic::on_unimplemented = "Message"] = help: only `message`, `note` and `label` are allowed as options warning: there is no parameter `from_desugaring` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:17 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `direct` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:34 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `cause` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:42 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `integral` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:49 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `integer` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:59 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `float` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:15 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `_Self` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:22 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `crate_local` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:29 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `Trait` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:42 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `ItemContext` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:49 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -191,91 +191,91 @@ LL | fn takes_bar(_: impl Bar) {} | ^^^ required by this bound in `takes_bar` warning: there is no parameter `from_desugaring` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:17 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: there is no parameter `direct` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:34 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: there is no parameter `cause` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:42 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: there is no parameter `integral` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:49 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: there is no parameter `integer` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:59 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: there is no parameter `float` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:15 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: there is no parameter `_Self` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:22 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: there is no parameter `crate_local` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:29 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: there is no parameter `Trait` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:42 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: there is no parameter `ItemContext` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:49 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr index 11263580b15e2..4dd8c1afca020 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr @@ -47,10 +47,10 @@ LL | #[diagnostic::on_unimplemented] = help: at least one of the `message`, `note` and `label` options are expected warning: there is no parameter `DoesNotExist` on trait `Test` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:32 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:44 | LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -167,10 +167,10 @@ LL | fn take_whatever(_: impl Whatever) {} | ^^^^^^^^ required by this bound in `take_whatever` warning: there is no parameter `DoesNotExist` on trait `Test` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:32 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:44 | LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs index c638681173d90..b06f56bd66e44 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs @@ -8,8 +8,8 @@ note = "custom note" )] #[diagnostic::on_unimplemented(message = "fallback!!")] -//~^ `message` is ignored due to previous definition of `message` -//~| `message` is ignored due to previous definition of `message` +//~^ WARN `message` is ignored due to previous definition of `message` +//~| WARN `message` is ignored due to previous definition of `message` #[diagnostic::on_unimplemented(label = "fallback label")] #[diagnostic::on_unimplemented(note = "fallback note")] trait Foo {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/on_impl_trait.rs b/tests/ui/diagnostic_namespace/on_unimplemented/on_impl_trait.rs new file mode 100644 index 0000000000000..1ffa604b2bc06 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/on_impl_trait.rs @@ -0,0 +1,18 @@ +//! used to ICE, see +//! Instead it should just ignore the diagnostic attribute + +#![feature(trait_alias)] + +trait Test {} + +#[diagnostic::on_unimplemented(message = "blah", label = "blah", note = "blah")] +//~^ WARN `#[diagnostic::on_unimplemented]` can only be applied to trait definitions +trait Alias = Test; + +// Use trait alias as bound on type parameter. +fn foo(v: &T) {} + +pub fn main() { + foo(&1); + //~^ ERROR the trait bound `{integer}: Alias` is not satisfied +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/on_impl_trait.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/on_impl_trait.stderr new file mode 100644 index 0000000000000..5eee647892271 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/on_impl_trait.stderr @@ -0,0 +1,31 @@ +warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions + --> $DIR/on_impl_trait.rs:8:1 + | +LL | #[diagnostic::on_unimplemented(message = "blah", label = "blah", note = "blah")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default + +error[E0277]: the trait bound `{integer}: Alias` is not satisfied + --> $DIR/on_impl_trait.rs:16:9 + | +LL | foo(&1); + | --- ^^ the trait `Test` is not implemented for `{integer}` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/on_impl_trait.rs:6:1 + | +LL | trait Test {} + | ^^^^^^^^^^ + = note: required for `{integer}` to implement `Alias` +note: required by a bound in `foo` + --> $DIR/on_impl_trait.rs:13:11 + | +LL | fn foo(v: &T) {} + | ^^^^^ required by this bound in `foo` + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/suggest_typos.rs b/tests/ui/diagnostic_namespace/suggest_typos.rs index 6fa4f800462fd..8d1dc6f59da97 100644 --- a/tests/ui/diagnostic_namespace/suggest_typos.rs +++ b/tests/ui/diagnostic_namespace/suggest_typos.rs @@ -16,4 +16,9 @@ trait Y{} //~^^HELP an attribute with a similar name exists trait Z{} +#[diagnostic::dont_recommend] +//~^ERROR unknown diagnostic attribute +//~^^HELP an attribute with a similar name exists +impl X for u8 {} + fn main(){} diff --git a/tests/ui/diagnostic_namespace/suggest_typos.stderr b/tests/ui/diagnostic_namespace/suggest_typos.stderr index 86d778c6ec05b..1f19fd4bbcf51 100644 --- a/tests/ui/diagnostic_namespace/suggest_typos.stderr +++ b/tests/ui/diagnostic_namespace/suggest_typos.stderr @@ -37,5 +37,17 @@ help: an attribute with a similar name exists LL | #[diagnostic::on_unimplemented] | ++ -error: aborting due to 3 previous errors +error: unknown diagnostic attribute + --> $DIR/suggest_typos.rs:19:15 + | +LL | #[diagnostic::dont_recommend] + | ^^^^^^^^^^^^^^ + | +help: an attribute with a similar name exists + | +LL - #[diagnostic::dont_recommend] +LL + #[diagnostic::do_not_recommend] + | + +error: aborting due to 4 previous errors diff --git a/tests/ui/did_you_mean/E0178.stderr b/tests/ui/did_you_mean/E0178.stderr index 5f289da8a6c30..36e4dbdf7c45d 100644 --- a/tests/ui/did_you_mean/E0178.stderr +++ b/tests/ui/did_you_mean/E0178.stderr @@ -1,41 +1,43 @@ -error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo` +error[E0178]: expected a path on the left-hand side of `+` --> $DIR/E0178.rs:6:8 | LL | w: &'a Foo + Copy, - | ^^^^^^^^^^^^^^ + | ^^^^^^^ | help: try adding parentheses | LL | w: &'a (Foo + Copy), | + + -error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo` +error[E0178]: expected a path on the left-hand side of `+` --> $DIR/E0178.rs:7:8 | LL | x: &'a Foo + 'a, - | ^^^^^^^^^^^^ + | ^^^^^^^ | help: try adding parentheses | LL | x: &'a (Foo + 'a), | + + -error[E0178]: expected a path on the left-hand side of `+`, not `&'a mut Foo` +error[E0178]: expected a path on the left-hand side of `+` --> $DIR/E0178.rs:8:8 | LL | y: &'a mut Foo + 'a, - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^ | help: try adding parentheses | LL | y: &'a mut (Foo + 'a), | + + -error[E0178]: expected a path on the left-hand side of `+`, not `fn() -> Foo` +error[E0178]: expected a path on the left-hand side of `+` --> $DIR/E0178.rs:9:8 | LL | z: fn() -> Foo + 'a, - | ^^^^^^^^^^^^^^^^ perhaps you forgot parentheses? + | ^^^^^^^^^^^----- + | | + | perhaps you forgot parentheses? error: aborting due to 4 previous errors diff --git a/tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr b/tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr index 411eec8496339..08166355748b5 100644 --- a/tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr +++ b/tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr @@ -1,18 +1,3 @@ -error[E0560]: struct `Crate` has no field named `fiel` - --> $DIR/dont-suggest-hygienic-fields.rs:44:34 - | -LL | environment!(); - | -------------- in this macro invocation -... -LL | const CRATE: Crate = Crate { fiel: () }; - | ^^^^ unknown field - | - = note: this error originates in the macro `environment` (in Nightly builds, run with -Z macro-backtrace for more info) -help: a field with a similar name exists - | -LL | const CRATE: Crate = Crate { field: () }; - | + - error[E0609]: no field `field` on type `Compound` --> $DIR/dont-suggest-hygienic-fields.rs:24:16 | @@ -48,6 +33,21 @@ error[E0609]: no field `0` on type `Component` LL | let _ = ty.0; | ^ unknown field +error[E0560]: struct `Crate` has no field named `fiel` + --> $DIR/dont-suggest-hygienic-fields.rs:44:34 + | +LL | environment!(); + | -------------- in this macro invocation +... +LL | const CRATE: Crate = Crate { fiel: () }; + | ^^^^ unknown field + | + = note: this error originates in the macro `environment` (in Nightly builds, run with -Z macro-backtrace for more info) +help: a field with a similar name exists + | +LL | const CRATE: Crate = Crate { field: () }; + | + + error: aborting due to 6 previous errors Some errors have detailed explanations: E0026, E0560, E0609. diff --git a/tests/ui/did_you_mean/issue-38147-1.rs b/tests/ui/did_you_mean/issue-38147-1.rs index c068a1834f350..80f71b8b821ee 100644 --- a/tests/ui/did_you_mean/issue-38147-1.rs +++ b/tests/ui/did_you_mean/issue-38147-1.rs @@ -14,7 +14,7 @@ struct Foo<'a> { impl<'a> Foo<'a> { fn f(&self) { - self.s.push('x'); //~ cannot borrow `*self.s` as mutable, as it is behind a `&` reference + self.s.push('x'); //~ ERROR cannot borrow `*self.s` as mutable, as it is behind a `&` reference } } diff --git a/tests/ui/did_you_mean/issue-40006.rs b/tests/ui/did_you_mean/issue-40006.rs index fff31bfc85e3e..fcb86814c6e55 100644 --- a/tests/ui/did_you_mean/issue-40006.rs +++ b/tests/ui/did_you_mean/issue-40006.rs @@ -35,5 +35,5 @@ impl S { } fn main() { - S.hello_method(); //~ no method named `hello_method` found + S.hello_method(); //~ ERROR no method named `hello_method` found } diff --git a/tests/ui/did_you_mean/issue-42764.rs b/tests/ui/did_you_mean/issue-42764.rs index eb96c2480630e..2766bb2c1b61d 100644 --- a/tests/ui/did_you_mean/issue-42764.rs +++ b/tests/ui/did_you_mean/issue-42764.rs @@ -26,5 +26,5 @@ struct Context { wrapper: Wrapper } fn overton() { let _c = Context { wrapper: Payload{} }; //~^ ERROR mismatched types - //~| try wrapping the expression in `Wrapper` + //~| HELP try wrapping the expression in `Wrapper` } diff --git a/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr b/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr index d4812d4831b03..c74cb89f85cb9 100644 --- a/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr +++ b/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr @@ -66,7 +66,6 @@ error: unexpected `,` in pattern LL | let women, men: (Vec, Vec) = genomes.iter().cloned() | ^ | - = note: type ascription syntax has been removed, see issue #101728 help: try adding parentheses to match on a tuple | LL | let (women, men): (Vec, Vec) = genomes.iter().cloned() diff --git a/tests/ui/did_you_mean/recursion_limit_deref.rs b/tests/ui/did_you_mean/recursion_limit_deref.rs index af4c4ddda69ac..e53007388af25 100644 --- a/tests/ui/did_you_mean/recursion_limit_deref.rs +++ b/tests/ui/did_you_mean/recursion_limit_deref.rs @@ -51,3 +51,6 @@ fn main() { let x: &Bottom = &t; //~ ERROR mismatched types //~^ error recursion limit } + +//~? ERROR reached the recursion limit finding the struct tail for `K` +//~? ERROR reached the recursion limit finding the struct tail for `Bottom` diff --git a/tests/ui/did_you_mean/sugg-stable-import-first-issue-140240.rs b/tests/ui/did_you_mean/sugg-stable-import-first-issue-140240.rs new file mode 100644 index 0000000000000..aa7178271a792 --- /dev/null +++ b/tests/ui/did_you_mean/sugg-stable-import-first-issue-140240.rs @@ -0,0 +1,3 @@ +fn main() { + const _: Range = 0..1; //~ ERROR cannot find type `Range` in this scope +} diff --git a/tests/ui/did_you_mean/sugg-stable-import-first-issue-140240.stderr b/tests/ui/did_you_mean/sugg-stable-import-first-issue-140240.stderr new file mode 100644 index 0000000000000..9e4314a0699d8 --- /dev/null +++ b/tests/ui/did_you_mean/sugg-stable-import-first-issue-140240.stderr @@ -0,0 +1,20 @@ +error[E0412]: cannot find type `Range` in this scope + --> $DIR/sugg-stable-import-first-issue-140240.rs:2:14 + | +LL | const _: Range = 0..1; + | ^^^^^ not found in this scope + | +help: consider importing one of these structs + | +LL + use std::collections::btree_map::Range; + | +LL + use std::collections::btree_set::Range; + | +LL + use std::ops::Range; + | +LL + use std::range::Range; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr index 4fee6cc9a2227..762b37b9e9d69 100644 --- a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr +++ b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr @@ -1,19 +1,19 @@ -error[E0178]: expected a path on the left-hand side of `+`, not `&Copy` +error[E0178]: expected a path on the left-hand side of `+` --> $DIR/trait-object-reference-without-parens-suggestion.rs:4:12 | LL | let _: &Copy + 'static; - | ^^^^^^^^^^^^^^^ + | ^^^^^ | help: try adding parentheses | LL | let _: &(Copy + 'static); | + + -error[E0178]: expected a path on the left-hand side of `+`, not `&'static Copy` +error[E0178]: expected a path on the left-hand side of `+` --> $DIR/trait-object-reference-without-parens-suggestion.rs:6:12 | LL | let _: &'static Copy + 'static; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ | help: try adding parentheses | diff --git a/tests/ui/directory_ownership/macro_expanded_mod_helper/foo/bar.rs b/tests/ui/directory_ownership/macro_expanded_mod_helper/foo/bar.rs index 1d832a36ef500..2ccdd798c73d5 100644 --- a/tests/ui/directory_ownership/macro_expanded_mod_helper/foo/bar.rs +++ b/tests/ui/directory_ownership/macro_expanded_mod_helper/foo/bar.rs @@ -1 +1 @@ -//@ ignore-test not a test, auxiliary +//@ ignore-auxiliary (used by `../../macro-expanded-mod.rs`) diff --git a/tests/ui/directory_ownership/macro_expanded_mod_helper/foo/mod.rs b/tests/ui/directory_ownership/macro_expanded_mod_helper/foo/mod.rs index 08349ba6747d4..9009f80c69104 100644 --- a/tests/ui/directory_ownership/macro_expanded_mod_helper/foo/mod.rs +++ b/tests/ui/directory_ownership/macro_expanded_mod_helper/foo/mod.rs @@ -1,3 +1,3 @@ -//@ ignore-test not a test, auxiliary +//@ ignore-auxiliary (used by `../../macro-expanded-mod.rs`) mod_decl!(bar); diff --git a/tests/ui/directory_ownership/mod_file_not_owning_aux1.rs b/tests/ui/directory_ownership/mod_file_not_owning_aux1.rs deleted file mode 100644 index 6d6884fef0400..0000000000000 --- a/tests/ui/directory_ownership/mod_file_not_owning_aux1.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ ignore-test this is not a test - -macro_rules! m { - () => { mod mod_file_not_owning_aux2; } -} -m!(); diff --git a/tests/ui/directory_ownership/mod_file_not_owning_aux2.rs b/tests/ui/directory_ownership/mod_file_not_owning_aux2.rs deleted file mode 100644 index 76f1c1a727632..0000000000000 --- a/tests/ui/directory_ownership/mod_file_not_owning_aux2.rs +++ /dev/null @@ -1 +0,0 @@ -//@ ignore-test this is not a test diff --git a/tests/ui/directory_ownership/mod_file_not_owning_aux3.rs b/tests/ui/directory_ownership/mod_file_not_owning_aux3.rs deleted file mode 100644 index 96a5780d971f5..0000000000000 --- a/tests/ui/directory_ownership/mod_file_not_owning_aux3.rs +++ /dev/null @@ -1,3 +0,0 @@ -//@ ignore-test this is not a test - -mod mod_file_not_owning_aux2; diff --git a/tests/ui/drop/drop-order-comparisons.e2021.fixed b/tests/ui/drop/drop-order-comparisons.e2021.fixed index 78cf421cfbf34..6c8d2d3fa9c12 100644 --- a/tests/ui/drop/drop-order-comparisons.e2021.fixed +++ b/tests/ui/drop/drop-order-comparisons.e2021.fixed @@ -24,7 +24,8 @@ //@ [e2024] edition: 2024 //@ run-pass -#![feature(let_chains)] +#![feature(if_let_guard)] +#![cfg_attr(e2021, feature(let_chains))] #![cfg_attr(e2021, warn(rust_2024_compatibility))] fn t_bindings() { @@ -344,6 +345,25 @@ fn t_if_let_chains_then() { e.assert(9); } +#[rustfmt::skip] +fn t_guard_if_let_chains_then() { + let e = Events::new(); + _ = match () { + () if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(8) + && let Ok(_) = e.ok(7) + && let Ok(_) = e.ok(6).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(5) + && let Ok(_) = e.ok(4).as_ref() => { + e.mark(3); + } + _ => {} + }; + e.assert(9); +} + #[cfg(e2021)] #[rustfmt::skip] fn t_if_let_nested_else() { @@ -484,6 +504,25 @@ fn t_if_let_chains_then_else() { e.assert(9); } +#[rustfmt::skip] +fn t_guard_if_let_chains_then_else() { + let e = Events::new(); + _ = match () { + () if e.ok(1).is_ok() + && let true = e.ok(8).is_ok() + && let Ok(_v) = e.ok(7) + && let Ok(_) = e.ok(6) + && let Ok(_) = e.ok(5).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.err(3) => {} + _ => { + e.mark(9); + } + }; + e.assert(9); +} + fn main() { t_bindings(); t_tuples(); @@ -502,10 +541,12 @@ fn main() { t_if_let_nested_then(); t_let_else_chained_then(); t_if_let_chains_then(); + t_guard_if_let_chains_then(); t_if_let_nested_else(); t_if_let_nested_then_else(); t_let_else_chained_then_else(); t_if_let_chains_then_else(); + t_guard_if_let_chains_then_else(); } // # Test scaffolding diff --git a/tests/ui/drop/drop-order-comparisons.e2021.stderr b/tests/ui/drop/drop-order-comparisons.e2021.stderr index 601b0a38412f0..8b93376cc0d05 100644 --- a/tests/ui/drop/drop-order-comparisons.e2021.stderr +++ b/tests/ui/drop/drop-order-comparisons.e2021.stderr @@ -1,5 +1,5 @@ warning: relative drop order changing in Rust 2024 - --> $DIR/drop-order-comparisons.rs:76:9 + --> $DIR/drop-order-comparisons.rs:77:9 | LL | _ = ({ | _________- @@ -29,51 +29,35 @@ LL | | }, e.mark(3), e.ok(4)); = warning: this changes meaning in Rust 2024 = note: for more information, see note: `#3` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#1` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `_v` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#2` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages note: the lint level is defined here - --> $DIR/drop-order-comparisons.rs:28:25 + --> $DIR/drop-order-comparisons.rs:29:25 | LL | #![cfg_attr(e2021, warn(rust_2024_compatibility))] | ^^^^^^^^^^^^^^^^^^^^^^^ = note: `#[warn(tail_expr_drop_order)]` implied by `#[warn(rust_2024_compatibility)]` warning: relative drop order changing in Rust 2024 - --> $DIR/drop-order-comparisons.rs:100:45 + --> $DIR/drop-order-comparisons.rs:101:45 | LL | _ = ({ | _________- @@ -93,27 +77,19 @@ LL | | }, e.mark(1), e.ok(4)); = warning: this changes meaning in Rust 2024 = note: for more information, see note: `#2` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#1` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages warning: relative drop order changing in Rust 2024 - --> $DIR/drop-order-comparisons.rs:100:19 + --> $DIR/drop-order-comparisons.rs:101:19 | LL | _ = ({ | _________- @@ -133,27 +109,19 @@ LL | | }, e.mark(1), e.ok(4)); = warning: this changes meaning in Rust 2024 = note: for more information, see note: `#2` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#1` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages warning: relative drop order changing in Rust 2024 - --> $DIR/drop-order-comparisons.rs:221:24 + --> $DIR/drop-order-comparisons.rs:222:24 | LL | _ = ({ | _________- @@ -173,27 +141,19 @@ LL | | }, e.mark(2), e.ok(3)); = warning: this changes meaning in Rust 2024 = note: for more information, see note: `#2` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#1` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages warning: relative drop order changing in Rust 2024 - --> $DIR/drop-order-comparisons.rs:247:24 + --> $DIR/drop-order-comparisons.rs:248:24 | LL | _ = ({ | _________- @@ -213,27 +173,19 @@ LL | | }, e.mark(2), e.ok(3)); = warning: this changes meaning in Rust 2024 = note: for more information, see note: `#2` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#1` invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:123:13 + --> $DIR/drop-order-comparisons.rs:124:13 | LL | _ = (if let Ok(_) = e.ok(4).as_ref() { | ^^^^^^^^^^^^-------^^^^^^^^^ @@ -243,16 +195,12 @@ LL | _ = (if let Ok(_) = e.ok(4).as_ref() { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:127:5 + --> $DIR/drop-order-comparisons.rs:128:5 | LL | }, e.mark(2), e.ok(3)); | ^ @@ -267,7 +215,7 @@ LL ~ } _ => {}}, e.mark(2), e.ok(3)); | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:145:13 + --> $DIR/drop-order-comparisons.rs:146:13 | LL | _ = (if let Ok(_) = e.err(4).as_ref() {} else { | ^^^^^^^^^^^^--------^^^^^^^^^ @@ -277,16 +225,12 @@ LL | _ = (if let Ok(_) = e.err(4).as_ref() {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:145:44 + --> $DIR/drop-order-comparisons.rs:146:44 | LL | _ = (if let Ok(_) = e.err(4).as_ref() {} else { | ^ @@ -300,7 +244,7 @@ LL ~ }}, e.mark(2), e.ok(3)); | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:247:12 + --> $DIR/drop-order-comparisons.rs:248:12 | LL | if let Ok(_) = e.err(4).as_ref() {} else { | ^^^^^^^^^^^^--------^^^^^^^^^ @@ -310,16 +254,12 @@ LL | if let Ok(_) = e.err(4).as_ref() {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:247:43 + --> $DIR/drop-order-comparisons.rs:248:43 | LL | if let Ok(_) = e.err(4).as_ref() {} else { | ^ @@ -333,7 +273,7 @@ LL ~ }} | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:352:12 + --> $DIR/drop-order-comparisons.rs:372:12 | LL | if let true = e.err(9).is_ok() {} else { | ^^^^^^^^^^^--------^^^^^^^^ @@ -343,16 +283,12 @@ LL | if let true = e.err(9).is_ok() {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:352:41 + --> $DIR/drop-order-comparisons.rs:372:41 | LL | if let true = e.err(9).is_ok() {} else { | ^ @@ -366,7 +302,7 @@ LL ~ }}}}}}}}}; | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:355:12 + --> $DIR/drop-order-comparisons.rs:375:12 | LL | if let Ok(_v) = e.err(8) {} else { | ^^^^^^^^^^^^^-------- @@ -376,16 +312,12 @@ LL | if let Ok(_v) = e.err(8) {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:355:35 + --> $DIR/drop-order-comparisons.rs:375:35 | LL | if let Ok(_v) = e.err(8) {} else { | ^ @@ -399,7 +331,7 @@ LL ~ }}}}}}}}}; | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:358:12 + --> $DIR/drop-order-comparisons.rs:378:12 | LL | if let Ok(_) = e.err(7) {} else { | ^^^^^^^^^^^^-------- @@ -409,16 +341,12 @@ LL | if let Ok(_) = e.err(7) {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:358:34 + --> $DIR/drop-order-comparisons.rs:378:34 | LL | if let Ok(_) = e.err(7) {} else { | ^ @@ -432,7 +360,7 @@ LL ~ }}}}}}}}}; | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:361:12 + --> $DIR/drop-order-comparisons.rs:381:12 | LL | if let Ok(_) = e.err(6).as_ref() {} else { | ^^^^^^^^^^^^--------^^^^^^^^^ @@ -442,16 +370,12 @@ LL | if let Ok(_) = e.err(6).as_ref() {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:361:43 + --> $DIR/drop-order-comparisons.rs:381:43 | LL | if let Ok(_) = e.err(6).as_ref() {} else { | ^ @@ -465,7 +389,7 @@ LL ~ }}}}}}}}}; | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:365:12 + --> $DIR/drop-order-comparisons.rs:385:12 | LL | if let Ok(_v) = e.err(5) {} else { | ^^^^^^^^^^^^^-------- @@ -475,16 +399,12 @@ LL | if let Ok(_v) = e.err(5) {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:365:35 + --> $DIR/drop-order-comparisons.rs:385:35 | LL | if let Ok(_v) = e.err(5) {} else { | ^ @@ -498,7 +418,7 @@ LL ~ }}}}}}}}}; | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:368:12 + --> $DIR/drop-order-comparisons.rs:388:12 | LL | if let Ok(_) = e.err(4) {} else { | ^^^^^^^^^^^^-------- @@ -508,16 +428,12 @@ LL | if let Ok(_) = e.err(4) {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:368:34 + --> $DIR/drop-order-comparisons.rs:388:34 | LL | if let Ok(_) = e.err(4) {} else { | ^ @@ -531,7 +447,7 @@ LL ~ }}}}}}}}}; | warning: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/drop-order-comparisons.rs:404:12 + --> $DIR/drop-order-comparisons.rs:424:12 | LL | if let Ok(_) = e.err(4).as_ref() {} else { | ^^^^^^^^^^^^--------^^^^^^^^^ @@ -541,16 +457,12 @@ LL | if let Ok(_) = e.err(4).as_ref() {} else { = warning: this changes meaning in Rust 2024 = note: for more information, see note: value invokes this custom destructor - --> $DIR/drop-order-comparisons.rs:571:1 - | -LL | / impl<'b> Drop for LogDrop<'b> { -LL | | fn drop(&mut self) { -LL | | self.0.mark(self.1); -LL | | } -LL | | } - | |_^ + --> $DIR/drop-order-comparisons.rs:612:1 + | +LL | impl<'b> Drop for LogDrop<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 - --> $DIR/drop-order-comparisons.rs:404:43 + --> $DIR/drop-order-comparisons.rs:424:43 | LL | if let Ok(_) = e.err(4).as_ref() {} else { | ^ diff --git a/tests/ui/drop/drop-order-comparisons.rs b/tests/ui/drop/drop-order-comparisons.rs index 78c75a9449f17..9a10a08a3ff74 100644 --- a/tests/ui/drop/drop-order-comparisons.rs +++ b/tests/ui/drop/drop-order-comparisons.rs @@ -24,7 +24,8 @@ //@ [e2024] edition: 2024 //@ run-pass -#![feature(let_chains)] +#![feature(if_let_guard)] +#![cfg_attr(e2021, feature(let_chains))] #![cfg_attr(e2021, warn(rust_2024_compatibility))] fn t_bindings() { @@ -344,6 +345,25 @@ fn t_if_let_chains_then() { e.assert(9); } +#[rustfmt::skip] +fn t_guard_if_let_chains_then() { + let e = Events::new(); + _ = match () { + () if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(8) + && let Ok(_) = e.ok(7) + && let Ok(_) = e.ok(6).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(5) + && let Ok(_) = e.ok(4).as_ref() => { + e.mark(3); + } + _ => {} + }; + e.assert(9); +} + #[cfg(e2021)] #[rustfmt::skip] fn t_if_let_nested_else() { @@ -484,6 +504,25 @@ fn t_if_let_chains_then_else() { e.assert(9); } +#[rustfmt::skip] +fn t_guard_if_let_chains_then_else() { + let e = Events::new(); + _ = match () { + () if e.ok(1).is_ok() + && let true = e.ok(8).is_ok() + && let Ok(_v) = e.ok(7) + && let Ok(_) = e.ok(6) + && let Ok(_) = e.ok(5).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.err(3) => {} + _ => { + e.mark(9); + } + }; + e.assert(9); +} + fn main() { t_bindings(); t_tuples(); @@ -502,10 +541,12 @@ fn main() { t_if_let_nested_then(); t_let_else_chained_then(); t_if_let_chains_then(); + t_guard_if_let_chains_then(); t_if_let_nested_else(); t_if_let_nested_then_else(); t_let_else_chained_then_else(); t_if_let_chains_then_else(); + t_guard_if_let_chains_then_else(); } // # Test scaffolding diff --git a/tests/ui/drop/drop_elaboration_with_errors.rs b/tests/ui/drop/drop_elaboration_with_errors.rs index 35d05e6cf64a3..6b686e49d45f1 100644 --- a/tests/ui/drop/drop_elaboration_with_errors.rs +++ b/tests/ui/drop/drop_elaboration_with_errors.rs @@ -10,6 +10,7 @@ struct Foo { type Tait = impl Sized; +#[define_opaque(Tait)] fn ice_cold(beverage: Tait) { let Foo { field } = beverage; _ = field; diff --git a/tests/ui/drop/drop_elaboration_with_errors.stderr b/tests/ui/drop/drop_elaboration_with_errors.stderr index bec229631e189..ea2c9b949e429 100644 --- a/tests/ui/drop/drop_elaboration_with_errors.stderr +++ b/tests/ui/drop/drop_elaboration_with_errors.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/drop_elaboration_with_errors.rs:19:5 + --> $DIR/drop_elaboration_with_errors.rs:20:5 | LL | fn main() { | - expected `()` because of default return type diff --git a/tests/ui/drop/drop_elaboration_with_errors2.rs b/tests/ui/drop/drop_elaboration_with_errors2.rs new file mode 100644 index 0000000000000..946c253179cb8 --- /dev/null +++ b/tests/ui/drop/drop_elaboration_with_errors2.rs @@ -0,0 +1,33 @@ +// Regression test for #137287 + +mod defining_scope { + use super::*; + pub type Alias = impl Sized; + //~^ ERROR unconstrained opaque type + //~| ERROR `impl Trait` in type aliases is unstable + + pub fn cast(x: Container, T>) -> Container { + x + //~^ ERROR mismatched types + } +} + +struct Container, U> { + x: >::Assoc, +} + +trait Trait { + type Assoc; +} + +impl Trait for T { + type Assoc = Box; +} +impl Trait for defining_scope::Alias { + //~^ ERROR conflicting implementations of trait `Trait<_>` + type Assoc = usize; +} + +fn main() { + let x: Box = defining_scope::cast::<()>(Container { x: 0 }).x; +} diff --git a/tests/ui/drop/drop_elaboration_with_errors2.stderr b/tests/ui/drop/drop_elaboration_with_errors2.stderr new file mode 100644 index 0000000000000..15fe3f6ecc1f4 --- /dev/null +++ b/tests/ui/drop/drop_elaboration_with_errors2.stderr @@ -0,0 +1,47 @@ +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/drop_elaboration_with_errors2.rs:5:25 + | +LL | pub type Alias = impl Sized; + | ^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0119]: conflicting implementations of trait `Trait<_>` + --> $DIR/drop_elaboration_with_errors2.rs:26:1 + | +LL | impl Trait for T { + | ---------------------- first implementation here +... +LL | impl Trait for defining_scope::Alias { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation + +error: unconstrained opaque type + --> $DIR/drop_elaboration_with_errors2.rs:5:25 + | +LL | pub type Alias = impl Sized; + | ^^^^^^^^^^ + | + = note: `Alias` must be used in combination with a concrete type within the same crate + +error[E0308]: mismatched types + --> $DIR/drop_elaboration_with_errors2.rs:10:9 + | +LL | pub type Alias = impl Sized; + | ---------- the found opaque type +... +LL | pub fn cast(x: Container, T>) -> Container { + | - expected this type parameter --------------- expected `Container` because of return type +LL | x + | ^ expected `Container`, found `Container, T>` + | + = note: expected struct `Container` + found struct `Container, _>` + = help: type parameters must be constrained to match other types + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0119, E0308, E0658. +For more information about an error, try `rustc --explain E0119`. diff --git a/tests/ui/drop/drop_elaboration_with_errors3.rs b/tests/ui/drop/drop_elaboration_with_errors3.rs new file mode 100644 index 0000000000000..c5ed63eb7ac2b --- /dev/null +++ b/tests/ui/drop/drop_elaboration_with_errors3.rs @@ -0,0 +1,42 @@ +// Regression test for #135668 +//@ edition: 2021 + +use std::future::Future; + +pub async fn foo() { + let _ = create_task().await; +} + +async fn create_task() -> impl Sized { + bind(documentation) +} + +async fn documentation() { + compile_error!("bonjour"); + //~^ ERROR bonjour +} + +fn bind(_filter: F) -> impl Sized +where + F: FilterBase, +{ + || -> ::Assoc { panic!() } +} + +trait FilterBase { + type Assoc; +} + +impl FilterBase for F +where + F: Fn() -> R, + // Removing the below line makes it correctly error on both stable and beta + R: Future, + // Removing the below line makes it ICE on both stable and beta + R: Send, + // Removing the above two bounds makes it ICE on stable but correctly error on beta +{ + type Assoc = F; +} + +fn main() {} diff --git a/tests/ui/drop/drop_elaboration_with_errors3.stderr b/tests/ui/drop/drop_elaboration_with_errors3.stderr new file mode 100644 index 0000000000000..2d44e7c662597 --- /dev/null +++ b/tests/ui/drop/drop_elaboration_with_errors3.stderr @@ -0,0 +1,8 @@ +error: bonjour + --> $DIR/drop_elaboration_with_errors3.rs:15:5 + | +LL | compile_error!("bonjour"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/drop/drop_order.rs b/tests/ui/drop/drop_order.rs index d1a5b9bc5e268..b96e55a2535a6 100644 --- a/tests/ui/drop/drop_order.rs +++ b/tests/ui/drop/drop_order.rs @@ -2,9 +2,10 @@ //@ compile-flags: -Z validate-mir //@ revisions: edition2021 edition2024 //@ [edition2021] edition: 2021 +//@ [edition2024] compile-flags: -Z lint-mir //@ [edition2024] edition: 2024 -#![feature(let_chains)] +#![cfg_attr(edition2021, feature(let_chains))] use std::cell::RefCell; use std::convert::TryInto; diff --git a/tests/ui/drop/drop_order_if_let_rescope.rs b/tests/ui/drop/drop_order_if_let_rescope.rs index 7445e3a6a5f1f..27bced5fa62fd 100644 --- a/tests/ui/drop/drop_order_if_let_rescope.rs +++ b/tests/ui/drop/drop_order_if_let_rescope.rs @@ -1,8 +1,6 @@ //@ run-pass //@ edition:2024 -//@ compile-flags: -Z validate-mir - -#![feature(let_chains)] +//@ compile-flags: -Z validate-mir -Z lint-mir use std::cell::RefCell; use std::convert::TryInto; diff --git a/tests/ui/drop/dropck-normalize-errors.rs b/tests/ui/drop/dropck-normalize-errors.rs new file mode 100644 index 0000000000000..793122bd33d4b --- /dev/null +++ b/tests/ui/drop/dropck-normalize-errors.rs @@ -0,0 +1,31 @@ +// Test that we don't ICE when computing the drop types for + +trait Decode<'a> { + type Decoder; +} + +trait NonImplementedTrait { + type Assoc; +} +struct NonImplementedStruct; + +pub struct ADecoder<'a> { + b: >::Decoder, +} +fn make_a_decoder<'a>() -> ADecoder<'a> { + //~^ ERROR the trait bound + //~| ERROR the trait bound + panic!() +} + +struct B; +impl<'a> Decode<'a> for B { + type Decoder = BDecoder; + //~^ ERROR the trait bound +} +pub struct BDecoder { + non_implemented: ::Assoc, + //~^ ERROR the trait bound +} + +fn main() {} diff --git a/tests/ui/drop/dropck-normalize-errors.stderr b/tests/ui/drop/dropck-normalize-errors.stderr new file mode 100644 index 0000000000000..2bb5909c6b226 --- /dev/null +++ b/tests/ui/drop/dropck-normalize-errors.stderr @@ -0,0 +1,76 @@ +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied in `ADecoder<'a>` + --> $DIR/dropck-normalize-errors.rs:15:28 + | +LL | fn make_a_decoder<'a>() -> ADecoder<'a> { + | ^^^^^^^^^^^^ within `ADecoder<'a>`, the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:7:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `BDecoder` + --> $DIR/dropck-normalize-errors.rs:26:12 + | +LL | pub struct BDecoder { + | ^^^^^^^^ +note: required because it appears within the type `ADecoder<'a>` + --> $DIR/dropck-normalize-errors.rs:12:12 + | +LL | pub struct ADecoder<'a> { + | ^^^^^^^^ + = note: the return type of a function must have a statically known size + +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied in `BDecoder` + --> $DIR/dropck-normalize-errors.rs:23:20 + | +LL | type Decoder = BDecoder; + | ^^^^^^^^ within `BDecoder`, the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:7:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `BDecoder` + --> $DIR/dropck-normalize-errors.rs:26:12 + | +LL | pub struct BDecoder { + | ^^^^^^^^ +note: required by a bound in `Decode::Decoder` + --> $DIR/dropck-normalize-errors.rs:4:5 + | +LL | type Decoder; + | ^^^^^^^^^^^^^ required by this bound in `Decode::Decoder` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Decoder: ?Sized; + | ++++++++ + +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied + --> $DIR/dropck-normalize-errors.rs:27:22 + | +LL | non_implemented: ::Assoc, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:7:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied + --> $DIR/dropck-normalize-errors.rs:15:28 + | +LL | fn make_a_decoder<'a>() -> ADecoder<'a> { + | ^^^^^^^^^^^^ the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:7:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/drop/issue-100276.rs b/tests/ui/drop/issue-100276.rs index b44710e7c3f21..5d212b3a0a954 100644 --- a/tests/ui/drop/issue-100276.rs +++ b/tests/ui/drop/issue-100276.rs @@ -1,6 +1,11 @@ //@ check-pass //@ compile-flags: -Z validate-mir -#![feature(let_chains)] +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] compile-flags: -Z lint-mir +//@ [edition2024] edition: 2024 + +#![cfg_attr(edition2021, feature(let_chains))] fn let_chains(entry: std::io::Result) { if let Ok(entry) = entry diff --git a/tests/ui/drop/lint-if-let-rescope-gated.edition2021.stderr b/tests/ui/drop/lint-if-let-rescope-gated.edition2021.stderr index 070ba1c6a4cfa..0d6974d516b6e 100644 --- a/tests/ui/drop/lint-if-let-rescope-gated.edition2021.stderr +++ b/tests/ui/drop/lint-if-let-rescope-gated.edition2021.stderr @@ -11,12 +11,8 @@ LL | if let Some(_value) = Droppy.get() { note: value invokes this custom destructor --> $DIR/lint-if-let-rescope-gated.rs:14:1 | -LL | / impl Drop for Droppy { -LL | | fn drop(&mut self) { -LL | | println!("dropped"); -LL | | } -LL | | } - | |_^ +LL | impl Drop for Droppy { + | ^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope-gated.rs:30:5 | diff --git a/tests/ui/drop/lint-if-let-rescope-with-macro.stderr b/tests/ui/drop/lint-if-let-rescope-with-macro.stderr index f1ca0ba57de4f..a0afb8eddb532 100644 --- a/tests/ui/drop/lint-if-let-rescope-with-macro.stderr +++ b/tests/ui/drop/lint-if-let-rescope-with-macro.stderr @@ -18,12 +18,8 @@ LL | | }; note: value invokes this custom destructor --> $DIR/lint-if-let-rescope-with-macro.rs:22:1 | -LL | / impl Drop for Droppy { -LL | | fn drop(&mut self) { -LL | | println!("dropped"); -LL | | } -LL | | } - | |_^ +LL | impl Drop for Droppy { + | ^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope-with-macro.rs:12:38 | diff --git a/tests/ui/drop/lint-if-let-rescope.stderr b/tests/ui/drop/lint-if-let-rescope.stderr index e95ec8fcea7fe..ca2416efcb1a2 100644 --- a/tests/ui/drop/lint-if-let-rescope.stderr +++ b/tests/ui/drop/lint-if-let-rescope.stderr @@ -11,12 +11,8 @@ LL | if let Some(_value) = droppy().get() { note: value invokes this custom destructor --> $DIR/lint-if-let-rescope.rs:11:1 | -LL | / impl Drop for Droppy { -LL | | fn drop(&mut self) { -LL | | println!("dropped"); -LL | | } -LL | | } - | |_^ +LL | impl Drop for Droppy { + | ^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:32:5 | @@ -55,21 +51,13 @@ LL | } else if let Some(_value) = droppy().get() { note: value invokes this custom destructor --> $DIR/lint-if-let-rescope.rs:11:1 | -LL | / impl Drop for Droppy { -LL | | fn drop(&mut self) { -LL | | println!("dropped"); -LL | | } -LL | | } - | |_^ +LL | impl Drop for Droppy { + | ^^^^^^^^^^^^^^^^^^^^ note: value invokes this custom destructor --> $DIR/lint-if-let-rescope.rs:11:1 | -LL | / impl Drop for Droppy { -LL | | fn drop(&mut self) { -LL | | println!("dropped"); -LL | | } -LL | | } - | |_^ +LL | impl Drop for Droppy { + | ^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:42:5 | @@ -105,12 +93,8 @@ LL | } else if let Some(_value) = droppy().get() { note: value invokes this custom destructor --> $DIR/lint-if-let-rescope.rs:11:1 | -LL | / impl Drop for Droppy { -LL | | fn drop(&mut self) { -LL | | println!("dropped"); -LL | | } -LL | | } - | |_^ +LL | impl Drop for Droppy { + | ^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:54:5 | @@ -140,12 +124,8 @@ LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else note: value invokes this custom destructor --> $DIR/lint-if-let-rescope.rs:11:1 | -LL | / impl Drop for Droppy { -LL | | fn drop(&mut self) { -LL | | println!("dropped"); -LL | | } -LL | | } - | |_^ +LL | impl Drop for Droppy { + | ^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:58:69 | @@ -170,12 +150,8 @@ LL | if (if let Some(_value) = droppy().get() { true } else { false }) { note: value invokes this custom destructor --> $DIR/lint-if-let-rescope.rs:11:1 | -LL | / impl Drop for Droppy { -LL | | fn drop(&mut self) { -LL | | println!("dropped"); -LL | | } -LL | | } - | |_^ +LL | impl Drop for Droppy { + | ^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:72:53 | @@ -200,12 +176,8 @@ LL | } else if (((if let Some(_value) = droppy().get() { true } else { false note: value invokes this custom destructor --> $DIR/lint-if-let-rescope.rs:11:1 | -LL | / impl Drop for Droppy { -LL | | fn drop(&mut self) { -LL | | println!("dropped"); -LL | | } -LL | | } - | |_^ +LL | impl Drop for Droppy { + | ^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:78:62 | @@ -230,12 +202,8 @@ LL | while (if let Some(_value) = droppy().get() { false } else { true }) { note: value invokes this custom destructor --> $DIR/lint-if-let-rescope.rs:11:1 | -LL | / impl Drop for Droppy { -LL | | fn drop(&mut self) { -LL | | println!("dropped"); -LL | | } -LL | | } - | |_^ +LL | impl Drop for Droppy { + | ^^^^^^^^^^^^^^^^^^^^ help: the value is now dropped here in Edition 2024 --> $DIR/lint-if-let-rescope.rs:90:57 | diff --git a/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs index 6f64d83f8a0c3..e8368b0a369d8 100644 --- a/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs +++ b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs @@ -10,7 +10,7 @@ fn should_lint_with_potential_borrowck_err() { //~^ ERROR: relative drop order changing //~| WARN: this changes meaning in Rust 2024 //~| NOTE: this temporary value will be dropped at the end of the block - //~| borrow later used by call + //~| NOTE: borrow later used by call //~| NOTE: for more information, see } @@ -20,7 +20,7 @@ fn should_lint_with_unsafe_block() { //~^ ERROR: relative drop order changing //~| WARN: this changes meaning in Rust 2024 //~| NOTE: this temporary value will be dropped at the end of the block - //~| borrow later used by call + //~| NOTE: borrow later used by call //~| NOTE: for more information, see } @@ -32,7 +32,7 @@ fn should_lint_with_big_block() { //~^ ERROR: relative drop order changing //~| WARN: this changes meaning in Rust 2024 //~| NOTE: this temporary value will be dropped at the end of the block - //~| borrow later used here + //~| NOTE: borrow later used here //~| NOTE: for more information, see }) } @@ -44,7 +44,7 @@ fn another_temp_that_is_copy_in_arg() { //~^ ERROR: relative drop order changing //~| WARN: this changes meaning in Rust 2024 //~| NOTE: this temporary value will be dropped at the end of the block - //~| borrow later used by call + //~| NOTE: borrow later used by call //~| NOTE: for more information, see } diff --git a/tests/ui/drop/lint-tail-expr-drop-order.stderr b/tests/ui/drop/lint-tail-expr-drop-order.stderr index 6ff9b7c12681d..e124e9874d0b4 100644 --- a/tests/ui/drop/lint-tail-expr-drop-order.stderr +++ b/tests/ui/drop/lint-tail-expr-drop-order.stderr @@ -21,17 +21,13 @@ LL | } note: `#1` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:10:1 | -LL | / impl Drop for LoudDropper { -... | -LL | | } - | |_^ +LL | impl Drop for LoudDropper { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: `x` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:10:1 | -LL | / impl Drop for LoudDropper { -... | -LL | | } - | |_^ +LL | impl Drop for LoudDropper { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages note: the lint level is defined here --> $DIR/lint-tail-expr-drop-order.rs:6:9 @@ -62,17 +58,13 @@ LL | } note: `#1` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:10:1 | -LL | / impl Drop for LoudDropper { -... | -LL | | } - | |_^ +LL | impl Drop for LoudDropper { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: `x` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:10:1 | -LL | / impl Drop for LoudDropper { -... | -LL | | } - | |_^ +LL | impl Drop for LoudDropper { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 @@ -98,17 +90,13 @@ LL | } note: `#1` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:10:1 | -LL | / impl Drop for LoudDropper { -... | -LL | | } - | |_^ +LL | impl Drop for LoudDropper { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: `x` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:10:1 | -LL | / impl Drop for LoudDropper { -... | -LL | | } - | |_^ +LL | impl Drop for LoudDropper { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 @@ -134,10 +122,8 @@ LL | } note: `#1` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:10:1 | -LL | / impl Drop for LoudDropper { -... | -LL | | } - | |_^ +LL | impl Drop for LoudDropper { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 @@ -185,17 +171,13 @@ LL | } note: `#1` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:10:1 | -LL | / impl Drop for LoudDropper { -... | -LL | | } - | |_^ +LL | impl Drop for LoudDropper { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: `x` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:10:1 | -LL | / impl Drop for LoudDropper { -... | -LL | | } - | |_^ +LL | impl Drop for LoudDropper { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 @@ -221,23 +203,13 @@ LL | } note: `#1` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:193:5 | -LL | / impl Drop for LoudDropper3 { -LL | | -LL | | fn drop(&mut self) { -LL | | println!("loud drop"); -LL | | } -LL | | } - | |_____^ +LL | impl Drop for LoudDropper3 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `x` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:205:5 | -LL | / impl Drop for LoudDropper2 { -LL | | -LL | | fn drop(&mut self) { -LL | | println!("loud drop"); -LL | | } -LL | | } - | |_____^ +LL | impl Drop for LoudDropper2 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 @@ -263,17 +235,13 @@ LL | )); note: `#1` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:10:1 | -LL | / impl Drop for LoudDropper { -... | -LL | | } - | |_^ +LL | impl Drop for LoudDropper { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: `_x` invokes this custom destructor --> $DIR/lint-tail-expr-drop-order.rs:10:1 | -LL | / impl Drop for LoudDropper { -... | -LL | | } - | |_^ +LL | impl Drop for LoudDropper { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: aborting due to 8 previous errors diff --git a/tests/ui/drop/nonsense-drop-impl-issue-139278.rs b/tests/ui/drop/nonsense-drop-impl-issue-139278.rs new file mode 100644 index 0000000000000..86f4e4d86072a --- /dev/null +++ b/tests/ui/drop/nonsense-drop-impl-issue-139278.rs @@ -0,0 +1,10 @@ +//@ check-fail +struct Foo; + +impl Drop for Foo { //~ ERROR: not all trait items implemented + const SPLOK: u32 = 0; //~ ERROR: not a member of trait +} + +const X: Foo = Foo; + +fn main() {} diff --git a/tests/ui/drop/nonsense-drop-impl-issue-139278.stderr b/tests/ui/drop/nonsense-drop-impl-issue-139278.stderr new file mode 100644 index 0000000000000..825e883fa6d8a --- /dev/null +++ b/tests/ui/drop/nonsense-drop-impl-issue-139278.stderr @@ -0,0 +1,18 @@ +error[E0438]: const `SPLOK` is not a member of trait `Drop` + --> $DIR/nonsense-drop-impl-issue-139278.rs:5:5 + | +LL | const SPLOK: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^ not a member of trait `Drop` + +error[E0046]: not all trait items implemented, missing: `drop` + --> $DIR/nonsense-drop-impl-issue-139278.rs:4:1 + | +LL | impl Drop for Foo { + | ^^^^^^^^^^^^^^^^^ missing `drop` in implementation + | + = help: implement the missing item: `fn drop(&mut self) { todo!() }` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0046, E0438. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr b/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr index b0f971dd5cec6..7bf452e2496cb 100644 --- a/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr +++ b/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr @@ -27,24 +27,18 @@ LL | } note: `#2` invokes this custom destructor --> $DIR/tail_expr_drop_order-on-coroutine-unwind.rs:9:1 | -LL | / impl std::ops::Drop for Drop { -LL | | fn drop(&mut self) {} -LL | | } - | |_^ +LL | impl std::ops::Drop for Drop { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `#1` invokes this custom destructor --> $DIR/tail_expr_drop_order-on-coroutine-unwind.rs:9:1 | -LL | / impl std::ops::Drop for Drop { -LL | | fn drop(&mut self) {} -LL | | } - | |_^ +LL | impl std::ops::Drop for Drop { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `e` invokes this custom destructor --> $DIR/tail_expr_drop_order-on-coroutine-unwind.rs:9:1 | -LL | / impl std::ops::Drop for Drop { -LL | | fn drop(&mut self) {} -LL | | } - | |_^ +LL | impl std::ops::Drop for Drop { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages note: the lint level is defined here --> $DIR/tail_expr_drop_order-on-coroutine-unwind.rs:6:9 diff --git a/tests/ui/dropck/explicit-drop-bounds.bad1.stderr b/tests/ui/dropck/explicit-drop-bounds.bad1.stderr index 12d7f5b6cd301..28d7546d0c9bb 100644 --- a/tests/ui/dropck/explicit-drop-bounds.bad1.stderr +++ b/tests/ui/dropck/explicit-drop-bounds.bad1.stderr @@ -14,6 +14,22 @@ help: consider further restricting type parameter `T` with trait `Copy` LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` | ++++++++++++++++++++ +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/explicit-drop-bounds.rs:32:5 + | +LL | fn drop(&mut self) {} + | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +note: required by a bound in `DropMe` + --> $DIR/explicit-drop-bounds.rs:7:18 + | +LL | struct DropMe(T); + | ^^^^ required by this bound in `DropMe` +help: consider further restricting type parameter `T` with trait `Copy` + | +LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` + | ++++++++++++++++++++ + error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/explicit-drop-bounds.rs:32:18 | @@ -30,6 +46,6 @@ help: consider further restricting type parameter `T` with trait `Copy` LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` | ++++++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dropck/explicit-drop-bounds.bad2.stderr b/tests/ui/dropck/explicit-drop-bounds.bad2.stderr index 5851731e83461..c363676edea3e 100644 --- a/tests/ui/dropck/explicit-drop-bounds.bad2.stderr +++ b/tests/ui/dropck/explicit-drop-bounds.bad2.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/explicit-drop-bounds.rs:37:18 + --> $DIR/explicit-drop-bounds.rs:38:18 | LL | impl Drop for DropMe | ^^^^^^^^^ the trait `Copy` is not implemented for `T` @@ -15,7 +15,23 @@ LL | impl Drop for DropMe | +++++++++++++++++++ error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/explicit-drop-bounds.rs:40:18 + --> $DIR/explicit-drop-bounds.rs:41:5 + | +LL | fn drop(&mut self) {} + | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +note: required by a bound in `DropMe` + --> $DIR/explicit-drop-bounds.rs:7:18 + | +LL | struct DropMe(T); + | ^^^^ required by this bound in `DropMe` +help: consider restricting type parameter `T` with trait `Copy` + | +LL | impl Drop for DropMe + | +++++++++++++++++++ + +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/explicit-drop-bounds.rs:41:18 | LL | fn drop(&mut self) {} | ^^^^ the trait `Copy` is not implemented for `T` @@ -30,6 +46,6 @@ help: consider restricting type parameter `T` with trait `Copy` LL | impl Drop for DropMe | +++++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dropck/explicit-drop-bounds.rs b/tests/ui/dropck/explicit-drop-bounds.rs index 6ddac4d314f41..cd1d89ed9dbd1 100644 --- a/tests/ui/dropck/explicit-drop-bounds.rs +++ b/tests/ui/dropck/explicit-drop-bounds.rs @@ -31,6 +31,7 @@ where { fn drop(&mut self) {} //[bad1]~^ ERROR the trait bound `T: Copy` is not satisfied + //[bad1]~| ERROR the trait bound `T: Copy` is not satisfied } #[cfg(bad2)] @@ -39,6 +40,7 @@ impl Drop for DropMe { fn drop(&mut self) {} //[bad2]~^ ERROR the trait bound `T: Copy` is not satisfied + //[bad2]~| ERROR the trait bound `T: Copy` is not satisfied } fn main() {} diff --git a/tests/ui/dst/dst-bad-assign-3.rs b/tests/ui/dst/dst-bad-assign-3.rs index d199864d99c2d..e9e69134a62bc 100644 --- a/tests/ui/dst/dst-bad-assign-3.rs +++ b/tests/ui/dst/dst-bad-assign-3.rs @@ -1,8 +1,8 @@ // Forbid assignment into a dynamically sized type. -#![feature(unsized_tuple_coercion)] +//@ dont-require-annotations: NOTE -type Fat = (isize, &'static str, T); +struct Fat(isize, &'static str, T); #[derive(PartialEq,Eq)] struct Bar; @@ -28,12 +28,12 @@ impl ToBar for Bar1 { pub fn main() { // Assignment. - let f5: &mut Fat = &mut (5, "some str", Bar1 {f :42}); + let f5: &mut Fat = &mut Fat(5, "some str", Bar1 {f :42}); let z: Box = Box::new(Bar1 {f: 36}); f5.2 = Bar1 {f: 36}; //~^ ERROR mismatched types - //~| expected `dyn ToBar`, found `Bar1` - //~| expected trait object `dyn ToBar` - //~| found struct `Bar1` + //~| NOTE expected `dyn ToBar`, found `Bar1` + //~| NOTE expected trait object `dyn ToBar` + //~| NOTE found struct `Bar1` //~| ERROR the size for values of type } diff --git a/tests/ui/dst/dst-bad-assign.rs b/tests/ui/dst/dst-bad-assign.rs index c55fb2c3e5709..79a3f5c44d3aa 100644 --- a/tests/ui/dst/dst-bad-assign.rs +++ b/tests/ui/dst/dst-bad-assign.rs @@ -1,5 +1,7 @@ // Forbid assignment into a dynamically sized type. +//@ dont-require-annotations: NOTE + struct Fat { f1: isize, f2: &'static str, @@ -34,8 +36,8 @@ pub fn main() { let z: Box = Box::new(Bar1 {f: 36}); f5.ptr = Bar1 {f: 36}; //~^ ERROR mismatched types - //~| expected `dyn ToBar`, found `Bar1` - //~| expected trait object `dyn ToBar` - //~| found struct `Bar1` + //~| NOTE expected `dyn ToBar`, found `Bar1` + //~| NOTE expected trait object `dyn ToBar` + //~| NOTE found struct `Bar1` //~| ERROR the size for values of type } diff --git a/tests/ui/dst/dst-bad-assign.stderr b/tests/ui/dst/dst-bad-assign.stderr index f935d27e96e2f..fc73069bee011 100644 --- a/tests/ui/dst/dst-bad-assign.stderr +++ b/tests/ui/dst/dst-bad-assign.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/dst-bad-assign.rs:35:14 + --> $DIR/dst-bad-assign.rs:37:14 | LL | f5.ptr = Bar1 {f: 36}; | ------ ^^^^^^^^^^^^ expected `dyn ToBar`, found `Bar1` @@ -11,7 +11,7 @@ LL | f5.ptr = Bar1 {f: 36}; = help: `Bar1` implements `ToBar` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well error[E0277]: the size for values of type `dyn ToBar` cannot be known at compilation time - --> $DIR/dst-bad-assign.rs:35:5 + --> $DIR/dst-bad-assign.rs:37:5 | LL | f5.ptr = Bar1 {f: 36}; | ^^^^^^ doesn't have a size known at compile-time diff --git a/tests/ui/dst/dst-bad-coerce1.rs b/tests/ui/dst/dst-bad-coerce1.rs index 7ef237e39e36e..5ee0bfa9fbfba 100644 --- a/tests/ui/dst/dst-bad-coerce1.rs +++ b/tests/ui/dst/dst-bad-coerce1.rs @@ -1,7 +1,5 @@ // Attempt to change the type as well as unsizing. -#![feature(unsized_tuple_coercion)] - struct Fat { ptr: T } @@ -21,16 +19,4 @@ pub fn main() { let f2: &Fat = &f1; let f3: &Fat = f2; //~^ ERROR `Foo: Bar` is not satisfied - - // Tuple with a vec of isize. - let f1 = ([1, 2, 3],); - let f2: &([isize; 3],) = &f1; - let f3: &([usize],) = f2; - //~^ ERROR mismatched types - - // Tuple with a trait. - let f1 = (Foo,); - let f2: &(Foo,) = &f1; - let f3: &(dyn Bar,) = f2; - //~^ ERROR `Foo: Bar` is not satisfied } diff --git a/tests/ui/dst/dst-bad-coerce1.stderr b/tests/ui/dst/dst-bad-coerce1.stderr index 455d15e935fae..68456b8642c7b 100644 --- a/tests/ui/dst/dst-bad-coerce1.stderr +++ b/tests/ui/dst/dst-bad-coerce1.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/dst-bad-coerce1.rs:16:29 + --> $DIR/dst-bad-coerce1.rs:14:29 | LL | let f3: &Fat<[usize]> = f2; | ------------- ^^ expected `&Fat<[usize]>`, found `&Fat<[isize; 3]>` @@ -10,43 +10,19 @@ LL | let f3: &Fat<[usize]> = f2; found reference `&Fat<[isize; 3]>` error[E0277]: the trait bound `Foo: Bar` is not satisfied - --> $DIR/dst-bad-coerce1.rs:22:29 + --> $DIR/dst-bad-coerce1.rs:20:29 | LL | let f3: &Fat = f2; | ^^ the trait `Bar` is not implemented for `Foo` | help: this trait has no implementations, consider adding one - --> $DIR/dst-bad-coerce1.rs:10:1 + --> $DIR/dst-bad-coerce1.rs:8:1 | LL | trait Bar { fn bar(&self) {} } | ^^^^^^^^^ = note: required for the cast from `&Fat` to `&Fat` -error[E0308]: mismatched types - --> $DIR/dst-bad-coerce1.rs:28:27 - | -LL | let f3: &([usize],) = f2; - | ----------- ^^ expected `&([usize],)`, found `&([isize; 3],)` - | | - | expected due to this - | - = note: expected reference `&([usize],)` - found reference `&([isize; 3],)` - -error[E0277]: the trait bound `Foo: Bar` is not satisfied - --> $DIR/dst-bad-coerce1.rs:34:27 - | -LL | let f3: &(dyn Bar,) = f2; - | ^^ the trait `Bar` is not implemented for `Foo` - | -help: this trait has no implementations, consider adding one - --> $DIR/dst-bad-coerce1.rs:10:1 - | -LL | trait Bar { fn bar(&self) {} } - | ^^^^^^^^^ - = note: required for the cast from `&(Foo,)` to `&(dyn Bar,)` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0308. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/dst/dst-bad-coerce3.rs b/tests/ui/dst/dst-bad-coerce3.rs index fd5ee3b57bb42..508f009c33e44 100644 --- a/tests/ui/dst/dst-bad-coerce3.rs +++ b/tests/ui/dst/dst-bad-coerce3.rs @@ -1,7 +1,5 @@ // Attempt to extend the lifetime as well as unsizing. -#![feature(unsized_tuple_coercion)] - struct Fat { ptr: T } @@ -20,16 +18,6 @@ fn baz<'a>() { let f1 = Fat { ptr: Foo }; let f2: &Fat = &f1; //~ ERROR `f1` does not live long enough let f3: &'a Fat = f2; - - // Tuple with a vec of ints. - let f1 = ([1, 2, 3],); - let f2: &([isize; 3],) = &f1; //~ ERROR `f1` does not live long enough - let f3: &'a ([isize],) = f2; - - // Tuple with a trait. - let f1 = (Foo,); - let f2: &(Foo,) = &f1; //~ ERROR `f1` does not live long enough - let f3: &'a (dyn Bar,) = f2; } pub fn main() { diff --git a/tests/ui/dst/dst-bad-coerce3.stderr b/tests/ui/dst/dst-bad-coerce3.stderr index 1254250bcbd85..177535b8c7c75 100644 --- a/tests/ui/dst/dst-bad-coerce3.stderr +++ b/tests/ui/dst/dst-bad-coerce3.stderr @@ -1,5 +1,5 @@ error[E0597]: `f1` does not live long enough - --> $DIR/dst-bad-coerce3.rs:16:32 + --> $DIR/dst-bad-coerce3.rs:14:32 | LL | fn baz<'a>() { | -- lifetime `'a` defined here @@ -15,7 +15,7 @@ LL | } | - `f1` dropped here while still borrowed error[E0597]: `f1` does not live long enough - --> $DIR/dst-bad-coerce3.rs:21:25 + --> $DIR/dst-bad-coerce3.rs:19:25 | LL | fn baz<'a>() { | -- lifetime `'a` defined here @@ -26,41 +26,9 @@ LL | let f2: &Fat = &f1; | ^^^ borrowed value does not live long enough LL | let f3: &'a Fat = f2; | ---------------- type annotation requires that `f1` is borrowed for `'a` -... -LL | } - | - `f1` dropped here while still borrowed - -error[E0597]: `f1` does not live long enough - --> $DIR/dst-bad-coerce3.rs:26:30 - | -LL | fn baz<'a>() { - | -- lifetime `'a` defined here -... -LL | let f1 = ([1, 2, 3],); - | -- binding `f1` declared here -LL | let f2: &([isize; 3],) = &f1; - | ^^^ borrowed value does not live long enough -LL | let f3: &'a ([isize],) = f2; - | -------------- type annotation requires that `f1` is borrowed for `'a` -... -LL | } - | - `f1` dropped here while still borrowed - -error[E0597]: `f1` does not live long enough - --> $DIR/dst-bad-coerce3.rs:31:23 - | -LL | fn baz<'a>() { - | -- lifetime `'a` defined here -... -LL | let f1 = (Foo,); - | -- binding `f1` declared here -LL | let f2: &(Foo,) = &f1; - | ^^^ borrowed value does not live long enough -LL | let f3: &'a (dyn Bar,) = f2; - | -------------- type annotation requires that `f1` is borrowed for `'a` LL | } | - `f1` dropped here while still borrowed -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/dst/dst-bad-coerce4.rs b/tests/ui/dst/dst-bad-coerce4.rs index 9f297915e58c9..b5288d5d3e1f4 100644 --- a/tests/ui/dst/dst-bad-coerce4.rs +++ b/tests/ui/dst/dst-bad-coerce4.rs @@ -1,7 +1,5 @@ // Attempt to coerce from unsized to sized. -#![feature(unsized_tuple_coercion)] - struct Fat { ptr: T } @@ -11,15 +9,8 @@ pub fn main() { let f1: &Fat<[isize]> = &Fat { ptr: [1, 2, 3] }; let f2: &Fat<[isize; 3]> = f1; //~^ ERROR mismatched types - //~| expected `&Fat<[isize; 3]>`, found `&Fat<[isize]>` - //~| expected reference `&Fat<[isize; 3]>` - //~| found reference `&Fat<[isize]>` - - // Tuple with a vec of isizes. - let f1: &([isize],) = &([1, 2, 3],); - let f2: &([isize; 3],) = f1; - //~^ ERROR mismatched types - //~| expected `&([isize; 3],)`, found `&([isize],)` - //~| expected reference `&([isize; 3],)` - //~| found reference `&([isize],)` + //~| NOTE expected `&Fat<[isize; 3]>`, found `&Fat<[isize]>` + //~| NOTE expected reference `&Fat<[isize; 3]>` + //~| NOTE found reference `&Fat<[isize]>` + //~| NOTE expected due to this } diff --git a/tests/ui/dst/dst-bad-coerce4.stderr b/tests/ui/dst/dst-bad-coerce4.stderr index 46e7dba817c84..5c14c0bfedd4d 100644 --- a/tests/ui/dst/dst-bad-coerce4.stderr +++ b/tests/ui/dst/dst-bad-coerce4.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/dst-bad-coerce4.rs:12:32 + --> $DIR/dst-bad-coerce4.rs:10:32 | LL | let f2: &Fat<[isize; 3]> = f1; | ---------------- ^^ expected `&Fat<[isize; 3]>`, found `&Fat<[isize]>` @@ -9,17 +9,6 @@ LL | let f2: &Fat<[isize; 3]> = f1; = note: expected reference `&Fat<[isize; 3]>` found reference `&Fat<[isize]>` -error[E0308]: mismatched types - --> $DIR/dst-bad-coerce4.rs:20:30 - | -LL | let f2: &([isize; 3],) = f1; - | -------------- ^^ expected `&([isize; 3],)`, found `&([isize],)` - | | - | expected due to this - | - = note: expected reference `&([isize; 3],)` - found reference `&([isize],)` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/dst/dst-bad-deep-2.rs b/tests/ui/dst/dst-bad-deep-2.rs index e587399135d32..76d3575136973 100644 --- a/tests/ui/dst/dst-bad-deep-2.rs +++ b/tests/ui/dst/dst-bad-deep-2.rs @@ -3,11 +3,11 @@ // because it would require stack allocation of an unsized temporary (*g in the // test). -#![feature(unsized_tuple_coercion)] +struct Fat(T); pub fn main() { - let f: ([isize; 3],) = ([5, 6, 7],); - let g: &([isize],) = &f; - let h: &(([isize],),) = &(*g,); + let f: Fat<[isize; 3]> = Fat([5, 6, 7]); + let g: &Fat<[isize]> = &f; + let h: &Fat> = &Fat(*g); //~^ ERROR the size for values of type } diff --git a/tests/ui/dst/dst-bad-deep-2.stderr b/tests/ui/dst/dst-bad-deep-2.stderr index c7e9854340f09..de3f71d206fb4 100644 --- a/tests/ui/dst/dst-bad-deep-2.stderr +++ b/tests/ui/dst/dst-bad-deep-2.stderr @@ -1,13 +1,17 @@ error[E0277]: the size for values of type `[isize]` cannot be known at compilation time - --> $DIR/dst-bad-deep-2.rs:11:30 + --> $DIR/dst-bad-deep-2.rs:11:38 | -LL | let h: &(([isize],),) = &(*g,); - | ^^^^^ doesn't have a size known at compile-time +LL | let h: &Fat> = &Fat(*g); + | ^^ doesn't have a size known at compile-time | - = 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 + = help: within `Fat<[isize]>`, the trait `Sized` is not implemented for `[isize]` +note: required because it appears within the type `Fat<[isize]>` + --> $DIR/dst-bad-deep-2.rs:6:8 + | +LL | struct Fat(T); + | ^^^ + = note: all function arguments must have a statically known size + = help: unsized fn params are gated as an unstable feature error: aborting due to 1 previous error diff --git a/tests/ui/dupe-first-attr.rs b/tests/ui/dupe-first-attr.rs deleted file mode 100644 index c254df050c153..0000000000000 --- a/tests/ui/dupe-first-attr.rs +++ /dev/null @@ -1,25 +0,0 @@ -//@ run-pass - -// Regression test for a problem with the first mod attribute -// being applied to every mod - - -#[cfg(target_os = "linux")] -mod hello {} - -#[cfg(target_os = "macos")] -mod hello {} - -#[cfg(target_os = "windows")] -mod hello {} - -#[cfg(target_os = "freebsd")] -mod hello {} - -#[cfg(target_os = "dragonfly")] -mod hello {} - -#[cfg(target_os = "android")] -mod hello {} - -fn main() {} diff --git a/tests/ui/duplicate-label-E0381-issue-129274.rs b/tests/ui/duplicate-label-E0381-issue-129274.rs deleted file mode 100644 index b2156e630c87f..0000000000000 --- a/tests/ui/duplicate-label-E0381-issue-129274.rs +++ /dev/null @@ -1,13 +0,0 @@ -fn main() { - fn test() { - loop { - let blah: Option; - if true { - blah = Some("".to_string()); - } - if let Some(blah) = blah.as_ref() { //~ ERROR E0381 - } - } - } - println!("{:?}", test()) -} diff --git a/tests/ui/duplicate-label-E0381-issue-129274.stderr b/tests/ui/duplicate-label-E0381-issue-129274.stderr deleted file mode 100644 index 7f8bddb17c5a8..0000000000000 --- a/tests/ui/duplicate-label-E0381-issue-129274.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0381]: used binding `blah` is possibly-uninitialized - --> $DIR/duplicate-label-E0381-issue-129274.rs:8:33 - | -LL | let blah: Option; - | ---- binding declared here but left uninitialized -LL | if true { -LL | blah = Some("".to_string()); - | ---- binding initialized here in some conditions -LL | } -LL | if let Some(blah) = blah.as_ref() { - | ^^^^ `blah` used here but it is possibly-uninitialized - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0381`. diff --git a/tests/ui/duplicate/dupe-symbols-1.rs b/tests/ui/duplicate/dupe-symbols-1.rs index f49bf44a06126..4e03d7c4ce5a7 100644 --- a/tests/ui/duplicate/dupe-symbols-1.rs +++ b/tests/ui/duplicate/dupe-symbols-1.rs @@ -10,5 +10,5 @@ pub fn a() { #[export_name="fail"] pub fn b() { -//~^ symbol `fail` is already defined +//~^ ERROR symbol `fail` is already defined } diff --git a/tests/ui/duplicate/dupe-symbols-2.rs b/tests/ui/duplicate/dupe-symbols-2.rs index 343c7131d1fb1..03fff570dff5b 100644 --- a/tests/ui/duplicate/dupe-symbols-2.rs +++ b/tests/ui/duplicate/dupe-symbols-2.rs @@ -13,6 +13,6 @@ pub mod a { pub mod b { #[no_mangle] pub extern "C" fn fail() { - //~^ symbol `fail` is already defined + //~^ ERROR symbol `fail` is already defined } } diff --git a/tests/ui/duplicate/dupe-symbols-3.rs b/tests/ui/duplicate/dupe-symbols-3.rs index 365ec182f53a3..41bbe51735733 100644 --- a/tests/ui/duplicate/dupe-symbols-3.rs +++ b/tests/ui/duplicate/dupe-symbols-3.rs @@ -10,5 +10,5 @@ pub fn a() { #[no_mangle] pub fn fail() { -//~^ symbol `fail` is already defined +//~^ ERROR symbol `fail` is already defined } diff --git a/tests/ui/duplicate/dupe-symbols-4.rs b/tests/ui/duplicate/dupe-symbols-4.rs index a9b7d689ad425..d961ba10e72fe 100644 --- a/tests/ui/duplicate/dupe-symbols-4.rs +++ b/tests/ui/duplicate/dupe-symbols-4.rs @@ -1,7 +1,5 @@ //@ build-fail -// -//@ error-pattern: symbol `fail` is already defined #![crate_type="rlib"] #![allow(warnings)] @@ -20,5 +18,5 @@ impl A for B { impl A for C { #[no_mangle] - fn fail(self) {} + fn fail(self) {} //~ ERROR symbol `fail` is already defined } diff --git a/tests/ui/duplicate/dupe-symbols-4.stderr b/tests/ui/duplicate/dupe-symbols-4.stderr index 4c5f1e7867f50..e85d3f2b06341 100644 --- a/tests/ui/duplicate/dupe-symbols-4.stderr +++ b/tests/ui/duplicate/dupe-symbols-4.stderr @@ -1,5 +1,5 @@ error: symbol `fail` is already defined - --> $DIR/dupe-symbols-4.rs:23:5 + --> $DIR/dupe-symbols-4.rs:21:5 | LL | fn fail(self) {} | ^^^^^^^^^^^^^ diff --git a/tests/ui/duplicate/dupe-symbols-5.rs b/tests/ui/duplicate/dupe-symbols-5.rs index 2ed803c1ddadb..4aaf2bd29c5c7 100644 --- a/tests/ui/duplicate/dupe-symbols-5.rs +++ b/tests/ui/duplicate/dupe-symbols-5.rs @@ -9,5 +9,5 @@ static HELLO: u8 = 0; #[export_name="fail"] pub fn b() { -//~^ symbol `fail` is already defined +//~^ ERROR symbol `fail` is already defined } diff --git a/tests/ui/duplicate/dupe-symbols-6.rs b/tests/ui/duplicate/dupe-symbols-6.rs index 9841be7365a30..b3f430f51bb92 100644 --- a/tests/ui/duplicate/dupe-symbols-6.rs +++ b/tests/ui/duplicate/dupe-symbols-6.rs @@ -8,4 +8,4 @@ static HELLO: u8 = 0; #[export_name="fail"] static HELLO_TWICE: u16 = 0; -//~^ symbol `fail` is already defined +//~^ ERROR symbol `fail` is already defined diff --git a/tests/ui/duplicate/dupe-symbols-7.rs b/tests/ui/duplicate/dupe-symbols-7.rs index 162c3c40446d7..ea17942493cd7 100644 --- a/tests/ui/duplicate/dupe-symbols-7.rs +++ b/tests/ui/duplicate/dupe-symbols-7.rs @@ -1,10 +1,7 @@ //@ build-fail //@ ignore-wasi wasi does different things with the `main` symbol -// -//@ error-pattern: entry symbol `main` declared multiple times - #![allow(warnings)] #[no_mangle] -fn main(){} +fn main(){} //~ ERROR entry symbol `main` declared multiple times diff --git a/tests/ui/duplicate/dupe-symbols-7.stderr b/tests/ui/duplicate/dupe-symbols-7.stderr index aa6213af2e4f5..d75038569397f 100644 --- a/tests/ui/duplicate/dupe-symbols-7.stderr +++ b/tests/ui/duplicate/dupe-symbols-7.stderr @@ -1,5 +1,5 @@ error: entry symbol `main` declared multiple times - --> $DIR/dupe-symbols-7.rs:10:1 + --> $DIR/dupe-symbols-7.rs:7:1 | LL | fn main(){} | ^^^^^^^^^ diff --git a/tests/ui/duplicate/dupe-symbols-8.rs b/tests/ui/duplicate/dupe-symbols-8.rs index 258e91fa8c878..b3c635f38ed64 100644 --- a/tests/ui/duplicate/dupe-symbols-8.rs +++ b/tests/ui/duplicate/dupe-symbols-8.rs @@ -1,11 +1,10 @@ //@ build-fail -//@ error-pattern: entry symbol `main` declared multiple times //@ ignore-wasi wasi does different things with the `main` symbol // // See #67946. #![allow(warnings)] -fn main() { +fn main() { //~ ERROR entry symbol `main` declared multiple times extern "Rust" { fn main(); } diff --git a/tests/ui/duplicate/dupe-symbols-8.stderr b/tests/ui/duplicate/dupe-symbols-8.stderr index 0f47d3683b5a5..494fdcca327bb 100644 --- a/tests/ui/duplicate/dupe-symbols-8.stderr +++ b/tests/ui/duplicate/dupe-symbols-8.stderr @@ -1,5 +1,5 @@ error: entry symbol `main` declared multiple times - --> $DIR/dupe-symbols-8.rs:8:1 + --> $DIR/dupe-symbols-8.rs:7:1 | LL | fn main() { | ^^^^^^^^^ diff --git a/tests/ui/duplicate_entry_error.rs b/tests/ui/duplicate_entry_error.rs deleted file mode 100644 index 5a25802c6e789..0000000000000 --- a/tests/ui/duplicate_entry_error.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@ normalize-stderr: "loaded from .*libstd-.*.rlib" -> "loaded from SYSROOT/libstd-*.rlib" -// note-pattern: first defined in crate `std`. - -// Test for issue #31788 and E0152 - -#![feature(lang_items)] - -extern crate core; - -use core::panic::PanicInfo; - -#[lang = "panic_impl"] -fn panic_impl(info: &PanicInfo) -> ! { -//~^ ERROR: found duplicate lang item `panic_impl` - loop {} -} - -fn main() {} diff --git a/tests/ui/duplicate_entry_error.stderr b/tests/ui/duplicate_entry_error.stderr deleted file mode 100644 index 958e9c7527d8c..0000000000000 --- a/tests/ui/duplicate_entry_error.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0152]: found duplicate lang item `panic_impl` - --> $DIR/duplicate_entry_error.rs:13:1 - | -LL | / fn panic_impl(info: &PanicInfo) -> ! { -LL | | -LL | | loop {} -LL | | } - | |_^ - | - = 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 - = note: second definition in the local crate (`duplicate_entry_error`) - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0152`. diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.new.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.new.stderr index 87207d607e2a7..4c4cb45293fe1 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.new.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.new.stderr @@ -4,7 +4,7 @@ error[E0782]: expected a type, found a trait LL | fn id(f: Copy) -> usize { | ^^^^ | - = note: `Copy` it is dyn-incompatible, so it can't be `dyn` + = note: `Copy` is dyn-incompatible, otherwise a trait object could be used help: use a new generic type parameter, constrained by `Copy` | LL - fn id(f: Copy) -> usize { diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.new.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.new.stderr index 6075e313f4ed4..d3208d07e91ef 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.new.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.new.stderr @@ -4,7 +4,7 @@ error[E0782]: expected a type, found a trait LL | trait A { fn g(b: B) -> B; } | ^ | - = note: `B` it is dyn-incompatible, so it can't be `dyn` + = note: `B` is dyn-incompatible, otherwise a trait object could be used help: use a new generic type parameter, constrained by `B` | LL - trait A { fn g(b: B) -> B; } @@ -32,7 +32,7 @@ error[E0782]: expected a type, found a trait LL | trait B { fn f(a: A) -> A; } | ^ | - = note: `A` it is dyn-incompatible, so it can't be `dyn` + = note: `A` is dyn-incompatible, otherwise a trait object could be used help: use a new generic type parameter, constrained by `A` | LL - trait B { fn f(a: A) -> A; } diff --git a/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr b/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr index bb3f7899bf1cc..c1e93ccb83ca9 100644 --- a/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr +++ b/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr @@ -1,8 +1,8 @@ error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/mention-correct-dyn-incompatible-trait.rs:19:15 + --> $DIR/mention-correct-dyn-incompatible-trait.rs:19:30 | LL | let test: &mut dyn Bar = &mut thing; - | ^^^^^^^^^^^^ `Bar` is not dyn compatible + | ^^^^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -15,12 +15,13 @@ LL | trait Bar: Foo { } | --- this trait is not dyn compatible... = help: consider moving `foo` to another trait = help: only type `Thing` implements `Bar`; consider using it directly instead. + = note: required for the cast from `&mut Thing` to `&mut dyn Bar` error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/mention-correct-dyn-incompatible-trait.rs:19:30 + --> $DIR/mention-correct-dyn-incompatible-trait.rs:19:15 | LL | let test: &mut dyn Bar = &mut thing; - | ^^^^^^^^^^ `Bar` is not dyn compatible + | ^^^^^^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -33,7 +34,6 @@ LL | trait Bar: Foo { } | --- this trait is not dyn compatible... = help: consider moving `foo` to another trait = help: only type `Thing` implements `Bar`; consider using it directly instead. - = note: required for the cast from `&mut Thing` to `&mut dyn Bar` error: aborting due to 2 previous errors diff --git a/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.stderr b/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.stderr index 832e7ef4dc38c..2cf244185e697 100644 --- a/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.stderr +++ b/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.stderr @@ -187,166 +187,6 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're LL | fn parrot() -> &'static mut Trait { | +++++++ -error[E0782]: expected a type, found a trait - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:52:16 - | -LL | fn foo(_: &Trait); - | ^^^^^ - | -help: use a new generic type parameter, constrained by `Trait` - | -LL - fn foo(_: &Trait); -LL + fn foo(_: &T); - | -help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference - | -LL | fn foo(_: &impl Trait); - | ++++ -help: alternatively, use a trait object to accept any type that implements `Trait`, accessing its methods at runtime using dynamic dispatch - | -LL | fn foo(_: &dyn Trait); - | +++ - -error[E0782]: expected a type, found a trait - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:55:19 - | -LL | fn bar(_: &'a Trait); - | ^^^^^ - | -help: use a new generic type parameter, constrained by `Trait` - | -LL - fn bar(_: &'a Trait); -LL + fn bar(_: &'a T); - | -help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference - | -LL | fn bar(_: &'a impl Trait); - | ++++ -help: alternatively, use a trait object to accept any type that implements `Trait`, accessing its methods at runtime using dynamic dispatch - | -LL | fn bar(_: &'a dyn Trait); - | +++ - -error[E0782]: expected a type, found a trait - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:59:22 - | -LL | fn alice<'a>(_: &Trait); - | ^^^^^ - | -help: use a new generic type parameter, constrained by `Trait` - | -LL - fn alice<'a>(_: &Trait); -LL + fn alice<'a, T: Trait>(_: &T); - | -help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference - | -LL | fn alice<'a>(_: &impl Trait); - | ++++ -help: alternatively, use a trait object to accept any type that implements `Trait`, accessing its methods at runtime using dynamic dispatch - | -LL | fn alice<'a>(_: &dyn Trait); - | +++ - -error[E0782]: expected a type, found a trait - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:62:23 - | -LL | fn bob<'a>(_: &'a Trait); - | ^^^^^ - | -help: use a new generic type parameter, constrained by `Trait` - | -LL - fn bob<'a>(_: &'a Trait); -LL + fn bob<'a, T: Trait>(_: &'a T); - | -help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference - | -LL | fn bob<'a>(_: &'a impl Trait); - | ++++ -help: alternatively, use a trait object to accept any type that implements `Trait`, accessing its methods at runtime using dynamic dispatch - | -LL | fn bob<'a>(_: &'a dyn Trait); - | +++ - -error[E0782]: expected a type, found a trait - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:65:18 - | -LL | fn cat() -> &Trait; - | ^^^^^ - | -help: use `impl Trait` to return an opaque type, as long as you return a single underlying type - | -LL | fn cat() -> &impl Trait; - | ++++ -help: alternatively, you can return an owned trait object - | -LL - fn cat() -> &Trait; -LL + fn cat() -> Box; - | - -error[E0782]: expected a type, found a trait - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:69:22 - | -LL | fn dog<'a>() -> &Trait { - | ^^^^^ - | -help: use `impl Trait` to return an opaque type, as long as you return a single underlying type - | -LL | fn dog<'a>() -> &impl Trait { - | ++++ -help: alternatively, you can return an owned trait object - | -LL - fn dog<'a>() -> &Trait { -LL + fn dog<'a>() -> Box { - | - -error[E0782]: expected a type, found a trait - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:75:24 - | -LL | fn kitten() -> &'a Trait { - | ^^^^^ - | -help: use `impl Trait` to return an opaque type, as long as you return a single underlying type - | -LL | fn kitten() -> &'a impl Trait { - | ++++ -help: alternatively, you can return an owned trait object - | -LL - fn kitten() -> &'a Trait { -LL + fn kitten() -> Box { - | - -error[E0782]: expected a type, found a trait - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:81:27 - | -LL | fn puppy<'a>() -> &'a Trait { - | ^^^^^ - | -help: use `impl Trait` to return an opaque type, as long as you return a single underlying type - | -LL | fn puppy<'a>() -> &'a impl Trait { - | ++++ -help: alternatively, you can return an owned trait object - | -LL - fn puppy<'a>() -> &'a Trait { -LL + fn puppy<'a>() -> Box { - | - -error[E0782]: expected a type, found a trait - --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:86:25 - | -LL | fn parrot() -> &mut Trait { - | ^^^^^ - | -help: use `impl Trait` to return an opaque type, as long as you return a single underlying type - | -LL | fn parrot() -> &mut impl Trait { - | ++++ -help: alternatively, you can return an owned trait object - | -LL - fn parrot() -> &mut Trait { -LL + fn parrot() -> Box { - | - error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:93:12 | @@ -667,6 +507,166 @@ LL - fn parrot() -> &mut Trait { LL + fn parrot() -> Box { | +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:52:16 + | +LL | fn foo(_: &Trait); + | ^^^^^ + | +help: use a new generic type parameter, constrained by `Trait` + | +LL - fn foo(_: &Trait); +LL + fn foo(_: &T); + | +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn foo(_: &impl Trait); + | ++++ +help: alternatively, use a trait object to accept any type that implements `Trait`, accessing its methods at runtime using dynamic dispatch + | +LL | fn foo(_: &dyn Trait); + | +++ + +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:55:19 + | +LL | fn bar(_: &'a Trait); + | ^^^^^ + | +help: use a new generic type parameter, constrained by `Trait` + | +LL - fn bar(_: &'a Trait); +LL + fn bar(_: &'a T); + | +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn bar(_: &'a impl Trait); + | ++++ +help: alternatively, use a trait object to accept any type that implements `Trait`, accessing its methods at runtime using dynamic dispatch + | +LL | fn bar(_: &'a dyn Trait); + | +++ + +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:59:22 + | +LL | fn alice<'a>(_: &Trait); + | ^^^^^ + | +help: use a new generic type parameter, constrained by `Trait` + | +LL - fn alice<'a>(_: &Trait); +LL + fn alice<'a, T: Trait>(_: &T); + | +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn alice<'a>(_: &impl Trait); + | ++++ +help: alternatively, use a trait object to accept any type that implements `Trait`, accessing its methods at runtime using dynamic dispatch + | +LL | fn alice<'a>(_: &dyn Trait); + | +++ + +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:62:23 + | +LL | fn bob<'a>(_: &'a Trait); + | ^^^^^ + | +help: use a new generic type parameter, constrained by `Trait` + | +LL - fn bob<'a>(_: &'a Trait); +LL + fn bob<'a, T: Trait>(_: &'a T); + | +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn bob<'a>(_: &'a impl Trait); + | ++++ +help: alternatively, use a trait object to accept any type that implements `Trait`, accessing its methods at runtime using dynamic dispatch + | +LL | fn bob<'a>(_: &'a dyn Trait); + | +++ + +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:65:18 + | +LL | fn cat() -> &Trait; + | ^^^^^ + | +help: use `impl Trait` to return an opaque type, as long as you return a single underlying type + | +LL | fn cat() -> &impl Trait; + | ++++ +help: alternatively, you can return an owned trait object + | +LL - fn cat() -> &Trait; +LL + fn cat() -> Box; + | + +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:69:22 + | +LL | fn dog<'a>() -> &Trait { + | ^^^^^ + | +help: use `impl Trait` to return an opaque type, as long as you return a single underlying type + | +LL | fn dog<'a>() -> &impl Trait { + | ++++ +help: alternatively, you can return an owned trait object + | +LL - fn dog<'a>() -> &Trait { +LL + fn dog<'a>() -> Box { + | + +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:75:24 + | +LL | fn kitten() -> &'a Trait { + | ^^^^^ + | +help: use `impl Trait` to return an opaque type, as long as you return a single underlying type + | +LL | fn kitten() -> &'a impl Trait { + | ++++ +help: alternatively, you can return an owned trait object + | +LL - fn kitten() -> &'a Trait { +LL + fn kitten() -> Box { + | + +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:81:27 + | +LL | fn puppy<'a>() -> &'a Trait { + | ^^^^^ + | +help: use `impl Trait` to return an opaque type, as long as you return a single underlying type + | +LL | fn puppy<'a>() -> &'a impl Trait { + | ++++ +help: alternatively, you can return an owned trait object + | +LL - fn puppy<'a>() -> &'a Trait { +LL + fn puppy<'a>() -> Box { + | + +error[E0782]: expected a type, found a trait + --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:86:25 + | +LL | fn parrot() -> &mut Trait { + | ^^^^^ + | +help: use `impl Trait` to return an opaque type, as long as you return a single underlying type + | +LL | fn parrot() -> &mut impl Trait { + | ++++ +help: alternatively, you can return an owned trait object + | +LL - fn parrot() -> &mut Trait { +LL + fn parrot() -> Box { + | + error: aborting due to 42 previous errors Some errors have detailed explanations: E0106, E0261, E0782. diff --git a/tests/ui/dyn-compatibility/taint-const-eval.rs b/tests/ui/dyn-compatibility/taint-const-eval.rs index 7ea763e184698..64c4df611e658 100644 --- a/tests/ui/dyn-compatibility/taint-const-eval.rs +++ b/tests/ui/dyn-compatibility/taint-const-eval.rs @@ -5,8 +5,8 @@ trait Qux { } static FOO: &(dyn Qux + Sync) = "desc"; -//~^ the trait `Qux` is not dyn compatible -//~| the trait `Qux` is not dyn compatible -//~| the trait `Qux` is not dyn compatible +//~^ ERROR the trait `Qux` is not dyn compatible +//~| ERROR the trait `Qux` is not dyn compatible +//~| ERROR the trait `Qux` is not dyn compatible fn main() {} diff --git a/tests/ui/dyn-compatibility/trait-alias-self-projection.rs b/tests/ui/dyn-compatibility/trait-alias-self-projection.rs new file mode 100644 index 0000000000000..0badb738809e9 --- /dev/null +++ b/tests/ui/dyn-compatibility/trait-alias-self-projection.rs @@ -0,0 +1,12 @@ +#![feature(trait_alias)] +trait B = Fn() -> Self; +type D = &'static dyn B; +//~^ ERROR E0411 + +fn a() -> D { + unreachable!(); +} + +fn main() { + _ = a(); +} diff --git a/tests/ui/dyn-compatibility/trait-alias-self-projection.stderr b/tests/ui/dyn-compatibility/trait-alias-self-projection.stderr new file mode 100644 index 0000000000000..dccee02e9cd18 --- /dev/null +++ b/tests/ui/dyn-compatibility/trait-alias-self-projection.stderr @@ -0,0 +1,9 @@ +error[E0411]: `Self` is not allowed in type aliases + --> $DIR/trait-alias-self-projection.rs:3:19 + | +LL | type D = &'static dyn B; + | ^^^^^ `Self` is only available in impls, traits, and concrete type definitions + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0411`. diff --git a/tests/ui/dyn-keyword/dyn-2021-edition-error.rs b/tests/ui/dyn-keyword/dyn-2021-edition-error.rs index 5d607d82ea1ac..cc23c2c5055db 100644 --- a/tests/ui/dyn-keyword/dyn-2021-edition-error.rs +++ b/tests/ui/dyn-keyword/dyn-2021-edition-error.rs @@ -7,6 +7,12 @@ fn function(x: &SomeTrait, y: Box) { //~^ ERROR expected a type, found a trait } +// Regression test for . +extern "C" { + fn foo() -> *const SomeTrait; + //~^ ERROR expected a type, found a trait +} + trait SomeTrait {} fn main() {} diff --git a/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr b/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr index 8c4b809e76b04..b1d6385bde99e 100644 --- a/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr +++ b/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr @@ -29,6 +29,17 @@ help: you can add the `dyn` keyword if you want a trait object LL | fn function(x: &SomeTrait, y: Box) { | +++ +error[E0782]: expected a type, found a trait + --> $DIR/dyn-2021-edition-error.rs:12:24 + | +LL | fn foo() -> *const SomeTrait; + | ^^^^^^^^^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | fn foo() -> *const dyn SomeTrait; + | +++ + error[E0782]: expected a type, found a trait --> $DIR/dyn-2021-edition-error.rs:6:14 | @@ -40,6 +51,6 @@ help: you can add the `dyn` keyword if you want a trait object LL | let _x: &dyn SomeTrait = todo!(); | +++ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/dyn-keyword/suggest-struct-or-union-add-generic-impl-trait.rs b/tests/ui/dyn-keyword/suggest-struct-or-union-add-generic-impl-trait.rs new file mode 100644 index 0000000000000..4d573b90d606d --- /dev/null +++ b/tests/ui/dyn-keyword/suggest-struct-or-union-add-generic-impl-trait.rs @@ -0,0 +1,41 @@ +//@ edition:2021 +trait Trait {} + +struct Foo1 { + a: Trait, + //~^ ERROR expected a type, found a trait + b: u32, +} + +struct Foo2 { + a: i32, + b: Trait, + //~^ ERROR expected a type, found a trait +} + +enum Enum1 { + A(Trait), + //~^ ERROR expected a type, found a trait + B(u32), +} + +enum Enum2 { + A(u32), + B(Trait), + //~^ ERROR expected a type, found a trait +} + +// Regression test for . +pub struct InWhereClause +where + Trait:, {} +//~^ ERROR expected a type, found a trait + +struct HasGenerics { + f: Trait, + //~^ ERROR expected a type, found a trait + t: T, +} + + +fn main() {} diff --git a/tests/ui/dyn-keyword/suggest-struct-or-union-add-generic-impl-trait.stderr b/tests/ui/dyn-keyword/suggest-struct-or-union-add-generic-impl-trait.stderr new file mode 100644 index 0000000000000..9584147bbc77f --- /dev/null +++ b/tests/ui/dyn-keyword/suggest-struct-or-union-add-generic-impl-trait.stderr @@ -0,0 +1,74 @@ +error[E0782]: expected a type, found a trait + --> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:5:8 + | +LL | a: Trait, + | ^^^^^ + | +help: you might be missing a type parameter + | +LL ~ struct Foo1 { +LL ~ a: T, + | + +error[E0782]: expected a type, found a trait + --> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:12:8 + | +LL | b: Trait, + | ^^^^^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | b: dyn Trait, + | +++ + +error[E0782]: expected a type, found a trait + --> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:17:7 + | +LL | A(Trait), + | ^^^^^ + | +help: you might be missing a type parameter + | +LL ~ enum Enum1 { +LL ~ A(T), + | + +error[E0782]: expected a type, found a trait + --> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:24:7 + | +LL | B(Trait), + | ^^^^^ + | +help: you might be missing a type parameter + | +LL ~ enum Enum2 { +LL | A(u32), +LL ~ B(T), + | + +error[E0782]: expected a type, found a trait + --> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:35:8 + | +LL | f: Trait, + | ^^^^^ + | +help: you might be missing a type parameter + | +LL ~ struct HasGenerics { +LL ~ f: U, + | + +error[E0782]: expected a type, found a trait + --> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:31:5 + | +LL | Trait:, {} + | ^^^^^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | dyn Trait:, {} + | +++ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/dyn-star/async-block-dyn-star.rs b/tests/ui/dyn-star/async-block-dyn-star.rs index 9bffd6c672539..db133d94c9187 100644 --- a/tests/ui/dyn-star/async-block-dyn-star.rs +++ b/tests/ui/dyn-star/async-block-dyn-star.rs @@ -4,6 +4,6 @@ //~^ WARN the feature `dyn_star` is incomplete static S: dyn* Send + Sync = async { 42 }; -//~^ needs to have the same ABI as a pointer +//~^ ERROR needs to have the same ABI as a pointer pub fn main() {} diff --git a/tests/ui/dyn-star/feature-gate-dyn_star.rs b/tests/ui/dyn-star/feature-gate-dyn_star.rs index 41eed71cdc30a..b12fd7755be04 100644 --- a/tests/ui/dyn-star/feature-gate-dyn_star.rs +++ b/tests/ui/dyn-star/feature-gate-dyn_star.rs @@ -3,7 +3,7 @@ /// dyn* is not necessarily the final surface syntax (if we have one at all), /// but for now we will support it to aid in writing tests independently. pub fn dyn_star_parameter(_: &dyn* Send) { - //~^ `dyn*` trait objects are experimental + //~^ ERROR `dyn*` trait objects are experimental } fn main() {} diff --git a/tests/ui/dynamically-sized-types/dst-irrefutable-bind.rs b/tests/ui/dynamically-sized-types/dst-irrefutable-bind.rs index 5e352e930d82a..f4576229864b8 100644 --- a/tests/ui/dynamically-sized-types/dst-irrefutable-bind.rs +++ b/tests/ui/dynamically-sized-types/dst-irrefutable-bind.rs @@ -1,5 +1,4 @@ //@ run-pass -#![feature(unsized_tuple_coercion)] struct Test(T); @@ -14,14 +13,4 @@ fn main() { let slice = &[1,2,3]; let x = Test(&slice); let Test(&_slice) = x; - - - let x = (10, [1,2,3]); - let x : &(i32, [i32]) = &x; - - let & ref _y = x; - - let slice = &[1,2,3]; - let x = (10, &slice); - let (_, &_slice) = x; } diff --git a/tests/ui/dynamically-sized-types/dst-raw.rs b/tests/ui/dynamically-sized-types/dst-raw.rs index c32ee67dab9fb..935f0f11ca61f 100644 --- a/tests/ui/dynamically-sized-types/dst-raw.rs +++ b/tests/ui/dynamically-sized-types/dst-raw.rs @@ -1,8 +1,7 @@ //@ run-pass // Test DST raw pointers - -#![feature(unsized_tuple_coercion)] +#![allow(dangerous_implicit_autorefs)] trait Trait { fn foo(&self) -> isize; @@ -38,14 +37,6 @@ pub fn main() { }; assert_eq!(r, 42); - // raw DST tuple - let p = (A { f: 42 },); - let o: *const (dyn Trait,) = &p; - let r = unsafe { - (&*o).0.foo() - }; - assert_eq!(r, 42); - // raw slice let a: *const [_] = &[1, 2, 3]; unsafe { @@ -73,15 +64,6 @@ pub fn main() { assert_eq!(len, 3); } - // raw DST tuple with slice - let c: *const ([_],) = &([1, 2, 3],); - unsafe { - let b = (&*c).0[0]; - assert_eq!(b, 1); - let len = (&*c).0.len(); - assert_eq!(len, 3); - } - // all of the above with *mut let mut x = A { f: 42 }; let z: *mut dyn Trait = &mut x; @@ -97,13 +79,6 @@ pub fn main() { }; assert_eq!(r, 42); - let mut p = (A { f: 42 },); - let o: *mut (dyn Trait,) = &mut p; - let r = unsafe { - (&*o).0.foo() - }; - assert_eq!(r, 42); - let a: *mut [_] = &mut [1, 2, 3]; unsafe { let b = (*a)[2]; @@ -127,12 +102,4 @@ pub fn main() { let len = (&*c).f.len(); assert_eq!(len, 3); } - - let c: *mut ([_],) = &mut ([1, 2, 3],); - unsafe { - let b = (&*c).0[0]; - assert_eq!(b, 1); - let len = (&*c).0.len(); - assert_eq!(len, 3); - } } diff --git a/tests/ui/dynamically-sized-types/dst-trait-tuple.rs b/tests/ui/dynamically-sized-types/dst-trait-tuple.rs deleted file mode 100644 index 6ab8829859bf0..0000000000000 --- a/tests/ui/dynamically-sized-types/dst-trait-tuple.rs +++ /dev/null @@ -1,102 +0,0 @@ -//@ run-pass -#![allow(type_alias_bounds)] - -#![allow(unused_features)] -#![feature(unsized_tuple_coercion)] - -type Fat = (isize, &'static str, T); - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -struct Bar; - -#[derive(Copy, Clone, PartialEq, Eq)] -struct Bar1 { - f: isize -} - -trait ToBar { - fn to_bar(&self) -> Bar; - fn to_val(&self) -> isize; -} - -impl ToBar for Bar { - fn to_bar(&self) -> Bar { - *self - } - fn to_val(&self) -> isize { - 0 - } -} -impl ToBar for Bar1 { - fn to_bar(&self) -> Bar { - Bar - } - fn to_val(&self) -> isize { - self.f - } -} - -// x is a fat pointer -fn foo(x: &Fat) { - assert_eq!(x.0, 5); - assert_eq!(x.1, "some str"); - assert_eq!(x.2.to_bar(), Bar); - assert_eq!(x.2.to_val(), 42); - - let y = &x.2; - assert_eq!(y.to_bar(), Bar); - assert_eq!(y.to_val(), 42); -} - -fn bar(x: &dyn ToBar) { - assert_eq!(x.to_bar(), Bar); - assert_eq!(x.to_val(), 42); -} - -fn baz(x: &Fat>) { - assert_eq!(x.0, 5); - assert_eq!(x.1, "some str"); - assert_eq!((x.2).0, 8); - assert_eq!((x.2).1, "deep str"); - assert_eq!((x.2).2.to_bar(), Bar); - assert_eq!((x.2).2.to_val(), 42); - - let y = &(x.2).2; - assert_eq!(y.to_bar(), Bar); - assert_eq!(y.to_val(), 42); - -} - -pub fn main() { - let f1 = (5, "some str", Bar1 {f :42}); - foo(&f1); - let f2 = &f1; - foo(f2); - let f3: &Fat = f2; - foo(f3); - let f4: &Fat = &f1; - foo(f4); - let f5: &Fat = &(5, "some str", Bar1 {f :42}); - foo(f5); - - // Zero size object. - let f6: &Fat = &(5, "some str", Bar); - assert_eq!(f6.2.to_bar(), Bar); - - // &* - // - let f7: Box = Box::new(Bar1 {f :42}); - bar(&*f7); - - // Deep nesting - let f1 = (5, "some str", (8, "deep str", Bar1 {f :42})); - baz(&f1); - let f2 = &f1; - baz(f2); - let f3: &Fat> = f2; - baz(f3); - let f4: &Fat> = &f1; - baz(f4); - let f5: &Fat> = &(5, "some str", (8, "deep str", Bar1 {f :42})); - baz(f5); -} diff --git a/tests/ui/dynamically-sized-types/dst-tuple-no-reorder.rs b/tests/ui/dynamically-sized-types/dst-tuple-no-reorder.rs deleted file mode 100644 index ad48d88e48078..0000000000000 --- a/tests/ui/dynamically-sized-types/dst-tuple-no-reorder.rs +++ /dev/null @@ -1,26 +0,0 @@ -//@ run-pass - -#![feature(unsized_tuple_coercion)] - -// Ensure that unsizable fields that might be accessed don't get reordered - -fn nonzero_size() { - let sized: (u8, [u32; 2]) = (123, [456, 789]); - let unsize: &(u8, [u32]) = &sized; - assert_eq!(unsize.0, 123); - assert_eq!(unsize.1.len(), 2); - assert_eq!(unsize.1[0], 456); - assert_eq!(unsize.1[1], 789); -} - -fn zst() { - let sized: (u8, [u32; 0]) = (123, []); - let unsize: &(u8, [u32]) = &sized; - assert_eq!(unsize.0, 123); - assert_eq!(unsize.1.len(), 0); -} - -pub fn main() { - nonzero_size(); - zst(); -} diff --git a/tests/ui/dynamically-sized-types/dst-tuple-sole.rs b/tests/ui/dynamically-sized-types/dst-tuple-sole.rs deleted file mode 100644 index dc3363b9bde38..0000000000000 --- a/tests/ui/dynamically-sized-types/dst-tuple-sole.rs +++ /dev/null @@ -1,79 +0,0 @@ -//@ run-pass -#![allow(stable_features)] -#![allow(type_alias_bounds)] - -// As dst-tuple.rs, but the unsized field is the only field in the tuple. - - -#![feature(unsized_tuple_coercion)] - -type Fat = (T,); - -// x is a fat pointer -fn foo(x: &Fat<[isize]>) { - let y = &x.0; - assert_eq!(x.0.len(), 3); - assert_eq!(y[0], 1); - assert_eq!(x.0[1], 2); -} - -fn foo2(x: &Fat<[T]>) { - let y = &x.0; - let bar = Bar; - assert_eq!(x.0.len(), 3); - assert_eq!(y[0].to_bar(), bar); - assert_eq!(x.0[1].to_bar(), bar); -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -struct Bar; - -trait ToBar { - fn to_bar(&self) -> Bar; -} - -impl ToBar for Bar { - fn to_bar(&self) -> Bar { - *self - } -} - -pub fn main() { - // With a vec of ints. - let f1 = ([1, 2, 3],); - foo(&f1); - let f2 = &f1; - foo(f2); - let f3: &Fat<[isize]> = f2; - foo(f3); - let f4: &Fat<[isize]> = &f1; - foo(f4); - let f5: &Fat<[isize]> = &([1, 2, 3],); - foo(f5); - - // With a vec of Bars. - let bar = Bar; - let f1 = ([bar, bar, bar],); - foo2(&f1); - let f2 = &f1; - foo2(f2); - let f3: &Fat<[Bar]> = f2; - foo2(f3); - let f4: &Fat<[Bar]> = &f1; - foo2(f4); - let f5: &Fat<[Bar]> = &([bar, bar, bar],); - foo2(f5); - - // Assignment. - let f5: &mut Fat<[isize]> = &mut ([1, 2, 3],); - f5.0[1] = 34; - assert_eq!(f5.0[0], 1); - assert_eq!(f5.0[1], 34); - assert_eq!(f5.0[2], 3); - - // Zero size vec. - let f5: &Fat<[isize]> = &([],); - assert!(f5.0.is_empty()); - let f5: &Fat<[Bar]> = &([],); - assert!(f5.0.is_empty()); -} diff --git a/tests/ui/dynamically-sized-types/dst-tuple-zst-offsets.rs b/tests/ui/dynamically-sized-types/dst-tuple-zst-offsets.rs deleted file mode 100644 index b4ee0fb4070d7..0000000000000 --- a/tests/ui/dynamically-sized-types/dst-tuple-zst-offsets.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ run-pass - -#![feature(unsized_tuple_coercion)] - -// Check that we do not change the offsets of ZST fields when unsizing - -fn scalar_layout() { - let sized: &(u8, [(); 13]) = &(123, [(); 13]); - let unsize: &(u8, [()]) = sized; - assert_eq!(sized.1.as_ptr(), unsize.1.as_ptr()); -} - -fn scalarpair_layout() { - let sized: &(u8, u16, [(); 13]) = &(123, 456, [(); 13]); - let unsize: &(u8, u16, [()]) = sized; - assert_eq!(sized.2.as_ptr(), unsize.2.as_ptr()); -} - -pub fn main() { - scalar_layout(); - scalarpair_layout(); -} diff --git a/tests/ui/dynamically-sized-types/dst-tuple.rs b/tests/ui/dynamically-sized-types/dst-tuple.rs deleted file mode 100644 index 52fc69f7809fd..0000000000000 --- a/tests/ui/dynamically-sized-types/dst-tuple.rs +++ /dev/null @@ -1,119 +0,0 @@ -//@ run-pass -#![allow(type_alias_bounds)] - -#![feature(unsized_tuple_coercion)] - -type Fat = (isize, &'static str, T); - -// x is a fat pointer -fn foo(x: &Fat<[isize]>) { - let y = &x.2; - assert_eq!(x.2.len(), 3); - assert_eq!(y[0], 1); - assert_eq!(x.2[1], 2); - assert_eq!(x.0, 5); - assert_eq!(x.1, "some str"); -} - -fn foo2(x: &Fat<[T]>) { - let y = &x.2; - let bar = Bar; - assert_eq!(x.2.len(), 3); - assert_eq!(y[0].to_bar(), bar); - assert_eq!(x.2[1].to_bar(), bar); - assert_eq!(x.0, 5); - assert_eq!(x.1, "some str"); -} - -fn foo3(x: &Fat>) { - let y = &(x.2).2; - assert_eq!(x.0, 5); - assert_eq!(x.1, "some str"); - assert_eq!((x.2).0, 8); - assert_eq!((x.2).1, "deep str"); - assert_eq!((x.2).2.len(), 3); - assert_eq!(y[0], 1); - assert_eq!((x.2).2[1], 2); -} - - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -struct Bar; - -trait ToBar { - fn to_bar(&self) -> Bar; -} - -impl ToBar for Bar { - fn to_bar(&self) -> Bar { - *self - } -} - -pub fn main() { - // With a vec of ints. - let f1 = (5, "some str", [1, 2, 3]); - foo(&f1); - let f2 = &f1; - foo(f2); - let f3: &Fat<[isize]> = f2; - foo(f3); - let f4: &Fat<[isize]> = &f1; - foo(f4); - let f5: &Fat<[isize]> = &(5, "some str", [1, 2, 3]); - foo(f5); - - // With a vec of Bars. - let bar = Bar; - let f1 = (5, "some str", [bar, bar, bar]); - foo2(&f1); - let f2 = &f1; - foo2(f2); - let f3: &Fat<[Bar]> = f2; - foo2(f3); - let f4: &Fat<[Bar]> = &f1; - foo2(f4); - let f5: &Fat<[Bar]> = &(5, "some str", [bar, bar, bar]); - foo2(f5); - - // Assignment. - let f5: &mut Fat<[isize]> = &mut (5, "some str", [1, 2, 3]); - f5.2[1] = 34; - assert_eq!(f5.2[0], 1); - assert_eq!(f5.2[1], 34); - assert_eq!(f5.2[2], 3); - - // Zero size vec. - let f5: &Fat<[isize]> = &(5, "some str", []); - assert!(f5.2.is_empty()); - let f5: &Fat<[Bar]> = &(5, "some str", []); - assert!(f5.2.is_empty()); - - // Deeply nested. - let f1 = (5, "some str", (8, "deep str", [1, 2, 3])); - foo3(&f1); - let f2 = &f1; - foo3(f2); - let f3: &Fat> = f2; - foo3(f3); - let f4: &Fat> = &f1; - foo3(f4); - let f5: &Fat> = &(5, "some str", (8, "deep str", [1, 2, 3])); - foo3(f5); - - // Box. - let f1 = Box::new([1, 2, 3]); - assert_eq!((*f1)[1], 2); - let f2: Box<[isize]> = f1; - assert_eq!((*f2)[1], 2); - - // Nested Box. - let f1 : Box> = Box::new((5, "some str", [1, 2, 3])); - foo(&*f1); - let f2 : Box> = f1; - foo(&*f2); - - let f3 : Box> = - Box::>::new((5, "some str", [1, 2, 3])); - foo(&*f3); -} diff --git a/tests/ui/editions/edition-keywords-2018-2015-parsing.rs b/tests/ui/editions/edition-keywords-2018-2015-parsing.rs index c3dfcfb19cb4b..f8d2755b9d795 100644 --- a/tests/ui/editions/edition-keywords-2018-2015-parsing.rs +++ b/tests/ui/editions/edition-keywords-2018-2015-parsing.rs @@ -28,3 +28,5 @@ pub fn check_async() { let _recovery_witness: () = 0; //~ ERROR mismatched types } + +//~? ERROR macro expansion ends with an incomplete expression diff --git a/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr b/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr index 905e1249d972d..152a6f3a41e64 100644 --- a/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr +++ b/tests/ui/editions/edition-keywords-2018-2015-parsing.stderr @@ -44,22 +44,22 @@ note: while trying to match `r#async` LL | (r#async) => (1) | ^^^^^^^ -error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` +error: macro expansion ends with an incomplete expression: expected one of `move`, `use`, `|`, or `||` --> $DIR/auxiliary/edition-kw-macro-2015.rs:27:23 | LL | ($i: ident) => ($i) - | ^ expected one of `move`, `|`, or `||` + | ^ expected one of `move`, `use`, `|`, or `||` | ::: $DIR/edition-keywords-2018-2015-parsing.rs:22:8 | LL | if passes_ident!(async) == 1 {} // FIXME: Edition hygiene bug, async here is 2018 and reserved | -------------------- in this macro invocation -error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` +error: macro expansion ends with an incomplete expression: expected one of `move`, `use`, `|`, or `||` --> $DIR/edition-keywords-2018-2015-parsing.rs:24:24 | LL | if passes_tt!(async) == 1 {} - | ^ expected one of `move`, `|`, or `||` + | ^ expected one of `move`, `use`, `|`, or `||` error[E0308]: mismatched types --> $DIR/edition-keywords-2018-2015-parsing.rs:29:33 diff --git a/tests/ui/editions/edition-keywords-2018-2018-parsing.rs b/tests/ui/editions/edition-keywords-2018-2018-parsing.rs index 1447c49ef7173..f4438472a0e6d 100644 --- a/tests/ui/editions/edition-keywords-2018-2018-parsing.rs +++ b/tests/ui/editions/edition-keywords-2018-2018-parsing.rs @@ -39,3 +39,5 @@ pub fn check_async() { let _recovery_witness: () = 0; //~ ERROR mismatched types } + +//~? ERROR macro expansion ends with an incomplete expression diff --git a/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr b/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr index af5cc515bb224..53f1b827f9c6a 100644 --- a/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr +++ b/tests/ui/editions/edition-keywords-2018-2018-parsing.stderr @@ -44,34 +44,34 @@ note: while trying to match `r#async` LL | (r#async) => (1) | ^^^^^^^ -error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` +error: macro expansion ends with an incomplete expression: expected one of `move`, `use`, `|`, or `||` --> $DIR/auxiliary/edition-kw-macro-2018.rs:27:23 | LL | ($i: ident) => ($i) - | ^ expected one of `move`, `|`, or `||` + | ^ expected one of `move`, `use`, `|`, or `||` | ::: $DIR/edition-keywords-2018-2018-parsing.rs:29:8 | LL | if passes_ident!(async) == 1 {} // FIXME: Edition hygiene bug, async here is 2018 and reserved | -------------------- in this macro invocation -error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` +error: macro expansion ends with an incomplete expression: expected one of `move`, `use`, `|`, or `||` --> $DIR/edition-keywords-2018-2018-parsing.rs:31:24 | LL | if passes_tt!(async) == 1 {} - | ^ expected one of `move`, `|`, or `||` + | ^ expected one of `move`, `use`, `|`, or `||` -error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` +error: macro expansion ends with an incomplete expression: expected one of `move`, `use`, `|`, or `||` --> $DIR/edition-keywords-2018-2018-parsing.rs:14:23 | LL | ($i: ident) => ($i) - | ^ expected one of `move`, `|`, or `||` + | ^ expected one of `move`, `use`, `|`, or `||` -error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||` +error: macro expansion ends with an incomplete expression: expected one of `move`, `use`, `|`, or `||` --> $DIR/edition-keywords-2018-2018-parsing.rs:35:30 | LL | if local_passes_tt!(async) == 1 {} - | ^ expected one of `move`, `|`, or `||` + | ^ expected one of `move`, `use`, `|`, or `||` error[E0308]: mismatched types --> $DIR/edition-keywords-2018-2018-parsing.rs:40:33 diff --git a/tests/ui/editions/never-type-fallback-breaking.e2021.fixed b/tests/ui/editions/never-type-fallback-breaking.e2021.fixed index 11ec273fc4bf9..8c11ab0791ddb 100644 --- a/tests/ui/editions/never-type-fallback-breaking.e2021.fixed +++ b/tests/ui/editions/never-type-fallback-breaking.e2021.fixed @@ -16,8 +16,8 @@ fn main() { } fn m() { - //[e2021]~^ this function depends on never type fallback being `()` - //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + //[e2021]~^ WARN this function depends on never type fallback being `()` + //[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! let x: () = match true { true => Default::default(), //[e2024]~^ error: the trait bound `!: Default` is not satisfied @@ -28,8 +28,8 @@ fn m() { } fn q() -> Option<()> { - //[e2021]~^ this function depends on never type fallback being `()` - //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + //[e2021]~^ WARN this function depends on never type fallback being `()` + //[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! fn deserialize() -> Option { Some(T::default()) } @@ -45,8 +45,8 @@ fn help<'a: 'a, T: Into<()>, U>(_: U) -> Result { Err(()) } fn meow() -> Result<(), ()> { - //[e2021]~^ this function depends on never type fallback being `()` - //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + //[e2021]~^ WARN this function depends on never type fallback being `()` + //[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! help::<(), _>(1)?; //[e2024]~^ error: the trait bound `(): From` is not satisfied Ok(()) @@ -57,8 +57,8 @@ pub fn takes_apit(_y: impl Fn() -> T) -> Result { } pub fn fallback_return() -> Result<(), ()> { - //[e2021]~^ this function depends on never type fallback being `()` - //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + //[e2021]~^ WARN this function depends on never type fallback being `()` + //[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! takes_apit::<()>(|| Default::default())?; //[e2024]~^ error: the trait bound `!: Default` is not satisfied Ok(()) @@ -71,8 +71,8 @@ fn mk() -> Result { fn takes_apit2(_x: impl Default) {} fn fully_apit() -> Result<(), ()> { - //[e2021]~^ this function depends on never type fallback being `()` - //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + //[e2021]~^ WARN this function depends on never type fallback being `()` + //[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! takes_apit2(mk::<()>()?); //[e2024]~^ error: the trait bound `!: Default` is not satisfied Ok(()) diff --git a/tests/ui/editions/never-type-fallback-breaking.rs b/tests/ui/editions/never-type-fallback-breaking.rs index daafc526eff41..80974f8301379 100644 --- a/tests/ui/editions/never-type-fallback-breaking.rs +++ b/tests/ui/editions/never-type-fallback-breaking.rs @@ -16,8 +16,8 @@ fn main() { } fn m() { - //[e2021]~^ this function depends on never type fallback being `()` - //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + //[e2021]~^ WARN this function depends on never type fallback being `()` + //[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! let x = match true { true => Default::default(), //[e2024]~^ error: the trait bound `!: Default` is not satisfied @@ -28,8 +28,8 @@ fn m() { } fn q() -> Option<()> { - //[e2021]~^ this function depends on never type fallback being `()` - //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + //[e2021]~^ WARN this function depends on never type fallback being `()` + //[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! fn deserialize() -> Option { Some(T::default()) } @@ -45,8 +45,8 @@ fn help<'a: 'a, T: Into<()>, U>(_: U) -> Result { Err(()) } fn meow() -> Result<(), ()> { - //[e2021]~^ this function depends on never type fallback being `()` - //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + //[e2021]~^ WARN this function depends on never type fallback being `()` + //[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! help(1)?; //[e2024]~^ error: the trait bound `(): From` is not satisfied Ok(()) @@ -57,8 +57,8 @@ pub fn takes_apit(_y: impl Fn() -> T) -> Result { } pub fn fallback_return() -> Result<(), ()> { - //[e2021]~^ this function depends on never type fallback being `()` - //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + //[e2021]~^ WARN this function depends on never type fallback being `()` + //[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! takes_apit(|| Default::default())?; //[e2024]~^ error: the trait bound `!: Default` is not satisfied Ok(()) @@ -71,8 +71,8 @@ fn mk() -> Result { fn takes_apit2(_x: impl Default) {} fn fully_apit() -> Result<(), ()> { - //[e2021]~^ this function depends on never type fallback being `()` - //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + //[e2021]~^ WARN this function depends on never type fallback being `()` + //[e2021]~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! takes_apit2(mk()?); //[e2024]~^ error: the trait bound `!: Default` is not satisfied Ok(()) diff --git a/tests/ui/elided-test.rs b/tests/ui/elided-test.rs index 025b75c1b5c87..2bedc25e17bb5 100644 --- a/tests/ui/elided-test.rs +++ b/tests/ui/elided-test.rs @@ -1,7 +1,5 @@ -//@ error-pattern: `main` function not found - // Since we're not compiling a test runner this function should be elided // and the build will fail because main doesn't exist #[test] fn main() { -} +} //~ ERROR `main` function not found in crate `elided_test` diff --git a/tests/ui/elided-test.stderr b/tests/ui/elided-test.stderr index e323b8ba7eadc..7aebe5d8264d1 100644 --- a/tests/ui/elided-test.stderr +++ b/tests/ui/elided-test.stderr @@ -1,5 +1,5 @@ error[E0601]: `main` function not found in crate `elided_test` - --> $DIR/elided-test.rs:7:2 + --> $DIR/elided-test.rs:5:2 | LL | } | ^ consider adding a `main` function to `$DIR/elided-test.rs` diff --git a/tests/ui/empty/empty-never-array.rs b/tests/ui/empty/empty-never-array.rs index fd93346101d84..c84d9bd85a3bc 100644 --- a/tests/ui/empty/empty-never-array.rs +++ b/tests/ui/empty/empty-never-array.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + #![feature(never_type)] enum Helper { @@ -9,7 +11,7 @@ enum Helper { fn transmute(t: T) -> U { let Helper::U(u) = Helper::T(t, []); //~^ ERROR refutable pattern in local binding - //~| `Helper::T(_, _)` not covered + //~| NOTE `Helper::T(_, _)` not covered u } diff --git a/tests/ui/empty/empty-never-array.stderr b/tests/ui/empty/empty-never-array.stderr index f9f39a6371e26..ee04ff162a4cb 100644 --- a/tests/ui/empty/empty-never-array.stderr +++ b/tests/ui/empty/empty-never-array.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in local binding - --> $DIR/empty-never-array.rs:10:9 + --> $DIR/empty-never-array.rs:12:9 | LL | let Helper::U(u) = Helper::T(t, []); | ^^^^^^^^^^^^ pattern `Helper::T(_, _)` not covered @@ -7,7 +7,7 @@ LL | let Helper::U(u) = Helper::T(t, []); = 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/ch19-02-refutability.html note: `Helper` defined here - --> $DIR/empty-never-array.rs:3:6 + --> $DIR/empty-never-array.rs:5:6 | LL | enum Helper { | ^^^^^^ diff --git a/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.rs b/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.rs index d0be240e07ba6..4e6be341ef0d9 100644 --- a/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.rs +++ b/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.rs @@ -4,7 +4,11 @@ pub mod foo { type MainFn = impl Fn(); fn bar() {} - pub const BAR: MainFn = bar; + #[define_opaque(MainFn)] + const fn def() -> MainFn { + bar + } + pub const BAR: MainFn = def(); } use foo::BAR as main; //~ ERROR `main` function not found in crate diff --git a/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.stderr b/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.stderr index 50e4cd7d39f79..375b3a53030a4 100644 --- a/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.stderr +++ b/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.stderr @@ -1,5 +1,5 @@ error[E0601]: `main` function not found in crate `imported_main_const_fn_item_type_forbidden` - --> $DIR/imported_main_const_fn_item_type_forbidden.rs:10:22 + --> $DIR/imported_main_const_fn_item_type_forbidden.rs:14:22 | LL | use foo::BAR as main; | ---------------- ^ consider adding a `main` function to `$DIR/imported_main_const_fn_item_type_forbidden.rs` diff --git a/tests/ui/entry-point/imported_main_from_extern_crate_wrong_type.rs b/tests/ui/entry-point/imported_main_from_extern_crate_wrong_type.rs index d8ae12d200f40..0b527baaafe76 100644 --- a/tests/ui/entry-point/imported_main_from_extern_crate_wrong_type.rs +++ b/tests/ui/entry-point/imported_main_from_extern_crate_wrong_type.rs @@ -2,3 +2,5 @@ extern crate bad_main_functions; pub use bad_main_functions::boilerplate as main; + +//~? ERROR `main` function has wrong type diff --git a/tests/ui/enum-discriminant/discriminant-ill-typed.rs b/tests/ui/enum-discriminant/discriminant-ill-typed.rs index 3cf0ea0e6b997..e3cbd01a1ddf7 100644 --- a/tests/ui/enum-discriminant/discriminant-ill-typed.rs +++ b/tests/ui/enum-discriminant/discriminant-ill-typed.rs @@ -14,7 +14,7 @@ fn f_i8() { Ok2, OhNo = 0_u8, //~^ ERROR mismatched types - //~| expected `i8`, found `u8` + //~| NOTE expected `i8`, found `u8` } let x = A::Ok; @@ -27,7 +27,7 @@ fn f_u8() { Ok2, OhNo = 0_i8, //~^ ERROR mismatched types - //~| expected `u8`, found `i8` + //~| NOTE expected `u8`, found `i8` } let x = A::Ok; @@ -40,7 +40,7 @@ fn f_i16() { Ok2, OhNo = 0_u16, //~^ ERROR mismatched types - //~| expected `i16`, found `u16` + //~| NOTE expected `i16`, found `u16` } let x = A::Ok; @@ -53,7 +53,7 @@ fn f_u16() { Ok2, OhNo = 0_i16, //~^ ERROR mismatched types - //~| expected `u16`, found `i16` + //~| NOTE expected `u16`, found `i16` } let x = A::Ok; @@ -66,7 +66,7 @@ fn f_i32() { Ok2, OhNo = 0_u32, //~^ ERROR mismatched types - //~| expected `i32`, found `u32` + //~| NOTE expected `i32`, found `u32` } let x = A::Ok; @@ -79,7 +79,7 @@ fn f_u32() { Ok2, OhNo = 0_i32, //~^ ERROR mismatched types - //~| expected `u32`, found `i32` + //~| NOTE expected `u32`, found `i32` } let x = A::Ok; @@ -92,7 +92,7 @@ fn f_i64() { Ok2, OhNo = 0_u64, //~^ ERROR mismatched types - //~| expected `i64`, found `u64` + //~| NOTE expected `i64`, found `u64` } let x = A::Ok; @@ -105,7 +105,7 @@ fn f_u64() { Ok2, OhNo = 0_i64, //~^ ERROR mismatched types - //~| expected `u64`, found `i64` + //~| NOTE expected `u64`, found `i64` } let x = A::Ok; diff --git a/tests/ui/enum-discriminant/discriminant-overflow.rs b/tests/ui/enum-discriminant/discriminant-overflow.rs index 774ced93c1723..aa3f4c13c4e3c 100644 --- a/tests/ui/enum-discriminant/discriminant-overflow.rs +++ b/tests/ui/enum-discriminant/discriminant-overflow.rs @@ -1,8 +1,9 @@ // Issue 23030: Detect overflowing discriminant - // See also run-pass/discrim-explicit-23030.rs where the suggested // workaround is tested. +//@ dont-require-annotations: NOTE + fn f_i8() { #[repr(i8)] enum A { @@ -42,7 +43,7 @@ fn f_u16() { Ok = u16::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 65535 + //~| NOTE overflowed on value after 65535 } let x = A::Ok; @@ -54,7 +55,7 @@ fn f_i32() { Ok = i32::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 2147483647 + //~| NOTE overflowed on value after 2147483647 } let x = A::Ok; @@ -66,7 +67,7 @@ fn f_u32() { Ok = u32::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 4294967295 + //~| NOTE overflowed on value after 4294967295 } let x = A::Ok; @@ -78,7 +79,7 @@ fn f_i64() { Ok = i64::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 9223372036854775807 + //~| NOTE overflowed on value after 9223372036854775807 } let x = A::Ok; @@ -90,7 +91,7 @@ fn f_u64() { Ok = u64::MAX - 1, Ok2, OhNo, //~ ERROR enum discriminant overflowed [E0370] - //~| overflowed on value after 18446744073709551615 + //~| NOTE overflowed on value after 18446744073709551615 } let x = A::Ok; diff --git a/tests/ui/enum-discriminant/discriminant-overflow.stderr b/tests/ui/enum-discriminant/discriminant-overflow.stderr index 2ecc1839f57de..2662ddb2b9338 100644 --- a/tests/ui/enum-discriminant/discriminant-overflow.stderr +++ b/tests/ui/enum-discriminant/discriminant-overflow.stderr @@ -1,5 +1,5 @@ error[E0370]: enum discriminant overflowed - --> $DIR/discriminant-overflow.rs:11:9 + --> $DIR/discriminant-overflow.rs:12:9 | LL | OhNo, | ^^^^ overflowed on value after 127 @@ -7,7 +7,7 @@ LL | OhNo, = note: explicitly set `OhNo = -128` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discriminant-overflow.rs:22:9 + --> $DIR/discriminant-overflow.rs:23:9 | LL | OhNo, | ^^^^ overflowed on value after 255 @@ -15,7 +15,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discriminant-overflow.rs:33:9 + --> $DIR/discriminant-overflow.rs:34:9 | LL | OhNo, | ^^^^ overflowed on value after 32767 @@ -23,7 +23,7 @@ LL | OhNo, = note: explicitly set `OhNo = -32768` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discriminant-overflow.rs:44:9 + --> $DIR/discriminant-overflow.rs:45:9 | LL | OhNo, | ^^^^ overflowed on value after 65535 @@ -31,7 +31,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discriminant-overflow.rs:56:9 + --> $DIR/discriminant-overflow.rs:57:9 | LL | OhNo, | ^^^^ overflowed on value after 2147483647 @@ -39,7 +39,7 @@ LL | OhNo, = note: explicitly set `OhNo = -2147483648` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discriminant-overflow.rs:68:9 + --> $DIR/discriminant-overflow.rs:69:9 | LL | OhNo, | ^^^^ overflowed on value after 4294967295 @@ -47,7 +47,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discriminant-overflow.rs:80:9 + --> $DIR/discriminant-overflow.rs:81:9 | LL | OhNo, | ^^^^ overflowed on value after 9223372036854775807 @@ -55,7 +55,7 @@ LL | OhNo, = note: explicitly set `OhNo = -9223372036854775808` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discriminant-overflow.rs:92:9 + --> $DIR/discriminant-overflow.rs:93:9 | LL | OhNo, | ^^^^ overflowed on value after 18446744073709551615 diff --git a/tests/ui/env-macro/env-escaped-var.stderr b/tests/ui/env-macro/env-escaped-var.stderr index 53a2ead67429d..65d53a7fa1fa8 100644 --- a/tests/ui/env-macro/env-escaped-var.stderr +++ b/tests/ui/env-macro/env-escaped-var.stderr @@ -5,7 +5,6 @@ LL | env!("\t"); | ^^^^^^^^^^ | = help: use `std::env::var("\t")` to read the variable at run time - = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/env-macro/env-not-defined-custom.stderr b/tests/ui/env-macro/env-not-defined-custom.stderr index 70c41bcc52eb3..638f261e48e1f 100644 --- a/tests/ui/env-macro/env-not-defined-custom.stderr +++ b/tests/ui/env-macro/env-not-defined-custom.stderr @@ -3,8 +3,6 @@ error: my error message | LL | fn main() { env!("__HOPEFULLY_NOT_DEFINED__", "my error message"); } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/env-macro/env-not-defined-default.stderr b/tests/ui/env-macro/env-not-defined-default.stderr index efd7fdb4e5371..77ba00e45c1c6 100644 --- a/tests/ui/env-macro/env-not-defined-default.stderr +++ b/tests/ui/env-macro/env-not-defined-default.stderr @@ -5,7 +5,6 @@ LL | env!("CARGO__HOPEFULLY_NOT_DEFINED__"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: Cargo sets build script variables at run time. Use `std::env::var("CARGO__HOPEFULLY_NOT_DEFINED__")` instead - = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/env-macro/error-recovery-issue-55897.stderr b/tests/ui/env-macro/error-recovery-issue-55897.stderr index f1cacf5420eb6..3d9ef56c50276 100644 --- a/tests/ui/env-macro/error-recovery-issue-55897.stderr +++ b/tests/ui/env-macro/error-recovery-issue-55897.stderr @@ -5,7 +5,6 @@ LL | include!(concat!(env!("NON_EXISTENT"), "/data.rs")); | ^^^^^^^^^^^^^^^^^^^^ | = help: use `std::env::var("NON_EXISTENT")` to read the variable at run time - = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: suffixes on string literals are invalid --> $DIR/error-recovery-issue-55897.rs:15:22 diff --git a/tests/ui/env-macro/name-whitespace-issue-110547.stderr b/tests/ui/env-macro/name-whitespace-issue-110547.stderr index 5f34904d4ae01..f29c5dbf8d12f 100644 --- a/tests/ui/env-macro/name-whitespace-issue-110547.stderr +++ b/tests/ui/env-macro/name-whitespace-issue-110547.stderr @@ -5,7 +5,6 @@ LL | env!{"\t"}; | ^^^^^^^^^^ | = help: use `std::env::var("\t")` to read the variable at run time - = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: environment variable `\t` not defined at compile time --> $DIR/name-whitespace-issue-110547.rs:5:5 @@ -14,7 +13,6 @@ LL | env!("\t"); | ^^^^^^^^^^ | = help: use `std::env::var("\t")` to read the variable at run time - = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: environment variable `\u{2069}` not defined at compile time --> $DIR/name-whitespace-issue-110547.rs:6:5 @@ -23,7 +21,6 @@ LL | env!("\u{2069}"); | ^^^^^^^^^^^^^^^^ | = help: use `std::env::var("\u{2069}")` to read the variable at run time - = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/ergonomic-clones/async/basic.rs b/tests/ui/ergonomic-clones/async/basic.rs new file mode 100644 index 0000000000000..ad2bfd97cd1b5 --- /dev/null +++ b/tests/ui/ergonomic-clones/async/basic.rs @@ -0,0 +1,17 @@ +//@ check-pass +//@ edition:2018 + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +use std::future::Future; + +fn ergonomic_clone_async_closures() -> impl Future { + let s = String::from("hi"); + + async use { + s + } +} + +fn main() {} diff --git a/tests/ui/ergonomic-clones/async/edition-2015.rs b/tests/ui/ergonomic-clones/async/edition-2015.rs new file mode 100644 index 0000000000000..d3b2071b9f91f --- /dev/null +++ b/tests/ui/ergonomic-clones/async/edition-2015.rs @@ -0,0 +1,7 @@ +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn main() { + async use {}; + //~^ ERROR `async use` blocks are only allowed in Rust 2018 or later +} diff --git a/tests/ui/ergonomic-clones/async/edition-2015.stderr b/tests/ui/ergonomic-clones/async/edition-2015.stderr new file mode 100644 index 0000000000000..b218e6b242e19 --- /dev/null +++ b/tests/ui/ergonomic-clones/async/edition-2015.stderr @@ -0,0 +1,8 @@ +error: `async use` blocks are only allowed in Rust 2018 or later + --> $DIR/edition-2015.rs:5:5 + | +LL | async use {}; + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/ergonomic-clones/async/local-type.rs b/tests/ui/ergonomic-clones/async/local-type.rs new file mode 100644 index 0000000000000..e891686b550ae --- /dev/null +++ b/tests/ui/ergonomic-clones/async/local-type.rs @@ -0,0 +1,10 @@ +// Check that using the parameter name in its type does not ICE. +//@ edition:2018 + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn main() { + let _ = async use |x: x| x; //~ ERROR expected type + let _ = async use |x: bool| -> x { x }; //~ ERROR expected type +} diff --git a/tests/ui/ergonomic-clones/async/local-type.stderr b/tests/ui/ergonomic-clones/async/local-type.stderr new file mode 100644 index 0000000000000..fd832fbc8d295 --- /dev/null +++ b/tests/ui/ergonomic-clones/async/local-type.stderr @@ -0,0 +1,15 @@ +error[E0573]: expected type, found local variable `x` + --> $DIR/local-type.rs:8:27 + | +LL | let _ = async use |x: x| x; + | ^ not a type + +error[E0573]: expected type, found local variable `x` + --> $DIR/local-type.rs:9:36 + | +LL | let _ = async use |x: bool| -> x { x }; + | ^ not a type + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0573`. diff --git a/tests/ui/ergonomic-clones/closure/basic.rs b/tests/ui/ergonomic-clones/closure/basic.rs new file mode 100644 index 0000000000000..01d3b08ffa27c --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/basic.rs @@ -0,0 +1,58 @@ +//@ check-pass + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +use std::clone::UseCloned; +use std::future::Future; + +fn ergonomic_clone_closure_no_captures() -> i32 { + let cl = use || { + 1 + }; + cl() +} + +fn ergonomic_clone_closure_move() -> String { + let s = String::from("hi"); + + let cl = use || { + s + }; + cl() +} + +#[derive(Clone)] +struct Foo; + +impl UseCloned for Foo {} + +fn ergonomic_clone_closure_use_cloned() -> Foo { + let f = Foo; + + let f1 = use || { + f + }; + + let f2 = use || { + f + }; + + f +} + +fn ergonomic_clone_closure_copy() -> i32 { + let i = 1; + + let i1 = use || { + i + }; + + let i2 = use || { + i + }; + + i +} + +fn main() {} diff --git a/tests/ui/ergonomic-clones/closure/const-closure.rs b/tests/ui/ergonomic-clones/closure/const-closure.rs new file mode 100644 index 0000000000000..6b4de824df9ef --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/const-closure.rs @@ -0,0 +1,11 @@ +//@ check-pass + +#![feature(const_closures)] +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +const fn foo() { + let cl = const use || {}; +} + +fn main() {} diff --git a/tests/ui/ergonomic-clones/closure/expect-region.rs b/tests/ui/ergonomic-clones/closure/expect-region.rs new file mode 100644 index 0000000000000..cbb13e56a18a8 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/expect-region.rs @@ -0,0 +1,25 @@ +#![feature(ergonomic_clones)] +#![allow(warnings)] + +fn closure_expecting_bound(_: F) +where + F: FnOnce(&u32), +{ +} + +fn expect_bound_supply_named<'x>() { + let mut f: Option<&u32> = None; + + // Here we give a type annotation that `x` should be free. We get + // an error because of that. + closure_expecting_bound(use |x: &'x u32| { + //~^ ERROR lifetime may not live long enough + //~| ERROR lifetime may not live long enough + + // Borrowck doesn't get a chance to run, but if it did it should error + // here. + f = Some(x); + }); +} + +fn main() {} diff --git a/tests/ui/ergonomic-clones/closure/expect-region.stderr b/tests/ui/ergonomic-clones/closure/expect-region.stderr new file mode 100644 index 0000000000000..4df1df1378b8d --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/expect-region.stderr @@ -0,0 +1,22 @@ +error: lifetime may not live long enough + --> $DIR/expect-region.rs:15:34 + | +LL | fn expect_bound_supply_named<'x>() { + | -- lifetime `'x` defined here +... +LL | closure_expecting_bound(use |x: &'x u32| { + | ^ - let's call the lifetime of this reference `'1` + | | + | requires that `'1` must outlive `'x` + +error: lifetime may not live long enough + --> $DIR/expect-region.rs:15:34 + | +LL | fn expect_bound_supply_named<'x>() { + | -- lifetime `'x` defined here +... +LL | closure_expecting_bound(use |x: &'x u32| { + | ^ requires that `'x` must outlive `'static` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/ergonomic-clones/closure/fn-once.rs b/tests/ui/ergonomic-clones/closure/fn-once.rs new file mode 100644 index 0000000000000..24060f3ed3b8b --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/fn-once.rs @@ -0,0 +1,14 @@ +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn get_closure() -> Box Vec> { + let vec = vec![1u8, 2u8]; + + let closure = use || { //~ ERROR expected a closure + vec + }; + + Box::new(closure) +} + +fn main() {} diff --git a/tests/ui/ergonomic-clones/closure/fn-once.stderr b/tests/ui/ergonomic-clones/closure/fn-once.stderr new file mode 100644 index 0000000000000..40f1200695cc3 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/fn-once.stderr @@ -0,0 +1,16 @@ +error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` + --> $DIR/fn-once.rs:7:19 + | +LL | let closure = use || { + | ^^^^^^ this closure implements `FnOnce`, not `Fn` +LL | vec + | --- closure is `FnOnce` because it moves the variable `vec` out of its environment +... +LL | Box::new(closure) + | ----------------- the requirement to implement `Fn` derives from here + | + = note: required for the cast from `Box<{closure@$DIR/fn-once.rs:7:19: 7:25}>` to `Box<(dyn Fn() -> Vec + 'static)>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0525`. diff --git a/tests/ui/ergonomic-clones/closure/immutable-outer-variable.fixed b/tests/ui/ergonomic-clones/closure/immutable-outer-variable.fixed new file mode 100644 index 0000000000000..1e57063245272 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/immutable-outer-variable.fixed @@ -0,0 +1,16 @@ +//@ run-rustfix + +// Point at the captured immutable outer variable + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn foo(mut f: Box) { + f(); +} + +fn main() { + let mut y = true; + foo(Box::new(use || y = !y) as Box<_>); + //~^ ERROR cannot assign to `y`, as it is not declared as mutable +} diff --git a/tests/ui/ergonomic-clones/closure/immutable-outer-variable.rs b/tests/ui/ergonomic-clones/closure/immutable-outer-variable.rs new file mode 100644 index 0000000000000..59aa61f581d92 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/immutable-outer-variable.rs @@ -0,0 +1,16 @@ +//@ run-rustfix + +// Point at the captured immutable outer variable + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn foo(mut f: Box) { + f(); +} + +fn main() { + let y = true; + foo(Box::new(use || y = !y) as Box<_>); + //~^ ERROR cannot assign to `y`, as it is not declared as mutable +} diff --git a/tests/ui/ergonomic-clones/closure/immutable-outer-variable.stderr b/tests/ui/ergonomic-clones/closure/immutable-outer-variable.stderr new file mode 100644 index 0000000000000..f7c742d33fda9 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/immutable-outer-variable.stderr @@ -0,0 +1,14 @@ +error[E0594]: cannot assign to `y`, as it is not declared as mutable + --> $DIR/immutable-outer-variable.rs:14:25 + | +LL | foo(Box::new(use || y = !y) as Box<_>); + | ^^^^^^ cannot assign + | +help: consider changing this to be mutable + | +LL | let mut y = true; + | +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0594`. diff --git a/tests/ui/ergonomic-clones/closure/local-type.rs b/tests/ui/ergonomic-clones/closure/local-type.rs new file mode 100644 index 0000000000000..b2f99efa1e666 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/local-type.rs @@ -0,0 +1,9 @@ +// Check that using the parameter name in its type does not ICE. + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn main() { + let _ = use |x: x| x; //~ ERROR expected type + let _ = use |x: bool| -> x { x }; //~ ERROR expected type +} diff --git a/tests/ui/ergonomic-clones/closure/local-type.stderr b/tests/ui/ergonomic-clones/closure/local-type.stderr new file mode 100644 index 0000000000000..5a42ae63afaf1 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/local-type.stderr @@ -0,0 +1,15 @@ +error[E0573]: expected type, found local variable `x` + --> $DIR/local-type.rs:7:21 + | +LL | let _ = use |x: x| x; + | ^ not a type + +error[E0573]: expected type, found local variable `x` + --> $DIR/local-type.rs:8:30 + | +LL | let _ = use |x: bool| -> x { x }; + | ^ not a type + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0573`. diff --git a/tests/ui/ergonomic-clones/closure/multiple-use-variants.rs b/tests/ui/ergonomic-clones/closure/multiple-use-variants.rs new file mode 100644 index 0000000000000..e2e9820a740f2 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/multiple-use-variants.rs @@ -0,0 +1,35 @@ +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +use std::clone::UseCloned; + +fn takes_val(_: T) {} +fn takes_ref<'a, T>(_: &'a T) {} + +#[derive(Clone)] +struct Inner<'a, T>(&'a T); + +impl<'a, T> UseCloned for Inner<'a, T> where T: Clone {} + +fn main() { + let v = String::new(); + let inner = Inner(&v); + + let _ = use || { + takes_ref(inner.0); + takes_val(inner.0) + }; + let _ = use || { + takes_ref(inner.0); + takes_val(inner.0); + takes_val(inner.0); + takes_val(inner) + }; + let _ = use || { + takes_ref(inner.0); + takes_val(inner.0); + takes_val(inner); + takes_val(inner) + //~^ ERROR: use of moved value: `inner` [E0382] + }; +} diff --git a/tests/ui/ergonomic-clones/closure/multiple-use-variants.stderr b/tests/ui/ergonomic-clones/closure/multiple-use-variants.stderr new file mode 100644 index 0000000000000..7b25ca9bba83a --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/multiple-use-variants.stderr @@ -0,0 +1,13 @@ +error[E0382]: use of moved value: `inner` + --> $DIR/multiple-use-variants.rs:32:19 + | +LL | takes_val(inner); + | ----- value moved here +LL | takes_val(inner) + | ^^^^^ value used here after move + | + = note: move occurs because `inner` has type `Inner<'_, String>`, which does not implement the `Copy` trait + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/ergonomic-clones/closure/mutation.rs b/tests/ui/ergonomic-clones/closure/mutation.rs new file mode 100644 index 0000000000000..ef05fffd47993 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/mutation.rs @@ -0,0 +1,12 @@ +//@ check-pass + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn main() { + let mut my_var = false; + let mut callback = use || { + my_var = true; + }; + callback(); +} diff --git a/tests/ui/ergonomic-clones/closure/mutation2.rs b/tests/ui/ergonomic-clones/closure/mutation2.rs new file mode 100644 index 0000000000000..1cb5b8a7ec3f6 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/mutation2.rs @@ -0,0 +1,11 @@ +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn main() { + let mut my_var = false; + let callback = use || { + my_var = true; + }; + callback(); + //~^ ERROR cannot borrow `callback` as mutable, as it is not declared as mutable [E0596] +} diff --git a/tests/ui/ergonomic-clones/closure/mutation2.stderr b/tests/ui/ergonomic-clones/closure/mutation2.stderr new file mode 100644 index 0000000000000..3ff33cf701748 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/mutation2.stderr @@ -0,0 +1,17 @@ +error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable + --> $DIR/mutation2.rs:9:5 + | +LL | my_var = true; + | ------ calling `callback` requires mutable binding due to possible mutation of `my_var` +LL | }; +LL | callback(); + | ^^^^^^^^ cannot borrow as mutable + | +help: consider changing this to be mutable + | +LL | let mut callback = use || { + | +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/ergonomic-clones/closure/nested.rs b/tests/ui/ergonomic-clones/closure/nested.rs new file mode 100644 index 0000000000000..fc364fb594b2c --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/nested.rs @@ -0,0 +1,21 @@ +//@ run-pass + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +use std::clone::UseCloned; + +#[derive(Clone)] +struct Foo; + +impl UseCloned for Foo {} + +fn work(_: Box) {} +fn foo(_: F) {} + +pub fn main() { + let a = Box::new(Foo); + foo(use || { foo(use || { work(a) }) }); + let x = use || { use || { Foo } }; + let _y = x(); +} diff --git a/tests/ui/ergonomic-clones/closure/once-move-out-on-heap.rs b/tests/ui/ergonomic-clones/closure/once-move-out-on-heap.rs new file mode 100644 index 0000000000000..a8267ac5359f1 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/once-move-out-on-heap.rs @@ -0,0 +1,19 @@ +//@ run-pass +// Testing guarantees provided by once functions. + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +use std::sync::Arc; + +fn foo(blk: F) { + blk(); +} + +pub fn main() { + let x = Arc::new(true); + foo(use || { + assert!(*x); + drop(x); + }); +} diff --git a/tests/ui/ergonomic-clones/closure/parse.rs b/tests/ui/ergonomic-clones/closure/parse.rs new file mode 100644 index 0000000000000..0b3bfae0608ab --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/parse.rs @@ -0,0 +1,22 @@ +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn parse1() { + || use { + //~^ ERROR expected one of `async`, `|`, or `||`, found `{` + }; +} + +fn parse2() { + move use || { + //~^ ERROR expected one of `async`, `|`, or `||`, found keyword `use` + }; +} + +fn parse3() { + use move || { + //~^ ERROR expected one of `async`, `|`, or `||`, found keyword `move` + }; +} + +fn main() {} diff --git a/tests/ui/ergonomic-clones/closure/parse.stderr b/tests/ui/ergonomic-clones/closure/parse.stderr new file mode 100644 index 0000000000000..c37cb71394be8 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/parse.stderr @@ -0,0 +1,28 @@ +error: expected one of `async`, `|`, or `||`, found `{` + --> $DIR/parse.rs:5:12 + | +LL | || use { + | -- ^ expected one of `async`, `|`, or `||` + | | + | while parsing the body of this closure + | +help: you might have meant to open the body of the closure, instead of enclosing the closure in a block + | +LL ~ fn parse1() +LL ~ || { use { + | + +error: expected one of `async`, `|`, or `||`, found keyword `use` + --> $DIR/parse.rs:11:10 + | +LL | move use || { + | ^^^ expected one of `async`, `|`, or `||` + +error: expected one of `async`, `|`, or `||`, found keyword `move` + --> $DIR/parse.rs:17:9 + | +LL | use move || { + | ^^^^ expected one of `async`, `|`, or `||` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/ergonomic-clones/closure/print-verbose.rs b/tests/ui/ergonomic-clones/closure/print-verbose.rs new file mode 100644 index 0000000000000..e80d0d4b649a5 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/print-verbose.rs @@ -0,0 +1,28 @@ +//@ compile-flags: -Zverbose-internals + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn to_fn_once(f: F) -> F { + f +} + +fn f(y: T) { + struct Foo { + x: U, + }; + + let foo = Foo { x: "x" }; + + let c = to_fn_once(use || { + println!("{} {}", foo.x, y); + }); + + c(); + c(); + //~^ ERROR use of moved value +} + +fn main() { + f("S"); +} diff --git a/tests/ui/ergonomic-clones/closure/print-verbose.stderr b/tests/ui/ergonomic-clones/closure/print-verbose.stderr new file mode 100644 index 0000000000000..283405c79d61e --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/print-verbose.stderr @@ -0,0 +1,20 @@ +error[E0382]: use of moved value: `c` + --> $DIR/print-verbose.rs:22:5 + | +LL | let c = to_fn_once(use || { + | - move occurs because `c` has type `{f::{closure#0} closure_kind_ty=i32 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=(Foo<&'?9 str>, T)}`, which does not implement the `Copy` trait +... +LL | c(); + | --- `c` moved due to this call +LL | c(); + | ^ value used here after move + | +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/print-verbose.rs:21:5 + | +LL | c(); + | ^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/ergonomic-clones/closure/print.rs b/tests/ui/ergonomic-clones/closure/print.rs new file mode 100644 index 0000000000000..c24a4cc5094be --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/print.rs @@ -0,0 +1,26 @@ +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn to_fn_once(f: F) -> F { + f +} + +fn f(y: T) { + struct Foo { + x: U, + }; + + let foo = Foo { x: "x" }; + + let c = to_fn_once(use || { + println!("{} {}", foo.x, y); + }); + + c(); + c(); + //~^ ERROR use of moved value +} + +fn main() { + f("S"); +} diff --git a/tests/ui/ergonomic-clones/closure/print.stderr b/tests/ui/ergonomic-clones/closure/print.stderr new file mode 100644 index 0000000000000..5f48059ebff29 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/print.stderr @@ -0,0 +1,20 @@ +error[E0382]: use of moved value: `c` + --> $DIR/print.rs:20:5 + | +LL | let c = to_fn_once(use || { + | - move occurs because `c` has type `{closure@$DIR/print.rs:15:24: 15:30}`, which does not implement the `Copy` trait +... +LL | c(); + | --- `c` moved due to this call +LL | c(); + | ^ value used here after move + | +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/print.rs:19:5 + | +LL | c(); + | ^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/ergonomic-clones/closure/rfc2229-migration.fixed b/tests/ui/ergonomic-clones/closure/rfc2229-migration.fixed new file mode 100644 index 0000000000000..fa83b53526a85 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/rfc2229-migration.fixed @@ -0,0 +1,26 @@ +//@ run-rustfix +//@ edition:2018 +//@ check-pass +#![feature(ergonomic_clones)] +#![warn(rust_2021_compatibility)] +#![allow(incomplete_features)] + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +fn main() { + let a = (Foo(0), Foo(1)); + let f = use || { + let _ = &a; + //~^ HELP: add a dummy + //~| WARNING: drop order + let x = a.0; + println!("{:?}", x); + }; + f(); +} diff --git a/tests/ui/ergonomic-clones/closure/rfc2229-migration.rs b/tests/ui/ergonomic-clones/closure/rfc2229-migration.rs new file mode 100644 index 0000000000000..4070e5c35a441 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/rfc2229-migration.rs @@ -0,0 +1,25 @@ +//@ run-rustfix +//@ edition:2018 +//@ check-pass +#![feature(ergonomic_clones)] +#![warn(rust_2021_compatibility)] +#![allow(incomplete_features)] + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +fn main() { + let a = (Foo(0), Foo(1)); + let f = use || { + //~^ HELP: add a dummy + //~| WARNING: drop order + let x = a.0; + println!("{:?}", x); + }; + f(); +} diff --git a/tests/ui/ergonomic-clones/closure/rfc2229-migration.stderr b/tests/ui/ergonomic-clones/closure/rfc2229-migration.stderr new file mode 100644 index 0000000000000..b980be6cb86bf --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/rfc2229-migration.stderr @@ -0,0 +1,27 @@ +warning: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/rfc2229-migration.rs:18:13 + | +LL | let f = use || { + | ^^^^^^ +... +LL | let x = a.0; + | --- in Rust 2018, this closure captures all of `a`, but in Rust 2021, it will only capture `a.0` +... +LL | } + | - in Rust 2018, `a` is dropped here, but in Rust 2021, only `a.0` will be dropped here as part of the closure + | + = note: for more information, see +note: the lint level is defined here + --> $DIR/rfc2229-migration.rs:5:9 + | +LL | #![warn(rust_2021_compatibility)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(rust_2021_incompatible_closure_captures)]` implied by `#[warn(rust_2021_compatibility)]` +help: add a dummy let to cause `a` to be fully captured + | +LL ~ let f = use || { +LL + let _ = &a; + | + +warning: 1 warning emitted + diff --git a/tests/ui/ergonomic-clones/closure/spawn-thread.edition2018.stderr b/tests/ui/ergonomic-clones/closure/spawn-thread.edition2018.stderr new file mode 100644 index 0000000000000..ac8e1c5fa858d --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/spawn-thread.edition2018.stderr @@ -0,0 +1,28 @@ +error[E0382]: use of moved value: `x` + --> $DIR/spawn-thread.rs:15:42 + | +LL | let x = (Arc::new("foo".to_owned()), Arc::new(vec![1, 2, 3]), Arc::new(1)); + | - move occurs because `x` has type `(Arc, Arc>, Arc)`, which does not implement the `Copy` trait +LL | for _ in 0..10 { + | -------------- inside of this loop +LL | let handler = std::thread::spawn(use || { + | __________________________________________-^^^^^ +LL | | +LL | | drop((x.0, x.1, x.2)); + | | --- use occurs due to use in closure +LL | | }); + | |_________- value moved here, in previous iteration of loop + | +help: consider moving the expression out of the loop so it is only moved once + | +LL ~ let mut value = std::thread::spawn(use || { +LL + +LL + drop((x.0, x.1, x.2)); +LL + }); +LL ~ for _ in 0..10 { +LL ~ let handler = value; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/ergonomic-clones/closure/spawn-thread.rs b/tests/ui/ergonomic-clones/closure/spawn-thread.rs new file mode 100644 index 0000000000000..289d446c6e6f7 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/spawn-thread.rs @@ -0,0 +1,50 @@ +//@ revisions: edition2018 edition2024 +//@ [edition2018] edition: 2018 +//@ [edition2024] edition: 2024 +//@ [edition2024] check-pass + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +use std::sync::Arc; + +fn foo() { + // The type is a tuple and doesn't implement UseCloned + let x = (Arc::new("foo".to_owned()), Arc::new(vec![1, 2, 3]), Arc::new(1)); + for _ in 0..10 { + let handler = std::thread::spawn(use || { + //[edition2018]~^ ERROR use of moved value: `x` [E0382] + drop((x.0, x.1, x.2)); + }); + handler.join().unwrap(); + } +} + +fn bar() { + let x = Arc::new("foo".to_owned()); + let y = Arc::new(vec![1, 2, 3]); + let z = Arc::new(1); + + for _ in 0..10 { + let handler = std::thread::spawn(use || { + drop((x, y, z)); + }); + handler.join().unwrap(); + } +} + +fn baz() { + use std::sync::Arc; + use std::thread; + + let five = Arc::new(5); + + for _ in 0..10 { + let handler = thread::spawn(use || { + println!("{five:?}"); + }); + handler.join().unwrap(); + } +} + +fn main() {} diff --git a/tests/ui/ergonomic-clones/closure/with-binders.rs b/tests/ui/ergonomic-clones/closure/with-binders.rs new file mode 100644 index 0000000000000..4260c252d9dd9 --- /dev/null +++ b/tests/ui/ergonomic-clones/closure/with-binders.rs @@ -0,0 +1,10 @@ +//@ edition:2021 +//@ check-pass + +#![feature(closure_lifetime_binder)] +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn main() { + for<'a> use || -> () {}; +} diff --git a/tests/ui/ergonomic-clones/dotuse/basic.rs b/tests/ui/ergonomic-clones/dotuse/basic.rs new file mode 100644 index 0000000000000..8f962f079df75 --- /dev/null +++ b/tests/ui/ergonomic-clones/dotuse/basic.rs @@ -0,0 +1,22 @@ +//@ check-pass + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +use std::clone::UseCloned; + +fn basic_test(x: i32) -> i32 { + x.use.use.abs() +} + +#[derive(Clone)] +struct Foo; + +impl UseCloned for Foo {} + +fn do_not_move_test(x: Foo) -> Foo { + let s = x.use; + x +} + +fn main() {} diff --git a/tests/ui/ergonomic-clones/dotuse/block.rs b/tests/ui/ergonomic-clones/dotuse/block.rs new file mode 100644 index 0000000000000..2e423c67d02b7 --- /dev/null +++ b/tests/ui/ergonomic-clones/dotuse/block.rs @@ -0,0 +1,11 @@ +//@ check-pass + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn use_block_test(x: i32) -> i32 { + let x = { let x = x + 1; x }.use; + x +} + +fn main() {} diff --git a/tests/ui/ergonomic-clones/dotuse/parse.rs b/tests/ui/ergonomic-clones/dotuse/parse.rs new file mode 100644 index 0000000000000..37ef5c37029cf --- /dev/null +++ b/tests/ui/ergonomic-clones/dotuse/parse.rs @@ -0,0 +1,39 @@ +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +fn parse1() { + 1.use!; + //~^ ERROR: expected one of `.`, `;`, `?`, `}`, or an operator, found `!` + } + +fn parse2() { + 1.use!(2); + //~^ ERROR: expected one of `.`, `;`, `?`, `}`, or an operator, found `!` + } + +fn parse3() { + 1.use 2; + //~^ ERROR: expected one of `.`, `;`, `?`, `}`, or an operator, found `2` + } + +fn parse4() { + 1.use? 2; + //~^ ERROR: expected one of `.`, `;`, `?`, `}`, or an operator, found `2` + } + +fn parse5() { + 1.use(); + //~^ ERROR: incorrect use of `use` +} + +fn parse6() { + 1.use(2); + //~^ ERROR: expected function, found `{integer}` [E0618] +} + +fn parse7() { + 1.use { 2 }; + //~^ ERROR: expected one of `.`, `;`, `?`, `}`, or an operator, found `{` +} + +fn main() {} diff --git a/tests/ui/ergonomic-clones/dotuse/parse.stderr b/tests/ui/ergonomic-clones/dotuse/parse.stderr new file mode 100644 index 0000000000000..4b7a92534eda1 --- /dev/null +++ b/tests/ui/ergonomic-clones/dotuse/parse.stderr @@ -0,0 +1,53 @@ +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `!` + --> $DIR/parse.rs:5:10 + | +LL | 1.use!; + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `!` + --> $DIR/parse.rs:10:10 + | +LL | 1.use!(2); + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `2` + --> $DIR/parse.rs:15:11 + | +LL | 1.use 2; + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `2` + --> $DIR/parse.rs:20:12 + | +LL | 1.use? 2; + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: incorrect use of `use` + --> $DIR/parse.rs:25:10 + | +LL | 1.use(); + | ^^ + | +help: `use` is not a method call, try removing the parentheses + | +LL - 1.use(); +LL + 1.use; + | + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `{` + --> $DIR/parse.rs:35:11 + | +LL | 1.use { 2 }; + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error[E0618]: expected function, found `{integer}` + --> $DIR/parse.rs:30:5 + | +LL | 1.use(2); + | ^^^^^--- + | | + | call expression requires function + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0618`. diff --git a/tests/ui/error-codes/E0063.rs b/tests/ui/error-codes/E0063.rs index 48c9c13f018f7..2ef09b0a426a6 100644 --- a/tests/ui/error-codes/E0063.rs +++ b/tests/ui/error-codes/E0063.rs @@ -32,7 +32,7 @@ fn main() { let x = PluralFoo {x: 1}; //~^ ERROR missing fields `y` and `z` in initializer of `PluralFoo` let y = TruncatedFoo{x:1}; - //~^ missing fields `a`, `b`, `y` and 1 other field in initializer of `TruncatedFoo` + //~^ ERROR missing fields `a`, `b`, `y` and 1 other field in initializer of `TruncatedFoo` let z = TruncatedPluralFoo{x:1}; //~^ ERROR missing fields `a`, `b`, `c` and 2 other fields in initializer of `TruncatedPluralFoo` } diff --git a/tests/ui/error-codes/E0080.rs b/tests/ui/error-codes/E0080.rs index ea3264b61b356..55f45055f013c 100644 --- a/tests/ui/error-codes/E0080.rs +++ b/tests/ui/error-codes/E0080.rs @@ -1,7 +1,8 @@ enum Enum { X = (1 << 500), //~ ERROR E0080 - //~| attempt to shift left by `500_i32`, which would overflow + //~| NOTE attempt to shift left by `500_i32`, which would overflow Y = (1 / 0) //~ ERROR E0080 + //~| NOTE attempt to divide `1_isize` by zero } fn main() { diff --git a/tests/ui/error-codes/E0092.rs b/tests/ui/error-codes/E0092.rs index ddaace98bd4af..19a7c65a48ed2 100644 --- a/tests/ui/error-codes/E0092.rs +++ b/tests/ui/error-codes/E0092.rs @@ -1,7 +1,6 @@ #![feature(intrinsics)] -extern "rust-intrinsic" { - fn atomic_foo(); //~ ERROR E0092 -} -fn main() { -} +#[rustc_intrinsic] +unsafe fn atomic_foo(); //~ ERROR E0092 + +fn main() {} diff --git a/tests/ui/error-codes/E0092.stderr b/tests/ui/error-codes/E0092.stderr index 4ff2e6f077d24..003c989fd5961 100644 --- a/tests/ui/error-codes/E0092.stderr +++ b/tests/ui/error-codes/E0092.stderr @@ -1,8 +1,8 @@ error[E0092]: unrecognized atomic operation function: `foo` - --> $DIR/E0092.rs:3:5 + --> $DIR/E0092.rs:4:11 | -LL | fn atomic_foo(); - | ^^^^^^^^^^^^^^^^ unrecognized atomic operation +LL | unsafe fn atomic_foo(); + | ^^^^^^^^^^ unrecognized atomic operation error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0093.rs b/tests/ui/error-codes/E0093.rs index a2f0b1ae44379..24df7a9a32b97 100644 --- a/tests/ui/error-codes/E0093.rs +++ b/tests/ui/error-codes/E0093.rs @@ -1,8 +1,7 @@ #![feature(intrinsics)] -extern "rust-intrinsic" { - fn foo(); - //~^ ERROR E0093 -} -fn main() { -} +#[rustc_intrinsic] +unsafe fn foo(); +//~^ ERROR E0093 + +fn main() {} diff --git a/tests/ui/error-codes/E0093.stderr b/tests/ui/error-codes/E0093.stderr index 51c367b343ab1..d81bf53976aee 100644 --- a/tests/ui/error-codes/E0093.stderr +++ b/tests/ui/error-codes/E0093.stderr @@ -1,8 +1,8 @@ error[E0093]: unrecognized intrinsic function: `foo` - --> $DIR/E0093.rs:3:5 + --> $DIR/E0093.rs:4:11 | -LL | fn foo(); - | ^^^^^^^^^ unrecognized intrinsic +LL | unsafe fn foo(); + | ^^^ unrecognized intrinsic | = help: if you're adding an intrinsic, be sure to update `check_intrinsic_type` diff --git a/tests/ui/error-codes/E0106.rs b/tests/ui/error-codes/E0106.rs index cc3438727a817..c8030495628e2 100644 --- a/tests/ui/error-codes/E0106.rs +++ b/tests/ui/error-codes/E0106.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + struct Foo { x: &bool, //~^ ERROR E0106 @@ -16,10 +18,10 @@ struct Buzz<'a, 'b>(&'a str, &'b str); struct Quux { baz: Baz, //~^ ERROR E0106 - //~| expected named lifetime parameter + //~| NOTE expected named lifetime parameter buzz: Buzz, //~^ ERROR E0106 - //~| expected 2 lifetime parameters + //~| NOTE expected 2 lifetime parameters } fn main() { diff --git a/tests/ui/error-codes/E0106.stderr b/tests/ui/error-codes/E0106.stderr index d11a24f776855..97ae1df987321 100644 --- a/tests/ui/error-codes/E0106.stderr +++ b/tests/ui/error-codes/E0106.stderr @@ -1,5 +1,5 @@ error[E0106]: missing lifetime specifier - --> $DIR/E0106.rs:2:8 + --> $DIR/E0106.rs:4:8 | LL | x: &bool, | ^ expected named lifetime parameter @@ -11,7 +11,7 @@ LL ~ x: &'a bool, | error[E0106]: missing lifetime specifier - --> $DIR/E0106.rs:7:7 + --> $DIR/E0106.rs:9:7 | LL | B(&bool), | ^ expected named lifetime parameter @@ -24,7 +24,7 @@ LL ~ B(&'a bool), | error[E0106]: missing lifetime specifier - --> $DIR/E0106.rs:10:14 + --> $DIR/E0106.rs:12:14 | LL | type MyStr = &str; | ^ expected named lifetime parameter @@ -35,7 +35,7 @@ LL | type MyStr<'a> = &'a str; | ++++ ++ error[E0106]: missing lifetime specifier - --> $DIR/E0106.rs:17:10 + --> $DIR/E0106.rs:19:10 | LL | baz: Baz, | ^^^ expected named lifetime parameter @@ -47,7 +47,7 @@ LL ~ baz: Baz<'a>, | error[E0106]: missing lifetime specifiers - --> $DIR/E0106.rs:20:11 + --> $DIR/E0106.rs:22:11 | LL | buzz: Buzz, | ^^^^ expected 2 lifetime parameters diff --git a/tests/ui/error-codes/E0128.stderr b/tests/ui/error-codes/E0128.stderr index c1ccb4c9e7440..22f1e200e29c2 100644 --- a/tests/ui/error-codes/E0128.stderr +++ b/tests/ui/error-codes/E0128.stderr @@ -1,8 +1,8 @@ -error[E0128]: generic parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameter defaults cannot reference parameters before they are declared --> $DIR/E0128.rs:1:14 | LL | struct Foo { - | ^ defaulted generic parameters cannot be forward declared + | ^ cannot reference `U` before it is declared error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0152-duplicate-lang-items.rs b/tests/ui/error-codes/E0152-duplicate-lang-items.rs new file mode 100644 index 0000000000000..f707b72f9b2b5 --- /dev/null +++ b/tests/ui/error-codes/E0152-duplicate-lang-items.rs @@ -0,0 +1,22 @@ +//! Validates the correct printing of E0152 in the case of duplicate "lang_item" function +//! definitions. +//! +//! Issue: + +//@ normalize-stderr: "loaded from .*libstd-.*.rlib" -> "loaded from SYSROOT/libstd-*.rlib" +//@ dont-require-annotations: NOTE + +#![feature(lang_items)] + +extern crate core; + +use core::panic::PanicInfo; + +#[lang = "panic_impl"] +fn panic_impl(info: &PanicInfo) -> ! { + //~^ ERROR: found duplicate lang item `panic_impl` + //~| NOTE first defined in crate `std` + loop {} +} + +fn main() {} diff --git a/tests/ui/error-codes/E0152-duplicate-lang-items.stderr b/tests/ui/error-codes/E0152-duplicate-lang-items.stderr new file mode 100644 index 0000000000000..2fe0d18fc2f47 --- /dev/null +++ b/tests/ui/error-codes/E0152-duplicate-lang-items.stderr @@ -0,0 +1,17 @@ +error[E0152]: found duplicate lang item `panic_impl` + --> $DIR/E0152-duplicate-lang-items.rs:16:1 + | +LL | / fn panic_impl(info: &PanicInfo) -> ! { +LL | | +LL | | +LL | | loop {} +LL | | } + | |_^ + | + = note: the lang item is first defined in crate `std` (which `E0152_duplicate_lang_items` depends on) + = note: first definition in `std` loaded from SYSROOT/libstd-*.rlib + = note: second definition in the local crate (`E0152_duplicate_lang_items`) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0152`. diff --git a/tests/ui/error-codes/E0184.stderr b/tests/ui/error-codes/E0184.stderr index 625c6685a6144..1a7df9ac095ad 100644 --- a/tests/ui/error-codes/E0184.stderr +++ b/tests/ui/error-codes/E0184.stderr @@ -3,8 +3,6 @@ error[E0184]: the trait `Copy` cannot be implemented for this type; the type has | LL | #[derive(Copy)] | ^^^^ `Copy` not allowed on types with destructors - | - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0186.rs b/tests/ui/error-codes/E0186.rs index 83ef78ef2c033..9b507f9b9882b 100644 --- a/tests/ui/error-codes/E0186.rs +++ b/tests/ui/error-codes/E0186.rs @@ -1,12 +1,12 @@ trait Foo { - fn foo(&self); //~ `&self` used in trait + fn foo(&self); //~ NOTE `&self` used in trait } struct Bar; impl Foo for Bar { fn foo() {} //~ ERROR E0186 - //~^ expected `&self` in impl + //~^ NOTE expected `&self` in impl } fn main() { diff --git a/tests/ui/error-codes/E0195.rs b/tests/ui/error-codes/E0195.rs index a7e51dff2f3fd..66968f70bd9e0 100644 --- a/tests/ui/error-codes/E0195.rs +++ b/tests/ui/error-codes/E0195.rs @@ -1,13 +1,25 @@ trait Trait { +//~^ NOTE in this trait... +//~| NOTE in this trait... fn bar<'a,'b:'a>(x: &'a str, y: &'b str); - //~^ NOTE lifetimes in impl do not match this associated function in trait + //~^ NOTE `'a` is early-bound + //~| NOTE this lifetime bound makes `'a` early-bound + //~| NOTE `'b` is early-bound + //~| NOTE this lifetime bound makes `'b` early-bound } struct Foo; impl Trait for Foo { - fn bar<'a,'b>(x: &'a str, y: &'b str) { //~ ERROR E0195 - //~^ NOTE lifetimes do not match associated function in trait +//~^ NOTE in this impl... +//~| NOTE in this impl... + fn bar<'a,'b>(x: &'a str, y: &'b str) { + //~^ ERROR E0195 + //~| NOTE `'a` differs between the trait and impl + //~| NOTE `'a` is late-bound + //~| NOTE `'b` differs between the trait and impl + //~| NOTE `'b` is late-bound + //~| NOTE lifetime parameters differ in whether they are early- or late-bound } } diff --git a/tests/ui/error-codes/E0195.stderr b/tests/ui/error-codes/E0195.stderr index 9767dee9aecd1..d0295b3643477 100644 --- a/tests/ui/error-codes/E0195.stderr +++ b/tests/ui/error-codes/E0195.stderr @@ -1,11 +1,42 @@ -error[E0195]: lifetime parameters or bounds on associated function `bar` do not match the trait declaration - --> $DIR/E0195.rs:9:11 +error[E0195]: lifetime parameters do not match the trait definition + --> $DIR/E0195.rs:16:12 | +LL | fn bar<'a,'b>(x: &'a str, y: &'b str) { + | ^^ ^^ + | + = note: lifetime parameters differ in whether they are early- or late-bound +note: `'a` differs between the trait and impl + --> $DIR/E0195.rs:4:12 + | +LL | trait Trait { + | ----------- in this trait... +... LL | fn bar<'a,'b:'a>(x: &'a str, y: &'b str); - | ---------- lifetimes in impl do not match this associated function in trait + | ^^ -- this lifetime bound makes `'a` early-bound + | | + | `'a` is early-bound +... +LL | impl Trait for Foo { + | ------------------ in this impl... +... +LL | fn bar<'a,'b>(x: &'a str, y: &'b str) { + | ^^ `'a` is late-bound +note: `'b` differs between the trait and impl + --> $DIR/E0195.rs:4:15 + | +LL | trait Trait { + | ----------- in this trait... +... +LL | fn bar<'a,'b:'a>(x: &'a str, y: &'b str); + | ^^ -- this lifetime bound makes `'b` early-bound + | | + | `'b` is early-bound +... +LL | impl Trait for Foo { + | ------------------ in this impl... ... LL | fn bar<'a,'b>(x: &'a str, y: &'b str) { - | ^^^^^^^ lifetimes do not match associated function in trait + | ^^ `'b` is late-bound error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0253.rs b/tests/ui/error-codes/E0253.rs deleted file mode 100644 index 8284f791c6480..0000000000000 --- a/tests/ui/error-codes/E0253.rs +++ /dev/null @@ -1,10 +0,0 @@ -mod foo { - pub trait MyTrait { - type SomeType; - } -} - -use foo::MyTrait::SomeType; - //~^ ERROR E0253 - -fn main() {} diff --git a/tests/ui/error-codes/E0253.stderr b/tests/ui/error-codes/E0253.stderr deleted file mode 100644 index 954dbc8169395..0000000000000 --- a/tests/ui/error-codes/E0253.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0253]: `SomeType` is not directly importable - --> $DIR/E0253.rs:7:5 - | -LL | use foo::MyTrait::SomeType; - | ^^^^^^^^^^^^^^^^^^^^^^ cannot be imported directly - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0253`. diff --git a/tests/ui/error-codes/E0261.rs b/tests/ui/error-codes/E0261.rs index f05e09aa0da66..e37eab9501ed6 100644 --- a/tests/ui/error-codes/E0261.rs +++ b/tests/ui/error-codes/E0261.rs @@ -1,9 +1,9 @@ fn foo(x: &'a str) { } //~ ERROR E0261 - //~| undeclared lifetime + //~| NOTE undeclared lifetime struct Foo { x: &'a str, //~ ERROR E0261 - //~| undeclared lifetime + //~| NOTE undeclared lifetime } fn main() {} diff --git a/tests/ui/error-codes/E0262.rs b/tests/ui/error-codes/E0262.rs index 55264f1387f80..460ea95148c40 100644 --- a/tests/ui/error-codes/E0262.rs +++ b/tests/ui/error-codes/E0262.rs @@ -1,4 +1,4 @@ fn foo<'static>(x: &'static str) { } //~ ERROR E0262 - //~| 'static is a reserved lifetime name + //~| NOTE 'static is a reserved lifetime name fn main() {} diff --git a/tests/ui/error-codes/E0275.rs b/tests/ui/error-codes/E0275.rs index df7b606155eb4..28a9676f03e39 100644 --- a/tests/ui/error-codes/E0275.rs +++ b/tests/ui/error-codes/E0275.rs @@ -1,4 +1,3 @@ -//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" trait Foo {} struct Bar(T); diff --git a/tests/ui/error-codes/E0275.stderr b/tests/ui/error-codes/E0275.stderr index b507540834060..3b31c87320ae9 100644 --- a/tests/ui/error-codes/E0275.stderr +++ b/tests/ui/error-codes/E0275.stderr @@ -1,12 +1,12 @@ error[E0275]: overflow evaluating the requirement `Bar>>>>>>: Foo` - --> $DIR/E0275.rs:6:33 + --> $DIR/E0275.rs:5:33 | LL | impl Foo for T where Bar: Foo {} | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`E0275`) note: required for `Bar>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Foo` - --> $DIR/E0275.rs:6:9 + --> $DIR/E0275.rs:5:9 | LL | impl Foo for T where Bar: Foo {} | ^^^ ^ --- unsatisfied trait bound introduced here diff --git a/tests/ui/error-codes/E0381-duplicated-label.rs b/tests/ui/error-codes/E0381-duplicated-label.rs new file mode 100644 index 0000000000000..84a85caa65df3 --- /dev/null +++ b/tests/ui/error-codes/E0381-duplicated-label.rs @@ -0,0 +1,16 @@ +//! Regression test for duplicated label in E0381 error message. +//! +//! Issue: +fn main() { + fn test() { + loop { + let blah: Option; + if true { + blah = Some("".to_string()); + } + if let Some(blah) = blah.as_ref() { //~ ERROR E0381 + } + } + } + println!("{:?}", test()) +} diff --git a/tests/ui/error-codes/E0381-duplicated-label.stderr b/tests/ui/error-codes/E0381-duplicated-label.stderr new file mode 100644 index 0000000000000..36e6d966f09d1 --- /dev/null +++ b/tests/ui/error-codes/E0381-duplicated-label.stderr @@ -0,0 +1,15 @@ +error[E0381]: used binding `blah` is possibly-uninitialized + --> $DIR/E0381-duplicated-label.rs:11:33 + | +LL | let blah: Option; + | ---- binding declared here but left uninitialized +LL | if true { +LL | blah = Some("".to_string()); + | ---- binding initialized here in some conditions +LL | } +LL | if let Some(blah) = blah.as_ref() { + | ^^^^ `blah` used here but it is possibly-uninitialized + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0381`. diff --git a/tests/ui/error-codes/E0428.rs b/tests/ui/error-codes/E0428.rs index eb9594fb8b6e3..08393b009d5eb 100644 --- a/tests/ui/error-codes/E0428.rs +++ b/tests/ui/error-codes/E0428.rs @@ -1,4 +1,6 @@ -struct Bar; //~ previous definition of the type `Bar` here +//@ dont-require-annotations: NOTE + +struct Bar; //~ NOTE previous definition of the type `Bar` here struct Bar; //~ ERROR E0428 fn main () { diff --git a/tests/ui/error-codes/E0428.stderr b/tests/ui/error-codes/E0428.stderr index b5bb84e2e68fc..78f57066e2634 100644 --- a/tests/ui/error-codes/E0428.stderr +++ b/tests/ui/error-codes/E0428.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `Bar` is defined multiple times - --> $DIR/E0428.rs:2:1 + --> $DIR/E0428.rs:4:1 | LL | struct Bar; | ----------- previous definition of the type `Bar` here diff --git a/tests/ui/error-codes/E0462.stderr b/tests/ui/error-codes/E0462.stderr index 18f20d79385e3..be32001fad46f 100644 --- a/tests/ui/error-codes/E0462.stderr +++ b/tests/ui/error-codes/E0462.stderr @@ -5,7 +5,7 @@ LL | extern crate found_staticlib; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the following crate versions were found: - crate `found_staticlib`: $TEST_BUILD_DIR/error-codes/E0462/auxiliary/libfound_staticlib.somelib + crate `found_staticlib`: $TEST_BUILD_DIR/auxiliary/libfound_staticlib.somelib = help: please recompile that crate using --crate-type lib error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0464.stderr b/tests/ui/error-codes/E0464.stderr index edb0b1cf41e1a..ec18978a59ac5 100644 --- a/tests/ui/error-codes/E0464.stderr +++ b/tests/ui/error-codes/E0464.stderr @@ -4,9 +4,9 @@ error[E0464]: multiple candidates for `rlib` dependency `crateresolve1` found LL | extern crate crateresolve1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: candidate #1: $TEST_BUILD_DIR/error-codes/E0464/auxiliary/libcrateresolve1-1.somelib - = note: candidate #2: $TEST_BUILD_DIR/error-codes/E0464/auxiliary/libcrateresolve1-2.somelib - = note: candidate #3: $TEST_BUILD_DIR/error-codes/E0464/auxiliary/libcrateresolve1-3.somelib + = note: candidate #1: $TEST_BUILD_DIR/auxiliary/libcrateresolve1-1.somelib + = note: candidate #2: $TEST_BUILD_DIR/auxiliary/libcrateresolve1-2.somelib + = note: candidate #3: $TEST_BUILD_DIR/auxiliary/libcrateresolve1-3.somelib error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0516.rs b/tests/ui/error-codes/E0516.rs index 834bb630989ff..f81b98cdadc8e 100644 --- a/tests/ui/error-codes/E0516.rs +++ b/tests/ui/error-codes/E0516.rs @@ -1,4 +1,4 @@ fn main() { let x: typeof(92) = 92; //~ ERROR E0516 - //~| reserved keyword + //~| NOTE reserved keyword } diff --git a/tests/ui/error-codes/E0523.stderr b/tests/ui/error-codes/E0523.stderr index c6e65b55e2b30..f4072d6910444 100644 --- a/tests/ui/error-codes/E0523.stderr +++ b/tests/ui/error-codes/E0523.stderr @@ -4,9 +4,9 @@ error[E0464]: multiple candidates for `rlib` dependency `crateresolve1` found LL | extern crate crateresolve1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: candidate #1: $TEST_BUILD_DIR/error-codes/E0523/auxiliary/libcrateresolve1-1.somelib - = note: candidate #2: $TEST_BUILD_DIR/error-codes/E0523/auxiliary/libcrateresolve1-2.somelib - = note: candidate #3: $TEST_BUILD_DIR/error-codes/E0523/auxiliary/libcrateresolve1-3.somelib + = note: candidate #1: $TEST_BUILD_DIR/auxiliary/libcrateresolve1-1.somelib + = note: candidate #2: $TEST_BUILD_DIR/auxiliary/libcrateresolve1-2.somelib + = note: candidate #3: $TEST_BUILD_DIR/auxiliary/libcrateresolve1-3.somelib error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0582.stderr b/tests/ui/error-codes/E0582.stderr index 64b527cdcc2a2..ac97a6ff00a81 100644 --- a/tests/ui/error-codes/E0582.stderr +++ b/tests/ui/error-codes/E0582.stderr @@ -14,7 +14,7 @@ error[E0308]: mismatched types --> $DIR/E0582.rs:22:5 | LL | bar(mk_unexpected_char_err) - | ^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other | = note: expected enum `Option<&_>` found enum `Option<&'a _>` diff --git a/tests/ui/error-codes/E0597.rs b/tests/ui/error-codes/E0597.rs index 7217e351281da..ebe42f54212f5 100644 --- a/tests/ui/error-codes/E0597.rs +++ b/tests/ui/error-codes/E0597.rs @@ -6,7 +6,7 @@ fn main() { let mut x = Foo { x: None }; let y = 0; x.x = Some(&y); - //~^ `y` does not live long enough [E0597] + //~^ ERROR `y` does not live long enough [E0597] } impl<'a> Drop for Foo<'a> { fn drop(&mut self) { } } diff --git a/tests/ui/error-codes/E0602.rs b/tests/ui/error-codes/E0602.rs index 1849fd2d895cf..381bd1ffb54c0 100644 --- a/tests/ui/error-codes/E0602.rs +++ b/tests/ui/error-codes/E0602.rs @@ -1,8 +1,11 @@ //@ compile-flags:-D bogus //@ check-pass - -//@ error-pattern:E0602 -//@ error-pattern:requested on the command line with `-D bogus` -//@ error-pattern:`#[warn(unknown_lints)]` on by default +//@ dont-require-annotations: NOTE fn main() {} + +//~? WARN unknown lint: `bogus` +//~? WARN unknown lint: `bogus` +//~? WARN unknown lint: `bogus` +//~? NOTE requested on the command line with `-D bogus` +//~? NOTE `#[warn(unknown_lints)]` on by default diff --git a/tests/ui/error-codes/E0606.rs b/tests/ui/error-codes/E0606.rs index 6f6c6513846cf..2d430fcdc169f 100644 --- a/tests/ui/error-codes/E0606.rs +++ b/tests/ui/error-codes/E0606.rs @@ -1,4 +1,4 @@ fn main() { let x = &(&0u8 as u8); //~ ERROR E0606 - x as u8; //~ casting `&u8` as `u8` is invalid [E0606] + x as u8; //~ ERROR casting `&u8` as `u8` is invalid [E0606] } diff --git a/tests/ui/error-codes/E0622.rs b/tests/ui/error-codes/E0622.rs deleted file mode 100644 index 08c6d17129604..0000000000000 --- a/tests/ui/error-codes/E0622.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![feature(intrinsics)] -extern "rust-intrinsic" { - pub static atomic_singlethreadfence_seqcst : unsafe extern "rust-intrinsic" fn(); - //~^ ERROR intrinsic must be a function [E0622] -} -fn main() { unsafe { atomic_singlethreadfence_seqcst(); } } diff --git a/tests/ui/error-codes/E0622.stderr b/tests/ui/error-codes/E0622.stderr deleted file mode 100644 index 739ec984fc606..0000000000000 --- a/tests/ui/error-codes/E0622.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0622]: intrinsic must be a function - --> $DIR/E0622.rs:3:5 - | -LL | pub static atomic_singlethreadfence_seqcst : unsafe extern "rust-intrinsic" fn(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a function - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0622`. diff --git a/tests/ui/error-festival.rs b/tests/ui/error-emitter/error-festival.rs similarity index 86% rename from tests/ui/error-festival.rs rename to tests/ui/error-emitter/error-festival.rs index 356564e54077a..ebb5882352cba 100644 --- a/tests/ui/error-festival.rs +++ b/tests/ui/error-emitter/error-festival.rs @@ -1,3 +1,5 @@ +//! Check that if there are a lot of errors we truncate the list of errors appropriately + enum Question { Yes, No, diff --git a/tests/ui/error-festival.stderr b/tests/ui/error-emitter/error-festival.stderr similarity index 86% rename from tests/ui/error-festival.stderr rename to tests/ui/error-emitter/error-festival.stderr index 9db9536379141..be484bc8094fe 100644 --- a/tests/ui/error-festival.stderr +++ b/tests/ui/error-emitter/error-festival.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `y` in this scope - --> $DIR/error-festival.rs:14:5 + --> $DIR/error-festival.rs:16:5 | LL | y = 2; | ^ @@ -15,19 +15,19 @@ LL | let y = 2; | +++ error[E0603]: constant `FOO` is private - --> $DIR/error-festival.rs:22:10 + --> $DIR/error-festival.rs:24:10 | LL | foo::FOO; | ^^^ private constant | note: the constant `FOO` is defined here - --> $DIR/error-festival.rs:7:5 + --> $DIR/error-festival.rs:9:5 | LL | const FOO: u32 = 0; | ^^^^^^^^^^^^^^^^^^^ error[E0368]: binary assignment operation `+=` cannot be applied to type `&str` - --> $DIR/error-festival.rs:12:5 + --> $DIR/error-festival.rs:14:5 | LL | x += 2; | -^^^^^ @@ -35,19 +35,19 @@ LL | x += 2; | cannot use `+=` on type `&str` error[E0599]: no method named `z` found for reference `&str` in the current scope - --> $DIR/error-festival.rs:16:7 + --> $DIR/error-festival.rs:18:7 | LL | x.z(); | ^ method not found in `&str` error[E0600]: cannot apply unary operator `!` to type `Question` - --> $DIR/error-festival.rs:19:5 + --> $DIR/error-festival.rs:21:5 | LL | !Question::Yes; | ^^^^^^^^^^^^^^ cannot apply unary operator `!` | note: an implementation of `Not` might be missing for `Question` - --> $DIR/error-festival.rs:1:1 + --> $DIR/error-festival.rs:3:1 | LL | enum Question { | ^^^^^^^^^^^^^ must implement `Not` @@ -55,7 +55,7 @@ note: the trait `Not` must be implemented --> $SRC_DIR/core/src/ops/bit.rs:LL:COL error[E0604]: only `u8` can be cast as `char`, not `u32` - --> $DIR/error-festival.rs:25:5 + --> $DIR/error-festival.rs:27:5 | LL | 0u32 as char; | ^^^^^^^^^^^^ @@ -64,13 +64,13 @@ LL | 0u32 as char; | help: try `char::from_u32` instead: `char::from_u32(0u32)` error[E0605]: non-primitive cast: `u8` as `Vec` - --> $DIR/error-festival.rs:29:5 + --> $DIR/error-festival.rs:31:5 | LL | x as Vec; | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0054]: cannot cast `{integer}` as `bool` - --> $DIR/error-festival.rs:33:24 + --> $DIR/error-festival.rs:35:24 | LL | let x_is_nonzero = x as bool; | ^^^^^^^^^ @@ -82,7 +82,7 @@ LL + let x_is_nonzero = x != 0; | error[E0606]: casting `&u8` as `u32` is invalid - --> $DIR/error-festival.rs:37:18 + --> $DIR/error-festival.rs:39:18 | LL | let y: u32 = x as u32; | ^^^^^^^^ @@ -93,7 +93,7 @@ LL | let y: u32 = *x as u32; | + error[E0607]: cannot cast thin pointer `*const u8` to wide pointer `*const [u8]` - --> $DIR/error-festival.rs:41:5 + --> $DIR/error-festival.rs:43:5 | LL | v as *const [u8]; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/error-emitter/highlighting.rs b/tests/ui/error-emitter/highlighting.rs index b3c1acbd342ca..d7a0c9d224665 100644 --- a/tests/ui/error-emitter/highlighting.rs +++ b/tests/ui/error-emitter/highlighting.rs @@ -1,7 +1,6 @@ // Make sure "highlighted" code is colored purple //@ compile-flags: --error-format=human --color=always -//@ error-pattern:for<'a>  //@ edition:2018 use core::pin::Pin; @@ -21,3 +20,5 @@ fn wrapped_fn<'a>(_: Box<(dyn Any + Send)>) -> Pin  diff --git a/tests/ui/error-emitter/highlighting.svg b/tests/ui/error-emitter/highlighting.svg index 68fc118f1a6e4..19818ab6146c7 100644 --- a/tests/ui/error-emitter/highlighting.svg +++ b/tests/ui/error-emitter/highlighting.svg @@ -23,7 +23,7 @@ error[E0308]: mismatched types - --> $DIR/highlighting.rs:22:11 + --> $DIR/highlighting.rs:21:11 | @@ -43,7 +43,7 @@ note: function defined here - --> $DIR/highlighting.rs:11:4 + --> $DIR/highlighting.rs:10:4 | diff --git a/tests/ui/error-emitter/highlighting.windows.svg b/tests/ui/error-emitter/highlighting.windows.svg index c7dd001434eef..f891bc1d2a687 100644 --- a/tests/ui/error-emitter/highlighting.windows.svg +++ b/tests/ui/error-emitter/highlighting.windows.svg @@ -24,7 +24,7 @@ error[E0308]: mismatched types - --> $DIR/highlighting.rs:22:11 + --> $DIR/highlighting.rs:21:11 | @@ -44,7 +44,7 @@ note: function defined here - --> $DIR/highlighting.rs:11:4 + --> $DIR/highlighting.rs:10:4 | diff --git a/tests/ui/error-emitter/multiline-multipart-suggestion.rs b/tests/ui/error-emitter/multiline-multipart-suggestion.rs index a938b280ca2ec..92dede1b5d83e 100644 --- a/tests/ui/error-emitter/multiline-multipart-suggestion.rs +++ b/tests/ui/error-emitter/multiline-multipart-suggestion.rs @@ -1,5 +1,4 @@ //@ compile-flags: --error-format=human --color=always -//@ error-pattern: missing lifetime specifier fn short(foo_bar: &Vec<&i32>) -> &i32 { &12 @@ -17,3 +16,5 @@ fn long2( &12 } fn main() {} + +//~? RAW missing lifetime specifier diff --git a/tests/ui/error-emitter/multiline-multipart-suggestion.svg b/tests/ui/error-emitter/multiline-multipart-suggestion.svg index c0fb98555ad86..dd84234236d77 100644 --- a/tests/ui/error-emitter/multiline-multipart-suggestion.svg +++ b/tests/ui/error-emitter/multiline-multipart-suggestion.svg @@ -23,7 +23,7 @@ error[E0106]: missing lifetime specifier - --> $DIR/multiline-multipart-suggestion.rs:4:34 + --> $DIR/multiline-multipart-suggestion.rs:3:34 | @@ -47,7 +47,7 @@ error[E0106]: missing lifetime specifier - --> $DIR/multiline-multipart-suggestion.rs:11:6 + --> $DIR/multiline-multipart-suggestion.rs:10:6 | @@ -83,7 +83,7 @@ error[E0106]: missing lifetime specifier - --> $DIR/multiline-multipart-suggestion.rs:16:29 + --> $DIR/multiline-multipart-suggestion.rs:15:29 | diff --git a/tests/ui/error-emitter/multiline-multipart-suggestion.windows.svg b/tests/ui/error-emitter/multiline-multipart-suggestion.windows.svg index 61b544001f08c..144e57165da7c 100644 --- a/tests/ui/error-emitter/multiline-multipart-suggestion.windows.svg +++ b/tests/ui/error-emitter/multiline-multipart-suggestion.windows.svg @@ -23,7 +23,7 @@ error[E0106]: missing lifetime specifier - --> $DIR/multiline-multipart-suggestion.rs:4:34 + --> $DIR/multiline-multipart-suggestion.rs:3:34 | @@ -47,7 +47,7 @@ error[E0106]: missing lifetime specifier - --> $DIR/multiline-multipart-suggestion.rs:11:6 + --> $DIR/multiline-multipart-suggestion.rs:10:6 | @@ -83,7 +83,7 @@ error[E0106]: missing lifetime specifier - --> $DIR/multiline-multipart-suggestion.rs:16:29 + --> $DIR/multiline-multipart-suggestion.rs:15:29 | diff --git a/tests/ui/errors/auxiliary/file-debuginfo.rs b/tests/ui/errors/auxiliary/file-debuginfo.rs new file mode 100644 index 0000000000000..08113ec26bfd4 --- /dev/null +++ b/tests/ui/errors/auxiliary/file-debuginfo.rs @@ -0,0 +1,11 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=remapped +//@ compile-flags: -Zremap-path-scope=debuginfo + +#[macro_export] +macro_rules! my_file { + () => { file!() } +} + +pub fn file() -> &'static str { + file!() +} diff --git a/tests/ui/errors/auxiliary/file-diag.rs b/tests/ui/errors/auxiliary/file-diag.rs new file mode 100644 index 0000000000000..f29c349f703b2 --- /dev/null +++ b/tests/ui/errors/auxiliary/file-diag.rs @@ -0,0 +1,11 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=remapped +//@ compile-flags: -Zremap-path-scope=diagnostics + +#[macro_export] +macro_rules! my_file { + () => { file!() } +} + +pub fn file() -> &'static str { + file!() +} diff --git a/tests/ui/errors/auxiliary/file-macro.rs b/tests/ui/errors/auxiliary/file-macro.rs new file mode 100644 index 0000000000000..11abc0549a7b8 --- /dev/null +++ b/tests/ui/errors/auxiliary/file-macro.rs @@ -0,0 +1,11 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=remapped +//@ compile-flags: -Zremap-path-scope=macro + +#[macro_export] +macro_rules! my_file { + () => { file!() } +} + +pub fn file() -> &'static str { + file!() +} diff --git a/tests/ui/errors/auxiliary/file.rs b/tests/ui/errors/auxiliary/file.rs new file mode 100644 index 0000000000000..63a7b3b58f0f5 --- /dev/null +++ b/tests/ui/errors/auxiliary/file.rs @@ -0,0 +1,8 @@ +#[macro_export] +macro_rules! my_file { + () => { file!() } +} + +pub fn file() -> &'static str { + file!() +} diff --git a/tests/ui/errors/auxiliary/trait-debuginfo.rs b/tests/ui/errors/auxiliary/trait-debuginfo.rs new file mode 100644 index 0000000000000..d5a0825fe6d13 --- /dev/null +++ b/tests/ui/errors/auxiliary/trait-debuginfo.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=remapped +//@ compile-flags: -Zremap-path-scope=debuginfo + +pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/auxiliary/trait-diag.rs b/tests/ui/errors/auxiliary/trait-diag.rs new file mode 100644 index 0000000000000..e07961a276a9d --- /dev/null +++ b/tests/ui/errors/auxiliary/trait-diag.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=remapped +//@ compile-flags: -Zremap-path-scope=diagnostics + +pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/auxiliary/trait-macro.rs b/tests/ui/errors/auxiliary/trait-macro.rs new file mode 100644 index 0000000000000..48673d04ee16f --- /dev/null +++ b/tests/ui/errors/auxiliary/trait-macro.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=remapped +//@ compile-flags: -Zremap-path-scope=macro + +pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/auxiliary/trait.rs b/tests/ui/errors/auxiliary/trait.rs new file mode 100644 index 0000000000000..0e7e5400aac59 --- /dev/null +++ b/tests/ui/errors/auxiliary/trait.rs @@ -0,0 +1 @@ +pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/pic-linker.rs b/tests/ui/errors/pic-linker.rs index d90989903048e..36495ca8fe958 100644 --- a/tests/ui/errors/pic-linker.rs +++ b/tests/ui/errors/pic-linker.rs @@ -5,6 +5,7 @@ //@ ignore-windows //@ ignore-macos //@ ignore-cross-compile +//@ ignore-aix //@ compile-flags: -Clink-args=-Wl,-z,text //@ run-pass diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.not-diag-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.not-diag-in-deps.stderr new file mode 100644 index 0000000000000..3ddff11798de3 --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-diagnostics.not-diag-in-deps.stderr @@ -0,0 +1,17 @@ +error[E0277]: `A` doesn't implement `std::fmt::Display` + --> remapped/errors/remap-path-prefix-diagnostics.rs:LL:COL + | +LL | impl r#trait::Trait for A {} + | ^ `A` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `A` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `Trait` + --> $DIR/auxiliary/trait.rs:LL:COL + | +LL | pub trait Trait: std::fmt::Display {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.only-debuginfo-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.only-debuginfo-in-deps.stderr new file mode 100644 index 0000000000000..85c781425b16a --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-diagnostics.only-debuginfo-in-deps.stderr @@ -0,0 +1,17 @@ +error[E0277]: `A` doesn't implement `std::fmt::Display` + --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL + | +LL | impl r#trait::Trait for A {} + | ^ `A` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `A` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `Trait` + --> $DIR/auxiliary/trait-debuginfo.rs:LL:COL + | +LL | pub trait Trait: std::fmt::Display {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr new file mode 100644 index 0000000000000..792ea7925ad56 --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr @@ -0,0 +1,17 @@ +error[E0277]: `A` doesn't implement `std::fmt::Display` + --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL + | +LL | impl r#trait::Trait for A {} + | ^ `A` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `A` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `Trait` + --> $DIR/auxiliary/trait-diag.rs:LL:COL + | +LL | pub trait Trait: std::fmt::Display {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.only-macro-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.only-macro-in-deps.stderr new file mode 100644 index 0000000000000..d13333d2e4825 --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-diagnostics.only-macro-in-deps.stderr @@ -0,0 +1,17 @@ +error[E0277]: `A` doesn't implement `std::fmt::Display` + --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL + | +LL | impl r#trait::Trait for A {} + | ^ `A` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `A` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `Trait` + --> $DIR/auxiliary/trait-macro.rs:LL:COL + | +LL | pub trait Trait: std::fmt::Display {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.rs b/tests/ui/errors/remap-path-prefix-diagnostics.rs new file mode 100644 index 0000000000000..fac7e937cb0b8 --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-diagnostics.rs @@ -0,0 +1,57 @@ +// This test exercises `-Zremap-path-scope`, diagnostics printing paths and dependency. +// +// We test different combinations with/without remap in deps, with/without remap in this +// crate but always in deps and always here but never in deps. + +//@ revisions: with-diag-in-deps with-macro-in-deps with-debuginfo-in-deps +//@ revisions: only-diag-in-deps only-macro-in-deps only-debuginfo-in-deps +//@ revisions: not-diag-in-deps + +//@[with-diag-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[with-macro-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[with-debuginfo-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[not-diag-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped + +//@[with-diag-in-deps] compile-flags: -Zremap-path-scope=diagnostics +//@[with-macro-in-deps] compile-flags: -Zremap-path-scope=macro +//@[with-debuginfo-in-deps] compile-flags: -Zremap-path-scope=debuginfo +//@[not-diag-in-deps] compile-flags: -Zremap-path-scope=diagnostics + +//@[with-diag-in-deps] aux-build:trait-diag.rs +//@[with-macro-in-deps] aux-build:trait-macro.rs +//@[with-debuginfo-in-deps] aux-build:trait-debuginfo.rs +//@[only-diag-in-deps] aux-build:trait-diag.rs +//@[only-macro-in-deps] aux-build:trait-macro.rs +//@[only-debuginfo-in-deps] aux-build:trait-debuginfo.rs +//@[not-diag-in-deps] aux-build:trait.rs + +// The $SRC_DIR*.rs:LL:COL normalisation doesn't kick in automatically +// as the remapped revision will not begin with $SRC_DIR_REAL, +// so we have to do it ourselves. +//@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:COL" + +#[cfg(any(with_diag_in_deps, only_diag_in_deps))] +extern crate trait_diag as r#trait; + +#[cfg(any(with_macro_in_deps, only_macro_in_deps))] +extern crate trait_macro as r#trait; + +#[cfg(any(with_debuginfo_in_deps, only_debuginfo_in_deps))] +extern crate trait_debuginfo as r#trait; + +#[cfg(not_diag_in_deps)] +extern crate r#trait as r#trait; + +struct A; + +impl r#trait::Trait for A {} +//[with-macro-in-deps]~^ ERROR `A` doesn't implement `std::fmt::Display` +//[with-debuginfo-in-deps]~^^ ERROR `A` doesn't implement `std::fmt::Display` +//[only-diag-in-deps]~^^^ ERROR `A` doesn't implement `std::fmt::Display` +//[only-macro-in-deps]~^^^^ ERROR `A` doesn't implement `std::fmt::Display` +//[only-debuginfo-in-deps]~^^^^^ ERROR `A` doesn't implement `std::fmt::Display` + +//[with-diag-in-deps]~? ERROR `A` doesn't implement `std::fmt::Display` +//[not-diag-in-deps]~? ERROR `A` doesn't implement `std::fmt::Display` + +fn main() {} diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.with-debuginfo-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.with-debuginfo-in-deps.stderr new file mode 100644 index 0000000000000..85c781425b16a --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-diagnostics.with-debuginfo-in-deps.stderr @@ -0,0 +1,17 @@ +error[E0277]: `A` doesn't implement `std::fmt::Display` + --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL + | +LL | impl r#trait::Trait for A {} + | ^ `A` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `A` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `Trait` + --> $DIR/auxiliary/trait-debuginfo.rs:LL:COL + | +LL | pub trait Trait: std::fmt::Display {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.with-diag-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.with-diag-in-deps.stderr new file mode 100644 index 0000000000000..08f7fb2c73642 --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-diagnostics.with-diag-in-deps.stderr @@ -0,0 +1,17 @@ +error[E0277]: `A` doesn't implement `std::fmt::Display` + --> remapped/errors/remap-path-prefix-diagnostics.rs:LL:COL + | +LL | impl r#trait::Trait for A {} + | ^ `A` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `A` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `Trait` + --> remapped/errors/auxiliary/trait-diag.rs:LL:COL + | +LL | pub trait Trait: std::fmt::Display {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.with-macro-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.with-macro-in-deps.stderr new file mode 100644 index 0000000000000..d13333d2e4825 --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-diagnostics.with-macro-in-deps.stderr @@ -0,0 +1,17 @@ +error[E0277]: `A` doesn't implement `std::fmt::Display` + --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL + | +LL | impl r#trait::Trait for A {} + | ^ `A` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `A` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `Trait` + --> $DIR/auxiliary/trait-macro.rs:LL:COL + | +LL | pub trait Trait: std::fmt::Display {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/errors/remap-path-prefix-macro.normal.run.stdout b/tests/ui/errors/remap-path-prefix-macro.normal.run.stdout deleted file mode 100644 index 3bbdcbb8655b2..0000000000000 --- a/tests/ui/errors/remap-path-prefix-macro.normal.run.stdout +++ /dev/null @@ -1 +0,0 @@ -remapped/errors/remap-path-prefix-macro.rs diff --git a/tests/ui/errors/remap-path-prefix-macro.not-macro-in-deps.run.stdout b/tests/ui/errors/remap-path-prefix-macro.not-macro-in-deps.run.stdout new file mode 100644 index 0000000000000..13d4611ae543c --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-macro.not-macro-in-deps.run.stdout @@ -0,0 +1,3 @@ +file::my_file!() = remapped/errors/remap-path-prefix-macro.rs +file::file() = $DIR/auxiliary/file.rs +file!() = remapped/errors/remap-path-prefix-macro.rs diff --git a/tests/ui/errors/remap-path-prefix-macro.only-debuginfo-in-deps.run.stdout b/tests/ui/errors/remap-path-prefix-macro.only-debuginfo-in-deps.run.stdout new file mode 100644 index 0000000000000..b2c62ac88c04c --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-macro.only-debuginfo-in-deps.run.stdout @@ -0,0 +1,3 @@ +file::my_file!() = $DIR/remap-path-prefix-macro.rs +file::file() = $DIR/auxiliary/file-debuginfo.rs +file!() = $DIR/remap-path-prefix-macro.rs diff --git a/tests/ui/errors/remap-path-prefix-macro.only-diag-in-deps.run.stdout b/tests/ui/errors/remap-path-prefix-macro.only-diag-in-deps.run.stdout new file mode 100644 index 0000000000000..e64cc0723619c --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-macro.only-diag-in-deps.run.stdout @@ -0,0 +1,3 @@ +file::my_file!() = $DIR/remap-path-prefix-macro.rs +file::file() = $DIR/auxiliary/file-diag.rs +file!() = $DIR/remap-path-prefix-macro.rs diff --git a/tests/ui/errors/remap-path-prefix-macro.only-macro-in-deps.run.stdout b/tests/ui/errors/remap-path-prefix-macro.only-macro-in-deps.run.stdout new file mode 100644 index 0000000000000..b1a93a5bc1ea2 --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-macro.only-macro-in-deps.run.stdout @@ -0,0 +1,3 @@ +file::my_file!() = $DIR/remap-path-prefix-macro.rs +file::file() = remapped/errors/auxiliary/file-macro.rs +file!() = $DIR/remap-path-prefix-macro.rs diff --git a/tests/ui/errors/remap-path-prefix-macro.rs b/tests/ui/errors/remap-path-prefix-macro.rs index 665156027c995..3e93843f91641 100644 --- a/tests/ui/errors/remap-path-prefix-macro.rs +++ b/tests/ui/errors/remap-path-prefix-macro.rs @@ -1,12 +1,47 @@ +// This test exercises `-Zremap-path-scope`, macros (like file!()) and dependency. +// +// We test different combinations with/without remap in deps, with/without remap in +// this crate but always in deps and always here but never in deps. + //@ run-pass //@ check-run-results -//@ revisions: normal with-macro-scope without-macro-scope -//@ compile-flags: --remap-path-prefix={{src-base}}=remapped -//@ [with-macro-scope]compile-flags: -Zremap-path-scope=macro,diagnostics -//@ [without-macro-scope]compile-flags: -Zremap-path-scope=diagnostics -// no-remap-src-base: Manually remap, so the remapped path remains in .stderr file. +//@ revisions: with-diag-in-deps with-macro-in-deps with-debuginfo-in-deps +//@ revisions: only-diag-in-deps only-macro-in-deps only-debuginfo-in-deps +//@ revisions: not-macro-in-deps + +//@[with-diag-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[with-macro-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[with-debuginfo-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[not-macro-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped + +//@[with-diag-in-deps] compile-flags: -Zremap-path-scope=diagnostics +//@[with-macro-in-deps] compile-flags: -Zremap-path-scope=macro +//@[with-debuginfo-in-deps] compile-flags: -Zremap-path-scope=debuginfo +//@[not-macro-in-deps] compile-flags: -Zremap-path-scope=macro + +//@[with-diag-in-deps] aux-build:file-diag.rs +//@[with-macro-in-deps] aux-build:file-macro.rs +//@[with-debuginfo-in-deps] aux-build:file-debuginfo.rs +//@[only-diag-in-deps] aux-build:file-diag.rs +//@[only-macro-in-deps] aux-build:file-macro.rs +//@[only-debuginfo-in-deps] aux-build:file-debuginfo.rs +//@[not-macro-in-deps] aux-build:file.rs + +#[cfg(any(with_diag_in_deps, only_diag_in_deps))] +extern crate file_diag as file; + +#[cfg(any(with_macro_in_deps, only_macro_in_deps))] +extern crate file_macro as file; + +#[cfg(any(with_debuginfo_in_deps, only_debuginfo_in_deps))] +extern crate file_debuginfo as file; + +#[cfg(not_macro_in_deps)] +extern crate file; fn main() { - println!("{}", file!()); + println!("file::my_file!() = {}", file::my_file!()); + println!("file::file() = {}", file::file()); + println!("file!() = {}", file!()); } diff --git a/tests/ui/errors/remap-path-prefix-macro.with-debuginfo-in-deps.run.stdout b/tests/ui/errors/remap-path-prefix-macro.with-debuginfo-in-deps.run.stdout new file mode 100644 index 0000000000000..b2c62ac88c04c --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-macro.with-debuginfo-in-deps.run.stdout @@ -0,0 +1,3 @@ +file::my_file!() = $DIR/remap-path-prefix-macro.rs +file::file() = $DIR/auxiliary/file-debuginfo.rs +file!() = $DIR/remap-path-prefix-macro.rs diff --git a/tests/ui/errors/remap-path-prefix-macro.with-diag-in-deps.run.stdout b/tests/ui/errors/remap-path-prefix-macro.with-diag-in-deps.run.stdout new file mode 100644 index 0000000000000..e64cc0723619c --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-macro.with-diag-in-deps.run.stdout @@ -0,0 +1,3 @@ +file::my_file!() = $DIR/remap-path-prefix-macro.rs +file::file() = $DIR/auxiliary/file-diag.rs +file!() = $DIR/remap-path-prefix-macro.rs diff --git a/tests/ui/errors/remap-path-prefix-macro.with-macro-in-deps.run.stdout b/tests/ui/errors/remap-path-prefix-macro.with-macro-in-deps.run.stdout new file mode 100644 index 0000000000000..5c1781dda1615 --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-macro.with-macro-in-deps.run.stdout @@ -0,0 +1,3 @@ +file::my_file!() = remapped/errors/remap-path-prefix-macro.rs +file::file() = remapped/errors/auxiliary/file-macro.rs +file!() = remapped/errors/remap-path-prefix-macro.rs diff --git a/tests/ui/errors/remap-path-prefix-macro.with-macro-scope.run.stdout b/tests/ui/errors/remap-path-prefix-macro.with-macro-scope.run.stdout deleted file mode 100644 index 3bbdcbb8655b2..0000000000000 --- a/tests/ui/errors/remap-path-prefix-macro.with-macro-scope.run.stdout +++ /dev/null @@ -1 +0,0 @@ -remapped/errors/remap-path-prefix-macro.rs diff --git a/tests/ui/errors/remap-path-prefix-macro.without-macro-scope.run.stdout b/tests/ui/errors/remap-path-prefix-macro.without-macro-scope.run.stdout deleted file mode 100644 index 642823fec86a1..0000000000000 --- a/tests/ui/errors/remap-path-prefix-macro.without-macro-scope.run.stdout +++ /dev/null @@ -1 +0,0 @@ -$DIR/remap-path-prefix-macro.rs diff --git a/tests/ui/errors/remap-path-prefix-reverse.local-self.stderr b/tests/ui/errors/remap-path-prefix-reverse.local-self.stderr index 7aa66be0d0977..b4f83f6bfc0a6 100644 --- a/tests/ui/errors/remap-path-prefix-reverse.local-self.stderr +++ b/tests/ui/errors/remap-path-prefix-reverse.local-self.stderr @@ -1,7 +1,7 @@ error[E0423]: expected value, found struct `remapped_dep::SomeStruct` --> $DIR/remap-path-prefix-reverse.rs:16:13 | -LL | let _ = remapped_dep::SomeStruct; // ~ERROR E0423 +LL | let _ = remapped_dep::SomeStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `remapped_dep::SomeStruct {}` | ::: remapped-aux/remapped_dep.rs:4:1 diff --git a/tests/ui/errors/remap-path-prefix-reverse.remapped-self.stderr b/tests/ui/errors/remap-path-prefix-reverse.remapped-self.stderr index 7aa66be0d0977..b4f83f6bfc0a6 100644 --- a/tests/ui/errors/remap-path-prefix-reverse.remapped-self.stderr +++ b/tests/ui/errors/remap-path-prefix-reverse.remapped-self.stderr @@ -1,7 +1,7 @@ error[E0423]: expected value, found struct `remapped_dep::SomeStruct` --> $DIR/remap-path-prefix-reverse.rs:16:13 | -LL | let _ = remapped_dep::SomeStruct; // ~ERROR E0423 +LL | let _ = remapped_dep::SomeStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `remapped_dep::SomeStruct {}` | ::: remapped-aux/remapped_dep.rs:4:1 diff --git a/tests/ui/errors/remap-path-prefix-reverse.rs b/tests/ui/errors/remap-path-prefix-reverse.rs index 7743e38f50f08..28fdabb8f4df1 100644 --- a/tests/ui/errors/remap-path-prefix-reverse.rs +++ b/tests/ui/errors/remap-path-prefix-reverse.rs @@ -13,5 +13,6 @@ extern crate remapped_dep; fn main() { // The actual error is irrelevant. The important part it that is should show // a snippet of the dependency's source. - let _ = remapped_dep::SomeStruct; // ~ERROR E0423 + let _ = remapped_dep::SomeStruct; + //~^ ERROR expected value, found struct `remapped_dep::SomeStruct` } diff --git a/tests/ui/errors/remap-path-prefix-sysroot.rs b/tests/ui/errors/remap-path-prefix-sysroot.rs index 7281e6da09440..5e2e4fab51de3 100644 --- a/tests/ui/errors/remap-path-prefix-sysroot.rs +++ b/tests/ui/errors/remap-path-prefix-sysroot.rs @@ -3,7 +3,6 @@ //@ [with-remap]compile-flags: --remap-path-prefix={{rust-src-base}}=remapped //@ [with-remap]compile-flags: --remap-path-prefix={{src-base}}=remapped-tests-ui //@ [without-remap]compile-flags: -//@ error-pattern: E0507 // The $SRC_DIR*.rs:LL:COL normalisation doesn't kick in automatically // as the remapped revision will not begin with $SRC_DIR_REAL, @@ -18,7 +17,10 @@ struct Worker { impl Drop for Worker { fn drop(&mut self) { self.thread.join().unwrap(); + //[without-remap]~^ ERROR cannot move out of `self.thread` which is behind a mutable reference } } pub fn main(){} + +//[with-remap]~? ERROR cannot move out of `self.thread` which is behind a mutable reference diff --git a/tests/ui/errors/remap-path-prefix.normal.stderr b/tests/ui/errors/remap-path-prefix.normal.stderr index 46d33d26052cb..5a125a7024a95 100644 --- a/tests/ui/errors/remap-path-prefix.normal.stderr +++ b/tests/ui/errors/remap-path-prefix.normal.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `ferris` in this scope - --> remapped/errors/remap-path-prefix.rs:19:5 + --> remapped/errors/remap-path-prefix.rs:15:5 | LL | ferris | ^^^^^^ not found in this scope diff --git a/tests/ui/errors/remap-path-prefix.rs b/tests/ui/errors/remap-path-prefix.rs index 8809caa4d4f19..7e38e16280f51 100644 --- a/tests/ui/errors/remap-path-prefix.rs +++ b/tests/ui/errors/remap-path-prefix.rs @@ -7,14 +7,13 @@ // The remapped paths are not normalized by compiletest. //@ normalize-stderr: "\\(errors)" -> "/$1" -// The remapped paths aren't recognized by compiletest, so we -// cannot use line-specific patterns. -//@ error-pattern: E0425 - fn main() { // We cannot actually put an ERROR marker here because // the file name in the error message is not what the // test framework expects (since the filename gets remapped). // We still test the expected error in the stderr file. - ferris + ferris //[without-diagnostic-scope]~ ERROR cannot find value `ferris` in this scope } + +//[normal]~? ERROR cannot find value `ferris` in this scope +//[with-diagnostic-scope]~? ERROR cannot find value `ferris` in this scope diff --git a/tests/ui/errors/remap-path-prefix.with-diagnostic-scope.stderr b/tests/ui/errors/remap-path-prefix.with-diagnostic-scope.stderr index 46d33d26052cb..5a125a7024a95 100644 --- a/tests/ui/errors/remap-path-prefix.with-diagnostic-scope.stderr +++ b/tests/ui/errors/remap-path-prefix.with-diagnostic-scope.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `ferris` in this scope - --> remapped/errors/remap-path-prefix.rs:19:5 + --> remapped/errors/remap-path-prefix.rs:15:5 | LL | ferris | ^^^^^^ not found in this scope diff --git a/tests/ui/errors/remap-path-prefix.without-diagnostic-scope.stderr b/tests/ui/errors/remap-path-prefix.without-diagnostic-scope.stderr index 0badea6e27bf3..b6332685dbf0b 100644 --- a/tests/ui/errors/remap-path-prefix.without-diagnostic-scope.stderr +++ b/tests/ui/errors/remap-path-prefix.without-diagnostic-scope.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `ferris` in this scope - --> $DIR/remap-path-prefix.rs:19:5 + --> $DIR/remap-path-prefix.rs:15:5 | LL | ferris | ^^^^^^ not found in this scope diff --git a/tests/ui/errors/wrong-target-spec.rs b/tests/ui/errors/wrong-target-spec.rs index bc9038c1fab54..a3a0e05d82662 100644 --- a/tests/ui/errors/wrong-target-spec.rs +++ b/tests/ui/errors/wrong-target-spec.rs @@ -6,3 +6,5 @@ //@ compile-flags: --target x86_64_unknown-linux-musl fn main() {} + +//~? ERROR error loading target specification: could not find specification for target "x86_64_unknown-linux-musl" diff --git a/tests/ui/errors/wrong-target-spec.stderr b/tests/ui/errors/wrong-target-spec.stderr index 8b06f404078d0..98b03ae00cb3a 100644 --- a/tests/ui/errors/wrong-target-spec.stderr +++ b/tests/ui/errors/wrong-target-spec.stderr @@ -1,2 +1,4 @@ -error: Error loading target specification: Could not find specification for target "x86_64_unknown-linux-musl". Run `rustc --print target-list` for a list of built-in targets +error: error loading target specification: could not find specification for target "x86_64_unknown-linux-musl" + | + = help: run `rustc --print target-list` for a list of built-in targets diff --git a/tests/ui/exclusive-drop-and-copy.stderr b/tests/ui/exclusive-drop-and-copy.stderr index 546079422a733..340ca89c396b4 100644 --- a/tests/ui/exclusive-drop-and-copy.stderr +++ b/tests/ui/exclusive-drop-and-copy.stderr @@ -3,16 +3,12 @@ error[E0184]: the trait `Copy` cannot be implemented for this type; the type has | LL | #[derive(Copy, Clone)] | ^^^^ `Copy` not allowed on types with destructors - | - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0184]: the trait `Copy` cannot be implemented for this type; the type has a destructor --> $DIR/exclusive-drop-and-copy.rs:10:10 | LL | #[derive(Copy, Clone)] | ^^^^ `Copy` not allowed on types with destructors - | - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/explain.rs b/tests/ui/explain/basic.rs similarity index 100% rename from tests/ui/explain.rs rename to tests/ui/explain/basic.rs diff --git a/tests/ui/explain.stdout b/tests/ui/explain/basic.stdout similarity index 100% rename from tests/ui/explain.stdout rename to tests/ui/explain/basic.stdout diff --git a/tests/ui/explain/error-with-no-explanation.rs b/tests/ui/explain/error-with-no-explanation.rs new file mode 100644 index 0000000000000..383820eb45804 --- /dev/null +++ b/tests/ui/explain/error-with-no-explanation.rs @@ -0,0 +1,3 @@ +// It's a valid error with no added explanation +//@ compile-flags: --explain E9999 +//~? ERROR: E9999 is not a valid error code diff --git a/tests/ui/explain/error-with-no-explanation.stderr b/tests/ui/explain/error-with-no-explanation.stderr new file mode 100644 index 0000000000000..afb738cfecb9d --- /dev/null +++ b/tests/ui/explain/error-with-no-explanation.stderr @@ -0,0 +1,2 @@ +error: E9999 is not a valid error code + diff --git a/tests/ui/explain/invalid-error-code.rs b/tests/ui/explain/invalid-error-code.rs new file mode 100644 index 0000000000000..114118d5196b0 --- /dev/null +++ b/tests/ui/explain/invalid-error-code.rs @@ -0,0 +1,2 @@ +//@ compile-flags: --explain error_code +//~? ERROR: error_code is not a valid error code diff --git a/tests/ui/explain/invalid-error-code.stderr b/tests/ui/explain/invalid-error-code.stderr new file mode 100644 index 0000000000000..c33122ea88cb1 --- /dev/null +++ b/tests/ui/explain/invalid-error-code.stderr @@ -0,0 +1,2 @@ +error: error_code is not a valid error code + diff --git a/tests/ui/explain/no-E-prefix.rs b/tests/ui/explain/no-E-prefix.rs new file mode 100644 index 0000000000000..39c9122bfe1b7 --- /dev/null +++ b/tests/ui/explain/no-E-prefix.rs @@ -0,0 +1,2 @@ +//@ compile-flags: --explain 425 +//@ check-pass diff --git a/tests/ui/explain/no-E-prefix.stdout b/tests/ui/explain/no-E-prefix.stdout new file mode 100644 index 0000000000000..756b970aa7e69 --- /dev/null +++ b/tests/ui/explain/no-E-prefix.stdout @@ -0,0 +1,57 @@ +An unresolved name was used. + +Erroneous code examples: + +``` +something_that_doesnt_exist::foo; +// error: unresolved name `something_that_doesnt_exist::foo` + +// or: + +trait Foo { + fn bar() { + Self; // error: unresolved name `Self` + } +} + +// or: + +let x = unknown_variable; // error: unresolved name `unknown_variable` +``` + +Please verify that the name wasn't misspelled and ensure that the +identifier being referred to is valid for the given situation. Example: + +``` +enum something_that_does_exist { + Foo, +} +``` + +Or: + +``` +mod something_that_does_exist { + pub static foo : i32 = 0i32; +} + +something_that_does_exist::foo; // ok! +``` + +Or: + +``` +let unknown_variable = 12u32; +let x = unknown_variable; // ok! +``` + +If the item is not defined in the current module, it must be imported using a +`use` statement, like so: + +``` +use foo::bar; +bar(); +``` + +If the item you are importing is not defined in some super-module of the +current module, then it must also be declared as public (e.g., `pub fn`). diff --git a/tests/ui/explain/overflow-error-code.rs b/tests/ui/explain/overflow-error-code.rs new file mode 100644 index 0000000000000..284d5cddb35a8 --- /dev/null +++ b/tests/ui/explain/overflow-error-code.rs @@ -0,0 +1,4 @@ +// Check that we don't crash on error codes exceeding our internal limit. +// issue: +//@ compile-flags: --explain E10000 +//~? ERROR: E10000 is not a valid error code diff --git a/tests/ui/explain/overflow-error-code.stderr b/tests/ui/explain/overflow-error-code.stderr new file mode 100644 index 0000000000000..67e584ea32e18 --- /dev/null +++ b/tests/ui/explain/overflow-error-code.stderr @@ -0,0 +1,2 @@ +error: E10000 is not a valid error code + diff --git a/tests/ui/explicit-tail-calls/ctfe-tail-call-panic.stderr b/tests/ui/explicit-tail-calls/ctfe-tail-call-panic.stderr index ef71722a0ab50..3bdd09e9683d3 100644 --- a/tests/ui/explicit-tail-calls/ctfe-tail-call-panic.stderr +++ b/tests/ui/explicit-tail-calls/ctfe-tail-call-panic.stderr @@ -9,7 +9,6 @@ note: inside `g` | LL | panic!() | ^^^^^^^^ the failure occurred here - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/explicit/explicit-self-lifetime-mismatch.rs b/tests/ui/explicit/explicit-self-lifetime-mismatch.rs index a9a6f50fb8ea5..aa5e352b6ebef 100644 --- a/tests/ui/explicit/explicit-self-lifetime-mismatch.rs +++ b/tests/ui/explicit/explicit-self-lifetime-mismatch.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + struct Foo<'a,'b> { x: &'a isize, y: &'b isize, @@ -7,13 +9,13 @@ impl<'a,'b> Foo<'a,'b> { fn bar(self: Foo<'b,'a> //~^ ERROR mismatched `self` parameter type - //~| expected struct `Foo<'a, 'b>` - //~| found struct `Foo<'b, 'a>` - //~| lifetime mismatch + //~| NOTE expected struct `Foo<'a, 'b>` + //~| NOTE found struct `Foo<'b, 'a>` + //~| NOTE lifetime mismatch //~| ERROR mismatched `self` parameter type - //~| expected struct `Foo<'a, 'b>` - //~| found struct `Foo<'b, 'a>` - //~| lifetime mismatch + //~| NOTE expected struct `Foo<'a, 'b>` + //~| NOTE found struct `Foo<'b, 'a>` + //~| NOTE lifetime mismatch ) {} } diff --git a/tests/ui/explicit/explicit-self-lifetime-mismatch.stderr b/tests/ui/explicit/explicit-self-lifetime-mismatch.stderr index d5ffa8f1b2fb1..a20901e8c74d2 100644 --- a/tests/ui/explicit/explicit-self-lifetime-mismatch.stderr +++ b/tests/ui/explicit/explicit-self-lifetime-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched `self` parameter type - --> $DIR/explicit-self-lifetime-mismatch.rs:8:12 + --> $DIR/explicit-self-lifetime-mismatch.rs:10:12 | LL | Foo<'b,'a> | ^^^^^^^^^^ lifetime mismatch @@ -7,18 +7,18 @@ LL | Foo<'b,'a> = note: expected struct `Foo<'a, 'b>` found struct `Foo<'b, 'a>` note: the lifetime `'b` as defined here... - --> $DIR/explicit-self-lifetime-mismatch.rs:6:9 + --> $DIR/explicit-self-lifetime-mismatch.rs:8:9 | LL | impl<'a,'b> Foo<'a,'b> { | ^^ note: ...does not necessarily outlive the lifetime `'a` as defined here - --> $DIR/explicit-self-lifetime-mismatch.rs:6:6 + --> $DIR/explicit-self-lifetime-mismatch.rs:8:6 | LL | impl<'a,'b> Foo<'a,'b> { | ^^ error[E0308]: mismatched `self` parameter type - --> $DIR/explicit-self-lifetime-mismatch.rs:8:12 + --> $DIR/explicit-self-lifetime-mismatch.rs:10:12 | LL | Foo<'b,'a> | ^^^^^^^^^^ lifetime mismatch @@ -26,12 +26,12 @@ LL | Foo<'b,'a> = note: expected struct `Foo<'a, 'b>` found struct `Foo<'b, 'a>` note: the lifetime `'a` as defined here... - --> $DIR/explicit-self-lifetime-mismatch.rs:6:6 + --> $DIR/explicit-self-lifetime-mismatch.rs:8:6 | LL | impl<'a,'b> Foo<'a,'b> { | ^^ note: ...does not necessarily outlive the lifetime `'b` as defined here - --> $DIR/explicit-self-lifetime-mismatch.rs:6:9 + --> $DIR/explicit-self-lifetime-mismatch.rs:8:9 | LL | impl<'a,'b> Foo<'a,'b> { | ^^ diff --git a/tests/ui/expr/if/attrs/bad-cfg.rs b/tests/ui/expr/if/attrs/bad-cfg.rs index 3f84929a00e4f..6e7f4b007a925 100644 --- a/tests/ui/expr/if/attrs/bad-cfg.rs +++ b/tests/ui/expr/if/attrs/bad-cfg.rs @@ -1,5 +1,5 @@ #![feature(stmt_expr_attributes)] fn main() { - let _ = #[cfg(FALSE)] if true {}; //~ ERROR removing an expression + let _ = #[cfg(false)] if true {}; //~ ERROR removing an expression } diff --git a/tests/ui/expr/if/attrs/bad-cfg.stderr b/tests/ui/expr/if/attrs/bad-cfg.stderr index ca0eced267d65..d12f5eeaf5fae 100644 --- a/tests/ui/expr/if/attrs/bad-cfg.stderr +++ b/tests/ui/expr/if/attrs/bad-cfg.stderr @@ -1,7 +1,7 @@ error: removing an expression is not supported in this position --> $DIR/bad-cfg.rs:4:13 | -LL | let _ = #[cfg(FALSE)] if true {}; +LL | let _ = #[cfg(false)] if true {}; | ^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/expr/if/attrs/cfg-false-if-attr.rs b/tests/ui/expr/if/attrs/cfg-false-if-attr.rs index c139b347d9983..e6c83b86cb773 100644 --- a/tests/ui/expr/if/attrs/cfg-false-if-attr.rs +++ b/tests/ui/expr/if/attrs/cfg-false-if-attr.rs @@ -1,12 +1,12 @@ //@ check-pass -#[cfg(FALSE)] +#[cfg(false)] fn simple_attr() { #[attr] if true {} #[allow_warnings] if true {} } -#[cfg(FALSE)] +#[cfg(false)] fn if_else_chain() { #[first_attr] if true { } else if false { @@ -14,20 +14,20 @@ fn if_else_chain() { } } -#[cfg(FALSE)] +#[cfg(false)] fn if_let() { #[attr] if let Some(_) = Some(true) {} } fn bar() { - #[cfg(FALSE)] + #[cfg(false)] if true { - let x: () = true; // Should not error due to the #[cfg(FALSE)] + let x: () = true; // Should not error due to the #[cfg(false)] } - #[cfg_attr(not(FALSE), cfg(FALSE))] + #[cfg_attr(not(FALSE), cfg(false))] if true { - let a: () = true; // Should not error due to the applied #[cfg(FALSE)] + let a: () = true; // Should not error due to the applied #[cfg(false)] } } diff --git a/tests/ui/expr/if/attrs/else-attrs.rs b/tests/ui/expr/if/attrs/else-attrs.rs index 85da7cf6bb8c3..4010d9d6132b9 100644 --- a/tests/ui/expr/if/attrs/else-attrs.rs +++ b/tests/ui/expr/if/attrs/else-attrs.rs @@ -1,11 +1,11 @@ -#[cfg(FALSE)] +#[cfg(false)] fn if_else_parse_error() { if true { } #[attr] else if false { //~ ERROR expected } } -#[cfg(FALSE)] +#[cfg(false)] fn else_attr_ifparse_error() { if true { } else #[attr] if false { //~ ERROR outer attributes are not allowed @@ -13,7 +13,7 @@ fn else_attr_ifparse_error() { } } -#[cfg(FALSE)] +#[cfg(false)] fn else_parse_error() { if true { } else if false { diff --git a/tests/ui/expr/if/attrs/gate-whole-expr.rs b/tests/ui/expr/if/attrs/gate-whole-expr.rs index bab01592c247b..885909016b5e1 100644 --- a/tests/ui/expr/if/attrs/gate-whole-expr.rs +++ b/tests/ui/expr/if/attrs/gate-whole-expr.rs @@ -3,7 +3,7 @@ fn main() { let x = 1; - #[cfg(FALSE)] + #[cfg(false)] if false { x = 2; } else if true { diff --git a/tests/ui/expr/if/attrs/let-chains-attr.rs b/tests/ui/expr/if/attrs/let-chains-attr.rs index b3dbd53e5798c..7b74b17784e80 100644 --- a/tests/ui/expr/if/attrs/let-chains-attr.rs +++ b/tests/ui/expr/if/attrs/let-chains-attr.rs @@ -1,8 +1,7 @@ //@ check-pass +//@ edition:2024 -#![feature(let_chains)] - -#[cfg(FALSE)] +#[cfg(false)] fn foo() { #[attr] if let Some(_) = Some(true) && let Ok(_) = Ok(1) { diff --git a/tests/ui/expr/if/if-branch-types.rs b/tests/ui/expr/if/if-branch-types.rs index c125ba30606c5..d6da3ee71931f 100644 --- a/tests/ui/expr/if/if-branch-types.rs +++ b/tests/ui/expr/if/if-branch-types.rs @@ -1,5 +1,6 @@ fn main() { let x = if true { 10i32 } else { 10u32 }; //~^ ERROR `if` and `else` have incompatible types - //~| expected `i32`, found `u32` + //~| NOTE expected `i32`, found `u32` + //~| NOTE expected because of this } diff --git a/tests/ui/expr/if/if-typeck.rs b/tests/ui/expr/if/if-typeck.rs index ba828f11e79ba..0985598f0f757 100644 --- a/tests/ui/expr/if/if-typeck.rs +++ b/tests/ui/expr/if/if-typeck.rs @@ -1,4 +1,3 @@ -//@ error-pattern:mismatched types // issue #513 fn f() { } @@ -6,5 +5,5 @@ fn f() { } fn main() { // f is not a bool - if f { } + if f { } //~ ERROR mismatched types } diff --git a/tests/ui/expr/if/if-typeck.stderr b/tests/ui/expr/if/if-typeck.stderr index 1be43a20105fa..bca00f890cbed 100644 --- a/tests/ui/expr/if/if-typeck.stderr +++ b/tests/ui/expr/if/if-typeck.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/if-typeck.rs:9:8 + --> $DIR/if-typeck.rs:8:8 | LL | if f { } | ^ expected `bool`, found fn item diff --git a/tests/ui/expr/if/if-without-else-result.rs b/tests/ui/expr/if/if-without-else-result.rs index 95604758a6b3e..29aa3d3f6269d 100644 --- a/tests/ui/expr/if/if-without-else-result.rs +++ b/tests/ui/expr/if/if-without-else-result.rs @@ -1,6 +1,8 @@ +//@ dont-require-annotations: NOTE + fn main() { let a = if true { true }; //~^ ERROR `if` may be missing an `else` clause [E0317] - //~| expected `bool`, found `()` + //~| NOTE expected `bool`, found `()` println!("{}", a); } diff --git a/tests/ui/expr/if/if-without-else-result.stderr b/tests/ui/expr/if/if-without-else-result.stderr index 4eaa039349670..d0a0981e2bde6 100644 --- a/tests/ui/expr/if/if-without-else-result.stderr +++ b/tests/ui/expr/if/if-without-else-result.stderr @@ -1,5 +1,5 @@ error[E0317]: `if` may be missing an `else` clause - --> $DIR/if-without-else-result.rs:2:13 + --> $DIR/if-without-else-result.rs:4:13 | LL | let a = if true { true }; | ^^^^^^^^^^----^^ diff --git a/tests/ui/expr/if/issue-4201.rs b/tests/ui/expr/if/issue-4201.rs index 59c465b9e1492..b456673b3c856 100644 --- a/tests/ui/expr/if/issue-4201.rs +++ b/tests/ui/expr/if/issue-4201.rs @@ -1,9 +1,11 @@ +//@ dont-require-annotations: NOTE + fn main() { let a = if true { 0 } else if false { //~^ ERROR `if` may be missing an `else` clause -//~| expected integer, found `()` +//~| NOTE expected integer, found `()` 1 }; } diff --git a/tests/ui/expr/if/issue-4201.stderr b/tests/ui/expr/if/issue-4201.stderr index c761d0b855318..4f5462c0372c1 100644 --- a/tests/ui/expr/if/issue-4201.stderr +++ b/tests/ui/expr/if/issue-4201.stderr @@ -1,5 +1,5 @@ error[E0317]: `if` may be missing an `else` clause - --> $DIR/issue-4201.rs:4:12 + --> $DIR/issue-4201.rs:6:12 | LL | } else if false { | ____________^ diff --git a/tests/ui/ext-nonexistent.rs b/tests/ui/ext-nonexistent.rs index a66407953a990..1293324b67ebe 100644 --- a/tests/ui/ext-nonexistent.rs +++ b/tests/ui/ext-nonexistent.rs @@ -1,2 +1,2 @@ -//@ error-pattern:cannot find macro fn main() { iamnotanextensionthatexists!(""); } +//~^ ERROR cannot find macro `iamnotanextensionthatexists` in this scope diff --git a/tests/ui/ext-nonexistent.stderr b/tests/ui/ext-nonexistent.stderr index 8891e823e4af7..edb59bba6e5d1 100644 --- a/tests/ui/ext-nonexistent.stderr +++ b/tests/ui/ext-nonexistent.stderr @@ -1,5 +1,5 @@ error: cannot find macro `iamnotanextensionthatexists` in this scope - --> $DIR/ext-nonexistent.rs:2:13 + --> $DIR/ext-nonexistent.rs:1:13 | LL | fn main() { iamnotanextensionthatexists!(""); } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/extern-flag/empty-extern-arg.rs b/tests/ui/extern-flag/empty-extern-arg.rs index dea68b5b1ad40..2dee721ed03c0 100644 --- a/tests/ui/extern-flag/empty-extern-arg.rs +++ b/tests/ui/extern-flag/empty-extern-arg.rs @@ -1,6 +1,9 @@ +//~ ERROR extern location for std does not exist //@ compile-flags: --extern std= -//@ error-pattern: extern location for std does not exist //@ needs-unwind since it affects the error output //@ ignore-emscripten missing eh_catch_typeinfo lang item fn main() {} + +//~? ERROR `#[panic_handler]` function required, but not found +//~? ERROR unwinding panics are not supported without std diff --git a/tests/ui/extern-flag/invalid-crate-name-dashed.rs b/tests/ui/extern-flag/invalid-crate-name-dashed.rs index b846214175e1c..bbf473cc5142f 100644 --- a/tests/ui/extern-flag/invalid-crate-name-dashed.rs +++ b/tests/ui/extern-flag/invalid-crate-name-dashed.rs @@ -1,6 +1,4 @@ //@ compile-flags: --extern=my-awesome-library=libawesome.rlib -//@ error-pattern: crate name `my-awesome-library` passed to `--extern` is not a valid ASCII identifier -//@ error-pattern: consider replacing the dashes with underscores: `my_awesome_library` // In a sense, this is a regression test for issue #113035. We no longer suggest // `pub use my-awesome-library::*;` (sic!) as we outright ban this crate name. @@ -8,3 +6,6 @@ pub use my_awesome_library::*; fn main() {} + +//~? ERROR crate name `my-awesome-library` passed to `--extern` is not a valid ASCII identifier +//~? HELP consider replacing the dashes with underscores: `my_awesome_library` diff --git a/tests/ui/extern-flag/invalid-crate-name-non-ascii.rs b/tests/ui/extern-flag/invalid-crate-name-non-ascii.rs index 5231503820fac..7181893828382 100644 --- a/tests/ui/extern-flag/invalid-crate-name-non-ascii.rs +++ b/tests/ui/extern-flag/invalid-crate-name-non-ascii.rs @@ -1,4 +1,5 @@ //@ compile-flags: --extern čɍαţē=libnon_ascii.rlib -//@ error-pattern: crate name `čɍαţē` passed to `--extern` is not a valid ASCII identifier fn main() {} + +//~? ERROR crate name `čɍαţē` passed to `--extern` is not a valid ASCII identifier diff --git a/tests/ui/extern-flag/invalid-crate-name.rs b/tests/ui/extern-flag/invalid-crate-name.rs index c7b5b637217a2..5d73e11967363 100644 --- a/tests/ui/extern-flag/invalid-crate-name.rs +++ b/tests/ui/extern-flag/invalid-crate-name.rs @@ -1,4 +1,5 @@ //@ compile-flags: --extern=?#1%$ -//@ error-pattern: crate name `?#1%$` passed to `--extern` is not a valid ASCII identifier fn main() {} + +//~? ERROR crate name `?#1%$` passed to `--extern` is not a valid ASCII identifier diff --git a/tests/ui/extern-flag/no-force-extern.rs b/tests/ui/extern-flag/no-force-extern.rs index 11d2f91c7bbf9..c9317abe292ca 100644 --- a/tests/ui/extern-flag/no-force-extern.rs +++ b/tests/ui/extern-flag/no-force-extern.rs @@ -1,10 +1,12 @@ //@ aux-crate:panic_handler=panic_handler.rs //@ ignore-cross-compile (needs dylibs and compiletest doesn't have a more specific header) // compile_flags: -Zunstable-options --crate-type dylib -//@ error-pattern: `#[panic_handler]` function required, but not found //@ dont-check-compiler-stderr //@ edition: 2018 #![no_std] -fn foo() {} +fn foo() {} //~ ERROR `main` function not found in crate `no_force_extern` + +//~? ERROR `#[panic_handler]` function required, but not found +//~? ERROR unwinding panics are not supported without std diff --git a/tests/ui/extern/extern-empty-string-issue-140884.rs b/tests/ui/extern/extern-empty-string-issue-140884.rs new file mode 100644 index 0000000000000..10291513d34d2 --- /dev/null +++ b/tests/ui/extern/extern-empty-string-issue-140884.rs @@ -0,0 +1,3 @@ +extern "" {} //~ ERROR invalid ABI: found `` + +fn main() {} diff --git a/tests/ui/extern/extern-empty-string-issue-140884.stderr b/tests/ui/extern/extern-empty-string-issue-140884.stderr new file mode 100644 index 0000000000000..accae0c0f7c73 --- /dev/null +++ b/tests/ui/extern/extern-empty-string-issue-140884.stderr @@ -0,0 +1,15 @@ +error[E0703]: invalid ABI: found `` + --> $DIR/extern-empty-string-issue-140884.rs:1:8 + | +LL | extern "" {} + | ^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions +help: there's a similarly named valid ABI `C` + | +LL | extern "C" {} + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0703`. diff --git a/tests/ui/extern/extern-main-issue-86110.rs b/tests/ui/extern/extern-main-issue-86110.rs index 83af7a14ccc7a..007d137170409 100644 --- a/tests/ui/extern/extern-main-issue-86110.rs +++ b/tests/ui/extern/extern-main-issue-86110.rs @@ -2,6 +2,6 @@ extern "C" { fn missing(); fn main(); - //~^ the `main` function cannot be declared in an `extern` block + //~^ ERROR the `main` function cannot be declared in an `extern` block fn missing2(); } diff --git a/tests/ui/extern/extern-with-type-bounds.rs b/tests/ui/extern/extern-with-type-bounds.rs deleted file mode 100644 index 3fbddfc99a6fa..0000000000000 --- a/tests/ui/extern/extern-with-type-bounds.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![feature(intrinsics, rustc_attrs)] - -// Intrinsics are the only (?) extern blocks supporting generics. -// Once intrinsics have to be declared via `#[rustc_intrinsic]`, -// the entire support for generics in extern fn can probably be removed. - -extern "rust-intrinsic" { - // Silent bounds made explicit to make sure they are actually - // resolved. - fn transmute(val: T) -> U; - - // Bounds aren't checked right now, so this should work - // even though it's incorrect. - fn size_of_val(x: *const T) -> usize; - - // Unresolved bounds should still error. - fn align_of() -> usize; - //~^ ERROR cannot find trait `NoSuchTrait` in this scope -} - -fn main() {} diff --git a/tests/ui/extern/extern-with-type-bounds.stderr b/tests/ui/extern/extern-with-type-bounds.stderr deleted file mode 100644 index 893947e831fd7..0000000000000 --- a/tests/ui/extern/extern-with-type-bounds.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0405]: cannot find trait `NoSuchTrait` in this scope - --> $DIR/extern-with-type-bounds.rs:17:20 - | -LL | fn align_of() -> usize; - | ^^^^^^^^^^^ not found in this scope - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0405`. diff --git a/tests/ui/extern/issue-112363-extern-item-where-clauses-debug-ice.rs b/tests/ui/extern/issue-112363-extern-item-where-clauses-debug-ice.rs index 17e08f511d71f..6a4b7814ef18d 100644 --- a/tests/ui/extern/issue-112363-extern-item-where-clauses-debug-ice.rs +++ b/tests/ui/extern/issue-112363-extern-item-where-clauses-debug-ice.rs @@ -1,10 +1,10 @@ extern "C" { type Item = [T] where [T]: Sized; - //~^ incorrect `type` inside `extern` block - //~| `type`s inside `extern` blocks cannot have `where` clauses - //~| cannot find type `T` in this scope - //~| cannot find type `T` in this scope - //~| extern types are experimental + //~^ ERROR incorrect `type` inside `extern` block + //~| ERROR `type`s inside `extern` blocks cannot have `where` clauses + //~| ERROR cannot find type `T` in this scope + //~| ERROR cannot find type `T` in this scope + //~| ERROR extern types are experimental } fn main() {} diff --git a/tests/ui/extern/issue-28324.stderr b/tests/ui/extern/issue-28324.stderr index 1fccb34fdf37b..93eb6ff8174e9 100644 --- a/tests/ui/extern/issue-28324.stderr +++ b/tests/ui/extern/issue-28324.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/issue-28324.rs:5:23 | LL | pub static BAZ: u32 = *&error_message_count; - | ^^^^^^^^^^^^^^^^^^^^^ cannot access extern static (DefId(0:4 ~ issue_28324[8ec4]::{extern#0}::error_message_count)) + | ^^^^^^^^^^^^^^^^^^^^^ cannot access extern static `error_message_count` error[E0133]: use of extern static is unsafe and requires unsafe function or block --> $DIR/issue-28324.rs:5:25 diff --git a/tests/ui/extern/issue-36122-accessing-externed-dst.stderr b/tests/ui/extern/issue-36122-accessing-externed-dst.stderr index 64178e6f84390..6f805aec1df02 100644 --- a/tests/ui/extern/issue-36122-accessing-externed-dst.stderr +++ b/tests/ui/extern/issue-36122-accessing-externed-dst.stderr @@ -5,6 +5,7 @@ LL | static symbol: [usize]; | ^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[usize]` + = note: statics and constants must have a statically known size error[E0133]: use of extern static is unsafe and requires unsafe function or block --> $DIR/issue-36122-accessing-externed-dst.rs:5:20 diff --git a/tests/ui/fail-simple.stderr b/tests/ui/fail-simple.stderr deleted file mode 100644 index 50c350b3ef55e..0000000000000 --- a/tests/ui/fail-simple.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: no rules expected `@` - --> $DIR/fail-simple.rs:2:12 - | -LL | panic!(@); - | ^ no rules expected this token in macro call - | - = note: while trying to match end of macro - -error: aborting due to 1 previous error - diff --git a/tests/ui/feature-gates/bench.rs b/tests/ui/feature-gates/bench.rs index 12e646f7a323a..94c8992a8fe68 100644 --- a/tests/ui/feature-gates/bench.rs +++ b/tests/ui/feature-gates/bench.rs @@ -1,9 +1,7 @@ //@ edition:2018 #[bench] //~ ERROR use of unstable library feature `test` - //~| WARN this was previously accepted fn bench() {} use bench as _; //~ ERROR use of unstable library feature `test` - //~| WARN this was previously accepted fn main() {} diff --git a/tests/ui/feature-gates/bench.stderr b/tests/ui/feature-gates/bench.stderr index de78e863012dd..f5fc579b23be3 100644 --- a/tests/ui/feature-gates/bench.stderr +++ b/tests/ui/feature-gates/bench.stderr @@ -1,43 +1,23 @@ -error: use of unstable library feature `test`: `bench` is a part of custom test frameworks which are unstable +error[E0658]: use of unstable library feature `test`: `bench` is a part of custom test frameworks which are unstable --> $DIR/bench.rs:3:3 | LL | #[bench] | ^^^^^ | - = 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 #64266 - = note: `#[deny(soft_unstable)]` on by default + = note: see issue #50297 for more information + = help: add `#![feature(test)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: use of unstable library feature `test`: `bench` is a part of custom test frameworks which are unstable - --> $DIR/bench.rs:7:5 +error[E0658]: use of unstable library feature `test`: `bench` is a part of custom test frameworks which are unstable + --> $DIR/bench.rs:6:5 | LL | use bench as _; | ^^^^^ | - = 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 #64266 + = note: see issue #50297 for more information + = help: add `#![feature(test)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 2 previous errors -Future incompatibility report: Future breakage diagnostic: -error: use of unstable library feature `test`: `bench` is a part of custom test frameworks which are unstable - --> $DIR/bench.rs:3:3 - | -LL | #[bench] - | ^^^^^ - | - = 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 #64266 - = note: `#[deny(soft_unstable)]` on by default - -Future breakage diagnostic: -error: use of unstable library feature `test`: `bench` is a part of custom test frameworks which are unstable - --> $DIR/bench.rs:7:5 - | -LL | use bench as _; - | ^^^^^ - | - = 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 #64266 - = note: `#[deny(soft_unstable)]` on by default - +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-abi.rs b/tests/ui/feature-gates/feature-gate-abi.rs index 2af623734ee87..bafd3643788e0 100644 --- a/tests/ui/feature-gates/feature-gate-abi.rs +++ b/tests/ui/feature-gates/feature-gate-abi.rs @@ -8,19 +8,10 @@ extern crate minicore; use minicore::*; -// Functions -extern "rust-intrinsic" fn f1() {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail - //~^ ERROR intrinsic must be in -extern "rust-intrinsic" fn f2() {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail - //~^ ERROR intrinsic must be in extern "rust-call" fn f4(_: ()) {} //~ ERROR extern "rust-call" ABI is experimental and subject to change // Methods in trait definition trait Tr { - extern "rust-intrinsic" fn m1(); //~ ERROR extern "rust-intrinsic" ABI is an implementation detail - //~^ ERROR intrinsic must be in - extern "rust-intrinsic" fn m2(); //~ ERROR extern "rust-intrinsic" ABI is an implementation detail - //~^ ERROR intrinsic must be in extern "rust-call" fn m4(_: ()); //~ ERROR extern "rust-call" ABI is experimental and subject to change extern "rust-call" fn dm4(_: ()) {} //~ ERROR extern "rust-call" ABI is experimental and subject to change @@ -30,28 +21,16 @@ struct S; // Methods in trait impl impl Tr for S { - extern "rust-intrinsic" fn m1() {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail - //~^ ERROR intrinsic must be in - extern "rust-intrinsic" fn m2() {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail - //~^ ERROR intrinsic must be in extern "rust-call" fn m4(_: ()) {} //~ ERROR extern "rust-call" ABI is experimental and subject to change } // Methods in inherent impl impl S { - extern "rust-intrinsic" fn im1() {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail - //~^ ERROR intrinsic must be in - extern "rust-intrinsic" fn im2() {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail - //~^ ERROR intrinsic must be in extern "rust-call" fn im4(_: ()) {} //~ ERROR extern "rust-call" ABI is experimental and subject to change } // Function pointer types -type A1 = extern "rust-intrinsic" fn(); //~ ERROR extern "rust-intrinsic" ABI is an implementation detail -type A2 = extern "rust-intrinsic" fn(); //~ ERROR extern "rust-intrinsic" ABI is an implementation detail type A4 = extern "rust-call" fn(_: ()); //~ ERROR extern "rust-call" ABI is experimental and subject to change // Foreign modules -extern "rust-intrinsic" {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail -extern "rust-intrinsic" {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail extern "rust-call" {} //~ ERROR extern "rust-call" ABI is experimental and subject to change diff --git a/tests/ui/feature-gates/feature-gate-abi.stderr b/tests/ui/feature-gates/feature-gate-abi.stderr index a974c0099cbde..7897a60b34f44 100644 --- a/tests/ui/feature-gates/feature-gate-abi.stderr +++ b/tests/ui/feature-gates/feature-gate-abi.stderr @@ -1,23 +1,5 @@ -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-abi.rs:12:8 - | -LL | extern "rust-intrinsic" fn f1() {} - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-abi.rs:14:8 - | -LL | extern "rust-intrinsic" fn f2() {} - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error[E0658]: the extern "rust-call" ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:16:8 + --> $DIR/feature-gate-abi.rs:11:8 | LL | extern "rust-call" fn f4(_: ()) {} | ^^^^^^^^^^^ @@ -26,26 +8,8 @@ LL | extern "rust-call" fn f4(_: ()) {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-abi.rs:20:12 - | -LL | extern "rust-intrinsic" fn m1(); - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-abi.rs:22:12 - | -LL | extern "rust-intrinsic" fn m2(); - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error[E0658]: the extern "rust-call" ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:24:12 + --> $DIR/feature-gate-abi.rs:15:12 | LL | extern "rust-call" fn m4(_: ()); | ^^^^^^^^^^^ @@ -55,7 +19,7 @@ LL | extern "rust-call" fn m4(_: ()); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the extern "rust-call" ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:26:12 + --> $DIR/feature-gate-abi.rs:17:12 | LL | extern "rust-call" fn dm4(_: ()) {} | ^^^^^^^^^^^ @@ -64,26 +28,8 @@ LL | extern "rust-call" fn dm4(_: ()) {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-abi.rs:33:12 - | -LL | extern "rust-intrinsic" fn m1() {} - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-abi.rs:35:12 - | -LL | extern "rust-intrinsic" fn m2() {} - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error[E0658]: the extern "rust-call" ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:37:12 + --> $DIR/feature-gate-abi.rs:24:12 | LL | extern "rust-call" fn m4(_: ()) {} | ^^^^^^^^^^^ @@ -92,26 +38,8 @@ LL | extern "rust-call" fn m4(_: ()) {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-abi.rs:42:12 - | -LL | extern "rust-intrinsic" fn im1() {} - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-abi.rs:44:12 - | -LL | extern "rust-intrinsic" fn im2() {} - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error[E0658]: the extern "rust-call" ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:46:12 + --> $DIR/feature-gate-abi.rs:29:12 | LL | extern "rust-call" fn im4(_: ()) {} | ^^^^^^^^^^^ @@ -120,26 +48,8 @@ LL | extern "rust-call" fn im4(_: ()) {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-abi.rs:50:18 - | -LL | type A1 = extern "rust-intrinsic" fn(); - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-abi.rs:51:18 - | -LL | type A2 = extern "rust-intrinsic" fn(); - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error[E0658]: the extern "rust-call" ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:52:18 + --> $DIR/feature-gate-abi.rs:33:18 | LL | type A4 = extern "rust-call" fn(_: ()); | ^^^^^^^^^^^ @@ -148,26 +58,8 @@ LL | type A4 = extern "rust-call" fn(_: ()); = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-abi.rs:55:8 - | -LL | extern "rust-intrinsic" {} - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-abi.rs:56:8 - | -LL | extern "rust-intrinsic" {} - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error[E0658]: the extern "rust-call" ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:57:8 + --> $DIR/feature-gate-abi.rs:36:8 | LL | extern "rust-call" {} | ^^^^^^^^^^^ @@ -176,54 +68,6 @@ LL | extern "rust-call" {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:20:32 - | -LL | extern "rust-intrinsic" fn m1(); - | ^^ - -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:22:32 - | -LL | extern "rust-intrinsic" fn m2(); - | ^^ - -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:12:33 - | -LL | extern "rust-intrinsic" fn f1() {} - | ^^ - -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:14:33 - | -LL | extern "rust-intrinsic" fn f2() {} - | ^^ - -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:33:37 - | -LL | extern "rust-intrinsic" fn m1() {} - | ^^ - -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:35:37 - | -LL | extern "rust-intrinsic" fn m2() {} - | ^^ - -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:42:38 - | -LL | extern "rust-intrinsic" fn im1() {} - | ^^ - -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:44:38 - | -LL | extern "rust-intrinsic" fn im2() {} - | ^^ - -error: aborting due to 27 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.stderr b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.stderr index d8dbd94696eb7..aa9c67f0151fc 100644 --- a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.stderr +++ b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.stderr @@ -112,3 +112,14 @@ error: aborting due to 12 previous errors; 1 warning emitted Some errors have detailed explanations: E0570, E0658. For more information about an error, try `rustc --explain E0570`. +Future incompatibility report: Future breakage diagnostic: +warning: the calling convention "gpu-kernel" is not supported on this target + --> $DIR/feature-gate-abi_gpu_kernel.rs:37:11 + | +LL | type A1 = extern "gpu-kernel" fn(_: ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + diff --git a/tests/ui/feature-gates/feature-gate-apx-target-feature.rs b/tests/ui/feature-gates/feature-gate-apx-target-feature.rs new file mode 100644 index 0000000000000..a2ac4ac86ac0e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-apx-target-feature.rs @@ -0,0 +1,6 @@ +//@ only-x86_64 +#[target_feature(enable = "apxf")] +//~^ ERROR: currently unstable +unsafe fn foo() {} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-apx-target-feature.stderr b/tests/ui/feature-gates/feature-gate-apx-target-feature.stderr new file mode 100644 index 0000000000000..1999ab5353797 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-apx-target-feature.stderr @@ -0,0 +1,13 @@ +error[E0658]: the target feature `apxf` is currently unstable + --> $DIR/feature-gate-apx-target-feature.rs:2:18 + | +LL | #[target_feature(enable = "apxf")] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #139284 for more information + = help: add `#![feature(apx_target_feature)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-asm_goto.rs b/tests/ui/feature-gates/feature-gate-asm_goto.rs deleted file mode 100644 index beac4590349f6..0000000000000 --- a/tests/ui/feature-gates/feature-gate-asm_goto.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ only-x86_64 - -use std::arch::asm; - -fn main() { - unsafe { - asm!("jmp {}", label {}); - //~^ ERROR label operands for inline assembly are unstable - } -} diff --git a/tests/ui/feature-gates/feature-gate-asm_goto.stderr b/tests/ui/feature-gates/feature-gate-asm_goto.stderr deleted file mode 100644 index 62fd1a320d3c0..0000000000000 --- a/tests/ui/feature-gates/feature-gate-asm_goto.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: label operands for inline assembly are unstable - --> $DIR/feature-gate-asm_goto.rs:7:24 - | -LL | asm!("jmp {}", label {}); - | ^^^^^^^^ - | - = note: see issue #119364 for more information - = help: add `#![feature(asm_goto)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-asm_goto_with_outputs.rs b/tests/ui/feature-gates/feature-gate-asm_goto_with_outputs.rs index 294827f78d263..26b7d5d2fbfbb 100644 --- a/tests/ui/feature-gates/feature-gate-asm_goto_with_outputs.rs +++ b/tests/ui/feature-gates/feature-gate-asm_goto_with_outputs.rs @@ -1,7 +1,5 @@ //@ only-x86_64 -#![feature(asm_goto)] - use std::arch::asm; fn main() { diff --git a/tests/ui/feature-gates/feature-gate-asm_goto_with_outputs.stderr b/tests/ui/feature-gates/feature-gate-asm_goto_with_outputs.stderr index ff7a7d5760a7f..06e113015651b 100644 --- a/tests/ui/feature-gates/feature-gate-asm_goto_with_outputs.stderr +++ b/tests/ui/feature-gates/feature-gate-asm_goto_with_outputs.stderr @@ -1,5 +1,5 @@ error[E0658]: using both label and output operands for inline assembly is unstable - --> $DIR/feature-gate-asm_goto_with_outputs.rs:10:52 + --> $DIR/feature-gate-asm_goto_with_outputs.rs:8:52 | LL | asm!("mov {}, 1", "jmp {}", out(reg) _out, label {}); | ^^^^^^^^ diff --git a/tests/ui/feature-gates/feature-gate-async-drop.rs b/tests/ui/feature-gates/feature-gate-async-drop.rs new file mode 100644 index 0000000000000..20511b1d8ee86 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-async-drop.rs @@ -0,0 +1,16 @@ +//@ edition: 2021 + +use std::future::AsyncDrop; //~ ERROR use of unstable library feature `async_drop` +use std::pin::Pin; + +struct Foo {} + +impl Drop for Foo { + fn drop(&mut self) {} +} + +impl AsyncDrop for Foo { //~ ERROR use of unstable library feature `async_drop` + async fn drop(self: Pin<&mut Self>) {} //~ ERROR use of unstable library feature `async_drop` +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-async-drop.stderr b/tests/ui/feature-gates/feature-gate-async-drop.stderr new file mode 100644 index 0000000000000..e795c3a342269 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-async-drop.stderr @@ -0,0 +1,33 @@ +error[E0658]: use of unstable library feature `async_drop` + --> $DIR/feature-gate-async-drop.rs:3:5 + | +LL | use std::future::AsyncDrop; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #126482 for more information + = help: add `#![feature(async_drop)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `async_drop` + --> $DIR/feature-gate-async-drop.rs:13:5 + | +LL | async fn drop(self: Pin<&mut Self>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #126482 for more information + = help: add `#![feature(async_drop)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `async_drop` + --> $DIR/feature-gate-async-drop.rs:12:6 + | +LL | impl AsyncDrop for Foo { + | ^^^^^^^^^ + | + = note: see issue #126482 for more information + = help: add `#![feature(async_drop)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-avx10_target_feature.rs b/tests/ui/feature-gates/feature-gate-avx10_target_feature.rs new file mode 100644 index 0000000000000..8557e67d1f416 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-avx10_target_feature.rs @@ -0,0 +1,6 @@ +//@ only-x86_64 +#[target_feature(enable = "avx10.1")] +//~^ ERROR: currently unstable +unsafe fn foo() {} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-avx10_target_feature.stderr b/tests/ui/feature-gates/feature-gate-avx10_target_feature.stderr new file mode 100644 index 0000000000000..e45ea3524ca74 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-avx10_target_feature.stderr @@ -0,0 +1,13 @@ +error[E0658]: the target feature `avx10.1` is currently unstable + --> $DIR/feature-gate-avx10_target_feature.rs:2:18 + | +LL | #[target_feature(enable = "avx10.1")] + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #138843 for more information + = help: add `#![feature(avx10_target_feature)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.rs b/tests/ui/feature-gates/feature-gate-c_variadic.rs index 8b40c36c7db5e..f189f02a26ddf 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.rs +++ b/tests/ui/feature-gates/feature-gate-c_variadic.rs @@ -1,4 +1,4 @@ #![crate_type="lib"] pub unsafe extern "C" fn test(_: i32, ap: ...) { } -//~^ C-variadic functions are unstable +//~^ ERROR C-variadic functions are unstable diff --git a/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs b/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs deleted file mode 100644 index 6784b4450490b..0000000000000 --- a/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[cfg(true)] //~ ERROR `cfg(true)` is experimental -fn foo() {} - -#[cfg_attr(true, cfg(false))] //~ ERROR `cfg(true)` is experimental -//~^ ERROR `cfg(false)` is experimental -fn foo() {} - -fn main() { - cfg!(false); //~ ERROR `cfg(false)` is experimental -} diff --git a/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr b/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr deleted file mode 100644 index 64491464f1d43..0000000000000 --- a/tests/ui/feature-gates/feature-gate-cfg-boolean-literals.stderr +++ /dev/null @@ -1,43 +0,0 @@ -error[E0658]: `cfg(true)` is experimental and subject to change - --> $DIR/feature-gate-cfg-boolean-literals.rs:1:7 - | -LL | #[cfg(true)] - | ^^^^ - | - = note: see issue #131204 for more information - = help: add `#![feature(cfg_boolean_literals)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `cfg(true)` is experimental and subject to change - --> $DIR/feature-gate-cfg-boolean-literals.rs:4:12 - | -LL | #[cfg_attr(true, cfg(false))] - | ^^^^ - | - = note: see issue #131204 for more information - = help: add `#![feature(cfg_boolean_literals)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `cfg(false)` is experimental and subject to change - --> $DIR/feature-gate-cfg-boolean-literals.rs:4:22 - | -LL | #[cfg_attr(true, cfg(false))] - | ^^^^^ - | - = note: see issue #131204 for more information - = help: add `#![feature(cfg_boolean_literals)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `cfg(false)` is experimental and subject to change - --> $DIR/feature-gate-cfg-boolean-literals.rs:9:10 - | -LL | cfg!(false); - | ^^^^^ - | - = note: see issue #131204 for more information - = help: add `#![feature(cfg_boolean_literals)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.rs b/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.rs index cff98b43fe745..232061e239c93 100644 --- a/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.rs +++ b/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.rs @@ -1,4 +1,4 @@ //@ compile-flags: --check-cfg=cfg(emscripten_wasm_eh) #[cfg(not(emscripten_wasm_eh))] -//~^ `cfg(emscripten_wasm_eh)` is experimental +//~^ ERROR `cfg(emscripten_wasm_eh)` is experimental fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-cfg-sanitizer_cfi.rs b/tests/ui/feature-gates/feature-gate-cfg-sanitizer_cfi.rs index 76d96de750a62..1419a978138aa 100644 --- a/tests/ui/feature-gates/feature-gate-cfg-sanitizer_cfi.rs +++ b/tests/ui/feature-gates/feature-gate-cfg-sanitizer_cfi.rs @@ -1,9 +1,9 @@ #[cfg(sanitizer_cfi_generalize_pointers)] -//~^ `cfg(sanitizer_cfi_generalize_pointers)` is experimental +//~^ ERROR `cfg(sanitizer_cfi_generalize_pointers)` is experimental fn foo() {} #[cfg(sanitizer_cfi_normalize_integers)] -//~^ `cfg(sanitizer_cfi_normalize_integers)` is experimental +//~^ ERROR `cfg(sanitizer_cfi_normalize_integers)` is experimental fn bar() {} fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16-f128.rs b/tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16-f128.rs new file mode 100644 index 0000000000000..f1b0e3b01a423 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16-f128.rs @@ -0,0 +1,12 @@ +//@ compile-flags: --check-cfg=cfg(target_has_reliable_f16,target_has_reliable_f16_math,target_has_reliable_f128,target_has_reliable_f128_math) + +fn main() { + cfg!(target_has_reliable_f16); + //~^ ERROR `cfg(target_has_reliable_f16)` is experimental and subject to change + cfg!(target_has_reliable_f16_math); + //~^ ERROR `cfg(target_has_reliable_f16_math)` is experimental and subject to change + cfg!(target_has_reliable_f128); + //~^ ERROR `cfg(target_has_reliable_f128)` is experimental and subject to change + cfg!(target_has_reliable_f128_math); + //~^ ERROR `cfg(target_has_reliable_f128_math)` is experimental and subject to change +} diff --git a/tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16-f128.stderr b/tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16-f128.stderr new file mode 100644 index 0000000000000..9b90d18699e6f --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16-f128.stderr @@ -0,0 +1,39 @@ +error[E0658]: `cfg(target_has_reliable_f16)` is experimental and subject to change + --> $DIR/feature-gate-cfg-target-has-reliable-f16-f128.rs:4:10 + | +LL | cfg!(target_has_reliable_f16); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(cfg_target_has_reliable_f16_f128)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `cfg(target_has_reliable_f16_math)` is experimental and subject to change + --> $DIR/feature-gate-cfg-target-has-reliable-f16-f128.rs:6:10 + | +LL | cfg!(target_has_reliable_f16_math); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(cfg_target_has_reliable_f16_f128)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `cfg(target_has_reliable_f128)` is experimental and subject to change + --> $DIR/feature-gate-cfg-target-has-reliable-f16-f128.rs:8:10 + | +LL | cfg!(target_has_reliable_f128); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(cfg_target_has_reliable_f16_f128)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `cfg(target_has_reliable_f128_math)` is experimental and subject to change + --> $DIR/feature-gate-cfg-target-has-reliable-f16-f128.rs:10:10 + | +LL | cfg!(target_has_reliable_f128_math); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(cfg_target_has_reliable_f16_f128)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-cfg-target-thread-local.rs b/tests/ui/feature-gates/feature-gate-cfg-target-thread-local.rs index 131a10ea03b1e..a9d4f6fe04f22 100644 --- a/tests/ui/feature-gates/feature-gate-cfg-target-thread-local.rs +++ b/tests/ui/feature-gates/feature-gate-cfg-target-thread-local.rs @@ -7,7 +7,7 @@ extern crate cfg_target_thread_local; extern "C" { #[cfg_attr(target_thread_local, thread_local)] - //~^ `cfg(target_thread_local)` is experimental and subject to change + //~^ ERROR `cfg(target_thread_local)` is experimental and subject to change static FOO: u32; } diff --git a/tests/ui/feature-gates/feature-gate-cfg_sanitize.rs b/tests/ui/feature-gates/feature-gate-cfg_sanitize.rs index c3e7cc9ed8a9b..0933f43e76b6e 100644 --- a/tests/ui/feature-gates/feature-gate-cfg_sanitize.rs +++ b/tests/ui/feature-gates/feature-gate-cfg_sanitize.rs @@ -1,3 +1,3 @@ #[cfg(not(sanitize = "thread"))] -//~^ `cfg(sanitize)` is experimental +//~^ ERROR `cfg(sanitize)` is experimental fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-closure_track_caller.rs b/tests/ui/feature-gates/feature-gate-closure_track_caller.rs index d90fb765a237a..d7cfc13cae559 100644 --- a/tests/ui/feature-gates/feature-gate-closure_track_caller.rs +++ b/tests/ui/feature-gates/feature-gate-closure_track_caller.rs @@ -3,7 +3,7 @@ #![feature(coroutines)] fn main() { - let _closure = #[track_caller] || {}; //~ `#[track_caller]` on closures - let _coroutine = #[coroutine] #[track_caller] || { yield; }; //~ `#[track_caller]` on closures - let _future = #[track_caller] async {}; //~ `#[track_caller]` on closures + let _closure = #[track_caller] || {}; //~ ERROR `#[track_caller]` on closures + let _coroutine = #[coroutine] #[track_caller] || { yield; }; //~ ERROR `#[track_caller]` on closures + let _future = #[track_caller] async {}; //~ ERROR `#[track_caller]` on closures } diff --git a/tests/ui/feature-gates/feature-gate-concat_idents.rs b/tests/ui/feature-gates/feature-gate-concat_idents.rs index 68caf3d71e907..4fc3b69159733 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents.rs +++ b/tests/ui/feature-gates/feature-gate-concat_idents.rs @@ -1,3 +1,5 @@ +#![expect(deprecated)] // concat_idents is deprecated + const XY_1: i32 = 10; fn main() { diff --git a/tests/ui/feature-gates/feature-gate-concat_idents.stderr b/tests/ui/feature-gates/feature-gate-concat_idents.stderr index d0f4fe62d048f..6399424eecd8b 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents.stderr +++ b/tests/ui/feature-gates/feature-gate-concat_idents.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents.rs:5:13 + --> $DIR/feature-gate-concat_idents.rs:7:13 | LL | let a = concat_idents!(X, Y_1); | ^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let a = concat_idents!(X, Y_1); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents.rs:6:13 + --> $DIR/feature-gate-concat_idents.rs:8:13 | LL | let b = concat_idents!(X, Y_2); | ^^^^^^^^^^^^^ diff --git a/tests/ui/feature-gates/feature-gate-concat_idents2.rs b/tests/ui/feature-gates/feature-gate-concat_idents2.rs index 9660ffeafa518..bc2b4f7cddf99 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents2.rs +++ b/tests/ui/feature-gates/feature-gate-concat_idents2.rs @@ -1,3 +1,5 @@ +#![expect(deprecated)] // concat_idents is deprecated + fn main() { concat_idents!(a, b); //~ ERROR `concat_idents` is not stable enough //~| ERROR cannot find value `ab` in this scope diff --git a/tests/ui/feature-gates/feature-gate-concat_idents2.stderr b/tests/ui/feature-gates/feature-gate-concat_idents2.stderr index 2052813ea4ac6..a770c1a348b5d 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents2.stderr +++ b/tests/ui/feature-gates/feature-gate-concat_idents2.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents2.rs:2:5 + --> $DIR/feature-gate-concat_idents2.rs:4:5 | LL | concat_idents!(a, b); | ^^^^^^^^^^^^^ @@ -9,12 +9,10 @@ LL | concat_idents!(a, b); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0425]: cannot find value `ab` in this scope - --> $DIR/feature-gate-concat_idents2.rs:2:5 + --> $DIR/feature-gate-concat_idents2.rs:4:5 | LL | concat_idents!(a, b); | ^^^^^^^^^^^^^^^^^^^^ not found in this scope - | - = note: this error originates in the macro `concat_idents` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/feature-gates/feature-gate-concat_idents3.rs b/tests/ui/feature-gates/feature-gate-concat_idents3.rs index 81710fd9fb041..d4a0d2e6bb0e4 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents3.rs +++ b/tests/ui/feature-gates/feature-gate-concat_idents3.rs @@ -1,3 +1,5 @@ +#![expect(deprecated)] // concat_idents is deprecated + const XY_1: i32 = 10; fn main() { diff --git a/tests/ui/feature-gates/feature-gate-concat_idents3.stderr b/tests/ui/feature-gates/feature-gate-concat_idents3.stderr index b186601d0ed68..7d929322bc06e 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents3.stderr +++ b/tests/ui/feature-gates/feature-gate-concat_idents3.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents3.rs:5:20 + --> $DIR/feature-gate-concat_idents3.rs:7:20 | LL | assert_eq!(10, concat_idents!(X, Y_1)); | ^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | assert_eq!(10, concat_idents!(X, Y_1)); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents3.rs:6:20 + --> $DIR/feature-gate-concat_idents3.rs:8:20 | LL | assert_eq!(20, concat_idents!(X, Y_2)); | ^^^^^^^^^^^^^ diff --git a/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr b/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr index 032d7adf77ab8..381e7a210be06 100644 --- a/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr +++ b/tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr @@ -45,7 +45,7 @@ LL | yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks @@ -66,7 +66,7 @@ LL | let _ = || yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks diff --git a/tests/ui/feature-gates/feature-gate-coroutines.none.stderr b/tests/ui/feature-gates/feature-gate-coroutines.none.stderr index 032d7adf77ab8..381e7a210be06 100644 --- a/tests/ui/feature-gates/feature-gate-coroutines.none.stderr +++ b/tests/ui/feature-gates/feature-gate-coroutines.none.stderr @@ -45,7 +45,7 @@ LL | yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks @@ -66,7 +66,7 @@ LL | let _ = || yield true; | ^^^^^^^^^^ | = note: see issue #43122 for more information - = help: add `#![feature(coroutines)]` to the crate attributes to enable + = help: add `#![feature(yield_expr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks diff --git a/tests/ui/feature-gates/feature-gate-coroutines.rs b/tests/ui/feature-gates/feature-gate-coroutines.rs index f20dc56f12293..b37a61d9105f1 100644 --- a/tests/ui/feature-gates/feature-gate-coroutines.rs +++ b/tests/ui/feature-gates/feature-gate-coroutines.rs @@ -12,7 +12,7 @@ fn main() { //~^^ ERROR `yield` can only be used } -#[cfg(FALSE)] +#[cfg(false)] fn foo() { // Ok in 2024 edition yield; //~ ERROR yield syntax is experimental diff --git a/tests/ui/feature-gates/feature-gate-custom_mir.rs b/tests/ui/feature-gates/feature-gate-custom_mir.rs index e100df08ee70d..4d713c524b335 100644 --- a/tests/ui/feature-gates/feature-gate-custom_mir.rs +++ b/tests/ui/feature-gates/feature-gate-custom_mir.rs @@ -1,13 +1,13 @@ #![feature(core_intrinsics)] extern crate core; -use core::intrinsics::mir::*; //~ custom_mir +use core::intrinsics::mir::*; //~ ERROR custom_mir #[custom_mir(dialect = "built")] //~ ERROR the `#[custom_mir]` attribute is just used for the Rust test suite pub fn foo(_x: i32) -> i32 { mir! { { - Return() //~ custom_mir + Return() //~ ERROR custom_mir } } } diff --git a/tests/ui/feature-gates/feature-gate-deref_patterns.rs b/tests/ui/feature-gates/feature-gate-deref_patterns.rs index b43001f2d53fa..53b4301f10c0c 100644 --- a/tests/ui/feature-gates/feature-gate-deref_patterns.rs +++ b/tests/ui/feature-gates/feature-gate-deref_patterns.rs @@ -4,6 +4,6 @@ fn main() { println!("x: {}", x); // `box` syntax is allowed to be cfg-ed out for historical reasons (#65742). - #[cfg(FALSE)] + #[cfg(false)] let box _x = Box::new('c'); } diff --git a/tests/ui/feature-gates/feature-gate-ergonomic-clones.rs b/tests/ui/feature-gates/feature-gate-ergonomic-clones.rs new file mode 100644 index 0000000000000..c2e44064cfad7 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-ergonomic-clones.rs @@ -0,0 +1,32 @@ +use std::clone::UseCloned; +//~^ ERROR use of unstable library feature `ergonomic_clones` [E0658] + +fn ergonomic_clone(x: i32) -> i32 { + x.use + //~^ ERROR ergonomic clones are experimental [E0658] +} + +#[derive(Clone)] +struct Foo; + +fn foo() {} +//~^ ERROR use of unstable library feature `ergonomic_clones` [E0658] + +impl UseCloned for Foo {} +//~^ ERROR use of unstable library feature `ergonomic_clones` [E0658] + +fn ergonomic_closure_clone() { + let f1 = Foo; + + let f2 = use || { + //~^ ERROR ergonomic clones are experimental [E0658] + f1 + }; + + let f3 = use || { + //~^ ERROR ergonomic clones are experimental [E0658] + f1 + }; +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr b/tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr new file mode 100644 index 0000000000000..cf92f2f28df80 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr @@ -0,0 +1,63 @@ +error[E0658]: ergonomic clones are experimental + --> $DIR/feature-gate-ergonomic-clones.rs:5:7 + | +LL | x.use + | ^^^ + | + = note: see issue #132290 for more information + = help: add `#![feature(ergonomic_clones)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: ergonomic clones are experimental + --> $DIR/feature-gate-ergonomic-clones.rs:21:14 + | +LL | let f2 = use || { + | ^^^ + | + = note: see issue #132290 for more information + = help: add `#![feature(ergonomic_clones)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: ergonomic clones are experimental + --> $DIR/feature-gate-ergonomic-clones.rs:26:14 + | +LL | let f3 = use || { + | ^^^ + | + = note: see issue #132290 for more information + = help: add `#![feature(ergonomic_clones)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `ergonomic_clones` + --> $DIR/feature-gate-ergonomic-clones.rs:1:5 + | +LL | use std::clone::UseCloned; + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #132290 for more information + = help: add `#![feature(ergonomic_clones)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `ergonomic_clones` + --> $DIR/feature-gate-ergonomic-clones.rs:12:11 + | +LL | fn foo() {} + | ^^^^^^^^^ + | + = note: see issue #132290 for more information + = help: add `#![feature(ergonomic_clones)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `ergonomic_clones` + --> $DIR/feature-gate-ergonomic-clones.rs:15:6 + | +LL | impl UseCloned for Foo {} + | ^^^^^^^^^ + | + = note: see issue #132290 for more information + = help: add `#![feature(ergonomic_clones)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.fixed b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.fixed new file mode 100644 index 0000000000000..525f78d162fc0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.fixed @@ -0,0 +1,45 @@ +// The purpose of this feature gate is to make something into a hard error in a +// future edition. Consequently, this test differs from most other feature gate +// tests. Instead of verifying that an error occurs when the feature gate is +// missing, it ensures that the hard error is only produced with the feature +// gate is present in the `future` edition -- and otherwise that only a warning +// is emitted. + +//@ revisions: current current_feature future future_feature + +//@ [current] run-rustfix +//@ [current] check-pass + +//@ [current_feature] run-rustfix +//@ [current_feature] check-pass + +//@ [future] edition: future +//@ [future] compile-flags: -Z unstable-options +//@ [future] run-rustfix +//@ [future] check-pass + +//@ [future_feature] edition: future +//@ [future_feature] compile-flags: -Z unstable-options + +#![cfg_attr(future_feature, feature(explicit_extern_abis))] +#![cfg_attr(current_feature, feature(explicit_extern_abis))] + +extern "C" fn _foo() {} +//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated +//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed + +unsafe extern "C" fn _bar() {} +//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated +//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed + +unsafe extern "C" {} +//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated +//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.stderr b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.stderr new file mode 100644 index 0000000000000..cf927807c7c3c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current.stderr @@ -0,0 +1,22 @@ +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/feature-gate-explicit-extern-abis.rs:27:1 + | +LL | extern fn _foo() {} + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + | + = note: `#[warn(missing_abi)]` on by default + +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/feature-gate-explicit-extern-abis.rs:33:8 + | +LL | unsafe extern fn _bar() {} + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/feature-gate-explicit-extern-abis.rs:39:8 + | +LL | unsafe extern {} + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + +warning: 3 warnings emitted + diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.fixed b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.fixed new file mode 100644 index 0000000000000..525f78d162fc0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.fixed @@ -0,0 +1,45 @@ +// The purpose of this feature gate is to make something into a hard error in a +// future edition. Consequently, this test differs from most other feature gate +// tests. Instead of verifying that an error occurs when the feature gate is +// missing, it ensures that the hard error is only produced with the feature +// gate is present in the `future` edition -- and otherwise that only a warning +// is emitted. + +//@ revisions: current current_feature future future_feature + +//@ [current] run-rustfix +//@ [current] check-pass + +//@ [current_feature] run-rustfix +//@ [current_feature] check-pass + +//@ [future] edition: future +//@ [future] compile-flags: -Z unstable-options +//@ [future] run-rustfix +//@ [future] check-pass + +//@ [future_feature] edition: future +//@ [future_feature] compile-flags: -Z unstable-options + +#![cfg_attr(future_feature, feature(explicit_extern_abis))] +#![cfg_attr(current_feature, feature(explicit_extern_abis))] + +extern "C" fn _foo() {} +//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated +//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed + +unsafe extern "C" fn _bar() {} +//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated +//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed + +unsafe extern "C" {} +//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated +//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.stderr b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.stderr new file mode 100644 index 0000000000000..cf927807c7c3c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.current_feature.stderr @@ -0,0 +1,22 @@ +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/feature-gate-explicit-extern-abis.rs:27:1 + | +LL | extern fn _foo() {} + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + | + = note: `#[warn(missing_abi)]` on by default + +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/feature-gate-explicit-extern-abis.rs:33:8 + | +LL | unsafe extern fn _bar() {} + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/feature-gate-explicit-extern-abis.rs:39:8 + | +LL | unsafe extern {} + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + +warning: 3 warnings emitted + diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.fixed b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.fixed new file mode 100644 index 0000000000000..525f78d162fc0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.fixed @@ -0,0 +1,45 @@ +// The purpose of this feature gate is to make something into a hard error in a +// future edition. Consequently, this test differs from most other feature gate +// tests. Instead of verifying that an error occurs when the feature gate is +// missing, it ensures that the hard error is only produced with the feature +// gate is present in the `future` edition -- and otherwise that only a warning +// is emitted. + +//@ revisions: current current_feature future future_feature + +//@ [current] run-rustfix +//@ [current] check-pass + +//@ [current_feature] run-rustfix +//@ [current_feature] check-pass + +//@ [future] edition: future +//@ [future] compile-flags: -Z unstable-options +//@ [future] run-rustfix +//@ [future] check-pass + +//@ [future_feature] edition: future +//@ [future_feature] compile-flags: -Z unstable-options + +#![cfg_attr(future_feature, feature(explicit_extern_abis))] +#![cfg_attr(current_feature, feature(explicit_extern_abis))] + +extern "C" fn _foo() {} +//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated +//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed + +unsafe extern "C" fn _bar() {} +//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated +//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed + +unsafe extern "C" {} +//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated +//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.stderr b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.stderr new file mode 100644 index 0000000000000..cf927807c7c3c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future.stderr @@ -0,0 +1,22 @@ +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/feature-gate-explicit-extern-abis.rs:27:1 + | +LL | extern fn _foo() {} + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + | + = note: `#[warn(missing_abi)]` on by default + +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/feature-gate-explicit-extern-abis.rs:33:8 + | +LL | unsafe extern fn _bar() {} + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/feature-gate-explicit-extern-abis.rs:39:8 + | +LL | unsafe extern {} + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + +warning: 3 warnings emitted + diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future_feature.stderr b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future_feature.stderr new file mode 100644 index 0000000000000..096a6f4341699 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.future_feature.stderr @@ -0,0 +1,26 @@ +error: `extern` declarations without an explicit ABI are disallowed + --> $DIR/feature-gate-explicit-extern-abis.rs:27:1 + | +LL | extern fn _foo() {} + | ^^^^^^ help: specify an ABI: `extern ""` + | + = help: prior to Rust 2024, a default ABI was inferred + +error: `extern` declarations without an explicit ABI are disallowed + --> $DIR/feature-gate-explicit-extern-abis.rs:33:8 + | +LL | unsafe extern fn _bar() {} + | ^^^^^^ help: specify an ABI: `extern ""` + | + = help: prior to Rust 2024, a default ABI was inferred + +error: `extern` declarations without an explicit ABI are disallowed + --> $DIR/feature-gate-explicit-extern-abis.rs:39:8 + | +LL | unsafe extern {} + | ^^^^^^ help: specify an ABI: `extern ""` + | + = help: prior to Rust 2024, a default ABI was inferred + +error: aborting due to 3 previous errors + diff --git a/tests/ui/feature-gates/feature-gate-explicit-extern-abis.rs b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.rs new file mode 100644 index 0000000000000..379c45f589907 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-explicit-extern-abis.rs @@ -0,0 +1,45 @@ +// The purpose of this feature gate is to make something into a hard error in a +// future edition. Consequently, this test differs from most other feature gate +// tests. Instead of verifying that an error occurs when the feature gate is +// missing, it ensures that the hard error is only produced with the feature +// gate is present in the `future` edition -- and otherwise that only a warning +// is emitted. + +//@ revisions: current current_feature future future_feature + +//@ [current] run-rustfix +//@ [current] check-pass + +//@ [current_feature] run-rustfix +//@ [current_feature] check-pass + +//@ [future] edition: future +//@ [future] compile-flags: -Z unstable-options +//@ [future] run-rustfix +//@ [future] check-pass + +//@ [future_feature] edition: future +//@ [future_feature] compile-flags: -Z unstable-options + +#![cfg_attr(future_feature, feature(explicit_extern_abis))] +#![cfg_attr(current_feature, feature(explicit_extern_abis))] + +extern fn _foo() {} +//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated +//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed + +unsafe extern fn _bar() {} +//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated +//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed + +unsafe extern {} +//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated +//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated +//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-export_stable.rs b/tests/ui/feature-gates/feature-gate-export_stable.rs new file mode 100644 index 0000000000000..5d05fee059b82 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-export_stable.rs @@ -0,0 +1,5 @@ +#![crate_type="lib"] + +#[export_stable] +//~^ ERROR the `#[export_stable]` attribute is an experimental feature +pub mod a {} diff --git a/tests/ui/feature-gates/feature-gate-export_stable.stderr b/tests/ui/feature-gates/feature-gate-export_stable.stderr new file mode 100644 index 0000000000000..6beb52a77e5cc --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-export_stable.stderr @@ -0,0 +1,13 @@ +error[E0658]: the `#[export_stable]` attribute is an experimental feature + --> $DIR/feature-gate-export_stable.rs:3:1 + | +LL | #[export_stable] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #139939 for more information + = help: add `#![feature(export_stable)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-f128.e2015.stderr b/tests/ui/feature-gates/feature-gate-f128.e2015.stderr index 771aee79dce16..7e8ea5e948dca 100644 --- a/tests/ui/feature-gates/feature-gate-f128.e2015.stderr +++ b/tests/ui/feature-gates/feature-gate-f128.e2015.stderr @@ -19,7 +19,7 @@ LL | let a: f128 = 100.0; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f128` is unstable - --> $DIR/feature-gate-f128.rs:15:11 + --> $DIR/feature-gate-f128.rs:16:11 | LL | fn foo(a: f128) {} | ^^^^ @@ -29,7 +29,7 @@ LL | fn foo(a: f128) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f128` is unstable - --> $DIR/feature-gate-f128.rs:18:8 + --> $DIR/feature-gate-f128.rs:19:8 | LL | a: f128, | ^^^^ @@ -48,6 +48,16 @@ LL | let b = 0.0f128; = help: add `#![feature(f128)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 5 previous errors +error[E0658]: the type `f128` is unstable + --> $DIR/feature-gate-f128.rs:12:13 + | +LL | let c = 0f128; + | ^^^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f128)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-f128.e2018.stderr b/tests/ui/feature-gates/feature-gate-f128.e2018.stderr index 771aee79dce16..7e8ea5e948dca 100644 --- a/tests/ui/feature-gates/feature-gate-f128.e2018.stderr +++ b/tests/ui/feature-gates/feature-gate-f128.e2018.stderr @@ -19,7 +19,7 @@ LL | let a: f128 = 100.0; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f128` is unstable - --> $DIR/feature-gate-f128.rs:15:11 + --> $DIR/feature-gate-f128.rs:16:11 | LL | fn foo(a: f128) {} | ^^^^ @@ -29,7 +29,7 @@ LL | fn foo(a: f128) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f128` is unstable - --> $DIR/feature-gate-f128.rs:18:8 + --> $DIR/feature-gate-f128.rs:19:8 | LL | a: f128, | ^^^^ @@ -48,6 +48,16 @@ LL | let b = 0.0f128; = help: add `#![feature(f128)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 5 previous errors +error[E0658]: the type `f128` is unstable + --> $DIR/feature-gate-f128.rs:12:13 + | +LL | let c = 0f128; + | ^^^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f128)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-f128.rs b/tests/ui/feature-gates/feature-gate-f128.rs index d25b6dde4ee47..dcbe60e934ae6 100644 --- a/tests/ui/feature-gates/feature-gate-f128.rs +++ b/tests/ui/feature-gates/feature-gate-f128.rs @@ -9,6 +9,7 @@ const A: f128 = 10.0; //~ ERROR the type `f128` is unstable pub fn main() { let a: f128 = 100.0; //~ ERROR the type `f128` is unstable let b = 0.0f128; //~ ERROR the type `f128` is unstable + let c = 0f128; //~ ERROR the type `f128` is unstable foo(1.23); } diff --git a/tests/ui/feature-gates/feature-gate-f16.e2015.stderr b/tests/ui/feature-gates/feature-gate-f16.e2015.stderr index 2bb3b59465a08..5d1ca8f6d047a 100644 --- a/tests/ui/feature-gates/feature-gate-f16.e2015.stderr +++ b/tests/ui/feature-gates/feature-gate-f16.e2015.stderr @@ -19,7 +19,7 @@ LL | let a: f16 = 100.0; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f16` is unstable - --> $DIR/feature-gate-f16.rs:15:11 + --> $DIR/feature-gate-f16.rs:16:11 | LL | fn foo(a: f16) {} | ^^^ @@ -29,7 +29,7 @@ LL | fn foo(a: f16) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f16` is unstable - --> $DIR/feature-gate-f16.rs:18:8 + --> $DIR/feature-gate-f16.rs:19:8 | LL | a: f16, | ^^^ @@ -48,6 +48,16 @@ LL | let b = 0.0f16; = help: add `#![feature(f16)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 5 previous errors +error[E0658]: the type `f16` is unstable + --> $DIR/feature-gate-f16.rs:12:13 + | +LL | let c = 0f16; + | ^^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f16)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-f16.e2018.stderr b/tests/ui/feature-gates/feature-gate-f16.e2018.stderr index 2bb3b59465a08..5d1ca8f6d047a 100644 --- a/tests/ui/feature-gates/feature-gate-f16.e2018.stderr +++ b/tests/ui/feature-gates/feature-gate-f16.e2018.stderr @@ -19,7 +19,7 @@ LL | let a: f16 = 100.0; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f16` is unstable - --> $DIR/feature-gate-f16.rs:15:11 + --> $DIR/feature-gate-f16.rs:16:11 | LL | fn foo(a: f16) {} | ^^^ @@ -29,7 +29,7 @@ LL | fn foo(a: f16) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: the type `f16` is unstable - --> $DIR/feature-gate-f16.rs:18:8 + --> $DIR/feature-gate-f16.rs:19:8 | LL | a: f16, | ^^^ @@ -48,6 +48,16 @@ LL | let b = 0.0f16; = help: add `#![feature(f16)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 5 previous errors +error[E0658]: the type `f16` is unstable + --> $DIR/feature-gate-f16.rs:12:13 + | +LL | let c = 0f16; + | ^^^^ + | + = note: see issue #116909 for more information + = help: add `#![feature(f16)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-f16.rs b/tests/ui/feature-gates/feature-gate-f16.rs index af906d71f5fe2..f748c603efe92 100644 --- a/tests/ui/feature-gates/feature-gate-f16.rs +++ b/tests/ui/feature-gates/feature-gate-f16.rs @@ -9,6 +9,7 @@ const A: f16 = 10.0; //~ ERROR the type `f16` is unstable pub fn main() { let a: f16 = 100.0; //~ ERROR the type `f16` is unstable let b = 0.0f16; //~ ERROR the type `f16` is unstable + let c = 0f16; //~ ERROR the type `f16` is unstable foo(1.23); } diff --git a/tests/ui/feature-gates/feature-gate-ffi_const.rs b/tests/ui/feature-gates/feature-gate-ffi_const.rs index 9f3d783ccd69c..35f91b99a6f40 100644 --- a/tests/ui/feature-gates/feature-gate-ffi_const.rs +++ b/tests/ui/feature-gates/feature-gate-ffi_const.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] extern "C" { - #[ffi_const] //~ ERROR the `#[ffi_const]` attribute is an experimental feature + #[unsafe(ffi_const)] //~ ERROR the `#[ffi_const]` attribute is an experimental feature pub fn foo(); } diff --git a/tests/ui/feature-gates/feature-gate-ffi_const.stderr b/tests/ui/feature-gates/feature-gate-ffi_const.stderr index d083b826d6e1c..7e8c941be07de 100644 --- a/tests/ui/feature-gates/feature-gate-ffi_const.stderr +++ b/tests/ui/feature-gates/feature-gate-ffi_const.stderr @@ -1,8 +1,8 @@ error[E0658]: the `#[ffi_const]` attribute is an experimental feature --> $DIR/feature-gate-ffi_const.rs:4:5 | -LL | #[ffi_const] - | ^^^^^^^^^^^^ +LL | #[unsafe(ffi_const)] + | ^^^^^^^^^^^^^^^^^^^^ | = note: see issue #58328 for more information = help: add `#![feature(ffi_const)]` to the crate attributes to enable diff --git a/tests/ui/feature-gates/feature-gate-ffi_pure.rs b/tests/ui/feature-gates/feature-gate-ffi_pure.rs index b0dfa01ff4c20..0f1288b234e43 100644 --- a/tests/ui/feature-gates/feature-gate-ffi_pure.rs +++ b/tests/ui/feature-gates/feature-gate-ffi_pure.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] extern "C" { - #[ffi_pure] //~ ERROR the `#[ffi_pure]` attribute is an experimental feature + #[unsafe(ffi_pure)] //~ ERROR the `#[ffi_pure]` attribute is an experimental feature pub fn foo(); } diff --git a/tests/ui/feature-gates/feature-gate-ffi_pure.stderr b/tests/ui/feature-gates/feature-gate-ffi_pure.stderr index 6544d450eeb9f..cf923536d6c55 100644 --- a/tests/ui/feature-gates/feature-gate-ffi_pure.stderr +++ b/tests/ui/feature-gates/feature-gate-ffi_pure.stderr @@ -1,8 +1,8 @@ error[E0658]: the `#[ffi_pure]` attribute is an experimental feature --> $DIR/feature-gate-ffi_pure.rs:4:5 | -LL | #[ffi_pure] - | ^^^^^^^^^^^ +LL | #[unsafe(ffi_pure)] + | ^^^^^^^^^^^^^^^^^^^ | = note: see issue #58329 for more information = help: add `#![feature(ffi_pure)]` to the crate attributes to enable diff --git a/tests/ui/feature-gates/feature-gate-freeze-impls.rs b/tests/ui/feature-gates/feature-gate-freeze-impls.rs index c14c9494874a2..401095dd83ba1 100644 --- a/tests/ui/feature-gates/feature-gate-freeze-impls.rs +++ b/tests/ui/feature-gates/feature-gate-freeze-impls.rs @@ -5,11 +5,11 @@ use std::marker::Freeze; struct Foo; unsafe impl Freeze for Foo {} -//~^ explicit impls for the `Freeze` trait are not permitted +//~^ ERROR explicit impls for the `Freeze` trait are not permitted struct Bar; impl !Freeze for Bar {} -//~^ explicit impls for the `Freeze` trait are not permitted +//~^ ERROR explicit impls for the `Freeze` trait are not permitted fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-frontmatter.rs b/tests/ui/feature-gates/feature-gate-frontmatter.rs new file mode 100644 index 0000000000000..58e1f57eec025 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-frontmatter.rs @@ -0,0 +1,5 @@ +---cargo +//~^ ERROR: frontmatters are experimental +--- + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-frontmatter.stderr b/tests/ui/feature-gates/feature-gate-frontmatter.stderr new file mode 100644 index 0000000000000..57d38db8e76a9 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-frontmatter.stderr @@ -0,0 +1,15 @@ +error[E0658]: frontmatters are experimental + --> $DIR/feature-gate-frontmatter.rs:1:1 + | +LL | / ---cargo +LL | | +LL | | --- + | |___^ + | + = note: see issue #136889 for more information + = help: add `#![feature(frontmatter)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-gen_blocks.rs b/tests/ui/feature-gates/feature-gate-gen_blocks.rs index 01fd922b0e999..989daf471bcb9 100644 --- a/tests/ui/feature-gates/feature-gate-gen_blocks.rs +++ b/tests/ui/feature-gates/feature-gate-gen_blocks.rs @@ -17,7 +17,7 @@ fn test_async_gen() { fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn foo() { gen {}; //[e2024]~^ ERROR: gen blocks are experimental diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.rs b/tests/ui/feature-gates/feature-gate-guard-patterns.rs index 52ed89e668b1f..095f66eeb9068 100644 --- a/tests/ui/feature-gates/feature-gate-guard-patterns.rs +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.rs @@ -22,7 +22,6 @@ fn other_guards_dont() { let ((x if guard(x)) | x) = 0; //~^ ERROR: guard patterns are experimental - //~| ERROR: cannot find value `x` if let (x if guard(x)) = 0 {} //~^ ERROR: guard patterns are experimental @@ -30,14 +29,13 @@ fn other_guards_dont() { while let (x if guard(x)) = 0 {} //~^ ERROR: guard patterns are experimental - #[cfg(FALSE)] + #[cfg(false)] while let (x if guard(x)) = 0 {} //~^ ERROR: guard patterns are experimental } fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} //~^ ERROR: guard patterns are experimental -//~| ERROR: cannot find value `x` fn guard(x: T) -> bool { unimplemented!() diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr index 8b85b663889f7..b0bf302f3cb6c 100644 --- a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr @@ -10,24 +10,6 @@ LL - (0 if guard(0)) => {}, LL + 0 if guard(0) => {}, | -error[E0425]: cannot find value `x` in this scope - --> $DIR/feature-gate-guard-patterns.rs:23:22 - | -LL | let ((x if guard(x)) | x) = 0; - | ^ not found in this scope - -error[E0425]: cannot find value `x` in this scope - --> $DIR/feature-gate-guard-patterns.rs:38:45 - | -LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} - | ^ - | -help: the binding `x` is available in a different scope in the same function - --> $DIR/feature-gate-guard-patterns.rs:23:11 - | -LL | let ((x if guard(x)) | x) = 0; - | ^ - error[E0658]: guard patterns are experimental --> $DIR/feature-gate-guard-patterns.rs:18:15 | @@ -51,7 +33,7 @@ LL | let ((x if guard(x)) | x) = 0; = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:27:18 + --> $DIR/feature-gate-guard-patterns.rs:26:18 | LL | if let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -62,7 +44,7 @@ LL | if let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:30:21 + --> $DIR/feature-gate-guard-patterns.rs:29:21 | LL | while let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -73,7 +55,7 @@ LL | while let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:34:21 + --> $DIR/feature-gate-guard-patterns.rs:33:21 | LL | while let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -84,7 +66,7 @@ LL | while let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:38:39 + --> $DIR/feature-gate-guard-patterns.rs:37:39 | LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} | ^^^^^^^^ @@ -94,7 +76,6 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider using match arm guards -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0425, E0658. -For more information about an error, try `rustc --explain E0425`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.rs b/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.rs index 2c130664e9a1f..f9b5176d78ac7 100644 --- a/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.rs +++ b/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.rs @@ -17,4 +17,13 @@ impl Mop { //~| ERROR: unconstrained opaque type } +fn funky(_: [(); { + impl Foo for fn() { + type Bar = impl Sized; + //~^ ERROR: `impl Trait` in associated types is unstable + //~| ERROR: unconstrained opaque type + } + 0 +}]) {} + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.stderr b/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.stderr index 7dfd79c728641..01d25c1622813 100644 --- a/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.stderr +++ b/tests/ui/feature-gates/feature-gate-impl_trait_in_assoc_type.stderr @@ -18,6 +18,16 @@ LL | type Bop = impl std::fmt::Debug; = help: add `#![feature(impl_trait_in_assoc_type)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: `impl Trait` in associated types is unstable + --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:22:20 + | +LL | type Bar = impl Sized; + | ^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(impl_trait_in_assoc_type)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: inherent associated types are unstable --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:14:5 | @@ -44,6 +54,14 @@ LL | type Bop = impl std::fmt::Debug; | = note: `Bop` must be used in combination with a concrete type within the same impl -error: aborting due to 5 previous errors +error: unconstrained opaque type + --> $DIR/feature-gate-impl_trait_in_assoc_type.rs:22:20 + | +LL | type Bar = impl Sized; + | ^^^^^^^^^^ + | + = note: `Bar` must be used in combination with a concrete type within the same impl + +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.rs b/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.rs index aec13fb020288..c2c010eaba636 100644 --- a/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.rs +++ b/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.rs @@ -60,4 +60,7 @@ fn f() { let t: Option = DEFAULT; } +trait Glob {} +use Glob::*; //~ ERROR `use` associated items of traits is unstable + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.stderr b/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.stderr index d342f5bd5512e..fca3cef3e203d 100644 --- a/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.stderr +++ b/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.stderr @@ -48,6 +48,16 @@ LL | use super::A::{self, DEFAULT, new}; = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 5 previous errors +error[E0658]: `use` associated items of traits is unstable + --> $DIR/feature-gate-import-trait-associated-functions.rs:64:5 + | +LL | use Glob::*; + | ^^^^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-inline_const_pat.rs b/tests/ui/feature-gates/feature-gate-inline_const_pat.rs deleted file mode 100644 index 3d0df289fb74a..0000000000000 --- a/tests/ui/feature-gates/feature-gate-inline_const_pat.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - let const { () } = (); - //~^ ERROR inline-const in pattern position is experimental [E0658] -} diff --git a/tests/ui/feature-gates/feature-gate-inline_const_pat.stderr b/tests/ui/feature-gates/feature-gate-inline_const_pat.stderr deleted file mode 100644 index 7d7376fa818bb..0000000000000 --- a/tests/ui/feature-gates/feature-gate-inline_const_pat.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: inline-const in pattern position is experimental - --> $DIR/feature-gate-inline_const_pat.rs:2:9 - | -LL | let const { () } = (); - | ^^^^^ - | - = note: see issue #76001 for more information - = help: add `#![feature(inline_const_pat)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-intrinsics.rs b/tests/ui/feature-gates/feature-gate-intrinsics.rs index 65806a0223e74..b7ebba672728d 100644 --- a/tests/ui/feature-gates/feature-gate-intrinsics.rs +++ b/tests/ui/feature-gates/feature-gate-intrinsics.rs @@ -1,8 +1,5 @@ -extern "rust-intrinsic" { //~ ERROR "rust-intrinsic" ABI is an implementation detail - fn bar(); //~ ERROR unrecognized intrinsic function: `bar` -} - -extern "rust-intrinsic" fn baz() {} //~ ERROR "rust-intrinsic" ABI is an implementation detail -//~^ ERROR intrinsic must be in +#[rustc_intrinsic] +//~^ ERROR the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items +fn bar(); fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-intrinsics.stderr b/tests/ui/feature-gates/feature-gate-intrinsics.stderr index 97246f05258f0..a7a725883a928 100644 --- a/tests/ui/feature-gates/feature-gate-intrinsics.stderr +++ b/tests/ui/feature-gates/feature-gate-intrinsics.stderr @@ -1,36 +1,12 @@ -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-intrinsics.rs:1:8 +error[E0658]: the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items + --> $DIR/feature-gate-intrinsics.rs:1:1 | -LL | extern "rust-intrinsic" { - | ^^^^^^^^^^^^^^^^ +LL | #[rustc_intrinsic] + | ^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gate-intrinsics.rs:5:8 - | -LL | extern "rust-intrinsic" fn baz() {} - | ^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(intrinsics)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0093]: unrecognized intrinsic function: `bar` - --> $DIR/feature-gate-intrinsics.rs:2:5 - | -LL | fn bar(); - | ^^^^^^^^^ unrecognized intrinsic - | - = help: if you're adding an intrinsic, be sure to update `check_intrinsic_type` - -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-intrinsics.rs:5:34 - | -LL | extern "rust-intrinsic" fn baz() {} - | ^^ - -error: aborting due to 4 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0093, E0658. -For more information about an error, try `rustc --explain E0093`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-link-arg-attribute.rs b/tests/ui/feature-gates/feature-gate-link-arg-attribute.rs index c12ff5b04dc30..8e11a05c89be2 100644 --- a/tests/ui/feature-gates/feature-gate-link-arg-attribute.rs +++ b/tests/ui/feature-gates/feature-gate-link-arg-attribute.rs @@ -7,3 +7,5 @@ extern "C" {} fn main() {} + +//[in_flag]~? ERROR unknown linking modifier `link-arg` diff --git a/tests/ui/feature-gates/feature-gate-min-generic-const-args.rs b/tests/ui/feature-gates/feature-gate-min-generic-const-args.rs index 171509876d187..1cf755b2c565d 100644 --- a/tests/ui/feature-gates/feature-gate-min-generic-const-args.rs +++ b/tests/ui/feature-gates/feature-gate-min-generic-const-args.rs @@ -1,8 +1,10 @@ trait Trait { + #[type_const] + //~^ ERROR experimental const ASSOC: usize; } -// FIXME(min_generic_const_args): implement support for this, behind the feature gate +// FIXME(mgca): add suggestion for mgca to this error fn foo() -> [u8; ::ASSOC] { //~^ ERROR generic parameters may not be used in const operations loop {} diff --git a/tests/ui/feature-gates/feature-gate-min-generic-const-args.stderr b/tests/ui/feature-gates/feature-gate-min-generic-const-args.stderr index 04d96b4c11ee6..af528a3c1b790 100644 --- a/tests/ui/feature-gates/feature-gate-min-generic-const-args.stderr +++ b/tests/ui/feature-gates/feature-gate-min-generic-const-args.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/feature-gate-min-generic-const-args.rs:6:29 + --> $DIR/feature-gate-min-generic-const-args.rs:8:29 | LL | fn foo() -> [u8; ::ASSOC] { | ^ cannot perform const operation using `T` @@ -7,5 +7,16 @@ LL | fn foo() -> [u8; ::ASSOC] { = note: type parameters may not be used in const expressions = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions -error: aborting due to 1 previous error +error[E0658]: the `#[type_const]` attribute is an experimental feature + --> $DIR/feature-gate-min-generic-const-args.rs:2:5 + | +LL | #[type_const] + | ^^^^^^^^^^^^^ + | + = note: see issue #132980 for more information + = help: add `#![feature(min_generic_const_args)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-movrs_target_feature.rs b/tests/ui/feature-gates/feature-gate-movrs_target_feature.rs new file mode 100644 index 0000000000000..738cab5a06d69 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-movrs_target_feature.rs @@ -0,0 +1,6 @@ +//@ only-x86_64 +#[target_feature(enable = "movrs")] +//~^ ERROR: currently unstable +unsafe fn foo() {} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-movrs_target_feature.stderr b/tests/ui/feature-gates/feature-gate-movrs_target_feature.stderr new file mode 100644 index 0000000000000..16fe7aaead5c8 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-movrs_target_feature.stderr @@ -0,0 +1,13 @@ +error[E0658]: the target feature `movrs` is currently unstable + --> $DIR/feature-gate-movrs_target_feature.rs:2:18 + | +LL | #[target_feature(enable = "movrs")] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #137976 for more information + = help: add `#![feature(movrs_target_feature)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-mut-ref.rs b/tests/ui/feature-gates/feature-gate-mut-ref.rs index 806b25de66ff2..752ae35d8a9ae 100644 --- a/tests/ui/feature-gates/feature-gate-mut-ref.rs +++ b/tests/ui/feature-gates/feature-gate-mut-ref.rs @@ -6,8 +6,8 @@ fn main() { let mut ref mut z = 14; //~ ERROR [E0658] z = &mut 15; - #[cfg(FALSE)] + #[cfg(false)] let mut ref x = 10; //~ ERROR [E0658] - #[cfg(FALSE)] + #[cfg(false)] let mut ref mut y = 10; //~ ERROR [E0658] } diff --git a/tests/ui/feature-gates/feature-gate-naked_functions.rs b/tests/ui/feature-gates/feature-gate-naked_functions.rs deleted file mode 100644 index abb55b9a557a9..0000000000000 --- a/tests/ui/feature-gates/feature-gate-naked_functions.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ needs-asm-support - -use std::arch::naked_asm; -//~^ ERROR use of unstable library feature `naked_functions` - -#[naked] -//~^ the `#[naked]` attribute is an experimental feature -extern "C" fn naked() { - naked_asm!("") - //~^ ERROR use of unstable library feature `naked_functions` - //~| ERROR: requires unsafe -} - -#[naked] -//~^ the `#[naked]` attribute is an experimental feature -extern "C" fn naked_2() -> isize { - naked_asm!("") - //~^ ERROR use of unstable library feature `naked_functions` - //~| ERROR: requires unsafe -} - -fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-naked_functions.stderr b/tests/ui/feature-gates/feature-gate-naked_functions.stderr deleted file mode 100644 index 9bfb9275bb201..0000000000000 --- a/tests/ui/feature-gates/feature-gate-naked_functions.stderr +++ /dev/null @@ -1,70 +0,0 @@ -error[E0658]: use of unstable library feature `naked_functions` - --> $DIR/feature-gate-naked_functions.rs:9:5 - | -LL | naked_asm!("") - | ^^^^^^^^^ - | - = note: see issue #90957 for more information - = help: add `#![feature(naked_functions)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: use of unstable library feature `naked_functions` - --> $DIR/feature-gate-naked_functions.rs:17:5 - | -LL | naked_asm!("") - | ^^^^^^^^^ - | - = note: see issue #90957 for more information - = help: add `#![feature(naked_functions)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: the `#[naked]` attribute is an experimental feature - --> $DIR/feature-gate-naked_functions.rs:6:1 - | -LL | #[naked] - | ^^^^^^^^ - | - = note: see issue #90957 for more information - = help: add `#![feature(naked_functions)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: the `#[naked]` attribute is an experimental feature - --> $DIR/feature-gate-naked_functions.rs:14:1 - | -LL | #[naked] - | ^^^^^^^^ - | - = note: see issue #90957 for more information - = help: add `#![feature(naked_functions)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: use of unstable library feature `naked_functions` - --> $DIR/feature-gate-naked_functions.rs:3:5 - | -LL | use std::arch::naked_asm; - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #90957 for more information - = help: add `#![feature(naked_functions)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0133]: use of inline assembly is unsafe and requires unsafe function or block - --> $DIR/feature-gate-naked_functions.rs:9:5 - | -LL | naked_asm!("") - | ^^^^^^^^^^^^^^ use of inline assembly - | - = note: inline assembly is entirely unchecked and can cause undefined behavior - -error[E0133]: use of inline assembly is unsafe and requires unsafe function or block - --> $DIR/feature-gate-naked_functions.rs:17:5 - | -LL | naked_asm!("") - | ^^^^^^^^^^^^^^ use of inline assembly - | - = note: inline assembly is entirely unchecked and can cause undefined behavior - -error: aborting due to 7 previous errors - -Some errors have detailed explanations: E0133, E0658. -For more information about an error, try `rustc --explain E0133`. diff --git a/tests/ui/feature-gates/feature-gate-naked_functions_rustic_abi.rs b/tests/ui/feature-gates/feature-gate-naked_functions_rustic_abi.rs new file mode 100644 index 0000000000000..d16c6ccd4c331 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-naked_functions_rustic_abi.rs @@ -0,0 +1,26 @@ +//@ needs-asm-support +//@ only-x86_64 + +#![feature(rust_cold_cc)] + +use std::arch::naked_asm; + +#[unsafe(naked)] +pub unsafe fn rust_implicit() { + //~^ ERROR `#[naked]` is currently unstable on `extern "Rust"` functions + naked_asm!("ret"); +} + +#[unsafe(naked)] +pub unsafe extern "Rust" fn rust_explicit() { + //~^ ERROR `#[naked]` is currently unstable on `extern "Rust"` functions + naked_asm!("ret"); +} + +#[unsafe(naked)] +pub unsafe extern "rust-cold" fn rust_cold() { + //~^ ERROR `#[naked]` is currently unstable on `extern "rust-cold"` functions + naked_asm!("ret"); +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-naked_functions_rustic_abi.stderr b/tests/ui/feature-gates/feature-gate-naked_functions_rustic_abi.stderr new file mode 100644 index 0000000000000..ba45e15ec86b6 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-naked_functions_rustic_abi.stderr @@ -0,0 +1,33 @@ +error[E0658]: `#[naked]` is currently unstable on `extern "Rust"` functions + --> $DIR/feature-gate-naked_functions_rustic_abi.rs:9:1 + | +LL | pub unsafe fn rust_implicit() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #138997 for more information + = help: add `#![feature(naked_functions_rustic_abi)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `#[naked]` is currently unstable on `extern "Rust"` functions + --> $DIR/feature-gate-naked_functions_rustic_abi.rs:15:1 + | +LL | pub unsafe extern "Rust" fn rust_explicit() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #138997 for more information + = help: add `#![feature(naked_functions_rustic_abi)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `#[naked]` is currently unstable on `extern "rust-cold"` functions + --> $DIR/feature-gate-naked_functions_rustic_abi.rs:21:1 + | +LL | pub unsafe extern "rust-cold" fn rust_cold() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #138997 for more information + = help: add `#![feature(naked_functions_rustic_abi)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.rs b/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.rs new file mode 100644 index 0000000000000..1fee3e7dcd1a9 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.rs @@ -0,0 +1,13 @@ +//@ needs-asm-support +//@ only-x86_64 + +use std::arch::naked_asm; + +#[unsafe(naked)] +#[target_feature(enable = "avx2")] +//~^ ERROR: `#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions +extern "C" fn naked() { + unsafe { naked_asm!("") } +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr b/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr new file mode 100644 index 0000000000000..8e601a14753bb --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr @@ -0,0 +1,13 @@ +error[E0658]: `#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions + --> $DIR/feature-gate-naked_functions_target_feature.rs:7:1 + | +LL | #[target_feature(enable = "avx2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #138568 for more information + = help: add `#![feature(naked_functions_target_feature)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-native_link_modifiers_as_needed.rs b/tests/ui/feature-gates/feature-gate-native_link_modifiers_as_needed.rs index c2965e42f276d..c5859eec8db51 100644 --- a/tests/ui/feature-gates/feature-gate-native_link_modifiers_as_needed.rs +++ b/tests/ui/feature-gates/feature-gate-native_link_modifiers_as_needed.rs @@ -7,3 +7,5 @@ extern "C" {} fn main() {} + +//[in_flag]~? ERROR linking modifier `as-needed` is unstable diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.rs b/tests/ui/feature-gates/feature-gate-never_patterns.rs index d23405ada2d4d..2cb0b5a6679d5 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.rs +++ b/tests/ui/feature-gates/feature-gate-never_patterns.rs @@ -15,12 +15,12 @@ fn main() { //~^ ERROR `!` patterns are experimental } // Check that the gate operates even behind `cfg`. - #[cfg(FALSE)] + #[cfg(false)] match *ptr { ! //~^ ERROR `!` patterns are experimental } - #[cfg(FALSE)] + #[cfg(false)] match *ptr { ! => {} //~^ ERROR `!` patterns are experimental @@ -60,13 +60,13 @@ fn main() { // Check that the gate operates even behind `cfg`. match Some(0) { None => {} - #[cfg(FALSE)] + #[cfg(false)] Some(_) //~^ ERROR `match` arm with no body } match Some(0) { _ => {} - #[cfg(FALSE)] + #[cfg(false)] Some(_) if false //~^ ERROR `match` arm with no body } diff --git a/tests/ui/feature-gates/feature-gate-new_range.rs b/tests/ui/feature-gates/feature-gate-new_range.rs index ecb73546d6a60..32eeb0424b645 100644 --- a/tests/ui/feature-gates/feature-gate-new_range.rs +++ b/tests/ui/feature-gates/feature-gate-new_range.rs @@ -2,9 +2,9 @@ fn main() { let a: core::range::RangeFrom = 1..; - //~^ mismatched types + //~^ ERROR mismatched types let b: core::range::Range = 2..3; - //~^ mismatched types + //~^ ERROR mismatched types let c: core::range::RangeInclusive = 4..=5; - //~^ mismatched types + //~^ ERROR mismatched types } diff --git a/tests/ui/feature-gates/feature-gate-no_sanitize.rs b/tests/ui/feature-gates/feature-gate-no_sanitize.rs index 66a9263e13a53..5ac014f1c5bf4 100644 --- a/tests/ui/feature-gates/feature-gate-no_sanitize.rs +++ b/tests/ui/feature-gates/feature-gate-no_sanitize.rs @@ -1,4 +1,4 @@ #[no_sanitize(address)] -//~^ the `#[no_sanitize]` attribute is an experimental feature +//~^ ERROR the `#[no_sanitize]` attribute is an experimental feature fn main() { } diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs index 4624faf1e53cf..663a83a665c48 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs @@ -7,9 +7,13 @@ struct Foo; impl Foo { fn foo(self: Pin<&mut Self>) { } + fn foo_sugar(&pin mut self) {} //~ ERROR pinned reference syntax is experimental + fn foo_sugar_const(&pin const self) {} //~ ERROR pinned reference syntax is experimental } -fn foo(x: Pin<&mut Foo>) { +fn foo(mut x: Pin<&mut Foo>) { + Foo::foo_sugar(x.as_mut()); + Foo::foo_sugar_const(x.as_ref()); let _y: &pin mut Foo = x; //~ ERROR pinned reference syntax is experimental } @@ -27,4 +31,38 @@ fn baz(mut x: Pin<&mut Foo>) { fn baz_sugar(_: &pin const Foo) {} //~ ERROR pinned reference syntax is experimental +#[cfg(any())] +mod not_compiled { + use std::pin::Pin; + + struct Foo; + + impl Foo { + fn foo(self: Pin<&mut Self>) { + } + fn foo_sugar(&pin mut self) {} //~ ERROR pinned reference syntax is experimental + fn foo_sugar_const(&pin const self) {} //~ ERROR pinned reference syntax is experimental + } + + fn foo(mut x: Pin<&mut Foo>) { + Foo::foo_sugar(x.as_mut()); + Foo::foo_sugar_const(x.as_ref()); + let _y: &pin mut Foo = x; //~ ERROR pinned reference syntax is experimental + } + + fn foo_sugar(_: &pin mut Foo) {} //~ ERROR pinned reference syntax is experimental + + fn bar(x: Pin<&mut Foo>) { + foo(x); + foo(x); + } + + fn baz(mut x: Pin<&mut Foo>) { + x.foo(); + x.foo(); + } + + fn baz_sugar(_: &pin const Foo) {} //~ ERROR pinned reference syntax is experimental +} + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr index dd93a7be1ada1..8ed7543d86e3d 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr @@ -1,5 +1,25 @@ error[E0658]: pinned reference syntax is experimental - --> $DIR/feature-gate-pin_ergonomics.rs:13:14 + --> $DIR/feature-gate-pin_ergonomics.rs:10:19 + | +LL | fn foo_sugar(&pin mut self) {} + | ^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: pinned reference syntax is experimental + --> $DIR/feature-gate-pin_ergonomics.rs:11:25 + | +LL | fn foo_sugar_const(&pin const self) {} + | ^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: pinned reference syntax is experimental + --> $DIR/feature-gate-pin_ergonomics.rs:17:14 | LL | let _y: &pin mut Foo = x; | ^^^ @@ -9,7 +29,7 @@ LL | let _y: &pin mut Foo = x; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: pinned reference syntax is experimental - --> $DIR/feature-gate-pin_ergonomics.rs:16:18 + --> $DIR/feature-gate-pin_ergonomics.rs:20:18 | LL | fn foo_sugar(_: &pin mut Foo) {} | ^^^ @@ -19,7 +39,7 @@ LL | fn foo_sugar(_: &pin mut Foo) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: pinned reference syntax is experimental - --> $DIR/feature-gate-pin_ergonomics.rs:28:18 + --> $DIR/feature-gate-pin_ergonomics.rs:32:18 | LL | fn baz_sugar(_: &pin const Foo) {} | ^^^ @@ -28,8 +48,58 @@ LL | fn baz_sugar(_: &pin const Foo) {} = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: pinned reference syntax is experimental + --> $DIR/feature-gate-pin_ergonomics.rs:43:23 + | +LL | fn foo_sugar(&pin mut self) {} + | ^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: pinned reference syntax is experimental + --> $DIR/feature-gate-pin_ergonomics.rs:44:29 + | +LL | fn foo_sugar_const(&pin const self) {} + | ^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: pinned reference syntax is experimental + --> $DIR/feature-gate-pin_ergonomics.rs:50:18 + | +LL | let _y: &pin mut Foo = x; + | ^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: pinned reference syntax is experimental + --> $DIR/feature-gate-pin_ergonomics.rs:53:22 + | +LL | fn foo_sugar(_: &pin mut Foo) {} + | ^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: pinned reference syntax is experimental + --> $DIR/feature-gate-pin_ergonomics.rs:65:22 + | +LL | fn baz_sugar(_: &pin const Foo) {} + | ^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0382]: use of moved value: `x` - --> $DIR/feature-gate-pin_ergonomics.rs:20:9 + --> $DIR/feature-gate-pin_ergonomics.rs:24:9 | LL | fn bar(x: Pin<&mut Foo>) { | - move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait @@ -39,15 +109,15 @@ LL | foo(x); | ^ value used here after move | note: consider changing this parameter type in function `foo` to borrow instead if owning the value isn't necessary - --> $DIR/feature-gate-pin_ergonomics.rs:12:11 + --> $DIR/feature-gate-pin_ergonomics.rs:14:15 | -LL | fn foo(x: Pin<&mut Foo>) { - | --- ^^^^^^^^^^^^^ this parameter takes ownership of the value +LL | fn foo(mut x: Pin<&mut Foo>) { + | --- ^^^^^^^^^^^^^ this parameter takes ownership of the value | | | in this function error[E0382]: use of moved value: `x` - --> $DIR/feature-gate-pin_ergonomics.rs:25:5 + --> $DIR/feature-gate-pin_ergonomics.rs:29:5 | LL | fn baz(mut x: Pin<&mut Foo>) { | ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait @@ -66,7 +136,7 @@ help: consider reborrowing the `Pin` instead of moving it LL | x.as_mut().foo(); | +++++++++ -error: aborting due to 5 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0382, E0658. For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/feature-gates/feature-gate-postfix_match.rs b/tests/ui/feature-gates/feature-gate-postfix_match.rs index dce7e81a9ae44..2226816e5ea7c 100644 --- a/tests/ui/feature-gates/feature-gate-postfix_match.rs +++ b/tests/ui/feature-gates/feature-gate-postfix_match.rs @@ -9,7 +9,7 @@ fn main() { }; // Test that the gate works behind a cfg - #[cfg(FALSE)] + #[cfg(false)] val.match { //~ ERROR postfix match is experimental Some(42) => "the answer to life, the universe, and everything", _ => "might be the answer to something" diff --git a/tests/ui/feature-gates/feature-gate-precise_capturing_in_traits.rs b/tests/ui/feature-gates/feature-gate-precise_capturing_in_traits.rs deleted file mode 100644 index 308b41dfc68ab..0000000000000 --- a/tests/ui/feature-gates/feature-gate-precise_capturing_in_traits.rs +++ /dev/null @@ -1,6 +0,0 @@ -trait Foo { - fn test() -> impl Sized + use; - //~^ ERROR `use<...>` precise capturing syntax is currently not allowed in return-position -} - -fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-precise_capturing_in_traits.stderr b/tests/ui/feature-gates/feature-gate-precise_capturing_in_traits.stderr deleted file mode 100644 index b2c6bf61124d9..0000000000000 --- a/tests/ui/feature-gates/feature-gate-precise_capturing_in_traits.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits - --> $DIR/feature-gate-precise_capturing_in_traits.rs:2:31 - | -LL | fn test() -> impl Sized + use; - | ^^^^^^^^^ - | - = note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope - = note: see issue #130044 for more information - = help: add `#![feature(precise_capturing_in_traits)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - diff --git a/tests/ui/feature-gates/feature-gate-print-check-cfg.rs b/tests/ui/feature-gates/feature-gate-print-check-cfg.rs deleted file mode 100644 index 304e0c132e50a..0000000000000 --- a/tests/ui/feature-gates/feature-gate-print-check-cfg.rs +++ /dev/null @@ -1,3 +0,0 @@ -//@ compile-flags: --print=check-cfg - -fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-print-check-cfg.stderr b/tests/ui/feature-gates/feature-gate-print-check-cfg.stderr deleted file mode 100644 index 62ee44b94a489..0000000000000 --- a/tests/ui/feature-gates/feature-gate-print-check-cfg.stderr +++ /dev/null @@ -1,2 +0,0 @@ -error: the `-Z unstable-options` flag must also be passed to enable the check-cfg print option - diff --git a/tests/ui/feature-gates/feature-gate-raw-dylib-elf.rs b/tests/ui/feature-gates/feature-gate-raw-dylib-elf.rs new file mode 100644 index 0000000000000..ec49e53d9b447 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-raw-dylib-elf.rs @@ -0,0 +1,9 @@ +//@ only-elf +//@ needs-dynamic-linking + +#[link(name = "meow", kind = "raw-dylib")] //~ ERROR: link kind `raw-dylib` is unstable on ELF platforms +unsafe extern "C" { + safe fn meowmeow(); +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-raw-dylib-elf.stderr b/tests/ui/feature-gates/feature-gate-raw-dylib-elf.stderr new file mode 100644 index 0000000000000..dfbf39f5a189c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-raw-dylib-elf.stderr @@ -0,0 +1,13 @@ +error[E0658]: link kind `raw-dylib` is unstable on ELF platforms + --> $DIR/feature-gate-raw-dylib-elf.rs:4:30 + | +LL | #[link(name = "meow", kind = "raw-dylib")] + | ^^^^^^^^^^^ + | + = note: see issue #135694 for more information + = help: add `#![feature(raw_dylib_elf)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs index 7ae4a8d911bad..025b4b52f12fe 100644 --- a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs +++ b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.rs @@ -1,7 +1,6 @@ // Test that `#[rustc_*]` attributes are gated by `rustc_attrs` feature gate. #[rustc_variance] //~ ERROR the `#[rustc_variance]` attribute is just used for rustc unit tests and will never be stable -#[rustc_error] //~ ERROR the `#[rustc_error]` attribute is just used for rustc unit tests and will never be stable #[rustc_nonnull_optimization_guaranteed] //~ ERROR the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in libcore and libstd and will never be stable fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr index 8c3a8eb2df875..0f760e0602d2f 100644 --- a/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr +++ b/tests/ui/feature-gates/feature-gate-rustc-attrs-1.stderr @@ -7,18 +7,9 @@ LL | #[rustc_variance] = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the `#[rustc_error]` attribute is just used for rustc unit tests and will never be stable - --> $DIR/feature-gate-rustc-attrs-1.rs:4:1 - | -LL | #[rustc_error] - | ^^^^^^^^^^^^^^ - | - = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error[E0658]: the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in libcore and libstd and will never be stable (note that the compiler does not even check whether the type indeed is being non-null-optimized; it is your responsibility to ensure that the attribute is only used on types that are optimized) - --> $DIR/feature-gate-rustc-attrs-1.rs:5:1 + --> $DIR/feature-gate-rustc-attrs-1.rs:4:1 | LL | #[rustc_nonnull_optimization_guaranteed] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,6 +17,6 @@ LL | #[rustc_nonnull_optimization_guaranteed] = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-rustc_const_unstable.rs b/tests/ui/feature-gates/feature-gate-rustc_const_unstable.rs index d7daaaaa101e7..522db1643fd4e 100644 --- a/tests/ui/feature-gates/feature-gate-rustc_const_unstable.rs +++ b/tests/ui/feature-gates/feature-gate-rustc_const_unstable.rs @@ -1,6 +1,6 @@ // Test internal const fn feature gate. -#[rustc_const_unstable(feature="fzzzzzt")] //~ stability attributes may not be used outside +#[rustc_const_unstable(feature="fzzzzzt")] //~ ERROR stability attributes may not be used outside pub const fn bazinga() {} fn main() { diff --git a/tests/ui/feature-gates/feature-gate-super-let.rs b/tests/ui/feature-gates/feature-gate-super-let.rs new file mode 100644 index 0000000000000..7be0800391338 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-super-let.rs @@ -0,0 +1,11 @@ +fn main() { + super let a = 1; + //~^ ERROR `super let` is experimental +} + +// Check that it also isn't accepted in cfg'd out code. +#[cfg(any())] +fn a() { + super let a = 1; + //~^ ERROR `super let` is experimental +} diff --git a/tests/ui/feature-gates/feature-gate-super-let.stderr b/tests/ui/feature-gates/feature-gate-super-let.stderr new file mode 100644 index 0000000000000..4d088594f6df5 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-super-let.stderr @@ -0,0 +1,23 @@ +error[E0658]: `super let` is experimental + --> $DIR/feature-gate-super-let.rs:2:5 + | +LL | super let a = 1; + | ^^^^^ + | + = note: see issue #139076 for more information + = help: add `#![feature(super_let)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `super let` is experimental + --> $DIR/feature-gate-super-let.rs:9:5 + | +LL | super let a = 1; + | ^^^^^ + | + = note: see issue #139076 for more information + = help: add `#![feature(super_let)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-try_blocks.rs b/tests/ui/feature-gates/feature-gate-try_blocks.rs index f565dd014de8e..90816293624aa 100644 --- a/tests/ui/feature-gates/feature-gate-try_blocks.rs +++ b/tests/ui/feature-gates/feature-gate-try_blocks.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 pub fn main() { let try_result: Option<_> = try { //~ ERROR `try` expression is experimental diff --git a/tests/ui/feature-gates/feature-gate-type_alias_impl_trait.rs b/tests/ui/feature-gates/feature-gate-type_alias_impl_trait.rs index 6d17f5e837d94..d332fba0623c3 100644 --- a/tests/ui/feature-gates/feature-gate-type_alias_impl_trait.rs +++ b/tests/ui/feature-gates/feature-gate-type_alias_impl_trait.rs @@ -1,33 +1,42 @@ -//@ check-pass -#![feature(type_alias_impl_trait)] use std::fmt::Debug; -type Foo = impl Debug; +type Foo = impl Debug; //~ ERROR `impl Trait` in type aliases is unstable struct Bar(Foo); +#[define_opaque(Foo)] //~ ERROR use of unstable library feature `type_alias_impl_trait` fn define() -> Bar { Bar(42) } -type Foo2 = impl Debug; +type Foo2 = impl Debug; //~ ERROR `impl Trait` in type aliases is unstable -fn define2(_: Foo2) { +#[define_opaque(Foo2)] //~ ERROR use of unstable library feature `type_alias_impl_trait` +fn define2() { let x = || -> Foo2 { 42 }; } -type Foo3 = impl Debug; +type Foo3 = impl Debug; //~ ERROR `impl Trait` in type aliases is unstable +#[define_opaque(Foo3)] //~ ERROR use of unstable library feature `type_alias_impl_trait` fn define3(x: Foo3) { let y: i32 = x; } -fn define3_1(_: Foo3) { +#[define_opaque(Foo3)] //~ ERROR use of unstable library feature `type_alias_impl_trait` +fn define3_1() { define3(42) } -type Foo4 = impl Debug; +type Foo4 = impl Debug; //~ ERROR `impl Trait` in type aliases is unstable +#[define_opaque(Foo4)] //~ ERROR use of unstable library feature `type_alias_impl_trait` fn define4(_: Foo4) { let y: Foo4 = 42; } +type Foo5 = [(); { + type Foo = impl Debug; //~ ERROR `impl Trait` in type aliases is unstable + //~^ ERROR unconstrained opaque type + 0 +}]; + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-type_alias_impl_trait.stderr b/tests/ui/feature-gates/feature-gate-type_alias_impl_trait.stderr new file mode 100644 index 0000000000000..bab60eb429303 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-type_alias_impl_trait.stderr @@ -0,0 +1,111 @@ +error[E0658]: use of unstable library feature `type_alias_impl_trait`: `type_alias_impl_trait` has open design concerns + --> $DIR/feature-gate-type_alias_impl_trait.rs:6:3 + | +LL | #[define_opaque(Foo)] + | ^^^^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `type_alias_impl_trait`: `type_alias_impl_trait` has open design concerns + --> $DIR/feature-gate-type_alias_impl_trait.rs:13:3 + | +LL | #[define_opaque(Foo2)] + | ^^^^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `type_alias_impl_trait`: `type_alias_impl_trait` has open design concerns + --> $DIR/feature-gate-type_alias_impl_trait.rs:20:3 + | +LL | #[define_opaque(Foo3)] + | ^^^^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `type_alias_impl_trait`: `type_alias_impl_trait` has open design concerns + --> $DIR/feature-gate-type_alias_impl_trait.rs:24:3 + | +LL | #[define_opaque(Foo3)] + | ^^^^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `type_alias_impl_trait`: `type_alias_impl_trait` has open design concerns + --> $DIR/feature-gate-type_alias_impl_trait.rs:31:3 + | +LL | #[define_opaque(Foo4)] + | ^^^^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-type_alias_impl_trait.rs:3:12 + | +LL | type Foo = impl Debug; + | ^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-type_alias_impl_trait.rs:11:13 + | +LL | type Foo2 = impl Debug; + | ^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-type_alias_impl_trait.rs:18:13 + | +LL | type Foo3 = impl Debug; + | ^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-type_alias_impl_trait.rs:29:13 + | +LL | type Foo4 = impl Debug; + | ^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-type_alias_impl_trait.rs:37:16 + | +LL | type Foo = impl Debug; + | ^^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: unconstrained opaque type + --> $DIR/feature-gate-type_alias_impl_trait.rs:37:16 + | +LL | type Foo = impl Debug; + | ^^^^^^^^^^ + | + = note: `Foo` must be used in combination with a concrete type within the same crate + +error: aborting due to 11 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-unqualified-local-imports.stderr b/tests/ui/feature-gates/feature-gate-unqualified-local-imports.stderr index bc8edd847cc0f..30e36acb871a4 100644 --- a/tests/ui/feature-gates/feature-gate-unqualified-local-imports.stderr +++ b/tests/ui/feature-gates/feature-gate-unqualified-local-imports.stderr @@ -5,6 +5,7 @@ LL | #![allow(unqualified_local_imports)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the `unqualified_local_imports` lint is unstable + = note: see issue #138299 for more information = help: add `#![feature(unqualified_local_imports)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = note: `#[warn(unknown_lints)]` on by default diff --git a/tests/ui/feature-gates/feature-gate-unsafe_pin_internals.rs b/tests/ui/feature-gates/feature-gate-unsafe_pin_internals.rs deleted file mode 100644 index deb5a2f691b8e..0000000000000 --- a/tests/ui/feature-gates/feature-gate-unsafe_pin_internals.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ edition:2018 -#![forbid(internal_features, unsafe_code)] -#![feature(unsafe_pin_internals)] -//~^ ERROR the feature `unsafe_pin_internals` is internal to the compiler or standard library - -use core::{marker::PhantomPinned, pin::Pin}; - -/// The `unsafe_pin_internals` is indeed unsound. -fn non_unsafe_pin_new_unchecked(pointer: &mut T) -> Pin<&mut T> { - Pin { __pointer: pointer } -} - -fn main() { - let mut self_referential = PhantomPinned; - let _: Pin<&mut PhantomPinned> = non_unsafe_pin_new_unchecked(&mut self_referential); -} diff --git a/tests/ui/feature-gates/feature-gate-unsafe_pin_internals.stderr b/tests/ui/feature-gates/feature-gate-unsafe_pin_internals.stderr deleted file mode 100644 index fc9bcd90e52ee..0000000000000 --- a/tests/ui/feature-gates/feature-gate-unsafe_pin_internals.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: the feature `unsafe_pin_internals` is internal to the compiler or standard library - --> $DIR/feature-gate-unsafe_pin_internals.rs:3:12 - | -LL | #![feature(unsafe_pin_internals)] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: using it is strongly discouraged -note: the lint level is defined here - --> $DIR/feature-gate-unsafe_pin_internals.rs:2:11 - | -LL | #![forbid(internal_features, unsafe_code)] - | ^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.rs b/tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.rs index c3d62a231e5e4..b5fbcc9ccf8c0 100644 --- a/tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.rs +++ b/tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.rs @@ -1,4 +1,4 @@ fn main() { let _ : &(dyn Send,) = &((),); - //~^ ERROR unsized tuple coercion is not stable enough + //~^ ERROR 2:28: 2:34: mismatched types [E0308] } diff --git a/tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.stderr b/tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.stderr index 251928658df2f..12847bd3c0dff 100644 --- a/tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.stderr +++ b/tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.stderr @@ -1,13 +1,14 @@ -error[E0658]: unsized tuple coercion is not stable enough for use and is subject to change +error[E0308]: mismatched types --> $DIR/feature-gate-unsized_tuple_coercion.rs:2:28 | LL | let _ : &(dyn Send,) = &((),); - | ^^^^^^ + | ------------ ^^^^^^ expected `&(dyn Send,)`, found `&((),)` + | | + | expected due to this | - = note: see issue #42877 for more information - = help: add `#![feature(unsized_tuple_coercion)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: expected reference `&(dyn Send,)` + found reference `&((),)` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/feature-gates/feature-gate-where_clause_attrs.a.stderr b/tests/ui/feature-gates/feature-gate-where_clause_attrs.a.stderr new file mode 100644 index 0000000000000..6a8f01bbcce11 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-where_clause_attrs.a.stderr @@ -0,0 +1,883 @@ +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:33:5 + | +LL | #[cfg(a)] T: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:34:5 + | +LL | #[cfg(b)] T: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:35:5 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:36:5 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:37:5 + | +LL | #[cfg_attr(a, cfg(a))] T: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:38:5 + | +LL | #[cfg_attr(b, cfg(b))] T: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:39:5 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:40:5 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:44:9 + | +LL | #[cfg(a)] U: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:45:9 + | +LL | #[cfg(b)] U: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:46:9 + | +LL | #[cfg(all())] U: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:47:9 + | +LL | #[cfg(any())] U: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:48:9 + | +LL | #[cfg_attr(a, cfg(a))] U: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:49:9 + | +LL | #[cfg_attr(b, cfg(b))] U: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:50:9 + | +LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:51:9 + | +LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:55:9 + | +LL | #[cfg(a)] U: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:56:9 + | +LL | #[cfg(b)] U: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:57:9 + | +LL | #[cfg(all())] U: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:58:9 + | +LL | #[cfg(any())] U: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:59:9 + | +LL | #[cfg_attr(a, cfg(a))] U: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:60:9 + | +LL | #[cfg_attr(b, cfg(b))] U: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:61:9 + | +LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:62:9 + | +LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:67:5 + | +LL | #[cfg(a)] T: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:68:5 + | +LL | #[cfg(b)] T: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:69:5 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:70:5 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:71:5 + | +LL | #[cfg_attr(a, cfg(a))] T: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:72:5 + | +LL | #[cfg_attr(b, cfg(b))] T: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:73:5 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:74:5 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:77:9 + | +LL | #[cfg(a)] U: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:78:9 + | +LL | #[cfg(b)] U: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:79:9 + | +LL | #[cfg(all())] U: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:80:9 + | +LL | #[cfg(any())] U: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:81:9 + | +LL | #[cfg_attr(a, cfg(a))] U: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:82:9 + | +LL | #[cfg_attr(b, cfg(b))] U: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:83:9 + | +LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:84:9 + | +LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:88:9 + | +LL | #[cfg(a)] U: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:89:9 + | +LL | #[cfg(b)] U: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:90:9 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:91:9 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:92:9 + | +LL | #[cfg_attr(a, cfg(a))] U: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:93:9 + | +LL | #[cfg_attr(b, cfg(b))] U: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:94:9 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:95:9 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:101:5 + | +LL | #[cfg(a)] T: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:102:5 + | +LL | #[cfg(b)] T: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:103:5 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:104:5 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:105:5 + | +LL | #[cfg_attr(a, cfg(a))] T: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:106:5 + | +LL | #[cfg_attr(b, cfg(b))] T: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:107:5 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:108:5 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:115:5 + | +LL | #[cfg(a)] T: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:116:5 + | +LL | #[cfg(b)] T: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:117:5 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:118:5 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:119:5 + | +LL | #[cfg_attr(a, cfg(a))] T: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:120:5 + | +LL | #[cfg_attr(b, cfg(b))] T: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:121:5 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:122:5 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:130:5 + | +LL | #[cfg(a)] T: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:131:5 + | +LL | #[cfg(b)] T: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:132:5 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:133:5 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:134:5 + | +LL | #[cfg_attr(a, cfg(a))] T: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:135:5 + | +LL | #[cfg_attr(b, cfg(b))] T: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:136:5 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:137:5 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:143:5 + | +LL | #[cfg(a)] T: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:144:5 + | +LL | #[cfg(b)] T: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:145:5 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:146:5 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:147:5 + | +LL | #[cfg_attr(a, cfg(a))] T: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:148:5 + | +LL | #[cfg_attr(b, cfg(b))] T: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:149:5 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:150:5 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:153:9 + | +LL | #[cfg(a)] U: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:154:9 + | +LL | #[cfg(b)] U: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:155:9 + | +LL | #[cfg(all())] U: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:156:9 + | +LL | #[cfg(any())] U: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:157:9 + | +LL | #[cfg_attr(a, cfg(a))] U: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:158:9 + | +LL | #[cfg_attr(b, cfg(b))] U: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:159:9 + | +LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:160:9 + | +LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 88 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-where_clause_attrs.b.stderr b/tests/ui/feature-gates/feature-gate-where_clause_attrs.b.stderr new file mode 100644 index 0000000000000..6a8f01bbcce11 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-where_clause_attrs.b.stderr @@ -0,0 +1,883 @@ +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:33:5 + | +LL | #[cfg(a)] T: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:34:5 + | +LL | #[cfg(b)] T: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:35:5 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:36:5 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:37:5 + | +LL | #[cfg_attr(a, cfg(a))] T: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:38:5 + | +LL | #[cfg_attr(b, cfg(b))] T: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:39:5 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:40:5 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:44:9 + | +LL | #[cfg(a)] U: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:45:9 + | +LL | #[cfg(b)] U: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:46:9 + | +LL | #[cfg(all())] U: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:47:9 + | +LL | #[cfg(any())] U: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:48:9 + | +LL | #[cfg_attr(a, cfg(a))] U: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:49:9 + | +LL | #[cfg_attr(b, cfg(b))] U: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:50:9 + | +LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:51:9 + | +LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:55:9 + | +LL | #[cfg(a)] U: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:56:9 + | +LL | #[cfg(b)] U: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:57:9 + | +LL | #[cfg(all())] U: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:58:9 + | +LL | #[cfg(any())] U: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:59:9 + | +LL | #[cfg_attr(a, cfg(a))] U: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:60:9 + | +LL | #[cfg_attr(b, cfg(b))] U: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:61:9 + | +LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:62:9 + | +LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:67:5 + | +LL | #[cfg(a)] T: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:68:5 + | +LL | #[cfg(b)] T: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:69:5 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:70:5 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:71:5 + | +LL | #[cfg_attr(a, cfg(a))] T: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:72:5 + | +LL | #[cfg_attr(b, cfg(b))] T: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:73:5 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:74:5 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:77:9 + | +LL | #[cfg(a)] U: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:78:9 + | +LL | #[cfg(b)] U: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:79:9 + | +LL | #[cfg(all())] U: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:80:9 + | +LL | #[cfg(any())] U: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:81:9 + | +LL | #[cfg_attr(a, cfg(a))] U: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:82:9 + | +LL | #[cfg_attr(b, cfg(b))] U: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:83:9 + | +LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:84:9 + | +LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:88:9 + | +LL | #[cfg(a)] U: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:89:9 + | +LL | #[cfg(b)] U: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:90:9 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:91:9 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:92:9 + | +LL | #[cfg_attr(a, cfg(a))] U: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:93:9 + | +LL | #[cfg_attr(b, cfg(b))] U: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:94:9 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:95:9 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:101:5 + | +LL | #[cfg(a)] T: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:102:5 + | +LL | #[cfg(b)] T: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:103:5 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:104:5 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:105:5 + | +LL | #[cfg_attr(a, cfg(a))] T: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:106:5 + | +LL | #[cfg_attr(b, cfg(b))] T: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:107:5 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:108:5 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:115:5 + | +LL | #[cfg(a)] T: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:116:5 + | +LL | #[cfg(b)] T: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:117:5 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:118:5 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:119:5 + | +LL | #[cfg_attr(a, cfg(a))] T: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:120:5 + | +LL | #[cfg_attr(b, cfg(b))] T: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:121:5 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:122:5 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:130:5 + | +LL | #[cfg(a)] T: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:131:5 + | +LL | #[cfg(b)] T: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:132:5 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:133:5 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:134:5 + | +LL | #[cfg_attr(a, cfg(a))] T: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:135:5 + | +LL | #[cfg_attr(b, cfg(b))] T: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:136:5 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:137:5 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:143:5 + | +LL | #[cfg(a)] T: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:144:5 + | +LL | #[cfg(b)] T: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:145:5 + | +LL | #[cfg(all())] T: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:146:5 + | +LL | #[cfg(any())] T: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:147:5 + | +LL | #[cfg_attr(a, cfg(a))] T: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:148:5 + | +LL | #[cfg_attr(b, cfg(b))] T: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:149:5 + | +LL | #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:150:5 + | +LL | #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:153:9 + | +LL | #[cfg(a)] U: TraitA, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:154:9 + | +LL | #[cfg(b)] U: TraitB, + | ^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:155:9 + | +LL | #[cfg(all())] U: TraitAll, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:156:9 + | +LL | #[cfg(any())] U: TraitAny, + | ^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:157:9 + | +LL | #[cfg_attr(a, cfg(a))] U: TraitAA, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:158:9 + | +LL | #[cfg_attr(b, cfg(b))] U: TraitBB, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:159:9 + | +LL | #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: attributes in `where` clause are unstable + --> $DIR/feature-gate-where_clause_attrs.rs:160:9 + | +LL | #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #115590 for more information + = help: add `#![feature(where_clause_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 88 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-where_clause_attrs.rs b/tests/ui/feature-gates/feature-gate-where_clause_attrs.rs new file mode 100644 index 0000000000000..09c734a52de04 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-where_clause_attrs.rs @@ -0,0 +1,162 @@ +//@ revisions: a b + +#![crate_type = "lib"] +use std::marker::PhantomData; + +#[cfg(a)] +trait TraitA {} + +#[cfg(b)] +trait TraitB {} + +#[cfg_attr(a, cfg(a))] +trait TraitAA {} + +#[cfg_attr(b, cfg(b))] +trait TraitBB {} + +#[cfg(all())] +trait TraitAll {} + +#[cfg(any())] +trait TraitAny {} + +#[cfg_attr(all(), cfg(all()))] +trait TraitAllAll {} + +#[cfg_attr(any(), cfg(any()))] +trait TraitAnyAny {} + + +trait A +where + #[cfg(a)] T: TraitA, //~ ERROR attributes in `where` clause are unstable + #[cfg(b)] T: TraitB, //~ ERROR attributes in `where` clause are unstable + #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(a, cfg(a))] T: TraitAA, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(b, cfg(b))] T: TraitBB, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable +{ + type B + where + #[cfg(a)] U: TraitA, //~ ERROR attributes in `where` clause are unstable + #[cfg(b)] U: TraitB, //~ ERROR attributes in `where` clause are unstable + #[cfg(all())] U: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(any())] U: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(a, cfg(a))] U: TraitAA, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(b, cfg(b))] U: TraitBB, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; //~ ERROR attributes in `where` clause are unstable + + fn foo(&self) + where + #[cfg(a)] U: TraitA, //~ ERROR attributes in `where` clause are unstable + #[cfg(b)] U: TraitB, //~ ERROR attributes in `where` clause are unstable + #[cfg(all())] U: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(any())] U: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(a, cfg(a))] U: TraitAA, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(b, cfg(b))] U: TraitBB, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; //~ ERROR attributes in `where` clause are unstable +} + +impl A for T +where + #[cfg(a)] T: TraitA, //~ ERROR attributes in `where` clause are unstable + #[cfg(b)] T: TraitB, //~ ERROR attributes in `where` clause are unstable + #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(a, cfg(a))] T: TraitAA, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(b, cfg(b))] T: TraitBB, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable +{ + type B = () where + #[cfg(a)] U: TraitA, //~ ERROR attributes in `where` clause are unstable + #[cfg(b)] U: TraitB, //~ ERROR attributes in `where` clause are unstable + #[cfg(all())] U: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(any())] U: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(a, cfg(a))] U: TraitAA, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(b, cfg(b))] U: TraitBB, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny; //~ ERROR attributes in `where` clause are unstable + + fn foo(&self) + where + #[cfg(a)] U: TraitA, //~ ERROR attributes in `where` clause are unstable + #[cfg(b)] U: TraitB, //~ ERROR attributes in `where` clause are unstable + #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(a, cfg(a))] U: TraitAA, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(b, cfg(b))] U: TraitBB, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable + {} +} + +struct C +where + #[cfg(a)] T: TraitA, //~ ERROR attributes in `where` clause are unstable + #[cfg(b)] T: TraitB, //~ ERROR attributes in `where` clause are unstable + #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(a, cfg(a))] T: TraitAA, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(b, cfg(b))] T: TraitBB, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable +{ + _t: PhantomData, +} + +union D +where + #[cfg(a)] T: TraitA, //~ ERROR attributes in `where` clause are unstable + #[cfg(b)] T: TraitB, //~ ERROR attributes in `where` clause are unstable + #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(a, cfg(a))] T: TraitAA, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(b, cfg(b))] T: TraitBB, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable +{ + + _t: PhantomData, +} + +enum E +where + #[cfg(a)] T: TraitA, //~ ERROR attributes in `where` clause are unstable + #[cfg(b)] T: TraitB, //~ ERROR attributes in `where` clause are unstable + #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(a, cfg(a))] T: TraitAA, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(b, cfg(b))] T: TraitBB, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable +{ + E(PhantomData), +} + +impl C where + #[cfg(a)] T: TraitA, //~ ERROR attributes in `where` clause are unstable + #[cfg(b)] T: TraitB, //~ ERROR attributes in `where` clause are unstable + #[cfg(all())] T: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(any())] T: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(a, cfg(a))] T: TraitAA, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(b, cfg(b))] T: TraitBB, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(all(), cfg(all()))] T: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(any(), cfg(any()))] T: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable +{ + fn new() where + #[cfg(a)] U: TraitA, //~ ERROR attributes in `where` clause are unstable + #[cfg(b)] U: TraitB, //~ ERROR attributes in `where` clause are unstable + #[cfg(all())] U: TraitAll, //~ ERROR attributes in `where` clause are unstable + #[cfg(any())] U: TraitAny, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(a, cfg(a))] U: TraitAA, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(b, cfg(b))] U: TraitBB, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(all(), cfg(all()))] U: TraitAllAll, //~ ERROR attributes in `where` clause are unstable + #[cfg_attr(any(), cfg(any()))] U: TraitAnyAny, //~ ERROR attributes in `where` clause are unstable + {} +} diff --git a/tests/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs b/tests/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs index 33fda822baad6..73a17c12035dc 100644 --- a/tests/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs +++ b/tests/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs @@ -1,7 +1,7 @@ -//@ compile-flags: --edition 2021 +//@ edition: 2021 pub fn demo() -> Option { - #[cfg(FALSE)] + #[cfg(false)] { do yeet //~ ERROR `do yeet` expression is experimental } @@ -9,7 +9,7 @@ pub fn demo() -> Option { Some(1) } -#[cfg(FALSE)] +#[cfg(false)] pub fn alternative() -> Result<(), String> { do yeet "hello"; //~ ERROR `do yeet` expression is experimental } diff --git a/tests/ui/feature-gates/feature-gate-yeet_expr.rs b/tests/ui/feature-gates/feature-gate-yeet_expr.rs index 12cc17e1cc89a..6604f496917f0 100644 --- a/tests/ui/feature-gates/feature-gate-yeet_expr.rs +++ b/tests/ui/feature-gates/feature-gate-yeet_expr.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 pub fn demo() -> Option { do yeet //~ ERROR `do yeet` expression is experimental diff --git a/tests/ui/feature-gates/feature-gate-yield-expr.rs b/tests/ui/feature-gates/feature-gate-yield-expr.rs new file mode 100644 index 0000000000000..382bf89069e7e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-yield-expr.rs @@ -0,0 +1,9 @@ +//@ edition: 2024 +#![feature(stmt_expr_attributes)] + +fn main() { + yield (); //~ ERROR yield syntax is experimental + //~^ ERROR yield syntax is experimental + //~^^ ERROR `yield` can only be used in `#[coroutine]` closures, or `gen` blocks + //~^^^ ERROR yield expression outside of coroutine literal +} diff --git a/tests/ui/feature-gates/feature-gate-yield-expr.stderr b/tests/ui/feature-gates/feature-gate-yield-expr.stderr new file mode 100644 index 0000000000000..ad8a15a0f3650 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-yield-expr.stderr @@ -0,0 +1,41 @@ +error[E0658]: yield syntax is experimental + --> $DIR/feature-gate-yield-expr.rs:5:5 + | +LL | yield (); + | ^^^^^^^^ + | + = note: see issue #43122 for more information + = help: add `#![feature(coroutines)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: yield syntax is experimental + --> $DIR/feature-gate-yield-expr.rs:5:5 + | +LL | yield (); + | ^^^^^^^^ + | + = note: see issue #43122 for more information + = help: add `#![feature(yield_expr)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks + --> $DIR/feature-gate-yield-expr.rs:5:5 + | +LL | yield (); + | ^^^^^^^^ + | +help: use `#[coroutine]` to make this closure a coroutine + | +LL | #[coroutine] fn main() { + | ++++++++++++ + +error[E0627]: yield expression outside of coroutine literal + --> $DIR/feature-gate-yield-expr.rs:5:5 + | +LL | yield (); + | ^^^^^^^^ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0627, E0658. +For more information about an error, try `rustc --explain E0627`. diff --git a/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.rs b/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.rs index 2328798d74c32..44c0f1130f05d 100644 --- a/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.rs +++ b/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.rs @@ -5,9 +5,9 @@ fn main() { let a = &[1, 2, 3]; println!("{}", { - extern "rust-intrinsic" { //~ ERROR "rust-intrinsic" ABI is an implementation detail - fn atomic_fence(); - } + #[rustc_intrinsic] //~ ERROR the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items + unsafe fn atomic_fence(); + atomic_fence(); //~ ERROR: is unsafe 42 }); diff --git a/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.stderr b/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.stderr index 86f88fdff5fc8..aaaaeece67a84 100644 --- a/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.stderr +++ b/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.stderr @@ -1,13 +1,13 @@ -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/feature-gated-feature-in-macro-arg.rs:8:16 +error[E0658]: the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items + --> $DIR/feature-gated-feature-in-macro-arg.rs:8:9 | -LL | extern "rust-intrinsic" { - | ^^^^^^^^^^^^^^^^ +LL | #[rustc_intrinsic] + | ^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0133]: call to unsafe function `main::atomic_fence` is unsafe and requires unsafe function or block +error[E0133]: call to unsafe function `atomic_fence` is unsafe and requires unsafe function or block --> $DIR/feature-gated-feature-in-macro-arg.rs:11:9 | LL | atomic_fence(); diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index a196b8ecdb3ea..d07201ebbd1aa 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -174,16 +174,16 @@ mod macro_use { mod inner { #![macro_use] } #[macro_use] fn f() { } - //~^ `#[macro_use]` only has an effect + //~^ WARN `#[macro_use]` only has an effect #[macro_use] struct S; - //~^ `#[macro_use]` only has an effect + //~^ WARN `#[macro_use]` only has an effect #[macro_use] type T = S; - //~^ `#[macro_use]` only has an effect + //~^ WARN `#[macro_use]` only has an effect #[macro_use] impl S { } - //~^ `#[macro_use]` only has an effect + //~^ WARN `#[macro_use]` only has an effect } #[macro_export] diff --git a/tests/ui/feature-gates/soft-syntax-gates-with-errors.rs b/tests/ui/feature-gates/soft-syntax-gates-with-errors.rs index 2aa2ed34020cf..87629a5bcce77 100644 --- a/tests/ui/feature-gates/soft-syntax-gates-with-errors.rs +++ b/tests/ui/feature-gates/soft-syntax-gates-with-errors.rs @@ -5,7 +5,7 @@ macro a() {} //~^ ERROR: `macro` is experimental -#[cfg(FALSE)] +#[cfg(false)] macro b() {} macro_rules! identity { @@ -17,13 +17,13 @@ identity! { //~^ ERROR: `macro` is experimental } -#[cfg(FALSE)] +#[cfg(false)] identity! { macro d() {} // No error } identity! { - #[cfg(FALSE)] + #[cfg(false)] macro e() {} } diff --git a/tests/ui/feature-gates/soft-syntax-gates-without-errors.rs b/tests/ui/feature-gates/soft-syntax-gates-without-errors.rs index 056c8fb04f45d..72d0bf1ccd524 100644 --- a/tests/ui/feature-gates/soft-syntax-gates-without-errors.rs +++ b/tests/ui/feature-gates/soft-syntax-gates-without-errors.rs @@ -2,7 +2,7 @@ // This file is used to test the behavior of the early-pass syntax warnings. // If macro syntax is stabilized, replace with a different unstable syntax. -#[cfg(FALSE)] +#[cfg(false)] macro b() {} //~^ WARN: `macro` is experimental //~| WARN: unstable syntax @@ -11,13 +11,13 @@ macro_rules! identity { ($($x:tt)*) => ($($x)*); } -#[cfg(FALSE)] +#[cfg(false)] identity! { macro d() {} // No error } identity! { - #[cfg(FALSE)] + #[cfg(false)] macro e() {} //~^ WARN: `macro` is experimental //~| WARN: unstable syntax diff --git a/tests/ui/feature-gates/stmt_expr_attrs_no_feature.rs b/tests/ui/feature-gates/stmt_expr_attrs_no_feature.rs index a160a9bb082dc..4523afa7c4bd1 100644 --- a/tests/ui/feature-gates/stmt_expr_attrs_no_feature.rs +++ b/tests/ui/feature-gates/stmt_expr_attrs_no_feature.rs @@ -25,7 +25,7 @@ fn main() { // Check that cfg works right -#[cfg(FALSE)] +#[cfg(false)] fn c() { #[rustc_dummy] 5; @@ -37,7 +37,7 @@ fn j() { 5; } -#[cfg_attr(not(FALSE), cfg(FALSE))] +#[cfg_attr(not(FALSE), cfg(false))] fn d() { #[rustc_dummy] 8; @@ -57,7 +57,7 @@ macro_rules! item_mac { #[rustc_dummy] 42; - #[cfg(FALSE)] + #[cfg(false)] fn f() { #[rustc_dummy] 5; @@ -69,7 +69,7 @@ macro_rules! item_mac { 5; } - #[cfg_attr(not(FALSE), cfg(FALSE))] + #[cfg_attr(not(FALSE), cfg(false))] fn g() { #[rustc_dummy] 8; @@ -90,42 +90,42 @@ item_mac!(e); // check that the gate visitor works right: extern "C" { - #[cfg(FALSE)] + #[cfg(false)] fn x(a: [u8; #[rustc_dummy] 5]); fn y(a: [u8; #[rustc_dummy] 5]); //~ ERROR attributes on expressions are experimental } struct Foo; impl Foo { - #[cfg(FALSE)] + #[cfg(false)] const X: u8 = #[rustc_dummy] 5; const Y: u8 = #[rustc_dummy] 5; //~ ERROR attributes on expressions are experimental } trait Bar { - #[cfg(FALSE)] + #[cfg(false)] const X: [u8; #[rustc_dummy] 5]; const Y: [u8; #[rustc_dummy] 5]; //~ ERROR attributes on expressions are experimental } struct Joyce { - #[cfg(FALSE)] + #[cfg(false)] field: [u8; #[rustc_dummy] 5], field2: [u8; #[rustc_dummy] 5] //~ ERROR attributes on expressions are experimental } struct Walky( - #[cfg(FALSE)] [u8; #[rustc_dummy] 5], + #[cfg(false)] [u8; #[rustc_dummy] 5], [u8; #[rustc_dummy] 5] //~ ERROR attributes on expressions are experimental ); enum Mike { Happy( - #[cfg(FALSE)] [u8; #[rustc_dummy] 5], + #[cfg(false)] [u8; #[rustc_dummy] 5], [u8; #[rustc_dummy] 5] //~ ERROR attributes on expressions are experimental ), Angry { - #[cfg(FALSE)] + #[cfg(false)] field: [u8; #[rustc_dummy] 5], field2: [u8; #[rustc_dummy] 5] //~ ERROR attributes on expressions are experimental } @@ -133,7 +133,7 @@ enum Mike { fn pat() { match 5 { - #[cfg(FALSE)] + #[cfg(false)] 5 => #[rustc_dummy] (), 6 => #[rustc_dummy] (), //~ ERROR attributes on expressions are experimental _ => (), diff --git a/tests/ui/ffi-attrs/ffi_const.rs b/tests/ui/ffi-attrs/ffi_const.rs index aa20a4d4c653a..dddc862b0fa60 100644 --- a/tests/ui/ffi-attrs/ffi_const.rs +++ b/tests/ui/ffi-attrs/ffi_const.rs @@ -1,15 +1,18 @@ #![feature(ffi_const)] #![crate_type = "lib"] -#[ffi_const] //~ ERROR `#[ffi_const]` may only be used on foreign functions +#[unsafe(ffi_const)] //~ ERROR `#[ffi_const]` may only be used on foreign functions pub fn foo() {} -#[ffi_const] //~ ERROR `#[ffi_const]` may only be used on foreign functions +#[unsafe(ffi_const)] //~ ERROR `#[ffi_const]` may only be used on foreign functions macro_rules! bar { - () => () + () => {}; } extern "C" { - #[ffi_const] //~ ERROR `#[ffi_const]` may only be used on foreign functions + #[unsafe(ffi_const)] //~ ERROR `#[ffi_const]` may only be used on foreign functions static INT: i32; + + #[ffi_const] //~ ERROR unsafe attribute used without unsafe + fn bar(); } diff --git a/tests/ui/ffi-attrs/ffi_const.stderr b/tests/ui/ffi-attrs/ffi_const.stderr index 394b98f89712a..7f31237539d11 100644 --- a/tests/ui/ffi-attrs/ffi_const.stderr +++ b/tests/ui/ffi-attrs/ffi_const.stderr @@ -1,21 +1,32 @@ +error: unsafe attribute used without unsafe + --> $DIR/ffi_const.rs:16:7 + | +LL | #[ffi_const] + | ^^^^^^^^^ usage of unsafe attribute + | +help: wrap the attribute in `unsafe(...)` + | +LL | #[unsafe(ffi_const)] + | +++++++ + + error[E0756]: `#[ffi_const]` may only be used on foreign functions --> $DIR/ffi_const.rs:4:1 | -LL | #[ffi_const] - | ^^^^^^^^^^^^ +LL | #[unsafe(ffi_const)] + | ^^^^^^^^^^^^^^^^^^^^ error[E0756]: `#[ffi_const]` may only be used on foreign functions --> $DIR/ffi_const.rs:7:1 | -LL | #[ffi_const] - | ^^^^^^^^^^^^ +LL | #[unsafe(ffi_const)] + | ^^^^^^^^^^^^^^^^^^^^ error[E0756]: `#[ffi_const]` may only be used on foreign functions --> $DIR/ffi_const.rs:13:5 | -LL | #[ffi_const] - | ^^^^^^^^^^^^ +LL | #[unsafe(ffi_const)] + | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0756`. diff --git a/tests/ui/ffi-attrs/ffi_const2.rs b/tests/ui/ffi-attrs/ffi_const2.rs index 82fe8a9c91dd1..8a8de13b1530b 100644 --- a/tests/ui/ffi-attrs/ffi_const2.rs +++ b/tests/ui/ffi-attrs/ffi_const2.rs @@ -1,8 +1,8 @@ #![feature(ffi_const, ffi_pure)] extern "C" { - #[ffi_pure] //~ ERROR `#[ffi_const]` function cannot be `#[ffi_pure]` - #[ffi_const] + #[unsafe(ffi_pure)] //~ ERROR `#[ffi_const]` function cannot be `#[ffi_pure]` + #[unsafe(ffi_const)] pub fn baz(); } diff --git a/tests/ui/ffi-attrs/ffi_const2.stderr b/tests/ui/ffi-attrs/ffi_const2.stderr index b8cbc296370de..d4c9bc42ec9e4 100644 --- a/tests/ui/ffi-attrs/ffi_const2.stderr +++ b/tests/ui/ffi-attrs/ffi_const2.stderr @@ -1,8 +1,8 @@ error[E0757]: `#[ffi_const]` function cannot be `#[ffi_pure]` --> $DIR/ffi_const2.rs:4:5 | -LL | #[ffi_pure] - | ^^^^^^^^^^^ +LL | #[unsafe(ffi_pure)] + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/ffi-attrs/ffi_pure.rs b/tests/ui/ffi-attrs/ffi_pure.rs index 6d2f3a614ec97..1f4812f55cf1b 100644 --- a/tests/ui/ffi-attrs/ffi_pure.rs +++ b/tests/ui/ffi-attrs/ffi_pure.rs @@ -1,15 +1,18 @@ #![feature(ffi_pure)] #![crate_type = "lib"] -#[ffi_pure] //~ ERROR `#[ffi_pure]` may only be used on foreign functions +#[unsafe(ffi_pure)] //~ ERROR `#[ffi_pure]` may only be used on foreign functions pub fn foo() {} -#[ffi_pure] //~ ERROR `#[ffi_pure]` may only be used on foreign functions +#[unsafe(ffi_pure)] //~ ERROR `#[ffi_pure]` may only be used on foreign functions macro_rules! bar { - () => () + () => {}; } extern "C" { - #[ffi_pure] //~ ERROR `#[ffi_pure]` may only be used on foreign functions + #[unsafe(ffi_pure)] //~ ERROR `#[ffi_pure]` may only be used on foreign functions static INT: i32; + + #[ffi_pure] //~ ERROR unsafe attribute used without unsafe + fn bar(); } diff --git a/tests/ui/ffi-attrs/ffi_pure.stderr b/tests/ui/ffi-attrs/ffi_pure.stderr index 8b61a4b609fc0..bd1177c01e2b0 100644 --- a/tests/ui/ffi-attrs/ffi_pure.stderr +++ b/tests/ui/ffi-attrs/ffi_pure.stderr @@ -1,21 +1,32 @@ +error: unsafe attribute used without unsafe + --> $DIR/ffi_pure.rs:16:7 + | +LL | #[ffi_pure] + | ^^^^^^^^ usage of unsafe attribute + | +help: wrap the attribute in `unsafe(...)` + | +LL | #[unsafe(ffi_pure)] + | +++++++ + + error[E0755]: `#[ffi_pure]` may only be used on foreign functions --> $DIR/ffi_pure.rs:4:1 | -LL | #[ffi_pure] - | ^^^^^^^^^^^ +LL | #[unsafe(ffi_pure)] + | ^^^^^^^^^^^^^^^^^^^ error[E0755]: `#[ffi_pure]` may only be used on foreign functions --> $DIR/ffi_pure.rs:7:1 | -LL | #[ffi_pure] - | ^^^^^^^^^^^ +LL | #[unsafe(ffi_pure)] + | ^^^^^^^^^^^^^^^^^^^ error[E0755]: `#[ffi_pure]` may only be used on foreign functions --> $DIR/ffi_pure.rs:13:5 | -LL | #[ffi_pure] - | ^^^^^^^^^^^ +LL | #[unsafe(ffi_pure)] + | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0755`. diff --git a/tests/ui/filter-block-view-items.rs b/tests/ui/filter-block-view-items.rs index 975ab19ddf25f..cb599c2726470 100644 --- a/tests/ui/filter-block-view-items.rs +++ b/tests/ui/filter-block-view-items.rs @@ -3,5 +3,5 @@ pub fn main() { // Make sure that this view item is filtered out because otherwise it would // trigger a compilation error - #[cfg(FALSE)] use bar as foo; + #[cfg(false)] use bar as foo; } diff --git a/tests/ui/float/target-has-reliable-nightly-float.rs b/tests/ui/float/target-has-reliable-nightly-float.rs new file mode 100644 index 0000000000000..ad8600fc63593 --- /dev/null +++ b/tests/ui/float/target-has-reliable-nightly-float.rs @@ -0,0 +1,31 @@ +//@ run-pass +//@ compile-flags: --check-cfg=cfg(target_has_reliable_f16,target_has_reliable_f16_math,target_has_reliable_f128,target_has_reliable_f128_math) +// Verify that the feature gates and config work and are registered as known config +// options. + +#![deny(unexpected_cfgs)] +#![feature(cfg_target_has_reliable_f16_f128)] + +#[cfg(target_has_reliable_f16)] +pub fn has_f16() {} + +#[cfg(target_has_reliable_f16_math)] +pub fn has_f16_math() {} + +#[cfg(target_has_reliable_f128 )] +pub fn has_f128() {} + +#[cfg(target_has_reliable_f128_math)] +pub fn has_f128_math() {} + +fn main() { + if cfg!(target_arch = "aarch64") && cfg!(target_os = "linux") { + // Aarch64+Linux is one target that has support for all features, so use it to spot + // check that the compiler does indeed enable these gates. + + assert!(cfg!(target_has_reliable_f16)); + assert!(cfg!(target_has_reliable_f16_math)); + assert!(cfg!(target_has_reliable_f128)); + assert!(cfg!(target_has_reliable_f128_math)); + } +} diff --git a/tests/ui/fmt/fmt_debug/invalid.rs b/tests/ui/fmt/fmt_debug/invalid.rs index 09cb46f1ea6e2..f2652fe277d12 100644 --- a/tests/ui/fmt/fmt_debug/invalid.rs +++ b/tests/ui/fmt/fmt_debug/invalid.rs @@ -2,3 +2,5 @@ //@ failure-status: 1 fn main() { } + +//~? ERROR incorrect value `invalid-value` for unstable option `fmt-debug` diff --git a/tests/ui/fmt/format-args-capture-first-literal-is-macro.stderr b/tests/ui/fmt/format-args-capture-first-literal-is-macro.stderr index e399361579ffb..e9b2e83c288d0 100644 --- a/tests/ui/fmt/format-args-capture-first-literal-is-macro.stderr +++ b/tests/ui/fmt/format-args-capture-first-literal-is-macro.stderr @@ -24,7 +24,6 @@ LL | format!(concat!("{a}")); | = note: did you intend to capture a variable `a` from the surrounding scope? = note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro - = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.stderr b/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.stderr index 950dea721e29d..e7ed2a76e6af1 100644 --- a/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.stderr +++ b/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.stderr @@ -6,7 +6,6 @@ LL | format_string_proc_macro::bad_format_args_captures!(); | = note: did you intend to capture a variable `x` from the surrounding scope? = note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro - = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/fmt/format-args-capture-macro-hygiene.stderr b/tests/ui/fmt/format-args-capture-macro-hygiene.stderr index 1b5fbd2af34d9..47ef581629e20 100644 --- a/tests/ui/fmt/format-args-capture-macro-hygiene.stderr +++ b/tests/ui/fmt/format-args-capture-macro-hygiene.stderr @@ -6,7 +6,6 @@ LL | format!(concat!("{foo}")); | = note: did you intend to capture a variable `foo` from the surrounding scope? = note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro - = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) error: there is no argument named `bar` --> $DIR/format-args-capture-macro-hygiene.rs:16:13 @@ -16,7 +15,6 @@ LL | format!(concat!("{ba", "r} {}"), 1); | = note: did you intend to capture a variable `bar` from the surrounding scope? = note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro - = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) error: there is no argument named `foo` --> $DIR/format-args-capture-macro-hygiene.rs:7:13 diff --git a/tests/ui/fmt/format-expanded-string.stderr b/tests/ui/fmt/format-expanded-string.stderr index 26ce7f26958f4..ab5baaf378902 100644 --- a/tests/ui/fmt/format-expanded-string.stderr +++ b/tests/ui/fmt/format-expanded-string.stderr @@ -5,7 +5,6 @@ LL | format!(concat!("abc}")); | ^^^^^^^^^^^^^^^ unmatched `}` in format string | = note: if you intended to print `}`, you can escape it using `}}` - = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) error: invalid format string: unmatched `}` found --> $DIR/format-expanded-string.rs:22:34 diff --git a/tests/ui/fmt/ifmt-bad-format-args.stderr b/tests/ui/fmt/ifmt-bad-format-args.stderr index 2db280c5e2aa4..d458f2bc05bf6 100644 --- a/tests/ui/fmt/ifmt-bad-format-args.stderr +++ b/tests/ui/fmt/ifmt-bad-format-args.stderr @@ -3,8 +3,6 @@ error: requires at least a format string argument | LL | format_args!(); | ^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format_args` (in Nightly builds, run with -Z macro-backtrace for more info) error: format argument must be a string literal --> $DIR/ifmt-bad-format-args.rs:3:18 diff --git a/tests/ui/fmt/issue-86085.stderr b/tests/ui/fmt/issue-86085.stderr index f325a3ce2931c..4cc19ec436538 100644 --- a/tests/ui/fmt/issue-86085.stderr +++ b/tests/ui/fmt/issue-86085.stderr @@ -5,7 +5,6 @@ LL | format ! ( concat ! ( r#"lJ𐏿Æ�.𐏿�"# , "r} {}" ) ) ; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unmatched `}` in format string | = note: if you intended to print `}`, you can escape it using `}}` - = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/fmt/issue-91556.stderr b/tests/ui/fmt/issue-91556.stderr index 52917fb8c4247..69f3779b93c19 100644 --- a/tests/ui/fmt/issue-91556.stderr +++ b/tests/ui/fmt/issue-91556.stderr @@ -5,7 +5,6 @@ LL | let _ = format!(concat!("{0}𝖳𝖾𝗌𝗍{"), i); | ^^^^^^^^^^^^^^^^^^^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` - = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/fmt/raw-idents.stderr b/tests/ui/fmt/raw-idents.stderr index 2ddc114d28614..178340cf57e1a 100644 --- a/tests/ui/fmt/raw-idents.stderr +++ b/tests/ui/fmt/raw-idents.stderr @@ -27,7 +27,6 @@ LL | println!(concat!("{r#", "type}")); | ^^^^^^^^^^^^^^^^^^^^^^^ raw identifier used here in format string | = note: identifiers in format strings can be keywords and don't need to be prefixed with `r#` - = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) error: invalid format string: raw identifiers are not supported --> $DIR/raw-idents.rs:11:16 diff --git a/tests/ui/fmt/respanned-literal-issue-106191.stderr b/tests/ui/fmt/respanned-literal-issue-106191.stderr index 17ab29e799bbd..5c9e538aa0cff 100644 --- a/tests/ui/fmt/respanned-literal-issue-106191.stderr +++ b/tests/ui/fmt/respanned-literal-issue-106191.stderr @@ -13,7 +13,6 @@ LL | format_args!(r#concat!("¡ {")); | ^^^^^^^^^^^^^^^^^^^^^^^ expected `}` in format string | = note: if you intended to print `{`, you can escape it using `{{` - = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/fn/bad-turbofish-hints-issue-121901.rs b/tests/ui/fn/bad-turbofish-hints-issue-121901.rs new file mode 100644 index 0000000000000..1425e576d75a3 --- /dev/null +++ b/tests/ui/fn/bad-turbofish-hints-issue-121901.rs @@ -0,0 +1,8 @@ +// Regression test for the parser wrongfully suggesting turbofish syntax in below syntax errors + +type One = for<'a> fn(Box`, found `)` +type Two = for<'a> fn(Box>); +//~^ ERROR: unmatched angle bracket + +fn main() {} diff --git a/tests/ui/fn/bad-turbofish-hints-issue-121901.stderr b/tests/ui/fn/bad-turbofish-hints-issue-121901.stderr new file mode 100644 index 0000000000000..aacb326f8a22a --- /dev/null +++ b/tests/ui/fn/bad-turbofish-hints-issue-121901.stderr @@ -0,0 +1,25 @@ +error: expected one of `+`, `,`, or `>`, found `)` + --> $DIR/bad-turbofish-hints-issue-121901.rs:3:40 + | +LL | type One = for<'a> fn(Box` + | +help: you might have meant to end the type parameters here + | +LL | type One = for<'a> fn(Box); + | + + +error: unmatched angle bracket + --> $DIR/bad-turbofish-hints-issue-121901.rs:5:41 + | +LL | type Two = for<'a> fn(Box>); + | ^ + | +help: remove extra angle bracket + | +LL - type Two = for<'a> fn(Box>); +LL + type Two = for<'a> fn(Box); + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/fn/coerce-suggestion-infer-region.rs b/tests/ui/fn/coerce-suggestion-infer-region.rs new file mode 100644 index 0000000000000..7edb828c97339 --- /dev/null +++ b/tests/ui/fn/coerce-suggestion-infer-region.rs @@ -0,0 +1,26 @@ +//! Functions with a mismatch between the expected and found type where the difference is a +//! reference may trigger analysis for additional help. In this test the expected type will be +//! &'a Container<&'a u8> and the found type will be Container<&'?0 u8>. +//! +//! This test exercises a scenario where the found type being analyzed contains an inference region +//! variable ('?0). This cannot be used in comparisons because the variable no longer exists by the +//! time the later analysis is performed. +//! +//! This is a regression test of #140823 + +trait MyFn

    Trait

    for () { const D: u16 = N; //~^ ERROR const `D` has an incompatible generic parameter for trait `Trait` const E: &'static () = &(); - //~^ ERROR lifetime parameters or bounds on const `E` do not match the trait declaration + //~^ ERROR lifetime parameters or bounds on associated const `E` do not match the trait declaration const F: usize = 1024 where diff --git a/tests/ui/generic-const-items/compare-impl-item.stderr b/tests/ui/generic-const-items/compare-impl-item.stderr index 3bf28e9da60f6..f7e3ff6501b11 100644 --- a/tests/ui/generic-const-items/compare-impl-item.stderr +++ b/tests/ui/generic-const-items/compare-impl-item.stderr @@ -1,4 +1,4 @@ -error[E0049]: const `A` has 1 type parameter but its trait declaration has 0 type parameters +error[E0049]: associated const `A` has 1 type parameter but its trait declaration has 0 type parameters --> $DIR/compare-impl-item.rs:16:13 | LL | const A: (); @@ -7,7 +7,7 @@ LL | const A: (); LL | const A: () = (); | ^ found 1 type parameter -error[E0049]: const `B` has 1 const parameter but its trait declaration has 2 const parameters +error[E0049]: associated const `B` has 1 const parameter but its trait declaration has 2 const parameters --> $DIR/compare-impl-item.rs:18:13 | LL | const B: u64; @@ -18,7 +18,7 @@ LL | const B: u64; LL | const B: u64 = 0; | ^^^^^^^^^^^^ found 1 const parameter -error[E0049]: const `C` has 0 type parameters but its trait declaration has 1 type parameter +error[E0049]: associated const `C` has 0 type parameters but its trait declaration has 1 type parameter --> $DIR/compare-impl-item.rs:20:13 | LL | const C: T; @@ -27,7 +27,7 @@ LL | const C: T; LL | const C<'a>: &'a str = ""; | ^^ found 0 type parameters -error[E0053]: const `D` has an incompatible generic parameter for trait `Trait` +error[E0053]: associated const `D` has an incompatible generic parameter for trait `Trait` --> $DIR/compare-impl-item.rs:22:13 | LL | trait Trait

    { @@ -42,14 +42,14 @@ LL | impl

    Trait

    for () { LL | const D: u16 = N; | ^^^^^^^^^^^^ found const parameter of type `u16` -error[E0195]: lifetime parameters or bounds on const `E` do not match the trait declaration +error[E0195]: lifetime parameters or bounds on associated const `E` do not match the trait declaration --> $DIR/compare-impl-item.rs:24:12 | LL | const E<'a>: &'a (); - | ---- lifetimes in impl do not match this const in trait + | ---- lifetimes in impl do not match this associated const in trait ... LL | const E: &'static () = &(); - | ^ lifetimes do not match const in trait + | ^ lifetimes do not match associated const in trait error[E0276]: impl has stricter requirements than trait --> $DIR/compare-impl-item.rs:29:12 diff --git a/tests/ui/generic-const-items/def-site-eval.fail.stderr b/tests/ui/generic-const-items/def-site-eval.fail.stderr index 1ba4455c7bebe..fa07f38552243 100644 --- a/tests/ui/generic-const-items/def-site-eval.fail.stderr +++ b/tests/ui/generic-const-items/def-site-eval.fail.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `_::<'_>` failed | LL | const _<'_a>: () = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/generic-const-items/user_type_annotations_pattern.rs b/tests/ui/generic-const-items/user_type_annotations_pattern.rs new file mode 100644 index 0000000000000..aa3846df2bca2 --- /dev/null +++ b/tests/ui/generic-const-items/user_type_annotations_pattern.rs @@ -0,0 +1,14 @@ +#![feature(generic_const_items)] +#![expect(incomplete_features)] + +const FOO<'a: 'static>: usize = 10; + +fn bar<'a>() { + match 10_usize { + FOO::<'a> => todo!(), + //~^ ERROR: lifetime may not live long enough + _ => todo!(), + } +} + +fn main() {} diff --git a/tests/ui/generic-const-items/user_type_annotations_pattern.stderr b/tests/ui/generic-const-items/user_type_annotations_pattern.stderr new file mode 100644 index 0000000000000..e15be275d2976 --- /dev/null +++ b/tests/ui/generic-const-items/user_type_annotations_pattern.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/user_type_annotations_pattern.rs:8:9 + | +LL | fn bar<'a>() { + | -- lifetime `'a` defined here +LL | match 10_usize { +LL | FOO::<'a> => todo!(), + | ^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to 1 previous error + diff --git a/tests/ui/generics/export-name-on-generics.fixed b/tests/ui/generics/export-name-on-generics.fixed new file mode 100644 index 0000000000000..4430cd9a2998a --- /dev/null +++ b/tests/ui/generics/export-name-on-generics.fixed @@ -0,0 +1,157 @@ +//@ run-rustfix +#![allow(dead_code, elided_named_lifetimes)] +#![deny(no_mangle_generic_items)] + +pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled + +pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + +#[export_name = "baz"] +pub fn baz(x: &i32) -> &i32 { x } + +#[export_name = "qux"] +pub fn qux<'a>(x: &'a i32) -> &i32 { x } + +pub struct Foo; + +impl Foo { + + pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + + pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + pub fn baz(x: &i32) -> &i32 { x } + + #[export_name = "qux"] + pub fn qux<'a>(x: &'a i32) -> &i32 { x } +} + +trait Trait1 { + fn foo(); + extern "C" fn bar(); + fn baz(x: &i32) -> &i32; + fn qux<'a>(x: &'a i32) -> &i32; +} + +impl Trait1 for Foo { + + fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + + extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + fn baz(x: &i32) -> &i32 { x } + + #[export_name = "qux"] + fn qux<'a>(x: &'a i32) -> &i32 { x } +} + +trait Trait2 { + fn foo(); + fn foo2(); + extern "C" fn bar(); + fn baz(x: &i32) -> &i32; + fn qux<'a>(x: &'a i32) -> &i32; +} + +impl Trait2 for Foo { + + fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + + fn foo2() {} //~ ERROR functions generic over types or consts must be mangled + + + extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + + fn baz(x: &i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled + + + fn qux<'a>(x: &'a i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled +} + +pub struct Bar(#[allow(dead_code)] T); + +impl Bar { + + pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + + pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + + pub fn baz() {} //~ ERROR functions generic over types or consts must be mangled +} + +impl Bar { + #[export_name = "qux"] + pub fn qux() {} +} + +trait Trait3 { + fn foo(); + extern "C" fn bar(); + fn baz(); +} + +impl Trait3 for Bar { + + fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + + extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + + fn baz() {} //~ ERROR functions generic over types or consts must be mangled +} + +pub struct Baz<'a>(#[allow(dead_code)] &'a i32); + +impl<'a> Baz<'a> { + #[export_name = "foo"] + pub fn foo() {} + + #[export_name = "bar"] + pub fn bar<'b>(x: &'b i32) -> &i32 { x } +} + +trait Trait4 { + fn foo(); + fn bar<'a>(x: &'a i32) -> &i32; +} + +impl Trait4 for Bar { + #[export_name = "foo"] + fn foo() {} + + #[export_name = "bar"] + fn bar<'b>(x: &'b i32) -> &i32 { x } +} + +impl<'a> Trait4 for Baz<'a> { + #[export_name = "foo"] + fn foo() {} + + #[export_name = "bar"] + fn bar<'b>(x: &'b i32) -> &i32 { x } +} + +trait Trait5 { + fn foo(); +} + +impl Trait5 for Foo { + #[export_name = "foo"] + fn foo() {} +} + +impl Trait5 for Bar { + #[export_name = "foo"] + fn foo() {} +} + +fn main() {} diff --git a/tests/ui/generics/export-name-on-generics.rs b/tests/ui/generics/export-name-on-generics.rs new file mode 100644 index 0000000000000..cbf1102196052 --- /dev/null +++ b/tests/ui/generics/export-name-on-generics.rs @@ -0,0 +1,159 @@ +//@ run-rustfix +#![allow(dead_code, elided_named_lifetimes)] +#![deny(no_mangle_generic_items)] + +#[export_name = "foo"] +pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled + +#[export_name = "bar"] +pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + +#[export_name = "baz"] +pub fn baz(x: &i32) -> &i32 { x } + +#[export_name = "qux"] +pub fn qux<'a>(x: &'a i32) -> &i32 { x } + +pub struct Foo; + +impl Foo { + #[export_name = "foo"] + pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "bar"] + pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + pub fn baz(x: &i32) -> &i32 { x } + + #[export_name = "qux"] + pub fn qux<'a>(x: &'a i32) -> &i32 { x } +} + +trait Trait1 { + fn foo(); + extern "C" fn bar(); + fn baz(x: &i32) -> &i32; + fn qux<'a>(x: &'a i32) -> &i32; +} + +impl Trait1 for Foo { + #[export_name = "foo"] + fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "bar"] + extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + fn baz(x: &i32) -> &i32 { x } + + #[export_name = "qux"] + fn qux<'a>(x: &'a i32) -> &i32 { x } +} + +trait Trait2 { + fn foo(); + fn foo2(); + extern "C" fn bar(); + fn baz(x: &i32) -> &i32; + fn qux<'a>(x: &'a i32) -> &i32; +} + +impl Trait2 for Foo { + #[export_name = "foo"] + fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "foo2"] + fn foo2() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + fn baz(x: &i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "qux"] + fn qux<'a>(x: &'a i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled +} + +pub struct Bar(#[allow(dead_code)] T); + +impl Bar { + #[export_name = "foo"] + pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "bar"] + pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + pub fn baz() {} //~ ERROR functions generic over types or consts must be mangled +} + +impl Bar { + #[export_name = "qux"] + pub fn qux() {} +} + +trait Trait3 { + fn foo(); + extern "C" fn bar(); + fn baz(); +} + +impl Trait3 for Bar { + #[export_name = "foo"] + fn foo() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "bar"] + extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled + + #[export_name = "baz"] + fn baz() {} //~ ERROR functions generic over types or consts must be mangled +} + +pub struct Baz<'a>(#[allow(dead_code)] &'a i32); + +impl<'a> Baz<'a> { + #[export_name = "foo"] + pub fn foo() {} + + #[export_name = "bar"] + pub fn bar<'b>(x: &'b i32) -> &i32 { x } +} + +trait Trait4 { + fn foo(); + fn bar<'a>(x: &'a i32) -> &i32; +} + +impl Trait4 for Bar { + #[export_name = "foo"] + fn foo() {} + + #[export_name = "bar"] + fn bar<'b>(x: &'b i32) -> &i32 { x } +} + +impl<'a> Trait4 for Baz<'a> { + #[export_name = "foo"] + fn foo() {} + + #[export_name = "bar"] + fn bar<'b>(x: &'b i32) -> &i32 { x } +} + +trait Trait5 { + fn foo(); +} + +impl Trait5 for Foo { + #[export_name = "foo"] + fn foo() {} +} + +impl Trait5 for Bar { + #[export_name = "foo"] + fn foo() {} +} + +fn main() {} diff --git a/tests/ui/generics/export-name-on-generics.stderr b/tests/ui/generics/export-name-on-generics.stderr new file mode 100644 index 0000000000000..7bc7b8ca55931 --- /dev/null +++ b/tests/ui/generics/export-name-on-generics.stderr @@ -0,0 +1,144 @@ +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:6:1 + | +LL | #[export_name = "foo"] + | ---------------------- help: remove this attribute +LL | pub fn foo() {} + | ^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/export-name-on-generics.rs:3:9 + | +LL | #![deny(no_mangle_generic_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:9:1 + | +LL | #[export_name = "bar"] + | ---------------------- help: remove this attribute +LL | pub extern "C" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:21:5 + | +LL | #[export_name = "foo"] + | ---------------------- help: remove this attribute +LL | pub fn foo() {} + | ^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:24:5 + | +LL | #[export_name = "bar"] + | ---------------------- help: remove this attribute +LL | pub extern "C" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:42:5 + | +LL | #[export_name = "foo"] + | ---------------------- help: remove this attribute +LL | fn foo() {} + | ^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:45:5 + | +LL | #[export_name = "bar"] + | ---------------------- help: remove this attribute +LL | extern "C" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:64:5 + | +LL | #[export_name = "foo"] + | ---------------------- help: remove this attribute +LL | fn foo() {} + | ^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:67:5 + | +LL | #[export_name = "foo2"] + | ----------------------- help: remove this attribute +LL | fn foo2() {} + | ^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:70:5 + | +LL | #[export_name = "baz"] + | ---------------------- help: remove this attribute +LL | extern "C" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:73:5 + | +LL | #[export_name = "baz"] + | ---------------------- help: remove this attribute +LL | fn baz(x: &i32) -> &i32 { x } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:76:5 + | +LL | #[export_name = "qux"] + | ---------------------- help: remove this attribute +LL | fn qux<'a>(x: &'a i32) -> &i32 { x } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:83:5 + | +LL | #[export_name = "foo"] + | ---------------------- help: remove this attribute +LL | pub fn foo() {} + | ^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:86:5 + | +LL | #[export_name = "bar"] + | ---------------------- help: remove this attribute +LL | pub extern "C" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:89:5 + | +LL | #[export_name = "baz"] + | ---------------------- help: remove this attribute +LL | pub fn baz() {} + | ^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:105:5 + | +LL | #[export_name = "foo"] + | ---------------------- help: remove this attribute +LL | fn foo() {} + | ^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:108:5 + | +LL | #[export_name = "bar"] + | ---------------------- help: remove this attribute +LL | extern "C" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: functions generic over types or consts must be mangled + --> $DIR/export-name-on-generics.rs:111:5 + | +LL | #[export_name = "baz"] + | ---------------------- help: remove this attribute +LL | fn baz() {} + | ^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors + diff --git a/tests/ui/generics/generic-non-trailing-defaults.rs b/tests/ui/generics/generic-non-trailing-defaults.rs index 16ea71d48c825..ef500c420680f 100644 --- a/tests/ui/generics/generic-non-trailing-defaults.rs +++ b/tests/ui/generics/generic-non-trailing-defaults.rs @@ -5,6 +5,6 @@ struct Vec(A, T); struct Foo, C>(A, B, C); //~^ ERROR generic parameters with a default must be trailing -//~| ERROR generic parameters with a default cannot use +//~| ERROR generic parameter defaults cannot reference parameters before they are declared fn main() {} diff --git a/tests/ui/generics/generic-non-trailing-defaults.stderr b/tests/ui/generics/generic-non-trailing-defaults.stderr index 713ba091b861c..d9057e932f5fa 100644 --- a/tests/ui/generics/generic-non-trailing-defaults.stderr +++ b/tests/ui/generics/generic-non-trailing-defaults.stderr @@ -10,11 +10,11 @@ error: generic parameters with a default must be trailing LL | struct Foo, C>(A, B, C); | ^ -error[E0128]: generic parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameter defaults cannot reference parameters before they are declared --> $DIR/generic-non-trailing-defaults.rs:6:23 | LL | struct Foo, C>(A, B, C); - | ^ defaulted generic parameters cannot be forward declared + | ^ cannot reference `C` before it is declared error: aborting due to 3 previous errors diff --git a/tests/ui/generics/generic-type-params-forward-mention.rs b/tests/ui/generics/generic-type-params-forward-mention.rs index 000c47095d27c..4a8f76d34aa90 100644 --- a/tests/ui/generics/generic-type-params-forward-mention.rs +++ b/tests/ui/generics/generic-type-params-forward-mention.rs @@ -1,6 +1,6 @@ // Ensure that we get an error and not an ICE for this problematic case. struct Foo, U = bool>(T, U); -//~^ ERROR generic parameters with a default cannot use forward declared identifiers +//~^ ERROR generic parameter defaults cannot reference parameters before they are declared fn main() { let x: Foo; } diff --git a/tests/ui/generics/generic-type-params-forward-mention.stderr b/tests/ui/generics/generic-type-params-forward-mention.stderr index d7a6faa19410d..ecc2387b48be8 100644 --- a/tests/ui/generics/generic-type-params-forward-mention.stderr +++ b/tests/ui/generics/generic-type-params-forward-mention.stderr @@ -1,8 +1,8 @@ -error[E0128]: generic parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameter defaults cannot reference parameters before they are declared --> $DIR/generic-type-params-forward-mention.rs:2:23 | LL | struct Foo, U = bool>(T, U); - | ^ defaulted generic parameters cannot be forward declared + | ^ cannot reference `U` before it is declared error: aborting due to 1 previous error diff --git a/tests/ui/generics/generic-type-params-name-repr.rs b/tests/ui/generics/generic-type-params-name-repr.rs index d60856b8904bb..bee8405a72c30 100644 --- a/tests/ui/generics/generic-type-params-name-repr.rs +++ b/tests/ui/generics/generic-type-params-name-repr.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + use std::marker; struct A; @@ -12,40 +14,40 @@ fn main() { // Ensure that the printed type doesn't include the default type params... let _: Foo = (); //~^ ERROR mismatched types - //~| expected `Foo`, found `()` - //~| expected struct `Foo` - //~| found unit type `()` + //~| NOTE expected `Foo`, found `()` + //~| NOTE expected struct `Foo` + //~| NOTE found unit type `()` // ...even when they're present, but the same types as the defaults. let _: Foo = (); //~^ ERROR mismatched types - //~| expected `Foo`, found `()` - //~| expected struct `Foo` - //~| found unit type `()` + //~| NOTE expected `Foo`, found `()` + //~| NOTE expected struct `Foo` + //~| NOTE found unit type `()` // Including cases where the default is using previous type params. let _: HashMap = (); //~^ ERROR mismatched types - //~| expected `HashMap`, found `()` - //~| expected struct `HashMap` - //~| found unit type `()` + //~| NOTE expected `HashMap`, found `()` + //~| NOTE expected struct `HashMap` + //~| NOTE found unit type `()` let _: HashMap> = (); //~^ ERROR mismatched types - //~| expected `HashMap`, found `()` - //~| expected struct `HashMap` - //~| found unit type `()` + //~| NOTE expected `HashMap`, found `()` + //~| NOTE expected struct `HashMap` + //~| NOTE found unit type `()` // But not when there's a different type in between. let _: Foo = (); //~^ ERROR mismatched types - //~| expected `Foo`, found `()` - //~| expected struct `Foo` - //~| found unit type `()` + //~| NOTE expected `Foo`, found `()` + //~| NOTE expected struct `Foo` + //~| NOTE found unit type `()` // And don't print <> at all when there's just defaults. let _: Foo = (); //~^ ERROR mismatched types - //~| expected `Foo`, found `()` - //~| expected struct `Foo` - //~| found unit type `()` + //~| NOTE expected `Foo`, found `()` + //~| NOTE expected struct `Foo` + //~| NOTE found unit type `()` } diff --git a/tests/ui/generics/generic-type-params-name-repr.stderr b/tests/ui/generics/generic-type-params-name-repr.stderr index 946f14cc1c638..ab03e8182ab44 100644 --- a/tests/ui/generics/generic-type-params-name-repr.stderr +++ b/tests/ui/generics/generic-type-params-name-repr.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/generic-type-params-name-repr.rs:13:25 + --> $DIR/generic-type-params-name-repr.rs:15:25 | LL | let _: Foo = (); | ---------- ^^ expected `Foo`, found `()` @@ -10,7 +10,7 @@ LL | let _: Foo = (); found unit type `()` error[E0308]: mismatched types - --> $DIR/generic-type-params-name-repr.rs:20:31 + --> $DIR/generic-type-params-name-repr.rs:22:31 | LL | let _: Foo = (); | ---------------- ^^ expected `Foo`, found `()` @@ -21,7 +21,7 @@ LL | let _: Foo = (); found unit type `()` error[E0308]: mismatched types - --> $DIR/generic-type-params-name-repr.rs:27:37 + --> $DIR/generic-type-params-name-repr.rs:29:37 | LL | let _: HashMap = (); | ---------------------- ^^ expected `HashMap`, found `()` @@ -32,7 +32,7 @@ LL | let _: HashMap = (); found unit type `()` error[E0308]: mismatched types - --> $DIR/generic-type-params-name-repr.rs:32:51 + --> $DIR/generic-type-params-name-repr.rs:34:51 | LL | let _: HashMap> = (); | ------------------------------------ ^^ expected `HashMap`, found `()` @@ -43,7 +43,7 @@ LL | let _: HashMap> = (); found unit type `()` error[E0308]: mismatched types - --> $DIR/generic-type-params-name-repr.rs:39:31 + --> $DIR/generic-type-params-name-repr.rs:41:31 | LL | let _: Foo = (); | ---------------- ^^ expected `Foo`, found `()` @@ -54,7 +54,7 @@ LL | let _: Foo = (); found unit type `()` error[E0308]: mismatched types - --> $DIR/generic-type-params-name-repr.rs:46:27 + --> $DIR/generic-type-params-name-repr.rs:48:27 | LL | let _: Foo = (); | ------------ ^^ expected `Foo`, found `()` diff --git a/tests/ui/generics/issue-61631-default-type-param-cannot-reference-self.stderr b/tests/ui/generics/issue-61631-default-type-param-cannot-reference-self.stderr index f3a550801b9bd..f659a144deff2 100644 --- a/tests/ui/generics/issue-61631-default-type-param-cannot-reference-self.stderr +++ b/tests/ui/generics/issue-61631-default-type-param-cannot-reference-self.stderr @@ -2,37 +2,37 @@ error[E0735]: generic parameters cannot use `Self` in their defaults --> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:13:25 | LL | struct Snobound<'a, P = Self> { x: Option<&'a P> } - | ^^^^ `Self` in generic parameter default + | ^^^^ error[E0735]: generic parameters cannot use `Self` in their defaults --> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:16:23 | LL | enum Enobound<'a, P = Self> { A, B(Option<&'a P>) } - | ^^^^ `Self` in generic parameter default + | ^^^^ error[E0735]: generic parameters cannot use `Self` in their defaults --> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:19:24 | LL | union Unobound<'a, P = Self> { x: i32, y: Option<&'a P> } - | ^^^^ `Self` in generic parameter default + | ^^^^ error[E0735]: generic parameters cannot use `Self` in their defaults --> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:25:31 | LL | struct Ssized<'a, P: Sized = [Self]> { x: Option<&'a P> } - | ^^^^ `Self` in generic parameter default + | ^^^^ error[E0735]: generic parameters cannot use `Self` in their defaults --> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:28:29 | LL | enum Esized<'a, P: Sized = [Self]> { A, B(Option<&'a P>) } - | ^^^^ `Self` in generic parameter default + | ^^^^ error[E0735]: generic parameters cannot use `Self` in their defaults --> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:31:30 | LL | union Usized<'a, P: Sized = [Self]> { x: i32, y: Option<&'a P> } - | ^^^^ `Self` in generic parameter default + | ^^^^ error: aborting due to 6 previous errors diff --git a/tests/ui/generics/post_monomorphization_error_backtrace.stderr b/tests/ui/generics/post_monomorphization_error_backtrace.stderr index 1366d5491f40e..96aa04ee359a9 100644 --- a/tests/ui/generics/post_monomorphization_error_backtrace.stderr +++ b/tests/ui/generics/post_monomorphization_error_backtrace.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `assert_zst::F::::V` failed | LL | const V: () = assert!(std::mem::size_of::() == 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: assertion failed: std::mem::size_of::() == 0 - | - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/post_monomorphization_error_backtrace.rs:14:5 @@ -23,8 +21,6 @@ error[E0080]: evaluation of `assert_zst::F::::V` failed | LL | const V: () = assert!(std::mem::size_of::() == 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: assertion failed: std::mem::size_of::() == 0 - | - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/post_monomorphization_error_backtrace.rs:14:5 diff --git a/tests/ui/generics/single-colon-path-not-const-generics.stderr b/tests/ui/generics/single-colon-path-not-const-generics.stderr index c14a5e62a0c8f..163ea4bbda6cd 100644 --- a/tests/ui/generics/single-colon-path-not-const-generics.stderr +++ b/tests/ui/generics/single-colon-path-not-const-generics.stderr @@ -1,14 +1,15 @@ error: path separator must be a double colon --> $DIR/single-colon-path-not-const-generics.rs:8:18 | +LL | pub struct Foo { + | --- while parsing this struct LL | a: Vec, | ^ | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 help: use a double colon instead | LL | a: Vec, - | + + | + error: aborting due to 1 previous error diff --git a/tests/ui/generics/slightly-nice-generic-literal-messages.rs b/tests/ui/generics/slightly-nice-generic-literal-messages.rs index 268009f65a5c1..83c08e977b6d1 100644 --- a/tests/ui/generics/slightly-nice-generic-literal-messages.rs +++ b/tests/ui/generics/slightly-nice-generic-literal-messages.rs @@ -3,12 +3,12 @@ use std::marker; struct Foo(T, marker::PhantomData); fn main() { - match Foo(1.1, marker::PhantomData) { + match Foo(1.1, marker::PhantomData) { //~ NOTE this expression has type `Foo<{float}, _>` 1 => {} //~^ ERROR mismatched types - //~| expected struct `Foo<{float}, _>` - //~| found type `{integer}` - //~| expected `Foo<{float}, _>`, found integer + //~| NOTE expected struct `Foo<{float}, _>` + //~| NOTE found type `{integer}` + //~| NOTE expected `Foo<{float}, _>`, found integer } } diff --git a/tests/ui/half-open-range-patterns/feature-gate-half-open-range-patterns-in-slices.rs b/tests/ui/half-open-range-patterns/feature-gate-half-open-range-patterns-in-slices.rs index a7b0ca6fe4a54..f2c873f3edf1e 100644 --- a/tests/ui/half-open-range-patterns/feature-gate-half-open-range-patterns-in-slices.rs +++ b/tests/ui/half-open-range-patterns/feature-gate-half-open-range-patterns-in-slices.rs @@ -1,6 +1,6 @@ fn main() { let xs = [13, 1, 5, 2, 3, 1, 21, 8]; let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs; - //~^ `X..` patterns in slices are experimental + //~^ ERROR `X..` patterns in slices are experimental //~| ERROR: refutable pattern } diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs index 33506a5c444a7..01f4340b14ada 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs @@ -9,7 +9,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn syntax() { match scrutinee { ...X => {} //~ ERROR range-to patterns with `...` are not allowed diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-no-end.rs b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-no-end.rs index 2f1ec65897211..24eb993473291 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-no-end.rs +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-no-end.rs @@ -3,7 +3,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn foo() { if let 0... = 1 {} //~ ERROR inclusive range with no end if let 0..= = 1 {} //~ ERROR inclusive range with no end diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-ref-ambiguous-interp.rs b/tests/ui/half-open-range-patterns/half-open-range-pats-ref-ambiguous-interp.rs index 2d63fe0785616..6b33ead3f87dd 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-ref-ambiguous-interp.rs +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-ref-ambiguous-interp.rs @@ -1,6 +1,6 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn syntax() { match &0 { &0.. | _ => {} diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-syntactic-pass.rs b/tests/ui/half-open-range-patterns/half-open-range-pats-syntactic-pass.rs index 4e3fffbef2de5..02699e76ad296 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-syntactic-pass.rs +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-syntactic-pass.rs @@ -4,7 +4,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn syntax() { match scrutinee { X.. | 0.. | 'a'.. | 0.0f32.. => {} diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions0.rs b/tests/ui/half-open-range-patterns/range_pat_interactions0.rs index f943ea0271da2..f79001ff1f171 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions0.rs +++ b/tests/ui/half-open-range-patterns/range_pat_interactions0.rs @@ -1,7 +1,5 @@ //@ run-pass #![allow(non_contiguous_range_endpoints)] -#![feature(inline_const_pat)] - fn main() { let mut if_lettable = vec![]; let mut first_or = vec![]; @@ -16,7 +14,6 @@ fn main() { match x { 1 | -3..0 => first_or.push(x), y @ (0..5 | 6) => or_two.push(y), - y @ 0..const { 5 + 1 } => assert_eq!(y, 5), y @ -5.. => range_from.push(y), y @ ..-7 => assert_eq!(y, -8), y => bottom.push(y), @@ -25,6 +22,6 @@ fn main() { assert_eq!(if_lettable, [-1, 0, 2, 4]); assert_eq!(first_or, [-3, -2, -1, 1]); assert_eq!(or_two, [0, 2, 3, 4, 6]); - assert_eq!(range_from, [-5, -4, 7]); + assert_eq!(range_from, [-5, -4, 5, 7]); assert_eq!(bottom, [-7, -6]); } diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions1.rs b/tests/ui/half-open-range-patterns/range_pat_interactions1.rs index 4d7c9f10261f2..c5705d5db60f8 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions1.rs +++ b/tests/ui/half-open-range-patterns/range_pat_interactions1.rs @@ -18,8 +18,6 @@ fn main() { //~^ error: expected a pattern range bound, found an expression 1 | -3..0 => first_or.push(x), y @ (0..5 | 6) => or_two.push(y), - y @ 0..const { 5 + 1 } => assert_eq!(y, 5), - //~^ error: inline-const in pattern position is experimental [E0658] y @ -5.. => range_from.push(y), y @ ..-7 => assert_eq!(y, -8), y => bottom.push(y), diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr index 62be2ef7a4d83..5cd98faa8abb2 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr @@ -11,10 +11,6 @@ LL + const VAL: /* Type */ = 5+1; LL ~ match x as i32 { LL ~ 0..VAL => errors_only.push(x), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | 0..const { 5+1 } => errors_only.push(x), - | +++++++ + error[E0408]: variable `n` is not bound in all patterns --> $DIR/range_pat_interactions1.rs:10:25 @@ -24,17 +20,6 @@ LL | if let n @ 2..3|4 = x { | | | variable not in all patterns -error[E0658]: inline-const in pattern position is experimental - --> $DIR/range_pat_interactions1.rs:21:20 - | -LL | y @ 0..const { 5 + 1 } => assert_eq!(y, 5), - | ^^^^^ - | - = note: see issue #76001 for more information - = help: add `#![feature(inline_const_pat)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0408, E0658. -For more information about an error, try `rustc --explain E0408`. +For more information about this error, try `rustc --explain E0408`. diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.rs b/tests/ui/half-open-range-patterns/range_pat_interactions2.rs deleted file mode 100644 index 0dbdb8fe7b6ef..0000000000000 --- a/tests/ui/half-open-range-patterns/range_pat_interactions2.rs +++ /dev/null @@ -1,22 +0,0 @@ -fn main() { - let mut first_or = Vec::::new(); - let mut or_two = Vec::::new(); - let mut range_from = Vec::::new(); - let mut bottom = Vec::::new(); - let mut errors_only = Vec::::new(); - - for x in -9 + 1..=(9 - 2) { - match x as i32 { - 0..=(5+1) => errors_only.push(x), - //~^ error: expected a pattern range bound, found an expression - //~| error: range pattern bounds cannot have parentheses - 1 | -3..0 => first_or.push(x), - y @ (0..5 | 6) => or_two.push(y), - y @ 0..const { 5 + 1 } => assert_eq!(y, 5), - //~^ error: inline-const in pattern position is experimental - y @ -5.. => range_from.push(y), - y @ ..-7 => assert_eq!(y, -8), - y => bottom.push(y), - } - } -} diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr deleted file mode 100644 index dbe7f4482eed4..0000000000000 --- a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr +++ /dev/null @@ -1,43 +0,0 @@ -error: range pattern bounds cannot have parentheses - --> $DIR/range_pat_interactions2.rs:10:17 - | -LL | 0..=(5+1) => errors_only.push(x), - | ^ ^ - | -help: remove these parentheses - | -LL - 0..=(5+1) => errors_only.push(x), -LL + 0..=5+1 => errors_only.push(x), - | - -error: expected a pattern range bound, found an expression - --> $DIR/range_pat_interactions2.rs:10:18 - | -LL | 0..=(5+1) => errors_only.push(x), - | ^^^ not a pattern - | - = note: arbitrary expressions are not allowed in patterns: -help: consider extracting the expression into a `const` - | -LL + const VAL: /* Type */ = 5+1; -LL ~ match x as i32 { -LL ~ 0..=(VAL) => errors_only.push(x), - | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | 0..=(const { 5+1 }) => errors_only.push(x), - | +++++++ + - -error[E0658]: inline-const in pattern position is experimental - --> $DIR/range_pat_interactions2.rs:15:20 - | -LL | y @ 0..const { 5 + 1 } => assert_eq!(y, 5), - | ^^^^^ - | - = note: see issue #76001 for more information - = help: add `#![feature(inline_const_pat)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions3.rs b/tests/ui/half-open-range-patterns/range_pat_interactions3.rs deleted file mode 100644 index 2f2778095cf49..0000000000000 --- a/tests/ui/half-open-range-patterns/range_pat_interactions3.rs +++ /dev/null @@ -1,19 +0,0 @@ -fn main() { - let mut first_or = Vec::::new(); - let mut or_two = Vec::::new(); - let mut range_from = Vec::::new(); - let mut bottom = Vec::::new(); - - for x in -9 + 1..=(9 - 2) { - match x as i32 { - 8.. => bottom.push(x), - 1 | -3..0 => first_or.push(x), - y @ (0..5 | 6) => or_two.push(y), - y @ 0..const { 5 + 1 } => assert_eq!(y, 5), - //~^ inline-const in pattern position is experimental - y @ -5.. => range_from.push(y), - y @ ..-7 => assert_eq!(y, -8), - y => bottom.push(y), - } - } -} diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions3.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions3.stderr deleted file mode 100644 index dc7dc0efa7a28..0000000000000 --- a/tests/ui/half-open-range-patterns/range_pat_interactions3.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: inline-const in pattern position is experimental - --> $DIR/range_pat_interactions3.rs:12:20 - | -LL | y @ 0..const { 5 + 1 } => assert_eq!(y, 5), - | ^^^^^ - | - = note: see issue #76001 for more information - = help: add `#![feature(inline_const_pat)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/half-open-range-patterns/slice_pattern_syntax_problem0.rs b/tests/ui/half-open-range-patterns/slice_pattern_syntax_problem0.rs index aa2690f377768..7bd1c100dc4fe 100644 --- a/tests/ui/half-open-range-patterns/slice_pattern_syntax_problem0.rs +++ b/tests/ui/half-open-range-patterns/slice_pattern_syntax_problem0.rs @@ -8,7 +8,7 @@ fn main() { // What if we wanted to pull this apart without individually binding a, b, and c? let [first_three @ ..3, rest @ 2..] = xs; - //~^ pattern requires 2 elements but array has 8 + //~^ ERROR pattern requires 2 elements but array has 8 // This is somewhat unintuitive and makes slice patterns exceedingly verbose. // We want to stabilize half-open RangeFrom (`X..`) patterns // but without banning us from using them for a more efficient slice pattern syntax. diff --git a/tests/ui/half-open-range-patterns/slice_pattern_syntax_problem1.rs b/tests/ui/half-open-range-patterns/slice_pattern_syntax_problem1.rs index 60b056fbcb694..12ceaa8c878ce 100644 --- a/tests/ui/half-open-range-patterns/slice_pattern_syntax_problem1.rs +++ b/tests/ui/half-open-range-patterns/slice_pattern_syntax_problem1.rs @@ -2,6 +2,6 @@ fn main() { let xs = [13, 1, 5, 2, 3, 1, 21, 8]; let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs; - //~^ `X..` patterns in slices are experimental + //~^ ERROR `X..` patterns in slices are experimental //~| ERROR: refutable pattern } diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.current.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.current.stderr index 7b9fd6bb4c571..c8394575e71de 100644 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.current.stderr +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.current.stderr @@ -13,7 +13,7 @@ LL | fn projection_bound Trait<'a, Assoc = usize>>() {} | ^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/candidate-from-env-universe-err-project.rs:53:30 + --> $DIR/candidate-from-env-universe-err-project.rs:52:30 | LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other @@ -22,7 +22,7 @@ LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); found associated type `>::Assoc` error[E0308]: mismatched types - --> $DIR/candidate-from-env-universe-err-project.rs:53:30 + --> $DIR/candidate-from-env-universe-err-project.rs:52:30 | LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr index 6e0ec5620da0c..468dc3b082e5c 100644 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr @@ -22,38 +22,20 @@ note: required by a bound in `projection_bound` LL | fn projection_bound Trait<'a, Assoc = usize>>() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `projection_bound` -error[E0271]: type mismatch resolving `>::Assoc == usize` - --> $DIR/candidate-from-env-universe-err-project.rs:38:24 - | -LL | projection_bound::(); - | ^ type mismatch resolving `>::Assoc == usize` - | -note: types differ - --> $DIR/candidate-from-env-universe-err-project.rs:14:18 - | -LL | type Assoc = usize; - | ^^^^^ -note: required by a bound in `projection_bound` - --> $DIR/candidate-from-env-universe-err-project.rs:18:42 - | -LL | fn projection_bound Trait<'a, Assoc = usize>>() {} - | ^^^^^^^^^^^^^ required by this bound in `projection_bound` - error: higher-ranked subtype error - --> $DIR/candidate-from-env-universe-err-project.rs:53:30 + --> $DIR/candidate-from-env-universe-err-project.rs:52:30 | LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: higher-ranked subtype error - --> $DIR/candidate-from-env-universe-err-project.rs:53:30 + --> $DIR/candidate-from-env-universe-err-project.rs:52:30 | LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs index a77d87f6fa78e..d70e392238218 100644 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs @@ -36,9 +36,8 @@ fn function2>() { // does not use the leak check when trying the where-bound, causing us // to prefer it over the impl, resulting in a placeholder error. projection_bound::(); - //[next]~^ ERROR type mismatch resolving `>::Assoc == usize` - //[next]~| ERROR the trait bound `for<'a> T: Trait<'a>` is not satisfied - //[current]~^^^ ERROR mismatched types + //[next]~^ ERROR the trait bound `for<'a> T: Trait<'a>` is not satisfied + //[current]~^^ ERROR mismatched types } fn function3>() { diff --git a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.rs b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.rs index 7b6ba9f7f1695..e22a49d039935 100644 --- a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.rs +++ b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.rs @@ -1,5 +1,3 @@ -//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" - fn id( f: &dyn Fn(u32), ) -> &dyn Fn( diff --git a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr index be2eca3e61ad9..00a5948bdd472 100644 --- a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/hang-on-deeply-nested-dyn.rs:12:5 + --> $DIR/hang-on-deeply-nested-dyn.rs:10:5 | LL | ) -> &dyn Fn( | ______- diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.rs b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.rs index f880749ec8352..ff6f4861e1443 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.rs +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.rs @@ -1,5 +1,3 @@ -//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" - // rust-lang/rust#30786: the use of `for<'b> &'b mut A: Stream` // should act as assertion that item does not borrow from its stream; // but an earlier buggy rustc allowed `.map(|x: &_| x)` which does diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr index ae364de8cc061..240e1c3dde1a1 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-1.stderr @@ -1,5 +1,5 @@ -error[E0599]: the method `filterx` exists for struct `Map`, but its trait bounds were not satisfied - --> $DIR/hrtb-doesnt-borrow-self-1.rs:116:22 +error[E0599]: the method `filterx` exists for struct `Map`, but its trait bounds were not satisfied + --> $DIR/hrtb-doesnt-borrow-self-1.rs:114:22 | LL | pub struct Map { | -------------------- method `filterx` not found for this struct because it doesn't satisfy `_: StreamExt` @@ -8,16 +8,16 @@ LL | let filter = map.filterx(|x: &_| true); | ^^^^^^^ method cannot be called due to unsatisfied trait bounds | note: the following trait bounds were not satisfied: - `&'a mut &Map: Stream` - `&'a mut &mut Map: Stream` - `&'a mut Map: Stream` - --> $DIR/hrtb-doesnt-borrow-self-1.rs:98:50 + `&'a mut &Map: Stream` + `&'a mut &mut Map: Stream` + `&'a mut Map: Stream` + --> $DIR/hrtb-doesnt-borrow-self-1.rs:96:50 | LL | impl StreamExt for T where for<'a> &'a mut T: Stream {} | --------- - ^^^^^^ unsatisfied trait bound introduced here = help: items from traits can only be used if the trait is implemented and in scope note: `StreamExt` defines an item `filterx`, perhaps you need to implement it - --> $DIR/hrtb-doesnt-borrow-self-1.rs:66:1 + --> $DIR/hrtb-doesnt-borrow-self-1.rs:64:1 | LL | pub trait StreamExt | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.rs b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.rs index ff4c0cf24d69f..d8a1f3fa69e4b 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.rs +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-doesnt-borrow-self-2.rs @@ -1,5 +1,3 @@ -//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" - // rust-lang/rust#30786: the use of `for<'b> &'b mut A: Stream &u64 {identity::}>, {closure@hrtb-doesnt-borrow-self-2.rs:111:30}>`, but its trait bounds were not satisfied - --> $DIR/hrtb-doesnt-borrow-self-2.rs:112:24 +error[E0599]: the method `countx` exists for struct `Filter &u64 {identity::}>, {closure@hrtb-doesnt-borrow-self-2.rs:109:30}>`, but its trait bounds were not satisfied + --> $DIR/hrtb-doesnt-borrow-self-2.rs:110:24 | LL | pub struct Filter { | ----------------------- method `countx` not found for this struct because it doesn't satisfy `_: StreamExt` @@ -8,16 +8,16 @@ LL | let count = filter.countx(); | ^^^^^^ method cannot be called due to unsatisfied trait bounds | note: the following trait bounds were not satisfied: - `&'a mut &Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/hrtb-doesnt-borrow-self-2.rs:111:30: 111:37}>: Stream` - `&'a mut &mut Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/hrtb-doesnt-borrow-self-2.rs:111:30: 111:37}>: Stream` - `&'a mut Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/hrtb-doesnt-borrow-self-2.rs:111:30: 111:37}>: Stream` - --> $DIR/hrtb-doesnt-borrow-self-2.rs:98:50 + `&'a mut &Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/hrtb-doesnt-borrow-self-2.rs:109:30: 109:37}>: Stream` + `&'a mut &mut Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/hrtb-doesnt-borrow-self-2.rs:109:30: 109:37}>: Stream` + `&'a mut Filter fn(&'a u64) -> &'a u64 {identity::}>, {closure@$DIR/hrtb-doesnt-borrow-self-2.rs:109:30: 109:37}>: Stream` + --> $DIR/hrtb-doesnt-borrow-self-2.rs:96:50 | LL | impl StreamExt for T where for<'a> &'a mut T: Stream {} | --------- - ^^^^^^ unsatisfied trait bound introduced here = help: items from traits can only be used if the trait is implemented and in scope note: `StreamExt` defines an item `countx`, perhaps you need to implement it - --> $DIR/hrtb-doesnt-borrow-self-2.rs:66:1 + --> $DIR/hrtb-doesnt-borrow-self-2.rs:64:1 | LL | pub trait StreamExt | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-just-for-static.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-just-for-static.stderr index 31e11e1283516..1c077a9b906cd 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-just-for-static.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-just-for-static.stderr @@ -2,7 +2,7 @@ error: implementation of `Foo` is not general enough --> $DIR/hrtb-just-for-static.rs:24:5 | LL | want_hrtb::() - | ^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough | = note: `StaticInt` must implement `Foo<&'0 isize>`, for any lifetime `'0`... = note: ...but it actually implements `Foo<&'static isize>` diff --git a/tests/ui/higher-ranked/trait-bounds/issue-95034.rs b/tests/ui/higher-ranked/trait-bounds/issue-95034.rs index 53b28c2bea453..f33469796c2f9 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-95034.rs +++ b/tests/ui/higher-ranked/trait-bounds/issue-95034.rs @@ -1,5 +1,6 @@ //@ check-pass -//@ compile-flags: --edition=2021 --crate-type=lib +//@ compile-flags: --crate-type=lib +//@ edition: 2021 use std::{ future::Future, diff --git a/tests/ui/higher-ranked/trait-bounds/issue-95230.rs b/tests/ui/higher-ranked/trait-bounds/issue-95230.rs index d1ca6834551e1..821a04ff0655e 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-95230.rs +++ b/tests/ui/higher-ranked/trait-bounds/issue-95230.rs @@ -1,11 +1,10 @@ -//@ revisions: old next -//@[next] compile-flags: -Znext-solver -//@[old] check-pass -//@[next] known-bug: #109764 - +// This used to be a test for overflow handling + higher-ranked outlives +// in the new solver, but this test isn't expected to pass since WF preds +// are not coinductive anymore. pub struct Bar where for<'a> &'a mut Self:; +//~^ ERROR overflow evaluating the requirement `for<'a> &'a mut Bar well-formed` fn main() {} diff --git a/tests/ui/higher-ranked/trait-bounds/issue-95230.stderr b/tests/ui/higher-ranked/trait-bounds/issue-95230.stderr new file mode 100644 index 0000000000000..7070af75d290c --- /dev/null +++ b/tests/ui/higher-ranked/trait-bounds/issue-95230.stderr @@ -0,0 +1,18 @@ +error[E0275]: overflow evaluating the requirement `for<'a> &'a mut Bar well-formed` + --> $DIR/issue-95230.rs:7:13 + | +LL | for<'a> &'a mut Self:; + | ^^^^^^^^^^^^ + | +note: required by a bound in `Bar` + --> $DIR/issue-95230.rs:7:13 + | +LL | pub struct Bar + | --- required by a bound in this struct +LL | where +LL | for<'a> &'a mut Self:; + | ^^^^^^^^^^^^ required by this bound in `Bar` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.rs index d84e30f4984ea..10bfc954a4742 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.rs +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.rs @@ -23,7 +23,7 @@ impl WithDefault for () { //f(()); // Going through another generic function works fine. call(f, ()); - //~^ expected a + //~^ ERROR expected a } } diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr index 64707642eeb01..36264b0d997b2 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr @@ -6,8 +6,8 @@ LL | call(f, ()); | | | required by a bound introduced by this call | - = note: expected a closure with arguments `((),)` - found a closure with arguments `(<_ as ATC<'a>>::Type,)` + = note: expected a closure with signature `for<'a> fn(<_ as ATC<'a>>::Type)` + found a closure with signature `fn(())` note: this is a known limitation of the trait solver that will be lifted in the future --> $DIR/issue-62529-3.rs:25:14 | diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.old.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.old.stderr index 57cbe16911824..79ded34d9cd9e 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.old.stderr +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.old.stderr @@ -1,16 +1,3 @@ -error: item does not constrain `Foo::{opaque#0}`, but has it in its signature - --> $DIR/norm-before-method-resolution-opaque-type.rs:16:4 - | -LL | fn weird_bound(x: &>::Out) -> X - | ^^^^^^^^^^^ - | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/norm-before-method-resolution-opaque-type.rs:14:12 - | -LL | type Foo = impl Sized; - | ^^^^^^^^^^ - error[E0507]: cannot move out of `*x` which is behind a shared reference --> $DIR/norm-before-method-resolution-opaque-type.rs:22:13 | @@ -23,6 +10,6 @@ LL - let x = *x; LL + let x = x; | -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.rs index 43207d8927649..f881fcb779fa8 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.rs +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution-opaque-type.rs @@ -13,8 +13,8 @@ impl<'a, T> Trait<'a> for T { type Foo = impl Sized; +#[define_opaque(Foo)] fn weird_bound(x: &>::Out) -> X -//[old]~^ ERROR: item does not constrain where for<'a> X: Trait<'a>, for<'a> >::Out<()>: Copy, diff --git a/tests/ui/hygiene/generate-mod.stderr b/tests/ui/hygiene/generate-mod.stderr index 32a2e145ca942..58b9c642dabe3 100644 --- a/tests/ui/hygiene/generate-mod.stderr +++ b/tests/ui/hygiene/generate-mod.stderr @@ -1,12 +1,18 @@ error[E0412]: cannot find type `FromOutside` in this scope --> $DIR/generate-mod.rs:35:13 | +LL | type A = $FromOutside; + | ------------ due to this macro variable +... LL | genmod!(FromOutside, Outer); | ^^^^^^^^^^^ not found in this scope error[E0412]: cannot find type `Outer` in this scope --> $DIR/generate-mod.rs:35:26 | +LL | struct $Outer; + | ------ due to this macro variable +... LL | genmod!(FromOutside, Outer); | ^^^^^ not found in this scope diff --git a/tests/ui/hygiene/globs.stderr b/tests/ui/hygiene/globs.stderr index 3f7a0ae7efa1b..31f25b182f1f5 100644 --- a/tests/ui/hygiene/globs.stderr +++ b/tests/ui/hygiene/globs.stderr @@ -50,6 +50,9 @@ error[E0425]: cannot find function `f` in this scope LL | n!(f); | ----- in this macro invocation ... +LL | $j(); + | -- due to this macro variable +... LL | n!(f); | ^ not found in this scope | @@ -63,6 +66,9 @@ error[E0425]: cannot find function `f` in this scope LL | n!(f); | ----- in this macro invocation ... +LL | $j(); + | -- due to this macro variable +... LL | f | ^ not found in this scope | diff --git a/tests/ui/hygiene/rustc-macro-transparency.rs b/tests/ui/hygiene/rustc-macro-transparency.rs index 5f36993af2f30..1a78a7543cfdc 100644 --- a/tests/ui/hygiene/rustc-macro-transparency.rs +++ b/tests/ui/hygiene/rustc-macro-transparency.rs @@ -6,9 +6,9 @@ macro transparent() { let transparent = 0; } #[rustc_macro_transparency = "semitransparent"] -macro semitransparent() { - struct SemiTransparent; - let semitransparent = 0; +macro semiopaque() { + struct SemiOpaque; + let semiopaque = 0; } #[rustc_macro_transparency = "opaque"] macro opaque() { @@ -18,14 +18,14 @@ macro opaque() { fn main() { transparent!(); - semitransparent!(); + semiopaque!(); opaque!(); Transparent; // OK - SemiTransparent; // OK + SemiOpaque; // OK Opaque; //~ ERROR cannot find value `Opaque` in this scope transparent; // OK - semitransparent; //~ ERROR expected value, found macro `semitransparent` + semiopaque; //~ ERROR expected value, found macro `semiopaque` opaque; //~ ERROR expected value, found macro `opaque` } diff --git a/tests/ui/hygiene/rustc-macro-transparency.stderr b/tests/ui/hygiene/rustc-macro-transparency.stderr index 1d2a1e1249864..1bea8a0ee4f31 100644 --- a/tests/ui/hygiene/rustc-macro-transparency.stderr +++ b/tests/ui/hygiene/rustc-macro-transparency.stderr @@ -4,17 +4,17 @@ error[E0425]: cannot find value `Opaque` in this scope LL | Opaque; | ^^^^^^ not found in this scope -error[E0423]: expected value, found macro `semitransparent` +error[E0423]: expected value, found macro `semiopaque` --> $DIR/rustc-macro-transparency.rs:29:5 | -LL | struct SemiTransparent; - | ----------------------- similarly named unit struct `SemiTransparent` defined here +LL | struct SemiOpaque; + | ------------------ similarly named unit struct `SemiOpaque` defined here ... -LL | semitransparent; - | ^^^^^^^^^^^^^^^ +LL | semiopaque; + | ^^^^^^^^^^ | | | not a value - | help: a unit struct with a similar name exists: `SemiTransparent` + | help: a unit struct with a similar name exists (notice the capitalization): `SemiOpaque` error[E0423]: expected value, found macro `opaque` --> $DIR/rustc-macro-transparency.rs:30:5 diff --git a/tests/ui/hygiene/unpretty-debug.stdout b/tests/ui/hygiene/unpretty-debug.stdout index e475cfac2fc17..f35bd7a7cb2ca 100644 --- a/tests/ui/hygiene/unpretty-debug.stdout +++ b/tests/ui/hygiene/unpretty-debug.stdout @@ -24,5 +24,5 @@ crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: SyntaxContexts: #0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque) -#1: parent: #0, outer_mark: (crate0::{{expn1}}, SemiTransparent) +#1: parent: #0, outer_mark: (crate0::{{expn1}}, SemiOpaque) */ diff --git a/tests/ui/illegal-sized-bound/regular.rs b/tests/ui/illegal-sized-bound/regular.rs index 7abd27ef98318..b5a4cbf10eca1 100644 --- a/tests/ui/illegal-sized-bound/regular.rs +++ b/tests/ui/illegal-sized-bound/regular.rs @@ -4,7 +4,7 @@ pub trait MutTrait { fn function(&mut self) where Self: Sized; - //~^ this has a `Sized` requirement + //~^ NOTE this has a `Sized` requirement } impl MutTrait for MutType { @@ -17,7 +17,7 @@ pub trait Trait { fn function(&self) where Self: Sized; - //~^ this has a `Sized` requirement + //~^ NOTE this has a `Sized` requirement } impl Trait for Type { diff --git a/tests/ui/impl-privacy-xc-1.rs b/tests/ui/impl-privacy-xc-1.rs deleted file mode 100644 index 6a10986739cd2..0000000000000 --- a/tests/ui/impl-privacy-xc-1.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass -//@ aux-build:impl_privacy_xc_1.rs - - -extern crate impl_privacy_xc_1; - -pub fn main() { - let fish = impl_privacy_xc_1::Fish { x: 1 }; - fish.swim(); -} diff --git a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.rs b/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.rs index 65e90c1ca53c0..f703269a4388d 100644 --- a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.rs +++ b/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.rs @@ -8,31 +8,29 @@ impl Captures<'_> for T {} pub struct MyTy<'a, 'b>(Option<*mut &'a &'b ()>); unsafe impl Send for MyTy<'_, 'static> {} -pub mod step1 { - use super::*; - pub type Step1<'a, 'b: 'a> = impl Sized + Captures<'b> + 'a; - pub fn step1<'a, 'b: 'a>() -> Step1<'a, 'b> { - MyTy::<'a, 'b>(None) - } +pub type Step1<'a, 'b: 'a> = impl Sized + Captures<'b> + 'a; +#[define_opaque(Step1)] +pub fn step1<'a, 'b: 'a>() -> Step1<'a, 'b> { + MyTy::<'a, 'b>(None) } -pub mod step2 { - pub type Step2<'a> = impl Send + 'a; - - // Although `Step2` is WF at the definition site, it's not WF in its - // declaration site (above). We check this in `check_opaque_meets_bounds`, - // which must remain sound. - pub fn step2<'a, 'b: 'a>() -> Step2<'a> - where crate::step1::Step1<'a, 'b>: Send - { - crate::step1::step1::<'a, 'b>() - //~^ ERROR hidden type for `Step2<'a>` captures lifetime that does not appear in bounds - } +pub type Step2<'a> = impl Send + 'a; + +// Although `Step2` is WF at the definition site, it's not WF in its +// declaration site (above). We check this in `check_opaque_meets_bounds`, +// which must remain sound. +#[define_opaque(Step2)] +pub fn step2<'a, 'b: 'a>() -> Step2<'a> +where + Step1<'a, 'b>: Send, +{ + step1::<'a, 'b>() + //~^ ERROR hidden type for `Step2<'a>` captures lifetime that does not appear in bounds } fn step3<'a, 'b>() { fn is_send() {} - is_send::>(); + is_send::>(); } fn main() {} diff --git a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.stderr b/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.stderr index 58d7f9959d346..ce797ccf40c90 100644 --- a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.stderr +++ b/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.stderr @@ -1,14 +1,14 @@ error[E0700]: hidden type for `Step2<'a>` captures lifetime that does not appear in bounds - --> $DIR/tait-hidden-erased-unsoundness-2.rs:28:9 + --> $DIR/tait-hidden-erased-unsoundness-2.rs:27:5 | -LL | pub type Step2<'a> = impl Send + 'a; - | -------------- opaque type defined here +LL | pub type Step2<'a> = impl Send + 'a; + | -------------- opaque type defined here ... -LL | pub fn step2<'a, 'b: 'a>() -> Step2<'a> - | -- hidden type `Step1<'a, 'b>` captures the lifetime `'b` as defined here +LL | pub fn step2<'a, 'b: 'a>() -> Step2<'a> + | -- hidden type `Step1<'a, 'b>` captures the lifetime `'b` as defined here ... -LL | crate::step1::step1::<'a, 'b>() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | step1::<'a, 'b>() + | ^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness.rs b/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness.rs index 40efd941e338c..ba22f16003ca6 100644 --- a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness.rs +++ b/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness.rs @@ -12,16 +12,15 @@ fn step1<'a, 'b: 'a>() -> impl Sized + Captures<'b> + 'a { MyTy::<'a, 'b>(None) } -mod tait { - type Tait<'a> = impl Sized + 'a; - pub(super) fn step2<'a, 'b: 'a>() -> Tait<'a> { - super::step1::<'a, 'b>() - //~^ ERROR hidden type for `Tait<'a>` captures lifetime that does not appear in bounds - } +type Tait<'a> = impl Sized + 'a; +#[define_opaque(Tait)] +fn step2<'a, 'b: 'a>() -> Tait<'a> { + step1::<'a, 'b>() + //~^ ERROR hidden type for `Tait<'a>` captures lifetime that does not appear in bounds } fn step3<'a, 'b: 'a>() -> impl Send + 'a { - tait::step2::<'a, 'b>() + step2::<'a, 'b>() // This should not be Send unless `'b: 'static` } diff --git a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness.stderr b/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness.stderr index 6c9b8cf24273b..ed1afd6450728 100644 --- a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness.stderr +++ b/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness.stderr @@ -1,12 +1,13 @@ error[E0700]: hidden type for `Tait<'a>` captures lifetime that does not appear in bounds - --> $DIR/tait-hidden-erased-unsoundness.rs:18:9 + --> $DIR/tait-hidden-erased-unsoundness.rs:18:5 | -LL | type Tait<'a> = impl Sized + 'a; - | --------------- opaque type defined here -LL | pub(super) fn step2<'a, 'b: 'a>() -> Tait<'a> { - | -- hidden type `impl Captures<'b> + 'a` captures the lifetime `'b` as defined here -LL | super::step1::<'a, 'b>() - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type Tait<'a> = impl Sized + 'a; + | --------------- opaque type defined here +LL | #[define_opaque(Tait)] +LL | fn step2<'a, 'b: 'a>() -> Tait<'a> { + | -- hidden type `impl Captures<'b> + 'a` captures the lifetime `'b` as defined here +LL | step1::<'a, 'b>() + | ^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/arg-position-impl-trait-too-long.rs b/tests/ui/impl-trait/apit/arg-position-impl-trait-too-long.rs similarity index 100% rename from tests/ui/impl-trait/arg-position-impl-trait-too-long.rs rename to tests/ui/impl-trait/apit/arg-position-impl-trait-too-long.rs diff --git a/tests/ui/impl-trait/arg-position-impl-trait-too-long.stderr b/tests/ui/impl-trait/apit/arg-position-impl-trait-too-long.stderr similarity index 100% rename from tests/ui/impl-trait/arg-position-impl-trait-too-long.stderr rename to tests/ui/impl-trait/apit/arg-position-impl-trait-too-long.stderr diff --git a/tests/ui/impl-trait/impl-generic-mismatch-ab.rs b/tests/ui/impl-trait/apit/impl-generic-mismatch-ab.rs similarity index 100% rename from tests/ui/impl-trait/impl-generic-mismatch-ab.rs rename to tests/ui/impl-trait/apit/impl-generic-mismatch-ab.rs diff --git a/tests/ui/impl-trait/impl-generic-mismatch-ab.stderr b/tests/ui/impl-trait/apit/impl-generic-mismatch-ab.stderr similarity index 100% rename from tests/ui/impl-trait/impl-generic-mismatch-ab.stderr rename to tests/ui/impl-trait/apit/impl-generic-mismatch-ab.stderr diff --git a/tests/ui/impl-trait/associated-type-undefine.rs b/tests/ui/impl-trait/associated-type-undefine.rs index c8f07021fbf16..895525960fc1a 100644 --- a/tests/ui/impl-trait/associated-type-undefine.rs +++ b/tests/ui/impl-trait/associated-type-undefine.rs @@ -16,6 +16,7 @@ impl Foo for u32 { impl Foo for () { type Bar = impl Sized; + //~^ ERROR: unconstrained opaque type type Gat = ::Bar; // Because we encounter `Gat` first, we never walk into another `Gat` // again, thus missing the opaque type that we could be defining. diff --git a/tests/ui/impl-trait/associated-type-undefine.stderr b/tests/ui/impl-trait/associated-type-undefine.stderr index 5d9d525eb93bf..e567f1bcdf6a1 100644 --- a/tests/ui/impl-trait/associated-type-undefine.stderr +++ b/tests/ui/impl-trait/associated-type-undefine.stderr @@ -1,5 +1,13 @@ +error: unconstrained opaque type + --> $DIR/associated-type-undefine.rs:18:16 + | +LL | type Bar = impl Sized; + | ^^^^^^^^^^ + | + = note: `Bar` must be used in combination with a concrete type within the same impl + error[E0308]: mismatched types - --> $DIR/associated-type-undefine.rs:23:14 + --> $DIR/associated-type-undefine.rs:24:14 | LL | type Bar = impl Sized; | ---------- the expected opaque type @@ -9,12 +17,7 @@ LL | ((), ()) | = note: expected opaque type `<() as Foo>::Bar` found unit type `()` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/associated-type-undefine.rs:22:8 - | -LL | fn foo(self) -> (::Gat, ::Gat) { - | ^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/async_scope_creep.rs b/tests/ui/impl-trait/async_scope_creep.rs index 0fb355c523373..449f090a2e47b 100644 --- a/tests/ui/impl-trait/async_scope_creep.rs +++ b/tests/ui/impl-trait/async_scope_creep.rs @@ -22,11 +22,13 @@ impl Pending { } #[cfg(tait)] + #[define_opaque(OpeningReadFuture)] fn read_fut(&mut self) -> OpeningReadFuture<'_> { self.read() } #[cfg(rpit)] + #[define_opaque(PendingReader)] fn read_fut( &mut self, ) -> impl std::future::Future, CantOpen>> { diff --git a/tests/ui/impl-trait/auto-trait-coherence.rs b/tests/ui/impl-trait/auto-trait-leakage/auto-trait-coherence.rs similarity index 95% rename from tests/ui/impl-trait/auto-trait-coherence.rs rename to tests/ui/impl-trait/auto-trait-leakage/auto-trait-coherence.rs index 0d7fef21cc92b..fdb981ea40646 100644 --- a/tests/ui/impl-trait/auto-trait-coherence.rs +++ b/tests/ui/impl-trait/auto-trait-leakage/auto-trait-coherence.rs @@ -5,6 +5,7 @@ trait OpaqueTrait {} impl OpaqueTrait for T {} type OpaqueType = impl OpaqueTrait; +#[define_opaque(OpaqueType)] fn mk_opaque() -> OpaqueType { () } diff --git a/tests/ui/impl-trait/auto-trait-coherence.stderr b/tests/ui/impl-trait/auto-trait-leakage/auto-trait-coherence.stderr similarity index 91% rename from tests/ui/impl-trait/auto-trait-coherence.stderr rename to tests/ui/impl-trait/auto-trait-leakage/auto-trait-coherence.stderr index e0f4c857717cf..cfeccc3d76664 100644 --- a/tests/ui/impl-trait/auto-trait-coherence.stderr +++ b/tests/ui/impl-trait/auto-trait-leakage/auto-trait-coherence.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<_>` - --> $DIR/auto-trait-coherence.rs:21:1 + --> $DIR/auto-trait-coherence.rs:22:1 | LL | impl AnotherTrait for T {} | -------------------------------- first implementation here diff --git a/tests/ui/impl-trait/auto-trait-contains-err.rs b/tests/ui/impl-trait/auto-trait-leakage/auto-trait-contains-err.rs similarity index 92% rename from tests/ui/impl-trait/auto-trait-contains-err.rs rename to tests/ui/impl-trait/auto-trait-leakage/auto-trait-contains-err.rs index d7f094211d7d9..8a2ebe301f119 100644 --- a/tests/ui/impl-trait/auto-trait-contains-err.rs +++ b/tests/ui/impl-trait/auto-trait-leakage/auto-trait-contains-err.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition=2021 +//@ edition: 2021 use std::future::Future; diff --git a/tests/ui/impl-trait/auto-trait-contains-err.stderr b/tests/ui/impl-trait/auto-trait-leakage/auto-trait-contains-err.stderr similarity index 100% rename from tests/ui/impl-trait/auto-trait-contains-err.stderr rename to tests/ui/impl-trait/auto-trait-leakage/auto-trait-contains-err.stderr diff --git a/tests/ui/impl-trait/auto-trait-leak-rpass.rs b/tests/ui/impl-trait/auto-trait-leakage/auto-trait-leak-rpass.rs similarity index 100% rename from tests/ui/impl-trait/auto-trait-leak-rpass.rs rename to tests/ui/impl-trait/auto-trait-leakage/auto-trait-leak-rpass.rs diff --git a/tests/ui/impl-trait/auto-trait-leak.rs b/tests/ui/impl-trait/auto-trait-leakage/auto-trait-leak.rs similarity index 100% rename from tests/ui/impl-trait/auto-trait-leak.rs rename to tests/ui/impl-trait/auto-trait-leakage/auto-trait-leak.rs diff --git a/tests/ui/impl-trait/auto-trait-leak.stderr b/tests/ui/impl-trait/auto-trait-leakage/auto-trait-leak.stderr similarity index 100% rename from tests/ui/impl-trait/auto-trait-leak.stderr rename to tests/ui/impl-trait/auto-trait-leakage/auto-trait-leak.stderr diff --git a/tests/ui/impl-trait/auto-trait-leak2.rs b/tests/ui/impl-trait/auto-trait-leakage/auto-trait-leak2.rs similarity index 100% rename from tests/ui/impl-trait/auto-trait-leak2.rs rename to tests/ui/impl-trait/auto-trait-leakage/auto-trait-leak2.rs diff --git a/tests/ui/impl-trait/auto-trait-leak2.stderr b/tests/ui/impl-trait/auto-trait-leakage/auto-trait-leak2.stderr similarity index 100% rename from tests/ui/impl-trait/auto-trait-leak2.stderr rename to tests/ui/impl-trait/auto-trait-leakage/auto-trait-leak2.stderr diff --git a/tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs b/tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs new file mode 100644 index 0000000000000..7f366fdcabd28 --- /dev/null +++ b/tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs @@ -0,0 +1,33 @@ +//@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +// When proving auto trait bounds, make sure that we depend on auto trait +// leakage if we can also prove it via an item bound. +fn is_send(_: T) {} + +fn direct() -> impl Send { + is_send(check(false)); // leaks auto traits, depends on `check` + 1u16 +} + +trait Indir: Send {} +impl Indir for u32 {} +fn indir() -> impl Indir { + is_send(check(false)); // leaks auto traits, depends on `check` + 1u32 +} + +fn check(b: bool) -> impl Sized { + if b { + // must not leak auto traits, as we otherwise get a query cycle. + is_send(direct()); + is_send(indir()); + } + 1u64 +} + +fn main() { + check(true); +} diff --git a/tests/ui/impl-trait/bound-normalization-pass.rs b/tests/ui/impl-trait/bound-normalization-pass.rs index 801187b6f5e0e..77bc0206830a2 100644 --- a/tests/ui/impl-trait/bound-normalization-pass.rs +++ b/tests/ui/impl-trait/bound-normalization-pass.rs @@ -77,6 +77,7 @@ mod opaque_types { type Ex = impl Trait::Assoc>; + #[define_opaque(Ex)] fn define() -> Ex { () } diff --git a/tests/ui/impl-trait/call_method_ambiguous.rs b/tests/ui/impl-trait/call_method_ambiguous.rs index 8fd6f727b73f6..6bcafc8ce1494 100644 --- a/tests/ui/impl-trait/call_method_ambiguous.rs +++ b/tests/ui/impl-trait/call_method_ambiguous.rs @@ -24,7 +24,7 @@ where fn foo(n: usize, m: &mut ()) -> impl Get + use<'_> { if n > 0 { let mut iter = foo(n - 1, m); - //[next]~^ type annotations needed + //[next]~^ ERROR type annotations needed assert_eq!(iter.get(), 1); } m diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl.rs b/tests/ui/impl-trait/call_method_on_inherent_impl.rs index 17f7cad660db1..0e333c3260a2f 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl.rs +++ b/tests/ui/impl-trait/call_method_on_inherent_impl.rs @@ -16,7 +16,7 @@ where fn my_foo() -> impl std::fmt::Debug { if false { let x = my_foo(); - //[next]~^ type annotations needed + //[next]~^ ERROR type annotations needed x.my_debug(); } () diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.rs b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.rs index abe60e5e45a34..4e4098b37f93b 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.rs +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.rs @@ -15,9 +15,9 @@ where fn my_foo() -> impl std::fmt::Debug { if false { let x = my_foo(); - //[next]~^ type annotations needed + //[next]~^ ERROR type annotations needed x.my_debug(); - //[current]~^ no method named `my_debug` found + //[current]~^ ERROR no method named `my_debug` found } () } @@ -25,7 +25,7 @@ fn my_foo() -> impl std::fmt::Debug { fn my_bar() -> impl std::fmt::Debug { if false { let x = &my_bar(); - //[next]~^ type annotations needed + //[next]~^ ERROR type annotations needed x.my_debug(); } () diff --git a/tests/ui/impl-trait/can-return-unconstrained-closure.rs b/tests/ui/impl-trait/can-return-unconstrained-closure.rs deleted file mode 100644 index 1f8bdbc5054d8..0000000000000 --- a/tests/ui/impl-trait/can-return-unconstrained-closure.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Test that we are special casing "outlives" for opaque types. -// -// The return type of a closure is not required to outlive the closure. As such -// the following code would not compile if we used a standard outlives check -// when checking the return type, because the return type of the closure would -// be `&ReEmpty i32`, and we don't allow `ReEmpty` to occur in the concrete -// type used for an opaque type. -// -// However, opaque types are special cased to include check all regions in the -// concrete type against the bound, which forces the return type to be -// `&'static i32` here. - -//@ build-pass (FIXME(62277): could be check-pass?) - -fn make_identity() -> impl Sized { - |x: &'static i32| x -} - -fn make_identity_static() -> impl Sized + 'static { - |x: &'static i32| x -} - -fn main() {} diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.rs b/tests/ui/impl-trait/coherence-treats-tait-ambig.rs deleted file mode 100644 index e8c1fcdd21334..0000000000000 --- a/tests/ui/impl-trait/coherence-treats-tait-ambig.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![feature(type_alias_impl_trait)] - -type T = impl Sized; - -struct Foo; - -impl Into for Foo { -//~^ ERROR conflicting implementations of trait `Into<_>` for type `Foo` - fn into(self) -> T { - Foo - } -} - -fn main() { - let _: T = Foo.into(); -} diff --git a/tests/ui/impl-trait/deduce-signature-from-supertrait.rs b/tests/ui/impl-trait/deduce-signature-from-supertrait.rs index 4e452994f72e7..28b6697f4bcb2 100644 --- a/tests/ui/impl-trait/deduce-signature-from-supertrait.rs +++ b/tests/ui/impl-trait/deduce-signature-from-supertrait.rs @@ -8,7 +8,8 @@ impl SuperExpectation for T {} type Foo = impl SuperExpectation; -fn bop(_: Foo) { +#[define_opaque(Foo)] +fn bop() { let _: Foo = |x| { let _ = x.to_string(); }; diff --git a/tests/ui/impl-trait/define-via-const.rs b/tests/ui/impl-trait/define-via-const.rs new file mode 100644 index 0000000000000..a4b9123654c45 --- /dev/null +++ b/tests/ui/impl-trait/define-via-const.rs @@ -0,0 +1,12 @@ +//@ check-pass + +#![feature(type_alias_impl_trait)] + +type Closure = impl Fn(u32) -> u32; + +#[define_opaque(Closure)] +const ADDER: Closure = |x| x + 1; + +fn main() { + let z = (ADDER)(1); +} diff --git a/tests/ui/impl-trait/define-via-extern.rs b/tests/ui/impl-trait/define-via-extern.rs new file mode 100644 index 0000000000000..599c31ff917fc --- /dev/null +++ b/tests/ui/impl-trait/define-via-extern.rs @@ -0,0 +1,16 @@ +#![feature(type_alias_impl_trait)] + +type Hi = impl Sized; + +extern "C" { + #[define_opaque(Hi)] fn foo(); + //~^ ERROR only functions, statics, and consts can define opaque types + + #[define_opaque(Hi)] static HI: Hi; + //~^ ERROR only functions, statics, and consts can define opaque types +} + +#[define_opaque(Hi)] +fn main() { + let _: Hi = 0; +} diff --git a/tests/ui/impl-trait/define-via-extern.stderr b/tests/ui/impl-trait/define-via-extern.stderr new file mode 100644 index 0000000000000..4a0ca5edd47d0 --- /dev/null +++ b/tests/ui/impl-trait/define-via-extern.stderr @@ -0,0 +1,14 @@ +error: only functions, statics, and consts can define opaque types + --> $DIR/define-via-extern.rs:6:5 + | +LL | #[define_opaque(Hi)] fn foo(); + | ^^^^^^^^^^^^^^^^^^^^ + +error: only functions, statics, and consts can define opaque types + --> $DIR/define-via-extern.rs:9:5 + | +LL | #[define_opaque(Hi)] static HI: Hi; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.rs b/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.rs index f1c196a154d52..ffb7a1008e0f0 100644 --- a/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.rs +++ b/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.rs @@ -1,6 +1,5 @@ //@ only-linux //@ compile-flags: --error-format=human --color=always -//@ error-pattern: the trait bound trait Foo: Bar {} @@ -18,3 +17,5 @@ fn foo() -> impl Foo { } fn main() {} + +//~? RAW the trait bound diff --git a/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg b/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg index 1a79a9d7efa25..9832e28e008b5 100644 --- a/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg +++ b/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg @@ -1,4 +1,4 @@ - + Bar for S { type E = impl std::marker::Send; fn foo() -> Self::E { - async {} //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias //~| 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/tests/ui/impl-trait/issues/issue-55872-2.stderr b/tests/ui/impl-trait/issues/issue-55872-2.stderr new file mode 100644 index 0000000000000..51a7dd00ade63 --- /dev/null +++ b/tests/ui/impl-trait/issues/issue-55872-2.stderr @@ -0,0 +1,16 @@ +error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-55872-2.rs:13:20 + | +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-2.rs:13:20 + | +LL | fn foo() -> Self::E { + | ^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/impl-trait/issue-55872-3.rs b/tests/ui/impl-trait/issues/issue-55872-3.rs similarity index 84% rename from tests/ui/impl-trait/issue-55872-3.rs rename to tests/ui/impl-trait/issues/issue-55872-3.rs index 50b9eb3ce0ecd..698e7f362341e 100644 --- a/tests/ui/impl-trait/issue-55872-3.rs +++ b/tests/ui/impl-trait/issues/issue-55872-3.rs @@ -13,6 +13,7 @@ impl Bar for S { type E = impl std::marker::Copy; fn foo() -> Self::E { //~^ ERROR : Copy` is not satisfied [E0277] + //~| ERROR type parameter `T` is part of concrete type async {} } } diff --git a/tests/ui/impl-trait/issues/issue-55872-3.stderr b/tests/ui/impl-trait/issues/issue-55872-3.stderr new file mode 100644 index 0000000000000..3281dcc3501d6 --- /dev/null +++ b/tests/ui/impl-trait/issues/issue-55872-3.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `{async block@$DIR/issue-55872-3.rs:17:9: 17:14}: Copy` is not satisfied + --> $DIR/issue-55872-3.rs:14:20 + | +LL | fn foo() -> Self::E { + | ^^^^^^^ the trait `Copy` is not implemented for `{async block@$DIR/issue-55872-3.rs:17:9: 17:14}` +... +LL | async {} + | -------- return type was inferred to be `{async block@$DIR/issue-55872-3.rs:17:9: 17:14}` here + +error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-55872-3.rs:14:20 + | +LL | fn foo() -> Self::E { + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/issue-55872.rs b/tests/ui/impl-trait/issues/issue-55872.rs similarity index 100% rename from tests/ui/impl-trait/issue-55872.rs rename to tests/ui/impl-trait/issues/issue-55872.rs index 10850f0a9335e..b76f8182b204f 100644 --- a/tests/ui/impl-trait/issue-55872.rs +++ b/tests/ui/impl-trait/issues/issue-55872.rs @@ -10,8 +10,8 @@ impl Bar for S { type E = impl Copy; 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/tests/ui/impl-trait/issues/issue-55872.stderr b/tests/ui/impl-trait/issues/issue-55872.stderr new file mode 100644 index 0000000000000..54e852f8edf8d --- /dev/null +++ b/tests/ui/impl-trait/issues/issue-55872.stderr @@ -0,0 +1,8 @@ +error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-55872.rs:12:20 + | +LL | fn foo() -> Self::E { + | ^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/impl-trait/issue-56445.rs b/tests/ui/impl-trait/issues/issue-56445.rs similarity index 100% rename from tests/ui/impl-trait/issue-56445.rs rename to tests/ui/impl-trait/issues/issue-56445.rs diff --git a/tests/ui/impl-trait/issues/issue-58504.rs b/tests/ui/impl-trait/issues/issue-58504.rs index 856e1297e58b8..57119671680ed 100644 --- a/tests/ui/impl-trait/issues/issue-58504.rs +++ b/tests/ui/impl-trait/issues/issue-58504.rs @@ -8,5 +8,5 @@ fn mk_gen() -> impl Coroutine { fn main() { let gens: [impl Coroutine;2] = [ mk_gen(), mk_gen() ]; - //~^ `impl Trait` is not allowed in the type of variable bindings + //~^ ERROR `impl Trait` is not allowed in the type of variable bindings } diff --git a/tests/ui/impl-trait/issues/issue-58956.rs b/tests/ui/impl-trait/issues/issue-58956.rs index a59de2379d8fa..8fb69aca9bcec 100644 --- a/tests/ui/impl-trait/issues/issue-58956.rs +++ b/tests/ui/impl-trait/issues/issue-58956.rs @@ -5,9 +5,9 @@ impl Lam for B {} pub struct Wrap(T); const _A: impl Lam = { - //~^ `impl Trait` is not allowed in const types + //~^ ERROR `impl Trait` is not allowed in const types let x: Wrap = Wrap(B); - //~^ `impl Trait` is not allowed in the type of variable bindings + //~^ ERROR `impl Trait` is not allowed in the type of variable bindings x.0 }; diff --git a/tests/ui/impl-trait/issue-68532.rs b/tests/ui/impl-trait/issues/issue-68532.rs similarity index 100% rename from tests/ui/impl-trait/issue-68532.rs rename to tests/ui/impl-trait/issues/issue-68532.rs diff --git a/tests/ui/impl-trait/issues/issue-70877.rs b/tests/ui/impl-trait/issues/issue-70877.rs index 6ced0bbba8b36..0f0a1b5187d3f 100644 --- a/tests/ui/impl-trait/issues/issue-70877.rs +++ b/tests/ui/impl-trait/issues/issue-70877.rs @@ -15,27 +15,27 @@ impl Iterator for Bar { } } -mod ret { - pub type FooRet = impl std::fmt::Debug; - pub fn quux(st: super::FooArg) -> FooRet { - Some(st.to_string()) - } +pub type FooRet = impl std::fmt::Debug; +#[define_opaque(FooRet)] +pub fn quux(st: FooArg) -> FooRet { + Some(st.to_string()) } -use ret::*; -mod foo { - pub type Foo = impl Iterator; - pub fn ham() -> Foo { - super::Bar(1) - } - pub fn oof(_: Foo) -> impl std::fmt::Debug { - //~^ ERROR: item does not constrain `Foo::{opaque#0}`, but has it in its signature - let mut bar = ham(); - let func = bar.next().unwrap(); - return func(&"oof"); - } +pub type Foo = impl Iterator; +#[define_opaque(Foo)] +pub fn ham() -> Foo { + //~^ ERROR: item does not constrain `FooRet::{opaque#0}` + Bar(1) +} +#[define_opaque(Foo)] +pub fn oof() -> impl std::fmt::Debug { + //~^ ERROR: item does not constrain `FooRet::{opaque#0}` + //~| ERROR: item does not constrain `Foo::{opaque#0}` + let mut bar = ham(); + let func = bar.next().unwrap(); + return func(&"oof"); + //~^ ERROR: opaque type's hidden type cannot be another opaque type } -use foo::*; fn main() { - let _ = oof(ham()); + let _ = oof(); } diff --git a/tests/ui/impl-trait/issues/issue-70877.stderr b/tests/ui/impl-trait/issues/issue-70877.stderr index 4b23a02aaee3f..b2f37c8af9ebe 100644 --- a/tests/ui/impl-trait/issues/issue-70877.stderr +++ b/tests/ui/impl-trait/issues/issue-70877.stderr @@ -1,15 +1,58 @@ -error: item does not constrain `Foo::{opaque#0}`, but has it in its signature - --> $DIR/issue-70877.rs:30:12 +error: item does not constrain `FooRet::{opaque#0}` + --> $DIR/issue-70877.rs:25:8 | -LL | pub fn oof(_: Foo) -> impl std::fmt::Debug { - | ^^^ +LL | pub fn ham() -> Foo { + | ^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/issue-70877.rs:26:20 + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/issue-70877.rs:18:19 | -LL | pub type Foo = impl Iterator; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub type FooRet = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error: item does not constrain `FooRet::{opaque#0}` + --> $DIR/issue-70877.rs:30:8 + | +LL | pub fn oof() -> impl std::fmt::Debug { + | ^^^ + | + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/issue-70877.rs:18:19 + | +LL | pub type FooRet = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + +error: item does not constrain `Foo::{opaque#0}` + --> $DIR/issue-70877.rs:30:8 + | +LL | pub fn oof() -> impl std::fmt::Debug { + | ^^^ + | + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/issue-70877.rs:23:16 + | +LL | pub type Foo = impl Iterator; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: opaque type's hidden type cannot be another opaque type from the same scope + --> $DIR/issue-70877.rs:35:12 + | +LL | return func(&"oof"); + | ^^^^^^^^^^^^ one of the two opaque types used here has to be outside its defining scope + | +note: opaque type whose hidden type is being assigned + --> $DIR/issue-70877.rs:30:17 + | +LL | pub fn oof() -> impl std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^^^^ +note: opaque type being used as hidden type + --> $DIR/issue-70877.rs:18:19 + | +LL | pub type FooRet = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors diff --git a/tests/ui/impl-trait/issues/issue-70971.rs b/tests/ui/impl-trait/issues/issue-70971.rs index 2f2c2e8f441a8..e7b51e0e6db4f 100644 --- a/tests/ui/impl-trait/issues/issue-70971.rs +++ b/tests/ui/impl-trait/issues/issue-70971.rs @@ -1,4 +1,4 @@ fn main() { let x : (impl Copy,) = (true,); - //~^ `impl Trait` is not allowed in the type of variable bindings + //~^ ERROR `impl Trait` is not allowed in the type of variable bindings } diff --git a/tests/ui/impl-trait/issue-72911.rs b/tests/ui/impl-trait/issues/issue-72911.rs similarity index 100% rename from tests/ui/impl-trait/issue-72911.rs rename to tests/ui/impl-trait/issues/issue-72911.rs diff --git a/tests/ui/impl-trait/issue-72911.stderr b/tests/ui/impl-trait/issues/issue-72911.stderr similarity index 100% rename from tests/ui/impl-trait/issue-72911.stderr rename to tests/ui/impl-trait/issues/issue-72911.stderr diff --git a/tests/ui/impl-trait/issues/issue-74282.rs b/tests/ui/impl-trait/issues/issue-74282.rs index 51bd5f67ed574..eb14a76a06925 100644 --- a/tests/ui/impl-trait/issues/issue-74282.rs +++ b/tests/ui/impl-trait/issues/issue-74282.rs @@ -3,7 +3,8 @@ type Closure = impl Fn() -> u64; struct Anonymous(Closure); -fn bop(_: Closure) { +#[define_opaque(Closure)] +fn bop() { let y = || -> Closure { || 3 }; Anonymous(|| { //~^ ERROR mismatched types diff --git a/tests/ui/impl-trait/issues/issue-74282.stderr b/tests/ui/impl-trait/issues/issue-74282.stderr index f8e85f7ae0008..7a49041cace7e 100644 --- a/tests/ui/impl-trait/issues/issue-74282.stderr +++ b/tests/ui/impl-trait/issues/issue-74282.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-74282.rs:8:15 + --> $DIR/issue-74282.rs:9:15 | LL | type Closure = impl Fn() -> u64; | ---------------- the expected opaque type @@ -14,7 +14,7 @@ LL | | }) | |_____^ expected opaque type, found closure | = note: expected opaque type `Closure` - found closure `{closure@$DIR/issue-74282.rs:8:15: 8:17}` + found closure `{closure@$DIR/issue-74282.rs:9:15: 9:17}` = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object note: tuple struct defined here @@ -24,7 +24,7 @@ LL | struct Anonymous(Closure); | ^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/issue-74282.rs:8:5 + --> $DIR/issue-74282.rs:9:5 | LL | / Anonymous(|| { LL | | @@ -38,8 +38,8 @@ LL | }); | + help: try adding a return type | -LL | fn bop(_: Closure) -> Anonymous { - | ++++++++++++ +LL | fn bop() -> Anonymous { + | ++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/issues/issue-77987.rs b/tests/ui/impl-trait/issues/issue-77987.rs index a7e7b067d5ff8..f134224071f71 100644 --- a/tests/ui/impl-trait/issues/issue-77987.rs +++ b/tests/ui/impl-trait/issues/issue-77987.rs @@ -5,17 +5,16 @@ pub trait Foo {} impl Foo for U {} -mod scope { - pub type Scope = impl super::Foo<()>; +pub type Scope = impl Foo<()>; - #[allow(unused)] - fn infer_scope() -> Scope { - () - } +#[allow(unused)] +#[define_opaque(Scope)] +fn infer_scope() -> Scope { + () } #[allow(unused)] -fn ice() -> impl Foo { +fn ice() -> impl Foo { loop {} } diff --git a/tests/ui/impl-trait/issues/issue-78722-2.rs b/tests/ui/impl-trait/issues/issue-78722-2.rs index e811620c03bad..ef4d26b697559 100644 --- a/tests/ui/impl-trait/issues/issue-78722-2.rs +++ b/tests/ui/impl-trait/issues/issue-78722-2.rs @@ -8,6 +8,7 @@ type F = impl core::future::Future; struct Bug { V1: [(); { + #[define_opaque(F)] fn concrete_use() -> F { //~^ ERROR future that resolves to `u8`, but it resolves to `()` async {} diff --git a/tests/ui/impl-trait/issues/issue-78722-2.stderr b/tests/ui/impl-trait/issues/issue-78722-2.stderr index 27b4b71283023..ede830bf72c8b 100644 --- a/tests/ui/impl-trait/issues/issue-78722-2.stderr +++ b/tests/ui/impl-trait/issues/issue-78722-2.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-78722-2.rs:16:20 + --> $DIR/issue-78722-2.rs:17:20 | LL | type F = impl core::future::Future; | -------------------------------------- the expected future @@ -10,10 +10,10 @@ LL | let f: F = async { 1 }; | expected due to this | = note: expected opaque type `F` - found `async` block `{async block@$DIR/issue-78722-2.rs:16:20: 16:25}` + found `async` block `{async block@$DIR/issue-78722-2.rs:17:20: 17:25}` -error[E0271]: expected `{async block@$DIR/issue-78722-2.rs:13:13: 13:18}` to be a future that resolves to `u8`, but it resolves to `()` - --> $DIR/issue-78722-2.rs:11:30 +error[E0271]: expected `{async block@$DIR/issue-78722-2.rs:14:13: 14:18}` to be a future that resolves to `u8`, but it resolves to `()` + --> $DIR/issue-78722-2.rs:12:30 | LL | fn concrete_use() -> F { | ^ expected `u8`, found `()` diff --git a/tests/ui/impl-trait/issues/issue-78722.rs b/tests/ui/impl-trait/issues/issue-78722.rs index 5518c2cf12a3b..374cd564b8e6f 100644 --- a/tests/ui/impl-trait/issues/issue-78722.rs +++ b/tests/ui/impl-trait/issues/issue-78722.rs @@ -5,6 +5,7 @@ struct Bug { V1: [(); { type F = impl core::future::Future; + #[define_opaque(F)] fn concrete_use() -> F { //~^ ERROR to be a future that resolves to `u8`, but it resolves to `()` async {} diff --git a/tests/ui/impl-trait/issues/issue-78722.stderr b/tests/ui/impl-trait/issues/issue-78722.stderr index 109bda0c5cd60..84c7dfb033849 100644 --- a/tests/ui/impl-trait/issues/issue-78722.stderr +++ b/tests/ui/impl-trait/issues/issue-78722.stderr @@ -1,5 +1,5 @@ error[E0658]: `async` blocks are not allowed in constants - --> $DIR/issue-78722.rs:12:20 + --> $DIR/issue-78722.rs:13:20 | LL | let f: F = async { 1 }; | ^^^^^^^^^^^ @@ -8,8 +8,8 @@ LL | let f: F = async { 1 }; = help: add `#![feature(const_async_blocks)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0271]: expected `{async block@$DIR/issue-78722.rs:10:13: 10:18}` to be a future that resolves to `u8`, but it resolves to `()` - --> $DIR/issue-78722.rs:8:30 +error[E0271]: expected `{async block@$DIR/issue-78722.rs:11:13: 11:18}` to be a future that resolves to `u8`, but it resolves to `()` + --> $DIR/issue-78722.rs:9:30 | LL | fn concrete_use() -> F { | ^ expected `u8`, found `()` diff --git a/tests/ui/impl-trait/issues/issue-79099.rs b/tests/ui/impl-trait/issues/issue-79099.rs index 757e61fb631ea..c2bad59045b22 100644 --- a/tests/ui/impl-trait/issues/issue-79099.rs +++ b/tests/ui/impl-trait/issues/issue-79099.rs @@ -1,8 +1,8 @@ struct Bug { V1: [(); { let f: impl core::future::Future = async { 1 }; - //~^ `impl Trait` is not allowed in the type of variable bindings - //~| expected identifier + //~^ ERROR `impl Trait` is not allowed in the type of variable bindings + //~| ERROR expected identifier 1 }], } diff --git a/tests/ui/impl-trait/issues/issue-84919.rs b/tests/ui/impl-trait/issues/issue-84919.rs index 0f911ba23ae20..1012024f21e1d 100644 --- a/tests/ui/impl-trait/issues/issue-84919.rs +++ b/tests/ui/impl-trait/issues/issue-84919.rs @@ -3,7 +3,7 @@ impl Trait for () {} fn foo<'a: 'a>() { let _x: impl Trait = (); - //~^ `impl Trait` is not allowed in the type of variable bindings + //~^ ERROR `impl Trait` is not allowed in the type of variable bindings } fn main() {} diff --git a/tests/ui/impl-trait/issues/issue-86201.rs b/tests/ui/impl-trait/issues/issue-86201.rs index cde0b86116033..19c68f7697baa 100644 --- a/tests/ui/impl-trait/issues/issue-86201.rs +++ b/tests/ui/impl-trait/issues/issue-86201.rs @@ -4,10 +4,13 @@ //@ check-pass type FunType = impl Fn<()>; -static STATIC_FN: FunType = some_fn; +#[define_opaque(FunType)] +fn foo() -> FunType { + some_fn +} fn some_fn() {} fn main() { - let _: >::Output = STATIC_FN(); + let _: >::Output = foo()(); } diff --git a/tests/ui/impl-trait/issues/issue-86642.rs b/tests/ui/impl-trait/issues/issue-86642.rs index 74be8779d4458..2feb045abee26 100644 --- a/tests/ui/impl-trait/issues/issue-86642.rs +++ b/tests/ui/impl-trait/issues/issue-86642.rs @@ -1,5 +1,5 @@ static x: impl Fn(&str) -> Result<&str, ()> = move |source| { - //~^ `impl Trait` is not allowed in static types + //~^ ERROR `impl Trait` is not allowed in static types let res = (move |source| Ok(source))(source); let res = res.or((move |source| Ok(source))(source)); res diff --git a/tests/ui/impl-trait/issues/issue-86800.rs b/tests/ui/impl-trait/issues/issue-86800.rs index ff1d273ae482e..cf82467b72ee3 100644 --- a/tests/ui/impl-trait/issues/issue-86800.rs +++ b/tests/ui/impl-trait/issues/issue-86800.rs @@ -4,45 +4,40 @@ use std::future::Future; -struct Connection { -} +struct Connection {} -trait Transaction { -} +trait Transaction {} struct TestTransaction<'conn> { - conn: &'conn Connection + conn: &'conn Connection, } -impl<'conn> Transaction for TestTransaction<'conn> { -} +impl<'conn> Transaction for TestTransaction<'conn> {} -struct Context { -} +struct Context {} type TransactionResult = Result; type TransactionFuture<'__, O> = impl '__ + Future>; +#[define_opaque(TransactionFuture)] fn execute_transaction_fut<'f, F, O>( //~^ ERROR: item does not constrain f: F, ) -> impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O> where - F: FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O> + 'f + F: FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O> + 'f, { f - //~^ ERROR expected generic lifetime parameter, found `'_` } impl Context { + #[define_opaque(TransactionFuture)] async fn do_transaction( //~^ ERROR: item does not constrain - &self, f: impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O> - ) -> TransactionResult - { - //~^ ERROR expected generic lifetime parameter, found `'_` - //~| ERROR: item does not constrain + &self, + f: impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O>, + ) -> TransactionResult { let mut conn = Connection {}; let mut transaction = TestTransaction { conn: &mut conn }; f(&mut transaction).await diff --git a/tests/ui/impl-trait/issues/issue-86800.stderr b/tests/ui/impl-trait/issues/issue-86800.stderr index fd9b8e7ac9993..e122fc229ccd3 100644 --- a/tests/ui/impl-trait/issues/issue-86800.stderr +++ b/tests/ui/impl-trait/issues/issue-86800.stderr @@ -1,72 +1,28 @@ -error: item does not constrain `TransactionFuture::{opaque#0}`, but has it in its signature - --> $DIR/issue-86800.rs:27:4 +error: item does not constrain `TransactionFuture::{opaque#0}` + --> $DIR/issue-86800.rs:24:4 | LL | fn execute_transaction_fut<'f, F, O>( | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/issue-86800.rs:25:34 + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/issue-86800.rs:21:34 | LL | type TransactionFuture<'__, O> = impl '__ + Future>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: item does not constrain `TransactionFuture::{opaque#0}`, but has it in its signature - --> $DIR/issue-86800.rs:39:14 +error: item does not constrain `TransactionFuture::{opaque#0}` + --> $DIR/issue-86800.rs:36:14 | LL | async fn do_transaction( | ^^^^^^^^^^^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/issue-86800.rs:25:34 + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/issue-86800.rs:21:34 | LL | type TransactionFuture<'__, O> = impl '__ + Future>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: item does not constrain `TransactionFuture::{opaque#0}`, but has it in its signature - --> $DIR/issue-86800.rs:43:5 - | -LL | / { -LL | | -LL | | -LL | | let mut conn = Connection {}; -LL | | let mut transaction = TestTransaction { conn: &mut conn }; -LL | | f(&mut transaction).await -LL | | } - | |_____^ - | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/issue-86800.rs:25:34 - | -LL | type TransactionFuture<'__, O> = impl '__ + Future>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/issue-86800.rs:34:5 - | -LL | type TransactionFuture<'__, O> = impl '__ + Future>; - | --- this generic parameter must be used with a generic lifetime parameter -... -LL | f - | ^ - -error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/issue-86800.rs:43:5 - | -LL | type TransactionFuture<'__, O> = impl '__ + Future>; - | --- this generic parameter must be used with a generic lifetime parameter -... -LL | / { -LL | | -LL | | -LL | | let mut conn = Connection {}; -LL | | let mut transaction = TestTransaction { conn: &mut conn }; -LL | | f(&mut transaction).await -LL | | } - | |_____^ - -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/issues/issue-87295.rs b/tests/ui/impl-trait/issues/issue-87295.rs index a765e14884b09..d8d1b462ca3b6 100644 --- a/tests/ui/impl-trait/issues/issue-87295.rs +++ b/tests/ui/impl-trait/issues/issue-87295.rs @@ -14,5 +14,5 @@ impl Struct { fn main() { let _do_not_waste: Struct> = Struct::new(()); - //~^ `impl Trait` is not allowed in the type of variable bindings + //~^ ERROR `impl Trait` is not allowed in the type of variable bindings } diff --git a/tests/ui/impl-trait/issues/issue-87450.rs b/tests/ui/impl-trait/issues/issue-87450.rs new file mode 100644 index 0000000000000..5a7759af1119f --- /dev/null +++ b/tests/ui/impl-trait/issues/issue-87450.rs @@ -0,0 +1,16 @@ +fn bar() -> impl Fn() { + wrap(wrap(wrap(wrap(foo())))) +} + +fn foo() -> impl Fn() { + //~^ WARN function cannot return without recursing + //~| ERROR cannot resolve opaque type + wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo()))))))) +} + +fn wrap(f: impl Fn()) -> impl Fn() { + move || f() +} + +fn main() { +} diff --git a/tests/ui/impl-trait/issues/issue-87450.stderr b/tests/ui/impl-trait/issues/issue-87450.stderr new file mode 100644 index 0000000000000..f0f8b5859c031 --- /dev/null +++ b/tests/ui/impl-trait/issues/issue-87450.stderr @@ -0,0 +1,21 @@ +error[E0720]: cannot resolve opaque type + --> $DIR/issue-87450.rs:5:13 + | +LL | fn foo() -> impl Fn() { + | ^^^^^^^^^ + +warning: function cannot return without recursing + --> $DIR/issue-87450.rs:5:1 + | +LL | fn foo() -> impl Fn() { + | ^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +... +LL | wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo()))))))) + | ----- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/issues/issue-89312.rs b/tests/ui/impl-trait/issues/issue-89312.rs index 3b0e976780beb..35cba41dd4b27 100644 --- a/tests/ui/impl-trait/issues/issue-89312.rs +++ b/tests/ui/impl-trait/issues/issue-89312.rs @@ -2,23 +2,21 @@ //@ check-pass -mod helper { - pub trait T { - type Item; - } +pub trait T { + type Item; +} - pub type Alias<'a> = impl T; +pub type Alias<'a> = impl T; - struct S; - impl<'a> T for &'a S { - type Item = &'a (); - } +struct S; +impl<'a> T for &'a S { + type Item = &'a (); +} - pub fn filter_positive<'a>() -> Alias<'a> { - &S - } +#[define_opaque(Alias)] +pub fn filter_positive<'a>() -> Alias<'a> { + &S } -use helper::*; fn with_positive(fun: impl Fn(Alias<'_>)) { fun(filter_positive()); diff --git a/tests/ui/impl-trait/issues/issue-99073-2.rs b/tests/ui/impl-trait/issues/issue-99073-2.rs new file mode 100644 index 0000000000000..bfb8850857de2 --- /dev/null +++ b/tests/ui/impl-trait/issues/issue-99073-2.rs @@ -0,0 +1,17 @@ +use std::fmt::Display; + +fn main() { + test("hi", true); +} + +fn test(t: T, recurse: bool) -> impl Display { + let f = || { + let i: u32 = test::(-1, false); + //~^ ERROR expected generic type parameter, found `i32` + println!("{i}"); + }; + if recurse { + f(); + } + t +} diff --git a/tests/ui/impl-trait/issues/issue-99073-2.stderr b/tests/ui/impl-trait/issues/issue-99073-2.stderr new file mode 100644 index 0000000000000..519530b539625 --- /dev/null +++ b/tests/ui/impl-trait/issues/issue-99073-2.stderr @@ -0,0 +1,12 @@ +error[E0792]: expected generic type parameter, found `i32` + --> $DIR/issue-99073-2.rs:9:22 + | +LL | fn test(t: T, recurse: bool) -> impl Display { + | - this generic parameter must be used with a generic type parameter +LL | let f = || { +LL | let i: u32 = test::(-1, false); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/issues/issue-99073.rs b/tests/ui/impl-trait/issues/issue-99073.rs new file mode 100644 index 0000000000000..d2a2a61a40845 --- /dev/null +++ b/tests/ui/impl-trait/issues/issue-99073.rs @@ -0,0 +1,8 @@ +fn main() { + let _ = fix(|_: &dyn Fn()| {}); +} + +fn fix(f: F) -> impl Fn() { + move || f(fix(&f)) + //~^ ERROR expected generic type parameter, found `&F` +} diff --git a/tests/ui/impl-trait/issues/issue-99073.stderr b/tests/ui/impl-trait/issues/issue-99073.stderr new file mode 100644 index 0000000000000..1917c1bfd6b4f --- /dev/null +++ b/tests/ui/impl-trait/issues/issue-99073.stderr @@ -0,0 +1,11 @@ +error[E0792]: expected generic type parameter, found `&F` + --> $DIR/issue-99073.rs:6:13 + | +LL | fn fix(f: F) -> impl Fn() { + | - this generic parameter must be used with a generic type parameter +LL | move || f(fix(&f)) + | ^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/issues/issue-99348-impl-compatibility.rs b/tests/ui/impl-trait/issues/issue-99348-impl-compatibility.rs index b05579f216620..4937794b040c8 100644 --- a/tests/ui/impl-trait/issues/issue-99348-impl-compatibility.rs +++ b/tests/ui/impl-trait/issues/issue-99348-impl-compatibility.rs @@ -6,7 +6,7 @@ type Tait = impl Sized; impl Foo for Concrete { type Item = Concrete; - //~^ type mismatch resolving + //~^ ERROR type mismatch resolving } impl Bar for Concrete { @@ -21,6 +21,7 @@ trait Bar { type Other; } +#[define_opaque(Tait)] fn tait() -> Tait {} fn main() {} diff --git a/tests/ui/impl-trait/issue-99642-2.rs b/tests/ui/impl-trait/issues/issue-99642-2.rs similarity index 82% rename from tests/ui/impl-trait/issue-99642-2.rs rename to tests/ui/impl-trait/issues/issue-99642-2.rs index acbf3e3e2a024..d8d367a5d35f0 100644 --- a/tests/ui/impl-trait/issue-99642-2.rs +++ b/tests/ui/impl-trait/issues/issue-99642-2.rs @@ -2,7 +2,8 @@ #![feature(type_alias_impl_trait)] type Opq = impl Sized; +#[define_opaque(Opq)] fn test() -> impl Iterator { Box::new(0..) as Box> } -fn main(){} +fn main() {} diff --git a/tests/ui/impl-trait/issue-99642.rs b/tests/ui/impl-trait/issues/issue-99642.rs similarity index 100% rename from tests/ui/impl-trait/issue-99642.rs rename to tests/ui/impl-trait/issues/issue-99642.rs diff --git a/tests/ui/impl-trait/issue-99914.rs b/tests/ui/impl-trait/issues/issue-99914.rs similarity index 100% rename from tests/ui/impl-trait/issue-99914.rs rename to tests/ui/impl-trait/issues/issue-99914.rs diff --git a/tests/ui/impl-trait/issue-99914.stderr b/tests/ui/impl-trait/issues/issue-99914.stderr similarity index 100% rename from tests/ui/impl-trait/issue-99914.stderr rename to tests/ui/impl-trait/issues/issue-99914.stderr diff --git a/tests/ui/impl-trait/lifetimes2.rs b/tests/ui/impl-trait/lifetimes2.rs deleted file mode 100644 index facf2f75bc4d0..0000000000000 --- a/tests/ui/impl-trait/lifetimes2.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ check-pass - -pub fn keys<'a>(x: &'a Result) -> impl std::fmt::Debug + 'a { - match x { - Ok(map) => Ok(map), - Err(map) => Err(map), - } -} - -fn main() {} diff --git a/tests/ui/impl-trait/method-resolution4.rs b/tests/ui/impl-trait/method-resolution4.rs index 5c8813ed79224..90e7850cad51e 100644 --- a/tests/ui/impl-trait/method-resolution4.rs +++ b/tests/ui/impl-trait/method-resolution4.rs @@ -11,7 +11,7 @@ fn foo(b: bool) -> impl Iterator { if b { foo(false).next().unwrap(); - //[next]~^ type annotations needed + //[next]~^ ERROR type annotations needed } std::iter::empty() } diff --git a/tests/ui/impl-trait/multiple-lifetimes/error-handling-2.rs b/tests/ui/impl-trait/multiple-lifetimes/error-handling-2.rs index 2a2be6b742992..21ca558153de2 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/error-handling-2.rs +++ b/tests/ui/impl-trait/multiple-lifetimes/error-handling-2.rs @@ -7,6 +7,7 @@ impl Copy for CopyIfEq {} type E<'a, 'b> = impl Sized; +#[define_opaque(E)] fn foo<'a: 'b, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { let v = CopyIfEq::<*mut _, *mut _>(&mut { x }, &mut y); diff --git a/tests/ui/impl-trait/multiple-lifetimes/error-handling-2.stderr b/tests/ui/impl-trait/multiple-lifetimes/error-handling-2.stderr index b968592beffd9..712cbbecd74b4 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/error-handling-2.stderr +++ b/tests/ui/impl-trait/multiple-lifetimes/error-handling-2.stderr @@ -1,9 +1,9 @@ error[E0700]: hidden type for `E<'b, 'c>` captures lifetime that does not appear in bounds - --> $DIR/error-handling-2.rs:22:5 + --> $DIR/error-handling-2.rs:23:5 | LL | type E<'a, 'b> = impl Sized; | ---------- opaque type defined here -LL | +... LL | fn foo<'a: 'b, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { | -- hidden type `*mut &'a i32` captures the lifetime `'a` as defined here ... diff --git a/tests/ui/impl-trait/multiple-lifetimes/error-handling.rs b/tests/ui/impl-trait/multiple-lifetimes/error-handling.rs index 367e7f4e6eafb..0f3b7506318bc 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/error-handling.rs +++ b/tests/ui/impl-trait/multiple-lifetimes/error-handling.rs @@ -7,6 +7,7 @@ impl Copy for CopyIfEq {} type E<'a, 'b> = impl Sized; +#[define_opaque(E)] fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { let v = CopyIfEq::<*mut _, *mut _>(&mut { x }, &mut y); diff --git a/tests/ui/impl-trait/multiple-lifetimes/error-handling.stderr b/tests/ui/impl-trait/multiple-lifetimes/error-handling.stderr index 945fb0fc618fb..3732307ca354b 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/error-handling.stderr +++ b/tests/ui/impl-trait/multiple-lifetimes/error-handling.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/error-handling.rs:20:16 + --> $DIR/error-handling.rs:21:16 | LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { | -- -- lifetime `'b` defined here diff --git a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-type-alias-impl-trait.rs b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-type-alias-impl-trait.rs index 6f90160866ba4..6a1cb61b8ba28 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-type-alias-impl-trait.rs +++ b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-type-alias-impl-trait.rs @@ -10,6 +10,7 @@ impl Trait<'_, '_> for T {} type Foo<'a, 'b> = impl Trait<'a, 'b>; +#[define_opaque(Foo)] fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> Foo<'a, 'b> { // In this simple case, you have a hidden type `(&'0 u8, &'1 u8)` and constraints like // diff --git a/tests/ui/impl-trait/negative-reasoning.rs b/tests/ui/impl-trait/negative-reasoning.rs index 0474dc0beda6d..0d4a1ba75d8c9 100644 --- a/tests/ui/impl-trait/negative-reasoning.rs +++ b/tests/ui/impl-trait/negative-reasoning.rs @@ -5,6 +5,7 @@ trait OpaqueTrait {} impl OpaqueTrait for T {} type OpaqueType = impl OpaqueTrait; +#[define_opaque(OpaqueType)] fn mk_opaque() -> OpaqueType { () } diff --git a/tests/ui/impl-trait/negative-reasoning.stderr b/tests/ui/impl-trait/negative-reasoning.stderr index 631784c817b87..2918be5135cec 100644 --- a/tests/ui/impl-trait/negative-reasoning.stderr +++ b/tests/ui/impl-trait/negative-reasoning.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<_>` - --> $DIR/negative-reasoning.rs:19:1 + --> $DIR/negative-reasoning.rs:20:1 | LL | impl AnotherTrait for T {} | ------------------------------------------- first implementation here diff --git a/tests/ui/impl-trait/nested-return-type2-tait.rs b/tests/ui/impl-trait/nested-return-type2-tait.rs index 7cb98cfe0601f..aa871df9d9c5b 100644 --- a/tests/ui/impl-trait/nested-return-type2-tait.rs +++ b/tests/ui/impl-trait/nested-return-type2-tait.rs @@ -25,10 +25,10 @@ type Sendable = impl Send; // the hidden type. We already have obligations registered on the inference // var to make it uphold the `: Duh` bound on `Trait::Assoc`. The opaque // type does not implement `Duh`, but if its hidden type does. +#[define_opaque(Sendable)] fn foo() -> impl Trait { //~^ WARN opaque type `impl Trait` does not satisfy its associated type bounds || 42 } -fn main() { -} +fn main() {} diff --git a/tests/ui/impl-trait/nested-return-type2-tait.stderr b/tests/ui/impl-trait/nested-return-type2-tait.stderr index 4383e8ab3a0cd..8105990eac4a3 100644 --- a/tests/ui/impl-trait/nested-return-type2-tait.stderr +++ b/tests/ui/impl-trait/nested-return-type2-tait.stderr @@ -1,5 +1,5 @@ warning: opaque type `impl Trait` does not satisfy its associated type bounds - --> $DIR/nested-return-type2-tait.rs:28:24 + --> $DIR/nested-return-type2-tait.rs:29:24 | LL | type Assoc: Duh; | --- this associated type bound is unsatisfied for `Sendable` diff --git a/tests/ui/impl-trait/nested-return-type2-tait2.rs b/tests/ui/impl-trait/nested-return-type2-tait2.rs index 574602079d434..bd89dad7dfd62 100644 --- a/tests/ui/impl-trait/nested-return-type2-tait2.rs +++ b/tests/ui/impl-trait/nested-return-type2-tait2.rs @@ -26,9 +26,9 @@ type Traitable = impl Trait; // the hidden type. We already have obligations registered on the inference // var to make it uphold the `: Duh` bound on `Trait::Assoc`. The opaque // type does not implement `Duh`, even if its hidden type does. So we error out. +#[define_opaque(Traitable)] fn foo() -> Traitable { || 42 } -fn main() { -} +fn main() {} diff --git a/tests/ui/impl-trait/nested-return-type2-tait3.rs b/tests/ui/impl-trait/nested-return-type2-tait3.rs index e342973178257..83bf441181a45 100644 --- a/tests/ui/impl-trait/nested-return-type2-tait3.rs +++ b/tests/ui/impl-trait/nested-return-type2-tait3.rs @@ -25,9 +25,9 @@ type Traitable = impl Trait; // the hidden type. We already have obligations registered on the inference // var to make it uphold the `: Duh` bound on `Trait::Assoc`. The opaque // type does not implement `Duh`, even if its hidden type does. So we error out. +#[define_opaque(Traitable)] fn foo() -> Traitable { || 42 } -fn main() { -} +fn main() {} diff --git a/tests/ui/impl-trait/nested-return-type3-tait.rs b/tests/ui/impl-trait/nested-return-type3-tait.rs index 05759fb26979c..fb2e4d875435b 100644 --- a/tests/ui/impl-trait/nested-return-type3-tait.rs +++ b/tests/ui/impl-trait/nested-return-type3-tait.rs @@ -16,10 +16,10 @@ impl Trait for F { type Sendable = impl Send; +#[define_opaque(Sendable)] fn foo() -> impl Trait { //~^ WARN opaque type `impl Trait` does not satisfy its associated type bounds 42 } -fn main() { -} +fn main() {} diff --git a/tests/ui/impl-trait/nested-return-type3-tait.stderr b/tests/ui/impl-trait/nested-return-type3-tait.stderr index d32944a0d7214..bb1f524b99250 100644 --- a/tests/ui/impl-trait/nested-return-type3-tait.stderr +++ b/tests/ui/impl-trait/nested-return-type3-tait.stderr @@ -1,5 +1,5 @@ warning: opaque type `impl Trait` does not satisfy its associated type bounds - --> $DIR/nested-return-type3-tait.rs:19:24 + --> $DIR/nested-return-type3-tait.rs:20:24 | LL | type Assoc: Duh; | --- this associated type bound is unsatisfied for `Sendable` diff --git a/tests/ui/impl-trait/nested-return-type3-tait2.rs b/tests/ui/impl-trait/nested-return-type3-tait2.rs index 927fa8d596b9d..f3fda61e92078 100644 --- a/tests/ui/impl-trait/nested-return-type3-tait2.rs +++ b/tests/ui/impl-trait/nested-return-type3-tait2.rs @@ -18,9 +18,9 @@ type Sendable = impl Send; type Traitable = impl Trait; //~^ WARN opaque type `Traitable` does not satisfy its associated type bounds +#[define_opaque(Traitable)] fn foo() -> Traitable { 42 } -fn main() { -} +fn main() {} diff --git a/tests/ui/impl-trait/nested-return-type3-tait3.rs b/tests/ui/impl-trait/nested-return-type3-tait3.rs index 5b3b2d2e1986e..d2acee9cafd3e 100644 --- a/tests/ui/impl-trait/nested-return-type3-tait3.rs +++ b/tests/ui/impl-trait/nested-return-type3-tait3.rs @@ -17,9 +17,9 @@ impl Trait for F { type Traitable = impl Trait; //~^ WARN opaque type `Traitable` does not satisfy its associated type bounds +#[define_opaque(Traitable)] fn foo() -> Traitable { 42 } -fn main() { -} +fn main() {} diff --git a/tests/ui/impl-trait/nested_impl_trait.rs b/tests/ui/impl-trait/nested_impl_trait.rs index 760102794c34e..3e985b5dcca93 100644 --- a/tests/ui/impl-trait/nested_impl_trait.rs +++ b/tests/ui/impl-trait/nested_impl_trait.rs @@ -9,7 +9,7 @@ fn bad_in_ret_position(x: impl Into) -> impl Into { x } fn bad_in_fn_syntax(x: fn() -> impl Into) {} //~^ ERROR nested `impl Trait` is not allowed -//~| `impl Trait` is not allowed in `fn` pointer +//~| ERROR `impl Trait` is not allowed in `fn` pointer fn bad_in_arg_position(_: impl Into) { } //~^ ERROR nested `impl Trait` is not allowed diff --git a/tests/ui/impl-trait/no-method-suggested-traits.rs b/tests/ui/impl-trait/no-method-suggested-traits.rs index 6fc96f27a671e..5a9361310c349 100644 --- a/tests/ui/impl-trait/no-method-suggested-traits.rs +++ b/tests/ui/impl-trait/no-method-suggested-traits.rs @@ -1,4 +1,6 @@ //@ aux-build:no_method_suggested_traits.rs +//@ dont-require-annotations: HELP + extern crate no_method_suggested_traits; struct Foo; @@ -22,9 +24,9 @@ fn main() { 1u32.method(); //~^ ERROR no method named - //~|items from traits can only be used if the trait is in scope + //~| HELP items from traits can only be used if the trait is in scope std::rc::Rc::new(&mut Box::new(&1u32)).method(); - //~^items from traits can only be used if the trait is in scope + //~^ HELP items from traits can only be used if the trait is in scope //~| ERROR no method named `method` found for struct 'a'.method(); diff --git a/tests/ui/impl-trait/no-method-suggested-traits.stderr b/tests/ui/impl-trait/no-method-suggested-traits.stderr index 0a974668188e2..64202513ae2b7 100644 --- a/tests/ui/impl-trait/no-method-suggested-traits.stderr +++ b/tests/ui/impl-trait/no-method-suggested-traits.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `method` found for type `u32` in the current scope - --> $DIR/no-method-suggested-traits.rs:23:10 + --> $DIR/no-method-suggested-traits.rs:25:10 | LL | 1u32.method(); | ^^^^^^ @@ -21,7 +21,7 @@ LL | 1u32.method2(); | + 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 + --> $DIR/no-method-suggested-traits.rs:28:44 | LL | std::rc::Rc::new(&mut Box::new(&1u32)).method(); | ^^^^^^ @@ -43,7 +43,7 @@ LL | std::rc::Rc::new(&mut Box::new(&1u32)).method2(); | + error[E0599]: no method named `method` found for type `char` in the current scope - --> $DIR/no-method-suggested-traits.rs:30:9 + --> $DIR/no-method-suggested-traits.rs:32:9 | LL | fn method(&self) {} | ------ the method is available for `char` here @@ -62,7 +62,7 @@ LL | 'a'.method2(); | + 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 + --> $DIR/no-method-suggested-traits.rs:34:43 | LL | std::rc::Rc::new(&mut Box::new(&'a')).method(); | ^^^^^^ @@ -78,7 +78,7 @@ LL | std::rc::Rc::new(&mut Box::new(&'a')).method2(); | + error[E0599]: no method named `method` found for type `i32` in the current scope - --> $DIR/no-method-suggested-traits.rs:35:10 + --> $DIR/no-method-suggested-traits.rs:37:10 | LL | 1i32.method(); | ^^^^^^ @@ -99,7 +99,7 @@ LL | 1i32.method3(); | + 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 + --> $DIR/no-method-suggested-traits.rs:39:44 | LL | std::rc::Rc::new(&mut Box::new(&1i32)).method(); | ^^^^^^ @@ -115,7 +115,7 @@ LL | std::rc::Rc::new(&mut Box::new(&1i32)).method3(); | + error[E0599]: no method named `method` found for struct `Foo` in the current scope - --> $DIR/no-method-suggested-traits.rs:40:9 + --> $DIR/no-method-suggested-traits.rs:42:9 | LL | struct Foo; | ---------- method `method` not found for this struct @@ -131,7 +131,7 @@ LL | Foo.method(); candidate #4: `no_method_suggested_traits::qux::PrivPub` 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 + --> $DIR/no-method-suggested-traits.rs:44:43 | LL | std::rc::Rc::new(&mut Box::new(&Foo)).method(); | ^^^^^^ method not found in `Rc<&mut Box<&Foo>>` @@ -144,85 +144,85 @@ LL | std::rc::Rc::new(&mut Box::new(&Foo)).method(); candidate #4: `no_method_suggested_traits::qux::PrivPub` error[E0599]: no method named `method2` found for type `u64` in the current scope - --> $DIR/no-method-suggested-traits.rs:45:10 + --> $DIR/no-method-suggested-traits.rs:47:10 | LL | 1u64.method2(); | ^^^^^^^ method not found in `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 - --> $DIR/no-method-suggested-traits.rs:8:5 + --> $DIR/no-method-suggested-traits.rs:10:5 | LL | pub trait Bar { | ^^^^^^^^^^^^^ 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 + --> $DIR/no-method-suggested-traits.rs:49:44 | LL | std::rc::Rc::new(&mut Box::new(&1u64)).method2(); | ^^^^^^^ 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 - --> $DIR/no-method-suggested-traits.rs:8:5 + --> $DIR/no-method-suggested-traits.rs:10:5 | LL | pub trait Bar { | ^^^^^^^^^^^^^ error[E0599]: no method named `method2` found for struct `no_method_suggested_traits::Foo` in the current scope - --> $DIR/no-method-suggested-traits.rs:50:37 + --> $DIR/no-method-suggested-traits.rs:52:37 | LL | no_method_suggested_traits::Foo.method2(); | ^^^^^^^ method not found in `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 - --> $DIR/no-method-suggested-traits.rs:8:5 + --> $DIR/no-method-suggested-traits.rs:10:5 | LL | pub trait Bar { | ^^^^^^^^^^^^^ 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 + --> $DIR/no-method-suggested-traits.rs:54:71 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method2(); | ^^^^^^^ 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: `foo::Bar` defines an item `method2`, perhaps you need to implement it - --> $DIR/no-method-suggested-traits.rs:8:5 + --> $DIR/no-method-suggested-traits.rs:10:5 | LL | pub trait Bar { | ^^^^^^^^^^^^^ error[E0599]: no method named `method2` found for enum `no_method_suggested_traits::Bar` in the current scope - --> $DIR/no-method-suggested-traits.rs:54:40 + --> $DIR/no-method-suggested-traits.rs:56:40 | LL | no_method_suggested_traits::Bar::X.method2(); | ^^^^^^^ method not found in `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 - --> $DIR/no-method-suggested-traits.rs:8:5 + --> $DIR/no-method-suggested-traits.rs:10:5 | LL | pub trait Bar { | ^^^^^^^^^^^^^ 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 + --> $DIR/no-method-suggested-traits.rs:58:74 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method2(); | ^^^^^^^ 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: `foo::Bar` defines an item `method2`, perhaps you need to implement it - --> $DIR/no-method-suggested-traits.rs:8:5 + --> $DIR/no-method-suggested-traits.rs:10:5 | LL | pub trait Bar { | ^^^^^^^^^^^^^ error[E0599]: no method named `method3` found for struct `Foo` in the current scope - --> $DIR/no-method-suggested-traits.rs:59:9 + --> $DIR/no-method-suggested-traits.rs:61:9 | LL | struct Foo; | ---------- method `method3` not found for this struct @@ -235,7 +235,7 @@ LL | Foo.method3(); candidate #1: `PubPub` 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 + --> $DIR/no-method-suggested-traits.rs:63:43 | LL | std::rc::Rc::new(&mut Box::new(&Foo)).method3(); | ^^^^^^^ method not found in `Rc<&mut Box<&Foo>>` @@ -245,7 +245,7 @@ LL | std::rc::Rc::new(&mut Box::new(&Foo)).method3(); 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 + --> $DIR/no-method-suggested-traits.rs:65:12 | LL | enum Bar { X } | -------- method `method3` not found for this enum @@ -258,7 +258,7 @@ LL | Bar::X.method3(); candidate #1: `PubPub` 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 + --> $DIR/no-method-suggested-traits.rs:67:46 | LL | std::rc::Rc::new(&mut Box::new(&Bar::X)).method3(); | ^^^^^^^ method not found in `Rc<&mut Box<&Bar>>` @@ -268,37 +268,37 @@ LL | std::rc::Rc::new(&mut Box::new(&Bar::X)).method3(); 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 + --> $DIR/no-method-suggested-traits.rs:71:13 | LL | 1_usize.method3(); | ^^^^^^^ method not found in `usize` 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 + --> $DIR/no-method-suggested-traits.rs:72:47 | LL | std::rc::Rc::new(&mut Box::new(&1_usize)).method3(); | ^^^^^^^ 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 + --> $DIR/no-method-suggested-traits.rs:73:37 | LL | no_method_suggested_traits::Foo.method3(); | ^^^^^^^ method not found in `Foo` 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 + --> $DIR/no-method-suggested-traits.rs:74:71 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method3(); | ^^^^^^^ method not found in `Rc<&mut Box<&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 + --> $DIR/no-method-suggested-traits.rs:76:40 | LL | no_method_suggested_traits::Bar::X.method3(); | ^^^^^^^ method not found in `Bar` 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 + --> $DIR/no-method-suggested-traits.rs:77:74 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method3(); | ^^^^^^^ method not found in `Rc<&mut Box<&Bar>>` diff --git a/tests/ui/impl-trait/non-defining-uses/as-projection-term.next.stderr b/tests/ui/impl-trait/non-defining-uses/as-projection-term.next.stderr new file mode 100644 index 0000000000000..08c8365b1805a --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/as-projection-term.next.stderr @@ -0,0 +1,12 @@ +error[E0792]: expected generic lifetime parameter, found `'_` + --> $DIR/as-projection-term.rs:14:19 + | +LL | fn recur<'a>() -> impl Sized + 'a { + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | prove_proj(|| recur()); + | ^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/non-defining-uses/as-projection-term.rs b/tests/ui/impl-trait/non-defining-uses/as-projection-term.rs new file mode 100644 index 0000000000000..4c5adc7a00ab1 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/as-projection-term.rs @@ -0,0 +1,17 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] check-pass + +fn prove_proj(_: impl FnOnce() -> R) {} +fn recur<'a>() -> impl Sized + 'a { + // The closure has the signature `fn() -> opaque<'1>`. `prove_proj` + // requires us to prove `>::Output = opaque<'2>`. + // The old solver uses `replace_opaque_types_with_infer` during normalization + // to replace `opaque<'2>` with its hidden type. If that hidden type is still an + // inference variable at this point, we unify it with `opaque<'1>` and + // end up ignoring that defining use as the hidden type is equal to its key. + prove_proj(|| recur()); + //[next]~^ ERROR expected generic lifetime parameter, found `'_` +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs new file mode 100644 index 0000000000000..339277fec37e1 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs @@ -0,0 +1,12 @@ +// Regression test for ICE from issue #140545 +// The error message is confusing and wrong, but that's a different problem (#139350) +//@ edition:2018 + +trait Foo {} +fn a(x: impl Foo) -> impl Foo { + if true { x } else { a(a(x)) } + //~^ ERROR: expected generic type parameter, found `impl Foo` [E0792] + //~| ERROR: type parameter `impl Foo` is part of concrete type but not used in parameter list for the `impl Trait` type alias +} + +fn main(){} diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.stderr b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.stderr new file mode 100644 index 0000000000000..1b02811e31b7d --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.stderr @@ -0,0 +1,17 @@ +error[E0792]: expected generic type parameter, found `impl Foo` + --> $DIR/double-wrap-with-defining-use.rs:7:26 + | +LL | fn a(x: impl Foo) -> impl Foo { + | -------- this generic parameter must be used with a generic type parameter +LL | if true { x } else { a(a(x)) } + | ^^^^^^^ + +error: type parameter `impl Foo` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/double-wrap-with-defining-use.rs:7:26 + | +LL | if true { x } else { a(a(x)) } + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/normalize-tait-in-const.stderr b/tests/ui/impl-trait/normalize-tait-in-const.stderr index c6cd1b139c5b0..2b6825b1ac672 100644 --- a/tests/ui/impl-trait/normalize-tait-in-const.stderr +++ b/tests/ui/impl-trait/normalize-tait-in-const.stderr @@ -17,6 +17,33 @@ note: `Fn` can't be used with `~const` because it isn't annotated with `#[const_ --> $SRC_DIR/core/src/ops/function.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +error: unconstrained opaque type + --> $DIR/normalize-tait-in-const.rs:14:26 + | +LL | pub type Alias<'a> = impl T; + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Alias` must be used in combination with a concrete type within the same crate + +error[E0308]: mismatched types + --> $DIR/normalize-tait-in-const.rs:22:9 + | +LL | pub type Alias<'a> = impl T; + | --------------------- the expected opaque type +... +LL | pub const fn filter_positive<'a>() -> &'a Alias<'a> { + | ------------- expected `&'a foo::Alias<'a>` because of return type +LL | &&S + | ^^^ expected `&Alias<'_>`, found `&&S` + | + = note: expected reference `&'a foo::Alias<'a>` + found reference `&&S` +note: this item must have a `#[define_opaque(foo::Alias)]` attribute to be able to define hidden types + --> $DIR/normalize-tait-in-const.rs:21:18 + | +LL | pub const fn filter_positive<'a>() -> &'a Alias<'a> { + | ^^^^^^^^^^^^^^^ + error[E0015]: cannot call non-const closure in constant functions --> $DIR/normalize-tait-in-const.rs:28:5 | @@ -25,6 +52,7 @@ LL | fun(filter_positive()); | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0015`. +Some errors have detailed explanations: E0015, E0308. +For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr b/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr index 4198095db65ff..73e7890f91fd1 100644 --- a/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr +++ b/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr @@ -75,7 +75,7 @@ error[E0061]: this function takes 0 arguments but 1 argument was supplied --> $DIR/opaque-used-in-extraneous-argument.rs:20:5 | LL | open_parent(&old_path) - | ^^^^^^^^^^^ --------- unexpected argument of type `&impl Fn<{type error}> + FnOnce<{type error}, Output = {type error}> + 'static` + | ^^^^^^^^^^^ --------- unexpected argument of type `&impl Fn<{type error}> + FnOnce<{type error}, Output = {type error}> + '_` | note: function defined here --> $DIR/opaque-used-in-extraneous-argument.rs:12:4 diff --git a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs index 161fe23c89995..8e30973cd3a18 100644 --- a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs +++ b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs @@ -1,6 +1,5 @@ fn no_elided_lt() -> impl Sized + use<'_> {} //~^ ERROR missing lifetime specifier -//~| ERROR expected lifetime parameter in `use<...>` precise captures list, found `'_` fn static_lt() -> impl Sized + use<'static> {} //~^ ERROR expected lifetime parameter in `use<...>` precise captures list, found `'static` diff --git a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr index c8dac3a69cd98..98f629f52cf34 100644 --- a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr +++ b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr @@ -12,26 +12,20 @@ LL + fn no_elided_lt() -> impl Sized + use<'static> {} | error[E0261]: use of undeclared lifetime name `'missing` - --> $DIR/bad-lifetimes.rs:8:37 + --> $DIR/bad-lifetimes.rs:7:37 | LL | fn missing_lt() -> impl Sized + use<'missing> {} | - ^^^^^^^^ undeclared lifetime | | | help: consider introducing lifetime `'missing` here: `<'missing>` -error: expected lifetime parameter in `use<...>` precise captures list, found `'_` - --> $DIR/bad-lifetimes.rs:1:39 - | -LL | fn no_elided_lt() -> impl Sized + use<'_> {} - | ^^ - error: expected lifetime parameter in `use<...>` precise captures list, found `'static` - --> $DIR/bad-lifetimes.rs:5:36 + --> $DIR/bad-lifetimes.rs:4:36 | LL | fn static_lt() -> impl Sized + use<'static> {} | ^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0106, E0261. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs index 6c2477c9744ab..1b52b17020195 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs @@ -1,5 +1,3 @@ -#![feature(precise_capturing_in_traits)] - fn type_param() -> impl Sized + use<> {} //~^ ERROR `impl Trait` must mention all type parameters in scope diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr index 93b44a0c18c27..93c35203f1dcb 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr @@ -1,5 +1,5 @@ error: `impl Trait` must mention all type parameters in scope in `use<...>` - --> $DIR/forgot-to-capture-type.rs:3:23 + --> $DIR/forgot-to-capture-type.rs:1:23 | LL | fn type_param() -> impl Sized + use<> {} | - ^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | fn type_param() -> impl Sized + use<> {} = note: currently, all type parameters are required to be mentioned in the precise captures list error: `impl Trait` must mention the `Self` type of the trait in `use<...>` - --> $DIR/forgot-to-capture-type.rs:7:17 + --> $DIR/forgot-to-capture-type.rs:5:17 | LL | trait Foo { | --------- `Self` type parameter is implicitly captured by this `impl Trait` diff --git a/tests/ui/impl-trait/precise-capturing/redundant.rs b/tests/ui/impl-trait/precise-capturing/redundant.rs index 32dc09273175d..2385827db228e 100644 --- a/tests/ui/impl-trait/precise-capturing/redundant.rs +++ b/tests/ui/impl-trait/precise-capturing/redundant.rs @@ -1,6 +1,5 @@ //@ edition: 2024 -#![feature(precise_capturing_in_traits)] #![deny(impl_trait_redundant_captures)] fn hello<'a>() -> impl Sized + use<'a> {} diff --git a/tests/ui/impl-trait/precise-capturing/redundant.stderr b/tests/ui/impl-trait/precise-capturing/redundant.stderr index 5c8b35c228585..c9f84d360e3c6 100644 --- a/tests/ui/impl-trait/precise-capturing/redundant.stderr +++ b/tests/ui/impl-trait/precise-capturing/redundant.stderr @@ -1,5 +1,5 @@ error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - --> $DIR/redundant.rs:6:19 + --> $DIR/redundant.rs:5:19 | LL | fn hello<'a>() -> impl Sized + use<'a> {} | ^^^^^^^^^^^^^------- @@ -7,13 +7,13 @@ LL | fn hello<'a>() -> impl Sized + use<'a> {} | help: remove the `use<...>` syntax | note: the lint level is defined here - --> $DIR/redundant.rs:4:9 + --> $DIR/redundant.rs:3:9 | LL | #![deny(impl_trait_redundant_captures)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - --> $DIR/redundant.rs:11:27 + --> $DIR/redundant.rs:10:27 | LL | fn inherent(&self) -> impl Sized + use<'_> {} | ^^^^^^^^^^^^^------- @@ -21,7 +21,7 @@ LL | fn inherent(&self) -> impl Sized + use<'_> {} | help: remove the `use<...>` syntax error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - --> $DIR/redundant.rs:16:22 + --> $DIR/redundant.rs:15:22 | LL | fn in_trait() -> impl Sized + use<'a, Self>; | ^^^^^^^^^^^^^------------- @@ -29,7 +29,7 @@ LL | fn in_trait() -> impl Sized + use<'a, Self>; | help: remove the `use<...>` syntax error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - --> $DIR/redundant.rs:20:22 + --> $DIR/redundant.rs:19:22 | LL | fn in_trait() -> impl Sized + use<'a> {} | ^^^^^^^^^^^^^------- diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.rs b/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.rs index b39c1408c0509..f6126c0333914 100644 --- a/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.rs +++ b/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.rs @@ -2,8 +2,6 @@ // trait definition, which is not allowed. Due to the default lifetime capture // rules of RPITITs, this is only doable if we use precise capturing. -#![feature(precise_capturing_in_traits)] - pub trait Foo { fn bar<'tr: 'tr>(&'tr mut self) -> impl Sized + use; } diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.stderr b/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.stderr index 45f755d3cc1be..d90660188806e 100644 --- a/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.stderr +++ b/tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.stderr @@ -1,5 +1,5 @@ error: return type captures more lifetimes than trait definition - --> $DIR/rpitit-captures-more-method-lifetimes.rs:12:40 + --> $DIR/rpitit-captures-more-method-lifetimes.rs:10:40 | LL | fn bar<'im: 'im>(&'im mut self) -> impl Sized + use<'im> {} | --- ^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | fn bar<'im: 'im>(&'im mut self) -> impl Sized + use<'im> {} | this lifetime was captured | note: hidden type must only reference lifetimes captured by this impl trait - --> $DIR/rpitit-captures-more-method-lifetimes.rs:8:40 + --> $DIR/rpitit-captures-more-method-lifetimes.rs:6:40 | LL | fn bar<'tr: 'tr>(&'tr mut self) -> impl Sized + use; | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-impl-captures-too-much.rs b/tests/ui/impl-trait/precise-capturing/rpitit-impl-captures-too-much.rs index b16b0522d6e11..115cab1cb9929 100644 --- a/tests/ui/impl-trait/precise-capturing/rpitit-impl-captures-too-much.rs +++ b/tests/ui/impl-trait/precise-capturing/rpitit-impl-captures-too-much.rs @@ -1,5 +1,3 @@ -#![feature(precise_capturing_in_traits)] - struct Invariant<'a>(&'a mut &'a mut ()); trait Trait { diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-impl-captures-too-much.stderr b/tests/ui/impl-trait/precise-capturing/rpitit-impl-captures-too-much.stderr index 360f0d7e7f37f..123e0acf171c4 100644 --- a/tests/ui/impl-trait/precise-capturing/rpitit-impl-captures-too-much.stderr +++ b/tests/ui/impl-trait/precise-capturing/rpitit-impl-captures-too-much.stderr @@ -1,5 +1,5 @@ error: return type captures more lifetimes than trait definition - --> $DIR/rpitit-impl-captures-too-much.rs:10:39 + --> $DIR/rpitit-impl-captures-too-much.rs:8:39 | LL | fn hello(self_: Invariant<'_>) -> impl Sized + use; | -- this lifetime was captured @@ -8,7 +8,7 @@ LL | fn hello(self_: Invariant<'_>) -> impl Sized + use<'_> {} | ^^^^^^^^^^^^^^^^^^^^ | note: hidden type must only reference lifetimes captured by this impl trait - --> $DIR/rpitit-impl-captures-too-much.rs:6:39 + --> $DIR/rpitit-impl-captures-too-much.rs:4:39 | LL | fn hello(self_: Invariant<'_>) -> impl Sized + use; | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs b/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs index 6f7e1a0eaefae..6fc129a6480c7 100644 --- a/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs +++ b/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs @@ -2,8 +2,6 @@ // Ensure that we skip uncaptured args from RPITITs when comptuing outlives. -#![feature(precise_capturing_in_traits)] - struct Invariant(*mut T); trait Foo { diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs b/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs index 94d81d766f725..616368d25cf46 100644 --- a/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs +++ b/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs @@ -3,8 +3,6 @@ // Ensure that we skip uncaptured args from RPITITs when collecting the regions // to enforce member constraints in opaque type inference. -#![feature(precise_capturing_in_traits)] - struct Invariant(*mut T); trait Foo { diff --git a/tests/ui/impl-trait/precise-capturing/rpitit.rs b/tests/ui/impl-trait/precise-capturing/rpitit.rs index 3f887e8e47f1c..91c52817d8573 100644 --- a/tests/ui/impl-trait/precise-capturing/rpitit.rs +++ b/tests/ui/impl-trait/precise-capturing/rpitit.rs @@ -3,8 +3,6 @@ // To fix this soundly, we need to make sure that all the trait header args // remain captured, since they affect trait selection. -#![feature(precise_capturing_in_traits)] - fn eq_types(_: T, _: T) {} trait TraitLt<'a: 'a> { diff --git a/tests/ui/impl-trait/precise-capturing/rpitit.stderr b/tests/ui/impl-trait/precise-capturing/rpitit.stderr index 498eae54a1c68..ff461e81079b8 100644 --- a/tests/ui/impl-trait/precise-capturing/rpitit.stderr +++ b/tests/ui/impl-trait/precise-capturing/rpitit.stderr @@ -1,5 +1,5 @@ error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list - --> $DIR/rpitit.rs:11:19 + --> $DIR/rpitit.rs:9:19 | LL | trait TraitLt<'a: 'a> { | -- all lifetime parameters originating from a trait are captured implicitly @@ -7,7 +7,7 @@ LL | fn hello() -> impl Sized + use; | ^^^^^^^^^^^^^^^^^^^^^^ error: lifetime may not live long enough - --> $DIR/rpitit.rs:15:5 + --> $DIR/rpitit.rs:13:5 | LL | fn trait_lt<'a, 'b, T: for<'r> TraitLt<'r>> () { | -- -- lifetime `'b` defined here @@ -24,7 +24,7 @@ LL | | ); = help: consider adding the following bound: `'a: 'b` error: lifetime may not live long enough - --> $DIR/rpitit.rs:15:5 + --> $DIR/rpitit.rs:13:5 | LL | fn trait_lt<'a, 'b, T: for<'r> TraitLt<'r>> () { | -- -- lifetime `'b` defined here diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.rs b/tests/ui/impl-trait/precise-capturing/self-capture.rs index 15985da50b55e..66fbfc780438a 100644 --- a/tests/ui/impl-trait/precise-capturing/self-capture.rs +++ b/tests/ui/impl-trait/precise-capturing/self-capture.rs @@ -1,7 +1,5 @@ //@ check-pass -#![feature(precise_capturing_in_traits)] - trait Foo { fn bar<'a>() -> impl Sized + use; } diff --git a/tests/ui/impl-trait/recursive-ice-101862.stderr b/tests/ui/impl-trait/recursive-ice-101862.stderr index 970373422e8a5..85745c25e2bfa 100644 --- a/tests/ui/impl-trait/recursive-ice-101862.stderr +++ b/tests/ui/impl-trait/recursive-ice-101862.stderr @@ -1,3 +1,12 @@ +error[E0792]: expected generic type parameter, found `&str` + --> $DIR/recursive-ice-101862.rs:6:19 + | +LL | pub fn ice(x: impl AsRef) -> impl IntoIterator { + | --------------- this generic parameter must be used with a generic type parameter +LL | +LL | vec![].append(&mut ice(x.as_ref())); + | ^^^^^^^^^^^^^^^^^^^^ + warning: function cannot return without recursing --> $DIR/recursive-ice-101862.rs:4:1 | @@ -10,15 +19,6 @@ LL | vec![].append(&mut ice(x.as_ref())); = help: a `loop` may express intention better if this is on purpose = note: `#[warn(unconditional_recursion)]` on by default -error[E0792]: expected generic type parameter, found `&str` - --> $DIR/recursive-ice-101862.rs:6:19 - | -LL | pub fn ice(x: impl AsRef) -> impl IntoIterator { - | --------------- this generic parameter must be used with a generic type parameter -LL | -LL | vec![].append(&mut ice(x.as_ref())); - | ^^^^^^^^^^^^^^^^^^^^ - error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 2d2731e4368f5..af84375c74766 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -2,112 +2,67 @@ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:6:22 | 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 `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:11:15 | LL | fn tuple() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | (tuple(),) - | ---------- returning here with type `(impl Sized,)` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:16:15 | LL | fn array() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | [array()] - | --------- returning here with type `[impl Sized; 1]` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:21:13 | LL | fn ptr() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | &ptr() as *const _ - | ------------------ returning here with type `*const impl Sized` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:26:16 | LL | fn fn_ptr() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | fn_ptr as fn() -> _ - | ------------------- returning here with type `fn() -> impl Sized` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:31:25 | -LL | fn closure_capture() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | / move || { -LL | | x; - | | - closure captures itself here -LL | | } - | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:34:5: 34:12}` +LL | fn closure_capture() -> impl Sized { + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:39:29 | -LL | fn closure_ref_capture() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | / move || { -LL | | &x; - | | - closure captures itself here -LL | | } - | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:42:5: 42:12}` +LL | fn closure_ref_capture() -> impl Sized { + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:47:21 | LL | fn closure_sig() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | || closure_sig() - | ---------------- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:49:5: 49:7}` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:52:23 | LL | fn coroutine_sig() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | || coroutine_sig() - | ------------------ returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:54:5: 54:7}` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:57:27 | -LL | fn coroutine_capture() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -... -LL | / move || { -LL | | yield; -LL | | x; - | | - coroutine captures itself here -LL | | } - | |_____- returning here with type `{coroutine@$DIR/recursive-impl-trait-type-indirect.rs:62:5: 62:12}` +LL | fn coroutine_capture() -> impl Sized { + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:68:35 | LL | fn substs_change() -> impl Sized { - | ^^^^^^^^^^ recursive opaque type -LL | -LL | (substs_change::<&T>(),) - | ------------------------ returning here with type `(impl Sized,)` + | ^^^^^^^^^^ error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:78:26 diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.current.stderr b/tests/ui/impl-trait/recursive-in-exhaustiveness.current.stderr new file mode 100644 index 0000000000000..080c328464153 --- /dev/null +++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.current.stderr @@ -0,0 +1,21 @@ +error[E0720]: cannot resolve opaque type + --> $DIR/recursive-in-exhaustiveness.rs:17:22 + | +LL | fn build(x: T) -> impl Sized { + | ^^^^^^^^^^ + +error[E0720]: cannot resolve opaque type + --> $DIR/recursive-in-exhaustiveness.rs:27:23 + | +LL | fn build2(x: T) -> impl Sized { + | ^^^^^^^^^^ + +error[E0720]: cannot resolve opaque type + --> $DIR/recursive-in-exhaustiveness.rs:39:23 + | +LL | fn build3(x: T) -> impl Sized { + | ^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr b/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr new file mode 100644 index 0000000000000..a3609b93cb3a8 --- /dev/null +++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr @@ -0,0 +1,80 @@ +error[E0284]: type annotations needed: cannot satisfy `impl Sized == _` + --> $DIR/recursive-in-exhaustiveness.rs:19:17 + | +LL | let (x,) = (build(x),); + | ^^^^^^^^ cannot satisfy `impl Sized == _` + +error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _` + --> $DIR/recursive-in-exhaustiveness.rs:30:6 + | +LL | (build2(x),) + | ^^^^^^^^^ types differ + +error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _` + --> $DIR/recursive-in-exhaustiveness.rs:30:5 + | +LL | (build2(x),) + | ^^^^^^^^^^^^ types differ + +error[E0277]: the size for values of type `(impl Sized,)` cannot be known at compilation time + --> $DIR/recursive-in-exhaustiveness.rs:30:5 + | +LL | (build2(x),) + | ^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(impl Sized,)` + = note: tuples must have a statically known size to be initialized + +error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _` + --> $DIR/recursive-in-exhaustiveness.rs:41:17 + | +LL | let (x,) = (build3((x,)),); + | ^^^^^^^^^^^^ types differ + +error[E0277]: the size for values of type `(impl Sized,)` cannot be known at compilation time + --> $DIR/recursive-in-exhaustiveness.rs:41:16 + | +LL | let (x,) = (build3((x,)),); + | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(impl Sized,)` + = note: tuples must have a statically known size to be initialized + +error[E0308]: mismatched types + --> $DIR/recursive-in-exhaustiveness.rs:41:16 + | +LL | fn build3(x: T) -> impl Sized { + | ---------- the found opaque type +LL | +LL | let (x,) = (build3((x,)),); + | ^^^^^^^^^^^^^^^ types differ + | + = note: expected type `_` + found tuple `(impl Sized,)` + +error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _` + --> $DIR/recursive-in-exhaustiveness.rs:41:17 + | +LL | let (x,) = (build3((x,)),); + | ^^^^^^^^^^^^ types differ + | + = note: the return type of a function must have a statically known size + +error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _` + --> $DIR/recursive-in-exhaustiveness.rs:41:16 + | +LL | let (x,) = (build3((x,)),); + | ^^^^^^^^^^^^^^^ types differ + +error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _` + --> $DIR/recursive-in-exhaustiveness.rs:41:17 + | +LL | let (x,) = (build3((x,)),); + | ^^^^^^^^^^^^ types differ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0271, E0277, E0284, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.rs b/tests/ui/impl-trait/recursive-in-exhaustiveness.rs new file mode 100644 index 0000000000000..fa8fa0e81743b --- /dev/null +++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.rs @@ -0,0 +1,51 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Test several spicy non-trivial recursive opaque definitions inferred from HIR typeck +// don't cause stack overflows in exhaustiveness code, which currently reveals opaques +// manually in a way that is not overflow aware. +// +// These should eventually be outright rejected, but today (some) non-trivial recursive +// opaque definitions are accepted, and changing that requires an FCP, so for now just +// make sure we don't stack overflow :^) + +// Opaque = Opaque> +// +// We unfortunately accept this today, and due to how opaque type relating is implemented +// in the NLL type relation, this defines `Opaque = T`. +fn build(x: T) -> impl Sized { + //[current]~^ ERROR cannot resolve opaque type + let (x,) = (build(x),); + //[next]~^ ERROR type annotations needed + build(x) +} + +// Opaque = (Opaque,) +// +// Not allowed today. Detected as recursive. +fn build2(x: T) -> impl Sized { + //[current]~^ ERROR cannot resolve opaque type + let (x,) = (build2(x),); + (build2(x),) + //[next]~^ ERROR type mismatch resolving + //[next]~| ERROR type mismatch resolving + //[next]~| ERROR the size for values of type +} + +// Opaque = Opaque<(T,)> +// +// Not allowed today. Detected as not defining. +fn build3(x: T) -> impl Sized { + //[current]~^ ERROR cannot resolve opaque type + let (x,) = (build3((x,)),); + //[next]~^ ERROR type mismatch resolving + //[next]~| ERROR type mismatch resolving + //[next]~| ERROR type mismatch resolving + //[next]~| ERROR type mismatch resolving + //[next]~| ERROR the size for values of type + //[next]~| ERROR mismatched types + build3(x) +} + +fn main() {} diff --git a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs index 0b29af5df5b58..662c7ee2ecf0b 100644 --- a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs +++ b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs @@ -2,14 +2,12 @@ //@ check-pass -mod foo { - pub type Foo = impl PartialEq<(Foo, i32)>; +pub type Foo = impl PartialEq<(Foo, i32)>; - fn foo() -> Foo { - super::Bar - } +#[define_opaque(Foo)] +fn foo() -> Foo { + Bar } -use foo::Foo; struct Bar; diff --git a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.rs b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.rs index 3f41c5984b419..5b9c40ecedea8 100644 --- a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.rs +++ b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.rs @@ -6,6 +6,7 @@ mod a { struct Bar; impl PartialEq<(Bar, i32)> for Bar { + #[define_opaque(Foo)] fn eq(&self, _other: &(Foo, i32)) -> bool { //~^ ERROR: `eq` has an incompatible type for trait //~| ERROR: item does not constrain `a::Foo::{opaque#0}` diff --git a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr index 767bd312407a3..7f642fa1bedfa 100644 --- a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr +++ b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr @@ -1,5 +1,5 @@ error[E0053]: method `eq` has an incompatible type for trait - --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:9:30 + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:10:30 | LL | type Foo = impl PartialEq<(Foo, i32)>; | -------------------------- the found opaque type @@ -15,21 +15,21 @@ LL - fn eq(&self, _other: &(Foo, i32)) -> bool { LL + fn eq(&self, _other: &(a::Bar, i32)) -> bool { | -error: item does not constrain `a::Foo::{opaque#0}`, but has it in its signature - --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:9:12 +error: item does not constrain `a::Foo::{opaque#0}` + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:10:12 | LL | fn eq(&self, _other: &(Foo, i32)) -> bool { | ^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:4:16 | LL | type Foo = impl PartialEq<(Foo, i32)>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0053]: method `eq` has an incompatible type for trait - --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:24:30 + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:25:30 | LL | type Foo = impl PartialEq<(Foo, i32)>; | -------------------------- the expected opaque type @@ -39,8 +39,8 @@ LL | fn eq(&self, _other: &(Bar, i32)) -> bool { | = note: expected signature `fn(&b::Bar, &(b::Foo, _)) -> _` found signature `fn(&b::Bar, &(b::Bar, _)) -> _` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:24:12 +note: this item must have a `#[define_opaque(b::Foo)]` attribute to be able to define hidden types + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:25:12 | LL | fn eq(&self, _other: &(Bar, i32)) -> bool { | ^^ @@ -51,12 +51,12 @@ LL + fn eq(&self, _other: &(b::Foo, i32)) -> bool { | error: unconstrained opaque type - --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:18:16 + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:19:16 | LL | type Foo = impl PartialEq<(Foo, i32)>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `Foo` must be used in combination with a concrete type within the same module + = note: `Foo` must be used in combination with a concrete type within the same crate error: aborting due to 4 previous errors diff --git a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration.rs b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration.rs index aab10be2de27a..372a095192bce 100644 --- a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration.rs +++ b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration.rs @@ -10,6 +10,7 @@ impl PartialEq<(Bar, i32)> for Bar { } } +#[define_opaque(Foo)] fn foo() -> Foo { //~^ ERROR can't compare `Bar` with `(Foo, i32)` Bar diff --git a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration.stderr b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration.stderr index bc810c0f88f3d..a9a5483caa9d4 100644 --- a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration.stderr +++ b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration.stderr @@ -1,5 +1,5 @@ error[E0277]: can't compare `Bar` with `(Foo, i32)` - --> $DIR/recursive-type-alias-impl-trait-declaration.rs:13:13 + --> $DIR/recursive-type-alias-impl-trait-declaration.rs:14:13 | LL | fn foo() -> Foo { | ^^^ no implementation for `Bar == (Foo, i32)` diff --git a/tests/ui/impl-trait/rpit/early_bound.rs b/tests/ui/impl-trait/rpit/early_bound.rs index 005bcea59f243..0337f605e487b 100644 --- a/tests/ui/impl-trait/rpit/early_bound.rs +++ b/tests/ui/impl-trait/rpit/early_bound.rs @@ -1,11 +1,10 @@ use std::convert::identity; fn test<'a: 'a>(n: bool) -> impl Sized + 'a { - //~^ ERROR concrete type differs from previous defining opaque type use let true = n else { loop {} }; let _ = || { let _ = identity::<&'a ()>(test(false)); - //~^ ERROR expected generic lifetime parameter, found `'_` + //~^ ERROR concrete type differs from previous defining opaque type use }; loop {} } diff --git a/tests/ui/impl-trait/rpit/early_bound.stderr b/tests/ui/impl-trait/rpit/early_bound.stderr index 230dde95764bf..d00005f20d4a3 100644 --- a/tests/ui/impl-trait/rpit/early_bound.stderr +++ b/tests/ui/impl-trait/rpit/early_bound.stderr @@ -1,24 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/early_bound.rs:3:29 + --> $DIR/early_bound.rs:6:36 | -LL | fn test<'a: 'a>(n: bool) -> impl Sized + 'a { - | ^^^^^^^^^^^^^^^ expected `&()`, got `()` +LL | let _ = identity::<&'a ()>(test(false)); + | ^^^^^^^^^^^ expected `()`, got `&()` | note: previous use here - --> $DIR/early_bound.rs:7:36 - | -LL | let _ = identity::<&'a ()>(test(false)); - | ^^^^^^^^^^^ - -error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/early_bound.rs:7:17 + --> $DIR/early_bound.rs:3:29 | LL | fn test<'a: 'a>(n: bool) -> impl Sized + 'a { - | -- this generic parameter must be used with a generic lifetime parameter -... -LL | let _ = identity::<&'a ()>(test(false)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/rpit/non-defining-use.rs b/tests/ui/impl-trait/rpit/non-defining-use.rs index 255a8929a8786..3ab0e0ee4e29f 100644 --- a/tests/ui/impl-trait/rpit/non-defining-use.rs +++ b/tests/ui/impl-trait/rpit/non-defining-use.rs @@ -6,8 +6,7 @@ fn foo() -> impl Sized { fn bar(val: T) -> impl Sized { let _: u8 = bar(0u8); - //~^ ERROR concrete type differs from previous defining opaque type use - //~| ERROR expected generic type parameter, found `u8` + //~^ ERROR expected generic type parameter, found `u8` val } diff --git a/tests/ui/impl-trait/rpit/non-defining-use.stderr b/tests/ui/impl-trait/rpit/non-defining-use.stderr index 10a8232e646d0..c2b1b0f824976 100644 --- a/tests/ui/impl-trait/rpit/non-defining-use.stderr +++ b/tests/ui/impl-trait/rpit/non-defining-use.stderr @@ -1,31 +1,19 @@ error[E0792]: expected generic type parameter, found `u8` - --> $DIR/non-defining-use.rs:4:12 + --> $DIR/non-defining-use.rs:4:17 | LL | fn foo() -> impl Sized { | - this generic parameter must be used with a generic type parameter LL | let _: () = foo::(); - | ^^ - -error: concrete type differs from previous defining opaque type use - --> $DIR/non-defining-use.rs:8:17 - | -LL | let _: u8 = bar(0u8); - | ^^^^^^^^ expected `T`, got `u8` - | -note: previous use here - --> $DIR/non-defining-use.rs:7:22 - | -LL | fn bar(val: T) -> impl Sized { - | ^^^^^^^^^^ + | ^^^^^^^^^^^ error[E0792]: expected generic type parameter, found `u8` - --> $DIR/non-defining-use.rs:8:12 + --> $DIR/non-defining-use.rs:8:17 | LL | fn bar(val: T) -> impl Sized { | - this generic parameter must be used with a generic type parameter LL | let _: u8 = bar(0u8); - | ^^ + | ^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/transmute/in-defining-scope.stderr b/tests/ui/impl-trait/transmute/in-defining-scope.stderr index 7172bfdf0d7e2..3153569517835 100644 --- a/tests/ui/impl-trait/transmute/in-defining-scope.stderr +++ b/tests/ui/impl-trait/transmute/in-defining-scope.stderr @@ -9,6 +9,31 @@ note: ...which requires computing type of opaque `foo::{opaque#0}`... | LL | fn foo() -> impl Sized { | ^^^^^^^^^^ +note: ...which requires borrow-checking `foo`... + --> $DIR/in-defining-scope.rs:6:1 + | +LL | fn foo() -> impl Sized { + | ^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires promoting constants in MIR for `foo`... + --> $DIR/in-defining-scope.rs:6:1 + | +LL | fn foo() -> impl Sized { + | ^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires checking if `foo` contains FFI-unwind calls... + --> $DIR/in-defining-scope.rs:6:1 + | +LL | fn foo() -> impl Sized { + | ^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires building MIR for `foo`... + --> $DIR/in-defining-scope.rs:6:1 + | +LL | fn foo() -> impl Sized { + | ^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires match-checking `foo`... + --> $DIR/in-defining-scope.rs:6:1 + | +LL | fn foo() -> impl Sized { + | ^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires type-checking `foo`... --> $DIR/in-defining-scope.rs:6:1 | diff --git a/tests/ui/impl-trait/two_tait_defining_each_other.current.stderr b/tests/ui/impl-trait/two_tait_defining_each_other.current.stderr index bf194f997b4a4..8845b3ff2c824 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other.current.stderr +++ b/tests/ui/impl-trait/two_tait_defining_each_other.current.stderr @@ -1,5 +1,5 @@ error: opaque type's hidden type cannot be another opaque type from the same scope - --> $DIR/two_tait_defining_each_other.rs:17:5 + --> $DIR/two_tait_defining_each_other.rs:18:5 | LL | x // A's hidden type is `Bar`, because all the hidden types of `B` are compared with each other | ^ one of the two opaque types used here has to be outside its defining scope diff --git a/tests/ui/impl-trait/two_tait_defining_each_other.rs b/tests/ui/impl-trait/two_tait_defining_each_other.rs index ebfe7f674be0c..d3038688eee1f 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other.rs +++ b/tests/ui/impl-trait/two_tait_defining_each_other.rs @@ -10,6 +10,7 @@ type B = impl Foo; trait Foo {} +#[define_opaque(A, B)] fn muh(x: A) -> B { if false { return Bar; // B's hidden type is Bar diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.current.stderr b/tests/ui/impl-trait/two_tait_defining_each_other2.current.stderr index 7d02a0606fc2b..11b57ad98c458 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.current.stderr +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.current.stderr @@ -1,16 +1,3 @@ -error: item does not constrain `A::{opaque#0}`, but has it in its signature - --> $DIR/two_tait_defining_each_other2.rs:11:4 - | -LL | fn muh(x: A) -> B { - | ^^^ - | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/two_tait_defining_each_other2.rs:6:10 - | -LL | type A = impl Foo; - | ^^^^^^^^ - error: opaque type's hidden type cannot be another opaque type from the same scope --> $DIR/two_tait_defining_each_other2.rs:14:5 | @@ -28,5 +15,5 @@ note: opaque type being used as hidden type LL | type A = impl Foo; | ^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr index 5316160125bff..1a4c0f5f7eef5 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr @@ -1,5 +1,5 @@ error[E0284]: type annotations needed: cannot satisfy `_ == A` - --> $DIR/two_tait_defining_each_other2.rs:11:8 + --> $DIR/two_tait_defining_each_other2.rs:12:8 | LL | fn muh(x: A) -> B { | ^ cannot satisfy `_ == A` diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.rs b/tests/ui/impl-trait/two_tait_defining_each_other2.rs index 1681b019418e7..6c454bba5023f 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.rs +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.rs @@ -8,9 +8,9 @@ type B = impl Foo; trait Foo {} +#[define_opaque(A, B)] fn muh(x: A) -> B { - //[current]~^ ERROR: item does not constrain `A::{opaque#0}` - //[next]~^^ ERROR: cannot satisfy `_ == A` + //[next]~^ ERROR: cannot satisfy `_ == A` x // B's hidden type is A (opaquely) //[current]~^ ERROR opaque type's hidden type cannot be another opaque type } diff --git a/tests/ui/impl-trait/two_tait_defining_each_other3.current.stderr b/tests/ui/impl-trait/two_tait_defining_each_other3.current.stderr index fa353a7753606..7b0003ff59b15 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other3.current.stderr +++ b/tests/ui/impl-trait/two_tait_defining_each_other3.current.stderr @@ -1,5 +1,5 @@ error: opaque type's hidden type cannot be another opaque type from the same scope - --> $DIR/two_tait_defining_each_other3.rs:14:16 + --> $DIR/two_tait_defining_each_other3.rs:15:16 | LL | return x; // B's hidden type is A (opaquely) | ^ one of the two opaque types used here has to be outside its defining scope diff --git a/tests/ui/impl-trait/two_tait_defining_each_other3.rs b/tests/ui/impl-trait/two_tait_defining_each_other3.rs index 33695d8ed8030..ec3cd58a3a57f 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other3.rs +++ b/tests/ui/impl-trait/two_tait_defining_each_other3.rs @@ -9,6 +9,7 @@ type B = impl Foo; trait Foo {} +#[define_opaque(A, B)] fn muh(x: A) -> B { if false { return x; // B's hidden type is A (opaquely) diff --git a/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.rs b/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.rs index 4879d2db40b81..77b2407e304fe 100644 --- a/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.rs +++ b/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.rs @@ -9,6 +9,7 @@ fn main() { //~^ ERROR: item does not constrain type Existential = impl Debug; + #[define_opaque(Existential)] fn f() -> Existential {} println!("{:?}", f()); } diff --git a/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.stderr b/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.stderr index 7744fa2f2ae66..0877b4b71bee3 100644 --- a/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.stderr +++ b/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.stderr @@ -1,11 +1,11 @@ -error: item does not constrain `Existential::{opaque#0}`, but has it in its signature +error: item does not constrain `Existential::{opaque#0}` --> $DIR/type-alias-impl-trait-in-fn-body.rs:8:4 | LL | fn main() { | ^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/type-alias-impl-trait-in-fn-body.rs:10:24 | LL | type Existential = impl Debug; diff --git a/tests/ui/impl-trait/unsize_tuple.rs b/tests/ui/impl-trait/unsize_tuple.rs deleted file mode 100644 index 2678564f0e869..0000000000000 --- a/tests/ui/impl-trait/unsize_tuple.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Test that we allow unsizing `([Opaque; N],)` to `([Concrete],)`. - -//@check-pass - -#![feature(unsized_tuple_coercion)] - -fn hello() -> ([impl Sized; 2],) { - if false { - let x = hello(); - let _: &([i32],) = &x; - } - todo!() -} - -fn main() {} diff --git a/tests/ui/impl-trait/variance.rs b/tests/ui/impl-trait/variance.rs index bde3a886a4d38..e73e0c623aa40 100644 --- a/tests/ui/impl-trait/variance.rs +++ b/tests/ui/impl-trait/variance.rs @@ -9,15 +9,15 @@ trait Captures<'a> {} impl Captures<'_> for T {} fn not_captured_early<'a: 'a>() -> impl Sized {} -//[old]~^ ['a: *] -//[e2024]~^^ ['a: *, 'a: o] +//[old]~^ ERROR ['a: *] +//[e2024]~^^ ERROR ['a: *, 'a: o] -fn captured_early<'a: 'a>() -> impl Sized + Captures<'a> {} //~ ['a: *, 'a: o] +fn captured_early<'a: 'a>() -> impl Sized + Captures<'a> {} //~ ERROR ['a: *, 'a: o] fn not_captured_late<'a>(_: &'a ()) -> impl Sized {} -//[old]~^ [] -//[e2024]~^^ ['a: o] +//[old]~^ ERROR [] +//[e2024]~^^ ERROR ['a: o] -fn captured_late<'a>(_: &'a ()) -> impl Sized + Captures<'a> {} //~ ['a: o] +fn captured_late<'a>(_: &'a ()) -> impl Sized + Captures<'a> {} //~ ERROR ['a: o] fn main() {} diff --git a/tests/ui/impl-trait/where-allowed.rs b/tests/ui/impl-trait/where-allowed.rs index 3f435f0f443b4..1c3c66c537ff9 100644 --- a/tests/ui/impl-trait/where-allowed.rs +++ b/tests/ui/impl-trait/where-allowed.rs @@ -157,6 +157,7 @@ extern "C" fn in_extern_fn_return() -> impl Debug { type InTypeAlias = impl Debug; //~^ ERROR `impl Trait` in type aliases is unstable +//~| ERROR unconstrained opaque type type InReturnInTypeAlias = fn() -> impl Debug; //~^ ERROR `impl Trait` is not allowed in `fn` pointer diff --git a/tests/ui/impl-trait/where-allowed.stderr b/tests/ui/impl-trait/where-allowed.stderr index ebce9b7e445b6..052ae5a99315c 100644 --- a/tests/ui/impl-trait/where-allowed.stderr +++ b/tests/ui/impl-trait/where-allowed.stderr @@ -37,7 +37,7 @@ LL | type InTypeAlias = impl Debug; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/where-allowed.rs:161:39 + --> $DIR/where-allowed.rs:162:39 | LL | type InReturnInTypeAlias = fn() -> impl Debug; | ^^^^^^^^^^ @@ -199,7 +199,7 @@ LL | fn in_foreign_return() -> impl Debug; = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in `fn` pointer return types - --> $DIR/where-allowed.rs:161:39 + --> $DIR/where-allowed.rs:162:39 | LL | type InReturnInTypeAlias = fn() -> impl Debug; | ^^^^^^^^^^ @@ -207,7 +207,7 @@ LL | type InReturnInTypeAlias = fn() -> impl Debug; = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in traits - --> $DIR/where-allowed.rs:166:16 + --> $DIR/where-allowed.rs:167:16 | LL | impl PartialEq for () { | ^^^^^^^^^^ @@ -215,7 +215,7 @@ LL | impl PartialEq for () { = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in impl headers - --> $DIR/where-allowed.rs:171:24 + --> $DIR/where-allowed.rs:172:24 | LL | impl PartialEq<()> for impl Debug { | ^^^^^^^^^^ @@ -223,7 +223,7 @@ LL | impl PartialEq<()> for impl Debug { = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in impl headers - --> $DIR/where-allowed.rs:176:6 + --> $DIR/where-allowed.rs:177:6 | LL | impl impl Debug { | ^^^^^^^^^^ @@ -231,7 +231,7 @@ LL | impl impl Debug { = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in impl headers - --> $DIR/where-allowed.rs:182:24 + --> $DIR/where-allowed.rs:183:24 | LL | impl InInherentImplAdt { | ^^^^^^^^^^ @@ -239,7 +239,7 @@ LL | impl InInherentImplAdt { = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in bounds - --> $DIR/where-allowed.rs:188:11 + --> $DIR/where-allowed.rs:189:11 | LL | where impl Debug: Debug | ^^^^^^^^^^ @@ -247,7 +247,7 @@ LL | where impl Debug: Debug = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in bounds - --> $DIR/where-allowed.rs:195:15 + --> $DIR/where-allowed.rs:196:15 | LL | where Vec: Debug | ^^^^^^^^^^ @@ -255,7 +255,7 @@ LL | where Vec: Debug = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in bounds - --> $DIR/where-allowed.rs:202:24 + --> $DIR/where-allowed.rs:203:24 | LL | where T: PartialEq | ^^^^^^^^^^ @@ -263,7 +263,7 @@ LL | where T: PartialEq = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in the parameters of `Fn` trait bounds - --> $DIR/where-allowed.rs:209:17 + --> $DIR/where-allowed.rs:210:17 | LL | where T: Fn(impl Debug) | ^^^^^^^^^^ @@ -271,7 +271,7 @@ LL | where T: Fn(impl Debug) = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in the return type of `Fn` trait bounds - --> $DIR/where-allowed.rs:216:22 + --> $DIR/where-allowed.rs:217:22 | LL | where T: Fn() -> impl Debug | ^^^^^^^^^^ @@ -279,7 +279,7 @@ LL | where T: Fn() -> impl Debug = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in generic parameter defaults - --> $DIR/where-allowed.rs:222:40 + --> $DIR/where-allowed.rs:223:40 | LL | struct InStructGenericParamDefault(T); | ^^^^^^^^^^ @@ -287,7 +287,7 @@ LL | struct InStructGenericParamDefault(T); = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in generic parameter defaults - --> $DIR/where-allowed.rs:226:36 + --> $DIR/where-allowed.rs:227:36 | LL | enum InEnumGenericParamDefault { Variant(T) } | ^^^^^^^^^^ @@ -295,7 +295,7 @@ LL | enum InEnumGenericParamDefault { Variant(T) } = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in generic parameter defaults - --> $DIR/where-allowed.rs:230:38 + --> $DIR/where-allowed.rs:231:38 | LL | trait InTraitGenericParamDefault {} | ^^^^^^^^^^ @@ -303,7 +303,7 @@ LL | trait InTraitGenericParamDefault {} = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in generic parameter defaults - --> $DIR/where-allowed.rs:234:41 + --> $DIR/where-allowed.rs:235:41 | LL | type InTypeAliasGenericParamDefault = T; | ^^^^^^^^^^ @@ -311,7 +311,7 @@ LL | type InTypeAliasGenericParamDefault = T; = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in generic parameter defaults - --> $DIR/where-allowed.rs:238:11 + --> $DIR/where-allowed.rs:239:11 | LL | impl T {} | ^^^^^^^^^^ @@ -319,7 +319,7 @@ LL | impl T {} = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in generic parameter defaults - --> $DIR/where-allowed.rs:245:40 + --> $DIR/where-allowed.rs:246:40 | LL | fn in_method_generic_param_default(_: T) {} | ^^^^^^^^^^ @@ -327,7 +327,7 @@ LL | fn in_method_generic_param_default(_: T) {} = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/where-allowed.rs:251:29 + --> $DIR/where-allowed.rs:252:29 | LL | let _in_local_variable: impl Fn() = || {}; | ^^^^^^^^^ @@ -338,7 +338,7 @@ LL | let _in_local_variable: impl Fn() = || {}; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in closure return types - --> $DIR/where-allowed.rs:253:46 + --> $DIR/where-allowed.rs:254:46 | LL | let _in_return_in_local_variable = || -> impl Fn() { || {} }; | ^^^^^^^^^ @@ -369,7 +369,7 @@ LL + fn in_trait_impl_return() -> <() as DummyTrait>::Out { () } | error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/where-allowed.rs:245:36 + --> $DIR/where-allowed.rs:246:36 | LL | fn in_method_generic_param_default(_: T) {} | ^^^^^^^^^^^^^^ @@ -379,7 +379,7 @@ LL | fn in_method_generic_param_default(_: T) {} = note: `#[deny(invalid_type_param_default)]` on by default error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/where-allowed.rs:238:7 + --> $DIR/where-allowed.rs:239:7 | LL | impl T {} | ^^^^^^^^^^^^^^ @@ -408,7 +408,7 @@ LL | fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { pani where Args: Tuple, F: Fn, A: Allocator, F: ?Sized; error[E0118]: no nominal type found for inherent implementation - --> $DIR/where-allowed.rs:238:1 + --> $DIR/where-allowed.rs:239:1 | LL | impl T {} | ^^^^^^^^^^^^^^^^^^^^^^^ impl requires a nominal type @@ -423,13 +423,21 @@ LL | type Out = impl Debug; | = note: `Out` must be used in combination with a concrete type within the same impl -error: aborting due to 49 previous errors +error: unconstrained opaque type + --> $DIR/where-allowed.rs:158:23 + | +LL | type InTypeAlias = impl Debug; + | ^^^^^^^^^^ + | + = note: `InTypeAlias` must be used in combination with a concrete type within the same crate + +error: aborting due to 50 previous errors Some errors have detailed explanations: E0053, E0118, E0283, E0562, E0658, E0666. For more information about an error, try `rustc --explain E0053`. Future incompatibility report: Future breakage diagnostic: error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/where-allowed.rs:245:36 + --> $DIR/where-allowed.rs:246:36 | LL | fn in_method_generic_param_default(_: T) {} | ^^^^^^^^^^^^^^ @@ -440,7 +448,7 @@ LL | fn in_method_generic_param_default(_: T) {} Future breakage diagnostic: error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/where-allowed.rs:238:7 + --> $DIR/where-allowed.rs:239:7 | LL | impl T {} | ^^^^^^^^^^^^^^ diff --git a/tests/ui/impl-unused-tps.stderr b/tests/ui/impl-unused-tps.stderr index 09c3fce641cfe..eff5ffff9b6ac 100644 --- a/tests/ui/impl-unused-tps.stderr +++ b/tests/ui/impl-unused-tps.stderr @@ -7,23 +7,6 @@ LL | impl Foo for [isize; 0] { LL | impl Foo for U { | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `[isize; 0]` -error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates - --> $DIR/impl-unused-tps.rs:32:9 - | -LL | impl Bar for T { - | ^ unconstrained type parameter - -error[E0119]: conflicting implementations of trait `Bar` - --> $DIR/impl-unused-tps.rs:40:1 - | -LL | impl Bar for T { - | -------------------- first implementation here -... -LL | / impl Bar for T -LL | | where -LL | | T: Bar, - | |____________________^ conflicting implementation - error[E0119]: conflicting implementations of trait `Foo<[isize; 0]>` for type `[isize; 0]` --> $DIR/impl-unused-tps.rs:49:1 | @@ -52,6 +35,12 @@ error[E0207]: the type parameter `U` is not constrained by the impl trait, self LL | impl Foo for [isize; 1] { | ^ unconstrained type parameter +error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates + --> $DIR/impl-unused-tps.rs:32:9 + | +LL | impl Bar for T { + | ^ unconstrained type parameter + error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates --> $DIR/impl-unused-tps.rs:40:9 | @@ -70,6 +59,17 @@ error[E0207]: the type parameter `V` is not constrained by the impl trait, self LL | impl Foo for T | ^ unconstrained type parameter +error[E0119]: conflicting implementations of trait `Bar` + --> $DIR/impl-unused-tps.rs:40:1 + | +LL | impl Bar for T { + | -------------------- first implementation here +... +LL | / impl Bar for T +LL | | where +LL | | T: Bar, + | |____________________^ conflicting implementation + error: aborting due to 9 previous errors Some errors have detailed explanations: E0119, E0207. diff --git a/tests/ui/implied-bounds/dyn-erasure-tait.rs b/tests/ui/implied-bounds/dyn-erasure-tait.rs index 4766d221d67c0..1744e52939366 100644 --- a/tests/ui/implied-bounds/dyn-erasure-tait.rs +++ b/tests/ui/implied-bounds/dyn-erasure-tait.rs @@ -14,6 +14,7 @@ type T<'lt> = &'lt str; type F<'a, 'b> = impl 'static + Fn(T<'a>) -> T<'b>; +#[define_opaque(F)] fn helper<'a, 'b>(_: [&'b &'a (); 0]) -> F<'a, 'b> { |x: T<'a>| -> T<'b> { x } // this should *not* be `: 'static` } diff --git a/tests/ui/implied-bounds/from-trait-impl.rs b/tests/ui/implied-bounds/from-trait-impl.rs index 6e126575aa9a2..5822c5474bdcb 100644 --- a/tests/ui/implied-bounds/from-trait-impl.rs +++ b/tests/ui/implied-bounds/from-trait-impl.rs @@ -12,6 +12,7 @@ where fn func1(foo: Foo<(&str,)>) { //~^ ERROR `&str` does not fulfill the required lifetime + //~| ERROR lifetime may not live long enough let _: &'static str = foo.0.0; } @@ -19,5 +20,6 @@ trait TestTrait {} impl TestTrait for [Foo<(X,)>; 1] {} //~^ ERROR `X` may not live long enough +//~| ERROR `X` may not live long enough fn main() {} diff --git a/tests/ui/implied-bounds/from-trait-impl.stderr b/tests/ui/implied-bounds/from-trait-impl.stderr index 4151d206ae25f..5f189d3385ac1 100644 --- a/tests/ui/implied-bounds/from-trait-impl.stderr +++ b/tests/ui/implied-bounds/from-trait-impl.stderr @@ -7,7 +7,21 @@ LL | fn func1(foo: Foo<(&str,)>) { = note: type must satisfy the static lifetime error[E0310]: the parameter type `X` may not live long enough - --> $DIR/from-trait-impl.rs:20:23 + --> $DIR/from-trait-impl.rs:21:1 + | +LL | impl TestTrait for [Foo<(X,)>; 1] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `X` must be valid for the static lifetime... + | ...so that the type `X` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | impl TestTrait for [Foo<(X,)>; 1] {} + | +++++++++ + +error[E0310]: the parameter type `X` may not live long enough + --> $DIR/from-trait-impl.rs:21:23 | LL | impl TestTrait for [Foo<(X,)>; 1] {} | ^^^^^^^^^^^^^^ @@ -20,7 +34,16 @@ help: consider adding an explicit lifetime bound LL | impl TestTrait for [Foo<(X,)>; 1] {} | +++++++++ -error: aborting due to 2 previous errors +error: lifetime may not live long enough + --> $DIR/from-trait-impl.rs:13:1 + | +LL | fn func1(foo: Foo<(&str,)>) { + | ^^^^^^^^^^^^^^^^^^^-^^^^^^^ + | | | + | | let's call the lifetime of this reference `'1` + | requires that `'1` must outlive `'static` + +error: aborting due to 4 previous errors Some errors have detailed explanations: E0310, E0477. For more information about an error, try `rustc --explain E0310`. diff --git a/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-1.rs b/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-1.rs index b532a110a1c7b..83803646a2dc1 100644 --- a/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-1.rs +++ b/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy-1.rs @@ -32,7 +32,7 @@ fn main() { { let x = "Hello World".to_string(); subs_to_soup((x.as_str(), &mut d)); - //~^ does not live long enough + //~^ ERROR does not live long enough } println!("{}", d); } diff --git a/tests/ui/implied-bounds/issue-100690.stderr b/tests/ui/implied-bounds/issue-100690.stderr index 4964dccd551d9..d00895d8fc9cc 100644 --- a/tests/ui/implied-bounds/issue-100690.stderr +++ b/tests/ui/implied-bounds/issue-100690.stderr @@ -6,8 +6,8 @@ LL | real_dispatch(f) | | | required by a bound introduced by this call | - = note: expected a closure with arguments `(&mut UIView<'a, _>,)` - found a closure with arguments `(&mut UIView<'_, _>,)` + = note: expected a closure with signature `for<'a, 'b> fn(&'a mut UIView<'b, _>)` + found a closure with signature `fn(&mut UIView<'a, _>)` note: required by a bound in `real_dispatch` --> $DIR/issue-100690.rs:8:8 | diff --git a/tests/ui/implied-bounds/normalization-nested.lifetime.stderr b/tests/ui/implied-bounds/normalization-nested.lifetime.stderr deleted file mode 100644 index c43cc0999f1d7..0000000000000 --- a/tests/ui/implied-bounds/normalization-nested.lifetime.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/normalization-nested.rs:40:5 - | -LL | pub fn test_borrowck<'x>(_: Map>, s: &'x str) -> &'static str { - | -- lifetime `'x` defined here -LL | s - | ^ returning this value requires that `'x` must outlive `'static` - -error: aborting due to 1 previous error - diff --git a/tests/ui/implied-bounds/normalization-nested.rs b/tests/ui/implied-bounds/normalization-nested.rs index 4527e33a291bb..9a0defd16675a 100644 --- a/tests/ui/implied-bounds/normalization-nested.rs +++ b/tests/ui/implied-bounds/normalization-nested.rs @@ -3,9 +3,7 @@ // //@ revisions: param_ty lifetime param_ty_no_compat lifetime_no_compat -//@[param_ty] check-pass -//@[param_ty_no_compat] check-pass -//@[lifetime_no_compat] check-pass +//@ check-pass //@[param_ty_no_compat] compile-flags: -Zno-implied-bounds-compat //@[lifetime_no_compat] compile-flags: -Zno-implied-bounds-compat @@ -38,7 +36,6 @@ pub fn test_wfcheck<'x>(_: Map>) {} pub fn test_borrowck<'x>(_: Map>, s: &'x str) -> &'static str { s - //[lifetime]~^ ERROR lifetime may not live long enough } fn main() {} diff --git a/tests/ui/implied-bounds/normalization-placeholder-leak.fail.stderr b/tests/ui/implied-bounds/normalization-placeholder-leak.fail.stderr index a591d0f5d4d0a..8919919d04e5c 100644 --- a/tests/ui/implied-bounds/normalization-placeholder-leak.fail.stderr +++ b/tests/ui/implied-bounds/normalization-placeholder-leak.fail.stderr @@ -1,3 +1,9 @@ +error[E0477]: the type `&'lt u8` does not fulfill the required lifetime + --> $DIR/normalization-placeholder-leak.rs:31:5 + | +LL | fn test_lifetime<'lt, T: Trait>(_: Foo<&'lt u8>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0477]: the type `&'lt u8` does not fulfill the required lifetime --> $DIR/normalization-placeholder-leak.rs:31:40 | @@ -5,11 +11,35 @@ LL | fn test_lifetime<'lt, T: Trait>(_: Foo<&'lt u8>) {} | ^^^^^^^^^^^^ error[E0477]: the type `::Ty2<'lt>` does not fulfill the required lifetime - --> $DIR/normalization-placeholder-leak.rs:36:44 + --> $DIR/normalization-placeholder-leak.rs:38:5 + | +LL | fn test_alias<'lt, T: AnotherTrait>(_: Foo>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0477]: the type `::Ty2<'lt>` does not fulfill the required lifetime + --> $DIR/normalization-placeholder-leak.rs:38:44 | LL | fn test_alias<'lt, T: AnotherTrait>(_: Foo>) {} | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: lifetime may not live long enough + --> $DIR/normalization-placeholder-leak.rs:31:5 + | +LL | fn test_lifetime<'lt, T: Trait>(_: Foo<&'lt u8>) {} + | ^^^^^^^^^^^^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | lifetime `'lt` defined here + | requires that `'lt` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/normalization-placeholder-leak.rs:38:5 + | +LL | fn test_alias<'lt, T: AnotherTrait>(_: Foo>) {} + | ^^^^^^^^^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | lifetime `'lt` defined here + | requires that `'lt` must outlive `'static` + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0477`. diff --git a/tests/ui/implied-bounds/normalization-placeholder-leak.rs b/tests/ui/implied-bounds/normalization-placeholder-leak.rs index a9dfa69cfd6a4..3a15897db8211 100644 --- a/tests/ui/implied-bounds/normalization-placeholder-leak.rs +++ b/tests/ui/implied-bounds/normalization-placeholder-leak.rs @@ -30,11 +30,15 @@ mod fail { // don't use the bound to prove `'lt: 'static`. fn test_lifetime<'lt, T: Trait>(_: Foo<&'lt u8>) {} //[fail]~^ ERROR `&'lt u8` does not fulfill the required lifetime + //[fail]~| ERROR `&'lt u8` does not fulfill the required lifetime + //[fail]~| ERROR may not live long enough // implied bound: `T::Ty2<'lt>: placeholder('x)`. // don't use the bound to prove `T::Ty2<'lt>: 'static`. fn test_alias<'lt, T: AnotherTrait>(_: Foo>) {} //[fail]~^ ERROR `::Ty2<'lt>` does not fulfill the required lifetime + //[fail]~| ERROR `::Ty2<'lt>` does not fulfill the required lifetime + //[fail]~| ERROR may not live long enough } diff --git a/tests/ui/implied-bounds/overflow.rs b/tests/ui/implied-bounds/overflow.rs new file mode 100644 index 0000000000000..7c36998dd4d3b --- /dev/null +++ b/tests/ui/implied-bounds/overflow.rs @@ -0,0 +1,11 @@ +trait Tailed<'a>: 'a { + type Tail: Tailed<'a>; +} + +struct List<'a, T: Tailed<'a>> { + //~^ ERROR overflow computing implied lifetime bounds for `List` + next: Box>, + node: &'a T, +} + +fn main() {} diff --git a/tests/ui/implied-bounds/overflow.stderr b/tests/ui/implied-bounds/overflow.stderr new file mode 100644 index 0000000000000..2e5a9ab141cf8 --- /dev/null +++ b/tests/ui/implied-bounds/overflow.stderr @@ -0,0 +1,8 @@ +error: overflow computing implied lifetime bounds for `List` + --> $DIR/overflow.rs:5:1 + | +LL | struct List<'a, T: Tailed<'a>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/implied-bounds/sod_service_chain.rs b/tests/ui/implied-bounds/sod_service_chain.rs index 7443a29f30cbd..df1d430b37bc2 100644 --- a/tests/ui/implied-bounds/sod_service_chain.rs +++ b/tests/ui/implied-bounds/sod_service_chain.rs @@ -1,5 +1,7 @@ // Found in a crater run on #118553 +//@ dont-require-annotations: NOTE + pub trait Debug {} pub trait Service { @@ -27,11 +29,19 @@ pub struct ServiceChainBuilder> { } impl> ServiceChainBuilder { pub fn next>( + //~^ ERROR the associated type + //~| ERROR the associated type + //~| ERROR the associated type + //~| NOTE the associated type + //~| NOTE the associated type + //~| NOTE the associated type + //~| ERROR may not live long enough self, ) -> ServiceChainBuilder, NS> { - //~^ the associated type - //~| the associated type - //~| the associated type + //~^ ERROR the associated type + //~| ERROR the associated type + //~| NOTE the associated type + //~| NOTE the associated type panic!(); } } diff --git a/tests/ui/implied-bounds/sod_service_chain.stderr b/tests/ui/implied-bounds/sod_service_chain.stderr index 1c0ef573e7d33..a3cc131d235ff 100644 --- a/tests/ui/implied-bounds/sod_service_chain.stderr +++ b/tests/ui/implied-bounds/sod_service_chain.stderr @@ -1,5 +1,39 @@ error[E0310]: the associated type `

    ::Error` may not live long enough - --> $DIR/sod_service_chain.rs:31:10 + --> $DIR/sod_service_chain.rs:31:5 + | +LL | / pub fn next>( +... | +LL | | self, +LL | | ) -> ServiceChainBuilder, NS> { + | | ^ + | | | + | |____________________________________________________the associated type `

    ::Error` must be valid for the static lifetime... + | ...so that the type `

    ::Error` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | ) -> ServiceChainBuilder, NS> where

    ::Error: 'static { + | ++++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Error` may not live long enough + --> $DIR/sod_service_chain.rs:31:5 + | +LL | / pub fn next>( +... | +LL | | self, +LL | | ) -> ServiceChainBuilder, NS> { + | | ^ + | | | + | |____________________________________________________the associated type `::Error` must be valid for the static lifetime... + | ...so that the type `::Error` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | ) -> ServiceChainBuilder, NS> where ::Error: 'static { + | ++++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `

    ::Error` may not live long enough + --> $DIR/sod_service_chain.rs:40:10 | LL | ) -> ServiceChainBuilder, NS> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +47,7 @@ LL | ) -> ServiceChainBuilder, NS> where

    :: | ++++++++++++++++++++++++++++++++++++ error[E0310]: the associated type `::Error` may not live long enough - --> $DIR/sod_service_chain.rs:31:10 + --> $DIR/sod_service_chain.rs:40:10 | LL | ) -> ServiceChainBuilder, NS> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,6 +60,42 @@ help: consider adding an explicit lifetime bound LL | ) -> ServiceChainBuilder, NS> where ::Error: 'static { | ++++++++++++++++++++++++++++++++++++ -error: aborting due to 2 previous errors +error[E0310]: the associated type `

    ::Error` may not live long enough + --> $DIR/sod_service_chain.rs:31:5 + | +LL | / pub fn next>( +... | +LL | | self, +LL | | ) -> ServiceChainBuilder, NS> { + | | ^ + | | | + | |____________________________________________________the associated type `

    ::Error` must be valid for the static lifetime... + | ...so that the type `

    ::Error` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | ) -> ServiceChainBuilder, NS> where

    ::Error: 'static { + | ++++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Error` may not live long enough + --> $DIR/sod_service_chain.rs:31:5 + | +LL | / pub fn next>( +... | +LL | | self, +LL | | ) -> ServiceChainBuilder, NS> { + | | ^ + | | | + | |____________________________________________________the associated type `::Error` must be valid for the static lifetime... + | ...so that the type `::Error` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | ) -> ServiceChainBuilder, NS> where ::Error: 'static { + | ++++++++++++++++++++++++++++++++++++ + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/imports/glob-resolve1.stderr b/tests/ui/imports/glob-resolve1.stderr index 75e65681c3ab8..23b0db0fa465b 100644 --- a/tests/ui/imports/glob-resolve1.stderr +++ b/tests/ui/imports/glob-resolve1.stderr @@ -58,6 +58,11 @@ error[E0425]: cannot find function `import` in this scope LL | import(); | ^^^^^^ not found in this scope | +note: function `bar::import` exists but is inaccessible + --> $DIR/glob-resolve1.rs:7:5 + | +LL | fn fpriv() {} + | ^^^^^^^^^^ not accessible help: consider importing this function | LL + use other::import; diff --git a/tests/ui/imports/import-from-missing.rs b/tests/ui/imports/import-from-missing.rs index 8eae700208f99..1dcc3a9013378 100644 --- a/tests/ui/imports/import-from-missing.rs +++ b/tests/ui/imports/import-from-missing.rs @@ -1,5 +1,5 @@ use spam::{ham, eggs}; //~ ERROR unresolved import `spam::eggs` [E0432] - //~^ no `eggs` in `spam` + //~^ NOTE no `eggs` in `spam` mod spam { pub fn ham() { } diff --git a/tests/ui/imports/import-loop-2.rs b/tests/ui/imports/import-loop-2.rs index d9a56cb13784c..1bd0f06c671a3 100644 --- a/tests/ui/imports/import-loop-2.rs +++ b/tests/ui/imports/import-loop-2.rs @@ -1,11 +1,9 @@ -//@ error-pattern:import - mod a { pub use b::x; } mod b { - pub use a::x; + pub use a::x; //~ ERROR unresolved import `a::x` fn main() { let y = x; } } diff --git a/tests/ui/imports/import-loop-2.stderr b/tests/ui/imports/import-loop-2.stderr index 1a95200a66221..2521b6e7c0440 100644 --- a/tests/ui/imports/import-loop-2.stderr +++ b/tests/ui/imports/import-loop-2.stderr @@ -1,5 +1,5 @@ error[E0432]: unresolved import `a::x` - --> $DIR/import-loop-2.rs:8:13 + --> $DIR/import-loop-2.rs:6:13 | LL | pub use a::x; | ^^^^ no `x` in `a` diff --git a/tests/ui/imports/import-loop.rs b/tests/ui/imports/import-loop.rs index 1ba9e09003349..fc5bd32adeee9 100644 --- a/tests/ui/imports/import-loop.rs +++ b/tests/ui/imports/import-loop.rs @@ -1,9 +1,7 @@ -//@ error-pattern:import - use y::x; mod y { - pub use y::x; + pub use y::x; //~ ERROR unresolved import `y::x` } fn main() { } diff --git a/tests/ui/imports/import-loop.stderr b/tests/ui/imports/import-loop.stderr index 8ad2d6be4d27e..801fc2552b6ee 100644 --- a/tests/ui/imports/import-loop.stderr +++ b/tests/ui/imports/import-loop.stderr @@ -1,5 +1,5 @@ error[E0432]: unresolved import `y::x` - --> $DIR/import-loop.rs:6:13 + --> $DIR/import-loop.rs:4:13 | LL | pub use y::x; | ^^^^ no `x` in `y` diff --git a/tests/ui/imports/import.rs b/tests/ui/imports/import.rs index 3170dd2fae108..39a087c7a4394 100644 --- a/tests/ui/imports/import.rs +++ b/tests/ui/imports/import.rs @@ -1,6 +1,8 @@ +//@ dont-require-annotations: NOTE + use zed::bar; use zed::baz; //~ ERROR unresolved import `zed::baz` [E0432] - //~| no `baz` in `zed` + //~| NOTE no `baz` in `zed` //~| HELP a similar name exists in the module //~| SUGGESTION bar @@ -8,7 +10,7 @@ use zed::baz; //~ ERROR unresolved import `zed::baz` [E0432] mod zed { pub fn bar() { println!("bar"); } use foo; //~ ERROR unresolved import `foo` [E0432] - //~^ no `foo` in the root + //~^ NOTE no `foo` in the root } fn main() { diff --git a/tests/ui/imports/import.stderr b/tests/ui/imports/import.stderr index 797712e2db99e..4dae164991b2c 100644 --- a/tests/ui/imports/import.stderr +++ b/tests/ui/imports/import.stderr @@ -1,5 +1,5 @@ error[E0432]: unresolved import `zed::baz` - --> $DIR/import.rs:2:5 + --> $DIR/import.rs:4:5 | LL | use zed::baz; | ^^^^^--- @@ -8,19 +8,19 @@ LL | use zed::baz; | no `baz` in `zed` error[E0432]: unresolved import `foo` - --> $DIR/import.rs:10:9 + --> $DIR/import.rs:12:9 | LL | use foo; | ^^^ no `foo` in the root error[E0603]: unresolved item import `foo` is private - --> $DIR/import.rs:15:10 + --> $DIR/import.rs:17:10 | LL | zed::foo(); | ^^^ private unresolved item import | note: the unresolved item import `foo` is defined here - --> $DIR/import.rs:10:9 + --> $DIR/import.rs:12:9 | LL | use foo; | ^^^ diff --git a/tests/ui/imports/import2.rs b/tests/ui/imports/import2.rs index 036d6bc07e281..731ccb6f41c2e 100644 --- a/tests/ui/imports/import2.rs +++ b/tests/ui/imports/import2.rs @@ -1,5 +1,5 @@ use baz::zed::bar; //~ ERROR unresolved import `baz::zed` [E0432] - //~^ could not find `zed` in `baz` + //~^ NOTE could not find `zed` in `baz` mod baz {} mod zed { diff --git a/tests/ui/imports/import3.rs b/tests/ui/imports/import3.rs index 71eea0ebb2666..7bd6458cba103 100644 --- a/tests/ui/imports/import3.rs +++ b/tests/ui/imports/import3.rs @@ -1,4 +1,3 @@ -//@ error-pattern: unresolved -use main::bar; +use main::bar; //~ ERROR unresolved import `main` fn main() { println!("foo"); } diff --git a/tests/ui/imports/import3.stderr b/tests/ui/imports/import3.stderr index 7f58114678117..871104ce50da1 100644 --- a/tests/ui/imports/import3.stderr +++ b/tests/ui/imports/import3.stderr @@ -1,5 +1,5 @@ error[E0432]: unresolved import `main` - --> $DIR/import3.rs:2:5 + --> $DIR/import3.rs:1:5 | LL | use main::bar; | ^^^^ use of unresolved module or unlinked crate `main` diff --git a/tests/ui/imports/import4.rs b/tests/ui/imports/import4.rs index 8d727ced8907c..01535fc6f45d7 100644 --- a/tests/ui/imports/import4.rs +++ b/tests/ui/imports/import4.rs @@ -1,7 +1,4 @@ -//@ error-pattern: import - - mod a { pub use b::foo; } -mod b { pub use a::foo; } +mod b { pub use a::foo; } //~ ERROR unresolved import `a::foo` fn main() { println!("loop"); } diff --git a/tests/ui/imports/import4.stderr b/tests/ui/imports/import4.stderr index c99e838511877..c979d6c9ae24e 100644 --- a/tests/ui/imports/import4.stderr +++ b/tests/ui/imports/import4.stderr @@ -1,5 +1,5 @@ error[E0432]: unresolved import `a::foo` - --> $DIR/import4.rs:5:17 + --> $DIR/import4.rs:2:17 | LL | mod b { pub use a::foo; } | ^^^^^^ no `foo` in `a` diff --git a/tests/ui/imports/issue-13404.rs b/tests/ui/imports/issue-13404.rs index c5af827d50c4e..b6e5804abac48 100644 --- a/tests/ui/imports/issue-13404.rs +++ b/tests/ui/imports/issue-13404.rs @@ -1,6 +1,6 @@ use a::f; use b::f; //~ ERROR: unresolved import `b::f` [E0432] - //~^ no `f` in `b` + //~^ NOTE no `f` in `b` mod a { pub fn f() {} } mod b { } diff --git a/tests/ui/imports/issue-19498.rs b/tests/ui/imports/issue-19498.rs index 5fe6742f55e03..5b9ce85fd8bc7 100644 --- a/tests/ui/imports/issue-19498.rs +++ b/tests/ui/imports/issue-19498.rs @@ -1,13 +1,15 @@ +//@ dont-require-annotations: NOTE + use self::A; use self::B; mod A {} //~ ERROR the name `A` is defined multiple times -//~| `A` redefined here +//~| NOTE `A` redefined here pub mod B {} //~ ERROR the name `B` is defined multiple times -//~| `B` redefined here +//~| NOTE `B` redefined here mod C { use C::D; mod D {} //~ ERROR the name `D` is defined multiple times - //~| `D` redefined here + //~| NOTE `D` redefined here } fn main() {} diff --git a/tests/ui/imports/issue-19498.stderr b/tests/ui/imports/issue-19498.stderr index 69bdb67d3898b..eb0d68a24c908 100644 --- a/tests/ui/imports/issue-19498.stderr +++ b/tests/ui/imports/issue-19498.stderr @@ -1,5 +1,5 @@ error[E0255]: the name `A` is defined multiple times - --> $DIR/issue-19498.rs:3:1 + --> $DIR/issue-19498.rs:5:1 | LL | use self::A; | ------- previous import of the module `A` here @@ -14,7 +14,7 @@ LL | use self::A as OtherA; | +++++++++ error[E0255]: the name `B` is defined multiple times - --> $DIR/issue-19498.rs:5:1 + --> $DIR/issue-19498.rs:7:1 | LL | use self::B; | ------- previous import of the module `B` here @@ -29,7 +29,7 @@ LL | use self::B as OtherB; | +++++++++ error[E0255]: the name `D` is defined multiple times - --> $DIR/issue-19498.rs:9:5 + --> $DIR/issue-19498.rs:11:5 | LL | use C::D; | ---- previous import of the module `D` here diff --git a/tests/ui/imports/issue-24081.rs b/tests/ui/imports/issue-24081.rs index 10983ce11b501..561bbb067b8b4 100644 --- a/tests/ui/imports/issue-24081.rs +++ b/tests/ui/imports/issue-24081.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + use std::ops::Add; use std::ops::Sub; use std::ops::Mul; @@ -5,14 +7,14 @@ use std::ops::Div; use std::ops::Rem; type Add = bool; //~ ERROR the name `Add` is defined multiple times -//~| `Add` redefined here +//~| NOTE `Add` redefined here struct Sub { x: f32 } //~ ERROR the name `Sub` is defined multiple times -//~| `Sub` redefined here +//~| NOTE `Sub` redefined here enum Mul { A, B } //~ ERROR the name `Mul` is defined multiple times -//~| `Mul` redefined here +//~| NOTE `Mul` redefined here mod Div { } //~ ERROR the name `Div` is defined multiple times -//~| `Div` redefined here +//~| NOTE `Div` redefined here trait Rem { } //~ ERROR the name `Rem` is defined multiple times -//~| `Rem` redefined here +//~| NOTE `Rem` redefined here fn main() {} diff --git a/tests/ui/imports/issue-24081.stderr b/tests/ui/imports/issue-24081.stderr index 6ceba6c7013ac..c2346f524ce35 100644 --- a/tests/ui/imports/issue-24081.stderr +++ b/tests/ui/imports/issue-24081.stderr @@ -1,5 +1,5 @@ error[E0255]: the name `Add` is defined multiple times - --> $DIR/issue-24081.rs:7:1 + --> $DIR/issue-24081.rs:9:1 | LL | use std::ops::Add; | ------------- previous import of the trait `Add` here @@ -14,7 +14,7 @@ LL | use std::ops::Add as OtherAdd; | +++++++++++ error[E0255]: the name `Sub` is defined multiple times - --> $DIR/issue-24081.rs:9:1 + --> $DIR/issue-24081.rs:11:1 | LL | use std::ops::Sub; | ------------- previous import of the trait `Sub` here @@ -29,7 +29,7 @@ LL | use std::ops::Sub as OtherSub; | +++++++++++ error[E0255]: the name `Mul` is defined multiple times - --> $DIR/issue-24081.rs:11:1 + --> $DIR/issue-24081.rs:13:1 | LL | use std::ops::Mul; | ------------- previous import of the trait `Mul` here @@ -44,7 +44,7 @@ LL | use std::ops::Mul as OtherMul; | +++++++++++ error[E0255]: the name `Div` is defined multiple times - --> $DIR/issue-24081.rs:13:1 + --> $DIR/issue-24081.rs:15:1 | LL | use std::ops::Div; | ------------- previous import of the trait `Div` here @@ -59,7 +59,7 @@ LL | use std::ops::Div as OtherDiv; | +++++++++++ error[E0255]: the name `Rem` is defined multiple times - --> $DIR/issue-24081.rs:15:1 + --> $DIR/issue-24081.rs:17:1 | LL | use std::ops::Rem; | ------------- previous import of the trait `Rem` here diff --git a/tests/ui/imports/issue-26886.rs b/tests/ui/imports/issue-26886.rs index 6e6d406c656d6..91be86ae7749c 100644 --- a/tests/ui/imports/issue-26886.rs +++ b/tests/ui/imports/issue-26886.rs @@ -1,8 +1,10 @@ +//@ dont-require-annotations: NOTE + use std::sync::{self, Arc}; use std::sync::Arc; //~ ERROR the name `Arc` is defined multiple times - //~| `Arc` must be defined only once in the type namespace of this module + //~| NOTE `Arc` must be defined only once in the type namespace of this module use std::sync; //~ ERROR the name `sync` is defined multiple times - //~| `sync` must be defined only once in the type namespace of this module + //~| NOTE `sync` must be defined only once in the type namespace of this module fn main() { } diff --git a/tests/ui/imports/issue-26886.stderr b/tests/ui/imports/issue-26886.stderr index e2b925ec5a705..a6143543446c0 100644 --- a/tests/ui/imports/issue-26886.stderr +++ b/tests/ui/imports/issue-26886.stderr @@ -1,5 +1,5 @@ error[E0252]: the name `Arc` is defined multiple times - --> $DIR/issue-26886.rs:2:5 + --> $DIR/issue-26886.rs:4:5 | LL | use std::sync::{self, Arc}; | --- previous import of the type `Arc` here @@ -9,7 +9,7 @@ LL | use std::sync::Arc; = note: `Arc` must be defined only once in the type namespace of this module error[E0252]: the name `sync` is defined multiple times - --> $DIR/issue-26886.rs:4:5 + --> $DIR/issue-26886.rs:6:5 | LL | use std::sync::{self, Arc}; | ---- previous import of the module `sync` here diff --git a/tests/ui/imports/issue-2937.rs b/tests/ui/imports/issue-2937.rs index 335df5c07e256..7ca6ffb479e05 100644 --- a/tests/ui/imports/issue-2937.rs +++ b/tests/ui/imports/issue-2937.rs @@ -1,5 +1,5 @@ use m::f as x; //~ ERROR unresolved import `m::f` [E0432] - //~^ no `f` in `m` + //~^ NOTE no `f` in `m` mod m {} diff --git a/tests/ui/imports/issue-30560.rs b/tests/ui/imports/issue-30560.rs index d8d4ca608f1c0..fe5d17c187364 100644 --- a/tests/ui/imports/issue-30560.rs +++ b/tests/ui/imports/issue-30560.rs @@ -3,7 +3,4 @@ use Alias::*; //~ ERROR unresolved import `Alias` [E0432] use std::io::Result::*; //~ ERROR unresolved import `std::io::Result` [E0432] -trait T {} -use T::*; //~ ERROR items in traits are not importable - fn main() {} diff --git a/tests/ui/imports/issue-30560.stderr b/tests/ui/imports/issue-30560.stderr index 69cfd4c06a8b8..89492261cbaff 100644 --- a/tests/ui/imports/issue-30560.stderr +++ b/tests/ui/imports/issue-30560.stderr @@ -1,9 +1,3 @@ -error: items in traits are not importable - --> $DIR/issue-30560.rs:7:5 - | -LL | use T::*; - | ^^^^ - error[E0432]: unresolved import `Alias` --> $DIR/issue-30560.rs:2:5 | @@ -16,6 +10,6 @@ error[E0432]: unresolved import `std::io::Result` LL | use std::io::Result::*; | ^^^^^^ `Result` is a type alias, not a module -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/issue-32833.rs b/tests/ui/imports/issue-32833.rs index 379eedde72635..8f82eb68c1ff2 100644 --- a/tests/ui/imports/issue-32833.rs +++ b/tests/ui/imports/issue-32833.rs @@ -1,5 +1,5 @@ use bar::Foo; //~ ERROR unresolved import `bar::Foo` [E0432] - //~^ no `Foo` in `bar` + //~^ NOTE no `Foo` in `bar` mod bar { use Foo; } diff --git a/tests/ui/imports/issue-4366-2.stderr b/tests/ui/imports/issue-4366-2.stderr index 412423f4d595e..b1c0092b05d11 100644 --- a/tests/ui/imports/issue-4366-2.stderr +++ b/tests/ui/imports/issue-4366-2.stderr @@ -16,6 +16,11 @@ error[E0423]: expected function, found module `foo` LL | foo(); | ^^^ not a function | +note: function `m1::foo` exists but is inaccessible + --> $DIR/issue-4366-2.rs:21:5 + | +LL | fn foo() {} + | ^^^^^^^^ not accessible help: consider importing this function instead | LL + use foo::foo; diff --git a/tests/ui/imports/issue-4366.stderr b/tests/ui/imports/issue-4366.stderr index e63399d554ee3..54b7f31b2313a 100644 --- a/tests/ui/imports/issue-4366.stderr +++ b/tests/ui/imports/issue-4366.stderr @@ -4,6 +4,11 @@ error[E0425]: cannot find function `foo` in this scope LL | fn sub() -> isize { foo(); 1 } | ^^^ not found in this scope | +note: function `m1::foo` exists but is inaccessible + --> $DIR/issue-4366.rs:23:5 + | +LL | fn foo() {} + | ^^^^^^^^ not accessible help: consider importing this function | LL + use foo::foo; diff --git a/tests/ui/imports/issue-8208.rs b/tests/ui/imports/issue-8208.rs index 1c566938f9d0e..997d4d227b3d7 100644 --- a/tests/ui/imports/issue-8208.rs +++ b/tests/ui/imports/issue-8208.rs @@ -1,14 +1,14 @@ use self::*; //~ ERROR: unresolved import `self::*` [E0432] - //~^ cannot glob-import a module into itself + //~^ NOTE cannot glob-import a module into itself mod foo { use foo::*; //~ ERROR: unresolved import `foo::*` [E0432] - //~^ cannot glob-import a module into itself + //~^ NOTE cannot glob-import a module into itself mod bar { use super::bar::*; //~^ ERROR: unresolved import `super::bar::*` [E0432] - //~| cannot glob-import a module into itself + //~| NOTE cannot glob-import a module into itself } } diff --git a/tests/ui/imports/issue-99695-b.fixed b/tests/ui/imports/issue-99695-b.fixed index 0108f762400f6..ae63b0c4627c5 100644 --- a/tests/ui/imports/issue-99695-b.fixed +++ b/tests/ui/imports/issue-99695-b.fixed @@ -11,7 +11,7 @@ mod m { pub struct other_item; } - use ::nu; + use crate::nu; pub use self::p::{other_item as _}; //~^ ERROR unresolved import `self::p::nu` [E0432] //~| HELP a macro with this name exists at the root of the crate diff --git a/tests/ui/imports/issue-99695-b.stderr b/tests/ui/imports/issue-99695-b.stderr index d58d2798746c1..ad752d5c45a7e 100644 --- a/tests/ui/imports/issue-99695-b.stderr +++ b/tests/ui/imports/issue-99695-b.stderr @@ -7,7 +7,7 @@ LL | pub use self::p::{nu, other_item as _}; = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined help: a macro with this name exists at the root of the crate | -LL ~ use ::nu; +LL ~ use crate::nu; LL ~ pub use self::p::{other_item as _}; | diff --git a/tests/ui/imports/issue-99695.edition_2015.fixed b/tests/ui/imports/issue-99695.edition_2015.fixed new file mode 100644 index 0000000000000..798acfd587467 --- /dev/null +++ b/tests/ui/imports/issue-99695.edition_2015.fixed @@ -0,0 +1,21 @@ +//@ run-rustfix +//@ revisions: edition_2015 edition_2018 +//@ [edition_2015] edition: 2015 +//@ [edition_2018] edition: 2018 + +#![allow(unused, nonstandard_style)] +mod m { + #[macro_export] + macro_rules! nu { + {} => {}; + } + + pub struct other_item; + + use crate::nu; +pub use self::{other_item as _}; + //~^ ERROR unresolved import `self::nu` [E0432] + //~| HELP a macro with this name exists at the root of the crate +} + +fn main() {} diff --git a/tests/ui/imports/issue-99695.edition_2015.stderr b/tests/ui/imports/issue-99695.edition_2015.stderr new file mode 100644 index 0000000000000..4ef8e6426fb29 --- /dev/null +++ b/tests/ui/imports/issue-99695.edition_2015.stderr @@ -0,0 +1,16 @@ +error[E0432]: unresolved import `self::nu` + --> $DIR/issue-99695.rs:15:20 + | +LL | pub use self::{nu, other_item as _}; + | ^^ no `nu` in `m` + | + = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined +help: a macro with this name exists at the root of the crate + | +LL ~ use crate::nu; +LL ~ pub use self::{other_item as _}; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/issue-99695.edition_2018.fixed b/tests/ui/imports/issue-99695.edition_2018.fixed new file mode 100644 index 0000000000000..798acfd587467 --- /dev/null +++ b/tests/ui/imports/issue-99695.edition_2018.fixed @@ -0,0 +1,21 @@ +//@ run-rustfix +//@ revisions: edition_2015 edition_2018 +//@ [edition_2015] edition: 2015 +//@ [edition_2018] edition: 2018 + +#![allow(unused, nonstandard_style)] +mod m { + #[macro_export] + macro_rules! nu { + {} => {}; + } + + pub struct other_item; + + use crate::nu; +pub use self::{other_item as _}; + //~^ ERROR unresolved import `self::nu` [E0432] + //~| HELP a macro with this name exists at the root of the crate +} + +fn main() {} diff --git a/tests/ui/imports/issue-99695.edition_2018.stderr b/tests/ui/imports/issue-99695.edition_2018.stderr new file mode 100644 index 0000000000000..4ef8e6426fb29 --- /dev/null +++ b/tests/ui/imports/issue-99695.edition_2018.stderr @@ -0,0 +1,16 @@ +error[E0432]: unresolved import `self::nu` + --> $DIR/issue-99695.rs:15:20 + | +LL | pub use self::{nu, other_item as _}; + | ^^ no `nu` in `m` + | + = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined +help: a macro with this name exists at the root of the crate + | +LL ~ use crate::nu; +LL ~ pub use self::{other_item as _}; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/issue-99695.fixed b/tests/ui/imports/issue-99695.fixed deleted file mode 100644 index 51ccd3f8c48d1..0000000000000 --- a/tests/ui/imports/issue-99695.fixed +++ /dev/null @@ -1,17 +0,0 @@ -//@ run-rustfix -#![allow(unused, nonstandard_style)] -mod m { - #[macro_export] - macro_rules! nu { - {} => {}; - } - - pub struct other_item; - - use ::nu; -pub use self::{other_item as _}; - //~^ ERROR unresolved import `self::nu` [E0432] - //~| HELP a macro with this name exists at the root of the crate -} - -fn main() {} diff --git a/tests/ui/imports/issue-99695.rs b/tests/ui/imports/issue-99695.rs index a52370e0eb085..dc0a8f438e008 100644 --- a/tests/ui/imports/issue-99695.rs +++ b/tests/ui/imports/issue-99695.rs @@ -1,4 +1,8 @@ //@ run-rustfix +//@ revisions: edition_2015 edition_2018 +//@ [edition_2015] edition: 2015 +//@ [edition_2018] edition: 2018 + #![allow(unused, nonstandard_style)] mod m { #[macro_export] diff --git a/tests/ui/imports/issue-99695.stderr b/tests/ui/imports/issue-99695.stderr deleted file mode 100644 index 536f51dcb3b20..0000000000000 --- a/tests/ui/imports/issue-99695.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0432]: unresolved import `self::nu` - --> $DIR/issue-99695.rs:11:20 - | -LL | pub use self::{nu, other_item as _}; - | ^^ no `nu` in `m` - | - = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined -help: a macro with this name exists at the root of the crate - | -LL ~ use ::nu; -LL ~ pub use self::{other_item as _}; - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/redundant-import-extern-prelude.rs b/tests/ui/imports/redundant-import-extern-prelude.rs index 0064eaa931882..b573b8fc61095 100644 --- a/tests/ui/imports/redundant-import-extern-prelude.rs +++ b/tests/ui/imports/redundant-import-extern-prelude.rs @@ -5,7 +5,8 @@ // See also the discussion in . -//@ compile-flags: --extern aux_issue_121915 --edition 2018 +//@ compile-flags: --extern aux_issue_121915 +//@ edition: 2018 //@ aux-build: aux-issue-121915.rs #[deny(redundant_imports)] diff --git a/tests/ui/imports/redundant-import-extern-prelude.stderr b/tests/ui/imports/redundant-import-extern-prelude.stderr index 6d2518c1284b6..06cce7e17256c 100644 --- a/tests/ui/imports/redundant-import-extern-prelude.stderr +++ b/tests/ui/imports/redundant-import-extern-prelude.stderr @@ -1,11 +1,11 @@ error: the item `aux_issue_121915` is imported redundantly - --> $DIR/redundant-import-extern-prelude.rs:14:9 + --> $DIR/redundant-import-extern-prelude.rs:15:9 | LL | use aux_issue_121915; | ^^^^^^^^^^^^^^^^ the item `aux_issue_121915` is already defined by the extern prelude | note: the lint level is defined here - --> $DIR/redundant-import-extern-prelude.rs:11:8 + --> $DIR/redundant-import-extern-prelude.rs:12:8 | LL | #[deny(redundant_imports)] | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/imports/redundant-import-issue-121915-2015.rs b/tests/ui/imports/redundant-import-issue-121915-2015.rs index dc499bc40b616..7108776757e96 100644 --- a/tests/ui/imports/redundant-import-issue-121915-2015.rs +++ b/tests/ui/imports/redundant-import-issue-121915-2015.rs @@ -1,4 +1,5 @@ -//@ compile-flags: --extern aux_issue_121915 --edition 2015 +//@ compile-flags: --extern aux_issue_121915 +//@ edition: 2015 //@ aux-build: aux-issue-121915.rs extern crate aux_issue_121915; diff --git a/tests/ui/imports/redundant-import-issue-121915-2015.stderr b/tests/ui/imports/redundant-import-issue-121915-2015.stderr index f4e9f604896ab..367a4f1cd15a5 100644 --- a/tests/ui/imports/redundant-import-issue-121915-2015.stderr +++ b/tests/ui/imports/redundant-import-issue-121915-2015.stderr @@ -1,5 +1,5 @@ error: the item `aux_issue_121915` is imported redundantly - --> $DIR/redundant-import-issue-121915-2015.rs:8:9 + --> $DIR/redundant-import-issue-121915-2015.rs:9:9 | LL | extern crate aux_issue_121915; | ------------------------------ the item `aux_issue_121915` is already imported here @@ -8,7 +8,7 @@ LL | use aux_issue_121915; | ^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/redundant-import-issue-121915-2015.rs:6:8 + --> $DIR/redundant-import-issue-121915-2015.rs:7:8 | LL | #[deny(redundant_imports)] | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/imports/reexports.stderr b/tests/ui/imports/reexports.stderr index bf4ba474875bf..fa05c0c0f8e84 100644 --- a/tests/ui/imports/reexports.stderr +++ b/tests/ui/imports/reexports.stderr @@ -62,7 +62,7 @@ warning: glob import doesn't reexport anything with visibility `pub` because no LL | pub use super::*; | ^^^^^^^^ | -note: the most public imported item is `pub(a)` +note: the most public imported item is `pub(in crate::a)` --> $DIR/reexports.rs:11:17 | LL | pub use super::*; diff --git a/tests/ui/imports/show-private-items-issue-138626.rs b/tests/ui/imports/show-private-items-issue-138626.rs new file mode 100644 index 0000000000000..d9708fc33c215 --- /dev/null +++ b/tests/ui/imports/show-private-items-issue-138626.rs @@ -0,0 +1,19 @@ +pub mod one { + mod foo { + pub struct Foo; + } + + pub use self::foo::Foo; +} + +pub mod two { + mod foo { + mod bar { + pub struct Foo; + } + } + + pub use crate::two::foo::Foo; //~ ERROR unresolved import `crate::two::foo::Foo` [E0432] +} + +fn main() {} diff --git a/tests/ui/imports/show-private-items-issue-138626.stderr b/tests/ui/imports/show-private-items-issue-138626.stderr new file mode 100644 index 0000000000000..b664462daed7d --- /dev/null +++ b/tests/ui/imports/show-private-items-issue-138626.stderr @@ -0,0 +1,20 @@ +error[E0432]: unresolved import `crate::two::foo::Foo` + --> $DIR/show-private-items-issue-138626.rs:16:13 + | +LL | pub use crate::two::foo::Foo; + | ^^^^^^^^^^^^^^^^^^^^ no `Foo` in `two::foo` + | +note: struct `two::foo::bar::Foo` exists but is inaccessible + --> $DIR/show-private-items-issue-138626.rs:12:13 + | +LL | pub struct Foo; + | ^^^^^^^^^^^^^^^ not accessible +help: consider importing this struct through its public re-export instead + | +LL - pub use crate::two::foo::Foo; +LL + pub use one::Foo; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/suggest-remove-issue-121315.rs b/tests/ui/imports/suggest-remove-issue-121315.rs index ee3ceb6e3a380..3c036b843fd42 100644 --- a/tests/ui/imports/suggest-remove-issue-121315.rs +++ b/tests/ui/imports/suggest-remove-issue-121315.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2021 +//@ edition: 2021 #![deny(unused_imports, redundant_imports)] #![allow(dead_code)] diff --git a/tests/ui/include-macros/mismatched-types.stderr b/tests/ui/include-macros/mismatched-types.stderr index 9bc0e64464e23..8d541966a6a40 100644 --- a/tests/ui/include-macros/mismatched-types.stderr +++ b/tests/ui/include-macros/mismatched-types.stderr @@ -11,7 +11,6 @@ LL | let b: &[u8] = include_str!("file.txt"); | = note: expected reference `&[u8]` found reference `&'static str` - = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/mismatched-types.rs:3:19 @@ -23,7 +22,6 @@ LL | let s: &str = include_bytes!("file.txt"); | = note: expected reference `&str` found reference `&'static [u8; 0]` - = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/include-macros/parent_dir.stderr b/tests/ui/include-macros/parent_dir.stderr index d0a1f4fd3b91f..4ee6fe104b06a 100644 --- a/tests/ui/include-macros/parent_dir.stderr +++ b/tests/ui/include-macros/parent_dir.stderr @@ -4,7 +4,6 @@ error: couldn't read `$DIR/include-macros/file.txt`: $FILE_NOT_FOUND_MSG LL | let _ = include_str!("include-macros/file.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) help: there is a file with the same name in a different directory | LL - let _ = include_str!("include-macros/file.txt"); @@ -17,7 +16,6 @@ error: couldn't read `$DIR/hello.rs`: $FILE_NOT_FOUND_MSG LL | let _ = include_str!("hello.rs"); | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) help: there is a file with the same name in a different directory | LL | let _ = include_str!("../hello.rs"); @@ -29,7 +27,6 @@ error: couldn't read `$DIR/../../data.bin`: $FILE_NOT_FOUND_MSG LL | let _ = include_bytes!("../../data.bin"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) help: there is a file with the same name in a different directory | LL - let _ = include_bytes!("../../data.bin"); @@ -42,7 +39,6 @@ error: couldn't read `$DIR/tests/ui/include-macros/file.txt`: $FILE_NOT_FOUND_MS LL | let _ = include_str!("tests/ui/include-macros/file.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) help: there is a file with the same name in a different directory | LL - let _ = include_str!("tests/ui/include-macros/file.txt"); diff --git a/tests/ui/indexing/index-help.stderr b/tests/ui/indexing/index-help.stderr index 1974e13eabcee..ac79e3f12bd03 100644 --- a/tests/ui/indexing/index-help.stderr +++ b/tests/ui/indexing/index-help.stderr @@ -5,8 +5,9 @@ LL | x[0i32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[{integer}]>` is not implemented for `i32` - but it is implemented for `usize` - = help: for that trait implementation, expected `usize`, found `i32` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `Vec<{integer}>` to implement `Index` error: aborting due to 1 previous error diff --git a/tests/ui/indexing/indexing-requires-a-uint.stderr b/tests/ui/indexing/indexing-requires-a-uint.stderr index 5c60a30946d37..62a1ca3d0573c 100644 --- a/tests/ui/indexing/indexing-requires-a-uint.stderr +++ b/tests/ui/indexing/indexing-requires-a-uint.stderr @@ -5,8 +5,9 @@ LL | [0][0u8]; | ^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[{integer}]>` is not implemented for `u8` - but it is implemented for `usize` - = help: for that trait implementation, expected `usize`, found `u8` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `[{integer}]` to implement `Index` = note: 1 redundant requirement hidden = note: required for `[{integer}; 1]` to implement `Index` diff --git a/tests/ui/inference/auto-instantiate.rs b/tests/ui/inference/auto-instantiate.rs new file mode 100644 index 0000000000000..bf43330a0b77c --- /dev/null +++ b/tests/ui/inference/auto-instantiate.rs @@ -0,0 +1,28 @@ +//! Check that type parameters in generic function arg position and in "nested" return type position +//! can be inferred on an invocation of the generic function. +//! +//! See . + +//@ run-pass + +#![allow(dead_code)] +#[derive(Debug)] +struct Pair { + a: T, + b: U, +} + +struct Triple { + x: isize, + y: isize, + z: isize, +} + +fn f(x: T, y: U) -> Pair { + return Pair { a: x, b: y }; +} + +pub fn main() { + println!("{}", f(Triple {x: 3, y: 4, z: 5}, 4).a.x); + println!("{}", f(5, 6).a); +} diff --git a/tests/ui/inference/detect-old-time-version-format_description-parse.rs b/tests/ui/inference/detect-old-time-version-format_description-parse.rs deleted file mode 100644 index 386b2a3bf3c8a..0000000000000 --- a/tests/ui/inference/detect-old-time-version-format_description-parse.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![crate_name = "time"] -#![crate_type = "lib"] - -// This code compiled without error in Rust 1.79, but started failing in 1.80 -// after the addition of several `impl FromIterator<_> for Box`. - -pub fn parse() -> Option> { - let iter = std::iter::once(Some(())).map(|o| o.map(Into::into)); - let items = iter.collect::>>()?; //~ ERROR E0282 - //~^ NOTE this is an inference error on crate `time` caused by an API change in Rust 1.80.0; update `time` to version `>=0.3.35` - Some(items.into()) - //~^ NOTE type must be known at this point -} diff --git a/tests/ui/inference/detect-old-time-version-format_description-parse.stderr b/tests/ui/inference/detect-old-time-version-format_description-parse.stderr deleted file mode 100644 index a70ce9dd2681c..0000000000000 --- a/tests/ui/inference/detect-old-time-version-format_description-parse.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0282]: type annotations needed for `Box<_>` - --> $DIR/detect-old-time-version-format_description-parse.rs:9:9 - | -LL | let items = iter.collect::>>()?; - | ^^^^^ -LL | -LL | Some(items.into()) - | ---- type must be known at this point - | - = note: this is an inference error on crate `time` caused by an API change in Rust 1.80.0; update `time` to version `>=0.3.35` by calling `cargo update` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/inference/issue-86094-suggest-add-return-to-coerce-ret-ty.stderr b/tests/ui/inference/issue-86094-suggest-add-return-to-coerce-ret-ty.stderr index 1fea73529a8a2..c61ca699b0d35 100644 --- a/tests/ui/inference/issue-86094-suggest-add-return-to-coerce-ret-ty.stderr +++ b/tests/ui/inference/issue-86094-suggest-add-return-to-coerce-ret-ty.stderr @@ -8,10 +8,6 @@ help: consider specifying the generic arguments | LL | Err::(MyError); | ++++++++++++++ -help: you might have meant to return this to infer its type parameters - | -LL | return Err(MyError); - | ++++++ error[E0282]: type annotations needed --> $DIR/issue-86094-suggest-add-return-to-coerce-ret-ty.rs:14:9 @@ -23,10 +19,6 @@ help: consider specifying the generic arguments | LL | Ok::<(), E>(()); | +++++++++ -help: you might have meant to return this to infer its type parameters - | -LL | return Ok(()); - | ++++++ error[E0308]: mismatched types --> $DIR/issue-86094-suggest-add-return-to-coerce-ret-ty.rs:21:20 diff --git a/tests/ui/inference/tutorial-suffix-inference-test.rs b/tests/ui/inference/tutorial-suffix-inference-test.rs index 849adfd53686b..ca593ff1b70c6 100644 --- a/tests/ui/inference/tutorial-suffix-inference-test.rs +++ b/tests/ui/inference/tutorial-suffix-inference-test.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + fn main() { let x = 3; let y: i32 = 3; @@ -8,10 +10,10 @@ fn main() { identity_u8(x); // after this, `x` is assumed to have type `u8` identity_u16(x); //~^ ERROR mismatched types - //~| expected `u16`, found `u8` + //~| NOTE expected `u16`, found `u8` identity_u16(y); //~^ ERROR mismatched types - //~| expected `u16`, found `i32` + //~| NOTE expected `u16`, found `i32` let a = 3; @@ -20,5 +22,5 @@ fn main() { identity_i(a); // ok identity_u16(a); //~^ ERROR mismatched types - //~| expected `u16`, found `isize` + //~| NOTE expected `u16`, found `isize` } diff --git a/tests/ui/inference/tutorial-suffix-inference-test.stderr b/tests/ui/inference/tutorial-suffix-inference-test.stderr index d83a1367dbfae..39627aa9fc43c 100644 --- a/tests/ui/inference/tutorial-suffix-inference-test.stderr +++ b/tests/ui/inference/tutorial-suffix-inference-test.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/tutorial-suffix-inference-test.rs:9:18 + --> $DIR/tutorial-suffix-inference-test.rs:11:18 | LL | identity_u16(x); | ------------ ^ expected `u16`, found `u8` @@ -7,7 +7,7 @@ LL | identity_u16(x); | arguments to this function are incorrect | note: function defined here - --> $DIR/tutorial-suffix-inference-test.rs:6:8 + --> $DIR/tutorial-suffix-inference-test.rs:8:8 | LL | fn identity_u16(n: u16) -> u16 { n } | ^^^^^^^^^^^^ ------ @@ -17,7 +17,7 @@ LL | identity_u16(x.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/tutorial-suffix-inference-test.rs:12:18 + --> $DIR/tutorial-suffix-inference-test.rs:14:18 | LL | identity_u16(y); | ------------ ^ expected `u16`, found `i32` @@ -25,7 +25,7 @@ LL | identity_u16(y); | arguments to this function are incorrect | note: function defined here - --> $DIR/tutorial-suffix-inference-test.rs:6:8 + --> $DIR/tutorial-suffix-inference-test.rs:8:8 | LL | fn identity_u16(n: u16) -> u16 { n } | ^^^^^^^^^^^^ ------ @@ -35,7 +35,7 @@ LL | identity_u16(y.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/tutorial-suffix-inference-test.rs:21:18 + --> $DIR/tutorial-suffix-inference-test.rs:23:18 | LL | identity_u16(a); | ------------ ^ expected `u16`, found `isize` @@ -43,7 +43,7 @@ LL | identity_u16(a); | arguments to this function are incorrect | note: function defined here - --> $DIR/tutorial-suffix-inference-test.rs:6:8 + --> $DIR/tutorial-suffix-inference-test.rs:8:8 | LL | fn identity_u16(n: u16) -> u16 { n } | ^^^^^^^^^^^^ ------ diff --git a/tests/ui/infinite/infinite-autoderef.stderr b/tests/ui/infinite/infinite-autoderef.stderr index 7d09af9a7d4af..7770cc8a72047 100644 --- a/tests/ui/infinite/infinite-autoderef.stderr +++ b/tests/ui/infinite/infinite-autoderef.stderr @@ -1,8 +1,8 @@ error[E0275]: overflow assigning `Box<_>` to `_` - --> $DIR/infinite-autoderef.rs:16:13 + --> $DIR/infinite-autoderef.rs:16:22 | LL | x = Box::new(x); - | ^^^^^^^^^^^ + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs index 62fc079d9d974..f50c4a5207a5f 100644 --- a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs +++ b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs @@ -1,11 +1,6 @@ +//~ ERROR reached the recursion limit while instantiating ` "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" - // Regression test for #114484: This used to ICE during monomorphization, because we treated // ` as Pointee>::Metadata` as a rigid projection after reaching the recursion // limit when finding the struct tail. @@ -76,3 +71,16 @@ fn main() { let test = SomeData([0; 256]); test.virtualize(); } + +//~? ERROR reached the recursion limit finding the struct tail for `[u8; 256]` +//~? ERROR reached the recursion limit finding the struct tail for `[u8; 256]` +//~? ERROR reached the recursion limit finding the struct tail for `[u8; 256]` +//~? ERROR reached the recursion limit finding the struct tail for `[u8; 256]` +//~? ERROR reached the recursion limit finding the struct tail for `SomeData<256>` +//~? ERROR reached the recursion limit finding the struct tail for `SomeData<256>` +//~? ERROR reached the recursion limit finding the struct tail for `SomeData<256>` +//~? ERROR reached the recursion limit finding the struct tail for `SomeData<256>` +//~? ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` +//~? ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` +//~? ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` +//~? ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` diff --git a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr index b67e000bf74a8..59addc5cc4a7d 100644 --- a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr +++ b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr @@ -18,7 +18,7 @@ error: reached the recursion limit finding the struct tail for `[u8; 256]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: the above error was encountered while instantiating `fn virtualize_my_trait::, 0>, 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>, 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>, 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>, 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>>` - --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:29:18 + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:24:18 | LL | unsafe { virtualize_my_trait(L, self) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -43,7 +43,7 @@ error: reached the recursion limit finding the struct tail for `SomeData<256>` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: the above error was encountered while instantiating `fn virtualize_my_trait::, 0>, 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>, 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>, 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>, 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>>` - --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:29:18 + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:24:18 | LL | unsafe { virtualize_my_trait(L, self) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ error: reached the recursion limit finding the struct tail for `VirtualWrapper, 0>, 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>, 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>, 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>, 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>>` - --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:29:18 + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:24:18 | LL | unsafe { virtualize_my_trait(L, self) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,11 +76,11 @@ LL | unsafe { virtualize_my_trait(L, self) } error: reached the recursion limit while instantiating `, 1>, 1>, 1>, 1> as MyTrait>::virtualize` | note: ` as MyTrait>::virtualize` defined here - --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:28:5 + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:23:5 | LL | fn virtualize(&self) -> &dyn MyTrait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/infinite-instantiation-struct-tail-ice-114484.long-type.txt' error: aborting due to 13 previous errors diff --git a/tests/ui/infinite/infinite-instantiation.rs b/tests/ui/infinite/infinite-instantiation.rs index d5cb8e79592f1..7898cc1ffc106 100644 --- a/tests/ui/infinite/infinite-instantiation.rs +++ b/tests/ui/infinite/infinite-instantiation.rs @@ -1,6 +1,4 @@ //@ build-fail -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" trait ToOpt: Sized { fn to_option(&self) -> Option; diff --git a/tests/ui/infinite/infinite-instantiation.stderr b/tests/ui/infinite/infinite-instantiation.stderr index 71c745cf5eb8b..d7a4a49961a64 100644 --- a/tests/ui/infinite/infinite-instantiation.stderr +++ b/tests/ui/infinite/infinite-instantiation.stderr @@ -1,15 +1,15 @@ error: reached the recursion limit while instantiating `function::>>>>>` - --> $DIR/infinite-instantiation.rs:23:9 + --> $DIR/infinite-instantiation.rs:21:9 | LL | function(counter - 1, t.to_option()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `function` defined here - --> $DIR/infinite-instantiation.rs:21:1 + --> $DIR/infinite-instantiation.rs:19:1 | LL | fn function(counter: usize, t: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/infinite-instantiation.long-type.txt' error: aborting due to 1 previous error diff --git a/tests/ui/infinite/infinite-struct.rs b/tests/ui/infinite/infinite-struct.rs index 62f9702b9f411..fd47a4ec9cc6a 100644 --- a/tests/ui/infinite/infinite-struct.rs +++ b/tests/ui/infinite/infinite-struct.rs @@ -15,3 +15,5 @@ struct Foo { //~ ERROR has infinite size struct Bar([T; 1]); fn main() {} + +//~? ERROR reached the recursion limit finding the struct tail for `Take` diff --git a/tests/ui/inline-const/collect-scopes-in-pat.rs b/tests/ui/inline-const/collect-scopes-in-pat.rs deleted file mode 100644 index 16baf920f584e..0000000000000 --- a/tests/ui/inline-const/collect-scopes-in-pat.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ compile-flags: -Zlint-mir -//@ check-pass - -#![feature(inline_const_pat)] - -fn main() { - match 1 { - const { - || match 0 { - x => 0, - }; - 0 - } => (), - _ => (), - } -} diff --git a/tests/ui/inline-const/const-block-pat-liveness.rs b/tests/ui/inline-const/const-block-pat-liveness.rs deleted file mode 100644 index 26393a4f65b84..0000000000000 --- a/tests/ui/inline-const/const-block-pat-liveness.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! This test used to ICE because const blocks didn't have a body -//! anymore, making a lot of logic very fragile around handling the -//! HIR of a const block. -//! https://github.com/rust-lang/rust/issues/125846 - -//@ check-pass - -#![feature(inline_const_pat)] - -fn main() { - match 0 { - const { - let a = 10_usize; - *&a - } - | _ => {} - } -} diff --git a/tests/ui/inline-const/const-expr-generic-err.stderr b/tests/ui/inline-const/const-expr-generic-err.stderr index e67c0b28e023f..a40e8bceb3605 100644 --- a/tests/ui/inline-const/const-expr-generic-err.stderr +++ b/tests/ui/inline-const/const-expr-generic-err.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `foo::::{constant#0}` failed | LL | const { assert!(std::mem::size_of::() == 0); } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation panicked: assertion failed: std::mem::size_of::() == 0 - | - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/const-expr-generic-err.rs:4:5 diff --git a/tests/ui/inline-const/const-match-pat-generic.rs b/tests/ui/inline-const/const-match-pat-generic.rs deleted file mode 100644 index 889c015e9acb3..0000000000000 --- a/tests/ui/inline-const/const-match-pat-generic.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![feature(inline_const_pat)] - -// rust-lang/rust#82518: ICE with inline-const in match referencing const-generic parameter - -fn foo() { - match 0 { - const { V } => {}, - //~^ ERROR constant pattern cannot depend on generic parameters - _ => {}, - } -} - -const fn f(x: usize) -> usize { - x + 1 -} - -fn bar() { - match 0 { - const { f(V) } => {}, - //~^ ERROR constant pattern cannot depend on generic parameters - _ => {}, - } -} - -fn main() { - foo::<1>(); - bar::<1>(); -} diff --git a/tests/ui/inline-const/const-match-pat-generic.stderr b/tests/ui/inline-const/const-match-pat-generic.stderr deleted file mode 100644 index 7d9e1d9e407e1..0000000000000 --- a/tests/ui/inline-const/const-match-pat-generic.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0158]: constant pattern cannot depend on generic parameters - --> $DIR/const-match-pat-generic.rs:7:9 - | -LL | const { V } => {}, - | ^^^^^^^^^^^ `const` depends on a generic parameter - -error[E0158]: constant pattern cannot depend on generic parameters - --> $DIR/const-match-pat-generic.rs:19:9 - | -LL | const { f(V) } => {}, - | ^^^^^^^^^^^^^^ `const` depends on a generic parameter - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0158`. diff --git a/tests/ui/inline-const/const-match-pat-inference.rs b/tests/ui/inline-const/const-match-pat-inference.rs deleted file mode 100644 index 3d3533839bc79..0000000000000 --- a/tests/ui/inline-const/const-match-pat-inference.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ check-pass - -#![feature(inline_const_pat)] - -fn main() { - match 1u64 { - 0 => (), - const { 0 + 1 } => (), - const { 2 - 1 } ..= const { u64::MAX } => (), - } -} diff --git a/tests/ui/inline-const/const-match-pat-lifetime-err.rs b/tests/ui/inline-const/const-match-pat-lifetime-err.rs deleted file mode 100644 index 7f450ebe6fccf..0000000000000 --- a/tests/ui/inline-const/const-match-pat-lifetime-err.rs +++ /dev/null @@ -1,47 +0,0 @@ -#![feature(inline_const_pat)] - -use std::marker::PhantomData; - -#[derive(PartialEq, Eq)] -pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>); - -#[derive(PartialEq, Eq)] -pub struct CovariantRef<'a, T: ?Sized>(&'a T); - -impl<'a, T: ?Sized> InvariantRef<'a, T> { - pub const fn new(r: &'a T) -> Self { - InvariantRef(r, PhantomData) - } -} - -impl<'a> InvariantRef<'a, ()> { - pub const NEW: Self = InvariantRef::new(&()); -} - -impl<'a> CovariantRef<'a, ()> { - pub const NEW: Self = CovariantRef(&()); -} - -fn match_invariant_ref<'a>() { - let y = (); - match InvariantRef::new(&y) { - //~^ ERROR `y` does not live long enough [E0597] - const { InvariantRef::<'a>::NEW } => (), - } -} - -fn match_covariant_ref<'a>() { - // Unclear if we should error here (should we be able to subtype the type of - // `y.0`), but using the associated const directly in the pattern also - // errors. - let y: (CovariantRef<'static, _>,) = (CovariantRef(&()),); - //~^ ERROR lifetime may not live long enough - match y.0 { - const { CovariantRef::<'a>::NEW } => (), - } -} - -fn main() { - match_invariant_ref(); - match_covariant_ref(); -} diff --git a/tests/ui/inline-const/const-match-pat-lifetime-err.stderr b/tests/ui/inline-const/const-match-pat-lifetime-err.stderr deleted file mode 100644 index 7eea1846057af..0000000000000 --- a/tests/ui/inline-const/const-match-pat-lifetime-err.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error[E0597]: `y` does not live long enough - --> $DIR/const-match-pat-lifetime-err.rs:27:29 - | -LL | fn match_invariant_ref<'a>() { - | -- lifetime `'a` defined here -LL | let y = (); - | - binding `y` declared here -LL | match InvariantRef::new(&y) { - | ^^ borrowed value does not live long enough -LL | -LL | const { InvariantRef::<'a>::NEW } => (), - | ----------------------- using this value as a constant requires that `y` is borrowed for `'a` -LL | } -LL | } - | - `y` dropped here while still borrowed - -error: lifetime may not live long enough - --> $DIR/const-match-pat-lifetime-err.rs:37:12 - | -LL | fn match_covariant_ref<'a>() { - | -- lifetime `'a` defined here -... -LL | let y: (CovariantRef<'static, _>,) = (CovariantRef(&()),); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/inline-const/const-match-pat-lifetime.rs b/tests/ui/inline-const/const-match-pat-lifetime.rs deleted file mode 100644 index 7f1011ea2400d..0000000000000 --- a/tests/ui/inline-const/const-match-pat-lifetime.rs +++ /dev/null @@ -1,34 +0,0 @@ -//@ run-pass - -#![feature(inline_const_pat)] - -use std::marker::PhantomData; - -// rust-lang/rust#78174: ICE: "cannot convert ReErased to a region vid" -fn issue_78174() { - match "foo" { - const { concat!("fo", "o") } => (), - _ => unreachable!(), - } -} - -#[derive(PartialEq, Eq)] -pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>); - -impl<'a, T: ?Sized> InvariantRef<'a, T> { - pub const fn new(r: &'a T) -> Self { - InvariantRef(r, PhantomData) - } -} - -fn match_invariant_ref<'a>() { - match const { InvariantRef::<'a, _>::new(&()) } { - const { InvariantRef::<'a, ()>::new(&()) } => { - } - } -} - -fn main() { - issue_78174(); - match_invariant_ref(); -} diff --git a/tests/ui/inline-const/const-match-pat-range.rs b/tests/ui/inline-const/const-match-pat-range.rs deleted file mode 100644 index 7202c0c04521e..0000000000000 --- a/tests/ui/inline-const/const-match-pat-range.rs +++ /dev/null @@ -1,38 +0,0 @@ -//@ build-pass - -#![feature(inline_const_pat)] - -fn main() { - const N: u32 = 10; - let x: u32 = 3; - - match x { - 1 ..= const { N + 1 } => {}, - _ => {}, - } - - match x { - const { N - 1 } ..= 10 => {}, - _ => {}, - } - - match x { - const { N - 1 } ..= const { N + 1 } => {}, - _ => {}, - } - - match x { - .. const { N + 1 } => {}, - _ => {}, - } - - match x { - const { N - 1 } .. => {}, - _ => {}, - } - - match x { - ..= const { N + 1 } => {}, - _ => {} - } -} diff --git a/tests/ui/inline-const/const-match-pat.rs b/tests/ui/inline-const/const-match-pat.rs deleted file mode 100644 index 1580ef258129e..0000000000000 --- a/tests/ui/inline-const/const-match-pat.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ run-pass - -#![feature(inline_const_pat)] -const MMIO_BIT1: u8 = 4; -const MMIO_BIT2: u8 = 5; - -fn main() { - let s = match read_mmio() { - 0 => "FOO", - const { 1 << MMIO_BIT1 } => "BAR", - const { 1 << MMIO_BIT2 } => "BAZ", - _ => unreachable!(), - }; - - assert_eq!("BAZ", s); -} - -fn read_mmio() -> i32 { - 1 << 5 -} diff --git a/tests/ui/inline-const/in-pat-recovery.rs b/tests/ui/inline-const/in-pat-recovery.rs new file mode 100644 index 0000000000000..e9e60116ff4c7 --- /dev/null +++ b/tests/ui/inline-const/in-pat-recovery.rs @@ -0,0 +1,11 @@ +// While `feature(inline_const_pat)` has been removed from the +// compiler, we should still make sure that the resulting error +// message is acceptable. +fn main() { + match 1 { + const { 1 + 7 } => {} + //~^ ERROR `inline_const_pat` has been removed + 2 => {} + _ => {} + } +} diff --git a/tests/ui/inline-const/in-pat-recovery.stderr b/tests/ui/inline-const/in-pat-recovery.stderr new file mode 100644 index 0000000000000..e1f2e681e77f4 --- /dev/null +++ b/tests/ui/inline-const/in-pat-recovery.stderr @@ -0,0 +1,10 @@ +error: `inline_const_pat` has been removed + --> $DIR/in-pat-recovery.rs:6:15 + | +LL | const { 1 + 7 } => {} + | ^^^^^^^^^ + | + = help: use a named `const`-item or an `if`-guard instead + +error: aborting due to 1 previous error + diff --git a/tests/ui/inline-const/pat-match-fndef.rs b/tests/ui/inline-const/pat-match-fndef.rs deleted file mode 100644 index 013a4a6756102..0000000000000 --- a/tests/ui/inline-const/pat-match-fndef.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(inline_const_pat)] - -fn uwu() {} - -fn main() { - let x = []; - match x[123] { - const { uwu } => {} - //~^ ERROR `fn() {uwu}` cannot be used in patterns - _ => {} - } -} diff --git a/tests/ui/inline-const/pat-match-fndef.stderr b/tests/ui/inline-const/pat-match-fndef.stderr deleted file mode 100644 index 220437a0491af..0000000000000 --- a/tests/ui/inline-const/pat-match-fndef.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fn item `fn() {uwu}` cannot be used in patterns - --> $DIR/pat-match-fndef.rs:8:9 - | -LL | const { uwu } => {} - | ^^^^^^^^^^^^^ fn item can't be used in patterns - -error: aborting due to 1 previous error - diff --git a/tests/ui/inline-const/pat-unsafe-err.rs b/tests/ui/inline-const/pat-unsafe-err.rs deleted file mode 100644 index b906def702950..0000000000000 --- a/tests/ui/inline-const/pat-unsafe-err.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![feature(inline_const_pat)] - -const unsafe fn require_unsafe() -> usize { - 1 -} - -fn main() { - match () { - const { - require_unsafe(); - //~^ ERROR [E0133] - } => (), - } - - match 1 { - const { - require_unsafe() - //~^ ERROR [E0133] - }..=4 => (), - _ => (), - } -} diff --git a/tests/ui/inline-const/pat-unsafe-err.stderr b/tests/ui/inline-const/pat-unsafe-err.stderr deleted file mode 100644 index 786c7f31ccce1..0000000000000 --- a/tests/ui/inline-const/pat-unsafe-err.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0133]: call to unsafe function `require_unsafe` is unsafe and requires unsafe function or block - --> $DIR/pat-unsafe-err.rs:10:13 - | -LL | require_unsafe(); - | ^^^^^^^^^^^^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error[E0133]: call to unsafe function `require_unsafe` is unsafe and requires unsafe function or block - --> $DIR/pat-unsafe-err.rs:17:13 - | -LL | require_unsafe() - | ^^^^^^^^^^^^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/inline-const/pat-unsafe.rs b/tests/ui/inline-const/pat-unsafe.rs deleted file mode 100644 index 4b05f3a1cddd8..0000000000000 --- a/tests/ui/inline-const/pat-unsafe.rs +++ /dev/null @@ -1,29 +0,0 @@ -//@ check-pass - -#![warn(unused_unsafe)] -#![feature(inline_const_pat)] - -const unsafe fn require_unsafe() -> usize { - 1 -} - -fn main() { - unsafe { - match () { - const { - require_unsafe(); - unsafe {} - //~^ WARNING unnecessary `unsafe` block - } => (), - } - - match 1 { - const { - unsafe {} - //~^ WARNING unnecessary `unsafe` block - require_unsafe() - }..=4 => (), - _ => (), - } - } -} diff --git a/tests/ui/inline-const/pat-unsafe.stderr b/tests/ui/inline-const/pat-unsafe.stderr deleted file mode 100644 index 59460271ac010..0000000000000 --- a/tests/ui/inline-const/pat-unsafe.stderr +++ /dev/null @@ -1,20 +0,0 @@ -warning: unnecessary `unsafe` block - --> $DIR/pat-unsafe.rs:15:17 - | -LL | unsafe {} - | ^^^^^^ unnecessary `unsafe` block - | -note: the lint level is defined here - --> $DIR/pat-unsafe.rs:3:9 - | -LL | #![warn(unused_unsafe)] - | ^^^^^^^^^^^^^ - -warning: unnecessary `unsafe` block - --> $DIR/pat-unsafe.rs:22:17 - | -LL | unsafe {} - | ^^^^^^ unnecessary `unsafe` block - -warning: 2 warnings emitted - diff --git a/tests/ui/inline-const/required-const.stderr b/tests/ui/inline-const/required-const.stderr index a6c630b5477a6..2f618e54ed3f4 100644 --- a/tests/ui/inline-const/required-const.stderr +++ b/tests/ui/inline-const/required-const.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of `foo::::{constant#0}` failed | LL | const { panic!() } | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/required-const.rs:6:9 diff --git a/tests/ui/inline-const/using-late-bound-from-closure.rs b/tests/ui/inline-const/using-late-bound-from-closure.rs index 2b12b2e26a226..cb3dfd9573974 100644 --- a/tests/ui/inline-const/using-late-bound-from-closure.rs +++ b/tests/ui/inline-const/using-late-bound-from-closure.rs @@ -7,7 +7,7 @@ fn foo() { const { let awd = (); let _: &'a () = &awd; - //~^ `awd` does not live long enough + //~^ ERROR `awd` does not live long enough }; b }; diff --git a/tests/ui/inner-attrs-on-impl.rs b/tests/ui/inner-attrs-on-impl.rs index 75f406232e200..1dce1cdd261af 100644 --- a/tests/ui/inner-attrs-on-impl.rs +++ b/tests/ui/inner-attrs-on-impl.rs @@ -3,7 +3,7 @@ struct Foo; impl Foo { - #![cfg(FALSE)] + #![cfg(false)] fn method(&self) -> bool { false } } @@ -12,7 +12,7 @@ impl Foo { #![cfg(not(FALSE))] // check that we don't eat attributes too eagerly. - #[cfg(FALSE)] + #[cfg(false)] fn method(&self) -> bool { false } fn method(&self) -> bool { true } diff --git a/tests/ui/inner-static.rs b/tests/ui/inner-static.rs deleted file mode 100644 index 9455ec5712fef..0000000000000 --- a/tests/ui/inner-static.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ run-pass -//@ aux-build:inner_static.rs - - -extern crate inner_static; - -pub fn main() { - let a = inner_static::A::<()> { v: () }; - let b = inner_static::B::<()> { v: () }; - let c = inner_static::test::A::<()> { v: () }; - assert_eq!(a.bar(), 2); - assert_eq!(b.bar(), 4); - assert_eq!(c.bar(), 6); -} diff --git a/tests/ui/instrument-coverage/bad-value.rs b/tests/ui/instrument-coverage/bad-value.rs index 3441738529162..d44f982ea46f1 100644 --- a/tests/ui/instrument-coverage/bad-value.rs +++ b/tests/ui/instrument-coverage/bad-value.rs @@ -3,3 +3,6 @@ //@ [bad] compile-flags: -Cinstrument-coverage=bad-value fn main() {} + +//[blank]~? ERROR incorrect value `` for codegen option `instrument-coverage` +//[bad]~? ERROR incorrect value `bad-value` for codegen option `instrument-coverage` diff --git a/tests/ui/instrument-coverage/coverage-options.rs b/tests/ui/instrument-coverage/coverage-options.rs index 7615a0fb2751c..c3eae9625da98 100644 --- a/tests/ui/instrument-coverage/coverage-options.rs +++ b/tests/ui/instrument-coverage/coverage-options.rs @@ -17,3 +17,5 @@ //@ [bad] compile-flags: -Zcoverage-options=bad fn main() {} + +//[bad]~? ERROR incorrect value `bad` for unstable option `coverage-options` diff --git a/tests/ui/instrument-coverage/mcdc-condition-limit.rs b/tests/ui/instrument-coverage/mcdc-condition-limit.rs index 118ae482fc6ce..74707ba2e670d 100644 --- a/tests/ui/instrument-coverage/mcdc-condition-limit.rs +++ b/tests/ui/instrument-coverage/mcdc-condition-limit.rs @@ -1,5 +1,4 @@ //@ edition: 2021 -//@ min-llvm-version: 19 //@ revisions: good //@ check-pass //@ compile-flags: -Cinstrument-coverage -Zcoverage-options=mcdc -Zno-profiler-runtime diff --git a/tests/ui/instrument-xray/flags-always-never-1.rs b/tests/ui/instrument-xray/flags-always-never-1.rs index 91032662e6b22..97f03fca10278 100644 --- a/tests/ui/instrument-xray/flags-always-never-1.rs +++ b/tests/ui/instrument-xray/flags-always-never-1.rs @@ -2,6 +2,7 @@ // //@ needs-xray //@ compile-flags: -Z instrument-xray=always,never -//@ error-pattern: incorrect value `always,never` for unstable option `instrument-xray` fn main() {} + +//~? ERROR incorrect value `always,never` for unstable option `instrument-xray` diff --git a/tests/ui/instrument-xray/flags-dupe-always.rs b/tests/ui/instrument-xray/flags-dupe-always.rs index 41e4f267b471f..e3a5b3b1c3e25 100644 --- a/tests/ui/instrument-xray/flags-dupe-always.rs +++ b/tests/ui/instrument-xray/flags-dupe-always.rs @@ -2,6 +2,7 @@ // //@ needs-xray //@ compile-flags: -Z instrument-xray=always,always -//@ error-pattern: incorrect value `always,always` for unstable option `instrument-xray` fn main() {} + +//~? ERROR incorrect value `always,always` for unstable option `instrument-xray` diff --git a/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs b/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs index ba5ea28d40b7e..648bd14463263 100644 --- a/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs +++ b/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs @@ -2,6 +2,7 @@ // //@ needs-xray //@ compile-flags: -Z instrument-xray=ignore-loops,ignore-loops -//@ error-pattern: incorrect value `ignore-loops,ignore-loops` for unstable option `instrument-xray` fn main() {} + +//~? ERROR incorrect value `ignore-loops,ignore-loops` for unstable option `instrument-xray` diff --git a/tests/ui/instrument-xray/target-not-supported.rs b/tests/ui/instrument-xray/target-not-supported.rs index cdae26f993d3b..2045913b186cd 100644 --- a/tests/ui/instrument-xray/target-not-supported.rs +++ b/tests/ui/instrument-xray/target-not-supported.rs @@ -2,8 +2,9 @@ // //@ needs-llvm-components: x86 //@ compile-flags: -Z instrument-xray --target x86_64-apple-darwin -//@ error-pattern: error: XRay instrumentation is not supported for this target #![feature(no_core)] #![no_core] #![no_main] + +//~? ERROR XRay instrumentation is not supported for this target diff --git a/tests/ui/integral-indexing.stderr b/tests/ui/integral-indexing.stderr index e7a45c2c88dd9..26253e078cb40 100644 --- a/tests/ui/integral-indexing.stderr +++ b/tests/ui/integral-indexing.stderr @@ -5,8 +5,9 @@ LL | v[3u8]; | ^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[isize]>` is not implemented for `u8` - but it is implemented for `usize` - = help: for that trait implementation, expected `usize`, found `u8` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `Vec` to implement `Index` error[E0277]: the type `[isize]` cannot be indexed by `i8` @@ -16,8 +17,9 @@ LL | v[3i8]; | ^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[isize]>` is not implemented for `i8` - but it is implemented for `usize` - = help: for that trait implementation, expected `usize`, found `i8` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `Vec` to implement `Index` error[E0277]: the type `[isize]` cannot be indexed by `u32` @@ -27,8 +29,9 @@ LL | v[3u32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[isize]>` is not implemented for `u32` - but it is implemented for `usize` - = help: for that trait implementation, expected `usize`, found `u32` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `Vec` to implement `Index` error[E0277]: the type `[isize]` cannot be indexed by `i32` @@ -38,8 +41,9 @@ LL | v[3i32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[isize]>` is not implemented for `i32` - but it is implemented for `usize` - = help: for that trait implementation, expected `usize`, found `i32` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `Vec` to implement `Index` error[E0277]: the type `[u8]` cannot be indexed by `u8` @@ -49,8 +53,9 @@ LL | s.as_bytes()[3u8]; | ^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[u8]>` is not implemented for `u8` - but it is implemented for `usize` - = help: for that trait implementation, expected `usize`, found `u8` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `[u8]` to implement `Index` error[E0277]: the type `[u8]` cannot be indexed by `i8` @@ -60,8 +65,9 @@ LL | s.as_bytes()[3i8]; | ^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[u8]>` is not implemented for `i8` - but it is implemented for `usize` - = help: for that trait implementation, expected `usize`, found `i8` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `[u8]` to implement `Index` error[E0277]: the type `[u8]` cannot be indexed by `u32` @@ -71,8 +77,9 @@ LL | s.as_bytes()[3u32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[u8]>` is not implemented for `u32` - but it is implemented for `usize` - = help: for that trait implementation, expected `usize`, found `u32` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `[u8]` to implement `Index` error[E0277]: the type `[u8]` cannot be indexed by `i32` @@ -82,8 +89,9 @@ LL | s.as_bytes()[3i32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[u8]>` is not implemented for `i32` - but it is implemented for `usize` - = help: for that trait implementation, expected `usize`, found `i32` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `[u8]` to implement `Index` error: aborting due to 8 previous errors diff --git a/tests/ui/internal/internal-unstable-noallow.rs b/tests/ui/internal/internal-unstable-noallow.rs index 57ddb93d8801c..507eeebddc0d9 100644 --- a/tests/ui/internal/internal-unstable-noallow.rs +++ b/tests/ui/internal/internal-unstable-noallow.rs @@ -4,20 +4,18 @@ // the // ~ form. //@ aux-build:internal_unstable.rs -//@ error-pattern:use of unstable library feature `function` -//@ error-pattern:use of unstable library feature `struct_field` -//@ error-pattern:use of unstable library feature `method` -//@ error-pattern:use of unstable library feature `struct2_field` #[macro_use] extern crate internal_unstable; fn main() { - call_unstable_noallow!(); + call_unstable_noallow!(); //~ ERROR use of unstable library feature `function` - construct_unstable_noallow!(0); + construct_unstable_noallow!(0); //~ ERROR use of unstable library feature `struct_field` |x: internal_unstable::Foo| { call_method_noallow!(x) }; + //~^ ERROR use of unstable library feature `method` |x: internal_unstable::Bar| { access_field_noallow!(x) }; + //~^ ERROR use of unstable library feature `struct2_field` } diff --git a/tests/ui/internal/internal-unstable-noallow.stderr b/tests/ui/internal/internal-unstable-noallow.stderr index 22f42abbd1146..2cc55022ef3d8 100644 --- a/tests/ui/internal/internal-unstable-noallow.stderr +++ b/tests/ui/internal/internal-unstable-noallow.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `function` - --> $DIR/internal-unstable-noallow.rs:16:5 + --> $DIR/internal-unstable-noallow.rs:12:5 | LL | call_unstable_noallow!(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | call_unstable_noallow!(); = note: this error originates in the macro `call_unstable_noallow` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: use of unstable library feature `struct_field` - --> $DIR/internal-unstable-noallow.rs:18:5 + --> $DIR/internal-unstable-noallow.rs:14:5 | LL | construct_unstable_noallow!(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | construct_unstable_noallow!(0); = note: this error originates in the macro `construct_unstable_noallow` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: use of unstable library feature `method` - --> $DIR/internal-unstable-noallow.rs:20:35 + --> $DIR/internal-unstable-noallow.rs:16:35 | LL | |x: internal_unstable::Foo| { call_method_noallow!(x) }; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | |x: internal_unstable::Foo| { call_method_noallow!(x) }; = note: this error originates in the macro `call_method_noallow` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: use of unstable library feature `struct2_field` - --> $DIR/internal-unstable-noallow.rs:22:35 + --> $DIR/internal-unstable-noallow.rs:19:35 | LL | |x: internal_unstable::Bar| { access_field_noallow!(x) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/intrinsics/always-extern.rs b/tests/ui/intrinsics/always-extern.rs deleted file mode 100644 index 0afd8353455f2..0000000000000 --- a/tests/ui/intrinsics/always-extern.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![feature(intrinsics)] - -trait Foo { - extern "rust-intrinsic" fn foo(&self); //~ ERROR intrinsic must -} - -impl Foo for () { - extern "rust-intrinsic" fn foo(&self) { //~ ERROR intrinsic must - } -} - -extern "rust-intrinsic" fn hello() {//~ ERROR intrinsic must - //~^ ERROR unrecognized intrinsic function: `hello` -} - -fn main() { -} diff --git a/tests/ui/intrinsics/always-extern.stderr b/tests/ui/intrinsics/always-extern.stderr deleted file mode 100644 index 44b889c6faac6..0000000000000 --- a/tests/ui/intrinsics/always-extern.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/always-extern.rs:4:32 - | -LL | extern "rust-intrinsic" fn foo(&self); - | ^^^ - -error[E0093]: unrecognized intrinsic function: `hello` - --> $DIR/always-extern.rs:12:28 - | -LL | extern "rust-intrinsic" fn hello() { - | ^^^^^ unrecognized intrinsic - | - = help: if you're adding an intrinsic, be sure to update `check_intrinsic_type` - -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/always-extern.rs:8:43 - | -LL | extern "rust-intrinsic" fn foo(&self) { - | ___________________________________________^ -LL | | } - | |_____^ - -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/always-extern.rs:12:36 - | -LL | extern "rust-intrinsic" fn hello() { - | ____________________________________^ -LL | | -LL | | } - | |_^ - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0093`. diff --git a/tests/ui/intrinsics/auxiliary/cci_intrinsic.rs b/tests/ui/intrinsics/auxiliary/cci_intrinsic.rs index f3b9d569ce3b1..1014ac6f5609e 100644 --- a/tests/ui/intrinsics/auxiliary/cci_intrinsic.rs +++ b/tests/ui/intrinsics/auxiliary/cci_intrinsic.rs @@ -1,14 +1,11 @@ #![feature(intrinsics)] pub mod rusti { - extern "rust-intrinsic" { - pub fn atomic_xchg_seqcst(dst: *mut T, src: T) -> T; - } + #[rustc_intrinsic] + pub unsafe fn atomic_xchg_seqcst(dst: *mut T, src: T) -> T; } #[inline(always)] pub fn atomic_xchg_seqcst(dst: *mut isize, src: isize) -> isize { - unsafe { - rusti::atomic_xchg_seqcst(dst, src) - } + unsafe { rusti::atomic_xchg_seqcst(dst, src) } } diff --git a/tests/ui/intrinsics/incorrect-read_via_copy-defn.rs b/tests/ui/intrinsics/incorrect-read_via_copy-defn.rs index 5520430e140b3..e9f9270de87f3 100644 --- a/tests/ui/intrinsics/incorrect-read_via_copy-defn.rs +++ b/tests/ui/intrinsics/incorrect-read_via_copy-defn.rs @@ -1,7 +1,8 @@ fn main() { read_via_copy(); + //~^ ERROR call to unsafe function `read_via_copy` is unsafe and requires unsafe function or block } -extern "rust-intrinsic" fn read_via_copy() {} -//~^ ERROR "rust-intrinsic" ABI is an implementation detail -//~| ERROR intrinsic must be in `extern "rust-intrinsic" { ... }` block +#[rustc_intrinsic] +//~^ ERROR the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items +unsafe fn read_via_copy() {} diff --git a/tests/ui/intrinsics/incorrect-read_via_copy-defn.stderr b/tests/ui/intrinsics/incorrect-read_via_copy-defn.stderr index c6682693f7409..6711c77a11eb3 100644 --- a/tests/ui/intrinsics/incorrect-read_via_copy-defn.stderr +++ b/tests/ui/intrinsics/incorrect-read_via_copy-defn.stderr @@ -1,18 +1,21 @@ -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/incorrect-read_via_copy-defn.rs:5:8 +error[E0658]: the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items + --> $DIR/incorrect-read_via_copy-defn.rs:6:1 | -LL | extern "rust-intrinsic" fn read_via_copy() {} - | ^^^^^^^^^^^^^^^^ +LL | #[rustc_intrinsic] + | ^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/incorrect-read_via_copy-defn.rs:5:44 +error[E0133]: call to unsafe function `read_via_copy` is unsafe and requires unsafe function or block + --> $DIR/incorrect-read_via_copy-defn.rs:2:5 | -LL | extern "rust-intrinsic" fn read_via_copy() {} - | ^^ +LL | read_via_copy(); + | ^^^^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0133, E0658. +For more information about an error, try `rustc --explain E0133`. diff --git a/tests/ui/intrinsics/incorrect-transmute.rs b/tests/ui/intrinsics/incorrect-transmute.rs index 15d1ab939ed0b..25fbc7a92ee7c 100644 --- a/tests/ui/intrinsics/incorrect-transmute.rs +++ b/tests/ui/intrinsics/incorrect-transmute.rs @@ -1,7 +1,8 @@ fn main() { transmute(); // does not ICE + //~^ ERROR call to unsafe function `transmute` is unsafe and requires unsafe function or block } -extern "rust-intrinsic" fn transmute() {} -//~^ ERROR "rust-intrinsic" ABI is an implementation detail -//~| ERROR intrinsic must be in `extern "rust-intrinsic" { ... }` block +#[rustc_intrinsic] +//~^ ERROR the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items +unsafe fn transmute() {} diff --git a/tests/ui/intrinsics/incorrect-transmute.stderr b/tests/ui/intrinsics/incorrect-transmute.stderr index 99dfb9847ff2e..6145a11c4ae0f 100644 --- a/tests/ui/intrinsics/incorrect-transmute.stderr +++ b/tests/ui/intrinsics/incorrect-transmute.stderr @@ -1,18 +1,21 @@ -error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable - --> $DIR/incorrect-transmute.rs:5:8 +error[E0658]: the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items + --> $DIR/incorrect-transmute.rs:6:1 | -LL | extern "rust-intrinsic" fn transmute() {} - | ^^^^^^^^^^^^^^^^ +LL | #[rustc_intrinsic] + | ^^^^^^^^^^^^^^^^^^ | = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/incorrect-transmute.rs:5:40 +error[E0133]: call to unsafe function `transmute` is unsafe and requires unsafe function or block + --> $DIR/incorrect-transmute.rs:2:5 | -LL | extern "rust-intrinsic" fn transmute() {} - | ^^ +LL | transmute(); // does not ICE + | ^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0133, E0658. +For more information about an error, try `rustc --explain E0133`. diff --git a/tests/ui/intrinsics/intrinsic-atomics.rs b/tests/ui/intrinsics/intrinsic-atomics.rs index 4ad267e3ddb3f..9127cc649e66d 100644 --- a/tests/ui/intrinsics/intrinsic-atomics.rs +++ b/tests/ui/intrinsics/intrinsic-atomics.rs @@ -1,35 +1,6 @@ //@ run-pass -#![feature(intrinsics)] - -mod rusti { - extern "rust-intrinsic" { - pub fn atomic_cxchg_seqcst_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); - pub fn atomic_cxchg_acquire_acquire(dst: *mut T, old: T, src: T) -> (T, bool); - pub fn atomic_cxchg_release_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); - - pub fn atomic_cxchgweak_seqcst_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); - pub fn atomic_cxchgweak_acquire_acquire(dst: *mut T, old: T, src: T) -> (T, bool); - pub fn atomic_cxchgweak_release_relaxed(dst: *mut T, old: T, src: T) -> (T, bool); - - pub fn atomic_load_seqcst(src: *const T) -> T; - pub fn atomic_load_acquire(src: *const T) -> T; - - pub fn atomic_store_seqcst(dst: *mut T, val: T); - pub fn atomic_store_release(dst: *mut T, val: T); - - pub fn atomic_xchg_seqcst(dst: *mut T, src: T) -> T; - pub fn atomic_xchg_acquire(dst: *mut T, src: T) -> T; - pub fn atomic_xchg_release(dst: *mut T, src: T) -> T; - - pub fn atomic_xadd_seqcst(dst: *mut T, src: T) -> T; - pub fn atomic_xadd_acquire(dst: *mut T, src: T) -> T; - pub fn atomic_xadd_release(dst: *mut T, src: T) -> T; - - pub fn atomic_xsub_seqcst(dst: *mut T, src: T) -> T; - pub fn atomic_xsub_acquire(dst: *mut T, src: T) -> T; - pub fn atomic_xsub_release(dst: *mut T, src: T) -> T; - } -} +#![feature(core_intrinsics)] +use std::intrinsics as rusti; pub fn main() { unsafe { @@ -39,9 +10,9 @@ pub fn main() { *x = 5; assert_eq!(rusti::atomic_load_acquire(&*x), 5); - rusti::atomic_store_seqcst(&mut *x,3); + rusti::atomic_store_seqcst(&mut *x, 3); assert_eq!(*x, 3); - rusti::atomic_store_release(&mut *x,1); + rusti::atomic_store_release(&mut *x, 1); assert_eq!(*x, 1); assert_eq!(rusti::atomic_cxchg_seqcst_seqcst(&mut *x, 1, 2), (1, true)); diff --git a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs index 76b6a8c8395cc..fdb25a3ae3f5e 100644 --- a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs +++ b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs @@ -3,13 +3,13 @@ const RAW_EQ_PADDING: bool = unsafe { std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16)) //~^ ERROR evaluation of constant value failed -//~| requires initialized memory +//~| NOTE requires initialized memory }; const RAW_EQ_PTR: bool = unsafe { std::intrinsics::raw_eq(&(&0), &(&1)) //~^ ERROR evaluation of constant value failed -//~| unable to turn pointer into integer +//~| NOTE unable to turn pointer into integer }; const RAW_EQ_NOT_ALIGNED: bool = unsafe { @@ -17,7 +17,7 @@ const RAW_EQ_NOT_ALIGNED: bool = unsafe { let aref = &*arr.as_ptr().cast::(); std::intrinsics::raw_eq(aref, aref) //~^ ERROR evaluation of constant value failed -//~| alignment +//~| NOTE alignment }; pub fn main() { diff --git a/tests/ui/intrinsics/invalid-ABI-rust-intrinsic.rs b/tests/ui/intrinsics/invalid-ABI-rust-intrinsic.rs new file mode 100644 index 0000000000000..4b777deb8b502 --- /dev/null +++ b/tests/ui/intrinsics/invalid-ABI-rust-intrinsic.rs @@ -0,0 +1,19 @@ +#![feature(intrinsics)] + +trait Foo { + extern "rust-intrinsic" fn foo(&self); //~ ERROR invalid ABI +} + +impl Foo for () { + extern "rust-intrinsic" fn foo(&self) { //~ ERROR invalid ABI + } +} + +extern "rust-intrinsic" fn hello() { //~ ERROR invalid ABI +} + +extern "rust-intrinsic" { + //~^ ERROR invalid ABI +} + +fn main() {} diff --git a/tests/ui/intrinsics/invalid-ABI-rust-intrinsic.stderr b/tests/ui/intrinsics/invalid-ABI-rust-intrinsic.stderr new file mode 100644 index 0000000000000..fc8bf62915b1b --- /dev/null +++ b/tests/ui/intrinsics/invalid-ABI-rust-intrinsic.stderr @@ -0,0 +1,35 @@ +error[E0703]: invalid ABI: found `rust-intrinsic` + --> $DIR/invalid-ABI-rust-intrinsic.rs:4:12 + | +LL | extern "rust-intrinsic" fn foo(&self); + | ^^^^^^^^^^^^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error[E0703]: invalid ABI: found `rust-intrinsic` + --> $DIR/invalid-ABI-rust-intrinsic.rs:8:12 + | +LL | extern "rust-intrinsic" fn foo(&self) { + | ^^^^^^^^^^^^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error[E0703]: invalid ABI: found `rust-intrinsic` + --> $DIR/invalid-ABI-rust-intrinsic.rs:12:8 + | +LL | extern "rust-intrinsic" fn hello() { + | ^^^^^^^^^^^^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error[E0703]: invalid ABI: found `rust-intrinsic` + --> $DIR/invalid-ABI-rust-intrinsic.rs:15:8 + | +LL | extern "rust-intrinsic" { + | ^^^^^^^^^^^^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0703`. diff --git a/tests/ui/intrinsics/issue-28575.rs b/tests/ui/intrinsics/issue-28575.rs index 141136d25b215..841bc45138a14 100644 --- a/tests/ui/intrinsics/issue-28575.rs +++ b/tests/ui/intrinsics/issue-28575.rs @@ -2,6 +2,7 @@ extern "C" { pub static FOO: extern "rust-intrinsic" fn(); + //~^ ERROR invalid ABI } fn main() { diff --git a/tests/ui/intrinsics/issue-28575.stderr b/tests/ui/intrinsics/issue-28575.stderr index 8a7816f231f70..09c52aa4c9982 100644 --- a/tests/ui/intrinsics/issue-28575.stderr +++ b/tests/ui/intrinsics/issue-28575.stderr @@ -1,11 +1,20 @@ +error[E0703]: invalid ABI: found `rust-intrinsic` + --> $DIR/issue-28575.rs:4:28 + | +LL | pub static FOO: extern "rust-intrinsic" fn(); + | ^^^^^^^^^^^^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + error[E0133]: use of extern static is unsafe and requires unsafe function or block - --> $DIR/issue-28575.rs:8:5 + --> $DIR/issue-28575.rs:9:5 | LL | FOO() | ^^^ use of extern static | = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0133`. +Some errors have detailed explanations: E0133, E0703. +For more information about an error, try `rustc --explain E0133`. diff --git a/tests/ui/intrinsics/safe-intrinsic-mismatch.rs b/tests/ui/intrinsics/safe-intrinsic-mismatch.rs index 915a23b59053d..4c301f9dbb09e 100644 --- a/tests/ui/intrinsics/safe-intrinsic-mismatch.rs +++ b/tests/ui/intrinsics/safe-intrinsic-mismatch.rs @@ -1,13 +1,14 @@ #![feature(intrinsics)] #![feature(rustc_attrs)] -extern "rust-intrinsic" { - fn size_of() -> usize; //~ ERROR intrinsic safety mismatch - //~^ ERROR intrinsic safety mismatch -} +#[rustc_intrinsic] +unsafe fn size_of() -> usize; +//~^ ERROR intrinsic safety mismatch +//~| ERROR intrinsic has wrong type #[rustc_intrinsic] -const fn assume(_b: bool) {} //~ ERROR intrinsic safety mismatch +const fn assume(_b: bool) {} +//~^ ERROR intrinsic safety mismatch //~| ERROR intrinsic has wrong type #[rustc_intrinsic] diff --git a/tests/ui/intrinsics/safe-intrinsic-mismatch.stderr b/tests/ui/intrinsics/safe-intrinsic-mismatch.stderr index aa4f294232d2d..04f6daeced2a9 100644 --- a/tests/ui/intrinsics/safe-intrinsic-mismatch.stderr +++ b/tests/ui/intrinsics/safe-intrinsic-mismatch.stderr @@ -1,16 +1,17 @@ error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `size_of` - --> $DIR/safe-intrinsic-mismatch.rs:5:5 + --> $DIR/safe-intrinsic-mismatch.rs:5:1 | -LL | fn size_of() -> usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe fn size_of() -> usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `size_of` - --> $DIR/safe-intrinsic-mismatch.rs:5:5 +error[E0308]: intrinsic has wrong type + --> $DIR/safe-intrinsic-mismatch.rs:5:18 | -LL | fn size_of() -> usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe fn size_of() -> usize; + | ^^^ expected safe fn, found unsafe fn | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: expected signature `fn() -> _` + found signature `unsafe fn() -> _` error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `assume` --> $DIR/safe-intrinsic-mismatch.rs:10:1 @@ -28,13 +29,13 @@ LL | const fn assume(_b: bool) {} found signature `fn(_)` error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `const_deallocate` - --> $DIR/safe-intrinsic-mismatch.rs:14:1 + --> $DIR/safe-intrinsic-mismatch.rs:15:1 | LL | const fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: intrinsic has wrong type - --> $DIR/safe-intrinsic-mismatch.rs:14:26 + --> $DIR/safe-intrinsic-mismatch.rs:15:26 | LL | const fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} | ^ expected unsafe fn, found safe fn diff --git a/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.rs b/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.rs index b4025080034ab..2a39d579c51fa 100644 --- a/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.rs +++ b/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.rs @@ -15,3 +15,7 @@ #[lang = "sized"] trait Sized {} + +//[BADFLAGS]~? ERROR incorrect value `leaf` for unstable option `branch-protection` +//[BADFLAGSPC]~? ERROR incorrect value `pc` for unstable option `branch-protection` +//[BADTARGET]~? ERROR `-Zbranch-protection` is only supported on aarch64 diff --git a/tests/ui/invalid-compile-flags/crate-type-flag.empty_crate_type.stderr b/tests/ui/invalid-compile-flags/crate-type-flag.empty_crate_type.stderr index 0f8772024dfab..4ab816d30f6c9 100644 --- a/tests/ui/invalid-compile-flags/crate-type-flag.empty_crate_type.stderr +++ b/tests/ui/invalid-compile-flags/crate-type-flag.empty_crate_type.stderr @@ -1,2 +1,2 @@ -error: unknown crate type: `` +error: unknown crate type: ``, expected one of: `lib`, `rlib`, `staticlib`, `dylib`, `cdylib`, `bin`, `proc-macro` diff --git a/tests/ui/invalid-compile-flags/crate-type-flag.proc_underscore_macro.stderr b/tests/ui/invalid-compile-flags/crate-type-flag.proc_underscore_macro.stderr index a4a9741699643..91cc66801f102 100644 --- a/tests/ui/invalid-compile-flags/crate-type-flag.proc_underscore_macro.stderr +++ b/tests/ui/invalid-compile-flags/crate-type-flag.proc_underscore_macro.stderr @@ -1,2 +1,2 @@ -error: unknown crate type: `proc_macro` +error: unknown crate type: `proc_macro`, expected one of: `lib`, `rlib`, `staticlib`, `dylib`, `cdylib`, `bin`, `proc-macro` diff --git a/tests/ui/invalid-compile-flags/crate-type-flag.rs b/tests/ui/invalid-compile-flags/crate-type-flag.rs index 07d853b330716..61b35cf8c64fb 100644 --- a/tests/ui/invalid-compile-flags/crate-type-flag.rs +++ b/tests/ui/invalid-compile-flags/crate-type-flag.rs @@ -3,8 +3,6 @@ //! //! This test does not try to check if the output artifacts are valid. -// FIXME(#132309): add a proper `supports-crate-type` directive. - // Single valid crate types should pass //@ revisions: lib rlib staticlib dylib cdylib bin proc_dash_macro @@ -17,19 +15,18 @@ //@[staticlib] compile-flags: --crate-type=staticlib //@[staticlib] check-pass -//@[dylib] ignore-musl (dylib is supported, but musl libc is statically linked by default) -//@[dylib] ignore-wasm (dylib is not supported) +//@[dylib] needs-crate-type: dylib //@[dylib] compile-flags: --crate-type=dylib //@[dylib] check-pass -//@[cdylib] ignore-musl (cdylib is supported, but musl libc is statically linked by default) +//@[cdylib] needs-crate-type: cdylib //@[cdylib] compile-flags: --crate-type=cdylib //@[cdylib] check-pass //@[bin] compile-flags: --crate-type=bin //@[bin] check-pass -//@[proc_dash_macro] ignore-wasm (proc-macro is not supported) +//@[proc_dash_macro] needs-crate-type: proc-macro //@[proc_dash_macro] needs-unwind (panic=abort causes warning to be emitted) //@[proc_dash_macro] compile-flags: --crate-type=proc-macro //@[proc_dash_macro] check-pass @@ -45,16 +42,17 @@ // `proc-macro` is accepted, but `proc_macro` is not. //@ revisions: proc_underscore_macro //@[proc_underscore_macro] compile-flags: --crate-type=proc_macro -//@[proc_underscore_macro] error-pattern: "unknown crate type: `proc_macro`" // Empty `--crate-type` not accepted. //@ revisions: empty_crate_type //@[empty_crate_type] compile-flags: --crate-type= -//@[empty_crate_type] error-pattern: "unknown crate type: ``" // Random unknown crate type. Also check that we can handle non-ASCII. //@ revisions: unknown //@[unknown] compile-flags: --crate-type=🤡 -//@[unknown] error-pattern: "unknown crate type: `🤡`" fn main() {} + +//[proc_underscore_macro]~? ERROR unknown crate type: `proc_macro` +//[empty_crate_type]~? ERROR unknown crate type: `` +//[unknown]~? ERROR unknown crate type: `🤡` diff --git a/tests/ui/invalid-compile-flags/crate-type-flag.unknown.stderr b/tests/ui/invalid-compile-flags/crate-type-flag.unknown.stderr index 7fb0f09a1afbd..ec202e171f058 100644 --- a/tests/ui/invalid-compile-flags/crate-type-flag.unknown.stderr +++ b/tests/ui/invalid-compile-flags/crate-type-flag.unknown.stderr @@ -1,2 +1,2 @@ -error: unknown crate type: `🤡` +error: unknown crate type: `🤡`, expected one of: `lib`, `rlib`, `staticlib`, `dylib`, `cdylib`, `bin`, `proc-macro` diff --git a/tests/ui/invalid-compile-flags/emit-output-types-without-args.rs b/tests/ui/invalid-compile-flags/emit-output-types-without-args.rs new file mode 100644 index 0000000000000..98d1fefbfb0bc --- /dev/null +++ b/tests/ui/invalid-compile-flags/emit-output-types-without-args.rs @@ -0,0 +1,3 @@ +//@ compile-flags: --emit + +//~? RAW Argument to option 'emit' missing diff --git a/tests/ui/invalid-compile-flags/emit-output-types-without-args.stderr b/tests/ui/invalid-compile-flags/emit-output-types-without-args.stderr new file mode 100644 index 0000000000000..9e4486a272f9d --- /dev/null +++ b/tests/ui/invalid-compile-flags/emit-output-types-without-args.stderr @@ -0,0 +1,16 @@ +error: Argument to option 'emit' missing + Usage: + --emit [=] + Comma separated list of types of output for the + compiler to emit. + Each TYPE has the default FILE name: + * asm - CRATE_NAME.s + * llvm-bc - CRATE_NAME.bc + * dep-info - CRATE_NAME.d + * link - (platform and crate-type dependent) + * llvm-ir - CRATE_NAME.ll + * metadata - libCRATE_NAME.rmeta + * mir - CRATE_NAME.mir + * obj - CRATE_NAME.o + * thin-link-bitcode - CRATE_NAME.indexing.o + diff --git a/tests/ui/invalid-compile-flags/function-return/requires-x86-or-x86_64.rs b/tests/ui/invalid-compile-flags/function-return/requires-x86-or-x86_64.rs index 6a4ecb9d83978..c7973c686da00 100644 --- a/tests/ui/invalid-compile-flags/function-return/requires-x86-or-x86_64.rs +++ b/tests/ui/invalid-compile-flags/function-return/requires-x86-or-x86_64.rs @@ -13,8 +13,9 @@ //@[aarch64] check-fail //@[aarch64] needs-llvm-components: aarch64 //@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu -//@[aarch64] error-pattern: `-Zfunction-return` (except `keep`) is only supported on x86 and x86_64 #![feature(no_core)] #![no_core] #![no_main] + +//[aarch64]~? ERROR `-Zfunction-return` (except `keep`) is only supported on x86 and x86_64 diff --git a/tests/ui/invalid-compile-flags/function-return/thunk-extern-requires-non-large-code-model.rs b/tests/ui/invalid-compile-flags/function-return/thunk-extern-requires-non-large-code-model.rs index f4be36e08f09c..fa3958c1f9875 100644 --- a/tests/ui/invalid-compile-flags/function-return/thunk-extern-requires-non-large-code-model.rs +++ b/tests/ui/invalid-compile-flags/function-return/thunk-extern-requires-non-large-code-model.rs @@ -14,8 +14,9 @@ //@[large] check-fail //@[large] compile-flags: -Ccode-model=large -//@[large] error-pattern: `-Zfunction-return=thunk-extern` is only supported on non-large code models #![feature(no_core)] #![no_core] #![no_main] + +//[large]~? ERROR `-Zfunction-return=thunk-extern` is only supported on non-large code models diff --git a/tests/ui/invalid-compile-flags/invalid-llvm-passes.rs b/tests/ui/invalid-compile-flags/invalid-llvm-passes.rs index ddb46e5971115..832821c9c883f 100644 --- a/tests/ui/invalid-compile-flags/invalid-llvm-passes.rs +++ b/tests/ui/invalid-compile-flags/invalid-llvm-passes.rs @@ -2,3 +2,5 @@ //@ compile-flags: -Cpasses=unknown-pass fn main() {} + +//~? ERROR failed to run LLVM passes: unknown pass name 'unknown-pass' diff --git a/tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.rs b/tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.rs index b1ac4a4ae2167..0e80b1dd178b7 100644 --- a/tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.rs +++ b/tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.rs @@ -1,2 +1,5 @@ // issue: 113981 + pub fn main() {} + +//~? ERROR invalid character '$' in crate name: `need_crate_arg_ignore_tidy$x` diff --git a/tests/ui/invalid-compile-flags/print-without-arg.stderr b/tests/ui/invalid-compile-flags/print-without-arg.stderr index a18d2779cade9..3048a59d0d00c 100644 --- a/tests/ui/invalid-compile-flags/print-without-arg.stderr +++ b/tests/ui/invalid-compile-flags/print-without-arg.stderr @@ -1,5 +1,7 @@ error: Argument to option 'print' missing Usage: - --print [crate-name|file-names|sysroot|target-libdir|cfg|check-cfg|calling-conventions|target-list|target-cpus|target-features|relocation-models|code-models|tls-models|target-spec-json|all-target-specs-json|native-static-libs|stack-protector-strategies|link-args|deployment-target] - Compiler information to print on stdout + --print [=] + Compiler information to print on stdout (or to a file) + INFO may be one of + . diff --git a/tests/ui/invalid-compile-flags/print.rs b/tests/ui/invalid-compile-flags/print.rs index 0d0a9d22750dd..4665bb2c53687 100644 --- a/tests/ui/invalid-compile-flags/print.rs +++ b/tests/ui/invalid-compile-flags/print.rs @@ -1 +1,3 @@ //@ compile-flags: --print yyyy + +//~? ERROR unknown print request: `yyyy` diff --git a/tests/ui/invalid-compile-flags/print.stderr b/tests/ui/invalid-compile-flags/print.stderr index df0c3977dc8f6..e3374eb1e6e7e 100644 --- a/tests/ui/invalid-compile-flags/print.stderr +++ b/tests/ui/invalid-compile-flags/print.stderr @@ -1,4 +1,5 @@ error: unknown print request: `yyyy` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` + = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/ui/invalid-compile-flags/reg-struct-return/requires-x86.rs b/tests/ui/invalid-compile-flags/reg-struct-return/requires-x86.rs index b5e34bece3200..436442ee72993 100644 --- a/tests/ui/invalid-compile-flags/reg-struct-return/requires-x86.rs +++ b/tests/ui/invalid-compile-flags/reg-struct-return/requires-x86.rs @@ -9,13 +9,14 @@ //@[x86_64] check-fail //@[x86_64] needs-llvm-components: x86 //@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu -//@[x86_64] error-pattern: `-Zreg-struct-return` is only supported on x86 //@[aarch64] check-fail //@[aarch64] needs-llvm-components: aarch64 //@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu -//@[aarch64] error-pattern: `-Zreg-struct-return` is only supported on x86 #![feature(no_core)] #![no_core] #![no_main] + +//[x86_64]~? ERROR `-Zreg-struct-return` is only supported on x86 +//[aarch64]~? ERROR `-Zreg-struct-return` is only supported on x86 diff --git a/tests/ui/invalid-compile-flags/regparm/regparm-valid-values.rs b/tests/ui/invalid-compile-flags/regparm/regparm-valid-values.rs index b548d678520b6..a66dfe8597718 100644 --- a/tests/ui/invalid-compile-flags/regparm/regparm-valid-values.rs +++ b/tests/ui/invalid-compile-flags/regparm/regparm-valid-values.rs @@ -17,8 +17,9 @@ //@[regparm4] check-fail //@[regparm4] compile-flags: -Zregparm=4 -//@[regparm4] error-pattern: `-Zregparm=4` is unsupported (valid values 0-3) #![feature(no_core)] #![no_core] #![no_main] + +//[regparm4]~? ERROR `-Zregparm=4` is unsupported (valid values 0-3) diff --git a/tests/ui/invalid-compile-flags/regparm/requires-x86.rs b/tests/ui/invalid-compile-flags/regparm/requires-x86.rs index ce6e437fb476d..32c4b5af35772 100644 --- a/tests/ui/invalid-compile-flags/regparm/requires-x86.rs +++ b/tests/ui/invalid-compile-flags/regparm/requires-x86.rs @@ -9,13 +9,14 @@ //@[x86_64] check-fail //@[x86_64] needs-llvm-components: x86 //@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu -//@[x86_64] error-pattern: `-Zregparm=N` is only supported on x86 //@[aarch64] check-fail //@[aarch64] needs-llvm-components: aarch64 //@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu -//@[aarch64] error-pattern: `-Zregparm=N` is only supported on x86 #![feature(no_core)] #![no_core] #![no_main] + +//[x86_64]~? ERROR `-Zregparm=N` is only supported on x86 +//[aarch64]~? ERROR `-Zregparm=N` is only supported on x86 diff --git a/tests/ui/invalid-module-declaration/invalid-module-declaration.rs b/tests/ui/invalid-module-declaration/invalid-module-declaration.rs index 254d810d79dbc..1c6c282f4b7e6 100644 --- a/tests/ui/invalid-module-declaration/invalid-module-declaration.rs +++ b/tests/ui/invalid-module-declaration/invalid-module-declaration.rs @@ -3,3 +3,5 @@ mod auxiliary { } fn main() {} + +//~? ERROR file not found for module `baz` diff --git a/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.rs b/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.rs index 8d854c6c23707..f3a15a58f26a3 100644 --- a/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.rs +++ b/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.rs @@ -6,26 +6,26 @@ const fn foo() -> i32 { fn main() { std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, || ()); - //~^ invalid argument to a legacy const generic + //~^ ERROR invalid argument to a legacy const generic std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, 5 + || ()); - //~^ invalid argument to a legacy const generic + //~^ ERROR invalid argument to a legacy const generic std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, foo::<{ 1 + 2 }>()); - //~^ invalid argument to a legacy const generic + //~^ ERROR invalid argument to a legacy const generic std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, foo::<3>()); - //~^ invalid argument to a legacy const generic + //~^ ERROR invalid argument to a legacy const generic std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, &const {}); - //~^ invalid argument to a legacy const generic + //~^ ERROR invalid argument to a legacy const generic std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, { struct F(); - //~^ invalid argument to a legacy const generic + //~^ ERROR invalid argument to a legacy const generic 1 }); std::arch::x86_64::_mm_inserti_si64(loop {}, loop {}, || (), 1 + || ()); - //~^ invalid argument to a legacy const generic + //~^ ERROR invalid argument to a legacy const generic } diff --git a/tests/ui/invalid/issue-114435-layout-type-err.rs b/tests/ui/invalid/issue-114435-layout-type-err.rs index 2a86839e416f6..07f310478d3c9 100644 --- a/tests/ui/invalid/issue-114435-layout-type-err.rs +++ b/tests/ui/invalid/issue-114435-layout-type-err.rs @@ -1,6 +1,5 @@ //@ check-fail //@ compile-flags: --crate-type lib -Cdebuginfo=2 -//@ error-pattern: recursion limit #![recursion_limit = "10"] macro_rules! link { @@ -41,3 +40,5 @@ link!(J, K); link!(K, Bottom); fn main() {} + +//~? ERROR reached the recursion limit finding the struct tail for `Bottom` diff --git a/tests/ui/invalid_dispatch_from_dyn_impls.rs b/tests/ui/invalid_dispatch_from_dyn_impls.rs index 972465d71978c..b1d4b261babee 100644 --- a/tests/ui/invalid_dispatch_from_dyn_impls.rs +++ b/tests/ui/invalid_dispatch_from_dyn_impls.rs @@ -20,7 +20,7 @@ struct MultiplePointers{ } impl DispatchFromDyn> for MultiplePointers -//~^ implementing `DispatchFromDyn` does not allow multiple fields to be coerced +//~^ ERROR implementing `DispatchFromDyn` does not allow multiple fields to be coerced where T: Unsize, {} diff --git a/tests/ui/io-checks/non-ice-error-on-worker-io-fail.rs b/tests/ui/io-checks/non-ice-error-on-worker-io-fail.rs index 7cae77eb67f41..5a5d0086aa968 100644 --- a/tests/ui/io-checks/non-ice-error-on-worker-io-fail.rs +++ b/tests/ui/io-checks/non-ice-error-on-worker-io-fail.rs @@ -16,8 +16,6 @@ // rely on the checking of the normalized stderr output as our actual // "verification" of the diagnostic). -//@ error-pattern: error - // On Mac OS X, we get an error like the below //@ normalize-stderr: "failed to write bytecode to ./does-not-exist/output.non_ice_error_on_worker_io_fail.*" -> "io error modifying ./does-not-exist/" @@ -29,3 +27,5 @@ //@ ignore-arm - the file-system issues do not replicate here, at least on armhf-gnu #![crate_type = "lib"] + +//~? ERROR /does-not-exist/ diff --git a/tests/ui/issue-13560.rs b/tests/ui/issue-13560.rs deleted file mode 100644 index 6174fa9324b1b..0000000000000 --- a/tests/ui/issue-13560.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ run-pass -//@ ignore-cross-compile (needs dylibs and compiletest doesn't have a more specific header) -//@ aux-build:issue-13560-1.rs -//@ aux-build:issue-13560-2.rs -//@ aux-build:issue-13560-3.rs - -// Regression test for issue #13560, the test itself is all in the dependent -// libraries. The fail which previously failed to compile is the one numbered 3. - -extern crate issue_13560_2 as t2; -extern crate issue_13560_3 as t3; - -fn main() {} diff --git a/tests/ui/issue-18502.rs b/tests/ui/issue-18502.rs deleted file mode 100644 index 3e2c37ee8aa94..0000000000000 --- a/tests/ui/issue-18502.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ run-pass -//@ aux-build:issue-18502.rs - -extern crate issue_18502 as fmt; - -fn main() { - ::fmt::baz(); -} diff --git a/tests/ui/issue-24106.rs b/tests/ui/issue-24106.rs deleted file mode 100644 index 4f7b299b12f5c..0000000000000 --- a/tests/ui/issue-24106.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ run-pass -//@ aux-build:issue-24106.rs - -extern crate issue_24106; - -fn main() { - issue_24106::go::<()>(); -} diff --git a/tests/ui/issue-76387-llvm-miscompile.rs b/tests/ui/issue-76387-llvm-miscompile.rs deleted file mode 100644 index d674ebb5eaf17..0000000000000 --- a/tests/ui/issue-76387-llvm-miscompile.rs +++ /dev/null @@ -1,21 +0,0 @@ -//@ compile-flags: -C opt-level=3 -//@ aux-build: issue-76387.rs -//@ run-pass - -// Regression test for issue #76387 -// Tests that LLVM doesn't miscompile this - -extern crate issue_76387; - -use issue_76387::FatPtr; - -fn print(data: &[u8]) { - println!("{:#?}", data); -} - -fn main() { - let ptr = FatPtr::new(20); - let data = unsafe { std::slice::from_raw_parts(ptr.as_ptr(), ptr.len()) }; - - print(data); -} diff --git a/tests/ui/issues/auxiliary/issue-14421.rs b/tests/ui/issues/auxiliary/issue-14421.rs deleted file mode 100644 index 5fe4b24cf1708..0000000000000 --- a/tests/ui/issues/auxiliary/issue-14421.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![crate_type="lib"] -#![deny(warnings)] -#![allow(dead_code)] - -pub use src::aliases::B; -pub use src::hidden_core::make; - -mod src { - pub mod aliases { - use super::hidden_core::A; - pub type B = A; - } - - pub mod hidden_core { - use super::aliases::B; - - pub struct A { t: T } - - pub fn make() -> B { A { t: 1.0 } } - - impl A { - pub fn foo(&mut self) { println!("called foo"); } - } - } -} diff --git a/tests/ui/issues/issue-106755.rs b/tests/ui/issues/issue-106755.rs index 689b1d885ae60..d7e7122ebda16 100644 --- a/tests/ui/issues/issue-106755.rs +++ b/tests/ui/issues/issue-106755.rs @@ -10,10 +10,13 @@ struct TestType(::std::marker::PhantomData); unsafe impl Send for TestType {} -impl !Send for TestType {} //~ ERROR found both positive and negative implementation +impl !Send for TestType {} +//~^ ERROR found both positive and negative implementation +//~| ERROR `!Send` impl requires `T: MyTrait` but the struct it is implemented for does not unsafe impl Send for TestType {} //~ ERROR conflicting implementations impl !Send for TestType {} +//~^ ERROR `!Send` impls cannot be specialized fn main() {} diff --git a/tests/ui/issues/issue-106755.stderr b/tests/ui/issues/issue-106755.stderr index 543970340620d..da6b8c5c56325 100644 --- a/tests/ui/issues/issue-106755.stderr +++ b/tests/ui/issues/issue-106755.stderr @@ -8,7 +8,7 @@ LL | impl !Send for TestType {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here error[E0119]: conflicting implementations of trait `Send` for type `TestType<_>` - --> $DIR/issue-106755.rs:15:1 + --> $DIR/issue-106755.rs:17:1 | LL | unsafe impl Send for TestType {} | ------------------------------------------------------ first implementation here @@ -16,7 +16,32 @@ LL | unsafe impl Send for TestType {} LL | unsafe impl Send for TestType {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `TestType<_>` -error: aborting due to 2 previous errors +error[E0367]: `!Send` impl requires `T: MyTrait` but the struct it is implemented for does not + --> $DIR/issue-106755.rs:13:9 + | +LL | impl !Send for TestType {} + | ^^^^^^^ + | +note: the implementor must specify the same requirement + --> $DIR/issue-106755.rs:9:1 + | +LL | struct TestType(::std::marker::PhantomData); + | ^^^^^^^^^^^^^^^^^^ + +error[E0366]: `!Send` impls cannot be specialized + --> $DIR/issue-106755.rs:19:1 + | +LL | impl !Send for TestType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `i32` is not a generic parameter +note: use the same sequence of generic lifetime, type and const parameters as the struct definition + --> $DIR/issue-106755.rs:9:1 + | +LL | struct TestType(::std::marker::PhantomData); + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0119, E0751. +Some errors have detailed explanations: E0119, E0366, E0367, E0751. For more information about an error, try `rustc --explain E0119`. diff --git a/tests/ui/issues/issue-11004.rs b/tests/ui/issues/issue-11004.rs index 09d5476dbe608..0c34554c12d11 100644 --- a/tests/ui/issues/issue-11004.rs +++ b/tests/ui/issues/issue-11004.rs @@ -4,12 +4,12 @@ struct A { x: i32, y: f64 } #[cfg(not(FALSE))] unsafe fn access(n:*mut A) -> (i32, f64) { - let x : i32 = n.x; //~ no field `x` on type `*mut A` - let y : f64 = n.y; //~ no field `y` on type `*mut A` + let x : i32 = n.x; //~ ERROR no field `x` on type `*mut A` + let y : f64 = n.y; //~ ERROR no field `y` on type `*mut A` (x, y) } -#[cfg(FALSE)] +#[cfg(false)] unsafe fn access(n:*mut A) -> (i32, f64) { let x : i32 = (*n).x; let y : f64 = (*n).y; diff --git a/tests/ui/issues/issue-11085.rs b/tests/ui/issues/issue-11085.rs index d0703b0639542..c3f13199b308e 100644 --- a/tests/ui/issues/issue-11085.rs +++ b/tests/ui/issues/issue-11085.rs @@ -3,7 +3,7 @@ #![allow(dead_code)] struct Foo { - #[cfg(FALSE)] + #[cfg(false)] bar: baz, foo: isize, } @@ -15,18 +15,18 @@ struct Foo2 { enum Bar1 { Bar1_1, - #[cfg(FALSE)] + #[cfg(false)] Bar1_2(NotAType), } enum Bar2 { - #[cfg(FALSE)] + #[cfg(false)] Bar2_1(NotAType), } enum Bar3 { Bar3_1 { - #[cfg(FALSE)] + #[cfg(false)] foo: isize, bar: isize, } diff --git a/tests/ui/issues/issue-13359.rs b/tests/ui/issues/issue-13359.rs index 9129790c501e3..5d31d7f861c68 100644 --- a/tests/ui/issues/issue-13359.rs +++ b/tests/ui/issues/issue-13359.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + fn foo(_s: i16) { } fn bar(_s: u32) { } @@ -5,9 +7,9 @@ fn bar(_s: u32) { } fn main() { foo(1*(1 as isize)); //~^ ERROR mismatched types - //~| expected `i16`, found `isize` + //~| NOTE expected `i16`, found `isize` bar(1*(1 as usize)); //~^ ERROR mismatched types - //~| expected `u32`, found `usize` + //~| NOTE expected `u32`, found `usize` } diff --git a/tests/ui/issues/issue-13359.stderr b/tests/ui/issues/issue-13359.stderr index fef63680a8676..91f5de8e8f3ad 100644 --- a/tests/ui/issues/issue-13359.stderr +++ b/tests/ui/issues/issue-13359.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-13359.rs:6:9 + --> $DIR/issue-13359.rs:8:9 | LL | foo(1*(1 as isize)); | --- ^^^^^^^^^^^^^^ expected `i16`, found `isize` @@ -7,7 +7,7 @@ LL | foo(1*(1 as isize)); | arguments to this function are incorrect | note: function defined here - --> $DIR/issue-13359.rs:1:4 + --> $DIR/issue-13359.rs:3:4 | LL | fn foo(_s: i16) { } | ^^^ ------- @@ -17,7 +17,7 @@ LL | foo((1*(1 as isize)).try_into().unwrap()); | + +++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/issue-13359.rs:10:9 + --> $DIR/issue-13359.rs:12:9 | LL | bar(1*(1 as usize)); | --- ^^^^^^^^^^^^^^ expected `u32`, found `usize` @@ -25,7 +25,7 @@ LL | bar(1*(1 as usize)); | arguments to this function are incorrect | note: function defined here - --> $DIR/issue-13359.rs:3:4 + --> $DIR/issue-13359.rs:5:4 | LL | fn bar(_s: u32) { } | ^^^ ------- diff --git a/tests/ui/issues/issue-13466.rs b/tests/ui/issues/issue-13466.rs index 52d4d75d29da2..78ce4c1d2f604 100644 --- a/tests/ui/issues/issue-13466.rs +++ b/tests/ui/issues/issue-13466.rs @@ -1,5 +1,7 @@ // Regression test for #13466 +//@ dont-require-annotations: NOTE + pub fn main() { // The expected arm type `Option` has one type parameter, while // the actual arm `Result` has two. typeck should not be @@ -7,14 +9,14 @@ pub fn main() { let _x: usize = match Some(1) { Ok(u) => u, //~^ ERROR mismatched types - //~| expected enum `Option<{integer}>` - //~| found enum `Result<_, _>` - //~| expected `Option<{integer}>`, found `Result<_, _>` + //~| NOTE expected enum `Option<{integer}>` + //~| NOTE found enum `Result<_, _>` + //~| NOTE expected `Option<{integer}>`, found `Result<_, _>` Err(e) => panic!(e) //~^ ERROR mismatched types - //~| expected enum `Option<{integer}>` - //~| found enum `Result<_, _>` - //~| expected `Option<{integer}>`, found `Result<_, _>` + //~| NOTE expected enum `Option<{integer}>` + //~| NOTE found enum `Result<_, _>` + //~| NOTE expected `Option<{integer}>`, found `Result<_, _>` }; } diff --git a/tests/ui/issues/issue-13466.stderr b/tests/ui/issues/issue-13466.stderr index fd928e45863db..68a555a16260a 100644 --- a/tests/ui/issues/issue-13466.stderr +++ b/tests/ui/issues/issue-13466.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-13466.rs:8:9 + --> $DIR/issue-13466.rs:10:9 | LL | let _x: usize = match Some(1) { | ------- this expression has type `Option<{integer}>` @@ -10,7 +10,7 @@ LL | Ok(u) => u, found enum `Result<_, _>` error[E0308]: mismatched types - --> $DIR/issue-13466.rs:14:9 + --> $DIR/issue-13466.rs:16:9 | LL | let _x: usize = match Some(1) { | ------- this expression has type `Option<{integer}>` diff --git a/tests/ui/issues/issue-13497.rs b/tests/ui/issues/issue-13497.rs index 7f786a54b9f71..4b2795aa841e8 100644 --- a/tests/ui/issues/issue-13497.rs +++ b/tests/ui/issues/issue-13497.rs @@ -3,7 +3,6 @@ fn read_lines_borrowed1() -> Vec< > { let rawLines: Vec = vec!["foo ".to_string(), " bar".to_string()]; rawLines.iter().map(|l| l.trim()).collect() - //~^ ERROR: cannot return value referencing } fn main() {} diff --git a/tests/ui/issues/issue-13497.stderr b/tests/ui/issues/issue-13497.stderr index 8016b55d6aadc..ee111f1d262e0 100644 --- a/tests/ui/issues/issue-13497.stderr +++ b/tests/ui/issues/issue-13497.stderr @@ -15,16 +15,6 @@ LL - &str LL + String | -error[E0515]: cannot return value referencing local variable `rawLines` - --> $DIR/issue-13497.rs:5:5 - | -LL | rawLines.iter().map(|l| l.trim()).collect() - | --------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | returns a value referencing data owned by the current function - | `rawLines` is borrowed here - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0106, E0515. -For more information about an error, try `rustc --explain E0106`. +For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/issues/issue-14091-2.stderr b/tests/ui/issues/issue-14091-2.stderr index ac8947670592a..d573a0917be5f 100644 --- a/tests/ui/issues/issue-14091-2.stderr +++ b/tests/ui/issues/issue-14091-2.stderr @@ -11,7 +11,6 @@ LL | pub struct BytePos(pub u32); | ^^^^^^^^^^^^^^^^^^ must implement `Not` note: the trait `Not` must be implemented --> $SRC_DIR/core/src/ops/bit.rs:LL:COL - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-14421.rs b/tests/ui/issues/issue-14421.rs deleted file mode 100644 index b7038584fcea6..0000000000000 --- a/tests/ui/issues/issue-14421.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-pass -#![allow(non_snake_case)] - -//@ aux-build:issue-14421.rs - - -extern crate issue_14421 as bug_lib; - -use bug_lib::B; -use bug_lib::make; - -pub fn main() { - let mut an_A: B = make(); - an_A.foo(); -} diff --git a/tests/ui/issues/issue-14541.rs b/tests/ui/issues/issue-14541.rs index 2ff1c1f8876cd..358d29419f95b 100644 --- a/tests/ui/issues/issue-14541.rs +++ b/tests/ui/issues/issue-14541.rs @@ -4,7 +4,8 @@ struct Vec3 { y: f32, z: f32 } fn make(v: Vec2) { let Vec3 { y: _, z: _ } = v; //~^ ERROR mismatched types - //~| expected `Vec2`, found `Vec3` + //~| NOTE expected `Vec2`, found `Vec3` + //~| NOTE this expression has type `Vec2` } fn main() { } diff --git a/tests/ui/issues/issue-14721.rs b/tests/ui/issues/issue-14721.rs index a5c47dd8cb3d6..c015a6bab083b 100644 --- a/tests/ui/issues/issue-14721.rs +++ b/tests/ui/issues/issue-14721.rs @@ -1,4 +1,4 @@ fn main() { let foo = "str"; - println!("{}", foo.desc); //~ no field `desc` on type `&str` + println!("{}", foo.desc); //~ ERROR no field `desc` on type `&str` } diff --git a/tests/ui/issues/issue-15094.rs b/tests/ui/issues/issue-15094.rs index cb27e2bcfb6c0..408ab82eb8c87 100644 --- a/tests/ui/issues/issue-15094.rs +++ b/tests/ui/issues/issue-15094.rs @@ -10,8 +10,9 @@ impl ops::FnOnce<(),> for Debuger { type Output = (); fn call_once(self, _args: ()) { //~^ ERROR `call_once` has an incompatible type for trait - //~| expected signature `extern "rust-call" fn - //~| found signature `fn + //~| NOTE expected signature `extern "rust-call" fn + //~| NOTE found signature `fn + //~| NOTE expected "rust-call" fn, found "Rust" fn println!("{:?}", self.x); } } diff --git a/tests/ui/issues/issue-15381.rs b/tests/ui/issues/issue-15381.rs index 23b266bef1d91..bd5f62ddc674f 100644 --- a/tests/ui/issues/issue-15381.rs +++ b/tests/ui/issues/issue-15381.rs @@ -3,7 +3,8 @@ fn main() { for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) { //~^ ERROR refutable pattern in `for` loop binding - //~| patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered + //~| NOTE patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered + //~| NOTE the matched value is of type `&[u8]` println!("y={}", y); } } diff --git a/tests/ui/issues/issue-15783.rs b/tests/ui/issues/issue-15783.rs index ceb37a20e3f30..ef948a1a88ce1 100644 --- a/tests/ui/issues/issue-15783.rs +++ b/tests/ui/issues/issue-15783.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + pub fn foo(params: Option<&[&str]>) -> usize { params.unwrap().first().unwrap().len() } @@ -7,8 +9,8 @@ fn main() { let x = Some(&[name]); let msg = foo(x); //~^ ERROR mismatched types - //~| expected enum `Option<&[&str]>` - //~| found enum `Option<&[&str; 1]>` - //~| expected `Option<&[&str]>`, found `Option<&[&str; 1]>` + //~| NOTE expected enum `Option<&[&str]>` + //~| NOTE found enum `Option<&[&str; 1]>` + //~| NOTE expected `Option<&[&str]>`, found `Option<&[&str; 1]>` assert_eq!(msg, 3); } diff --git a/tests/ui/issues/issue-15783.stderr b/tests/ui/issues/issue-15783.stderr index 7ed2e19913031..c9c9a723a860b 100644 --- a/tests/ui/issues/issue-15783.stderr +++ b/tests/ui/issues/issue-15783.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-15783.rs:8:19 + --> $DIR/issue-15783.rs:10:19 | LL | let msg = foo(x); | --- ^ expected `Option<&[&str]>`, found `Option<&[&str; 1]>` @@ -9,7 +9,7 @@ LL | let msg = foo(x); = note: expected enum `Option<&[&str]>` found enum `Option<&[&str; 1]>` note: function defined here - --> $DIR/issue-15783.rs:1:8 + --> $DIR/issue-15783.rs:3:8 | LL | pub fn foo(params: Option<&[&str]>) -> usize { | ^^^ ----------------------- diff --git a/tests/ui/issues/issue-16401.rs b/tests/ui/issues/issue-16401.rs index 19ae7da107ca2..0985a6806d260 100644 --- a/tests/ui/issues/issue-16401.rs +++ b/tests/ui/issues/issue-16401.rs @@ -4,12 +4,12 @@ struct Slice { } fn main() { - match () { + match () { //~ NOTE this expression has type `()` Slice { data: data, len: len } => (), //~^ ERROR mismatched types - //~| expected unit type `()` - //~| found struct `Slice<_>` - //~| expected `()`, found `Slice<_>` + //~| NOTE expected unit type `()` + //~| NOTE found struct `Slice<_>` + //~| NOTE expected `()`, found `Slice<_>` _ => unreachable!() } } diff --git a/tests/ui/issues/issue-16819.rs b/tests/ui/issues/issue-16819.rs index e2b1090917774..2805c82acfb2d 100644 --- a/tests/ui/issues/issue-16819.rs +++ b/tests/ui/issues/issue-16819.rs @@ -3,7 +3,7 @@ // `#[cfg]` on struct field permits empty unusable struct struct S { - #[cfg(FALSE)] + #[cfg(false)] a: int, } diff --git a/tests/ui/issues/issue-16939.rs b/tests/ui/issues/issue-16939.rs deleted file mode 100644 index ad7248343918d..0000000000000 --- a/tests/ui/issues/issue-16939.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Make sure we don't ICE when making an overloaded call with the -// wrong arity. - -fn _foo (f: F) { - |t| f(t); //~ ERROR E0057 -} - -fn main() {} diff --git a/tests/ui/issues/issue-16939.stderr b/tests/ui/issues/issue-16939.stderr deleted file mode 100644 index 6e0889b89635b..0000000000000 --- a/tests/ui/issues/issue-16939.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error[E0057]: this function takes 0 arguments but 1 argument was supplied - --> $DIR/issue-16939.rs:5:9 - | -LL | |t| f(t); - | ^ - unexpected argument - | -note: callable defined here - --> $DIR/issue-16939.rs:4:12 - | -LL | fn _foo (f: F) { - | ^^^^ -help: remove the extra argument - | -LL - |t| f(t); -LL + |t| f(); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0057`. diff --git a/tests/ui/issues/issue-17033.rs b/tests/ui/issues/issue-17033.rs index 72a8cd9823a4b..b8eec3b6b2505 100644 --- a/tests/ui/issues/issue-17033.rs +++ b/tests/ui/issues/issue-17033.rs @@ -1,6 +1,7 @@ fn f<'r>(p: &'r mut fn(p: &mut ())) { (*p)(()) //~ ERROR mismatched types - //~| expected `&mut ()`, found `()` + //~| NOTE expected `&mut ()`, found `()` + //~| NOTE arguments to this function are incorrect } fn main() {} diff --git a/tests/ui/issues/issue-17740.rs b/tests/ui/issues/issue-17740.rs index 3b868555fc522..20a73756ea3ea 100644 --- a/tests/ui/issues/issue-17740.rs +++ b/tests/ui/issues/issue-17740.rs @@ -1,17 +1,19 @@ +//@ dont-require-annotations: NOTE + struct Foo<'a> { data: &'a[u8], } impl <'a> Foo<'a>{ fn bar(self: &mut Foo) { - //~^ mismatched `self` parameter type - //~| expected struct `Foo<'a>` - //~| found struct `Foo<'_>` - //~| lifetime mismatch - //~| mismatched `self` parameter type - //~| expected struct `Foo<'a>` - //~| found struct `Foo<'_>` - //~| lifetime mismatch + //~^ ERROR mismatched `self` parameter type + //~| NOTE expected struct `Foo<'a>` + //~| NOTE found struct `Foo<'_>` + //~| NOTE lifetime mismatch + //~| ERROR mismatched `self` parameter type + //~| NOTE expected struct `Foo<'a>` + //~| NOTE found struct `Foo<'_>` + //~| NOTE lifetime mismatch } } diff --git a/tests/ui/issues/issue-17740.stderr b/tests/ui/issues/issue-17740.stderr index d177380415591..198d7d5b37cc7 100644 --- a/tests/ui/issues/issue-17740.stderr +++ b/tests/ui/issues/issue-17740.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched `self` parameter type - --> $DIR/issue-17740.rs:6:18 + --> $DIR/issue-17740.rs:8:18 | LL | fn bar(self: &mut Foo) { | ^^^^^^^^ lifetime mismatch @@ -7,18 +7,18 @@ LL | fn bar(self: &mut Foo) { = note: expected struct `Foo<'a>` found struct `Foo<'_>` note: the anonymous lifetime defined here... - --> $DIR/issue-17740.rs:6:23 + --> $DIR/issue-17740.rs:8:23 | LL | fn bar(self: &mut Foo) { | ^^^ note: ...does not necessarily outlive the lifetime `'a` as defined here - --> $DIR/issue-17740.rs:5:7 + --> $DIR/issue-17740.rs:7:7 | LL | impl <'a> Foo<'a>{ | ^^ error[E0308]: mismatched `self` parameter type - --> $DIR/issue-17740.rs:6:18 + --> $DIR/issue-17740.rs:8:18 | LL | fn bar(self: &mut Foo) { | ^^^^^^^^ lifetime mismatch @@ -26,12 +26,12 @@ LL | fn bar(self: &mut Foo) { = note: expected struct `Foo<'a>` found struct `Foo<'_>` note: the lifetime `'a` as defined here... - --> $DIR/issue-17740.rs:5:7 + --> $DIR/issue-17740.rs:7:7 | LL | impl <'a> Foo<'a>{ | ^^ note: ...does not necessarily outlive the anonymous lifetime defined here - --> $DIR/issue-17740.rs:6:23 + --> $DIR/issue-17740.rs:8:23 | LL | fn bar(self: &mut Foo) { | ^^^ diff --git a/tests/ui/issues/issue-18183.stderr b/tests/ui/issues/issue-18183.stderr index 11015d75d9735..07fa4cdc7535c 100644 --- a/tests/ui/issues/issue-18183.stderr +++ b/tests/ui/issues/issue-18183.stderr @@ -1,8 +1,8 @@ -error[E0128]: generic parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameter defaults cannot reference parameters before they are declared --> $DIR/issue-18183.rs:1:20 | LL | pub struct Foo(Bar); - | ^^^ defaulted generic parameters cannot be forward declared + | ^^^ cannot reference `Bar` before it is declared error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-18959.rs b/tests/ui/issues/issue-18959.rs index f4cab630f2e95..6aeb34879ea1c 100644 --- a/tests/ui/issues/issue-18959.rs +++ b/tests/ui/issues/issue-18959.rs @@ -20,5 +20,4 @@ fn main() { //~^ ERROR E0038 //~| ERROR E0038 foo(test); - //~^ ERROR E0038 } diff --git a/tests/ui/issues/issue-18959.stderr b/tests/ui/issues/issue-18959.stderr index c37c4177bfc1e..1e050b115e573 100644 --- a/tests/ui/issues/issue-18959.stderr +++ b/tests/ui/issues/issue-18959.stderr @@ -30,22 +30,6 @@ LL | pub trait Bar: Foo { } | --- this trait is not dyn compatible... = help: consider moving `foo` to another trait -error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/issue-18959.rs:19:15 - | -LL | let test: &dyn Bar = &mut thing; - | ^^^^^^^^ `Bar` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-18959.rs:1:20 - | -LL | pub trait Foo { fn foo(&self, ext_thing: &T); } - | ^^^ ...because method `foo` has generic type parameters -LL | pub trait Bar: Foo { } - | --- this trait is not dyn compatible... - = help: consider moving `foo` to another trait - error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/issue-18959.rs:19:26 | @@ -64,10 +48,10 @@ LL | pub trait Bar: Foo { } = note: required for the cast from `&mut Thing` to `&dyn Bar` error[E0038]: the trait `Bar` is not dyn compatible - --> $DIR/issue-18959.rs:22:9 + --> $DIR/issue-18959.rs:19:15 | -LL | foo(test); - | ^^^^ `Bar` is not dyn compatible +LL | let test: &dyn Bar = &mut thing; + | ^^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit @@ -79,6 +63,6 @@ LL | pub trait Bar: Foo { } | --- this trait is not dyn compatible... = help: consider moving `foo` to another trait -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/issues/issue-19991.rs b/tests/ui/issues/issue-19991.rs index dd0efa972ba3e..cb558b5869700 100644 --- a/tests/ui/issues/issue-19991.rs +++ b/tests/ui/issues/issue-19991.rs @@ -1,9 +1,11 @@ // Test if the sugared `if let` construct correctly prints "missing an else clause" when an else // clause does not exist, instead of the unsympathetic "`match` arms have incompatible types" +//@ dont-require-annotations: NOTE + fn main() { if let Some(homura) = Some("madoka") { //~ ERROR missing an `else` clause - //~| expected integer, found `()` + //~| NOTE expected integer, found `()` 765 }; } diff --git a/tests/ui/issues/issue-19991.stderr b/tests/ui/issues/issue-19991.stderr index ff039f3adba90..1449bab514200 100644 --- a/tests/ui/issues/issue-19991.stderr +++ b/tests/ui/issues/issue-19991.stderr @@ -1,5 +1,5 @@ error[E0317]: `if` may be missing an `else` clause - --> $DIR/issue-19991.rs:5:5 + --> $DIR/issue-19991.rs:7:5 | LL | / if let Some(homura) = Some("madoka") { LL | | diff --git a/tests/ui/issues/issue-20413.rs b/tests/ui/issues/issue-20413.rs index 7766f3755993b..138a235e675e3 100644 --- a/tests/ui/issues/issue-20413.rs +++ b/tests/ui/issues/issue-20413.rs @@ -1,4 +1,3 @@ -//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" trait Foo { fn answer(self); } diff --git a/tests/ui/issues/issue-20413.stderr b/tests/ui/issues/issue-20413.stderr index 5d442eb989851..42f3cd2d06274 100644 --- a/tests/ui/issues/issue-20413.stderr +++ b/tests/ui/issues/issue-20413.stderr @@ -1,5 +1,5 @@ error[E0392]: type parameter `T` is never used - --> $DIR/issue-20413.rs:6:15 + --> $DIR/issue-20413.rs:5:15 | LL | struct NoData; | ^ unused type parameter @@ -8,14 +8,14 @@ LL | struct NoData; = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead error[E0275]: overflow evaluating the requirement `NoData>>>>>>: Foo` - --> $DIR/issue-20413.rs:9:36 + --> $DIR/issue-20413.rs:8:36 | LL | impl Foo for T where NoData: Foo { | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`) note: required for `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Foo` - --> $DIR/issue-20413.rs:9:9 + --> $DIR/issue-20413.rs:8:9 | LL | impl Foo for T where NoData: Foo { | ^^^ ^ --- unsatisfied trait bound introduced here @@ -23,19 +23,19 @@ LL | impl Foo for T where NoData: Foo { = note: required for `NoData` to implement `Foo` error[E0275]: overflow evaluating the requirement `AlmostNoData>>>>>>: Bar` - --> $DIR/issue-20413.rs:28:42 + --> $DIR/issue-20413.rs:27:42 | LL | impl Bar for T where EvenLessData: Baz { | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`) note: required for `EvenLessData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Baz` - --> $DIR/issue-20413.rs:35:9 + --> $DIR/issue-20413.rs:34:9 | LL | impl Baz for T where AlmostNoData: Bar { | ^^^ ^ --- unsatisfied trait bound introduced here note: required for `AlmostNoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Bar` - --> $DIR/issue-20413.rs:28:9 + --> $DIR/issue-20413.rs:27:9 | LL | impl Bar for T where EvenLessData: Baz { | ^^^ ^ --- unsatisfied trait bound introduced here @@ -43,19 +43,19 @@ LL | impl Bar for T where EvenLessData: Baz { = note: required for `EvenLessData` to implement `Baz` error[E0275]: overflow evaluating the requirement `EvenLessData>>>>>>: Baz` - --> $DIR/issue-20413.rs:35:42 + --> $DIR/issue-20413.rs:34:42 | LL | impl Baz for T where AlmostNoData: Bar { | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`) note: required for `AlmostNoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Bar` - --> $DIR/issue-20413.rs:28:9 + --> $DIR/issue-20413.rs:27:9 | LL | impl Bar for T where EvenLessData: Baz { | ^^^ ^ --- unsatisfied trait bound introduced here note: required for `EvenLessData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `Baz` - --> $DIR/issue-20413.rs:35:9 + --> $DIR/issue-20413.rs:34:9 | LL | impl Baz for T where AlmostNoData: Bar { | ^^^ ^ --- unsatisfied trait bound introduced here diff --git a/tests/ui/issues/issue-21160.stderr b/tests/ui/issues/issue-21160.stderr index 892a4530ebcbb..f1145a0a7c1d0 100644 --- a/tests/ui/issues/issue-21160.stderr +++ b/tests/ui/issues/issue-21160.stderr @@ -6,7 +6,6 @@ LL | #[derive(Hash)] LL | struct Foo(Bar); | ^^^ the trait `Hash` is not implemented for `Bar` | - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Bar` with `#[derive(Hash)]` | LL + #[derive(Hash)] diff --git a/tests/ui/issues/issue-21332.rs b/tests/ui/issues/issue-21332.rs index 4473d00fd5da2..ad764f84aa81a 100644 --- a/tests/ui/issues/issue-21332.rs +++ b/tests/ui/issues/issue-21332.rs @@ -4,7 +4,8 @@ impl Iterator for S { type Item = i32; fn next(&mut self) -> Result { Ok(7) } //~^ ERROR method `next` has an incompatible type for trait - //~| expected `Option`, found `Result` + //~| NOTE expected `Option`, found `Result` + //~| NOTE expected signature `fn(&mut S) -> Option` } fn main() {} diff --git a/tests/ui/issues/issue-23253.rs b/tests/ui/issues/issue-23253.rs index 22b55c2858145..b285cb81ee230 100644 --- a/tests/ui/issues/issue-23253.rs +++ b/tests/ui/issues/issue-23253.rs @@ -2,5 +2,5 @@ enum Foo { Bar } fn main() { Foo::Bar.a; - //~^ no field `a` on type `Foo` + //~^ ERROR no field `a` on type `Foo` } diff --git a/tests/ui/issues/issue-23808.rs b/tests/ui/issues/issue-23808.rs deleted file mode 100644 index 6af0bd422e3a5..0000000000000 --- a/tests/ui/issues/issue-23808.rs +++ /dev/null @@ -1,59 +0,0 @@ -//@ run-pass - -#![deny(dead_code)] - -// use different types / traits to test all combinations - -trait Const { - const C: (); -} - -trait StaticFn { - fn sfn(); -} - -struct ConstStruct; -struct StaticFnStruct; - -enum ConstEnum {} -enum StaticFnEnum {} - -struct AliasedConstStruct; -struct AliasedStaticFnStruct; - -enum AliasedConstEnum {} -enum AliasedStaticFnEnum {} - -type AliasConstStruct = AliasedConstStruct; -type AliasStaticFnStruct = AliasedStaticFnStruct; -type AliasConstEnum = AliasedConstEnum; -type AliasStaticFnEnum = AliasedStaticFnEnum; - -macro_rules! impl_Const {($($T:ident),*) => {$( - impl Const for $T { - const C: () = (); - } -)*}} - -macro_rules! impl_StaticFn {($($T:ident),*) => {$( - impl StaticFn for $T { - fn sfn() {} - } -)*}} - -impl_Const!(ConstStruct, ConstEnum, AliasedConstStruct, AliasedConstEnum); -impl_StaticFn!(StaticFnStruct, StaticFnEnum, AliasedStaticFnStruct, AliasedStaticFnEnum); - -fn main() { - let () = ConstStruct::C; - let () = ConstEnum::C; - - StaticFnStruct::sfn(); - StaticFnEnum::sfn(); - - let () = AliasConstStruct::C; - let () = AliasConstEnum::C; - - AliasStaticFnStruct::sfn(); - AliasStaticFnEnum::sfn(); -} diff --git a/tests/ui/issues/issue-24363.rs b/tests/ui/issues/issue-24363.rs index 34726fba9c66a..f358b8b90171d 100644 --- a/tests/ui/issues/issue-24363.rs +++ b/tests/ui/issues/issue-24363.rs @@ -1,5 +1,5 @@ fn main() { - 1.create_a_type_error[ //~ `{integer}` is a primitive type and therefore doesn't have fields + 1.create_a_type_error[ //~ ERROR `{integer}` is a primitive type and therefore doesn't have fields ()+() //~ ERROR cannot add // ^ ensure that we typeck the inner expression ^ ]; diff --git a/tests/ui/issues/issue-24365.rs b/tests/ui/issues/issue-24365.rs index eca104b6f1d89..da19511604783 100644 --- a/tests/ui/issues/issue-24365.rs +++ b/tests/ui/issues/issue-24365.rs @@ -7,13 +7,13 @@ pub enum Foo { } fn test(a: Foo) { - println!("{}", a.b); //~ no field `b` on type `Foo` + println!("{}", a.b); //~ ERROR no field `b` on type `Foo` } fn main() { let x = Attribute::Code { attr_name_idx: 42, }; - let z = (&x).attr_name_idx; //~ no field `attr_name_idx` on type `&Attribute` - let y = x.attr_name_idx; //~ no field `attr_name_idx` on type `Attribute` + let z = (&x).attr_name_idx; //~ ERROR no field `attr_name_idx` on type `&Attribute` + let y = x.attr_name_idx; //~ ERROR no field `attr_name_idx` on type `Attribute` } diff --git a/tests/ui/issues/issue-24819.rs b/tests/ui/issues/issue-24819.rs index fb4cfb7b29e8b..97d288e5cb144 100644 --- a/tests/ui/issues/issue-24819.rs +++ b/tests/ui/issues/issue-24819.rs @@ -1,10 +1,12 @@ +//@ dont-require-annotations: NOTE + use std::collections::HashSet; fn main() { let mut v = Vec::new(); foo(&mut v); //~^ ERROR mismatched types - //~| expected `&mut HashSet`, found `&mut Vec<_>` + //~| NOTE expected `&mut HashSet`, found `&mut Vec<_>` } fn foo(h: &mut HashSet) { diff --git a/tests/ui/issues/issue-24819.stderr b/tests/ui/issues/issue-24819.stderr index 8ec34aa13fa6a..e144f37d6e48b 100644 --- a/tests/ui/issues/issue-24819.stderr +++ b/tests/ui/issues/issue-24819.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-24819.rs:5:9 + --> $DIR/issue-24819.rs:7:9 | LL | foo(&mut v); | --- ^^^^^^ expected `&mut HashSet`, found `&mut Vec<_>` @@ -9,7 +9,7 @@ LL | foo(&mut v); = note: expected mutable reference `&mut HashSet` found mutable reference `&mut Vec<_>` note: function defined here - --> $DIR/issue-24819.rs:10:4 + --> $DIR/issue-24819.rs:12:4 | LL | fn foo(h: &mut HashSet) { | ^^^ -------------------- diff --git a/tests/ui/issues/issue-25746-bool-transmute.rs b/tests/ui/issues/issue-25746-bool-transmute.rs index f8cdc980daa48..046dcf83f62d6 100644 --- a/tests/ui/issues/issue-25746-bool-transmute.rs +++ b/tests/ui/issues/issue-25746-bool-transmute.rs @@ -1,4 +1,5 @@ //@ run-pass +#![allow(unnecessary_transmutes)] use std::mem::transmute; fn main() { diff --git a/tests/ui/issues/issue-26812.rs b/tests/ui/issues/issue-26812.rs index e0723e016b381..8eb030a8ec94f 100644 --- a/tests/ui/issues/issue-26812.rs +++ b/tests/ui/issues/issue-26812.rs @@ -1,5 +1,5 @@ fn avg(_: T) {} -//~^ ERROR generic parameters with a default cannot use forward declared identifiers +//~^ ERROR generic parameter defaults cannot reference parameters before they are declared //~| ERROR defaults for type parameters //~| WARN previously accepted diff --git a/tests/ui/issues/issue-26812.stderr b/tests/ui/issues/issue-26812.stderr index 4a18b23fd8b13..bb60d67e2876e 100644 --- a/tests/ui/issues/issue-26812.stderr +++ b/tests/ui/issues/issue-26812.stderr @@ -1,8 +1,8 @@ -error[E0128]: generic parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameter defaults cannot reference parameters before they are declared --> $DIR/issue-26812.rs:1:10 | LL | fn avg(_: T) {} - | ^^^^^^^ defaulted generic parameters cannot be forward declared + | ^^^^^^^ cannot reference `T` before it is declared error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions --> $DIR/issue-26812.rs:1:8 diff --git a/tests/ui/issues/issue-27008.rs b/tests/ui/issues/issue-27008.rs index adf8e779e0a04..20aa4f282c7e6 100644 --- a/tests/ui/issues/issue-27008.rs +++ b/tests/ui/issues/issue-27008.rs @@ -3,5 +3,5 @@ struct S; fn main() { let b = [0; S]; //~^ ERROR mismatched types - //~| expected `usize`, found `S` + //~| NOTE expected `usize`, found `S` } diff --git a/tests/ui/issues/issue-27340.stderr b/tests/ui/issues/issue-27340.stderr index 61ae660f4cf70..d5ff29a618b01 100644 --- a/tests/ui/issues/issue-27340.stderr +++ b/tests/ui/issues/issue-27340.stderr @@ -6,8 +6,6 @@ LL | #[derive(Copy, Clone)] LL | LL | struct Bar(Foo); | --- this field does not implement `Copy` - | - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `Foo: Clone` is not satisfied --> $DIR/issue-27340.rs:4:12 @@ -20,7 +18,6 @@ LL | struct Bar(Foo); | note: required by a bound in `AssertParamIsClone` --> $SRC_DIR/core/src/clone.rs:LL:COL - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Foo` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/issues/issue-27592.stderr b/tests/ui/issues/issue-27592.stderr index 1205a8b656b11..c8649d82d7451 100644 --- a/tests/ui/issues/issue-27592.stderr +++ b/tests/ui/issues/issue-27592.stderr @@ -6,16 +6,12 @@ LL | write(|| format_args!("{}", String::from("Hello world"))); | | | | | temporary value created here | returns a value referencing data owned by the current function - | - = note: this error originates in the macro `format_args` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0515]: cannot return reference to temporary value --> $DIR/issue-27592.rs:16:14 | LL | write(|| format_args!("{}", String::from("Hello world"))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a reference to data owned by the current function - | - = note: this error originates in the macro `format_args` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-27942.rs b/tests/ui/issues/issue-27942.rs index 34b34de387976..1c2e6456937bb 100644 --- a/tests/ui/issues/issue-27942.rs +++ b/tests/ui/issues/issue-27942.rs @@ -1,12 +1,14 @@ +//@ dont-require-annotations: NOTE + pub trait Resources<'a> {} pub trait Buffer<'a, R: Resources<'a>> { fn select(&self) -> BufferViewHandle; //~^ ERROR mismatched types - //~| lifetime mismatch + //~| NOTE lifetime mismatch //~| ERROR mismatched types - //~| lifetime mismatch + //~| NOTE lifetime mismatch } pub struct BufferViewHandle<'a, R: 'a+Resources<'a>>(&'a R); diff --git a/tests/ui/issues/issue-27942.stderr b/tests/ui/issues/issue-27942.stderr index 8ea46bae26d4f..671875aa7e2ac 100644 --- a/tests/ui/issues/issue-27942.stderr +++ b/tests/ui/issues/issue-27942.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-27942.rs:5:25 + --> $DIR/issue-27942.rs:7:25 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch @@ -7,18 +7,18 @@ LL | fn select(&self) -> BufferViewHandle; = note: expected trait `Resources<'_>` found trait `Resources<'a>` note: the lifetime `'a` as defined here... - --> $DIR/issue-27942.rs:3:18 + --> $DIR/issue-27942.rs:5:18 | LL | pub trait Buffer<'a, R: Resources<'a>> { | ^^ note: ...does not necessarily outlive the anonymous lifetime defined here - --> $DIR/issue-27942.rs:5:15 + --> $DIR/issue-27942.rs:7:15 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^ error[E0308]: mismatched types - --> $DIR/issue-27942.rs:5:25 + --> $DIR/issue-27942.rs:7:25 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch @@ -26,12 +26,12 @@ LL | fn select(&self) -> BufferViewHandle; = note: expected trait `Resources<'_>` found trait `Resources<'a>` note: the anonymous lifetime defined here... - --> $DIR/issue-27942.rs:5:15 + --> $DIR/issue-27942.rs:7:15 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^ note: ...does not necessarily outlive the lifetime `'a` as defined here - --> $DIR/issue-27942.rs:3:18 + --> $DIR/issue-27942.rs:5:18 | LL | pub trait Buffer<'a, R: Resources<'a>> { | ^^ diff --git a/tests/ui/issues/issue-2823.rs b/tests/ui/issues/issue-2823.rs deleted file mode 100644 index 7b443b4152613..0000000000000 --- a/tests/ui/issues/issue-2823.rs +++ /dev/null @@ -1,14 +0,0 @@ -struct C { - x: isize, -} - -impl Drop for C { - fn drop(&mut self) { - println!("dropping: {}", self.x); - } -} - -fn main() { - let c = C{ x: 2}; - let _d = c.clone(); //~ ERROR no method named `clone` found -} diff --git a/tests/ui/issues/issue-2823.stderr b/tests/ui/issues/issue-2823.stderr deleted file mode 100644 index 5cd3f080450d6..0000000000000 --- a/tests/ui/issues/issue-2823.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0599]: no method named `clone` found for struct `C` in the current scope - --> $DIR/issue-2823.rs:13:16 - | -LL | struct C { - | -------- method `clone` not found for this struct -... -LL | let _d = c.clone(); - | ^^^^^ method not found in `C` - | - = 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: `Clone` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/issues/issue-2848.rs b/tests/ui/issues/issue-2848.rs index 34181acdd0556..8499459cec269 100644 --- a/tests/ui/issues/issue-2848.rs +++ b/tests/ui/issues/issue-2848.rs @@ -11,8 +11,8 @@ mod bar { fn main() { use bar::foo::{alpha, charlie}; match alpha { - alpha | beta => {} //~ ERROR variable `beta` is not bound in all patterns - //~^ ERROR: `beta` is named the same as one of the variants + alpha | beta => {} //~ ERROR variable `beta` is not bound in all patterns + //~^ ERROR `beta` is named the same as one of the variants charlie => {} } } diff --git a/tests/ui/issues/issue-28777.rs b/tests/ui/issues/issue-28777.rs deleted file mode 100644 index f67e11e369466..0000000000000 --- a/tests/ui/issues/issue-28777.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ run-pass -#![allow(unused_braces)] -fn main() { - let v1 = { 1 + {2} * {3} }; - let v2 = 1 + {2} * {3} ; - - assert_eq!(7, v1); - assert_eq!(7, v2); - - let v3; - v3 = { 1 + {2} * {3} }; - let v4; - v4 = 1 + {2} * {3}; - assert_eq!(7, v3); - assert_eq!(7, v4); - - let v5 = { 1 + {2} * 3 }; - assert_eq!(7, v5); - - let v9 = { 1 + if 1 > 2 {1} else {2} * {3} }; - assert_eq!(7, v9); -} diff --git a/tests/ui/issues/issue-2951.rs b/tests/ui/issues/issue-2951.rs index 1798e3e751923..e28516fa30730 100644 --- a/tests/ui/issues/issue-2951.rs +++ b/tests/ui/issues/issue-2951.rs @@ -1,10 +1,12 @@ +//@ dont-require-annotations: NOTE + fn foo(x: T, y: U) { let mut xx = x; xx = y; //~^ ERROR mismatched types - //~| expected type parameter `T`, found type parameter `U` - //~| expected type parameter `T` - //~| found type parameter `U` + //~| NOTE expected type parameter `T`, found type parameter `U` + //~| NOTE expected type parameter `T` + //~| NOTE found type parameter `U` } fn main() { diff --git a/tests/ui/issues/issue-2951.stderr b/tests/ui/issues/issue-2951.stderr index 134808b4d236b..d4b45935bf9a6 100644 --- a/tests/ui/issues/issue-2951.stderr +++ b/tests/ui/issues/issue-2951.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-2951.rs:3:10 + --> $DIR/issue-2951.rs:5:10 | LL | fn foo(x: T, y: U) { | - - found type parameter diff --git a/tests/ui/issues/issue-31011.rs b/tests/ui/issues/issue-31011.rs index 4dead04c2ca30..078d8b2d50fc7 100644 --- a/tests/ui/issues/issue-31011.rs +++ b/tests/ui/issues/issue-31011.rs @@ -1,7 +1,9 @@ +//@ dont-require-annotations: NOTE + macro_rules! log { ( $ctx:expr, $( $args:expr),* ) => { if $ctx.trace { - //~^ no field `trace` on type `&T` + //~^ ERROR no field `trace` on type `&T` println!( $( $args, )* ); } } @@ -16,7 +18,7 @@ struct Foo { fn wrap(context: &T) -> () { log!(context, "entered wrapper"); - //~^ in this expansion of log! + //~^ NOTE in this expansion of log! } fn main() { diff --git a/tests/ui/issues/issue-31011.stderr b/tests/ui/issues/issue-31011.stderr index 9785d56f4fcbc..701141ee4406c 100644 --- a/tests/ui/issues/issue-31011.stderr +++ b/tests/ui/issues/issue-31011.stderr @@ -1,5 +1,5 @@ error[E0609]: no field `trace` on type `&T` - --> $DIR/issue-31011.rs:3:17 + --> $DIR/issue-31011.rs:5:17 | LL | if $ctx.trace { | ^^^^^ unknown field diff --git a/tests/ui/issues/issue-31260.rs b/tests/ui/issues/issue-31260.rs deleted file mode 100644 index 5e9fffb195c64..0000000000000 --- a/tests/ui/issues/issue-31260.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ check-pass -#![allow(dead_code)] -pub struct Struct { - pub field: K, -} - -static STRUCT: Struct<&'static [u8]> = Struct { - field: {&[1]} -}; - -static STRUCT2: Struct<&'static [u8]> = Struct { - field: &[1] -}; - -fn main() {} diff --git a/tests/ui/issues/issue-31910.rs b/tests/ui/issues/issue-31910.rs index 19cfc4627c756..fc82fda0ccd87 100644 --- a/tests/ui/issues/issue-31910.rs +++ b/tests/ui/issues/issue-31910.rs @@ -1,8 +1,9 @@ enum Enum { - //~^ ERROR: `T` is never used + //~^ ERROR `T` is never used + //~| NOTE unused type parameter X = Trait::Number, //~^ ERROR mismatched types - //~| expected `isize`, found `i32` + //~| NOTE expected `isize`, found `i32` } trait Trait { diff --git a/tests/ui/issues/issue-31910.stderr b/tests/ui/issues/issue-31910.stderr index ca2d2f619e62e..56e4cee53d63e 100644 --- a/tests/ui/issues/issue-31910.stderr +++ b/tests/ui/issues/issue-31910.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-31910.rs:3:9 + --> $DIR/issue-31910.rs:4:9 | LL | X = Trait::Number, | ^^^^^^^^^^^^^ expected `isize`, found `i32` diff --git a/tests/ui/issues/issue-32950.rs b/tests/ui/issues/issue-32950.rs index 27d68a11c1f16..b51ac2967768a 100644 --- a/tests/ui/issues/issue-32950.rs +++ b/tests/ui/issues/issue-32950.rs @@ -1,4 +1,5 @@ #![feature(concat_idents)] +#![expect(deprecated)] // concat_idents is deprecated #[derive(Debug)] struct Baz( diff --git a/tests/ui/issues/issue-32950.stderr b/tests/ui/issues/issue-32950.stderr index f6635d982e4ec..38a82542f8964 100644 --- a/tests/ui/issues/issue-32950.stderr +++ b/tests/ui/issues/issue-32950.stderr @@ -1,16 +1,14 @@ error: `derive` cannot be used on items with type macros - --> $DIR/issue-32950.rs:5:5 + --> $DIR/issue-32950.rs:6:5 | LL | concat_idents!(Foo, Bar) | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0412]: cannot find type `FooBar` in this scope - --> $DIR/issue-32950.rs:5:5 + --> $DIR/issue-32950.rs:6:5 | LL | concat_idents!(Foo, Bar) | ^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope - | - = note: this error originates in the macro `concat_idents` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-34229.stderr b/tests/ui/issues/issue-34229.stderr index e6aa0d25934cb..2385284de0b16 100644 --- a/tests/ui/issues/issue-34229.stderr +++ b/tests/ui/issues/issue-34229.stderr @@ -7,7 +7,6 @@ LL | #[derive(PartialEq, PartialOrd)] struct Nope(Comparable); | in this derive macro expansion | = help: the trait `PartialOrd` is not implemented for `Comparable` - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Comparable` with `#[derive(PartialOrd)]` | LL | #[derive(PartialEq)] #[derive(PartialOrd)] diff --git a/tests/ui/issues/issue-3477.rs b/tests/ui/issues/issue-3477.rs index 3817d0e6a3ee9..eb94294d5a877 100644 --- a/tests/ui/issues/issue-3477.rs +++ b/tests/ui/issues/issue-3477.rs @@ -1,5 +1,6 @@ fn main() { let _p: char = 100; //~^ ERROR mismatched types - //~| expected `char`, found `u8` + //~| NOTE expected `char`, found `u8` + //~| NOTE expected due to this } diff --git a/tests/ui/issues/issue-3680.rs b/tests/ui/issues/issue-3680.rs index a0e5279812297..7fdc6e179e47a 100644 --- a/tests/ui/issues/issue-3680.rs +++ b/tests/ui/issues/issue-3680.rs @@ -1,9 +1,9 @@ fn main() { - match None { + match None { //~ NOTE this expression has type `Option<_>` Err(_) => () //~^ ERROR mismatched types - //~| expected enum `Option<_>` - //~| found enum `Result<_, _>` - //~| expected `Option<_>`, found `Result<_, _>` + //~| NOTE expected enum `Option<_>` + //~| NOTE found enum `Result<_, _>` + //~| NOTE expected `Option<_>`, found `Result<_, _>` } } diff --git a/tests/ui/issues/issue-37131.rs b/tests/ui/issues/issue-37131.rs index 3ea14672e239a..e91c76e1390ea 100644 --- a/tests/ui/issues/issue-37131.rs +++ b/tests/ui/issues/issue-37131.rs @@ -1,3 +1,6 @@ +//~ ERROR can't find crate for `std` +//~| NOTE target may not be installed +//~| NOTE can't find crate // Tests that compiling for a target which is not installed will result in a helpful // error message. @@ -5,5 +8,4 @@ //@ ignore-arm //@ needs-llvm-components: arm -//@ error-pattern:target may not be installed fn main() { } diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs index ebaf244ac9c24..05adde4520442 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs @@ -1,6 +1,4 @@ //@ build-fail -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" trait Mirror { type Image; diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr index fbbf80021bee9..945fa605e140c 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr @@ -1,15 +1,15 @@ error: reached the recursion limit while instantiating `<(&(&(..., ...), ...), ...) as Foo>::recurse` - --> $DIR/issue-37311.rs:18:9 + --> $DIR/issue-37311.rs:16:9 | LL | (self, self).recurse(); | ^^^^^^^^^^^^^^^^^^^^^^ | note: `::recurse` defined here - --> $DIR/issue-37311.rs:17:5 + --> $DIR/issue-37311.rs:15:5 | LL | fn recurse(&self) { | ^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/issue-37311.long-type.txt' error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-37884.rs b/tests/ui/issues/issue-37884.rs index ee37481b23f72..3480942d9d282 100644 --- a/tests/ui/issues/issue-37884.rs +++ b/tests/ui/issues/issue-37884.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + struct RepeatMut<'a, T>(T, &'a ()); impl<'a, T: 'a> Iterator for RepeatMut<'a, T> { @@ -5,7 +7,7 @@ impl<'a, T: 'a> Iterator for RepeatMut<'a, T> { type Item = &'a mut T; fn next(&'a mut self) -> Option //~^ ERROR method not compatible with trait - //~| lifetime mismatch + //~| NOTE lifetime mismatch { Some(&mut self.0) } diff --git a/tests/ui/issues/issue-37884.stderr b/tests/ui/issues/issue-37884.stderr index 17037d2180dcc..a7a19e3c6dfdc 100644 --- a/tests/ui/issues/issue-37884.stderr +++ b/tests/ui/issues/issue-37884.stderr @@ -1,5 +1,5 @@ error[E0308]: method not compatible with trait - --> $DIR/issue-37884.rs:6:5 + --> $DIR/issue-37884.rs:8:5 | LL | fn next(&'a mut self) -> Option | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch @@ -9,7 +9,7 @@ LL | fn next(&'a mut self) -> Option note: the anonymous lifetime as defined here... --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL note: ...does not necessarily outlive the lifetime `'a` as defined here - --> $DIR/issue-37884.rs:3:6 + --> $DIR/issue-37884.rs:5:6 | LL | impl<'a, T: 'a> Iterator for RepeatMut<'a, T> { | ^^ diff --git a/tests/ui/issues/issue-39367.rs b/tests/ui/issues/issue-39367.rs index 937d6c4b9e0b0..68b4d28aae3ba 100644 --- a/tests/ui/issues/issue-39367.rs +++ b/tests/ui/issues/issue-39367.rs @@ -20,7 +20,7 @@ fn arena() -> &'static ArenaSet> { static mut ONCE: Once = Once::new(); ONCE.call_once(|| { - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] DATA = transmute ::>>, *const ArenaSet>> (Box::new(__static_ref_initialize())); diff --git a/tests/ui/issues/issue-39367.stderr b/tests/ui/issues/issue-39367.stderr index 333c9f9634a31..df21c09983e26 100644 --- a/tests/ui/issues/issue-39367.stderr +++ b/tests/ui/issues/issue-39367.stderr @@ -1,4 +1,4 @@ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/issue-39367.rs:22:13 | LL | / ONCE.call_once(|| { diff --git a/tests/ui/issues/issue-41628.rs b/tests/ui/issues/issue-41628.rs deleted file mode 100644 index 255e4243e0119..0000000000000 --- a/tests/ui/issues/issue-41628.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ check-pass -#![deny(dead_code)] - -#[used] -static FOO: u32 = 0; - -fn main() {} diff --git a/tests/ui/issues/issue-43355.rs b/tests/ui/issues/issue-43355.rs index bf819af79625f..597472b4f48f0 100644 --- a/tests/ui/issues/issue-43355.rs +++ b/tests/ui/issues/issue-43355.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + pub trait Trait1 { type Output; } @@ -12,7 +14,7 @@ impl Trait1 for T where T: Trait2 { impl Trait1> for A { //~^ ERROR conflicting implementations of trait -//~| downstream crates may implement trait `Trait2>` for type `A` +//~| NOTE downstream crates may implement trait `Trait2>` for type `A` type Output = i32; } diff --git a/tests/ui/issues/issue-43355.stderr b/tests/ui/issues/issue-43355.stderr index 25179ef6a96c7..5a089cb3d8bbb 100644 --- a/tests/ui/issues/issue-43355.stderr +++ b/tests/ui/issues/issue-43355.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Trait1>` for type `A` - --> $DIR/issue-43355.rs:13:1 + --> $DIR/issue-43355.rs:15:1 | LL | impl Trait1 for T where T: Trait2 { | --------------------------------------------- first implementation here diff --git a/tests/ui/issues/issue-44078.stderr b/tests/ui/issues/issue-44078.stderr index 41106b29aad78..3e12de34e11ee 100644 --- a/tests/ui/issues/issue-44078.stderr +++ b/tests/ui/issues/issue-44078.stderr @@ -4,7 +4,7 @@ error[E0765]: unterminated double quote string LL | "😊""; | _________^ LL | | } - | |_^ + | |__^ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-4517.rs b/tests/ui/issues/issue-4517.rs index 469304e2cf70f..778f8f97bccb8 100644 --- a/tests/ui/issues/issue-4517.rs +++ b/tests/ui/issues/issue-4517.rs @@ -1,8 +1,10 @@ +//@ dont-require-annotations: NOTE + fn bar(int_param: usize) {} fn main() { let foo: [u8; 4] = [1; 4]; bar(foo); //~^ ERROR mismatched types - //~| expected `usize`, found `[u8; 4]` + //~| NOTE expected `usize`, found `[u8; 4]` } diff --git a/tests/ui/issues/issue-4517.stderr b/tests/ui/issues/issue-4517.stderr index 5d544ee10a909..d078c7eb95a3d 100644 --- a/tests/ui/issues/issue-4517.stderr +++ b/tests/ui/issues/issue-4517.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-4517.rs:5:9 + --> $DIR/issue-4517.rs:7:9 | LL | bar(foo); | --- ^^^ expected `usize`, found `[u8; 4]` @@ -7,7 +7,7 @@ LL | bar(foo); | arguments to this function are incorrect | note: function defined here - --> $DIR/issue-4517.rs:1:4 + --> $DIR/issue-4517.rs:3:4 | LL | fn bar(int_param: usize) {} | ^^^ ---------------- diff --git a/tests/ui/issues/issue-48131.rs b/tests/ui/issues/issue-48131.rs index 85664e62eaded..a1084ea243b26 100644 --- a/tests/ui/issues/issue-48131.rs +++ b/tests/ui/issues/issue-48131.rs @@ -1,13 +1,12 @@ // This note is annotated because the purpose of the test // is to ensure that certain other notes are not generated. -#![deny(unused_unsafe)] //~ NOTE +#![deny(unused_unsafe)] // (test that no note is generated on this unsafe fn) pub unsafe fn a() { fn inner() { unsafe { /* unnecessary */ } //~ ERROR unnecessary `unsafe` - //~^ NOTE } inner() @@ -18,7 +17,6 @@ pub fn b() { unsafe { fn inner() { unsafe { /* unnecessary */ } //~ ERROR unnecessary `unsafe` - //~^ NOTE } // `()` is fine to zero-initialize as it is zero sized and inhabited. let () = ::std::mem::zeroed(); diff --git a/tests/ui/issues/issue-48131.stderr b/tests/ui/issues/issue-48131.stderr index 5acc4f16e9fc2..fe0cd4efae891 100644 --- a/tests/ui/issues/issue-48131.stderr +++ b/tests/ui/issues/issue-48131.stderr @@ -11,7 +11,7 @@ LL | #![deny(unused_unsafe)] | ^^^^^^^^^^^^^ error: unnecessary `unsafe` block - --> $DIR/issue-48131.rs:20:13 + --> $DIR/issue-48131.rs:19:13 | LL | unsafe { /* unnecessary */ } | ^^^^^^ unnecessary `unsafe` block diff --git a/tests/ui/issues/issue-4935.rs b/tests/ui/issues/issue-4935.rs index c95020a0c006c..ef8a3eb32abbe 100644 --- a/tests/ui/issues/issue-4935.rs +++ b/tests/ui/issues/issue-4935.rs @@ -1,6 +1,7 @@ // Regression test for issue #4935 fn foo(a: usize) {} -//~^ defined here +//~^ NOTE defined here fn main() { foo(5, 6) } //~^ ERROR function takes 1 argument but 2 arguments were supplied +//~| NOTE unexpected argument #2 of type `{integer}` diff --git a/tests/ui/issues/issue-4968.rs b/tests/ui/issues/issue-4968.rs index c8df46dc26740..08539d6debd82 100644 --- a/tests/ui/issues/issue-4968.rs +++ b/tests/ui/issues/issue-4968.rs @@ -1,10 +1,12 @@ // Regression test for issue #4968 +//@ dont-require-annotations: NOTE + const A: (isize,isize) = (4,2); fn main() { match 42 { A => () } //~^ ERROR mismatched types - //~| expected type `{integer}` - //~| found tuple `(isize, isize)` - //~| expected integer, found `(isize, isize)` + //~| NOTE expected type `{integer}` + //~| NOTE found tuple `(isize, isize)` + //~| NOTE expected integer, found `(isize, isize)` } diff --git a/tests/ui/issues/issue-4968.stderr b/tests/ui/issues/issue-4968.stderr index 549e5509474de..2c1e1d7bfe5a1 100644 --- a/tests/ui/issues/issue-4968.stderr +++ b/tests/ui/issues/issue-4968.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-4968.rs:5:16 + --> $DIR/issue-4968.rs:7:16 | LL | const A: (isize,isize) = (4,2); | ---------------------- constant defined here diff --git a/tests/ui/issues/issue-50403.rs b/tests/ui/issues/issue-50403.rs index ab22aff26d99c..f14958afc34dc 100644 --- a/tests/ui/issues/issue-50403.rs +++ b/tests/ui/issues/issue-50403.rs @@ -1,4 +1,5 @@ #![feature(concat_idents)] +#![expect(deprecated)] // concat_idents is deprecated fn main() { let x = concat_idents!(); //~ ERROR `concat_idents!()` takes 1 or more arguments diff --git a/tests/ui/issues/issue-50403.stderr b/tests/ui/issues/issue-50403.stderr index 193d815d5195f..e7dd05bb0183d 100644 --- a/tests/ui/issues/issue-50403.stderr +++ b/tests/ui/issues/issue-50403.stderr @@ -1,5 +1,5 @@ error: `concat_idents!()` takes 1 or more arguments - --> $DIR/issue-50403.rs:4:13 + --> $DIR/issue-50403.rs:5:13 | LL | let x = concat_idents!(); | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-5100.rs b/tests/ui/issues/issue-5100.rs index 53ebdec816493..e9ae551bb77f4 100644 --- a/tests/ui/issues/issue-5100.rs +++ b/tests/ui/issues/issue-5100.rs @@ -1,5 +1,6 @@ -#![feature(box_patterns)] +//@ dont-require-annotations: NOTE +#![feature(box_patterns)] enum A { B, C } @@ -7,41 +8,41 @@ fn main() { match (true, false) { A::B => (), //~^ ERROR mismatched types -//~| expected `(bool, bool)`, found `A` -//~| expected tuple `(bool, bool)` -//~| found enum `A` +//~| NOTE expected `(bool, bool)`, found `A` +//~| NOTE expected tuple `(bool, bool)` +//~| NOTE found enum `A` _ => () } match (true, false) { (true, false, false) => () //~^ ERROR mismatched types -//~| expected a tuple with 2 elements, found one with 3 elements -//~| expected tuple `(bool, bool)` -//~| found tuple `(_, _, _)` +//~| NOTE expected a tuple with 2 elements, found one with 3 elements +//~| NOTE expected tuple `(bool, bool)` +//~| NOTE found tuple `(_, _, _)` } match (true, false) { (true, false, false) => () //~^ ERROR mismatched types -//~| expected a tuple with 2 elements, found one with 3 elements -//~| expected tuple `(bool, bool)` -//~| found tuple `(_, _, _)` +//~| NOTE expected a tuple with 2 elements, found one with 3 elements +//~| NOTE expected tuple `(bool, bool)` +//~| NOTE found tuple `(_, _, _)` } match (true, false) { box (true, false) => () //~^ ERROR mismatched types -//~| expected tuple `(bool, bool)` -//~| found struct `Box<_>` +//~| NOTE expected tuple `(bool, bool)` +//~| NOTE found struct `Box<_>` } match (true, false) { &(true, false) => () //~^ ERROR mismatched types -//~| expected `(bool, bool)`, found `&_` -//~| expected tuple `(bool, bool)` -//~| found reference `&_` +//~| NOTE expected `(bool, bool)`, found `&_` +//~| NOTE expected tuple `(bool, bool)` +//~| NOTE found reference `&_` } @@ -53,5 +54,5 @@ fn main() { // Make sure none of the errors above were fatal let x: char = true; //~ ERROR mismatched types - //~| expected `char`, found `bool` + //~| NOTE expected `char`, found `bool` } diff --git a/tests/ui/issues/issue-5100.stderr b/tests/ui/issues/issue-5100.stderr index b1680aacd1656..24d41a1a8afae 100644 --- a/tests/ui/issues/issue-5100.stderr +++ b/tests/ui/issues/issue-5100.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-5100.rs:8:9 + --> $DIR/issue-5100.rs:9:9 | LL | enum A { B, C } | - unit variant defined here @@ -13,7 +13,7 @@ LL | A::B => (), found enum `A` error[E0308]: mismatched types - --> $DIR/issue-5100.rs:17:9 + --> $DIR/issue-5100.rs:18:9 | LL | match (true, false) { | ------------- this expression has type `(bool, bool)` @@ -24,7 +24,7 @@ LL | (true, false, false) => () found tuple `(_, _, _)` error[E0308]: mismatched types - --> $DIR/issue-5100.rs:25:9 + --> $DIR/issue-5100.rs:26:9 | LL | match (true, false) { | ------------- this expression has type `(bool, bool)` @@ -35,7 +35,7 @@ LL | (true, false, false) => () found tuple `(_, _, _)` error[E0308]: mismatched types - --> $DIR/issue-5100.rs:33:9 + --> $DIR/issue-5100.rs:34:9 | LL | match (true, false) { | ------------- this expression has type `(bool, bool)` @@ -46,7 +46,7 @@ LL | box (true, false) => () found struct `Box<_>` error[E0308]: mismatched types - --> $DIR/issue-5100.rs:40:9 + --> $DIR/issue-5100.rs:41:9 | LL | match (true, false) { | ------------- this expression has type `(bool, bool)` @@ -57,13 +57,13 @@ LL | &(true, false) => () found reference `&_` error[E0618]: expected function, found `(char, char)` - --> $DIR/issue-5100.rs:48:14 + --> $DIR/issue-5100.rs:49:14 | LL | let v = [('a', 'b') | ^^^^^^^^^^- help: consider separating array elements with a comma: `,` error[E0308]: mismatched types - --> $DIR/issue-5100.rs:55:19 + --> $DIR/issue-5100.rs:56:19 | LL | let x: char = true; | ---- ^^^^ expected `char`, found `bool` diff --git a/tests/ui/issues/issue-51102.rs b/tests/ui/issues/issue-51102.rs index 41446cd29b0df..b5ddc7221d06a 100644 --- a/tests/ui/issues/issue-51102.rs +++ b/tests/ui/issues/issue-51102.rs @@ -11,7 +11,7 @@ fn main() { match simple { SimpleStruct { state: 0, - //~^ struct `SimpleStruct` does not have a field named `state` [E0026] + //~^ ERROR struct `SimpleStruct` does not have a field named `state` [E0026] .. } => (), } diff --git a/tests/ui/issues/issue-5358-1.rs b/tests/ui/issues/issue-5358-1.rs index 14ee962b722e8..281f219c03940 100644 --- a/tests/ui/issues/issue-5358-1.rs +++ b/tests/ui/issues/issue-5358-1.rs @@ -2,12 +2,12 @@ enum Either { Left(T), Right(U) } struct S(Either); fn main() { - match S(Either::Left(5)) { + match S(Either::Left(5)) { //~ NOTE this expression has type `S` Either::Right(_) => {} //~^ ERROR mismatched types - //~| expected `S`, found `Either<_, _>` - //~| expected struct `S` - //~| found enum `Either<_, _>` + //~| NOTE expected `S`, found `Either<_, _>` + //~| NOTE expected struct `S` + //~| NOTE found enum `Either<_, _>` _ => {} } } diff --git a/tests/ui/issues/issue-54410.stderr b/tests/ui/issues/issue-54410.stderr index 97e5990750e3c..2cd5a2a49ef5d 100644 --- a/tests/ui/issues/issue-54410.stderr +++ b/tests/ui/issues/issue-54410.stderr @@ -5,6 +5,7 @@ LL | pub static mut symbol: [i8]; | ^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[i8]` + = note: statics and constants must have a statically known size error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-67552.rs b/tests/ui/issues/issue-67552.rs index 8c7e95bd2e3e7..53f0e931d60bc 100644 --- a/tests/ui/issues/issue-67552.rs +++ b/tests/ui/issues/issue-67552.rs @@ -1,7 +1,5 @@ //@ build-fail //@ compile-flags: -Copt-level=0 -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" fn main() { rec(Empty); diff --git a/tests/ui/issues/issue-67552.stderr b/tests/ui/issues/issue-67552.stderr index f94cd78c87011..def0a29f3e51e 100644 --- a/tests/ui/issues/issue-67552.stderr +++ b/tests/ui/issues/issue-67552.stderr @@ -1,17 +1,17 @@ error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut ...>` - --> $DIR/issue-67552.rs:30:9 + --> $DIR/issue-67552.rs:28:9 | LL | rec(identity(&mut it)) | ^^^^^^^^^^^^^^^^^^^^^^ | note: `rec` defined here - --> $DIR/issue-67552.rs:23:1 + --> $DIR/issue-67552.rs:21:1 | LL | / fn rec(mut it: T) LL | | where LL | | T: Iterator, | |________________^ - = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/issue-67552.long-type.txt' error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-69130.rs b/tests/ui/issues/issue-69130.rs index 9552e8ec2a876..90dffc99db7f5 100644 --- a/tests/ui/issues/issue-69130.rs +++ b/tests/ui/issues/issue-69130.rs @@ -3,5 +3,5 @@ enum F { M (§& u8)} //~^ ERROR unknown start of token -//~| missing lifetime specifier +//~| ERROR missing lifetime specifier fn main() {} diff --git a/tests/ui/issues/issue-7061.rs b/tests/ui/issues/issue-7061.rs index 8a6ee920a3a8b..c5d5a9d9498df 100644 --- a/tests/ui/issues/issue-7061.rs +++ b/tests/ui/issues/issue-7061.rs @@ -1,10 +1,12 @@ +//@ dont-require-annotations: NOTE + struct BarStruct; impl<'a> BarStruct { fn foo(&'a mut self) -> Box { self } //~^ ERROR mismatched types - //~| expected struct `Box` - //~| found mutable reference `&'a mut BarStruct` + //~| NOTE expected struct `Box` + //~| NOTE found mutable reference `&'a mut BarStruct` } fn main() {} diff --git a/tests/ui/issues/issue-7061.stderr b/tests/ui/issues/issue-7061.stderr index 4fca2ff39fed2..b4c0ebfbdd537 100644 --- a/tests/ui/issues/issue-7061.stderr +++ b/tests/ui/issues/issue-7061.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-7061.rs:4:46 + --> $DIR/issue-7061.rs:6:46 | LL | fn foo(&'a mut self) -> Box { self } | -------------- ^^^^ expected `Box`, found `&mut BarStruct` diff --git a/tests/ui/issues/issue-7092.rs b/tests/ui/issues/issue-7092.rs index c3c96c7d3f61d..fab18bd7cf759 100644 --- a/tests/ui/issues/issue-7092.rs +++ b/tests/ui/issues/issue-7092.rs @@ -2,12 +2,12 @@ enum Whatever { } fn foo(x: Whatever) { - match x { + match x { //~ NOTE this expression has type `Whatever` Some(field) => //~^ ERROR mismatched types -//~| expected `Whatever`, found `Option<_>` -//~| expected enum `Whatever` -//~| found enum `Option<_>` +//~| NOTE expected `Whatever`, found `Option<_>` +//~| NOTE expected enum `Whatever` +//~| NOTE found enum `Option<_>` field.access(), } } diff --git a/tests/ui/issues/issue-76191.stderr b/tests/ui/issues/issue-76191.stderr index de08fc4ed0d87..ac1bdfaa847eb 100644 --- a/tests/ui/issues/issue-76191.stderr +++ b/tests/ui/issues/issue-76191.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of constant value failed | LL | const RANGE2: RangeInclusive = panic!(); | ^^^^^^^^ evaluation panicked: explicit panic - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/issue-76191.rs:14:9 diff --git a/tests/ui/issues/issue-7867.rs b/tests/ui/issues/issue-7867.rs index e9fd10c6613d4..87e7c831e6855 100644 --- a/tests/ui/issues/issue-7867.rs +++ b/tests/ui/issues/issue-7867.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + enum A { B, C } mod foo { pub fn bar() {} } @@ -6,9 +8,9 @@ fn main() { match (true, false) { A::B => (), //~^ ERROR mismatched types - //~| expected `(bool, bool)`, found `A` - //~| expected tuple `(bool, bool)` - //~| found enum `A` + //~| NOTE expected `(bool, bool)`, found `A` + //~| NOTE expected tuple `(bool, bool)` + //~| NOTE found enum `A` _ => () } } diff --git a/tests/ui/issues/issue-7867.stderr b/tests/ui/issues/issue-7867.stderr index 1a0cf5da8be50..fcb69d775face 100644 --- a/tests/ui/issues/issue-7867.stderr +++ b/tests/ui/issues/issue-7867.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-7867.rs:7:9 + --> $DIR/issue-7867.rs:9:9 | LL | enum A { B, C } | - unit variant defined here diff --git a/tests/ui/issues/issue-87199.rs b/tests/ui/issues/issue-87199.rs index 6664119e57981..4e4e35c6a71e8 100644 --- a/tests/ui/issues/issue-87199.rs +++ b/tests/ui/issues/issue-87199.rs @@ -17,5 +17,5 @@ fn ret() -> impl Iterator + ?Send { std::iter::empty() } fn main() { ref_arg::(&5); ref_arg::<[i32]>(&[5]); - //~^ the size for values of type `[i32]` cannot be known + //~^ ERROR the size for values of type `[i32]` cannot be known } diff --git a/tests/ui/issues/issue-8727.rs b/tests/ui/issues/issue-8727.rs index 7767729109eac..1883287f1407c 100644 --- a/tests/ui/issues/issue-8727.rs +++ b/tests/ui/issues/issue-8727.rs @@ -2,8 +2,6 @@ // recursions. //@ build-fail -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" fn generic() { //~ WARN function cannot return without recursing generic::>(); diff --git a/tests/ui/issues/issue-8727.stderr b/tests/ui/issues/issue-8727.stderr index 22286eb8d7be0..04158962a0101 100644 --- a/tests/ui/issues/issue-8727.stderr +++ b/tests/ui/issues/issue-8727.stderr @@ -1,5 +1,5 @@ warning: function cannot return without recursing - --> $DIR/issue-8727.rs:8:1 + --> $DIR/issue-8727.rs:6:1 | LL | fn generic() { | ^^^^^^^^^^^^^^^ cannot return without recursing @@ -10,17 +10,17 @@ LL | generic::>(); = note: `#[warn(unconditional_recursion)]` on by default error: reached the recursion limit while instantiating `generic::>>>>>` - --> $DIR/issue-8727.rs:9:5 + --> $DIR/issue-8727.rs:7:5 | LL | generic::>(); | ^^^^^^^^^^^^^^^^^^^^^^ | note: `generic` defined here - --> $DIR/issue-8727.rs:8:1 + --> $DIR/issue-8727.rs:6:1 | LL | fn generic() { | ^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/issue-8727.long-type.txt' error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/issues/issue-8761.rs b/tests/ui/issues/issue-8761.rs index 1453c3d757f7f..5883bb9669562 100644 --- a/tests/ui/issues/issue-8761.rs +++ b/tests/ui/issues/issue-8761.rs @@ -1,10 +1,10 @@ enum Foo { A = 1i64, //~^ ERROR mismatched types - //~| expected `isize`, found `i64` + //~| NOTE expected `isize`, found `i64` B = 2u8 //~^ ERROR mismatched types - //~| expected `isize`, found `u8` + //~| NOTE expected `isize`, found `u8` } fn main() {} diff --git a/tests/ui/issues/issue-9382.rs b/tests/ui/issues/issue-9382.rs deleted file mode 100644 index 27f9ab577437f..0000000000000 --- a/tests/ui/issues/issue-9382.rs +++ /dev/null @@ -1,37 +0,0 @@ -//@ run-pass -#![allow(dead_code)] - -// Tests for a previous bug that occurred due to an interaction -// between struct field initialization and the auto-coercion -// from a vector to a slice. The drop glue was being invoked on -// the temporary slice with a wrong type, triggering an LLVM assert. - - -struct Thing1<'a> { - baz: &'a [Box], - bar: Box, -} - -struct Thing2<'a> { - baz: &'a [Box], - bar: u64, -} - -pub fn main() { - let _t1_fixed = Thing1 { - baz: &[], - bar: Box::new(32), - }; - Thing1 { - baz: &Vec::new(), - bar: Box::new(32), - }; - let _t2_fixed = Thing2 { - baz: &[], - bar: 32, - }; - Thing2 { - baz: &Vec::new(), - bar: 32, - }; -} diff --git a/tests/ui/issues/issue-9719.rs b/tests/ui/issues/issue-9719.rs deleted file mode 100644 index 904768c934144..0000000000000 --- a/tests/ui/issues/issue-9719.rs +++ /dev/null @@ -1,40 +0,0 @@ -//@ build-pass -#![allow(dead_code)] - -mod a { - pub enum Enum { - A(T), - } - - pub trait X { - fn dummy(&self) { } - } - impl X for isize {} - - pub struct Z<'a>(Enum<&'a (dyn X + 'a)>); - fn foo() { let x: isize = 42; let z = Z(Enum::A(&x as &dyn X)); let _ = z; } -} - -mod b { - trait X { - fn dummy(&self) { } - } - impl X for isize {} - struct Y<'a>{ - x:Option<&'a (dyn X + 'a)>, - } - - fn bar() { - let x: isize = 42; - let _y = Y { x: Some(&x as &dyn X) }; - } -} - -mod c { - pub trait X { fn f(&self); } - impl X for isize { fn f(&self) {} } - pub struct Z<'a>(Option<&'a (dyn X + 'a)>); - fn main() { let x: isize = 42; let z = Z(Some(&x as &dyn X)); let _ = z; } -} - -pub fn main() {} diff --git a/tests/ui/iterators/float_iterator_hint.rs b/tests/ui/iterators/float_iterator_hint.rs index a3335ca41f77e..9e7a10ca497e0 100644 --- a/tests/ui/iterators/float_iterator_hint.rs +++ b/tests/ui/iterators/float_iterator_hint.rs @@ -3,7 +3,7 @@ fn main() { for i in 0.2 { //~^ ERROR `{float}` is not an iterator - //~| `{float}` is not an iterator + //~| NOTE `{float}` is not an iterator //~| 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/tests/ui/json/json-bom-plus-crlf-multifile.rs b/tests/ui/json/json-bom-plus-crlf-multifile.rs index 79c4b2430e1d1..074a2583e8276 100644 --- a/tests/ui/json/json-bom-plus-crlf-multifile.rs +++ b/tests/ui/json/json-bom-plus-crlf-multifile.rs @@ -11,3 +11,8 @@ mod json_bom_plus_crlf_multifile_aux; fn main() { json_bom_plus_crlf_multifile_aux::test(); } + +//~? ERROR mismatched types +//~? ERROR mismatched types +//~? ERROR mismatched types +//~? ERROR mismatched types diff --git a/tests/ui/json/json-bom-plus-crlf.rs b/tests/ui/json/json-bom-plus-crlf.rs index 9e250c256ebe1..900a92f368298 100644 --- a/tests/ui/json/json-bom-plus-crlf.rs +++ b/tests/ui/json/json-bom-plus-crlf.rs @@ -16,13 +16,17 @@ fn main() { let s : String = 1; // Error in the middle of line. + //~^ ERROR mismatched types let s : String = 1 ; // Error before the newline. + //~^^ ERROR mismatched types let s : String = 1; // Error after the newline. + //~^ ERROR mismatched types let s : String = ( ); // Error spanning the newline. + //~^^ ERROR mismatched types } diff --git a/tests/ui/json/json-bom-plus-crlf.stderr b/tests/ui/json/json-bom-plus-crlf.stderr index fee70431bdfbf..59f0e8178f0e3 100644 --- a/tests/ui/json/json-bom-plus-crlf.stderr +++ b/tests/ui/json/json-bom-plus-crlf.stderr @@ -52,7 +52,7 @@ This error occurs when an expression was used in a place where the compiler expected an expression of a different type. It can occur in several cases, the most common being when calling a function and passing an argument which has a different type than the matching type in the function declaration. -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":732,"byte_end":733,"line_start":20,"line_end":20,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":723,"byte_end":729,"line_start":20,"line_end":20,"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":733,"byte_end":733,"line_start":20,"line_end":20,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:20:22: error[E0308]: mismatched types: expected `String`, found integer +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":765,"byte_end":766,"line_start":21,"line_end":21,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":756,"byte_end":762,"line_start":21,"line_end":21,"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":766,"byte_end":766,"line_start":21,"line_end":21,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:21:22: error[E0308]: mismatched types: expected `String`, found integer "} {"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -80,7 +80,7 @@ This error occurs when an expression was used in a place where the compiler expected an expression of a different type. It can occur in several cases, the most common being when calling a function and passing an argument which has a different type than the matching type in the function declaration. -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":796,"byte_end":797,"line_start":24,"line_end":24,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":786,"byte_end":792,"line_start":23,"line_end":23,"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":797,"byte_end":797,"line_start":24,"line_end":24,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:24:1: error[E0308]: mismatched types: expected `String`, found integer +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":863,"byte_end":864,"line_start":26,"line_end":26,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":853,"byte_end":859,"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":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":864,"byte_end":864,"line_start":26,"line_end":26,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:26:1: error[E0308]: mismatched types: expected `String`, found integer "} {"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. @@ -108,7 +108,7 @@ This error occurs when an expression was used in a place where the compiler expected an expression of a different type. It can occur in several cases, the most common being when calling a function and passing an argument which has a different type than the matching type in the function declaration. -"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":852,"byte_end":860,"line_start":26,"line_end":27,"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 `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":843,"byte_end":849,"line_start":26,"line_end":26,"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:26:22: error[E0308]: mismatched types: expected `String`, found `()` +"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":952,"byte_end":960,"line_start":29,"line_end":30,"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 `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":943,"byte_end":949,"line_start":29,"line_end":29,"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:29:22: error[E0308]: mismatched types: expected `String`, found `()` "} {"$message_type":"diagnostic","message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors "} diff --git a/tests/ui/json/json-multiple.stderr b/tests/ui/json/json-multiple.stderr index 7689fb94a6c8d..e285817cffb93 100644 --- a/tests/ui/json/json-multiple.stderr +++ b/tests/ui/json/json-multiple.stderr @@ -1 +1 @@ -{"$message_type":"artifact","artifact":"$TEST_BUILD_DIR/json/json-multiple/libjson_multiple.rlib","emit":"link"} +{"$message_type":"artifact","artifact":"$TEST_BUILD_DIR/libjson_multiple.rlib","emit":"link"} diff --git a/tests/ui/json/json-options.stderr b/tests/ui/json/json-options.stderr index 668fc18097f0d..fff4ec9a6df14 100644 --- a/tests/ui/json/json-options.stderr +++ b/tests/ui/json/json-options.stderr @@ -1 +1 @@ -{"$message_type":"artifact","artifact":"$TEST_BUILD_DIR/json/json-options/libjson_options.rlib","emit":"link"} +{"$message_type":"artifact","artifact":"$TEST_BUILD_DIR/libjson_options.rlib","emit":"link"} diff --git a/tests/ui/json/json-short.rs b/tests/ui/json/json-short.rs index 1b8f0b463668e..30b0bce9efa3d 100644 --- a/tests/ui/json/json-short.rs +++ b/tests/ui/json/json-short.rs @@ -1 +1,2 @@ +//~v ERROR `main` function not found in crate `json_short` //@ compile-flags: --json=diagnostic-short --error-format=json diff --git a/tests/ui/json/json-short.stderr b/tests/ui/json/json-short.stderr index 8a4a55edf68e9..e8aa7e5d0873c 100644 --- a/tests/ui/json/json-short.stderr +++ b/tests/ui/json/json-short.stderr @@ -13,7 +13,7 @@ If you don't know the basics of Rust, you can look at the [Rust Book][rust-book] to get started. [rust-book]: https://doc.rust-lang.org/book/ -"},"level":"error","spans":[{"file_name":"$DIR/json-short.rs","byte_start":63,"byte_end":63,"line_start":1,"line_end":1,"column_start":64,"column_end":64,"is_primary":true,"text":[{"text":"//@ compile-flags: --json=diagnostic-short --error-format=json","highlight_start":64,"highlight_end":64}],"label":"consider adding a `main` function to `$DIR/json-short.rs`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-short.rs:1:64: error[E0601]: `main` function not found in crate `json_short`: consider adding a `main` function to `$DIR/json-short.rs` +"},"level":"error","spans":[{"file_name":"$DIR/json-short.rs","byte_start":122,"byte_end":122,"line_start":2,"line_end":2,"column_start":64,"column_end":64,"is_primary":true,"text":[{"text":"//@ compile-flags: --json=diagnostic-short --error-format=json","highlight_start":64,"highlight_end":64}],"label":"consider adding a `main` function to `$DIR/json-short.rs`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-short.rs:2:64: error[E0601]: `main` function not found in crate `json_short`: consider adding a `main` function to `$DIR/json-short.rs` "} {"$message_type":"diagnostic","message":"aborting due to 1 previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 1 previous error "} diff --git a/tests/ui/label/continue-pointing-to-block-ice-113379.rs b/tests/ui/label/continue-pointing-to-block-ice-113379.rs new file mode 100644 index 0000000000000..8a6a9cc840986 --- /dev/null +++ b/tests/ui/label/continue-pointing-to-block-ice-113379.rs @@ -0,0 +1,12 @@ +//! Regression test for ICE #113379. Liveness linting assumes that `continue`s all point to loops. +//! This tests that if a `continue` points to a block, we don't run liveness lints. + +async fn f999() -> Vec { + //~^ ERROR `async fn` is not permitted in Rust 2015 + 'b: { + //~^ ERROR mismatched types + continue 'b; + //~^ ERROR `continue` pointing to a labeled block + } +} +//~^ ERROR `main` function not found diff --git a/tests/ui/label/continue-pointing-to-block-ice-113379.stderr b/tests/ui/label/continue-pointing-to-block-ice-113379.stderr new file mode 100644 index 0000000000000..ada6305ec999b --- /dev/null +++ b/tests/ui/label/continue-pointing-to-block-ice-113379.stderr @@ -0,0 +1,43 @@ +error[E0670]: `async fn` is not permitted in Rust 2015 + --> $DIR/continue-pointing-to-block-ice-113379.rs:4:1 + | +LL | async fn f999() -> Vec { + | ^^^^^ to use `async fn`, switch to Rust 2018 or later + | + = help: pass `--edition 2024` to `rustc` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0601]: `main` function not found in crate `continue_pointing_to_block_ice_113379` + --> $DIR/continue-pointing-to-block-ice-113379.rs:11:2 + | +LL | } + | ^ consider adding a `main` function to `$DIR/continue-pointing-to-block-ice-113379.rs` + +error[E0696]: `continue` pointing to a labeled block + --> $DIR/continue-pointing-to-block-ice-113379.rs:8:9 + | +LL | / 'b: { +LL | | +LL | | continue 'b; + | | ^^^^^^^^^^^ labeled blocks cannot be `continue`'d +LL | | +LL | | } + | |_____- labeled block the `continue` points to + +error[E0308]: mismatched types + --> $DIR/continue-pointing-to-block-ice-113379.rs:6:5 + | +LL | / 'b: { +LL | | +LL | | continue 'b; +LL | | +LL | | } + | |_____^ expected `Vec`, found `()` + | + = note: expected struct `Vec` + found unit type `()` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0308, E0601, E0670, E0696. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/label/continue-pointing-to-block-ice-121623.rs b/tests/ui/label/continue-pointing-to-block-ice-121623.rs new file mode 100644 index 0000000000000..7047f7a309aaf --- /dev/null +++ b/tests/ui/label/continue-pointing-to-block-ice-121623.rs @@ -0,0 +1,11 @@ +//! Regression test for ICE #121623. Liveness linting assumes that `continue`s all point to loops. +//! This tests that if a `continue` points to a block, we don't run liveness lints. + +fn main() { + match () { + _ => 'b: { + continue 'b; + //~^ ERROR `continue` pointing to a labeled block + } + } +} diff --git a/tests/ui/label/continue-pointing-to-block-ice-121623.stderr b/tests/ui/label/continue-pointing-to-block-ice-121623.stderr new file mode 100644 index 0000000000000..a484fb629d181 --- /dev/null +++ b/tests/ui/label/continue-pointing-to-block-ice-121623.stderr @@ -0,0 +1,14 @@ +error[E0696]: `continue` pointing to a labeled block + --> $DIR/continue-pointing-to-block-ice-121623.rs:7:13 + | +LL | _ => 'b: { + | ______________- +LL | | continue 'b; + | | ^^^^^^^^^^^ labeled blocks cannot be `continue`'d +LL | | +LL | | } + | |_________- labeled block the `continue` points to + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0696`. diff --git a/tests/ui/label/label_break_value_desugared_break.rs b/tests/ui/label/label_break_value_desugared_break.rs index b7e7fd47c27f3..17ef1ba6d24bc 100644 --- a/tests/ui/label/label_break_value_desugared_break.rs +++ b/tests/ui/label/label_break_value_desugared_break.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] //@ run-pass diff --git a/tests/ui/lang-items/lang-item-generic-requirements.rs b/tests/ui/lang-items/lang-item-generic-requirements.rs index 0f982df61e8e7..90ed5f3f0efd0 100644 --- a/tests/ui/lang-items/lang-item-generic-requirements.rs +++ b/tests/ui/lang-items/lang-item-generic-requirements.rs @@ -59,3 +59,5 @@ fn ice() { // use `start` fn main() {} + +//~? ERROR requires `copy` lang_item diff --git a/tests/ui/lang-items/lang-item-missing.rs b/tests/ui/lang-items/lang-item-missing.rs index 5b832a5bb8f01..75e203d04f29d 100644 --- a/tests/ui/lang-items/lang-item-missing.rs +++ b/tests/ui/lang-items/lang-item-missing.rs @@ -1,13 +1,11 @@ // Test that a missing lang item (in this case `sized`) does not cause an ICE, // see #17392. -//@ error-pattern: requires `sized` lang_item - #![feature(lang_items, no_core)] #![no_core] #![no_main] #[no_mangle] -extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { //~ ERROR requires `sized` lang_item loop {} } diff --git a/tests/ui/lang-items/lang-item-missing.stderr b/tests/ui/lang-items/lang-item-missing.stderr index 63bca95adf700..85e9eaafe72c4 100644 --- a/tests/ui/lang-items/lang-item-missing.stderr +++ b/tests/ui/lang-items/lang-item-missing.stderr @@ -1,5 +1,5 @@ error: requires `sized` lang_item - --> $DIR/lang-item-missing.rs:11:60 + --> $DIR/lang-item-missing.rs:9:60 | LL | extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { | ^^^ diff --git a/tests/ui/lang-items/missing-copy-lang-item-issue-19660.rs b/tests/ui/lang-items/missing-copy-lang-item-issue-19660.rs index 9b634ee8ee3e4..f92a00e602cff 100644 --- a/tests/ui/lang-items/missing-copy-lang-item-issue-19660.rs +++ b/tests/ui/lang-items/missing-copy-lang-item-issue-19660.rs @@ -1,5 +1,3 @@ -//@ error-pattern: requires `copy` lang_item - #![feature(lang_items, no_core)] #![no_core] #![no_main] @@ -11,5 +9,5 @@ struct S; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { - argc + argc //~ ERROR requires `copy` lang_item } diff --git a/tests/ui/lang-items/missing-copy-lang-item-issue-19660.stderr b/tests/ui/lang-items/missing-copy-lang-item-issue-19660.stderr index 3dc7716ecd2a6..9b25b1db292f5 100644 --- a/tests/ui/lang-items/missing-copy-lang-item-issue-19660.stderr +++ b/tests/ui/lang-items/missing-copy-lang-item-issue-19660.stderr @@ -1,5 +1,5 @@ error: requires `copy` lang_item - --> $DIR/missing-copy-lang-item-issue-19660.rs:14:5 + --> $DIR/missing-copy-lang-item-issue-19660.rs:12:5 | LL | argc | ^^^^ diff --git a/tests/ui/layout/debug.rs b/tests/ui/layout/debug.rs index b87a1d2031df4..90e3c58dad719 100644 --- a/tests/ui/layout/debug.rs +++ b/tests/ui/layout/debug.rs @@ -18,6 +18,7 @@ type Test = Result; //~ ERROR: layout_of #[rustc_layout(debug)] type T = impl std::fmt::Debug; //~ ERROR: layout_of +#[define_opaque(T)] fn f() -> T { 0i32 } diff --git a/tests/ui/layout/debug.stderr b/tests/ui/layout/debug.stderr index 07cad7766920f..abaa16cdefacd 100644 --- a/tests/ui/layout/debug.stderr +++ b/tests/ui/layout/debug.stderr @@ -1,5 +1,5 @@ error: unions cannot have zero fields - --> $DIR/debug.rs:83:1 + --> $DIR/debug.rs:84:1 | LL | union EmptyUnion {} | ^^^^^^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ error: layout_of(E) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -49,7 +49,7 @@ error: layout_of(E) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -71,7 +71,7 @@ error: layout_of(E) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -112,7 +112,7 @@ error: layout_of(S) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -160,7 +160,7 @@ error: layout_of(U) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Union( @@ -186,7 +186,7 @@ error: layout_of(Result) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -238,7 +238,7 @@ error: layout_of(Result) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -277,7 +277,7 @@ error: layout_of(Result) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -327,7 +327,7 @@ error: layout_of(i32) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -357,7 +357,7 @@ error: layout_of(V) = Layout { abi: Align(2 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Union( @@ -372,7 +372,7 @@ error: layout_of(V) = Layout { unadjusted_abi_align: Align(2 bytes), randomization_seed: $SEED, } - --> $DIR/debug.rs:26:1 + --> $DIR/debug.rs:27:1 | LL | pub union V { | ^^^^^^^^^^^ @@ -383,7 +383,7 @@ error: layout_of(W) = Layout { abi: Align(2 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Union( @@ -398,7 +398,7 @@ error: layout_of(W) = Layout { unadjusted_abi_align: Align(2 bytes), randomization_seed: $SEED, } - --> $DIR/debug.rs:32:1 + --> $DIR/debug.rs:33:1 | LL | pub union W { | ^^^^^^^^^^^ @@ -409,7 +409,7 @@ error: layout_of(Y) = Layout { abi: Align(2 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Union( @@ -424,7 +424,7 @@ error: layout_of(Y) = Layout { unadjusted_abi_align: Align(2 bytes), randomization_seed: $SEED, } - --> $DIR/debug.rs:38:1 + --> $DIR/debug.rs:39:1 | LL | pub union Y { | ^^^^^^^^^^^ @@ -435,7 +435,7 @@ error: layout_of(P1) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Union( @@ -450,7 +450,7 @@ error: layout_of(P1) = Layout { unadjusted_abi_align: Align(1 bytes), randomization_seed: $SEED, } - --> $DIR/debug.rs:45:1 + --> $DIR/debug.rs:46:1 | LL | union P1 { x: u32 } | ^^^^^^^^ @@ -461,7 +461,7 @@ error: layout_of(P2) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Union( @@ -476,7 +476,7 @@ error: layout_of(P2) = Layout { unadjusted_abi_align: Align(1 bytes), randomization_seed: $SEED, } - --> $DIR/debug.rs:49:1 + --> $DIR/debug.rs:50:1 | LL | union P2 { x: (u32, u32) } | ^^^^^^^^ @@ -487,7 +487,7 @@ error: layout_of(P3) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Union( @@ -502,7 +502,7 @@ error: layout_of(P3) = Layout { unadjusted_abi_align: Align(1 bytes), randomization_seed: $SEED, } - --> $DIR/debug.rs:57:1 + --> $DIR/debug.rs:58:1 | LL | union P3 { x: F32x4 } | ^^^^^^^^ @@ -513,7 +513,7 @@ error: layout_of(P4) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Union( @@ -528,7 +528,7 @@ error: layout_of(P4) = Layout { unadjusted_abi_align: Align(1 bytes), randomization_seed: $SEED, } - --> $DIR/debug.rs:61:1 + --> $DIR/debug.rs:62:1 | LL | union P4 { x: E } | ^^^^^^^^ @@ -539,7 +539,7 @@ error: layout_of(P5) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Union { value: Int( I8, @@ -559,7 +559,7 @@ error: layout_of(P5) = Layout { unadjusted_abi_align: Align(1 bytes), randomization_seed: $SEED, } - --> $DIR/debug.rs:65:1 + --> $DIR/debug.rs:66:1 | LL | union P5 { zst: [u16; 0], byte: u8 } | ^^^^^^^^ @@ -570,7 +570,7 @@ error: layout_of(MaybeUninit) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Union { value: Int( I8, @@ -590,19 +590,19 @@ error: layout_of(MaybeUninit) = Layout { unadjusted_abi_align: Align(1 bytes), randomization_seed: $SEED, } - --> $DIR/debug.rs:68:1 + --> $DIR/debug.rs:69:1 | LL | type X = std::mem::MaybeUninit; | ^^^^^^ error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases - --> $DIR/debug.rs:71:1 + --> $DIR/debug.rs:72:1 | LL | const C: () = (); | ^^^^^^^^^^^ error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/debug.rs:79:19 + --> $DIR/debug.rs:80:19 | LL | type Impossible = (str, str); | ^^^^^^^^^^ doesn't have a size known at compile-time @@ -611,19 +611,19 @@ LL | type Impossible = (str, str); = note: only the last element of a tuple may have a dynamically sized type error: the type has an unknown layout - --> $DIR/debug.rs:83:1 + --> $DIR/debug.rs:84:1 | LL | union EmptyUnion {} | ^^^^^^^^^^^^^^^^ error: the type `T` does not have a fixed layout - --> $DIR/debug.rs:89:1 + --> $DIR/debug.rs:90:1 | LL | type TooGeneric = T; | ^^^^^^^^^^^^^^^^^^ error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases - --> $DIR/debug.rs:75:5 + --> $DIR/debug.rs:76:5 | LL | const C: () = (); | ^^^^^^^^^^^ diff --git a/tests/ui/layout/hexagon-enum.stderr b/tests/ui/layout/hexagon-enum.stderr index b802b400b18b8..9c3a8662d4f08 100644 --- a/tests/ui/layout/hexagon-enum.stderr +++ b/tests/ui/layout/hexagon-enum.stderr @@ -4,7 +4,7 @@ error: layout_of(A) = Layout { abi: Align(1 bytes), pref: Align(1 bytes), }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -49,7 +49,7 @@ error: layout_of(A) = Layout { abi: Align(1 bytes), pref: Align(1 bytes), }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -82,7 +82,7 @@ error: layout_of(B) = Layout { abi: Align(1 bytes), pref: Align(1 bytes), }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -127,7 +127,7 @@ error: layout_of(B) = Layout { abi: Align(1 bytes), pref: Align(1 bytes), }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -160,7 +160,7 @@ error: layout_of(C) = Layout { abi: Align(2 bytes), pref: Align(2 bytes), }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I16, @@ -205,7 +205,7 @@ error: layout_of(C) = Layout { abi: Align(2 bytes), pref: Align(2 bytes), }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -238,7 +238,7 @@ error: layout_of(P) = Layout { abi: Align(4 bytes), pref: Align(4 bytes), }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -283,7 +283,7 @@ error: layout_of(P) = Layout { abi: Align(4 bytes), pref: Align(4 bytes), }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -316,7 +316,7 @@ error: layout_of(T) = Layout { abi: Align(4 bytes), pref: Align(4 bytes), }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -361,7 +361,7 @@ error: layout_of(T) = Layout { abi: Align(4 bytes), pref: Align(4 bytes), }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/layout/issue-84108.stderr b/tests/ui/layout/issue-84108.stderr index 8ddce285e23f8..e296abfc3b53b 100644 --- a/tests/ui/layout/issue-84108.stderr +++ b/tests/ui/layout/issue-84108.stderr @@ -29,6 +29,15 @@ LL | const BAR: (&Path, [u8], usize) = ("hello", [], 42); = help: the trait `Sized` is not implemented for `[u8]` = note: only the last element of a tuple may have a dynamically sized type +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/issue-84108.rs:15:13 + | +LL | static BAZ: ([u8], usize) = ([], 0); + | ^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: only the last element of a tuple may have a dynamically sized type + error[E0277]: the size for values of type `[u8]` cannot be known at compilation time --> $DIR/issue-84108.rs:9:12 | @@ -48,15 +57,6 @@ LL | const BAR: (&Path, [u8], usize) = ("hello", [], 42); = note: expected slice `[u8]` found array `[_; 0]` -error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/issue-84108.rs:15:13 - | -LL | static BAZ: ([u8], usize) = ([], 0); - | ^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `[u8]` - = note: only the last element of a tuple may have a dynamically sized type - error[E0277]: the size for values of type `[u8]` cannot be known at compilation time --> $DIR/issue-84108.rs:15:13 | diff --git a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr index 3bdb9c5c143e4..ef7f0cd2d1c34 100644 --- a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr +++ b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr @@ -4,7 +4,7 @@ error: layout_of(MissingPayloadField) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -55,7 +55,7 @@ error: layout_of(MissingPayloadField) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -93,7 +93,7 @@ error: layout_of(MissingPayloadField) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -126,7 +126,7 @@ error: layout_of(CommonPayloadField) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -178,7 +178,7 @@ error: layout_of(CommonPayloadField) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -217,7 +217,7 @@ error: layout_of(CommonPayloadField) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -267,7 +267,7 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -318,7 +318,7 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -356,7 +356,7 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -405,7 +405,7 @@ error: layout_of(NicheFirst) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -460,7 +460,7 @@ error: layout_of(NicheFirst) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -510,7 +510,7 @@ error: layout_of(NicheFirst) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -532,7 +532,7 @@ error: layout_of(NicheFirst) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -565,7 +565,7 @@ error: layout_of(NicheSecond) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -620,7 +620,7 @@ error: layout_of(NicheSecond) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -670,7 +670,7 @@ error: layout_of(NicheSecond) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -692,7 +692,7 @@ error: layout_of(NicheSecond) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/layout/issue-96185-overaligned-enum.stderr b/tests/ui/layout/issue-96185-overaligned-enum.stderr index 1d4e443644826..a9081afc50944 100644 --- a/tests/ui/layout/issue-96185-overaligned-enum.stderr +++ b/tests/ui/layout/issue-96185-overaligned-enum.stderr @@ -4,7 +4,7 @@ error: layout_of(Aligned1) = Layout { abi: Align(8 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -43,7 +43,7 @@ error: layout_of(Aligned1) = Layout { abi: Align(8 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -67,7 +67,7 @@ error: layout_of(Aligned1) = Layout { abi: Align(8 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -104,7 +104,7 @@ error: layout_of(Aligned2) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -149,7 +149,7 @@ error: layout_of(Aligned2) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -173,7 +173,7 @@ error: layout_of(Aligned2) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/layout/post-mono-layout-cycle-2.rs b/tests/ui/layout/post-mono-layout-cycle-2.rs index 2daac12d7ac93..c8a4a222cc68a 100644 --- a/tests/ui/layout/post-mono-layout-cycle-2.rs +++ b/tests/ui/layout/post-mono-layout-cycle-2.rs @@ -1,4 +1,4 @@ -//@ build-fail +//@ check-fail //@ edition: 2021 use std::future::Future; diff --git a/tests/ui/layout/post-mono-layout-cycle-2.stderr b/tests/ui/layout/post-mono-layout-cycle-2.stderr index d8c51deffe3b9..f04e01071d7a9 100644 --- a/tests/ui/layout/post-mono-layout-cycle-2.stderr +++ b/tests/ui/layout/post-mono-layout-cycle-2.stderr @@ -12,12 +12,6 @@ LL | Blah::iter(self, iterator).await | = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future -note: the above error was encountered while instantiating `fn Wrap::<()>::ice` - --> $DIR/post-mono-layout-cycle-2.rs:54:9 - | -LL | t.ice(); - | ^^^^^^^ - error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0733`. diff --git a/tests/ui/layout/post-mono-layout-cycle.rs b/tests/ui/layout/post-mono-layout-cycle.rs index 6753c01267ecd..841fc30a50bcf 100644 --- a/tests/ui/layout/post-mono-layout-cycle.rs +++ b/tests/ui/layout/post-mono-layout-cycle.rs @@ -1,5 +1,5 @@ //@ build-fail -//~^ cycle detected when computing layout of `Wrapper<()>` +//~^ ERROR cycle detected when computing layout of `Wrapper<()>` trait Trait { type Assoc; diff --git a/tests/ui/layout/reprc-power-alignment.rs b/tests/ui/layout/reprc-power-alignment.rs index f6c1df55988ba..f144094d43fbc 100644 --- a/tests/ui/layout/reprc-power-alignment.rs +++ b/tests/ui/layout/reprc-power-alignment.rs @@ -148,5 +148,29 @@ pub struct I { d: f32, e: f64, } - +#[repr(C)] +pub struct J { + a: u8, + b: I, +} +// The lint also ignores diagnosing #[repr(align(n))]. +#[repr(C, align(8))] +pub struct K { + a: u8, + b: u8, + c: f64, + d: f32, + e: f64, +} +#[repr(C)] +pub struct L { + a: u8, + b: K, +} +#[repr(C, align(8))] +pub struct M { + a: u8, + b: K, + c: L, +} fn main() { } diff --git a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.rs b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.rs index 6380449124ffb..3b297a9a66293 100644 --- a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.rs +++ b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.rs @@ -30,7 +30,8 @@ where type Output = B; extern "rust-call" fn call_once(mut self, a: A) -> Self::Output { //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument - self.call_mut(a) + //~| ERROR type parameter to bare `FnOnce` trait must be a tuple + self.call_mut(a) //~^ ERROR `A` is not a tuple } } @@ -43,6 +44,7 @@ where { extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output { //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument + //~| ERROR type parameter to bare `FnOnce` trait must be a tuple self.cache.get(&a).map(|a| a.clone()).unwrap_or_else(|| { let b = (self.fun)(self, a.clone()); self.cache.insert(a, b.clone()); diff --git a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr index 3b051ef9a8823..32a564e466be1 100644 --- a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr +++ b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr @@ -11,8 +11,21 @@ help: consider further restricting type parameter `A` with unstable trait `Tuple LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ +error[E0059]: type parameter to bare `FnOnce` trait must be a tuple + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:31:5 + | +LL | extern "rust-call" fn call_once(mut self, a: A) -> Self::Output { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` + | +note: required by a bound in `FnOnce` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL +help: consider further restricting type parameter `A` with unstable trait `Tuple` + | +LL | A: Eq + Hash + Clone + std::marker::Tuple, + | ++++++++++++++++++++ + error[E0059]: type parameter to bare `FnMut` trait must be a tuple - --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:38:12 + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:39:12 | LL | impl FnMut for CachedFun | ^^^^^^^^ the trait `Tuple` is not implemented for `A` @@ -24,6 +37,19 @@ help: consider further restricting type parameter `A` with unstable trait `Tuple LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ +error[E0059]: type parameter to bare `FnOnce` trait must be a tuple + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:45:5 + | +LL | extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` + | +note: required by a bound in `FnOnce` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL +help: consider further restricting type parameter `A` with unstable trait `Tuple` + | +LL | A: Eq + Hash + Clone + std::marker::Tuple, + | ++++++++++++++++++++ + error[E0277]: functions with the "rust-call" ABI must take a single non-self tuple argument --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:31:5 | @@ -36,7 +62,7 @@ LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ error[E0277]: functions with the "rust-call" ABI must take a single non-self tuple argument - --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:44:5 + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:45:5 | LL | extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` @@ -47,12 +73,12 @@ LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ error[E0277]: `A` is not a tuple - --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:33:23 + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:34:19 | -LL | self.call_mut(a) - | -------- ^ the trait `Tuple` is not implemented for `A` - | | - | required by a bound introduced by this call +LL | self.call_mut(a) + | -------- ^ the trait `Tuple` is not implemented for `A` + | | + | required by a bound introduced by this call | note: required by a bound in `call_mut` --> $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -62,7 +88,7 @@ LL | A: Eq + Hash + Clone + std::marker::Tuple, | ++++++++++++++++++++ error[E0277]: `i32` is not a tuple - --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:57:26 + --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:59:26 | LL | cachedcoso.call_once(1); | --------- ^ the trait `Tuple` is not implemented for `i32` @@ -76,7 +102,7 @@ help: use a unary tuple instead LL | cachedcoso.call_once((1,)); | + ++ -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors Some errors have detailed explanations: E0059, E0277. For more information about an error, try `rustc --explain E0059`. diff --git a/tests/ui/layout/size-of-val-raw-too-big.rs b/tests/ui/layout/size-of-val-raw-too-big.rs index dfca6d6eb764f..566b01255fae1 100644 --- a/tests/ui/layout/size-of-val-raw-too-big.rs +++ b/tests/ui/layout/size-of-val-raw-too-big.rs @@ -1,7 +1,8 @@ +//~ ERROR values of the type `Example` are too big for the target architecture //@ build-fail //@ compile-flags: --crate-type lib //@ only-32bit Layout computation rejects this layout for different reasons on 64-bit. -//@ error-pattern: too big for the target architecture + #![feature(core_intrinsics)] #![allow(internal_features)] diff --git a/tests/ui/layout/thumb-enum.stderr b/tests/ui/layout/thumb-enum.stderr index 07153fee02736..b635d1a45bb7a 100644 --- a/tests/ui/layout/thumb-enum.stderr +++ b/tests/ui/layout/thumb-enum.stderr @@ -4,7 +4,7 @@ error: layout_of(A) = Layout { abi: Align(1 bytes), pref: Align(4 bytes), }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -49,7 +49,7 @@ error: layout_of(A) = Layout { abi: Align(1 bytes), pref: Align(4 bytes), }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -82,7 +82,7 @@ error: layout_of(B) = Layout { abi: Align(1 bytes), pref: Align(4 bytes), }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -127,7 +127,7 @@ error: layout_of(B) = Layout { abi: Align(1 bytes), pref: Align(4 bytes), }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -160,7 +160,7 @@ error: layout_of(C) = Layout { abi: Align(2 bytes), pref: Align(4 bytes), }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I16, @@ -205,7 +205,7 @@ error: layout_of(C) = Layout { abi: Align(2 bytes), pref: Align(4 bytes), }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -238,7 +238,7 @@ error: layout_of(P) = Layout { abi: Align(4 bytes), pref: Align(4 bytes), }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -283,7 +283,7 @@ error: layout_of(P) = Layout { abi: Align(4 bytes), pref: Align(4 bytes), }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -316,7 +316,7 @@ error: layout_of(T) = Layout { abi: Align(4 bytes), pref: Align(4 bytes), }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -361,7 +361,7 @@ error: layout_of(T) = Layout { abi: Align(4 bytes), pref: Align(4 bytes), }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/layout/unknown-when-no-type-parameter.rs b/tests/ui/layout/unknown-when-no-type-parameter.rs index 94c32cf262f2a..87f90aa438ab3 100644 --- a/tests/ui/layout/unknown-when-no-type-parameter.rs +++ b/tests/ui/layout/unknown-when-no-type-parameter.rs @@ -1,14 +1,13 @@ #![feature(trivial_bounds)] -//@ error-pattern: error[E0080]: evaluation of constant value failed -//@ error-pattern: the type `<() as Project>::Assoc` has an unknown layout - trait Project { type Assoc; } fn foo() where (): Project { - [(); size_of::<<() as Project>::Assoc>()]; + [(); size_of::<<() as Project>::Assoc>()]; //~ ERROR evaluation of constant value failed + //~| NOTE the type `<() as Project>::Assoc` has an unknown layout + //~| NOTE inside `std::mem::size_of::<<() as Project>::Assoc>` } fn main() {} diff --git a/tests/ui/layout/unknown-when-no-type-parameter.stderr b/tests/ui/layout/unknown-when-no-type-parameter.stderr index 9e414d224bbc0..35fbb11f156ab 100644 --- a/tests/ui/layout/unknown-when-no-type-parameter.stderr +++ b/tests/ui/layout/unknown-when-no-type-parameter.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation of constant value failed - --> $DIR/unknown-when-no-type-parameter.rs:11:10 + --> $DIR/unknown-when-no-type-parameter.rs:8:10 | LL | [(); size_of::<<() as Project>::Assoc>()]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `<() as Project>::Assoc` has an unknown layout diff --git a/tests/ui/layout/zero-sized-array-enum-niche.stderr b/tests/ui/layout/zero-sized-array-enum-niche.stderr index 33d2eede22090..1ba184bdacefb 100644 --- a/tests/ui/layout/zero-sized-array-enum-niche.stderr +++ b/tests/ui/layout/zero-sized-array-enum-niche.stderr @@ -4,7 +4,7 @@ error: layout_of(Result<[u32; 0], bool>) = Layout { abi: Align(4 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -43,7 +43,7 @@ error: layout_of(Result<[u32; 0], bool>) = Layout { abi: Align(4 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -69,7 +69,7 @@ error: layout_of(Result<[u32; 0], bool>) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -115,7 +115,7 @@ error: layout_of(MultipleAlignments) = Layout { abi: Align(4 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -154,7 +154,7 @@ error: layout_of(MultipleAlignments) = Layout { abi: Align(2 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -180,7 +180,7 @@ error: layout_of(MultipleAlignments) = Layout { abi: Align(4 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -206,7 +206,7 @@ error: layout_of(MultipleAlignments) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -252,7 +252,7 @@ error: layout_of(Result<[u32; 0], Packed>>) = Layout { abi: Align(4 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -291,7 +291,7 @@ error: layout_of(Result<[u32; 0], Packed>>) = Layout { abi: Align(4 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -317,7 +317,7 @@ error: layout_of(Result<[u32; 0], Packed>>) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -363,7 +363,7 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { abi: Align(4 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -406,7 +406,7 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { abi: Align(4 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -432,7 +432,7 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { abi: Align(1 bytes), pref: $PREF_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/lazy-and-or.rs b/tests/ui/lazy-and-or.rs deleted file mode 100644 index f9dbeb68959a3..0000000000000 --- a/tests/ui/lazy-and-or.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ run-pass - -fn incr(x: &mut isize) -> bool { *x += 1; assert!((false)); return false; } - -pub fn main() { - let x = 1 == 2 || 3 == 3; - assert!((x)); - let mut y: isize = 10; - println!("{}", x || incr(&mut y)); - assert_eq!(y, 10); - if true && x { assert!((true)); } else { assert!((false)); } -} diff --git a/tests/ui/lazy-type-alias-impl-trait/branches.rs b/tests/ui/lazy-type-alias-impl-trait/branches.rs index 95239e2e341c8..30f9c08a27fd0 100644 --- a/tests/ui/lazy-type-alias-impl-trait/branches.rs +++ b/tests/ui/lazy-type-alias-impl-trait/branches.rs @@ -2,16 +2,14 @@ type Foo = impl std::fmt::Debug; +#[define_opaque(Foo)] fn foo(b: bool) -> Foo { - if b { - vec![42_i32] - } else { - std::iter::empty().collect() - } + if b { vec![42_i32] } else { std::iter::empty().collect() } } type Bar = impl std::fmt::Debug; +#[define_opaque(Bar)] fn bar(b: bool) -> Bar { let x: Bar = if b { vec![42_i32] diff --git a/tests/ui/lazy-type-alias-impl-trait/branches.stderr b/tests/ui/lazy-type-alias-impl-trait/branches.stderr index 9e93762277518..0cc727bc3ded4 100644 --- a/tests/ui/lazy-type-alias-impl-trait/branches.stderr +++ b/tests/ui/lazy-type-alias-impl-trait/branches.stderr @@ -1,5 +1,5 @@ error[E0277]: a value of type `Bar` cannot be built from an iterator over elements of type `_` - --> $DIR/branches.rs:19:28 + --> $DIR/branches.rs:17:28 | LL | std::iter::empty().collect() | ^^^^^^^ value of type `Bar` cannot be built from `std::iter::Iterator` diff --git a/tests/ui/lazy-type-alias-impl-trait/branches2.rs b/tests/ui/lazy-type-alias-impl-trait/branches2.rs index 467400f1c2445..35b76845c85b5 100644 --- a/tests/ui/lazy-type-alias-impl-trait/branches2.rs +++ b/tests/ui/lazy-type-alias-impl-trait/branches2.rs @@ -4,20 +4,13 @@ type Foo = impl std::iter::FromIterator + PartialEq> + std::fmt::Debug; +#[define_opaque(Foo)] fn foo(b: bool) -> Foo { - if b { - vec![42_i32] - } else { - std::iter::empty().collect() - } + if b { vec![42_i32] } else { std::iter::empty().collect() } } fn bar(b: bool) -> impl PartialEq> + std::fmt::Debug { - if b { - vec![42_i32] - } else { - std::iter::empty().collect() - } + if b { vec![42_i32] } else { std::iter::empty().collect() } } fn main() { diff --git a/tests/ui/lazy-type-alias-impl-trait/branches3.rs b/tests/ui/lazy-type-alias-impl-trait/branches3.rs index 30c0af8a5dc97..cff7a0b437f6b 100644 --- a/tests/ui/lazy-type-alias-impl-trait/branches3.rs +++ b/tests/ui/lazy-type-alias-impl-trait/branches3.rs @@ -3,6 +3,7 @@ type Foo = impl for<'a> FnOnce(&'a str) -> usize; type Bar = impl FnOnce(&'static str) -> usize; +#[define_opaque(Foo)] fn foo() -> Foo { if true { |s| s.len() //~ ERROR type annotations needed @@ -10,6 +11,8 @@ fn foo() -> Foo { panic!() } } + +#[define_opaque(Bar)] fn bar() -> Bar { if true { |s| s.len() //~ ERROR type annotations needed diff --git a/tests/ui/lazy-type-alias-impl-trait/branches3.stderr b/tests/ui/lazy-type-alias-impl-trait/branches3.stderr index fe2631f947420..117d189867bd7 100644 --- a/tests/ui/lazy-type-alias-impl-trait/branches3.stderr +++ b/tests/ui/lazy-type-alias-impl-trait/branches3.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/branches3.rs:8:10 + --> $DIR/branches3.rs:9:10 | LL | |s| s.len() | ^ - type must be known at this point @@ -10,7 +10,7 @@ LL | |s: /* Type */| s.len() | ++++++++++++ error[E0282]: type annotations needed - --> $DIR/branches3.rs:15:10 + --> $DIR/branches3.rs:18:10 | LL | |s| s.len() | ^ - type must be known at this point @@ -21,7 +21,7 @@ LL | |s: /* Type */| s.len() | ++++++++++++ error[E0282]: type annotations needed - --> $DIR/branches3.rs:23:10 + --> $DIR/branches3.rs:26:10 | LL | |s| s.len() | ^ - type must be known at this point @@ -32,7 +32,7 @@ LL | |s: /* Type */| s.len() | ++++++++++++ error[E0282]: type annotations needed - --> $DIR/branches3.rs:30:10 + --> $DIR/branches3.rs:33:10 | LL | |s| s.len() | ^ - type must be known at this point diff --git a/tests/ui/lazy-type-alias-impl-trait/recursion.rs b/tests/ui/lazy-type-alias-impl-trait/recursion.rs index 5193356059904..33dbf8d0280b3 100644 --- a/tests/ui/lazy-type-alias-impl-trait/recursion.rs +++ b/tests/ui/lazy-type-alias-impl-trait/recursion.rs @@ -4,9 +4,10 @@ type Foo = impl std::fmt::Debug; +#[define_opaque(Foo)] fn foo(b: bool) -> Foo { if b { - return 42 + return 42; } let x: u32 = foo(false); 99 @@ -14,7 +15,7 @@ fn foo(b: bool) -> Foo { fn bar(b: bool) -> impl std::fmt::Debug { if b { - return 42 + return 42; } let x: u32 = bar(false); 99 diff --git a/tests/ui/lazy-type-alias-impl-trait/recursion2.rs b/tests/ui/lazy-type-alias-impl-trait/recursion2.rs index e14da32e1166e..b28a95c53f481 100644 --- a/tests/ui/lazy-type-alias-impl-trait/recursion2.rs +++ b/tests/ui/lazy-type-alias-impl-trait/recursion2.rs @@ -4,6 +4,7 @@ type Foo = impl std::fmt::Debug; +#[define_opaque(Foo)] fn foo(b: bool) -> Foo { if b { return vec![]; @@ -14,7 +15,7 @@ fn foo(b: bool) -> Foo { fn bar(b: bool) -> impl std::fmt::Debug { if b { - return vec![] + return vec![]; } let x: Vec = bar(false); std::iter::empty().collect() diff --git a/tests/ui/lazy-type-alias-impl-trait/recursion3.rs b/tests/ui/lazy-type-alias-impl-trait/recursion3.rs index 7f1cedae068f0..0b15484f7ef53 100644 --- a/tests/ui/lazy-type-alias-impl-trait/recursion3.rs +++ b/tests/ui/lazy-type-alias-impl-trait/recursion3.rs @@ -2,9 +2,10 @@ type Foo = impl std::fmt::Debug; +#[define_opaque(Foo)] fn foo(b: bool) -> Foo { if b { - return 42 + return 42; } let x: u32 = foo(false) + 42; //~ ERROR cannot add 99 @@ -12,7 +13,7 @@ fn foo(b: bool) -> Foo { fn bar(b: bool) -> impl std::fmt::Debug { if b { - return 42 + return 42; } let x: u32 = bar(false) + 42; //~ ERROR cannot add 99 diff --git a/tests/ui/lazy-type-alias-impl-trait/recursion3.stderr b/tests/ui/lazy-type-alias-impl-trait/recursion3.stderr index e1d5cafedc808..0cbedfb69f85a 100644 --- a/tests/ui/lazy-type-alias-impl-trait/recursion3.stderr +++ b/tests/ui/lazy-type-alias-impl-trait/recursion3.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot add `{integer}` to `Foo` - --> $DIR/recursion3.rs:9:29 + --> $DIR/recursion3.rs:10:29 | LL | let x: u32 = foo(false) + 42; | ---------- ^ -- {integer} @@ -7,7 +7,7 @@ LL | let x: u32 = foo(false) + 42; | Foo error[E0369]: cannot add `{integer}` to `impl Debug` - --> $DIR/recursion3.rs:17:29 + --> $DIR/recursion3.rs:18:29 | LL | let x: u32 = bar(false) + 42; | ---------- ^ -- {integer} diff --git a/tests/ui/lazy-type-alias-impl-trait/recursion4.rs b/tests/ui/lazy-type-alias-impl-trait/recursion4.rs index 57dd7fb067c6d..892e72e233588 100644 --- a/tests/ui/lazy-type-alias-impl-trait/recursion4.rs +++ b/tests/ui/lazy-type-alias-impl-trait/recursion4.rs @@ -2,6 +2,7 @@ type Foo = impl std::fmt::Debug; +#[define_opaque(Foo)] fn foo(b: bool) -> Foo { if b { return vec![]; diff --git a/tests/ui/lazy-type-alias-impl-trait/recursion4.stderr b/tests/ui/lazy-type-alias-impl-trait/recursion4.stderr index d8ac39a4f27a3..b04bf6b99877a 100644 --- a/tests/ui/lazy-type-alias-impl-trait/recursion4.stderr +++ b/tests/ui/lazy-type-alias-impl-trait/recursion4.stderr @@ -1,5 +1,5 @@ error[E0277]: a value of type `Foo` cannot be built from an iterator over elements of type `_` - --> $DIR/recursion4.rs:10:28 + --> $DIR/recursion4.rs:11:28 | LL | x = std::iter::empty().collect(); | ^^^^^^^ value of type `Foo` cannot be built from `std::iter::Iterator` @@ -9,7 +9,7 @@ note: required by a bound in `collect` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0277]: a value of type `impl Debug` cannot be built from an iterator over elements of type `_` - --> $DIR/recursion4.rs:19:28 + --> $DIR/recursion4.rs:20:28 | LL | x = std::iter::empty().collect(); | ^^^^^^^ value of type `impl Debug` cannot be built from `std::iter::Iterator` diff --git a/tests/ui/lazy-type-alias/extern-crate-has-eager-type-aliases.rs b/tests/ui/lazy-type-alias/extern-crate-has-eager-type-aliases.rs index efd7763919840..3384698776056 100644 --- a/tests/ui/lazy-type-alias/extern-crate-has-eager-type-aliases.rs +++ b/tests/ui/lazy-type-alias/extern-crate-has-eager-type-aliases.rs @@ -9,9 +9,9 @@ #![allow(incomplete_features)] // This used to crash when we were computing the variances of `Struct` since we would convert -// `eager::Alias` to a weak projection due to the presence of `#![feature(lazy_type_alias)]` in +// `eager::Alias` to a weak alias due to the presence of `#![feature(lazy_type_alias)]` in // this (!) crate and subsequently attempt to obtain the variances of the type alias associated with -// the weak projection which would panic because we don't compute this information for eager type +// the weak alias which would panic because we don't compute this information for eager type // aliases at all. struct Struct(eager::Alias); diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.current.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.current.stderr index 05f5449dbc8be..85ac98f40501b 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.current.stderr +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.current.stderr @@ -7,7 +7,7 @@ LL | type Loop = Loop; = note: in case this is a recursive type alias, consider using a struct, enum, or union instead error[E0275]: overflow normalizing the type alias `Loop` - --> $DIR/inherent-impls-overflow.rs:10:1 + --> $DIR/inherent-impls-overflow.rs:12:1 | LL | impl Loop {} | ^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | impl Loop {} = note: in case this is a recursive type alias, consider using a struct, enum, or union instead error[E0275]: overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>` - --> $DIR/inherent-impls-overflow.rs:14:17 + --> $DIR/inherent-impls-overflow.rs:17:17 | LL | type Poly0 = Poly1<(T,)>; | ^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | type Poly0 = Poly1<(T,)>; = note: in case this is a recursive type alias, consider using a struct, enum, or union instead error[E0275]: overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` - --> $DIR/inherent-impls-overflow.rs:17:17 + --> $DIR/inherent-impls-overflow.rs:21:17 | LL | type Poly1 = Poly0<(T,)>; | ^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | type Poly1 = Poly0<(T,)>; = note: in case this is a recursive type alias, consider using a struct, enum, or union instead error[E0275]: overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` - --> $DIR/inherent-impls-overflow.rs:21:1 + --> $DIR/inherent-impls-overflow.rs:26:1 | LL | impl Poly0<()> {} | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr index 192b5eebdaac8..e94f29de44f0f 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr @@ -1,11 +1,31 @@ -error[E0275]: overflow evaluating the requirement `Loop == _` - --> $DIR/inherent-impls-overflow.rs:10:6 +error[E0271]: type mismatch resolving `Loop normalizes-to _` + --> $DIR/inherent-impls-overflow.rs:8:13 + | +LL | type Loop = Loop; + | ^^^^ types differ + +error[E0271]: type mismatch resolving `Loop normalizes-to _` + --> $DIR/inherent-impls-overflow.rs:12:1 + | +LL | impl Loop {} + | ^^^^^^^^^^^^ types differ + +error[E0271]: type mismatch resolving `Loop normalizes-to _` + --> $DIR/inherent-impls-overflow.rs:12:6 | LL | impl Loop {} - | ^^^^ + | ^^^^ types differ + +error[E0275]: overflow evaluating the requirement `Poly1<(T,)> == _` + --> $DIR/inherent-impls-overflow.rs:17:17 + | +LL | type Poly0 = Poly1<(T,)>; + | ^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_impls_overflow`) error: type parameter `T` is only used recursively - --> $DIR/inherent-impls-overflow.rs:14:24 + --> $DIR/inherent-impls-overflow.rs:17:24 | LL | type Poly0 = Poly1<(T,)>; | - ^ @@ -15,8 +35,16 @@ LL | type Poly0 = Poly1<(T,)>; = help: consider removing `T` or referring to it in the body of the type alias = note: all type parameters must be used in a non-recursive way in order to constrain their variance +error[E0275]: overflow evaluating the requirement `Poly0<(T,)> == _` + --> $DIR/inherent-impls-overflow.rs:21:17 + | +LL | type Poly1 = Poly0<(T,)>; + | ^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_impls_overflow`) + error: type parameter `T` is only used recursively - --> $DIR/inherent-impls-overflow.rs:17:24 + --> $DIR/inherent-impls-overflow.rs:21:24 | LL | type Poly1 = Poly0<(T,)>; | - ^ @@ -27,13 +55,22 @@ LL | type Poly1 = Poly0<(T,)>; = note: all type parameters must be used in a non-recursive way in order to constrain their variance error[E0275]: overflow evaluating the requirement `Poly0<()> == _` - --> $DIR/inherent-impls-overflow.rs:21:6 + --> $DIR/inherent-impls-overflow.rs:26:1 + | +LL | impl Poly0<()> {} + | ^^^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_impls_overflow`) + +error[E0275]: overflow evaluating the requirement `Poly0<()> == _` + --> $DIR/inherent-impls-overflow.rs:26:6 | LL | impl Poly0<()> {} | ^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_impls_overflow`) -error: aborting due to 4 previous errors +error: aborting due to 9 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/tests/ui/lazy-type-alias/inherent-impls-overflow.rs b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs index 1397695a3fe41..b4a347cb098ca 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs @@ -5,21 +5,27 @@ #![feature(lazy_type_alias)] #![allow(incomplete_features)] -type Loop = Loop; //[current]~ ERROR overflow normalizing the type alias `Loop` +type Loop = Loop; +//[current]~^ ERROR overflow normalizing the type alias `Loop` +//[next]~^^ ERROR type mismatch resolving `Loop normalizes-to _` impl Loop {} //[current]~^ ERROR overflow normalizing the type alias `Loop` -//[next]~^^ ERROR overflow evaluating the requirement `Loop == _` +//[next]~^^ ERROR type mismatch resolving `Loop normalizes-to _` +//[next]~| ERROR type mismatch resolving `Loop normalizes-to _` type Poly0 = Poly1<(T,)>; //[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>` //[next]~^^ ERROR type parameter `T` is only used recursively +//[next]~| ERROR overflow evaluating the requirement type Poly1 = Poly0<(T,)>; //[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` //[next]~^^ ERROR type parameter `T` is only used recursively +//[next]~| ERROR overflow evaluating the requirement impl Poly0<()> {} //[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` //[next]~^^ ERROR overflow evaluating the requirement `Poly0<()> == _` +//[next]~| ERROR overflow evaluating the requirement fn main() {} diff --git a/tests/ui/let-else/issue-102317.rs b/tests/ui/let-else/issue-102317.rs index d94410e10a8de..5afcacfc3aad5 100644 --- a/tests/ui/let-else/issue-102317.rs +++ b/tests/ui/let-else/issue-102317.rs @@ -1,6 +1,7 @@ // issue #102317 //@ build-pass -//@ compile-flags: --edition 2021 -C opt-level=3 -Zvalidate-mir +//@ compile-flags: -C opt-level=3 -Zvalidate-mir +//@ edition: 2021 struct SegmentJob; diff --git a/tests/ui/lexer/error-stage.rs b/tests/ui/lexer/error-stage.rs index c8d88f745a1f0..f0ccb886d0d28 100644 --- a/tests/ui/lexer/error-stage.rs +++ b/tests/ui/lexer/error-stage.rs @@ -59,7 +59,7 @@ const _: () = sink! { // The invalid literals used to cause errors, but this was changed by #102944. // Except for `0b010.0f32`, because it's a lexer error. -#[cfg(FALSE)] +#[cfg(false)] fn configured_out() { "string"any_suffix; // OK 10u123; // OK diff --git a/tests/ui/lexer/unterminated-nested-comment.stderr b/tests/ui/lexer/unterminated-nested-comment.stderr index 78b72ce1fe4a0..9117b689c94e3 100644 --- a/tests/ui/lexer/unterminated-nested-comment.stderr +++ b/tests/ui/lexer/unterminated-nested-comment.stderr @@ -12,7 +12,7 @@ LL | | /* | | | | | ...as last nested comment starts here, maybe you want to close this instead? LL | | */ - | |_-^ + | |_--^ | | | ...and last nested comment terminates here. diff --git a/tests/ui/lifetimes/conflicting-bounds.rs b/tests/ui/lifetimes/conflicting-bounds.rs index f37f163dbb639..62240792afd76 100644 --- a/tests/ui/lifetimes/conflicting-bounds.rs +++ b/tests/ui/lifetimes/conflicting-bounds.rs @@ -1,4 +1,4 @@ -//~ type annotations needed: cannot satisfy `Self: Gen<'source>` +//~ ERROR type annotations needed: cannot satisfy `Self: Gen<'source>` pub trait Gen<'source> { type Output; diff --git a/tests/ui/lifetimes/issue-26638.rs b/tests/ui/lifetimes/issue-26638.rs index 11c730165f228..e73cf21bd6cfe 100644 --- a/tests/ui/lifetimes/issue-26638.rs +++ b/tests/ui/lifetimes/issue-26638.rs @@ -3,7 +3,6 @@ fn parse_type(iter: Box+'static>) -> &str { iter.next() fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } //~^ ERROR missing lifetime specifier [E0106] -//~| ERROR mismatched types //~| ERROR function takes 1 argument but 0 arguments were supplied fn parse_type_3() -> &str { unimplemented!() } diff --git a/tests/ui/lifetimes/issue-26638.stderr b/tests/ui/lifetimes/issue-26638.stderr index bdf911367653b..74a1676f68481 100644 --- a/tests/ui/lifetimes/issue-26638.stderr +++ b/tests/ui/lifetimes/issue-26638.stderr @@ -32,7 +32,7 @@ LL + fn parse_type_2(iter: fn(&u8)->&u8) -> String { iter() } | error[E0106]: missing lifetime specifier - --> $DIR/issue-26638.rs:9:22 + --> $DIR/issue-26638.rs:8:22 | LL | fn parse_type_3() -> &str { unimplemented!() } | ^ expected named lifetime parameter @@ -59,18 +59,7 @@ help: provide the argument LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter(/* &u8 */) } | +++++++++ -error[E0308]: mismatched types - --> $DIR/issue-26638.rs:4:47 - | -LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } - | ---- ^^^^^^ expected `&str`, found `&u8` - | | - | expected `&'static str` because of return type - | - = note: expected reference `&'static str` - found reference `&u8` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0061, E0106, E0308. +Some errors have detailed explanations: E0061, E0106. For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/lifetimes/issue-83737-binders-across-types.rs b/tests/ui/lifetimes/issue-83737-binders-across-types.rs index d20c84dae3f79..6f39938129444 100644 --- a/tests/ui/lifetimes/issue-83737-binders-across-types.rs +++ b/tests/ui/lifetimes/issue-83737-binders-across-types.rs @@ -1,5 +1,5 @@ //@ build-pass -//@ compile-flags: --edition 2018 +//@ edition: 2018 //@ compile-flags: --crate-type rlib use std::future::Future; diff --git a/tests/ui/lifetimes/issue-83737-erasing-bound-vars.rs b/tests/ui/lifetimes/issue-83737-erasing-bound-vars.rs index 466bcdc6be0fc..dcc8c7e94ba64 100644 --- a/tests/ui/lifetimes/issue-83737-erasing-bound-vars.rs +++ b/tests/ui/lifetimes/issue-83737-erasing-bound-vars.rs @@ -1,5 +1,5 @@ //@ build-pass -//@ compile-flags: --edition 2018 +//@ edition: 2018 //@ compile-flags: --crate-type rlib use std::future::Future; diff --git a/tests/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs b/tests/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs index 78069e682c132..4aa212bb4bd33 100644 --- a/tests/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs +++ b/tests/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs @@ -3,6 +3,6 @@ struct Foo {} impl Foo { fn bar(foo: Foo) {} - //~^ associated item constraints are not allowed here + //~^ ERROR associated item constraints are not allowed here } fn main() {} diff --git a/tests/ui/lifetimes/issue-83907-invalid-fn-like-path.rs b/tests/ui/lifetimes/issue-83907-invalid-fn-like-path.rs index 4e093bb2e17dc..4f4e2cda9469a 100644 --- a/tests/ui/lifetimes/issue-83907-invalid-fn-like-path.rs +++ b/tests/ui/lifetimes/issue-83907-invalid-fn-like-path.rs @@ -1,7 +1,7 @@ //@ check-fail static STATIC_VAR_FIVE: &One(); -//~^ cannot find type -//~| free static item without body +//~^ ERROR cannot find type +//~| ERROR free static item without body fn main() {} diff --git a/tests/ui/lifetimes/no_lending_iterators.rs b/tests/ui/lifetimes/no_lending_iterators.rs index 21395475fb3dc..b3e8ad08ba18b 100644 --- a/tests/ui/lifetimes/no_lending_iterators.rs +++ b/tests/ui/lifetimes/no_lending_iterators.rs @@ -25,7 +25,7 @@ impl Bar for usize { impl Bar for isize { type Item<'a> = &'a isize; - //~^ ERROR 27:14: 27:18: lifetime parameters or bounds on type `Item` do not match the trait declaration [E0195] + //~^ ERROR 27:14: 27:18: lifetime parameters or bounds on associated type `Item` do not match the trait declaration [E0195] fn poke(&mut self, item: Self::Item) { self += *item; diff --git a/tests/ui/lifetimes/no_lending_iterators.stderr b/tests/ui/lifetimes/no_lending_iterators.stderr index 9ceaef2f9b1aa..340ef93588515 100644 --- a/tests/ui/lifetimes/no_lending_iterators.stderr +++ b/tests/ui/lifetimes/no_lending_iterators.stderr @@ -16,14 +16,14 @@ error: in the trait associated type is declared without lifetime parameters, so LL | type Item = &usize; | ^ this lifetime must come from the implemented type -error[E0195]: lifetime parameters or bounds on type `Item` do not match the trait declaration +error[E0195]: lifetime parameters or bounds on associated type `Item` do not match the trait declaration --> $DIR/no_lending_iterators.rs:27:14 | LL | type Item; - | - lifetimes in impl do not match this type in trait + | - lifetimes in impl do not match this associated type in trait ... LL | type Item<'a> = &'a isize; - | ^^^^ lifetimes do not match type in trait + | ^^^^ lifetimes do not match associated type in trait error: aborting due to 3 previous errors diff --git a/tests/ui/lifetimes/static-typos.rs b/tests/ui/lifetimes/static-typos.rs new file mode 100644 index 0000000000000..941d1b376bce9 --- /dev/null +++ b/tests/ui/lifetimes/static-typos.rs @@ -0,0 +1,7 @@ +fn stati() {} +//~^ ERROR use of undeclared lifetime name `'stati` + +fn statoc() {} +//~^ ERROR use of undeclared lifetime name `'statoc` + +fn main() {} diff --git a/tests/ui/lifetimes/static-typos.stderr b/tests/ui/lifetimes/static-typos.stderr new file mode 100644 index 0000000000000..a817fa89c7e54 --- /dev/null +++ b/tests/ui/lifetimes/static-typos.stderr @@ -0,0 +1,26 @@ +error[E0261]: use of undeclared lifetime name `'stati` + --> $DIR/static-typos.rs:1:13 + | +LL | fn stati() {} + | ^^^^^^ undeclared lifetime + | +help: you may have misspelled the `'static` lifetime + | +LL | fn stati() {} + | + + +error[E0261]: use of undeclared lifetime name `'statoc` + --> $DIR/static-typos.rs:4:14 + | +LL | fn statoc() {} + | ^^^^^^^ undeclared lifetime + | +help: you may have misspelled the `'static` lifetime + | +LL - fn statoc() {} +LL + fn statoc() {} + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0261`. diff --git a/tests/ui/limits/huge-static.rs b/tests/ui/limits/huge-static.rs index 4709b46e59d15..4c3641ac7d4b1 100644 --- a/tests/ui/limits/huge-static.rs +++ b/tests/ui/limits/huge-static.rs @@ -18,9 +18,9 @@ impl TooBigArray { static MY_TOO_BIG_ARRAY_1: TooBigArray = TooBigArray::new(); //~^ ERROR could not evaluate static initializer -//~| too big +//~| NOTE too big static MY_TOO_BIG_ARRAY_2: [u8; HUGE_SIZE] = [0x00; HUGE_SIZE]; //~^ ERROR could not evaluate static initializer -//~| too big +//~| NOTE too big fn main() { } diff --git a/tests/ui/limits/issue-17913.rs b/tests/ui/limits/issue-17913.rs index 5407902daba59..85deab4bc4c74 100644 --- a/tests/ui/limits/issue-17913.rs +++ b/tests/ui/limits/issue-17913.rs @@ -1,6 +1,5 @@ //@ build-fail //@ normalize-stderr: "\[&usize; \d+\]" -> "[&usize; usize::MAX]" -//@ error-pattern: too big for the target architecture #[cfg(target_pointer_width = "64")] fn main() { @@ -15,3 +14,5 @@ fn main() { let a: Box<_> = Box::new([&n; 0xFFFFFFFF_usize]); println!("{}", a[0xFFFFFF_usize]); } + +//~? ERROR are too big for the target architecture diff --git a/tests/ui/limits/issue-55878.rs b/tests/ui/limits/issue-55878.rs index db4a2724452ad..614ff328bf5ed 100644 --- a/tests/ui/limits/issue-55878.rs +++ b/tests/ui/limits/issue-55878.rs @@ -1,6 +1,6 @@ //@ build-fail -//@ error-pattern: are too big for the target architecture fn main() { println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>()); + //~^ ERROR evaluation of constant value failed } diff --git a/tests/ui/limits/issue-55878.stderr b/tests/ui/limits/issue-55878.stderr index d705b3daf79e3..9b3922d7933dd 100644 --- a/tests/ui/limits/issue-55878.stderr +++ b/tests/ui/limits/issue-55878.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation of constant value failed - --> $DIR/issue-55878.rs:5:26 + --> $DIR/issue-55878.rs:4:26 | LL | println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ values of the type `[u8; usize::MAX]` are too big for the target architecture diff --git a/tests/ui/link-native-libs/empty-kind-1.rs b/tests/ui/link-native-libs/empty-kind-1.rs index d9b8d8a7f7d6a..04e7e3c2c7b3e 100644 --- a/tests/ui/link-native-libs/empty-kind-1.rs +++ b/tests/ui/link-native-libs/empty-kind-1.rs @@ -1,6 +1,7 @@ // Unspecified kind should fail with an error //@ compile-flags: -l =mylib -//@ error-pattern: unknown library kind ``, expected one of: static, dylib, framework, link-arg fn main() {} + +//~? ERROR unknown library kind `` diff --git a/tests/ui/link-native-libs/empty-kind-2.rs b/tests/ui/link-native-libs/empty-kind-2.rs index 16cb3b917e4bf..1436c086dfdc5 100644 --- a/tests/ui/link-native-libs/empty-kind-2.rs +++ b/tests/ui/link-native-libs/empty-kind-2.rs @@ -1,6 +1,7 @@ // Unspecified kind should fail with an error //@ compile-flags: -l :+bundle=mylib -//@ error-pattern: unknown library kind ``, expected one of: static, dylib, framework, link-arg fn main() {} + +//~? ERROR unknown library kind `` diff --git a/tests/ui/link-native-libs/lib-defaults.rs b/tests/ui/link-native-libs/lib-defaults.rs new file mode 100644 index 0000000000000..4e38adb643dbf --- /dev/null +++ b/tests/ui/link-native-libs/lib-defaults.rs @@ -0,0 +1,17 @@ +//! By default, `-l NAME` without an explicit kind will default to dylib. However, if there's also +//! an `#[link(name = NAME, kind = KIND)]` attribute with an explicit `KIND`, it should override the +//! CLI flag. In particular, this should not result in any duplicate flag warnings from the linker. + +//@ run-pass +//@ compile-flags: -lrust_test_helpers + +#[link(name = "rust_test_helpers", kind = "static")] +extern "C" { + pub fn rust_dbg_extern_identity_u32(x: u32) -> u32; +} + +fn main() { + unsafe { + rust_dbg_extern_identity_u32(42); + } +} diff --git a/tests/ui/link-native-libs/link-arg-error.rs b/tests/ui/link-native-libs/link-arg-error.rs index 4defb108178b7..9d9dcc27ccb8a 100644 --- a/tests/ui/link-native-libs/link-arg-error.rs +++ b/tests/ui/link-native-libs/link-arg-error.rs @@ -1,4 +1,5 @@ //@ compile-flags: -l link-arg:+bundle=arg -Z unstable-options -//@ error-pattern: linking modifier `bundle` is only compatible with `static` linking kind fn main() {} + +//~? ERROR linking modifier `bundle` is only compatible with `static` linking kind diff --git a/tests/ui/link-native-libs/link-attr-validation-late.rs b/tests/ui/link-native-libs/link-attr-validation-late.rs index 34f720dd2d3c5..4eeb8ba4884ff 100644 --- a/tests/ui/link-native-libs/link-attr-validation-late.rs +++ b/tests/ui/link-native-libs/link-attr-validation-late.rs @@ -9,7 +9,7 @@ extern "C" {} #[link(name = "foo", name = "bar")] //~ ERROR multiple `name` arguments #[link(name = "...", kind = "dylib", kind = "bar")] //~ ERROR multiple `kind` arguments #[link(name = "...", modifiers = "+verbatim", modifiers = "bar")] //~ ERROR multiple `modifiers` arguments -#[link(name = "...", cfg(FALSE), cfg(FALSE))] //~ ERROR multiple `cfg` arguments +#[link(name = "...", cfg(false), cfg(false))] //~ ERROR multiple `cfg` arguments #[link(wasm_import_module = "foo", wasm_import_module = "bar")] //~ ERROR multiple `wasm_import_module` arguments extern "C" {} diff --git a/tests/ui/link-native-libs/link-attr-validation-late.stderr b/tests/ui/link-native-libs/link-attr-validation-late.stderr index 1ad5fbaf7de80..f3989c09360fc 100644 --- a/tests/ui/link-native-libs/link-attr-validation-late.stderr +++ b/tests/ui/link-native-libs/link-attr-validation-late.stderr @@ -31,7 +31,7 @@ LL | #[link(name = "...", modifiers = "+verbatim", modifiers = "bar")] error: multiple `cfg` arguments in a single `#[link]` attribute --> $DIR/link-attr-validation-late.rs:12:34 | -LL | #[link(name = "...", cfg(FALSE), cfg(FALSE))] +LL | #[link(name = "...", cfg(false), cfg(false))] | ^^^^^^^^^^ error: multiple `wasm_import_module` arguments in a single `#[link]` attribute diff --git a/tests/ui/link-native-libs/manual-link-bad-form.rs b/tests/ui/link-native-libs/manual-link-bad-form.rs index 0f5723adec980..71e80c60deceb 100644 --- a/tests/ui/link-native-libs/manual-link-bad-form.rs +++ b/tests/ui/link-native-libs/manual-link-bad-form.rs @@ -1,5 +1,6 @@ //@ compile-flags:-l static= -//@ error-pattern: library name must not be empty fn main() { } + +//~? ERROR library name must not be empty diff --git a/tests/ui/link-native-libs/manual-link-bad-kind.rs b/tests/ui/link-native-libs/manual-link-bad-kind.rs index d070faa657450..8b5f478e80a65 100644 --- a/tests/ui/link-native-libs/manual-link-bad-kind.rs +++ b/tests/ui/link-native-libs/manual-link-bad-kind.rs @@ -1,5 +1,6 @@ //@ compile-flags:-l bar=foo -//@ error-pattern: unknown library kind `bar`, expected one of: static, dylib, framework, link-arg fn main() { } + +//~? ERROR unknown library kind `bar` diff --git a/tests/ui/link-native-libs/manual-link-bad-search-path.rs b/tests/ui/link-native-libs/manual-link-bad-search-path.rs index c9ced4734fc08..cc4a5ef22679b 100644 --- a/tests/ui/link-native-libs/manual-link-bad-search-path.rs +++ b/tests/ui/link-native-libs/manual-link-bad-search-path.rs @@ -1,5 +1,6 @@ //@ compile-flags:-L native= -//@ error-pattern: empty search path given via `-L` fn main() { } + +//~? ERROR empty search path given via `-L` diff --git a/tests/ui/link-native-libs/manual-link-framework.rs b/tests/ui/link-native-libs/manual-link-framework.rs index 43cdda0a4e6ac..b3c3d6a7c3c2e 100644 --- a/tests/ui/link-native-libs/manual-link-framework.rs +++ b/tests/ui/link-native-libs/manual-link-framework.rs @@ -1,5 +1,6 @@ //@ ignore-apple //@ compile-flags:-l framework=foo -//@ error-pattern: library kind `framework` is only supported on Apple targets fn main() {} + +//~? ERROR library kind `framework` is only supported on Apple targets diff --git a/tests/ui/link-native-libs/manual-link-unsupported-kind.rs b/tests/ui/link-native-libs/manual-link-unsupported-kind.rs index b5b9e3e65777c..3dfe9bdebc558 100644 --- a/tests/ui/link-native-libs/manual-link-unsupported-kind.rs +++ b/tests/ui/link-native-libs/manual-link-unsupported-kind.rs @@ -1,5 +1,6 @@ //@ compile-flags:-l raw-dylib=foo -//@ error-pattern: unknown library kind `raw-dylib`, expected one of: static, dylib, framework, link-arg fn main() { } + +//~? ERROR unknown library kind `raw-dylib` diff --git a/tests/ui/link-native-libs/modifiers-bad.rs b/tests/ui/link-native-libs/modifiers-bad.rs index 185201e0d8429..4d6c8a278d4c0 100644 --- a/tests/ui/link-native-libs/modifiers-bad.rs +++ b/tests/ui/link-native-libs/modifiers-bad.rs @@ -9,3 +9,8 @@ // Tests various illegal values for the "modifier" part of an `-l` flag. fn main() {} + +//[blank]~? ERROR invalid linking modifier syntax, expected '+' or '-' prefix +//[no-prefix]~? ERROR invalid linking modifier syntax, expected '+' or '-' prefix +//[prefix-only]~? ERROR unknown linking modifier `` +//[unknown]~? ERROR unknown linking modifier `ferris` diff --git a/tests/ui/link-native-libs/modifiers-override-2.rs b/tests/ui/link-native-libs/modifiers-override-2.rs index a462a741ac611..d132f2419d81f 100644 --- a/tests/ui/link-native-libs/modifiers-override-2.rs +++ b/tests/ui/link-native-libs/modifiers-override-2.rs @@ -1,3 +1,5 @@ //@ compile-flags:-lstatic:+whole-archive,-whole-archive=foo fn main() {} + +//~? ERROR multiple `whole-archive` modifiers in a single `-l` option diff --git a/tests/ui/link-native-libs/modifiers-override-3.rs b/tests/ui/link-native-libs/modifiers-override-3.rs index d05735ad61626..3d269dbd81240 100644 --- a/tests/ui/link-native-libs/modifiers-override-3.rs +++ b/tests/ui/link-native-libs/modifiers-override-3.rs @@ -2,6 +2,7 @@ // overrides another command line library with modifiers. //@ compile-flags:-lstatic:+whole-archive=foo -lstatic:+whole-archive=foo -//@ error-pattern: overriding linking modifiers from command line is not supported fn main() {} + +//~? ERROR overriding linking modifiers from command line is not supported diff --git a/tests/ui/link-native-libs/msvc-non-utf8-output.rs b/tests/ui/link-native-libs/msvc-non-utf8-output.rs index 03b1f6516ab9b..5cc4cd9a3d6d8 100644 --- a/tests/ui/link-native-libs/msvc-non-utf8-output.rs +++ b/tests/ui/link-native-libs/msvc-non-utf8-output.rs @@ -3,3 +3,5 @@ //@ only-msvc //@ normalize-stderr: "(?:.|\n)*(⦺ⅈ⽯⭏⽽◃⡽⚞)(?:.|\n)*" -> "$1" pub fn main() {} + +//~? ERROR linking with ` diff --git a/tests/ui/link-native-libs/suggest-libname-only-1.rs b/tests/ui/link-native-libs/suggest-libname-only-1.rs index 328181fb5cbf8..8699e4819e623 100644 --- a/tests/ui/link-native-libs/suggest-libname-only-1.rs +++ b/tests/ui/link-native-libs/suggest-libname-only-1.rs @@ -1,9 +1,11 @@ //@ build-fail //@ compile-flags: --crate-type rlib -//@ error-pattern: could not find native static library `libfoo.a` -//@ error-pattern: only provide the library name `foo`, not the full filename #[link(name = "libfoo.a", kind = "static")] -extern { } +extern { } //~ WARN `extern` declarations without an explicit ABI are deprecated + //~| HELP explicitly specify the "C" ABI pub fn main() { } + +//~? ERROR could not find native static library `libfoo.a` +//~? HELP only provide the library name `foo`, not the full filename diff --git a/tests/ui/link-native-libs/suggest-libname-only-1.stderr b/tests/ui/link-native-libs/suggest-libname-only-1.stderr index 47f7d92c9f9e2..59bd99f619a0f 100644 --- a/tests/ui/link-native-libs/suggest-libname-only-1.stderr +++ b/tests/ui/link-native-libs/suggest-libname-only-1.stderr @@ -1,5 +1,5 @@ -warning: extern declarations without an explicit ABI are deprecated - --> $DIR/suggest-libname-only-1.rs:7:1 +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/suggest-libname-only-1.rs:5:1 | LL | extern { } | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` diff --git a/tests/ui/link-native-libs/suggest-libname-only-2.rs b/tests/ui/link-native-libs/suggest-libname-only-2.rs index 7ed106e4ab431..87373f563d090 100644 --- a/tests/ui/link-native-libs/suggest-libname-only-2.rs +++ b/tests/ui/link-native-libs/suggest-libname-only-2.rs @@ -1,9 +1,11 @@ //@ build-fail //@ compile-flags: --crate-type rlib -//@ error-pattern: could not find native static library `bar.lib` -//@ error-pattern: only provide the library name `bar`, not the full filename #[link(name = "bar.lib", kind = "static")] -extern { } +extern { } //~ WARN `extern` declarations without an explicit ABI are deprecated + //~| HELP explicitly specify the "C" ABI pub fn main() { } + +//~? ERROR could not find native static library `bar.lib` +//~? HELP only provide the library name `bar`, not the full filename diff --git a/tests/ui/link-native-libs/suggest-libname-only-2.stderr b/tests/ui/link-native-libs/suggest-libname-only-2.stderr index a2d8f4c8191bf..298a9ced17076 100644 --- a/tests/ui/link-native-libs/suggest-libname-only-2.stderr +++ b/tests/ui/link-native-libs/suggest-libname-only-2.stderr @@ -1,5 +1,5 @@ -warning: extern declarations without an explicit ABI are deprecated - --> $DIR/suggest-libname-only-2.rs:7:1 +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/suggest-libname-only-2.rs:5:1 | LL | extern { } | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` diff --git a/tests/ui/linkage-attr/incompatible-flavor.rs b/tests/ui/linkage-attr/incompatible-flavor.rs index acf720bc97a13..7f583f47e2f5d 100644 --- a/tests/ui/linkage-attr/incompatible-flavor.rs +++ b/tests/ui/linkage-attr/incompatible-flavor.rs @@ -1,6 +1,7 @@ //@ compile-flags: --target=x86_64-unknown-linux-gnu -C linker-flavor=msvc --crate-type=rlib -//@ error-pattern: linker flavor `msvc` is incompatible with the current target //@ needs-llvm-components: #![feature(no_core)] #![no_core] + +//~? ERROR linker flavor `msvc` is incompatible with the current target diff --git a/tests/ui/linkage-attr/issue-10755.rs b/tests/ui/linkage-attr/issue-10755.rs index 58d5b5ead573c..509f484adf821 100644 --- a/tests/ui/linkage-attr/issue-10755.rs +++ b/tests/ui/linkage-attr/issue-10755.rs @@ -1,10 +1,11 @@ //@ build-fail //@ dont-check-compiler-stderr //@ compile-flags: -C linker=llllll -//@ error-pattern: `llllll` // Before, the error-pattern checked for "not found". On WSL with appendWindowsPath=true, running // in invalid command returns a PermissionDenied instead. fn main() { } + +//~? ERROR `llllll` diff --git a/tests/ui/linkage-attr/issue-12133-3.rs b/tests/ui/linkage-attr/issue-12133-3.rs index 473d5774c162f..df1b0b2f728e4 100644 --- a/tests/ui/linkage-attr/issue-12133-3.rs +++ b/tests/ui/linkage-attr/issue-12133-3.rs @@ -2,8 +2,7 @@ //@ aux-build:issue-12133-rlib.rs //@ aux-build:issue-12133-dylib.rs //@ aux-build:issue-12133-dylib2.rs -//@ ignore-wasm32 no dylib support -//@ ignore-musl +//@ needs-crate-type: dylib //@ needs-dynamic-linking diff --git a/tests/ui/linkage-attr/link-self-contained-consistency.rs b/tests/ui/linkage-attr/link-self-contained-consistency.rs deleted file mode 100644 index def63233e9414..0000000000000 --- a/tests/ui/linkage-attr/link-self-contained-consistency.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Checks that self-contained linking components cannot be both enabled and disabled at the same -// time on the CLI. - -//@ check-fail -//@ revisions: one many -//@ [one] compile-flags: -Clink-self-contained=-linker -Clink-self-contained=+linker -Zunstable-options -//@ [many] compile-flags: -Clink-self-contained=+linker,+crto -Clink-self-contained=-linker,-crto -Zunstable-options -// ignore-tidy-linelength - -fn main() {} diff --git a/tests/ui/linkage-attr/linkage-detect-extern-generated-name-collision.rs b/tests/ui/linkage-attr/linkage-detect-extern-generated-name-collision.rs index b43df63411217..23848056ee1aa 100644 --- a/tests/ui/linkage-attr/linkage-detect-extern-generated-name-collision.rs +++ b/tests/ui/linkage-attr/linkage-detect-extern-generated-name-collision.rs @@ -22,3 +22,5 @@ fn main() { println!("{:p}", &dep1::collision); } } + +//~? ERROR symbol `collision` is already defined diff --git a/tests/ui/linkage-attr/raw-dylib/elf/multiple-libraries.rs b/tests/ui/linkage-attr/raw-dylib/elf/multiple-libraries.rs new file mode 100644 index 0000000000000..030b95198f545 --- /dev/null +++ b/tests/ui/linkage-attr/raw-dylib/elf/multiple-libraries.rs @@ -0,0 +1,39 @@ +//@ only-elf +//@ needs-dynamic-linking +// FIXME(raw_dylib_elf): Debug the failures on other targets. +//@ only-gnu +//@ only-x86_64 + +//@ revisions: with without + +//@ [without] build-fail +//@ [without] regex-error-pattern:error: linking with `.*` failed +//@ [without] dont-check-compiler-stderr + +//@ [with] build-pass + +//! Ensures that linking fails when there's an undefined symbol, +//! and that it does succeed with raw-dylib. + +#![feature(raw_dylib_elf)] +#![allow(incomplete_features)] + +#[cfg_attr(with, link(name = "rawdylibbutforcats", kind = "raw-dylib"))] +#[cfg_attr(without, link(name = "rawdylibbutforcats"))] +unsafe extern "C" { + safe fn meooooooooooooooow(); +} + + +#[cfg_attr(with, link(name = "rawdylibbutfordogs", kind = "raw-dylib"))] +#[cfg_attr(without, link(name = "rawdylibbutfordogs"))] +unsafe extern "C" { + safe fn woooooooooooooooooof(); +} + +fn main() { + meooooooooooooooow(); + woooooooooooooooooof(); +} + +//[without]~? ERROR linking with ` diff --git a/tests/ui/linkage-attr/raw-dylib/elf/single-symbol.rs b/tests/ui/linkage-attr/raw-dylib/elf/single-symbol.rs new file mode 100644 index 0000000000000..20de4afcee0bc --- /dev/null +++ b/tests/ui/linkage-attr/raw-dylib/elf/single-symbol.rs @@ -0,0 +1,30 @@ +//@ only-elf +//@ needs-dynamic-linking +// FIXME(raw_dylib_elf): Debug the failures on other targets. +//@ only-gnu +//@ only-x86_64 +//@ revisions: with without + +//@ [without] build-fail +//@ [without] regex-error-pattern:error: linking with `.*` failed +//@ [without] dont-check-compiler-stderr + +//@ [with] build-pass + +//! Ensures that linking fails when there's an undefined symbol, +//! and that it does succeed with raw-dylib. + +#![feature(raw_dylib_elf)] +#![allow(incomplete_features)] + +#[cfg_attr(with, link(name = "rawdylibbutforcats", kind = "raw-dylib"))] +#[cfg_attr(without, link(name = "rawdylibbutforcats"))] +unsafe extern "C" { + safe fn meooooooooooooooow(); +} + +fn main() { + meooooooooooooooow(); +} + +//[without]~? ERROR linking with ` diff --git a/tests/ui/linkage-attr/raw-dylib/elf/verbatim.rs b/tests/ui/linkage-attr/raw-dylib/elf/verbatim.rs new file mode 100644 index 0000000000000..302ec0edeaa18 --- /dev/null +++ b/tests/ui/linkage-attr/raw-dylib/elf/verbatim.rs @@ -0,0 +1,31 @@ +//@ only-elf +//@ needs-dynamic-linking +// FIXME(raw_dylib_elf): Debug the failures on other targets. +//@ only-gnu +//@ only-x86_64 + +//@ revisions: with without + +//@ [without] build-fail +//@ [without] regex-error-pattern:error: linking with `.*` failed +//@ [without] dont-check-compiler-stderr + +//@ [with] build-pass + +//! Ensures that linking fails when there's an undefined symbol, +//! and that it does succeed with raw-dylib, but with verbatim. + +#![feature(raw_dylib_elf)] +#![allow(incomplete_features)] + +#[cfg_attr(with, link(name = "rawdylibbutforcats", kind = "raw-dylib", modifiers = "+verbatim"))] +#[cfg_attr(without, link(name = "rawdylibbutforcats", modifiers = "+verbatim"))] +unsafe extern "C" { + safe fn meooooooooooooooow(); +} + +fn main() { + meooooooooooooooow(); +} + +//[without]~? ERROR linking with ` diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.rs b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs similarity index 93% rename from tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.rs rename to tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs index e69a453793525..92cb60bb16d48 100644 --- a/tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.rs +++ b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs @@ -19,3 +19,5 @@ extern "C" { pub fn lib_main() { unsafe { f(42); } } + +//~? ERROR Dlltool could not create import library with diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.stderr b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-invalid-format.rs b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-invalid-format.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-invalid-format.rs rename to tests/ui/linkage-attr/raw-dylib/windows/import-name-type-invalid-format.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-invalid-format.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/import-name-type-invalid-format.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-multiple.rs b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-multiple.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-multiple.rs rename to tests/ui/linkage-attr/raw-dylib/windows/import-name-type-multiple.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-multiple.stderr b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-multiple.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-multiple.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/import-name-type-multiple.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unknown-value.rs b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-unknown-value.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unknown-value.rs rename to tests/ui/linkage-attr/raw-dylib/windows/import-name-type-unknown-value.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-unknown-value.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/import-name-type-unknown-value.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-unsupported-link-kind.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs rename to tests/ui/linkage-attr/raw-dylib/windows/import-name-type-unsupported-link-kind.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-unsupported-link-kind.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/import-name-type-unsupported-link-kind.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-x86-only.rs b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-x86-only.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-x86-only.rs rename to tests/ui/linkage-attr/raw-dylib/windows/import-name-type-x86-only.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-x86-only.stderr b/tests/ui/linkage-attr/raw-dylib/windows/import-name-type-x86-only.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-x86-only.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/import-name-type-x86-only.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.rs b/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.rs similarity index 78% rename from tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.rs rename to tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.rs index 057242246f0ed..0c78d799bad2d 100644 --- a/tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.rs +++ b/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.rs @@ -10,3 +10,5 @@ extern "C" { pub fn lib_main() { unsafe { f(42); } } + +//~? ERROR Error calling dlltool 'does_not_exist.exe': program not found diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.stderr b/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-and-name.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-and-name.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-and-name.rs rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-and-name.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-and-name.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-and-name.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-and-name.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-and-name.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-missing-argument.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-missing-argument.rs rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-missing-argument.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-missing-argument.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-multiple.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-multiple.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-multiple.rs rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-multiple.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-multiple.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-multiple.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-multiple.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-multiple.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-not-foreign-fn.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-not-foreign-fn.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-not-foreign-fn.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-not-foreign-fn.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-large.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-large.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-large.rs rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-large.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-large.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-large.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-large.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-large.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.rs rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-unsupported-link-kind.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.rs rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-unsupported-link-kind.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-unsupported-link-kind.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-unsupported-link-kind.stderr diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.rs b/tests/ui/linkage-attr/raw-dylib/windows/multiple-declarations.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.rs rename to tests/ui/linkage-attr/raw-dylib/windows/multiple-declarations.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr b/tests/ui/linkage-attr/raw-dylib/windows/multiple-declarations.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/multiple-declarations.stderr diff --git a/tests/ui/linkage-attr/raw-dylib/windows/raw-dylib-windows-only.elf.stderr b/tests/ui/linkage-attr/raw-dylib/windows/raw-dylib-windows-only.elf.stderr new file mode 100644 index 0000000000000..70945ed6fc0bc --- /dev/null +++ b/tests/ui/linkage-attr/raw-dylib/windows/raw-dylib-windows-only.elf.stderr @@ -0,0 +1,13 @@ +error[E0658]: link kind `raw-dylib` is unstable on ELF platforms + --> $DIR/raw-dylib-windows-only.rs:6:29 + | +LL | #[link(name = "foo", kind = "raw-dylib")] + | ^^^^^^^^^^^ + | + = note: see issue #135694 for more information + = help: add `#![feature(raw_dylib_elf)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/linkage-attr/raw-dylib/windows/raw-dylib-windows-only.notelf.stderr b/tests/ui/linkage-attr/raw-dylib/windows/raw-dylib-windows-only.notelf.stderr new file mode 100644 index 0000000000000..daed15d784a5d --- /dev/null +++ b/tests/ui/linkage-attr/raw-dylib/windows/raw-dylib-windows-only.notelf.stderr @@ -0,0 +1,9 @@ +error[E0455]: link kind `raw-dylib` is only supported on Windows targets + --> $DIR/raw-dylib-windows-only.rs:6:29 + | +LL | #[link(name = "foo", kind = "raw-dylib")] + | ^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0455`. diff --git a/tests/ui/linkage-attr/raw-dylib/windows/raw-dylib-windows-only.rs b/tests/ui/linkage-attr/raw-dylib/windows/raw-dylib-windows-only.rs new file mode 100644 index 0000000000000..935c59b5aaa5e --- /dev/null +++ b/tests/ui/linkage-attr/raw-dylib/windows/raw-dylib-windows-only.rs @@ -0,0 +1,9 @@ +//@ revisions: elf notelf +//@ [elf] only-elf +//@ [notelf] ignore-windows +//@ [notelf] ignore-elf +//@ compile-flags: --crate-type lib +#[link(name = "foo", kind = "raw-dylib")] +//[notelf]~^ ERROR: link kind `raw-dylib` is only supported on Windows targets +//[elf]~^^ ERROR: link kind `raw-dylib` is unstable on ELF platforms +extern "C" {} diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.rs b/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.rs similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.rs rename to tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.rs diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr b/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr similarity index 100% rename from tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr rename to tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr diff --git a/tests/ui/linkage-attr/unstable-flavor.rs b/tests/ui/linkage-attr/unstable-flavor.rs index 82d9dff38741c..6aa9efb58d1f8 100644 --- a/tests/ui/linkage-attr/unstable-flavor.rs +++ b/tests/ui/linkage-attr/unstable-flavor.rs @@ -4,11 +4,12 @@ // //@ revisions: bpf ptx //@ [bpf] compile-flags: --target=bpfel-unknown-none -C linker-flavor=bpf --crate-type=rlib -//@ [bpf] error-pattern: linker flavor `bpf` is unstable, the `-Z unstable-options` flag //@ [bpf] needs-llvm-components: //@ [ptx] compile-flags: --target=nvptx64-nvidia-cuda -C linker-flavor=ptx --crate-type=rlib -//@ [ptx] error-pattern: linker flavor `ptx` is unstable, the `-Z unstable-options` flag //@ [ptx] needs-llvm-components: #![feature(no_core)] #![no_core] + +//[bpf]~? ERROR the linker flavor `bpf` is unstable +//[ptx]~? ERROR the linker flavor `ptx` is unstable diff --git a/tests/ui/linking/cdylib-no-mangle.rs b/tests/ui/linking/cdylib-no-mangle.rs new file mode 100644 index 0000000000000..f442c3f584d6e --- /dev/null +++ b/tests/ui/linking/cdylib-no-mangle.rs @@ -0,0 +1,20 @@ +//@ only-apple +//@ build-fail +//@ dont-check-compiler-stderr +//@ dont-check-compiler-stdout + +// Regression test for . +// Functions in the dynamic library marked with no_mangle should not be GC-ed. + +#![crate_type = "cdylib"] + +unsafe extern "C" { + unsafe static THIS_SYMBOL_SHOULD_BE_UNDEFINED: usize; +} + +#[unsafe(no_mangle)] +pub unsafe fn function_marked_with_no_mangle() { + println!("FUNCTION_MARKED_WITH_NO_MANGLE = {}", unsafe { THIS_SYMBOL_SHOULD_BE_UNDEFINED }); +} + +//~? ERROR linking diff --git a/tests/ui/linking/executable-no-mangle-strip.rs b/tests/ui/linking/executable-no-mangle-strip.rs new file mode 100644 index 0000000000000..cc283dc53ee3a --- /dev/null +++ b/tests/ui/linking/executable-no-mangle-strip.rs @@ -0,0 +1,27 @@ +//@ run-pass +//@ ignore-windows-gnu: only statics marked with used can be GC-ed on windows-gnu + +// Regression test for . +// Functions in the binary marked with no_mangle should be GC-ed if they +// are not indirectly referenced by main. + +#![feature(used_with_arg)] + +#[cfg_attr(windows, link(name = "this_lib_does_not_exist", kind = "raw-dylib"))] +unsafe extern "C" { + unsafe static THIS_SYMBOL_SHOULD_BE_UNDEFINED: usize; +} + +#[unsafe(no_mangle)] +pub unsafe fn function_marked_with_no_mangle() { + println!("FUNCTION_MARKED_WITH_NO_MANGLE = {}", unsafe { THIS_SYMBOL_SHOULD_BE_UNDEFINED }); +} + +#[used(compiler)] +pub static FUNCTION_MARKED_WITH_USED: unsafe fn() = || { + println!("FUNCTION_MARKED_WITH_USED = {}", unsafe { THIS_SYMBOL_SHOULD_BE_UNDEFINED }); +}; + +fn main() { + println!("MAIN"); +} diff --git a/tests/ui/linkage-attr/link-self-contained-consistency.many.stderr b/tests/ui/linking/link-self-contained-consistency.many.stderr similarity index 100% rename from tests/ui/linkage-attr/link-self-contained-consistency.many.stderr rename to tests/ui/linking/link-self-contained-consistency.many.stderr diff --git a/tests/ui/linkage-attr/link-self-contained-consistency.one.stderr b/tests/ui/linking/link-self-contained-consistency.one.stderr similarity index 100% rename from tests/ui/linkage-attr/link-self-contained-consistency.one.stderr rename to tests/ui/linking/link-self-contained-consistency.one.stderr diff --git a/tests/ui/linking/link-self-contained-consistency.rs b/tests/ui/linking/link-self-contained-consistency.rs new file mode 100644 index 0000000000000..0822743389160 --- /dev/null +++ b/tests/ui/linking/link-self-contained-consistency.rs @@ -0,0 +1,13 @@ +// Checks that self-contained linking components cannot be both enabled and disabled at the same +// time on the CLI. + +//@ check-fail +//@ revisions: one many +//@ [one] compile-flags: -Clink-self-contained=-linker -Clink-self-contained=+linker -Zunstable-options +//@ [many] compile-flags: -Clink-self-contained=+linker,+crto -Clink-self-contained=-linker,-crto -Zunstable-options +// ignore-tidy-linelength + +fn main() {} + +//[one]~? ERROR some `-C link-self-contained` components were both enabled and disabled: linker +//[many]~? ERROR some `-C link-self-contained` components were both enabled and disabled: crto, linker diff --git a/tests/ui/linking/link-self-contained-malformed.invalid_modifier.stderr b/tests/ui/linking/link-self-contained-malformed.invalid_modifier.stderr new file mode 100644 index 0000000000000..28e2c74fda229 --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.invalid_modifier.stderr @@ -0,0 +1,2 @@ +error: incorrect value `*lld` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/link-self-contained-malformed.no_value.stderr b/tests/ui/linking/link-self-contained-malformed.no_value.stderr new file mode 100644 index 0000000000000..dd8e8af074bfc --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.no_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/link-self-contained-malformed.rs b/tests/ui/linking/link-self-contained-malformed.rs new file mode 100644 index 0000000000000..8ccb82eee2707 --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.rs @@ -0,0 +1,23 @@ +//! Check that malformed `-Clink-self-contained` invocations are properly rejected. + +//@ revisions: no_value +//@[no_value] compile-flags: -Clink-self-contained= +//[no_value]~? ERROR incorrect value `` for codegen option `link-self-contained` + +//@ revisions: invalid_modifier +//@[invalid_modifier] compile-flags: -Clink-self-contained=*lld +//[invalid_modifier]~? ERROR incorrect value `*lld` for codegen option `link-self-contained` + +//@ revisions: unknown_value +//@[unknown_value] compile-flags: -Clink-self-contained=unknown +//[unknown_value]~? ERROR incorrect value `unknown` for codegen option `link-self-contained` + +//@ revisions: unknown_modifier_value +//@[unknown_modifier_value] compile-flags: -Clink-self-contained=-unknown +//[unknown_modifier_value]~? ERROR incorrect value `-unknown` for codegen option `link-self-contained` + +//@ revisions: unknown_boolean +//@[unknown_boolean] compile-flags: -Clink-self-contained=maybe +//[unknown_boolean]~? ERROR incorrect value `maybe` for codegen option `link-self-contained` + +fn main() {} diff --git a/tests/ui/linking/link-self-contained-malformed.unknown_boolean.stderr b/tests/ui/linking/link-self-contained-malformed.unknown_boolean.stderr new file mode 100644 index 0000000000000..7924074d1bf47 --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.unknown_boolean.stderr @@ -0,0 +1,2 @@ +error: incorrect value `maybe` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/link-self-contained-malformed.unknown_modifier_value.stderr b/tests/ui/linking/link-self-contained-malformed.unknown_modifier_value.stderr new file mode 100644 index 0000000000000..2dc58c0f7e8f9 --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.unknown_modifier_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `-unknown` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/link-self-contained-malformed.unknown_value.stderr b/tests/ui/linking/link-self-contained-malformed.unknown_value.stderr new file mode 100644 index 0000000000000..ce4c44299cd2e --- /dev/null +++ b/tests/ui/linking/link-self-contained-malformed.unknown_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `unknown` for codegen option `link-self-contained` - one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw` was expected + diff --git a/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr b/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr new file mode 100644 index 0000000000000..909b277089f42 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.invalid_modifier.stderr @@ -0,0 +1,2 @@ +error: incorrect value `*lld` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.invalid_separator.stderr b/tests/ui/linking/linker-features-malformed.invalid_separator.stderr new file mode 100644 index 0000000000000..0f84898a77422 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.invalid_separator.stderr @@ -0,0 +1,2 @@ +error: incorrect value `-lld@+lld` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.no_value.stderr b/tests/ui/linking/linker-features-malformed.no_value.stderr new file mode 100644 index 0000000000000..e93a4e79bb1d8 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.no_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.rs b/tests/ui/linking/linker-features-malformed.rs new file mode 100644 index 0000000000000..0bdcfa39920f0 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.rs @@ -0,0 +1,27 @@ +//! Check that malformed `-Zlinker-features` flags are properly rejected. + +//@ revisions: no_value +//@[no_value] compile-flags: -Zlinker-features= +//[no_value]~? ERROR incorrect value `` for unstable option `linker-features` + +//@ revisions: invalid_modifier +//@[invalid_modifier] compile-flags: -Zlinker-features=*lld +//[invalid_modifier]~? ERROR incorrect value `*lld` for unstable option `linker-features` + +//@ revisions: unknown_value +//@[unknown_value] compile-flags: -Zlinker-features=unknown +//[unknown_value]~? ERROR incorrect value `unknown` for unstable option `linker-features` + +//@ revisions: unknown_modifier_value +//@[unknown_modifier_value] compile-flags: -Zlinker-features=-unknown +//[unknown_modifier_value]~? ERROR incorrect value `-unknown` for unstable option `linker-features` + +//@ revisions: unknown_boolean +//@[unknown_boolean] compile-flags: -Zlinker-features=maybe +//[unknown_boolean]~? ERROR incorrect value `maybe` for unstable option `linker-features` + +//@ revisions: invalid_separator +//@[invalid_separator] compile-flags: -Zlinker-features=-lld@+lld +//[invalid_separator]~? ERROR incorrect value `-lld@+lld` for unstable option `linker-features` + +fn main() {} diff --git a/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr b/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr new file mode 100644 index 0000000000000..865738d0ccc1a --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.unknown_boolean.stderr @@ -0,0 +1,2 @@ +error: incorrect value `maybe` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr b/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr new file mode 100644 index 0000000000000..03b9620ca2633 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.unknown_modifier_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `-unknown` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/linker-features-malformed.unknown_value.stderr b/tests/ui/linking/linker-features-malformed.unknown_value.stderr new file mode 100644 index 0000000000000..566632a3df381 --- /dev/null +++ b/tests/ui/linking/linker-features-malformed.unknown_value.stderr @@ -0,0 +1,2 @@ +error: incorrect value `unknown` for unstable option `linker-features` - a list of enabled (`+` prefix) and disabled (`-` prefix) features: `lld` was expected + diff --git a/tests/ui/linking/no-gc-encapsulation-symbols.rs b/tests/ui/linking/no-gc-encapsulation-symbols.rs new file mode 100644 index 0000000000000..36d69969199ce --- /dev/null +++ b/tests/ui/linking/no-gc-encapsulation-symbols.rs @@ -0,0 +1,25 @@ +// This test checks that encapsulation symbols are not garbage collected by the linker. +// LLD will remove them by default, so this test checks that we pass `-znostart-stop-gc` to LLD +// to avoid that behavior. Without that flag, the test should fail. +// This test is inspired by the behavior of the linkme crate. +// +//@ build-pass +//@ only-x86_64-unknown-linux-gnu + +unsafe extern "Rust" { + // The __start_ section name is magical for the linker, + // It will put link sections named EXTERNFNS after it. + #[link_name = "__start_EXTERNFNS"] + static SECTION_START: fn(); +} + +#[used] +#[unsafe(link_section = "EXTERNFNS")] +static EXTERN_FN_LOCAL: fn() = extern_fn; + +fn extern_fn() {} + +fn main() { + // We need to reference the SECTION_START symbol to avoid it being garbage collected + let slice = unsafe { SECTION_START }; +} diff --git a/tests/ui/linking/weird-export-names.rs b/tests/ui/linking/weird-export-names.rs new file mode 100644 index 0000000000000..8fb2dc6bf5e8d --- /dev/null +++ b/tests/ui/linking/weird-export-names.rs @@ -0,0 +1,10 @@ +//@ build-pass +//@ needs-crate-type: cdylib + +#![crate_type = "cdylib"] + +#[export_name = "foo.0123"] +pub extern "C" fn foo() {} + +#[export_name = "EXPORTS"] +pub extern "C" fn bar() {} diff --git a/tests/ui/lint/bad-lint-cap.rs b/tests/ui/lint/bad-lint-cap.rs index aab3f723796d6..c219cb1b66551 100644 --- a/tests/ui/lint/bad-lint-cap.rs +++ b/tests/ui/lint/bad-lint-cap.rs @@ -1,4 +1,5 @@ //@ compile-flags: --cap-lints test -//@ error-pattern: unknown lint level: `test` fn main() {} + +//~? ERROR unknown lint level: `test` diff --git a/tests/ui/lint/bare-trait-objects-path.stderr b/tests/ui/lint/bare-trait-objects-path.stderr index d2d139dd025a5..fbb647c37c5ae 100644 --- a/tests/ui/lint/bare-trait-objects-path.stderr +++ b/tests/ui/lint/bare-trait-objects-path.stderr @@ -1,23 +1,3 @@ -warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/bare-trait-objects-path.rs:23:12 - | -LL | let _: Dyn::Ty; - | ^^^ - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! - = note: for more information, see - = note: `#[warn(bare_trait_objects)]` on by default -help: if this is a dyn-compatible trait, use `dyn` - | -LL | let _: ::Ty; - | ++++ + - -error[E0223]: ambiguous associated type - --> $DIR/bare-trait-objects-path.rs:23:12 - | -LL | let _: Dyn::Ty; - | ^^^^^^^ help: use fully-qualified syntax: `::Ty` - warning: trait objects without an explicit `dyn` are deprecated --> $DIR/bare-trait-objects-path.rs:14:5 | @@ -26,6 +6,7 @@ LL | Dyn::func(); | = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! = note: for more information, see + = note: `#[warn(bare_trait_objects)]` on by default help: if this is a dyn-compatible trait, use `dyn` | LL | ::func(); @@ -57,6 +38,25 @@ help: if this is a dyn-compatible trait, use `dyn` LL | ::CONST; | ++++ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/bare-trait-objects-path.rs:23:12 + | +LL | let _: Dyn::Ty; + | ^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see +help: if this is a dyn-compatible trait, use `dyn` + | +LL | let _: ::Ty; + | ++++ + + +error[E0223]: ambiguous associated type + --> $DIR/bare-trait-objects-path.rs:23:12 + | +LL | let _: Dyn::Ty; + | ^^^^^^^ help: use fully-qualified syntax: `::Ty` + error: aborting due to 1 previous error; 4 warnings emitted For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/lint/break-with-label-and-unsafe-block.rs b/tests/ui/lint/break-with-label-and-unsafe-block.rs new file mode 100644 index 0000000000000..a76a576147556 --- /dev/null +++ b/tests/ui/lint/break-with-label-and-unsafe-block.rs @@ -0,0 +1,11 @@ +//@ check-pass + +#![deny(break_with_label_and_loop)] + +unsafe fn foo() -> i32 { 42 } + +fn main () { + 'label: loop { + break 'label unsafe { foo() } + }; +} diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr index 118b18b224c05..0c27547a6ed8f 100644 --- a/tests/ui/lint/clashing-extern-fn.stderr +++ b/tests/ui/lint/clashing-extern-fn.stderr @@ -17,7 +17,7 @@ LL | fn hidden_niche_unsafe_cell() -> Option`, which is not FFI-safe +warning: `extern` block uses type `Option<(usize) is 0..>`, which is not FFI-safe --> $DIR/clashing-extern-fn.rs:502:54 | LL | fn pt_non_zero_usize_opt_full_range() -> Option; diff --git a/tests/ui/lint/cli-lint-override.forbid_warn.stderr b/tests/ui/lint/cli-lint-override.forbid_warn.stderr index fb8779ad4f15d..fe3437ae3d752 100644 --- a/tests/ui/lint/cli-lint-override.forbid_warn.stderr +++ b/tests/ui/lint/cli-lint-override.forbid_warn.stderr @@ -1,4 +1,4 @@ -error: extern declarations without an explicit ABI are deprecated +error: `extern` declarations without an explicit ABI are deprecated --> $DIR/cli-lint-override.rs:12:1 | LL | extern fn foo() {} diff --git a/tests/ui/lint/cli-lint-override.force_warn_deny.stderr b/tests/ui/lint/cli-lint-override.force_warn_deny.stderr index 10fc13e3f52f8..f48fca4bd9f37 100644 --- a/tests/ui/lint/cli-lint-override.force_warn_deny.stderr +++ b/tests/ui/lint/cli-lint-override.force_warn_deny.stderr @@ -1,4 +1,4 @@ -warning: extern declarations without an explicit ABI are deprecated +warning: `extern` declarations without an explicit ABI are deprecated --> $DIR/cli-lint-override.rs:12:1 | LL | extern fn foo() {} diff --git a/tests/ui/lint/cli-lint-override.rs b/tests/ui/lint/cli-lint-override.rs index 4b3fd0d9c01c9..b733872166aa2 100644 --- a/tests/ui/lint/cli-lint-override.rs +++ b/tests/ui/lint/cli-lint-override.rs @@ -10,8 +10,8 @@ extern fn foo() {} -//[warn_deny]~^ ERROR extern declarations without an explicit ABI are deprecated -//[forbid_warn]~^^ ERROR extern declarations without an explicit ABI are deprecated -//[force_warn_deny]~^^^ WARN extern declarations without an explicit ABI are deprecated +//[warn_deny]~^ ERROR `extern` declarations without an explicit ABI are deprecated +//[forbid_warn]~^^ ERROR `extern` declarations without an explicit ABI are deprecated +//[force_warn_deny]~^^^ WARN `extern` declarations without an explicit ABI are deprecated fn main() {} diff --git a/tests/ui/lint/cli-lint-override.warn_deny.stderr b/tests/ui/lint/cli-lint-override.warn_deny.stderr index 979ca22324f1e..91baad1f9f870 100644 --- a/tests/ui/lint/cli-lint-override.warn_deny.stderr +++ b/tests/ui/lint/cli-lint-override.warn_deny.stderr @@ -1,4 +1,4 @@ -error: extern declarations without an explicit ABI are deprecated +error: `extern` declarations without an explicit ABI are deprecated --> $DIR/cli-lint-override.rs:12:1 | LL | extern fn foo() {} diff --git a/tests/ui/lint/cli-unknown-force-warn.rs b/tests/ui/lint/cli-unknown-force-warn.rs index 007f8dd8732a8..0c60f48c5142d 100644 --- a/tests/ui/lint/cli-unknown-force-warn.rs +++ b/tests/ui/lint/cli-unknown-force-warn.rs @@ -3,9 +3,12 @@ //@ check-pass //@ compile-flags: --force-warn foo-qux - -//@ error-pattern: unknown lint: `foo_qux` -//@ error-pattern: requested on the command line with `--force-warn foo_qux` -//@ error-pattern: `#[warn(unknown_lints)]` on by default +//@ dont-require-annotations: NOTE fn main() {} + +//~? WARN unknown lint: `foo_qux` +//~? WARN unknown lint: `foo_qux` +//~? WARN unknown lint: `foo_qux` +//~? NOTE requested on the command line with `--force-warn foo_qux` +//~? NOTE `#[warn(unknown_lints)]` on by default diff --git a/tests/ui/lint/command-line-register-unknown-lint-tool.rs b/tests/ui/lint/command-line-register-unknown-lint-tool.rs index b4e9a067fe29f..a517f4c7ea68c 100644 --- a/tests/ui/lint/command-line-register-unknown-lint-tool.rs +++ b/tests/ui/lint/command-line-register-unknown-lint-tool.rs @@ -1,4 +1,7 @@ //@ compile-flags: -A unknown_tool::foo -//@ error-pattern: unknown lint tool: `unknown_tool` fn main() {} + +//~? ERROR unknown lint tool: `unknown_tool` +//~? ERROR unknown lint tool: `unknown_tool` +//~? ERROR unknown lint tool: `unknown_tool` diff --git a/tests/ui/lint/dead-code/allow-or-expect-dead_code-114557-2.rs b/tests/ui/lint/dead-code/allow-or-expect-dead_code-114557-2.rs index 20999df9844eb..db23dcd5e5eb7 100644 --- a/tests/ui/lint/dead-code/allow-or-expect-dead_code-114557-2.rs +++ b/tests/ui/lint/dead-code/allow-or-expect-dead_code-114557-2.rs @@ -1,10 +1,10 @@ //@ check-pass -// this test checks that the `dead_code` lint is *NOT* being emited +// this test checks that the `dead_code` lint is *NOT* being emitted // for `foo` as `foo` is being used by `main`, and so the `#[expect]` // is unfulfilled // -// it also checks that the `dead_code` lint is also *NOT* emited +// it also checks that the `dead_code` lint is also *NOT* emitted // for `bar` as it's suppresed by the `#[expect]` on `bar` #![warn(dead_code)] // to override compiletest diff --git a/tests/ui/lint/dead-code/allow-or-expect-dead_code-114557-3.rs b/tests/ui/lint/dead-code/allow-or-expect-dead_code-114557-3.rs index 08103b233872a..c4476e43e1ffc 100644 --- a/tests/ui/lint/dead-code/allow-or-expect-dead_code-114557-3.rs +++ b/tests/ui/lint/dead-code/allow-or-expect-dead_code-114557-3.rs @@ -1,7 +1,7 @@ //@ check-pass // this test makes sure that the `unfulfilled_lint_expectations` lint -// is being emited for `foo` as foo is not dead code, it's pub +// is being emitted for `foo` as foo is not dead code, it's pub #![warn(dead_code)] // to override compiletest diff --git a/tests/ui/lint/dead-code/allow-or-expect-dead_code-114557.rs b/tests/ui/lint/dead-code/allow-or-expect-dead_code-114557.rs index f2625f0781f6e..ea2e81261d10c 100644 --- a/tests/ui/lint/dead-code/allow-or-expect-dead_code-114557.rs +++ b/tests/ui/lint/dead-code/allow-or-expect-dead_code-114557.rs @@ -2,7 +2,7 @@ //@ revisions: allow expect // this test checks that no matter if we put #[allow(dead_code)] -// or #[expect(dead_code)], no warning is being emited +// or #[expect(dead_code)], no warning is being emitted #![warn(dead_code)] // to override compiletest diff --git a/tests/ui/lint/dead-code/anon-const-in-pat.rs b/tests/ui/lint/dead-code/anon-const-in-pat.rs deleted file mode 100644 index e2d8c90edcca2..0000000000000 --- a/tests/ui/lint/dead-code/anon-const-in-pat.rs +++ /dev/null @@ -1,44 +0,0 @@ -//@ check-pass -#![feature(inline_const_pat)] -#![deny(dead_code)] - -const fn one() -> i32 { - 1 -} - -const fn two() -> i32 { - 2 -} - -const fn three() -> i32 { - 3 -} - -fn inline_const() { - // rust-lang/rust#78171: dead_code lint triggers even though function is used in const pattern - match 1 { - const { one() } => {} - _ => {} - } -} - -fn inline_const_range() { - match 1 { - 1 ..= const { two() } => {} - _ => {} - } -} - -struct S; - -fn const_generic_arg() { - match S::<3> { - S::<{three()}> => {} - } -} - -fn main() { - inline_const(); - inline_const_range(); - const_generic_arg(); -} diff --git a/tests/ui/lint/dead-code/auxiliary/no-dead-code-reexported-types-across-crates.rs b/tests/ui/lint/dead-code/auxiliary/no-dead-code-reexported-types-across-crates.rs new file mode 100644 index 0000000000000..5f328d1abf6ab --- /dev/null +++ b/tests/ui/lint/dead-code/auxiliary/no-dead-code-reexported-types-across-crates.rs @@ -0,0 +1,33 @@ +//! Auxilary file for testing `dead_code` lint. This crate is compiled as a library and exposes +//! aliased types. When used externally, there should not be warnings of `dead_code` +//! +//! Issue: + +// Expose internal types to be used in external test +pub use src::aliases::ExposedType; +pub use src::hidden_core::new; + +mod src { + pub mod aliases { + use super::hidden_core::InternalStruct; + pub type ExposedType = InternalStruct; + } + + pub mod hidden_core { + use super::aliases::ExposedType; + + pub struct InternalStruct { + _x: T, + } + + pub fn new() -> ExposedType { + InternalStruct { _x: 1.0 } + } + + impl InternalStruct { + pub fn foo(&mut self) { + println!("called foo"); + } + } + } +} diff --git a/tests/ui/lint/dead-code/no-dead-code-for-static-trait-impl.rs b/tests/ui/lint/dead-code/no-dead-code-for-static-trait-impl.rs new file mode 100644 index 0000000000000..8d54eda6bcaae --- /dev/null +++ b/tests/ui/lint/dead-code/no-dead-code-for-static-trait-impl.rs @@ -0,0 +1,61 @@ +//! Regression test to ensure false positive `dead_code` diagnostic warnings are not triggered for +//! structs and enums that implement static trait functions or use associated constants. +//! +//! Aliased versions of all cases are also tested +//! +//! Issue: + +//@ check-pass +#![deny(dead_code)] + +trait Const { + const C: (); +} + +trait StaticFn { + fn sfn(); +} + +macro_rules! impl_const {($($T:ident),*) => {$( + impl Const for $T { + const C: () = (); + } +)*}} + +macro_rules! impl_static_fn {($($T:ident),*) => {$( + impl StaticFn for $T { + fn sfn() {} + } +)*}} + +struct ConstStruct; +enum ConstEnum {} +struct AliasedConstStruct; +type AliasConstStruct = AliasedConstStruct; +enum AliasedConstEnum {} +type AliasConstEnum = AliasedConstEnum; + +impl_const!(ConstStruct, ConstEnum, AliasedConstStruct, AliasedConstEnum); + +struct StaticFnStruct; +enum StaticFnEnum {} +struct AliasedStaticFnStruct; +type AliasStaticFnStruct = AliasedStaticFnStruct; +enum AliasedStaticFnEnum {} +type AliasStaticFnEnum = AliasedStaticFnEnum; + +impl_static_fn!(StaticFnStruct, StaticFnEnum, AliasedStaticFnStruct, AliasedStaticFnEnum); + +fn main() { + // Use the associated constant for all the types, they should be considered "used" + let () = ConstStruct::C; + let () = ConstEnum::C; + let () = AliasConstStruct::C; + let () = AliasConstEnum::C; + + // Use the associated static function for all the types, they should be considered "used" + StaticFnStruct::sfn(); + StaticFnEnum::sfn(); + AliasStaticFnStruct::sfn(); + AliasStaticFnEnum::sfn(); +} diff --git a/tests/ui/lint/dead-code/no-dead-code-reexported-types-across-crates.rs b/tests/ui/lint/dead-code/no-dead-code-reexported-types-across-crates.rs new file mode 100644 index 0000000000000..11082f772ff61 --- /dev/null +++ b/tests/ui/lint/dead-code/no-dead-code-reexported-types-across-crates.rs @@ -0,0 +1,17 @@ +//! Regression test to ensure that `dead_code` warning does not get triggered when using re-exported +//! types that are exposed from a different crate +//! +//! Issue: + +//@ check-pass +//@ aux-build:no-dead-code-reexported-types-across-crates.rs + +extern crate no_dead_code_reexported_types_across_crates as bug_lib; + +use bug_lib::ExposedType; +use bug_lib::new; + +pub fn main() { + let mut x: ExposedType = new(); + x.foo(); +} diff --git a/tests/ui/lint/dead-code/self-assign.rs b/tests/ui/lint/dead-code/self-assign.rs index 072a899e1bdb1..357846baf2212 100644 --- a/tests/ui/lint/dead-code/self-assign.rs +++ b/tests/ui/lint/dead-code/self-assign.rs @@ -1,19 +1,29 @@ -// Test that dead code warnings are issued for superfluous assignments of -// fields or variables to themselves (issue #75356). - -//@ ignore-test FIXME(81658, 83171) +//! Test that dead code warnings are issued for superfluous assignments of fields or variables to +//! themselves (issue #75356). +//! +//! # History of this test (to aid relanding of a fixed version of #81473) +//! +//! - Original lint request was about self-assignments not triggering sth like `dead_code`. +//! - `dead_code` lint expansion for self-assignments was implemented in #87129. +//! - Unfortunately implementation components of #87129 had to be disabled as part of reverts +//! #86212, #83171 (to revert #81473) to address regressions #81626 and #81658. +//! - Consequently, none of the following warnings are emitted. //@ check-pass + +// Implementation of self-assignment `dead_code` lint expansions disabled due to reverts. +//@ known-bug: #75356 + #![allow(unused_assignments)] #![warn(dead_code)] fn main() { let mut x = 0; x = x; - //~^ WARNING: useless assignment of variable of type `i32` to itself + // FIXME ~^ WARNING: useless assignment of variable of type `i32` to itself x = (x); - //~^ WARNING: useless assignment of variable of type `i32` to itself + // FIXME ~^ WARNING: useless assignment of variable of type `i32` to itself x = {x}; // block expressions don't count as self-assignments @@ -22,10 +32,10 @@ fn main() { struct S<'a> { f: &'a str } let mut s = S { f: "abc" }; s = s; - //~^ WARNING: useless assignment of variable of type `S` to itself + // FIXME ~^ WARNING: useless assignment of variable of type `S` to itself s.f = s.f; - //~^ WARNING: useless assignment of field of type `&str` to itself + // FIXME ~^ WARNING: useless assignment of field of type `&str` to itself struct N0 { x: Box } @@ -34,11 +44,11 @@ fn main() { struct N3 { n: N2 }; let mut n3 = N3 { n: N2(N1 { n: N0 { x: Box::new(42) } }) }; n3.n.0.n.x = n3.n.0.n.x; - //~^ WARNING: useless assignment of field of type `Box` to itself + // FIXME ~^ WARNING: useless assignment of field of type `Box` to itself let mut t = (1, ((2, 3, (4, 5)),)); t.1.0.2.1 = t.1.0.2.1; - //~^ WARNING: useless assignment of field of type `i32` to itself + // FIXME ~^ WARNING: useless assignment of field of type `i32` to itself let mut y = 0; diff --git a/tests/ui/lint/dead-code/self-assign.stderr b/tests/ui/lint/dead-code/self-assign.stderr deleted file mode 100644 index bb79c0ec72a34..0000000000000 --- a/tests/ui/lint/dead-code/self-assign.stderr +++ /dev/null @@ -1,44 +0,0 @@ -warning: useless assignment of variable of type `i32` to itself - --> $DIR/self-assign.rs:10:5 - | -LL | x = x; - | ^^^^^ - | -note: the lint level is defined here - --> $DIR/self-assign.rs:6:9 - | -LL | #![warn(dead_code)] - | ^^^^^^^^^ - -warning: useless assignment of variable of type `i32` to itself - --> $DIR/self-assign.rs:13:5 - | -LL | x = (x); - | ^^^^^^^ - -warning: useless assignment of variable of type `S` to itself - --> $DIR/self-assign.rs:22:5 - | -LL | s = s; - | ^^^^^ - -warning: useless assignment of field of type `&str` to itself - --> $DIR/self-assign.rs:25:5 - | -LL | s.f = s.f; - | ^^^^^^^^^ - -warning: useless assignment of field of type `Box` to itself - --> $DIR/self-assign.rs:34:5 - | -LL | n3.n.0.n.x = n3.n.0.n.x; - | ^^^^^^^^^^^^^^^^^^^^^^^ - -warning: useless assignment of field of type `i32` to itself - --> $DIR/self-assign.rs:38:5 - | -LL | t.1.0.2.1 = t.1.0.2.1; - | ^^^^^^^^^^^^^^^^^^^^^ - -warning: 6 warnings emitted - diff --git a/tests/ui/lint/expansion-time-include.rs b/tests/ui/lint/expansion-time-include.rs index 3ecc01b045c41..cbe3510f04ae2 100644 --- a/tests/ui/lint/expansion-time-include.rs +++ b/tests/ui/lint/expansion-time-include.rs @@ -1,4 +1,4 @@ -//@ ignore-test auxiliary file for expansion-time.rs +//@ ignore-auxiliary (used by `./expansion-time.rs`) 1 2 diff --git a/tests/ui/lint/expansion-time.rs b/tests/ui/lint/expansion-time.rs index d0f26a87385c7..5ffb0c7881e33 100644 --- a/tests/ui/lint/expansion-time.rs +++ b/tests/ui/lint/expansion-time.rs @@ -9,13 +9,6 @@ macro_rules! foo { 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` - //~| WARN this was previously accepted - fn foo() {} -} - #[deprecated = "reason"] macro_rules! deprecated { () => {} @@ -31,3 +24,5 @@ fn main() { // WARN see in the stderr file, the warning points to the included file. include!("expansion-time-include.rs"); } + +//~? WARN include macro expected single expression in source diff --git a/tests/ui/lint/expansion-time.stderr b/tests/ui/lint/expansion-time.stderr index f65627c2c0878..f24d1b68a8dae 100644 --- a/tests/ui/lint/expansion-time.stderr +++ b/tests/ui/lint/expansion-time.stderr @@ -26,20 +26,6 @@ note: the lint level is defined here LL | #[warn(missing_fragment_specifier)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: use of unstable library feature `test`: `bench` is a part of custom test frameworks which are unstable - --> $DIR/expansion-time.rs:14:7 - | -LL | #[bench] - | ^^^^^ - | - = 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 #64266 -note: the lint level is defined here - --> $DIR/expansion-time.rs:12:8 - | -LL | #[warn(soft_unstable)] - | ^^^^^^^^^^^^^ - warning: include macro expected single expression in source --> $DIR/expansion-time-include.rs:4:1 | @@ -47,12 +33,12 @@ LL | 2 | ^ | note: the lint level is defined here - --> $DIR/expansion-time.rs:29:8 + --> $DIR/expansion-time.rs:22:8 | LL | #[warn(incomplete_include)] | ^^^^^^^^^^^^^^^^^^ -warning: 4 warnings emitted +warning: 3 warnings emitted Future incompatibility report: Future breakage diagnostic: warning: missing fragment specifier @@ -69,18 +55,3 @@ note: the lint level is defined here LL | #[warn(missing_fragment_specifier)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -Future breakage diagnostic: -warning: use of unstable library feature `test`: `bench` is a part of custom test frameworks which are unstable - --> $DIR/expansion-time.rs:14:7 - | -LL | #[bench] - | ^^^^^ - | - = 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 #64266 -note: the lint level is defined here - --> $DIR/expansion-time.rs:12:8 - | -LL | #[warn(soft_unstable)] - | ^^^^^^^^^^^^^ - diff --git a/tests/ui/lint/for-loops-over-falibles/macro-issue-140747.rs b/tests/ui/lint/for-loops-over-falibles/macro-issue-140747.rs new file mode 100644 index 0000000000000..33a89ced963e9 --- /dev/null +++ b/tests/ui/lint/for-loops-over-falibles/macro-issue-140747.rs @@ -0,0 +1,10 @@ +#![forbid(for_loops_over_fallibles)] + +fn main() { + macro_rules! x { + () => { + None:: + }; + } + for _ in x! {} {} //~ ERROR for loop over an `Option`. This is more readably written as an `if let` statement [for_loops_over_fallibles] +} diff --git a/tests/ui/lint/for-loops-over-falibles/macro-issue-140747.stderr b/tests/ui/lint/for-loops-over-falibles/macro-issue-140747.stderr new file mode 100644 index 0000000000000..550d26045fbc9 --- /dev/null +++ b/tests/ui/lint/for-loops-over-falibles/macro-issue-140747.stderr @@ -0,0 +1,24 @@ +error: for loop over an `Option`. This is more readably written as an `if let` statement + --> $DIR/macro-issue-140747.rs:9:14 + | +LL | for _ in x! {} {} + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/macro-issue-140747.rs:1:11 + | +LL | #![forbid(for_loops_over_fallibles)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: to check pattern in a loop use `while let` + | +LL - for _ in x! {} {} +LL + while let Some(_) = x! {} {} + | +help: consider using `if let` to clear intent + | +LL - for _ in x! {} {} +LL + if let Some(_) = x! {} {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/force-warn/warnings-lint-group.rs b/tests/ui/lint/force-warn/warnings-lint-group.rs index 944070527a159..28de42639d3a4 100644 --- a/tests/ui/lint/force-warn/warnings-lint-group.rs +++ b/tests/ui/lint/force-warn/warnings-lint-group.rs @@ -1,5 +1,8 @@ // --force-warn warnings is an error //@ compile-flags: --force-warn warnings -//@ error-pattern: `warnings` lint group is not supported fn main() {} + +//~? ERROR `warnings` lint group is not supported with ´--force-warn´ +//~? ERROR `warnings` lint group is not supported with ´--force-warn´ +//~? ERROR `warnings` lint group is not supported with ´--force-warn´ diff --git a/tests/ui/lint/implicit_autorefs.fixed b/tests/ui/lint/implicit_autorefs.fixed new file mode 100644 index 0000000000000..454dfe7637261 --- /dev/null +++ b/tests/ui/lint/implicit_autorefs.fixed @@ -0,0 +1,105 @@ +//@ check-pass +//@ run-rustfix + +#![allow(dead_code)] // For the rustfix-ed code. + +use std::mem::ManuallyDrop; +use std::ops::Deref; + +unsafe fn test_const(ptr: *const [u8]) { + let _ = (&(*ptr))[..16]; + //~^ WARN implicit autoref +} + +struct Test { + field: [u8], +} + +unsafe fn test_field(ptr: *const Test) -> *const [u8] { + let l = (&(*ptr).field).len(); + //~^ WARN implicit autoref + + &raw const (&(*ptr).field)[..l - 1] + //~^ WARN implicit autoref +} + +unsafe fn test_builtin_index(a: *mut [String]) { + _ = (&(*a)[0]).len(); + //~^ WARN implicit autoref + + _ = (&(&(*a))[..1][0]).len(); + //~^ WARN implicit autoref + //~^^ WARN implicit autoref +} + +unsafe fn test_overloaded_deref_const(ptr: *const ManuallyDrop) { + let _ = (&(*ptr)).field; + //~^ WARN implicit autoref + let _ = &raw const (&(*ptr)).field; + //~^ WARN implicit autoref +} + +unsafe fn test_overloaded_deref_mut(ptr: *mut ManuallyDrop) { + let _ = (&(*ptr)).field; + //~^ WARN implicit autoref +} + +unsafe fn test_double_overloaded_deref_const(ptr: *const ManuallyDrop>) { + let _ = (&(*ptr)).field; + //~^ WARN implicit autoref +} + +unsafe fn test_manually_overloaded_deref() { + struct W(T); + + impl Deref for W { + type Target = T; + fn deref(&self) -> &T { &self.0 } + } + + let w: W = W(5); + let w = &raw const w; + let _p: *const i32 = &raw const *(&**w); + //~^ WARN implicit autoref +} + +struct Test2 { + // Derefs to `[u8]`. + field: &'static [u8] +} + +fn test_more_manual_deref(ptr: *const Test2) -> usize { + unsafe { (&(*ptr).field).len() } + //~^ WARN implicit autoref +} + +unsafe fn test_no_attr(ptr: *mut ManuallyDrop) { + ptr.write(ManuallyDrop::new(1)); // Should not warn, as `ManuallyDrop::write` is not + // annotated with `#[rustc_no_implicit_auto_ref]` +} + +unsafe fn test_vec_get(ptr: *mut Vec) { + let _ = (&(*ptr)).get(0); + //~^ WARN implicit autoref + let _ = (&(*ptr)).get_unchecked(0); + //~^ WARN implicit autoref + let _ = (&mut (*ptr)).get_mut(0); + //~^ WARN implicit autoref + let _ = (&mut (*ptr)).get_unchecked_mut(0); + //~^ WARN implicit autoref +} + +unsafe fn test_string(ptr: *mut String) { + let _ = (&(*ptr)).len(); + //~^ WARN implicit autoref + let _ = (&(*ptr)).is_empty(); + //~^ WARN implicit autoref +} + +unsafe fn slice_ptr_len_because_of_msrv(slice: *const [T]) { + let _ = (&(&(*slice))[..]).len(); + //~^ WARN implicit autoref + //~^^ WARN implicit autoref +} + +fn main() {} diff --git a/tests/ui/lint/implicit_autorefs.rs b/tests/ui/lint/implicit_autorefs.rs new file mode 100644 index 0000000000000..507d653682839 --- /dev/null +++ b/tests/ui/lint/implicit_autorefs.rs @@ -0,0 +1,105 @@ +//@ check-pass +//@ run-rustfix + +#![allow(dead_code)] // For the rustfix-ed code. + +use std::mem::ManuallyDrop; +use std::ops::Deref; + +unsafe fn test_const(ptr: *const [u8]) { + let _ = (*ptr)[..16]; + //~^ WARN implicit autoref +} + +struct Test { + field: [u8], +} + +unsafe fn test_field(ptr: *const Test) -> *const [u8] { + let l = (*ptr).field.len(); + //~^ WARN implicit autoref + + &raw const (*ptr).field[..l - 1] + //~^ WARN implicit autoref +} + +unsafe fn test_builtin_index(a: *mut [String]) { + _ = (*a)[0].len(); + //~^ WARN implicit autoref + + _ = (*a)[..1][0].len(); + //~^ WARN implicit autoref + //~^^ WARN implicit autoref +} + +unsafe fn test_overloaded_deref_const(ptr: *const ManuallyDrop) { + let _ = (*ptr).field; + //~^ WARN implicit autoref + let _ = &raw const (*ptr).field; + //~^ WARN implicit autoref +} + +unsafe fn test_overloaded_deref_mut(ptr: *mut ManuallyDrop) { + let _ = (*ptr).field; + //~^ WARN implicit autoref +} + +unsafe fn test_double_overloaded_deref_const(ptr: *const ManuallyDrop>) { + let _ = (*ptr).field; + //~^ WARN implicit autoref +} + +unsafe fn test_manually_overloaded_deref() { + struct W(T); + + impl Deref for W { + type Target = T; + fn deref(&self) -> &T { &self.0 } + } + + let w: W = W(5); + let w = &raw const w; + let _p: *const i32 = &raw const **w; + //~^ WARN implicit autoref +} + +struct Test2 { + // Derefs to `[u8]`. + field: &'static [u8] +} + +fn test_more_manual_deref(ptr: *const Test2) -> usize { + unsafe { (*ptr).field.len() } + //~^ WARN implicit autoref +} + +unsafe fn test_no_attr(ptr: *mut ManuallyDrop) { + ptr.write(ManuallyDrop::new(1)); // Should not warn, as `ManuallyDrop::write` is not + // annotated with `#[rustc_no_implicit_auto_ref]` +} + +unsafe fn test_vec_get(ptr: *mut Vec) { + let _ = (*ptr).get(0); + //~^ WARN implicit autoref + let _ = (*ptr).get_unchecked(0); + //~^ WARN implicit autoref + let _ = (*ptr).get_mut(0); + //~^ WARN implicit autoref + let _ = (*ptr).get_unchecked_mut(0); + //~^ WARN implicit autoref +} + +unsafe fn test_string(ptr: *mut String) { + let _ = (*ptr).len(); + //~^ WARN implicit autoref + let _ = (*ptr).is_empty(); + //~^ WARN implicit autoref +} + +unsafe fn slice_ptr_len_because_of_msrv(slice: *const [T]) { + let _ = (*slice)[..].len(); + //~^ WARN implicit autoref + //~^^ WARN implicit autoref +} + +fn main() {} diff --git a/tests/ui/lint/implicit_autorefs.stderr b/tests/ui/lint/implicit_autorefs.stderr new file mode 100644 index 0000000000000..80ba8ae2fd277 --- /dev/null +++ b/tests/ui/lint/implicit_autorefs.stderr @@ -0,0 +1,389 @@ +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:10:13 + | +LL | let _ = (*ptr)[..16]; + | ^^---^^^^^^^ + | | + | this raw pointer has type `*const [u8]` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:10:13 + | +LL | let _ = (*ptr)[..16]; + | ^^^^^^ + = note: `#[warn(dangerous_implicit_autorefs)]` on by default +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr))[..16]; + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:19:13 + | +LL | let l = (*ptr).field.len(); + | ^^---^^^^^^^^^^^^^ + | | + | this raw pointer has type `*const Test` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:19:13 + | +LL | let l = (*ptr).field.len(); + | ^^^^^^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let l = (&(*ptr).field).len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:22:16 + | +LL | &raw const (*ptr).field[..l - 1] + | ^^---^^^^^^^^^^^^^^^^ + | | + | this raw pointer has type `*const Test` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:22:16 + | +LL | &raw const (*ptr).field[..l - 1] + | ^^^^^^^^^^^^ +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | &raw const (&(*ptr).field)[..l - 1] + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:27:9 + | +LL | _ = (*a)[0].len(); + | ^^-^^^^^^^^^^ + | | + | this raw pointer has type `*mut [String]` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&String` + --> $DIR/implicit_autorefs.rs:27:9 + | +LL | _ = (*a)[0].len(); + | ^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/alloc/src/string.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | _ = (&(*a)[0]).len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:30:9 + | +LL | _ = (*a)[..1][0].len(); + | ^^-^^^^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut [String]` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&String` + --> $DIR/implicit_autorefs.rs:30:9 + | +LL | _ = (*a)[..1][0].len(); + | ^^^^^^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/alloc/src/string.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | _ = (&(*a)[..1][0]).len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:30:9 + | +LL | _ = (*a)[..1][0].len(); + | ^^-^^^^^^ + | | + | this raw pointer has type `*mut [String]` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[String]` + --> $DIR/implicit_autorefs.rs:30:9 + | +LL | _ = (*a)[..1][0].len(); + | ^^^^ +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | _ = (&(*a))[..1][0].len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:36:13 + | +LL | let _ = (*ptr).field; + | ^^---^^^^^^^ + | | + | this raw pointer has type `*const ManuallyDrop` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements + = note: references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).field; + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:38:24 + | +LL | let _ = &raw const (*ptr).field; + | ^^---^^^^^^^ + | | + | this raw pointer has type `*const ManuallyDrop` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements + = note: references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = &raw const (&(*ptr)).field; + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:43:13 + | +LL | let _ = (*ptr).field; + | ^^---^^^^^^^ + | | + | this raw pointer has type `*mut ManuallyDrop` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements + = note: references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).field; + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:48:13 + | +LL | let _ = (*ptr).field; + | ^^---^^^^^^^ + | | + | this raw pointer has type `*const ManuallyDrop>` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements + = note: references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).field; + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:62:26 + | +LL | let _p: *const i32 = &raw const **w; + | ^^^^^^^^^^^^^- + | | + | this raw pointer has type `*const W` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&W` + --> $DIR/implicit_autorefs.rs:62:38 + | +LL | let _p: *const i32 = &raw const **w; + | ^^ +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _p: *const i32 = &raw const *(&**w); + | +++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:72:14 + | +LL | unsafe { (*ptr).field.len() } + | ^^---^^^^^^^^^^^^^ + | | + | this raw pointer has type `*const Test2` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:72:14 + | +LL | unsafe { (*ptr).field.len() } + | ^^^^^^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | unsafe { (&(*ptr).field).len() } + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:82:13 + | +LL | let _ = (*ptr).get(0); + | ^^---^^^^^^^^ + | | + | this raw pointer has type `*mut Vec` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:82:13 + | +LL | let _ = (*ptr).get(0); + | ^^^^^^ +note: method calls to `get` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).get(0); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:84:13 + | +LL | let _ = (*ptr).get_unchecked(0); + | ^^---^^^^^^^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut Vec` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[u8]` + --> $DIR/implicit_autorefs.rs:84:13 + | +LL | let _ = (*ptr).get_unchecked(0); + | ^^^^^^ +note: method calls to `get_unchecked` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).get_unchecked(0); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:86:13 + | +LL | let _ = (*ptr).get_mut(0); + | ^^---^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut Vec` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&mut [u8]` + --> $DIR/implicit_autorefs.rs:86:13 + | +LL | let _ = (*ptr).get_mut(0); + | ^^^^^^ +note: method calls to `get_mut` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&mut (*ptr)).get_mut(0); + | +++++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:88:13 + | +LL | let _ = (*ptr).get_unchecked_mut(0); + | ^^---^^^^^^^^^^^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut Vec` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&mut [u8]` + --> $DIR/implicit_autorefs.rs:88:13 + | +LL | let _ = (*ptr).get_unchecked_mut(0); + | ^^^^^^ +note: method calls to `get_unchecked_mut` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&mut (*ptr)).get_unchecked_mut(0); + | +++++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:93:13 + | +LL | let _ = (*ptr).len(); + | ^^---^^^^^^^ + | | + | this raw pointer has type `*mut String` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&String` + --> $DIR/implicit_autorefs.rs:93:13 + | +LL | let _ = (*ptr).len(); + | ^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/alloc/src/string.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:95:13 + | +LL | let _ = (*ptr).is_empty(); + | ^^---^^^^^^^^^^^^ + | | + | this raw pointer has type `*mut String` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&String` + --> $DIR/implicit_autorefs.rs:95:13 + | +LL | let _ = (*ptr).is_empty(); + | ^^^^^^ +note: method calls to `is_empty` require a reference + --> $SRC_DIR/alloc/src/string.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).is_empty(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:100:13 + | +LL | let _ = (*slice)[..].len(); + | ^^-----^^^^^^^^^^^ + | | + | this raw pointer has type `*const [T]` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[T]` + --> $DIR/implicit_autorefs.rs:100:13 + | +LL | let _ = (*slice)[..].len(); + | ^^^^^^^^^^^^ +note: method calls to `len` require a reference + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*slice)[..]).len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:100:13 + | +LL | let _ = (*slice)[..].len(); + | ^^-----^^^^^ + | | + | this raw pointer has type `*const [T]` + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +note: autoref is being applied to this expression, resulting in: `&[T]` + --> $DIR/implicit_autorefs.rs:100:13 + | +LL | let _ = (*slice)[..].len(); + | ^^^^^^^^ +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*slice))[..].len(); + | ++ + + +warning: 20 warnings emitted + diff --git a/tests/ui/lint/inert-attr-macro.rs b/tests/ui/lint/inert-attr-macro.rs index 90303a1fc3d1c..f2d50e30aecc2 100644 --- a/tests/ui/lint/inert-attr-macro.rs +++ b/tests/ui/lint/inert-attr-macro.rs @@ -17,4 +17,10 @@ fn main() { // This does work, since the attribute is on a parent // of the macro invocation. #[allow(warnings)] { #[inline] foo!(); } + + // Ok, `cfg` and `cfg_attr` are expanded eagerly and do not warn. + #[cfg(true)] foo!(); + #[cfg(false)] foo!(); + #[cfg_attr(true, cfg(true))] foo!(); + #[cfg_attr(false, nonexistent)] foo!(); } diff --git a/tests/ui/lint/inline-exported.rs b/tests/ui/lint/inline-exported.rs new file mode 100644 index 0000000000000..6a23cd58236ff --- /dev/null +++ b/tests/ui/lint/inline-exported.rs @@ -0,0 +1,28 @@ +//! Ensure the unused_attributes lint fires for externally exported functions with `#[inline]`, +//! because `#[inline]` is ignored for such functions. + +#![crate_type = "lib"] +#![feature(linkage)] +#![deny(unused_attributes)] + +#[inline] +//~^ ERROR: `#[inline]` is ignored on externally exported functions +#[no_mangle] +fn no_mangle() {} + +#[inline] +//~^ ERROR: `#[inline]` is ignored on externally exported functions +#[export_name = "export_name"] +fn export_name() {} + +#[inline] +//~^ ERROR: `#[inline]` is ignored on externally exported functions +#[linkage = "external"] +fn external_linkage() {} + +#[inline] +fn normal() {} + +#[inline] +#[linkage = "internal"] // not exported +fn internal_linkage() {} diff --git a/tests/ui/lint/inline-exported.stderr b/tests/ui/lint/inline-exported.stderr new file mode 100644 index 0000000000000..05a2bda24067e --- /dev/null +++ b/tests/ui/lint/inline-exported.stderr @@ -0,0 +1,31 @@ +error: `#[inline]` is ignored on externally exported functions + --> $DIR/inline-exported.rs:8:1 + | +LL | #[inline] + | ^^^^^^^^^ + | + = help: externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]` +note: the lint level is defined here + --> $DIR/inline-exported.rs:6:9 + | +LL | #![deny(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + +error: `#[inline]` is ignored on externally exported functions + --> $DIR/inline-exported.rs:13:1 + | +LL | #[inline] + | ^^^^^^^^^ + | + = help: externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]` + +error: `#[inline]` is ignored on externally exported functions + --> $DIR/inline-exported.rs:18:1 + | +LL | #[inline] + | ^^^^^^^^^ + | + = help: externally exported functions are functions with `#[no_mangle]`, `#[export_name]`, or `#[linkage]` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/lint/internal_features.rs b/tests/ui/lint/internal_features.rs index 32ce9540cb364..6456078a5c2fa 100644 --- a/tests/ui/lint/internal_features.rs +++ b/tests/ui/lint/internal_features.rs @@ -4,8 +4,7 @@ //~^ ERROR: internal //~| ERROR: internal -extern "rust-intrinsic" { - fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); -} +#[rustc_intrinsic] +unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); fn main() {} diff --git a/tests/ui/lint/invalid_from_utf8.rs b/tests/ui/lint/invalid_from_utf8.rs index 87a906761c075..cbc1d8e90459d 100644 --- a/tests/ui/lint/invalid_from_utf8.rs +++ b/tests/ui/lint/invalid_from_utf8.rs @@ -128,18 +128,21 @@ pub fn from_utf8() { } pub fn from_utf8_with_indirections() { - let mut a = [99, 108, 130, 105, 112, 112, 121]; - std::str::from_utf8_mut(&mut a); - //~^ WARN calls to `std::str::from_utf8_mut` - str::from_utf8_mut(&mut a); - //~^ WARN calls to `str::from_utf8_mut` - let mut b = &mut a; - let mut c = b; - std::str::from_utf8_mut(c); - //~^ WARN calls to `std::str::from_utf8_mut` - str::from_utf8_mut(c); - //~^ WARN calls to `str::from_utf8_mut` - let mut c = &[99, 108, 130, 105, 112, 112, 121]; + // NOTE: We used to lint on the patterns below, but due to the + // binding being mutable it could be changed between the + // declaration and the call and that would have created a + // false-positive, so until we can reliably avoid those false + // postive we don't lint on them. Example of FP below. + // + // let mut a = [99, 108, 130, 105, 112, 112, 121]; + // std::str::from_utf8_mut(&mut a); + // str::from_utf8_mut(&mut a); + // let mut b = &mut a; + // let mut c = b; + // std::str::from_utf8_mut(c); + // str::from_utf8_mut(c); + + let c = &[99, 108, 130, 105, 112, 112, 121]; std::str::from_utf8(c); //~^ WARN calls to `std::str::from_utf8` str::from_utf8(c); @@ -164,6 +167,20 @@ pub fn from_utf8_with_indirections() { //~^ WARN calls to `std::str::from_utf8` str::from_utf8(INVALID_4); //~^ WARN calls to `str::from_utf8` + + let mut a = [99, 108, 130, 105, 112, 112, 121]; // invalid + loop { + a = [99, 108, 130, 105, 112, 112, 121]; // still invalid, but too complex + break; + } + std::str::from_utf8_mut(&mut a); + + let mut a = [99, 108, 130, 105, 112, 112]; // invalid + loop { + a = *b"clippy"; // valid + break; + } + std::str::from_utf8_mut(&mut a); } fn main() {} diff --git a/tests/ui/lint/invalid_from_utf8.stderr b/tests/ui/lint/invalid_from_utf8.stderr index 3cd4d227fc276..26bee5c403862 100644 --- a/tests/ui/lint/invalid_from_utf8.stderr +++ b/tests/ui/lint/invalid_from_utf8.stderr @@ -202,60 +202,25 @@ LL | str::from_utf8(concat_bytes!(b"cl", b"\x82ippy")); | | | the literal was valid UTF-8 up to the 2 bytes -warning: calls to `std::str::from_utf8_mut` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:132:5 - | -LL | let mut a = [99, 108, 130, 105, 112, 112, 121]; - | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes -LL | std::str::from_utf8_mut(&mut a); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: calls to `str::from_utf8_mut` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:134:5 - | -LL | let mut a = [99, 108, 130, 105, 112, 112, 121]; - | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes -... -LL | str::from_utf8_mut(&mut a); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: calls to `std::str::from_utf8_mut` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:138:5 - | -LL | let mut a = [99, 108, 130, 105, 112, 112, 121]; - | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes -... -LL | std::str::from_utf8_mut(c); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: calls to `str::from_utf8_mut` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:140:5 - | -LL | let mut a = [99, 108, 130, 105, 112, 112, 121]; - | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes -... -LL | str::from_utf8_mut(c); - | ^^^^^^^^^^^^^^^^^^^^^ - warning: calls to `std::str::from_utf8` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:143:5 + --> $DIR/invalid_from_utf8.rs:146:5 | -LL | let mut c = &[99, 108, 130, 105, 112, 112, 121]; - | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes +LL | let c = &[99, 108, 130, 105, 112, 112, 121]; + | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes LL | std::str::from_utf8(c); | ^^^^^^^^^^^^^^^^^^^^^^ warning: calls to `str::from_utf8` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:145:5 + --> $DIR/invalid_from_utf8.rs:148:5 | -LL | let mut c = &[99, 108, 130, 105, 112, 112, 121]; - | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes +LL | let c = &[99, 108, 130, 105, 112, 112, 121]; + | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes ... LL | str::from_utf8(c); | ^^^^^^^^^^^^^^^^^ warning: calls to `std::str::from_utf8` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:148:5 + --> $DIR/invalid_from_utf8.rs:151:5 | LL | const INVALID_1: [u8; 7] = [99, 108, 130, 105, 112, 112, 121]; | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes @@ -263,7 +228,7 @@ LL | std::str::from_utf8(&INVALID_1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: calls to `str::from_utf8` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:150:5 + --> $DIR/invalid_from_utf8.rs:153:5 | LL | const INVALID_1: [u8; 7] = [99, 108, 130, 105, 112, 112, 121]; | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes @@ -272,7 +237,7 @@ LL | str::from_utf8(&INVALID_1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: calls to `std::str::from_utf8` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:153:5 + --> $DIR/invalid_from_utf8.rs:156:5 | LL | static INVALID_2: [u8; 7] = [99, 108, 130, 105, 112, 112, 121]; | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes @@ -280,7 +245,7 @@ LL | std::str::from_utf8(&INVALID_2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: calls to `str::from_utf8` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:155:5 + --> $DIR/invalid_from_utf8.rs:158:5 | LL | static INVALID_2: [u8; 7] = [99, 108, 130, 105, 112, 112, 121]; | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes @@ -289,7 +254,7 @@ LL | str::from_utf8(&INVALID_2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: calls to `std::str::from_utf8` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:158:5 + --> $DIR/invalid_from_utf8.rs:161:5 | LL | const INVALID_3: &'static [u8; 7] = &[99, 108, 130, 105, 112, 112, 121]; | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes @@ -297,7 +262,7 @@ LL | std::str::from_utf8(INVALID_3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: calls to `str::from_utf8` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:160:5 + --> $DIR/invalid_from_utf8.rs:163:5 | LL | const INVALID_3: &'static [u8; 7] = &[99, 108, 130, 105, 112, 112, 121]; | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes @@ -306,7 +271,7 @@ LL | str::from_utf8(INVALID_3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: calls to `std::str::from_utf8` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:163:5 + --> $DIR/invalid_from_utf8.rs:166:5 | LL | const INVALID_4: &'static [u8; 7] = { &[99, 108, 130, 105, 112, 112, 121] }; | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes @@ -314,7 +279,7 @@ LL | std::str::from_utf8(INVALID_4); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: calls to `str::from_utf8` with an invalid literal always return an error - --> $DIR/invalid_from_utf8.rs:165:5 + --> $DIR/invalid_from_utf8.rs:168:5 | LL | const INVALID_4: &'static [u8; 7] = { &[99, 108, 130, 105, 112, 112, 121] }; | ---------------------------------- the literal was valid UTF-8 up to the 2 bytes @@ -322,5 +287,5 @@ LL | const INVALID_4: &'static [u8; 7] = { &[99, 108, 130, 105, 112, 112, 12 LL | str::from_utf8(INVALID_4); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 38 warnings emitted +warning: 34 warnings emitted diff --git a/tests/ui/lint/invalid_null_args.rs b/tests/ui/lint/invalid_null_args.rs new file mode 100644 index 0000000000000..f40f06a0d3624 --- /dev/null +++ b/tests/ui/lint/invalid_null_args.rs @@ -0,0 +1,136 @@ +// check-fail +// run-rustfix +#![allow(unnecessary_transmutes)] + +use std::{mem, ptr}; + +unsafe fn null_ptr() { + ptr::write( + //~^ ERROR calling this function with a null pointer is undefined behavior + ptr::null_mut() as *mut u32, + mem::transmute::<[u8; 4], _>([0, 0, 0, 255]), + ); + + let null_ptr = ptr::null_mut(); + ptr::write( + //~^ ERROR calling this function with a null pointer is undefined behavior + null_ptr as *mut u32, + mem::transmute::<[u8; 4], _>([0, 0, 0, 255]), + ); + + let _: &[usize] = std::slice::from_raw_parts(ptr::null(), 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + let _: &[usize] = std::slice::from_raw_parts(ptr::null_mut(), 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + let _: &[usize] = std::slice::from_raw_parts(0 as *mut _, 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + let _: &[usize] = std::slice::from_raw_parts(mem::transmute(0usize), 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + + let _: &[usize] = std::slice::from_raw_parts_mut(ptr::null_mut(), 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + + ptr::copy::(ptr::null(), ptr::NonNull::dangling().as_ptr(), 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + ptr::copy::(ptr::NonNull::dangling().as_ptr(), ptr::null_mut(), 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + + ptr::copy_nonoverlapping::(ptr::null(), ptr::NonNull::dangling().as_ptr(), 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + ptr::copy_nonoverlapping::( + //~^ ERROR calling this function with a null pointer is undefined behavior + ptr::NonNull::dangling().as_ptr(), + ptr::null_mut(), + 0, + ); + + #[derive(Copy, Clone)] + struct A(usize); + let mut v = A(200); + + let _a: A = ptr::read(ptr::null()); + //~^ ERROR calling this function with a null pointer is undefined behavior + let _a: A = ptr::read(ptr::null_mut()); + //~^ ERROR calling this function with a null pointer is undefined behavior + + let _a: A = ptr::read_unaligned(ptr::null()); + //~^ ERROR calling this function with a null pointer is undefined behavior + let _a: A = ptr::read_unaligned(ptr::null_mut()); + //~^ ERROR calling this function with a null pointer is undefined behavior + + let _a: A = ptr::read_volatile(ptr::null()); + //~^ ERROR calling this function with a null pointer is undefined behavior + let _a: A = ptr::read_volatile(ptr::null_mut()); + //~^ ERROR calling this function with a null pointer is undefined behavior + + let _a: A = ptr::replace(ptr::null_mut(), v); + //~^ ERROR calling this function with a null pointer is undefined behavior + + ptr::swap::(ptr::null_mut(), &mut v); + //~^ ERROR calling this function with a null pointer is undefined behavior + ptr::swap::(&mut v, ptr::null_mut()); + //~^ ERROR calling this function with a null pointer is undefined behavior + + ptr::swap_nonoverlapping::(ptr::null_mut(), &mut v, 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + ptr::swap_nonoverlapping::(&mut v, ptr::null_mut(), 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + + ptr::write(ptr::null_mut(), v); + //~^ ERROR calling this function with a null pointer is undefined behavior + + ptr::write_unaligned(ptr::null_mut(), v); + //~^ ERROR calling this function with a null pointer is undefined behavior + + ptr::write_volatile(ptr::null_mut(), v); + //~^ ERROR calling this function with a null pointer is undefined behavior + + ptr::write_bytes::(ptr::null_mut(), 42, 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + + // with indirections + let const_ptr = null_ptr as *const u8; + let _a: u8 = ptr::read(const_ptr); + //~^ ERROR calling this function with a null pointer is undefined behavior +} + +unsafe fn zst() { + struct Zst; // zero-sized type + + std::slice::from_raw_parts::<()>(ptr::null(), 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + std::slice::from_raw_parts::(ptr::null(), 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + std::slice::from_raw_parts_mut::<()>(ptr::null_mut(), 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + std::slice::from_raw_parts_mut::(ptr::null_mut(), 0); + //~^ ERROR calling this function with a null pointer is undefined behavior + + ptr::read::<()>(ptr::null()); + ptr::read::(ptr::null()); + + ptr::write(ptr::null_mut(), ()); + ptr::write(ptr::null_mut(), Zst); + + ptr::copy(ptr::null::<()>(), ptr::null_mut::<()>(), 1); + ptr::copy(ptr::null::(), ptr::null_mut::(), 1); +} + +unsafe fn not_invalid() { + // Simplified false-positive from std quicksort implementation + + let mut a = ptr::null_mut(); + let mut b = (); + + loop { + if false { + break; + } + + a = &raw mut b; + } + + ptr::write(a, ()); +} + +fn main() {} diff --git a/tests/ui/lint/invalid_null_args.stderr b/tests/ui/lint/invalid_null_args.stderr new file mode 100644 index 0000000000000..11c6270cfb78e --- /dev/null +++ b/tests/ui/lint/invalid_null_args.stderr @@ -0,0 +1,330 @@ +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:8:5 + | +LL | / ptr::write( +LL | | +LL | | ptr::null_mut() as *mut u32, + | | --------------- null pointer originates from here +LL | | mem::transmute::<[u8; 4], _>([0, 0, 0, 255]), +LL | | ); + | |_____^ + | + = help: for more information, visit and + = note: `#[deny(invalid_null_arguments)]` on by default + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:15:5 + | +LL | / ptr::write( +LL | | +LL | | null_ptr as *mut u32, +LL | | mem::transmute::<[u8; 4], _>([0, 0, 0, 255]), +LL | | ); + | |_____^ + | + = help: for more information, visit and +note: null pointer originates from here + --> $DIR/invalid_null_args.rs:14:20 + | +LL | let null_ptr = ptr::null_mut(); + | ^^^^^^^^^^^^^^^ + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:21:23 + | +LL | let _: &[usize] = std::slice::from_raw_parts(ptr::null(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:23:23 + | +LL | let _: &[usize] = std::slice::from_raw_parts(ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:25:23 + | +LL | let _: &[usize] = std::slice::from_raw_parts(0 as *mut _, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:27:23 + | +LL | let _: &[usize] = std::slice::from_raw_parts(mem::transmute(0usize), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------^^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:30:23 + | +LL | let _: &[usize] = std::slice::from_raw_parts_mut(ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:33:5 + | +LL | ptr::copy::(ptr::null(), ptr::NonNull::dangling().as_ptr(), 0); + | ^^^^^^^^^^^^^^^^^^^-----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:35:5 + | +LL | ptr::copy::(ptr::NonNull::dangling().as_ptr(), ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:38:5 + | +LL | ptr::copy_nonoverlapping::(ptr::null(), ptr::NonNull::dangling().as_ptr(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:40:5 + | +LL | / ptr::copy_nonoverlapping::( +LL | | +LL | | ptr::NonNull::dangling().as_ptr(), +LL | | ptr::null_mut(), + | | --------------- null pointer originates from here +LL | | 0, +LL | | ); + | |_____^ + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:51:17 + | +LL | let _a: A = ptr::read(ptr::null()); + | ^^^^^^^^^^-----------^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:53:17 + | +LL | let _a: A = ptr::read(ptr::null_mut()); + | ^^^^^^^^^^---------------^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:56:17 + | +LL | let _a: A = ptr::read_unaligned(ptr::null()); + | ^^^^^^^^^^^^^^^^^^^^-----------^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:58:17 + | +LL | let _a: A = ptr::read_unaligned(ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^---------------^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:61:17 + | +LL | let _a: A = ptr::read_volatile(ptr::null()); + | ^^^^^^^^^^^^^^^^^^^-----------^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:63:17 + | +LL | let _a: A = ptr::read_volatile(ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^---------------^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:66:17 + | +LL | let _a: A = ptr::replace(ptr::null_mut(), v); + | ^^^^^^^^^^^^^---------------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:69:5 + | +LL | ptr::swap::(ptr::null_mut(), &mut v); + | ^^^^^^^^^^^^^^^---------------^^^^^^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:71:5 + | +LL | ptr::swap::(&mut v, ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:74:5 + | +LL | ptr::swap_nonoverlapping::(ptr::null_mut(), &mut v, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^^^^^^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:76:5 + | +LL | ptr::swap_nonoverlapping::(&mut v, ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:79:5 + | +LL | ptr::write(ptr::null_mut(), v); + | ^^^^^^^^^^^---------------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:82:5 + | +LL | ptr::write_unaligned(ptr::null_mut(), v); + | ^^^^^^^^^^^^^^^^^^^^^---------------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:85:5 + | +LL | ptr::write_volatile(ptr::null_mut(), v); + | ^^^^^^^^^^^^^^^^^^^^---------------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:88:5 + | +LL | ptr::write_bytes::(ptr::null_mut(), 42, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:93:18 + | +LL | let _a: u8 = ptr::read(const_ptr); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: for more information, visit and +note: null pointer originates from here + --> $DIR/invalid_null_args.rs:14:20 + | +LL | let null_ptr = ptr::null_mut(); + | ^^^^^^^^^^^^^^^ + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:100:5 + | +LL | std::slice::from_raw_parts::<()>(ptr::null(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:102:5 + | +LL | std::slice::from_raw_parts::(ptr::null(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:104:5 + | +LL | std::slice::from_raw_parts_mut::<()>(ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused + --> $DIR/invalid_null_args.rs:106:5 + | +LL | std::slice::from_raw_parts_mut::(ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ + | | + | null pointer originates from here + | + = help: for more information, visit and + +error: aborting due to 31 previous errors + diff --git a/tests/ui/lint/issue-104897.rs b/tests/ui/lint/issue-104897.rs index 3cfe94bbd222d..99bc1cf3a4298 100644 --- a/tests/ui/lint/issue-104897.rs +++ b/tests/ui/lint/issue-104897.rs @@ -1,5 +1,2 @@ -//@ error-pattern: this file contains an unclosed delimiter -//@ error-pattern: this file contains an unclosed delimiter -//@ error-pattern: this file contains an unclosed delimiter - +//~v ERROR this file contains an unclosed delimiter fn f(){(print!(á diff --git a/tests/ui/lint/issue-104897.stderr b/tests/ui/lint/issue-104897.stderr index 584902ee4c03c..ebc794bfb7301 100644 --- a/tests/ui/lint/issue-104897.stderr +++ b/tests/ui/lint/issue-104897.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-104897.rs:5:18 + --> $DIR/issue-104897.rs:2:18 | LL | fn f(){(print!(á | -- - ^ diff --git a/tests/ui/lint/issue-121070-let-range.rs b/tests/ui/lint/issue-121070-let-range.rs index 1f575cfaca556..82878587f8d7b 100644 --- a/tests/ui/lint/issue-121070-let-range.rs +++ b/tests/ui/lint/issue-121070-let-range.rs @@ -1,6 +1,6 @@ //@ check-pass +//@ edition:2024 -#![feature(let_chains)] #![allow(irrefutable_let_patterns)] fn main() { let _a = 0..1; diff --git a/tests/ui/lint/issue-99387.rs b/tests/ui/lint/issue-99387.rs index 6f08223945640..b40d31384bed9 100644 --- a/tests/ui/lint/issue-99387.rs +++ b/tests/ui/lint/issue-99387.rs @@ -6,6 +6,7 @@ pub type Successors<'a> = impl Iterator; +#[define_opaque(Successors)] pub fn f<'a>() -> Successors<'a> { None.into_iter() } diff --git a/tests/ui/lint/issue-99387.stderr b/tests/ui/lint/issue-99387.stderr index 4eee4f3639297..0d9ded23c7849 100644 --- a/tests/ui/lint/issue-99387.stderr +++ b/tests/ui/lint/issue-99387.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-99387.rs:22:5 + --> $DIR/issue-99387.rs:23:5 | LL | pub type Successors<'a> = impl Iterator; | ---------------------------- the expected opaque type @@ -11,8 +11,8 @@ LL | None.into_iter() | = note: expected opaque type `Successors<'a>` found struct `std::option::IntoIter<_>` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/issue-99387.rs:21:8 +note: this item must have a `#[define_opaque(Successors)]` attribute to be able to define hidden types + --> $DIR/issue-99387.rs:22:8 | LL | pub fn ohno<'a>() -> <&'a () as Tr>::Item { | ^^^^ diff --git a/tests/ui/lint/known-tool-in-submodule/submodule.rs b/tests/ui/lint/known-tool-in-submodule/submodule.rs index 0bb2b93d53b73..9c24964e94f92 100644 --- a/tests/ui/lint/known-tool-in-submodule/submodule.rs +++ b/tests/ui/lint/known-tool-in-submodule/submodule.rs @@ -1,4 +1,4 @@ -//@ ignore-test: not a test +//@ ignore-auxiliary (used by `./root.rs`) #[allow(tool::lint)] pub fn foo() {} diff --git a/tests/ui/lint/large_assignments/inline_mir.rs b/tests/ui/lint/large_assignments/inline_mir.rs new file mode 100644 index 0000000000000..fc27b8ff244d1 --- /dev/null +++ b/tests/ui/lint/large_assignments/inline_mir.rs @@ -0,0 +1,24 @@ +#![feature(large_assignments)] +#![deny(large_assignments)] +#![move_size_limit = "1000"] + +//! Tests that with `-Zinline-mir`, we do NOT get an error that points to the +//! implementation of `UnsafeCell` since that is not actionable by the user: +//! +//! ```text +//! error: moving 9999 bytes +//! --> /rustc/FAKE_PREFIX/library/core/src/cell.rs:2054:9 +//! | +//! = note: value moved from here +//! ``` +//! +//! We want the diagnostics to point to the relevant user code. + +//@ build-fail +//@ compile-flags: -Zmir-opt-level=1 -Zinline-mir + +pub fn main() { + let data = [10u8; 9999]; + let cell = std::cell::UnsafeCell::new(data); //~ ERROR large_assignments + std::hint::black_box(cell); +} diff --git a/tests/ui/lint/large_assignments/inline_mir.stderr b/tests/ui/lint/large_assignments/inline_mir.stderr new file mode 100644 index 0000000000000..d9010e24d03be --- /dev/null +++ b/tests/ui/lint/large_assignments/inline_mir.stderr @@ -0,0 +1,15 @@ +error: moving 9999 bytes + --> $DIR/inline_mir.rs:22:16 + | +LL | let cell = std::cell::UnsafeCell::new(data); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value moved from here + | + = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` +note: the lint level is defined here + --> $DIR/inline_mir.rs:2:9 + | +LL | #![deny(large_assignments)] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/let_underscore/issue-119697-extra-let.rs b/tests/ui/lint/let_underscore/issue-119697-extra-let.rs index 84abb933911f5..9782b3191fd68 100644 --- a/tests/ui/lint/let_underscore/issue-119697-extra-let.rs +++ b/tests/ui/lint/let_underscore/issue-119697-extra-let.rs @@ -8,6 +8,7 @@ pub struct Foo { pub type Tait = impl Sized; +#[define_opaque(Tait)] pub fn ice_cold(beverage: Tait) { // Must destructure at least one field of `Foo` let Foo { field } = beverage; @@ -17,5 +18,4 @@ pub fn ice_cold(beverage: Tait) { let _ = field; //~ ERROR non-binding let on a type that has a destructor } - pub fn main() {} diff --git a/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr b/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr index 3ff57ab441dc9..8d5deadd46e21 100644 --- a/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr +++ b/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr @@ -1,5 +1,5 @@ error: non-binding let on a type that has a destructor - --> $DIR/issue-119697-extra-let.rs:15:5 + --> $DIR/issue-119697-extra-let.rs:16:5 | LL | _ = field; | ^^^^^^^^^ @@ -21,7 +21,7 @@ LL + drop(field); | error: non-binding let on a type that has a destructor - --> $DIR/issue-119697-extra-let.rs:17:5 + --> $DIR/issue-119697-extra-let.rs:18:5 | LL | let _ = field; | ^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/linker-warning.stderr b/tests/ui/lint/linker-warning.stderr index 3a2c392fd0312..c678562ab5466 100644 --- a/tests/ui/lint/linker-warning.stderr +++ b/tests/ui/lint/linker-warning.stderr @@ -16,7 +16,7 @@ warning: unused attribute LL | #![allow(linker_messages)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | - = note: the `linker_warnings` lint can only be controlled at the root of a crate that needs to be linked + = note: the `linker_messages` lint can only be controlled at the root of a crate that needs to be linked warning: 2 warnings emitted diff --git a/tests/ui/lint/lint-attr-everywhere-early.rs b/tests/ui/lint/lint-attr-everywhere-early.rs index ae32ec426f232..7ca127d066f56 100644 --- a/tests/ui/lint/lint-attr-everywhere-early.rs +++ b/tests/ui/lint/lint-attr-everywhere-early.rs @@ -40,7 +40,7 @@ struct Associated; impl Associated { #![deny(unsafe_code)] - fn inherent_denied_from_inner() { unsafe {} } //~ usage of an `unsafe` block + fn inherent_denied_from_inner() { unsafe {} } //~ ERROR usage of an `unsafe` block #[deny(while_true)] fn inherent_fn() { while true {} } //~ ERROR denote infinite loops with diff --git a/tests/ui/lint/lint-ctypes-73249-2.rs b/tests/ui/lint/lint-ctypes-73249-2.rs index f30377d6c16e3..31af0e3d381ef 100644 --- a/tests/ui/lint/lint-ctypes-73249-2.rs +++ b/tests/ui/lint/lint-ctypes-73249-2.rs @@ -7,6 +7,7 @@ impl Baz for () {} type Qux = impl Baz; +#[define_opaque(Qux)] fn assign() -> Qux {} trait Foo { diff --git a/tests/ui/lint/lint-ctypes-73249-2.stderr b/tests/ui/lint/lint-ctypes-73249-2.stderr index ef30a406969d3..2d0dfe94f097e 100644 --- a/tests/ui/lint/lint-ctypes-73249-2.stderr +++ b/tests/ui/lint/lint-ctypes-73249-2.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-2.rs:26:21 + --> $DIR/lint-ctypes-73249-2.rs:27:21 | LL | fn lint_me() -> A<()>; | ^^^^^ not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-73249-3.rs b/tests/ui/lint/lint-ctypes-73249-3.rs index ef8ab7e03d2f0..8bdf536bf77e6 100644 --- a/tests/ui/lint/lint-ctypes-73249-3.rs +++ b/tests/ui/lint/lint-ctypes-73249-3.rs @@ -7,6 +7,7 @@ impl Baz for u32 {} type Qux = impl Baz; +#[define_opaque(Qux)] fn assign() -> Qux { 3 } diff --git a/tests/ui/lint/lint-ctypes-73249-3.stderr b/tests/ui/lint/lint-ctypes-73249-3.stderr index e5607ba72e978..e1a313a290651 100644 --- a/tests/ui/lint/lint-ctypes-73249-3.stderr +++ b/tests/ui/lint/lint-ctypes-73249-3.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-3.rs:20:25 + --> $DIR/lint-ctypes-73249-3.rs:21:25 | LL | pub fn lint_me() -> A; | ^ not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-73249-5.rs b/tests/ui/lint/lint-ctypes-73249-5.rs index 083fb6c5fb16d..cc6da59950d7a 100644 --- a/tests/ui/lint/lint-ctypes-73249-5.rs +++ b/tests/ui/lint/lint-ctypes-73249-5.rs @@ -7,6 +7,7 @@ impl Baz for u32 {} type Qux = impl Baz; +#[define_opaque(Qux)] fn assign() -> Qux { 3 } diff --git a/tests/ui/lint/lint-ctypes-73249-5.stderr b/tests/ui/lint/lint-ctypes-73249-5.stderr index fcb106c485d85..c4fa955de05ed 100644 --- a/tests/ui/lint/lint-ctypes-73249-5.stderr +++ b/tests/ui/lint/lint-ctypes-73249-5.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-5.rs:20:25 + --> $DIR/lint-ctypes-73249-5.rs:21:25 | LL | pub fn lint_me() -> A; | ^ not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-73251-1.rs b/tests/ui/lint/lint-ctypes-73251-1.rs index fc11f23a1049c..07ae05be69f6c 100644 --- a/tests/ui/lint/lint-ctypes-73251-1.rs +++ b/tests/ui/lint/lint-ctypes-73251-1.rs @@ -15,6 +15,7 @@ impl Foo for u32 { type Assoc = Qux; } +#[define_opaque(Qux)] fn assign() -> Qux { 1 } diff --git a/tests/ui/lint/lint-ctypes-73251-1.stderr b/tests/ui/lint/lint-ctypes-73251-1.stderr index a3b3ebaac3031..675a9de51cd02 100644 --- a/tests/ui/lint/lint-ctypes-73251-1.stderr +++ b/tests/ui/lint/lint-ctypes-73251-1.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73251-1.rs:23:21 + --> $DIR/lint-ctypes-73251-1.rs:24:21 | LL | fn lint_me() -> ::Assoc; | ^^^^^^^^^^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-73251-2.rs b/tests/ui/lint/lint-ctypes-73251-2.rs index fbe0a58f3b5bb..c47118672e072 100644 --- a/tests/ui/lint/lint-ctypes-73251-2.rs +++ b/tests/ui/lint/lint-ctypes-73251-2.rs @@ -24,10 +24,12 @@ type AliasA = impl TraitA; type AliasB = impl TraitB; +#[define_opaque(AliasA)] fn use_of_a() -> AliasA { 3 } +#[define_opaque(AliasB)] fn use_of_b() -> AliasB { 3 } diff --git a/tests/ui/lint/lint-ctypes-73251-2.stderr b/tests/ui/lint/lint-ctypes-73251-2.stderr index 40a9cd00c50ff..634950b29ed43 100644 --- a/tests/ui/lint/lint-ctypes-73251-2.stderr +++ b/tests/ui/lint/lint-ctypes-73251-2.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `AliasA`, which is not FFI-safe - --> $DIR/lint-ctypes-73251-2.rs:36:21 + --> $DIR/lint-ctypes-73251-2.rs:38:21 | LL | fn lint_me() -> ::Assoc; | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-73251.rs b/tests/ui/lint/lint-ctypes-73251.rs index 68eeb67deeae3..15c1dfcaabf57 100644 --- a/tests/ui/lint/lint-ctypes-73251.rs +++ b/tests/ui/lint/lint-ctypes-73251.rs @@ -13,6 +13,7 @@ impl Foo for () { type Bar = impl Foo; +#[define_opaque(Bar)] fn assign() -> Bar {} extern "C" { diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/lint-ctypes-enum.rs index 0d19d5b534713..b2ef27b833bdb 100644 --- a/tests/ui/lint/lint-ctypes-enum.rs +++ b/tests/ui/lint/lint-ctypes-enum.rs @@ -2,6 +2,8 @@ #![deny(improper_ctypes)] #![feature(ptr_internals)] #![feature(transparent_unions)] +#![feature(repr128)] +#![allow(incomplete_features)] use std::num; @@ -40,6 +42,20 @@ enum Isize { C, } +#[repr(u128)] +enum U128 { + A, + B, + C, +} + +#[repr(i128)] +enum I128 { + A, + B, + C, +} + #[repr(transparent)] struct TransparentStruct(T, std::marker::PhantomData); @@ -71,6 +87,8 @@ extern "C" { fn repr_c(x: ReprC); fn repr_u8(x: U8); fn repr_isize(x: Isize); + fn repr_u128(x: U128); //~ ERROR `extern` block uses type `U128` + fn repr_i128(x: I128); //~ ERROR `extern` block uses type `I128` fn option_ref(x: Option<&'static u8>); fn option_fn(x: Option); fn option_nonnull(x: Option>); diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/lint-ctypes-enum.stderr index a491bd1960563..d5fc844f75607 100644 --- a/tests/ui/lint/lint-ctypes-enum.stderr +++ b/tests/ui/lint/lint-ctypes-enum.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `U`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:68:14 + --> $DIR/lint-ctypes-enum.rs:84:14 | LL | fn uf(x: U); | ^ not FFI-safe @@ -7,7 +7,7 @@ LL | fn uf(x: U); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:9:1 + --> $DIR/lint-ctypes-enum.rs:11:1 | LL | enum U { | ^^^^^^ @@ -18,7 +18,7 @@ LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `B`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:69:14 + --> $DIR/lint-ctypes-enum.rs:85:14 | LL | fn bf(x: B); | ^ not FFI-safe @@ -26,13 +26,13 @@ LL | fn bf(x: B); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:12:1 + --> $DIR/lint-ctypes-enum.rs:14:1 | LL | enum B { | ^^^^^^ error: `extern` block uses type `T`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:70:14 + --> $DIR/lint-ctypes-enum.rs:86:14 | LL | fn tf(x: T); | ^ not FFI-safe @@ -40,13 +40,39 @@ LL | fn tf(x: T); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:16:1 + --> $DIR/lint-ctypes-enum.rs:18:1 | LL | enum T { | ^^^^^^ +error: `extern` block uses type `U128`, which is not FFI-safe + --> $DIR/lint-ctypes-enum.rs:90:21 + | +LL | fn repr_u128(x: U128); + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI +note: the type is defined here + --> $DIR/lint-ctypes-enum.rs:46:1 + | +LL | enum U128 { + | ^^^^^^^^^ + +error: `extern` block uses type `I128`, which is not FFI-safe + --> $DIR/lint-ctypes-enum.rs:91:21 + | +LL | fn repr_i128(x: I128); + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI +note: the type is defined here + --> $DIR/lint-ctypes-enum.rs:53:1 + | +LL | enum I128 { + | ^^^^^^^^^ + error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:82:31 + --> $DIR/lint-ctypes-enum.rs:100:31 | LL | fn option_nonzero_u128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -54,7 +80,7 @@ LL | fn option_nonzero_u128(x: Option>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:89:31 + --> $DIR/lint-ctypes-enum.rs:107:31 | LL | fn option_nonzero_i128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -62,7 +88,7 @@ LL | fn option_nonzero_i128(x: Option>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:94:36 + --> $DIR/lint-ctypes-enum.rs:112:36 | LL | fn option_transparent_union(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -71,7 +97,7 @@ LL | fn option_transparent_union(x: Option = note: enum has no representation hint error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:96:28 + --> $DIR/lint-ctypes-enum.rs:114:28 | LL | fn option_repr_rust(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -80,7 +106,7 @@ LL | fn option_repr_rust(x: Option>>); = note: enum has no representation hint error: `extern` block uses type `Option`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:97:21 + --> $DIR/lint-ctypes-enum.rs:115:21 | LL | fn option_u8(x: Option); | ^^^^^^^^^^ not FFI-safe @@ -89,7 +115,7 @@ LL | fn option_u8(x: Option); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:107:33 + --> $DIR/lint-ctypes-enum.rs:125:33 | LL | fn result_nonzero_u128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -97,7 +123,7 @@ LL | fn result_nonzero_u128_t(x: Result, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:114:33 + --> $DIR/lint-ctypes-enum.rs:132:33 | LL | fn result_nonzero_i128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -105,7 +131,7 @@ LL | fn result_nonzero_i128_t(x: Result, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:119:38 + --> $DIR/lint-ctypes-enum.rs:137:38 | LL | fn result_transparent_union_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -114,7 +140,7 @@ LL | fn result_transparent_union_t(x: Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:121:30 + --> $DIR/lint-ctypes-enum.rs:139:30 | LL | fn result_repr_rust_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -123,7 +149,7 @@ LL | fn result_repr_rust_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `Result, U>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:125:51 + --> $DIR/lint-ctypes-enum.rs:143:51 | LL | fn result_1zst_exhaustive_single_variant_t(x: Result, U>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -132,7 +158,7 @@ LL | fn result_1zst_exhaustive_single_variant_t(x: Result, = note: enum has no representation hint error: `extern` block uses type `Result, B>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:127:53 + --> $DIR/lint-ctypes-enum.rs:145:53 | LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result, B>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -141,7 +167,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result = note: enum has no representation hint error: `extern` block uses type `Result, NonExhaustive>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:129:51 + --> $DIR/lint-ctypes-enum.rs:147:51 | LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, NonExhaustive>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -150,7 +176,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, = note: enum has no representation hint error: `extern` block uses type `Result, Field>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:132:49 + --> $DIR/lint-ctypes-enum.rs:150:49 | LL | fn result_1zst_exhaustive_single_field_t(x: Result, Field>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -159,7 +185,7 @@ LL | fn result_1zst_exhaustive_single_field_t(x: Result, Fi = note: enum has no representation hint error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:134:30 + --> $DIR/lint-ctypes-enum.rs:152:30 | LL | fn result_cascading_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -168,7 +194,7 @@ LL | fn result_cascading_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:145:33 + --> $DIR/lint-ctypes-enum.rs:163:33 | LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -176,7 +202,7 @@ LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:152:33 + --> $DIR/lint-ctypes-enum.rs:170:33 | LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -184,7 +210,7 @@ LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:157:38 + --> $DIR/lint-ctypes-enum.rs:175:38 | LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -193,7 +219,7 @@ LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:159:30 + --> $DIR/lint-ctypes-enum.rs:177:30 | LL | fn result_repr_rust_e(x: Result<(), Rust>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -202,7 +228,7 @@ LL | fn result_repr_rust_e(x: Result<(), Rust>>); = note: enum has no representation hint error: `extern` block uses type `Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:163:51 + --> $DIR/lint-ctypes-enum.rs:181:51 | LL | fn result_1zst_exhaustive_single_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -211,7 +237,7 @@ LL | fn result_1zst_exhaustive_single_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:165:53 + --> $DIR/lint-ctypes-enum.rs:183:53 | LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -220,7 +246,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:167:51 + --> $DIR/lint-ctypes-enum.rs:185:51 | LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -229,7 +255,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:170:49 + --> $DIR/lint-ctypes-enum.rs:188:49 | LL | fn result_1zst_exhaustive_single_field_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -238,7 +264,7 @@ LL | fn result_1zst_exhaustive_single_field_e(x: Result>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:172:30 + --> $DIR/lint-ctypes-enum.rs:190:30 | LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -247,7 +273,7 @@ LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); = note: enum has no representation hint error: `extern` block uses type `Result<(), ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:174:27 + --> $DIR/lint-ctypes-enum.rs:192:27 | LL | fn result_unit_t_e(x: Result<(), ()>); | ^^^^^^^^^^^^^^ not FFI-safe @@ -255,5 +281,5 @@ LL | fn result_unit_t_e(x: Result<(), ()>); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint -error: aborting due to 27 previous errors +error: aborting due to 29 previous errors diff --git a/tests/ui/lint/lint-incoherent-auto-trait-objects.rs b/tests/ui/lint/lint-incoherent-auto-trait-objects.rs index d53b514476025..d34e6658435d2 100644 --- a/tests/ui/lint/lint-incoherent-auto-trait-objects.rs +++ b/tests/ui/lint/lint-incoherent-auto-trait-objects.rs @@ -4,16 +4,13 @@ impl Foo for dyn Send {} impl Foo for dyn Send + Send {} //~^ ERROR conflicting implementations -//~| hard error impl Foo for dyn Send + Sync {} impl Foo for dyn Sync + Send {} //~^ ERROR conflicting implementations -//~| hard error impl Foo for dyn Send + Sync + Send {} //~^ ERROR conflicting implementations -//~| hard error fn main() {} diff --git a/tests/ui/lint/lint-incoherent-auto-trait-objects.stderr b/tests/ui/lint/lint-incoherent-auto-trait-objects.stderr index 553ab3869b338..28e8f74672da3 100644 --- a/tests/ui/lint/lint-incoherent-auto-trait-objects.stderr +++ b/tests/ui/lint/lint-incoherent-auto-trait-objects.stderr @@ -1,4 +1,4 @@ -error: conflicting implementations of trait `Foo` for type `(dyn Send + 'static)`: (E0119) +error[E0119]: conflicting implementations of trait `Foo` for type `(dyn Send + 'static)` --> $DIR/lint-incoherent-auto-trait-objects.rs:5:1 | LL | impl Foo for dyn Send {} @@ -6,76 +6,25 @@ LL | impl Foo for dyn Send {} LL | LL | impl Foo for dyn Send + Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + 'static)` - | - = 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 #56484 - = note: `#[deny(order_dependent_trait_objects)]` on by default -error: conflicting implementations of trait `Foo` for type `(dyn Send + Sync + 'static)`: (E0119) - --> $DIR/lint-incoherent-auto-trait-objects.rs:11:1 +error[E0119]: conflicting implementations of trait `Foo` for type `(dyn Send + Sync + 'static)` + --> $DIR/lint-incoherent-auto-trait-objects.rs:10:1 | LL | impl Foo for dyn Send + Sync {} | ---------------------------- first implementation here LL | LL | impl Foo for dyn Sync + Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)` - | - = 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 #56484 -error: conflicting implementations of trait `Foo` for type `(dyn Send + Sync + 'static)`: (E0119) - --> $DIR/lint-incoherent-auto-trait-objects.rs:15:1 +error[E0119]: conflicting implementations of trait `Foo` for type `(dyn Send + Sync + 'static)` + --> $DIR/lint-incoherent-auto-trait-objects.rs:13:1 | -LL | impl Foo for dyn Sync + Send {} +LL | impl Foo for dyn Send + Sync {} | ---------------------------- first implementation here ... LL | impl Foo for dyn Send + Sync + Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)` - | - = 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 #56484 error: aborting due to 3 previous errors -Future incompatibility report: Future breakage diagnostic: -error: conflicting implementations of trait `Foo` for type `(dyn Send + 'static)`: (E0119) - --> $DIR/lint-incoherent-auto-trait-objects.rs:5:1 - | -LL | impl Foo for dyn Send {} - | --------------------- first implementation here -LL | -LL | impl Foo for dyn Send + Send {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + 'static)` - | - = 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 #56484 - = note: `#[deny(order_dependent_trait_objects)]` on by default - -Future breakage diagnostic: -error: conflicting implementations of trait `Foo` for type `(dyn Send + Sync + 'static)`: (E0119) - --> $DIR/lint-incoherent-auto-trait-objects.rs:11:1 - | -LL | impl Foo for dyn Send + Sync {} - | ---------------------------- first implementation here -LL | -LL | impl Foo for dyn Sync + Send {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)` - | - = 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 #56484 - = note: `#[deny(order_dependent_trait_objects)]` on by default - -Future breakage diagnostic: -error: conflicting implementations of trait `Foo` for type `(dyn Send + Sync + 'static)`: (E0119) - --> $DIR/lint-incoherent-auto-trait-objects.rs:15:1 - | -LL | impl Foo for dyn Sync + Send {} - | ---------------------------- first implementation here -... -LL | impl Foo for dyn Send + Sync + Send {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)` - | - = 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 #56484 - = note: `#[deny(order_dependent_trait_objects)]` on by default - +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/lint/lint-pre-expansion-extern-module.rs b/tests/ui/lint/lint-pre-expansion-extern-module.rs index b76879ccbb80e..f1ab0cf3b7425 100644 --- a/tests/ui/lint/lint-pre-expansion-extern-module.rs +++ b/tests/ui/lint/lint-pre-expansion-extern-module.rs @@ -1,7 +1,9 @@ //@ check-pass //@ compile-flags: -W rust-2018-compatibility -//@ error-pattern: `try` is a keyword in the 2018 edition fn main() {} mod lint_pre_expansion_extern_module_aux; + +//~? WARN `try` is a keyword in the 2018 edition +//~? WARN this is accepted in the current edition diff --git a/tests/ui/lint/lint-removed-cmdline-deny.rs b/tests/ui/lint/lint-removed-cmdline-deny.rs index e56a95d292a43..83bbd248aa0c7 100644 --- a/tests/ui/lint/lint-removed-cmdline-deny.rs +++ b/tests/ui/lint/lint-removed-cmdline-deny.rs @@ -2,12 +2,15 @@ // cc #30346 //@ compile-flags:-D renamed-and-removed-lints -D raw_pointer_derive - -//@ error-pattern:lint `raw_pointer_derive` has been removed -//@ error-pattern:requested on the command line with `-D raw_pointer_derive` -//@ error-pattern:requested on the command line with `-D renamed-and-removed-lints` +//@ dont-require-annotations: NOTE #![warn(unused)] #[deny(warnings)] -fn main() { let unused = (); } +fn main() { let unused = (); } //~ ERROR unused variable: `unused` + +//~? ERROR lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok +//~? ERROR lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok +//~? ERROR lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok +//~? NOTE requested on the command line with `-D raw_pointer_derive` +//~? NOTE requested on the command line with `-D renamed-and-removed-lints` diff --git a/tests/ui/lint/lint-removed-cmdline-deny.stderr b/tests/ui/lint/lint-removed-cmdline-deny.stderr index 3321afa7fcd75..2fb237339cd88 100644 --- a/tests/ui/lint/lint-removed-cmdline-deny.stderr +++ b/tests/ui/lint/lint-removed-cmdline-deny.stderr @@ -14,13 +14,13 @@ error: lint `raw_pointer_derive` has been removed: using derive with raw pointer = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: unused variable: `unused` - --> $DIR/lint-removed-cmdline-deny.rs:13:17 + --> $DIR/lint-removed-cmdline-deny.rs:10:17 | LL | fn main() { let unused = (); } | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` | note: the lint level is defined here - --> $DIR/lint-removed-cmdline-deny.rs:12:8 + --> $DIR/lint-removed-cmdline-deny.rs:9:8 | LL | #[deny(warnings)] | ^^^^^^^^ diff --git a/tests/ui/lint/lint-removed-cmdline.rs b/tests/ui/lint/lint-removed-cmdline.rs index 3c9d3eb8e7ba6..f83747a3a6be5 100644 --- a/tests/ui/lint/lint-removed-cmdline.rs +++ b/tests/ui/lint/lint-removed-cmdline.rs @@ -2,12 +2,15 @@ // cc #30346 //@ compile-flags:-D raw_pointer_derive - -//@ error-pattern:lint `raw_pointer_derive` has been removed -//@ error-pattern:`#[warn(renamed_and_removed_lints)]` on by default -//@ error-pattern:requested on the command line with `-D raw_pointer_derive` +//@ dont-require-annotations: NOTE #![warn(unused)] #[deny(warnings)] -fn main() { let unused = (); } +fn main() { let unused = (); } //~ ERROR unused variable: `unused` + +//~? WARN lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok +//~? WARN lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok +//~? WARN lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok +//~? NOTE `#[warn(renamed_and_removed_lints)]` on by default +//~? NOTE requested on the command line with `-D raw_pointer_derive` diff --git a/tests/ui/lint/lint-removed-cmdline.stderr b/tests/ui/lint/lint-removed-cmdline.stderr index fd63433c30867..64e7c572ca736 100644 --- a/tests/ui/lint/lint-removed-cmdline.stderr +++ b/tests/ui/lint/lint-removed-cmdline.stderr @@ -14,13 +14,13 @@ warning: lint `raw_pointer_derive` has been removed: using derive with raw point = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: unused variable: `unused` - --> $DIR/lint-removed-cmdline.rs:13:17 + --> $DIR/lint-removed-cmdline.rs:10:17 | LL | fn main() { let unused = (); } | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` | note: the lint level is defined here - --> $DIR/lint-removed-cmdline.rs:12:8 + --> $DIR/lint-removed-cmdline.rs:9:8 | LL | #[deny(warnings)] | ^^^^^^^^ diff --git a/tests/ui/lint/lint-renamed-cmdline-deny.rs b/tests/ui/lint/lint-renamed-cmdline-deny.rs index 13500d006f86c..c8b0350781548 100644 --- a/tests/ui/lint/lint-renamed-cmdline-deny.rs +++ b/tests/ui/lint/lint-renamed-cmdline-deny.rs @@ -1,10 +1,13 @@ //@ compile-flags:-D renamed-and-removed-lints -D bare_trait_object - -//@ error-pattern:lint `bare_trait_object` has been renamed to `bare_trait_objects` -//@ error-pattern:use the new name `bare_trait_objects` -//@ error-pattern:requested on the command line with `-D bare_trait_object` -//@ error-pattern:requested on the command line with `-D renamed-and-removed-lints` -//@ error-pattern:unused +//@ dont-require-annotations: HELP +//@ dont-require-annotations: NOTE #[deny(unused)] -fn main() { let unused = (); } +fn main() { let unused = (); } //~ ERROR unused variable: `unused` + +//~? ERROR lint `bare_trait_object` has been renamed to `bare_trait_objects` +//~? ERROR lint `bare_trait_object` has been renamed to `bare_trait_objects` +//~? ERROR lint `bare_trait_object` has been renamed to `bare_trait_objects` +//~? HELP use the new name `bare_trait_objects` +//~? NOTE requested on the command line with `-D bare_trait_object` +//~? NOTE requested on the command line with `-D renamed-and-removed-lints` diff --git a/tests/ui/lint/lint-renamed-cmdline-deny.stderr b/tests/ui/lint/lint-renamed-cmdline-deny.stderr index 0e182a4e5dea0..b42b82834c19a 100644 --- a/tests/ui/lint/lint-renamed-cmdline-deny.stderr +++ b/tests/ui/lint/lint-renamed-cmdline-deny.stderr @@ -17,13 +17,13 @@ error: lint `bare_trait_object` has been renamed to `bare_trait_objects` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: unused variable: `unused` - --> $DIR/lint-renamed-cmdline-deny.rs:10:17 + --> $DIR/lint-renamed-cmdline-deny.rs:6:17 | LL | fn main() { let unused = (); } | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` | note: the lint level is defined here - --> $DIR/lint-renamed-cmdline-deny.rs:9:8 + --> $DIR/lint-renamed-cmdline-deny.rs:5:8 | LL | #[deny(unused)] | ^^^^^^ diff --git a/tests/ui/lint/lint-renamed-cmdline.rs b/tests/ui/lint/lint-renamed-cmdline.rs index 7adea98a609f4..757cb514267c0 100644 --- a/tests/ui/lint/lint-renamed-cmdline.rs +++ b/tests/ui/lint/lint-renamed-cmdline.rs @@ -1,9 +1,11 @@ //@ compile-flags:-D bare_trait_object - -//@ error-pattern:lint `bare_trait_object` has been renamed to `bare_trait_objects` -//@ error-pattern:requested on the command line with `-D bare_trait_object` -//@ error-pattern:`#[warn(renamed_and_removed_lints)]` on by default -//@ error-pattern:unused +//@ dont-require-annotations: NOTE #[deny(unused)] -fn main() { let unused = (); } +fn main() { let unused = (); } //~ ERROR unused variable: `unused` + +//~? WARN lint `bare_trait_object` has been renamed to `bare_trait_objects` +//~? WARN lint `bare_trait_object` has been renamed to `bare_trait_objects` +//~? WARN lint `bare_trait_object` has been renamed to `bare_trait_objects` +//~? NOTE requested on the command line with `-D bare_trait_object` +//~? NOTE `#[warn(renamed_and_removed_lints)]` on by default diff --git a/tests/ui/lint/lint-renamed-cmdline.stderr b/tests/ui/lint/lint-renamed-cmdline.stderr index d6bb72f34dce2..efd399e21694b 100644 --- a/tests/ui/lint/lint-renamed-cmdline.stderr +++ b/tests/ui/lint/lint-renamed-cmdline.stderr @@ -17,13 +17,13 @@ warning: lint `bare_trait_object` has been renamed to `bare_trait_objects` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: unused variable: `unused` - --> $DIR/lint-renamed-cmdline.rs:9:17 + --> $DIR/lint-renamed-cmdline.rs:5:17 | LL | fn main() { let unused = (); } | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` | note: the lint level is defined here - --> $DIR/lint-renamed-cmdline.rs:8:8 + --> $DIR/lint-renamed-cmdline.rs:4:8 | LL | #[deny(unused)] | ^^^^^^ diff --git a/tests/ui/lint/lint-stability2.rs b/tests/ui/lint/lint-stability2.rs index 254ec8f9bee1e..056ff5a3e0adb 100644 --- a/tests/ui/lint/lint-stability2.rs +++ b/tests/ui/lint/lint-stability2.rs @@ -1,5 +1,4 @@ //@ aux-build:lint_stability.rs -//@ error-pattern: use of deprecated function #![deny(deprecated)] @@ -9,5 +8,5 @@ extern crate lint_stability; use lint_stability::*; fn main() { - macro_test!(); + macro_test!(); //~ ERROR use of deprecated function `lint_stability::deprecated`: text } diff --git a/tests/ui/lint/lint-stability2.stderr b/tests/ui/lint/lint-stability2.stderr index 3df0c42816793..fa1bce716fc0e 100644 --- a/tests/ui/lint/lint-stability2.stderr +++ b/tests/ui/lint/lint-stability2.stderr @@ -1,11 +1,11 @@ error: use of deprecated function `lint_stability::deprecated`: text - --> $DIR/lint-stability2.rs:12:5 + --> $DIR/lint-stability2.rs:11:5 | LL | macro_test!(); | ^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/lint-stability2.rs:4:9 + --> $DIR/lint-stability2.rs:3:9 | LL | #![deny(deprecated)] | ^^^^^^^^^^ diff --git a/tests/ui/lint/lint-stability3.rs b/tests/ui/lint/lint-stability3.rs index 3c5652ae03042..1ca346f746ade 100644 --- a/tests/ui/lint/lint-stability3.rs +++ b/tests/ui/lint/lint-stability3.rs @@ -1,5 +1,4 @@ //@ aux-build:lint_stability.rs -//@ error-pattern: use of deprecated function #![deny(deprecated)] #![allow(warnings)] @@ -11,4 +10,5 @@ use lint_stability::*; fn main() { macro_test_arg_nested!(deprecated_text); + //~^ ERROR use of deprecated function `lint_stability::deprecated_text`: text } diff --git a/tests/ui/lint/lint-stability3.stderr b/tests/ui/lint/lint-stability3.stderr index 3a2af452ce200..85f14c6fc17b7 100644 --- a/tests/ui/lint/lint-stability3.stderr +++ b/tests/ui/lint/lint-stability3.stderr @@ -1,11 +1,11 @@ error: use of deprecated function `lint_stability::deprecated_text`: text - --> $DIR/lint-stability3.rs:13:28 + --> $DIR/lint-stability3.rs:12:28 | LL | macro_test_arg_nested!(deprecated_text); | ^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/lint-stability3.rs:4:9 + --> $DIR/lint-stability3.rs:3:9 | LL | #![deny(deprecated)] | ^^^^^^^^^^ diff --git a/tests/ui/lint/lint-unexported-no-mangle.rs b/tests/ui/lint/lint-unexported-no-mangle.rs index 63eeb3374d22d..65a7fd9d7696a 100644 --- a/tests/ui/lint/lint-unexported-no-mangle.rs +++ b/tests/ui/lint/lint-unexported-no-mangle.rs @@ -27,3 +27,10 @@ fn main() { foo(); bar(); } + +//~? WARN lint `private_no_mangle_fns` has been removed +//~? WARN lint `private_no_mangle_statics` has been removed +//~? WARN lint `private_no_mangle_fns` has been removed +//~? WARN lint `private_no_mangle_statics` has been removed +//~? WARN lint `private_no_mangle_fns` has been removed +//~? WARN lint `private_no_mangle_statics` has been removed diff --git a/tests/ui/lint/lint-unknown-lint-cmdline-deny.rs b/tests/ui/lint/lint-unknown-lint-cmdline-deny.rs index c92c3999ce90d..ac001e1b6a04d 100644 --- a/tests/ui/lint/lint-unknown-lint-cmdline-deny.rs +++ b/tests/ui/lint/lint-unknown-lint-cmdline-deny.rs @@ -1,9 +1,16 @@ //@ compile-flags:-D unknown-lints -D bogus -D dead_cod - -//@ error-pattern:unknown lint: `bogus` -//@ error-pattern:requested on the command line with `-D bogus` -//@ error-pattern:requested on the command line with `-D dead_cod` -//@ error-pattern:requested on the command line with `-D unknown-lints` -//@ error-pattern:did you mean: `dead_code` +//@ dont-require-annotations: HELP +//@ dont-require-annotations: NOTE fn main() { } + +//~? ERROR unknown lint: `bogus` +//~? ERROR unknown lint: `dead_cod` +//~? ERROR unknown lint: `bogus` +//~? ERROR unknown lint: `dead_cod` +//~? ERROR unknown lint: `bogus` +//~? ERROR unknown lint: `dead_cod` +//~? NOTE requested on the command line with `-D bogus` +//~? NOTE requested on the command line with `-D dead_cod` +//~? NOTE requested on the command line with `-D unknown-lints` +//~? HELP did you mean: `dead_code` diff --git a/tests/ui/lint/lint-unknown-lint-cmdline.rs b/tests/ui/lint/lint-unknown-lint-cmdline.rs index 202c617235fae..7eb8c1f73142f 100644 --- a/tests/ui/lint/lint-unknown-lint-cmdline.rs +++ b/tests/ui/lint/lint-unknown-lint-cmdline.rs @@ -1,11 +1,17 @@ //@ check-pass //@ compile-flags:-D bogus -D dead_cod - -//@ error-pattern:unknown lint: `bogus` -//@ error-pattern:requested on the command line with `-D bogus` -//@ error-pattern:`#[warn(unknown_lints)]` on by default -//@ error-pattern:unknown lint: `dead_cod` -//@ error-pattern:requested on the command line with `-D dead_cod` -//@ error-pattern:did you mean: `dead_code` +//@ dont-require-annotations: HELP +//@ dont-require-annotations: NOTE fn main() { } + +//~? WARN unknown lint: `bogus` +//~? WARN unknown lint: `dead_cod` +//~? WARN unknown lint: `bogus` +//~? WARN unknown lint: `dead_cod` +//~? WARN unknown lint: `bogus` +//~? WARN unknown lint: `dead_cod` +//~? NOTE requested on the command line with `-D bogus` +//~? NOTE `#[warn(unknown_lints)]` on by default +//~? NOTE requested on the command line with `-D dead_cod` +//~? HELP did you mean: `dead_code` diff --git a/tests/ui/lint/lint_pre_expansion_extern_module_aux.rs b/tests/ui/lint/lint_pre_expansion_extern_module_aux.rs index 6e16a796ff19d..10e3c0f956440 100644 --- a/tests/ui/lint/lint_pre_expansion_extern_module_aux.rs +++ b/tests/ui/lint/lint_pre_expansion_extern_module_aux.rs @@ -1,3 +1,3 @@ -//@ ignore-test: not a test +//@ ignore-auxiliary (used by `./lint-pre-expansion-extern-module.rs`) pub fn try() {} diff --git a/tests/ui/lint/lints-in-foreign-macros.rs b/tests/ui/lint/lints-in-foreign-macros.rs index 49e83bae6421f..31b59f4943a53 100644 --- a/tests/ui/lint/lints-in-foreign-macros.rs +++ b/tests/ui/lint/lints-in-foreign-macros.rs @@ -1,7 +1,7 @@ //@ aux-build:lints-in-foreign-macros.rs //@ check-pass -#![warn(unused_imports)] //~ missing documentation for the crate [missing_docs] +#![warn(unused_imports)] //~ WARN missing documentation for the crate [missing_docs] #![warn(missing_docs)] #[macro_use] diff --git a/tests/ui/lint/non-local-defs/cargo-update.rs b/tests/ui/lint/non-local-defs/cargo-update.rs index 8b8c15795d376..f778752b28af7 100644 --- a/tests/ui/lint/non-local-defs/cargo-update.rs +++ b/tests/ui/lint/non-local-defs/cargo-update.rs @@ -8,7 +8,7 @@ // // and since we specifically want to check the presence // of the `cargo update` suggestion we assert it here. -//@ error-pattern: `cargo update -p non_local_macro` +//@ dont-require-annotations: NOTE extern crate non_local_macro; @@ -16,5 +16,6 @@ struct LocalStruct; non_local_macro::non_local_impl!(LocalStruct); //~^ WARN non-local `impl` definition +//~| NOTE `cargo update -p non_local_macro` fn main() {} diff --git a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.cdylib_.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.cdylib_.stderr index 1192b690e29c2..a9c0188674425 100644 --- a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.cdylib_.stderr +++ b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.cdylib_.stderr @@ -1,11 +1,11 @@ error: crate `NonSnakeCase` should have a snake case name - --> $DIR/lint-non-snake-case-crate.rs:36:18 + --> $DIR/lint-non-snake-case-crate.rs:35:18 | LL | #![crate_name = "NonSnakeCase"] | ^^^^^^^^^^^^ help: convert the identifier to snake case: `non_snake_case` | note: the lint level is defined here - --> $DIR/lint-non-snake-case-crate.rs:38:9 + --> $DIR/lint-non-snake-case-crate.rs:37:9 | LL | #![deny(non_snake_case)] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.dylib_.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.dylib_.stderr index 1192b690e29c2..a9c0188674425 100644 --- a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.dylib_.stderr +++ b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.dylib_.stderr @@ -1,11 +1,11 @@ error: crate `NonSnakeCase` should have a snake case name - --> $DIR/lint-non-snake-case-crate.rs:36:18 + --> $DIR/lint-non-snake-case-crate.rs:35:18 | LL | #![crate_name = "NonSnakeCase"] | ^^^^^^^^^^^^ help: convert the identifier to snake case: `non_snake_case` | note: the lint level is defined here - --> $DIR/lint-non-snake-case-crate.rs:38:9 + --> $DIR/lint-non-snake-case-crate.rs:37:9 | LL | #![deny(non_snake_case)] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.lib_.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.lib_.stderr index 1192b690e29c2..a9c0188674425 100644 --- a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.lib_.stderr +++ b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.lib_.stderr @@ -1,11 +1,11 @@ error: crate `NonSnakeCase` should have a snake case name - --> $DIR/lint-non-snake-case-crate.rs:36:18 + --> $DIR/lint-non-snake-case-crate.rs:35:18 | LL | #![crate_name = "NonSnakeCase"] | ^^^^^^^^^^^^ help: convert the identifier to snake case: `non_snake_case` | note: the lint level is defined here - --> $DIR/lint-non-snake-case-crate.rs:38:9 + --> $DIR/lint-non-snake-case-crate.rs:37:9 | LL | #![deny(non_snake_case)] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.proc_macro_.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.proc_macro_.stderr index 1192b690e29c2..a9c0188674425 100644 --- a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.proc_macro_.stderr +++ b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.proc_macro_.stderr @@ -1,11 +1,11 @@ error: crate `NonSnakeCase` should have a snake case name - --> $DIR/lint-non-snake-case-crate.rs:36:18 + --> $DIR/lint-non-snake-case-crate.rs:35:18 | LL | #![crate_name = "NonSnakeCase"] | ^^^^^^^^^^^^ help: convert the identifier to snake case: `non_snake_case` | note: the lint level is defined here - --> $DIR/lint-non-snake-case-crate.rs:38:9 + --> $DIR/lint-non-snake-case-crate.rs:37:9 | LL | #![deny(non_snake_case)] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.rlib_.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.rlib_.stderr index 1192b690e29c2..a9c0188674425 100644 --- a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.rlib_.stderr +++ b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.rlib_.stderr @@ -1,11 +1,11 @@ error: crate `NonSnakeCase` should have a snake case name - --> $DIR/lint-non-snake-case-crate.rs:36:18 + --> $DIR/lint-non-snake-case-crate.rs:35:18 | LL | #![crate_name = "NonSnakeCase"] | ^^^^^^^^^^^^ help: convert the identifier to snake case: `non_snake_case` | note: the lint level is defined here - --> $DIR/lint-non-snake-case-crate.rs:38:9 + --> $DIR/lint-non-snake-case-crate.rs:37:9 | LL | #![deny(non_snake_case)] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.rs index 6f701cd27c62c..a63e9c5ddf24e 100644 --- a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.rs +++ b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.rs @@ -10,28 +10,27 @@ // But should fire on non-binary crates. -// FIXME(#132309): dylib crate type is not supported on wasm; we need a proper -// supports-crate-type directive. Also, needs-dynamic-linking should rule out -// musl since it supports neither dylibs nor cdylibs. -//@[dylib_] ignore-wasm -//@[dylib_] ignore-musl -//@[cdylib_] ignore-musl - -//@[dylib_] needs-dynamic-linking +//@[cdylib_] compile-flags: --crate-type=cdylib //@[cdylib_] needs-dynamic-linking -//@[proc_macro_] force-host -//@[proc_macro_] no-prefer-dynamic +//@[cdylib_] needs-crate-type: cdylib -//@[cdylib_] compile-flags: --crate-type=cdylib //@[dylib_] compile-flags: --crate-type=dylib +//@[dylib_] needs-dynamic-linking +//@[dylib_] needs-crate-type: dylib + //@[lib_] compile-flags: --crate-type=lib + +//@[proc_macro_] force-host +//@[proc_macro_] no-prefer-dynamic //@[proc_macro_] compile-flags: --crate-type=proc-macro +// The compiler may emit a warning that causes stderr output that contains a warning this test does +// not wish to check. +//@[proc_macro_] needs-unwind +//@[proc_macro_] needs-crate-type: proc-macro + //@[rlib_] compile-flags: --crate-type=rlib -//@[staticlib_] compile-flags: --crate-type=staticlib -// The compiler may emit a warning that causes stderr output -// that contains a warning this test does not wish to check. -//@[proc_macro_] needs-unwind +//@[staticlib_] compile-flags: --crate-type=staticlib #![crate_name = "NonSnakeCase"] //[cdylib_,dylib_,lib_,proc_macro_,rlib_,staticlib_]~^ ERROR crate `NonSnakeCase` should have a snake case name diff --git a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.staticlib_.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.staticlib_.stderr index 1192b690e29c2..a9c0188674425 100644 --- a/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.staticlib_.stderr +++ b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate.staticlib_.stderr @@ -1,11 +1,11 @@ error: crate `NonSnakeCase` should have a snake case name - --> $DIR/lint-non-snake-case-crate.rs:36:18 + --> $DIR/lint-non-snake-case-crate.rs:35:18 | LL | #![crate_name = "NonSnakeCase"] | ^^^^^^^^^^^^ help: convert the identifier to snake case: `non_snake_case` | note: the lint level is defined here - --> $DIR/lint-non-snake-case-crate.rs:38:9 + --> $DIR/lint-non-snake-case-crate.rs:37:9 | LL | #![deny(non_snake_case)] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/non-snake-case/lint-uppercase-variables.rs b/tests/ui/lint/non-snake-case/lint-uppercase-variables.rs index 59dba536f24b6..aefbe63606a14 100644 --- a/tests/ui/lint/non-snake-case/lint-uppercase-variables.rs +++ b/tests/ui/lint/non-snake-case/lint-uppercase-variables.rs @@ -35,6 +35,9 @@ fn main() { //~^^ ERROR `Foo` is named the same as one of the variants of the type `foo::Foo` //~^^^ WARN unused variable: `Foo` + let _: fn(CamelCase: i32); + //~^ ERROR variable `CamelCase` should have a snake case name + test(1); let _ = Something { X: 0 }; diff --git a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr index 9220828014fda..b0c56003957c9 100644 --- a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr +++ b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr @@ -85,6 +85,12 @@ error: variable `Foo` should have a snake case name LL | fn in_param(Foo: foo::Foo) {} | ^^^ help: convert the identifier to snake case (notice the capitalization): `foo` -error: aborting due to 9 previous errors; 3 warnings emitted +error: variable `CamelCase` should have a snake case name + --> $DIR/lint-uppercase-variables.rs:38:15 + | +LL | let _: fn(CamelCase: i32); + | ^^^^^^^^^ help: convert the identifier to snake case: `camel_case` + +error: aborting due to 10 previous errors; 3 warnings emitted For more information about this error, try `rustc --explain E0170`. diff --git a/tests/ui/lint/opaque-ty-ffi-normalization-cycle.rs b/tests/ui/lint/opaque-ty-ffi-normalization-cycle.rs index c83bca4a4c570..dee77cf48738b 100644 --- a/tests/ui/lint/opaque-ty-ffi-normalization-cycle.rs +++ b/tests/ui/lint/opaque-ty-ffi-normalization-cycle.rs @@ -25,10 +25,12 @@ type AliasA = impl TraitA; type AliasB = impl TraitB; +#[define_opaque(AliasA)] fn use_of_a() -> AliasA { 3 } +#[define_opaque(AliasB)] fn use_of_b() -> AliasB { 3 } diff --git a/tests/ui/lint/opaque-ty-ffi-normalization-cycle.stderr b/tests/ui/lint/opaque-ty-ffi-normalization-cycle.stderr index 9efc187833f00..020eac4febb80 100644 --- a/tests/ui/lint/opaque-ty-ffi-normalization-cycle.stderr +++ b/tests/ui/lint/opaque-ty-ffi-normalization-cycle.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `AliasB`, which is not FFI-safe - --> $DIR/opaque-ty-ffi-normalization-cycle.rs:37:21 + --> $DIR/opaque-ty-ffi-normalization-cycle.rs:39:21 | LL | fn lint_me() -> ::Assoc; | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/lint/opaque-ty-ffi-unsafe.rs b/tests/ui/lint/opaque-ty-ffi-unsafe.rs index 5faeac9ed4c38..97016267fa873 100644 --- a/tests/ui/lint/opaque-ty-ffi-unsafe.rs +++ b/tests/ui/lint/opaque-ty-ffi-unsafe.rs @@ -3,6 +3,7 @@ type A = impl Fn(); +#[define_opaque(A)] pub(crate) fn ret_closure() -> A { || {} } diff --git a/tests/ui/lint/opaque-ty-ffi-unsafe.stderr b/tests/ui/lint/opaque-ty-ffi-unsafe.stderr index 7f5d1792bf12a..5c52f70267141 100644 --- a/tests/ui/lint/opaque-ty-ffi-unsafe.stderr +++ b/tests/ui/lint/opaque-ty-ffi-unsafe.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `A`, which is not FFI-safe - --> $DIR/opaque-ty-ffi-unsafe.rs:11:24 + --> $DIR/opaque-ty-ffi-unsafe.rs:12:24 | LL | pub(crate) fn a(_: A); | ^ not FFI-safe diff --git a/tests/ui/lint/removed-lints/README.md b/tests/ui/lint/removed-lints/README.md new file mode 100644 index 0000000000000..abe7ee04115c3 --- /dev/null +++ b/tests/ui/lint/removed-lints/README.md @@ -0,0 +1,4 @@ +# Removed lints + +This directory contains tests to confirm that lints that have been +removed do not cause errors and produce the appropriate warnings. diff --git a/tests/ui/lint/removed-lints/ptr_cast_add_auto_to_object.rs b/tests/ui/lint/removed-lints/ptr_cast_add_auto_to_object.rs new file mode 100644 index 0000000000000..7fb635487ff2e --- /dev/null +++ b/tests/ui/lint/removed-lints/ptr_cast_add_auto_to_object.rs @@ -0,0 +1,5 @@ +//@ check-pass + +#![deny(ptr_cast_add_auto_to_object)] +//~^ WARN lint `ptr_cast_add_auto_to_object` has been removed +fn main() {} diff --git a/tests/ui/lint/removed-lints/ptr_cast_add_auto_to_object.stderr b/tests/ui/lint/removed-lints/ptr_cast_add_auto_to_object.stderr new file mode 100644 index 0000000000000..36b5e847d807c --- /dev/null +++ b/tests/ui/lint/removed-lints/ptr_cast_add_auto_to_object.stderr @@ -0,0 +1,10 @@ +warning: lint `ptr_cast_add_auto_to_object` has been removed: converted into hard error, see issue #127323 for more information + --> $DIR/ptr_cast_add_auto_to_object.rs:3:9 + | +LL | #![deny(ptr_cast_add_auto_to_object)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(renamed_and_removed_lints)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/lint/removed-lints/undefined_naked_function_abi.rs b/tests/ui/lint/removed-lints/undefined_naked_function_abi.rs new file mode 100644 index 0000000000000..cf3ac66ac86d4 --- /dev/null +++ b/tests/ui/lint/removed-lints/undefined_naked_function_abi.rs @@ -0,0 +1,5 @@ +//@ check-pass + +#![deny(undefined_naked_function_abi)] +//~^ WARN lint `undefined_naked_function_abi` has been removed +fn main() {} diff --git a/tests/ui/lint/removed-lints/undefined_naked_function_abi.stderr b/tests/ui/lint/removed-lints/undefined_naked_function_abi.stderr new file mode 100644 index 0000000000000..5a546688beb5c --- /dev/null +++ b/tests/ui/lint/removed-lints/undefined_naked_function_abi.stderr @@ -0,0 +1,10 @@ +warning: lint `undefined_naked_function_abi` has been removed: converted into hard error, see PR #139001 for more information + --> $DIR/undefined_naked_function_abi.rs:3:9 + | +LL | #![deny(undefined_naked_function_abi)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(renamed_and_removed_lints)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/lint/rfc-2383-lint-reason/no_ice_for_partial_compiler_runs.rs b/tests/ui/lint/rfc-2383-lint-reason/no_ice_for_partial_compiler_runs.rs index 9e38b94b76ce6..11ee2bc852abb 100644 --- a/tests/ui/lint/rfc-2383-lint-reason/no_ice_for_partial_compiler_runs.rs +++ b/tests/ui/lint/rfc-2383-lint-reason/no_ice_for_partial_compiler_runs.rs @@ -1,6 +1,7 @@ // This ensures that ICEs like rust#94953 don't happen //@ check-pass //@ compile-flags: -Z unpretty=expanded +//@ edition: 2015 // This `expect` will create an expectation with an unstable expectation id #[expect(while_true)] diff --git a/tests/ui/lint/rfc-2383-lint-reason/no_ice_for_partial_compiler_runs.stdout b/tests/ui/lint/rfc-2383-lint-reason/no_ice_for_partial_compiler_runs.stdout index d804c1d2d200b..d63abea92302a 100644 --- a/tests/ui/lint/rfc-2383-lint-reason/no_ice_for_partial_compiler_runs.stdout +++ b/tests/ui/lint/rfc-2383-lint-reason/no_ice_for_partial_compiler_runs.stdout @@ -7,6 +7,7 @@ extern crate std; // This ensures that ICEs like rust#94953 don't happen //@ check-pass //@ compile-flags: -Z unpretty=expanded +//@ edition: 2015 // This `expect` will create an expectation with an unstable expectation id #[expect(while_true)] diff --git a/tests/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.rs b/tests/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.rs index a51452f069590..787b8999a1067 100644 --- a/tests/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.rs +++ b/tests/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.rs @@ -1,6 +1,6 @@ #![deny(uncommon_codepoints)] -const µ: f64 = 0.000001; //~ identifier contains a non normalized (NFKC) character: 'µ' +const µ: f64 = 0.000001; //~ ERROR identifier contains a non normalized (NFKC) character: 'µ' //~| WARNING should have an upper case name fn dijkstra() {} diff --git a/tests/ui/lint/static-mut-refs.e2021.stderr b/tests/ui/lint/static-mut-refs.e2021.stderr index 00a2ca99f241c..320e0cee8e8b7 100644 --- a/tests/ui/lint/static-mut-refs.e2021.stderr +++ b/tests/ui/lint/static-mut-refs.e2021.stderr @@ -1,4 +1,4 @@ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:38:18 | LL | let _y = &X; @@ -12,7 +12,7 @@ help: use `&raw const` instead to create a raw pointer LL | let _y = &raw const X; | +++++++++ -warning: creating a mutable reference to mutable static is discouraged +warning: creating a mutable reference to mutable static --> $DIR/static-mut-refs.rs:42:18 | LL | let _y = &mut X; @@ -25,7 +25,7 @@ help: use `&raw mut` instead to create a raw pointer LL | let _y = &raw mut X; | +++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:50:22 | LL | let ref _a = X; @@ -34,7 +34,7 @@ LL | let ref _a = X; = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:54:25 | LL | let (_b, _c) = (&X, &Y); @@ -47,7 +47,7 @@ help: use `&raw const` instead to create a raw pointer LL | let (_b, _c) = (&raw const X, &Y); | +++++++++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:54:29 | LL | let (_b, _c) = (&X, &Y); @@ -60,7 +60,7 @@ help: use `&raw const` instead to create a raw pointer LL | let (_b, _c) = (&X, &raw const Y); | +++++++++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:60:13 | LL | foo(&X); @@ -73,7 +73,7 @@ help: use `&raw const` instead to create a raw pointer LL | foo(&raw const X); | +++++++++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:66:17 | LL | let _ = Z.len(); @@ -82,7 +82,7 @@ LL | let _ = Z.len(); = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:72:33 | LL | let _ = format!("{:?}", Z); @@ -91,7 +91,7 @@ LL | let _ = format!("{:?}", Z); = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:76:18 | LL | let _v = &A.value; @@ -104,7 +104,7 @@ help: use `&raw const` instead to create a raw pointer LL | let _v = &raw const A.value; | +++++++++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:80:18 | LL | let _s = &A.s.value; @@ -117,7 +117,7 @@ help: use `&raw const` instead to create a raw pointer LL | let _s = &raw const A.s.value; | +++++++++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:84:22 | LL | let ref _v = A.value; @@ -126,7 +126,7 @@ LL | let ref _v = A.value; = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a mutable reference to mutable static is discouraged +warning: creating a mutable reference to mutable static --> $DIR/static-mut-refs.rs:14:14 | LL | &mut ($x.0) diff --git a/tests/ui/lint/static-mut-refs.e2024.stderr b/tests/ui/lint/static-mut-refs.e2024.stderr index ff41f316250d1..bf7ffc62ce17b 100644 --- a/tests/ui/lint/static-mut-refs.e2024.stderr +++ b/tests/ui/lint/static-mut-refs.e2024.stderr @@ -1,4 +1,4 @@ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:38:18 | LL | let _y = &X; @@ -12,7 +12,7 @@ help: use `&raw const` instead to create a raw pointer LL | let _y = &raw const X; | +++++++++ -error: creating a mutable reference to mutable static is discouraged +error: creating a mutable reference to mutable static --> $DIR/static-mut-refs.rs:42:18 | LL | let _y = &mut X; @@ -25,7 +25,7 @@ help: use `&raw mut` instead to create a raw pointer LL | let _y = &raw mut X; | +++ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:50:22 | LL | let ref _a = X; @@ -34,7 +34,7 @@ LL | let ref _a = X; = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:54:25 | LL | let (_b, _c) = (&X, &Y); @@ -47,7 +47,7 @@ help: use `&raw const` instead to create a raw pointer LL | let (_b, _c) = (&raw const X, &Y); | +++++++++ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:54:29 | LL | let (_b, _c) = (&X, &Y); @@ -60,7 +60,7 @@ help: use `&raw const` instead to create a raw pointer LL | let (_b, _c) = (&X, &raw const Y); | +++++++++ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:60:13 | LL | foo(&X); @@ -73,7 +73,7 @@ help: use `&raw const` instead to create a raw pointer LL | foo(&raw const X); | +++++++++ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:66:17 | LL | let _ = Z.len(); @@ -82,7 +82,7 @@ LL | let _ = Z.len(); = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:72:33 | LL | let _ = format!("{:?}", Z); @@ -91,7 +91,7 @@ LL | let _ = format!("{:?}", Z); = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:76:18 | LL | let _v = &A.value; @@ -104,7 +104,7 @@ help: use `&raw const` instead to create a raw pointer LL | let _v = &raw const A.value; | +++++++++ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:80:18 | LL | let _s = &A.s.value; @@ -117,7 +117,7 @@ help: use `&raw const` instead to create a raw pointer LL | let _s = &raw const A.s.value; | +++++++++ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:84:22 | LL | let ref _v = A.value; @@ -126,7 +126,7 @@ LL | let ref _v = A.value; = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -error: creating a mutable reference to mutable static is discouraged +error: creating a mutable reference to mutable static --> $DIR/static-mut-refs.rs:14:14 | LL | &mut ($x.0) diff --git a/tests/ui/lint/static-mut-refs.rs b/tests/ui/lint/static-mut-refs.rs index 1040dfcae7a95..d2511fb5b128a 100644 --- a/tests/ui/lint/static-mut-refs.rs +++ b/tests/ui/lint/static-mut-refs.rs @@ -12,8 +12,8 @@ static mut FOO: (u32, u32) = (1, 2); macro_rules! bar { ($x:expr) => { &mut ($x.0) - //[e2021]~^ WARN creating a mutable reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR creating a mutable reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN creating a mutable reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR creating a mutable reference to mutable static [static_mut_refs] }; } @@ -36,54 +36,54 @@ fn main() { unsafe { let _y = &X; - //[e2021]~^ WARN shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR shared reference to mutable static [static_mut_refs] let _y = &mut X; - //[e2021]~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR mutable reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN mutable reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR mutable reference to mutable static [static_mut_refs] let _z = &raw mut X; let _p = &raw const X; let ref _a = X; - //[e2021]~^ WARN shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR shared reference to mutable static [static_mut_refs] let (_b, _c) = (&X, &Y); - //[e2021]~^ WARN shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] - //[e2021]~^^^ WARN shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR shared reference to mutable static [static_mut_refs] + //[e2021]~^^^ WARN shared reference to mutable static [static_mut_refs] + //[e2024]~^^^^ ERROR shared reference to mutable static [static_mut_refs] foo(&X); - //[e2021]~^ WARN shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR shared reference to mutable static [static_mut_refs] static mut Z: &[i32; 3] = &[0, 1, 2]; let _ = Z.len(); - //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN creating a shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static [static_mut_refs] let _ = Z[0]; let _ = format!("{:?}", Z); - //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN creating a shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static [static_mut_refs] let _v = &A.value; - //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN creating a shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static [static_mut_refs] let _s = &A.s.value; - //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN creating a shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static [static_mut_refs] let ref _v = A.value; - //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN creating a shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static [static_mut_refs] let _x = bar!(FOO); diff --git a/tests/ui/lint/test-inner-fn.stderr b/tests/ui/lint/test-inner-fn.stderr index 7a32bc86bf706..bf476a45f772c 100644 --- a/tests/ui/lint/test-inner-fn.stderr +++ b/tests/ui/lint/test-inner-fn.stderr @@ -5,15 +5,12 @@ LL | #[test] | ^^^^^^^ | = note: requested on the command line with `-D unnameable-test-items` - = note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot test inner items --> $DIR/test-inner-fn.rs:13:9 | LL | #[test] | ^^^^^^^ - | - = note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/lint/unknown-lints/other.rs b/tests/ui/lint/unknown-lints/other.rs index f917bff6d6022..7770bc59108d7 100644 --- a/tests/ui/lint/unknown-lints/other.rs +++ b/tests/ui/lint/unknown-lints/other.rs @@ -1,6 +1,4 @@ -//@ ignore-test - -// Companion to allow-in-other-module.rs +//@ ignore-auxiliary (used by `./allow-in-other-module.rs`) // This should not warn. #![allow(not_a_real_lint)] diff --git a/tests/ui/lint/unqualified_local_imports.rs b/tests/ui/lint/unqualified_local_imports.rs index 9de71471342dc..b7036f9c68ea6 100644 --- a/tests/ui/lint/unqualified_local_imports.rs +++ b/tests/ui/lint/unqualified_local_imports.rs @@ -1,4 +1,4 @@ -//@compile-flags: --edition 2018 +//@ edition: 2018 #![feature(unqualified_local_imports)] #![deny(unqualified_local_imports)] diff --git a/tests/ui/lint/unused-borrows.rs b/tests/ui/lint/unused-borrows.rs index 4518522ae00f7..07d783382fae8 100644 --- a/tests/ui/lint/unused-borrows.rs +++ b/tests/ui/lint/unused-borrows.rs @@ -4,24 +4,24 @@ fn foo(_: i32) -> bool { todo!() } fn bar() -> &'static i32 { &42; - //~^ unused + //~^ ERROR unused &mut foo(42); - //~^ unused + //~^ ERROR unused &&42; - //~^ unused + //~^ ERROR unused &&mut 42; - //~^ unused + //~^ ERROR unused &mut &42; - //~^ unused + //~^ ERROR unused let _result = foo(4) && foo(2); // Misplaced semi-colon (perhaps due to reordering of lines) && foo(42); - //~^ unused + //~^ ERROR unused let _ = &42; // ok diff --git a/tests/ui/lint/unused-parens-for-macro-call-with-brace.fixed b/tests/ui/lint/unused-parens-for-macro-call-with-brace.fixed new file mode 100644 index 0000000000000..4c9995fcb61a4 --- /dev/null +++ b/tests/ui/lint/unused-parens-for-macro-call-with-brace.fixed @@ -0,0 +1,28 @@ +//@ run-rustfix + +#![deny(unused_parens)] + +fn main() { + macro_rules! x { + () => { None:: }; + } + + let Some(_) = (x!{}) else { return }; // no error + let Some(_) = (x!{}) else { return }; + //~^ ERROR: unnecessary parentheses around assigned value + + let Some(_) = (x!{}) else { return }; + //~^ ERROR: unnecessary parentheses around pattern + + let _ = x!{}; + let _ = x!{}; + //~^ ERROR: unnecessary parentheses around assigned value + + if let Some(_) = x!{} {}; + if let Some(_) = x!{} {}; + //~^ ERROR: unnecessary parentheses around `let` scrutinee expression + + while let Some(_) = x!{} {}; + while let Some(_) = x!{} {}; + //~^ ERROR: unnecessary parentheses around `let` scrutinee expression +} diff --git a/tests/ui/lint/unused-parens-for-macro-call-with-brace.rs b/tests/ui/lint/unused-parens-for-macro-call-with-brace.rs new file mode 100644 index 0000000000000..59e215a48ccae --- /dev/null +++ b/tests/ui/lint/unused-parens-for-macro-call-with-brace.rs @@ -0,0 +1,28 @@ +//@ run-rustfix + +#![deny(unused_parens)] + +fn main() { + macro_rules! x { + () => { None:: }; + } + + let Some(_) = (x!{}) else { return }; // no error + let Some(_) = ((x!{})) else { return }; + //~^ ERROR: unnecessary parentheses around assigned value + + let Some((_)) = (x!{}) else { return }; + //~^ ERROR: unnecessary parentheses around pattern + + let _ = x!{}; + let _ = (x!{}); + //~^ ERROR: unnecessary parentheses around assigned value + + if let Some(_) = x!{} {}; + if let Some(_) = (x!{}) {}; + //~^ ERROR: unnecessary parentheses around `let` scrutinee expression + + while let Some(_) = x!{} {}; + while let Some(_) = (x!{}) {}; + //~^ ERROR: unnecessary parentheses around `let` scrutinee expression +} diff --git a/tests/ui/lint/unused-parens-for-macro-call-with-brace.stderr b/tests/ui/lint/unused-parens-for-macro-call-with-brace.stderr new file mode 100644 index 0000000000000..8d3b4fe493e22 --- /dev/null +++ b/tests/ui/lint/unused-parens-for-macro-call-with-brace.stderr @@ -0,0 +1,67 @@ +error: unnecessary parentheses around assigned value + --> $DIR/unused-parens-for-macro-call-with-brace.rs:11:19 + | +LL | let Some(_) = ((x!{})) else { return }; + | ^ ^ + | +note: the lint level is defined here + --> $DIR/unused-parens-for-macro-call-with-brace.rs:3:9 + | +LL | #![deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - let Some(_) = ((x!{})) else { return }; +LL + let Some(_) = (x!{}) else { return }; + | + +error: unnecessary parentheses around pattern + --> $DIR/unused-parens-for-macro-call-with-brace.rs:14:14 + | +LL | let Some((_)) = (x!{}) else { return }; + | ^ ^ + | +help: remove these parentheses + | +LL - let Some((_)) = (x!{}) else { return }; +LL + let Some(_) = (x!{}) else { return }; + | + +error: unnecessary parentheses around assigned value + --> $DIR/unused-parens-for-macro-call-with-brace.rs:18:13 + | +LL | let _ = (x!{}); + | ^ ^ + | +help: remove these parentheses + | +LL - let _ = (x!{}); +LL + let _ = x!{}; + | + +error: unnecessary parentheses around `let` scrutinee expression + --> $DIR/unused-parens-for-macro-call-with-brace.rs:22:22 + | +LL | if let Some(_) = (x!{}) {}; + | ^ ^ + | +help: remove these parentheses + | +LL - if let Some(_) = (x!{}) {}; +LL + if let Some(_) = x!{} {}; + | + +error: unnecessary parentheses around `let` scrutinee expression + --> $DIR/unused-parens-for-macro-call-with-brace.rs:26:25 + | +LL | while let Some(_) = (x!{}) {}; + | ^ ^ + | +help: remove these parentheses + | +LL - while let Some(_) = (x!{}) {}; +LL + while let Some(_) = x!{} {}; + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui/lint/unused/issue-70041.rs b/tests/ui/lint/unused/issue-70041.rs index 817dfe8211496..890ba378263bc 100644 --- a/tests/ui/lint/unused/issue-70041.rs +++ b/tests/ui/lint/unused/issue-70041.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition=2018 +//@ edition: 2018 //@ run-pass macro_rules! regex { diff --git a/tests/ui/lint/unused/lint-unused-imports-self-single.fixed b/tests/ui/lint/unused/lint-unused-imports-self-single.fixed new file mode 100644 index 0000000000000..361548bfdc12a --- /dev/null +++ b/tests/ui/lint/unused/lint-unused-imports-self-single.fixed @@ -0,0 +1,29 @@ +//@ run-rustfix + +#![deny(unused_imports)] +#![allow(unreachable_code)] + +use std::collections::{self as coll}; +//~^ ERROR unused import: `HashMap` + +//~^ ERROR unused import: `self as std_io` + +use std::sync::Mutex; +//~^ ERROR unused import: `self as std_sync` + +use std::sync::mpsc::Sender; +//~^ ERROR unused import: `self as std_sync_mpsc` + +use std::collections::hash_map::{self as std_coll_hm}; +//~^ ERROR unused import: `Keys` + +use std::borrow::Cow; +//~^ ERROR unused import: `self` + +fn main() { + let _ = coll::BTreeSet::::default(); + let _ = Mutex::new(String::new()); + let _: Cow<'static, str> = "foo".into(); + let _: Sender = todo!(); + let _: std_coll_hm::Entry<'static, u32, u32> = todo!(); +} diff --git a/tests/ui/lint/unused/lint-unused-imports-self-single.rs b/tests/ui/lint/unused/lint-unused-imports-self-single.rs new file mode 100644 index 0000000000000..d03d3822e04e7 --- /dev/null +++ b/tests/ui/lint/unused/lint-unused-imports-self-single.rs @@ -0,0 +1,30 @@ +//@ run-rustfix + +#![deny(unused_imports)] +#![allow(unreachable_code)] + +use std::collections::{HashMap, self as coll}; +//~^ ERROR unused import: `HashMap` + +use std::io::{self as std_io}; +//~^ ERROR unused import: `self as std_io` + +use std::sync::{Mutex, self as std_sync}; +//~^ ERROR unused import: `self as std_sync` + +use std::sync::{mpsc::{self as std_sync_mpsc, Sender}}; +//~^ ERROR unused import: `self as std_sync_mpsc` + +use std::collections::{hash_map::{self as std_coll_hm, Keys}}; +//~^ ERROR unused import: `Keys` + +use std::borrow::{self, Cow}; +//~^ ERROR unused import: `self` + +fn main() { + let _ = coll::BTreeSet::::default(); + let _ = Mutex::new(String::new()); + let _: Cow<'static, str> = "foo".into(); + let _: Sender = todo!(); + let _: std_coll_hm::Entry<'static, u32, u32> = todo!(); +} diff --git a/tests/ui/lint/unused/lint-unused-imports-self-single.stderr b/tests/ui/lint/unused/lint-unused-imports-self-single.stderr new file mode 100644 index 0000000000000..70a9b78a66401 --- /dev/null +++ b/tests/ui/lint/unused/lint-unused-imports-self-single.stderr @@ -0,0 +1,44 @@ +error: unused import: `HashMap` + --> $DIR/lint-unused-imports-self-single.rs:6:24 + | +LL | use std::collections::{HashMap, self as coll}; + | ^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-unused-imports-self-single.rs:3:9 + | +LL | #![deny(unused_imports)] + | ^^^^^^^^^^^^^^ + +error: unused import: `self as std_io` + --> $DIR/lint-unused-imports-self-single.rs:9:15 + | +LL | use std::io::{self as std_io}; + | ^^^^^^^^^^^^^^ + +error: unused import: `self as std_sync` + --> $DIR/lint-unused-imports-self-single.rs:12:24 + | +LL | use std::sync::{Mutex, self as std_sync}; + | ^^^^^^^^^^^^^^^^ + +error: unused import: `self as std_sync_mpsc` + --> $DIR/lint-unused-imports-self-single.rs:15:24 + | +LL | use std::sync::{mpsc::{self as std_sync_mpsc, Sender}}; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: unused import: `Keys` + --> $DIR/lint-unused-imports-self-single.rs:18:56 + | +LL | use std::collections::{hash_map::{self as std_coll_hm, Keys}}; + | ^^^^ + +error: unused import: `self` + --> $DIR/lint-unused-imports-self-single.rs:21:19 + | +LL | use std::borrow::{self, Cow}; + | ^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/lint/unused/must-use-ops.rs b/tests/ui/lint/unused/must-use-ops.rs index f61cf0fcfcb49..5085dbb58c19f 100644 --- a/tests/ui/lint/unused/must-use-ops.rs +++ b/tests/ui/lint/unused/must-use-ops.rs @@ -6,7 +6,7 @@ #![feature(never_type)] fn deref_never(x: &!) { - // Don't lint for uninhabited typess + // Don't lint for uninhabited types *x; } diff --git a/tests/ui/lint/unused/must_use-unit.rs b/tests/ui/lint/unused/must_use-unit.rs index 4dd4798abb7ce..171ae3257b6c2 100644 --- a/tests/ui/lint/unused/must_use-unit.rs +++ b/tests/ui/lint/unused/must_use-unit.rs @@ -10,7 +10,7 @@ fn bar() -> ! { } fn main() { - foo(); //~ unused return value of `foo` + foo(); //~ ERROR unused return value of `foo` - bar(); //~ unused return value of `bar` + bar(); //~ ERROR unused return value of `bar` } diff --git a/tests/ui/lint/unused/unused-attr-macro-rules.rs b/tests/ui/lint/unused/unused-attr-macro-rules.rs index c0fc280ab1a7c..7a8a1bb1ae523 100644 --- a/tests/ui/lint/unused/unused-attr-macro-rules.rs +++ b/tests/ui/lint/unused/unused-attr-macro-rules.rs @@ -17,7 +17,7 @@ macro_rules! foo2 { () => {}; } -#[cfg(FALSE)] +#[cfg(false)] macro_rules! foo { () => {}; } diff --git a/tests/ui/lint/unused/unused-field-in-pat-field.rs b/tests/ui/lint/unused/unused-field-in-pat-field.rs new file mode 100644 index 0000000000000..34959a1023f95 --- /dev/null +++ b/tests/ui/lint/unused/unused-field-in-pat-field.rs @@ -0,0 +1,21 @@ +//@ check-pass + +// Ensure we collect lint levels from pat fields in structs. + +#![deny(unused_variables)] + +pub struct Foo { + bar: u32, + baz: u32, +} + +pub fn test(foo: Foo) { + let Foo { + #[allow(unused_variables)] + bar, + #[allow(unused_variables)] + baz, + } = foo; +} + +fn main() {} diff --git a/tests/ui/lint/unused/useless-comment.rs b/tests/ui/lint/unused/useless-comment.rs index 7d2e5ab6f2b7f..4ec52f20747da 100644 --- a/tests/ui/lint/unused/useless-comment.rs +++ b/tests/ui/lint/unused/useless-comment.rs @@ -13,7 +13,7 @@ fn foo() { /// a //~ ERROR unused doc comment let x = 12; - /// multi-line //~ unused doc comment + /// multi-line //~ ERROR unused doc comment /// doc comment /// that is unused match x { diff --git a/tests/ui/lint/unused_parens_json_suggestion.fixed b/tests/ui/lint/unused_parens_json_suggestion.fixed index f26bedc3fd50f..a26f292425cf6 100644 --- a/tests/ui/lint/unused_parens_json_suggestion.fixed +++ b/tests/ui/lint/unused_parens_json_suggestion.fixed @@ -1,5 +1,4 @@ //@ compile-flags: --error-format json -//@ error-pattern:unnecessary parentheses //@ run-rustfix // The output for humans should just highlight the whole span without showing @@ -15,6 +14,7 @@ fn main() { // We want to suggest the properly-balanced expression `1 / (2 + 3)`, not // the malformed `1 / (2 + 3` let _a = 1 / (2 + 3); + //~^ ERROR unnecessary parentheses around assigned value f(); } diff --git a/tests/ui/lint/unused_parens_json_suggestion.rs b/tests/ui/lint/unused_parens_json_suggestion.rs index af3d88f71bbfa..1ad85411fbf25 100644 --- a/tests/ui/lint/unused_parens_json_suggestion.rs +++ b/tests/ui/lint/unused_parens_json_suggestion.rs @@ -1,5 +1,4 @@ //@ compile-flags: --error-format json -//@ error-pattern:unnecessary parentheses //@ run-rustfix // The output for humans should just highlight the whole span without showing @@ -15,6 +14,7 @@ fn main() { // We want to suggest the properly-balanced expression `1 / (2 + 3)`, not // the malformed `1 / (2 + 3` let _a = (1 / (2 + 3)); + //~^ ERROR unnecessary parentheses around assigned value f(); } diff --git a/tests/ui/lint/unused_parens_json_suggestion.stderr b/tests/ui/lint/unused_parens_json_suggestion.stderr index 2ce31817d295d..9df3e918ac166 100644 --- a/tests/ui/lint/unused_parens_json_suggestion.stderr +++ b/tests/ui/lint/unused_parens_json_suggestion.stderr @@ -1,11 +1,11 @@ -{"$message_type":"diagnostic","message":"unnecessary parentheses around assigned value","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_json_suggestion.rs","byte_start":636,"byte_end":637,"line_start":17,"line_end":17,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" let _a = (1 / (2 + 3));","highlight_start":14,"highlight_end":15}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_json_suggestion.rs","byte_start":648,"byte_end":649,"line_start":17,"line_end":17,"column_start":26,"column_end":27,"is_primary":true,"text":[{"text":" let _a = (1 / (2 + 3));","highlight_start":26,"highlight_end":27}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"the lint level is defined here","code":null,"level":"note","spans":[{"file_name":"$DIR/unused_parens_json_suggestion.rs","byte_start":439,"byte_end":452,"line_start":11,"line_end":11,"column_start":9,"column_end":22,"is_primary":true,"text":[{"text":"#![deny(unused_parens)]","highlight_start":9,"highlight_end":22}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":null},{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_json_suggestion.rs","byte_start":636,"byte_end":637,"line_start":17,"line_end":17,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" let _a = (1 / (2 + 3));","highlight_start":14,"highlight_end":15}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_json_suggestion.rs","byte_start":648,"byte_end":649,"line_start":17,"line_end":17,"column_start":26,"column_end":27,"is_primary":true,"text":[{"text":" let _a = (1 / (2 + 3));","highlight_start":26,"highlight_end":27}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around assigned value - --> $DIR/unused_parens_json_suggestion.rs:17:14 +{"$message_type":"diagnostic","message":"unnecessary parentheses around assigned value","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_json_suggestion.rs","byte_start":594,"byte_end":595,"line_start":16,"line_end":16,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" let _a = (1 / (2 + 3));","highlight_start":14,"highlight_end":15}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_json_suggestion.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":26,"column_end":27,"is_primary":true,"text":[{"text":" let _a = (1 / (2 + 3));","highlight_start":26,"highlight_end":27}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"the lint level is defined here","code":null,"level":"note","spans":[{"file_name":"$DIR/unused_parens_json_suggestion.rs","byte_start":397,"byte_end":410,"line_start":10,"line_end":10,"column_start":9,"column_end":22,"is_primary":true,"text":[{"text":"#![deny(unused_parens)]","highlight_start":9,"highlight_end":22}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":null},{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_json_suggestion.rs","byte_start":594,"byte_end":595,"line_start":16,"line_end":16,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" let _a = (1 / (2 + 3));","highlight_start":14,"highlight_end":15}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_json_suggestion.rs","byte_start":606,"byte_end":607,"line_start":16,"line_end":16,"column_start":26,"column_end":27,"is_primary":true,"text":[{"text":" let _a = (1 / (2 + 3));","highlight_start":26,"highlight_end":27}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around assigned value + --> $DIR/unused_parens_json_suggestion.rs:16:14 | LL | let _a = (1 / (2 + 3)); | ^ ^ | note: the lint level is defined here - --> $DIR/unused_parens_json_suggestion.rs:11:9 + --> $DIR/unused_parens_json_suggestion.rs:10:9 | LL | #![deny(unused_parens)] | ^^^^^^^^^^^^^ diff --git a/tests/ui/lint/unused_parens_multibyte_recovery.rs b/tests/ui/lint/unused_parens_multibyte_recovery.rs index 630b25d192a44..8f53c7ad26262 100644 --- a/tests/ui/lint/unused_parens_multibyte_recovery.rs +++ b/tests/ui/lint/unused_parens_multibyte_recovery.rs @@ -1,10 +1,7 @@ // ignore-tidy-trailing-newlines -// -//@ error-pattern: this file contains an unclosed delimiter -//@ error-pattern: this file contains an unclosed delimiter -//@ error-pattern: this file contains an unclosed delimiter -// + // Verify that unused parens lint does not try to create a span // which points in the middle of a multibyte character. +//~v ERROR this file contains an unclosed delimiter fn f(){(print!(á \ No newline at end of file diff --git a/tests/ui/lint/unused_parens_multibyte_recovery.stderr b/tests/ui/lint/unused_parens_multibyte_recovery.stderr index ef4089f31f430..770fd6f084e3e 100644 --- a/tests/ui/lint/unused_parens_multibyte_recovery.stderr +++ b/tests/ui/lint/unused_parens_multibyte_recovery.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/unused_parens_multibyte_recovery.rs:10:17 + --> $DIR/unused_parens_multibyte_recovery.rs:7:17 | LL | fn f(){(print!(á | -- - ^ diff --git a/tests/ui/lint/unused_parens_remove_json_suggestion.fixed b/tests/ui/lint/unused_parens_remove_json_suggestion.fixed index 899c24f83ed55..4b08c3e51f610 100644 --- a/tests/ui/lint/unused_parens_remove_json_suggestion.fixed +++ b/tests/ui/lint/unused_parens_remove_json_suggestion.fixed @@ -1,5 +1,4 @@ //@ compile-flags: --error-format json -//@ error-pattern:unnecessary parentheses //@ run-rustfix // The output for humans should just highlight the whole span without showing @@ -16,6 +15,7 @@ fn main() { let _b = false; if _b { + //~^ ERROR unnecessary parentheses around `if` condition println!("hello"); } @@ -27,28 +27,36 @@ fn f() -> bool { let c = false; if c { + //~^ ERROR unnecessary parentheses around `if` condition println!("next"); } if c { + //~^ ERROR unnecessary parentheses around `if` condition println!("prev"); } while false && true { + //~^ ERROR unnecessary parentheses around `while` condition if c { + //~^ ERROR unnecessary parentheses around `if` condition println!("norm"); } } while true && false { + //~^ ERROR unnecessary parentheses around `while` condition for _ in 0 .. 3 { + //~^ ERROR unnecessary parentheses around `for` iterator expression println!("e~") } } for _ in 0 .. 3 { + //~^ ERROR unnecessary parentheses around `for` iterator expression while true && false { + //~^ ERROR unnecessary parentheses around `while` condition println!("e~") } } diff --git a/tests/ui/lint/unused_parens_remove_json_suggestion.rs b/tests/ui/lint/unused_parens_remove_json_suggestion.rs index 7f5d935c4acf8..481e6a19d824e 100644 --- a/tests/ui/lint/unused_parens_remove_json_suggestion.rs +++ b/tests/ui/lint/unused_parens_remove_json_suggestion.rs @@ -1,5 +1,4 @@ //@ compile-flags: --error-format json -//@ error-pattern:unnecessary parentheses //@ run-rustfix // The output for humans should just highlight the whole span without showing @@ -16,6 +15,7 @@ fn main() { let _b = false; if (_b) { + //~^ ERROR unnecessary parentheses around `if` condition println!("hello"); } @@ -27,28 +27,36 @@ fn f() -> bool { let c = false; if(c) { + //~^ ERROR unnecessary parentheses around `if` condition println!("next"); } if (c){ + //~^ ERROR unnecessary parentheses around `if` condition println!("prev"); } while (false && true){ + //~^ ERROR unnecessary parentheses around `while` condition if (c) { + //~^ ERROR unnecessary parentheses around `if` condition println!("norm"); } } while(true && false) { + //~^ ERROR unnecessary parentheses around `while` condition for _ in (0 .. 3){ + //~^ ERROR unnecessary parentheses around `for` iterator expression println!("e~") } } for _ in (0 .. 3) { + //~^ ERROR unnecessary parentheses around `for` iterator expression while (true && false) { + //~^ ERROR unnecessary parentheses around `while` condition println!("e~") } } diff --git a/tests/ui/lint/unused_parens_remove_json_suggestion.stderr b/tests/ui/lint/unused_parens_remove_json_suggestion.stderr index 975de4edfdfca..1b28a4a2dc380 100644 --- a/tests/ui/lint/unused_parens_remove_json_suggestion.stderr +++ b/tests/ui/lint/unused_parens_remove_json_suggestion.stderr @@ -1,11 +1,11 @@ -{"$message_type":"diagnostic","message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":540,"byte_end":541,"line_start":18,"line_end":18,"column_start":8,"column_end":9,"is_primary":true,"text":[{"text":" if (_b) {","highlight_start":8,"highlight_end":9}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":543,"byte_end":544,"line_start":18,"line_end":18,"column_start":11,"column_end":12,"is_primary":true,"text":[{"text":" if (_b) {","highlight_start":11,"highlight_end":12}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"the lint level is defined here","code":null,"level":"note","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":439,"byte_end":452,"line_start":11,"line_end":11,"column_start":9,"column_end":22,"is_primary":true,"text":[{"text":"#![deny(unused_parens)]","highlight_start":9,"highlight_end":22}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":null},{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":540,"byte_end":541,"line_start":18,"line_end":18,"column_start":8,"column_end":9,"is_primary":true,"text":[{"text":" if (_b) {","highlight_start":8,"highlight_end":9}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":543,"byte_end":544,"line_start":18,"line_end":18,"column_start":11,"column_end":12,"is_primary":true,"text":[{"text":" if (_b) {","highlight_start":11,"highlight_end":12}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `if` condition - --> $DIR/unused_parens_remove_json_suggestion.rs:18:8 +{"$message_type":"diagnostic","message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":498,"byte_end":499,"line_start":17,"line_end":17,"column_start":8,"column_end":9,"is_primary":true,"text":[{"text":" if (_b) {","highlight_start":8,"highlight_end":9}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":501,"byte_end":502,"line_start":17,"line_end":17,"column_start":11,"column_end":12,"is_primary":true,"text":[{"text":" if (_b) {","highlight_start":11,"highlight_end":12}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"the lint level is defined here","code":null,"level":"note","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":397,"byte_end":410,"line_start":10,"line_end":10,"column_start":9,"column_end":22,"is_primary":true,"text":[{"text":"#![deny(unused_parens)]","highlight_start":9,"highlight_end":22}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":null},{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":498,"byte_end":499,"line_start":17,"line_end":17,"column_start":8,"column_end":9,"is_primary":true,"text":[{"text":" if (_b) {","highlight_start":8,"highlight_end":9}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":501,"byte_end":502,"line_start":17,"line_end":17,"column_start":11,"column_end":12,"is_primary":true,"text":[{"text":" if (_b) {","highlight_start":11,"highlight_end":12}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `if` condition + --> $DIR/unused_parens_remove_json_suggestion.rs:17:8 | LL | if (_b) { | ^ ^ | note: the lint level is defined here - --> $DIR/unused_parens_remove_json_suggestion.rs:11:9 + --> $DIR/unused_parens_remove_json_suggestion.rs:10:9 | LL | #![deny(unused_parens)] | ^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL + if _b { | "} -{"$message_type":"diagnostic","message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":637,"byte_end":638,"line_start":29,"line_end":29,"column_start":7,"column_end":8,"is_primary":true,"text":[{"text":" if(c) {","highlight_start":7,"highlight_end":8}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":639,"byte_end":640,"line_start":29,"line_end":29,"column_start":9,"column_end":10,"is_primary":true,"text":[{"text":" if(c) {","highlight_start":9,"highlight_end":10}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":637,"byte_end":638,"line_start":29,"line_end":29,"column_start":7,"column_end":8,"is_primary":true,"text":[{"text":" if(c) {","highlight_start":7,"highlight_end":8}],"label":null,"suggested_replacement":" ","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":639,"byte_end":640,"line_start":29,"line_end":29,"column_start":9,"column_end":10,"is_primary":true,"text":[{"text":" if(c) {","highlight_start":9,"highlight_end":10}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `if` condition +{"$message_type":"diagnostic","message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":656,"byte_end":657,"line_start":29,"line_end":29,"column_start":7,"column_end":8,"is_primary":true,"text":[{"text":" if(c) {","highlight_start":7,"highlight_end":8}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":658,"byte_end":659,"line_start":29,"line_end":29,"column_start":9,"column_end":10,"is_primary":true,"text":[{"text":" if(c) {","highlight_start":9,"highlight_end":10}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":656,"byte_end":657,"line_start":29,"line_end":29,"column_start":7,"column_end":8,"is_primary":true,"text":[{"text":" if(c) {","highlight_start":7,"highlight_end":8}],"label":null,"suggested_replacement":" ","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":658,"byte_end":659,"line_start":29,"line_end":29,"column_start":9,"column_end":10,"is_primary":true,"text":[{"text":" if(c) {","highlight_start":9,"highlight_end":10}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `if` condition --> $DIR/unused_parens_remove_json_suggestion.rs:29:7 | LL | if(c) { @@ -29,8 +29,8 @@ LL + if c { | "} -{"$message_type":"diagnostic","message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":683,"byte_end":684,"line_start":33,"line_end":33,"column_start":8,"column_end":9,"is_primary":true,"text":[{"text":" if (c){","highlight_start":8,"highlight_end":9}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":685,"byte_end":686,"line_start":33,"line_end":33,"column_start":10,"column_end":11,"is_primary":true,"text":[{"text":" if (c){","highlight_start":10,"highlight_end":11}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":683,"byte_end":684,"line_start":33,"line_end":33,"column_start":8,"column_end":9,"is_primary":true,"text":[{"text":" if (c){","highlight_start":8,"highlight_end":9}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":685,"byte_end":686,"line_start":33,"line_end":33,"column_start":10,"column_end":11,"is_primary":true,"text":[{"text":" if (c){","highlight_start":10,"highlight_end":11}],"label":null,"suggested_replacement":" ","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `if` condition - --> $DIR/unused_parens_remove_json_suggestion.rs:33:8 +{"$message_type":"diagnostic","message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":764,"byte_end":765,"line_start":34,"line_end":34,"column_start":8,"column_end":9,"is_primary":true,"text":[{"text":" if (c){","highlight_start":8,"highlight_end":9}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":766,"byte_end":767,"line_start":34,"line_end":34,"column_start":10,"column_end":11,"is_primary":true,"text":[{"text":" if (c){","highlight_start":10,"highlight_end":11}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":764,"byte_end":765,"line_start":34,"line_end":34,"column_start":8,"column_end":9,"is_primary":true,"text":[{"text":" if (c){","highlight_start":8,"highlight_end":9}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":766,"byte_end":767,"line_start":34,"line_end":34,"column_start":10,"column_end":11,"is_primary":true,"text":[{"text":" if (c){","highlight_start":10,"highlight_end":11}],"label":null,"suggested_replacement":" ","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `if` condition + --> $DIR/unused_parens_remove_json_suggestion.rs:34:8 | LL | if (c){ | ^ ^ @@ -42,8 +42,8 @@ LL + if c { | "} -{"$message_type":"diagnostic","message":"unnecessary parentheses around `while` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":731,"byte_end":732,"line_start":37,"line_end":37,"column_start":11,"column_end":12,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":11,"highlight_end":12}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":745,"byte_end":746,"line_start":37,"line_end":37,"column_start":25,"column_end":26,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":25,"highlight_end":26}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":731,"byte_end":732,"line_start":37,"line_end":37,"column_start":11,"column_end":12,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":11,"highlight_end":12}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":745,"byte_end":746,"line_start":37,"line_end":37,"column_start":25,"column_end":26,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":25,"highlight_end":26}],"label":null,"suggested_replacement":" ","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `while` condition - --> $DIR/unused_parens_remove_json_suggestion.rs:37:11 +{"$message_type":"diagnostic","message":"unnecessary parentheses around `while` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":874,"byte_end":875,"line_start":39,"line_end":39,"column_start":11,"column_end":12,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":11,"highlight_end":12}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":888,"byte_end":889,"line_start":39,"line_end":39,"column_start":25,"column_end":26,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":25,"highlight_end":26}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":874,"byte_end":875,"line_start":39,"line_end":39,"column_start":11,"column_end":12,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":11,"highlight_end":12}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":888,"byte_end":889,"line_start":39,"line_end":39,"column_start":25,"column_end":26,"is_primary":true,"text":[{"text":" while (false && true){","highlight_start":25,"highlight_end":26}],"label":null,"suggested_replacement":" ","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `while` condition + --> $DIR/unused_parens_remove_json_suggestion.rs:39:11 | LL | while (false && true){ | ^ ^ @@ -55,8 +55,8 @@ LL + while false && true { | "} -{"$message_type":"diagnostic","message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":759,"byte_end":760,"line_start":38,"line_end":38,"column_start":12,"column_end":13,"is_primary":true,"text":[{"text":" if (c) {","highlight_start":12,"highlight_end":13}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":761,"byte_end":762,"line_start":38,"line_end":38,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" if (c) {","highlight_start":14,"highlight_end":15}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":759,"byte_end":760,"line_start":38,"line_end":38,"column_start":12,"column_end":13,"is_primary":true,"text":[{"text":" if (c) {","highlight_start":12,"highlight_end":13}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":761,"byte_end":762,"line_start":38,"line_end":38,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" if (c) {","highlight_start":14,"highlight_end":15}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `if` condition - --> $DIR/unused_parens_remove_json_suggestion.rs:38:12 +{"$message_type":"diagnostic","message":"unnecessary parentheses around `if` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":966,"byte_end":967,"line_start":41,"line_end":41,"column_start":12,"column_end":13,"is_primary":true,"text":[{"text":" if (c) {","highlight_start":12,"highlight_end":13}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":968,"byte_end":969,"line_start":41,"line_end":41,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" if (c) {","highlight_start":14,"highlight_end":15}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":966,"byte_end":967,"line_start":41,"line_end":41,"column_start":12,"column_end":13,"is_primary":true,"text":[{"text":" if (c) {","highlight_start":12,"highlight_end":13}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":968,"byte_end":969,"line_start":41,"line_end":41,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" if (c) {","highlight_start":14,"highlight_end":15}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `if` condition + --> $DIR/unused_parens_remove_json_suggestion.rs:41:12 | LL | if (c) { | ^ ^ @@ -68,8 +68,8 @@ LL + if c { | "} -{"$message_type":"diagnostic","message":"unnecessary parentheses around `while` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":822,"byte_end":823,"line_start":44,"line_end":44,"column_start":10,"column_end":11,"is_primary":true,"text":[{"text":" while(true && false) {","highlight_start":10,"highlight_end":11}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":836,"byte_end":837,"line_start":44,"line_end":44,"column_start":24,"column_end":25,"is_primary":true,"text":[{"text":" while(true && false) {","highlight_start":24,"highlight_end":25}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":822,"byte_end":823,"line_start":44,"line_end":44,"column_start":10,"column_end":11,"is_primary":true,"text":[{"text":" while(true && false) {","highlight_start":10,"highlight_end":11}],"label":null,"suggested_replacement":" ","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":836,"byte_end":837,"line_start":44,"line_end":44,"column_start":24,"column_end":25,"is_primary":true,"text":[{"text":" while(true && false) {","highlight_start":24,"highlight_end":25}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `while` condition - --> $DIR/unused_parens_remove_json_suggestion.rs:44:10 +{"$message_type":"diagnostic","message":"unnecessary parentheses around `while` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1094,"byte_end":1095,"line_start":48,"line_end":48,"column_start":10,"column_end":11,"is_primary":true,"text":[{"text":" while(true && false) {","highlight_start":10,"highlight_end":11}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1108,"byte_end":1109,"line_start":48,"line_end":48,"column_start":24,"column_end":25,"is_primary":true,"text":[{"text":" while(true && false) {","highlight_start":24,"highlight_end":25}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1094,"byte_end":1095,"line_start":48,"line_end":48,"column_start":10,"column_end":11,"is_primary":true,"text":[{"text":" while(true && false) {","highlight_start":10,"highlight_end":11}],"label":null,"suggested_replacement":" ","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1108,"byte_end":1109,"line_start":48,"line_end":48,"column_start":24,"column_end":25,"is_primary":true,"text":[{"text":" while(true && false) {","highlight_start":24,"highlight_end":25}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `while` condition + --> $DIR/unused_parens_remove_json_suggestion.rs:48:10 | LL | while(true && false) { | ^ ^ @@ -81,8 +81,8 @@ LL + while true && false { | "} -{"$message_type":"diagnostic","message":"unnecessary parentheses around `for` iterator expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":857,"byte_end":858,"line_start":45,"line_end":45,"column_start":18,"column_end":19,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){","highlight_start":18,"highlight_end":19}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":864,"byte_end":865,"line_start":45,"line_end":45,"column_start":25,"column_end":26,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){","highlight_start":25,"highlight_end":26}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":857,"byte_end":858,"line_start":45,"line_end":45,"column_start":18,"column_end":19,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){","highlight_start":18,"highlight_end":19}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":864,"byte_end":865,"line_start":45,"line_end":45,"column_start":25,"column_end":26,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){","highlight_start":25,"highlight_end":26}],"label":null,"suggested_replacement":" ","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `for` iterator expression - --> $DIR/unused_parens_remove_json_suggestion.rs:45:18 +{"$message_type":"diagnostic","message":"unnecessary parentheses around `for` iterator expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1193,"byte_end":1194,"line_start":50,"line_end":50,"column_start":18,"column_end":19,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){","highlight_start":18,"highlight_end":19}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1200,"byte_end":1201,"line_start":50,"line_end":50,"column_start":25,"column_end":26,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){","highlight_start":25,"highlight_end":26}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1193,"byte_end":1194,"line_start":50,"line_end":50,"column_start":18,"column_end":19,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){","highlight_start":18,"highlight_end":19}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1200,"byte_end":1201,"line_start":50,"line_end":50,"column_start":25,"column_end":26,"is_primary":true,"text":[{"text":" for _ in (0 .. 3){","highlight_start":25,"highlight_end":26}],"label":null,"suggested_replacement":" ","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `for` iterator expression + --> $DIR/unused_parens_remove_json_suggestion.rs:50:18 | LL | for _ in (0 .. 3){ | ^ ^ @@ -94,8 +94,8 @@ LL + for _ in 0 .. 3 { | "} -{"$message_type":"diagnostic","message":"unnecessary parentheses around `for` iterator expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":924,"byte_end":925,"line_start":50,"line_end":50,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) {","highlight_start":14,"highlight_end":15}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":931,"byte_end":932,"line_start":50,"line_end":50,"column_start":21,"column_end":22,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) {","highlight_start":21,"highlight_end":22}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":924,"byte_end":925,"line_start":50,"line_end":50,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) {","highlight_start":14,"highlight_end":15}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":931,"byte_end":932,"line_start":50,"line_end":50,"column_start":21,"column_end":22,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) {","highlight_start":21,"highlight_end":22}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `for` iterator expression - --> $DIR/unused_parens_remove_json_suggestion.rs:50:14 +{"$message_type":"diagnostic","message":"unnecessary parentheses around `for` iterator expression","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1336,"byte_end":1337,"line_start":56,"line_end":56,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) {","highlight_start":14,"highlight_end":15}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1343,"byte_end":1344,"line_start":56,"line_end":56,"column_start":21,"column_end":22,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) {","highlight_start":21,"highlight_end":22}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1336,"byte_end":1337,"line_start":56,"line_end":56,"column_start":14,"column_end":15,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) {","highlight_start":14,"highlight_end":15}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1343,"byte_end":1344,"line_start":56,"line_end":56,"column_start":21,"column_end":22,"is_primary":true,"text":[{"text":" for _ in (0 .. 3) {","highlight_start":21,"highlight_end":22}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `for` iterator expression + --> $DIR/unused_parens_remove_json_suggestion.rs:56:14 | LL | for _ in (0 .. 3) { | ^ ^ @@ -107,8 +107,8 @@ LL + for _ in 0 .. 3 { | "} -{"$message_type":"diagnostic","message":"unnecessary parentheses around `while` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":949,"byte_end":950,"line_start":51,"line_end":51,"column_start":15,"column_end":16,"is_primary":true,"text":[{"text":" while (true && false) {","highlight_start":15,"highlight_end":16}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":963,"byte_end":964,"line_start":51,"line_end":51,"column_start":29,"column_end":30,"is_primary":true,"text":[{"text":" while (true && false) {","highlight_start":29,"highlight_end":30}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":949,"byte_end":950,"line_start":51,"line_end":51,"column_start":15,"column_end":16,"is_primary":true,"text":[{"text":" while (true && false) {","highlight_start":15,"highlight_end":16}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":963,"byte_end":964,"line_start":51,"line_end":51,"column_start":29,"column_end":30,"is_primary":true,"text":[{"text":" while (true && false) {","highlight_start":29,"highlight_end":30}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `while` condition - --> $DIR/unused_parens_remove_json_suggestion.rs:51:15 +{"$message_type":"diagnostic","message":"unnecessary parentheses around `while` condition","code":{"code":"unused_parens","explanation":null},"level":"error","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1433,"byte_end":1434,"line_start":58,"line_end":58,"column_start":15,"column_end":16,"is_primary":true,"text":[{"text":" while (true && false) {","highlight_start":15,"highlight_end":16}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1447,"byte_end":1448,"line_start":58,"line_end":58,"column_start":29,"column_end":30,"is_primary":true,"text":[{"text":" while (true && false) {","highlight_start":29,"highlight_end":30}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove these parentheses","code":null,"level":"help","spans":[{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1433,"byte_end":1434,"line_start":58,"line_end":58,"column_start":15,"column_end":16,"is_primary":true,"text":[{"text":" while (true && false) {","highlight_start":15,"highlight_end":16}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"$DIR/unused_parens_remove_json_suggestion.rs","byte_start":1447,"byte_end":1448,"line_start":58,"line_end":58,"column_start":29,"column_end":30,"is_primary":true,"text":[{"text":" while (true && false) {","highlight_start":29,"highlight_end":30}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"error: unnecessary parentheses around `while` condition + --> $DIR/unused_parens_remove_json_suggestion.rs:58:15 | LL | while (true && false) { | ^ ^ diff --git a/tests/ui/lint/wasm_c_abi_transition.rs b/tests/ui/lint/wasm_c_abi_transition.rs new file mode 100644 index 0000000000000..411772ae890b7 --- /dev/null +++ b/tests/ui/lint/wasm_c_abi_transition.rs @@ -0,0 +1,57 @@ +//@ compile-flags: --target wasm32-unknown-unknown +//@ needs-llvm-components: webassembly +//@ add-core-stubs +//@ build-fail + +#![feature(no_core, repr_simd)] +#![no_core] +#![crate_type = "lib"] +#![deny(wasm_c_abi)] + +extern crate minicore; +use minicore::*; + +pub extern "C" fn my_fun_trivial(_x: i32, _y: f32) {} + +#[repr(C)] +pub struct MyType(i32, i32); +pub extern "C" fn my_fun(_x: MyType) {} //~ERROR: wasm ABI transition +//~^WARN: previously accepted + +// This one is ABI-safe as it only wraps a single field, +// and the return type can be anything. +#[repr(C)] +pub struct MySafeType(i32); +pub extern "C" fn my_fun_safe(_x: MySafeType) -> MyType { loop {} } + +// This one not ABI-safe due to the alignment. +#[repr(C, align(16))] +pub struct MyAlignedType(i32); +pub extern "C" fn my_fun_aligned(_x: MyAlignedType) {} //~ERROR: wasm ABI transition +//~^WARN: previously accepted + +// Check call-site warning +extern "C" { + fn other_fun(x: MyType); +} + +pub fn call_other_fun(x: MyType) { + unsafe { other_fun(x) } //~ERROR: wasm ABI transition + //~^WARN: previously accepted +} + +// Zero-sized types are safe in both ABIs +#[repr(C)] +pub struct MyZstType; +#[allow(improper_ctypes_definitions)] +pub extern "C" fn zst_safe(_x: (), _y: MyZstType) {} + +// The old and new wasm ABI treats simd types like `v128` the same way, so no +// wasm_c_abi warning should be emitted. +#[repr(simd)] +#[allow(non_camel_case_types)] +pub struct v128([i32; 4]); +#[target_feature(enable = "simd128")] +pub extern "C" fn my_safe_simd(x: v128) -> v128 { x } +//~^ WARN `extern` fn uses type `v128`, which is not FFI-safe +//~| WARN `extern` fn uses type `v128`, which is not FFI-safe diff --git a/tests/ui/lint/wasm_c_abi_transition.stderr b/tests/ui/lint/wasm_c_abi_transition.stderr new file mode 100644 index 0000000000000..b4526bf8d6873 --- /dev/null +++ b/tests/ui/lint/wasm_c_abi_transition.stderr @@ -0,0 +1,114 @@ +warning: `extern` fn uses type `v128`, which is not FFI-safe + --> $DIR/wasm_c_abi_transition.rs:55:35 + | +LL | pub extern "C" fn my_safe_simd(x: v128) -> v128 { x } + | ^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/wasm_c_abi_transition.rs:53:1 + | +LL | pub struct v128([i32; 4]); + | ^^^^^^^^^^^^^^^ + = note: `#[warn(improper_ctypes_definitions)]` on by default + +warning: `extern` fn uses type `v128`, which is not FFI-safe + --> $DIR/wasm_c_abi_transition.rs:55:44 + | +LL | pub extern "C" fn my_safe_simd(x: v128) -> v128 { x } + | ^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/wasm_c_abi_transition.rs:53:1 + | +LL | pub struct v128([i32; 4]); + | ^^^^^^^^^^^^^^^ + +error: this function definition involves an argument of type `MyType` which is affected by the wasm ABI transition + --> $DIR/wasm_c_abi_transition.rs:18:1 + | +LL | pub extern "C" fn my_fun(_x: MyType) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #138762 + = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target +note: the lint level is defined here + --> $DIR/wasm_c_abi_transition.rs:9:9 + | +LL | #![deny(wasm_c_abi)] + | ^^^^^^^^^^ + +error: this function definition involves an argument of type `MyAlignedType` which is affected by the wasm ABI transition + --> $DIR/wasm_c_abi_transition.rs:30:1 + | +LL | pub extern "C" fn my_fun_aligned(_x: MyAlignedType) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #138762 + = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target + +error: this function call involves an argument of type `MyType` which is affected by the wasm ABI transition + --> $DIR/wasm_c_abi_transition.rs:39:14 + | +LL | unsafe { other_fun(x) } + | ^^^^^^^^^^^^ + | + = 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 #138762 + = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target + +error: aborting due to 3 previous errors; 2 warnings emitted + +Future incompatibility report: Future breakage diagnostic: +error: this function definition involves an argument of type `MyType` which is affected by the wasm ABI transition + --> $DIR/wasm_c_abi_transition.rs:18:1 + | +LL | pub extern "C" fn my_fun(_x: MyType) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #138762 + = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target +note: the lint level is defined here + --> $DIR/wasm_c_abi_transition.rs:9:9 + | +LL | #![deny(wasm_c_abi)] + | ^^^^^^^^^^ + +Future breakage diagnostic: +error: this function definition involves an argument of type `MyAlignedType` which is affected by the wasm ABI transition + --> $DIR/wasm_c_abi_transition.rs:30:1 + | +LL | pub extern "C" fn my_fun_aligned(_x: MyAlignedType) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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 #138762 + = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target +note: the lint level is defined here + --> $DIR/wasm_c_abi_transition.rs:9:9 + | +LL | #![deny(wasm_c_abi)] + | ^^^^^^^^^^ + +Future breakage diagnostic: +error: this function call involves an argument of type `MyType` which is affected by the wasm ABI transition + --> $DIR/wasm_c_abi_transition.rs:39:14 + | +LL | unsafe { other_fun(x) } + | ^^^^^^^^^^^^ + | + = 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 #138762 + = help: the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target +note: the lint level is defined here + --> $DIR/wasm_c_abi_transition.rs:9:9 + | +LL | #![deny(wasm_c_abi)] + | ^^^^^^^^^^ + diff --git a/tests/ui/list.rs b/tests/ui/list.rs deleted file mode 100644 index 443c4c9f28f88..0000000000000 --- a/tests/ui/list.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ run-pass - -#![allow(non_camel_case_types)] - -enum list { #[allow(dead_code)] cons(isize, Box), nil, } - -pub fn main() { - list::cons(10, Box::new(list::cons(11, Box::new(list::cons(12, Box::new(list::nil)))))); -} diff --git a/tests/ui/loops/label-on-block-suggest-move.rs b/tests/ui/loops/label-on-block-suggest-move.rs new file mode 100644 index 0000000000000..656034cd0e95f --- /dev/null +++ b/tests/ui/loops/label-on-block-suggest-move.rs @@ -0,0 +1,90 @@ +// see https://github.com/rust-lang/rust/issues/138585 +#![allow(break_with_label_and_loop)] // doesn't work locally + +fn main() { + loop 'a: {} + //~^ ERROR: block label not supported here + //~| HELP: if you meant to label the loop, move this label before the loop + while false 'a: {} + //~^ ERROR: block label not supported here + //~| HELP: if you meant to label the loop, move this label before the loop + for i in [0] 'a: {} + //~^ ERROR: block label not supported here + //~| HELP: if you meant to label the loop, move this label before the loop + 'a: loop { + // first block is parsed as the break expr's value with or without parens + while break 'a 'b: {} 'c: {} + //~^ ERROR: block label not supported here + //~| HELP: if you meant to label the loop, move this label before the loop + while break 'a ('b: {}) 'c: {} + //~^ ERROR: block label not supported here + //~| HELP: if you meant to label the loop, move this label before the loop + + // without the parens, the first block is parsed as the while-loop's body + // (see the 'no errors' section) + // #[allow(break_with_label_and_loop)] (doesn't work locally) + while (break 'a {}) 'c: {} + //~^ ERROR: block label not supported here + //~| HELP: if you meant to label the loop, move this label before the loop + } + + // do not suggest moving the label if there is already a label on the loop + 'a: loop 'b: {} + //~^ ERROR: block label not supported here + //~| HELP: remove this block label + 'a: while false 'b: {} + //~^ ERROR: block label not supported here + //~| HELP: remove this block label + 'a: for i in [0] 'b: {} + //~^ ERROR: block label not supported here + //~| HELP: remove this block label + 'a: loop { + // first block is parsed as the break expr's value with or without parens + 'd: while break 'a 'b: {} 'c: {} + //~^ ERROR: block label not supported here + //~| HELP: remove this block label + 'd: while break 'a ('b: {}) 'c: {} + //~^ ERROR: block label not supported here + //~| HELP: remove this block label + + // without the parens, the first block is parsed as the while-loop's body + // (see the 'no errors' section) + // #[allow(break_with_label_and_loop)] (doesn't work locally) + 'd: while (break 'a {}) 'c: {} + //~^ ERROR: block label not supported here + //~| HELP: remove this block label + } + + // no errors + loop { 'a: {} } + 'a: loop { 'b: {} } + while false { 'a: {} } + 'a: while false { 'b: {} } + for i in [0] { 'a: {} } + 'a: for i in [0] { 'b: {} } + 'a: {} + 'a: { 'b: {} } + 'a: loop { + // first block is parsed as the break expr's value if it is a labeled block + while break 'a 'b: {} {} + 'd: while break 'a 'b: {} {} + while break 'a ('b: {}) {} + 'd: while break 'a ('b: {}) {} + // first block is parsed as the while-loop's body if it has no label + // (the break expr is parsed as having no value), + // so the second block is a normal stmt-block, and the label is allowed + while break 'a {} 'c: {} + while break 'a {} {} + 'd: while break 'a {} 'c: {} + 'd: while break 'a {} {} + } + + // unrelated errors that should not be affected + 'a: 'b: {} + //~^ ERROR: expected `while`, `for`, `loop` or `{` after a label + //~| HELP: consider removing the label + loop { while break 'b: {} {} } + //~^ ERROR: parentheses are required around this expression to avoid confusion with a labeled break expression + //~| HELP: wrap the expression in parentheses + //~| ERROR: `break` or `continue` with no label in the condition of a `while` loop [E0590] +} diff --git a/tests/ui/loops/label-on-block-suggest-move.stderr b/tests/ui/loops/label-on-block-suggest-move.stderr new file mode 100644 index 0000000000000..66866703ca673 --- /dev/null +++ b/tests/ui/loops/label-on-block-suggest-move.stderr @@ -0,0 +1,140 @@ +error: block label not supported here + --> $DIR/label-on-block-suggest-move.rs:5:10 + | +LL | loop 'a: {} + | ^^^ not supported here + | +help: if you meant to label the loop, move this label before the loop + | +LL - loop 'a: {} +LL + 'a: loop {} + | + +error: block label not supported here + --> $DIR/label-on-block-suggest-move.rs:8:17 + | +LL | while false 'a: {} + | ^^^ not supported here + | +help: if you meant to label the loop, move this label before the loop + | +LL - while false 'a: {} +LL + 'a: while false {} + | + +error: block label not supported here + --> $DIR/label-on-block-suggest-move.rs:11:18 + | +LL | for i in [0] 'a: {} + | ^^^ not supported here + | +help: if you meant to label the loop, move this label before the loop + | +LL - for i in [0] 'a: {} +LL + 'a: for i in [0] {} + | + +error: block label not supported here + --> $DIR/label-on-block-suggest-move.rs:16:31 + | +LL | while break 'a 'b: {} 'c: {} + | ^^^ not supported here + | +help: if you meant to label the loop, move this label before the loop + | +LL - while break 'a 'b: {} 'c: {} +LL + 'c: while break 'a 'b: {} {} + | + +error: block label not supported here + --> $DIR/label-on-block-suggest-move.rs:19:33 + | +LL | while break 'a ('b: {}) 'c: {} + | ^^^ not supported here + | +help: if you meant to label the loop, move this label before the loop + | +LL - while break 'a ('b: {}) 'c: {} +LL + 'c: while break 'a ('b: {}) {} + | + +error: block label not supported here + --> $DIR/label-on-block-suggest-move.rs:26:29 + | +LL | while (break 'a {}) 'c: {} + | ^^^ not supported here + | +help: if you meant to label the loop, move this label before the loop + | +LL - while (break 'a {}) 'c: {} +LL + 'c: while (break 'a {}) {} + | + +error: block label not supported here + --> $DIR/label-on-block-suggest-move.rs:32:14 + | +LL | 'a: loop 'b: {} + | ^^^ not supported here + +error: block label not supported here + --> $DIR/label-on-block-suggest-move.rs:35:21 + | +LL | 'a: while false 'b: {} + | ^^^ not supported here + +error: block label not supported here + --> $DIR/label-on-block-suggest-move.rs:38:22 + | +LL | 'a: for i in [0] 'b: {} + | ^^^ not supported here + +error: block label not supported here + --> $DIR/label-on-block-suggest-move.rs:43:35 + | +LL | 'd: while break 'a 'b: {} 'c: {} + | ^^^ not supported here + +error: block label not supported here + --> $DIR/label-on-block-suggest-move.rs:46:37 + | +LL | 'd: while break 'a ('b: {}) 'c: {} + | ^^^ not supported here + +error: block label not supported here + --> $DIR/label-on-block-suggest-move.rs:53:33 + | +LL | 'd: while (break 'a {}) 'c: {} + | ^^^ not supported here + +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/label-on-block-suggest-move.rs:83:9 + | +LL | 'a: 'b: {} + | ^^ expected `while`, `for`, `loop` or `{` after a label + | +help: consider removing the label + | +LL - 'a: 'b: {} +LL + 'b: {} + | + +error: parentheses are required around this expression to avoid confusion with a labeled break expression + --> $DIR/label-on-block-suggest-move.rs:86:24 + | +LL | loop { while break 'b: {} {} } + | ^^^^^^ + | +help: wrap the expression in parentheses + | +LL | loop { while break ('b: {}) {} } + | + + + +error[E0590]: `break` or `continue` with no label in the condition of a `while` loop + --> $DIR/label-on-block-suggest-move.rs:86:18 + | +LL | loop { while break 'b: {} {} } + | ^^^^^^^^^^^^ unlabeled `break` in the condition of a `while` loop + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0590`. diff --git a/tests/ui/lowering/no-name-for-DefPath-issue-133426.rs b/tests/ui/lowering/no-name-for-DefPath-issue-133426.rs new file mode 100644 index 0000000000000..fc3b51b40a545 --- /dev/null +++ b/tests/ui/lowering/no-name-for-DefPath-issue-133426.rs @@ -0,0 +1,20 @@ +//! Test for the crash in #133426, caused by an empty symbol being used for a +//! type name. + +#![allow(incomplete_features)] +#![feature(never_patterns)] + +fn a( + _: impl Iterator< + Item = [(); { + match *todo!() { ! }; //~ ERROR type `!` cannot be dereferenced + }], + >, +) { +} + +fn b(_: impl Iterator) {} +//~^ ERROR associated const equality is incomplete +//~| ERROR expected type, found constant + +fn main() {} diff --git a/tests/ui/lowering/no-name-for-DefPath-issue-133426.stderr b/tests/ui/lowering/no-name-for-DefPath-issue-133426.stderr new file mode 100644 index 0000000000000..555d8eec6babf --- /dev/null +++ b/tests/ui/lowering/no-name-for-DefPath-issue-133426.stderr @@ -0,0 +1,31 @@ +error[E0658]: associated const equality is incomplete + --> $DIR/no-name-for-DefPath-issue-133426.rs:16:23 + | +LL | fn b(_: impl Iterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #92827 for more information + = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0614]: type `!` cannot be dereferenced + --> $DIR/no-name-for-DefPath-issue-133426.rs:10:19 + | +LL | match *todo!() { ! }; + | ^^^^^^^^ can't be dereferenced + +error: expected type, found constant + --> $DIR/no-name-for-DefPath-issue-133426.rs:16:30 + | +LL | fn b(_: impl Iterator) {} + | ---- ^^^^^^^^^^^^^^^^^ unexpected constant + | | + | expected a type because of this associated type + | +note: the associated type is defined here + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0614, E0658. +For more information about an error, try `rustc --explain E0614`. diff --git a/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs b/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs index 3c81127ee65c0..2f2bf57f42b4f 100644 --- a/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs +++ b/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -g --crate-type=rlib -Zdwarf-version=4 +//@ compile-flags: -g --crate-type=rlib -Cdwarf-version=4 pub fn say_hi() { println!("hello there") diff --git a/tests/ui/lto/dwarf-mixed-versions-lto.rs b/tests/ui/lto/dwarf-mixed-versions-lto.rs index 14ef65a868e02..900274eb22f44 100644 --- a/tests/ui/lto/dwarf-mixed-versions-lto.rs +++ b/tests/ui/lto/dwarf-mixed-versions-lto.rs @@ -4,7 +4,7 @@ //@ ignore-msvc Platform must use DWARF //@ aux-build:dwarf-mixed-versions-lto-aux.rs -//@ compile-flags: -C lto -g -Zdwarf-version=5 +//@ compile-flags: -C lto -g -Cdwarf-version=5 //@ no-prefer-dynamic //@ build-pass diff --git a/tests/ui/lto/issue-11154.rs b/tests/ui/lto/issue-11154.rs index 914b0b73e4749..7770aeccd6a88 100644 --- a/tests/ui/lto/issue-11154.rs +++ b/tests/ui/lto/issue-11154.rs @@ -1,6 +1,6 @@ //@ build-fail //@ compile-flags: -C lto -C prefer-dynamic -//@ error-pattern: cannot prefer dynamic linking - fn main() {} + +//~? ERROR cannot prefer dynamic linking when performing LTO diff --git a/tests/ui/lto/lto-and-no-bitcode-in-rlib.rs b/tests/ui/lto/lto-and-no-bitcode-in-rlib.rs index f742cd78697f8..2f0aeb0b7ba91 100644 --- a/tests/ui/lto/lto-and-no-bitcode-in-rlib.rs +++ b/tests/ui/lto/lto-and-no-bitcode-in-rlib.rs @@ -1,3 +1,5 @@ //@ compile-flags: -C lto -C embed-bitcode=no fn main() {} + +//~? ERROR options `-C embed-bitcode=no` and `-C lto` are incompatible diff --git a/tests/ui/lto/lto-duplicate-symbols.rs b/tests/ui/lto/lto-duplicate-symbols.rs index 27bdde418f855..a62ab2e22172b 100644 --- a/tests/ui/lto/lto-duplicate-symbols.rs +++ b/tests/ui/lto/lto-duplicate-symbols.rs @@ -1,7 +1,6 @@ //@ build-fail //@ aux-build:lto-duplicate-symbols1.rs //@ aux-build:lto-duplicate-symbols2.rs -//@ error-pattern:Linking globals named 'foo': symbol multiply defined! //@ compile-flags: -C lto //@ no-prefer-dynamic //@ normalize-stderr: "lto-duplicate-symbols2\.lto_duplicate_symbols2\.[0-9a-zA-Z]+-cgu" -> "lto-duplicate-symbols2.lto_duplicate_symbols2.HASH-cgu" @@ -9,3 +8,6 @@ extern crate lto_duplicate_symbols1; extern crate lto_duplicate_symbols2; fn main() {} + +//~? WARN Linking globals named 'foo': symbol multiply defined +//~? ERROR failed to load bitcode of module "lto-duplicate-symbols2.lto_duplicate_symbols2 diff --git a/tests/ui/auxiliary/fancy-panic.rs b/tests/ui/macros/auxiliary/fancy-panic.rs similarity index 100% rename from tests/ui/auxiliary/fancy-panic.rs rename to tests/ui/macros/auxiliary/fancy-panic.rs diff --git a/tests/ui/macros/auxiliary/macro-include-items-expr.rs b/tests/ui/macros/auxiliary/macro-include-items-expr.rs index 7394f194b80ea..d00491fd7e5bb 100644 --- a/tests/ui/macros/auxiliary/macro-include-items-expr.rs +++ b/tests/ui/macros/auxiliary/macro-include-items-expr.rs @@ -1,3 +1 @@ -// ignore-test: this is not a test - 1 diff --git a/tests/ui/macros/auxiliary/macro-include-items-item.rs b/tests/ui/macros/auxiliary/macro-include-items-item.rs index 7d54745e03bff..761cd00218977 100644 --- a/tests/ui/macros/auxiliary/macro-include-items-item.rs +++ b/tests/ui/macros/auxiliary/macro-include-items-item.rs @@ -1,3 +1 @@ -// ignore-test: this is not a test - fn foo() { bar() } diff --git a/tests/ui/macros/auxiliary/return_from_external_macro.rs b/tests/ui/macros/auxiliary/return_from_external_macro.rs new file mode 100644 index 0000000000000..df59d8bdcb0d9 --- /dev/null +++ b/tests/ui/macros/auxiliary/return_from_external_macro.rs @@ -0,0 +1,11 @@ +#![feature(super_let)] + +#[macro_export] +macro_rules! foo { + () => { + { + super let args = 1; + &args + } + }; +} diff --git a/tests/ui/macros/builtin-env-issue-114010.stderr b/tests/ui/macros/builtin-env-issue-114010.stderr index 0da42089cce77..2751160e2d467 100644 --- a/tests/ui/macros/builtin-env-issue-114010.stderr +++ b/tests/ui/macros/builtin-env-issue-114010.stderr @@ -5,7 +5,6 @@ LL | env![r#"oopsie"#]; | ^^^^^^^^^^^^^^^^^ | = help: use `std::env::var(r#"oopsie"#)` to read the variable at run time - = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: environment variable `a""a` not defined at compile time --> $DIR/builtin-env-issue-114010.rs:7:1 @@ -14,7 +13,6 @@ LL | env![r#"a""a"#]; | ^^^^^^^^^^^^^^^ | = help: use `std::env::var(r#"a""a"#)` to read the variable at run time - = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/macros/cfg.stderr b/tests/ui/macros/cfg.stderr index 5332691486562..ad3f9b188ddb9 100644 --- a/tests/ui/macros/cfg.stderr +++ b/tests/ui/macros/cfg.stderr @@ -3,8 +3,6 @@ error: macro requires a cfg-pattern as an argument | LL | cfg!(); | ^^^^^^ cfg-pattern required - | - = note: this error originates in the macro `cfg` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0565]: literal in `cfg` predicate value must be a boolean --> $DIR/cfg.rs:3:10 @@ -23,8 +21,6 @@ error: expected 1 cfg-pattern | LL | cfg!(foo, bar); | ^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `cfg` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/macros/duplicate-builtin.rs b/tests/ui/macros/duplicate-builtin.rs deleted file mode 100644 index c75782128f425..0000000000000 --- a/tests/ui/macros/duplicate-builtin.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ 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/tests/ui/macros/duplicate-builtin.stderr b/tests/ui/macros/duplicate-builtin.stderr deleted file mode 100644 index 887a4fbbdc8e0..0000000000000 --- a/tests/ui/macros/duplicate-builtin.stderr +++ /dev/null @@ -1,21 +0,0 @@ -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 1 previous error - -For more information about this error, try `rustc --explain E0773`. diff --git a/tests/ui/macros/expr_2021_cargo_fix_edition.fixed b/tests/ui/macros/expr_2021_cargo_fix_edition.fixed index 061a4b98033c4..c0d2760935893 100644 --- a/tests/ui/macros/expr_2021_cargo_fix_edition.fixed +++ b/tests/ui/macros/expr_2021_cargo_fix_edition.fixed @@ -1,6 +1,6 @@ //@ run-rustfix //@ check-pass -//@ compile-flags: --edition=2021 +//@ edition: 2021 #![warn(edition_2024_expr_fragment_specifier)] macro_rules! m { diff --git a/tests/ui/macros/expr_2021_cargo_fix_edition.rs b/tests/ui/macros/expr_2021_cargo_fix_edition.rs index cd9cd965fad22..b2c16fc7041c0 100644 --- a/tests/ui/macros/expr_2021_cargo_fix_edition.rs +++ b/tests/ui/macros/expr_2021_cargo_fix_edition.rs @@ -1,6 +1,6 @@ //@ run-rustfix //@ check-pass -//@ compile-flags: --edition=2021 +//@ edition: 2021 #![warn(edition_2024_expr_fragment_specifier)] macro_rules! m { diff --git a/tests/ui/macros/failed-to-reparse-issue-137874.rs b/tests/ui/macros/failed-to-reparse-issue-137874.rs new file mode 100644 index 0000000000000..3e55ac376fea8 --- /dev/null +++ b/tests/ui/macros/failed-to-reparse-issue-137874.rs @@ -0,0 +1,12 @@ +// This originally crashed because `Recovery::Forbidden` wasn't being applied +// when fragments pasted by declarative macros were reparsed. + +macro_rules! m { + ($p:pat) => { + if let $p = 0 {} + } +} + +fn main() { + m!(0X0); //~ ERROR invalid base prefix for number literal +} diff --git a/tests/ui/macros/failed-to-reparse-issue-137874.stderr b/tests/ui/macros/failed-to-reparse-issue-137874.stderr new file mode 100644 index 0000000000000..5bbb8b7f9e2f4 --- /dev/null +++ b/tests/ui/macros/failed-to-reparse-issue-137874.stderr @@ -0,0 +1,10 @@ +error: invalid base prefix for number literal + --> $DIR/failed-to-reparse-issue-137874.rs:11:8 + | +LL | m!(0X0); + | ^^^ help: try making the prefix lowercase (notice the capitalization): `0x0` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: aborting due to 1 previous error + diff --git a/tests/ui/macros/failed-to-reparse-issue-139445.rs b/tests/ui/macros/failed-to-reparse-issue-139445.rs new file mode 100644 index 0000000000000..babe26b9d293e --- /dev/null +++ b/tests/ui/macros/failed-to-reparse-issue-139445.rs @@ -0,0 +1,6 @@ +fn main() { + assert_eq!(3, 'a,) + //~^ ERROR expected `while`, `for`, `loop` or `{` after a label + //~| ERROR expected `while`, `for`, `loop` or `{` after a label + //~| ERROR expected expression, found `` +} diff --git a/tests/ui/macros/failed-to-reparse-issue-139445.stderr b/tests/ui/macros/failed-to-reparse-issue-139445.stderr new file mode 100644 index 0000000000000..6f7d88fb34466 --- /dev/null +++ b/tests/ui/macros/failed-to-reparse-issue-139445.stderr @@ -0,0 +1,24 @@ +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/failed-to-reparse-issue-139445.rs:2:21 + | +LL | assert_eq!(3, 'a,) + | ^ expected `while`, `for`, `loop` or `{` after a label + +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/failed-to-reparse-issue-139445.rs:2:5 + | +LL | assert_eq!(3, 'a,) + | ^^^^^^^^^^^^^^^^^^ expected `while`, `for`, `loop` or `{` after a label + | + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected expression, found `` + --> $DIR/failed-to-reparse-issue-139445.rs:2:5 + | +LL | assert_eq!(3, 'a,) + | ^^^^^^^^^^^^^^^^^^ expected expression + | + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + diff --git a/tests/ui/macros/genercs-in-path-with-prettry-hir.rs b/tests/ui/macros/genercs-in-path-with-prettry-hir.rs index 84370fcebbcbc..e6773f610da30 100644 --- a/tests/ui/macros/genercs-in-path-with-prettry-hir.rs +++ b/tests/ui/macros/genercs-in-path-with-prettry-hir.rs @@ -1,4 +1,5 @@ //@ compile-flags: -Zunpretty=hir +//@ edition: 2015 // issue#97006 diff --git a/tests/ui/macros/genercs-in-path-with-prettry-hir.stderr b/tests/ui/macros/genercs-in-path-with-prettry-hir.stderr index 8fcc7c6fbff06..173e77569a805 100644 --- a/tests/ui/macros/genercs-in-path-with-prettry-hir.stderr +++ b/tests/ui/macros/genercs-in-path-with-prettry-hir.stderr @@ -1,5 +1,5 @@ error: unexpected generic arguments in path - --> $DIR/genercs-in-path-with-prettry-hir.rs:12:10 + --> $DIR/genercs-in-path-with-prettry-hir.rs:13:10 | LL | m!(inline); | ^^^^ diff --git a/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout b/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout index e8c88d2dcdf2c..6b41eb530dbc8 100644 --- a/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout +++ b/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout @@ -3,6 +3,7 @@ use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; //@ compile-flags: -Zunpretty=hir +//@ edition: 2015 // issue#97006 diff --git a/tests/ui/macros/include-single-expr-helper-1.rs b/tests/ui/macros/include-single-expr-helper-1.rs index ddeeb982f64cd..6802719afa1b3 100644 --- a/tests/ui/macros/include-single-expr-helper-1.rs +++ b/tests/ui/macros/include-single-expr-helper-1.rs @@ -1,4 +1,4 @@ -//@ ignore-test auxiliary file for include-single-expr.rs +//@ ignore-auxiliary (used by `./include-single-expr.rs`) 0 diff --git a/tests/ui/macros/include-single-expr-helper.rs b/tests/ui/macros/include-single-expr-helper.rs index e8ad9746b02cd..bd75bbbd583e6 100644 --- a/tests/ui/macros/include-single-expr-helper.rs +++ b/tests/ui/macros/include-single-expr-helper.rs @@ -1,4 +1,4 @@ -//@ ignore-test auxiliary file for include-single-expr.rs +//@ ignore-auxiliary (used by `./include-single-expr.rs`) 0 10 diff --git a/tests/ui/macros/include-single-expr.rs b/tests/ui/macros/include-single-expr.rs index c501f5d97ca55..e3ab1257b42ad 100644 --- a/tests/ui/macros/include-single-expr.rs +++ b/tests/ui/macros/include-single-expr.rs @@ -1,6 +1,6 @@ -//@ error-pattern include macro expected single expression - fn main() { include!("include-single-expr-helper.rs"); include!("include-single-expr-helper-1.rs"); } + +//~? ERROR include macro expected single expression diff --git a/tests/ui/macros/issue-118786.rs b/tests/ui/macros/issue-118786.rs index a73b737fe07e4..78fd6ab6edddf 100644 --- a/tests/ui/macros/issue-118786.rs +++ b/tests/ui/macros/issue-118786.rs @@ -1,4 +1,5 @@ //@ compile-flags: --crate-type lib -O -C debug-assertions=yes +//@ dont-require-annotations: NOTE // Regression test for issue 118786 @@ -7,7 +8,7 @@ macro_rules! make_macro { macro_rules! $macro_name { //~^ ERROR macro expansion ignores `{` and any tokens following //~| ERROR cannot find macro `macro_rules` in this scope - //~| put a macro name here + //~| NOTE put a macro name here () => {} } } diff --git a/tests/ui/macros/issue-118786.stderr b/tests/ui/macros/issue-118786.stderr index af4cc9ad86377..ddec281b82325 100644 --- a/tests/ui/macros/issue-118786.stderr +++ b/tests/ui/macros/issue-118786.stderr @@ -1,5 +1,5 @@ error: macros that expand to items must be delimited with braces or followed by a semicolon - --> $DIR/issue-118786.rs:16:13 + --> $DIR/issue-118786.rs:17:13 | LL | make_macro!((meow)); | ^^^^^^ @@ -15,7 +15,7 @@ LL | macro_rules! $macro_name; { | + error: macro expansion ignores `{` and any tokens following - --> $DIR/issue-118786.rs:7:34 + --> $DIR/issue-118786.rs:8:34 | LL | macro_rules! $macro_name { | ^ @@ -26,7 +26,7 @@ LL | make_macro!((meow)); = note: the usage of `make_macro!` is likely invalid in item context error: cannot find macro `macro_rules` in this scope - --> $DIR/issue-118786.rs:7:9 + --> $DIR/issue-118786.rs:8:9 | LL | macro_rules! $macro_name { | ^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL | make_macro!((meow)); | ------------------- in this macro invocation | note: maybe you have forgotten to define a name for this `macro_rules!` - --> $DIR/issue-118786.rs:7:20 + --> $DIR/issue-118786.rs:8:20 | LL | macro_rules! $macro_name { | ^ put a macro name here diff --git a/tests/ui/macros/issue-29084.rs b/tests/ui/macros/issue-29084.rs index d16252686698d..a0a2966f75b5b 100644 --- a/tests/ui/macros/issue-29084.rs +++ b/tests/ui/macros/issue-29084.rs @@ -1,13 +1,15 @@ +//@ dont-require-annotations: NOTE + macro_rules! foo { ($d:expr) => {{ fn bar(d: u8) { } bar(&mut $d); //~^ ERROR mismatched types - //~| expected `u8`, found `&mut u8` + //~| NOTE expected `u8`, found `&mut u8` }} } fn main() { foo!(0u8); - //~^ in this expansion of foo! + //~^ NOTE in this expansion of foo! } diff --git a/tests/ui/macros/issue-29084.stderr b/tests/ui/macros/issue-29084.stderr index 9c33e4e8427ab..6e7474c5b3f74 100644 --- a/tests/ui/macros/issue-29084.stderr +++ b/tests/ui/macros/issue-29084.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-29084.rs:4:13 + --> $DIR/issue-29084.rs:6:13 | LL | bar(&mut $d); | --- ^^^^^^^ expected `u8`, found `&mut u8` @@ -10,7 +10,7 @@ LL | foo!(0u8); | --------- in this macro invocation | note: function defined here - --> $DIR/issue-29084.rs:3:12 + --> $DIR/issue-29084.rs:5:12 | LL | fn bar(d: u8) { } | ^^^ ----- diff --git a/tests/ui/macros/issue-42954.fixed b/tests/ui/macros/issue-42954.fixed index acfc36e2bff2f..b1d4e0a56a6b4 100644 --- a/tests/ui/macros/issue-42954.fixed +++ b/tests/ui/macros/issue-42954.fixed @@ -4,7 +4,7 @@ macro_rules! is_plainly_printable { ($i: ident) => { - ($i as u32) < 0 //~ `<` is interpreted as a start of generic arguments + ($i as u32) < 0 //~ ERROR `<` is interpreted as a start of generic arguments }; } diff --git a/tests/ui/macros/issue-42954.rs b/tests/ui/macros/issue-42954.rs index 91362946f845a..29e2aa3a682ca 100644 --- a/tests/ui/macros/issue-42954.rs +++ b/tests/ui/macros/issue-42954.rs @@ -4,7 +4,7 @@ macro_rules! is_plainly_printable { ($i: ident) => { - $i as u32 < 0 //~ `<` is interpreted as a start of generic arguments + $i as u32 < 0 //~ ERROR `<` is interpreted as a start of generic arguments }; } diff --git a/tests/ui/macros/issue-69838-dir/bar.rs b/tests/ui/macros/issue-69838-dir/bar.rs index 4433005b85f15..6f91f8e2ffadc 100644 --- a/tests/ui/macros/issue-69838-dir/bar.rs +++ b/tests/ui/macros/issue-69838-dir/bar.rs @@ -1,3 +1,3 @@ -//@ ignore-test -- this is an auxiliary file as part of another test. +//@ ignore-auxiliary (used by `../issue-69838-mods-relative-to-included-path.rs`) pub fn i_am_in_bar() {} diff --git a/tests/ui/macros/issue-69838-dir/included.rs b/tests/ui/macros/issue-69838-dir/included.rs index 11fcd3eff725d..328334d5e66af 100644 --- a/tests/ui/macros/issue-69838-dir/included.rs +++ b/tests/ui/macros/issue-69838-dir/included.rs @@ -1,3 +1,3 @@ -//@ ignore-test -- this is an auxiliary file as part of another test. +//@ ignore-auxiliary (used by `../issue-69838-mods-relative-to-included-path.rs`) pub mod bar; diff --git a/tests/ui/macros/issue-88228.rs b/tests/ui/macros/issue-88228.rs index 48b405264fef5..b4195a92557ed 100644 --- a/tests/ui/macros/issue-88228.rs +++ b/tests/ui/macros/issue-88228.rs @@ -13,7 +13,7 @@ struct A; #[derive(println)] //~^ ERROR cannot find derive macro `println` -//~|`println` is in scope, but it is a function-like macro +//~| NOTE `println` is in scope, but it is a function-like macro struct B; fn main() { diff --git a/tests/ui/macros/lint-trailing-macro-call.rs b/tests/ui/macros/lint-trailing-macro-call.rs index 66dce057d0f5c..78b861f1df17a 100644 --- a/tests/ui/macros/lint-trailing-macro-call.rs +++ b/tests/ui/macros/lint-trailing-macro-call.rs @@ -6,7 +6,7 @@ macro_rules! expand_it { () => { - #[cfg(FALSE)] 25; //~ WARN trailing semicolon in macro + #[cfg(false)] 25; //~ WARN trailing semicolon in macro //~| WARN this was previously } } diff --git a/tests/ui/macros/lint-trailing-macro-call.stderr b/tests/ui/macros/lint-trailing-macro-call.stderr index 13cecc3a31d23..223b85e112ed6 100644 --- a/tests/ui/macros/lint-trailing-macro-call.stderr +++ b/tests/ui/macros/lint-trailing-macro-call.stderr @@ -1,7 +1,7 @@ warning: trailing semicolon in macro used in expression position --> $DIR/lint-trailing-macro-call.rs:9:25 | -LL | #[cfg(FALSE)] 25; +LL | #[cfg(false)] 25; | ^ ... LL | expand_it!() @@ -20,7 +20,7 @@ Future incompatibility report: Future breakage diagnostic: warning: trailing semicolon in macro used in expression position --> $DIR/lint-trailing-macro-call.rs:9:25 | -LL | #[cfg(FALSE)] 25; +LL | #[cfg(false)] 25; | ^ ... LL | expand_it!() diff --git a/tests/ui/macros/macro-as-fn-body.rs b/tests/ui/macros/macro-as-fn-body.rs index e0542edc2a521..188c7f7f728c9 100644 --- a/tests/ui/macros/macro-as-fn-body.rs +++ b/tests/ui/macros/macro-as-fn-body.rs @@ -1,7 +1,7 @@ // //@ run-pass // -// Description - ensure Interpolated blocks can act as valid function bodies +// Description - ensure block metavariables can act as valid function bodies // Covered cases: free functions, struct methods, and default trait functions macro_rules! def_fn { diff --git a/tests/ui/macros/macro-attributes.rs b/tests/ui/macros/macro-attributes.rs index 8329079076698..976d2cbcccdbb 100644 --- a/tests/ui/macros/macro-attributes.rs +++ b/tests/ui/macros/macro-attributes.rs @@ -9,7 +9,7 @@ macro_rules! compiles_fine { // check that the attributes are recognised by requiring this // to be removed to avoid a compile error - #[cfg(FALSE)] + #[cfg(false)] static MISTYPED: () = "foo"; } } diff --git a/tests/ui/macros/macro-expanded-include/foo/mod.rs b/tests/ui/macros/macro-expanded-include/foo/mod.rs index 926d84c93e58f..4e6d9e4aea407 100644 --- a/tests/ui/macros/macro-expanded-include/foo/mod.rs +++ b/tests/ui/macros/macro-expanded-include/foo/mod.rs @@ -1,4 +1,4 @@ -//@ ignore-test (auxiliary, used by other tests) +//@ ignore-auxiliary (used by `../test.rs`) macro_rules! m { () => { include!("file.txt"); } diff --git a/tests/ui/macros/macro-inner-attributes.rs b/tests/ui/macros/macro-inner-attributes.rs index a1eb7cd15c4cc..1a832ca9b0c4b 100644 --- a/tests/ui/macros/macro-inner-attributes.rs +++ b/tests/ui/macros/macro-inner-attributes.rs @@ -5,7 +5,7 @@ macro_rules! test { ($nm:ident, $i:item) => (mod $nm { #![$a] $i }); } test!(a, - #[cfg(FALSE)], + #[cfg(false)], pub fn bar() { }); test!(b, diff --git a/tests/ui/macros/macro-outer-attributes.rs b/tests/ui/macros/macro-outer-attributes.rs index 8c79683f49a96..5b41cf9fd29d6 100644 --- a/tests/ui/macros/macro-outer-attributes.rs +++ b/tests/ui/macros/macro-outer-attributes.rs @@ -5,7 +5,7 @@ macro_rules! test { ($nm:ident, $i:item) => (mod $nm { #[$a] $i }); } test!(a, - #[cfg(FALSE)], + #[cfg(false)], pub fn bar() { }); test!(b, diff --git a/tests/ui/macros/macro-outer-attributes.stderr b/tests/ui/macros/macro-outer-attributes.stderr index a8809f3fcff69..a894c90f4c32a 100644 --- a/tests/ui/macros/macro-outer-attributes.stderr +++ b/tests/ui/macros/macro-outer-attributes.stderr @@ -16,7 +16,7 @@ LL | $i:item) => (mod $nm { #[$a] $i }); } | ^^^^^ LL | LL | / test!(a, -LL | | #[cfg(FALSE)], +LL | | #[cfg(false)], LL | | pub fn bar() { }); | |_______________________- in this macro invocation = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/macros/macro-parameter-span.stderr b/tests/ui/macros/macro-parameter-span.stderr index 247750a8ad705..44c8c56dff999 100644 --- a/tests/ui/macros/macro-parameter-span.stderr +++ b/tests/ui/macros/macro-parameter-span.stderr @@ -1,6 +1,9 @@ error[E0425]: cannot find value `x` in this scope --> $DIR/macro-parameter-span.rs:11:9 | +LL | $id + | --- due to this macro variable +... LL | x | ^ not found in this scope diff --git a/tests/ui/macros/macro-stmt-2.rs b/tests/ui/macros/macro-stmt-2.rs new file mode 100644 index 0000000000000..663d8ddc4843a --- /dev/null +++ b/tests/ui/macros/macro-stmt-2.rs @@ -0,0 +1,11 @@ +//@ check-pass +// +// This shows a tricky case for #124141, where `declare!(_x)` was incorrectly +// being categorised as a `StmtKind::Expr` instead of a `StmtKind::MacCall` in +// `parse_stmt_mac`. + +macro_rules! as_stmt { ($s:stmt) => { $s }; } + +macro_rules! declare { ($name:ident) => { let $name = 0u32; }; } + +fn main() { as_stmt!(declare!(_x)); } diff --git a/tests/ui/macros/macro-with-attrs2.rs b/tests/ui/macros/macro-with-attrs2.rs index 37188e45ad3b5..7d0bf91142532 100644 --- a/tests/ui/macros/macro-with-attrs2.rs +++ b/tests/ui/macros/macro-with-attrs2.rs @@ -1,6 +1,6 @@ //@ run-pass -#[cfg(FALSE)] +#[cfg(false)] macro_rules! foo { () => (1) } #[cfg(not(FALSE))] diff --git a/tests/ui/macros/macros-nonfatal-errors.rs b/tests/ui/macros/macros-nonfatal-errors.rs index 79beffbe986e3..091d64ea5d9eb 100644 --- a/tests/ui/macros/macros-nonfatal-errors.rs +++ b/tests/ui/macros/macros-nonfatal-errors.rs @@ -5,6 +5,7 @@ #![feature(trace_macros, concat_idents)] #![feature(stmt_expr_attributes)] +#![expect(deprecated)] // concat_idents is deprecated use std::arch::asm; diff --git a/tests/ui/macros/macros-nonfatal-errors.stderr b/tests/ui/macros/macros-nonfatal-errors.stderr index fd5e41986a836..2f990cb24e2bf 100644 --- a/tests/ui/macros/macros-nonfatal-errors.stderr +++ b/tests/ui/macros/macros-nonfatal-errors.stderr @@ -1,5 +1,5 @@ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:13:5 + --> $DIR/macros-nonfatal-errors.rs:14:5 | LL | #[default] | ^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[default] = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:18:36 + --> $DIR/macros-nonfatal-errors.rs:19:36 | LL | struct DefaultInnerAttrTupleStruct(#[default] ()); | ^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | struct DefaultInnerAttrTupleStruct(#[default] ()); = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:22:1 + --> $DIR/macros-nonfatal-errors.rs:23:1 | LL | #[default] | ^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | #[default] = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:26:1 + --> $DIR/macros-nonfatal-errors.rs:27:1 | LL | #[default] | ^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | #[default] = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:36:11 + --> $DIR/macros-nonfatal-errors.rs:37:11 | LL | Foo = #[default] 0, | ^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | Foo = #[default] 0, = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:37:14 + --> $DIR/macros-nonfatal-errors.rs:38:14 | LL | Bar([u8; #[default] 1]), | ^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | Bar([u8; #[default] 1]), = help: consider a manual implementation of `Default` error[E0665]: `#[derive(Default)]` on enum with no `#[default]` - --> $DIR/macros-nonfatal-errors.rs:42:10 + --> $DIR/macros-nonfatal-errors.rs:43:10 | LL | #[derive(Default)] | ^^^^^^^ @@ -57,7 +57,6 @@ LL | | Bar, LL | | } | |_- this enum needs a unit variant marked with `#[default]` | - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) help: make this unit variant default by placing `#[default]` on it | LL | #[default] Foo, @@ -68,7 +67,7 @@ LL | #[default] Bar, | ++++++++++ error[E0665]: `#[derive(Default)]` on enum with no `#[default]` - --> $DIR/macros-nonfatal-errors.rs:48:10 + --> $DIR/macros-nonfatal-errors.rs:49:10 | LL | #[derive(Default)] | ^^^^^^^ @@ -77,11 +76,9 @@ LL | | Foo(i32), LL | | Bar(i32), LL | | } | |_- this enum needs a unit variant marked with `#[default]` - | - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error: multiple declared defaults - --> $DIR/macros-nonfatal-errors.rs:54:10 + --> $DIR/macros-nonfatal-errors.rs:55:10 | LL | #[derive(Default)] | ^^^^^^^ @@ -96,10 +93,9 @@ LL | Baz, | --- additional default | = note: only one variant can be default - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error: `#[default]` attribute does not accept a value - --> $DIR/macros-nonfatal-errors.rs:66:5 + --> $DIR/macros-nonfatal-errors.rs:67:5 | LL | #[default = 1] | ^^^^^^^^^^^^^^ @@ -107,7 +103,7 @@ LL | #[default = 1] = help: try using `#[default]` error: multiple `#[default]` attributes - --> $DIR/macros-nonfatal-errors.rs:74:5 + --> $DIR/macros-nonfatal-errors.rs:75:5 | LL | #[default] | ---------- `#[default]` used here @@ -118,13 +114,13 @@ LL | Foo, | = note: only one `#[default]` attribute is needed help: try removing this - --> $DIR/macros-nonfatal-errors.rs:73:5 + --> $DIR/macros-nonfatal-errors.rs:74:5 | LL | #[default] | ^^^^^^^^^^ error: multiple `#[default]` attributes - --> $DIR/macros-nonfatal-errors.rs:84:5 + --> $DIR/macros-nonfatal-errors.rs:85:5 | LL | #[default] | ---------- `#[default]` used here @@ -136,7 +132,7 @@ LL | Foo, | = note: only one `#[default]` attribute is needed help: try removing these - --> $DIR/macros-nonfatal-errors.rs:81:5 + --> $DIR/macros-nonfatal-errors.rs:82:5 | LL | #[default] | ^^^^^^^^^^ @@ -146,7 +142,7 @@ LL | #[default] | ^^^^^^^^^^ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:91:5 + --> $DIR/macros-nonfatal-errors.rs:92:5 | LL | Foo {}, | ^^^ @@ -154,7 +150,7 @@ LL | Foo {}, = help: consider a manual implementation of `Default` error: default variant must be exhaustive - --> $DIR/macros-nonfatal-errors.rs:99:5 + --> $DIR/macros-nonfatal-errors.rs:100:5 | LL | #[non_exhaustive] | ----------------- declared `#[non_exhaustive]` here @@ -164,46 +160,45 @@ LL | Foo, = help: consider a manual implementation of `Default` error: asm template must be a string literal - --> $DIR/macros-nonfatal-errors.rs:104:10 + --> $DIR/macros-nonfatal-errors.rs:105:10 | LL | asm!(invalid); | ^^^^^^^ error: `concat_idents!()` requires ident args - --> $DIR/macros-nonfatal-errors.rs:107:5 + --> $DIR/macros-nonfatal-errors.rs:108:5 | LL | concat_idents!("not", "idents"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:109:17 + --> $DIR/macros-nonfatal-errors.rs:110:17 | LL | option_env!(invalid); | ^^^^^^^ error: expected string literal - --> $DIR/macros-nonfatal-errors.rs:110:10 + --> $DIR/macros-nonfatal-errors.rs:111:10 | LL | env!(invalid); | ^^^^^^^ error: `env!()` takes 1 or 2 arguments - --> $DIR/macros-nonfatal-errors.rs:111:5 + --> $DIR/macros-nonfatal-errors.rs:112:5 | LL | env!(foo, abr, baz); | ^^^^^^^^^^^^^^^^^^^ error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined at compile time - --> $DIR/macros-nonfatal-errors.rs:112:5 + --> $DIR/macros-nonfatal-errors.rs:113:5 | LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `std::env::var("RUST_HOPEFULLY_THIS_DOESNT_EXIST")` to read the variable at run time - = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: format argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:114:13 + --> $DIR/macros-nonfatal-errors.rs:115:13 | LL | format!(invalid); | ^^^^^^^ @@ -214,47 +209,43 @@ LL | format!("{}", invalid); | +++++ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:116:14 + --> $DIR/macros-nonfatal-errors.rs:117:14 | LL | include!(invalid); | ^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:118:18 + --> $DIR/macros-nonfatal-errors.rs:119:18 | LL | include_str!(invalid); | ^^^^^^^ error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: $FILE_NOT_FOUND_MSG - --> $DIR/macros-nonfatal-errors.rs:119:5 + --> $DIR/macros-nonfatal-errors.rs:120:5 | LL | include_str!("i'd be quite surprised if a file with this name existed"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:120:20 + --> $DIR/macros-nonfatal-errors.rs:121:20 | LL | include_bytes!(invalid); | ^^^^^^^ error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: $FILE_NOT_FOUND_MSG - --> $DIR/macros-nonfatal-errors.rs:121:5 + --> $DIR/macros-nonfatal-errors.rs:122:5 | LL | include_bytes!("i'd be quite surprised if a file with this name existed"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) error: trace_macros! accepts only `true` or `false` - --> $DIR/macros-nonfatal-errors.rs:123:5 + --> $DIR/macros-nonfatal-errors.rs:124:5 | LL | trace_macros!(invalid); | ^^^^^^^^^^^^^^^^^^^^^^ error: default variant must be exhaustive - --> $DIR/macros-nonfatal-errors.rs:133:9 + --> $DIR/macros-nonfatal-errors.rs:134:9 | LL | #[non_exhaustive] | ----------------- declared `#[non_exhaustive]` here @@ -264,7 +255,7 @@ LL | Foo, = help: consider a manual implementation of `Default` error: cannot find macro `llvm_asm` in this scope - --> $DIR/macros-nonfatal-errors.rs:105:5 + --> $DIR/macros-nonfatal-errors.rs:106:5 | LL | llvm_asm!(invalid); | ^^^^^^^^ diff --git a/tests/ui/macros/no-close-delim-issue-139248.rs b/tests/ui/macros/no-close-delim-issue-139248.rs new file mode 100644 index 0000000000000..86583b2724ed0 --- /dev/null +++ b/tests/ui/macros/no-close-delim-issue-139248.rs @@ -0,0 +1,14 @@ +// This code caused a "no close delim when reparsing Expr" ICE in #139248. + +macro_rules! m { + (static a : () = $e:expr) => { + static a : () = $e; + //~^ ERROR macro expansion ends with an incomplete expression: expected expression + } +} + +m! { static a : () = (if b) } +//~^ ERROR expected `{`, found `)` +//~| ERROR expected `{`, found `)` + +fn main() {} diff --git a/tests/ui/macros/no-close-delim-issue-139248.stderr b/tests/ui/macros/no-close-delim-issue-139248.stderr new file mode 100644 index 0000000000000..6ed41ae9b46a6 --- /dev/null +++ b/tests/ui/macros/no-close-delim-issue-139248.stderr @@ -0,0 +1,33 @@ +error: expected `{`, found `)` + --> $DIR/no-close-delim-issue-139248.rs:10:27 + | +LL | m! { static a : () = (if b) } + | ^ expected `{` + | +note: the `if` expression is missing a block after this condition + --> $DIR/no-close-delim-issue-139248.rs:10:26 + | +LL | m! { static a : () = (if b) } + | ^ + +error: expected `{`, found `)` + --> $DIR/no-close-delim-issue-139248.rs:10:27 + | +LL | m! { static a : () = (if b) } + | ^ expected `{` + | +note: the `if` expression is missing a block after this condition + --> $DIR/no-close-delim-issue-139248.rs:10:26 + | +LL | m! { static a : () = (if b) } + | ^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: macro expansion ends with an incomplete expression: expected expression + --> $DIR/no-close-delim-issue-139248.rs:5:28 + | +LL | static a : () = $e; + | ^ expected expression + +error: aborting due to 3 previous errors + diff --git a/tests/ui/fail-simple.rs b/tests/ui/macros/no-matching-rule.rs similarity index 100% rename from tests/ui/fail-simple.rs rename to tests/ui/macros/no-matching-rule.rs diff --git a/tests/ui/macros/no-matching-rule.stderr b/tests/ui/macros/no-matching-rule.stderr new file mode 100644 index 0000000000000..a6312a843f398 --- /dev/null +++ b/tests/ui/macros/no-matching-rule.stderr @@ -0,0 +1,10 @@ +error: no rules expected `@` + --> $DIR/no-matching-rule.rs:2:12 + | +LL | panic!(@); + | ^ no rules expected this token in macro call + | + = note: while trying to match end of macro + +error: aborting due to 1 previous error + diff --git a/tests/ui/non-fmt-panic.fixed b/tests/ui/macros/non-fmt-panic.fixed similarity index 94% rename from tests/ui/non-fmt-panic.fixed rename to tests/ui/macros/non-fmt-panic.fixed index fa9a1ad89bdd8..b102dba17319a 100644 --- a/tests/ui/non-fmt-panic.fixed +++ b/tests/ui/macros/non-fmt-panic.fixed @@ -1,3 +1,9 @@ +//! The non_fmt_panics lint detects panic!(..) invocations where +//! the first argument is not a formatting string. +//! +//! Also, this test checks that this is not emitted if it originates +//! in an external macro. + //@ run-rustfix //@ rustfix-only-machine-applicable //@ build-pass (FIXME(62277): should be check-pass) diff --git a/tests/ui/non-fmt-panic.rs b/tests/ui/macros/non-fmt-panic.rs similarity index 94% rename from tests/ui/non-fmt-panic.rs rename to tests/ui/macros/non-fmt-panic.rs index 451a0c76018ca..9277529c6d421 100644 --- a/tests/ui/non-fmt-panic.rs +++ b/tests/ui/macros/non-fmt-panic.rs @@ -1,3 +1,9 @@ +//! The non_fmt_panics lint detects panic!(..) invocations where +//! the first argument is not a formatting string. +//! +//! Also, this test checks that this is not emitted if it originates +//! in an external macro. + //@ run-rustfix //@ rustfix-only-machine-applicable //@ build-pass (FIXME(62277): should be check-pass) diff --git a/tests/ui/non-fmt-panic.stderr b/tests/ui/macros/non-fmt-panic.stderr similarity index 93% rename from tests/ui/non-fmt-panic.stderr rename to tests/ui/macros/non-fmt-panic.stderr index 0134a8ddf2924..30b63cb46e22b 100644 --- a/tests/ui/non-fmt-panic.stderr +++ b/tests/ui/macros/non-fmt-panic.stderr @@ -1,5 +1,5 @@ warning: panic message contains a brace - --> $DIR/non-fmt-panic.rs:13:29 + --> $DIR/non-fmt-panic.rs:19:29 | LL | panic!("here's a brace: {"); | ^ @@ -12,7 +12,7 @@ LL | panic!("{}", "here's a brace: {"); | +++++ warning: panic message contains a brace - --> $DIR/non-fmt-panic.rs:14:35 + --> $DIR/non-fmt-panic.rs:20:35 | LL | unreachable!("here's a brace: {"); | ^ @@ -24,7 +24,7 @@ LL | unreachable!("{}", "here's a brace: {"); | +++++ warning: panic message contains a brace - --> $DIR/non-fmt-panic.rs:15:31 + --> $DIR/non-fmt-panic.rs:21:31 | LL | std::panic!("another one: }"); | ^ @@ -36,7 +36,7 @@ LL | std::panic!("{}", "another one: }"); | +++++ warning: panic message contains an unused formatting placeholder - --> $DIR/non-fmt-panic.rs:16:25 + --> $DIR/non-fmt-panic.rs:22:25 | LL | core::panic!("Hello {}"); | ^^ @@ -52,7 +52,7 @@ LL | core::panic!("{}", "Hello {}"); | +++++ warning: panic message contains unused formatting placeholders - --> $DIR/non-fmt-panic.rs:17:21 + --> $DIR/non-fmt-panic.rs:23:21 | LL | assert!(false, "{:03x} {test} bla"); | ^^^^^^ ^^^^^^ @@ -68,7 +68,7 @@ LL | assert!(false, "{}", "{:03x} {test} bla"); | +++++ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:19:20 + --> $DIR/non-fmt-panic.rs:25:20 | LL | assert!(false, S); | ^ @@ -81,7 +81,7 @@ LL | assert!(false, "{}", S); | +++++ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:21:20 + --> $DIR/non-fmt-panic.rs:27:20 | LL | assert!(false, 123); | ^^^ @@ -94,7 +94,7 @@ LL | assert!(false, "{}", 123); | +++++ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:23:20 + --> $DIR/non-fmt-panic.rs:29:20 | LL | assert!(false, Some(123)); | ^^^^^^^^^ @@ -107,7 +107,7 @@ LL | assert!(false, "{:?}", Some(123)); | +++++++ warning: panic message contains braces - --> $DIR/non-fmt-panic.rs:25:27 + --> $DIR/non-fmt-panic.rs:31:27 | LL | debug_assert!(false, "{{}} bla"); | ^^^^ @@ -119,7 +119,7 @@ LL | debug_assert!(false, "{}", "{{}} bla"); | +++++ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:26:12 + --> $DIR/non-fmt-panic.rs:32:12 | LL | panic!(C); | ^ @@ -132,7 +132,7 @@ LL | panic!("{}", C); | +++++ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:27:12 + --> $DIR/non-fmt-panic.rs:33:12 | LL | panic!(S); | ^ @@ -145,7 +145,7 @@ LL | panic!("{}", S); | +++++ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:28:18 + --> $DIR/non-fmt-panic.rs:34:18 | LL | unreachable!(S); | ^ @@ -158,7 +158,7 @@ LL | unreachable!("{}", S); | +++++ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:29:18 + --> $DIR/non-fmt-panic.rs:35:18 | LL | unreachable!(S); | ^ @@ -171,7 +171,7 @@ LL | unreachable!("{}", S); | +++++ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:30:17 + --> $DIR/non-fmt-panic.rs:36:17 | LL | std::panic!(123); | ^^^ @@ -189,7 +189,7 @@ LL + std::panic::panic_any(123); | warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:31:18 + --> $DIR/non-fmt-panic.rs:37:18 | LL | core::panic!(&*"abc"); | ^^^^^^^ @@ -202,7 +202,7 @@ LL | core::panic!("{}", &*"abc"); | +++++ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:32:12 + --> $DIR/non-fmt-panic.rs:38:12 | LL | panic!(Some(123)); | ^^^^^^^^^ @@ -220,7 +220,7 @@ LL + std::panic::panic_any(Some(123)); | warning: panic message contains an unused formatting placeholder - --> $DIR/non-fmt-panic.rs:33:12 + --> $DIR/non-fmt-panic.rs:39:12 | LL | panic!(concat!("{", "}")); | ^^^^^^^^^^^^^^^^^ @@ -236,7 +236,7 @@ LL | panic!("{}", concat!("{", "}")); | +++++ warning: panic message contains braces - --> $DIR/non-fmt-panic.rs:34:5 + --> $DIR/non-fmt-panic.rs:40:5 | LL | panic!(concat!("{", "{")); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -248,7 +248,7 @@ LL | panic!("{}", concat!("{", "{")); | +++++ warning: panic message contains an unused formatting placeholder - --> $DIR/non-fmt-panic.rs:36:37 + --> $DIR/non-fmt-panic.rs:42:37 | LL | fancy_panic::fancy_panic!("test {} 123"); | ^^ @@ -256,7 +256,7 @@ LL | fancy_panic::fancy_panic!("test {} 123"); = note: this message is not used as a format string when given without arguments, but will be in Rust 2021 warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:46:12 + --> $DIR/non-fmt-panic.rs:52:12 | LL | panic!(a!()); | ^^^^ @@ -274,7 +274,7 @@ LL + std::panic::panic_any(a!()); | warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:47:18 + --> $DIR/non-fmt-panic.rs:53:18 | LL | unreachable!(a!()); | ^^^^ @@ -287,7 +287,7 @@ LL | unreachable!("{}", a!()); | +++++ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:49:12 + --> $DIR/non-fmt-panic.rs:55:12 | LL | panic!(format!("{}", 1)); | ^^^^^^^^^^^^^^^^ @@ -302,7 +302,7 @@ LL + panic!("{}", 1); | warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:50:18 + --> $DIR/non-fmt-panic.rs:56:18 | LL | unreachable!(format!("{}", 1)); | ^^^^^^^^^^^^^^^^ @@ -317,7 +317,7 @@ LL + unreachable!("{}", 1); | warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:51:20 + --> $DIR/non-fmt-panic.rs:57:20 | LL | assert!(false, format!("{}", 1)); | ^^^^^^^^^^^^^^^^ @@ -332,7 +332,7 @@ LL + assert!(false, "{}", 1); | warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:52:26 + --> $DIR/non-fmt-panic.rs:58:26 | LL | debug_assert!(false, format!("{}", 1)); | ^^^^^^^^^^^^^^^^ @@ -347,7 +347,7 @@ LL + debug_assert!(false, "{}", 1); | warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:54:12 + --> $DIR/non-fmt-panic.rs:60:12 | LL | panic![123]; | ^^^ @@ -365,7 +365,7 @@ LL + std::panic::panic_any(123); | warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:55:12 + --> $DIR/non-fmt-panic.rs:61:12 | LL | panic!{123}; | ^^^ @@ -383,7 +383,7 @@ LL + std::panic::panic_any(123); | warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:72:12 + --> $DIR/non-fmt-panic.rs:78:12 | LL | panic!(v); | ------ ^ @@ -394,7 +394,7 @@ LL | panic!(v); = note: for more information, see warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:73:20 + --> $DIR/non-fmt-panic.rs:79:20 | LL | assert!(false, v); | ^ @@ -403,7 +403,7 @@ LL | assert!(false, v); = note: for more information, see warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:77:12 + --> $DIR/non-fmt-panic.rs:83:12 | LL | panic!(v); | ^ @@ -421,7 +421,7 @@ LL + std::panic::panic_any(v); | warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:78:20 + --> $DIR/non-fmt-panic.rs:84:20 | LL | assert!(false, v); | ^ @@ -434,7 +434,7 @@ LL | assert!(false, "{:?}", v); | +++++++ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:82:12 + --> $DIR/non-fmt-panic.rs:88:12 | LL | panic!(v); | ^ @@ -452,7 +452,7 @@ LL + std::panic::panic_any(v); | warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:83:20 + --> $DIR/non-fmt-panic.rs:89:20 | LL | assert!(false, v); | ^ @@ -465,7 +465,7 @@ LL | assert!(false, "{}", v); | +++++ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:87:12 + --> $DIR/non-fmt-panic.rs:93:12 | LL | panic!(v); | ^ @@ -483,7 +483,7 @@ LL + std::panic::panic_any(v); | warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:88:20 + --> $DIR/non-fmt-panic.rs:94:20 | LL | assert!(false, v); | ^ diff --git a/tests/ui/macros/nonterminal-matching.rs b/tests/ui/macros/nonterminal-matching.rs index ca95e8fac050c..a03ede20c5499 100644 --- a/tests/ui/macros/nonterminal-matching.rs +++ b/tests/ui/macros/nonterminal-matching.rs @@ -16,7 +16,7 @@ macro complex_nonterminal($nt_item: item) { struct S; } - n!(a $nt_item b); //~ ERROR no rules expected item `enum E {}` + n!(a $nt_item b); //~ ERROR no rules expected `item` metavariable } simple_nonterminal!(a, 'a, (x, y, z)); // OK @@ -29,10 +29,10 @@ macro_rules! foo { (ident $x:ident) => { bar!(ident $x); }; (lifetime $x:lifetime) => { bar!(lifetime $x); }; (tt $x:tt) => { bar!(tt $x); }; - (expr $x:expr) => { bar!(expr $x); }; //~ ERROR: no rules expected expression `3` - (literal $x:literal) => { bar!(literal $x); }; //~ ERROR: no rules expected literal `4` + (expr $x:expr) => { bar!(expr $x); }; //~ ERROR: no rules expected `expr` metavariable + (literal $x:literal) => { bar!(literal $x); }; //~ ERROR: no rules expected `literal` metavariable (path $x:path) => { bar!(path $x); }; //~ ERROR: no rules expected `path` metavariable - (stmt $x:stmt) => { bar!(stmt $x); }; //~ ERROR: no rules expected statement `let abc = 0` + (stmt $x:stmt) => { bar!(stmt $x); }; //~ ERROR: no rules expected `stmt` metavariable } macro_rules! bar { diff --git a/tests/ui/macros/nonterminal-matching.stderr b/tests/ui/macros/nonterminal-matching.stderr index ba2b3e213ad08..d01561415664c 100644 --- a/tests/ui/macros/nonterminal-matching.stderr +++ b/tests/ui/macros/nonterminal-matching.stderr @@ -1,4 +1,4 @@ -error: no rules expected item `enum E {}` +error: no rules expected `item` metavariable --> $DIR/nonterminal-matching.rs:19:10 | LL | macro n(a $nt_item b) { @@ -10,7 +10,7 @@ LL | n!(a $nt_item b); LL | complex_nonterminal!(enum E {}); | ------------------------------- in this macro invocation | -note: while trying to match item `enum E {}` +note: while trying to match `item` metavariable --> $DIR/nonterminal-matching.rs:15:15 | LL | macro n(a $nt_item b) { @@ -23,7 +23,7 @@ LL | complex_nonterminal!(enum E {}); = help: try using `:tt` instead in the macro definition = note: this error originates in the macro `complex_nonterminal` (in Nightly builds, run with -Z macro-backtrace for more info) -error: no rules expected expression `3` +error: no rules expected `expr` metavariable --> $DIR/nonterminal-matching.rs:32:35 | LL | (expr $x:expr) => { bar!(expr $x); }; @@ -45,7 +45,7 @@ LL | (expr 3) => {}; = help: try using `:tt` instead in the macro definition = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) -error: no rules expected literal `4` +error: no rules expected `literal` metavariable --> $DIR/nonterminal-matching.rs:33:44 | LL | (literal $x:literal) => { bar!(literal $x); }; @@ -89,7 +89,7 @@ LL | (path a::b::c) => {}; = help: try using `:tt` instead in the macro definition = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) -error: no rules expected statement `let abc = 0` +error: no rules expected `stmt` metavariable --> $DIR/nonterminal-matching.rs:35:35 | LL | (stmt $x:stmt) => { bar!(stmt $x); }; diff --git a/tests/ui/macros/not-utf8.rs b/tests/ui/macros/not-utf8.rs index ad8ac39d230e4..0993c0688605f 100644 --- a/tests/ui/macros/not-utf8.rs +++ b/tests/ui/macros/not-utf8.rs @@ -1,7 +1,7 @@ -//@ error-pattern: did not contain valid UTF-8 //@ reference: input.encoding.utf8 //@ reference: input.encoding.invalid fn foo() { include!("not-utf8.bin"); + //~^ ERROR couldn't read `$DIR/not-utf8.bin`: stream did not contain valid UTF-8 } diff --git a/tests/ui/macros/not-utf8.stderr b/tests/ui/macros/not-utf8.stderr index 17ee8197ac8be..c310e70935e38 100644 --- a/tests/ui/macros/not-utf8.stderr +++ b/tests/ui/macros/not-utf8.stderr @@ -1,5 +1,5 @@ error: couldn't read `$DIR/not-utf8.bin`: stream did not contain valid UTF-8 - --> $DIR/not-utf8.rs:6:5 + --> $DIR/not-utf8.rs:5:5 | LL | include!("not-utf8.bin"); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,9 +7,8 @@ LL | include!("not-utf8.bin"); note: byte `193` is not valid utf-8 --> $DIR/not-utf8.bin:1:1 | -LL | �|�␂!5�cc␕␂�Ӻi��WWj�ȥ�'�}�␒�J�ȉ��W�␞O�@����␜w�V���LO����␔[ ␃_�'���SQ�~ذ��ų&��- ��lN~��!@␌ _#���kQ��h�␝�:�... +LL | �|�␂!5�cc␕␂�Ӻi��WWj�ȥ�'�}�␒�J�ȉ��W�␞O�@����␜w�V���LO����␔[ ␃_�'���SQ�~ذ��ų&��- ��lN~��!@␌ _#���kQ��h�␝�:�␜␇� | ^ - = note: this error originates in the macro `include` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/macros/remove-repetition-issue-139480.rs b/tests/ui/macros/remove-repetition-issue-139480.rs new file mode 100644 index 0000000000000..1efb4306763e4 --- /dev/null +++ b/tests/ui/macros/remove-repetition-issue-139480.rs @@ -0,0 +1,28 @@ +macro_rules! ciallo { + ($($v: vis)? $name: ident) => { + //~^ error: repetition matches empty token tree + }; +} + +macro_rules! meow { + ($name: ident $($v: vis)?) => { + //~^ error: repetition matches empty token tree + }; +} + +macro_rules! gbc { + ($name: ident $/* + this comment gets removed by the suggestion + */ + ($v: vis)?) => { + //~^ error: repetition matches empty token tree + }; +} + +ciallo!(hello); + +meow!(miaow, pub); + +gbc!(mygo,); + +fn main() {} diff --git a/tests/ui/macros/remove-repetition-issue-139480.stderr b/tests/ui/macros/remove-repetition-issue-139480.stderr new file mode 100644 index 0000000000000..c2475589ee9ad --- /dev/null +++ b/tests/ui/macros/remove-repetition-issue-139480.stderr @@ -0,0 +1,44 @@ +error: repetition matches empty token tree + --> $DIR/remove-repetition-issue-139480.rs:2:7 + | +LL | ($($v: vis)? $name: ident) => { + | ^^^^^^^^^ + | + = note: a `vis` fragment can already be empty +help: remove the `$(` and `)?` + | +LL - ($($v: vis)? $name: ident) => { +LL + ($v: vis $name: ident) => { + | + +error: repetition matches empty token tree + --> $DIR/remove-repetition-issue-139480.rs:8:20 + | +LL | ($name: ident $($v: vis)?) => { + | ^^^^^^^^^ + | + = note: a `vis` fragment can already be empty +help: remove the `$(` and `)?` + | +LL - ($name: ident $($v: vis)?) => { +LL + ($name: ident $v: vis) => { + | + +error: repetition matches empty token tree + --> $DIR/remove-repetition-issue-139480.rs:17:9 + | +LL | ($v: vis)?) => { + | ^^^^^^^^^ + | + = note: a `vis` fragment can already be empty +help: remove the `$(` and `)?` + | +LL - ($name: ident $/* +LL - this comment gets removed by the suggestion +LL - */ +LL - ($v: vis)?) => { +LL + ($name: ident $v: vis) => { + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui/macros/reparse-expr-issue-139495.rs b/tests/ui/macros/reparse-expr-issue-139495.rs new file mode 100644 index 0000000000000..89734cae0a61d --- /dev/null +++ b/tests/ui/macros/reparse-expr-issue-139495.rs @@ -0,0 +1,15 @@ +macro_rules! m1 { + ($abi: literal) => { extern $abi } //~ ERROR expected expression, found keyword `extern` +} + +macro_rules! m2 { + ($abi: expr) => { extern $abi } //~ ERROR expected expression, found keyword `extern` +} + +fn main() { + m1!(-2) +} + +fn f() { + m2!(-2) +} diff --git a/tests/ui/macros/reparse-expr-issue-139495.stderr b/tests/ui/macros/reparse-expr-issue-139495.stderr new file mode 100644 index 0000000000000..e2e05d67ecc22 --- /dev/null +++ b/tests/ui/macros/reparse-expr-issue-139495.stderr @@ -0,0 +1,24 @@ +error: expected expression, found keyword `extern` + --> $DIR/reparse-expr-issue-139495.rs:2:24 + | +LL | ($abi: literal) => { extern $abi } + | ^^^^^^ expected expression +... +LL | m1!(-2) + | ------- in this macro invocation + | + = note: this error originates in the macro `m1` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected expression, found keyword `extern` + --> $DIR/reparse-expr-issue-139495.rs:6:21 + | +LL | ($abi: expr) => { extern $abi } + | ^^^^^^ expected expression +... +LL | m2!(-2) + | ------- in this macro invocation + | + = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui/macros/return_from_external_macro.rs b/tests/ui/macros/return_from_external_macro.rs new file mode 100644 index 0000000000000..43fe99e63add4 --- /dev/null +++ b/tests/ui/macros/return_from_external_macro.rs @@ -0,0 +1,17 @@ +//@ aux-crate: ret_from_ext=return_from_external_macro.rs + +#![feature(super_let)] +extern crate ret_from_ext; + +fn foo() -> impl Sized { + drop(|| ret_from_ext::foo!()); + //~^ ERROR cannot return reference to local binding + + ret_from_ext::foo!() + //~^ ERROR temporary value dropped while borrowed +} +//~^ NOTE temporary value is freed at the end of this statement + +fn main() { + foo(); +} diff --git a/tests/ui/macros/return_from_external_macro.stderr b/tests/ui/macros/return_from_external_macro.stderr new file mode 100644 index 0000000000000..b6010b8ec793a --- /dev/null +++ b/tests/ui/macros/return_from_external_macro.stderr @@ -0,0 +1,29 @@ +error[E0515]: cannot return reference to local binding + --> $DIR/return_from_external_macro.rs:7:13 + | +LL | drop(|| ret_from_ext::foo!()); + | ^^^^^^^^^^^^^^^^^^^^ + | | + | returns a reference to data owned by the current function + | local binding introduced here + | + = note: this error originates in the macro `ret_from_ext::foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0716]: temporary value dropped while borrowed + --> $DIR/return_from_external_macro.rs:10:5 + | +LL | ret_from_ext::foo!() + | ^^^^^^^^^^^^^^^^^^^^ + | | + | creates a temporary value which is freed while still in use + | opaque type requires that borrow lasts for `'static` +LL | +LL | } + | - temporary value is freed at the end of this statement + | + = note: this error originates in the macro `ret_from_ext::foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0515, E0716. +For more information about an error, try `rustc --explain E0515`. diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.rs b/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.rs index cf47a1e67aed8..29f71d10719b7 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.rs +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.rs @@ -1,5 +1,6 @@ //@ check-pass //@ compile-flags: -Z unpretty=expanded +//@ edition: 2015 #![feature(core_intrinsics, generic_assert)] diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout b/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout index 8065d0dff8fc1..33193c78334c9 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout @@ -2,6 +2,7 @@ #![no_std] //@ check-pass //@ compile-flags: -Z unpretty=expanded +//@ edition: 2015 #![feature(core_intrinsics, generic_assert)] #[prelude_import] @@ -17,18 +18,18 @@ fn arbitrary_consuming_method_for_demonstration_purposes() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*{ - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - __local_bind0 - } as usize)) { + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + __local_bind0 + } as usize)) { - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem as usize\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem as usize\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; } fn addr_of() { @@ -39,12 +40,12 @@ fn addr_of() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!&*__local_bind0) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: &elem\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: &elem\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; } fn binary() { @@ -55,12 +56,12 @@ fn binary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*__local_bind0 == 1)) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem == 1\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem == 1\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; { #[allow(unused_imports)] @@ -68,12 +69,12 @@ fn binary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*__local_bind0 >= 1)) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem >= 1\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem >= 1\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; { #[allow(unused_imports)] @@ -81,12 +82,12 @@ fn binary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*__local_bind0 > 0)) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem > 0\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem > 0\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; { #[allow(unused_imports)] @@ -94,12 +95,12 @@ fn binary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*__local_bind0 < 3)) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem < 3\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem < 3\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; { #[allow(unused_imports)] @@ -107,12 +108,12 @@ fn binary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*__local_bind0 <= 3)) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem <= 3\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem <= 3\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; { #[allow(unused_imports)] @@ -120,12 +121,12 @@ fn binary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*__local_bind0 != 3)) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem != 3\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem != 3\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; } fn unary() { @@ -136,12 +137,12 @@ fn unary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!**__local_bind0) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: *elem\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: *elem\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; } fn main() {} diff --git a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr index ce7694ecb1da3..d9646760cea92 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr +++ b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr @@ -338,6 +338,9 @@ LL | no_curly__no_rhs_dollar__no_round!(a); error[E0425]: cannot find value `a` in this scope --> $DIR/syntax-errors.rs:152:37 | +LL | ( $i:ident ) => { count($i) }; + | -- due to this macro variable +... LL | no_curly__rhs_dollar__no_round!(a); | ^ not found in this scope diff --git a/tests/ui/macros/same-sequence-span.stderr b/tests/ui/macros/same-sequence-span.stderr index ff32ef9438614..34df201f5a5c7 100644 --- a/tests/ui/macros/same-sequence-span.stderr +++ b/tests/ui/macros/same-sequence-span.stderr @@ -18,7 +18,7 @@ error: `$x:expr` may be followed by `$y:tt`, which is not allowed for `expr` fra --> $DIR/same-sequence-span.rs:19:1 | LL | | macro_rules! manual_foo { - | |_________________________________^ not allowed after `expr` fragments + | |__________________________^not allowed after `expr` fragments ... LL | proc_macro_sequence::make_foo!(); | ^------------------------------- diff --git a/tests/ui/macros/std-2024-macros.rs b/tests/ui/macros/std-2024-macros.rs new file mode 100644 index 0000000000000..453c7ee16e5a6 --- /dev/null +++ b/tests/ui/macros/std-2024-macros.rs @@ -0,0 +1,13 @@ +// Tests a small handful of macros in the standard library how they handle the +// new behavior introduced in 2024 that allows `const{}` expressions. + +//@ check-pass + +fn main() { + assert_eq!(0, const { 0 }); + assert_eq!(const { 0 }, const { 0 }); + assert_eq!(const { 0 }, 0); + + let _: Vec> = vec![const { vec![] }]; + let _: Vec> = vec![const { vec![] }; 10]; +} diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index 40033f546d30b..3f3d9252adbe8 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -288,6 +288,9 @@ fn test_expr() { // ExprKind::OffsetOf: untestable because this test works pre-expansion. // ExprKind::MacCall + c1!(expr, [ mac!() ], "mac!()"); + c1!(expr, [ mac![] ], "mac![]"); + c1!(expr, [ mac! {} ], "mac! {}"); c1!(expr, [ mac!(...) ], "mac!(...)"); c1!(expr, [ mac![...] ], "mac![...]"); c1!(expr, [ mac! { ... } ], "mac! { ... }"); @@ -353,7 +356,8 @@ fn test_item() { c1!(item, [ pub extern crate self as std; ], "pub extern crate self as std;"); // ItemKind::Use - c1!(item, [ pub use crate::{a, b::c}; ], "pub use crate::{ a, b::c };"); // FIXME + c1!(item, [ pub use crate::{a, b::c}; ], "pub use crate::{a, b::c};"); + c1!(item, [ pub use crate::{ e, ff }; ], "pub use crate::{ e, ff };"); c1!(item, [ pub use A::*; ], "pub use A::*;"); // ItemKind::Static @@ -482,9 +486,12 @@ fn test_item() { c1!(item, [ impl ~const Struct {} ], "impl ~const Struct {}"); // ItemKind::MacCall + c1!(item, [ mac!(); ], "mac!();"); + c1!(item, [ mac![]; ], "mac![];"); + c1!(item, [ mac! {} ], "mac! {}"); c1!(item, [ mac!(...); ], "mac!(...);"); c1!(item, [ mac![...]; ], "mac![...];"); - c1!(item, [ mac! { ... } ], "mac! { ... }"); + c1!(item, [ mac! {...} ], "mac! {...}"); // ItemKind::MacroDef c1!(item, @@ -515,6 +522,8 @@ fn test_meta() { #[test] fn test_pat() { + // PatKind::Missing: untestable in isolation. + // PatKind::Wild c1!(pat, [ _ ], "_"); @@ -596,8 +605,11 @@ fn test_pat() { c1!(pat, [ (pat) ], "(pat)"); // PatKind::MacCall + c1!(pat, [ mac!() ], "mac!()"); + c1!(pat, [ mac![] ], "mac![]"); + c1!(pat, [ mac! {} ], "mac! {}"); c1!(pat, [ mac!(...) ], "mac!(...)"); - c1!(pat, [ mac![...] ], "mac![...]"); + c1!(pat, [ mac! [ ... ] ], "mac! [...]"); c1!(pat, [ mac! { ... } ], "mac! { ... }"); // Attributes are not allowed on patterns. @@ -642,6 +654,9 @@ fn test_stmt() { c1!(stmt, [ ; ], ";"); // StmtKind::MacCall + c1!(stmt, [ mac! ( ) ], "mac! ()"); + c1!(stmt, [ mac![] ], "mac![]"); + c1!(stmt, [ mac!{} ], "mac!{}"); c1!(stmt, [ mac!(...) ], "mac!(...)"); c1!(stmt, [ mac![...] ], "mac![...]"); c1!(stmt, [ mac! { ... } ], "mac! { ... }"); @@ -737,6 +752,9 @@ fn test_ty() { // TyKind::ImplicitSelf: there is no syntax for this. // TyKind::MacCall + c1!(ty, [ mac!() ], "mac!()"); + c1!(ty, [ mac![] ], "mac![]"); + c1!(ty, [ mac! { } ], "mac! {}"); c1!(ty, [ mac!(...) ], "mac!(...)"); c1!(ty, [ mac![...] ], "mac![...]"); c1!(ty, [ mac! { ... } ], "mac! { ... }"); diff --git a/tests/ui/macros/syntax-error-recovery.rs b/tests/ui/macros/syntax-error-recovery.rs index 6cf9d54e82636..e1681ea32a2ee 100644 --- a/tests/ui/macros/syntax-error-recovery.rs +++ b/tests/ui/macros/syntax-error-recovery.rs @@ -5,14 +5,14 @@ macro_rules! values { $( #[$attr] $token $($inner)? = $value, + //~^ ERROR expected one of `!` or `::`, found `` )* } }; } -//~^^^^^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `ty` metavariable +//~^^^^^^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `ty` metavariable //~| ERROR macro expansion ignores `ty` metavariable and any tokens following values!(STRING(1) as (String) => cfg(test),); -//~^ ERROR expected one of `!` or `::`, found `` fn main() {} diff --git a/tests/ui/macros/syntax-error-recovery.stderr b/tests/ui/macros/syntax-error-recovery.stderr index 61758fb9d7dc6..a2059aa1aa802 100644 --- a/tests/ui/macros/syntax-error-recovery.stderr +++ b/tests/ui/macros/syntax-error-recovery.stderr @@ -22,10 +22,10 @@ LL | values!(STRING(1) as (String) => cfg(test),); = note: the usage of `values!` is likely invalid in item context error: expected one of `!` or `::`, found `` - --> $DIR/syntax-error-recovery.rs:15:9 + --> $DIR/syntax-error-recovery.rs:7:17 | -LL | values!(STRING(1) as (String) => cfg(test),); - | ^^^^^^ expected one of `!` or `::` +LL | $token $($inner)? = $value, + | ^^^^^^ expected one of `!` or `::` error: aborting due to 3 previous errors diff --git a/tests/ui/macros/trace_faulty_macros.stderr b/tests/ui/macros/trace_faulty_macros.stderr index 73fed66e61906..e90d7a98db4c7 100644 --- a/tests/ui/macros/trace_faulty_macros.stderr +++ b/tests/ui/macros/trace_faulty_macros.stderr @@ -87,9 +87,9 @@ LL | let a = pat_macro!(); | ^^^^^^^^^^^^ | = note: expanding `pat_macro! { }` - = note: to `pat_macro! (A { a : a, b : 0, c : _, .. });` - = note: expanding `pat_macro! { A { a : a, b : 0, c : _, .. } }` - = note: to `A { a : a, b : 0, c : _, .. }` + = note: to `pat_macro! (A {a : a, b : 0, c : _, ..});` + = note: expanding `pat_macro! { A {a : a, b : 0, c : _, ..} }` + = note: to `A {a : a, b : 0, c : _, ..}` note: trace_macro --> $DIR/trace_faulty_macros.rs:53:5 diff --git a/tests/ui/macros/unknown-builtin.rs b/tests/ui/macros/unknown-builtin.rs index 048f5d68d3421..aa6e04d3fb859 100644 --- a/tests/ui/macros/unknown-builtin.rs +++ b/tests/ui/macros/unknown-builtin.rs @@ -1,12 +1,11 @@ -//@ error-pattern: attempted to define built-in macro more than once - #![feature(rustc_attrs)] #[rustc_builtin_macro] macro_rules! unknown { () => () } //~ ERROR cannot find a built-in macro with name `unknown` +// Defining another `line` builtin macro should not cause an error. #[rustc_builtin_macro] -macro_rules! line { () => () } //~ NOTE previously defined here +macro_rules! line { () => () } fn main() { line!(); diff --git a/tests/ui/macros/unknown-builtin.stderr b/tests/ui/macros/unknown-builtin.stderr index 22f54e04e54c3..1a83398891b9f 100644 --- a/tests/ui/macros/unknown-builtin.stderr +++ b/tests/ui/macros/unknown-builtin.stderr @@ -1,18 +1,8 @@ error: cannot find a built-in macro with name `unknown` - --> $DIR/unknown-builtin.rs:6:1 + --> $DIR/unknown-builtin.rs:4:1 | LL | macro_rules! unknown { () => () } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0773]: attempted to define built-in macro more than once - --> $SRC_DIR/core/src/macros/mod.rs:LL:COL - | -note: previously defined here - --> $DIR/unknown-builtin.rs:9:1 - | -LL | macro_rules! line { () => () } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0773`. diff --git a/tests/ui/macros/unreachable-arg.edition_2021.stderr b/tests/ui/macros/unreachable-arg.edition_2021.stderr index ddaa2b9c1efe1..fa776070789cc 100644 --- a/tests/ui/macros/unreachable-arg.edition_2021.stderr +++ b/tests/ui/macros/unreachable-arg.edition_2021.stderr @@ -1,5 +1,5 @@ error: format argument must be a string literal - --> $DIR/unreachable-arg.rs:15:18 + --> $DIR/unreachable-arg.rs:14:18 | LL | unreachable!(a); | ^ diff --git a/tests/ui/macros/unreachable-arg.rs b/tests/ui/macros/unreachable-arg.rs index 702bd053ab022..d18272c8072e0 100644 --- a/tests/ui/macros/unreachable-arg.rs +++ b/tests/ui/macros/unreachable-arg.rs @@ -6,11 +6,10 @@ //@ [edition_2015]run-fail //@ [edition_2021]check-fail //@ [edition_2015]error-pattern:internal error: entered unreachable code: hello -//@ [edition_2021]error-pattern:format argument must be a string literal #![allow(non_fmt_panics)] fn main() { let a = "hello"; - unreachable!(a); + unreachable!(a); //[edition_2021]~ ERROR format argument must be a string literal } diff --git a/tests/ui/macros/unreachable-format-args.edition_2015.stderr b/tests/ui/macros/unreachable-format-args.edition_2015.stderr index 6a133d5e40647..e2286b72e64ca 100644 --- a/tests/ui/macros/unreachable-format-args.edition_2015.stderr +++ b/tests/ui/macros/unreachable-format-args.edition_2015.stderr @@ -1,12 +1,11 @@ error: there is no argument named `x` - --> $DIR/unreachable-format-args.rs:13:5 + --> $DIR/unreachable-format-args.rs:12:5 | LL | unreachable!("x is {x} and y is {y}", y = 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: did you intend to capture a variable `x` from the surrounding scope? = note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro - = note: this error originates in the macro `$crate::concat` which comes from the expansion of the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/macros/unreachable-format-args.rs b/tests/ui/macros/unreachable-format-args.rs index 856fc992685ac..79a851348ffe1 100644 --- a/tests/ui/macros/unreachable-format-args.rs +++ b/tests/ui/macros/unreachable-format-args.rs @@ -5,10 +5,10 @@ //@ [edition_2021]edition:2021 //@ [edition_2015]check-fail //@ [edition_2021]run-fail -//@ [edition_2015]error-pattern:there is no argument named `x` //@ [edition_2021]error-pattern:internal error: entered unreachable code: x is 5 and y is 0 fn main() { let x = 5; unreachable!("x is {x} and y is {y}", y = 0); + //[edition_2015]~^ ERROR there is no argument named `x` } diff --git a/tests/ui/malformed/issue-107423-unused-delim-only-one-no-pair.rs b/tests/ui/malformed/issue-107423-unused-delim-only-one-no-pair.rs index da56fe03184a9..94e6900bf40ef 100644 --- a/tests/ui/malformed/issue-107423-unused-delim-only-one-no-pair.rs +++ b/tests/ui/malformed/issue-107423-unused-delim-only-one-no-pair.rs @@ -1,7 +1,4 @@ // check that we don't generate a span that points beyond EOF -//@ error-pattern: unclosed delimiter -//@ error-pattern: unclosed delimiter -//@ error-pattern: unclosed delimiter - +//~v ERROR this file contains an unclosed delimiter fn a(){{{ diff --git a/tests/ui/malformed/issue-107423-unused-delim-only-one-no-pair.stderr b/tests/ui/malformed/issue-107423-unused-delim-only-one-no-pair.stderr index d9748843fd7a8..9a86c267340c7 100644 --- a/tests/ui/malformed/issue-107423-unused-delim-only-one-no-pair.stderr +++ b/tests/ui/malformed/issue-107423-unused-delim-only-one-no-pair.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-107423-unused-delim-only-one-no-pair.rs:7:11 + --> $DIR/issue-107423-unused-delim-only-one-no-pair.rs:4:11 | LL | fn a(){{{ | ---^ diff --git a/tests/ui/malformed/malformed-derive-entry.stderr b/tests/ui/malformed/malformed-derive-entry.stderr index 3059d75d718a5..02036e8d4c90b 100644 --- a/tests/ui/malformed/malformed-derive-entry.stderr +++ b/tests/ui/malformed/malformed-derive-entry.stderr @@ -24,7 +24,6 @@ LL | #[derive(Copy(Bad))] | note: required by a bound in `Copy` --> $SRC_DIR/core/src/marker.rs:LL:COL - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Test1` with `#[derive(Clone)]` | LL + #[derive(Clone)] @@ -39,7 +38,6 @@ LL | #[derive(Copy="bad")] | note: required by a bound in `Copy` --> $SRC_DIR/core/src/marker.rs:LL:COL - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Test2` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/match/intended-binding-pattern-is-const.rs b/tests/ui/match/intended-binding-pattern-is-const.rs index 95c8119cdb9de..12a8e2c18bc15 100644 --- a/tests/ui/match/intended-binding-pattern-is-const.rs +++ b/tests/ui/match/intended-binding-pattern-is-const.rs @@ -1,8 +1,8 @@ fn main() { match 1 { //~ ERROR non-exhaustive patterns - //~^ patterns `i32::MIN..=3_i32` and `5_i32..=i32::MAX` not covered - //~| the matched value is of type `i32` - x => {} //~ this pattern doesn't introduce a new catch-all binding + //~^ NOTE patterns `i32::MIN..=3_i32` and `5_i32..=i32::MAX` not covered + //~| NOTE the matched value is of type `i32` + x => {} //~ NOTE this pattern doesn't introduce a new catch-all binding //~^ HELP ensure that all possible cases are being handled //~| HELP if you meant to introduce a binding, use a different name } diff --git a/tests/ui/match/issue-112438.rs b/tests/ui/match/issue-112438.rs deleted file mode 100644 index b2febe2921054..0000000000000 --- a/tests/ui/match/issue-112438.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass -#![feature(inline_const_pat)] -#![allow(dead_code)] -fn foo() { - match 0 { - const { 1 << 5 } | _ => {} - } -} - -fn main() {} diff --git a/tests/ui/match/issue-82392.rs b/tests/ui/match/issue-82392.rs index 6f9527fb337ba..4ae08fed93a29 100644 --- a/tests/ui/match/issue-82392.rs +++ b/tests/ui/match/issue-82392.rs @@ -1,6 +1,7 @@ // https://github.com/rust-lang/rust/issues/82329 //@ compile-flags: -Zunpretty=hir,typed //@ check-pass +//@ edition:2015 pub fn main() { if true { diff --git a/tests/ui/match/issue-82392.stdout b/tests/ui/match/issue-82392.stdout index 8949611ac12f4..a0d83d962e73e 100644 --- a/tests/ui/match/issue-82392.stdout +++ b/tests/ui/match/issue-82392.stdout @@ -5,12 +5,13 @@ extern crate std; // https://github.com/rust-lang/rust/issues/82329 //@ compile-flags: -Zunpretty=hir,typed //@ check-pass +//@ edition:2015 fn main() ({ - (if (true as bool) - ({ } as - ()) else if (let Some(a) = - ((Some as - fn(i32) -> Option {Option::::Some})((3 as i32)) as - Option) as bool) ({ } as ()) as ()) - } as ()) + (if (true as bool) { + } else if (let Some(a) = + ((Some as + fn(i32) -> Option {Option::::Some})((3 as i32)) as + Option) as bool) { + } as ()) +} as ()) diff --git a/tests/ui/match/match-struct.rs b/tests/ui/match/match-struct.rs index 4da7b436ba8a8..2160571302f91 100644 --- a/tests/ui/match/match-struct.rs +++ b/tests/ui/match/match-struct.rs @@ -2,10 +2,10 @@ struct S { a: isize } enum E { C(isize) } fn main() { - match (S { a: 1 }) { + match (S { a: 1 }) { //~ NOTE this expression has type `S` E::C(_) => (), //~^ ERROR mismatched types - //~| expected `S`, found `E` + //~| NOTE expected `S`, found `E` _ => () } } diff --git a/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs b/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs index f4cac46f7cd64..cbd35482d222e 100644 --- a/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs +++ b/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs @@ -1,7 +1,7 @@ #![feature(postfix_match)] fn main() { - Some(1).match { //~ non-exhaustive patterns + Some(1).match { //~ ERROR non-exhaustive patterns None => {}, } } diff --git a/tests/ui/match/privately-uninhabited-issue-137999.rs b/tests/ui/match/privately-uninhabited-issue-137999.rs new file mode 100644 index 0000000000000..918393a0c6acf --- /dev/null +++ b/tests/ui/match/privately-uninhabited-issue-137999.rs @@ -0,0 +1,44 @@ +//@ edition:2024 +//@ check-fail + +mod m { + enum Void {} + + pub struct Internal { + _v: Void, + } + + pub enum Test { + A(u32, u32), + B(Internal), + } +} + +use m::Test; + +pub fn f1(x: &mut Test) { + let r1: &mut u32 = match x { + Test::A(a, _) => a, + _ => todo!(), + }; + + let r2: &mut u32 = match x { //~ ERROR cannot use `*x` because it was mutably borrowed + Test::A(_, b) => b, + _ => todo!(), + }; + + let _ = *r1; + let _ = *r2; +} + +pub fn f2(x: &mut Test) { + let r = &mut *x; + match x { //~ ERROR cannot use `*x` because it was mutably borrowed + Test::A(_, _) => {} + _ => {} + } + + let _ = r; +} + +fn main() {} diff --git a/tests/ui/match/privately-uninhabited-issue-137999.stderr b/tests/ui/match/privately-uninhabited-issue-137999.stderr new file mode 100644 index 0000000000000..6f74a75375e32 --- /dev/null +++ b/tests/ui/match/privately-uninhabited-issue-137999.stderr @@ -0,0 +1,26 @@ +error[E0503]: cannot use `*x` because it was mutably borrowed + --> $DIR/privately-uninhabited-issue-137999.rs:25:30 + | +LL | Test::A(a, _) => a, + | - `x.0` is borrowed here +... +LL | let r2: &mut u32 = match x { + | ^ use of borrowed `x.0` +... +LL | let _ = *r1; + | --- borrow later used here + +error[E0503]: cannot use `*x` because it was mutably borrowed + --> $DIR/privately-uninhabited-issue-137999.rs:36:11 + | +LL | let r = &mut *x; + | ------- `*x` is borrowed here +LL | match x { + | ^ use of borrowed `*x` +... +LL | let _ = r; + | - borrow later used here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0503`. diff --git a/tests/ui/match/validate-range-endpoints.rs b/tests/ui/match/validate-range-endpoints.rs index 46d4239886d3c..678cedf016b9e 100644 --- a/tests/ui/match/validate-range-endpoints.rs +++ b/tests/ui/match/validate-range-endpoints.rs @@ -1,4 +1,3 @@ -#![feature(inline_const_pat)] #![allow(overlapping_range_endpoints)] fn main() { @@ -17,8 +16,6 @@ fn main() { // There isn't really a way to detect these 1..=TOO_BIG => {} //~^ ERROR lower range bound must be less than or equal to upper - 1..=const { 256 } => {} - //~^ ERROR lower range bound must be less than or equal to upper _ => {} } diff --git a/tests/ui/match/validate-range-endpoints.stderr b/tests/ui/match/validate-range-endpoints.stderr index 2d0538804a378..6a8a81a1cc645 100644 --- a/tests/ui/match/validate-range-endpoints.stderr +++ b/tests/ui/match/validate-range-endpoints.stderr @@ -1,59 +1,53 @@ error: literal out of range for `u8` - --> $DIR/validate-range-endpoints.rs:7:12 + --> $DIR/validate-range-endpoints.rs:6:12 | LL | 1..257 => {} | ^^^ this value does not fit into the type `u8` whose range is `0..=255` error: literal out of range for `u8` - --> $DIR/validate-range-endpoints.rs:9:13 + --> $DIR/validate-range-endpoints.rs:8:13 | LL | 1..=256 => {} | ^^^ this value does not fit into the type `u8` whose range is `0..=255` error[E0030]: lower range bound must be less than or equal to upper - --> $DIR/validate-range-endpoints.rs:18:9 + --> $DIR/validate-range-endpoints.rs:17:9 | LL | 1..=TOO_BIG => {} | ^^^^^^^^^^^ lower bound larger than upper bound -error[E0030]: lower range bound must be less than or equal to upper - --> $DIR/validate-range-endpoints.rs:20:9 - | -LL | 1..=const { 256 } => {} - | ^^^^^^^^^^^^^^^^^ lower bound larger than upper bound - error: literal out of range for `u64` - --> $DIR/validate-range-endpoints.rs:26:32 + --> $DIR/validate-range-endpoints.rs:23:32 | LL | 10000000000000000000..=99999999999999999999 => {} | ^^^^^^^^^^^^^^^^^^^^ this value does not fit into the type `u64` whose range is `0..=18446744073709551615` error: literal out of range for `i8` - --> $DIR/validate-range-endpoints.rs:32:12 + --> $DIR/validate-range-endpoints.rs:29:12 | LL | 0..129 => {} | ^^^ this value does not fit into the type `i8` whose range is `-128..=127` error: literal out of range for `i8` - --> $DIR/validate-range-endpoints.rs:34:13 + --> $DIR/validate-range-endpoints.rs:31:13 | LL | 0..=128 => {} | ^^^ this value does not fit into the type `i8` whose range is `-128..=127` error: literal out of range for `i8` - --> $DIR/validate-range-endpoints.rs:36:9 + --> $DIR/validate-range-endpoints.rs:33:9 | LL | -129..0 => {} | ^^^^ this value does not fit into the type `i8` whose range is `-128..=127` error: literal out of range for `i8` - --> $DIR/validate-range-endpoints.rs:38:9 + --> $DIR/validate-range-endpoints.rs:35:9 | LL | -10000..=-20 => {} | ^^^^^^ this value does not fit into the type `i8` whose range is `-128..=127` error[E0004]: non-exhaustive patterns: `i8::MIN..=-17_i8` and `1_i8..=i8::MAX` not covered - --> $DIR/validate-range-endpoints.rs:49:11 + --> $DIR/validate-range-endpoints.rs:46:11 | LL | match 0i8 { | ^^^ patterns `i8::MIN..=-17_i8` and `1_i8..=i8::MAX` not covered @@ -66,7 +60,7 @@ LL + i8::MIN..=-17_i8 | 1_i8..=i8::MAX => todo!() | error[E0004]: non-exhaustive patterns: `i8::MIN..=-17_i8` not covered - --> $DIR/validate-range-endpoints.rs:53:11 + --> $DIR/validate-range-endpoints.rs:50:11 | LL | match 0i8 { | ^^^ pattern `i8::MIN..=-17_i8` not covered @@ -78,7 +72,7 @@ LL ~ -10000.. => {}, LL + i8::MIN..=-17_i8 => todo!() | -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors Some errors have detailed explanations: E0004, E0030. For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/methods/bad-wf-when-selecting-method.rs b/tests/ui/methods/bad-wf-when-selecting-method.rs index 638d1ffa982fa..1f1a10f420462 100644 --- a/tests/ui/methods/bad-wf-when-selecting-method.rs +++ b/tests/ui/methods/bad-wf-when-selecting-method.rs @@ -12,7 +12,7 @@ fn test(t: T) { Wrapper(t).needs_sized(); //~^ ERROR the trait bound `T: Wf` is not satisfied //~| ERROR the trait bound `T: Wf` is not satisfied - //~| the method `needs_sized` exists for struct `Wrapper`, but its trait bounds were not satisfied + //~| ERROR the method `needs_sized` exists for struct `Wrapper`, but its trait bounds were not satisfied } fn main() {} diff --git a/tests/ui/methods/clone-missing.rs b/tests/ui/methods/clone-missing.rs new file mode 100644 index 0000000000000..c5ecd3f175e2d --- /dev/null +++ b/tests/ui/methods/clone-missing.rs @@ -0,0 +1,34 @@ +//! This test checks that calling `.clone()` on a type that does +//! not implement the `Clone` trait results in a compilation error. +//! The `NotClone` and AlsoNotClone structs do not derive or +//! implement `Clone`, so attempting to clone them should fail. + +struct NotClone { + i: isize, +} + +fn not_clone(i: isize) -> NotClone { + NotClone { i } +} + +struct AlsoNotClone { + i: isize, + j: NotClone, +} + +fn also_not_clone(i: isize) -> AlsoNotClone { + AlsoNotClone { + i, + j: NotClone { i: i }, + } +} + +fn main() { + let x = not_clone(10); + let _y = x.clone(); + //~^ ERROR no method named `clone` found + + let x = also_not_clone(10); + let _y = x.clone(); + //~^ ERROR no method named `clone` found +} diff --git a/tests/ui/methods/clone-missing.stderr b/tests/ui/methods/clone-missing.stderr new file mode 100644 index 0000000000000..8676e73c8ca25 --- /dev/null +++ b/tests/ui/methods/clone-missing.stderr @@ -0,0 +1,29 @@ +error[E0599]: no method named `clone` found for struct `NotClone` in the current scope + --> $DIR/clone-missing.rs:28:16 + | +LL | struct NotClone { + | --------------- method `clone` not found for this struct +... +LL | let _y = x.clone(); + | ^^^^^ method not found in `NotClone` + | + = 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: `Clone` + +error[E0599]: no method named `clone` found for struct `AlsoNotClone` in the current scope + --> $DIR/clone-missing.rs:32:16 + | +LL | struct AlsoNotClone { + | ------------------- method `clone` not found for this struct +... +LL | let _y = x.clone(); + | ^^^^^ method not found in `AlsoNotClone` + | + = 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: `Clone` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/methods/disambiguate-associated-function-first-arg.rs b/tests/ui/methods/disambiguate-associated-function-first-arg.rs index 4c8192fc14bd3..c88be8424814c 100644 --- a/tests/ui/methods/disambiguate-associated-function-first-arg.rs +++ b/tests/ui/methods/disambiguate-associated-function-first-arg.rs @@ -45,5 +45,5 @@ impl TraitB for T { fn test() { S.f(); - //~^ multiple applicable items in scope + //~^ ERROR multiple applicable items in scope } diff --git a/tests/ui/methods/disambiguate-multiple-blanket-impl.stderr b/tests/ui/methods/disambiguate-multiple-blanket-impl.stderr index 1b81dc5aafb7f..ee698ed0e75f6 100644 --- a/tests/ui/methods/disambiguate-multiple-blanket-impl.stderr +++ b/tests/ui/methods/disambiguate-multiple-blanket-impl.stderr @@ -1,18 +1,3 @@ -error[E0223]: ambiguous associated type - --> $DIR/disambiguate-multiple-blanket-impl.rs:36:12 - | -LL | let _: S::Type; - | ^^^^^^^ - | -help: use fully-qualified syntax - | -LL - let _: S::Type; -LL + let _: ::Type; - | -LL - let _: S::Type; -LL + let _: ::Type; - | - error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-blanket-impl.rs:30:8 | @@ -63,6 +48,21 @@ LL - S::CONST; LL + ::CONST; | +error[E0223]: ambiguous associated type + --> $DIR/disambiguate-multiple-blanket-impl.rs:36:12 + | +LL | let _: S::Type; + | ^^^^^^^ + | +help: use fully-qualified syntax + | +LL - let _: S::Type; +LL + let _: ::Type; + | +LL - let _: S::Type; +LL + let _: ::Type; + | + error: aborting due to 3 previous errors Some errors have detailed explanations: E0034, E0223. diff --git a/tests/ui/methods/disambiguate-multiple-impl.stderr b/tests/ui/methods/disambiguate-multiple-impl.stderr index 2563c2327b7a5..319d5886f8ed6 100644 --- a/tests/ui/methods/disambiguate-multiple-impl.stderr +++ b/tests/ui/methods/disambiguate-multiple-impl.stderr @@ -1,18 +1,3 @@ -error[E0223]: ambiguous associated type - --> $DIR/disambiguate-multiple-impl.rs:32:12 - | -LL | let _: S::Type = (); - | ^^^^^^^ - | -help: use fully-qualified syntax - | -LL - let _: S::Type = (); -LL + let _: ::Type = (); - | -LL - let _: S::Type = (); -LL + let _: ::Type = (); - | - error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-impl.rs:29:8 | @@ -38,6 +23,21 @@ LL - S::foo(&s); LL + B::foo(&s); | +error[E0223]: ambiguous associated type + --> $DIR/disambiguate-multiple-impl.rs:32:12 + | +LL | let _: S::Type = (); + | ^^^^^^^ + | +help: use fully-qualified syntax + | +LL - let _: S::Type = (); +LL + let _: ::Type = (); + | +LL - let _: S::Type = (); +LL + let _: ::Type = (); + | + error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-impl.rs:34:16 | diff --git a/tests/ui/methods/disambiguate-multiple-trait-2.stderr b/tests/ui/methods/disambiguate-multiple-trait-2.stderr index 08e264c20c893..f9119e6075018 100644 --- a/tests/ui/methods/disambiguate-multiple-trait-2.stderr +++ b/tests/ui/methods/disambiguate-multiple-trait-2.stderr @@ -1,26 +1,3 @@ -error[E0221]: ambiguous associated type `Type` in bounds of `T` - --> $DIR/disambiguate-multiple-trait-2.rs:23:12 - | -LL | type Type; - | --------- ambiguous `Type` from `A` -... -LL | type Type; - | --------- ambiguous `Type` from `B` -... -LL | let _: T::Type; - | ^^^^^^^ ambiguous associated type `Type` - | -help: use fully-qualified syntax to disambiguate - | -LL - let _: T::Type; -LL + let _: ::Type; - | -help: use fully-qualified syntax to disambiguate - | -LL - let _: T::Type; -LL + let _: ::Type; - | - error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-trait-2.rs:16:7 | @@ -73,19 +50,27 @@ LL - let _ = T::CONST; LL + let _ = ::CONST; | -error[E0223]: ambiguous associated type - --> $DIR/disambiguate-multiple-trait-2.rs:52:12 +error[E0221]: ambiguous associated type `Type` in bounds of `T` + --> $DIR/disambiguate-multiple-trait-2.rs:23:12 | -LL | let _: S::Type; - | ^^^^^^^ +LL | type Type; + | --------- ambiguous `Type` from `A` +... +LL | type Type; + | --------- ambiguous `Type` from `B` +... +LL | let _: T::Type; + | ^^^^^^^ ambiguous associated type `Type` | -help: use fully-qualified syntax +help: use fully-qualified syntax to disambiguate | -LL - let _: S::Type; -LL + let _: ::Type; +LL - let _: T::Type; +LL + let _: ::Type; | -LL - let _: S::Type; -LL + let _: ::Type; +help: use fully-qualified syntax to disambiguate + | +LL - let _: T::Type; +LL + let _: ::Type; | error[E0034]: multiple applicable items in scope @@ -138,6 +123,21 @@ LL - let _ = S::CONST; LL + let _ = ::CONST; | +error[E0223]: ambiguous associated type + --> $DIR/disambiguate-multiple-trait-2.rs:52:12 + | +LL | let _: S::Type; + | ^^^^^^^ + | +help: use fully-qualified syntax + | +LL - let _: S::Type; +LL + let _: ::Type; + | +LL - let _: S::Type; +LL + let _: ::Type; + | + error: aborting due to 6 previous errors Some errors have detailed explanations: E0034, E0221, E0223. diff --git a/tests/ui/methods/disambiguate-multiple-trait.stderr b/tests/ui/methods/disambiguate-multiple-trait.stderr index a977fe2cd033d..f35eec3b048cc 100644 --- a/tests/ui/methods/disambiguate-multiple-trait.stderr +++ b/tests/ui/methods/disambiguate-multiple-trait.stderr @@ -1,18 +1,3 @@ -error[E0223]: ambiguous associated type - --> $DIR/disambiguate-multiple-trait.rs:30:12 - | -LL | let _: S::Type; - | ^^^^^^^ - | -help: use fully-qualified syntax - | -LL - let _: S::Type; -LL + let _: ::Type; - | -LL - let _: S::Type; -LL + let _: ::Type; - | - error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-trait.rs:24:8 | @@ -63,6 +48,21 @@ LL - let _ = S::CONST; LL + let _ = ::CONST; | +error[E0223]: ambiguous associated type + --> $DIR/disambiguate-multiple-trait.rs:30:12 + | +LL | let _: S::Type; + | ^^^^^^^ + | +help: use fully-qualified syntax + | +LL - let _: S::Type; +LL + let _: ::Type; + | +LL - let _: S::Type; +LL + let _: ::Type; + | + error: aborting due to 3 previous errors Some errors have detailed explanations: E0034, E0223. diff --git a/tests/ui/methods/ident-from-macro-expansion.rs b/tests/ui/methods/ident-from-macro-expansion.rs new file mode 100644 index 0000000000000..38d2fee0e53c0 --- /dev/null +++ b/tests/ui/methods/ident-from-macro-expansion.rs @@ -0,0 +1,18 @@ +macro_rules! dot { + ($id:ident) => { + ().$id(); + } +} + +macro_rules! dispatch { + ($id:ident) => { + <()>::$id(); + } +} + +fn main() { + dot!(hello); + //~^ ERROR no method named `hello` found for unit type `()` in the current scope + dispatch!(hello); + //~^ ERROR no function or associated item named `hello` found for unit type `()` in the current scope +} diff --git a/tests/ui/methods/ident-from-macro-expansion.stderr b/tests/ui/methods/ident-from-macro-expansion.stderr new file mode 100644 index 0000000000000..b596ce29f6fee --- /dev/null +++ b/tests/ui/methods/ident-from-macro-expansion.stderr @@ -0,0 +1,21 @@ +error[E0599]: no method named `hello` found for unit type `()` in the current scope + --> $DIR/ident-from-macro-expansion.rs:14:10 + | +LL | ().$id(); + | --- due to this macro variable +... +LL | dot!(hello); + | ^^^^^ method not found in `()` + +error[E0599]: no function or associated item named `hello` found for unit type `()` in the current scope + --> $DIR/ident-from-macro-expansion.rs:16:15 + | +LL | <()>::$id(); + | --- due to this macro variable +... +LL | dispatch!(hello); + | ^^^^^ function or associated item not found in `()` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/methods/inherent-bound-in-probe.rs b/tests/ui/methods/inherent-bound-in-probe.rs index 9b9eb91559b3a..4add93e808d22 100644 --- a/tests/ui/methods/inherent-bound-in-probe.rs +++ b/tests/ui/methods/inherent-bound-in-probe.rs @@ -1,5 +1,3 @@ -//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" - // Fixes #110131 // // The issue is that we were constructing an `ImplDerived` cause code for the diff --git a/tests/ui/methods/inherent-bound-in-probe.stderr b/tests/ui/methods/inherent-bound-in-probe.stderr index 433ef02a2aa4c..77aed390c9ace 100644 --- a/tests/ui/methods/inherent-bound-in-probe.stderr +++ b/tests/ui/methods/inherent-bound-in-probe.stderr @@ -1,5 +1,5 @@ error[E0277]: `Helper<'a, T>` is not an iterator - --> $DIR/inherent-bound-in-probe.rs:40:21 + --> $DIR/inherent-bound-in-probe.rs:38:21 | LL | type IntoIter = Helper<'a, T>; | ^^^^^^^^^^^^^ `Helper<'a, T>` is not an iterator @@ -9,14 +9,14 @@ note: required by a bound in `std::iter::IntoIterator::IntoIter` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL error[E0275]: overflow evaluating the requirement `&_: IntoIterator` - --> $DIR/inherent-bound-in-probe.rs:44:9 + --> $DIR/inherent-bound-in-probe.rs:42:9 | LL | Helper::new(&self.0) | ^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_bound_in_probe`) note: required for `&BitReaderWrapper<_>` to implement `IntoIterator` - --> $DIR/inherent-bound-in-probe.rs:34:13 + --> $DIR/inherent-bound-in-probe.rs:32:13 | LL | impl<'a, T> IntoIterator for &'a BitReaderWrapper | ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | &'a T: IntoIterator, = note: 126 redundant requirements hidden = note: required for `&BitReaderWrapper>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `IntoIterator` note: required by a bound in `Helper` - --> $DIR/inherent-bound-in-probe.rs:18:12 + --> $DIR/inherent-bound-in-probe.rs:16:12 | LL | struct Helper<'a, T> | ------ required by a bound in this struct diff --git a/tests/ui/methods/issues/issue-90315.rs b/tests/ui/methods/issues/issue-90315.rs index fbecaf9b971ff..3a82454b7b4fe 100644 --- a/tests/ui/methods/issues/issue-90315.rs +++ b/tests/ui/methods/issues/issue-90315.rs @@ -3,7 +3,7 @@ fn main() { let arr = &[0, 1, 2, 3]; for _i in 0..arr.len().rev() { //~^ ERROR can't call method - //~| surround the range in parentheses + //~| HELP surround the range in parentheses // The above error used to say “the method `rev` exists for type `usize`”. // This regression test ensures it doesn't say that any more. } diff --git a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs index 9e53ff0791728..5ef1d0c6dc9b5 100644 --- a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs +++ b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + #![feature(arbitrary_self_types, coerce_unsized, dispatch_from_dyn, unsize)] #![feature(unsized_locals, unsized_fn_params)] //~^ WARN the feature `unsized_locals` is incomplete @@ -85,7 +87,7 @@ fn objectcandidate_impl() { // Observe the type of `z` is `u32` let _seetype: () = z; //~ ERROR mismatched types - //~| expected `()`, found `u32` + //~| NOTE expected `()`, found `u32` } fn traitcandidate_impl() { @@ -102,7 +104,7 @@ fn traitcandidate_impl() { // Observe the type of `z` is `u64` let _seetype: () = z; //~ ERROR mismatched types - //~| expected `()`, found `u64` + //~| NOTE expected `()`, found `u64` } fn traitcandidate_impl_with_nuisance() { @@ -137,7 +139,7 @@ fn neither_impl() { // Observe the type of `z` is `u8` let _seetype: () = z; //~ ERROR mismatched types - //~| expected `()`, found `u8` + //~| NOTE expected `()`, found `u8` } fn both_impls() { @@ -155,7 +157,7 @@ fn both_impls() { // Observe the type of `z` is `u32` let _seetype: () = z; //~ ERROR mismatched types - //~| expected `()`, found `u32` + //~| NOTE expected `()`, found `u32` } @@ -172,7 +174,7 @@ fn both_impls_with_nuisance() { // Observe the type of `z` is `u32` let _seetype: () = z; //~ ERROR mismatched types - //~| expected `()`, found `u32` + //~| NOTE expected `()`, found `u32` } fn main() { diff --git a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr index d6da3f2cc3988..213139a9b0b6d 100644 --- a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr +++ b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr @@ -1,5 +1,5 @@ warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:2:12 + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:4:12 | LL | #![feature(unsized_locals, unsized_fn_params)] | ^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #![feature(unsized_locals, unsized_fn_params)] = note: `#[warn(incomplete_features)]` on by default error[E0308]: mismatched types - --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:87:24 + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:89:24 | LL | let _seetype: () = z; | -- ^ expected `()`, found `u32` @@ -16,7 +16,7 @@ LL | let _seetype: () = z; | expected due to this error[E0308]: mismatched types - --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:104:24 + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:106:24 | LL | let _seetype: () = z; | -- ^ expected `()`, found `u64` @@ -24,23 +24,23 @@ LL | let _seetype: () = z; | expected due to this error[E0034]: multiple applicable items in scope - --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:122:15 + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:124:15 | LL | let z = x.foo(); | ^^^ multiple `foo` found | note: candidate #1 is defined in the trait `FinalFoo` - --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:59:5 + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:61:5 | LL | fn foo(&self) -> u8; | ^^^^^^^^^^^^^^^^^^^^ 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:72:9 + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:74:9 | LL | fn foo(self) {} | ^^^^^^^^^^^^ note: candidate #3 is defined in an impl of the trait `X` for the type `T` - --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:45:9 + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:47:9 | LL | fn foo(self: Smaht) -> u64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + let z = X::foo(x); | error[E0308]: mismatched types - --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:139:24 + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:141:24 | LL | let _seetype: () = z; | -- ^ expected `()`, found `u8` @@ -69,7 +69,7 @@ LL | let _seetype: () = z; | expected due to this error[E0308]: mismatched types - --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:157:24 + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:159:24 | LL | let _seetype: () = z; | -- ^ expected `()`, found `u32` @@ -77,7 +77,7 @@ LL | let _seetype: () = z; | expected due to this error[E0308]: mismatched types - --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:174:24 + --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:176:24 | LL | let _seetype: () = z; | -- ^ expected `()`, found `u32` diff --git a/tests/ui/methods/method-self-arg-1.rs b/tests/ui/methods/method-self-arg-1.rs index 5912b4ec2c3cb..a0056b540a72b 100644 --- a/tests/ui/methods/method-self-arg-1.rs +++ b/tests/ui/methods/method-self-arg-1.rs @@ -1,5 +1,7 @@ // Test method calls with self as an argument cannot subvert type checking. +//@ dont-require-annotations: NOTE + struct Foo; impl Foo { @@ -9,9 +11,9 @@ impl Foo { fn main() { let x = Foo; Foo::bar(x); //~ ERROR mismatched types - //~| expected `&Foo`, found `Foo` + //~| NOTE expected `&Foo`, found `Foo` Foo::bar(&42); //~ ERROR mismatched types - //~| expected `&Foo`, found `&{integer}` - //~| expected reference `&Foo` - //~| found reference `&{integer}` + //~| NOTE expected `&Foo`, found `&{integer}` + //~| NOTE expected reference `&Foo` + //~| NOTE found reference `&{integer}` } diff --git a/tests/ui/methods/method-self-arg-1.stderr b/tests/ui/methods/method-self-arg-1.stderr index dcc21acc5c0f4..1d5927da97fa2 100644 --- a/tests/ui/methods/method-self-arg-1.stderr +++ b/tests/ui/methods/method-self-arg-1.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/method-self-arg-1.rs:11:14 + --> $DIR/method-self-arg-1.rs:13:14 | LL | Foo::bar(x); | -------- ^ expected `&Foo`, found `Foo` @@ -7,7 +7,7 @@ LL | Foo::bar(x); | arguments to this function are incorrect | note: method defined here - --> $DIR/method-self-arg-1.rs:6:8 + --> $DIR/method-self-arg-1.rs:8:8 | LL | fn bar(&self) {} | ^^^ ----- @@ -17,7 +17,7 @@ LL | Foo::bar(&x); | + error[E0308]: mismatched types - --> $DIR/method-self-arg-1.rs:13:14 + --> $DIR/method-self-arg-1.rs:15:14 | LL | Foo::bar(&42); | -------- ^^^ expected `&Foo`, found `&{integer}` @@ -27,7 +27,7 @@ LL | Foo::bar(&42); = note: expected reference `&Foo` found reference `&{integer}` note: method defined here - --> $DIR/method-self-arg-1.rs:6:8 + --> $DIR/method-self-arg-1.rs:8:8 | LL | fn bar(&self) {} | ^^^ ----- diff --git a/tests/ui/methods/opaque_param_in_ufc.rs b/tests/ui/methods/opaque_param_in_ufc.rs index b170e6805f646..3b0c8b778ffca 100644 --- a/tests/ui/methods/opaque_param_in_ufc.rs +++ b/tests/ui/methods/opaque_param_in_ufc.rs @@ -11,11 +11,13 @@ impl Foo { type Bar = impl Sized; +#[define_opaque(Bar)] fn bar() -> Bar { 42_u32 } impl Foo { + #[define_opaque(Bar)] fn foo() -> Bar { Self::method(); Foo::::method(); diff --git a/tests/ui/minus-string.rs b/tests/ui/minus-string.rs deleted file mode 100644 index 8d9b8d8bbf49e..0000000000000 --- a/tests/ui/minus-string.rs +++ /dev/null @@ -1,3 +0,0 @@ -//@ error-pattern:cannot apply unary operator `-` to type `String` - -fn main() { -"foo".to_string(); } diff --git a/tests/ui/minus-string.stderr b/tests/ui/minus-string.stderr deleted file mode 100644 index cf63ec2441670..0000000000000 --- a/tests/ui/minus-string.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0600]: cannot apply unary operator `-` to type `String` - --> $DIR/minus-string.rs:3:13 - | -LL | fn main() { -"foo".to_string(); } - | ^^^^^^^^^^^^^^^^^^ cannot apply unary operator `-` - | -note: the foreign item type `String` doesn't implement `Neg` - --> $SRC_DIR/alloc/src/string.rs:LL:COL - | - = note: not implement `Neg` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0600`. diff --git a/tests/ui/mir-dataflow/README.md b/tests/ui/mir-dataflow/README.md index a3ab14b23c7db..886020226d031 100644 --- a/tests/ui/mir-dataflow/README.md +++ b/tests/ui/mir-dataflow/README.md @@ -42,12 +42,3 @@ each generated output path. on *entry* to each block, as well as the gen- and kill-sets that were so-called "transfer functions" summarizing the effect of each basic block. - - * (In addition to the `borrowck_graphviz_postflow` attribute-key - noted above, there is also `borrowck_graphviz_preflow`; it has the - same interface and generates the same set of files, but it renders - the dataflow state after building the gen- and kill-sets but - *before* running the dataflow analysis itself, so each entry-set is - just the initial default state for that dataflow analysis. This is - less useful for understanding the error message output in these - tests.) diff --git a/tests/ui/mir-dataflow/inits-1.rs b/tests/ui/mir-dataflow/inits-1.rs index 8fb1d4bc736d6..3331809f3594a 100644 --- a/tests/ui/mir-dataflow/inits-1.rs +++ b/tests/ui/mir-dataflow/inits-1.rs @@ -51,3 +51,5 @@ fn main() { foo(true, &mut S(13), S(14), S(15)); foo(false, &mut S(13), S(14), S(15)); } + +//~? ERROR stop_after_dataflow ended compilation diff --git a/tests/ui/mir-dataflow/liveness-enum.rs b/tests/ui/mir-dataflow/liveness-enum.rs index 5eb04ae8c8d37..515f36698fb48 100644 --- a/tests/ui/mir-dataflow/liveness-enum.rs +++ b/tests/ui/mir-dataflow/liveness-enum.rs @@ -20,3 +20,5 @@ fn foo() -> Option { } fn main() {} + +//~? ERROR stop_after_dataflow ended compilation diff --git a/tests/ui/mir-dataflow/liveness-projection.rs b/tests/ui/mir-dataflow/liveness-projection.rs index 486f31b635dca..edea6ee60f64e 100644 --- a/tests/ui/mir-dataflow/liveness-projection.rs +++ b/tests/ui/mir-dataflow/liveness-projection.rs @@ -30,3 +30,5 @@ fn foo() { } fn main() {} + +//~? ERROR stop_after_dataflow ended compilation diff --git a/tests/ui/mir-dataflow/liveness-ptr.rs b/tests/ui/mir-dataflow/liveness-ptr.rs index 786da523a3391..704949aa9c89e 100644 --- a/tests/ui/mir-dataflow/liveness-ptr.rs +++ b/tests/ui/mir-dataflow/liveness-ptr.rs @@ -26,3 +26,5 @@ fn foo() -> i32 { } fn main() {} + +//~? ERROR stop_after_dataflow ended compilation diff --git a/tests/ui/mir-dataflow/uninits-1.rs b/tests/ui/mir-dataflow/uninits-1.rs index c2b4284a7b4f8..c689512833d80 100644 --- a/tests/ui/mir-dataflow/uninits-1.rs +++ b/tests/ui/mir-dataflow/uninits-1.rs @@ -49,3 +49,5 @@ fn main() { foo(true, &mut S(13), S(14), S(15)); foo(false, &mut S(13), S(14), S(15)); } + +//~? ERROR stop_after_dataflow ended compilation diff --git a/tests/ui/mir-dataflow/uninits-2.rs b/tests/ui/mir-dataflow/uninits-2.rs index c584ee74afb48..04daf78e56f71 100644 --- a/tests/ui/mir-dataflow/uninits-2.rs +++ b/tests/ui/mir-dataflow/uninits-2.rs @@ -22,3 +22,5 @@ fn main() { foo(&mut S(13)); foo(&mut S(13)); } + +//~? ERROR stop_after_dataflow ended compilation diff --git a/tests/ui/mir/alignment/place_computation.rs b/tests/ui/mir/alignment/borrow_aligned_field_projection.rs similarity index 100% rename from tests/ui/mir/alignment/place_computation.rs rename to tests/ui/mir/alignment/borrow_aligned_field_projection.rs diff --git a/tests/ui/mir/alignment/borrow_misaligned_field_projection.rs b/tests/ui/mir/alignment/borrow_misaligned_field_projection.rs new file mode 100644 index 0000000000000..a22965ce1d873 --- /dev/null +++ b/tests/ui/mir/alignment/borrow_misaligned_field_projection.rs @@ -0,0 +1,16 @@ +//@ run-fail +//@ ignore-i686-pc-windows-msvc: #112480 +//@ compile-flags: -C debug-assertions +//@ error-pattern: misaligned pointer dereference: address must be a multiple of 0x4 but is + +struct Misalignment { + a: u32, +} + +fn main() { + let mut items: [Misalignment; 2] = [Misalignment { a: 0 }, Misalignment { a: 1 }]; + unsafe { + let ptr: *const Misalignment = items.as_ptr().byte_add(1); + let _ptr: &u32 = unsafe { &(*ptr).a }; + } +} diff --git a/tests/ui/mir/alignment/misaligned_borrow.rs b/tests/ui/mir/alignment/misaligned_borrow.rs new file mode 100644 index 0000000000000..de8912c703827 --- /dev/null +++ b/tests/ui/mir/alignment/misaligned_borrow.rs @@ -0,0 +1,12 @@ +//@ run-fail +//@ ignore-i686-pc-windows-msvc: #112480 +//@ compile-flags: -C debug-assertions +//@ error-pattern: misaligned pointer dereference: address must be a multiple of 0x4 but is + +fn main() { + let x = [0u32; 2]; + let ptr = x.as_ptr(); + unsafe { + let _ptr = &(*(ptr.byte_add(1))); + } +} diff --git a/tests/ui/mir/alignment/misaligned_mut_borrow.rs b/tests/ui/mir/alignment/misaligned_mut_borrow.rs new file mode 100644 index 0000000000000..bba20edecfddb --- /dev/null +++ b/tests/ui/mir/alignment/misaligned_mut_borrow.rs @@ -0,0 +1,12 @@ +//@ run-fail +//@ ignore-i686-pc-windows-msvc: #112480 +//@ compile-flags: -C debug-assertions +//@ error-pattern: misaligned pointer dereference: address must be a multiple of 0x4 but is + +fn main() { + let mut x = [0u32; 2]; + let ptr = x.as_mut_ptr(); + unsafe { + let _ptr = &mut (*(ptr.byte_add(1))); + } +} diff --git a/tests/ui/mir/enable_passes_validation.rs b/tests/ui/mir/enable_passes_validation.rs index 957e7d4d96dff..99b1ba528b0cc 100644 --- a/tests/ui/mir/enable_passes_validation.rs +++ b/tests/ui/mir/enable_passes_validation.rs @@ -1,21 +1,25 @@ //@ revisions: empty unprefixed all_unknown all_known mixed //@[empty] compile-flags: -Zmir-enable-passes= -//@[empty] error-pattern error: incorrect value `` for unstable option `mir-enable-passes` - a comma-separated list of strings, with elements beginning with + or - was expected //@[unprefixed] compile-flags: -Zmir-enable-passes=CheckAlignment -//@[unprefixed] error-pattern error: incorrect value `CheckAlignment` for unstable option `mir-enable-passes` - a comma-separated list of strings, with elements beginning with + or - was expected //@[all_unknown] check-pass //@[all_unknown] compile-flags: -Zmir-enable-passes=+ThisPass,-DoesNotExist -//@[all_unknown] error-pattern: warning: MIR pass `ThisPass` is unknown and will be ignored -//@[all_unknown] error-pattern: warning: MIR pass `DoesNotExist` is unknown and will be ignored //@[all_known] check-pass //@[all_known] compile-flags: -Zmir-enable-passes=+CheckAlignment,+LowerIntrinsics //@[mixed] check-pass //@[mixed] compile-flags: -Zmir-enable-passes=+ThisPassDoesNotExist,+CheckAlignment -//@[mixed] error-pattern: warning: MIR pass `ThisPassDoesNotExist` is unknown and will be ignored fn main() {} + +//[empty]~? ERROR incorrect value `` for unstable option `mir-enable-passes` +//[unprefixed]~? ERROR incorrect value `CheckAlignment` for unstable option `mir-enable-passes` +//[mixed]~? WARN MIR pass `ThisPassDoesNotExist` is unknown and will be ignored +//[mixed]~? WARN MIR pass `ThisPassDoesNotExist` is unknown and will be ignored +//[all_unknown]~? WARN MIR pass `ThisPass` is unknown and will be ignored +//[all_unknown]~? WARN MIR pass `DoesNotExist` is unknown and will be ignored +//[all_unknown]~? WARN MIR pass `ThisPass` is unknown and will be ignored +//[all_unknown]~? WARN MIR pass `DoesNotExist` is unknown and will be ignored diff --git a/tests/ui/mir/inline-causes-trimmed-paths.rs b/tests/ui/mir/inline-causes-trimmed-paths.rs new file mode 100644 index 0000000000000..d626ab4e1d9cd --- /dev/null +++ b/tests/ui/mir/inline-causes-trimmed-paths.rs @@ -0,0 +1,36 @@ +//@ build-pass +//@ compile-flags: -Zinline-mir + +trait Storage { + type Buffer: ?Sized; +} + +struct Array; +impl Storage for Array { + type Buffer = [(); N]; +} + +struct Slice; +impl Storage for Slice { + type Buffer = [()]; +} + +struct Wrap { + _b: S::Buffer, +} + +fn coerce(this: &Wrap>) -> &Wrap +where + Array: Storage, +{ + coerce_again(this) +} + +fn coerce_again(this: &Wrap>) -> &Wrap { + this +} + +fn main() { + let inner: Wrap> = Wrap { _b: [(); 1] }; + let _: &Wrap = coerce(&inner); +} diff --git a/tests/ui/mir/issue-105809.rs b/tests/ui/mir/issue-105809.rs index e7a8fb6526896..a46dcf350adf3 100644 --- a/tests/ui/mir/issue-105809.rs +++ b/tests/ui/mir/issue-105809.rs @@ -1,7 +1,8 @@ // Non-regression test ICE from issue #105809 and duplicates. //@ build-pass: the ICE is during codegen -//@ compile-flags: --edition 2018 -Zmir-opt-level=1 +//@ compile-flags: -Zmir-opt-level=1 +//@ edition: 2018 use std::{future::Future, pin::Pin}; diff --git a/tests/ui/mir/issue-75053.rs b/tests/ui/mir/issue-75053.rs index 38684f3548f29..4bc481b82e53e 100644 --- a/tests/ui/mir/issue-75053.rs +++ b/tests/ui/mir/issue-75053.rs @@ -1,6 +1,7 @@ +//@ check-pass //@ compile-flags: -Z mir-opt-level=3 -#![feature(type_alias_impl_trait, rustc_attrs)] +#![feature(type_alias_impl_trait)] use std::marker::PhantomData; @@ -13,13 +14,11 @@ trait MyFrom: Sized { fn my_from(value: T) -> Result; } -mod f { - pub trait F {} - impl F for () {} - pub type DummyT = impl F; - fn _dummy_t() -> DummyT {} -} -use f::*; +pub trait F {} +impl F for () {} +pub type DummyT = impl F; +#[define_opaque(DummyT)] +fn _dummy_t() -> DummyT {} struct Phantom1(PhantomData); struct Phantom2(PhantomData); @@ -45,8 +44,6 @@ impl>>, U> MyIndex> for Scope { } } -#[rustc_error] fn main() { - //~^ ERROR let _pos: Phantom1> = Scope::new().my_index(); } diff --git a/tests/ui/mir/issue-75053.stderr b/tests/ui/mir/issue-75053.stderr deleted file mode 100644 index a464d3266f4eb..0000000000000 --- a/tests/ui/mir/issue-75053.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fatal error triggered by #[rustc_error] - --> $DIR/issue-75053.rs:49:1 - | -LL | fn main() { - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/mir/issue-99852.rs b/tests/ui/mir/issue-99852.rs index 59459c672281e..af754cf854699 100644 --- a/tests/ui/mir/issue-99852.rs +++ b/tests/ui/mir/issue-99852.rs @@ -1,6 +1,6 @@ //@ check-pass //@ compile-flags: -Z validate-mir -#![feature(let_chains)] +//@ edition: 2024 fn lambda() -> U where diff --git a/tests/ui/mir/lint/assignment-overlap.rs b/tests/ui/mir/lint/assignment-overlap.rs index 6396cccd4e88c..bbc140904671e 100644 --- a/tests/ui/mir/lint/assignment-overlap.rs +++ b/tests/ui/mir/lint/assignment-overlap.rs @@ -2,7 +2,7 @@ //@ build-fail //@ failure-status: 101 //@ dont-check-compiler-stderr -//@ error-pattern: encountered `Assign` statement with overlapping memory + #![feature(custom_mir, core_intrinsics)] extern crate core; use core::intrinsics::mir::*; @@ -12,7 +12,8 @@ pub fn main() { mir! { let a: [u8; 1024]; { - a = a; + a = a; //~ ERROR broken MIR + //~^ ERROR encountered `Assign` statement with overlapping memory Return() } } diff --git a/tests/ui/mir/lint/call-overlap.rs b/tests/ui/mir/lint/call-overlap.rs index def78ea1e3b11..f43edfb873826 100644 --- a/tests/ui/mir/lint/call-overlap.rs +++ b/tests/ui/mir/lint/call-overlap.rs @@ -2,7 +2,7 @@ //@ build-fail //@ failure-status: 101 //@ dont-check-compiler-stderr -//@ error-pattern: encountered overlapping memory in `Move` arguments to `Call` + #![feature(custom_mir, core_intrinsics)] extern crate core; use core::intrinsics::mir::*; @@ -12,7 +12,8 @@ pub fn main() { mir! { let a: [u8; 1024]; { - Call(a = f(Move(a)), ReturnTo(bb1), UnwindUnreachable()) + Call(a = f(Move(a)), ReturnTo(bb1), UnwindUnreachable()) //~ ERROR broken MIR + //~^ ERROR encountered overlapping memory in `Move` arguments to `Call` } bb1 = { Return() diff --git a/tests/ui/mir/lint/no-storage.rs b/tests/ui/mir/lint/no-storage.rs index a6af8646f6ec6..68c1a2d2aabde 100644 --- a/tests/ui/mir/lint/no-storage.rs +++ b/tests/ui/mir/lint/no-storage.rs @@ -21,7 +21,7 @@ pub fn f(a: bool) { Goto(bb3) } bb3 = { - b = (); + b = (); //~ ERROR broken MIR RET = b; StorageDead(b); Return() diff --git a/tests/ui/mir/lint/storage-live.rs b/tests/ui/mir/lint/storage-live.rs index 3e0cc4ee06140..252e3b8456c2d 100644 --- a/tests/ui/mir/lint/storage-live.rs +++ b/tests/ui/mir/lint/storage-live.rs @@ -1,7 +1,5 @@ //@ compile-flags: -Zlint-mir -Ztreat-err-as-bug //@ failure-status: 101 -//@ error-pattern: broken MIR in -//@ error-pattern: StorageLive(_1) which already has storage here //@ normalize-stderr: "note: .*\n\n" -> "" //@ normalize-stderr: "thread 'rustc' panicked.*\n" -> "" //@ normalize-stderr: "storage_live\[....\]" -> "storage_live[HASH]" @@ -20,7 +18,8 @@ fn multiple_storage() { let a: usize; { StorageLive(a); - StorageLive(a); + StorageLive(a); //~ ERROR broken MIR + //~| ERROR StorageLive(_1) which already has storage here Return() } } diff --git a/tests/ui/mir/lint/storage-live.stderr b/tests/ui/mir/lint/storage-live.stderr index 651b8e2327ee6..50df9ae061fcc 100644 --- a/tests/ui/mir/lint/storage-live.stderr +++ b/tests/ui/mir/lint/storage-live.stderr @@ -1,12 +1,12 @@ error: internal compiler error: broken MIR in Item(DefId(0:8 ~ storage_live[HASH]::multiple_storage)) (after pass CheckForceInline) at bb0[1]: StorageLive(_1) which already has storage here - --> $DIR/storage-live.rs:23:13 + --> $DIR/storage-live.rs:21:13 | LL | StorageLive(a); | ^^^^^^^^^^^^^^ | note: delayed at compiler/rustc_mir_transform/src/lint.rs:LL:CC - disabled backtrace - --> $DIR/storage-live.rs:23:13 + --> $DIR/storage-live.rs:21:13 | LL | StorageLive(a); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/mir/lint/storage-return.rs b/tests/ui/mir/lint/storage-return.rs index d51aee9518fb3..c3e1f00bd720c 100644 --- a/tests/ui/mir/lint/storage-return.rs +++ b/tests/ui/mir/lint/storage-return.rs @@ -1,7 +1,7 @@ //@ compile-flags: -Zlint-mir -Ztreat-err-as-bug -Zeagerly-emit-delayed-bugs //@ failure-status: 101 //@ dont-check-compiler-stderr -//@ error-pattern: has storage when returning + #![feature(custom_mir, core_intrinsics)] extern crate core; use core::intrinsics::mir::*; @@ -13,7 +13,8 @@ fn main() { { StorageLive(a); RET = a; - Return() + Return() //~ ERROR broken MIR + //~^ ERROR has storage when returning } } } diff --git a/tests/ui/mir/mir_let_chains_drop_order.rs b/tests/ui/mir/mir_let_chains_drop_order.rs index 8991c6db7b98f..4794f3427ddac 100644 --- a/tests/ui/mir/mir_let_chains_drop_order.rs +++ b/tests/ui/mir/mir_let_chains_drop_order.rs @@ -6,7 +6,7 @@ // See `mir_drop_order.rs` for more information -#![feature(let_chains)] +#![cfg_attr(edition2021, feature(let_chains))] #![allow(irrefutable_let_patterns)] use std::cell::RefCell; diff --git a/tests/ui/mir/mir_match_guard_let_chains_drop_order.rs b/tests/ui/mir/mir_match_guard_let_chains_drop_order.rs new file mode 100644 index 0000000000000..e98d57d1154c4 --- /dev/null +++ b/tests/ui/mir/mir_match_guard_let_chains_drop_order.rs @@ -0,0 +1,110 @@ +//@ run-pass +//@ needs-unwind +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 + +// See `mir_drop_order.rs` for more information + +#![feature(if_let_guard)] +#![allow(irrefutable_let_patterns)] + +use std::cell::RefCell; +use std::panic; + +pub struct DropLogger<'a, T> { + extra: T, + id: usize, + log: &'a panic::AssertUnwindSafe>>, +} + +impl<'a, T> Drop for DropLogger<'a, T> { + fn drop(&mut self) { + self.log.0.borrow_mut().push(self.id); + } +} + +struct InjectedFailure; + +#[allow(unreachable_code)] +fn main() { + let log = panic::AssertUnwindSafe(RefCell::new(vec![])); + let d = |id, extra| DropLogger { extra, id: id, log: &log }; + let get = || -> Vec<_> { + let mut m = log.0.borrow_mut(); + let n = m.drain(..); + n.collect() + }; + + { + let _x = ( + d( + 0, + d( + 1, + match () { () if let Some(_) = d(2, Some(true)).extra + && let DropLogger { .. } = d(3, None) => { + None + } + _ => { + Some(true) + } + } + ) + .extra, + ), + d(4, None), + &d(5, None), + d(6, None), + match () { + () if let DropLogger { .. } = d(7, None) + && let DropLogger { .. } = d(8, None) => { + d(9, None) + } + _ => { + // 10 is not constructed + d(10, None) + } + }, + ); + assert_eq!(get(), vec![3, 2, 8, 7, 1]); + } + assert_eq!(get(), vec![0, 4, 6, 9, 5]); + + let _ = std::panic::catch_unwind(|| { + ( + d( + 11, + d( + 12, + match () { + () if let Some(_) = d(13, Some(true)).extra + && let DropLogger { .. } = d(14, None) => { + None + } + _ => { + Some(true) + } + } + ) + .extra, + ), + d(15, None), + &d(16, None), + d(17, None), + match () { + () if let DropLogger { .. } = d(18, None) + && let DropLogger { .. } = d(19, None) + => { + d(20, None) + } + _ => { + // 10 is not constructed + d(21, None) + } + }, + panic::panic_any(InjectedFailure), + ); + }); + assert_eq!(get(), vec![14, 13, 19, 18, 20, 17, 15, 11, 16, 12]); +} diff --git a/tests/ui/mir/validate/critical-edge.rs b/tests/ui/mir/validate/critical-edge.rs index 9048d08a22a73..2a3bf6a6181b4 100644 --- a/tests/ui/mir/validate/critical-edge.rs +++ b/tests/ui/mir/validate/critical-edge.rs @@ -5,7 +5,7 @@ //@ compile-flags: --crate-type=lib //@ failure-status: 101 //@ dont-check-compiler-stderr -//@ error-pattern: encountered critical edge in `Call` terminator + #![feature(custom_mir, core_intrinsics)] use core::intrinsics::mir::*; @@ -29,3 +29,5 @@ pub fn f(a: u32) -> u32 { } } } + +//~? RAW encountered critical edge in `Call` terminator diff --git a/tests/ui/mir/validate/noncleanup-cleanup.rs b/tests/ui/mir/validate/noncleanup-cleanup.rs index b46bb46952b8a..3010a3804ce79 100644 --- a/tests/ui/mir/validate/noncleanup-cleanup.rs +++ b/tests/ui/mir/validate/noncleanup-cleanup.rs @@ -2,16 +2,16 @@ // //@ failure-status: 101 //@ dont-check-compiler-stderr -//@ error-pattern: cleanuppad mismatch + #![feature(custom_mir, core_intrinsics)] extern crate core; use core::intrinsics::mir::*; #[custom_mir(dialect = "built")] -pub fn main() { +pub fn main() { //~ WARN function cannot return without recursing mir! { { - Call(RET = main(), ReturnTo(block), UnwindCleanup(block)) + Call(RET = main(), ReturnTo(block), UnwindCleanup(block)) //~ ERROR cleanuppad mismatch } block = { Return() diff --git a/tests/ui/mir/validate/noncleanup-resume.rs b/tests/ui/mir/validate/noncleanup-resume.rs index b2a0e92e0686c..a80e99e296772 100644 --- a/tests/ui/mir/validate/noncleanup-resume.rs +++ b/tests/ui/mir/validate/noncleanup-resume.rs @@ -2,7 +2,7 @@ // //@ failure-status: 101 //@ dont-check-compiler-stderr -//@ error-pattern: resume on non-cleanup block + #![feature(custom_mir, core_intrinsics)] extern crate core; use core::intrinsics::mir::*; @@ -11,7 +11,7 @@ use core::intrinsics::mir::*; pub fn main() { mir! { { - UnwindResume() + UnwindResume() //~ ERROR resume on non-cleanup block } } } diff --git a/tests/ui/mir/validate/noncleanup-terminate.rs b/tests/ui/mir/validate/noncleanup-terminate.rs index 24cf75e7d8e16..859c5e71c0ef0 100644 --- a/tests/ui/mir/validate/noncleanup-terminate.rs +++ b/tests/ui/mir/validate/noncleanup-terminate.rs @@ -2,7 +2,7 @@ // //@ failure-status: 101 //@ dont-check-compiler-stderr -//@ error-pattern: terminate on non-cleanup block + #![feature(custom_mir, core_intrinsics)] extern crate core; use core::intrinsics::mir::*; @@ -11,7 +11,7 @@ use core::intrinsics::mir::*; pub fn main() { mir! { { - UnwindTerminate(ReasonAbi) + UnwindTerminate(ReasonAbi) //~ ERROR terminate on non-cleanup block } } } diff --git a/tests/ui/mir/var_debug_ref.rs b/tests/ui/mir/var_debug_ref.rs new file mode 100644 index 0000000000000..1dcf38b5bb9f7 --- /dev/null +++ b/tests/ui/mir/var_debug_ref.rs @@ -0,0 +1,24 @@ +// Regression test for #138942, where a function was incorrectly internalized, despite the fact +// that it was referenced by a var debug info from another code generation unit. +// +//@ build-pass +//@ revisions: limited full +//@ compile-flags: -Ccodegen-units=4 +//@[limited] compile-flags: -Cdebuginfo=limited +//@[full] compile-flags: -Cdebuginfo=full +trait Fun { + const FUN: &'static fn(); +} +impl Fun for () { + const FUN: &'static fn() = &(detail::f as fn()); +} +mod detail { + // Place `f` in a distinct module to generate a separate code generation unit. + #[inline(never)] + pub(super) fn f() {} +} +fn main() { + // SingleUseConsts represents "x" using VarDebugInfoContents::Const. + // It is the only reference to `f` remaining. + let x = <() as ::Fun>::FUN; +} diff --git a/tests/ui/mismatched_types/assignment-operator-unimplemented.rs b/tests/ui/mismatched_types/assignment-operator-unimplemented.rs index 21df464d5e450..04a379bbd0484 100644 --- a/tests/ui/mismatched_types/assignment-operator-unimplemented.rs +++ b/tests/ui/mismatched_types/assignment-operator-unimplemented.rs @@ -3,5 +3,5 @@ struct Foo; fn main() { let mut a = Foo; let ref b = Foo; - a += *b; //~ Error: binary assignment operation `+=` cannot be applied to type `Foo` + a += *b; //~ ERROR binary assignment operation `+=` cannot be applied to type `Foo` } diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr index b716131061993..7912ed4d7071a 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr @@ -7,8 +7,8 @@ LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0); | required by a bound introduced by this call | = help: the trait `for<'a> FnMut(&'a as Iterator>::Item)` is not implemented for closure `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}` - = note: expected a closure with arguments `(i32,)` - found a closure with arguments `(& as Iterator>::Item,)` + = note: expected a closure with signature `for<'a> fn(&'a as Iterator>::Item)` + found a closure with signature `fn(i32)` note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL @@ -27,8 +27,8 @@ LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); | required by a bound introduced by this call | = help: the trait `for<'a> FnMut(&'a as Iterator>::Item)` is not implemented for closure `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}` - = note: expected a closure with arguments `(&&&i32,)` - found a closure with arguments `(& as Iterator>::Item,)` + = note: expected a closure with signature `for<'a> fn(&'a as Iterator>::Item)` + found a closure with signature `fn(&&&i32)` note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL diff --git a/tests/ui/mismatched_types/closure-mismatch.current.stderr b/tests/ui/mismatched_types/closure-mismatch.current.stderr new file mode 100644 index 0000000000000..378fe83ea89bd --- /dev/null +++ b/tests/ui/mismatched_types/closure-mismatch.current.stderr @@ -0,0 +1,38 @@ +error: implementation of `FnOnce` is not general enough + --> $DIR/closure-mismatch.rs:12:5 + | +LL | baz(|_| ()); + | ^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` + +error: implementation of `Fn` is not general enough + --> $DIR/closure-mismatch.rs:12:5 + | +LL | baz(|_| ()); + | ^^^^^^^^^^^ implementation of `Fn` is not general enough + | + = note: closure with signature `fn(&'2 ())` must implement `Fn<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 (),)>`, for some specific lifetime `'2` + +error: implementation of `FnOnce` is not general enough + --> $DIR/closure-mismatch.rs:16:5 + | +LL | baz(|x| ()); + | ^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` + +error: implementation of `Fn` is not general enough + --> $DIR/closure-mismatch.rs:16:5 + | +LL | baz(|x| ()); + | ^^^^^^^^^^^ implementation of `Fn` is not general enough + | + = note: closure with signature `fn(&'2 ())` must implement `Fn<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 (),)>`, for some specific lifetime `'2` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/mismatched_types/closure-mismatch.next.stderr b/tests/ui/mismatched_types/closure-mismatch.next.stderr new file mode 100644 index 0000000000000..6b4620aa8d1ba --- /dev/null +++ b/tests/ui/mismatched_types/closure-mismatch.next.stderr @@ -0,0 +1,67 @@ +error[E0277]: the trait bound `{closure@$DIR/closure-mismatch.rs:12:9: 12:12}: Foo` is not satisfied + --> $DIR/closure-mismatch.rs:12:9 + | +LL | baz(|_| ()); + | --- ^^^^^^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | + = help: the trait `for<'a> FnOnce(&'a ())` is not implemented for closure `{closure@$DIR/closure-mismatch.rs:12:9: 12:12}` + = note: expected a closure with signature `for<'a> fn(&'a ())` + found a closure with signature `fn(&())` +note: this is a known limitation of the trait solver that will be lifted in the future + --> $DIR/closure-mismatch.rs:12:9 + | +LL | baz(|_| ()); + | ----^^^---- + | | | + | | the trait solver is unable to infer the generic types that should be inferred from this argument + | add turbofish arguments to this call to specify the types manually, even if it's redundant +note: required for `{closure@$DIR/closure-mismatch.rs:12:9: 12:12}` to implement `Foo` + --> $DIR/closure-mismatch.rs:7:18 + | +LL | impl Foo for T {} + | ------- ^^^ ^ + | | + | unsatisfied trait bound introduced here +note: required by a bound in `baz` + --> $DIR/closure-mismatch.rs:9:11 + | +LL | fn baz(_: T) {} + | ^^^ required by this bound in `baz` + +error[E0277]: the trait bound `{closure@$DIR/closure-mismatch.rs:16:9: 16:12}: Foo` is not satisfied + --> $DIR/closure-mismatch.rs:16:9 + | +LL | baz(|x| ()); + | --- ^^^^^^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | + = help: the trait `for<'a> FnOnce(&'a ())` is not implemented for closure `{closure@$DIR/closure-mismatch.rs:16:9: 16:12}` + = note: expected a closure with signature `for<'a> fn(&'a ())` + found a closure with signature `fn(&())` +note: this is a known limitation of the trait solver that will be lifted in the future + --> $DIR/closure-mismatch.rs:16:9 + | +LL | baz(|x| ()); + | ----^^^---- + | | | + | | the trait solver is unable to infer the generic types that should be inferred from this argument + | add turbofish arguments to this call to specify the types manually, even if it's redundant +note: required for `{closure@$DIR/closure-mismatch.rs:16:9: 16:12}` to implement `Foo` + --> $DIR/closure-mismatch.rs:7:18 + | +LL | impl Foo for T {} + | ------- ^^^ ^ + | | + | unsatisfied trait bound introduced here +note: required by a bound in `baz` + --> $DIR/closure-mismatch.rs:9:11 + | +LL | fn baz(_: T) {} + | ^^^ required by this bound in `baz` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/mismatched_types/closure-mismatch.rs b/tests/ui/mismatched_types/closure-mismatch.rs index efaed4dc1b99d..1a24c760a6af6 100644 --- a/tests/ui/mismatched_types/closure-mismatch.rs +++ b/tests/ui/mismatched_types/closure-mismatch.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + trait Foo {} impl Foo for T {} @@ -6,9 +10,11 @@ fn baz(_: T) {} fn main() { baz(|_| ()); - //~^ ERROR implementation of `FnOnce` is not general enough - //~| ERROR implementation of `Fn` is not general enough + //[current]~^ ERROR implementation of `FnOnce` is not general enough + //[current]~| ERROR implementation of `Fn` is not general enough + //[next]~^^^ ERROR Foo` is not satisfied baz(|x| ()); - //~^ ERROR implementation of `FnOnce` is not general enough - //~| ERROR implementation of `Fn` is not general enough + //[current]~^ ERROR implementation of `FnOnce` is not general enough + //[current]~| ERROR implementation of `Fn` is not general enough + //[next]~^^^ ERROR Foo` is not satisfied } diff --git a/tests/ui/mismatched_types/closure-mismatch.stderr b/tests/ui/mismatched_types/closure-mismatch.stderr deleted file mode 100644 index 802110c6511de..0000000000000 --- a/tests/ui/mismatched_types/closure-mismatch.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: implementation of `FnOnce` is not general enough - --> $DIR/closure-mismatch.rs:8:5 - | -LL | baz(|_| ()); - | ^^^^^^^^^^^ implementation of `FnOnce` is not general enough - | - = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... - = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` - -error: implementation of `Fn` is not general enough - --> $DIR/closure-mismatch.rs:8:5 - | -LL | baz(|_| ()); - | ^^^^^^^^^^^ implementation of `Fn` is not general enough - | - = note: closure with signature `fn(&'2 ())` must implement `Fn<(&'1 (),)>`, for any lifetime `'1`... - = note: ...but it actually implements `Fn<(&'2 (),)>`, for some specific lifetime `'2` - -error: implementation of `FnOnce` is not general enough - --> $DIR/closure-mismatch.rs:11:5 - | -LL | baz(|x| ()); - | ^^^^^^^^^^^ implementation of `FnOnce` is not general enough - | - = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... - = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` - -error: implementation of `Fn` is not general enough - --> $DIR/closure-mismatch.rs:11:5 - | -LL | baz(|x| ()); - | ^^^^^^^^^^^ implementation of `Fn` is not general enough - | - = note: closure with signature `fn(&'2 ())` must implement `Fn<(&'1 (),)>`, for any lifetime `'1`... - = note: ...but it actually implements `Fn<(&'2 (),)>`, for some specific lifetime `'2` - -error: aborting due to 4 previous errors - diff --git a/tests/ui/mismatched_types/dont-point-return-on-E0308.rs b/tests/ui/mismatched_types/dont-point-return-on-E0308.rs index dca917e3ba1e8..750329d29c00d 100644 --- a/tests/ui/mismatched_types/dont-point-return-on-E0308.rs +++ b/tests/ui/mismatched_types/dont-point-return-on-E0308.rs @@ -2,7 +2,6 @@ async fn f(_: &()) {} //~^ NOTE function defined here -//~| NOTE // Second note is the span of the underlined argument, I think... fn main() { diff --git a/tests/ui/mismatched_types/dont-point-return-on-E0308.stderr b/tests/ui/mismatched_types/dont-point-return-on-E0308.stderr index 9d4852cff8c62..105dc5c2e7982 100644 --- a/tests/ui/mismatched_types/dont-point-return-on-E0308.stderr +++ b/tests/ui/mismatched_types/dont-point-return-on-E0308.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/dont-point-return-on-E0308.rs:11:11 + --> $DIR/dont-point-return-on-E0308.rs:10:11 | LL | f(()); | - ^^ expected `&()`, found `()` diff --git a/tests/ui/mismatched_types/issue-13033.rs b/tests/ui/mismatched_types/issue-13033.rs index fdb356e70c5f0..3b08857d7808e 100644 --- a/tests/ui/mismatched_types/issue-13033.rs +++ b/tests/ui/mismatched_types/issue-13033.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + trait Foo { fn bar(&mut self, other: &mut dyn Foo); } @@ -7,8 +9,8 @@ struct Baz; impl Foo for Baz { fn bar(&mut self, other: &dyn Foo) {} //~^ ERROR method `bar` has an incompatible type for trait - //~| expected signature `fn(&mut Baz, &mut dyn Foo)` - //~| found signature `fn(&mut Baz, &dyn Foo)` + //~| NOTE expected signature `fn(&mut Baz, &mut dyn Foo)` + //~| NOTE found signature `fn(&mut Baz, &dyn Foo)` } fn main() {} diff --git a/tests/ui/mismatched_types/issue-13033.stderr b/tests/ui/mismatched_types/issue-13033.stderr index 61786ef14c252..f12f81dcfa52e 100644 --- a/tests/ui/mismatched_types/issue-13033.stderr +++ b/tests/ui/mismatched_types/issue-13033.stderr @@ -1,11 +1,11 @@ error[E0053]: method `bar` has an incompatible type for trait - --> $DIR/issue-13033.rs:8:30 + --> $DIR/issue-13033.rs:10:30 | LL | fn bar(&mut self, other: &dyn Foo) {} | ^^^^^^^^ types differ in mutability | note: type in trait - --> $DIR/issue-13033.rs:2:30 + --> $DIR/issue-13033.rs:4:30 | LL | fn bar(&mut self, other: &mut dyn Foo); | ^^^^^^^^^^^^ diff --git a/tests/ui/mismatched_types/suggest-adding-or-removing-ref-for-binding-pattern.fixed b/tests/ui/mismatched_types/suggest-adding-or-removing-ref-for-binding-pattern.fixed index cf923362ad818..04e0fcca32d60 100644 --- a/tests/ui/mismatched_types/suggest-adding-or-removing-ref-for-binding-pattern.fixed +++ b/tests/ui/mismatched_types/suggest-adding-or-removing-ref-for-binding-pattern.fixed @@ -16,6 +16,6 @@ fn main() { match Blah::A(1, 1, 2) { Blah::A(_, x, y) | Blah::B(x, y) => {} //~^ ERROR mismatched types - //~| variable `y` is bound inconsistently across alternatives separated by `|` + //~| ERROR variable `y` is bound inconsistently across alternatives separated by `|` } } diff --git a/tests/ui/mismatched_types/suggest-adding-or-removing-ref-for-binding-pattern.rs b/tests/ui/mismatched_types/suggest-adding-or-removing-ref-for-binding-pattern.rs index dc556f6576aea..dd6329c909052 100644 --- a/tests/ui/mismatched_types/suggest-adding-or-removing-ref-for-binding-pattern.rs +++ b/tests/ui/mismatched_types/suggest-adding-or-removing-ref-for-binding-pattern.rs @@ -16,6 +16,6 @@ fn main() { match Blah::A(1, 1, 2) { Blah::A(_, x, y) | Blah::B(x, ref y) => {} //~^ ERROR mismatched types - //~| variable `y` is bound inconsistently across alternatives separated by `|` + //~| ERROR variable `y` is bound inconsistently across alternatives separated by `|` } } diff --git a/tests/ui/missing/missing-allocator.rs b/tests/ui/missing/missing-allocator.rs index 3a65e657d0bc4..60aa9fcc898b3 100644 --- a/tests/ui/missing/missing-allocator.rs +++ b/tests/ui/missing/missing-allocator.rs @@ -16,3 +16,5 @@ fn oom(_: core::alloc::Layout) -> ! { } extern crate alloc; + +//~? ERROR no global memory allocator found but one is required diff --git a/tests/ui/missing/missing-main.rs b/tests/ui/missing/missing-main.rs index 3cafca09afb38..2b8995fb8ff31 100644 --- a/tests/ui/missing/missing-main.rs +++ b/tests/ui/missing/missing-main.rs @@ -1,2 +1 @@ -//@ error-pattern: `main` function not found -fn mian() { } +fn mian() { } //~ ERROR `main` function not found in crate `missing_main` diff --git a/tests/ui/missing/missing-main.stderr b/tests/ui/missing/missing-main.stderr index 2139981eac0d3..51a299d6a850c 100644 --- a/tests/ui/missing/missing-main.stderr +++ b/tests/ui/missing/missing-main.stderr @@ -1,5 +1,5 @@ error[E0601]: `main` function not found in crate `missing_main` - --> $DIR/missing-main.rs:2:14 + --> $DIR/missing-main.rs:1:14 | LL | fn mian() { } | ^ consider adding a `main` function to `$DIR/missing-main.rs` diff --git a/tests/ui/missing/missing-return.rs b/tests/ui/missing/missing-return.rs index defd8a3bb78aa..4d48e7c13e2c5 100644 --- a/tests/ui/missing/missing-return.rs +++ b/tests/ui/missing/missing-return.rs @@ -1,5 +1,4 @@ -//@ error-pattern: return - -fn f() -> isize { } - +fn f() -> isize { } //~ ERROR mismatched types + //~| NOTE implicitly returns `()` as its body has no tail or `return` expression + //~| NOTE expected `isize`, found `()` fn main() { f(); } diff --git a/tests/ui/missing/missing-return.stderr b/tests/ui/missing/missing-return.stderr index 5f7fb504075ce..b2d202b9b572c 100644 --- a/tests/ui/missing/missing-return.stderr +++ b/tests/ui/missing/missing-return.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/missing-return.rs:3:11 + --> $DIR/missing-return.rs:1:11 | LL | fn f() -> isize { } | - ^^^^^ expected `isize`, found `()` diff --git a/tests/ui/missing_non_modrs_mod/foo.rs b/tests/ui/missing_non_modrs_mod/foo.rs index dd3e970b8c6e9..afdc5e39b84d2 100644 --- a/tests/ui/missing_non_modrs_mod/foo.rs +++ b/tests/ui/missing_non_modrs_mod/foo.rs @@ -1,4 +1,3 @@ -// -//@ ignore-test this is just a helper for the real test in this dir +//@ ignore-auxiliary (used by `./missing_non_modrs_mod.rs`) mod missing; diff --git a/tests/ui/missing_non_modrs_mod/foo_inline.rs b/tests/ui/missing_non_modrs_mod/foo_inline.rs index 9d46e9bdd0c36..ed6d3a49101da 100644 --- a/tests/ui/missing_non_modrs_mod/foo_inline.rs +++ b/tests/ui/missing_non_modrs_mod/foo_inline.rs @@ -1,4 +1,4 @@ -//@ ignore-test this is just a helper for the real test in this dir +//@ ignore-auxiliary (used by `./missing_non_modrs_mod_inline.rs`) mod inline { mod missing; diff --git a/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod.rs b/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod.rs index 4ff975af67d76..b1ac0756688fe 100644 --- a/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod.rs +++ b/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod.rs @@ -1,2 +1,4 @@ mod foo; fn main() {} + +//~? ERROR file not found for module `missing` diff --git a/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr b/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr index 4e48799318b6b..c084fbf00c26f 100644 --- a/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr +++ b/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr @@ -1,5 +1,5 @@ error[E0583]: file not found for module `missing` - --> $DIR/foo.rs:4:1 + --> $DIR/foo.rs:3:1 | LL | mod missing; | ^^^^^^^^^^^^ diff --git a/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.rs b/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.rs index 9ebb4f1bdbdb3..987fe1166d74a 100644 --- a/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.rs +++ b/tests/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.rs @@ -1,2 +1,4 @@ mod foo_inline; fn main() {} + +//~? ERROR file not found for module `missing` diff --git a/tests/ui/modules/issue-107649.rs b/tests/ui/modules/issue-107649.rs index af5758d798588..f93fb07e17af0 100644 --- a/tests/ui/modules/issue-107649.rs +++ b/tests/ui/modules/issue-107649.rs @@ -102,5 +102,5 @@ fn main() { (); (); (); - dbg!(lib::Dummy); //~ Error: `Dummy` doesn't implement `Debug` + dbg!(lib::Dummy); //~ ERROR `Dummy` doesn't implement `Debug` } diff --git a/tests/ui/modules/mod-pub-access.rs b/tests/ui/modules/mod-pub-access.rs new file mode 100644 index 0000000000000..c07e7a2ff3062 --- /dev/null +++ b/tests/ui/modules/mod-pub-access.rs @@ -0,0 +1,11 @@ +//@ run-pass +// This is a name resolution smoke test that ensures paths with more than one +// segment (e.g., `foo::bar`) resolve correctly. +// It also serves as a basic visibility test — confirming that a `pub` item +// inside a private module can still be accessed from outside that module. + +mod foo { + pub fn bar(_offset: usize) {} +} + +fn main() { foo::bar(0); } diff --git a/tests/ui/modules/mod_file_aux.rs b/tests/ui/modules/mod_file_aux.rs index f37296b3af0be..eec38d189b4a8 100644 --- a/tests/ui/modules/mod_file_aux.rs +++ b/tests/ui/modules/mod_file_aux.rs @@ -1,4 +1,3 @@ -//@ run-pass -//@ ignore-test Not a test. Used by other tests +//@ ignore-auxiliary (used by `./mod_file_with_path_attr.rs` and `mod_file.rs`) pub fn foo() -> isize { 10 } diff --git a/tests/ui/modules_and_files_visibility/mod_file_aux.rs b/tests/ui/modules_and_files_visibility/mod_file_aux.rs index 77390da75f8f3..6fac8dae3d766 100644 --- a/tests/ui/modules_and_files_visibility/mod_file_aux.rs +++ b/tests/ui/modules_and_files_visibility/mod_file_aux.rs @@ -1,3 +1,3 @@ -//@ ignore-test Not a test. Used by other tests +//@ ignore-auxiliary (used by `./mod_file_correct_spans.rs`) pub fn foo() -> isize { 10 } diff --git a/tests/ui/modules_and_files_visibility/mod_file_disambig_aux.rs b/tests/ui/modules_and_files_visibility/mod_file_disambig_aux.rs index e00b5629c0833..9a0b1c4b0d8e8 100644 --- a/tests/ui/modules_and_files_visibility/mod_file_disambig_aux.rs +++ b/tests/ui/modules_and_files_visibility/mod_file_disambig_aux.rs @@ -1 +1 @@ -//@ ignore-test not a test. aux file +//@ ignore-auxiliary (used by `./mod_file_disambig.rs`) diff --git a/tests/ui/modules_and_files_visibility/mod_file_disambig_aux/mod.rs b/tests/ui/modules_and_files_visibility/mod_file_disambig_aux/mod.rs index e00b5629c0833..232c933c4cbfc 100644 --- a/tests/ui/modules_and_files_visibility/mod_file_disambig_aux/mod.rs +++ b/tests/ui/modules_and_files_visibility/mod_file_disambig_aux/mod.rs @@ -1 +1 @@ -//@ ignore-test not a test. aux file +//@ ignore-auxiliary (used by `../mod_file_disambig.rs`) diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.current.fixed b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.current.fixed new file mode 100644 index 0000000000000..922c883f4f71f --- /dev/null +++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.current.fixed @@ -0,0 +1,31 @@ +//@ run-rustfix +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +#![allow(unused_variables, dead_code)] +use std::collections::{BTreeMap, HashSet}; + +#[derive(Debug, Eq, PartialEq, Hash)] +#[derive(Clone)] +enum Day { + Mon, +} + +struct Class { + days: BTreeMap>, +} + +impl Class { + fn do_stuff(&self) { + for (_, v) in &self.days { + let mut x: HashSet = v.clone(); //~ ERROR + let y: Vec = x.drain().collect(); + println!("{:?}", x); + } + } +} + +fn fail() { + let c = Class { days: BTreeMap::new() }; + c.do_stuff(); +} +fn main() {} diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.current.stderr b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.current.stderr new file mode 100644 index 0000000000000..301f3c3a458de --- /dev/null +++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.current.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:19:39 + | +LL | let mut x: HashSet = v.clone(); + | ------------ ^^^^^^^^^ expected `HashSet`, found `&HashSet` + | | + | expected due to this + | + = note: expected struct `HashSet<_>` + found reference `&HashSet<_>` +note: `HashSet` does not implement `Clone`, so `&HashSet` was cloned instead + --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:19:39 + | +LL | let mut x: HashSet = v.clone(); + | ^ + = help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied +help: consider annotating `Day` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | enum Day { + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.fixed b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.fixed deleted file mode 100644 index 85b1853c23b82..0000000000000 --- a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.fixed +++ /dev/null @@ -1,30 +0,0 @@ -//@ run-rustfix -#![allow(unused_variables, dead_code)] -use std::collections::BTreeMap; -use std::collections::HashSet; - -#[derive(Debug,Eq,PartialEq,Hash)] -#[derive(Clone)] -enum Day { - Mon, -} - -struct Class { - days: BTreeMap> -} - -impl Class { - fn do_stuff(&self) { - for (_, v) in &self.days { - let mut x: HashSet = v.clone(); //~ ERROR - let y: Vec = x.drain().collect(); - println!("{:?}", x); - } - } -} - -fn fail() { - let c = Class { days: BTreeMap::new() }; - c.do_stuff(); -} -fn main() {} diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.next.fixed b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.next.fixed new file mode 100644 index 0000000000000..922c883f4f71f --- /dev/null +++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.next.fixed @@ -0,0 +1,31 @@ +//@ run-rustfix +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +#![allow(unused_variables, dead_code)] +use std::collections::{BTreeMap, HashSet}; + +#[derive(Debug, Eq, PartialEq, Hash)] +#[derive(Clone)] +enum Day { + Mon, +} + +struct Class { + days: BTreeMap>, +} + +impl Class { + fn do_stuff(&self) { + for (_, v) in &self.days { + let mut x: HashSet = v.clone(); //~ ERROR + let y: Vec = x.drain().collect(); + println!("{:?}", x); + } + } +} + +fn fail() { + let c = Class { days: BTreeMap::new() }; + c.do_stuff(); +} +fn main() {} diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.next.stderr b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.next.stderr new file mode 100644 index 0000000000000..301f3c3a458de --- /dev/null +++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.next.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:19:39 + | +LL | let mut x: HashSet = v.clone(); + | ------------ ^^^^^^^^^ expected `HashSet`, found `&HashSet` + | | + | expected due to this + | + = note: expected struct `HashSet<_>` + found reference `&HashSet<_>` +note: `HashSet` does not implement `Clone`, so `&HashSet` was cloned instead + --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:19:39 + | +LL | let mut x: HashSet = v.clone(); + | ^ + = help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied +help: consider annotating `Day` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | enum Day { + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.rs b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.rs index 740cda470d913..6f7b55be8bd28 100644 --- a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.rs +++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.rs @@ -1,15 +1,16 @@ //@ run-rustfix +//@ revisions: current next +//@[next] compile-flags: -Znext-solver #![allow(unused_variables, dead_code)] -use std::collections::BTreeMap; -use std::collections::HashSet; +use std::collections::{BTreeMap, HashSet}; -#[derive(Debug,Eq,PartialEq,Hash)] +#[derive(Debug, Eq, PartialEq, Hash)] enum Day { Mon, } struct Class { - days: BTreeMap> + days: BTreeMap>, } impl Class { diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.stderr b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.stderr deleted file mode 100644 index 6a9d76f7998c9..0000000000000 --- a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:18:39 - | -LL | let mut x: HashSet = v.clone(); - | ------------ ^^^^^^^^^ expected `HashSet`, found `&HashSet` - | | - | expected due to this - | - = note: expected struct `HashSet<_>` - found reference `&HashSet<_>` -note: `HashSet` does not implement `Clone`, so `&HashSet` was cloned instead - --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:18:39 - | -LL | let mut x: HashSet = v.clone(); - | ^ - = help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied -help: consider annotating `Day` with `#[derive(Clone)]` - | -LL + #[derive(Clone)] -LL | enum Day { - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/moves/moves-based-on-type-match-bindings.rs b/tests/ui/moves/moves-based-on-type-match-bindings.rs index 4fb9b40e87526..407f097690045 100644 --- a/tests/ui/moves/moves-based-on-type-match-bindings.rs +++ b/tests/ui/moves/moves-based-on-type-match-bindings.rs @@ -10,12 +10,12 @@ fn f10() { let x = Foo {f: "hi".to_string()}; let y = match x { - Foo {f} => {} + Foo {f} => {} //~ NOTE value partially moved here }; touch(&x); //~ ERROR borrow of partially moved value: `x` - //~^ value borrowed here after partial move - //~| partial move occurs because `x.f` has type `String` + //~^ NOTE value borrowed here after partial move + //~| NOTE partial move occurs because `x.f` has type `String` } fn main() {} diff --git a/tests/ui/moves/moves-based-on-type-tuple.rs b/tests/ui/moves/moves-based-on-type-tuple.rs index 2e67d8f8a6913..c9951a930225e 100644 --- a/tests/ui/moves/moves-based-on-type-tuple.rs +++ b/tests/ui/moves/moves-based-on-type-tuple.rs @@ -2,7 +2,7 @@ fn dup(x: Box) -> Box<(Box,Box)> { Box::new((x, x)) - //~^ use of moved value: `x` [E0382] + //~^ ERROR use of moved value: `x` [E0382] } fn main() { diff --git a/tests/ui/moves/nested-loop-moved-value-wrong-continue.rs b/tests/ui/moves/nested-loop-moved-value-wrong-continue.rs index 0235b291df540..87800d314ed50 100644 --- a/tests/ui/moves/nested-loop-moved-value-wrong-continue.rs +++ b/tests/ui/moves/nested-loop-moved-value-wrong-continue.rs @@ -9,6 +9,8 @@ fn foo() { //~| NOTE inside of this loop //~| HELP consider moving the expression out of the loop //~| NOTE in this expansion of desugaring of `for` loop + //~| NOTE + //~| NOTE baz.push(foo); //~^ NOTE value moved here //~| HELP consider cloning the value @@ -30,17 +32,19 @@ fn main() { for foo in foos { //~^ NOTE this reinitialization might get skipped //~| NOTE move occurs because `foo` has type `String` + //~| NOTE for bar in &bars { //~^ NOTE inside of this loop //~| HELP consider moving the expression out of the loop //~| NOTE in this expansion of desugaring of `for` loop + //~| NOTE if foo == *bar { baz.push(foo); //~^ NOTE value moved here //~| HELP consider cloning the value continue; //~^ NOTE verify that your loop breaking logic is correct - //~| NOTE this `continue` advances the loop at line 33 + //~| NOTE this `continue` advances the loop at line 36 } } qux.push(foo); diff --git a/tests/ui/moves/nested-loop-moved-value-wrong-continue.stderr b/tests/ui/moves/nested-loop-moved-value-wrong-continue.stderr index cf863ff8af148..6ef1a4193b1ae 100644 --- a/tests/ui/moves/nested-loop-moved-value-wrong-continue.stderr +++ b/tests/ui/moves/nested-loop-moved-value-wrong-continue.stderr @@ -1,5 +1,5 @@ error[E0382]: use of moved value: `foo` - --> $DIR/nested-loop-moved-value-wrong-continue.rs:19:14 + --> $DIR/nested-loop-moved-value-wrong-continue.rs:21:14 | LL | for foo in foos { for bar in &bars { if foo == *bar { | --- ---------------- inside of this loop @@ -14,13 +14,13 @@ LL | qux.push(foo); | ^^^ value used here after move | note: verify that your loop breaking logic is correct - --> $DIR/nested-loop-moved-value-wrong-continue.rs:15:9 + --> $DIR/nested-loop-moved-value-wrong-continue.rs:17:9 | LL | for foo in foos { for bar in &bars { if foo == *bar { | --------------- ---------------- ... LL | continue; - | ^^^^^^^^ this `continue` advances the loop at $DIR/nested-loop-moved-value-wrong-continue.rs:6:23: 18:8 + | ^^^^^^^^ this `continue` advances the loop at $DIR/nested-loop-moved-value-wrong-continue.rs:6:23: 20:8 help: consider moving the expression out of the loop so it is only moved once | LL ~ for foo in foos { let mut value = baz.push(foo); @@ -36,7 +36,7 @@ LL | baz.push(foo.clone()); | ++++++++ error[E0382]: use of moved value: `foo` - --> $DIR/nested-loop-moved-value-wrong-continue.rs:46:18 + --> $DIR/nested-loop-moved-value-wrong-continue.rs:50:18 | LL | for foo in foos { | --- @@ -54,7 +54,7 @@ LL | qux.push(foo); | ^^^ value used here after move | note: verify that your loop breaking logic is correct - --> $DIR/nested-loop-moved-value-wrong-continue.rs:41:17 + --> $DIR/nested-loop-moved-value-wrong-continue.rs:45:17 | LL | for foo in foos { | --------------- @@ -63,7 +63,7 @@ LL | for bar in &bars { | ---------------- ... LL | continue; - | ^^^^^^^^ this `continue` advances the loop at line 33 + | ^^^^^^^^ this `continue` advances the loop at line 36 help: consider moving the expression out of the loop so it is only moved once | LL ~ let mut value = baz.push(foo); diff --git a/tests/ui/moves/use_of_moved_value_clone_suggestions.rs b/tests/ui/moves/use_of_moved_value_clone_suggestions.rs index d5c8d4e6bdf2b..9a0a397a73387 100644 --- a/tests/ui/moves/use_of_moved_value_clone_suggestions.rs +++ b/tests/ui/moves/use_of_moved_value_clone_suggestions.rs @@ -1,6 +1,6 @@ // `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn main() {} diff --git a/tests/ui/moves/use_of_moved_value_copy_suggestions.fixed b/tests/ui/moves/use_of_moved_value_copy_suggestions.fixed index a5e0dd819b461..53d9cb20b3de9 100644 --- a/tests/ui/moves/use_of_moved_value_copy_suggestions.fixed +++ b/tests/ui/moves/use_of_moved_value_copy_suggestions.fixed @@ -4,27 +4,27 @@ fn duplicate_t(t: T) -> (T, T) { //~^ HELP consider restricting type parameter `T` //~| HELP if `T` implemented `Clone`, you could clone the value - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_opt(t: Option) -> (Option, Option) { //~^ HELP consider restricting type parameter `T` - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { //~^ HELP consider restricting type parameter `T` - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { //~^ HELP consider restricting type parameters - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_custom(t: S) -> (S, S) { //~^ HELP consider restricting type parameter `T` - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } struct S(T); @@ -42,7 +42,7 @@ trait B {} // Test where bounds are added with different bound placements fn duplicate_custom_1(t: S) -> (S, S) where { //~^ HELP consider restricting type parameter `T` - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_custom_2(t: S) -> (S, S) @@ -50,7 +50,7 @@ where T: A + Copy + Trait, //~^ HELP consider further restricting { - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_custom_3(t: S) -> (S, S) @@ -59,7 +59,7 @@ where //~^ HELP consider further restricting T: B, { - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_custom_4(t: S) -> (S, S) @@ -67,14 +67,14 @@ fn duplicate_custom_4(t: S) -> (S, S) where T: B, { - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } #[rustfmt::skip] fn existing_colon(t: T) { //~^ HELP consider restricting type parameter `T` //~| HELP if `T` implemented `Clone`, you could clone the value - [t, t]; //~ use of moved value: `t` + [t, t]; //~ ERROR use of moved value: `t` } fn existing_colon_in_where(t: T) //~ HELP if `T` implemented `Clone`, you could clone the value @@ -82,7 +82,7 @@ where T:, T: Copy //~^ HELP consider further restricting type parameter `T` { - [t, t]; //~ use of moved value: `t` + [t, t]; //~ ERROR use of moved value: `t` } fn main() {} diff --git a/tests/ui/moves/use_of_moved_value_copy_suggestions.rs b/tests/ui/moves/use_of_moved_value_copy_suggestions.rs index 60ca03ed6984a..c8cfc80cd82df 100644 --- a/tests/ui/moves/use_of_moved_value_copy_suggestions.rs +++ b/tests/ui/moves/use_of_moved_value_copy_suggestions.rs @@ -4,27 +4,27 @@ fn duplicate_t(t: T) -> (T, T) { //~^ HELP consider restricting type parameter `T` //~| HELP if `T` implemented `Clone`, you could clone the value - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_opt(t: Option) -> (Option, Option) { //~^ HELP consider restricting type parameter `T` - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { //~^ HELP consider restricting type parameter `T` - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { //~^ HELP consider restricting type parameters - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_custom(t: S) -> (S, S) { //~^ HELP consider restricting type parameter `T` - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } struct S(T); @@ -42,7 +42,7 @@ trait B {} // Test where bounds are added with different bound placements fn duplicate_custom_1(t: S) -> (S, S) where { //~^ HELP consider restricting type parameter `T` - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_custom_2(t: S) -> (S, S) @@ -50,7 +50,7 @@ where T: A, //~^ HELP consider further restricting { - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_custom_3(t: S) -> (S, S) @@ -59,7 +59,7 @@ where //~^ HELP consider further restricting T: B, { - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } fn duplicate_custom_4(t: S) -> (S, S) @@ -67,14 +67,14 @@ fn duplicate_custom_4(t: S) -> (S, S) where T: B, { - (t, t) //~ use of moved value: `t` + (t, t) //~ ERROR use of moved value: `t` } #[rustfmt::skip] fn existing_colon(t: T) { //~^ HELP consider restricting type parameter `T` //~| HELP if `T` implemented `Clone`, you could clone the value - [t, t]; //~ use of moved value: `t` + [t, t]; //~ ERROR use of moved value: `t` } fn existing_colon_in_where(t: T) //~ HELP if `T` implemented `Clone`, you could clone the value @@ -82,7 +82,7 @@ where T:, //~^ HELP consider further restricting type parameter `T` { - [t, t]; //~ use of moved value: `t` + [t, t]; //~ ERROR use of moved value: `t` } fn main() {} diff --git a/tests/ui/mut/mut-pattern-mismatched.rs b/tests/ui/mut/mut-pattern-mismatched.rs index 700261fe40b65..d99831bdaf512 100644 --- a/tests/ui/mut/mut-pattern-mismatched.rs +++ b/tests/ui/mut/mut-pattern-mismatched.rs @@ -1,20 +1,22 @@ +//@ dont-require-annotations: NOTE + fn main() { let foo = &mut 1; // (separate lines to ensure the spans are accurate) let &_ //~ ERROR mismatched types - //~| expected mutable reference `&mut {integer}` - //~| found reference `&_` - //~| types differ in mutability + //~| NOTE expected mutable reference `&mut {integer}` + //~| NOTE found reference `&_` + //~| NOTE types differ in mutability = foo; let &mut _ = foo; let bar = &1; let &_ = bar; let &mut _ //~ ERROR mismatched types - //~| expected reference `&{integer}` - //~| found mutable reference `&mut _` - //~| types differ in mutability + //~| NOTE expected reference `&{integer}` + //~| NOTE found mutable reference `&mut _` + //~| NOTE types differ in mutability = bar; } diff --git a/tests/ui/mut/mut-pattern-mismatched.stderr b/tests/ui/mut/mut-pattern-mismatched.stderr index cad1cef5155d5..25b1bfefaa283 100644 --- a/tests/ui/mut/mut-pattern-mismatched.stderr +++ b/tests/ui/mut/mut-pattern-mismatched.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/mut-pattern-mismatched.rs:6:10 + --> $DIR/mut-pattern-mismatched.rs:8:10 | LL | let &_ | ^^ types differ in mutability @@ -11,7 +11,7 @@ LL | = foo; found reference `&_` error[E0308]: mismatched types - --> $DIR/mut-pattern-mismatched.rs:15:9 + --> $DIR/mut-pattern-mismatched.rs:17:9 | LL | let &mut _ | ^^^^^^ types differ in mutability diff --git a/tests/ui/nested-cfg-attrs.rs b/tests/ui/nested-cfg-attrs.rs index 0af28fc3d8ec6..941807a84310e 100644 --- a/tests/ui/nested-cfg-attrs.rs +++ b/tests/ui/nested-cfg-attrs.rs @@ -1,4 +1,4 @@ -#[cfg_attr(all(), cfg_attr(all(), cfg(FALSE)))] +#[cfg_attr(all(), cfg_attr(all(), cfg(false)))] fn f() {} fn main() { f() } //~ ERROR cannot find function `f` in this scope diff --git a/tests/ui/nested-ty-params.rs b/tests/ui/nested-ty-params.rs index b7cedf97c9100..c00c3bc337230 100644 --- a/tests/ui/nested-ty-params.rs +++ b/tests/ui/nested-ty-params.rs @@ -1,6 +1,7 @@ -//@ error-pattern:can't use generic parameters from outer item fn hd(v: Vec ) -> U { fn hd1(w: [U]) -> U { return w[0]; } + //~^ ERROR can't use generic parameters from outer item + //~| ERROR can't use generic parameters from outer item return hd1(v); } diff --git a/tests/ui/nested-ty-params.stderr b/tests/ui/nested-ty-params.stderr index a9cdec667199a..7ca65b421b25f 100644 --- a/tests/ui/nested-ty-params.stderr +++ b/tests/ui/nested-ty-params.stderr @@ -1,5 +1,5 @@ error[E0401]: can't use generic parameters from outer item - --> $DIR/nested-ty-params.rs:3:16 + --> $DIR/nested-ty-params.rs:2:16 | LL | fn hd(v: Vec ) -> U { | - type parameter from outer item @@ -9,7 +9,7 @@ LL | fn hd1(w: [U]) -> U { return w[0]; } | help: try introducing a local generic parameter here: `` error[E0401]: can't use generic parameters from outer item - --> $DIR/nested-ty-params.rs:3:23 + --> $DIR/nested-ty-params.rs:2:23 | LL | fn hd(v: Vec ) -> U { | - type parameter from outer item diff --git a/tests/ui/never_type/fallback-closure-wrap.fallback.stderr b/tests/ui/never_type/fallback-closure-wrap.fallback.stderr index ac99a1fc24cf0..afb929454c7da 100644 --- a/tests/ui/never_type/fallback-closure-wrap.fallback.stderr +++ b/tests/ui/never_type/fallback-closure-wrap.fallback.stderr @@ -9,7 +9,6 @@ LL | panic!("Can't connect to server."); = note: expected unit type `()` found type `!` = note: required for the cast from `Box<{closure@$DIR/fallback-closure-wrap.rs:18:40: 18:47}>` to `Box` - = note: this error originates in the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/never_type/fallback-closure-wrap.rs b/tests/ui/never_type/fallback-closure-wrap.rs index e7f7d5aae3f8b..106b82b5f781b 100644 --- a/tests/ui/never_type/fallback-closure-wrap.rs +++ b/tests/ui/never_type/fallback-closure-wrap.rs @@ -17,7 +17,7 @@ use std::marker::PhantomData; fn main() { let error = Closure::wrap(Box::new(move || { panic!("Can't connect to server."); - //[fallback]~^ to return `()`, but it returns `!` + //[fallback]~^ ERROR to return `()`, but it returns `!` }) as Box); } diff --git a/tests/ui/never_type/impl_trait_fallback2.rs b/tests/ui/never_type/impl_trait_fallback2.rs index 12c187b9e82aa..399bd72561b41 100644 --- a/tests/ui/never_type/impl_trait_fallback2.rs +++ b/tests/ui/never_type/impl_trait_fallback2.rs @@ -12,11 +12,13 @@ fn should_ret_unit() -> impl T { type Foo = impl T; +#[define_opaque(Foo)] fn a() -> Foo { //~^ ERROR `(): T` is not satisfied panic!() } +#[define_opaque(Foo)] fn b() -> Foo { 42 } diff --git a/tests/ui/never_type/impl_trait_fallback2.stderr b/tests/ui/never_type/impl_trait_fallback2.stderr index 4c32dce465b85..0f197aa5cc6fb 100644 --- a/tests/ui/never_type/impl_trait_fallback2.stderr +++ b/tests/ui/never_type/impl_trait_fallback2.stderr @@ -10,7 +10,7 @@ LL | panic!() = help: the trait `T` is implemented for `i32` error[E0277]: the trait bound `(): T` is not satisfied - --> $DIR/impl_trait_fallback2.rs:15:11 + --> $DIR/impl_trait_fallback2.rs:16:11 | LL | fn a() -> Foo { | ^^^ the trait `T` is not implemented for `()` diff --git a/tests/ui/never_type/impl_trait_fallback3.rs b/tests/ui/never_type/impl_trait_fallback3.rs index ed645b82394a0..3740aad73f0be 100644 --- a/tests/ui/never_type/impl_trait_fallback3.rs +++ b/tests/ui/never_type/impl_trait_fallback3.rs @@ -8,6 +8,7 @@ trait T { type Foo = impl T; +#[define_opaque(Foo)] fn a() -> Foo { //~^ ERROR the trait bound `(): T` is not satisfied // This is not a defining use, it doesn't actually constrain the opaque type. diff --git a/tests/ui/never_type/impl_trait_fallback3.stderr b/tests/ui/never_type/impl_trait_fallback3.stderr index fde8d0896dda2..11425a749534f 100644 --- a/tests/ui/never_type/impl_trait_fallback3.stderr +++ b/tests/ui/never_type/impl_trait_fallback3.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `(): T` is not satisfied - --> $DIR/impl_trait_fallback3.rs:11:11 + --> $DIR/impl_trait_fallback3.rs:12:11 | LL | fn a() -> Foo { | ^^^ the trait `T` is not implemented for `()` diff --git a/tests/ui/never_type/impl_trait_fallback4.rs b/tests/ui/never_type/impl_trait_fallback4.rs index fe62773fa02db..2fa3d0028be8e 100644 --- a/tests/ui/never_type/impl_trait_fallback4.rs +++ b/tests/ui/never_type/impl_trait_fallback4.rs @@ -15,6 +15,7 @@ fn foo() -> impl T { panic!() } +#[define_opaque(Foo)] fn a() -> Foo { foo() } diff --git a/tests/ui/never_type/issue-10176.rs b/tests/ui/never_type/issue-10176.rs index 5ac4359c5016f..41e012d023f9c 100644 --- a/tests/ui/never_type/issue-10176.rs +++ b/tests/ui/never_type/issue-10176.rs @@ -1,9 +1,9 @@ -fn f() -> isize { +fn f() -> isize { //~ NOTE expected `isize` because of return type (return 1, return 2) //~^ ERROR mismatched types -//~| expected type `isize` -//~| found tuple `(!, !)` -//~| expected `isize`, found `(!, !)` +//~| NOTE expected type `isize` +//~| NOTE found tuple `(!, !)` +//~| NOTE expected `isize`, found `(!, !)` } fn main() {} diff --git a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs index be882085c5c90..73597408d1061 100644 --- a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs +++ b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs @@ -15,7 +15,7 @@ struct S1 { impl S1 { fn new(_x: u64) -> S1 { S1 { a: unsafe { &mut X1 } } - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] + //~^ WARN mutable reference to mutable static [static_mut_refs] } } diff --git a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr index 9dbd676969364..8268f5df23655 100644 --- a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr +++ b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr @@ -1,4 +1,4 @@ -warning: creating a mutable reference to mutable static is discouraged +warning: creating a mutable reference to mutable static --> $DIR/borrowck-thread-local-static-mut-borrow-outlives-fn.rs:17:26 | LL | S1 { a: unsafe { &mut X1 } } diff --git a/tests/ui/nll/borrowed-match-issue-45045.rs b/tests/ui/nll/borrowed-match-issue-45045.rs index 978eeb868edc0..a252dc817caf2 100644 --- a/tests/ui/nll/borrowed-match-issue-45045.rs +++ b/tests/ui/nll/borrowed-match-issue-45045.rs @@ -10,7 +10,7 @@ fn main() { let f = &mut e; let g = f; match e { - //~^ cannot use `e` because it was mutably borrowed [E0503] + //~^ ERROR cannot use `e` because it was mutably borrowed [E0503] Xyz::A => println!("a"), Xyz::B => println!("b"), }; diff --git a/tests/ui/nll/issue-48697.rs b/tests/ui/nll/issue-48697.rs index 16e29ab2a8ad2..8cb5fc36949a0 100644 --- a/tests/ui/nll/issue-48697.rs +++ b/tests/ui/nll/issue-48697.rs @@ -4,7 +4,7 @@ fn foo(x: &i32) -> &i32 { let z = 4; let f = &|y| y; let k = f(&z); - f(x) //~ cannot return value referencing local variable + f(x) //~ ERROR cannot return value referencing local variable } fn main() {} diff --git a/tests/ui/nll/issue-54779-anon-static-lifetime.rs b/tests/ui/nll/issue-54779-anon-static-lifetime.rs index 6b8fa608ebbb2..49e4180fe8ce0 100644 --- a/tests/ui/nll/issue-54779-anon-static-lifetime.rs +++ b/tests/ui/nll/issue-54779-anon-static-lifetime.rs @@ -29,7 +29,7 @@ impl DebugWith for Foo { fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { let Foo { bar } = self; - bar.debug_with(cx); //~ borrowed data escapes outside of method + bar.debug_with(cx); //~ ERROR borrowed data escapes outside of method Ok(()) } } diff --git a/tests/ui/nll/issue-97997.rs b/tests/ui/nll/issue-97997.rs index c64e720b12f7f..8e0fbe89ac230 100644 --- a/tests/ui/nll/issue-97997.rs +++ b/tests/ui/nll/issue-97997.rs @@ -12,5 +12,4 @@ fn main() { ::ASSOC; //~^ ERROR implementation of `Foo` is not general enough - //~| ERROR implementation of `Foo` is not general enough } diff --git a/tests/ui/nll/issue-97997.stderr b/tests/ui/nll/issue-97997.stderr index 89eaf77adf063..ff08daaeaac07 100644 --- a/tests/ui/nll/issue-97997.stderr +++ b/tests/ui/nll/issue-97997.stderr @@ -7,15 +7,5 @@ LL | ::ASSOC; = note: `Foo` would have to be implemented for the type `for<'a> fn(&'a u8)` = note: ...but `Foo` is actually implemented for the type `fn(&'0 u8)`, for some specific lifetime `'0` -error: implementation of `Foo` is not general enough - --> $DIR/issue-97997.rs:13:5 - | -LL | ::ASSOC; - | ^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough - | - = note: `Foo` would have to be implemented for the type `for<'a> fn(&'a u8)` - = note: ...but `Foo` is actually implemented for the type `fn(&'0 u8)`, for some specific lifetime `'0` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/nll/member-constraints/nested-impl-trait-fail.rs b/tests/ui/nll/member-constraints/nested-impl-trait-fail.rs index d676c967f30f5..0bf32a2624fcf 100644 --- a/tests/ui/nll/member-constraints/nested-impl-trait-fail.rs +++ b/tests/ui/nll/member-constraints/nested-impl-trait-fail.rs @@ -15,8 +15,8 @@ where 's: 'b, { [a] - //~^ E0700 - //~| E0700 + //~^ ERROR E0700 + //~| ERROR E0700 } // Same as the above but with late-bound regions. @@ -26,8 +26,8 @@ fn fail_late_bound<'s, 'a, 'b>( _: &'b &'s u8, ) -> impl IntoIterator + Cap<'b>> { [a] - //~^ E0700 - //~| E0700 + //~^ ERROR E0700 + //~| ERROR E0700 } fn main() {} diff --git a/tests/ui/nll/nested-bodies-in-dead-code.rs b/tests/ui/nll/nested-bodies-in-dead-code.rs new file mode 100644 index 0000000000000..6ac68f5adbce8 --- /dev/null +++ b/tests/ui/nll/nested-bodies-in-dead-code.rs @@ -0,0 +1,28 @@ +//@ edition: 2024 + +// Regression test for #140583. We want to borrowck nested +// bodies even if they are in dead code. While not necessary for +// soundness, it is desirable to error in such cases. + +fn main() { + return; + |x: &str| -> &'static str { x }; + //~^ ERROR lifetime may not live long enough + || { + || { + let temp = 1; + let p: &'static u32 = &temp; + //~^ ERROR `temp` does not live long enough + }; + }; + const { + let temp = 1; + let p: &'static u32 = &temp; + //~^ ERROR `temp` does not live long enough + }; + async { + let temp = 1; + let p: &'static u32 = &temp; + //~^ ERROR `temp` does not live long enough + }; +} diff --git a/tests/ui/nll/nested-bodies-in-dead-code.stderr b/tests/ui/nll/nested-bodies-in-dead-code.stderr new file mode 100644 index 0000000000000..da6ccff5777cd --- /dev/null +++ b/tests/ui/nll/nested-bodies-in-dead-code.stderr @@ -0,0 +1,50 @@ +error: lifetime may not live long enough + --> $DIR/nested-bodies-in-dead-code.rs:9:33 + | +LL | |x: &str| -> &'static str { x }; + | - ^ returning this value requires that `'1` must outlive `'static` + | | + | let's call the lifetime of this reference `'1` + +error[E0597]: `temp` does not live long enough + --> $DIR/nested-bodies-in-dead-code.rs:14:35 + | +LL | let temp = 1; + | ---- binding `temp` declared here +LL | let p: &'static u32 = &temp; + | ------------ ^^^^^ borrowed value does not live long enough + | | + | type annotation requires that `temp` is borrowed for `'static` +LL | +LL | }; + | - `temp` dropped here while still borrowed + +error[E0597]: `temp` does not live long enough + --> $DIR/nested-bodies-in-dead-code.rs:20:31 + | +LL | let temp = 1; + | ---- binding `temp` declared here +LL | let p: &'static u32 = &temp; + | ------------ ^^^^^ borrowed value does not live long enough + | | + | type annotation requires that `temp` is borrowed for `'static` +LL | +LL | }; + | - `temp` dropped here while still borrowed + +error[E0597]: `temp` does not live long enough + --> $DIR/nested-bodies-in-dead-code.rs:25:31 + | +LL | let temp = 1; + | ---- binding `temp` declared here +LL | let p: &'static u32 = &temp; + | ------------ ^^^^^ borrowed value does not live long enough + | | + | type annotation requires that `temp` is borrowed for `'static` +LL | +LL | }; + | - `temp` dropped here while still borrowed + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/nll/relate_tys/placeholder-outlives-existential.rs b/tests/ui/nll/relate_tys/placeholder-outlives-existential.rs new file mode 100644 index 0000000000000..3f22e9a313af6 --- /dev/null +++ b/tests/ui/nll/relate_tys/placeholder-outlives-existential.rs @@ -0,0 +1,31 @@ +// Test that we correctly handle some cases of placeholder leaks. +// +//@ compile-flags:-Zno-leak-check + + +struct Co<'a>(&'a ()); +struct Inv<'a>(*mut &'a ()); +struct Contra<'a>(fn(&'a ())); + +// `exists<'e> forall<'p> 'p: 'e` -> ERROR +fn p_outlives_e( + x: for<'e> fn(for<'p> fn(fn(fn(Contra<'e>, Co<'p>)))), +) -> fn(fn(fn(for<'unify> fn(Contra<'unify>, Co<'unify>)))) { + x //~ ERROR mismatched types [E0308] +} + +// `exists<'e> forall<'p> 'e: 'p` -> Ok, 'e: 'static +fn e_outlives_p_static( + x: for<'e> fn(Inv<'e>, for<'p> fn(fn(fn(Contra<'p>, Co<'e>)))), +) -> fn(Inv<'static>, fn(fn(for<'unify> fn(Contra<'unify>, Co<'unify>)))) { + x +} + +// `exists<'e> forall<'p> 'e: 'p` -> Ok, 'e: 'static -> ERROR +fn e_outlives_p_static_err<'not_static>( + x: for<'e> fn(Inv<'e>, for<'p> fn(fn(fn(Contra<'p>, Co<'e>)))), +) -> fn(Inv<'not_static>, fn(fn(for<'unify> fn(Contra<'unify>, Co<'unify>)))) { + x //~ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/nll/relate_tys/placeholder-outlives-existential.stderr b/tests/ui/nll/relate_tys/placeholder-outlives-existential.stderr new file mode 100644 index 0000000000000..80ab5c8d6e9d8 --- /dev/null +++ b/tests/ui/nll/relate_tys/placeholder-outlives-existential.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/placeholder-outlives-existential.rs:14:5 + | +LL | x + | ^ one type is more general than the other + | + = note: expected fn pointer `fn(fn(fn(for<'unify> fn(Contra<'unify>, Co<'unify>))))` + found fn pointer `for<'e> fn(for<'e, 'p> fn(for<'e, 'p> fn(for<'e, 'p> fn(Contra<'e>, Co<'p>))))` + +error: lifetime may not live long enough + --> $DIR/placeholder-outlives-existential.rs:28:5 + | +LL | fn e_outlives_p_static_err<'not_static>( + | ----------- lifetime `'not_static` defined here +... +LL | x + | ^ returning this value requires that `'not_static` must outlive `'static` + | + = note: requirement occurs because of the type `Inv<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Inv<'a>` is invariant over the parameter `'a` + = help: see for more information about variance + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/nll/ty-outlives/impl-trait-captures.stderr b/tests/ui/nll/ty-outlives/impl-trait-captures.stderr index 87bbd50a15c27..6fd41a761e996 100644 --- a/tests/ui/nll/ty-outlives/impl-trait-captures.stderr +++ b/tests/ui/nll/ty-outlives/impl-trait-captures.stderr @@ -4,14 +4,14 @@ error[E0700]: hidden type for `Opaque(DefId(0:11 ~ impl_trait_captures[aeb9]::fo LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> { | -- ------------ opaque type defined here | | - | hidden type `&ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::'_), '_)) T` captures the anonymous lifetime defined here + | hidden type `&ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), LateNamed(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::'_), '_)) T` captures the anonymous lifetime defined here LL | x | ^ | -help: add a `use<...>` bound to explicitly capture `ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::'_), '_))` +help: add a `use<...>` bound to explicitly capture `ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), LateNamed(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::'_), '_))` | -LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + use<'a, ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::'_), '_)), T> { - | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + use<'a, ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), LateNamed(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::'_), '_)), T> { + | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/no-capture-arc.rs b/tests/ui/no-capture-arc.rs index aafb170c7e1a8..9c957a4e01b41 100644 --- a/tests/ui/no-capture-arc.rs +++ b/tests/ui/no-capture-arc.rs @@ -1,5 +1,3 @@ -//@ error-pattern: borrow of moved value - use std::sync::Arc; use std::thread; @@ -11,7 +9,7 @@ fn main() { assert_eq!((*arc_v)[3], 4); }); - assert_eq!((*arc_v)[2], 3); + assert_eq!((*arc_v)[2], 3); //~ ERROR borrow of moved value: `arc_v` println!("{:?}", *arc_v); } diff --git a/tests/ui/no-capture-arc.stderr b/tests/ui/no-capture-arc.stderr index 4a51ddb67a39d..9c1f5c65066fa 100644 --- a/tests/ui/no-capture-arc.stderr +++ b/tests/ui/no-capture-arc.stderr @@ -1,5 +1,5 @@ error[E0382]: borrow of moved value: `arc_v` - --> $DIR/no-capture-arc.rs:14:18 + --> $DIR/no-capture-arc.rs:12:18 | LL | let arc_v = Arc::new(v); | ----- move occurs because `arc_v` has type `Arc>`, which does not implement the `Copy` trait diff --git a/tests/ui/no-link-unknown-crate.rs b/tests/ui/no-link-unknown-crate.rs deleted file mode 100644 index c7da2e41832f6..0000000000000 --- a/tests/ui/no-link-unknown-crate.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[no_link] -extern crate doesnt_exist; //~ ERROR can't find crate - -fn main() {} diff --git a/tests/ui/no_std/no-std-no-start-binary.rs b/tests/ui/no_std/no-std-no-start-binary.rs index 5c8a0e6c0b8d9..6853e2d4228ff 100644 --- a/tests/ui/no_std/no-std-no-start-binary.rs +++ b/tests/ui/no_std/no-std-no-start-binary.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Cpanic=abort --emit link -//@ error-pattern:using `fn main` requires the standard library // Make sure that we don't emit an error message mentioning internal lang items. @@ -11,3 +10,5 @@ fn handler(_info: &core::panic::PanicInfo<'_>) -> ! { } fn main() {} + +//~? ERROR using `fn main` requires the standard library diff --git a/tests/ui/no_std/no-std-unwind-binary.rs b/tests/ui/no_std/no-std-unwind-binary.rs index 74c80d75c3e1f..cb1dc4427ae1c 100644 --- a/tests/ui/no_std/no-std-unwind-binary.rs +++ b/tests/ui/no_std/no-std-unwind-binary.rs @@ -1,4 +1,3 @@ -//@ error-pattern:unwinding panics are not supported without std //@ needs-unwind //@ compile-flags: -Cpanic=unwind @@ -13,3 +12,5 @@ fn handler(_info: &core::panic::PanicInfo<'_>) -> ! { } fn main() {} + +//~? ERROR unwinding panics are not supported without std diff --git a/tests/ui/no_std/simple-runs.rs b/tests/ui/no_std/simple-runs.rs new file mode 100644 index 0000000000000..8931ac7ed11be --- /dev/null +++ b/tests/ui/no_std/simple-runs.rs @@ -0,0 +1,41 @@ +//! Check that `no_std` binaries can link and run without depending on `libstd`. + +//@ run-pass +//@ compile-flags: -Cpanic=abort +//@ ignore-wasm different `main` convention + +#![no_std] +#![no_main] + +use core::ffi::{c_char, c_int}; +use core::panic::PanicInfo; + +// # Linux +// +// Linking `libc` is required by crt1.o, otherwise the linker fails with: +// > /usr/bin/ld: in function `_start': undefined reference to `__libc_start_main' +// +// # Apple +// +// Linking `libSystem` is required, otherwise the linker fails with: +// > ld: dynamic executables or dylibs must link with libSystem.dylib +// +// With the new linker introduced in Xcode 15, the error is instead: +// > Undefined symbols: "dyld_stub_binder", referenced from: +// +// This _can_ be worked around by raising the deployment target with +// MACOSX_DEPLOYMENT_TARGET=13.0, though it's a bit hard to test that while +// still allowing the test suite to support running with older Xcode versions. +#[cfg_attr(all(not(target_vendor = "apple"), unix), link(name = "c"))] +#[cfg_attr(target_vendor = "apple", link(name = "System"))] +extern "C" {} + +#[panic_handler] +fn panic_handler(_info: &PanicInfo<'_>) -> ! { + loop {} +} + +#[no_mangle] +extern "C" fn main(_argc: c_int, _argv: *const *const c_char) -> c_int { + 0 +} diff --git a/tests/ui/noexporttypeexe.rs b/tests/ui/noexporttypeexe.rs index 6b4402a81f00d..35257b20ccdf8 100644 --- a/tests/ui/noexporttypeexe.rs +++ b/tests/ui/noexporttypeexe.rs @@ -9,7 +9,8 @@ fn main() { // not convertible to a path. let x: isize = noexporttypelib::foo(); //~^ ERROR mismatched types - //~| expected type `isize` - //~| found enum `Option` - //~| expected `isize`, found `Option` + //~| NOTE expected type `isize` + //~| NOTE found enum `Option` + //~| NOTE expected `isize`, found `Option` + //~| NOTE expected due to this } diff --git a/tests/ui/non-copyable-void.rs b/tests/ui/non-copyable-void.rs deleted file mode 100644 index 55bad82bc339d..0000000000000 --- a/tests/ui/non-copyable-void.rs +++ /dev/null @@ -1,10 +0,0 @@ -use std::ffi::c_void; - -fn main() { - let x : *const Vec = &vec![1,2,3]; - let y : *const c_void = x as *const c_void; - unsafe { - let _z = (*y).clone(); - //~^ ERROR no method named `clone` found - } -} diff --git a/tests/ui/non-copyable-void.stderr b/tests/ui/non-copyable-void.stderr deleted file mode 100644 index 373557fa01a4b..0000000000000 --- a/tests/ui/non-copyable-void.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0599]: no method named `clone` found for enum `c_void` in the current scope - --> $DIR/non-copyable-void.rs:7:23 - | -LL | let _z = (*y).clone(); - | ^^^^^ method not found in `c_void` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/non_modrs_mods/foors_mod.rs b/tests/ui/non_modrs_mods/foors_mod.rs index b215e5f09e93e..dfaa11bfe13f3 100644 --- a/tests/ui/non_modrs_mods/foors_mod.rs +++ b/tests/ui/non_modrs_mods/foors_mod.rs @@ -1,6 +1,4 @@ -//@ run-pass -// -//@ ignore-test: not a test, used by non_modrs_mods.rs +//@ ignore-auxiliary (used by `./non_modrs_mods.rs`) pub mod inner_modrs_mod; pub mod inner_foors_mod; diff --git a/tests/ui/non_modrs_mods_and_inline_mods/x.rs b/tests/ui/non_modrs_mods_and_inline_mods/x.rs index c4548d39fad88..38ff011d40956 100644 --- a/tests/ui/non_modrs_mods_and_inline_mods/x.rs +++ b/tests/ui/non_modrs_mods_and_inline_mods/x.rs @@ -1,4 +1,4 @@ -//@ ignore-test: not a test +//@ ignore-auxiliary (used by `./non_modrs_mods_and_inline_mods.rs`) pub mod y { pub mod z; diff --git a/tests/ui/non_modrs_mods_and_inline_mods/x/y/z/mod.rs b/tests/ui/non_modrs_mods_and_inline_mods/x/y/z/mod.rs index ec7b7de78d8b7..cac5e274fbed6 100644 --- a/tests/ui/non_modrs_mods_and_inline_mods/x/y/z/mod.rs +++ b/tests/ui/non_modrs_mods_and_inline_mods/x/y/z/mod.rs @@ -1 +1 @@ -//@ ignore-test: not a test +//@ ignore-auxiliary (used by `../../../non_modrs_mods_and_inline_mods.rs`) diff --git a/tests/ui/noncopyable-class.rs b/tests/ui/noncopyable-class.rs deleted file mode 100644 index 11b6eb736e9db..0000000000000 --- a/tests/ui/noncopyable-class.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Test that a class with a non-copyable field can't be -// copied - -#[derive(Debug)] -struct Bar { - x: isize, -} - -impl Drop for Bar { - fn drop(&mut self) {} -} - -fn bar(x:isize) -> Bar { - Bar { - x: x - } -} - -#[derive(Debug)] -struct Foo { - i: isize, - j: Bar, -} - -fn foo(i:isize) -> Foo { - Foo { - i: i, - j: bar(5) - } -} - -fn main() { - let x = foo(10); - let _y = x.clone(); //~ ERROR no method named `clone` found - println!("{:?}", x); -} diff --git a/tests/ui/noncopyable-class.stderr b/tests/ui/noncopyable-class.stderr deleted file mode 100644 index b8f7276c898f8..0000000000000 --- a/tests/ui/noncopyable-class.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0599]: no method named `clone` found for struct `Foo` in the current scope - --> $DIR/noncopyable-class.rs:34:16 - | -LL | struct Foo { - | ---------- method `clone` not found for this struct -... -LL | let _y = x.clone(); - | ^^^^^ method not found in `Foo` - | - = 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: `Clone` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/nonscalar-cast.fixed b/tests/ui/nonscalar-cast.fixed index f6154222ca22f..cb5591dbb9de4 100644 --- a/tests/ui/nonscalar-cast.fixed +++ b/tests/ui/nonscalar-cast.fixed @@ -12,5 +12,5 @@ impl From for isize { } fn main() { - println!("{}", isize::from(Foo { x: 1 })); //~ non-primitive cast: `Foo` as `isize` [E0605] + println!("{}", isize::from(Foo { x: 1 })); //~ ERROR non-primitive cast: `Foo` as `isize` [E0605] } diff --git a/tests/ui/nonscalar-cast.rs b/tests/ui/nonscalar-cast.rs index 71e7c43a1db09..27429b44cd086 100644 --- a/tests/ui/nonscalar-cast.rs +++ b/tests/ui/nonscalar-cast.rs @@ -12,5 +12,5 @@ impl From for isize { } fn main() { - println!("{}", Foo { x: 1 } as isize); //~ non-primitive cast: `Foo` as `isize` [E0605] + println!("{}", Foo { x: 1 } as isize); //~ ERROR non-primitive cast: `Foo` as `isize` [E0605] } diff --git a/tests/ui/numbers-arithmetic/int.rs b/tests/ui/numbers-arithmetic/int.rs deleted file mode 100644 index 42f8e50d6efaf..0000000000000 --- a/tests/ui/numbers-arithmetic/int.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ run-pass - - - - -pub fn main() { let _x: isize = 10; } diff --git a/tests/ui/numbers-arithmetic/isize-base.rs b/tests/ui/numbers-arithmetic/isize-base.rs new file mode 100644 index 0000000000000..412e7ac7a2e92 --- /dev/null +++ b/tests/ui/numbers-arithmetic/isize-base.rs @@ -0,0 +1,25 @@ +//! Tests basic `isize` functionality + +//@ run-pass + +pub fn main() { + // Literal matches assignment type + let a: isize = 42isize; + // Literal cast + let b: isize = 42 as isize; + // Literal type inference from assignment type + let c: isize = 42; + // Assignment type inference from literal (and later comparison) + let d = 42isize; + // Function return value type inference + let e = return_val(); + + assert_eq!(a, b); + assert_eq!(a, c); + assert_eq!(a, d); + assert_eq!(a, e); +} + +fn return_val() -> isize { + 42 +} diff --git a/tests/ui/numbers-arithmetic/saturating-float-casts-impl.rs b/tests/ui/numbers-arithmetic/saturating-float-casts-impl.rs index 4b176ef5caa60..4e578f6132fc2 100644 --- a/tests/ui/numbers-arithmetic/saturating-float-casts-impl.rs +++ b/tests/ui/numbers-arithmetic/saturating-float-casts-impl.rs @@ -1,4 +1,4 @@ -//@ ignore-test (auxiliary, used by other tests) +//@ ignore-auxiliary (used by `./saturating-float-casts.rs` and `./saturating-float-casts-wasm.rs`) // Tests saturating float->int casts. See u128-as-f32.rs for the opposite direction. // diff --git a/tests/ui/numbers-arithmetic/uint.rs b/tests/ui/numbers-arithmetic/uint.rs deleted file mode 100644 index c2087b5a06c63..0000000000000 --- a/tests/ui/numbers-arithmetic/uint.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ run-pass - - - - -pub fn main() { let _x: usize = 10 as usize; } diff --git a/tests/ui/numbers-arithmetic/usize-base.rs b/tests/ui/numbers-arithmetic/usize-base.rs new file mode 100644 index 0000000000000..833fc0497989d --- /dev/null +++ b/tests/ui/numbers-arithmetic/usize-base.rs @@ -0,0 +1,25 @@ +//! Tests basic `usize` functionality + +//@ run-pass + +pub fn main() { + // Literal matches assignment type + let a: usize = 42usize; + // Literal cast + let b: usize = 42 as usize; + // Literal type inference from assignment type + let c: usize = 42; + // Assignment type inference from literal (and later comparison) + let d = 42usize; + // Function return value type inference + let e = return_val(); + + assert_eq!(a, b); + assert_eq!(a, c); + assert_eq!(a, d); + assert_eq!(a, e); +} + +fn return_val() -> usize { + 42 +} diff --git a/tests/ui/numeric/integer-literal-suffix-inference.rs b/tests/ui/numeric/integer-literal-suffix-inference.rs index c320f2bb7b446..775e374e5712e 100644 --- a/tests/ui/numeric/integer-literal-suffix-inference.rs +++ b/tests/ui/numeric/integer-literal-suffix-inference.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + fn main() { // the smallest positive values that need these types @@ -37,184 +39,184 @@ fn main() { id_i8(a8); // ok id_i8(a16); //~^ ERROR mismatched types - //~| expected `i8`, found `i16` + //~| NOTE expected `i8`, found `i16` id_i8(a32); //~^ ERROR mismatched types - //~| expected `i8`, found `i32` + //~| NOTE expected `i8`, found `i32` id_i8(a64); //~^ ERROR mismatched types - //~| expected `i8`, found `i64` + //~| NOTE expected `i8`, found `i64` id_i8(asize); //~^ ERROR mismatched types - //~| expected `i8`, found `isize` + //~| NOTE expected `i8`, found `isize` id_i16(a8); //~^ ERROR mismatched types - //~| expected `i16`, found `i8` + //~| NOTE expected `i16`, found `i8` id_i16(a16); // ok id_i16(a32); //~^ ERROR mismatched types - //~| expected `i16`, found `i32` + //~| NOTE expected `i16`, found `i32` id_i16(a64); //~^ ERROR mismatched types - //~| expected `i16`, found `i64` + //~| NOTE expected `i16`, found `i64` id_i16(asize); //~^ ERROR mismatched types - //~| expected `i16`, found `isize` + //~| NOTE expected `i16`, found `isize` id_i32(a8); //~^ ERROR mismatched types - //~| expected `i32`, found `i8` + //~| NOTE expected `i32`, found `i8` id_i32(a16); //~^ ERROR mismatched types - //~| expected `i32`, found `i16` + //~| NOTE expected `i32`, found `i16` id_i32(a32); // ok id_i32(a64); //~^ ERROR mismatched types - //~| expected `i32`, found `i64` + //~| NOTE expected `i32`, found `i64` id_i32(asize); //~^ ERROR mismatched types - //~| expected `i32`, found `isize` + //~| NOTE expected `i32`, found `isize` id_i64(a8); //~^ ERROR mismatched types - //~| expected `i64`, found `i8` + //~| NOTE expected `i64`, found `i8` id_i64(a16); //~^ ERROR mismatched types - //~| expected `i64`, found `i16` + //~| NOTE expected `i64`, found `i16` id_i64(a32); //~^ ERROR mismatched types - //~| expected `i64`, found `i32` + //~| NOTE expected `i64`, found `i32` id_i64(a64); // ok id_i64(asize); //~^ ERROR mismatched types - //~| expected `i64`, found `isize` + //~| NOTE expected `i64`, found `isize` id_isize(a8); //~^ ERROR mismatched types - //~| expected `isize`, found `i8` + //~| NOTE expected `isize`, found `i8` id_isize(a16); //~^ ERROR mismatched types - //~| expected `isize`, found `i16` + //~| NOTE expected `isize`, found `i16` id_isize(a32); //~^ ERROR mismatched types - //~| expected `isize`, found `i32` + //~| NOTE expected `isize`, found `i32` id_isize(a64); //~^ ERROR mismatched types - //~| expected `isize`, found `i64` + //~| NOTE expected `isize`, found `i64` id_isize(asize); //ok id_i8(c8); // ok id_i8(c16); //~^ ERROR mismatched types - //~| expected `i8`, found `i16` + //~| NOTE expected `i8`, found `i16` id_i8(c32); //~^ ERROR mismatched types - //~| expected `i8`, found `i32` + //~| NOTE expected `i8`, found `i32` id_i8(c64); //~^ ERROR mismatched types - //~| expected `i8`, found `i64` + //~| NOTE expected `i8`, found `i64` id_i16(c8); //~^ ERROR mismatched types - //~| expected `i16`, found `i8` + //~| NOTE expected `i16`, found `i8` id_i16(c16); // ok id_i16(c32); //~^ ERROR mismatched types - //~| expected `i16`, found `i32` + //~| NOTE expected `i16`, found `i32` id_i16(c64); //~^ ERROR mismatched types - //~| expected `i16`, found `i64` + //~| NOTE expected `i16`, found `i64` id_i32(c8); //~^ ERROR mismatched types - //~| expected `i32`, found `i8` + //~| NOTE expected `i32`, found `i8` id_i32(c16); //~^ ERROR mismatched types - //~| expected `i32`, found `i16` + //~| NOTE expected `i32`, found `i16` id_i32(c32); // ok id_i32(c64); //~^ ERROR mismatched types - //~| expected `i32`, found `i64` + //~| NOTE expected `i32`, found `i64` id_i64(a8); //~^ ERROR mismatched types - //~| expected `i64`, found `i8` + //~| NOTE expected `i64`, found `i8` id_i64(a16); //~^ ERROR mismatched types - //~| expected `i64`, found `i16` + //~| NOTE expected `i64`, found `i16` id_i64(a32); //~^ ERROR mismatched types - //~| expected `i64`, found `i32` + //~| NOTE expected `i64`, found `i32` id_i64(a64); // ok id_u8(b8); // ok id_u8(b16); //~^ ERROR mismatched types - //~| expected `u8`, found `u16` + //~| NOTE expected `u8`, found `u16` id_u8(b32); //~^ ERROR mismatched types - //~| expected `u8`, found `u32` + //~| NOTE expected `u8`, found `u32` id_u8(b64); //~^ ERROR mismatched types - //~| expected `u8`, found `u64` + //~| NOTE expected `u8`, found `u64` id_u8(bsize); //~^ ERROR mismatched types - //~| expected `u8`, found `usize` + //~| NOTE expected `u8`, found `usize` id_u16(b8); //~^ ERROR mismatched types - //~| expected `u16`, found `u8` + //~| NOTE expected `u16`, found `u8` id_u16(b16); // ok id_u16(b32); //~^ ERROR mismatched types - //~| expected `u16`, found `u32` + //~| NOTE expected `u16`, found `u32` id_u16(b64); //~^ ERROR mismatched types - //~| expected `u16`, found `u64` + //~| NOTE expected `u16`, found `u64` id_u16(bsize); //~^ ERROR mismatched types - //~| expected `u16`, found `usize` + //~| NOTE expected `u16`, found `usize` id_u32(b8); //~^ ERROR mismatched types - //~| expected `u32`, found `u8` + //~| NOTE expected `u32`, found `u8` id_u32(b16); //~^ ERROR mismatched types - //~| expected `u32`, found `u16` + //~| NOTE expected `u32`, found `u16` id_u32(b32); // ok id_u32(b64); //~^ ERROR mismatched types - //~| expected `u32`, found `u64` + //~| NOTE expected `u32`, found `u64` id_u32(bsize); //~^ ERROR mismatched types - //~| expected `u32`, found `usize` + //~| NOTE expected `u32`, found `usize` id_u64(b8); //~^ ERROR mismatched types - //~| expected `u64`, found `u8` + //~| NOTE expected `u64`, found `u8` id_u64(b16); //~^ ERROR mismatched types - //~| expected `u64`, found `u16` + //~| NOTE expected `u64`, found `u16` id_u64(b32); //~^ ERROR mismatched types - //~| expected `u64`, found `u32` + //~| NOTE expected `u64`, found `u32` id_u64(b64); // ok id_u64(bsize); //~^ ERROR mismatched types - //~| expected `u64`, found `usize` + //~| NOTE expected `u64`, found `usize` id_usize(b8); //~^ ERROR mismatched types - //~| expected `usize`, found `u8` + //~| NOTE expected `usize`, found `u8` id_usize(b16); //~^ ERROR mismatched types - //~| expected `usize`, found `u16` + //~| NOTE expected `usize`, found `u16` id_usize(b32); //~^ ERROR mismatched types - //~| expected `usize`, found `u32` + //~| NOTE expected `usize`, found `u32` id_usize(b64); //~^ ERROR mismatched types - //~| expected `usize`, found `u64` + //~| NOTE expected `usize`, found `u64` id_usize(bsize); //ok } diff --git a/tests/ui/numeric/integer-literal-suffix-inference.stderr b/tests/ui/numeric/integer-literal-suffix-inference.stderr index 5045f584c8977..30232e4d3754f 100644 --- a/tests/ui/numeric/integer-literal-suffix-inference.stderr +++ b/tests/ui/numeric/integer-literal-suffix-inference.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:38:11 + --> $DIR/integer-literal-suffix-inference.rs:40:11 | LL | id_i8(a16); | ----- ^^^ expected `i8`, found `i16` @@ -7,7 +7,7 @@ LL | id_i8(a16); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:15:8 + --> $DIR/integer-literal-suffix-inference.rs:17:8 | LL | fn id_i8(n: i8) -> i8 { n } | ^^^^^ ----- @@ -17,7 +17,7 @@ LL | id_i8(a16.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:41:11 + --> $DIR/integer-literal-suffix-inference.rs:43:11 | LL | id_i8(a32); | ----- ^^^ expected `i8`, found `i32` @@ -25,7 +25,7 @@ LL | id_i8(a32); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:15:8 + --> $DIR/integer-literal-suffix-inference.rs:17:8 | LL | fn id_i8(n: i8) -> i8 { n } | ^^^^^ ----- @@ -35,7 +35,7 @@ LL | id_i8(a32.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:44:11 + --> $DIR/integer-literal-suffix-inference.rs:46:11 | LL | id_i8(a64); | ----- ^^^ expected `i8`, found `i64` @@ -43,7 +43,7 @@ LL | id_i8(a64); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:15:8 + --> $DIR/integer-literal-suffix-inference.rs:17:8 | LL | fn id_i8(n: i8) -> i8 { n } | ^^^^^ ----- @@ -53,7 +53,7 @@ LL | id_i8(a64.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:47:11 + --> $DIR/integer-literal-suffix-inference.rs:49:11 | LL | id_i8(asize); | ----- ^^^^^ expected `i8`, found `isize` @@ -61,7 +61,7 @@ LL | id_i8(asize); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:15:8 + --> $DIR/integer-literal-suffix-inference.rs:17:8 | LL | fn id_i8(n: i8) -> i8 { n } | ^^^^^ ----- @@ -71,7 +71,7 @@ LL | id_i8(asize.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:51:12 + --> $DIR/integer-literal-suffix-inference.rs:53:12 | LL | id_i16(a8); | ------ ^^ expected `i16`, found `i8` @@ -79,7 +79,7 @@ LL | id_i16(a8); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:16:8 + --> $DIR/integer-literal-suffix-inference.rs:18:8 | LL | fn id_i16(n: i16) -> i16 { n } | ^^^^^^ ------ @@ -89,7 +89,7 @@ LL | id_i16(a8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:55:12 + --> $DIR/integer-literal-suffix-inference.rs:57:12 | LL | id_i16(a32); | ------ ^^^ expected `i16`, found `i32` @@ -97,7 +97,7 @@ LL | id_i16(a32); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:16:8 + --> $DIR/integer-literal-suffix-inference.rs:18:8 | LL | fn id_i16(n: i16) -> i16 { n } | ^^^^^^ ------ @@ -107,7 +107,7 @@ LL | id_i16(a32.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:58:12 + --> $DIR/integer-literal-suffix-inference.rs:60:12 | LL | id_i16(a64); | ------ ^^^ expected `i16`, found `i64` @@ -115,7 +115,7 @@ LL | id_i16(a64); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:16:8 + --> $DIR/integer-literal-suffix-inference.rs:18:8 | LL | fn id_i16(n: i16) -> i16 { n } | ^^^^^^ ------ @@ -125,7 +125,7 @@ LL | id_i16(a64.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:61:12 + --> $DIR/integer-literal-suffix-inference.rs:63:12 | LL | id_i16(asize); | ------ ^^^^^ expected `i16`, found `isize` @@ -133,7 +133,7 @@ LL | id_i16(asize); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:16:8 + --> $DIR/integer-literal-suffix-inference.rs:18:8 | LL | fn id_i16(n: i16) -> i16 { n } | ^^^^^^ ------ @@ -143,7 +143,7 @@ LL | id_i16(asize.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:65:12 + --> $DIR/integer-literal-suffix-inference.rs:67:12 | LL | id_i32(a8); | ------ ^^ expected `i32`, found `i8` @@ -151,7 +151,7 @@ LL | id_i32(a8); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:17:8 + --> $DIR/integer-literal-suffix-inference.rs:19:8 | LL | fn id_i32(n: i32) -> i32 { n } | ^^^^^^ ------ @@ -161,7 +161,7 @@ LL | id_i32(a8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:68:12 + --> $DIR/integer-literal-suffix-inference.rs:70:12 | LL | id_i32(a16); | ------ ^^^ expected `i32`, found `i16` @@ -169,7 +169,7 @@ LL | id_i32(a16); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:17:8 + --> $DIR/integer-literal-suffix-inference.rs:19:8 | LL | fn id_i32(n: i32) -> i32 { n } | ^^^^^^ ------ @@ -179,7 +179,7 @@ LL | id_i32(a16.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:72:12 + --> $DIR/integer-literal-suffix-inference.rs:74:12 | LL | id_i32(a64); | ------ ^^^ expected `i32`, found `i64` @@ -187,7 +187,7 @@ LL | id_i32(a64); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:17:8 + --> $DIR/integer-literal-suffix-inference.rs:19:8 | LL | fn id_i32(n: i32) -> i32 { n } | ^^^^^^ ------ @@ -197,7 +197,7 @@ LL | id_i32(a64.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:75:12 + --> $DIR/integer-literal-suffix-inference.rs:77:12 | LL | id_i32(asize); | ------ ^^^^^ expected `i32`, found `isize` @@ -205,7 +205,7 @@ LL | id_i32(asize); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:17:8 + --> $DIR/integer-literal-suffix-inference.rs:19:8 | LL | fn id_i32(n: i32) -> i32 { n } | ^^^^^^ ------ @@ -215,7 +215,7 @@ LL | id_i32(asize.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:79:12 + --> $DIR/integer-literal-suffix-inference.rs:81:12 | LL | id_i64(a8); | ------ ^^ expected `i64`, found `i8` @@ -223,7 +223,7 @@ LL | id_i64(a8); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:18:8 + --> $DIR/integer-literal-suffix-inference.rs:20:8 | LL | fn id_i64(n: i64) -> i64 { n } | ^^^^^^ ------ @@ -233,7 +233,7 @@ LL | id_i64(a8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:82:12 + --> $DIR/integer-literal-suffix-inference.rs:84:12 | LL | id_i64(a16); | ------ ^^^ expected `i64`, found `i16` @@ -241,7 +241,7 @@ LL | id_i64(a16); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:18:8 + --> $DIR/integer-literal-suffix-inference.rs:20:8 | LL | fn id_i64(n: i64) -> i64 { n } | ^^^^^^ ------ @@ -251,7 +251,7 @@ LL | id_i64(a16.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:85:12 + --> $DIR/integer-literal-suffix-inference.rs:87:12 | LL | id_i64(a32); | ------ ^^^ expected `i64`, found `i32` @@ -259,7 +259,7 @@ LL | id_i64(a32); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:18:8 + --> $DIR/integer-literal-suffix-inference.rs:20:8 | LL | fn id_i64(n: i64) -> i64 { n } | ^^^^^^ ------ @@ -269,7 +269,7 @@ LL | id_i64(a32.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:89:12 + --> $DIR/integer-literal-suffix-inference.rs:91:12 | LL | id_i64(asize); | ------ ^^^^^ expected `i64`, found `isize` @@ -277,7 +277,7 @@ LL | id_i64(asize); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:18:8 + --> $DIR/integer-literal-suffix-inference.rs:20:8 | LL | fn id_i64(n: i64) -> i64 { n } | ^^^^^^ ------ @@ -287,7 +287,7 @@ LL | id_i64(asize.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:93:14 + --> $DIR/integer-literal-suffix-inference.rs:95:14 | LL | id_isize(a8); | -------- ^^ expected `isize`, found `i8` @@ -295,7 +295,7 @@ LL | id_isize(a8); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:19:8 + --> $DIR/integer-literal-suffix-inference.rs:21:8 | LL | fn id_isize(n: isize) -> isize { n } | ^^^^^^^^ -------- @@ -305,7 +305,7 @@ LL | id_isize(a8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:96:14 + --> $DIR/integer-literal-suffix-inference.rs:98:14 | LL | id_isize(a16); | -------- ^^^ expected `isize`, found `i16` @@ -313,7 +313,7 @@ LL | id_isize(a16); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:19:8 + --> $DIR/integer-literal-suffix-inference.rs:21:8 | LL | fn id_isize(n: isize) -> isize { n } | ^^^^^^^^ -------- @@ -323,7 +323,7 @@ LL | id_isize(a16.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:99:14 + --> $DIR/integer-literal-suffix-inference.rs:101:14 | LL | id_isize(a32); | -------- ^^^ expected `isize`, found `i32` @@ -331,7 +331,7 @@ LL | id_isize(a32); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:19:8 + --> $DIR/integer-literal-suffix-inference.rs:21:8 | LL | fn id_isize(n: isize) -> isize { n } | ^^^^^^^^ -------- @@ -341,7 +341,7 @@ LL | id_isize(a32.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:102:14 + --> $DIR/integer-literal-suffix-inference.rs:104:14 | LL | id_isize(a64); | -------- ^^^ expected `isize`, found `i64` @@ -349,7 +349,7 @@ LL | id_isize(a64); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:19:8 + --> $DIR/integer-literal-suffix-inference.rs:21:8 | LL | fn id_isize(n: isize) -> isize { n } | ^^^^^^^^ -------- @@ -359,7 +359,7 @@ LL | id_isize(a64.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:108:11 + --> $DIR/integer-literal-suffix-inference.rs:110:11 | LL | id_i8(c16); | ----- ^^^ expected `i8`, found `i16` @@ -367,7 +367,7 @@ LL | id_i8(c16); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:15:8 + --> $DIR/integer-literal-suffix-inference.rs:17:8 | LL | fn id_i8(n: i8) -> i8 { n } | ^^^^^ ----- @@ -377,7 +377,7 @@ LL | id_i8(c16.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:111:11 + --> $DIR/integer-literal-suffix-inference.rs:113:11 | LL | id_i8(c32); | ----- ^^^ expected `i8`, found `i32` @@ -385,7 +385,7 @@ LL | id_i8(c32); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:15:8 + --> $DIR/integer-literal-suffix-inference.rs:17:8 | LL | fn id_i8(n: i8) -> i8 { n } | ^^^^^ ----- @@ -395,7 +395,7 @@ LL | id_i8(c32.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:114:11 + --> $DIR/integer-literal-suffix-inference.rs:116:11 | LL | id_i8(c64); | ----- ^^^ expected `i8`, found `i64` @@ -403,7 +403,7 @@ LL | id_i8(c64); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:15:8 + --> $DIR/integer-literal-suffix-inference.rs:17:8 | LL | fn id_i8(n: i8) -> i8 { n } | ^^^^^ ----- @@ -413,7 +413,7 @@ LL | id_i8(c64.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:118:12 + --> $DIR/integer-literal-suffix-inference.rs:120:12 | LL | id_i16(c8); | ------ ^^ expected `i16`, found `i8` @@ -421,7 +421,7 @@ LL | id_i16(c8); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:16:8 + --> $DIR/integer-literal-suffix-inference.rs:18:8 | LL | fn id_i16(n: i16) -> i16 { n } | ^^^^^^ ------ @@ -431,7 +431,7 @@ LL | id_i16(c8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:122:12 + --> $DIR/integer-literal-suffix-inference.rs:124:12 | LL | id_i16(c32); | ------ ^^^ expected `i16`, found `i32` @@ -439,7 +439,7 @@ LL | id_i16(c32); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:16:8 + --> $DIR/integer-literal-suffix-inference.rs:18:8 | LL | fn id_i16(n: i16) -> i16 { n } | ^^^^^^ ------ @@ -449,7 +449,7 @@ LL | id_i16(c32.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:125:12 + --> $DIR/integer-literal-suffix-inference.rs:127:12 | LL | id_i16(c64); | ------ ^^^ expected `i16`, found `i64` @@ -457,7 +457,7 @@ LL | id_i16(c64); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:16:8 + --> $DIR/integer-literal-suffix-inference.rs:18:8 | LL | fn id_i16(n: i16) -> i16 { n } | ^^^^^^ ------ @@ -467,7 +467,7 @@ LL | id_i16(c64.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:129:12 + --> $DIR/integer-literal-suffix-inference.rs:131:12 | LL | id_i32(c8); | ------ ^^ expected `i32`, found `i8` @@ -475,7 +475,7 @@ LL | id_i32(c8); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:17:8 + --> $DIR/integer-literal-suffix-inference.rs:19:8 | LL | fn id_i32(n: i32) -> i32 { n } | ^^^^^^ ------ @@ -485,7 +485,7 @@ LL | id_i32(c8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:132:12 + --> $DIR/integer-literal-suffix-inference.rs:134:12 | LL | id_i32(c16); | ------ ^^^ expected `i32`, found `i16` @@ -493,7 +493,7 @@ LL | id_i32(c16); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:17:8 + --> $DIR/integer-literal-suffix-inference.rs:19:8 | LL | fn id_i32(n: i32) -> i32 { n } | ^^^^^^ ------ @@ -503,7 +503,7 @@ LL | id_i32(c16.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:136:12 + --> $DIR/integer-literal-suffix-inference.rs:138:12 | LL | id_i32(c64); | ------ ^^^ expected `i32`, found `i64` @@ -511,7 +511,7 @@ LL | id_i32(c64); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:17:8 + --> $DIR/integer-literal-suffix-inference.rs:19:8 | LL | fn id_i32(n: i32) -> i32 { n } | ^^^^^^ ------ @@ -521,7 +521,7 @@ LL | id_i32(c64.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:140:12 + --> $DIR/integer-literal-suffix-inference.rs:142:12 | LL | id_i64(a8); | ------ ^^ expected `i64`, found `i8` @@ -529,7 +529,7 @@ LL | id_i64(a8); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:18:8 + --> $DIR/integer-literal-suffix-inference.rs:20:8 | LL | fn id_i64(n: i64) -> i64 { n } | ^^^^^^ ------ @@ -539,7 +539,7 @@ LL | id_i64(a8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:143:12 + --> $DIR/integer-literal-suffix-inference.rs:145:12 | LL | id_i64(a16); | ------ ^^^ expected `i64`, found `i16` @@ -547,7 +547,7 @@ LL | id_i64(a16); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:18:8 + --> $DIR/integer-literal-suffix-inference.rs:20:8 | LL | fn id_i64(n: i64) -> i64 { n } | ^^^^^^ ------ @@ -557,7 +557,7 @@ LL | id_i64(a16.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:146:12 + --> $DIR/integer-literal-suffix-inference.rs:148:12 | LL | id_i64(a32); | ------ ^^^ expected `i64`, found `i32` @@ -565,7 +565,7 @@ LL | id_i64(a32); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:18:8 + --> $DIR/integer-literal-suffix-inference.rs:20:8 | LL | fn id_i64(n: i64) -> i64 { n } | ^^^^^^ ------ @@ -575,7 +575,7 @@ LL | id_i64(a32.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:152:11 + --> $DIR/integer-literal-suffix-inference.rs:154:11 | LL | id_u8(b16); | ----- ^^^ expected `u8`, found `u16` @@ -583,7 +583,7 @@ LL | id_u8(b16); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:27:8 + --> $DIR/integer-literal-suffix-inference.rs:29:8 | LL | fn id_u8(n: u8) -> u8 { n } | ^^^^^ ----- @@ -593,7 +593,7 @@ LL | id_u8(b16.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:155:11 + --> $DIR/integer-literal-suffix-inference.rs:157:11 | LL | id_u8(b32); | ----- ^^^ expected `u8`, found `u32` @@ -601,7 +601,7 @@ LL | id_u8(b32); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:27:8 + --> $DIR/integer-literal-suffix-inference.rs:29:8 | LL | fn id_u8(n: u8) -> u8 { n } | ^^^^^ ----- @@ -611,7 +611,7 @@ LL | id_u8(b32.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:158:11 + --> $DIR/integer-literal-suffix-inference.rs:160:11 | LL | id_u8(b64); | ----- ^^^ expected `u8`, found `u64` @@ -619,7 +619,7 @@ LL | id_u8(b64); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:27:8 + --> $DIR/integer-literal-suffix-inference.rs:29:8 | LL | fn id_u8(n: u8) -> u8 { n } | ^^^^^ ----- @@ -629,7 +629,7 @@ LL | id_u8(b64.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:161:11 + --> $DIR/integer-literal-suffix-inference.rs:163:11 | LL | id_u8(bsize); | ----- ^^^^^ expected `u8`, found `usize` @@ -637,7 +637,7 @@ LL | id_u8(bsize); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:27:8 + --> $DIR/integer-literal-suffix-inference.rs:29:8 | LL | fn id_u8(n: u8) -> u8 { n } | ^^^^^ ----- @@ -647,7 +647,7 @@ LL | id_u8(bsize.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:165:12 + --> $DIR/integer-literal-suffix-inference.rs:167:12 | LL | id_u16(b8); | ------ ^^ expected `u16`, found `u8` @@ -655,7 +655,7 @@ LL | id_u16(b8); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:28:8 + --> $DIR/integer-literal-suffix-inference.rs:30:8 | LL | fn id_u16(n: u16) -> u16 { n } | ^^^^^^ ------ @@ -665,7 +665,7 @@ LL | id_u16(b8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:169:12 + --> $DIR/integer-literal-suffix-inference.rs:171:12 | LL | id_u16(b32); | ------ ^^^ expected `u16`, found `u32` @@ -673,7 +673,7 @@ LL | id_u16(b32); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:28:8 + --> $DIR/integer-literal-suffix-inference.rs:30:8 | LL | fn id_u16(n: u16) -> u16 { n } | ^^^^^^ ------ @@ -683,7 +683,7 @@ LL | id_u16(b32.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:172:12 + --> $DIR/integer-literal-suffix-inference.rs:174:12 | LL | id_u16(b64); | ------ ^^^ expected `u16`, found `u64` @@ -691,7 +691,7 @@ LL | id_u16(b64); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:28:8 + --> $DIR/integer-literal-suffix-inference.rs:30:8 | LL | fn id_u16(n: u16) -> u16 { n } | ^^^^^^ ------ @@ -701,7 +701,7 @@ LL | id_u16(b64.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:175:12 + --> $DIR/integer-literal-suffix-inference.rs:177:12 | LL | id_u16(bsize); | ------ ^^^^^ expected `u16`, found `usize` @@ -709,7 +709,7 @@ LL | id_u16(bsize); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:28:8 + --> $DIR/integer-literal-suffix-inference.rs:30:8 | LL | fn id_u16(n: u16) -> u16 { n } | ^^^^^^ ------ @@ -719,7 +719,7 @@ LL | id_u16(bsize.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:179:12 + --> $DIR/integer-literal-suffix-inference.rs:181:12 | LL | id_u32(b8); | ------ ^^ expected `u32`, found `u8` @@ -727,7 +727,7 @@ LL | id_u32(b8); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:29:8 + --> $DIR/integer-literal-suffix-inference.rs:31:8 | LL | fn id_u32(n: u32) -> u32 { n } | ^^^^^^ ------ @@ -737,7 +737,7 @@ LL | id_u32(b8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:182:12 + --> $DIR/integer-literal-suffix-inference.rs:184:12 | LL | id_u32(b16); | ------ ^^^ expected `u32`, found `u16` @@ -745,7 +745,7 @@ LL | id_u32(b16); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:29:8 + --> $DIR/integer-literal-suffix-inference.rs:31:8 | LL | fn id_u32(n: u32) -> u32 { n } | ^^^^^^ ------ @@ -755,7 +755,7 @@ LL | id_u32(b16.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:186:12 + --> $DIR/integer-literal-suffix-inference.rs:188:12 | LL | id_u32(b64); | ------ ^^^ expected `u32`, found `u64` @@ -763,7 +763,7 @@ LL | id_u32(b64); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:29:8 + --> $DIR/integer-literal-suffix-inference.rs:31:8 | LL | fn id_u32(n: u32) -> u32 { n } | ^^^^^^ ------ @@ -773,7 +773,7 @@ LL | id_u32(b64.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:189:12 + --> $DIR/integer-literal-suffix-inference.rs:191:12 | LL | id_u32(bsize); | ------ ^^^^^ expected `u32`, found `usize` @@ -781,7 +781,7 @@ LL | id_u32(bsize); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:29:8 + --> $DIR/integer-literal-suffix-inference.rs:31:8 | LL | fn id_u32(n: u32) -> u32 { n } | ^^^^^^ ------ @@ -791,7 +791,7 @@ LL | id_u32(bsize.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:193:12 + --> $DIR/integer-literal-suffix-inference.rs:195:12 | LL | id_u64(b8); | ------ ^^ expected `u64`, found `u8` @@ -799,7 +799,7 @@ LL | id_u64(b8); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:30:8 + --> $DIR/integer-literal-suffix-inference.rs:32:8 | LL | fn id_u64(n: u64) -> u64 { n } | ^^^^^^ ------ @@ -809,7 +809,7 @@ LL | id_u64(b8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:196:12 + --> $DIR/integer-literal-suffix-inference.rs:198:12 | LL | id_u64(b16); | ------ ^^^ expected `u64`, found `u16` @@ -817,7 +817,7 @@ LL | id_u64(b16); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:30:8 + --> $DIR/integer-literal-suffix-inference.rs:32:8 | LL | fn id_u64(n: u64) -> u64 { n } | ^^^^^^ ------ @@ -827,7 +827,7 @@ LL | id_u64(b16.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:199:12 + --> $DIR/integer-literal-suffix-inference.rs:201:12 | LL | id_u64(b32); | ------ ^^^ expected `u64`, found `u32` @@ -835,7 +835,7 @@ LL | id_u64(b32); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:30:8 + --> $DIR/integer-literal-suffix-inference.rs:32:8 | LL | fn id_u64(n: u64) -> u64 { n } | ^^^^^^ ------ @@ -845,7 +845,7 @@ LL | id_u64(b32.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:203:12 + --> $DIR/integer-literal-suffix-inference.rs:205:12 | LL | id_u64(bsize); | ------ ^^^^^ expected `u64`, found `usize` @@ -853,7 +853,7 @@ LL | id_u64(bsize); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:30:8 + --> $DIR/integer-literal-suffix-inference.rs:32:8 | LL | fn id_u64(n: u64) -> u64 { n } | ^^^^^^ ------ @@ -863,7 +863,7 @@ LL | id_u64(bsize.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:207:14 + --> $DIR/integer-literal-suffix-inference.rs:209:14 | LL | id_usize(b8); | -------- ^^ expected `usize`, found `u8` @@ -871,7 +871,7 @@ LL | id_usize(b8); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:31:8 + --> $DIR/integer-literal-suffix-inference.rs:33:8 | LL | fn id_usize(n: usize) -> usize { n } | ^^^^^^^^ -------- @@ -881,7 +881,7 @@ LL | id_usize(b8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:210:14 + --> $DIR/integer-literal-suffix-inference.rs:212:14 | LL | id_usize(b16); | -------- ^^^ expected `usize`, found `u16` @@ -889,7 +889,7 @@ LL | id_usize(b16); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:31:8 + --> $DIR/integer-literal-suffix-inference.rs:33:8 | LL | fn id_usize(n: usize) -> usize { n } | ^^^^^^^^ -------- @@ -899,7 +899,7 @@ LL | id_usize(b16.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:213:14 + --> $DIR/integer-literal-suffix-inference.rs:215:14 | LL | id_usize(b32); | -------- ^^^ expected `usize`, found `u32` @@ -907,7 +907,7 @@ LL | id_usize(b32); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:31:8 + --> $DIR/integer-literal-suffix-inference.rs:33:8 | LL | fn id_usize(n: usize) -> usize { n } | ^^^^^^^^ -------- @@ -917,7 +917,7 @@ LL | id_usize(b32.try_into().unwrap()); | ++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/integer-literal-suffix-inference.rs:216:14 + --> $DIR/integer-literal-suffix-inference.rs:218:14 | LL | id_usize(b64); | -------- ^^^ expected `usize`, found `u64` @@ -925,7 +925,7 @@ LL | id_usize(b64); | arguments to this function are incorrect | note: function defined here - --> $DIR/integer-literal-suffix-inference.rs:31:8 + --> $DIR/integer-literal-suffix-inference.rs:33:8 | LL | fn id_usize(n: usize) -> usize { n } | ^^^^^^^^ -------- diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.fixed b/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.fixed index e3b613cc3f641..9f891afc313a4 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.fixed +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.fixed @@ -12,17 +12,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE fn main() { foo::(42_i32); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.rs b/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.rs index 3b384e763107f..a735fade86ed5 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.rs +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.rs @@ -12,17 +12,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE fn main() { foo::(42_usize); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.stderr index 6c6b8b51c226d..b3450b41d38cb 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/numeric-suffix-i32.rs:28:16 + --> $DIR/numeric-suffix-i32.rs:17:16 | LL | foo::(42_usize); | ---------- ^^^^^^^^ expected `i32`, found `usize` @@ -18,7 +18,7 @@ LL + foo::(42_i32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i32.rs:32:16 + --> $DIR/numeric-suffix-i32.rs:21:16 | LL | foo::(42_u64); | ---------- ^^^^^^ expected `i32`, found `u64` @@ -37,7 +37,7 @@ LL + foo::(42_i32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i32.rs:36:16 + --> $DIR/numeric-suffix-i32.rs:25:16 | LL | foo::(42_u32); | ---------- ^^^^^^ expected `i32`, found `u32` @@ -56,7 +56,7 @@ LL + foo::(42_i32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i32.rs:40:16 + --> $DIR/numeric-suffix-i32.rs:29:16 | LL | foo::(42_u16); | ---------- ^^^^^^ expected `i32`, found `u16` @@ -75,7 +75,7 @@ LL + foo::(42_i32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i32.rs:44:16 + --> $DIR/numeric-suffix-i32.rs:33:16 | LL | foo::(42_u8); | ---------- ^^^^^ expected `i32`, found `u8` @@ -94,7 +94,7 @@ LL + foo::(42_i32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i32.rs:48:16 + --> $DIR/numeric-suffix-i32.rs:37:16 | LL | foo::(42_isize); | ---------- ^^^^^^^^ expected `i32`, found `isize` @@ -113,7 +113,7 @@ LL + foo::(42_i32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i32.rs:52:16 + --> $DIR/numeric-suffix-i32.rs:41:16 | LL | foo::(42_i64); | ---------- ^^^^^^ expected `i32`, found `i64` @@ -132,7 +132,7 @@ LL + foo::(42_i32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i32.rs:57:16 + --> $DIR/numeric-suffix-i32.rs:46:16 | LL | foo::(42_i16); | ---------- ^^^^^^ expected `i32`, found `i16` @@ -151,7 +151,7 @@ LL + foo::(42_i32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i32.rs:61:16 + --> $DIR/numeric-suffix-i32.rs:50:16 | LL | foo::(42_i8); | ---------- ^^^^^ expected `i32`, found `i8` @@ -170,7 +170,7 @@ LL + foo::(42_i32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i32.rs:65:16 + --> $DIR/numeric-suffix-i32.rs:54:16 | LL | foo::(42.0_f64); | ---------- ^^^^^^^^ expected `i32`, found `f64` @@ -189,7 +189,7 @@ LL + foo::(42i32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i32.rs:69:16 + --> $DIR/numeric-suffix-i32.rs:58:16 | LL | foo::(42.0_f32); | ---------- ^^^^^^^^ expected `i32`, found `f32` diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.fixed b/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.fixed index 0fcda6c1f8067..6d1c585a56396 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.fixed +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.fixed @@ -12,17 +12,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE fn main() { foo::(42_i64); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.rs b/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.rs index 9c912bc38dad9..fd4862be3873f 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.rs +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.rs @@ -12,17 +12,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE fn main() { foo::(42_usize); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.stderr index 7c26dd7be1c64..63326f49a8f04 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/numeric-suffix-i64.rs:28:16 + --> $DIR/numeric-suffix-i64.rs:17:16 | LL | foo::(42_usize); | ---------- ^^^^^^^^ expected `i64`, found `usize` @@ -18,7 +18,7 @@ LL + foo::(42_i64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i64.rs:32:16 + --> $DIR/numeric-suffix-i64.rs:21:16 | LL | foo::(42_u64); | ---------- ^^^^^^ expected `i64`, found `u64` @@ -37,7 +37,7 @@ LL + foo::(42_i64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i64.rs:36:16 + --> $DIR/numeric-suffix-i64.rs:25:16 | LL | foo::(42_u32); | ---------- ^^^^^^ expected `i64`, found `u32` @@ -56,7 +56,7 @@ LL + foo::(42_i64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i64.rs:40:16 + --> $DIR/numeric-suffix-i64.rs:29:16 | LL | foo::(42_u16); | ---------- ^^^^^^ expected `i64`, found `u16` @@ -75,7 +75,7 @@ LL + foo::(42_i64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i64.rs:44:16 + --> $DIR/numeric-suffix-i64.rs:33:16 | LL | foo::(42_u8); | ---------- ^^^^^ expected `i64`, found `u8` @@ -94,7 +94,7 @@ LL + foo::(42_i64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i64.rs:48:16 + --> $DIR/numeric-suffix-i64.rs:37:16 | LL | foo::(42_isize); | ---------- ^^^^^^^^ expected `i64`, found `isize` @@ -113,7 +113,7 @@ LL + foo::(42_i64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i64.rs:53:16 + --> $DIR/numeric-suffix-i64.rs:42:16 | LL | foo::(42_i32); | ---------- ^^^^^^ expected `i64`, found `i32` @@ -132,7 +132,7 @@ LL + foo::(42_i64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i64.rs:57:16 + --> $DIR/numeric-suffix-i64.rs:46:16 | LL | foo::(42_i16); | ---------- ^^^^^^ expected `i64`, found `i16` @@ -151,7 +151,7 @@ LL + foo::(42_i64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i64.rs:61:16 + --> $DIR/numeric-suffix-i64.rs:50:16 | LL | foo::(42_i8); | ---------- ^^^^^ expected `i64`, found `i8` @@ -170,7 +170,7 @@ LL + foo::(42_i64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i64.rs:65:16 + --> $DIR/numeric-suffix-i64.rs:54:16 | LL | foo::(42.0_f64); | ---------- ^^^^^^^^ expected `i64`, found `f64` @@ -189,7 +189,7 @@ LL + foo::(42i64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-i64.rs:69:16 + --> $DIR/numeric-suffix-i64.rs:58:16 | LL | foo::(42.0_f32); | ---------- ^^^^^^^^ expected `i64`, found `f32` diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.fixed b/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.fixed index 23e7cf780e9a3..f24fce3f1d67b 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.fixed +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.fixed @@ -12,17 +12,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE fn main() { foo::(42_isize); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.rs b/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.rs index 5d6fd4d932ac4..fdf86895ff23b 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.rs +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.rs @@ -12,17 +12,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE fn main() { foo::(42_usize); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.stderr index 8365350f2bfec..99561b19021ad 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/numeric-suffix-isize.rs:28:18 + --> $DIR/numeric-suffix-isize.rs:17:18 | LL | foo::(42_usize); | ------------ ^^^^^^^^ expected `isize`, found `usize` @@ -18,7 +18,7 @@ LL + foo::(42_isize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-isize.rs:32:18 + --> $DIR/numeric-suffix-isize.rs:21:18 | LL | foo::(42_u64); | ------------ ^^^^^^ expected `isize`, found `u64` @@ -37,7 +37,7 @@ LL + foo::(42_isize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-isize.rs:36:18 + --> $DIR/numeric-suffix-isize.rs:25:18 | LL | foo::(42_u32); | ------------ ^^^^^^ expected `isize`, found `u32` @@ -56,7 +56,7 @@ LL + foo::(42_isize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-isize.rs:40:18 + --> $DIR/numeric-suffix-isize.rs:29:18 | LL | foo::(42_u16); | ------------ ^^^^^^ expected `isize`, found `u16` @@ -75,7 +75,7 @@ LL + foo::(42_isize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-isize.rs:44:18 + --> $DIR/numeric-suffix-isize.rs:33:18 | LL | foo::(42_u8); | ------------ ^^^^^ expected `isize`, found `u8` @@ -94,7 +94,7 @@ LL + foo::(42_isize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-isize.rs:49:18 + --> $DIR/numeric-suffix-isize.rs:38:18 | LL | foo::(42_i64); | ------------ ^^^^^^ expected `isize`, found `i64` @@ -113,7 +113,7 @@ LL + foo::(42_isize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-isize.rs:53:18 + --> $DIR/numeric-suffix-isize.rs:42:18 | LL | foo::(42_i32); | ------------ ^^^^^^ expected `isize`, found `i32` @@ -132,7 +132,7 @@ LL + foo::(42_isize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-isize.rs:57:18 + --> $DIR/numeric-suffix-isize.rs:46:18 | LL | foo::(42_i16); | ------------ ^^^^^^ expected `isize`, found `i16` @@ -151,7 +151,7 @@ LL + foo::(42_isize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-isize.rs:61:18 + --> $DIR/numeric-suffix-isize.rs:50:18 | LL | foo::(42_i8); | ------------ ^^^^^ expected `isize`, found `i8` @@ -170,7 +170,7 @@ LL + foo::(42_isize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-isize.rs:65:18 + --> $DIR/numeric-suffix-isize.rs:54:18 | LL | foo::(42.0_f64); | ------------ ^^^^^^^^ expected `isize`, found `f64` @@ -189,7 +189,7 @@ LL + foo::(42isize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-isize.rs:69:18 + --> $DIR/numeric-suffix-isize.rs:58:18 | LL | foo::(42.0_f32); | ------------ ^^^^^^^^ expected `isize`, found `f32` diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.fixed b/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.fixed index 2dd7d9aabdb57..eba5d7c1c2667 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.fixed +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.fixed @@ -12,17 +12,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE fn main() { foo::(42_u32); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.rs b/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.rs index 46bbb03318540..d127353e97540 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.rs +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.rs @@ -12,17 +12,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE fn main() { foo::(42_usize); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.stderr index 610e6ece27691..95e913595e5e5 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/numeric-suffix-u32.rs:28:16 + --> $DIR/numeric-suffix-u32.rs:17:16 | LL | foo::(42_usize); | ---------- ^^^^^^^^ expected `u32`, found `usize` @@ -18,7 +18,7 @@ LL + foo::(42_u32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u32.rs:32:16 + --> $DIR/numeric-suffix-u32.rs:21:16 | LL | foo::(42_u64); | ---------- ^^^^^^ expected `u32`, found `u64` @@ -37,7 +37,7 @@ LL + foo::(42_u32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u32.rs:37:16 + --> $DIR/numeric-suffix-u32.rs:26:16 | LL | foo::(42_u16); | ---------- ^^^^^^ expected `u32`, found `u16` @@ -56,7 +56,7 @@ LL + foo::(42_u32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u32.rs:41:16 + --> $DIR/numeric-suffix-u32.rs:30:16 | LL | foo::(42_u8); | ---------- ^^^^^ expected `u32`, found `u8` @@ -75,7 +75,7 @@ LL + foo::(42_u32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u32.rs:45:16 + --> $DIR/numeric-suffix-u32.rs:34:16 | LL | foo::(42_isize); | ---------- ^^^^^^^^ expected `u32`, found `isize` @@ -94,7 +94,7 @@ LL + foo::(42_u32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u32.rs:49:16 + --> $DIR/numeric-suffix-u32.rs:38:16 | LL | foo::(42_i64); | ---------- ^^^^^^ expected `u32`, found `i64` @@ -113,7 +113,7 @@ LL + foo::(42_u32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u32.rs:53:16 + --> $DIR/numeric-suffix-u32.rs:42:16 | LL | foo::(42_i32); | ---------- ^^^^^^ expected `u32`, found `i32` @@ -132,7 +132,7 @@ LL + foo::(42_u32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u32.rs:57:16 + --> $DIR/numeric-suffix-u32.rs:46:16 | LL | foo::(42_i16); | ---------- ^^^^^^ expected `u32`, found `i16` @@ -151,7 +151,7 @@ LL + foo::(42_u32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u32.rs:61:16 + --> $DIR/numeric-suffix-u32.rs:50:16 | LL | foo::(42_i8); | ---------- ^^^^^ expected `u32`, found `i8` @@ -170,7 +170,7 @@ LL + foo::(42_u32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u32.rs:65:16 + --> $DIR/numeric-suffix-u32.rs:54:16 | LL | foo::(42.0_f64); | ---------- ^^^^^^^^ expected `u32`, found `f64` @@ -189,7 +189,7 @@ LL + foo::(42u32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u32.rs:69:16 + --> $DIR/numeric-suffix-u32.rs:58:16 | LL | foo::(42.0_f32); | ---------- ^^^^^^^^ expected `u32`, found `f32` diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.fixed b/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.fixed index 2dea195f09810..c8344211d3d0b 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.fixed +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.fixed @@ -12,17 +12,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE fn main() { foo::(42_u64); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.rs b/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.rs index 6fca089b07d49..f9b35efbc4be8 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.rs +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.rs @@ -12,17 +12,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE fn main() { foo::(42_usize); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.stderr index 112dddccd6f47..0c92b41376144 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/numeric-suffix-u64.rs:28:16 + --> $DIR/numeric-suffix-u64.rs:17:16 | LL | foo::(42_usize); | ---------- ^^^^^^^^ expected `u64`, found `usize` @@ -18,7 +18,7 @@ LL + foo::(42_u64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u64.rs:33:16 + --> $DIR/numeric-suffix-u64.rs:22:16 | LL | foo::(42_u32); | ---------- ^^^^^^ expected `u64`, found `u32` @@ -37,7 +37,7 @@ LL + foo::(42_u64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u64.rs:37:16 + --> $DIR/numeric-suffix-u64.rs:26:16 | LL | foo::(42_u16); | ---------- ^^^^^^ expected `u64`, found `u16` @@ -56,7 +56,7 @@ LL + foo::(42_u64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u64.rs:41:16 + --> $DIR/numeric-suffix-u64.rs:30:16 | LL | foo::(42_u8); | ---------- ^^^^^ expected `u64`, found `u8` @@ -75,7 +75,7 @@ LL + foo::(42_u64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u64.rs:45:16 + --> $DIR/numeric-suffix-u64.rs:34:16 | LL | foo::(42_isize); | ---------- ^^^^^^^^ expected `u64`, found `isize` @@ -94,7 +94,7 @@ LL + foo::(42_u64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u64.rs:49:16 + --> $DIR/numeric-suffix-u64.rs:38:16 | LL | foo::(42_i64); | ---------- ^^^^^^ expected `u64`, found `i64` @@ -113,7 +113,7 @@ LL + foo::(42_u64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u64.rs:53:16 + --> $DIR/numeric-suffix-u64.rs:42:16 | LL | foo::(42_i32); | ---------- ^^^^^^ expected `u64`, found `i32` @@ -132,7 +132,7 @@ LL + foo::(42_u64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u64.rs:57:16 + --> $DIR/numeric-suffix-u64.rs:46:16 | LL | foo::(42_i16); | ---------- ^^^^^^ expected `u64`, found `i16` @@ -151,7 +151,7 @@ LL + foo::(42_u64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u64.rs:61:16 + --> $DIR/numeric-suffix-u64.rs:50:16 | LL | foo::(42_i8); | ---------- ^^^^^ expected `u64`, found `i8` @@ -170,7 +170,7 @@ LL + foo::(42_u64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u64.rs:65:16 + --> $DIR/numeric-suffix-u64.rs:54:16 | LL | foo::(42.0_f64); | ---------- ^^^^^^^^ expected `u64`, found `f64` @@ -189,7 +189,7 @@ LL + foo::(42u64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-u64.rs:69:16 + --> $DIR/numeric-suffix-u64.rs:58:16 | LL | foo::(42.0_f32); | ---------- ^^^^^^^^ expected `u64`, found `f32` diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.fixed b/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.fixed index 63422c305d357..4d5176fe72456 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.fixed +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.fixed @@ -12,17 +12,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE fn main() { foo::(42_usize); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.rs b/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.rs index 4d20e4fc8431d..118d8ca219ec5 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.rs +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.rs @@ -12,17 +12,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE fn main() { foo::(42_usize); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.stderr index e7d6a04f18ef3..86181d6714f5f 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/numeric-suffix-usize.rs:29:18 + --> $DIR/numeric-suffix-usize.rs:18:18 | LL | foo::(42_u64); | ------------ ^^^^^^ expected `usize`, found `u64` @@ -18,7 +18,7 @@ LL + foo::(42_usize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-usize.rs:33:18 + --> $DIR/numeric-suffix-usize.rs:22:18 | LL | foo::(42_u32); | ------------ ^^^^^^ expected `usize`, found `u32` @@ -37,7 +37,7 @@ LL + foo::(42_usize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-usize.rs:37:18 + --> $DIR/numeric-suffix-usize.rs:26:18 | LL | foo::(42_u16); | ------------ ^^^^^^ expected `usize`, found `u16` @@ -56,7 +56,7 @@ LL + foo::(42_usize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-usize.rs:41:18 + --> $DIR/numeric-suffix-usize.rs:30:18 | LL | foo::(42_u8); | ------------ ^^^^^ expected `usize`, found `u8` @@ -75,7 +75,7 @@ LL + foo::(42_usize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-usize.rs:45:18 + --> $DIR/numeric-suffix-usize.rs:34:18 | LL | foo::(42_isize); | ------------ ^^^^^^^^ expected `usize`, found `isize` @@ -94,7 +94,7 @@ LL + foo::(42_usize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-usize.rs:49:18 + --> $DIR/numeric-suffix-usize.rs:38:18 | LL | foo::(42_i64); | ------------ ^^^^^^ expected `usize`, found `i64` @@ -113,7 +113,7 @@ LL + foo::(42_usize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-usize.rs:53:18 + --> $DIR/numeric-suffix-usize.rs:42:18 | LL | foo::(42_i32); | ------------ ^^^^^^ expected `usize`, found `i32` @@ -132,7 +132,7 @@ LL + foo::(42_usize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-usize.rs:57:18 + --> $DIR/numeric-suffix-usize.rs:46:18 | LL | foo::(42_i16); | ------------ ^^^^^^ expected `usize`, found `i16` @@ -151,7 +151,7 @@ LL + foo::(42_usize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-usize.rs:61:18 + --> $DIR/numeric-suffix-usize.rs:50:18 | LL | foo::(42_i8); | ------------ ^^^^^ expected `usize`, found `i8` @@ -170,7 +170,7 @@ LL + foo::(42_usize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-usize.rs:65:18 + --> $DIR/numeric-suffix-usize.rs:54:18 | LL | foo::(42.0_f64); | ------------ ^^^^^^^^ expected `usize`, found `f64` @@ -189,7 +189,7 @@ LL + foo::(42usize); | error[E0308]: mismatched types - --> $DIR/numeric-suffix-usize.rs:69:18 + --> $DIR/numeric-suffix-usize.rs:58:18 | LL | foo::(42.0_f32); | ------------ ^^^^^^^^ expected `usize`, found `f32` diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix.fixed b/tests/ui/numeric/numeric-suffix/numeric-suffix.fixed index 270afb639575d..b20b594dc2c35 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix.fixed +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix.fixed @@ -69,75 +69,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE - fn main() { foo::(42_u16); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix.rs b/tests/ui/numeric/numeric-suffix/numeric-suffix.rs index 05be58e335bca..9272902f51902 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix.rs +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix.rs @@ -69,75 +69,6 @@ fn foo(_x: N) {} //~| NOTE function defined here //~| NOTE function defined here //~| NOTE function defined here -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE -//~| NOTE - fn main() { foo::(42_usize); diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix.stderr index d26639a76f0ff..cae1268beec04 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:143:16 + --> $DIR/numeric-suffix.rs:74:16 | LL | foo::(42_usize); | ---------- ^^^^^^^^ expected `u16`, found `usize` @@ -18,7 +18,7 @@ LL + foo::(42_u16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:147:16 + --> $DIR/numeric-suffix.rs:78:16 | LL | foo::(42_u64); | ---------- ^^^^^^ expected `u16`, found `u64` @@ -37,7 +37,7 @@ LL + foo::(42_u16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:151:16 + --> $DIR/numeric-suffix.rs:82:16 | LL | foo::(42_u32); | ---------- ^^^^^^ expected `u16`, found `u32` @@ -56,7 +56,7 @@ LL + foo::(42_u16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:156:16 + --> $DIR/numeric-suffix.rs:87:16 | LL | foo::(42_u8); | ---------- ^^^^^ expected `u16`, found `u8` @@ -75,7 +75,7 @@ LL + foo::(42_u16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:160:16 + --> $DIR/numeric-suffix.rs:91:16 | LL | foo::(42_isize); | ---------- ^^^^^^^^ expected `u16`, found `isize` @@ -94,7 +94,7 @@ LL + foo::(42_u16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:164:16 + --> $DIR/numeric-suffix.rs:95:16 | LL | foo::(42_i64); | ---------- ^^^^^^ expected `u16`, found `i64` @@ -113,7 +113,7 @@ LL + foo::(42_u16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:168:16 + --> $DIR/numeric-suffix.rs:99:16 | LL | foo::(42_i32); | ---------- ^^^^^^ expected `u16`, found `i32` @@ -132,7 +132,7 @@ LL + foo::(42_u16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:172:16 + --> $DIR/numeric-suffix.rs:103:16 | LL | foo::(42_i16); | ---------- ^^^^^^ expected `u16`, found `i16` @@ -151,7 +151,7 @@ LL + foo::(42_u16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:176:16 + --> $DIR/numeric-suffix.rs:107:16 | LL | foo::(42_i8); | ---------- ^^^^^ expected `u16`, found `i8` @@ -170,7 +170,7 @@ LL + foo::(42_u16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:180:16 + --> $DIR/numeric-suffix.rs:111:16 | LL | foo::(42.0_f64); | ---------- ^^^^^^^^ expected `u16`, found `f64` @@ -189,7 +189,7 @@ LL + foo::(42u16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:184:16 + --> $DIR/numeric-suffix.rs:115:16 | LL | foo::(42.0_f32); | ---------- ^^^^^^^^ expected `u16`, found `f32` @@ -208,7 +208,7 @@ LL + foo::(42u16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:189:16 + --> $DIR/numeric-suffix.rs:120:16 | LL | foo::(42_usize); | ---------- ^^^^^^^^ expected `i16`, found `usize` @@ -227,7 +227,7 @@ LL + foo::(42_i16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:193:16 + --> $DIR/numeric-suffix.rs:124:16 | LL | foo::(42_u64); | ---------- ^^^^^^ expected `i16`, found `u64` @@ -246,7 +246,7 @@ LL + foo::(42_i16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:197:16 + --> $DIR/numeric-suffix.rs:128:16 | LL | foo::(42_u32); | ---------- ^^^^^^ expected `i16`, found `u32` @@ -265,7 +265,7 @@ LL + foo::(42_i16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:201:16 + --> $DIR/numeric-suffix.rs:132:16 | LL | foo::(42_u16); | ---------- ^^^^^^ expected `i16`, found `u16` @@ -284,7 +284,7 @@ LL + foo::(42_i16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:205:16 + --> $DIR/numeric-suffix.rs:136:16 | LL | foo::(42_u8); | ---------- ^^^^^ expected `i16`, found `u8` @@ -303,7 +303,7 @@ LL + foo::(42_i16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:209:16 + --> $DIR/numeric-suffix.rs:140:16 | LL | foo::(42_isize); | ---------- ^^^^^^^^ expected `i16`, found `isize` @@ -322,7 +322,7 @@ LL + foo::(42_i16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:213:16 + --> $DIR/numeric-suffix.rs:144:16 | LL | foo::(42_i64); | ---------- ^^^^^^ expected `i16`, found `i64` @@ -341,7 +341,7 @@ LL + foo::(42_i16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:217:16 + --> $DIR/numeric-suffix.rs:148:16 | LL | foo::(42_i32); | ---------- ^^^^^^ expected `i16`, found `i32` @@ -360,7 +360,7 @@ LL + foo::(42_i16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:222:16 + --> $DIR/numeric-suffix.rs:153:16 | LL | foo::(42_i8); | ---------- ^^^^^ expected `i16`, found `i8` @@ -379,7 +379,7 @@ LL + foo::(42_i16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:226:16 + --> $DIR/numeric-suffix.rs:157:16 | LL | foo::(42.0_f64); | ---------- ^^^^^^^^ expected `i16`, found `f64` @@ -398,7 +398,7 @@ LL + foo::(42i16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:230:16 + --> $DIR/numeric-suffix.rs:161:16 | LL | foo::(42.0_f32); | ---------- ^^^^^^^^ expected `i16`, found `f32` @@ -417,7 +417,7 @@ LL + foo::(42i16); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:235:15 + --> $DIR/numeric-suffix.rs:166:15 | LL | foo::(42_usize); | --------- ^^^^^^^^ expected `u8`, found `usize` @@ -436,7 +436,7 @@ LL + foo::(42_u8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:239:15 + --> $DIR/numeric-suffix.rs:170:15 | LL | foo::(42_u64); | --------- ^^^^^^ expected `u8`, found `u64` @@ -455,7 +455,7 @@ LL + foo::(42_u8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:243:15 + --> $DIR/numeric-suffix.rs:174:15 | LL | foo::(42_u32); | --------- ^^^^^^ expected `u8`, found `u32` @@ -474,7 +474,7 @@ LL + foo::(42_u8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:247:15 + --> $DIR/numeric-suffix.rs:178:15 | LL | foo::(42_u16); | --------- ^^^^^^ expected `u8`, found `u16` @@ -493,7 +493,7 @@ LL + foo::(42_u8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:252:15 + --> $DIR/numeric-suffix.rs:183:15 | LL | foo::(42_isize); | --------- ^^^^^^^^ expected `u8`, found `isize` @@ -512,7 +512,7 @@ LL + foo::(42_u8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:256:15 + --> $DIR/numeric-suffix.rs:187:15 | LL | foo::(42_i64); | --------- ^^^^^^ expected `u8`, found `i64` @@ -531,7 +531,7 @@ LL + foo::(42_u8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:260:15 + --> $DIR/numeric-suffix.rs:191:15 | LL | foo::(42_i32); | --------- ^^^^^^ expected `u8`, found `i32` @@ -550,7 +550,7 @@ LL + foo::(42_u8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:264:15 + --> $DIR/numeric-suffix.rs:195:15 | LL | foo::(42_i16); | --------- ^^^^^^ expected `u8`, found `i16` @@ -569,7 +569,7 @@ LL + foo::(42_u8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:268:15 + --> $DIR/numeric-suffix.rs:199:15 | LL | foo::(42_i8); | --------- ^^^^^ expected `u8`, found `i8` @@ -588,7 +588,7 @@ LL + foo::(42_u8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:272:15 + --> $DIR/numeric-suffix.rs:203:15 | LL | foo::(42.0_f64); | --------- ^^^^^^^^ expected `u8`, found `f64` @@ -607,7 +607,7 @@ LL + foo::(42u8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:276:15 + --> $DIR/numeric-suffix.rs:207:15 | LL | foo::(42.0_f32); | --------- ^^^^^^^^ expected `u8`, found `f32` @@ -626,7 +626,7 @@ LL + foo::(42u8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:281:15 + --> $DIR/numeric-suffix.rs:212:15 | LL | foo::(42_usize); | --------- ^^^^^^^^ expected `i8`, found `usize` @@ -645,7 +645,7 @@ LL + foo::(42_i8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:285:15 + --> $DIR/numeric-suffix.rs:216:15 | LL | foo::(42_u64); | --------- ^^^^^^ expected `i8`, found `u64` @@ -664,7 +664,7 @@ LL + foo::(42_i8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:289:15 + --> $DIR/numeric-suffix.rs:220:15 | LL | foo::(42_u32); | --------- ^^^^^^ expected `i8`, found `u32` @@ -683,7 +683,7 @@ LL + foo::(42_i8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:293:15 + --> $DIR/numeric-suffix.rs:224:15 | LL | foo::(42_u16); | --------- ^^^^^^ expected `i8`, found `u16` @@ -702,7 +702,7 @@ LL + foo::(42_i8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:297:15 + --> $DIR/numeric-suffix.rs:228:15 | LL | foo::(42_u8); | --------- ^^^^^ expected `i8`, found `u8` @@ -721,7 +721,7 @@ LL + foo::(42_i8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:301:15 + --> $DIR/numeric-suffix.rs:232:15 | LL | foo::(42_isize); | --------- ^^^^^^^^ expected `i8`, found `isize` @@ -740,7 +740,7 @@ LL + foo::(42_i8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:305:15 + --> $DIR/numeric-suffix.rs:236:15 | LL | foo::(42_i64); | --------- ^^^^^^ expected `i8`, found `i64` @@ -759,7 +759,7 @@ LL + foo::(42_i8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:309:15 + --> $DIR/numeric-suffix.rs:240:15 | LL | foo::(42_i32); | --------- ^^^^^^ expected `i8`, found `i32` @@ -778,7 +778,7 @@ LL + foo::(42_i8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:313:15 + --> $DIR/numeric-suffix.rs:244:15 | LL | foo::(42_i16); | --------- ^^^^^^ expected `i8`, found `i16` @@ -797,7 +797,7 @@ LL + foo::(42_i8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:318:15 + --> $DIR/numeric-suffix.rs:249:15 | LL | foo::(42.0_f64); | --------- ^^^^^^^^ expected `i8`, found `f64` @@ -816,7 +816,7 @@ LL + foo::(42i8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:322:15 + --> $DIR/numeric-suffix.rs:253:15 | LL | foo::(42.0_f32); | --------- ^^^^^^^^ expected `i8`, found `f32` @@ -835,7 +835,7 @@ LL + foo::(42i8); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:327:16 + --> $DIR/numeric-suffix.rs:258:16 | LL | foo::(42_usize); | ---------- ^^^^^^^^ expected `f64`, found `usize` @@ -854,7 +854,7 @@ LL + foo::(42_f64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:331:16 + --> $DIR/numeric-suffix.rs:262:16 | LL | foo::(42_u64); | ---------- ^^^^^^ expected `f64`, found `u64` @@ -873,7 +873,7 @@ LL + foo::(42_f64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:335:16 + --> $DIR/numeric-suffix.rs:266:16 | LL | foo::(42_u32); | ---------- ^^^^^^ expected `f64`, found `u32` @@ -891,7 +891,7 @@ LL | foo::(42_u32.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:339:16 + --> $DIR/numeric-suffix.rs:270:16 | LL | foo::(42_u16); | ---------- ^^^^^^ expected `f64`, found `u16` @@ -909,7 +909,7 @@ LL | foo::(42_u16.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:343:16 + --> $DIR/numeric-suffix.rs:274:16 | LL | foo::(42_u8); | ---------- ^^^^^ expected `f64`, found `u8` @@ -927,7 +927,7 @@ LL | foo::(42_u8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:347:16 + --> $DIR/numeric-suffix.rs:278:16 | LL | foo::(42_isize); | ---------- ^^^^^^^^ expected `f64`, found `isize` @@ -946,7 +946,7 @@ LL + foo::(42_f64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:351:16 + --> $DIR/numeric-suffix.rs:282:16 | LL | foo::(42_i64); | ---------- ^^^^^^ expected `f64`, found `i64` @@ -965,7 +965,7 @@ LL + foo::(42_f64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:355:16 + --> $DIR/numeric-suffix.rs:286:16 | LL | foo::(42_i32); | ---------- ^^^^^^ expected `f64`, found `i32` @@ -983,7 +983,7 @@ LL | foo::(42_i32.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:359:16 + --> $DIR/numeric-suffix.rs:290:16 | LL | foo::(42_i16); | ---------- ^^^^^^ expected `f64`, found `i16` @@ -1001,7 +1001,7 @@ LL | foo::(42_i16.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:363:16 + --> $DIR/numeric-suffix.rs:294:16 | LL | foo::(42_i8); | ---------- ^^^^^ expected `f64`, found `i8` @@ -1019,7 +1019,7 @@ LL | foo::(42_i8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:368:16 + --> $DIR/numeric-suffix.rs:299:16 | LL | foo::(42.0_f32); | ---------- ^^^^^^^^ expected `f64`, found `f32` @@ -1038,7 +1038,7 @@ LL + foo::(42.0_f64); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:373:16 + --> $DIR/numeric-suffix.rs:304:16 | LL | foo::(42_usize); | ---------- ^^^^^^^^ expected `f32`, found `usize` @@ -1057,7 +1057,7 @@ LL + foo::(42_f32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:377:16 + --> $DIR/numeric-suffix.rs:308:16 | LL | foo::(42_u64); | ---------- ^^^^^^ expected `f32`, found `u64` @@ -1076,7 +1076,7 @@ LL + foo::(42_f32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:381:16 + --> $DIR/numeric-suffix.rs:312:16 | LL | foo::(42_u32); | ---------- ^^^^^^ expected `f32`, found `u32` @@ -1095,7 +1095,7 @@ LL + foo::(42_f32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:385:16 + --> $DIR/numeric-suffix.rs:316:16 | LL | foo::(42_u16); | ---------- ^^^^^^ expected `f32`, found `u16` @@ -1113,7 +1113,7 @@ LL | foo::(42_u16.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:389:16 + --> $DIR/numeric-suffix.rs:320:16 | LL | foo::(42_u8); | ---------- ^^^^^ expected `f32`, found `u8` @@ -1131,7 +1131,7 @@ LL | foo::(42_u8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:393:16 + --> $DIR/numeric-suffix.rs:324:16 | LL | foo::(42_isize); | ---------- ^^^^^^^^ expected `f32`, found `isize` @@ -1150,7 +1150,7 @@ LL + foo::(42_f32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:397:16 + --> $DIR/numeric-suffix.rs:328:16 | LL | foo::(42_i64); | ---------- ^^^^^^ expected `f32`, found `i64` @@ -1169,7 +1169,7 @@ LL + foo::(42_f32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:401:16 + --> $DIR/numeric-suffix.rs:332:16 | LL | foo::(42_i32); | ---------- ^^^^^^ expected `f32`, found `i32` @@ -1188,7 +1188,7 @@ LL + foo::(42_f32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:405:16 + --> $DIR/numeric-suffix.rs:336:16 | LL | foo::(42_i16); | ---------- ^^^^^^ expected `f32`, found `i16` @@ -1206,7 +1206,7 @@ LL | foo::(42_i16.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:409:16 + --> $DIR/numeric-suffix.rs:340:16 | LL | foo::(42_i8); | ---------- ^^^^^ expected `f32`, found `i8` @@ -1224,7 +1224,7 @@ LL | foo::(42_i8.into()); | +++++++ error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:413:16 + --> $DIR/numeric-suffix.rs:344:16 | LL | foo::(42.0_f64); | ---------- ^^^^^^^^ expected `f32`, found `f64` @@ -1243,7 +1243,7 @@ LL + foo::(42.0_f32); | error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:419:16 + --> $DIR/numeric-suffix.rs:350:16 | LL | foo::(42_u8 as u16); | ---------- ^^^^^^^^^^^^ expected `u32`, found `u16` @@ -1261,7 +1261,7 @@ LL | foo::((42_u8 as u16).into()); | + ++++++++ error[E0308]: mismatched types - --> $DIR/numeric-suffix.rs:423:16 + --> $DIR/numeric-suffix.rs:354:16 | LL | foo::(-42_i8); | ---------- ^^^^^^ expected `i32`, found `i8` diff --git a/tests/ui/occurs-check-2.rs b/tests/ui/occurs-check-2.rs index 1ec460a873527..9289a8e870a18 100644 --- a/tests/ui/occurs-check-2.rs +++ b/tests/ui/occurs-check-2.rs @@ -4,6 +4,6 @@ fn main() { let g; g = f; - f = Box::new(g); //~^ ERROR overflow assigning `Box<_>` to `_` + f = Box::new(g); } diff --git a/tests/ui/occurs-check-2.stderr b/tests/ui/occurs-check-2.stderr index 54307a6c5474f..5f296967f30d8 100644 --- a/tests/ui/occurs-check-2.stderr +++ b/tests/ui/occurs-check-2.stderr @@ -1,8 +1,8 @@ error[E0275]: overflow assigning `Box<_>` to `_` - --> $DIR/occurs-check-2.rs:7:9 + --> $DIR/occurs-check-2.rs:6:9 | -LL | f = Box::new(g); - | ^^^^^^^^^^^ +LL | g = f; + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/occurs-check-3.stderr b/tests/ui/occurs-check-3.stderr index 77b67ec1a62c6..eb05c94957c98 100644 --- a/tests/ui/occurs-check-3.stderr +++ b/tests/ui/occurs-check-3.stderr @@ -1,8 +1,8 @@ error[E0275]: overflow assigning `Clam<_>` to `_` - --> $DIR/occurs-check-3.rs:6:9 + --> $DIR/occurs-check-3.rs:6:17 | LL | c = Clam::A(c); - | ^^^^^^^^^^ + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/occurs-check.stderr b/tests/ui/occurs-check.stderr index 30468d68cbd05..ea7c541abc135 100644 --- a/tests/ui/occurs-check.stderr +++ b/tests/ui/occurs-check.stderr @@ -1,8 +1,8 @@ error[E0275]: overflow assigning `Box<_>` to `_` - --> $DIR/occurs-check.rs:3:9 + --> $DIR/occurs-check.rs:3:18 | LL | f = Box::new(f); - | ^^^^^^^^^^^ + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/offset-of/offset-of-self.rs b/tests/ui/offset-of/offset-of-self.rs index e5730b8cf6cdb..0a6de2ebbb356 100644 --- a/tests/ui/offset-of/offset-of-self.rs +++ b/tests/ui/offset-of/offset-of-self.rs @@ -15,8 +15,8 @@ impl S { offset_of!(Self, v) } fn v_offs_wrong_syntax() { - offset_of!(Self, Self::v); //~ offset_of expects dot-separated field and variant names - offset_of!(S, Self); //~ no field `Self` on type `S` + offset_of!(Self, Self::v); //~ ERROR offset_of expects dot-separated field and variant names + offset_of!(S, Self); //~ ERROR no field `Self` on type `S` } fn offs_in_c() -> usize { offset_of!(C, w) @@ -48,6 +48,6 @@ fn main() { offset_of!(self::S, v); offset_of!(Self, v); //~ ERROR cannot find type `Self` in this scope - offset_of!(S, self); //~ no field `self` on type `S` - offset_of!(S, v.self); //~ no field `self` on type `u8` + offset_of!(S, self); //~ ERROR no field `self` on type `S` + offset_of!(S, v.self); //~ ERROR no field `self` on type `u8` } diff --git a/tests/ui/offset-of/offset-of-tuple.rs b/tests/ui/offset-of/offset-of-tuple.rs index db00fe05583f0..e844724944131 100644 --- a/tests/ui/offset-of/offset-of-tuple.rs +++ b/tests/ui/offset-of/offset-of-tuple.rs @@ -11,7 +11,7 @@ fn main() { offset_of!((u8, u8), +1); //~ ERROR no rules expected offset_of!((u8, u8), -1); //~ ERROR offset_of expects dot-separated field and variant names offset_of!((u8, u8), 1.); //~ ERROR offset_of expects dot-separated field and variant names - offset_of!((u8, u8), 1 .); //~ unexpected token: `)` + offset_of!((u8, u8), 1 .); //~ ERROR unexpected token: `)` builtin # offset_of((u8, u8), 1e2); //~ ERROR no field `1e2` builtin # offset_of((u8, u8), _0); //~ ERROR no field `_0` builtin # offset_of((u8, u8), 01); //~ ERROR no field `01` diff --git a/tests/ui/on-unimplemented/bad-annotation.rs b/tests/ui/on-unimplemented/bad-annotation.rs index 4c6610f886433..25de597811027 100644 --- a/tests/ui/on-unimplemented/bad-annotation.rs +++ b/tests/ui/on-unimplemented/bad-annotation.rs @@ -1,64 +1,109 @@ -// ignore-tidy-linelength - +#![crate_type = "lib"] #![feature(rustc_attrs)] - #![allow(unused)] #[rustc_on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}`"] -trait Foo -{} +trait Foo {} -#[rustc_on_unimplemented="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`"] +#[rustc_on_unimplemented = "a collection of type `{Self}` cannot \ + be built from an iterator over elements of type `{A}`"] trait MyFromIterator { /// Builds a container with elements from an external iterator. - fn my_from_iter>(iterator: T) -> Self; + fn my_from_iter>(iterator: T) -> Self; } #[rustc_on_unimplemented] //~^ ERROR malformed `rustc_on_unimplemented` attribute -trait BadAnnotation1 -{} +trait NoContent {} #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"] -//~^ ERROR there is no parameter `C` on trait `BadAnnotation2` -trait BadAnnotation2 -{} +//~^ ERROR cannot find parameter C on this trait +trait ParameterNotPresent {} #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"] -//~^ only named generic parameters are allowed -trait BadAnnotation3 -{} +//~^ ERROR positional format arguments are not allowed here +trait NoPositionalArgs {} -#[rustc_on_unimplemented(lorem="")] -//~^ this attribute must have a valid -trait BadAnnotation4 {} +#[rustc_on_unimplemented(lorem = "")] +//~^ ERROR this attribute must have a value +//~^^ NOTE e.g. `#[rustc_on_unimplemented(message="foo")]` +//~^^^ NOTE expected value here +trait EmptyMessage {} #[rustc_on_unimplemented(lorem(ipsum(dolor)))] -//~^ this attribute must have a valid -trait BadAnnotation5 {} - -#[rustc_on_unimplemented(message="x", message="y")] -//~^ this attribute must have a valid -trait BadAnnotation6 {} - -#[rustc_on_unimplemented(message="x", on(desugared, message="y"))] -//~^ this attribute must have a valid -trait BadAnnotation7 {} - -#[rustc_on_unimplemented(on(), message="y")] -//~^ empty `on`-clause -trait BadAnnotation8 {} - -#[rustc_on_unimplemented(on="x", message="y")] -//~^ this attribute must have a valid -trait BadAnnotation9 {} - -#[rustc_on_unimplemented(on(x="y"), message="y")] -trait BadAnnotation10 {} - -#[rustc_on_unimplemented(on(desugared, on(desugared, message="x")), message="y")] -//~^ this attribute must have a valid -trait BadAnnotation11 {} - -pub fn main() { -} +//~^ ERROR this attribute must have a value +//~^^ NOTE e.g. `#[rustc_on_unimplemented(message="foo")]` +//~^^^ NOTE expected value here +trait Invalid {} + +#[rustc_on_unimplemented(message = "x", message = "y")] +//~^ ERROR this attribute must have a value +//~^^ NOTE e.g. `#[rustc_on_unimplemented(message="foo")]` +//~^^^ NOTE expected value here +trait DuplicateMessage {} + +#[rustc_on_unimplemented(message = "x", on(desugared, message = "y"))] +//~^ ERROR this attribute must have a value +//~^^ NOTE e.g. `#[rustc_on_unimplemented(message="foo")]` +//~^^^ NOTE expected value here +trait OnInWrongPosition {} + +#[rustc_on_unimplemented(on(), message = "y")] +//~^ ERROR empty `on`-clause +//~^^ NOTE empty `on`-clause here +trait EmptyOn {} + +#[rustc_on_unimplemented(on = "x", message = "y")] +//~^ ERROR this attribute must have a value +//~^^ NOTE e.g. `#[rustc_on_unimplemented(message="foo")]` +//~^^^ NOTE expected value here +trait ExpectedPredicateInOn {} + +#[rustc_on_unimplemented(on(x = "y"), message = "y")] +trait OnWithoutDirectives {} + +#[rustc_on_unimplemented(on(from_desugaring, on(from_desugaring, message = "x")), message = "y")] +//~^ ERROR this attribute must have a value +//~^^ NOTE e.g. `#[rustc_on_unimplemented(message="foo")]` +//~^^^ NOTE expected value here +trait NestedOn {} + +#[rustc_on_unimplemented(on("y", message = "y"))] +//~^ ERROR literals inside `on`-clauses are not supported +//~^^ NOTE unexpected literal here +trait UnsupportedLiteral {} + +#[rustc_on_unimplemented(on(42, message = "y"))] +//~^ ERROR literals inside `on`-clauses are not supported +//~^^ NOTE unexpected literal here +trait UnsupportedLiteral2 {} + +#[rustc_on_unimplemented(on(not(a, b), message = "y"))] +//~^ ERROR expected a single predicate in `not(..)` [E0232] +//~^^ NOTE unexpected quantity of predicates here +trait ExpectedOnePattern {} + +#[rustc_on_unimplemented(on(not(), message = "y"))] +//~^ ERROR expected a single predicate in `not(..)` [E0232] +//~^^ NOTE unexpected quantity of predicates here +trait ExpectedOnePattern2 {} + +#[rustc_on_unimplemented(on(thing::What, message = "y"))] +//~^ ERROR expected an identifier inside this `on`-clause +//~^^ NOTE expected an identifier here, not `thing::What` +trait KeyMustBeIdentifier {} + +#[rustc_on_unimplemented(on(thing::What = "value", message = "y"))] +//~^ ERROR expected an identifier inside this `on`-clause +//~^^ NOTE expected an identifier here, not `thing::What` +trait KeyMustBeIdentifier2 {} + +#[rustc_on_unimplemented(on(aaaaaaaaaaaaaa(a, b), message = "y"))] +//~^ ERROR this predicate is invalid +//~^^ NOTE expected one of `any`, `all` or `not` here, not `aaaaaaaaaaaaaa` +trait InvalidPredicate {} + +#[rustc_on_unimplemented(on(something, message = "y"))] +//~^ ERROR invalid flag in `on`-clause +//~^^ NOTE expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `something` +trait InvalidFlag {} diff --git a/tests/ui/on-unimplemented/bad-annotation.stderr b/tests/ui/on-unimplemented/bad-annotation.stderr index 4ceea779b29dc..35b919c7b7853 100644 --- a/tests/ui/on-unimplemented/bad-annotation.stderr +++ b/tests/ui/on-unimplemented/bad-annotation.stderr @@ -1,5 +1,5 @@ error: malformed `rustc_on_unimplemented` attribute input - --> $DIR/bad-annotation.rs:17:1 + --> $DIR/bad-annotation.rs:15:1 | LL | #[rustc_on_unimplemented] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,73 +11,121 @@ LL | #[rustc_on_unimplemented = "message"] LL | #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")] | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -error[E0230]: there is no parameter `C` on trait `BadAnnotation2` - --> $DIR/bad-annotation.rs:22:1 +error[E0230]: cannot find parameter C on this trait + --> $DIR/bad-annotation.rs:19:90 | LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ -error[E0231]: only named generic parameters are allowed - --> $DIR/bad-annotation.rs:27:1 +error[E0231]: positional format arguments are not allowed here + --> $DIR/bad-annotation.rs:23:90 | LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ -error[E0232]: this attribute must have a valid value - --> $DIR/bad-annotation.rs:32:26 +error[E0232]: this attribute must have a value + --> $DIR/bad-annotation.rs:27:26 | -LL | #[rustc_on_unimplemented(lorem="")] - | ^^^^^^^^ expected value here +LL | #[rustc_on_unimplemented(lorem = "")] + | ^^^^^^^^^^ expected value here | - = note: eg `#[rustc_on_unimplemented(message="foo")]` + = note: e.g. `#[rustc_on_unimplemented(message="foo")]` -error[E0232]: this attribute must have a valid value - --> $DIR/bad-annotation.rs:36:26 +error[E0232]: this attribute must have a value + --> $DIR/bad-annotation.rs:33:26 | LL | #[rustc_on_unimplemented(lorem(ipsum(dolor)))] | ^^^^^^^^^^^^^^^^^^^ expected value here | - = note: eg `#[rustc_on_unimplemented(message="foo")]` + = note: e.g. `#[rustc_on_unimplemented(message="foo")]` -error[E0232]: this attribute must have a valid value - --> $DIR/bad-annotation.rs:40:39 +error[E0232]: this attribute must have a value + --> $DIR/bad-annotation.rs:39:41 | -LL | #[rustc_on_unimplemented(message="x", message="y")] - | ^^^^^^^^^^^ expected value here +LL | #[rustc_on_unimplemented(message = "x", message = "y")] + | ^^^^^^^^^^^^^ expected value here | - = note: eg `#[rustc_on_unimplemented(message="foo")]` + = note: e.g. `#[rustc_on_unimplemented(message="foo")]` -error[E0232]: this attribute must have a valid value - --> $DIR/bad-annotation.rs:44:39 +error[E0232]: this attribute must have a value + --> $DIR/bad-annotation.rs:45:41 | -LL | #[rustc_on_unimplemented(message="x", on(desugared, message="y"))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected value here +LL | #[rustc_on_unimplemented(message = "x", on(desugared, message = "y"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected value here | - = note: eg `#[rustc_on_unimplemented(message="foo")]` + = note: e.g. `#[rustc_on_unimplemented(message="foo")]` error[E0232]: empty `on`-clause in `#[rustc_on_unimplemented]` - --> $DIR/bad-annotation.rs:48:26 + --> $DIR/bad-annotation.rs:51:26 + | +LL | #[rustc_on_unimplemented(on(), message = "y")] + | ^^^^ empty `on`-clause here + +error[E0232]: this attribute must have a value + --> $DIR/bad-annotation.rs:56:26 + | +LL | #[rustc_on_unimplemented(on = "x", message = "y")] + | ^^^^^^^^ expected value here + | + = note: e.g. `#[rustc_on_unimplemented(message="foo")]` + +error[E0232]: this attribute must have a value + --> $DIR/bad-annotation.rs:65:46 + | +LL | #[rustc_on_unimplemented(on(from_desugaring, on(from_desugaring, message = "x")), message = "y")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected value here + | + = note: e.g. `#[rustc_on_unimplemented(message="foo")]` + +error[E0232]: literals inside `on`-clauses are not supported + --> $DIR/bad-annotation.rs:71:29 | -LL | #[rustc_on_unimplemented(on(), message="y")] - | ^^^^ empty on-clause here +LL | #[rustc_on_unimplemented(on("y", message = "y"))] + | ^^^ unexpected literal here -error[E0232]: this attribute must have a valid value - --> $DIR/bad-annotation.rs:52:26 +error[E0232]: literals inside `on`-clauses are not supported + --> $DIR/bad-annotation.rs:76:29 | -LL | #[rustc_on_unimplemented(on="x", message="y")] - | ^^^^^^ expected value here +LL | #[rustc_on_unimplemented(on(42, message = "y"))] + | ^^ unexpected literal here + +error[E0232]: expected a single predicate in `not(..)` + --> $DIR/bad-annotation.rs:81:33 | - = note: eg `#[rustc_on_unimplemented(message="foo")]` +LL | #[rustc_on_unimplemented(on(not(a, b), message = "y"))] + | ^^^^ unexpected quantity of predicates here -error[E0232]: this attribute must have a valid value - --> $DIR/bad-annotation.rs:59:40 +error[E0232]: expected a single predicate in `not(..)` + --> $DIR/bad-annotation.rs:86:29 | -LL | #[rustc_on_unimplemented(on(desugared, on(desugared, message="x")), message="y")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected value here +LL | #[rustc_on_unimplemented(on(not(), message = "y"))] + | ^^^^^ unexpected quantity of predicates here + +error[E0232]: expected an identifier inside this `on`-clause + --> $DIR/bad-annotation.rs:91:29 + | +LL | #[rustc_on_unimplemented(on(thing::What, message = "y"))] + | ^^^^^^^^^^^ expected an identifier here, not `thing::What` + +error[E0232]: expected an identifier inside this `on`-clause + --> $DIR/bad-annotation.rs:96:29 + | +LL | #[rustc_on_unimplemented(on(thing::What = "value", message = "y"))] + | ^^^^^^^^^^^ expected an identifier here, not `thing::What` + +error[E0232]: this predicate is invalid + --> $DIR/bad-annotation.rs:101:29 + | +LL | #[rustc_on_unimplemented(on(aaaaaaaaaaaaaa(a, b), message = "y"))] + | ^^^^^^^^^^^^^^ expected one of `any`, `all` or `not` here, not `aaaaaaaaaaaaaa` + +error[E0232]: invalid flag in `on`-clause + --> $DIR/bad-annotation.rs:106:29 | - = note: eg `#[rustc_on_unimplemented(message="foo")]` +LL | #[rustc_on_unimplemented(on(something, message = "y"))] + | ^^^^^^^^^ expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `something` -error: aborting due to 10 previous errors +error: aborting due to 18 previous errors Some errors have detailed explanations: E0230, E0231, E0232. For more information about an error, try `rustc --explain E0230`. diff --git a/tests/ui/on-unimplemented/expected-comma-found-token.rs b/tests/ui/on-unimplemented/expected-comma-found-token.rs index 8fb34f21152ab..d60ab3341fd6d 100644 --- a/tests/ui/on-unimplemented/expected-comma-found-token.rs +++ b/tests/ui/on-unimplemented/expected-comma-found-token.rs @@ -1,7 +1,6 @@ -// Tests that two closures cannot simultaneously have mutable -// access to the variable, whether that mutable access be used -// for direct assignment or for taking mutable ref. Issue #6801. +//! Test for invalid MetaItem syntax in the attribute +#![crate_type = "lib"] #![feature(rustc_attrs)] #[rustc_on_unimplemented( @@ -9,5 +8,3 @@ label="the label" //~ ERROR expected `,`, found `label` )] trait T {} - -fn main() { } diff --git a/tests/ui/on-unimplemented/expected-comma-found-token.stderr b/tests/ui/on-unimplemented/expected-comma-found-token.stderr index 7c0874e36a6a9..2717100a1dc64 100644 --- a/tests/ui/on-unimplemented/expected-comma-found-token.stderr +++ b/tests/ui/on-unimplemented/expected-comma-found-token.stderr @@ -1,5 +1,5 @@ error: expected `,`, found `label` - --> $DIR/expected-comma-found-token.rs:9:5 + --> $DIR/expected-comma-found-token.rs:8:5 | LL | message="the message" | - expected `,` diff --git a/tests/ui/on-unimplemented/impl-substs.rs b/tests/ui/on-unimplemented/impl-substs.rs deleted file mode 100644 index fe9c50ec3d4a2..0000000000000 --- a/tests/ui/on-unimplemented/impl-substs.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![feature(rustc_attrs)] - -trait Foo { - fn foo(self); -} - -#[rustc_on_unimplemented = "an impl did not match: {A} {B} {C}"] -impl Foo for (A, B, C) { - fn foo(self) {} -} - -fn main() { - Foo::::foo((1i32, 1i32, 1i32)); - //~^ ERROR the trait bound `(i32, i32, i32): Foo` is not satisfied -} diff --git a/tests/ui/on-unimplemented/impl-substs.stderr b/tests/ui/on-unimplemented/impl-substs.stderr deleted file mode 100644 index b85d45eba5bbc..0000000000000 --- a/tests/ui/on-unimplemented/impl-substs.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0277]: the trait bound `(i32, i32, i32): Foo` is not satisfied - --> $DIR/impl-substs.rs:13:23 - | -LL | Foo::::foo((1i32, 1i32, 1i32)); - | ----------------- ^^^^^^^^^^^^^^^^^^ an impl did not match: usize _ _ - | | - | required by a bound introduced by this call - | - = help: the trait `Foo` is not implemented for `(i32, i32, i32)` - but trait `Foo` is implemented for it - = help: for that trait implementation, expected `i32`, found `usize` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/on-unimplemented/issue-104140.rs b/tests/ui/on-unimplemented/issue-104140.rs deleted file mode 100644 index ade3f727004ab..0000000000000 --- a/tests/ui/on-unimplemented/issue-104140.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![feature(rustc_attrs)] - -trait Foo {} - -#[rustc_on_unimplemented] //~ ERROR malformed `rustc_on_unimplemented` attribute input -impl Foo for u32 {} - -fn main() {} diff --git a/tests/ui/on-unimplemented/issue-104140.stderr b/tests/ui/on-unimplemented/issue-104140.stderr deleted file mode 100644 index 3c317135dd43a..0000000000000 --- a/tests/ui/on-unimplemented/issue-104140.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: malformed `rustc_on_unimplemented` attribute input - --> $DIR/issue-104140.rs:5:1 - | -LL | #[rustc_on_unimplemented] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the following are the possible correct uses - | -LL | #[rustc_on_unimplemented = "message"] - | +++++++++++ -LL | #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")] - | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -error: aborting due to 1 previous error - diff --git a/tests/ui/on-unimplemented/multiple-impls.rs b/tests/ui/on-unimplemented/multiple-impls.rs deleted file mode 100644 index b74957ebcd406..0000000000000 --- a/tests/ui/on-unimplemented/multiple-impls.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Test if the on_unimplemented message override works - -#![feature(rustc_attrs)] - - -struct Foo(T); -struct Bar(T); - -#[rustc_on_unimplemented = "trait message"] -trait Index { - type Output: ?Sized; - fn index(&self, index: Idx) -> &Self::Output; -} - -#[rustc_on_unimplemented = "on impl for Foo"] -impl Index> for [i32] { - type Output = i32; - fn index(&self, _index: Foo) -> &i32 { - loop {} - } -} - -#[rustc_on_unimplemented = "on impl for Bar"] -impl Index> for [i32] { - type Output = i32; - fn index(&self, _index: Bar) -> &i32 { - loop {} - } -} - - -fn main() { - Index::index(&[] as &[i32], 2u32); - //~^ ERROR E0277 - //~| ERROR E0277 - Index::index(&[] as &[i32], Foo(2u32)); - //~^ ERROR E0277 - //~| ERROR E0277 - Index::index(&[] as &[i32], Bar(2u32)); - //~^ ERROR E0277 - //~| ERROR E0277 -} diff --git a/tests/ui/on-unimplemented/multiple-impls.stderr b/tests/ui/on-unimplemented/multiple-impls.stderr deleted file mode 100644 index ba4e43ff3594f..0000000000000 --- a/tests/ui/on-unimplemented/multiple-impls.stderr +++ /dev/null @@ -1,75 +0,0 @@ -error[E0277]: the trait bound `[i32]: Index` is not satisfied - --> $DIR/multiple-impls.rs:33:33 - | -LL | Index::index(&[] as &[i32], 2u32); - | ------------ ^^^^ trait message - | | - | required by a bound introduced by this call - | - = help: the trait `Index` is not implemented for `[i32]` - = help: the following other types implement trait `Index`: - `[i32]` implements `Index>` - `[i32]` implements `Index>` - -error[E0277]: the trait bound `[i32]: Index>` is not satisfied - --> $DIR/multiple-impls.rs:36:33 - | -LL | Index::index(&[] as &[i32], Foo(2u32)); - | ------------ ^^^^^^^^^ on impl for Foo - | | - | required by a bound introduced by this call - | - = help: the trait `Index>` is not implemented for `[i32]` - = help: the following other types implement trait `Index`: - `[i32]` implements `Index>` - `[i32]` implements `Index>` - -error[E0277]: the trait bound `[i32]: Index>` is not satisfied - --> $DIR/multiple-impls.rs:39:33 - | -LL | Index::index(&[] as &[i32], Bar(2u32)); - | ------------ ^^^^^^^^^ on impl for Bar - | | - | required by a bound introduced by this call - | - = help: the trait `Index>` is not implemented for `[i32]` - = help: the following other types implement trait `Index`: - `[i32]` implements `Index>` - `[i32]` implements `Index>` - -error[E0277]: the trait bound `[i32]: Index` is not satisfied - --> $DIR/multiple-impls.rs:33:5 - | -LL | Index::index(&[] as &[i32], 2u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait message - | - = help: the trait `Index` is not implemented for `[i32]` - = help: the following other types implement trait `Index`: - `[i32]` implements `Index>` - `[i32]` implements `Index>` - -error[E0277]: the trait bound `[i32]: Index>` is not satisfied - --> $DIR/multiple-impls.rs:36:5 - | -LL | Index::index(&[] as &[i32], Foo(2u32)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ on impl for Foo - | - = help: the trait `Index>` is not implemented for `[i32]` - = help: the following other types implement trait `Index`: - `[i32]` implements `Index>` - `[i32]` implements `Index>` - -error[E0277]: the trait bound `[i32]: Index>` is not satisfied - --> $DIR/multiple-impls.rs:39:5 - | -LL | Index::index(&[] as &[i32], Bar(2u32)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ on impl for Bar - | - = help: the trait `Index>` is not implemented for `[i32]` - = help: the following other types implement trait `Index`: - `[i32]` implements `Index>` - `[i32]` implements `Index>` - -error: aborting due to 6 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/on-unimplemented/on-impl.rs b/tests/ui/on-unimplemented/on-impl.rs deleted file mode 100644 index ab3e67d01fe44..0000000000000 --- a/tests/ui/on-unimplemented/on-impl.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Test if the on_unimplemented message override works - -#![feature(rustc_attrs)] - - -#[rustc_on_unimplemented = "invalid"] -trait Index { - type Output: ?Sized; - fn index(&self, index: Idx) -> &Self::Output; -} - -#[rustc_on_unimplemented = "a usize is required to index into a slice"] -impl Index for [i32] { - type Output = i32; - fn index(&self, index: usize) -> &i32 { - &self[index] - } -} - - -fn main() { - Index::::index(&[1, 2, 3] as &[i32], 2u32); - //~^ ERROR E0277 - //~| ERROR E0277 -} diff --git a/tests/ui/on-unimplemented/on-impl.stderr b/tests/ui/on-unimplemented/on-impl.stderr deleted file mode 100644 index 5e7e2c4ea7744..0000000000000 --- a/tests/ui/on-unimplemented/on-impl.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0277]: the trait bound `[i32]: Index` is not satisfied - --> $DIR/on-impl.rs:22:47 - | -LL | Index::::index(&[1, 2, 3] as &[i32], 2u32); - | ------------------- ^^^^ a usize is required to index into a slice - | | - | required by a bound introduced by this call - | - = help: the trait `Index` is not implemented for `[i32]` - but trait `Index` is implemented for it - = help: for that trait implementation, expected `usize`, found `u32` - -error[E0277]: the trait bound `[i32]: Index` is not satisfied - --> $DIR/on-impl.rs:22:5 - | -LL | Index::::index(&[1, 2, 3] as &[i32], 2u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a usize is required to index into a slice - | - = help: the trait `Index` is not implemented for `[i32]` - but trait `Index` is implemented for it - = help: for that trait implementation, expected `usize`, found `u32` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/on-unimplemented/slice-index.stderr b/tests/ui/on-unimplemented/slice-index.stderr index 0a9ebe3f088e0..e011826bc8f7b 100644 --- a/tests/ui/on-unimplemented/slice-index.stderr +++ b/tests/ui/on-unimplemented/slice-index.stderr @@ -5,8 +5,9 @@ LL | x[1i32]; | ^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[i32]>` is not implemented for `i32` - but it is implemented for `usize` - = help: for that trait implementation, expected `usize`, found `i32` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `[i32]` to implement `Index` error[E0277]: the type `[i32]` cannot be indexed by `RangeTo` @@ -17,6 +18,7 @@ LL | x[..1i32]; | = help: the trait `SliceIndex<[i32]>` is not implemented for `RangeTo` = help: the following other types implement trait `SliceIndex`: + `RangeTo` implements `SliceIndex` `RangeTo` implements `SliceIndex<[T]>` `RangeTo` implements `SliceIndex` = note: required for `[i32]` to implement `Index>` diff --git a/tests/ui/on-unimplemented/use_self_no_underscore.rs b/tests/ui/on-unimplemented/use_self_no_underscore.rs new file mode 100644 index 0000000000000..045ef1a5d3ff0 --- /dev/null +++ b/tests/ui/on-unimplemented/use_self_no_underscore.rs @@ -0,0 +1,14 @@ +#![feature(rustc_attrs)] + +#[rustc_on_unimplemented(on( + all(A = "{integer}", any(Self = "[{integral}; _]",)), + message = "an array of type `{Self}` cannot be built directly from an iterator", +))] +pub trait FromIterator: Sized { + fn from_iter>(iter: T) -> Self; +} +fn main() { + let iter = 0..42_8; + let x: [u8; 8] = FromIterator::from_iter(iter); + //~^ ERROR an array of type `[u8; 8]` cannot be built directly from an iterator +} diff --git a/tests/ui/on-unimplemented/use_self_no_underscore.stderr b/tests/ui/on-unimplemented/use_self_no_underscore.stderr new file mode 100644 index 0000000000000..d01aee3485f4c --- /dev/null +++ b/tests/ui/on-unimplemented/use_self_no_underscore.stderr @@ -0,0 +1,15 @@ +error[E0277]: an array of type `[u8; 8]` cannot be built directly from an iterator + --> $DIR/use_self_no_underscore.rs:12:22 + | +LL | let x: [u8; 8] = FromIterator::from_iter(iter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromIterator<{integer}>` is not implemented for `[u8; 8]` + | +help: this trait has no implementations, consider adding one + --> $DIR/use_self_no_underscore.rs:7:1 + | +LL | pub trait FromIterator: Sized { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/optimization-remark.rs b/tests/ui/optimization-remark.rs index ebcf3b40ab250..165fc63c0076a 100644 --- a/tests/ui/optimization-remark.rs +++ b/tests/ui/optimization-remark.rs @@ -12,9 +12,8 @@ // //@ [merge1] compile-flags: -Cremark=all -Cremark=giraffe //@ [merge2] compile-flags: -Cremark=inline -Cremark=giraffe -// -//@ error-pattern: inline (missed): 'f' not inlined into 'g' //@ dont-check-compiler-stderr +//@ dont-require-annotations: NOTE #[no_mangle] #[inline(never)] @@ -25,3 +24,5 @@ pub fn f() { pub fn g() { f(); } + +//~? NOTE inline (missed): 'f' not inlined into 'g' diff --git a/tests/ui/or-patterns/fn-param-wrap-parens.fixed b/tests/ui/or-patterns/fn-param-wrap-parens.fixed index 7b0bbd04d9784..608f826390da6 100644 --- a/tests/ui/or-patterns/fn-param-wrap-parens.fixed +++ b/tests/ui/or-patterns/fn-param-wrap-parens.fixed @@ -9,5 +9,5 @@ fn main() {} enum E { A, B } use E::*; -#[cfg(FALSE)] -fn fun1((A | B): E) {} //~ ERROR top-level or-patterns are not allowed +#[cfg(false)] +fn fun1((A | B): E) {} //~ ERROR function parameters require top-level or-patterns in parentheses diff --git a/tests/ui/or-patterns/fn-param-wrap-parens.rs b/tests/ui/or-patterns/fn-param-wrap-parens.rs index dadbb8a906a7b..d6fe7e9bf5860 100644 --- a/tests/ui/or-patterns/fn-param-wrap-parens.rs +++ b/tests/ui/or-patterns/fn-param-wrap-parens.rs @@ -9,5 +9,5 @@ fn main() {} enum E { A, B } use E::*; -#[cfg(FALSE)] -fn fun1(A | B: E) {} //~ ERROR top-level or-patterns are not allowed +#[cfg(false)] +fn fun1(A | B: E) {} //~ ERROR function parameters require top-level or-patterns in parentheses diff --git a/tests/ui/or-patterns/fn-param-wrap-parens.stderr b/tests/ui/or-patterns/fn-param-wrap-parens.stderr index da2832ef1ae41..e0307b44e8386 100644 --- a/tests/ui/or-patterns/fn-param-wrap-parens.stderr +++ b/tests/ui/or-patterns/fn-param-wrap-parens.stderr @@ -1,4 +1,4 @@ -error: top-level or-patterns are not allowed in function parameters +error: function parameters require top-level or-patterns in parentheses --> $DIR/fn-param-wrap-parens.rs:13:9 | LL | fn fun1(A | B: E) {} diff --git a/tests/ui/or-patterns/inner-or-pat.rs b/tests/ui/or-patterns/inner-or-pat.rs index 4d136de005357..6c7968df7265c 100644 --- a/tests/ui/or-patterns/inner-or-pat.rs +++ b/tests/ui/or-patterns/inner-or-pat.rs @@ -49,7 +49,7 @@ fn hey() { match x { x @ ("foo" | "bar") | (x @ "red" | (x @ "blue" | "red")) => { - //[or4]~^ variable `x` is not bound in all patterns + //[or4]~^ ERROR variable `x` is not bound in all patterns } _ => (), } diff --git a/tests/ui/or-patterns/lazy-and-or.rs b/tests/ui/or-patterns/lazy-and-or.rs new file mode 100644 index 0000000000000..3d69553132b98 --- /dev/null +++ b/tests/ui/or-patterns/lazy-and-or.rs @@ -0,0 +1,25 @@ +//@ run-pass +// This test verifies the short-circuiting behavior of logical operators `||` and `&&`. +// It ensures that the right-hand expression is not evaluated when the left-hand +// expression is sufficient to determine the result. + +fn would_panic_if_called(x: &mut isize) -> bool { + *x += 1; + assert!(false, "This function should never be called due to short-circuiting"); + false +} + +fn main() { + let x = 1 == 2 || 3 == 3; + assert!(x); + + let mut y: isize = 10; + println!("Result of short-circuit: {}", x || would_panic_if_called(&mut y)); + assert_eq!(y, 10, "y should remain 10 if short-circuiting works correctly"); + + if true && x { + assert!(true); + } else { + assert!(false, "This branch should not be reached"); + } +} diff --git a/tests/ui/or-patterns/nested-undelimited-precedence.rs b/tests/ui/or-patterns/nested-undelimited-precedence.rs index 047836203575f..73f72cb3f8616 100644 --- a/tests/ui/or-patterns/nested-undelimited-precedence.rs +++ b/tests/ui/or-patterns/nested-undelimited-precedence.rs @@ -17,7 +17,7 @@ fn foo() { let b @ (A | B): E = A; let b @ A | B: E = A; //~ERROR `b` is not bound in all patterns - //~^ ERROR top-level or-patterns are not allowed + //~^ ERROR `let` bindings require top-level or-patterns in parentheses } enum F { @@ -32,13 +32,13 @@ fn bar() { let (A(x) | B(x)): F = A(3); let &A(_) | B(_): F = A(3); //~ERROR mismatched types - //~^ ERROR top-level or-patterns are not allowed + //~^ ERROR `let` bindings require top-level or-patterns in parentheses let &&A(_) | B(_): F = A(3); //~ERROR mismatched types - //~^ ERROR top-level or-patterns are not allowed + //~^ ERROR `let` bindings require top-level or-patterns in parentheses let &mut A(_) | B(_): F = A(3); //~ERROR mismatched types - //~^ ERROR top-level or-patterns are not allowed + //~^ ERROR `let` bindings require top-level or-patterns in parentheses let &&mut A(_) | B(_): F = A(3); //~ERROR mismatched types - //~^ ERROR top-level or-patterns are not allowed + //~^ ERROR `let` bindings require top-level or-patterns in parentheses } fn main() {} diff --git a/tests/ui/or-patterns/nested-undelimited-precedence.stderr b/tests/ui/or-patterns/nested-undelimited-precedence.stderr index f16d83ecaea93..0835ca1929f15 100644 --- a/tests/ui/or-patterns/nested-undelimited-precedence.stderr +++ b/tests/ui/or-patterns/nested-undelimited-precedence.stderr @@ -1,4 +1,4 @@ -error: top-level or-patterns are not allowed in `let` bindings +error: `let` bindings require top-level or-patterns in parentheses --> $DIR/nested-undelimited-precedence.rs:19:9 | LL | let b @ A | B: E = A; @@ -9,7 +9,7 @@ help: wrap the pattern in parentheses LL | let (b @ A | B): E = A; | + + -error: top-level or-patterns are not allowed in `let` bindings +error: `let` bindings require top-level or-patterns in parentheses --> $DIR/nested-undelimited-precedence.rs:34:9 | LL | let &A(_) | B(_): F = A(3); @@ -20,7 +20,7 @@ help: wrap the pattern in parentheses LL | let (&A(_) | B(_)): F = A(3); | + + -error: top-level or-patterns are not allowed in `let` bindings +error: `let` bindings require top-level or-patterns in parentheses --> $DIR/nested-undelimited-precedence.rs:36:9 | LL | let &&A(_) | B(_): F = A(3); @@ -31,7 +31,7 @@ help: wrap the pattern in parentheses LL | let (&&A(_) | B(_)): F = A(3); | + + -error: top-level or-patterns are not allowed in `let` bindings +error: `let` bindings require top-level or-patterns in parentheses --> $DIR/nested-undelimited-precedence.rs:38:9 | LL | let &mut A(_) | B(_): F = A(3); @@ -42,7 +42,7 @@ help: wrap the pattern in parentheses LL | let (&mut A(_) | B(_)): F = A(3); | + + -error: top-level or-patterns are not allowed in `let` bindings +error: `let` bindings require top-level or-patterns in parentheses --> $DIR/nested-undelimited-precedence.rs:40:9 | LL | let &&mut A(_) | B(_): F = A(3); diff --git a/tests/ui/or-patterns/or-patterns-syntactic-fail.rs b/tests/ui/or-patterns/or-patterns-syntactic-fail.rs index 23dbb57cbcf0f..bc4babe709b70 100644 --- a/tests/ui/or-patterns/or-patterns-syntactic-fail.rs +++ b/tests/ui/or-patterns/or-patterns-syntactic-fail.rs @@ -16,18 +16,18 @@ fn no_top_level_or_patterns() { fn no_top_level_or_patterns_2() { // ...and for now neither do we allow or-patterns at the top level of functions. fn fun1(A | B: E) {} - //~^ ERROR top-level or-patterns are not allowed + //~^ ERROR function parameters require top-level or-patterns in parentheses fn fun2(| A | B: E) {} - //~^ ERROR top-level or-patterns are not allowed + //~^ ERROR function parameters require top-level or-patterns in parentheses // We don't allow top-level or-patterns before type annotation in let-statements because we // want to reserve this syntactic space for possible future type ascription. let A | B: E = A; - //~^ ERROR top-level or-patterns are not allowed + //~^ ERROR `let` bindings require top-level or-patterns in parentheses let | A | B: E = A; - //~^ ERROR top-level or-patterns are not allowed + //~^ ERROR `let` bindings require top-level or-patterns in parentheses let (A | B): E = A; // ok -- wrapped in parens } diff --git a/tests/ui/or-patterns/or-patterns-syntactic-fail.stderr b/tests/ui/or-patterns/or-patterns-syntactic-fail.stderr index 5608138078fbb..f6b7d427bd60e 100644 --- a/tests/ui/or-patterns/or-patterns-syntactic-fail.stderr +++ b/tests/ui/or-patterns/or-patterns-syntactic-fail.stderr @@ -6,13 +6,12 @@ LL | let _ = |A | B: E| (); | | | while parsing the body of this closure | - = note: type ascription syntax has been removed, see issue #101728 help: you might have meant to open the body of the closure | LL | let _ = |A | { B: E| (); | + -error: top-level or-patterns are not allowed in function parameters +error: function parameters require top-level or-patterns in parentheses --> $DIR/or-patterns-syntactic-fail.rs:18:13 | LL | fn fun1(A | B: E) {} @@ -23,7 +22,7 @@ help: wrap the pattern in parentheses LL | fn fun1((A | B): E) {} | + + -error: top-level or-patterns are not allowed in function parameters +error: function parameters require top-level or-patterns in parentheses --> $DIR/or-patterns-syntactic-fail.rs:21:13 | LL | fn fun2(| A | B: E) {} @@ -34,7 +33,7 @@ help: wrap the pattern in parentheses LL | fn fun2((| A | B): E) {} | + + -error: top-level or-patterns are not allowed in `let` bindings +error: `let` bindings require top-level or-patterns in parentheses --> $DIR/or-patterns-syntactic-fail.rs:26:9 | LL | let A | B: E = A; @@ -45,7 +44,7 @@ help: wrap the pattern in parentheses LL | let (A | B): E = A; | + + -error: top-level or-patterns are not allowed in `let` bindings +error: `let` bindings require top-level or-patterns in parentheses --> $DIR/or-patterns-syntactic-fail.rs:29:9 | LL | let | A | B: E = A; diff --git a/tests/ui/or-patterns/or-patterns-syntactic-pass.rs b/tests/ui/or-patterns/or-patterns-syntactic-pass.rs index 6a8d0a5adb490..6fd5840e801a9 100644 --- a/tests/ui/or-patterns/or-patterns-syntactic-pass.rs +++ b/tests/ui/or-patterns/or-patterns-syntactic-pass.rs @@ -18,7 +18,7 @@ accept_pat!([p | q]); // Non-macro tests: -#[cfg(FALSE)] +#[cfg(false)] fn or_patterns() { // Top level of `let`: let (| A | B); diff --git a/tests/ui/or-patterns/remove-leading-vert.fixed b/tests/ui/or-patterns/remove-leading-vert.fixed index 3ec815c846843..2851b8f18c54a 100644 --- a/tests/ui/or-patterns/remove-leading-vert.fixed +++ b/tests/ui/or-patterns/remove-leading-vert.fixed @@ -6,9 +6,9 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn leading() { - fn fun1( A: E) {} //~ ERROR top-level or-patterns are not allowed + fn fun1( A: E) {} //~ ERROR function parameters require top-level or-patterns in parentheses fn fun2( A: E) {} //~ ERROR unexpected `||` before function parameter let ( | A): E; let ( | A): (E); //~ ERROR unexpected token `||` in pattern @@ -21,7 +21,7 @@ fn leading() { let NS { f: | A }: NS; //~ ERROR unexpected token `||` in pattern } -#[cfg(FALSE)] +#[cfg(false)] fn trailing() { let ( A ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern let (a ,): (E,); //~ ERROR a trailing `|` is not allowed in an or-pattern diff --git a/tests/ui/or-patterns/remove-leading-vert.rs b/tests/ui/or-patterns/remove-leading-vert.rs index 2aeeb0e979f6d..1e1dbfbc6e665 100644 --- a/tests/ui/or-patterns/remove-leading-vert.rs +++ b/tests/ui/or-patterns/remove-leading-vert.rs @@ -6,9 +6,9 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn leading() { - fn fun1( | A: E) {} //~ ERROR top-level or-patterns are not allowed + fn fun1( | A: E) {} //~ ERROR function parameters require top-level or-patterns in parentheses fn fun2( || A: E) {} //~ ERROR unexpected `||` before function parameter let ( | A): E; let ( || A): (E); //~ ERROR unexpected token `||` in pattern @@ -21,7 +21,7 @@ fn leading() { let NS { f: || A }: NS; //~ ERROR unexpected token `||` in pattern } -#[cfg(FALSE)] +#[cfg(false)] fn trailing() { let ( A | ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern let (a |,): (E,); //~ ERROR a trailing `|` is not allowed in an or-pattern diff --git a/tests/ui/or-patterns/remove-leading-vert.stderr b/tests/ui/or-patterns/remove-leading-vert.stderr index b92fcb89a404e..0323c64f04231 100644 --- a/tests/ui/or-patterns/remove-leading-vert.stderr +++ b/tests/ui/or-patterns/remove-leading-vert.stderr @@ -1,4 +1,4 @@ -error: top-level or-patterns are not allowed in function parameters +error: function parameters require top-level or-patterns in parentheses --> $DIR/remove-leading-vert.rs:11:14 | LL | fn fun1( | A: E) {} diff --git a/tests/ui/or-patterns/while-parsing-this-or-pattern.rs b/tests/ui/or-patterns/while-parsing-this-or-pattern.rs index b9bfb8638b2ce..b034a52ac9451 100644 --- a/tests/ui/or-patterns/while-parsing-this-or-pattern.rs +++ b/tests/ui/or-patterns/while-parsing-this-or-pattern.rs @@ -3,7 +3,7 @@ fn main() { match Some(42) { Some(42) | .=. => {} //~ ERROR expected pattern, found `.` - //~^ while parsing this or-pattern starting here + //~^ NOTE while parsing this or-pattern starting here //~| NOTE expected pattern } } diff --git a/tests/ui/packed/packed-struct-generic-transmute.rs b/tests/ui/packed/packed-struct-generic-transmute.rs index ed655a1d483d4..66972633d8009 100644 --- a/tests/ui/packed/packed-struct-generic-transmute.rs +++ b/tests/ui/packed/packed-struct-generic-transmute.rs @@ -3,8 +3,6 @@ // the error points to the start of the file, not the line with the // transmute -//@ error-pattern: cannot transmute between types of different sizes, or dependently-sized types - use std::mem; #[repr(packed)] @@ -22,6 +20,7 @@ fn main() { let foo = Foo { bar: [1u8, 2, 3, 4, 5], baz: 10i32 }; unsafe { let oof: Oof<[u8; 5], i32> = mem::transmute(foo); + //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types println!("{:?} {:?}", &oof.rab[..], oof.zab); } } diff --git a/tests/ui/packed/packed-struct-generic-transmute.stderr b/tests/ui/packed/packed-struct-generic-transmute.stderr index e91f498842986..983742b78be18 100644 --- a/tests/ui/packed/packed-struct-generic-transmute.stderr +++ b/tests/ui/packed/packed-struct-generic-transmute.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/packed-struct-generic-transmute.rs:24:38 + --> $DIR/packed-struct-generic-transmute.rs:22:38 | LL | let oof: Oof<[u8; 5], i32> = mem::transmute(foo); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/packed/packed-struct-transmute.rs b/tests/ui/packed/packed-struct-transmute.rs index 98feeea88714f..24ac1f4ac41c4 100644 --- a/tests/ui/packed/packed-struct-transmute.rs +++ b/tests/ui/packed/packed-struct-transmute.rs @@ -4,7 +4,6 @@ // transmute //@ normalize-stderr: "\d+ bits" -> "N bits" -//@ error-pattern: cannot transmute between types of different sizes, or dependently-sized types use std::mem; @@ -24,6 +23,7 @@ fn main() { let foo = Foo { bar: 1, baz: 10 }; unsafe { let oof: Oof = mem::transmute(foo); + //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types println!("{:?}", oof); } } diff --git a/tests/ui/packed/packed-struct-transmute.stderr b/tests/ui/packed/packed-struct-transmute.stderr index 4d75820e9449c..c5f556f6d0c17 100644 --- a/tests/ui/packed/packed-struct-transmute.stderr +++ b/tests/ui/packed/packed-struct-transmute.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/packed-struct-transmute.rs:26:24 + --> $DIR/packed-struct-transmute.rs:25:24 | LL | let oof: Oof = mem::transmute(foo); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/panic-handler/panic-handler-bad-signature-1.rs b/tests/ui/panic-handler/panic-handler-bad-signature-1.rs index 8f42f3a8897fc..71911afaa8430 100644 --- a/tests/ui/panic-handler/panic-handler-bad-signature-1.rs +++ b/tests/ui/panic-handler/panic-handler-bad-signature-1.rs @@ -7,4 +7,4 @@ use core::panic::PanicInfo; #[panic_handler] fn panic(info: PanicInfo) -> () {} -//~^ `#[panic_handler]` function has wrong type [E0308] +//~^ ERROR `#[panic_handler]` function has wrong type [E0308] diff --git a/tests/ui/panic-handler/panic-handler-bad-signature-2.rs b/tests/ui/panic-handler/panic-handler-bad-signature-2.rs index 79ad4598e10c6..9c0130eff21f7 100644 --- a/tests/ui/panic-handler/panic-handler-bad-signature-2.rs +++ b/tests/ui/panic-handler/panic-handler-bad-signature-2.rs @@ -7,7 +7,7 @@ use core::panic::PanicInfo; #[panic_handler] fn panic(info: &'static PanicInfo) -> ! -//~^ #[panic_handler]` function has wrong type [E0308] +//~^ ERROR #[panic_handler]` function has wrong type [E0308] { loop {} } diff --git a/tests/ui/panic-handler/panic-handler-bad-signature-3.rs b/tests/ui/panic-handler/panic-handler-bad-signature-3.rs index 1c6e2e2ff492c..14c8c7c63b70c 100644 --- a/tests/ui/panic-handler/panic-handler-bad-signature-3.rs +++ b/tests/ui/panic-handler/panic-handler-bad-signature-3.rs @@ -6,6 +6,6 @@ use core::panic::PanicInfo; #[panic_handler] -fn panic() -> ! { //~ #[panic_handler]` function has wrong type [E0308] +fn panic() -> ! { //~ ERROR #[panic_handler]` function has wrong type [E0308] loop {} } diff --git a/tests/ui/panic-handler/panic-handler-bad-signature-5.rs b/tests/ui/panic-handler/panic-handler-bad-signature-5.rs index d7ee8f25b1141..a2a0e46ec6829 100644 --- a/tests/ui/panic-handler/panic-handler-bad-signature-5.rs +++ b/tests/ui/panic-handler/panic-handler-bad-signature-5.rs @@ -7,7 +7,7 @@ use core::panic::PanicInfo; #[panic_handler] fn panic(info: &PanicInfo<'static>) -> ! -//~^ #[panic_handler]` function has wrong type [E0308] +//~^ ERROR #[panic_handler]` function has wrong type [E0308] { loop {} } diff --git a/tests/ui/panic-handler/panic-handler-missing.rs b/tests/ui/panic-handler/panic-handler-missing.rs index 09fbd9a69cfd0..ab617f93a9919 100644 --- a/tests/ui/panic-handler/panic-handler-missing.rs +++ b/tests/ui/panic-handler/panic-handler-missing.rs @@ -1,5 +1,4 @@ //@ dont-check-compiler-stderr -//@ error-pattern: `#[panic_handler]` function required, but not found #![feature(lang_items)] #![no_main] @@ -7,3 +6,5 @@ #[lang = "eh_personality"] fn eh() {} + +//~? ERROR `#[panic_handler]` function required, but not found diff --git a/tests/ui/panic-handler/panic-handler-std.rs b/tests/ui/panic-handler/panic-handler-std.rs index 4eb05b5365feb..f6a4b60461cee 100644 --- a/tests/ui/panic-handler/panic-handler-std.rs +++ b/tests/ui/panic-handler/panic-handler-std.rs @@ -1,12 +1,11 @@ //@ normalize-stderr: "loaded from .*libstd-.*.rlib" -> "loaded from SYSROOT/libstd-*.rlib" -//@ error-pattern: found duplicate lang item `panic_impl` extern crate core; use core::panic::PanicInfo; #[panic_handler] -fn panic(info: PanicInfo) -> ! { +fn panic(info: PanicInfo) -> ! { //~ ERROR found duplicate lang item `panic_impl` loop {} } diff --git a/tests/ui/panic-handler/panic-handler-std.stderr b/tests/ui/panic-handler/panic-handler-std.stderr index caae16118ef7f..48c216ce27ec4 100644 --- a/tests/ui/panic-handler/panic-handler-std.stderr +++ b/tests/ui/panic-handler/panic-handler-std.stderr @@ -1,5 +1,5 @@ error[E0152]: found duplicate lang item `panic_impl` - --> $DIR/panic-handler-std.rs:9:1 + --> $DIR/panic-handler-std.rs:8:1 | LL | / fn panic(info: PanicInfo) -> ! { LL | | loop {} diff --git a/tests/ui/panic-handler/panic-handler-wrong-location.rs b/tests/ui/panic-handler/panic-handler-wrong-location.rs index 49685ee45926e..8fff7067136e2 100644 --- a/tests/ui/panic-handler/panic-handler-wrong-location.rs +++ b/tests/ui/panic-handler/panic-handler-wrong-location.rs @@ -4,5 +4,6 @@ #![no_main] #[panic_handler] //~ ERROR `panic_impl` lang item must be applied to a function -#[no_mangle] static X: u32 = 42; + +//~? ERROR `#[panic_handler]` function required, but not found diff --git a/tests/ui/panic-handler/weak-lang-item.rs b/tests/ui/panic-handler/weak-lang-item.rs index 605e1bdd94b7f..cc5ccb75104be 100644 --- a/tests/ui/panic-handler/weak-lang-item.rs +++ b/tests/ui/panic-handler/weak-lang-item.rs @@ -1,12 +1,13 @@ //@ aux-build:weak-lang-items.rs -//@ error-pattern: `#[panic_handler]` function required, but not found -//@ error-pattern: unwinding panics are not supported without std //@ needs-unwind since it affects the error output //@ ignore-emscripten missing eh_catch_typeinfo lang item #![no_std] -extern crate core; +extern crate core; //~ ERROR the name `core` is defined multiple times extern crate weak_lang_items; fn main() {} + +//~? ERROR `#[panic_handler]` function required, but not found +//~? ERROR unwinding panics are not supported without std diff --git a/tests/ui/panic-handler/weak-lang-item.stderr b/tests/ui/panic-handler/weak-lang-item.stderr index 5dcb37df68921..5acd3e3187051 100644 --- a/tests/ui/panic-handler/weak-lang-item.stderr +++ b/tests/ui/panic-handler/weak-lang-item.stderr @@ -1,5 +1,5 @@ error[E0259]: the name `core` is defined multiple times - --> $DIR/weak-lang-item.rs:9:1 + --> $DIR/weak-lang-item.rs:7:1 | LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ `core` reimported here diff --git a/tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs b/tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs index 2939835b0f41e..a691ceb566b0c 100644 --- a/tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs +++ b/tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs @@ -1,10 +1,7 @@ //@ build-fail //@ compile-flags:-C panic=abort -C prefer-dynamic //@ needs-unwind -//@ ignore-musl - no dylibs here -//@ ignore-emscripten -//@ ignore-sgx no dynamic lib support -//@ error-pattern:`panic_unwind` is not compiled with this crate's panic strategy +//@ needs-crate-type: dylib // This is a test where the local crate, compiled with `panic=abort`, links to // the standard library **dynamically** which is already linked against @@ -16,3 +13,5 @@ fn main() { } + +//~? ERROR the linked panic runtime `panic_unwind` is not compiled with this crate's panic strategy `abort` diff --git a/tests/ui/panic-runtime/bad-panic-flag1.rs b/tests/ui/panic-runtime/bad-panic-flag1.rs index 82b7c2f723b4f..117935847cbd7 100644 --- a/tests/ui/panic-runtime/bad-panic-flag1.rs +++ b/tests/ui/panic-runtime/bad-panic-flag1.rs @@ -1,4 +1,5 @@ //@ compile-flags:-C panic=foo -//@ error-pattern:either `unwind` or `abort` was expected fn main() {} + +//~? ERROR incorrect value `foo` for codegen option `panic` - either `unwind` or `abort` was expected diff --git a/tests/ui/panic-runtime/bad-panic-flag2.rs b/tests/ui/panic-runtime/bad-panic-flag2.rs index 3875325deae28..b5d0442a03326 100644 --- a/tests/ui/panic-runtime/bad-panic-flag2.rs +++ b/tests/ui/panic-runtime/bad-panic-flag2.rs @@ -1,4 +1,5 @@ //@ compile-flags:-C panic -//@ error-pattern:requires either `unwind` or `abort` fn main() {} + +//~? ERROR codegen option `panic` requires either `unwind` or `abort` diff --git a/tests/ui/panic-runtime/need-abort-got-unwind.rs b/tests/ui/panic-runtime/need-abort-got-unwind.rs index 74b7edd968fab..afd0842639546 100644 --- a/tests/ui/panic-runtime/need-abort-got-unwind.rs +++ b/tests/ui/panic-runtime/need-abort-got-unwind.rs @@ -1,8 +1,9 @@ //@ build-fail //@ needs-unwind -//@ error-pattern:is incompatible with this crate's strategy of `unwind` //@ aux-build:needs-abort.rs extern crate needs_abort; fn main() {} + +//~? ERROR the crate `needs_abort` requires panic strategy `abort` which is incompatible with this crate's strategy of `unwind` diff --git a/tests/ui/panic-runtime/need-unwind-got-abort.rs b/tests/ui/panic-runtime/need-unwind-got-abort.rs index 6bc41509b6b23..bc65e9ab3d9e6 100644 --- a/tests/ui/panic-runtime/need-unwind-got-abort.rs +++ b/tests/ui/panic-runtime/need-unwind-got-abort.rs @@ -1,5 +1,4 @@ //@ build-fail -//@ error-pattern:is incompatible with this crate's strategy of `abort` //@ aux-build:needs-unwind.rs //@ compile-flags:-C panic=abort //@ no-prefer-dynamic @@ -7,3 +6,5 @@ extern crate needs_unwind; fn main() {} + +//~? ERROR the crate `needs_unwind` requires panic strategy `unwind` which is incompatible with this crate's strategy of `abort` diff --git a/tests/ui/panic-runtime/runtime-depend-on-needs-runtime.rs b/tests/ui/panic-runtime/runtime-depend-on-needs-runtime.rs index d0a82bd8507d4..eb00c071702c3 100644 --- a/tests/ui/panic-runtime/runtime-depend-on-needs-runtime.rs +++ b/tests/ui/panic-runtime/runtime-depend-on-needs-runtime.rs @@ -1,8 +1,9 @@ //@ dont-check-compiler-stderr //@ aux-build:needs-panic-runtime.rs //@ aux-build:depends.rs -//@ error-pattern:cannot depend on a crate that needs a panic runtime extern crate depends; fn main() {} + +//~? ERROR the crate `depends` cannot depend on a crate that needs a panic runtime, but it depends on `needs_panic_runtime` diff --git a/tests/ui/panic-runtime/transitive-link-a-bunch.rs b/tests/ui/panic-runtime/transitive-link-a-bunch.rs index 15557d35bc5cc..2a0b9e3fa6321 100644 --- a/tests/ui/panic-runtime/transitive-link-a-bunch.rs +++ b/tests/ui/panic-runtime/transitive-link-a-bunch.rs @@ -5,7 +5,6 @@ //@ aux-build:wants-panic-runtime-unwind.rs //@ aux-build:wants-panic-runtime-abort.rs //@ aux-build:panic-runtime-lang-items.rs -//@ error-pattern: is not compiled with this crate's panic strategy `unwind` #![no_std] #![no_main] @@ -13,3 +12,7 @@ extern crate wants_panic_runtime_unwind; extern crate wants_panic_runtime_abort; extern crate panic_runtime_lang_items; + +//~? ERROR cannot link together two panic runtimes: panic_runtime_unwind and panic_runtime_abort +//~? ERROR the linked panic runtime `panic_runtime_abort` is not compiled with this crate's panic strategy `unwind` +//~? ERROR the crate `wants_panic_runtime_abort` requires panic strategy `abort` which is incompatible with this crate's strategy of `unwind` diff --git a/tests/ui/panic-runtime/two-panic-runtimes.rs b/tests/ui/panic-runtime/two-panic-runtimes.rs index 3608dca2124c1..de76578a267fd 100644 --- a/tests/ui/panic-runtime/two-panic-runtimes.rs +++ b/tests/ui/panic-runtime/two-panic-runtimes.rs @@ -1,10 +1,19 @@ +// ignore-tidy-linelength //@ build-fail //@ dont-check-compiler-stderr -//@ error-pattern:cannot link together two panic runtimes: panic_runtime_unwind and panic_runtime_unwind2 //@ aux-build:panic-runtime-unwind.rs //@ aux-build:panic-runtime-unwind2.rs //@ aux-build:panic-runtime-lang-items.rs +// NOTE: there can be additional errors regarding trying to mix this crate if the precompiled target +// (such as `wasm32-unknown-unknown` currently unconditionally defaulting to panic=abort) panic +// strategy differs to abort, then involving a potentially-unwinding `panic_runtime_unwind` that +// uses a different panic strategy. These errors are important but not to the test intention, which +// is to check that trying to bring two panic runtimes (`panic_runtime_unwind`) and +// (`panic_runtime_unwind2`) is prohibited. As such, the additional errors are not checked in this +// test. +//@ dont-require-annotations: ERROR + #![no_std] #![no_main] @@ -13,3 +22,5 @@ extern crate panic_runtime_unwind2; extern crate panic_runtime_lang_items; fn main() {} + +//~? ERROR cannot link together two panic runtimes: panic_runtime_unwind and panic_runtime_unwind2 diff --git a/tests/ui/panic-runtime/unwind-tables-target-required.rs b/tests/ui/panic-runtime/unwind-tables-target-required.rs index 5c6ec19c16d02..ff8df284f6b11 100644 --- a/tests/ui/panic-runtime/unwind-tables-target-required.rs +++ b/tests/ui/panic-runtime/unwind-tables-target-required.rs @@ -5,7 +5,8 @@ //@ compile-flags: -C force-unwind-tables=no // //@ dont-check-compiler-stderr -//@ error-pattern: target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no` pub fn main() { } + +//~? ERROR target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no` diff --git a/tests/ui/panic-runtime/want-abort-got-unwind.rs b/tests/ui/panic-runtime/want-abort-got-unwind.rs index ad9fa52f3d44d..7a6bd011d9e4f 100644 --- a/tests/ui/panic-runtime/want-abort-got-unwind.rs +++ b/tests/ui/panic-runtime/want-abort-got-unwind.rs @@ -1,9 +1,18 @@ +// ignore-tidy-linelength //@ build-fail //@ dont-check-compiler-stderr -//@ error-pattern:is not compiled with this crate's panic strategy `abort` //@ aux-build:panic-runtime-unwind.rs //@ compile-flags:-C panic=abort +// NOTE: depending on the target's default panic strategy, there can be additional errors that +// complain about linking two panic runtimes (e.g. precompiled `panic_unwind` if target default +// panic strategy is unwind, in addition to `panic_runtime_unwind`). These additional errors will +// not be observed on targets whose default panic strategy is abort, where `panic_abort` is linked +// in instead. +//@ dont-require-annotations: ERROR + extern crate panic_runtime_unwind; fn main() {} + +//~? ERROR the linked panic runtime `panic_runtime_unwind` is not compiled with this crate's panic strategy `abort` diff --git a/tests/ui/panic-runtime/want-abort-got-unwind2.rs b/tests/ui/panic-runtime/want-abort-got-unwind2.rs index d63161db55cb0..da239a675e5bf 100644 --- a/tests/ui/panic-runtime/want-abort-got-unwind2.rs +++ b/tests/ui/panic-runtime/want-abort-got-unwind2.rs @@ -1,10 +1,23 @@ +// ignore-tidy-linelength //@ build-fail //@ dont-check-compiler-stderr -//@ error-pattern:is not compiled with this crate's panic strategy `abort` //@ aux-build:panic-runtime-unwind.rs //@ aux-build:wants-panic-runtime-unwind.rs //@ compile-flags:-C panic=abort +// Like `want-abort-got-unwind.rs`, this version checks that if the root binary wants abort panic +// runtime, that the compiler rejects a setup where a dependency crate in the dependency DAG +// transitively provides an unwind panic runtime (which also is built with `-Cpanic=unwind`, making +// that potentially-unwinding). + +// NOTE: similar to `want-abort-got-unwind.rs`, there can be additional errors if the target default +// panic strategy is unwind, because then the precompiled `panic_unwind` would also be linked in, +// duplicating `panic_runtime_unwind` (transitively). But those additional errors are not important +// to test intention. +//@ dont-require-annotations: ERROR + extern crate wants_panic_runtime_unwind; fn main() {} + +//~? ERROR the linked panic runtime `panic_runtime_unwind` is not compiled with this crate's panic strategy `abort` diff --git a/tests/ui/panic-runtime/want-unwind-got-abort.rs b/tests/ui/panic-runtime/want-unwind-got-abort.rs index 93342a09182bb..497d1eafda5ba 100644 --- a/tests/ui/panic-runtime/want-unwind-got-abort.rs +++ b/tests/ui/panic-runtime/want-unwind-got-abort.rs @@ -1,6 +1,5 @@ //@ build-fail //@ needs-unwind -//@ error-pattern:is not compiled with this crate's panic strategy `unwind` //@ aux-build:panic-runtime-abort.rs //@ aux-build:panic-runtime-lang-items.rs @@ -9,3 +8,5 @@ extern crate panic_runtime_abort; extern crate panic_runtime_lang_items; + +//~? ERROR the linked panic runtime `panic_runtime_abort` is not compiled with this crate's panic strategy `unwind` diff --git a/tests/ui/panic-runtime/want-unwind-got-abort2.rs b/tests/ui/panic-runtime/want-unwind-got-abort2.rs index ee3f221d09c6b..2609545336c32 100644 --- a/tests/ui/panic-runtime/want-unwind-got-abort2.rs +++ b/tests/ui/panic-runtime/want-unwind-got-abort2.rs @@ -1,6 +1,5 @@ //@ build-fail //@ needs-unwind -//@ error-pattern:is incompatible with this crate's strategy of `unwind` //@ aux-build:panic-runtime-abort.rs //@ aux-build:wants-panic-runtime-abort.rs //@ aux-build:panic-runtime-lang-items.rs @@ -10,3 +9,6 @@ extern crate wants_panic_runtime_abort; extern crate panic_runtime_lang_items; + +//~? ERROR the linked panic runtime `panic_runtime_abort` is not compiled with this crate's panic strategy `unwind` +//~? ERROR the crate `wants_panic_runtime_abort` requires panic strategy `abort` which is incompatible with this crate's strategy of `unwind` diff --git a/tests/ui/panics/catch-unwind-bang.rs b/tests/ui/panics/catch-unwind-bang.rs new file mode 100644 index 0000000000000..80eb377e5ca13 --- /dev/null +++ b/tests/ui/panics/catch-unwind-bang.rs @@ -0,0 +1,15 @@ +//! Check that the unwind machinery handles uninhabited types correctly. +//! It used to call `std::mem::uninitialized::();` at some point... +//! +//! See + +//@ run-pass +//@ needs-unwind + +fn worker() -> ! { + panic!() +} + +fn main() { + std::panic::catch_unwind(worker).unwrap_err(); +} diff --git a/tests/ui/panics/default-backtrace-ice.rs b/tests/ui/panics/default-backtrace-ice.rs index 9933f54875836..e919e1a4ea625 100644 --- a/tests/ui/panics/default-backtrace-ice.rs +++ b/tests/ui/panics/default-backtrace-ice.rs @@ -1,8 +1,5 @@ //@ unset-rustc-env:RUST_BACKTRACE //@ compile-flags:-Z treat-err-as-bug=1 -//@ error-pattern:stack backtrace: -// Verify this is a full backtrace, not a short backtrace. -//@ error-pattern:__rust_begin_short_backtrace //@ failure-status:101 //@ ignore-msvc //@ normalize-stderr: "note: .*" -> "" @@ -20,4 +17,8 @@ // Ignored on msvc because the `__rust_{begin,end}_short_backtrace` symbols // aren't reliable. -fn main() { missing_ident; } +fn main() { missing_ident; } //~ ERROR cannot find value `missing_ident` in this scope + +//~? RAW stack backtrace: +// Verify this is a full backtrace, not a short backtrace. +//~? RAW __rust_begin_short_backtrace diff --git a/tests/ui/panics/default-backtrace-ice.stderr b/tests/ui/panics/default-backtrace-ice.stderr index 2147b0971b5f9..a7d99e325d9b9 100644 --- a/tests/ui/panics/default-backtrace-ice.stderr +++ b/tests/ui/panics/default-backtrace-ice.stderr @@ -1,5 +1,5 @@ error: internal compiler error[E0425]: cannot find value `missing_ident` in this scope - --> $DIR/default-backtrace-ice.rs:23:13 + --> $DIR/default-backtrace-ice.rs:20:13 | LL | fn main() { missing_ident; } | ^^^^^^^^^^^^^ not found in this scope diff --git a/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs b/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs index b23cb9ce917be..73d173022f6ac 100644 --- a/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs +++ b/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs @@ -10,7 +10,7 @@ pub fn a() { #[export_name="fail"] pub fn b() { -//~^ Error symbol `fail` is already defined +//~^ ERROR symbol `fail` is already defined } fn main() {} diff --git a/tests/ui/parser/assoc/assoc-const-underscore-syntactic-pass.rs b/tests/ui/parser/assoc/assoc-const-underscore-syntactic-pass.rs index 6c04537919165..63c567b2d0332 100644 --- a/tests/ui/parser/assoc/assoc-const-underscore-syntactic-pass.rs +++ b/tests/ui/parser/assoc/assoc-const-underscore-syntactic-pass.rs @@ -4,7 +4,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] const _: () = { pub trait A { const _: () = (); diff --git a/tests/ui/parser/assoc/assoc-static-syntactic-fail.rs b/tests/ui/parser/assoc/assoc-static-syntactic-fail.rs index 492f2ea16ef57..e875d733bd625 100644 --- a/tests/ui/parser/assoc/assoc-static-syntactic-fail.rs +++ b/tests/ui/parser/assoc/assoc-static-syntactic-fail.rs @@ -2,7 +2,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] impl S { static IA: u8 = 0; //~ ERROR associated `static` items are not allowed static IB: u8; //~ ERROR associated `static` items are not allowed @@ -12,7 +12,7 @@ impl S { //~^ ERROR a static item cannot be `default` } -#[cfg(FALSE)] +#[cfg(false)] trait T { static TA: u8 = 0; //~ ERROR associated `static` items are not allowed static TB: u8; //~ ERROR associated `static` items are not allowed @@ -22,7 +22,7 @@ trait T { //~^ ERROR a static item cannot be `default` } -#[cfg(FALSE)] +#[cfg(false)] impl T for S { static TA: u8 = 0; //~ ERROR associated `static` items are not allowed static TB: u8; //~ ERROR associated `static` items are not allowed diff --git a/tests/ui/parser/attribute/attr-bad-meta-4.rs b/tests/ui/parser/attribute/attr-bad-meta-4.rs index 2d0c6dbb50ab8..937390a6da5ba 100644 --- a/tests/ui/parser/attribute/attr-bad-meta-4.rs +++ b/tests/ui/parser/attribute/attr-bad-meta-4.rs @@ -2,7 +2,6 @@ macro_rules! mac { ($attr_item: meta) => { #[cfg($attr_item)] //~^ ERROR expected unsuffixed literal, found `meta` metavariable - //~| ERROR expected unsuffixed literal, found `meta` metavariable struct S; } } @@ -11,7 +10,6 @@ mac!(an(arbitrary token stream)); #[cfg(feature = -1)] //~^ ERROR expected unsuffixed literal, found `-` -//~| ERROR expected unsuffixed literal, found `-` fn handler() {} fn main() {} diff --git a/tests/ui/parser/attribute/attr-bad-meta-4.stderr b/tests/ui/parser/attribute/attr-bad-meta-4.stderr index dea574fd36d5b..9c6ab5adadf70 100644 --- a/tests/ui/parser/attribute/attr-bad-meta-4.stderr +++ b/tests/ui/parser/attribute/attr-bad-meta-4.stderr @@ -1,5 +1,5 @@ error: expected unsuffixed literal, found `-` - --> $DIR/attr-bad-meta-4.rs:12:17 + --> $DIR/attr-bad-meta-4.rs:11:17 | LL | #[cfg(feature = -1)] | ^ @@ -15,25 +15,5 @@ LL | mac!(an(arbitrary token stream)); | = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected unsuffixed literal, found `meta` metavariable - --> $DIR/attr-bad-meta-4.rs:3:15 - | -LL | #[cfg($attr_item)] - | ^^^^^^^^^^ -... -LL | mac!(an(arbitrary token stream)); - | -------------------------------- in this macro invocation - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: expected unsuffixed literal, found `-` - --> $DIR/attr-bad-meta-4.rs:12:17 - | -LL | #[cfg(feature = -1)] - | ^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/parser/attribute/attr-dangling-in-fn.rs b/tests/ui/parser/attribute/attr-dangling-in-fn.rs index d59f90aed5d15..b1436b8d89db8 100644 --- a/tests/ui/parser/attribute/attr-dangling-in-fn.rs +++ b/tests/ui/parser/attribute/attr-dangling-in-fn.rs @@ -1,7 +1,5 @@ -//@ error-pattern:expected statement - fn f() { - #[foo = "bar"] + #[foo = "bar"] //~ ERROR expected statement after outer attribute } fn main() { diff --git a/tests/ui/parser/attribute/attr-dangling-in-fn.stderr b/tests/ui/parser/attribute/attr-dangling-in-fn.stderr index c7b948ea8f768..3e9413f535740 100644 --- a/tests/ui/parser/attribute/attr-dangling-in-fn.stderr +++ b/tests/ui/parser/attribute/attr-dangling-in-fn.stderr @@ -1,5 +1,5 @@ error: expected statement after outer attribute - --> $DIR/attr-dangling-in-fn.rs:4:3 + --> $DIR/attr-dangling-in-fn.rs:2:3 | LL | #[foo = "bar"] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/parser/attribute/attr-dangling-in-mod.rs b/tests/ui/parser/attribute/attr-dangling-in-mod.rs index 001ac1135f6e4..17bc7e547d95f 100644 --- a/tests/ui/parser/attribute/attr-dangling-in-mod.rs +++ b/tests/ui/parser/attribute/attr-dangling-in-mod.rs @@ -1,6 +1,4 @@ -//@ error-pattern:expected item - fn main() { } -#[foo = "bar"] +#[foo = "bar"] //~ ERROR expected item after attributes diff --git a/tests/ui/parser/attribute/attr-dangling-in-mod.stderr b/tests/ui/parser/attribute/attr-dangling-in-mod.stderr index 882400c1d6fba..22cc092109d1d 100644 --- a/tests/ui/parser/attribute/attr-dangling-in-mod.stderr +++ b/tests/ui/parser/attribute/attr-dangling-in-mod.stderr @@ -1,5 +1,5 @@ error: expected item after attributes - --> $DIR/attr-dangling-in-mod.rs:6:1 + --> $DIR/attr-dangling-in-mod.rs:4:1 | LL | #[foo = "bar"] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.rs b/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.rs index 26761a1d2544c..1380974538a50 100644 --- a/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.rs +++ b/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.rs @@ -1,108 +1,108 @@ fn main() {} -#[cfg(FALSE)] fn e() { let _ = [#[attr]]; } +#[cfg(false)] fn e() { let _ = [#[attr]]; } //~^ ERROR expected expression, found `]` -#[cfg(FALSE)] fn e() { let _ = foo#[attr](); } +#[cfg(false)] fn e() { let _ = foo#[attr](); } //~^ ERROR expected one of -#[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } +#[cfg(false)] fn e() { let _ = foo(#![attr]); } //~^ ERROR an inner attribute is not permitted in this context //~| ERROR an inner attribute is not permitted in this context //~| ERROR expected expression, found `)` -#[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } +#[cfg(false)] fn e() { let _ = x.foo(#![attr]); } //~^ ERROR an inner attribute is not permitted in this context //~| ERROR expected expression, found `)` -#[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; } +#[cfg(false)] fn e() { let _ = 0 + #![attr] 0; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = !#![attr] 0; } +#[cfg(false)] fn e() { let _ = !#![attr] 0; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = -#![attr] 0; } +#[cfg(false)] fn e() { let _ = -#![attr] 0; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; } +#[cfg(false)] fn e() { let _ = x #![attr] as Y; } //~^ ERROR expected one of -#[cfg(FALSE)] fn e() { let _ = || #![attr] foo; } +#[cfg(false)] fn e() { let _ = || #![attr] foo; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; } +#[cfg(false)] fn e() { let _ = move || #![attr] foo; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; } +#[cfg(false)] fn e() { let _ = || #![attr] {foo}; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } +#[cfg(false)] fn e() { let _ = move || #![attr] {foo}; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; } +#[cfg(false)] fn e() { let _ = #[attr] ..#[attr] 0; } //~^ ERROR attributes are not allowed on range expressions starting with `..` -#[cfg(FALSE)] fn e() { let _ = #[attr] ..; } +#[cfg(false)] fn e() { let _ = #[attr] ..; } //~^ ERROR attributes are not allowed on range expressions starting with `..` -#[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; } +#[cfg(false)] fn e() { let _ = #[attr] &#![attr] 0; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } +#[cfg(false)] fn e() { let _ = #[attr] &mut #![attr] 0; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } +#[cfg(false)] fn e() { let _ = if 0 #[attr] {}; } //~^ ERROR outer attributes are not allowed on `if` -#[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } +#[cfg(false)] fn e() { let _ = if 0 {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; } +#[cfg(false)] fn e() { let _ = if 0 {} #[attr] else {}; } //~^ ERROR expected one of -#[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } +#[cfg(false)] fn e() { let _ = if 0 {} else #[attr] {}; } //~^ ERROR outer attributes are not allowed on `if` -#[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } +#[cfg(false)] fn e() { let _ = if 0 {} else {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } +#[cfg(false)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } //~^ ERROR outer attributes are not allowed on `if` -#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } +#[cfg(false)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } //~^ ERROR outer attributes are not allowed on `if` -#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } +#[cfg(false)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } +#[cfg(false)] fn e() { let _ = if let _ = 0 #[attr] {}; } //~^ ERROR outer attributes are not allowed on `if` -#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } +#[cfg(false)] fn e() { let _ = if let _ = 0 {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } +#[cfg(false)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } //~^ ERROR expected one of -#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } +#[cfg(false)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } //~^ ERROR outer attributes are not allowed on `if` -#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } +#[cfg(false)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } +#[cfg(false)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } //~^ ERROR outer attributes are not allowed on `if` -#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } +#[cfg(false)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } //~^ ERROR outer attributes are not allowed on `if` -#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; } +#[cfg(false)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; } //~^ ERROR an inner attribute is not permitted in this context -#[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; } +#[cfg(false)] fn s() { #[attr] #![attr] let _ = 0; } //~^ ERROR an inner attribute is not permitted following an outer attribute -#[cfg(FALSE)] fn s() { #[attr] #![attr] 0; } +#[cfg(false)] fn s() { #[attr] #![attr] 0; } //~^ ERROR an inner attribute is not permitted following an outer attribute -#[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); } +#[cfg(false)] fn s() { #[attr] #![attr] foo!(); } //~^ ERROR an inner attribute is not permitted following an outer attribute -#[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; } +#[cfg(false)] fn s() { #[attr] #![attr] foo![]; } //~^ ERROR an inner attribute is not permitted following an outer attribute -#[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; } +#[cfg(false)] fn s() { #[attr] #![attr] foo!{}; } //~^ ERROR an inner attribute is not permitted following an outer attribute // FIXME: Allow attributes in pattern constexprs? // note: requires parens in patterns to allow disambiguation -#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } +#[cfg(false)] fn e() { match 0 { 0..=#[attr] 10 => () } } //~^ ERROR inclusive range with no end //~| ERROR expected one of `=>`, `if`, or `|`, found `#` -#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } +#[cfg(false)] fn e() { match 0 { 0..=#[attr] -10 => () } } //~^ ERROR inclusive range with no end //~| ERROR expected one of `=>`, `if`, or `|`, found `#` -#[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } } +#[cfg(false)] fn e() { match 0 { 0..=-#[attr] 10 => () } } //~^ ERROR unexpected token: `#` -#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } +#[cfg(false)] fn e() { match 0 { 0..=#[attr] FOO => () } } //~^ ERROR inclusive range with no end //~| ERROR expected one of `=>`, `if`, or `|`, found `#` -#[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); } +#[cfg(false)] fn e() { let _ = x.#![attr]foo(); } //~^ ERROR unexpected token: `#` //~| ERROR expected one of `.` -#[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); } +#[cfg(false)] fn e() { let _ = x.#[attr]foo(); } //~^ ERROR unexpected token: `#` //~| ERROR expected one of `.` // make sure we don't catch this bug again... -#[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } } +#[cfg(false)] fn e() { { fn foo() { #[attr]; } } } //~^ ERROR expected statement after outer attribute -#[cfg(FALSE)] fn e() { { fn foo() { #[attr] } } } +#[cfg(false)] fn e() { { fn foo() { #[attr] } } } //~^ ERROR expected statement after outer attribute diff --git a/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.stderr b/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.stderr index bd860841b8060..5d94a8dcbdb81 100644 --- a/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.stderr +++ b/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.stderr @@ -1,19 +1,19 @@ error: expected expression, found `]` --> $DIR/attr-stmt-expr-attr-bad.rs:3:40 | -LL | #[cfg(FALSE)] fn e() { let _ = [#[attr]]; } +LL | #[cfg(false)] fn e() { let _ = [#[attr]]; } | ^ expected expression error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `#` --> $DIR/attr-stmt-expr-attr-bad.rs:5:35 | -LL | #[cfg(FALSE)] fn e() { let _ = foo#[attr](); } +LL | #[cfg(false)] fn e() { let _ = foo#[attr](); } | ^ expected one of 8 possible tokens error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:7:36 | -LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } +LL | #[cfg(false)] fn e() { let _ = foo(#![attr]); } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -22,7 +22,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:7:36 | -LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } +LL | #[cfg(false)] fn e() { let _ = foo(#![attr]); } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -32,13 +32,13 @@ LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } error: expected expression, found `)` --> $DIR/attr-stmt-expr-attr-bad.rs:7:44 | -LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } +LL | #[cfg(false)] fn e() { let _ = foo(#![attr]); } | ^ expected expression error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:11:38 | -LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } +LL | #[cfg(false)] fn e() { let _ = x.foo(#![attr]); } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -47,13 +47,13 @@ LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } error: expected expression, found `)` --> $DIR/attr-stmt-expr-attr-bad.rs:11:46 | -LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } +LL | #[cfg(false)] fn e() { let _ = x.foo(#![attr]); } | ^ expected expression error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:14:36 | -LL | #[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; } +LL | #[cfg(false)] fn e() { let _ = 0 + #![attr] 0; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -62,7 +62,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; } error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:16:33 | -LL | #[cfg(FALSE)] fn e() { let _ = !#![attr] 0; } +LL | #[cfg(false)] fn e() { let _ = !#![attr] 0; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -71,7 +71,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = !#![attr] 0; } error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:18:33 | -LL | #[cfg(FALSE)] fn e() { let _ = -#![attr] 0; } +LL | #[cfg(false)] fn e() { let _ = -#![attr] 0; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -80,13 +80,13 @@ LL | #[cfg(FALSE)] fn e() { let _ = -#![attr] 0; } error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `#` --> $DIR/attr-stmt-expr-attr-bad.rs:20:34 | -LL | #[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; } +LL | #[cfg(false)] fn e() { let _ = x #![attr] as Y; } | ^ expected one of 8 possible tokens error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:22:35 | -LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] foo; } +LL | #[cfg(false)] fn e() { let _ = || #![attr] foo; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -95,7 +95,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] foo; } error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:24:40 | -LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; } +LL | #[cfg(false)] fn e() { let _ = move || #![attr] foo; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -104,7 +104,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; } error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:26:35 | -LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; } +LL | #[cfg(false)] fn e() { let _ = || #![attr] {foo}; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -113,7 +113,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; } error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:28:40 | -LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } +LL | #[cfg(false)] fn e() { let _ = move || #![attr] {foo}; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -122,19 +122,19 @@ LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } error: attributes are not allowed on range expressions starting with `..` --> $DIR/attr-stmt-expr-attr-bad.rs:30:40 | -LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; } +LL | #[cfg(false)] fn e() { let _ = #[attr] ..#[attr] 0; } | ^^ error: attributes are not allowed on range expressions starting with `..` --> $DIR/attr-stmt-expr-attr-bad.rs:32:40 | -LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..; } +LL | #[cfg(false)] fn e() { let _ = #[attr] ..; } | ^^ error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:34:41 | -LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; } +LL | #[cfg(false)] fn e() { let _ = #[attr] &#![attr] 0; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -143,7 +143,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; } error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:36:45 | -LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } +LL | #[cfg(false)] fn e() { let _ = #[attr] &mut #![attr] 0; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -152,21 +152,21 @@ LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:38:37 | -LL | #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } +LL | #[cfg(false)] fn e() { let _ = if 0 #[attr] {}; } | -- ^^^^^^^ -- the attributes are attached to this branch | | | the branch belongs to this `if` | help: remove the attributes | -LL - #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } -LL + #[cfg(FALSE)] fn e() { let _ = if 0 {}; } +LL - #[cfg(false)] fn e() { let _ = if 0 #[attr] {}; } +LL + #[cfg(false)] fn e() { let _ = if 0 {}; } | error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:40:38 | -LL | #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } +LL | #[cfg(false)] fn e() { let _ = if 0 {#![attr]}; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -175,27 +175,27 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` --> $DIR/attr-stmt-expr-attr-bad.rs:42:40 | -LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; } +LL | #[cfg(false)] fn e() { let _ = if 0 {} #[attr] else {}; } | ^ expected one of `.`, `;`, `?`, `else`, or an operator error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:44:45 | -LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } +LL | #[cfg(false)] fn e() { let _ = if 0 {} else #[attr] {}; } | ---- ^^^^^^^ -- the attributes are attached to this branch | | | the branch belongs to this `else` | help: remove the attributes | -LL - #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } -LL + #[cfg(FALSE)] fn e() { let _ = if 0 {} else {}; } +LL - #[cfg(false)] fn e() { let _ = if 0 {} else #[attr] {}; } +LL + #[cfg(false)] fn e() { let _ = if 0 {} else {}; } | error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:46:46 | -LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } +LL | #[cfg(false)] fn e() { let _ = if 0 {} else {#![attr]}; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -204,35 +204,35 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:48:45 | -LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } +LL | #[cfg(false)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } | ---- ^^^^^^^ ------- the attributes are attached to this branch | | | the branch belongs to this `else` | help: remove the attributes | -LL - #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } -LL + #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {}; } +LL - #[cfg(false)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } +LL + #[cfg(false)] fn e() { let _ = if 0 {} else if 0 {}; } | error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:50:50 | -LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } +LL | #[cfg(false)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } | -- ^^^^^^^ -- the attributes are attached to this branch | | | the branch belongs to this `if` | help: remove the attributes | -LL - #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } -LL + #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {}; } +LL - #[cfg(false)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } +LL + #[cfg(false)] fn e() { let _ = if 0 {} else if 0 {}; } | error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:52:51 | -LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } +LL | #[cfg(false)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -241,21 +241,21 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:54:45 | -LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } +LL | #[cfg(false)] fn e() { let _ = if let _ = 0 #[attr] {}; } | -- ^^^^^^^ -- the attributes are attached to this branch | | | the branch belongs to this `if` | help: remove the attributes | -LL - #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } -LL + #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {}; } +LL - #[cfg(false)] fn e() { let _ = if let _ = 0 #[attr] {}; } +LL + #[cfg(false)] fn e() { let _ = if let _ = 0 {}; } | error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:56:46 | -LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } +LL | #[cfg(false)] fn e() { let _ = if let _ = 0 {#![attr]}; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -264,27 +264,27 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` --> $DIR/attr-stmt-expr-attr-bad.rs:58:48 | -LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } +LL | #[cfg(false)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } | ^ expected one of `.`, `;`, `?`, `else`, or an operator error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:60:53 | -LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } +LL | #[cfg(false)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } | ---- ^^^^^^^ -- the attributes are attached to this branch | | | the branch belongs to this `else` | help: remove the attributes | -LL - #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } -LL + #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {}; } +LL - #[cfg(false)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } +LL + #[cfg(false)] fn e() { let _ = if let _ = 0 {} else {}; } | error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:62:54 | -LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } +LL | #[cfg(false)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -293,35 +293,35 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:64:53 | -LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } +LL | #[cfg(false)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } | ---- ^^^^^^^ --------------- the attributes are attached to this branch | | | the branch belongs to this `else` | help: remove the attributes | -LL - #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } -LL + #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {}; } +LL - #[cfg(false)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } +LL + #[cfg(false)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {}; } | error: outer attributes are not allowed on `if` and `else` branches --> $DIR/attr-stmt-expr-attr-bad.rs:66:66 | -LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } +LL | #[cfg(false)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } | -- ^^^^^^^ -- the attributes are attached to this branch | | | the branch belongs to this `if` | help: remove the attributes | -LL - #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } -LL + #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {}; } +LL - #[cfg(false)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } +LL + #[cfg(false)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {}; } | error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:68:67 | -LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; } +LL | #[cfg(false)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; } | ^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files @@ -330,7 +330,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]} error: an inner attribute is not permitted following an outer attribute --> $DIR/attr-stmt-expr-attr-bad.rs:71:32 | -LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; } +LL | #[cfg(false)] fn s() { #[attr] #![attr] let _ = 0; } | ------- ^^^^^^^^ not permitted following an outer attribute | | | previous outer attribute @@ -341,7 +341,7 @@ LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; } error: an inner attribute is not permitted following an outer attribute --> $DIR/attr-stmt-expr-attr-bad.rs:73:32 | -LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] 0; } +LL | #[cfg(false)] fn s() { #[attr] #![attr] 0; } | ------- ^^^^^^^^ not permitted following an outer attribute | | | previous outer attribute @@ -352,7 +352,7 @@ LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] 0; } error: an inner attribute is not permitted following an outer attribute --> $DIR/attr-stmt-expr-attr-bad.rs:75:32 | -LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); } +LL | #[cfg(false)] fn s() { #[attr] #![attr] foo!(); } | ------- ^^^^^^^^ ------- the inner attribute doesn't annotate this item macro invocation | | | | | not permitted following an outer attribute @@ -363,7 +363,7 @@ LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); } error: an inner attribute is not permitted following an outer attribute --> $DIR/attr-stmt-expr-attr-bad.rs:77:32 | -LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; } +LL | #[cfg(false)] fn s() { #[attr] #![attr] foo![]; } | ------- ^^^^^^^^ ------- the inner attribute doesn't annotate this item macro invocation | | | | | not permitted following an outer attribute @@ -374,7 +374,7 @@ LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; } error: an inner attribute is not permitted following an outer attribute --> $DIR/attr-stmt-expr-attr-bad.rs:79:32 | -LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; } +LL | #[cfg(false)] fn s() { #[attr] #![attr] foo!{}; } | ------- ^^^^^^^^ ------ the inner attribute doesn't annotate this item macro invocation | | | | | not permitted following an outer attribute @@ -385,100 +385,100 @@ LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; } error[E0586]: inclusive range with no end --> $DIR/attr-stmt-expr-attr-bad.rs:85:35 | -LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } +LL | #[cfg(false)] fn e() { match 0 { 0..=#[attr] 10 => () } } | ^^^ | = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) help: use `..` instead | -LL - #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } -LL + #[cfg(FALSE)] fn e() { match 0 { 0..#[attr] 10 => () } } +LL - #[cfg(false)] fn e() { match 0 { 0..=#[attr] 10 => () } } +LL + #[cfg(false)] fn e() { match 0 { 0..#[attr] 10 => () } } | error: expected one of `=>`, `if`, or `|`, found `#` --> $DIR/attr-stmt-expr-attr-bad.rs:85:38 | -LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } +LL | #[cfg(false)] fn e() { match 0 { 0..=#[attr] 10 => () } } | ^ expected one of `=>`, `if`, or `|` error[E0586]: inclusive range with no end --> $DIR/attr-stmt-expr-attr-bad.rs:88:35 | -LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } +LL | #[cfg(false)] fn e() { match 0 { 0..=#[attr] -10 => () } } | ^^^ | = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) help: use `..` instead | -LL - #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } -LL + #[cfg(FALSE)] fn e() { match 0 { 0..#[attr] -10 => () } } +LL - #[cfg(false)] fn e() { match 0 { 0..=#[attr] -10 => () } } +LL + #[cfg(false)] fn e() { match 0 { 0..#[attr] -10 => () } } | error: expected one of `=>`, `if`, or `|`, found `#` --> $DIR/attr-stmt-expr-attr-bad.rs:88:38 | -LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } +LL | #[cfg(false)] fn e() { match 0 { 0..=#[attr] -10 => () } } | ^ expected one of `=>`, `if`, or `|` error: unexpected token: `#` --> $DIR/attr-stmt-expr-attr-bad.rs:91:39 | -LL | #[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } } +LL | #[cfg(false)] fn e() { match 0 { 0..=-#[attr] 10 => () } } | ^ error[E0586]: inclusive range with no end --> $DIR/attr-stmt-expr-attr-bad.rs:93:35 | -LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } +LL | #[cfg(false)] fn e() { match 0 { 0..=#[attr] FOO => () } } | ^^^ | = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) help: use `..` instead | -LL - #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } -LL + #[cfg(FALSE)] fn e() { match 0 { 0..#[attr] FOO => () } } +LL - #[cfg(false)] fn e() { match 0 { 0..=#[attr] FOO => () } } +LL + #[cfg(false)] fn e() { match 0 { 0..#[attr] FOO => () } } | error: expected one of `=>`, `if`, or `|`, found `#` --> $DIR/attr-stmt-expr-attr-bad.rs:93:38 | -LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } +LL | #[cfg(false)] fn e() { match 0 { 0..=#[attr] FOO => () } } | ^ expected one of `=>`, `if`, or `|` error: unexpected token: `#` --> $DIR/attr-stmt-expr-attr-bad.rs:97:34 | -LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); } +LL | #[cfg(false)] fn e() { let _ = x.#![attr]foo(); } | ^ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` --> $DIR/attr-stmt-expr-attr-bad.rs:97:34 | -LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); } +LL | #[cfg(false)] fn e() { let _ = x.#![attr]foo(); } | ^ expected one of `.`, `;`, `?`, `else`, or an operator error: unexpected token: `#` --> $DIR/attr-stmt-expr-attr-bad.rs:100:34 | -LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); } +LL | #[cfg(false)] fn e() { let _ = x.#[attr]foo(); } | ^ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` --> $DIR/attr-stmt-expr-attr-bad.rs:100:34 | -LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); } +LL | #[cfg(false)] fn e() { let _ = x.#[attr]foo(); } | ^ expected one of `.`, `;`, `?`, `else`, or an operator error: expected statement after outer attribute --> $DIR/attr-stmt-expr-attr-bad.rs:105:37 | -LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } } +LL | #[cfg(false)] fn e() { { fn foo() { #[attr]; } } } | ^^^^^^^ error: expected statement after outer attribute --> $DIR/attr-stmt-expr-attr-bad.rs:107:37 | -LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr] } } } +LL | #[cfg(false)] fn e() { { fn foo() { #[attr] } } } | ^^^^^^^ error: aborting due to 53 previous errors diff --git a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs index 33671df949280..371f19d487268 100644 --- a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs +++ b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.rs @@ -1,7 +1,7 @@ #![feature(stmt_expr_attributes)] fn foo() -> String { - #[cfg(FALSE)] + #[cfg(false)] [1, 2, 3].iter().map(|c| c.to_string()).collect::() //~ ERROR expected `;`, found `#` #[cfg(not(FALSE))] String::new() diff --git a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr index 6266718162f8e..3a97a14b3c301 100644 --- a/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr +++ b/tests/ui/parser/attribute/multiple-tail-expr-behind-cfg.stderr @@ -1,7 +1,7 @@ error: expected `;`, found `#` --> $DIR/multiple-tail-expr-behind-cfg.rs:5:64 | -LL | #[cfg(FALSE)] +LL | #[cfg(false)] | ------------- only `;` terminated statements or tail expressions are allowed after this attribute LL | [1, 2, 3].iter().map(|c| c.to_string()).collect::() | ^ expected `;` here @@ -18,7 +18,7 @@ LL | { [1, 2, 3].iter().map(|c| c.to_string()).collect::() } | + + help: it seems like you are trying to provide different expressions depending on `cfg`, consider using `if cfg!(..)` | -LL ~ if cfg!(FALSE) { +LL ~ if cfg!(false) { LL ~ [1, 2, 3].iter().map(|c| c.to_string()).collect::() LL ~ } else if cfg!(not(FALSE)) { LL ~ String::new() diff --git a/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.rs b/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.rs index e2a62922bcc5d..1cd3f13d7b67a 100644 --- a/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.rs +++ b/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.rs @@ -5,7 +5,7 @@ macro_rules! the_macro { #[cfg()] $foo //~ ERROR expected `;`, found `#` - #[cfg(FALSE)] + #[cfg(false)] $bar }; } diff --git a/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.stderr b/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.stderr index fa4409f73fa50..41e7b5ab759dd 100644 --- a/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.stderr +++ b/tests/ui/parser/attribute/properly-recover-from-trailing-outer-attribute-in-body-2.stderr @@ -6,7 +6,7 @@ LL | #[cfg()] LL | $foo | ^ expected `;` here LL | -LL | #[cfg(FALSE)] +LL | #[cfg(false)] | - unexpected token ... LL | the_macro!( (); (); ); diff --git a/tests/ui/parser/bad-lit-suffixes.stderr b/tests/ui/parser/bad-lit-suffixes.stderr index d6b50b0e0d1fe..86ef35bf7833f 100644 --- a/tests/ui/parser/bad-lit-suffixes.stderr +++ b/tests/ui/parser/bad-lit-suffixes.stderr @@ -51,7 +51,7 @@ LL | #[rustc_layout_scalar_valid_range_start(0suffix)] | = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) -warning: extern declarations without an explicit ABI are deprecated +warning: `extern` declarations without an explicit ABI are deprecated --> $DIR/bad-lit-suffixes.rs:3:1 | LL | extern @@ -59,7 +59,7 @@ LL | extern | = note: `#[warn(missing_abi)]` on by default -warning: extern declarations without an explicit ABI are deprecated +warning: `extern` declarations without an explicit ABI are deprecated --> $DIR/bad-lit-suffixes.rs:7:1 | LL | extern diff --git a/tests/ui/parser/block-no-opening-brace.rs b/tests/ui/parser/block-no-opening-brace.rs index 2fde37ce6acef..b08c830bfc76f 100644 --- a/tests/ui/parser/block-no-opening-brace.rs +++ b/tests/ui/parser/block-no-opening-brace.rs @@ -30,7 +30,7 @@ fn in_try() { // FIXME(#80931) fn in_async() { async - let x = 0; //~ ERROR expected one of `move`, `|`, or `||`, found keyword `let` + let x = 0; //~ ERROR expected one of `move`, `use`, `|`, or `||`, found keyword `let` } // FIXME(#78168) diff --git a/tests/ui/parser/block-no-opening-brace.stderr b/tests/ui/parser/block-no-opening-brace.stderr index b65de4eac3f80..f51ee92626f53 100644 --- a/tests/ui/parser/block-no-opening-brace.stderr +++ b/tests/ui/parser/block-no-opening-brace.stderr @@ -43,11 +43,11 @@ error: expected expression, found reserved keyword `try` LL | try | ^^^ expected expression -error: expected one of `move`, `|`, or `||`, found keyword `let` +error: expected one of `move`, `use`, `|`, or `||`, found keyword `let` --> $DIR/block-no-opening-brace.rs:33:9 | LL | async - | - expected one of `move`, `|`, or `||` + | - expected one of `move`, `use`, `|`, or `||` LL | let x = 0; | ^^^ unexpected token diff --git a/tests/ui/parser/bounds-lifetime-where.rs b/tests/ui/parser/bounds-lifetime-where.rs index 7ff75233d3a8e..730be7139bec8 100644 --- a/tests/ui/parser/bounds-lifetime-where.rs +++ b/tests/ui/parser/bounds-lifetime-where.rs @@ -5,6 +5,6 @@ type A where 'a:, = u8; // OK type A where 'a: 'b + 'c = u8; // OK type A where = u8; // OK type A where 'a: 'b + = u8; // OK -type A where , = u8; //~ ERROR expected one of `;`, `=`, `where`, lifetime, or type, found `,` +type A where , = u8; //~ ERROR expected one of `#`, `;`, `=`, `where`, lifetime, or type, found `,` fn main() {} diff --git a/tests/ui/parser/bounds-lifetime-where.stderr b/tests/ui/parser/bounds-lifetime-where.stderr index 9dd963afc7963..5aadb5fff320b 100644 --- a/tests/ui/parser/bounds-lifetime-where.stderr +++ b/tests/ui/parser/bounds-lifetime-where.stderr @@ -1,8 +1,8 @@ -error: expected one of `;`, `=`, `where`, lifetime, or type, found `,` +error: expected one of `#`, `;`, `=`, `where`, lifetime, or type, found `,` --> $DIR/bounds-lifetime-where.rs:8:14 | LL | type A where , = u8; - | ^ expected one of `;`, `=`, `where`, lifetime, or type + | ^ expected one of `#`, `;`, `=`, `where`, lifetime, or type error: aborting due to 1 previous error diff --git a/tests/ui/parser/brace-in-let-chain.rs b/tests/ui/parser/brace-in-let-chain.rs index 2009bc88d9e8d..25586441c1886 100644 --- a/tests/ui/parser/brace-in-let-chain.rs +++ b/tests/ui/parser/brace-in-let-chain.rs @@ -1,6 +1,6 @@ // issue #117766 +//@ edition: 2024 -#![feature(let_chains)] fn main() { if let () = () && let () = () { diff --git a/tests/ui/parser/byte-string-literals.stderr b/tests/ui/parser/byte-string-literals.stderr index 086337425577f..3e589258d4132 100644 --- a/tests/ui/parser/byte-string-literals.stderr +++ b/tests/ui/parser/byte-string-literals.stderr @@ -44,7 +44,7 @@ error[E0766]: unterminated double quote byte string LL | b"a | ______^ LL | | } - | |_^ + | |__^ error: aborting due to 6 previous errors diff --git a/tests/ui/parser/circular_modules_hello.rs b/tests/ui/parser/circular_modules_hello.rs index eb0284d8b410f..540752ea2311f 100644 --- a/tests/ui/parser/circular_modules_hello.rs +++ b/tests/ui/parser/circular_modules_hello.rs @@ -1,4 +1,4 @@ -//@ ignore-test: this is an auxiliary file for circular-modules-main.rs +//@ ignore-auxiliary (used by `./circular-modules-main.rs`) #[path = "circular_modules_main.rs"] mod circular_modules_main; diff --git a/tests/ui/parser/circular_modules_main.rs b/tests/ui/parser/circular_modules_main.rs index d5cdff34a264f..c130c6792b86e 100644 --- a/tests/ui/parser/circular_modules_main.rs +++ b/tests/ui/parser/circular_modules_main.rs @@ -1,5 +1,3 @@ -//@ error-pattern: circular modules - #[path = "circular_modules_hello.rs"] mod circular_modules_hello; @@ -10,3 +8,5 @@ pub fn hi_str() -> String { fn main() { circular_modules_hello::say_hello(); } + +//~? ERROR circular modules: $DIR/circular_modules_main.rs -> $DIR/circular_modules_hello.rs -> $DIR/circular_modules_main.rs diff --git a/tests/ui/parser/class-implements-bad-trait.rs b/tests/ui/parser/class-implements-bad-trait.rs index 152fe09b51c0b..e30c1bc3a7f7b 100644 --- a/tests/ui/parser/class-implements-bad-trait.rs +++ b/tests/ui/parser/class-implements-bad-trait.rs @@ -1,5 +1,4 @@ -//@ error-pattern:nonexistent -class cat : nonexistent { +class cat : nonexistent { //~ ERROR expected one of `!` or `::`, found `cat` let meows: usize; new(in_x : usize) { self.meows = in_x; } } diff --git a/tests/ui/parser/class-implements-bad-trait.stderr b/tests/ui/parser/class-implements-bad-trait.stderr index 5290e3594d553..5cdb85a8cffbb 100644 --- a/tests/ui/parser/class-implements-bad-trait.stderr +++ b/tests/ui/parser/class-implements-bad-trait.stderr @@ -1,5 +1,5 @@ error: expected one of `!` or `::`, found `cat` - --> $DIR/class-implements-bad-trait.rs:2:7 + --> $DIR/class-implements-bad-trait.rs:1:7 | LL | class cat : nonexistent { | ^^^ expected one of `!` or `::` diff --git a/tests/ui/parser/closure-return-syntax.rs b/tests/ui/parser/closure-return-syntax.rs index c6a08abeff4ba..6865d8c5393d4 100644 --- a/tests/ui/parser/closure-return-syntax.rs +++ b/tests/ui/parser/closure-return-syntax.rs @@ -1,7 +1,21 @@ // Test that we cannot parse a closure with an explicit return type // unless it uses braces. -fn main() { +fn needs_braces_1() { let x = || -> i32 22; //~^ ERROR expected `{`, found `22` } + +// Check other delimiters too. + +fn needs_braces_2() { + let x = || -> (i32, i32) (1, 2); + //~^ ERROR expected `{`, found `(` +} + +fn needs_braces_3() { + let c = || -> [i32; 2] [1, 2]; + //~^ ERROR expected `{`, found `[` +} + +fn main() {} diff --git a/tests/ui/parser/closure-return-syntax.stderr b/tests/ui/parser/closure-return-syntax.stderr index aacc31ed871d3..763f19ccc6454 100644 --- a/tests/ui/parser/closure-return-syntax.stderr +++ b/tests/ui/parser/closure-return-syntax.stderr @@ -2,12 +2,40 @@ error: expected `{`, found `22` --> $DIR/closure-return-syntax.rs:5:23 | LL | let x = || -> i32 22; - | ^^ expected `{` + | --- ^^ + | | + | explicit return type requires closure body to be enclosed in braces | -help: you might have meant to write this as part of a block +help: wrap the expression in curly braces | LL | let x = || -> i32 { 22 }; | + + -error: aborting due to 1 previous error +error: expected `{`, found `(` + --> $DIR/closure-return-syntax.rs:12:34 + | +LL | let x = || -> (i32, i32) (1, 2); + | ---------- ^ + | | + | explicit return type requires closure body to be enclosed in braces + | +help: wrap the expression in curly braces + | +LL | let x = || -> (i32, i32) { (1, 2) }; + | + + + +error: expected `{`, found `[` + --> $DIR/closure-return-syntax.rs:17:32 + | +LL | let c = || -> [i32; 2] [1, 2]; + | -------- ^ + | | + | explicit return type requires closure body to be enclosed in braces + | +help: wrap the expression in curly braces + | +LL | let c = || -> [i32; 2] { [1, 2] }; + | + + + +error: aborting due to 3 previous errors diff --git a/tests/ui/parser/constraints-before-generic-args-syntactic-pass.rs b/tests/ui/parser/constraints-before-generic-args-syntactic-pass.rs index ed3ffed2f8026..acc58a47fbcf4 100644 --- a/tests/ui/parser/constraints-before-generic-args-syntactic-pass.rs +++ b/tests/ui/parser/constraints-before-generic-args-syntactic-pass.rs @@ -1,6 +1,6 @@ //@ check-pass -#[cfg(FALSE)] +#[cfg(false)] fn syntax() { foo::(); foo::(); diff --git a/tests/ui/parser/default-on-wrong-item-kind.rs b/tests/ui/parser/default-on-wrong-item-kind.rs index 98a95cfa35a9e..da990a4b42123 100644 --- a/tests/ui/parser/default-on-wrong-item-kind.rs +++ b/tests/ui/parser/default-on-wrong-item-kind.rs @@ -4,7 +4,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] mod free_items { default extern crate foo; //~ ERROR an extern crate cannot be `default` default use foo; //~ ERROR a `use` import cannot be `default` @@ -28,7 +28,7 @@ mod free_items { default macro_rules! foo {} //~ ERROR a macro definition cannot be `default` } -#[cfg(FALSE)] +#[cfg(false)] extern "C" { default extern crate foo; //~ ERROR an extern crate cannot be `default` //~^ ERROR extern crate is not supported in `extern` blocks @@ -65,7 +65,7 @@ extern "C" { //~^ ERROR macro definition is not supported in `extern` blocks } -#[cfg(FALSE)] +#[cfg(false)] impl S { default extern crate foo; //~ ERROR an extern crate cannot be `default` //~^ ERROR extern crate is not supported in `trait`s or `impl`s @@ -102,7 +102,7 @@ impl S { //~^ ERROR macro definition is not supported in `trait`s or `impl`s } -#[cfg(FALSE)] +#[cfg(false)] trait T { default extern crate foo; //~ ERROR an extern crate cannot be `default` //~^ ERROR extern crate is not supported in `trait`s or `impl`s diff --git a/tests/ui/parser/deli-ident-issue-1.rs b/tests/ui/parser/deli-ident-issue-1.rs index 224ee6c09e0c2..b7e7df1f860bb 100644 --- a/tests/ui/parser/deli-ident-issue-1.rs +++ b/tests/ui/parser/deli-ident-issue-1.rs @@ -1,4 +1,4 @@ -#![feature(let_chains)] +//@ edition: 2024 trait Demo {} impl dyn Demo { diff --git a/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs b/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs index 47df107a26123..db25ce440893d 100644 --- a/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs +++ b/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs @@ -1,7 +1,11 @@ +//@ dont-require-annotations: NOTE + fn main() { unsafe { - dealloc(ptr2, Layout::(x: !)(1, 1)); //~ ERROR: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `:` - //~^ ERROR: expected one of `.`, `;`, `?`, `}`, or an operator, found `)` - //~| while parsing this parenthesized list of type arguments starting here + dealloc(ptr2, Layout::(x: !)(1, 1)); //~ ERROR `Trait(...)` syntax does not support named parameters + //~^ ERROR cannot find function `dealloc` in this scope [E0425] + //~| ERROR cannot find value `ptr2` in this scope [E0425] + //~| ERROR the `!` type is experimental [E0658] + //~| ERROR cannot find function, tuple struct or tuple variant `Layout` in this scope [E0425] } } diff --git a/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.stderr b/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.stderr index 8067c97ae4b5a..a083883af219f 100644 --- a/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.stderr +++ b/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.stderr @@ -1,16 +1,43 @@ -error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `:` - --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:3:33 +error: `Trait(...)` syntax does not support named parameters + --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:5:32 | LL | dealloc(ptr2, Layout::(x: !)(1, 1)); - | --- ^ expected one of 7 possible tokens - | | - | while parsing this parenthesized list of type arguments starting here + | ^ help: remove the parameter name -error: expected one of `.`, `;`, `?`, `}`, or an operator, found `)` - --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:3:43 +error[E0425]: cannot find function `dealloc` in this scope + --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:5:9 | LL | dealloc(ptr2, Layout::(x: !)(1, 1)); - | ^ expected one of `.`, `;`, `?`, `}`, or an operator + | ^^^^^^^ not found in this scope + | +help: consider importing this function + | +LL + use std::alloc::dealloc; + | + +error[E0425]: cannot find value `ptr2` in this scope + --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:5:17 + | +LL | dealloc(ptr2, Layout::(x: !)(1, 1)); + | ^^^^ not found in this scope + +error[E0658]: the `!` type is experimental + --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:5:35 + | +LL | dealloc(ptr2, Layout::(x: !)(1, 1)); + | ^ + | + = note: see issue #35121 for more information + = help: add `#![feature(never_type)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0425]: cannot find function, tuple struct or tuple variant `Layout` in this scope + --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:5:23 + | +LL | dealloc(ptr2, Layout::(x: !)(1, 1)); + | ^^^^^^ not found in this scope -error: aborting due to 2 previous errors +error: aborting due to 5 previous errors +Some errors have detailed explanations: E0425, E0658. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/parser/diff-markers/unclosed-delims-in-macro.rs b/tests/ui/parser/diff-markers/unclosed-delims-in-macro.rs index 41a7de03d4b79..acd608e27c613 100644 --- a/tests/ui/parser/diff-markers/unclosed-delims-in-macro.rs +++ b/tests/ui/parser/diff-markers/unclosed-delims-in-macro.rs @@ -8,4 +8,4 @@ macro_rules! foo { () { // >>>>>>> 7a4f13c blah blah blah } -} //~ this file contains an unclosed delimiter +} //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr b/tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr index b33f2b5d1b8b2..a5558352b6947 100644 --- a/tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr +++ b/tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/unclosed-delims-in-macro.rs:11:48 + --> $DIR/unclosed-delims-in-macro.rs:11:54 | LL | macro_rules! foo { | - unclosed delimiter @@ -8,7 +8,7 @@ LL | () { | - this delimiter might not be properly closed... ... LL | } - | - ^ + | - ^ | | | ...as it matches this but it has different indentation diff --git a/tests/ui/parser/diff-markers/unclosed-delims.rs b/tests/ui/parser/diff-markers/unclosed-delims.rs index 827c1eebb9d5f..daf2a9bac98ab 100644 --- a/tests/ui/parser/diff-markers/unclosed-delims.rs +++ b/tests/ui/parser/diff-markers/unclosed-delims.rs @@ -9,4 +9,4 @@ mod tests { fn test2() { >>>>>>> 7a4f13c blah blah blah } -} //~ this file contains an unclosed delimiter +} //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/diff-markers/unclosed-delims.stderr b/tests/ui/parser/diff-markers/unclosed-delims.stderr index b2541aa47baed..cb083a043b8e0 100644 --- a/tests/ui/parser/diff-markers/unclosed-delims.stderr +++ b/tests/ui/parser/diff-markers/unclosed-delims.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/unclosed-delims.rs:12:48 + --> $DIR/unclosed-delims.rs:12:54 | LL | mod tests { | - unclosed delimiter @@ -8,7 +8,7 @@ LL | fn test1() { | - this delimiter might not be properly closed... ... LL | } - | - ^ + | - ^ | | | ...as it matches this but it has different indentation diff --git a/tests/ui/parser/do-catch-suggests-try.rs b/tests/ui/parser/do-catch-suggests-try.rs index f64568d06e96d..af85373e120b5 100644 --- a/tests/ui/parser/do-catch-suggests-try.rs +++ b/tests/ui/parser/do-catch-suggests-try.rs @@ -1,10 +1,12 @@ +//@ dont-require-annotations: NOTE + #![feature(try_blocks)] fn main() { let _: Option<()> = do catch {}; //~^ ERROR found removed `do catch` syntax - //~| replace with the new syntax - //~| following RFC #2388, the new non-placeholder syntax is `try` + //~| HELP replace with the new syntax + //~| NOTE following RFC #2388, the new non-placeholder syntax is `try` let _recovery_witness: () = 1; //~ ERROR mismatched types } diff --git a/tests/ui/parser/do-catch-suggests-try.stderr b/tests/ui/parser/do-catch-suggests-try.stderr index 2eaab8360759b..eecf588c5e993 100644 --- a/tests/ui/parser/do-catch-suggests-try.stderr +++ b/tests/ui/parser/do-catch-suggests-try.stderr @@ -1,5 +1,5 @@ error: found removed `do catch` syntax - --> $DIR/do-catch-suggests-try.rs:4:25 + --> $DIR/do-catch-suggests-try.rs:6:25 | LL | let _: Option<()> = do catch {}; | ^^^^^^^^ @@ -12,7 +12,7 @@ LL + let _: Option<()> = try {}; | error[E0308]: mismatched types - --> $DIR/do-catch-suggests-try.rs:9:33 + --> $DIR/do-catch-suggests-try.rs:11:33 | LL | let _recovery_witness: () = 1; | -- ^ expected `()`, found integer diff --git a/tests/ui/parser/expr-rarrow-call-on-a-raw-pointer.rs b/tests/ui/parser/expr-rarrow-call-on-a-raw-pointer.rs new file mode 100644 index 0000000000000..0ce5e233930e0 --- /dev/null +++ b/tests/ui/parser/expr-rarrow-call-on-a-raw-pointer.rs @@ -0,0 +1,22 @@ +#![allow( + dead_code, + unused_must_use +)] + +struct Named { + foo: usize, +} + +struct Unnamed(usize); + +unsafe fn named_struct_field_access(named: *mut Named) { + named->foo += 1; //~ ERROR `->` is not valid syntax for field accesses and method calls + //~^ ERROR no field `foo` on type `*mut Named` +} + +unsafe fn unnamed_struct_field_access(unnamed: *mut Unnamed) { + unnamed->0 += 1; //~ ERROR `->` is not valid syntax for field accesses and method calls + //~^ ERROR no field `0` on type `*mut Unnamed` +} + +fn main() {} diff --git a/tests/ui/parser/expr-rarrow-call-on-a-raw-pointer.stderr b/tests/ui/parser/expr-rarrow-call-on-a-raw-pointer.stderr new file mode 100644 index 0000000000000..45f3c5549f7ef --- /dev/null +++ b/tests/ui/parser/expr-rarrow-call-on-a-raw-pointer.stderr @@ -0,0 +1,53 @@ +error: `->` is not valid syntax for field accesses and method calls + --> $DIR/expr-rarrow-call-on-a-raw-pointer.rs:13:10 + | +LL | named->foo += 1; + | ^^ + | + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer +help: try using `.` instead + | +LL - named->foo += 1; +LL + named.foo += 1; + | + +error: `->` is not valid syntax for field accesses and method calls + --> $DIR/expr-rarrow-call-on-a-raw-pointer.rs:18:12 + | +LL | unnamed->0 += 1; + | ^^ + | + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer +help: try using `.` instead + | +LL - unnamed->0 += 1; +LL + unnamed.0 += 1; + | + +error[E0609]: no field `foo` on type `*mut Named` + --> $DIR/expr-rarrow-call-on-a-raw-pointer.rs:13:12 + | +LL | named->foo += 1; + | ^^^ unknown field + | +help: `named` is a raw pointer; try dereferencing it + | +LL - named->foo += 1; +LL + (*named).foo += 1; + | + +error[E0609]: no field `0` on type `*mut Unnamed` + --> $DIR/expr-rarrow-call-on-a-raw-pointer.rs:18:14 + | +LL | unnamed->0 += 1; + | ^ unknown field + | +help: `unnamed` is a raw pointer; try dereferencing it + | +LL - unnamed->0 += 1; +LL + (*unnamed).0 += 1; + | + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0609`. diff --git a/tests/ui/parser/expr-rarrow-call.fixed b/tests/ui/parser/expr-rarrow-call.fixed index 9a05e20092dd8..c97284c4b01e7 100644 --- a/tests/ui/parser/expr-rarrow-call.fixed +++ b/tests/ui/parser/expr-rarrow-call.fixed @@ -11,23 +11,23 @@ struct Named { struct Unnamed(usize); fn named_struct_field_access(named: &Named) { - named.foo; //~ ERROR `->` used for field access or method call + named.foo; //~ ERROR `->` is not valid syntax for field accesses and method calls } fn unnamed_struct_field_access(unnamed: &Unnamed) { - unnamed.0; //~ ERROR `->` used for field access or method call + unnamed.0; //~ ERROR `->` is not valid syntax for field accesses and method calls } fn tuple_field_access(t: &(u8, u8)) { - t.0; //~ ERROR `->` used for field access or method call - t.1; //~ ERROR `->` used for field access or method call + t.0; //~ ERROR `->` is not valid syntax for field accesses and method calls + t.1; //~ ERROR `->` is not valid syntax for field accesses and method calls } #[derive(Clone)] struct Foo; fn method_call(foo: &Foo) { - foo.clone(); //~ ERROR `->` used for field access or method call + foo.clone(); //~ ERROR `->` is not valid syntax for field accesses and method calls } fn main() {} diff --git a/tests/ui/parser/expr-rarrow-call.rs b/tests/ui/parser/expr-rarrow-call.rs index 760b0f6f345b9..78cd72b12ec85 100644 --- a/tests/ui/parser/expr-rarrow-call.rs +++ b/tests/ui/parser/expr-rarrow-call.rs @@ -11,23 +11,23 @@ struct Named { struct Unnamed(usize); fn named_struct_field_access(named: &Named) { - named->foo; //~ ERROR `->` used for field access or method call + named->foo; //~ ERROR `->` is not valid syntax for field accesses and method calls } fn unnamed_struct_field_access(unnamed: &Unnamed) { - unnamed->0; //~ ERROR `->` used for field access or method call + unnamed->0; //~ ERROR `->` is not valid syntax for field accesses and method calls } fn tuple_field_access(t: &(u8, u8)) { - t->0; //~ ERROR `->` used for field access or method call - t->1; //~ ERROR `->` used for field access or method call + t->0; //~ ERROR `->` is not valid syntax for field accesses and method calls + t->1; //~ ERROR `->` is not valid syntax for field accesses and method calls } #[derive(Clone)] struct Foo; fn method_call(foo: &Foo) { - foo->clone(); //~ ERROR `->` used for field access or method call + foo->clone(); //~ ERROR `->` is not valid syntax for field accesses and method calls } fn main() {} diff --git a/tests/ui/parser/expr-rarrow-call.stderr b/tests/ui/parser/expr-rarrow-call.stderr index 2e168ca26fecc..0b105273861f8 100644 --- a/tests/ui/parser/expr-rarrow-call.stderr +++ b/tests/ui/parser/expr-rarrow-call.stderr @@ -1,62 +1,62 @@ -error: `->` used for field access or method call +error: `->` is not valid syntax for field accesses and method calls --> $DIR/expr-rarrow-call.rs:14:10 | LL | named->foo; | ^^ | - = help: the `.` operator will dereference the value if needed + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer help: try using `.` instead | LL - named->foo; LL + named.foo; | -error: `->` used for field access or method call +error: `->` is not valid syntax for field accesses and method calls --> $DIR/expr-rarrow-call.rs:18:12 | LL | unnamed->0; | ^^ | - = help: the `.` operator will dereference the value if needed + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer help: try using `.` instead | LL - unnamed->0; LL + unnamed.0; | -error: `->` used for field access or method call +error: `->` is not valid syntax for field accesses and method calls --> $DIR/expr-rarrow-call.rs:22:6 | LL | t->0; | ^^ | - = help: the `.` operator will dereference the value if needed + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer help: try using `.` instead | LL - t->0; LL + t.0; | -error: `->` used for field access or method call +error: `->` is not valid syntax for field accesses and method calls --> $DIR/expr-rarrow-call.rs:23:6 | LL | t->1; | ^^ | - = help: the `.` operator will dereference the value if needed + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer help: try using `.` instead | LL - t->1; LL + t.1; | -error: `->` used for field access or method call +error: `->` is not valid syntax for field accesses and method calls --> $DIR/expr-rarrow-call.rs:30:8 | LL | foo->clone(); | ^^ | - = help: the `.` operator will dereference the value if needed + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer help: try using `.` instead | LL - foo->clone(); diff --git a/tests/ui/parser/extern-abi-from-mac-literal-frag.rs b/tests/ui/parser/extern-abi-from-mac-literal-frag.rs index a4e9134218cea..12b6c98705ce6 100644 --- a/tests/ui/parser/extern-abi-from-mac-literal-frag.rs +++ b/tests/ui/parser/extern-abi-from-mac-literal-frag.rs @@ -12,6 +12,8 @@ macro_rules! abi_from_lit_frag { fn _import(); } + unsafe extern $abi {} + extern $abi fn _export() {} type _PTR = extern $abi fn(); @@ -24,6 +26,8 @@ macro_rules! abi_from_expr_frag { fn _import(); } + unsafe extern $abi {} + extern $abi fn _export() {} type _PTR = extern $abi fn(); diff --git a/tests/ui/parser/extern-abi-syntactic.rs b/tests/ui/parser/extern-abi-syntactic.rs index d3e2ba0e2d3f6..28565a3f4befd 100644 --- a/tests/ui/parser/extern-abi-syntactic.rs +++ b/tests/ui/parser/extern-abi-syntactic.rs @@ -5,13 +5,13 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] extern "some_abi_that_we_are_sure_does_not_exist_semantically" fn foo() {} -#[cfg(FALSE)] +#[cfg(false)] extern "some_abi_that_we_are_sure_does_not_exist_semantically" { fn foo(); } -#[cfg(FALSE)] +#[cfg(false)] type T = extern "some_abi_that_we_are_sure_does_not_exist_semantically" fn(); diff --git a/tests/ui/parser/extern-crate-async.rs b/tests/ui/parser/extern-crate-async.rs index 7c7769075b658..529e0f1ab5cb8 100644 --- a/tests/ui/parser/extern-crate-async.rs +++ b/tests/ui/parser/extern-crate-async.rs @@ -5,8 +5,8 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] extern crate async; -#[cfg(FALSE)] +#[cfg(false)] extern crate async as something_else; diff --git a/tests/ui/parser/float-field-interpolated.rs b/tests/ui/parser/float-field-interpolated.rs index 990f2926dc861..bf7163039c420 100644 --- a/tests/ui/parser/float-field-interpolated.rs +++ b/tests/ui/parser/float-field-interpolated.rs @@ -5,10 +5,10 @@ macro_rules! generate_field_accesses { let s = S(0, (0, 0)); s.$a; // OK - { s.$b; } //~ ERROR unexpected token: `1.1` - //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found literal `1.1` - { s.$c; } //~ ERROR unexpected token: `1.1` - //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found expression `1.1` + { s.$b; } //~ ERROR unexpected token: `literal` metavariable + //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `literal` metavariable + { s.$c; } //~ ERROR unexpected token: `expr` metavariable + //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `expr` metavariable }; } diff --git a/tests/ui/parser/float-field-interpolated.stderr b/tests/ui/parser/float-field-interpolated.stderr index 2a1a4926cb3c6..e2b7e3a7dbe75 100644 --- a/tests/ui/parser/float-field-interpolated.stderr +++ b/tests/ui/parser/float-field-interpolated.stderr @@ -1,4 +1,4 @@ -error: unexpected token: `1.1` +error: unexpected token: `literal` metavariable --> $DIR/float-field-interpolated.rs:8:13 | LL | { s.$b; } @@ -9,7 +9,7 @@ LL | generate_field_accesses!(1.1, 1.1, 1.1); | = note: this error originates in the macro `generate_field_accesses` (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected one of `.`, `;`, `?`, `}`, or an operator, found literal `1.1` +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `literal` metavariable --> $DIR/float-field-interpolated.rs:8:13 | LL | { s.$b; } @@ -20,7 +20,7 @@ LL | generate_field_accesses!(1.1, 1.1, 1.1); | = note: this error originates in the macro `generate_field_accesses` (in Nightly builds, run with -Z macro-backtrace for more info) -error: unexpected token: `1.1` +error: unexpected token: `expr` metavariable --> $DIR/float-field-interpolated.rs:10:13 | LL | { s.$c; } @@ -31,7 +31,7 @@ LL | generate_field_accesses!(1.1, 1.1, 1.1); | = note: this error originates in the macro `generate_field_accesses` (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected one of `.`, `;`, `?`, `}`, or an operator, found expression `1.1` +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `expr` metavariable --> $DIR/float-field-interpolated.rs:10:13 | LL | { s.$c; } diff --git a/tests/ui/parser/float-field.rs b/tests/ui/parser/float-field.rs index 59fefee26aa6f..9bf4d90fb46b5 100644 --- a/tests/ui/parser/float-field.rs +++ b/tests/ui/parser/float-field.rs @@ -39,11 +39,11 @@ fn main() { { s.0x1.1; } //~ ERROR hexadecimal float literal is not supported //~| ERROR unexpected token: `0x1.1` - //~| expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1` + //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1` { s.0x1.1e1; } //~ ERROR hexadecimal float literal is not supported //~| ERROR unexpected token: `0x1.1e1` - //~| expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1e1` + //~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1e1` { s.0x1e+; } //~ ERROR expected expression, found `;` diff --git a/tests/ui/parser/fn-arg-doc-comment.rs b/tests/ui/parser/fn-arg-doc-comment.rs index 21d753ad03725..57a4d15fa2564 100644 --- a/tests/ui/parser/fn-arg-doc-comment.rs +++ b/tests/ui/parser/fn-arg-doc-comment.rs @@ -2,12 +2,10 @@ pub fn f( //~ NOTE function defined here /// Comment //~^ ERROR documentation comments cannot be applied to function parameters //~| NOTE doc comments are not allowed here - //~| NOTE id: u8, /// Other //~^ ERROR documentation comments cannot be applied to function parameters //~| NOTE doc comments are not allowed here - //~| NOTE a: u8, ) {} @@ -15,7 +13,6 @@ fn bar(id: #[allow(dead_code)] i32) {} //~^ ERROR attributes cannot be applied to a function parameter's type //~| NOTE attributes are not allowed here //~| NOTE function defined here -//~| NOTE fn main() { // verify that the parser recovered and properly typechecked the args diff --git a/tests/ui/parser/fn-arg-doc-comment.stderr b/tests/ui/parser/fn-arg-doc-comment.stderr index 1891c70890342..84c8bb3c2d094 100644 --- a/tests/ui/parser/fn-arg-doc-comment.stderr +++ b/tests/ui/parser/fn-arg-doc-comment.stderr @@ -1,5 +1,5 @@ error: attributes cannot be applied to a function parameter's type - --> $DIR/fn-arg-doc-comment.rs:14:12 + --> $DIR/fn-arg-doc-comment.rs:12:12 | LL | fn bar(id: #[allow(dead_code)] i32) {} | ^^^^^^^^^^^^^^^^^^^ attributes are not allowed here @@ -11,13 +11,13 @@ LL | /// Comment | ^^^^^^^^^^^ doc comments are not allowed here error: documentation comments cannot be applied to function parameters - --> $DIR/fn-arg-doc-comment.rs:7:5 + --> $DIR/fn-arg-doc-comment.rs:6:5 | LL | /// Other | ^^^^^^^^^ doc comments are not allowed here error[E0308]: arguments to this function are incorrect - --> $DIR/fn-arg-doc-comment.rs:22:5 + --> $DIR/fn-arg-doc-comment.rs:19:5 | LL | f("", ""); | ^ -- -- expected `u8`, found `&str` @@ -39,7 +39,7 @@ LL | | a: u8, | |_________- error[E0308]: mismatched types - --> $DIR/fn-arg-doc-comment.rs:26:9 + --> $DIR/fn-arg-doc-comment.rs:23:9 | LL | bar(""); | --- ^^ expected `i32`, found `&str` @@ -47,7 +47,7 @@ LL | bar(""); | arguments to this function are incorrect | note: function defined here - --> $DIR/fn-arg-doc-comment.rs:14:4 + --> $DIR/fn-arg-doc-comment.rs:12:4 | LL | fn bar(id: #[allow(dead_code)] i32) {} | ^^^ --------------------------- diff --git a/tests/ui/parser/fn-body-optional-syntactic-pass.rs b/tests/ui/parser/fn-body-optional-syntactic-pass.rs index 140471dfc774b..762247e63e99c 100644 --- a/tests/ui/parser/fn-body-optional-syntactic-pass.rs +++ b/tests/ui/parser/fn-body-optional-syntactic-pass.rs @@ -4,7 +4,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn syntax() { fn f(); fn f() {} diff --git a/tests/ui/parser/fn-header-syntactic-pass.rs b/tests/ui/parser/fn-header-syntactic-pass.rs index 065ded31b0730..1e15886e5645f 100644 --- a/tests/ui/parser/fn-header-syntactic-pass.rs +++ b/tests/ui/parser/fn-header-syntactic-pass.rs @@ -5,7 +5,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn syntax() { async fn f(); unsafe fn f(); diff --git a/tests/ui/parser/foreign-const-syntactic-fail.rs b/tests/ui/parser/foreign-const-syntactic-fail.rs index a6e77f846638e..fc3cd0b3430df 100644 --- a/tests/ui/parser/foreign-const-syntactic-fail.rs +++ b/tests/ui/parser/foreign-const-syntactic-fail.rs @@ -2,7 +2,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] extern "C" { const A: isize; //~ ERROR extern items cannot be `const` const B: isize = 42; //~ ERROR extern items cannot be `const` diff --git a/tests/ui/parser/foreign-static-syntactic-pass.rs b/tests/ui/parser/foreign-static-syntactic-pass.rs index a76b9bab49112..d7c21c672ab6c 100644 --- a/tests/ui/parser/foreign-static-syntactic-pass.rs +++ b/tests/ui/parser/foreign-static-syntactic-pass.rs @@ -4,7 +4,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] extern "C" { static X: u8; static mut Y: u8; diff --git a/tests/ui/parser/foreign-ty-syntactic-pass.rs b/tests/ui/parser/foreign-ty-syntactic-pass.rs index 50bb68cd83be2..337276852010a 100644 --- a/tests/ui/parser/foreign-ty-syntactic-pass.rs +++ b/tests/ui/parser/foreign-ty-syntactic-pass.rs @@ -2,7 +2,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] extern "C" { type A: Ord; type A<'a> where 'a: 'static; diff --git a/tests/ui/parser/impl-item-const-pass.rs b/tests/ui/parser/impl-item-const-pass.rs index 8ebdf633b5b97..6ca4cd9cd9306 100644 --- a/tests/ui/parser/impl-item-const-pass.rs +++ b/tests/ui/parser/impl-item-const-pass.rs @@ -2,7 +2,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] impl X { const Y: u8; } diff --git a/tests/ui/parser/impl-item-fn-no-body-pass.rs b/tests/ui/parser/impl-item-fn-no-body-pass.rs index 5a593fe1d124d..b8269fc42708f 100644 --- a/tests/ui/parser/impl-item-fn-no-body-pass.rs +++ b/tests/ui/parser/impl-item-fn-no-body-pass.rs @@ -2,7 +2,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] impl X { fn f(); } diff --git a/tests/ui/parser/impl-item-type-no-body-pass.rs b/tests/ui/parser/impl-item-type-no-body-pass.rs index 039825bcc538c..979b5f7659600 100644 --- a/tests/ui/parser/impl-item-type-no-body-pass.rs +++ b/tests/ui/parser/impl-item-type-no-body-pass.rs @@ -2,7 +2,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] impl X { type Y; type Z: Ord; diff --git a/tests/ui/parser/impl-parsing-2.rs b/tests/ui/parser/impl-parsing-2.rs new file mode 100644 index 0000000000000..7a71217b21c59 --- /dev/null +++ b/tests/ui/parser/impl-parsing-2.rs @@ -0,0 +1,4 @@ +impl ! {} // OK + +default unsafe FAIL //~ ERROR expected item, found keyword `unsafe` +//~^ ERROR `default` is not followed by an item diff --git a/tests/ui/parser/impl-parsing-2.stderr b/tests/ui/parser/impl-parsing-2.stderr new file mode 100644 index 0000000000000..45e2c428242d6 --- /dev/null +++ b/tests/ui/parser/impl-parsing-2.stderr @@ -0,0 +1,18 @@ +error: `default` is not followed by an item + --> $DIR/impl-parsing-2.rs:3:1 + | +LL | default unsafe FAIL + | ^^^^^^^ the `default` qualifier + | + = note: only `fn`, `const`, `type`, or `impl` items may be prefixed by `default` + +error: expected item, found keyword `unsafe` + --> $DIR/impl-parsing-2.rs:3:9 + | +LL | default unsafe FAIL + | ^^^^^^ expected item + | + = note: for a full list of items that can appear in modules, see + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/impl-parsing.rs b/tests/ui/parser/impl-parsing.rs index 80ce888557078..7692a81dd42cd 100644 --- a/tests/ui/parser/impl-parsing.rs +++ b/tests/ui/parser/impl-parsing.rs @@ -2,9 +2,4 @@ impl ! {} // OK impl ! where u8: Copy {} // OK impl Trait Type {} //~ ERROR missing `for` in a trait impl -impl Trait .. {} //~ ERROR missing `for` in a trait impl impl ?Sized for Type {} //~ ERROR expected a trait, found type -impl ?Sized for .. {} //~ ERROR expected a trait, found type - -default unsafe FAIL //~ ERROR expected item, found keyword `unsafe` -//~^ ERROR `default` is not followed by an item diff --git a/tests/ui/parser/impl-parsing.stderr b/tests/ui/parser/impl-parsing.stderr index 6a24a9453e631..b2512120dc87c 100644 --- a/tests/ui/parser/impl-parsing.stderr +++ b/tests/ui/parser/impl-parsing.stderr @@ -9,44 +9,11 @@ help: add `for` here LL | impl Trait for Type {} | +++ -error: missing `for` in a trait impl - --> $DIR/impl-parsing.rs:5:11 - | -LL | impl Trait .. {} - | ^ - | -help: add `for` here - | -LL | impl Trait for .. {} - | +++ - error: expected a trait, found type - --> $DIR/impl-parsing.rs:6:6 + --> $DIR/impl-parsing.rs:5:6 | LL | impl ?Sized for Type {} | ^^^^^^ -error: expected a trait, found type - --> $DIR/impl-parsing.rs:7:6 - | -LL | impl ?Sized for .. {} - | ^^^^^^ - -error: `default` is not followed by an item - --> $DIR/impl-parsing.rs:9:1 - | -LL | default unsafe FAIL - | ^^^^^^^ the `default` qualifier - | - = note: only `fn`, `const`, `type`, or `impl` items may be prefixed by `default` - -error: expected item, found keyword `unsafe` - --> $DIR/impl-parsing.rs:9:9 - | -LL | default unsafe FAIL - | ^^^^^^ expected item - | - = note: for a full list of items that can appear in modules, see - -error: aborting due to 6 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/parser/import-from-path.rs b/tests/ui/parser/import-from-path.rs index 54349d4971eff..0d38c7224c518 100644 --- a/tests/ui/parser/import-from-path.rs +++ b/tests/ui/parser/import-from-path.rs @@ -1,2 +1 @@ -//@ error-pattern:expected -use foo::{bar}::baz +use foo::{bar}::baz //~ ERROR expected `;`, found `::` diff --git a/tests/ui/parser/import-from-path.stderr b/tests/ui/parser/import-from-path.stderr index b63e48d6679a6..f70c3b2e87076 100644 --- a/tests/ui/parser/import-from-path.stderr +++ b/tests/ui/parser/import-from-path.stderr @@ -1,5 +1,5 @@ error: expected `;`, found `::` - --> $DIR/import-from-path.rs:2:15 + --> $DIR/import-from-path.rs:1:15 | LL | use foo::{bar}::baz | ^^ expected `;` diff --git a/tests/ui/parser/import-from-rename.rs b/tests/ui/parser/import-from-rename.rs index f6a4bb55553c2..4929b270e45f9 100644 --- a/tests/ui/parser/import-from-rename.rs +++ b/tests/ui/parser/import-from-rename.rs @@ -1,6 +1,4 @@ -//@ error-pattern:expected - -use foo::{bar} as baz; +use foo::{bar} as baz; //~ ERROR expected `;`, found keyword `as` mod foo { pub fn bar() {} diff --git a/tests/ui/parser/import-from-rename.stderr b/tests/ui/parser/import-from-rename.stderr index 2f267a8d02694..3289f1bdbe72e 100644 --- a/tests/ui/parser/import-from-rename.stderr +++ b/tests/ui/parser/import-from-rename.stderr @@ -1,5 +1,5 @@ error: expected `;`, found keyword `as` - --> $DIR/import-from-rename.rs:3:16 + --> $DIR/import-from-rename.rs:1:16 | LL | use foo::{bar} as baz; | ^^ expected `;` diff --git a/tests/ui/parser/import-glob-path.rs b/tests/ui/parser/import-glob-path.rs index cb854de0cff69..3f1c98fcade93 100644 --- a/tests/ui/parser/import-glob-path.rs +++ b/tests/ui/parser/import-glob-path.rs @@ -1,2 +1 @@ -//@ error-pattern:expected -use foo::*::bar +use foo::*::bar //~ ERROR expected `;`, found `::` diff --git a/tests/ui/parser/import-glob-path.stderr b/tests/ui/parser/import-glob-path.stderr index 3bde32d1ea4d3..04f8a6e96fad3 100644 --- a/tests/ui/parser/import-glob-path.stderr +++ b/tests/ui/parser/import-glob-path.stderr @@ -1,5 +1,5 @@ error: expected `;`, found `::` - --> $DIR/import-glob-path.rs:2:11 + --> $DIR/import-glob-path.rs:1:11 | LL | use foo::*::bar | ^^ expected `;` diff --git a/tests/ui/parser/import-glob-rename.rs b/tests/ui/parser/import-glob-rename.rs index 899818b15b6f6..5027a75f881eb 100644 --- a/tests/ui/parser/import-glob-rename.rs +++ b/tests/ui/parser/import-glob-rename.rs @@ -1,6 +1,4 @@ -//@ error-pattern:expected - -use foo::* as baz; +use foo::* as baz; //~ ERROR expected `;`, found keyword `as` mod foo { pub fn bar() {} diff --git a/tests/ui/parser/import-glob-rename.stderr b/tests/ui/parser/import-glob-rename.stderr index 24e6c3f000632..2a5f2b66404dc 100644 --- a/tests/ui/parser/import-glob-rename.stderr +++ b/tests/ui/parser/import-glob-rename.stderr @@ -1,5 +1,5 @@ error: expected `;`, found keyword `as` - --> $DIR/import-glob-rename.rs:3:12 + --> $DIR/import-glob-rename.rs:1:12 | LL | use foo::* as baz; | ^^ expected `;` diff --git a/tests/ui/parser/intersection-patterns-1.fixed b/tests/ui/parser/intersection-patterns-1.fixed index f63d57472cf10..8ade795f7eef9 100644 --- a/tests/ui/parser/intersection-patterns-1.fixed +++ b/tests/ui/parser/intersection-patterns-1.fixed @@ -16,8 +16,8 @@ fn main() { match s { y @ Some(x) => {} //~^ ERROR pattern on wrong side of `@` - //~| pattern on the left, should be on the right - //~| binding on the right, should be on the left + //~| NOTE pattern on the left, should be on the right + //~| NOTE binding on the right, should be on the left //~| HELP switch the order //~| SUGGESTION y @ Some(x) _ => {} @@ -26,8 +26,8 @@ fn main() { match 2 { e @ 1..=5 => {} //~^ ERROR pattern on wrong side of `@` - //~| pattern on the left, should be on the right - //~| binding on the right, should be on the left + //~| NOTE pattern on the left, should be on the right + //~| NOTE binding on the right, should be on the left //~| HELP switch the order //~| SUGGESTION e @ 1..=5 _ => {} diff --git a/tests/ui/parser/intersection-patterns-1.rs b/tests/ui/parser/intersection-patterns-1.rs index 3a457659aac2e..b5a7892fd1c5a 100644 --- a/tests/ui/parser/intersection-patterns-1.rs +++ b/tests/ui/parser/intersection-patterns-1.rs @@ -16,8 +16,8 @@ fn main() { match s { Some(x) @ y => {} //~^ ERROR pattern on wrong side of `@` - //~| pattern on the left, should be on the right - //~| binding on the right, should be on the left + //~| NOTE pattern on the left, should be on the right + //~| NOTE binding on the right, should be on the left //~| HELP switch the order //~| SUGGESTION y @ Some(x) _ => {} @@ -26,8 +26,8 @@ fn main() { match 2 { 1 ..= 5 @ e => {} //~^ ERROR pattern on wrong side of `@` - //~| pattern on the left, should be on the right - //~| binding on the right, should be on the left + //~| NOTE pattern on the left, should be on the right + //~| NOTE binding on the right, should be on the left //~| HELP switch the order //~| SUGGESTION e @ 1..=5 _ => {} diff --git a/tests/ui/parser/intersection-patterns-2.rs b/tests/ui/parser/intersection-patterns-2.rs index 408415e87ef98..387127fc4d97e 100644 --- a/tests/ui/parser/intersection-patterns-2.rs +++ b/tests/ui/parser/intersection-patterns-2.rs @@ -12,8 +12,8 @@ fn main() { match s { Some(x) @ Some(y) => {} //~^ ERROR left-hand side of `@` must be a binding - //~| interpreted as a pattern, not a binding - //~| also a pattern + //~| NOTE interpreted as a pattern, not a binding + //~| NOTE also a pattern //~| NOTE bindings are `x`, `mut x`, `ref x`, and `ref mut x` _ => {} } diff --git a/tests/ui/parser/inverted-parameters.rs b/tests/ui/parser/inverted-parameters.rs index 5c4272504e061..bc2f53f0be1bb 100644 --- a/tests/ui/parser/inverted-parameters.rs +++ b/tests/ui/parser/inverted-parameters.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: SUGGESTION + struct S; impl S { diff --git a/tests/ui/parser/inverted-parameters.stderr b/tests/ui/parser/inverted-parameters.stderr index 8662277820390..7b969032d0f9c 100644 --- a/tests/ui/parser/inverted-parameters.stderr +++ b/tests/ui/parser/inverted-parameters.stderr @@ -1,5 +1,5 @@ error: expected one of `:`, `@`, or `|`, found `bar` - --> $DIR/inverted-parameters.rs:4:24 + --> $DIR/inverted-parameters.rs:6:24 | LL | fn foo(&self, &str bar) {} | -----^^^ @@ -8,7 +8,7 @@ LL | fn foo(&self, &str bar) {} | help: declare the type after the parameter binding: `: ` error: expected one of `:`, `@`, or `|`, found `quux` - --> $DIR/inverted-parameters.rs:10:10 + --> $DIR/inverted-parameters.rs:12:10 | LL | fn baz(S quux, xyzzy: i32) {} | --^^^^ @@ -17,19 +17,19 @@ LL | fn baz(S quux, xyzzy: i32) {} | help: declare the type after the parameter binding: `: ` error: expected one of `:`, `@`, or `|`, found `a` - --> $DIR/inverted-parameters.rs:15:12 + --> $DIR/inverted-parameters.rs:17:12 | LL | fn one(i32 a b) {} | ^ expected one of `:`, `@`, or `|` error: expected one of `:` or `|`, found `(` - --> $DIR/inverted-parameters.rs:18:23 + --> $DIR/inverted-parameters.rs:20:23 | LL | fn pattern((i32, i32) (a, b)) {} | ^ expected one of `:` or `|` error: expected one of `:`, `@`, or `|`, found `)` - --> $DIR/inverted-parameters.rs:21:12 + --> $DIR/inverted-parameters.rs:23:12 | LL | fn fizz(i32) {} | ^ expected one of `:`, `@`, or `|` @@ -49,7 +49,7 @@ LL | fn fizz(_: i32) {} | ++ error: expected one of `:`, `@`, or `|`, found `S` - --> $DIR/inverted-parameters.rs:27:23 + --> $DIR/inverted-parameters.rs:29:23 | LL | fn missing_colon(quux S) {} | -----^ diff --git a/tests/ui/parser/issues/circular-module-with-doc-comment-issue-97589/circular-module-with-doc-comment-issue-97589.rs b/tests/ui/parser/issues/circular-module-with-doc-comment-issue-97589/circular-module-with-doc-comment-issue-97589.rs index ff28548b795fc..9c164813de2a2 100644 --- a/tests/ui/parser/issues/circular-module-with-doc-comment-issue-97589/circular-module-with-doc-comment-issue-97589.rs +++ b/tests/ui/parser/issues/circular-module-with-doc-comment-issue-97589/circular-module-with-doc-comment-issue-97589.rs @@ -1,6 +1,7 @@ -//@ error-pattern: circular modules // Regression test for #97589: a doc-comment on a circular module bypassed cycle detection #![crate_type = "lib"] pub mod recursive; + +//~? ERROR circular modules: $DIR/recursive.rs -> $DIR/recursive.rs diff --git a/tests/ui/parser/issues/circular-module-with-doc-comment-issue-97589/recursive.rs b/tests/ui/parser/issues/circular-module-with-doc-comment-issue-97589/recursive.rs index 3d758be8c0553..2e9a15eb06dae 100644 --- a/tests/ui/parser/issues/circular-module-with-doc-comment-issue-97589/recursive.rs +++ b/tests/ui/parser/issues/circular-module-with-doc-comment-issue-97589/recursive.rs @@ -1,4 +1,4 @@ -//@ ignore-test: this is an auxiliary file for circular-module-with-doc-comment-issue-97589.rs +//@ ignore-auxiliary (used by `./circular-module-with-doc-comment-issue-97589.rs`) //! this comment caused the circular dependency checker to break diff --git a/tests/ui/parser/issues/invalid-parse-format-issue-139104.rs b/tests/ui/parser/issues/invalid-parse-format-issue-139104.rs new file mode 100644 index 0000000000000..7644df8be49a0 --- /dev/null +++ b/tests/ui/parser/issues/invalid-parse-format-issue-139104.rs @@ -0,0 +1,13 @@ +fn main() { + println!("{foo:_1.4}", foo = 3.14); //~ ERROR invalid format string: expected `}`, found `.` + println!("{0:_1.4}", 1.11); //~ ERROR invalid format string: expected `}`, found `.` + println!("{:_1.4}", 3.14); //~ ERROR invalid format string: expected `}`, found `.` + + println!("{foo:_1.4", foo = 3.14); //~ ERROR invalid format string: expected `}`, found `.` + println!("{0:_1.4", 1.11); //~ ERROR invalid format string: expected `}`, found `.` + println!("{:_1.4", 3.14); //~ ERROR invalid format string: expected `}`, found `.` + + println!("{ 0", 1.11); //~ ERROR invalid format string: expected `}`, found `0` + println!("{foo:1.4_1.4}", foo = 3.14); //~ ERROR invalid format string: expected `}`, found `.` + println!("{0:1.4_1.4}", 3.14); //~ ERROR invalid format string: expected `}`, found `.` +} diff --git a/tests/ui/parser/issues/invalid-parse-format-issue-139104.stderr b/tests/ui/parser/issues/invalid-parse-format-issue-139104.stderr new file mode 100644 index 0000000000000..202aa450cab71 --- /dev/null +++ b/tests/ui/parser/issues/invalid-parse-format-issue-139104.stderr @@ -0,0 +1,92 @@ +error: invalid format string: expected `}`, found `.` + --> $DIR/invalid-parse-format-issue-139104.rs:2:22 + | +LL | println!("{foo:_1.4}", foo = 3.14); + | - ^ expected `}` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: invalid format string: expected `}`, found `.` + --> $DIR/invalid-parse-format-issue-139104.rs:3:20 + | +LL | println!("{0:_1.4}", 1.11); + | - ^ expected `}` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: invalid format string: expected `}`, found `.` + --> $DIR/invalid-parse-format-issue-139104.rs:4:19 + | +LL | println!("{:_1.4}", 3.14); + | - ^ expected `}` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: invalid format string: expected `}`, found `.` + --> $DIR/invalid-parse-format-issue-139104.rs:6:22 + | +LL | println!("{foo:_1.4", foo = 3.14); + | - ^ expected `}` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: invalid format string: expected `}`, found `.` + --> $DIR/invalid-parse-format-issue-139104.rs:7:20 + | +LL | println!("{0:_1.4", 1.11); + | - ^ expected `}` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: invalid format string: expected `}`, found `.` + --> $DIR/invalid-parse-format-issue-139104.rs:8:19 + | +LL | println!("{:_1.4", 3.14); + | - ^ expected `}` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: invalid format string: expected `}`, found `0` + --> $DIR/invalid-parse-format-issue-139104.rs:10:18 + | +LL | println!("{ 0", 1.11); + | - ^ expected `}` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: invalid format string: expected `}`, found `.` + --> $DIR/invalid-parse-format-issue-139104.rs:11:25 + | +LL | println!("{foo:1.4_1.4}", foo = 3.14); + | - ^ expected `}` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: invalid format string: expected `}`, found `.` + --> $DIR/invalid-parse-format-issue-139104.rs:12:23 + | +LL | println!("{0:1.4_1.4}", 3.14); + | - ^ expected `}` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: aborting due to 9 previous errors + diff --git a/tests/ui/parser/issues/issue-101477-enum.fixed b/tests/ui/parser/issues/issue-101477-enum.fixed index 92c2b7c470f94..7fd08b65929d4 100644 --- a/tests/ui/parser/issues/issue-101477-enum.fixed +++ b/tests/ui/parser/issues/issue-101477-enum.fixed @@ -4,7 +4,7 @@ enum Demo { A = 1, B = 2 //~ ERROR unexpected `==` - //~^ expected item, found `==` + //~^ ERROR expected item, found `==` } fn main() {} diff --git a/tests/ui/parser/issues/issue-101477-enum.rs b/tests/ui/parser/issues/issue-101477-enum.rs index 21d377384d3f9..2e9d6b25c1446 100644 --- a/tests/ui/parser/issues/issue-101477-enum.rs +++ b/tests/ui/parser/issues/issue-101477-enum.rs @@ -4,7 +4,7 @@ enum Demo { A = 1, B == 2 //~ ERROR unexpected `==` - //~^ expected item, found `==` + //~^ ERROR expected item, found `==` } fn main() {} diff --git a/tests/ui/parser/issues/issue-102806.rs b/tests/ui/parser/issues/issue-102806.rs index ba297bdc9677b..5ee8c5c1e3de0 100644 --- a/tests/ui/parser/issues/issue-102806.rs +++ b/tests/ui/parser/issues/issue-102806.rs @@ -15,7 +15,7 @@ fn pz(v: V3) { //~^ ERROR expected `..` let _ = V3 { z: 0.0, ... }; - //~^ expected identifier + //~^ ERROR expected identifier //~| ERROR missing fields `x` and `y` in initializer of `V3` let V3 { z: val, ... } = v; diff --git a/tests/ui/parser/issues/issue-103381.fixed b/tests/ui/parser/issues/issue-103381.fixed index 87c308789a18e..955b246b86368 100644 --- a/tests/ui/parser/issues/issue-103381.fixed +++ b/tests/ui/parser/issues/issue-103381.fixed @@ -1,6 +1,6 @@ +//@ edition: 2024 //@ run-rustfix -#![feature(let_chains)] #![allow(unused_variables)] #![allow(dead_code)] #![allow(irrefutable_let_patterns)] diff --git a/tests/ui/parser/issues/issue-103381.rs b/tests/ui/parser/issues/issue-103381.rs index ccbc40e5d0216..d4b06b9c77016 100644 --- a/tests/ui/parser/issues/issue-103381.rs +++ b/tests/ui/parser/issues/issue-103381.rs @@ -1,6 +1,6 @@ +//@ edition: 2024 //@ run-rustfix -#![feature(let_chains)] #![allow(unused_variables)] #![allow(dead_code)] #![allow(irrefutable_let_patterns)] diff --git a/tests/ui/parser/issues/issue-103451.rs b/tests/ui/parser/issues/issue-103451.rs index 6b0928229e912..687dbc632d678 100644 --- a/tests/ui/parser/issues/issue-103451.rs +++ b/tests/ui/parser/issues/issue-103451.rs @@ -1,4 +1,4 @@ -//@ error-pattern: this file contains an unclosed delimiter struct R { } +//~vv ERROR this file contains an unclosed delimiter struct S { x: [u8; R diff --git a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs index 1c28c0632fa84..60dd88e65400a 100644 --- a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs +++ b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs @@ -2,7 +2,4 @@ struct Apple((Apple, Option(Banana ? Citron))); //~^ ERROR invalid `?` in type -//~| ERROR expected one of `)` or `,`, found `Citron` -//~| ERROR cannot find type `Citron` in this scope [E0412] -//~| ERROR parenthesized type parameters may only be used with a `Fn` trait [E0214] -//~| ERROR `Apple` has infinite size +//~| ERROR unexpected token: `Citron` diff --git a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr index 97a73b4fd5ed3..c92535c3906bc 100644 --- a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr +++ b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr @@ -10,44 +10,11 @@ LL - struct Apple((Apple, Option(Banana ? Citron))); LL + struct Apple((Apple, Option(Option Citron))); | -error: expected one of `)` or `,`, found `Citron` +error: unexpected token: `Citron` --> $DIR/issue-103748-ICE-wrong-braces.rs:3:38 | LL | struct Apple((Apple, Option(Banana ? Citron))); - | -^^^^^^ expected one of `)` or `,` - | | - | help: missing `,` + | ^^^^^^ unexpected token after this -error[E0412]: cannot find type `Citron` in this scope - --> $DIR/issue-103748-ICE-wrong-braces.rs:3:38 - | -LL | struct Apple((Apple, Option(Banana ? Citron))); - | ^^^^^^ not found in this scope - -error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/issue-103748-ICE-wrong-braces.rs:3:22 - | -LL | struct Apple((Apple, Option(Banana ? Citron))); - | ^^^^^^^^^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses - | -help: use angle brackets instead - | -LL - struct Apple((Apple, Option(Banana ? Citron))); -LL + struct Apple((Apple, Option)); - | - -error[E0072]: recursive type `Apple` has infinite size - --> $DIR/issue-103748-ICE-wrong-braces.rs:3:1 - | -LL | struct Apple((Apple, Option(Banana ? Citron))); - | ^^^^^^^^^^^^ ----- recursive without indirection - | -help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle - | -LL | struct Apple((Box, Option(Banana ? Citron))); - | ++++ + - -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0072, E0214, E0412. -For more information about an error, try `rustc --explain E0072`. diff --git a/tests/ui/parser/issues/issue-10636-2.rs b/tests/ui/parser/issues/issue-10636-2.rs index 7200ea1f1dd1f..dcc03fec545cd 100644 --- a/tests/ui/parser/issues/issue-10636-2.rs +++ b/tests/ui/parser/issues/issue-10636-2.rs @@ -1,7 +1,7 @@ -//@ error-pattern: mismatched closing delimiter: `}` // FIXME(31528) we emit a bunch of silly errors here due to continuing past the // first one. This would be easy-ish to address by better recovery in tokenisation. +//~vvvvv ERROR mismatched closing delimiter: `}` pub fn trace_option(option: Option) { option.map(|some| 42; diff --git a/tests/ui/parser/issues/issue-111692.rs b/tests/ui/parser/issues/issue-111692.rs deleted file mode 100644 index 56096f706a8a7..0000000000000 --- a/tests/ui/parser/issues/issue-111692.rs +++ /dev/null @@ -1,32 +0,0 @@ -mod module { - #[derive(Eq, PartialEq)] - pub struct Type { - pub x: u8, - pub y: u8, - } - - pub const C: u8 = 32u8; -} - -fn test(x: module::Type) { - if x == module::Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal - } -} - -fn test2(x: module::Type) { - if x ==module::Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal - } -} - - -fn test3(x: module::Type) { - if x == Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal - } -} - -fn test4(x: module::Type) { - if x == demo_module::Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal - } -} - -fn main() { } diff --git a/tests/ui/parser/issues/issue-111692.stderr b/tests/ui/parser/issues/issue-111692.stderr deleted file mode 100644 index 068b0483b0fd3..0000000000000 --- a/tests/ui/parser/issues/issue-111692.stderr +++ /dev/null @@ -1,46 +0,0 @@ -error: invalid struct literal - --> $DIR/issue-111692.rs:12:21 - | -LL | if x == module::Type { x: module::C, y: 1 } { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: you might need to surround the struct literal with parentheses - | -LL | if x == (module::Type { x: module::C, y: 1 }) { - | + + - -error: invalid struct literal - --> $DIR/issue-111692.rs:17:20 - | -LL | if x ==module::Type { x: module::C, y: 1 } { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: you might need to surround the struct literal with parentheses - | -LL | if x ==(module::Type { x: module::C, y: 1 }) { - | + + - -error: invalid struct literal - --> $DIR/issue-111692.rs:23:13 - | -LL | if x == Type { x: module::C, y: 1 } { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: you might need to surround the struct literal with parentheses - | -LL | if x == (Type { x: module::C, y: 1 }) { - | + + - -error: invalid struct literal - --> $DIR/issue-111692.rs:28:26 - | -LL | if x == demo_module::Type { x: module::C, y: 1 } { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: you might need to surround the struct literal with parentheses - | -LL | if x == (demo_module::Type { x: module::C, y: 1 }) { - | + + - -error: aborting due to 4 previous errors - diff --git a/tests/ui/parser/issues/issue-118530-ice.rs b/tests/ui/parser/issues/issue-118530-ice.rs index cf14eebec2b76..8930eb86c6b24 100644 --- a/tests/ui/parser/issues/issue-118530-ice.rs +++ b/tests/ui/parser/issues/issue-118530-ice.rs @@ -5,7 +5,7 @@ fn bar() -> String { attr::fn bar() -> String { //~ ERROR expected identifier, found keyword `fn` //~^ ERROR expected one of `(`, `.`, `::`, `;`, `?`, `}`, or an operator, found `{` //~| ERROR expected `;`, found `bar` - //~| ERROR `->` used for field access or method call + //~| ERROR `->` is not valid syntax for field accesses and method calls #[attr] [1, 2, 3].iter().map().collect::() #[attr] diff --git a/tests/ui/parser/issues/issue-118530-ice.stderr b/tests/ui/parser/issues/issue-118530-ice.stderr index 72c0397e9c95b..ef891d1dc2906 100644 --- a/tests/ui/parser/issues/issue-118530-ice.stderr +++ b/tests/ui/parser/issues/issue-118530-ice.stderr @@ -33,13 +33,13 @@ LL | attr::fn bar() -> String { | | | help: add `;` here -error: `->` used for field access or method call +error: `->` is not valid syntax for field accesses and method calls --> $DIR/issue-118530-ice.rs:5:20 | LL | attr::fn bar() -> String { | ^^ | - = help: the `.` operator will dereference the value if needed + = help: the `.` operator will automatically dereference the value, except if the value is a raw pointer help: try using `.` instead | LL - attr::fn bar() -> String { diff --git a/tests/ui/parser/issues/issue-21146.rs b/tests/ui/parser/issues/issue-21146.rs index 81112808b21e3..95cae47939f76 100644 --- a/tests/ui/parser/issues/issue-21146.rs +++ b/tests/ui/parser/issues/issue-21146.rs @@ -1,3 +1,4 @@ -//@ error-pattern: expected one of `!` or `::`, found `` include!("auxiliary/issue-21146-inc.rs"); fn main() {} + +//~? ERROR expected one of `!` or `::`, found `` diff --git a/tests/ui/parser/issues/issue-24375.stderr b/tests/ui/parser/issues/issue-24375.stderr index 2af57a52035c3..e96c004fb3516 100644 --- a/tests/ui/parser/issues/issue-24375.stderr +++ b/tests/ui/parser/issues/issue-24375.stderr @@ -16,10 +16,6 @@ LL + const VAL: /* Type */ = tmp[0]; LL ~ match z { LL ~ VAL => {} | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { tmp[0] } => {} - | +++++++ + error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr b/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr index e34371be3d262..64cf8baf9a5d4 100644 --- a/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr +++ b/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr @@ -49,24 +49,18 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `:` | LL | let _ = 0i32: i32: i32.count_ones(); | ^ expected one of `.`, `;`, `?`, `else`, or an operator - | - = note: type ascription syntax has been removed, see issue #101728 error: expected one of `!`, `(`, `.`, `::`, `;`, `<`, `?`, or `else`, found `:` --> $DIR/issue-35813-postfix-after-cast.rs:43:21 | LL | let _ = 0 as i32: i32.count_ones(); | ^ expected one of 8 possible tokens - | - = note: type ascription syntax has been removed, see issue #101728 error: expected one of `.`, `;`, `?`, `else`, or an operator, found `:` --> $DIR/issue-35813-postfix-after-cast.rs:47:17 | LL | let _ = 0i32: i32 as i32.count_ones(); | ^ expected one of `.`, `;`, `?`, `else`, or an operator - | - = note: type ascription syntax has been removed, see issue #101728 error: cast cannot be followed by a method call --> $DIR/issue-35813-postfix-after-cast.rs:51:13 @@ -84,16 +78,12 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `:` | LL | let _ = 0i32: i32: i32 as u32 as i32.count_ones(); | ^ expected one of `.`, `;`, `?`, `else`, or an operator - | - = note: type ascription syntax has been removed, see issue #101728 error: expected one of `.`, `;`, `?`, `else`, or an operator, found `:` --> $DIR/issue-35813-postfix-after-cast.rs:60:17 | LL | let _ = 0i32: i32.count_ones(): u32; | ^ expected one of `.`, `;`, `?`, `else`, or an operator - | - = note: type ascription syntax has been removed, see issue #101728 error: cast cannot be followed by a method call --> $DIR/issue-35813-postfix-after-cast.rs:64:13 @@ -111,16 +101,12 @@ error: expected one of `.`, `;`, `?`, or `else`, found `:` | LL | let _ = 0 as i32.count_ones(): u32; | ^ expected one of `.`, `;`, `?`, or `else` - | - = note: type ascription syntax has been removed, see issue #101728 error: expected one of `.`, `;`, `?`, `else`, or an operator, found `:` --> $DIR/issue-35813-postfix-after-cast.rs:69:17 | LL | let _ = 0i32: i32.count_ones() as u32; | ^ expected one of `.`, `;`, `?`, `else`, or an operator - | - = note: type ascription syntax has been removed, see issue #101728 error: cast cannot be followed by a method call --> $DIR/issue-35813-postfix-after-cast.rs:73:13 @@ -138,8 +124,6 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `:` | LL | let _ = 0i32: i32: i32.count_ones() as u32 as i32; | ^ expected one of `.`, `;`, `?`, `else`, or an operator - | - = note: type ascription syntax has been removed, see issue #101728 error: cast cannot be followed by a method call --> $DIR/issue-35813-postfix-after-cast.rs:82:13 @@ -262,8 +246,6 @@ error: expected identifier, found `:` | LL | drop_ptr: F(); | ^ expected identifier - | - = note: type ascription syntax has been removed, see issue #101728 error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `:` --> $DIR/issue-35813-postfix-after-cast.rs:160:13 diff --git a/tests/ui/parser/issues/issue-48508-aux.rs b/tests/ui/parser/issues/issue-48508-aux.rs index 0f2b4427383fd..0bf6490edf4db 100644 --- a/tests/ui/parser/issues/issue-48508-aux.rs +++ b/tests/ui/parser/issues/issue-48508-aux.rs @@ -1,5 +1,4 @@ -//@ run-pass -//@ ignore-test Not a test. Used by issue-48508.rs +//@ ignore-auxiliary (used by `./issue-48508.rs`) pub fn other() -> f64 { let µ = 1.0; diff --git a/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.rs b/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.rs index 7952d29c26024..0c0fbd7d5929b 100644 --- a/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.rs +++ b/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.rs @@ -1,5 +1,4 @@ // Fixed in #66054. // ignore-tidy-trailing-newlines -//@ error-pattern: this file contains an unclosed delimiter -//@ error-pattern: aborting due to 1 previous error +//~v ERROR this file contains an unclosed delimiter #[Ѕ \ No newline at end of file diff --git a/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.stderr b/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.stderr index 14f5469f6af94..28fd78d660ddc 100644 --- a/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.stderr +++ b/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-58094-missing-right-square-bracket.rs:5:4 + --> $DIR/issue-58094-missing-right-square-bracket.rs:4:4 | LL | #[Ѕ | - ^ diff --git a/tests/ui/parser/issues/issue-62524.rs b/tests/ui/parser/issues/issue-62524.rs index a219f662cf78c..a8c7d6eb9fdd1 100644 --- a/tests/ui/parser/issues/issue-62524.rs +++ b/tests/ui/parser/issues/issue-62524.rs @@ -1,6 +1,7 @@ // ignore-tidy-trailing-newlines -//@ error-pattern: aborting due to 1 previous error + #![allow(uncommon_codepoints)] +//~vv ERROR this file contains an unclosed delimiter y![ Ϥ, \ No newline at end of file diff --git a/tests/ui/parser/issues/issue-62524.stderr b/tests/ui/parser/issues/issue-62524.stderr index d83a49aedd6e8..c1ff6e7e7158e 100644 --- a/tests/ui/parser/issues/issue-62524.stderr +++ b/tests/ui/parser/issues/issue-62524.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-62524.rs:6:3 + --> $DIR/issue-62524.rs:7:3 | LL | y![ | - unclosed delimiter diff --git a/tests/ui/parser/issues/issue-62554.rs b/tests/ui/parser/issues/issue-62554.rs index 9f196e4b0d614..4a8a1684a412d 100644 --- a/tests/ui/parser/issues/issue-62554.rs +++ b/tests/ui/parser/issues/issue-62554.rs @@ -1,5 +1,4 @@ -//@ error-pattern:this file contains an unclosed delimiter - fn main() {} +//~v ERROR this file contains an unclosed delimiter fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { diff --git a/tests/ui/parser/issues/issue-62554.stderr b/tests/ui/parser/issues/issue-62554.stderr index d4aaef1618139..50515c4c574a1 100644 --- a/tests/ui/parser/issues/issue-62554.stderr +++ b/tests/ui/parser/issues/issue-62554.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-62554.rs:5:89 + --> $DIR/issue-62554.rs:4:89 | LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { | - - - - -^ diff --git a/tests/ui/parser/issues/issue-62894.rs b/tests/ui/parser/issues/issue-62894.rs index 5b1627a255372..c49cbe4b934dd 100644 --- a/tests/ui/parser/issues/issue-62894.rs +++ b/tests/ui/parser/issues/issue-62894.rs @@ -1,6 +1,6 @@ // Regression test for #62894, shouldn't crash. -//@ error-pattern: this file contains an unclosed delimiter +//~vvv ERROR this file contains an unclosed delimiter fn f() { assert_eq!(f(), (), assert_eq!(assert_eq! fn main() {} diff --git a/tests/ui/parser/issues/issue-62973.rs b/tests/ui/parser/issues/issue-62973.rs index 5c666d802fe44..a091e4eec1d92 100644 --- a/tests/ui/parser/issues/issue-62973.rs +++ b/tests/ui/parser/issues/issue-62973.rs @@ -1,8 +1,10 @@ // ignore-tidy-trailing-newlines -//@ error-pattern: aborting due to 3 previous errors fn main() {} +//~vvv ERROR mismatched closing delimiter: `)` +//~vv ERROR mismatched closing delimiter: `)` +//~vvv ERROR this file contains an unclosed delimiter fn p() { match s { v, E { [) {) } diff --git a/tests/ui/parser/issues/issue-62973.stderr b/tests/ui/parser/issues/issue-62973.stderr index 493183988e18b..ea3e2bebee404 100644 --- a/tests/ui/parser/issues/issue-62973.stderr +++ b/tests/ui/parser/issues/issue-62973.stderr @@ -1,5 +1,5 @@ error: mismatched closing delimiter: `)` - --> $DIR/issue-62973.rs:6:27 + --> $DIR/issue-62973.rs:8:27 | LL | fn p() { match s { v, E { [) {) } | ^^ mismatched closing delimiter @@ -7,7 +7,7 @@ LL | fn p() { match s { v, E { [) {) } | unclosed delimiter error: mismatched closing delimiter: `)` - --> $DIR/issue-62973.rs:6:30 + --> $DIR/issue-62973.rs:8:30 | LL | fn p() { match s { v, E { [) {) } | ^^ mismatched closing delimiter @@ -15,7 +15,7 @@ LL | fn p() { match s { v, E { [) {) } | unclosed delimiter error: this file contains an unclosed delimiter - --> $DIR/issue-62973.rs:8:2 + --> $DIR/issue-62973.rs:10:2 | LL | fn p() { match s { v, E { [) {) } | - - - - missing open `(` for this delimiter diff --git a/tests/ui/parser/issues/issue-63116.rs b/tests/ui/parser/issues/issue-63116.rs index 3be9606b4edbc..48abe639e8d97 100644 --- a/tests/ui/parser/issues/issue-63116.rs +++ b/tests/ui/parser/issues/issue-63116.rs @@ -1,3 +1,4 @@ // fixed by #66361 -//@ error-pattern: aborting due to 2 previous errors +//~vv ERROR mismatched closing delimiter: `]` +//~v ERROR this file contains an unclosed delimiter impl W $DIR/issue-63116.rs:3:14 + --> $DIR/issue-63116.rs:4:14 | LL | impl W $DIR/issue-63116.rs:3:18 + --> $DIR/issue-63116.rs:4:18 | LL | impl W $DIR/issue-63135.rs:3:16 + --> $DIR/issue-63135.rs:2:16 | LL | fn i(n{...,f # | - - ^ diff --git a/tests/ui/parser/issues/issue-65041-empty-vis-matcher-in-enum.rs b/tests/ui/parser/issues/issue-65041-empty-vis-matcher-in-enum.rs index 4fa803bb318e4..13bb9351bb692 100644 --- a/tests/ui/parser/issues/issue-65041-empty-vis-matcher-in-enum.rs +++ b/tests/ui/parser/issues/issue-65041-empty-vis-matcher-in-enum.rs @@ -20,7 +20,7 @@ macro_rules! mac_variant { mac_variant!(MARKER); // We also accept visibilities on variants syntactically but not semantically. -#[cfg(FALSE)] +#[cfg(false)] enum E { pub U, pub(crate) T(u8), diff --git a/tests/ui/parser/issues/issue-65041-empty-vis-matcher-in-trait.rs b/tests/ui/parser/issues/issue-65041-empty-vis-matcher-in-trait.rs index cd474db63b71a..55e69cd14d6b5 100644 --- a/tests/ui/parser/issues/issue-65041-empty-vis-matcher-in-trait.rs +++ b/tests/ui/parser/issues/issue-65041-empty-vis-matcher-in-trait.rs @@ -20,7 +20,7 @@ trait Alpha { } // We also accept visibilities on items in traits syntactically but not semantically. -#[cfg(FALSE)] +#[cfg(false)] trait Foo { pub fn bar(); pub(crate) type baz; diff --git a/tests/ui/parser/issues/issue-66473.rs b/tests/ui/parser/issues/issue-66473.rs index baa00f95e12fa..d0786add1af50 100644 Binary files a/tests/ui/parser/issues/issue-66473.rs and b/tests/ui/parser/issues/issue-66473.rs differ diff --git a/tests/ui/parser/issues/issue-66473.stderr b/tests/ui/parser/issues/issue-66473.stderr index ba38c4fa1b7ad..65b2d5a34ee47 100644 --- a/tests/ui/parser/issues/issue-66473.stderr +++ b/tests/ui/parser/issues/issue-66473.stderr @@ -1,11 +1,11 @@ error: unknown start of token: \u{348} - --> $DIR/issue-66473.rs:4:2 + --> $DIR/issue-66473.rs:1:2 | LL | #͈␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀ | ^ error: unknown start of token: \u{0} - --> $DIR/issue-66473.rs:4:3 + --> $DIR/issue-66473.rs:1:3 | LL | #͈␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀ | ^^^^^^^^^^^^^^^^^^ @@ -14,19 +14,19 @@ LL | #͈␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀ = note: character appears 17 more times error: unknown start of token: \u{1d} - --> $DIR/issue-66473.rs:5:2 + --> $DIR/issue-66473.rs:4:2 | LL | ␋␝6␝␀␀ | ^ error: unknown start of token: \u{1d} - --> $DIR/issue-66473.rs:5:4 + --> $DIR/issue-66473.rs:4:4 | LL | ␋␝6␝␀␀ | ^ error: unknown start of token: \u{0} - --> $DIR/issue-66473.rs:5:5 + --> $DIR/issue-66473.rs:4:5 | LL | ␋␝6␝␀␀ | ^^ @@ -35,10 +35,11 @@ LL | ␋␝6␝␀␀ = note: character appears once more error: expected one of `!` or `[`, found `6` - --> $DIR/issue-66473.rs:5:3 + --> $DIR/issue-66473.rs:4:3 | LL | #͈␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀␀ | - expected one of `!` or `[` +... LL | ␋␝6␝␀␀ | ^ unexpected token diff --git a/tests/ui/parser/issues/issue-68629.rs b/tests/ui/parser/issues/issue-68629.rs index d353d1f56ce18..d1ea4ca8b0371 100644 Binary files a/tests/ui/parser/issues/issue-68629.rs and b/tests/ui/parser/issues/issue-68629.rs differ diff --git a/tests/ui/parser/issues/issue-68629.stderr b/tests/ui/parser/issues/issue-68629.stderr index f003f37817951..373ad142778b0 100644 --- a/tests/ui/parser/issues/issue-68629.stderr +++ b/tests/ui/parser/issues/issue-68629.stderr @@ -1,17 +1,17 @@ error: unknown start of token: \u{1c} - --> $DIR/issue-68629.rs:4:1 + --> $DIR/issue-68629.rs:6:1 | LL | ␜␟ts␀![{i | ^ error: unknown start of token: \u{1f} - --> $DIR/issue-68629.rs:4:2 + --> $DIR/issue-68629.rs:6:2 | LL | ␜␟ts␀![{i | ^ error: unknown start of token: \u{0} - --> $DIR/issue-68629.rs:4:5 + --> $DIR/issue-68629.rs:6:5 | LL | ␜␟ts␀![{i | ^ @@ -19,7 +19,7 @@ LL | ␜␟ts␀![{i = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/issue-68629.rs:5:1 + --> $DIR/issue-68629.rs:7:1 | LL | ␀␀ fn rݻoa>rݻm | ^^ @@ -28,7 +28,7 @@ LL | ␀␀ fn rݻoa>rݻm = note: character appears once more error: this file contains an unclosed delimiter - --> $DIR/issue-68629.rs:5:17 + --> $DIR/issue-68629.rs:7:17 | LL | ␜␟ts␀![{i | -- unclosed delimiter diff --git a/tests/ui/parser/issues/issue-68730.rs b/tests/ui/parser/issues/issue-68730.rs index f7f11cbc98ba8..9cf51b720eb38 100644 Binary files a/tests/ui/parser/issues/issue-68730.rs and b/tests/ui/parser/issues/issue-68730.rs differ diff --git a/tests/ui/parser/issues/issue-68730.stderr b/tests/ui/parser/issues/issue-68730.stderr index 838a6569bdc3f..1e01e02bdb7aa 100644 --- a/tests/ui/parser/issues/issue-68730.stderr +++ b/tests/ui/parser/issues/issue-68730.stderr @@ -1,5 +1,5 @@ error: unknown start of token: \u{0} - --> $DIR/issue-68730.rs:5:5 + --> $DIR/issue-68730.rs:8:5 | LL | enum␀em␀˂˂ | ^ @@ -7,7 +7,7 @@ LL | enum␀em␀˂˂ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/issue-68730.rs:5:8 + --> $DIR/issue-68730.rs:8:8 | LL | enum␀em␀˂˂ | ^ @@ -15,7 +15,7 @@ LL | enum␀em␀˂˂ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{2c2} - --> $DIR/issue-68730.rs:5:9 + --> $DIR/issue-68730.rs:8:9 | LL | enum␀em␀˂˂ | ^^ @@ -28,7 +28,7 @@ LL + enum␀em␀<< | error: unknown start of token: \u{2c2} - --> $DIR/issue-68730.rs:5:10 + --> $DIR/issue-68730.rs:8:10 | LL | enum␀em␀˂˂ | ^ @@ -40,7 +40,7 @@ LL + enum␀em␀˂< | error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `<` - --> $DIR/issue-68730.rs:5:10 + --> $DIR/issue-68730.rs:8:10 | LL | enum␀em␀˂˂ | ^ expected one of `#`, `>`, `const`, identifier, or lifetime diff --git a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs index cf754a6854ecf..782a46ab060e0 100644 --- a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs +++ b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.rs @@ -12,7 +12,7 @@ mac!('a); // avoid false positives fn y<'a>(y: &mut 'a + Send) { - //~^ ERROR expected a path on the left-hand side of `+`, not `&mut 'a` + //~^ ERROR expected a path on the left-hand side of `+` //~| ERROR at least one trait is required for an object type let z = y as &mut 'a + Send; //~^ ERROR expected value, found trait `Send` diff --git a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr index 6b8f8e4fe4ee3..ae1ed72853de3 100644 --- a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr +++ b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr @@ -10,11 +10,11 @@ LL - fn x<'a>(x: &mut 'a i32){} LL + fn x<'a>(x: &'a mut i32){} | -error[E0178]: expected a path on the left-hand side of `+`, not `&mut 'a` +error[E0178]: expected a path on the left-hand side of `+` --> $DIR/issue-73568-lifetime-after-mut.rs:14:13 | LL | fn y<'a>(y: &mut 'a + Send) { - | ^^^^^^^^^^^^^^ + | ^^^^^^^ | help: try adding parentheses | diff --git a/tests/ui/parser/issues/issue-81804.rs b/tests/ui/parser/issues/issue-81804.rs index 7c9e6e905825b..57951ca5c4bc1 100644 --- a/tests/ui/parser/issues/issue-81804.rs +++ b/tests/ui/parser/issues/issue-81804.rs @@ -1,6 +1,5 @@ -//@ error-pattern: this file contains an unclosed delimiter -//@ error-pattern: this file contains an unclosed delimiter - fn main() {} +//~vv ERROR mismatched closing delimiter: `}` +//~v ERROR this file contains an unclosed delimiter fn p([=(} diff --git a/tests/ui/parser/issues/issue-81804.stderr b/tests/ui/parser/issues/issue-81804.stderr index 6caaaa792b196..f12c6a61ce5ec 100644 --- a/tests/ui/parser/issues/issue-81804.stderr +++ b/tests/ui/parser/issues/issue-81804.stderr @@ -1,5 +1,5 @@ error: mismatched closing delimiter: `}` - --> $DIR/issue-81804.rs:6:8 + --> $DIR/issue-81804.rs:5:8 | LL | fn p([=(} | ^^ mismatched closing delimiter @@ -7,7 +7,7 @@ LL | fn p([=(} | unclosed delimiter error: this file contains an unclosed delimiter - --> $DIR/issue-81804.rs:6:11 + --> $DIR/issue-81804.rs:5:11 | LL | fn p([=(} | -- ^ diff --git a/tests/ui/parser/issues/issue-81827.rs b/tests/ui/parser/issues/issue-81827.rs index a2bd345fc050e..7dfeec13022a0 100644 --- a/tests/ui/parser/issues/issue-81827.rs +++ b/tests/ui/parser/issues/issue-81827.rs @@ -1,10 +1,7 @@ -//@ error-pattern: this file contains an unclosed delimiter -//@ error-pattern: mismatched closing delimiter: `]` - #![crate_name="0"] - - fn main() {} +//~vv ERROR mismatched closing delimiter: `]` +//~v ERROR this file contains an unclosed delimiter fn r()->i{0|{#[cfg(r(0{]0 diff --git a/tests/ui/parser/issues/issue-81827.stderr b/tests/ui/parser/issues/issue-81827.stderr index d12c74b4a3420..986ed6b7e70f9 100644 --- a/tests/ui/parser/issues/issue-81827.stderr +++ b/tests/ui/parser/issues/issue-81827.stderr @@ -1,5 +1,5 @@ error: mismatched closing delimiter: `]` - --> $DIR/issue-81827.rs:10:23 + --> $DIR/issue-81827.rs:7:23 | LL | fn r()->i{0|{#[cfg(r(0{]0 | - ^^ mismatched closing delimiter @@ -8,7 +8,7 @@ LL | fn r()->i{0|{#[cfg(r(0{]0 | closing delimiter possibly meant for this error: this file contains an unclosed delimiter - --> $DIR/issue-81827.rs:10:27 + --> $DIR/issue-81827.rs:7:27 | LL | fn r()->i{0|{#[cfg(r(0{]0 | - - - ^ diff --git a/tests/ui/parser/issues/issue-84104.rs b/tests/ui/parser/issues/issue-84104.rs index bced05e684a7f..6baf882701d3c 100644 --- a/tests/ui/parser/issues/issue-84104.rs +++ b/tests/ui/parser/issues/issue-84104.rs @@ -1,2 +1,2 @@ -//@ error-pattern: this file contains an unclosed delimiter +//~v ERROR this file contains an unclosed delimiter #[i=i::<ښܖ< diff --git a/tests/ui/parser/issues/issue-84148-2.rs b/tests/ui/parser/issues/issue-84148-2.rs index 560475bd32c3f..452279021ab9d 100644 --- a/tests/ui/parser/issues/issue-84148-2.rs +++ b/tests/ui/parser/issues/issue-84148-2.rs @@ -1,2 +1,2 @@ -//@ error-pattern: this file contains an unclosed delimiter +//~v ERROR this file contains an unclosed delimiter fn f(t:for<>t? diff --git a/tests/ui/parser/issues/issue-88770.rs b/tests/ui/parser/issues/issue-88770.rs index ecc50481f65d7..0dd18435ce3ff 100644 --- a/tests/ui/parser/issues/issue-88770.rs +++ b/tests/ui/parser/issues/issue-88770.rs @@ -1,7 +1,6 @@ // Regression test for the ICE described in #88770. -//@ error-pattern:this file contains an unclosed delimiter - +//~vvvv ERROR this file contains an unclosed delimiter fn m(){print!("",(c for&g u e diff --git a/tests/ui/parser/issues/issue-88770.stderr b/tests/ui/parser/issues/issue-88770.stderr index 5b54072d009fb..137cfea7e1d0d 100644 --- a/tests/ui/parser/issues/issue-88770.stderr +++ b/tests/ui/parser/issues/issue-88770.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-88770.rs:8:3 + --> $DIR/issue-88770.rs:7:3 | LL | fn m(){print!("",(c for&g | - - - unclosed delimiter diff --git a/tests/ui/parser/issues/issue-89574.rs b/tests/ui/parser/issues/issue-89574.rs index 276abfe71102b..4d28ad051dfaf 100644 --- a/tests/ui/parser/issues/issue-89574.rs +++ b/tests/ui/parser/issues/issue-89574.rs @@ -1,5 +1,5 @@ fn main() { const EMPTY_ARRAY = []; - //~^ missing type for `const` item + //~^ ERROR missing type for `const` item //~| ERROR type annotations needed } diff --git a/tests/ui/parser/issues/issue-93867.rs b/tests/ui/parser/issues/issue-93867.rs index 507447923915b..192b158189637 100644 --- a/tests/ui/parser/issues/issue-93867.rs +++ b/tests/ui/parser/issues/issue-93867.rs @@ -5,6 +5,6 @@ pub struct Entry<'a, K, V> { pub fn entry<'a, K, V>() -> Entry<'a K, V> { // ^ missing comma -//~^^ expected one of `,` or `>`, found `K` +//~^^ ERROR expected one of `,` or `>`, found `K` unimplemented!() } diff --git a/tests/ui/parser/issues/issue-94340.rs b/tests/ui/parser/issues/issue-94340.rs index d0fb84a689a33..4f3dbc6acdd78 100644 --- a/tests/ui/parser/issues/issue-94340.rs +++ b/tests/ui/parser/issues/issue-94340.rs @@ -6,3 +6,6 @@ include!("auxiliary/issue-94340-inc.rs"); fn main() {} + +//~? ERROR an inner attribute is not permitted in this context +//~? ERROR an inner attribute is not permitted in this context diff --git a/tests/ui/parser/issues/misplaced-return-type-where-in-next-line-issue-126311.stderr b/tests/ui/parser/issues/misplaced-return-type-where-in-next-line-issue-126311.stderr index 196a46d7ea54b..6245f81d533be 100644 --- a/tests/ui/parser/issues/misplaced-return-type-where-in-next-line-issue-126311.stderr +++ b/tests/ui/parser/issues/misplaced-return-type-where-in-next-line-issue-126311.stderr @@ -2,7 +2,7 @@ error: return type should be specified after the function parameters --> $DIR/misplaced-return-type-where-in-next-line-issue-126311.rs:5:15 | LL | K: Clone, -> Result - | ^^ expected one of `{`, lifetime, or type + | ^^ expected one of `#`, `{`, lifetime, or type | help: place the return type after the function parameters | diff --git a/tests/ui/parser/item-free-const-no-body-syntactic-pass.rs b/tests/ui/parser/item-free-const-no-body-syntactic-pass.rs index 4edbee54de673..0b9229860bf86 100644 --- a/tests/ui/parser/item-free-const-no-body-syntactic-pass.rs +++ b/tests/ui/parser/item-free-const-no-body-syntactic-pass.rs @@ -4,5 +4,5 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] const X: u8; diff --git a/tests/ui/parser/item-free-static-no-body-syntactic-pass.rs b/tests/ui/parser/item-free-static-no-body-syntactic-pass.rs index df5192645e11e..8dae4338ee7ed 100644 --- a/tests/ui/parser/item-free-static-no-body-syntactic-pass.rs +++ b/tests/ui/parser/item-free-static-no-body-syntactic-pass.rs @@ -4,5 +4,5 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] static X: u8; diff --git a/tests/ui/parser/item-free-type-bounds-syntactic-pass.rs b/tests/ui/parser/item-free-type-bounds-syntactic-pass.rs index 80de3cfc668dc..8603dc3eaf864 100644 --- a/tests/ui/parser/item-free-type-bounds-syntactic-pass.rs +++ b/tests/ui/parser/item-free-type-bounds-syntactic-pass.rs @@ -2,7 +2,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn syntax() { type A: Ord; type B: Ord = u8; diff --git a/tests/ui/parser/keyword-try-as-identifier-edition2018.rs b/tests/ui/parser/keyword-try-as-identifier-edition2018.rs index 27452f4d45e85..795f32059f221 100644 --- a/tests/ui/parser/keyword-try-as-identifier-edition2018.rs +++ b/tests/ui/parser/keyword-try-as-identifier-edition2018.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 fn main() { let try = "foo"; //~ error: expected identifier, found reserved keyword `try` diff --git a/tests/ui/parser/lit-err-in-macro.stderr b/tests/ui/parser/lit-err-in-macro.stderr index 9422f22f9c8fe..08fe58643d40a 100644 --- a/tests/ui/parser/lit-err-in-macro.stderr +++ b/tests/ui/parser/lit-err-in-macro.stderr @@ -4,7 +4,7 @@ error: suffixes on string literals are invalid LL | f!("Foo"__); | ^^^^^^^ invalid suffix `__` -warning: extern declarations without an explicit ABI are deprecated +warning: `extern` declarations without an explicit ABI are deprecated --> $DIR/lit-err-in-macro.rs:3:9 | LL | extern $abi fn f() {} diff --git a/tests/ui/parser/macro/macro-expand-to-field.rs b/tests/ui/parser/macro/macro-expand-to-field.rs index ccdcf013cd25f..f5100a5d70034 100644 --- a/tests/ui/parser/macro/macro-expand-to-field.rs +++ b/tests/ui/parser/macro/macro-expand-to-field.rs @@ -49,7 +49,7 @@ enum EnumVariantField { field!(oopsies:()), //~^ NOTE macros cannot expand to struct fields //~| ERROR unexpected token: `!` - //~| unexpected token after this + //~| NOTE unexpected token after this field!(oopsies2:()), }, } diff --git a/tests/ui/parser/macro/trait-non-item-macros.rs b/tests/ui/parser/macro/trait-non-item-macros.rs index e93000193b6e3..b4140613cbae7 100644 --- a/tests/ui/parser/macro/trait-non-item-macros.rs +++ b/tests/ui/parser/macro/trait-non-item-macros.rs @@ -1,7 +1,7 @@ macro_rules! bah { ($a:expr) => { $a - }; //~^ ERROR macro expansion ignores expression `2` and any tokens following + }; //~^ ERROR macro expansion ignores `expr` metavariable and any tokens following } trait Bar { diff --git a/tests/ui/parser/macro/trait-non-item-macros.stderr b/tests/ui/parser/macro/trait-non-item-macros.stderr index 1a82848377895..62b42fa8b8dd7 100644 --- a/tests/ui/parser/macro/trait-non-item-macros.stderr +++ b/tests/ui/parser/macro/trait-non-item-macros.stderr @@ -1,4 +1,4 @@ -error: macro expansion ignores expression `2` and any tokens following +error: macro expansion ignores `expr` metavariable and any tokens following --> $DIR/trait-non-item-macros.rs:3:9 | LL | $a diff --git a/tests/ui/parser/macro/trait-object-macro-matcher.e2015.stderr b/tests/ui/parser/macro/trait-object-macro-matcher.e2015.stderr new file mode 100644 index 0000000000000..f2db351de4a7f --- /dev/null +++ b/tests/ui/parser/macro/trait-object-macro-matcher.e2015.stderr @@ -0,0 +1,32 @@ +error: lifetimes must be followed by `+` to form a trait object type + --> $DIR/trait-object-macro-matcher.rs:17:8 + | +LL | m!('static); + | ^^^^^^^ + | +help: consider adding a trait bound after the potential lifetime bound + | +LL | m!('static + /* Trait */); + | +++++++++++++ + +error: lifetimes must be followed by `+` to form a trait object type + --> $DIR/trait-object-macro-matcher.rs:17:8 + | +LL | m!('static); + | ^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding a trait bound after the potential lifetime bound + | +LL | m!('static + /* Trait */); + | +++++++++++++ + +error[E0224]: at least one trait is required for an object type + --> $DIR/trait-object-macro-matcher.rs:17:8 + | +LL | m!('static); + | ^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0224`. diff --git a/tests/ui/parser/macro/trait-object-macro-matcher.e2021.stderr b/tests/ui/parser/macro/trait-object-macro-matcher.e2021.stderr new file mode 100644 index 0000000000000..7d9e8d795d18a --- /dev/null +++ b/tests/ui/parser/macro/trait-object-macro-matcher.e2021.stderr @@ -0,0 +1,16 @@ +error: expected type, found lifetime + --> $DIR/trait-object-macro-matcher.rs:17:8 + | +LL | m!('static); + | ^^^^^^^ expected type + +error: expected type, found lifetime + --> $DIR/trait-object-macro-matcher.rs:17:8 + | +LL | m!('static); + | ^^^^^^^ expected type + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/macro/trait-object-macro-matcher.rs b/tests/ui/parser/macro/trait-object-macro-matcher.rs index d4ec199070e7c..ba61752fe4024 100644 --- a/tests/ui/parser/macro/trait-object-macro-matcher.rs +++ b/tests/ui/parser/macro/trait-object-macro-matcher.rs @@ -1,6 +1,10 @@ // A single lifetime is not parsed as a type. // `ty` matcher in particular doesn't accept a single lifetime +//@ revisions: e2015 e2021 +//@[e2015] edition: 2015 +//@[e2021] edition: 2021 + macro_rules! m { ($t: ty) => { let _: $t; @@ -8,8 +12,10 @@ macro_rules! m { } fn main() { + //[e2021]~vv ERROR expected type, found lifetime + //[e2021]~v ERROR expected type, found lifetime m!('static); - //~^ ERROR lifetime in trait object type must be followed by `+` - //~| ERROR lifetime in trait object type must be followed by `+` - //~| ERROR at least one trait is required for an object type + //[e2015]~^ ERROR lifetimes must be followed by `+` to form a trait object type + //[e2015]~| ERROR lifetimes must be followed by `+` to form a trait object type + //[e2015]~| ERROR at least one trait is required for an object type } diff --git a/tests/ui/parser/macro/trait-object-macro-matcher.stderr b/tests/ui/parser/macro/trait-object-macro-matcher.stderr deleted file mode 100644 index 81dca6f71c436..0000000000000 --- a/tests/ui/parser/macro/trait-object-macro-matcher.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: lifetime in trait object type must be followed by `+` - --> $DIR/trait-object-macro-matcher.rs:11:8 - | -LL | m!('static); - | ^^^^^^^ - -error: lifetime in trait object type must be followed by `+` - --> $DIR/trait-object-macro-matcher.rs:11:8 - | -LL | m!('static); - | ^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0224]: at least one trait is required for an object type - --> $DIR/trait-object-macro-matcher.rs:11:8 - | -LL | m!('static); - | ^^^^^^^ - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0224`. diff --git a/tests/ui/parser/mbe_missing_right_paren.rs b/tests/ui/parser/mbe_missing_right_paren.rs index 9c57b0ebcfc3d..851919316647d 100644 --- a/tests/ui/parser/mbe_missing_right_paren.rs +++ b/tests/ui/parser/mbe_missing_right_paren.rs @@ -1,3 +1,3 @@ // ignore-tidy-trailing-newlines -//@ error-pattern: this file contains an unclosed delimiter +//~v ERROR this file contains an unclosed delimiter macro_rules! abc(ؼ \ No newline at end of file diff --git a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.rs b/tests/ui/parser/method-call-on-struct-literal-in-if-condition.rs deleted file mode 100644 index 8be7c9ee8ac3a..0000000000000 --- a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub struct Example { a: i32 } - -impl Example { - fn is_pos(&self) -> bool { self.a > 0 } -} - -fn one() -> i32 { 1 } - -fn main() { - if Example { a: one(), }.is_pos() { //~ ERROR invalid struct literal - println!("Positive!"); - } -} diff --git a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr b/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr deleted file mode 100644 index f7822ba11246b..0000000000000 --- a/tests/ui/parser/method-call-on-struct-literal-in-if-condition.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: invalid struct literal - --> $DIR/method-call-on-struct-literal-in-if-condition.rs:10:8 - | -LL | if Example { a: one(), }.is_pos() { - | ^^^^^^^^^^^^^^^^^^^^^ - | -help: you might need to surround the struct literal with parentheses - | -LL | if (Example { a: one(), }).is_pos() { - | + + - -error: aborting due to 1 previous error - diff --git a/tests/ui/parser/missing_right_paren.rs b/tests/ui/parser/missing_right_paren.rs index bbf4519a713ee..311a16c214c39 100644 --- a/tests/ui/parser/missing_right_paren.rs +++ b/tests/ui/parser/missing_right_paren.rs @@ -1,4 +1,3 @@ // ignore-tidy-trailing-newlines -//@ error-pattern: this file contains an unclosed delimiter -//@ error-pattern: aborting due to 1 previous error +//~v ERROR this file contains an unclosed delimiter fn main((ؼ \ No newline at end of file diff --git a/tests/ui/parser/missing_right_paren.stderr b/tests/ui/parser/missing_right_paren.stderr index 4815f04fbce07..97ccb40a5a2a7 100644 --- a/tests/ui/parser/missing_right_paren.stderr +++ b/tests/ui/parser/missing_right_paren.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/missing_right_paren.rs:4:11 + --> $DIR/missing_right_paren.rs:3:11 | LL | fn main((ؼ | -- ^ diff --git a/tests/ui/parser/misspelled-keywords/async-move.stderr b/tests/ui/parser/misspelled-keywords/async-move.stderr index a002d54dc9112..2507326fb2854 100644 --- a/tests/ui/parser/misspelled-keywords/async-move.stderr +++ b/tests/ui/parser/misspelled-keywords/async-move.stderr @@ -1,8 +1,8 @@ -error: expected one of `move`, `|`, or `||`, found `Move` +error: expected one of `move`, `use`, `|`, or `||`, found `Move` --> $DIR/async-move.rs:4:11 | LL | async Move {} - | ^^^^ expected one of `move`, `|`, or `||` + | ^^^^ expected one of `move`, `use`, `|`, or `||` | help: write keyword `move` in lowercase | diff --git a/tests/ui/parser/mut-patterns.rs b/tests/ui/parser/mut-patterns.rs index ed33968c627e8..a2af816074056 100644 --- a/tests/ui/parser/mut-patterns.rs +++ b/tests/ui/parser/mut-patterns.rs @@ -1,6 +1,7 @@ // Can't put mut in non-ident pattern //@ edition:2018 +//@ dont-require-annotations: HELP #![feature(box_patterns)] #![allow(warnings)] @@ -13,20 +14,20 @@ pub fn main() { let mut mut x = 0; //~^ ERROR `mut` on a binding may not be repeated - //~| remove the additional `mut`s + //~| HELP remove the additional `mut`s let mut mut mut mut mut x = 0; //~^ ERROR `mut` on a binding may not be repeated - //~| remove the additional `mut`s + //~| HELP remove the additional `mut`s struct Foo { x: isize } let mut Foo { x: x } = Foo { x: 3 }; //~^ ERROR `mut` must be attached to each individual binding - //~| add `mut` to each binding + //~| HELP add `mut` to each binding let mut Foo { x } = Foo { x: 3 }; //~^ ERROR `mut` must be attached to each individual binding - //~| add `mut` to each binding + //~| HELP add `mut` to each binding struct r#yield(u8, u8); let mut mut yield(become, await) = r#yield(0, 0); diff --git a/tests/ui/parser/mut-patterns.stderr b/tests/ui/parser/mut-patterns.stderr index 9dda2499f03c3..70099989c9ff0 100644 --- a/tests/ui/parser/mut-patterns.stderr +++ b/tests/ui/parser/mut-patterns.stderr @@ -1,5 +1,5 @@ error: `mut` must be followed by a named binding - --> $DIR/mut-patterns.rs:9:9 + --> $DIR/mut-patterns.rs:10:9 | LL | let mut _ = 0; | ^^^^ @@ -12,7 +12,7 @@ LL + let _ = 0; | error: `mut` must be followed by a named binding - --> $DIR/mut-patterns.rs:10:9 + --> $DIR/mut-patterns.rs:11:9 | LL | let mut (_, _) = (0, 0); | ^^^^ @@ -25,7 +25,7 @@ LL + let (_, _) = (0, 0); | error: `mut` must be attached to each individual binding - --> $DIR/mut-patterns.rs:12:9 + --> $DIR/mut-patterns.rs:13:9 | LL | let mut (x @ y) = 0; | ^^^^^^^^^^^ @@ -38,7 +38,7 @@ LL + let (mut x @ mut y) = 0; | error: `mut` on a binding may not be repeated - --> $DIR/mut-patterns.rs:14:13 + --> $DIR/mut-patterns.rs:15:13 | LL | let mut mut x = 0; | ^^^ @@ -50,7 +50,7 @@ LL + let mut x = 0; | error: `mut` on a binding may not be repeated - --> $DIR/mut-patterns.rs:18:13 + --> $DIR/mut-patterns.rs:19:13 | LL | let mut mut mut mut mut x = 0; | ^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + let mut x = 0; | error: `mut` must be attached to each individual binding - --> $DIR/mut-patterns.rs:23:9 + --> $DIR/mut-patterns.rs:24:9 | LL | let mut Foo { x: x } = Foo { x: 3 }; | ^^^^^^^^^^^^^^^^ @@ -75,7 +75,7 @@ LL + let Foo { x: mut x } = Foo { x: 3 }; | error: `mut` must be attached to each individual binding - --> $DIR/mut-patterns.rs:27:9 + --> $DIR/mut-patterns.rs:28:9 | LL | let mut Foo { x } = Foo { x: 3 }; | ^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL + let Foo { mut x } = Foo { x: 3 }; | error: `mut` on a binding may not be repeated - --> $DIR/mut-patterns.rs:32:13 + --> $DIR/mut-patterns.rs:33:13 | LL | let mut mut yield(become, await) = r#yield(0, 0); | ^^^ @@ -100,7 +100,7 @@ LL + let mut yield(become, await) = r#yield(0, 0); | error: expected identifier, found reserved keyword `yield` - --> $DIR/mut-patterns.rs:32:17 + --> $DIR/mut-patterns.rs:33:17 | LL | let mut mut yield(become, await) = r#yield(0, 0); | ^^^^^ expected identifier, found reserved keyword @@ -111,7 +111,7 @@ LL | let mut mut r#yield(become, await) = r#yield(0, 0); | ++ error: expected identifier, found reserved keyword `become` - --> $DIR/mut-patterns.rs:32:23 + --> $DIR/mut-patterns.rs:33:23 | LL | let mut mut yield(become, await) = r#yield(0, 0); | ^^^^^^ expected identifier, found reserved keyword @@ -122,7 +122,7 @@ LL | let mut mut yield(r#become, await) = r#yield(0, 0); | ++ error: expected identifier, found keyword `await` - --> $DIR/mut-patterns.rs:32:31 + --> $DIR/mut-patterns.rs:33:31 | LL | let mut mut yield(become, await) = r#yield(0, 0); | ^^^^^ expected identifier, found keyword @@ -133,7 +133,7 @@ LL | let mut mut yield(become, r#await) = r#yield(0, 0); | ++ error: `mut` must be followed by a named binding - --> $DIR/mut-patterns.rs:32:9 + --> $DIR/mut-patterns.rs:33:9 | LL | let mut mut yield(become, await) = r#yield(0, 0); | ^^^^^^^^ @@ -146,7 +146,7 @@ LL + let yield(become, await) = r#yield(0, 0); | error: `mut` must be attached to each individual binding - --> $DIR/mut-patterns.rs:41:9 + --> $DIR/mut-patterns.rs:42:9 | LL | let mut W(mut a, W(b, W(ref c, W(d, B { box f })))) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -159,7 +159,7 @@ LL + let W(mut a, W(mut b, W(ref c, W(mut d, B { box mut f })))) | error: expected identifier, found metavariable - --> $DIR/mut-patterns.rs:48:21 + --> $DIR/mut-patterns.rs:49:21 | LL | let mut $p = 0; | ^^ expected identifier, found metavariable diff --git a/tests/ui/parser/operator-precedence-braces-exprs.rs b/tests/ui/parser/operator-precedence-braces-exprs.rs new file mode 100644 index 0000000000000..d6f44ef879ce8 --- /dev/null +++ b/tests/ui/parser/operator-precedence-braces-exprs.rs @@ -0,0 +1,28 @@ +//! Regression test for ensuring that operator precedence is correctly handled in the presence of +//! braces +//! +//! Issue: + +//@ run-pass + +#[allow(unused_braces)] +fn main() { + let v1 = { 1 + { 2 } * { 3 } }; + let v2 = 1 + { 2 } * { 3 }; + + assert_eq!(7, v1); + assert_eq!(7, v2); + + let v3; + v3 = { 1 + { 2 } * { 3 } }; + let v4; + v4 = 1 + { 2 } * { 3 }; + assert_eq!(7, v3); + assert_eq!(7, v4); + + let v5 = { 1 + { 2 } * 3 }; + assert_eq!(7, v5); + + let v9 = { 1 + if 1 > 2 { 1 } else { 2 } * { 3 } }; + assert_eq!(7, v9); +} diff --git a/tests/ui/parser/or-in-let-chain.edition2021.stderr b/tests/ui/parser/or-in-let-chain.edition2021.stderr new file mode 100644 index 0000000000000..a97095cc3b82e --- /dev/null +++ b/tests/ui/parser/or-in-let-chain.edition2021.stderr @@ -0,0 +1,28 @@ +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:6:24 + | +LL | if let true = true || false {} + | ^^ + +error: expected expression, found `let` statement + --> $DIR/or-in-let-chain.rs:9:9 + | +LL | if (let true = true) || false {} + | ^^^^^^^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:12:24 + | +LL | if let true = true || false || true {} + | ^^ + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:15:33 + | +LL | if let true = true && false || true {} + | ^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/parser/or-in-let-chain.edition2024.stderr b/tests/ui/parser/or-in-let-chain.edition2024.stderr new file mode 100644 index 0000000000000..a97095cc3b82e --- /dev/null +++ b/tests/ui/parser/or-in-let-chain.edition2024.stderr @@ -0,0 +1,28 @@ +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:6:24 + | +LL | if let true = true || false {} + | ^^ + +error: expected expression, found `let` statement + --> $DIR/or-in-let-chain.rs:9:9 + | +LL | if (let true = true) || false {} + | ^^^^^^^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:12:24 + | +LL | if let true = true || false || true {} + | ^^ + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:15:33 + | +LL | if let true = true && false || true {} + | ^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/parser/or-in-let-chain.rs b/tests/ui/parser/or-in-let-chain.rs new file mode 100644 index 0000000000000..4c4372bb00f0f --- /dev/null +++ b/tests/ui/parser/or-in-let-chain.rs @@ -0,0 +1,17 @@ +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 + +fn main() { + if let true = true || false {} + //~^ ERROR `||` operators are not supported in let chain conditions + // With parentheses + if (let true = true) || false {} + //~^ ERROR expected expression, found `let` statement + // Multiple || operators + if let true = true || false || true {} + //~^ ERROR `||` operators are not supported in let chain conditions + // Mixed operators (should still show error for ||) + if let true = true && false || true {} + //~^ ERROR `||` operators are not supported in let chain conditions +} diff --git a/tests/ui/parser/pat-lt-bracket-6.stderr b/tests/ui/parser/pat-lt-bracket-6.stderr index 0274809f800c5..83c88d1085ef8 100644 --- a/tests/ui/parser/pat-lt-bracket-6.stderr +++ b/tests/ui/parser/pat-lt-bracket-6.stderr @@ -6,12 +6,6 @@ LL | let Test(&desc[..]) = x; | = note: arbitrary expressions are not allowed in patterns: -error[E0308]: mismatched types - --> $DIR/pat-lt-bracket-6.rs:10:30 - | -LL | const RECOVERY_WITNESS: () = 0; - | ^ expected `()`, found integer - error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields --> $DIR/pat-lt-bracket-6.rs:5:14 | @@ -26,6 +20,12 @@ help: use `_` to explicitly ignore each field LL | let Test(&desc[..], _) = x; | +++ +error[E0308]: mismatched types + --> $DIR/pat-lt-bracket-6.rs:10:30 + | +LL | const RECOVERY_WITNESS: () = 0; + | ^ expected `()`, found integer + error: aborting due to 3 previous errors Some errors have detailed explanations: E0023, E0308. diff --git a/tests/ui/parser/ranges-precedence.rs b/tests/ui/parser/ranges-precedence.rs index 14dd6488ff26d..783799f709989 100644 --- a/tests/ui/parser/ranges-precedence.rs +++ b/tests/ui/parser/ranges-precedence.rs @@ -1,7 +1,8 @@ //@ run-pass +//@ edition: 2021 // Test that the precedence of ranges is correct - +use core::ops::{Add, RangeTo}; struct Foo { foo: usize, @@ -11,6 +12,13 @@ impl Foo { fn bar(&self) -> usize { 5 } } +impl Add> for Foo { + type Output = usize; + fn add(self, range: RangeTo) -> Self::Output { + self.foo + range.end + } +} + fn main() { let x = 1+3..4+5; assert_eq!(x, (4..9)); @@ -49,4 +57,22 @@ fn main() { let y = ..; assert_eq!(y, (..)); + + let reference = &..0; + assert_eq!(*reference, ..0); + let reference2 = &&..0; + assert_eq!(**reference2, ..0); + + let closure = || ..0; + assert_eq!(closure(), ..0); + + let sum = Foo { foo: 3 } + ..4; + assert_eq!(sum, 7); + + macro_rules! expr { + ($e:expr) => {}; + } + expr!(!..0); + expr!(-..0); + expr!(*..0); } diff --git a/tests/ui/parser/raw/issue-70677-panic-on-unterminated-raw-str-at-eof.rs b/tests/ui/parser/raw/issue-70677-panic-on-unterminated-raw-str-at-eof.rs index bdfc29a3d57ab..b7afb8fff81d1 100644 --- a/tests/ui/parser/raw/issue-70677-panic-on-unterminated-raw-str-at-eof.rs +++ b/tests/ui/parser/raw/issue-70677-panic-on-unterminated-raw-str-at-eof.rs @@ -2,4 +2,4 @@ // the last byte in the file (including not having a trailing newline) // Prior to the fix you get the error: 'expected item, found `r" ...`' // because the string being unterminated wasn't properly detected. -r" //~ unterminated raw string +r" //~ ERROR unterminated raw string diff --git a/tests/ui/parser/raw/raw-byte-string-eof.rs b/tests/ui/parser/raw/raw-byte-string-eof.rs index b74907b72b0cf..d62aa332c2f2a 100644 --- a/tests/ui/parser/raw/raw-byte-string-eof.rs +++ b/tests/ui/parser/raw/raw-byte-string-eof.rs @@ -1,3 +1,3 @@ pub fn main() { - br##"a"#; //~ unterminated raw string + br##"a"#; //~ ERROR unterminated raw string } diff --git a/tests/ui/parser/raw/too-many-hash.stderr b/tests/ui/parser/raw/too-many-hash.stderr index 1c46b5385cd9c..6b3854eb4a237 100644 --- a/tests/ui/parser/raw/too-many-hash.stderr +++ b/tests/ui/parser/raw/too-many-hash.stderr @@ -1,8 +1,8 @@ error: too many `#` symbols: raw strings may be delimited by up to 255 `#` symbols, but found 256 --> $DIR/too-many-hash.rs:4:19 | -LL | ... = r################################################################################################################################################################################################################################################################"very raw"##############################################################################################################################################################################################################################################################... - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... = r####################################################...#######################################; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/parser/recover/raw-no-const-mut.rs b/tests/ui/parser/recover/raw-no-const-mut.rs new file mode 100644 index 0000000000000..d0ae69cc30848 --- /dev/null +++ b/tests/ui/parser/recover/raw-no-const-mut.rs @@ -0,0 +1,31 @@ +fn a() { + let x = &raw 1; + //~^ ERROR expected one of +} + +fn b() { + [&raw const 1, &raw 2] + //~^ ERROR expected one of + //~| ERROR cannot find value `raw` in this scope + //~| ERROR cannot take address of a temporary +} + +fn c() { + if x == &raw z {} + //~^ ERROR expected `{` +} + +fn d() { + f(&raw 2); + //~^ ERROR expected one of + //~| ERROR cannot find value `raw` in this scope + //~| ERROR cannot find function `f` in this scope +} + +fn e() { + let x; + x = &raw 1; + //~^ ERROR expected one of +} + +fn main() {} diff --git a/tests/ui/parser/recover/raw-no-const-mut.stderr b/tests/ui/parser/recover/raw-no-const-mut.stderr new file mode 100644 index 0000000000000..65032c807953d --- /dev/null +++ b/tests/ui/parser/recover/raw-no-const-mut.stderr @@ -0,0 +1,109 @@ +error: expected one of `!`, `.`, `::`, `;`, `?`, `const`, `else`, `mut`, `{`, or an operator, found `1` + --> $DIR/raw-no-const-mut.rs:2:18 + | +LL | let x = &raw 1; + | ^ expected one of 10 possible tokens + | +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | let x = &raw const 1; + | +++++ +LL | let x = &raw mut 1; + | +++ + +error: expected one of `!`, `,`, `.`, `::`, `?`, `]`, `const`, `mut`, `{`, or an operator, found `2` + --> $DIR/raw-no-const-mut.rs:7:25 + | +LL | [&raw const 1, &raw 2] + | ^ expected one of 10 possible tokens + | +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | [&raw const 1, &raw const 2] + | +++++ +LL | [&raw const 1, &raw mut 2] + | +++ +help: missing `,` + | +LL | [&raw const 1, &raw, 2] + | + + +error: expected `{`, found `z` + --> $DIR/raw-no-const-mut.rs:14:18 + | +LL | if x == &raw z {} + | ^ expected `{` + | +note: the `if` expression is missing a block after this condition + --> $DIR/raw-no-const-mut.rs:14:8 + | +LL | if x == &raw z {} + | ^^^^^^^^^ +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | if x == &raw const z {} + | +++++ +LL | if x == &raw mut z {} + | +++ + +error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `2` + --> $DIR/raw-no-const-mut.rs:19:12 + | +LL | f(&raw 2); + | ^ expected one of 10 possible tokens + | +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | f(&raw const 2); + | +++++ +LL | f(&raw mut 2); + | +++ +help: missing `,` + | +LL | f(&raw, 2); + | + + +error: expected one of `!`, `.`, `::`, `;`, `?`, `const`, `mut`, `{`, `}`, or an operator, found `1` + --> $DIR/raw-no-const-mut.rs:27:14 + | +LL | x = &raw 1; + | ^ expected one of 10 possible tokens + | +help: `&raw` must be followed by `const` or `mut` to be a raw reference expression + | +LL | x = &raw const 1; + | +++++ +LL | x = &raw mut 1; + | +++ + +error[E0425]: cannot find value `raw` in this scope + --> $DIR/raw-no-const-mut.rs:7:21 + | +LL | [&raw const 1, &raw 2] + | ^^^ not found in this scope + +error[E0425]: cannot find value `raw` in this scope + --> $DIR/raw-no-const-mut.rs:19:8 + | +LL | f(&raw 2); + | ^^^ not found in this scope + +error[E0745]: cannot take address of a temporary + --> $DIR/raw-no-const-mut.rs:7:17 + | +LL | [&raw const 1, &raw 2] + | ^ temporary value + +error[E0425]: cannot find function `f` in this scope + --> $DIR/raw-no-const-mut.rs:19:5 + | +LL | fn a() { + | ------ similarly named function `a` defined here +... +LL | f(&raw 2); + | ^ help: a function with a similar name exists: `a` + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0425, E0745. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/parser/recover/recover-ampersand-less-ref-ty.rs b/tests/ui/parser/recover/recover-ampersand-less-ref-ty.rs new file mode 100644 index 0000000000000..8f1a42473b5a6 --- /dev/null +++ b/tests/ui/parser/recover/recover-ampersand-less-ref-ty.rs @@ -0,0 +1,13 @@ +//@ edition: 2021 + +struct Entity<'a> { + name: 'a str, //~ ERROR expected type, found lifetime + //~^ HELP you might have meant to write a reference type here +} + +struct Buffer<'buf> { + bytes: 'buf mut [u8], //~ ERROR expected type, found lifetime + //~^ HELP you might have meant to write a reference type here +} + +fn main() {} diff --git a/tests/ui/parser/recover/recover-ampersand-less-ref-ty.stderr b/tests/ui/parser/recover/recover-ampersand-less-ref-ty.stderr new file mode 100644 index 0000000000000..033348b2c4058 --- /dev/null +++ b/tests/ui/parser/recover/recover-ampersand-less-ref-ty.stderr @@ -0,0 +1,24 @@ +error: expected type, found lifetime + --> $DIR/recover-ampersand-less-ref-ty.rs:4:11 + | +LL | name: 'a str, + | ^^ expected type + | +help: you might have meant to write a reference type here + | +LL | name: &'a str, + | + + +error: expected type, found lifetime + --> $DIR/recover-ampersand-less-ref-ty.rs:9:12 + | +LL | bytes: 'buf mut [u8], + | ^^^^ expected type + | +help: you might have meant to write a reference type here + | +LL | bytes: &'buf mut [u8], + | + + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/recover/recover-assoc-const-constraint.rs b/tests/ui/parser/recover/recover-assoc-const-constraint.rs index 1453e6cb5cd7c..d938b4ccaca7e 100644 --- a/tests/ui/parser/recover/recover-assoc-const-constraint.rs +++ b/tests/ui/parser/recover/recover-assoc-const-constraint.rs @@ -1,4 +1,4 @@ -#[cfg(FALSE)] +#[cfg(false)] fn syntax() { bar::(); //~^ ERROR associated const equality is incomplete diff --git a/tests/ui/parser/recover/recover-assoc-eq-missing-term.rs b/tests/ui/parser/recover/recover-assoc-eq-missing-term.rs index 4b42c44dc64e5..73b4e22cab6fd 100644 --- a/tests/ui/parser/recover/recover-assoc-eq-missing-term.rs +++ b/tests/ui/parser/recover/recover-assoc-eq-missing-term.rs @@ -1,4 +1,4 @@ -#[cfg(FALSE)] +#[cfg(false)] fn syntax() { bar::(); //~ ERROR missing type to the right of `=` } diff --git a/tests/ui/parser/recover/recover-assoc-lifetime-constraint.rs b/tests/ui/parser/recover/recover-assoc-lifetime-constraint.rs index cb65f80b08909..30bac49e63a5f 100644 --- a/tests/ui/parser/recover/recover-assoc-lifetime-constraint.rs +++ b/tests/ui/parser/recover/recover-assoc-lifetime-constraint.rs @@ -1,4 +1,4 @@ -#[cfg(FALSE)] +#[cfg(false)] fn syntax() { bar::(); //~ ERROR lifetimes are not permitted in this context } diff --git a/tests/ui/parser/recover/recover-fn-trait-from-fn-kw.rs b/tests/ui/parser/recover/recover-fn-trait-from-fn-kw.rs index b6611e6273d37..1f19f4b9c064b 100644 --- a/tests/ui/parser/recover/recover-fn-trait-from-fn-kw.rs +++ b/tests/ui/parser/recover/recover-fn-trait-from-fn-kw.rs @@ -6,7 +6,7 @@ fn foo2(_: T) {} fn main() { foo(|| ()); - //~^ mismatched types + //~^ ERROR mismatched types foo2(|_: ()| {}); - //~^ type mismatch in closure arguments + //~^ ERROR type mismatch in closure arguments } diff --git a/tests/ui/parser/recover/recover-pat-exprs.rs b/tests/ui/parser/recover/recover-pat-exprs.rs index a78bb82828d3a..41b44ec96422c 100644 --- a/tests/ui/parser/recover/recover-pat-exprs.rs +++ b/tests/ui/parser/recover/recover-pat-exprs.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: HELP + // FieldExpression, TupleIndexingExpression fn field_access() { match 0 { @@ -28,7 +30,7 @@ fn array_indexing() { { let x[0; 20]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` { let x[]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` { let (x[]); } //~ error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` - //~^ missing `,` + //~^ HELP missing `,` } // MethodCallExpression, CallExpression, ErrorPropagationExpression diff --git a/tests/ui/parser/recover/recover-pat-exprs.stderr b/tests/ui/parser/recover/recover-pat-exprs.stderr index dcc1945d569c0..33000022b8f90 100644 --- a/tests/ui/parser/recover/recover-pat-exprs.stderr +++ b/tests/ui/parser/recover/recover-pat-exprs.stderr @@ -1,5 +1,5 @@ error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:5:9 + --> $DIR/recover-pat-exprs.rs:7:9 | LL | x.y => (), | ^^^ not a pattern @@ -17,13 +17,9 @@ LL ~ match 0 { LL | x => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x.y } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:6:9 + --> $DIR/recover-pat-exprs.rs:8:9 | LL | x.0 => (), | ^^^ not a pattern @@ -42,13 +38,9 @@ LL | x => (), LL | x.y => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x.0 } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:7:9 + --> $DIR/recover-pat-exprs.rs:9:9 | LL | x._0 => (), | ^^^^ not a pattern @@ -68,13 +60,9 @@ LL | x.y => (), LL | x.0 => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x._0 } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:8:9 + --> $DIR/recover-pat-exprs.rs:10:9 | LL | x.0.1 => (), | ^^^^^ not a pattern @@ -94,13 +82,9 @@ LL | x => (), LL | x._0 => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x.0.1 } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:9:9 + --> $DIR/recover-pat-exprs.rs:11:9 | LL | x.4.y.17.__z => (), | ^^^^^^^^^^^^ not a pattern @@ -120,43 +104,39 @@ LL | x => (), LL | x.0.1 => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x.4.y.17.__z } => (), - | +++++++ + error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` - --> $DIR/recover-pat-exprs.rs:12:12 + --> $DIR/recover-pat-exprs.rs:14:12 | LL | { let x.0e0; } | ^ expected one of `:`, `;`, `=`, `@`, or `|` error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` - --> $DIR/recover-pat-exprs.rs:13:12 + --> $DIR/recover-pat-exprs.rs:15:12 | LL | { let x.-0.0; } | ^ expected one of `:`, `;`, `=`, `@`, or `|` error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` - --> $DIR/recover-pat-exprs.rs:14:12 + --> $DIR/recover-pat-exprs.rs:16:12 | LL | { let x.-0; } | ^ expected one of `:`, `;`, `=`, `@`, or `|` error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` - --> $DIR/recover-pat-exprs.rs:16:12 + --> $DIR/recover-pat-exprs.rs:18:12 | LL | { let x.0u32; } | ^ expected one of `:`, `;`, `=`, `@`, or `|` error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` - --> $DIR/recover-pat-exprs.rs:17:12 + --> $DIR/recover-pat-exprs.rs:19:12 | LL | { let x.0.0_f64; } | ^ expected one of `:`, `;`, `=`, `@`, or `|` error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:23:9 + --> $DIR/recover-pat-exprs.rs:25:9 | LL | x[0] => (), | ^^^^ not a pattern @@ -173,13 +153,9 @@ LL + const VAL: /* Type */ = x[0]; LL ~ match 0 { LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x[0] } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:24:9 + --> $DIR/recover-pat-exprs.rs:26:9 | LL | x[..] => (), | ^^^^^ not a pattern @@ -197,31 +173,27 @@ LL ~ match 0 { LL | x[0] => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x[..] } => (), - | +++++++ + error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` - --> $DIR/recover-pat-exprs.rs:27:12 + --> $DIR/recover-pat-exprs.rs:29:12 | LL | { let x[0, 1, 2]; } | ^ expected one of `:`, `;`, `=`, `@`, or `|` error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` - --> $DIR/recover-pat-exprs.rs:28:12 + --> $DIR/recover-pat-exprs.rs:30:12 | LL | { let x[0; 20]; } | ^ expected one of `:`, `;`, `=`, `@`, or `|` error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` - --> $DIR/recover-pat-exprs.rs:29:12 + --> $DIR/recover-pat-exprs.rs:31:12 | LL | { let x[]; } | ^ expected one of `:`, `;`, `=`, `@`, or `|` error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` - --> $DIR/recover-pat-exprs.rs:30:13 + --> $DIR/recover-pat-exprs.rs:32:13 | LL | { let (x[]); } | ^ @@ -230,7 +202,7 @@ LL | { let (x[]); } | help: missing `,` error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:37:9 + --> $DIR/recover-pat-exprs.rs:39:9 | LL | x.f() => (), | ^^^^^ not a pattern @@ -247,13 +219,9 @@ LL + const VAL: /* Type */ = x.f(); LL ~ match 0 { LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x.f() } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:38:9 + --> $DIR/recover-pat-exprs.rs:40:9 | LL | x._f() => (), | ^^^^^^ not a pattern @@ -271,13 +239,9 @@ LL ~ match 0 { LL | x.f() => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x._f() } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:39:9 + --> $DIR/recover-pat-exprs.rs:41:9 | LL | x? => (), | ^^ not a pattern @@ -296,13 +260,9 @@ LL | x.f() => (), LL | x._f() => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x? } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:40:9 + --> $DIR/recover-pat-exprs.rs:42:9 | LL | ().f() => (), | ^^^^^^ not a pattern @@ -322,13 +282,9 @@ LL | x._f() => (), LL | x? => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { ().f() } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:41:9 + --> $DIR/recover-pat-exprs.rs:43:9 | LL | (0, x)?.f() => (), | ^^^^^^^^^^^ not a pattern @@ -348,13 +304,9 @@ LL | x.f() => (), LL | ().f() => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { (0, x)?.f() } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:42:9 + --> $DIR/recover-pat-exprs.rs:44:9 | LL | x.f().g() => (), | ^^^^^^^^^ not a pattern @@ -374,13 +326,9 @@ LL | x.f() => (), LL | (0, x)?.f() => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x.f().g() } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:43:9 + --> $DIR/recover-pat-exprs.rs:45:9 | LL | 0.f()?.g()?? => (), | ^^^^^^^^^^^^ not a pattern @@ -400,13 +348,9 @@ LL | x.f() => (), LL | x.f().g() => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { 0.f()?.g()?? } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:50:9 + --> $DIR/recover-pat-exprs.rs:52:9 | LL | x as usize => (), | ^^^^^^^^^^ not a pattern @@ -423,13 +367,9 @@ LL + const VAL: /* Type */ = x as usize; LL ~ match 0 { LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x as usize } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:51:9 + --> $DIR/recover-pat-exprs.rs:53:9 | LL | 0 as usize => (), | ^^^^^^^^^^ not a pattern @@ -447,13 +387,9 @@ LL ~ match 0 { LL | x as usize => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { 0 as usize } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:52:9 + --> $DIR/recover-pat-exprs.rs:54:9 | LL | x.f().0.4 as f32 => (), | ^^^^^^^^^^^^^^^^ not a pattern @@ -472,13 +408,9 @@ LL | x as usize => (), LL | 0 as usize => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x.f().0.4 as f32 } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:59:9 + --> $DIR/recover-pat-exprs.rs:61:9 | LL | 1 + 1 => (), | ^^^^^ not a pattern @@ -495,13 +427,9 @@ LL + const VAL: /* Type */ = 1 + 1; LL ~ match 0 { LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { 1 + 1 } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:60:9 + --> $DIR/recover-pat-exprs.rs:62:9 | LL | (1 + 2) * 3 => (), | ^^^^^^^^^^^ not a pattern @@ -519,13 +447,9 @@ LL ~ match 0 { LL | 1 + 1 => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { (1 + 2) * 3 } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:63:9 + --> $DIR/recover-pat-exprs.rs:65:9 | LL | x.0 > 2 => (), | ^^^^^^^ not a pattern @@ -545,13 +469,9 @@ LL | 1 + 1 => (), LL | LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x.0 > 2 } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:64:9 + --> $DIR/recover-pat-exprs.rs:66:9 | LL | x.0 == 2 => (), | ^^^^^^^^ not a pattern @@ -571,13 +491,9 @@ LL | 1 + 1 => (), LL | x.0 > 2 => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { x.0 == 2 } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:69:13 + --> $DIR/recover-pat-exprs.rs:71:13 | LL | (x, y.0 > 2) if x != 0 => (), | ^^^^^^^ not a pattern @@ -594,13 +510,9 @@ LL + const VAL: /* Type */ = y.0 > 2; LL ~ match (0, 0) { LL ~ (x, VAL) if x != 0 => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | (x, const { y.0 > 2 }) if x != 0 => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:70:13 + --> $DIR/recover-pat-exprs.rs:72:13 | LL | (x, y.0 > 2) if x != 0 || x != 1 => (), | ^^^^^^^ not a pattern @@ -618,13 +530,9 @@ LL ~ match (0, 0) { LL | (x, y.0 > 2) if x != 0 => (), LL ~ (x, VAL) if x != 0 || x != 1 => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | (x, const { y.0 > 2 }) if x != 0 || x != 1 => (), - | +++++++ + error: left-hand side of `@` must be a binding - --> $DIR/recover-pat-exprs.rs:83:9 + --> $DIR/recover-pat-exprs.rs:85:9 | LL | x.sqrt() @ .. => (), | --------^^^-- @@ -635,13 +543,13 @@ LL | x.sqrt() @ .. => (), = note: bindings are `x`, `mut x`, `ref x`, and `ref mut x` error: expected one of `)`, `,`, `if`, or `|`, found `+` - --> $DIR/recover-pat-exprs.rs:97:12 + --> $DIR/recover-pat-exprs.rs:99:12 | LL | (_ + 1) => (), | ^ expected one of `)`, `,`, `if`, or `|` error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:81:9 + --> $DIR/recover-pat-exprs.rs:83:9 | LL | u8::MAX.abs() => (), | ^^^^^^^^^^^^^ not a pattern @@ -658,13 +566,9 @@ LL + const VAL: /* Type */ = u8::MAX.abs(); LL ~ match u8::MAX { LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { u8::MAX.abs() } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:86:17 + --> $DIR/recover-pat-exprs.rs:88:17 | LL | z @ w @ v.u() => (), | ^^^^^ not a pattern @@ -684,13 +588,9 @@ LL | u8::MAX.abs() => (), LL | LL ~ z @ w @ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | z @ w @ const { v.u() } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:88:9 + --> $DIR/recover-pat-exprs.rs:90:9 | LL | y.ilog(3) => (), | ^^^^^^^^^ not a pattern @@ -710,13 +610,9 @@ LL | u8::MAX.abs() => (), LL | LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { y.ilog(3) } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:90:9 + --> $DIR/recover-pat-exprs.rs:92:9 | LL | n + 1 => (), | ^^^^^ not a pattern @@ -736,13 +632,9 @@ LL | u8::MAX.abs() => (), LL | LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { n + 1 } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:92:10 + --> $DIR/recover-pat-exprs.rs:94:10 | LL | ("".f() + 14 * 8) => (), | ^^^^^^^^^^^^^^^ not a pattern @@ -762,13 +654,9 @@ LL | u8::MAX.abs() => (), LL | LL ~ (VAL) => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | (const { "".f() + 14 * 8 }) => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:95:9 + --> $DIR/recover-pat-exprs.rs:97:9 | LL | f?() => (), | ^^^^ not a pattern @@ -788,13 +676,9 @@ LL | u8::MAX.abs() => (), LL | 0 | ((1) | 2) | 3 => (), LL ~ VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { f?() } => (), - | +++++++ + error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:101:9 + --> $DIR/recover-pat-exprs.rs:103:9 | LL | let 1 + 1 = 2; | ^^^^^ not a pattern @@ -802,7 +686,7 @@ LL | let 1 + 1 = 2; = note: arbitrary expressions are not allowed in patterns: error: expected one of `)`, `,`, `@`, `if`, or `|`, found `*` - --> $DIR/recover-pat-exprs.rs:104:28 + --> $DIR/recover-pat-exprs.rs:106:28 | LL | let b = matches!(x, (x * x | x.f()) | x[0]); | ^ expected one of `)`, `,`, `@`, `if`, or `|` @@ -811,7 +695,7 @@ LL | let b = matches!(x, (x * x | x.f()) | x[0]); = note: while parsing argument for this `pat` macro fragment error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:60:10 + --> $DIR/recover-pat-exprs.rs:62:10 | LL | (1 + 2) * 3 => (), | ^^^^^ not a pattern @@ -819,7 +703,7 @@ LL | (1 + 2) * 3 => (), = note: arbitrary expressions are not allowed in patterns: error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:75:5 + --> $DIR/recover-pat-exprs.rs:77:5 | LL | 1 + 2 * PI.cos() => 2, | ^^^^^^^^^^^^^^^^ not a pattern @@ -827,7 +711,7 @@ LL | 1 + 2 * PI.cos() => 2, = note: arbitrary expressions are not allowed in patterns: error: expected a pattern, found an expression - --> $DIR/recover-pat-exprs.rs:83:9 + --> $DIR/recover-pat-exprs.rs:85:9 | LL | x.sqrt() @ .. => (), | ^^^^^^^^ not a pattern diff --git a/tests/ui/parser/recover/recover-pat-issues.stderr b/tests/ui/parser/recover/recover-pat-issues.stderr index 0c65b16dd951f..ec7fcda3497f4 100644 --- a/tests/ui/parser/recover/recover-pat-issues.stderr +++ b/tests/ui/parser/recover/recover-pat-issues.stderr @@ -16,10 +16,6 @@ LL + const VAL: /* Type */ = "hi".to_owned(); LL ~ match foo { LL ~ Foo(VAL) => true, | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | Foo(const { "hi".to_owned() }) => true, - | +++++++ + error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:14:20 @@ -39,10 +35,6 @@ LL + const BAZ: /* Type */ = "hi".to_owned(); LL ~ match bar { LL ~ Bar { baz: BAZ } => true, | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | Bar { baz: const { "hi".to_owned() } } => true, - | +++++++ + error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:25:11 @@ -62,10 +54,6 @@ LL + const VAL: /* Type */ = "foo".to_string(); LL ~ match foo.as_slice() { LL ~ &[VAL] => {} | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | &[const { "foo".to_string() }] => {} - | +++++++ + error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:36:17 @@ -79,10 +67,6 @@ help: consider extracting the expression into a `const` LL + const VAL: /* Type */ = MAGIC.0 as usize; LL ~ if let Some(VAL) = None:: {} | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | if let Some(const { MAGIC.0 as usize }) = None:: {} - | +++++++ + error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:41:13 @@ -96,10 +80,6 @@ help: consider extracting the expression into a `const` LL + const VAL: /* Type */ = -1.some(4); LL ~ if let (VAL) = (0, Some(4)) {} | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | if let (const { -1.some(4) }) = (0, Some(4)) {} - | +++++++ + error: expected a pattern, found an expression --> $DIR/recover-pat-issues.rs:44:13 @@ -113,10 +93,6 @@ help: consider extracting the expression into a `const` LL + const VAL: /* Type */ = -1.Some(4); LL ~ if let (VAL) = (0, Some(4)) {} | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | if let (const { -1.Some(4) }) = (0, Some(4)) {} - | +++++++ + error: aborting due to 6 previous errors diff --git a/tests/ui/parser/recover/recover-pat-lets.stderr b/tests/ui/parser/recover/recover-pat-lets.stderr index 55252729d7baa..ab79e4ebcaebe 100644 --- a/tests/ui/parser/recover/recover-pat-lets.stderr +++ b/tests/ui/parser/recover/recover-pat-lets.stderr @@ -34,10 +34,6 @@ help: consider extracting the expression into a `const` LL + const VAL: /* Type */ = 1 + 1; LL ~ let Some(VAL) = x else { | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | let Some(const { 1 + 1 }) = x else { - | +++++++ + error: expected a pattern, found an expression --> $DIR/recover-pat-lets.rs:17:17 @@ -51,10 +47,6 @@ help: consider extracting the expression into a `const` LL + const VAL: /* Type */ = 1 + 1; LL ~ if let Some(VAL) = x { | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | if let Some(const { 1 + 1 }) = x { - | +++++++ + error: aborting due to 5 previous errors diff --git a/tests/ui/parser/recover/recover-pat-ranges.stderr b/tests/ui/parser/recover/recover-pat-ranges.stderr index e8f323596d0fa..6c17182618b5e 100644 --- a/tests/ui/parser/recover/recover-pat-ranges.stderr +++ b/tests/ui/parser/recover/recover-pat-ranges.stderr @@ -98,10 +98,6 @@ LL | 0..=1 => (), LL | LL ~ ..=VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | ..=const { 1 + 2 } => (), - | +++++++ + error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:15:10 @@ -119,10 +115,6 @@ LL | 0..=1 => (), LL | LL ~ (VAL).. => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | (const { -4 + 0 }).. => (), - | +++++++ + error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:18:10 @@ -140,10 +132,6 @@ LL | 0..=1 => (), LL | LL ~ (VAL)...1 * 2 => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | (const { 1 + 4 })...1 * 2 => (), - | +++++++ + error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:18:19 @@ -161,10 +149,6 @@ LL | 0..=1 => (), LL | LL ~ (1 + 4)...VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | (1 + 4)...const { 1 * 2 } => (), - | +++++++ + error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:24:9 @@ -182,10 +166,6 @@ LL | 0..=1 => (), LL | LL ~ VAL..="y".z() => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | const { 0.x() }..="y".z() => (), - | +++++++ + error: expected a pattern range bound, found an expression --> $DIR/recover-pat-ranges.rs:24:17 @@ -203,10 +183,6 @@ LL | 0..=1 => (), LL | LL ~ 0.x()..=VAL => (), | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | 0.x()..=const { "y".z() } => (), - | +++++++ + warning: `...` range patterns are deprecated --> $DIR/recover-pat-ranges.rs:18:16 diff --git a/tests/ui/parser/recover/recover-pat-wildcards.stderr b/tests/ui/parser/recover/recover-pat-wildcards.stderr index f939e5133700f..ebc1cbf7d591e 100644 --- a/tests/ui/parser/recover/recover-pat-wildcards.stderr +++ b/tests/ui/parser/recover/recover-pat-wildcards.stderr @@ -84,10 +84,6 @@ LL + const VAL: /* Type */ = 2 + _; LL ~ match 9 { LL ~ 4..=(VAL) => () | -help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) - | -LL | 4..=(const { 2 + _ }) => () - | +++++++ + error: aborting due to 11 previous errors diff --git a/tests/ui/parser/recover/recover-quantified-closure.rs b/tests/ui/parser/recover/recover-quantified-closure.rs index 10af39b700748..1f5004ad0998d 100644 --- a/tests/ui/parser/recover/recover-quantified-closure.rs +++ b/tests/ui/parser/recover/recover-quantified-closure.rs @@ -7,6 +7,6 @@ fn main() { enum Foo { Bar } fn foo(x: impl Iterator) { for ::Bar in x {} - //~^ ERROR expected one of `move`, `static`, `|` + //~^ ERROR expected one of `move`, `static`, `use`, `|` //~^^ ERROR `for<...>` binders for closures are experimental } diff --git a/tests/ui/parser/recover/recover-quantified-closure.stderr b/tests/ui/parser/recover/recover-quantified-closure.stderr index 6e03bbb586959..48dea071ae64e 100644 --- a/tests/ui/parser/recover/recover-quantified-closure.stderr +++ b/tests/ui/parser/recover/recover-quantified-closure.stderr @@ -1,8 +1,8 @@ -error: expected one of `move`, `static`, `|`, or `||`, found `::` +error: expected one of `move`, `static`, `use`, `|`, or `||`, found `::` --> $DIR/recover-quantified-closure.rs:9:14 | LL | for ::Bar in x {} - | ^^ expected one of `move`, `static`, `|`, or `||` + | ^^ expected one of `move`, `static`, `use`, `|`, or `||` error[E0658]: `for<...>` binders for closures are experimental --> $DIR/recover-quantified-closure.rs:2:5 diff --git a/tests/ui/parser/recover/turbofish-arg-with-stray-colon.stderr b/tests/ui/parser/recover/turbofish-arg-with-stray-colon.stderr index 583b98c650f03..c0f9db9184c0b 100644 --- a/tests/ui/parser/recover/turbofish-arg-with-stray-colon.stderr +++ b/tests/ui/parser/recover/turbofish-arg-with-stray-colon.stderr @@ -4,7 +4,6 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, fo LL | let x = Tr; | ^ expected one of 8 possible tokens | - = note: type ascription syntax has been removed, see issue #101728 help: maybe write a path separator here | LL | let x = Tr; diff --git a/tests/ui/parser/require-parens-for-chained-comparison.rs b/tests/ui/parser/require-parens-for-chained-comparison.rs index 916f1b83db245..21a908923f22a 100644 --- a/tests/ui/parser/require-parens-for-chained-comparison.rs +++ b/tests/ui/parser/require-parens-for-chained-comparison.rs @@ -27,7 +27,7 @@ fn main() { //~| ERROR invalid label name f<'_>(); - //~^ comparison operators cannot be chained + //~^ ERROR comparison operators cannot be chained //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments //~| ERROR expected //~| HELP add `'` to close the char literal diff --git a/tests/ui/parser/self-param-syntactic-pass.rs b/tests/ui/parser/self-param-syntactic-pass.rs index c7fdc5297164a..331e652f9c576 100644 --- a/tests/ui/parser/self-param-syntactic-pass.rs +++ b/tests/ui/parser/self-param-syntactic-pass.rs @@ -5,7 +5,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn free() { fn f(self) {} fn f(mut self) {} @@ -17,7 +17,7 @@ fn free() { fn f(mut self: u8) {} } -#[cfg(FALSE)] +#[cfg(false)] extern "C" { fn f(self); fn f(mut self); @@ -29,7 +29,7 @@ extern "C" { fn f(mut self: u8); } -#[cfg(FALSE)] +#[cfg(false)] trait X { fn f(self) {} fn f(mut self) {} @@ -41,7 +41,7 @@ trait X { fn f(mut self: u8) {} } -#[cfg(FALSE)] +#[cfg(false)] impl X for Y { fn f(self) {} fn f(mut self) {} @@ -53,7 +53,7 @@ impl X for Y { fn f(mut self: u8) {} } -#[cfg(FALSE)] +#[cfg(false)] impl X for Y { type X = fn(self); type X = fn(mut self); diff --git a/tests/ui/parser/semi-in-let-chain.rs b/tests/ui/parser/semi-in-let-chain.rs index 9c21af0372d25..522b90ea250b7 100644 --- a/tests/ui/parser/semi-in-let-chain.rs +++ b/tests/ui/parser/semi-in-let-chain.rs @@ -1,6 +1,5 @@ // Issue #117720 - -#![feature(let_chains)] +//@ edition: 2024 fn main() { if let () = () diff --git a/tests/ui/parser/semi-in-let-chain.stderr b/tests/ui/parser/semi-in-let-chain.stderr index c1a8f92965eb8..f36d5e041e5d5 100644 --- a/tests/ui/parser/semi-in-let-chain.stderr +++ b/tests/ui/parser/semi-in-let-chain.stderr @@ -1,11 +1,11 @@ error: expected `{`, found `;` - --> $DIR/semi-in-let-chain.rs:7:23 + --> $DIR/semi-in-let-chain.rs:6:23 | LL | && let () = (); | ^ expected `{` | note: you likely meant to continue parsing the let-chain starting here - --> $DIR/semi-in-let-chain.rs:8:9 + --> $DIR/semi-in-let-chain.rs:7:9 | LL | && let () = () | ^^^^^^ @@ -16,13 +16,13 @@ LL + && let () = () | error: expected `{`, found `;` - --> $DIR/semi-in-let-chain.rs:15:20 + --> $DIR/semi-in-let-chain.rs:14:20 | LL | && () == (); | ^ expected `{` | note: the `if` expression is missing a block after this condition - --> $DIR/semi-in-let-chain.rs:14:8 + --> $DIR/semi-in-let-chain.rs:13:8 | LL | if let () = () | ________^ @@ -30,13 +30,13 @@ LL | | && () == (); | |___________________^ error: expected `{`, found `;` - --> $DIR/semi-in-let-chain.rs:23:20 + --> $DIR/semi-in-let-chain.rs:22:20 | LL | && () == (); | ^ expected `{` | note: you likely meant to continue parsing the let-chain starting here - --> $DIR/semi-in-let-chain.rs:24:9 + --> $DIR/semi-in-let-chain.rs:23:9 | LL | && let () = () | ^^^^^^ diff --git a/tests/ui/parser/shebang/issue-71471-ignore-tidy.rs b/tests/ui/parser/shebang/issue-71471-ignore-tidy.rs index e9bff91f1e8ce..858c06048cc65 100644 --- a/tests/ui/parser/shebang/issue-71471-ignore-tidy.rs +++ b/tests/ui/parser/shebang/issue-71471-ignore-tidy.rs @@ -1,4 +1,4 @@ -#!B //~ expected `[`, found `B` +#!B //~ ERROR expected `[`, found `B` //@ reference: input.shebang diff --git a/tests/ui/parser/shebang/issue-71471-ignore-tidy.stderr b/tests/ui/parser/shebang/issue-71471-ignore-tidy.stderr index 41cd4fb93faa0..cdd36ba4cae5b 100644 --- a/tests/ui/parser/shebang/issue-71471-ignore-tidy.stderr +++ b/tests/ui/parser/shebang/issue-71471-ignore-tidy.stderr @@ -3,6 +3,9 @@ error: expected `[`, found `B` | LL | #!B | ^ expected `[` + | + = note: the token sequence `#!` here looks like the start of a shebang interpreter directive but it is not + = help: if you meant this to be a shebang interpreter directive, move it to the very start of the file error: aborting due to 1 previous error diff --git a/tests/ui/parser/shebang/shebang-must-start-file.rs b/tests/ui/parser/shebang/shebang-must-start-file.rs index f91e32f744e15..f956cff3b0810 100644 --- a/tests/ui/parser/shebang/shebang-must-start-file.rs +++ b/tests/ui/parser/shebang/shebang-must-start-file.rs @@ -1,5 +1,5 @@ // something on the first line for tidy -#!/bin/bash //~ expected `[`, found `/` +#!/bin/bash //~ ERROR expected `[`, found `/` //@ reference: input.shebang diff --git a/tests/ui/parser/shebang/shebang-must-start-file.stderr b/tests/ui/parser/shebang/shebang-must-start-file.stderr index 56991c96b7a0a..cf897d0778087 100644 --- a/tests/ui/parser/shebang/shebang-must-start-file.stderr +++ b/tests/ui/parser/shebang/shebang-must-start-file.stderr @@ -3,6 +3,9 @@ error: expected `[`, found `/` | LL | #!/bin/bash | ^ expected `[` + | + = note: the token sequence `#!` here looks like the start of a shebang interpreter directive but it is not + = help: if you meant this to be a shebang interpreter directive, move it to the very start of the file error: aborting due to 1 previous error diff --git a/tests/ui/parser/shebang/shebang-split.rs b/tests/ui/parser/shebang/shebang-split.rs new file mode 100644 index 0000000000000..eb8f0f769c851 --- /dev/null +++ b/tests/ui/parser/shebang/shebang-split.rs @@ -0,0 +1,4 @@ +// empty line +# !/bin/env +//~^ ERROR expected `[`, found `/` +// checks that diagnostics for shebang lookalikes is not present diff --git a/tests/ui/parser/shebang/shebang-split.stderr b/tests/ui/parser/shebang/shebang-split.stderr new file mode 100644 index 0000000000000..804df1f0086e1 --- /dev/null +++ b/tests/ui/parser/shebang/shebang-split.stderr @@ -0,0 +1,8 @@ +error: expected `[`, found `/` + --> $DIR/shebang-split.rs:2:4 + | +LL | # !/bin/env + | ^ expected `[` + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/stripped-nested-outline-mod-pass.rs b/tests/ui/parser/stripped-nested-outline-mod-pass.rs index 8909d8ae0eb63..166a60f257a33 100644 --- a/tests/ui/parser/stripped-nested-outline-mod-pass.rs +++ b/tests/ui/parser/stripped-nested-outline-mod-pass.rs @@ -5,7 +5,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] mod foo { mod bar { mod baz; // This was an error before. diff --git a/tests/ui/parser/struct-literal-in-for.rs b/tests/ui/parser/struct-literal-in-for.rs deleted file mode 100644 index 3227ae37bfd05..0000000000000 --- a/tests/ui/parser/struct-literal-in-for.rs +++ /dev/null @@ -1,17 +0,0 @@ -struct Foo { - x: isize, -} - -impl Foo { - fn hi(&self) -> bool { - true - } -} - -fn main() { - for x in Foo { //~ ERROR struct literals are not allowed here - x: 3 //~^ ERROR `bool` is not an iterator - }.hi() { - println!("yo"); - } -} diff --git a/tests/ui/parser/struct-literal-in-for.stderr b/tests/ui/parser/struct-literal-in-for.stderr deleted file mode 100644 index 1c91eba68e399..0000000000000 --- a/tests/ui/parser/struct-literal-in-for.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/struct-literal-in-for.rs:12:14 - | -LL | for x in Foo { - | ______________^ -LL | | x: 3 -LL | | }.hi() { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ for x in (Foo { -LL | x: 3 -LL ~ }).hi() { - | - -error[E0277]: `bool` is not an iterator - --> $DIR/struct-literal-in-for.rs:12:14 - | -LL | for x in Foo { - | ______________^ -LL | | x: 3 -LL | | }.hi() { - | |__________^ `bool` is not an iterator - | - = help: the trait `Iterator` is not implemented for `bool` - = note: required for `bool` to implement `IntoIterator` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/parser/struct-literal-in-if.rs b/tests/ui/parser/struct-literal-in-if.rs deleted file mode 100644 index c4a253c3da25a..0000000000000 --- a/tests/ui/parser/struct-literal-in-if.rs +++ /dev/null @@ -1,22 +0,0 @@ -struct Foo { - x: isize, -} - -impl Foo { - fn hi(&self) -> bool { - true - } -} - -fn main() { - if Foo { //~ ERROR struct literals are not allowed here - x: 3 - }.hi() { - println!("yo"); - } - if let true = Foo { //~ ERROR struct literals are not allowed here - x: 3 - }.hi() { - println!("yo"); - } -} diff --git a/tests/ui/parser/struct-literal-in-if.stderr b/tests/ui/parser/struct-literal-in-if.stderr deleted file mode 100644 index 8b72469fcf582..0000000000000 --- a/tests/ui/parser/struct-literal-in-if.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/struct-literal-in-if.rs:12:8 - | -LL | if Foo { - | ________^ -LL | | x: 3 -LL | | }.hi() { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ if (Foo { -LL | x: 3 -LL ~ }).hi() { - | - -error: struct literals are not allowed here - --> $DIR/struct-literal-in-if.rs:17:19 - | -LL | if let true = Foo { - | ___________________^ -LL | | x: 3 -LL | | }.hi() { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ if let true = (Foo { -LL | x: 3 -LL ~ }).hi() { - | - -error: aborting due to 2 previous errors - diff --git a/tests/ui/parser/struct-literal-in-match-discriminant.rs b/tests/ui/parser/struct-literal-in-match-discriminant.rs deleted file mode 100644 index ce132df5a888b..0000000000000 --- a/tests/ui/parser/struct-literal-in-match-discriminant.rs +++ /dev/null @@ -1,13 +0,0 @@ -struct Foo { - x: isize, -} - -fn main() { - match Foo { //~ ERROR struct literals are not allowed here - x: 3 - } { - Foo { - x: x - } => {} - } -} diff --git a/tests/ui/parser/struct-literal-in-match-discriminant.stderr b/tests/ui/parser/struct-literal-in-match-discriminant.stderr deleted file mode 100644 index 5177f5f126e0b..0000000000000 --- a/tests/ui/parser/struct-literal-in-match-discriminant.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/struct-literal-in-match-discriminant.rs:6:11 - | -LL | match Foo { - | ___________^ -LL | | x: 3 -LL | | } { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ match (Foo { -LL | x: 3 -LL ~ }) { - | - -error: aborting due to 1 previous error - diff --git a/tests/ui/parser/struct-literal-in-while.rs b/tests/ui/parser/struct-literal-in-while.rs deleted file mode 100644 index 86931f7888dd8..0000000000000 --- a/tests/ui/parser/struct-literal-in-while.rs +++ /dev/null @@ -1,22 +0,0 @@ -struct Foo { - x: isize, -} - -impl Foo { - fn hi(&self) -> bool { - true - } -} - -fn main() { - while Foo { //~ ERROR struct literals are not allowed here - x: 3 - }.hi() { - println!("yo"); - } - while let true = Foo { //~ ERROR struct literals are not allowed here - x: 3 - }.hi() { - println!("yo"); - } -} diff --git a/tests/ui/parser/struct-literal-in-while.stderr b/tests/ui/parser/struct-literal-in-while.stderr deleted file mode 100644 index 13d003608a1b0..0000000000000 --- a/tests/ui/parser/struct-literal-in-while.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/struct-literal-in-while.rs:12:11 - | -LL | while Foo { - | ___________^ -LL | | x: 3 -LL | | }.hi() { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ while (Foo { -LL | x: 3 -LL ~ }).hi() { - | - -error: struct literals are not allowed here - --> $DIR/struct-literal-in-while.rs:17:22 - | -LL | while let true = Foo { - | ______________________^ -LL | | x: 3 -LL | | }.hi() { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ while let true = (Foo { -LL | x: 3 -LL ~ }).hi() { - | - -error: aborting due to 2 previous errors - diff --git a/tests/ui/parser/struct-literal-restrictions-in-lamda.rs b/tests/ui/parser/struct-literal-restrictions-in-lamda.rs deleted file mode 100644 index e185153dcf62a..0000000000000 --- a/tests/ui/parser/struct-literal-restrictions-in-lamda.rs +++ /dev/null @@ -1,17 +0,0 @@ -struct Foo { - x: isize, -} - -impl Foo { - fn hi(&self) -> bool { - true - } -} - -fn main() { - while || Foo { //~ ERROR struct literals are not allowed here - x: 3 //~^ ERROR mismatched types - }.hi() { - println!("yo"); - } -} diff --git a/tests/ui/parser/struct-literal-restrictions-in-lamda.stderr b/tests/ui/parser/struct-literal-restrictions-in-lamda.stderr deleted file mode 100644 index c715486e2da9c..0000000000000 --- a/tests/ui/parser/struct-literal-restrictions-in-lamda.stderr +++ /dev/null @@ -1,37 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/struct-literal-restrictions-in-lamda.rs:12:14 - | -LL | while || Foo { - | ______________^ -LL | | x: 3 -LL | | }.hi() { - | |_____^ - | -help: surround the struct literal with parentheses - | -LL ~ while || (Foo { -LL | x: 3 -LL ~ }).hi() { - | - -error[E0308]: mismatched types - --> $DIR/struct-literal-restrictions-in-lamda.rs:12:11 - | -LL | while || Foo { - | ___________^ -LL | | x: 3 -LL | | }.hi() { - | |__________^ expected `bool`, found closure - | - = note: expected type `bool` - found closure `{closure@$DIR/struct-literal-restrictions-in-lamda.rs:12:11: 12:13}` -help: use parentheses to call this closure - | -LL ~ while (|| Foo { -LL | x: 3 -LL ~ }.hi())() { - | - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/struct-literal-variant-in-if.rs b/tests/ui/parser/struct-literal-variant-in-if.rs deleted file mode 100644 index 4ef8effaf1f5f..0000000000000 --- a/tests/ui/parser/struct-literal-variant-in-if.rs +++ /dev/null @@ -1,25 +0,0 @@ -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -enum E { - V { field: bool }, - I { field1: bool, field2: usize }, - J { field: isize }, - K { field: &'static str}, -} -fn test_E(x: E) { - let field = true; - if x == E::V { field } {} - //~^ ERROR expected value, found struct variant `E::V` - //~| ERROR mismatched types - if x == E::I { field1: true, field2: 42 } {} - //~^ ERROR struct literals are not allowed here - if x == E::V { field: false } {} - //~^ ERROR struct literals are not allowed here - if x == E::J { field: -42 } {} - //~^ ERROR struct literals are not allowed here - if x == E::K { field: "" } {} - //~^ ERROR struct literals are not allowed here - let y: usize = (); - //~^ ERROR mismatched types -} - -fn main() {} diff --git a/tests/ui/parser/struct-literal-variant-in-if.stderr b/tests/ui/parser/struct-literal-variant-in-if.stderr deleted file mode 100644 index 15f059f145bbb..0000000000000 --- a/tests/ui/parser/struct-literal-variant-in-if.stderr +++ /dev/null @@ -1,76 +0,0 @@ -error: struct literals are not allowed here - --> $DIR/struct-literal-variant-in-if.rs:13:13 - | -LL | if x == E::I { field1: true, field2: 42 } {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if x == (E::I { field1: true, field2: 42 }) {} - | + + - -error: struct literals are not allowed here - --> $DIR/struct-literal-variant-in-if.rs:15:13 - | -LL | if x == E::V { field: false } {} - | ^^^^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if x == (E::V { field: false }) {} - | + + - -error: struct literals are not allowed here - --> $DIR/struct-literal-variant-in-if.rs:17:13 - | -LL | if x == E::J { field: -42 } {} - | ^^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if x == (E::J { field: -42 }) {} - | + + - -error: struct literals are not allowed here - --> $DIR/struct-literal-variant-in-if.rs:19:13 - | -LL | if x == E::K { field: "" } {} - | ^^^^^^^^^^^^^^^^^^ - | -help: surround the struct literal with parentheses - | -LL | if x == (E::K { field: "" }) {} - | + + - -error[E0533]: 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: you might have meant to create a new value of the struct - | -LL | if x == (E::V { field }) {} - | + + - -error[E0308]: mismatched types - --> $DIR/struct-literal-variant-in-if.rs:10:20 - | -LL | if x == E::V { field } {} - | ---------------^^^^^-- - | | | - | | expected `()`, found `bool` - | expected this to be `()` - -error[E0308]: mismatched types - --> $DIR/struct-literal-variant-in-if.rs:21:20 - | -LL | let y: usize = (); - | ----- ^^ expected `usize`, found `()` - | | - | expected due to this - -error: aborting due to 7 previous errors - -Some errors have detailed explanations: E0308, E0533. -For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/struct-literals-in-invalid-places.rs b/tests/ui/parser/struct-literals-in-invalid-places.rs new file mode 100644 index 0000000000000..eed51b9458318 --- /dev/null +++ b/tests/ui/parser/struct-literals-in-invalid-places.rs @@ -0,0 +1,92 @@ +fn main() { + if Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here + println!("yo"); + } + if let true = Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here + println!("yo"); + } + + for x in Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here + //~^ ERROR `bool` is not an iterator + println!("yo"); + } + + while Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here + println!("yo"); + } + while let true = Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here + println!("yo"); + } + + match Foo { x: 3 } { //~ ERROR struct literals are not allowed here + Foo { x: x } => {} + } + + let _ = |x: E| { + let field = true; + if x == E::V { field } {} + //~^ ERROR expected value, found struct variant `E::V` + //~| ERROR mismatched types + if x == E::I { field1: true, field2: 42 } {} + //~^ ERROR struct literals are not allowed here + if x == E::V { field: false } {} + //~^ ERROR struct literals are not allowed here + if x == E::J { field: -42 } {} + //~^ ERROR struct literals are not allowed here + if x == E::K { field: "" } {} + //~^ ERROR struct literals are not allowed here + let y: usize = (); + //~^ ERROR mismatched types + }; + + // Regression test for . + while || Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here + //~^ ERROR mismatched types + println!("yo"); + } + + // This uses `one()` over `1` as token `one` may begin a type and thus back when type ascription + // `$expr : $ty` still existed, `{ x: one` could've been the start of a block expr which used to + // make the compiler take a different execution path. Now it no longer makes a difference tho. + + // Regression test for . + if Foo { x: one(), }.hi() { //~ ERROR struct literals are not allowed here + println!("Positive!"); + } + + const FOO: Foo = Foo { x: 1 }; + // Below, test that we correctly parenthesize the struct literals. + + // Regression test for . + if FOO == self::Foo { x: one() } {} //~ ERROR struct literals are not allowed here + + if FOO == Foo::<> { x: one() } {} //~ ERROR struct literals are not allowed here + + fn env>() { + if FOO == ::Out { x: one() } {} //~ ERROR struct literals are not allowed here + //~^ ERROR usage of qualified paths in this context is experimental + } +} + +#[derive(PartialEq, Eq)] +struct Foo { + x: isize, +} + +impl Foo { + fn hi(&self) -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +enum E { + V { field: bool }, + I { field1: bool, field2: usize }, + J { field: isize }, + K { field: &'static str}, +} + +fn one() -> isize { 1 } + +trait Trait { type Out; } diff --git a/tests/ui/parser/struct-literals-in-invalid-places.stderr b/tests/ui/parser/struct-literals-in-invalid-places.stderr new file mode 100644 index 0000000000000..39dc2d2efb75b --- /dev/null +++ b/tests/ui/parser/struct-literals-in-invalid-places.stderr @@ -0,0 +1,234 @@ +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:2:8 + | +LL | if Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if (Foo { x: 3 }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:5:19 + | +LL | if let true = Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if let true = (Foo { x: 3 }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:9:14 + | +LL | for x in Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | for x in (Foo { x: 3 }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:14:11 + | +LL | while Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | while (Foo { x: 3 }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:17:22 + | +LL | while let true = Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | while let true = (Foo { x: 3 }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:21:11 + | +LL | match Foo { x: 3 } { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | match (Foo { x: 3 }) { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:30:17 + | +LL | if x == E::I { field1: true, field2: 42 } {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if x == (E::I { field1: true, field2: 42 }) {} + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:32:17 + | +LL | if x == E::V { field: false } {} + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if x == (E::V { field: false }) {} + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:34:17 + | +LL | if x == E::J { field: -42 } {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if x == (E::J { field: -42 }) {} + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:36:17 + | +LL | if x == E::K { field: "" } {} + | ^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if x == (E::K { field: "" }) {} + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:43:14 + | +LL | while || Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | while || (Foo { x: 3 }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:53:8 + | +LL | if Foo { x: one(), }.hi() { + | ^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if (Foo { x: one(), }).hi() { + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:61:15 + | +LL | if FOO == self::Foo { x: one() } {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if FOO == (self::Foo { x: one() }) {} + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:63:15 + | +LL | if FOO == Foo::<> { x: one() } {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if FOO == (Foo::<> { x: one() }) {} + | + + + +error: struct literals are not allowed here + --> $DIR/struct-literals-in-invalid-places.rs:66:19 + | +LL | if FOO == ::Out { x: one() } {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if FOO == (::Out { x: one() }) {} + | + + + +error[E0658]: usage of qualified paths in this context is experimental + --> $DIR/struct-literals-in-invalid-places.rs:66:19 + | +LL | if FOO == ::Out { x: one() } {} + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #86935 for more information + = help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0277]: `bool` is not an iterator + --> $DIR/struct-literals-in-invalid-places.rs:9:14 + | +LL | for x in Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^^^^^^ `bool` is not an iterator + | + = help: the trait `Iterator` is not implemented for `bool` + = note: required for `bool` to implement `IntoIterator` + +error[E0533]: expected value, found struct variant `E::V` + --> $DIR/struct-literals-in-invalid-places.rs:27:17 + | +LL | if x == E::V { field } {} + | ^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | if x == (E::V { field }) {} + | + + + +error[E0308]: mismatched types + --> $DIR/struct-literals-in-invalid-places.rs:27:24 + | +LL | if x == E::V { field } {} + | ---------------^^^^^-- + | | | + | | expected `()`, found `bool` + | expected this to be `()` + | +help: you might have meant to return this value + | +LL | if x == E::V { return field; } {} + | ++++++ + + +error[E0308]: mismatched types + --> $DIR/struct-literals-in-invalid-places.rs:38:24 + | +LL | let y: usize = (); + | ----- ^^ expected `usize`, found `()` + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/struct-literals-in-invalid-places.rs:43:11 + | +LL | while || Foo { x: 3 }.hi() { + | ^^^^^^^^^^^^^^^^^^^^ expected `bool`, found closure + | + = note: expected type `bool` + found closure `{closure@$DIR/struct-literals-in-invalid-places.rs:43:11: 43:13}` +help: use parentheses to call this closure + | +LL | while (|| Foo { x: 3 }.hi())() { + | + +++ + +error: aborting due to 21 previous errors + +Some errors have detailed explanations: E0277, E0308, E0533, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/parser/ternary_operator.rs b/tests/ui/parser/ternary_operator.rs index c8810781b3d84..08f6a4b2a244f 100644 --- a/tests/ui/parser/ternary_operator.rs +++ b/tests/ui/parser/ternary_operator.rs @@ -28,3 +28,9 @@ fn main() { //~| HELP use an `if-else` expression instead //~| ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `:` } + +fn expr(a: u64, b: u64) -> u64 { + a > b ? a : b + //~^ ERROR Rust has no ternary operator + //~| HELP use an `if-else` expression instead +} diff --git a/tests/ui/parser/ternary_operator.stderr b/tests/ui/parser/ternary_operator.stderr index 6635e1672f781..d4a633e5e5571 100644 --- a/tests/ui/parser/ternary_operator.stderr +++ b/tests/ui/parser/ternary_operator.stderr @@ -2,7 +2,7 @@ error: Rust has no ternary operator --> $DIR/ternary_operator.rs:2:19 | LL | let x = 5 > 2 ? true : false; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ | = help: use an `if-else` expression instead @@ -10,7 +10,7 @@ error: Rust has no ternary operator --> $DIR/ternary_operator.rs:8:19 | LL | let x = 5 > 2 ? { true } : { false }; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ | = help: use an `if-else` expression instead @@ -18,7 +18,7 @@ error: Rust has no ternary operator --> $DIR/ternary_operator.rs:14:19 | LL | let x = 5 > 2 ? f32::MAX : f32::MIN; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ | = help: use an `if-else` expression instead @@ -33,16 +33,26 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `:` | LL | let x = 5 > 2 ? { let x = vec![]: Vec; x } : { false }; | ^ expected one of `.`, `;`, `?`, `else`, or an operator - | - = note: type ascription syntax has been removed, see issue #101728 error: Rust has no ternary operator --> $DIR/ternary_operator.rs:26:19 | LL | let x = 5 > 2 ? { let x = vec![]: Vec; x } : { false }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use an `if-else` expression instead -error: aborting due to 6 previous errors +error: Rust has no ternary operator + --> $DIR/ternary_operator.rs:33:5 + | +LL | a > b ? a : b + | ^^^^^^^^^^^^^ + | +help: use an `if-else` expression instead + | +LL - a > b ? a : b +LL + if a > b { a } else { b } + | + +error: aborting due to 7 previous errors diff --git a/tests/ui/parser/trait-item-with-defaultness-pass.rs b/tests/ui/parser/trait-item-with-defaultness-pass.rs index c636342f6ca42..164d0b13b539c 100644 --- a/tests/ui/parser/trait-item-with-defaultness-pass.rs +++ b/tests/ui/parser/trait-item-with-defaultness-pass.rs @@ -2,7 +2,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] trait X { default const A: u8; default const B: u8 = 0; diff --git a/tests/ui/parser/trait-object-bad-parens.rs b/tests/ui/parser/trait-object-bad-parens.rs index 8e267c7448feb..bb047a4b43142 100644 --- a/tests/ui/parser/trait-object-bad-parens.rs +++ b/tests/ui/parser/trait-object-bad-parens.rs @@ -5,12 +5,8 @@ auto trait Auto {} fn main() { - let _: Box<((Auto)) + Auto>; - //~^ ERROR expected a path on the left-hand side of `+`, not `((Auto))` - let _: Box<(Auto + Auto) + Auto>; - //~^ ERROR expected a path on the left-hand side of `+`, not `(Auto + Auto)` - let _: Box<(Auto +) + Auto>; - //~^ ERROR expected a path on the left-hand side of `+`, not `(Auto)` - let _: Box<(dyn Auto) + Auto>; - //~^ ERROR expected a path on the left-hand side of `+`, not `(dyn Auto)` + let _: Box<((Auto)) + Auto>; //~ ERROR expected a path on the left-hand side of `+` + let _: Box<(Auto + Auto) + Auto>; //~ ERROR expected a path on the left-hand side of `+` + let _: Box<(Auto +) + Auto>; //~ ERROR expected a path on the left-hand side of `+` + let _: Box<(dyn Auto) + Auto>; //~ ERROR expected a path on the left-hand side of `+` } diff --git a/tests/ui/parser/trait-object-bad-parens.stderr b/tests/ui/parser/trait-object-bad-parens.stderr index 74e484eebee1f..7c2559ce89fd1 100644 --- a/tests/ui/parser/trait-object-bad-parens.stderr +++ b/tests/ui/parser/trait-object-bad-parens.stderr @@ -1,26 +1,26 @@ -error[E0178]: expected a path on the left-hand side of `+`, not `((Auto))` +error[E0178]: expected a path on the left-hand side of `+` --> $DIR/trait-object-bad-parens.rs:8:16 | LL | let _: Box<((Auto)) + Auto>; - | ^^^^^^^^^^^^^^^ expected a path + | ^^^^^^^^ expected a path -error[E0178]: expected a path on the left-hand side of `+`, not `(Auto + Auto)` - --> $DIR/trait-object-bad-parens.rs:10:16 +error[E0178]: expected a path on the left-hand side of `+` + --> $DIR/trait-object-bad-parens.rs:9:16 | LL | let _: Box<(Auto + Auto) + Auto>; - | ^^^^^^^^^^^^^^^^^^^^ expected a path + | ^^^^^^^^^^^^^ expected a path -error[E0178]: expected a path on the left-hand side of `+`, not `(Auto)` - --> $DIR/trait-object-bad-parens.rs:12:16 +error[E0178]: expected a path on the left-hand side of `+` + --> $DIR/trait-object-bad-parens.rs:10:16 | LL | let _: Box<(Auto +) + Auto>; - | ^^^^^^^^^^^^^^^ expected a path + | ^^^^^^^^ expected a path -error[E0178]: expected a path on the left-hand side of `+`, not `(dyn Auto)` - --> $DIR/trait-object-bad-parens.rs:14:16 +error[E0178]: expected a path on the left-hand side of `+` + --> $DIR/trait-object-bad-parens.rs:11:16 | LL | let _: Box<(dyn Auto) + Auto>; - | ^^^^^^^^^^^^^^^^^ expected a path + | ^^^^^^^^^^ expected a path error: aborting due to 4 previous errors diff --git a/tests/ui/parser/trait-object-lifetime-parens.e2015.stderr b/tests/ui/parser/trait-object-lifetime-parens.e2015.stderr new file mode 100644 index 0000000000000..cf0b3d77f5b5a --- /dev/null +++ b/tests/ui/parser/trait-object-lifetime-parens.e2015.stderr @@ -0,0 +1,37 @@ +error: parenthesized lifetime bounds are not supported + --> $DIR/trait-object-lifetime-parens.rs:9:21 + | +LL | fn f<'a, T: Trait + ('a)>() {} + | ^^^^ + | +help: remove the parentheses + | +LL - fn f<'a, T: Trait + ('a)>() {} +LL + fn f<'a, T: Trait + 'a>() {} + | + +error: parenthesized lifetime bounds are not supported + --> $DIR/trait-object-lifetime-parens.rs:12:24 + | +LL | let _: Box; + | ^^^^ + | +help: remove the parentheses + | +LL - let _: Box; +LL + let _: Box; + | + +error: lifetimes must be followed by `+` to form a trait object type + --> $DIR/trait-object-lifetime-parens.rs:16:17 + | +LL | let _: Box<('a) + Trait>; + | ^^ + | +help: consider adding a trait bound after the potential lifetime bound + | +LL | let _: Box<('a + /* Trait */) + Trait>; + | +++++++++++++ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/parser/trait-object-lifetime-parens.e2021.stderr b/tests/ui/parser/trait-object-lifetime-parens.e2021.stderr new file mode 100644 index 0000000000000..b65c079788a9c --- /dev/null +++ b/tests/ui/parser/trait-object-lifetime-parens.e2021.stderr @@ -0,0 +1,51 @@ +error: parenthesized lifetime bounds are not supported + --> $DIR/trait-object-lifetime-parens.rs:9:21 + | +LL | fn f<'a, T: Trait + ('a)>() {} + | ^^^^ + | +help: remove the parentheses + | +LL - fn f<'a, T: Trait + ('a)>() {} +LL + fn f<'a, T: Trait + 'a>() {} + | + +error: parenthesized lifetime bounds are not supported + --> $DIR/trait-object-lifetime-parens.rs:12:24 + | +LL | let _: Box; + | ^^^^ + | +help: remove the parentheses + | +LL - let _: Box; +LL + let _: Box; + | + +error: expected type, found lifetime + --> $DIR/trait-object-lifetime-parens.rs:16:17 + | +LL | let _: Box<('a) + Trait>; + | ^^ expected type + +error[E0178]: expected a path on the left-hand side of `+` + --> $DIR/trait-object-lifetime-parens.rs:16:16 + | +LL | let _: Box<('a) + Trait>; + | ^^^^ expected a path + +error[E0782]: expected a type, found a trait + --> $DIR/trait-object-lifetime-parens.rs:12:16 + | +LL | let _: Box; + | ^^^^^^^^^^^^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | let _: Box; + | +++ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0178, E0782. +For more information about an error, try `rustc --explain E0178`. diff --git a/tests/ui/parser/trait-object-lifetime-parens.rs b/tests/ui/parser/trait-object-lifetime-parens.rs index f44ebe5ba5bf2..0ff4660bb0d58 100644 --- a/tests/ui/parser/trait-object-lifetime-parens.rs +++ b/tests/ui/parser/trait-object-lifetime-parens.rs @@ -1,4 +1,8 @@ -#![allow(bare_trait_objects)] +//@ revisions: e2015 e2021 +//@[e2015] edition: 2015 +//@[e2021] edition: 2021 + +#![cfg_attr(e2015, allow(bare_trait_objects))] trait Trait {} @@ -6,8 +10,12 @@ fn f<'a, T: Trait + ('a)>() {} //~ ERROR parenthesized lifetime bounds are not s fn check<'a>() { let _: Box; //~ ERROR parenthesized lifetime bounds are not supported - // FIXME: It'd be great if we could add suggestion to the following case. - let _: Box<('a) + Trait>; //~ ERROR lifetime in trait object type must be followed by `+` + //[e2021]~^ ERROR expected a type, found a trait + // FIXME: It'd be great if we could suggest removing the parentheses here too. + //[e2015]~v ERROR lifetimes must be followed by `+` to form a trait object type + let _: Box<('a) + Trait>; + //[e2021]~^ ERROR expected type, found lifetime + //[e2021]~| ERROR expected a path on the left-hand side of `+` } fn main() {} diff --git a/tests/ui/parser/trait-object-lifetime-parens.stderr b/tests/ui/parser/trait-object-lifetime-parens.stderr deleted file mode 100644 index 280c0e40c6408..0000000000000 --- a/tests/ui/parser/trait-object-lifetime-parens.stderr +++ /dev/null @@ -1,32 +0,0 @@ -error: parenthesized lifetime bounds are not supported - --> $DIR/trait-object-lifetime-parens.rs:5:21 - | -LL | fn f<'a, T: Trait + ('a)>() {} - | ^^^^ - | -help: remove the parentheses - | -LL - fn f<'a, T: Trait + ('a)>() {} -LL + fn f<'a, T: Trait + 'a>() {} - | - -error: parenthesized lifetime bounds are not supported - --> $DIR/trait-object-lifetime-parens.rs:8:24 - | -LL | let _: Box; - | ^^^^ - | -help: remove the parentheses - | -LL - let _: Box; -LL + let _: Box; - | - -error: lifetime in trait object type must be followed by `+` - --> $DIR/trait-object-lifetime-parens.rs:10:17 - | -LL | let _: Box<('a) + Trait>; - | ^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui/parser/trait-object-polytrait-priority.rs b/tests/ui/parser/trait-object-polytrait-priority.rs index e7f085104ae9b..85568f0fe1b18 100644 --- a/tests/ui/parser/trait-object-polytrait-priority.rs +++ b/tests/ui/parser/trait-object-polytrait-priority.rs @@ -4,6 +4,6 @@ trait Trait<'a> {} fn main() { let _: &for<'a> Trait<'a> + 'static; - //~^ ERROR expected a path on the left-hand side of `+`, not `&for<'a> Trait<'a>` + //~^ ERROR expected a path on the left-hand side of `+` //~| HELP try adding parentheses } diff --git a/tests/ui/parser/trait-object-polytrait-priority.stderr b/tests/ui/parser/trait-object-polytrait-priority.stderr index 8cb564e79300e..a291a8e229c4b 100644 --- a/tests/ui/parser/trait-object-polytrait-priority.stderr +++ b/tests/ui/parser/trait-object-polytrait-priority.stderr @@ -1,8 +1,8 @@ -error[E0178]: expected a path on the left-hand side of `+`, not `&for<'a> Trait<'a>` +error[E0178]: expected a path on the left-hand side of `+` --> $DIR/trait-object-polytrait-priority.rs:6:12 | LL | let _: &for<'a> Trait<'a> + 'static; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ | help: try adding parentheses | diff --git a/tests/ui/parser/ty-path-followed-by-single-colon.rs b/tests/ui/parser/ty-path-followed-by-single-colon.rs new file mode 100644 index 0000000000000..a9082ea317a78 --- /dev/null +++ b/tests/ui/parser/ty-path-followed-by-single-colon.rs @@ -0,0 +1,22 @@ +// Paths in type contexts may be followed by single colons. +// This means we can't generally assume that the user typo'ed a double colon. +// issue: +//@ check-pass +#![crate_type = "lib"] +#![expect(non_camel_case_types)] + +#[rustfmt::skip] +mod garden { + + fn f() where path:to::somewhere {} // OK! + + fn g(_: impl Take) {} // OK! + + #[cfg(any())] fn h() where a::path:to::nowhere {} // OK! + + fn i(_: impl Take:to::somewhere>) {} // OK! + + mod to { pub(super) trait somewhere {} } + trait Take { type path; } + +} diff --git a/tests/ui/parser/type-ascription-in-pattern.rs b/tests/ui/parser/type-ascription-in-pattern.rs index fec168afba1dd..18d7061d69c8d 100644 --- a/tests/ui/parser/type-ascription-in-pattern.rs +++ b/tests/ui/parser/type-ascription-in-pattern.rs @@ -1,11 +1,10 @@ fn foo(x: bool) -> i32 { - match x { + match x { //~ ERROR struct literals are not allowed here x: i32 => x, //~ ERROR expected - //~^ ERROR mismatched types - true => 42., - false => 0.333, + true => 42., //~ ERROR expected identifier + false => 0.333, //~ ERROR expected identifier } -} +} //~ ERROR expected one of fn main() { match foo(true) { diff --git a/tests/ui/parser/type-ascription-in-pattern.stderr b/tests/ui/parser/type-ascription-in-pattern.stderr index 0919075499368..135879f208b2b 100644 --- a/tests/ui/parser/type-ascription-in-pattern.stderr +++ b/tests/ui/parser/type-ascription-in-pattern.stderr @@ -1,18 +1,64 @@ -error: expected one of `@` or `|`, found `:` - --> $DIR/type-ascription-in-pattern.rs:3:10 +error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, `}`, or an operator, found `=>` + --> $DIR/type-ascription-in-pattern.rs:3:16 | +LL | match x { + | - while parsing this struct LL | x: i32 => x, - | ^ --- specifying the type of a pattern isn't supported - | | - | expected one of `@` or `|` + | -^^ expected one of 8 possible tokens + | | + | help: try adding a comma: `,` + +error: expected identifier, found keyword `true` + --> $DIR/type-ascription-in-pattern.rs:4:9 | -help: maybe write a path separator here +LL | match x { + | - while parsing this struct +LL | x: i32 => x, +LL | true => 42., + | ^^^^ expected identifier, found keyword + +error: expected identifier, found keyword `false` + --> $DIR/type-ascription-in-pattern.rs:5:9 | -LL | x::i32 => x, - | ~~ +LL | match x { + | - while parsing this struct +... +LL | false => 0.333, + | ^^^^^ expected identifier, found keyword + +error: struct literals are not allowed here + --> $DIR/type-ascription-in-pattern.rs:2:11 + | +LL | match x { + | ___________^ +LL | | x: i32 => x, +LL | | true => 42., +LL | | false => 0.333, +LL | | } + | |_____^ + | +help: surround the struct literal with parentheses + | +LL ~ match (x { +LL | x: i32 => x, +LL | true => 42., +LL | false => 0.333, +LL ~ }) + | + +error: expected one of `.`, `?`, `{`, or an operator, found `}` + --> $DIR/type-ascription-in-pattern.rs:7:1 + | +LL | match x { + | ----- while parsing this `match` expression +... +LL | } + | - expected one of `.`, `?`, `{`, or an operator +LL | } + | ^ unexpected token error: expected one of `...`, `..=`, `..`, or `|`, found `:` - --> $DIR/type-ascription-in-pattern.rs:12:11 + --> $DIR/type-ascription-in-pattern.rs:11:11 | LL | 42: i32 => (), | ^ --- specifying the type of a pattern isn't supported @@ -20,7 +66,7 @@ LL | 42: i32 => (), | expected one of `...`, `..=`, `..`, or `|` error: expected `|`, found `:` - --> $DIR/type-ascription-in-pattern.rs:13:10 + --> $DIR/type-ascription-in-pattern.rs:12:10 | LL | _: f64 => (), | ^ --- specifying the type of a pattern isn't supported @@ -28,7 +74,7 @@ LL | _: f64 => (), | expected `|` error: expected one of `@` or `|`, found `:` - --> $DIR/type-ascription-in-pattern.rs:14:10 + --> $DIR/type-ascription-in-pattern.rs:13:10 | LL | x: i32 => (), | ^ --- specifying the type of a pattern isn't supported @@ -40,15 +86,5 @@ help: maybe write a path separator here LL | x::i32 => (), | ~~ -error[E0308]: mismatched types - --> $DIR/type-ascription-in-pattern.rs:3:19 - | -LL | fn foo(x: bool) -> i32 { - | --- expected `i32` because of return type -LL | match x { -LL | x: i32 => x, - | ^ expected `i32`, found `bool` - -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/unbalanced-doublequote.rs b/tests/ui/parser/unbalanced-doublequote.rs index d9c936186ea07..43e23a70271b0 100644 --- a/tests/ui/parser/unbalanced-doublequote.rs +++ b/tests/ui/parser/unbalanced-doublequote.rs @@ -1,6 +1,4 @@ -//@ error-pattern: unterminated double quote string - - +//~vv ERROR unterminated double quote string fn main() { " } diff --git a/tests/ui/parser/unbalanced-doublequote.stderr b/tests/ui/parser/unbalanced-doublequote.stderr index 9fdad87a86cf1..60057eddbb423 100644 --- a/tests/ui/parser/unbalanced-doublequote.stderr +++ b/tests/ui/parser/unbalanced-doublequote.stderr @@ -1,9 +1,9 @@ error[E0765]: unterminated double quote string - --> $DIR/unbalanced-doublequote.rs:5:5 + --> $DIR/unbalanced-doublequote.rs:3:5 | LL | / " LL | | } - | |_^ + | |__^ error: aborting due to 1 previous error diff --git a/tests/ui/parser/unclosed-delimiter-in-dep.rs b/tests/ui/parser/unclosed-delimiter-in-dep.rs index 4de83ee640a73..40f517f317ef7 100644 --- a/tests/ui/parser/unclosed-delimiter-in-dep.rs +++ b/tests/ui/parser/unclosed-delimiter-in-dep.rs @@ -3,3 +3,5 @@ mod unclosed_delim_mod; fn main() { let _: usize = unclosed_delim_mod::new(); } + +//~? ERROR mismatched closing delimiter: `}` diff --git a/tests/ui/parser/use-unclosed-brace.rs b/tests/ui/parser/use-unclosed-brace.rs index 6679651fe47e2..aa52fe92ac1ba 100644 --- a/tests/ui/parser/use-unclosed-brace.rs +++ b/tests/ui/parser/use-unclosed-brace.rs @@ -1,4 +1,3 @@ -//@ error-pattern: this file contains an unclosed delimiter use foo::{bar, baz; use std::fmt::Display; @@ -7,4 +6,5 @@ mod bar { } mod baz { } +//~v ERROR this file contains an unclosed delimiter fn main() {} diff --git a/tests/ui/parser/utf16-be-without-bom.rs b/tests/ui/parser/utf16-be-without-bom.rs index f5fe8dc5a8c79..1f2abc1ad5616 100644 Binary files a/tests/ui/parser/utf16-be-without-bom.rs and b/tests/ui/parser/utf16-be-without-bom.rs differ diff --git a/tests/ui/parser/utf16-be-without-bom.stderr b/tests/ui/parser/utf16-be-without-bom.stderr index 0493bcbc77a4a..467e0ed73136e 100644 --- a/tests/ui/parser/utf16-be-without-bom.stderr +++ b/tests/ui/parser/utf16-be-without-bom.stderr @@ -1,5 +1,5 @@ error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:1 + --> $DIR/utf16-be-without-bom.rs:6:1 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -7,7 +7,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:3 + --> $DIR/utf16-be-without-bom.rs:6:3 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -15,7 +15,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:5 + --> $DIR/utf16-be-without-bom.rs:6:5 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -23,7 +23,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:7 + --> $DIR/utf16-be-without-bom.rs:6:7 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -31,7 +31,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:9 + --> $DIR/utf16-be-without-bom.rs:6:9 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -39,7 +39,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:11 + --> $DIR/utf16-be-without-bom.rs:6:11 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -47,7 +47,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:13 + --> $DIR/utf16-be-without-bom.rs:6:13 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -55,7 +55,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:15 + --> $DIR/utf16-be-without-bom.rs:6:15 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -63,7 +63,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:17 + --> $DIR/utf16-be-without-bom.rs:6:17 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -71,7 +71,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:19 + --> $DIR/utf16-be-without-bom.rs:6:19 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -79,7 +79,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:21 + --> $DIR/utf16-be-without-bom.rs:6:21 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -87,7 +87,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:23 + --> $DIR/utf16-be-without-bom.rs:6:23 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -95,7 +95,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-be-without-bom.rs:5:25 + --> $DIR/utf16-be-without-bom.rs:6:25 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -103,7 +103,7 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: expected one of `!` or `::`, found `n` - --> $DIR/utf16-be-without-bom.rs:5:4 + --> $DIR/utf16-be-without-bom.rs:6:4 | LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ expected one of `!` or `::` diff --git a/tests/ui/parser/utf16-le-without-bom.rs b/tests/ui/parser/utf16-le-without-bom.rs index 8c781b27dc02e..bb95f0dd07178 100644 Binary files a/tests/ui/parser/utf16-le-without-bom.rs and b/tests/ui/parser/utf16-le-without-bom.rs differ diff --git a/tests/ui/parser/utf16-le-without-bom.stderr b/tests/ui/parser/utf16-le-without-bom.stderr index 4b195ed0da1a9..701379d4067e3 100644 --- a/tests/ui/parser/utf16-le-without-bom.stderr +++ b/tests/ui/parser/utf16-le-without-bom.stderr @@ -1,5 +1,5 @@ error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:5:2 + --> $DIR/utf16-le-without-bom.rs:6:2 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -7,7 +7,7 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:5:4 + --> $DIR/utf16-le-without-bom.rs:6:4 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -15,7 +15,7 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:5:6 + --> $DIR/utf16-le-without-bom.rs:6:6 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -23,7 +23,7 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:5:8 + --> $DIR/utf16-le-without-bom.rs:6:8 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -31,7 +31,7 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:5:10 + --> $DIR/utf16-le-without-bom.rs:6:10 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -39,7 +39,7 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:5:12 + --> $DIR/utf16-le-without-bom.rs:6:12 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -47,7 +47,7 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:5:14 + --> $DIR/utf16-le-without-bom.rs:6:14 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -55,7 +55,7 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:5:16 + --> $DIR/utf16-le-without-bom.rs:6:16 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -63,7 +63,7 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:5:18 + --> $DIR/utf16-le-without-bom.rs:6:18 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -71,7 +71,7 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:5:20 + --> $DIR/utf16-le-without-bom.rs:6:20 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -79,7 +79,7 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:5:22 + --> $DIR/utf16-le-without-bom.rs:6:22 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -87,7 +87,7 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:5:24 + --> $DIR/utf16-le-without-bom.rs:6:24 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ @@ -95,7 +95,7 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: unknown start of token: \u{0} - --> $DIR/utf16-le-without-bom.rs:6:1 + --> $DIR/utf16-le-without-bom.rs:7:1 | LL | ␀ | ^ @@ -103,7 +103,7 @@ LL | ␀ = help: source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used error: expected one of `!` or `::`, found `n` - --> $DIR/utf16-le-without-bom.rs:5:3 + --> $DIR/utf16-le-without-bom.rs:6:3 | LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | ^ expected one of `!` or `::` diff --git a/tests/ui/parser/variadic-ffi-syntactic-pass.rs b/tests/ui/parser/variadic-ffi-syntactic-pass.rs index da81f1362160a..ebe0b6c2dd258 100644 --- a/tests/ui/parser/variadic-ffi-syntactic-pass.rs +++ b/tests/ui/parser/variadic-ffi-syntactic-pass.rs @@ -2,31 +2,31 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn f1_1(x: isize, ...) {} -#[cfg(FALSE)] +#[cfg(false)] fn f1_2(...) {} -#[cfg(FALSE)] +#[cfg(false)] extern "C" fn f2_1(x: isize, ...) {} -#[cfg(FALSE)] +#[cfg(false)] extern "C" fn f2_2(...) {} -#[cfg(FALSE)] +#[cfg(false)] extern "C" fn f2_3(..., x: isize) {} -#[cfg(FALSE)] +#[cfg(false)] extern fn f3_1(x: isize, ...) {} -#[cfg(FALSE)] +#[cfg(false)] extern fn f3_2(...) {} -#[cfg(FALSE)] +#[cfg(false)] extern fn f3_3(..., x: isize) {} -#[cfg(FALSE)] +#[cfg(false)] extern { fn e_f1(...); fn e_f2(..., x: isize); @@ -34,7 +34,7 @@ extern { struct X; -#[cfg(FALSE)] +#[cfg(false)] impl X { fn i_f1(x: isize, ...) {} fn i_f2(...) {} @@ -42,7 +42,7 @@ impl X { fn i_f4(..., x: isize, ...) {} } -#[cfg(FALSE)] +#[cfg(false)] trait T { fn t_f1(x: isize, ...) {} fn t_f2(x: isize, ...); diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr index d9710c6e6a2f5..9357a86c4153f 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr @@ -20,7 +20,7 @@ error: unexpected parameter name --> $DIR/patchable-function-entry-attribute.rs:13:46 | LL | #[patchable_function_entry(prefix_nops = 10, something = 0)] - | ^^^^^^^^^^^^^ expected prefix_nops or entry_nops + | ^^^^^^^^^^^^^ expected `prefix_nops` or `entry_nops` error: must specify at least one parameter --> $DIR/patchable-function-entry-attribute.rs:16:1 diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-flags.rs b/tests/ui/patchable-function-entry/patchable-function-entry-flags.rs index cb5bc62b6b347..2d5a0b0a77155 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-flags.rs +++ b/tests/ui/patchable-function-entry/patchable-function-entry-flags.rs @@ -1,2 +1,5 @@ //@ compile-flags: -Z patchable-function-entry=1,2 + fn main() {} + +//~? ERROR incorrect value `1,2` for unstable option `patchable-function-entry` diff --git a/tests/ui/path.rs b/tests/ui/path.rs deleted file mode 100644 index bd7b99ac01ad5..0000000000000 --- a/tests/ui/path.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ run-pass - -mod foo { - pub fn bar(_offset: usize) { } -} - -pub fn main() { foo::bar(0); } diff --git a/tests/ui/auxiliary/issue-24106.rs b/tests/ui/pattern/auxiliary/cross-crate-enum-pattern.rs similarity index 100% rename from tests/ui/auxiliary/issue-24106.rs rename to tests/ui/pattern/auxiliary/cross-crate-enum-pattern.rs diff --git a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.rs b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.rs index 01a978d55574b..9582d2729a895 100644 --- a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.rs +++ b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.rs @@ -13,19 +13,19 @@ fn _ok() { fn _f(_a @ _b: u8) {} // OK. } -#[cfg(FALSE)] +#[cfg(false)] fn case_1() { let a: u8 @ b = 0; //~^ ERROR expected one of `!` } -#[cfg(FALSE)] +#[cfg(false)] fn case_2() { let a @ (b: u8); //~^ ERROR expected one of `)` } -#[cfg(FALSE)] +#[cfg(false)] fn case_3() { let a: T1 @ Outer(b: T2); //~^ ERROR expected one of `!` diff --git a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr index 6ce8f6d31a031..1847e407f6ba0 100644 --- a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr +++ b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr @@ -11,8 +11,6 @@ error: expected one of `)`, `,`, `@`, `if`, or `|`, found `:` | LL | let a @ (b: u8); | ^ expected one of `)`, `,`, `@`, `if`, or `|` - | - = note: type ascription syntax has been removed, see issue #101728 error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found `@` --> $DIR/nested-type-ascription-syntactically-invalid.rs:30:15 diff --git a/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.rs b/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.rs index 50ac0ef27834e..c3994d35c421c 100644 --- a/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.rs +++ b/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.rs @@ -3,7 +3,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] fn wild_before_at_is_bad_syntax() { let _ @ a = 0; //~^ ERROR pattern on wrong side of `@` diff --git a/tests/ui/pattern/byte-string-mutability-mismatch.rs b/tests/ui/pattern/byte-string-mutability-mismatch.rs new file mode 100644 index 0000000000000..9f7054ae82ee1 --- /dev/null +++ b/tests/ui/pattern/byte-string-mutability-mismatch.rs @@ -0,0 +1,21 @@ +//! Byte string literal patterns use the mutability of the literal, rather than the mutability of +//! the pattern's scrutinee. Since byte string literals are always shared references, it's a +//! mismatch to use a byte string literal pattern to match on a mutable array or slice reference. + +//@ dont-require-annotations: NOTE + +fn main() { + let mut val = [97u8, 10u8]; + match &mut val { + b"a\n" => {}, + //~^ ERROR mismatched types + //~| NOTE types differ in mutability + _ => {}, + } + match &mut val[..] { + b"a\n" => {}, + //~^ ERROR mismatched types + //~| NOTE types differ in mutability + _ => {}, + } +} diff --git a/tests/ui/pattern/byte-string-mutability-mismatch.stderr b/tests/ui/pattern/byte-string-mutability-mismatch.stderr new file mode 100644 index 0000000000000..f64b452b594e9 --- /dev/null +++ b/tests/ui/pattern/byte-string-mutability-mismatch.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/byte-string-mutability-mismatch.rs:10:9 + | +LL | match &mut val { + | -------- this expression has type `&mut [u8; 2]` +LL | b"a\n" => {}, + | ^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut _` + found reference `&'static _` + +error[E0308]: mismatched types + --> $DIR/byte-string-mutability-mismatch.rs:16:10 + | +LL | match &mut val[..] { + | ------------ this expression has type `&mut [u8]` +LL | b"a\n" => {}, + | ^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut _` + found reference `&'static _` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/check-struct-pat-fields-stability-issue-138319.rs b/tests/ui/pattern/check-struct-pat-fields-stability-issue-138319.rs new file mode 100644 index 0000000000000..b951c6d92ee6b --- /dev/null +++ b/tests/ui/pattern/check-struct-pat-fields-stability-issue-138319.rs @@ -0,0 +1,12 @@ +//@ check-pass +struct Point { + #[deprecated = "x is deprecated"] + _x: i32, + _y: i32, +} + +fn main() { + let p = Point { _x: 1, _y: 2 }; //~ WARNING use of deprecated field `Point::_x` + // Before fix, it report an warning + let Point { #[expect(deprecated)]_x, .. } = p; +} diff --git a/tests/ui/pattern/check-struct-pat-fields-stability-issue-138319.stderr b/tests/ui/pattern/check-struct-pat-fields-stability-issue-138319.stderr new file mode 100644 index 0000000000000..707eb58e5478e --- /dev/null +++ b/tests/ui/pattern/check-struct-pat-fields-stability-issue-138319.stderr @@ -0,0 +1,10 @@ +warning: use of deprecated field `Point::_x`: x is deprecated + --> $DIR/check-struct-pat-fields-stability-issue-138319.rs:9:21 + | +LL | let p = Point { _x: 1, _y: 2 }; + | ^^^^^ + | + = note: `#[warn(deprecated)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/pattern/cross-crate-enum-pattern.rs b/tests/ui/pattern/cross-crate-enum-pattern.rs new file mode 100644 index 0000000000000..254caf99277c5 --- /dev/null +++ b/tests/ui/pattern/cross-crate-enum-pattern.rs @@ -0,0 +1,13 @@ +//! Test that we can match on enum constants across crates. +//! +//! Regression test for . + + +//@ run-pass +//@ aux-build:cross-crate-enum-pattern.rs + +extern crate cross_crate_enum_pattern; + +fn main() { + cross_crate_enum_pattern::go::<()>(); +} diff --git a/tests/ui/pattern/deref-patterns/bindings.rs b/tests/ui/pattern/deref-patterns/bindings.rs index 5881e4166a46f..92c01d737bac6 100644 --- a/tests/ui/pattern/deref-patterns/bindings.rs +++ b/tests/ui/pattern/deref-patterns/bindings.rs @@ -1,7 +1,11 @@ +//@ revisions: explicit implicit //@ run-pass #![feature(deref_patterns)] #![allow(incomplete_features)] +use std::rc::Rc; + +#[cfg(explicit)] fn simple_vec(vec: Vec) -> u32 { match vec { deref!([]) => 100, @@ -9,10 +13,21 @@ fn simple_vec(vec: Vec) -> u32 { deref!([x]) => x, deref!([1, x]) => x + 200, deref!(ref slice) => slice.iter().sum(), - _ => 2000, } } +#[cfg(implicit)] +fn simple_vec(vec: Vec) -> u32 { + match vec { + [] => 100, + [x] if x == 4 => x + 4, + [x] => x, + [1, x] => x + 200, + deref!(ref slice) => slice.iter().sum(), + } +} + +#[cfg(explicit)] fn nested_vec(vecvec: Vec>) -> u32 { match vecvec { deref!([]) => 0, @@ -24,23 +39,51 @@ fn nested_vec(vecvec: Vec>) -> u32 { } } +#[cfg(implicit)] +fn nested_vec(vecvec: Vec>) -> u32 { + match vecvec { + [] => 0, + [[x]] => x, + [[0, x] | [1, x]] => x, + [ref x] => x.iter().sum(), + [[], [1, x, y]] => y - x, + _ => 2000, + } +} + +#[cfg(explicit)] +fn ref_mut(val: u32) -> u32 { + let mut b = vec![0u32]; + match &mut b { + deref!([_x]) if false => unreachable!(), + deref!([x]) => { + *x = val; + } + _ => unreachable!(), + } + let deref!([x]) = &b else { unreachable!() }; + *x +} + +#[cfg(implicit)] fn ref_mut(val: u32) -> u32 { - let mut b = Box::new(0u32); + let mut b = vec![0u32]; match &mut b { - deref!(_x) if false => unreachable!(), - deref!(x) => { + [_x] if false => unreachable!(), + [x] => { *x = val; } _ => unreachable!(), } - let deref!(x) = &b else { unreachable!() }; + let [x] = &b else { unreachable!() }; *x } +#[cfg(explicit)] #[rustfmt::skip] fn or_and_guard(tuple: (u32, u32)) -> u32 { let mut sum = 0; - let b = Box::new(tuple); + let b = Rc::new(tuple); match b { deref!((x, _) | (_, x)) if { sum += x; false } => {}, _ => {}, @@ -48,6 +91,18 @@ fn or_and_guard(tuple: (u32, u32)) -> u32 { sum } +#[cfg(implicit)] +#[rustfmt::skip] +fn or_and_guard(tuple: (u32, u32)) -> u32 { + let mut sum = 0; + let b = Rc::new(tuple); + match b { + (x, _) | (_, x) if { sum += x; false } => {}, + _ => {}, + } + sum +} + fn main() { assert_eq!(simple_vec(vec![1]), 1); assert_eq!(simple_vec(vec![1, 2]), 202); diff --git a/tests/ui/pattern/deref-patterns/branch.rs b/tests/ui/pattern/deref-patterns/branch.rs index 1bac1006d9dc0..9d72b35fd2f1c 100644 --- a/tests/ui/pattern/deref-patterns/branch.rs +++ b/tests/ui/pattern/deref-patterns/branch.rs @@ -1,8 +1,10 @@ +//@ revisions: explicit implicit //@ run-pass // Test the execution of deref patterns. #![feature(deref_patterns)] #![allow(incomplete_features)] +#[cfg(explicit)] fn branch(vec: Vec) -> u32 { match vec { deref!([]) => 0, @@ -12,6 +14,17 @@ fn branch(vec: Vec) -> u32 { } } +#[cfg(implicit)] +fn branch(vec: Vec) -> u32 { + match vec { + [] => 0, + [1, _, 3] => 1, + [2, ..] => 2, + _ => 1000, + } +} + +#[cfg(explicit)] fn nested(vec: Vec>) -> u32 { match vec { deref!([deref!([]), ..]) => 1, @@ -20,6 +33,15 @@ fn nested(vec: Vec>) -> u32 { } } +#[cfg(implicit)] +fn nested(vec: Vec>) -> u32 { + match vec { + [[], ..] => 1, + [[0, ..], [1, ..]] => 2, + _ => 1000, + } +} + fn main() { assert!(matches!(Vec::::new(), deref!([]))); assert!(matches!(vec![1], deref!([1]))); diff --git a/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs b/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs new file mode 100644 index 0000000000000..fdcc6cb461114 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs @@ -0,0 +1,55 @@ +//! Test type errors for byte string literal patterns. `deref_patterns` allows byte string literal +//! patterns to have type `[u8]` or `[u8; N]` when matching on a slice or array; this can affect the +//! "found" type reported in error messages when matching on a slice or array of the wrong type. + +//@ dont-require-annotations: NOTE + +#![feature(deref_patterns)] +#![expect(incomplete_features)] + +fn main() { + // Baseline 1: under normal circumstances, byte string literal patterns have type `&[u8; N]`, + // the same as byte string literals. + if let b"test" = () {} + //~^ ERROR mismatched types + //~| NOTE expected `()`, found `&[u8; 4]` + + // Baseline 2: there's a special case for byte string patterns in stable rust, allowing them to + // match on slice references. This affects the error when matching on a non-`&[u8]` slice ref, + // reporting the "found" type as `&[u8]`. + if let b"test" = &[] as &[i8] {} + //~^ ERROR mismatched types + //~| NOTE expected `&[i8]`, found `&[u8]` + + // Test matching on a non-`[u8]` slice: the pattern has type `[u8]` if a slice is expected. + if let b"test" = *(&[] as &[i8]) {} + //~^ ERROR mismatched types + //~| NOTE expected `[i8]`, found `[u8]` + + // Test matching on a non-`[u8;4]` array: the pattern has type `[u8;4]` if an array is expected. + if let b"test" = [()] {} + //~^ ERROR mismatched types + //~| NOTE expected `[(); 1]`, found `[u8; 4]` + if let b"test" = *b"this array is too long" {} + //~^ ERROR mismatched types + //~| NOTE expected an array with a size of 22, found one with a size of 4 + + // Test matching on `&mut T`: we peel the `&mut` before applying the usual special cases. + // No special cases apply to `()`, so the "found" type is the type of the literal. + if let b"test" = &mut () {} + //~^ ERROR mismatched types + //~| NOTE expected `()`, found `&[u8; 4]` + + // If the pointee is an array or slice, the usual special cases will apply to the "found" type: + if let b"test" = &mut [] as &mut [i8] {} + //~^ ERROR mismatched type + //~| NOTE expected `[i8]`, found `[u8]` + + if let b"test" = &mut [()] {} + //~^ ERROR mismatched types + //~| NOTE expected `[(); 1]`, found `[u8; 4]` + + if let b"test" = &mut *b"this array is too long" {} + //~^ ERROR mismatched type + //~| NOTE expected an array with a size of 22, found one with a size of 4 +} diff --git a/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr b/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr new file mode 100644 index 0000000000000..046682004be7e --- /dev/null +++ b/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr @@ -0,0 +1,90 @@ +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:13:12 + | +LL | if let b"test" = () {} + | ^^^^^^^ -- this expression has type `()` + | | + | expected `()`, found `&[u8; 4]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:20:12 + | +LL | if let b"test" = &[] as &[i8] {} + | ^^^^^^^ ------------ this expression has type `&[i8]` + | | + | expected `&[i8]`, found `&[u8]` + | + = note: expected reference `&[i8]` + found reference `&'static [u8]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:25:12 + | +LL | if let b"test" = *(&[] as &[i8]) {} + | ^^^^^^^ --------------- this expression has type `[i8]` + | | + | expected `[i8]`, found `[u8]` + | + = note: expected slice `[i8]` + found slice `[u8]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:30:12 + | +LL | if let b"test" = [()] {} + | ^^^^^^^ ---- this expression has type `[(); 1]` + | | + | expected `[(); 1]`, found `[u8; 4]` + | + = note: expected array `[(); 1]` + found array `[u8; 4]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:33:12 + | +LL | if let b"test" = *b"this array is too long" {} + | ^^^^^^^ -------------------------- this expression has type `[u8; 22]` + | | + | expected an array with a size of 22, found one with a size of 4 + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:39:12 + | +LL | if let b"test" = &mut () {} + | ^^^^^^^ ------- this expression has type `&mut ()` + | | + | expected `()`, found `&[u8; 4]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:44:12 + | +LL | if let b"test" = &mut [] as &mut [i8] {} + | ^^^^^^^ -------------------- this expression has type `&mut [i8]` + | | + | expected `[i8]`, found `[u8]` + | + = note: expected slice `[i8]` + found slice `[u8]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:48:12 + | +LL | if let b"test" = &mut [()] {} + | ^^^^^^^ --------- this expression has type `&mut [(); 1]` + | | + | expected `[(); 1]`, found `[u8; 4]` + | + = note: expected array `[(); 1]` + found array `[u8; 4]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:52:12 + | +LL | if let b"test" = &mut *b"this array is too long" {} + | ^^^^^^^ ------------------------------- this expression has type `&mut [u8; 22]` + | | + | expected an array with a size of 22, found one with a size of 4 + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs b/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs index 84b5ec09dc7d8..2b4746e33e678 100644 --- a/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs +++ b/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs @@ -5,18 +5,36 @@ use std::rc::Rc; struct Struct; -fn cant_move_out_box(b: Box) -> Struct { +fn cant_move_out_vec(b: Vec) -> Struct { match b { + //~^ ERROR: cannot move out of type `[Struct]`, a non-copy slice + deref!([x]) => x, + _ => panic!(), + } +} + +fn cant_move_out_rc(rc: Rc) -> Struct { + match rc { //~^ ERROR: cannot move out of a shared reference deref!(x) => x, _ => unreachable!(), } } -fn cant_move_out_rc(rc: Rc) -> Struct { +fn cant_move_out_vec_implicit(b: Vec) -> Struct { + match b { + //~^ ERROR: cannot move out of type `[Struct]`, a non-copy slice + [x] => x, + _ => panic!(), + } +} + +struct Container(Struct); + +fn cant_move_out_rc_implicit(rc: Rc) -> Struct { match rc { //~^ ERROR: cannot move out of a shared reference - deref!(x) => x, + Container(x) => x, _ => unreachable!(), } } diff --git a/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.stderr b/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.stderr index 2cf435b117999..a548ac5909a8c 100644 --- a/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.stderr +++ b/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.stderr @@ -1,19 +1,19 @@ -error[E0507]: cannot move out of a shared reference +error[E0508]: cannot move out of type `[Struct]`, a non-copy slice --> $DIR/cant_move_out_of_pattern.rs:9:11 | LL | match b { - | ^ + | ^ cannot move out of here LL | -LL | deref!(x) => x, - | - - | | - | data moved here - | move occurs because `x` has type `Struct`, which does not implement the `Copy` trait +LL | deref!([x]) => x, + | - + | | + | data moved here + | move occurs because `x` has type `Struct`, which does not implement the `Copy` trait | help: consider borrowing the pattern binding | -LL | deref!(ref x) => x, - | +++ +LL | deref!([ref x]) => x, + | +++ error[E0507]: cannot move out of a shared reference --> $DIR/cant_move_out_of_pattern.rs:17:11 @@ -32,6 +32,41 @@ help: consider borrowing the pattern binding LL | deref!(ref x) => x, | +++ -error: aborting due to 2 previous errors +error[E0508]: cannot move out of type `[Struct]`, a non-copy slice + --> $DIR/cant_move_out_of_pattern.rs:25:11 + | +LL | match b { + | ^ cannot move out of here +LL | +LL | [x] => x, + | - + | | + | data moved here + | move occurs because `x` has type `Struct`, which does not implement the `Copy` trait + | +help: consider borrowing the pattern binding + | +LL | [ref x] => x, + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/cant_move_out_of_pattern.rs:35:11 + | +LL | match rc { + | ^^ +LL | +LL | Container(x) => x, + | - + | | + | data moved here + | move occurs because `x` has type `Struct`, which does not implement the `Copy` trait + | +help: consider borrowing the pattern binding + | +LL | Container(ref x) => x, + | +++ + +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0507`. +Some errors have detailed explanations: E0507, E0508. +For more information about an error, try `rustc --explain E0507`. diff --git a/tests/ui/pattern/deref-patterns/closure_capture.rs b/tests/ui/pattern/deref-patterns/closure_capture.rs index fc0ddedac2b78..497ec622b0cf8 100644 --- a/tests/ui/pattern/deref-patterns/closure_capture.rs +++ b/tests/ui/pattern/deref-patterns/closure_capture.rs @@ -2,20 +2,67 @@ #![feature(deref_patterns)] #![allow(incomplete_features)] +use std::rc::Rc; + +struct NoCopy; + fn main() { - let b = Box::new("aaa".to_string()); + let b = Rc::new("aaa".to_string()); let f = || { - let deref!(ref s) = b else { unreachable!() }; + let deref!(ref s) = b; assert_eq!(s.len(), 3); }; assert_eq!(b.len(), 3); f(); - let mut b = Box::new("aaa".to_string()); + let v = vec![1, 2, 3]; + let f = || { + // this should count as a borrow of `v` as a whole + let [.., x] = v else { unreachable!() }; + assert_eq!(x, 3); + }; + assert_eq!(v, [1, 2, 3]); + f(); + + let mut b = "aaa".to_string(); + let mut f = || { + let deref!(ref mut s) = b; + s.make_ascii_uppercase(); + }; + f(); + assert_eq!(b, "AAA"); + + let mut v = vec![1, 2, 3]; + let mut f = || { + // this should count as a mutable borrow of `v` as a whole + let [.., ref mut x] = v else { unreachable!() }; + *x = 4; + }; + f(); + assert_eq!(v, [1, 2, 4]); + + let mut v = vec![1, 2, 3]; let mut f = || { - let deref!(ref mut s) = b else { unreachable!() }; - s.push_str("aa"); + // here, `[.., x]` is adjusted by both an overloaded deref and a builtin deref + let [.., x] = &mut v else { unreachable!() }; + *x = 4; + }; + f(); + assert_eq!(v, [1, 2, 4]); + + let b = Box::new(NoCopy); + let f = || { + // this should move out of the box rather than borrow. + let deref!(x) = b; + drop::(x); + }; + f(); + + let b = Box::new((NoCopy,)); + let f = || { + // this should move out of the box rather than borrow. + let (x,) = b; + drop::(x); }; f(); - assert_eq!(b.len(), 5); } diff --git a/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.rs b/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.rs new file mode 100644 index 0000000000000..3a2531f4b95e6 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.rs @@ -0,0 +1,54 @@ +//@ revisions: stable deref_patterns +//@[deref_patterns] check-pass +//! `deref_patterns` allows string and byte string literal patterns to implicitly peel references +//! and smart pointers from the scrutinee before matching. Since strings and byte strings themselves +//! have reference types, we need to make sure we don't peel too much. By leaving the type of the +//! match scrutinee partially uninferred, these tests make sure we only peel as much as needed in +//! order to match. In particular, when peeling isn't needed, the results should be the same was +//! we'd get without `deref_patterns` enabled. + +#![cfg_attr(deref_patterns, feature(deref_patterns))] +#![cfg_attr(deref_patterns, expect(incomplete_features))] + +fn uninferred() -> T { unimplemented!() } + +// Assert type equality without allowing coercions. +trait Is {} +impl Is for T {} +fn has_type(_: impl Is) {} + +fn main() { + // We don't need to peel anything to unify the type of `x` with `&str`, so `x: &str`. + let x = uninferred(); + if let "..." = x {} + has_type::<&str>(x); + + // We don't need to peel anything to unify the type of `&x` with `&[u8; 3]`, so `x: [u8; 3]`. + let x = uninferred(); + if let b"..." = &x {} + has_type::<[u8; 3]>(x); + + // Peeling a single `&` lets us unify the type of `&x` with `&[u8; 3]`, giving `x: [u8; 3]`. + let x = uninferred(); + if let b"..." = &&x {} + //[stable]~^ ERROR: mismatched types + has_type::<[u8; 3]>(x); + + // We have to peel both the `&` and the box before unifying the type of `x` with `&str`. + let x = uninferred(); + if let "..." = &Box::new(x) {} + //[stable]~^ ERROR mismatched types + has_type::<&str>(x); + + // After peeling the box, we can unify the type of `&x` with `&[u8; 3]`, giving `x: [u8; 3]`. + let x = uninferred(); + if let b"..." = Box::new(&x) {} + //[stable]~^ ERROR mismatched types + has_type::<[u8; 3]>(x); + + // `&` and `&mut` aren't interchangeable: `&mut`s need to be peeled before unifying, like boxes: + let mut x = uninferred(); + if let "..." = &mut x {} + //[stable]~^ ERROR mismatched types + has_type::<&str>(x); +} diff --git a/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.stable.stderr b/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.stable.stderr new file mode 100644 index 0000000000000..61079718c5d5a --- /dev/null +++ b/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.stable.stderr @@ -0,0 +1,55 @@ +error[E0308]: mismatched types + --> $DIR/const-pats-do-not-mislead-inference.rs:33:12 + | +LL | if let b"..." = &&x {} + | ^^^^^^ --- this expression has type `&&_` + | | + | expected `&&_`, found `&[u8; 3]` + | + = note: expected reference `&&_` + found reference `&'static [u8; 3]` + +error[E0308]: mismatched types + --> $DIR/const-pats-do-not-mislead-inference.rs:39:12 + | +LL | if let "..." = &Box::new(x) {} + | ^^^^^ ------------ this expression has type `&Box<_>` + | | + | expected `&Box<_>`, found `&str` + | + = note: expected reference `&Box<_>` + found reference `&'static str` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | if let "..." = &*Box::new(x) {} + | + + +error[E0308]: mismatched types + --> $DIR/const-pats-do-not-mislead-inference.rs:45:12 + | +LL | if let b"..." = Box::new(&x) {} + | ^^^^^^ ------------ this expression has type `Box<&_>` + | | + | expected `Box<&_>`, found `&[u8; 3]` + | + = note: expected struct `Box<&_>` + found reference `&'static [u8; 3]` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | if let b"..." = *Box::new(&x) {} + | + + +error[E0308]: mismatched types + --> $DIR/const-pats-do-not-mislead-inference.rs:51:12 + | +LL | if let "..." = &mut x {} + | ^^^^^ ------ this expression has type `&mut _` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut _` + found reference `&'static str` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/deref-patterns/deref-box.rs b/tests/ui/pattern/deref-patterns/deref-box.rs new file mode 100644 index 0000000000000..39b23dcab51e7 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/deref-box.rs @@ -0,0 +1,37 @@ +//@ run-pass +//! Deref patterns on boxes are lowered using built-in derefs, rather than generic `Deref::deref` +//! and `DerefMut::deref_mut`. Test that they work as expected. + +#![feature(deref_patterns)] +#![expect(incomplete_features)] + +fn unbox_1(b: Box) -> T { + let deref!(x) = b; + x +} + +fn unbox_2(b: Box<(T,)>) -> T { + let (x,) = b; + x +} + +fn unbox_separately(b: Box<(T, T)>) -> (T, T) { + let (x, _) = b; + let (_, y) = b; + (x, y) +} + +fn main() { + // test that deref patterns can move out of boxes + let b1 = Box::new(0); + let b2 = Box::new((0,)); + assert_eq!(unbox_1(b1), unbox_2(b2)); + let b3 = Box::new((1, 2)); + assert_eq!(unbox_separately(b3), (1, 2)); + + // test that borrowing from a box also works + let mut b = "hi".to_owned().into_boxed_str(); + let deref!(ref mut s) = b; + s.make_ascii_uppercase(); + assert_eq!(&*b, "HI"); +} diff --git a/tests/ui/pattern/deref-patterns/dont-ice-on-slice-in-deref-pat-in-closure.rs b/tests/ui/pattern/deref-patterns/dont-ice-on-slice-in-deref-pat-in-closure.rs new file mode 100644 index 0000000000000..e1a37b9c65f4b --- /dev/null +++ b/tests/ui/pattern/deref-patterns/dont-ice-on-slice-in-deref-pat-in-closure.rs @@ -0,0 +1,15 @@ +//@ check-pass +//! Regression test for ICE in `rustc_hir_typeck::expr_use_visitor` on nesting a slice pattern +//! inside a deref pattern inside a closure: rust-lang/rust#125059 + +#![feature(deref_patterns)] +#![allow(incomplete_features, unused)] + +fn simple_vec(vec: Vec) -> u32 { + (|| match Vec::::new() { + deref!([]) => 100, + _ => 2000, + })() +} + +fn main() {} diff --git a/tests/ui/pattern/deref-patterns/fake_borrows.rs b/tests/ui/pattern/deref-patterns/fake_borrows.rs index 35fa9cbf7d859..fba2873fd02a3 100644 --- a/tests/ui/pattern/deref-patterns/fake_borrows.rs +++ b/tests/ui/pattern/deref-patterns/fake_borrows.rs @@ -3,6 +3,23 @@ #[rustfmt::skip] fn main() { + let mut v = vec![false]; + match v { + deref!([true]) => {} + _ if { v[0] = true; false } => {} + //~^ ERROR cannot borrow `v` as mutable because it is also borrowed as immutable + deref!([false]) => {} + _ => {}, + } + match v { + [true] => {} + _ if { v[0] = true; false } => {} + //~^ ERROR cannot borrow `v` as mutable because it is also borrowed as immutable + [false] => {} + _ => {}, + } + + // deref patterns on boxes are lowered specially; test them separately. let mut b = Box::new(false); match b { deref!(true) => {} @@ -11,4 +28,11 @@ fn main() { deref!(false) => {} _ => {}, } + match b { + true => {} + _ if { *b = true; false } => {} + //~^ ERROR cannot assign `*b` in match guard + false => {} + _ => {}, + } } diff --git a/tests/ui/pattern/deref-patterns/fake_borrows.stderr b/tests/ui/pattern/deref-patterns/fake_borrows.stderr index 6a591e6416c5d..7dc3001739e67 100644 --- a/tests/ui/pattern/deref-patterns/fake_borrows.stderr +++ b/tests/ui/pattern/deref-patterns/fake_borrows.stderr @@ -1,12 +1,44 @@ -error[E0510]: cannot assign `*b` in match guard +error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable --> $DIR/fake_borrows.rs:9:16 | +LL | match v { + | - immutable borrow occurs here +LL | deref!([true]) => {} +LL | _ if { v[0] = true; false } => {} + | ^ - immutable borrow later used here + | | + | mutable borrow occurs here + +error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable + --> $DIR/fake_borrows.rs:16:16 + | +LL | match v { + | - immutable borrow occurs here +LL | [true] => {} +LL | _ if { v[0] = true; false } => {} + | ^ - immutable borrow later used here + | | + | mutable borrow occurs here + +error[E0510]: cannot assign `*b` in match guard + --> $DIR/fake_borrows.rs:26:16 + | LL | match b { | - value is immutable in match guard LL | deref!(true) => {} LL | _ if { *b = true; false } => {} | ^^^^^^^^^ cannot assign -error: aborting due to 1 previous error +error[E0510]: cannot assign `*b` in match guard + --> $DIR/fake_borrows.rs:33:16 + | +LL | match b { + | - value is immutable in match guard +LL | true => {} +LL | _ if { *b = true; false } => {} + | ^^^^^^^^^ cannot assign + +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0510`. +Some errors have detailed explanations: E0502, E0510. +For more information about an error, try `rustc --explain E0502`. diff --git a/tests/ui/pattern/deref-patterns/implicit-const-deref.rs b/tests/ui/pattern/deref-patterns/implicit-const-deref.rs new file mode 100644 index 0000000000000..70f89629bc228 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/implicit-const-deref.rs @@ -0,0 +1,19 @@ +//! Test that we get an error about structural equality rather than a type error when attempting to +//! use const patterns of library pointer types. Currently there aren't any smart pointers that can +//! be used in constant patterns, but we still need to make sure we don't implicitly dereference the +//! scrutinee and end up with a type error; this would prevent us from reporting that only constants +//! supporting structural equality can be used as patterns. +#![feature(deref_patterns)] +#![allow(incomplete_features)] + +const EMPTY: Vec<()> = Vec::new(); + +fn main() { + // FIXME(inline_const_pat): if `inline_const_pat` is reinstated, there should be a case here for + // inline const block patterns as well; they're checked differently than named constants. + match vec![()] { + EMPTY => {} + //~^ ERROR: constant of non-structural type `Vec<()>` in a pattern + _ => {} + } +} diff --git a/tests/ui/pattern/deref-patterns/implicit-const-deref.stderr b/tests/ui/pattern/deref-patterns/implicit-const-deref.stderr new file mode 100644 index 0000000000000..21d09ec44c4f8 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/implicit-const-deref.stderr @@ -0,0 +1,16 @@ +error: constant of non-structural type `Vec<()>` in a pattern + --> $DIR/implicit-const-deref.rs:15:9 + | +LL | const EMPTY: Vec<()> = Vec::new(); + | -------------------- constant defined here +... +LL | EMPTY => {} + | ^^^^^ constant of non-structural type + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + | + = note: `Vec<()>` must be annotated with `#[derive(PartialEq)]` to be usable in patterns + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + +error: aborting due to 1 previous error + diff --git a/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs b/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs new file mode 100644 index 0000000000000..24770261edc53 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs @@ -0,0 +1,42 @@ +//@ run-pass +//! Test that implicit deref patterns interact as expected with `Cow` constructor patterns. +#![feature(deref_patterns)] +#![allow(incomplete_features)] + +use std::borrow::Cow; +use std::rc::Rc; + +fn main() { + let cow: Cow<'static, [u8]> = Cow::Borrowed(&[1, 2, 3]); + + match cow { + [..] => {} + } + + match cow { + Cow::Borrowed(_) => {} + Cow::Owned(_) => unreachable!(), + } + + match Rc::new(&cow) { + Cow::Borrowed { 0: _ } => {} + Cow::Owned { 0: _ } => unreachable!(), + } + + let cow_of_cow: Cow<'_, Cow<'static, [u8]>> = Cow::Owned(cow); + + match cow_of_cow { + [..] => {} + } + + // This matches on the outer `Cow` (the owned one). + match cow_of_cow { + Cow::Borrowed(_) => unreachable!(), + Cow::Owned(_) => {} + } + + match Rc::new(&cow_of_cow) { + Cow::Borrowed { 0: _ } => unreachable!(), + Cow::Owned { 0: _ } => {} + } +} diff --git a/tests/ui/pattern/deref-patterns/needs-gate.rs b/tests/ui/pattern/deref-patterns/needs-gate.rs new file mode 100644 index 0000000000000..953051f7b04b3 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/needs-gate.rs @@ -0,0 +1,58 @@ +// gate-test-deref_patterns + +fn main() { + match Box::new(0) { + deref!(0) => {} + //~^ ERROR: use of unstable library feature `deref_patterns`: placeholder syntax for deref patterns + _ => {} + } + + match Box::new(0) { + 0 => {} + //~^ ERROR: mismatched types + _ => {} + } + + // `deref_patterns` allows string and byte string literals to have non-ref types. + match *"test" { + "test" => {} + //~^ ERROR: mismatched types + _ => {} + } + match *b"test" { + b"test" => {} + //~^ ERROR: mismatched types + _ => {} + } + match *(b"test" as &[u8]) { + b"test" => {} + //~^ ERROR: mismatched types + _ => {} + } + + // `deref_patterns` allows string and byte string patterns to implicitly peel references. + match &"str" { + "str" => {} + //~^ ERROR: mismatched types + _ => {} + } + match &b"str" { + b"str" => {} + //~^ ERROR: mismatched types + _ => {} + } + match "str".to_owned() { + "str" => {} + //~^ ERROR: mismatched types + _ => {} + } + + // `deref_patterns` allows string and byte string patterns to match on mutable references. + // See also `tests/ui/pattern/byte-string-mutability-mismatch.rs`. + if let "str" = &mut *"str".to_string() {} + //~^ ERROR mismatched types + if let b"str" = &mut b"str".clone() {} + //~^ ERROR mismatched types + if let b"str" = &mut b"str".clone()[..] {} + //~^ ERROR mismatched types +} diff --git a/tests/ui/pattern/deref-patterns/needs-gate.stderr b/tests/ui/pattern/deref-patterns/needs-gate.stderr new file mode 100644 index 0000000000000..3d938a7e23fc0 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/needs-gate.stderr @@ -0,0 +1,116 @@ +error[E0658]: use of unstable library feature `deref_patterns`: placeholder syntax for deref patterns + --> $DIR/needs-gate.rs:5:9 + | +LL | deref!(0) => {} + | ^^^^^ + | + = note: see issue #87121 for more information + = help: add `#![feature(deref_patterns)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:11:9 + | +LL | match Box::new(0) { + | ----------- this expression has type `Box<{integer}>` +LL | 0 => {} + | ^ expected `Box<{integer}>`, found integer + | + = note: expected struct `Box<{integer}>` + found type `{integer}` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | match *Box::new(0) { + | + + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:18:9 + | +LL | match *"test" { + | ------- this expression has type `str` +LL | "test" => {} + | ^^^^^^ expected `str`, found `&str` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:23:9 + | +LL | match *b"test" { + | -------- this expression has type `[u8; 4]` +LL | b"test" => {} + | ^^^^^^^ expected `[u8; 4]`, found `&[u8; 4]` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:28:9 + | +LL | match *(b"test" as &[u8]) { + | ------------------- this expression has type `[u8]` +LL | b"test" => {} + | ^^^^^^^ expected `[u8]`, found `&[u8; 4]` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:35:9 + | +LL | match &"str" { + | ------ this expression has type `&&str` +LL | "str" => {} + | ^^^^^ expected `&&str`, found `&str` + | + = note: expected reference `&&_` + found reference `&'static _` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:40:9 + | +LL | match &b"str" { + | ------- this expression has type `&&[u8; 3]` +LL | b"str" => {} + | ^^^^^^ expected `&&[u8; 3]`, found `&[u8; 3]` + | + = note: expected reference `&&_` + found reference `&'static _` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:45:9 + | +LL | match "str".to_owned() { + | ---------------- this expression has type `String` +LL | "str" => {} + | ^^^^^ expected `String`, found `&str` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:52:12 + | +LL | if let "str" = &mut *"str".to_string() {} + | ^^^^^ ----------------------- this expression has type `&mut str` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut _` + found reference `&'static _` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:54:12 + | +LL | if let b"str" = &mut b"str".clone() {} + | ^^^^^^ ------------------- this expression has type `&mut [u8; 3]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut _` + found reference `&'static _` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:56:12 + | +LL | if let b"str" = &mut b"str".clone()[..] {} + | ^^^^^^ ----------------------- this expression has type `&mut [u8]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut _` + found reference `&'static _` + +error: aborting due to 11 previous errors + +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/deref-patterns/recursion-limit.rs b/tests/ui/pattern/deref-patterns/recursion-limit.rs new file mode 100644 index 0000000000000..c5fe520f6f1ab --- /dev/null +++ b/tests/ui/pattern/deref-patterns/recursion-limit.rs @@ -0,0 +1,23 @@ +//! Test that implicit deref patterns respect the recursion limit +#![feature(deref_patterns)] +#![allow(incomplete_features)] +#![recursion_limit = "8"] + +use std::ops::Deref; + +struct Cyclic; +impl Deref for Cyclic { + type Target = Cyclic; + fn deref(&self) -> &Cyclic { + &Cyclic + } +} + +fn main() { + match &Box::new(Cyclic) { + () => {} + //~^ ERROR: reached the recursion limit while auto-dereferencing `Cyclic` + //~| ERROR: the trait bound `Cyclic: DerefPure` is not satisfied + _ => {} + } +} diff --git a/tests/ui/pattern/deref-patterns/recursion-limit.stderr b/tests/ui/pattern/deref-patterns/recursion-limit.stderr new file mode 100644 index 0000000000000..9a83d1eb5a4a4 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/recursion-limit.stderr @@ -0,0 +1,18 @@ +error[E0055]: reached the recursion limit while auto-dereferencing `Cyclic` + --> $DIR/recursion-limit.rs:18:9 + | +LL | () => {} + | ^^ deref recursion limit reached + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "16"]` attribute to your crate (`recursion_limit`) + +error[E0277]: the trait bound `Cyclic: DerefPure` is not satisfied + --> $DIR/recursion-limit.rs:18:9 + | +LL | () => {} + | ^^ the trait `DerefPure` is not implemented for `Cyclic` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0055, E0277. +For more information about an error, try `rustc --explain E0055`. diff --git a/tests/ui/pattern/deref-patterns/ref-mut.rs b/tests/ui/pattern/deref-patterns/ref-mut.rs index 1918008a761a7..43738671346d3 100644 --- a/tests/ui/pattern/deref-patterns/ref-mut.rs +++ b/tests/ui/pattern/deref-patterns/ref-mut.rs @@ -8,10 +8,19 @@ fn main() { deref!(x) => {} _ => {} } + match &mut vec![1] { + [x] => {} + _ => {} + } match &mut Rc::new(1) { deref!(x) => {} //~^ ERROR the trait bound `Rc<{integer}>: DerefMut` is not satisfied _ => {} } + match &mut Rc::new((1,)) { + (x,) => {} + //~^ ERROR the trait bound `Rc<({integer},)>: DerefMut` is not satisfied + _ => {} + } } diff --git a/tests/ui/pattern/deref-patterns/ref-mut.stderr b/tests/ui/pattern/deref-patterns/ref-mut.stderr index 41f1c3061ce6d..24a35b418e95c 100644 --- a/tests/ui/pattern/deref-patterns/ref-mut.stderr +++ b/tests/ui/pattern/deref-patterns/ref-mut.stderr @@ -8,13 +8,19 @@ LL | #![feature(deref_patterns)] = note: `#[warn(incomplete_features)]` on by default error[E0277]: the trait bound `Rc<{integer}>: DerefMut` is not satisfied - --> $DIR/ref-mut.rs:13:9 + --> $DIR/ref-mut.rs:17:9 | LL | deref!(x) => {} | ^^^^^^^^^ the trait `DerefMut` is not implemented for `Rc<{integer}>` | = note: this error originates in the macro `deref` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 1 previous error; 1 warning emitted +error[E0277]: the trait bound `Rc<({integer},)>: DerefMut` is not satisfied + --> $DIR/ref-mut.rs:22:9 + | +LL | (x,) => {} + | ^^^^ the trait `DerefMut` is not implemented for `Rc<({integer},)>` + +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/pattern/deref-patterns/strings.rs b/tests/ui/pattern/deref-patterns/strings.rs new file mode 100644 index 0000000000000..fac15a9aee3de --- /dev/null +++ b/tests/ui/pattern/deref-patterns/strings.rs @@ -0,0 +1,98 @@ +//@ run-pass +//! Test deref patterns using string and bytestring literals. + +#![feature(deref_patterns)] +#![allow(incomplete_features)] + +fn main() { + for (test_in, test_expect) in [("zero", 0), ("one", 1), ("two", 2)] { + // Test string literal patterns having type `str`. + let test_actual = match *test_in { + "zero" => 0, + "one" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test matching on `&mut str`. + let test_actual = match &mut *test_in.to_string() { + "zero" => 0, + "one" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test string literals in deref patterns. + let test_actual = match test_in.to_string() { + deref!("zero") => 0, + "one" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test peeling references in addition to smart pointers. + let test_actual = match &test_in.to_string() { + deref!("zero") => 0, + "one" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + } + + // Test that we can still mutate in the match arm after using a literal to test equality: + let mut test = "test".to_string(); + if let deref!(s @ "test") = &mut test { + s.make_ascii_uppercase(); + } + assert_eq!(test, "TEST"); + + for (test_in, test_expect) in [(b"0", 0), (b"1", 1), (b"2", 2)] { + // Test byte string literal patterns having type `[u8; N]` + let test_actual = match *test_in { + b"0" => 0, + b"1" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test byte string literal patterns having type `[u8]` + let test_actual = match *(test_in as &[u8]) { + b"0" => 0, + b"1" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test matching on `&mut [u8; N]`. + let test_actual = match &mut test_in.clone() { + b"0" => 0, + b"1" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test matching on `&mut [u8]`. + let test_actual = match &mut test_in.clone()[..] { + b"0" => 0, + b"1" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test byte string literals used as arrays in deref patterns. + let test_actual = match Box::new(*test_in) { + deref!(b"0") => 0, + b"1" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test byte string literals used as slices in deref patterns. + let test_actual = match test_in.to_vec() { + deref!(b"0") => 0, + b"1" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + } +} diff --git a/tests/ui/pattern/deref-patterns/typeck.rs b/tests/ui/pattern/deref-patterns/typeck.rs index f23f7042cd892..3a7ce9d1deb3d 100644 --- a/tests/ui/pattern/deref-patterns/typeck.rs +++ b/tests/ui/pattern/deref-patterns/typeck.rs @@ -10,26 +10,32 @@ fn main() { let vec: Vec = Vec::new(); match vec { deref!([..]) => {} + [..] => {} _ => {} } match Box::new(true) { deref!(true) => {} + true => {} _ => {} } match &Box::new(true) { deref!(true) => {} + true => {} _ => {} } match &Rc::new(0) { deref!(1..) => {} + 1.. => {} _ => {} } let _: &Struct = match &Rc::new(Struct) { deref!(x) => x, + Struct => &Struct, _ => unreachable!(), }; let _: &[Struct] = match &Rc::new(vec![Struct]) { deref!(deref!(x)) => x, + [Struct] => &[Struct], _ => unreachable!(), }; } diff --git a/tests/ui/pattern/deref-patterns/typeck_fail.rs b/tests/ui/pattern/deref-patterns/typeck_fail.rs index 040118449ecca..6ae87bb7bc362 100644 --- a/tests/ui/pattern/deref-patterns/typeck_fail.rs +++ b/tests/ui/pattern/deref-patterns/typeck_fail.rs @@ -2,15 +2,9 @@ #![allow(incomplete_features)] fn main() { - // FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a - // place of type `str`. - match "foo".to_string() { - deref!("foo") => {} - //~^ ERROR: mismatched types - _ => {} - } - match &"foo".to_string() { - deref!("foo") => {} + // Make sure we don't try implicitly dereferncing any ADT. + match Some(0) { + Ok(0) => {} //~^ ERROR: mismatched types _ => {} } diff --git a/tests/ui/pattern/deref-patterns/typeck_fail.stderr b/tests/ui/pattern/deref-patterns/typeck_fail.stderr index 1c14802745a0c..fc29caac56301 100644 --- a/tests/ui/pattern/deref-patterns/typeck_fail.stderr +++ b/tests/ui/pattern/deref-patterns/typeck_fail.stderr @@ -1,19 +1,14 @@ error[E0308]: mismatched types - --> $DIR/typeck_fail.rs:8:16 + --> $DIR/typeck_fail.rs:7:9 | -LL | match "foo".to_string() { - | ----------------- this expression has type `String` -LL | deref!("foo") => {} - | ^^^^^ expected `str`, found `&str` - -error[E0308]: mismatched types - --> $DIR/typeck_fail.rs:13:16 +LL | match Some(0) { + | ------- this expression has type `Option<{integer}>` +LL | Ok(0) => {} + | ^^^^^ expected `Option<{integer}>`, found `Result<_, _>` | -LL | match &"foo".to_string() { - | ------------------ this expression has type `&String` -LL | deref!("foo") => {} - | ^^^^^ expected `str`, found `&str` + = note: expected enum `Option<{integer}>` + found enum `Result<_, _>` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/deref-patterns/unsatisfied-bounds.rs b/tests/ui/pattern/deref-patterns/unsatisfied-bounds.rs new file mode 100644 index 0000000000000..9e95f4ec40962 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/unsatisfied-bounds.rs @@ -0,0 +1,21 @@ +#![feature(deref_patterns)] +#![allow(incomplete_features)] + +struct MyPointer; + +impl std::ops::Deref for MyPointer { + type Target = (); + fn deref(&self) -> &() { + &() + } +} + +fn main() { + // Test that we get a trait error if a user attempts implicit deref pats on their own impls. + // FIXME(deref_patterns): there should be a special diagnostic for missing `DerefPure`. + match MyPointer { + () => {} + //~^ ERROR the trait bound `MyPointer: DerefPure` is not satisfied + _ => {} + } +} diff --git a/tests/ui/pattern/deref-patterns/unsatisfied-bounds.stderr b/tests/ui/pattern/deref-patterns/unsatisfied-bounds.stderr new file mode 100644 index 0000000000000..983ce27865c99 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/unsatisfied-bounds.stderr @@ -0,0 +1,9 @@ +error[E0277]: the trait bound `MyPointer: DerefPure` is not satisfied + --> $DIR/unsatisfied-bounds.rs:17:9 + | +LL | () => {} + | ^^ the trait `DerefPure` is not implemented for `MyPointer` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/pattern/deref-patterns/usefulness/empty-types.rs b/tests/ui/pattern/deref-patterns/usefulness/empty-types.rs new file mode 100644 index 0000000000000..03419030e72fd --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/empty-types.rs @@ -0,0 +1,47 @@ +//! Test that the place behind a deref pattern is treated as maybe-invalid, and thus empty arms +//! cannot be omitted. This is handled the same as for refs and union fields, so this leaves the +//! bulk of the testing to `tests/ui/pattern/usefulness/empty-types.rs`. +// FIXME(deref_patterns): On stabilization, cases for deref patterns could be worked into that file +// to keep the tests for empty types in one place and test more thoroughly. +#![feature(deref_patterns)] +#![expect(incomplete_features)] +#![deny(unreachable_patterns)] + +enum Void {} + +fn main() { + // Sanity check: matching on an empty type without pointer indirection lets us omit arms. + let opt_void: Option = None; + match opt_void { + None => {} + } + + // But if we hide it behind a smart pointer, we need an arm. + let box_opt_void: Box> = Box::new(None); + match box_opt_void { + //~^ ERROR non-exhaustive patterns: `deref!(Some(_))` not covered + None => {} + } + match box_opt_void { + None => {} + Some(_) => {} + } + match box_opt_void { + None => {} + _ => {} + } + + // For consistency, this behaves the same as if we manually dereferenced the scrutinee. + match *box_opt_void { + //~^ ERROR non-exhaustive patterns: `Some(_)` not covered + None => {} + } + match *box_opt_void { + None => {} + Some(_) => {} + } + match *box_opt_void { + None => {} + _ => {} + } +} diff --git a/tests/ui/pattern/deref-patterns/usefulness/empty-types.stderr b/tests/ui/pattern/deref-patterns/usefulness/empty-types.stderr new file mode 100644 index 0000000000000..e32477085661c --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/empty-types.stderr @@ -0,0 +1,38 @@ +error[E0004]: non-exhaustive patterns: `deref!(Some(_))` not covered + --> $DIR/empty-types.rs:21:11 + | +LL | match box_opt_void { + | ^^^^^^^^^^^^ pattern `deref!(Some(_))` not covered + | +note: `Box>` defined here + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + = note: the matched value is of type `Box>` + = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ None => {}, +LL + deref!(Some(_)) => todo!() + | + +error[E0004]: non-exhaustive patterns: `Some(_)` not covered + --> $DIR/empty-types.rs:35:11 + | +LL | match *box_opt_void { + | ^^^^^^^^^^^^^ pattern `Some(_)` not covered + | +note: `Option` defined here + --> $SRC_DIR/core/src/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL + | + = note: not covered + = note: the matched value is of type `Option` + = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ None => {}, +LL + Some(_) => todo!() + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.rs b/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.rs new file mode 100644 index 0000000000000..f567dc07bb592 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.rs @@ -0,0 +1,48 @@ +//! Test matches with a mix of ADT constructors and deref patterns. Currently, usefulness analysis +//! doesn't support this, so make sure we catch it beforehand. As a consequence, it takes priority +//! over non-exhaustive match and unreachable pattern errors. +#![feature(deref_patterns)] +#![expect(incomplete_features)] +#![deny(unreachable_patterns)] + +use std::borrow::Cow; + +fn main() { + let cow: Cow<'static, bool> = Cow::Borrowed(&false); + + match cow { + true => {} + //~v ERROR mix of deref patterns and normal constructors + false => {} + Cow::Borrowed(_) => {} + } + + match cow { + Cow::Owned(_) => {} + Cow::Borrowed(_) => {} + true => {} + //~^ ERROR mix of deref patterns and normal constructors + } + + match cow { + _ => {} + Cow::Owned(_) => {} + false => {} + //~^ ERROR mix of deref patterns and normal constructors + } + + match (cow, 0) { + (Cow::Owned(_), 0) => {} + (Cow::Borrowed(_), 0) => {} + (true, 0) => {} + //~^ ERROR mix of deref patterns and normal constructors + } + + match (0, cow) { + (0, Cow::Owned(_)) => {} + (0, Cow::Borrowed(_)) => {} + _ => {} + (1, true) => {} + //~^ ERROR mix of deref patterns and normal constructors + } +} diff --git a/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.stderr b/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.stderr new file mode 100644 index 0000000000000..5ad24164b9850 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.stderr @@ -0,0 +1,43 @@ +error: mix of deref patterns and normal constructors + --> $DIR/mixed-constructors.rs:16:9 + | +LL | false => {} + | ^^^^^ matches on the result of dereferencing `Cow<'_, bool>` +LL | Cow::Borrowed(_) => {} + | ^^^^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>` + +error: mix of deref patterns and normal constructors + --> $DIR/mixed-constructors.rs:22:9 + | +LL | Cow::Borrowed(_) => {} + | ^^^^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>` +LL | true => {} + | ^^^^ matches on the result of dereferencing `Cow<'_, bool>` + +error: mix of deref patterns and normal constructors + --> $DIR/mixed-constructors.rs:29:9 + | +LL | Cow::Owned(_) => {} + | ^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>` +LL | false => {} + | ^^^^^ matches on the result of dereferencing `Cow<'_, bool>` + +error: mix of deref patterns and normal constructors + --> $DIR/mixed-constructors.rs:36:10 + | +LL | (Cow::Borrowed(_), 0) => {} + | ^^^^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>` +LL | (true, 0) => {} + | ^^^^ matches on the result of dereferencing `Cow<'_, bool>` + +error: mix of deref patterns and normal constructors + --> $DIR/mixed-constructors.rs:43:13 + | +LL | (0, Cow::Borrowed(_)) => {} + | ^^^^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>` +LL | _ => {} +LL | (1, true) => {} + | ^^^^ matches on the result of dereferencing `Cow<'_, bool>` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.rs b/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.rs new file mode 100644 index 0000000000000..704cae8bdbc4b --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.rs @@ -0,0 +1,28 @@ +//! Test non-exhaustive matches involving deref patterns. +#![feature(deref_patterns)] +#![expect(incomplete_features)] +#![deny(unreachable_patterns)] + +fn main() { + match Box::new(false) { + //~^ ERROR non-exhaustive patterns: `deref!(true)` not covered + false => {} + } + + match Box::new(Box::new(false)) { + //~^ ERROR non-exhaustive patterns: `deref!(deref!(false))` not covered + true => {} + } + + match Box::new((true, Box::new(false))) { + //~^ ERROR non-exhaustive patterns: `deref!((false, deref!(false)))` and `deref!((true, deref!(true)))` not covered + (true, false) => {} + (false, true) => {} + } + + enum T { A, B, C } + match Box::new((Box::new(T::A), Box::new(T::A))) { + //~^ ERROR non-exhaustive patterns: `deref!((deref!(T::C), _))` not covered + (T::A | T::B, T::C) => {} + } +} diff --git a/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.stderr b/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.stderr new file mode 100644 index 0000000000000..55fa84bafde26 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.stderr @@ -0,0 +1,63 @@ +error[E0004]: non-exhaustive patterns: `deref!(true)` not covered + --> $DIR/non-exhaustive.rs:7:11 + | +LL | match Box::new(false) { + | ^^^^^^^^^^^^^^^ pattern `deref!(true)` not covered + | +note: `Box` defined here + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + = note: the matched value is of type `Box` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ false => {}, +LL + deref!(true) => todo!() + | + +error[E0004]: non-exhaustive patterns: `deref!(deref!(false))` not covered + --> $DIR/non-exhaustive.rs:12:11 + | +LL | match Box::new(Box::new(false)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `deref!(deref!(false))` not covered + | +note: `Box>` defined here + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + = note: the matched value is of type `Box>` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ true => {}, +LL + deref!(deref!(false)) => todo!() + | + +error[E0004]: non-exhaustive patterns: `deref!((false, deref!(false)))` and `deref!((true, deref!(true)))` not covered + --> $DIR/non-exhaustive.rs:17:11 + | +LL | match Box::new((true, Box::new(false))) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ patterns `deref!((false, deref!(false)))` and `deref!((true, deref!(true)))` not covered + | +note: `Box<(bool, Box)>` defined here + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + = note: the matched value is of type `Box<(bool, Box)>` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms + | +LL ~ (false, true) => {}, +LL + deref!((false, deref!(false))) | deref!((true, deref!(true))) => todo!() + | + +error[E0004]: non-exhaustive patterns: `deref!((deref!(T::C), _))` not covered + --> $DIR/non-exhaustive.rs:24:11 + | +LL | match Box::new((Box::new(T::A), Box::new(T::A))) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `deref!((deref!(T::C), _))` not covered + | +note: `Box<(Box, Box)>` defined here + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + = note: the matched value is of type `Box<(Box, Box)>` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ (T::A | T::B, T::C) => {}, +LL + deref!((deref!(T::C), _)) => todo!() + | + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.rs b/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.rs new file mode 100644 index 0000000000000..2677fc54dedc2 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.rs @@ -0,0 +1,33 @@ +//! Test unreachable patterns involving deref patterns. +#![feature(deref_patterns)] +#![expect(incomplete_features)] +#![deny(unreachable_patterns)] + +fn main() { + match Box::new(false) { + true => {} + false => {} + false => {} //~ ERROR unreachable pattern + } + + match Box::new(Box::new(false)) { + true => {} + false => {} + true => {} //~ ERROR unreachable pattern + } + + match Box::new((true, Box::new(false))) { + (true, _) => {} + (_, true) => {} + (false, false) => {} + _ => {} //~ ERROR unreachable pattern + } + + enum T { A, B, C } + match Box::new((Box::new(T::A), Box::new(T::A))) { + (T::A | T::B, T::A | T::C) => {} + (T::A, T::C) => {} //~ ERROR unreachable pattern + (T::B, T::A) => {} //~ ERROR unreachable pattern + _ => {} + } +} diff --git a/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.stderr b/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.stderr new file mode 100644 index 0000000000000..045e11be31966 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.stderr @@ -0,0 +1,60 @@ +error: unreachable pattern + --> $DIR/unreachable-patterns.rs:10:9 + | +LL | false => {} + | ----- matches all the relevant values +LL | false => {} + | ^^^^^ no value can reach this + | +note: the lint level is defined here + --> $DIR/unreachable-patterns.rs:4:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable-patterns.rs:16:9 + | +LL | true => {} + | ---- matches all the relevant values +LL | false => {} +LL | true => {} + | ^^^^ no value can reach this + +error: unreachable pattern + --> $DIR/unreachable-patterns.rs:23:9 + | +LL | _ => {} + | ^ no value can reach this + | +note: multiple earlier patterns match some of the same values + --> $DIR/unreachable-patterns.rs:23:9 + | +LL | (true, _) => {} + | --------- matches some of the same values +LL | (_, true) => {} + | --------- matches some of the same values +LL | (false, false) => {} + | -------------- matches some of the same values +LL | _ => {} + | ^ collectively making this unreachable + +error: unreachable pattern + --> $DIR/unreachable-patterns.rs:29:9 + | +LL | (T::A | T::B, T::A | T::C) => {} + | -------------------------- matches all the relevant values +LL | (T::A, T::C) => {} + | ^^^^^^^^^^^^ no value can reach this + +error: unreachable pattern + --> $DIR/unreachable-patterns.rs:30:9 + | +LL | (T::A | T::B, T::A | T::C) => {} + | -------------------------- matches all the relevant values +LL | (T::A, T::C) => {} +LL | (T::B, T::A) => {} + | ^^^^^^^^^^^^ no value can reach this + +error: aborting due to 5 previous errors + diff --git a/tests/ui/pattern/non-structural-match-types-cycle-err.rs b/tests/ui/pattern/non-structural-match-types-cycle-err.rs new file mode 100644 index 0000000000000..a8e494c35b0c3 --- /dev/null +++ b/tests/ui/pattern/non-structural-match-types-cycle-err.rs @@ -0,0 +1,24 @@ +//@ edition:2021 + +struct AnyOption(T); +impl AnyOption { + const NONE: Option = None; +} + +// This is an unfortunate side-effect of borrowchecking nested items +// together with their parent. Evaluating the `AnyOption::<_>::NONE` +// pattern for exhaustiveness checking relies on the layout of the +// async block. This layout relies on `optimized_mir` of the nested +// item which is now borrowck'd together with its parent. As +// borrowck of the parent requires us to have already lowered the match, +// this is a query cycle. + +fn uwu() {} +fn defines() { + match Some(async {}) { + AnyOption::<_>::NONE => {} + //~^ ERROR cycle detected when building THIR for `defines` + _ => {} + } +} +fn main() {} diff --git a/tests/ui/pattern/non-structural-match-types-cycle-err.stderr b/tests/ui/pattern/non-structural-match-types-cycle-err.stderr new file mode 100644 index 0000000000000..2f4ac63fc570a --- /dev/null +++ b/tests/ui/pattern/non-structural-match-types-cycle-err.stderr @@ -0,0 +1,64 @@ +error[E0391]: cycle detected when building THIR for `defines` + --> $DIR/non-structural-match-types-cycle-err.rs:19:9 + | +LL | AnyOption::<_>::NONE => {} + | ^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires evaluating type-level constant... + --> $DIR/non-structural-match-types-cycle-err.rs:5:5 + | +LL | const NONE: Option = None; + | ^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `::NONE`... + --> $DIR/non-structural-match-types-cycle-err.rs:5:5 + | +LL | const NONE: Option = None; + | ^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires computing layout of `core::option::Option<{async block@$DIR/non-structural-match-types-cycle-err.rs:18:16: 18:21}>`... + = note: ...which requires computing layout of `{async block@$DIR/non-structural-match-types-cycle-err.rs:18:16: 18:21}`... +note: ...which requires optimizing MIR for `defines::{closure#0}`... + --> $DIR/non-structural-match-types-cycle-err.rs:18:16 + | +LL | match Some(async {}) { + | ^^^^^ +note: ...which requires elaborating drops for `defines::{closure#0}`... + --> $DIR/non-structural-match-types-cycle-err.rs:18:16 + | +LL | match Some(async {}) { + | ^^^^^ +note: ...which requires borrow-checking `defines`... + --> $DIR/non-structural-match-types-cycle-err.rs:17:1 + | +LL | fn defines() { + | ^^^^^^^^^^^^ +note: ...which requires promoting constants in MIR for `defines`... + --> $DIR/non-structural-match-types-cycle-err.rs:17:1 + | +LL | fn defines() { + | ^^^^^^^^^^^^ +note: ...which requires checking if `defines` contains FFI-unwind calls... + --> $DIR/non-structural-match-types-cycle-err.rs:17:1 + | +LL | fn defines() { + | ^^^^^^^^^^^^ +note: ...which requires building MIR for `defines`... + --> $DIR/non-structural-match-types-cycle-err.rs:17:1 + | +LL | fn defines() { + | ^^^^^^^^^^^^ +note: ...which requires match-checking `defines`... + --> $DIR/non-structural-match-types-cycle-err.rs:17:1 + | +LL | fn defines() { + | ^^^^^^^^^^^^ + = note: ...which again requires building THIR for `defines`, completing the cycle +note: cycle used when unsafety-checking `defines` + --> $DIR/non-structural-match-types-cycle-err.rs:17:1 + | +LL | fn defines() { + | ^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/pattern/non-structural-match-types.rs b/tests/ui/pattern/non-structural-match-types.rs index dde44dfee9c3d..a5ff7fcdb5e4e 100644 --- a/tests/ui/pattern/non-structural-match-types.rs +++ b/tests/ui/pattern/non-structural-match-types.rs @@ -1,14 +1,21 @@ //@ edition:2021 +struct AnyOption(T); +impl AnyOption { + const NONE: Option = None; +} -#![allow(unreachable_code)] -#![feature(const_async_blocks)] -#![feature(inline_const_pat)] - -fn main() { - match loop {} { - const { || {} } => {} //~ ERROR cannot be used in patterns +fn uwu() {} +fn defines() { + match Some(uwu) { + AnyOption::<_>::NONE => {} + //~^ ERROR constant of non-structural type + _ => {} } - match loop {} { - const { async {} } => {} //~ ERROR cannot be used in patterns + + match Some(|| {}) { + AnyOption::<_>::NONE => {} + //~^ ERROR constant of non-structural type + _ => {} } } +fn main() {} diff --git a/tests/ui/pattern/non-structural-match-types.stderr b/tests/ui/pattern/non-structural-match-types.stderr index 3588751bf6687..3b74ffe7cb7f4 100644 --- a/tests/ui/pattern/non-structural-match-types.stderr +++ b/tests/ui/pattern/non-structural-match-types.stderr @@ -1,14 +1,28 @@ -error: closure `{closure@$DIR/non-structural-match-types.rs:9:17: 9:19}` cannot be used in patterns - --> $DIR/non-structural-match-types.rs:9:9 +error: constant of non-structural type `Option` in a pattern + --> $DIR/non-structural-match-types.rs:10:9 | -LL | const { || {} } => {} - | ^^^^^^^^^^^^^^^ closure can't be used in patterns +LL | impl AnyOption { + | -------------------- +LL | const NONE: Option = None; + | --------------------- constant defined here +... +LL | AnyOption::<_>::NONE => {} + | ^^^^^^^^^^^^^^^^^^^^ constant of non-structural type + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -error: `async` block `{async block@$DIR/non-structural-match-types.rs:12:17: 12:22}` cannot be used in patterns - --> $DIR/non-structural-match-types.rs:12:9 +error: constant of non-structural type `Option<{closure@$DIR/non-structural-match-types.rs:15:16: 15:18}>` in a pattern + --> $DIR/non-structural-match-types.rs:16:9 + | +LL | impl AnyOption { + | -------------------- +LL | const NONE: Option = None; + | --------------------- constant defined here +... +LL | AnyOption::<_>::NONE => {} + | ^^^^^^^^^^^^^^^^^^^^ constant of non-structural type | -LL | const { async {} } => {} - | ^^^^^^^^^^^^^^^^^^ `async` block can't be used in patterns + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details error: aborting due to 2 previous errors diff --git a/tests/ui/pattern/normalize-ty-in-range.rs b/tests/ui/pattern/normalize-ty-in-range.rs new file mode 100644 index 0000000000000..f0d22362608e1 --- /dev/null +++ b/tests/ui/pattern/normalize-ty-in-range.rs @@ -0,0 +1,24 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for . +// Make sure we structurally normalize in range pattern checking in HIR typeck. + +trait Foo { + type Bar; +} + +impl Foo for () { + type Bar = i32; +} + +fn main() { + const X: <() as Foo>::Bar = 0; + + match 0 { + X..=X => {} + _ => {} + } +} diff --git a/tests/ui/pattern/patkind-ref-binding-issue-114896.fixed b/tests/ui/pattern/patkind-ref-binding-issue-114896.fixed index 086a119eb7764..2eeca4bdb7a50 100644 --- a/tests/ui/pattern/patkind-ref-binding-issue-114896.fixed +++ b/tests/ui/pattern/patkind-ref-binding-issue-114896.fixed @@ -5,6 +5,6 @@ fn main() { fn x(a: &char) { let &(mut b) = a; b.make_ascii_uppercase(); -//~^ cannot borrow `b` as mutable, as it is not declared as mutable + //~^ ERROR cannot borrow `b` as mutable, as it is not declared as mutable } } diff --git a/tests/ui/pattern/patkind-ref-binding-issue-114896.rs b/tests/ui/pattern/patkind-ref-binding-issue-114896.rs index b4d6b72c01f2a..f074246b78290 100644 --- a/tests/ui/pattern/patkind-ref-binding-issue-114896.rs +++ b/tests/ui/pattern/patkind-ref-binding-issue-114896.rs @@ -5,6 +5,6 @@ fn main() { fn x(a: &char) { let &b = a; b.make_ascii_uppercase(); -//~^ cannot borrow `b` as mutable, as it is not declared as mutable + //~^ ERROR cannot borrow `b` as mutable, as it is not declared as mutable } } diff --git a/tests/ui/pattern/pattern-error-continue.rs b/tests/ui/pattern/pattern-error-continue.rs index bed949439237a..664d4e80ef561 100644 --- a/tests/ui/pattern/pattern-error-continue.rs +++ b/tests/ui/pattern/pattern-error-continue.rs @@ -1,5 +1,7 @@ // Test that certain pattern-match type errors are non-fatal +//@ dont-require-annotations: NOTE + enum A { B(isize, isize), C(isize, isize, isize), @@ -21,13 +23,13 @@ fn main() { match 'c' { S { .. } => (), //~^ ERROR mismatched types - //~| expected `char`, found `S` + //~| NOTE expected `char`, found `S` _ => () } f(true); //~^ ERROR mismatched types - //~| expected `char`, found `bool` + //~| NOTE expected `char`, found `bool` match () { E::V => {} //~ ERROR failed to resolve: use of undeclared type `E` diff --git a/tests/ui/pattern/pattern-error-continue.stderr b/tests/ui/pattern/pattern-error-continue.stderr index bb5582dd873a7..a9ac96e3eaffb 100644 --- a/tests/ui/pattern/pattern-error-continue.stderr +++ b/tests/ui/pattern/pattern-error-continue.stderr @@ -1,5 +1,5 @@ error[E0532]: expected tuple struct or tuple variant, found unit variant `A::D` - --> $DIR/pattern-error-continue.rs:18:9 + --> $DIR/pattern-error-continue.rs:20:9 | LL | B(isize, isize), | --------------- similarly named tuple variant `B` defined here @@ -22,7 +22,7 @@ LL + A::B(_) => (), | error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields - --> $DIR/pattern-error-continue.rs:17:14 + --> $DIR/pattern-error-continue.rs:19:14 | LL | B(isize, isize), | ----- ----- tuple variant has 2 fields @@ -31,7 +31,7 @@ LL | A::B(_, _, _) => (), | ^ ^ ^ expected 2 fields, found 3 error[E0308]: mismatched types - --> $DIR/pattern-error-continue.rs:22:9 + --> $DIR/pattern-error-continue.rs:24:9 | LL | match 'c' { | --- this expression has type `char` @@ -39,7 +39,7 @@ LL | S { .. } => (), | ^^^^^^^^ expected `char`, found `S` error[E0308]: mismatched types - --> $DIR/pattern-error-continue.rs:28:7 + --> $DIR/pattern-error-continue.rs:30:7 | LL | f(true); | - ^^^^ expected `char`, found `bool` @@ -47,13 +47,13 @@ LL | f(true); | arguments to this function are incorrect | note: function defined here - --> $DIR/pattern-error-continue.rs:13:4 + --> $DIR/pattern-error-continue.rs:15:4 | LL | fn f(_c: char) {} | ^ -------- error[E0433]: failed to resolve: use of undeclared type `E` - --> $DIR/pattern-error-continue.rs:33:9 + --> $DIR/pattern-error-continue.rs:35:9 | LL | E::V => {} | ^ diff --git a/tests/ui/pattern/rest-pat-syntactic.rs b/tests/ui/pattern/rest-pat-syntactic.rs index 1de29e69b0543..59c687bb5a8d8 100644 --- a/tests/ui/pattern/rest-pat-syntactic.rs +++ b/tests/ui/pattern/rest-pat-syntactic.rs @@ -11,7 +11,7 @@ macro_rules! accept_pat { accept_pat!(..); -#[cfg(FALSE)] +#[cfg(false)] fn rest_patterns() { // Top level: fn foo(..: u8) {} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2021.stderr index 355a8af6760f3..4d795d5c38518 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2021.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of a shared reference - --> $DIR/borrowck-errors.rs:31:29 + --> $DIR/borrowck-errors.rs:33:29 | LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { | - ^^^^^^^^^^^^^^^^^^^ @@ -14,19 +14,19 @@ LL + if let Some(Some(x)) = Some(&Some(&mut 0)) { | error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:36:10 + --> $DIR/borrowck-errors.rs:38:10 | LL | let &ref mut x = &0; | ^^^^^^^^^ cannot borrow as mutable error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:41:23 + --> $DIR/borrowck-errors.rs:43:23 | LL | if let &Some(Some(x)) = &Some(&mut Some(0)) { | ^ cannot borrow as mutable error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:46:11 + --> $DIR/borrowck-errors.rs:48:11 | LL | let &[x] = &&mut [0]; | ^ cannot borrow as mutable diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr index d40bdb9111b30..6cc6c58bf7af5 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr @@ -1,5 +1,5 @@ error[E0508]: cannot move out of type `[&mut i32; 1]`, a non-copy array - --> $DIR/borrowck-errors.rs:15:16 + --> $DIR/borrowck-errors.rs:17:16 | LL | let [&x] = &[&mut 0]; | - ^^^^^^^^^ cannot move out of here @@ -13,7 +13,7 @@ LL | let [&ref x] = &[&mut 0]; | +++ error[E0508]: cannot move out of type `[&mut i32; 1]`, a non-copy array - --> $DIR/borrowck-errors.rs:22:16 + --> $DIR/borrowck-errors.rs:24:16 | LL | let [&x] = &mut [&mut 0]; | - ^^^^^^^^^^^^^ cannot move out of here @@ -27,7 +27,7 @@ LL | let [&ref x] = &mut [&mut 0]; | +++ error[E0507]: cannot move out of a shared reference - --> $DIR/borrowck-errors.rs:31:29 + --> $DIR/borrowck-errors.rs:33:29 | LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { | - ^^^^^^^^^^^^^^^^^^^ @@ -42,25 +42,25 @@ LL + if let Some(Some(x)) = Some(&Some(&mut 0)) { | error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:36:10 + --> $DIR/borrowck-errors.rs:38:10 | LL | let &ref mut x = &0; | ^^^^^^^^^ cannot borrow as mutable error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:41:23 + --> $DIR/borrowck-errors.rs:43:23 | LL | if let &Some(Some(x)) = &Some(&mut Some(0)) { | ^ cannot borrow as mutable error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:46:11 + --> $DIR/borrowck-errors.rs:48:11 | LL | let &[x] = &&mut [0]; | ^ cannot borrow as mutable error[E0508]: cannot move out of type `[&mut i32; 1]`, a non-copy array - --> $DIR/borrowck-errors.rs:50:20 + --> $DIR/borrowck-errors.rs:52:20 | LL | let [&mut x] = &mut [&mut 0]; | - ^^^^^^^^^^^^^ cannot move out of here diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs index 621ca7cc792eb..15575510e1651 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs @@ -4,6 +4,8 @@ //@[structural2021] edition: 2021 //@[classic2024] edition: 2024 //@[structural2024] edition: 2024 +//@ dont-require-annotations: NOTE + //! Tests for pattern errors not handled by the pattern typing rules, but by borrowck. #![allow(incomplete_features)] #![cfg_attr(any(classic2021, classic2024), feature(ref_pat_eat_one_layer_2024))] @@ -13,15 +15,15 @@ /// to fail in HIR typeck on stable. As such, they need to be separate from the other tests. fn errors_caught_in_hir_typeck_on_stable() { let [&x] = &[&mut 0]; - //[stable2021]~^ mismatched types - //[stable2021]~| types differ in mutability + //[stable2021]~^ ERROR mismatched types + //[stable2021]~| NOTE types differ in mutability //[classic2024]~^^^ ERROR: cannot move out of type #[cfg(any(classic2021, structural2021))] let _: u32 = x; #[cfg(structural2024)] let _: &u32 = x; let [&x] = &mut [&mut 0]; - //[stable2021]~^ mismatched types - //[stable2021]~| types differ in mutability + //[stable2021]~^ ERROR mismatched types + //[stable2021]~| NOTE types differ in mutability //[classic2024]~^^^ ERROR: cannot move out of type #[cfg(any(classic2021, structural2021))] let _: u32 = x; #[cfg(structural2024)] let _: &u32 = x; @@ -34,7 +36,7 @@ pub fn main() { } let &ref mut x = &0; - //~^ cannot borrow data in a `&` reference as mutable [E0596] + //~^ ERROR cannot borrow data in a `&` reference as mutable [E0596] // For 2021 edition, this is also a regression test for #136223 // since the maximum mutability is downgraded during the pattern check process. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.stable2021.stderr index edcf9f3035707..036b3dbeae39d 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.stable2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.stable2021.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/borrowck-errors.rs:15:10 + --> $DIR/borrowck-errors.rs:17:10 | LL | let [&x] = &[&mut 0]; | ^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -15,7 +15,7 @@ LL + let [x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/borrowck-errors.rs:22:10 + --> $DIR/borrowck-errors.rs:24:10 | LL | let [&x] = &mut [&mut 0]; | ^^ ------------- this expression has type `&mut [&mut {integer}; 1]` @@ -31,7 +31,7 @@ LL + let [x] = &mut [&mut 0]; | error[E0507]: cannot move out of a shared reference - --> $DIR/borrowck-errors.rs:31:29 + --> $DIR/borrowck-errors.rs:33:29 | LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { | - ^^^^^^^^^^^^^^^^^^^ @@ -46,19 +46,19 @@ LL + if let Some(Some(x)) = Some(&Some(&mut 0)) { | error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:36:10 + --> $DIR/borrowck-errors.rs:38:10 | LL | let &ref mut x = &0; | ^^^^^^^^^ cannot borrow as mutable error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:41:23 + --> $DIR/borrowck-errors.rs:43:23 | LL | if let &Some(Some(x)) = &Some(&mut Some(0)) { | ^ cannot borrow as mutable error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:46:11 + --> $DIR/borrowck-errors.rs:48:11 | LL | let &[x] = &&mut [0]; | ^ cannot borrow as mutable diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2021.stderr index 208f6c8bbed03..e1764fa1d55da 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2021.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of a shared reference - --> $DIR/borrowck-errors.rs:31:29 + --> $DIR/borrowck-errors.rs:33:29 | LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { | - ^^^^^^^^^^^^^^^^^^^ @@ -14,7 +14,7 @@ LL + if let Some(Some(x)) = Some(&Some(&mut 0)) { | error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:36:10 + --> $DIR/borrowck-errors.rs:38:10 | LL | let &ref mut x = &0; | ^^^^^^^^^ cannot borrow as mutable diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr index 208f6c8bbed03..e1764fa1d55da 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of a shared reference - --> $DIR/borrowck-errors.rs:31:29 + --> $DIR/borrowck-errors.rs:33:29 | LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { | - ^^^^^^^^^^^^^^^^^^^ @@ -14,7 +14,7 @@ LL + if let Some(Some(x)) = Some(&Some(&mut 0)) { | error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:36:10 + --> $DIR/borrowck-errors.rs:38:10 | LL | let &ref mut x = &0; | ^^^^^^^^^ cannot borrow as mutable diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr index 6ddced3d16812..97c74b5c44802 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr @@ -1,5 +1,5 @@ error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/mut-ref-mut.rs:18:13 + --> $DIR/mut-ref-mut.rs:20:13 | LL | let Foo(mut a) = &Foo(0); | ^^^^ @@ -9,7 +9,7 @@ LL | let Foo(mut a) = &Foo(0); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/mut-ref-mut.rs:23:13 + --> $DIR/mut-ref-mut.rs:25:13 | LL | let Foo(mut a) = &mut Foo(0); | ^^^^ @@ -19,7 +19,7 @@ LL | let Foo(mut a) = &mut Foo(0); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0308]: mismatched types - --> $DIR/mut-ref-mut.rs:28:10 + --> $DIR/mut-ref-mut.rs:30:10 | LL | let [&mut mut x] = &[&mut 0]; | ^^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs index c8e988ad76d97..814c7d90c8ccc 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs @@ -7,6 +7,8 @@ //@[stable2021] run-pass //@[classic2021] run-pass //@[structural2021] run-pass +//@ dont-require-annotations: NOTE + //! Test diagnostics for binding with `mut` when the default binding mode is by-ref. #![allow(incomplete_features, unused_assignments, unused_variables)] #![cfg_attr(any(classic2021, classic2024), feature(ref_pat_eat_one_layer_2024))] @@ -27,7 +29,7 @@ pub fn main() { let [&mut mut x] = &[&mut 0]; //[classic2024]~^ ERROR: mismatched types - //[classic2024]~| cannot match inherited `&` with `&mut` pattern - //[structural2024]~^^^ binding cannot be both mutable and by-reference + //[classic2024]~| NOTE cannot match inherited `&` with `&mut` pattern + //[structural2024]~^^^ ERROR binding cannot be both mutable and by-reference #[cfg(any(stable2021, classic2021, structural2021))] { x = 0 } } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr index c0c0f966b6804..a3e8f2af6e912 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr @@ -1,5 +1,5 @@ error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/mut-ref-mut.rs:18:13 + --> $DIR/mut-ref-mut.rs:20:13 | LL | let Foo(mut a) = &Foo(0); | ^^^^ @@ -9,7 +9,7 @@ LL | let Foo(mut a) = &Foo(0); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/mut-ref-mut.rs:23:13 + --> $DIR/mut-ref-mut.rs:25:13 | LL | let Foo(mut a) = &mut Foo(0); | ^^^^ @@ -19,7 +19,7 @@ LL | let Foo(mut a) = &mut Foo(0); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/mut-ref-mut.rs:28:15 + --> $DIR/mut-ref-mut.rs:30:15 | LL | let [&mut mut x] = &[&mut 0]; | ^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2021.stderr index a856a0eaf2a7c..208873c1bab42 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2021.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:20:27 + --> $DIR/pattern-errors.rs:22:27 | LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` @@ -15,7 +15,7 @@ LL + if let Some(&mut Some(x)) = &Some(&mut Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:33:17 + --> $DIR/pattern-errors.rs:35:17 | LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` @@ -26,7 +26,7 @@ LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:38:23 + --> $DIR/pattern-errors.rs:40:23 | LL | if let Some(&Some(&mut x)) = &Some(&mut Some(0)) { | ^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` @@ -36,7 +36,7 @@ LL | if let Some(&Some(&mut x)) = &Some(&mut Some(0)) { = note: expected type `{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-errors.rs:38:23 + --> $DIR/pattern-errors.rs:40:23 | LL | if let Some(&Some(&mut x)) = &Some(&mut Some(0)) { | ^^^^^^ @@ -47,7 +47,7 @@ LL + if let Some(&Some(x)) = &Some(&mut Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:45:23 + --> $DIR/pattern-errors.rs:47:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` @@ -58,7 +58,7 @@ LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:56:17 + --> $DIR/pattern-errors.rs:58:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^ @@ -71,7 +71,7 @@ LL + if let Some(&Some(x)) = &Some(Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:114:11 + --> $DIR/pattern-errors.rs:116:11 | LL | let [&&mut x] = &[&mut 0]; | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -87,7 +87,7 @@ LL + let [&x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:121:11 + --> $DIR/pattern-errors.rs:123:11 | LL | let [&&mut x] = &mut [&mut 0]; | ^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` @@ -103,7 +103,7 @@ LL + let [&x] = &mut [&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:128:11 + --> $DIR/pattern-errors.rs:130:11 | LL | let [&&mut ref x] = &[&mut 0]; | ^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -119,7 +119,7 @@ LL + let [&ref x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:135:11 + --> $DIR/pattern-errors.rs:137:11 | LL | let [&&mut ref x] = &mut [&mut 0]; | ^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` @@ -135,7 +135,7 @@ LL + let [&ref x] = &mut [&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:142:11 + --> $DIR/pattern-errors.rs:144:11 | LL | let [&&mut mut x] = &[&mut 0]; | ^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -151,7 +151,7 @@ LL + let [&mut x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:149:11 + --> $DIR/pattern-errors.rs:151:11 | LL | let [&&mut mut x] = &mut [&mut 0]; | ^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` @@ -167,7 +167,7 @@ LL + let [&mut x] = &mut [&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:164:15 + --> $DIR/pattern-errors.rs:166:15 | LL | let [&mut &x] = &[&mut 0]; | ^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -183,7 +183,7 @@ LL + let [&mut x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:170:15 + --> $DIR/pattern-errors.rs:172:15 | LL | let [&mut &ref x] = &[&mut 0]; | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -199,7 +199,7 @@ LL + let [&mut ref x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:176:15 + --> $DIR/pattern-errors.rs:178:15 | LL | let [&mut &(mut x)] = &[&mut 0]; | ^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr index 90510d23e6614..4f778e04ecebb 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:14:17 + --> $DIR/pattern-errors.rs:16:17 | LL | if let Some(&mut x) = &Some(&mut 0) { | ^^^^^ @@ -12,7 +12,7 @@ LL + if let Some(&x) = &Some(&mut 0) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:20:17 + --> $DIR/pattern-errors.rs:22:17 | LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { | ^^^^^ @@ -25,7 +25,7 @@ LL + if let Some(&Some(&x)) = &Some(&mut Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:26:22 + --> $DIR/pattern-errors.rs:28:22 | LL | if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { | ^^^^^ @@ -38,7 +38,7 @@ LL + if let Some(Some(&x)) = &Some(Some(&mut 0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:33:17 + --> $DIR/pattern-errors.rs:35:17 | LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { | ^^^^^ @@ -51,7 +51,7 @@ LL + if let Some(&Some(&_)) = &Some(&Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:45:23 + --> $DIR/pattern-errors.rs:47:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^ @@ -64,7 +64,7 @@ LL + if let Some(&Some(&_)) = &mut Some(&Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:56:17 + --> $DIR/pattern-errors.rs:58:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^ @@ -77,7 +77,7 @@ LL + if let Some(&Some(x)) = &Some(Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:158:10 + --> $DIR/pattern-errors.rs:160:10 | LL | let [&mut x] = &[&mut 0]; | ^^^^^ @@ -90,7 +90,7 @@ LL + let [&x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:164:10 + --> $DIR/pattern-errors.rs:166:10 | LL | let [&mut &x] = &[&mut 0]; | ^^^^^ @@ -103,7 +103,7 @@ LL + let [&&x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:170:10 + --> $DIR/pattern-errors.rs:172:10 | LL | let [&mut &ref x] = &[&mut 0]; | ^^^^^ @@ -116,7 +116,7 @@ LL + let [&&ref x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:176:10 + --> $DIR/pattern-errors.rs:178:10 | LL | let [&mut &(mut x)] = &[&mut 0]; | ^^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs index 5e677445644a6..70a5bde5e89fc 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs @@ -4,6 +4,8 @@ //@[structural2021] edition: 2021 //@[classic2024] edition: 2024 //@[structural2024] edition: 2024 +//@ dont-require-annotations: NOTE + //! Test cases for poorly-typed patterns in edition 2024 which are caught by HIR typeck. These must //! be separate from cases caught by MIR borrowck or the latter errors may not be emitted. #![allow(incomplete_features)] @@ -13,88 +15,88 @@ pub fn main() { if let Some(&mut x) = &Some(&mut 0) { //[classic2024]~^ ERROR: mismatched types - //[classic2024]~| cannot match inherited `&` with `&mut` pattern + //[classic2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(any(stable2021, classic2021, structural2021))] let _: u32 = x; #[cfg(structural2024)] let _: &u32 = x; } if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { //[stable2021,classic2021,structural2021,classic2024]~^ ERROR: mismatched types - //[stable2021,classic2021,structural2021]~| expected integer, found `&_` - //[classic2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021,classic2021,structural2021]~| NOTE expected integer, found `&_` + //[classic2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(structural2024)] let _: u32 = x; } if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { //[classic2024]~^ ERROR: mismatched types - //[classic2024]~| cannot match inherited `&` with `&mut` pattern + //[classic2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(any(stable2021, classic2021, structural2021))] let _: u32 = x; #[cfg(structural2024)] let _: &u32 = x; } if let Some(&mut Some(&_)) = &Some(&Some(0)) { //~^ ERROR: mismatched types - //[stable2021,classic2021,structural2021]~| types differ in mutability - //[classic2024,structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021,classic2021,structural2021]~| NOTE types differ in mutability + //[classic2024,structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern } if let Some(&Some(&mut x)) = &Some(&mut Some(0)) { //[stable2021,classic2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| types differ in mutability - //[classic2021,structural2021]~| expected integer, found `&mut _` - //[structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE types differ in mutability + //[classic2021,structural2021]~| NOTE expected integer, found `&mut _` + //[structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(classic2024)] let _: u32 = x; } if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { //~^ ERROR: mismatched types - //[stable2021,classic2021,structural2021]~| expected integer, found `&mut _` - //[classic2024,structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021,classic2021,structural2021]~| NOTE expected integer, found `&mut _` + //[classic2024,structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern } if let Some(&Some(Some(&mut x))) = &Some(Some(&mut Some(0))) { //[stable2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| expected `Option<&mut Option<{integer}>>`, found `&_` - //[structural2021,structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE expected `Option<&mut Option<{integer}>>`, found `&_` + //[structural2021,structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(any(classic2021, classic2024))] let _: u32 = x; } if let Some(&mut Some(x)) = &Some(Some(0)) { //~^ ERROR: mismatched types - //[stable2021]~| expected `Option<{integer}>`, found `&mut _` - //[classic2021,structural2021,classic2024,structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE expected `Option<{integer}>`, found `&mut _` + //[classic2021,structural2021,classic2024,structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern } } fn structural_errors_0() { let &[&mut x] = &&mut [0]; //[stable2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| expected integer, found `&mut _` - //[structural2021,structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE expected integer, found `&mut _` + //[structural2021,structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(any(classic2021, classic2024))] let _: u32 = x; let &[&mut x] = &mut &mut [0]; //[stable2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| types differ in mutability - //[structural2021,structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE types differ in mutability + //[structural2021,structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(any(classic2021, classic2024))] let _: u32 = x; let &[&mut ref x] = &&mut [0]; //[stable2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| expected integer, found `&mut _` - //[structural2021,structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE expected integer, found `&mut _` + //[structural2021,structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(any(classic2021, classic2024))] let _: &u32 = x; let &[&mut ref x] = &mut &mut [0]; //[stable2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| types differ in mutability - //[structural2021,structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE types differ in mutability + //[structural2021,structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(any(classic2021, classic2024))] let _: &u32 = x; let &[&mut mut x] = &&mut [0]; //[stable2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| expected integer, found `&mut _` - //[structural2021,structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE expected integer, found `&mut _` + //[structural2021,structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(any(classic2021, classic2024))] let _: u32 = x; let &[&mut mut x] = &mut &mut [0]; //[stable2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| types differ in mutability - //[structural2021,structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE types differ in mutability + //[structural2021,structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(any(classic2021, classic2024))] let _: u32 = x; } @@ -113,69 +115,69 @@ fn structural_errors_1() { fn structural_errors_2() { let [&&mut x] = &[&mut 0]; //[stable2021,classic2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| types differ in mutability - //[classic2021,structural2021] expected integer, found `&mut _` - //[structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE types differ in mutability + //[classic2021,structural2021] NOTE expected integer, found `&mut _` + //[structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(classic2024)] let _: u32 = x; let [&&mut x] = &mut [&mut 0]; //[stable2021,classic2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| types differ in mutability - //[classic2021,structural2021] expected integer, found `&mut _` - //[structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE types differ in mutability + //[classic2021,structural2021] NOTE expected integer, found `&mut _` + //[structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(classic2024)] let _: u32 = x; let [&&mut ref x] = &[&mut 0]; //[stable2021,classic2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| types differ in mutability - //[classic2021,structural2021] expected integer, found `&mut _` - //[structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE types differ in mutability + //[classic2021,structural2021] NOTE expected integer, found `&mut _` + //[structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(classic2024)] let _: &u32 = x; let [&&mut ref x] = &mut [&mut 0]; //[stable2021,classic2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| types differ in mutability - //[classic2021,structural2021] expected integer, found `&mut _` - //[structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE types differ in mutability + //[classic2021,structural2021] NOTE expected integer, found `&mut _` + //[structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(classic2024)] let _: &u32 = x; let [&&mut mut x] = &[&mut 0]; //[stable2021,classic2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| types differ in mutability - //[classic2021,structural2021] expected integer, found `&mut _` - //[structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE types differ in mutability + //[classic2021,structural2021] NOTE expected integer, found `&mut _` + //[structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(classic2024)] let _: u32 = x; let [&&mut mut x] = &mut [&mut 0]; //[stable2021,classic2021,structural2021,structural2024]~^ ERROR: mismatched types - //[stable2021]~| types differ in mutability - //[classic2021,structural2021] expected integer, found `&mut _` - //[structural2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE types differ in mutability + //[classic2021,structural2021] NOTE expected integer, found `&mut _` + //[structural2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(classic2024)] let _: u32 = x; } fn classic_errors_0() { let [&mut x] = &[&mut 0]; //[classic2024]~^ ERROR: mismatched types - //[classic2024]~| cannot match inherited `&` with `&mut` pattern + //[classic2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(any(stable2021, classic2021, structural2021))] let _: u32 = x; #[cfg(structural2024)] let _: &u32 = x; let [&mut &x] = &[&mut 0]; //[stable2021,classic2021,structural2021,classic2024]~^ ERROR: mismatched types - //[stable2021]~| expected integer, found `&_` - //[classic2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE expected integer, found `&_` + //[classic2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(structural2024)] let _: u32 = x; let [&mut &ref x] = &[&mut 0]; //[stable2021,classic2021,structural2021,classic2024]~^ ERROR: mismatched types - //[stable2021]~| expected integer, found `&_` - //[classic2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE expected integer, found `&_` + //[classic2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(structural2024)] let _: &u32 = x; let [&mut &(mut x)] = &[&mut 0]; //[stable2021,classic2021,structural2021,classic2024]~^ ERROR: mismatched types - //[stable2021]~| expected integer, found `&_` - //[classic2024]~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| NOTE expected integer, found `&_` + //[classic2024]~| NOTE cannot match inherited `&` with `&mut` pattern #[cfg(structural2024)] let _: u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.stable2021.stderr index 76e6d2f562a27..8b32e4f7c0233 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.stable2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.stable2021.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:20:27 + --> $DIR/pattern-errors.rs:22:27 | LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` @@ -15,7 +15,7 @@ LL + if let Some(&mut Some(x)) = &Some(&mut Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:33:17 + --> $DIR/pattern-errors.rs:35:17 | LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` @@ -26,7 +26,7 @@ LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:38:17 + --> $DIR/pattern-errors.rs:40:17 | LL | if let Some(&Some(&mut x)) = &Some(&mut Some(0)) { | ^^^^^^^^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` @@ -37,7 +37,7 @@ LL | if let Some(&Some(&mut x)) = &Some(&mut Some(0)) { found reference `&_` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:45:23 + --> $DIR/pattern-errors.rs:47:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` @@ -48,7 +48,7 @@ LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:50:17 + --> $DIR/pattern-errors.rs:52:17 | LL | if let Some(&Some(Some(&mut x))) = &Some(Some(&mut Some(0))) { | ^^^^^^^^^^^^^^^^^^^ ------------------------- this expression has type `&Option>>` @@ -59,7 +59,7 @@ LL | if let Some(&Some(Some(&mut x))) = &Some(Some(&mut Some(0))) { found reference `&_` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:56:17 + --> $DIR/pattern-errors.rs:58:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` @@ -70,7 +70,7 @@ LL | if let Some(&mut Some(x)) = &Some(Some(0)) { found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:64:11 + --> $DIR/pattern-errors.rs:66:11 | LL | let &[&mut x] = &&mut [0]; | ^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` @@ -80,7 +80,7 @@ LL | let &[&mut x] = &&mut [0]; = note: expected type `{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-errors.rs:64:11 + --> $DIR/pattern-errors.rs:66:11 | LL | let &[&mut x] = &&mut [0]; | ^^^^^^ @@ -91,7 +91,7 @@ LL + let &[x] = &&mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:70:9 + --> $DIR/pattern-errors.rs:72:9 | LL | let &[&mut x] = &mut &mut [0]; | ^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` @@ -102,7 +102,7 @@ LL | let &[&mut x] = &mut &mut [0]; found reference `&_` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:76:11 + --> $DIR/pattern-errors.rs:78:11 | LL | let &[&mut ref x] = &&mut [0]; | ^^^^^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` @@ -112,7 +112,7 @@ LL | let &[&mut ref x] = &&mut [0]; = note: expected type `{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-errors.rs:76:11 + --> $DIR/pattern-errors.rs:78:11 | LL | let &[&mut ref x] = &&mut [0]; | ^^^^^^^^^^ @@ -123,7 +123,7 @@ LL + let &[ref x] = &&mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:82:9 + --> $DIR/pattern-errors.rs:84:9 | LL | let &[&mut ref x] = &mut &mut [0]; | ^^^^^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` @@ -134,7 +134,7 @@ LL | let &[&mut ref x] = &mut &mut [0]; found reference `&_` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:88:11 + --> $DIR/pattern-errors.rs:90:11 | LL | let &[&mut mut x] = &&mut [0]; | ^^^^^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` @@ -144,7 +144,7 @@ LL | let &[&mut mut x] = &&mut [0]; = note: expected type `{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-errors.rs:88:11 + --> $DIR/pattern-errors.rs:90:11 | LL | let &[&mut mut x] = &&mut [0]; | ^^^^^^^^^^ @@ -155,7 +155,7 @@ LL + let &[mut x] = &&mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:94:9 + --> $DIR/pattern-errors.rs:96:9 | LL | let &[&mut mut x] = &mut &mut [0]; | ^^^^^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` @@ -166,7 +166,7 @@ LL | let &[&mut mut x] = &mut &mut [0]; found reference `&_` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:114:10 + --> $DIR/pattern-errors.rs:116:10 | LL | let [&&mut x] = &[&mut 0]; | ^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -177,7 +177,7 @@ LL | let [&&mut x] = &[&mut 0]; found reference `&_` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:121:10 + --> $DIR/pattern-errors.rs:123:10 | LL | let [&&mut x] = &mut [&mut 0]; | ^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` @@ -188,7 +188,7 @@ LL | let [&&mut x] = &mut [&mut 0]; found reference `&_` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:128:10 + --> $DIR/pattern-errors.rs:130:10 | LL | let [&&mut ref x] = &[&mut 0]; | ^^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -199,7 +199,7 @@ LL | let [&&mut ref x] = &[&mut 0]; found reference `&_` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:135:10 + --> $DIR/pattern-errors.rs:137:10 | LL | let [&&mut ref x] = &mut [&mut 0]; | ^^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` @@ -210,7 +210,7 @@ LL | let [&&mut ref x] = &mut [&mut 0]; found reference `&_` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:142:10 + --> $DIR/pattern-errors.rs:144:10 | LL | let [&&mut mut x] = &[&mut 0]; | ^^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -221,7 +221,7 @@ LL | let [&&mut mut x] = &[&mut 0]; found reference `&_` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:149:10 + --> $DIR/pattern-errors.rs:151:10 | LL | let [&&mut mut x] = &mut [&mut 0]; | ^^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` @@ -232,7 +232,7 @@ LL | let [&&mut mut x] = &mut [&mut 0]; found reference `&_` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:164:15 + --> $DIR/pattern-errors.rs:166:15 | LL | let [&mut &x] = &[&mut 0]; | ^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -248,7 +248,7 @@ LL + let [&mut x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:170:15 + --> $DIR/pattern-errors.rs:172:15 | LL | let [&mut &ref x] = &[&mut 0]; | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -264,7 +264,7 @@ LL + let [&mut ref x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:176:15 + --> $DIR/pattern-errors.rs:178:15 | LL | let [&mut &(mut x)] = &[&mut 0]; | ^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2021.stderr index 1ca6bff3f38c3..09cff1f8e92ca 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2021.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:20:27 + --> $DIR/pattern-errors.rs:22:27 | LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` @@ -15,7 +15,7 @@ LL + if let Some(&mut Some(x)) = &Some(&mut Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:33:17 + --> $DIR/pattern-errors.rs:35:17 | LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` @@ -26,7 +26,7 @@ LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:38:23 + --> $DIR/pattern-errors.rs:40:23 | LL | if let Some(&Some(&mut x)) = &Some(&mut Some(0)) { | ^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` @@ -36,7 +36,7 @@ LL | if let Some(&Some(&mut x)) = &Some(&mut Some(0)) { = note: expected type `{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-errors.rs:38:23 + --> $DIR/pattern-errors.rs:40:23 | LL | if let Some(&Some(&mut x)) = &Some(&mut Some(0)) { | ^^^^^^ @@ -47,7 +47,7 @@ LL + if let Some(&Some(x)) = &Some(&mut Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:45:23 + --> $DIR/pattern-errors.rs:47:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` @@ -58,7 +58,7 @@ LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:50:28 + --> $DIR/pattern-errors.rs:52:28 | LL | if let Some(&Some(Some(&mut x))) = &Some(Some(&mut Some(0))) { | ^^^^^ @@ -71,7 +71,7 @@ LL + if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:56:17 + --> $DIR/pattern-errors.rs:58:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^ @@ -84,7 +84,7 @@ LL + if let Some(&Some(x)) = &Some(Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:64:11 + --> $DIR/pattern-errors.rs:66:11 | LL | let &[&mut x] = &&mut [0]; | ^^^^^ @@ -97,7 +97,7 @@ LL + let &[&x] = &&mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:70:11 + --> $DIR/pattern-errors.rs:72:11 | LL | let &[&mut x] = &mut &mut [0]; | ^^^^^ @@ -110,7 +110,7 @@ LL + let &[&x] = &mut &mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:76:11 + --> $DIR/pattern-errors.rs:78:11 | LL | let &[&mut ref x] = &&mut [0]; | ^^^^^ @@ -123,7 +123,7 @@ LL + let &[&ref x] = &&mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:82:11 + --> $DIR/pattern-errors.rs:84:11 | LL | let &[&mut ref x] = &mut &mut [0]; | ^^^^^ @@ -136,7 +136,7 @@ LL + let &[&ref x] = &mut &mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:88:11 + --> $DIR/pattern-errors.rs:90:11 | LL | let &[&mut mut x] = &&mut [0]; | ^^^^^ @@ -149,7 +149,7 @@ LL + let &[&mut x] = &&mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:94:11 + --> $DIR/pattern-errors.rs:96:11 | LL | let &[&mut mut x] = &mut &mut [0]; | ^^^^^ @@ -162,7 +162,7 @@ LL + let &[&mut x] = &mut &mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:114:11 + --> $DIR/pattern-errors.rs:116:11 | LL | let [&&mut x] = &[&mut 0]; | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -178,7 +178,7 @@ LL + let [&x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:121:11 + --> $DIR/pattern-errors.rs:123:11 | LL | let [&&mut x] = &mut [&mut 0]; | ^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` @@ -194,7 +194,7 @@ LL + let [&x] = &mut [&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:128:11 + --> $DIR/pattern-errors.rs:130:11 | LL | let [&&mut ref x] = &[&mut 0]; | ^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -210,7 +210,7 @@ LL + let [&ref x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:135:11 + --> $DIR/pattern-errors.rs:137:11 | LL | let [&&mut ref x] = &mut [&mut 0]; | ^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` @@ -226,7 +226,7 @@ LL + let [&ref x] = &mut [&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:142:11 + --> $DIR/pattern-errors.rs:144:11 | LL | let [&&mut mut x] = &[&mut 0]; | ^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -242,7 +242,7 @@ LL + let [&mut x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:149:11 + --> $DIR/pattern-errors.rs:151:11 | LL | let [&&mut mut x] = &mut [&mut 0]; | ^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` @@ -258,7 +258,7 @@ LL + let [&mut x] = &mut [&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:164:15 + --> $DIR/pattern-errors.rs:166:15 | LL | let [&mut &x] = &[&mut 0]; | ^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -274,7 +274,7 @@ LL + let [&mut x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:170:15 + --> $DIR/pattern-errors.rs:172:15 | LL | let [&mut &ref x] = &[&mut 0]; | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -290,7 +290,7 @@ LL + let [&mut ref x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:176:15 + --> $DIR/pattern-errors.rs:178:15 | LL | let [&mut &(mut x)] = &[&mut 0]; | ^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr index 3658893df9cc1..d3d2c47c29cbb 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:33:17 + --> $DIR/pattern-errors.rs:35:17 | LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { | ^^^^^ @@ -12,7 +12,7 @@ LL + if let Some(&Some(&_)) = &Some(&Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:38:23 + --> $DIR/pattern-errors.rs:40:23 | LL | if let Some(&Some(&mut x)) = &Some(&mut Some(0)) { | ^^^^^ @@ -25,7 +25,7 @@ LL + if let Some(&Some(&x)) = &Some(&mut Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:45:23 + --> $DIR/pattern-errors.rs:47:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^ @@ -38,7 +38,7 @@ LL + if let Some(&Some(&_)) = &mut Some(&Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:50:28 + --> $DIR/pattern-errors.rs:52:28 | LL | if let Some(&Some(Some(&mut x))) = &Some(Some(&mut Some(0))) { | ^^^^^ @@ -51,7 +51,7 @@ LL + if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:56:17 + --> $DIR/pattern-errors.rs:58:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^ @@ -64,7 +64,7 @@ LL + if let Some(&Some(x)) = &Some(Some(0)) { | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:64:11 + --> $DIR/pattern-errors.rs:66:11 | LL | let &[&mut x] = &&mut [0]; | ^^^^^ @@ -77,7 +77,7 @@ LL + let &[&x] = &&mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:70:11 + --> $DIR/pattern-errors.rs:72:11 | LL | let &[&mut x] = &mut &mut [0]; | ^^^^^ @@ -90,7 +90,7 @@ LL + let &[&x] = &mut &mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:76:11 + --> $DIR/pattern-errors.rs:78:11 | LL | let &[&mut ref x] = &&mut [0]; | ^^^^^ @@ -103,7 +103,7 @@ LL + let &[&ref x] = &&mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:82:11 + --> $DIR/pattern-errors.rs:84:11 | LL | let &[&mut ref x] = &mut &mut [0]; | ^^^^^ @@ -116,7 +116,7 @@ LL + let &[&ref x] = &mut &mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:88:11 + --> $DIR/pattern-errors.rs:90:11 | LL | let &[&mut mut x] = &&mut [0]; | ^^^^^ @@ -129,7 +129,7 @@ LL + let &[&mut x] = &&mut [0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:94:11 + --> $DIR/pattern-errors.rs:96:11 | LL | let &[&mut mut x] = &mut &mut [0]; | ^^^^^ @@ -142,7 +142,7 @@ LL + let &[&mut x] = &mut &mut [0]; | error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:102:12 + --> $DIR/pattern-errors.rs:104:12 | LL | let [&(mut x)] = &[&0]; | ^^^^ @@ -152,7 +152,7 @@ LL | let [&(mut x)] = &[&0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:107:12 + --> $DIR/pattern-errors.rs:109:12 | LL | let [&(mut x)] = &mut [&0]; | ^^^^ @@ -162,7 +162,7 @@ LL | let [&(mut x)] = &mut [&0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:114:11 + --> $DIR/pattern-errors.rs:116:11 | LL | let [&&mut x] = &[&mut 0]; | ^^^^^ @@ -175,7 +175,7 @@ LL + let [&&x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:121:11 + --> $DIR/pattern-errors.rs:123:11 | LL | let [&&mut x] = &mut [&mut 0]; | ^^^^^ @@ -188,7 +188,7 @@ LL + let [&&x] = &mut [&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:128:11 + --> $DIR/pattern-errors.rs:130:11 | LL | let [&&mut ref x] = &[&mut 0]; | ^^^^^ @@ -201,7 +201,7 @@ LL + let [&&ref x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:135:11 + --> $DIR/pattern-errors.rs:137:11 | LL | let [&&mut ref x] = &mut [&mut 0]; | ^^^^^ @@ -214,7 +214,7 @@ LL + let [&&ref x] = &mut [&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:142:11 + --> $DIR/pattern-errors.rs:144:11 | LL | let [&&mut mut x] = &[&mut 0]; | ^^^^^ @@ -227,7 +227,7 @@ LL + let [&&mut x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:149:11 + --> $DIR/pattern-errors.rs:151:11 | LL | let [&&mut mut x] = &mut [&mut 0]; | ^^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2021.stderr index 1dda2dca4a48a..dbb8ae87b661c 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2021.stderr @@ -1,5 +1,5 @@ error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/ref-binding-on-inh-ref-errors.rs:71:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:73:10 | LL | let [ref mut x] = &[0]; | ^^^^^^^^^ cannot borrow as mutable diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr index 44cb005a748da..04e53e06a22e6 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/ref-binding-on-inh-ref-errors.rs:58:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:60:10 | LL | let [&mut ref x] = &[&mut 0]; | ^^^^^ @@ -12,14 +12,14 @@ LL + let [&ref x] = &[&mut 0]; | error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:71:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:73:10 | LL | let [ref mut x] = &[0]; | ^^^^^^^ binding modifier not allowed under `ref` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:71:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:73:9 | LL | let [ref mut x] = &[0]; | ^^^^^^^^^^^ this matches on type `&_` @@ -29,20 +29,20 @@ LL | let &[ref mut x] = &[0]; | + error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/ref-binding-on-inh-ref-errors.rs:71:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:73:10 | LL | let [ref mut x] = &[0]; | ^^^^^^^^^ cannot borrow as mutable error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:79:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:81:10 | LL | let [ref x] = &[0]; | ^^^ binding modifier not allowed under `ref` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:79:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:81:9 | LL | let [ref x] = &[0]; | ^^^^^^^ this matches on type `&_` @@ -52,14 +52,14 @@ LL | let &[ref x] = &[0]; | + error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:83:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:85:10 | LL | let [ref x] = &mut [0]; | ^^^ binding modifier not allowed under `ref mut` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:83:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:85:9 | LL | let [ref x] = &mut [0]; | ^^^^^^^ this matches on type `&mut _` @@ -69,14 +69,14 @@ LL | let &mut [ref x] = &mut [0]; | ++++ error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:87:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:89:10 | LL | let [ref mut x] = &mut [0]; | ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:87:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:89:9 | LL | let [ref mut x] = &mut [0]; | ^^^^^^^^^^^ this matches on type `&mut _` diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs index ea6f028fe4b8b..c9e3f75cf178a 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs @@ -4,6 +4,8 @@ //@[structural2021] edition: 2021 //@[classic2024] edition: 2024 //@[structural2024] edition: 2024 +//@ dont-require-annotations: NOTE + //! Tests for errors from binding with `ref x` under a by-ref default binding mode in edition 2024. //! These can't be in the same body as tests for other errors, since they're emitted during THIR //! construction. The errors on stable edition 2021 Rust are unrelated. @@ -40,14 +42,14 @@ fn errors_from_eating_the_real_reference() { fn errors_from_eating_the_real_reference_caught_in_hir_typeck_on_stable() { let [&ref x] = &[&mut 0]; //[stable2021]~^ ERROR: mismatched types - //[stable2021]~| types differ in mutability + //[stable2021]~| NOTE types differ in mutability //[structural2024]~^^^ ERROR: binding modifiers may only be written when the default binding mode is `move` #[cfg(any(classic2021, structural2021))] let _: &u32 = x; #[cfg(classic2024)] let _: &&mut u32 = x; let [&ref x] = &mut [&mut 0]; //[stable2021]~^ ERROR: mismatched types - //[stable2021]~| types differ in mutability + //[stable2021]~| NOTE types differ in mutability //[structural2024]~^^^ ERROR: binding modifiers may only be written when the default binding mode is `move` #[cfg(any(classic2021, structural2021))] let _: &u32 = x; #[cfg(classic2024)] let _: &&mut u32 = x; @@ -57,7 +59,7 @@ fn errors_from_eating_the_real_reference_caught_in_hir_typeck_on_stable() { fn errors_dependent_on_eating_order_caught_in_hir_typeck_when_eating_outer() { let [&mut ref x] = &[&mut 0]; //[classic2024]~^ ERROR: mismatched types - //[classic2024]~| cannot match inherited `&` with `&mut` pattern + //[classic2024]~| NOTE cannot match inherited `&` with `&mut` pattern //[structural2024]~^^^ ERROR: binding modifiers may only be written when the default binding mode is `move` #[cfg(any(stable2021, classic2021, structural2021))] let _: &u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr index 2ec6650dd7d0b..33119c4447ada 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/ref-binding-on-inh-ref-errors.rs:41:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:43:10 | LL | let [&ref x] = &[&mut 0]; | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -15,7 +15,7 @@ LL + let [ref x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/ref-binding-on-inh-ref-errors.rs:48:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:50:10 | LL | let [&ref x] = &mut [&mut 0]; | ^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` @@ -31,7 +31,7 @@ LL + let [ref x] = &mut [&mut 0]; | error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/ref-binding-on-inh-ref-errors.rs:71:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:73:10 | LL | let [ref mut x] = &[0]; | ^^^^^^^^^ cannot borrow as mutable diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2021.stderr index 1dda2dca4a48a..dbb8ae87b661c 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2021.stderr @@ -1,5 +1,5 @@ error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/ref-binding-on-inh-ref-errors.rs:71:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:73:10 | LL | let [ref mut x] = &[0]; | ^^^^^^^^^ cannot borrow as mutable diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr index 6f62ad06cc493..def6deb325acd 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr @@ -1,12 +1,12 @@ error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:17:11 + --> $DIR/ref-binding-on-inh-ref-errors.rs:19:11 | LL | let [&ref x] = &[&0]; | ^^^ binding modifier not allowed under `ref` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:17:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:19:9 | LL | let [&ref x] = &[&0]; | ^^^^^^^^ this matches on type `&_` @@ -16,14 +16,14 @@ LL | let &[&ref x] = &[&0]; | + error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:22:11 + --> $DIR/ref-binding-on-inh-ref-errors.rs:24:11 | LL | let [&ref x] = &mut [&0]; | ^^^ binding modifier not allowed under `ref` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:22:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:24:9 | LL | let [&ref x] = &mut [&0]; | ^^^^^^^^ this matches on type `&mut _` @@ -33,14 +33,14 @@ LL | let &mut [&ref x] = &mut [&0]; | ++++ error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:27:15 + --> $DIR/ref-binding-on-inh-ref-errors.rs:29:15 | LL | let [&mut ref x] = &mut [&mut 0]; | ^^^ binding modifier not allowed under `ref mut` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:27:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:29:9 | LL | let [&mut ref x] = &mut [&mut 0]; | ^^^^^^^^^^^^ this matches on type `&mut _` @@ -50,14 +50,14 @@ LL | let &mut [&mut ref x] = &mut [&mut 0]; | ++++ error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:32:15 + --> $DIR/ref-binding-on-inh-ref-errors.rs:34:15 | LL | let [&mut ref mut x] = &mut [&mut 0]; | ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:32:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:34:9 | LL | let [&mut ref mut x] = &mut [&mut 0]; | ^^^^^^^^^^^^^^^^ this matches on type `&mut _` @@ -67,14 +67,14 @@ LL | let &mut [&mut ref mut x] = &mut [&mut 0]; | ++++ error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:41:11 + --> $DIR/ref-binding-on-inh-ref-errors.rs:43:11 | LL | let [&ref x] = &[&mut 0]; | ^^^ binding modifier not allowed under `ref` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:41:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:43:9 | LL | let [&ref x] = &[&mut 0]; | ^^^^^^^^ this matches on type `&_` @@ -84,14 +84,14 @@ LL | let &[&ref x] = &[&mut 0]; | + error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:48:11 + --> $DIR/ref-binding-on-inh-ref-errors.rs:50:11 | LL | let [&ref x] = &mut [&mut 0]; | ^^^ binding modifier not allowed under `ref` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:48:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:50:9 | LL | let [&ref x] = &mut [&mut 0]; | ^^^^^^^^ this matches on type `&mut _` @@ -101,14 +101,14 @@ LL | let &mut [&ref x] = &mut [&mut 0]; | ++++ error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:58:15 + --> $DIR/ref-binding-on-inh-ref-errors.rs:60:15 | LL | let [&mut ref x] = &[&mut 0]; | ^^^ binding modifier not allowed under `ref` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:58:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:60:9 | LL | let [&mut ref x] = &[&mut 0]; | ^^^^^^^^^^^^ this matches on type `&_` @@ -118,14 +118,14 @@ LL | let &[&mut ref x] = &[&mut 0]; | + error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:71:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:73:10 | LL | let [ref mut x] = &[0]; | ^^^^^^^ binding modifier not allowed under `ref` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:71:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:73:9 | LL | let [ref mut x] = &[0]; | ^^^^^^^^^^^ this matches on type `&_` @@ -135,20 +135,20 @@ LL | let &[ref mut x] = &[0]; | + error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/ref-binding-on-inh-ref-errors.rs:71:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:73:10 | LL | let [ref mut x] = &[0]; | ^^^^^^^^^ cannot borrow as mutable error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:79:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:81:10 | LL | let [ref x] = &[0]; | ^^^ binding modifier not allowed under `ref` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:79:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:81:9 | LL | let [ref x] = &[0]; | ^^^^^^^ this matches on type `&_` @@ -158,14 +158,14 @@ LL | let &[ref x] = &[0]; | + error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:83:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:85:10 | LL | let [ref x] = &mut [0]; | ^^^ binding modifier not allowed under `ref mut` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:83:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:85:9 | LL | let [ref x] = &mut [0]; | ^^^^^^^ this matches on type `&mut _` @@ -175,14 +175,14 @@ LL | let &mut [ref x] = &mut [0]; | ++++ error: binding modifiers may only be written when the default binding mode is `move` - --> $DIR/ref-binding-on-inh-ref-errors.rs:87:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:89:10 | LL | let [ref mut x] = &mut [0]; | ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode | = note: for more information, see note: matching on a reference type with a non-reference pattern changes the default binding mode - --> $DIR/ref-binding-on-inh-ref-errors.rs:87:9 + --> $DIR/ref-binding-on-inh-ref-errors.rs:89:9 | LL | let [ref mut x] = &mut [0]; | ^^^^^^^^^^^ this matches on type `&mut _` diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.classic2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.classic2021.stderr index f8c2bd9a92128..9bf5c9544e51c 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.classic2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.classic2021.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:32:23 + --> $DIR/well-typed-edition-2024.rs:34:23 | LL | if let Some(Some(&&x)) = &Some(Some(&0)) { | ^^ --------------- this expression has type `&Option>` @@ -15,7 +15,7 @@ LL + if let Some(Some(&x)) = &Some(Some(&0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:63:23 + --> $DIR/well-typed-edition-2024.rs:65:23 | LL | if let Some(&Some(&x)) = &Some(&Some(0)) { | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` @@ -31,7 +31,7 @@ LL + if let Some(&Some(x)) = &Some(&Some(0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:82:23 + --> $DIR/well-typed-edition-2024.rs:84:23 | LL | if let Some(&Some(&x)) = &Some(&mut Some(0)) { | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` @@ -47,7 +47,7 @@ LL + if let Some(&Some(x)) = &Some(&mut Some(0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:88:23 + --> $DIR/well-typed-edition-2024.rs:90:23 | LL | if let Some(&Some(&x)) = &mut Some(&Some(0)) { | ^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` @@ -63,7 +63,7 @@ LL + if let Some(&Some(x)) = &mut Some(&Some(0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:123:15 + --> $DIR/well-typed-edition-2024.rs:125:15 | LL | let [&mut &x] = &mut [&0]; | ^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -79,7 +79,7 @@ LL + let [&mut x] = &mut [&0]; | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:129:15 + --> $DIR/well-typed-edition-2024.rs:131:15 | LL | let [&mut &ref x] = &mut [&0]; | ^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -95,7 +95,7 @@ LL + let [&mut ref x] = &mut [&0]; | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:135:15 + --> $DIR/well-typed-edition-2024.rs:137:15 | LL | let [&mut &(mut x)] = &mut [&0]; | ^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -111,7 +111,7 @@ LL + let [&mut mut x)] = &mut [&0]; | error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/well-typed-edition-2024.rs:109:19 + --> $DIR/well-typed-edition-2024.rs:111:19 | LL | let [&mut ref mut x] = &mut [&0]; | ^^^^^^^^^ cannot borrow as mutable diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs index 62c1c28022b7d..e2913e0f60a8a 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs @@ -6,6 +6,8 @@ //@[structural2024] edition: 2024 //@[classic2024] run-pass //@[structural2024] run-pass +//@ dont-require-annotations: NOTE + //! Test cases for well-typed patterns in edition 2024. These are in their own file to ensure we //! pass both HIR typeck and MIR borrowck, as we may skip the latter if grouped with failing tests. #![allow(incomplete_features, unused_mut)] @@ -30,111 +32,111 @@ pub fn main() { #[cfg(any(classic2024, structural2024))] let _: &u32 = x; } if let Some(Some(&&x)) = &Some(Some(&0)) { - //[stable2021,classic2021,structural2021]~^ mismatched types - //[stable2021,classic2021,structural2021]~| expected integer, found `&_` + //[stable2021,classic2021,structural2021]~^ ERROR mismatched types + //[stable2021,classic2021,structural2021]~| NOTE expected integer, found `&_` #[cfg(any(classic2024, structural2024))] let _: u32 = x; } // Tests for eating a lone inherited reference if let Some(Some(&x)) = &Some(&Some(0)) { - //[stable2021]~^ mismatched types - //[stable2021]~| expected integer, found `&_` + //[stable2021]~^ ERROR mismatched types + //[stable2021]~| NOTE expected integer, found `&_` #[cfg(any(classic2021, structural2021, classic2024, structural2024))] let _: u32 = x; } if let Some(&Some(x)) = &Some(Some(0)) { - //[stable2021]~^ mismatched types - //[stable2021]~| expected `Option<{integer}>`, found `&_` + //[stable2021]~^ ERROR mismatched types + //[stable2021]~| NOTE expected `Option<{integer}>`, found `&_` #[cfg(any(classic2021, structural2021, classic2024, structural2024))] let _: u32 = x; } if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { - //[stable2021]~^ mismatched types - //[stable2021]~| expected integer, found `&mut _` + //[stable2021]~^ ERROR mismatched types + //[stable2021]~| NOTE expected integer, found `&mut _` #[cfg(any(classic2021, structural2021, classic2024, structural2024))] let _: u32 = x; } // Tests for `&` patterns matching real `&mut` reference types if let Some(&Some(&x)) = Some(&Some(&mut 0)) { - //[stable2021]~^ mismatched types - //[stable2021]~| types differ in mutability + //[stable2021]~^ ERROR mismatched types + //[stable2021]~| NOTE types differ in mutability #[cfg(any(classic2021, structural2021, classic2024, structural2024))] let _: u32 = x; } // Tests for eating only one layer and also eating a lone inherited reference if let Some(&Some(&x)) = &Some(&Some(0)) { - //[stable2021,classic2021,structural2021]~^ mismatched types - //[stable2021,classic2021,structural2021]~| expected integer, found `&_` + //[stable2021,classic2021,structural2021]~^ ERROR mismatched types + //[stable2021,classic2021,structural2021]~| NOTE expected integer, found `&_` #[cfg(any(classic2024, structural2024))] let _: u32 = x; } // Tests for `&` matching a lone inherited possibly-`&mut` reference if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { - //[stable2021]~^ mismatched types - //[stable2021]~| expected `Option<&mut Option<{integer}>>`, found `&_` + //[stable2021]~^ ERROR mismatched types + //[stable2021]~| NOTE expected `Option<&mut Option<{integer}>>`, found `&_` #[cfg(any(classic2021, structural2021, classic2024, structural2024))] let _: u32 = x; } if let Some(&Some(x)) = &mut Some(Some(0)) { - //[stable2021]~^ mismatched types - //[stable2021]~| expected `Option<{integer}>`, found `&_` + //[stable2021]~^ ERROR mismatched types + //[stable2021]~| NOTE expected `Option<{integer}>`, found `&_` #[cfg(any(classic2021, structural2021, classic2024, structural2024))] let _: u32 = x; } // Tests eating one layer, eating a lone inherited ref, and `&` eating `&mut` (realness varies) if let Some(&Some(&x)) = &Some(&mut Some(0)) { - //[stable2021,classic2021,structural2021]~^ mismatched types - //[stable2021]~| types differ in mutability - //[classic2021,structural2021]~| expected integer, found `&_` + //[stable2021,classic2021,structural2021]~^ ERROR mismatched types + //[stable2021]~| NOTE types differ in mutability + //[classic2021,structural2021]~| NOTE expected integer, found `&_` #[cfg(any(classic2024, structural2024))] let _: u32 = x; } if let Some(&Some(&x)) = &mut Some(&Some(0)) { - //[stable2021,classic2021,structural2021]~^ mismatched types - //[stable2021,classic2021,structural2021]~| expected integer, found `&_` + //[stable2021,classic2021,structural2021]~^ ERROR mismatched types + //[stable2021,classic2021,structural2021]~| NOTE expected integer, found `&_` #[cfg(any(classic2024, structural2024))] let _: u32 = x; } // Tests for eat-inner and eat-both rulesets matching on the outer reference if matching on the // inner reference causes a mutability mismatch. i.e. tests for "fallback-to-outer" deref rules. let [&mut x] = &mut [&0]; - //[stable2021]~^ mismatched types - //[stable2021]~| types differ in mutability + //[stable2021]~^ ERROR mismatched types + //[stable2021]~| NOTE types differ in mutability #[cfg(any(classic2021, structural2021))] let _: u32 = x; #[cfg(any(classic2024, structural2024))] let _: &u32 = x; let [&mut ref x] = &mut [&0]; - //[stable2021]~^ mismatched types - //[stable2021]~| types differ in mutability + //[stable2021]~^ ERROR mismatched types + //[stable2021]~| NOTE types differ in mutability #[cfg(any(classic2021, structural2021))] let _: &u32 = x; #[cfg(any(classic2024, structural2024))] let _: &&u32 = x; fn borrowck_error_on_structural2021() { let [&mut ref mut x] = &mut [&0]; - //[stable2021]~^ mismatched types - //[stable2021]~| types differ in mutability - //[classic2021,structural2021]~^^^ cannot borrow data in a `&` reference as mutable + //[stable2021]~^ ERROR mismatched types + //[stable2021]~| NOTE types differ in mutability + //[classic2021,structural2021]~^^^ ERROR cannot borrow data in a `&` reference as mutable #[cfg(any(classic2024, structural2024))] let _: &mut &u32 = x; } borrowck_error_on_structural2021(); let [&mut mut x] = &mut [&0]; - //[stable2021]~^ mismatched types - //[stable2021]~| types differ in mutability + //[stable2021]~^ ERROR mismatched types + //[stable2021]~| NOTE types differ in mutability #[cfg(any(classic2021, structural2021))] let _: u32 = x; #[cfg(any(classic2024, structural2024))] let _: &u32 = x; let [&mut &x] = &mut [&0]; - //[stable2021,classic2021,structural2021]~^ mismatched types - //[stable2021]~| types differ in mutability - //[classic2021,structural2021]~| expected integer, found `&_` + //[stable2021,classic2021,structural2021]~^ ERROR mismatched types + //[stable2021]~| NOTE types differ in mutability + //[classic2021,structural2021]~| NOTE expected integer, found `&_` #[cfg(any(classic2024, structural2024))] let _: u32 = x; let [&mut &ref x] = &mut [&0]; - //[stable2021,classic2021,structural2021]~^ mismatched types - //[stable2021]~| types differ in mutability - //[classic2021,structural2021]~| expected integer, found `&_` + //[stable2021,classic2021,structural2021]~^ ERROR mismatched types + //[stable2021]~| NOTE types differ in mutability + //[classic2021,structural2021]~| NOTE expected integer, found `&_` #[cfg(any(classic2024, structural2024))] let _: &u32 = x; let [&mut &(mut x)] = &mut [&0]; - //[stable2021,classic2021,structural2021]~^ mismatched types - //[stable2021]~| types differ in mutability - //[classic2021,structural2021]~| expected integer, found `&_` + //[stable2021,classic2021,structural2021]~^ ERROR mismatched types + //[stable2021]~| NOTE types differ in mutability + //[classic2021,structural2021]~| NOTE expected integer, found `&_` #[cfg(any(classic2024, structural2024))] let _: u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr index adb47172f34cc..4f9dfaf639ca0 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:32:23 + --> $DIR/well-typed-edition-2024.rs:34:23 | LL | if let Some(Some(&&x)) = &Some(Some(&0)) { | ^^ --------------- this expression has type `&Option>` @@ -15,7 +15,7 @@ LL + if let Some(Some(&x)) = &Some(Some(&0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:39:22 + --> $DIR/well-typed-edition-2024.rs:41:22 | LL | if let Some(Some(&x)) = &Some(&Some(0)) { | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` @@ -31,7 +31,7 @@ LL + if let Some(Some(x)) = &Some(&Some(0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:44:17 + --> $DIR/well-typed-edition-2024.rs:46:17 | LL | if let Some(&Some(x)) = &Some(Some(0)) { | ^^^^^^^^ -------------- this expression has type `&Option>` @@ -42,7 +42,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { found reference `&_` error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:49:22 + --> $DIR/well-typed-edition-2024.rs:51:22 | LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { | ^^^^^^ ----------------------- this expression has type `&mut Option<&mut Option<{integer}>>` @@ -52,7 +52,7 @@ LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { = note: expected type `{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/well-typed-edition-2024.rs:49:22 + --> $DIR/well-typed-edition-2024.rs:51:22 | LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { | ^^^^^^ @@ -63,7 +63,7 @@ LL + if let Some(Some(x)) = &mut Some(&mut Some(0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:56:23 + --> $DIR/well-typed-edition-2024.rs:58:23 | LL | if let Some(&Some(&x)) = Some(&Some(&mut 0)) { | ^^ ------------------- this expression has type `Option<&Option<&mut {integer}>>` @@ -79,7 +79,7 @@ LL + if let Some(&Some(x)) = Some(&Some(&mut 0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:63:23 + --> $DIR/well-typed-edition-2024.rs:65:23 | LL | if let Some(&Some(&x)) = &Some(&Some(0)) { | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` @@ -95,7 +95,7 @@ LL + if let Some(&Some(x)) = &Some(&Some(0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:70:17 + --> $DIR/well-typed-edition-2024.rs:72:17 | LL | if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { | ^^^^^^^^^^^^^^^ ------------------------- this expression has type `&Option>>` @@ -106,7 +106,7 @@ LL | if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { found reference `&_` error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:75:17 + --> $DIR/well-typed-edition-2024.rs:77:17 | LL | if let Some(&Some(x)) = &mut Some(Some(0)) { | ^^^^^^^^ ------------------ this expression has type `&mut Option>` @@ -117,7 +117,7 @@ LL | if let Some(&Some(x)) = &mut Some(Some(0)) { found reference `&_` error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:82:17 + --> $DIR/well-typed-edition-2024.rs:84:17 | LL | if let Some(&Some(&x)) = &Some(&mut Some(0)) { | ^^^^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` @@ -128,7 +128,7 @@ LL | if let Some(&Some(&x)) = &Some(&mut Some(0)) { found reference `&_` error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:88:23 + --> $DIR/well-typed-edition-2024.rs:90:23 | LL | if let Some(&Some(&x)) = &mut Some(&Some(0)) { | ^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` @@ -144,7 +144,7 @@ LL + if let Some(&Some(x)) = &mut Some(&Some(0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:96:10 + --> $DIR/well-typed-edition-2024.rs:98:10 | LL | let [&mut x] = &mut [&0]; | ^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -154,7 +154,7 @@ LL | let [&mut x] = &mut [&0]; = note: expected reference `&{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/well-typed-edition-2024.rs:96:10 + --> $DIR/well-typed-edition-2024.rs:98:10 | LL | let [&mut x] = &mut [&0]; | ^^^^^^ @@ -165,7 +165,7 @@ LL + let [x] = &mut [&0]; | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:102:10 + --> $DIR/well-typed-edition-2024.rs:104:10 | LL | let [&mut ref x] = &mut [&0]; | ^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -175,7 +175,7 @@ LL | let [&mut ref x] = &mut [&0]; = note: expected reference `&{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/well-typed-edition-2024.rs:102:10 + --> $DIR/well-typed-edition-2024.rs:104:10 | LL | let [&mut ref x] = &mut [&0]; | ^^^^^^^^^^ @@ -186,7 +186,7 @@ LL + let [ref x] = &mut [&0]; | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:117:10 + --> $DIR/well-typed-edition-2024.rs:119:10 | LL | let [&mut mut x] = &mut [&0]; | ^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -196,7 +196,7 @@ LL | let [&mut mut x] = &mut [&0]; = note: expected reference `&{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/well-typed-edition-2024.rs:117:10 + --> $DIR/well-typed-edition-2024.rs:119:10 | LL | let [&mut mut x] = &mut [&0]; | ^^^^^^^^^^ @@ -207,7 +207,7 @@ LL + let [mut x] = &mut [&0]; | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:123:10 + --> $DIR/well-typed-edition-2024.rs:125:10 | LL | let [&mut &x] = &mut [&0]; | ^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -218,7 +218,7 @@ LL | let [&mut &x] = &mut [&0]; found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:129:10 + --> $DIR/well-typed-edition-2024.rs:131:10 | LL | let [&mut &ref x] = &mut [&0]; | ^^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -229,7 +229,7 @@ LL | let [&mut &ref x] = &mut [&0]; found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:135:10 + --> $DIR/well-typed-edition-2024.rs:137:10 | LL | let [&mut &(mut x)] = &mut [&0]; | ^^^^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -240,7 +240,7 @@ LL | let [&mut &(mut x)] = &mut [&0]; found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:109:14 + --> $DIR/well-typed-edition-2024.rs:111:14 | LL | let [&mut ref mut x] = &mut [&0]; | ^^^^^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -250,7 +250,7 @@ LL | let [&mut ref mut x] = &mut [&0]; = note: expected reference `&{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/well-typed-edition-2024.rs:109:14 + --> $DIR/well-typed-edition-2024.rs:111:14 | LL | let [&mut ref mut x] = &mut [&0]; | ^^^^^^^^^^^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.structural2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.structural2021.stderr index f8c2bd9a92128..9bf5c9544e51c 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.structural2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.structural2021.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:32:23 + --> $DIR/well-typed-edition-2024.rs:34:23 | LL | if let Some(Some(&&x)) = &Some(Some(&0)) { | ^^ --------------- this expression has type `&Option>` @@ -15,7 +15,7 @@ LL + if let Some(Some(&x)) = &Some(Some(&0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:63:23 + --> $DIR/well-typed-edition-2024.rs:65:23 | LL | if let Some(&Some(&x)) = &Some(&Some(0)) { | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` @@ -31,7 +31,7 @@ LL + if let Some(&Some(x)) = &Some(&Some(0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:82:23 + --> $DIR/well-typed-edition-2024.rs:84:23 | LL | if let Some(&Some(&x)) = &Some(&mut Some(0)) { | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` @@ -47,7 +47,7 @@ LL + if let Some(&Some(x)) = &Some(&mut Some(0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:88:23 + --> $DIR/well-typed-edition-2024.rs:90:23 | LL | if let Some(&Some(&x)) = &mut Some(&Some(0)) { | ^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` @@ -63,7 +63,7 @@ LL + if let Some(&Some(x)) = &mut Some(&Some(0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:123:15 + --> $DIR/well-typed-edition-2024.rs:125:15 | LL | let [&mut &x] = &mut [&0]; | ^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -79,7 +79,7 @@ LL + let [&mut x] = &mut [&0]; | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:129:15 + --> $DIR/well-typed-edition-2024.rs:131:15 | LL | let [&mut &ref x] = &mut [&0]; | ^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -95,7 +95,7 @@ LL + let [&mut ref x] = &mut [&0]; | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:135:15 + --> $DIR/well-typed-edition-2024.rs:137:15 | LL | let [&mut &(mut x)] = &mut [&0]; | ^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -111,7 +111,7 @@ LL + let [&mut mut x)] = &mut [&0]; | error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/well-typed-edition-2024.rs:109:19 + --> $DIR/well-typed-edition-2024.rs:111:19 | LL | let [&mut ref mut x] = &mut [&0]; | ^^^^^^^^^ cannot borrow as mutable diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs new file mode 100644 index 0000000000000..83ad8c76bb1cf --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs @@ -0,0 +1,81 @@ +//! Test that guard patterns can see bindings already in scope and bindings introduced in their +//! subpattern, but no other bindings from the containing pattern. Also make sure bindings +//! introduced in guard patterns are visible in fn/arm/loop/etc bodies. + +#![feature(guard_patterns)] +#![expect(incomplete_features)] + +fn good_fn_item(((x if x) | x): bool) -> bool { x } + +fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} +//~^ ERROR cannot find value `x` in this scope +fn bad_fn_item_2(((x if y) | x): bool, y: bool) {} +//~^ ERROR cannot find value `y` in this scope + +fn main() { + let ((local if local) if local) = false; + + match (true, true) { + (x if local, y if good_fn_item(y)) => x && y, + (x, y if x) => x && y, + //~^ ERROR cannot find value `x` in this scope + (x if y, y) => x && y, + //~^ ERROR cannot find value `y` in this scope + }; + + match (true,) { + (x @ y if x && y,) => x && y, + (x @ (y if y),) => x && y, + (x @ (y if x),) => x && y, + //~^ ERROR cannot find value `x` in this scope + }; + + match (Ok(true),) { + ((Ok(x) | Err(x)) if good_fn_item(x),) => x, + ((Ok(x) if local) | (Err(x) if good_fn_item(x)),) => x, + ((Ok(x if x) if x) | (Err(x if x) if x) if x,) if x => x, + ((Ok(x) if y) | (Err(y) if x),) => x && y, + //~^ ERROR variable `x` is not bound in all patterns + //~| ERROR variable `y` is not bound in all patterns + //~| ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + }; + + let (_ if nonexistent) = true; + //~^ ERROR cannot find value `nonexistent` in this scope + if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + + (|(x if x), (y if y)| x && y)(true, true); + (|(x if y), (y if x)| x && y)(true, true); + //~^ ERROR cannot find value `x` in this scope + //~| ERROR cannot find value `y` in this scope + + // FIXME(guard_patterns): mismatched bindings are not yet allowed + match Some(0) { + Some(x if x > 0) | None => {} + //~^ ERROR variable `x` is not bound in all patterns + } +} + +/// Make sure shadowing is handled properly. In particular, if a pattern shadows an identifier, +/// a guard pattern's guard should still see the original binding if the shadowing binding isn't in +/// its subpattern. +fn test_shadowing(local: bool) -> u8 { + match (0, 0) { + // The `local` binding here shadows the `bool` definition, so we get a type error. + //~v ERROR mismatched types + local if local => 0, + // The guards here should see the `bool` definition of `local`, not the new `u8` binding. + // The body should see the new binding. + (local, _ if local) => local, + (_ if local, local) => local, + } +} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr new file mode 100644 index 0000000000000..d76e60478a146 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr @@ -0,0 +1,133 @@ +error[E0408]: variable `y` is not bound in all patterns + --> $DIR/name-resolution.rs:37:10 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | ^^^^^^^^^^^^ - variable not in all patterns + | | + | pattern doesn't bind `y` + +error[E0408]: variable `x` is not bound in all patterns + --> $DIR/name-resolution.rs:37:25 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | - ^^^^^^^^^^^^^ pattern doesn't bind `x` + | | + | variable not in all patterns + +error[E0408]: variable `x` is not bound in all patterns + --> $DIR/name-resolution.rs:63:28 + | +LL | Some(x if x > 0) | None => {} + | - ^^^^ pattern doesn't bind `x` + | | + | variable not in all patterns + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:10:34 + | +LL | fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:12:25 + | +LL | fn bad_fn_item_2(((x if y) | x): bool, y: bool) {} + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:20:18 + | +LL | (x, y if x) => x && y, + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:22:15 + | +LL | (x if y, y) => x && y, + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:29:20 + | +LL | (x @ (y if x),) => x && y, + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:37:20 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:37:36 + | +LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `nonexistent` in this scope + --> $DIR/name-resolution.rs:44:15 + | +LL | let (_ if nonexistent) = true; + | ^^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:46:22 + | +LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:46:33 + | +LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:49:25 + | +LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:49:36 + | +LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:52:19 + | +LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } + | ^ help: a local variable with a similar name exists: `y` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:52:30 + | +LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `y` in this scope + --> $DIR/name-resolution.rs:57:13 + | +LL | (|(x if y), (y if x)| x && y)(true, true); + | ^ help: a local variable with a similar name exists: `x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/name-resolution.rs:57:23 + | +LL | (|(x if y), (y if x)| x && y)(true, true); + | ^ help: a local variable with a similar name exists: `y` + +error[E0308]: mismatched types + --> $DIR/name-resolution.rs:75:18 + | +LL | local if local => 0, + | ^^^^^ expected `bool`, found `({integer}, {integer})` + | + = note: expected type `bool` + found tuple `({integer}, {integer})` + +error: aborting due to 20 previous errors + +Some errors have detailed explanations: E0308, E0408, E0425. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs b/tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs new file mode 100644 index 0000000000000..7bb39ca7bb987 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs @@ -0,0 +1,15 @@ +//@ check-pass +//! Test that `GatherLocalsVisitor` only visits expressions in guard patterns when checking the +//! expressions, and not a second time when visiting the pattern. If locals are declared inside the +//! the guard expression, it would ICE if visited twice ("evaluated expression more than once"). + +#![feature(guard_patterns)] +#![expect(incomplete_features)] + +fn main() { + match (0,) { + // FIXME(guard_patterns): liveness lints don't work yet; this will ICE without the `_`. + (_ if { let _x = false; _x },) => {} + _ => {} + } +} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/only-resolve-top-level-guard-expr-once-ice-141265.rs b/tests/ui/pattern/rfc-3637-guard-patterns/only-resolve-top-level-guard-expr-once-ice-141265.rs new file mode 100644 index 0000000000000..7dc9ef7161d1b --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/only-resolve-top-level-guard-expr-once-ice-141265.rs @@ -0,0 +1,12 @@ +//! Regression test for . +//! Make sure expressions in top-level guard patterns are only resolved once. + +fn main() { + for + else if b 0 {} + //~^ ERROR expected identifier, found keyword `else` + //~| ERROR missing `in` in `for` loop + //~| ERROR cannot find value `b` in this scope + //~| ERROR guard patterns are experimental + //~| ERROR `{integer}` is not an iterator +} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/only-resolve-top-level-guard-expr-once-ice-141265.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/only-resolve-top-level-guard-expr-once-ice-141265.stderr new file mode 100644 index 0000000000000..dae384137f529 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/only-resolve-top-level-guard-expr-once-ice-141265.stderr @@ -0,0 +1,48 @@ +error: expected identifier, found keyword `else` + --> $DIR/only-resolve-top-level-guard-expr-once-ice-141265.rs:6:5 + | +LL | else if b 0 {} + | ^^^^ expected identifier, found keyword + +error: missing `in` in `for` loop + --> $DIR/only-resolve-top-level-guard-expr-once-ice-141265.rs:6:14 + | +LL | else if b 0 {} + | ^ + | +help: try adding `in` here + | +LL | else if b in 0 {} + | ++ + +error[E0425]: cannot find value `b` in this scope + --> $DIR/only-resolve-top-level-guard-expr-once-ice-141265.rs:6:13 + | +LL | else if b 0 {} + | ^ not found in this scope + +error[E0658]: guard patterns are experimental + --> $DIR/only-resolve-top-level-guard-expr-once-ice-141265.rs:6:13 + | +LL | else if b 0 {} + | ^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider using match arm guards + +error[E0277]: `{integer}` is not an iterator + --> $DIR/only-resolve-top-level-guard-expr-once-ice-141265.rs:6:15 + | +LL | else if b 0 {} + | ^ `{integer}` is not an iterator + | + = 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 for `{integer}` to implement `IntoIterator` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0277, E0425, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/pattern/uninit-trivial.rs b/tests/ui/pattern/uninit-trivial.rs new file mode 100644 index 0000000000000..6ea6796c1c1f0 --- /dev/null +++ b/tests/ui/pattern/uninit-trivial.rs @@ -0,0 +1,8 @@ +// Regression test for the semantic changes in +// . + +fn main() { + let x; + let (0 | _) = x; + //~^ ERROR used binding `x` isn't initialized +} diff --git a/tests/ui/pattern/uninit-trivial.stderr b/tests/ui/pattern/uninit-trivial.stderr new file mode 100644 index 0000000000000..2ff8557c94543 --- /dev/null +++ b/tests/ui/pattern/uninit-trivial.stderr @@ -0,0 +1,16 @@ +error[E0381]: used binding `x` isn't initialized + --> $DIR/uninit-trivial.rs:6:10 + | +LL | let x; + | - binding declared here but left uninitialized +LL | let (0 | _) = x; + | ^^^^^ `x` used here but it isn't initialized + | +help: consider assigning a value + | +LL | let x = 42; + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0381`. diff --git a/tests/ui/pattern/usefulness/doc-hidden-fields.rs b/tests/ui/pattern/usefulness/doc-hidden-fields.rs index 549e0d1af558a..a3c3740b34ada 100644 --- a/tests/ui/pattern/usefulness/doc-hidden-fields.rs +++ b/tests/ui/pattern/usefulness/doc-hidden-fields.rs @@ -13,14 +13,14 @@ struct InCrate { fn main() { let HiddenStruct { one, two } = HiddenStruct::default(); - //~^ pattern requires `..` due to inaccessible fields + //~^ ERROR pattern requires `..` due to inaccessible fields let HiddenStruct { one } = HiddenStruct::default(); - //~^ pattern does not mention field `two` and inaccessible fields + //~^ ERROR pattern does not mention field `two` and inaccessible fields let HiddenStruct { one, hide } = HiddenStruct::default(); - //~^ pattern does not mention field `two` + //~^ ERROR pattern does not mention field `two` let InCrate { a, b } = InCrate { a: 0, b: false, im_hidden: 0 }; - //~^ pattern does not mention field `im_hidden` + //~^ ERROR pattern does not mention field `im_hidden` } diff --git a/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs index 56842917f500b..4bbdc7b139f4f 100644 --- a/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs +++ b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs @@ -16,28 +16,28 @@ fn main() { HiddenEnum::A => {} HiddenEnum::B => {} } - //~^^^^ non-exhaustive patterns: `_` not covered + //~^^^^ ERROR non-exhaustive patterns: `_` not covered match HiddenEnum::A { HiddenEnum::A => {} HiddenEnum::C => {} } - //~^^^^ non-exhaustive patterns: `HiddenEnum::B` not covered + //~^^^^ ERROR non-exhaustive patterns: `HiddenEnum::B` not covered match HiddenEnum::A { HiddenEnum::A => {} } - //~^^^ non-exhaustive patterns: `HiddenEnum::B` and `_` not covered + //~^^^ ERROR non-exhaustive patterns: `HiddenEnum::B` and `_` not covered match None { None => {} Some(HiddenEnum::A) => {} } - //~^^^^ non-exhaustive patterns: `Some(HiddenEnum::B)` and `Some(_)` not covered + //~^^^^ ERROR non-exhaustive patterns: `Some(HiddenEnum::B)` and `Some(_)` not covered match InCrate::A { InCrate::A => {} InCrate::B => {} } - //~^^^^ non-exhaustive patterns: `InCrate::C` not covered + //~^^^^ ERROR non-exhaustive patterns: `InCrate::C` not covered } diff --git a/tests/ui/pattern/usefulness/impl-trait.rs b/tests/ui/pattern/usefulness/impl-trait.rs index 16560a092675f..47cee17b57994 100644 --- a/tests/ui/pattern/usefulness/impl-trait.rs +++ b/tests/ui/pattern/usefulness/impl-trait.rs @@ -25,6 +25,7 @@ fn friend_of_return_never_rpit(x: Void) { } type T = impl Copy; +#[define_opaque(T)] fn return_never_tait(x: Void) -> T { if false { match return_never_tait(x) { @@ -88,6 +89,7 @@ fn inner_tuple() { } type U = impl Copy; +#[define_opaque(U)] fn unify_never(x: Void, u: U) -> U { if false { match u { @@ -98,6 +100,7 @@ fn unify_never(x: Void, u: U) -> U { } type V = impl Copy; +#[define_opaque(V)] fn infer_in_match(x: Option) { match x { None => {} @@ -116,6 +119,7 @@ struct Rec<'a> { n: u32, w: Option<&'a W>, } +#[define_opaque(W)] fn recursive_opaque() -> W { if false { match recursive_opaque() { @@ -130,6 +134,7 @@ fn recursive_opaque() -> W { type X = impl Copy; struct SecretelyVoid(X); +#[define_opaque(X)] fn nested_empty_opaque(x: Void) -> X { if false { let opaque_void = nested_empty_opaque(x); @@ -143,6 +148,7 @@ fn nested_empty_opaque(x: Void) -> X { type Y = (impl Copy, impl Copy); struct SecretelyDoubleVoid(Y); +#[define_opaque(Y)] fn super_nested_empty_opaque(x: Void) -> Y { if false { let opaque_void = super_nested_empty_opaque(x); diff --git a/tests/ui/pattern/usefulness/impl-trait.stderr b/tests/ui/pattern/usefulness/impl-trait.stderr index c3e1c267b6140..62045151968e2 100644 --- a/tests/ui/pattern/usefulness/impl-trait.stderr +++ b/tests/ui/pattern/usefulness/impl-trait.stderr @@ -15,7 +15,7 @@ LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/impl-trait.rs:31:13 + --> $DIR/impl-trait.rs:32:13 | LL | _ => {} | ^------ @@ -25,22 +25,8 @@ LL | _ => {} | = note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types -error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty - --> $DIR/impl-trait.rs:23:11 - | -LL | match return_never_rpit(x) {} - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: the matched value is of type `impl Copy` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match return_never_rpit(x) { -LL + _ => todo!(), -LL ~ } - | - error: unreachable pattern - --> $DIR/impl-trait.rs:45:13 + --> $DIR/impl-trait.rs:46:13 | LL | Some(_) => {} | ^^^^^^^------ @@ -51,7 +37,7 @@ LL | Some(_) => {} = note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types error: unreachable pattern - --> $DIR/impl-trait.rs:49:13 + --> $DIR/impl-trait.rs:50:13 | LL | None => {} | ---- matches all the relevant values @@ -59,7 +45,7 @@ LL | _ => {} | ^ no value can reach this error: unreachable pattern - --> $DIR/impl-trait.rs:59:13 + --> $DIR/impl-trait.rs:60:13 | LL | Some(_) => {} | ^^^^^^^------ @@ -70,7 +56,7 @@ LL | Some(_) => {} = note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types error: unreachable pattern - --> $DIR/impl-trait.rs:63:13 + --> $DIR/impl-trait.rs:64:13 | LL | None => {} | ---- matches all the relevant values @@ -78,7 +64,7 @@ LL | _ => {} | ^ no value can reach this error: unreachable pattern - --> $DIR/impl-trait.rs:76:9 + --> $DIR/impl-trait.rs:77:9 | LL | _ => {} | ^------ @@ -89,7 +75,7 @@ LL | _ => {} = note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types error: unreachable pattern - --> $DIR/impl-trait.rs:86:9 + --> $DIR/impl-trait.rs:87:9 | LL | _ => {} | - matches any value @@ -97,7 +83,7 @@ LL | Some((a, b)) => {} | ^^^^^^^^^^^^ no value can reach this error: unreachable pattern - --> $DIR/impl-trait.rs:94:13 + --> $DIR/impl-trait.rs:96:13 | LL | _ => {} | ^------ @@ -107,22 +93,8 @@ LL | _ => {} | = note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types -error[E0004]: non-exhaustive patterns: type `T` is non-empty - --> $DIR/impl-trait.rs:37:11 - | -LL | match return_never_tait(x) {} - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: the matched value is of type `T` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match return_never_tait(x) { -LL + _ => todo!(), -LL ~ } - | - error: unreachable pattern - --> $DIR/impl-trait.rs:105:9 + --> $DIR/impl-trait.rs:108:9 | LL | Some((a, b)) => {} | ------------ matches all the relevant values @@ -130,7 +102,7 @@ LL | Some((mut x, mut y)) => { | ^^^^^^^^^^^^^^^^^^^^ no value can reach this error: unreachable pattern - --> $DIR/impl-trait.rs:124:13 + --> $DIR/impl-trait.rs:128:13 | LL | _ => {} | - matches any value @@ -138,7 +110,7 @@ LL | Rec { n: 0, w: Some(Rec { n: 0, w: _ }) } => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no value can reach this error: unreachable pattern - --> $DIR/impl-trait.rs:138:13 + --> $DIR/impl-trait.rs:143:13 | LL | _ => {} | ^------ @@ -149,7 +121,7 @@ LL | _ => {} = note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types error: unreachable pattern - --> $DIR/impl-trait.rs:151:13 + --> $DIR/impl-trait.rs:157:13 | LL | _ => {} | ^------ @@ -159,6 +131,34 @@ LL | _ => {} | = note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types +error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty + --> $DIR/impl-trait.rs:23:11 + | +LL | match return_never_rpit(x) {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: the matched value is of type `impl Copy` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + | +LL ~ match return_never_rpit(x) { +LL + _ => todo!(), +LL ~ } + | + +error[E0004]: non-exhaustive patterns: type `T` is non-empty + --> $DIR/impl-trait.rs:38:11 + | +LL | match return_never_tait(x) {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: the matched value is of type `T` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + | +LL ~ match return_never_tait(x) { +LL + _ => todo!(), +LL ~ } + | + error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/issue-31561.rs b/tests/ui/pattern/usefulness/issue-31561.rs index 82414f0418bb2..fe1b2bb4f8358 100644 --- a/tests/ui/pattern/usefulness/issue-31561.rs +++ b/tests/ui/pattern/usefulness/issue-31561.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + enum Thing { Foo(u8), Bar, @@ -7,5 +9,5 @@ enum Thing { fn main() { let Thing::Foo(y) = Thing::Foo(1); //~^ ERROR refutable pattern in local binding - //~| `Thing::Bar` and `Thing::Baz` not covered + //~| NOTE `Thing::Bar` and `Thing::Baz` not covered } diff --git a/tests/ui/pattern/usefulness/issue-31561.stderr b/tests/ui/pattern/usefulness/issue-31561.stderr index ba7ae3fa9a003..382b2337ffaba 100644 --- a/tests/ui/pattern/usefulness/issue-31561.stderr +++ b/tests/ui/pattern/usefulness/issue-31561.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in local binding - --> $DIR/issue-31561.rs:8:9 + --> $DIR/issue-31561.rs:10:9 | LL | let Thing::Foo(y) = Thing::Foo(1); | ^^^^^^^^^^^^^ patterns `Thing::Bar` and `Thing::Baz` not covered @@ -7,7 +7,7 @@ LL | let Thing::Foo(y) = Thing::Foo(1); = 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/ch19-02-refutability.html note: `Thing` defined here - --> $DIR/issue-31561.rs:1:6 + --> $DIR/issue-31561.rs:3:6 | LL | enum Thing { | ^^^^^ diff --git a/tests/ui/pattern/usefulness/issue-39362.rs b/tests/ui/pattern/usefulness/issue-39362.rs index ea3c8f88e0b36..0db3980359c84 100644 --- a/tests/ui/pattern/usefulness/issue-39362.rs +++ b/tests/ui/pattern/usefulness/issue-39362.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + enum Foo { Bar { bar: Bar, id: usize } } @@ -9,7 +11,7 @@ enum Bar { fn test(f: Foo) { match f { //~^ ERROR non-exhaustive patterns - //~| patterns + //~| NOTE patterns Foo::Bar { bar: Bar::A, .. } => (), Foo::Bar { bar: Bar::B, .. } => (), } diff --git a/tests/ui/pattern/usefulness/issue-39362.stderr b/tests/ui/pattern/usefulness/issue-39362.stderr index 9cce87a1c65cf..18d542cc6ad32 100644 --- a/tests/ui/pattern/usefulness/issue-39362.stderr +++ b/tests/ui/pattern/usefulness/issue-39362.stderr @@ -1,11 +1,11 @@ error[E0004]: non-exhaustive patterns: `Foo::Bar { bar: Bar::C, .. }`, `Foo::Bar { bar: Bar::D, .. }`, `Foo::Bar { bar: Bar::E, .. }` and 1 more not covered - --> $DIR/issue-39362.rs:10:11 + --> $DIR/issue-39362.rs:12:11 | LL | match f { | ^ patterns `Foo::Bar { bar: Bar::C, .. }`, `Foo::Bar { bar: Bar::D, .. }`, `Foo::Bar { bar: Bar::E, .. }` and 1 more not covered | note: `Foo` defined here - --> $DIR/issue-39362.rs:1:6 + --> $DIR/issue-39362.rs:3:6 | LL | enum Foo { | ^^^ diff --git a/tests/ui/pattern/usefulness/issue-72377.rs b/tests/ui/pattern/usefulness/issue-72377.rs index b5ad3075ca725..782a9963f2e5d 100644 --- a/tests/ui/pattern/usefulness/issue-72377.rs +++ b/tests/ui/pattern/usefulness/issue-72377.rs @@ -7,7 +7,8 @@ fn main() { match (x, y) { //~^ ERROR non-exhaustive patterns: `(X::A, Some(X::A))`, `(X::A, Some(X::B))`, `(X::B, Some(X::B))` and 2 - //~| more not covered + //~| NOTE more not covered + //~| NOTE the matched value is of type `(X, Option)` (_, None) => false, (v, Some(w)) if v == w => true, (X::B, Some(X::C)) => false, diff --git a/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs b/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs index cbfcf0eafd490..6c5a331b4b564 100644 --- a/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs +++ b/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs @@ -1,6 +1,5 @@ enum A {} //~^ NOTE `A` defined here - //~| NOTE fn f(a: &A) { match a {} diff --git a/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr b/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr index c37a9a5157904..67ee38eed60c9 100644 --- a/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr +++ b/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: type `&A` is non-empty - --> $DIR/issue-78123-non-exhaustive-reference.rs:6:11 + --> $DIR/issue-78123-non-exhaustive-reference.rs:5:11 | LL | match a {} | ^ diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs index d0a8a10f2f371..4a7b2c956fc3c 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs +++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs @@ -24,12 +24,12 @@ enum E { //~| NOTE not covered //~| NOTE not covered C - //~^ not covered - //~| not covered - //~| not covered - //~| not covered - //~| not covered - //~| not covered + //~^ NOTE not covered + //~| NOTE not covered + //~| NOTE not covered + //~| NOTE not covered + //~| NOTE not covered + //~| NOTE not covered } fn by_val(e: E) { @@ -42,7 +42,7 @@ fn by_val(e: E) { let E::A = e; //~^ ERROR refutable pattern in local binding - //~| patterns `E::B` and `E::C` not covered + //~| NOTE patterns `E::B` and `E::C` not covered //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html //~| NOTE the matched value is of type `E` @@ -51,14 +51,14 @@ fn by_val(e: E) { fn by_ref_once(e: &E) { match e { //~^ ERROR non-exhaustive patterns - //~| patterns `&E::B` and `&E::C` not covered + //~| NOTE patterns `&E::B` and `&E::C` not covered //~| NOTE the matched value is of type `&E` E::A => {} } let E::A = e; //~^ ERROR refutable pattern in local binding - //~| patterns `&E::B` and `&E::C` not covered + //~| NOTE patterns `&E::B` and `&E::C` not covered //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html //~| NOTE the matched value is of type `&E` @@ -67,14 +67,14 @@ fn by_ref_once(e: &E) { fn by_ref_thrice(e: & &mut &E) { match e { //~^ ERROR non-exhaustive patterns - //~| patterns `&&mut &E::B` and `&&mut &E::C` not covered + //~| NOTE patterns `&&mut &E::B` and `&&mut &E::C` not covered //~| NOTE the matched value is of type `&&mut &E` E::A => {} } let E::A = e; //~^ ERROR refutable pattern in local binding - //~| patterns `&&mut &E::B` and `&&mut &E::C` not covered + //~| NOTE patterns `&&mut &E::B` and `&&mut &E::C` not covered //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html //~| NOTE the matched value is of type `&&mut &E` @@ -93,7 +93,7 @@ enum Opt { fn ref_pat(e: Opt) { match e { //~^ ERROR non-exhaustive patterns - //~| pattern `Opt::None` not covered + //~| NOTE pattern `Opt::None` not covered //~| NOTE the matched value is of type `Opt` Opt::Some(ref _x) => {} } diff --git a/tests/ui/pattern/usefulness/refutable-pattern-errors.rs b/tests/ui/pattern/usefulness/refutable-pattern-errors.rs index 7603da1bb2ce8..de9fc24bbd004 100644 --- a/tests/ui/pattern/usefulness/refutable-pattern-errors.rs +++ b/tests/ui/pattern/usefulness/refutable-pattern-errors.rs @@ -1,9 +1,11 @@ +//@ dont-require-annotations: NOTE + fn func((1, (Some(1), 2..=3)): (isize, (Option, isize))) {} //~^ ERROR refutable pattern in function argument -//~| `(..=0_isize, _)` and `(2_isize.., _)` not covered +//~| NOTE `(..=0_isize, _)` and `(2_isize.., _)` not covered fn main() { let (1, (Some(1), 2..=3)) = (1, (None, 2)); //~^ ERROR refutable pattern in local binding - //~| `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered + //~| NOTE `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered } diff --git a/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr b/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr index 23a5d895d6c1a..37d1dc3efe90b 100644 --- a/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr +++ b/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in function argument - --> $DIR/refutable-pattern-errors.rs:1:9 + --> $DIR/refutable-pattern-errors.rs:3:9 | LL | fn func((1, (Some(1), 2..=3)): (isize, (Option, isize))) {} | ^^^^^^^^^^^^^^^^^^^^^ patterns `(..=0_isize, _)` and `(2_isize.., _)` not covered @@ -7,7 +7,7 @@ LL | fn func((1, (Some(1), 2..=3)): (isize, (Option, isize))) {} = note: the matched value is of type `(isize, (Option, isize))` error[E0005]: refutable pattern in local binding - --> $DIR/refutable-pattern-errors.rs:6:9 + --> $DIR/refutable-pattern-errors.rs:8:9 | LL | let (1, (Some(1), 2..=3)) = (1, (None, 2)); | ^^^^^^^^^^^^^^^^^^^^^ patterns `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered diff --git a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs index 51ff641509df5..416564d94dcb4 100644 --- a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs +++ b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs @@ -1,6 +1,7 @@ fn main() { let f = |3: isize| println!("hello"); //~^ ERROR refutable pattern in closure argument - //~| `..=2_isize` and `4_isize..` not covered + //~| NOTE `..=2_isize` and `4_isize..` not covered + //~| NOTE the matched value is of type `isize` f(4); } diff --git a/tests/ui/pattern/usefulness/stable-gated-fields.rs b/tests/ui/pattern/usefulness/stable-gated-fields.rs index 61b202b77f61f..7be2722daac77 100644 --- a/tests/ui/pattern/usefulness/stable-gated-fields.rs +++ b/tests/ui/pattern/usefulness/stable-gated-fields.rs @@ -6,10 +6,10 @@ use unstable::UnstableStruct; fn main() { let UnstableStruct { stable } = UnstableStruct::default(); - //~^ pattern does not mention field `stable2` and inaccessible fields + //~^ ERROR pattern does not mention field `stable2` and inaccessible fields let UnstableStruct { stable, stable2 } = UnstableStruct::default(); - //~^ pattern requires `..` due to inaccessible fields + //~^ ERROR pattern requires `..` due to inaccessible fields // OK: stable field is matched let UnstableStruct { stable, stable2, .. } = UnstableStruct::default(); diff --git a/tests/ui/pattern/usefulness/stable-gated-patterns.rs b/tests/ui/pattern/usefulness/stable-gated-patterns.rs index 5ceffbf092366..d58483441549b 100644 --- a/tests/ui/pattern/usefulness/stable-gated-patterns.rs +++ b/tests/ui/pattern/usefulness/stable-gated-patterns.rs @@ -8,11 +8,11 @@ fn main() { match UnstableEnum::Stable { UnstableEnum::Stable => {} } - //~^^^ non-exhaustive patterns: `UnstableEnum::Stable2` and `_` not covered + //~^^^ ERROR non-exhaustive patterns: `UnstableEnum::Stable2` and `_` not covered match UnstableEnum::Stable { UnstableEnum::Stable => {} UnstableEnum::Stable2 => {} } - //~^^^^ non-exhaustive patterns: `_` not covered + //~^^^^ ERROR non-exhaustive patterns: `_` not covered } diff --git a/tests/ui/pattern/usefulness/unstable-gated-fields.rs b/tests/ui/pattern/usefulness/unstable-gated-fields.rs index e6a4494867a0c..08b46283f6594 100644 --- a/tests/ui/pattern/usefulness/unstable-gated-fields.rs +++ b/tests/ui/pattern/usefulness/unstable-gated-fields.rs @@ -8,10 +8,10 @@ use unstable::UnstableStruct; fn main() { let UnstableStruct { stable, stable2, } = UnstableStruct::default(); - //~^ pattern does not mention field `unstable` + //~^ ERROR pattern does not mention field `unstable` let UnstableStruct { stable, unstable, } = UnstableStruct::default(); - //~^ pattern does not mention field `stable2` + //~^ ERROR pattern does not mention field `stable2` // OK: stable field is matched let UnstableStruct { stable, stable2, unstable } = UnstableStruct::default(); diff --git a/tests/ui/pattern/usefulness/unstable-gated-patterns.rs b/tests/ui/pattern/usefulness/unstable-gated-patterns.rs index e6db495a14940..3acc0738bbc85 100644 --- a/tests/ui/pattern/usefulness/unstable-gated-patterns.rs +++ b/tests/ui/pattern/usefulness/unstable-gated-patterns.rs @@ -11,7 +11,7 @@ fn main() { UnstableEnum::Stable => {} UnstableEnum::Stable2 => {} } - //~^^^^ non-exhaustive patterns: `UnstableEnum::Unstable` not covered + //~^^^^ ERROR non-exhaustive patterns: `UnstableEnum::Unstable` not covered // Ok: all variants are explicitly matched match UnstableEnum::Stable { diff --git a/tests/ui/pin-macro/cant_access_internals.rs b/tests/ui/pin-macro/cant_access_internals.rs deleted file mode 100644 index 36a47d0fdf937..0000000000000 --- a/tests/ui/pin-macro/cant_access_internals.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ edition:2018 - -use core::{ - marker::PhantomPinned, - mem, - pin::{pin, Pin}, -}; - -fn main() { - let mut phantom_pinned = pin!(PhantomPinned); - mem::take(phantom_pinned.__pointer); //~ ERROR use of unstable library feature `unsafe_pin_internals` -} diff --git a/tests/ui/pin-macro/cant_access_internals.stderr b/tests/ui/pin-macro/cant_access_internals.stderr deleted file mode 100644 index 8ad897bbbb951..0000000000000 --- a/tests/ui/pin-macro/cant_access_internals.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: use of unstable library feature `unsafe_pin_internals` - --> $DIR/cant_access_internals.rs:11:15 - | -LL | mem::take(phantom_pinned.__pointer); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: add `#![feature(unsafe_pin_internals)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs b/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs index 8a0244e8145a7..2771b4717fbf8 100644 --- a/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs +++ b/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs @@ -9,14 +9,14 @@ use core::{ fn function_call_stops_borrow_extension() { let phantom_pinned = identity(pin!(PhantomPinned)); - //~^ ERROR temporary value dropped while borrowed + //~^ ERROR temporary value dropped while borrowed [E0716] stuff(phantom_pinned) } fn promotion_only_works_for_the_innermost_block() { let phantom_pinned = { let phantom_pinned = pin!(PhantomPinned); - //~^ ERROR temporary value dropped while borrowed + //~^ ERROR temporary value dropped while borrowed [E0716] phantom_pinned }; stuff(phantom_pinned) diff --git a/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr b/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr index 9df7f0ffd0c3f..4ecc6370d3caa 100644 --- a/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr +++ b/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr @@ -9,12 +9,8 @@ LL | LL | stuff(phantom_pinned) | -------------- borrow later used here | + = note: consider using a `let` binding to create a longer lived value = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider using a `let` binding to create a longer lived value - | -LL ~ let binding = pin!(PhantomPinned); -LL ~ let phantom_pinned = identity(binding); - | error[E0716]: temporary value dropped while borrowed --> $DIR/lifetime_errors_on_promotion_misusage.rs:18:30 diff --git a/tests/ui/pin-macro/pin_move.rs b/tests/ui/pin-macro/pin_move.rs new file mode 100644 index 0000000000000..0f6d34fad951f --- /dev/null +++ b/tests/ui/pin-macro/pin_move.rs @@ -0,0 +1,26 @@ +//@ edition:2024 + +use core::marker::PhantomPinned; +use core::pin::pin; + +fn a() { + struct NotCopy(T); + #[allow(unused_mut)] + let mut pointee = NotCopy(PhantomPinned); + pin!(pointee); + let _moved = pointee; + //~^ ERROR use of moved value +} + +fn b() { + struct NotCopy(T); + let mut pointee = NotCopy(PhantomPinned); + pin!(*&mut pointee); + //~^ ERROR cannot move + let _moved = pointee; +} + +fn main() { + a(); + b(); +} diff --git a/tests/ui/pin-macro/pin_move.stderr b/tests/ui/pin-macro/pin_move.stderr new file mode 100644 index 0000000000000..3f46602098850 --- /dev/null +++ b/tests/ui/pin-macro/pin_move.stderr @@ -0,0 +1,43 @@ +error[E0382]: use of moved value: `pointee` + --> $DIR/pin_move.rs:11:18 + | +LL | let mut pointee = NotCopy(PhantomPinned); + | ----------- move occurs because `pointee` has type `a::NotCopy`, which does not implement the `Copy` trait +LL | pin!(pointee); + | ------- value moved here +LL | let _moved = pointee; + | ^^^^^^^ value used here after move + | +note: if `a::NotCopy` implemented `Clone`, you could clone the value + --> $DIR/pin_move.rs:7:5 + | +LL | struct NotCopy(T); + | ^^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | pin!(pointee); + | ------- you could clone this value + +error[E0507]: cannot move out of a mutable reference + --> $DIR/pin_move.rs:18:10 + | +LL | pin!(*&mut pointee); + | ^^^^^^^^^^^^^ move occurs because value has type `b::NotCopy`, which does not implement the `Copy` trait + | +note: if `b::NotCopy` implemented `Clone`, you could clone the value + --> $DIR/pin_move.rs:16:5 + | +LL | struct NotCopy(T); + | ^^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type +LL | let mut pointee = NotCopy(PhantomPinned); +LL | pin!(*&mut pointee); + | ------------- you could clone this value +help: consider removing the dereference here + | +LL - pin!(*&mut pointee); +LL + pin!(&mut pointee); + | + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0382, E0507. +For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/pptypedef.rs b/tests/ui/pptypedef.rs index e28d323f88371..d5f43df9d85c5 100644 --- a/tests/ui/pptypedef.rs +++ b/tests/ui/pptypedef.rs @@ -1,11 +1,13 @@ +//@ dont-require-annotations: NOTE + fn let_in(x: T, f: F) where F: FnOnce(T) {} fn main() { let_in(3u32, |i| { assert!(i == 3i32); }); //~^ ERROR mismatched types - //~| expected `u32`, found `i32` + //~| NOTE expected `u32`, found `i32` let_in(3i32, |i| { assert!(i == 3u32); }); //~^ ERROR mismatched types - //~| expected `i32`, found `u32` + //~| NOTE expected `i32`, found `u32` } diff --git a/tests/ui/pptypedef.stderr b/tests/ui/pptypedef.stderr index 96327cfcc653e..a6d673e61c698 100644 --- a/tests/ui/pptypedef.stderr +++ b/tests/ui/pptypedef.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pptypedef.rs:4:37 + --> $DIR/pptypedef.rs:6:37 | LL | let_in(3u32, |i| { assert!(i == 3i32); }); | - ^^^^ expected `u32`, found `i32` @@ -13,7 +13,7 @@ LL + let_in(3u32, |i| { assert!(i == 3u32); }); | error[E0308]: mismatched types - --> $DIR/pptypedef.rs:8:37 + --> $DIR/pptypedef.rs:10:37 | LL | let_in(3i32, |i| { assert!(i == 3u32); }); | - ^^^^ expected `i32`, found `u32` diff --git a/tests/ui/precondition-checks/copy-nonoverlapping.rs b/tests/ui/precondition-checks/copy-nonoverlapping.rs index 81018e4bff3e5..eacaa63e543af 100644 --- a/tests/ui/precondition-checks/copy-nonoverlapping.rs +++ b/tests/ui/precondition-checks/copy-nonoverlapping.rs @@ -3,6 +3,8 @@ //@ error-pattern: unsafe precondition(s) violated: ptr::copy_nonoverlapping requires //@ revisions: null_src null_dst misaligned_src misaligned_dst overlapping +#![allow(invalid_null_arguments)] + use std::ptr; fn main() { diff --git a/tests/ui/precondition-checks/copy.rs b/tests/ui/precondition-checks/copy.rs index 694853f950ab5..1fadd90bf70bd 100644 --- a/tests/ui/precondition-checks/copy.rs +++ b/tests/ui/precondition-checks/copy.rs @@ -3,6 +3,8 @@ //@ error-pattern: unsafe precondition(s) violated: ptr::copy requires //@ revisions: null_src null_dst misaligned_src misaligned_dst +#![allow(invalid_null_arguments)] + use std::ptr; fn main() { diff --git a/tests/ui/precondition-checks/read.rs b/tests/ui/precondition-checks/read.rs index ab9921a0ceebe..d5ab7773987fc 100644 --- a/tests/ui/precondition-checks/read.rs +++ b/tests/ui/precondition-checks/read.rs @@ -2,7 +2,7 @@ //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes //@ error-pattern: unsafe precondition(s) violated: ptr::read requires //@ revisions: null misaligned -//@ ignore-test +//@ ignore-test (unimplemented) use std::ptr; diff --git a/tests/ui/precondition-checks/read_volatile.rs b/tests/ui/precondition-checks/read_volatile.rs index e14881d029037..ada8932c398ce 100644 --- a/tests/ui/precondition-checks/read_volatile.rs +++ b/tests/ui/precondition-checks/read_volatile.rs @@ -3,6 +3,8 @@ //@ error-pattern: unsafe precondition(s) violated: ptr::read_volatile requires //@ revisions: null misaligned +#![allow(invalid_null_arguments)] + use std::ptr; fn main() { diff --git a/tests/ui/precondition-checks/replace.rs b/tests/ui/precondition-checks/replace.rs index 2808cee7b64b1..44afbd8174c03 100644 --- a/tests/ui/precondition-checks/replace.rs +++ b/tests/ui/precondition-checks/replace.rs @@ -3,6 +3,8 @@ //@ error-pattern: unsafe precondition(s) violated: ptr::replace requires //@ revisions: null misaligned +#![allow(invalid_null_arguments)] + use std::ptr; fn main() { diff --git a/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs b/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs index 3801639e2551b..9b9ded69a83b6 100644 --- a/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs +++ b/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs @@ -3,6 +3,8 @@ //@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts_mut requires //@ revisions: null misaligned toolarge +#![allow(invalid_null_arguments)] + fn main() { unsafe { #[cfg(null)] diff --git a/tests/ui/precondition-checks/slice-from-raw-parts.rs b/tests/ui/precondition-checks/slice-from-raw-parts.rs index a3690fa045eb7..96578c1eae58b 100644 --- a/tests/ui/precondition-checks/slice-from-raw-parts.rs +++ b/tests/ui/precondition-checks/slice-from-raw-parts.rs @@ -3,6 +3,8 @@ //@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts requires //@ revisions: null misaligned toolarge +#![allow(invalid_null_arguments)] + fn main() { unsafe { #[cfg(null)] diff --git a/tests/ui/precondition-checks/swap-nonoverlapping.rs b/tests/ui/precondition-checks/swap-nonoverlapping.rs index 52e4a3c870be5..ea1f6f36ad787 100644 --- a/tests/ui/precondition-checks/swap-nonoverlapping.rs +++ b/tests/ui/precondition-checks/swap-nonoverlapping.rs @@ -3,6 +3,8 @@ //@ error-pattern: unsafe precondition(s) violated: ptr::swap_nonoverlapping requires //@ revisions: null_src null_dst misaligned_src misaligned_dst overlapping +#![allow(invalid_null_arguments)] + use std::ptr; fn main() { diff --git a/tests/ui/precondition-checks/write.rs b/tests/ui/precondition-checks/write.rs index f76e776fcf35d..5d6b9586fad7d 100644 --- a/tests/ui/precondition-checks/write.rs +++ b/tests/ui/precondition-checks/write.rs @@ -2,7 +2,7 @@ //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes //@ error-pattern: unsafe precondition(s) violated: ptr::write requires //@ revisions: null misaligned -//@ ignore-test +//@ ignore-test (unimplemented) use std::ptr; diff --git a/tests/ui/precondition-checks/write_bytes.rs b/tests/ui/precondition-checks/write_bytes.rs index 3f64be9d1ee14..be4f5a089f035 100644 --- a/tests/ui/precondition-checks/write_bytes.rs +++ b/tests/ui/precondition-checks/write_bytes.rs @@ -2,7 +2,7 @@ //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes //@ error-pattern: unsafe precondition(s) violated: ptr::write requires //@ revisions: null misaligned -//@ ignore-test +//@ ignore-test (unimplemented) use std::ptr; diff --git a/tests/ui/precondition-checks/write_volatile.rs b/tests/ui/precondition-checks/write_volatile.rs index ac0b89b5ecf2a..0d5ecb014b3d9 100644 --- a/tests/ui/precondition-checks/write_volatile.rs +++ b/tests/ui/precondition-checks/write_volatile.rs @@ -3,6 +3,8 @@ //@ error-pattern: unsafe precondition(s) violated: ptr::write_volatile requires //@ revisions: null misaligned +#![allow(invalid_null_arguments)] + use std::ptr; fn main() { diff --git a/tests/ui/precondition-checks/zero-size-null.rs b/tests/ui/precondition-checks/zero-size-null.rs index 43a81175f9448..55d768fc9e58e 100644 --- a/tests/ui/precondition-checks/zero-size-null.rs +++ b/tests/ui/precondition-checks/zero-size-null.rs @@ -7,8 +7,10 @@ use std::ptr; fn main() { unsafe { + #[expect(invalid_null_arguments)] // false-positive, copy of 0 ptr::copy_nonoverlapping::(ptr::null(), ptr::null_mut(), 0); ptr::copy_nonoverlapping::<()>(ptr::null(), ptr::null_mut(), 123); + #[expect(invalid_null_arguments)] // false-positive, copy of 0 ptr::copy::(ptr::null(), ptr::null_mut(), 0); ptr::copy::<()>(ptr::null(), ptr::null_mut(), 123); ptr::swap::<()>(ptr::null_mut(), ptr::null_mut()); diff --git a/tests/ui/print-calling-conventions.stdout b/tests/ui/print-calling-conventions.stdout index 539b2d5dee405..feee8cc3aa9c3 100644 --- a/tests/ui/print-calling-conventions.stdout +++ b/tests/ui/print-calling-conventions.stdout @@ -19,7 +19,6 @@ riscv-interrupt-m riscv-interrupt-s rust-call rust-cold -rust-intrinsic stdcall stdcall-unwind system diff --git a/tests/ui/print-request/invalid-target.rs b/tests/ui/print-request/invalid-target.rs index 52f09ea73d739..573d5493b25cd 100644 --- a/tests/ui/print-request/invalid-target.rs +++ b/tests/ui/print-request/invalid-target.rs @@ -2,3 +2,5 @@ //@ needs-llvm-components: x86 fn main() {} + +//~? ERROR only Apple targets currently support deployment version info diff --git a/tests/ui/print-request/print-lints-help.rs b/tests/ui/print-request/print-lints-help.rs new file mode 100644 index 0000000000000..9a706a296956e --- /dev/null +++ b/tests/ui/print-request/print-lints-help.rs @@ -0,0 +1,9 @@ +//! Check that we point to `-Whelp` to guide the user to find the list of lints if the user requests +//! `--print=lints` (which is not a valid print request). + +//@ compile-flags: --print lints + +//~? ERROR unknown print request: `lints` +//~? HELP use `-Whelp` to print a list of lints +//~? HELP for more information, see the rustc book +//~? HELP valid print requests are diff --git a/tests/ui/print-request/print-lints-help.stderr b/tests/ui/print-request/print-lints-help.stderr new file mode 100644 index 0000000000000..bc48b2fa73cc7 --- /dev/null +++ b/tests/ui/print-request/print-lints-help.stderr @@ -0,0 +1,6 @@ +error: unknown print request: `lints` + | + = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` + = help: use `-Whelp` to print a list of lints + = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information + diff --git a/tests/ui/print-request/stability.rs b/tests/ui/print-request/stability.rs new file mode 100644 index 0000000000000..54142ce78cefb --- /dev/null +++ b/tests/ui/print-request/stability.rs @@ -0,0 +1,112 @@ +//! Check that we properly gate unstable print requests (`--print=KIND`) and require the user to +//! specify `-Z unstable-options` to use unstable print requests. + +// We don't care about the exact *stdout* output (i.e. what the print requests actually give back) +// for the purposes of this test. +//@ dont-check-compiler-stdout + +// We want to check for the core error message of the unstable print requests being `-Z +// unstable-options`-gated and not the help because the help can change with addition of a new print +// request, which is not important for the purposes of this test. +//@ dont-check-compiler-stderr + +// ======================= +// Unstable print requests +// ======================= + +//@ revisions: all_target_specs_json +//@[all_target_specs_json] compile-flags: --print=all-target-specs-json + +//@ revisions: crate_root_lint_levels +//@[crate_root_lint_levels] compile-flags: --print=crate-root-lint-levels + +//@ revisions: check_cfg +//@[check_cfg] compile-flags: --print=check-cfg + +//@ revisions: supported_crate_types +//@[supported_crate_types] compile-flags: --print=supported-crate-types + +//@ revisions: target_spec_json +//@[target_spec_json] compile-flags: --print=target-spec-json + +// ======================= +// Stable print requests +// ======================= + +//@ revisions: calling_conventions +//@[calling_conventions] compile-flags: --print=calling-conventions +//@[calling_conventions] check-pass + +//@ revisions: cfg +//@[cfg] compile-flags: --print=cfg +//@[cfg] check-pass + +//@ revisions: code_models +//@[code_models] compile-flags: --print=code-models +//@[code_models] check-pass + +//@ revisions: crate_name +//@[crate_name] compile-flags: --print=crate-name +//@[crate_name] check-pass + +// Note: `--print=deployment_target` is only accepted on Apple targets. +//@ revisions: deployment_target +//@[deployment_target] only-apple +//@[deployment_target] compile-flags: --print=deployment-target +//@[deployment_target] check-pass + +//@ revisions: file_names +//@[file_names] compile-flags: --print=file-names +//@[file_names] check-pass + +//@ revisions: host_tuple +//@[host_tuple] compile-flags: --print=host-tuple +//@[host_tuple] check-pass + +//@ revisions: link_args +//@[link_args] compile-flags: --print=link-args +//@[link_args] check-pass + +//@ revisions: native_static_libs +//@[native_static_libs] compile-flags: --print=native-static-libs +//@[native_static_libs] check-pass + +//@ revisions: relocation_models +//@[relocation_models] compile-flags: --print=relocation-models +//@[relocation_models] check-pass + +//@ revisions: split_debuginfo +//@[split_debuginfo] compile-flags: --print=split-debuginfo +//@[split_debuginfo] check-pass + +//@ revisions: stack_protector_strategies +//@[stack_protector_strategies] compile-flags: --print=stack-protector-strategies +//@[stack_protector_strategies] check-pass + +//@ revisions: target_cpus +//@[target_cpus] compile-flags: --print=target-cpus +//@[target_cpus] check-pass + +//@ revisions: target_features +//@[target_features] compile-flags: --print=target-features +//@[target_features] check-pass + +//@ revisions: target_libdir +//@[target_libdir] compile-flags: --print=target-libdir +//@[target_libdir] check-pass + +//@ revisions: target_list +//@[target_list] compile-flags: --print=target-list +//@[target_list] check-pass + +//@ revisions: tls_models +//@[tls_models] compile-flags: --print=tls-models +//@[tls_models] check-pass + +fn main() {} + +//[all_target_specs_json]~? ERROR the `-Z unstable-options` flag must also be passed to enable the `all-target-specs-json` print option +//[crate_root_lint_levels]~? ERROR the `-Z unstable-options` flag must also be passed to enable the `crate-root-lint-levels` print option +//[check_cfg]~? ERROR the `-Z unstable-options` flag must also be passed to enable the `check-cfg` print option +//[supported_crate_types]~? ERROR the `-Z unstable-options` flag must also be passed to enable the `supported-crate-types` print option +//[target_spec_json]~? ERROR the `-Z unstable-options` flag must also be passed to enable the `target-spec-json` print option diff --git a/tests/ui/print-request/supported-crate-types.linux.stdout b/tests/ui/print-request/supported-crate-types.linux.stdout new file mode 100644 index 0000000000000..721adb432e7a1 --- /dev/null +++ b/tests/ui/print-request/supported-crate-types.linux.stdout @@ -0,0 +1,7 @@ +bin +cdylib +dylib +lib +proc-macro +rlib +staticlib diff --git a/tests/ui/print-request/supported-crate-types.musl.stdout b/tests/ui/print-request/supported-crate-types.musl.stdout new file mode 100644 index 0000000000000..1f4b991e49fd2 --- /dev/null +++ b/tests/ui/print-request/supported-crate-types.musl.stdout @@ -0,0 +1,5 @@ +bin +lib +proc-macro +rlib +staticlib diff --git a/tests/ui/print-request/supported-crate-types.rs b/tests/ui/print-request/supported-crate-types.rs new file mode 100644 index 0000000000000..c8b4c0c1a4164 --- /dev/null +++ b/tests/ui/print-request/supported-crate-types.rs @@ -0,0 +1,20 @@ +//! Basic smoke test for `--print=supported-crate-types`, which should print a newline delimited +//! list of crate types supported by the given target. This test cherry-picks a few well-known +//! targets as examples. +//! +//! Tracking issue: + +// ignore-tidy-linelength + +//@ check-pass + +//@ revisions: wasm musl linux + +//@[wasm] compile-flags: --target=wasm32-unknown-unknown --print=supported-crate-types -Zunstable-options +//@[wasm] needs-llvm-components: webassembly + +//@[musl] compile-flags: --target=x86_64-unknown-linux-musl --print=supported-crate-types -Zunstable-options +//@[musl] needs-llvm-components: x86 + +//@[linux] compile-flags: --target=x86_64-unknown-linux-gnu --print=supported-crate-types -Zunstable-options +//@[linux] needs-llvm-components: x86 diff --git a/tests/ui/print-request/supported-crate-types.wasm.stdout b/tests/ui/print-request/supported-crate-types.wasm.stdout new file mode 100644 index 0000000000000..ca1de519598af --- /dev/null +++ b/tests/ui/print-request/supported-crate-types.wasm.stdout @@ -0,0 +1,5 @@ +bin +cdylib +lib +rlib +staticlib diff --git a/tests/ui/auxiliary/impl_privacy_xc_1.rs b/tests/ui/privacy/auxiliary/impl-privacy-cross-crate-1.rs similarity index 100% rename from tests/ui/auxiliary/impl_privacy_xc_1.rs rename to tests/ui/privacy/auxiliary/impl-privacy-cross-crate-1.rs diff --git a/tests/ui/privacy/auxiliary/impl_privacy_xc_2.rs b/tests/ui/privacy/auxiliary/impl-privacy-cross-crate-2.rs similarity index 100% rename from tests/ui/privacy/auxiliary/impl_privacy_xc_2.rs rename to tests/ui/privacy/auxiliary/impl-privacy-cross-crate-2.rs diff --git a/tests/ui/privacy/impl-privacy-cross-crate-1.rs b/tests/ui/privacy/impl-privacy-cross-crate-1.rs new file mode 100644 index 0000000000000..6b2ef3ccc781e --- /dev/null +++ b/tests/ui/privacy/impl-privacy-cross-crate-1.rs @@ -0,0 +1,10 @@ +//@ run-pass +//@ aux-build:impl-privacy-cross-crate-1.rs + + +extern crate impl_privacy_cross_crate_1; + +pub fn main() { + let fish = impl_privacy_cross_crate_1::Fish { x: 1 }; + fish.swim(); +} diff --git a/tests/ui/privacy/impl-privacy-cross-crate-2.rs b/tests/ui/privacy/impl-privacy-cross-crate-2.rs new file mode 100644 index 0000000000000..fa07e6e8cceaf --- /dev/null +++ b/tests/ui/privacy/impl-privacy-cross-crate-2.rs @@ -0,0 +1,10 @@ +//@ run-pass +//@ aux-build:impl-privacy-cross-crate-2.rs + +extern crate impl_privacy_cross_crate_2; + +pub fn main() { + let fish1 = impl_privacy_cross_crate_2::Fish { x: 1 }; + let fish2 = impl_privacy_cross_crate_2::Fish { x: 2 }; + if fish1.eq(&fish2) { println!("yes") } else { println!("no") }; +} diff --git a/tests/ui/privacy/impl-privacy-xc-2.rs b/tests/ui/privacy/impl-privacy-xc-2.rs deleted file mode 100644 index da345ba207201..0000000000000 --- a/tests/ui/privacy/impl-privacy-xc-2.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass -//@ aux-build:impl_privacy_xc_2.rs - -extern crate impl_privacy_xc_2; - -pub fn main() { - let fish1 = impl_privacy_xc_2::Fish { x: 1 }; - let fish2 = impl_privacy_xc_2::Fish { x: 2 }; - if fish1.eq(&fish2) { println!("yes") } else { println!("no") }; -} diff --git a/tests/ui/privacy/privacy-ns1.stderr b/tests/ui/privacy/privacy-ns1.stderr index 3396330c993cf..c782c67e71c6e 100644 --- a/tests/ui/privacy/privacy-ns1.stderr +++ b/tests/ui/privacy/privacy-ns1.stderr @@ -7,6 +7,14 @@ LL | pub struct Baz; LL | Bar(); | ^^^ | +note: these functions exist but are inaccessible + --> $DIR/privacy-ns1.rs:14:5 + | +LL | fn Bar() { } + | ^^^^^^^^ `foo1::Bar`: not accessible +... +LL | fn Bar() { } + | ^^^^^^^^ `foo3::Bar`: not accessible help: a unit struct with a similar name exists | LL - Bar(); @@ -26,6 +34,14 @@ LL | pub struct Baz; LL | Bar(); | ^^^ | +note: these functions exist but are inaccessible + --> $DIR/privacy-ns1.rs:14:5 + | +LL | fn Bar() { } + | ^^^^^^^^ `foo1::Bar`: not accessible +... +LL | fn Bar() { } + | ^^^^^^^^ `foo3::Bar`: not accessible help: a unit struct with a similar name exists | LL - Bar(); @@ -45,6 +61,14 @@ LL | pub struct Baz; LL | let _x: Box; | ^^^ | +note: these traits exist but are inaccessible + --> $DIR/privacy-ns1.rs:25:5 + | +LL | trait Bar { + | ^^^^^^^^^ `foo2::Bar`: not accessible +... +LL | trait Bar { + | ^^^^^^^^^ `foo3::Bar`: not accessible help: a struct with a similar name exists | LL - let _x: Box; diff --git a/tests/ui/privacy/privacy-ns2.stderr b/tests/ui/privacy/privacy-ns2.stderr index ac98682b2b39e..fe1f0c9bd48ca 100644 --- a/tests/ui/privacy/privacy-ns2.stderr +++ b/tests/ui/privacy/privacy-ns2.stderr @@ -4,6 +4,14 @@ error[E0423]: expected function, tuple struct or tuple variant, found trait `Bar LL | Bar(); | ^^^ not a function, tuple struct or tuple variant | +note: these functions exist but are inaccessible + --> $DIR/privacy-ns2.rs:14:5 + | +LL | fn Bar() { } + | ^^^^^^^^ `foo1::Bar`: not accessible +... +LL | fn Bar() { } + | ^^^^^^^^ `foo3::Bar`: not accessible help: consider importing this function instead | LL + use foo2::Bar; @@ -18,6 +26,14 @@ LL | pub struct Baz; LL | Bar(); | ^^^ | +note: these functions exist but are inaccessible + --> $DIR/privacy-ns2.rs:14:5 + | +LL | fn Bar() { } + | ^^^^^^^^ `foo1::Bar`: not accessible +... +LL | fn Bar() { } + | ^^^^^^^^ `foo3::Bar`: not accessible help: a unit struct with a similar name exists | LL - Bar(); @@ -34,6 +50,14 @@ error[E0573]: expected type, found function `Bar` LL | let _x : Bar(); | ^^^^^ not a type | +note: these traits exist but are inaccessible + --> $DIR/privacy-ns2.rs:31:5 + | +LL | trait Bar { + | ^^^^^^^^^ `foo2::Bar`: not accessible +... +LL | trait Bar { + | ^^^^^^^^^ `foo3::Bar`: not accessible help: use `=` if you meant to assign | LL - let _x : Bar(); diff --git a/tests/ui/privacy/privacy2.rs b/tests/ui/privacy/privacy2.rs index c82cd4425596f..e44100a805908 100644 --- a/tests/ui/privacy/privacy2.rs +++ b/tests/ui/privacy/privacy2.rs @@ -1,4 +1,5 @@ //@ compile-flags: -Zdeduplicate-diagnostics=yes +//@ dont-require-annotations: NOTE #![feature(no_core)] #![no_core] // makes debugging this test *a lot* easier (during resolve) @@ -20,7 +21,7 @@ fn test1() { //~^ ERROR requires `sized` lang_item use bar::foo; //~^ ERROR unresolved import `bar::foo` [E0432] - //~| no `foo` in `bar` + //~| NOTE no `foo` in `bar` } fn test2() { diff --git a/tests/ui/privacy/privacy2.stderr b/tests/ui/privacy/privacy2.stderr index 39bab67a6600a..b70134965fa96 100644 --- a/tests/ui/privacy/privacy2.stderr +++ b/tests/ui/privacy/privacy2.stderr @@ -1,34 +1,34 @@ error[E0432]: unresolved import `bar::foo` - --> $DIR/privacy2.rs:21:9 + --> $DIR/privacy2.rs:22:9 | LL | use bar::foo; | ^^^^^^^^ no `foo` in `bar` error[E0603]: function import `foo` is private - --> $DIR/privacy2.rs:28:20 + --> $DIR/privacy2.rs:29:20 | LL | use bar::glob::foo; | ^^^ private function import | note: the function import `foo` is defined here... - --> $DIR/privacy2.rs:12:13 + --> $DIR/privacy2.rs:13:13 | LL | use foo; | ^^^ note: ...and refers to the function `foo` which is defined here - --> $DIR/privacy2.rs:16:1 + --> $DIR/privacy2.rs:17:1 | LL | pub fn foo() {} | ^^^^^^^^^^^^ you could import this directly error: requires `sized` lang_item - --> $DIR/privacy2.rs:16:14 + --> $DIR/privacy2.rs:17:14 | LL | pub fn foo() {} | ^^ error: requires `sized` lang_item - --> $DIR/privacy2.rs:19:12 + --> $DIR/privacy2.rs:20:12 | LL | fn test1() { | ____________^ @@ -39,7 +39,7 @@ LL | | } | |_^ error: requires `sized` lang_item - --> $DIR/privacy2.rs:26:12 + --> $DIR/privacy2.rs:27:12 | LL | fn test2() { | ____________^ @@ -50,7 +50,7 @@ LL | | } | |_^ error: requires `sized` lang_item - --> $DIR/privacy2.rs:32:11 + --> $DIR/privacy2.rs:33:11 | LL | fn main() {} | ^^ diff --git a/tests/ui/privacy/privacy3.rs b/tests/ui/privacy/privacy3.rs index 2bb3c1b3c61dd..ec213285b405f 100644 --- a/tests/ui/privacy/privacy3.rs +++ b/tests/ui/privacy/privacy3.rs @@ -22,7 +22,7 @@ fn test1() { //~^ ERROR requires `sized` lang_item use bar::gpriv; //~^ ERROR unresolved import `bar::gpriv` [E0432] - //~| no `gpriv` in `bar` + //~| NOTE no `gpriv` in `bar` // This should pass because the compiler will insert a fake name binding // for `gpriv` diff --git a/tests/ui/privacy/private-in-public-type-alias-impl-trait.rs b/tests/ui/privacy/private-in-public-type-alias-impl-trait.rs index fd0e07fb9b49e..743ca650fdd42 100644 --- a/tests/ui/privacy/private-in-public-type-alias-impl-trait.rs +++ b/tests/ui/privacy/private-in-public-type-alias-impl-trait.rs @@ -8,6 +8,7 @@ pub type Pub = impl Default; #[derive(Default)] struct Priv; +#[define_opaque(Pub)] fn check() -> Pub { Priv } diff --git a/tests/ui/privacy/private-inferred-type-3.rs b/tests/ui/privacy/private-inferred-type-3.rs index 7bf6bea4b0f6e..820b0cbb30faa 100644 --- a/tests/ui/privacy/private-inferred-type-3.rs +++ b/tests/ui/privacy/private-inferred-type-3.rs @@ -1,17 +1,16 @@ //@ aux-build:private-inferred-type.rs -//@ error-pattern:type `fn() {ext::priv_fn}` is private -//@ error-pattern:static `ext::PRIV_STATIC` is private -//@ 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) -> PubTupleStruct {PubTupleStruct}` is private -//@ error-pattern:type `for<'a> fn(&'a Pub) {Pub::::priv_method}` is private - #![feature(decl_macro)] extern crate private_inferred_type as ext; fn main() { ext::m!(); + //~^ ERROR type `fn() {ext::priv_fn}` is private + //~| ERROR static `ext::PRIV_STATIC` is private + //~| ERROR type `ext::PrivEnum` is private + //~| ERROR type `fn() {::method}` is private + //~| ERROR type `fn(u8) -> ext::PrivTupleStruct {ext::PrivTupleStruct}` is private + //~| ERROR type `fn(u8) -> PubTupleStruct {PubTupleStruct}` is private + //~| ERROR type `for<'a> fn(&'a Pub) {Pub::::priv_method}` is private } diff --git a/tests/ui/privacy/private-inferred-type-3.stderr b/tests/ui/privacy/private-inferred-type-3.stderr index 42faeb4bf341d..0b4899c8d3d1d 100644 --- a/tests/ui/privacy/private-inferred-type-3.stderr +++ b/tests/ui/privacy/private-inferred-type-3.stderr @@ -1,5 +1,5 @@ error: type `fn() {ext::priv_fn}` is private - --> $DIR/private-inferred-type-3.rs:16:5 + --> $DIR/private-inferred-type-3.rs:8:5 | LL | ext::m!(); | ^^^^^^^^^ private type @@ -7,7 +7,7 @@ LL | ext::m!(); = note: this error originates in the macro `ext::m` (in Nightly builds, run with -Z macro-backtrace for more info) error: static `ext::PRIV_STATIC` is private - --> $DIR/private-inferred-type-3.rs:16:5 + --> $DIR/private-inferred-type-3.rs:8:5 | LL | ext::m!(); | ^^^^^^^^^ private static @@ -15,7 +15,7 @@ LL | ext::m!(); = note: this error originates in the macro `ext::m` (in Nightly builds, run with -Z macro-backtrace for more info) error: type `ext::PrivEnum` is private - --> $DIR/private-inferred-type-3.rs:16:5 + --> $DIR/private-inferred-type-3.rs:8:5 | LL | ext::m!(); | ^^^^^^^^^ private type @@ -23,7 +23,7 @@ LL | ext::m!(); = note: this error originates in the macro `ext::m` (in Nightly builds, run with -Z macro-backtrace for more info) error: type `fn() {::method}` is private - --> $DIR/private-inferred-type-3.rs:16:5 + --> $DIR/private-inferred-type-3.rs:8:5 | LL | ext::m!(); | ^^^^^^^^^ private type @@ -31,7 +31,7 @@ LL | ext::m!(); = note: this error originates in the macro `ext::m` (in Nightly builds, run with -Z macro-backtrace for more info) error: type `fn(u8) -> ext::PrivTupleStruct {ext::PrivTupleStruct}` is private - --> $DIR/private-inferred-type-3.rs:16:5 + --> $DIR/private-inferred-type-3.rs:8:5 | LL | ext::m!(); | ^^^^^^^^^ private type @@ -39,7 +39,7 @@ LL | ext::m!(); = note: this error originates in the macro `ext::m` (in Nightly builds, run with -Z macro-backtrace for more info) error: type `fn(u8) -> PubTupleStruct {PubTupleStruct}` is private - --> $DIR/private-inferred-type-3.rs:16:5 + --> $DIR/private-inferred-type-3.rs:8:5 | LL | ext::m!(); | ^^^^^^^^^ private type @@ -47,7 +47,7 @@ LL | ext::m!(); = note: this error originates in the macro `ext::m` (in Nightly builds, run with -Z macro-backtrace for more info) error: type `for<'a> fn(&'a Pub) {Pub::::priv_method}` is private - --> $DIR/private-inferred-type-3.rs:16:5 + --> $DIR/private-inferred-type-3.rs:8:5 | LL | ext::m!(); | ^^^^^^^^^ private type diff --git a/tests/ui/privacy/pub-priv-dep/priv-dep-issue-122756.rs b/tests/ui/privacy/pub-priv-dep/priv-dep-issue-122756.rs index d7ade7f0e96c5..009cc66f3f74e 100644 --- a/tests/ui/privacy/pub-priv-dep/priv-dep-issue-122756.rs +++ b/tests/ui/privacy/pub-priv-dep/priv-dep-issue-122756.rs @@ -5,7 +5,7 @@ #![deny(exported_private_dependencies)] // Ensure the libbar.rlib is loaded first. If the command line parameter `--extern foo` does not -// exist, previus version would fail to compile +// exist, previous version would fail to compile #![crate_type = "rlib"] extern crate bar; extern crate foo; diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs index 112eaf528be27..877029f3de37a 100644 --- a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs +++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs @@ -77,15 +77,14 @@ pub type Alias = OtherType; pub struct PublicWithPrivateImpl; -// FIXME: This should trigger. -// See https://github.com/rust-lang/rust/issues/71043 impl OtherTrait for PublicWithPrivateImpl {} +//~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface pub trait PubTraitOnPrivate {} -// FIXME: This should trigger. -// See https://github.com/rust-lang/rust/issues/71043 impl PubTraitOnPrivate for OtherType {} +//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface +//~| ERROR type `OtherType` from private dependency 'priv_dep' in public interface pub struct AllowedPrivType { #[allow(exported_private_dependencies)] diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr index 53d461a5774a0..adfe13424cdf3 100644 --- a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr +++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr @@ -70,5 +70,25 @@ error: type `OtherType` from private dependency 'priv_dep' in public interface LL | pub type Alias = OtherType; | ^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: trait `OtherTrait` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:80:1 + | +LL | impl OtherTrait for PublicWithPrivateImpl {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:85:1 + | +LL | impl PubTraitOnPrivate for OtherType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:85:1 + | +LL | impl PubTraitOnPrivate for OtherType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 14 previous errors diff --git a/tests/ui/privacy/where-pub-type-impls-priv-trait.rs b/tests/ui/privacy/where-pub-type-impls-priv-trait.rs index 1ebc396cdf5b1..57548f75d5e49 100644 --- a/tests/ui/privacy/where-pub-type-impls-priv-trait.rs +++ b/tests/ui/privacy/where-pub-type-impls-priv-trait.rs @@ -3,7 +3,6 @@ // priv-in-pub lint tests where the private trait bounds a public type #![crate_type = "lib"] -#![feature(generic_const_exprs)] #![allow(incomplete_features)] struct PrivTy; diff --git a/tests/ui/privacy/where-pub-type-impls-priv-trait.stderr b/tests/ui/privacy/where-pub-type-impls-priv-trait.stderr index ee79ce3f5d750..33f82a3a4fec3 100644 --- a/tests/ui/privacy/where-pub-type-impls-priv-trait.stderr +++ b/tests/ui/privacy/where-pub-type-impls-priv-trait.stderr @@ -1,30 +1,30 @@ warning: trait `PrivTr` is more private than the item `S` - --> $DIR/where-pub-type-impls-priv-trait.rs:20:1 + --> $DIR/where-pub-type-impls-priv-trait.rs:19:1 | LL | pub struct S | ^^^^^^^^^^^^ struct `S` is reachable at visibility `pub` | note: but trait `PrivTr` is only usable at visibility `pub(crate)` - --> $DIR/where-pub-type-impls-priv-trait.rs:10:1 + --> $DIR/where-pub-type-impls-priv-trait.rs:9:1 | LL | trait PrivTr {} | ^^^^^^^^^^^^ = note: `#[warn(private_bounds)]` on by default warning: trait `PrivTr` is more private than the item `E` - --> $DIR/where-pub-type-impls-priv-trait.rs:27:1 + --> $DIR/where-pub-type-impls-priv-trait.rs:26:1 | LL | pub enum E | ^^^^^^^^^^ enum `E` is reachable at visibility `pub` | note: but trait `PrivTr` is only usable at visibility `pub(crate)` - --> $DIR/where-pub-type-impls-priv-trait.rs:10:1 + --> $DIR/where-pub-type-impls-priv-trait.rs:9:1 | LL | trait PrivTr {} | ^^^^^^^^^^^^ warning: trait `PrivTr` is more private than the item `f` - --> $DIR/where-pub-type-impls-priv-trait.rs:34:1 + --> $DIR/where-pub-type-impls-priv-trait.rs:33:1 | LL | / pub fn f() LL | | @@ -33,13 +33,13 @@ LL | | PubTy: PrivTr | |_________________^ function `f` is reachable at visibility `pub` | note: but trait `PrivTr` is only usable at visibility `pub(crate)` - --> $DIR/where-pub-type-impls-priv-trait.rs:10:1 + --> $DIR/where-pub-type-impls-priv-trait.rs:9:1 | LL | trait PrivTr {} | ^^^^^^^^^^^^ warning: trait `PrivTr` is more private than the item `S` - --> $DIR/where-pub-type-impls-priv-trait.rs:41:1 + --> $DIR/where-pub-type-impls-priv-trait.rs:40:1 | LL | / impl S LL | | @@ -48,13 +48,13 @@ LL | | PubTy: PrivTr | |_________________^ implementation `S` is reachable at visibility `pub` | note: but trait `PrivTr` is only usable at visibility `pub(crate)` - --> $DIR/where-pub-type-impls-priv-trait.rs:10:1 + --> $DIR/where-pub-type-impls-priv-trait.rs:9:1 | LL | trait PrivTr {} | ^^^^^^^^^^^^ warning: trait `PrivTr` is more private than the item `S::f` - --> $DIR/where-pub-type-impls-priv-trait.rs:46:5 + --> $DIR/where-pub-type-impls-priv-trait.rs:45:5 | LL | / pub fn f() LL | | @@ -63,7 +63,7 @@ LL | | PubTy: PrivTr | |_____________________^ associated function `S::f` is reachable at visibility `pub` | note: but trait `PrivTr` is only usable at visibility `pub(crate)` - --> $DIR/where-pub-type-impls-priv-trait.rs:10:1 + --> $DIR/where-pub-type-impls-priv-trait.rs:9:1 | LL | trait PrivTr {} | ^^^^^^^^^^^^ diff --git a/tests/ui/proc-macro/attr-complex-fn.stdout b/tests/ui/proc-macro/attr-complex-fn.stdout index 7c23d1ecae45d..9bbb746bb4d62 100644 --- a/tests/ui/proc-macro/attr-complex-fn.stdout +++ b/tests/ui/proc-macro/attr-complex-fn.stdout @@ -77,7 +77,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ span: $DIR/attr-complex-fn.rs:19:42: 19:44 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): impl MyTrait for MyStruct<{ true }> { #![rustc_dummy] } +PRINT-ATTR INPUT (DISPLAY): impl MyTrait for MyStruct<{true}> { #![rustc_dummy] } PRINT-ATTR RE-COLLECTED (DISPLAY): impl < T > MyTrait < T > for MyStruct < { true } > { #![rustc_dummy] } PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): impl < T > MyTrait < T > for MyStruct < { true } > { #! [rustc_dummy] } PRINT-ATTR INPUT (DEBUG): TokenStream [ diff --git a/tests/ui/proc-macro/attribute-after-derive.rs b/tests/ui/proc-macro/attribute-after-derive.rs index f2e2eb12a195e..382ef1f6ddfd3 100644 --- a/tests/ui/proc-macro/attribute-after-derive.rs +++ b/tests/ui/proc-macro/attribute-after-derive.rs @@ -14,14 +14,14 @@ extern crate test_macros; #[print_attr] #[derive(Print)] struct AttributeDerive { - #[cfg(FALSE)] + #[cfg(false)] field: u8, } #[derive(Print)] #[print_attr] struct DeriveAttribute { - #[cfg(FALSE)] + #[cfg(false)] field: u8, } diff --git a/tests/ui/proc-macro/attribute-after-derive.stdout b/tests/ui/proc-macro/attribute-after-derive.stdout index 6d9531df8caf2..bc0fc6dc1af14 100644 --- a/tests/ui/proc-macro/attribute-after-derive.stdout +++ b/tests/ui/proc-macro/attribute-after-derive.stdout @@ -1,5 +1,5 @@ -PRINT-ATTR INPUT (DISPLAY): #[derive(Print)] struct AttributeDerive { #[cfg(FALSE)] field: u8, } -PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): #[derive(Print)] struct AttributeDerive { #[cfg(FALSE)] field : u8, } +PRINT-ATTR INPUT (DISPLAY): #[derive(Print)] struct AttributeDerive { #[cfg(false)] field: u8, } +PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): #[derive(Print)] struct AttributeDerive { #[cfg(false)] field : u8, } PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', @@ -53,7 +53,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/attribute-after-derive.rs:17:11: 17:16 (#0), }, ], @@ -131,8 +131,8 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ span: $DIR/attribute-after-derive.rs:23:24: 26:2 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): struct DeriveAttribute { #[cfg(FALSE)] field: u8, } -PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): struct DeriveAttribute { #[cfg(FALSE)] field : u8, } +PRINT-ATTR INPUT (DISPLAY): struct DeriveAttribute { #[cfg(false)] field: u8, } +PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): struct DeriveAttribute { #[cfg(false)] field : u8, } PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -161,7 +161,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/attribute-after-derive.rs:24:11: 24:16 (#0), }, ], diff --git a/tests/ui/proc-macro/auxiliary/expand-expr.rs b/tests/ui/proc-macro/auxiliary/expand-expr.rs index 78c9fa75d9ff2..14efc3c6b9f17 100644 --- a/tests/ui/proc-macro/auxiliary/expand-expr.rs +++ b/tests/ui/proc-macro/auxiliary/expand-expr.rs @@ -3,9 +3,10 @@ extern crate proc_macro; -use proc_macro::*; use std::str::FromStr; +use proc_macro::*; + // Flatten the TokenStream, removing any toplevel `Delimiter::None`s for // comparison. fn flatten(ts: TokenStream) -> Vec { @@ -136,9 +137,8 @@ pub fn check_expand_expr_file(ts: TokenStream) -> TokenStream { .to_string(); assert_eq!(input_t, parse_t); - // Check that the literal matches `Span::call_site().source_file().path()` - let expect_t = - Literal::string(&Span::call_site().source_file().path().to_string_lossy()).to_string(); + // Check that the literal matches `Span::call_site().file()` + let expect_t = Literal::string(&Span::call_site().file()).to_string(); assert_eq!(input_t, expect_t); TokenStream::new() diff --git a/tests/ui/proc-macro/auxiliary/macro-only-syntax.rs b/tests/ui/proc-macro/auxiliary/macro-only-syntax.rs index 4971de284b760..11e1910288eee 100644 --- a/tests/ui/proc-macro/auxiliary/macro-only-syntax.rs +++ b/tests/ui/proc-macro/auxiliary/macro-only-syntax.rs @@ -79,7 +79,7 @@ fn check_useful_span(token: TokenTree, expected_filename: &str) { let span = token.span(); assert!(span.column() < span.end().column()); - let source_path = span.source_file().path(); + let source_path = span.local_file().unwrap(); let filename = source_path.components().last().unwrap(); assert_eq!(filename, Component::Normal(expected_filename.as_ref())); } diff --git a/tests/ui/proc-macro/auxiliary/span-api-tests.rs b/tests/ui/proc-macro/auxiliary/span-api-tests.rs index 99db66ed6a94c..036f2e3ac3f5e 100644 --- a/tests/ui/proc-macro/auxiliary/span-api-tests.rs +++ b/tests/ui/proc-macro/auxiliary/span-api-tests.rs @@ -11,20 +11,9 @@ pub fn reemit(input: TokenStream) -> TokenStream { } #[proc_macro] -pub fn assert_fake_source_file(input: TokenStream) -> TokenStream { +pub fn assert_local_file(input: TokenStream) -> TokenStream { for tk in input { - let source_file = tk.span().source_file(); - assert!(!source_file.is_real(), "Source file is real: {:?}", source_file); - } - - "".parse().unwrap() -} - -#[proc_macro] -pub fn assert_source_file(input: TokenStream) -> TokenStream { - for tk in input { - let source_file = tk.span().source_file(); - assert!(source_file.is_real(), "Source file is not real: {:?}", source_file); + assert!(tk.span().local_file().is_some(), "No local file for span: {:?}", tk.span()); } "".parse().unwrap() diff --git a/tests/ui/proc-macro/bad-projection.stderr b/tests/ui/proc-macro/bad-projection.stderr index aa6b3da6da995..f2981499367b8 100644 --- a/tests/ui/proc-macro/bad-projection.stderr +++ b/tests/ui/proc-macro/bad-projection.stderr @@ -10,6 +10,19 @@ help: this trait has no implementations, consider adding one LL | trait Project { | ^^^^^^^^^^^^^ +error[E0277]: the trait bound `(): Project` is not satisfied + --> $DIR/bad-projection.rs:14:17 + | +LL | pub fn uwu() -> <() as Project>::Assoc {} + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Project` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/bad-projection.rs:9:1 + | +LL | trait Project { + | ^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0593]: function is expected to take 1 argument, but it takes 0 arguments --> $DIR/bad-projection.rs:14:1 | @@ -47,19 +60,6 @@ help: this trait has no implementations, consider adding one LL | trait Project { | ^^^^^^^^^^^^^ -error[E0277]: the trait bound `(): Project` is not satisfied - --> $DIR/bad-projection.rs:14:17 - | -LL | pub fn uwu() -> <() as Project>::Assoc {} - | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Project` is not implemented for `()` - | -help: this trait has no implementations, consider adding one - --> $DIR/bad-projection.rs:9:1 - | -LL | trait Project { - | ^^^^^^^^^^^^^ - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error: aborting due to 5 previous errors Some errors have detailed explanations: E0277, E0593. diff --git a/tests/ui/proc-macro/cfg-attr-trace.rs b/tests/ui/proc-macro/cfg-attr-trace.rs new file mode 100644 index 0000000000000..412c65bed1d88 --- /dev/null +++ b/tests/ui/proc-macro/cfg-attr-trace.rs @@ -0,0 +1,22 @@ +// Ensure that `cfg_attr_trace` attributes aren't observable by proc-macros. + +//@ check-pass +//@ proc-macro: test-macros.rs + +#![feature(cfg_eval)] + +#[macro_use] +extern crate test_macros; + +#[cfg_eval] +#[test_macros::print_attr] +#[cfg_attr(false, test_macros::print_attr)] +#[cfg_attr(true, test_macros::print_attr)] +struct S; + +#[cfg_eval] +#[test_macros::print_attr] +#[cfg(true)] +struct Z; + +fn main() {} diff --git a/tests/ui/proc-macro/cfg-attr-trace.stdout b/tests/ui/proc-macro/cfg-attr-trace.stdout new file mode 100644 index 0000000000000..33bcfe5d69b7e --- /dev/null +++ b/tests/ui/proc-macro/cfg-attr-trace.stdout @@ -0,0 +1,78 @@ +PRINT-ATTR INPUT (DISPLAY): #[test_macros::print_attr] struct S; +PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): #[test_macros :: print_attr] struct S; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: #0 bytes(271..272), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "test_macros", + span: #0 bytes(288..299), + }, + Punct { + ch: ':', + spacing: Joint, + span: #0 bytes(299..300), + }, + Punct { + ch: ':', + spacing: Alone, + span: #0 bytes(300..301), + }, + Ident { + ident: "print_attr", + span: #0 bytes(301..311), + }, + ], + span: #0 bytes(272..313), + }, + Ident { + ident: "struct", + span: #0 bytes(314..320), + }, + Ident { + ident: "S", + span: #0 bytes(321..322), + }, + Punct { + ch: ';', + spacing: Alone, + span: #0 bytes(322..323), + }, +] +PRINT-ATTR INPUT (DISPLAY): struct S; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "struct", + span: #0 bytes(314..320), + }, + Ident { + ident: "S", + span: #0 bytes(321..322), + }, + Punct { + ch: ';', + spacing: Alone, + span: #0 bytes(322..323), + }, +] +PRINT-ATTR INPUT (DISPLAY): struct Z; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "struct", + span: #0 bytes(377..383), + }, + Ident { + ident: "Z", + span: #0 bytes(384..385), + }, + Punct { + ch: ';', + spacing: Alone, + span: #0 bytes(385..386), + }, +] diff --git a/tests/ui/proc-macro/cfg-eval-fail.rs b/tests/ui/proc-macro/cfg-eval-fail.rs index a259aa2e6ec0c..a94dcd2837811 100644 --- a/tests/ui/proc-macro/cfg-eval-fail.rs +++ b/tests/ui/proc-macro/cfg-eval-fail.rs @@ -2,6 +2,6 @@ #![feature(stmt_expr_attributes)] fn main() { - let _ = #[cfg_eval] #[cfg(FALSE)] 0; + let _ = #[cfg_eval] #[cfg(false)] 0; //~^ ERROR removing an expression is not supported in this position } diff --git a/tests/ui/proc-macro/cfg-eval-fail.stderr b/tests/ui/proc-macro/cfg-eval-fail.stderr index 945ad46bf33c3..7f21e4646b1cc 100644 --- a/tests/ui/proc-macro/cfg-eval-fail.stderr +++ b/tests/ui/proc-macro/cfg-eval-fail.stderr @@ -1,7 +1,7 @@ error: removing an expression is not supported in this position --> $DIR/cfg-eval-fail.rs:5:25 | -LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0; +LL | let _ = #[cfg_eval] #[cfg(false)] 0; | ^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/cfg-eval-inner.rs b/tests/ui/proc-macro/cfg-eval-inner.rs index 7493f3ea52366..dc4efd6ba15fb 100644 --- a/tests/ui/proc-macro/cfg-eval-inner.rs +++ b/tests/ui/proc-macro/cfg-eval-inner.rs @@ -32,7 +32,7 @@ impl Foo<[u8; { #![cfg_attr(not(FALSE), rustc_dummy(evaluated_attr))] fn bar() { - #[cfg(FALSE)] let a = 1; + #[cfg(false)] let a = 1; } } diff --git a/tests/ui/proc-macro/cfg-eval.rs b/tests/ui/proc-macro/cfg-eval.rs index 1d9b4f23ea5c4..ddf3708059608 100644 --- a/tests/ui/proc-macro/cfg-eval.rs +++ b/tests/ui/proc-macro/cfg-eval.rs @@ -15,7 +15,7 @@ extern crate test_macros; #[cfg_eval] #[print_attr] struct S1 { - #[cfg(FALSE)] + #[cfg(false)] field_false: u8, #[cfg(all(/*true*/))] #[cfg_attr(FALSE, unknown_attr)] @@ -24,7 +24,7 @@ struct S1 { } #[cfg_eval] -#[cfg(FALSE)] +#[cfg(false)] struct S2 {} fn main() { @@ -33,5 +33,5 @@ fn main() { // expression. `#[cfg]` is not supported inside parenthesized expressions, so this will // produce an error when attribute collection runs. let _ = #[cfg_eval] #[print_attr] #[cfg_attr(not(FALSE), rustc_dummy)] - (#[cfg(FALSE)] 0, #[cfg(all(/*true*/))] 1,); + (#[cfg(false)] 0, #[cfg(all(/*true*/))] 1,); } diff --git a/tests/ui/proc-macro/derive-cfg-nested-tokens.rs b/tests/ui/proc-macro/derive-cfg-nested-tokens.rs index 7d4e8d8373d65..ec6aba0d1ea33 100644 --- a/tests/ui/proc-macro/derive-cfg-nested-tokens.rs +++ b/tests/ui/proc-macro/derive-cfg-nested-tokens.rs @@ -15,7 +15,7 @@ struct S { // - on eagerly configured `S` (from `impl Copy`), only 11 should be printed // - on non-configured `S` (from `struct S`), both 10 and 11 should be printed field: [u8; #[print_attr] { - #[cfg(FALSE)] { 10 } + #[cfg(false)] { 10 } #[cfg(not(FALSE))] { 11 } }], } diff --git a/tests/ui/proc-macro/derive-cfg-nested-tokens.stdout b/tests/ui/proc-macro/derive-cfg-nested-tokens.stdout index 05bf21ee8f9bf..9dbddc95902dc 100644 --- a/tests/ui/proc-macro/derive-cfg-nested-tokens.stdout +++ b/tests/ui/proc-macro/derive-cfg-nested-tokens.stdout @@ -54,7 +54,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ span: #0 bytes(452..523), }, ] -PRINT-ATTR INPUT (DISPLAY): { #[cfg(FALSE)] { 10 } #[cfg(not(FALSE))] { 11 } } +PRINT-ATTR INPUT (DISPLAY): { #[cfg(false)] { 10 } #[cfg(not(FALSE))] { 11 } } PRINT-ATTR INPUT (DEBUG): TokenStream [ Group { delimiter: Brace, @@ -75,7 +75,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: #0 bytes(468..473), }, ], diff --git a/tests/ui/proc-macro/expand-to-derive.rs b/tests/ui/proc-macro/expand-to-derive.rs index 0e38e471980bc..05c8e32624313 100644 --- a/tests/ui/proc-macro/expand-to-derive.rs +++ b/tests/ui/proc-macro/expand-to-derive.rs @@ -14,7 +14,7 @@ macro_rules! expand_to_derive { ($item:item) => { #[derive(Print)] struct Foo { - #[cfg(FALSE)] removed: bool, + #[cfg(false)] removed: bool, field: [bool; { $item 0 @@ -26,7 +26,7 @@ macro_rules! expand_to_derive { expand_to_derive! { #[cfg_attr(not(FALSE), rustc_dummy)] struct Inner { - #[cfg(FALSE)] removed_inner_field: bool, + #[cfg(false)] removed_inner_field: bool, other_inner_field: u8, } } diff --git a/tests/ui/proc-macro/expand-to-derive.stdout b/tests/ui/proc-macro/expand-to-derive.stdout index 81fc52ea22d7b..3a16f23ecf23c 100644 --- a/tests/ui/proc-macro/expand-to-derive.stdout +++ b/tests/ui/proc-macro/expand-to-derive.stdout @@ -44,52 +44,58 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Group { delimiter: Brace, stream: TokenStream [ - Punct { - ch: '#', - spacing: Alone, - span: $DIR/expand-to-derive.rs:27:5: 27:6 (#0), - }, - Group { - delimiter: Bracket, - stream: TokenStream [ - Ident { - ident: "rustc_dummy", - span: $DIR/expand-to-derive.rs:27:28: 27:39 (#0), - }, - ], - span: $DIR/expand-to-derive.rs:27:6: 27:41 (#0), - }, - Ident { - ident: "struct", - span: $DIR/expand-to-derive.rs:28:5: 28:11 (#0), - }, - Ident { - ident: "Inner", - span: $DIR/expand-to-derive.rs:28:12: 28:17 (#0), - }, Group { - delimiter: Brace, + delimiter: None, stream: TokenStream [ - Ident { - ident: "other_inner_field", - span: $DIR/expand-to-derive.rs:30:9: 30:26 (#0), - }, Punct { - ch: ':', + ch: '#', spacing: Alone, - span: $DIR/expand-to-derive.rs:30:26: 30:27 (#0), + span: $DIR/expand-to-derive.rs:27:5: 27:6 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/expand-to-derive.rs:27:28: 27:39 (#0), + }, + ], + span: $DIR/expand-to-derive.rs:27:6: 27:41 (#0), }, Ident { - ident: "u8", - span: $DIR/expand-to-derive.rs:30:28: 30:30 (#0), + ident: "struct", + span: $DIR/expand-to-derive.rs:28:5: 28:11 (#0), }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/expand-to-derive.rs:30:30: 30:31 (#0), + Ident { + ident: "Inner", + span: $DIR/expand-to-derive.rs:28:12: 28:17 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "other_inner_field", + span: $DIR/expand-to-derive.rs:30:9: 30:26 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/expand-to-derive.rs:30:26: 30:27 (#0), + }, + Ident { + ident: "u8", + span: $DIR/expand-to-derive.rs:30:28: 30:30 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/expand-to-derive.rs:30:30: 30:31 (#0), + }, + ], + span: $DIR/expand-to-derive.rs:28:18: 31:6 (#0), }, ], - span: $DIR/expand-to-derive.rs:28:18: 31:6 (#0), + span: $DIR/expand-to-derive.rs:19:17: 19:22 (#3), }, Literal { kind: Integer, diff --git a/tests/ui/proc-macro/export-macro.rs b/tests/ui/proc-macro/export-macro.rs index e6001d06f0f32..33bf8cfb2552f 100644 --- a/tests/ui/proc-macro/export-macro.rs +++ b/tests/ui/proc-macro/export-macro.rs @@ -1,11 +1,9 @@ -//@ error-pattern: cannot export macro_rules! macros from a `proc-macro` crate - //@ force-host //@ no-prefer-dynamic #![crate_type = "proc-macro"] #[macro_export] -macro_rules! foo { +macro_rules! foo { //~ ERROR cannot export macro_rules! macros from a `proc-macro` crate type ($e:expr) => ($e) } diff --git a/tests/ui/proc-macro/export-macro.stderr b/tests/ui/proc-macro/export-macro.stderr index 410770eca08a3..be586d504598b 100644 --- a/tests/ui/proc-macro/export-macro.stderr +++ b/tests/ui/proc-macro/export-macro.stderr @@ -1,5 +1,5 @@ error: cannot export macro_rules! macros from a `proc-macro` crate type currently - --> $DIR/export-macro.rs:9:1 + --> $DIR/export-macro.rs:7:1 | LL | macro_rules! foo { | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/proc-macro/inner-attr-non-inline-mod.rs b/tests/ui/proc-macro/inner-attr-non-inline-mod.rs index d4336a7f3e1e0..2dcdbf3c40226 100644 --- a/tests/ui/proc-macro/inner-attr-non-inline-mod.rs +++ b/tests/ui/proc-macro/inner-attr-non-inline-mod.rs @@ -1,6 +1,4 @@ //@ compile-flags: -Z span-debug -//@ error-pattern:custom inner attributes are unstable -//@ error-pattern:inner macro attributes are unstable //@ proc-macro: test-macros.rs #![no_std] // Don't load unnecessary hygiene information from std @@ -15,3 +13,6 @@ mod module_with_attrs; //~| ERROR custom inner attributes are unstable fn main() {} + +//~? ERROR custom inner attributes are unstable +//~? ERROR inner macro attributes are unstable diff --git a/tests/ui/proc-macro/inner-attr-non-inline-mod.stderr b/tests/ui/proc-macro/inner-attr-non-inline-mod.stderr index 025eec248188b..c0a9385f4c688 100644 --- a/tests/ui/proc-macro/inner-attr-non-inline-mod.stderr +++ b/tests/ui/proc-macro/inner-attr-non-inline-mod.stderr @@ -19,7 +19,7 @@ LL | #![print_attr] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: non-inline modules in proc macro input are unstable - --> $DIR/inner-attr-non-inline-mod.rs:13:1 + --> $DIR/inner-attr-non-inline-mod.rs:11:1 | LL | mod module_with_attrs; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | mod module_with_attrs; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: custom inner attributes are unstable - --> $DIR/inner-attr-non-inline-mod.rs:13:1 + --> $DIR/inner-attr-non-inline-mod.rs:11:1 | LL | mod module_with_attrs; | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/proc-macro/inner-attr-non-inline-mod.stdout b/tests/ui/proc-macro/inner-attr-non-inline-mod.stdout index 450542f68c65c..219794a8eb8cd 100644 --- a/tests/ui/proc-macro/inner-attr-non-inline-mod.stdout +++ b/tests/ui/proc-macro/inner-attr-non-inline-mod.stdout @@ -4,35 +4,35 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "deny", - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "unused_attributes", - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, ], - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, ], - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, Ident { ident: "mod", - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, Ident { ident: "module_with_attrs", - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, Group { delimiter: Brace, @@ -40,38 +40,38 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustfmt", - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, Punct { ch: ':', spacing: Joint, - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, Ident { ident: "skip", - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, ], - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, ], - span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:11:1: 11:23 (#0), }, ] diff --git a/tests/ui/proc-macro/inner-attrs.rs b/tests/ui/proc-macro/inner-attrs.rs index 34c37ddfac35d..ca4b2029a3384 100644 --- a/tests/ui/proc-macro/inner-attrs.rs +++ b/tests/ui/proc-macro/inner-attrs.rs @@ -1,6 +1,5 @@ // gate-test-custom_inner_attributes -//@ compile-flags: -Z span-debug --error-format human -//@ error-pattern:expected non-macro inner attribute +//@ compile-flags: -Z span-debug //@ proc-macro: test-macros.rs //@ edition:2018 @@ -63,23 +62,27 @@ fn bar() { for _ in &[true] { #![print_attr] + //~^ ERROR expected non-macro inner attribute, found attribute macro `print_attr` } let _ = { #![print_attr] + //~^ ERROR expected non-macro inner attribute, found attribute macro `print_attr` }; let _ = async { #![print_attr] + //~^ ERROR expected non-macro inner attribute, found attribute macro `print_attr` }; { #![print_attr] + //~^ ERROR expected non-macro inner attribute, found attribute macro `print_attr` }; } -extern { +extern { //~ WARN `extern` declarations without an explicit ABI are deprecated fn weird_extern() { #![print_target_and_args_consume(tenth)] } diff --git a/tests/ui/proc-macro/inner-attrs.stderr b/tests/ui/proc-macro/inner-attrs.stderr index 8b5fec1b4c37e..54cccae8da082 100644 --- a/tests/ui/proc-macro/inner-attrs.stderr +++ b/tests/ui/proc-macro/inner-attrs.stderr @@ -1,5 +1,5 @@ error: expected non-macro inner attribute, found attribute macro `print_attr` - --> $DIR/inner-attrs.rs:65:12 + --> $DIR/inner-attrs.rs:64:12 | LL | #![print_attr] | ^^^^^^^^^^ not a non-macro inner attribute @@ -11,19 +11,19 @@ LL | #![print_attr] | ^^^^^^^^^^ not a non-macro inner attribute error: expected non-macro inner attribute, found attribute macro `print_attr` - --> $DIR/inner-attrs.rs:73:12 + --> $DIR/inner-attrs.rs:74:12 | LL | #![print_attr] | ^^^^^^^^^^ not a non-macro inner attribute error: expected non-macro inner attribute, found attribute macro `print_attr` - --> $DIR/inner-attrs.rs:77:12 + --> $DIR/inner-attrs.rs:79:12 | LL | #![print_attr] | ^^^^^^^^^^ not a non-macro inner attribute -warning: extern declarations without an explicit ABI are deprecated - --> $DIR/inner-attrs.rs:82:1 +warning: `extern` declarations without an explicit ABI are deprecated + --> $DIR/inner-attrs.rs:85:1 | LL | extern { | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` diff --git a/tests/ui/proc-macro/inner-attrs.stdout b/tests/ui/proc-macro/inner-attrs.stdout index ed47ee2cf5ac4..4496f7b90c4e3 100644 --- a/tests/ui/proc-macro/inner-attrs.stdout +++ b/tests/ui/proc-macro/inner-attrs.stdout @@ -2,7 +2,7 @@ PRINT-ATTR_ARGS INPUT (DISPLAY): first PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "first", - span: $DIR/inner-attrs.rs:18:25: 18:30 (#0), + span: $DIR/inner-attrs.rs:17:25: 17:30 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): #[print_target_and_args(second)] fn foo() @@ -13,40 +13,40 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/inner-attrs.rs:19:1: 19:2 (#0), + span: $DIR/inner-attrs.rs:18:1: 18:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:19:3: 19:24 (#0), + span: $DIR/inner-attrs.rs:18:3: 18:24 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second", - span: $DIR/inner-attrs.rs:19:25: 19:31 (#0), + span: $DIR/inner-attrs.rs:18:25: 18:31 (#0), }, ], - span: $DIR/inner-attrs.rs:19:24: 19:32 (#0), + span: $DIR/inner-attrs.rs:18:24: 18:32 (#0), }, ], - span: $DIR/inner-attrs.rs:19:2: 19:33 (#0), + span: $DIR/inner-attrs.rs:18:2: 18:33 (#0), }, Ident { ident: "fn", - span: $DIR/inner-attrs.rs:20:1: 20:3 (#0), + span: $DIR/inner-attrs.rs:19:1: 19:3 (#0), }, Ident { ident: "foo", - span: $DIR/inner-attrs.rs:20:4: 20:7 (#0), + span: $DIR/inner-attrs.rs:19:4: 19:7 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:20:7: 20:9 (#0), + span: $DIR/inner-attrs.rs:19:7: 19:9 (#0), }, Group { delimiter: Brace, @@ -54,72 +54,72 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:21:5: 21:6 (#0), + span: $DIR/inner-attrs.rs:20:5: 20:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:21:6: 21:7 (#0), + span: $DIR/inner-attrs.rs:20:6: 20:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:21:8: 21:29 (#0), + span: $DIR/inner-attrs.rs:20:8: 20:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "third", - span: $DIR/inner-attrs.rs:21:30: 21:35 (#0), + span: $DIR/inner-attrs.rs:20:30: 20:35 (#0), }, ], - span: $DIR/inner-attrs.rs:21:29: 21:36 (#0), + span: $DIR/inner-attrs.rs:20:29: 20:36 (#0), }, ], - span: $DIR/inner-attrs.rs:21:7: 21:37 (#0), + span: $DIR/inner-attrs.rs:20:7: 20:37 (#0), }, Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:22:5: 22:6 (#0), + span: $DIR/inner-attrs.rs:21:5: 21:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:22:6: 22:7 (#0), + span: $DIR/inner-attrs.rs:21:6: 21:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:22:8: 22:29 (#0), + span: $DIR/inner-attrs.rs:21:8: 21:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "fourth", - span: $DIR/inner-attrs.rs:22:30: 22:36 (#0), + span: $DIR/inner-attrs.rs:21:30: 21:36 (#0), }, ], - span: $DIR/inner-attrs.rs:22:29: 22:37 (#0), + span: $DIR/inner-attrs.rs:21:29: 21:37 (#0), }, ], - span: $DIR/inner-attrs.rs:22:7: 22:38 (#0), + span: $DIR/inner-attrs.rs:21:7: 21:38 (#0), }, ], - span: $DIR/inner-attrs.rs:20:10: 23:2 (#0), + span: $DIR/inner-attrs.rs:19:10: 22:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): second PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "second", - span: $DIR/inner-attrs.rs:19:25: 19:31 (#0), + span: $DIR/inner-attrs.rs:18:25: 18:31 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn foo() @@ -129,16 +129,16 @@ PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): fn foo() PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:20:1: 20:3 (#0), + span: $DIR/inner-attrs.rs:19:1: 19:3 (#0), }, Ident { ident: "foo", - span: $DIR/inner-attrs.rs:20:4: 20:7 (#0), + span: $DIR/inner-attrs.rs:19:4: 19:7 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:20:7: 20:9 (#0), + span: $DIR/inner-attrs.rs:19:7: 19:9 (#0), }, Group { delimiter: Brace, @@ -146,72 +146,72 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:21:5: 21:6 (#0), + span: $DIR/inner-attrs.rs:20:5: 20:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:21:6: 21:7 (#0), + span: $DIR/inner-attrs.rs:20:6: 20:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:21:8: 21:29 (#0), + span: $DIR/inner-attrs.rs:20:8: 20:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "third", - span: $DIR/inner-attrs.rs:21:30: 21:35 (#0), + span: $DIR/inner-attrs.rs:20:30: 20:35 (#0), }, ], - span: $DIR/inner-attrs.rs:21:29: 21:36 (#0), + span: $DIR/inner-attrs.rs:20:29: 20:36 (#0), }, ], - span: $DIR/inner-attrs.rs:21:7: 21:37 (#0), + span: $DIR/inner-attrs.rs:20:7: 20:37 (#0), }, Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:22:5: 22:6 (#0), + span: $DIR/inner-attrs.rs:21:5: 21:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:22:6: 22:7 (#0), + span: $DIR/inner-attrs.rs:21:6: 21:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:22:8: 22:29 (#0), + span: $DIR/inner-attrs.rs:21:8: 21:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "fourth", - span: $DIR/inner-attrs.rs:22:30: 22:36 (#0), + span: $DIR/inner-attrs.rs:21:30: 21:36 (#0), }, ], - span: $DIR/inner-attrs.rs:22:29: 22:37 (#0), + span: $DIR/inner-attrs.rs:21:29: 21:37 (#0), }, ], - span: $DIR/inner-attrs.rs:22:7: 22:38 (#0), + span: $DIR/inner-attrs.rs:21:7: 21:38 (#0), }, ], - span: $DIR/inner-attrs.rs:20:10: 23:2 (#0), + span: $DIR/inner-attrs.rs:19:10: 22:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): third PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "third", - span: $DIR/inner-attrs.rs:21:30: 21:35 (#0), + span: $DIR/inner-attrs.rs:20:30: 20:35 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn foo() { #![print_target_and_args(fourth)] } @@ -219,16 +219,16 @@ PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): fn foo() { #! [print_target_and_args(fou PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:20:1: 20:3 (#0), + span: $DIR/inner-attrs.rs:19:1: 19:3 (#0), }, Ident { ident: "foo", - span: $DIR/inner-attrs.rs:20:4: 20:7 (#0), + span: $DIR/inner-attrs.rs:19:4: 19:7 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:20:7: 20:9 (#0), + span: $DIR/inner-attrs.rs:19:7: 19:9 (#0), }, Group { delimiter: Brace, @@ -236,70 +236,70 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:22:5: 22:6 (#0), + span: $DIR/inner-attrs.rs:21:5: 21:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:22:6: 22:7 (#0), + span: $DIR/inner-attrs.rs:21:6: 21:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:22:8: 22:29 (#0), + span: $DIR/inner-attrs.rs:21:8: 21:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "fourth", - span: $DIR/inner-attrs.rs:22:30: 22:36 (#0), + span: $DIR/inner-attrs.rs:21:30: 21:36 (#0), }, ], - span: $DIR/inner-attrs.rs:22:29: 22:37 (#0), + span: $DIR/inner-attrs.rs:21:29: 21:37 (#0), }, ], - span: $DIR/inner-attrs.rs:22:7: 22:38 (#0), + span: $DIR/inner-attrs.rs:21:7: 21:38 (#0), }, ], - span: $DIR/inner-attrs.rs:20:10: 23:2 (#0), + span: $DIR/inner-attrs.rs:19:10: 22:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): fourth PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "fourth", - span: $DIR/inner-attrs.rs:22:30: 22:36 (#0), + span: $DIR/inner-attrs.rs:21:30: 21:36 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn foo() {} PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:20:1: 20:3 (#0), + span: $DIR/inner-attrs.rs:19:1: 19:3 (#0), }, Ident { ident: "foo", - span: $DIR/inner-attrs.rs:20:4: 20:7 (#0), + span: $DIR/inner-attrs.rs:19:4: 19:7 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:20:7: 20:9 (#0), + span: $DIR/inner-attrs.rs:19:7: 19:9 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/inner-attrs.rs:20:10: 23:2 (#0), + span: $DIR/inner-attrs.rs:19:10: 22:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): mod_first PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "mod_first", - span: $DIR/inner-attrs.rs:25:25: 25:34 (#0), + span: $DIR/inner-attrs.rs:24:25: 24:34 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): #[print_target_and_args(mod_second)] mod inline_mod @@ -313,35 +313,35 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/inner-attrs.rs:26:1: 26:2 (#0), + span: $DIR/inner-attrs.rs:25:1: 25:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:26:3: 26:24 (#0), + span: $DIR/inner-attrs.rs:25:3: 25:24 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "mod_second", - span: $DIR/inner-attrs.rs:26:25: 26:35 (#0), + span: $DIR/inner-attrs.rs:25:25: 25:35 (#0), }, ], - span: $DIR/inner-attrs.rs:26:24: 26:36 (#0), + span: $DIR/inner-attrs.rs:25:24: 25:36 (#0), }, ], - span: $DIR/inner-attrs.rs:26:2: 26:37 (#0), + span: $DIR/inner-attrs.rs:25:2: 25:37 (#0), }, Ident { ident: "mod", - span: $DIR/inner-attrs.rs:27:1: 27:4 (#0), + span: $DIR/inner-attrs.rs:26:1: 26:4 (#0), }, Ident { ident: "inline_mod", - span: $DIR/inner-attrs.rs:27:5: 27:15 (#0), + span: $DIR/inner-attrs.rs:26:5: 26:15 (#0), }, Group { delimiter: Brace, @@ -349,72 +349,72 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:28:5: 28:6 (#0), + span: $DIR/inner-attrs.rs:27:5: 27:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:28:6: 28:7 (#0), + span: $DIR/inner-attrs.rs:27:6: 27:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:28:8: 28:29 (#0), + span: $DIR/inner-attrs.rs:27:8: 27:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "mod_third", - span: $DIR/inner-attrs.rs:28:30: 28:39 (#0), + span: $DIR/inner-attrs.rs:27:30: 27:39 (#0), }, ], - span: $DIR/inner-attrs.rs:28:29: 28:40 (#0), + span: $DIR/inner-attrs.rs:27:29: 27:40 (#0), }, ], - span: $DIR/inner-attrs.rs:28:7: 28:41 (#0), + span: $DIR/inner-attrs.rs:27:7: 27:41 (#0), }, Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:29:5: 29:6 (#0), + span: $DIR/inner-attrs.rs:28:5: 28:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:29:6: 29:7 (#0), + span: $DIR/inner-attrs.rs:28:6: 28:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:29:8: 29:29 (#0), + span: $DIR/inner-attrs.rs:28:8: 28:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "mod_fourth", - span: $DIR/inner-attrs.rs:29:30: 29:40 (#0), + span: $DIR/inner-attrs.rs:28:30: 28:40 (#0), }, ], - span: $DIR/inner-attrs.rs:29:29: 29:41 (#0), + span: $DIR/inner-attrs.rs:28:29: 28:41 (#0), }, ], - span: $DIR/inner-attrs.rs:29:7: 29:42 (#0), + span: $DIR/inner-attrs.rs:28:7: 28:42 (#0), }, ], - span: $DIR/inner-attrs.rs:27:16: 30:2 (#0), + span: $DIR/inner-attrs.rs:26:16: 29:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): mod_second PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "mod_second", - span: $DIR/inner-attrs.rs:26:25: 26:35 (#0), + span: $DIR/inner-attrs.rs:25:25: 25:35 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): mod inline_mod @@ -427,11 +427,11 @@ PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): mod inline_mod PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "mod", - span: $DIR/inner-attrs.rs:27:1: 27:4 (#0), + span: $DIR/inner-attrs.rs:26:1: 26:4 (#0), }, Ident { ident: "inline_mod", - span: $DIR/inner-attrs.rs:27:5: 27:15 (#0), + span: $DIR/inner-attrs.rs:26:5: 26:15 (#0), }, Group { delimiter: Brace, @@ -439,72 +439,72 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:28:5: 28:6 (#0), + span: $DIR/inner-attrs.rs:27:5: 27:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:28:6: 28:7 (#0), + span: $DIR/inner-attrs.rs:27:6: 27:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:28:8: 28:29 (#0), + span: $DIR/inner-attrs.rs:27:8: 27:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "mod_third", - span: $DIR/inner-attrs.rs:28:30: 28:39 (#0), + span: $DIR/inner-attrs.rs:27:30: 27:39 (#0), }, ], - span: $DIR/inner-attrs.rs:28:29: 28:40 (#0), + span: $DIR/inner-attrs.rs:27:29: 27:40 (#0), }, ], - span: $DIR/inner-attrs.rs:28:7: 28:41 (#0), + span: $DIR/inner-attrs.rs:27:7: 27:41 (#0), }, Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:29:5: 29:6 (#0), + span: $DIR/inner-attrs.rs:28:5: 28:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:29:6: 29:7 (#0), + span: $DIR/inner-attrs.rs:28:6: 28:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:29:8: 29:29 (#0), + span: $DIR/inner-attrs.rs:28:8: 28:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "mod_fourth", - span: $DIR/inner-attrs.rs:29:30: 29:40 (#0), + span: $DIR/inner-attrs.rs:28:30: 28:40 (#0), }, ], - span: $DIR/inner-attrs.rs:29:29: 29:41 (#0), + span: $DIR/inner-attrs.rs:28:29: 28:41 (#0), }, ], - span: $DIR/inner-attrs.rs:29:7: 29:42 (#0), + span: $DIR/inner-attrs.rs:28:7: 28:42 (#0), }, ], - span: $DIR/inner-attrs.rs:27:16: 30:2 (#0), + span: $DIR/inner-attrs.rs:26:16: 29:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): mod_third PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "mod_third", - span: $DIR/inner-attrs.rs:28:30: 28:39 (#0), + span: $DIR/inner-attrs.rs:27:30: 27:39 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): mod inline_mod { #![print_target_and_args(mod_fourth)] } @@ -512,11 +512,11 @@ PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): mod inline_mod { #! [print_target_and_ar PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "mod", - span: $DIR/inner-attrs.rs:27:1: 27:4 (#0), + span: $DIR/inner-attrs.rs:26:1: 26:4 (#0), }, Ident { ident: "inline_mod", - span: $DIR/inner-attrs.rs:27:5: 27:15 (#0), + span: $DIR/inner-attrs.rs:26:5: 26:15 (#0), }, Group { delimiter: Brace, @@ -524,58 +524,58 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:29:5: 29:6 (#0), + span: $DIR/inner-attrs.rs:28:5: 28:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:29:6: 29:7 (#0), + span: $DIR/inner-attrs.rs:28:6: 28:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:29:8: 29:29 (#0), + span: $DIR/inner-attrs.rs:28:8: 28:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "mod_fourth", - span: $DIR/inner-attrs.rs:29:30: 29:40 (#0), + span: $DIR/inner-attrs.rs:28:30: 28:40 (#0), }, ], - span: $DIR/inner-attrs.rs:29:29: 29:41 (#0), + span: $DIR/inner-attrs.rs:28:29: 28:41 (#0), }, ], - span: $DIR/inner-attrs.rs:29:7: 29:42 (#0), + span: $DIR/inner-attrs.rs:28:7: 28:42 (#0), }, ], - span: $DIR/inner-attrs.rs:27:16: 30:2 (#0), + span: $DIR/inner-attrs.rs:26:16: 29:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): mod_fourth PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "mod_fourth", - span: $DIR/inner-attrs.rs:29:30: 29:40 (#0), + span: $DIR/inner-attrs.rs:28:30: 28:40 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): mod inline_mod {} PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "mod", - span: $DIR/inner-attrs.rs:27:1: 27:4 (#0), + span: $DIR/inner-attrs.rs:26:1: 26:4 (#0), }, Ident { ident: "inline_mod", - span: $DIR/inner-attrs.rs:27:5: 27:15 (#0), + span: $DIR/inner-attrs.rs:26:5: 26:15 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/inner-attrs.rs:27:16: 30:2 (#0), + span: $DIR/inner-attrs.rs:26:16: 29:2 (#0), }, ] PRINT-DERIVE INPUT (DISPLAY): struct MyDerivePrint @@ -585,63 +585,63 @@ PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): struct MyDerivePrint PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/inner-attrs.rs:37:1: 37:7 (#0), + span: $DIR/inner-attrs.rs:36:1: 36:7 (#0), }, Ident { ident: "MyDerivePrint", - span: $DIR/inner-attrs.rs:37:8: 37:21 (#0), + span: $DIR/inner-attrs.rs:36:8: 36:21 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "field", - span: $DIR/inner-attrs.rs:38:5: 38:10 (#0), + span: $DIR/inner-attrs.rs:37:5: 37:10 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/inner-attrs.rs:38:10: 38:11 (#0), + span: $DIR/inner-attrs.rs:37:10: 37:11 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "u8", - span: $DIR/inner-attrs.rs:38:13: 38:15 (#0), + span: $DIR/inner-attrs.rs:37:13: 37:15 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:38:15: 38:16 (#0), + span: $DIR/inner-attrs.rs:37:15: 37:16 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "match", - span: $DIR/inner-attrs.rs:39:9: 39:14 (#0), + span: $DIR/inner-attrs.rs:38:9: 38:14 (#0), }, Ident { ident: "true", - span: $DIR/inner-attrs.rs:39:15: 39:19 (#0), + span: $DIR/inner-attrs.rs:38:15: 38:19 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "_", - span: $DIR/inner-attrs.rs:40:13: 40:14 (#0), + span: $DIR/inner-attrs.rs:39:13: 39:14 (#0), }, Punct { ch: '=', spacing: Joint, - span: $DIR/inner-attrs.rs:40:15: 40:16 (#0), + span: $DIR/inner-attrs.rs:39:15: 39:16 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/inner-attrs.rs:40:16: 40:17 (#0), + span: $DIR/inner-attrs.rs:39:16: 39:17 (#0), }, Group { delimiter: Brace, @@ -649,69 +649,69 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:41:17: 41:18 (#0), + span: $DIR/inner-attrs.rs:40:17: 40:18 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:41:18: 41:19 (#0), + span: $DIR/inner-attrs.rs:40:18: 40:19 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustc_dummy", - span: $DIR/inner-attrs.rs:41:41: 41:52 (#0), + span: $DIR/inner-attrs.rs:40:41: 40:52 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "third", - span: $DIR/inner-attrs.rs:41:53: 41:58 (#0), + span: $DIR/inner-attrs.rs:40:53: 40:58 (#0), }, ], - span: $DIR/inner-attrs.rs:41:52: 41:59 (#0), + span: $DIR/inner-attrs.rs:40:52: 40:59 (#0), }, ], - span: $DIR/inner-attrs.rs:41:19: 41:61 (#0), + span: $DIR/inner-attrs.rs:40:19: 40:61 (#0), }, Ident { ident: "true", - span: $DIR/inner-attrs.rs:42:17: 42:21 (#0), + span: $DIR/inner-attrs.rs:41:17: 41:21 (#0), }, ], - span: $DIR/inner-attrs.rs:40:18: 43:14 (#0), + span: $DIR/inner-attrs.rs:39:18: 42:14 (#0), }, ], - span: $DIR/inner-attrs.rs:39:20: 44:10 (#0), + span: $DIR/inner-attrs.rs:38:20: 43:10 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:44:10: 44:11 (#0), + span: $DIR/inner-attrs.rs:43:10: 43:11 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/inner-attrs.rs:45:9: 45:10 (#0), + span: $DIR/inner-attrs.rs:44:9: 44:10 (#0), }, ], - span: $DIR/inner-attrs.rs:38:17: 46:6 (#0), + span: $DIR/inner-attrs.rs:37:17: 45:6 (#0), }, ], - span: $DIR/inner-attrs.rs:38:12: 46:7 (#0), + span: $DIR/inner-attrs.rs:37:12: 45:7 (#0), }, ], - span: $DIR/inner-attrs.rs:37:22: 47:2 (#0), + span: $DIR/inner-attrs.rs:36:22: 46:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): tuple_attrs PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "tuple_attrs", - span: $DIR/inner-attrs.rs:50:29: 50:40 (#0), + span: $DIR/inner-attrs.rs:49:29: 49:40 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): (3, 4, { #![cfg_attr(not(FALSE), rustc_dummy(innermost))] 5 }); @@ -724,23 +724,23 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "3", suffix: None, - span: $DIR/inner-attrs.rs:51:9: 51:10 (#0), + span: $DIR/inner-attrs.rs:50:9: 50:10 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:51:10: 51:11 (#0), + span: $DIR/inner-attrs.rs:50:10: 50:11 (#0), }, Literal { kind: Integer, symbol: "4", suffix: None, - span: $DIR/inner-attrs.rs:51:12: 51:13 (#0), + span: $DIR/inner-attrs.rs:50:12: 50:13 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:51:13: 51:14 (#0), + span: $DIR/inner-attrs.rs:50:13: 50:14 (#0), }, Group { delimiter: Brace, @@ -748,85 +748,85 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:52:13: 52:14 (#0), + span: $DIR/inner-attrs.rs:51:13: 51:14 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:52:14: 52:15 (#0), + span: $DIR/inner-attrs.rs:51:14: 51:15 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg_attr", - span: $DIR/inner-attrs.rs:52:16: 52:24 (#0), + span: $DIR/inner-attrs.rs:51:16: 51:24 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/inner-attrs.rs:52:25: 52:28 (#0), + span: $DIR/inner-attrs.rs:51:25: 51:28 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/inner-attrs.rs:52:29: 52:34 (#0), + span: $DIR/inner-attrs.rs:51:29: 51:34 (#0), }, ], - span: $DIR/inner-attrs.rs:52:28: 52:35 (#0), + span: $DIR/inner-attrs.rs:51:28: 51:35 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:52:35: 52:36 (#0), + span: $DIR/inner-attrs.rs:51:35: 51:36 (#0), }, Ident { ident: "rustc_dummy", - span: $DIR/inner-attrs.rs:52:37: 52:48 (#0), + span: $DIR/inner-attrs.rs:51:37: 51:48 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "innermost", - span: $DIR/inner-attrs.rs:52:49: 52:58 (#0), + span: $DIR/inner-attrs.rs:51:49: 51:58 (#0), }, ], - span: $DIR/inner-attrs.rs:52:48: 52:59 (#0), + span: $DIR/inner-attrs.rs:51:48: 51:59 (#0), }, ], - span: $DIR/inner-attrs.rs:52:24: 52:60 (#0), + span: $DIR/inner-attrs.rs:51:24: 51:60 (#0), }, ], - span: $DIR/inner-attrs.rs:52:15: 52:61 (#0), + span: $DIR/inner-attrs.rs:51:15: 51:61 (#0), }, Literal { kind: Integer, symbol: "5", suffix: None, - span: $DIR/inner-attrs.rs:53:13: 53:14 (#0), + span: $DIR/inner-attrs.rs:52:13: 52:14 (#0), }, ], - span: $DIR/inner-attrs.rs:51:15: 54:10 (#0), + span: $DIR/inner-attrs.rs:50:15: 53:10 (#0), }, ], - span: $DIR/inner-attrs.rs:50:43: 55:6 (#0), + span: $DIR/inner-attrs.rs:49:43: 54:6 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:55:6: 55:7 (#0), + span: $DIR/inner-attrs.rs:54:6: 54:7 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): tuple_attrs PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "tuple_attrs", - span: $DIR/inner-attrs.rs:57:29: 57:40 (#0), + span: $DIR/inner-attrs.rs:56:29: 56:40 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): (3, 4, { #![cfg_attr(not(FALSE), rustc_dummy(innermost))] 5 }); @@ -839,23 +839,23 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "3", suffix: None, - span: $DIR/inner-attrs.rs:58:9: 58:10 (#0), + span: $DIR/inner-attrs.rs:57:9: 57:10 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:58:10: 58:11 (#0), + span: $DIR/inner-attrs.rs:57:10: 57:11 (#0), }, Literal { kind: Integer, symbol: "4", suffix: None, - span: $DIR/inner-attrs.rs:58:12: 58:13 (#0), + span: $DIR/inner-attrs.rs:57:12: 57:13 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:58:13: 58:14 (#0), + span: $DIR/inner-attrs.rs:57:13: 57:14 (#0), }, Group { delimiter: Brace, @@ -863,105 +863,105 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:59:13: 59:14 (#0), + span: $DIR/inner-attrs.rs:58:13: 58:14 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:59:14: 59:15 (#0), + span: $DIR/inner-attrs.rs:58:14: 58:15 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg_attr", - span: $DIR/inner-attrs.rs:59:16: 59:24 (#0), + span: $DIR/inner-attrs.rs:58:16: 58:24 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/inner-attrs.rs:59:25: 59:28 (#0), + span: $DIR/inner-attrs.rs:58:25: 58:28 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/inner-attrs.rs:59:29: 59:34 (#0), + span: $DIR/inner-attrs.rs:58:29: 58:34 (#0), }, ], - span: $DIR/inner-attrs.rs:59:28: 59:35 (#0), + span: $DIR/inner-attrs.rs:58:28: 58:35 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:59:35: 59:36 (#0), + span: $DIR/inner-attrs.rs:58:35: 58:36 (#0), }, Ident { ident: "rustc_dummy", - span: $DIR/inner-attrs.rs:59:37: 59:48 (#0), + span: $DIR/inner-attrs.rs:58:37: 58:48 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "innermost", - span: $DIR/inner-attrs.rs:59:49: 59:58 (#0), + span: $DIR/inner-attrs.rs:58:49: 58:58 (#0), }, ], - span: $DIR/inner-attrs.rs:59:48: 59:59 (#0), + span: $DIR/inner-attrs.rs:58:48: 58:59 (#0), }, ], - span: $DIR/inner-attrs.rs:59:24: 59:60 (#0), + span: $DIR/inner-attrs.rs:58:24: 58:60 (#0), }, ], - span: $DIR/inner-attrs.rs:59:15: 59:61 (#0), + span: $DIR/inner-attrs.rs:58:15: 58:61 (#0), }, Literal { kind: Integer, symbol: "5", suffix: None, - span: $DIR/inner-attrs.rs:60:13: 60:14 (#0), + span: $DIR/inner-attrs.rs:59:13: 59:14 (#0), }, ], - span: $DIR/inner-attrs.rs:58:15: 61:10 (#0), + span: $DIR/inner-attrs.rs:57:15: 60:10 (#0), }, ], - span: $DIR/inner-attrs.rs:57:43: 62:6 (#0), + span: $DIR/inner-attrs.rs:56:43: 61:6 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:62:6: 62:7 (#0), + span: $DIR/inner-attrs.rs:61:6: 61:7 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): tenth PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "tenth", - span: $DIR/inner-attrs.rs:84:42: 84:47 (#0), + span: $DIR/inner-attrs.rs:87:42: 87:47 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn weird_extern() {} PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:83:5: 83:7 (#0), + span: $DIR/inner-attrs.rs:86:5: 86:7 (#0), }, Ident { ident: "weird_extern", - span: $DIR/inner-attrs.rs:83:8: 83:20 (#0), + span: $DIR/inner-attrs.rs:86:8: 86:20 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:83:20: 83:22 (#0), + span: $DIR/inner-attrs.rs:86:20: 86:22 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/inner-attrs.rs:83:23: 85:6 (#0), + span: $DIR/inner-attrs.rs:86:23: 88:6 (#0), }, ] diff --git a/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr b/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr index 0fe560afcb5d3..c12c8d033615f 100644 --- a/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr +++ b/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr @@ -7,7 +7,6 @@ LL | #[derive(PartialOrd, AddImpl)] = help: the trait `PartialEq` is not implemented for `PriorityQueue` note: required by a bound in `PartialOrd` --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `PriorityQueue: Eq` is not satisfied --> $DIR/issue-104884-trait-impl-sugg-err.rs:13:22 @@ -32,7 +31,6 @@ LL | #[derive(PartialOrd, AddImpl)] | ^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro note: required by a bound in `Ord` --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `AddImpl` which comes from the expansion of the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `BinaryHeap>` with `_` --> $DIR/issue-104884-trait-impl-sugg-err.rs:20:25 @@ -44,7 +42,6 @@ LL | struct PriorityQueue(BinaryHeap>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `BinaryHeap> < _` and `BinaryHeap> > _` | = help: the trait `PartialOrd<_>` is not implemented for `BinaryHeap>` - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0599]: no method named `cmp` found for struct `BinaryHeap>` in the current scope --> $DIR/issue-104884-trait-impl-sugg-err.rs:13:22 diff --git a/tests/ui/proc-macro/issue-50493.rs b/tests/ui/proc-macro/issue-50493.rs index e23f1f7c7d872..4a88eee21852b 100644 --- a/tests/ui/proc-macro/issue-50493.rs +++ b/tests/ui/proc-macro/issue-50493.rs @@ -5,7 +5,7 @@ extern crate issue_50493; #[derive(Derive)] struct Restricted { - pub(in restricted) field: usize, //~ visibilities can only be restricted to ancestor modules + pub(in restricted) field: usize, //~ ERROR visibilities can only be restricted to ancestor modules } mod restricted {} diff --git a/tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs b/tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs index 6afafb7114a61..df236cce6d2a8 100644 --- a/tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs +++ b/tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs @@ -4,8 +4,10 @@ //@ edition:2018 //@ proc-macro: issue-59191.rs //@ needs-unwind (affects error output) -//@ error-pattern: error: `#[panic_handler]` function required #![feature(custom_inner_attributes)] #![issue_59191::no_main] #![issue_59191::no_main] + +//~? ERROR `#[panic_handler]` function required, but not found +//~? ERROR unwinding panics are not supported without std diff --git a/tests/ui/proc-macro/issue-75930-derive-cfg.rs b/tests/ui/proc-macro/issue-75930-derive-cfg.rs index 376a8ea42783a..f0851b31e9cb8 100644 --- a/tests/ui/proc-macro/issue-75930-derive-cfg.rs +++ b/tests/ui/proc-macro/issue-75930-derive-cfg.rs @@ -31,7 +31,7 @@ extern crate test_macros; // // It is because of this code from below: // ``` -// struct Foo<#[cfg(FALSE)] A, B> +// struct Foo<#[cfg(false)] A, B> // ``` // When the token stream is formed during parsing, `<` is followed immediately // by `#`, which is punctuation, so it is marked `Joint`. But before being @@ -51,22 +51,22 @@ extern crate test_macros; #[print_attr] #[derive(Print)] #[print_helper(b)] -struct Foo<#[cfg(FALSE)] A, B> { - #[cfg(FALSE)] first: String, +struct Foo<#[cfg(false)] A, B> { + #[cfg(false)] first: String, #[cfg_attr(FALSE, deny(warnings))] second: bool, third: [u8; { - #[cfg(FALSE)] struct Bar; + #[cfg(false)] struct Bar; #[cfg(not(FALSE))] struct Inner; - #[cfg(FALSE)] let a = 25; + #[cfg(false)] let a = 25; match true { - #[cfg(FALSE)] true => {}, + #[cfg(false)] true => {}, #[cfg_attr(not(FALSE), allow(warnings))] false => {}, _ => {} }; #[print_helper(should_be_removed)] fn removed_fn() { - #![cfg(FALSE)] + #![cfg(false)] } #[print_helper(c)] #[cfg(not(FALSE))] fn kept_fn() { @@ -76,22 +76,22 @@ struct Foo<#[cfg(FALSE)] A, B> { enum TupleEnum { Foo( - #[cfg(FALSE)] u8, - #[cfg(FALSE)] bool, + #[cfg(false)] u8, + #[cfg(false)] bool, #[cfg(not(FALSE))] i32, - #[cfg(FALSE)] String, u8 + #[cfg(false)] String, u8 ) } struct TupleStruct( - #[cfg(FALSE)] String, + #[cfg(false)] String, #[cfg(not(FALSE))] i32, - #[cfg(FALSE)] bool, + #[cfg(false)] bool, u8 ); fn plain_removed_fn() { - #![cfg_attr(not(FALSE), cfg(FALSE))] + #![cfg_attr(not(FALSE), cfg(false))] } 0 diff --git a/tests/ui/proc-macro/issue-75930-derive-cfg.stdout b/tests/ui/proc-macro/issue-75930-derive-cfg.stdout index 4dcf2b717d8a8..549621fdca313 100644 --- a/tests/ui/proc-macro/issue-75930-derive-cfg.stdout +++ b/tests/ui/proc-macro/issue-75930-derive-cfg.stdout @@ -1,73 +1,73 @@ PRINT-ATTR INPUT (DISPLAY): #[print_helper(a)] #[allow(dead_code)] #[derive(Print)] #[print_helper(b)] -struct Foo<#[cfg(FALSE)] A, B> +struct Foo<#[cfg(false)] A, B> { - #[cfg(FALSE)] first: String, #[cfg_attr(FALSE, deny(warnings))] second: + #[cfg(false)] first: String, #[cfg_attr(FALSE, deny(warnings))] second: bool, third: [u8; { - #[cfg(FALSE)] struct Bar; #[cfg(not(FALSE))] struct Inner; - #[cfg(FALSE)] let a = 25; match true + #[cfg(false)] struct Bar; #[cfg(not(FALSE))] struct Inner; + #[cfg(false)] let a = 25; match true { - #[cfg(FALSE)] true => {}, #[cfg_attr(not(FALSE), allow(warnings))] + #[cfg(false)] true => {}, #[cfg_attr(not(FALSE), allow(warnings))] false => {}, _ => {} }; #[print_helper(should_be_removed)] fn removed_fn() - { #![cfg(FALSE)] } #[print_helper(c)] #[cfg(not(FALSE))] fn kept_fn() + { #![cfg(false)] } #[print_helper(c)] #[cfg(not(FALSE))] fn kept_fn() { #![cfg(not(FALSE))] let my_val = true; } enum TupleEnum { - Foo(#[cfg(FALSE)] u8, #[cfg(FALSE)] bool, #[cfg(not(FALSE))] i32, - #[cfg(FALSE)] String, u8) + Foo(#[cfg(false)] u8, #[cfg(false)] bool, #[cfg(not(FALSE))] i32, + #[cfg(false)] String, u8) } struct - TupleStruct(#[cfg(FALSE)] String, #[cfg(not(FALSE))] i32, - #[cfg(FALSE)] bool, u8); fn plain_removed_fn() - { #![cfg_attr(not(FALSE), cfg(FALSE))] } 0 + TupleStruct(#[cfg(false)] String, #[cfg(not(FALSE))] i32, + #[cfg(false)] bool, u8); fn plain_removed_fn() + { #![cfg_attr(not(FALSE), cfg(false))] } 0 }], #[print_helper(d)] fourth: B } PRINT-ATTR RE-COLLECTED (DISPLAY): #[print_helper(a)] #[allow(dead_code)] #[derive(Print)] #[print_helper(b)] -struct Foo <#[cfg(FALSE)] A, B > +struct Foo <#[cfg(false)] A, B > { - #[cfg(FALSE)] first: String, #[cfg_attr(FALSE, deny(warnings))] second: + #[cfg(false)] first: String, #[cfg_attr(FALSE, deny(warnings))] second: bool, third: [u8; { - #[cfg(FALSE)] struct Bar; #[cfg(not(FALSE))] struct Inner; - #[cfg(FALSE)] let a = 25; match true + #[cfg(false)] struct Bar; #[cfg(not(FALSE))] struct Inner; + #[cfg(false)] let a = 25; match true { - #[cfg(FALSE)] true => {}, #[cfg_attr(not(FALSE), allow(warnings))] + #[cfg(false)] true => {}, #[cfg_attr(not(FALSE), allow(warnings))] false => {}, _ => {} }; #[print_helper(should_be_removed)] fn removed_fn() - { #![cfg(FALSE)] } #[print_helper(c)] #[cfg(not(FALSE))] fn kept_fn() + { #![cfg(false)] } #[print_helper(c)] #[cfg(not(FALSE))] fn kept_fn() { #![cfg(not(FALSE))] let my_val = true; } enum TupleEnum { - Foo(#[cfg(FALSE)] u8, #[cfg(FALSE)] bool, #[cfg(not(FALSE))] i32, - #[cfg(FALSE)] String, u8) + Foo(#[cfg(false)] u8, #[cfg(false)] bool, #[cfg(not(FALSE))] i32, + #[cfg(false)] String, u8) } struct - TupleStruct(#[cfg(FALSE)] String, #[cfg(not(FALSE))] i32, - #[cfg(FALSE)] bool, u8); fn plain_removed_fn() - { #![cfg_attr(not(FALSE), cfg(FALSE))] } 0 + TupleStruct(#[cfg(false)] String, #[cfg(not(FALSE))] i32, + #[cfg(false)] bool, u8); fn plain_removed_fn() + { #![cfg_attr(not(FALSE), cfg(false))] } 0 }], #[print_helper(d)] fourth: B } PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): #[print_helper(a)] #[allow(dead_code)] #[derive(Print)] #[print_helper(b)] -struct Foo <#[cfg(FALSE)] A, B > +struct Foo <#[cfg(false)] A, B > { - #[cfg(FALSE)] first : String, #[cfg_attr(FALSE, deny(warnings))] second : + #[cfg(false)] first : String, #[cfg_attr(FALSE, deny(warnings))] second : bool, third : [u8; { - #[cfg(FALSE)] struct Bar; #[cfg(not(FALSE))] struct Inner; - #[cfg(FALSE)] let a = 25; match true + #[cfg(false)] struct Bar; #[cfg(not(FALSE))] struct Inner; + #[cfg(false)] let a = 25; match true { - #[cfg(FALSE)] true => {}, #[cfg_attr(not(FALSE), allow(warnings))] + #[cfg(false)] true => {}, #[cfg_attr(not(FALSE), allow(warnings))] false => {}, _ => {} }; #[print_helper(should_be_removed)] fn removed_fn() - { #! [cfg(FALSE)] } #[print_helper(c)] #[cfg(not(FALSE))] fn kept_fn() + { #! [cfg(false)] } #[print_helper(c)] #[cfg(not(FALSE))] fn kept_fn() { #! [cfg(not(FALSE))] let my_val = true; } enum TupleEnum { - Foo(#[cfg(FALSE)] u8, #[cfg(FALSE)] bool, #[cfg(not(FALSE))] i32, - #[cfg(FALSE)] String, u8) + Foo(#[cfg(false)] u8, #[cfg(false)] bool, #[cfg(not(FALSE))] i32, + #[cfg(false)] String, u8) } struct - TupleStruct(#[cfg(FALSE)] String, #[cfg(not(FALSE))] i32, - #[cfg(FALSE)] bool, u8); fn plain_removed_fn() - { #! [cfg_attr(not(FALSE), cfg(FALSE))] } 0 + TupleStruct(#[cfg(false)] String, #[cfg(not(FALSE))] i32, + #[cfg(false)] bool, u8); fn plain_removed_fn() + { #! [cfg_attr(not(FALSE), cfg(false))] } 0 }], #[print_helper(d)] fourth : B } PRINT-ATTR INPUT (DEBUG): TokenStream [ @@ -200,7 +200,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/issue-75930-derive-cfg.rs:54:18: 54:23 (#0), }, ], @@ -246,7 +246,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/issue-75930-derive-cfg.rs:55:11: 55:16 (#0), }, ], @@ -375,7 +375,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/issue-75930-derive-cfg.rs:58:15: 58:20 (#0), }, ], @@ -461,7 +461,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/issue-75930-derive-cfg.rs:60:15: 60:20 (#0), }, ], @@ -521,7 +521,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/issue-75930-derive-cfg.rs:62:19: 62:24 (#0), }, ], @@ -721,7 +721,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/issue-75930-derive-cfg.rs:69:20: 69:25 (#0), }, ], @@ -908,7 +908,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/issue-75930-derive-cfg.rs:79:23: 79:28 (#0), }, ], @@ -942,7 +942,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/issue-75930-derive-cfg.rs:80:23: 80:28 (#0), }, ], @@ -1020,7 +1020,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/issue-75930-derive-cfg.rs:82:23: 82:28 (#0), }, ], @@ -1075,7 +1075,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/issue-75930-derive-cfg.rs:87:19: 87:24 (#0), }, ], @@ -1153,7 +1153,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/issue-75930-derive-cfg.rs:89:19: 89:24 (#0), }, ], @@ -1246,7 +1246,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", + ident: "false", span: $DIR/issue-75930-derive-cfg.rs:94:41: 94:46 (#0), }, ], diff --git a/tests/ui/proc-macro/issue-91800.rs b/tests/ui/proc-macro/issue-91800.rs index bc78bcacfd0f7..8cecfad32b55b 100644 --- a/tests/ui/proc-macro/issue-91800.rs +++ b/tests/ui/proc-macro/issue-91800.rs @@ -6,11 +6,14 @@ extern crate issue_91800_macro; #[derive(MyTrait)] //~^ ERROR macros that expand to items must be delimited with braces or followed by a semicolon //~| ERROR proc-macro derive produced unparsable tokens +//~| ERROR #[attribute_macro] //~^ ERROR macros that expand to items must be delimited with braces or followed by a semicolon +//~| ERROR struct MyStruct; fn_macro! {} //~^ ERROR macros that expand to items must be delimited with braces or followed by a semicolon +//~| ERROR fn main() {} diff --git a/tests/ui/proc-macro/issue-91800.stderr b/tests/ui/proc-macro/issue-91800.stderr index d831d62e919d2..63ebc0a552e33 100644 --- a/tests/ui/proc-macro/issue-91800.stderr +++ b/tests/ui/proc-macro/issue-91800.stderr @@ -21,7 +21,7 @@ LL | #[derive(MyTrait)] = note: this error originates in the derive macro `MyTrait` (in Nightly builds, run with -Z macro-backtrace for more info) error: macros that expand to items must be delimited with braces or followed by a semicolon - --> $DIR/issue-91800.rs:9:1 + --> $DIR/issue-91800.rs:10:1 | LL | #[attribute_macro] | ^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | #[attribute_macro] = note: this error originates in the attribute macro `attribute_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: - --> $DIR/issue-91800.rs:9:1 + --> $DIR/issue-91800.rs:10:1 | LL | #[attribute_macro] | ^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | #[attribute_macro] = note: this error originates in the attribute macro `attribute_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: macros that expand to items must be delimited with braces or followed by a semicolon - --> $DIR/issue-91800.rs:13:1 + --> $DIR/issue-91800.rs:15:1 | LL | fn_macro! {} | ^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | fn_macro! {} = note: this error originates in the macro `fn_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: - --> $DIR/issue-91800.rs:13:1 + --> $DIR/issue-91800.rs:15:1 | LL | fn_macro! {} | ^^^^^^^^^^^^ diff --git a/tests/ui/proc-macro/macro-rules-derive-cfg.stdout b/tests/ui/proc-macro/macro-rules-derive-cfg.stdout index c1e46b50d40c2..9482b2784d222 100644 --- a/tests/ui/proc-macro/macro-rules-derive-cfg.stdout +++ b/tests/ui/proc-macro/macro-rules-derive-cfg.stdout @@ -1,8 +1,9 @@ PRINT-DERIVE INPUT (DISPLAY): struct -Foo([bool; #[rustc_dummy(first)] #[rustc_dummy(second)] -{ #![rustc_dummy(third)] #[rustc_dummy(fourth)] 30 }]); +Foo([bool; #[rustc_dummy(first)] +#[rustc_dummy(second)] { #![rustc_dummy(third)] #[rustc_dummy(fourth)] 30 }]); PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): struct -Foo([bool; #[rustc_dummy(first)] #[rustc_dummy(second)] +Foo([bool; #[rustc_dummy(first)] +#[rustc_dummy(second)] { #! [rustc_dummy(third)] #[rustc_dummy(fourth)] 30 }]); PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { @@ -53,97 +54,103 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ ], span: $DIR/macro-rules-derive-cfg.rs:18:21: 18:63 (#3), }, - Punct { - ch: '#', - spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:23:13: 23:14 (#0), - }, Group { - delimiter: Bracket, - stream: TokenStream [ - Ident { - ident: "rustc_dummy", - span: $DIR/macro-rules-derive-cfg.rs:23:36: 23:47 (#0), - }, - Group { - delimiter: Parenthesis, - stream: TokenStream [ - Ident { - ident: "second", - span: $DIR/macro-rules-derive-cfg.rs:23:48: 23:54 (#0), - }, - ], - span: $DIR/macro-rules-derive-cfg.rs:23:47: 23:55 (#0), - }, - ], - span: $DIR/macro-rules-derive-cfg.rs:23:14: 23:57 (#0), - }, - Group { - delimiter: Brace, + delimiter: None, stream: TokenStream [ Punct { ch: '#', - spacing: Joint, - span: $DIR/macro-rules-derive-cfg.rs:24:5: 24:6 (#0), - }, - Punct { - ch: '!', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:24:6: 24:7 (#0), + span: $DIR/macro-rules-derive-cfg.rs:23:13: 23:14 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustc_dummy", - span: $DIR/macro-rules-derive-cfg.rs:24:29: 24:40 (#0), + span: $DIR/macro-rules-derive-cfg.rs:23:36: 23:47 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "third", - span: $DIR/macro-rules-derive-cfg.rs:24:41: 24:46 (#0), + ident: "second", + span: $DIR/macro-rules-derive-cfg.rs:23:48: 23:54 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:24:40: 24:47 (#0), + span: $DIR/macro-rules-derive-cfg.rs:23:47: 23:55 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:24:7: 24:49 (#0), - }, - Punct { - ch: '#', - spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:25:5: 25:6 (#0), + span: $DIR/macro-rules-derive-cfg.rs:23:14: 23:57 (#0), }, Group { - delimiter: Bracket, + delimiter: Brace, stream: TokenStream [ - Ident { - ident: "rustc_dummy", - span: $DIR/macro-rules-derive-cfg.rs:25:28: 25:39 (#0), + Punct { + ch: '#', + spacing: Joint, + span: $DIR/macro-rules-derive-cfg.rs:24:5: 24:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/macro-rules-derive-cfg.rs:24:6: 24:7 (#0), }, Group { - delimiter: Parenthesis, + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/macro-rules-derive-cfg.rs:24:29: 24:40 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "third", + span: $DIR/macro-rules-derive-cfg.rs:24:41: 24:46 (#0), + }, + ], + span: $DIR/macro-rules-derive-cfg.rs:24:40: 24:47 (#0), + }, + ], + span: $DIR/macro-rules-derive-cfg.rs:24:7: 24:49 (#0), + }, + Punct { + ch: '#', + spacing: Alone, + span: $DIR/macro-rules-derive-cfg.rs:25:5: 25:6 (#0), + }, + Group { + delimiter: Bracket, stream: TokenStream [ Ident { - ident: "fourth", - span: $DIR/macro-rules-derive-cfg.rs:25:40: 25:46 (#0), + ident: "rustc_dummy", + span: $DIR/macro-rules-derive-cfg.rs:25:28: 25:39 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "fourth", + span: $DIR/macro-rules-derive-cfg.rs:25:40: 25:46 (#0), + }, + ], + span: $DIR/macro-rules-derive-cfg.rs:25:39: 25:47 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:25:39: 25:47 (#0), + span: $DIR/macro-rules-derive-cfg.rs:25:6: 25:49 (#0), + }, + Literal { + kind: Integer, + symbol: "30", + suffix: None, + span: $DIR/macro-rules-derive-cfg.rs:26:5: 26:7 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:25:6: 25:49 (#0), - }, - Literal { - kind: Integer, - symbol: "30", - suffix: None, - span: $DIR/macro-rules-derive-cfg.rs:26:5: 26:7 (#0), + span: $DIR/macro-rules-derive-cfg.rs:23:58: 27:2 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:23:58: 27:2 (#0), + span: $DIR/macro-rules-derive-cfg.rs:18:64: 18:69 (#3), }, ], span: $DIR/macro-rules-derive-cfg.rs:18:13: 18:70 (#3), diff --git a/tests/ui/proc-macro/meta-macro-hygiene.stdout b/tests/ui/proc-macro/meta-macro-hygiene.stdout index 1ee7179e84c04..1734b9afe92eb 100644 --- a/tests/ui/proc-macro/meta-macro-hygiene.stdout +++ b/tests/ui/proc-macro/meta-macro-hygiene.stdout @@ -60,11 +60,11 @@ SyntaxContexts: #0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque) #1: parent: #0, outer_mark: (crate0::{{expn1}}, Opaque) #2: parent: #0, outer_mark: (crate0::{{expn1}}, Transparent) -#3: parent: #0, outer_mark: (crate0::{{expn2}}, SemiTransparent) +#3: parent: #0, outer_mark: (crate0::{{expn2}}, SemiOpaque) #4: parent: #0, outer_mark: (crate0::{{expn3}}, Opaque) #5: parent: #3, outer_mark: (crate0::{{expn3}}, Transparent) -#6: parent: #0, outer_mark: (crate0::{{expn3}}, SemiTransparent) +#6: parent: #0, outer_mark: (crate0::{{expn3}}, SemiOpaque) #7: parent: #0, outer_mark: (crate0::{{expn4}}, Opaque) #8: parent: #4, outer_mark: (crate0::{{expn4}}, Transparent) -#9: parent: #4, outer_mark: (crate0::{{expn4}}, SemiTransparent) +#9: parent: #4, outer_mark: (crate0::{{expn4}}, SemiOpaque) */ diff --git a/tests/ui/proc-macro/module.rs b/tests/ui/proc-macro/module.rs index 210c05988bf23..5878f1b7ddd8a 100644 --- a/tests/ui/proc-macro/module.rs +++ b/tests/ui/proc-macro/module.rs @@ -1 +1 @@ -//@ ignore-test (auxiliary, used by other tests) +//@ ignore-auxiliary (used by `./attributes-on-modules-fail.rs`) diff --git a/tests/ui/proc-macro/module_with_attrs.rs b/tests/ui/proc-macro/module_with_attrs.rs index 8a4ca92e44b6c..7e4ec978736f1 100644 --- a/tests/ui/proc-macro/module_with_attrs.rs +++ b/tests/ui/proc-macro/module_with_attrs.rs @@ -1,4 +1,4 @@ -//@ ignore-test (auxiliary, used by other tests) +//@ ignore-auxiliary (used by `../inner-attr-non-inline-mod.rs`) #![rustfmt::skip] #![print_attr] diff --git a/tests/ui/proc-macro/nested-derive-cfg.rs b/tests/ui/proc-macro/nested-derive-cfg.rs index bd8f231ac2c2a..b3dcfb7c39665 100644 --- a/tests/ui/proc-macro/nested-derive-cfg.rs +++ b/tests/ui/proc-macro/nested-derive-cfg.rs @@ -10,10 +10,10 @@ extern crate test_macros; #[derive(Print)] struct Foo { - #[cfg(FALSE)] removed: bool, + #[cfg(false)] removed: bool, my_array: [bool; { struct Inner { - #[cfg(FALSE)] removed_inner_field: u8, + #[cfg(false)] removed_inner_field: u8, non_removed_inner_field: usize } 0 diff --git a/tests/ui/proc-macro/no-macro-use-attr.rs b/tests/ui/proc-macro/no-macro-use-attr.rs index d44f51bfd8d47..2520037813118 100644 --- a/tests/ui/proc-macro/no-macro-use-attr.rs +++ b/tests/ui/proc-macro/no-macro-use-attr.rs @@ -1,10 +1,9 @@ +//@ check-pass //@ proc-macro: test-macros.rs -#![feature(rustc_attrs)] #![warn(unused_extern_crates)] extern crate test_macros; //~^ WARN unused extern crate -#[rustc_error] -fn main() {} //~ ERROR fatal error triggered by #[rustc_error] +fn main() {} diff --git a/tests/ui/proc-macro/no-macro-use-attr.stderr b/tests/ui/proc-macro/no-macro-use-attr.stderr index 3dda3cc7d5a5b..4913672450ab9 100644 --- a/tests/ui/proc-macro/no-macro-use-attr.stderr +++ b/tests/ui/proc-macro/no-macro-use-attr.stderr @@ -10,11 +10,5 @@ note: the lint level is defined here LL | #![warn(unused_extern_crates)] | ^^^^^^^^^^^^^^^^^^^^ -error: fatal error triggered by #[rustc_error] - --> $DIR/no-macro-use-attr.rs:10:1 - | -LL | fn main() {} - | ^^^^^^^^^ - -error: aborting due to 1 previous error; 1 warning emitted +warning: 1 warning emitted diff --git a/tests/ui/proc-macro/nodelim-groups.rs b/tests/ui/proc-macro/nodelim-groups.rs index 9acdc7023c083..8b0324214b94d 100644 --- a/tests/ui/proc-macro/nodelim-groups.rs +++ b/tests/ui/proc-macro/nodelim-groups.rs @@ -19,4 +19,17 @@ macro_rules! expand_it { fn main() { expand_it!(1 + (25) + 1); expand_it!(("hello".len()) ("world".len())); + f(); +} + +// The key thing here is to produce a single `None`-delimited `Group`, even +// though there is multiple levels of macros. +macro_rules! m5 { ($e:expr) => { print_bang_consume!($e) }; } +macro_rules! m4 { ($e:expr) => { m5!($e); } } +macro_rules! m3 { ($e:expr) => { m4!($e); } } +macro_rules! m2 { ($e:expr) => { m3!($e); } } +macro_rules! m1 { ($e:expr) => { m2!($e); } } + +fn f() { + m1!(123); } diff --git a/tests/ui/proc-macro/nodelim-groups.stdout b/tests/ui/proc-macro/nodelim-groups.stdout index cdf851b535aa5..61001035c26cf 100644 --- a/tests/ui/proc-macro/nodelim-groups.stdout +++ b/tests/ui/proc-macro/nodelim-groups.stdout @@ -165,3 +165,18 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ span: $DIR/nodelim-groups.rs:16:52: 16:59 (#8), }, ] +PRINT-BANG INPUT (DISPLAY): 123 +PRINT-BANG INPUT (DEBUG): TokenStream [ + Group { + delimiter: None, + stream: TokenStream [ + Literal { + kind: Integer, + symbol: "123", + suffix: None, + span: $DIR/nodelim-groups.rs:34:9: 34:12 (#0), + }, + ], + span: $DIR/nodelim-groups.rs:27:54: 27:56 (#16), + }, +] diff --git a/tests/ui/proc-macro/nonterminal-token-hygiene.rs b/tests/ui/proc-macro/nonterminal-token-hygiene.rs index e2aedb245d075..b7b0d1ea3dd7c 100644 --- a/tests/ui/proc-macro/nonterminal-token-hygiene.rs +++ b/tests/ui/proc-macro/nonterminal-token-hygiene.rs @@ -8,6 +8,7 @@ //@ normalize-stdout: "expn\d{3,}" -> "expnNNN" //@ normalize-stdout: "extern crate compiler_builtins /\* \d+ \*/" -> "extern crate compiler_builtins /* NNN */" //@ proc-macro: test-macros.rs +//@ edition: 2015 #![feature(decl_macro)] #![no_std] // Don't load unnecessary hygiene information from std diff --git a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout index c80a33206fb4f..42257312a8729 100644 --- a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout +++ b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout @@ -5,19 +5,19 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ stream: TokenStream [ Ident { ident: "struct", - span: $DIR/nonterminal-token-hygiene.rs:32:5: 32:11 (#4), + span: $DIR/nonterminal-token-hygiene.rs:33:5: 33:11 (#5), }, Ident { ident: "S", - span: $DIR/nonterminal-token-hygiene.rs:32:12: 32:13 (#4), + span: $DIR/nonterminal-token-hygiene.rs:33:12: 33:13 (#5), }, Punct { ch: ';', spacing: Alone, - span: $DIR/nonterminal-token-hygiene.rs:32:13: 32:14 (#4), + span: $DIR/nonterminal-token-hygiene.rs:33:13: 33:14 (#5), }, ], - span: $DIR/nonterminal-token-hygiene.rs:22:27: 22:32 (#5), + span: $DIR/nonterminal-token-hygiene.rs:23:27: 23:32 (#4), }, ] #![feature /* 0#0 */(prelude_import)] @@ -32,6 +32,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ //@ normalize-stdout: "expn\d{3,}" -> "expnNNN" //@ normalize-stdout: "extern crate compiler_builtins /\* \d+ \*/" -> "extern crate compiler_builtins /* NNN */" //@ proc-macro: test-macros.rs +//@ edition: 2015 #![feature /* 0#0 */(decl_macro)] #![no_std /* 0#0 */] @@ -59,7 +60,7 @@ macro_rules! outer struct S /* 0#0 */; macro inner /* 0#3 */ { () => { print_bang! { struct S; } } } -struct S /* 0#4 */; +struct S /* 0#5 */; // OK, not a duplicate definition of `S` fn main /* 0#0 */() {} @@ -70,7 +71,7 @@ crate0::{{expn0}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports) crate0::{{expn2}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "outer") crate0::{{expn3}}: parent: crate0::{{expn2}}, call_site_ctxt: #3, def_site_ctxt: #3, kind: Macro(Bang, "inner") -crate0::{{expn4}}: parent: crate0::{{expn3}}, call_site_ctxt: #5, def_site_ctxt: #0, kind: Macro(Bang, "print_bang") +crate0::{{expn4}}: parent: crate0::{{expn3}}, call_site_ctxt: #4, def_site_ctxt: #0, kind: Macro(Bang, "print_bang") crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "diagnostic::on_unimplemented") crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "diagnostic::on_unimplemented") crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "diagnostic::on_unimplemented") @@ -82,10 +83,10 @@ SyntaxContexts: #0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque) #1: parent: #0, outer_mark: (crate0::{{expn1}}, Opaque) #2: parent: #0, outer_mark: (crate0::{{expn1}}, Transparent) -#3: parent: #0, outer_mark: (crate0::{{expn2}}, SemiTransparent) -#4: parent: #0, outer_mark: (crate0::{{expn3}}, Opaque) -#5: parent: #3, outer_mark: (crate0::{{expn3}}, Opaque) +#3: parent: #0, outer_mark: (crate0::{{expn2}}, SemiOpaque) +#4: parent: #3, outer_mark: (crate0::{{expn3}}, Opaque) +#5: parent: #0, outer_mark: (crate0::{{expn3}}, Opaque) #6: parent: #0, outer_mark: (crate0::{{expn4}}, Opaque) -#7: parent: #5, outer_mark: (crate0::{{expn4}}, Transparent) -#8: parent: #4, outer_mark: (crate0::{{expn4}}, SemiTransparent) +#7: parent: #4, outer_mark: (crate0::{{expn4}}, Transparent) +#8: parent: #5, outer_mark: (crate0::{{expn4}}, SemiOpaque) */ diff --git a/tests/ui/proc-macro/outer/inner.rs b/tests/ui/proc-macro/outer/inner.rs index 210c05988bf23..d0f2087321f45 100644 --- a/tests/ui/proc-macro/outer/inner.rs +++ b/tests/ui/proc-macro/outer/inner.rs @@ -1 +1 @@ -//@ ignore-test (auxiliary, used by other tests) +//@ ignore-auxiliary (used by `../attributes-on-modules-fail.rs`) diff --git a/tests/ui/proc-macro/panic-abort.rs b/tests/ui/proc-macro/panic-abort.rs index 40d8aec5ef690..58e1d00643309 100644 --- a/tests/ui/proc-macro/panic-abort.rs +++ b/tests/ui/proc-macro/panic-abort.rs @@ -1,4 +1,5 @@ -//@ error-pattern: building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic //@ compile-flags: --crate-type proc-macro -Cpanic=abort //@ force-host //@ check-pass + +//~? WARN building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic diff --git a/tests/ui/proc-macro/pretty-print-hack-show.rs b/tests/ui/proc-macro/pretty-print-hack-show.rs index de6453c6a821d..70f0d5f6ea975 100644 --- a/tests/ui/proc-macro/pretty-print-hack-show.rs +++ b/tests/ui/proc-macro/pretty-print-hack-show.rs @@ -18,3 +18,5 @@ mod second { } fn main() {} + +//~? ERROR using an old version of `rental` diff --git a/tests/ui/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs b/tests/ui/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs index f89098f3a5ecc..a27176a38e224 100644 --- a/tests/ui/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs +++ b/tests/ui/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs @@ -1,4 +1,4 @@ -//@ ignore-test (auxiliary, used by other tests) +//@ ignore-auxiliary (used by `../../../pretty-print-hack-show.rs`) #[derive(Print)] enum ProceduralMasqueradeDummyType { diff --git a/tests/ui/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs b/tests/ui/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs index f89098f3a5ecc..a27176a38e224 100644 --- a/tests/ui/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs +++ b/tests/ui/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs @@ -1,4 +1,4 @@ -//@ ignore-test (auxiliary, used by other tests) +//@ ignore-auxiliary (used by `../../../pretty-print-hack-show.rs`) #[derive(Print)] enum ProceduralMasqueradeDummyType { diff --git a/tests/ui/proc-macro/pretty-print-hack/rental-0.5.6/src/lib.rs b/tests/ui/proc-macro/pretty-print-hack/rental-0.5.6/src/lib.rs index f89098f3a5ecc..765ee4be656e3 100644 --- a/tests/ui/proc-macro/pretty-print-hack/rental-0.5.6/src/lib.rs +++ b/tests/ui/proc-macro/pretty-print-hack/rental-0.5.6/src/lib.rs @@ -1,4 +1,4 @@ -//@ ignore-test (auxiliary, used by other tests) +//@ ignore-auxiliary (used by `../../../pretty-print-hack/hide.rs`) #[derive(Print)] enum ProceduralMasqueradeDummyType { diff --git a/tests/ui/proc-macro/pub-at-crate-root.rs b/tests/ui/proc-macro/pub-at-crate-root.rs index 32e4999a71bc6..b8de66967b109 100644 --- a/tests/ui/proc-macro/pub-at-crate-root.rs +++ b/tests/ui/proc-macro/pub-at-crate-root.rs @@ -5,7 +5,7 @@ extern crate proc_macro; -pub mod a { //~ `proc-macro` crate types currently cannot export any items +pub mod a { //~ ERROR `proc-macro` crate types currently cannot export any items use proc_macro::TokenStream; #[proc_macro_derive(B)] diff --git a/tests/ui/proc-macro/quote/debug.rs b/tests/ui/proc-macro/quote/debug.rs index ce113079e5678..ce1ef81bedaf1 100644 --- a/tests/ui/proc-macro/quote/debug.rs +++ b/tests/ui/proc-macro/quote/debug.rs @@ -3,6 +3,7 @@ //@ no-prefer-dynamic //@ compile-flags: -Z unpretty=expanded //@ needs-unwind compiling proc macros with panic=abort causes a warning +//@ edition: 2015 // // This file is not actually used as a proc-macro - instead, // it's just used to show the output of the `quote!` macro diff --git a/tests/ui/proc-macro/quote/debug.stdout b/tests/ui/proc-macro/quote/debug.stdout index 3eaad9eb96997..3acb472d9c0fb 100644 --- a/tests/ui/proc-macro/quote/debug.stdout +++ b/tests/ui/proc-macro/quote/debug.stdout @@ -5,6 +5,7 @@ //@ no-prefer-dynamic //@ compile-flags: -Z unpretty=expanded //@ needs-unwind compiling proc macros with panic=abort causes a warning +//@ edition: 2015 // // This file is not actually used as a proc-macro - instead, // it's just used to show the output of the `quote!` macro @@ -31,12 +32,12 @@ fn main() { let mut iter = "\"world\"".parse::().unwrap().into_iter(); if let (Some(crate::TokenTree::Literal(mut lit)), None) = - (iter.next(), iter.next()) { - lit.set_span(crate::Span::recover_proc_macro_span(2)); - lit - } else { - ::core::panicking::panic("internal error: entered unreachable code") - } + (iter.next(), iter.next()) { + lit.set_span(crate::Span::recover_proc_macro_span(2)); + lit + } else { + ::core::panicking::panic("internal error: entered unreachable code") + } }), &mut ts); crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new(';', crate::Spacing::Alone)), &mut ts); @@ -50,12 +51,12 @@ fn main() { let mut iter = "r#\"raw\"literal\"#".parse::().unwrap().into_iter(); if let (Some(crate::TokenTree::Literal(mut lit)), None) = - (iter.next(), iter.next()) { - lit.set_span(crate::Span::recover_proc_macro_span(5)); - lit - } else { - ::core::panicking::panic("internal error: entered unreachable code") - } + (iter.next(), iter.next()) { + lit.set_span(crate::Span::recover_proc_macro_span(5)); + lit + } else { + ::core::panicking::panic("internal error: entered unreachable code") + } }), &mut ts); crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new(';', crate::Spacing::Alone)), &mut ts); diff --git a/tests/ui/proc-macro/quote/not-quotable.stderr b/tests/ui/proc-macro/quote/not-quotable.stderr index e349b2dce5300..62a02638e548b 100644 --- a/tests/ui/proc-macro/quote/not-quotable.stderr +++ b/tests/ui/proc-macro/quote/not-quotable.stderr @@ -17,7 +17,6 @@ LL | let _ = quote! { $ip }; Rc bool and 24 others - = note: this error originates in the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/span-api-tests.rs b/tests/ui/proc-macro/span-api-tests.rs index ac42a7ea611ab..792859ed05b16 100644 --- a/tests/ui/proc-macro/span-api-tests.rs +++ b/tests/ui/proc-macro/span-api-tests.rs @@ -8,26 +8,24 @@ extern crate span_test_macros; extern crate span_api_tests; -// FIXME(69775): Investigate `assert_fake_source_file`. - -use span_api_tests::{reemit, assert_source_file, macro_stringify}; +use span_api_tests::{reemit, assert_local_file, macro_stringify}; macro_rules! say_hello { ($macname:ident) => ( $macname! { "Hello, world!" }) } -assert_source_file! { "Hello, world!" } +assert_local_file! { "Hello, world!" } -say_hello! { assert_source_file } +say_hello! { assert_local_file } reemit_legacy! { - assert_source_file! { "Hello, world!" } + assert_local_file! { "Hello, world!" } } -say_hello_extern! { assert_source_file } +say_hello_extern! { assert_local_file } reemit! { - assert_source_file! { "Hello, world!" } + assert_local_file! { "Hello, world!" } } fn main() { diff --git a/tests/ui/proc-macro/two-crate-types-1.rs b/tests/ui/proc-macro/two-crate-types-1.rs index 432b0a601b22a..9d21a43053713 100644 --- a/tests/ui/proc-macro/two-crate-types-1.rs +++ b/tests/ui/proc-macro/two-crate-types-1.rs @@ -1,7 +1,7 @@ -//@ error-pattern: cannot mix `proc-macro` crate type with others - //@ force-host //@ no-prefer-dynamic #![crate_type = "proc-macro"] #![crate_type = "rlib"] + +//~? ERROR cannot mix `proc-macro` crate type with others diff --git a/tests/ui/proc-macro/two-crate-types-2.rs b/tests/ui/proc-macro/two-crate-types-2.rs index 491c5c71d76c4..c4cc0b4d80df6 100644 --- a/tests/ui/proc-macro/two-crate-types-2.rs +++ b/tests/ui/proc-macro/two-crate-types-2.rs @@ -1,3 +1,4 @@ -//@ error-pattern: cannot mix `proc-macro` crate type with others //@ compile-flags: --crate-type rlib --crate-type proc-macro //@ force-host + +//~? ERROR cannot mix `proc-macro` crate type with others diff --git a/tests/ui/proc-macro/weird-braces.stdout b/tests/ui/proc-macro/weird-braces.stdout index 7da769ef0d247..0215deb05c302 100644 --- a/tests/ui/proc-macro/weird-braces.stdout +++ b/tests/ui/proc-macro/weird-braces.stdout @@ -5,7 +5,7 @@ PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ span: $DIR/weird-braces.rs:16:25: 16:36 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): #[print_target_and_args(second_outer)] impl Bar<{ 1 > 0 }> for Foo<{ true }> +PRINT-ATTR INPUT (DISPLAY): #[print_target_and_args(second_outer)] impl Bar<{1 > 0}> for Foo<{true}> { #![print_target_and_args(first_inner)] #![print_target_and_args(second_inner)] @@ -191,7 +191,7 @@ PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ span: $DIR/weird-braces.rs:17:25: 17:37 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): impl Bar<{ 1 > 0 }> for Foo<{ true }> +PRINT-ATTR INPUT (DISPLAY): impl Bar<{1 > 0}> for Foo<{true}> { #![print_target_and_args(first_inner)] #![print_target_and_args(second_inner)] @@ -350,8 +350,7 @@ PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ span: $DIR/weird-braces.rs:19:30: 19:41 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): impl Bar<{ 1 > 0 }> for Foo<{ true }> -{ #![print_target_and_args(second_inner)] } +PRINT-ATTR INPUT (DISPLAY): impl Bar<{1 > 0}> for Foo<{true}> { #![print_target_and_args(second_inner)] } PRINT-ATTR RE-COLLECTED (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > { #![print_target_and_args(second_inner)] } PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > @@ -470,7 +469,7 @@ PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ span: $DIR/weird-braces.rs:20:30: 20:42 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): impl Bar<{ 1 > 0 }> for Foo<{ true }> {} +PRINT-ATTR INPUT (DISPLAY): impl Bar<{1 > 0}> for Foo<{true}> {} PRINT-ATTR RE-COLLECTED (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > {} PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { diff --git a/tests/ui/process/win-command-curdir-no-verbatim.rs b/tests/ui/process/win-command-curdir-no-verbatim.rs new file mode 100644 index 0000000000000..99943e1c8abc2 --- /dev/null +++ b/tests/ui/process/win-command-curdir-no-verbatim.rs @@ -0,0 +1,36 @@ +// Test that windows verbatim paths in `Command::current_dir` are converted to +// non-verbatim paths before passing to the subprocess. + +//@ run-pass +//@ only-windows +//@ needs-subprocess + +use std::env; +use std::process::Command; + +fn main() { + if env::args().skip(1).any(|s| s == "--child") { + child(); + } else { + parent(); + } +} + +fn parent() { + let exe = env::current_exe().unwrap(); + let dir = env::current_dir().unwrap(); + let status = Command::new(&exe) + .arg("--child") + .current_dir(dir.canonicalize().unwrap()) + .spawn() + .unwrap() + .wait() + .unwrap(); + assert_eq!(status.code(), Some(0)); +} + +fn child() { + let current_dir = env::current_dir().unwrap(); + let current_dir = current_dir.as_os_str().as_encoded_bytes(); + assert!(!current_dir.starts_with(br"\\?\")); +} diff --git a/tests/ui/ptr-coercion.rs b/tests/ui/ptr-coercion.rs index 193899034c7b4..2549bd6f134d7 100644 --- a/tests/ui/ptr-coercion.rs +++ b/tests/ui/ptr-coercion.rs @@ -1,23 +1,25 @@ // Test coercions between pointers which don't do anything fancy like unsizing. // These are testing that we don't lose mutability when converting to raw pointers. +//@ dont-require-annotations: NOTE + pub fn main() { // *const -> *mut let x: *const isize = &42; let x: *mut isize = x; //~ ERROR mismatched types - //~| expected raw pointer `*mut isize` - //~| found raw pointer `*const isize` - //~| types differ in mutability + //~| NOTE expected raw pointer `*mut isize` + //~| NOTE found raw pointer `*const isize` + //~| NOTE types differ in mutability // & -> *mut let x: *mut isize = &42; //~ ERROR mismatched types - //~| expected raw pointer `*mut isize` - //~| found reference `&isize` - //~| types differ in mutability + //~| NOTE expected raw pointer `*mut isize` + //~| NOTE found reference `&isize` + //~| NOTE types differ in mutability let x: *const isize = &42; let x: *mut isize = x; //~ ERROR mismatched types - //~| expected raw pointer `*mut isize` - //~| found raw pointer `*const isize` - //~| types differ in mutability + //~| NOTE expected raw pointer `*mut isize` + //~| NOTE found raw pointer `*const isize` + //~| NOTE types differ in mutability } diff --git a/tests/ui/ptr-coercion.stderr b/tests/ui/ptr-coercion.stderr index 29b7e5da849d4..8de41d2c38276 100644 --- a/tests/ui/ptr-coercion.stderr +++ b/tests/ui/ptr-coercion.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/ptr-coercion.rs:7:25 + --> $DIR/ptr-coercion.rs:9:25 | LL | let x: *mut isize = x; | ---------- ^ types differ in mutability @@ -10,7 +10,7 @@ LL | let x: *mut isize = x; found raw pointer `*const isize` error[E0308]: mismatched types - --> $DIR/ptr-coercion.rs:13:25 + --> $DIR/ptr-coercion.rs:15:25 | LL | let x: *mut isize = &42; | ---------- ^^^ types differ in mutability @@ -21,7 +21,7 @@ LL | let x: *mut isize = &42; found reference `&isize` error[E0308]: mismatched types - --> $DIR/ptr-coercion.rs:19:25 + --> $DIR/ptr-coercion.rs:21:25 | LL | let x: *mut isize = x; | ---------- ^ types differ in mutability diff --git a/tests/ui/pub/pub-restricted-warning.rs b/tests/ui/pub/pub-restricted-warning.rs new file mode 100644 index 0000000000000..80384afbb008b --- /dev/null +++ b/tests/ui/pub/pub-restricted-warning.rs @@ -0,0 +1,25 @@ +//@ check-pass + +#![allow(dead_code)] + +mod outer { + pub mod inner { + pub(in crate::outer) struct Foo; + pub fn bar() -> Foo { + //~^ WARNING type `Foo` is more private than the item `outer::inner::bar` [private_interfaces] + Foo + } + } + + pub mod nested { + pub mod inner { + pub(in crate::outer::nested) struct NestedFoo; + pub fn bar() -> NestedFoo { + //~^ WARNING type `NestedFoo` is more private than the item `nested::inner::bar` [private_interfaces] + NestedFoo + } + } + } +} + +fn main() {} diff --git a/tests/ui/pub/pub-restricted-warning.stderr b/tests/ui/pub/pub-restricted-warning.stderr new file mode 100644 index 0000000000000..74f32d3de3c25 --- /dev/null +++ b/tests/ui/pub/pub-restricted-warning.stderr @@ -0,0 +1,27 @@ +warning: type `Foo` is more private than the item `outer::inner::bar` + --> $DIR/pub-restricted-warning.rs:8:9 + | +LL | pub fn bar() -> Foo { + | ^^^^^^^^^^^^^^^^^^^ function `outer::inner::bar` is reachable at visibility `pub(crate)` + | +note: but type `Foo` is only usable at visibility `pub(in crate::outer)` + --> $DIR/pub-restricted-warning.rs:7:9 + | +LL | pub(in crate::outer) struct Foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(private_interfaces)]` on by default + +warning: type `NestedFoo` is more private than the item `nested::inner::bar` + --> $DIR/pub-restricted-warning.rs:17:13 + | +LL | pub fn bar() -> NestedFoo { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ function `nested::inner::bar` is reachable at visibility `pub(crate)` + | +note: but type `NestedFoo` is only usable at visibility `pub(in crate::outer::nested)` + --> $DIR/pub-restricted-warning.rs:16:13 + | +LL | pub(in crate::outer::nested) struct NestedFoo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 2 warnings emitted + diff --git a/tests/ui/pub/pub-restricted.rs b/tests/ui/pub/pub-restricted.rs index bcd21082f75c6..2aa24121335c2 100644 --- a/tests/ui/pub/pub-restricted.rs +++ b/tests/ui/pub/pub-restricted.rs @@ -1,8 +1,8 @@ mod a {} -pub (a) fn afn() {} //~ incorrect visibility restriction -pub (b) fn bfn() {} //~ incorrect visibility restriction -pub (crate::a) fn cfn() {} //~ incorrect visibility restriction +pub (a) fn afn() {} //~ ERROR incorrect visibility restriction +pub (b) fn bfn() {} //~ ERROR incorrect visibility restriction +pub (crate::a) fn cfn() {} //~ ERROR incorrect visibility restriction pub fn privfn() {} mod x { @@ -19,7 +19,7 @@ mod y { pub (super) s: usize, valid_private: usize, pub (in y) valid_in_x: usize, - pub (a) invalid: usize, //~ incorrect visibility restriction + pub (a) invalid: usize, //~ ERROR incorrect visibility restriction pub (in x) non_parent_invalid: usize, //~ ERROR visibilities can only be restricted } } @@ -28,4 +28,4 @@ fn main() {} // test multichar names mod xyz {} -pub (xyz) fn xyz() {} //~ incorrect visibility restriction +pub (xyz) fn xyz() {} //~ ERROR incorrect visibility restriction diff --git a/tests/ui/range/range_traits-1.stderr b/tests/ui/range/range_traits-1.stderr index 617afc995305e..ab1035778cd6c 100644 --- a/tests/ui/range/range_traits-1.stderr +++ b/tests/ui/range/range_traits-1.stderr @@ -8,7 +8,6 @@ LL | a: Range, | ^^^^^^^^^^^^^^^ no implementation for `std::ops::Range < std::ops::Range` and `std::ops::Range > std::ops::Range` | = help: the trait `PartialOrd` is not implemented for `std::ops::Range` - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `std::ops::RangeTo` with `std::ops::RangeTo` --> $DIR/range_traits-1.rs:8:5 @@ -20,7 +19,6 @@ LL | b: RangeTo, | ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo < std::ops::RangeTo` and `std::ops::RangeTo > std::ops::RangeTo` | = help: the trait `PartialOrd` is not implemented for `std::ops::RangeTo` - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `std::ops::RangeFrom` with `std::ops::RangeFrom` --> $DIR/range_traits-1.rs:11:5 @@ -32,7 +30,6 @@ LL | c: RangeFrom, | ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom < std::ops::RangeFrom` and `std::ops::RangeFrom > std::ops::RangeFrom` | = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFrom` - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull` --> $DIR/range_traits-1.rs:14:5 @@ -44,7 +41,6 @@ LL | d: RangeFull, | ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull` | = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFull` - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `std::ops::RangeInclusive` with `std::ops::RangeInclusive` --> $DIR/range_traits-1.rs:17:5 @@ -56,7 +52,6 @@ LL | e: RangeInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive < std::ops::RangeInclusive` and `std::ops::RangeInclusive > std::ops::RangeInclusive` | = help: the trait `PartialOrd` is not implemented for `std::ops::RangeInclusive` - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `std::ops::RangeToInclusive` with `std::ops::RangeToInclusive` --> $DIR/range_traits-1.rs:20:5 @@ -68,7 +63,6 @@ LL | f: RangeToInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive < std::ops::RangeToInclusive` and `std::ops::RangeToInclusive > std::ops::RangeToInclusive` | = help: the trait `PartialOrd` is not implemented for `std::ops::RangeToInclusive` - = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `std::ops::Range: Ord` is not satisfied --> $DIR/range_traits-1.rs:5:5 @@ -78,8 +72,6 @@ LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] LL | struct AllTheRanges { LL | a: Range, | ^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::Range` - | - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `std::ops::RangeTo: Ord` is not satisfied --> $DIR/range_traits-1.rs:8:5 @@ -89,8 +81,6 @@ LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] ... LL | b: RangeTo, | ^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeTo` - | - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `std::ops::RangeFrom: Ord` is not satisfied --> $DIR/range_traits-1.rs:11:5 @@ -100,8 +90,6 @@ LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] ... LL | c: RangeFrom, | ^^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeFrom` - | - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `std::ops::RangeFull: Ord` is not satisfied --> $DIR/range_traits-1.rs:14:5 @@ -111,8 +99,6 @@ LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] ... LL | d: RangeFull, | ^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeFull` - | - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `std::ops::RangeInclusive: Ord` is not satisfied --> $DIR/range_traits-1.rs:17:5 @@ -122,8 +108,6 @@ LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] ... LL | e: RangeInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeInclusive` - | - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `std::ops::RangeToInclusive: Ord` is not satisfied --> $DIR/range_traits-1.rs:20:5 @@ -133,8 +117,6 @@ LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] ... LL | f: RangeToInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeToInclusive` - | - = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 12 previous errors diff --git a/tests/ui/range/range_traits-2.stderr b/tests/ui/range/range_traits-2.stderr index 04778eecf569d..2001c85c4348a 100644 --- a/tests/ui/range/range_traits-2.stderr +++ b/tests/ui/range/range_traits-2.stderr @@ -5,8 +5,6 @@ LL | #[derive(Copy, Clone)] | ^^^^ LL | struct R(Range); | ------------ this field does not implement `Copy` - | - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/range/range_traits-3.stderr b/tests/ui/range/range_traits-3.stderr index 094cc146b6c1d..71210379c79cd 100644 --- a/tests/ui/range/range_traits-3.stderr +++ b/tests/ui/range/range_traits-3.stderr @@ -5,8 +5,6 @@ LL | #[derive(Copy, Clone)] | ^^^^ LL | struct R(RangeFrom); | ---------------- this field does not implement `Copy` - | - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/range/range_traits-6.stderr b/tests/ui/range/range_traits-6.stderr index 7c66d1c749c2e..a58022ef7e4fd 100644 --- a/tests/ui/range/range_traits-6.stderr +++ b/tests/ui/range/range_traits-6.stderr @@ -5,8 +5,6 @@ LL | #[derive(Copy, Clone)] | ^^^^ LL | struct R(RangeInclusive); | --------------------- this field does not implement `Copy` - | - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/reachable/unreachable-by-call-arguments-issue-139627.rs b/tests/ui/reachable/unreachable-by-call-arguments-issue-139627.rs new file mode 100644 index 0000000000000..b3310ac1c754c --- /dev/null +++ b/tests/ui/reachable/unreachable-by-call-arguments-issue-139627.rs @@ -0,0 +1,16 @@ +//@ check-pass +#![deny(unreachable_code)] +#![deny(unused)] + +pub enum Void {} + +pub struct S(T); + +pub fn foo(void: Void, void1: Void) { + let s = S(void); + drop(s); + let s1 = S { 0: void1 }; + drop(s1); +} + +fn main() {} diff --git a/tests/ui/reachable/unreachable-code-ret.rs b/tests/ui/reachable/unreachable-code-ret.rs index ed9fbd5c5a2d2..746c4d53e7bf9 100644 --- a/tests/ui/reachable/unreachable-code-ret.rs +++ b/tests/ui/reachable/unreachable-code-ret.rs @@ -1,8 +1,6 @@ -//@ error-pattern: unreachable statement - #![deny(unreachable_code)] fn main() { return; - println!("Paul is dead"); + println!("Paul is dead"); //~ ERROR unreachable statement } diff --git a/tests/ui/reachable/unreachable-code-ret.stderr b/tests/ui/reachable/unreachable-code-ret.stderr index 824515a2271f1..d86def536df8d 100644 --- a/tests/ui/reachable/unreachable-code-ret.stderr +++ b/tests/ui/reachable/unreachable-code-ret.stderr @@ -1,5 +1,5 @@ error: unreachable statement - --> $DIR/unreachable-code-ret.rs:7:5 + --> $DIR/unreachable-code-ret.rs:5:5 | LL | return; | ------ any code following this expression is unreachable @@ -7,7 +7,7 @@ LL | println!("Paul is dead"); | ^^^^^^^^^^^^^^^^^^^^^^^^ unreachable statement | note: the lint level is defined here - --> $DIR/unreachable-code-ret.rs:3:9 + --> $DIR/unreachable-code-ret.rs:1:9 | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/recursion/issue-23122-2.rs b/tests/ui/recursion/issue-23122-2.rs index 3e14fa92dd0f5..95e1f60d8b029 100644 --- a/tests/ui/recursion/issue-23122-2.rs +++ b/tests/ui/recursion/issue-23122-2.rs @@ -1,4 +1,3 @@ -//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" trait Next { type Next: Next; } diff --git a/tests/ui/recursion/issue-23122-2.stderr b/tests/ui/recursion/issue-23122-2.stderr index 10463ab2c33fe..c5774cc188829 100644 --- a/tests/ui/recursion/issue-23122-2.stderr +++ b/tests/ui/recursion/issue-23122-2.stderr @@ -1,12 +1,12 @@ error[E0275]: overflow evaluating the requirement `<<<<<<<... 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:11:17 + --> $DIR/issue-23122-2.rs:10:17 | LL | type Next = as Next>::Next; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_23122_2`) note: required 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>` to implement `Next` - --> $DIR/issue-23122-2.rs:10:15 + --> $DIR/issue-23122-2.rs:9:15 | LL | impl Next for GetNext { | - ^^^^ ^^^^^^^^^^ diff --git a/tests/ui/recursion/issue-83150.rs b/tests/ui/recursion/issue-83150.rs index ea1bef4fce391..b720c168187b8 100644 --- a/tests/ui/recursion/issue-83150.rs +++ b/tests/ui/recursion/issue-83150.rs @@ -1,7 +1,6 @@ -//~ ERROR overflow evaluating the requirement `Map<&mut std::ops::Range, {closure@$DIR/issue-83150.rs:13:24: 13:27}>: Iterator` +//~ ERROR overflow evaluating the requirement `Map<&mut std::ops::Range, {closure@$DIR/issue-83150.rs:12:24: 12:27}>: Iterator` //@ build-fail //@ compile-flags: -Copt-level=0 -//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" fn main() { let mut iter = 0u8..1; diff --git a/tests/ui/recursion/issue-83150.stderr b/tests/ui/recursion/issue-83150.stderr index 92a39a44e4fa2..600922f1e57a7 100644 --- a/tests/ui/recursion/issue-83150.stderr +++ b/tests/ui/recursion/issue-83150.stderr @@ -1,5 +1,5 @@ warning: function cannot return without recursing - --> $DIR/issue-83150.rs:11:1 + --> $DIR/issue-83150.rs:10:1 | LL | fn func>(iter: &mut T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -10,12 +10,12 @@ LL | func(&mut iter.map(|x| x + 1)) = help: a `loop` may express intention better if this is on purpose = note: `#[warn(unconditional_recursion)]` on by default -error[E0275]: overflow evaluating the requirement `Map<&mut std::ops::Range, {closure@$DIR/issue-83150.rs:13:24: 13:27}>: Iterator` +error[E0275]: overflow evaluating the requirement `Map<&mut std::ops::Range, {closure@$DIR/issue-83150.rs:12:24: 12:27}>: Iterator` | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_83150`) - = note: required for `&mut Map<&mut std::ops::Range, {closure@$DIR/issue-83150.rs:13:24: 13:27}>` to implement `Iterator` + = note: required for `&mut Map<&mut std::ops::Range, {closure@$DIR/issue-83150.rs:12:24: 12:27}>` to implement `Iterator` = note: 65 redundant requirements hidden - = note: required for `&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut std::ops::Range, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>, {closure@$DIR/issue-83150.rs:13:24: 13:27}>` to implement `Iterator` + = note: required for `&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut std::ops::Range, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>, {closure@$DIR/issue-83150.rs:12:24: 12:27}>` to implement `Iterator` error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/recursion/recursion.rs b/tests/ui/recursion/recursion.rs index ce56fe974b7c6..5cd4012a9d2d9 100644 --- a/tests/ui/recursion/recursion.rs +++ b/tests/ui/recursion/recursion.rs @@ -1,7 +1,5 @@ //@ build-fail //@ compile-flags:-C overflow-checks=off -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" enum Nil {NilValue} struct Cons {head:isize, tail:T} diff --git a/tests/ui/recursion/recursion.stderr b/tests/ui/recursion/recursion.stderr index cb9f67ba7418d..f959805defc64 100644 --- a/tests/ui/recursion/recursion.stderr +++ b/tests/ui/recursion/recursion.stderr @@ -1,15 +1,15 @@ error: reached the recursion limit while instantiating `test::>>>>>` - --> $DIR/recursion.rs:19:11 + --> $DIR/recursion.rs:17:11 | LL | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `test` defined here - --> $DIR/recursion.rs:17:1 + --> $DIR/recursion.rs:15:1 | LL | fn test (n:isize, i:isize, first:T, second:T) ->isize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/recursion.long-type.txt' error: aborting due to 1 previous error diff --git a/tests/ui/recursion/recursive-enum-box.rs b/tests/ui/recursion/recursive-enum-box.rs new file mode 100644 index 0000000000000..540b0c553603c --- /dev/null +++ b/tests/ui/recursion/recursive-enum-box.rs @@ -0,0 +1,21 @@ +//@ run-pass +// A smoke test for recursive enum structures using Box. +// This test constructs a linked list-like structure to exercise memory allocation and ownership. +// Originally introduced in 2010, this is one of Rust’s earliest test cases. + +#![allow(dead_code)] + +enum List { + Cons(isize, Box), + Nil, +} + +fn main() { + List::Cons( + 10, + Box::new(List::Cons( + 11, + Box::new(List::Cons(12, Box::new(List::Nil))), + )), + ); +} diff --git a/tests/ui/recursion_limit/empty.rs b/tests/ui/recursion_limit/empty.rs index 59dae106c0001..5987fa2f881c2 100644 --- a/tests/ui/recursion_limit/empty.rs +++ b/tests/ui/recursion_limit/empty.rs @@ -1,8 +1,9 @@ // Test the parse error for an empty recursion_limit #![recursion_limit = ""] //~ ERROR `limit` must be a non-negative integer - //~| `limit` must be a non-negative integer + //~| NOTE `limit` must be a non-negative integer //~| ERROR `limit` must be a non-negative integer - //~| `limit` must be a non-negative integer + //~| NOTE `limit` must be a non-negative integer + //~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` fn main() {} diff --git a/tests/ui/recursion_limit/invalid_digit.rs b/tests/ui/recursion_limit/invalid_digit.rs index 03df3e7a92713..79d8f3708bac6 100644 --- a/tests/ui/recursion_limit/invalid_digit.rs +++ b/tests/ui/recursion_limit/invalid_digit.rs @@ -1,7 +1,8 @@ // Test the parse error for an invalid digit in recursion_limit #![recursion_limit = "-100"] //~ ERROR `limit` must be a non-negative integer - //~| not a valid integer + //~| NOTE not a valid integer //~| ERROR `limit` must be a non-negative integer - //~| not a valid integer + //~| NOTE not a valid integer + //~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` fn main() {} diff --git a/tests/ui/recursion_limit/overflow.rs b/tests/ui/recursion_limit/overflow.rs index c733ba6b93ce5..7cd1d572e0971 100644 --- a/tests/ui/recursion_limit/overflow.rs +++ b/tests/ui/recursion_limit/overflow.rs @@ -2,8 +2,9 @@ #![recursion_limit = "999999999999999999999999"] //~^ ERROR `limit` must be a non-negative integer -//~| `limit` is too large +//~| NOTE `limit` is too large //~| ERROR `limit` must be a non-negative integer -//~| `limit` is too large +//~| NOTE `limit` is too large +//~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` fn main() {} diff --git a/tests/ui/regions/issue-102374.rs b/tests/ui/regions/issue-102374.rs index d640c29b2f4cb..e0a1164211a2c 100644 --- a/tests/ui/regions/issue-102374.rs +++ b/tests/ui/regions/issue-102374.rs @@ -1,4 +1,3 @@ -//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" use std::cell::Cell; #[rustfmt::skip] diff --git a/tests/ui/regions/issue-102374.stderr b/tests/ui/regions/issue-102374.stderr index e07dca0c7ee23..5416125d1c7b7 100644 --- a/tests/ui/regions/issue-102374.stderr +++ b/tests/ui/regions/issue-102374.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-102374.rs:17:5 + --> $DIR/issue-102374.rs:16:5 | LL | ) -> i32 { | --- expected `i32` because of return type diff --git a/tests/ui/regions/region-invariant-static-error-reporting.rs b/tests/ui/regions/region-invariant-static-error-reporting.rs index e58eea3f61ae7..9792c101fa894 100644 --- a/tests/ui/regions/region-invariant-static-error-reporting.rs +++ b/tests/ui/regions/region-invariant-static-error-reporting.rs @@ -3,7 +3,7 @@ // over time, but this test used to exhibit some pretty bogus messages // that were not remotely helpful. -//@ error-pattern:requires that `'a` must outlive `'static` +//@ dont-require-annotations: NOTE struct Invariant<'a>(Option<&'a mut &'a mut ()>); @@ -14,6 +14,7 @@ fn unify<'a>(x: Option>, f: fn(Invariant<'a>)) { x.unwrap() } else { mk_static() //~ ERROR lifetime may not live long enough + //~| NOTE assignment requires that `'a` must outlive `'static` }; f(bad); } diff --git a/tests/ui/removing-extern-crate.rs b/tests/ui/removing-extern-crate.rs deleted file mode 100644 index 0b819482c7101..0000000000000 --- a/tests/ui/removing-extern-crate.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ edition:2018 -//@ aux-build:removing-extern-crate.rs -//@ run-rustfix -//@ check-pass - -#![warn(rust_2018_idioms)] - -extern crate removing_extern_crate as foo; //~ WARNING unused extern crate -extern crate core; //~ WARNING unused extern crate - -mod another { - extern crate removing_extern_crate as foo; //~ WARNING unused extern crate - extern crate core; //~ WARNING unused extern crate -} - -fn main() {} diff --git a/tests/ui/repeat-expr/repeat_count.rs b/tests/ui/repeat-expr/repeat_count.rs index 18610bc5bb086..2febcdc07c2a1 100644 --- a/tests/ui/repeat-expr/repeat_count.rs +++ b/tests/ui/repeat-expr/repeat_count.rs @@ -1,34 +1,36 @@ // Regression test for issue #3645 +//@ dont-require-annotations: NOTE + fn main() { let n = 1; let a = [0; n]; //~^ ERROR attempt to use a non-constant value in a constant [E0435] let b = [0; ()]; //~^ ERROR mismatched types - //~| expected `usize`, found `()` + //~| NOTE expected `usize`, found `()` let c = [0; true]; //~^ ERROR mismatched types - //~| expected `usize`, found `bool` + //~| NOTE expected `usize`, found `bool` let d = [0; 0.5]; //~^ ERROR mismatched types - //~| expected `usize`, found floating-point number + //~| NOTE expected `usize`, found floating-point number let e = [0; "foo"]; //~^ ERROR mismatched types - //~| expected `usize`, found `&str` + //~| NOTE expected `usize`, found `&str` let f = [0; -4_isize]; //~^ ERROR mismatched types - //~| expected `usize`, found `isize` + //~| NOTE expected `usize`, found `isize` let f = [0_usize; -1_isize]; //~^ ERROR mismatched types - //~| expected `usize`, found `isize` + //~| NOTE expected `usize`, found `isize` let f = [0; 4u8]; //~^ ERROR mismatched types - //~| expected `usize`, found `u8` + //~| NOTE expected `usize`, found `u8` struct G { g: (), } let g = [0; G { g: () }]; //~^ ERROR mismatched types - //~| expected `usize`, found `G` + //~| NOTE expected `usize`, found `G` } diff --git a/tests/ui/repeat-expr/repeat_count.stderr b/tests/ui/repeat-expr/repeat_count.stderr index 34e29e8366671..cf94ad41ee363 100644 --- a/tests/ui/repeat-expr/repeat_count.stderr +++ b/tests/ui/repeat-expr/repeat_count.stderr @@ -1,5 +1,5 @@ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/repeat_count.rs:5:17 + --> $DIR/repeat_count.rs:7:17 | LL | let a = [0; n]; | ^ non-constant value @@ -11,37 +11,37 @@ LL + const n: /* Type */ = 1; | error[E0308]: mismatched types - --> $DIR/repeat_count.rs:7:17 + --> $DIR/repeat_count.rs:9:17 | LL | let b = [0; ()]; | ^^ expected `usize`, found `()` error[E0308]: mismatched types - --> $DIR/repeat_count.rs:31:17 + --> $DIR/repeat_count.rs:33:17 | LL | let g = [0; G { g: () }]; | ^^^^^^^^^^^ expected `usize`, found `G` error[E0308]: mismatched types - --> $DIR/repeat_count.rs:10:17 + --> $DIR/repeat_count.rs:12:17 | LL | let c = [0; true]; | ^^^^ expected `usize`, found `bool` error[E0308]: mismatched types - --> $DIR/repeat_count.rs:13:17 + --> $DIR/repeat_count.rs:15:17 | LL | let d = [0; 0.5]; | ^^^ expected `usize`, found floating-point number error[E0308]: mismatched types - --> $DIR/repeat_count.rs:16:17 + --> $DIR/repeat_count.rs:18:17 | LL | let e = [0; "foo"]; | ^^^^^ expected `usize`, found `&str` error[E0308]: mismatched types - --> $DIR/repeat_count.rs:19:17 + --> $DIR/repeat_count.rs:21:17 | LL | let f = [0; -4_isize]; | ^^^^^^^^ expected `usize`, found `isize` @@ -49,7 +49,7 @@ LL | let f = [0; -4_isize]; = note: `-4_isize` cannot fit into type `usize` error[E0308]: mismatched types - --> $DIR/repeat_count.rs:22:23 + --> $DIR/repeat_count.rs:24:23 | LL | let f = [0_usize; -1_isize]; | ^^^^^^^^ expected `usize`, found `isize` @@ -57,7 +57,7 @@ LL | let f = [0_usize; -1_isize]; = note: `-1_isize` cannot fit into type `usize` error[E0308]: mismatched types - --> $DIR/repeat_count.rs:25:17 + --> $DIR/repeat_count.rs:27:17 | LL | let f = [0; 4u8]; | ^^^ expected `usize`, found `u8` diff --git a/tests/ui/repr/attr-usage-repr.rs b/tests/ui/repr/attr-usage-repr.rs index d8515a12e0557..cbf99f16e036b 100644 --- a/tests/ui/repr/attr-usage-repr.rs +++ b/tests/ui/repr/attr-usage-repr.rs @@ -45,7 +45,7 @@ enum EInt { B, } -#[repr()] //~ attribute should be applied to a struct, enum, function, associated function, or union [E0517] +#[repr()] //~ ERROR attribute should be applied to a struct, enum, function, associated function, or union [E0517] type SirThisIsAType = i32; #[repr()] diff --git a/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr b/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr index 42fcaee2d4aa4..c11acc98637df 100644 --- a/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr +++ b/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr @@ -4,7 +4,7 @@ error: layout_of(Univariant) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -49,7 +49,7 @@ error: layout_of(Univariant) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -92,7 +92,7 @@ error: layout_of(TwoVariants) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -143,7 +143,7 @@ error: layout_of(TwoVariants) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -181,7 +181,7 @@ error: layout_of(TwoVariants) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -230,7 +230,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -269,7 +269,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -299,7 +299,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr b/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr index bb2c6e70dc7ab..a7888155deaf7 100644 --- a/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr +++ b/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr @@ -4,7 +4,7 @@ error: layout_of(Univariant) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -49,7 +49,7 @@ error: layout_of(Univariant) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -92,7 +92,7 @@ error: layout_of(TwoVariants) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -143,7 +143,7 @@ error: layout_of(TwoVariants) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -181,7 +181,7 @@ error: layout_of(TwoVariants) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -230,7 +230,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -269,7 +269,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -299,7 +299,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr b/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr index 42fcaee2d4aa4..c11acc98637df 100644 --- a/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr +++ b/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr @@ -4,7 +4,7 @@ error: layout_of(Univariant) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -49,7 +49,7 @@ error: layout_of(Univariant) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -92,7 +92,7 @@ error: layout_of(TwoVariants) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -143,7 +143,7 @@ error: layout_of(TwoVariants) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -181,7 +181,7 @@ error: layout_of(TwoVariants) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -230,7 +230,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -269,7 +269,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -299,7 +299,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr b/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr index 42fcaee2d4aa4..c11acc98637df 100644 --- a/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr +++ b/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr @@ -4,7 +4,7 @@ error: layout_of(Univariant) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -49,7 +49,7 @@ error: layout_of(Univariant) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -92,7 +92,7 @@ error: layout_of(TwoVariants) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -143,7 +143,7 @@ error: layout_of(TwoVariants) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -181,7 +181,7 @@ error: layout_of(TwoVariants) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I32, @@ -230,7 +230,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -269,7 +269,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -299,7 +299,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/repr/repr-c-int-dead-variants.stderr b/tests/ui/repr/repr-c-int-dead-variants.stderr index f852212deb908..f63574182c25f 100644 --- a/tests/ui/repr/repr-c-int-dead-variants.stderr +++ b/tests/ui/repr/repr-c-int-dead-variants.stderr @@ -4,7 +4,7 @@ error: layout_of(UnivariantU8) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -49,7 +49,7 @@ error: layout_of(UnivariantU8) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I8, @@ -92,7 +92,7 @@ error: layout_of(TwoVariantsU8) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -143,7 +143,7 @@ error: layout_of(TwoVariantsU8) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -181,7 +181,7 @@ error: layout_of(TwoVariantsU8) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: ScalarPair( + backend_repr: ScalarPair( Initialized { value: Int( I8, @@ -230,7 +230,7 @@ error: layout_of(DeadBranchHasOtherFieldU8) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -269,7 +269,7 @@ error: layout_of(DeadBranchHasOtherFieldU8) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -299,7 +299,7 @@ error: layout_of(DeadBranchHasOtherFieldU8) = Layout { abi: Align(8 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { diff --git a/tests/ui/repr/repr-empty-packed.rs b/tests/ui/repr/repr-empty-packed.rs new file mode 100644 index 0000000000000..6e390a12b15ac --- /dev/null +++ b/tests/ui/repr/repr-empty-packed.rs @@ -0,0 +1,9 @@ +//@ compile-flags: --crate-type=lib +#![deny(unused_attributes)] + +#[repr()] //~ ERROR unused attribute +#[repr(packed)] //~ ERROR attribute should be applied to a struct or union +pub enum Foo { + Bar, + Baz(i32), +} diff --git a/tests/ui/repr/repr-empty-packed.stderr b/tests/ui/repr/repr-empty-packed.stderr new file mode 100644 index 0000000000000..c824c2998b487 --- /dev/null +++ b/tests/ui/repr/repr-empty-packed.stderr @@ -0,0 +1,27 @@ +error: unused attribute + --> $DIR/repr-empty-packed.rs:4:1 + | +LL | #[repr()] + | ^^^^^^^^^ help: remove this attribute + | + = note: attribute `repr` with an empty list has no effect +note: the lint level is defined here + --> $DIR/repr-empty-packed.rs:2:9 + | +LL | #![deny(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + +error[E0517]: attribute should be applied to a struct or union + --> $DIR/repr-empty-packed.rs:5:8 + | +LL | #[repr(packed)] + | ^^^^^^ +LL | / pub enum Foo { +LL | | Bar, +LL | | Baz(i32), +LL | | } + | |_- not a struct or union + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0517`. diff --git a/tests/ui/resolve/auxiliary/empty.rs b/tests/ui/resolve/auxiliary/empty.rs new file mode 100644 index 0000000000000..bd9ec079d80d4 --- /dev/null +++ b/tests/ui/resolve/auxiliary/empty.rs @@ -0,0 +1 @@ +// Intentionally empty. diff --git a/tests/ui/resolve/auxiliary/macro_helpers.rs b/tests/ui/resolve/auxiliary/macro_helpers.rs new file mode 100644 index 0000000000000..43aa336457de7 --- /dev/null +++ b/tests/ui/resolve/auxiliary/macro_helpers.rs @@ -0,0 +1,16 @@ +/* macro namespace. */ + +extern crate proc_macro; +use proc_macro::*; +use std::str::FromStr; + +const ERROR: &str = "fn helper() { \"helper\" }"; +// https://doc.rust-lang.org/nightly/std/prelude/v1/index.html#attributes +// NOTE: all the bang macros in std are currently unstable. +#[proc_macro_attribute] pub fn test // lang. + (_: TokenStream, _: TokenStream) -> TokenStream { + TokenStream::from_str("fn test_macro() { \"\" }").unwrap() } +// https://doc.rust-lang.org/nightly/reference/attributes.html#built-in-attributes-index +#[proc_macro_attribute] pub fn global_allocator // lang. + (_: TokenStream, _: TokenStream) -> TokenStream { + TokenStream::from_str("fn global_allocator_macro() { \"\" }").unwrap() } diff --git a/tests/ui/resolve/bad-env-capture.rs b/tests/ui/resolve/bad-env-capture.rs index ccd98b6ef062d..a0efe88e2a9ec 100644 --- a/tests/ui/resolve/bad-env-capture.rs +++ b/tests/ui/resolve/bad-env-capture.rs @@ -1,6 +1,8 @@ -//@ error-pattern: can't capture dynamic environment in a fn item fn foo() { let x: isize; fn bar() { log(debug, x); } + //~^ ERROR can't capture dynamic environment in a fn item + //~| ERROR cannot find value `debug` in this scope + //~| ERROR cannot find function `log` in this scope } fn main() { foo(); } diff --git a/tests/ui/resolve/bad-env-capture.stderr b/tests/ui/resolve/bad-env-capture.stderr index 59b1fabfd7c46..a3a15ca245b2a 100644 --- a/tests/ui/resolve/bad-env-capture.stderr +++ b/tests/ui/resolve/bad-env-capture.stderr @@ -1,5 +1,5 @@ error[E0434]: can't capture dynamic environment in a fn item - --> $DIR/bad-env-capture.rs:4:27 + --> $DIR/bad-env-capture.rs:3:27 | LL | fn bar() { log(debug, x); } | ^ @@ -7,13 +7,13 @@ LL | fn bar() { log(debug, x); } = help: use the `|| { ... }` closure form instead error[E0425]: cannot find value `debug` in this scope - --> $DIR/bad-env-capture.rs:4:20 + --> $DIR/bad-env-capture.rs:3:20 | LL | fn bar() { log(debug, x); } | ^^^^^ not found in this scope error[E0425]: cannot find function `log` in this scope - --> $DIR/bad-env-capture.rs:4:16 + --> $DIR/bad-env-capture.rs:3:16 | LL | fn bar() { log(debug, x); } | ^^^ not found in this scope diff --git a/tests/ui/resolve/bad-env-capture2.rs b/tests/ui/resolve/bad-env-capture2.rs index 84d1832be6074..8298e6fcd243b 100644 --- a/tests/ui/resolve/bad-env-capture2.rs +++ b/tests/ui/resolve/bad-env-capture2.rs @@ -1,5 +1,7 @@ -//@ error-pattern: can't capture dynamic environment in a fn item fn foo(x: isize) { fn bar() { log(debug, x); } + //~^ ERROR can't capture dynamic environment in a fn item + //~| ERROR cannot find value `debug` in this scope + //~| ERROR cannot find function `log` in this scope } fn main() { foo(2); } diff --git a/tests/ui/resolve/bad-env-capture2.stderr b/tests/ui/resolve/bad-env-capture2.stderr index 811c259de6bd6..403fe2d32b980 100644 --- a/tests/ui/resolve/bad-env-capture2.stderr +++ b/tests/ui/resolve/bad-env-capture2.stderr @@ -1,5 +1,5 @@ error[E0434]: can't capture dynamic environment in a fn item - --> $DIR/bad-env-capture2.rs:3:27 + --> $DIR/bad-env-capture2.rs:2:27 | LL | fn bar() { log(debug, x); } | ^ @@ -7,13 +7,13 @@ LL | fn bar() { log(debug, x); } = help: use the `|| { ... }` closure form instead error[E0425]: cannot find value `debug` in this scope - --> $DIR/bad-env-capture2.rs:3:20 + --> $DIR/bad-env-capture2.rs:2:20 | LL | fn bar() { log(debug, x); } | ^^^^^ not found in this scope error[E0425]: cannot find function `log` in this scope - --> $DIR/bad-env-capture2.rs:3:16 + --> $DIR/bad-env-capture2.rs:2:16 | LL | fn bar() { log(debug, x); } | ^^^ not found in this scope diff --git a/tests/ui/resolve/bad-env-capture3.rs b/tests/ui/resolve/bad-env-capture3.rs index 849b84cb1ab77..8f5440d333974 100644 --- a/tests/ui/resolve/bad-env-capture3.rs +++ b/tests/ui/resolve/bad-env-capture3.rs @@ -1,7 +1,9 @@ -//@ error-pattern: can't capture dynamic environment in a fn item fn foo(x: isize) { fn mth() { fn bar() { log(debug, x); } + //~^ ERROR can't capture dynamic environment in a fn item + //~| ERROR cannot find value `debug` in this scope + //~| ERROR cannot find function `log` in this scope } } diff --git a/tests/ui/resolve/bad-env-capture3.stderr b/tests/ui/resolve/bad-env-capture3.stderr index eab37fde96e4c..962eb72ee68bf 100644 --- a/tests/ui/resolve/bad-env-capture3.stderr +++ b/tests/ui/resolve/bad-env-capture3.stderr @@ -1,5 +1,5 @@ error[E0434]: can't capture dynamic environment in a fn item - --> $DIR/bad-env-capture3.rs:4:31 + --> $DIR/bad-env-capture3.rs:3:31 | LL | fn bar() { log(debug, x); } | ^ @@ -7,13 +7,13 @@ LL | fn bar() { log(debug, x); } = help: use the `|| { ... }` closure form instead error[E0425]: cannot find value `debug` in this scope - --> $DIR/bad-env-capture3.rs:4:24 + --> $DIR/bad-env-capture3.rs:3:24 | LL | fn bar() { log(debug, x); } | ^^^^^ not found in this scope error[E0425]: cannot find function `log` in this scope - --> $DIR/bad-env-capture3.rs:4:20 + --> $DIR/bad-env-capture3.rs:3:20 | LL | fn bar() { log(debug, x); } | ^^^ not found in this scope diff --git a/tests/ui/resolve/crate-called-as-function.rs b/tests/ui/resolve/crate-called-as-function.rs index e8f52c0c029aa..30922d923f48b 100644 --- a/tests/ui/resolve/crate-called-as-function.rs +++ b/tests/ui/resolve/crate-called-as-function.rs @@ -1,3 +1,3 @@ fn main() { - ::foo() //~ cannot find external crate `foo` in the crate root + ::foo() //~ ERROR cannot find external crate `foo` in the crate root } diff --git a/tests/ui/resolve/dont-compute-arg-names-for-non-fn.rs b/tests/ui/resolve/dont-compute-arg-names-for-non-fn.rs index 20bbbff8fd20b..4053ccdfbf141 100644 --- a/tests/ui/resolve/dont-compute-arg-names-for-non-fn.rs +++ b/tests/ui/resolve/dont-compute-arg-names-for-non-fn.rs @@ -3,7 +3,7 @@ extern crate foreign_trait_with_assoc; use foreign_trait_with_assoc::Foo; -// Make sure we don't try to call `fn_arg_names` on a non-fn item. +// Make sure we don't try to call `fn_arg_idents` on a non-fn item. impl Foo for Bar {} //~^ ERROR cannot find type `Bar` in this scope diff --git a/tests/ui/resolve/fn-item-cant-capture-dynamic-env.rs b/tests/ui/resolve/fn-item-cant-capture-dynamic-env.rs new file mode 100644 index 0000000000000..4f8c04e3fa9dd --- /dev/null +++ b/tests/ui/resolve/fn-item-cant-capture-dynamic-env.rs @@ -0,0 +1,4 @@ +fn main() { + let bar: isize = 5; + fn foo() -> isize { return bar; } //~ ERROR can't capture dynamic environment in a fn item +} diff --git a/tests/ui/resolve/fn-item-cant-capture-dynamic-env.stderr b/tests/ui/resolve/fn-item-cant-capture-dynamic-env.stderr new file mode 100644 index 0000000000000..6b3e879201158 --- /dev/null +++ b/tests/ui/resolve/fn-item-cant-capture-dynamic-env.stderr @@ -0,0 +1,11 @@ +error[E0434]: can't capture dynamic environment in a fn item + --> $DIR/fn-item-cant-capture-dynamic-env.rs:3:32 + | +LL | fn foo() -> isize { return bar; } + | ^^^ + | + = help: use the `|| { ... }` closure form instead + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0434`. diff --git a/tests/ui/resolve/issue-118295.rs b/tests/ui/resolve/issue-118295.rs index 37a49baee80c4..d8c6310300a31 100644 --- a/tests/ui/resolve/issue-118295.rs +++ b/tests/ui/resolve/issue-118295.rs @@ -1,11 +1,11 @@ macro_rules! {} //~^ ERROR cannot find macro `macro_rules` in this scope //~| NOTE maybe you have forgotten to define a name for this `macro_rules!` -//~| put a macro name here +//~| NOTE put a macro name here macro_rules!{} //~^ ERROR cannot find macro `macro_rules` in this scope //~| NOTE maybe you have forgotten to define a name for this `macro_rules!` -//~| put a macro name here +//~| NOTE put a macro name here fn main() {} diff --git a/tests/ui/resolve/issue-21221-1.stderr b/tests/ui/resolve/issue-21221-1.stderr index ccf03afaa19fe..dafa41bf312f5 100644 --- a/tests/ui/resolve/issue-21221-1.stderr +++ b/tests/ui/resolve/issue-21221-1.stderr @@ -19,6 +19,17 @@ error[E0412]: cannot find type `Mul` in this scope LL | fn getMul() -> Mul { | ^^^ not found in this scope | +note: these items exist but are inaccessible + --> $DIR/issue-21221-1.rs:10:5 + | +LL | enum Mul { + | ^^^^^^^^ `mul3::Mul`: not accessible +... +LL | type Mul = String; + | ^^^^^^^^^^^^^^^^^^ `mul4::Mul`: not accessible +... +LL | struct Mul{ + | ^^^^^^^^^^ `mul5::Mul`: not accessible help: consider importing one of these traits | LL + use std::ops::Mul; diff --git a/tests/ui/resolve/issue-23716.rs b/tests/ui/resolve/issue-23716.rs index e9139c0aa143f..61b42e29f10e1 100644 --- a/tests/ui/resolve/issue-23716.rs +++ b/tests/ui/resolve/issue-23716.rs @@ -1,8 +1,10 @@ +//@ dont-require-annotations: NOTE + static foo: i32 = 0; fn bar(foo: i32) {} //~^ ERROR function parameters cannot shadow statics -//~| cannot be named the same as a static +//~| NOTE cannot be named the same as a static mod submod { pub static answer: i32 = 42; @@ -12,6 +14,6 @@ use self::submod::answer; fn question(answer: i32) {} //~^ ERROR function parameters cannot shadow statics -//~| cannot be named the same as a static +//~| NOTE cannot be named the same as a static fn main() { } diff --git a/tests/ui/resolve/issue-23716.stderr b/tests/ui/resolve/issue-23716.stderr index 8b89c350c8460..23650c48ac488 100644 --- a/tests/ui/resolve/issue-23716.stderr +++ b/tests/ui/resolve/issue-23716.stderr @@ -1,5 +1,5 @@ error[E0530]: function parameters cannot shadow statics - --> $DIR/issue-23716.rs:3:8 + --> $DIR/issue-23716.rs:5:8 | LL | static foo: i32 = 0; | -------------------- the static `foo` is defined here @@ -8,7 +8,7 @@ LL | fn bar(foo: i32) {} | ^^^ cannot be named the same as a static error[E0530]: function parameters cannot shadow statics - --> $DIR/issue-23716.rs:13:13 + --> $DIR/issue-23716.rs:15:13 | LL | use self::submod::answer; | -------------------- the static `answer` is imported here diff --git a/tests/ui/resolve/issue-5035.rs b/tests/ui/resolve/issue-5035.rs index 49fa312f9d257..82c4bc0d5ef9e 100644 --- a/tests/ui/resolve/issue-5035.rs +++ b/tests/ui/resolve/issue-5035.rs @@ -1,9 +1,11 @@ +//@ dont-require-annotations: NOTE + trait I {} type K = dyn I; impl K for isize {} //~ ERROR expected trait, found type alias `K` use ImportError; //~ ERROR unresolved import `ImportError` [E0432] - //~^ no `ImportError` in the root + //~^ NOTE no `ImportError` in the root impl ImportError for () {} // check that this is not an additional error (cf. issue #35142) fn main() {} diff --git a/tests/ui/resolve/issue-5035.stderr b/tests/ui/resolve/issue-5035.stderr index b249aaa4b2803..f5717438fc8c9 100644 --- a/tests/ui/resolve/issue-5035.stderr +++ b/tests/ui/resolve/issue-5035.stderr @@ -1,11 +1,11 @@ error[E0432]: unresolved import `ImportError` - --> $DIR/issue-5035.rs:5:5 + --> $DIR/issue-5035.rs:7:5 | LL | use ImportError; | ^^^^^^^^^^^ no `ImportError` in the root error[E0404]: expected trait, found type alias `K` - --> $DIR/issue-5035.rs:3:6 + --> $DIR/issue-5035.rs:5:6 | LL | trait I {} | ------- similarly named trait `I` defined here diff --git a/tests/ui/resolve/multiple_definitions_attribute_merging.stderr b/tests/ui/resolve/multiple_definitions_attribute_merging.stderr index ac6307c7a69eb..10ce68e506e6d 100644 --- a/tests/ui/resolve/multiple_definitions_attribute_merging.stderr +++ b/tests/ui/resolve/multiple_definitions_attribute_merging.stderr @@ -15,8 +15,8 @@ LL | #[derive(PartialEq)] LL | #[repr(C)] LL | struct Dealigned(u8, T); | ^ - | - = + + Box query stack during panic: #0 [mir_built] building MIR for `::eq` diff --git a/tests/ui/resolve/parse-error-resolve.rs b/tests/ui/resolve/parse-error-resolve.rs index 1e0772648afec..cb15ec7640363 100644 --- a/tests/ui/resolve/parse-error-resolve.rs +++ b/tests/ui/resolve/parse-error-resolve.rs @@ -5,3 +5,5 @@ fn main() { let _ = "" + 1; //~ ERROR E0369 parse_error::Canonical.foo(); // ok, `parse_error.rs` had parse errors } + +//~? ERROR expected one of `+`, `,`, `::`, `=`, or `>`, found `From` diff --git a/tests/ui/resolve/prelude-order.rs b/tests/ui/resolve/prelude-order.rs new file mode 100644 index 0000000000000..a3f194270d483 --- /dev/null +++ b/tests/ui/resolve/prelude-order.rs @@ -0,0 +1,89 @@ +//@ proc-macro:macro_helpers.rs +//@ compile-flags: --crate-type=lib + +/* There are 5 preludes and 3 namespaces. Test the order in which they are resolved. + * See https://doc.rust-lang.org/nightly/reference/names/preludes.html. + * + * Macros cannot be in the type or value namespace. + * Tools and extern crates cannot be in the macro or value namespace. + * + * Test the following truth tables: + +Type: +| ...... | tool | extern | macro | lang | libs | +| tool | N/A | mirror +| extern | extern | N/A | universe +| macro | N/A | N/A | N/A | +| lang | tool | extern | N/A | N/A | +| libs | tool | extern | N/A | X | N/A | + +Macro: +| ...... | tool | extern | macro | lang | libs | +| tool | N/A | mirror +| extern | N/A | N/A | universe +| macro | N/A | N/A | N/A | +| lang | N/A | N/A | macro | N/A | +| libs | N/A | N/A | macro | X | N/A | + +Value: N/A. Only libs has items in the value namespace. + +† ambiguous +X don't care (controlled namespace with no overlap) + +* Types are tested with `#[name::inner]`. Macros are tested with `#[name]`. +* WARNING: I have found in testing that attribute macros give ambiguity errors in some contexts +* instead of choosing a prelude. Have not been able to replicate. +* +* There should be 7 total tests. +* See `rustc_resolve::ident::visit_scopes` for more information, +* and for a definition of "controlled namespace". +*/ + +#![feature(register_tool)] + +/* tool prelude */ +#![register_tool(type_ns)] // extern prelude. type. +#![register_tool(i8)] // lang prelude. type. +#![register_tool(Sync)] // libs prelude. type. + +/* extern prelude */ +extern crate macro_helpers as type_ns; // tool prelude. type. +extern crate macro_helpers as usize; // lang prelude. type. +extern crate macro_helpers as Option; // libs prelude. type. + +/* macro_use prelude */ +#[macro_use] +extern crate macro_helpers as _; + +/* lang and libs implicitly in scope */ + +// tool/extern -> extern +#[type_ns::inner] //~ ERROR could not find `inner` in `type_ns` +fn t1() {} + +// tool/lang -> tool +#[i8::inner] // ok +fn t2() {} + +// tool/libs -> tool +#[Sync::not_real] // ok +fn t3() {} + +// extern/lang -> extern +#[usize::inner] //~ ERROR could not find `inner` in `usize` +fn e1() {} // NOTE: testing with `-> usize` isn't valid, crates aren't considered in that scope + // (unless they have generic arguments, for some reason.) + +// extern/libs -> extern +// https://github.com/rust-lang/rust/issues/139095 +fn e2() -> Option { None } //~ ERROR: expected type, found crate + +// macro/libs -> macro +#[test] //~ ERROR mismatched types +fn m1() {} + +// macro/lang -> macro +#[global_allocator] //~ ERROR mismatched types +fn m2() {} + +// lang/libs: no items that currently overlap, in either macro or type ns. diff --git a/tests/ui/resolve/prelude-order.stderr b/tests/ui/resolve/prelude-order.stderr new file mode 100644 index 0000000000000..1b9cc94285aa5 --- /dev/null +++ b/tests/ui/resolve/prelude-order.stderr @@ -0,0 +1,47 @@ +error[E0433]: failed to resolve: could not find `inner` in `type_ns` + --> $DIR/prelude-order.rs:61:12 + | +LL | #[type_ns::inner] + | ^^^^^ could not find `inner` in `type_ns` + +error[E0433]: failed to resolve: could not find `inner` in `usize` + --> $DIR/prelude-order.rs:73:10 + | +LL | #[usize::inner] + | ^^^^^ could not find `inner` in `usize` + +error[E0573]: expected type, found crate `Option` + --> $DIR/prelude-order.rs:79:12 + | +LL | fn e2() -> Option { None } + | ^^^^^^^^^^^ not a type + | +help: consider importing this enum instead + | +LL + use std::option::Option; + | + +error[E0308]: mismatched types + --> $DIR/prelude-order.rs:82:1 + | +LL | #[test] + | ^^^^^^^- help: try adding a return type: `-> &'static str` + | | + | expected `()`, found `&str` + | + = note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/prelude-order.rs:86:1 + | +LL | #[global_allocator] + | ^^^^^^^^^^^^^^^^^^^- help: try adding a return type: `-> &'static str` + | | + | expected `()`, found `&str` + | + = note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0308, E0433, E0573. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/resolve/prim-crate-partial-res.rs b/tests/ui/resolve/prim-crate-partial-res.rs new file mode 100644 index 0000000000000..955f4fa2aee71 --- /dev/null +++ b/tests/ui/resolve/prim-crate-partial-res.rs @@ -0,0 +1,8 @@ +//@ aux-build: empty.rs + +extern crate empty as usize; + +fn foo() -> usize<()> { 0 } +//~^ ERROR type arguments are not allowed on builtin type `usize` + +fn main() {} diff --git a/tests/ui/resolve/prim-crate-partial-res.stderr b/tests/ui/resolve/prim-crate-partial-res.stderr new file mode 100644 index 0000000000000..d10d37c9f1b08 --- /dev/null +++ b/tests/ui/resolve/prim-crate-partial-res.stderr @@ -0,0 +1,17 @@ +error[E0109]: type arguments are not allowed on builtin type `usize` + --> $DIR/prim-crate-partial-res.rs:5:19 + | +LL | fn foo() -> usize<()> { 0 } + | ----- ^^ type argument not allowed + | | + | not allowed on builtin type `usize` + | +help: primitive type `usize` doesn't have generic parameters + | +LL - fn foo() -> usize<()> { 0 } +LL + fn foo() -> usize { 0 } + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0109`. diff --git a/tests/ui/resolve/proc_macro_generated_packed.stderr b/tests/ui/resolve/proc_macro_generated_packed.stderr index 8b70059503486..47de5be82ddfd 100644 --- a/tests/ui/resolve/proc_macro_generated_packed.stderr +++ b/tests/ui/resolve/proc_macro_generated_packed.stderr @@ -6,8 +6,8 @@ LL | #[derive(PartialEq)] ... LL | struct Dealigned(u8, T); | ^ - | - = + + Box query stack during panic: #0 [mir_built] building MIR for `::eq` diff --git a/tests/ui/resolve/resolve-conflict-item-vs-import.rs b/tests/ui/resolve/resolve-conflict-item-vs-import.rs index 4308c7a27501a..830f02cdcbe2b 100644 --- a/tests/ui/resolve/resolve-conflict-item-vs-import.rs +++ b/tests/ui/resolve/resolve-conflict-item-vs-import.rs @@ -1,8 +1,8 @@ -use std::mem::transmute; +use std::mem::transmute; //~ NOTE previous import of the value `transmute` here fn transmute() {} //~^ ERROR the name `transmute` is defined multiple times -//~| `transmute` redefined here -//~| `transmute` must be defined only once in the value namespace of this module +//~| NOTE `transmute` redefined here +//~| NOTE `transmute` must be defined only once in the value namespace of this module fn main() { } diff --git a/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.rs b/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.rs new file mode 100644 index 0000000000000..d2aa61186bcd0 --- /dev/null +++ b/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.rs @@ -0,0 +1,15 @@ +struct Foo { + x: i32 +} + +impl Foo { + fn foo(&self) { + let _ = format!("{x}"); //~ ERROR cannot find value `x` in this scope [E0425] + let _ = format!("{x }"); //~ ERROR cannot find value `x` in this scope [E0425] + let _ = format!("{ x}"); //~ ERROR invalid format string: expected `}`, found `x` + let _ = format!("{}", x); //~ ERROR cannot find value `x` in this scope [E0425] + println!("{x}"); //~ ERROR cannot find value `x` in this scope [E0425] + } +} + +fn main(){} diff --git a/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.stderr b/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.stderr new file mode 100644 index 0000000000000..0a84848081d5c --- /dev/null +++ b/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.stderr @@ -0,0 +1,48 @@ +error: invalid format string: expected `}`, found `x` + --> $DIR/sugg-field-in-format-string-issue-141136.rs:9:28 + | +LL | let _ = format!("{ x}"); + | - ^ expected `}` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/sugg-field-in-format-string-issue-141136.rs:7:27 + | +LL | let _ = format!("{x}"); + | ^ + | + = help: you might have meant to use the available field in a format string: `"{}", self.x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/sugg-field-in-format-string-issue-141136.rs:8:27 + | +LL | let _ = format!("{x }"); + | ^^ + | + = help: you might have meant to use the available field in a format string: `"{}", self.x` + +error[E0425]: cannot find value `x` in this scope + --> $DIR/sugg-field-in-format-string-issue-141136.rs:10:31 + | +LL | let _ = format!("{}", x); + | ^ + | +help: you might have meant to use the available field + | +LL | let _ = format!("{}", self.x); + | +++++ + +error[E0425]: cannot find value `x` in this scope + --> $DIR/sugg-field-in-format-string-issue-141136.rs:11:20 + | +LL | println!("{x}"); + | ^ + | + = help: you might have meant to use the available field in a format string: `"{}", self.x` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/resolve/tool-import.rs b/tests/ui/resolve/tool-import.rs index bde375a2c490e..951505b92a0bf 100644 --- a/tests/ui/resolve/tool-import.rs +++ b/tests/ui/resolve/tool-import.rs @@ -1,7 +1,7 @@ //@ edition: 2018 use clippy::time::Instant; -//~^ `clippy` is a tool module +//~^ ERROR `clippy` is a tool module fn main() { Instant::now(); diff --git a/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs b/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs index ecd3f58811904..7216b0294dcec 100644 --- a/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs +++ b/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs @@ -4,15 +4,15 @@ struct A { impl A { fn new(cofig: String) -> Self { - Self { config } //~ Error cannot find value `config` in this scope + Self { config } //~ ERROR cannot find value `config` in this scope } fn do_something(cofig: String) { - println!("{config}"); //~ Error cannot find value `config` in this scope + println!("{config}"); //~ ERROR cannot find value `config` in this scope } fn self_is_available(self, cofig: String) { - println!("{config}"); //~ Error cannot find value `config` in this scope + println!("{config}"); //~ ERROR cannot find value `config` in this scope } } diff --git a/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr b/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr index 5832cb69a3dd8..1ecbfee17bc70 100644 --- a/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr +++ b/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr @@ -20,17 +20,9 @@ error[E0425]: cannot find value `config` in this scope --> $DIR/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs:15:20 | LL | println!("{config}"); - | ^^^^^^ - | -help: you might have meant to use the available field - | -LL | println!("{self.config}"); - | +++++ -help: a local variable with a similar name exists - | -LL - println!("{config}"); -LL + println!("{cofig}"); + | ^^^^^^ help: a local variable with a similar name exists: `cofig` | + = help: you might have meant to use the available field in a format string: `"{}", self.config` error[E0425]: cannot find value `bah` in this scope --> $DIR/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs:33:9 diff --git a/tests/ui/return/dont-suggest-through-inner-const.stderr b/tests/ui/return/dont-suggest-through-inner-const.stderr index 6aeee74b0adf9..fc98b7373754e 100644 --- a/tests/ui/return/dont-suggest-through-inner-const.stderr +++ b/tests/ui/return/dont-suggest-through-inner-const.stderr @@ -1,9 +1,3 @@ -error[E0308]: mismatched types - --> $DIR/dont-suggest-through-inner-const.rs:4:9 - | -LL | 0 - | ^ expected `()`, found integer - error[E0308]: mismatched types --> $DIR/dont-suggest-through-inner-const.rs:1:17 | @@ -12,6 +6,12 @@ LL | const fn f() -> usize { | | | implicitly returns `()` as its body has no tail or `return` expression +error[E0308]: mismatched types + --> $DIR/dont-suggest-through-inner-const.rs:4:9 + | +LL | 0 + | ^ expected `()`, found integer + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/return/ret-non-nil.rs b/tests/ui/return/ret-non-nil.rs index 1d039ffe18c26..b9a53086b3c36 100644 --- a/tests/ui/return/ret-non-nil.rs +++ b/tests/ui/return/ret-non-nil.rs @@ -1,7 +1,5 @@ -//@ error-pattern: `return;` in a function whose return type is not `()` - fn f() { return; } -fn g() -> isize { return; } +fn g() -> isize { return; } //~ ERROR `return;` in a function whose return type is not `()` fn main() { f(); g(); } diff --git a/tests/ui/return/ret-non-nil.stderr b/tests/ui/return/ret-non-nil.stderr index 802900e61a30e..44edecf501fcd 100644 --- a/tests/ui/return/ret-non-nil.stderr +++ b/tests/ui/return/ret-non-nil.stderr @@ -1,5 +1,5 @@ error[E0069]: `return;` in a function whose return type is not `()` - --> $DIR/ret-non-nil.rs:5:19 + --> $DIR/ret-non-nil.rs:3:19 | LL | fn g() -> isize { return; } | ----- ^^^^^^ return type is not `()` diff --git a/tests/ui/return/tail-expr-as-potential-return.rs b/tests/ui/return/tail-expr-as-potential-return.rs index 11ecddb049b57..2e638f1897c22 100644 --- a/tests/ui/return/tail-expr-as-potential-return.rs +++ b/tests/ui/return/tail-expr-as-potential-return.rs @@ -60,7 +60,6 @@ fn method() -> Option { Receiver.generic(); //~^ ERROR type annotations needed //~| HELP consider specifying the generic argument - //~| HELP you might have meant to return this to infer its type parameters } None diff --git a/tests/ui/return/tail-expr-as-potential-return.stderr b/tests/ui/return/tail-expr-as-potential-return.stderr index 756de2b5a1668..8105b2df3fea6 100644 --- a/tests/ui/return/tail-expr-as-potential-return.stderr +++ b/tests/ui/return/tail-expr-as-potential-return.stderr @@ -57,10 +57,6 @@ help: consider specifying the generic argument | LL | Receiver.generic::(); | +++++ -help: you might have meant to return this to infer its type parameters - | -LL | return Receiver.generic(); - | ++++++ error: aborting due to 4 previous errors diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.rs b/tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.rs index 6b85ada3aadee..60e5452edb38c 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.rs +++ b/tests/ui/rfcs/rfc-0000-never_patterns/diverges-not.rs @@ -1,5 +1,5 @@ +//@ edition: 2024 #![feature(never_patterns)] -#![feature(let_chains)] #![allow(incomplete_features)] #![deny(unreachable_patterns)] diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/parse.rs b/tests/ui/rfcs/rfc-0000-never_patterns/parse.rs index 566bb07164640..e004e6612107a 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/parse.rs +++ b/tests/ui/rfcs/rfc-0000-never_patterns/parse.rs @@ -65,7 +65,7 @@ fn parse(x: Void) { let res: Result = Ok(false); let Ok(_) = res; let Ok(_) | Err(!) = &res; // Disallowed; see #82048. - //~^ ERROR top-level or-patterns are not allowed in `let` bindings + //~^ ERROR `let` bindings require top-level or-patterns in parentheses let (Ok(_) | Err(!)) = &res; let (Ok(_) | Err(&!)) = res.as_ref(); diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/parse.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/parse.stderr index 05980510f1cde..320e157f71724 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/parse.stderr +++ b/tests/ui/rfcs/rfc-0000-never_patterns/parse.stderr @@ -26,7 +26,7 @@ error: expected one of `,`, `=>`, `if`, `|`, or `}`, found `<=` LL | Some(!) <= | ^^ expected one of `,`, `=>`, `if`, `|`, or `}` -error: top-level or-patterns are not allowed in `let` bindings +error: `let` bindings require top-level or-patterns in parentheses --> $DIR/parse.rs:67:9 | LL | let Ok(_) | Err(!) = &res; // Disallowed; see #82048. diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr index 0858434962664..c5756269def66 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `structural_match` - --> $DIR/feature-gate.rs:29:6 + --> $DIR/feature-gate.rs:27:6 | LL | impl std::marker::StructuralPartialEq for Foo { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.rs index 711b07fee3b6f..694081654d58c 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.rs @@ -3,11 +3,10 @@ // used in a match. //@ revisions: with_gate no_gate - +//@[with_gate] check-pass // gate-test-structural_match #![allow(unused)] -#![feature(rustc_attrs)] #![cfg_attr(with_gate, feature(structural_match))] @@ -17,8 +16,7 @@ struct Foo { const FOO: Foo = Foo { x: 0 }; -#[rustc_error] -fn main() { //[with_gate]~ ERROR fatal error triggered by #[rustc_error] +fn main() { let y = Foo { x: 1 }; match y { FOO => { } diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.with_gate.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.with_gate.stderr deleted file mode 100644 index 505b7d79cadb8..0000000000000 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.with_gate.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fatal error triggered by #[rustc_error] - --> $DIR/feature-gate.rs:21:1 - | -LL | fn main() { - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/rfcs/rfc-1717-dllimport/missing-link-attr.rs b/tests/ui/rfcs/rfc-1717-dllimport/missing-link-attr.rs index d54b428bf22f8..9dc856959c00d 100644 --- a/tests/ui/rfcs/rfc-1717-dllimport/missing-link-attr.rs +++ b/tests/ui/rfcs/rfc-1717-dllimport/missing-link-attr.rs @@ -1,4 +1,5 @@ //@ compile-flags: -l foo:bar -//@ error-pattern: renaming of the library `foo` was specified #![crate_type = "lib"] + +//~? ERROR renaming of the library `foo` was specified diff --git a/tests/ui/rfcs/rfc-1717-dllimport/multiple-renames.rs b/tests/ui/rfcs/rfc-1717-dllimport/multiple-renames.rs index ec1a246245e3e..69132895510bb 100644 --- a/tests/ui/rfcs/rfc-1717-dllimport/multiple-renames.rs +++ b/tests/ui/rfcs/rfc-1717-dllimport/multiple-renames.rs @@ -1,7 +1,8 @@ //@ compile-flags: -l foo:bar -l foo:baz -//@ error-pattern: multiple renamings were specified for library #![crate_type = "lib"] #[link(name = "foo")] extern "C" {} + +//~? ERROR multiple renamings were specified for library `foo` diff --git a/tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.rs b/tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.rs index 2a13d22e22aeb..e1fec30593282 100644 --- a/tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.rs +++ b/tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.rs @@ -1,5 +1,4 @@ //@ compile-flags: -l dylib=foo:bar -//@ error-pattern: overriding linking modifiers from command line is not supported #![feature(native_link_modifiers_as_needed)] @@ -7,3 +6,4 @@ #[link(name = "foo", kind = "dylib", modifiers = "-as-needed")] extern "C" {} +//~^ ERROR overriding linking modifiers from command line is not supported diff --git a/tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.stderr b/tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.stderr index ce145689f9014..84b197a9b986f 100644 --- a/tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.stderr +++ b/tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.stderr @@ -1,5 +1,5 @@ error: overriding linking modifiers from command line is not supported - --> $DIR/rename-modifiers.rs:9:1 + --> $DIR/rename-modifiers.rs:8:1 | LL | extern "C" {} | ^^^^^^^^^^^^^ diff --git a/tests/ui/rfcs/rfc-1717-dllimport/rename-to-empty.rs b/tests/ui/rfcs/rfc-1717-dllimport/rename-to-empty.rs index 39205a11dd735..6fee80f02948c 100644 --- a/tests/ui/rfcs/rfc-1717-dllimport/rename-to-empty.rs +++ b/tests/ui/rfcs/rfc-1717-dllimport/rename-to-empty.rs @@ -1,7 +1,8 @@ //@ compile-flags: -l foo: -//@ error-pattern: an empty renaming target was specified for library #![crate_type = "lib"] #[link(name = "foo")] extern "C" {} + +//~? ERROR an empty renaming target was specified for library `foo` diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.stderr index f1f53e300abf9..99fd83e7b6ffd 100644 --- a/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.stderr +++ b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.stderr @@ -4,11 +4,11 @@ error[E0277]: the trait bound `Something: Termination` is not satisfied LL | fn main() -> Something { | ^^^^^^^^^ the trait `Termination` is not implemented for `Something` | -note: required by a bound in `Main::{synthetic#0}` +note: required by a bound in `Main::main::{anon_assoc#0}` --> $DIR/issue-103052-2.rs:3:27 | LL | fn main() -> impl std::process::Termination; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Main::{synthetic#0}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Main::main::{anon_assoc#0}` error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr index 0a703367d969d..b89c5e8dda840 100644 --- a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr +++ b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr @@ -9,7 +9,6 @@ LL | fn can_parse_zero_as_f32() -> Result { = note: required for `Result` to implement `Termination` note: required by a bound in `assert_test_result` --> $SRC_DIR/test/src/lib.rs:LL:COL - = note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs index 69a283c31633d..167f90b28d5c9 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs @@ -1,22 +1,24 @@ +//@ dont-require-annotations: NOTE + #![deny(unreachable_patterns)] #[non_exhaustive] pub enum NonExhaustiveEnum { Unit, - //~^ not covered + //~^ NOTE not covered Tuple(u32), - //~^ not covered + //~^ NOTE not covered Struct { field: u32 } - //~^ not covered + //~^ NOTE not covered } pub enum NormalEnum { Unit, - //~^ not covered + //~^ NOTE not covered Tuple(u32), - //~^ not covered + //~^ NOTE not covered Struct { field: u32 } - //~^ not covered + //~^ NOTE not covered } #[non_exhaustive] diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr index 100e0a501e016..2112344fe6e41 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr @@ -1,5 +1,5 @@ error: unreachable pattern - --> $DIR/enum_same_crate_empty_match.rs:28:9 + --> $DIR/enum_same_crate_empty_match.rs:30:9 | LL | _ => {} | ^------ @@ -9,19 +9,19 @@ LL | _ => {} | = note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types note: the lint level is defined here - --> $DIR/enum_same_crate_empty_match.rs:1:9 + --> $DIR/enum_same_crate_empty_match.rs:3:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error[E0004]: non-exhaustive patterns: `NonExhaustiveEnum::Unit`, `NonExhaustiveEnum::Tuple(_)` and `NonExhaustiveEnum::Struct { .. }` not covered - --> $DIR/enum_same_crate_empty_match.rs:33:11 + --> $DIR/enum_same_crate_empty_match.rs:35:11 | LL | match NonExhaustiveEnum::Unit {} | ^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonExhaustiveEnum::Unit`, `NonExhaustiveEnum::Tuple(_)` and `NonExhaustiveEnum::Struct { .. }` not covered | note: `NonExhaustiveEnum` defined here - --> $DIR/enum_same_crate_empty_match.rs:4:10 + --> $DIR/enum_same_crate_empty_match.rs:6:10 | LL | pub enum NonExhaustiveEnum { | ^^^^^^^^^^^^^^^^^ @@ -42,13 +42,13 @@ LL ~ } | error[E0004]: non-exhaustive patterns: `NormalEnum::Unit`, `NormalEnum::Tuple(_)` and `NormalEnum::Struct { .. }` not covered - --> $DIR/enum_same_crate_empty_match.rs:35:11 + --> $DIR/enum_same_crate_empty_match.rs:37:11 | LL | match NormalEnum::Unit {} | ^^^^^^^^^^^^^^^^ patterns `NormalEnum::Unit`, `NormalEnum::Tuple(_)` and `NormalEnum::Struct { .. }` not covered | note: `NormalEnum` defined here - --> $DIR/enum_same_crate_empty_match.rs:13:10 + --> $DIR/enum_same_crate_empty_match.rs:15:10 | LL | pub enum NormalEnum { | ^^^^^^^^^^ diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.rs index 5809e56fb7b49..1e746fdbbeaa5 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.rs @@ -1,5 +1,7 @@ // Test that the `non_exhaustive_omitted_patterns` lint is triggered correctly. +//@ dont-require-annotations: NOTE + #![feature(non_exhaustive_omitted_patterns_lint, unstable_test_feature)] #![deny(unreachable_patterns)] @@ -63,14 +65,14 @@ fn main() { } match non_enum { - //~^ some variants are not matched explicitly + //~^ ERROR some variants are not matched explicitly NonExhaustiveEnum::Unit => {} NonExhaustiveEnum::Tuple(_) => {} _ => {} } match non_enum { - //~^ some variants are not matched explicitly + //~^ ERROR some variants are not matched explicitly NonExhaustiveEnum::Unit | NonExhaustiveEnum::Struct { .. } => {} _ => {} } @@ -91,7 +93,7 @@ fn main() { _ => {} } match (non_enum, true) { - //~^ some variants are not matched explicitly + //~^ ERROR some variants are not matched explicitly (NonExhaustiveEnum::Unit, true) => {} (NonExhaustiveEnum::Tuple(_), false) => {} _ => {} @@ -104,14 +106,14 @@ fn main() { _ => {} } match (true, non_enum) { - //~^ some variants are not matched explicitly + //~^ ERROR some variants are not matched explicitly (true, NonExhaustiveEnum::Unit) => {} (false, NonExhaustiveEnum::Tuple(_)) => {} _ => {} } match Some(non_enum) { - //~^ some variants are not matched explicitly + //~^ ERROR some variants are not matched explicitly Some(NonExhaustiveEnum::Unit) => {} Some(NonExhaustiveEnum::Tuple(_)) => {} _ => {} @@ -127,7 +129,7 @@ fn main() { } match NestedNonExhaustive::B { - //~^ some variants are not matched explicitly + //~^ ERROR some variants are not matched explicitly NestedNonExhaustive::A(NonExhaustiveEnum::Unit) => {} NestedNonExhaustive::A(_) => {} NestedNonExhaustive::B => {} @@ -138,17 +140,17 @@ fn main() { VariantNonExhaustive::Baz(_, _) => {} VariantNonExhaustive::Bar { x, .. } => {} } - //~^^ some fields are not explicitly listed + //~^^ ERROR some fields are not explicitly listed let FunctionalRecord { first_field, second_field, .. } = FunctionalRecord::default(); - //~^ some fields are not explicitly listed + //~^ ERROR some fields are not explicitly listed // Ok: this is local let Foo { a, b, .. } = Foo::default(); let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default(); - //~^ some fields are not explicitly listed - //~^^ some fields are not explicitly listed + //~^ ERROR some fields are not explicitly listed + //~^^ ERROR some fields are not explicitly listed // Ok: this tests https://github.com/rust-lang/rust/issues/89382 let MixedVisFields { a, b, .. } = MixedVisFields::default(); @@ -182,7 +184,7 @@ fn main() { if let NonExhaustiveEnum::Tuple(_) = non_enum {} match UnstableEnum::Stable { - //~^ some variants are not matched explicitly + //~^ ERROR some variants are not matched explicitly UnstableEnum::Stable => {} UnstableEnum::Stable2 => {} _ => {} @@ -204,19 +206,19 @@ fn main() { } match OnlyUnstableEnum::Unstable { - //~^ some variants are not matched explicitly + //~^ ERROR some variants are not matched explicitly OnlyUnstableEnum::Unstable => {} _ => {} } let OnlyUnstableStruct { unstable, .. } = OnlyUnstableStruct::new(); - //~^ some fields are not explicitly listed + //~^ ERROR some fields are not explicitly listed // OK: both unstable fields are matched with feature on let OnlyUnstableStruct { unstable, unstable2, .. } = OnlyUnstableStruct::new(); let UnstableStruct { stable, stable2, .. } = UnstableStruct::default(); - //~^ some fields are not explicitly listed + //~^ ERROR some fields are not explicitly listed // OK: both unstable and stable fields are matched with feature on let UnstableStruct { stable, stable2, unstable, .. } = UnstableStruct::default(); @@ -226,34 +228,34 @@ fn main() { // Ok: missing patterns will be blocked by the pattern being refutable let local_refutable @ NonExhaustiveEnum::Unit = NonExhaustiveEnum::Unit; - //~^ refutable pattern in local binding + //~^ ERROR refutable pattern in local binding // Check that matching on a reference results in a correct diagnostic match &non_enum { - //~^ some variants are not matched explicitly - //~| pattern `&NonExhaustiveEnum::Struct { .. }` not covered + //~^ ERROR some variants are not matched explicitly + //~| NOTE pattern `&NonExhaustiveEnum::Struct { .. }` not covered NonExhaustiveEnum::Unit => {} NonExhaustiveEnum::Tuple(_) => {} _ => {} } match (true, &non_enum) { - //~^ some variants are not matched explicitly - //~| patterns `(_, &NonExhaustiveEnum::Tuple(_))` and `(_, &NonExhaustiveEnum::Struct { .. })` not covered + //~^ ERROR some variants are not matched explicitly + //~| NOTE patterns `(_, &NonExhaustiveEnum::Tuple(_))` and `(_, &NonExhaustiveEnum::Struct { .. })` not covered (true, NonExhaustiveEnum::Unit) => {} _ => {} } match (&non_enum, true) { - //~^ some variants are not matched explicitly - //~| patterns `(&NonExhaustiveEnum::Tuple(_), _)` and `(&NonExhaustiveEnum::Struct { .. }, _)` not covered + //~^ ERROR some variants are not matched explicitly + //~| NOTE patterns `(&NonExhaustiveEnum::Tuple(_), _)` and `(&NonExhaustiveEnum::Struct { .. }, _)` not covered (NonExhaustiveEnum::Unit, true) => {} _ => {} } match Some(&non_enum) { - //~^ some variants are not matched explicitly - //~| pattern `Some(&NonExhaustiveEnum::Struct { .. })` not covered + //~^ ERROR some variants are not matched explicitly + //~| NOTE pattern `Some(&NonExhaustiveEnum::Struct { .. })` not covered Some(NonExhaustiveEnum::Unit | NonExhaustiveEnum::Tuple(_)) => {} _ => {} } diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.stderr index f89ae241f44bc..c2c9ac15ab220 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.stderr @@ -1,5 +1,5 @@ error: some fields are not explicitly listed - --> $DIR/omitted-patterns.rs:139:9 + --> $DIR/omitted-patterns.rs:141:9 | LL | VariantNonExhaustive::Bar { x, .. } => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `y` not listed @@ -7,13 +7,13 @@ LL | VariantNonExhaustive::Bar { x, .. } => {} = help: ensure that all fields are mentioned explicitly by adding the suggested fields = note: the pattern is of type `VariantNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found note: the lint level is defined here - --> $DIR/omitted-patterns.rs:45:8 + --> $DIR/omitted-patterns.rs:47:8 | LL | #[deny(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: some fields are not explicitly listed - --> $DIR/omitted-patterns.rs:143:9 + --> $DIR/omitted-patterns.rs:145:9 | LL | let FunctionalRecord { first_field, second_field, .. } = FunctionalRecord::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `third_field` not listed @@ -22,7 +22,7 @@ LL | let FunctionalRecord { first_field, second_field, .. } = FunctionalReco = note: the pattern is of type `FunctionalRecord` and the `non_exhaustive_omitted_patterns` attribute was found error: some fields are not explicitly listed - --> $DIR/omitted-patterns.rs:149:29 + --> $DIR/omitted-patterns.rs:151:29 | LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `second_field` not listed @@ -31,7 +31,7 @@ LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = Nested = note: the pattern is of type `NormalStruct` and the `non_exhaustive_omitted_patterns` attribute was found error: some fields are not explicitly listed - --> $DIR/omitted-patterns.rs:149:9 + --> $DIR/omitted-patterns.rs:151:9 | LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `foo` not listed @@ -40,7 +40,7 @@ LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = Nested = note: the pattern is of type `NestedStruct` and the `non_exhaustive_omitted_patterns` attribute was found error: some fields are not explicitly listed - --> $DIR/omitted-patterns.rs:212:9 + --> $DIR/omitted-patterns.rs:214:9 | LL | let OnlyUnstableStruct { unstable, .. } = OnlyUnstableStruct::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `unstable2` not listed @@ -49,7 +49,7 @@ LL | let OnlyUnstableStruct { unstable, .. } = OnlyUnstableStruct::new(); = note: the pattern is of type `OnlyUnstableStruct` and the `non_exhaustive_omitted_patterns` attribute was found error: some fields are not explicitly listed - --> $DIR/omitted-patterns.rs:218:9 + --> $DIR/omitted-patterns.rs:220:9 | LL | let UnstableStruct { stable, stable2, .. } = UnstableStruct::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `unstable` not listed @@ -58,7 +58,7 @@ LL | let UnstableStruct { stable, stable2, .. } = UnstableStruct::default(); = note: the pattern is of type `UnstableStruct` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:65:11 + --> $DIR/omitted-patterns.rs:67:11 | LL | match non_enum { | ^^^^^^^^ pattern `NonExhaustiveEnum::Struct { .. }` not covered @@ -67,7 +67,7 @@ LL | match non_enum { = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:72:11 + --> $DIR/omitted-patterns.rs:74:11 | LL | match non_enum { | ^^^^^^^^ pattern `NonExhaustiveEnum::Tuple(_)` not covered @@ -76,7 +76,7 @@ LL | match non_enum { = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:93:11 + --> $DIR/omitted-patterns.rs:95:11 | LL | match (non_enum, true) { | ^^^^^^^^^^^^^^^^ pattern `(NonExhaustiveEnum::Struct { .. }, _)` not covered @@ -85,7 +85,7 @@ LL | match (non_enum, true) { = note: the matched value is of type `(NonExhaustiveEnum, bool)` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:106:11 + --> $DIR/omitted-patterns.rs:108:11 | LL | match (true, non_enum) { | ^^^^^^^^^^^^^^^^ pattern `(_, NonExhaustiveEnum::Struct { .. })` not covered @@ -94,7 +94,7 @@ LL | match (true, non_enum) { = note: the matched value is of type `(bool, NonExhaustiveEnum)` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:113:11 + --> $DIR/omitted-patterns.rs:115:11 | LL | match Some(non_enum) { | ^^^^^^^^^^^^^^ pattern `Some(NonExhaustiveEnum::Struct { .. })` not covered @@ -103,7 +103,7 @@ LL | match Some(non_enum) { = note: the matched value is of type `Option` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:129:11 + --> $DIR/omitted-patterns.rs:131:11 | LL | match NestedNonExhaustive::B { | ^^^^^^^^^^^^^^^^^^^^^^ patterns `NestedNonExhaustive::C`, `NestedNonExhaustive::A(NonExhaustiveEnum::Tuple(_))` and `NestedNonExhaustive::A(NonExhaustiveEnum::Struct { .. })` not covered @@ -112,7 +112,7 @@ LL | match NestedNonExhaustive::B { = note: the matched value is of type `NestedNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:184:11 + --> $DIR/omitted-patterns.rs:186:11 | LL | match UnstableEnum::Stable { | ^^^^^^^^^^^^^^^^^^^^ pattern `UnstableEnum::Unstable` not covered @@ -121,7 +121,7 @@ LL | match UnstableEnum::Stable { = note: the matched value is of type `UnstableEnum` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:206:11 + --> $DIR/omitted-patterns.rs:208:11 | LL | match OnlyUnstableEnum::Unstable { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `OnlyUnstableEnum::Unstable2` not covered @@ -130,7 +130,7 @@ LL | match OnlyUnstableEnum::Unstable { = note: the matched value is of type `OnlyUnstableEnum` and the `non_exhaustive_omitted_patterns` attribute was found error[E0005]: refutable pattern in local binding - --> $DIR/omitted-patterns.rs:228:9 + --> $DIR/omitted-patterns.rs:230:9 | LL | let local_refutable @ NonExhaustiveEnum::Unit = NonExhaustiveEnum::Unit; | ^^^^^^^^^^^^^^^ pattern `_` not covered @@ -144,7 +144,7 @@ LL | let local_refutable @ NonExhaustiveEnum::Unit = NonExhaustiveEnum::Unit | ++++++++++++++++ error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:232:11 + --> $DIR/omitted-patterns.rs:234:11 | LL | match &non_enum { | ^^^^^^^^^ pattern `&NonExhaustiveEnum::Struct { .. }` not covered @@ -153,7 +153,7 @@ LL | match &non_enum { = note: the matched value is of type `&NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:240:11 + --> $DIR/omitted-patterns.rs:242:11 | LL | match (true, &non_enum) { | ^^^^^^^^^^^^^^^^^ patterns `(_, &NonExhaustiveEnum::Tuple(_))` and `(_, &NonExhaustiveEnum::Struct { .. })` not covered @@ -162,7 +162,7 @@ LL | match (true, &non_enum) { = note: the matched value is of type `(bool, &NonExhaustiveEnum)` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:247:11 + --> $DIR/omitted-patterns.rs:249:11 | LL | match (&non_enum, true) { | ^^^^^^^^^^^^^^^^^ patterns `(&NonExhaustiveEnum::Tuple(_), _)` and `(&NonExhaustiveEnum::Struct { .. }, _)` not covered @@ -171,7 +171,7 @@ LL | match (&non_enum, true) { = note: the matched value is of type `(&NonExhaustiveEnum, bool)` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/omitted-patterns.rs:254:11 + --> $DIR/omitted-patterns.rs:256:11 | LL | match Some(&non_enum) { | ^^^^^^^^^^^^^^^ pattern `Some(&NonExhaustiveEnum::Struct { .. })` not covered diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.rs index 6d3072f3ddd70..6e064251b7217 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.rs @@ -19,7 +19,7 @@ fn main() { #[deny(non_exhaustive_omitted_patterns)] match UnstableEnum::Stable { - //~^ some variants are not matched explicitly + //~^ ERROR some variants are not matched explicitly UnstableEnum::Stable => {} _ => {} } @@ -37,7 +37,7 @@ fn main() { #[warn(non_exhaustive_omitted_patterns)] let UnstableStruct { stable, .. } = UnstableStruct::default(); - //~^ some fields are not explicitly listed + //~^ WARN some fields are not explicitly listed // OK: stable field is matched #[warn(non_exhaustive_omitted_patterns)] diff --git a/tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs b/tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs index ef970ebd14bc2..2b21d3987b9cf 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs +++ b/tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs @@ -30,3 +30,5 @@ fn main() { (CONSTANT.file(), CONSTANT.line(), CONSTANT.column()), ); } + +//~? WARN skipping const checks diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs index 0e85515fd104a..a4baf1fe4b97d 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs +++ b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs @@ -1,11 +1,10 @@ //@ needs-asm-support -#![feature(naked_functions)] use std::arch::naked_asm; #[track_caller] //~ ERROR [E0736] //~^ ERROR `#[track_caller]` requires Rust ABI -#[naked] +#[unsafe(naked)] extern "C" fn f() { unsafe { naked_asm!(""); @@ -17,7 +16,7 @@ struct S; impl S { #[track_caller] //~ ERROR [E0736] //~^ ERROR `#[track_caller]` requires Rust ABI - #[naked] + #[unsafe(naked)] extern "C" fn g() { unsafe { naked_asm!(""); diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr index 0625ed1183ba5..d3cafbc635086 100644 --- a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr +++ b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr @@ -1,29 +1,29 @@ -error[E0736]: attribute incompatible with `#[naked]` - --> $DIR/error-with-naked.rs:6:1 +error[E0736]: attribute incompatible with `#[unsafe(naked)]` + --> $DIR/error-with-naked.rs:5:1 | LL | #[track_caller] - | ^^^^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[naked]` + | ^^^^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[unsafe(naked)]` LL | -LL | #[naked] - | -------- function marked with `#[naked]` here +LL | #[unsafe(naked)] + | ---------------- function marked with `#[unsafe(naked)]` here -error[E0736]: attribute incompatible with `#[naked]` - --> $DIR/error-with-naked.rs:18:5 +error[E0736]: attribute incompatible with `#[unsafe(naked)]` + --> $DIR/error-with-naked.rs:17:5 | LL | #[track_caller] - | ^^^^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[naked]` + | ^^^^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[unsafe(naked)]` LL | -LL | #[naked] - | -------- function marked with `#[naked]` here +LL | #[unsafe(naked)] + | ---------------- function marked with `#[unsafe(naked)]` here error[E0737]: `#[track_caller]` requires Rust ABI - --> $DIR/error-with-naked.rs:6:1 + --> $DIR/error-with-naked.rs:5:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ error[E0737]: `#[track_caller]` requires Rust ABI - --> $DIR/error-with-naked.rs:18:5 + --> $DIR/error-with-naked.rs:17:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/rfcs/rfc-2093-infer-outlives/regions-enum-not-wf.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-enum-not-wf.rs index 8b491ee4e303f..038f53034ab09 100644 --- a/tests/ui/rfcs/rfc-2093-infer-outlives/regions-enum-not-wf.rs +++ b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-enum-not-wf.rs @@ -33,7 +33,7 @@ enum RefIndirect<'a, T> { enum RefDouble<'a, 'b, T> { RefDoubleVariant1(&'a RequireOutlives<'b, T>), - //~^ the parameter type `T` may not live long enough [E0309] + //~^ ERROR the parameter type `T` may not live long enough [E0309] } fn main() {} diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs index 4b2fc4a03b620..b1e305834cb2e 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs @@ -15,11 +15,9 @@ fn _if_let_guard() { () if true && let 0 = 1 => {} //~^ ERROR `if let` guards are experimental - //~| ERROR `let` expressions in this position are unstable () if let 0 = 1 && true => {} //~^ ERROR `if let` guards are experimental - //~| ERROR `let` expressions in this position are unstable () if (let 0 = 1) && true => {} //~^ ERROR expected expression, found `let` statement @@ -33,8 +31,6 @@ fn _if_let_guard() { () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} //~^ ERROR `if let` guards are experimental - //~| ERROR `let` expressions in this position are unstable - //~| ERROR `let` expressions in this position are unstable //~| ERROR expected expression, found `let` statement //~| ERROR expected expression, found `let` statement //~| ERROR expected expression, found `let` statement @@ -42,7 +38,6 @@ fn _if_let_guard() { () if let Range { start: _, end: _ } = (true..true) && false => {} //~^ ERROR `if let` guards are experimental - //~| ERROR `let` expressions in this position are unstable _ => {} } @@ -59,10 +54,12 @@ fn _macros() { } use_expr!((let 0 = 1 && 0 == 0)); //~^ ERROR expected expression, found `let` statement + //~| ERROR expected expression, found `let` statement use_expr!((let 0 = 1)); //~^ ERROR expected expression, found `let` statement + //~| ERROR expected expression, found `let` statement match () { - #[cfg(FALSE)] + #[cfg(false)] () if let 0 = 1 => {} //~^ ERROR `if let` guards are experimental _ => {} diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr index 1c710b04897cb..19d1f4b0a573b 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr @@ -25,98 +25,98 @@ LL | () if (((let 0 = 1))) => {} | ^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:24:16 + --> $DIR/feature-gate.rs:22:16 | LL | () if (let 0 = 1) && true => {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:24:16 + --> $DIR/feature-gate.rs:22:16 | LL | () if (let 0 = 1) && true => {} | ^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:27:24 + --> $DIR/feature-gate.rs:25:24 | LL | () if true && (let 0 = 1) => {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:27:24 + --> $DIR/feature-gate.rs:25:24 | LL | () if true && (let 0 = 1) => {} | ^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:30:16 + --> $DIR/feature-gate.rs:28:16 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:30:16 + --> $DIR/feature-gate.rs:28:16 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:30:31 + --> $DIR/feature-gate.rs:28:31 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:30:31 + --> $DIR/feature-gate.rs:28:31 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:34:42 + --> $DIR/feature-gate.rs:32: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: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:34:42 + --> $DIR/feature-gate.rs:32:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:34:55 + --> $DIR/feature-gate.rs:32: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: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:34:42 + --> $DIR/feature-gate.rs:32:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:34:68 + --> $DIR/feature-gate.rs:32: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: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/feature-gate.rs:34:42 + --> $DIR/feature-gate.rs:32:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:60:16 + --> $DIR/feature-gate.rs:55:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^ @@ -124,15 +124,33 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:62:16 + --> $DIR/feature-gate.rs:55:16 + | +LL | use_expr!((let 0 = 1 && 0 == 0)); + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: expected expression, found `let` statement + --> $DIR/feature-gate.rs:58:16 + | +LL | use_expr!((let 0 = 1)); + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error: expected expression, found `let` statement + --> $DIR/feature-gate.rs:58:16 | LL | use_expr!((let 0 = 1)); | ^^^ | = note: only supported directly in conditions of `if` and `while` expressions + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: no rules expected keyword `let` - --> $DIR/feature-gate.rs:70:15 + --> $DIR/feature-gate.rs:67:15 | LL | macro_rules! use_expr { | --------------------- when calling this macro @@ -141,7 +159,7 @@ LL | use_expr!(let 0 = 1); | ^^^ no rules expected this token in macro call | note: while trying to match meta-variable `$e:expr` - --> $DIR/feature-gate.rs:53:10 + --> $DIR/feature-gate.rs:48:10 | LL | ($e:expr) => { | ^^^^^^^ @@ -169,7 +187,7 @@ LL | () if true && let 0 = 1 => {} = help: you can write `if matches!(, )` instead of `if let = ` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:20:12 + --> $DIR/feature-gate.rs:19:12 | LL | () if let 0 = 1 && true => {} | ^^^^^^^^^^^^^^^^^^^^ @@ -180,7 +198,7 @@ LL | () if let 0 = 1 && true => {} = help: you can write `if matches!(, )` instead of `if let = ` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:34:12 + --> $DIR/feature-gate.rs:32:12 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -191,7 +209,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: you can write `if matches!(, )` instead of `if let = ` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:43:12 + --> $DIR/feature-gate.rs:39:12 | LL | () if let Range { start: _, end: _ } = (true..true) && false => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -202,7 +220,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {} = help: you can write `if matches!(, )` instead of `if let = ` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:66:12 + --> $DIR/feature-gate.rs:63:12 | LL | () if let 0 = 1 => {} | ^^^^^^^^^^^^ @@ -212,56 +230,6 @@ LL | () if let 0 = 1 => {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: you can write `if matches!(, )` instead of `if let = ` -error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:16: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 - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:20: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 - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:34: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 - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:34: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 - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:43: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 - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 23 previous errors +error: aborting due to 20 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.rs index 423a2cd53fc5c..e1138835006b9 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.rs +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.rs @@ -1,7 +1,6 @@ // Expression macros can't expand to a let match guard. #![feature(if_let_guard)] -#![feature(let_chains)] macro_rules! m { ($e:expr) => { let Some(x) = $e } diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr index b8065b9f55607..921cd083fffd7 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.stderr @@ -1,5 +1,5 @@ error: expected expression, found `let` statement - --> $DIR/macro-expanded.rs:7:20 + --> $DIR/macro-expanded.rs:6:20 | LL | ($e:expr) => { let Some(x) = $e } | ^^^ diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs index 47653efffb7b8..29f529a30c2ff 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs @@ -1,5 +1,5 @@ +//@ edition: 2024 #![feature(if_let_guard)] -#![feature(let_chains)] #![allow(irrefutable_let_patterns)] fn same_pattern(c: bool) { diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs index f12824db9c004..a4cb73c650835 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs @@ -1,9 +1,9 @@ +//@ edition: 2024 // Parenthesised let "expressions" are not allowed in guards #![feature(if_let_guard)] -#![feature(let_chains)] -#[cfg(FALSE)] +#[cfg(false)] fn un_cfged() { match () { () if let 0 = 1 => {} diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/shadowing.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/shadowing.rs index 32a223c915951..e7946a2a23b11 100644 --- a/tests/ui/rfcs/rfc-2294-if-let-guard/shadowing.rs +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/shadowing.rs @@ -1,8 +1,8 @@ // Check shadowing in if let guards works as expected. //@ check-pass +//@ edition: 2024 #![feature(if_let_guard)] -#![feature(let_chains)] fn main() { let x: Option> = Some(Some(6)); diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/temporary-early-drop.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/temporary-early-drop.rs new file mode 100644 index 0000000000000..9edbc3243c716 --- /dev/null +++ b/tests/ui/rfcs/rfc-2294-if-let-guard/temporary-early-drop.rs @@ -0,0 +1,29 @@ +// issue-103476 +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 +//@ check-pass + +#![feature(if_let_guard)] +#![allow(irrefutable_let_patterns)] + +struct Pd; + +impl Pd { + fn it(&self) -> It { + todo!() + } +} + +pub struct It<'a>(Box>); + +trait Tr<'a> {} + +fn f(m: Option) { + match () { + () if let Some(n) = m && let it = n.it() => {} + _ => {} + } +} + +fn main() {} diff --git a/tests/ui/rfcs/rfc-2457-non-ascii-idents/extern_block_nonascii_forbidden.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/extern_block_nonascii_forbidden.rs index ad6825404306a..1d064ec54031b 100644 --- a/tests/ui/rfcs/rfc-2457-non-ascii-idents/extern_block_nonascii_forbidden.rs +++ b/tests/ui/rfcs/rfc-2457-non-ascii-idents/extern_block_nonascii_forbidden.rs @@ -1,9 +1,9 @@ #![feature(extern_types)] extern "C" { - type 一; //~ items in `extern` blocks cannot use non-ascii identifiers - fn 二(); //~ items in `extern` blocks cannot use non-ascii identifiers - static 三: usize; //~ items in `extern` blocks cannot use non-ascii identifiers + type 一; //~ ERROR items in `extern` blocks cannot use non-ascii identifiers + fn 二(); //~ ERROR items in `extern` blocks cannot use non-ascii identifiers + static 三: usize; //~ ERROR items in `extern` blocks cannot use non-ascii identifiers } fn main() {} diff --git a/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.rs index e949e2319c1c4..90a7347f6a102 100644 --- a/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.rs +++ b/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.rs @@ -1,4 +1,4 @@ -mod řųśť; //~ trying to load file for -//~^ file not found for +mod řųśť; //~ ERROR trying to load file for +//~^ ERROR file not found for fn main() {} diff --git a/tests/ui/rfcs/rfc-2457-non-ascii-idents/no_mangle_nonascii_forbidden.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/no_mangle_nonascii_forbidden.rs index f4c126a6e025b..dc36c7539e92c 100644 --- a/tests/ui/rfcs/rfc-2457-non-ascii-idents/no_mangle_nonascii_forbidden.rs +++ b/tests/ui/rfcs/rfc-2457-non-ascii-idents/no_mangle_nonascii_forbidden.rs @@ -1,11 +1,11 @@ #[no_mangle] -pub fn řųśť() {} //~ `#[no_mangle]` requires ASCII identifier +pub fn řųśť() {} //~ ERROR `#[no_mangle]` requires ASCII identifier pub struct Foo; impl Foo { #[no_mangle] - pub fn řųśť() {} //~ `#[no_mangle]` requires ASCII identifier + pub fn řųśť() {} //~ ERROR `#[no_mangle]` requires ASCII identifier } trait Bar { @@ -14,7 +14,7 @@ trait Bar { impl Bar for Foo { #[no_mangle] - fn řųśť() {} //~ `#[no_mangle]` requires ASCII identifier + fn řųśť() {} //~ ERROR `#[no_mangle]` requires ASCII identifier } fn main() {} diff --git a/tests/ui/rfcs/rfc-2457-non-ascii-idents/utf8_idents.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/utf8_idents.rs new file mode 100644 index 0000000000000..3997e27bc9f99 --- /dev/null +++ b/tests/ui/rfcs/rfc-2457-non-ascii-idents/utf8_idents.rs @@ -0,0 +1,15 @@ +//! Check that non-ascii-idents are allowed. + +//@ check-pass +// +#![allow(mixed_script_confusables, non_camel_case_types)] + +fn foo<'β, γ>() {} + +struct X { + δ: usize, +} + +pub fn main() { + let α = 0.00001f64; +} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs index e1edde6de19c9..dc052c38fe7ad 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs @@ -1,6 +1,6 @@ //@ run-pass +//@ edition: 2024 -#![feature(let_chains)] #![allow(irrefutable_let_patterns)] fn main() { diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.rs index 8d78264633364..16165c4a42cce 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.rs @@ -1,5 +1,6 @@ //@ check-pass //@ compile-flags: -Z unpretty=expanded +//@ edition: 2015 fn main() { if let 0 = 1 {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout index 1c103f03c3547..e2e45ae94ea6e 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout @@ -6,5 +6,6 @@ use ::std::prelude::rust_2015::*; extern crate std; //@ check-pass //@ compile-flags: -Z unpretty=expanded +//@ edition: 2015 fn main() { if let 0 = 1 {} } diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs index cb3be59bee019..ed461af66aaf9 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs @@ -1,9 +1,9 @@ -#![feature(let_chains)] +//@ edition: 2024 fn let_or_guard(x: Result, ()>) { match x { Ok(opt) if let Some(4) = opt || false => {} - //~^ ERROR expected expression, found `let` statement + //~^ ERROR `||` operators are not supported in let chain conditions _ => {} } } diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr index 4b85fdd505057..0566d96fddc31 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr @@ -1,11 +1,4 @@ -error: expected expression, found `let` statement - --> $DIR/ast-validate-guards.rs:5:20 - | -LL | Ok(opt) if let Some(4) = opt || false => {} - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/ast-validate-guards.rs:5:38 | LL | Ok(opt) if let Some(4) = opt || false => {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/auxiliary/macro-in-2021.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/auxiliary/macro-in-2021.rs new file mode 100644 index 0000000000000..d2d5ae1110415 --- /dev/null +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/auxiliary/macro-in-2021.rs @@ -0,0 +1,26 @@ +//@ edition: 2021 + +#[macro_export] +macro_rules! make_if { + (($($tt:tt)*) { $body:expr } { $else:expr }) => {{ + if $($tt)* { + $body + } else { + $else + } + }}; + (let ($expr:expr) { $body:expr } { $else:expr }) => {{ + if let None = $expr { + $body + } else { + $else + } + }}; + (let ($expr:expr) let ($expr2:expr) { $body:expr } { $else:expr }) => {{ + if let None = $expr && let None = $expr2 { + $body + } else { + $else + } + }}; +} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/auxiliary/macro-in-2024.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/auxiliary/macro-in-2024.rs new file mode 100644 index 0000000000000..f5b6f35abf8fd --- /dev/null +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/auxiliary/macro-in-2024.rs @@ -0,0 +1,26 @@ +//@ edition: 2024 + +#[macro_export] +macro_rules! make_if { + (($($tt:tt)*) { $body:expr } { $else:expr }) => {{ + if $($tt)* { + $body + } else { + $else + } + }}; + (let ($expr:expr) { $body:expr } { $else:expr }) => {{ + if let None = $expr { + $body + } else { + $else + } + }}; + (let ($expr:expr) let ($expr2:expr) { $body:expr } { $else:expr }) => {{ + if let None = $expr && let None = $expr2 { + $body + } else { + $else + } + }}; +} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr index db32b8c1de4ff..141a6d255d086 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr @@ -233,7 +233,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:99:9 + --> $DIR/disallowed-positions.rs:103:9 | LL | if &let 0 = 0 {} | ^^^^^^^^^ @@ -241,7 +241,7 @@ LL | if &let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:102:9 + --> $DIR/disallowed-positions.rs:106:9 | LL | if !let 0 = 0 {} | ^^^^^^^^^ @@ -249,7 +249,7 @@ LL | if !let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:104:9 + --> $DIR/disallowed-positions.rs:108:9 | LL | if *let 0 = 0 {} | ^^^^^^^^^ @@ -257,7 +257,7 @@ LL | if *let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:106:9 + --> $DIR/disallowed-positions.rs:110:9 | LL | if -let 0 = 0 {} | ^^^^^^^^^ @@ -265,28 +265,21 @@ LL | if -let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:114:9 + --> $DIR/disallowed-positions.rs:118:9 | LL | if (let 0 = 0)? {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:117:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:117:13 +error: `||` operators are not supported in let chain conditions + --> $DIR/disallowed-positions.rs:121:13 | LL | if true || let 0 = 0 {} | ^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:119:17 + --> $DIR/disallowed-positions.rs:123:17 | LL | if (true || let 0 = 0) {} | ^^^^^^^^^ @@ -294,7 +287,7 @@ LL | if (true || let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:121:25 + --> $DIR/disallowed-positions.rs:125:25 | LL | if true && (true || let 0 = 0) {} | ^^^^^^^^^ @@ -302,7 +295,7 @@ LL | if true && (true || let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:123:25 + --> $DIR/disallowed-positions.rs:127:25 | LL | if true || (true && let 0 = 0) {} | ^^^^^^^^^ @@ -310,7 +303,7 @@ LL | if true || (true && let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:127:12 + --> $DIR/disallowed-positions.rs:131:12 | LL | if x = let 0 = 0 {} | ^^^ @@ -318,7 +311,7 @@ LL | if x = let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:130:15 + --> $DIR/disallowed-positions.rs:134:15 | LL | if true..(let 0 = 0) {} | ^^^^^^^^^ @@ -326,7 +319,7 @@ LL | if true..(let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:133:11 + --> $DIR/disallowed-positions.rs:137:11 | LL | if ..(let 0 = 0) {} | ^^^^^^^^^ @@ -334,7 +327,7 @@ LL | if ..(let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:135:9 + --> $DIR/disallowed-positions.rs:139:9 | LL | if (let 0 = 0).. {} | ^^^^^^^^^ @@ -342,7 +335,7 @@ LL | if (let 0 = 0).. {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:139:8 + --> $DIR/disallowed-positions.rs:143:8 | LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -350,7 +343,7 @@ LL | if let Range { start: _, end: _ } = true..true && false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:142:8 + --> $DIR/disallowed-positions.rs:146:8 | LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -358,7 +351,7 @@ LL | if let Range { start: _, end: _ } = true..true || false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:148:8 + --> $DIR/disallowed-positions.rs:152:8 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -366,7 +359,7 @@ LL | if let Range { start: F, end } = F..|| true {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:154:8 + --> $DIR/disallowed-positions.rs:158:8 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -374,7 +367,7 @@ LL | if let Range { start: true, end } = t..&&false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:158:19 + --> $DIR/disallowed-positions.rs:162:19 | LL | if let true = let true = true {} | ^^^ @@ -382,7 +375,7 @@ LL | if let true = let true = true {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:161:15 + --> $DIR/disallowed-positions.rs:165:15 | LL | if return let 0 = 0 {} | ^^^ @@ -390,7 +383,7 @@ LL | if return let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:164:21 + --> $DIR/disallowed-positions.rs:168:21 | LL | loop { if break let 0 = 0 {} } | ^^^ @@ -398,7 +391,7 @@ LL | loop { if break let 0 = 0 {} } = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:167:15 + --> $DIR/disallowed-positions.rs:171:15 | LL | if (match let 0 = 0 { _ => { false } }) {} | ^^^ @@ -406,7 +399,7 @@ LL | if (match let 0 = 0 { _ => { false } }) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:170:9 + --> $DIR/disallowed-positions.rs:174:9 | LL | if (let 0 = 0, false).1 {} | ^^^^^^^^^ @@ -414,7 +407,7 @@ LL | if (let 0 = 0, false).1 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:173:9 + --> $DIR/disallowed-positions.rs:177:9 | LL | if (let 0 = 0,) {} | ^^^^^^^^^ @@ -422,7 +415,7 @@ LL | if (let 0 = 0,) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:177:13 + --> $DIR/disallowed-positions.rs:181:13 | LL | if (let 0 = 0).await {} | ^^^^^^^^^ @@ -430,7 +423,7 @@ LL | if (let 0 = 0).await {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:181:12 + --> $DIR/disallowed-positions.rs:185:12 | LL | if (|| let 0 = 0) {} | ^^^ @@ -438,7 +431,7 @@ LL | if (|| let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:184:9 + --> $DIR/disallowed-positions.rs:188:9 | LL | if (let 0 = 0)() {} | ^^^^^^^^^ @@ -446,7 +439,7 @@ LL | if (let 0 = 0)() {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:190:12 + --> $DIR/disallowed-positions.rs:194:12 | LL | while &let 0 = 0 {} | ^^^^^^^^^ @@ -454,7 +447,7 @@ LL | while &let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:193:12 + --> $DIR/disallowed-positions.rs:197:12 | LL | while !let 0 = 0 {} | ^^^^^^^^^ @@ -462,7 +455,7 @@ LL | while !let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:195:12 + --> $DIR/disallowed-positions.rs:199:12 | LL | while *let 0 = 0 {} | ^^^^^^^^^ @@ -470,7 +463,7 @@ LL | while *let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:197:12 + --> $DIR/disallowed-positions.rs:201:12 | LL | while -let 0 = 0 {} | ^^^^^^^^^ @@ -478,28 +471,21 @@ LL | while -let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:205:12 + --> $DIR/disallowed-positions.rs:209:12 | LL | while (let 0 = 0)? {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:208:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:208:16 +error: `||` operators are not supported in let chain conditions + --> $DIR/disallowed-positions.rs:212:16 | LL | while true || let 0 = 0 {} | ^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:210:20 + --> $DIR/disallowed-positions.rs:214:20 | LL | while (true || let 0 = 0) {} | ^^^^^^^^^ @@ -507,7 +493,7 @@ LL | while (true || let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:212:28 + --> $DIR/disallowed-positions.rs:216:28 | LL | while true && (true || let 0 = 0) {} | ^^^^^^^^^ @@ -515,7 +501,7 @@ LL | while true && (true || let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:214:28 + --> $DIR/disallowed-positions.rs:218:28 | LL | while true || (true && let 0 = 0) {} | ^^^^^^^^^ @@ -523,7 +509,7 @@ LL | while true || (true && let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:218:15 + --> $DIR/disallowed-positions.rs:222:15 | LL | while x = let 0 = 0 {} | ^^^ @@ -531,7 +517,7 @@ LL | while x = let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:221:18 + --> $DIR/disallowed-positions.rs:225:18 | LL | while true..(let 0 = 0) {} | ^^^^^^^^^ @@ -539,7 +525,7 @@ LL | while true..(let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:224:14 + --> $DIR/disallowed-positions.rs:228:14 | LL | while ..(let 0 = 0) {} | ^^^^^^^^^ @@ -547,7 +533,7 @@ LL | while ..(let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:226:12 + --> $DIR/disallowed-positions.rs:230:12 | LL | while (let 0 = 0).. {} | ^^^^^^^^^ @@ -555,7 +541,7 @@ LL | while (let 0 = 0).. {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:230:11 + --> $DIR/disallowed-positions.rs:234:11 | LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -563,7 +549,7 @@ LL | while let Range { start: _, end: _ } = true..true && false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:233:11 + --> $DIR/disallowed-positions.rs:237:11 | LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -571,7 +557,7 @@ LL | while let Range { start: _, end: _ } = true..true || false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:239:11 + --> $DIR/disallowed-positions.rs:243:11 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -579,7 +565,7 @@ LL | while let Range { start: F, end } = F..|| true {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:245:11 + --> $DIR/disallowed-positions.rs:249:11 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -587,7 +573,7 @@ LL | while let Range { start: true, end } = t..&&false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:249:22 + --> $DIR/disallowed-positions.rs:253:22 | LL | while let true = let true = true {} | ^^^ @@ -595,7 +581,7 @@ LL | while let true = let true = true {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:252:18 + --> $DIR/disallowed-positions.rs:256:18 | LL | while return let 0 = 0 {} | ^^^ @@ -603,7 +589,7 @@ LL | while return let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:255:39 + --> $DIR/disallowed-positions.rs:259:39 | LL | 'outer: loop { while break 'outer let 0 = 0 {} } | ^^^ @@ -611,7 +597,7 @@ LL | 'outer: loop { while break 'outer let 0 = 0 {} } = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:258:18 + --> $DIR/disallowed-positions.rs:262:18 | LL | while (match let 0 = 0 { _ => { false } }) {} | ^^^ @@ -619,7 +605,7 @@ LL | while (match let 0 = 0 { _ => { false } }) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:261:12 + --> $DIR/disallowed-positions.rs:265:12 | LL | while (let 0 = 0, false).1 {} | ^^^^^^^^^ @@ -627,7 +613,7 @@ LL | while (let 0 = 0, false).1 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:264:12 + --> $DIR/disallowed-positions.rs:268:12 | LL | while (let 0 = 0,) {} | ^^^^^^^^^ @@ -635,7 +621,7 @@ LL | while (let 0 = 0,) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:268:16 + --> $DIR/disallowed-positions.rs:272:16 | LL | while (let 0 = 0).await {} | ^^^^^^^^^ @@ -643,7 +629,7 @@ LL | while (let 0 = 0).await {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:272:15 + --> $DIR/disallowed-positions.rs:276:15 | LL | while (|| let 0 = 0) {} | ^^^ @@ -651,7 +637,7 @@ LL | while (|| let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:275:12 + --> $DIR/disallowed-positions.rs:279:12 | LL | while (let 0 = 0)() {} | ^^^^^^^^^ @@ -659,7 +645,7 @@ LL | while (let 0 = 0)() {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:292:6 + --> $DIR/disallowed-positions.rs:296:6 | LL | &let 0 = 0; | ^^^ @@ -667,7 +653,7 @@ LL | &let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:295:6 + --> $DIR/disallowed-positions.rs:299:6 | LL | !let 0 = 0; | ^^^ @@ -675,7 +661,7 @@ LL | !let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:297:6 + --> $DIR/disallowed-positions.rs:301:6 | LL | *let 0 = 0; | ^^^ @@ -683,7 +669,7 @@ LL | *let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:299:6 + --> $DIR/disallowed-positions.rs:303:6 | LL | -let 0 = 0; | ^^^ @@ -691,7 +677,7 @@ LL | -let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:301:13 + --> $DIR/disallowed-positions.rs:305:13 | LL | let _ = let _ = 3; | ^^^ @@ -699,7 +685,7 @@ LL | let _ = let _ = 3; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:309:6 + --> $DIR/disallowed-positions.rs:313:6 | LL | (let 0 = 0)?; | ^^^ @@ -707,7 +693,7 @@ LL | (let 0 = 0)?; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:312:13 + --> $DIR/disallowed-positions.rs:316:13 | LL | true || let 0 = 0; | ^^^ @@ -715,7 +701,7 @@ LL | true || let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:314:14 + --> $DIR/disallowed-positions.rs:318:14 | LL | (true || let 0 = 0); | ^^^ @@ -723,7 +709,7 @@ LL | (true || let 0 = 0); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:316:22 + --> $DIR/disallowed-positions.rs:320:22 | LL | true && (true || let 0 = 0); | ^^^ @@ -731,7 +717,7 @@ LL | true && (true || let 0 = 0); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:320:9 + --> $DIR/disallowed-positions.rs:324:9 | LL | x = let 0 = 0; | ^^^ @@ -739,7 +725,7 @@ LL | x = let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:323:12 + --> $DIR/disallowed-positions.rs:327:12 | LL | true..(let 0 = 0); | ^^^ @@ -747,7 +733,7 @@ LL | true..(let 0 = 0); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:325:8 + --> $DIR/disallowed-positions.rs:329:8 | LL | ..(let 0 = 0); | ^^^ @@ -755,7 +741,7 @@ LL | ..(let 0 = 0); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:327:6 + --> $DIR/disallowed-positions.rs:331:6 | LL | (let 0 = 0)..; | ^^^ @@ -763,7 +749,7 @@ LL | (let 0 = 0)..; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:330:6 + --> $DIR/disallowed-positions.rs:334:6 | LL | (let Range { start: _, end: _ } = true..true || false); | ^^^ @@ -771,7 +757,7 @@ LL | (let Range { start: _, end: _ } = true..true || false); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:334:6 + --> $DIR/disallowed-positions.rs:338:6 | LL | (let true = let true = true); | ^^^ @@ -779,7 +765,7 @@ LL | (let true = let true = true); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:334:17 + --> $DIR/disallowed-positions.rs:338:17 | LL | (let true = let true = true); | ^^^ @@ -787,7 +773,7 @@ LL | (let true = let true = true); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:340:25 + --> $DIR/disallowed-positions.rs:344:25 | LL | let x = true && let y = 1; | ^^^ @@ -795,7 +781,7 @@ LL | let x = true && let y = 1; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:346:19 + --> $DIR/disallowed-positions.rs:350:19 | LL | [1, 2, 3][let _ = ()] | ^^^ @@ -803,7 +789,7 @@ LL | [1, 2, 3][let _ = ()] = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:351:6 + --> $DIR/disallowed-positions.rs:355:6 | LL | &let 0 = 0 | ^^^ @@ -811,7 +797,7 @@ LL | &let 0 = 0 = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:362:17 + --> $DIR/disallowed-positions.rs:366:17 | LL | true && let 1 = 1 | ^^^ @@ -819,7 +805,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:367:17 + --> $DIR/disallowed-positions.rs:371:17 | LL | true && let 1 = 1 | ^^^ @@ -827,7 +813,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:372:17 + --> $DIR/disallowed-positions.rs:376:17 | LL | true && let 1 = 1 | ^^^ @@ -835,7 +821,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:383:17 + --> $DIR/disallowed-positions.rs:387:17 | LL | true && let 1 = 1 | ^^^ @@ -843,7 +829,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/disallowed-positions.rs:383:9 + --> $DIR/disallowed-positions.rs:387:9 | LL | true && let 1 = 1 | ^^^^^^^^^^^^^^^^^ @@ -854,124 +840,124 @@ LL | { true && let 1 = 1 } | + + error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:393:9 + --> $DIR/disallowed-positions.rs:397:9 | LL | if (let Some(a) = opt && true) { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:393:9 + --> $DIR/disallowed-positions.rs:397:9 | LL | if (let Some(a) = opt && true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:397:9 + --> $DIR/disallowed-positions.rs:401:9 | LL | if (let Some(a) = opt) && true { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:397:9 + --> $DIR/disallowed-positions.rs:401:9 | LL | if (let Some(a) = opt) && true { | ^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:400:9 + --> $DIR/disallowed-positions.rs:404:9 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:400:9 + --> $DIR/disallowed-positions.rs:404:9 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:400:32 + --> $DIR/disallowed-positions.rs:404:32 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:400:32 + --> $DIR/disallowed-positions.rs:404:32 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:408:9 + --> $DIR/disallowed-positions.rs:412:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:408:9 + --> $DIR/disallowed-positions.rs:412:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:408:31 + --> $DIR/disallowed-positions.rs:412:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:408:31 + --> $DIR/disallowed-positions.rs:412:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:412:9 + --> $DIR/disallowed-positions.rs:416:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:412:9 + --> $DIR/disallowed-positions.rs:416:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:412:31 + --> $DIR/disallowed-positions.rs:416:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:412:31 + --> $DIR/disallowed-positions.rs:416:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:416:9 + --> $DIR/disallowed-positions.rs:420:9 | LL | if (let Some(a) = opt && (true)) && true { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:416:9 + --> $DIR/disallowed-positions.rs:420:9 | LL | if (let Some(a) = opt && (true)) && true { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:436:22 + --> $DIR/disallowed-positions.rs:440:22 | LL | let x = (true && let y = 1); | ^^^ @@ -979,7 +965,7 @@ LL | let x = (true && let y = 1); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:441:20 + --> $DIR/disallowed-positions.rs:445:20 | LL | ([1, 2, 3][let _ = ()]) | ^^^ @@ -995,15 +981,51 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:93:16 + --> $DIR/disallowed-positions.rs:91:16 + | +LL | use_expr!((let 0 = 1 && 0 == 0)); + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:91:16 + | +LL | use_expr!((let 0 = 1 && 0 == 0)); + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:95:16 + | +LL | use_expr!((let 0 = 1)); + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:95:16 + | +LL | use_expr!((let 0 = 1)); + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:95:16 | LL | use_expr!((let 0 = 1)); | ^^^ | = note: only supported directly in conditions of `if` and `while` expressions + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:130:8 + --> $DIR/disallowed-positions.rs:134:8 | LL | if true..(let 0 = 0) {} | ^^^^^^^^^^^^^^^^^ expected `bool`, found `Range` @@ -1012,7 +1034,7 @@ LL | if true..(let 0 = 0) {} found struct `std::ops::Range` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:139:12 + --> $DIR/disallowed-positions.rs:143:12 | LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1023,7 +1045,7 @@ LL | if let Range { start: _, end: _ } = true..true && false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:142:12 + --> $DIR/disallowed-positions.rs:146:12 | LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1034,7 +1056,7 @@ LL | if let Range { start: _, end: _ } = true..true || false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:148:12 + --> $DIR/disallowed-positions.rs:152:12 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool` @@ -1045,7 +1067,7 @@ LL | if let Range { start: F, end } = F..|| true {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:154:12 + --> $DIR/disallowed-positions.rs:158:12 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool` @@ -1056,7 +1078,7 @@ LL | if let Range { start: true, end } = t..&&false {} found struct `std::ops::Range<_>` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:110:20 + --> $DIR/disallowed-positions.rs:114:20 | LL | if let 0 = 0? {} | ^^ the `?` operator cannot be applied to type `{integer}` @@ -1064,7 +1086,7 @@ LL | if let 0 = 0? {} = help: the trait `Try` is not implemented for `{integer}` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:221:11 + --> $DIR/disallowed-positions.rs:225:11 | LL | while true..(let 0 = 0) {} | ^^^^^^^^^^^^^^^^^ expected `bool`, found `Range` @@ -1073,7 +1095,7 @@ LL | while true..(let 0 = 0) {} found struct `std::ops::Range` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:230:15 + --> $DIR/disallowed-positions.rs:234:15 | LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1084,7 +1106,7 @@ LL | while let Range { start: _, end: _ } = true..true && false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:233:15 + --> $DIR/disallowed-positions.rs:237:15 | LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1095,7 +1117,7 @@ LL | while let Range { start: _, end: _ } = true..true || false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:239:15 + --> $DIR/disallowed-positions.rs:243:15 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool` @@ -1106,7 +1128,7 @@ LL | while let Range { start: F, end } = F..|| true {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:245:15 + --> $DIR/disallowed-positions.rs:249:15 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool` @@ -1117,7 +1139,7 @@ LL | while let Range { start: true, end } = t..&&false {} found struct `std::ops::Range<_>` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:201:23 + --> $DIR/disallowed-positions.rs:205:23 | LL | while let 0 = 0? {} | ^^ the `?` operator cannot be applied to type `{integer}` @@ -1125,7 +1147,7 @@ LL | while let 0 = 0? {} = help: the trait `Try` is not implemented for `{integer}` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:330:10 + --> $DIR/disallowed-positions.rs:334:10 | LL | (let Range { start: _, end: _ } = true..true || false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1136,14 +1158,14 @@ LL | (let Range { start: _, end: _ } = true..true || false); found struct `std::ops::Range<_>` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:305:17 + --> $DIR/disallowed-positions.rs:309:17 | LL | let 0 = 0?; | ^^ the `?` operator cannot be applied to type `{integer}` | = help: the trait `Try` is not implemented for `{integer}` -error: aborting due to 121 previous errors +error: aborting due to 125 previous errors Some errors have detailed explanations: E0277, E0308. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr index ad16a0f8ed812..dda09de4c5344 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr @@ -233,7 +233,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:99:9 + --> $DIR/disallowed-positions.rs:103:9 | LL | if &let 0 = 0 {} | ^^^^^^^^^ @@ -241,7 +241,7 @@ LL | if &let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:102:9 + --> $DIR/disallowed-positions.rs:106:9 | LL | if !let 0 = 0 {} | ^^^^^^^^^ @@ -249,7 +249,7 @@ LL | if !let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:104:9 + --> $DIR/disallowed-positions.rs:108:9 | LL | if *let 0 = 0 {} | ^^^^^^^^^ @@ -257,7 +257,7 @@ LL | if *let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:106:9 + --> $DIR/disallowed-positions.rs:110:9 | LL | if -let 0 = 0 {} | ^^^^^^^^^ @@ -265,28 +265,21 @@ LL | if -let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:114:9 + --> $DIR/disallowed-positions.rs:118:9 | LL | if (let 0 = 0)? {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:117:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:117:13 +error: `||` operators are not supported in let chain conditions + --> $DIR/disallowed-positions.rs:121:13 | LL | if true || let 0 = 0 {} | ^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:119:17 + --> $DIR/disallowed-positions.rs:123:17 | LL | if (true || let 0 = 0) {} | ^^^^^^^^^ @@ -294,7 +287,7 @@ LL | if (true || let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:121:25 + --> $DIR/disallowed-positions.rs:125:25 | LL | if true && (true || let 0 = 0) {} | ^^^^^^^^^ @@ -302,7 +295,7 @@ LL | if true && (true || let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:123:25 + --> $DIR/disallowed-positions.rs:127:25 | LL | if true || (true && let 0 = 0) {} | ^^^^^^^^^ @@ -310,7 +303,7 @@ LL | if true || (true && let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:127:12 + --> $DIR/disallowed-positions.rs:131:12 | LL | if x = let 0 = 0 {} | ^^^ @@ -318,7 +311,7 @@ LL | if x = let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:130:15 + --> $DIR/disallowed-positions.rs:134:15 | LL | if true..(let 0 = 0) {} | ^^^^^^^^^ @@ -326,7 +319,7 @@ LL | if true..(let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:133:11 + --> $DIR/disallowed-positions.rs:137:11 | LL | if ..(let 0 = 0) {} | ^^^^^^^^^ @@ -334,7 +327,7 @@ LL | if ..(let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:135:9 + --> $DIR/disallowed-positions.rs:139:9 | LL | if (let 0 = 0).. {} | ^^^^^^^^^ @@ -342,7 +335,7 @@ LL | if (let 0 = 0).. {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:139:8 + --> $DIR/disallowed-positions.rs:143:8 | LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -350,7 +343,7 @@ LL | if let Range { start: _, end: _ } = true..true && false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:142:8 + --> $DIR/disallowed-positions.rs:146:8 | LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -358,7 +351,7 @@ LL | if let Range { start: _, end: _ } = true..true || false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:148:8 + --> $DIR/disallowed-positions.rs:152:8 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -366,7 +359,7 @@ LL | if let Range { start: F, end } = F..|| true {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:154:8 + --> $DIR/disallowed-positions.rs:158:8 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -374,7 +367,7 @@ LL | if let Range { start: true, end } = t..&&false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:158:19 + --> $DIR/disallowed-positions.rs:162:19 | LL | if let true = let true = true {} | ^^^ @@ -382,7 +375,7 @@ LL | if let true = let true = true {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:161:15 + --> $DIR/disallowed-positions.rs:165:15 | LL | if return let 0 = 0 {} | ^^^ @@ -390,7 +383,7 @@ LL | if return let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:164:21 + --> $DIR/disallowed-positions.rs:168:21 | LL | loop { if break let 0 = 0 {} } | ^^^ @@ -398,7 +391,7 @@ LL | loop { if break let 0 = 0 {} } = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:167:15 + --> $DIR/disallowed-positions.rs:171:15 | LL | if (match let 0 = 0 { _ => { false } }) {} | ^^^ @@ -406,7 +399,7 @@ LL | if (match let 0 = 0 { _ => { false } }) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:170:9 + --> $DIR/disallowed-positions.rs:174:9 | LL | if (let 0 = 0, false).1 {} | ^^^^^^^^^ @@ -414,7 +407,7 @@ LL | if (let 0 = 0, false).1 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:173:9 + --> $DIR/disallowed-positions.rs:177:9 | LL | if (let 0 = 0,) {} | ^^^^^^^^^ @@ -422,7 +415,7 @@ LL | if (let 0 = 0,) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:177:13 + --> $DIR/disallowed-positions.rs:181:13 | LL | if (let 0 = 0).await {} | ^^^^^^^^^ @@ -430,7 +423,7 @@ LL | if (let 0 = 0).await {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:181:12 + --> $DIR/disallowed-positions.rs:185:12 | LL | if (|| let 0 = 0) {} | ^^^ @@ -438,7 +431,7 @@ LL | if (|| let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:184:9 + --> $DIR/disallowed-positions.rs:188:9 | LL | if (let 0 = 0)() {} | ^^^^^^^^^ @@ -446,7 +439,7 @@ LL | if (let 0 = 0)() {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:190:12 + --> $DIR/disallowed-positions.rs:194:12 | LL | while &let 0 = 0 {} | ^^^^^^^^^ @@ -454,7 +447,7 @@ LL | while &let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:193:12 + --> $DIR/disallowed-positions.rs:197:12 | LL | while !let 0 = 0 {} | ^^^^^^^^^ @@ -462,7 +455,7 @@ LL | while !let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:195:12 + --> $DIR/disallowed-positions.rs:199:12 | LL | while *let 0 = 0 {} | ^^^^^^^^^ @@ -470,7 +463,7 @@ LL | while *let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:197:12 + --> $DIR/disallowed-positions.rs:201:12 | LL | while -let 0 = 0 {} | ^^^^^^^^^ @@ -478,28 +471,21 @@ LL | while -let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:205:12 + --> $DIR/disallowed-positions.rs:209:12 | LL | while (let 0 = 0)? {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:208:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:208:16 +error: `||` operators are not supported in let chain conditions + --> $DIR/disallowed-positions.rs:212:16 | LL | while true || let 0 = 0 {} | ^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:210:20 + --> $DIR/disallowed-positions.rs:214:20 | LL | while (true || let 0 = 0) {} | ^^^^^^^^^ @@ -507,7 +493,7 @@ LL | while (true || let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:212:28 + --> $DIR/disallowed-positions.rs:216:28 | LL | while true && (true || let 0 = 0) {} | ^^^^^^^^^ @@ -515,7 +501,7 @@ LL | while true && (true || let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:214:28 + --> $DIR/disallowed-positions.rs:218:28 | LL | while true || (true && let 0 = 0) {} | ^^^^^^^^^ @@ -523,7 +509,7 @@ LL | while true || (true && let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:218:15 + --> $DIR/disallowed-positions.rs:222:15 | LL | while x = let 0 = 0 {} | ^^^ @@ -531,7 +517,7 @@ LL | while x = let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:221:18 + --> $DIR/disallowed-positions.rs:225:18 | LL | while true..(let 0 = 0) {} | ^^^^^^^^^ @@ -539,7 +525,7 @@ LL | while true..(let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:224:14 + --> $DIR/disallowed-positions.rs:228:14 | LL | while ..(let 0 = 0) {} | ^^^^^^^^^ @@ -547,7 +533,7 @@ LL | while ..(let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:226:12 + --> $DIR/disallowed-positions.rs:230:12 | LL | while (let 0 = 0).. {} | ^^^^^^^^^ @@ -555,7 +541,7 @@ LL | while (let 0 = 0).. {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:230:11 + --> $DIR/disallowed-positions.rs:234:11 | LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -563,7 +549,7 @@ LL | while let Range { start: _, end: _ } = true..true && false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:233:11 + --> $DIR/disallowed-positions.rs:237:11 | LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -571,7 +557,7 @@ LL | while let Range { start: _, end: _ } = true..true || false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:239:11 + --> $DIR/disallowed-positions.rs:243:11 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -579,7 +565,7 @@ LL | while let Range { start: F, end } = F..|| true {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:245:11 + --> $DIR/disallowed-positions.rs:249:11 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -587,7 +573,7 @@ LL | while let Range { start: true, end } = t..&&false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:249:22 + --> $DIR/disallowed-positions.rs:253:22 | LL | while let true = let true = true {} | ^^^ @@ -595,7 +581,7 @@ LL | while let true = let true = true {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:252:18 + --> $DIR/disallowed-positions.rs:256:18 | LL | while return let 0 = 0 {} | ^^^ @@ -603,7 +589,7 @@ LL | while return let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:255:39 + --> $DIR/disallowed-positions.rs:259:39 | LL | 'outer: loop { while break 'outer let 0 = 0 {} } | ^^^ @@ -611,7 +597,7 @@ LL | 'outer: loop { while break 'outer let 0 = 0 {} } = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:258:18 + --> $DIR/disallowed-positions.rs:262:18 | LL | while (match let 0 = 0 { _ => { false } }) {} | ^^^ @@ -619,7 +605,7 @@ LL | while (match let 0 = 0 { _ => { false } }) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:261:12 + --> $DIR/disallowed-positions.rs:265:12 | LL | while (let 0 = 0, false).1 {} | ^^^^^^^^^ @@ -627,7 +613,7 @@ LL | while (let 0 = 0, false).1 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:264:12 + --> $DIR/disallowed-positions.rs:268:12 | LL | while (let 0 = 0,) {} | ^^^^^^^^^ @@ -635,7 +621,7 @@ LL | while (let 0 = 0,) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:268:16 + --> $DIR/disallowed-positions.rs:272:16 | LL | while (let 0 = 0).await {} | ^^^^^^^^^ @@ -643,7 +629,7 @@ LL | while (let 0 = 0).await {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:272:15 + --> $DIR/disallowed-positions.rs:276:15 | LL | while (|| let 0 = 0) {} | ^^^ @@ -651,7 +637,7 @@ LL | while (|| let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:275:12 + --> $DIR/disallowed-positions.rs:279:12 | LL | while (let 0 = 0)() {} | ^^^^^^^^^ @@ -659,7 +645,7 @@ LL | while (let 0 = 0)() {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:292:6 + --> $DIR/disallowed-positions.rs:296:6 | LL | &let 0 = 0; | ^^^ @@ -667,7 +653,7 @@ LL | &let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:295:6 + --> $DIR/disallowed-positions.rs:299:6 | LL | !let 0 = 0; | ^^^ @@ -675,7 +661,7 @@ LL | !let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:297:6 + --> $DIR/disallowed-positions.rs:301:6 | LL | *let 0 = 0; | ^^^ @@ -683,7 +669,7 @@ LL | *let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:299:6 + --> $DIR/disallowed-positions.rs:303:6 | LL | -let 0 = 0; | ^^^ @@ -691,7 +677,7 @@ LL | -let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:301:13 + --> $DIR/disallowed-positions.rs:305:13 | LL | let _ = let _ = 3; | ^^^ @@ -699,7 +685,7 @@ LL | let _ = let _ = 3; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:309:6 + --> $DIR/disallowed-positions.rs:313:6 | LL | (let 0 = 0)?; | ^^^ @@ -707,7 +693,7 @@ LL | (let 0 = 0)?; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:312:13 + --> $DIR/disallowed-positions.rs:316:13 | LL | true || let 0 = 0; | ^^^ @@ -715,7 +701,7 @@ LL | true || let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:314:14 + --> $DIR/disallowed-positions.rs:318:14 | LL | (true || let 0 = 0); | ^^^ @@ -723,7 +709,7 @@ LL | (true || let 0 = 0); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:316:22 + --> $DIR/disallowed-positions.rs:320:22 | LL | true && (true || let 0 = 0); | ^^^ @@ -731,7 +717,7 @@ LL | true && (true || let 0 = 0); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:320:9 + --> $DIR/disallowed-positions.rs:324:9 | LL | x = let 0 = 0; | ^^^ @@ -739,7 +725,7 @@ LL | x = let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:323:12 + --> $DIR/disallowed-positions.rs:327:12 | LL | true..(let 0 = 0); | ^^^ @@ -747,7 +733,7 @@ LL | true..(let 0 = 0); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:325:8 + --> $DIR/disallowed-positions.rs:329:8 | LL | ..(let 0 = 0); | ^^^ @@ -755,7 +741,7 @@ LL | ..(let 0 = 0); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:327:6 + --> $DIR/disallowed-positions.rs:331:6 | LL | (let 0 = 0)..; | ^^^ @@ -763,7 +749,7 @@ LL | (let 0 = 0)..; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:330:6 + --> $DIR/disallowed-positions.rs:334:6 | LL | (let Range { start: _, end: _ } = true..true || false); | ^^^ @@ -771,7 +757,7 @@ LL | (let Range { start: _, end: _ } = true..true || false); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:334:6 + --> $DIR/disallowed-positions.rs:338:6 | LL | (let true = let true = true); | ^^^ @@ -779,7 +765,7 @@ LL | (let true = let true = true); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:334:17 + --> $DIR/disallowed-positions.rs:338:17 | LL | (let true = let true = true); | ^^^ @@ -787,7 +773,7 @@ LL | (let true = let true = true); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:340:25 + --> $DIR/disallowed-positions.rs:344:25 | LL | let x = true && let y = 1; | ^^^ @@ -795,7 +781,7 @@ LL | let x = true && let y = 1; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:346:19 + --> $DIR/disallowed-positions.rs:350:19 | LL | [1, 2, 3][let _ = ()] | ^^^ @@ -803,7 +789,7 @@ LL | [1, 2, 3][let _ = ()] = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:351:6 + --> $DIR/disallowed-positions.rs:355:6 | LL | &let 0 = 0 | ^^^ @@ -811,7 +797,7 @@ LL | &let 0 = 0 = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:362:17 + --> $DIR/disallowed-positions.rs:366:17 | LL | true && let 1 = 1 | ^^^ @@ -819,7 +805,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:367:17 + --> $DIR/disallowed-positions.rs:371:17 | LL | true && let 1 = 1 | ^^^ @@ -827,7 +813,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:372:17 + --> $DIR/disallowed-positions.rs:376:17 | LL | true && let 1 = 1 | ^^^ @@ -835,7 +821,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:383:17 + --> $DIR/disallowed-positions.rs:387:17 | LL | true && let 1 = 1 | ^^^ @@ -843,7 +829,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/disallowed-positions.rs:383:9 + --> $DIR/disallowed-positions.rs:387:9 | LL | true && let 1 = 1 | ^^^^^^^^^^^^^^^^^ @@ -854,124 +840,124 @@ LL | { true && let 1 = 1 } | + + error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:393:9 + --> $DIR/disallowed-positions.rs:397:9 | LL | if (let Some(a) = opt && true) { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:393:9 + --> $DIR/disallowed-positions.rs:397:9 | LL | if (let Some(a) = opt && true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:397:9 + --> $DIR/disallowed-positions.rs:401:9 | LL | if (let Some(a) = opt) && true { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:397:9 + --> $DIR/disallowed-positions.rs:401:9 | LL | if (let Some(a) = opt) && true { | ^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:400:9 + --> $DIR/disallowed-positions.rs:404:9 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:400:9 + --> $DIR/disallowed-positions.rs:404:9 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:400:32 + --> $DIR/disallowed-positions.rs:404:32 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:400:32 + --> $DIR/disallowed-positions.rs:404:32 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:408:9 + --> $DIR/disallowed-positions.rs:412:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:408:9 + --> $DIR/disallowed-positions.rs:412:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:408:31 + --> $DIR/disallowed-positions.rs:412:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:408:31 + --> $DIR/disallowed-positions.rs:412:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:412:9 + --> $DIR/disallowed-positions.rs:416:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:412:9 + --> $DIR/disallowed-positions.rs:416:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:412:31 + --> $DIR/disallowed-positions.rs:416:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:412:31 + --> $DIR/disallowed-positions.rs:416:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:416:9 + --> $DIR/disallowed-positions.rs:420:9 | LL | if (let Some(a) = opt && (true)) && true { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:416:9 + --> $DIR/disallowed-positions.rs:420:9 | LL | if (let Some(a) = opt && (true)) && true { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:436:22 + --> $DIR/disallowed-positions.rs:440:22 | LL | let x = (true && let y = 1); | ^^^ @@ -979,7 +965,7 @@ LL | let x = (true && let y = 1); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:441:20 + --> $DIR/disallowed-positions.rs:445:20 | LL | ([1, 2, 3][let _ = ()]) | ^^^ @@ -995,12 +981,48 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:93:16 + --> $DIR/disallowed-positions.rs:91:16 + | +LL | use_expr!((let 0 = 1 && 0 == 0)); + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:91:16 + | +LL | use_expr!((let 0 = 1 && 0 == 0)); + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:95:16 + | +LL | use_expr!((let 0 = 1)); + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:95:16 + | +LL | use_expr!((let 0 = 1)); + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:95:16 | LL | use_expr!((let 0 = 1)); | ^^^ | = note: only supported directly in conditions of `if` and `while` expressions + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0658]: `let` expressions in this position are unstable --> $DIR/disallowed-positions.rs:49:8 @@ -1043,7 +1065,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:404:8 + --> $DIR/disallowed-positions.rs:408:8 | LL | if let Some(a) = opt && (true && true) { | ^^^^^^^^^^^^^^^^^ @@ -1053,7 +1075,7 @@ LL | if let Some(a) = opt && (true && true) { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:420:28 + --> $DIR/disallowed-positions.rs:424:28 | LL | if (true && (true)) && let Some(a) = opt { | ^^^^^^^^^^^^^^^^^ @@ -1063,7 +1085,7 @@ LL | if (true && (true)) && let Some(a) = opt { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:423:18 + --> $DIR/disallowed-positions.rs:427:18 | LL | if (true) && let Some(a) = opt { | ^^^^^^^^^^^^^^^^^ @@ -1073,7 +1095,7 @@ LL | if (true) && let Some(a) = opt { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:426:16 + --> $DIR/disallowed-positions.rs:430:16 | LL | if true && let Some(a) = opt { | ^^^^^^^^^^^^^^^^^ @@ -1083,7 +1105,7 @@ LL | if true && let Some(a) = opt { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:431:8 + --> $DIR/disallowed-positions.rs:435:8 | LL | if let true = (true && fun()) && (true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1093,7 +1115,7 @@ LL | if let true = (true && fun()) && (true) { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:130:8 + --> $DIR/disallowed-positions.rs:134:8 | LL | if true..(let 0 = 0) {} | ^^^^^^^^^^^^^^^^^ expected `bool`, found `Range` @@ -1102,7 +1124,7 @@ LL | if true..(let 0 = 0) {} found struct `std::ops::Range` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:139:12 + --> $DIR/disallowed-positions.rs:143:12 | LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1113,7 +1135,7 @@ LL | if let Range { start: _, end: _ } = true..true && false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:142:12 + --> $DIR/disallowed-positions.rs:146:12 | LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1124,7 +1146,7 @@ LL | if let Range { start: _, end: _ } = true..true || false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:148:12 + --> $DIR/disallowed-positions.rs:152:12 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool` @@ -1135,7 +1157,7 @@ LL | if let Range { start: F, end } = F..|| true {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:154:12 + --> $DIR/disallowed-positions.rs:158:12 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool` @@ -1146,7 +1168,7 @@ LL | if let Range { start: true, end } = t..&&false {} found struct `std::ops::Range<_>` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:110:20 + --> $DIR/disallowed-positions.rs:114:20 | LL | if let 0 = 0? {} | ^^ the `?` operator cannot be applied to type `{integer}` @@ -1154,7 +1176,7 @@ LL | if let 0 = 0? {} = help: the trait `Try` is not implemented for `{integer}` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:221:11 + --> $DIR/disallowed-positions.rs:225:11 | LL | while true..(let 0 = 0) {} | ^^^^^^^^^^^^^^^^^ expected `bool`, found `Range` @@ -1163,7 +1185,7 @@ LL | while true..(let 0 = 0) {} found struct `std::ops::Range` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:230:15 + --> $DIR/disallowed-positions.rs:234:15 | LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1174,7 +1196,7 @@ LL | while let Range { start: _, end: _ } = true..true && false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:233:15 + --> $DIR/disallowed-positions.rs:237:15 | LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1185,7 +1207,7 @@ LL | while let Range { start: _, end: _ } = true..true || false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:239:15 + --> $DIR/disallowed-positions.rs:243:15 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool` @@ -1196,7 +1218,7 @@ LL | while let Range { start: F, end } = F..|| true {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:245:15 + --> $DIR/disallowed-positions.rs:249:15 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool` @@ -1207,7 +1229,7 @@ LL | while let Range { start: true, end } = t..&&false {} found struct `std::ops::Range<_>` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:201:23 + --> $DIR/disallowed-positions.rs:205:23 | LL | while let 0 = 0? {} | ^^ the `?` operator cannot be applied to type `{integer}` @@ -1215,7 +1237,7 @@ LL | while let 0 = 0? {} = help: the trait `Try` is not implemented for `{integer}` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:330:10 + --> $DIR/disallowed-positions.rs:334:10 | LL | (let Range { start: _, end: _ } = true..true || false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1226,14 +1248,14 @@ LL | (let Range { start: _, end: _ } = true..true || false); found struct `std::ops::Range<_>` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:305:17 + --> $DIR/disallowed-positions.rs:309:17 | LL | let 0 = 0?; | ^^ the `?` operator cannot be applied to type `{integer}` | = help: the trait `Try` is not implemented for `{integer}` -error: aborting due to 130 previous errors +error: aborting due to 134 previous errors Some errors have detailed explanations: E0277, E0308, E0658. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr index 2d5fd1144addd..5b53691cbf546 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr @@ -233,7 +233,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:99:9 + --> $DIR/disallowed-positions.rs:103:9 | LL | if &let 0 = 0 {} | ^^^^^^^^^ @@ -241,7 +241,7 @@ LL | if &let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:102:9 + --> $DIR/disallowed-positions.rs:106:9 | LL | if !let 0 = 0 {} | ^^^^^^^^^ @@ -249,7 +249,7 @@ LL | if !let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:104:9 + --> $DIR/disallowed-positions.rs:108:9 | LL | if *let 0 = 0 {} | ^^^^^^^^^ @@ -257,7 +257,7 @@ LL | if *let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:106:9 + --> $DIR/disallowed-positions.rs:110:9 | LL | if -let 0 = 0 {} | ^^^^^^^^^ @@ -265,28 +265,21 @@ LL | if -let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:114:9 + --> $DIR/disallowed-positions.rs:118:9 | LL | if (let 0 = 0)? {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:117:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:117:13 +error: `||` operators are not supported in let chain conditions + --> $DIR/disallowed-positions.rs:121:13 | LL | if true || let 0 = 0 {} | ^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:119:17 + --> $DIR/disallowed-positions.rs:123:17 | LL | if (true || let 0 = 0) {} | ^^^^^^^^^ @@ -294,7 +287,7 @@ LL | if (true || let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:121:25 + --> $DIR/disallowed-positions.rs:125:25 | LL | if true && (true || let 0 = 0) {} | ^^^^^^^^^ @@ -302,7 +295,7 @@ LL | if true && (true || let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:123:25 + --> $DIR/disallowed-positions.rs:127:25 | LL | if true || (true && let 0 = 0) {} | ^^^^^^^^^ @@ -310,7 +303,7 @@ LL | if true || (true && let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:127:12 + --> $DIR/disallowed-positions.rs:131:12 | LL | if x = let 0 = 0 {} | ^^^ @@ -318,7 +311,7 @@ LL | if x = let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:130:15 + --> $DIR/disallowed-positions.rs:134:15 | LL | if true..(let 0 = 0) {} | ^^^^^^^^^ @@ -326,7 +319,7 @@ LL | if true..(let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:133:11 + --> $DIR/disallowed-positions.rs:137:11 | LL | if ..(let 0 = 0) {} | ^^^^^^^^^ @@ -334,7 +327,7 @@ LL | if ..(let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:135:9 + --> $DIR/disallowed-positions.rs:139:9 | LL | if (let 0 = 0).. {} | ^^^^^^^^^ @@ -342,7 +335,7 @@ LL | if (let 0 = 0).. {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:139:8 + --> $DIR/disallowed-positions.rs:143:8 | LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -350,7 +343,7 @@ LL | if let Range { start: _, end: _ } = true..true && false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:142:8 + --> $DIR/disallowed-positions.rs:146:8 | LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -358,7 +351,7 @@ LL | if let Range { start: _, end: _ } = true..true || false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:148:8 + --> $DIR/disallowed-positions.rs:152:8 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -366,7 +359,7 @@ LL | if let Range { start: F, end } = F..|| true {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:154:8 + --> $DIR/disallowed-positions.rs:158:8 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -374,7 +367,7 @@ LL | if let Range { start: true, end } = t..&&false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:158:19 + --> $DIR/disallowed-positions.rs:162:19 | LL | if let true = let true = true {} | ^^^ @@ -382,7 +375,7 @@ LL | if let true = let true = true {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:161:15 + --> $DIR/disallowed-positions.rs:165:15 | LL | if return let 0 = 0 {} | ^^^ @@ -390,7 +383,7 @@ LL | if return let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:164:21 + --> $DIR/disallowed-positions.rs:168:21 | LL | loop { if break let 0 = 0 {} } | ^^^ @@ -398,7 +391,7 @@ LL | loop { if break let 0 = 0 {} } = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:167:15 + --> $DIR/disallowed-positions.rs:171:15 | LL | if (match let 0 = 0 { _ => { false } }) {} | ^^^ @@ -406,7 +399,7 @@ LL | if (match let 0 = 0 { _ => { false } }) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:170:9 + --> $DIR/disallowed-positions.rs:174:9 | LL | if (let 0 = 0, false).1 {} | ^^^^^^^^^ @@ -414,7 +407,7 @@ LL | if (let 0 = 0, false).1 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:173:9 + --> $DIR/disallowed-positions.rs:177:9 | LL | if (let 0 = 0,) {} | ^^^^^^^^^ @@ -422,7 +415,7 @@ LL | if (let 0 = 0,) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:177:13 + --> $DIR/disallowed-positions.rs:181:13 | LL | if (let 0 = 0).await {} | ^^^^^^^^^ @@ -430,7 +423,7 @@ LL | if (let 0 = 0).await {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:181:12 + --> $DIR/disallowed-positions.rs:185:12 | LL | if (|| let 0 = 0) {} | ^^^ @@ -438,7 +431,7 @@ LL | if (|| let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:184:9 + --> $DIR/disallowed-positions.rs:188:9 | LL | if (let 0 = 0)() {} | ^^^^^^^^^ @@ -446,7 +439,7 @@ LL | if (let 0 = 0)() {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:190:12 + --> $DIR/disallowed-positions.rs:194:12 | LL | while &let 0 = 0 {} | ^^^^^^^^^ @@ -454,7 +447,7 @@ LL | while &let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:193:12 + --> $DIR/disallowed-positions.rs:197:12 | LL | while !let 0 = 0 {} | ^^^^^^^^^ @@ -462,7 +455,7 @@ LL | while !let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:195:12 + --> $DIR/disallowed-positions.rs:199:12 | LL | while *let 0 = 0 {} | ^^^^^^^^^ @@ -470,7 +463,7 @@ LL | while *let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:197:12 + --> $DIR/disallowed-positions.rs:201:12 | LL | while -let 0 = 0 {} | ^^^^^^^^^ @@ -478,28 +471,21 @@ LL | while -let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:205:12 + --> $DIR/disallowed-positions.rs:209:12 | LL | while (let 0 = 0)? {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:208:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:208:16 +error: `||` operators are not supported in let chain conditions + --> $DIR/disallowed-positions.rs:212:16 | LL | while true || let 0 = 0 {} | ^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:210:20 + --> $DIR/disallowed-positions.rs:214:20 | LL | while (true || let 0 = 0) {} | ^^^^^^^^^ @@ -507,7 +493,7 @@ LL | while (true || let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:212:28 + --> $DIR/disallowed-positions.rs:216:28 | LL | while true && (true || let 0 = 0) {} | ^^^^^^^^^ @@ -515,7 +501,7 @@ LL | while true && (true || let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:214:28 + --> $DIR/disallowed-positions.rs:218:28 | LL | while true || (true && let 0 = 0) {} | ^^^^^^^^^ @@ -523,7 +509,7 @@ LL | while true || (true && let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:218:15 + --> $DIR/disallowed-positions.rs:222:15 | LL | while x = let 0 = 0 {} | ^^^ @@ -531,7 +517,7 @@ LL | while x = let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:221:18 + --> $DIR/disallowed-positions.rs:225:18 | LL | while true..(let 0 = 0) {} | ^^^^^^^^^ @@ -539,7 +525,7 @@ LL | while true..(let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:224:14 + --> $DIR/disallowed-positions.rs:228:14 | LL | while ..(let 0 = 0) {} | ^^^^^^^^^ @@ -547,7 +533,7 @@ LL | while ..(let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:226:12 + --> $DIR/disallowed-positions.rs:230:12 | LL | while (let 0 = 0).. {} | ^^^^^^^^^ @@ -555,7 +541,7 @@ LL | while (let 0 = 0).. {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:230:11 + --> $DIR/disallowed-positions.rs:234:11 | LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -563,7 +549,7 @@ LL | while let Range { start: _, end: _ } = true..true && false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:233:11 + --> $DIR/disallowed-positions.rs:237:11 | LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -571,7 +557,7 @@ LL | while let Range { start: _, end: _ } = true..true || false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:239:11 + --> $DIR/disallowed-positions.rs:243:11 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -579,7 +565,7 @@ LL | while let Range { start: F, end } = F..|| true {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:245:11 + --> $DIR/disallowed-positions.rs:249:11 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -587,7 +573,7 @@ LL | while let Range { start: true, end } = t..&&false {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:249:22 + --> $DIR/disallowed-positions.rs:253:22 | LL | while let true = let true = true {} | ^^^ @@ -595,7 +581,7 @@ LL | while let true = let true = true {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:252:18 + --> $DIR/disallowed-positions.rs:256:18 | LL | while return let 0 = 0 {} | ^^^ @@ -603,7 +589,7 @@ LL | while return let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:255:39 + --> $DIR/disallowed-positions.rs:259:39 | LL | 'outer: loop { while break 'outer let 0 = 0 {} } | ^^^ @@ -611,7 +597,7 @@ LL | 'outer: loop { while break 'outer let 0 = 0 {} } = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:258:18 + --> $DIR/disallowed-positions.rs:262:18 | LL | while (match let 0 = 0 { _ => { false } }) {} | ^^^ @@ -619,7 +605,7 @@ LL | while (match let 0 = 0 { _ => { false } }) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:261:12 + --> $DIR/disallowed-positions.rs:265:12 | LL | while (let 0 = 0, false).1 {} | ^^^^^^^^^ @@ -627,7 +613,7 @@ LL | while (let 0 = 0, false).1 {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:264:12 + --> $DIR/disallowed-positions.rs:268:12 | LL | while (let 0 = 0,) {} | ^^^^^^^^^ @@ -635,7 +621,7 @@ LL | while (let 0 = 0,) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:268:16 + --> $DIR/disallowed-positions.rs:272:16 | LL | while (let 0 = 0).await {} | ^^^^^^^^^ @@ -643,7 +629,7 @@ LL | while (let 0 = 0).await {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:272:15 + --> $DIR/disallowed-positions.rs:276:15 | LL | while (|| let 0 = 0) {} | ^^^ @@ -651,7 +637,7 @@ LL | while (|| let 0 = 0) {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:275:12 + --> $DIR/disallowed-positions.rs:279:12 | LL | while (let 0 = 0)() {} | ^^^^^^^^^ @@ -659,7 +645,7 @@ LL | while (let 0 = 0)() {} = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:292:6 + --> $DIR/disallowed-positions.rs:296:6 | LL | &let 0 = 0; | ^^^ @@ -667,7 +653,7 @@ LL | &let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:295:6 + --> $DIR/disallowed-positions.rs:299:6 | LL | !let 0 = 0; | ^^^ @@ -675,7 +661,7 @@ LL | !let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:297:6 + --> $DIR/disallowed-positions.rs:301:6 | LL | *let 0 = 0; | ^^^ @@ -683,7 +669,7 @@ LL | *let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:299:6 + --> $DIR/disallowed-positions.rs:303:6 | LL | -let 0 = 0; | ^^^ @@ -691,7 +677,7 @@ LL | -let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:301:13 + --> $DIR/disallowed-positions.rs:305:13 | LL | let _ = let _ = 3; | ^^^ @@ -699,7 +685,7 @@ LL | let _ = let _ = 3; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:309:6 + --> $DIR/disallowed-positions.rs:313:6 | LL | (let 0 = 0)?; | ^^^ @@ -707,7 +693,7 @@ LL | (let 0 = 0)?; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:312:13 + --> $DIR/disallowed-positions.rs:316:13 | LL | true || let 0 = 0; | ^^^ @@ -715,7 +701,7 @@ LL | true || let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:314:14 + --> $DIR/disallowed-positions.rs:318:14 | LL | (true || let 0 = 0); | ^^^ @@ -723,7 +709,7 @@ LL | (true || let 0 = 0); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:316:22 + --> $DIR/disallowed-positions.rs:320:22 | LL | true && (true || let 0 = 0); | ^^^ @@ -731,7 +717,7 @@ LL | true && (true || let 0 = 0); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:320:9 + --> $DIR/disallowed-positions.rs:324:9 | LL | x = let 0 = 0; | ^^^ @@ -739,7 +725,7 @@ LL | x = let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:323:12 + --> $DIR/disallowed-positions.rs:327:12 | LL | true..(let 0 = 0); | ^^^ @@ -747,7 +733,7 @@ LL | true..(let 0 = 0); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:325:8 + --> $DIR/disallowed-positions.rs:329:8 | LL | ..(let 0 = 0); | ^^^ @@ -755,7 +741,7 @@ LL | ..(let 0 = 0); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:327:6 + --> $DIR/disallowed-positions.rs:331:6 | LL | (let 0 = 0)..; | ^^^ @@ -763,7 +749,7 @@ LL | (let 0 = 0)..; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:330:6 + --> $DIR/disallowed-positions.rs:334:6 | LL | (let Range { start: _, end: _ } = true..true || false); | ^^^ @@ -771,7 +757,7 @@ LL | (let Range { start: _, end: _ } = true..true || false); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:334:6 + --> $DIR/disallowed-positions.rs:338:6 | LL | (let true = let true = true); | ^^^ @@ -779,7 +765,7 @@ LL | (let true = let true = true); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:334:17 + --> $DIR/disallowed-positions.rs:338:17 | LL | (let true = let true = true); | ^^^ @@ -787,7 +773,7 @@ LL | (let true = let true = true); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:340:25 + --> $DIR/disallowed-positions.rs:344:25 | LL | let x = true && let y = 1; | ^^^ @@ -795,7 +781,7 @@ LL | let x = true && let y = 1; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:346:19 + --> $DIR/disallowed-positions.rs:350:19 | LL | [1, 2, 3][let _ = ()] | ^^^ @@ -803,7 +789,7 @@ LL | [1, 2, 3][let _ = ()] = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:351:6 + --> $DIR/disallowed-positions.rs:355:6 | LL | &let 0 = 0 | ^^^ @@ -811,7 +797,7 @@ LL | &let 0 = 0 = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:362:17 + --> $DIR/disallowed-positions.rs:366:17 | LL | true && let 1 = 1 | ^^^ @@ -819,7 +805,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:367:17 + --> $DIR/disallowed-positions.rs:371:17 | LL | true && let 1 = 1 | ^^^ @@ -827,7 +813,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:372:17 + --> $DIR/disallowed-positions.rs:376:17 | LL | true && let 1 = 1 | ^^^ @@ -835,7 +821,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:383:17 + --> $DIR/disallowed-positions.rs:387:17 | LL | true && let 1 = 1 | ^^^ @@ -843,7 +829,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/disallowed-positions.rs:383:9 + --> $DIR/disallowed-positions.rs:387:9 | LL | true && let 1 = 1 | ^^^^^^^^^^^^^^^^^ @@ -854,124 +840,124 @@ LL | { true && let 1 = 1 } | + + error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:393:9 + --> $DIR/disallowed-positions.rs:397:9 | LL | if (let Some(a) = opt && true) { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:393:9 + --> $DIR/disallowed-positions.rs:397:9 | LL | if (let Some(a) = opt && true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:397:9 + --> $DIR/disallowed-positions.rs:401:9 | LL | if (let Some(a) = opt) && true { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:397:9 + --> $DIR/disallowed-positions.rs:401:9 | LL | if (let Some(a) = opt) && true { | ^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:400:9 + --> $DIR/disallowed-positions.rs:404:9 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:400:9 + --> $DIR/disallowed-positions.rs:404:9 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:400:32 + --> $DIR/disallowed-positions.rs:404:32 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:400:32 + --> $DIR/disallowed-positions.rs:404:32 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:408:9 + --> $DIR/disallowed-positions.rs:412:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:408:9 + --> $DIR/disallowed-positions.rs:412:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:408:31 + --> $DIR/disallowed-positions.rs:412:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:408:31 + --> $DIR/disallowed-positions.rs:412:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:412:9 + --> $DIR/disallowed-positions.rs:416:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:412:9 + --> $DIR/disallowed-positions.rs:416:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:412:31 + --> $DIR/disallowed-positions.rs:416:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:412:31 + --> $DIR/disallowed-positions.rs:416:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:416:9 + --> $DIR/disallowed-positions.rs:420:9 | LL | if (let Some(a) = opt && (true)) && true { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:416:9 + --> $DIR/disallowed-positions.rs:420:9 | LL | if (let Some(a) = opt && (true)) && true { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:436:22 + --> $DIR/disallowed-positions.rs:440:22 | LL | let x = (true && let y = 1); | ^^^ @@ -979,7 +965,7 @@ LL | let x = (true && let y = 1); = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:441:20 + --> $DIR/disallowed-positions.rs:445:20 | LL | ([1, 2, 3][let _ = ()]) | ^^^ diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs index 8eb8d617d581c..65beccf2214fd 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs @@ -90,8 +90,12 @@ fn _macros() { } use_expr!((let 0 = 1 && 0 == 0)); //[feature,no_feature]~^ ERROR expected expression, found `let` statement + //[feature,no_feature]~| ERROR expected expression, found `let` statement + //[feature,no_feature]~| ERROR expected expression, found `let` statement use_expr!((let 0 = 1)); //[feature,no_feature]~^ ERROR expected expression, found `let` statement + //[feature,no_feature]~| ERROR expected expression, found `let` statement + //[feature,no_feature]~| ERROR expected expression, found `let` statement } #[cfg(not(nothing))] @@ -115,7 +119,7 @@ fn nested_within_if_expr() { //~^ ERROR expected expression, found `let` statement if true || let 0 = 0 {} - //~^ ERROR expected expression, found `let` statement + //~^ ERROR `||` operators are not supported in let chain conditions if (true || let 0 = 0) {} //~^ ERROR expected expression, found `let` statement if true && (true || let 0 = 0) {} @@ -206,7 +210,7 @@ fn nested_within_while_expr() { //~^ ERROR expected expression, found `let` statement while true || let 0 = 0 {} - //~^ ERROR expected expression, found `let` statement + //~^ ERROR `||` operators are not supported in let chain conditions while (true || let 0 = 0) {} //~^ ERROR expected expression, found `let` statement while true && (true || let 0 = 0) {} @@ -336,12 +340,12 @@ fn outside_if_and_while_expr() { //~| ERROR expected expression, found `let` statement { - #[cfg(FALSE)] + #[cfg(false)] let x = true && let y = 1; //~^ ERROR expected expression, found `let` statement } - #[cfg(FALSE)] + #[cfg(false)] { [1, 2, 3][let _ = ()] //~^ ERROR expected expression, found `let` statement @@ -432,11 +436,11 @@ fn with_parenthesis() { //[no_feature]~^ ERROR `let` expressions in this position are unstable } - #[cfg(FALSE)] + #[cfg(false)] let x = (true && let y = 1); //~^ ERROR expected expression, found `let` statement - #[cfg(FALSE)] + #[cfg(false)] { ([1, 2, 3][let _ = ()]) //~^ ERROR expected expression, found `let` statement diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.edition2021.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.edition2021.stderr new file mode 100644 index 0000000000000..23700f89f1080 --- /dev/null +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.edition2021.stderr @@ -0,0 +1,65 @@ +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:19:30 + | +LL | macro_in_2021::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:19:52 + | +LL | macro_in_2021::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:22:5 + | +LL | macro_in_2021::make_if!(let (Some(0)) let (Some(0)) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `macro_in_2021::make_if` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:22:5 + | +LL | macro_in_2021::make_if!(let (Some(0)) let (Some(0)) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `macro_in_2021::make_if` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:26:30 + | +LL | macro_in_2024::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:26:52 + | +LL | macro_in_2024::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.edition2024.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.edition2024.stderr new file mode 100644 index 0000000000000..3af844f4f9665 --- /dev/null +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.edition2024.stderr @@ -0,0 +1,45 @@ +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:19:30 + | +LL | macro_in_2021::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:19:52 + | +LL | macro_in_2021::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:22:5 + | +LL | macro_in_2021::make_if!(let (Some(0)) let (Some(0)) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `macro_in_2021::make_if` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/edition-gate-macro-error.rs:22:5 + | +LL | macro_in_2021::make_if!(let (Some(0)) let (Some(0)) { never!() } { never!() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `macro_in_2021::make_if` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.rs new file mode 100644 index 0000000000000..89b555d2c50b7 --- /dev/null +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro-error.rs @@ -0,0 +1,30 @@ +//@ revisions: edition2021 edition2024 +//@ compile-flags: -Z lint-mir -Z validate-mir +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 +//@ aux-build:macro-in-2021.rs +//@ aux-build:macro-in-2024.rs + +use std::unreachable as never; + +// Compiletest doesn't specify the needed --extern flags to make `extern crate` unneccessary +extern crate macro_in_2021; +extern crate macro_in_2024; + +fn main() { + // Gated on both 2021 and 2024 if the `if` comes from a 2021 macro + // Gated only on 2021 if the `if` comes from a 2024 macro + // No gating if both the `if` and the chain are from a 2024 macro + + macro_in_2021::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + //~^ ERROR `let` expressions in this position are unstable + //~| ERROR `let` expressions in this position are unstable + macro_in_2021::make_if!(let (Some(0)) let (Some(0)) { never!() } { never!() }); + //~^ ERROR `let` expressions in this position are unstable + //~| ERROR `let` expressions in this position are unstable + + macro_in_2024::make_if!((let Some(0) = None && let Some(0) = None) { never!() } { never!() }); + //[edition2021]~^ ERROR `let` expressions in this position are unstable + //[edition2021]~| ERROR `let` expressions in this position are unstable + macro_in_2024::make_if!(let (Some(0)) let (Some(0)) { never!() } { never!() }); +} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro.rs new file mode 100644 index 0000000000000..5dd928dbce5b0 --- /dev/null +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/edition-gate-macro.rs @@ -0,0 +1,76 @@ +//@ revisions: edition2021 edition2024 +//@ compile-flags: -Z lint-mir -Z validate-mir +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 +//@ aux-build:macro-in-2021.rs +//@ aux-build:macro-in-2024.rs +//@ run-pass + +#![allow(dead_code)] + +use std::unreachable as never; +use std::cell::RefCell; +use std::convert::TryInto; + +// Compiletest doesn't specify the needed --extern flags to make `extern crate` unneccessary +extern crate macro_in_2021; +extern crate macro_in_2024; + +#[derive(Default)] +struct DropOrderCollector(RefCell>); + +struct LoudDrop<'a>(&'a DropOrderCollector, u32); + +impl Drop for LoudDrop<'_> { + fn drop(&mut self) { + println!("{}", self.1); + self.0.0.borrow_mut().push(self.1); + } +} + +impl DropOrderCollector { + fn print(&self, n: u32) { + println!("{n}"); + self.0.borrow_mut().push(n) + } + fn some_loud(&self, n: u32) -> Option { + Some(LoudDrop(self, n)) + } + + #[track_caller] + fn validate(self) { + assert!( + self.0 + .into_inner() + .into_iter() + .enumerate() + .all(|(idx, item)| idx + 1 == item.try_into().unwrap()) + ); + } + fn with_macro_2021(self) { + // Edition 2021 drop behaviour + macro_in_2021::make_if!((let None = self.some_loud(2)) { never!() } {self.print(1) }); + macro_in_2021::make_if!(let (self.some_loud(4)) { never!() } { self.print(3) }); + self.validate(); + } + fn with_macro_2024(self) { + // Edition 2024 drop behaviour + macro_in_2024::make_if!((let None = self.some_loud(1)) { never!() } { self.print(2) }); + macro_in_2024::make_if!(let (self.some_loud(3)) { never!() } { self.print(4) }); + self.validate(); + } +} + +fn main() { + // 2021 drop order if it's a 2021 macro creating the `if` + // 2024 drop order if it's a 2024 macro creating the `if` + + // Compare this with edition-gate-macro-error.rs: We want to avoid exposing 2021 drop order, + // because it can create bad MIR (issue #104843) + // This test doesn't contain any let chains at all: it should be understood + // in combination with `edition-gate-macro-error.rs` + + DropOrderCollector::default().with_macro_2021(); + DropOrderCollector::default().with_macro_2024(); + +} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs index 3c572054e3fad..8e7db9a430cc5 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs @@ -1,4 +1,4 @@ -#![feature(let_chains)] +//@ edition: 2024 fn main() { let opt = Some(1i32); diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs index 2087fc42cf102..dad02b7f10639 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs @@ -51,7 +51,7 @@ fn _macros() { while $e {} } } - #[cfg(FALSE)] (let 0 = 1); + #[cfg(false)] (let 0 = 1); //~^ ERROR expected expression, found `let` statement use_expr!(let 0 = 1); //~^ ERROR no rules expected keyword `let` diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr index 7c874ae78a8ad..b9dac472dca15 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr @@ -1,7 +1,7 @@ error: expected expression, found `let` statement --> $DIR/feature-gate.rs:54:20 | -LL | #[cfg(FALSE)] (let 0 = 1); +LL | #[cfg(false)] (let 0 = 1); | ^^^ | = note: only supported directly in conditions of `if` and `while` expressions diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs index dce1c19ff339a..ae525aed4148f 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs @@ -3,12 +3,12 @@ fn main() { let _opt = Some(1i32); - #[cfg(FALSE)] + #[cfg(false)] { let _ = &&let Some(x) = Some(42); //~^ ERROR expected expression, found `let` statement } - #[cfg(FALSE)] + #[cfg(false)] { if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 { //~^ ERROR expected expression, found `let` statement @@ -18,7 +18,7 @@ fn main() { } } - #[cfg(FALSE)] + #[cfg(false)] { if let Some(elem) = _opt && { [1, 2, 3][let _ = ()]; @@ -28,7 +28,7 @@ fn main() { } } - #[cfg(FALSE)] + #[cfg(false)] { if let Some(elem) = _opt && [1, 2, 3][let _ = ()] = 1 { //~^ ERROR expected expression, found `let` statement @@ -36,7 +36,7 @@ fn main() { true } } - #[cfg(FALSE)] + #[cfg(false)] { if let a = 1 && { let x = let y = 1; diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs index 59c81d053bc76..0113a87cecc25 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs @@ -1,6 +1,5 @@ //@ check-pass - -#![feature(let_chains)] +//@ edition: 2024 fn main() { let x = Some(vec!["test"]); diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs index ea4b34eacba5d..f5d87d6999a23 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs @@ -1,6 +1,5 @@ //@ check-pass - -#![feature(let_chains)] +//@ edition:2024 fn main() { let opt = Some("foo bar"); diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs index f90b9ab0d40f0..f6de37867d855 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs @@ -2,7 +2,6 @@ fn main() { match true { _ if let true = true && true => {} //~^ ERROR `if let` guards are - //~| ERROR `let` expressions in this _ => {} } } diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.stderr index 637ae4915ed14..17fa37d2df322 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.stderr @@ -9,16 +9,6 @@ LL | _ if let true = true && true => {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: you can write `if matches!(, )` instead of `if let = ` -error[E0658]: `let` expressions in this position are unstable - --> $DIR/issue-93150.rs:3:14 - | -LL | _ if let true = true && true => {} - | ^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs index 77756d0bee06b..7589b33a73c19 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs @@ -1,6 +1,7 @@ //@ compile-flags: -Zvalidate-mir -C opt-level=3 //@ build-pass -#![feature(let_chains)] +//@ edition: 2024 + struct TupleIter> { inner: I, } diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/temporary-early-drop.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/temporary-early-drop.rs index 2c7fe2eea3308..7d8dfe37135c7 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/temporary-early-drop.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/temporary-early-drop.rs @@ -3,7 +3,6 @@ //@ edition: 2024 //@ check-pass -#![feature(let_chains)] #![allow(irrefutable_let_patterns)] struct Pd; diff --git a/tests/ui/rfcs/rfc-2528-type-changing-struct-update/issue-92010-trait-bound-not-satisfied.rs b/tests/ui/rfcs/rfc-2528-type-changing-struct-update/issue-92010-trait-bound-not-satisfied.rs index f1a54ee5867c5..3de88fcaecb6c 100644 --- a/tests/ui/rfcs/rfc-2528-type-changing-struct-update/issue-92010-trait-bound-not-satisfied.rs +++ b/tests/ui/rfcs/rfc-2528-type-changing-struct-update/issue-92010-trait-bound-not-satisfied.rs @@ -6,7 +6,7 @@ struct P { impl P { fn y(&self, y: f64) -> Self { P{y, .. self.clone() } } - //~^ mismatched types [E0308] + //~^ ERROR mismatched types [E0308] } fn main() {} diff --git a/tests/ui/rfcs/rfc-2565-param-attrs/attr-without-param.rs b/tests/ui/rfcs/rfc-2565-param-attrs/attr-without-param.rs index eeb2191bab462..c0f17570673ce 100644 --- a/tests/ui/rfcs/rfc-2565-param-attrs/attr-without-param.rs +++ b/tests/ui/rfcs/rfc-2565-param-attrs/attr-without-param.rs @@ -1,14 +1,14 @@ -#[cfg(FALSE)] +#[cfg(false)] impl S { fn f(#[attr]) {} //~ ERROR expected parameter name, found `)` } -#[cfg(FALSE)] +#[cfg(false)] impl T for S { fn f(#[attr]) {} //~ ERROR expected parameter name, found `)` } -#[cfg(FALSE)] +#[cfg(false)] trait T { fn f(#[attr]); //~ ERROR expected argument name, found `)` } diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.rs deleted file mode 100644 index 3b982857db8dc..0000000000000 --- a/tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ ignore-windows -//@ compile-flags: --crate-type lib -#[link(name = "foo", kind = "raw-dylib")] -//~^ ERROR: link kind `raw-dylib` is only supported on Windows targets -extern "C" {} diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr deleted file mode 100644 index ede20cb8c3f76..0000000000000 --- a/tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0455]: link kind `raw-dylib` is only supported on Windows targets - --> $DIR/raw-dylib-windows-only.rs:3:29 - | -LL | #[link(name = "foo", kind = "raw-dylib")] - | ^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0455`. diff --git a/tests/ui/rfcs/rfc-3467-unsafe-pinned/unsafe-pinned-hides-niche.rs b/tests/ui/rfcs/rfc-3467-unsafe-pinned/unsafe-pinned-hides-niche.rs new file mode 100644 index 0000000000000..a1ff9a1f69fd2 --- /dev/null +++ b/tests/ui/rfcs/rfc-3467-unsafe-pinned/unsafe-pinned-hides-niche.rs @@ -0,0 +1,29 @@ +//@ check-pass +// this test ensures that UnsafePinned hides the niche of its inner type, just like UnsafeCell does + +#![crate_type = "lib"] +#![feature(unsafe_pinned)] + +use std::num::NonZero; +use std::pin::UnsafePinned; + +macro_rules! assert_size_is { + ($ty:ty = $size:expr) => { + const _: () = assert!(size_of::<$ty>() == $size); + }; +} + +assert_size_is!(UnsafePinned<()> = 0); +assert_size_is!(UnsafePinned = 1); + +assert_size_is!( UnsafePinned< u32> = 4); +assert_size_is!( UnsafePinned< NonZero> = 4); +assert_size_is!( UnsafePinned>> = 4); +assert_size_is!(Option> = 8); +assert_size_is!(Option>> = 8); +assert_size_is!(Option>>> = 8); + +assert_size_is!( UnsafePinned< &()> = size_of::()); +assert_size_is!( UnsafePinned> = size_of::()); +assert_size_is!(Option> = size_of::() * 2); +assert_size_is!(Option>> = size_of::() * 2); diff --git a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs index 34b94f2e1c782..4cf2d1ac4a6be 100644 --- a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs +++ b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs @@ -12,6 +12,7 @@ impl<'a, T> Trait<'a> for T { mod basic_pass { use super::*; type Opq<'a> = impl Sized + 'a; + #[define_opaque(Opq)] fn test() -> impl for<'a> Trait<'a, Ty = Opq<'a>> {} //~^ ERROR: expected generic lifetime parameter, found `'a` } @@ -27,6 +28,7 @@ mod capture_tait { type Opq0 = impl Sized; type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0>; type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; + #[define_opaque(Opq2)] fn test() -> Opq2 {} //~^ ERROR hidden type for `capture_tait::Opq0` captures lifetime that does not appear in bounds } @@ -36,6 +38,7 @@ mod capture_tait_complex_pass { type Opq0<'a> = impl Sized; type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'b>>; // <- Note 'b type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; + #[define_opaque(Opq2)] fn test() -> Opq2 {} //~^ ERROR: expected generic lifetime parameter, found `'a` } @@ -46,6 +49,7 @@ mod capture_tait_complex_fail { type Opq0<'a> = impl Sized; type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a>>; // <- Note 'a type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; + #[define_opaque(Opq2)] fn test() -> Opq2 {} //~^ ERROR hidden type for `capture_tait_complex_fail::Opq0<'a>` captures lifetime that does not appear in bounds } @@ -54,18 +58,18 @@ mod capture_tait_complex_fail { mod constrain_fail0 { use super::*; type Opq0<'a, 'b> = impl Sized; + #[define_opaque(Opq0)] fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'static>> {} - //~^ ERROR non-defining opaque type use in defining scope - //~| ERROR: expected generic lifetime parameter, found `'a` + //~^ ERROR: expected generic lifetime parameter, found `'a` } // non-defining use because generic lifetime is used multiple times. mod constrain_fail { use super::*; type Opq0<'a, 'b> = impl Sized; + #[define_opaque(Opq0)] fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'a>> {} - //~^ ERROR non-defining opaque type use in defining scope - //~| ERROR: expected generic lifetime parameter, found `'a` + //~^ ERROR: expected generic lifetime parameter, found `'a` } mod constrain_pass { @@ -73,6 +77,7 @@ mod constrain_pass { type Opq0<'a, 'b> = impl Sized; type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a, 'b>>; type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; + #[define_opaque(Opq2)] fn test() -> Opq2 {} //~^ ERROR: expected generic lifetime parameter, found `'a` } diff --git a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr index fb1e4cca3f40b..3614fc8f45c74 100644 --- a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr +++ b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr @@ -1,13 +1,14 @@ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/higher-ranked-regions-basic.rs:15:55 + --> $DIR/higher-ranked-regions-basic.rs:16:55 | LL | type Opq<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter +LL | #[define_opaque(Opq)] LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq<'a>> {} | ^^ error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds - --> $DIR/higher-ranked-regions-basic.rs:21:58 + --> $DIR/higher-ranked-regions-basic.rs:22:58 | LL | fn test() -> impl for<'a> Trait<'a, Ty = impl Sized> {} | -- ---------- ^^ @@ -16,86 +17,64 @@ LL | fn test() -> impl for<'a> Trait<'a, Ty = impl Sized> {} | hidden type `&'a ()` captures the lifetime `'a` as defined here error[E0700]: hidden type for `capture_tait::Opq0` captures lifetime that does not appear in bounds - --> $DIR/higher-ranked-regions-basic.rs:30:23 + --> $DIR/higher-ranked-regions-basic.rs:32:23 | LL | type Opq0 = impl Sized; | ---------- opaque type defined here LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0>; | -- hidden type `&'b ()` captures the lifetime `'b` as defined here -LL | type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; +... LL | fn test() -> Opq2 {} | ^^ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/higher-ranked-regions-basic.rs:39:23 + --> $DIR/higher-ranked-regions-basic.rs:42:23 | LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'b>>; // <- Note 'b | -- this generic parameter must be used with a generic lifetime parameter -LL | type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; +... LL | fn test() -> Opq2 {} | ^^ error[E0700]: hidden type for `capture_tait_complex_fail::Opq0<'a>` captures lifetime that does not appear in bounds - --> $DIR/higher-ranked-regions-basic.rs:49:23 + --> $DIR/higher-ranked-regions-basic.rs:53:23 | LL | type Opq0<'a> = impl Sized; | ---------- opaque type defined here LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a>>; // <- Note 'a | -- hidden type `&'b ()` captures the lifetime `'b` as defined here -LL | type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; +... LL | fn test() -> Opq2 {} | ^^ -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/higher-ranked-regions-basic.rs:57:41 - | -LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'static>> {} - | ^^^^^^^^^^^^^^^^^^^^^^ argument `'static` is not a generic parameter - | -note: for this opaque type - --> $DIR/higher-ranked-regions-basic.rs:56:25 - | -LL | type Opq0<'a, 'b> = impl Sized; - | ^^^^^^^^^^ - error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/higher-ranked-regions-basic.rs:57:65 + --> $DIR/higher-ranked-regions-basic.rs:62:65 | LL | type Opq0<'a, 'b> = impl Sized; | -- this generic parameter must be used with a generic lifetime parameter +LL | #[define_opaque(Opq0)] LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'static>> {} | ^^ -error: non-defining opaque type use in defining scope - --> $DIR/higher-ranked-regions-basic.rs:66:41 - | -LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'a>> {} - | ^^^^^^^^^^^^^^^^^ generic argument `'a` used twice - | -note: for this opaque type - --> $DIR/higher-ranked-regions-basic.rs:65:25 - | -LL | type Opq0<'a, 'b> = impl Sized; - | ^^^^^^^^^^ - error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/higher-ranked-regions-basic.rs:66:60 + --> $DIR/higher-ranked-regions-basic.rs:71:60 | LL | type Opq0<'a, 'b> = impl Sized; | -- this generic parameter must be used with a generic lifetime parameter +LL | #[define_opaque(Opq0)] LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'a>> {} | ^^ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/higher-ranked-regions-basic.rs:76:23 + --> $DIR/higher-ranked-regions-basic.rs:81:23 | LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a, 'b>>; | -- this generic parameter must be used with a generic lifetime parameter -LL | type Opq2 = impl for<'a> Trait<'a, Ty = Opq1<'a>>; +... LL | fn test() -> Opq2 {} | ^^ -error: aborting due to 10 previous errors +error: aborting due to 8 previous errors Some errors have detailed explanations: E0700, E0792. For more information about an error, try `rustc --explain E0700`. diff --git a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-gat.rs b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-gat.rs index db5e5e05e54be..e0b7909c24091 100644 --- a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-gat.rs +++ b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-gat.rs @@ -14,7 +14,10 @@ impl Trait for Struct { type Assoc<'a> = &'a u32; } -const FOO: Foo = Struct; -//~^ ERROR: expected generic lifetime parameter, found `'a` +#[define_opaque(Foo)] +fn foo() -> Foo { + Struct + //~^ ERROR: expected generic lifetime parameter, found `'a` +} fn main() {} diff --git a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-gat.stderr b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-gat.stderr index 9b361445f1ec3..b3edc9427327e 100644 --- a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-gat.stderr +++ b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-gat.stderr @@ -1,11 +1,11 @@ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/higher-ranked-regions-gat.rs:17:18 + --> $DIR/higher-ranked-regions-gat.rs:19:5 | LL | pub type FooAssoc<'a> = impl Sized; | -- this generic parameter must be used with a generic lifetime parameter ... -LL | const FOO: Foo = Struct; - | ^^^^^^ +LL | Struct + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/rmeta/emit-artifact-notifications.stderr b/tests/ui/rmeta/emit-artifact-notifications.stderr index 4f68a2d74ed31..c002be7bcff05 100644 --- a/tests/ui/rmeta/emit-artifact-notifications.stderr +++ b/tests/ui/rmeta/emit-artifact-notifications.stderr @@ -1 +1 @@ -{"$message_type":"artifact","artifact":"$TEST_BUILD_DIR/rmeta/emit-artifact-notifications/libemit_artifact_notifications.rmeta","emit":"metadata"} +{"$message_type":"artifact","artifact":"$TEST_BUILD_DIR/libemit_artifact_notifications.rmeta","emit":"metadata"} diff --git a/tests/ui/rmeta/no_optitimized_mir.rs b/tests/ui/rmeta/no_optitimized_mir.rs index 7d2e1b87215fd..c8ed00b039b23 100644 --- a/tests/ui/rmeta/no_optitimized_mir.rs +++ b/tests/ui/rmeta/no_optitimized_mir.rs @@ -9,3 +9,5 @@ extern crate rmeta_meta; fn main() { rmeta_meta::missing_optimized_mir(); } + +//~? ERROR missing optimized MIR for `missing_optimized_mir` in the crate `rmeta_meta` diff --git a/tests/ui/rmeta/no_optitimized_mir.stderr b/tests/ui/rmeta/no_optitimized_mir.stderr index 92f22d7800050..254f100aa7b5e 100644 --- a/tests/ui/rmeta/no_optitimized_mir.stderr +++ b/tests/ui/rmeta/no_optitimized_mir.stderr @@ -1,4 +1,4 @@ -error: missing optimized MIR for an item in the crate `rmeta_meta` +error: missing optimized MIR for `missing_optimized_mir` in the crate `rmeta_meta` | note: missing optimized MIR for this item (was the crate `rmeta_meta` compiled with `--emit=metadata`?) --> $DIR/auxiliary/rmeta-meta.rs:10:1 diff --git a/tests/ui/rmeta/rmeta_bin-pass.rs b/tests/ui/rmeta/rmeta_bin-pass.rs new file mode 100644 index 0000000000000..7de4f3ba96137 --- /dev/null +++ b/tests/ui/rmeta/rmeta_bin-pass.rs @@ -0,0 +1,14 @@ +//@ compile-flags: --emit=obj,metadata --crate-type=bin +//@ aux-build:rmeta-meta.rs +//@ no-prefer-dynamic +//@ build-pass + +// Check that building a metadata bin crate works with a dependent, metadata +// crate if linking is not requested. + +extern crate rmeta_meta; +use rmeta_meta::Foo; + +pub fn main() { + let _ = Foo { field: 42 }; +} diff --git a/tests/ui/rmeta/rmeta_bin.rs b/tests/ui/rmeta/rmeta_bin.rs new file mode 100644 index 0000000000000..9eb65241fafdf --- /dev/null +++ b/tests/ui/rmeta/rmeta_bin.rs @@ -0,0 +1,15 @@ +//@ build-fail +//@ compile-flags: --crate-type=bin +//@ aux-build:rmeta-meta.rs +//@ no-prefer-dynamic + +// Check that building a bin crate fails if a dependent crate is metadata-only. + +extern crate rmeta_meta; +use rmeta_meta::Foo; + +fn main() { + let _ = Foo { field: 42 }; +} + +//~? ERROR crate `rmeta_meta` required to be available in rlib format, but was not found in this form diff --git a/tests/ui/rmeta/rmeta_bin.stderr b/tests/ui/rmeta/rmeta_bin.stderr new file mode 100644 index 0000000000000..830169e032a1b --- /dev/null +++ b/tests/ui/rmeta/rmeta_bin.stderr @@ -0,0 +1,4 @@ +error: crate `rmeta_meta` required to be available in rlib format, but was not found in this form + +error: aborting due to 1 previous error + diff --git a/tests/ui/rmeta/rmeta_lib.rs b/tests/ui/rmeta/rmeta_lib.rs index 1be4ee8de7937..d1a2b653ed80b 100644 --- a/tests/ui/rmeta/rmeta_lib.rs +++ b/tests/ui/rmeta/rmeta_lib.rs @@ -1,7 +1,6 @@ //@ build-fail //@ aux-build:rmeta-meta.rs //@ no-prefer-dynamic -//@ error-pattern: crate `rmeta_meta` required to be available in rlib format, but was not found // Check that building a non-metadata crate fails if a dependent crate is // metadata-only. @@ -12,3 +11,5 @@ use rmeta_meta::Foo; fn main() { let _ = Foo { field: 42 }; } + +//~? ERROR crate `rmeta_meta` required to be available in rlib format, but was not found in this form diff --git a/tests/ui/runtime/backtrace-debuginfo-aux.rs b/tests/ui/runtime/backtrace-debuginfo-aux.rs index 24180ed219665..4493fa51f9503 100644 --- a/tests/ui/runtime/backtrace-debuginfo-aux.rs +++ b/tests/ui/runtime/backtrace-debuginfo-aux.rs @@ -1,5 +1,4 @@ -//@ run-pass -//@ ignore-test: not a test, used by backtrace-debuginfo.rs to test file!() +//@ ignore-auxiliary (used by `./backtrace-debuginfo.rs` to test `file!()`) #[inline(never)] pub fn callback(f: F) where F: FnOnce((&'static str, u32)) { diff --git a/tests/ui/runtime/backtrace-debuginfo.rs b/tests/ui/runtime/backtrace-debuginfo.rs index afc96d6bb5f54..37fce2788b7f0 100644 --- a/tests/ui/runtime/backtrace-debuginfo.rs +++ b/tests/ui/runtime/backtrace-debuginfo.rs @@ -42,9 +42,13 @@ macro_rules! dump_and_die { // there, even on i686-pc-windows-msvc. We do the best we can in // rust-lang/rust to test it as well, but sometimes we just gotta keep // landing PRs. + // + // aarch64-msvc is broken as its backtraces are truncated. + // See https://github.com/rust-lang/rust/issues/140489 if cfg!(any(target_os = "android", all(target_os = "linux", target_arch = "arm"), all(target_env = "msvc", target_arch = "x86"), + all(target_env = "msvc", target_arch = "aarch64"), target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd")) { diff --git a/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs b/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs index f3f9ce0bd879e..3a574af9bba7e 100644 --- a/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs +++ b/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs @@ -26,14 +26,7 @@ extern "C" fn main(argc: core::ffi::c_int, argv: *const *const u8) -> core::ffi: let actual = unsafe { let mut actual: libc::sigaction = core::mem::zeroed(); libc::sigaction(libc::SIGPIPE, core::ptr::null(), &mut actual); - #[cfg(not(target_os = "aix"))] - { - actual.sa_sigaction - } - #[cfg(target_os = "aix")] - { - actual.sa_union.__su_sigaction as libc::sighandler_t - } + actual.sa_sigaction }; assert_eq!(actual, expected, "actual and expected SIGPIPE disposition in child differs"); diff --git a/tests/ui/runtime/on-broken-pipe/auxiliary/sigpipe-utils.rs b/tests/ui/runtime/on-broken-pipe/auxiliary/sigpipe-utils.rs index d16a2b4d8c8ab..3d93d50ca3fbb 100644 --- a/tests/ui/runtime/on-broken-pipe/auxiliary/sigpipe-utils.rs +++ b/tests/ui/runtime/on-broken-pipe/auxiliary/sigpipe-utils.rs @@ -20,14 +20,7 @@ pub fn assert_sigpipe_handler(expected_handler: SignalHandler) { let actual = unsafe { let mut actual: libc::sigaction = std::mem::zeroed(); libc::sigaction(libc::SIGPIPE, std::ptr::null(), &mut actual); - #[cfg(not(target_os = "aix"))] - { - actual.sa_sigaction - } - #[cfg(target_os = "aix")] - { - actual.sa_union.__su_sigaction as libc::sighandler_t - } + actual.sa_sigaction }; let expected = match expected_handler { diff --git a/tests/ui/runtime/on-broken-pipe/default.rs b/tests/ui/runtime/on-broken-pipe/default.rs index c10d1cfacc0c3..61b7810e2a186 100644 --- a/tests/ui/runtime/on-broken-pipe/default.rs +++ b/tests/ui/runtime/on-broken-pipe/default.rs @@ -2,3 +2,5 @@ //@ check-fail fn main() {} + +//~? ERROR incorrect value `default` for unstable option `on-broken-pipe` diff --git a/tests/ui/runtime/on-broken-pipe/no-flag-arg.rs b/tests/ui/runtime/on-broken-pipe/no-flag-arg.rs index 2273291bfa778..bb49533c0231a 100644 --- a/tests/ui/runtime/on-broken-pipe/no-flag-arg.rs +++ b/tests/ui/runtime/on-broken-pipe/no-flag-arg.rs @@ -2,3 +2,5 @@ //@ check-fail fn main() {} + +//~? ERROR unstable option `on-broken-pipe` requires either `kill`, `error`, or `inherit` diff --git a/tests/ui/runtime/on-broken-pipe/wrong-flag-arg.rs b/tests/ui/runtime/on-broken-pipe/wrong-flag-arg.rs index 14d0ac56b5a3d..c4a07932bc20e 100644 --- a/tests/ui/runtime/on-broken-pipe/wrong-flag-arg.rs +++ b/tests/ui/runtime/on-broken-pipe/wrong-flag-arg.rs @@ -2,3 +2,5 @@ //@ check-fail fn main() {} + +//~? ERROR incorrect value `wrong` for unstable option `on-broken-pipe` diff --git a/tests/ui/runtime/rt-explody-panic-payloads.rs b/tests/ui/runtime/rt-explody-panic-payloads.rs index c177fd260ed4f..d564a26ca7374 100644 --- a/tests/ui/runtime/rt-explody-panic-payloads.rs +++ b/tests/ui/runtime/rt-explody-panic-payloads.rs @@ -27,6 +27,6 @@ fn main() { // by QEMU in the stderr whenever a core dump happens. Remove it before the check. v.strip_suffix("qemu: uncaught target signal 6 (Aborted) - core dumped\n").unwrap_or(v) }) - .map(|v| { v.ends_with("fatal runtime error: drop of the panic payload panicked\n") }) + .map(|v| v.ends_with("fatal runtime error: drop of the panic payload panicked, aborting\n")) .unwrap_or(false)); } diff --git a/tests/ui/runtime/signal-alternate-stack-cleanup.rs b/tests/ui/runtime/signal-alternate-stack-cleanup.rs index 8fce092827318..f2af86be0a5f5 100644 --- a/tests/ui/runtime/signal-alternate-stack-cleanup.rs +++ b/tests/ui/runtime/signal-alternate-stack-cleanup.rs @@ -29,14 +29,7 @@ fn main() { // Install signal handler that runs on alternate signal stack. let mut action: sigaction = std::mem::zeroed(); action.sa_flags = (SA_ONSTACK | SA_SIGINFO) as _; - #[cfg(not(target_os = "aix"))] - { - action.sa_sigaction = signal_handler as sighandler_t; - } - #[cfg(target_os = "aix")] - { - action.sa_union.__su_sigaction = signal_handler as sighandler_t; - } + action.sa_sigaction = signal_handler as sighandler_t; sigaction(SIGWINCH, &action, std::ptr::null_mut()); // Send SIGWINCH on exit. diff --git a/tests/ui/rust-2018/auxiliary/dummy-crate.rs b/tests/ui/rust-2018/auxiliary/dummy-crate.rs new file mode 100644 index 0000000000000..c9e8881600dea --- /dev/null +++ b/tests/ui/rust-2018/auxiliary/dummy-crate.rs @@ -0,0 +1,2 @@ +// intentionally blank, used because we need an extern crate for +// `removing-extern-crate.rs` but don't care about what's in it. diff --git a/tests/ui/rust-2018/edition-lint-nested-paths.fixed b/tests/ui/rust-2018/edition-lint-nested-paths.fixed index 742732ebc494b..93ccb2fe6af22 100644 --- a/tests/ui/rust-2018/edition-lint-nested-paths.fixed +++ b/tests/ui/rust-2018/edition-lint-nested-paths.fixed @@ -4,9 +4,9 @@ use crate::foo::{a, b}; //~^ ERROR absolute paths must start with -//~| this is accepted in the current edition +//~| WARN this is accepted in the current edition //~| ERROR absolute paths must start with -//~| this is accepted in the current edition +//~| WARN this is accepted in the current edition mod foo { pub(crate) fn a() {} @@ -21,9 +21,9 @@ fn main() { { use crate::foo::{self as x, c}; //~^ ERROR absolute paths must start with - //~| this is accepted in the current edition + //~| WARN this is accepted in the current edition //~| ERROR absolute paths must start with - //~| this is accepted in the current edition + //~| WARN this is accepted in the current edition x::a(); c(); } diff --git a/tests/ui/rust-2018/edition-lint-nested-paths.rs b/tests/ui/rust-2018/edition-lint-nested-paths.rs index 861ca521bb7b0..1c1d21dbab911 100644 --- a/tests/ui/rust-2018/edition-lint-nested-paths.rs +++ b/tests/ui/rust-2018/edition-lint-nested-paths.rs @@ -4,9 +4,9 @@ use foo::{a, b}; //~^ ERROR absolute paths must start with -//~| this is accepted in the current edition +//~| WARN this is accepted in the current edition //~| ERROR absolute paths must start with -//~| this is accepted in the current edition +//~| WARN this is accepted in the current edition mod foo { pub(crate) fn a() {} @@ -21,9 +21,9 @@ fn main() { { use foo::{self as x, c}; //~^ ERROR absolute paths must start with - //~| this is accepted in the current edition + //~| WARN this is accepted in the current edition //~| ERROR absolute paths must start with - //~| this is accepted in the current edition + //~| WARN this is accepted in the current edition x::a(); c(); } diff --git a/tests/ui/removing-extern-crate.fixed b/tests/ui/rust-2018/removing-extern-crate.fixed similarity index 86% rename from tests/ui/removing-extern-crate.fixed rename to tests/ui/rust-2018/removing-extern-crate.fixed index 477161fba804d..e88a84cc93e2a 100644 --- a/tests/ui/removing-extern-crate.fixed +++ b/tests/ui/rust-2018/removing-extern-crate.fixed @@ -1,5 +1,5 @@ //@ edition:2018 -//@ aux-build:removing-extern-crate.rs +//@ aux-build:dummy-crate.rs //@ run-rustfix //@ check-pass diff --git a/tests/ui/rust-2018/removing-extern-crate.rs b/tests/ui/rust-2018/removing-extern-crate.rs new file mode 100644 index 0000000000000..844377945e02f --- /dev/null +++ b/tests/ui/rust-2018/removing-extern-crate.rs @@ -0,0 +1,16 @@ +//@ edition:2018 +//@ aux-build:dummy-crate.rs +//@ run-rustfix +//@ check-pass + +#![warn(rust_2018_idioms)] + +extern crate dummy_crate as foo; //~ WARNING unused extern crate +extern crate core; //~ WARNING unused extern crate + +mod another { + extern crate dummy_crate as foo; //~ WARNING unused extern crate + extern crate core; //~ WARNING unused extern crate +} + +fn main() {} diff --git a/tests/ui/removing-extern-crate.stderr b/tests/ui/rust-2018/removing-extern-crate.stderr similarity index 75% rename from tests/ui/removing-extern-crate.stderr rename to tests/ui/rust-2018/removing-extern-crate.stderr index 4dddf160ce27b..573125426404d 100644 --- a/tests/ui/removing-extern-crate.stderr +++ b/tests/ui/rust-2018/removing-extern-crate.stderr @@ -1,8 +1,8 @@ warning: unused extern crate --> $DIR/removing-extern-crate.rs:8:1 | -LL | extern crate removing_extern_crate as foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it +LL | extern crate dummy_crate as foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it | note: the lint level is defined here --> $DIR/removing-extern-crate.rs:6:9 @@ -20,8 +20,8 @@ LL | extern crate core; warning: unused extern crate --> $DIR/removing-extern-crate.rs:12:5 | -LL | extern crate removing_extern_crate as foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it +LL | extern crate dummy_crate as foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it warning: unused extern crate --> $DIR/removing-extern-crate.rs:13:5 diff --git a/tests/ui/rust-2021/future-prelude-collision-generic-trait.fixed b/tests/ui/rust-2021/future-prelude-collision-generic-trait.fixed index ea104011873ce..129fdaeaeb239 100644 --- a/tests/ui/rust-2021/future-prelude-collision-generic-trait.fixed +++ b/tests/ui/rust-2021/future-prelude-collision-generic-trait.fixed @@ -23,7 +23,7 @@ where fn try_into(&self) -> Result<&U, i32> { >::try_from(self) //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 - //~| this is accepted in the current edition (Rust 2018) + //~| WARN this is accepted in the current edition (Rust 2018) } } diff --git a/tests/ui/rust-2021/future-prelude-collision-generic-trait.rs b/tests/ui/rust-2021/future-prelude-collision-generic-trait.rs index ce7dd2fdac76a..4e4d5d0d667c7 100644 --- a/tests/ui/rust-2021/future-prelude-collision-generic-trait.rs +++ b/tests/ui/rust-2021/future-prelude-collision-generic-trait.rs @@ -23,7 +23,7 @@ where fn try_into(&self) -> Result<&U, i32> { U::try_from(self) //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 - //~| this is accepted in the current edition (Rust 2018) + //~| WARN this is accepted in the current edition (Rust 2018) } } diff --git a/tests/ui/rust-2021/future-prelude-collision-generic.fixed b/tests/ui/rust-2021/future-prelude-collision-generic.fixed index 3546b1aef6ca6..bb852832456ab 100644 --- a/tests/ui/rust-2021/future-prelude-collision-generic.fixed +++ b/tests/ui/rust-2021/future-prelude-collision-generic.fixed @@ -27,11 +27,11 @@ impl std::iter::FromIterator for Generic<'static, i32> { fn main() { as MyFromIter>::from_iter(1); //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 - //~| this is accepted in the current edition (Rust 2018) + //~| WARN this is accepted in the current edition (Rust 2018) as MyFromIter>::from_iter(1); //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 - //~| this is accepted in the current edition (Rust 2018) + //~| WARN this is accepted in the current edition (Rust 2018) as MyFromIter>::from_iter(1); //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 - //~| this is accepted in the current edition (Rust 2018) + //~| WARN this is accepted in the current edition (Rust 2018) } diff --git a/tests/ui/rust-2021/future-prelude-collision-generic.rs b/tests/ui/rust-2021/future-prelude-collision-generic.rs index 1ae5e8fce23cd..bcaced88763c3 100644 --- a/tests/ui/rust-2021/future-prelude-collision-generic.rs +++ b/tests/ui/rust-2021/future-prelude-collision-generic.rs @@ -27,11 +27,11 @@ impl std::iter::FromIterator for Generic<'static, i32> { fn main() { Generic::from_iter(1); //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 - //~| this is accepted in the current edition (Rust 2018) + //~| WARN this is accepted in the current edition (Rust 2018) Generic::<'static, i32>::from_iter(1); //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 - //~| this is accepted in the current edition (Rust 2018) + //~| WARN this is accepted in the current edition (Rust 2018) Generic::<'_, _>::from_iter(1); //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 - //~| this is accepted in the current edition (Rust 2018) + //~| WARN this is accepted in the current edition (Rust 2018) } diff --git a/tests/ui/rust-2021/ice-return-unsized-can-impl-2.stderr b/tests/ui/rust-2021/ice-return-unsized-can-impl-2.stderr index f2942820e2887..75718c94b19d8 100644 --- a/tests/ui/rust-2021/ice-return-unsized-can-impl-2.stderr +++ b/tests/ui/rust-2021/ice-return-unsized-can-impl-2.stderr @@ -4,7 +4,7 @@ error[E0782]: expected a type, found a trait LL | fn concrete(b: B) -> B; | ^ | - = note: `B` it is dyn-incompatible, so it can't be `dyn` + = note: `B` is dyn-incompatible, otherwise a trait object could be used help: use a new generic type parameter, constrained by `B` | LL - fn concrete(b: B) -> B; @@ -32,7 +32,7 @@ error[E0782]: expected a type, found a trait LL | fn f(a: A) -> A; | ^ | - = note: `A` it is dyn-incompatible, so it can't be `dyn` + = note: `A` is dyn-incompatible, otherwise a trait object could be used help: use a new generic type parameter, constrained by `A` | LL - fn f(a: A) -> A; diff --git a/tests/ui/rust-2021/ice-return-unsized-can-impl.stderr b/tests/ui/rust-2021/ice-return-unsized-can-impl.stderr index cfee506e29b74..284232cf4f43e 100644 --- a/tests/ui/rust-2021/ice-return-unsized-can-impl.stderr +++ b/tests/ui/rust-2021/ice-return-unsized-can-impl.stderr @@ -4,7 +4,7 @@ error[E0782]: expected a type, found a trait LL | fn g(new: B) -> B; | ^ | - = note: `B` it is dyn-incompatible, so it can't be `dyn` + = note: `B` is dyn-incompatible, otherwise a trait object could be used help: use a new generic type parameter, constrained by `B` | LL - fn g(new: B) -> B; diff --git a/tests/ui/rust-2021/ice-unsized-fn-params-2.stderr b/tests/ui/rust-2021/ice-unsized-fn-params-2.stderr index 7f837bbe50f0f..630bbc2b905c7 100644 --- a/tests/ui/rust-2021/ice-unsized-fn-params-2.stderr +++ b/tests/ui/rust-2021/ice-unsized-fn-params-2.stderr @@ -4,7 +4,7 @@ error[E0782]: expected a type, found a trait LL | fn guard(_s: Copy) -> bool { | ^^^^ | - = note: `Copy` it is dyn-incompatible, so it can't be `dyn` + = note: `Copy` is dyn-incompatible, otherwise a trait object could be used help: use a new generic type parameter, constrained by `Copy` | LL - fn guard(_s: Copy) -> bool { diff --git a/tests/ui/rust-2021/ice-unsized-fn-params.stderr b/tests/ui/rust-2021/ice-unsized-fn-params.stderr index 4d900711ed6ec..57708031d6b54 100644 --- a/tests/ui/rust-2021/ice-unsized-fn-params.stderr +++ b/tests/ui/rust-2021/ice-unsized-fn-params.stderr @@ -4,7 +4,7 @@ error[E0782]: expected a type, found a trait LL | fn g(b: B) -> B; | ^ | - = note: `B` it is dyn-incompatible, so it can't be `dyn` + = note: `B` is dyn-incompatible, otherwise a trait object could be used help: use a new generic type parameter, constrained by `B` | LL - fn g(b: B) -> B; @@ -32,7 +32,7 @@ error[E0782]: expected a type, found a trait LL | fn f(a: A) -> A; | ^ | - = note: `A` it is dyn-incompatible, so it can't be `dyn` + = note: `A` is dyn-incompatible, otherwise a trait object could be used help: use a new generic type parameter, constrained by `A` | LL - fn f(a: A) -> A; diff --git a/tests/ui/rust-2024/reserved-guarded-strings.rs b/tests/ui/rust-2024/reserved-guarded-strings.rs index ae68d34cb86e5..ae4bd670fcad9 100644 --- a/tests/ui/rust-2024/reserved-guarded-strings.rs +++ b/tests/ui/rust-2024/reserved-guarded-strings.rs @@ -46,13 +46,13 @@ fn main() { //~^ ERROR prefix `blah` is unknown //~| ERROR invalid string literal - demo2!(## "foo"); //~ reserved multi-hash token is forbidden - demo3!("foo"###); //~ reserved multi-hash token is forbidden - demo3!(### "foo"); //~ reserved multi-hash token is forbidden - demo3!(## "foo"#); //~ reserved multi-hash token is forbidden + demo2!(## "foo"); //~ ERROR reserved multi-hash token is forbidden + demo3!("foo"###); //~ ERROR reserved multi-hash token is forbidden + demo3!(### "foo"); //~ ERROR reserved multi-hash token is forbidden + demo3!(## "foo"#); //~ ERROR reserved multi-hash token is forbidden demo5!(### "foo"###); - //~^ reserved multi-hash token is forbidden - //~| reserved multi-hash token is forbidden + //~^ ERROR reserved multi-hash token is forbidden + //~| ERROR reserved multi-hash token is forbidden demo1!(#""); //~ ERROR invalid string literal demo1!(#""#); //~ ERROR invalid string literal diff --git a/tests/ui/rust-2024/reserved-guarded-strings.stderr b/tests/ui/rust-2024/reserved-guarded-strings.stderr index 0f3b06147c4fd..fde3a719e6717 100644 --- a/tests/ui/rust-2024/reserved-guarded-strings.stderr +++ b/tests/ui/rust-2024/reserved-guarded-strings.stderr @@ -241,7 +241,7 @@ LL | demo2!(#"foo"## #); error: invalid string literal --> $DIR/reserved-guarded-strings.rs:71:12 | -LL | ...n!(####################################################################################################################################################################################################################################################################"foo... +LL | ...n!(####################################################################################################################################################################################################################################################################"foo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: unprefixed guarded string literals are reserved for future use since Rust 2024 diff --git a/tests/ui/rust-2024/unsafe-attributes/auxiliary/safe_attr.rs b/tests/ui/rust-2024/unsafe-attributes/auxiliary/safe_attr.rs new file mode 100644 index 0000000000000..161b71b9737bc --- /dev/null +++ b/tests/ui/rust-2024/unsafe-attributes/auxiliary/safe_attr.rs @@ -0,0 +1,7 @@ +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn safe(_attr: TokenStream, item: TokenStream) -> TokenStream { + item +} diff --git a/tests/ui/rust-2024/unsafe-attributes/safe-proc-macro-attribute.rs b/tests/ui/rust-2024/unsafe-attributes/safe-proc-macro-attribute.rs new file mode 100644 index 0000000000000..56b7001bdbf02 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-attributes/safe-proc-macro-attribute.rs @@ -0,0 +1,22 @@ +//! Anti-regression test for `#[safe]` proc-macro attribute. + +//@ revisions: unknown_attr proc_macro_attr +//@[proc_macro_attr] proc-macro: safe_attr.rs +//@[proc_macro_attr] check-pass + +#![warn(unsafe_attr_outside_unsafe)] + +#[cfg(proc_macro_attr)] +extern crate safe_attr; +#[cfg(proc_macro_attr)] +use safe_attr::safe; + +#[safe] +//[unknown_attr]~^ ERROR cannot find attribute `safe` in this scope +fn foo() {} + +#[safe(no_mangle)] +//[unknown_attr]~^ ERROR cannot find attribute `safe` in this scope +fn bar() {} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-attributes/safe-proc-macro-attribute.unknown_attr.stderr b/tests/ui/rust-2024/unsafe-attributes/safe-proc-macro-attribute.unknown_attr.stderr new file mode 100644 index 0000000000000..93c6d75e52ffc --- /dev/null +++ b/tests/ui/rust-2024/unsafe-attributes/safe-proc-macro-attribute.unknown_attr.stderr @@ -0,0 +1,14 @@ +error: cannot find attribute `safe` in this scope + --> $DIR/safe-proc-macro-attribute.rs:18:3 + | +LL | #[safe(no_mangle)] + | ^^^^ + +error: cannot find attribute `safe` in this scope + --> $DIR/safe-proc-macro-attribute.rs:14:3 + | +LL | #[safe] + | ^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-allow.rs b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-allow.rs new file mode 100644 index 0000000000000..76fdce7e5cff9 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-allow.rs @@ -0,0 +1,16 @@ +//@ check-pass +//@ edition: 2021 +// +// Anti-regression test for https://github.com/rust-lang/rust/issues/140602 +// where the generated warning couldn't be allowed due too being attached to +// the wrong AST node. + +#![deny(unsafe_attr_outside_unsafe)] + +#[allow(unsafe_attr_outside_unsafe)] +mod generated { + #[no_mangle] + fn _generated_foo() {} +} + +fn main() {} diff --git a/tests/ui/rustc-env/min-stack-banana.rs b/tests/ui/rustc-env/min-stack-banana.rs index abbb68437100f..dfde277714b9f 100644 --- a/tests/ui/rustc-env/min-stack-banana.rs +++ b/tests/ui/rustc-env/min-stack-banana.rs @@ -1,2 +1,5 @@ //@ rustc-env:RUST_MIN_STACK=banana + fn main() {} + +//~? ERROR `RUST_MIN_STACK` should be a number of bytes, but was "banana" diff --git a/tests/ui/rustc-error.rs b/tests/ui/rustc-error.rs deleted file mode 100644 index 69d57948fb5e7..0000000000000 --- a/tests/ui/rustc-error.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![feature(rustc_attrs)] - -#[rustc_error] -fn main() { - //~^ ERROR fatal error triggered by #[rustc_error] -} diff --git a/tests/ui/rustc-error.stderr b/tests/ui/rustc-error.stderr deleted file mode 100644 index 67451195b64f3..0000000000000 --- a/tests/ui/rustc-error.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fatal error triggered by #[rustc_error] - --> $DIR/rustc-error.rs:4:1 - | -LL | fn main() { - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/rustdoc/doc-alias-use-item-list-stem.rs b/tests/ui/rustdoc/doc-alias-use-item-list-stem.rs new file mode 100644 index 0000000000000..ef310843e2136 --- /dev/null +++ b/tests/ui/rustdoc/doc-alias-use-item-list-stem.rs @@ -0,0 +1,11 @@ +// Check that we don't ICE on `#[doc(alias)]`es placed on use items with list stems. +// issue: +//@ check-pass + +#[doc(alias = "empty")] +pub use {}; + +#[doc(alias = "id")] +pub use {std::convert::identity}; + +fn main() {} diff --git a/tests/ui/sanitizer/asan_odr_windows.rs b/tests/ui/sanitizer/asan_odr_windows.rs index c618ac02a66d8..28c2471676155 100644 --- a/tests/ui/sanitizer/asan_odr_windows.rs +++ b/tests/ui/sanitizer/asan_odr_windows.rs @@ -5,6 +5,8 @@ //@ compile-flags:-Zsanitizer=address //@ aux-build: asan_odr_win-2.rs //@ only-windows-msvc +//@ needs-sanitizer-support +//@ needs-sanitizer-address extern crate othercrate; diff --git a/tests/ui/sanitizer/cfi/assoc-ty-lifetime-issue-123053.rs b/tests/ui/sanitizer/cfi/assoc-ty-lifetime-issue-123053.rs index dd604b6bf7dcd..f4f383e008a08 100644 --- a/tests/ui/sanitizer/cfi/assoc-ty-lifetime-issue-123053.rs +++ b/tests/ui/sanitizer/cfi/assoc-ty-lifetime-issue-123053.rs @@ -2,7 +2,8 @@ // trait object type to fail, causing an ICE. // //@ needs-sanitizer-cfi -//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi --edition=2021 +//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ edition: 2021 //@ no-prefer-dynamic //@ only-x86_64-unknown-linux-gnu //@ build-pass diff --git a/tests/ui/sanitizer/cfi/canonical-jump-tables-requires-cfi.rs b/tests/ui/sanitizer/cfi/canonical-jump-tables-requires-cfi.rs index 10c5bf6ea5e14..36f6e3bc95e18 100644 --- a/tests/ui/sanitizer/cfi/canonical-jump-tables-requires-cfi.rs +++ b/tests/ui/sanitizer/cfi/canonical-jump-tables-requires-cfi.rs @@ -6,3 +6,5 @@ #![feature(no_core)] #![no_core] #![no_main] + +//~? ERROR `-Zsanitizer-cfi-canonical-jump-tables` requires `-Zsanitizer=cfi` diff --git a/tests/ui/sanitizer/cfi/coroutine.rs b/tests/ui/sanitizer/cfi/coroutine.rs index 3ad896afd00b0..39a754f103637 100644 --- a/tests/ui/sanitizer/cfi/coroutine.rs +++ b/tests/ui/sanitizer/cfi/coroutine.rs @@ -26,7 +26,7 @@ use std::async_iter::AsyncIterator; #[test] fn general_coroutine() { - let mut coro = #[coroutine] |x: i32| { + let coro = #[coroutine] |x: i32| { yield x; "done" }; diff --git a/tests/ui/sanitizer/cfi/generalize-pointers-requires-cfi.rs b/tests/ui/sanitizer/cfi/generalize-pointers-requires-cfi.rs index 8ba13bd3639b1..83277da528c96 100644 --- a/tests/ui/sanitizer/cfi/generalize-pointers-requires-cfi.rs +++ b/tests/ui/sanitizer/cfi/generalize-pointers-requires-cfi.rs @@ -7,3 +7,5 @@ #![feature(no_core)] #![no_core] #![no_main] + +//~? ERROR `-Zsanitizer-cfi-generalize-pointers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi` diff --git a/tests/ui/sanitizer/cfi/is-incompatible-with-kcfi.rs b/tests/ui/sanitizer/cfi/is-incompatible-with-kcfi.rs index c628709d7a1ce..db8d11616440d 100644 --- a/tests/ui/sanitizer/cfi/is-incompatible-with-kcfi.rs +++ b/tests/ui/sanitizer/cfi/is-incompatible-with-kcfi.rs @@ -10,3 +10,6 @@ #![feature(no_core)] #![no_core] #![no_main] + +//~? ERROR cfi sanitizer is not supported for this target +//~? ERROR `-Zsanitizer=cfi` is incompatible with `-Zsanitizer=kcfi` diff --git a/tests/ui/sanitizer/cfi/normalize-integers-requires-cfi.rs b/tests/ui/sanitizer/cfi/normalize-integers-requires-cfi.rs index a7ecefbf7efb7..b9d5b9623d5f0 100644 --- a/tests/ui/sanitizer/cfi/normalize-integers-requires-cfi.rs +++ b/tests/ui/sanitizer/cfi/normalize-integers-requires-cfi.rs @@ -7,3 +7,5 @@ #![feature(no_core)] #![no_core] #![no_main] + +//~? ERROR `-Zsanitizer-cfi-normalize-integers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi` diff --git a/tests/ui/sanitizer/cfi/requires-lto.rs b/tests/ui/sanitizer/cfi/requires-lto.rs index 5a34f696e0546..db83f5f6bf020 100644 --- a/tests/ui/sanitizer/cfi/requires-lto.rs +++ b/tests/ui/sanitizer/cfi/requires-lto.rs @@ -6,3 +6,5 @@ #![feature(no_core)] #![no_core] #![no_main] + +//~? ERROR `-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto` diff --git a/tests/ui/sanitizer/cfi/with-rustc-lto-requires-single-codegen-unit.rs b/tests/ui/sanitizer/cfi/with-rustc-lto-requires-single-codegen-unit.rs index 954e4ec3b8538..4ef5b6756a495 100644 --- a/tests/ui/sanitizer/cfi/with-rustc-lto-requires-single-codegen-unit.rs +++ b/tests/ui/sanitizer/cfi/with-rustc-lto-requires-single-codegen-unit.rs @@ -6,3 +6,5 @@ #![feature(no_core)] #![no_core] #![no_main] + +//~? ERROR `-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1` diff --git a/tests/ui/sanitizer/crt-static.rs b/tests/ui/sanitizer/crt-static.rs index c24faeca3dc85..b8bdf28351c3d 100644 --- a/tests/ui/sanitizer/crt-static.rs +++ b/tests/ui/sanitizer/crt-static.rs @@ -4,3 +4,5 @@ #![feature(no_core)] #![no_core] #![no_main] + +//~? ERROR sanitizer is incompatible with statically linked libc diff --git a/tests/ui/sanitizer/incompatible.rs b/tests/ui/sanitizer/incompatible.rs index d000abb26ac74..c706a5a2e4e7b 100644 --- a/tests/ui/sanitizer/incompatible.rs +++ b/tests/ui/sanitizer/incompatible.rs @@ -1,7 +1,8 @@ //@ compile-flags: -Z sanitizer=address -Z sanitizer=memory --target x86_64-unknown-linux-gnu //@ needs-llvm-components: x86 -//@ error-pattern: error: `-Zsanitizer=address` is incompatible with `-Zsanitizer=memory` #![feature(no_core)] #![no_core] #![no_main] + +//~? ERROR `-Zsanitizer=address` is incompatible with `-Zsanitizer=memory` diff --git a/tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs b/tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs index e5b1e0322575c..7d0c73c2841e8 100644 --- a/tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs +++ b/tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs @@ -2,7 +2,8 @@ // encode_ty and caused the compiler to ICE. // //@ needs-sanitizer-cfi -//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi --edition=2021 +//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ edition: 2021 //@ no-prefer-dynamic //@ only-x86_64-unknown-linux-gnu //@ build-pass diff --git a/tests/ui/sanitizer/kcfi-arity-requires-kcfi.rs b/tests/ui/sanitizer/kcfi-arity-requires-kcfi.rs new file mode 100644 index 0000000000000..12aabb3b86236 --- /dev/null +++ b/tests/ui/sanitizer/kcfi-arity-requires-kcfi.rs @@ -0,0 +1,9 @@ +// Verifies that `-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi`. +// +//@ needs-sanitizer-kcfi +//@ compile-flags: -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-kcfi-arity + +//~? ERROR `-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi` +#![feature(no_core)] +#![no_core] +#![no_main] diff --git a/tests/ui/sanitizer/kcfi-arity-requires-kcfi.stderr b/tests/ui/sanitizer/kcfi-arity-requires-kcfi.stderr new file mode 100644 index 0000000000000..4ed1b754fd431 --- /dev/null +++ b/tests/ui/sanitizer/kcfi-arity-requires-kcfi.stderr @@ -0,0 +1,4 @@ +error: `-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi` + +error: aborting due to 1 previous error + diff --git a/tests/ui/sanitizer/kcfi-arity-requires-llvm-21-0-0.rs b/tests/ui/sanitizer/kcfi-arity-requires-llvm-21-0-0.rs new file mode 100644 index 0000000000000..8a724b853e13a --- /dev/null +++ b/tests/ui/sanitizer/kcfi-arity-requires-llvm-21-0-0.rs @@ -0,0 +1,11 @@ +// Verifies that `-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later. +// +//@ needs-sanitizer-kcfi +//@ compile-flags: -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Cpanic=abort -Zsanitizer=kcfi -Zsanitizer-kcfi-arity +//@ build-fail +//@ max-llvm-major-version: 20 + +//~? ERROR `-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later. +#![feature(no_core)] +#![no_core] +#![no_main] diff --git a/tests/ui/sanitizer/kcfi-arity-requires-llvm-21-0-0.stderr b/tests/ui/sanitizer/kcfi-arity-requires-llvm-21-0-0.stderr new file mode 100644 index 0000000000000..ac6bd7411fd81 --- /dev/null +++ b/tests/ui/sanitizer/kcfi-arity-requires-llvm-21-0-0.stderr @@ -0,0 +1,4 @@ +error: `-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later. + +error: aborting due to 1 previous error + diff --git a/tests/ui/sanitizer/split-lto-unit-requires-lto.rs b/tests/ui/sanitizer/split-lto-unit-requires-lto.rs index 35e610f03076b..1d08ca7423fd6 100644 --- a/tests/ui/sanitizer/split-lto-unit-requires-lto.rs +++ b/tests/ui/sanitizer/split-lto-unit-requires-lto.rs @@ -6,3 +6,5 @@ #![feature(no_core)] #![no_core] #![no_main] + +//~? ERROR `-Zsplit-lto-unit` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto` diff --git a/tests/ui/sanitizer/unsupported-target.rs b/tests/ui/sanitizer/unsupported-target.rs index 7c7dc24b5d9a5..14925548e9271 100644 --- a/tests/ui/sanitizer/unsupported-target.rs +++ b/tests/ui/sanitizer/unsupported-target.rs @@ -1,6 +1,8 @@ //@ compile-flags: -Z sanitizer=leak --target i686-unknown-linux-gnu //@ needs-llvm-components: x86 -//@ error-pattern: error: leak sanitizer is not supported for this target + #![feature(no_core)] #![no_core] #![no_main] + +//~? ERROR leak sanitizer is not supported for this target diff --git a/tests/ui/self/arbitrary-self-opaque.rs b/tests/ui/self/arbitrary-self-opaque.rs index c26ef658b695f..b176a982e5fcc 100644 --- a/tests/ui/self/arbitrary-self-opaque.rs +++ b/tests/ui/self/arbitrary-self-opaque.rs @@ -4,6 +4,7 @@ struct Foo; type Bar = impl Sized; impl Foo { + #[define_opaque(Bar)] fn foo(self: Bar) {} //~^ ERROR: invalid `self` parameter type: `Bar` //~| ERROR: item does not constrain diff --git a/tests/ui/self/arbitrary-self-opaque.stderr b/tests/ui/self/arbitrary-self-opaque.stderr index c75165d9f8e27..36ae3d6fd02bb 100644 --- a/tests/ui/self/arbitrary-self-opaque.stderr +++ b/tests/ui/self/arbitrary-self-opaque.stderr @@ -1,5 +1,5 @@ error[E0307]: invalid `self` parameter type: `Bar` - --> $DIR/arbitrary-self-opaque.rs:7:18 + --> $DIR/arbitrary-self-opaque.rs:8:18 | LL | fn foo(self: Bar) {} | ^^^ @@ -7,14 +7,14 @@ LL | fn foo(self: Bar) {} = note: type of `self` must be `Self` or a type that dereferences to it = 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: item does not constrain `Bar::{opaque#0}`, but has it in its signature - --> $DIR/arbitrary-self-opaque.rs:7:8 +error: item does not constrain `Bar::{opaque#0}` + --> $DIR/arbitrary-self-opaque.rs:8:8 | LL | fn foo(self: Bar) {} | ^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/arbitrary-self-opaque.rs:4:12 | LL | type Bar = impl Sized; diff --git a/tests/ui/self/arbitrary_self_type_infinite_recursion.rs b/tests/ui/self/arbitrary_self_type_infinite_recursion.rs index 6fbf35c0b867b..79ca7079c421e 100644 --- a/tests/ui/self/arbitrary_self_type_infinite_recursion.rs +++ b/tests/ui/self/arbitrary_self_type_infinite_recursion.rs @@ -10,15 +10,15 @@ struct Content; impl Content { fn method(self: MySmartPtr) { // note self type - //~^ reached the recursion limit - //~| reached the recursion limit - //~| invalid `self` parameter type + //~^ ERROR reached the recursion limit + //~| ERROR reached the recursion limit + //~| ERROR invalid `self` parameter type } } fn main() { let p = MySmartPtr(Content); p.method(); - //~^ reached the recursion limit - //~| no method named `method` + //~^ ERROR reached the recursion limit + //~| ERROR no method named `method` } diff --git a/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.rs b/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.rs index b5a8992542bf4..2fb7aed970e5b 100644 --- a/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.rs +++ b/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.rs @@ -6,16 +6,16 @@ struct Foo; impl Foo { async fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f } - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough async fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } type Alias = Pin; impl Foo { async fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn main() {} diff --git a/tests/ui/self/dyn-dispatch-do-not-mix-normalized-and-unnormalized.rs b/tests/ui/self/dyn-dispatch-do-not-mix-normalized-and-unnormalized.rs new file mode 100644 index 0000000000000..2bc70de2a340d --- /dev/null +++ b/tests/ui/self/dyn-dispatch-do-not-mix-normalized-and-unnormalized.rs @@ -0,0 +1,26 @@ +//@ check-pass + +// Regression test for . + +// Previously, we'd take the normalized param env's clauses which included +// `::Value = i32`, which comes from the supertraits of `TraitD` +// after normalizing `::Value = ::Scalar`. Since +// `normalize_param_env_or_error` ends up re-elaborating `PF: TraitD`, we'd +// end up with both versions of this predicate (normalized and unnormalized). +// Since these projections preds are not equal, we'd fail with ambiguity. + +trait TraitB {} + +trait TraitC: TraitB { + type Value; +} + +trait TraitD: TraitC { + type Scalar; +} + +trait TraitE { + fn apply>(&self); +} + +fn main() {} diff --git a/tests/ui/self/dyn-dispatch-requires-supertrait-norm.rs b/tests/ui/self/dyn-dispatch-requires-supertrait-norm.rs new file mode 100644 index 0000000000000..55c070eb03682 --- /dev/null +++ b/tests/ui/self/dyn-dispatch-requires-supertrait-norm.rs @@ -0,0 +1,38 @@ +//@ check-pass + +#![feature(derive_coerce_pointee)] +#![feature(arbitrary_self_types)] + +use std::ops::Deref; +use std::marker::CoercePointee; +use std::sync::Arc; + +trait MyTrait {} + +#[derive(CoercePointee)] +#[repr(transparent)] +struct MyArc>(Arc); + +impl> Deref for MyArc { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } +} + +trait Mirror { + type Assoc; +} +impl Mirror for T { + type Assoc = T; +} + +// This is variant on "tests/ui/self/dyn-dispatch-requires-supertrait.rs" but with +// a supertrait that requires normalization to match the pred in the old solver. +trait MyOtherTrait: MyTrait<::Assoc> { + fn foo(self: MyArc); +} + +fn test(_: MyArc) {} + +fn main() {} diff --git a/tests/ui/self/dyn-dispatch-requires-supertrait.rs b/tests/ui/self/dyn-dispatch-requires-supertrait.rs new file mode 100644 index 0000000000000..f2661c406fef0 --- /dev/null +++ b/tests/ui/self/dyn-dispatch-requires-supertrait.rs @@ -0,0 +1,38 @@ +//@ check-pass + +#![feature(derive_coerce_pointee)] +#![feature(arbitrary_self_types)] + +use std::ops::Deref; +use std::marker::CoercePointee; +use std::sync::Arc; + +trait MyTrait {} + +#[derive(CoercePointee)] +#[repr(transparent)] +struct MyArc +where + T: MyTrait + ?Sized, +{ + inner: Arc +} + +impl Deref for MyArc { + type Target = T; + fn deref(&self) -> &T { + &self.inner + } +} + +// Proving that `MyArc` is dyn-dispatchable requires proving `MyArc` implements +// `DispatchFromDyn>`. The `DispatchFromDyn` impl that is generated from the +// `CoercePointee` implementation requires the pointee impls `MyTrait`, but previously we +// were only assuming the pointee impl'd `MyOtherTrait`. Elaboration comes to the rescue here. +trait MyOtherTrait: MyTrait { + fn foo(self: MyArc); +} + +fn test(_: MyArc) {} + +fn main() {} diff --git a/tests/ui/simd/array-trait.stderr b/tests/ui/simd/array-trait.stderr index 2d2a11f25ade0..299f0ad96aee8 100644 --- a/tests/ui/simd/array-trait.stderr +++ b/tests/ui/simd/array-trait.stderr @@ -22,8 +22,6 @@ LL | #[derive(Copy, Clone)] | ----- in this derive macro expansion LL | pub struct T([S::Lane; S::SIZE]); | ^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/simd/const-err-trumps-simd-err.rs b/tests/ui/simd/const-err-trumps-simd-err.rs index fb87fe1cbca83..05a224d037ac4 100644 --- a/tests/ui/simd/const-err-trumps-simd-err.rs +++ b/tests/ui/simd/const-err-trumps-simd-err.rs @@ -1,4 +1,6 @@ //@build-fail +//@ dont-require-annotations: NOTE + //! Make sure that monomorphization-time const errors from `static_assert` take priority over the //! error from simd_extract. Basically this checks that if a const fails to evaluate in some //! function, we don't bother codegen'ing the function. @@ -15,7 +17,7 @@ struct int8x4_t([u8; 4]); fn get_elem(a: int8x4_t) -> u8 { const { assert!(LANE < 4); } // the error should be here... //~^ ERROR failed - //~| assertion failed + //~| NOTE assertion failed unsafe { simd_extract(a, LANE) } // ...not here } diff --git a/tests/ui/simd/const-err-trumps-simd-err.stderr b/tests/ui/simd/const-err-trumps-simd-err.stderr index e88c277885e35..389f75492c645 100644 --- a/tests/ui/simd/const-err-trumps-simd-err.stderr +++ b/tests/ui/simd/const-err-trumps-simd-err.stderr @@ -1,19 +1,17 @@ error[E0080]: evaluation of `get_elem::<4>::{constant#0}` failed - --> $DIR/const-err-trumps-simd-err.rs:16:13 + --> $DIR/const-err-trumps-simd-err.rs:18:13 | LL | const { assert!(LANE < 4); } // the error should be here... | ^^^^^^^^^^^^^^^^^ evaluation panicked: assertion failed: LANE < 4 - | - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/const-err-trumps-simd-err.rs:16:5 + --> $DIR/const-err-trumps-simd-err.rs:18:5 | LL | const { assert!(LANE < 4); } // the error should be here... | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn get_elem::<4>` - --> $DIR/const-err-trumps-simd-err.rs:23:5 + --> $DIR/const-err-trumps-simd-err.rs:25:5 | LL | get_elem::<4>(int8x4_t([0, 0, 0, 0])); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/simd/empty-simd-vector-in-operand.rs b/tests/ui/simd/empty-simd-vector-in-operand.rs new file mode 100644 index 0000000000000..813222c8a2ed5 --- /dev/null +++ b/tests/ui/simd/empty-simd-vector-in-operand.rs @@ -0,0 +1,15 @@ +// Regression test for issue #134224. +//@ only-x86_64 + +#![feature(repr_simd)] + +#[repr(simd)] +struct A(); +//~^ ERROR SIMD vector cannot be empty + +fn main() { + unsafe { + std::arch::asm!("{}", in(xmm_reg) A()); + //~^ ERROR use of empty SIMD vector `A` + } +} diff --git a/tests/ui/simd/empty-simd-vector-in-operand.stderr b/tests/ui/simd/empty-simd-vector-in-operand.stderr new file mode 100644 index 0000000000000..7210dddd461f0 --- /dev/null +++ b/tests/ui/simd/empty-simd-vector-in-operand.stderr @@ -0,0 +1,15 @@ +error[E0075]: SIMD vector cannot be empty + --> $DIR/empty-simd-vector-in-operand.rs:7:1 + | +LL | struct A(); + | ^^^^^^^^ + +error: use of empty SIMD vector `A` + --> $DIR/empty-simd-vector-in-operand.rs:12:43 + | +LL | std::arch::asm!("{}", in(xmm_reg) A()); + | ^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0075`. diff --git a/tests/ui/simd/intrinsic/generic-comparison-pass.rs b/tests/ui/simd/intrinsic/generic-comparison-pass.rs index 2ee164cdfd800..50a05eecb03b3 100644 --- a/tests/ui/simd/intrinsic/generic-comparison-pass.rs +++ b/tests/ui/simd/intrinsic/generic-comparison-pass.rs @@ -1,6 +1,6 @@ //@ run-pass -#![feature(repr_simd, core_intrinsics, concat_idents)] +#![feature(repr_simd, core_intrinsics, macro_metavar_expr_concat)] #![allow(non_camel_case_types)] use std::intrinsics::simd::{simd_eq, simd_ge, simd_gt, simd_le, simd_lt, simd_ne}; @@ -19,7 +19,7 @@ macro_rules! cmp { ($method: ident($lhs: expr, $rhs: expr)) => {{ let lhs = $lhs; let rhs = $rhs; - let e: u32x4 = concat_idents!(simd_, $method)($lhs, $rhs); + let e: u32x4 = ${concat(simd_, $method)}($lhs, $rhs); // assume the scalar version is correct/the behaviour we want. assert!((e.0[0] != 0) == lhs.0[0].$method(&rhs.0[0])); assert!((e.0[1] != 0) == lhs.0[1].$method(&rhs.0[1])); diff --git a/tests/ui/simd/intrinsic/generic-elements-pass.rs b/tests/ui/simd/intrinsic/generic-elements-pass.rs index 4dc2e4d5a80a9..e4d47cdb38184 100644 --- a/tests/ui/simd/intrinsic/generic-elements-pass.rs +++ b/tests/ui/simd/intrinsic/generic-elements-pass.rs @@ -1,8 +1,10 @@ //@ run-pass -#![feature(repr_simd, core_intrinsics)] +#![feature(repr_simd, intrinsics, core_intrinsics)] -use std::intrinsics::simd::{simd_extract, simd_insert, simd_shuffle}; +use std::intrinsics::simd::{ + simd_extract, simd_extract_dyn, simd_insert, simd_insert_dyn, simd_shuffle, +}; #[repr(simd)] #[derive(Copy, Clone, Debug, PartialEq)] @@ -70,6 +72,41 @@ fn main() { all_eq!(simd_extract(x8, 6), 86); all_eq!(simd_extract(x8, 7), 87); } + unsafe { + all_eq!(simd_insert_dyn(x2, 0, 100), i32x2([100, 21])); + all_eq!(simd_insert_dyn(x2, 1, 100), i32x2([20, 100])); + + all_eq!(simd_insert_dyn(x4, 0, 100), i32x4([100, 41, 42, 43])); + all_eq!(simd_insert_dyn(x4, 1, 100), i32x4([40, 100, 42, 43])); + all_eq!(simd_insert_dyn(x4, 2, 100), i32x4([40, 41, 100, 43])); + all_eq!(simd_insert_dyn(x4, 3, 100), i32x4([40, 41, 42, 100])); + + all_eq!(simd_insert_dyn(x8, 0, 100), i32x8([100, 81, 82, 83, 84, 85, 86, 87])); + all_eq!(simd_insert_dyn(x8, 1, 100), i32x8([80, 100, 82, 83, 84, 85, 86, 87])); + all_eq!(simd_insert_dyn(x8, 2, 100), i32x8([80, 81, 100, 83, 84, 85, 86, 87])); + all_eq!(simd_insert_dyn(x8, 3, 100), i32x8([80, 81, 82, 100, 84, 85, 86, 87])); + all_eq!(simd_insert_dyn(x8, 4, 100), i32x8([80, 81, 82, 83, 100, 85, 86, 87])); + all_eq!(simd_insert_dyn(x8, 5, 100), i32x8([80, 81, 82, 83, 84, 100, 86, 87])); + all_eq!(simd_insert_dyn(x8, 6, 100), i32x8([80, 81, 82, 83, 84, 85, 100, 87])); + all_eq!(simd_insert_dyn(x8, 7, 100), i32x8([80, 81, 82, 83, 84, 85, 86, 100])); + + all_eq!(simd_extract_dyn(x2, 0), 20); + all_eq!(simd_extract_dyn(x2, 1), 21); + + all_eq!(simd_extract_dyn(x4, 0), 40); + all_eq!(simd_extract_dyn(x4, 1), 41); + all_eq!(simd_extract_dyn(x4, 2), 42); + all_eq!(simd_extract_dyn(x4, 3), 43); + + all_eq!(simd_extract_dyn(x8, 0), 80); + all_eq!(simd_extract_dyn(x8, 1), 81); + all_eq!(simd_extract_dyn(x8, 2), 82); + all_eq!(simd_extract_dyn(x8, 3), 83); + all_eq!(simd_extract_dyn(x8, 4), 84); + all_eq!(simd_extract_dyn(x8, 5), 85); + all_eq!(simd_extract_dyn(x8, 6), 86); + all_eq!(simd_extract_dyn(x8, 7), 87); + } let y2 = i32x2([120, 121]); let y4 = i32x4([140, 141, 142, 143]); diff --git a/tests/ui/simd/intrinsic/generic-gather-pass.rs b/tests/ui/simd/intrinsic/generic-gather-scatter-pass.rs similarity index 100% rename from tests/ui/simd/intrinsic/generic-gather-pass.rs rename to tests/ui/simd/intrinsic/generic-gather-scatter-pass.rs diff --git a/tests/ui/simd/intrinsic/generic-gather-scatter.rs b/tests/ui/simd/intrinsic/generic-gather-scatter.rs new file mode 100644 index 0000000000000..c1de7fd1c72eb --- /dev/null +++ b/tests/ui/simd/intrinsic/generic-gather-scatter.rs @@ -0,0 +1,45 @@ +//@ build-fail +//@ ignore-emscripten + +// Test that the simd_{gather,scatter} intrinsics produce ok-ish error +// messages when misused. + +#![feature(repr_simd, core_intrinsics)] +#![allow(non_camel_case_types)] + +use std::intrinsics::simd::{simd_gather, simd_scatter}; + +#[repr(simd)] +#[derive(Copy, Clone, PartialEq, Debug)] +struct x4(pub [T; 4]); + +fn main() { + let mut x = [0_f32, 1., 2., 3., 4., 5., 6., 7.]; + + let default = x4([-3_f32, -3., -3., -3.]); + let s_strided = x4([0_f32, 2., -3., 6.]); + + let mask = x4([-1_i32, -1, 0, -1]); + let fmask = x4([0_f32; 4]); + + let pointer = x.as_mut_ptr(); + let pointers = + unsafe { x4([pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6)]) }; + + unsafe { + simd_gather(default, mask, mask); + //~^ ERROR expected element type `i32` of second argument `x4` to be a pointer to the element type `f32` + + simd_gather(default, pointers, fmask); + //~^ ERROR expected mask element type to be an integer, found `f32` + } + + unsafe { + let values = x4([42_f32, 43_f32, 44_f32, 45_f32]); + simd_scatter(values, mask, mask); + //~^ ERROR expected element type `i32` of second argument `x4` to be a pointer to the element type `f32` of the first argument `x4`, found `i32` != `*mut f32` + + simd_scatter(values, pointers, fmask); + //~^ ERROR expected mask element type to be an integer, found `f32` + } +} diff --git a/tests/ui/simd/intrinsic/generic-gather-scatter.stderr b/tests/ui/simd/intrinsic/generic-gather-scatter.stderr new file mode 100644 index 0000000000000..e1806671564b4 --- /dev/null +++ b/tests/ui/simd/intrinsic/generic-gather-scatter.stderr @@ -0,0 +1,27 @@ +error[E0511]: invalid monomorphization of `simd_gather` intrinsic: expected element type `i32` of second argument `x4` to be a pointer to the element type `f32` of the first argument `x4`, found `i32` != `*_ f32` + --> $DIR/generic-gather-scatter.rs:30:9 + | +LL | simd_gather(default, mask, mask); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0511]: invalid monomorphization of `simd_gather` intrinsic: expected mask element type to be an integer, found `f32` + --> $DIR/generic-gather-scatter.rs:33:9 + | +LL | simd_gather(default, pointers, fmask); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0511]: invalid monomorphization of `simd_scatter` intrinsic: expected element type `i32` of second argument `x4` to be a pointer to the element type `f32` of the first argument `x4`, found `i32` != `*mut f32` + --> $DIR/generic-gather-scatter.rs:39:9 + | +LL | simd_scatter(values, mask, mask); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0511]: invalid monomorphization of `simd_scatter` intrinsic: expected mask element type to be an integer, found `f32` + --> $DIR/generic-gather-scatter.rs:42:9 + | +LL | simd_scatter(values, pointers, fmask); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0511`. diff --git a/tests/ui/simd/intrinsic/generic-gather.rs b/tests/ui/simd/intrinsic/generic-gather.rs deleted file mode 100644 index 118d802948305..0000000000000 --- a/tests/ui/simd/intrinsic/generic-gather.rs +++ /dev/null @@ -1,52 +0,0 @@ -//@ build-fail -//@ ignore-emscripten - -// Test that the simd_{gather,scatter} intrinsics produce ok-ish error -// messages when misused. - -#![feature(repr_simd, core_intrinsics)] -#![allow(non_camel_case_types)] - -use std::intrinsics::simd::{simd_gather, simd_scatter}; - -#[repr(simd)] -#[derive(Copy, Clone, PartialEq, Debug)] -struct x4(pub [T; 4]); - -fn main() { - let mut x = [0_f32, 1., 2., 3., 4., 5., 6., 7.]; - - let default = x4([-3_f32, -3., -3., -3.]); - let s_strided = x4([0_f32, 2., -3., 6.]); - - let mask = x4([-1_i32, -1, 0, -1]); - let umask = x4([0u16; 4]); - let fmask = x4([0_f32; 4]); - - let pointer = x.as_mut_ptr(); - let pointers = - unsafe { x4([pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6)]) }; - - unsafe { - simd_gather(default, mask, mask); - //~^ ERROR expected element type `i32` of second argument `x4` to be a pointer to the element type `f32` - - simd_gather(default, pointers, umask); - //~^ ERROR expected element type `u16` of third argument `x4` to be a signed integer type - - simd_gather(default, pointers, fmask); - //~^ ERROR expected element type `f32` of third argument `x4` to be a signed integer type - } - - unsafe { - let values = x4([42_f32, 43_f32, 44_f32, 45_f32]); - simd_scatter(values, mask, mask); - //~^ ERROR expected element type `i32` of second argument `x4` to be a pointer to the element type `f32` of the first argument `x4`, found `i32` != `*mut f32` - - simd_scatter(values, pointers, umask); - //~^ ERROR expected element type `u16` of third argument `x4` to be a signed integer type - - simd_scatter(values, pointers, fmask); - //~^ ERROR expected element type `f32` of third argument `x4` to be a signed integer type - } -} diff --git a/tests/ui/simd/intrinsic/generic-gather.stderr b/tests/ui/simd/intrinsic/generic-gather.stderr deleted file mode 100644 index 81e10fc987523..0000000000000 --- a/tests/ui/simd/intrinsic/generic-gather.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error[E0511]: invalid monomorphization of `simd_gather` intrinsic: expected element type `i32` of second argument `x4` to be a pointer to the element type `f32` of the first argument `x4`, found `i32` != `*_ f32` - --> $DIR/generic-gather.rs:31:9 - | -LL | simd_gather(default, mask, mask); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0511]: invalid monomorphization of `simd_gather` intrinsic: expected element type `u16` of third argument `x4` to be a signed integer type - --> $DIR/generic-gather.rs:34:9 - | -LL | simd_gather(default, pointers, umask); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0511]: invalid monomorphization of `simd_gather` intrinsic: expected element type `f32` of third argument `x4` to be a signed integer type - --> $DIR/generic-gather.rs:37:9 - | -LL | simd_gather(default, pointers, fmask); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0511]: invalid monomorphization of `simd_scatter` intrinsic: expected element type `i32` of second argument `x4` to be a pointer to the element type `f32` of the first argument `x4`, found `i32` != `*mut f32` - --> $DIR/generic-gather.rs:43:9 - | -LL | simd_scatter(values, mask, mask); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0511]: invalid monomorphization of `simd_scatter` intrinsic: expected element type `u16` of third argument `x4` to be a signed integer type - --> $DIR/generic-gather.rs:46:9 - | -LL | simd_scatter(values, pointers, umask); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0511]: invalid monomorphization of `simd_scatter` intrinsic: expected element type `f32` of third argument `x4` to be a signed integer type - --> $DIR/generic-gather.rs:49:9 - | -LL | simd_scatter(values, pointers, fmask); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 6 previous errors - -For more information about this error, try `rustc --explain E0511`. diff --git a/tests/ui/simd/intrinsic/generic-select.rs b/tests/ui/simd/intrinsic/generic-select.rs index db14032f1f204..924938fd01a62 100644 --- a/tests/ui/simd/intrinsic/generic-select.rs +++ b/tests/ui/simd/intrinsic/generic-select.rs @@ -36,11 +36,8 @@ fn main() { simd_select(m8, x, x); //~^ ERROR mismatched lengths: mask length `8` != other vector length `4` - simd_select(x, x, x); - //~^ ERROR mask element type is `u32`, expected a signed integer type - simd_select(z, z, z); - //~^ ERROR mask element type is `f32`, expected a signed integer type + //~^ ERROR expected mask element type to be an integer, found `f32` simd_select(m4, 0u32, 1u32); //~^ ERROR found non-SIMD `u32` diff --git a/tests/ui/simd/intrinsic/generic-select.stderr b/tests/ui/simd/intrinsic/generic-select.stderr index b9af86515fdef..36b56adcfa65a 100644 --- a/tests/ui/simd/intrinsic/generic-select.stderr +++ b/tests/ui/simd/intrinsic/generic-select.stderr @@ -4,52 +4,42 @@ error[E0511]: invalid monomorphization of `simd_select` intrinsic: mismatched le LL | simd_select(m8, x, x); | ^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `simd_select` intrinsic: found mask element type is `u32`, expected a signed integer type +error[E0511]: invalid monomorphization of `simd_select` intrinsic: expected mask element type to be an integer, found `f32` --> $DIR/generic-select.rs:39:9 | -LL | simd_select(x, x, x); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: the mask may be widened, which only has the correct behavior for signed integers - -error[E0511]: invalid monomorphization of `simd_select` intrinsic: found mask element type is `f32`, expected a signed integer type - --> $DIR/generic-select.rs:42:9 - | LL | simd_select(z, z, z); | ^^^^^^^^^^^^^^^^^^^^ - | - = note: the mask may be widened, which only has the correct behavior for signed integers error[E0511]: invalid monomorphization of `simd_select` intrinsic: expected SIMD argument type, found non-SIMD `u32` - --> $DIR/generic-select.rs:45:9 + --> $DIR/generic-select.rs:42:9 | LL | simd_select(m4, 0u32, 1u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: invalid bitmask `u16`, expected `u8` or `[u8; 1]` - --> $DIR/generic-select.rs:48:9 + --> $DIR/generic-select.rs:45:9 | LL | simd_select_bitmask(0u16, x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: expected SIMD argument type, found non-SIMD `u32` - --> $DIR/generic-select.rs:51:9 + --> $DIR/generic-select.rs:48:9 | LL | simd_select_bitmask(0u8, 1u32, 2u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: invalid bitmask `f32`, expected `u8` or `[u8; 1]` - --> $DIR/generic-select.rs:54:9 + --> $DIR/generic-select.rs:51:9 | LL | simd_select_bitmask(0.0f32, x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: invalid bitmask `&str`, expected `u8` or `[u8; 1]` - --> $DIR/generic-select.rs:57:9 + --> $DIR/generic-select.rs:54:9 | LL | simd_select_bitmask("x", x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0511`. diff --git a/tests/ui/simd/masked-load-store-build-fail.rs b/tests/ui/simd/masked-load-store-build-fail.rs index ad2de5561033f..4b6cc17683c2c 100644 --- a/tests/ui/simd/masked-load-store-build-fail.rs +++ b/tests/ui/simd/masked-load-store-build-fail.rs @@ -21,8 +21,8 @@ fn main() { simd_masked_load(Simd::([-1, 0, -1, -1]), arr.as_ptr(), Simd::([9; 4])); //~^ ERROR expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd`, found `u32` != `*_ u32` - simd_masked_load(Simd::([1, 0, 1, 1]), arr.as_ptr(), default); - //~^ ERROR expected element type `u8` of third argument `Simd` to be a signed integer type + simd_masked_load(Simd::([1.0, 0.0, 1.0, 1.0]), arr.as_ptr(), default); + //~^ ERROR expected mask element type to be an integer, found `f32` simd_masked_store(Simd([-1i8; 4]), arr.as_ptr(), Simd([5u32; 4])); //~^ ERROR expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd`, found `u32` != `*mut u32` @@ -33,7 +33,7 @@ fn main() { simd_masked_store(Simd([-1i8; 4]), arr.as_mut_ptr(), Simd([5u8; 2])); //~^ ERROR expected third argument with length 4 (same as input type `Simd`), found `Simd` with length 2 - simd_masked_store(Simd([1u32; 4]), arr.as_mut_ptr(), Simd([5u8; 4])); - //~^ ERROR expected element type `u8` of third argument `Simd` to be a signed integer type + simd_masked_store(Simd([1f32; 4]), arr.as_mut_ptr(), Simd([5u8; 4])); + //~^ ERROR expected mask element type to be an integer, found `f32` } } diff --git a/tests/ui/simd/masked-load-store-build-fail.stderr b/tests/ui/simd/masked-load-store-build-fail.stderr index d57e0aa539f58..7f09841b59710 100644 --- a/tests/ui/simd/masked-load-store-build-fail.stderr +++ b/tests/ui/simd/masked-load-store-build-fail.stderr @@ -16,11 +16,11 @@ error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected LL | simd_masked_load(Simd::([-1, 0, -1, -1]), arr.as_ptr(), Simd::([9; 4])); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected element type `u8` of third argument `Simd` to be a signed integer type +error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected mask element type to be an integer, found `f32` --> $DIR/masked-load-store-build-fail.rs:24:9 | -LL | simd_masked_load(Simd::([1, 0, 1, 1]), arr.as_ptr(), default); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | simd_masked_load(Simd::([1.0, 0.0, 1.0, 1.0]), arr.as_ptr(), default); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd`, found `u32` != `*mut u32` --> $DIR/masked-load-store-build-fail.rs:27:9 @@ -40,10 +40,10 @@ error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expecte LL | simd_masked_store(Simd([-1i8; 4]), arr.as_mut_ptr(), Simd([5u8; 2])); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected element type `u8` of third argument `Simd` to be a signed integer type +error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected mask element type to be an integer, found `f32` --> $DIR/masked-load-store-build-fail.rs:36:9 | -LL | simd_masked_store(Simd([1u32; 4]), arr.as_mut_ptr(), Simd([5u8; 4])); +LL | simd_masked_store(Simd([1f32; 4]), arr.as_mut_ptr(), Simd([5u8; 4])); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 8 previous errors diff --git a/tests/ui/simd/monomorphize-shuffle-index.rs b/tests/ui/simd/monomorphize-shuffle-index.rs index 3a074dfd432c2..a56f2ea14520c 100644 --- a/tests/ui/simd/monomorphize-shuffle-index.rs +++ b/tests/ui/simd/monomorphize-shuffle-index.rs @@ -34,7 +34,7 @@ trait Shuffle { return simd_shuffle(a, b, Self::I); #[cfg(generic)] return simd_shuffle_const_generic::<_, _, { &Self::I.0 }>(a, b); - //[generic]~^ overly complex generic constant + //[generic]~^ ERROR overly complex generic constant #[cfg(generic_with_fn)] return simd_shuffle_const_generic::<_, _, { Self::J }>(a, b); } diff --git a/tests/ui/simd/monomorphize-too-long.rs b/tests/ui/simd/monomorphize-too-long.rs index 4bcde782292d6..4fac987b0b5aa 100644 --- a/tests/ui/simd/monomorphize-too-long.rs +++ b/tests/ui/simd/monomorphize-too-long.rs @@ -1,5 +1,4 @@ //@ build-fail -//@ error-pattern: monomorphising SIMD type `Simd` of length greater than 32768 #![feature(repr_simd)] @@ -9,3 +8,5 @@ struct Simd([T; N]); fn main() { let _too_big = Simd([1_u16; 54321]); } + +//~? ERROR monomorphising SIMD type `Simd` of length greater than 32768 diff --git a/tests/ui/simd/monomorphize-zero-length.rs b/tests/ui/simd/monomorphize-zero-length.rs index 44b4cfc0bcfab..d38870c572d97 100644 --- a/tests/ui/simd/monomorphize-zero-length.rs +++ b/tests/ui/simd/monomorphize-zero-length.rs @@ -1,5 +1,4 @@ //@ build-fail -//@ error-pattern: monomorphising SIMD type `Simd` of zero length #![feature(repr_simd)] @@ -9,3 +8,5 @@ struct Simd([T; N]); fn main() { let _empty = Simd([1.0; 0]); } + +//~? ERROR monomorphising SIMD type `Simd` of zero length diff --git a/tests/ui/simd/target-feature-mixup.rs b/tests/ui/simd/target-feature-mixup.rs index 2786251c7951b..77f1861524870 100644 --- a/tests/ui/simd/target-feature-mixup.rs +++ b/tests/ui/simd/target-feature-mixup.rs @@ -7,7 +7,6 @@ //@ ignore-fuchsia must translate zircon signal to SIGILL, FIXME (#58590) #![feature(repr_simd, target_feature, cfg_target_feature)] -#![feature(avx512_target_feature)] use std::process::{Command, ExitStatus}; use std::env; diff --git a/tests/ui/simd/type-generic-monomorphisation-empty.rs b/tests/ui/simd/type-generic-monomorphisation-empty.rs index 4700f642065be..c08dc9fe3df2f 100644 --- a/tests/ui/simd/type-generic-monomorphisation-empty.rs +++ b/tests/ui/simd/type-generic-monomorphisation-empty.rs @@ -2,11 +2,11 @@ #![feature(repr_simd, intrinsics)] -//@ error-pattern:monomorphising SIMD type `Simd<0>` of zero length - #[repr(simd)] struct Simd([f32; N]); fn main() { let _ = Simd::<0>([]); } + +//~? ERROR monomorphising SIMD type `Simd<0>` of zero length diff --git a/tests/ui/simd/type-generic-monomorphisation-non-primitive.rs b/tests/ui/simd/type-generic-monomorphisation-non-primitive.rs index a2f6998c6d939..7924aeb86849e 100644 --- a/tests/ui/simd/type-generic-monomorphisation-non-primitive.rs +++ b/tests/ui/simd/type-generic-monomorphisation-non-primitive.rs @@ -4,11 +4,11 @@ struct E; -//@ error-pattern:monomorphising SIMD type `S` with a non-primitive-scalar (integer/float/pointer) element type `E` - #[repr(simd)] struct S([T; 4]); fn main() { let _v: Option> = None; } + +//~? ERROR monomorphising SIMD type `S` with a non-primitive-scalar (integer/float/pointer) element type `E` diff --git a/tests/ui/simd/type-generic-monomorphisation-oversized.rs b/tests/ui/simd/type-generic-monomorphisation-oversized.rs index 9949f913c4459..efe3480317c9e 100644 --- a/tests/ui/simd/type-generic-monomorphisation-oversized.rs +++ b/tests/ui/simd/type-generic-monomorphisation-oversized.rs @@ -2,11 +2,11 @@ #![feature(repr_simd, intrinsics)] -//@ error-pattern:monomorphising SIMD type `Simd<65536>` of length greater than 32768 - #[repr(simd)] struct Simd([f32; N]); fn main() { let _ = Simd::<65536>([0.; 65536]); } + +//~? ERROR monomorphising SIMD type `Simd<65536>` of length greater than 32768 diff --git a/tests/ui/simd/type-generic-monomorphisation-wide-ptr.rs b/tests/ui/simd/type-generic-monomorphisation-wide-ptr.rs index 18fc075343069..97640514dee93 100644 --- a/tests/ui/simd/type-generic-monomorphisation-wide-ptr.rs +++ b/tests/ui/simd/type-generic-monomorphisation-wide-ptr.rs @@ -2,11 +2,11 @@ #![feature(repr_simd)] -//@ error-pattern:monomorphising SIMD type `S<*mut [u8]>` with a non-primitive-scalar (integer/float/pointer) element type `*mut [u8]` - #[repr(simd)] struct S([T; 4]); fn main() { let _v: Option> = None; } + +//~? ERROR monomorphising SIMD type `S<*mut [u8]>` with a non-primitive-scalar (integer/float/pointer) element type `*mut [u8]` diff --git a/tests/ui/simd/type-generic-monomorphisation.rs b/tests/ui/simd/type-generic-monomorphisation.rs index 8b8d645a2649b..2563b917e3442 100644 --- a/tests/ui/simd/type-generic-monomorphisation.rs +++ b/tests/ui/simd/type-generic-monomorphisation.rs @@ -2,9 +2,6 @@ #![feature(repr_simd, intrinsics)] - -//@ error-pattern:monomorphising SIMD type `Simd2` with a non-primitive-scalar (integer/float/pointer) element type `X` - struct X(Vec); #[repr(simd)] struct Simd2([T; 2]); @@ -12,3 +9,5 @@ struct Simd2([T; 2]); fn main() { let _ = Simd2([X(vec![]), X(vec![])]); } + +//~? ERROR monomorphising SIMD type `Simd2` with a non-primitive-scalar (integer/float/pointer) element type `X` diff --git a/tests/ui/simd/type-wide-ptr.rs b/tests/ui/simd/type-wide-ptr.rs index 41d9fac26ad42..5740a4e8318ab 100644 --- a/tests/ui/simd/type-wide-ptr.rs +++ b/tests/ui/simd/type-wide-ptr.rs @@ -2,11 +2,11 @@ #![feature(repr_simd)] -//@ error-pattern:monomorphising SIMD type `S` with a non-primitive-scalar (integer/float/pointer) element type `*mut [u8]` - #[repr(simd)] struct S([*mut [u8]; 4]); fn main() { let _v: Option = None; } + +//~? ERROR monomorphising SIMD type `S` with a non-primitive-scalar (integer/float/pointer) element type `*mut [u8]` diff --git a/tests/ui/sized/dont-incompletely-prefer-built-in.rs b/tests/ui/sized/dont-incompletely-prefer-built-in.rs new file mode 100644 index 0000000000000..f5bf0c8915e76 --- /dev/null +++ b/tests/ui/sized/dont-incompletely-prefer-built-in.rs @@ -0,0 +1,21 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +struct W(T); + +fn is_sized(x: *const T) {} + +fn dummy() -> *const T { todo!() } + +fn non_param_where_bound() +where + W: Sized, +{ + let x: *const W<_> = dummy(); + is_sized::>(x); + let _: *const W = x; +} + +fn main() {} diff --git a/tests/ui/span/E0204.stderr b/tests/ui/span/E0204.stderr index 3a0afb541ba8d..fe375b94781b5 100644 --- a/tests/ui/span/E0204.stderr +++ b/tests/ui/span/E0204.stderr @@ -15,8 +15,6 @@ LL | #[derive(Copy)] LL | struct Foo2<'a> { LL | ty: &'a mut bool, | ---------------- this field does not implement `Copy` - | - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0204]: the trait `Copy` cannot be implemented for this type --> $DIR/E0204.rs:17:15 @@ -35,8 +33,6 @@ LL | #[derive(Copy)] LL | enum EFoo2<'a> { LL | Bar(&'a mut bool), | ------------ this field does not implement `Copy` - | - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/span/range-2.rs b/tests/ui/span/range-2.rs index c4bb16f44bd1b..2e8252169b7e6 100644 --- a/tests/ui/span/range-2.rs +++ b/tests/ui/span/range-2.rs @@ -1,6 +1,6 @@ // Test range syntax - borrow errors. -#![feature(rustc_attrs)] -pub fn main() { #![rustc_error] // rust-lang/rust#49855 + +pub fn main() { let r = { let a = 42; let b = 42; diff --git a/tests/ui/span/regionck-unboxed-closure-lifetimes.rs b/tests/ui/span/regionck-unboxed-closure-lifetimes.rs index 60ccaa872e731..fe6c353e8f8e2 100644 --- a/tests/ui/span/regionck-unboxed-closure-lifetimes.rs +++ b/tests/ui/span/regionck-unboxed-closure-lifetimes.rs @@ -1,7 +1,6 @@ -#![feature(rustc_attrs)] use std::ops::FnMut; -fn main() { #![rustc_error] // rust-lang/rust#49855 +fn main() { let mut f; { let c = 1; diff --git a/tests/ui/span/regionck-unboxed-closure-lifetimes.stderr b/tests/ui/span/regionck-unboxed-closure-lifetimes.stderr index 225f83b6e6662..bb6298211bc16 100644 --- a/tests/ui/span/regionck-unboxed-closure-lifetimes.stderr +++ b/tests/ui/span/regionck-unboxed-closure-lifetimes.stderr @@ -1,5 +1,5 @@ error[E0597]: `c` does not live long enough - --> $DIR/regionck-unboxed-closure-lifetimes.rs:8:21 + --> $DIR/regionck-unboxed-closure-lifetimes.rs:7:21 | LL | let c = 1; | - binding `c` declared here diff --git a/tests/ui/specialization/defaultimpl/validation.rs b/tests/ui/specialization/defaultimpl/validation.rs index 4049c4ea14c5b..14771be8982dd 100644 --- a/tests/ui/specialization/defaultimpl/validation.rs +++ b/tests/ui/specialization/defaultimpl/validation.rs @@ -6,12 +6,17 @@ struct Z; default impl S {} //~ ERROR inherent impls cannot be `default` -default unsafe impl Send for S {} //~ ERROR impls of auto traits cannot be default -//~^ ERROR `S` cannot be sent between threads safely -default impl !Send for Z {} //~ ERROR impls of auto traits cannot be default - //~^ ERROR negative impls cannot be default impls +default unsafe impl Send for S {} +//~^ ERROR impls of auto traits cannot be default + +default impl !Send for Z {} +//~^ ERROR impls of auto traits cannot be default +//~| ERROR negative impls cannot be default impls +//~| ERROR `!Send` impl requires `Z: Send` but the struct it is implemented for does not trait Tr {} -default impl !Tr for S {} //~ ERROR negative impls cannot be default impls + +default impl !Tr for S {} +//~^ ERROR negative impls cannot be default impls fn main() {} diff --git a/tests/ui/specialization/defaultimpl/validation.stderr b/tests/ui/specialization/defaultimpl/validation.stderr index f56f16162a27f..d034386b842f3 100644 --- a/tests/ui/specialization/defaultimpl/validation.stderr +++ b/tests/ui/specialization/defaultimpl/validation.stderr @@ -26,22 +26,20 @@ LL | default unsafe impl Send for S {} | | | default because of this -error[E0277]: `S` cannot be sent between threads safely - --> $DIR/validation.rs:9:1 +error[E0367]: `!Send` impl requires `Z: Send` but the struct it is implemented for does not + --> $DIR/validation.rs:12:1 | -LL | default unsafe impl Send for S {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `S` cannot be sent between threads safely - | - = help: the trait `Send` is not implemented for `S` - = help: the trait `Send` is implemented for `S` - = help: see issue #48214 -help: add `#![feature(trivial_bounds)]` to the crate attributes to enable +LL | default impl !Send for Z {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ | -LL + #![feature(trivial_bounds)] +note: the implementor must specify the same requirement + --> $DIR/validation.rs:5:1 | +LL | struct Z; + | ^^^^^^^^ error: impls of auto traits cannot be default - --> $DIR/validation.rs:11:15 + --> $DIR/validation.rs:12:15 | LL | default impl !Send for Z {} | ------- ^^^^ auto trait @@ -49,18 +47,18 @@ LL | default impl !Send for Z {} | default because of this error[E0750]: negative impls cannot be default impls - --> $DIR/validation.rs:11:1 + --> $DIR/validation.rs:12:1 | LL | default impl !Send for Z {} | ^^^^^^^ ^ error[E0750]: negative impls cannot be default impls - --> $DIR/validation.rs:15:1 + --> $DIR/validation.rs:19:1 | LL | default impl !Tr for S {} | ^^^^^^^ ^ error: aborting due to 6 previous errors; 1 warning emitted -Some errors have detailed explanations: E0277, E0750. -For more information about an error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0367, E0750. +For more information about an error, try `rustc --explain E0367`. diff --git a/tests/ui/specialization/issue-63716-parse-async.rs b/tests/ui/specialization/issue-63716-parse-async.rs index 3314b4e20f90b..00c0b291a1ada 100644 --- a/tests/ui/specialization/issue-63716-parse-async.rs +++ b/tests/ui/specialization/issue-63716-parse-async.rs @@ -8,7 +8,7 @@ fn main() {} -#[cfg(FALSE)] +#[cfg(false)] impl Foo for Bar { default async fn baz() {} } diff --git a/tests/ui/specialization/min_specialization/impl-on-opaque.rs b/tests/ui/specialization/min_specialization/impl-on-opaque.rs index 7531dcaccf235..131ad8b9d65df 100644 --- a/tests/ui/specialization/min_specialization/impl-on-opaque.rs +++ b/tests/ui/specialization/min_specialization/impl-on-opaque.rs @@ -26,6 +26,7 @@ impl SpecTrait for () { fn f() {} } +#[define_opaque(Opaque)] fn foo() -> Opaque {} fn main() {} diff --git a/tests/ui/specialization/min_specialization/impl-on-opaque2.rs b/tests/ui/specialization/min_specialization/impl-on-opaque2.rs index 0cd8be84ed370..bcdb54c2b4f77 100644 --- a/tests/ui/specialization/min_specialization/impl-on-opaque2.rs +++ b/tests/ui/specialization/min_specialization/impl-on-opaque2.rs @@ -23,6 +23,7 @@ impl SpecTrait<(), Opaque> for () { fn f() {} } +#[define_opaque(Opaque)] fn foo() -> Opaque {} fn main() {} diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr b/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr new file mode 100644 index 0000000000000..7e3df0c83f9f3 --- /dev/null +++ b/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr @@ -0,0 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/prefer-specializing-impl-over-default.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr b/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr new file mode 100644 index 0000000000000..7e3df0c83f9f3 --- /dev/null +++ b/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr @@ -0,0 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/prefer-specializing-impl-over-default.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.rs b/tests/ui/specialization/prefer-specializing-impl-over-default.rs new file mode 100644 index 0000000000000..af6837b30cafa --- /dev/null +++ b/tests/ui/specialization/prefer-specializing-impl-over-default.rs @@ -0,0 +1,29 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass +#![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete + +trait WithAssoc: 'static { + type Assoc; +} +impl WithAssoc for (T,) { + type Assoc = (); +} + +struct GenericArray(U::Assoc); + +trait AbiExample { + fn example(); +} +impl AbiExample for GenericArray { + fn example() {} +} +impl AbiExample for T { + default fn example() {} +} + +fn main() { + let _ = GenericArray::<((),)>::example(); +} diff --git a/tests/ui/specialization/specialization-default-projection.current.stderr b/tests/ui/specialization/specialization-default-projection.current.stderr new file mode 100644 index 0000000000000..038c379c43e13 --- /dev/null +++ b/tests/ui/specialization/specialization-default-projection.current.stderr @@ -0,0 +1,43 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-projection.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/specialization-default-projection.rs:25:5 + | +LL | fn generic() -> ::Assoc { + | ----------------- expected `::Assoc` because of return type +... +LL | () + | ^^ expected associated type, found `()` + | + = note: expected associated type `::Assoc` + found unit type `()` + = help: consider constraining the associated type `::Assoc` to `()` or calling a method that returns `::Assoc` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error[E0308]: mismatched types + --> $DIR/specialization-default-projection.rs:32:5 + | +LL | fn monomorphic() -> () { + | -- expected `()` because of return type +... +LL | generic::<()>() + | ^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;` + | | + | expected `()`, found associated type + | + = note: expected unit type `()` + found associated type `<() as Foo>::Assoc` + = help: consider constraining the associated type `<() as Foo>::Assoc` to `()` + = 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 + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/specialization-default-projection.next.stderr b/tests/ui/specialization/specialization-default-projection.next.stderr new file mode 100644 index 0000000000000..9111f173a9c87 --- /dev/null +++ b/tests/ui/specialization/specialization-default-projection.next.stderr @@ -0,0 +1,43 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-projection.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/specialization-default-projection.rs:25:5 + | +LL | fn generic() -> ::Assoc { + | ----------------- expected `::Assoc` because of return type +... +LL | () + | ^^ types differ + | + = note: expected associated type `::Assoc` + found unit type `()` + = help: consider constraining the associated type `::Assoc` to `()` or calling a method that returns `::Assoc` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error[E0308]: mismatched types + --> $DIR/specialization-default-projection.rs:32:5 + | +LL | fn monomorphic() -> () { + | -- expected `()` because of return type +... +LL | generic::<()>() + | ^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;` + | | + | types differ + | + = note: expected unit type `()` + found associated type `<() as Foo>::Assoc` + = help: consider constraining the associated type `<() as Foo>::Assoc` to `()` + = 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 + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/specialization-default-projection.rs b/tests/ui/specialization/specialization-default-projection.rs index 7f3ae951287ca..4f69ccb59746b 100644 --- a/tests/ui/specialization/specialization-default-projection.rs +++ b/tests/ui/specialization/specialization-default-projection.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + #![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Make sure we can't project defaulted associated types diff --git a/tests/ui/specialization/specialization-default-projection.stderr b/tests/ui/specialization/specialization-default-projection.stderr deleted file mode 100644 index b8b81876d8131..0000000000000 --- a/tests/ui/specialization/specialization-default-projection.stderr +++ /dev/null @@ -1,43 +0,0 @@ -warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-default-projection.rs:1:12 - | -LL | #![feature(specialization)] - | ^^^^^^^^^^^^^^ - | - = note: see issue #31844 for more information - = help: consider using `min_specialization` instead, which is more stable and complete - = note: `#[warn(incomplete_features)]` on by default - -error[E0308]: mismatched types - --> $DIR/specialization-default-projection.rs:21:5 - | -LL | fn generic() -> ::Assoc { - | ----------------- expected `::Assoc` because of return type -... -LL | () - | ^^ expected associated type, found `()` - | - = note: expected associated type `::Assoc` - found unit type `()` - = help: consider constraining the associated type `::Assoc` to `()` or calling a method that returns `::Assoc` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html - -error[E0308]: mismatched types - --> $DIR/specialization-default-projection.rs:28:5 - | -LL | fn monomorphic() -> () { - | -- expected `()` because of return type -... -LL | generic::<()>() - | ^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;` - | | - | expected `()`, found associated type - | - = note: expected unit type `()` - found associated type `<() as Foo>::Assoc` - = help: consider constraining the associated type `<() as Foo>::Assoc` to `()` - = 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 - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/specialization-default-types.current.stderr b/tests/ui/specialization/specialization-default-types.current.stderr new file mode 100644 index 0000000000000..67477f9a6d537 --- /dev/null +++ b/tests/ui/specialization/specialization-default-types.current.stderr @@ -0,0 +1,39 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-types.rs:9:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/specialization-default-types.rs:19:9 + | +LL | default type Output = Box; + | ----------------------------- associated type is `default` and may be overridden +LL | default fn generate(self) -> Self::Output { + | ------------ expected `::Output` because of return type +LL | Box::new(self) + | ^^^^^^^^^^^^^^ expected associated type, found `Box` + | + = note: expected associated type `::Output` + found struct `Box` + +error[E0308]: mismatched types + --> $DIR/specialization-default-types.rs:29:5 + | +LL | fn trouble(t: T) -> Box { + | ------ expected `Box` because of return type +LL | Example::generate(t) + | ^^^^^^^^^^^^^^^^^^^^ expected `Box`, found associated type + | + = note: expected struct `Box` + found associated type `::Output` + = 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 + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/specialization-default-types.next.stderr b/tests/ui/specialization/specialization-default-types.next.stderr new file mode 100644 index 0000000000000..4f7c47654460e --- /dev/null +++ b/tests/ui/specialization/specialization-default-types.next.stderr @@ -0,0 +1,39 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-types.rs:9:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/specialization-default-types.rs:19:9 + | +LL | default type Output = Box; + | ----------------------------- associated type is `default` and may be overridden +LL | default fn generate(self) -> Self::Output { + | ------------ expected `::Output` because of return type +LL | Box::new(self) + | ^^^^^^^^^^^^^^ types differ + | + = note: expected associated type `::Output` + found struct `Box` + +error[E0308]: mismatched types + --> $DIR/specialization-default-types.rs:29:5 + | +LL | fn trouble(t: T) -> Box { + | ------ expected `Box` because of return type +LL | Example::generate(t) + | ^^^^^^^^^^^^^^^^^^^^ types differ + | + = note: expected struct `Box` + found associated type `::Output` + = 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 + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/specialization-default-types.rs b/tests/ui/specialization/specialization-default-types.rs index 346471f11e4a8..77817abea127c 100644 --- a/tests/ui/specialization/specialization-default-types.rs +++ b/tests/ui/specialization/specialization-default-types.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + // It should not be possible to use the concrete value of a defaulted // associated type in the impl defining it -- otherwise, what happens // if it's overridden? diff --git a/tests/ui/specialization/specialization-default-types.stderr b/tests/ui/specialization/specialization-default-types.stderr deleted file mode 100644 index 774ac9536175d..0000000000000 --- a/tests/ui/specialization/specialization-default-types.stderr +++ /dev/null @@ -1,39 +0,0 @@ -warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-default-types.rs:5:12 - | -LL | #![feature(specialization)] - | ^^^^^^^^^^^^^^ - | - = note: see issue #31844 for more information - = help: consider using `min_specialization` instead, which is more stable and complete - = note: `#[warn(incomplete_features)]` on by default - -error[E0308]: mismatched types - --> $DIR/specialization-default-types.rs:15:9 - | -LL | default type Output = Box; - | ----------------------------- associated type is `default` and may be overridden -LL | default fn generate(self) -> Self::Output { - | ------------ expected `::Output` because of return type -LL | Box::new(self) - | ^^^^^^^^^^^^^^ expected associated type, found `Box` - | - = note: expected associated type `::Output` - found struct `Box` - -error[E0308]: mismatched types - --> $DIR/specialization-default-types.rs:25:5 - | -LL | fn trouble(t: T) -> Box { - | ------ expected `Box` because of return type -LL | Example::generate(t) - | ^^^^^^^^^^^^^^^^^^^^ expected `Box`, found associated type - | - = note: expected struct `Box` - found associated type `::Output` - = 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 - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/specialization-overlap-negative.rs b/tests/ui/specialization/specialization-overlap-negative.rs index 550d370829539..244f21c7ba905 100644 --- a/tests/ui/specialization/specialization-overlap-negative.rs +++ b/tests/ui/specialization/specialization-overlap-negative.rs @@ -6,6 +6,8 @@ trait MyTrait {} struct TestType(::std::marker::PhantomData); unsafe impl Send for TestType {} -impl !Send for TestType {} //~ ERROR E0751 +impl !Send for TestType {} +//~^ ERROR found both positive and negative implementation of trait `Send` for type `TestType<_>` +//~| ERROR `!Send` impl requires `T: MyTrait` but the struct it is implemented for does not fn main() {} diff --git a/tests/ui/specialization/specialization-overlap-negative.stderr b/tests/ui/specialization/specialization-overlap-negative.stderr index a8e99953e2b65..4874e897726fc 100644 --- a/tests/ui/specialization/specialization-overlap-negative.stderr +++ b/tests/ui/specialization/specialization-overlap-negative.stderr @@ -16,6 +16,19 @@ LL | unsafe impl Send for TestType {} LL | impl !Send for TestType {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here -error: aborting due to 1 previous error; 1 warning emitted +error[E0367]: `!Send` impl requires `T: MyTrait` but the struct it is implemented for does not + --> $DIR/specialization-overlap-negative.rs:9:9 + | +LL | impl !Send for TestType {} + | ^^^^^^^ + | +note: the implementor must specify the same requirement + --> $DIR/specialization-overlap-negative.rs:6:1 + | +LL | struct TestType(::std::marker::PhantomData); + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0751`. +Some errors have detailed explanations: E0367, E0751. +For more information about an error, try `rustc --explain E0367`. diff --git a/tests/ui/stability-attribute/check-stability-issue-138319.rs b/tests/ui/stability-attribute/check-stability-issue-138319.rs new file mode 100644 index 0000000000000..5440e0cad94c3 --- /dev/null +++ b/tests/ui/stability-attribute/check-stability-issue-138319.rs @@ -0,0 +1,39 @@ +//@ check-pass +fn _foo() { + _Bar { //~ WARNING use of deprecated struct `_Bar`: reason + #[expect(deprecated)] + foo: 0, + }; +} + +#[deprecated = "reason"] +struct _Bar { + foo: u32, +} + +fn _foo2() { + #[expect(deprecated)] + _Bar2 { + foo2: 0, + }; +} + +#[deprecated = "reason"] +struct _Bar2 { + foo2: u32, +} + +fn _foo3() { + _Bar3 { + #[expect(deprecated)] + foo3: 0, + }; +} + +struct _Bar3 { + #[deprecated = "reason"] + foo3: u32, +} + + +fn main() {} diff --git a/tests/ui/stability-attribute/check-stability-issue-138319.stderr b/tests/ui/stability-attribute/check-stability-issue-138319.stderr new file mode 100644 index 0000000000000..4a2c3554a1e7e --- /dev/null +++ b/tests/ui/stability-attribute/check-stability-issue-138319.stderr @@ -0,0 +1,10 @@ +warning: use of deprecated struct `_Bar`: reason + --> $DIR/check-stability-issue-138319.rs:3:5 + | +LL | _Bar { + | ^^^^ + | + = note: `#[warn(deprecated)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/stability-attribute/generics-default-stability.rs b/tests/ui/stability-attribute/generics-default-stability.rs index e1b3971f70c06..400e22a8e6ccb 100644 --- a/tests/ui/stability-attribute/generics-default-stability.rs +++ b/tests/ui/stability-attribute/generics-default-stability.rs @@ -69,30 +69,30 @@ fn main() { let _ = STRUCT4; let _: Struct4 = Struct4 { field: 1 }; - //~^ use of deprecated struct `unstable_generic_param::Struct4`: test [deprecated] - //~^^ use of deprecated struct `unstable_generic_param::Struct4`: test [deprecated] - //~^^^ use of deprecated field `unstable_generic_param::Struct4::field`: test [deprecated] + //~^ WARN use of deprecated struct `unstable_generic_param::Struct4`: test [deprecated] + //~^^ WARN use of deprecated struct `unstable_generic_param::Struct4`: test [deprecated] + //~^^^ WARN use of deprecated field `unstable_generic_param::Struct4::field`: test [deprecated] let _ = STRUCT4; - let _: Struct4 = STRUCT4; //~ use of deprecated struct `unstable_generic_param::Struct4`: test [deprecated] - let _: Struct4 = STRUCT4; //~ use of deprecated struct `unstable_generic_param::Struct4`: test [deprecated] + let _: Struct4 = STRUCT4; //~ WARN use of deprecated struct `unstable_generic_param::Struct4`: test [deprecated] + let _: Struct4 = STRUCT4; //~ WARN use of deprecated struct `unstable_generic_param::Struct4`: test [deprecated] let _: Struct4 = Struct4 { field: 0 }; - //~^ use of deprecated struct `unstable_generic_param::Struct4`: test [deprecated] - //~^^ use of deprecated struct `unstable_generic_param::Struct4`: test [deprecated] - //~^^^ use of deprecated field `unstable_generic_param::Struct4::field`: test [deprecated] + //~^ WARN use of deprecated struct `unstable_generic_param::Struct4`: test [deprecated] + //~^^ WARN use of deprecated struct `unstable_generic_param::Struct4`: test [deprecated] + //~^^^ WARN use of deprecated field `unstable_generic_param::Struct4::field`: test [deprecated] let _ = STRUCT5; let _: Struct5 = Struct5 { field: 1 }; //~ ERROR use of unstable library feature `unstable_default` - //~^ use of deprecated struct `unstable_generic_param::Struct5`: test [deprecated] - //~^^ use of deprecated struct `unstable_generic_param::Struct5`: test [deprecated] - //~^^^ use of deprecated field `unstable_generic_param::Struct5::field`: test [deprecated] + //~^ WARN use of deprecated struct `unstable_generic_param::Struct5`: test [deprecated] + //~^^ WARN use of deprecated struct `unstable_generic_param::Struct5`: test [deprecated] + //~^^^ WARN use of deprecated field `unstable_generic_param::Struct5::field`: test [deprecated] let _ = STRUCT5; - let _: Struct5 = STRUCT5; //~ use of deprecated struct `unstable_generic_param::Struct5`: test [deprecated] + let _: Struct5 = STRUCT5; //~ WARN use of deprecated struct `unstable_generic_param::Struct5`: test [deprecated] let _: Struct5 = STRUCT5; //~ ERROR use of unstable library feature `unstable_default` - //~^ use of deprecated struct `unstable_generic_param::Struct5`: test [deprecated] + //~^ WARN use of deprecated struct `unstable_generic_param::Struct5`: test [deprecated] let _: Struct5 = Struct5 { field: 0 }; //~ ERROR use of unstable library feature `unstable_default` - //~^ use of deprecated struct `unstable_generic_param::Struct5`: test [deprecated] - //~^^ use of deprecated struct `unstable_generic_param::Struct5`: test [deprecated] - //~^^^ use of deprecated field `unstable_generic_param::Struct5::field`: test [deprecated] + //~^ WARN use of deprecated struct `unstable_generic_param::Struct5`: test [deprecated] + //~^^ WARN use of deprecated struct `unstable_generic_param::Struct5`: test [deprecated] + //~^^^ WARN use of deprecated field `unstable_generic_param::Struct5::field`: test [deprecated] let _: Struct6 = Struct6 { field: 1 }; // ok let _: Struct6 = Struct6 { field: 0 }; // ok @@ -145,26 +145,26 @@ fn main() { let _ = ALIAS4; let _: Alias4 = Alias4::Some(1); - //~^ use of deprecated type alias `unstable_generic_param::Alias4`: test [deprecated] - //~^^ use of deprecated type alias `unstable_generic_param::Alias4`: test [deprecated] + //~^ WARN use of deprecated type alias `unstable_generic_param::Alias4`: test [deprecated] + //~^^ WARN use of deprecated type alias `unstable_generic_param::Alias4`: test [deprecated] let _ = ALIAS4; - let _: Alias4 = ALIAS4; //~ use of deprecated type alias `unstable_generic_param::Alias4`: test [deprecated] - let _: Alias4 = ALIAS4; //~ use of deprecated type alias `unstable_generic_param::Alias4`: test [deprecated] + let _: Alias4 = ALIAS4; //~ WARN use of deprecated type alias `unstable_generic_param::Alias4`: test [deprecated] + let _: Alias4 = ALIAS4; //~ WARN use of deprecated type alias `unstable_generic_param::Alias4`: test [deprecated] let _: Alias4 = Alias4::Some(0); - //~^ use of deprecated type alias `unstable_generic_param::Alias4`: test [deprecated] - //~^^ use of deprecated type alias `unstable_generic_param::Alias4`: test [deprecated] + //~^ WARN use of deprecated type alias `unstable_generic_param::Alias4`: test [deprecated] + //~^^ WARN use of deprecated type alias `unstable_generic_param::Alias4`: test [deprecated] let _ = ALIAS5; let _: Alias5 = Alias5::Some(1); //~ ERROR use of unstable library feature `unstable_default` - //~^ use of deprecated type alias `unstable_generic_param::Alias5`: test [deprecated] - //~^^ use of deprecated type alias `unstable_generic_param::Alias5`: test [deprecated] + //~^ WARN use of deprecated type alias `unstable_generic_param::Alias5`: test [deprecated] + //~^^ WARN use of deprecated type alias `unstable_generic_param::Alias5`: test [deprecated] let _ = ALIAS5; - let _: Alias5 = ALIAS5; //~ use of deprecated type alias `unstable_generic_param::Alias5`: test [deprecated] + let _: Alias5 = ALIAS5; //~ WARN use of deprecated type alias `unstable_generic_param::Alias5`: test [deprecated] let _: Alias5 = ALIAS5; //~ ERROR use of unstable library feature `unstable_default` - //~^ use of deprecated type alias `unstable_generic_param::Alias5`: test [deprecated] + //~^ WARN use of deprecated type alias `unstable_generic_param::Alias5`: test [deprecated] let _: Alias5 = Alias5::Some(0); //~ ERROR use of unstable library feature `unstable_default` - //~^ use of deprecated type alias `unstable_generic_param::Alias5`: test [deprecated] - //~^^ use of deprecated type alias `unstable_generic_param::Alias5`: test [deprecated] + //~^ WARN use of deprecated type alias `unstable_generic_param::Alias5`: test [deprecated] + //~^^ WARN use of deprecated type alias `unstable_generic_param::Alias5`: test [deprecated] let _: Alias6 = Alias6::Some(1); // ok let _: Alias6 = Alias6::Some(0); // ok @@ -217,26 +217,26 @@ fn main() { let _ = ENUM4; let _: Enum4 = Enum4::Some(1); - //~^ use of deprecated tuple variant `unstable_generic_param::Enum4::Some`: test [deprecated] - //~^^ use of deprecated enum `unstable_generic_param::Enum4`: test [deprecated] + //~^ WARN use of deprecated tuple variant `unstable_generic_param::Enum4::Some`: test [deprecated] + //~^^ WARN use of deprecated enum `unstable_generic_param::Enum4`: test [deprecated] let _ = ENUM4; - let _: Enum4 = ENUM4; //~ use of deprecated enum `unstable_generic_param::Enum4`: test [deprecated] - let _: Enum4 = ENUM4; //~ use of deprecated enum `unstable_generic_param::Enum4`: test [deprecated] + let _: Enum4 = ENUM4; //~ WARN use of deprecated enum `unstable_generic_param::Enum4`: test [deprecated] + let _: Enum4 = ENUM4; //~ WARN use of deprecated enum `unstable_generic_param::Enum4`: test [deprecated] let _: Enum4 = Enum4::Some(0); - //~^ use of deprecated tuple variant `unstable_generic_param::Enum4::Some`: test [deprecated] - //~^^ use of deprecated enum `unstable_generic_param::Enum4`: test [deprecated] + //~^ WARN use of deprecated tuple variant `unstable_generic_param::Enum4::Some`: test [deprecated] + //~^^ WARN use of deprecated enum `unstable_generic_param::Enum4`: test [deprecated] let _ = ENUM5; let _: Enum5 = Enum5::Some(1); //~ ERROR use of unstable library feature `unstable_default` - //~^ use of deprecated tuple variant `unstable_generic_param::Enum5::Some`: test [deprecated] - //~^^ use of deprecated enum `unstable_generic_param::Enum5`: test [deprecated] + //~^ WARN use of deprecated tuple variant `unstable_generic_param::Enum5::Some`: test [deprecated] + //~^^ WARN use of deprecated enum `unstable_generic_param::Enum5`: test [deprecated] let _ = ENUM5; - let _: Enum5 = ENUM5; //~ use of deprecated enum `unstable_generic_param::Enum5`: test [deprecated] + let _: Enum5 = ENUM5; //~ WARN use of deprecated enum `unstable_generic_param::Enum5`: test [deprecated] let _: Enum5 = ENUM5; //~ ERROR use of unstable library feature `unstable_default` - //~^ use of deprecated enum `unstable_generic_param::Enum5`: test [deprecated] + //~^ WARN use of deprecated enum `unstable_generic_param::Enum5`: test [deprecated] let _: Enum5 = Enum5::Some(0); //~ ERROR use of unstable library feature `unstable_default` - //~^ use of deprecated tuple variant `unstable_generic_param::Enum5::Some`: test [deprecated] - //~^^ use of deprecated enum `unstable_generic_param::Enum5`: test [deprecated] + //~^ WARN use of deprecated tuple variant `unstable_generic_param::Enum5::Some`: test [deprecated] + //~^^ WARN use of deprecated enum `unstable_generic_param::Enum5`: test [deprecated] let _: Enum6 = Enum6::Some(1); // ok let _: Enum6 = Enum6::Some(0); // ok diff --git a/tests/ui/stability-attribute/generics-default-stability.stderr b/tests/ui/stability-attribute/generics-default-stability.stderr index f4f51a14248eb..53ef3e170cc1d 100644 --- a/tests/ui/stability-attribute/generics-default-stability.stderr +++ b/tests/ui/stability-attribute/generics-default-stability.stderr @@ -270,6 +270,18 @@ LL | let _: Struct3 = Struct3 { field1: 0, field2: 0 }; = help: add `#![feature(unstable_default)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +warning: use of deprecated field `unstable_generic_param::Struct4::field`: test + --> $DIR/generics-default-stability.rs:71:39 + | +LL | let _: Struct4 = Struct4 { field: 1 }; + | ^^^^^^^^ + +warning: use of deprecated field `unstable_generic_param::Struct4::field`: test + --> $DIR/generics-default-stability.rs:78:39 + | +LL | let _: Struct4 = Struct4 { field: 0 }; + | ^^^^^^^^ + error[E0658]: use of unstable library feature `unstable_default` --> $DIR/generics-default-stability.rs:84:20 | @@ -279,6 +291,12 @@ LL | let _: Struct5 = Struct5 { field: 1 }; = help: add `#![feature(unstable_default)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +warning: use of deprecated field `unstable_generic_param::Struct5::field`: test + --> $DIR/generics-default-stability.rs:84:39 + | +LL | let _: Struct5 = Struct5 { field: 1 }; + | ^^^^^^^^ + error[E0658]: use of unstable library feature `unstable_default` --> $DIR/generics-default-stability.rs:90:20 | @@ -297,6 +315,12 @@ LL | let _: Struct5 = Struct5 { field: 0 }; = help: add `#![feature(unstable_default)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +warning: use of deprecated field `unstable_generic_param::Struct5::field`: test + --> $DIR/generics-default-stability.rs:92:39 + | +LL | let _: Struct5 = Struct5 { field: 0 }; + | ^^^^^^^^ + error[E0658]: use of unstable library feature `unstable_default` --> $DIR/generics-default-stability.rs:100:19 | @@ -468,30 +492,6 @@ LL | let _: Box1 = Box1::new(1); = help: add `#![feature(box_alloc_param)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: use of deprecated field `unstable_generic_param::Struct4::field`: test - --> $DIR/generics-default-stability.rs:71:39 - | -LL | let _: Struct4 = Struct4 { field: 1 }; - | ^^^^^^^^ - -warning: use of deprecated field `unstable_generic_param::Struct4::field`: test - --> $DIR/generics-default-stability.rs:78:39 - | -LL | let _: Struct4 = Struct4 { field: 0 }; - | ^^^^^^^^ - -warning: use of deprecated field `unstable_generic_param::Struct5::field`: test - --> $DIR/generics-default-stability.rs:84:39 - | -LL | let _: Struct5 = Struct5 { field: 1 }; - | ^^^^^^^^ - -warning: use of deprecated field `unstable_generic_param::Struct5::field`: test - --> $DIR/generics-default-stability.rs:92:39 - | -LL | let _: Struct5 = Struct5 { field: 0 }; - | ^^^^^^^^ - error: aborting due to 28 previous errors; 40 warnings emitted For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/stability-attribute/issue-99286-stable-intrinsics.rs b/tests/ui/stability-attribute/issue-99286-stable-intrinsics.rs index b76603740ff9b..d9e810362f2e9 100644 --- a/tests/ui/stability-attribute/issue-99286-stable-intrinsics.rs +++ b/tests/ui/stability-attribute/issue-99286-stable-intrinsics.rs @@ -8,10 +8,9 @@ #![allow(unused_imports)] #![allow(deprecated)] -use std::intrinsics::drop_in_place as _; use std::intrinsics::copy_nonoverlapping as _; use std::intrinsics::copy as _; use std::intrinsics::write_bytes as _; -use std::intrinsics::{drop_in_place, copy_nonoverlapping, copy, write_bytes}; +use std::intrinsics::{copy_nonoverlapping, copy, write_bytes}; fn main() {} diff --git a/tests/ui/stability-attribute/suggest-vec-allocator-api.stderr b/tests/ui/stability-attribute/suggest-vec-allocator-api.stderr index 6662ceda90b9e..78b7d07b60cc3 100644 --- a/tests/ui/stability-attribute/suggest-vec-allocator-api.stderr +++ b/tests/ui/stability-attribute/suggest-vec-allocator-api.stderr @@ -27,22 +27,22 @@ LL ~ _)> = vec![]; | error[E0658]: use of unstable library feature `allocator_api` - --> $DIR/suggest-vec-allocator-api.rs:8:26 + --> $DIR/suggest-vec-allocator-api.rs:7:24 | -LL | let _boxed: Box = Box::new(10); - | ^ +LL | let _ = Vec::::new(); + | -----^ + | | + | help: consider wrapping the inner types in tuple: `(u16, _)` | = note: see issue #32838 for more information = help: add `#![feature(allocator_api)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `allocator_api` - --> $DIR/suggest-vec-allocator-api.rs:7:24 + --> $DIR/suggest-vec-allocator-api.rs:8:26 | -LL | let _ = Vec::::new(); - | -----^ - | | - | help: consider wrapping the inner types in tuple: `(u16, _)` +LL | let _boxed: Box = Box::new(10); + | ^ | = note: see issue #32838 for more information = help: add `#![feature(allocator_api)]` to the crate attributes to enable diff --git a/tests/ui/stable-mir-print/async-closure.stdout b/tests/ui/stable-mir-print/async-closure.stdout index 21df1fd395403..12e7a5530ace6 100644 --- a/tests/ui/stable-mir-print/async-closure.stdout +++ b/tests/ui/stable-mir-print/async-closure.stdout @@ -56,7 +56,7 @@ fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-clo unreachable; } } -fn foo::{closure#0}::{closure#1}(_1: Pin<&mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}>, _2: &mut Context<'_>) -> Poll<()> { +fn foo::{closure#0}::{synthetic#0}(_1: Pin<&mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}>, _2: &mut Context<'_>) -> Poll<()> { let mut _0: Poll<()>; let _3: i32; let mut _4: &i32; diff --git a/tests/ui/stack-protector/warn-stack-protector-unsupported.rs b/tests/ui/stack-protector/warn-stack-protector-unsupported.rs index 9205d4052ad48..dc61e35a089a5 100644 --- a/tests/ui/stack-protector/warn-stack-protector-unsupported.rs +++ b/tests/ui/stack-protector/warn-stack-protector-unsupported.rs @@ -17,3 +17,7 @@ trait Sized {} trait Copy {} pub fn main(){} + +//[all]~? WARN `-Z stack-protector=all` is not supported for target nvptx64-nvidia-cuda and will be ignored +//[strong]~? WARN `-Z stack-protector=strong` is not supported for target nvptx64-nvidia-cuda and will be ignored +//[basic]~? WARN `-Z stack-protector=basic` is not supported for target nvptx64-nvidia-cuda and will be ignored diff --git a/tests/ui/static/bad-const-type.rs b/tests/ui/static/bad-const-type.rs index 24fd67ecbaacd..d4e6352d7c1c9 100644 --- a/tests/ui/static/bad-const-type.rs +++ b/tests/ui/static/bad-const-type.rs @@ -1,4 +1,4 @@ static i: String = 10; //~^ ERROR mismatched types -//~| expected `String`, found integer +//~| NOTE expected `String`, found integer fn main() { println!("{}", i); } diff --git a/tests/ui/static/issue-24446.stderr b/tests/ui/static/issue-24446.stderr index 033caf07d8e54..0e6e338c5efeb 100644 --- a/tests/ui/static/issue-24446.stderr +++ b/tests/ui/static/issue-24446.stderr @@ -14,6 +14,7 @@ LL | static foo: dyn Fn() -> u32 = || -> u32 { | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Fn() -> u32 + 'static)` + = note: statics and constants must have a statically known size error[E0308]: mismatched types --> $DIR/issue-24446.rs:2:35 diff --git a/tests/ui/statics/auxiliary/inner_static.rs b/tests/ui/statics/auxiliary/inner_static.rs new file mode 100644 index 0000000000000..1c62046438fc1 --- /dev/null +++ b/tests/ui/statics/auxiliary/inner_static.rs @@ -0,0 +1,67 @@ +//! Test for inner statics with the same name. +//! +//! Before, the path name for all items defined in methods of traits and impls never +//! took into account the name of the method. This meant that if you had two statics +//! of the same name in two different methods the statics would end up having the +//! same symbol named (even after mangling) because the path components leading to +//! the symbol were exactly the same (just __extensions__ and the static name). +//! +//! It turns out that if you add the symbol "A" twice to LLVM, it automatically +//! makes the second one "A1" instead of "A". What this meant is that in local crate +//! compilations we never found this bug. Even across crates, this was never a +//! problem. The problem arises when you have generic methods that don't get +//! generated at compile-time of a library. If the statics were re-added to LLVM by +//! a client crate of a library in a different order, you would reference different +//! constants (the integer suffixes wouldn't be guaranteed to be the same). + +pub struct A { pub v: T } +pub struct B { pub v: T } + +pub mod test { + pub struct A { pub v: T } + + impl A { + pub fn foo(&self) -> isize { + static a: isize = 5; + return a + } + + pub fn bar(&self) -> isize { + static a: isize = 6; + return a; + } + } +} + +impl A { + pub fn foo(&self) -> isize { + static a: isize = 1; + return a + } + + pub fn bar(&self) -> isize { + static a: isize = 2; + return a; + } +} + +impl B { + pub fn foo(&self) -> isize { + static a: isize = 3; + return a + } + + pub fn bar(&self) -> isize { + static a: isize = 4; + return a; + } +} + +pub fn foo() -> isize { + let a = A { v: () }; + let b = B { v: () }; + let c = test::A { v: () }; + return a.foo() + a.bar() + + b.foo() + b.bar() + + c.foo() + c.bar(); +} diff --git a/tests/ui/statics/inner-static.rs b/tests/ui/statics/inner-static.rs new file mode 100644 index 0000000000000..1916435c46a12 --- /dev/null +++ b/tests/ui/statics/inner-static.rs @@ -0,0 +1,30 @@ +//! Test for inner statics with the same name. +//! +//! Before, the path name for all items defined in methods of traits and impls never +//! took into account the name of the method. This meant that if you had two statics +//! of the same name in two different methods the statics would end up having the +//! same symbol named (even after mangling) because the path components leading to +//! the symbol were exactly the same (just __extensions__ and the static name). +//! +//! It turns out that if you add the symbol "A" twice to LLVM, it automatically +//! makes the second one "A1" instead of "A". What this meant is that in local crate +//! compilations we never found this bug. Even across crates, this was never a +//! problem. The problem arises when you have generic methods that don't get +//! generated at compile-time of a library. If the statics were re-added to LLVM by +//! a client crate of a library in a different order, you would reference different +//! constants (the integer suffixes wouldn't be guaranteed to be the same). + +//@ run-pass +//@ aux-build:inner_static.rs + + +extern crate inner_static; + +pub fn main() { + let a = inner_static::A::<()> { v: () }; + let b = inner_static::B::<()> { v: () }; + let c = inner_static::test::A::<()> { v: () }; + assert_eq!(a.bar(), 2); + assert_eq!(b.bar(), 4); + assert_eq!(c.bar(), 6); +} diff --git a/tests/ui/statics/issue-14227.stderr b/tests/ui/statics/issue-14227.stderr index 0aeb973bff301..3551821a3dadb 100644 --- a/tests/ui/statics/issue-14227.stderr +++ b/tests/ui/statics/issue-14227.stderr @@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/issue-14227.rs:4:21 | LL | static CRASH: u32 = symbol; - | ^^^^^^ cannot access extern static (DefId(0:4 ~ issue_14227[1133]::{extern#0}::symbol)) + | ^^^^^^ cannot access extern static `symbol` error[E0133]: use of extern static is unsafe and requires unsafe function or block --> $DIR/issue-14227.rs:4:21 diff --git a/tests/ui/statics/issue-15261.rs b/tests/ui/statics/issue-15261.rs index ed79a201488f3..f8c5ebaa54840 100644 --- a/tests/ui/statics/issue-15261.rs +++ b/tests/ui/statics/issue-15261.rs @@ -2,10 +2,9 @@ #![allow(dead_code)] #![allow(non_upper_case_globals)] - static mut n_mut: usize = 0; static n: &'static usize = unsafe { &n_mut }; -//~^ WARN shared reference to mutable static is discouraged [static_mut_refs] +//~^ WARN shared reference to mutable static [static_mut_refs] fn main() {} diff --git a/tests/ui/statics/issue-15261.stderr b/tests/ui/statics/issue-15261.stderr index 7e6aebcbb1f0e..d2dd762aa66eb 100644 --- a/tests/ui/statics/issue-15261.stderr +++ b/tests/ui/statics/issue-15261.stderr @@ -1,5 +1,5 @@ -warning: creating a shared reference to mutable static is discouraged - --> $DIR/issue-15261.rs:8:37 +warning: creating a shared reference to mutable static + --> $DIR/issue-15261.rs:7:37 | LL | static n: &'static usize = unsafe { &n_mut }; | ^^^^^^ shared reference to mutable static diff --git a/tests/ui/statics/static-mut-shared-parens.rs b/tests/ui/statics/static-mut-shared-parens.rs index 8e58152e27acd..7eda7d67d9248 100644 --- a/tests/ui/statics/static-mut-shared-parens.rs +++ b/tests/ui/statics/static-mut-shared-parens.rs @@ -1,13 +1,12 @@ //Missing paren in diagnostic msg: https://github.com/rust-lang/rust/issues/131977 //@check-pass - static mut TEST: usize = 0; fn main() { let _ = unsafe { (&TEST) as *const usize }; - //~^WARN creating a shared reference to mutable static is discouraged + //~^WARN creating a shared reference to mutable static - let _ = unsafe { ((&mut TEST)) as *const usize }; - //~^WARN creating a mutable reference to mutable static is discouraged + let _ = unsafe { (&mut TEST) as *const usize }; + //~^WARN creating a mutable reference to mutable static } diff --git a/tests/ui/statics/static-mut-shared-parens.stderr b/tests/ui/statics/static-mut-shared-parens.stderr index 30a586c286ae7..3825e8efc4277 100644 --- a/tests/ui/statics/static-mut-shared-parens.stderr +++ b/tests/ui/statics/static-mut-shared-parens.stderr @@ -1,5 +1,5 @@ -warning: creating a shared reference to mutable static is discouraged - --> $DIR/static-mut-shared-parens.rs:8:22 +warning: creating a shared reference to mutable static + --> $DIR/static-mut-shared-parens.rs:7:22 | LL | let _ = unsafe { (&TEST) as *const usize }; | ^^^^^^^ shared reference to mutable static @@ -12,18 +12,18 @@ help: use `&raw const` instead to create a raw pointer LL | let _ = unsafe { (&raw const TEST) as *const usize }; | +++++++++ -warning: creating a mutable reference to mutable static is discouraged - --> $DIR/static-mut-shared-parens.rs:11:22 +warning: creating a mutable reference to mutable static + --> $DIR/static-mut-shared-parens.rs:10:22 | -LL | let _ = unsafe { ((&mut TEST)) as *const usize }; - | ^^^^^^^^^^^^^ mutable reference to mutable static +LL | let _ = unsafe { (&mut TEST) as *const usize }; + | ^^^^^^^^^^^ mutable reference to mutable static | = note: for more information, see = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives help: use `&raw mut` instead to create a raw pointer | -LL | let _ = unsafe { ((&raw mut TEST)) as *const usize }; - | +++ +LL | let _ = unsafe { (&raw mut TEST) as *const usize }; + | +++ warning: 2 warnings emitted diff --git a/tests/ui/statics/static-mut-xc.rs b/tests/ui/statics/static-mut-xc.rs index c23cc822ce741..878f5a8e0f70a 100644 --- a/tests/ui/statics/static-mut-xc.rs +++ b/tests/ui/statics/static-mut-xc.rs @@ -17,23 +17,23 @@ fn static_bound_set(a: &'static mut isize) { unsafe fn run() { assert_eq!(static_mut_xc::a, 3); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] static_mut_xc::a = 4; assert_eq!(static_mut_xc::a, 4); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] static_mut_xc::a += 1; assert_eq!(static_mut_xc::a, 5); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] static_mut_xc::a *= 3; assert_eq!(static_mut_xc::a, 15); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] static_mut_xc::a = -3; assert_eq!(static_mut_xc::a, -3); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] static_bound(&static_mut_xc::a); - //~^ WARN shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN shared reference to mutable static [static_mut_refs] static_bound_set(&mut static_mut_xc::a); - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] + //~^ WARN mutable reference to mutable static [static_mut_refs] } pub fn main() { diff --git a/tests/ui/statics/static-mut-xc.stderr b/tests/ui/statics/static-mut-xc.stderr index 69f334a5636af..2d7a0553e925f 100644 --- a/tests/ui/statics/static-mut-xc.stderr +++ b/tests/ui/statics/static-mut-xc.stderr @@ -1,4 +1,4 @@ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-xc.rs:19:16 | LL | assert_eq!(static_mut_xc::a, 3); @@ -8,7 +8,7 @@ LL | assert_eq!(static_mut_xc::a, 3); = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: `#[warn(static_mut_refs)]` on by default -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-xc.rs:22:16 | LL | assert_eq!(static_mut_xc::a, 4); @@ -17,7 +17,7 @@ LL | assert_eq!(static_mut_xc::a, 4); = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-xc.rs:25:16 | LL | assert_eq!(static_mut_xc::a, 5); @@ -26,7 +26,7 @@ LL | assert_eq!(static_mut_xc::a, 5); = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-xc.rs:28:16 | LL | assert_eq!(static_mut_xc::a, 15); @@ -35,7 +35,7 @@ LL | assert_eq!(static_mut_xc::a, 15); = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-xc.rs:31:16 | LL | assert_eq!(static_mut_xc::a, -3); @@ -44,7 +44,7 @@ LL | assert_eq!(static_mut_xc::a, -3); = note: for more information, see = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-xc.rs:33:18 | LL | static_bound(&static_mut_xc::a); @@ -57,7 +57,7 @@ help: use `&raw const` instead to create a raw pointer LL | static_bound(&raw const static_mut_xc::a); | +++++++++ -warning: creating a mutable reference to mutable static is discouraged +warning: creating a mutable reference to mutable static --> $DIR/static-mut-xc.rs:35:22 | LL | static_bound_set(&mut static_mut_xc::a); diff --git a/tests/ui/statics/static-recursive.rs b/tests/ui/statics/static-recursive.rs index da23b54d1fca0..dab60dd8641bd 100644 --- a/tests/ui/statics/static-recursive.rs +++ b/tests/ui/statics/static-recursive.rs @@ -1,7 +1,7 @@ //@ run-pass static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 }; -//~^ WARN shared reference to mutable static is discouraged [static_mut_refs] +//~^ WARN shared reference to mutable static [static_mut_refs] struct StaticDoubleLinked { prev: &'static StaticDoubleLinked, @@ -17,7 +17,7 @@ static L3: StaticDoubleLinked = StaticDoubleLinked { prev: &L2, next: &L1, data: pub fn main() { unsafe { assert_eq!(S, *(S as *const *const u8)); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] } let mut test_vec = Vec::new(); diff --git a/tests/ui/statics/static-recursive.stderr b/tests/ui/statics/static-recursive.stderr index 039934dfc692d..252807e2e5dea 100644 --- a/tests/ui/statics/static-recursive.stderr +++ b/tests/ui/statics/static-recursive.stderr @@ -1,4 +1,4 @@ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-recursive.rs:3:36 | LL | static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 }; @@ -12,7 +12,7 @@ help: use `&raw const` instead to create a raw pointer LL | static mut S: *const u8 = unsafe { &raw const S as *const *const u8 as *const u8 }; | +++++++++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-recursive.rs:19:20 | LL | assert_eq!(S, *(S as *const *const u8)); diff --git a/tests/ui/statics/unsized_type2.stderr b/tests/ui/statics/unsized_type2.stderr index ffbbe218c87a0..3f9b0879c166d 100644 --- a/tests/ui/statics/unsized_type2.stderr +++ b/tests/ui/statics/unsized_type2.stderr @@ -10,6 +10,7 @@ note: required because it appears within the type `Foo` | LL | pub struct Foo { | ^^^ + = note: statics and constants must have a statically known size error[E0308]: mismatched types --> $DIR/unsized_type2.rs:14:45 diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index 9b1568fa11665..b369af62f8760 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -5,55 +5,55 @@ ast-stats-1 Crate 40 ( 0.6%) 1 40 ast-stats-1 GenericArgs 40 ( 0.6%) 1 40 ast-stats-1 - AngleBracketed 40 ( 0.6%) 1 ast-stats-1 ExprField 48 ( 0.7%) 1 48 -ast-stats-1 Attribute 64 ( 1.0%) 2 32 +ast-stats-1 Attribute 64 ( 0.9%) 2 32 ast-stats-1 - DocComment 32 ( 0.5%) 1 ast-stats-1 - Normal 32 ( 0.5%) 1 -ast-stats-1 WherePredicate 64 ( 1.0%) 1 64 -ast-stats-1 - BoundPredicate 64 ( 1.0%) 1 -ast-stats-1 Local 80 ( 1.2%) 1 80 -ast-stats-1 ForeignItem 88 ( 1.3%) 1 88 -ast-stats-1 - Fn 88 ( 1.3%) 1 +ast-stats-1 WherePredicate 72 ( 1.1%) 1 72 +ast-stats-1 - BoundPredicate 72 ( 1.1%) 1 +ast-stats-1 ForeignItem 80 ( 1.2%) 1 80 +ast-stats-1 - Fn 80 ( 1.2%) 1 ast-stats-1 Arm 96 ( 1.4%) 2 48 +ast-stats-1 Local 96 ( 1.4%) 1 96 ast-stats-1 FnDecl 120 ( 1.8%) 5 24 ast-stats-1 Param 160 ( 2.4%) 4 40 ast-stats-1 Stmt 160 ( 2.4%) 5 32 ast-stats-1 - Let 32 ( 0.5%) 1 ast-stats-1 - MacCall 32 ( 0.5%) 1 ast-stats-1 - Expr 96 ( 1.4%) 3 -ast-stats-1 Block 192 ( 2.9%) 6 32 +ast-stats-1 Block 192 ( 2.8%) 6 32 ast-stats-1 FieldDef 208 ( 3.1%) 2 104 ast-stats-1 Variant 208 ( 3.1%) 2 104 -ast-stats-1 AssocItem 352 ( 5.3%) 4 88 -ast-stats-1 - Fn 176 ( 2.6%) 2 -ast-stats-1 - Type 176 ( 2.6%) 2 -ast-stats-1 GenericBound 352 ( 5.3%) 4 88 -ast-stats-1 - Trait 352 ( 5.3%) 4 -ast-stats-1 GenericParam 480 ( 7.2%) 5 96 +ast-stats-1 AssocItem 320 ( 4.7%) 4 80 +ast-stats-1 - Fn 160 ( 2.4%) 2 +ast-stats-1 - Type 160 ( 2.4%) 2 +ast-stats-1 GenericBound 352 ( 5.2%) 4 88 +ast-stats-1 - Trait 352 ( 5.2%) 4 +ast-stats-1 GenericParam 480 ( 7.1%) 5 96 ast-stats-1 Pat 504 ( 7.5%) 7 72 ast-stats-1 - Struct 72 ( 1.1%) 1 ast-stats-1 - Wild 72 ( 1.1%) 1 -ast-stats-1 - Ident 360 ( 5.4%) 5 -ast-stats-1 Expr 576 ( 8.6%) 8 72 +ast-stats-1 - Ident 360 ( 5.3%) 5 +ast-stats-1 Expr 576 ( 8.5%) 8 72 ast-stats-1 - Match 72 ( 1.1%) 1 ast-stats-1 - Path 72 ( 1.1%) 1 ast-stats-1 - Struct 72 ( 1.1%) 1 -ast-stats-1 - Lit 144 ( 2.2%) 2 +ast-stats-1 - Lit 144 ( 2.1%) 2 ast-stats-1 - Block 216 ( 3.2%) 3 -ast-stats-1 PathSegment 744 (11.1%) 31 24 -ast-stats-1 Ty 896 (13.4%) 14 64 -ast-stats-1 - Ptr 64 ( 1.0%) 1 -ast-stats-1 - Ref 64 ( 1.0%) 1 +ast-stats-1 PathSegment 744 (11.0%) 31 24 +ast-stats-1 Ty 896 (13.3%) 14 64 +ast-stats-1 - Ptr 64 ( 0.9%) 1 +ast-stats-1 - Ref 64 ( 0.9%) 1 ast-stats-1 - ImplicitSelf 128 ( 1.9%) 2 -ast-stats-1 - Path 640 ( 9.6%) 10 -ast-stats-1 Item 1_224 (18.3%) 9 136 -ast-stats-1 - Enum 136 ( 2.0%) 1 -ast-stats-1 - ForeignMod 136 ( 2.0%) 1 -ast-stats-1 - Impl 136 ( 2.0%) 1 -ast-stats-1 - Trait 136 ( 2.0%) 1 -ast-stats-1 - Fn 272 ( 4.1%) 2 -ast-stats-1 - Use 408 ( 6.1%) 3 +ast-stats-1 - Path 640 ( 9.5%) 10 +ast-stats-1 Item 1_296 (19.2%) 9 144 +ast-stats-1 - Enum 144 ( 2.1%) 1 +ast-stats-1 - ForeignMod 144 ( 2.1%) 1 +ast-stats-1 - Impl 144 ( 2.1%) 1 +ast-stats-1 - Trait 144 ( 2.1%) 1 +ast-stats-1 - Fn 288 ( 4.3%) 2 +ast-stats-1 - Use 432 ( 6.4%) 3 ast-stats-1 ---------------------------------------------------------------- -ast-stats-1 Total 6_696 116 +ast-stats-1 Total 6_752 116 ast-stats-1 ast-stats-2 POST EXPANSION AST STATS ast-stats-2 Name Accumulated Size Count Item Size @@ -61,13 +61,13 @@ ast-stats-2 ---------------------------------------------------------------- ast-stats-2 Crate 40 ( 0.5%) 1 40 ast-stats-2 GenericArgs 40 ( 0.5%) 1 40 ast-stats-2 - AngleBracketed 40 ( 0.5%) 1 -ast-stats-2 ExprField 48 ( 0.7%) 1 48 -ast-stats-2 WherePredicate 64 ( 0.9%) 1 64 -ast-stats-2 - BoundPredicate 64 ( 0.9%) 1 -ast-stats-2 Local 80 ( 1.1%) 1 80 -ast-stats-2 ForeignItem 88 ( 1.2%) 1 88 -ast-stats-2 - Fn 88 ( 1.2%) 1 +ast-stats-2 ExprField 48 ( 0.6%) 1 48 +ast-stats-2 WherePredicate 72 ( 1.0%) 1 72 +ast-stats-2 - BoundPredicate 72 ( 1.0%) 1 +ast-stats-2 ForeignItem 80 ( 1.1%) 1 80 +ast-stats-2 - Fn 80 ( 1.1%) 1 ast-stats-2 Arm 96 ( 1.3%) 2 48 +ast-stats-2 Local 96 ( 1.3%) 1 96 ast-stats-2 FnDecl 120 ( 1.6%) 5 24 ast-stats-2 InlineAsm 120 ( 1.6%) 1 120 ast-stats-2 Attribute 128 ( 1.7%) 4 32 @@ -81,56 +81,56 @@ ast-stats-2 - Expr 96 ( 1.3%) 3 ast-stats-2 Block 192 ( 2.6%) 6 32 ast-stats-2 FieldDef 208 ( 2.8%) 2 104 ast-stats-2 Variant 208 ( 2.8%) 2 104 -ast-stats-2 AssocItem 352 ( 4.8%) 4 88 -ast-stats-2 - Fn 176 ( 2.4%) 2 -ast-stats-2 - Type 176 ( 2.4%) 2 -ast-stats-2 GenericBound 352 ( 4.8%) 4 88 -ast-stats-2 - Trait 352 ( 4.8%) 4 +ast-stats-2 AssocItem 320 ( 4.3%) 4 80 +ast-stats-2 - Fn 160 ( 2.2%) 2 +ast-stats-2 - Type 160 ( 2.2%) 2 +ast-stats-2 GenericBound 352 ( 4.7%) 4 88 +ast-stats-2 - Trait 352 ( 4.7%) 4 ast-stats-2 GenericParam 480 ( 6.5%) 5 96 -ast-stats-2 Pat 504 ( 6.9%) 7 72 +ast-stats-2 Pat 504 ( 6.8%) 7 72 ast-stats-2 - Struct 72 ( 1.0%) 1 ast-stats-2 - Wild 72 ( 1.0%) 1 ast-stats-2 - Ident 360 ( 4.9%) 5 -ast-stats-2 Expr 648 ( 8.8%) 9 72 +ast-stats-2 Expr 648 ( 8.7%) 9 72 ast-stats-2 - InlineAsm 72 ( 1.0%) 1 ast-stats-2 - Match 72 ( 1.0%) 1 ast-stats-2 - Path 72 ( 1.0%) 1 ast-stats-2 - Struct 72 ( 1.0%) 1 -ast-stats-2 - Lit 144 ( 2.0%) 2 +ast-stats-2 - Lit 144 ( 1.9%) 2 ast-stats-2 - Block 216 ( 2.9%) 3 -ast-stats-2 PathSegment 864 (11.8%) 36 24 -ast-stats-2 Ty 896 (12.2%) 14 64 +ast-stats-2 PathSegment 864 (11.7%) 36 24 +ast-stats-2 Ty 896 (12.1%) 14 64 ast-stats-2 - Ptr 64 ( 0.9%) 1 ast-stats-2 - Ref 64 ( 0.9%) 1 ast-stats-2 - ImplicitSelf 128 ( 1.7%) 2 -ast-stats-2 - Path 640 ( 8.7%) 10 -ast-stats-2 Item 1_496 (20.4%) 11 136 -ast-stats-2 - Enum 136 ( 1.9%) 1 -ast-stats-2 - ExternCrate 136 ( 1.9%) 1 -ast-stats-2 - ForeignMod 136 ( 1.9%) 1 -ast-stats-2 - Impl 136 ( 1.9%) 1 -ast-stats-2 - Trait 136 ( 1.9%) 1 -ast-stats-2 - Fn 272 ( 3.7%) 2 -ast-stats-2 - Use 544 ( 7.4%) 4 +ast-stats-2 - Path 640 ( 8.6%) 10 +ast-stats-2 Item 1_584 (21.4%) 11 144 +ast-stats-2 - Enum 144 ( 1.9%) 1 +ast-stats-2 - ExternCrate 144 ( 1.9%) 1 +ast-stats-2 - ForeignMod 144 ( 1.9%) 1 +ast-stats-2 - Impl 144 ( 1.9%) 1 +ast-stats-2 - Trait 144 ( 1.9%) 1 +ast-stats-2 - Fn 288 ( 3.9%) 2 +ast-stats-2 - Use 576 ( 7.8%) 4 ast-stats-2 ---------------------------------------------------------------- -ast-stats-2 Total 7_344 127 +ast-stats-2 Total 7_416 127 ast-stats-2 hir-stats HIR STATS hir-stats Name Accumulated Size Count Item Size hir-stats ---------------------------------------------------------------- hir-stats ForeignItemRef 24 ( 0.3%) 1 24 -hir-stats Lifetime 24 ( 0.3%) 1 24 +hir-stats Lifetime 28 ( 0.3%) 1 28 hir-stats Mod 32 ( 0.4%) 1 32 hir-stats ExprField 40 ( 0.4%) 1 40 hir-stats TraitItemRef 56 ( 0.6%) 2 28 hir-stats GenericArg 64 ( 0.7%) 4 16 hir-stats - Type 16 ( 0.2%) 1 hir-stats - Lifetime 48 ( 0.5%) 3 -hir-stats Local 64 ( 0.7%) 1 64 hir-stats Param 64 ( 0.7%) 2 32 hir-stats Body 72 ( 0.8%) 3 24 hir-stats ImplItemRef 72 ( 0.8%) 2 36 hir-stats InlineAsm 72 ( 0.8%) 1 72 +hir-stats Local 72 ( 0.8%) 1 72 hir-stats WherePredicate 72 ( 0.8%) 3 24 hir-stats - BoundPredicate 72 ( 0.8%) 3 hir-stats Arm 80 ( 0.9%) 2 40 @@ -143,8 +143,8 @@ hir-stats Attribute 128 ( 1.4%) 4 32 hir-stats FieldDef 128 ( 1.4%) 2 64 hir-stats GenericArgs 144 ( 1.6%) 3 48 hir-stats Variant 144 ( 1.6%) 2 72 -hir-stats GenericBound 256 ( 2.9%) 4 64 -hir-stats - Trait 256 ( 2.9%) 4 +hir-stats GenericBound 256 ( 2.8%) 4 64 +hir-stats - Trait 256 ( 2.8%) 4 hir-stats Block 288 ( 3.2%) 6 48 hir-stats Pat 360 ( 4.0%) 5 72 hir-stats - Struct 72 ( 0.8%) 1 @@ -155,8 +155,8 @@ hir-stats Generics 560 ( 6.2%) 10 56 hir-stats Ty 720 ( 8.0%) 15 48 hir-stats - Ptr 48 ( 0.5%) 1 hir-stats - Ref 48 ( 0.5%) 1 -hir-stats - Path 624 ( 7.0%) 13 -hir-stats Expr 768 ( 8.6%) 12 64 +hir-stats - Path 624 ( 6.9%) 13 +hir-stats Expr 768 ( 8.5%) 12 64 hir-stats - InlineAsm 64 ( 0.7%) 1 hir-stats - Match 64 ( 0.7%) 1 hir-stats - Path 64 ( 0.7%) 1 @@ -174,5 +174,5 @@ hir-stats - Use 352 ( 3.9%) 4 hir-stats Path 1_240 (13.8%) 31 40 hir-stats PathSegment 1_920 (21.4%) 40 48 hir-stats ---------------------------------------------------------------- -hir-stats Total 8_976 180 +hir-stats Total 8_988 180 hir-stats diff --git a/tests/ui/std/channel-stack-overflow-issue-102246.rs b/tests/ui/std/channel-stack-overflow-issue-102246.rs index 984ebdd553fa0..7bf6647bdc5f4 100644 --- a/tests/ui/std/channel-stack-overflow-issue-102246.rs +++ b/tests/ui/std/channel-stack-overflow-issue-102246.rs @@ -10,9 +10,16 @@ // Ref: https://github.com/rust-lang/rust/issues/102246 use std::sync::mpsc::channel; -use std::thread; +use std::thread::Builder; const N: usize = 32_768; +const SLOTS: usize = 32; +// Use a stack size that's smaller than N * SLOTS, proving the allocation is on the heap. +// +// The test explicitly specifies the stack size, because not all platforms have the same default +// size. +const STACK_SIZE: usize = (N*SLOTS) - 1; + struct BigStruct { _data: [u8; N], } @@ -20,10 +27,13 @@ struct BigStruct { fn main() { let (sender, receiver) = channel::(); - let thread1 = thread::spawn(move || { + let thread1 = Builder::new().stack_size(STACK_SIZE).spawn(move || { sender.send(BigStruct { _data: [0u8; N] }).unwrap(); - }); - + }).expect("thread1 should spawn successfully"); thread1.join().unwrap(); - for _data in receiver.try_iter() {} + + let thread2 = Builder::new().stack_size(STACK_SIZE).spawn(move || { + for _data in receiver.try_iter() {} + }).expect("thread2 should spawn successfully"); + thread2.join().unwrap(); } diff --git a/tests/ui/str/str-idx.stderr b/tests/ui/str/str-idx.stderr index bedbbd9cb5029..60cae7e84e373 100644 --- a/tests/ui/str/str-idx.stderr +++ b/tests/ui/str/str-idx.stderr @@ -4,11 +4,12 @@ error[E0277]: the type `str` cannot be indexed by `{integer}` LL | let _: u8 = s[4]; | ^ string indices are ranges of `usize` | + = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` for more information, see chapter 8 in The Book: - = help: the trait `SliceIndex` is not implemented for `{integer}` - but trait `SliceIndex<[_]>` is implemented for `usize` - = help: for that trait implementation, expected `[_]`, found `str` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `str` to implement `Index<{integer}>` error[E0277]: the type `str` cannot be indexed by `{integer}` @@ -19,11 +20,12 @@ LL | let _ = s.get(4); | | | required by a bound introduced by this call | + = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` for more information, see chapter 8 in The Book: - = help: the trait `SliceIndex` is not implemented for `{integer}` - but trait `SliceIndex<[_]>` is implemented for `usize` - = help: for that trait implementation, expected `[_]`, found `str` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` note: required by a bound in `core::str::::get` --> $SRC_DIR/core/src/str/mod.rs:LL:COL @@ -35,11 +37,12 @@ LL | let _ = s.get_unchecked(4); | | | required by a bound introduced by this call | + = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` for more information, see chapter 8 in The Book: - = help: the trait `SliceIndex` is not implemented for `{integer}` - but trait `SliceIndex<[_]>` is implemented for `usize` - = help: for that trait implementation, expected `[_]`, found `str` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` note: required by a bound in `core::str::::get_unchecked` --> $SRC_DIR/core/src/str/mod.rs:LL:COL diff --git a/tests/ui/str/str-mut-idx.stderr b/tests/ui/str/str-mut-idx.stderr index f09a4c329e5d8..4e3fe126ed838 100644 --- a/tests/ui/str/str-mut-idx.stderr +++ b/tests/ui/str/str-mut-idx.stderr @@ -31,8 +31,9 @@ LL | s[1usize] = bot(); | ^^^^^^ string indices are ranges of `usize` | = help: the trait `SliceIndex` is not implemented for `usize` - but trait `SliceIndex<[_]>` is implemented for it - = help: for that trait implementation, expected `[_]`, found `str` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `str` to implement `Index` error[E0277]: the type `str` cannot be indexed by `{integer}` @@ -43,11 +44,12 @@ LL | s.get_mut(1); | | | required by a bound introduced by this call | + = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` for more information, see chapter 8 in The Book: - = help: the trait `SliceIndex` is not implemented for `{integer}` - but trait `SliceIndex<[_]>` is implemented for `usize` - = help: for that trait implementation, expected `[_]`, found `str` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` note: required by a bound in `core::str::::get_mut` --> $SRC_DIR/core/src/str/mod.rs:LL:COL @@ -59,11 +61,12 @@ LL | s.get_unchecked_mut(1); | | | required by a bound introduced by this call | + = help: the trait `SliceIndex` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` for more information, see chapter 8 in The Book: - = help: the trait `SliceIndex` is not implemented for `{integer}` - but trait `SliceIndex<[_]>` is implemented for `usize` - = help: for that trait implementation, expected `[_]`, found `str` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` note: required by a bound in `core::str::::get_unchecked_mut` --> $SRC_DIR/core/src/str/mod.rs:LL:COL diff --git a/tests/ui/structs/default-field-values/do-not-ice-on-invalid-lifetime.rs b/tests/ui/structs/default-field-values/do-not-ice-on-invalid-lifetime.rs index 71d90ddd935ff..ba99b03539cf6 100644 --- a/tests/ui/structs/default-field-values/do-not-ice-on-invalid-lifetime.rs +++ b/tests/ui/structs/default-field-values/do-not-ice-on-invalid-lifetime.rs @@ -1,5 +1,5 @@ #![feature(default_field_values)] -struct A<'a> { //~ ERROR lifetime parameter `'a` is never used +struct A<'a> { x: Vec = Vec::new(), //~ ERROR missing lifetime specifier } diff --git a/tests/ui/structs/default-field-values/do-not-ice-on-invalid-lifetime.stderr b/tests/ui/structs/default-field-values/do-not-ice-on-invalid-lifetime.stderr index 20b9afe80cdc8..7100efc695cef 100644 --- a/tests/ui/structs/default-field-values/do-not-ice-on-invalid-lifetime.stderr +++ b/tests/ui/structs/default-field-values/do-not-ice-on-invalid-lifetime.stderr @@ -9,15 +9,6 @@ help: consider using the `'a` lifetime LL | x: Vec> = Vec::new(), | ++++ -error[E0392]: lifetime parameter `'a` is never used - --> $DIR/do-not-ice-on-invalid-lifetime.rs:2:10 - | -LL | struct A<'a> { - | ^^ unused lifetime parameter - | - = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0106, E0392. -For more information about an error, try `rustc --explain E0106`. +For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/structs/default-field-values/failures.rs b/tests/ui/structs/default-field-values/failures.rs index 0ac071d91d65e..dee6566bd0ea1 100644 --- a/tests/ui/structs/default-field-values/failures.rs +++ b/tests/ui/structs/default-field-values/failures.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: HELP + #![feature(default_field_values)] #[derive(Debug)] @@ -17,9 +19,9 @@ pub struct Bar { #[derive(Default)] pub struct Qux { - bar: S = Self::S, //~ ERROR generic `Self` types are currently not permitted in anonymous constants + bar: S = Self::S, baz: i32 = foo(), - bat: i32 = as T>::K, //~ ERROR generic parameters may not be used in const operations + bat: i32 = as T>::K, bay: i32 = C, } @@ -50,15 +52,16 @@ enum E { fn main () { let _ = Foo { .. }; // ok let _ = Foo::default(); // ok - let _ = Bar { .. }; //~ ERROR mandatory field + let _ = Bar { .. }; //~ ERROR missing field + let _ = Bar { baz: 0, .. }; //~ ERROR missing field let _ = Bar::default(); // silenced let _ = Bar { bar: S, .. }; // ok let _ = Qux::<4> { .. }; let _ = Rak(..); //~ ERROR E0308 - //~^ you might have meant to use `..` to skip providing + //~^ HELP you might have meant to use `..` to skip providing let _ = Rak(0, ..); //~ ERROR E0061 - //~^ you might have meant to use `..` to skip providing + //~^ HELP you might have meant to use `..` to skip providing let _ = Rak(.., 0); //~ ERROR E0061 - //~^ you might have meant to use `..` to skip providing + //~^ HELP you might have meant to use `..` to skip providing let _ = Rak { .. }; // ok } diff --git a/tests/ui/structs/default-field-values/failures.stderr b/tests/ui/structs/default-field-values/failures.stderr index 65ec100fe2ea3..aaa75fd3180c5 100644 --- a/tests/ui/structs/default-field-values/failures.stderr +++ b/tests/ui/structs/default-field-values/failures.stderr @@ -1,34 +1,19 @@ error: the `#[default]` attribute may only be used on unit enum variants or variants where every field has a default value - --> $DIR/failures.rs:47:5 + --> $DIR/failures.rs:49:5 | LL | Variant {} | ^^^^^^^ | = help: consider a manual implementation of `Default` -error: generic parameters may not be used in const operations - --> $DIR/failures.rs:22:23 - | -LL | bat: i32 = as T>::K, - | ^ cannot perform const operation using `C` - | - = help: const parameters may only be used as standalone arguments, i.e. `C` - = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions - error: default fields are not supported in tuple structs - --> $DIR/failures.rs:26:22 + --> $DIR/failures.rs:28:22 | LL | pub struct Rak(i32 = 42); | ^^ default fields are only supported on structs -error: generic `Self` types are currently not permitted in anonymous constants - --> $DIR/failures.rs:20:14 - | -LL | bar: S = Self::S, - | ^^^^ - error[E0277]: the trait bound `S: Default` is not satisfied - --> $DIR/failures.rs:14:5 + --> $DIR/failures.rs:16:5 | LL | #[derive(Debug, Default)] | ------- in this derive macro expansion @@ -36,21 +21,26 @@ LL | pub struct Bar { LL | pub bar: S, | ^^^^^^^^^^ the trait `Default` is not implemented for `S` | - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `S` with `#[derive(Default)]` | LL + #[derive(Default)] LL | pub struct S; | -error: missing mandatory field `bar` - --> $DIR/failures.rs:53:21 +error: missing field `bar` in initializer + --> $DIR/failures.rs:55:19 | LL | let _ = Bar { .. }; - | ^ + | ^ fields that do not have a defaulted value must be provided explicitly + +error: missing field `bar` in initializer + --> $DIR/failures.rs:56:27 + | +LL | let _ = Bar { baz: 0, .. }; + | ^ fields that do not have a defaulted value must be provided explicitly error[E0308]: mismatched types - --> $DIR/failures.rs:57:17 + --> $DIR/failures.rs:60:17 | LL | let _ = Rak(..); | --- ^^ expected `i32`, found `RangeFull` @@ -58,29 +48,29 @@ LL | let _ = Rak(..); | arguments to this struct are incorrect | note: tuple struct defined here - --> $DIR/failures.rs:26:12 + --> $DIR/failures.rs:28:12 | LL | pub struct Rak(i32 = 42); | ^^^ help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal - --> $DIR/failures.rs:57:17 + --> $DIR/failures.rs:60:17 | LL | let _ = Rak(..); | ^^ error[E0061]: this struct takes 1 argument but 2 arguments were supplied - --> $DIR/failures.rs:59:13 + --> $DIR/failures.rs:62:13 | LL | let _ = Rak(0, ..); | ^^^ -- unexpected argument #2 of type `RangeFull` | help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal - --> $DIR/failures.rs:59:20 + --> $DIR/failures.rs:62:20 | LL | let _ = Rak(0, ..); | ^^ note: tuple struct defined here - --> $DIR/failures.rs:26:12 + --> $DIR/failures.rs:28:12 | LL | pub struct Rak(i32 = 42); | ^^^ @@ -91,18 +81,18 @@ LL + let _ = Rak(0); | error[E0061]: this struct takes 1 argument but 2 arguments were supplied - --> $DIR/failures.rs:61:13 + --> $DIR/failures.rs:64:13 | LL | let _ = Rak(.., 0); | ^^^ -- unexpected argument #1 of type `RangeFull` | help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal - --> $DIR/failures.rs:61:17 + --> $DIR/failures.rs:64:17 | LL | let _ = Rak(.., 0); | ^^ note: tuple struct defined here - --> $DIR/failures.rs:26:12 + --> $DIR/failures.rs:28:12 | LL | pub struct Rak(i32 = 42); | ^^^ @@ -112,7 +102,7 @@ LL - let _ = Rak(.., 0); LL + let _ = Rak(0); | -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors Some errors have detailed explanations: E0061, E0277, E0308. For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/structs/default-field-values/field-references-param.rs b/tests/ui/structs/default-field-values/field-references-param.rs new file mode 100644 index 0000000000000..ecee37edd42c0 --- /dev/null +++ b/tests/ui/structs/default-field-values/field-references-param.rs @@ -0,0 +1,29 @@ +//@ build-pass + +#![feature(default_field_values)] + +struct W; + +impl W { + const fn new() -> Self { W } +} + +struct Z { + // No inference. + one: W = W::::new(), + + // Inference works too. + two: W = W::new(), + + // An anon const that is too generic before substitution. + too_generic: usize = X + 1, +} + +fn use_generically() { + let x: Z = Z { .. }; +} + +fn main() { + let x: Z<0> = Z { .. }; + use_generically::<0>(); +} diff --git a/tests/ui/structs/default-field-values/invalid-const.stderr b/tests/ui/structs/default-field-values/invalid-const.stderr index 56d20d8d71115..030bdbfcc3ea9 100644 --- a/tests/ui/structs/default-field-values/invalid-const.stderr +++ b/tests/ui/structs/default-field-values/invalid-const.stderr @@ -3,8 +3,6 @@ error[E0080]: evaluation of constant value failed | LL | pub bax: u8 = panic!("asdf"), | ^^^^^^^^^^^^^^ evaluation panicked: asdf - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of `Baz::::bat::{constant#0}` failed --> $DIR/invalid-const.rs:11:19 diff --git a/tests/ui/structs/default-field-values/post-mono.direct.stderr b/tests/ui/structs/default-field-values/post-mono.direct.stderr new file mode 100644 index 0000000000000..cdd80620c48a9 --- /dev/null +++ b/tests/ui/structs/default-field-values/post-mono.direct.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Z::<1>::post_mono::{constant#0}` failed + --> $DIR/post-mono.rs:7:24 + | +LL | post_mono: usize = X / 0, + | ^^^^^ attempt to divide `1_usize` by zero + +note: erroneous constant encountered + --> $DIR/post-mono.rs:17:19 + | +LL | let x: Z<1> = Z { .. }; + | ^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/post-mono.rs:17:19 + | +LL | let x: Z<1> = Z { .. }; + | ^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/structs/default-field-values/post-mono.indirect.stderr b/tests/ui/structs/default-field-values/post-mono.indirect.stderr new file mode 100644 index 0000000000000..56c27a6e5dc81 --- /dev/null +++ b/tests/ui/structs/default-field-values/post-mono.indirect.stderr @@ -0,0 +1,29 @@ +error[E0080]: evaluation of `Z::<1>::post_mono::{constant#0}` failed + --> $DIR/post-mono.rs:7:24 + | +LL | post_mono: usize = X / 0, + | ^^^^^ attempt to divide `1_usize` by zero + +note: erroneous constant encountered + --> $DIR/post-mono.rs:12:19 + | +LL | let x: Z = Z { .. }; + | ^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/post-mono.rs:12:19 + | +LL | let x: Z = Z { .. }; + | ^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +note: the above error was encountered while instantiating `fn indirect::<1>` + --> $DIR/post-mono.rs:22:5 + | +LL | indirect::<1>(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/structs/default-field-values/post-mono.rs b/tests/ui/structs/default-field-values/post-mono.rs new file mode 100644 index 0000000000000..4de31f6e2fbac --- /dev/null +++ b/tests/ui/structs/default-field-values/post-mono.rs @@ -0,0 +1,23 @@ +//@ build-fail +//@ revisions: direct indirect + +#![feature(default_field_values)] + +struct Z { + post_mono: usize = X / 0, + //~^ ERROR evaluation of `Z::<1>::post_mono::{constant#0}` failed +} + +fn indirect() { + let x: Z = Z { .. }; +} + +#[cfg(direct)] +fn main() { + let x: Z<1> = Z { .. }; +} + +#[cfg(indirect)] +fn main() { + indirect::<1>(); +} diff --git a/tests/ui/structs/ident-from-macro-expansion.rs b/tests/ui/structs/ident-from-macro-expansion.rs new file mode 100644 index 0000000000000..56d31a4256168 --- /dev/null +++ b/tests/ui/structs/ident-from-macro-expansion.rs @@ -0,0 +1,19 @@ +struct Foo { + inner: Inner, +} + +struct Inner { + y: i32, +} + +macro_rules! access { + ($expr:expr, $ident:ident) => { + $expr.$ident + } +} + +fn main() { + let k = Foo { inner: Inner { y: 0 } }; + access!(k, y); + //~^ ERROR no field `y` on type `Foo` +} diff --git a/tests/ui/structs/ident-from-macro-expansion.stderr b/tests/ui/structs/ident-from-macro-expansion.stderr new file mode 100644 index 0000000000000..be2ab7c2e9966 --- /dev/null +++ b/tests/ui/structs/ident-from-macro-expansion.stderr @@ -0,0 +1,14 @@ +error[E0609]: no field `y` on type `Foo` + --> $DIR/ident-from-macro-expansion.rs:17:16 + | +LL | $expr.$ident + | ------ due to this macro variable +... +LL | access!(k, y); + | ^ unknown field + | + = note: available field is: `inner` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0609`. diff --git a/tests/ui/structs/struct-construct-with-call-issue-138931.rs b/tests/ui/structs/struct-construct-with-call-issue-138931.rs new file mode 100644 index 0000000000000..5d50eb14bff1a --- /dev/null +++ b/tests/ui/structs/struct-construct-with-call-issue-138931.rs @@ -0,0 +1,25 @@ +struct PersonOnlyName { + name: String +} + +struct PersonWithAge { + name: String, + age: u8, + height: u8, +} + + + +fn main() { + let wilfred = PersonOnlyName("Name1".to_owned()); + //~^ ERROR expected function, tuple struct or tuple variant, found struct `PersonOnlyName` [E0423] + + let bill = PersonWithAge( //~ ERROR expected function, tuple struct or tuple variant, found struct `PersonWithAge` [E0423] + "Name2".to_owned(), + 20, + 180, + ); + + let person = PersonWithAge("Name3".to_owned()); + //~^ ERROR expected function, tuple struct or tuple variant, found struct `PersonWithAge` [E0423] +} diff --git a/tests/ui/structs/struct-construct-with-call-issue-138931.stderr b/tests/ui/structs/struct-construct-with-call-issue-138931.stderr new file mode 100644 index 0000000000000..acae01df56361 --- /dev/null +++ b/tests/ui/structs/struct-construct-with-call-issue-138931.stderr @@ -0,0 +1,58 @@ +error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonOnlyName` + --> $DIR/struct-construct-with-call-issue-138931.rs:14:19 + | +LL | / struct PersonOnlyName { +LL | | name: String +LL | | } + | |_- `PersonOnlyName` defined here +... +LL | let wilfred = PersonOnlyName("Name1".to_owned()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use struct literal syntax instead of calling + | +LL - let wilfred = PersonOnlyName("Name1".to_owned()); +LL + let wilfred = PersonOnlyName{name: "Name1".to_owned()}; + | + +error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonWithAge` + --> $DIR/struct-construct-with-call-issue-138931.rs:17:16 + | +LL | / struct PersonWithAge { +LL | | name: String, +LL | | age: u8, +LL | | height: u8, +LL | | } + | |_- `PersonWithAge` defined here +... +LL | let bill = PersonWithAge( + | ________________^ +LL | | "Name2".to_owned(), +LL | | 20, +LL | | 180, +LL | | ); + | |_____^ + | +help: use struct literal syntax instead of calling + | +LL ~ let bill = PersonWithAge{name: "Name2".to_owned(), +LL ~ age: 20, +LL ~ height: 180}; + | + +error[E0423]: expected function, tuple struct or tuple variant, found struct `PersonWithAge` + --> $DIR/struct-construct-with-call-issue-138931.rs:23:18 + | +LL | / struct PersonWithAge { +LL | | name: String, +LL | | age: u8, +LL | | height: u8, +LL | | } + | |_- `PersonWithAge` defined here +... +LL | let person = PersonWithAge("Name3".to_owned()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `PersonWithAge { name: val, age: val, height: val }` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/structs/struct-missing-comma.fixed b/tests/ui/structs/struct-missing-comma.fixed index 800128f503bd6..cd02f61f1a46b 100644 --- a/tests/ui/structs/struct-missing-comma.fixed +++ b/tests/ui/structs/struct-missing-comma.fixed @@ -2,7 +2,7 @@ //@ run-rustfix pub struct S { - pub foo: u32, //~ expected `,`, or `}`, found keyword `pub` + pub foo: u32, //~ ERROR expected `,`, or `}`, found keyword `pub` // ~^ HELP try adding a comma: ',' pub bar: u32 } diff --git a/tests/ui/structs/struct-missing-comma.rs b/tests/ui/structs/struct-missing-comma.rs index b9d8c9eb30344..84944519fba94 100644 --- a/tests/ui/structs/struct-missing-comma.rs +++ b/tests/ui/structs/struct-missing-comma.rs @@ -2,7 +2,7 @@ //@ run-rustfix pub struct S { - pub foo: u32 //~ expected `,`, or `}`, found keyword `pub` + pub foo: u32 //~ ERROR expected `,`, or `}`, found keyword `pub` // ~^ HELP try adding a comma: ',' pub bar: u32 } diff --git a/tests/ui/structs/structure-constructor-type-mismatch.rs b/tests/ui/structs/structure-constructor-type-mismatch.rs index 21cd9d08b21d1..495deb01ea918 100644 --- a/tests/ui/structs/structure-constructor-type-mismatch.rs +++ b/tests/ui/structs/structure-constructor-type-mismatch.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + struct Point { x: T, y: T, @@ -16,32 +18,32 @@ fn main() { let pt = PointF { x: 1, //~^ ERROR mismatched types - //~| expected `f32`, found integer + //~| NOTE expected `f32`, found integer y: 2, //~^ ERROR mismatched types - //~| expected `f32`, found integer + //~| NOTE expected `f32`, found integer }; let pt2 = Point:: { x: 3, //~^ ERROR mismatched types - //~| expected `f32`, found integer + //~| NOTE expected `f32`, found integer y: 4, //~^ ERROR mismatched types - //~| expected `f32`, found integer + //~| NOTE expected `f32`, found integer }; let pair = PairF { x: 5, //~^ ERROR mismatched types - //~| expected `f32`, found integer + //~| NOTE expected `f32`, found integer y: 6, }; let pair2 = PairF:: { x: 7, //~^ ERROR mismatched types - //~| expected `f32`, found integer + //~| NOTE expected `f32`, found integer y: 8, }; diff --git a/tests/ui/structs/structure-constructor-type-mismatch.stderr b/tests/ui/structs/structure-constructor-type-mismatch.stderr index 819b65ffb716e..70518919daad4 100644 --- a/tests/ui/structs/structure-constructor-type-mismatch.stderr +++ b/tests/ui/structs/structure-constructor-type-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:17:12 + --> $DIR/structure-constructor-type-mismatch.rs:19:12 | LL | x: 1, | ^ expected `f32`, found integer @@ -10,7 +10,7 @@ LL | x: 1.0, | ++ error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:20:12 + --> $DIR/structure-constructor-type-mismatch.rs:22:12 | LL | y: 2, | ^ expected `f32`, found integer @@ -21,7 +21,7 @@ LL | y: 2.0, | ++ error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:26:12 + --> $DIR/structure-constructor-type-mismatch.rs:28:12 | LL | x: 3, | ^ expected `f32`, found integer @@ -32,7 +32,7 @@ LL | x: 3.0, | ++ error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:29:12 + --> $DIR/structure-constructor-type-mismatch.rs:31:12 | LL | y: 4, | ^ expected `f32`, found integer @@ -43,7 +43,7 @@ LL | y: 4.0, | ++ error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:35:12 + --> $DIR/structure-constructor-type-mismatch.rs:37:12 | LL | x: 5, | ^ expected `f32`, found integer @@ -54,7 +54,7 @@ LL | x: 5.0, | ++ error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:42:12 + --> $DIR/structure-constructor-type-mismatch.rs:44:12 | LL | x: 7, | ^ expected `f32`, found integer @@ -65,7 +65,7 @@ LL | x: 7.0, | ++ error[E0107]: type alias takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/structure-constructor-type-mismatch.rs:48:15 + --> $DIR/structure-constructor-type-mismatch.rs:50:15 | LL | let pt3 = PointF:: { | ^^^^^^------- help: remove the unnecessary generics @@ -73,13 +73,13 @@ LL | let pt3 = PointF:: { | expected 0 generic arguments | note: type alias defined here, with 0 generic parameters - --> $DIR/structure-constructor-type-mismatch.rs:6:6 + --> $DIR/structure-constructor-type-mismatch.rs:8:6 | LL | type PointF = Point; | ^^^^^^ error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:49:12 + --> $DIR/structure-constructor-type-mismatch.rs:51:12 | LL | x: 9, | ^ expected `f32`, found integer @@ -90,7 +90,7 @@ LL | x: 9.0, | ++ error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:50:12 + --> $DIR/structure-constructor-type-mismatch.rs:52:12 | LL | y: 10, | ^^ expected `f32`, found integer @@ -101,7 +101,7 @@ LL | y: 10.0, | ++ error[E0107]: type alias takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/structure-constructor-type-mismatch.rs:54:9 + --> $DIR/structure-constructor-type-mismatch.rs:56:9 | LL | PointF:: { .. } => {} | ^^^^^^------- help: remove the unnecessary generics @@ -109,13 +109,13 @@ LL | PointF:: { .. } => {} | expected 0 generic arguments | note: type alias defined here, with 0 generic parameters - --> $DIR/structure-constructor-type-mismatch.rs:6:6 + --> $DIR/structure-constructor-type-mismatch.rs:8:6 | LL | type PointF = Point; | ^^^^^^ error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:54:9 + --> $DIR/structure-constructor-type-mismatch.rs:56:9 | LL | match (Point { x: 1, y: 2 }) { | ---------------------- this expression has type `Point<{integer}>` @@ -126,7 +126,7 @@ LL | PointF:: { .. } => {} found struct `Point` error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:59:9 + --> $DIR/structure-constructor-type-mismatch.rs:61:9 | LL | match (Point { x: 1, y: 2 }) { | ---------------------- this expression has type `Point<{integer}>` @@ -137,7 +137,7 @@ LL | PointF { .. } => {} found struct `Point` error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:67:9 + --> $DIR/structure-constructor-type-mismatch.rs:69:9 | LL | match (Pair { x: 1, y: 2 }) { | --------------------- this expression has type `Pair<{integer}, {integer}>` diff --git a/tests/ui/structs/suggest-replacing-field-when-specifying-same-type.rs b/tests/ui/structs/suggest-replacing-field-when-specifying-same-type.rs index dd2fe79731eac..ab5815e23a612 100644 --- a/tests/ui/structs/suggest-replacing-field-when-specifying-same-type.rs +++ b/tests/ui/structs/suggest-replacing-field-when-specifying-same-type.rs @@ -22,7 +22,7 @@ fn main() { //~| ERROR pattern does not mention field `b` [E0027] Foo::Baz { bb: "" } => (), //~^ ERROR variant `Foo::Baz` does not have a field named `bb` [E0026] - //~| pattern does not mention field `a` [E0027] + //~| ERROR pattern does not mention field `a` [E0027] _ => (), } } diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed deleted file mode 100644 index f5dbf0c8b6f4e..0000000000000 --- a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-rustfix - -use std::fmt; - -struct Hello; - -impl fmt::Display for Hello { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { //~ ERROR path separator must be a double colon - write!(f, "hello") - } -} - -fn main() { - let _ = Hello; -} diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs deleted file mode 100644 index c41880a26f6ec..0000000000000 --- a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-rustfix - -use std::fmt; - -struct Hello; - -impl fmt::Display for Hello { - fn fmt(&self, f: &mut fmt:Formatter) -> fmt::Result { //~ ERROR path separator must be a double colon - write!(f, "hello") - } -} - -fn main() { - let _ = Hello; -} diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr deleted file mode 100644 index 92947e3b177f0..0000000000000 --- a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: path separator must be a double colon - --> $DIR/argument-list-from-path-sep-error-129273.rs:8:30 - | -LL | fn fmt(&self, f: &mut fmt:Formatter) -> fmt::Result { - | ^ - | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 -help: use a double colon instead - | -LL | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - | + - -error: aborting due to 1 previous error - diff --git a/tests/ui/suggestions/attribute-typos.rs b/tests/ui/suggestions/attribute-typos.rs index 7c8231bbb24f8..4c2336e105e2a 100644 --- a/tests/ui/suggestions/attribute-typos.rs +++ b/tests/ui/suggestions/attribute-typos.rs @@ -4,8 +4,8 @@ fn foo() {} #[tests] //~ ERROR cannot find attribute `tests` in this scope fn bar() {} -#[rustc_err] -//~^ ERROR cannot find attribute `rustc_err` in this scope +#[rustc_dumm] +//~^ ERROR cannot find attribute `rustc_dumm` in this scope //~| ERROR attributes starting with `rustc` are reserved for use by the `rustc` compiler fn main() {} diff --git a/tests/ui/suggestions/attribute-typos.stderr b/tests/ui/suggestions/attribute-typos.stderr index b871c9b45a56c..a1a01c0abd633 100644 --- a/tests/ui/suggestions/attribute-typos.stderr +++ b/tests/ui/suggestions/attribute-typos.stderr @@ -1,14 +1,14 @@ error: attributes starting with `rustc` are reserved for use by the `rustc` compiler --> $DIR/attribute-typos.rs:7:3 | -LL | #[rustc_err] - | ^^^^^^^^^ +LL | #[rustc_dumm] + | ^^^^^^^^^^ -error: cannot find attribute `rustc_err` in this scope +error: cannot find attribute `rustc_dumm` in this scope --> $DIR/attribute-typos.rs:7:3 | -LL | #[rustc_err] - | ^^^^^^^^^ help: a built-in attribute with a similar name exists: `rustc_error` +LL | #[rustc_dumm] + | ^^^^^^^^^^ help: a built-in attribute with a similar name exists: `rustc_dummy` error: cannot find attribute `tests` in this scope --> $DIR/attribute-typos.rs:4:3 diff --git a/tests/ui/suggestions/const-no-type.rs b/tests/ui/suggestions/const-no-type.rs index c6fdcdadbeafc..b72ace95213ce 100644 --- a/tests/ui/suggestions/const-no-type.rs +++ b/tests/ui/suggestions/const-no-type.rs @@ -10,19 +10,19 @@ fn main() {} // These will not reach typeck: -#[cfg(FALSE)] +#[cfg(false)] const C2 = 42; //~^ ERROR missing type for `const` item //~| HELP provide a type for the item //~| SUGGESTION : -#[cfg(FALSE)] +#[cfg(false)] static S2 = "abc"; //~^ ERROR missing type for `static` item //~| HELP provide a type for the item //~| SUGGESTION : -#[cfg(FALSE)] +#[cfg(false)] static mut SM2 = "abc"; //~^ ERROR missing type for `static mut` item //~| HELP provide a type for the item diff --git a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs index 1a440a90cd734..ebe7117080734 100644 --- a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs +++ b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs @@ -1,10 +1,12 @@ +//@ dont-require-annotations: NOTE + fn main() { let A = 3; //~^ ERROR refutable pattern in local binding - //~| patterns `i32::MIN..=1_i32` and `3_i32..=i32::MAX` not covered + //~| NOTE patterns `i32::MIN..=1_i32` and `3_i32..=i32::MAX` not covered //~| HELP introduce a variable instead //~| SUGGESTION A_var const A: i32 = 2; - //~^ missing patterns are not covered because `A` is interpreted as a constant pattern, not a new variable + //~^ NOTE missing patterns are not covered because `A` is interpreted as a constant pattern, not a new variable } diff --git a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr index 0dc17f2c25c38..e670bac2b3305 100644 --- a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr +++ b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in local binding - --> $DIR/const-pat-non-exaustive-let-new-var.rs:2:9 + --> $DIR/const-pat-non-exaustive-let-new-var.rs:4:9 | LL | let A = 3; | ^ patterns `i32::MIN..=1_i32` and `3_i32..=i32::MAX` not covered diff --git a/tests/ui/suggestions/derive-clone-for-eq.stderr b/tests/ui/suggestions/derive-clone-for-eq.stderr index eae0b0ae81755..eb0355853daac 100644 --- a/tests/ui/suggestions/derive-clone-for-eq.stderr +++ b/tests/ui/suggestions/derive-clone-for-eq.stderr @@ -13,7 +13,6 @@ LL | impl PartialEq for Struct | unsatisfied trait bound introduced here note: required by a bound in `Eq` --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` with trait `Clone` | LL | pub struct Struct(T); diff --git a/tests/ui/suggestions/derive-macro-missing-bounds.stderr b/tests/ui/suggestions/derive-macro-missing-bounds.stderr index 37a5f4932ff27..68c8204d1e18a 100644 --- a/tests/ui/suggestions/derive-macro-missing-bounds.stderr +++ b/tests/ui/suggestions/derive-macro-missing-bounds.stderr @@ -8,7 +8,6 @@ LL | struct Outer(Inner); | = help: the trait `Debug` is not implemented for `a::Inner` = note: add `#[derive(Debug)]` to `a::Inner` or manually `impl Debug for a::Inner` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `a::Inner` with `#[derive(Debug)]` | LL + #[derive(Debug)] @@ -37,7 +36,6 @@ LL | impl Debug for Inner { = note: 1 redundant requirement hidden = note: required for `&c::Inner` to implement `Debug` = note: required for the cast from `&&c::Inner` to `&dyn Debug` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` with trait `Trait` | LL | struct Outer(Inner); @@ -59,7 +57,6 @@ LL | impl Debug for Inner where T: Debug, T: Trait { = note: 1 redundant requirement hidden = note: required for `&d::Inner` to implement `Debug` = note: required for the cast from `&&d::Inner` to `&dyn Debug` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` with trait `Trait` | LL | struct Outer(Inner); @@ -81,7 +78,6 @@ LL | impl Debug for Inner where T: Debug + Trait { = note: 1 redundant requirement hidden = note: required for `&e::Inner` to implement `Debug` = note: required for the cast from `&&e::Inner` to `&dyn Debug` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` with trait `Trait` | LL | struct Outer(Inner); @@ -103,7 +99,6 @@ LL | impl Debug for Inner where T: Trait { = note: 1 redundant requirement hidden = note: required for `&f::Inner` to implement `Debug` = note: required for the cast from `&&f::Inner` to `&dyn Debug` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` with trait `Trait` | LL | struct Outer(Inner); diff --git a/tests/ui/suggestions/derive-trait-for-method-call.rs b/tests/ui/suggestions/derive-trait-for-method-call.rs index b5ce0078c9bc8..beac24148428b 100644 --- a/tests/ui/suggestions/derive-trait-for-method-call.rs +++ b/tests/ui/suggestions/derive-trait-for-method-call.rs @@ -26,19 +26,19 @@ impl Foo { fn test1() { let x = Foo(Enum::First, CloneEnum::First); let y = x.test(); - //~^the method `test` exists for struct `Foo`, but its trait bounds were not satisfied [E0599] + //~^ ERROR the method `test` exists for struct `Foo`, but its trait bounds were not satisfied [E0599] } fn test2() { let x = Foo(Struct{}, CloneStruct{}); let y = x.test(); - //~^the method `test` exists for struct `Foo`, but its trait bounds were not satisfied [E0599] + //~^ ERROR the method `test` exists for struct `Foo`, but its trait bounds were not satisfied [E0599] } fn test3() { let x = Foo(Vec::::new(), Instant::now()); let y = x.test(); - //~^the method `test` exists for struct `Foo, Instant>`, but its trait bounds were not satisfied [E0599] + //~^ ERROR the method `test` exists for struct `Foo, Instant>`, but its trait bounds were not satisfied [E0599] } fn main() {} diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr index 20aa227d10f9e..e189012d15c94 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr @@ -4,7 +4,7 @@ error[E0782]: expected a type, found a trait LL | fn f(a: A) -> A; | ^ | - = note: `A` it is dyn-incompatible, so it can't be `dyn` + = note: `A` is dyn-incompatible, otherwise a trait object could be used help: use a new generic type parameter, constrained by `A` | LL - fn f(a: A) -> A; @@ -26,13 +26,27 @@ help: `A` is dyn-incompatible, use `impl A` to return an opaque type, as long as LL | fn f(a: A) -> impl A; | ++++ +error: associated item referring to unboxed trait object for its own trait + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:4:13 + | +LL | trait A: Sized { + | - in this trait +LL | fn f(a: A) -> A; + | ^ ^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL - fn f(a: A) -> A; +LL + fn f(a: Self) -> Self; + | + error[E0782]: expected a type, found a trait --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:13 | LL | fn f(b: B) -> B; | ^ | - = note: `B` it is dyn-incompatible, so it can't be `dyn` + = note: `B` is dyn-incompatible, otherwise a trait object could be used help: use a new generic type parameter, constrained by `B` | LL - fn f(b: B) -> B; @@ -54,13 +68,27 @@ help: `B` is dyn-incompatible, use `impl B` to return an opaque type, as long as LL | fn f(b: B) -> impl B; | ++++ +error: associated item referring to unboxed trait object for its own trait + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:13 + | +LL | trait B { + | - in this trait +LL | fn f(b: B) -> B; + | ^ ^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL - fn f(b: B) -> B; +LL + fn f(b: Self) -> Self; + | + error[E0782]: expected a type, found a trait --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:16:20 | LL | fn f(&self, c: C) -> C; | ^ | - = note: `C` it is dyn-incompatible, so it can't be `dyn` + = note: `C` is dyn-incompatible, otherwise a trait object could be used help: use a new generic type parameter, constrained by `C` | LL - fn f(&self, c: C) -> C; @@ -82,34 +110,6 @@ help: `C` is dyn-incompatible, use `impl C` to return an opaque type, as long as LL | fn f(&self, c: C) -> impl C; | ++++ -error: associated item referring to unboxed trait object for its own trait - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:4:13 - | -LL | trait A: Sized { - | - in this trait -LL | fn f(a: A) -> A; - | ^ ^ - | -help: you might have meant to use `Self` to refer to the implementing type - | -LL - fn f(a: A) -> A; -LL + fn f(a: Self) -> Self; - | - -error: associated item referring to unboxed trait object for its own trait - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:13 - | -LL | trait B { - | - in this trait -LL | fn f(b: B) -> B; - | ^ ^ - | -help: you might have meant to use `Self` to refer to the implementing type - | -LL - fn f(b: B) -> B; -LL + fn f(b: Self) -> Self; - | - error: associated item referring to unboxed trait object for its own trait --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:16:20 | diff --git a/tests/ui/suggestions/enum-method-probe.fixed b/tests/ui/suggestions/enum-method-probe.fixed index 611be9911d975..e097fa8cc1d87 100644 --- a/tests/ui/suggestions/enum-method-probe.fixed +++ b/tests/ui/suggestions/enum-method-probe.fixed @@ -1,4 +1,4 @@ -//@ compile-flags: --edition=2021 +//@ edition: 2021 //@ run-rustfix #![allow(unused)] diff --git a/tests/ui/suggestions/enum-method-probe.rs b/tests/ui/suggestions/enum-method-probe.rs index e183ebd25f2ac..665ee7d0aaad0 100644 --- a/tests/ui/suggestions/enum-method-probe.rs +++ b/tests/ui/suggestions/enum-method-probe.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition=2021 +//@ edition: 2021 //@ run-rustfix #![allow(unused)] diff --git a/tests/ui/suggestions/field-has-method.rs b/tests/ui/suggestions/field-has-method.rs index 980000151e2f7..d28b6ba546c4a 100644 --- a/tests/ui/suggestions/field-has-method.rs +++ b/tests/ui/suggestions/field-has-method.rs @@ -17,7 +17,7 @@ struct InferOk { fn foo(i: InferOk) { let k = i.kind(); - //~^ no method named `kind` found for struct `InferOk` in the current scope + //~^ ERROR no method named `kind` found for struct `InferOk` in the current scope } fn main() {} diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs index daec66709b654..f5c3da847c72f 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs @@ -18,7 +18,6 @@ mod elided { // But that lifetime does not participate in resolution. async fn i(mut x: impl Iterator) -> Option<&()> { x.next() } //~^ ERROR missing lifetime specifier - //~| ERROR lifetime may not live long enough } mod underscore { @@ -37,7 +36,6 @@ mod underscore { // But that lifetime does not participate in resolution. async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } //~^ ERROR missing lifetime specifier - //~| ERROR lifetime may not live long enough } mod alone_in_path { @@ -51,6 +49,17 @@ mod alone_in_path { //~| ERROR missing lifetime specifier } +mod alone_in_path2 { + trait Foo<'a> { fn next(&mut self) -> Option<&'a ()>; } + + fn f(_: impl Foo<>) {} + //~^ ERROR anonymous lifetimes in `impl Trait` are unstable + + fn g(mut x: impl Foo<>) -> Option<&()> { x.next() } + //~^ ERROR anonymous lifetimes in `impl Trait` are unstable + //~| ERROR missing lifetime specifier +} + mod in_path { trait Foo<'a, T> { fn next(&mut self) -> Option<&'a T>; } diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr index 204209179adf3..92996ca846782 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr @@ -41,7 +41,7 @@ LL + async fn i(mut x: impl Iterator) -> Option<()> { x.next() } | error[E0106]: missing lifetime specifier - --> $DIR/impl-trait-missing-lifetime-gated.rs:28:58 + --> $DIR/impl-trait-missing-lifetime-gated.rs:27:58 | LL | fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } | ^^ expected named lifetime parameter @@ -64,7 +64,7 @@ LL + fn g(mut x: impl Iterator) -> Option<()> { x.next() } | error[E0106]: missing lifetime specifier - --> $DIR/impl-trait-missing-lifetime-gated.rs:38:64 + --> $DIR/impl-trait-missing-lifetime-gated.rs:37:64 | LL | async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } | ^^ expected named lifetime parameter @@ -87,7 +87,7 @@ LL + async fn i(mut x: impl Iterator) -> Option<()> { x.next( | error[E0106]: missing lifetime specifier - --> $DIR/impl-trait-missing-lifetime-gated.rs:49:37 + --> $DIR/impl-trait-missing-lifetime-gated.rs:47:37 | LL | fn g(mut x: impl Foo) -> Option<&()> { x.next() } | ^ expected named lifetime parameter @@ -108,7 +108,28 @@ LL + fn g(mut x: impl Foo) -> Option<()> { x.next() } | error[E0106]: missing lifetime specifier - --> $DIR/impl-trait-missing-lifetime-gated.rs:60:41 + --> $DIR/impl-trait-missing-lifetime-gated.rs:58:39 + | +LL | fn g(mut x: impl Foo<>) -> Option<&()> { x.next() } + | ^ 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 +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` + | +LL | fn g(mut x: impl Foo<>) -> Option<&'static ()> { x.next() } + | +++++++ +help: consider introducing a named lifetime parameter + | +LL | fn g<'a>(mut x: impl Foo<>) -> Option<&'a ()> { x.next() } + | ++++ ++ +help: alternatively, you might want to return an owned value + | +LL - fn g(mut x: impl Foo<>) -> Option<&()> { x.next() } +LL + fn g(mut x: impl Foo<>) -> Option<()> { x.next() } + | + +error[E0106]: missing lifetime specifier + --> $DIR/impl-trait-missing-lifetime-gated.rs:69:41 | LL | fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } | ^ expected named lifetime parameter @@ -129,7 +150,7 @@ LL + fn g(mut x: impl Foo<()>) -> Option<()> { x.next() } | warning: elided lifetime has a name - --> $DIR/impl-trait-missing-lifetime-gated.rs:66:57 + --> $DIR/impl-trait-missing-lifetime-gated.rs:75:57 | LL | fn resolved_anonymous<'a, T: 'a>(f: impl Fn(&'a str) -> &T) { | -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a` @@ -162,16 +183,8 @@ help: consider introducing a named lifetime parameter LL | fn g<'a>(mut x: impl Iterator) -> Option<&()> { x.next() } | ++++ ++ -error: lifetime may not live long enough - --> $DIR/impl-trait-missing-lifetime-gated.rs:19:67 - | -LL | async fn i(mut x: impl Iterator) -> Option<&()> { x.next() } - | ----------------------------------------------------------- ^^^^^^^^ returning this value requires that `'1` must outlive `'static` - | | - | return type `impl Future>` contains a lifetime `'1` - error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:25:35 + --> $DIR/impl-trait-missing-lifetime-gated.rs:24:35 | LL | fn f(_: impl Iterator) {} | ^^ expected named lifetime parameter @@ -185,7 +198,7 @@ LL + fn f<'a>(_: impl Iterator) {} | error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:28:39 + --> $DIR/impl-trait-missing-lifetime-gated.rs:27:39 | LL | fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } | ^^ expected named lifetime parameter @@ -198,16 +211,8 @@ LL - fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() LL + fn g<'a>(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } | -error: lifetime may not live long enough - --> $DIR/impl-trait-missing-lifetime-gated.rs:38:73 - | -LL | async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } - | ----------------------------------------------------------------- ^^^^^^^^ returning this value requires that `'1` must outlive `'static` - | | - | return type `impl Future>` contains a lifetime `'1` - error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:46:18 + --> $DIR/impl-trait-missing-lifetime-gated.rs:44:18 | LL | fn f(_: impl Foo) {} | ^^^ expected named lifetime parameter @@ -220,7 +225,7 @@ LL | fn f<'a>(_: impl Foo<'a>) {} | ++++ ++++ error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:49:22 + --> $DIR/impl-trait-missing-lifetime-gated.rs:47:22 | LL | fn g(mut x: impl Foo) -> Option<&()> { x.next() } | ^^^ expected named lifetime parameter @@ -233,7 +238,33 @@ LL | fn g<'a>(mut x: impl Foo<'a>) -> Option<&()> { x.next() } | ++++ ++++ error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:57:22 + --> $DIR/impl-trait-missing-lifetime-gated.rs:55:22 + | +LL | fn f(_: impl Foo<>) {} + | ^ expected named lifetime parameter + | + = help: add `#![feature(anonymous_lifetime_in_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider introducing a named lifetime parameter + | +LL | fn f<'a>(_: impl Foo<'a>) {} + | ++++ ++ + +error[E0658]: anonymous lifetimes in `impl Trait` are unstable + --> $DIR/impl-trait-missing-lifetime-gated.rs:58:26 + | +LL | fn g(mut x: impl Foo<>) -> Option<&()> { x.next() } + | ^ expected named lifetime parameter + | + = help: add `#![feature(anonymous_lifetime_in_impl_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: consider introducing a named lifetime parameter + | +LL | fn g<'a>(mut x: impl Foo<'a>) -> Option<&()> { x.next() } + | ++++ ++ + +error[E0658]: anonymous lifetimes in `impl Trait` are unstable + --> $DIR/impl-trait-missing-lifetime-gated.rs:66:22 | LL | fn f(_: impl Foo<()>) {} | ^ expected named lifetime parameter @@ -246,7 +277,7 @@ LL | fn f<'a>(_: impl Foo<'a, ()>) {} | ++++ +++ error[E0658]: anonymous lifetimes in `impl Trait` are unstable - --> $DIR/impl-trait-missing-lifetime-gated.rs:60:26 + --> $DIR/impl-trait-missing-lifetime-gated.rs:69:26 | LL | fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } | ^ expected named lifetime parameter @@ -258,7 +289,7 @@ help: consider introducing a named lifetime parameter LL | fn g<'a>(mut x: impl Foo<'a, ()>) -> Option<&()> { x.next() } | ++++ +++ -error: aborting due to 16 previous errors; 1 warning emitted +error: aborting due to 17 previous errors; 1 warning emitted Some errors have detailed explanations: E0106, E0658. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime.rs b/tests/ui/suggestions/impl-trait-missing-lifetime.rs index 12dc0e8216b7d..27f03431d098a 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime.rs +++ b/tests/ui/suggestions/impl-trait-missing-lifetime.rs @@ -8,7 +8,6 @@ fn f(_: impl Iterator) {} // But that lifetime does not participate in resolution. fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } //~^ ERROR missing lifetime specifier -//~| ERROR lifetime may not live long enough // This is understood as `fn foo<'_1>(_: impl Iterator) {}`. async fn h(_: impl Iterator) {} @@ -16,6 +15,5 @@ async fn h(_: impl Iterator) {} // But that lifetime does not participate in resolution. async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } //~^ ERROR missing lifetime specifier -//~| ERROR lifetime may not live long enough fn main() {} diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime.stderr index dfbc883680b23..c09c575c147f3 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime.stderr @@ -22,7 +22,7 @@ LL + fn g(mut x: impl Iterator) -> Option<()> { x.next() } | error[E0106]: missing lifetime specifier - --> $DIR/impl-trait-missing-lifetime.rs:17:60 + --> $DIR/impl-trait-missing-lifetime.rs:16:60 | LL | async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } | ^^ expected named lifetime parameter @@ -44,20 +44,6 @@ LL - async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next( LL + async fn i(mut x: impl Iterator) -> Option<()> { x.next() } | -error: lifetime may not live long enough - --> $DIR/impl-trait-missing-lifetime.rs:17:69 - | -LL | async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } - | ----------------------------------------------------------------- ^^^^^^^^ returning this value requires that `'1` must outlive `'static` - | | - | return type `impl Future>` contains a lifetime `'1` - -error: lifetime may not live long enough - --> $DIR/impl-trait-missing-lifetime.rs:9:63 - | -LL | fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } - | ----- has type `x` ^^^^^^^^ returning this value requires that `'1` must outlive `'static` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/suggestions/inner_type.fixed b/tests/ui/suggestions/inner_type.fixed index cfea66b57ec1c..3dc939d6b5c4f 100644 --- a/tests/ui/suggestions/inner_type.fixed +++ b/tests/ui/suggestions/inner_type.fixed @@ -1,4 +1,4 @@ -//@ compile-flags: --edition=2021 +//@ edition: 2021 //@ run-rustfix pub struct Struct { diff --git a/tests/ui/suggestions/inner_type.rs b/tests/ui/suggestions/inner_type.rs index 5fedf3f256e3a..81a05c2531197 100644 --- a/tests/ui/suggestions/inner_type.rs +++ b/tests/ui/suggestions/inner_type.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition=2021 +//@ edition: 2021 //@ run-rustfix pub struct Struct { diff --git a/tests/ui/suggestions/issue-101465.rs b/tests/ui/suggestions/issue-101465.rs index 8e42e2c222434..3ec4e81605409 100644 --- a/tests/ui/suggestions/issue-101465.rs +++ b/tests/ui/suggestions/issue-101465.rs @@ -18,7 +18,7 @@ fn foo() -> impl Tr { match true { true => B, false => C, - //~^ `match` arms have incompatible types + //~^ ERROR `match` arms have incompatible types } } diff --git a/tests/ui/suggestions/issue-103646.rs b/tests/ui/suggestions/issue-103646.rs index f679640c5dc90..d8b06d663affc 100644 --- a/tests/ui/suggestions/issue-103646.rs +++ b/tests/ui/suggestions/issue-103646.rs @@ -5,7 +5,7 @@ trait Cat { fn uwu(c: T) { c.nya(); //~^ ERROR no method named `nya` found for type parameter `T` in the current scope - //~| Suggestion T::nya() + //~| SUGGESTION T::nya() } fn main() {} diff --git a/tests/ui/suggestions/issue-116434-2015.stderr b/tests/ui/suggestions/issue-116434-2015.stderr index e7b8cd2f101d8..07a254432a28d 100644 --- a/tests/ui/suggestions/issue-116434-2015.stderr +++ b/tests/ui/suggestions/issue-116434-2015.stderr @@ -12,19 +12,6 @@ help: if this is a dyn-compatible trait, use `dyn` LL | fn foo() -> dyn Clone; | +++ -warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/issue-116434-2015.rs:18:20 - | -LL | fn handle() -> DbHandle; - | ^^^^^^^^ - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! - = note: for more information, see -help: if this is a dyn-compatible trait, use `dyn` - | -LL | fn handle() -> dyn DbHandle; - | +++ - warning: trait objects without an explicit `dyn` are deprecated --> $DIR/issue-116434-2015.rs:3:17 | @@ -53,6 +40,19 @@ help: there is an associated type with the same name LL | fn foo() -> Self::Clone; | ++++++ +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/issue-116434-2015.rs:18:20 + | +LL | fn handle() -> DbHandle; + | ^^^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see +help: if this is a dyn-compatible trait, use `dyn` + | +LL | fn handle() -> dyn DbHandle; + | +++ + warning: trait objects without an explicit `dyn` are deprecated --> $DIR/issue-116434-2015.rs:18:20 | diff --git a/tests/ui/suggestions/issue-81839.rs b/tests/ui/suggestions/issue-81839.rs index 3971aa9fe8417..99a03c175b396 100644 --- a/tests/ui/suggestions/issue-81839.rs +++ b/tests/ui/suggestions/issue-81839.rs @@ -8,7 +8,7 @@ async fn test(ans: &str, num: i32, cx: &issue_81839::Test) -> u32 { 1 => { cx.answer_str("hi"); } - _ => cx.answer_str("hi"), //~ `match` arms have incompatible types + _ => cx.answer_str("hi"), //~ ERROR `match` arms have incompatible types } 1 diff --git a/tests/ui/suggestions/issue-86667.rs b/tests/ui/suggestions/issue-86667.rs index 1f37e9a5f6de3..cc5b878b63213 100644 --- a/tests/ui/suggestions/issue-86667.rs +++ b/tests/ui/suggestions/issue-86667.rs @@ -1,7 +1,7 @@ // Regression test for #86667, where a garbled suggestion was issued for // a missing named lifetime parameter. -//@ compile-flags: --edition 2018 +//@ edition: 2018 async fn a(s1: &str, s2: &str) -> &str { //~^ ERROR: missing lifetime specifier [E0106] diff --git a/tests/ui/suggestions/many-type-ascription.stderr b/tests/ui/suggestions/many-type-ascription.stderr index feddc7d62a754..47e19c508ef90 100644 --- a/tests/ui/suggestions/many-type-ascription.stderr +++ b/tests/ui/suggestions/many-type-ascription.stderr @@ -3,8 +3,6 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `:` | LL | let _ = 0: i32; | ^ expected one of `.`, `;`, `?`, `else`, or an operator - | - = note: type ascription syntax has been removed, see issue #101728 error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr index 4408fe0a0a4bb..a48dd30d008a6 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr @@ -31,7 +31,6 @@ LL | #[derive(Debug, Copy, Clone)] LL | pub struct Vector2 { | ---- unsatisfied trait bound introduced in this `derive` macro = note: required for the cast from `&Vector2` to `&dyn Debug` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `K` with trait `Copy` | LL | pub struct AABB { @@ -51,7 +50,6 @@ note: required by a bound in `Vector2` | LL | pub struct Vector2 { | ^^^^ required by this bound in `Vector2` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `K` with trait `Copy` | LL | pub struct AABB { @@ -73,7 +71,6 @@ LL | #[derive(Debug, Copy, Clone)] | ^^^^^ LL | pub struct Vector2 { | ---- unsatisfied trait bound introduced in this `derive` macro - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `K` with trait `Copy` | LL | pub struct AABB { diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr index 1bbf6f66ab23f..3f8b6f93e1f4a 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr @@ -12,7 +12,6 @@ note: the `Copy` impl for `Vector2` requires that `K: Debug` | LL | pub loc: Vector2, | ^^^^^^^^^^ - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `K` with trait `Debug` | LL | pub struct AABB{ @@ -43,7 +42,6 @@ LL | pub struct AABB{ LL | pub loc: Vector2, | ^^^^^^^^^^^^^^^^^^^ `K` cannot be formatted using `{:?}` because it doesn't implement `Debug` | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `K` with trait `Debug` | LL | pub struct AABB{ @@ -58,7 +56,6 @@ LL | #[derive(Debug, Copy, Clone)] LL | pub size: Vector2 | ^^^^^^^^^^^^^^^^^^^^ `K` cannot be formatted using `{:?}` because it doesn't implement `Debug` | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `K` with trait `Debug` | LL | pub struct AABB{ diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr index 8b5cced4c4aaa..3766e3e2c7b54 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr @@ -12,7 +12,6 @@ note: the `Copy` impl for `Vector2` requires that `K: Debug` | LL | pub loc: Vector2, | ^^^^^^^^^^ - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `K` with trait `Debug` | LL | pub struct AABB { @@ -67,7 +66,6 @@ LL | #[derive(Debug, Copy, Clone)] LL | pub struct Vector2 { | ---- unsatisfied trait bound introduced in this `derive` macro = note: required for the cast from `&Vector2` to `&dyn Debug` - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `K` with trait `Copy` | LL | pub struct AABB { @@ -82,7 +80,6 @@ LL | pub struct AABB { LL | pub loc: Vector2, | ^^^^^^^^^^^^^^^^^^^ `K` cannot be formatted using `{:?}` because it doesn't implement `Debug` | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `K` with trait `Debug` | LL | pub struct AABB { @@ -102,7 +99,6 @@ note: required by a bound in `Vector2` | LL | pub struct Vector2 { | ^^^^ required by this bound in `Vector2` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `K` with trait `Copy` | LL | pub struct AABB { @@ -117,7 +113,6 @@ LL | #[derive(Debug, Copy, Clone)] LL | pub size: Vector2, | ^^^^^^^^^^^^^^^^^^^^ `K` cannot be formatted using `{:?}` because it doesn't implement `Debug` | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `K` with trait `Debug` | LL | pub struct AABB { @@ -139,7 +134,6 @@ LL | #[derive(Debug, Copy, Clone)] | ^^^^^ LL | pub struct Vector2 { | ---- unsatisfied trait bound introduced in this `derive` macro - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `K` with trait `Copy` | LL | pub struct AABB { diff --git a/tests/ui/suggestions/missing-lt-for-hrtb.rs b/tests/ui/suggestions/missing-lt-for-hrtb.rs index a48c5665d67a4..04ea3d831c93a 100644 --- a/tests/ui/suggestions/missing-lt-for-hrtb.rs +++ b/tests/ui/suggestions/missing-lt-for-hrtb.rs @@ -8,8 +8,6 @@ fn main() { let x = S(&|x| { println!("hi"); x - //~^ ERROR lifetime may not live long enough - //~| ERROR lifetime may not live long enough }); x.0(&X(&())); } diff --git a/tests/ui/suggestions/missing-lt-for-hrtb.stderr b/tests/ui/suggestions/missing-lt-for-hrtb.stderr index e8c536ac47d50..fa515644431ac 100644 --- a/tests/ui/suggestions/missing-lt-for-hrtb.stderr +++ b/tests/ui/suggestions/missing-lt-for-hrtb.stderr @@ -31,28 +31,6 @@ help: consider using one of the available lifetimes here LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &'lifetime X<'lifetime>); | +++++++++ +++++++++++ -error: lifetime may not live long enough - --> $DIR/missing-lt-for-hrtb.rs:10:9 - | -LL | let x = S(&|x| { - | -- return type of closure is &'2 X<'_> - | | - | has type `&'1 X<'_>` -LL | println!("hi"); -LL | x - | ^ returning this value requires that `'1` must outlive `'2` - -error: lifetime may not live long enough - --> $DIR/missing-lt-for-hrtb.rs:10:9 - | -LL | let x = S(&|x| { - | -- return type of closure is &X<'4> - | | - | has type `&X<'3>` -LL | println!("hi"); -LL | x - | ^ returning this value requires that `'3` must outlive `'4` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/suggestions/partialeq_suggest_swap.rs b/tests/ui/suggestions/partialeq_suggest_swap.rs index ee5583a5488ea..090f17f2fe2f4 100644 --- a/tests/ui/suggestions/partialeq_suggest_swap.rs +++ b/tests/ui/suggestions/partialeq_suggest_swap.rs @@ -7,5 +7,5 @@ impl PartialEq for T { } fn main() { - 4i32 == T(4); //~ mismatched types [E0308] + 4i32 == T(4); //~ ERROR mismatched types [E0308] } diff --git a/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.rs b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.rs index 2ebbed3c740f4..a54493e5922b9 100644 --- a/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.rs +++ b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.rs @@ -7,5 +7,5 @@ impl PartialEq for T { } fn main() { - String::from("Girls Band Cry") == T(String::from("Girls Band Cry")); //~ can't compare `String` with `T` [E0277] + String::from("Girls Band Cry") == T(String::from("Girls Band Cry")); //~ ERROR can't compare `String` with `T` [E0277] } diff --git a/tests/ui/suggestions/raw-c-string-prefix.rs b/tests/ui/suggestions/raw-c-string-prefix.rs new file mode 100644 index 0000000000000..6af72df40242c --- /dev/null +++ b/tests/ui/suggestions/raw-c-string-prefix.rs @@ -0,0 +1,10 @@ +// `rc` and `cr` are easy to confuse; check that we issue a suggestion to help. + +//@ edition:2021 + +fn main() { + rc"abc"; + //~^ ERROR: prefix `rc` is unknown + //~| HELP: use `cr` for a raw C-string + //~| ERROR: expected one of +} diff --git a/tests/ui/suggestions/raw-c-string-prefix.stderr b/tests/ui/suggestions/raw-c-string-prefix.stderr new file mode 100644 index 0000000000000..5341e3d04e8a1 --- /dev/null +++ b/tests/ui/suggestions/raw-c-string-prefix.stderr @@ -0,0 +1,21 @@ +error: prefix `rc` is unknown + --> $DIR/raw-c-string-prefix.rs:6:5 + | +LL | rc"abc"; + | ^^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: use `cr` for a raw C-string + | +LL - rc"abc"; +LL + cr"abc"; + | + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `"abc"` + --> $DIR/raw-c-string-prefix.rs:6:7 + | +LL | rc"abc"; + | ^^^^^ expected one of 8 possible tokens + +error: aborting due to 2 previous errors + diff --git a/tests/ui/suggestions/struct-field-type-including-single-colon.rs b/tests/ui/suggestions/struct-field-type-including-single-colon.rs index a3111028895dd..482641fc7cac9 100644 --- a/tests/ui/suggestions/struct-field-type-including-single-colon.rs +++ b/tests/ui/suggestions/struct-field-type-including-single-colon.rs @@ -7,14 +7,14 @@ mod foo { struct Foo { a: foo:A, - //~^ ERROR path separator must be a double colon - //~| ERROR struct `A` is private + //~^ ERROR found single colon in a struct field type path + //~| ERROR expected `,`, or `}`, found `:` } struct Bar { b: foo::bar:B, - //~^ ERROR path separator must be a double colon - //~| ERROR module `bar` is private + //~^ ERROR found single colon in a struct field type path + //~| ERROR expected `,`, or `}`, found `:` } fn main() {} diff --git a/tests/ui/suggestions/struct-field-type-including-single-colon.stderr b/tests/ui/suggestions/struct-field-type-including-single-colon.stderr index ce16aca1e14a2..5ffc5b40849b6 100644 --- a/tests/ui/suggestions/struct-field-type-including-single-colon.stderr +++ b/tests/ui/suggestions/struct-field-type-including-single-colon.stderr @@ -1,53 +1,40 @@ -error: path separator must be a double colon +error: found single colon in a struct field type path --> $DIR/struct-field-type-including-single-colon.rs:9:11 | LL | a: foo:A, | ^ | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 -help: use a double colon instead +help: write a path separator here | LL | a: foo::A, | + -error: path separator must be a double colon +error: expected `,`, or `}`, found `:` + --> $DIR/struct-field-type-including-single-colon.rs:9:11 + | +LL | struct Foo { + | --- while parsing this struct +LL | a: foo:A, + | ^ + +error: found single colon in a struct field type path --> $DIR/struct-field-type-including-single-colon.rs:15:16 | LL | b: foo::bar:B, | ^ | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 -help: use a double colon instead +help: write a path separator here | LL | b: foo::bar::B, | + -error[E0603]: struct `A` is private - --> $DIR/struct-field-type-including-single-colon.rs:9:12 - | -LL | a: foo:A, - | ^ private struct - | -note: the struct `A` is defined here - --> $DIR/struct-field-type-including-single-colon.rs:2:5 - | -LL | struct A; - | ^^^^^^^^^ - -error[E0603]: module `bar` is private - --> $DIR/struct-field-type-including-single-colon.rs:15:13 +error: expected `,`, or `}`, found `:` + --> $DIR/struct-field-type-including-single-colon.rs:15:16 | +LL | struct Bar { + | --- while parsing this struct LL | b: foo::bar:B, - | ^^^ - struct `B` is not publicly re-exported - | | - | private module - | -note: the module `bar` is defined here - --> $DIR/struct-field-type-including-single-colon.rs:3:5 - | -LL | mod bar { - | ^^^^^^^ + | ^ error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.fixed b/tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.fixed index 3b739312942a1..735c09438e2df 100644 --- a/tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.fixed +++ b/tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.fixed @@ -10,6 +10,6 @@ fn bar(bar: &usize) { } fn main() { - foo(&mut Default::default()); //~ the trait bound `&mut usize: Default` is not satisfied - bar(&Default::default()); //~ the trait bound `&usize: Default` is not satisfied + foo(&mut Default::default()); //~ ERROR the trait bound `&mut usize: Default` is not satisfied + bar(&Default::default()); //~ ERROR the trait bound `&usize: Default` is not satisfied } diff --git a/tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.rs b/tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.rs index 7fc870946b934..7364c59fd92e3 100644 --- a/tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.rs +++ b/tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.rs @@ -10,6 +10,6 @@ fn bar(bar: &usize) { } fn main() { - foo(Default::default()); //~ the trait bound `&mut usize: Default` is not satisfied - bar(Default::default()); //~ the trait bound `&usize: Default` is not satisfied + foo(Default::default()); //~ ERROR the trait bound `&mut usize: Default` is not satisfied + bar(Default::default()); //~ ERROR the trait bound `&usize: Default` is not satisfied } diff --git a/tests/ui/suggestions/suggest-dereferencing-index.stderr b/tests/ui/suggestions/suggest-dereferencing-index.stderr index 0335d8eafde4b..937f32677a6c8 100644 --- a/tests/ui/suggestions/suggest-dereferencing-index.stderr +++ b/tests/ui/suggestions/suggest-dereferencing-index.stderr @@ -4,9 +4,10 @@ error[E0277]: the type `[{integer}]` cannot be indexed by `&usize` LL | let one_item_please: i32 = [1, 2, 3][i]; | ^ slice indices are of type `usize` or ranges of `usize` | - = help: the trait `SliceIndex<[{integer}]>` is not implemented for `&_` - but it is implemented for `_` - = help: for that trait implementation, expected `usize`, found `&usize` + = help: the trait `SliceIndex<[{integer}]>` is not implemented for `&usize` + = help: the following other types implement trait `SliceIndex`: + `usize` implements `SliceIndex` + `usize` implements `SliceIndex<[T]>` = note: required for `[{integer}]` to implement `Index<&usize>` = note: 1 redundant requirement hidden = note: required for `[{integer}; 3]` to implement `Index<&usize>` diff --git a/tests/ui/suggestions/suggest-let-for-assignment.fixed b/tests/ui/suggestions/suggest-let-for-assignment.fixed index 80b9333827ec9..6d40f74a0d81c 100644 --- a/tests/ui/suggestions/suggest-let-for-assignment.fixed +++ b/tests/ui/suggestions/suggest-let-for-assignment.fixed @@ -7,10 +7,10 @@ fn main() { let x = "x"; //~ ERROR cannot find value `x` in this scope println!("x: {}", x); //~ ERROR cannot find value `x` in this scope - let some_variable = 6; //~ cannot find value `let_some_variable` in this scope + let some_variable = 6; //~ ERROR cannot find value `let_some_variable` in this scope println!("some_variable: {}", some_variable); //~ ERROR cannot find value `some_variable` in this scope - let other_variable = 6; //~ cannot find value `letother_variable` in this scope + let other_variable = 6; //~ ERROR cannot find value `letother_variable` in this scope println!("other_variable: {}", other_variable); //~ ERROR cannot find value `other_variable` in this scope if x == "x" { diff --git a/tests/ui/suggestions/suggest-let-for-assignment.rs b/tests/ui/suggestions/suggest-let-for-assignment.rs index 22560083d34c9..aeb22a0ded031 100644 --- a/tests/ui/suggestions/suggest-let-for-assignment.rs +++ b/tests/ui/suggestions/suggest-let-for-assignment.rs @@ -7,10 +7,10 @@ fn main() { x = "x"; //~ ERROR cannot find value `x` in this scope println!("x: {}", x); //~ ERROR cannot find value `x` in this scope - let_some_variable = 6; //~ cannot find value `let_some_variable` in this scope + let_some_variable = 6; //~ ERROR cannot find value `let_some_variable` in this scope println!("some_variable: {}", some_variable); //~ ERROR cannot find value `some_variable` in this scope - letother_variable = 6; //~ cannot find value `letother_variable` in this scope + letother_variable = 6; //~ ERROR cannot find value `letother_variable` in this scope println!("other_variable: {}", other_variable); //~ ERROR cannot find value `other_variable` in this scope if x == "x" { diff --git a/tests/ui/suggestions/suggest-null-ptr.fixed b/tests/ui/suggestions/suggest-null-ptr.fixed index 55c90859c83e0..97a628f8662bd 100644 --- a/tests/ui/suggestions/suggest-null-ptr.fixed +++ b/tests/ui/suggestions/suggest-null-ptr.fixed @@ -16,16 +16,16 @@ extern "C" { fn main() { unsafe { foo(std::ptr::null()); - //~^ mismatched types [E0308] - //~| if you meant to create a null pointer, use `std::ptr::null()` + //~^ ERROR mismatched types [E0308] + //~| HELP if you meant to create a null pointer, use `std::ptr::null()` foo_mut(std::ptr::null_mut()); - //~^ mismatched types [E0308] - //~| if you meant to create a null pointer, use `std::ptr::null_mut()` + //~^ ERROR mismatched types [E0308] + //~| HELP if you meant to create a null pointer, use `std::ptr::null_mut()` usize(std::ptr::null()); - //~^ mismatched types [E0308] - //~| if you meant to create a null pointer, use `std::ptr::null()` + //~^ ERROR mismatched types [E0308] + //~| HELP if you meant to create a null pointer, use `std::ptr::null()` usize_mut(std::ptr::null_mut()); - //~^ mismatched types [E0308] - //~| if you meant to create a null pointer, use `std::ptr::null_mut()` + //~^ ERROR mismatched types [E0308] + //~| HELP if you meant to create a null pointer, use `std::ptr::null_mut()` } } diff --git a/tests/ui/suggestions/suggest-null-ptr.rs b/tests/ui/suggestions/suggest-null-ptr.rs index f4f1269d512d8..46ace90c8aea9 100644 --- a/tests/ui/suggestions/suggest-null-ptr.rs +++ b/tests/ui/suggestions/suggest-null-ptr.rs @@ -16,16 +16,16 @@ extern "C" { fn main() { unsafe { foo(0); - //~^ mismatched types [E0308] - //~| if you meant to create a null pointer, use `std::ptr::null()` + //~^ ERROR mismatched types [E0308] + //~| HELP if you meant to create a null pointer, use `std::ptr::null()` foo_mut(0); - //~^ mismatched types [E0308] - //~| if you meant to create a null pointer, use `std::ptr::null_mut()` + //~^ ERROR mismatched types [E0308] + //~| HELP if you meant to create a null pointer, use `std::ptr::null_mut()` usize(0); - //~^ mismatched types [E0308] - //~| if you meant to create a null pointer, use `std::ptr::null()` + //~^ ERROR mismatched types [E0308] + //~| HELP if you meant to create a null pointer, use `std::ptr::null()` usize_mut(0); - //~^ mismatched types [E0308] - //~| if you meant to create a null pointer, use `std::ptr::null_mut()` + //~^ ERROR mismatched types [E0308] + //~| HELP if you meant to create a null pointer, use `std::ptr::null_mut()` } } diff --git a/tests/ui/suggestions/suggest-ref-mut.rs b/tests/ui/suggestions/suggest-ref-mut.rs index 9f5df9303c330..2a933f6305f63 100644 --- a/tests/ui/suggestions/suggest-ref-mut.rs +++ b/tests/ui/suggestions/suggest-ref-mut.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: SUGGESTION + struct X(usize); impl X { diff --git a/tests/ui/suggestions/suggest-ref-mut.stderr b/tests/ui/suggestions/suggest-ref-mut.stderr index 935a04c052ac9..7c0899f0fbc16 100644 --- a/tests/ui/suggestions/suggest-ref-mut.stderr +++ b/tests/ui/suggestions/suggest-ref-mut.stderr @@ -1,5 +1,5 @@ error[E0594]: cannot assign to `self.0`, which is behind a `&` reference - --> $DIR/suggest-ref-mut.rs:7:9 + --> $DIR/suggest-ref-mut.rs:9:9 | LL | self.0 = 32; | ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written @@ -10,7 +10,7 @@ LL | fn zap(&mut self) { | +++ error[E0594]: cannot assign to `*foo`, which is behind a `&` reference - --> $DIR/suggest-ref-mut.rs:15:5 + --> $DIR/suggest-ref-mut.rs:17:5 | LL | *foo = 32; | ^^^^^^^^^ `foo` is a `&` reference, so the data it refers to cannot be written @@ -21,7 +21,7 @@ LL | let ref mut foo = 16; | +++ error[E0594]: cannot assign to `*bar`, which is behind a `&` reference - --> $DIR/suggest-ref-mut.rs:19:9 + --> $DIR/suggest-ref-mut.rs:21:9 | LL | *bar = 32; | ^^^^^^^^^ `bar` is a `&` reference, so the data it refers to cannot be written @@ -32,7 +32,7 @@ LL | if let Some(ref mut bar) = Some(16) { | +++ error[E0594]: cannot assign to `*quo`, which is behind a `&` reference - --> $DIR/suggest-ref-mut.rs:23:22 + --> $DIR/suggest-ref-mut.rs:25:22 | LL | ref quo => { *quo = 32; }, | ^^^^^^^^^ `quo` is a `&` reference, so the data it refers to cannot be written diff --git a/tests/ui/suggestions/tuple-struct-where-clause-suggestion-91520.rs b/tests/ui/suggestions/tuple-struct-where-clause-suggestion-91520.rs new file mode 100644 index 0000000000000..b7086325d5f88 --- /dev/null +++ b/tests/ui/suggestions/tuple-struct-where-clause-suggestion-91520.rs @@ -0,0 +1,17 @@ +// Verify that the `where` clause suggestion is in the correct place +// Previously, the suggestion to add `where` clause was placed inside the derive +// like `#[derive(Clone where Inner: Clone)]` +// instead of `struct Outer(Inner) where Inner: Clone` + +#![crate_type = "lib"] + +struct Inner(T); +//~^ HELP consider annotating `Inner` with `#[derive(Clone)]` +impl Clone for Inner<()> { + fn clone(&self) -> Self { todo!() } +} + +#[derive(Clone)] +struct Outer(Inner); +//~^ ERROR the trait bound `Inner: Clone` is not satisfied [E0277] +//~| HELP consider introducing a `where` clause diff --git a/tests/ui/suggestions/tuple-struct-where-clause-suggestion-91520.stderr b/tests/ui/suggestions/tuple-struct-where-clause-suggestion-91520.stderr new file mode 100644 index 0000000000000..577b090ce1b4f --- /dev/null +++ b/tests/ui/suggestions/tuple-struct-where-clause-suggestion-91520.stderr @@ -0,0 +1,21 @@ +error[E0277]: the trait bound `Inner: Clone` is not satisfied + --> $DIR/tuple-struct-where-clause-suggestion-91520.rs:15:17 + | +LL | #[derive(Clone)] + | ----- in this derive macro expansion +LL | struct Outer(Inner); + | ^^^^^^^^ the trait `Clone` is not implemented for `Inner` + | +help: consider annotating `Inner` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct Inner(T); + | +help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement + | +LL | struct Outer(Inner) where Inner: Clone; + | +++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/suggestions/type-ascription-and-other-error.rs b/tests/ui/suggestions/type-ascription-and-other-error.rs index 99ab2f3c858b9..da985b53aef6f 100644 --- a/tests/ui/suggestions/type-ascription-and-other-error.rs +++ b/tests/ui/suggestions/type-ascription-and-other-error.rs @@ -1,6 +1,6 @@ fn main() { not rust; //~ ERROR let _ = 0: i32; // (error hidden by existing error) - #[cfg(FALSE)] + #[cfg(false)] let _ = 0: i32; // (warning hidden by existing error) } diff --git a/tests/ui/suggestions/type-ascription-instead-of-method.stderr b/tests/ui/suggestions/type-ascription-instead-of-method.stderr index 0bef1c185db63..6dc7b5e18ef80 100644 --- a/tests/ui/suggestions/type-ascription-instead-of-method.stderr +++ b/tests/ui/suggestions/type-ascription-instead-of-method.stderr @@ -4,7 +4,6 @@ error: path separator must be a double colon LL | let _ = Box:new("foo".to_string()); | ^ | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 help: use a double colon instead | LL | let _ = Box::new("foo".to_string()); diff --git a/tests/ui/suggestions/type-ascription-instead-of-path-2.stderr b/tests/ui/suggestions/type-ascription-instead-of-path-2.stderr index 0b37bf9a57bb7..79dffc0cf9b08 100644 --- a/tests/ui/suggestions/type-ascription-instead-of-path-2.stderr +++ b/tests/ui/suggestions/type-ascription-instead-of-path-2.stderr @@ -4,7 +4,6 @@ error: expected one of `(`, `.`, `::`, `;`, `?`, `else`, or an operator, found ` LL | let _ = vec![Ok(2)].into_iter().collect:,_>>()?; | ^ expected one of 7 possible tokens | - = note: type ascription syntax has been removed, see issue #101728 help: maybe write a path separator here | LL | let _ = vec![Ok(2)].into_iter().collect::,_>>()?; diff --git a/tests/ui/suggestions/type-ascription-instead-of-path.stderr b/tests/ui/suggestions/type-ascription-instead-of-path.stderr index 8c16acff79947..a8364611b50c3 100644 --- a/tests/ui/suggestions/type-ascription-instead-of-path.stderr +++ b/tests/ui/suggestions/type-ascription-instead-of-path.stderr @@ -4,7 +4,6 @@ error: path separator must be a double colon LL | std:io::stdin(); | ^ | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 help: use a double colon instead | LL | std::io::stdin(); diff --git a/tests/ui/suggestions/type-ascription-instead-of-variant.stderr b/tests/ui/suggestions/type-ascription-instead-of-variant.stderr index f0b31722e40ef..e836b37c100dc 100644 --- a/tests/ui/suggestions/type-ascription-instead-of-variant.stderr +++ b/tests/ui/suggestions/type-ascription-instead-of-variant.stderr @@ -4,7 +4,6 @@ error: path separator must be a double colon LL | let _ = Option:Some(""); | ^ | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 help: use a double colon instead | LL | let _ = Option::Some(""); diff --git a/tests/ui/suggestions/types/dont-suggest-path-names.rs b/tests/ui/suggestions/types/dont-suggest-path-names.rs index d160e49cd0358..dfd671bfe5569 100644 --- a/tests/ui/suggestions/types/dont-suggest-path-names.rs +++ b/tests/ui/suggestions/types/dont-suggest-path-names.rs @@ -5,11 +5,11 @@ struct Select(F, I); fn select(filter: F) -> Select {} -//~^ 7:31: 7:43: mismatched types [E0308] +//~^ ERROR mismatched types [E0308] fn parser1() { let lit = select(|x| match x { - //~^ 11:23: 11:24: type annotations needed [E0282] + //~^ ERROR type annotations needed [E0282] _ => (), }); } diff --git a/tests/ui/super-let.borrowck.stderr b/tests/ui/super-let.borrowck.stderr new file mode 100644 index 0000000000000..01ef29d875880 --- /dev/null +++ b/tests/ui/super-let.borrowck.stderr @@ -0,0 +1,174 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/super-let.rs:30:28 + | +LL | super let b = DropMe(&mut x); + | ------ `x` is borrowed here +... +LL | #[cfg(borrowck)] { x = true; } + | ^^^^^^^^ `x` is assigned to here but it was already borrowed +... +LL | } + | - borrow might be used here, when `b` is dropped and runs the `Drop` code for type `DropMe` + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/super-let.rs:46:28 + | +LL | super let b = &DropMe(&mut x); + | -------------- + | | | + | | `x` is borrowed here + | a temporary with access to the borrow is created here ... +... +LL | #[cfg(borrowck)] { x = true; } + | ^^^^^^^^ `x` is assigned to here but it was already borrowed +... +LL | } + | - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DropMe` + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/super-let.rs:64:32 + | +LL | super let b = identity(&DropMe(&mut x)); + | -------------- + | | | + | | `x` is borrowed here + | a temporary with access to the borrow is created here ... +LL | #[cfg(borrowck)] { x = true; } + | ^^^^^^^^ `x` is assigned to here but it was already borrowed +... +LL | }; + | - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DropMe` + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/super-let.rs:87:36 + | +LL | super let b = identity(&DropMe(&mut x)); + | -------------- + | | | + | | `x` is borrowed here + | a temporary with access to the borrow is created here ... +... +LL | #[cfg(borrowck)] { x = true; } + | ^^^^^^^^ `x` is assigned to here but it was already borrowed +... +LL | )); + | - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DropMe` + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/super-let.rs:107:28 + | +LL | super let b = DropMe(&mut x); + | ------ `x` is borrowed here +... +LL | #[cfg(borrowck)] { x = true; } + | ^^^^^^^^ `x` is assigned to here but it was already borrowed +... +LL | } + | - borrow might be used here, when `b` is dropped and runs the `Drop` code for type `DropMe` + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/super-let.rs:125:28 + | +LL | super let b = DropMe(&mut x); + | ------ `x` is borrowed here +... +LL | #[cfg(borrowck)] { x = true; } + | ^^^^^^^^ `x` is assigned to here but it was already borrowed +... +LL | } + | - borrow might be used here, when `b` is dropped and runs the `Drop` code for type `DropMe` + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/super-let.rs:143:28 + | +LL | super let b = DropMe(&mut x); + | ------ `x` is borrowed here +... +LL | #[cfg(borrowck)] { x = true; } + | ^^^^^^^^ `x` is assigned to here but it was already borrowed +... +LL | } + | - borrow might be used here, when `b` is dropped and runs the `Drop` code for type `DropMe` + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/super-let.rs:159:28 + | +LL | b = DropMe(&mut x); + | ------ `x` is borrowed here +... +LL | #[cfg(borrowck)] { x = true; } + | ^^^^^^^^ `x` is assigned to here but it was already borrowed +LL | drop(a); + | - borrow later used here + +error[E0716]: temporary value dropped while borrowed + --> $DIR/super-let.rs:172:33 + | +LL | #[cfg(borrowck)] { a = &String::from("asdf"); }; + | ^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement + | | + | creates a temporary value which is freed while still in use +... +LL | let _ = a; + | - borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/super-let.rs:206:28 + | +LL | super let d = &DropMe(&mut x); + | -------------- + | | | + | | `x` is borrowed here + | a temporary with access to the borrow is created here ... +... +LL | #[cfg(borrowck)] { x = true; } + | ^^^^^^^^ `x` is assigned to here but it was already borrowed +... +LL | } + | - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DropMe` + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/super-let.rs:227:32 + | +LL | super let d = identity(&DropMe(&mut x)); + | -------------- + | | | + | | `x` is borrowed here + | a temporary with access to the borrow is created here ... +... +LL | #[cfg(borrowck)] { x = true; } + | ^^^^^^^^ `x` is assigned to here but it was already borrowed +... +LL | }; + | - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DropMe` + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/super-let.rs:246:28 + | +LL | super let b = DropMe(&mut x); + | ------ `x` is borrowed here +... +LL | #[cfg(borrowck)] { x = true; } + | ^^^^^^^^ `x` is assigned to here but it was already borrowed +... +LL | } + | - borrow might be used here, when `b` is dropped and runs the `Drop` code for type `DropMe` + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/super-let.rs:263:28 + | +LL | let dropme = Some(DropMe(&mut x)); + | ------ `x` is borrowed here +... +LL | #[cfg(borrowck)] { x = true; } + | ^^^^^^^^ `x` is assigned to here but it was already borrowed +... +LL | } + | - borrow might be used here, when `x` is dropped and runs the `Drop` code for type `DropMe` + +error: aborting due to 13 previous errors + +Some errors have detailed explanations: E0506, E0716. +For more information about an error, try `rustc --explain E0506`. diff --git a/tests/ui/super-let.rs b/tests/ui/super-let.rs new file mode 100644 index 0000000000000..380470f792fe1 --- /dev/null +++ b/tests/ui/super-let.rs @@ -0,0 +1,286 @@ +// Check in two ways: +// - borrowck: Check with borrow checking errors when things are alive and dead. +// - runtime: Check with a mutable bool if things are dropped on time. +// +//@ revisions: runtime borrowck +//@ [runtime] run-pass +//@ [borrowck] check-fail + +#![allow(dropping_references)] +#![feature(super_let, stmt_expr_attributes)] + +use std::convert::identity; + +struct DropMe<'a>(&'a mut bool); + +impl Drop for DropMe<'_> { + fn drop(&mut self) { + *self.0 = true; + } +} + +// Check that a super let variable lives as long as the result of a block. +fn extended_variable() { + let mut x = false; + { + let a = { + super let b = DropMe(&mut x); + &b + }; + #[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed + drop(a); + // DropMe is still alive here... + } + // ... but not here. + assert_eq!(x, true) // ok +} + +// Check that the init expression of a super let is subject to (temporary) lifetime extension. +fn extended_temporary() { + let mut x = false; + { + let a = { + super let b = &DropMe(&mut x); + b + }; + #[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed + drop(a); + // DropMe is still alive here... + } + // ... but not here. + assert_eq!(x, true); // ok +} + +// Check that even non-extended temporaries live until the end of the block, +// but (unlike extended temporaries) not beyond that. +// +// This is necessary for things like select(pin!(identity(&temp()))) to work. +fn non_extended() { + let mut x = false; + { + let _a = { + // Use identity() to supress temporary lifetime extension. + super let b = identity(&DropMe(&mut x)); + #[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed + b + // DropMe is still alive here... + }; + // ... but not here. + assert_eq!(x, true); // ok + } +} + +// Check that even non-extended temporaries live until the end of the block, +// but (unlike extended temporaries) not beyond that. +// +// This is necessary for things like select(pin!(identity(&temp()))) to work. +fn non_extended_in_expression() { + let mut x = false; + { + identity(( + { + // Use identity() to supress temporary lifetime extension. + super let b = identity(&DropMe(&mut x)); + b + }, + { + #[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed + // DropMe is still alive here... + } + )); + // ... but not here. + assert_eq!(x, true); // ok + } +} + +// Check `super let` in a match arm. +fn match_arm() { + let mut x = false; + { + let a = match Some(123) { + Some(_) => { + super let b = DropMe(&mut x); + &b + } + None => unreachable!(), + }; + #[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed + drop(a); + // DropMe is still alive here... + } + // ... but not here. + assert_eq!(x, true); // ok +} + +// Check `super let` in an if body. +fn if_body() { + let mut x = false; + { + let a = if true { + super let b = DropMe(&mut x); + &b + } else { + unreachable!() + }; + #[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed + drop(a); + // DropMe is still alive here... + } + // ... but not here. + assert_eq!(x, true); // ok +} + +// Check `super let` in an else body. +fn else_body() { + let mut x = false; + { + let a = if false { + unreachable!() + } else { + super let b = DropMe(&mut x); + &b + }; + #[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed + drop(a); + // DropMe is still alive here... + } + // ... but not here. + assert_eq!(x, true); // ok +} + +fn without_initializer() { + let mut x = false; + { + let a = { + super let b; + b = DropMe(&mut x); + b + }; + #[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed + drop(a); + // DropMe is still alive here... + } + // ... but not here. + assert_eq!(x, true); +} + +// Assignment isn't special, even when assigning to a `super let` variable. +fn assignment() { + let mut x = false; + { + super let a; + #[cfg(borrowck)] { a = &String::from("asdf"); }; //[borrowck]~ ERROR dropped while borrowed + #[cfg(runtime)] { a = drop(&DropMe(&mut x)); } // Temporary dropped at the `;` as usual. + assert_eq!(x, true); + let _ = a; + } +} + +// `super let mut` should work just fine. +fn mutable() { + let mut x = false; + { + let a = { + super let mut b = None; + &mut b + }; + *a = Some(DropMe(&mut x)); + } + assert_eq!(x, true); +} + +// Temporary lifetime extension should recurse through `super let`s. +fn multiple_levels() { + let mut x = false; + { + let a = { + super let b = { + super let c = { + super let d = &DropMe(&mut x); + d + }; + c + }; + b + }; + #[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed + drop(a); + // DropMe is still alive here... + } + // ... but not here. + assert_eq!(x, true); +} + +// Non-extended temporaries should be dropped at the +// end of the first parent statement that isn't `super`. +fn multiple_levels_but_no_extension() { + let mut x = false; + { + let _a = { + super let b = { + super let c = { + super let d = identity(&DropMe(&mut x)); + d + }; + c + }; + #[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed + b + // DropMe is still alive here... + }; + // ... but not here. + assert_eq!(x, true); + } +} + +// Check for potential weird interactions with `let else`. +fn super_let_and_let_else() { + let mut x = false; + { + let a = 'a: { + let Some(_) = Some(123) else { unreachable!() }; + super let b = DropMe(&mut x); + let None = Some(123) else { break 'a &b }; + unreachable!() + }; + #[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed + // DropMe is still alive here... + drop(a); + } + // ... but not here. + assert_eq!(x, true); +} + +// Check if `super let .. else ..;` works. +fn super_let_else() { + let mut x = false; + { + let a = { + let dropme = Some(DropMe(&mut x)); + super let Some(x) = dropme else { unreachable!() }; + &x + }; + #[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed + // DropMe is still alive here... + drop(a); + } + // ... but not here. + assert_eq!(x, true); +} + +fn main() { + extended_variable(); + extended_temporary(); + non_extended(); + non_extended_in_expression(); + match_arm(); + if_body(); + else_body(); + without_initializer(); + assignment(); + mutable(); + multiple_levels(); + multiple_levels_but_no_extension(); + super_let_and_let_else(); + super_let_else(); +} diff --git a/tests/ui/symbol-mangling-version/bad-value.rs b/tests/ui/symbol-mangling-version/bad-value.rs index f6fa5c85f3386..b0875f3a23cd8 100644 --- a/tests/ui/symbol-mangling-version/bad-value.rs +++ b/tests/ui/symbol-mangling-version/bad-value.rs @@ -4,3 +4,7 @@ //@ [bad] compile-flags: -Csymbol-mangling-version=bad-value fn main() {} + +//[no-value]~? ERROR codegen option `symbol-mangling-version` requires one of +//[blank]~? ERROR incorrect value `` for codegen option `symbol-mangling-version` +//[bad]~? ERROR incorrect value `bad-value` for codegen option `symbol-mangling-version` diff --git a/tests/ui/symbol-mangling-version/unstable.rs b/tests/ui/symbol-mangling-version/unstable.rs index d5af8542996b3..d79320ccccd5b 100644 --- a/tests/ui/symbol-mangling-version/unstable.rs +++ b/tests/ui/symbol-mangling-version/unstable.rs @@ -7,3 +7,6 @@ //@ [hashed-ok] compile-flags: -Zunstable-options -Csymbol-mangling-version=hashed fn main() {} + +//[legacy]~? ERROR `-C symbol-mangling-version=legacy` requires `-Z unstable-options` +//[hashed]~? ERROR `-C symbol-mangling-version=hashed` requires `-Z unstable-options` diff --git a/tests/ui/syntax-extension-minor.rs b/tests/ui/syntax-extension-minor.rs index cdd572b50fcce..826990a89a533 100644 --- a/tests/ui/syntax-extension-minor.rs +++ b/tests/ui/syntax-extension-minor.rs @@ -1,6 +1,7 @@ //@ run-pass #![feature(concat_idents)] +#![expect(deprecated)] // concat_idents is deprecated pub fn main() { struct Foo; diff --git a/tests/ui/tail-typeck.rs b/tests/ui/tail-typeck.rs index feef58a338861..1deb43c94965b 100644 --- a/tests/ui/tail-typeck.rs +++ b/tests/ui/tail-typeck.rs @@ -1,6 +1,4 @@ -//@ error-pattern: mismatched types - -fn f() -> isize { return g(); } +fn f() -> isize { return g(); } //~ ERROR mismatched types fn g() -> usize { return 0; } diff --git a/tests/ui/tail-typeck.stderr b/tests/ui/tail-typeck.stderr index 0e470a7b4057f..3cfbfa0fb562e 100644 --- a/tests/ui/tail-typeck.stderr +++ b/tests/ui/tail-typeck.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/tail-typeck.rs:3:26 + --> $DIR/tail-typeck.rs:1:26 | LL | fn f() -> isize { return g(); } | ----- ^^^ expected `isize`, found `usize` diff --git a/tests/ui/amdgpu-require-explicit-cpu.nocpu.stderr b/tests/ui/target-cpu/explicit-target-cpu.amdgcn_nocpu.stderr similarity index 100% rename from tests/ui/amdgpu-require-explicit-cpu.nocpu.stderr rename to tests/ui/target-cpu/explicit-target-cpu.amdgcn_nocpu.stderr diff --git a/tests/ui/target-cpu/explicit-target-cpu.avr_nocpu.stderr b/tests/ui/target-cpu/explicit-target-cpu.avr_nocpu.stderr new file mode 100644 index 0000000000000..7480a8ed38f15 --- /dev/null +++ b/tests/ui/target-cpu/explicit-target-cpu.avr_nocpu.stderr @@ -0,0 +1,4 @@ +error: target requires explicitly specifying a cpu with `-C target-cpu` + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-cpu/explicit-target-cpu.rs b/tests/ui/target-cpu/explicit-target-cpu.rs new file mode 100644 index 0000000000000..cd4c2384bc1df --- /dev/null +++ b/tests/ui/target-cpu/explicit-target-cpu.rs @@ -0,0 +1,37 @@ +//! Check that certain target *requires* the user to specify a target CPU via `-C target-cpu`. + +//@ revisions: amdgcn_nocpu amdgcn_cpu + +//@[amdgcn_nocpu] compile-flags: --target=amdgcn-amd-amdhsa +//@[amdgcn_nocpu] needs-llvm-components: amdgpu +//@[amdgcn_nocpu] build-fail + +//@[amdgcn_cpu] compile-flags: --target=amdgcn-amd-amdhsa +//@[amdgcn_cpu] needs-llvm-components: amdgpu +//@[amdgcn_cpu] compile-flags: -Ctarget-cpu=gfx900 +//@[amdgcn_cpu] build-pass + +//@ revisions: avr_nocpu avr_cpu + +//@[avr_nocpu] compile-flags: --target=avr-none +//@[avr_nocpu] needs-llvm-components: avr +//@[avr_nocpu] build-fail + +//@[avr_cpu] compile-flags: --target=avr-none +//@[avr_cpu] needs-llvm-components: avr +//@[avr_cpu] compile-flags: -Ctarget-cpu=atmega328p +//@[avr_cpu] build-pass + +#![crate_type = "rlib"] + +// FIXME(#140038): this can't use `minicore` yet because `minicore` doesn't currently propagate the +// `-C target-cpu` for targets that *require* a `target-cpu` being specified. +#![feature(no_core, lang_items)] +#![no_core] + +#[lang="sized"] +trait Sized {} + +pub fn foo() {} + +//[amdgcn_nocpu,avr_nocpu]~? ERROR target requires explicitly specifying a cpu with `-C target-cpu` diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-attribute.riscv.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.riscv.stderr new file mode 100644 index 0000000000000..49c5479275f39 --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.riscv.stderr @@ -0,0 +1,8 @@ +error: target feature `d` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI + --> $DIR/abi-incompatible-target-feature-attribute.rs:15:90 + | +LL | #[cfg_attr(x86, target_feature(enable = "soft-float"))] #[cfg_attr(riscv, target_feature(enable = "d"))] + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-attribute.rs b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.rs new file mode 100644 index 0000000000000..a873344075920 --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.rs @@ -0,0 +1,17 @@ +//! Ensure ABI-incompatible features cannot be enabled via `#[target_feature]`. +// ignore-tidy-linelength +//@ compile-flags: --crate-type=lib +//@ revisions: x86 riscv +//@[x86] compile-flags: --target=x86_64-unknown-linux-gnu +//@[x86] needs-llvm-components: x86 +//@[riscv] compile-flags: --target=riscv32e-unknown-none-elf +//@[riscv] needs-llvm-components: riscv +#![feature(no_core, lang_items, riscv_target_feature, x87_target_feature)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +#[cfg_attr(x86, target_feature(enable = "soft-float"))] #[cfg_attr(riscv, target_feature(enable = "d"))] +//~^ERROR: cannot be enabled with +pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-attribute.x86.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.x86.stderr new file mode 100644 index 0000000000000..81471fd7e303e --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-attribute.x86.stderr @@ -0,0 +1,8 @@ +error: target feature `soft-float` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI + --> $DIR/abi-incompatible-target-feature-attribute.rs:15:32 + | +LL | #[cfg_attr(x86, target_feature(enable = "soft-float"))] #[cfg_attr(riscv, target_feature(enable = "d"))] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr new file mode 100644 index 0000000000000..2dca0c220332b --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr @@ -0,0 +1,19 @@ +warning: target feature `d` must be disabled to ensure that the ABI of the current target can be implemented correctly + | + = note: 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 #116344 + +warning: unstable feature specified for `-Ctarget-feature`: `d` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: unstable feature specified for `-Ctarget-feature`: `f` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: unstable feature specified for `-Ctarget-feature`: `zicsr` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: 4 warnings emitted + diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs new file mode 100644 index 0000000000000..68e1d3b9ddc61 --- /dev/null +++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs @@ -0,0 +1,22 @@ +//! Ensure ABI-incompatible features cannot be enabled via `-Ctarget-feature`. +// These are just warnings for now. +//@ check-pass +//@ compile-flags: --crate-type=lib +//@ revisions: x86 riscv +//@[x86] compile-flags: --target=x86_64-unknown-linux-gnu -Ctarget-feature=+soft-float +//@[x86] needs-llvm-components: x86 +//@[riscv] compile-flags: --target=riscv32e-unknown-none-elf -Ctarget-feature=+d +//@[riscv] needs-llvm-components: riscv + +#![feature(no_core, lang_items, riscv_target_feature)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} +#[lang = "freeze"] +pub trait Freeze {} + +//~? WARN must be disabled to ensure that the ABI of the current target can be implemented correctly +//~? WARN unstable feature specified for `-Ctarget-feature` +//[riscv]~? WARN unstable feature specified for `-Ctarget-feature` +//[riscv]~? WARN unstable feature specified for `-Ctarget-feature` diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.x86.stderr similarity index 100% rename from tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.stderr rename to tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.x86.stderr diff --git a/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.rs b/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.rs new file mode 100644 index 0000000000000..0013d033b9c5e --- /dev/null +++ b/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.rs @@ -0,0 +1,14 @@ +//! `x87` is a required target feature on some x86 targets, but not on this one as this one +//! uses soft-floats. So ensure disabling the target feature here (which is a NOP) does +//! not trigger a warning. +//@ compile-flags: --target=x86_64-unknown-none --crate-type=lib +//@ needs-llvm-components: x86 +//@ compile-flags: -Ctarget-feature=-x87 +//@ build-pass +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +//~? WARN unstable feature specified for `-Ctarget-feature`: `x87` diff --git a/tests/ui/target-feature/allowed-softfloat-target-feature-flag-disable.stderr b/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.stderr similarity index 100% rename from tests/ui/target-feature/allowed-softfloat-target-feature-flag-disable.stderr rename to tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.stderr diff --git a/tests/ui/target-feature/abi-required-target-feature-attribute.rs b/tests/ui/target-feature/abi-required-target-feature-attribute.rs new file mode 100644 index 0000000000000..95723c57f94d4 --- /dev/null +++ b/tests/ui/target-feature/abi-required-target-feature-attribute.rs @@ -0,0 +1,13 @@ +//! Enabling a target feature that is anyway required changes nothing, so this is allowed +//! for `#[target_feature]`. +//@ compile-flags: --target=x86_64-unknown-none --crate-type=lib +//@ needs-llvm-components: x86 +//@ build-pass +#![feature(no_core, lang_items, x87_target_feature)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +#[target_feature(enable = "x87")] +pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr b/tests/ui/target-feature/abi-required-target-feature-flag-disable.aarch64.stderr similarity index 100% rename from tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr rename to tests/ui/target-feature/abi-required-target-feature-flag-disable.aarch64.stderr diff --git a/tests/ui/target-feature/abi-required-target-feature-flag-disable.loongarch.stderr b/tests/ui/target-feature/abi-required-target-feature-flag-disable.loongarch.stderr new file mode 100644 index 0000000000000..a69544a34c9af --- /dev/null +++ b/tests/ui/target-feature/abi-required-target-feature-flag-disable.loongarch.stderr @@ -0,0 +1,7 @@ +warning: target feature `d` must be enabled to ensure that the ABI of the current target can be implemented correctly + | + = note: 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 #116344 + +warning: 1 warning emitted + diff --git a/tests/ui/target-feature/abi-required-target-feature-flag-disable.riscv.stderr b/tests/ui/target-feature/abi-required-target-feature-flag-disable.riscv.stderr new file mode 100644 index 0000000000000..35102e0571f43 --- /dev/null +++ b/tests/ui/target-feature/abi-required-target-feature-flag-disable.riscv.stderr @@ -0,0 +1,11 @@ +warning: target feature `d` must be enabled to ensure that the ABI of the current target can be implemented correctly + | + = note: 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 #116344 + +warning: unstable feature specified for `-Ctarget-feature`: `d` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: 2 warnings emitted + diff --git a/tests/ui/target-feature/abi-required-target-feature-flag-disable.rs b/tests/ui/target-feature/abi-required-target-feature-flag-disable.rs new file mode 100644 index 0000000000000..98723e99c36e3 --- /dev/null +++ b/tests/ui/target-feature/abi-required-target-feature-flag-disable.rs @@ -0,0 +1,27 @@ +//! Ensure ABI-required features cannot be disabled via `-Ctarget-feature`. +//! Also covers the case of a feature indirectly disabling another via feature implications. +//@ compile-flags: --crate-type=lib +//@ revisions: x86 x86-implied aarch64 riscv loongarch +//@[x86] compile-flags: --target=x86_64-unknown-linux-gnu -Ctarget-feature=-x87 +//@[x86] needs-llvm-components: x86 +//@[x86-implied] compile-flags: --target=x86_64-unknown-linux-gnu -Ctarget-feature=-sse +//@[x86-implied] needs-llvm-components: x86 +//@[aarch64] compile-flags: --target=aarch64-unknown-linux-gnu -Ctarget-feature=-neon +//@[aarch64] needs-llvm-components: aarch64 +//@[riscv] compile-flags: --target=riscv64gc-unknown-none-elf -Ctarget-feature=-d +//@[riscv] needs-llvm-components: riscv +//@[loongarch] compile-flags: --target=loongarch64-unknown-none -Ctarget-feature=-d +//@[loongarch] needs-llvm-components: loongarch +// For now this is just a warning. +//@ build-pass +// Remove some LLVM warnings that only show up sometimes. +//@ normalize-stderr: "\n[^\n]*(target-abi|lp64f)[^\n]*" -> "" + +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +//~? WARN must be enabled to ensure that the ABI of the current target can be implemented correctly +//[x86,riscv]~? WARN unstable feature specified for `-Ctarget-feature` diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr b/tests/ui/target-feature/abi-required-target-feature-flag-disable.x86-implied.stderr similarity index 100% rename from tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr rename to tests/ui/target-feature/abi-required-target-feature-flag-disable.x86-implied.stderr diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr b/tests/ui/target-feature/abi-required-target-feature-flag-disable.x86.stderr similarity index 100% rename from tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr rename to tests/ui/target-feature/abi-required-target-feature-flag-disable.x86.stderr diff --git a/tests/ui/target-feature/allowed-softfloat-target-feature-attribute.rs b/tests/ui/target-feature/allowed-softfloat-target-feature-attribute.rs deleted file mode 100644 index 8b60820cc9b68..0000000000000 --- a/tests/ui/target-feature/allowed-softfloat-target-feature-attribute.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ compile-flags: --target=x86_64-unknown-none --crate-type=lib -//@ needs-llvm-components: x86 -//@ build-pass -#![feature(no_core, lang_items, x87_target_feature)] -#![no_core] - -#[lang = "sized"] -pub trait Sized {} - -#[target_feature(enable = "x87")] -pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/allowed-softfloat-target-feature-flag-disable.rs b/tests/ui/target-feature/allowed-softfloat-target-feature-flag-disable.rs deleted file mode 100644 index e34faf5a983c3..0000000000000 --- a/tests/ui/target-feature/allowed-softfloat-target-feature-flag-disable.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ compile-flags: --target=x86_64-unknown-none --crate-type=lib -//@ needs-llvm-components: x86 -//@ compile-flags: -Ctarget-feature=-x87 -//@ build-pass -#![feature(no_core, lang_items)] -#![no_core] - -#[lang = "sized"] -pub trait Sized {} diff --git a/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs b/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs index 2682028936c19..15bcfdd9076e0 100644 --- a/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs +++ b/tests/ui/target-feature/auxiliary/using-target-feature-unstable.rs @@ -1,5 +1,5 @@ -#![feature(avx512_target_feature)] +#![feature(x87_target_feature)] #[inline] -#[target_feature(enable = "avx512ifma")] +#[target_feature(enable = "x87")] pub unsafe fn foo() {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-e-d.rs similarity index 100% rename from tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs rename to tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-e-d.rs diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-e-d.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-e-d.stderr new file mode 100644 index 0000000000000..84d27463b38cd --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-e-d.stderr @@ -0,0 +1,8 @@ +error: target feature `d` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI + --> $DIR/forbidden-hardfloat-target-feature-attribute-e-d.rs:10:18 + | +LL | #[target_feature(enable = "d")] + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-f-zfinx.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-f-zfinx.rs new file mode 100644 index 0000000000000..d74f4a1d4b170 --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-f-zfinx.rs @@ -0,0 +1,12 @@ +//! Ensure ABI-incompatible features cannot be enabled via `#[target_feature]`. +//@ compile-flags: --target=riscv64gc-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: riscv +#![feature(no_core, lang_items, riscv_target_feature)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +#[target_feature(enable = "zdinx")] +//~^ERROR: cannot be enabled with +pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-f-zfinx.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-f-zfinx.stderr new file mode 100644 index 0000000000000..af0e53f34f23a --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute-f-zfinx.stderr @@ -0,0 +1,8 @@ +error: target feature `zfinx` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI + --> $DIR/forbidden-hardfloat-target-feature-attribute-f-zfinx.rs:10:18 + | +LL | #[target_feature(enable = "zdinx")] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr deleted file mode 100644 index bfe767e5ffb07..0000000000000 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: target feature `d` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI - --> $DIR/forbidden-hardfloat-target-feature-attribute.rs:10:18 - | -LL | #[target_feature(enable = "d")] - | ^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs deleted file mode 100644 index 81f138b175f29..0000000000000 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Ensure that if disabling a target feature implies disabling an ABI-required target feature, -//! we complain. -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -//@ compile-flags: -Ctarget-feature=-sse -// For now this is just a warning. -//@ build-pass -//@error-pattern: must be enabled to ensure that the ABI -#![feature(no_core, lang_items)] -#![no_core] - -#[lang = "sized"] -pub trait Sized {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs deleted file mode 100644 index 7242bcc85bfd1..0000000000000 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ compile-flags: --target=aarch64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: aarch64 -//@ compile-flags: -Ctarget-feature=-neon -// For now this is just a warning. -//@ build-pass -//@error-pattern: must be enabled to ensure that the ABI -#![feature(no_core, lang_items)] -#![no_core] - -#[lang = "sized"] -pub trait Sized {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs deleted file mode 100644 index 7eebcf05dc0f4..0000000000000 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! Ensure ABI-required features cannot be disabled via `-Ctarget-feature`. -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -//@ compile-flags: -Ctarget-feature=-x87 -// For now this is just a warning. -//@ build-pass -//@error-pattern: must be enabled to ensure that the ABI -#![feature(no_core, lang_items)] -#![no_core] - -#[lang = "sized"] -pub trait Sized {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs deleted file mode 100644 index f277a309cd698..0000000000000 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! Ensure ABI-incompatible features cannot be enabled via `-Ctarget-feature`. -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -//@ compile-flags: -Ctarget-feature=+soft-float -// For now this is just a warning. -//@ build-pass -//@error-pattern: must be disabled to ensure that the ABI -#![feature(no_core, lang_items, riscv_target_feature)] -#![no_core] - -#[lang = "sized"] -pub trait Sized {} diff --git a/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs b/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs index cf85c5212289c..d394dbe7b15d7 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs +++ b/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs @@ -4,9 +4,11 @@ //@ compile-flags: -Ctarget-feature=-forced-atomics // For now this is just a warning. //@ build-pass -//@error-pattern: unsound because it changes the ABI + #![feature(no_core, lang_items)] #![no_core] #[lang = "sized"] pub trait Sized {} + +//~? WARN target feature `forced-atomics` cannot be disabled with `-Ctarget-feature`: unsound because it changes the ABI of atomic operations diff --git a/tests/ui/target-feature/forbidden-target-feature-flag.rs b/tests/ui/target-feature/forbidden-target-feature-flag.rs index 245841eb0395c..a04d7e3475305 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag.rs +++ b/tests/ui/target-feature/forbidden-target-feature-flag.rs @@ -4,9 +4,11 @@ //@ compile-flags: -Ctarget-feature=+forced-atomics // For now this is just a warning. //@ build-pass -//@error-pattern: unsound because it changes the ABI + #![feature(no_core, lang_items)] #![no_core] #[lang = "sized"] pub trait Sized {} + +//~? WARN target feature `forced-atomics` cannot be enabled with `-Ctarget-feature`: unsound because it changes the ABI of atomic operations diff --git a/tests/ui/target-feature/gate.rs b/tests/ui/target-feature/gate.rs index 14fdad02f5665..9244a98d82fdf 100644 --- a/tests/ui/target-feature/gate.rs +++ b/tests/ui/target-feature/gate.rs @@ -2,7 +2,6 @@ // // gate-test-sse4a_target_feature // gate-test-powerpc_target_feature -// gate-test-avx512_target_feature // gate-test-tbm_target_feature // gate-test-arm_target_feature // gate-test-hexagon_target_feature @@ -27,7 +26,7 @@ // gate-test-x87_target_feature // gate-test-m68k_target_feature -#[target_feature(enable = "avx512bw")] +#[target_feature(enable = "x87")] //~^ ERROR: currently unstable unsafe fn foo() {} diff --git a/tests/ui/target-feature/gate.stderr b/tests/ui/target-feature/gate.stderr index fa876893848f7..32d60ce438227 100644 --- a/tests/ui/target-feature/gate.stderr +++ b/tests/ui/target-feature/gate.stderr @@ -1,11 +1,11 @@ -error[E0658]: the target feature `avx512bw` is currently unstable - --> $DIR/gate.rs:30:18 +error[E0658]: the target feature `x87` is currently unstable + --> $DIR/gate.rs:29:18 | -LL | #[target_feature(enable = "avx512bw")] - | ^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "x87")] + | ^^^^^^^^^^^^^^ | = note: see issue #44839 for more information - = help: add `#![feature(avx512_target_feature)]` to the crate attributes to enable + = help: add `#![feature(x87_target_feature)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 1 previous error diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index dc8a53041640e..05ae49d6b0ddb 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -98,6 +98,12 @@ LL | LL | trait Baz {} | ------------ not a function definition +error: cannot use `#[inline(always)]` with `#[target_feature]` + --> $DIR/invalid-attribute.rs:69:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:74:1 | @@ -163,12 +169,6 @@ error: malformed `target_feature` attribute input LL | #[target_feature(disable = "baz")] | ^^^^^^^^^^^^^^^ help: must be of the form: `enable = ".."` -error: cannot use `#[inline(always)]` with `#[target_feature]` - --> $DIR/invalid-attribute.rs:69:1 - | -LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ - error[E0046]: not all trait items implemented, missing: `foo` --> $DIR/invalid-attribute.rs:81:1 | diff --git a/tests/ui/target-feature/missing-plusminus-2.rs b/tests/ui/target-feature/missing-plusminus-2.rs index 19f4bc6e72440..06291ab23ad5e 100644 --- a/tests/ui/target-feature/missing-plusminus-2.rs +++ b/tests/ui/target-feature/missing-plusminus-2.rs @@ -4,3 +4,5 @@ #![feature(no_core)] #![no_core] + +//~? WARN unknown feature specified for `-Ctarget-feature`: `rdrand` diff --git a/tests/ui/target-feature/missing-plusminus.rs b/tests/ui/target-feature/missing-plusminus.rs index eb3e93c2ef7fc..e8356e0fa3552 100644 --- a/tests/ui/target-feature/missing-plusminus.rs +++ b/tests/ui/target-feature/missing-plusminus.rs @@ -1,2 +1,4 @@ //@ compile-flags: -Ctarget-feature=banana --crate-type=rlib //@ build-pass + +//~? WARN unknown feature specified for `-Ctarget-feature`: `banana` diff --git a/tests/ui/target-feature/similar-feature-suggestion.rs b/tests/ui/target-feature/similar-feature-suggestion.rs index 242d472b794e3..b82d7e408c7c6 100644 --- a/tests/ui/target-feature/similar-feature-suggestion.rs +++ b/tests/ui/target-feature/similar-feature-suggestion.rs @@ -4,3 +4,5 @@ #![feature(no_core)] #![no_core] + +//~? WARN unknown and unstable feature specified for `-Ctarget-feature`: `rdrnd` diff --git a/tests/ui/target-feature/target-cpu-lacks-required-target-feature.rs b/tests/ui/target-feature/target-cpu-lacks-required-target-feature.rs index 28d026c1a9a2d..be6cd2b6faf8a 100644 --- a/tests/ui/target-feature/target-cpu-lacks-required-target-feature.rs +++ b/tests/ui/target-feature/target-cpu-lacks-required-target-feature.rs @@ -3,10 +3,11 @@ //@ compile-flags: -Ctarget-cpu=pentium // For now this is just a warning. //@ build-pass -//@error-pattern: must be enabled #![feature(no_core, lang_items)] #![no_core] #[lang = "sized"] pub trait Sized {} + +//~? WARN target feature `sse2` must be enabled to ensure that the ABI of the current target can be implemented correctly diff --git a/tests/ui/target-feature/tied-features-cli.rs b/tests/ui/target-feature/tied-features-cli.rs index 17c13826ce9e6..ce1dc3224a19a 100644 --- a/tests/ui/target-feature/tied-features-cli.rs +++ b/tests/ui/target-feature/tied-features-cli.rs @@ -18,3 +18,7 @@ trait Sized {} fn main() {} + +//[one]~? ERROR the target features paca, pacg must all be either enabled or disabled together +//[two]~? ERROR the target features paca, pacg must all be either enabled or disabled together +//[three]~? ERROR the target features paca, pacg must all be either enabled or disabled together diff --git a/tests/ui/target-feature/tied-features-no-implication-1.rs b/tests/ui/target-feature/tied-features-no-implication-1.rs index 0473ca319b8f0..63a1d77dae9f4 100644 --- a/tests/ui/target-feature/tied-features-no-implication-1.rs +++ b/tests/ui/target-feature/tied-features-no-implication-1.rs @@ -2,9 +2,7 @@ //@ compile-flags: --crate-type=rlib --target=aarch64-unknown-linux-gnu //@ needs-llvm-components: aarch64 //@[paca] compile-flags: -Ctarget-feature=+paca -//@[paca] error-pattern: the target features paca, pacg must all be either enabled or disabled together //@[pacg] compile-flags: -Ctarget-feature=+pacg -//@[paca] error-pattern: the target features paca, pacg must all be either enabled or disabled together #![feature(no_core, lang_items)] #![no_core] @@ -18,3 +16,5 @@ trait Sized {} #[cfg(target_feature = "pacg")] pub unsafe fn foo() { } + +//~? ERROR the target features paca, pacg must all be either enabled or disabled together diff --git a/tests/ui/target-feature/tied-features-no-implication.pacg.stderr b/tests/ui/target-feature/tied-features-no-implication.pacg.stderr index 0e31dea24ea4d..4ff42d31e9464 100644 --- a/tests/ui/target-feature/tied-features-no-implication.pacg.stderr +++ b/tests/ui/target-feature/tied-features-no-implication.pacg.stderr @@ -1,10 +1,10 @@ error[E0428]: the name `foo` is defined multiple times - --> $DIR/tied-features-no-implication.rs:28:1 + --> $DIR/tied-features-no-implication.rs:27:1 | LL | fn foo() {} | -------- previous definition of the value `foo` here ... -LL | pub unsafe fn foo() { +LL | pub unsafe fn foo() {} | ^^^^^^^^^^^^^^^^^^^ `foo` redefined here | = note: `foo` must be defined only once in the value namespace of this module diff --git a/tests/ui/target-feature/tied-features-no-implication.rs b/tests/ui/target-feature/tied-features-no-implication.rs index 157b50bb0d329..1625f71431a6b 100644 --- a/tests/ui/target-feature/tied-features-no-implication.rs +++ b/tests/ui/target-feature/tied-features-no-implication.rs @@ -2,9 +2,8 @@ //@ compile-flags: --crate-type=rlib --target=aarch64-unknown-linux-gnu //@ needs-llvm-components: aarch64 //@[paca] compile-flags: -Ctarget-feature=+paca -//@[paca] error-pattern: the target features paca, pacg must all be either enabled or disabled together //@[pacg] compile-flags: -Ctarget-feature=+pacg -//@[pacg] error-pattern: the name `foo` is defined multiple times + #![feature(no_core, lang_items)] #![no_core] @@ -25,5 +24,6 @@ fn foo() {} // be). #[cfg(target_feature = "pacg")] -pub unsafe fn foo() { -} +pub unsafe fn foo() {} //[pacg]~ ERROR the name `foo` is defined multiple times + +//[paca]~? ERROR the target features paca, pacg must all be either enabled or disabled together diff --git a/tests/ui/target-feature/unstable-feature.rs b/tests/ui/target-feature/unstable-feature.rs index c74c5ad5d7798..a79ad4696033a 100644 --- a/tests/ui/target-feature/unstable-feature.rs +++ b/tests/ui/target-feature/unstable-feature.rs @@ -1,6 +1,8 @@ -//@ compile-flags: -Ctarget-feature=+vaes --crate-type=rlib --target=x86_64-unknown-linux-gnu +//@ compile-flags: -Ctarget-feature=+x87 --crate-type=rlib --target=x86_64-unknown-linux-gnu //@ build-pass //@ needs-llvm-components: x86 #![feature(no_core)] #![no_core] + +//~? WARN unstable feature specified for `-Ctarget-feature`: `x87` diff --git a/tests/ui/target-feature/unstable-feature.stderr b/tests/ui/target-feature/unstable-feature.stderr index d34544c5c2773..309b64afd9224 100644 --- a/tests/ui/target-feature/unstable-feature.stderr +++ b/tests/ui/target-feature/unstable-feature.stderr @@ -1,4 +1,4 @@ -warning: unstable feature specified for `-Ctarget-feature`: `vaes` +warning: unstable feature specified for `-Ctarget-feature`: `x87` | = note: this feature is not stably supported; its behavior can change in the future diff --git a/tests/ui/target_modifiers/auxiliary/enabled_reg_struct_return.rs b/tests/ui/target_modifiers/auxiliary/enabled_reg_struct_return.rs new file mode 100644 index 0000000000000..4bda4ba24c548 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/enabled_reg_struct_return.rs @@ -0,0 +1,7 @@ +//@ no-prefer-dynamic +//@ compile-flags: --target i686-unknown-linux-gnu -Zreg-struct-return=true +//@ needs-llvm-components: x86 + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/ui/target_modifiers/auxiliary/fixed_x18.rs b/tests/ui/target_modifiers/auxiliary/fixed_x18.rs new file mode 100644 index 0000000000000..32eff76ec54c4 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/fixed_x18.rs @@ -0,0 +1,7 @@ +//@ no-prefer-dynamic +//@ compile-flags: --target aarch64-unknown-none -Zfixed-x18 +//@ needs-llvm-components: aarch64 + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/ui/target_modifiers/defaults_check.error.stderr b/tests/ui/target_modifiers/defaults_check.error.stderr index 4833fe906775c..936fbbc94d6d6 100644 --- a/tests/ui/target_modifiers/defaults_check.error.stderr +++ b/tests/ui/target_modifiers/defaults_check.error.stderr @@ -5,8 +5,8 @@ LL | #![feature(no_core)] | ^ | = help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely - = note: `-Zreg-struct-return=true` in this crate is incompatible with `-Zreg-struct-return=` in dependency `default_reg_struct_return` - = help: set `-Zreg-struct-return=` in this crate or `-Zreg-struct-return=true` in `default_reg_struct_return` + = note: `-Zreg-struct-return=true` in this crate is incompatible with unset `-Zreg-struct-return` in dependency `default_reg_struct_return` + = help: unset `-Zreg-struct-return` in this crate or set `-Zreg-struct-return=true` in `default_reg_struct_return` = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error error: aborting due to 1 previous error diff --git a/tests/ui/target_modifiers/incompatible_fixedx18.error_generated.stderr b/tests/ui/target_modifiers/incompatible_fixedx18.error_generated.stderr new file mode 100644 index 0000000000000..096d7cb5f2582 --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_fixedx18.error_generated.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zfixed-x18` will cause an ABI mismatch in crate `incompatible_fixedx18` + --> $DIR/incompatible_fixedx18.rs:12:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Zfixed-x18` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: unset `-Zfixed-x18` in this crate is incompatible with `-Zfixed-x18=` in dependency `fixed_x18` + = help: set `-Zfixed-x18=` in this crate or unset `-Zfixed-x18` in `fixed_x18` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=fixed-x18` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/incompatible_fixedx18.rs b/tests/ui/target_modifiers/incompatible_fixedx18.rs new file mode 100644 index 0000000000000..6c13984f608ec --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_fixedx18.rs @@ -0,0 +1,17 @@ +//@ aux-build:fixed_x18.rs +//@ compile-flags: --target aarch64-unknown-none +//@ needs-llvm-components: aarch64 + +//@ revisions:allow_match allow_mismatch error_generated +//@[allow_match] compile-flags: -Zfixed-x18 +//@[allow_mismatch] compile-flags: -Cunsafe-allow-abi-mismatch=fixed-x18 +//@[error_generated] compile-flags: +//@[allow_mismatch] check-pass +//@[allow_match] check-pass + +#![feature(no_core)] +//[error_generated]~^ ERROR mixing `-Zfixed-x18` will cause an ABI mismatch in crate `incompatible_fixedx18` +#![crate_type = "rlib"] +#![no_core] + +extern crate fixed_x18; diff --git a/tests/ui/target_modifiers/incompatible_regparm.rs b/tests/ui/target_modifiers/incompatible_regparm.rs index befe573b276ca..395c26fc2c02d 100644 --- a/tests/ui/target_modifiers/incompatible_regparm.rs +++ b/tests/ui/target_modifiers/incompatible_regparm.rs @@ -14,3 +14,5 @@ #![no_core] extern crate wrong_regparm; + +//[allow_no_value]~? ERROR codegen option `unsafe-allow-abi-mismatch` requires a comma-separated list of strings diff --git a/tests/ui/target_modifiers/no_value_bool.error.stderr b/tests/ui/target_modifiers/no_value_bool.error.stderr new file mode 100644 index 0000000000000..0484960dc62d6 --- /dev/null +++ b/tests/ui/target_modifiers/no_value_bool.error.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `no_value_bool` + --> $DIR/no_value_bool.rs:16:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: unset `-Zreg-struct-return` in this crate is incompatible with `-Zreg-struct-return=true` in dependency `enabled_reg_struct_return` + = help: set `-Zreg-struct-return=true` in this crate or unset `-Zreg-struct-return` in `enabled_reg_struct_return` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/no_value_bool.error_explicit.stderr b/tests/ui/target_modifiers/no_value_bool.error_explicit.stderr new file mode 100644 index 0000000000000..0484960dc62d6 --- /dev/null +++ b/tests/ui/target_modifiers/no_value_bool.error_explicit.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `no_value_bool` + --> $DIR/no_value_bool.rs:16:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: unset `-Zreg-struct-return` in this crate is incompatible with `-Zreg-struct-return=true` in dependency `enabled_reg_struct_return` + = help: set `-Zreg-struct-return=true` in this crate or unset `-Zreg-struct-return` in `enabled_reg_struct_return` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/no_value_bool.rs b/tests/ui/target_modifiers/no_value_bool.rs new file mode 100644 index 0000000000000..ceba40afa8968 --- /dev/null +++ b/tests/ui/target_modifiers/no_value_bool.rs @@ -0,0 +1,22 @@ +// Tests that bool target modifier value (true) in dependency crate is ok linked +// with the -Zflag specified without value (-Zflag=true is consistent with -Zflag) + +//@ aux-build:enabled_reg_struct_return.rs +//@ compile-flags: --target i686-unknown-linux-gnu -Cpanic=abort +//@ needs-llvm-components: x86 + +//@ revisions: ok ok_explicit error error_explicit +//@[ok] compile-flags: -Zreg-struct-return +//@[ok_explicit] compile-flags: -Zreg-struct-return=true +//@[error] compile-flags: +//@[error_explicit] compile-flags: -Zreg-struct-return=false +//@[ok] check-pass +//@[ok_explicit] check-pass + +#![feature(no_core)] +//[error]~^ ERROR mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `no_value_bool` +//[error_explicit]~^^ ERROR mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `no_value_bool` +#![crate_type = "rlib"] +#![no_core] + +extern crate enabled_reg_struct_return; diff --git a/tests/ui/test-attrs/custom-test-frameworks/issue-107454.stderr b/tests/ui/test-attrs/custom-test-frameworks/issue-107454.stderr index 6ac79924d8057..1b1a59ec582f1 100644 --- a/tests/ui/test-attrs/custom-test-frameworks/issue-107454.stderr +++ b/tests/ui/test-attrs/custom-test-frameworks/issue-107454.stderr @@ -9,7 +9,6 @@ note: the lint level is defined here | LL | #![deny(unnameable_test_items)] | ^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the attribute macro `test_case` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/test-attrs/issue-12997-2.stderr b/tests/ui/test-attrs/issue-12997-2.stderr index bc84ff413dfeb..1123630a4a1f1 100644 --- a/tests/ui/test-attrs/issue-12997-2.stderr +++ b/tests/ui/test-attrs/issue-12997-2.stderr @@ -14,7 +14,6 @@ note: function defined here | LL | fn bar(x: isize) { } | ^^^ -------- - = note: this error originates in the attribute macro `bench` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/test-attrs/test-function-signature.stderr b/tests/ui/test-attrs/test-function-signature.stderr index abdb30dc9312b..c025163c0bd9c 100644 --- a/tests/ui/test-attrs/test-function-signature.stderr +++ b/tests/ui/test-attrs/test-function-signature.stderr @@ -32,7 +32,6 @@ LL | fn bar() -> i32 { | note: required by a bound in `assert_test_result` --> $SRC_DIR/test/src/lib.rs:LL:COL - = note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 5 previous errors diff --git a/tests/ui/test-attrs/test-panic-abort-disabled.rs b/tests/ui/test-attrs/test-panic-abort-disabled.rs index e83be65f9250c..cb1b2af9e26da 100644 --- a/tests/ui/test-attrs/test-panic-abort-disabled.rs +++ b/tests/ui/test-attrs/test-panic-abort-disabled.rs @@ -1,4 +1,3 @@ -//@ error-pattern:building tests with panic=abort is not supported //@ no-prefer-dynamic //@ compile-flags: --test -Cpanic=abort -Zpanic-abort-tests=no //@ run-flags: --test-threads=1 @@ -18,3 +17,5 @@ fn it_works() { fn it_panics() { assert_eq!(1 + 1, 4); } + +//~? ERROR building tests with panic=abort is not supported without `-Zpanic_abort_tests` diff --git a/tests/ui/test-attrs/test-should-panic-failed-show-span.rs b/tests/ui/test-attrs/test-should-panic-failed-show-span.rs new file mode 100644 index 0000000000000..f400f614142de --- /dev/null +++ b/tests/ui/test-attrs/test-should-panic-failed-show-span.rs @@ -0,0 +1,46 @@ +//@ compile-flags: --test +//@ run-flags: --test-threads=1 --nocapture +//@ run-fail +//@ check-run-results +//@ exec-env:RUST_BACKTRACE=0 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "TypeId\(0x[0-9a-f]+\)" -> "TypeId($$HEX)" +//@ needs-threads +//@ needs-unwind (panic) + +#[test] +#[should_panic] +fn should_panic_with_any_message() { + panic!("Panic!"); +} + +#[test] +#[should_panic = "message"] +fn should_panic_with_message() { + panic!("message"); +} + +#[test] +#[should_panic] +fn should_panic_with_any_message_does_not_panic() { + // DON'T PANIC +} + +#[test] +#[should_panic = "message"] +fn should_panic_with_message_does_not_panic() { + // DON'T PANIC +} + +#[test] +#[should_panic = "message"] +fn should_panic_with_substring_panics_with_incorrect_string() { + panic!("ZOMGWTFBBQ"); +} + +#[test] +#[should_panic = "message"] +#[expect(non_fmt_panics)] +fn should_panic_with_substring_panics_with_non_string_value() { + panic!(123); +} diff --git a/tests/ui/test-attrs/test-should-panic-failed-show-span.run.stderr b/tests/ui/test-attrs/test-should-panic-failed-show-span.run.stderr new file mode 100644 index 0000000000000..db379a16b52a1 --- /dev/null +++ b/tests/ui/test-attrs/test-should-panic-failed-show-span.run.stderr @@ -0,0 +1,13 @@ + +thread 'should_panic_with_any_message' panicked at $DIR/test-should-panic-failed-show-span.rs:14:5: +Panic! +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +thread 'should_panic_with_message' panicked at $DIR/test-should-panic-failed-show-span.rs:20:5: +message + +thread 'should_panic_with_substring_panics_with_incorrect_string' panicked at $DIR/test-should-panic-failed-show-span.rs:38:5: +ZOMGWTFBBQ + +thread 'should_panic_with_substring_panics_with_non_string_value' panicked at $DIR/test-should-panic-failed-show-span.rs:45:5: +Box diff --git a/tests/ui/test-attrs/test-should-panic-failed-show-span.run.stdout b/tests/ui/test-attrs/test-should-panic-failed-show-span.run.stdout new file mode 100644 index 0000000000000..93204abb96873 --- /dev/null +++ b/tests/ui/test-attrs/test-should-panic-failed-show-span.run.stdout @@ -0,0 +1,32 @@ + +running 6 tests +test should_panic_with_any_message - should panic ... ok +test should_panic_with_any_message_does_not_panic - should panic ... FAILED +test should_panic_with_message - should panic ... ok +test should_panic_with_message_does_not_panic - should panic ... FAILED +test should_panic_with_substring_panics_with_incorrect_string - should panic ... FAILED +test should_panic_with_substring_panics_with_non_string_value - should panic ... FAILED + +failures: + +---- should_panic_with_any_message_does_not_panic stdout ---- +note: test did not panic as expected at $DIR/test-should-panic-failed-show-span.rs:25:4 +---- should_panic_with_message_does_not_panic stdout ---- +note: test did not panic as expected at $DIR/test-should-panic-failed-show-span.rs:31:4 +---- should_panic_with_substring_panics_with_incorrect_string stdout ---- +note: panic did not contain expected string + panic message: "ZOMGWTFBBQ" + expected substring: "message" +---- should_panic_with_substring_panics_with_non_string_value stdout ---- +note: expected panic with string value, + found non-string value: `TypeId($HEX)` + expected substring: "message" + +failures: + should_panic_with_any_message_does_not_panic + should_panic_with_message_does_not_panic + should_panic_with_substring_panics_with_incorrect_string + should_panic_with_substring_panics_with_non_string_value + +test result: FAILED. 2 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/ui/thread-local/spawn-hook-atexit.rs b/tests/ui/thread-local/spawn-hook-atexit.rs new file mode 100644 index 0000000000000..b084e0bb38785 --- /dev/null +++ b/tests/ui/thread-local/spawn-hook-atexit.rs @@ -0,0 +1,24 @@ +// Regression test for https://github.com/rust-lang/rust/issues/138696 +//@ only-unix +//@ needs-threads +//@ run-pass + +#![feature(rustc_private)] + +extern crate libc; + +fn main() { + std::thread::spawn(|| { + unsafe { libc::atexit(spawn_in_atexit) }; + }) + .join() + .unwrap(); +} + +extern "C" fn spawn_in_atexit() { + std::thread::spawn(|| { + println!("Thread spawned in atexit"); + }) + .join() + .unwrap(); +} diff --git a/tests/ui/tool-attributes/crate-attr.rs b/tests/ui/tool-attributes/crate-attr.rs new file mode 100644 index 0000000000000..c6d7974945f4a --- /dev/null +++ b/tests/ui/tool-attributes/crate-attr.rs @@ -0,0 +1,5 @@ +//@ check-pass +//@ compile-flags: -Z crate-attr=feature(register_tool) -Z crate-attr=register_tool(foo) + +#[allow(foo::bar)] +fn main() {} diff --git a/tests/ui/tool-attributes/duplicate-diagnostic.rs b/tests/ui/tool-attributes/duplicate-diagnostic.rs index 5061bcb9e4445..c36179611afcc 100644 --- a/tests/ui/tool-attributes/duplicate-diagnostic.rs +++ b/tests/ui/tool-attributes/duplicate-diagnostic.rs @@ -1,13 +1,14 @@ //@ aux-build: p1.rs //@ aux-build: p2.rs -//@ error-pattern: duplicate diagnostic item in crate `p2` -//@ error-pattern: note: the diagnostic item is first defined in crate `p1` - #![feature(rustc_attrs)] extern crate p1; extern crate p2; #[rustc_diagnostic_item = "Foo"] pub struct Foo {} //~ ERROR duplicate diagnostic item in crate `duplicate_diagnostic`: `Foo` + //~^ NOTE the diagnostic item is first defined in crate `p2` fn main() {} + +//~? ERROR duplicate diagnostic item in crate `p2` +//~? NOTE the diagnostic item is first defined in crate `p1` diff --git a/tests/ui/tool-attributes/duplicate-diagnostic.stderr b/tests/ui/tool-attributes/duplicate-diagnostic.stderr index 3cd438004c8e2..16d78d03ae954 100644 --- a/tests/ui/tool-attributes/duplicate-diagnostic.stderr +++ b/tests/ui/tool-attributes/duplicate-diagnostic.stderr @@ -3,7 +3,7 @@ error: duplicate diagnostic item in crate `p2`: `Foo` = note: the diagnostic item is first defined in crate `p1` error: duplicate diagnostic item in crate `duplicate_diagnostic`: `Foo` - --> $DIR/duplicate-diagnostic.rs:12:1 + --> $DIR/duplicate-diagnostic.rs:9:1 | LL | pub struct Foo {} | ^^^^^^^^^^^^^^ diff --git a/tests/ui/tool-attributes/multiple-registered.rs b/tests/ui/tool-attributes/multiple-registered.rs new file mode 100644 index 0000000000000..4d54c2dcb0820 --- /dev/null +++ b/tests/ui/tool-attributes/multiple-registered.rs @@ -0,0 +1,7 @@ +//@ check-pass + +#![feature(register_tool)] +#![register_tool(foo, bar, baz)] + +#[allow(foo::a, bar::b, baz::c)] +fn main() {} diff --git a/tests/ui/tool-attributes/nested-disallowed.rs b/tests/ui/tool-attributes/nested-disallowed.rs new file mode 100644 index 0000000000000..8e78042776106 --- /dev/null +++ b/tests/ui/tool-attributes/nested-disallowed.rs @@ -0,0 +1,4 @@ +#![feature(register_tool)] +#![register_tool(foo::bar)] //~ ERROR only accepts identifiers + +fn main() {} diff --git a/tests/ui/tool-attributes/nested-disallowed.stderr b/tests/ui/tool-attributes/nested-disallowed.stderr new file mode 100644 index 0000000000000..1af73fc2f1995 --- /dev/null +++ b/tests/ui/tool-attributes/nested-disallowed.stderr @@ -0,0 +1,8 @@ +error: `register_tool` only accepts identifiers + --> $DIR/nested-disallowed.rs:2:18 + | +LL | #![register_tool(foo::bar)] + | ^^^^^^^^ not an identifier + +error: aborting due to 1 previous error + diff --git a/tests/ui/tool-attributes/tool_lints.rs b/tests/ui/tool-attributes/tool_lints.rs index ef27532f6de1f..9e4aa7a939acb 100644 --- a/tests/ui/tool-attributes/tool_lints.rs +++ b/tests/ui/tool-attributes/tool_lints.rs @@ -1,4 +1,5 @@ #[warn(foo::bar)] //~^ ERROR unknown tool name `foo` found in scoped lint: `foo::bar` //~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` +//~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` fn main() {} diff --git a/tests/ui/tool-attributes/tool_lints.stderr b/tests/ui/tool-attributes/tool_lints.stderr index f1d825caba159..eee0a9784ece8 100644 --- a/tests/ui/tool-attributes/tool_lints.stderr +++ b/tests/ui/tool-attributes/tool_lints.stderr @@ -15,6 +15,15 @@ LL | #[warn(foo::bar)] = help: add `#![register_tool(foo)]` to the crate root = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 2 previous errors +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` + --> $DIR/tool_lints.rs:1:8 + | +LL | #[warn(foo::bar)] + | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0710`. diff --git a/tests/ui/tool-attributes/unknown-lint-tool-name.rs b/tests/ui/tool-attributes/unknown-lint-tool-name.rs index 59fc56d820ee2..84ab7c1944ab2 100644 --- a/tests/ui/tool-attributes/unknown-lint-tool-name.rs +++ b/tests/ui/tool-attributes/unknown-lint-tool-name.rs @@ -4,4 +4,5 @@ #[allow(foo::bar)] //~ ERROR unknown tool name `foo` found in scoped lint: `foo::bar` //~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` + //~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` fn main() {} diff --git a/tests/ui/tool-attributes/unknown-lint-tool-name.stderr b/tests/ui/tool-attributes/unknown-lint-tool-name.stderr index 5d99777a14ae3..91baf88273756 100644 --- a/tests/ui/tool-attributes/unknown-lint-tool-name.stderr +++ b/tests/ui/tool-attributes/unknown-lint-tool-name.stderr @@ -41,6 +41,15 @@ LL | #![deny(foo::bar)] = help: add `#![register_tool(foo)]` to the crate root = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 5 previous errors +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` + --> $DIR/unknown-lint-tool-name.rs:5:9 + | +LL | #[allow(foo::bar)] + | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0710`. diff --git a/tests/ui/track-diagnostics/track.rs b/tests/ui/track-diagnostics/track.rs index 9e81cb53fc112..78ff85489bede 100644 --- a/tests/ui/track-diagnostics/track.rs +++ b/tests/ui/track-diagnostics/track.rs @@ -15,4 +15,7 @@ fn main() { break rust + //~^ ERROR cannot find value `rust` in this scope + //~| ERROR `break` outside of a loop or labeled block + //~| ERROR It looks like you're trying to break rust; would you like some ICE? } diff --git a/tests/ui/track-diagnostics/track2.rs b/tests/ui/track-diagnostics/track2.rs index 5805fd21220d7..f51a42cf86f99 100644 --- a/tests/ui/track-diagnostics/track2.rs +++ b/tests/ui/track-diagnostics/track2.rs @@ -6,5 +6,5 @@ //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" fn main() { - let _moved @ _from = String::from("foo"); + let _moved @ _from = String::from("foo"); //~ ERROR use of moved value } diff --git a/tests/ui/track-diagnostics/track3.rs b/tests/ui/track-diagnostics/track3.rs index bac1fc7e184bf..428067572af77 100644 --- a/tests/ui/track-diagnostics/track3.rs +++ b/tests/ui/track-diagnostics/track3.rs @@ -7,4 +7,6 @@ fn main() { let _unimported = Blah { field: u8 }; + //~^ ERROR cannot find struct, variant or union type `Blah` in this scope + //~| ERROR expected value, found builtin type `u8` } diff --git a/tests/ui/track-diagnostics/track4.rs b/tests/ui/track-diagnostics/track4.rs index ec9e3efa481c6..b6edfdba2596a 100644 --- a/tests/ui/track-diagnostics/track4.rs +++ b/tests/ui/track-diagnostics/track4.rs @@ -5,7 +5,7 @@ // updating everytime someone adds or removes a line. //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" -pub onion { +pub onion { //~ ERROR missing `enum` for enum definition Owo(u8), Uwu(i8), } diff --git a/tests/ui/track-diagnostics/track5.rs b/tests/ui/track-diagnostics/track5.rs index e72e3482ad385..800bb21b2b160 100644 --- a/tests/ui/track-diagnostics/track5.rs +++ b/tests/ui/track-diagnostics/track5.rs @@ -5,4 +5,4 @@ // updating everytime someone adds or removes a line. //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" -} +} //~ ERROR unexpected closing delimiter: `}` diff --git a/tests/ui/track-diagnostics/track6.rs b/tests/ui/track-diagnostics/track6.rs index e4d124a22e495..55db2ecf939d5 100644 --- a/tests/ui/track-diagnostics/track6.rs +++ b/tests/ui/track-diagnostics/track6.rs @@ -11,7 +11,7 @@ pub trait Foo { } impl Foo for T { - default fn bar() {} + default fn bar() {} //~ ERROR specialization is unstable } fn main() {} diff --git a/tests/ui/trait-bounds/ice-unsized-tuple-const-issue-121443.stderr b/tests/ui/trait-bounds/ice-unsized-tuple-const-issue-121443.stderr index 76e015a7238b5..4609e02716fdc 100644 --- a/tests/ui/trait-bounds/ice-unsized-tuple-const-issue-121443.stderr +++ b/tests/ui/trait-bounds/ice-unsized-tuple-const-issue-121443.stderr @@ -11,6 +11,7 @@ LL | const TEST: Fn = some_fn; | ^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn FnOnce() -> u8 + 'static)` + = note: statics and constants must have a statically known size error[E0277]: the size for values of type `(dyn FnOnce() -> u8 + 'static)` cannot be known at compilation time --> $DIR/ice-unsized-tuple-const-issue-121443.rs:11:14 diff --git a/tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr b/tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr index e26cb22163f1e..f8a6252f991ea 100644 --- a/tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr +++ b/tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr @@ -10,26 +10,53 @@ LL | fn foo<'a, K>(self, _: T, _: K) where T: 'a, K: 'a; LL | fn foo<'a, K>(self, _: (), _: K) where { | ^^^^^^^ lifetimes do not match method in trait -error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration - --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:23:11 +error[E0195]: lifetime parameters do not match the trait definition + --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:23:12 | -LL | fn foo<'a>(&self, state: &'a State) -> &'a T - | ---- lifetimes in impl do not match this method in trait -LL | where -LL | T: 'a; - | -- this bound might be missing in the impl -... LL | fn foo<'a>(&self, state: &'a State) -> &'a T { - | ^^^^ lifetimes do not match method in trait + | ^^ + | + = note: lifetime parameters differ in whether they are early- or late-bound +note: `'a` differs between the trait and impl + --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:14:12 + | +LL | trait Foo { + | ------------ in this trait... +LL | fn foo<'a>(&self, state: &'a State) -> &'a T + | ^^ `'a` is early-bound +LL | where +LL | T: 'a; + | -- this lifetime bound makes `'a` early-bound +... +LL | / impl Foo for F +LL | | where +LL | | F: Fn(&State) -> &T, + | |________________________- in this impl... +LL | { +LL | fn foo<'a>(&self, state: &'a State) -> &'a T { + | ^^ `'a` is late-bound -error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration - --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:33:11 +error[E0195]: lifetime parameters do not match the trait definition + --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:33:12 + | +LL | fn foo<'a: 'a>(&'a self) {} + | ^^ + | + = note: lifetime parameters differ in whether they are early- or late-bound +note: `'a` differs between the trait and impl + --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:29:12 | +LL | trait Bar { + | --------- in this trait... LL | fn foo<'a>(&'a self) {} - | ---- lifetimes in impl do not match this method in trait + | ^^ `'a` is late-bound ... +LL | impl Bar for () { + | --------------- in this impl... LL | fn foo<'a: 'a>(&'a self) {} - | ^^^^^^^^ lifetimes do not match method in trait + | ^^ -- this lifetime bound makes `'a` early-bound + | | + | `'a` is early-bound error: aborting due to 3 previous errors diff --git a/tests/ui/trait-bounds/mismatch-fn-trait.stderr b/tests/ui/trait-bounds/mismatch-fn-trait.stderr index 519aa9ea3f3bc..051e660c2d106 100644 --- a/tests/ui/trait-bounds/mismatch-fn-trait.stderr +++ b/tests/ui/trait-bounds/mismatch-fn-trait.stderr @@ -6,8 +6,8 @@ LL | take(f) | | | required by a bound introduced by this call | - = note: expected a closure with arguments `(u32,)` - found a closure with arguments `(i32,)` + = note: expected a closure with signature `fn(i32)` + found a closure with signature `fn(u32)` note: required by a bound in `take` --> $DIR/mismatch-fn-trait.rs:1:18 | @@ -68,8 +68,8 @@ LL | take(f) | required by a bound introduced by this call | = note: `impl FnOnce(u32)` implements `FnOnce`, but it must implement `FnMut`, which is more general - = note: expected a closure with arguments `(u32,)` - found a closure with arguments `(i32,)` + = note: expected a closure with signature `fn(i32)` + found a closure with signature `fn(u32)` note: required by a bound in `take` --> $DIR/mismatch-fn-trait.rs:1:18 | diff --git a/tests/ui/traits/alias/issue-108132-unmet-trait-alias-bound-on-generic-impl.rs b/tests/ui/traits/alias/issue-108132-unmet-trait-alias-bound-on-generic-impl.rs index 0b1f9ab57c987..39ca19fc3a588 100644 --- a/tests/ui/traits/alias/issue-108132-unmet-trait-alias-bound-on-generic-impl.rs +++ b/tests/ui/traits/alias/issue-108132-unmet-trait-alias-bound-on-generic-impl.rs @@ -11,5 +11,5 @@ impl Foo { } fn main() { - Foo::<()>::f() //~ trait bounds were not satisfied + Foo::<()>::f() //~ ERROR trait bounds were not satisfied } diff --git a/tests/ui/traits/alias/issue-83613.rs b/tests/ui/traits/alias/issue-83613.rs index 6f0012bf08967..752c4b84546b8 100644 --- a/tests/ui/traits/alias/issue-83613.rs +++ b/tests/ui/traits/alias/issue-83613.rs @@ -2,6 +2,7 @@ trait OpaqueTrait {} impl OpaqueTrait for T {} type OpaqueType = impl OpaqueTrait; +#[define_opaque(OpaqueType)] fn mk_opaque() -> OpaqueType { || 0 } diff --git a/tests/ui/traits/alias/issue-83613.stderr b/tests/ui/traits/alias/issue-83613.stderr index 47181c3f33ed6..7d2bdd7e18635 100644 --- a/tests/ui/traits/alias/issue-83613.stderr +++ b/tests/ui/traits/alias/issue-83613.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `AnotherTrait` - --> $DIR/issue-83613.rs:10:1 + --> $DIR/issue-83613.rs:11:1 | LL | impl AnotherTrait for T {} | -------------------------------- first implementation here diff --git a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs index 1359eb6cb87a4..891cc32e7b758 100644 --- a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs +++ b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs @@ -11,5 +11,5 @@ fn f X<'r> + ?Sized>() { fn main() { f:: X<'x, F = i32>>(); - //~^ expected a `FnOnce(&i32)` closure, found `i32` + //~^ ERROR expected a `FnOnce(&i32)` closure, found `i32` } diff --git a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-5.rs b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-5.rs index 7d733ad26b752..829a40c64e666 100644 --- a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-5.rs +++ b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-5.rs @@ -21,7 +21,7 @@ fn is_obj(_: &T) {} fn f(x: &dyn Obj) { is_obj(x) - //~^ type mismatch resolving `::T == i64` + //~^ ERROR type mismatch resolving `::T == i64` } fn main() {} diff --git a/tests/ui/traits/associated_type_bound/hrtb-associated.rs b/tests/ui/traits/associated_type_bound/hrtb-associated.rs new file mode 100644 index 0000000000000..59e5a09c0cbbf --- /dev/null +++ b/tests/ui/traits/associated_type_bound/hrtb-associated.rs @@ -0,0 +1,30 @@ +//@ check-pass +//! This test ensures that HRTB (higher-ranked trait bounds) on associated types +//! compile correctly. This was previously rejected by the compiler. +//! Related issue: + +pub trait Provides<'a> { + type Item; +} + +pub trait Selector: for<'a> Provides<'a> { + type Namespace: PartialEq + for<'a> PartialEq<>::Item>; + + fn get_namespace(&self) -> ::Item; +} + +pub struct MySelector; + +impl<'a> Provides<'a> for MySelector { + type Item = &'a str; +} + +impl Selector for MySelector { + type Namespace = String; + + fn get_namespace(&self) -> &str { + unimplemented!() + } +} + +fn main() {} diff --git a/tests/ui/traits/bound/on-structs-and-enums-locals.stderr b/tests/ui/traits/bound/on-structs-and-enums-locals.stderr index 01cf76c62d540..88077f4e4d7d7 100644 --- a/tests/ui/traits/bound/on-structs-and-enums-locals.stderr +++ b/tests/ui/traits/bound/on-structs-and-enums-locals.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `usize: Trait` is not satisfied - --> $DIR/on-structs-and-enums-locals.rs:15:14 +error[E0277]: the trait bound `{integer}: Trait` is not satisfied + --> $DIR/on-structs-and-enums-locals.rs:11:12 | -LL | let baz: Foo = loop { }; - | ^^^^^^^^^^ the trait `Trait` is not implemented for `usize` +LL | x: 3 + | ^ the trait `Trait` is not implemented for `{integer}` | help: this trait has no implementations, consider adding one --> $DIR/on-structs-and-enums-locals.rs:1:1 @@ -15,11 +15,11 @@ note: required by a bound in `Foo` LL | struct Foo { | ^^^^^ required by this bound in `Foo` -error[E0277]: the trait bound `{integer}: Trait` is not satisfied - --> $DIR/on-structs-and-enums-locals.rs:11:12 +error[E0277]: the trait bound `usize: Trait` is not satisfied + --> $DIR/on-structs-and-enums-locals.rs:15:14 | -LL | x: 3 - | ^ the trait `Trait` is not implemented for `{integer}` +LL | let baz: Foo = loop { }; + | ^^^^^^^^^^ the trait `Trait` is not implemented for `usize` | help: this trait has no implementations, consider adding one --> $DIR/on-structs-and-enums-locals.rs:1:1 diff --git a/tests/ui/traits/bound/on-structs-and-enums-xc1.stderr b/tests/ui/traits/bound/on-structs-and-enums-xc1.stderr index 3fb5decb723ea..1f46415e24369 100644 --- a/tests/ui/traits/bound/on-structs-and-enums-xc1.stderr +++ b/tests/ui/traits/bound/on-structs-and-enums-xc1.stderr @@ -1,15 +1,3 @@ -error[E0277]: the trait bound `f64: Trait` is not satisfied - --> $DIR/on-structs-and-enums-xc1.rs:12:14 - | -LL | let bar: Bar = return; - | ^^^^^^^^ the trait `Trait` is not implemented for `f64` - | -note: required by a bound in `Bar` - --> $DIR/auxiliary/on_structs_and_enums_xc.rs:9:16 - | -LL | pub enum Bar { - | ^^^^^ required by this bound in `Bar` - error[E0277]: the trait bound `{integer}: Trait` is not satisfied --> $DIR/on-structs-and-enums-xc1.rs:9:12 | @@ -22,6 +10,18 @@ note: required by a bound in `Foo` LL | pub struct Foo { | ^^^^^ required by this bound in `Foo` +error[E0277]: the trait bound `f64: Trait` is not satisfied + --> $DIR/on-structs-and-enums-xc1.rs:12:14 + | +LL | let bar: Bar = return; + | ^^^^^^^^ the trait `Trait` is not implemented for `f64` + | +note: required by a bound in `Bar` + --> $DIR/auxiliary/on_structs_and_enums_xc.rs:9:16 + | +LL | pub enum Bar { + | ^^^^^ required by this bound in `Bar` + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/bound/same-crate-name.rs b/tests/ui/traits/bound/same-crate-name.rs index 06d79a0c8b8d1..395b963031a55 100644 --- a/tests/ui/traits/bound/same-crate-name.rs +++ b/tests/ui/traits/bound/same-crate-name.rs @@ -1,5 +1,7 @@ //@ aux-build:crate_a1.rs //@ aux-build:crate_a2.rs +//@ dont-require-annotations: HELP +//@ dont-require-annotations: NOTE // Issue 22750 // This tests the extra help message reported when a trait bound @@ -30,8 +32,8 @@ fn main() { extern crate crate_a1 as a; a::try_foo(foo); //~^ ERROR E0277 - //~| trait impl with same name found - //~| perhaps two different versions of crate `crate_a2` + //~| HELP trait impl with same name found + //~| NOTE perhaps two different versions of crate `crate_a2` // We don't want to see the "version mismatch" help message here // because `implements_no_traits` has no impl for `Foo` @@ -50,6 +52,6 @@ fn main() { // impls for the correct trait where the path is not misleading. a::try_foo(other_variant_implements_correct_trait); //~^ ERROR E0277 - //~| the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize` + //~| HELP the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize` } } diff --git a/tests/ui/traits/bound/same-crate-name.stderr b/tests/ui/traits/bound/same-crate-name.stderr index f66cad77fcd1e..71a8159fd8906 100644 --- a/tests/ui/traits/bound/same-crate-name.stderr +++ b/tests/ui/traits/bound/same-crate-name.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Foo: main::a::Bar` is not satisfied - --> $DIR/same-crate-name.rs:31:20 + --> $DIR/same-crate-name.rs:33:20 | LL | a::try_foo(foo); | ---------- ^^^ the trait `main::a::Bar` is not implemented for `Foo` @@ -20,7 +20,7 @@ LL | pub fn try_foo(x: impl Bar) {} | ^^^ required by this bound in `try_foo` error[E0277]: the trait bound `DoesNotImplementTrait: main::a::Bar` is not satisfied - --> $DIR/same-crate-name.rs:38:20 + --> $DIR/same-crate-name.rs:40:20 | LL | a::try_foo(implements_no_traits); | ---------- ^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `DoesNotImplementTrait` @@ -35,7 +35,7 @@ LL | pub fn try_foo(x: impl Bar) {} | ^^^ required by this bound in `try_foo` error[E0277]: the trait bound `ImplementsWrongTraitConditionally: main::a::Bar` is not satisfied - --> $DIR/same-crate-name.rs:45:20 + --> $DIR/same-crate-name.rs:47:20 | LL | a::try_foo(other_variant_implements_mismatched_trait); | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `ImplementsWrongTraitConditionally` @@ -56,7 +56,7 @@ LL | pub fn try_foo(x: impl Bar) {} | ^^^ required by this bound in `try_foo` error[E0277]: the trait bound `ImplementsTraitForUsize: main::a::Bar` is not satisfied - --> $DIR/same-crate-name.rs:51:20 + --> $DIR/same-crate-name.rs:53:20 | LL | a::try_foo(other_variant_implements_correct_trait); | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `ImplementsTraitForUsize` diff --git a/tests/ui/traits/const-traits/assoc-type.current.stderr b/tests/ui/traits/const-traits/assoc-type.current.stderr index 4bf9acfbd6507..7526369194b4e 100644 --- a/tests/ui/traits/const-traits/assoc-type.current.stderr +++ b/tests/ui/traits/const-traits/assoc-type.current.stderr @@ -8,7 +8,7 @@ note: required by a bound in `Foo::Bar` --> $DIR/assoc-type.rs:33:15 | LL | type Bar: ~const Add; - | ^^^^^^ required by this bound in `Foo::Bar` + | ^^^^^^^^^^ required by this bound in `Foo::Bar` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/assoc-type.next.stderr b/tests/ui/traits/const-traits/assoc-type.next.stderr index 4bf9acfbd6507..7526369194b4e 100644 --- a/tests/ui/traits/const-traits/assoc-type.next.stderr +++ b/tests/ui/traits/const-traits/assoc-type.next.stderr @@ -8,7 +8,7 @@ note: required by a bound in `Foo::Bar` --> $DIR/assoc-type.rs:33:15 | LL | type Bar: ~const Add; - | ^^^^^^ required by this bound in `Foo::Bar` + | ^^^^^^^^^^ required by this bound in `Foo::Bar` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/call-generic-method-nonconst.stderr b/tests/ui/traits/const-traits/call-generic-method-nonconst.stderr index 74a22186a1637..11bbe8bbb408b 100644 --- a/tests/ui/traits/const-traits/call-generic-method-nonconst.stderr +++ b/tests/ui/traits/const-traits/call-generic-method-nonconst.stderr @@ -10,7 +10,7 @@ note: required by a bound in `equals_self` --> $DIR/call-generic-method-nonconst.rs:17:25 | LL | const fn equals_self(t: &T) -> bool { - | ^^^^^^ required by this bound in `equals_self` + | ^^^^^^^^^^ required by this bound in `equals_self` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr b/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr index 2b5e66b1a086c..76207ea0939b3 100644 --- a/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr @@ -8,14 +8,14 @@ note: required for `ConstDropImplWithBounds` to implement `const --> $DIR/const-drop-fail-2.rs:25:25 | LL | impl const Drop for ConstDropImplWithBounds { - | ------ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | -------- ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | unsatisfied trait bound introduced here note: required by a bound in `check` --> $DIR/const-drop-fail-2.rs:21:19 | LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` + | ^^^^^^^^^^^^^^^ required by this bound in `check` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr b/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr index 2b5e66b1a086c..76207ea0939b3 100644 --- a/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr @@ -8,14 +8,14 @@ note: required for `ConstDropImplWithBounds` to implement `const --> $DIR/const-drop-fail-2.rs:25:25 | LL | impl const Drop for ConstDropImplWithBounds { - | ------ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | -------- ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | unsatisfied trait bound introduced here note: required by a bound in `check` --> $DIR/const-drop-fail-2.rs:21:19 | LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` + | ^^^^^^^^^^^^^^^ required by this bound in `check` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr b/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr index 682f48fe07af6..f38e642bb63e5 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr @@ -11,7 +11,7 @@ note: required by a bound in `check` --> $DIR/const-drop-fail.rs:24:19 | LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` + | ^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied --> $DIR/const-drop-fail.rs:35:5 @@ -26,7 +26,7 @@ note: required by a bound in `check` --> $DIR/const-drop-fail.rs:24:19 | LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` + | ^^^^^^^^^^^^^^^ required by this bound in `check` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr b/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr index 682f48fe07af6..f38e642bb63e5 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr @@ -11,7 +11,7 @@ note: required by a bound in `check` --> $DIR/const-drop-fail.rs:24:19 | LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` + | ^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied --> $DIR/const-drop-fail.rs:35:5 @@ -26,7 +26,7 @@ note: required by a bound in `check` --> $DIR/const-drop-fail.rs:24:19 | LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` + | ^^^^^^^^^^^^^^^ required by this bound in `check` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr b/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr index 682f48fe07af6..f38e642bb63e5 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr @@ -11,7 +11,7 @@ note: required by a bound in `check` --> $DIR/const-drop-fail.rs:24:19 | LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` + | ^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied --> $DIR/const-drop-fail.rs:35:5 @@ -26,7 +26,7 @@ note: required by a bound in `check` --> $DIR/const-drop-fail.rs:24:19 | LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` + | ^^^^^^^^^^^^^^^ required by this bound in `check` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr b/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr index 682f48fe07af6..f38e642bb63e5 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr @@ -11,7 +11,7 @@ note: required by a bound in `check` --> $DIR/const-drop-fail.rs:24:19 | LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` + | ^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied --> $DIR/const-drop-fail.rs:35:5 @@ -26,7 +26,7 @@ note: required by a bound in `check` --> $DIR/const-drop-fail.rs:24:19 | LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` + | ^^^^^^^^^^^^^^^ required by this bound in `check` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/const-opaque.no.stderr b/tests/ui/traits/const-traits/const-opaque.no.stderr index 1278e1257467b..47e692936e046 100644 --- a/tests/ui/traits/const-traits/const-opaque.no.stderr +++ b/tests/ui/traits/const-traits/const-opaque.no.stderr @@ -10,7 +10,7 @@ note: required by a bound in `bar` --> $DIR/const-opaque.rs:26:17 | LL | const fn bar(t: T) -> impl ~const Foo { - | ^^^^^^ required by this bound in `bar` + | ^^^^^^^^^^ required by this bound in `bar` error[E0277]: the trait bound `(): const Foo` is not satisfied --> $DIR/const-opaque.rs:33:12 diff --git a/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs b/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs new file mode 100644 index 0000000000000..f90ff91aff4bb --- /dev/null +++ b/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs @@ -0,0 +1,32 @@ +// This test demonstrates an ICE that may occur when we try to resolve the instance +// of a impl that has different generics than the trait it's implementing. This ensures +// we first check that the args are compatible before resolving the body, just like +// we do in projection before substituting a GAT. +// +// Regression test for issue #125877. + +//@ compile-flags: -Znext-solver + +#![feature(const_trait_impl, effects)] +//~^ ERROR feature has been removed + +#[const_trait] +trait Main { + fn compute() -> u32; +} + +impl const Main for () { + fn compute<'x>() -> u32 { + //~^ ERROR associated function `compute` has 0 type parameters but its trait declaration has 1 type parameter + 0 + } +} + +#[const_trait] +trait Aux {} + +impl const Aux for () {} + +fn main() { + const _: u32 = <()>::compute::<()>(); +} diff --git a/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr b/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr new file mode 100644 index 0000000000000..d45c4cba1f8b5 --- /dev/null +++ b/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr @@ -0,0 +1,21 @@ +error[E0557]: feature has been removed + --> $DIR/const-trait-impl-parameter-mismatch.rs:10:30 + | +LL | #![feature(const_trait_impl, effects)] + | ^^^^^^^ feature has been removed + | + = note: removed, redundant with `#![feature(const_trait_impl)]` + +error[E0049]: associated function `compute` has 0 type parameters but its trait declaration has 1 type parameter + --> $DIR/const-trait-impl-parameter-mismatch.rs:19:16 + | +LL | fn compute() -> u32; + | - expected 1 type parameter +... +LL | fn compute<'x>() -> u32 { + | ^^ found 0 type parameters + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0049, E0557. +For more information about an error, try `rustc --explain E0049`. diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-gate.stderr b/tests/ui/traits/const-traits/const_derives/derive-const-gate.stderr index fae871a4c85ac..202210a2e659a 100644 --- a/tests/ui/traits/const-traits/const_derives/derive-const-gate.stderr +++ b/tests/ui/traits/const-traits/const_derives/derive-const-gate.stderr @@ -15,7 +15,6 @@ LL | #[derive_const(Default)] | = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` = note: adding a non-const method body in the future would be a breaking change - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-non-const-type.stderr b/tests/ui/traits/const-traits/const_derives/derive-const-non-const-type.stderr index 64564de2a0ce4..27f4bcf46ef82 100644 --- a/tests/ui/traits/const-traits/const_derives/derive-const-non-const-type.stderr +++ b/tests/ui/traits/const-traits/const_derives/derive-const-non-const-type.stderr @@ -6,7 +6,6 @@ LL | #[derive_const(Default)] | = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` = note: adding a non-const method body in the future would be a breaking change - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const associated function `::default` in constant functions --> $DIR/derive-const-non-const-type.rs:11:14 @@ -17,7 +16,6 @@ LL | pub struct S(A); | ^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr b/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr index 9ad3b0c1617a5..8297911a3f3c7 100644 --- a/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr +++ b/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr @@ -27,7 +27,6 @@ LL | #[derive_const(Default, PartialEq)] | = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` = note: adding a non-const method body in the future would be a breaking change - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error: const `impl` for trait `PartialEq` which is not marked with `#[const_trait]` --> $DIR/derive-const-use.rs:11:12 @@ -46,7 +45,6 @@ LL | #[derive_const(Default, PartialEq)] | = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` = note: adding a non-const method body in the future would be a breaking change - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const associated function `::default` in constants --> $DIR/derive-const-use.rs:18:35 @@ -73,7 +71,6 @@ LL | pub struct S((), A); | ^^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const associated function `::default` in constant functions --> $DIR/derive-const-use.rs:16:18 @@ -84,7 +81,6 @@ LL | pub struct S((), A); | ^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const operator in constant functions --> $DIR/derive-const-use.rs:16:14 @@ -95,7 +91,6 @@ LL | pub struct S((), A); | ^^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const operator in constant functions --> $DIR/derive-const-use.rs:16:18 @@ -106,7 +101,6 @@ LL | pub struct S((), A); | ^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 12 previous errors diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-with-params.stderr b/tests/ui/traits/const-traits/const_derives/derive-const-with-params.stderr index 6b1405712ef7d..d1dbf62d5666a 100644 --- a/tests/ui/traits/const-traits/const_derives/derive-const-with-params.stderr +++ b/tests/ui/traits/const-traits/const_derives/derive-const-with-params.stderr @@ -6,7 +6,6 @@ LL | #[derive_const(PartialEq)] | = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` = note: adding a non-const method body in the future would be a breaking change - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error: `~const` can only be applied to `#[const_trait]` traits | @@ -22,7 +21,6 @@ LL | pub struct Reverse(T); | ^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const operator in constant functions --> $DIR/derive-const-with-params.rs:11:5 diff --git a/tests/ui/traits/const-traits/default-method-body-is-const-body-checking.stderr b/tests/ui/traits/const-traits/default-method-body-is-const-body-checking.stderr index 8c284bde67e7f..b3017523b27d0 100644 --- a/tests/ui/traits/const-traits/default-method-body-is-const-body-checking.stderr +++ b/tests/ui/traits/const-traits/default-method-body-is-const-body-checking.stderr @@ -8,7 +8,7 @@ note: required by a bound in `foo` --> $DIR/default-method-body-is-const-body-checking.rs:7:28 | LL | const fn foo() where T: ~const Tr {} - | ^^^^^^ required by this bound in `foo` + | ^^^^^^^^^ required by this bound in `foo` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/drop-manually-drop-no-drop-impl.rs b/tests/ui/traits/const-traits/drop-manually-drop-no-drop-impl.rs new file mode 100644 index 0000000000000..060a543d6c3db --- /dev/null +++ b/tests/ui/traits/const-traits/drop-manually-drop-no-drop-impl.rs @@ -0,0 +1,17 @@ +//@[new] compile-flags: -Znext-solver +//@ revisions: old new +//@ check-pass + +use std::mem::ManuallyDrop; + +struct Moose; + +impl Drop for Moose { + fn drop(&mut self) {} +} + +struct ConstDropper(ManuallyDrop); + +const fn foo(_var: ConstDropper) {} + +fn main() {} diff --git a/tests/ui/traits/const-traits/drop-manually-drop.rs b/tests/ui/traits/const-traits/drop-manually-drop.rs new file mode 100644 index 0000000000000..62e8a815f1002 --- /dev/null +++ b/tests/ui/traits/const-traits/drop-manually-drop.rs @@ -0,0 +1,24 @@ +//@[new] compile-flags: -Znext-solver +//@ revisions: old new +//@ check-pass + +#![feature(const_destruct)] +#![feature(const_trait_impl)] + +use std::mem::ManuallyDrop; + +struct Moose; + +impl Drop for Moose { + fn drop(&mut self) {} +} + +struct ConstDropper(ManuallyDrop); + +impl const Drop for ConstDropper { + fn drop(&mut self) {} +} + +const fn foo(_var: ConstDropper) {} + +fn main() {} diff --git a/tests/ui/traits/const-traits/feature-gate.gated.stderr b/tests/ui/traits/const-traits/feature-gate.gated.stderr deleted file mode 100644 index 12f9355e41d4e..0000000000000 --- a/tests/ui/traits/const-traits/feature-gate.gated.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fatal error triggered by #[rustc_error] - --> $DIR/feature-gate.rs:22:1 - | -LL | fn main() {} - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/traits/const-traits/feature-gate.rs b/tests/ui/traits/const-traits/feature-gate.rs index c36ec3538c364..921dfb054e305 100644 --- a/tests/ui/traits/const-traits/feature-gate.rs +++ b/tests/ui/traits/const-traits/feature-gate.rs @@ -1,8 +1,8 @@ //@ revisions: stock gated +//@[gated] check-pass // gate-test-const_trait_impl #![cfg_attr(gated, feature(const_trait_impl))] -#![feature(rustc_attrs)] struct S; #[const_trait] //[stock]~ ERROR `const_trait` is a temporary placeholder @@ -18,5 +18,4 @@ macro_rules! discard { ($ty:ty) => {} } discard! { impl ~const T } //[stock]~ ERROR const trait impls are experimental discard! { impl const T } //[stock]~ ERROR const trait impls are experimental -#[rustc_error] -fn main() {} //[gated]~ ERROR fatal error triggered by #[rustc_error] +fn main() {} diff --git a/tests/ui/traits/const-traits/ice-119717-constant-lifetime.rs b/tests/ui/traits/const-traits/ice-119717-constant-lifetime.rs index dc1e719bded1a..e53b87274d3a7 100644 --- a/tests/ui/traits/const-traits/ice-119717-constant-lifetime.rs +++ b/tests/ui/traits/const-traits/ice-119717-constant-lifetime.rs @@ -5,9 +5,9 @@ use std::ops::FromResidual; impl const FromResidual for T { //~^ ERROR const `impl` for trait `FromResidual` which is not marked with `#[const_trait]` - //~| type parameter `T` must be used as the type parameter for some local type + //~| ERROR type parameter `T` must be used as the type parameter for some local type fn from_residual(t: T) -> _ { - //~^ the placeholder `_` is not allowed + //~^ ERROR the placeholder `_` is not allowed t } } diff --git a/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr b/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr index 0d53bc5897ef3..7e72dc9abaa24 100644 --- a/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr +++ b/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr @@ -8,7 +8,7 @@ note: required by a bound in `Foo::Assoc` --> $DIR/item-bound-entailment-fails.rs:5:20 | LL | type Assoc: ~const Bar - | ^^^^^^ required by this bound in `Foo::Assoc` + | ^^^^^^^^^^ required by this bound in `Foo::Assoc` error[E0277]: the trait bound `T: ~const Bar` is not satisfied --> $DIR/item-bound-entailment-fails.rs:24:21 @@ -20,12 +20,12 @@ note: required for `C` to implement `~const Bar` --> $DIR/item-bound-entailment-fails.rs:14:15 | LL | impl const Bar for C where T: ~const Bar {} - | ^^^ ^^^^ ------ unsatisfied trait bound introduced here + | ^^^ ^^^^ ---------- unsatisfied trait bound introduced here note: required by a bound in `Foo::Assoc` --> $DIR/item-bound-entailment-fails.rs:5:20 | LL | type Assoc: ~const Bar - | ^^^^^^ required by this bound in `Foo::Assoc` + | ^^^^^^^^^^ required by this bound in `Foo::Assoc` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/minicore-fn-fail.stderr b/tests/ui/traits/const-traits/minicore-fn-fail.stderr index fa8be631a26bb..03c7ade87c011 100644 --- a/tests/ui/traits/const-traits/minicore-fn-fail.stderr +++ b/tests/ui/traits/const-traits/minicore-fn-fail.stderr @@ -10,7 +10,7 @@ note: required by a bound in `call_indirect` --> $DIR/minicore-fn-fail.rs:11:27 | LL | const fn call_indirect(t: &T) { t() } - | ^^^^^^ required by this bound in `call_indirect` + | ^^^^^^^^^^^ required by this bound in `call_indirect` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/no-explicit-const-params.stderr b/tests/ui/traits/const-traits/no-explicit-const-params.stderr index 9bd2c2cb8da2a..6955e9ab2985d 100644 --- a/tests/ui/traits/const-traits/no-explicit-const-params.stderr +++ b/tests/ui/traits/const-traits/no-explicit-const-params.stderr @@ -1,8 +1,8 @@ error[E0107]: function takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/no-explicit-const-params.rs:22:5 + --> $DIR/no-explicit-const-params.rs:15:5 | -LL | foo::(); - | ^^^--------- help: remove the unnecessary generics +LL | foo::(); + | ^^^-------- help: remove the unnecessary generics | | | expected 0 generic arguments | @@ -13,10 +13,10 @@ LL | const fn foo() {} | ^^^ error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/no-explicit-const-params.rs:24:12 + --> $DIR/no-explicit-const-params.rs:17:12 | -LL | <() as Bar>::bar(); - | ^^^------- help: remove the unnecessary generics +LL | <() as Bar>::bar(); + | ^^^------ help: remove the unnecessary generics | | | expected 0 generic arguments | @@ -26,17 +26,11 @@ note: trait defined here, with 0 generic parameters LL | trait Bar { | ^^^ -error[E0277]: the trait bound `(): const Bar` is not satisfied - --> $DIR/no-explicit-const-params.rs:24:6 - | -LL | <() as Bar>::bar(); - | ^^ - error[E0107]: function takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/no-explicit-const-params.rs:15:5 + --> $DIR/no-explicit-const-params.rs:22:5 | -LL | foo::(); - | ^^^-------- help: remove the unnecessary generics +LL | foo::(); + | ^^^--------- help: remove the unnecessary generics | | | expected 0 generic arguments | @@ -47,10 +41,10 @@ LL | const fn foo() {} | ^^^ error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/no-explicit-const-params.rs:17:12 + --> $DIR/no-explicit-const-params.rs:24:12 | -LL | <() as Bar>::bar(); - | ^^^------ help: remove the unnecessary generics +LL | <() as Bar>::bar(); + | ^^^------- help: remove the unnecessary generics | | | expected 0 generic arguments | @@ -60,6 +54,12 @@ note: trait defined here, with 0 generic parameters LL | trait Bar { | ^^^ +error[E0277]: the trait bound `(): const Bar` is not satisfied + --> $DIR/no-explicit-const-params.rs:24:6 + | +LL | <() as Bar>::bar(); + | ^^ + error: aborting due to 5 previous errors Some errors have detailed explanations: E0107, E0277. diff --git a/tests/ui/traits/const-traits/predicate-entailment-fails.stderr b/tests/ui/traits/const-traits/predicate-entailment-fails.stderr index 369e95688a9b2..dfdc4d232508f 100644 --- a/tests/ui/traits/const-traits/predicate-entailment-fails.stderr +++ b/tests/ui/traits/const-traits/predicate-entailment-fails.stderr @@ -5,7 +5,7 @@ LL | type Bar where T: ~const Bar; | ----------- definition of `Bar` from trait ... LL | type Bar = () where T: const Bar; - | ^^^^^ impl has extra requirement `T: const Bar` + | ^^^^^^^^^ impl has extra requirement `T: const Bar` error[E0276]: impl has stricter requirements than trait --> $DIR/predicate-entailment-fails.rs:17:26 @@ -14,7 +14,7 @@ LL | fn foo() where T: ~const Bar; | -------------------------------- definition of `foo` from trait ... LL | fn foo() where T: const Bar {} - | ^^^^^ impl has extra requirement `T: const Bar` + | ^^^^^^^^^ impl has extra requirement `T: const Bar` error[E0276]: impl has stricter requirements than trait --> $DIR/predicate-entailment-fails.rs:28:31 @@ -23,7 +23,7 @@ LL | type Bar where T: Bar; | ----------- definition of `Bar` from trait ... LL | type Bar = () where T: const Bar; - | ^^^^^ impl has extra requirement `T: const Bar` + | ^^^^^^^^^ impl has extra requirement `T: const Bar` error[E0276]: impl has stricter requirements than trait --> $DIR/predicate-entailment-fails.rs:31:26 @@ -32,7 +32,7 @@ LL | fn foo() where T: Bar; | ------------------------- definition of `foo` from trait ... LL | fn foo() where T: const Bar {} - | ^^^^^ impl has extra requirement `T: const Bar` + | ^^^^^^^^^ impl has extra requirement `T: const Bar` error[E0276]: impl has stricter requirements than trait --> $DIR/predicate-entailment-fails.rs:35:31 @@ -41,7 +41,7 @@ LL | type Bar where T: Bar; | ----------- definition of `Bar` from trait ... LL | type Bar = () where T: ~const Bar; - | ^^^^^^ impl has extra requirement `T: ~const Bar` + | ^^^^^^^^^^ impl has extra requirement `T: ~const Bar` error[E0276]: impl has stricter requirements than trait --> $DIR/predicate-entailment-fails.rs:38:26 @@ -50,7 +50,7 @@ LL | fn foo() where T: Bar; | ------------------------- definition of `foo` from trait ... LL | fn foo() where T: ~const Bar {} - | ^^^^^^ impl has extra requirement `T: ~const Bar` + | ^^^^^^^^^^ impl has extra requirement `T: ~const Bar` error: aborting due to 6 previous errors diff --git a/tests/ui/traits/const-traits/staged-api.rs b/tests/ui/traits/const-traits/staged-api.rs index 8dd7226fc298e..bf09a5f780385 100644 --- a/tests/ui/traits/const-traits/staged-api.rs +++ b/tests/ui/traits/const-traits/staged-api.rs @@ -96,12 +96,12 @@ trait S {} // implied stable impl const U for u8 {} -//~^ const stability on the impl does not match the const stability on the trait +//~^ ERROR const stability on the impl does not match the const stability on the trait #[rustc_const_stable(since = "0.0.0", feature = "beef2")] impl const U for u16 {} -//~^ const stability on the impl does not match the const stability on the trait -//~| trait implementations cannot be const stable yet +//~^ ERROR const stability on the impl does not match the const stability on the trait +//~| ERROR trait implementations cannot be const stable yet #[rustc_const_unstable(feature = "beef", issue = "none")] impl const U for u32 {} @@ -111,10 +111,10 @@ impl const S for u8 {} #[rustc_const_stable(since = "0.0.0", feature = "beef2")] impl const S for u16 {} -//~^ trait implementations cannot be const stable yet +//~^ ERROR trait implementations cannot be const stable yet #[rustc_const_unstable(feature = "beef", issue = "none")] impl const S for u32 {} -//~^ const stability on the impl does not match the const stability on the trait +//~^ ERROR const stability on the impl does not match the const stability on the trait fn main() {} diff --git a/tests/ui/traits/const-traits/trait-where-clause-const.stderr b/tests/ui/traits/const-traits/trait-where-clause-const.stderr index 4100ae1c6bfd7..4ebd7b9757fe7 100644 --- a/tests/ui/traits/const-traits/trait-where-clause-const.stderr +++ b/tests/ui/traits/const-traits/trait-where-clause-const.stderr @@ -8,7 +8,7 @@ note: required by a bound in `Foo::b` --> $DIR/trait-where-clause-const.rs:15:24 | LL | fn b() where Self: ~const Bar; - | ^^^^^^ required by this bound in `Foo::b` + | ^^^^^^^^^^ required by this bound in `Foo::b` error[E0277]: the trait bound `T: ~const Bar` is not satisfied --> $DIR/trait-where-clause-const.rs:23:12 @@ -20,7 +20,7 @@ note: required by a bound in `Foo::c` --> $DIR/trait-where-clause-const.rs:16:13 | LL | fn c(); - | ^^^^^^ required by this bound in `Foo::c` + | ^^^^^^^^^^ required by this bound in `Foo::c` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr b/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr index bda6a029cc2a6..03e26615d7edc 100644 --- a/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr +++ b/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr @@ -6,30 +6,134 @@ LL | #![feature(const_trait_impl, generic_const_exprs)] | = help: remove one of these features -error[E0277]: the trait bound `T: const Trait` is not satisfied - --> $DIR/unsatisfied-const-trait-bound.rs:29:37 +error[E0391]: cycle detected when evaluating type-level constant + --> $DIR/unsatisfied-const-trait-bound.rs:29:35 | LL | fn accept0(_: Container<{ T::make() }>) {} - | ^ + | ^^^^^^^^^^^^^ + | +note: ...which requires const-evaluating + checking `accept0::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:29:35 + | +LL | fn accept0(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires caching mir of `accept0::{constant#0}` for CTFE... + --> $DIR/unsatisfied-const-trait-bound.rs:29:35 + | +LL | fn accept0(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires elaborating drops for `accept0::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:29:35 + | +LL | fn accept0(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires borrow-checking `accept0::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:29:35 + | +LL | fn accept0(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires promoting constants in MIR for `accept0::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:29:35 + | +LL | fn accept0(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires const checking `accept0::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:29:35 + | +LL | fn accept0(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires building MIR for `accept0::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:29:35 + | +LL | fn accept0(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires building an abstract representation for `accept0::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:29:35 + | +LL | fn accept0(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires building THIR for `accept0::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:29:35 + | +LL | fn accept0(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires type-checking `accept0::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:29:35 + | +LL | fn accept0(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ + = note: ...which again requires evaluating type-level constant, completing the cycle +note: cycle used when checking that `accept0` is well-formed + --> $DIR/unsatisfied-const-trait-bound.rs:29:1 + | +LL | fn accept0(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0277]: the trait bound `T: const Trait` is not satisfied - --> $DIR/unsatisfied-const-trait-bound.rs:33:50 +error[E0391]: cycle detected when caching mir of `accept1::{constant#0}` for CTFE + --> $DIR/unsatisfied-const-trait-bound.rs:33:48 | LL | const fn accept1(_: Container<{ T::make() }>) {} - | ^ - -error[E0277]: the trait bound `Ty: const Trait` is not satisfied - --> $DIR/unsatisfied-const-trait-bound.rs:22:15 + | ^^^^^^^^^^^^^ + | +note: ...which requires elaborating drops for `accept1::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:33:48 + | +LL | const fn accept1(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires borrow-checking `accept1::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:33:48 + | +LL | const fn accept1(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires promoting constants in MIR for `accept1::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:33:48 + | +LL | const fn accept1(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires const checking `accept1::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:33:48 + | +LL | const fn accept1(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires building MIR for `accept1::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:33:48 | -LL | require::(); - | ^^ +LL | const fn accept1(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires building an abstract representation for `accept1::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:33:48 + | +LL | const fn accept1(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires building THIR for `accept1::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:33:48 | -note: required by a bound in `require` - --> $DIR/unsatisfied-const-trait-bound.rs:8:15 +LL | const fn accept1(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires type-checking `accept1::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:33:48 + | +LL | const fn accept1(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires evaluating type-level constant... + --> $DIR/unsatisfied-const-trait-bound.rs:33:48 | -LL | fn require() {} - | ^^^^^ required by this bound in `require` +LL | const fn accept1(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `accept1::{constant#0}`... + --> $DIR/unsatisfied-const-trait-bound.rs:33:48 + | +LL | const fn accept1(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ + = note: ...which again requires caching mir of `accept1::{constant#0}` for CTFE, completing the cycle +note: cycle used when const-evaluating + checking `accept1::{constant#0}` + --> $DIR/unsatisfied-const-trait-bound.rs:33:48 + | +LL | const fn accept1(_: Container<{ T::make() }>) {} + | ^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/traits/copy-is-not-modulo-regions.rs b/tests/ui/traits/copy-is-not-modulo-regions.rs index 1e7d2d9c691ac..2efbeb4889def 100644 --- a/tests/ui/traits/copy-is-not-modulo-regions.rs +++ b/tests/ui/traits/copy-is-not-modulo-regions.rs @@ -11,7 +11,7 @@ struct Bar<'lt>(Foo<'lt>); #[cfg(not_static)] impl<'any> Copy for Bar<'any> {} -//[not_static]~^ the trait `Copy` cannot be implemented for this type +//[not_static]~^ ERROR the trait `Copy` cannot be implemented for this type #[cfg(yes_static)] impl<'any> Copy for Bar<'static> {} diff --git a/tests/ui/traits/deep-norm-pending.rs b/tests/ui/traits/deep-norm-pending.rs new file mode 100644 index 0000000000000..f56c3cfa3eab4 --- /dev/null +++ b/tests/ui/traits/deep-norm-pending.rs @@ -0,0 +1,24 @@ +trait Foo { + type Assoc; +} + +trait Bar { + fn method() -> impl Sized; + //~^ ERROR the trait bound `T: Foo` is not satisfied +} +impl Bar for T +//~^ ERROR the trait bound `T: Foo` is not satisfied +//~| ERROR the trait bound `T: Foo` is not satisfied +where + ::Assoc: Sized, +{ + fn method() {} + //~^ ERROR the trait bound `T: Foo` is not satisfied + //~| ERROR the trait bound `T: Foo` is not satisfied + //~| ERROR the trait bound `T: Foo` is not satisfied + //~| ERROR the trait bound `T: Foo` is not satisfied + //~| ERROR the trait bound `T: Foo` is not satisfied + //~| ERROR the trait bound `T: Foo` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/deep-norm-pending.stderr b/tests/ui/traits/deep-norm-pending.stderr new file mode 100644 index 0000000000000..b95b9d7f4aec7 --- /dev/null +++ b/tests/ui/traits/deep-norm-pending.stderr @@ -0,0 +1,130 @@ +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:15:5 + | +LL | fn method() {} + | ^^^^^^^^^^^ the trait `Foo` is not implemented for `T` + | +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:9:1 + | +LL | / impl Bar for T +LL | | +LL | | +LL | | where +LL | | ::Assoc: Sized, + | |_____________________________^ the trait `Foo` is not implemented for `T` + | +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:9:1 + | +LL | / impl Bar for T +LL | | +LL | | +LL | | where +... | +LL | | } + | |_^ the trait `Foo` is not implemented for `T` + | +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:15:5 + | +LL | fn method() {} + | ^^^^^^^^^^^ the trait `Foo` is not implemented for `T` + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:15:5 + | +LL | fn method() {} + | ^^^^^^^^^^^ the trait `Foo` is not implemented for `T` + | +note: required for `T` to implement `Bar` + --> $DIR/deep-norm-pending.rs:9:9 + | +LL | impl Bar for T + | ^^^ ^ +... +LL | ::Assoc: Sized, + | ----- unsatisfied trait bound introduced here +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:15:5 + | +LL | fn method() {} + | ^^^^^^^^^^^ the trait `Foo` is not implemented for `T` + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:6:20 + | +LL | fn method() -> impl Sized; + | ^^^^^^^^^^ the trait `Foo` is not implemented for `T` + | +note: required for `T` to implement `Bar` + --> $DIR/deep-norm-pending.rs:9:9 + | +LL | impl Bar for T + | ^^^ ^ +... +LL | ::Assoc: Sized, + | ----- unsatisfied trait bound introduced here +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:15:5 + | +LL | fn method() {} + | ^^^^^^^^^^^ the trait `Foo` is not implemented for `T` + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error[E0277]: the trait bound `T: Foo` is not satisfied + --> $DIR/deep-norm-pending.rs:15:8 + | +LL | fn method() {} + | ^^^^^^ the trait `Foo` is not implemented for `T` + | +help: consider further restricting type parameter `T` with trait `Foo` + | +LL | ::Assoc: Sized, T: Foo + | ++++++ + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of.rs b/tests/ui/traits/default-method/rustc_must_implement_one_of.rs index 5ba2f5ce33426..a77f1d5e608e3 100644 --- a/tests/ui/traits/default-method/rustc_must_implement_one_of.rs +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of.rs @@ -39,6 +39,6 @@ impl Equal for T2 { } impl Equal for T3 {} -//~^ not all trait items implemented, missing one of: `eq`, `neq` +//~^ ERROR not all trait items implemented, missing one of: `eq`, `neq` fn main() {} diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of_duplicates.rs b/tests/ui/traits/default-method/rustc_must_implement_one_of_duplicates.rs index 8db5fa615c08d..1e91b06079267 100644 --- a/tests/ui/traits/default-method/rustc_must_implement_one_of_duplicates.rs +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of_duplicates.rs @@ -1,15 +1,15 @@ #![feature(rustc_attrs)] #[rustc_must_implement_one_of(a, a)] -//~^ functions names are duplicated +//~^ ERROR functions names are duplicated trait Trait { fn a() {} } #[rustc_must_implement_one_of(b, a, a, c, b, c)] -//~^ functions names are duplicated -//~| functions names are duplicated -//~| functions names are duplicated +//~^ ERROR functions names are duplicated +//~| ERROR functions names are duplicated +//~| ERROR functions names are duplicated trait Trait1 { fn a() {} fn b() {} diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs b/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs index ec2995872de02..27e70556b7af8 100644 --- a/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of_gated.rs @@ -1,5 +1,5 @@ #[rustc_must_implement_one_of(eq, neq)] -//~^ the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait, it's currently in experimental form and should be changed before being exposed outside of the std +//~^ ERROR the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait, it's currently in experimental form and should be changed before being exposed outside of the std trait Equal { fn eq(&self, other: &Self) -> bool { !self.neq(other) diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs b/tests/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs index b1b91966c8d88..9b2e489c54251 100644 --- a/tests/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs @@ -1,46 +1,46 @@ #![feature(rustc_attrs)] #[rustc_must_implement_one_of(a, b)] -//~^ function not found in this trait -//~| function not found in this trait +//~^ ERROR function not found in this trait +//~| ERROR function not found in this trait trait Tr0 {} #[rustc_must_implement_one_of(a, b)] -//~^ function not found in this trait +//~^ ERROR function not found in this trait trait Tr1 { fn a() {} } #[rustc_must_implement_one_of(a)] -//~^ the `#[rustc_must_implement_one_of]` attribute must be used with at least 2 args +//~^ ERROR the `#[rustc_must_implement_one_of]` attribute must be used with at least 2 args trait Tr2 { fn a() {} } #[rustc_must_implement_one_of] -//~^ malformed `rustc_must_implement_one_of` attribute input +//~^ ERROR malformed `rustc_must_implement_one_of` attribute input trait Tr3 {} #[rustc_must_implement_one_of(A, B)] trait Tr4 { - const A: u8 = 1; //~ not a function + const A: u8 = 1; //~ ERROR not a function - type B; //~ not a function + type B; //~ ERROR not a function } #[rustc_must_implement_one_of(a, b)] trait Tr5 { - fn a(); //~ function doesn't have a default implementation + fn a(); //~ ERROR function doesn't have a default implementation - fn b(); //~ function doesn't have a default implementation + fn b(); //~ ERROR function doesn't have a default implementation } #[rustc_must_implement_one_of(abc, xyz)] -//~^ attribute should be applied to a trait +//~^ ERROR attribute should be applied to a trait fn function() {} #[rustc_must_implement_one_of(abc, xyz)] -//~^ attribute should be applied to a trait +//~^ ERROR attribute should be applied to a trait struct Struct {} fn main() {} diff --git a/tests/ui/traits/default_auto_traits/backward-compatible-lazy-bounds-pass.rs b/tests/ui/traits/default_auto_traits/backward-compatible-lazy-bounds-pass.rs new file mode 100644 index 0000000000000..3818456d3a675 --- /dev/null +++ b/tests/ui/traits/default_auto_traits/backward-compatible-lazy-bounds-pass.rs @@ -0,0 +1,25 @@ +//@ check-pass +//@ compile-flags: -Zexperimental-default-bounds + +#![feature(auto_traits, lang_items, no_core, rustc_attrs, trait_alias)] +#![no_std] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[lang = "default_trait1"] +auto trait DefaultTrait1 {} + +#[lang = "default_trait2"] +auto trait DefaultTrait2 {} + +trait Trait {} +trait Trait1 : Trait {} + +trait Trait2 { + type Type; +} +trait Trait3 = Trait2; + +fn main() {} diff --git a/tests/ui/traits/default_auto_traits/default-bounds.rs b/tests/ui/traits/default_auto_traits/default-bounds.rs new file mode 100644 index 0000000000000..64733a4003409 --- /dev/null +++ b/tests/ui/traits/default_auto_traits/default-bounds.rs @@ -0,0 +1,41 @@ +//@ compile-flags: -Zexperimental-default-bounds + +#![feature( + auto_traits, + lang_items, + negative_impls, + no_core, + rustc_attrs +)] +#![allow(incomplete_features)] +#![no_std] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[lang = "copy"] +pub trait Copy {} + +#[lang = "default_trait1"] +auto trait Leak {} + +#[lang = "default_trait2"] +auto trait SyncDrop {} + +struct Forbidden; + +impl !Leak for Forbidden {} +impl !SyncDrop for Forbidden {} + +struct Accepted; + +fn bar(_: T) {} + +fn main() { + // checking that bounds can be added explicitly + bar(Forbidden); + //~^ ERROR the trait bound `Forbidden: Leak` is not satisfied + //~| ERROR the trait bound `Forbidden: SyncDrop` is not satisfied + bar(Accepted); +} diff --git a/tests/ui/traits/default_auto_traits/default-bounds.stderr b/tests/ui/traits/default_auto_traits/default-bounds.stderr new file mode 100644 index 0000000000000..10fdcc43417de --- /dev/null +++ b/tests/ui/traits/default_auto_traits/default-bounds.stderr @@ -0,0 +1,31 @@ +error[E0277]: the trait bound `Forbidden: SyncDrop` is not satisfied + --> $DIR/default-bounds.rs:37:9 + | +LL | bar(Forbidden); + | --- ^^^^^^^^^ the trait `SyncDrop` is not implemented for `Forbidden` + | | + | required by a bound introduced by this call + | +note: required by a bound in `bar` + --> $DIR/default-bounds.rs:33:8 + | +LL | fn bar(_: T) {} + | ^ required by this bound in `bar` + +error[E0277]: the trait bound `Forbidden: Leak` is not satisfied + --> $DIR/default-bounds.rs:37:9 + | +LL | bar(Forbidden); + | --- ^^^^^^^^^ the trait `Leak` is not implemented for `Forbidden` + | | + | required by a bound introduced by this call + | +note: required by a bound in `bar` + --> $DIR/default-bounds.rs:33:11 + | +LL | fn bar(_: T) {} + | ^^^^ required by this bound in `bar` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/default_auto_traits/extern-types.current.stderr b/tests/ui/traits/default_auto_traits/extern-types.current.stderr new file mode 100644 index 0000000000000..e1bd99b900f56 --- /dev/null +++ b/tests/ui/traits/default_auto_traits/extern-types.current.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `extern_non_leak::Opaque: Leak` is not satisfied + --> $DIR/extern-types.rs:44:13 + | +LL | foo(x); + | --- ^ the trait `Leak` is not implemented for `extern_non_leak::Opaque` + | | + | required by a bound introduced by this call + | +note: required by a bound in `foo` + --> $DIR/extern-types.rs:20:8 + | +LL | fn foo(_: &T) {} + | ^ required by this bound in `foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/default_auto_traits/extern-types.next.stderr b/tests/ui/traits/default_auto_traits/extern-types.next.stderr new file mode 100644 index 0000000000000..e1bd99b900f56 --- /dev/null +++ b/tests/ui/traits/default_auto_traits/extern-types.next.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `extern_non_leak::Opaque: Leak` is not satisfied + --> $DIR/extern-types.rs:44:13 + | +LL | foo(x); + | --- ^ the trait `Leak` is not implemented for `extern_non_leak::Opaque` + | | + | required by a bound introduced by this call + | +note: required by a bound in `foo` + --> $DIR/extern-types.rs:20:8 + | +LL | fn foo(_: &T) {} + | ^ required by this bound in `foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/default_auto_traits/extern-types.rs b/tests/ui/traits/default_auto_traits/extern-types.rs new file mode 100644 index 0000000000000..822d4c0637f07 --- /dev/null +++ b/tests/ui/traits/default_auto_traits/extern-types.rs @@ -0,0 +1,49 @@ +//@ compile-flags: -Zexperimental-default-bounds +//@ revisions: current next +//@ [next] compile-flags: -Znext-solver + +#![feature(auto_traits, extern_types, lang_items, negative_impls, no_core, rustc_attrs)] +#![allow(incomplete_features)] +#![no_std] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[lang = "copy"] +pub trait Copy {} + +#[lang = "default_trait1"] +auto trait Leak {} + +// implicit T: Leak here +fn foo(_: &T) {} + +mod extern_leak { + use crate::*; + + extern "C" { + type Opaque; + } + + fn forward_extern_ty(x: &Opaque) { + // ok, extern type leak by default + crate::foo(x); + } +} + +mod extern_non_leak { + use crate::*; + + extern "C" { + type Opaque; + } + + impl !Leak for Opaque {} + fn forward_extern_ty(x: &Opaque) { + foo(x); + //~^ ERROR: the trait bound `extern_non_leak::Opaque: Leak` is not satisfied + } +} + +fn main() {} diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.rs b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.rs new file mode 100644 index 0000000000000..49f2faba1468a --- /dev/null +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.rs @@ -0,0 +1,61 @@ +//@ compile-flags: -Zexperimental-default-bounds + +#![feature( + auto_traits, + lang_items, + more_maybe_bounds, + negative_impls, + no_core, + rustc_attrs +)] +#![allow(internal_features)] +#![no_std] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[lang = "copy"] +pub trait Copy {} +impl<'a, T: ?Sized> Copy for &'a T {} + +#[lang = "legacy_receiver"] +trait Receiver {} +impl Receiver for &T {} + +#[lang = "unsize"] +trait Unsize {} + +#[lang = "coerce_unsized"] +trait CoerceUnsized {} +impl<'a, 'b: 'a, T: ?Sized + ?Leak + Unsize, U: ?Sized + ?Leak> CoerceUnsized<&'a U> for &'b T {} + +#[lang = "dispatch_from_dyn"] +trait DispatchFromDyn {} +impl<'a, T: ?Sized + ?Leak + Unsize, U: ?Sized + ?Leak> DispatchFromDyn<&'a U> for &'a T {} + +#[lang = "default_trait1"] +auto trait Leak {} + +struct NonLeakS; +impl !Leak for NonLeakS {} +struct LeakS; + +trait Trait { + fn leak_foo(&self) {} + fn maybe_leak_foo(&self) where Self: ?Leak {} +} + +impl Trait for NonLeakS {} +impl Trait for LeakS {} + +fn main() { + let _: &dyn Trait = &NonLeakS; + //~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied + let _: &dyn Trait = &LeakS; + let _: &(dyn Trait + ?Leak) = &LeakS; + let x: &(dyn Trait + ?Leak) = &NonLeakS; + x.leak_foo(); + //~^ ERROR the trait bound `dyn Trait: Leak` is not satisfied + x.maybe_leak_foo(); +} diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr new file mode 100644 index 0000000000000..b7ffb66e60bf2 --- /dev/null +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied + --> $DIR/maybe-bounds-in-dyn-traits.rs:53:25 + | +LL | let _: &dyn Trait = &NonLeakS; + | ^^^^^^^^^ the trait `Leak` is not implemented for `NonLeakS` + | + = note: required for the cast from `&NonLeakS` to `&dyn Trait + Leak` + +error[E0277]: the trait bound `dyn Trait: Leak` is not satisfied + --> $DIR/maybe-bounds-in-dyn-traits.rs:58:7 + | +LL | x.leak_foo(); + | ^^^^^^^^ the trait `Leak` is not implemented for `dyn Trait` + | +note: required by a bound in `Trait::leak_foo` + --> $DIR/maybe-bounds-in-dyn-traits.rs:45:5 + | +LL | fn leak_foo(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Trait::leak_foo` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs new file mode 100644 index 0000000000000..4cb38bc8e7957 --- /dev/null +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs @@ -0,0 +1,115 @@ +//@ compile-flags: -Zexperimental-default-bounds + +#![feature( + auto_traits, + associated_type_defaults, + generic_const_items, + lang_items, + more_maybe_bounds, + negative_impls, + no_core, + rustc_attrs +)] +#![allow(incomplete_features)] +#![no_std] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[lang = "legacy_receiver"] +trait LegacyReceiver {} +impl LegacyReceiver for &T {} +impl LegacyReceiver for &mut T {} + +#[lang = "default_trait1"] +auto trait Leak {} + +struct NonLeakS; +impl !Leak for NonLeakS {} +struct LeakS; + +mod supertraits { + use crate::*; + + trait MaybeLeakT1: ?Leak {} + trait MaybeLeakT2 where Self: ?Leak {} + + impl MaybeLeakT1 for NonLeakS {} + impl MaybeLeakT2 for NonLeakS {} +} + +mod maybe_self_assoc_type { + use crate::*; + + trait TestBase1 {} + trait TestBase2 {} + + trait Test1 { + type MaybeLeakSelf: TestBase1 where Self: ?Leak; + //~^ ERROR the trait bound `Self: Leak` is not satisfied + type LeakSelf: TestBase1; + } + + trait Test2 { + type MaybeLeakSelf: TestBase2 where Self: ?Leak; + type LeakSelf: TestBase2; + } + + trait Test3 { + type Leak1 = LeakS; + type Leak2 = NonLeakS; + //~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied + } + + trait Test4 { + type MaybeLeak1: ?Leak = LeakS; + type MaybeLeak2: ?Leak = NonLeakS; + } + + trait Test5: ?Leak { + // ok, because assoc types have implicit where Self: Leak + type MaybeLeakSelf1: TestBase1; + type MaybeLeakSelf2: TestBase2; + } +} + +mod maybe_self_assoc_const { + use crate::*; + + const fn size_of() -> usize { + 0 + } + + trait Trait { + const CLeak: usize = size_of::(); + const CNonLeak: usize = size_of::() where Self: ?Leak; + //~^ ERROR the trait bound `Self: Leak` is not satisfied + } +} + +mod methods { + use crate::*; + + trait Trait { + fn leak_foo(&self) {} + fn maybe_leak_foo(&self) where Self: ?Leak {} + fn mut_leak_foo(&mut self) {} + // there is no relax bound on corresponding Receiver impl + fn mut_maybe_leak_foo(&mut self) where Self: ?Leak {} + //~^ ERROR `&mut Self` cannot be used as the type of `self` without the `arbitrary_self_types` + } + + impl Trait for NonLeakS {} + impl Trait for LeakS {} + + fn foo() { + LeakS.leak_foo(); + LeakS.maybe_leak_foo(); + NonLeakS.leak_foo(); + //~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied + NonLeakS.maybe_leak_foo(); + } +} + +fn main() {} diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr new file mode 100644 index 0000000000000..3dd8418b100fc --- /dev/null +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr @@ -0,0 +1,71 @@ +error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied + --> $DIR/maybe-bounds-in-traits.rs:61:22 + | +LL | type Leak2 = NonLeakS; + | ^^^^^^^^ the trait `Leak` is not implemented for `NonLeakS` + | +note: required by a bound in `Test3::Leak2` + --> $DIR/maybe-bounds-in-traits.rs:61:9 + | +LL | type Leak2 = NonLeakS; + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Test3::Leak2` + +error[E0277]: the trait bound `Self: Leak` is not satisfied + --> $DIR/maybe-bounds-in-traits.rs:49:29 + | +LL | type MaybeLeakSelf: TestBase1 where Self: ?Leak; + | ^^^^^^^^^^^^^^^ the trait `Leak` is not implemented for `Self` + | +note: required by a bound in `TestBase1` + --> $DIR/maybe-bounds-in-traits.rs:45:21 + | +LL | trait TestBase1 {} + | ^ required by this bound in `TestBase1` +help: consider further restricting `Self` + | +LL | trait Test1: Leak { + | ++++++ + +error[E0658]: `&mut Self` cannot be used as the type of `self` without the `arbitrary_self_types` feature + --> $DIR/maybe-bounds-in-traits.rs:99:31 + | +LL | fn mut_maybe_leak_foo(&mut self) where Self: ?Leak {} + | ^^^^^^^^^ + | + = note: see issue #44874 for more information + = help: add `#![feature(arbitrary_self_types)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` + +error[E0277]: the trait bound `Self: Leak` is not satisfied + --> $DIR/maybe-bounds-in-traits.rs:86:43 + | +LL | const CNonLeak: usize = size_of::() where Self: ?Leak; + | ^^^^ the trait `Leak` is not implemented for `Self` + | +note: required by a bound in `size_of` + --> $DIR/maybe-bounds-in-traits.rs:80:22 + | +LL | const fn size_of() -> usize { + | ^ required by this bound in `size_of` +help: consider further restricting `Self` + | +LL | trait Trait: Leak { + | ++++++ + +error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied + --> $DIR/maybe-bounds-in-traits.rs:109:18 + | +LL | NonLeakS.leak_foo(); + | ^^^^^^^^ the trait `Leak` is not implemented for `NonLeakS` + | +note: required by a bound in `methods::Trait::leak_foo` + --> $DIR/maybe-bounds-in-traits.rs:95:9 + | +LL | fn leak_foo(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Trait::leak_foo` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0277, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/dont-suggest-impl-as-closure-arg.rs b/tests/ui/traits/dont-suggest-impl-as-closure-arg.rs new file mode 100644 index 0000000000000..68e234b0f20d3 --- /dev/null +++ b/tests/ui/traits/dont-suggest-impl-as-closure-arg.rs @@ -0,0 +1,5 @@ +// Suggestion to use impl trait in closure parameter is invalid, see issue 138932 +fn main() { + let c = |f: dyn Fn()| f(); + //~^ ERROR: the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time +} diff --git a/tests/ui/traits/dont-suggest-impl-as-closure-arg.stderr b/tests/ui/traits/dont-suggest-impl-as-closure-arg.stderr new file mode 100644 index 0000000000000..8218990503c2f --- /dev/null +++ b/tests/ui/traits/dont-suggest-impl-as-closure-arg.stderr @@ -0,0 +1,16 @@ +error[E0277]: the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time + --> $DIR/dont-suggest-impl-as-closure-arg.rs:3:17 + | +LL | let c = |f: dyn Fn()| f(); + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Fn() + 'static)` + = help: unsized fn params are gated as an unstable feature +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | let c = |f: &dyn Fn()| f(); + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/dyn-drop-principal-with-projections.rs b/tests/ui/traits/dyn-drop-principal-with-projections.rs new file mode 100644 index 0000000000000..912061847c69f --- /dev/null +++ b/tests/ui/traits/dyn-drop-principal-with-projections.rs @@ -0,0 +1,13 @@ +//@ check-pass + +trait Tr { + type Assoc; +} + +impl Tr for () { + type Assoc = (); +} + +fn main() { + let x = &() as &(dyn Tr + Send) as &dyn Send; +} diff --git a/tests/ui/traits/impl-method-mismatch.rs b/tests/ui/traits/impl-method-mismatch.rs index 62580755c814e..a7de5e3811372 100644 --- a/tests/ui/traits/impl-method-mismatch.rs +++ b/tests/ui/traits/impl-method-mismatch.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + trait Mumbo { fn jumbo(&self, x: &usize) -> usize; } @@ -6,8 +8,8 @@ impl Mumbo for usize { // Cannot have a larger effect than the trait: unsafe fn jumbo(&self, x: &usize) { *self + *x; } //~^ ERROR method `jumbo` has an incompatible type for trait - //~| expected signature `fn - //~| found signature `unsafe fn + //~| NOTE expected signature `fn + //~| NOTE found signature `unsafe fn } fn main() {} diff --git a/tests/ui/traits/impl-method-mismatch.stderr b/tests/ui/traits/impl-method-mismatch.stderr index db457b77a2342..ca8b35e06c78f 100644 --- a/tests/ui/traits/impl-method-mismatch.stderr +++ b/tests/ui/traits/impl-method-mismatch.stderr @@ -1,11 +1,11 @@ error[E0053]: method `jumbo` has an incompatible type for trait - --> $DIR/impl-method-mismatch.rs:7:5 + --> $DIR/impl-method-mismatch.rs:9:5 | LL | unsafe fn jumbo(&self, x: &usize) { *self + *x; } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected safe fn, found unsafe fn | note: type in trait - --> $DIR/impl-method-mismatch.rs:2:5 + --> $DIR/impl-method-mismatch.rs:4:5 | LL | fn jumbo(&self, x: &usize) -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/incomplete-infer-via-sized-wc.current.stderr b/tests/ui/traits/incomplete-infer-via-sized-wc.current.stderr new file mode 100644 index 0000000000000..f4930bf890c2b --- /dev/null +++ b/tests/ui/traits/incomplete-infer-via-sized-wc.current.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/incomplete-infer-via-sized-wc.rs:15:5 + | +LL | is_sized::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `is_sized` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/incomplete-infer-via-sized-wc.next.stderr b/tests/ui/traits/incomplete-infer-via-sized-wc.next.stderr new file mode 100644 index 0000000000000..f4930bf890c2b --- /dev/null +++ b/tests/ui/traits/incomplete-infer-via-sized-wc.next.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/incomplete-infer-via-sized-wc.rs:15:5 + | +LL | is_sized::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `is_sized` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/incomplete-infer-via-sized-wc.rs b/tests/ui/traits/incomplete-infer-via-sized-wc.rs new file mode 100644 index 0000000000000..9dcddea3551d1 --- /dev/null +++ b/tests/ui/traits/incomplete-infer-via-sized-wc.rs @@ -0,0 +1,19 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Exercises change in . + +struct MaybeSized(T); + +fn is_sized() -> Box { todo!() } + +fn foo() +where + MaybeSized: Sized, +{ + is_sized::>(); + //~^ ERROR type annotations needed +} + +fn main() {} diff --git a/tests/ui/traits/inductive-overflow/supertrait-auto-trait.rs b/tests/ui/traits/inductive-overflow/supertrait-auto-trait.rs index 5fea47a1be87d..ca31d39de9821 100644 --- a/tests/ui/traits/inductive-overflow/supertrait-auto-trait.rs +++ b/tests/ui/traits/inductive-overflow/supertrait-auto-trait.rs @@ -13,6 +13,6 @@ fn copy(x: T) -> (T, T) { (x, x) } struct NoClone; fn main() { - let (a, b) = copy(NoClone); //~ ERROR + let (a, b) = copy(NoClone); println!("{:?} {:?}", a, b); } diff --git a/tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr b/tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr index 07edc4ede7619..3a3b99f6c5b10 100644 --- a/tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr +++ b/tests/ui/traits/inductive-overflow/supertrait-auto-trait.stderr @@ -6,31 +6,6 @@ LL | auto trait Magic: Copy {} | | | auto traits cannot have super traits or lifetime bounds -error[E0277]: the trait bound `NoClone: Magic` is not satisfied - --> $DIR/supertrait-auto-trait.rs:16:23 - | -LL | let (a, b) = copy(NoClone); - | ---- ^^^^^^^ the trait `Copy` is not implemented for `NoClone` - | | - | required by a bound introduced by this call - | -note: required for `NoClone` to implement `Magic` - --> $DIR/supertrait-auto-trait.rs:8:12 - | -LL | auto trait Magic: Copy {} - | ^^^^^ -note: required by a bound in `copy` - --> $DIR/supertrait-auto-trait.rs:10:12 - | -LL | fn copy(x: T) -> (T, T) { (x, x) } - | ^^^^^ required by this bound in `copy` -help: consider annotating `NoClone` with `#[derive(Copy)]` - | -LL + #[derive(Copy)] -LL | struct NoClone; - | - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0277, E0568. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0568`. diff --git a/tests/ui/traits/issue-106072.rs b/tests/ui/traits/issue-106072.rs index d75c26642c600..d45668312218b 100644 --- a/tests/ui/traits/issue-106072.rs +++ b/tests/ui/traits/issue-106072.rs @@ -1,6 +1,6 @@ #[derive(Clone)] -//~^ expected a type, found a trait -//~| expected a type, found a trait +//~^ ERROR expected a type, found a trait +//~| ERROR expected a type, found a trait struct Foo; -trait Foo {} //~ the name `Foo` is defined multiple times +trait Foo {} //~ ERROR the name `Foo` is defined multiple times fn main() {} diff --git a/tests/ui/traits/issue-106072.stderr b/tests/ui/traits/issue-106072.stderr index 3e0d6d88086f7..815787c3bfecb 100644 --- a/tests/ui/traits/issue-106072.stderr +++ b/tests/ui/traits/issue-106072.stderr @@ -13,8 +13,6 @@ error[E0782]: expected a type, found a trait | LL | #[derive(Clone)] | ^^^^^ - | - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0782]: expected a type, found a trait --> $DIR/issue-106072.rs:1:10 @@ -23,7 +21,6 @@ LL | #[derive(Clone)] | ^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/traits/issue-33140-hack-boundaries.rs b/tests/ui/traits/issue-33140-hack-boundaries.rs index d091162fced6b..06786dcc8fe87 100644 --- a/tests/ui/traits/issue-33140-hack-boundaries.rs +++ b/tests/ui/traits/issue-33140-hack-boundaries.rs @@ -1,5 +1,4 @@ #![feature(negative_impls)] -#![allow(order_dependent_trait_objects)] // Check that the issue #33140 hack does not allow unintended things. @@ -8,6 +7,7 @@ trait Trait0 {} impl Trait0 for dyn Send {} impl Trait0 for dyn Send {} +//~^ ERROR: E0119 // Problem 1: associated types trait Trait1 { diff --git a/tests/ui/traits/issue-33140-hack-boundaries.stderr b/tests/ui/traits/issue-33140-hack-boundaries.stderr index d9c4efbb721c7..ed3ae2b31675a 100644 --- a/tests/ui/traits/issue-33140-hack-boundaries.stderr +++ b/tests/ui/traits/issue-33140-hack-boundaries.stderr @@ -1,3 +1,11 @@ +error[E0119]: conflicting implementations of trait `Trait0` for type `(dyn Send + 'static)` + --> $DIR/issue-33140-hack-boundaries.rs:9:1 + | +LL | impl Trait0 for dyn Send {} + | ------------------------ first implementation here +LL | impl Trait0 for dyn Send {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + 'static)` + error[E0119]: conflicting implementations of trait `Trait1` for type `(dyn Send + 'static)` --> $DIR/issue-33140-hack-boundaries.rs:18:1 | @@ -62,19 +70,7 @@ LL | impl Trait5 for dyn Send {} LL | impl Trait5 for dyn Send where u32: Copy {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + 'static)` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0119, E0751. For more information about an error, try `rustc --explain E0119`. -Future incompatibility report: Future breakage diagnostic: -warning: conflicting implementations of trait `Trait0` for type `(dyn Send + 'static)`: (E0119) - --> $DIR/issue-33140-hack-boundaries.rs:10:1 - | -LL | impl Trait0 for dyn Send {} - | ------------------------ first implementation here -LL | impl Trait0 for dyn Send {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + 'static)` - | - = 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 #56484 - diff --git a/tests/ui/traits/issue-50480.stderr b/tests/ui/traits/issue-50480.stderr index d3c11238ede16..ed8c74e9bddcf 100644 --- a/tests/ui/traits/issue-50480.stderr +++ b/tests/ui/traits/issue-50480.stderr @@ -79,8 +79,6 @@ LL | struct Foo(N, NotDefined, ::Item, Vec, String); | -------- ------ this field does not implement `Copy` | | | this field does not implement `Copy` - | - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0204]: the trait `Copy` cannot be implemented for this type --> $DIR/issue-50480.rs:11:17 @@ -92,8 +90,6 @@ LL | struct Bar(T, N, NotDefined, ::Item, Vec, String); | -------- ------ this field does not implement `Copy` | | | this field does not implement `Copy` - | - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `i32` is not an iterator --> $DIR/issue-50480.rs:14:33 @@ -118,7 +114,6 @@ LL | #[derive(Clone, Copy)] | ^^^^^ `i32` is not an iterator | = help: the trait `Iterator` is not implemented for `i32` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `i32` is not an iterator --> $DIR/issue-50480.rs:14:33 @@ -130,7 +125,6 @@ LL | struct Bar(T, N, NotDefined, ::Item, Vec, String); | ^^^^^^^^^^^^^^^^^^^^^^^ `i32` is not an iterator | = help: the trait `Iterator` is not implemented for `i32` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 13 previous errors diff --git a/tests/ui/traits/issue-71136.stderr b/tests/ui/traits/issue-71136.stderr index 2c03c6bf08ed8..cd891c88c9f95 100644 --- a/tests/ui/traits/issue-71136.stderr +++ b/tests/ui/traits/issue-71136.stderr @@ -8,7 +8,6 @@ LL | the_foos: Vec, | ^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `Foo` | = note: required for `Vec` to implement `Clone` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Foo` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/traits/issue-79458.stderr b/tests/ui/traits/issue-79458.stderr index c80efbe928729..9e53ec8cc90d0 100644 --- a/tests/ui/traits/issue-79458.stderr +++ b/tests/ui/traits/issue-79458.stderr @@ -9,7 +9,6 @@ LL | bar: &'a mut T | = help: the trait `Clone` is implemented for `&T` = note: `Clone` is implemented for `&T`, but not for `&mut T` - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/traits/issue-85360-eval-obligation-ice.rs b/tests/ui/traits/issue-85360-eval-obligation-ice.rs index 931879a672262..f7c49049e2d33 100644 --- a/tests/ui/traits/issue-85360-eval-obligation-ice.rs +++ b/tests/ui/traits/issue-85360-eval-obligation-ice.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition=2021 +//@ edition: 2021 #![feature(rustc_attrs)] diff --git a/tests/ui/traits/lifetime-incomplete-prefer-sized-builtin-over-wc-2.rs b/tests/ui/traits/lifetime-incomplete-prefer-sized-builtin-over-wc-2.rs new file mode 100644 index 0000000000000..8a8f7b933b539 --- /dev/null +++ b/tests/ui/traits/lifetime-incomplete-prefer-sized-builtin-over-wc-2.rs @@ -0,0 +1,28 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Exercises change in . + +trait Trait: Sized {} +impl Trait for T {} + +fn is_sized() {} + +fn normal_ref<'a, 'b, T>() +where + &'a u32: Trait, +{ + is_sized::<&'b u32>(); +} + +struct MyRef<'a, U: ?Sized = ()>(&'a u32, U); +fn my_ref<'a, 'b, T>() +where + MyRef<'a>: Trait, +{ + is_sized::>(); +} + +fn main() {} diff --git a/tests/ui/traits/lifetime-incomplete-prefer-sized-builtin-over-wc.current.stderr b/tests/ui/traits/lifetime-incomplete-prefer-sized-builtin-over-wc.current.stderr new file mode 100644 index 0000000000000..dd9393fae853d --- /dev/null +++ b/tests/ui/traits/lifetime-incomplete-prefer-sized-builtin-over-wc.current.stderr @@ -0,0 +1,27 @@ +error[E0308]: mismatched types + --> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:13:23 + | +LL | (MyType<'a, T>,): Sized, + | ^^^^^ lifetime mismatch + | + = note: expected trait ` as Sized>` + found trait ` as Sized>` +note: the lifetime `'a` as defined here... + --> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:11:8 + | +LL | fn foo<'a, T: ?Sized>() + | ^^ + = note: ...does not necessarily outlive the static lifetime + +error: lifetime may not live long enough + --> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:22:5 + | +LL | fn foo<'a, T: ?Sized>() + | -- lifetime `'a` defined here +... +LL | is_sized::<(MyType<'a, T>,)>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/lifetime-incomplete-prefer-sized-builtin-over-wc.next.stderr b/tests/ui/traits/lifetime-incomplete-prefer-sized-builtin-over-wc.next.stderr new file mode 100644 index 0000000000000..05861877d413b --- /dev/null +++ b/tests/ui/traits/lifetime-incomplete-prefer-sized-builtin-over-wc.next.stderr @@ -0,0 +1,25 @@ +error[E0478]: lifetime bound not satisfied + --> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:13:23 + | +LL | (MyType<'a, T>,): Sized, + | ^^^^^ + | +note: lifetime parameter instantiated with the lifetime `'a` as defined here + --> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:11:8 + | +LL | fn foo<'a, T: ?Sized>() + | ^^ + = note: but lifetime parameter must outlive the static lifetime + +error: lifetime may not live long enough + --> $DIR/lifetime-incomplete-prefer-sized-builtin-over-wc.rs:22:5 + | +LL | fn foo<'a, T: ?Sized>() + | -- lifetime `'a` defined here +... +LL | is_sized::<(MyType<'a, T>,)>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0478`. diff --git a/tests/ui/traits/lifetime-incomplete-prefer-sized-builtin-over-wc.rs b/tests/ui/traits/lifetime-incomplete-prefer-sized-builtin-over-wc.rs new file mode 100644 index 0000000000000..ae7a6c9bba335 --- /dev/null +++ b/tests/ui/traits/lifetime-incomplete-prefer-sized-builtin-over-wc.rs @@ -0,0 +1,26 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Exercises change in . + +struct MyType<'a, T: ?Sized>(&'a (), T); + +fn is_sized() {} + +fn foo<'a, T: ?Sized>() +where + (MyType<'a, T>,): Sized, + //[current]~^ ERROR mismatched types + //[next]~^^ ERROR lifetime bound not satisfied + MyType<'static, T>: Sized, +{ + // Preferring the builtin `Sized` impl of tuples + // requires proving `MyType<'a, T>: Sized` which + // can only be proven by using the where-clause, + // adding an unnecessary `'static` constraint. + is_sized::<(MyType<'a, T>,)>(); + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/traits/mutual-recursion-issue-75860.stderr b/tests/ui/traits/mutual-recursion-issue-75860.stderr index 272c56301bc84..9e8eb1adb1111 100644 --- a/tests/ui/traits/mutual-recursion-issue-75860.stderr +++ b/tests/ui/traits/mutual-recursion-issue-75860.stderr @@ -2,7 +2,7 @@ error[E0275]: overflow assigning `_` to `Option<_>` --> $DIR/mutual-recursion-issue-75860.rs:9:33 | LL | let left = |o_a: Option<_>| o_a.unwrap(); - | ^^^ + | ^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/negative-impls/negated-auto-traits-validity-error.rs b/tests/ui/traits/negative-impls/negated-auto-traits-validity-error.rs new file mode 100644 index 0000000000000..cd675a5efd162 --- /dev/null +++ b/tests/ui/traits/negative-impls/negated-auto-traits-validity-error.rs @@ -0,0 +1,22 @@ +#![feature(auto_traits, negative_impls)] + +auto trait Foo {} + +struct AdditionalLt<'a, T>(&'a (), T); +impl<'a, T: 'a> !Foo for AdditionalLt<'a, T> {} +//~^ ERROR `!Foo` impl requires `T: 'a` but the struct it is implemented for does not + +struct AdditionalBound(T); +trait Bound {} +impl !Foo for AdditionalBound {} +//~^ ERROR `!Foo` impl requires `T: Bound` but the struct it is implemented for does not + +struct TwoParam(T, U); +impl !Foo for TwoParam {} +//~^ ERROR `!Foo` impls cannot be specialized + +struct ConcreteParam(T); +impl !Foo for ConcreteParam {} +//~^ ERROR `!Foo` impls cannot be specialized + +fn main() {} diff --git a/tests/ui/traits/negative-impls/negated-auto-traits-validity-error.stderr b/tests/ui/traits/negative-impls/negated-auto-traits-validity-error.stderr new file mode 100644 index 0000000000000..ef783e9015592 --- /dev/null +++ b/tests/ui/traits/negative-impls/negated-auto-traits-validity-error.stderr @@ -0,0 +1,54 @@ +error[E0367]: `!Foo` impl requires `T: 'a` but the struct it is implemented for does not + --> $DIR/negated-auto-traits-validity-error.rs:6:13 + | +LL | impl<'a, T: 'a> !Foo for AdditionalLt<'a, T> {} + | ^^ + | +note: the implementor must specify the same requirement + --> $DIR/negated-auto-traits-validity-error.rs:5:1 + | +LL | struct AdditionalLt<'a, T>(&'a (), T); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0367]: `!Foo` impl requires `T: Bound` but the struct it is implemented for does not + --> $DIR/negated-auto-traits-validity-error.rs:11:9 + | +LL | impl !Foo for AdditionalBound {} + | ^^^^^ + | +note: the implementor must specify the same requirement + --> $DIR/negated-auto-traits-validity-error.rs:9:1 + | +LL | struct AdditionalBound(T); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0366]: `!Foo` impls cannot be specialized + --> $DIR/negated-auto-traits-validity-error.rs:15:1 + | +LL | impl !Foo for TwoParam {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `T` is mentioned multiple times +note: use the same sequence of generic lifetime, type and const parameters as the struct definition + --> $DIR/negated-auto-traits-validity-error.rs:14:1 + | +LL | struct TwoParam(T, U); + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0366]: `!Foo` impls cannot be specialized + --> $DIR/negated-auto-traits-validity-error.rs:19:1 + | +LL | impl !Foo for ConcreteParam {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `i32` is not a generic parameter +note: use the same sequence of generic lifetime, type and const parameters as the struct definition + --> $DIR/negated-auto-traits-validity-error.rs:18:1 + | +LL | struct ConcreteParam(T); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0366, E0367. +For more information about an error, try `rustc --explain E0366`. diff --git a/tests/ui/traits/negative-impls/negated-auto-traits-validity.rs b/tests/ui/traits/negative-impls/negated-auto-traits-validity.rs new file mode 100644 index 0000000000000..76996b5593e60 --- /dev/null +++ b/tests/ui/traits/negative-impls/negated-auto-traits-validity.rs @@ -0,0 +1,22 @@ +//@ check-pass + +#![feature(auto_traits, negative_impls)] + +auto trait Foo {} +auto trait Bar {} + +struct NeedsOutlives<'a, T>(&'a T); + +impl<'a, T: 'a> !Foo for NeedsOutlives<'a, T> {} + +// Leaving out the lifetime bound +impl<'a, T> !Bar for NeedsOutlives<'a, T> {} + +struct NeedsSend(T); + +impl !Foo for NeedsSend {} + +// Leaving off the trait bound +impl !Bar for NeedsSend {} + +fn main() {} diff --git a/tests/ui/traits/next-solver/alias-relate/tait-eq-proj-2.rs b/tests/ui/traits/next-solver/alias-relate/tait-eq-proj-2.rs index cb9fe176ac958..92a0913dee284 100644 --- a/tests/ui/traits/next-solver/alias-relate/tait-eq-proj-2.rs +++ b/tests/ui/traits/next-solver/alias-relate/tait-eq-proj-2.rs @@ -12,7 +12,8 @@ fn mk() -> T { todo!() } -fn a(_: Tait) { +#[define_opaque(Tait)] +fn a() { let x: Tait = mk(); let mut array = mk(); let mut z = IntoIterator::into_iter(array); diff --git a/tests/ui/traits/next-solver/alias-relate/tait-eq-proj.rs b/tests/ui/traits/next-solver/alias-relate/tait-eq-proj.rs index 8d92c88ae7251..0e99ef87c89b6 100644 --- a/tests/ui/traits/next-solver/alias-relate/tait-eq-proj.rs +++ b/tests/ui/traits/next-solver/alias-relate/tait-eq-proj.rs @@ -28,7 +28,8 @@ goals together. Essentially: */ -fn a(_: Tait) { +#[define_opaque(Tait)] +fn a() { let _: Tait = IntoIterator::into_iter([0i32; 32]); } diff --git a/tests/ui/traits/next-solver/assembly/better_any-backcompat.rs b/tests/ui/traits/next-solver/assembly/better_any-backcompat.rs new file mode 100644 index 0000000000000..8b153833e3a7a --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/better_any-backcompat.rs @@ -0,0 +1,33 @@ +//@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +// A regression test for trait-system-refactor-initiative#183. While +// this concrete instance is likely not practically unsound, the general +// pattern is, see #57893. + +use std::any::TypeId; + +unsafe trait TidAble<'a>: Tid<'a> {} +trait TidExt<'a>: Tid<'a> { + fn downcast_box(self: Box) { + loop {} + } +} + +impl<'a, X: ?Sized + Tid<'a>> TidExt<'a> for X {} + +unsafe trait Tid<'a>: 'a {} + +unsafe impl<'a, T: ?Sized + TidAble<'a>> Tid<'a> for T {} + +impl<'a> dyn Tid<'a> + 'a { + fn downcast_any_box(self: Box) { + self.downcast_box(); + } +} + +unsafe impl<'a> TidAble<'a> for dyn Tid<'a> + 'a {} + +fn main() {} diff --git a/tests/ui/traits/next-solver/async.fail.stderr b/tests/ui/traits/next-solver/async.fail.stderr index bc89842d16a14..a76a10d20ee8b 100644 --- a/tests/ui/traits/next-solver/async.fail.stderr +++ b/tests/ui/traits/next-solver/async.fail.stderr @@ -1,8 +1,8 @@ -error[E0271]: expected `{async block@$DIR/async.rs:12:17: 12:22}` to be a future that resolves to `i32`, but it resolves to `()` +error[E0271]: type mismatch resolving `() == i32` --> $DIR/async.rs:12:17 | LL | needs_async(async {}); - | ----------- ^^^^^^^^ expected `i32`, found `()` + | ----------- ^^^^^^^^ types differ | | | required by a bound introduced by this call | diff --git a/tests/ui/traits/next-solver/async.rs b/tests/ui/traits/next-solver/async.rs index fded774354759..34c0ed02eeb12 100644 --- a/tests/ui/traits/next-solver/async.rs +++ b/tests/ui/traits/next-solver/async.rs @@ -10,7 +10,7 @@ fn needs_async(_: impl Future) {} #[cfg(fail)] fn main() { needs_async(async {}); - //[fail]~^ ERROR expected `{async block@$DIR/async.rs:12:17: 12:22}` to be a future that resolves to `i32`, but it resolves to `()` + //[fail]~^ ERROR type mismatch resolving `() == i32` } #[cfg(pass)] diff --git a/tests/ui/traits/next-solver/builtin-fn-must-return-sized.rs b/tests/ui/traits/next-solver/builtin-fn-must-return-sized.rs index f8926b24e3fab..fbe801aedcd8e 100644 --- a/tests/ui/traits/next-solver/builtin-fn-must-return-sized.rs +++ b/tests/ui/traits/next-solver/builtin-fn-must-return-sized.rs @@ -13,5 +13,5 @@ fn foo, T: Tuple>(f: Option, t: T) { fn main() { foo:: str, _>(None, ()); - //~^ the size for values of type `str` cannot be known at compilation time + //~^ ERROR the size for values of type `str` cannot be known at compilation time } diff --git a/tests/ui/traits/next-solver/coerce-ambig-alias-to-rigid-alias.rs b/tests/ui/traits/next-solver/coercion/coerce-ambig-alias-to-rigid-alias.rs similarity index 100% rename from tests/ui/traits/next-solver/coerce-ambig-alias-to-rigid-alias.rs rename to tests/ui/traits/next-solver/coercion/coerce-ambig-alias-to-rigid-alias.rs diff --git a/tests/ui/traits/next-solver/coercion/coerce-depth.rs b/tests/ui/traits/next-solver/coercion/coerce-depth.rs new file mode 100644 index 0000000000000..c8fc3fcab59d8 --- /dev/null +++ b/tests/ui/traits/next-solver/coercion/coerce-depth.rs @@ -0,0 +1,31 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// Ensure that a stack of coerce predicates doesn't end up overflowing when they get procesed +// in *reverse* order, which may require O(N) iterations of the fulfillment loop. + +#![recursion_limit = "16"] + +fn main() { + match 0 { + 0 => None, + 1 => None, + 2 => None, + 3 => None, + 4 => None, + 5 => None, + 6 => None, + 7 => None, + 8 => None, + 9 => None, + 10 => None, + 11 => None, + 12 => None, + 13 => None, + 14 => None, + 15 => None, + 16 => None, + 17 => None, + _ => Some(1u32), + }; +} diff --git a/tests/ui/traits/next-solver/coercion/fn-def-coerce-nested-obligations.rs b/tests/ui/traits/next-solver/coercion/fn-def-coerce-nested-obligations.rs new file mode 100644 index 0000000000000..1b5abcb02f2f8 --- /dev/null +++ b/tests/ui/traits/next-solver/coercion/fn-def-coerce-nested-obligations.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Make sure that we consider nested obligations when checking whether +// we should coerce fn definitions to function pointers. + +fn foo() {} +fn bar() {} +fn main() { + let _ = if true { foo::<{ 0 + 0 }> } else { foo::<1> }; + let _ = if true { + bar:: fn( as IntoIterator>::Item)> + } else { + bar:: + }; +} diff --git a/tests/ui/traits/next-solver/non-wf-in-coerce-pointers.rs b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.rs similarity index 100% rename from tests/ui/traits/next-solver/non-wf-in-coerce-pointers.rs rename to tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.rs diff --git a/tests/ui/traits/next-solver/non-wf-in-coerce-pointers.stderr b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr similarity index 90% rename from tests/ui/traits/next-solver/non-wf-in-coerce-pointers.stderr rename to tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr index 32a7766a638da..72be10367dac9 100644 --- a/tests/ui/traits/next-solver/non-wf-in-coerce-pointers.stderr +++ b/tests/ui/traits/next-solver/coercion/non-wf-in-coerce-pointers.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `(): Wf` is not satisfied - --> $DIR/non-wf-in-coerce-pointers.rs:8:17 + --> $DIR/non-wf-in-coerce-pointers.rs:8:8 | LL | f: &'static <() as Wf>::Assoc, - | ^^^^^^^^^^^^^^^^^ the trait `Wf` is not implemented for `()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Wf` is not implemented for `()` | help: this trait has no implementations, consider adding one --> $DIR/non-wf-in-coerce-pointers.rs:3:1 diff --git a/tests/ui/traits/next-solver/trait-upcast-lhs-needs-normalization.rs b/tests/ui/traits/next-solver/coercion/trait-upcast-lhs-needs-normalization.rs similarity index 100% rename from tests/ui/traits/next-solver/trait-upcast-lhs-needs-normalization.rs rename to tests/ui/traits/next-solver/coercion/trait-upcast-lhs-needs-normalization.rs diff --git a/tests/ui/traits/next-solver/upcast-right-substs.rs b/tests/ui/traits/next-solver/coercion/upcast-right-substs.rs similarity index 100% rename from tests/ui/traits/next-solver/upcast-right-substs.rs rename to tests/ui/traits/next-solver/coercion/upcast-right-substs.rs diff --git a/tests/ui/traits/next-solver/upcast-wrong-substs.rs b/tests/ui/traits/next-solver/coercion/upcast-wrong-substs.rs similarity index 100% rename from tests/ui/traits/next-solver/upcast-wrong-substs.rs rename to tests/ui/traits/next-solver/coercion/upcast-wrong-substs.rs diff --git a/tests/ui/traits/next-solver/upcast-wrong-substs.stderr b/tests/ui/traits/next-solver/coercion/upcast-wrong-substs.stderr similarity index 100% rename from tests/ui/traits/next-solver/upcast-wrong-substs.stderr rename to tests/ui/traits/next-solver/coercion/upcast-wrong-substs.stderr diff --git a/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.rs b/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.rs index 3238f02836283..28fd66cd1694a 100644 --- a/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.rs +++ b/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.rs @@ -6,9 +6,11 @@ trait Overflow { type Assoc; } -impl Overflow for T { - type Assoc = ::Assoc; - //~^ ERROR: overflow +impl Overflow for T +where + (T,): Overflow +{ + type Assoc = <(T,) as Overflow>::Assoc; } diff --git a/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr b/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr index 294fa0d7613c5..34a45e9363069 100644 --- a/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr +++ b/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr @@ -1,19 +1,15 @@ -error[E0275]: overflow evaluating the requirement `::Assoc == _` - --> $DIR/trait_ref_is_knowable-norm-overflow.rs:10:18 - | -LL | type Assoc = ::Assoc; - | ^^^^^^^^^^^^^^^^^^^^^^ - error[E0119]: conflicting implementations of trait `Trait` - --> $DIR/trait_ref_is_knowable-norm-overflow.rs:18:1 + --> $DIR/trait_ref_is_knowable-norm-overflow.rs:20:1 | LL | impl Trait for T {} | ------------------------- first implementation here LL | struct LocalTy; LL | impl Trait for ::Assoc {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation + | + = note: overflow evaluating the requirement `_ == ::Assoc` + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`trait_ref_is_knowable_norm_overflow`) -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0119, E0275. -For more information about an error, try `rustc --explain E0119`. +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/next-solver/cycles/cyclic-normalization-to-error-nalgebra.rs b/tests/ui/traits/next-solver/cycles/cyclic-normalization-to-error-nalgebra.rs new file mode 100644 index 0000000000000..ec478aa02b770 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/cyclic-normalization-to-error-nalgebra.rs @@ -0,0 +1,21 @@ +// Regression test for trait-system-refactor-initiative#114. +// +// We previously treated the cycle when trying to use the +// `>::Output: DimMin` where-bound when +// normalizing `>::Output` as ambiguous, causing +// this to error. + +//@ check-pass +//@ compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver + +pub trait DimMin { + type Output; +} +pub fn repro, C>() +where + >::Output: DimMin>::Output>, +{ +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.rs b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.rs index b0c778e7f5742..754fc872e457c 100644 --- a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.rs +++ b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.rs @@ -34,9 +34,17 @@ where MultipleNested: Trait, {} +// We ignore the trivially true global where-bounds when checking that this +// impl is well-formed, meaning that we depend on `MultipleNested: Trait` when +// recursively proving `MultipleCandidates: Trait`. +// +// These overflow errors will disappear once we treat these cycles as either +// productive or an error. impl Trait for MultipleNested +//~^ ERROR overflow evaluating the requirement `MultipleNested: Trait` where MultipleCandidates: Trait, + //~^ ERROR overflow evaluating the requirement `MultipleCandidates: Trait` DoesNotImpl: Trait, {} diff --git a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.stderr b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.stderr index acacaf6a331d9..7895a2636345a 100644 --- a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.stderr +++ b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.stderr @@ -1,16 +1,29 @@ +error[E0275]: overflow evaluating the requirement `MultipleNested: Trait` + --> $DIR/inductive-cycle-but-err.rs:43:16 + | +LL | impl Trait for MultipleNested + | ^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating the requirement `MultipleCandidates: Trait` + --> $DIR/inductive-cycle-but-err.rs:46:25 + | +LL | MultipleCandidates: Trait, + | ^^^^^ + error[E0277]: the trait bound `MultipleCandidates: Trait` is not satisfied - --> $DIR/inductive-cycle-but-err.rs:46:19 + --> $DIR/inductive-cycle-but-err.rs:54:19 | LL | impls_trait::(); | ^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `MultipleCandidates` | = help: the trait `Trait` is implemented for `MultipleCandidates` note: required by a bound in `impls_trait` - --> $DIR/inductive-cycle-but-err.rs:43:19 + --> $DIR/inductive-cycle-but-err.rs:51:19 | LL | fn impls_trait() {} | ^^^^^ required by this bound in `impls_trait` -error: aborting due to 1 previous error +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0275, E0277. +For more information about an error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive-2.rs b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive-2.rs new file mode 100644 index 0000000000000..bb3540f9a214f --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive-2.rs @@ -0,0 +1,24 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#176. +// +// Normalizing ` as IntoIterator>::IntoIter` has two candidates +// inside of the function: +// - `impl IntoIterator for Vec` which trivially applies +// - `impl IntoIterator for I` +// - requires `Vec: Iterator` +// - where-clause requires ` as IntoIterator>::IntoIter eq Vec` +// - normalize ` as IntoIterator>::IntoIter` again, cycle +// +// We need to treat this cycle as an error to be able to use the actual impl. + +fn test() +where + as IntoIterator>::IntoIter: Iterator, +{ +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.current.stderr b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.current.stderr new file mode 100644 index 0000000000000..0a5b90f3d12bf --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.current.stderr @@ -0,0 +1,48 @@ +error[E0277]: the trait bound `Foo: Bound` is not satisfied + --> $DIR/normalizes-to-is-not-productive.rs:41:1 + | +LL | / fn generic() +LL | | where +LL | | >::Assoc: Bound, + | |____________________________________^ the trait `Bound` is not implemented for `Foo` + | + = help: the trait `Bound` is implemented for `u32` +note: required for `Foo` to implement `Trait` + --> $DIR/normalizes-to-is-not-productive.rs:24:19 + | +LL | impl Trait for T { + | ----- ^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error[E0277]: the trait bound `Foo: Bound` is not satisfied + --> $DIR/normalizes-to-is-not-productive.rs:41:4 + | +LL | fn generic() + | ^^^^^^^ the trait `Bound` is not implemented for `Foo` + | + = help: the trait `Bound` is implemented for `u32` +note: required for `Foo` to implement `Trait` + --> $DIR/normalizes-to-is-not-productive.rs:24:19 + | +LL | impl Trait for T { + | ----- ^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error[E0277]: the trait bound `Foo: Bound` is not satisfied + --> $DIR/normalizes-to-is-not-productive.rs:48:19 + | +LL | impls_bound::(); + | ^^^ the trait `Bound` is not implemented for `Foo` + | + = help: the trait `Bound` is implemented for `u32` +note: required by a bound in `impls_bound` + --> $DIR/normalizes-to-is-not-productive.rs:28:19 + | +LL | fn impls_bound() { + | ^^^^^ required by this bound in `impls_bound` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.next.stderr b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.next.stderr new file mode 100644 index 0000000000000..d888fbf9db183 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.next.stderr @@ -0,0 +1,31 @@ +error[E0277]: the trait bound `Foo: Bound` is not satisfied + --> $DIR/normalizes-to-is-not-productive.rs:43:31 + | +LL | >::Assoc: Bound, + | ^^^^^ the trait `Bound` is not implemented for `Foo` + | + = help: the trait `Bound` is implemented for `u32` +note: required for `Foo` to implement `Trait` + --> $DIR/normalizes-to-is-not-productive.rs:24:19 + | +LL | impl Trait for T { + | ----- ^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error[E0277]: the trait bound `Foo: Bound` is not satisfied + --> $DIR/normalizes-to-is-not-productive.rs:48:19 + | +LL | impls_bound::(); + | ^^^ the trait `Bound` is not implemented for `Foo` + | + = help: the trait `Bound` is implemented for `u32` +note: required by a bound in `impls_bound` + --> $DIR/normalizes-to-is-not-productive.rs:28:19 + | +LL | fn impls_bound() { + | ^^^^^ required by this bound in `impls_bound` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs new file mode 100644 index 0000000000000..ffbbecaf89570 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs @@ -0,0 +1,54 @@ +//@ ignore-compare-mode-next-solver (explicit) +//@ compile-flags: -Znext-solver + +// Make sure that stepping into impl where-clauses of `NormalizesTo` +// goals is unproductive. This must not compile, see the inline +// comments. + +trait Bound { + fn method(); +} +impl Bound for u32 { + fn method() {} +} +trait Trait { + type Assoc: Bound; +} + +struct Foo; + +impl Trait for Foo { + type Assoc = u32; +} +impl Trait for T { + type Assoc = T; +} + +fn impls_bound() { + T::method(); +} + +// The where-clause requires `Foo: Trait` to hold to be wf. +// If stepping into where-clauses during normalization is considered +// to be productive, this would be the case: +// +// - `Foo: Trait` +// - via blanket impls, requires `Foo: Bound` +// - via where-bound, requires `Foo eq >::Assoc` +// - normalize `>::Assoc` +// - via blanket impl, requires where-clause `Foo: Bound` -> cycle +fn generic() +where + >::Assoc: Bound, + //~^ ERROR the trait bound `Foo: Bound` is not satisfied +{ + // Requires proving `Foo: Bound` by normalizing + // `>::Assoc` to `Foo`. + impls_bound::(); + //~^ ERROR the trait bound `Foo: Bound` is not satisfied +} +fn main() { + // Requires proving `>::Assoc: Bound`. + // This is trivially true. + generic::(); +} diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr new file mode 100644 index 0000000000000..8901805a20f5f --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr @@ -0,0 +1,31 @@ +error[E0277]: the trait bound `Foo: Bound` is not satisfied + --> $DIR/normalizes-to-is-not-productive.rs:42:31 + | +LL | >::Assoc: Bound, + | ^^^^^ the trait `Bound` is not implemented for `Foo` + | + = help: the trait `Bound` is implemented for `u32` +note: required for `Foo` to implement `Trait` + --> $DIR/normalizes-to-is-not-productive.rs:23:19 + | +LL | impl Trait for T { + | ----- ^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error[E0277]: the trait bound `Foo: Bound` is not satisfied + --> $DIR/normalizes-to-is-not-productive.rs:47:19 + | +LL | impls_bound::(); + | ^^^ the trait `Bound` is not implemented for `Foo` + | + = help: the trait `Bound` is implemented for `u32` +note: required by a bound in `impls_bound` + --> $DIR/normalizes-to-is-not-productive.rs:27:19 + | +LL | fn impls_bound() { + | ^^^^^ required by this bound in `impls_bound` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/cycles/unproductive-in-coherence.rs b/tests/ui/traits/next-solver/cycles/unproductive-in-coherence.rs new file mode 100644 index 0000000000000..46dd6adf66225 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/unproductive-in-coherence.rs @@ -0,0 +1,21 @@ +// If we treat known inductive cycles as errors, this test compiles +// as normalizing `Overflow::Assoc` fails. +// +// As coherence already uses the new solver on stable, this change +// would require an FCP. + +trait Trait { + type Assoc; +} + +struct Overflow; +impl Trait for Overflow { + type Assoc = ::Assoc; +} + +trait Overlap {} +impl Overlap, U> for T {} +impl Overlap for Overflow {} +//~^ ERROR conflicting implementations of trait `Overlap<::Assoc, _>` for type `Overflow` + +fn main() {} diff --git a/tests/ui/traits/next-solver/cycles/unproductive-in-coherence.stderr b/tests/ui/traits/next-solver/cycles/unproductive-in-coherence.stderr new file mode 100644 index 0000000000000..6605a28d54771 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/unproductive-in-coherence.stderr @@ -0,0 +1,11 @@ +error[E0119]: conflicting implementations of trait `Overlap<::Assoc, _>` for type `Overflow` + --> $DIR/unproductive-in-coherence.rs:18:1 + | +LL | impl Overlap, U> for T {} + | ----------------------------------------------------- first implementation here +LL | impl Overlap for Overflow {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Overflow` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/next-solver/diagnostics/point-at-failing-nested.rs b/tests/ui/traits/next-solver/diagnostics/point-at-failing-nested.rs index 840a4eb648c59..6da08619b47ae 100644 --- a/tests/ui/traits/next-solver/diagnostics/point-at-failing-nested.rs +++ b/tests/ui/traits/next-solver/diagnostics/point-at-failing-nested.rs @@ -20,5 +20,5 @@ impl Constrain for () { fn needs_foo() {} fn main() { needs_foo::<()>(); - //~^ the trait bound `(): Foo` is not satisfied + //~^ ERROR the trait bound `(): Foo` is not satisfied } diff --git a/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.stderr b/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.stderr index 2d42fedae4381..e1ae981b2492c 100644 --- a/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.stderr +++ b/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied - --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:7:11 + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:7:22 | LL | fn foo(x: for<'a> fn(<() as Trait<'a>>::Assoc)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Trait<'a>` is not implemented for `()` | help: this trait has no implementations, consider adding one --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 diff --git a/tests/ui/traits/next-solver/gat-wf.rs b/tests/ui/traits/next-solver/gat-wf.rs index ff6e2665ef3ee..cd4b96b3a5832 100644 --- a/tests/ui/traits/next-solver/gat-wf.rs +++ b/tests/ui/traits/next-solver/gat-wf.rs @@ -10,7 +10,7 @@ trait Foo { } impl Foo for &() { - type T<'a> = (); //~ the type `&()` does not fulfill the required lifetime + type T<'a> = (); //~ ERROR the type `&()` does not fulfill the required lifetime } fn main() {} diff --git a/tests/ui/traits/next-solver/global-cache-and-parallel-frontend.stderr b/tests/ui/traits/next-solver/global-cache-and-parallel-frontend.stderr index da269bbeae4c6..290b1df1665d3 100644 --- a/tests/ui/traits/next-solver/global-cache-and-parallel-frontend.stderr +++ b/tests/ui/traits/next-solver/global-cache-and-parallel-frontend.stderr @@ -13,7 +13,6 @@ LL | impl PartialEq for Struct | unsatisfied trait bound introduced here note: required by a bound in `Eq` --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` with trait `Clone` | LL | pub struct Struct(T); diff --git a/tests/ui/traits/next-solver/global-param-env-after-norm.rs b/tests/ui/traits/next-solver/global-param-env-after-norm.rs new file mode 100644 index 0000000000000..0d098db67d36d --- /dev/null +++ b/tests/ui/traits/next-solver/global-param-env-after-norm.rs @@ -0,0 +1,15 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +struct NewSolver; +struct OldSolver; + +fn foo() +where + T: Iterator, + OldSolver: Into, +{ + let x: OldSolver = OldSolver.into(); +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/issue-118950-root-region.rs b/tests/ui/traits/next-solver/issue-118950-root-region.rs index 8fe53d6773b95..fe336766891da 100644 --- a/tests/ui/traits/next-solver/issue-118950-root-region.rs +++ b/tests/ui/traits/next-solver/issue-118950-root-region.rs @@ -19,5 +19,6 @@ impl Overlap for T {} impl Overlap fn(Assoc<'a, T>)> for T where Missing: Overlap {} //~^ ERROR cannot find type `Missing` in this scope //~| ERROR the trait bound `T: Overlap fn(Assoc<'a, T>)>` is not satisfied +//~| ERROR the trait bound `for<'a> *const T: ToUnit<'a>` is not satisfied fn main() {} diff --git a/tests/ui/traits/next-solver/issue-118950-root-region.stderr b/tests/ui/traits/next-solver/issue-118950-root-region.stderr index d2a58e95629a4..45fa33dff52f6 100644 --- a/tests/ui/traits/next-solver/issue-118950-root-region.stderr +++ b/tests/ui/traits/next-solver/issue-118950-root-region.stderr @@ -26,6 +26,18 @@ LL | trait ToUnit<'a> { | ^^^^^^^^^^^^^^^^ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), .. } +error[E0277]: the trait bound `for<'a> *const T: ToUnit<'a>` is not satisfied + --> $DIR/issue-118950-root-region.rs:19:9 + | +LL | impl Overlap fn(Assoc<'a, T>)> for T where Missing: Overlap {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `*const T` + | +help: this trait has no implementations, consider adding one + --> $DIR/issue-118950-root-region.rs:8:1 + | +LL | trait ToUnit<'a> { + | ^^^^^^^^^^^^^^^^ + error[E0277]: the trait bound `T: Overlap fn(Assoc<'a, T>)>` is not satisfied --> $DIR/issue-118950-root-region.rs:19:47 | @@ -37,7 +49,7 @@ help: consider further restricting type parameter `T` with trait `Overlap` LL | impl Overlap fn(Assoc<'a, T>)> for T where Missing: Overlap, T: Overlap fn(Assoc<'a, T>)> {} | ++++++++++++++++++++++++++++++++++++++ -error: aborting due to 3 previous errors; 1 warning emitted +error: aborting due to 4 previous errors; 1 warning emitted Some errors have detailed explanations: E0277, E0412. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/method/param-method-from-unnormalized-param-env-2.rs b/tests/ui/traits/next-solver/method/param-method-from-unnormalized-param-env-2.rs new file mode 100644 index 0000000000000..ffb99d6d63855 --- /dev/null +++ b/tests/ui/traits/next-solver/method/param-method-from-unnormalized-param-env-2.rs @@ -0,0 +1,29 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for . +// See comment below. + +trait A { + fn hello(&self) {} +} + +trait B { + fn hello(&self) {} +} + +impl A for T {} +impl B for T {} + +fn test(q: F::Item) +where + F: Iterator, + // We want to prefer `A` for `R.hello()` + F::Item: A, +{ + q.hello(); +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/method/param-method-from-unnormalized-param-env.rs b/tests/ui/traits/next-solver/method/param-method-from-unnormalized-param-env.rs new file mode 100644 index 0000000000000..dde4f745879e4 --- /dev/null +++ b/tests/ui/traits/next-solver/method/param-method-from-unnormalized-param-env.rs @@ -0,0 +1,17 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// Regression test for . + +fn execute(q: F::Item) -> R +where + F: Iterator, + // Both of the below bounds should be considered for `.into()`, and then be combined + // into a single `R: Into` bound which can be inferred to `?0 = R`. + F::Item: Into, + R: Into, +{ + q.into() +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/more-object-bound.rs b/tests/ui/traits/next-solver/more-object-bound.rs index 3d3fdc926f658..1dad1903a649d 100644 --- a/tests/ui/traits/next-solver/more-object-bound.rs +++ b/tests/ui/traits/next-solver/more-object-bound.rs @@ -10,7 +10,7 @@ trait Trait: SuperTrait::B> {} fn transmute(x: A) -> B { foo::>(x) - //~^ ERROR type mismatch resolving ` as SuperTrait>::A == B` + //~^ ERROR type mismatch resolving `A == B` } fn foo(x: T::A) -> B diff --git a/tests/ui/traits/next-solver/more-object-bound.stderr b/tests/ui/traits/next-solver/more-object-bound.stderr index 39849d4c865e9..d04376cc9c643 100644 --- a/tests/ui/traits/next-solver/more-object-bound.stderr +++ b/tests/ui/traits/next-solver/more-object-bound.stderr @@ -1,17 +1,9 @@ -error[E0271]: type mismatch resolving ` as SuperTrait>::A == B` +error[E0271]: type mismatch resolving `A == B` --> $DIR/more-object-bound.rs:12:5 | -LL | fn transmute(x: A) -> B { - | - - expected type parameter - | | - | found type parameter LL | foo::>(x) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `B`, found type parameter `A` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ | - = note: expected type parameter `B` - found type parameter `A` - = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters = note: required because it appears within the type `dyn Trait` note: required by a bound in `foo` --> $DIR/more-object-bound.rs:18:8 diff --git a/tests/ui/traits/next-solver/no-param-env-const-fold.rs b/tests/ui/traits/next-solver/no-param-env-const-fold.rs new file mode 100644 index 0000000000000..4f47332dd23ae --- /dev/null +++ b/tests/ui/traits/next-solver/no-param-env-const-fold.rs @@ -0,0 +1,10 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// Regression test for . + +use std::ops::Deref; + +trait Trait: Deref {} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/eager-norm-pre-normalizes-to.rs b/tests/ui/traits/next-solver/normalize/eager-norm-pre-normalizes-to.rs new file mode 100644 index 0000000000000..ea18ac54c0557 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/eager-norm-pre-normalizes-to.rs @@ -0,0 +1,44 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// A regression test for trait-system-refactor-initiative#184. +// +// When adding nested goals we replace aliases with infer vars +// and add `AliasRelate` goals to constrain them. When doing this +// for `NormalizesTo` goals, we then first tries to prove the +// `NormalizesTo` goal and then normalized the nested aliases. + +trait Trait { + type Assoc; +} +impl Trait for T { + type Assoc = (); +} + +trait Id { + type This; +} +impl Id for T { + type This = T; +} +trait Relate { + type Alias; +} +impl Relate for T { + type Alias = ::This>>::Assoc; +} + + +fn guide_me>() { + // Normalizing `>::Alias` relates the associated type with an unconstrained + // term. This resulted in a `NormalizesTo(::This>>::Assoc, ?x)` goal. + // We replace `::This` with an infer var `?y`, resulting in the following goals: + // - `NormalizesTo(::Assoc, ?x)` + // - `AliasRelate(::This, ?y)` + // + // When proving the `NormalizesTo` goal first, we incompletely constrain `?y` to `u32`, + // causing an unexpected type mismatch. + let _: >::Alias; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs b/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs index 5284220ac38fe..3150d9a88d0e8 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs +++ b/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs @@ -1,4 +1,5 @@ //@ check-pass +//@ compile-flags: -Znext-solver // When canonicalizing a response in the trait solver, we bail with overflow // if there are too many non-region inference variables. Doing so in normalizes-to diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr index 74a0a90885da5..d179c80596238 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr +++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr @@ -4,14 +4,14 @@ error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A` LL | Self::Assoc: A, | ^^^^ | -note: the requirement `<() as A>::Assoc: A` appears on the `impl`'s method `f` but not on the corresponding trait's method +note: the requirement `<() as A>::Assoc: A` appears on the `impl`'s associated function `f` but not on the corresponding trait's associated function --> $DIR/normalize-param-env-2.rs:12:8 | LL | trait A { | - in this trait ... LL | fn f() - | ^ this trait's method doesn't have the requirement `<() as A>::Assoc: A` + | ^ this trait's associated function doesn't have the requirement `<() as A>::Assoc: A` error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A` --> $DIR/normalize-param-env-2.rs:24:22 diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr index e91a48f62aec3..f5fd9ce9864ce 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr +++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr @@ -4,24 +4,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc: Trait` LL | ::Assoc: Trait, | ^^^^^ -error[E0275]: overflow evaluating the requirement `::Assoc well-formed` - --> $DIR/normalize-param-env-4.rs:19:26 - | -LL | ::Assoc: Trait, - | ^^^^^ - -error[E0275]: overflow evaluating the requirement `T: Trait` - --> $DIR/normalize-param-env-4.rs:32:19 - | -LL | impls_trait::(); - | ^ - | -note: required by a bound in `impls_trait` - --> $DIR/normalize-param-env-4.rs:15:19 - | -LL | fn impls_trait() {} - | ^^^^^ required by this bound in `impls_trait` - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/object-soundness-requires-generalization.rs b/tests/ui/traits/next-solver/object-soundness-requires-generalization.rs index 11a2617ad4278..3ef6bea4fd5c9 100644 --- a/tests/ui/traits/next-solver/object-soundness-requires-generalization.rs +++ b/tests/ui/traits/next-solver/object-soundness-requires-generalization.rs @@ -1,5 +1,5 @@ //@ compile-flags: -Znext-solver -//@ ignore-test +//@ ignore-test (see #114196) trait Trait { type Gat<'lt>; diff --git a/tests/ui/traits/next-solver/opaques/ambig-in-mir-typeck.rs b/tests/ui/traits/next-solver/opaques/ambig-in-mir-typeck.rs new file mode 100644 index 0000000000000..198e6199e92c6 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/ambig-in-mir-typeck.rs @@ -0,0 +1,17 @@ +// Regression test for #132335. This previously ICE'd due to ambiguity +// in MIR typeck. + +//@ compile-flags: -Znext-solver=globally --crate-type lib +//@ edition: 2018 +//@ check-pass +use core::future::Future; +use core::pin::Pin; + +pub trait Unit {} +impl Unit for () {} + +pub fn get_all_files_in_dir() -> Pin>> { + Box::pin(async { + get_all_files_in_dir().await; + }) +} diff --git a/tests/ui/traits/next-solver/opaques/dont-remap-tait-substs.rs b/tests/ui/traits/next-solver/opaques/dont-remap-tait-substs.rs index 904bc17949590..38fcc561b32d7 100644 --- a/tests/ui/traits/next-solver/opaques/dont-remap-tait-substs.rs +++ b/tests/ui/traits/next-solver/opaques/dont-remap-tait-substs.rs @@ -12,7 +12,8 @@ type Foo = impl NeedsSend; trait NeedsSend {} impl NeedsSend for T {} -fn define(a: A, b: B, _: Foo) { +#[define_opaque(Foo)] +fn define(a: A, b: B) { let y: Option> = Some(b); } diff --git a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr index 158fefd1538f3..736c8c10da97b 100644 --- a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr +++ b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr @@ -1,5 +1,5 @@ error[E0284]: type annotations needed: cannot satisfy `Foo == _` - --> $DIR/dont-type_of-tait-in-defining-scope.rs:15:18 + --> $DIR/dont-type_of-tait-in-defining-scope.rs:16:18 | LL | needs_send::(); | ^^^ cannot satisfy `Foo == _` diff --git a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr index 158fefd1538f3..736c8c10da97b 100644 --- a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr +++ b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr @@ -1,5 +1,5 @@ error[E0284]: type annotations needed: cannot satisfy `Foo == _` - --> $DIR/dont-type_of-tait-in-defining-scope.rs:15:18 + --> $DIR/dont-type_of-tait-in-defining-scope.rs:16:18 | LL | needs_send::(); | ^^^ cannot satisfy `Foo == _` diff --git a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs index 10b746cc9895e..2a08a3b2b9481 100644 --- a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs +++ b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs @@ -11,12 +11,14 @@ type Foo = impl Sized; fn needs_send() {} +#[define_opaque(Foo)] fn test(_: Foo) { needs_send::(); //~^ ERROR type annotations needed: cannot satisfy `Foo == _` } -fn defines(_: Foo) { +#[define_opaque(Foo)] +fn defines() { let _: Foo = (); } diff --git a/tests/ui/traits/next-solver/opaques/duplicate-opaque-type-entries.rs b/tests/ui/traits/next-solver/opaques/duplicate-opaque-type-entries.rs new file mode 100644 index 0000000000000..e0668ac3d39f4 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/duplicate-opaque-type-entries.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass +#![crate_type = "lib"] +trait Eq {} +impl Eq for T {} +trait ConstrainAndEq {} +impl ConstrainAndEq for U +where + T: FnOnce() -> u32, + U: FnOnce() -> u32, + T: Eq, +{} + +fn constrain_and_eq, U>(_: T, _: U) {} +fn foo<'a>() -> impl Sized + use<'a> { + // This proves `foo<'a>: FnOnce() -> u32` and `foo<'1>: FnOnce() -> u32`, + // We constrain both `opaque<'a>` and `opaque<'1>` to `u32`, resulting in + // two distinct opaque type uses. Proving `foo<'a>: Eq>` then + // equates the two regions at which point the two opaque type keys are now + // equal. This previously caused an ICE. + constrain_and_eq(foo::<'a>, foo::<'_>); + 1u32 +} diff --git a/tests/ui/traits/next-solver/opaques/no-define-in-wf-check.current.stderr b/tests/ui/traits/next-solver/opaques/no-define-in-wf-check.current.stderr index 9a28dc093c1be..ff0afd319d90d 100644 --- a/tests/ui/traits/next-solver/opaques/no-define-in-wf-check.current.stderr +++ b/tests/ui/traits/next-solver/opaques/no-define-in-wf-check.current.stderr @@ -1,34 +1,54 @@ -error: unconstrained opaque type +error: item does not constrain `ex1::Tait1::{opaque#0}` + --> $DIR/no-define-in-wf-check.rs:21:8 + | +LL | fn foo(x: Tait1) -> impl Sized { + | ^^^ + | + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/no-define-in-wf-check.rs:19:18 | LL | type Tait1 = impl Sized; | ^^^^^^^^^^ - | - = note: `Tait1` must be used in combination with a concrete type within the same module -error: unconstrained opaque type - --> $DIR/no-define-in-wf-check.rs:27:18 +error: item does not constrain `ex2::Tait1::{opaque#0}` + --> $DIR/no-define-in-wf-check.rs:31:8 + | +LL | fn foo(x: Tait1) -> Tait2 { + | ^^^ + | + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/no-define-in-wf-check.rs:28:18 | LL | type Tait1 = impl Sized; | ^^^^^^^^^^ - | - = note: `Tait1` must be used in combination with a concrete type within the same module -error: unconstrained opaque type - --> $DIR/no-define-in-wf-check.rs:36:18 +error: item does not constrain `ex3::Tait1::{opaque#0}` + --> $DIR/no-define-in-wf-check.rs:43:8 + | +LL | fn foo(x: Tait1) -> Tait2 { + | ^^^ + | + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/no-define-in-wf-check.rs:38:18 | LL | type Tait1 = impl Sized; | ^^^^^^^^^^ - | - = note: `Tait1` must be used in combination with a concrete type within the same module -error: unconstrained opaque type - --> $DIR/no-define-in-wf-check.rs:47:18 +error: item does not constrain `ex4::Tait1::{opaque#0}` + --> $DIR/no-define-in-wf-check.rs:64:8 + | +LL | fn foo(x: Tait1) -> Tait2 { + | ^^^ + | + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/no-define-in-wf-check.rs:50:18 | LL | type Tait1 = impl Sized; | ^^^^^^^^^^ - | - = note: `Tait1` must be used in combination with a concrete type within the same module error: aborting due to 4 previous errors diff --git a/tests/ui/traits/next-solver/opaques/no-define-in-wf-check.rs b/tests/ui/traits/next-solver/opaques/no-define-in-wf-check.rs index dd6df097da1bb..26c17edeb93bb 100644 --- a/tests/ui/traits/next-solver/opaques/no-define-in-wf-check.rs +++ b/tests/ui/traits/next-solver/opaques/no-define-in-wf-check.rs @@ -17,35 +17,37 @@ mod ex0 { } mod ex1 { type Tait1 = impl Sized; - //[current]~^ ERROR unconstrained opaque type + #[define_opaque(Tait1)] fn foo(x: Tait1) -> impl Sized { + //[current]~^ ERROR item does not constrain `ex1::Tait1::{opaque#0}` let () = x; } } mod ex2 { type Tait1 = impl Sized; - //[current]~^ ERROR unconstrained opaque type type Tait2 = impl Sized; + #[define_opaque(Tait1, Tait2)] fn foo(x: Tait1) -> Tait2 { + //[current]~^ ERROR item does not constrain `ex2::Tait1::{opaque#0}` let () = x; } } mod ex3 { type Tait1 = impl Sized; - //[current]~^ ERROR unconstrained opaque type trait Something {} impl Something for T {} type Tait2 = impl Something; + #[define_opaque(Tait1, Tait2)] fn foo(x: Tait1) -> Tait2 { + //[current]~^ ERROR item does not constrain `ex3::Tait1::{opaque#0}` let () = x; } } mod ex4 { type Tait1 = impl Sized; - //[current]~^ ERROR unconstrained opaque type trait Trait { type Assoc; } @@ -58,7 +60,9 @@ mod ex4 { // // ambiguity proving `(): Trait`. type Tait2 = impl Trait<(), Assoc = impl Trait>; + #[define_opaque(Tait1, Tait2)] fn foo(x: Tait1) -> Tait2 { + //[current]~^ ERROR item does not constrain `ex4::Tait1::{opaque#0}` let () = x; } } diff --git a/tests/ui/traits/next-solver/opaques/revealing-use-in-nested-body.rs b/tests/ui/traits/next-solver/opaques/revealing-use-in-nested-body.rs new file mode 100644 index 0000000000000..b79926eebda3b --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/revealing-use-in-nested-body.rs @@ -0,0 +1,11 @@ +// Regression test for #137751. This previously ICE'd as +// we did not provide the hidden type of the opaque inside +// of the async block. This caused borrowck of the recursive +// call to ICE. + +//@ edition: 2021 +//@ check-pass +async fn test() { + Box::pin(test()).await; +} +fn main() {} diff --git a/tests/ui/traits/next-solver/overflow-plus-ambiguity-normalizes-to-response.rs b/tests/ui/traits/next-solver/overflow-plus-ambiguity-normalizes-to-response.rs new file mode 100644 index 0000000000000..4b20cd11b0d41 --- /dev/null +++ b/tests/ui/traits/next-solver/overflow-plus-ambiguity-normalizes-to-response.rs @@ -0,0 +1,56 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// Regression test for . +// See comment below on `fn main`. + +trait Intersect { + type Output; +} + +impl Intersect> for T +where + T: Intersect, +{ + type Output = T; +} + +impl Intersect for Cuboid { + type Output = Cuboid; +} + +fn intersect(_: &T, _: &U) -> T::Output +where + T: Intersect, +{ + todo!() +} + +struct Cuboid; +impl Cuboid { + fn method(&self) {} +} + +fn main() { + let x = vec![]; + // Here we end up trying to normalize `>>::Output` + // for the return type of the function, to constrain `y`. The impl then requires + // `Cuboid: Intersect`, which has two candidates. One that constrains + // `?0 = Vec`, which itself has the same two candidates and ends up leading + // to a recursion depth overflow. In the second impl, we constrain `?0 = Cuboid`. + // + // Floundering leads to us combining the overflow candidate and yes candidate to + // overflow. Because the response was overflow, we used to bubble it up to the + // parent normalizes-to goal, which caused us to drop its constraint that would + // guide us to normalize the associated type mentioned before. + // + // After this PR, we implement a new floundering "algebra" such that `Overflow OR Maybe` + // returns anew `Overflow { keep_constraints: true }`, which means that we don't + // need to drop constraints in the parent normalizes-to goal. This allows us to + // normalize `y` to `Cuboid`, and allows us to call the method successfully. We + // then constrain the `?0` in `let x: Vec = x` below, so that we don't have + // a left over ambiguous goal. + let y = intersect(&Cuboid, &x); + y.method(); + let x: Vec = x; +} diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs index 0f01a453b332e..b2a8c8cb4ae6d 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs @@ -13,12 +13,7 @@ fn needs_bar() {} fn test::Assoc2> + Foo2::Assoc1>>() { needs_bar::(); - //~^ ERROR overflow evaluating the requirement `::Assoc1 == _` - //~| ERROR overflow evaluating the requirement `::Assoc1 == _` - //~| ERROR overflow evaluating the requirement `::Assoc1 == _` - //~| ERROR overflow evaluating the requirement `::Assoc1 == _` - //~| ERROR overflow evaluating the requirement `::Assoc1: Sized` - //~| ERROR overflow evaluating the requirement `::Assoc1: Bar` + //~^ ERROR the trait bound `::Assoc2: Bar` is not satisfied } fn main() {} diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr index 2b0e57966fe7b..c4be47e3520da 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr @@ -1,59 +1,19 @@ -error[E0275]: overflow evaluating the requirement `::Assoc1 == _` +error[E0277]: the trait bound `::Assoc2: Bar` is not satisfied --> $DIR/recursive-self-normalization-2.rs:15:17 | LL | needs_bar::(); - | ^^^^^^^^^ - -error[E0275]: overflow evaluating the requirement `::Assoc1: Bar` - --> $DIR/recursive-self-normalization-2.rs:15:17 - | -LL | needs_bar::(); - | ^^^^^^^^^ + | ^^^^^^^^^ the trait `Bar` is not implemented for `::Assoc2` | note: required by a bound in `needs_bar` --> $DIR/recursive-self-normalization-2.rs:12:17 | LL | fn needs_bar() {} | ^^^ required by this bound in `needs_bar` - -error[E0275]: overflow evaluating the requirement `::Assoc1: Sized` - --> $DIR/recursive-self-normalization-2.rs:15:17 - | -LL | needs_bar::(); - | ^^^^^^^^^ - | -note: required by an implicit `Sized` bound in `needs_bar` - --> $DIR/recursive-self-normalization-2.rs:12:14 - | -LL | fn needs_bar() {} - | ^ required by the implicit `Sized` requirement on this type parameter in `needs_bar` -help: consider relaxing the implicit `Sized` restriction - | -LL | fn needs_bar() {} - | ++++++++ - -error[E0275]: overflow evaluating the requirement `::Assoc1 == _` - --> $DIR/recursive-self-normalization-2.rs:15:5 - | -LL | needs_bar::(); - | ^^^^^^^^^^^^^^^^^^^^^^ - -error[E0275]: overflow evaluating the requirement `::Assoc1 == _` - --> $DIR/recursive-self-normalization-2.rs:15:5 - | -LL | needs_bar::(); - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0275]: overflow evaluating the requirement `::Assoc1 == _` - --> $DIR/recursive-self-normalization-2.rs:15:17 - | -LL | needs_bar::(); - | ^^^^^^^^^ +help: consider further restricting the associated type | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +LL | fn test::Assoc2> + Foo2::Assoc1>>() where ::Assoc2: Bar { + | ++++++++++++++++++++++++++++++ -error: aborting due to 6 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization.rs b/tests/ui/traits/next-solver/overflow/recursive-self-normalization.rs index f435b48737e23..f441ac499f99c 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization.rs +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization.rs @@ -9,12 +9,7 @@ fn needs_bar() {} fn test::Assoc>>() { needs_bar::(); - //~^ ERROR overflow evaluating the requirement `::Assoc == _` - //~| ERROR overflow evaluating the requirement `::Assoc == _` - //~| ERROR overflow evaluating the requirement `::Assoc == _` - //~| ERROR overflow evaluating the requirement `::Assoc == _` - //~| ERROR overflow evaluating the requirement `::Assoc: Sized` - //~| ERROR overflow evaluating the requirement `::Assoc: Bar` + //~^ ERROR the trait bound `::Assoc: Bar` is not satisfied } fn main() {} diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization.stderr b/tests/ui/traits/next-solver/overflow/recursive-self-normalization.stderr index af8504dcaeeea..c551823468741 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization.stderr +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization.stderr @@ -1,59 +1,19 @@ -error[E0275]: overflow evaluating the requirement `::Assoc == _` +error[E0277]: the trait bound `::Assoc: Bar` is not satisfied --> $DIR/recursive-self-normalization.rs:11:17 | LL | needs_bar::(); - | ^^^^^^^^ - -error[E0275]: overflow evaluating the requirement `::Assoc: Bar` - --> $DIR/recursive-self-normalization.rs:11:17 - | -LL | needs_bar::(); - | ^^^^^^^^ + | ^^^^^^^^ the trait `Bar` is not implemented for `::Assoc` | note: required by a bound in `needs_bar` --> $DIR/recursive-self-normalization.rs:8:17 | LL | fn needs_bar() {} | ^^^ required by this bound in `needs_bar` - -error[E0275]: overflow evaluating the requirement `::Assoc: Sized` - --> $DIR/recursive-self-normalization.rs:11:17 - | -LL | needs_bar::(); - | ^^^^^^^^ - | -note: required by an implicit `Sized` bound in `needs_bar` - --> $DIR/recursive-self-normalization.rs:8:14 - | -LL | fn needs_bar() {} - | ^ required by the implicit `Sized` requirement on this type parameter in `needs_bar` -help: consider relaxing the implicit `Sized` restriction - | -LL | fn needs_bar() {} - | ++++++++ - -error[E0275]: overflow evaluating the requirement `::Assoc == _` - --> $DIR/recursive-self-normalization.rs:11:5 - | -LL | needs_bar::(); - | ^^^^^^^^^^^^^^^^^^^^^ - -error[E0275]: overflow evaluating the requirement `::Assoc == _` - --> $DIR/recursive-self-normalization.rs:11:5 - | -LL | needs_bar::(); - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0275]: overflow evaluating the requirement `::Assoc == _` - --> $DIR/recursive-self-normalization.rs:11:17 - | -LL | needs_bar::(); - | ^^^^^^^^ +help: consider further restricting the associated type | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +LL | fn test::Assoc>>() where ::Assoc: Bar { + | ++++++++++++++++++++++++++++ -error: aborting due to 6 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/rpitit-cycle-due-to-rigid.rs b/tests/ui/traits/next-solver/rpitit-cycle-due-to-rigid.rs new file mode 100644 index 0000000000000..ec3d710ef3719 --- /dev/null +++ b/tests/ui/traits/next-solver/rpitit-cycle-due-to-rigid.rs @@ -0,0 +1,32 @@ +//@ compile-flags: -Znext-solver +//@ check-pass +//@ edition: 2024 + +// Ensure we don't end up in a query cycle due to trying to assemble an impl candidate +// for an RPITIT normalizes-to goal, even though that impl candidate would *necessarily* +// be made rigid by a where clause. This query cycle is thus avoidable by not assembling +// that impl candidate which we *know* we are going to throw away anyways. + +use std::future::Future; + +pub trait ReactiveFunction: Send { + type Output; + + fn invoke(self) -> Self::Output; +} + +trait AttributeValue { + fn resolve(self) -> impl Future + Send; +} + +impl AttributeValue for F +where + F: ReactiveFunction, + V: AttributeValue, +{ + async fn resolve(self) { + self.invoke().resolve().await + } +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/specialization-transmute.rs b/tests/ui/traits/next-solver/specialization-transmute.rs index 376fa22ae1923..f1447cd6a9e2e 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.rs +++ b/tests/ui/traits/next-solver/specialization-transmute.rs @@ -10,11 +10,8 @@ trait Default { impl Default for T { default type Id = T; - // This will be fixed by #111994 fn intu(&self) -> &Self::Id { - //~^ ERROR type annotations needed - //~| ERROR cannot normalize `::Id: '_` - self //~ ERROR cannot satisfy + self //~ ERROR mismatched types } } @@ -25,6 +22,7 @@ fn transmute, U: Copy>(t: T) -> U { use std::num::NonZero; fn main() { - let s = transmute::>>(0); //~ ERROR cannot satisfy + let s = transmute::>>(0); + //~^ ERROR type mismatch resolving `::Id == Option>` assert_eq!(s, None); } diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr index eeb101911c435..8bd290ea19707 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.stderr +++ b/tests/ui/traits/next-solver/specialization-transmute.stderr @@ -8,37 +8,32 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -error: cannot normalize `::Id: '_` - --> $DIR/specialization-transmute.rs:14:5 +error[E0308]: mismatched types + --> $DIR/specialization-transmute.rs:14:9 | LL | fn intu(&self) -> &Self::Id { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0282]: type annotations needed - --> $DIR/specialization-transmute.rs:14:23 - | -LL | fn intu(&self) -> &Self::Id { - | ^^^^^^^^^ cannot infer type for reference `&::Id` - -error[E0284]: type annotations needed: cannot satisfy `::Id normalizes-to T` - --> $DIR/specialization-transmute.rs:17:9 - | + | --------- expected `&::Id` because of return type LL | self - | ^^^^ cannot satisfy `::Id normalizes-to T` + | ^^^^ types differ + | + = note: expected reference `&::Id` + found reference `&T` -error[E0284]: type annotations needed: cannot satisfy `::Id normalizes-to Option>` - --> $DIR/specialization-transmute.rs:28:13 +error[E0271]: type mismatch resolving `::Id == Option>` + --> $DIR/specialization-transmute.rs:25:50 | LL | let s = transmute::>>(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `::Id normalizes-to Option>` + | ------------------------------------ ^ types differ + | | + | required by a bound introduced by this call | note: required by a bound in `transmute` - --> $DIR/specialization-transmute.rs:21:25 + --> $DIR/specialization-transmute.rs:18:25 | LL | fn transmute, U: Copy>(t: T) -> U { | ^^^^^^ required by this bound in `transmute` -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted -Some errors have detailed explanations: E0282, E0284. -For more information about an error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.rs b/tests/ui/traits/next-solver/specialization-unconstrained.rs index 2bbe784011071..6835c0764d6c8 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.rs +++ b/tests/ui/traits/next-solver/specialization-unconstrained.rs @@ -18,5 +18,5 @@ fn test, U>() {} fn main() { test::(); - //~^ ERROR cannot satisfy `::Id normalizes-to ()` + //~^ ERROR type mismatch resolving `::Id == ()` } diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.stderr b/tests/ui/traits/next-solver/specialization-unconstrained.stderr index e1d785b554bd3..1bcf5eddb5bca 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.stderr +++ b/tests/ui/traits/next-solver/specialization-unconstrained.stderr @@ -8,11 +8,11 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -error[E0284]: type annotations needed: cannot satisfy `::Id normalizes-to ()` - --> $DIR/specialization-unconstrained.rs:20:5 +error[E0271]: type mismatch resolving `::Id == ()` + --> $DIR/specialization-unconstrained.rs:20:12 | LL | test::(); - | ^^^^^^^^^^^^^^^^^ cannot satisfy `::Id normalizes-to ()` + | ^^^ types differ | note: required by a bound in `test` --> $DIR/specialization-unconstrained.rs:17:20 @@ -22,4 +22,4 @@ LL | fn test, U>() {} error: aborting due to 1 previous error; 1 warning emitted -For more information about this error, try `rustc --explain E0284`. +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/supertrait-alias-1.rs b/tests/ui/traits/next-solver/supertrait-alias-1.rs new file mode 100644 index 0000000000000..579a44677c2ee --- /dev/null +++ b/tests/ui/traits/next-solver/supertrait-alias-1.rs @@ -0,0 +1,22 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Regression test for . +// Tests that we don't try to replace `::Output` when replacing projections in the +// required bounds for `dyn Trait`, b/c `V` is not relevant to the dyn type, which we were +// previously encountering b/c we were walking into the existential projection bounds of the dyn +// type itself. + +pub trait Trait: Super {} + +pub trait Super { + type Output; +} + +fn bound() {} + +fn visit_simd_operator() { + bound::::Output>>(); +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/supertrait-alias-2.rs b/tests/ui/traits/next-solver/supertrait-alias-2.rs new file mode 100644 index 0000000000000..a0f3e038dca63 --- /dev/null +++ b/tests/ui/traits/next-solver/supertrait-alias-2.rs @@ -0,0 +1,25 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Regression test for . +// Tests that we don't try to replace `::Assoc` when replacing projections in the +// required bounds for `dyn Foo`, b/c `T` is not relevant to the dyn type, which we were +// encountering when walking through the elaborated supertraits of `dyn Foo`. + +trait Other {} + +trait Foo>: Other<>::Assoc> { + type Assoc; +} + +impl Foo for T { + type Assoc = (); +} + +impl Other<()> for T {} + +fn is_foo + ?Sized>() {} + +fn main() { + is_foo::>(); +} diff --git a/tests/ui/traits/next-solver/supertrait-alias-3.rs b/tests/ui/traits/next-solver/supertrait-alias-3.rs new file mode 100644 index 0000000000000..78182bbc41568 --- /dev/null +++ b/tests/ui/traits/next-solver/supertrait-alias-3.rs @@ -0,0 +1,32 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Regression test for . +// Exercises a case where structural equality is insufficient when replacing projections in a dyn's +// bounds. In this case, the bound will contain `:Assoc>::Assoc`, but +// the existential projections from the dyn will have `>::Assoc` because as an +// optimization we eagerly normalize aliases in goals. + +trait Other {} +impl Other for T {} + +trait Super { + type Assoc; +} + +trait Mirror { + type Assoc; +} +impl Mirror for T { + type Assoc = T; +} + +trait Foo: Super<::Assoc, Assoc = A> { + type FooAssoc: Other<::Assoc>>::Assoc>; +} + +fn is_foo + ?Sized, T, U>() {} + +fn main() { + is_foo::, _, _>(); +} diff --git a/tests/ui/traits/next-solver/supertrait-alias-4.rs b/tests/ui/traits/next-solver/supertrait-alias-4.rs new file mode 100644 index 0000000000000..919a768fcf281 --- /dev/null +++ b/tests/ui/traits/next-solver/supertrait-alias-4.rs @@ -0,0 +1,24 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Exercises the ambiguity that comes from replacing the associated types within the bounds +// that are required for a `impl Trait for dyn Trait` built-in object impl to hold. + +trait Sup { + type Assoc; +} + +trait Foo: Sup + Sup { + type Other: Bar<>::Assoc>; +} + +trait Bar {} +impl Bar for () {} + +fn foo(x: &(impl Foo + ?Sized)) {} + +fn main() { + let x: &dyn Foo<_, _, Other = ()> = todo!(); + foo(x); + let y: &dyn Foo = x; +} diff --git a/tests/ui/traits/next-solver/unsize-good.rs b/tests/ui/traits/next-solver/unsize-good.rs index 4456e4f21884c..f3d8b08e693c8 100644 --- a/tests/ui/traits/next-solver/unsize-good.rs +++ b/tests/ui/traits/next-solver/unsize-good.rs @@ -1,8 +1,6 @@ //@ compile-flags: -Znext-solver //@ check-pass -#![feature(unsized_tuple_coercion)] - trait Foo {} impl Foo for i32 {} @@ -15,10 +13,6 @@ fn main() { let y = [1, 2, 3]; let _: &[i32] = &y; - // Tuple unsizing - let hi = (1i32,); - let _: &(dyn Foo,) = &hi; - // Dropping auto traits let a: &(dyn Foo + Send) = &1; let _: &dyn Foo = a; diff --git a/tests/ui/traits/next-solver/well-formed-in-relate.rs b/tests/ui/traits/next-solver/well-formed-in-relate.rs new file mode 100644 index 0000000000000..eec1ddef228c4 --- /dev/null +++ b/tests/ui/traits/next-solver/well-formed-in-relate.rs @@ -0,0 +1,21 @@ +fn main() { + let x; + //~^ ERROR type annotations needed for `Map<_, _>` + higher_ranked(); + x = unconstrained_map(); +} + +fn higher_ranked() where for<'a> &'a (): Sized {} + +struct Map where T: Fn() -> U { + t: T, +} + +trait Mirror { + type Assoc; +} +impl Mirror for T { + type Assoc = T; +} + +fn unconstrained_map U, U>() -> as Mirror>::Assoc { todo!() } diff --git a/tests/ui/traits/next-solver/well-formed-in-relate.stderr b/tests/ui/traits/next-solver/well-formed-in-relate.stderr new file mode 100644 index 0000000000000..5294a072d3123 --- /dev/null +++ b/tests/ui/traits/next-solver/well-formed-in-relate.stderr @@ -0,0 +1,27 @@ +error[E0283]: type annotations needed for `Map<_, _>` + --> $DIR/well-formed-in-relate.rs:2:9 + | +LL | let x; + | ^ +... +LL | x = unconstrained_map(); + | ------------------- type must be known at this point + | + = note: multiple `impl`s satisfying `_: Fn()` found in the following crates: `alloc`, `core`: + - impl Fn for &F + where A: Tuple, F: Fn, F: ?Sized; + - impl Fn for Box + where Args: Tuple, F: Fn, A: Allocator, F: ?Sized; +note: required by a bound in `unconstrained_map` + --> $DIR/well-formed-in-relate.rs:21:25 + | +LL | fn unconstrained_map U, U>() -> as Mirror>::Assoc { todo!() } + | ^^^^^^^^^ required by this bound in `unconstrained_map` +help: consider giving `x` an explicit type, where the type for type parameter `T` is specified + | +LL | let x: Map; + | +++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/non_lifetime_binders/diagnostic-hir-wf-check.rs b/tests/ui/traits/non_lifetime_binders/diagnostic-hir-wf-check.rs new file mode 100644 index 0000000000000..74c23a59bee9e --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/diagnostic-hir-wf-check.rs @@ -0,0 +1,18 @@ +// Make sure not to construct predicates with escaping bound vars in `diagnostic_hir_wf_check`. +// Regression test for . + +#![feature(non_lifetime_binders)] +//~^ WARN the feature `non_lifetime_binders` is incomplete + +trait A {} +impl A for () {} + +trait B {} +struct W(T); + +fn b() -> (W<()>, impl for A) { (W(()), ()) } +//~^ ERROR the trait bound `(): B` is not satisfied +//~| ERROR the trait bound `(): B` is not satisfied +//~| ERROR the trait bound `(): B` is not satisfied + +fn main() {} diff --git a/tests/ui/traits/non_lifetime_binders/diagnostic-hir-wf-check.stderr b/tests/ui/traits/non_lifetime_binders/diagnostic-hir-wf-check.stderr new file mode 100644 index 0000000000000..df99f4a67abb2 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/diagnostic-hir-wf-check.stderr @@ -0,0 +1,65 @@ +warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/diagnostic-hir-wf-check.rs:4:12 + | +LL | #![feature(non_lifetime_binders)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #108185 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the trait bound `(): B` is not satisfied + --> $DIR/diagnostic-hir-wf-check.rs:13:12 + | +LL | fn b() -> (W<()>, impl for A) { (W(()), ()) } + | ^^^^^ the trait `B` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/diagnostic-hir-wf-check.rs:10:1 + | +LL | trait B {} + | ^^^^^^^ +note: required by a bound in `W` + --> $DIR/diagnostic-hir-wf-check.rs:11:13 + | +LL | struct W(T); + | ^ required by this bound in `W` + +error[E0277]: the trait bound `(): B` is not satisfied + --> $DIR/diagnostic-hir-wf-check.rs:13:42 + | +LL | fn b() -> (W<()>, impl for A) { (W(()), ()) } + | - ^^ the trait `B` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/diagnostic-hir-wf-check.rs:10:1 + | +LL | trait B {} + | ^^^^^^^ +note: required by a bound in `W` + --> $DIR/diagnostic-hir-wf-check.rs:11:13 + | +LL | struct W(T); + | ^ required by this bound in `W` + +error[E0277]: the trait bound `(): B` is not satisfied + --> $DIR/diagnostic-hir-wf-check.rs:13:40 + | +LL | fn b() -> (W<()>, impl for A) { (W(()), ()) } + | ^^^^^ the trait `B` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/diagnostic-hir-wf-check.rs:10:1 + | +LL | trait B {} + | ^^^^^^^ +note: required by a bound in `W` + --> $DIR/diagnostic-hir-wf-check.rs:11:13 + | +LL | struct W(T); + | ^ required by this bound in `W` + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs index 2a30178852550..aff81207a930b 100644 --- a/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs +++ b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs @@ -6,7 +6,7 @@ trait Trait {} fn produce() -> impl for Trait<(), Assoc = impl Trait> { //~^ ERROR associated type `Assoc` not found for `Trait` //~| ERROR associated type `Assoc` not found for `Trait` - //~| the trait bound `{integer}: Trait<()>` is not satisfied + //~| ERROR the trait bound `{integer}: Trait<()>` is not satisfied //~| ERROR cannot capture late-bound type parameter in nested `impl Trait` 16 } diff --git a/tests/ui/traits/object/ambiguity-vtable-segfault.rs b/tests/ui/traits/object/ambiguity-vtable-segfault.rs new file mode 100644 index 0000000000000..dff7d26ae9339 --- /dev/null +++ b/tests/ui/traits/object/ambiguity-vtable-segfault.rs @@ -0,0 +1,37 @@ +// In this example below, we have two overlapping candidates for `dyn Q: Q`. +// Specifically, the user written impl for `::Assoc` and the +// built-in impl for object types. Since they differ by their region responses, +// the goal is ambiguous. This affects codegen since impossible obligations +// for method dispatch will lead to a segfault, since we end up emitting dummy +// call vtable offsets due to . + +// Test for . + +//@ run-pass + +trait Mirror { + type Assoc: ?Sized; +} +impl Mirror for T { + type Assoc = T; +} + +trait Q: 'static { + fn q(&self); +} + +impl Q for i32 { + fn q(&self) { println!("i32"); } +} + +impl Q for ::Assoc where Self: 'static { + fn q(&self) { println!("dyn Q"); } +} + +fn foo(t: &T) { + t.q(); +} + +fn main() { + foo(&1 as &dyn Q); +} diff --git a/tests/ui/traits/object/constrain-via-unnecessary-bound.rs b/tests/ui/traits/object/constrain-via-unnecessary-bound.rs new file mode 100644 index 0000000000000..4640d6b3ed593 --- /dev/null +++ b/tests/ui/traits/object/constrain-via-unnecessary-bound.rs @@ -0,0 +1,24 @@ +//@ check-pass + +// Regression test for . +// Test that we lower impossible-to-satisfy associated type bounds, which +// may for example constrain impl parameters. + +pub trait Other {} + +pub trait Trait { + type Assoc + where + Self: Sized; +} + +impl Other for dyn Trait {} +// `dyn Trait` is a different "nominal type" than `dyn Trait`. +impl Other for dyn Trait {} +//~^ WARN unnecessary associated type bound for dyn-incompatible associated type + +// I hope it's clear that `dyn Trait` (w/o `Assoc`) wouldn't match this impl. +impl dyn Trait {} +//~^ WARN unnecessary associated type bound for dyn-incompatible associated type + +fn main() {} diff --git a/tests/ui/traits/object/constrain-via-unnecessary-bound.stderr b/tests/ui/traits/object/constrain-via-unnecessary-bound.stderr new file mode 100644 index 0000000000000..4383ca869eacc --- /dev/null +++ b/tests/ui/traits/object/constrain-via-unnecessary-bound.stderr @@ -0,0 +1,19 @@ +warning: unnecessary associated type bound for dyn-incompatible associated type + --> $DIR/constrain-via-unnecessary-bound.rs:17:26 + | +LL | impl Other for dyn Trait {} + | ^^^^^^^^^^ help: remove this bound + | + = note: this associated type has a `where Self: Sized` bound, and while the associated type can be specified, it cannot be used because trait objects are never `Sized` + = note: `#[warn(unused_associated_type_bounds)]` on by default + +warning: unnecessary associated type bound for dyn-incompatible associated type + --> $DIR/constrain-via-unnecessary-bound.rs:21:19 + | +LL | impl dyn Trait {} + | ^^^^^^^^^ help: remove this bound + | + = note: this associated type has a `where Self: Sized` bound, and while the associated type can be specified, it cannot be used because trait objects are never `Sized` + +warning: 2 warnings emitted + diff --git a/tests/ui/traits/object/issue-33140-traitobject-crate.rs b/tests/ui/traits/object/issue-33140-traitobject-crate.rs index 00ef6430d6382..ff7cd30644b75 100644 --- a/tests/ui/traits/object/issue-33140-traitobject-crate.rs +++ b/tests/ui/traits/object/issue-33140-traitobject-crate.rs @@ -1,6 +1,3 @@ -//@ check-pass - -#![warn(order_dependent_trait_objects)] #![allow(dyn_drop)] // Check that traitobject 0.1.0 compiles @@ -84,15 +81,12 @@ unsafe impl Trait for dyn (::std::iter::Iterator) + Send + Sync { } unsafe impl Trait for dyn (::std::marker::Send) + Send { } unsafe impl Trait for dyn (::std::marker::Send) + Sync { } unsafe impl Trait for dyn (::std::marker::Send) + Send + Sync { } -//~^ WARNING conflicting implementations of trait `Trait` for type -//~| WARNING this was previously accepted by the compiler but is being phased out +//~^ ERROR conflicting implementations of trait `Trait` for type unsafe impl Trait for dyn (::std::marker::Sync) + Send { } -//~^ WARNING conflicting implementations of trait `Trait` for type -//~| WARNING this was previously accepted by the compiler but is being phased out +//~^ ERROR conflicting implementations of trait `Trait` for type unsafe impl Trait for dyn (::std::marker::Sync) + Sync { } unsafe impl Trait for dyn (::std::marker::Sync) + Send + Sync { } -//~^ WARNING conflicting implementations of trait `Trait` for type -//~| WARNING this was previously accepted by the compiler but is being phased out +//~^ ERROR conflicting implementations of trait `Trait` for type unsafe impl Trait for dyn (::std::ops::Drop) + Send { } unsafe impl Trait for dyn (::std::ops::Drop) + Sync { } unsafe impl Trait for dyn (::std::ops::Drop) + Send + Sync { } diff --git a/tests/ui/traits/object/issue-33140-traitobject-crate.stderr b/tests/ui/traits/object/issue-33140-traitobject-crate.stderr index 525401f9d69ec..a01c7990db365 100644 --- a/tests/ui/traits/object/issue-33140-traitobject-crate.stderr +++ b/tests/ui/traits/object/issue-33140-traitobject-crate.stderr @@ -1,95 +1,29 @@ -warning: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`: (E0119) - --> $DIR/issue-33140-traitobject-crate.rs:86:1 +error[E0119]: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)` + --> $DIR/issue-33140-traitobject-crate.rs:83:1 | LL | unsafe impl Trait for dyn (::std::marker::Send) + Sync { } | ------------------------------------------------------ first implementation here LL | unsafe impl Trait for dyn (::std::marker::Send) + Send + Sync { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)` - | - = 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 #56484 -note: the lint level is defined here - --> $DIR/issue-33140-traitobject-crate.rs:3:9 - | -LL | #![warn(order_dependent_trait_objects)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`: (E0119) - --> $DIR/issue-33140-traitobject-crate.rs:89:1 - | -LL | unsafe impl Trait for dyn (::std::marker::Send) + Send + Sync { } - | ------------------------------------------------------------- first implementation here -... -LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)` - | - = 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 #56484 - -warning: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`: (E0119) - --> $DIR/issue-33140-traitobject-crate.rs:93:1 - | -LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send { } - | ------------------------------------------------------ first implementation here -... -LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send + Sync { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)` - | - = 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 #56484 - -warning: 3 warnings emitted -Future incompatibility report: Future breakage diagnostic: -warning: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`: (E0119) - --> $DIR/issue-33140-traitobject-crate.rs:86:1 +error[E0119]: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)` + --> $DIR/issue-33140-traitobject-crate.rs:85:1 | LL | unsafe impl Trait for dyn (::std::marker::Send) + Sync { } | ------------------------------------------------------ first implementation here -LL | unsafe impl Trait for dyn (::std::marker::Send) + Send + Sync { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)` - | - = 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 #56484 -note: the lint level is defined here - --> $DIR/issue-33140-traitobject-crate.rs:3:9 - | -LL | #![warn(order_dependent_trait_objects)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -warning: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`: (E0119) - --> $DIR/issue-33140-traitobject-crate.rs:89:1 - | -LL | unsafe impl Trait for dyn (::std::marker::Send) + Send + Sync { } - | ------------------------------------------------------------- first implementation here ... LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)` - | - = 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 #56484 -note: the lint level is defined here - --> $DIR/issue-33140-traitobject-crate.rs:3:9 - | -LL | #![warn(order_dependent_trait_objects)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Future breakage diagnostic: -warning: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`: (E0119) - --> $DIR/issue-33140-traitobject-crate.rs:93:1 +error[E0119]: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)` + --> $DIR/issue-33140-traitobject-crate.rs:88:1 | -LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send { } +LL | unsafe impl Trait for dyn (::std::marker::Send) + Sync { } | ------------------------------------------------------ first implementation here ... LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send + Sync { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)` - | - = 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 #56484 -note: the lint level is defined here - --> $DIR/issue-33140-traitobject-crate.rs:3:9 - | -LL | #![warn(order_dependent_trait_objects)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/object/pretty.stderr b/tests/ui/traits/object/pretty.stderr index 37fe142951d89..2f9fdf151f08c 100644 --- a/tests/ui/traits/object/pretty.stderr +++ b/tests/ui/traits/object/pretty.stderr @@ -154,12 +154,12 @@ error[E0308]: mismatched types --> $DIR/pretty.rs:41:56 | LL | fn dyn_has_gat(x: &dyn HasGat = ()>) { x } - | - ^ expected `()`, found `&dyn HasGat` + | - ^ expected `()`, found `&dyn HasGat = ()>` | | - | help: try adding a return type: `-> &dyn HasGat` + | help: try adding a return type: `-> &dyn HasGat = ()>` | = note: expected unit type `()` - found reference `&dyn HasGat` + found reference `&dyn HasGat = ()>` error: aborting due to 14 previous errors; 1 warning emitted diff --git a/tests/ui/traits/object/suggestion-trait-object-issue-139174.rs b/tests/ui/traits/object/suggestion-trait-object-issue-139174.rs new file mode 100644 index 0000000000000..50851a5b0f84c --- /dev/null +++ b/tests/ui/traits/object/suggestion-trait-object-issue-139174.rs @@ -0,0 +1,24 @@ +//@ edition: 2021 + +fn f<'a>(x: Box Option>) -> usize { + //~^ ERROR expected trait, found builtin type `usize` + //~| ERROR expected a type, found a trait [E0782] + 0 +} + +fn create_adder<'a>(x: i32) -> usize + 'a { + //~^ ERROR expected trait, found builtin type `usize` + //~| ERROR expected a type, found a trait [E0782] + move |y| x + y +} + +struct Struct<'a>{ + x: usize + 'a, + //~^ ERROR expected trait, found builtin type `usize` + //~| ERROR expected a type, found a trait [E0782] +} + + +fn main() { + +} diff --git a/tests/ui/traits/object/suggestion-trait-object-issue-139174.stderr b/tests/ui/traits/object/suggestion-trait-object-issue-139174.stderr new file mode 100644 index 0000000000000..0d1ce85fc2888 --- /dev/null +++ b/tests/ui/traits/object/suggestion-trait-object-issue-139174.stderr @@ -0,0 +1,40 @@ +error[E0404]: expected trait, found builtin type `usize` + --> $DIR/suggestion-trait-object-issue-139174.rs:3:36 + | +LL | fn f<'a>(x: Box Option>) -> usize { + | ^^^^^ not a trait + +error[E0404]: expected trait, found builtin type `usize` + --> $DIR/suggestion-trait-object-issue-139174.rs:9:32 + | +LL | fn create_adder<'a>(x: i32) -> usize + 'a { + | ^^^^^ not a trait + +error[E0404]: expected trait, found builtin type `usize` + --> $DIR/suggestion-trait-object-issue-139174.rs:16:8 + | +LL | x: usize + 'a, + | ^^^^^ not a trait + +error[E0782]: expected a type, found a trait + --> $DIR/suggestion-trait-object-issue-139174.rs:3:36 + | +LL | fn f<'a>(x: Box Option>) -> usize { + | ^^^^^^^^^^ + +error[E0782]: expected a type, found a trait + --> $DIR/suggestion-trait-object-issue-139174.rs:9:32 + | +LL | fn create_adder<'a>(x: i32) -> usize + 'a { + | ^^^^^^^^^^ + +error[E0782]: expected a type, found a trait + --> $DIR/suggestion-trait-object-issue-139174.rs:16:8 + | +LL | x: usize + 'a, + | ^^^^^^^^^^ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0404, E0782. +For more information about an error, try `rustc --explain E0404`. diff --git a/tests/ui/traits/object/vs-lifetime.rs b/tests/ui/traits/object/vs-lifetime.rs index d3e6c0b217c98..52385b2638439 100644 --- a/tests/ui/traits/object/vs-lifetime.rs +++ b/tests/ui/traits/object/vs-lifetime.rs @@ -7,7 +7,7 @@ fn main() { // `'static` is a lifetime argument, `'static +` is a type argument let _: S<'static, u8>; let _: S<'static, dyn 'static +>; - //~^ at least one trait is required for an object type + //~^ ERROR at least one trait is required for an object type let _: S<'static, 'static>; //~^ ERROR struct takes 1 lifetime argument but 2 lifetime arguments were supplied //~| ERROR struct takes 1 generic argument but 0 generic arguments were supplied diff --git a/tests/ui/traits/on_unimplemented_long_types.rs b/tests/ui/traits/on_unimplemented_long_types.rs index c652b71e51acd..d6c3e8ef75faa 100644 --- a/tests/ui/traits/on_unimplemented_long_types.rs +++ b/tests/ui/traits/on_unimplemented_long_types.rs @@ -1,6 +1,4 @@ //@ compile-flags: --diagnostic-width=60 -Z write-long-types-to-disk=yes -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" pub fn foo() -> impl std::fmt::Display { //~^ ERROR doesn't implement `std::fmt::Display` diff --git a/tests/ui/traits/on_unimplemented_long_types.stderr b/tests/ui/traits/on_unimplemented_long_types.stderr index 2705d7c501ec6..1628466e0818f 100644 --- a/tests/ui/traits/on_unimplemented_long_types.stderr +++ b/tests/ui/traits/on_unimplemented_long_types.stderr @@ -1,5 +1,5 @@ error[E0277]: `Option>>` doesn't implement `std::fmt::Display` - --> $DIR/on_unimplemented_long_types.rs:5:17 + --> $DIR/on_unimplemented_long_types.rs:3:17 | LL | pub fn foo() -> impl std::fmt::Display { | ^^^^^^^^^^^^^^^^^^^^^^ `Option>>` cannot be formatted with the default formatter @@ -15,7 +15,7 @@ LL | | ))))))))))) | = help: the trait `std::fmt::Display` is not implemented for `Option>>` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/on_unimplemented_long_types.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console error: aborting due to 1 previous error diff --git a/tests/ui/traits/pointee-tail-is-generic-errors.rs b/tests/ui/traits/pointee-tail-is-generic-errors.rs index 92a83f40b184c..8ddac5b2f3964 100644 --- a/tests/ui/traits/pointee-tail-is-generic-errors.rs +++ b/tests/ui/traits/pointee-tail-is-generic-errors.rs @@ -5,6 +5,7 @@ type Opaque = impl std::fmt::Debug + ?Sized; +#[define_opaque(Opaque)] fn opaque() -> &'static Opaque { &[1] as &[i32] } diff --git a/tests/ui/traits/pointee-tail-is-generic-errors.stderr b/tests/ui/traits/pointee-tail-is-generic-errors.stderr index 0c3d7060dd7c3..907f07026a43a 100644 --- a/tests/ui/traits/pointee-tail-is-generic-errors.stderr +++ b/tests/ui/traits/pointee-tail-is-generic-errors.stderr @@ -1,5 +1,5 @@ error[E0271]: type mismatch resolving `::Metadata == ()` - --> $DIR/pointee-tail-is-generic-errors.rs:13:15 + --> $DIR/pointee-tail-is-generic-errors.rs:14:15 | LL | is_thin::(); | ^ expected `()`, found associated type @@ -9,13 +9,13 @@ LL | is_thin::(); = help: consider constraining the associated type `::Metadata` to `()` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html note: required by a bound in `is_thin` - --> $DIR/pointee-tail-is-generic-errors.rs:20:33 + --> $DIR/pointee-tail-is-generic-errors.rs:21:33 | LL | fn is_thin + ?Sized>() {} | ^^^^^^^^^^^^^ required by this bound in `is_thin` error[E0271]: type mismatch resolving `::Metadata == ()` - --> $DIR/pointee-tail-is-generic-errors.rs:16:15 + --> $DIR/pointee-tail-is-generic-errors.rs:17:15 | LL | type Opaque = impl std::fmt::Debug + ?Sized; | ----------------------------- the found opaque type @@ -26,7 +26,7 @@ LL | is_thin::(); = note: expected unit type `()` found associated type `::Metadata` note: required by a bound in `is_thin` - --> $DIR/pointee-tail-is-generic-errors.rs:20:33 + --> $DIR/pointee-tail-is-generic-errors.rs:21:33 | LL | fn is_thin + ?Sized>() {} | ^^^^^^^^^^^^^ required by this bound in `is_thin` diff --git a/tests/ui/traits/pointee-tail-is-generic.rs b/tests/ui/traits/pointee-tail-is-generic.rs index 14bdf0880c73b..b41fb61e4a4ab 100644 --- a/tests/ui/traits/pointee-tail-is-generic.rs +++ b/tests/ui/traits/pointee-tail-is-generic.rs @@ -4,12 +4,11 @@ #![feature(ptr_metadata)] #![feature(type_alias_impl_trait)] -mod opaque { - pub type Opaque = impl std::future::Future; +pub type Opaque = impl std::future::Future; - fn opaque() -> Opaque { - async {} - } +#[define_opaque(Opaque)] +fn opaque() -> Opaque { + async {} } fn a() { @@ -18,7 +17,7 @@ fn a() { // tail of ADT (which is a type param) is known to be sized is_thin::>(); // opaque type is known to be sized - is_thin::(); + is_thin::(); } fn a2() { diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs index 1f5d0a772a2b3..723179302e30a 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs +++ b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs @@ -4,7 +4,6 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[unique_next] compile-flags: -Znext-solver //@[multiple_next] compile-flags: -Znext-solver -//@ error-pattern: reached the recursion limit finding the struct tail for `<[Hello] as Normalize>::Assoc` trait Bound {} trait Normalize { @@ -25,3 +24,5 @@ struct Hello { } fn main() {} + +//~? ERROR reached the recursion limit finding the struct tail for `<[Hello] as Normalize>::Assoc` diff --git a/tests/ui/traits/suggest-remove-deref-issue-140166.rs b/tests/ui/traits/suggest-remove-deref-issue-140166.rs new file mode 100644 index 0000000000000..1b832c7eba5c0 --- /dev/null +++ b/tests/ui/traits/suggest-remove-deref-issue-140166.rs @@ -0,0 +1,18 @@ +trait Trait {} + +struct Chars; +impl Trait for Chars {} + +struct FlatMap(T); +impl std::fmt::Debug for FlatMap { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + unimplemented!() + } +} + +fn lol() { + format_args!("{:?}", FlatMap(&Chars)); + //~^ ERROR the trait bound `&Chars: Trait` is not satisfied [E0277] +} + +fn main() {} diff --git a/tests/ui/traits/suggest-remove-deref-issue-140166.stderr b/tests/ui/traits/suggest-remove-deref-issue-140166.stderr new file mode 100644 index 0000000000000..90f24d86d53e0 --- /dev/null +++ b/tests/ui/traits/suggest-remove-deref-issue-140166.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `&Chars: Trait` is not satisfied + --> $DIR/suggest-remove-deref-issue-140166.rs:14:26 + | +LL | format_args!("{:?}", FlatMap(&Chars)); + | ---- ^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `&Chars` + | | + | required by a bound introduced by this call + | + = help: the trait `Trait` is implemented for `Chars` +note: required for `FlatMap<&Chars>` to implement `Debug` + --> $DIR/suggest-remove-deref-issue-140166.rs:7:16 + | +LL | impl std::fmt::Debug for FlatMap { + | ----- ^^^^^^^^^^^^^^^ ^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here +note: required by a bound in `core::fmt::rt::Argument::<'_>::new_debug` + --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trait-impl-self-mismatch.rs b/tests/ui/traits/trait-impl-self-mismatch.rs new file mode 100644 index 0000000000000..54226cf2c9ad4 --- /dev/null +++ b/tests/ui/traits/trait-impl-self-mismatch.rs @@ -0,0 +1,19 @@ +//@compile-flags: -Zvalidate-mir -Zinline-mir -Zinline-mir-threshold=300 + +//! Ensure that a trait method implemented with the wrong signature +//! correctly triggers a compile error and not an ICE. +//! Regression test for . + +trait Bar { + fn bar(&self) {} +} + +impl Bar for T { + fn bar() { //~ ERROR method `bar` has a `&self` declaration in the trait, but not in the impl + let _ = "Hello".bytes().nth(3); + } +} + +fn main() { + ().bar(); +} diff --git a/tests/ui/traits/trait-impl-self-mismatch.stderr b/tests/ui/traits/trait-impl-self-mismatch.stderr new file mode 100644 index 0000000000000..4ee06787c7d87 --- /dev/null +++ b/tests/ui/traits/trait-impl-self-mismatch.stderr @@ -0,0 +1,12 @@ +error[E0186]: method `bar` has a `&self` declaration in the trait, but not in the impl + --> $DIR/trait-impl-self-mismatch.rs:12:5 + | +LL | fn bar(&self) {} + | ------------- `&self` used in trait +... +LL | fn bar() { + | ^^^^^^^^ expected `&self` in impl + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0186`. diff --git a/tests/ui/traits/trait-upcasting/dyn-to-dyn-star.rs b/tests/ui/traits/trait-upcasting/dyn-to-dyn-star.rs new file mode 100644 index 0000000000000..5936c93efad31 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/dyn-to-dyn-star.rs @@ -0,0 +1,19 @@ +// While somewhat nonsensical, this is a cast from a wide pointer to a thin pointer. +// Thus, we don't need to check an unsize goal here; there isn't any vtable casting +// happening at all. + +// Regression test for . + +//@ check-pass + +#![allow(incomplete_features)] +#![feature(dyn_star)] + +trait Foo {} +trait Bar {} + +fn cast(x: *const dyn Foo) { + x as *const dyn* Bar; +} + +fn main() {} diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr index 55a5e4cd4971f..0fca91fd2f2f6 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr @@ -7,12 +7,12 @@ LL | let _ = x as &dyn Bar<'static, 'a>; // Error | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:22:18 + --> $DIR/type-checking-test-4.rs:22:13 | LL | fn test_wrong2<'a>(x: &dyn Foo<'static>, y: &'a u32) { | -- lifetime `'a` defined here LL | let _ = x as &dyn Bar<'a, 'static>; // Error - | ^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static` error: lifetime may not live long enough --> $DIR/type-checking-test-4.rs:28:5 diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-opaques.rs b/tests/ui/traits/trait-upcasting/type-checking-test-opaques.rs index ab3817da28b1e..ed852ef986a3a 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-opaques.rs +++ b/tests/ui/traits/trait-upcasting/type-checking-test-opaques.rs @@ -15,6 +15,7 @@ fn test_correct2<'a>(x: &dyn Foo<'a>) { let _ = x as &dyn Bar<'_, '_, Tait>; } +#[define_opaque(Tait)] fn test_correct3<'a>(x: &dyn Foo<'a>, _: Tait) { let _ = x as &dyn Bar<'_, '_, ()>; } diff --git a/tests/ui/traits/trait-upcasting/upcast-defining-opaque.rs b/tests/ui/traits/trait-upcasting/upcast-defining-opaque.rs index 0548eda046842..3e36ce0dc726e 100644 --- a/tests/ui/traits/trait-upcasting/upcast-defining-opaque.rs +++ b/tests/ui/traits/trait-upcasting/upcast-defining-opaque.rs @@ -17,6 +17,7 @@ impl Super for T { type Foo = impl Sized; +#[define_opaque(Foo)] fn upcast(x: &dyn Sub) -> &dyn Super { x } diff --git a/tests/ui/traits/trivial-unsized-projection-2.bad.stderr b/tests/ui/traits/trivial-unsized-projection-2.bad.stderr new file mode 100644 index 0000000000000..bf8d3c40cf653 --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection-2.bad.stderr @@ -0,0 +1,54 @@ +error[E0277]: the size for values of type `[()]` cannot be known at compilation time + --> $DIR/trivial-unsized-projection-2.rs:22:12 + | +LL | const FOO: ::Assert = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `Tail`, the trait `Sized` is not implemented for `[()]` +note: required because it appears within the type `Tail` + --> $DIR/trivial-unsized-projection-2.rs:17:8 + | +LL | struct Tail([()]); + | ^^^^ +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection-2.rs:14:15 + | +LL | type Assert + | ------ required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Bad::Assert` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assert: ?Sized + | ++++++++ + +error[E0277]: the size for values of type `[()]` cannot be known at compilation time + --> $DIR/trivial-unsized-projection-2.rs:22:12 + | +LL | const FOO: ::Assert = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `Tail`, the trait `Sized` is not implemented for `[()]` +note: required because it appears within the type `Tail` + --> $DIR/trivial-unsized-projection-2.rs:17:8 + | +LL | struct Tail([()]); + | ^^^^ +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection-2.rs:14:15 + | +LL | type Assert + | ------ required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Bad::Assert` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assert: ?Sized + | ++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr new file mode 100644 index 0000000000000..bf8d3c40cf653 --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr @@ -0,0 +1,54 @@ +error[E0277]: the size for values of type `[()]` cannot be known at compilation time + --> $DIR/trivial-unsized-projection-2.rs:22:12 + | +LL | const FOO: ::Assert = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `Tail`, the trait `Sized` is not implemented for `[()]` +note: required because it appears within the type `Tail` + --> $DIR/trivial-unsized-projection-2.rs:17:8 + | +LL | struct Tail([()]); + | ^^^^ +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection-2.rs:14:15 + | +LL | type Assert + | ------ required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Bad::Assert` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assert: ?Sized + | ++++++++ + +error[E0277]: the size for values of type `[()]` cannot be known at compilation time + --> $DIR/trivial-unsized-projection-2.rs:22:12 + | +LL | const FOO: ::Assert = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `Tail`, the trait `Sized` is not implemented for `[()]` +note: required because it appears within the type `Tail` + --> $DIR/trivial-unsized-projection-2.rs:17:8 + | +LL | struct Tail([()]); + | ^^^^ +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection-2.rs:14:15 + | +LL | type Assert + | ------ required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Bad::Assert` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assert: ?Sized + | ++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trivial-unsized-projection-2.rs b/tests/ui/traits/trivial-unsized-projection-2.rs new file mode 100644 index 0000000000000..af4e12f6f9008 --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection-2.rs @@ -0,0 +1,34 @@ +//@ revisions: good bad good_new bad_new +//@[good_new] compile-flags: -Znext-solver +//@[bad_new] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[good] check-pass +//@[good_new] check-pass + +#![feature(trivial_bounds)] +#![allow(trivial_bounds)] + +trait Bad { + type Assert + where + Self: Sized; +} + +struct Tail([()]); + +impl Bad for Tail {} + +#[cfg(any(bad, bad_new))] +const FOO: ::Assert = todo!(); +//[bad]~^ ERROR the size for values of type `[()]` cannot be known at compilation time +//[bad]~| ERROR the size for values of type `[()]` cannot be known at compilation time +//[bad_new]~^^^ ERROR the size for values of type `[()]` cannot be known at compilation time +//[bad_new]~| ERROR the size for values of type `[()]` cannot be known at compilation time + +#[cfg(any(good, good_new))] +// Well-formed in trivially false param-env +fn foo() where Tail: Sized { + let _: ::Assert; +} + +fn main() {} diff --git a/tests/ui/traits/trivial-unsized-projection-in-coherence.rs b/tests/ui/traits/trivial-unsized-projection-in-coherence.rs new file mode 100644 index 0000000000000..4407d544b4ef6 --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection-in-coherence.rs @@ -0,0 +1,45 @@ +// Make sure we don't treat missing associated items as rigid +// during coherence, even if we know they've got an impossible +// `Sized`-bound. As we check whether the self type is definitely +// not `Sized` outside of coherence, this check can be incomplete. +// +// In this test we only use `impl Overlap for T` to normalize +// the field of `MaybeUnsized` when checking whether it's +// definitely not `Sized`. However, for `MaybeUnsized` we +// could also use `impl Overlap for u32` for normalization, which +// would result in a `Sized` type. cc #139000 + +struct MaybeUnsized, U>(>::MaybeUnsized); + +trait ReqSized { + type Missing1 + where + Self: Sized; + type Missing2 + where + Self: Sized; +} +impl ReqSized for MaybeUnsized {} + +struct W(T); +trait Eq {} +impl Eq for W {} + +trait RelateReqSized {} +impl RelateReqSized for T where W: Eq {} + +trait Overlap { + type MaybeUnsized: ?Sized; +} +impl Overlap for T { + type MaybeUnsized = str; +} +impl Overlap for u32 +//~^ ERROR conflicting implementations of trait `Overlap` for type `u32` +where + MaybeUnsized: RelateReqSized, +{ + type MaybeUnsized = u32; +} + +fn main() {} diff --git a/tests/ui/traits/trivial-unsized-projection-in-coherence.stderr b/tests/ui/traits/trivial-unsized-projection-in-coherence.stderr new file mode 100644 index 0000000000000..52fca9479ca46 --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection-in-coherence.stderr @@ -0,0 +1,15 @@ +error[E0119]: conflicting implementations of trait `Overlap` for type `u32` + --> $DIR/trivial-unsized-projection-in-coherence.rs:37:1 + | +LL | impl Overlap for T { + | -------------------------- first implementation here +... +LL | / impl Overlap for u32 +LL | | +LL | | where +LL | | MaybeUnsized: RelateReqSized, + | |_________________________________________^ conflicting implementation for `u32` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/trivial-unsized-projection.bad.stderr b/tests/ui/traits/trivial-unsized-projection.bad.stderr new file mode 100644 index 0000000000000..4aea63329b360 --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection.bad.stderr @@ -0,0 +1,44 @@ +error[E0277]: the size for values of type `[()]` cannot be known at compilation time + --> $DIR/trivial-unsized-projection.rs:20:12 + | +LL | const FOO: <[()] as Bad>::Assert = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[()]` +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection.rs:14:15 + | +LL | type Assert + | ------ required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Bad::Assert` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assert: ?Sized + | ++++++++ + +error[E0277]: the size for values of type `[()]` cannot be known at compilation time + --> $DIR/trivial-unsized-projection.rs:20:12 + | +LL | const FOO: <[()] as Bad>::Assert = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[()]` +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection.rs:14:15 + | +LL | type Assert + | ------ required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Bad::Assert` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assert: ?Sized + | ++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trivial-unsized-projection.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr new file mode 100644 index 0000000000000..4aea63329b360 --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr @@ -0,0 +1,44 @@ +error[E0277]: the size for values of type `[()]` cannot be known at compilation time + --> $DIR/trivial-unsized-projection.rs:20:12 + | +LL | const FOO: <[()] as Bad>::Assert = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[()]` +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection.rs:14:15 + | +LL | type Assert + | ------ required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Bad::Assert` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assert: ?Sized + | ++++++++ + +error[E0277]: the size for values of type `[()]` cannot be known at compilation time + --> $DIR/trivial-unsized-projection.rs:20:12 + | +LL | const FOO: <[()] as Bad>::Assert = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[()]` +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection.rs:14:15 + | +LL | type Assert + | ------ required by a bound in this associated type +LL | where +LL | Self: Sized; + | ^^^^^ required by this bound in `Bad::Assert` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Assert: ?Sized + | ++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trivial-unsized-projection.rs b/tests/ui/traits/trivial-unsized-projection.rs new file mode 100644 index 0000000000000..62ff25fb7ac0a --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection.rs @@ -0,0 +1,32 @@ +//@ revisions: good bad good_new bad_new +//@[good_new] compile-flags: -Znext-solver +//@[bad_new] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[good] check-pass +//@[good_new] check-pass + +#![feature(trivial_bounds)] +#![allow(trivial_bounds)] + +trait Bad { + type Assert + where + Self: Sized; +} + +impl Bad for [()] {} + +#[cfg(any(bad, bad_new))] +const FOO: <[()] as Bad>::Assert = todo!(); +//[bad]~^ ERROR the size for values of type `[()]` cannot be known at compilation time +//[bad]~| ERROR the size for values of type `[()]` cannot be known at compilation time +//[bad_new]~^^^ ERROR the size for values of type `[()]` cannot be known at compilation time +//[bad_new]~| ERROR the size for values of type `[()]` cannot be known at compilation time + +#[cfg(any(good, good_new))] +// Well-formed in trivially false param-env +fn foo() where [()]: Sized { + let _: <[()] as Bad>::Assert; +} + +fn main() {} diff --git a/tests/ui/traits/vtable/impossible-method.rs b/tests/ui/traits/vtable/impossible-method.rs new file mode 100644 index 0000000000000..ff7910cfac86b --- /dev/null +++ b/tests/ui/traits/vtable/impossible-method.rs @@ -0,0 +1,38 @@ +//@ run-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +trait Id { + type This<'a>; +} +impl Id for T { + type This<'a> = T; +} + +trait Trait {} +impl Trait fn(T::This<'a>)> for T {} + +trait Method { + fn call_me(&self) + where + T: Trait fn(T::This<'a>)>; +} + +impl Method for T { + fn call_me(&self) { + println!("method was reachable"); + } +} + +fn generic(x: &dyn Method) { + // Proving `T: Trait fn(T::This<'a>)>` holds. + x.call_me(); +} + +fn main() { + // Proving `u32: Trait` fails due to incompleteness. + // We don't add the method to the vtable of `dyn Method`, so + // calling it causes UB. + generic::(&()); +} diff --git a/tests/ui/traits/winnowing/global-where-bound-region-constraints-2.rs b/tests/ui/traits/winnowing/global-where-bound-region-constraints-2.rs new file mode 100644 index 0000000000000..d422605a2923e --- /dev/null +++ b/tests/ui/traits/winnowing/global-where-bound-region-constraints-2.rs @@ -0,0 +1,33 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#172. +// +// In this test the global where-bound simply constrains the +// object lifetime bound to 'static while the builtin impl +// ends up also emitting a `dyn Any: 'static` type outlives +// constraint. This previously resulted in ambiguity. We now +// always prefer the impl. + +pub trait Any: 'static {} + +pub trait Downcast: Any +where + T: Any, +{ +} + +// elided object lifetime: `dyn Any + 'static` +impl dyn Any { + pub fn is(&self) + where + T: Any, + // elaboration adds global where-clause `dyn Any + 'static: Any` + Self: Downcast, + { + } +} + +fn main() {} diff --git a/tests/ui/traits/winnowing/global-where-bound-region-constraints.rs b/tests/ui/traits/winnowing/global-where-bound-region-constraints.rs new file mode 100644 index 0000000000000..3bc8b0438bf82 --- /dev/null +++ b/tests/ui/traits/winnowing/global-where-bound-region-constraints.rs @@ -0,0 +1,29 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#172. +// +// The next-generation trait solver previously simply tried +// to merge the global where-bounds with the impl candidates. +// This caused ambiguity in case the where-bound had stricter +// region requirements than the impl. + +trait Trait {} +struct Foo<'a, 'b>(&'a (), &'b ()); +impl<'a> Trait for Foo<'a, 'static> {} + +fn impls_trait() {} +fn foo() +where + Foo<'static, 'static>: Trait, +{ + // impl requires `'1 to be 'static + // global where-bound requires both '0 and '1 to be 'static + // + // we always prefer the impl here. + impls_trait::>(); +} + +fn main() {} diff --git a/tests/ui/traits/winnowing/norm-where-bound-gt-alias-bound.rs b/tests/ui/traits/winnowing/norm-where-bound-gt-alias-bound.rs new file mode 100644 index 0000000000000..cdfb0ee45af4d --- /dev/null +++ b/tests/ui/traits/winnowing/norm-where-bound-gt-alias-bound.rs @@ -0,0 +1,29 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Make sure we prefer the `I::IntoIterator: Iterator` +// where-bound over the `I::Intoiterator: Iterator` +// alias-bound. + +trait Iterator { + type Item; +} + +trait IntoIterator { + type Item; + type IntoIter: Iterator; +} + +fn normalize>() {} + +fn foo() +where + I: IntoIterator, + I::IntoIter: Iterator, +{ + normalize::(); +} + +fn main() {} diff --git a/tests/ui/transmutability/char.rs b/tests/ui/transmutability/char.rs new file mode 100644 index 0000000000000..55a6153732955 --- /dev/null +++ b/tests/ui/transmutability/char.rs @@ -0,0 +1,41 @@ +#![feature(never_type)] +#![feature(transmutability)] + +use std::mem::{Assume, TransmuteFrom}; + +pub fn is_transmutable() +where + Dst: TransmuteFrom, +{ +} + +fn main() { + is_transmutable::(); + + // `char`s can be in the following ranges: + // - [0, 0xD7FF] + // - [0xE000, 10FFFF] + // + // `Char` has variants whose tags are in the top and bottom of each range. + // It also has generic variants which are out of bounds of these ranges, but + // are generic on types which may be set to `!` to "disable" them in the + // transmutability analysis. + #[repr(u32)] + enum Char { + A = 0, + B = 0xD7FF, + OverB(B) = 0xD800, + UnderC(C) = 0xDFFF, + C = 0xE000, + D = 0x10FFFF, + OverD(D) = 0x110000, + } + + is_transmutable::, char>(); + is_transmutable::, char>(); + //~^ ERROR cannot be safely transmuted into `char` + is_transmutable::, char>(); + //~^ ERROR cannot be safely transmuted into `char` + is_transmutable::, char>(); + //~^ ERROR cannot be safely transmuted into `char` +} diff --git a/tests/ui/transmutability/char.stderr b/tests/ui/transmutability/char.stderr new file mode 100644 index 0000000000000..98e7ae9c0d49c --- /dev/null +++ b/tests/ui/transmutability/char.stderr @@ -0,0 +1,48 @@ +error[E0277]: `main::Char<(), !, !>` cannot be safely transmuted into `char` + --> $DIR/char.rs:35:39 + | +LL | is_transmutable::, char>(); + | ^^^^ at least one value of `main::Char<(), !, !>` isn't a bit-valid value of `char` + | +note: required by a bound in `is_transmutable` + --> $DIR/char.rs:8:10 + | +LL | pub fn is_transmutable() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error[E0277]: `main::Char` cannot be safely transmuted into `char` + --> $DIR/char.rs:37:39 + | +LL | is_transmutable::, char>(); + | ^^^^ at least one value of `main::Char` isn't a bit-valid value of `char` + | +note: required by a bound in `is_transmutable` + --> $DIR/char.rs:8:10 + | +LL | pub fn is_transmutable() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error[E0277]: `main::Char` cannot be safely transmuted into `char` + --> $DIR/char.rs:39:39 + | +LL | is_transmutable::, char>(); + | ^^^^ at least one value of `main::Char` isn't a bit-valid value of `char` + | +note: required by a bound in `is_transmutable` + --> $DIR/char.rs:8:10 + | +LL | pub fn is_transmutable() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/issue-101739-1.rs b/tests/ui/transmutability/issue-101739-1.rs index 4fde120e2be8b..b801e2355404c 100644 --- a/tests/ui/transmutability/issue-101739-1.rs +++ b/tests/ui/transmutability/issue-101739-1.rs @@ -6,7 +6,7 @@ mod assert { pub fn is_transmutable() where Dst: TransmuteFrom, //~ ERROR cannot find type `Dst` in this scope - //~| the constant `ASSUME_ALIGNMENT` is not of type `Assume` + //~| ERROR the constant `ASSUME_ALIGNMENT` is not of type `Assume` { } } diff --git a/tests/ui/transmutability/malformed-program-gracefulness/coherence-bikeshed-intrinsic-from.stderr b/tests/ui/transmutability/malformed-program-gracefulness/coherence-bikeshed-intrinsic-from.stderr index cdf9deecd5159..7f51d1519db22 100644 --- a/tests/ui/transmutability/malformed-program-gracefulness/coherence-bikeshed-intrinsic-from.stderr +++ b/tests/ui/transmutability/malformed-program-gracefulness/coherence-bikeshed-intrinsic-from.stderr @@ -19,7 +19,7 @@ error: unconstrained opaque type LL | type OpaqueType = impl OpaqueTrait; | ^^^^^^^^^^^^^^^^ | - = note: `OpaqueType` must be used in combination with a concrete type within the same module + = note: `OpaqueType` must be used in combination with a concrete type within the same crate error: aborting due to 3 previous errors diff --git a/tests/ui/transmutability/malformed-program-gracefulness/unknown_dst.rs b/tests/ui/transmutability/malformed-program-gracefulness/unknown_dst.rs index b8828c59d355f..e004f2cf4feb7 100644 --- a/tests/ui/transmutability/malformed-program-gracefulness/unknown_dst.rs +++ b/tests/ui/transmutability/malformed-program-gracefulness/unknown_dst.rs @@ -15,5 +15,5 @@ mod assert { fn should_gracefully_handle_unknown_dst() { struct Src; - assert::is_transmutable::(); //~ cannot find type + assert::is_transmutable::(); //~ ERROR cannot find type } diff --git a/tests/ui/transmutability/malformed-program-gracefulness/unknown_dst_field.rs b/tests/ui/transmutability/malformed-program-gracefulness/unknown_dst_field.rs index 2285d2f532eec..081d1bf11ec04 100644 --- a/tests/ui/transmutability/malformed-program-gracefulness/unknown_dst_field.rs +++ b/tests/ui/transmutability/malformed-program-gracefulness/unknown_dst_field.rs @@ -15,12 +15,12 @@ mod assert { fn should_gracefully_handle_unknown_dst_field() { #[repr(C)] struct Src; - #[repr(C)] struct Dst(Missing); //~ cannot find type + #[repr(C)] struct Dst(Missing); //~ ERROR cannot find type assert::is_transmutable::(); //~ ERROR cannot be safely transmuted } fn should_gracefully_handle_unknown_dst_ref_field() { #[repr(C)] struct Src(&'static Src); - #[repr(C)] struct Dst(&'static Missing); //~ cannot find type + #[repr(C)] struct Dst(&'static Missing); //~ ERROR cannot find type assert::is_transmutable::(); //~ ERROR cannot be safely transmuted } diff --git a/tests/ui/transmutability/malformed-program-gracefulness/unknown_src.rs b/tests/ui/transmutability/malformed-program-gracefulness/unknown_src.rs index 10ba7a61b8723..0aca4acd0a32b 100644 --- a/tests/ui/transmutability/malformed-program-gracefulness/unknown_src.rs +++ b/tests/ui/transmutability/malformed-program-gracefulness/unknown_src.rs @@ -15,5 +15,5 @@ mod assert { fn should_gracefully_handle_unknown_src() { #[repr(C)] struct Dst; - assert::is_transmutable::(); //~ cannot find type + assert::is_transmutable::(); //~ ERROR cannot find type } diff --git a/tests/ui/transmutability/malformed-program-gracefulness/unknown_src_field.rs b/tests/ui/transmutability/malformed-program-gracefulness/unknown_src_field.rs index 598e04971e28f..ee09651d7410e 100644 --- a/tests/ui/transmutability/malformed-program-gracefulness/unknown_src_field.rs +++ b/tests/ui/transmutability/malformed-program-gracefulness/unknown_src_field.rs @@ -14,13 +14,13 @@ mod assert { } fn should_gracefully_handle_unknown_src_field() { - #[repr(C)] struct Src(Missing); //~ cannot find type + #[repr(C)] struct Src(Missing); //~ ERROR cannot find type #[repr(C)] struct Dst(); assert::is_transmutable::(); //~ ERROR cannot be safely transmuted } fn should_gracefully_handle_unknown_src_ref_field() { - #[repr(C)] struct Src(&'static Missing); //~ cannot find type + #[repr(C)] struct Src(&'static Missing); //~ ERROR cannot find type #[repr(C)] struct Dst(&'static Dst); assert::is_transmutable::(); //~ ERROR cannot be safely transmuted } diff --git a/tests/ui/transmutability/transmute-higher-ranked.rs b/tests/ui/transmutability/transmute-higher-ranked.rs new file mode 100644 index 0000000000000..f0fe02a7908ee --- /dev/null +++ b/tests/ui/transmutability/transmute-higher-ranked.rs @@ -0,0 +1,18 @@ +// Ensure we don't ICE when transmuting higher-ranked types via a +// higher-ranked transmute goal. + +//@ check-pass + +#![feature(transmutability)] + +use std::mem::TransmuteFrom; + +pub fn transmute() +where + for<'a> &'a &'a i32: TransmuteFrom<&'a &'a u32>, +{ +} + +fn main() { + transmute(); +} diff --git a/tests/ui/transmutability/uninhabited.stderr b/tests/ui/transmutability/uninhabited.stderr index 39f8bf19c363e..3044b502d3146 100644 --- a/tests/ui/transmutability/uninhabited.stderr +++ b/tests/ui/transmutability/uninhabited.stderr @@ -1,27 +1,3 @@ -error[E0080]: evaluation of constant value failed - --> $DIR/uninhabited.rs:41:9 - | -LL | assert!(false); - | ^^^^^^^^^^^^^^ evaluation panicked: assertion failed: false - | - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0080]: evaluation of constant value failed - --> $DIR/uninhabited.rs:63:9 - | -LL | assert!(false); - | ^^^^^^^^^^^^^^ evaluation panicked: assertion failed: false - | - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0080]: evaluation of constant value failed - --> $DIR/uninhabited.rs:87:9 - | -LL | assert!(false); - | ^^^^^^^^^^^^^^ evaluation panicked: assertion failed: false - | - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0277]: `()` cannot be safely transmuted into `void::Void` --> $DIR/uninhabited.rs:29:41 | @@ -64,6 +40,12 @@ LL | | lifetimes: true, LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` +error[E0080]: evaluation of constant value failed + --> $DIR/uninhabited.rs:41:9 + | +LL | assert!(false); + | ^^^^^^^^^^^^^^ evaluation panicked: assertion failed: false + error[E0277]: `()` cannot be safely transmuted into `yawning_void_enum::Void` --> $DIR/uninhabited.rs:71:41 | @@ -85,6 +67,12 @@ LL | | lifetimes: true, LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` +error[E0080]: evaluation of constant value failed + --> $DIR/uninhabited.rs:63:9 + | +LL | assert!(false); + | ^^^^^^^^^^^^^^ evaluation panicked: assertion failed: false + error[E0277]: `u128` cannot be safely transmuted into `DistantVoid` --> $DIR/uninhabited.rs:92:43 | @@ -106,6 +94,12 @@ LL | | lifetimes: true, LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` +error[E0080]: evaluation of constant value failed + --> $DIR/uninhabited.rs:87:9 + | +LL | assert!(false); + | ^^^^^^^^^^^^^^ evaluation panicked: assertion failed: false + error[E0277]: `Src` cannot be safely transmuted into `issue_126267::Error` --> $DIR/uninhabited.rs:108:42 | diff --git a/tests/ui/transmutability/unions/extension.rs b/tests/ui/transmutability/unions/extension.rs new file mode 100644 index 0000000000000..eb4dcd4dff3df --- /dev/null +++ b/tests/ui/transmutability/unions/extension.rs @@ -0,0 +1,12 @@ +#![crate_type = "lib"] +#![feature(transmutability)] +use std::mem::{Assume, MaybeUninit, TransmuteFrom}; + +pub fn is_maybe_transmutable() + where Dst: TransmuteFrom +{} + +fn extension() { + is_maybe_transmutable::<(), MaybeUninit>(); + is_maybe_transmutable::, [u8; 2]>(); //~ ERROR `MaybeUninit` cannot be safely transmuted into `[u8; 2]` +} diff --git a/tests/ui/transmutability/unions/extension.stderr b/tests/ui/transmutability/unions/extension.stderr new file mode 100644 index 0000000000000..c99e46f3d12b7 --- /dev/null +++ b/tests/ui/transmutability/unions/extension.stderr @@ -0,0 +1,17 @@ +error[E0277]: `MaybeUninit` cannot be safely transmuted into `[u8; 2]` + --> $DIR/extension.rs:11:46 + | +LL | is_maybe_transmutable::, [u8; 2]>(); + | ^^^^^^^ the size of `MaybeUninit` is smaller than the size of `[u8; 2]` + | +note: required by a bound in `is_maybe_transmutable` + --> $DIR/extension.rs:6:16 + | +LL | pub fn is_maybe_transmutable() + | --------------------- required by a bound in this function +LL | where Dst: TransmuteFrom + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/unions/init_as_uninit.rs b/tests/ui/transmutability/unions/init_as_uninit.rs new file mode 100644 index 0000000000000..d14eca800ef5d --- /dev/null +++ b/tests/ui/transmutability/unions/init_as_uninit.rs @@ -0,0 +1,26 @@ +//@ check-pass +// Regression test for issue #140337. +#![crate_type = "lib"] +#![feature(transmutability)] +#![allow(dead_code)] +use std::mem::{Assume, MaybeUninit, TransmuteFrom}; + +pub fn is_transmutable() +where + Dst: TransmuteFrom +{} + +#[derive(Copy, Clone)] +#[repr(u8)] +pub enum B0 { Value = 0 } + +#[derive(Copy, Clone)] +#[repr(u8)] +pub enum B1 { Value = 1 } + +fn main() { + is_transmutable::<(B0, B0), MaybeUninit<(B0, B0)>>(); + is_transmutable::<(B0, B0), MaybeUninit<(B0, B1)>>(); + is_transmutable::<(B0, B0), MaybeUninit<(B1, B0)>>(); + is_transmutable::<(B0, B0), MaybeUninit<(B1, B1)>>(); +} diff --git a/tests/ui/transmutability/unions/should_permit_intersecting_if_validity_is_assumed.rs b/tests/ui/transmutability/unions/should_permit_intersecting_if_validity_is_assumed.rs index 359ba51543981..24c6fa2e6ac0f 100644 --- a/tests/ui/transmutability/unions/should_permit_intersecting_if_validity_is_assumed.rs +++ b/tests/ui/transmutability/unions/should_permit_intersecting_if_validity_is_assumed.rs @@ -34,4 +34,19 @@ fn test() { assert::is_maybe_transmutable::(); assert::is_maybe_transmutable::(); + + #[repr(C)] + struct C { + a: Ox00, + b: Ox00, + } + + #[repr(C, align(2))] + struct D { + a: Ox00, + } + + assert::is_maybe_transmutable::(); + // With Assume::VALIDITY a padding byte can hold any value. + assert::is_maybe_transmutable::(); } diff --git a/tests/ui/transmute/auxiliary/unnecessary-transmute-path-remap-ice-140277-trans.rs b/tests/ui/transmute/auxiliary/unnecessary-transmute-path-remap-ice-140277-trans.rs new file mode 100644 index 0000000000000..0f273a6f536e0 --- /dev/null +++ b/tests/ui/transmute/auxiliary/unnecessary-transmute-path-remap-ice-140277-trans.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --remap-path-prefix=/=/non-existent +// helper for ../unnecessary-transmute-path-remap-ice-140277.rs + +#[macro_export] +macro_rules! transmute { + ($e:expr) => {{ + let e = $e; + std::mem::transmute(e) + }}; +} diff --git a/tests/ui/transmute/unnecessary-transmutation.fixed b/tests/ui/transmute/unnecessary-transmutation.fixed new file mode 100644 index 0000000000000..bf7d769348aaf --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.fixed @@ -0,0 +1,94 @@ +//@ run-rustfix +#![deny(unnecessary_transmutes)] +#![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] +use std::mem::transmute; + +pub fn bytes_at_home(x: u32) -> [u8; 4] { + unsafe { u32::to_ne_bytes(x) } + //~^ ERROR +} + +fn main() { + unsafe { + let x: u16 = u16::from_ne_bytes(*b"01"); + //~^ ERROR + let x: [u8; 2] = u16::to_ne_bytes(x); + //~^ ERROR + let x: u32 = u32::from_ne_bytes(*b"0123"); + //~^ ERROR + let x: [u8; 4] = u32::to_ne_bytes(x); + //~^ ERROR + let x: u64 = u64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let x: [u8; 8] = u64::to_ne_bytes(x); + //~^ ERROR + + let y: i16 = i16::from_ne_bytes(*b"01"); + //~^ ERROR + let y: [u8; 2] = i16::to_ne_bytes(y); + //~^ ERROR + let y: i32 = i32::from_ne_bytes(*b"0123"); + //~^ ERROR + let y: [u8; 4] = i32::to_ne_bytes(y); + //~^ ERROR + let y: i64 = i64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let y: [u8; 8] = i64::to_ne_bytes(y); + //~^ ERROR + + let z: f32 = f32::from_ne_bytes(*b"0123"); + //~^ ERROR + let z: [u8; 4] = f32::to_ne_bytes(z); + //~^ ERROR + let z: f64 = f64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let z: [u8; 8] = f64::to_ne_bytes(z); + //~^ ERROR + + let y: u32 = u32::from('🦀'); + //~^ ERROR + let y: char = char::from_u32_unchecked(y); + //~^ ERROR + let y: i32 = u32::from('🐱').cast_signed(); + //~^ ERROR + let y: char = char::from_u32_unchecked(i32::cast_unsigned(y)); + //~^ ERROR + + let x: u16 = i16::cast_unsigned(8i16); + //~^ ERROR + let x: i16 = u16::cast_signed(x); + //~^ ERROR + let x: u32 = i32::cast_unsigned(4i32); + //~^ ERROR + let x: i32 = u32::cast_signed(x); + //~^ ERROR + let x: u64 = i64::cast_unsigned(7i64); + //~^ ERROR + let x: i64 = u64::cast_signed(x); + //~^ ERROR + + let y: f32 = f32::from_bits(1u32); + //~^ ERROR + let y: u32 = f32::to_bits(y); + //~^ ERROR + let y: f64 = f64::from_bits(3u64); + //~^ ERROR + let y: u64 = f64::to_bits(2.0); + //~^ ERROR + + let y: f64 = f64::from_bits(i64::cast_unsigned(1i64)); + //~^ ERROR + let y: i64 = f64::to_bits(1f64).cast_signed(); + //~^ ERROR + + let z: bool = (1u8 == 1); + //~^ ERROR + let z: u8 = (z) as u8; + //~^ ERROR + + let z: bool = transmute(1i8); + // no error! + let z: i8 = (z) as i8; + //~^ ERROR + } +} diff --git a/tests/ui/transmute/unnecessary-transmutation.rs b/tests/ui/transmute/unnecessary-transmutation.rs new file mode 100644 index 0000000000000..b9de529f1cccd --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.rs @@ -0,0 +1,94 @@ +//@ run-rustfix +#![deny(unnecessary_transmutes)] +#![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] +use std::mem::transmute; + +pub fn bytes_at_home(x: u32) -> [u8; 4] { + unsafe { transmute(x) } + //~^ ERROR +} + +fn main() { + unsafe { + let x: u16 = transmute(*b"01"); + //~^ ERROR + let x: [u8; 2] = transmute(x); + //~^ ERROR + let x: u32 = transmute(*b"0123"); + //~^ ERROR + let x: [u8; 4] = transmute(x); + //~^ ERROR + let x: u64 = transmute(*b"feriscat"); + //~^ ERROR + let x: [u8; 8] = transmute(x); + //~^ ERROR + + let y: i16 = transmute(*b"01"); + //~^ ERROR + let y: [u8; 2] = transmute(y); + //~^ ERROR + let y: i32 = transmute(*b"0123"); + //~^ ERROR + let y: [u8; 4] = transmute(y); + //~^ ERROR + let y: i64 = transmute(*b"feriscat"); + //~^ ERROR + let y: [u8; 8] = transmute(y); + //~^ ERROR + + let z: f32 = transmute(*b"0123"); + //~^ ERROR + let z: [u8; 4] = transmute(z); + //~^ ERROR + let z: f64 = transmute(*b"feriscat"); + //~^ ERROR + let z: [u8; 8] = transmute(z); + //~^ ERROR + + let y: u32 = transmute('🦀'); + //~^ ERROR + let y: char = transmute(y); + //~^ ERROR + let y: i32 = transmute('🐱'); + //~^ ERROR + let y: char = transmute(y); + //~^ ERROR + + let x: u16 = transmute(8i16); + //~^ ERROR + let x: i16 = transmute(x); + //~^ ERROR + let x: u32 = transmute(4i32); + //~^ ERROR + let x: i32 = transmute(x); + //~^ ERROR + let x: u64 = transmute(7i64); + //~^ ERROR + let x: i64 = transmute(x); + //~^ ERROR + + let y: f32 = transmute(1u32); + //~^ ERROR + let y: u32 = transmute(y); + //~^ ERROR + let y: f64 = transmute(3u64); + //~^ ERROR + let y: u64 = transmute(2.0); + //~^ ERROR + + let y: f64 = transmute(1i64); + //~^ ERROR + let y: i64 = transmute(1f64); + //~^ ERROR + + let z: bool = transmute(1u8); + //~^ ERROR + let z: u8 = transmute(z); + //~^ ERROR + + let z: bool = transmute(1i8); + // no error! + let z: i8 = transmute(z); + //~^ ERROR + } +} diff --git a/tests/ui/transmute/unnecessary-transmutation.stderr b/tests/ui/transmute/unnecessary-transmutation.stderr new file mode 100644 index 0000000000000..a19f1bebf16fd --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.stderr @@ -0,0 +1,261 @@ +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:7:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^^^^ help: replace this with: `u32::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +note: the lint level is defined here + --> $DIR/unnecessary-transmutation.rs:2:9 + | +LL | #![deny(unnecessary_transmutes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:13:22 + | +LL | let x: u16 = transmute(*b"01"); + | ^^^^^^^^^^^^^^^^^ help: replace this with: `u16::from_ne_bytes(*b"01")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:15:26 + | +LL | let x: [u8; 2] = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u16::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:17:22 + | +LL | let x: u32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `u32::from_ne_bytes(*b"0123")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:19:26 + | +LL | let x: [u8; 4] = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u32::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:21:22 + | +LL | let x: u64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `u64::from_ne_bytes(*b"feriscat")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:23:26 + | +LL | let x: [u8; 8] = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u64::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:26:22 + | +LL | let y: i16 = transmute(*b"01"); + | ^^^^^^^^^^^^^^^^^ help: replace this with: `i16::from_ne_bytes(*b"01")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:28:26 + | +LL | let y: [u8; 2] = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `i16::to_ne_bytes(y)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:30:22 + | +LL | let y: i32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `i32::from_ne_bytes(*b"0123")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:32:26 + | +LL | let y: [u8; 4] = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `i32::to_ne_bytes(y)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:34:22 + | +LL | let y: i64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `i64::from_ne_bytes(*b"feriscat")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:36:26 + | +LL | let y: [u8; 8] = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `i64::to_ne_bytes(y)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:39:22 + | +LL | let z: f32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `f32::from_ne_bytes(*b"0123")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:41:26 + | +LL | let z: [u8; 4] = transmute(z); + | ^^^^^^^^^^^^ help: replace this with: `f32::to_ne_bytes(z)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:43:22 + | +LL | let z: f64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `f64::from_ne_bytes(*b"feriscat")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:45:26 + | +LL | let z: [u8; 8] = transmute(z); + | ^^^^^^^^^^^^ help: replace this with: `f64::to_ne_bytes(z)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:48:22 + | +LL | let y: u32 = transmute('🦀'); + | ^^^^^^^^^^^^^^^ help: replace this with: `u32::from('🦀')` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:50:23 + | +LL | let y: char = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `char::from_u32_unchecked(y)` + | + = help: consider `char::from_u32(…).unwrap()` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:52:22 + | +LL | let y: i32 = transmute('🐱'); + | ^^^^^^^^^^^^^^^ help: replace this with: `u32::from('🐱').cast_signed()` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:54:23 + | +LL | let y: char = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `char::from_u32_unchecked(i32::cast_unsigned(y))` + | + = help: consider `char::from_u32(i32::cast_unsigned(…)).unwrap()` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:57:22 + | +LL | let x: u16 = transmute(8i16); + | ^^^^^^^^^^^^^^^ help: replace this with: `i16::cast_unsigned(8i16)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:59:22 + | +LL | let x: i16 = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u16::cast_signed(x)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:61:22 + | +LL | let x: u32 = transmute(4i32); + | ^^^^^^^^^^^^^^^ help: replace this with: `i32::cast_unsigned(4i32)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:63:22 + | +LL | let x: i32 = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u32::cast_signed(x)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:65:22 + | +LL | let x: u64 = transmute(7i64); + | ^^^^^^^^^^^^^^^ help: replace this with: `i64::cast_unsigned(7i64)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:67:22 + | +LL | let x: i64 = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u64::cast_signed(x)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:70:22 + | +LL | let y: f32 = transmute(1u32); + | ^^^^^^^^^^^^^^^ help: replace this with: `f32::from_bits(1u32)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:72:22 + | +LL | let y: u32 = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `f32::to_bits(y)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:74:22 + | +LL | let y: f64 = transmute(3u64); + | ^^^^^^^^^^^^^^^ help: replace this with: `f64::from_bits(3u64)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:76:22 + | +LL | let y: u64 = transmute(2.0); + | ^^^^^^^^^^^^^^ help: replace this with: `f64::to_bits(2.0)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:79:22 + | +LL | let y: f64 = transmute(1i64); + | ^^^^^^^^^^^^^^^ help: replace this with: `f64::from_bits(i64::cast_unsigned(1i64))` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:81:22 + | +LL | let y: i64 = transmute(1f64); + | ^^^^^^^^^^^^^^^ help: replace this with: `f64::to_bits(1f64).cast_signed()` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:84:23 + | +LL | let z: bool = transmute(1u8); + | ^^^^^^^^^^^^^^ help: replace this with: `(1u8 == 1)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:86:21 + | +LL | let z: u8 = transmute(z); + | ^^^^^^^^^^^^ help: replace this with: `(z) as u8` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:91:21 + | +LL | let z: i8 = transmute(z); + | ^^^^^^^^^^^^ help: replace this with: `(z) as i8` + +error: aborting due to 36 previous errors + diff --git a/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs b/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs new file mode 100644 index 0000000000000..756ce7b3d5074 --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs @@ -0,0 +1,10 @@ +//@ aux-crate: zerocopy=unnecessary-transmute-path-remap-ice-140277-trans.rs +//@ check-pass +// tests for a regression in linting for unnecessary transmutes +// where a span was inacessible for snippet procuring, +// when remap-path-prefix was set, causing a panic. + +fn bytes_at_home(x: [u8; 4]) -> u32 { + unsafe { zerocopy::transmute!(x) } +} +fn main() {} diff --git a/tests/ui/treat-err-as-bug/err.rs b/tests/ui/treat-err-as-bug/err.rs index 9f0e293b4cb1c..7868c06fee1c5 100644 --- a/tests/ui/treat-err-as-bug/err.rs +++ b/tests/ui/treat-err-as-bug/err.rs @@ -1,7 +1,5 @@ //@ compile-flags: -Ztreat-err-as-bug //@ failure-status: 101 -//@ error-pattern: aborting due to `-Z treat-err-as-bug=1` -//@ error-pattern: [eval_static_initializer] evaluating initializer of static `C` //@ normalize-stderr: "note: .*\n\n" -> "" //@ normalize-stderr: "thread 'rustc' panicked.*:\n.*\n" -> "" //@ rustc-env:RUST_BACKTRACE=0 @@ -10,3 +8,6 @@ pub static C: u32 = 0 - 1; //~^ ERROR could not evaluate static initializer + +//~? RAW aborting due to `-Z treat-err-as-bug=1` +//~? RAW [eval_static_initializer] evaluating initializer of static `C` diff --git a/tests/ui/treat-err-as-bug/err.stderr b/tests/ui/treat-err-as-bug/err.stderr index df5fed3fb8e94..2049df22000a1 100644 --- a/tests/ui/treat-err-as-bug/err.stderr +++ b/tests/ui/treat-err-as-bug/err.stderr @@ -1,5 +1,5 @@ error: internal compiler error[E0080]: could not evaluate static initializer - --> $DIR/err.rs:11:21 + --> $DIR/err.rs:9:21 | LL | pub static C: u32 = 0 - 1; | ^^^^^ attempt to compute `0_u32 - 1_u32`, which would overflow diff --git a/tests/ui/treat-err-as-bug/panic-causes-oom-112708.rs b/tests/ui/treat-err-as-bug/panic-causes-oom-112708.rs index 0b75bb23faff9..3f963888c300d 100644 --- a/tests/ui/treat-err-as-bug/panic-causes-oom-112708.rs +++ b/tests/ui/treat-err-as-bug/panic-causes-oom-112708.rs @@ -1,10 +1,11 @@ //@ compile-flags: -Ztreat-err-as-bug //@ dont-check-failure-status -//@ error-pattern: aborting due to `-Z treat-err-as-bug=1` //@ dont-check-compiler-stderr //@ rustc-env:RUST_BACKTRACE=0 fn main() { #[deny(while_true)] - while true {} + while true {} //~ ERROR denote infinite loops with `loop { ... }` } + +//~? RAW aborting due to `-Z treat-err-as-bug=1` diff --git a/tests/ui/treat-err-as-bug/span_delayed_bug.rs b/tests/ui/treat-err-as-bug/span_delayed_bug.rs index 296bdd7a12d9b..4681a8fc1ee0e 100644 --- a/tests/ui/treat-err-as-bug/span_delayed_bug.rs +++ b/tests/ui/treat-err-as-bug/span_delayed_bug.rs @@ -1,12 +1,13 @@ //@ compile-flags: -Ztreat-err-as-bug -Zeagerly-emit-delayed-bugs //@ failure-status: 101 -//@ error-pattern: aborting due to `-Z treat-err-as-bug=1` -//@ error-pattern: [trigger_delayed_bug] triggering a delayed bug for testing incremental //@ normalize-stderr: "note: .*\n\n" -> "" //@ normalize-stderr: "thread 'rustc' panicked.*:\n.*\n" -> "" //@ rustc-env:RUST_BACKTRACE=0 #![feature(rustc_attrs)] -#[rustc_error(delayed_bug_from_inside_query)] -fn main() {} +#[rustc_delayed_bug_from_inside_query] +fn main() {} //~ ERROR delayed bug triggered by #[rustc_delayed_bug_from_inside_query] + +//~? RAW aborting due to `-Z treat-err-as-bug=1` +//~? RAW [trigger_delayed_bug] triggering a delayed bug for testing incremental diff --git a/tests/ui/treat-err-as-bug/span_delayed_bug.stderr b/tests/ui/treat-err-as-bug/span_delayed_bug.stderr index aec1b89c7666e..88983e95cee77 100644 --- a/tests/ui/treat-err-as-bug/span_delayed_bug.stderr +++ b/tests/ui/treat-err-as-bug/span_delayed_bug.stderr @@ -1,5 +1,5 @@ -error: internal compiler error: delayed bug triggered by #[rustc_error(delayed_bug_from_inside_query)] - --> $DIR/span_delayed_bug.rs:12:1 +error: internal compiler error: delayed bug triggered by #[rustc_delayed_bug_from_inside_query] + --> $DIR/span_delayed_bug.rs:10:1 | LL | fn main() {} | ^^^^^^^^^ diff --git a/tests/ui/try-block/issue-45124.rs b/tests/ui/try-block/issue-45124.rs index e9e0e767efa25..26e1736faacaa 100644 --- a/tests/ui/try-block/issue-45124.rs +++ b/tests/ui/try-block/issue-45124.rs @@ -1,6 +1,6 @@ //@ run-pass #![allow(unreachable_code)] -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block-bad-lifetime.rs b/tests/ui/try-block/try-block-bad-lifetime.rs index bfff757a2df63..9b45b0d955947 100644 --- a/tests/ui/try-block/try-block-bad-lifetime.rs +++ b/tests/ui/try-block/try-block-bad-lifetime.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block-bad-type.rs b/tests/ui/try-block/try-block-bad-type.rs index 71eb832dd4eb2..00cd0af127c87 100644 --- a/tests/ui/try-block/try-block-bad-type.rs +++ b/tests/ui/try-block/try-block-bad-type.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block-catch.rs b/tests/ui/try-block/try-block-catch.rs index c3aa442ba6635..170d5bab3e500 100644 --- a/tests/ui/try-block/try-block-catch.rs +++ b/tests/ui/try-block/try-block-catch.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block-in-edition2015.rs b/tests/ui/try-block/try-block-in-edition2015.rs index 423269df12d68..4ebe2d31b81d4 100644 --- a/tests/ui/try-block/try-block-in-edition2015.rs +++ b/tests/ui/try-block/try-block-in-edition2015.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2015 +//@ edition: 2015 pub fn main() { let try_result: Option<_> = try { diff --git a/tests/ui/try-block/try-block-in-match-arm.rs b/tests/ui/try-block/try-block-in-match-arm.rs index cecbf724916ca..703b1c54bbb1b 100644 --- a/tests/ui/try-block/try-block-in-match-arm.rs +++ b/tests/ui/try-block/try-block-in-match-arm.rs @@ -1,5 +1,5 @@ //@ check-pass -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block-in-match.rs b/tests/ui/try-block/try-block-in-match.rs index 5c62f41efdba3..63a32000f625e 100644 --- a/tests/ui/try-block/try-block-in-match.rs +++ b/tests/ui/try-block/try-block-in-match.rs @@ -1,5 +1,5 @@ //@ run-pass -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block-in-return.rs b/tests/ui/try-block/try-block-in-return.rs index ee5ca696b6d6c..7dc023b90dbcd 100644 --- a/tests/ui/try-block/try-block-in-return.rs +++ b/tests/ui/try-block/try-block-in-return.rs @@ -1,5 +1,5 @@ //@ run-pass -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block-in-while.rs b/tests/ui/try-block/try-block-in-while.rs index 88a97136c5947..d6232546cea05 100644 --- a/tests/ui/try-block/try-block-in-while.rs +++ b/tests/ui/try-block/try-block-in-while.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block-maybe-bad-lifetime.rs b/tests/ui/try-block/try-block-maybe-bad-lifetime.rs index 52ec0c44a059c..14adee8944fa2 100644 --- a/tests/ui/try-block/try-block-maybe-bad-lifetime.rs +++ b/tests/ui/try-block/try-block-maybe-bad-lifetime.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block-opt-init.rs b/tests/ui/try-block/try-block-opt-init.rs index fbe7f90d03039..55ac5b4a37cc3 100644 --- a/tests/ui/try-block/try-block-opt-init.rs +++ b/tests/ui/try-block/try-block-opt-init.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block-type-error.rs b/tests/ui/try-block/try-block-type-error.rs index 79cdb7a2e48d7..4e482b40140d5 100644 --- a/tests/ui/try-block/try-block-type-error.rs +++ b/tests/ui/try-block/try-block-type-error.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block-unreachable-code-lint.rs b/tests/ui/try-block/try-block-unreachable-code-lint.rs index 62c74b76d59c0..9135882a57b28 100644 --- a/tests/ui/try-block/try-block-unreachable-code-lint.rs +++ b/tests/ui/try-block/try-block-unreachable-code-lint.rs @@ -1,6 +1,6 @@ // Test unreachable_code lint for `try {}` block ok-wrapping. See issues #54165, #63324. -//@ compile-flags: --edition 2018 +//@ edition: 2018 //@ check-pass #![feature(try_blocks)] #![warn(unreachable_code)] diff --git a/tests/ui/try-block/try-block-unused-delims.fixed b/tests/ui/try-block/try-block-unused-delims.fixed index 348eb8f796567..4769c45d38ccd 100644 --- a/tests/ui/try-block/try-block-unused-delims.fixed +++ b/tests/ui/try-block/try-block-unused-delims.fixed @@ -1,5 +1,5 @@ //@ check-pass -//@ compile-flags: --edition 2018 +//@ edition: 2018 //@ run-rustfix #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block-unused-delims.rs b/tests/ui/try-block/try-block-unused-delims.rs index f119e1074f6e5..0520d1d620f5a 100644 --- a/tests/ui/try-block/try-block-unused-delims.rs +++ b/tests/ui/try-block/try-block-unused-delims.rs @@ -1,5 +1,5 @@ //@ check-pass -//@ compile-flags: --edition 2018 +//@ edition: 2018 //@ run-rustfix #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-block.rs b/tests/ui/try-block/try-block.rs index 7520cbaad378e..3fa0d2ba0f246 100644 --- a/tests/ui/try-block/try-block.rs +++ b/tests/ui/try-block/try-block.rs @@ -2,7 +2,7 @@ #![allow(non_camel_case_types)] #![allow(dead_code)] -//@ compile-flags: --edition 2018 +//@ edition: 2018 #![feature(try_blocks)] diff --git a/tests/ui/try-block/try-is-identifier-edition2015.rs b/tests/ui/try-block/try-is-identifier-edition2015.rs index 54bd049442fcc..99f4d2068599f 100644 --- a/tests/ui/try-block/try-is-identifier-edition2015.rs +++ b/tests/ui/try-block/try-is-identifier-edition2015.rs @@ -1,7 +1,7 @@ //@ run-pass #![allow(non_camel_case_types)] -//@ compile-flags: --edition 2015 +//@ edition: 2015 fn main() { let try = 2; diff --git a/tests/ui/tuple/tuple-arity-mismatch.rs b/tests/ui/tuple/tuple-arity-mismatch.rs index f1e525c93e17f..0b7c9deec9f38 100644 --- a/tests/ui/tuple/tuple-arity-mismatch.rs +++ b/tests/ui/tuple/tuple-arity-mismatch.rs @@ -1,17 +1,19 @@ // Issue #6155 +//@ dont-require-annotations: NOTE + fn first((value, _): (isize, f64)) -> isize { value } fn main() { let y = first ((1,2.0,3)); //~^ ERROR mismatched types - //~| expected tuple `(isize, f64)` - //~| found tuple `(isize, f64, {integer})` - //~| expected a tuple with 2 elements, found one with 3 elements + //~| NOTE expected tuple `(isize, f64)` + //~| NOTE found tuple `(isize, f64, {integer})` + //~| NOTE expected a tuple with 2 elements, found one with 3 elements let y = first ((1,)); //~^ ERROR mismatched types - //~| expected tuple `(isize, f64)` - //~| found tuple `(isize,)` - //~| expected a tuple with 2 elements, found one with 1 element + //~| NOTE expected tuple `(isize, f64)` + //~| NOTE found tuple `(isize,)` + //~| NOTE expected a tuple with 2 elements, found one with 1 element } diff --git a/tests/ui/tuple/tuple-arity-mismatch.stderr b/tests/ui/tuple/tuple-arity-mismatch.stderr index fff7be987f2e8..49dd98b6e7370 100644 --- a/tests/ui/tuple/tuple-arity-mismatch.stderr +++ b/tests/ui/tuple/tuple-arity-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/tuple-arity-mismatch.rs:6:20 + --> $DIR/tuple-arity-mismatch.rs:8:20 | LL | let y = first ((1,2.0,3)); | ----- ^^^^^^^^^ expected a tuple with 2 elements, found one with 3 elements @@ -9,13 +9,13 @@ LL | let y = first ((1,2.0,3)); = note: expected tuple `(isize, f64)` found tuple `(isize, f64, {integer})` note: function defined here - --> $DIR/tuple-arity-mismatch.rs:3:4 + --> $DIR/tuple-arity-mismatch.rs:5:4 | LL | fn first((value, _): (isize, f64)) -> isize { value } | ^^^^^ ------------------------ error[E0308]: mismatched types - --> $DIR/tuple-arity-mismatch.rs:12:20 + --> $DIR/tuple-arity-mismatch.rs:14:20 | LL | let y = first ((1,)); | ----- ^^^^ expected a tuple with 2 elements, found one with 1 element @@ -25,7 +25,7 @@ LL | let y = first ((1,)); = note: expected tuple `(isize, f64)` found tuple `(isize,)` note: function defined here - --> $DIR/tuple-arity-mismatch.rs:3:4 + --> $DIR/tuple-arity-mismatch.rs:5:4 | LL | fn first((value, _): (isize, f64)) -> isize { value } | ^^^^^ ------------------------ diff --git a/tests/ui/type-alias-impl-trait/argument-types.rs b/tests/ui/type-alias-impl-trait/argument-types.rs index 7382d4c78c7df..ea87bcb3e4569 100644 --- a/tests/ui/type-alias-impl-trait/argument-types.rs +++ b/tests/ui/type-alias-impl-trait/argument-types.rs @@ -2,20 +2,19 @@ #![allow(dead_code)] //@ check-pass -mod foo { - use std::fmt::Debug; +use std::fmt::Debug; - pub type Foo = impl Debug; +pub type Foo = impl Debug; - fn foo1(mut x: Foo) { - x = 22_u32; - } +#[define_opaque(Foo)] +fn foo1(mut x: Foo) { + x = 22_u32; +} - pub fn foo_value() -> Foo { - 11_u32 - } +#[define_opaque(Foo)] +pub fn foo_value() -> Foo { + 11_u32 } -use foo::*; fn foo2(mut x: Foo) { // no constraint on x diff --git a/tests/ui/type-alias-impl-trait/assoc-projection-ice.rs b/tests/ui/type-alias-impl-trait/assoc-projection-ice.rs index 9dcbc75db3f12..97c1c1b64cc4a 100644 --- a/tests/ui/type-alias-impl-trait/assoc-projection-ice.rs +++ b/tests/ui/type-alias-impl-trait/assoc-projection-ice.rs @@ -2,23 +2,21 @@ //@ build-pass -mod helper { - pub trait T { - type Item; - } +pub trait T { + type Item; +} - pub type Alias<'a> = impl T; +pub type Alias<'a> = impl T; - struct S; - impl<'a> T for &'a S { - type Item = &'a (); - } +struct S; +impl<'a> T for &'a S { + type Item = &'a (); +} - pub fn filter_positive<'a>() -> Alias<'a> { - &S - } +#[define_opaque(Alias)] +pub fn filter_positive<'a>() -> Alias<'a> { + &S } -use helper::*; fn with_positive(fun: impl Fn(Alias<'_>)) { fun(filter_positive()); diff --git a/tests/ui/type-alias-impl-trait/associated-type-alias-impl-trait.rs b/tests/ui/type-alias-impl-trait/associated-type-alias-impl-trait.rs index a1185cd5ba88f..6761835be896f 100644 --- a/tests/ui/type-alias-impl-trait/associated-type-alias-impl-trait.rs +++ b/tests/ui/type-alias-impl-trait/associated-type-alias-impl-trait.rs @@ -15,9 +15,11 @@ type Helper = impl Bar; impl Foo for i32 { type Assoc = Helper; + #[define_opaque(Helper)] fn foo() -> Helper { Dummy } + #[define_opaque(Helper)] fn bar() -> Helper { Dummy } diff --git a/tests/ui/type-alias-impl-trait/auto-trait-leakage.rs b/tests/ui/type-alias-impl-trait/auto-trait-leakage.rs index a03a146d0418c..cf385223ce45e 100644 --- a/tests/ui/type-alias-impl-trait/auto-trait-leakage.rs +++ b/tests/ui/type-alias-impl-trait/auto-trait-leakage.rs @@ -3,16 +3,15 @@ #![feature(type_alias_impl_trait)] #![allow(dead_code)] -mod m { - pub(crate) type Foo = impl std::fmt::Debug; +pub(crate) type Foo = impl std::fmt::Debug; - pub(crate) fn foo() -> Foo { - 22_u32 - } +#[define_opaque(Foo)] +pub(crate) fn foo() -> Foo { + 22_u32 } fn is_send(_: T) {} fn main() { - is_send(m::foo()); + is_send(foo()); } diff --git a/tests/ui/type-alias-impl-trait/auto-trait-leakage2.rs b/tests/ui/type-alias-impl-trait/auto-trait-leakage2.rs index fc89b0e870e39..0b60640272b59 100644 --- a/tests/ui/type-alias-impl-trait/auto-trait-leakage2.rs +++ b/tests/ui/type-alias-impl-trait/auto-trait-leakage2.rs @@ -1,24 +1,23 @@ #![feature(type_alias_impl_trait)] #![allow(dead_code)] -mod m { - use std::rc::Rc; +use std::rc::Rc; - type Foo = impl std::fmt::Debug; //~ NOTE appears within the type - //~^ within this `Foo` - //~| expansion of desugaring +type Foo = impl std::fmt::Debug; //~ NOTE appears within the type +//~^ NOTE within this `Foo` +//~| NOTE expansion of desugaring - pub fn foo() -> Foo { - Rc::new(22_u32) - } +#[define_opaque(Foo)] +pub fn foo() -> Foo { + Rc::new(22_u32) } fn is_send(_: T) {} -//~^ required by this bound -//~| required by a bound +//~^ NOTE required by this bound +//~| NOTE required by a bound fn main() { - is_send(m::foo()); + is_send(foo()); //~^ ERROR: `Rc` cannot be sent between threads safely [E0277] //~| NOTE cannot be sent //~| NOTE required by a bound diff --git a/tests/ui/type-alias-impl-trait/auto-trait-leakage2.stderr b/tests/ui/type-alias-impl-trait/auto-trait-leakage2.stderr index 2ed918eca1714..d2db468b51958 100644 --- a/tests/ui/type-alias-impl-trait/auto-trait-leakage2.stderr +++ b/tests/ui/type-alias-impl-trait/auto-trait-leakage2.stderr @@ -1,22 +1,22 @@ error[E0277]: `Rc` cannot be sent between threads safely - --> $DIR/auto-trait-leakage2.rs:21:13 + --> $DIR/auto-trait-leakage2.rs:20:13 | -LL | type Foo = impl std::fmt::Debug; - | -------------------- within this `Foo` +LL | type Foo = impl std::fmt::Debug; + | -------------------- within this `Foo` ... -LL | is_send(m::foo()); - | ------- ^^^^^^^^ `Rc` cannot be sent between threads safely +LL | is_send(foo()); + | ------- ^^^^^ `Rc` cannot be sent between threads safely | | | required by a bound introduced by this call | = help: within `Foo`, the trait `Send` is not implemented for `Rc` note: required because it appears within the type `Foo` - --> $DIR/auto-trait-leakage2.rs:7:16 + --> $DIR/auto-trait-leakage2.rs:6:12 | -LL | type Foo = impl std::fmt::Debug; - | ^^^^^^^^^^^^^^^^^^^^ +LL | type Foo = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `is_send` - --> $DIR/auto-trait-leakage2.rs:16:15 + --> $DIR/auto-trait-leakage2.rs:15:15 | LL | fn is_send(_: T) {} | ^^^^ required by this bound in `is_send` diff --git a/tests/ui/type-alias-impl-trait/auto-trait-leakage3.rs b/tests/ui/type-alias-impl-trait/auto-trait-leakage3.rs index cad75cffe05f6..3e0bd3b6521c8 100644 --- a/tests/ui/type-alias-impl-trait/auto-trait-leakage3.rs +++ b/tests/ui/type-alias-impl-trait/auto-trait-leakage3.rs @@ -1,17 +1,17 @@ #![feature(type_alias_impl_trait)] #![allow(dead_code)] -// FIXME This should compile, but it currently doesn't +//@ check-pass mod m { pub type Foo = impl std::fmt::Debug; + #[define_opaque(Foo)] pub fn foo() -> Foo { 22_u32 } pub fn bar() { is_send(foo()); - //~^ ERROR: cannot check whether the hidden type of `auto_trait_leakage3[211d]::m::Foo::{opaque#0} } fn is_send(_: T) {} diff --git a/tests/ui/type-alias-impl-trait/auto-trait-leakage3.stderr b/tests/ui/type-alias-impl-trait/auto-trait-leakage3.stderr deleted file mode 100644 index 6bdc76aab4503..0000000000000 --- a/tests/ui/type-alias-impl-trait/auto-trait-leakage3.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: cannot check whether the hidden type of `auto_trait_leakage3[211d]::m::Foo::{opaque#0}` satisfies auto traits - --> $DIR/auto-trait-leakage3.rs:13:17 - | -LL | is_send(foo()); - | ------- ^^^^^ - | | - | required by a bound introduced by this call - | - = note: fetching the hidden types of an opaque inside of the defining scope is not supported. You can try moving the opaque type and the item that actually registers a hidden type into a new submodule -note: opaque type is declared here - --> $DIR/auto-trait-leakage3.rs:7:20 - | -LL | pub type Foo = impl std::fmt::Debug; - | ^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `is_send` - --> $DIR/auto-trait-leakage3.rs:17:19 - | -LL | fn is_send(_: T) {} - | ^^^^ required by this bound in `is_send` - -error: aborting due to 1 previous error - diff --git a/tests/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs b/tests/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs index e7bca2231de4b..c60fd0b6798f6 100644 --- a/tests/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs +++ b/tests/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs @@ -5,6 +5,7 @@ pub type Foo = impl std::fmt::Debug; +#[define_opaque(Foo)] pub fn foo() -> Foo { 5 } diff --git a/tests/ui/type-alias-impl-trait/auxiliary/drop-shim-relates-opaque-aux.rs b/tests/ui/type-alias-impl-trait/auxiliary/drop-shim-relates-opaque-aux.rs index 54a22510066fe..3c823d3e5d279 100644 --- a/tests/ui/type-alias-impl-trait/auxiliary/drop-shim-relates-opaque-aux.rs +++ b/tests/ui/type-alias-impl-trait/auxiliary/drop-shim-relates-opaque-aux.rs @@ -3,6 +3,7 @@ #![feature(type_alias_impl_trait)] type Tait = impl Sized; +#[define_opaque(Tait)] fn _constrain() -> Tait {} struct WrapperWithDrop(T); diff --git a/tests/ui/type-alias-impl-trait/bad-tait-no-substs.rs b/tests/ui/type-alias-impl-trait/bad-tait-no-substs.rs index 4b2ee344aa329..5c93bd14d3252 100644 --- a/tests/ui/type-alias-impl-trait/bad-tait-no-substs.rs +++ b/tests/ui/type-alias-impl-trait/bad-tait-no-substs.rs @@ -8,11 +8,10 @@ pub enum UninhabitedVariants { Tuple(Alias), //~^ ERROR missing lifetime specifier //~| ERROR missing generics - //~| ERROR non-defining opaque type use in defining scope } +#[define_opaque(Alias)] fn uwu(x: UninhabitedVariants) { - //~^ ERROR item does not constrain match x {} //~^ ERROR non-exhaustive patterns } diff --git a/tests/ui/type-alias-impl-trait/bad-tait-no-substs.stderr b/tests/ui/type-alias-impl-trait/bad-tait-no-substs.stderr index 38fbff9d59dae..72fae0173cbb5 100644 --- a/tests/ui/type-alias-impl-trait/bad-tait-no-substs.stderr +++ b/tests/ui/type-alias-impl-trait/bad-tait-no-substs.stderr @@ -26,33 +26,8 @@ help: add missing generic argument LL | Tuple(Alias), | +++ -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/bad-tait-no-substs.rs:8:11 - | -LL | Tuple(Alias), - | ^^^^^ argument `'_` is not a generic parameter - | -note: for this opaque type - --> $DIR/bad-tait-no-substs.rs:5:21 - | -LL | type Alias<'a, U> = impl Trait; - | ^^^^^^^^^^^^^ - -error: item does not constrain `Alias::{opaque#0}`, but has it in its signature - --> $DIR/bad-tait-no-substs.rs:14:4 - | -LL | fn uwu(x: UninhabitedVariants) { - | ^^^ - | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/bad-tait-no-substs.rs:5:21 - | -LL | type Alias<'a, U> = impl Trait; - | ^^^^^^^^^^^^^ - error[E0004]: non-exhaustive patterns: `UninhabitedVariants::Tuple(_)` not covered - --> $DIR/bad-tait-no-substs.rs:16:11 + --> $DIR/bad-tait-no-substs.rs:15:11 | LL | match x {} | ^ pattern `UninhabitedVariants::Tuple(_)` not covered @@ -72,7 +47,7 @@ LL + UninhabitedVariants::Tuple(_) => todo!(), LL ~ } | -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0004, E0106, E0107, E0792. +Some errors have detailed explanations: E0004, E0106, E0107. For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/type-alias-impl-trait/bivariant-duplicate-lifetime-unconstrained.rs b/tests/ui/type-alias-impl-trait/bivariant-duplicate-lifetime-unconstrained.rs index 3b83b2e544b01..4dba8d7b2308e 100644 --- a/tests/ui/type-alias-impl-trait/bivariant-duplicate-lifetime-unconstrained.rs +++ b/tests/ui/type-alias-impl-trait/bivariant-duplicate-lifetime-unconstrained.rs @@ -11,6 +11,7 @@ type Opaque<'a> = impl Sized + 'a; +#[define_opaque(Opaque)] fn test<'a>() -> Opaque<'a> { let _: () = test::<'a>(); } diff --git a/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.rs b/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.rs index df589473a8460..e714aca812bc7 100644 --- a/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.rs +++ b/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.rs @@ -9,7 +9,7 @@ fn dyn_hoops() -> dyn for<'a> Iterator> { } pub fn main() { - //~^ ERROR item does not constrain `Opaque::{opaque#0}`, but has it in its signature + //~^ ERROR item does not constrain `Opaque::{opaque#0}` type Opaque = impl Sized; fn define() -> Opaque { let x: Opaque = dyn_hoops::<()>(); diff --git a/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.stderr b/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.stderr index 59d9ff86c6e09..7219fda4772a2 100644 --- a/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.stderr +++ b/tests/ui/type-alias-impl-trait/bound-lifetime-through-dyn-trait.stderr @@ -10,14 +10,14 @@ note: lifetime declared here LL | fn dyn_hoops() -> dyn for<'a> Iterator> { | ^^ -error: item does not constrain `Opaque::{opaque#0}`, but has it in its signature +error: item does not constrain `Opaque::{opaque#0}` --> $DIR/bound-lifetime-through-dyn-trait.rs:11:8 | LL | pub fn main() { | ^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/bound-lifetime-through-dyn-trait.rs:13:19 | LL | type Opaque = impl Sized; diff --git a/tests/ui/type-alias-impl-trait/bound_reduction.rs b/tests/ui/type-alias-impl-trait/bound_reduction.rs index 74012e34e9214..5966b5cc81598 100644 --- a/tests/ui/type-alias-impl-trait/bound_reduction.rs +++ b/tests/ui/type-alias-impl-trait/bound_reduction.rs @@ -10,6 +10,7 @@ type Foo = impl std::fmt::Debug; trait Trait {} +#[define_opaque(Foo)] fn foo_desugared {} impl Trait for () {} +#[define_opaque(Foo)] fn foo_desugared(_: T) -> Foo { - //~^ ERROR non-defining opaque type use - () //~^ ERROR expected generic type parameter, found `::Assoc` + () } diff --git a/tests/ui/type-alias-impl-trait/bound_reduction2.stderr b/tests/ui/type-alias-impl-trait/bound_reduction2.stderr index 14f9dbbdb4e93..53b20d61025f0 100644 --- a/tests/ui/type-alias-impl-trait/bound_reduction2.stderr +++ b/tests/ui/type-alias-impl-trait/bound_reduction2.stderr @@ -1,24 +1,12 @@ -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/bound_reduction2.rs:15:46 - | -LL | fn foo_desugared(_: T) -> Foo { - | ^^^^^^^^^^^^^ argument `::Assoc` is not a generic parameter - | -note: for this opaque type - --> $DIR/bound_reduction2.rs:9:15 - | -LL | type Foo = impl Trait; - | ^^^^^^^^^^^^^ - error[E0792]: expected generic type parameter, found `::Assoc` - --> $DIR/bound_reduction2.rs:17:5 + --> $DIR/bound_reduction2.rs:16:46 | LL | type Foo = impl Trait; | - this generic parameter must be used with a generic type parameter ... -LL | () - | ^^ +LL | fn foo_desugared(_: T) -> Foo { + | ^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/bounds-are-checked-2.rs b/tests/ui/type-alias-impl-trait/bounds-are-checked-2.rs index 45f54266014d6..4151a6a5f12da 100644 --- a/tests/ui/type-alias-impl-trait/bounds-are-checked-2.rs +++ b/tests/ui/type-alias-impl-trait/bounds-are-checked-2.rs @@ -3,15 +3,13 @@ #![feature(type_alias_impl_trait)] -mod foo { - pub type X = impl Clone; +pub type X = impl Clone; - fn f(t: T) -> X { - t - //~^ ERROR the trait bound `T: Clone` is not satisfied - } +#[define_opaque(X)] +fn f(t: T) -> X { + t + //~^ ERROR the trait bound `T: Clone` is not satisfied } -use foo::X; fn g(o: Option>) -> Option> { o.clone() diff --git a/tests/ui/type-alias-impl-trait/bounds-are-checked-2.stderr b/tests/ui/type-alias-impl-trait/bounds-are-checked-2.stderr index bbb32b2d604d0..21166631c3e2f 100644 --- a/tests/ui/type-alias-impl-trait/bounds-are-checked-2.stderr +++ b/tests/ui/type-alias-impl-trait/bounds-are-checked-2.stderr @@ -1,23 +1,23 @@ error[E0277]: the trait bound `T: Clone` is not satisfied - --> $DIR/bounds-are-checked-2.rs:10:9 + --> $DIR/bounds-are-checked-2.rs:10:5 | -LL | t - | ^ the trait `Clone` is not implemented for `T` +LL | t + | ^ the trait `Clone` is not implemented for `T` | note: required by a bound in an opaque type - --> $DIR/bounds-are-checked-2.rs:7:26 + --> $DIR/bounds-are-checked-2.rs:6:22 | -LL | pub type X = impl Clone; - | ^^^^^ +LL | pub type X = impl Clone; + | ^^^^^ note: this definition site has more where clauses than the opaque type - --> $DIR/bounds-are-checked-2.rs:9:5 + --> $DIR/bounds-are-checked-2.rs:9:1 | -LL | fn f(t: T) -> X { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn f(t: T) -> X { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider restricting type parameter `T` with trait `Clone` | -LL | pub type X = impl Clone; - | +++++++++++++++++++ +LL | pub type X = impl Clone; + | +++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/bounds-are-checked.rs b/tests/ui/type-alias-impl-trait/bounds-are-checked.rs index 7c3a3a8440606..3d2953587282b 100644 --- a/tests/ui/type-alias-impl-trait/bounds-are-checked.rs +++ b/tests/ui/type-alias-impl-trait/bounds-are-checked.rs @@ -5,6 +5,7 @@ type X<'a> = impl Into<&'static str> + From<&'a str>; +#[define_opaque(X)] fn f<'a: 'static>(t: &'a str) -> X<'a> { t //~^ ERROR expected generic lifetime parameter, found `'static` diff --git a/tests/ui/type-alias-impl-trait/bounds-are-checked.stderr b/tests/ui/type-alias-impl-trait/bounds-are-checked.stderr index ad1b9f19d8ef9..7617268dd8e0c 100644 --- a/tests/ui/type-alias-impl-trait/bounds-are-checked.stderr +++ b/tests/ui/type-alias-impl-trait/bounds-are-checked.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic lifetime parameter, found `'static` - --> $DIR/bounds-are-checked.rs:9:5 + --> $DIR/bounds-are-checked.rs:10:5 | LL | type X<'a> = impl Into<&'static str> + From<&'a str>; | -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type diff --git a/tests/ui/type-alias-impl-trait/bounds-are-checked3.rs b/tests/ui/type-alias-impl-trait/bounds-are-checked3.rs index 5a9e87c091961..a8524770a8528 100644 --- a/tests/ui/type-alias-impl-trait/bounds-are-checked3.rs +++ b/tests/ui/type-alias-impl-trait/bounds-are-checked3.rs @@ -9,6 +9,7 @@ struct Struct(Option); type Foo = (impl Debug, Struct); //~^ ERROR: `T` doesn't implement `std::fmt::Display` +#[define_opaque(Foo)] fn foo() -> Foo { (Vec::::new(), Struct(None)) } diff --git a/tests/ui/type-alias-impl-trait/bounds.rs b/tests/ui/type-alias-impl-trait/bounds.rs index 8e24a937d1d47..c131d52a4badd 100644 --- a/tests/ui/type-alias-impl-trait/bounds.rs +++ b/tests/ui/type-alias-impl-trait/bounds.rs @@ -9,6 +9,7 @@ use std::fmt::Debug; // type alias bounds. type Foo = (impl Debug, usize); +#[define_opaque(Foo)] fn foo() -> Foo { (Vec::::new(), 1234) } diff --git a/tests/ui/type-alias-impl-trait/closure-normalization-ice-109020.rs b/tests/ui/type-alias-impl-trait/closure-normalization-ice-109020.rs index 0dfa1f40ae6c2..7fd8fa6f9dbac 100644 --- a/tests/ui/type-alias-impl-trait/closure-normalization-ice-109020.rs +++ b/tests/ui/type-alias-impl-trait/closure-normalization-ice-109020.rs @@ -6,14 +6,12 @@ use std::marker::PhantomData; -mod foo { - pub type WithEmplacableForFn<'a> = impl super::EmplacableFn + 'a; +type WithEmplacableForFn<'a> = impl EmplacableFn + 'a; - fn _constrain(_: &mut ()) -> WithEmplacableForFn<'_> { - () - } +#[define_opaque(WithEmplacableForFn)] +fn _constrain(_: &mut ()) -> WithEmplacableForFn<'_> { + () } -use foo::*; fn with_emplacable_for<'a, F, R>(mut f: F) -> R where diff --git a/tests/ui/type-alias-impl-trait/closure_args.rs b/tests/ui/type-alias-impl-trait/closure_args.rs index 0141a01aad026..8a518c19dbab0 100644 --- a/tests/ui/type-alias-impl-trait/closure_args.rs +++ b/tests/ui/type-alias-impl-trait/closure_args.rs @@ -4,21 +4,19 @@ #![feature(type_alias_impl_trait)] -mod foo { - pub trait Anything {} - impl Anything for T {} - pub type Input = impl Anything; +pub trait Anything {} +impl Anything for T {} +pub type Input = impl Anything; - fn bop(_: Input) { - super::run( - |x: u32| { - println!("{x}"); - }, - 0, - ); - } +#[define_opaque(Input)] +fn bop(_: Input) { + run( + |x: u32| { + println!("{x}"); + }, + 0, + ); } -use foo::Input; fn run ()>(f: F, i: Input) { f(i); diff --git a/tests/ui/type-alias-impl-trait/closure_args2.rs b/tests/ui/type-alias-impl-trait/closure_args2.rs index 13ac3d31d838a..257c5f86633ef 100644 --- a/tests/ui/type-alias-impl-trait/closure_args2.rs +++ b/tests/ui/type-alias-impl-trait/closure_args2.rs @@ -2,27 +2,25 @@ #![feature(type_alias_impl_trait)] -mod foo { - pub trait Foo { - // This was reachable in https://github.com/rust-lang/rust/issues/100800 - fn foo(&self) { - unreachable!() - } +pub trait Foo { + // This was reachable in https://github.com/rust-lang/rust/issues/100800 + fn foo(&self) { + unreachable!() } - impl Foo for T {} +} +impl Foo for T {} - pub struct B; - impl B { - fn foo(&self) {} - } - pub type Input = impl Foo; - fn bop() -> Input { - super::run1(|x: B| x.foo(), B); - super::run2(|x: B| x.foo(), B); - panic!() - } +pub struct B; +impl B { + fn foo(&self) {} +} +pub type Input = impl Foo; +#[define_opaque(Input)] +fn bop() -> Input { + run1(|x: B| x.foo(), B); + run2(|x: B| x.foo(), B); + panic!() } -use foo::*; fn run1(f: F, i: Input) { f(i) diff --git a/tests/ui/type-alias-impl-trait/closure_infer.rs b/tests/ui/type-alias-impl-trait/closure_infer.rs index fa0514c34a091..2aa19913ad6eb 100644 --- a/tests/ui/type-alias-impl-trait/closure_infer.rs +++ b/tests/ui/type-alias-impl-trait/closure_infer.rs @@ -22,12 +22,14 @@ impl StreamConsumer for DispatchExecutor { // Functions that constrain TAITs can contain closures with an `_` in the return type. type Foo = impl Sized; +#[define_opaque(Foo)] fn foo() -> Foo { || -> _ {} } // The `_` in the closure return type can also be the TAIT itself. type Bar = impl Sized; +#[define_opaque(Bar)] fn bar() -> impl FnOnce() -> Bar { || -> _ {} } diff --git a/tests/ui/type-alias-impl-trait/closure_parent_substs.rs b/tests/ui/type-alias-impl-trait/closure_parent_substs.rs index e78c7c16c8e68..4c99778b66e73 100644 --- a/tests/ui/type-alias-impl-trait/closure_parent_substs.rs +++ b/tests/ui/type-alias-impl-trait/closure_parent_substs.rs @@ -15,6 +15,7 @@ mod test1 { // Hidden type = Closure['?0] type Opaque = impl Sized; + #[define_opaque(Opaque)] fn define<'a: 'a>() -> Opaque { || {} } @@ -31,6 +32,7 @@ mod test2 { &'a (): Trait, = impl Sized + 'a; + #[define_opaque(Opaque)] fn define<'a, 'x, 'y>() -> Opaque<'a> where &'a (): Trait, @@ -52,6 +54,7 @@ mod test3 { (&'a (), &'b ()): Trait, = impl Sized + 'a + 'b; + #[define_opaque(Opaque)] fn define<'a, 'b, 'x>() -> Opaque<'a, 'b> where (&'a (), &'b ()): Trait, diff --git a/tests/ui/type-alias-impl-trait/closure_wf_outlives.rs b/tests/ui/type-alias-impl-trait/closure_wf_outlives.rs index caa9b6d979aba..5316a47bbd469 100644 --- a/tests/ui/type-alias-impl-trait/closure_wf_outlives.rs +++ b/tests/ui/type-alias-impl-trait/closure_wf_outlives.rs @@ -13,6 +13,7 @@ mod test1 { type Opaque<'a, 'b> = impl Sized + 'a + 'b; + #[define_opaque(Opaque)] fn define<'a, 'b>() -> Opaque<'a, 'b> where 'a: 'b, @@ -26,6 +27,7 @@ mod test1 { mod test2 { type Opaque<'a, 'b> = impl Sized + 'a + 'b; + #[define_opaque(Opaque)] fn define<'a, 'b, 'x>() -> Opaque<'a, 'b> where 'a: 'x, @@ -40,6 +42,7 @@ mod test2 { mod test2_fixed { type Opaque<'a: 'b, 'b> = impl Sized + 'a + 'b; + #[define_opaque(Opaque)] fn define<'a, 'b, 'x>() -> Opaque<'a, 'b> where 'a: 'x, @@ -53,6 +56,7 @@ mod test2_fixed { mod test3 { type Opaque = impl Sized; + #[define_opaque(Opaque)] fn define() -> Opaque where T: 'static, diff --git a/tests/ui/type-alias-impl-trait/closure_wf_outlives.stderr b/tests/ui/type-alias-impl-trait/closure_wf_outlives.stderr index ae00d3fc6678e..ccba2d37fd03f 100644 --- a/tests/ui/type-alias-impl-trait/closure_wf_outlives.stderr +++ b/tests/ui/type-alias-impl-trait/closure_wf_outlives.stderr @@ -1,5 +1,5 @@ error[E0478]: lifetime bound not satisfied - --> $DIR/closure_wf_outlives.rs:20:9 + --> $DIR/closure_wf_outlives.rs:21:9 | LL | || {} | ^^^^^ @@ -16,34 +16,34 @@ LL | type Opaque<'a, 'b> = impl Sized + 'a + 'b; | ^^ error[E0803]: cannot infer an appropriate lifetime due to conflicting requirements - --> $DIR/closure_wf_outlives.rs:34:9 + --> $DIR/closure_wf_outlives.rs:36:9 | LL | || {} | ^^^^^ | note: first, the lifetime cannot outlive the lifetime `'a` as defined here... - --> $DIR/closure_wf_outlives.rs:27:17 + --> $DIR/closure_wf_outlives.rs:28:17 | LL | type Opaque<'a, 'b> = impl Sized + 'a + 'b; | ^^ note: ...so that the declared lifetime parameter bounds are satisfied - --> $DIR/closure_wf_outlives.rs:34:9 + --> $DIR/closure_wf_outlives.rs:36:9 | LL | || {} | ^^^^^ note: but, the lifetime must be valid for the lifetime `'b` as defined here... - --> $DIR/closure_wf_outlives.rs:27:21 + --> $DIR/closure_wf_outlives.rs:28:21 | LL | type Opaque<'a, 'b> = impl Sized + 'a + 'b; | ^^ note: ...so that the declared lifetime parameter bounds are satisfied - --> $DIR/closure_wf_outlives.rs:34:9 + --> $DIR/closure_wf_outlives.rs:36:9 | LL | || {} | ^^^^^ error[E0310]: the parameter type `T` may not live long enough - --> $DIR/closure_wf_outlives.rs:60:9 + --> $DIR/closure_wf_outlives.rs:64:9 | LL | || {} | ^^^^^ @@ -52,7 +52,7 @@ LL | || {} | ...so that the type `T` will meet its required lifetime bounds... | note: ...that is required by this bound - --> $DIR/closure_wf_outlives.rs:58:12 + --> $DIR/closure_wf_outlives.rs:62:12 | LL | T: 'static, | ^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/closures_in_branches.rs b/tests/ui/type-alias-impl-trait/closures_in_branches.rs index 7bb490bbec818..d83bf36a444e5 100644 --- a/tests/ui/type-alias-impl-trait/closures_in_branches.rs +++ b/tests/ui/type-alias-impl-trait/closures_in_branches.rs @@ -2,6 +2,7 @@ type Foo = impl std::ops::FnOnce(String) -> usize; +#[define_opaque(Foo)] fn foo(b: bool) -> Foo { if b { |x| x.len() //~ ERROR type annotations needed @@ -10,8 +11,8 @@ fn foo(b: bool) -> Foo { } } - type Foo1 = impl std::ops::FnOnce(String) -> usize; +#[define_opaque(Foo1)] fn foo1(b: bool) -> Foo1 { |x| x.len() } diff --git a/tests/ui/type-alias-impl-trait/closures_in_branches.stderr b/tests/ui/type-alias-impl-trait/closures_in_branches.stderr index 9cc15f14a991d..849ffd214f07d 100644 --- a/tests/ui/type-alias-impl-trait/closures_in_branches.stderr +++ b/tests/ui/type-alias-impl-trait/closures_in_branches.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/closures_in_branches.rs:7:10 + --> $DIR/closures_in_branches.rs:8:10 | LL | |x| x.len() | ^ - type must be known at this point @@ -10,7 +10,7 @@ LL | |x: /* Type */| x.len() | ++++++++++++ error[E0282]: type annotations needed - --> $DIR/closures_in_branches.rs:21:10 + --> $DIR/closures_in_branches.rs:22:10 | LL | |x| x.len() | ^ - type must be known at this point diff --git a/tests/ui/type-alias-impl-trait/auxiliary/coherence_cross_crate_trait_decl.rs b/tests/ui/type-alias-impl-trait/coherence/auxiliary/coherence_cross_crate_trait_decl.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/auxiliary/coherence_cross_crate_trait_decl.rs rename to tests/ui/type-alias-impl-trait/coherence/auxiliary/coherence_cross_crate_trait_decl.rs diff --git a/tests/ui/type-alias-impl-trait/auxiliary/foreign-crate.rs b/tests/ui/type-alias-impl-trait/coherence/auxiliary/foreign-crate.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/auxiliary/foreign-crate.rs rename to tests/ui/type-alias-impl-trait/coherence/auxiliary/foreign-crate.rs diff --git a/tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.rs b/tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.rs new file mode 100644 index 0000000000000..54d68afc31f69 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.rs @@ -0,0 +1,17 @@ +#![feature(type_alias_impl_trait)] + +type T = impl Sized; + +struct Foo; + +impl Into for Foo { + //~^ ERROR conflicting implementations of trait `Into<_>` for type `Foo` + #[define_opaque(T)] + fn into(self) -> T { + Foo + } +} + +fn main() { + let _: T = Foo.into(); +} diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.stderr similarity index 100% rename from tests/ui/impl-trait/coherence-treats-tait-ambig.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence-treats-tait-ambig.stderr diff --git a/tests/ui/type-alias-impl-trait/coherence.classic.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence.classic.stderr similarity index 96% rename from tests/ui/type-alias-impl-trait/coherence.classic.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence.classic.stderr index 98badeef382de..e99d4636b1343 100644 --- a/tests/ui/type-alias-impl-trait/coherence.classic.stderr +++ b/tests/ui/type-alias-impl-trait/coherence/coherence.classic.stderr @@ -1,5 +1,5 @@ error[E0117]: only traits defined in the current crate can be implemented for arbitrary types - --> $DIR/coherence.rs:16:1 + --> $DIR/coherence.rs:17:1 | LL | impl foreign_crate::ForeignTrait for AliasOfForeignType<()> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------- diff --git a/tests/ui/type-alias-impl-trait/coherence.next.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence.next.stderr similarity index 96% rename from tests/ui/type-alias-impl-trait/coherence.next.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence.next.stderr index 8d7183831109a..6d14594e33a0f 100644 --- a/tests/ui/type-alias-impl-trait/coherence.next.stderr +++ b/tests/ui/type-alias-impl-trait/coherence/coherence.next.stderr @@ -1,5 +1,5 @@ error[E0117]: only traits defined in the current crate can be implemented for arbitrary types - --> $DIR/coherence.rs:16:1 + --> $DIR/coherence.rs:17:1 | LL | impl foreign_crate::ForeignTrait for AliasOfForeignType<()> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------- diff --git a/tests/ui/type-alias-impl-trait/coherence.rs b/tests/ui/type-alias-impl-trait/coherence/coherence.rs similarity index 93% rename from tests/ui/type-alias-impl-trait/coherence.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence.rs index 760e5210c5b7a..eb27c2708042a 100644 --- a/tests/ui/type-alias-impl-trait/coherence.rs +++ b/tests/ui/type-alias-impl-trait/coherence/coherence.rs @@ -9,6 +9,7 @@ trait LocalTrait {} impl LocalTrait for foreign_crate::ForeignType {} type AliasOfForeignType = impl LocalTrait; +#[define_opaque(AliasOfForeignType)] fn use_alias(val: T) -> AliasOfForeignType { foreign_crate::ForeignType(val) } diff --git a/tests/ui/type-alias-impl-trait/coherence_cross_crate.rs b/tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.rs similarity index 96% rename from tests/ui/type-alias-impl-trait/coherence_cross_crate.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.rs index c1958e4f246d7..73f13f22bee24 100644 --- a/tests/ui/type-alias-impl-trait/coherence_cross_crate.rs +++ b/tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.rs @@ -13,6 +13,7 @@ trait OtherTrait {} type Alias = impl SomeTrait; +#[define_opaque(Alias)] fn constrain() -> Alias { () } diff --git a/tests/ui/type-alias-impl-trait/coherence_cross_crate.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.stderr similarity index 92% rename from tests/ui/type-alias-impl-trait/coherence_cross_crate.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.stderr index 893a27faced67..6b251cfac73c8 100644 --- a/tests/ui/type-alias-impl-trait/coherence_cross_crate.stderr +++ b/tests/ui/type-alias-impl-trait/coherence/coherence_cross_crate.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `OtherTrait` for type `Alias` - --> $DIR/coherence_cross_crate.rs:21:1 + --> $DIR/coherence_cross_crate.rs:22:1 | LL | impl OtherTrait for Alias {} | ------------------------- first implementation here diff --git a/tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.rs b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs similarity index 97% rename from tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs index 39b3d535ad45c..a7e251b1ab99d 100644 --- a/tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.rs +++ b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.rs @@ -20,6 +20,7 @@ impl Trait for (TAIT, TAIT) {} impl Trait for (u32, i32) {} //~^ ERROR conflicting implementations of trait `Trait` for type `(TAIT, TAIT)` +#[define_opaque(TAIT)] fn define() -> TAIT {} fn main() {} diff --git a/tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.stderr b/tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr similarity index 100% rename from tests/ui/type-alias-impl-trait/coherence_different_hidden_ty.stderr rename to tests/ui/type-alias-impl-trait/coherence/coherence_different_hidden_ty.stderr diff --git a/tests/ui/type-alias-impl-trait/coherence_generalization.rs b/tests/ui/type-alias-impl-trait/coherence/coherence_generalization.rs similarity index 93% rename from tests/ui/type-alias-impl-trait/coherence_generalization.rs rename to tests/ui/type-alias-impl-trait/coherence/coherence_generalization.rs index 2d7de1add490d..46cde115b7f75 100644 --- a/tests/ui/type-alias-impl-trait/coherence_generalization.rs +++ b/tests/ui/type-alias-impl-trait/coherence/coherence_generalization.rs @@ -6,6 +6,7 @@ #![feature(type_alias_impl_trait)] trait Trait {} type Opaque = impl Sized; +#[define_opaque(Opaque)] fn foo() -> Opaque { () } diff --git a/tests/ui/type-alias-impl-trait/const_generic_type.infer.stderr b/tests/ui/type-alias-impl-trait/const_generic_type.infer.stderr index 5b77bb6c2bc26..dc15d530fd7a0 100644 --- a/tests/ui/type-alias-impl-trait/const_generic_type.infer.stderr +++ b/tests/ui/type-alias-impl-trait/const_generic_type.infer.stderr @@ -1,8 +1,8 @@ error: `Bar` is forbidden as the type of a const generic parameter - --> $DIR/const_generic_type.rs:7:24 + --> $DIR/const_generic_type.rs:8:24 | -LL | async fn test() { - | ^^^^^^^^^^ +LL | async fn test() { + | ^^^ | = note: the only supported types are integers, `bool`, and `char` diff --git a/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr b/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr index c7c93eee63e98..241eccc5f2b09 100644 --- a/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr +++ b/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr @@ -1,40 +1,23 @@ error: `Bar` is forbidden as the type of a const generic parameter - --> $DIR/const_generic_type.rs:7:24 + --> $DIR/const_generic_type.rs:8:24 | -LL | async fn test() { - | ^^^^^^^^^^ +LL | async fn test() { + | ^^^ | = note: the only supported types are integers, `bool`, and `char` -error: item does not constrain `Bar::{opaque#0}`, but has it in its signature - --> $DIR/const_generic_type.rs:7:10 +error: item does not constrain `Bar::{opaque#0}` + --> $DIR/const_generic_type.rs:8:10 | -LL | async fn test() { +LL | async fn test() { | ^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/const_generic_type.rs:5:12 | LL | type Bar = impl std::fmt::Display; | ^^^^^^^^^^^^^^^^^^^^^^ -error: item does not constrain `Bar::{opaque#0}`, but has it in its signature - --> $DIR/const_generic_type.rs:7:38 - | -LL | async fn test() { - | ______________________________________^ -... | -LL | | let x: u32 = N; -LL | | } - | |_^ - | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/const_generic_type.rs:5:12 - | -LL | type Bar = impl std::fmt::Display; - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/const_generic_type.rs b/tests/ui/type-alias-impl-trait/const_generic_type.rs index 7149370048b35..9b38f1449f89b 100644 --- a/tests/ui/type-alias-impl-trait/const_generic_type.rs +++ b/tests/ui/type-alias-impl-trait/const_generic_type.rs @@ -4,10 +4,10 @@ #![feature(type_alias_impl_trait)] type Bar = impl std::fmt::Display; -async fn test() { +#[define_opaque(Bar)] +async fn test() { //~^ ERROR: `Bar` is forbidden as the type of a const generic parameter //[no_infer]~^^ ERROR item does not constrain - //[no_infer]~| ERROR item does not constrain #[cfg(infer)] let x: u32 = N; } diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection.current.stderr b/tests/ui/type-alias-impl-trait/constrain_in_projection.current.stderr index 580258bbb28e4..c4c55d8e0927b 100644 --- a/tests/ui/type-alias-impl-trait/constrain_in_projection.current.stderr +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection.current.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Foo: Trait` is not satisfied - --> $DIR/constrain_in_projection.rs:24:14 + --> $DIR/constrain_in_projection.rs:25:14 | LL | let x = >::Assoc::default(); | ^^^ the trait `Trait` is not implemented for `Foo` @@ -8,7 +8,7 @@ LL | let x = >::Assoc::default(); but trait `Trait<()>` is implemented for it error[E0277]: the trait bound `Foo: Trait` is not satisfied - --> $DIR/constrain_in_projection.rs:24:13 + --> $DIR/constrain_in_projection.rs:25:13 | LL | let x = >::Assoc::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `Foo` diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection.rs b/tests/ui/type-alias-impl-trait/constrain_in_projection.rs index 355c0e1692b2a..62a105611b08a 100644 --- a/tests/ui/type-alias-impl-trait/constrain_in_projection.rs +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection.rs @@ -20,10 +20,11 @@ impl Trait<()> for Foo { type Assoc = u32; } -fn bop(_: Bar) { +#[define_opaque(Bar)] +fn bop() { let x = >::Assoc::default(); - //[current]~^ `Foo: Trait` is not satisfied - //[current]~| `Foo: Trait` is not satisfied + //[current]~^ ERROR `Foo: Trait` is not satisfied + //[current]~| ERROR `Foo: Trait` is not satisfied } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr b/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr index 777fe1e2788ce..d7fb6e67ad29d 100644 --- a/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Foo: Trait` is not satisfied - --> $DIR/constrain_in_projection2.rs:27:14 + --> $DIR/constrain_in_projection2.rs:28:14 | LL | let x = >::Assoc::default(); | ^^^ the trait `Trait` is not implemented for `Foo` @@ -9,7 +9,7 @@ LL | let x = >::Assoc::default(); `Foo` implements `Trait` error[E0277]: the trait bound `Foo: Trait` is not satisfied - --> $DIR/constrain_in_projection2.rs:27:13 + --> $DIR/constrain_in_projection2.rs:28:13 | LL | let x = >::Assoc::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `Foo` diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr b/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr index 0d6eac4216bae..7c09ab6a91ae9 100644 --- a/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr @@ -1,5 +1,5 @@ error[E0283]: type annotations needed: cannot satisfy `Foo: Trait` - --> $DIR/constrain_in_projection2.rs:27:14 + --> $DIR/constrain_in_projection2.rs:28:14 | LL | let x = >::Assoc::default(); | ^^^ help: use the fully qualified path to an implementation: `::Assoc` diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs b/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs index 16b1329b52f3d..61773cf59d475 100644 --- a/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs @@ -23,7 +23,8 @@ impl Trait for Foo { type Assoc = u32; } -fn bop(_: Bar) { +#[define_opaque(Bar)] +fn bop() { let x = >::Assoc::default(); //[next]~^ ERROR: cannot satisfy `Foo: Trait` //[current]~^^ ERROR: `Foo: Trait` is not satisfied diff --git a/tests/ui/type-alias-impl-trait/constrain_inputs.rs b/tests/ui/type-alias-impl-trait/constrain_inputs.rs index 1391a2036b2e1..7f6698e641d0e 100644 --- a/tests/ui/type-alias-impl-trait/constrain_inputs.rs +++ b/tests/ui/type-alias-impl-trait/constrain_inputs.rs @@ -2,7 +2,9 @@ mod lifetime_params { type Ty<'a> = impl Sized; + #[define_opaque(Ty)] fn defining(s: &str) -> Ty<'_> { s } + #[define_opaque(Ty)] fn execute(ty: Ty<'_>) -> &str { todo!() } //~^ ERROR return type references an anonymous lifetime, which is not constrained by the fn input types //~| ERROR item does not constrain @@ -15,7 +17,9 @@ mod lifetime_params { mod lifetime_params_2 { type Ty<'a> = impl FnOnce() -> &'a str; + #[define_opaque(Ty)] fn defining(s: &str) -> Ty<'_> { move || s } + #[define_opaque(Ty)] fn execute(ty: Ty<'_>) -> &str { ty() } //~^ ERROR return type references an anonymous lifetime, which is not constrained by the fn input types //~| ERROR item does not constrain @@ -24,6 +28,7 @@ mod lifetime_params_2 { // regression test for https://github.com/rust-lang/rust/issues/97104 mod type_params { type Ty = impl Sized; + #[define_opaque(Ty)] fn define(s: T) -> Ty { s } type BadFnSig = fn(Ty<&str>) -> &str; diff --git a/tests/ui/type-alias-impl-trait/constrain_inputs.stderr b/tests/ui/type-alias-impl-trait/constrain_inputs.stderr index 436326e66c386..b016715b1292a 100644 --- a/tests/ui/type-alias-impl-trait/constrain_inputs.stderr +++ b/tests/ui/type-alias-impl-trait/constrain_inputs.stderr @@ -1,5 +1,5 @@ error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types - --> $DIR/constrain_inputs.rs:6:31 + --> $DIR/constrain_inputs.rs:8:31 | LL | fn execute(ty: Ty<'_>) -> &str { todo!() } | ^^^^ @@ -8,7 +8,7 @@ LL | fn execute(ty: Ty<'_>) -> &str { todo!() } = note: consider introducing a named lifetime parameter error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types - --> $DIR/constrain_inputs.rs:10:35 + --> $DIR/constrain_inputs.rs:12:35 | LL | type BadFnSig = fn(Ty<'_>) -> &str; | ^^^^ @@ -17,7 +17,7 @@ LL | type BadFnSig = fn(Ty<'_>) -> &str; = note: consider introducing a named lifetime parameter error[E0582]: binding for associated type `Output` references an anonymous lifetime, which does not appear in the trait input types - --> $DIR/constrain_inputs.rs:12:42 + --> $DIR/constrain_inputs.rs:14:42 | LL | type BadTraitRef = dyn Fn(Ty<'_>) -> &str; | ^^^^ @@ -25,21 +25,21 @@ LL | type BadTraitRef = dyn Fn(Ty<'_>) -> &str; = note: lifetimes appearing in an associated or opaque type are not considered constrained = note: consider introducing a named lifetime parameter -error: item does not constrain `lifetime_params::Ty::{opaque#0}`, but has it in its signature - --> $DIR/constrain_inputs.rs:6:8 +error: item does not constrain `lifetime_params::Ty::{opaque#0}` + --> $DIR/constrain_inputs.rs:8:8 | LL | fn execute(ty: Ty<'_>) -> &str { todo!() } | ^^^^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/constrain_inputs.rs:4:19 | LL | type Ty<'a> = impl Sized; | ^^^^^^^^^^ error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types - --> $DIR/constrain_inputs.rs:19:31 + --> $DIR/constrain_inputs.rs:23:31 | LL | fn execute(ty: Ty<'_>) -> &str { ty() } | ^^^^ @@ -47,21 +47,21 @@ LL | fn execute(ty: Ty<'_>) -> &str { ty() } = note: lifetimes appearing in an associated or opaque type are not considered constrained = note: consider introducing a named lifetime parameter -error: item does not constrain `lifetime_params_2::Ty::{opaque#0}`, but has it in its signature - --> $DIR/constrain_inputs.rs:19:8 +error: item does not constrain `lifetime_params_2::Ty::{opaque#0}` + --> $DIR/constrain_inputs.rs:23:8 | LL | fn execute(ty: Ty<'_>) -> &str { ty() } | ^^^^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/constrain_inputs.rs:17:19 + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/constrain_inputs.rs:19:19 | LL | type Ty<'a> = impl FnOnce() -> &'a str; | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types - --> $DIR/constrain_inputs.rs:29:37 + --> $DIR/constrain_inputs.rs:34:37 | LL | type BadFnSig = fn(Ty<&str>) -> &str; | ^^^^ @@ -70,7 +70,7 @@ LL | type BadFnSig = fn(Ty<&str>) -> &str; = note: consider introducing a named lifetime parameter error[E0582]: binding for associated type `Output` references an anonymous lifetime, which does not appear in the trait input types - --> $DIR/constrain_inputs.rs:31:44 + --> $DIR/constrain_inputs.rs:36:44 | LL | type BadTraitRef = dyn Fn(Ty<&str>) -> &str; | ^^^^ diff --git a/tests/ui/type-alias-impl-trait/constrain_inputs_unsound.rs b/tests/ui/type-alias-impl-trait/constrain_inputs_unsound.rs index 3bae0f1730994..dc38e9a8cdc2b 100644 --- a/tests/ui/type-alias-impl-trait/constrain_inputs_unsound.rs +++ b/tests/ui/type-alias-impl-trait/constrain_inputs_unsound.rs @@ -4,9 +4,12 @@ trait Static: 'static {} impl Static for () {} type Gal = impl Static; +#[define_opaque(Gal)] fn _defining() -> Gal {} -trait Callable { type Output; } +trait Callable { + type Output; +} /// We can infer `>::Output: 'static`, /// because we know `C: 'static` and `Arg: 'static`, diff --git a/tests/ui/type-alias-impl-trait/constrain_inputs_unsound.stderr b/tests/ui/type-alias-impl-trait/constrain_inputs_unsound.stderr index 948bd6deacd0d..0edb0a068846d 100644 --- a/tests/ui/type-alias-impl-trait/constrain_inputs_unsound.stderr +++ b/tests/ui/type-alias-impl-trait/constrain_inputs_unsound.stderr @@ -1,5 +1,5 @@ error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types - --> $DIR/constrain_inputs_unsound.rs:23:58 + --> $DIR/constrain_inputs_unsound.rs:26:58 | LL | type MalformedTy = dyn for<'a> Callable, Output = &'a str>; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs b/tests/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs index 4f3f6d37eff4e..f69fd5747b653 100644 --- a/tests/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs +++ b/tests/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --edition=2021 +//@ edition: 2021 //@ build-pass #![feature(type_alias_impl_trait)] diff --git a/tests/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs b/tests/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs index cad1cbf61a295..be8b7fa6a9352 100644 --- a/tests/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs +++ b/tests/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs @@ -1,5 +1,5 @@ -//@ compile-flags: --edition=2021 --crate-type=lib -//@ rustc-env:RUST_BACKTRACE=0 +//@ compile-flags: --crate-type=lib +//@ edition: 2021 //@ check-pass // tracked in https://github.com/rust-lang/rust/issues/96572 diff --git a/tests/ui/type-alias-impl-trait/debug-ty-with-weak.rs b/tests/ui/type-alias-impl-trait/debug-ty-with-weak.rs index db9c2cc096ab3..6d9c05d9c68b5 100644 --- a/tests/ui/type-alias-impl-trait/debug-ty-with-weak.rs +++ b/tests/ui/type-alias-impl-trait/debug-ty-with-weak.rs @@ -3,12 +3,10 @@ #![feature(type_alias_impl_trait)] -mod bar { - pub type Debuggable = impl core::fmt::Debug; - fn foo() -> Debuggable { - 0u32 - } +pub type Debuggable = impl core::fmt::Debug; +#[define_opaque(Debuggable)] +fn foo() -> Debuggable { + 0u32 } -use bar::Debuggable; static mut TEST: Option = None; diff --git a/tests/ui/type-alias-impl-trait/declared_but_never_defined.stderr b/tests/ui/type-alias-impl-trait/declared_but_never_defined.stderr index 772f487d96aae..c2c401da0e89d 100644 --- a/tests/ui/type-alias-impl-trait/declared_but_never_defined.stderr +++ b/tests/ui/type-alias-impl-trait/declared_but_never_defined.stderr @@ -4,7 +4,7 @@ error: unconstrained opaque type LL | type Bar = impl std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^ | - = note: `Bar` must be used in combination with a concrete type within the same module + = note: `Bar` must be used in combination with a concrete type within the same crate error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.rs b/tests/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.rs index 5bda5f0fceaaa..2c538853e7bab 100644 --- a/tests/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.rs +++ b/tests/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.rs @@ -9,5 +9,5 @@ mod boo { fn bomp() -> boo::Boo { "" - //~^ mismatched types + //~^ ERROR mismatched types } diff --git a/tests/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.stderr b/tests/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.stderr index d60f1ffbccc2d..e97a69bd92c0a 100644 --- a/tests/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.stderr +++ b/tests/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.stderr @@ -4,7 +4,7 @@ error: unconstrained opaque type LL | pub type Boo = impl ::std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: `Boo` must be used in combination with a concrete type within the same module + = note: `Boo` must be used in combination with a concrete type within the same crate error[E0308]: mismatched types --> $DIR/declared_but_not_defined_in_scope.rs:11:5 @@ -19,7 +19,7 @@ LL | "" | = note: expected opaque type `Boo` found reference `&'static str` -note: this item must have the opaque type in its signature in order to be able to register hidden types +note: this item must have a `#[define_opaque(Boo)]` attribute to be able to define hidden types --> $DIR/declared_but_not_defined_in_scope.rs:10:4 | LL | fn bomp() -> boo::Boo { diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/foreign_type.rs b/tests/ui/type-alias-impl-trait/define_opaques_attr/foreign_type.rs new file mode 100644 index 0000000000000..c8b6bdbe2b920 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/foreign_type.rs @@ -0,0 +1,5 @@ +#![feature(type_alias_impl_trait)] + +#[define_opaque(String)] +//~^ ERROR: only opaque types defined in the local crate can be defined +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/foreign_type.stderr b/tests/ui/type-alias-impl-trait/define_opaques_attr/foreign_type.stderr new file mode 100644 index 0000000000000..65820e158f372 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/foreign_type.stderr @@ -0,0 +1,8 @@ +error: only opaque types defined in the local crate can be defined + --> $DIR/foreign_type.rs:3:17 + | +LL | #[define_opaque(String)] + | ^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/generics.rs b/tests/ui/type-alias-impl-trait/define_opaques_attr/generics.rs new file mode 100644 index 0000000000000..2ca2a753e2016 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/generics.rs @@ -0,0 +1,12 @@ +#![feature(type_alias_impl_trait)] + +type Tait = impl Sized; +//~^ ERROR: unconstrained opaque type + +#[define_opaque(Tait::<()>)] +//~^ ERROR: expected unsuffixed literal +fn foo() {} + +#[define_opaque(Tait<()>)] +//~^ ERROR: expected one of `(`, `,`, `::`, or `=`, found `<` +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/generics.stderr b/tests/ui/type-alias-impl-trait/define_opaques_attr/generics.stderr new file mode 100644 index 0000000000000..96e8bc9851c1e --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/generics.stderr @@ -0,0 +1,22 @@ +error: expected unsuffixed literal, found `<` + --> $DIR/generics.rs:6:23 + | +LL | #[define_opaque(Tait::<()>)] + | ^ + +error: expected one of `(`, `,`, `::`, or `=`, found `<` + --> $DIR/generics.rs:10:21 + | +LL | #[define_opaque(Tait<()>)] + | ^ expected one of `(`, `,`, `::`, or `=` + +error: unconstrained opaque type + --> $DIR/generics.rs:3:16 + | +LL | type Tait = impl Sized; + | ^^^^^^^^^^ + | + = note: `Tait` must be used in combination with a concrete type within the same crate + +error: aborting due to 3 previous errors + diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/invalid-extern-fn-body.rs b/tests/ui/type-alias-impl-trait/define_opaques_attr/invalid-extern-fn-body.rs new file mode 100644 index 0000000000000..d873af44adf9b --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/invalid-extern-fn-body.rs @@ -0,0 +1,11 @@ +#![feature(type_alias_impl_trait)] + +extern "C" { + fn a() { + //~^ ERROR incorrect function inside `extern` block + #[define_opaque(String)] + fn c() {} + } +} + +pub fn main() {} diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/invalid-extern-fn-body.stderr b/tests/ui/type-alias-impl-trait/define_opaques_attr/invalid-extern-fn-body.stderr new file mode 100644 index 0000000000000..2e944257d8f65 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/invalid-extern-fn-body.stderr @@ -0,0 +1,20 @@ +error: incorrect function inside `extern` block + --> $DIR/invalid-extern-fn-body.rs:4:8 + | +LL | extern "C" { + | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body +LL | fn a() { + | ________^___- + | | | + | | cannot have a body +LL | | +LL | | #[define_opaque(String)] +LL | | fn c() {} +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 1 previous error + diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/invalid_path.rs b/tests/ui/type-alias-impl-trait/define_opaques_attr/invalid_path.rs new file mode 100644 index 0000000000000..a02e80c6d89cd --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/invalid_path.rs @@ -0,0 +1,5 @@ +#![feature(type_alias_impl_trait)] + +#[define_opaque(Boom)] +//~^ ERROR: cannot find type alias or associated type +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/invalid_path.stderr b/tests/ui/type-alias-impl-trait/define_opaques_attr/invalid_path.stderr new file mode 100644 index 0000000000000..6b4cd4eac512b --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/invalid_path.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type alias or associated type with opaqaue types `Boom` in this scope + --> $DIR/invalid_path.rs:3:17 + | +LL | #[define_opaque(Boom)] + | ^^^^ not found in this scope + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/missing_parens.rs b/tests/ui/type-alias-impl-trait/define_opaques_attr/missing_parens.rs new file mode 100644 index 0000000000000..6a5a1b456c3ad --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/missing_parens.rs @@ -0,0 +1,5 @@ +#![feature(type_alias_impl_trait)] + +#[define_opaque] +//~^ ERROR: expected list of type aliases +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/missing_parens.stderr b/tests/ui/type-alias-impl-trait/define_opaques_attr/missing_parens.stderr new file mode 100644 index 0000000000000..a3b98ec83b2e3 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/missing_parens.stderr @@ -0,0 +1,8 @@ +error: expected list of type aliases + --> $DIR/missing_parens.rs:3:1 + | +LL | #[define_opaque] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/no_opaque.rs b/tests/ui/type-alias-impl-trait/define_opaques_attr/no_opaque.rs new file mode 100644 index 0000000000000..4b60bad5c03ab --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/no_opaque.rs @@ -0,0 +1,7 @@ +#![feature(type_alias_impl_trait)] + +type Thing = (); + +#[define_opaque(Thing)] +//~^ ERROR item does not contain any opaque types +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/no_opaque.stderr b/tests/ui/type-alias-impl-trait/define_opaques_attr/no_opaque.stderr new file mode 100644 index 0000000000000..66ae3a43b9515 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/no_opaque.stderr @@ -0,0 +1,8 @@ +error: item does not contain any opaque types + --> $DIR/no_opaque.rs:5:17 + | +LL | #[define_opaque(Thing)] + | ^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/non_type.rs b/tests/ui/type-alias-impl-trait/define_opaques_attr/non_type.rs new file mode 100644 index 0000000000000..0ad87616a518b --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/non_type.rs @@ -0,0 +1,7 @@ +#![feature(type_alias_impl_trait)] + +fn foo() {} + +#[define_opaque(foo)] +//~^ ERROR: expected type alias or associated type with opaqaue types +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/define_opaques_attr/non_type.stderr b/tests/ui/type-alias-impl-trait/define_opaques_attr/non_type.stderr new file mode 100644 index 0000000000000..0f91533bf7e17 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/define_opaques_attr/non_type.stderr @@ -0,0 +1,9 @@ +error[E0573]: expected type alias or associated type with opaqaue types, found function `foo` + --> $DIR/non_type.rs:5:17 + | +LL | #[define_opaque(foo)] + | ^^^ not a type alias or associated type with opaqaue types + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0573`. diff --git a/tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs b/tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs index 75a4fbdb5d635..6a07ea05c1a14 100644 --- a/tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs +++ b/tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs @@ -11,9 +11,11 @@ impl> Indirect for (A, B) { type Ty = (); } mod basic { use super::*; type Opq = impl Sized; + #[define_opaque(Opq)] fn define_1(_: Opq) { let _ = None::<<(Opq, u8) as Indirect>::Ty>; } + #[define_opaque(Opq)] fn define_2() -> Opq { 0u8 } @@ -23,6 +25,7 @@ mod basic { mod lifetime { use super::*; type Opq<'a> = impl Sized + 'a; + #[define_opaque(Opq)] fn define<'a: 'b, 'b: 'a>(_: Opq<'a>) { let _ = None::<<(Opq<'a>, &'b u8) as Indirect>::Ty>; } diff --git a/tests/ui/type-alias-impl-trait/defined-in-closure-external-lifetime.rs b/tests/ui/type-alias-impl-trait/defined-in-closure-external-lifetime.rs index 9101e4385b3d3..bc3904eb4e5fe 100644 --- a/tests/ui/type-alias-impl-trait/defined-in-closure-external-lifetime.rs +++ b/tests/ui/type-alias-impl-trait/defined-in-closure-external-lifetime.rs @@ -2,6 +2,7 @@ mod case1 { type Opaque<'x> = impl Sized + 'x; + #[define_opaque(Opaque)] fn foo<'s>() -> Opaque<'s> { let _ = || { let _: Opaque<'s> = (); }; //~^ ERROR expected generic lifetime parameter, found `'_` @@ -10,6 +11,7 @@ mod case1 { mod case2 { type Opaque<'x> = impl Sized + 'x; + #[define_opaque(Opaque)] fn foo<'s>() -> Opaque<'s> { let _ = || -> Opaque<'s> {}; //~^ ERROR expected generic lifetime parameter, found `'_` diff --git a/tests/ui/type-alias-impl-trait/defined-in-closure-external-lifetime.stderr b/tests/ui/type-alias-impl-trait/defined-in-closure-external-lifetime.stderr index a8fd1f691dd40..2210291d37b5a 100644 --- a/tests/ui/type-alias-impl-trait/defined-in-closure-external-lifetime.stderr +++ b/tests/ui/type-alias-impl-trait/defined-in-closure-external-lifetime.stderr @@ -1,18 +1,18 @@ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/defined-in-closure-external-lifetime.rs:6:29 + --> $DIR/defined-in-closure-external-lifetime.rs:7:29 | LL | type Opaque<'x> = impl Sized + 'x; | -- this generic parameter must be used with a generic lifetime parameter -LL | fn foo<'s>() -> Opaque<'s> { +... LL | let _ = || { let _: Opaque<'s> = (); }; | ^^^^^^^^^^ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/defined-in-closure-external-lifetime.rs:14:34 + --> $DIR/defined-in-closure-external-lifetime.rs:16:34 | LL | type Opaque<'x> = impl Sized + 'x; | -- this generic parameter must be used with a generic lifetime parameter -LL | fn foo<'s>() -> Opaque<'s> { +... LL | let _ = || -> Opaque<'s> {}; | ^^ diff --git a/tests/ui/type-alias-impl-trait/defining-use-submodule.rs b/tests/ui/type-alias-impl-trait/defining-use-submodule.rs index 3e7bc32640f52..b953cd646a027 100644 --- a/tests/ui/type-alias-impl-trait/defining-use-submodule.rs +++ b/tests/ui/type-alias-impl-trait/defining-use-submodule.rs @@ -11,13 +11,24 @@ type Foo = impl std::fmt::Display; type Bar = impl std::fmt::Display; mod foo { + #[define_opaque(super::Foo)] pub(crate) fn foo() -> super::Foo { "foo" } pub(crate) mod bar { + #[define_opaque(crate::Bar)] pub(crate) fn bar() -> crate::Bar { 1 } } } + +mod bar { + pub type Baz = impl std::fmt::Display; +} + +#[define_opaque(bar::Baz)] +fn baz() -> bar::Baz { + "boom" +} diff --git a/tests/ui/type-alias-impl-trait/destructure_tait-ice-113594.rs b/tests/ui/type-alias-impl-trait/destructure_tait-ice-113594.rs index eadf21c9138c3..a42ea083d740c 100644 --- a/tests/ui/type-alias-impl-trait/destructure_tait-ice-113594.rs +++ b/tests/ui/type-alias-impl-trait/destructure_tait-ice-113594.rs @@ -1,3 +1,5 @@ +//@ revisions: current next +//@ [next] compile-flags: -Znext-solver //@ build-pass //@ edition: 2021 @@ -10,6 +12,7 @@ pub struct Foo { pub type Tait = impl Sized; +#[define_opaque(Tait)] pub async fn ice_cold(beverage: Tait) { // Must destructure at least one field of `Foo` let Foo { field } = beverage; diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs index 8ce471e395681..b5a9bf4a59f36 100644 --- a/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs @@ -2,12 +2,14 @@ pub type Opaque<'a> = impl Sized; +#[define_opaque(Opaque)] fn get_one<'a>(a: *mut &'a str) -> Opaque<'a> { a } +#[define_opaque(Opaque)] fn get_iter<'a>() -> impl IntoIterator> { - //~^ ERROR: item does not constrain + //~^ ERROR item does not constrain `Opaque::{opaque#0}` None::> } diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr index f27f223452523..587328e287005 100644 --- a/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr @@ -1,11 +1,11 @@ -error: item does not constrain `Opaque::{opaque#0}`, but has it in its signature - --> $DIR/different_args_considered_equal.rs:9:4 +error: item does not constrain `Opaque::{opaque#0}` + --> $DIR/different_args_considered_equal.rs:11:4 | LL | fn get_iter<'a>() -> impl IntoIterator> { | ^^^^^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/different_args_considered_equal.rs:3:23 | LL | pub type Opaque<'a> = impl Sized; diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs index 43dfea97e6dbd..902d6ca57e6c9 100644 --- a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs @@ -2,6 +2,7 @@ pub type Opaque<'a> = impl Sized; +#[define_opaque(Opaque)] fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator> { if a.is_null() { Some(a) diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr index 213272f5f3451..562ab4168b536 100644 --- a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr @@ -1,9 +1,9 @@ error[E0700]: hidden type for `Opaque<'static>` captures lifetime that does not appear in bounds - --> $DIR/different_args_considered_equal2.rs:9:9 + --> $DIR/different_args_considered_equal2.rs:10:9 | LL | pub type Opaque<'a> = impl Sized; | ---------- opaque type defined here -LL | +... LL | fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator> { | -- hidden type `*mut &'a str` captures the lifetime `'a` as defined here ... diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs index ea69175ba3108..85a7e371b073f 100644 --- a/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs @@ -5,14 +5,12 @@ #![feature(type_alias_impl_trait)] -mod defining_scope { - pub type Opaque<'a> = impl Sized; +pub type Opaque<'a> = impl Sized; - fn get_one<'a>(a: *mut &'a str) -> Opaque<'a> { - a - } +#[define_opaque(Opaque)] +fn get_one<'a>(a: *mut &'a str) -> Opaque<'a> { + a } -use defining_scope::Opaque; fn get_iter<'a>() -> impl IntoIterator> { None::> diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr index d8f70e3d77825..06d6433c4b5fd 100644 --- a/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/different_args_considered_equal3.rs:18:5 + --> $DIR/different_args_considered_equal3.rs:16:5 | LL | fn get_iter<'a>() -> impl IntoIterator> { | -- lifetime `'a` defined here diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses.rs b/tests/ui/type-alias-impl-trait/different_defining_uses.rs index 4505c4d95248c..547696b83da56 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses.rs +++ b/tests/ui/type-alias-impl-trait/different_defining_uses.rs @@ -5,11 +5,13 @@ fn main() {} // two definitions with different types type Foo = impl std::fmt::Debug; +#[define_opaque(Foo)] fn foo() -> Foo { "" } +#[define_opaque(Foo)] fn bar() -> Foo { + //~^ ERROR concrete type differs 42i32 - //~^ ERROR concrete type differs from previous } diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses.stderr b/tests/ui/type-alias-impl-trait/different_defining_uses.stderr index 9e6169b2af73f..36d7e33dca0aa 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses.stderr +++ b/tests/ui/type-alias-impl-trait/different_defining_uses.stderr @@ -1,14 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/different_defining_uses.rs:13:5 + --> $DIR/different_defining_uses.rs:14:13 | -LL | 42i32 - | ^^^^^ expected `&'static str`, got `i32` +LL | fn bar() -> Foo { + | ^^^ expected `&str`, got `i32` | note: previous use here - --> $DIR/different_defining_uses.rs:9:5 + --> $DIR/different_defining_uses.rs:9:13 | -LL | "" - | ^^ +LL | fn foo() -> Foo { + | ^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.rs b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.rs index 4b5f455e38150..bbbc2086bdf5c 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.rs +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.rs @@ -2,6 +2,7 @@ type Tait<'a> = impl Sized + 'a; +#[define_opaque(Tait)] fn foo<'a, 'b>() -> Tait<'a> { if false { if { return } { @@ -11,7 +12,6 @@ fn foo<'a, 'b>() -> Tait<'a> { } let x: Tait<'a> = (); x - //~^ ERROR concrete type differs from previous defining opaque type use } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.stderr b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.stderr index 6f5be5467f704..4d7dd6b2ad5f5 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.stderr +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-2.stderr @@ -1,26 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/different_defining_uses_never_type-2.rs:13:5 - | -LL | x - | ^ expected `i32`, got `()` - | -note: previous use here - --> $DIR/different_defining_uses_never_type-2.rs:8:31 - | -LL | let y: Tait<'b> = 1i32; - | ^^^^ - -error: concrete type differs from previous defining opaque type use - --> $DIR/different_defining_uses_never_type-2.rs:8:31 + --> $DIR/different_defining_uses_never_type-2.rs:9:31 | LL | let y: Tait<'b> = 1i32; | ^^^^ expected `()`, got `i32` | note: previous use here - --> $DIR/different_defining_uses_never_type-2.rs:7:14 + --> $DIR/different_defining_uses_never_type-2.rs:14:5 | -LL | if { return } { - | ^^^^^^ +LL | x + | ^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.rs b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.rs index a4ac27378e1d8..b2713b9602cec 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.rs +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.rs @@ -2,6 +2,7 @@ type Tait = impl Sized; +#[define_opaque(Tait)] fn foo() -> Tait { if false { if { return } { diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.stderr b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.stderr index 0fdcb81f66726..eb9001cc6244c 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.stderr +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type-3.stderr @@ -1,14 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/different_defining_uses_never_type-3.rs:8:30 + --> $DIR/different_defining_uses_never_type-3.rs:9:30 | LL | let y: Tait = 1i32; | ^^^^ expected `()`, got `i32` | note: previous use here - --> $DIR/different_defining_uses_never_type-3.rs:12:22 + --> $DIR/different_defining_uses_never_type-3.rs:14:5 | -LL | let x: Tait = (); - | ^^ +LL | x + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type.rs b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type.rs index 0b8157fe33dd7..38597ccaf4277 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type.rs +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type.rs @@ -5,14 +5,17 @@ fn main() {} // two definitions with different types type Foo = impl std::fmt::Debug; +#[define_opaque(Foo)] fn foo() -> Foo { "" } +#[define_opaque(Foo)] fn bar() -> Foo { //~ ERROR: concrete type differs from previous defining opaque type use panic!() } +#[define_opaque(Foo)] fn boo() -> Foo { loop {} } diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr index 2a77eb4c4ac33..0914dd1c546e4 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr @@ -1,14 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/different_defining_uses_never_type.rs:12:13 + --> $DIR/different_defining_uses_never_type.rs:14:13 | LL | fn bar() -> Foo { - | ^^^ expected `&'static str`, got `()` + | ^^^ expected `&str`, got `()` | note: previous use here - --> $DIR/different_defining_uses_never_type.rs:9:5 + --> $DIR/different_defining_uses_never_type.rs:9:13 | -LL | "" - | ^^ +LL | fn foo() -> Foo { + | ^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type2.rs b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type2.rs index c39cc192dc74d..03766da34cbb4 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type2.rs +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type2.rs @@ -7,10 +7,12 @@ fn main() {} // two definitions with different types type Foo = impl std::fmt::Debug; +#[define_opaque(Foo)] fn foo() -> Foo { "" } +#[define_opaque(Foo)] fn bar(arg: bool) -> Foo { if arg { panic!() @@ -19,6 +21,7 @@ fn bar(arg: bool) -> Foo { } } +#[define_opaque(Foo)] fn boo(arg: bool) -> Foo { if arg { loop {} @@ -27,6 +30,7 @@ fn boo(arg: bool) -> Foo { } } +#[define_opaque(Foo)] fn bar2(arg: bool) -> Foo { if arg { "bar2" @@ -35,6 +39,7 @@ fn bar2(arg: bool) -> Foo { } } +#[define_opaque(Foo)] fn boo2(arg: bool) -> Foo { if arg { "boo2" diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type3.rs b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type3.rs index bc827a8f2113a..b5c2bf504ac66 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type3.rs +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type3.rs @@ -3,9 +3,11 @@ type Tait = impl Sized; struct One; +#[define_opaque(Tait)] fn one() -> Tait { One } struct Two(T); +#[define_opaque(Tait)] fn two() -> Tait { Two::<()>(todo!()) } //~^ ERROR concrete type differs from previous defining opaque type use diff --git a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type3.stderr b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type3.stderr index abf4a0d241b2c..78a9f6b636543 100644 --- a/tests/ui/type-alias-impl-trait/different_defining_uses_never_type3.stderr +++ b/tests/ui/type-alias-impl-trait/different_defining_uses_never_type3.stderr @@ -1,14 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/different_defining_uses_never_type3.rs:9:13 + --> $DIR/different_defining_uses_never_type3.rs:11:13 | LL | fn two() -> Tait { Two::<()>(todo!()) } | ^^^^ expected `One`, got `Two<()>` | note: previous use here - --> $DIR/different_defining_uses_never_type3.rs:6:20 + --> $DIR/different_defining_uses_never_type3.rs:7:13 | LL | fn one() -> Tait { One } - | ^^^ + | ^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/different_lifetimes_defining_uses.rs b/tests/ui/type-alias-impl-trait/different_lifetimes_defining_uses.rs index 4f424b8c665ad..ba3265343c61d 100644 --- a/tests/ui/type-alias-impl-trait/different_lifetimes_defining_uses.rs +++ b/tests/ui/type-alias-impl-trait/different_lifetimes_defining_uses.rs @@ -3,10 +3,12 @@ type OneLifetime<'a, 'b> = impl std::fmt::Debug; +#[define_opaque(OneLifetime)] fn foo<'a, 'b>(a: &'a u32, b: &'b u32) -> OneLifetime<'a, 'b> { a } +#[define_opaque(OneLifetime)] fn bar<'a, 'b>(a: &'a u32, b: &'b u32) -> OneLifetime<'a, 'b> { b //~^ ERROR: concrete type differs from previous defining opaque type use diff --git a/tests/ui/type-alias-impl-trait/different_lifetimes_defining_uses.stderr b/tests/ui/type-alias-impl-trait/different_lifetimes_defining_uses.stderr index 07ba17ad27b8f..b87e884708ab1 100644 --- a/tests/ui/type-alias-impl-trait/different_lifetimes_defining_uses.stderr +++ b/tests/ui/type-alias-impl-trait/different_lifetimes_defining_uses.stderr @@ -1,11 +1,11 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/different_lifetimes_defining_uses.rs:11:5 + --> $DIR/different_lifetimes_defining_uses.rs:13:5 | LL | b | ^ expected `&'a u32`, got `&'b u32` | note: previous use here - --> $DIR/different_lifetimes_defining_uses.rs:7:5 + --> $DIR/different_lifetimes_defining_uses.rs:8:5 | LL | a | ^ diff --git a/tests/ui/type-alias-impl-trait/drop-analysis-on-unconstrained-tait.rs b/tests/ui/type-alias-impl-trait/drop-analysis-on-unconstrained-tait.rs index 4332f1264a80b..987ac38128988 100644 --- a/tests/ui/type-alias-impl-trait/drop-analysis-on-unconstrained-tait.rs +++ b/tests/ui/type-alias-impl-trait/drop-analysis-on-unconstrained-tait.rs @@ -2,29 +2,32 @@ #![feature(type_alias_impl_trait)] -mod impl_trait_mod { - use super::*; - pub type OpaqueBlock = impl Trait; - //~^ ERROR unconstrained opaque type - pub type OpaqueIf = impl Trait; +pub type OpaqueBlock = impl Trait; +//~^ ERROR unconstrained opaque type +pub type OpaqueIf = impl Trait; - pub struct BlockWrapper(OpaqueBlock); - pub struct IfWrapper(pub OpaqueIf); +pub struct BlockWrapper(OpaqueBlock); +pub struct IfWrapper(pub OpaqueIf); - pub fn if_impl() -> Parser { - bind(option(block()), |_| block()) - } +#[define_opaque(OpaqueIf)] +pub fn if_impl() -> Parser { + bind(option(block()), |_| block()) } -use impl_trait_mod::*; pub trait Trait { type Assoc; } pub struct Parser

    (P); pub struct Bind(P, F); -impl Trait for Bind { type Assoc = (); } -impl Trait for BlockWrapper { type Assoc = (); } -impl Trait for IfWrapper { type Assoc = (); } +impl Trait for Bind { + type Assoc = (); +} +impl Trait for BlockWrapper { + type Assoc = (); +} +impl Trait for IfWrapper { + type Assoc = (); +} pub fn block() -> Parser { loop {} @@ -32,8 +35,9 @@ pub fn block() -> Parser { pub fn option(arg: Parser

    ) -> Parser { bind(arg, |_| block()) } -fn bind Parser>(_: Parser

    , _: F) -> Parser> - { loop {} } +fn bind Parser>(_: Parser

    , _: F) -> Parser> { + loop {} +} fn main() { if_impl().0; diff --git a/tests/ui/type-alias-impl-trait/drop-analysis-on-unconstrained-tait.stderr b/tests/ui/type-alias-impl-trait/drop-analysis-on-unconstrained-tait.stderr index 8e5838d5ddf55..db97954f6985c 100644 --- a/tests/ui/type-alias-impl-trait/drop-analysis-on-unconstrained-tait.stderr +++ b/tests/ui/type-alias-impl-trait/drop-analysis-on-unconstrained-tait.stderr @@ -1,10 +1,10 @@ error: unconstrained opaque type - --> $DIR/drop-analysis-on-unconstrained-tait.rs:7:28 + --> $DIR/drop-analysis-on-unconstrained-tait.rs:5:24 | -LL | pub type OpaqueBlock = impl Trait; - | ^^^^^^^^^^ +LL | pub type OpaqueBlock = impl Trait; + | ^^^^^^^^^^ | - = note: `OpaqueBlock` must be used in combination with a concrete type within the same module + = note: `OpaqueBlock` must be used in combination with a concrete type within the same crate error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/duplicate-lifetimes-from-rpit-containing-tait.rs b/tests/ui/type-alias-impl-trait/duplicate-lifetimes-from-rpit-containing-tait.rs index b1d5961067bd7..5dda2597c667f 100644 --- a/tests/ui/type-alias-impl-trait/duplicate-lifetimes-from-rpit-containing-tait.rs +++ b/tests/ui/type-alias-impl-trait/duplicate-lifetimes-from-rpit-containing-tait.rs @@ -4,6 +4,7 @@ type Opaque<'lt> = impl Sized + 'lt; +#[define_opaque(Opaque)] fn test<'a>( arg: impl Iterator, ) -> impl Iterator> { diff --git a/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs b/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs index b209b4bc89df3..64f926fba9e4a 100644 --- a/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs +++ b/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs @@ -7,19 +7,21 @@ impl Trait<'_, '_> for T {} mod mod1 { type Opaque<'a, 'b> = impl super::Trait<'a, 'b>; + #[define_opaque(Opaque)] fn test<'a>() -> Opaque<'a, 'a> {} //~^ ERROR non-defining opaque type use in defining scope - //~| ERROR non-defining opaque type use in defining scope } mod mod2 { type Opaque<'a, 'b> = impl super::Trait<'a, 'b>; + #[define_opaque(Opaque)] fn test<'a: 'b, 'b: 'a>() -> Opaque<'a, 'b> {} //~^ ERROR non-defining opaque type use in defining scope } mod mod3 { type Opaque<'a, 'b> = impl super::Trait<'a, 'b>; + #[define_opaque(Opaque)] fn test<'a: 'b, 'b: 'a>(a: &'a str) -> Opaque<'a, 'b> { a } //~^ ERROR non-defining opaque type use in defining scope } @@ -30,6 +32,7 @@ mod mod3 { // it is ambiguous whether `Opaque<'a> := &'a ()` or `Opaque<'a> := &'static ()` mod mod4 { type Opaque<'a> = impl super::Trait<'a, 'a>; + #[define_opaque(Opaque)] fn test<'a: 'static>() -> Opaque<'a> {} //~^ ERROR expected generic lifetime parameter, found `'static` } diff --git a/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.stderr b/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.stderr index b08bc8b826873..34af7cb32c270 100644 --- a/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.stderr +++ b/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.stderr @@ -1,17 +1,5 @@ error: non-defining opaque type use in defining scope - --> $DIR/equal-lifetime-params-not-ok.rs:10:22 - | -LL | fn test<'a>() -> Opaque<'a, 'a> {} - | ^^^^^^^^^^^^^^ generic argument `'a` used twice - | -note: for this opaque type - --> $DIR/equal-lifetime-params-not-ok.rs:9:27 - | -LL | type Opaque<'a, 'b> = impl super::Trait<'a, 'b>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: non-defining opaque type use in defining scope - --> $DIR/equal-lifetime-params-not-ok.rs:10:37 + --> $DIR/equal-lifetime-params-not-ok.rs:11:37 | LL | fn test<'a>() -> Opaque<'a, 'a> {} | ^^ @@ -23,7 +11,7 @@ LL | type Opaque<'a, 'b> = impl super::Trait<'a, 'b>; | ^^ ^^ error: non-defining opaque type use in defining scope - --> $DIR/equal-lifetime-params-not-ok.rs:17:49 + --> $DIR/equal-lifetime-params-not-ok.rs:18:49 | LL | fn test<'a: 'b, 'b: 'a>() -> Opaque<'a, 'b> {} | ^^ @@ -35,25 +23,26 @@ LL | type Opaque<'a, 'b> = impl super::Trait<'a, 'b>; | ^^ ^^ error: non-defining opaque type use in defining scope - --> $DIR/equal-lifetime-params-not-ok.rs:23:61 + --> $DIR/equal-lifetime-params-not-ok.rs:25:61 | LL | fn test<'a: 'b, 'b: 'a>(a: &'a str) -> Opaque<'a, 'b> { a } | ^ | note: lifetime used multiple times - --> $DIR/equal-lifetime-params-not-ok.rs:22:17 + --> $DIR/equal-lifetime-params-not-ok.rs:23:17 | LL | type Opaque<'a, 'b> = impl super::Trait<'a, 'b>; | ^^ ^^ error[E0792]: expected generic lifetime parameter, found `'static` - --> $DIR/equal-lifetime-params-not-ok.rs:33:42 + --> $DIR/equal-lifetime-params-not-ok.rs:36:42 | LL | type Opaque<'a> = impl super::Trait<'a, 'a>; | -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type +LL | #[define_opaque(Opaque)] LL | fn test<'a: 'static>() -> Opaque<'a> {} | ^^ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/equal-lifetime-params-ok.rs b/tests/ui/type-alias-impl-trait/equal-lifetime-params-ok.rs index 0ce85a4d6cbe1..711aca662d12a 100644 --- a/tests/ui/type-alias-impl-trait/equal-lifetime-params-ok.rs +++ b/tests/ui/type-alias-impl-trait/equal-lifetime-params-ok.rs @@ -12,6 +12,7 @@ impl Trait<'_, '_> for T {} mod equal_params { type Opaque<'a: 'b, 'b: 'a> = impl super::Trait<'a, 'b>; + #[define_opaque(Opaque)] fn test<'a: 'b, 'b: 'a>() -> Opaque<'a, 'b> { let _ = None::<&'a &'b &'a ()>; 0u8 @@ -20,6 +21,7 @@ mod equal_params { mod equal_static { type Opaque<'a: 'static> = impl Sized + 'a; + #[define_opaque(Opaque)] fn test<'a: 'static>() -> Opaque<'a> { let _ = None::<&'static &'a ()>; 0u8 diff --git a/tests/ui/type-alias-impl-trait/error-tainting-issue-122904.rs b/tests/ui/type-alias-impl-trait/error-tainting-issue-122904.rs new file mode 100644 index 0000000000000..a2de3957c0bd7 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/error-tainting-issue-122904.rs @@ -0,0 +1,18 @@ +// We previously didn't taint the borrowck result in this test, +// causing an ICE later on. +#![feature(type_alias_impl_trait)] +trait T {} + +type Alias<'a> = impl T; + +struct S; +impl<'a> T for &'a S {} + +#[define_opaque(Alias)] +fn with_positive(fun: impl Fn(Alias<'_>)) { + //~^ WARN function cannot return without recursing + with_positive(|&n| ()); + //~^ ERROR cannot move out of a shared reference +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/error-tainting-issue-122904.stderr b/tests/ui/type-alias-impl-trait/error-tainting-issue-122904.stderr new file mode 100644 index 0000000000000..956ce3e5936b6 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/error-tainting-issue-122904.stderr @@ -0,0 +1,30 @@ +warning: function cannot return without recursing + --> $DIR/error-tainting-issue-122904.rs:12:1 + | +LL | fn with_positive(fun: impl Fn(Alias<'_>)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | with_positive(|&n| ()); + | ---------------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +error[E0507]: cannot move out of a shared reference + --> $DIR/error-tainting-issue-122904.rs:14:20 + | +LL | with_positive(|&n| ()); + | ^- + | | + | data moved here + | move occurs because `n` has type `S`, which does not implement the `Copy` trait + | +help: consider removing the borrow + | +LL - with_positive(|&n| ()); +LL + with_positive(|n| ()); + | + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/type-alias-impl-trait/escaping-bound-var.rs b/tests/ui/type-alias-impl-trait/escaping-bound-var.rs index 07206dd2491eb..31bdd0815ec34 100644 --- a/tests/ui/type-alias-impl-trait/escaping-bound-var.rs +++ b/tests/ui/type-alias-impl-trait/escaping-bound-var.rs @@ -16,6 +16,7 @@ impl Trait<'_> for () { impl Test<'_> for () {} +#[define_opaque(Foo)] fn constrain() -> Foo { () } diff --git a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs index 9ed010f2293ec..c2578297006b3 100644 --- a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs +++ b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs @@ -22,14 +22,15 @@ type StateWidget<'a> = impl Widget<&'a ()>; impl Fn(&'a ()) -> StateWidget<'a>> Widget<()> for StatefulWidget { type State = (); + #[define_opaque(StateWidget)] fn make_state(&self) -> Self::State {} //~^ ERROR item does not constrain } +#[define_opaque(StateWidget)] fn new_stateful_widget Fn(&'a ()) -> StateWidget<'a>>(build: F) -> impl Widget<()> { //~^ ERROR item does not constrain StatefulWidget(build) - //~^ ERROR expected generic lifetime parameter, found `'a` } fn main() { diff --git a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr index 9a3f4ae4c1c31..dee729e1f9f40 100644 --- a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr +++ b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr @@ -1,31 +1,31 @@ -error: item does not constrain `StateWidget::{opaque#0}`, but has it in its signature - --> $DIR/failed-to-normalize-ice-99945.rs:25:8 +error: item does not constrain `StateWidget::{opaque#0}` + --> $DIR/failed-to-normalize-ice-99945.rs:26:8 | LL | fn make_state(&self) -> Self::State {} | ^^^^^^^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/failed-to-normalize-ice-99945.rs:20:24 | LL | type StateWidget<'a> = impl Widget<&'a ()>; | ^^^^^^^^^^^^^^^^^^^ -error: item does not constrain `StateWidget::{opaque#0}`, but has it in its signature - --> $DIR/failed-to-normalize-ice-99945.rs:29:4 +error: item does not constrain `StateWidget::{opaque#0}` + --> $DIR/failed-to-normalize-ice-99945.rs:31:4 | LL | fn new_stateful_widget Fn(&'a ()) -> StateWidget<'a>>(build: F) -> impl Widget<()> { | ^^^^^^^^^^^^^^^^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/failed-to-normalize-ice-99945.rs:20:24 | LL | type StateWidget<'a> = impl Widget<&'a ()>; | ^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/failed-to-normalize-ice-99945.rs:36:29 + --> $DIR/failed-to-normalize-ice-99945.rs:37:29 | LL | type StateWidget<'a> = impl Widget<&'a ()>; | ------------------- the expected opaque type @@ -36,16 +36,6 @@ LL | new_stateful_widget(|_| ()).make_state(); = note: expected opaque type `StateWidget<'_>` found unit type `()` -error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/failed-to-normalize-ice-99945.rs:31:5 - | -LL | type StateWidget<'a> = impl Widget<&'a ()>; - | -- this generic parameter must be used with a generic lifetime parameter -... -LL | StatefulWidget(build) - | ^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0308, E0792. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/fallback.rs b/tests/ui/type-alias-impl-trait/fallback.rs index d8cf7d71fef74..a2f25acca0d3a 100644 --- a/tests/ui/type-alias-impl-trait/fallback.rs +++ b/tests/ui/type-alias-impl-trait/fallback.rs @@ -7,19 +7,20 @@ type Foo = impl Copy; enum Wrapper { First(T), - Second + Second, } // This method constrains `Foo` to be `bool` +#[define_opaque(Foo)] fn constrained_foo() -> Foo { true } - // This method does not constrain `Foo`. // Per RFC 2071, function bodies may either // fully constrain an opaque type, or place no // constraints on it. +#[define_opaque(Foo)] fn unconstrained_foo() -> Wrapper { Wrapper::Second //~^ ERROR: type annotations needed diff --git a/tests/ui/type-alias-impl-trait/fallback.stderr b/tests/ui/type-alias-impl-trait/fallback.stderr index c909ab66f0ee2..1eb0afb13a8d0 100644 --- a/tests/ui/type-alias-impl-trait/fallback.stderr +++ b/tests/ui/type-alias-impl-trait/fallback.stderr @@ -1,5 +1,5 @@ error[E0283]: type annotations needed - --> $DIR/fallback.rs:24:5 + --> $DIR/fallback.rs:25:5 | LL | fn unconstrained_foo() -> Wrapper { | ------------ type must be known at this point diff --git a/tests/ui/type-alias-impl-trait/field-types.rs b/tests/ui/type-alias-impl-trait/field-types.rs index 24e430afac3ab..3ea73abb03f9f 100644 --- a/tests/ui/type-alias-impl-trait/field-types.rs +++ b/tests/ui/type-alias-impl-trait/field-types.rs @@ -1,3 +1,6 @@ +//! Show that `defines(StructName)` works for +//! fields of that struct being an opaque type. + #![feature(type_alias_impl_trait)] #![allow(dead_code)] @@ -11,6 +14,7 @@ struct Bar { foo: Foo, } +#[define_opaque(Bar)] fn bar() -> Bar { Bar { foo: "foo" } } diff --git a/tests/ui/type-alias-impl-trait/future.rs b/tests/ui/type-alias-impl-trait/future.rs index 36d1dcd00ad13..e233554f6a13f 100644 --- a/tests/ui/type-alias-impl-trait/future.rs +++ b/tests/ui/type-alias-impl-trait/future.rs @@ -11,6 +11,7 @@ trait Bar { type FooFuture = impl Future; +#[define_opaque(FooFuture)] fn foo(bar: B) -> FooFuture { async move { bar.bar() } //~^ ERROR: the trait bound `B: Bar` is not satisfied diff --git a/tests/ui/type-alias-impl-trait/future.stderr b/tests/ui/type-alias-impl-trait/future.stderr index 047ad164239c0..8510ab27fb752 100644 --- a/tests/ui/type-alias-impl-trait/future.stderr +++ b/tests/ui/type-alias-impl-trait/future.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `B: Bar` is not satisfied - --> $DIR/future.rs:15:5 + --> $DIR/future.rs:16:5 | LL | async move { bar.bar() } | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `B` | note: required by a bound in `foo` - --> $DIR/future.rs:14:11 + --> $DIR/future.rs:15:11 | LL | fn foo(bar: B) -> FooFuture { | ^^^ required by this bound in `foo` diff --git a/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.basic.stderr b/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.basic.stderr index e5f86c8c19355..43e887f36c5fe 100644 --- a/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.basic.stderr +++ b/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.basic.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/generic-not-strictly-equal.rs:33:5 + --> $DIR/generic-not-strictly-equal.rs:34:5 | LL | type Opaque<'a> = impl Copy + Captures<'a>; | -- this generic parameter must be used with a generic lifetime parameter diff --git a/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.member_constraints.stderr b/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.member_constraints.stderr index 693af69d6fab9..4a5360c9922e9 100644 --- a/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.member_constraints.stderr +++ b/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.member_constraints.stderr @@ -1,9 +1,9 @@ error[E0700]: hidden type for `Opaque<'x>` captures lifetime that does not appear in bounds - --> $DIR/generic-not-strictly-equal.rs:33:5 + --> $DIR/generic-not-strictly-equal.rs:34:5 | LL | type Opaque<'a> = impl Copy + Captures<'a>; | ------------------------ opaque type defined here -LL | +... LL | fn test<'x>(_: Opaque<'x>) { | -- hidden type `&'x u8` captures the lifetime `'x` as defined here ... diff --git a/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.rs b/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.rs index a059fd3b8227e..c1059e3da3342 100644 --- a/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.rs +++ b/tests/ui/type-alias-impl-trait/generic-not-strictly-equal.rs @@ -20,6 +20,7 @@ fn relate(_: X, _: X) {} type Opaque<'a> = impl Copy + Captures<'a>; +#[define_opaque(Opaque)] fn test<'x>(_: Opaque<'x>) { let opaque = None::>; // let's call this lifetime '?1 diff --git a/tests/ui/type-alias-impl-trait/generic_different_defining_uses.rs b/tests/ui/type-alias-impl-trait/generic_different_defining_uses.rs index 8b683ad282883..41fb6fe8587fc 100644 --- a/tests/ui/type-alias-impl-trait/generic_different_defining_uses.rs +++ b/tests/ui/type-alias-impl-trait/generic_different_defining_uses.rs @@ -4,11 +4,13 @@ fn main() {} type MyIter = impl Iterator; +#[define_opaque(MyIter)] fn my_iter(t: T) -> MyIter { std::iter::once(t) } +#[define_opaque(MyIter)] fn my_iter2(t: T) -> MyIter { - Some(t).into_iter() //~^ ERROR concrete type differs from previous + Some(t).into_iter() } diff --git a/tests/ui/type-alias-impl-trait/generic_different_defining_uses.stderr b/tests/ui/type-alias-impl-trait/generic_different_defining_uses.stderr index 72271d158a1a9..b4be8542163fe 100644 --- a/tests/ui/type-alias-impl-trait/generic_different_defining_uses.stderr +++ b/tests/ui/type-alias-impl-trait/generic_different_defining_uses.stderr @@ -1,14 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/generic_different_defining_uses.rs:12:5 + --> $DIR/generic_different_defining_uses.rs:13:25 | -LL | Some(t).into_iter() - | ^^^^^^^^^^^^^^^^^^^ 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:8:5 + --> $DIR/generic_different_defining_uses.rs:8:24 | -LL | std::iter::once(t) - | ^^^^^^^^^^^^^^^^^^ +LL | fn my_iter(t: T) -> MyIter { + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.rs b/tests/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.rs index 169d4f8d50941..45da4145278e4 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.rs +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.rs @@ -4,8 +4,8 @@ fn main() {} type Two<'a, 'b> = impl std::fmt::Debug; +#[define_opaque(Two)] fn one<'a>(t: &'a ()) -> Two<'a, 'a> { - //~^ ERROR non-defining opaque type use t //~^ ERROR non-defining opaque type use } diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.stderr b/tests/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.stderr index b03bf2466e60c..352e6fd3c5c84 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.stderr +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.stderr @@ -1,15 +1,3 @@ -error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_lifetime_param.rs:7:26 - | -LL | fn one<'a>(t: &'a ()) -> Two<'a, 'a> { - | ^^^^^^^^^^^ generic argument `'a` used twice - | -note: for this opaque type - --> $DIR/generic_duplicate_lifetime_param.rs:5:20 - | -LL | type Two<'a, 'b> = impl std::fmt::Debug; - | ^^^^^^^^^^^^^^^^^^^^ - error: non-defining opaque type use in defining scope --> $DIR/generic_duplicate_lifetime_param.rs:9:5 | @@ -22,5 +10,5 @@ note: lifetime used multiple times LL | type Two<'a, 'b> = impl std::fmt::Debug; | ^^ ^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use.rs b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use.rs index e3c6f4d874bcb..6e791a3bdb9dc 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use.rs +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use.rs @@ -18,20 +18,20 @@ type TwoLifetimes<'a, 'b> = impl Debug; type TwoConsts = impl Debug; +#[define_opaque(TwoTys)] fn one_ty(t: T) -> TwoTys { //~^ ERROR non-defining opaque type use in defining scope t - //~^ ERROR non-defining opaque type use in defining scope } +#[define_opaque(TwoLifetimes)] fn one_lifetime<'a>(t: &'a u32) -> TwoLifetimes<'a, 'a> { - //~^ ERROR non-defining opaque type use in defining scope t //~^ ERROR non-defining opaque type use in defining scope } +#[define_opaque(TwoConsts)] fn one_const(t: *mut [u8; N]) -> TwoConsts { //~^ ERROR non-defining opaque type use in defining scope t - //~^ ERROR non-defining opaque type use in defining scope } diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use.stderr b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use.stderr index 73570de53266f..022e534df1a29 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use.stderr +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use.stderr @@ -1,20 +1,8 @@ error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_param_use.rs:21:30 + --> $DIR/generic_duplicate_param_use.rs:22:30 | LL | fn one_ty(t: T) -> TwoTys { - | ^^^^^^^^^^^^ generic argument `T` used twice - | -note: for this opaque type - --> $DIR/generic_duplicate_param_use.rs:15:21 - | -LL | type TwoTys = impl Debug; - | ^^^^^^^^^^ - -error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_param_use.rs:23:5 - | -LL | t - | ^ + | ^^^^^^^^^^^^ | note: type used multiple times --> $DIR/generic_duplicate_param_use.rs:15:13 @@ -22,30 +10,6 @@ note: type used multiple times LL | type TwoTys = impl Debug; | ^ ^ -error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_param_use.rs:27:36 - | -LL | fn one_lifetime<'a>(t: &'a u32) -> TwoLifetimes<'a, 'a> { - | ^^^^^^^^^^^^^^^^^^^^ generic argument `'a` used twice - | -note: for this opaque type - --> $DIR/generic_duplicate_param_use.rs:17:29 - | -LL | type TwoLifetimes<'a, 'b> = impl Debug; - | ^^^^^^^^^^ - -error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_param_use.rs:33:50 - | -LL | fn one_const(t: *mut [u8; N]) -> TwoConsts { - | ^^^^^^^^^^^^^^^ generic argument `N` used twice - | -note: for this opaque type - --> $DIR/generic_duplicate_param_use.rs:19:50 - | -LL | type TwoConsts = impl Debug; - | ^^^^^^^^^^ - error: non-defining opaque type use in defining scope --> $DIR/generic_duplicate_param_use.rs:29:5 | @@ -59,10 +23,10 @@ LL | type TwoLifetimes<'a, 'b> = impl Debug; | ^^ ^^ error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_param_use.rs:35:5 + --> $DIR/generic_duplicate_param_use.rs:34:50 | -LL | t - | ^ +LL | fn one_const(t: *mut [u8; N]) -> TwoConsts { + | ^^^^^^^^^^^^^^^ | note: constant used multiple times --> $DIR/generic_duplicate_param_use.rs:19:16 @@ -70,5 +34,5 @@ note: constant used multiple times LL | type TwoConsts = impl Debug; | ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use10.rs b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use10.rs index 439214911eb97..849d237b80957 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use10.rs +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use10.rs @@ -7,6 +7,7 @@ fn main() {} type Two = impl Debug; +#[define_opaque(Two)] fn two(t: T, _: U) -> Two { (t, 4u32) } diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.rs b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.rs index 201535efe1532..073684b8adde4 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.rs +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.rs @@ -7,6 +7,7 @@ fn main() {} // test that unused generic parameters are ok type Two = impl Debug; +#[define_opaque(Two)] fn two(t: T, _: U) -> Two { t //~^ ERROR `T` doesn't implement `Debug` diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr index cd6e85764bda4..ef0e73f1481f5 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr @@ -1,5 +1,5 @@ error[E0277]: `T` doesn't implement `Debug` - --> $DIR/generic_duplicate_param_use2.rs:11:5 + --> $DIR/generic_duplicate_param_use2.rs:12:5 | LL | t | ^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug` @@ -10,7 +10,7 @@ note: required by a bound in an opaque type LL | type Two = impl Debug; | ^^^^^ note: this definition site has more where clauses than the opaque type - --> $DIR/generic_duplicate_param_use2.rs:10:1 + --> $DIR/generic_duplicate_param_use2.rs:11:1 | LL | fn two(t: T, _: U) -> Two { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use3.rs b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use3.rs index 2074f12750f67..873c7b614b69d 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use3.rs +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use3.rs @@ -7,11 +7,13 @@ fn main() {} // test that unused generic parameters are ok type Two = impl Debug; +#[define_opaque(Two)] fn two(t: T, _: U) -> Two { t } +#[define_opaque(Two)] fn three(_: T, u: U) -> Two { - u //~^ ERROR concrete type differs + u } diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr index 9a10a4980d806..3f5f2c93c59d2 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr @@ -1,14 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/generic_duplicate_param_use3.rs:15:5 + --> $DIR/generic_duplicate_param_use3.rs:16:38 | -LL | u - | ^ expected `T`, got `U` +LL | fn three(_: T, u: U) -> Two { + | ^^^^^^^^^ expected `T`, got `U` | note: previous use here - --> $DIR/generic_duplicate_param_use3.rs:11:5 + --> $DIR/generic_duplicate_param_use3.rs:11:36 | -LL | t - | ^ +LL | fn two(t: T, _: U) -> Two { + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.rs b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.rs index d1e5a0f0198b6..68a8f3da168b1 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.rs +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.rs @@ -7,6 +7,7 @@ fn main() {} // test that unused generic parameters are ok type Two = impl Debug; +#[define_opaque(Two)] fn three(_: T, u: U) -> Two { u //~^ ERROR `U` doesn't implement `Debug` diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr index bf3c4a0e04fe6..0932c72ff934f 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr @@ -1,5 +1,5 @@ error[E0277]: `U` doesn't implement `Debug` - --> $DIR/generic_duplicate_param_use4.rs:11:5 + --> $DIR/generic_duplicate_param_use4.rs:12:5 | LL | u | ^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` @@ -10,7 +10,7 @@ note: required by a bound in an opaque type LL | type Two = impl Debug; | ^^^^^ note: this definition site has more where clauses than the opaque type - --> $DIR/generic_duplicate_param_use4.rs:10:1 + --> $DIR/generic_duplicate_param_use4.rs:11:1 | LL | fn three(_: T, u: U) -> Two { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use5.rs b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use5.rs index b3d6beaf84869..1d4d3ab737f35 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use5.rs +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use5.rs @@ -7,11 +7,13 @@ fn main() {} // test that unused generic parameters are ok type Two = impl Debug; +#[define_opaque(Two)] fn two(t: T, u: U) -> Two { (t, u) } +#[define_opaque(Two)] fn three(t: T, u: U) -> Two { - (u, t) //~^ ERROR concrete type differs + (u, t) } diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use5.stderr b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use5.stderr index b0027f8fa57bb..59b37a8b792c0 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use5.stderr +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use5.stderr @@ -1,14 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/generic_duplicate_param_use5.rs:15:5 + --> $DIR/generic_duplicate_param_use5.rs:16:45 | -LL | (u, t) - | ^^^^^^ 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:11:5 + --> $DIR/generic_duplicate_param_use5.rs:11:43 | -LL | (t, u) - | ^^^^^^ +LL | fn two(t: T, u: U) -> Two { + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use6.rs b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use6.rs index fa8b2a290b933..961e2910dbeec 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use6.rs +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use6.rs @@ -7,11 +7,13 @@ fn main() {} // test that unused generic parameters are ok type Two = impl Debug; +#[define_opaque(Two)] fn two(t: T, u: U) -> Two { (t, t) } +#[define_opaque(Two)] fn three(t: T, u: U) -> Two { - (u, t) //~^ ERROR concrete type differs + (u, t) } diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use6.stderr b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use6.stderr index 09c01932cef7c..0940d6f541fc3 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use6.stderr +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use6.stderr @@ -1,14 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/generic_duplicate_param_use6.rs:15:5 + --> $DIR/generic_duplicate_param_use6.rs:16:52 | -LL | (u, t) - | ^^^^^^ 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:11:5 + --> $DIR/generic_duplicate_param_use6.rs:11:50 | -LL | (t, t) - | ^^^^^^ +LL | fn two(t: T, u: U) -> Two { + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use7.rs b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use7.rs index adc912294fdf9..6f4a81ed760a3 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use7.rs +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use7.rs @@ -7,18 +7,22 @@ fn main() {} type Two = impl Debug; +#[define_opaque(Two)] fn two(t: T, u: U) -> Two { (t, t) } +#[define_opaque(Two)] fn three(t: T, t2: T, u: U) -> Two { (t, t2) } +#[define_opaque(Two)] fn four(t: T, t2: T, u: U, v: V) -> Two { (t, t2) } +#[define_opaque(Two)] fn five(x: X, y: Y, y2: Y) -> Two { (y, y2) } diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use8.rs b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use8.rs index 76c13bb027b0f..febe1df81ae85 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use8.rs +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use8.rs @@ -6,11 +6,13 @@ fn main() {} type Two = impl Debug; +#[define_opaque(Two)] fn two(t: T, _: U) -> Two { (t, 4u32) } +#[define_opaque(Two)] fn three(_: T, u: U) -> Two { + //~^ ERROR concrete type differs (u, 4u32) - //~^ concrete type differs } diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use8.stderr b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use8.stderr index 09d2abe366366..f9615d455d134 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use8.stderr +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use8.stderr @@ -1,14 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/generic_duplicate_param_use8.rs:14:5 + --> $DIR/generic_duplicate_param_use8.rs:15:45 | -LL | (u, 4u32) - | ^^^^^^^^^ 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:10:5 + --> $DIR/generic_duplicate_param_use8.rs:10:43 | -LL | (t, 4u32) - | ^^^^^^^^^ +LL | fn two(t: T, _: U) -> Two { + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use9.rs b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use9.rs index 5da7aab0da7a6..ec03ff1675edb 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use9.rs +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use9.rs @@ -11,11 +11,13 @@ trait Foo { const BAR: Self::Bar; } +#[define_opaque(Two)] fn two(t: T, u: U) -> Two { (t, u, T::BAR) } +#[define_opaque(Two)] fn three(t: T, u: U) -> Two { - (t, u, 42) //~^ ERROR concrete type differs + (t, u, 42) } diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr index 6e1bb3dfa1744..df9984cd07392 100644 --- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr +++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr @@ -1,14 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/generic_duplicate_param_use9.rs:19:5 + --> $DIR/generic_duplicate_param_use9.rs:20:45 | -LL | (t, u, 42) - | ^^^^^^^^^^ 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:15:5 + --> $DIR/generic_duplicate_param_use9.rs:15:49 | -LL | (t, u, T::BAR) - | ^^^^^^^^^^^^^^ +LL | fn two(t: T, u: U) -> Two { + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/generic_lifetime_param.rs b/tests/ui/type-alias-impl-trait/generic_lifetime_param.rs index b9b34f55e7e22..d5fb1b461ec4c 100644 --- a/tests/ui/type-alias-impl-trait/generic_lifetime_param.rs +++ b/tests/ui/type-alias-impl-trait/generic_lifetime_param.rs @@ -6,6 +6,7 @@ fn main() {} type Region<'a> = impl std::fmt::Debug; +#[define_opaque(Region)] fn region<'b>(a: &'b ()) -> Region<'b> { a } diff --git a/tests/ui/type-alias-impl-trait/generic_nondefining_use.rs b/tests/ui/type-alias-impl-trait/generic_nondefining_use.rs index 68f4c6923ae18..cf38c93bd921e 100644 --- a/tests/ui/type-alias-impl-trait/generic_nondefining_use.rs +++ b/tests/ui/type-alias-impl-trait/generic_nondefining_use.rs @@ -12,20 +12,20 @@ type OneConst = impl Debug; // Not defining uses, because they doesn't define *all* possible generics. +#[define_opaque(OneTy)] fn concrete_ty() -> OneTy { - //~^ ERROR: non-defining opaque type use in defining scope - 5u32 //~^ ERROR: expected generic type parameter, found `u32` + 5u32 } +#[define_opaque(OneLifetime)] fn concrete_lifetime() -> OneLifetime<'static> { - //~^ ERROR: non-defining opaque type use in defining scope 6u32 //~^ ERROR: expected generic lifetime parameter, found `'static` } +#[define_opaque(OneConst)] fn concrete_const() -> OneConst<{ 123 }> { - //~^ ERROR: non-defining opaque type use in defining scope - 7u32 //~^ ERROR: expected generic constant parameter, found `123` + 7u32 } diff --git a/tests/ui/type-alias-impl-trait/generic_nondefining_use.stderr b/tests/ui/type-alias-impl-trait/generic_nondefining_use.stderr index bd68b4e3ea469..71e415271ee82 100644 --- a/tests/ui/type-alias-impl-trait/generic_nondefining_use.stderr +++ b/tests/ui/type-alias-impl-trait/generic_nondefining_use.stderr @@ -1,47 +1,11 @@ -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/generic_nondefining_use.rs:15:21 - | -LL | fn concrete_ty() -> OneTy { - | ^^^^^^^^^^ argument `u32` is not a generic parameter - | -note: for this opaque type - --> $DIR/generic_nondefining_use.rs:7:17 - | -LL | type OneTy = impl Debug; - | ^^^^^^^^^^ - error[E0792]: expected generic type parameter, found `u32` - --> $DIR/generic_nondefining_use.rs:17:5 + --> $DIR/generic_nondefining_use.rs:16:21 | LL | type OneTy = impl Debug; | - this generic parameter must be used with a generic type parameter ... -LL | 5u32 - | ^^^^ - -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/generic_nondefining_use.rs:21:27 - | -LL | fn concrete_lifetime() -> OneLifetime<'static> { - | ^^^^^^^^^^^^^^^^^^^^ argument `'static` is not a generic parameter - | -note: for this opaque type - --> $DIR/generic_nondefining_use.rs:9:24 - | -LL | type OneLifetime<'a> = impl Debug; - | ^^^^^^^^^^ - -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/generic_nondefining_use.rs:27:24 - | -LL | fn concrete_const() -> OneConst<{ 123 }> { - | ^^^^^^^^^^^^^^^^^ argument `123` is not a generic parameter - | -note: for this opaque type - --> $DIR/generic_nondefining_use.rs:11:33 - | -LL | type OneConst = impl Debug; - | ^^^^^^^^^^ +LL | fn concrete_ty() -> OneTy { + | ^^^^^^^^^^ error[E0792]: expected generic lifetime parameter, found `'static` --> $DIR/generic_nondefining_use.rs:23:5 @@ -53,14 +17,14 @@ LL | 6u32 | ^^^^ error[E0792]: expected generic constant parameter, found `123` - --> $DIR/generic_nondefining_use.rs:29:5 + --> $DIR/generic_nondefining_use.rs:28:24 | LL | type OneConst = impl Debug; | -------------- this generic parameter must be used with a generic constant parameter ... -LL | 7u32 - | ^^^^ +LL | fn concrete_const() -> OneConst<{ 123 }> { + | ^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/generic_not_used.rs b/tests/ui/type-alias-impl-trait/generic_not_used.rs index c70f473cff578..f270f65f43ed9 100644 --- a/tests/ui/type-alias-impl-trait/generic_not_used.rs +++ b/tests/ui/type-alias-impl-trait/generic_not_used.rs @@ -5,7 +5,8 @@ fn main() {} type WrongGeneric = impl 'static; //~^ ERROR: at least one trait must be specified +#[define_opaque(WrongGeneric)] fn wrong_generic(_: U, v: V) -> WrongGeneric { - v //~^ ERROR type parameter `V` is part of concrete type but not used in parameter list + v } diff --git a/tests/ui/type-alias-impl-trait/generic_not_used.stderr b/tests/ui/type-alias-impl-trait/generic_not_used.stderr index fd720239a5239..a480040006eaf 100644 --- a/tests/ui/type-alias-impl-trait/generic_not_used.stderr +++ b/tests/ui/type-alias-impl-trait/generic_not_used.stderr @@ -5,10 +5,10 @@ LL | type WrongGeneric = impl 'static; | ^^^^^^^^^^^^ error: type parameter `V` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/generic_not_used.rs:9:5 + --> $DIR/generic_not_used.rs:9:57 | -LL | v - | ^ +LL | fn wrong_generic(_: U, v: V) -> WrongGeneric { + | ^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs b/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs index c60f5c11cd18c..4b8a1cfc47240 100644 --- a/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs +++ b/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs @@ -9,6 +9,7 @@ fn main() { type WrongGeneric = impl 'static; //~^ ERROR: at least one trait must be specified + #[define_opaque(WrongGeneric)] fn wrong_generic(t: T) -> WrongGeneric { t //~^ ERROR the parameter type `T` may not live long enough diff --git a/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr b/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr index c352a33fbbcf0..2b3f7d7502835 100644 --- a/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr +++ b/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr @@ -14,7 +14,7 @@ LL | type WrongGeneric = impl 'static; | - this generic parameter must be used with a generic type parameter error[E0310]: the parameter type `T` may not live long enough - --> $DIR/generic_type_does_not_live_long_enough.rs:13:9 + --> $DIR/generic_type_does_not_live_long_enough.rs:14:9 | LL | t | ^ diff --git a/tests/ui/type-alias-impl-trait/generic_underconstrained.rs b/tests/ui/type-alias-impl-trait/generic_underconstrained.rs index 1acacc778de6c..9c2180e92fc6a 100644 --- a/tests/ui/type-alias-impl-trait/generic_underconstrained.rs +++ b/tests/ui/type-alias-impl-trait/generic_underconstrained.rs @@ -6,6 +6,7 @@ trait Trait {} type Underconstrained = impl Send; // no `Trait` bound +#[define_opaque(Underconstrained)] fn underconstrain(_: T) -> Underconstrained { //~^ ERROR the trait bound `T: Trait` //~| ERROR the trait bound `T: Trait` diff --git a/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr b/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr index c24f8fd867fb3..b1bc62c5910e1 100644 --- a/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr +++ b/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `T: Trait` is not satisfied - --> $DIR/generic_underconstrained.rs:9:31 + --> $DIR/generic_underconstrained.rs:10:31 | LL | fn underconstrain(_: T) -> Underconstrained { | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` @@ -15,7 +15,7 @@ LL | fn underconstrain(_: T) -> Underconstrained { | +++++++ error[E0277]: the trait bound `T: Trait` is not satisfied - --> $DIR/generic_underconstrained.rs:9:31 + --> $DIR/generic_underconstrained.rs:10:31 | LL | fn underconstrain(_: T) -> Underconstrained { | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` diff --git a/tests/ui/type-alias-impl-trait/generic_underconstrained2.rs b/tests/ui/type-alias-impl-trait/generic_underconstrained2.rs index 1e1bece9a1c0b..f05f9776006d6 100644 --- a/tests/ui/type-alias-impl-trait/generic_underconstrained2.rs +++ b/tests/ui/type-alias-impl-trait/generic_underconstrained2.rs @@ -5,6 +5,7 @@ fn main() {} type Underconstrained = impl Send; // not a defining use, because it doesn't define *all* possible generics +#[define_opaque(Underconstrained)] fn underconstrained(_: U) -> Underconstrained { //~^ ERROR `U` doesn't implement `Debug` //~| ERROR `U` doesn't implement `Debug` @@ -14,6 +15,7 @@ fn underconstrained(_: U) -> Underconstrained { type Underconstrained2 = impl Send; // not a defining use, because it doesn't define *all* possible generics +#[define_opaque(Underconstrained2)] fn underconstrained2(_: U, _: V) -> Underconstrained2 { //~^ ERROR `V` doesn't implement `Debug` //~| ERROR `V` doesn't implement `Debug` diff --git a/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr b/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr index 93df5ddca796f..429c3b9175a59 100644 --- a/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr +++ b/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr @@ -1,5 +1,5 @@ error[E0277]: `U` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:8:33 + --> $DIR/generic_underconstrained2.rs:9:33 | LL | fn underconstrained(_: U) -> Underconstrained { | ^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` @@ -15,13 +15,13 @@ LL | fn underconstrained(_: U) -> Underconstrained { | +++++++++++++++++ error[E0277]: `V` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:17:43 + --> $DIR/generic_underconstrained2.rs:19:43 | LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { | ^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` | note: required by a bound on the type alias `Underconstrained2` - --> $DIR/generic_underconstrained2.rs:14:27 + --> $DIR/generic_underconstrained2.rs:15:27 | LL | type Underconstrained2 = impl Send; | ^^^^^^^^^^^^^^^ required by this bound @@ -31,7 +31,7 @@ LL | fn underconstrained2(_: U, _: V) -> Underconstrained | +++++++++++++++++ error[E0277]: `U` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:8:33 + --> $DIR/generic_underconstrained2.rs:9:33 | LL | fn underconstrained(_: U) -> Underconstrained { | ^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` @@ -48,13 +48,13 @@ LL | fn underconstrained(_: U) -> Underconstrained { | +++++++++++++++++ error[E0277]: `V` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:17:43 + --> $DIR/generic_underconstrained2.rs:19:43 | LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { | ^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` | note: required by a bound on the type alias `Underconstrained2` - --> $DIR/generic_underconstrained2.rs:14:27 + --> $DIR/generic_underconstrained2.rs:15:27 | LL | type Underconstrained2 = impl Send; | ^^^^^^^^^^^^^^^ required by this bound diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_projection_behind_struct_field.rs b/tests/ui/type-alias-impl-trait/hidden_behind_projection_behind_struct_field.rs index eb19b49c7e213..bf97dd9a25c5d 100644 --- a/tests/ui/type-alias-impl-trait/hidden_behind_projection_behind_struct_field.rs +++ b/tests/ui/type-alias-impl-trait/hidden_behind_projection_behind_struct_field.rs @@ -15,6 +15,7 @@ trait Trait: Sized { impl Trait for Bar { type Assoc = impl std::fmt::Debug; + //~^ ERROR: unconstrained opaque type fn foo() -> Foo { Foo { field: () } //~^ ERROR: mismatched types diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_projection_behind_struct_field.stderr b/tests/ui/type-alias-impl-trait/hidden_behind_projection_behind_struct_field.stderr index 00aedfae46375..879f024dee714 100644 --- a/tests/ui/type-alias-impl-trait/hidden_behind_projection_behind_struct_field.stderr +++ b/tests/ui/type-alias-impl-trait/hidden_behind_projection_behind_struct_field.stderr @@ -1,20 +1,23 @@ +error: unconstrained opaque type + --> $DIR/hidden_behind_projection_behind_struct_field.rs:17:18 + | +LL | type Assoc = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `Assoc` must be used in combination with a concrete type within the same impl + error[E0308]: mismatched types - --> $DIR/hidden_behind_projection_behind_struct_field.rs:19:22 + --> $DIR/hidden_behind_projection_behind_struct_field.rs:20:22 | LL | type Assoc = impl std::fmt::Debug; | -------------------- the expected opaque type -LL | fn foo() -> Foo { +... LL | Foo { field: () } | ^^ expected opaque type, found `()` | = note: expected opaque type `::Assoc` found unit type `()` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/hidden_behind_projection_behind_struct_field.rs:18:8 - | -LL | fn foo() -> Foo { - | ^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs index 3117060cef0d2..b1dcf98f4dd27 100644 --- a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs +++ b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs @@ -13,6 +13,7 @@ trait Trait: Sized { impl Trait for Bar { type Assoc = impl std::fmt::Debug; + //~^ ERROR: unconstrained opaque type fn foo() -> Foo { Foo { field: () } //~^ ERROR: mismatched types diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr index 4910e794e8d6e..1820560eaddd2 100644 --- a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr +++ b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr @@ -1,20 +1,23 @@ +error: unconstrained opaque type + --> $DIR/hidden_behind_struct_field2.rs:15:18 + | +LL | type Assoc = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `Assoc` must be used in combination with a concrete type within the same impl + error[E0308]: mismatched types - --> $DIR/hidden_behind_struct_field2.rs:17:22 + --> $DIR/hidden_behind_struct_field2.rs:18:22 | LL | type Assoc = impl std::fmt::Debug; | -------------------- the expected opaque type -LL | fn foo() -> Foo { +... LL | Foo { field: () } | ^^ expected opaque type, found `()` | = note: expected opaque type `::Assoc` found unit type `()` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/hidden_behind_struct_field2.rs:16:8 - | -LL | fn foo() -> Foo { - | ^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field3.rs b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field3.rs index c1f135994121e..70697e2fa6581 100644 --- a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field3.rs +++ b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field3.rs @@ -14,6 +14,7 @@ trait Trait: Sized { impl Trait for Bar { type Assoc2 = impl std::fmt::Debug; + //~^ ERROR: unconstrained opaque type type Assoc = impl Iterator; fn foo() -> Self::Assoc { vec![Foo { field: () }].into_iter() diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field3.stderr b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field3.stderr index f10ccc0029973..9ce696fa26c40 100644 --- a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field3.stderr +++ b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field3.stderr @@ -1,5 +1,13 @@ +error: unconstrained opaque type + --> $DIR/hidden_behind_struct_field3.rs:16:19 + | +LL | type Assoc2 = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `Assoc2` must be used in combination with a concrete type within the same impl + error[E0308]: mismatched types - --> $DIR/hidden_behind_struct_field3.rs:19:27 + --> $DIR/hidden_behind_struct_field3.rs:20:27 | LL | type Assoc2 = impl std::fmt::Debug; | -------------------- the expected opaque type @@ -9,12 +17,7 @@ LL | vec![Foo { field: () }].into_iter() | = note: expected opaque type `::Assoc2` found unit type `()` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/hidden_behind_struct_field3.rs:18:8 - | -LL | fn foo() -> Self::Assoc { - | ^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/hidden_type_mismatch.rs b/tests/ui/type-alias-impl-trait/hidden_type_mismatch.rs index 5b5acb31812f3..0a8860321daa9 100644 --- a/tests/ui/type-alias-impl-trait/hidden_type_mismatch.rs +++ b/tests/ui/type-alias-impl-trait/hidden_type_mismatch.rs @@ -9,6 +9,7 @@ mod sus { use super::*; pub type Sep = impl Sized + std::fmt::Display; + #[define_opaque(Sep)] pub fn mk_sep() -> Sep { String::from("hello") } @@ -34,6 +35,7 @@ mod sus { // `define_tait` is not actually callable, and thus assumed // `Bar<()>: Copy` even though it isn't. pub type Tait = impl Copy + From> + Into>; + #[define_opaque(Tait)] pub fn define_tait() -> Tait where // this proves `Bar<()>: Copy`, but `define_tait` is diff --git a/tests/ui/type-alias-impl-trait/hidden_type_mismatch.stderr b/tests/ui/type-alias-impl-trait/hidden_type_mismatch.stderr index 8547fd53c18e3..20991a30b531c 100644 --- a/tests/ui/type-alias-impl-trait/hidden_type_mismatch.stderr +++ b/tests/ui/type-alias-impl-trait/hidden_type_mismatch.stderr @@ -1,5 +1,5 @@ error[E0271]: type mismatch resolving `<() as Proj>::Assoc == i32` - --> $DIR/hidden_type_mismatch.rs:43:9 + --> $DIR/hidden_type_mismatch.rs:45:9 | LL | pub type Sep = impl Sized + std::fmt::Display; | ------------------------------ the found opaque type @@ -8,26 +8,26 @@ LL | Bar { inner: 1i32, _marker: () } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<() as Proj>::Assoc == i32` | note: expected this to be `i32` - --> $DIR/hidden_type_mismatch.rs:20:22 + --> $DIR/hidden_type_mismatch.rs:21:22 | LL | type Assoc = sus::Sep; | ^^^^^^^^ = note: expected type `i32` found opaque type `Sep` note: required for `Bar<()>` to implement `Copy` - --> $DIR/hidden_type_mismatch.rs:32:39 + --> $DIR/hidden_type_mismatch.rs:33:39 | LL | impl + Copy> Copy for Bar {} | ----------- ^^^^ ^^^^^^ | | | unsatisfied trait bound introduced here note: required by a bound in an opaque type - --> $DIR/hidden_type_mismatch.rs:36:26 + --> $DIR/hidden_type_mismatch.rs:37:26 | LL | pub type Tait = impl Copy + From> + Into>; | ^^^^ note: this definition site has more where clauses than the opaque type - --> $DIR/hidden_type_mismatch.rs:37:5 + --> $DIR/hidden_type_mismatch.rs:39:5 | LL | / pub fn define_tait() -> Tait LL | | where diff --git a/tests/ui/type-alias-impl-trait/higher_kinded_params.rs b/tests/ui/type-alias-impl-trait/higher_kinded_params.rs index e43f53e40578c..6008df3b0a2f8 100644 --- a/tests/ui/type-alias-impl-trait/higher_kinded_params.rs +++ b/tests/ui/type-alias-impl-trait/higher_kinded_params.rs @@ -22,6 +22,7 @@ struct Terminator; type Successors<'a> = impl std::fmt::Debug + 'a; impl Terminator { + #[define_opaque(Successors)] fn successors(&self, _: for<'x> fn(&'x ()) -> <&'x A as B>::C) -> Successors<'_> {} } diff --git a/tests/ui/type-alias-impl-trait/higher_kinded_params2.rs b/tests/ui/type-alias-impl-trait/higher_kinded_params2.rs index 19c6099135d83..8ed7c1336f26c 100644 --- a/tests/ui/type-alias-impl-trait/higher_kinded_params2.rs +++ b/tests/ui/type-alias-impl-trait/higher_kinded_params2.rs @@ -2,6 +2,7 @@ //! and normalizing something behind them actually works. //@ edition: 2021 +//@ check-pass #![feature(type_alias_impl_trait)] @@ -22,9 +23,9 @@ struct Terminator; type Successors<'a> = impl std::fmt::Debug + 'a; impl Terminator { + #[define_opaque(Successors, Tait)] fn successors(&self, mut f: for<'x> fn(&'x ()) -> <&'x A as B>::C) -> Successors<'_> { f = g; - //~^ ERROR mismatched types } } diff --git a/tests/ui/type-alias-impl-trait/higher_kinded_params2.stderr b/tests/ui/type-alias-impl-trait/higher_kinded_params2.stderr deleted file mode 100644 index 790e7fe858037..0000000000000 --- a/tests/ui/type-alias-impl-trait/higher_kinded_params2.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/higher_kinded_params2.rs:26:13 - | -LL | type Tait = impl std::fmt::Debug; - | -------------------- the expected opaque type -... -LL | f = g; - | ^ expected fn pointer, found fn item - | - = note: expected fn pointer `for<'x> fn(&'x ()) -> Tait` - found fn item `for<'a> fn(&'a ()) -> String {g}` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/higher_kinded_params2.rs:25:8 - | -LL | fn successors(&self, mut f: for<'x> fn(&'x ()) -> <&'x A as B>::C) -> Successors<'_> { - | ^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/higher_kinded_params3.rs b/tests/ui/type-alias-impl-trait/higher_kinded_params3.rs index 3845cde29fa55..4fb2e60b5c5a3 100644 --- a/tests/ui/type-alias-impl-trait/higher_kinded_params3.rs +++ b/tests/ui/type-alias-impl-trait/higher_kinded_params3.rs @@ -22,6 +22,7 @@ struct Terminator; type Successors<'a> = impl std::fmt::Debug + 'a; impl Terminator { + #[define_opaque(Successors, Tait)] fn successors(&self, mut f: for<'x> fn(&'x ()) -> <&'x A as B>::C) -> Successors<'_> { f = g; //~^ ERROR mismatched types diff --git a/tests/ui/type-alias-impl-trait/higher_kinded_params3.stderr b/tests/ui/type-alias-impl-trait/higher_kinded_params3.stderr index 41a3f9ce26820..558792987f31c 100644 --- a/tests/ui/type-alias-impl-trait/higher_kinded_params3.stderr +++ b/tests/ui/type-alias-impl-trait/higher_kinded_params3.stderr @@ -1,19 +1,14 @@ error[E0308]: mismatched types - --> $DIR/higher_kinded_params3.rs:26:13 + --> $DIR/higher_kinded_params3.rs:27:9 | LL | type Tait<'a> = impl std::fmt::Debug + 'a; | ------------------------- the expected opaque type ... LL | f = g; - | ^ expected fn pointer, found fn item + | ^^^^^ one type is more general than the other | = note: expected fn pointer `for<'x> fn(&'x ()) -> Tait<'x>` - found fn item `for<'a> fn(&'a ()) -> &'a () {g}` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/higher_kinded_params3.rs:25:8 - | -LL | fn successors(&self, mut f: for<'x> fn(&'x ()) -> <&'x A as B>::C) -> Successors<'_> { - | ^^^^^^^^^^ + found fn pointer `for<'a> fn(&'a ()) -> &'a ()` error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden.rs b/tests/ui/type-alias-impl-trait/hkl_forbidden.rs index c6d1202ef85fd..994adc476e242 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden.rs +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden.rs @@ -6,6 +6,7 @@ fn id(s: &str) -> &str { type Opaque<'a> = impl Sized + 'a; +#[define_opaque(Opaque)] fn test(s: &str) -> (impl Fn(&str) -> Opaque<'_>, impl Fn(&str) -> Opaque<'_>) { (id, id) //~ ERROR expected generic lifetime parameter, found `'_` } @@ -16,22 +17,26 @@ fn id2<'a, 'b>(s: (&'a str, &'b str)) -> (&'a str, &'b str) { type Opaque2<'a> = impl Sized + 'a; +#[define_opaque(Opaque2)] fn test2() -> impl for<'a, 'b> Fn((&'a str, &'b str)) -> (Opaque2<'a>, Opaque2<'b>) { id2 //~ ERROR expected generic lifetime parameter, found `'a` } type Opaque3<'a> = impl Sized + 'a; +#[define_opaque(Opaque3)] fn test3(s: &str) -> (impl Fn(&str) -> Opaque3<'_>, Opaque3<'_>) { (id, s) //~ ERROR expected generic lifetime parameter, found `'_` } type Opaque4<'a> = impl Sized + 'a; +#[define_opaque(Opaque4)] fn test4(s: &str) -> (Opaque4<'_>, impl Fn(&str) -> Opaque4<'_>) { (s, id) //~ ERROR expected generic lifetime parameter, found `'_` } type Inner<'a> = impl Sized; +#[define_opaque(Inner)] fn outer_impl() -> impl for<'a> Fn(&'a ()) -> Inner<'a> { |x| x //~ ERROR expected generic lifetime parameter, found `'a` } diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden.stderr b/tests/ui/type-alias-impl-trait/hkl_forbidden.stderr index d49be73d94e77..d404d60f31ed9 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden.stderr +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/hkl_forbidden.rs:10:5 + --> $DIR/hkl_forbidden.rs:11:5 | LL | type Opaque<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter @@ -8,7 +8,7 @@ LL | (id, id) | ^^^^^^^^ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/hkl_forbidden.rs:20:5 + --> $DIR/hkl_forbidden.rs:22:5 | LL | type Opaque2<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter @@ -17,7 +17,7 @@ LL | id2 | ^^^ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/hkl_forbidden.rs:26:5 + --> $DIR/hkl_forbidden.rs:29:5 | LL | type Opaque3<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter @@ -26,20 +26,20 @@ LL | (id, s) | ^^^^^^^ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/hkl_forbidden.rs:31:5 + --> $DIR/hkl_forbidden.rs:35:5 | LL | type Opaque4<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter -LL | fn test4(s: &str) -> (Opaque4<'_>, impl Fn(&str) -> Opaque4<'_>) { +... LL | (s, id) | ^^^^^^^ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/hkl_forbidden.rs:36:5 + --> $DIR/hkl_forbidden.rs:41:5 | LL | type Inner<'a> = impl Sized; | -- this generic parameter must be used with a generic lifetime parameter -LL | fn outer_impl() -> impl for<'a> Fn(&'a ()) -> Inner<'a> { +... LL | |x| x | ^^^^^ diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden2.rs b/tests/ui/type-alias-impl-trait/hkl_forbidden2.rs index 3d583d4413d9a..92e383d11f1c0 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden2.rs +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden2.rs @@ -10,6 +10,7 @@ impl<'a> Trait<'a> for () { type Assoc = (); } +#[define_opaque(Opaque)] fn test() -> &'static dyn for<'a> Trait<'a, Assoc = Opaque<'a>> { &() //~^ ERROR: expected generic lifetime parameter, found `'a` diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden2.stderr b/tests/ui/type-alias-impl-trait/hkl_forbidden2.stderr index 0a9a9d6bcf4e6..7404c844ae7b0 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden2.stderr +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden2.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/hkl_forbidden2.rs:14:5 + --> $DIR/hkl_forbidden2.rs:15:5 | LL | type Opaque<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden3.rs b/tests/ui/type-alias-impl-trait/hkl_forbidden3.rs index a4148599f77a3..c7f04dc07bb12 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden3.rs +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden3.rs @@ -6,6 +6,7 @@ fn foo<'a>(x: &'a ()) -> &'a () { x } +#[define_opaque(Opaque)] fn test() -> for<'a> fn(&'a ()) -> Opaque<'a> { foo //~ ERROR: mismatched types } diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden3.stderr b/tests/ui/type-alias-impl-trait/hkl_forbidden3.stderr index d262177a86bca..b8c04185a7d14 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden3.stderr +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden3.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/hkl_forbidden3.rs:10:5 + --> $DIR/hkl_forbidden3.rs:11:5 | LL | type Opaque<'a> = impl Sized + 'a; | --------------- the expected opaque type diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden4.rs b/tests/ui/type-alias-impl-trait/hkl_forbidden4.rs index fd06ea677c336..4b4db8ec2ed3b 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden4.rs +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden4.rs @@ -10,18 +10,15 @@ use std::future::Future; type FutNothing<'a> = impl 'a + Future; async fn operation(_: &mut ()) -> () { - //~^ ERROR: concrete type differs from previous call(operation).await - //~^ ERROR: expected generic lifetime parameter, found `'any` + //~^ ERROR: concrete type differs from previous } +#[define_opaque(FutNothing)] async fn call(_f: F) -//~^ ERROR item does not constrain +//~^ ERROR item does not constrain `FutNothing::{opaque#0}` where for<'any> F: FnMut(&'any mut ()) -> FutNothing<'any>, -{ - //~^ ERROR: expected generic lifetime parameter, found `'any` - //~| ERROR item does not constrain -} +{} fn main() {} diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr b/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr index feb161c3b048e..2aacf9698379b 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr @@ -1,63 +1,27 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/hkl_forbidden4.rs:12:1 - | -LL | async fn operation(_: &mut ()) -> () { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `FutNothing<'_>`, got `{async fn body of operation()}` - | -note: previous use here - --> $DIR/hkl_forbidden4.rs:14:5 - | -LL | call(operation).await - | ^^^^^^^^^^^^^^^ - -error: item does not constrain `FutNothing::{opaque#0}`, but has it in its signature +error: item does not constrain `FutNothing::{opaque#0}` --> $DIR/hkl_forbidden4.rs:18:10 | LL | async fn call(_f: F) | ^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/hkl_forbidden4.rs:10:23 | LL | type FutNothing<'a> = impl 'a + Future; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: item does not constrain `FutNothing::{opaque#0}`, but has it in its signature - --> $DIR/hkl_forbidden4.rs:22:1 - | -LL | / { -... | -LL | | } - | |_^ +error: concrete type differs from previous defining opaque type use + --> $DIR/hkl_forbidden4.rs:13:5 | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/hkl_forbidden4.rs:10:23 +LL | call(operation).await + | ^^^^^^^^^^^^^^^ expected `{async fn body of operation()}`, got `FutNothing<'_>` | -LL | type FutNothing<'a> = impl 'a + Future; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0792]: expected generic lifetime parameter, found `'any` - --> $DIR/hkl_forbidden4.rs:14:5 +note: previous use here + --> $DIR/hkl_forbidden4.rs:12:1 | LL | async fn operation(_: &mut ()) -> () { - | - this generic parameter must be used with a generic lifetime parameter -LL | -LL | call(operation).await - | ^^^^^^^^^^^^^^^ - -error[E0792]: expected generic lifetime parameter, found `'any` - --> $DIR/hkl_forbidden4.rs:22:1 - | -LL | type FutNothing<'a> = impl 'a + Future; - | -- this generic parameter must be used with a generic lifetime parameter -... -LL | / { -... | -LL | | } - | |_^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs index 2bcb8f06f4fd6..29e7c7d19fc62 100644 --- a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs +++ b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs @@ -18,6 +18,7 @@ trait MyFrom: Sized { trait F {} impl F for () {} type DummyT = impl F; +#[define_opaque(DummyT)] fn _dummy_t() -> DummyT {} struct Phantom1(PhantomData); @@ -25,6 +26,7 @@ struct Phantom2(PhantomData); struct Scope(Phantom2>); impl Scope { + #[define_opaque(DummyT)] fn new() -> Self { //~^ ERROR item does not constrain unimplemented!() @@ -41,6 +43,7 @@ impl MyFrom> for Phantom1 { impl>>, U> MyIndex> for Scope { //~^ ERROR the type parameter `T` is not constrained by the impl type O = T; + #[define_opaque(DummyT)] fn my_index(self) -> Self::O { MyFrom::my_from(self.0).ok().unwrap() } diff --git a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr index 0ab4c34381a26..d18a824287c0c 100644 --- a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr +++ b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr @@ -1,17 +1,17 @@ error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:41:6 + --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:43:6 | LL | impl>>, U> MyIndex> for Scope { | ^ unconstrained type parameter -error: item does not constrain `DummyT::{opaque#0}`, but has it in its signature - --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:28:8 +error: item does not constrain `DummyT::{opaque#0}` + --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:30:8 | LL | fn new() -> Self { | ^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:20:18 | LL | type DummyT = impl F; diff --git a/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.rs b/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.rs index 71416eb531ac2..a9cae611ec55e 100644 --- a/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.rs +++ b/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.rs @@ -18,6 +18,7 @@ impl Foo for () { type Baz = impl Sized; //~^ ERROR type `Baz` has 1 type parameter but its trait declaration has 0 type parameters + //~| ERROR: unconstrained opaque type fn test<'a>() -> Self::Bar<'a> { &() diff --git a/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.stderr b/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.stderr index e5a21ff8b4e87..c58d919c4b124 100644 --- a/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.stderr +++ b/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.stderr @@ -1,4 +1,4 @@ -error[E0049]: type `Baz` has 1 type parameter but its trait declaration has 0 type parameters +error[E0049]: associated type `Baz` has 1 type parameter but its trait declaration has 0 type parameters --> $DIR/impl-trait-in-type-alias-with-bad-substs.rs:19:14 | LL | type Baz<'a>; @@ -7,6 +7,14 @@ LL | type Baz<'a>; LL | type Baz = impl Sized; | ^ found 1 type parameter -error: aborting due to 1 previous error +error: unconstrained opaque type + --> $DIR/impl-trait-in-type-alias-with-bad-substs.rs:19:19 + | +LL | type Baz = impl Sized; + | ^^^^^^^^^^ + | + = note: `Baz` must be used in combination with a concrete type within the same impl + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0049`. diff --git a/tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs b/tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs index 00d1a1a226d2f..b50345bb637c3 100644 --- a/tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs +++ b/tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs @@ -7,6 +7,7 @@ auto trait Trait {} impl Trait for Alias {} //~^ ERROR traits with a default impl, like `Trait`, cannot be implemented for type alias `Alias` +#[define_opaque(Alias)] fn _def() -> Alias { (42, 42) } diff --git a/tests/ui/type-alias-impl-trait/impl_trait_for_generic_tait.rs b/tests/ui/type-alias-impl-trait/impl_trait_for_generic_tait.rs index 91610c92d2278..3bbfee42ef6c7 100644 --- a/tests/ui/type-alias-impl-trait/impl_trait_for_generic_tait.rs +++ b/tests/ui/type-alias-impl-trait/impl_trait_for_generic_tait.rs @@ -9,6 +9,7 @@ impl Foo for i32 { type Assoc = u32; } type ImplTrait = impl Sized; +#[define_opaque(ImplTrait)] fn constrain() -> ImplTrait { 1u64 } diff --git a/tests/ui/type-alias-impl-trait/impl_trait_for_same_tait.rs b/tests/ui/type-alias-impl-trait/impl_trait_for_same_tait.rs index 3f1a9d12b44f9..a05c65be48bcc 100644 --- a/tests/ui/type-alias-impl-trait/impl_trait_for_same_tait.rs +++ b/tests/ui/type-alias-impl-trait/impl_trait_for_same_tait.rs @@ -5,6 +5,7 @@ impl Foo for () {} impl Foo for i32 {} type Bar = impl std::fmt::Debug; +#[define_opaque(Bar)] fn defining_use() -> Bar { 42 } @@ -18,6 +19,7 @@ impl Bop for Bar {} //~^ ERROR conflicting implementations type Barr = impl std::fmt::Debug; +#[define_opaque(Barr)] fn defining_use2() -> Barr { 42 } diff --git a/tests/ui/type-alias-impl-trait/impl_trait_for_same_tait.stderr b/tests/ui/type-alias-impl-trait/impl_trait_for_same_tait.stderr index aaf75cc3db97c..2f44ee481adcd 100644 --- a/tests/ui/type-alias-impl-trait/impl_trait_for_same_tait.stderr +++ b/tests/ui/type-alias-impl-trait/impl_trait_for_same_tait.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Bop` for type `Bar<()>` - --> $DIR/impl_trait_for_same_tait.rs:17:1 + --> $DIR/impl_trait_for_same_tait.rs:18:1 | LL | impl Bop for Bar<()> {} | -------------------- first implementation here @@ -8,7 +8,7 @@ LL | impl Bop for Bar {} | ^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<()>` error[E0119]: conflicting implementations of trait `Bop` for type `Bar<()>` - --> $DIR/impl_trait_for_same_tait.rs:26:1 + --> $DIR/impl_trait_for_same_tait.rs:28:1 | LL | impl Bop for Bar<()> {} | -------------------- first implementation here @@ -17,7 +17,7 @@ LL | impl Bop for Barr {} | ^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<()>` error[E0119]: conflicting implementations of trait `Bop` for type `Bar<()>` - --> $DIR/impl_trait_for_same_tait.rs:30:1 + --> $DIR/impl_trait_for_same_tait.rs:32:1 | LL | impl Bop for Bar<()> {} | -------------------- first implementation here diff --git a/tests/ui/type-alias-impl-trait/impl_trait_for_tait.rs b/tests/ui/type-alias-impl-trait/impl_trait_for_tait.rs index bce5ba7c91c30..07ac284d5016c 100644 --- a/tests/ui/type-alias-impl-trait/impl_trait_for_tait.rs +++ b/tests/ui/type-alias-impl-trait/impl_trait_for_tait.rs @@ -4,6 +4,7 @@ #![feature(type_alias_impl_trait)] type Alias = impl Sized; +#[define_opaque(Alias)] fn constrain() -> Alias { 1i32 } diff --git a/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound.rs b/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound.rs index 8ec20acef4de6..e0045df08ec7c 100644 --- a/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound.rs +++ b/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound.rs @@ -3,17 +3,18 @@ use std::fmt::Debug; type Foo = impl Debug; -pub trait Yay { } -impl Yay for Foo { } +pub trait Yay {} +impl Yay for Foo {} +#[define_opaque(Foo)] fn foo() { - is_yay::(); //~ ERROR: the trait bound `u32: Yay` is not satisfied + is_yay::(); //~ ERROR: the trait bound `u32: Yay` is not satisfied is_debug::(); // OK - is_yay::(); // OK + is_yay::(); // OK is_debug::(); // OK } -fn is_yay() { } -fn is_debug() { } +fn is_yay() {} +fn is_debug() {} fn main() {} diff --git a/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound.stderr b/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound.stderr index 9840bcef7d14b..0ebf9f064373e 100644 --- a/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound.stderr +++ b/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound.stderr @@ -1,14 +1,14 @@ error[E0277]: the trait bound `u32: Yay` is not satisfied - --> $DIR/impl_trait_for_tait_bound.rs:10:14 + --> $DIR/impl_trait_for_tait_bound.rs:11:14 | LL | is_yay::(); | ^^^ the trait `Yay` is not implemented for `u32` | = help: the trait `Yay` is implemented for `Foo` note: required by a bound in `is_yay` - --> $DIR/impl_trait_for_tait_bound.rs:16:14 + --> $DIR/impl_trait_for_tait_bound.rs:17:14 | -LL | fn is_yay() { } +LL | fn is_yay() {} | ^^^ required by this bound in `is_yay` error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound2.rs b/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound2.rs index a4b8c2d190db9..0a8dadb89e2ed 100644 --- a/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound2.rs +++ b/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound2.rs @@ -4,13 +4,14 @@ use std::fmt::Debug; type Foo = impl Debug; -pub trait Yay { } -impl Yay for u32 { } +pub trait Yay {} +impl Yay for u32 {} +#[define_opaque(Foo)] fn foo() { is_yay::(); //~ ERROR: the trait bound `Foo: Yay` is not satisfied } -fn is_yay() { } +fn is_yay() {} fn main() {} diff --git a/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound2.stderr b/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound2.stderr index 2259aa7bb1508..4fff9ad26cb35 100644 --- a/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound2.stderr +++ b/tests/ui/type-alias-impl-trait/impl_trait_for_tait_bound2.stderr @@ -1,14 +1,14 @@ error[E0277]: the trait bound `Foo: Yay` is not satisfied - --> $DIR/impl_trait_for_tait_bound2.rs:11:14 + --> $DIR/impl_trait_for_tait_bound2.rs:12:14 | LL | is_yay::(); | ^^^ the trait `Yay` is not implemented for `Foo` | = help: the trait `Yay` is implemented for `u32` note: required by a bound in `is_yay` - --> $DIR/impl_trait_for_tait_bound2.rs:14:14 + --> $DIR/impl_trait_for_tait_bound2.rs:15:14 | -LL | fn is_yay() { } +LL | fn is_yay() {} | ^^^ required by this bound in `is_yay` error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr index 1d7a97c536713..a7e4ba29d3a6b 100644 --- a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr +++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr @@ -11,11 +11,6 @@ LL | let x: Self::Assoc = 42; | = note: expected opaque type `<() as Trait>::Assoc` found type `{integer}` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:14:8 - | -LL | fn foo() { - | ^^^ error[E0308]: mismatched types --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:31:30 @@ -30,11 +25,6 @@ LL | let x: Self::Assoc = 42; | = note: expected opaque type `<() as Trait2>::Assoc` found type `{integer}` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:30:11 - | -LL | const FOO: () = { - | ^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/implied_bounds.rs b/tests/ui/type-alias-impl-trait/implied_bounds.rs index 269c0eff025d2..65d7234bd4444 100644 --- a/tests/ui/type-alias-impl-trait/implied_bounds.rs +++ b/tests/ui/type-alias-impl-trait/implied_bounds.rs @@ -1,11 +1,8 @@ #![feature(type_alias_impl_trait)] -mod foo { - use super::Equals; - pub type WithLifetime<'a> = impl Equals; - fn _defining_use<'a>() -> WithLifetime<'a> {} -} -use foo::WithLifetime; +pub type WithLifetime<'a> = impl Equals; +#[define_opaque(WithLifetime)] +fn _defining_use<'a>() -> WithLifetime<'a> {} trait Convert<'a> { type Witness; diff --git a/tests/ui/type-alias-impl-trait/implied_bounds.stderr b/tests/ui/type-alias-impl-trait/implied_bounds.stderr index 23f1141e54447..eda87bcbf90c4 100644 --- a/tests/ui/type-alias-impl-trait/implied_bounds.stderr +++ b/tests/ui/type-alias-impl-trait/implied_bounds.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/implied_bounds.rs:21:9 + --> $DIR/implied_bounds.rs:18:9 | LL | impl<'a> Convert<'a> for () { | -- lifetime `'a` defined here diff --git a/tests/ui/type-alias-impl-trait/implied_bounds2.rs b/tests/ui/type-alias-impl-trait/implied_bounds2.rs index 845476ef97419..0d829113ce708 100644 --- a/tests/ui/type-alias-impl-trait/implied_bounds2.rs +++ b/tests/ui/type-alias-impl-trait/implied_bounds2.rs @@ -2,12 +2,10 @@ #![feature(type_alias_impl_trait)] -mod helper { - pub type Ty<'a, A> = impl Sized + 'a; - fn defining<'a, A>() -> Ty<'a, A> {} - pub fn assert_static() {} -} -use helper::*; +pub type Ty<'a, A> = impl Sized + 'a; +#[define_opaque(Ty)] +fn defining<'a, A>() -> Ty<'a, A> {} +pub fn assert_static() {} fn test<'a, A>() where Ty<'a, A>: 'static, diff --git a/tests/ui/type-alias-impl-trait/implied_bounds_from_types.rs b/tests/ui/type-alias-impl-trait/implied_bounds_from_types.rs index 76a63741e18e5..b6120cd4cc3db 100644 --- a/tests/ui/type-alias-impl-trait/implied_bounds_from_types.rs +++ b/tests/ui/type-alias-impl-trait/implied_bounds_from_types.rs @@ -1,11 +1,8 @@ #![feature(type_alias_impl_trait)] -mod foo { - use super::Equals; - pub type WithLifetime = impl Equals; - fn _defining_use() -> WithLifetime {} -} -use foo::WithLifetime; +pub type WithLifetime = impl Equals; +#[define_opaque(WithLifetime)] +fn _defining_use() -> WithLifetime {} trait Convert<'a> { type Witness; diff --git a/tests/ui/type-alias-impl-trait/implied_bounds_from_types.stderr b/tests/ui/type-alias-impl-trait/implied_bounds_from_types.stderr index 391a8a75786af..887c8552d2f74 100644 --- a/tests/ui/type-alias-impl-trait/implied_bounds_from_types.stderr +++ b/tests/ui/type-alias-impl-trait/implied_bounds_from_types.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/implied_bounds_from_types.rs:21:9 + --> $DIR/implied_bounds_from_types.rs:18:9 | LL | impl<'a> Convert<'a> for () { | -- lifetime `'a` defined here diff --git a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check.error.stderr b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check.error.stderr index 49997b073c954..3bc6b9205ec15 100644 --- a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check.error.stderr +++ b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check.error.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Yay` for type `<() as HideIt>::Assoc` - --> $DIR/implied_lifetime_wf_check.rs:26:1 + --> $DIR/implied_lifetime_wf_check.rs:27:1 | LL | impl Yay for <() as HideIt>::Assoc {} | ---------------------------------- first implementation here diff --git a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check.rs b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check.rs index d85c7f60023ee..7950d6ad80c0a 100644 --- a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check.rs +++ b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check.rs @@ -7,6 +7,7 @@ type Alias = impl Sized; +#[define_opaque(Alias)] fn constrain() -> Alias { 1i32 } diff --git a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs index fb251e9bde155..889438d1b733e 100644 --- a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs +++ b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs @@ -2,6 +2,7 @@ mod test_lifetime_param { pub type Ty<'a> = impl Sized; + #[define_opaque(Ty)] fn defining(a: &str) -> Ty<'_> { a } @@ -17,6 +18,7 @@ where mod test_higher_kinded_lifetime_param { pub type Ty<'a> = impl Sized + 'a; + #[define_opaque(Ty)] fn defining(a: &str) -> Ty<'_> { a } @@ -40,6 +42,7 @@ mod test_higher_kinded_lifetime_param2 { mod test_type_param { pub type Ty = impl Sized; + #[define_opaque(Ty)] fn defining(s: A) -> Ty { s } @@ -54,13 +57,13 @@ where } mod test_implied_from_fn_sig { - mod foo { - pub type Opaque = impl Sized; - fn defining() -> Opaque {} - } + pub type Opaque = impl Sized; + #[define_opaque(Opaque)] + fn defining() -> Opaque {} + fn assert_static() {} - fn test(_: foo::Opaque) { + fn test(_: Opaque) { assert_static::(); } } diff --git a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr index b7c9c131c7d1a..7311f5882f75d 100644 --- a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr +++ b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/implied_lifetime_wf_check3.rs:14:5 + --> $DIR/implied_lifetime_wf_check3.rs:15:5 | LL | fn test_lifetime_param_test<'a>() | -- lifetime `'a` defined here @@ -8,7 +8,7 @@ LL | test_lifetime_param::assert_static::<'a>() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/implied_lifetime_wf_check3.rs:29:5 + --> $DIR/implied_lifetime_wf_check3.rs:31:5 | LL | fn test_higher_kinded_lifetime_param_test<'a>() | -- lifetime `'a` defined here @@ -17,7 +17,7 @@ LL | test_higher_kinded_lifetime_param::assert_static::<'a>() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/implied_lifetime_wf_check3.rs:36:9 + --> $DIR/implied_lifetime_wf_check3.rs:38:9 | LL | fn test<'a>() { | -- lifetime `'a` defined here @@ -25,10 +25,10 @@ LL | assert_static::<'a>() | ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` error[E0310]: the parameter type `A` may not live long enough - --> $DIR/implied_lifetime_wf_check3.rs:52:5 + --> $DIR/implied_lifetime_wf_check3.rs:55:5 | LL | test_type_param::assert_static::() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | the parameter type `A` must be valid for the static lifetime... | ...so that the type `A` will meet its required lifetime bounds diff --git a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.rs b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.rs index 7b2bbc995307e..5484b91e6f1f4 100644 --- a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.rs +++ b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.rs @@ -1,14 +1,12 @@ #![feature(type_alias_impl_trait)] -mod test_type_param_static { - pub type Ty = impl Sized + 'static; - fn defining(s: A) -> Ty { - s - //~^ ERROR: the parameter type `A` may not live long enough - } - pub fn assert_static() {} +pub type Ty = impl Sized + 'static; +#[define_opaque(Ty)] +fn defining(s: A) -> Ty { + s + //~^ ERROR: the parameter type `A` may not live long enough } -use test_type_param_static::*; +pub fn assert_static() {} fn test() where diff --git a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.stderr b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.stderr index f23b978d0b640..3cec4bbb0993e 100644 --- a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.stderr +++ b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.stderr @@ -1,27 +1,27 @@ error[E0310]: the parameter type `A` may not live long enough - --> $DIR/implied_lifetime_wf_check4_static.rs:6:9 + --> $DIR/implied_lifetime_wf_check4_static.rs:6:5 | -LL | s - | ^ - | | - | the parameter type `A` must be valid for the static lifetime... - | ...so that the type `A` will meet its required lifetime bounds... +LL | s + | ^ + | | + | the parameter type `A` must be valid for the static lifetime... + | ...so that the type `A` will meet its required lifetime bounds... | note: ...that is required by this bound - --> $DIR/implied_lifetime_wf_check4_static.rs:4:35 + --> $DIR/implied_lifetime_wf_check4_static.rs:3:31 | -LL | pub type Ty = impl Sized + 'static; - | ^^^^^^^ +LL | pub type Ty = impl Sized + 'static; + | ^^^^^^^ help: consider adding an explicit lifetime bound | -LL | pub type Ty = impl Sized + 'static; - | +++++++++ +LL | pub type Ty = impl Sized + 'static; + | +++++++++ error[E0310]: the parameter type `A` may not live long enough - --> $DIR/implied_lifetime_wf_check4_static.rs:17:5 + --> $DIR/implied_lifetime_wf_check4_static.rs:15:5 | LL | assert_static::() - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ | | | the parameter type `A` must be valid for the static lifetime... | ...so that the type `A` will meet its required lifetime bounds diff --git a/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds.rs b/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds.rs index 4a9f162823e35..126fb66b9db6a 100644 --- a/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds.rs +++ b/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds.rs @@ -2,19 +2,17 @@ #![feature(impl_trait_in_assoc_type, type_alias_impl_trait)] -mod foo { - pub trait Callable { - type Output; - fn call() -> Self::Output; - } +pub trait Callable { + type Output; + fn call() -> Self::Output; +} - pub type OutputHelper = impl Sized; - impl<'a> Callable for &'a () { - type Output = OutputHelper; - fn call() -> Self::Output {} - } +pub type OutputHelper = impl Sized; +impl<'a> Callable for &'a () { + type Output = OutputHelper; + #[define_opaque(OutputHelper)] + fn call() -> Self::Output {} } -use foo::*; fn test<'a>() -> impl Sized { <&'a () as Callable>::call() diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs index 7452000b65dc8..92c8a8f321625 100644 --- a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs +++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs @@ -6,7 +6,7 @@ trait Foo { } impl Foo for () { - type Assoc<'a> = impl Sized; //~ ERROR unconstrained opaque type + type Assoc<'a> = impl Sized; fn bar<'a: 'a>() where Self::Assoc<'a>:, diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr index 1274a8b60dea0..7ce4517fb1e9a 100644 --- a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr +++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr @@ -9,14 +9,6 @@ LL | fn bar<'a: 'a>() LL | let _: Self::Assoc<'a> = x; | ^^^^^^^^^^^^^^^ -error: unconstrained opaque type - --> $DIR/in-assoc-ty-early-bound2.rs:9:22 - | -LL | type Assoc<'a> = impl Sized; - | ^^^^^^^^^^ - | - = note: `Assoc` must be used in combination with a concrete type within the same impl - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/type-alias-impl-trait/in-where-clause.rs b/tests/ui/type-alias-impl-trait/in-where-clause.rs index 065af57a86416..8d5bfc48a66e9 100644 --- a/tests/ui/type-alias-impl-trait/in-where-clause.rs +++ b/tests/ui/type-alias-impl-trait/in-where-clause.rs @@ -5,6 +5,7 @@ type Bar = impl Sized; //~^ ERROR: cycle +#[define_opaque(Bar)] fn foo() -> Bar where Bar: Send, diff --git a/tests/ui/type-alias-impl-trait/in-where-clause.stderr b/tests/ui/type-alias-impl-trait/in-where-clause.stderr index 5ac09e20b02c2..9fcb26c20a682 100644 --- a/tests/ui/type-alias-impl-trait/in-where-clause.stderr +++ b/tests/ui/type-alias-impl-trait/in-where-clause.stderr @@ -1,12 +1,12 @@ error[E0283]: type annotations needed: cannot satisfy `Bar: Send` - --> $DIR/in-where-clause.rs:12:9 + --> $DIR/in-where-clause.rs:13:9 | LL | [0; 1 + 2] | ^^^^^ | = note: cannot satisfy `Bar: Send` note: required by a bound in `foo` - --> $DIR/in-where-clause.rs:10:10 + --> $DIR/in-where-clause.rs:11:10 | LL | fn foo() -> Bar | --- required by a bound in this function @@ -20,8 +20,43 @@ error[E0391]: cycle detected when computing type of opaque `Bar::{opaque#0}` LL | type Bar = impl Sized; | ^^^^^^^^^^ | +note: ...which requires borrow-checking `foo`... + --> $DIR/in-where-clause.rs:9:1 + | +LL | / fn foo() -> Bar +LL | | where +LL | | Bar: Send, + | |______________^ +note: ...which requires promoting constants in MIR for `foo`... + --> $DIR/in-where-clause.rs:9:1 + | +LL | / fn foo() -> Bar +LL | | where +LL | | Bar: Send, + | |______________^ +note: ...which requires checking if `foo` contains FFI-unwind calls... + --> $DIR/in-where-clause.rs:9:1 + | +LL | / fn foo() -> Bar +LL | | where +LL | | Bar: Send, + | |______________^ +note: ...which requires building MIR for `foo`... + --> $DIR/in-where-clause.rs:9:1 + | +LL | / fn foo() -> Bar +LL | | where +LL | | Bar: Send, + | |______________^ +note: ...which requires match-checking `foo`... + --> $DIR/in-where-clause.rs:9:1 + | +LL | / fn foo() -> Bar +LL | | where +LL | | Bar: Send, + | |______________^ note: ...which requires type-checking `foo`... - --> $DIR/in-where-clause.rs:8:1 + --> $DIR/in-where-clause.rs:9:1 | LL | / fn foo() -> Bar LL | | where diff --git a/tests/ui/type-alias-impl-trait/incomplete-inference.rs b/tests/ui/type-alias-impl-trait/incomplete-inference.rs index 4c8bf2cfca11e..70acb1870e223 100644 --- a/tests/ui/type-alias-impl-trait/incomplete-inference.rs +++ b/tests/ui/type-alias-impl-trait/incomplete-inference.rs @@ -2,11 +2,13 @@ type Foo = impl Sized; +#[define_opaque(Foo)] fn bar() -> Foo { None //~^ ERROR: type annotations needed [E0282] } +#[define_opaque(Foo)] fn baz() -> Foo { Some(()) } diff --git a/tests/ui/type-alias-impl-trait/incomplete-inference.stderr b/tests/ui/type-alias-impl-trait/incomplete-inference.stderr index 3976a43a89c53..0b2bac0a153f9 100644 --- a/tests/ui/type-alias-impl-trait/incomplete-inference.stderr +++ b/tests/ui/type-alias-impl-trait/incomplete-inference.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/incomplete-inference.rs:6:5 + --> $DIR/incomplete-inference.rs:7:5 | LL | None | ^^^^ cannot infer type of the type parameter `T` declared on the enum `Option` diff --git a/tests/ui/type-alias-impl-trait/inference-cycle.rs b/tests/ui/type-alias-impl-trait/inference-cycle.rs index 6e4507ed46063..951f177377a19 100644 --- a/tests/ui/type-alias-impl-trait/inference-cycle.rs +++ b/tests/ui/type-alias-impl-trait/inference-cycle.rs @@ -1,24 +1,24 @@ #![feature(type_alias_impl_trait)] #![allow(dead_code)] -mod m { - pub type Foo = impl std::fmt::Debug; +//@ check-pass - pub fn foo() -> Foo { - is_send(bar()) - } +pub type Foo = impl std::fmt::Debug; - pub fn bar() { - // Cycle: error today, but it'd be nice if it eventually worked - is_send(foo()); - //~^ ERROR: cannot check whether the hidden type of `inference_cycle[4ecc]::m::Foo::{opaque#0}` satisfies auto traits - } +#[define_opaque(Foo)] +pub fn foo() -> Foo { + is_send(bar()) +} - fn baz() -> Foo { - () - } +pub fn bar() { + is_send(foo()); +} - fn is_send(_: T) {} +#[define_opaque(Foo)] +fn baz() -> Foo { + () } +fn is_send(_: T) {} + fn main() {} diff --git a/tests/ui/type-alias-impl-trait/inference-cycle.stderr b/tests/ui/type-alias-impl-trait/inference-cycle.stderr deleted file mode 100644 index 8b809ba014d96..0000000000000 --- a/tests/ui/type-alias-impl-trait/inference-cycle.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: cannot check whether the hidden type of `inference_cycle[4ecc]::m::Foo::{opaque#0}` satisfies auto traits - --> $DIR/inference-cycle.rs:13:17 - | -LL | is_send(foo()); - | ------- ^^^^^ - | | - | required by a bound introduced by this call - | - = note: fetching the hidden types of an opaque inside of the defining scope is not supported. You can try moving the opaque type and the item that actually registers a hidden type into a new submodule -note: opaque type is declared here - --> $DIR/inference-cycle.rs:5:20 - | -LL | pub type Foo = impl std::fmt::Debug; - | ^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `is_send` - --> $DIR/inference-cycle.rs:21:19 - | -LL | fn is_send(_: T) {} - | ^^^^ required by this bound in `is_send` - -error: aborting due to 1 previous error - diff --git a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.rs b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.rs index 6609d4eb5a274..0aaee5d1764e1 100644 --- a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.rs +++ b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.rs @@ -1,8 +1,11 @@ #![feature(type_alias_impl_trait)] type T = impl Copy; -//~^ ERROR cannot resolve opaque type -static STATIC: T = None::<&'static T>; +#[define_opaque(T)] +fn foo() -> T { + //~^ ERROR cannot resolve opaque type + None::<&'static T> +} fn main() {} diff --git a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr index d820df472f95b..426a6c29fff66 100644 --- a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr +++ b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr @@ -1,8 +1,8 @@ error[E0720]: cannot resolve opaque type - --> $DIR/infinite-cycle-involving-weak.rs:3:10 + --> $DIR/infinite-cycle-involving-weak.rs:6:13 | -LL | type T = impl Copy; - | ^^^^^^^^^ cannot resolve opaque type +LL | fn foo() -> T { + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/invalid_impl_trait_in_assoc_ty.rs b/tests/ui/type-alias-impl-trait/invalid_impl_trait_in_assoc_ty.rs index 93c52126d69b0..ae9712bd891af 100644 --- a/tests/ui/type-alias-impl-trait/invalid_impl_trait_in_assoc_ty.rs +++ b/tests/ui/type-alias-impl-trait/invalid_impl_trait_in_assoc_ty.rs @@ -7,6 +7,7 @@ trait Foo { impl Foo for () { type Foo = impl std::fmt::Debug; + //~^ ERROR: unconstrained opaque type fn bar() { let x: Self::Foo = (); //~^ ERROR: mismatched types diff --git a/tests/ui/type-alias-impl-trait/invalid_impl_trait_in_assoc_ty.stderr b/tests/ui/type-alias-impl-trait/invalid_impl_trait_in_assoc_ty.stderr index 169d8e41d20d7..82bd0025d6d0b 100644 --- a/tests/ui/type-alias-impl-trait/invalid_impl_trait_in_assoc_ty.stderr +++ b/tests/ui/type-alias-impl-trait/invalid_impl_trait_in_assoc_ty.stderr @@ -1,9 +1,17 @@ +error: unconstrained opaque type + --> $DIR/invalid_impl_trait_in_assoc_ty.rs:9:16 + | +LL | type Foo = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `Foo` must be used in combination with a concrete type within the same impl + error[E0308]: mismatched types - --> $DIR/invalid_impl_trait_in_assoc_ty.rs:11:28 + --> $DIR/invalid_impl_trait_in_assoc_ty.rs:12:28 | LL | type Foo = impl std::fmt::Debug; | -------------------- the expected opaque type -LL | fn bar() { +... LL | let x: Self::Foo = (); | --------- ^^ expected opaque type, found `()` | | @@ -11,12 +19,7 @@ LL | let x: Self::Foo = (); | = note: expected opaque type `<() as Foo>::Foo` found unit type `()` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/invalid_impl_trait_in_assoc_ty.rs:10:8 - | -LL | fn bar() { - | ^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/issue-101750.rs b/tests/ui/type-alias-impl-trait/issue-101750.rs index 9367be8ca07fd..2f9304ce3bc6a 100644 --- a/tests/ui/type-alias-impl-trait/issue-101750.rs +++ b/tests/ui/type-alias-impl-trait/issue-101750.rs @@ -2,21 +2,18 @@ //@ check-pass -mod foo { - pub trait Trait {} +pub trait Trait {} - pub type TAIT = impl Trait; +pub type TAIT = impl Trait; - pub struct Concrete; - impl Trait for Concrete {} +pub struct Concrete; +impl Trait for Concrete {} - pub fn tait() -> TAIT { - Concrete - } +#[define_opaque(TAIT)] +pub fn tait() -> TAIT { + Concrete } -use foo::*; - trait OuterTrait { type Item; } diff --git a/tests/ui/type-alias-impl-trait/issue-104817.rs b/tests/ui/type-alias-impl-trait/issue-104817.rs index 4914632161450..01935407e2bb2 100644 --- a/tests/ui/type-alias-impl-trait/issue-104817.rs +++ b/tests/ui/type-alias-impl-trait/issue-104817.rs @@ -8,12 +8,13 @@ trait OpaqueTrait {} impl OpaqueTrait for T {} type OpaqueType = impl OpaqueTrait; +#[define_opaque(OpaqueType)] fn mk_opaque() -> OpaqueType { || 0 } trait AnotherTrait {} impl AnotherTrait for T {} impl AnotherTrait for OpaqueType {} -//[stock]~^ conflicting implementations of trait `AnotherTrait` +//[stock]~^ ERROR conflicting implementations of trait `AnotherTrait` fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-104817.stock.stderr b/tests/ui/type-alias-impl-trait/issue-104817.stock.stderr index df5a6c320a8a7..4f227310ee789 100644 --- a/tests/ui/type-alias-impl-trait/issue-104817.stock.stderr +++ b/tests/ui/type-alias-impl-trait/issue-104817.stock.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `AnotherTrait` - --> $DIR/issue-104817.rs:16:1 + --> $DIR/issue-104817.rs:17:1 | LL | impl AnotherTrait for T {} | -------------------------------- first implementation here diff --git a/tests/ui/type-alias-impl-trait/issue-109054.rs b/tests/ui/type-alias-impl-trait/issue-109054.rs index d3eb652159346..a8bb5ee3301fb 100644 --- a/tests/ui/type-alias-impl-trait/issue-109054.rs +++ b/tests/ui/type-alias-impl-trait/issue-109054.rs @@ -10,13 +10,15 @@ type FnType = impl Fn(&u32) -> ReturnType; impl std::ops::Deref for CallMe { type Target = FnType; + #[define_opaque(FnType)] fn deref(&self) -> &Self::Target { //~^ ERROR: item does not constrain `ReturnType + #[define_opaque(ReturnType)] fn inner(val: &u32) -> ReturnType { async move { *val * 2 } } - &inner //~ ERROR: expected generic lifetime parameter, found `'_` + &inner } } diff --git a/tests/ui/type-alias-impl-trait/issue-109054.stderr b/tests/ui/type-alias-impl-trait/issue-109054.stderr index 2a4aa63bb8ca1..5ce6f54e5f9ad 100644 --- a/tests/ui/type-alias-impl-trait/issue-109054.stderr +++ b/tests/ui/type-alias-impl-trait/issue-109054.stderr @@ -1,25 +1,15 @@ -error: item does not constrain `ReturnType::{opaque#0}`, but has it in its signature - --> $DIR/issue-109054.rs:13:8 +error: item does not constrain `ReturnType::{opaque#0}` + --> $DIR/issue-109054.rs:14:8 | LL | fn deref(&self) -> &Self::Target { | ^^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/issue-109054.rs:7:23 | LL | type ReturnType<'a> = impl std::future::Future + 'a; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/issue-109054.rs:19:9 - | -LL | type ReturnType<'a> = impl std::future::Future + 'a; - | -- this generic parameter must be used with a generic lifetime parameter -... -LL | &inner - | ^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/issue-52843-closure-constrain.rs b/tests/ui/type-alias-impl-trait/issue-52843-closure-constrain.rs deleted file mode 100644 index 50eeff0b18fd4..0000000000000 --- a/tests/ui/type-alias-impl-trait/issue-52843-closure-constrain.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Checks to ensure that we properly detect when a closure constrains an opaque type - -#![feature(type_alias_impl_trait)] - -use std::fmt::Debug; - -fn main() { - type Opaque = impl Debug; - fn _unused() -> Opaque { String::new() } - let null = || -> Opaque { 0 }; - //~^ ERROR: concrete type differs from previous defining opaque type use - println!("{:?}", null()); -} diff --git a/tests/ui/type-alias-impl-trait/issue-52843-closure-constrain.stderr b/tests/ui/type-alias-impl-trait/issue-52843-closure-constrain.stderr deleted file mode 100644 index 4570ce8e41da0..0000000000000 --- a/tests/ui/type-alias-impl-trait/issue-52843-closure-constrain.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/issue-52843-closure-constrain.rs:10:31 - | -LL | let null = || -> Opaque { 0 }; - | ^ expected `String`, got `i32` - | -note: previous use here - --> $DIR/issue-52843-closure-constrain.rs:9:30 - | -LL | fn _unused() -> Opaque { String::new() } - | ^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/type-alias-impl-trait/issue-52843.rs b/tests/ui/type-alias-impl-trait/issue-52843.rs index 159d3ccd27e01..fd28eb8381d6d 100644 --- a/tests/ui/type-alias-impl-trait/issue-52843.rs +++ b/tests/ui/type-alias-impl-trait/issue-52843.rs @@ -3,6 +3,7 @@ type Foo = impl Default; #[allow(unused)] +#[define_opaque(Foo)] fn foo(t: T) -> Foo { t //~^ ERROR: the trait bound `T: Default` is not satisfied diff --git a/tests/ui/type-alias-impl-trait/issue-52843.stderr b/tests/ui/type-alias-impl-trait/issue-52843.stderr index 6673b03525d06..ee2390d09451d 100644 --- a/tests/ui/type-alias-impl-trait/issue-52843.stderr +++ b/tests/ui/type-alias-impl-trait/issue-52843.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `T: Default` is not satisfied - --> $DIR/issue-52843.rs:7:5 + --> $DIR/issue-52843.rs:8:5 | LL | t | ^ the trait `Default` is not implemented for `T` @@ -10,7 +10,7 @@ note: required by a bound in an opaque type LL | type Foo = impl Default; | ^^^^^^^ note: this definition site has more where clauses than the opaque type - --> $DIR/issue-52843.rs:6:1 + --> $DIR/issue-52843.rs:7:1 | LL | fn foo(t: T) -> Foo { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/issue-53092-2.rs b/tests/ui/type-alias-impl-trait/issue-53092-2.rs index 2383008d042f7..1a530d27971a5 100644 --- a/tests/ui/type-alias-impl-trait/issue-53092-2.rs +++ b/tests/ui/type-alias-impl-trait/issue-53092-2.rs @@ -2,11 +2,13 @@ #![allow(dead_code)] type Bug = impl Fn(T) -> U + Copy; +//~^ ERROR cycle detected when computing type of `Bug::{opaque#0}` +#[define_opaque(Bug)] const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; -//~^ ERROR cycle detected -//~| ERROR: non-defining opaque type use +//~^ ERROR item does not constrain `Bug::{opaque#0}` +#[define_opaque(Bug)] fn make_bug>() -> Bug { |x| x.into() } diff --git a/tests/ui/type-alias-impl-trait/issue-53092-2.stderr b/tests/ui/type-alias-impl-trait/issue-53092-2.stderr index ac580866704d7..c8db9fdfc579e 100644 --- a/tests/ui/type-alias-impl-trait/issue-53092-2.stderr +++ b/tests/ui/type-alias-impl-trait/issue-53092-2.stderr @@ -1,42 +1,67 @@ -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/issue-53092-2.rs:6:18 +error[E0391]: cycle detected when computing type of `Bug::{opaque#0}` + --> $DIR/issue-53092-2.rs:4:18 | -LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; - | ^^^^^^^^^^^ argument `u8` is not a generic parameter +LL | type Bug = impl Fn(T) -> U + Copy; + | ^^^^^^^^^^^^^^^^^^^^^^ | -note: for this opaque type +note: ...which requires computing type of opaque `Bug::{opaque#0}`... --> $DIR/issue-53092-2.rs:4:18 | LL | type Bug = impl Fn(T) -> U + Copy; | ^^^^^^^^^^^^^^^^^^^^^^ - -error[E0391]: cycle detected when type-checking `CONST_BUG` - --> $DIR/issue-53092-2.rs:6:1 +note: ...which requires borrow-checking `CONST_BUG`... + --> $DIR/issue-53092-2.rs:8:1 + | +LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires promoting constants in MIR for `CONST_BUG`... + --> $DIR/issue-53092-2.rs:8:1 + | +LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const checking `CONST_BUG`... + --> $DIR/issue-53092-2.rs:8:1 + | +LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires building MIR for `CONST_BUG`... + --> $DIR/issue-53092-2.rs:8:1 | LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires match-checking `CONST_BUG`... + --> $DIR/issue-53092-2.rs:8:1 | +LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires type-checking `CONST_BUG`... + --> $DIR/issue-53092-2.rs:8:1 + | +LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which requires computing layout of `Bug`... = note: ...which requires normalizing `Bug`... -note: ...which requires computing type of `Bug::{opaque#0}`... + = note: ...which again requires computing type of `Bug::{opaque#0}`, completing the cycle +note: cycle used when checking that `Bug::{opaque#0}` is well-formed --> $DIR/issue-53092-2.rs:4:18 | LL | type Bug = impl Fn(T) -> U + Copy; | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires computing type of opaque `Bug::{opaque#0}`... + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: item does not constrain `Bug::{opaque#0}` + --> $DIR/issue-53092-2.rs:8:7 + | +LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; + | ^^^^^^^^^ + | + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/issue-53092-2.rs:4:18 | LL | type Bug = impl Fn(T) -> U + Copy; | ^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires type-checking `CONST_BUG`, completing the cycle -note: cycle used when checking that `CONST_BUG` is well-formed - --> $DIR/issue-53092-2.rs:6:1 - | -LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 2 previous errors -Some errors have detailed explanations: E0391, E0792. -For more information about an error, try `rustc --explain E0391`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/type-alias-impl-trait/issue-53092.rs b/tests/ui/type-alias-impl-trait/issue-53092.rs index 83b51227aaae9..a2e6ddc11b601 100644 --- a/tests/ui/type-alias-impl-trait/issue-53092.rs +++ b/tests/ui/type-alias-impl-trait/issue-53092.rs @@ -1,14 +1,13 @@ #![feature(type_alias_impl_trait)] #![allow(dead_code)] -mod bug { - pub type Bug = impl Fn(T) -> U + Copy; +pub type Bug = impl Fn(T) -> U + Copy; - fn make_bug>() -> Bug { - |x| x.into() //~ ERROR the trait bound `U: From` is not satisfied - } +#[define_opaque(Bug)] +fn make_bug>() -> Bug { + |x| x.into() + //~^ ERROR the trait bound `U: From` is not satisfied } -use bug::Bug; union Moo { x: Bug, diff --git a/tests/ui/type-alias-impl-trait/issue-53092.stderr b/tests/ui/type-alias-impl-trait/issue-53092.stderr index 579902aa3ab28..a8554b9a98922 100644 --- a/tests/ui/type-alias-impl-trait/issue-53092.stderr +++ b/tests/ui/type-alias-impl-trait/issue-53092.stderr @@ -1,18 +1,18 @@ error[E0277]: the trait bound `U: From` is not satisfied - --> $DIR/issue-53092.rs:8:9 + --> $DIR/issue-53092.rs:8:5 | -LL | |x| x.into() - | ^^^^^^^^^^^^ the trait `From` is not implemented for `U` +LL | |x| x.into() + | ^^^^^^^^^^^^ the trait `From` is not implemented for `U` | note: required by a bound in `make_bug` - --> $DIR/issue-53092.rs:7:23 + --> $DIR/issue-53092.rs:7:19 | -LL | fn make_bug>() -> Bug { - | ^^^^^^^ required by this bound in `make_bug` +LL | fn make_bug>() -> Bug { + | ^^^^^^^ required by this bound in `make_bug` help: consider restricting type parameter `U` with trait `From` | -LL | pub type Bug> = impl Fn(T) -> U + Copy; - | +++++++++++++++++++++++ +LL | pub type Bug> = impl Fn(T) -> U + Copy; + | +++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/issue-53096.rs b/tests/ui/type-alias-impl-trait/issue-53096.rs index 590fce84fc9f1..60e455a6a07dd 100644 --- a/tests/ui/type-alias-impl-trait/issue-53096.rs +++ b/tests/ui/type-alias-impl-trait/issue-53096.rs @@ -1,14 +1,12 @@ -#![feature(rustc_attrs)] +//@ check-pass + #![feature(type_alias_impl_trait)] -mod foo { - pub type Foo = impl Fn() -> usize; - pub const fn bar() -> Foo { - || 0usize - } +pub type Foo = impl Fn() -> usize; +#[define_opaque(Foo)] +pub const fn bar() -> Foo { + || 0usize } -use foo::*; const BAZR: Foo = bar(); -#[rustc_error] -fn main() {} //~ ERROR +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-53096.stderr b/tests/ui/type-alias-impl-trait/issue-53096.stderr deleted file mode 100644 index 0a744e7be9cbd..0000000000000 --- a/tests/ui/type-alias-impl-trait/issue-53096.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fatal error triggered by #[rustc_error] - --> $DIR/issue-53096.rs:14:1 - | -LL | fn main() {} - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/type-alias-impl-trait/issue-53598.rs b/tests/ui/type-alias-impl-trait/issue-53598.rs index e3e2787b66bb4..d8eee3218ed39 100644 --- a/tests/ui/type-alias-impl-trait/issue-53598.rs +++ b/tests/ui/type-alias-impl-trait/issue-53598.rs @@ -17,8 +17,8 @@ impl Foo for S2 { type Item = impl Debug; fn foo(_: T) -> Self::Item { + //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias S::(Default::default()) - //~^ Error type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias } } diff --git a/tests/ui/type-alias-impl-trait/issue-53598.stderr b/tests/ui/type-alias-impl-trait/issue-53598.stderr index a31aabedba542..f1dd3c69443cf 100644 --- a/tests/ui/type-alias-impl-trait/issue-53598.stderr +++ b/tests/ui/type-alias-impl-trait/issue-53598.stderr @@ -1,8 +1,8 @@ error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/issue-53598.rs:20:9 + --> $DIR/issue-53598.rs:19:31 | -LL | S::(Default::default()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn foo(_: T) -> Self::Item { + | ^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs b/tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs index 0d9126d399334..f02ccbbb93c4c 100644 --- a/tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs +++ b/tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs @@ -3,21 +3,20 @@ //@ check-pass -mod gen { - use std::ops::Coroutine; +use std::ops::Coroutine; - pub type CoroOnce = impl Coroutine; +pub type CoroOnce = impl Coroutine; - pub const fn const_coroutine(yielding: Y, returning: R) -> CoroOnce { - #[coroutine] - move || { - yield yielding; +#[define_opaque(CoroOnce)] +pub const fn const_coroutine(yielding: Y, returning: R) -> CoroOnce { + #[coroutine] + move || { + yield yielding; - return returning; - } + return returning; } } -const FOO: gen::CoroOnce = gen::const_coroutine(10, 100); +const FOO: CoroOnce = const_coroutine(10, 100); fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs b/tests/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs index e54dd01122e0a..a3c37759ac0b9 100644 --- a/tests/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs +++ b/tests/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs @@ -18,6 +18,7 @@ struct Foo<'a> { type F = impl Future; impl<'a> Foo<'a> { + #[define_opaque(F)] fn reply(&mut self) -> F { AndThen(|| ()) } diff --git a/tests/ui/type-alias-impl-trait/issue-57961.rs b/tests/ui/type-alias-impl-trait/issue-57961.rs index 61af7a0f62500..0b39e21d8b7e8 100644 --- a/tests/ui/type-alias-impl-trait/issue-57961.rs +++ b/tests/ui/type-alias-impl-trait/issue-57961.rs @@ -11,6 +11,7 @@ impl Foo for () { //~^ ERROR expected `IntoIter` to be an iterator that yields `X`, but it yields `u32` } +#[define_opaque(X)] fn incoherent() -> X { 22_i32 } diff --git a/tests/ui/type-alias-impl-trait/issue-58662-coroutine-with-lifetime.rs b/tests/ui/type-alias-impl-trait/issue-58662-coroutine-with-lifetime.rs index 899e81ed562fc..c2a9f153815e2 100644 --- a/tests/ui/type-alias-impl-trait/issue-58662-coroutine-with-lifetime.rs +++ b/tests/ui/type-alias-impl-trait/issue-58662-coroutine-with-lifetime.rs @@ -7,6 +7,7 @@ use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; type RandCoroutine<'a> = impl Coroutine + 'a; +#[define_opaque(RandCoroutine)] fn rand_coroutine<'a>(rng: &'a ()) -> RandCoroutine<'a> { #[coroutine] move || { @@ -18,6 +19,7 @@ fn rand_coroutine<'a>(rng: &'a ()) -> RandCoroutine<'a> { } pub type RandCoroutineWithIndirection<'c> = impl Coroutine + 'c; +#[define_opaque(RandCoroutineWithIndirection)] pub fn rand_coroutine_with_indirection<'a>(rng: &'a ()) -> RandCoroutineWithIndirection<'a> { fn helper<'b>(rng: &'b ()) -> impl 'b + Coroutine { #[coroutine] diff --git a/tests/ui/type-alias-impl-trait/issue-58662-simplified.rs b/tests/ui/type-alias-impl-trait/issue-58662-simplified.rs index 9d74c0687fe67..aef13947f5540 100644 --- a/tests/ui/type-alias-impl-trait/issue-58662-simplified.rs +++ b/tests/ui/type-alias-impl-trait/issue-58662-simplified.rs @@ -8,6 +8,8 @@ trait Trait {} impl Trait for T {} type Foo<'c> = impl Trait + 'c; + +#[define_opaque(Foo)] fn foo<'a>(rng: &'a ()) -> Foo<'a> { fn helper<'b>(rng: &'b ()) -> impl 'b + Trait { rng @@ -16,5 +18,4 @@ fn foo<'a>(rng: &'a ()) -> Foo<'a> { helper(rng) } -fn main() { -} +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-58951-2.rs b/tests/ui/type-alias-impl-trait/issue-58951-2.rs index fb92b1274365c..de6b9e741198b 100644 --- a/tests/ui/type-alias-impl-trait/issue-58951-2.rs +++ b/tests/ui/type-alias-impl-trait/issue-58951-2.rs @@ -2,14 +2,12 @@ #![feature(type_alias_impl_trait)] -mod defining_use_scope { - pub type A = impl Iterator; +pub type A = impl Iterator; - pub fn def_a() -> A { - 0..1 - } +#[define_opaque(A)] +pub fn def_a() -> A { + 0..1 } -use defining_use_scope::*; pub fn use_a() { def_a().map(|x| x); diff --git a/tests/ui/type-alias-impl-trait/issue-58951.rs b/tests/ui/type-alias-impl-trait/issue-58951.rs index b9f27b031c72f..de6b9e741198b 100644 --- a/tests/ui/type-alias-impl-trait/issue-58951.rs +++ b/tests/ui/type-alias-impl-trait/issue-58951.rs @@ -2,16 +2,15 @@ #![feature(type_alias_impl_trait)] -mod helper { - pub type A = impl Iterator; +pub type A = impl Iterator; - pub fn def_a() -> A { - 0..1 - } +#[define_opaque(A)] +pub fn def_a() -> A { + 0..1 } pub fn use_a() { - helper::def_a().map(|x| x); + def_a().map(|x| x); } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-60407.rs b/tests/ui/type-alias-impl-trait/issue-60407.rs index 6c7c76b5ac728..53280c28ecb1c 100644 --- a/tests/ui/type-alias-impl-trait/issue-60407.rs +++ b/tests/ui/type-alias-impl-trait/issue-60407.rs @@ -1,18 +1,16 @@ -#![feature(type_alias_impl_trait, rustc_attrs)] +//@ check-pass -mod bar { - pub type Debuggable = impl core::fmt::Debug; +#![feature(type_alias_impl_trait)] - pub fn foo() -> Debuggable { - 0u32 - } +pub type Debuggable = impl core::fmt::Debug; + +#[define_opaque(Debuggable)] +pub fn foo() -> Debuggable { + 0u32 } -use bar::*; static mut TEST: Option = None; -#[rustc_error] fn main() { - //~^ ERROR unsafe { TEST = Some(foo()) } } diff --git a/tests/ui/type-alias-impl-trait/issue-60407.stderr b/tests/ui/type-alias-impl-trait/issue-60407.stderr deleted file mode 100644 index bba9092e97756..0000000000000 --- a/tests/ui/type-alias-impl-trait/issue-60407.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fatal error triggered by #[rustc_error] - --> $DIR/issue-60407.rs:15:1 - | -LL | fn main() { - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/type-alias-impl-trait/issue-60564.rs b/tests/ui/type-alias-impl-trait/issue-60564.rs index 48bd70bcca950..ae9a4d0267037 100644 --- a/tests/ui/type-alias-impl-trait/issue-60564.rs +++ b/tests/ui/type-alias-impl-trait/issue-60564.rs @@ -16,10 +16,10 @@ where E: std::fmt::Debug, { type BitsIter = IterBitsIter; + #[define_opaque(IterBitsIter)] fn iter_bits(self, n: u8) -> Self::BitsIter { - //~^ ERROR non-defining opaque type use - (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) //~^ ERROR expected generic type parameter, found `u8` + (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) } } diff --git a/tests/ui/type-alias-impl-trait/issue-60564.stderr b/tests/ui/type-alias-impl-trait/issue-60564.stderr index d42495e934d33..bfe8d92fedd3a 100644 --- a/tests/ui/type-alias-impl-trait/issue-60564.stderr +++ b/tests/ui/type-alias-impl-trait/issue-60564.stderr @@ -1,24 +1,12 @@ -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/issue-60564.rs:19:34 - | -LL | fn iter_bits(self, n: u8) -> Self::BitsIter { - | ^^^^^^^^^^^^^^ argument `u8` is not a generic parameter - | -note: for this opaque type - --> $DIR/issue-60564.rs:8:30 - | -LL | type IterBitsIter = impl std::iter::Iterator; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0792]: expected generic type parameter, found `u8` - --> $DIR/issue-60564.rs:21:9 + --> $DIR/issue-60564.rs:20:34 | LL | type IterBitsIter = impl std::iter::Iterator; | - this generic parameter must be used with a generic type parameter ... -LL | (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn iter_bits(self, n: u8) -> Self::BitsIter { + | ^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/issue-60662.rs b/tests/ui/type-alias-impl-trait/issue-60662.rs index 35d96e52fd611..7ecdd26473555 100644 --- a/tests/ui/type-alias-impl-trait/issue-60662.rs +++ b/tests/ui/type-alias-impl-trait/issue-60662.rs @@ -1,5 +1,6 @@ //@ check-pass //@ compile-flags: -Z unpretty=hir +//@ edition: 2015 #![feature(type_alias_impl_trait)] diff --git a/tests/ui/type-alias-impl-trait/issue-60662.stdout b/tests/ui/type-alias-impl-trait/issue-60662.stdout index b541cbeb2279a..56fef852e37bf 100644 --- a/tests/ui/type-alias-impl-trait/issue-60662.stdout +++ b/tests/ui/type-alias-impl-trait/issue-60662.stdout @@ -1,5 +1,6 @@ //@ check-pass //@ compile-flags: -Z unpretty=hir +//@ edition: 2015 #![feature(type_alias_impl_trait)] #[prelude_import] diff --git a/tests/ui/type-alias-impl-trait/issue-63263-closure-return.rs b/tests/ui/type-alias-impl-trait/issue-63263-closure-return.rs index 38abc3ec7e859..6de026a26d584 100644 --- a/tests/ui/type-alias-impl-trait/issue-63263-closure-return.rs +++ b/tests/ui/type-alias-impl-trait/issue-63263-closure-return.rs @@ -8,6 +8,7 @@ pub type Closure = impl FnOnce(); +#[define_opaque(Closure)] fn bop() -> Closure { || -> Closure { || () }; panic!() diff --git a/tests/ui/type-alias-impl-trait/issue-63279.rs b/tests/ui/type-alias-impl-trait/issue-63279.rs index 02f2111468a25..10da40d2f90d2 100644 --- a/tests/ui/type-alias-impl-trait/issue-63279.rs +++ b/tests/ui/type-alias-impl-trait/issue-63279.rs @@ -2,6 +2,7 @@ type Closure = impl FnOnce(); +#[define_opaque(Closure)] fn c() -> Closure { //~^ ERROR: expected a `FnOnce()` closure, found `()` || -> Closure { || () } diff --git a/tests/ui/type-alias-impl-trait/issue-63279.stderr b/tests/ui/type-alias-impl-trait/issue-63279.stderr index 97158ee297da2..8f0e9c5fde04d 100644 --- a/tests/ui/type-alias-impl-trait/issue-63279.stderr +++ b/tests/ui/type-alias-impl-trait/issue-63279.stderr @@ -1,5 +1,5 @@ error[E0277]: expected a `FnOnce()` closure, found `()` - --> $DIR/issue-63279.rs:5:11 + --> $DIR/issue-63279.rs:6:11 | LL | fn c() -> Closure { | ^^^^^^^ expected an `FnOnce()` closure, found `()` @@ -8,7 +8,7 @@ LL | fn c() -> Closure { = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` error[E0277]: expected a `FnOnce()` closure, found `()` - --> $DIR/issue-63279.rs:7:11 + --> $DIR/issue-63279.rs:8:11 | LL | || -> Closure { || () } | ^^^^^^^ expected an `FnOnce()` closure, found `()` @@ -17,26 +17,26 @@ LL | || -> Closure { || () } = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` error[E0308]: mismatched types - --> $DIR/issue-63279.rs:7:21 + --> $DIR/issue-63279.rs:8:21 | LL | || -> Closure { || () } | ^^^^^ expected `()`, found closure | = note: expected unit type `()` - found closure `{closure@$DIR/issue-63279.rs:7:21: 7:23}` + found closure `{closure@$DIR/issue-63279.rs:8:21: 8:23}` help: use parentheses to call this closure | LL | || -> Closure { (|| ())() } | + +++ error[E0308]: mismatched types - --> $DIR/issue-63279.rs:7:5 + --> $DIR/issue-63279.rs:8:5 | LL | || -> Closure { || () } | ^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found closure | = note: expected unit type `()` - found closure `{closure@$DIR/issue-63279.rs:7:5: 7:18}` + found closure `{closure@$DIR/issue-63279.rs:8:5: 8:18}` help: use parentheses to call this closure | LL | (|| -> Closure { || () })() diff --git a/tests/ui/type-alias-impl-trait/issue-63355.rs b/tests/ui/type-alias-impl-trait/issue-63355.rs index a0d0355b5af5c..0b29bfeb98f1e 100644 --- a/tests/ui/type-alias-impl-trait/issue-63355.rs +++ b/tests/ui/type-alias-impl-trait/issue-63355.rs @@ -21,6 +21,7 @@ impl Foo for () {} impl Bar for () { type Foo = FooImpl; + #[define_opaque(FooImpl)] fn foo() -> Self::Foo { () } @@ -33,10 +34,12 @@ impl Baz for () { type Foo = FooImpl; type Bar = BarImpl; + #[define_opaque(FooImpl)] fn foo() -> Self::Foo { () } + #[define_opaque(BarImpl)] fn bar() -> Self::Bar { //~^ ERROR: item does not constrain `FooImpl::{opaque#0}` () diff --git a/tests/ui/type-alias-impl-trait/issue-63355.stderr b/tests/ui/type-alias-impl-trait/issue-63355.stderr index 6755c03805607..cb1ef0ce41deb 100644 --- a/tests/ui/type-alias-impl-trait/issue-63355.stderr +++ b/tests/ui/type-alias-impl-trait/issue-63355.stderr @@ -1,12 +1,12 @@ -error: item does not constrain `FooImpl::{opaque#0}`, but has it in its signature - --> $DIR/issue-63355.rs:40:8 +error: item does not constrain `FooImpl::{opaque#0}` + --> $DIR/issue-63355.rs:43:8 | LL | fn bar() -> Self::Bar { | ^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/issue-63355.rs:29:20 + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/issue-63355.rs:30:20 | LL | pub type FooImpl = impl Foo; | ^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/issue-63677-type-alias-coherence.rs b/tests/ui/type-alias-impl-trait/issue-63677-type-alias-coherence.rs index 51f95637969a5..5ea7396570181 100644 --- a/tests/ui/type-alias-impl-trait/issue-63677-type-alias-coherence.rs +++ b/tests/ui/type-alias-impl-trait/issue-63677-type-alias-coherence.rs @@ -16,6 +16,9 @@ impl Trait for S1 {} impl S2 {} impl T3 {} -pub fn use_t1() -> T1 { S1(()) } +#[define_opaque(T1)] +pub fn use_t1() -> T1 { + S1(()) +} fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-65384.rs b/tests/ui/type-alias-impl-trait/issue-65384.rs index 44ca5cb94b0f1..86bbc93c8bd62 100644 --- a/tests/ui/type-alias-impl-trait/issue-65384.rs +++ b/tests/ui/type-alias-impl-trait/issue-65384.rs @@ -9,6 +9,7 @@ type Bar = impl MyTrait; impl MyTrait for Bar {} //~^ ERROR: conflicting implementations of trait `MyTrait` for type `()` -fn bazr() -> Bar { } +#[define_opaque(Bar)] +fn bazr() -> Bar {} fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs b/tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs index f98ce4a426c46..6fc3aa8ce7e68 100644 --- a/tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs +++ b/tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs @@ -1,20 +1,19 @@ //@ check-pass #![feature(type_alias_impl_trait, rustc_attrs)] -mod foo { - pub type T = impl Sized; - // The concrete type referred by impl-trait-type-alias(`T`) is guaranteed - // to be the same as where it occurs, whereas `impl Trait`'s instance is location sensitive; - // so difference assertion should not be declared on impl-trait-type-alias's instances. - // for details, check RFC-2515: - // https://github.com/rust-lang/rfcs/blob/master/text/2515-type_alias_impl_trait.md - fn bop(_: T) { - super::take(|| {}); - super::take(|| {}); - } +pub type T = impl Sized; +// The concrete type referred by impl-trait-type-alias(`T`) is guaranteed +// to be the same as where it occurs, whereas `impl Trait`'s instance is location sensitive; +// so difference assertion should not be declared on impl-trait-type-alias's instances. +// for details, check RFC-2515: +// https://github.com/rust-lang/rfcs/blob/master/text/2515-type_alias_impl_trait.md + +#[define_opaque(T)] +fn bop() { + take(|| {}); + take(|| {}); } -use foo::*; fn take(_: fn() -> T) {} diff --git a/tests/ui/type-alias-impl-trait/issue-65918.rs b/tests/ui/type-alias-impl-trait/issue-65918.rs index 275b9717cef33..00dab6f2744d6 100644 --- a/tests/ui/type-alias-impl-trait/issue-65918.rs +++ b/tests/ui/type-alias-impl-trait/issue-65918.rs @@ -15,13 +15,11 @@ trait MyFrom: Sized { } /* MCVE starts here */ -mod f { - pub trait F {} - impl F for () {} - pub type DummyT = impl F; - fn _dummy_t() -> DummyT {} -} -use f::DummyT; +pub trait F {} +impl F for () {} +pub type DummyT = impl F; +#[define_opaque(DummyT)] +fn _dummy_t() -> DummyT {} struct Phantom1(PhantomData); struct Phantom2(PhantomData); diff --git a/tests/ui/type-alias-impl-trait/issue-66580-closure-coherence.rs b/tests/ui/type-alias-impl-trait/issue-66580-closure-coherence.rs index 0e3d01c2d6181..009dc31ba2658 100644 --- a/tests/ui/type-alias-impl-trait/issue-66580-closure-coherence.rs +++ b/tests/ui/type-alias-impl-trait/issue-66580-closure-coherence.rs @@ -6,6 +6,7 @@ type Closure = impl FnOnce(); +#[define_opaque(Closure)] fn closure() -> Closure { || {} } diff --git a/tests/ui/type-alias-impl-trait/issue-67844-nested-opaque.rs b/tests/ui/type-alias-impl-trait/issue-67844-nested-opaque.rs index c320b0db31bb4..2d38b3030be33 100644 --- a/tests/ui/type-alias-impl-trait/issue-67844-nested-opaque.rs +++ b/tests/ui/type-alias-impl-trait/issue-67844-nested-opaque.rs @@ -22,6 +22,7 @@ impl WithAssoc for MyStruct { type AssocType = MyParam; } +#[define_opaque(Return)] fn my_fun() -> Return { MyStruct } diff --git a/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use-2.rs b/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use-2.rs index 9dcdb578568aa..205d4832b0acf 100644 --- a/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use-2.rs +++ b/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use-2.rs @@ -6,9 +6,9 @@ trait Trait {} type Alias<'a, U> = impl Trait; +#[define_opaque(Alias)] fn f<'a>() -> Alias<'a, ()> {} -//~^ ERROR non-defining opaque type use -//~| ERROR expected generic type parameter, found `()` +//~^ ERROR expected generic type parameter, found `()` fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use-2.stderr b/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use-2.stderr index 085bffe907b4b..e58b2d4aa6098 100644 --- a/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use-2.stderr +++ b/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use-2.stderr @@ -1,24 +1,12 @@ -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/issue-68368-non-defining-use-2.rs:9:15 - | -LL | fn f<'a>() -> Alias<'a, ()> {} - | ^^^^^^^^^^^^^ argument `()` is not a generic parameter - | -note: for this opaque type - --> $DIR/issue-68368-non-defining-use-2.rs:7:21 - | -LL | type Alias<'a, U> = impl Trait; - | ^^^^^^^^^^^^^ - error[E0792]: expected generic type parameter, found `()` - --> $DIR/issue-68368-non-defining-use-2.rs:9:29 + --> $DIR/issue-68368-non-defining-use-2.rs:10:15 | LL | type Alias<'a, U> = impl Trait; | - this generic parameter must be used with a generic type parameter -LL | +... LL | fn f<'a>() -> Alias<'a, ()> {} - | ^^ + | ^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use.rs b/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use.rs index dfe2ee8204c75..f3fa4fea74650 100644 --- a/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use.rs +++ b/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use.rs @@ -6,9 +6,9 @@ trait Trait {} type Alias<'a, U> = impl Trait; +#[define_opaque(Alias)] fn f<'a>() -> Alias<'a, ()> {} //~^ ERROR expected generic type parameter, found `()` -//~| ERROR non-defining opaque type use fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use.stderr b/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use.stderr index ea704ffff97ab..3ec19e206008d 100644 --- a/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use.stderr +++ b/tests/ui/type-alias-impl-trait/issue-68368-non-defining-use.stderr @@ -1,24 +1,12 @@ -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/issue-68368-non-defining-use.rs:9:15 - | -LL | fn f<'a>() -> Alias<'a, ()> {} - | ^^^^^^^^^^^^^ argument `()` is not a generic parameter - | -note: for this opaque type - --> $DIR/issue-68368-non-defining-use.rs:7:21 - | -LL | type Alias<'a, U> = impl Trait; - | ^^^^^^^^^^^^^ - error[E0792]: expected generic type parameter, found `()` - --> $DIR/issue-68368-non-defining-use.rs:9:29 + --> $DIR/issue-68368-non-defining-use.rs:10:15 | LL | type Alias<'a, U> = impl Trait; | - this generic parameter must be used with a generic type parameter -LL | +... LL | fn f<'a>() -> Alias<'a, ()> {} - | ^^ + | ^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.rs b/tests/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.rs index a0f8e48e268c9..f9a68a1b0b31e 100644 --- a/tests/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.rs +++ b/tests/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.rs @@ -19,6 +19,7 @@ impl WithAssoc for () { type Return = impl WithAssoc; //~^ ERROR use of undeclared lifetime name `'a` +#[define_opaque(Return)] fn my_fun() -> Return {} fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.rs b/tests/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.rs index 8e631fd1b6a3d..f62260bd3f091 100644 --- a/tests/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.rs +++ b/tests/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.rs @@ -18,6 +18,7 @@ impl WithAssoc for () { type Return<'a> = impl WithAssoc; +#[define_opaque(Return)] fn my_fun<'a>() -> Return<'a> {} fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-69323.rs b/tests/ui/type-alias-impl-trait/issue-69323.rs index 18bc4cf91787f..af536186f72b1 100644 --- a/tests/ui/type-alias-impl-trait/issue-69323.rs +++ b/tests/ui/type-alias-impl-trait/issue-69323.rs @@ -9,6 +9,7 @@ fn test1>(x: A) -> Chain = Chain>; +#[define_opaque(I)] fn test2>(x: A) -> I { x.chain(once(",")) } diff --git a/tests/ui/type-alias-impl-trait/issue-70121.rs b/tests/ui/type-alias-impl-trait/issue-70121.rs index b90bd312a0ba7..5aec99a1c371f 100644 --- a/tests/ui/type-alias-impl-trait/issue-70121.rs +++ b/tests/ui/type-alias-impl-trait/issue-70121.rs @@ -2,6 +2,7 @@ pub type Successors<'a> = impl Iterator; +#[define_opaque(Successors)] pub fn f<'a>() -> Successors<'a> { None.into_iter() } diff --git a/tests/ui/type-alias-impl-trait/issue-70121.stderr b/tests/ui/type-alias-impl-trait/issue-70121.stderr index ed2eb17fbead4..8388998956ffb 100644 --- a/tests/ui/type-alias-impl-trait/issue-70121.stderr +++ b/tests/ui/type-alias-impl-trait/issue-70121.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-70121.rs:18:5 + --> $DIR/issue-70121.rs:19:5 | LL | pub type Successors<'a> = impl Iterator; | ---------------------------- the expected opaque type @@ -11,8 +11,8 @@ LL | None.into_iter() | = note: expected opaque type `Successors<'a>` found struct `std::option::IntoIter<_>` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/issue-70121.rs:17:8 +note: this item must have a `#[define_opaque(Successors)]` attribute to be able to define hidden types + --> $DIR/issue-70121.rs:18:8 | LL | pub fn kazusa<'a>() -> <&'a () as Tr>::Item { | ^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/issue-72793.rs b/tests/ui/type-alias-impl-trait/issue-72793.rs index 0353b7f3787bc..a51d7f5634aeb 100644 --- a/tests/ui/type-alias-impl-trait/issue-72793.rs +++ b/tests/ui/type-alias-impl-trait/issue-72793.rs @@ -3,24 +3,21 @@ #![feature(type_alias_impl_trait)] -mod foo { - pub trait T { - type Item; - } - - pub type Alias<'a> = impl T; +pub trait T { + type Item; +} - struct S; - impl<'a> T for &'a S { - type Item = &'a (); - } +pub type Alias<'a> = impl T; - pub fn filter_positive<'a>() -> Alias<'a> { - &S - } +struct S; +impl<'a> T for &'a S { + type Item = &'a (); } -use foo::*; +#[define_opaque(Alias)] +pub fn filter_positive<'a>() -> Alias<'a> { + &S +} fn with_positive(fun: impl Fn(Alias<'_>)) { fun(filter_positive()); diff --git a/tests/ui/type-alias-impl-trait/issue-74244.rs b/tests/ui/type-alias-impl-trait/issue-74244.rs index bb4104b3d2519..60b1bee9ff63e 100644 --- a/tests/ui/type-alias-impl-trait/issue-74244.rs +++ b/tests/ui/type-alias-impl-trait/issue-74244.rs @@ -13,6 +13,7 @@ impl Allocator for DefaultAllocator { type A = impl Fn(::Buffer); +#[define_opaque(A)] fn foo() -> A { |_| () } diff --git a/tests/ui/type-alias-impl-trait/issue-74280.rs b/tests/ui/type-alias-impl-trait/issue-74280.rs index ad641eaa00d38..c8dc1adec413e 100644 --- a/tests/ui/type-alias-impl-trait/issue-74280.rs +++ b/tests/ui/type-alias-impl-trait/issue-74280.rs @@ -4,6 +4,7 @@ type Test = impl Copy; +#[define_opaque(Test)] fn test() -> Test { let y = || -> Test { () }; 7 //~ ERROR mismatched types diff --git a/tests/ui/type-alias-impl-trait/issue-74280.stderr b/tests/ui/type-alias-impl-trait/issue-74280.stderr index c09efbe4e1333..a89a3f77b3d52 100644 --- a/tests/ui/type-alias-impl-trait/issue-74280.stderr +++ b/tests/ui/type-alias-impl-trait/issue-74280.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-74280.rs:9:5 + --> $DIR/issue-74280.rs:10:5 | LL | fn test() -> Test { | ---- expected `()` because of return type diff --git a/tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs b/tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs index 90ab4fd8d9773..3e17126834cc4 100644 --- a/tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs +++ b/tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs @@ -7,19 +7,17 @@ //@ check-pass #![feature(type_alias_impl_trait)] -mod g { - pub trait Dummy {} - impl Dummy for () {} - pub type F = impl Dummy; - pub fn f() -> F {} -} -use g::*; +pub trait Dummy {} +impl Dummy for () {} +pub type F = impl Dummy; +#[define_opaque(F)] +pub fn f() -> F {} trait Test { fn test(self); } -impl Test for define::F { +impl Test for F { fn test(self) {} } @@ -29,17 +27,14 @@ impl Test for i32 { fn test(self) {} } -mod define { - use super::*; - - pub trait Dummy {} - impl Dummy for () {} +pub trait Dummy2 {} +impl Dummy2 for () {} - pub type F = impl Dummy; - pub fn f() -> F {} -} +pub type F2 = impl Dummy2; +#[define_opaque(F2)] +pub fn f2() -> F2 {} fn main() { - let x = define::f(); + let x = f(); x.test(); } diff --git a/tests/ui/type-alias-impl-trait/issue-77179.rs b/tests/ui/type-alias-impl-trait/issue-77179.rs index 1dc74c6b5fea2..9e0c8fbbd269a 100644 --- a/tests/ui/type-alias-impl-trait/issue-77179.rs +++ b/tests/ui/type-alias-impl-trait/issue-77179.rs @@ -4,10 +4,11 @@ type Pointer = impl std::ops::Deref; +#[define_opaque(Pointer)] fn test() -> Pointer<_> { - //~^ ERROR: the placeholder `_` is not allowed within types + //~^ ERROR the placeholder `_` is not allowed within types + //~| ERROR expected generic type parameter, found `i32` Box::new(1) - //~^ ERROR: mismatched types } fn main() { @@ -16,5 +17,5 @@ fn main() { extern "Rust" { fn bar() -> Pointer<_>; - //~^ ERROR: the placeholder `_` is not allowed within types + //~^ ERROR the placeholder `_` is not allowed within types } diff --git a/tests/ui/type-alias-impl-trait/issue-77179.stderr b/tests/ui/type-alias-impl-trait/issue-77179.stderr index 16bbc996d909c..c0f197ec48c37 100644 --- a/tests/ui/type-alias-impl-trait/issue-77179.stderr +++ b/tests/ui/type-alias-impl-trait/issue-77179.stderr @@ -1,36 +1,28 @@ -error[E0308]: mismatched types - --> $DIR/issue-77179.rs:9:5 +error[E0792]: expected generic type parameter, found `i32` + --> $DIR/issue-77179.rs:8:14 | LL | type Pointer = impl std::ops::Deref; - | -------------------------------- the expected opaque type -LL | + | - this generic parameter must be used with a generic type parameter +... LL | fn test() -> Pointer<_> { - | ---------- expected `Pointer<_>` because of return type -LL | -LL | Box::new(1) - | ^^^^^^^^^^^ expected opaque type, found `Box<{integer}>` - | - = note: expected opaque type `Pointer<_>` - found struct `Box<{integer}>` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/issue-77179.rs:7:4 - | -LL | fn test() -> Pointer<_> { - | ^^^^ + | ^^^^^^^^^^ error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/issue-77179.rs:7:22 + --> $DIR/issue-77179.rs:8:22 | LL | fn test() -> Pointer<_> { - | ^ not allowed in type signatures + | --------^- + | | | + | | not allowed in type signatures + | help: replace with the correct return type: `Pointer` error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions - --> $DIR/issue-77179.rs:18:25 + --> $DIR/issue-77179.rs:19:25 | LL | fn bar() -> Pointer<_>; | ^ not allowed in type signatures error: aborting due to 3 previous errors -Some errors have detailed explanations: E0121, E0308. +Some errors have detailed explanations: E0121, E0792. For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs b/tests/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs index 2a39da1176c30..99ad48c8c323e 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs +++ b/tests/ui/type-alias-impl-trait/issue-84660-trait-impl-for-tait.rs @@ -8,6 +8,7 @@ trait Foo {} impl Foo for () {} type Bar = impl Foo; +#[define_opaque(Bar)] fn _defining_use() -> Bar {} trait TraitArg { diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.current.stderr b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.current.stderr index ec7b9e0e12b3f..577d8667a57ad 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.current.stderr +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.current.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Trait` - --> $DIR/issue-84660-unsoundness.rs:29:1 + --> $DIR/issue-84660-unsoundness.rs:31:1 | LL | impl Trait for Out { | ------------------------------------ first implementation here @@ -7,14 +7,14 @@ LL | impl Trait for Out { LL | impl Trait<(), In> for Out { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation -error: item does not constrain `Bar::{opaque#0}`, but has it in its signature - --> $DIR/issue-84660-unsoundness.rs:22:8 +error: item does not constrain `Bar::{opaque#0}` + --> $DIR/issue-84660-unsoundness.rs:24:8 | LL | fn convert(_i: In) -> Self::Out { | ^^^^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/issue-84660-unsoundness.rs:12:12 | LL | type Bar = impl Foo; diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr index e33102f687c53..9e83de5375fd7 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Trait` - --> $DIR/issue-84660-unsoundness.rs:29:1 + --> $DIR/issue-84660-unsoundness.rs:31:1 | LL | impl Trait for Out { | ------------------------------------ first implementation here @@ -8,7 +8,7 @@ LL | impl Trait<(), In> for Out { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation error[E0284]: type annotations needed: cannot satisfy `Bar == _` - --> $DIR/issue-84660-unsoundness.rs:22:37 + --> $DIR/issue-84660-unsoundness.rs:24:37 | LL | fn convert(_i: In) -> Self::Out { | _____________________________________^ diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs index f3234bafd1153..7a540d2a57495 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs @@ -10,6 +10,7 @@ trait Foo {} impl Foo for () {} type Bar = impl Foo; +#[define_opaque(Bar)] fn _defining_use() -> Bar {} trait Trait { @@ -19,9 +20,10 @@ trait Trait { impl Trait for Out { type Out = Out; + #[define_opaque(Bar)] fn convert(_i: In) -> Self::Out { - //[next]~^ ERROR: cannot satisfy `Bar == _` - //[current]~^^ ERROR: item does not constrain `Bar::{opaque#0}`, but has it in its signature + //[next]~^ ERROR: type annotations needed: cannot satisfy `Bar == _` + //[current]~^^ ERROR: item does not constrain `Bar::{opaque#0}` unreachable!(); } } diff --git a/tests/ui/type-alias-impl-trait/issue-89686.rs b/tests/ui/type-alias-impl-trait/issue-89686.rs index f734c518dd21e..5370b8844ffa8 100644 --- a/tests/ui/type-alias-impl-trait/issue-89686.rs +++ b/tests/ui/type-alias-impl-trait/issue-89686.rs @@ -11,6 +11,7 @@ trait Trait { fn f(&self) -> Self::F; + #[define_opaque(G)] fn g<'a>(&'a self) -> G<'a, Self> where Self: Sized, diff --git a/tests/ui/type-alias-impl-trait/issue-89686.stderr b/tests/ui/type-alias-impl-trait/issue-89686.stderr index 6fa7e197c40c4..7ad1442f666c2 100644 --- a/tests/ui/type-alias-impl-trait/issue-89686.stderr +++ b/tests/ui/type-alias-impl-trait/issue-89686.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `T: Trait` is not satisfied - --> $DIR/issue-89686.rs:18:9 + --> $DIR/issue-89686.rs:19:9 | LL | async move { self.f().await } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` diff --git a/tests/ui/type-alias-impl-trait/issue-93411.rs b/tests/ui/type-alias-impl-trait/issue-93411.rs index 2d08b7ba4c11a..614d2d0471b97 100644 --- a/tests/ui/type-alias-impl-trait/issue-93411.rs +++ b/tests/ui/type-alias-impl-trait/issue-93411.rs @@ -2,7 +2,7 @@ // this test used to stack overflow due to infinite recursion. //@ check-pass -//@ compile-flags: --edition=2018 +//@ edition: 2018 use std::future::Future; @@ -14,6 +14,7 @@ fn main() { } type BlahFut<'a> = impl Future + Send + 'a; +#[define_opaque(BlahFut)] fn blah<'a>(_value: &'a u8) -> BlahFut<'a> { async {} } diff --git a/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs b/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs index 7097123d608c6..7f0f6a214aae9 100644 --- a/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs +++ b/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs @@ -1,7 +1,7 @@ #![feature(type_alias_impl_trait)] //@ check-pass //@ revisions: default edition2021 -//@[edition2021] compile-flags: --edition 2021 +//@[edition2021]edition: 2021 fn main() { type T = impl Copy; @@ -44,6 +44,7 @@ fn r#struct() { mod only_pattern { type T = impl Copy; + #[define_opaque(T)] fn foo(foo: T) { let (mut x, mut y) = foo; x = 42; @@ -52,6 +53,7 @@ mod only_pattern { type U = impl Copy; + #[define_opaque(U)] fn bar(bar: Option) { match bar { Some((mut x, mut y)) => { @@ -64,6 +66,7 @@ mod only_pattern { type V = impl Copy; + #[define_opaque(V)] fn baz(baz: Option) { match baz { _ => {} diff --git a/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr index c177201431a61..5b38b83e4d934 100644 --- a/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr +++ b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr @@ -11,11 +11,6 @@ LL | let x: <() as Foo>::Assoc = 42_i32; | = note: expected opaque type `<() as Foo>::Assoc` found type `i32` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/itiat-forbid-nested-items.rs:11:12 - | -LL | fn foo() -> <() as Foo>::Assoc { - | ^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs index 1bc352041a57d..075b0bd75fa53 100644 --- a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs +++ b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs @@ -6,6 +6,7 @@ type Tait = impl FnOnce() -> (); +#[define_opaque(Tait)] fn reify_as_tait() -> Thunk { //~^ ERROR: expected a `FnOnce()` closure, found `()` Thunk::new(|cont| cont) diff --git a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr index 921667f577b4e..0bee0dfa9c7a3 100644 --- a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr +++ b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/lazy_subtyping_of_opaques.rs:11:5 + --> $DIR/lazy_subtyping_of_opaques.rs:12:5 | LL | fn reify_as_tait() -> Thunk { | ----------- expected `Thunk<_>` because of return type @@ -11,7 +11,7 @@ LL | Thunk::new(|cont| cont) found unit type `()` error[E0277]: expected a `FnOnce()` closure, found `()` - --> $DIR/lazy_subtyping_of_opaques.rs:11:23 + --> $DIR/lazy_subtyping_of_opaques.rs:12:23 | LL | Thunk::new(|cont| cont) | ^^^^ expected an `FnOnce()` closure, found `()` @@ -20,7 +20,7 @@ LL | Thunk::new(|cont| cont) = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` error[E0277]: expected a `FnOnce()` closure, found `()` - --> $DIR/lazy_subtyping_of_opaques.rs:9:23 + --> $DIR/lazy_subtyping_of_opaques.rs:10:23 | LL | fn reify_as_tait() -> Thunk { | ^^^^^^^^^^^ expected an `FnOnce()` closure, found `()` diff --git a/tests/ui/type-alias-impl-trait/lifetime-def-path-conflict-40731.rs b/tests/ui/type-alias-impl-trait/lifetime-def-path-conflict-40731.rs new file mode 100644 index 0000000000000..bfaa48585ef19 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/lifetime-def-path-conflict-40731.rs @@ -0,0 +1,16 @@ +// https://github.com/rust-lang/rust/issues/140731 +// This tests that there's no def path conflict between the +// remapped lifetime and the lifetime present in the source. + +#![feature(impl_trait_in_assoc_type)] + +trait Trait<'a> {} + +impl<'a> Trait<'a> for u32 { + type Opq2 = impl for<'a> Trait<'a>; + //~^ ERROR: unconstrained opaque type + //~| ERROR: type `Opq2` is not a member of trait `Trait` + //~| ERROR: lifetime name `'a` shadows a lifetime name that is already in scope +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/lifetime-def-path-conflict-40731.stderr b/tests/ui/type-alias-impl-trait/lifetime-def-path-conflict-40731.stderr new file mode 100644 index 0000000000000..e1544c5911b94 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/lifetime-def-path-conflict-40731.stderr @@ -0,0 +1,26 @@ +error[E0437]: type `Opq2` is not a member of trait `Trait` + --> $DIR/lifetime-def-path-conflict-40731.rs:10:5 + | +LL | type Opq2 = impl for<'a> Trait<'a>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `Trait` + +error[E0496]: lifetime name `'a` shadows a lifetime name that is already in scope + --> $DIR/lifetime-def-path-conflict-40731.rs:10:26 + | +LL | impl<'a> Trait<'a> for u32 { + | -- first declared here +LL | type Opq2 = impl for<'a> Trait<'a>; + | ^^ lifetime `'a` already in scope + +error: unconstrained opaque type + --> $DIR/lifetime-def-path-conflict-40731.rs:10:17 + | +LL | type Opq2 = impl for<'a> Trait<'a>; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Opq2` must be used in combination with a concrete type within the same impl + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0437, E0496. +For more information about an error, try `rustc --explain E0437`. diff --git a/tests/ui/type-alias-impl-trait/lifetime_mismatch.rs b/tests/ui/type-alias-impl-trait/lifetime_mismatch.rs index 45a55050c4435..b58840718a354 100644 --- a/tests/ui/type-alias-impl-trait/lifetime_mismatch.rs +++ b/tests/ui/type-alias-impl-trait/lifetime_mismatch.rs @@ -2,6 +2,7 @@ type Foo<'a> = impl Sized; +#[define_opaque(Foo)] fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> (Foo<'a>, Foo<'b>) { (x, y) //~^ ERROR opaque type used twice with different lifetimes @@ -9,6 +10,7 @@ fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> (Foo<'a>, Foo<'b>) { type Bar<'a, 'b> = impl std::fmt::Debug; +#[define_opaque(Bar)] fn bar<'x, 'y>(i: &'x i32, j: &'y i32) -> (Bar<'x, 'y>, Bar<'y, 'x>) { (i, j) //~^ ERROR opaque type used twice with different lifetimes diff --git a/tests/ui/type-alias-impl-trait/lifetime_mismatch.stderr b/tests/ui/type-alias-impl-trait/lifetime_mismatch.stderr index 4f7b0f1740765..719748edc011f 100644 --- a/tests/ui/type-alias-impl-trait/lifetime_mismatch.stderr +++ b/tests/ui/type-alias-impl-trait/lifetime_mismatch.stderr @@ -1,5 +1,5 @@ error: opaque type used twice with different lifetimes - --> $DIR/lifetime_mismatch.rs:6:5 + --> $DIR/lifetime_mismatch.rs:7:5 | LL | (x, y) | ^^^^^^ @@ -8,13 +8,13 @@ LL | (x, y) | lifetime `'b` previously used here | note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types - --> $DIR/lifetime_mismatch.rs:6:5 + --> $DIR/lifetime_mismatch.rs:7:5 | LL | (x, y) | ^^^^^^ error: opaque type used twice with different lifetimes - --> $DIR/lifetime_mismatch.rs:13:5 + --> $DIR/lifetime_mismatch.rs:15:5 | LL | (i, j) | ^^^^^^ @@ -23,7 +23,7 @@ LL | (i, j) | lifetime `'y` previously used here | note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types - --> $DIR/lifetime_mismatch.rs:13:5 + --> $DIR/lifetime_mismatch.rs:15:5 | LL | (i, j) | ^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/match-upvar-discriminant-of-opaque.rs b/tests/ui/type-alias-impl-trait/match-upvar-discriminant-of-opaque.rs new file mode 100644 index 0000000000000..f4e2cb0c037b4 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/match-upvar-discriminant-of-opaque.rs @@ -0,0 +1,12 @@ +#![feature(type_alias_impl_trait)] + +fn enum_upvar() { + type T = impl Copy; + let foo: T = Some((42, std::marker::PhantomData::)); + let x = move || match foo { + None => (), + //~^ ERROR cannot resolve opaque type + }; +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/match-upvar-discriminant-of-opaque.stderr b/tests/ui/type-alias-impl-trait/match-upvar-discriminant-of-opaque.stderr new file mode 100644 index 0000000000000..2bfce2d63a8ee --- /dev/null +++ b/tests/ui/type-alias-impl-trait/match-upvar-discriminant-of-opaque.stderr @@ -0,0 +1,9 @@ +error[E0720]: cannot resolve opaque type + --> $DIR/match-upvar-discriminant-of-opaque.rs:7:9 + | +LL | None => (), + | ^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/type-alias-impl-trait/method_resolution.current.stderr b/tests/ui/type-alias-impl-trait/method_resolution.current.stderr index a9c05ad33424d..07e7126f8a058 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution.current.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution.current.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `bar` found for struct `Bar` in the current scope - --> $DIR/method_resolution.rs:21:14 + --> $DIR/method_resolution.rs:22:14 | LL | struct Bar(T); | ------------- method `bar` not found for this struct diff --git a/tests/ui/type-alias-impl-trait/method_resolution.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution.next.stderr index 6b34358a56ea3..7462d2b478bb6 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution.next.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution.next.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `bar` found for struct `Bar` in the current scope - --> $DIR/method_resolution.rs:21:14 + --> $DIR/method_resolution.rs:22:14 | LL | struct Bar(T); | ------------- method `bar` not found for this struct diff --git a/tests/ui/type-alias-impl-trait/method_resolution.rs b/tests/ui/type-alias-impl-trait/method_resolution.rs index f636aba15c0c4..a9162d0e28bee 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution.rs +++ b/tests/ui/type-alias-impl-trait/method_resolution.rs @@ -11,6 +11,7 @@ type Foo = impl Sized; struct Bar(T); impl Bar { + #[define_opaque(Foo)] fn bar(mut self) { self.0 = 42_u32; } @@ -23,6 +24,7 @@ impl Bar { } } +#[define_opaque(Foo)] fn foo() -> Foo { 42_u32 } diff --git a/tests/ui/type-alias-impl-trait/method_resolution2.rs b/tests/ui/type-alias-impl-trait/method_resolution2.rs index f69661db79922..a4c4bbbace6b2 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution2.rs +++ b/tests/ui/type-alias-impl-trait/method_resolution2.rs @@ -12,6 +12,7 @@ type Foo = impl Sized; struct Bar(T); impl Bar { + #[define_opaque(Foo)] fn bar(self) { self.foo() } @@ -21,6 +22,7 @@ impl Bar { fn foo(self) {} } +#[define_opaque(Foo)] fn foo() -> Foo { 42_u32 } diff --git a/tests/ui/type-alias-impl-trait/method_resolution3.rs b/tests/ui/type-alias-impl-trait/method_resolution3.rs index 0e6176bfe03c7..a18dcc9a2fe3a 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution3.rs +++ b/tests/ui/type-alias-impl-trait/method_resolution3.rs @@ -27,6 +27,7 @@ impl Bar { fn foo(self) {} } +#[define_opaque(Foo)] fn foo() -> Foo { 42_u32 } diff --git a/tests/ui/type-alias-impl-trait/method_resolution4.current.stderr b/tests/ui/type-alias-impl-trait/method_resolution4.current.stderr index e4c4d121733b6..569a9f49bbe4b 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution4.current.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution4.current.stderr @@ -1,5 +1,5 @@ error[E0307]: invalid `self` parameter type: `Bar` - --> $DIR/method_resolution4.rs:27:18 + --> $DIR/method_resolution4.rs:25:18 | LL | fn foo(self: Bar) { | ^^^^^^^^ @@ -8,7 +8,7 @@ LL | fn foo(self: Bar) { = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` error[E0307]: invalid `self` parameter type: `&Bar` - --> $DIR/method_resolution4.rs:31:20 + --> $DIR/method_resolution4.rs:29:20 | LL | fn foomp(self: &Bar) { | ^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/method_resolution4.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution4.next.stderr index e4c4d121733b6..569a9f49bbe4b 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution4.next.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution4.next.stderr @@ -1,5 +1,5 @@ error[E0307]: invalid `self` parameter type: `Bar` - --> $DIR/method_resolution4.rs:27:18 + --> $DIR/method_resolution4.rs:25:18 | LL | fn foo(self: Bar) { | ^^^^^^^^ @@ -8,7 +8,7 @@ LL | fn foo(self: Bar) { = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` error[E0307]: invalid `self` parameter type: `&Bar` - --> $DIR/method_resolution4.rs:31:20 + --> $DIR/method_resolution4.rs:29:20 | LL | fn foomp(self: &Bar) { | ^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/method_resolution4.rs b/tests/ui/type-alias-impl-trait/method_resolution4.rs index f33b4e473ae86..8a1b60b0c6e3a 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution4.rs +++ b/tests/ui/type-alias-impl-trait/method_resolution4.rs @@ -7,14 +7,12 @@ #![feature(type_alias_impl_trait, arbitrary_self_types)] -mod foo { - pub type Foo = impl Copy; +pub type Foo = impl Copy; - fn foo() -> Foo { - 42_u32 - } +#[define_opaque(Foo)] +fn foo() -> Foo { + 42_u32 } -use foo::Foo; #[derive(Copy, Clone)] struct Bar(T); diff --git a/tests/ui/type-alias-impl-trait/method_resolution5.rs b/tests/ui/type-alias-impl-trait/method_resolution5.rs index 64355e4560de2..b22c44ceb138a 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution5.rs +++ b/tests/ui/type-alias-impl-trait/method_resolution5.rs @@ -12,20 +12,20 @@ type Foo = impl Sized; struct Bar(T); impl Bar { + #[define_opaque(Foo)] fn bar(mut self) { self.0 = 42_u32; } } impl Bar { - fn foo(self) - where - Foo:, - { + #[define_opaque(Foo)] + fn foo(self) { self.bar() } } +#[define_opaque(Foo)] fn foo() -> Foo { 42_u32 } diff --git a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.current.stderr b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.current.stderr index f331da1af8793..841aa12f98309 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.current.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.current.stderr @@ -1,11 +1,11 @@ -error: item does not constrain `Tait::{opaque#0}`, but has it in its signature - --> $DIR/method_resolution_trait_method_from_opaque.rs:24:8 +error: item does not constrain `Tait::{opaque#0}` + --> $DIR/method_resolution_trait_method_from_opaque.rs:26:8 | LL | fn foo(&mut self) { | ^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/method_resolution_trait_method_from_opaque.rs:17:13 | LL | type Tait = impl Iterator; diff --git a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr index 2617ce124c105..bbdd3923821ff 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/method_resolution_trait_method_from_opaque.rs:26:9 + --> $DIR/method_resolution_trait_method_from_opaque.rs:28:9 | LL | self.bar.next().unwrap(); | ^^^^^^^^ cannot infer type diff --git a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.rs b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.rs index b6adf08853f2f..93461fcbb0b2a 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.rs +++ b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.rs @@ -17,10 +17,12 @@ pub struct Foo { type Tait = impl Iterator; impl Foo { + #[define_opaque(Tait)] pub fn new() -> Foo { Foo { bar: std::iter::empty() } } + #[define_opaque(Tait)] fn foo(&mut self) { //[current]~^ ERROR: item does not constrain self.bar.next().unwrap(); diff --git a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.rs b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.rs index c178fcf5a9142..d77efa39aeb61 100644 --- a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.rs +++ b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.rs @@ -2,6 +2,7 @@ type Opaque2 = impl Sized; type Opaque<'a, T> = Opaque2; +#[define_opaque(Opaque)] fn defining<'a, T>(x: &'a i32) -> Opaque { x } //~ WARNING elided lifetime has a name //~^ ERROR: hidden type for `Opaque2` captures lifetime that does not appear in bounds diff --git a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr index e2c21f1636b0c..61eb76ffc5ae2 100644 --- a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr +++ b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr @@ -1,5 +1,5 @@ warning: elided lifetime has a name - --> $DIR/missing_lifetime_bound.rs:5:41 + --> $DIR/missing_lifetime_bound.rs:6:41 | LL | fn defining<'a, T>(x: &'a i32) -> Opaque { x } | -- ^ this elided lifetime gets resolved as `'a` @@ -9,11 +9,11 @@ LL | fn defining<'a, T>(x: &'a i32) -> Opaque { x } = note: `#[warn(elided_named_lifetimes)]` on by default error[E0700]: hidden type for `Opaque2` captures lifetime that does not appear in bounds - --> $DIR/missing_lifetime_bound.rs:5:47 + --> $DIR/missing_lifetime_bound.rs:6:47 | LL | type Opaque2 = impl Sized; | ---------- opaque type defined here -LL | type Opaque<'a, T> = Opaque2; +... LL | fn defining<'a, T>(x: &'a i32) -> Opaque { x } | -- ^ | | diff --git a/tests/ui/type-alias-impl-trait/multi-error.rs b/tests/ui/type-alias-impl-trait/multi-error.rs index cb4ad4dc63372..2f42f14d4cb49 100644 --- a/tests/ui/type-alias-impl-trait/multi-error.rs +++ b/tests/ui/type-alias-impl-trait/multi-error.rs @@ -16,8 +16,8 @@ impl Foo for () { type Baz = impl Sized; fn foo() -> (Self::Bar, Self::Baz) { //~^ ERROR non-defining opaque type use + //~| ERROR expected generic type parameter, found `u32` ((), ()) - //~^ ERROR expected generic type parameter } } diff --git a/tests/ui/type-alias-impl-trait/multi-error.stderr b/tests/ui/type-alias-impl-trait/multi-error.stderr index 761f01b32acf4..3cb267c7c2615 100644 --- a/tests/ui/type-alias-impl-trait/multi-error.stderr +++ b/tests/ui/type-alias-impl-trait/multi-error.stderr @@ -11,13 +11,13 @@ LL | type Bar = impl Sized; | ^^^^^^^^^^ error[E0792]: expected generic type parameter, found `u32` - --> $DIR/multi-error.rs:19:9 + --> $DIR/multi-error.rs:17:17 | LL | type Bar = impl Sized; | - this generic parameter must be used with a generic type parameter -... -LL | ((), ()) - | ^^^^^^^^ +LL | type Baz = impl Sized; +LL | fn foo() -> (Self::Bar, Self::Baz) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-infer.rs b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-infer.rs index b887fcf30831d..839ac47162382 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-infer.rs +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-infer.rs @@ -6,8 +6,10 @@ type Y = impl std::fmt::Debug; +#[define_opaque(Y)] fn g() -> (Y, Y) { - (42_i64, 60) //~ ERROR concrete type differs from previous defining opaque type use + //~^ ERROR concrete type differs from previous defining opaque type use + (42_i64, 60) } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-infer.stderr b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-infer.stderr index b050b08a8e2e4..e044cbe819e8c 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-infer.stderr +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-infer.stderr @@ -1,11 +1,11 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/multiple-def-uses-in-one-fn-infer.rs:10:5 + --> $DIR/multiple-def-uses-in-one-fn-infer.rs:10:17 | -LL | (42_i64, 60) - | ^^^^^^^^^^^^ - | | - | expected `i64`, got `i32` - | this expression supplies two conflicting concrete types for the same opaque type +LL | fn g() -> (Y, Y) { + | ^^^^^^^^^^^^^^^^^^ + | | + | expected `i64`, got `i32` + | this expression supplies two conflicting concrete types for the same opaque type error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.rs b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.rs index 580fb58ef8388..39e4912ae3af6 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.rs +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.rs @@ -2,6 +2,7 @@ type Foo<'a, 'b> = impl std::fmt::Debug; +#[define_opaque(Foo)] fn foo<'x, 'y>(i: &'x i32, j: &'y i32) -> (Foo<'x, 'y>, Foo<'y, 'x>) { (i, j) //~^ ERROR opaque type used twice with different lifetimes diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.stderr b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.stderr index b2b9e604a6b69..03f2b1c532a00 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.stderr +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.stderr @@ -1,5 +1,5 @@ error: opaque type used twice with different lifetimes - --> $DIR/multiple-def-uses-in-one-fn-lifetimes.rs:6:5 + --> $DIR/multiple-def-uses-in-one-fn-lifetimes.rs:7:5 | LL | (i, j) | ^^^^^^ @@ -8,7 +8,7 @@ LL | (i, j) | lifetime `'y` previously used here | note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types - --> $DIR/multiple-def-uses-in-one-fn-lifetimes.rs:6:5 + --> $DIR/multiple-def-uses-in-one-fn-lifetimes.rs:7:5 | LL | (i, j) | ^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-pass.rs b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-pass.rs index aba41a9d85235..50c97060cef56 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-pass.rs +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-pass.rs @@ -3,11 +3,13 @@ type X = impl ToString; +#[define_opaque(X)] fn f(a: A, b: B) -> (X, X) { (a.clone(), a) } type Tait<'x> = impl Sized; +#[define_opaque(Tait)] fn define<'a: 'b, 'b: 'a>(x: &'a u8, y: &'b u8) -> (Tait<'a>, Tait<'b>) { ((), ()) } diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.rs b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.rs index da845e86147b7..5e2b140ef8b44 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.rs +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.rs @@ -6,6 +6,7 @@ type X = impl Into<&'static A>; +#[define_opaque(X)] fn f(a: &'static A, b: B) -> (X, X) { //~^ ERROR the trait bound `&'static B: From<&A>` is not satisfied (a, a) diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr index b5f38074632ee..05a169882cb11 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `&'static B: From<&A>` is not satisfied - --> $DIR/multiple-def-uses-in-one-fn.rs:9:45 + --> $DIR/multiple-def-uses-in-one-fn.rs:10:45 | LL | fn f(a: &'static A, b: B) -> (X, X) { | ^^^^^^^^^^^^^^^^^^ the trait `From<&A>` is not implemented for `&'static B` diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn2.rs b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn2.rs index 14510a5294e69..aa31901fc5ef3 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn2.rs +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn2.rs @@ -6,9 +6,10 @@ type X = impl ToString; +#[define_opaque(X)] fn f(a: A, b: B) -> (X, X) { - (a.clone(), a) //~^ ERROR concrete type differs from previous defining opaque type + (a.clone(), a) } fn main() { diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn2.stderr b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn2.stderr index c7a4b2115bf23..8d79b37f0f5f3 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn2.stderr +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn2.stderr @@ -1,11 +1,11 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/multiple-def-uses-in-one-fn2.rs:10:5 + --> $DIR/multiple-def-uses-in-one-fn2.rs:10:63 | -LL | (a.clone(), a) - | ^^^^^^^^^^^^^^ - | | - | expected `A`, got `B` - | this expression supplies two conflicting concrete types for the same opaque type +LL | fn f(a: A, b: B) -> (X, X) { + | ^^^^^^^^^^^^^^^^^^ + | | + | expected `A`, got `B` + | this expression supplies two conflicting concrete types for the same opaque type error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn3.rs b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn3.rs index 11a922443e64a..11d23efec75dd 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn3.rs +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn3.rs @@ -6,10 +6,12 @@ type X = impl ToString; +#[define_opaque(X)] fn f(a: A, b: B) -> (X, X) { (a, b) } +#[define_opaque(X)] fn g(a: A, b: B) -> (X, X) { (a, b) //~^ ERROR mismatched types diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn3.stderr b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn3.stderr index c3128ea6f5e9e..42ba769797b7f 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn3.stderr +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn3.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/multiple-def-uses-in-one-fn3.rs:14:9 + --> $DIR/multiple-def-uses-in-one-fn3.rs:16:9 | LL | fn g(a: A, b: B) -> (X, X) { | - - found type parameter diff --git a/tests/ui/type-alias-impl-trait/nested-impl-trait-in-tait.stderr b/tests/ui/type-alias-impl-trait/nested-impl-trait-in-tait.stderr index ca15b134a9947..3a13ab7cb9570 100644 --- a/tests/ui/type-alias-impl-trait/nested-impl-trait-in-tait.stderr +++ b/tests/ui/type-alias-impl-trait/nested-impl-trait-in-tait.stderr @@ -31,7 +31,7 @@ error: unconstrained opaque type LL | pub type Tait = impl Iterator; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `Tait` must be used in combination with a concrete type within the same module + = note: `Tait` must be used in combination with a concrete type within the same crate error: unconstrained opaque type --> $DIR/nested-impl-trait-in-tait.rs:3:54 @@ -39,7 +39,7 @@ error: unconstrained opaque type LL | pub type Tait = impl Iterator; | ^^^^^^^^^^^^^ | - = note: `Tait` must be used in combination with a concrete type within the same module + = note: `Tait` must be used in combination with a concrete type within the same crate error: aborting due to 4 previous errors diff --git a/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr b/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr index aa0c1076117cc..54389efc9c6de 100644 --- a/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr +++ b/tests/ui/type-alias-impl-trait/nested-in-anon-const.stderr @@ -13,7 +13,7 @@ error: unconstrained opaque type LL | type B = impl Sized; | ^^^^^^^^^^ | - = note: `B` must be used in combination with a concrete type within the same item + = note: `B` must be used in combination with a concrete type within the same crate error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs b/tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs index ba705d6f85ac7..6ef6f31953fb3 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs +++ b/tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs @@ -4,11 +4,13 @@ trait Trait<'a> { type Assoc; } impl<'a> Trait<'a> for () { type Assoc = &'a str; } type WithoutLt = impl Sized; +#[define_opaque(WithoutLt)] fn without_lt() -> impl for<'a> Trait<'a, Assoc = WithoutLt> {} //~^ ERROR captures lifetime that does not appear in bounds type WithLt<'a> = impl Sized + 'a; +#[define_opaque(WithLt)] fn with_lt() -> impl for<'a> Trait<'a, Assoc = WithLt<'a>> {} //~^ ERROR expected generic lifetime parameter, found `'a` diff --git a/tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr b/tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr index f208730552d90..ad60d186dd36e 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr +++ b/tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr @@ -1,19 +1,20 @@ error[E0700]: hidden type for `WithoutLt` captures lifetime that does not appear in bounds - --> $DIR/nested-tait-hrtb.rs:7:62 + --> $DIR/nested-tait-hrtb.rs:8:62 | LL | type WithoutLt = impl Sized; | ---------- opaque type defined here +LL | #[define_opaque(WithoutLt)] LL | fn without_lt() -> impl for<'a> Trait<'a, Assoc = WithoutLt> {} | -- ^^ | | | hidden type `&'a str` captures the lifetime `'a` as defined here error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/nested-tait-hrtb.rs:12:60 + --> $DIR/nested-tait-hrtb.rs:14:60 | LL | type WithLt<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter -LL | +... LL | fn with_lt() -> impl for<'a> Trait<'a, Assoc = WithLt<'a>> {} | ^^ diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference.current.stderr b/tests/ui/type-alias-impl-trait/nested-tait-inference.current.stderr index 915432bbe675f..b19f34a67ff7d 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference.current.stderr +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference.current.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `(): Foo` is not satisfied - --> $DIR/nested-tait-inference.rs:17:13 + --> $DIR/nested-tait-inference.rs:18:13 | LL | fn foo() -> impl Foo { | ^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()` diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference.rs b/tests/ui/type-alias-impl-trait/nested-tait-inference.rs index 50d51c7faf91b..d4ad72797cfe9 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference.rs +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference.rs @@ -14,6 +14,7 @@ trait Foo {} impl Foo<()> for () {} +#[define_opaque(FooX)] fn foo() -> impl Foo { //[current]~^ ERROR: the trait bound `(): Foo` is not satisfied // FIXME(type-alias-impl-trait): We could probably make this work. diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference2.current.stderr b/tests/ui/type-alias-impl-trait/nested-tait-inference2.current.stderr index 9da3926ac7081..27372ceed9499 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference2.current.stderr +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference2.current.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `(): Foo` is not satisfied - --> $DIR/nested-tait-inference2.rs:17:13 + --> $DIR/nested-tait-inference2.rs:18:13 | LL | fn foo() -> impl Foo { | ^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()` diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr b/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr index 9647d9e376eb4..b733739e4c863 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr @@ -1,5 +1,5 @@ error[E0284]: type annotations needed: cannot satisfy `impl Foo == ()` - --> $DIR/nested-tait-inference2.rs:19:5 + --> $DIR/nested-tait-inference2.rs:20:5 | LL | () | ^^ cannot satisfy `impl Foo == ()` diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference2.rs b/tests/ui/type-alias-impl-trait/nested-tait-inference2.rs index 28d72b0cbeede..4aeecb9140c52 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference2.rs +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference2.rs @@ -14,6 +14,7 @@ trait Foo {} impl Foo<()> for () {} impl Foo for () {} +#[define_opaque(FooX)] fn foo() -> impl Foo { //[current]~^ ERROR: the trait bound `(): Foo` is not satisfied () diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference3.rs b/tests/ui/type-alias-impl-trait/nested-tait-inference3.rs index aaf2812532d36..3e7b8c80d1b24 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference3.rs +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference3.rs @@ -9,6 +9,7 @@ trait Foo {} impl Foo for () {} +#[define_opaque(FooX)] fn foo() -> impl Foo { //~^ ERROR: item does not constrain () diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference3.stderr b/tests/ui/type-alias-impl-trait/nested-tait-inference3.stderr index 969409ebc59e8..be7aec92312a1 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference3.stderr +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference3.stderr @@ -1,11 +1,11 @@ -error: item does not constrain `FooX::{opaque#0}`, but has it in its signature - --> $DIR/nested-tait-inference3.rs:12:4 +error: item does not constrain `FooX::{opaque#0}` + --> $DIR/nested-tait-inference3.rs:13:4 | LL | fn foo() -> impl Foo { | ^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/nested-tait-inference3.rs:6:13 | LL | type FooX = impl Debug; diff --git a/tests/ui/type-alias-impl-trait/nested.rs b/tests/ui/type-alias-impl-trait/nested.rs index 524703939f1dc..1a89039af8a7e 100644 --- a/tests/ui/type-alias-impl-trait/nested.rs +++ b/tests/ui/type-alias-impl-trait/nested.rs @@ -7,6 +7,7 @@ trait Trait {} impl Trait for U {} +#[define_opaque(Bar)] fn bar() -> Bar { //~^ ERROR: item does not constrain 42 diff --git a/tests/ui/type-alias-impl-trait/nested.stderr b/tests/ui/type-alias-impl-trait/nested.stderr index ca1cf6058ea95..59911f65a2346 100644 --- a/tests/ui/type-alias-impl-trait/nested.stderr +++ b/tests/ui/type-alias-impl-trait/nested.stderr @@ -1,18 +1,18 @@ -error: item does not constrain `Foo::{opaque#0}`, but has it in its signature - --> $DIR/nested.rs:10:4 +error: item does not constrain `Foo::{opaque#0}` + --> $DIR/nested.rs:11:4 | LL | fn bar() -> Bar { | ^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/nested.rs:3:12 | LL | type Foo = impl std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^ error[E0277]: `Bar` doesn't implement `Debug` - --> $DIR/nested.rs:16:22 + --> $DIR/nested.rs:17:22 | LL | println!("{:?}", bar()); | ^^^^^ `Bar` cannot be formatted using `{:?}` because it doesn't implement `Debug` diff --git a/tests/ui/type-alias-impl-trait/nested_inference_failure.rs b/tests/ui/type-alias-impl-trait/nested_inference_failure.rs index 004e79d673879..c0581b018e949 100644 --- a/tests/ui/type-alias-impl-trait/nested_inference_failure.rs +++ b/tests/ui/type-alias-impl-trait/nested_inference_failure.rs @@ -22,6 +22,7 @@ struct Foo { type ImplT = impl Debug; type FooImpl = Foo; +#[define_opaque(FooImpl)] fn bar() -> FooImpl { Foo:: { f: |_| (), _phantom: PhantomData } } diff --git a/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs b/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs index 4def8948708fc..57fa86f5eb75d 100644 --- a/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs +++ b/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs @@ -1,21 +1,21 @@ #![feature(type_alias_impl_trait)] -mod my_mod { - use std::fmt::Debug; +use std::fmt::Debug; - pub type Foo = impl Debug; - pub type Foot = impl Debug; +pub type Foo = impl Debug; +pub type Foot = impl Debug; - pub fn get_foo() -> Foo { - 5i32 - } +#[define_opaque(Foo)] +pub fn get_foo() -> Foo { + 5i32 +} - pub fn get_foot(_: Foo) -> Foot { - //~^ ERROR: item does not constrain `Foo::{opaque#0}`, but has it in its signature - get_foo() //~ ERROR opaque type's hidden type cannot be another opaque type - } +#[define_opaque(Foot, Foo)] +pub fn get_foot(_: Foo) -> Foot { + //~^ ERROR item does not constrain `Foo::{opaque#0}` + get_foo() //~ ERROR opaque type's hidden type cannot be another opaque type } fn main() { - let _: my_mod::Foot = my_mod::get_foot(my_mod::get_foo()); + let _: Foot = get_foot(get_foo()); } diff --git a/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.stderr b/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.stderr index 889cff1ba09eb..aabae31577759 100644 --- a/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.stderr +++ b/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.stderr @@ -1,32 +1,32 @@ -error: item does not constrain `Foo::{opaque#0}`, but has it in its signature - --> $DIR/nested_type_alias_impl_trait.rs:13:12 +error: item does not constrain `Foo::{opaque#0}` + --> $DIR/nested_type_alias_impl_trait.rs:14:8 | -LL | pub fn get_foot(_: Foo) -> Foot { - | ^^^^^^^^ +LL | pub fn get_foot(_: Foo) -> Foot { + | ^^^^^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/nested_type_alias_impl_trait.rs:6:20 + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/nested_type_alias_impl_trait.rs:5:16 | -LL | pub type Foo = impl Debug; - | ^^^^^^^^^^ +LL | pub type Foo = impl Debug; + | ^^^^^^^^^^ error: opaque type's hidden type cannot be another opaque type from the same scope - --> $DIR/nested_type_alias_impl_trait.rs:15:9 + --> $DIR/nested_type_alias_impl_trait.rs:16:5 | -LL | get_foo() - | ^^^^^^^^^ one of the two opaque types used here has to be outside its defining scope +LL | get_foo() + | ^^^^^^^^^ one of the two opaque types used here has to be outside its defining scope | note: opaque type whose hidden type is being assigned - --> $DIR/nested_type_alias_impl_trait.rs:7:21 + --> $DIR/nested_type_alias_impl_trait.rs:6:17 | -LL | pub type Foot = impl Debug; - | ^^^^^^^^^^ +LL | pub type Foot = impl Debug; + | ^^^^^^^^^^ note: opaque type being used as hidden type - --> $DIR/nested_type_alias_impl_trait.rs:6:20 + --> $DIR/nested_type_alias_impl_trait.rs:5:16 | -LL | pub type Foo = impl Debug; - | ^^^^^^^^^^ +LL | pub type Foo = impl Debug; + | ^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/never_reveal_concrete_type.rs b/tests/ui/type-alias-impl-trait/never_reveal_concrete_type.rs index 590107d10384a..fc47243d693f3 100644 --- a/tests/ui/type-alias-impl-trait/never_reveal_concrete_type.rs +++ b/tests/ui/type-alias-impl-trait/never_reveal_concrete_type.rs @@ -4,10 +4,12 @@ fn main() {} type NoReveal = impl std::fmt::Debug; +#[define_opaque(NoReveal)] fn define_no_reveal() -> NoReveal { "" } +#[define_opaque(NoReveal)] fn no_reveal(x: NoReveal) { let _: &'static str = x; let _ = x as &'static str; diff --git a/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs b/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs index 41238c27351d1..8e5e47194153a 100644 --- a/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs +++ b/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs @@ -3,18 +3,17 @@ #![feature(type_alias_impl_trait)] -mod foo { - pub type Foo = impl Copy; +pub type Foo = impl Copy; - // make compiler happy about using 'Foo' - pub fn bar(x: Foo) -> Foo { - //~^ ERROR: item does not constrain `Foo::{opaque#0}` - x - } +// make compiler happy about using 'Foo' +#[define_opaque(Foo)] +pub fn bar(x: Foo) -> Foo { + //~^ ERROR: item does not constrain `Foo::{opaque#0}` + x } fn main() { unsafe { - let _: foo::Foo = std::mem::transmute(0u8); + let _: Foo = std::mem::transmute(0u8); } } diff --git a/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr b/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr index eed88c5df4f46..a57793d5a7725 100644 --- a/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr +++ b/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr @@ -1,15 +1,15 @@ -error: item does not constrain `Foo::{opaque#0}`, but has it in its signature - --> $DIR/no_inferrable_concrete_type.rs:10:12 +error: item does not constrain `Foo::{opaque#0}` + --> $DIR/no_inferrable_concrete_type.rs:10:8 | -LL | pub fn bar(x: Foo) -> Foo { - | ^^^ +LL | pub fn bar(x: Foo) -> Foo { + | ^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/no_inferrable_concrete_type.rs:7:20 + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/no_inferrable_concrete_type.rs:6:16 | -LL | pub type Foo = impl Copy; - | ^^^^^^^^^ +LL | pub type Foo = impl Copy; + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/no_revealing_outside_defining_module.rs b/tests/ui/type-alias-impl-trait/no_revealing_outside_defining_module.rs index 61153b1e17141..42161dcde6606 100644 --- a/tests/ui/type-alias-impl-trait/no_revealing_outside_defining_module.rs +++ b/tests/ui/type-alias-impl-trait/no_revealing_outside_defining_module.rs @@ -2,11 +2,10 @@ fn main() {} -mod boo { - pub type Boo = impl ::std::fmt::Debug; - fn bomp() -> Boo { - "" - } +pub type Boo = impl ::std::fmt::Debug; +#[define_opaque(Boo)] +fn define() -> Boo { + "" } // We don't actually know the type here. @@ -15,10 +14,10 @@ fn bomp2() { let _: &str = bomp(); //~ ERROR mismatched types } -fn bomp() -> boo::Boo { +fn bomp() -> Boo { "" //~ ERROR mismatched types } -fn bomp_loop() -> boo::Boo { +fn bomp_loop() -> Boo { loop {} } diff --git a/tests/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr b/tests/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr index 863282a0ff94d..f4416eb009aef 100644 --- a/tests/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr +++ b/tests/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr @@ -1,8 +1,8 @@ error[E0308]: mismatched types - --> $DIR/no_revealing_outside_defining_module.rs:15:19 + --> $DIR/no_revealing_outside_defining_module.rs:14:19 | -LL | pub type Boo = impl ::std::fmt::Debug; - | ---------------------- the found opaque type +LL | pub type Boo = impl ::std::fmt::Debug; + | ---------------------- the found opaque type ... LL | let _: &str = bomp(); | ---- ^^^^^^ expected `&str`, found opaque type @@ -11,29 +11,29 @@ LL | let _: &str = bomp(); | = note: expected reference `&str` found opaque type `Boo` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/no_revealing_outside_defining_module.rs:14:4 +note: this item must have a `#[define_opaque(Boo)]` attribute to be able to define hidden types + --> $DIR/no_revealing_outside_defining_module.rs:13:4 | LL | fn bomp2() { | ^^^^^ error[E0308]: mismatched types - --> $DIR/no_revealing_outside_defining_module.rs:19:5 + --> $DIR/no_revealing_outside_defining_module.rs:18:5 | -LL | pub type Boo = impl ::std::fmt::Debug; - | ---------------------- the expected opaque type +LL | pub type Boo = impl ::std::fmt::Debug; + | ---------------------- the expected opaque type ... -LL | fn bomp() -> boo::Boo { - | -------- expected `Boo` because of return type +LL | fn bomp() -> Boo { + | --- expected `Boo` because of return type LL | "" | ^^ expected opaque type, found `&str` | = note: expected opaque type `Boo` found reference `&'static str` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/no_revealing_outside_defining_module.rs:18:4 +note: this item must have a `#[define_opaque(Boo)]` attribute to be able to define hidden types + --> $DIR/no_revealing_outside_defining_module.rs:17:4 | -LL | fn bomp() -> boo::Boo { +LL | fn bomp() -> Boo { | ^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/non-defining-method.stderr b/tests/ui/type-alias-impl-trait/non-defining-method.stderr index 49a393ca745bb..22f173b5be9fb 100644 --- a/tests/ui/type-alias-impl-trait/non-defining-method.stderr +++ b/tests/ui/type-alias-impl-trait/non-defining-method.stderr @@ -11,12 +11,12 @@ LL | type Bar = impl Sized; | ^^^^^^^^^^ error[E0792]: expected generic type parameter, found `u32` - --> $DIR/non-defining-method.rs:16:32 + --> $DIR/non-defining-method.rs:16:17 | LL | type Bar = impl Sized; | - this generic parameter must be used with a generic type parameter LL | fn foo() -> Self::Bar {} - | ^^ + | ^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/normalize-alias-type.rs b/tests/ui/type-alias-impl-trait/normalize-alias-type.rs index 9fcf42e188c78..d0fcfb438fb97 100644 --- a/tests/ui/type-alias-impl-trait/normalize-alias-type.rs +++ b/tests/ui/type-alias-impl-trait/normalize-alias-type.rs @@ -18,7 +18,7 @@ pub fn tr1() -> impl Tr { } struct Inner { - x: helper::X, + x: X, } impl Tr for Inner { fn get(&self) -> u32 { @@ -26,14 +26,9 @@ impl Tr for Inner { } } -mod helper { - pub use super::*; - pub type X = impl Tr; +pub type X = impl Tr; - pub fn tr2() -> impl Tr - where - X:, - { - Inner { x: tr1() } - } +#[define_opaque(X)] +pub fn tr2() -> impl Tr { + Inner { x: tr1() } } diff --git a/tests/ui/type-alias-impl-trait/normalize-hidden-types.current.stderr b/tests/ui/type-alias-impl-trait/normalize-hidden-types.current.stderr index a40dac06a01c3..53e312e3e64ff 100644 --- a/tests/ui/type-alias-impl-trait/normalize-hidden-types.current.stderr +++ b/tests/ui/type-alias-impl-trait/normalize-hidden-types.current.stderr @@ -1,29 +1,17 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/normalize-hidden-types.rs:26:20 + --> $DIR/normalize-hidden-types.rs:38:22 | -LL | fn define() -> Opaque { - | ^^^^^^ expected `*const (dyn FnOnce(()) + 'static)`, got `*const dyn for<'a> FnOnce(::Gat<'a>)` +LL | fn define_2() -> Opaque { dyn_hoops::(0) } + | ^^^^^^ expected `*const dyn for<'a> FnOnce(::Gat<'a>)`, got `*const dyn FnOnce(())` | note: previous use here - --> $DIR/normalize-hidden-types.rs:27:9 - | -LL | dyn_hoops::<_>(0) - | ^^^^^^^^^^^^^^^^^ - -error: concrete type differs from previous defining opaque type use - --> $DIR/normalize-hidden-types.rs:34:22 - | -LL | fn define_1() -> Opaque { dyn_hoops::<_>(0) } - | ^^^^^^ expected `*const (dyn FnOnce(()) + 'static)`, got `*const dyn for<'a> FnOnce(::Gat<'a>)` - | -note: previous use here - --> $DIR/normalize-hidden-types.rs:34:31 + --> $DIR/normalize-hidden-types.rs:36:22 | LL | fn define_1() -> Opaque { dyn_hoops::<_>(0) } - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^ error[E0308]: mismatched types - --> $DIR/normalize-hidden-types.rs:43:25 + --> $DIR/normalize-hidden-types.rs:47:25 | LL | type Opaque = impl Sized; | ---------- the expected opaque type @@ -38,18 +26,6 @@ LL | let _: Opaque = dyn_hoops::(0); = help: consider constraining the associated type `::Gat<'_>` to `()` or calling a method that returns `::Gat<'_>` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html -error: concrete type differs from previous defining opaque type use - --> $DIR/normalize-hidden-types.rs:52:25 - | -LL | let _: Opaque = dyn_hoops::<_>(0); - | ^^^^^^^^^^^^^^^^^ expected `*const (dyn FnOnce(()) + 'static)`, got `*const dyn for<'a> FnOnce(::Gat<'a>)` - | -note: previous use here - --> $DIR/normalize-hidden-types.rs:53:9 - | -LL | None - | ^^^^ - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/normalize-hidden-types.rs b/tests/ui/type-alias-impl-trait/normalize-hidden-types.rs index 4028dba82bf15..bc007169b8381 100644 --- a/tests/ui/type-alias-impl-trait/normalize-hidden-types.rs +++ b/tests/ui/type-alias-impl-trait/normalize-hidden-types.rs @@ -23,6 +23,7 @@ fn dyn_hoops(_: T) -> *const dyn FnOnce(T::Gat<'_>) { mod typeof_1 { use super::*; type Opaque = impl Sized; + #[define_opaque(Opaque)] fn define() -> Opaque { dyn_hoops::<_>(0) } @@ -31,13 +32,16 @@ mod typeof_1 { mod typeof_2 { use super::*; type Opaque = impl Sized; + #[define_opaque(Opaque)] fn define_1() -> Opaque { dyn_hoops::<_>(0) } + #[define_opaque(Opaque)] fn define_2() -> Opaque { dyn_hoops::(0) } } mod typeck { use super::*; type Opaque = impl Sized; + #[define_opaque(Opaque)] fn define() -> Option { let _: Opaque = dyn_hoops::<_>(0); let _: Opaque = dyn_hoops::(0); @@ -48,6 +52,7 @@ mod typeck { mod borrowck { use super::*; type Opaque = impl Sized; + #[define_opaque(Opaque)] fn define() -> Option { let _: Opaque = dyn_hoops::<_>(0); None diff --git a/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.rs b/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.rs index 131f8d999d878..112ee4d557156 100644 --- a/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.rs +++ b/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.rs @@ -9,6 +9,7 @@ trait Foo { struct DefinesOpaque; impl Foo for () { type Assoc = impl Sized; + //~^ ERROR: unconstrained opaque type // This test's return type is `u32`, *not* the opaque that is defined above. // Previously we were only checking that the self type of the assoc matched, diff --git a/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.stderr b/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.stderr index d4528fb76fed9..1d1b14e146358 100644 --- a/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.stderr +++ b/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.stderr @@ -1,5 +1,13 @@ +error: unconstrained opaque type + --> $DIR/not-matching-trait-refs-isnt-defining.rs:11:18 + | +LL | type Assoc = impl Sized; + | ^^^^^^^^^^ + | + = note: `Assoc` must be used in combination with a concrete type within the same impl + error[E0308]: mismatched types - --> $DIR/not-matching-trait-refs-isnt-defining.rs:17:54 + --> $DIR/not-matching-trait-refs-isnt-defining.rs:18:54 | LL | type Assoc = impl Sized; | ---------- the expected opaque type @@ -11,12 +19,7 @@ LL | let _: >::Assoc = ""; | = note: expected opaque type `<() as Foo>::Assoc` found reference `&'static str` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/not-matching-trait-refs-isnt-defining.rs:16:8 - | -LL | fn test() -> <() as Foo>::Assoc { - | ^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/not_a_defining_use.rs b/tests/ui/type-alias-impl-trait/not_a_defining_use.rs index b5ef1470629bf..cfbbf9ce487cb 100644 --- a/tests/ui/type-alias-impl-trait/not_a_defining_use.rs +++ b/tests/ui/type-alias-impl-trait/not_a_defining_use.rs @@ -6,6 +6,7 @@ fn main() {} type Two = impl Debug; +#[define_opaque(Two)] fn three(t: T) -> Two { (t, 5i8) } @@ -20,9 +21,10 @@ impl Bar for u32 { const FOO: i32 = 42; } +#[define_opaque(Two)] fn four(t: T) -> Two { - (t, ::FOO) //~^ ERROR concrete type differs + (t, ::FOO) } fn is_sync() {} diff --git a/tests/ui/type-alias-impl-trait/not_a_defining_use.stderr b/tests/ui/type-alias-impl-trait/not_a_defining_use.stderr index b59f9c49b0776..37d28b3883c27 100644 --- a/tests/ui/type-alias-impl-trait/not_a_defining_use.stderr +++ b/tests/ui/type-alias-impl-trait/not_a_defining_use.stderr @@ -1,14 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/not_a_defining_use.rs:24:5 + --> $DIR/not_a_defining_use.rs:25:36 | -LL | (t, ::FOO) - | ^^^^^^^^^^^^^^^^^^^^ 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:10:5 + --> $DIR/not_a_defining_use.rs:10:32 | -LL | (t, 5i8) - | ^^^^^^^^ +LL | fn three(t: T) -> Two { + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/not_well_formed.rs b/tests/ui/type-alias-impl-trait/not_well_formed.rs index 0cf8d41c7fd4a..f8f7f8a20e9b1 100644 --- a/tests/ui/type-alias-impl-trait/not_well_formed.rs +++ b/tests/ui/type-alias-impl-trait/not_well_formed.rs @@ -9,13 +9,14 @@ trait TraitWithAssoc { } type Foo = impl Trait; -//~^ associated type `Assoc` not found for `V` -//~| associated type `Assoc` not found for `V` +//~^ ERROR associated type `Assoc` not found for `V` +//~| ERROR associated type `Assoc` not found for `V` trait Trait {} impl Trait for () {} +#[define_opaque(Foo)] fn foo_desugared(_: T) -> Foo { () } diff --git a/tests/ui/type-alias-impl-trait/obligation_ice.rs b/tests/ui/type-alias-impl-trait/obligation_ice.rs index e3698b23be86f..134255e1eb268 100644 --- a/tests/ui/type-alias-impl-trait/obligation_ice.rs +++ b/tests/ui/type-alias-impl-trait/obligation_ice.rs @@ -8,6 +8,7 @@ trait Trait<'a, 'b: 'a> {} impl<'a, 'b: 'a, T> Trait<'a, 'b> for std::iter::Cloned {} type I<'a, 'b: 'a, A: Trait<'a, 'b>> = Chain>; +#[define_opaque(I)] fn test2<'a, 'b, A: Trait<'a, 'b> + Iterator>(x: A) -> I<'a, 'b, A> { x.chain(once("5")) } diff --git a/tests/ui/type-alias-impl-trait/outlives-bound-var.rs b/tests/ui/type-alias-impl-trait/outlives-bound-var.rs index 2c6f44d5416e4..b1711e9eab844 100644 --- a/tests/ui/type-alias-impl-trait/outlives-bound-var.rs +++ b/tests/ui/type-alias-impl-trait/outlives-bound-var.rs @@ -5,11 +5,9 @@ //@ check-pass #![feature(type_alias_impl_trait)] -mod tait { - pub type Ty<'a> = impl Sized + 'a; - fn define<'a>() -> Ty<'a> {} -} -use tait::Ty; +pub type Ty<'a> = impl Sized + 'a; +#[define_opaque(Ty)] +fn define<'a>() -> Ty<'a> {} // Ty<'^0>: 'static fn test1(_: &'static fn(Ty<'_>)) {} diff --git a/tests/ui/type-alias-impl-trait/param_mismatch.rs b/tests/ui/type-alias-impl-trait/param_mismatch.rs index c746503071376..4e4e06acfd40c 100644 --- a/tests/ui/type-alias-impl-trait/param_mismatch.rs +++ b/tests/ui/type-alias-impl-trait/param_mismatch.rs @@ -8,6 +8,7 @@ fn id(s: &str) -> &str { } type Opaque<'a> = impl Sized + 'a; // The second `Opaque<'_>` has a higher kinded lifetime, not a generic parameter +#[define_opaque(Opaque)] fn test(s: &str) -> (Opaque<'_>, impl Fn(&str) -> Opaque<'_>) { (s, id) //~^ ERROR: expected generic lifetime parameter, found `'_` diff --git a/tests/ui/type-alias-impl-trait/param_mismatch.stderr b/tests/ui/type-alias-impl-trait/param_mismatch.stderr index 09ec550d71834..07a8ce38f9808 100644 --- a/tests/ui/type-alias-impl-trait/param_mismatch.stderr +++ b/tests/ui/type-alias-impl-trait/param_mismatch.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/param_mismatch.rs:12:5 + --> $DIR/param_mismatch.rs:13:5 | LL | type Opaque<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter diff --git a/tests/ui/type-alias-impl-trait/param_mismatch2.rs b/tests/ui/type-alias-impl-trait/param_mismatch2.rs index c7d5eaa16aaec..f6a9971141105 100644 --- a/tests/ui/type-alias-impl-trait/param_mismatch2.rs +++ b/tests/ui/type-alias-impl-trait/param_mismatch2.rs @@ -9,6 +9,7 @@ fn id(s: &str) -> &str { type Opaque<'a> = impl Sized + 'a; +#[define_opaque(Opaque)] fn test(s: &str) -> (impl Fn(&str) -> Opaque<'_>, impl Fn(&str) -> Opaque<'_>) { (id, id) //~ ERROR: expected generic lifetime parameter, found `'_` } diff --git a/tests/ui/type-alias-impl-trait/param_mismatch2.stderr b/tests/ui/type-alias-impl-trait/param_mismatch2.stderr index 1ecdd7c2b54c6..f5ecade2f02e6 100644 --- a/tests/ui/type-alias-impl-trait/param_mismatch2.stderr +++ b/tests/ui/type-alias-impl-trait/param_mismatch2.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/param_mismatch2.rs:13:5 + --> $DIR/param_mismatch2.rs:14:5 | LL | type Opaque<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter diff --git a/tests/ui/type-alias-impl-trait/param_mismatch3.rs b/tests/ui/type-alias-impl-trait/param_mismatch3.rs index 03c133d5d3c2c..17b6f8bce2a81 100644 --- a/tests/ui/type-alias-impl-trait/param_mismatch3.rs +++ b/tests/ui/type-alias-impl-trait/param_mismatch3.rs @@ -9,6 +9,7 @@ fn id2<'a, 'b>(s: (&'a str, &'b str)) -> (&'a str, &'b str) { type Opaque<'a> = impl Sized + 'a; +#[define_opaque(Opaque)] fn test() -> impl for<'a, 'b> Fn((&'a str, &'b str)) -> (Opaque<'a>, Opaque<'b>) { id2 //~ ERROR expected generic lifetime parameter, found `'a` } @@ -19,6 +20,7 @@ fn id(s: &str) -> &str { type Opaque2<'a> = impl Sized + 'a; +#[define_opaque(Opaque2)] fn test2(s: &str) -> (impl Fn(&str) -> Opaque2<'_>, Opaque2<'_>) { (id, s) //~ ERROR: expected generic lifetime parameter, found `'_` } diff --git a/tests/ui/type-alias-impl-trait/param_mismatch3.stderr b/tests/ui/type-alias-impl-trait/param_mismatch3.stderr index b8805f9b7f650..7565bbfc6ff0d 100644 --- a/tests/ui/type-alias-impl-trait/param_mismatch3.stderr +++ b/tests/ui/type-alias-impl-trait/param_mismatch3.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/param_mismatch3.rs:13:5 + --> $DIR/param_mismatch3.rs:14:5 | LL | type Opaque<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter @@ -8,7 +8,7 @@ LL | id2 | ^^^ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/param_mismatch3.rs:23:5 + --> $DIR/param_mismatch3.rs:25:5 | LL | type Opaque2<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter diff --git a/tests/ui/type-alias-impl-trait/param_mismatch4.rs b/tests/ui/type-alias-impl-trait/param_mismatch4.rs index e072f3ab8e05b..bc32143d0eb1d 100644 --- a/tests/ui/type-alias-impl-trait/param_mismatch4.rs +++ b/tests/ui/type-alias-impl-trait/param_mismatch4.rs @@ -8,6 +8,7 @@ type Opq<'a> = impl Sized; // Two defining uses: Opq<'{empty}> and Opq<'a>. // This used to ICE. // issue: #122782 +#[define_opaque(Opq)] fn build<'a>() -> Opq<'a> { let _: Opq<'_> = (); //~^ ERROR expected generic lifetime parameter, found `'_` diff --git a/tests/ui/type-alias-impl-trait/param_mismatch4.stderr b/tests/ui/type-alias-impl-trait/param_mismatch4.stderr index d3fdea25a3dc4..647637ae32a7c 100644 --- a/tests/ui/type-alias-impl-trait/param_mismatch4.stderr +++ b/tests/ui/type-alias-impl-trait/param_mismatch4.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/param_mismatch4.rs:12:12 + --> $DIR/param_mismatch4.rs:13:12 | LL | type Opq<'a> = impl Sized; | -- this generic parameter must be used with a generic lifetime parameter diff --git a/tests/ui/type-alias-impl-trait/path_resolution_taint.rs b/tests/ui/type-alias-impl-trait/path_resolution_taint.rs new file mode 100644 index 0000000000000..b320e88450bd6 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/path_resolution_taint.rs @@ -0,0 +1,16 @@ +//! This test used to ICE #131298 + +#![feature(type_alias_impl_trait)] + +fn dyn_hoops() -> *const dyn Iterator { + //~^ ERROR: cannot find trait `Captures` in this scope + loop {} +} + +type Opaque = impl Sized; +#[define_opaque(Opaque)] +fn define() -> Opaque { + let _: Opaque = dyn_hoops::(); +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/path_resolution_taint.stderr b/tests/ui/type-alias-impl-trait/path_resolution_taint.stderr new file mode 100644 index 0000000000000..122c7c1ffbfab --- /dev/null +++ b/tests/ui/type-alias-impl-trait/path_resolution_taint.stderr @@ -0,0 +1,9 @@ +error[E0405]: cannot find trait `Captures` in this scope + --> $DIR/path_resolution_taint.rs:5:54 + | +LL | fn dyn_hoops() -> *const dyn Iterator { + | ^^^^^^^^ not found in this scope + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0405`. diff --git a/tests/ui/type-alias-impl-trait/privacy.rs b/tests/ui/type-alias-impl-trait/privacy.rs index a5386bbec0d23..4fbb9c7313f80 100644 --- a/tests/ui/type-alias-impl-trait/privacy.rs +++ b/tests/ui/type-alias-impl-trait/privacy.rs @@ -3,6 +3,7 @@ #![feature(type_alias_impl_trait)] type Foo = (impl Sized, u8); +#[define_opaque(Foo)] pub fn foo() -> Foo { //~^ WARNING type alias `Foo` is more private than the item `foo` (42, 42) diff --git a/tests/ui/type-alias-impl-trait/privacy.stderr b/tests/ui/type-alias-impl-trait/privacy.stderr index 50870905c30dd..eeb49b1a4ac92 100644 --- a/tests/ui/type-alias-impl-trait/privacy.stderr +++ b/tests/ui/type-alias-impl-trait/privacy.stderr @@ -1,5 +1,5 @@ warning: type alias `Foo` is more private than the item `foo` - --> $DIR/privacy.rs:6:1 + --> $DIR/privacy.rs:7:1 | LL | pub fn foo() -> Foo { | ^^^^^^^^^^^^^^^^^^^ function `foo` is reachable at visibility `pub` diff --git a/tests/ui/type-alias-impl-trait/recursive-fn-tait.rs b/tests/ui/type-alias-impl-trait/recursive-fn-tait.rs index 3d1759097d6b3..fe354cc65452b 100644 --- a/tests/ui/type-alias-impl-trait/recursive-fn-tait.rs +++ b/tests/ui/type-alias-impl-trait/recursive-fn-tait.rs @@ -3,15 +3,18 @@ pub type Diff = impl Fn(usize) -> usize; +#[define_opaque(Diff)] pub fn lift() -> Diff { |_: usize |loop {} } +#[define_opaque(Diff)] pub fn add( n: Diff, m: Diff, ) -> Diff { - move |x: usize| m(n(x)) //~ ERROR: concrete type differs + //~^ ERROR cannot resolve opaque type + move |x: usize| m(n(x)) } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr b/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr index e8925b9b489fa..847f1cc6c2eb6 100644 --- a/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr +++ b/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr @@ -1,14 +1,9 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/recursive-fn-tait.rs:14:5 +error[E0720]: cannot resolve opaque type + --> $DIR/recursive-fn-tait.rs:15:6 | -LL | move |x: usize| m(n(x)) - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `{closure@$DIR/recursive-fn-tait.rs:7:5: 7:16}`, got `{closure@$DIR/recursive-fn-tait.rs:14:5: 14:20}` - | -note: previous use here - --> $DIR/recursive-fn-tait.rs:7:5 - | -LL | |_: usize |loop {} - | ^^^^^^^^^^^^^^^^^^ +LL | ) -> Diff { + | ^^^^ error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.rs b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.rs index 10588398c9d7f..7803b3b78db83 100644 --- a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.rs +++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.rs @@ -3,14 +3,18 @@ #![feature(type_alias_impl_trait)] type Op = impl std::fmt::Display; -fn foo() -> Op { &"hello world" } +#[define_opaque(Op)] +fn foo() -> Op { + &"hello world" +} fn transform() -> impl std::fmt::Display { &0usize } +#[define_opaque(Op)] fn bad() -> Op { + //~^ ERROR cannot resolve opaque type transform::() - //~^ ERROR concrete type differs from previous defining opaque type use } fn main() { diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr index eec35548c5504..837df7f5d4340 100644 --- a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr +++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn-2.stderr @@ -1,14 +1,9 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/recursive-tait-conflicting-defn-2.rs:12:5 +error[E0720]: cannot resolve opaque type + --> $DIR/recursive-tait-conflicting-defn-2.rs:15:13 | -LL | transform::() - | ^^^^^^^^^^^^^^^^^ expected `&'static &'static str`, got `impl std::fmt::Display` - | -note: previous use here - --> $DIR/recursive-tait-conflicting-defn-2.rs:6:18 - | -LL | fn foo() -> Op { &"hello world" } - | ^^^^^^^^^^^^^^ +LL | fn bad() -> Op { + | ^^ error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs index 38fb493b49893..d66ceda523498 100644 --- a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs +++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs @@ -14,24 +14,22 @@ struct B { impl Test for B {} -mod helper { - use super::*; - pub type TestImpl = impl Test; +pub type TestImpl = impl Test; - pub fn test() -> TestImpl { - A - } - - fn make_option2() -> Option { - let inner = make_option().unwrap(); +#[define_opaque(TestImpl)] +pub fn test() -> TestImpl { + A +} - Some(B { inner }) - //~^ ERROR concrete type differs from previous defining opaque type use - } +#[define_opaque(TestImpl)] +fn make_option2() -> Option { + //~^ ERROR cannot resolve opaque type + let inner = make_option().unwrap(); + Some(B { inner }) } -fn make_option() -> Option { - Some(helper::test()) +fn make_option() -> Option { + Some(test()) } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr index 252c5d7dfa760..c7a3381d61588 100644 --- a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr +++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr @@ -1,14 +1,9 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/recursive-tait-conflicting-defn.rs:28:9 +error[E0720]: cannot resolve opaque type + --> $DIR/recursive-tait-conflicting-defn.rs:25:22 | -LL | Some(B { inner }) - | ^^^^^^^^^^^^^^^^^ expected `A`, got `B` - | -note: previous use here - --> $DIR/recursive-tait-conflicting-defn.rs:22:9 - | -LL | A - | ^ +LL | fn make_option2() -> Option { + | ^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/type-alias-impl-trait/reveal_local.rs b/tests/ui/type-alias-impl-trait/reveal_local.rs index 34f3788e2341b..1e1e118937b2b 100644 --- a/tests/ui/type-alias-impl-trait/reveal_local.rs +++ b/tests/ui/type-alias-impl-trait/reveal_local.rs @@ -7,12 +7,13 @@ type Foo = impl Debug; fn is_send() {} fn not_good() { - // Error: this function does not constrain `Foo` to any particular - // hidden type, so it cannot rely on `Send` being true. + // This function does not define `Foo`, + // so it can actually check auto traits on the hidden type without + // risking cycle errors. is_send::(); - //~^ ERROR: cannot check whether the hidden type of `reveal_local[9507]::Foo::{opaque#0}` satisfies auto traits } +#[define_opaque(Foo)] fn not_gooder() -> Foo { // Constrain `Foo = u32` let x: Foo = 22_u32; diff --git a/tests/ui/type-alias-impl-trait/reveal_local.stderr b/tests/ui/type-alias-impl-trait/reveal_local.stderr index 9829c58cf73b7..bd0828885910b 100644 --- a/tests/ui/type-alias-impl-trait/reveal_local.stderr +++ b/tests/ui/type-alias-impl-trait/reveal_local.stderr @@ -1,23 +1,5 @@ -error: cannot check whether the hidden type of `reveal_local[9507]::Foo::{opaque#0}` satisfies auto traits - --> $DIR/reveal_local.rs:12:15 - | -LL | is_send::(); - | ^^^ - | - = note: fetching the hidden types of an opaque inside of the defining scope is not supported. You can try moving the opaque type and the item that actually registers a hidden type into a new submodule -note: opaque type is declared here - --> $DIR/reveal_local.rs:5:12 - | -LL | type Foo = impl Debug; - | ^^^^^^^^^^ -note: required by a bound in `is_send` - --> $DIR/reveal_local.rs:7:15 - | -LL | fn is_send() {} - | ^^^^ required by this bound in `is_send` - error[E0283]: type annotations needed: cannot satisfy `Foo: Send` - --> $DIR/reveal_local.rs:22:15 + --> $DIR/reveal_local.rs:23:15 | LL | is_send::(); | ^^^ @@ -29,6 +11,6 @@ note: required by a bound in `is_send` LL | fn is_send() {} | ^^^^ required by this bound in `is_send` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs index 37d84feee4b86..242a4b3cd3eed 100644 --- a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs +++ b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs @@ -12,19 +12,16 @@ #![feature(type_alias_impl_trait)] -mod helper { - pub type Opaque = impl Sized; +pub type Opaque = impl Sized; - pub fn get_rpit() -> impl Clone {} +pub fn get_rpit() -> impl Clone {} - fn test() -> Opaque { - super::query(get_rpit); - get_rpit() - } +#[define_opaque(Opaque)] +fn test() -> Opaque { + query(get_rpit); + get_rpit() } -use helper::*; - fn query(_: impl FnOnce() -> Opaque) {} fn main() {} diff --git a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query_2.rs b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query_2.rs index 80f1b1502d3b5..dc489c0d6fb35 100644 --- a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query_2.rs +++ b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query_2.rs @@ -5,16 +5,14 @@ #![feature(type_alias_impl_trait)] -mod helper { - pub type Opaque = impl Sized; +pub type Opaque = impl Sized; - pub fn get_rpit() -> impl Sized {} +pub fn get_rpit() -> impl Sized {} - fn test(_: Opaque) { - super::query(get_rpit); - } +#[define_opaque(Opaque)] +fn test() { + query(get_rpit); } -use helper::*; fn query(_: impl FnOnce() -> Opaque) {} diff --git a/tests/ui/type-alias-impl-trait/self-referential-2.current.stderr b/tests/ui/type-alias-impl-trait/self-referential-2.current.stderr index e4399f2d8f4d3..dca3ae05bb0ec 100644 --- a/tests/ui/type-alias-impl-trait/self-referential-2.current.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential-2.current.stderr @@ -1,5 +1,5 @@ error[E0277]: can't compare `i32` with `Foo` - --> $DIR/self-referential-2.rs:10:13 + --> $DIR/self-referential-2.rs:11:13 | LL | fn bar() -> Bar { | ^^^ no implementation for `i32 == Foo` diff --git a/tests/ui/type-alias-impl-trait/self-referential-2.rs b/tests/ui/type-alias-impl-trait/self-referential-2.rs index f96364ccfcddf..d3effe817b846 100644 --- a/tests/ui/type-alias-impl-trait/self-referential-2.rs +++ b/tests/ui/type-alias-impl-trait/self-referential-2.rs @@ -7,6 +7,7 @@ type Foo = impl std::fmt::Debug; type Bar = impl PartialEq; +#[define_opaque(Bar)] fn bar() -> Bar { 42_i32 //[current]~^ ERROR can't compare `i32` with `Foo` } diff --git a/tests/ui/type-alias-impl-trait/self-referential-3.rs b/tests/ui/type-alias-impl-trait/self-referential-3.rs index 18f09b548675a..b55625328935e 100644 --- a/tests/ui/type-alias-impl-trait/self-referential-3.rs +++ b/tests/ui/type-alias-impl-trait/self-referential-3.rs @@ -2,6 +2,7 @@ type Bar<'a, 'b> = impl PartialEq> + std::fmt::Debug; +#[define_opaque(Bar)] fn bar<'a, 'b>(i: &'a i32) -> Bar<'a, 'b> { //~^ ERROR can't compare `&i32` with `Bar<'a, 'b>` i diff --git a/tests/ui/type-alias-impl-trait/self-referential-3.stderr b/tests/ui/type-alias-impl-trait/self-referential-3.stderr index 15ebcdafca6fb..d0b1d46c29e07 100644 --- a/tests/ui/type-alias-impl-trait/self-referential-3.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential-3.stderr @@ -1,5 +1,5 @@ error[E0277]: can't compare `&i32` with `Bar<'a, 'b>` - --> $DIR/self-referential-3.rs:5:31 + --> $DIR/self-referential-3.rs:6:31 | LL | fn bar<'a, 'b>(i: &'a i32) -> Bar<'a, 'b> { | ^^^^^^^^^^^ no implementation for `&i32 == Bar<'a, 'b>` diff --git a/tests/ui/type-alias-impl-trait/self-referential-4.rs b/tests/ui/type-alias-impl-trait/self-referential-4.rs index 36742c8ad57fc..36df087dfcb62 100644 --- a/tests/ui/type-alias-impl-trait/self-referential-4.rs +++ b/tests/ui/type-alias-impl-trait/self-referential-4.rs @@ -2,18 +2,21 @@ type Bar<'a, 'b> = impl PartialEq> + std::fmt::Debug; +#[define_opaque(Bar)] fn bar<'a, 'b>(i: &'a i32) -> Bar<'a, 'b> { i //~^ ERROR can't compare `&i32` with `Bar<'b, 'static>` } type Foo<'a, 'b> = impl PartialEq> + std::fmt::Debug; +#[define_opaque(Foo)] fn foo<'a, 'b>(i: &'a i32) -> Foo<'a, 'b> { i //~^ ERROR can't compare `&i32` with `Foo<'static, 'b>` } type Moo<'a, 'b> = impl PartialEq> + std::fmt::Debug; +#[define_opaque(Moo)] fn moo<'a, 'b>(i: &'a i32) -> Moo<'a, 'b> { i //~^ ERROR can't compare `&i32` with `Moo<'static, 'a>` } diff --git a/tests/ui/type-alias-impl-trait/self-referential-4.stderr b/tests/ui/type-alias-impl-trait/self-referential-4.stderr index 98c762e3d3820..92534981eb94f 100644 --- a/tests/ui/type-alias-impl-trait/self-referential-4.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential-4.stderr @@ -1,5 +1,5 @@ error[E0277]: can't compare `&i32` with `Bar<'b, 'static>` - --> $DIR/self-referential-4.rs:5:31 + --> $DIR/self-referential-4.rs:6:31 | LL | fn bar<'a, 'b>(i: &'a i32) -> Bar<'a, 'b> { | ^^^^^^^^^^^ no implementation for `&i32 == Bar<'b, 'static>` @@ -10,7 +10,7 @@ LL | i = help: the trait `PartialEq` is implemented for `i32` error[E0277]: can't compare `&i32` with `Foo<'static, 'b>` - --> $DIR/self-referential-4.rs:11:31 + --> $DIR/self-referential-4.rs:13:31 | LL | fn foo<'a, 'b>(i: &'a i32) -> Foo<'a, 'b> { | ^^^^^^^^^^^ no implementation for `&i32 == Foo<'static, 'b>` @@ -21,7 +21,7 @@ LL | i = help: the trait `PartialEq` is implemented for `i32` error[E0277]: can't compare `&i32` with `Moo<'static, 'a>` - --> $DIR/self-referential-4.rs:17:31 + --> $DIR/self-referential-4.rs:20:31 | LL | fn moo<'a, 'b>(i: &'a i32) -> Moo<'a, 'b> { | ^^^^^^^^^^^ no implementation for `&i32 == Moo<'static, 'a>` diff --git a/tests/ui/type-alias-impl-trait/self-referential.rs b/tests/ui/type-alias-impl-trait/self-referential.rs index b899b12cc4a1e..a1d3b275b19ef 100644 --- a/tests/ui/type-alias-impl-trait/self-referential.rs +++ b/tests/ui/type-alias-impl-trait/self-referential.rs @@ -2,6 +2,7 @@ type Bar<'a, 'b> = impl PartialEq> + std::fmt::Debug; +#[define_opaque(Bar)] fn bar<'a, 'b>(i: &'a i32) -> Bar<'a, 'b> { //~^ ERROR can't compare `&i32` with `Bar<'b, 'a>` i @@ -9,6 +10,7 @@ fn bar<'a, 'b>(i: &'a i32) -> Bar<'a, 'b> { type Foo<'a, 'b> = (i32, impl PartialEq> + std::fmt::Debug); +#[define_opaque(Foo)] fn foo<'a, 'b>(i: &'a i32) -> Foo<'a, 'b> { //~^ ERROR can't compare `&i32` with `(i32, Foo<'a, 'b>::{opaque#0}<'a, 'b>)` (42, i) @@ -16,6 +18,7 @@ fn foo<'a, 'b>(i: &'a i32) -> Foo<'a, 'b> { type Moo<'a, 'b> = (i32, impl PartialEq> + std::fmt::Debug); +#[define_opaque(Moo)] fn moo<'a, 'b>(i: &'a i32) -> Moo<'a, 'b> { //~^ ERROR can't compare `&i32` with `(i32, Moo<'b, 'a>::{opaque#0}<'b, 'a>)` (42, i) diff --git a/tests/ui/type-alias-impl-trait/self-referential.stderr b/tests/ui/type-alias-impl-trait/self-referential.stderr index 57d67f6937610..4bcf659e8e61d 100644 --- a/tests/ui/type-alias-impl-trait/self-referential.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential.stderr @@ -1,5 +1,5 @@ error[E0277]: can't compare `&i32` with `Bar<'b, 'a>` - --> $DIR/self-referential.rs:5:31 + --> $DIR/self-referential.rs:6:31 | LL | fn bar<'a, 'b>(i: &'a i32) -> Bar<'a, 'b> { | ^^^^^^^^^^^ no implementation for `&i32 == Bar<'b, 'a>` @@ -11,7 +11,7 @@ LL | i = help: the trait `PartialEq` is implemented for `i32` error[E0277]: can't compare `&i32` with `(i32, Foo<'a, 'b>::{opaque#0}<'a, 'b>)` - --> $DIR/self-referential.rs:12:31 + --> $DIR/self-referential.rs:14:31 | LL | fn foo<'a, 'b>(i: &'a i32) -> Foo<'a, 'b> { | ^^^^^^^^^^^ no implementation for `&i32 == (i32, Foo<'a, 'b>::{opaque#0}<'a, 'b>)` @@ -23,7 +23,7 @@ LL | (42, i) = help: the trait `PartialEq` is implemented for `i32` error[E0277]: can't compare `&i32` with `(i32, Moo<'b, 'a>::{opaque#0}<'b, 'a>)` - --> $DIR/self-referential.rs:19:31 + --> $DIR/self-referential.rs:22:31 | LL | fn moo<'a, 'b>(i: &'a i32) -> Moo<'a, 'b> { | ^^^^^^^^^^^ no implementation for `&i32 == (i32, Moo<'b, 'a>::{opaque#0}<'b, 'a>)` diff --git a/tests/ui/type-alias-impl-trait/self_implication.rs b/tests/ui/type-alias-impl-trait/self_implication.rs index 56464af96ed8d..d7f701595714d 100644 --- a/tests/ui/type-alias-impl-trait/self_implication.rs +++ b/tests/ui/type-alias-impl-trait/self_implication.rs @@ -20,11 +20,10 @@ struct Foo<'a> { x: &'a mut u8, } // desugared -mod foo { - pub type FooX = impl Sized; - impl<'a> super::Foo<'a> { - pub fn foo(&self) -> FooX {} - } +pub type FooX = impl Sized; +impl<'a> Foo<'a> { + #[define_opaque(FooX)] + pub fn foo(&self) -> FooX {} } fn bar() { diff --git a/tests/ui/type-alias-impl-trait/static-const-types.rs b/tests/ui/type-alias-impl-trait/static-const-types.rs deleted file mode 100644 index dad515aaa7bd9..0000000000000 --- a/tests/ui/type-alias-impl-trait/static-const-types.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![feature(type_alias_impl_trait)] -#![allow(dead_code)] - -//@ check-pass - -use std::fmt::Debug; - -type Foo = impl Debug; - -static FOO1: Foo = 22_u32; -const FOO2: Foo = 22_u32; - -fn main() {} diff --git a/tests/ui/type-alias-impl-trait/structural-match-no-leak.rs b/tests/ui/type-alias-impl-trait/structural-match-no-leak.rs index 27f5799c38043..37cb2322175e9 100644 --- a/tests/ui/type-alias-impl-trait/structural-match-no-leak.rs +++ b/tests/ui/type-alias-impl-trait/structural-match-no-leak.rs @@ -1,20 +1,20 @@ #![feature(type_alias_impl_trait)] -mod bar { - pub type Bar = impl Send; +pub type Bar = impl Send; - // While i32 is structural-match, we do not want to leak this information. - // (See https://github.com/rust-lang/rust/issues/72156) - pub const fn leak_free() -> Bar { - 7i32 - } +// While i32 is structural-match, we do not want to leak this information. +// (See https://github.com/rust-lang/rust/issues/72156) +#[define_opaque(Bar)] +pub const fn leak_free() -> Bar { + 7i32 } -const LEAK_FREE: bar::Bar = bar::leak_free(); + +const LEAK_FREE: Bar = leak_free(); fn leak_free_test() { match LEAK_FREE { LEAK_FREE => (), - //~^ `Bar` cannot be used in patterns + //~^ ERROR `Bar` cannot be used in patterns _ => (), } } diff --git a/tests/ui/type-alias-impl-trait/structural-match-no-leak.stderr b/tests/ui/type-alias-impl-trait/structural-match-no-leak.stderr index 28f5d6728a9de..5067d78d16970 100644 --- a/tests/ui/type-alias-impl-trait/structural-match-no-leak.stderr +++ b/tests/ui/type-alias-impl-trait/structural-match-no-leak.stderr @@ -1,8 +1,8 @@ error: opaque type `Bar` cannot be used in patterns --> $DIR/structural-match-no-leak.rs:16:9 | -LL | const LEAK_FREE: bar::Bar = bar::leak_free(); - | ------------------------- constant defined here +LL | const LEAK_FREE: Bar = leak_free(); + | -------------------- constant defined here ... LL | LEAK_FREE => (), | ^^^^^^^^^ opaque type can't be used in patterns diff --git a/tests/ui/type-alias-impl-trait/structural-match.rs b/tests/ui/type-alias-impl-trait/structural-match.rs index 5025959153976..e922ffb74a543 100644 --- a/tests/ui/type-alias-impl-trait/structural-match.rs +++ b/tests/ui/type-alias-impl-trait/structural-match.rs @@ -1,22 +1,21 @@ #![feature(type_alias_impl_trait)] -mod foo { - pub type Foo = impl Send; +pub type Foo = impl Send; - // This is not structural-match - struct A; +// This is not structural-match +struct A; - pub const fn value() -> Foo { - A - } +#[define_opaque(Foo)] +pub const fn value() -> Foo { + A } -use foo::*; + const VALUE: Foo = value(); fn test() { match VALUE { VALUE => (), - //~^ `foo::Foo` cannot be used in patterns + //~^ ERROR `Foo` cannot be used in patterns _ => (), } } diff --git a/tests/ui/type-alias-impl-trait/structural-match.stderr b/tests/ui/type-alias-impl-trait/structural-match.stderr index b06b31a060fb9..a86b7f42ead6e 100644 --- a/tests/ui/type-alias-impl-trait/structural-match.stderr +++ b/tests/ui/type-alias-impl-trait/structural-match.stderr @@ -1,5 +1,5 @@ -error: opaque type `foo::Foo` cannot be used in patterns - --> $DIR/structural-match.rs:18:9 +error: opaque type `Foo` cannot be used in patterns + --> $DIR/structural-match.rs:17:9 | LL | const VALUE: Foo = value(); | ---------------- constant defined here diff --git a/tests/ui/type-alias-impl-trait/taint.rs b/tests/ui/type-alias-impl-trait/taint.rs index dfb947637c042..3d67c9bca4162 100644 --- a/tests/ui/type-alias-impl-trait/taint.rs +++ b/tests/ui/type-alias-impl-trait/taint.rs @@ -9,6 +9,7 @@ fn set(x: &mut isize) -> isize { *x } +#[define_opaque(Two)] fn d(x: Two) { let c1 = || set(x); //~ ERROR: expected generic lifetime parameter, found `'_` c1; diff --git a/tests/ui/type-alias-impl-trait/taint.stderr b/tests/ui/type-alias-impl-trait/taint.stderr index 17fcd4b7e9325..e2e34a5d1ba4b 100644 --- a/tests/ui/type-alias-impl-trait/taint.stderr +++ b/tests/ui/type-alias-impl-trait/taint.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/taint.rs:13:17 + --> $DIR/taint.rs:14:17 | LL | type Two<'a, 'b> = impl std::fmt::Debug; | -- this generic parameter must be used with a generic lifetime parameter diff --git a/tests/ui/type-alias-impl-trait/tait-in-function-return-type-issue-101903-fixed.rs b/tests/ui/type-alias-impl-trait/tait-in-function-return-type-issue-101903-fixed.rs index 109a70c766d42..ab33e9e794edb 100644 --- a/tests/ui/type-alias-impl-trait/tait-in-function-return-type-issue-101903-fixed.rs +++ b/tests/ui/type-alias-impl-trait/tait-in-function-return-type-issue-101903-fixed.rs @@ -19,6 +19,7 @@ type Sendable = impl Send + Duh; type Foo = impl Trait; +#[define_opaque(Foo)] fn foo() -> Foo { || 42 } diff --git a/tests/ui/type-alias-impl-trait/tait-normalize.rs b/tests/ui/type-alias-impl-trait/tait-normalize.rs index 38e09b6087b64..a34d167bcc391 100644 --- a/tests/ui/type-alias-impl-trait/tait-normalize.rs +++ b/tests/ui/type-alias-impl-trait/tait-normalize.rs @@ -1,3 +1,5 @@ +//@ revisions: current next +//@ [next] compile-flags: -Znext-solver //@ check-pass #![feature(type_alias_impl_trait)] diff --git a/tests/ui/type-alias-impl-trait/tait-param-inference-issue-117310.rs b/tests/ui/type-alias-impl-trait/tait-param-inference-issue-117310.rs index be743e8e27003..488c581247dcf 100644 --- a/tests/ui/type-alias-impl-trait/tait-param-inference-issue-117310.rs +++ b/tests/ui/type-alias-impl-trait/tait-param-inference-issue-117310.rs @@ -10,9 +10,11 @@ impl Trait for (A, B, u8) where A: Deref, B: Deref, {} impl Trait for (A, B, i8) {} type TaitSized = impl Sized; +#[define_opaque(TaitSized)] fn def_tait1() -> TaitSized {} type TaitCopy = impl Copy; +#[define_opaque(TaitCopy)] fn def_tait2() -> TaitCopy {} fn impl_trait () {} diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-dyn.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-dyn.rs index 66a6c0a35b50e..af787d66cc10b 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-dyn.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-dyn.rs @@ -5,6 +5,7 @@ type Foo = Box>; +#[define_opaque(Foo)] fn make_foo() -> Foo { Box::new(vec![1, 2, 3].into_iter()) } diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-impl-trait.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-impl-trait.rs index e46d2bd559cf2..921dc17593d3b 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-impl-trait.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-impl-trait.rs @@ -5,6 +5,7 @@ type Foo = impl Iterator; +#[define_opaque(Foo)] fn make_foo() -> Foo { vec![1, 2].into_iter() } @@ -12,6 +13,7 @@ fn make_foo() -> Foo { type Bar = impl Send; type Baz = impl Iterator; +#[define_opaque(Baz)] fn make_baz() -> Baz { vec!["1", "2"].into_iter() } diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs index 6b37552fed145..e21627e14b098 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs @@ -1,11 +1,15 @@ -#![feature(type_alias_impl_trait)] //@ check-pass -// Ensures that `const` items can constrain an opaque `impl Trait`. + +#![feature(type_alias_impl_trait)] use std::fmt::Debug; pub type Foo = impl Debug; +#[define_opaque(Foo)] const _FOO: Foo = 5; +#[define_opaque(Foo)] +static _BAR: Foo = 22_i32; + fn main() {} diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.rs index cbd91066c49da..95d52e1ff96ec 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.rs @@ -11,12 +11,14 @@ struct MyStruct { impl MyTrait for MyStruct {} +#[define_opaque(TE)] fn bla() -> TE { return MyStruct { v: 1 }; } +#[define_opaque(TE)] fn bla2() -> TE { - //~^ ERROR: item does not constrain `TE::{opaque#0}`, but has it in its signature + //~^ ERROR: item does not constrain `TE::{opaque#0}` bla() } diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.stderr index 819bde0218368..12aa94386dcbb 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.stderr +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.stderr @@ -1,12 +1,12 @@ -error: item does not constrain `TE::{opaque#0}`, but has it in its signature - --> $DIR/type-alias-impl-trait-fns.rs:18:4 +error: item does not constrain `TE::{opaque#0}` + --> $DIR/type-alias-impl-trait-fns.rs:20:4 | LL | fn bla2() -> TE { | ^^^^ | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/type-alias-impl-trait-fns.rs:23:11 + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained + --> $DIR/type-alias-impl-trait-fns.rs:25:11 | LL | type TE = impl MyTrait; | ^^^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-sized.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-sized.rs index 188b23732f912..929dec39b2f59 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-sized.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-sized.rs @@ -3,21 +3,25 @@ #![feature(type_alias_impl_trait)] type A = impl Sized; +#[define_opaque(A)] fn f1() -> A { 0 } type B = impl ?Sized; +#[define_opaque(B)] fn f2() -> &'static B { &[0] } type C = impl ?Sized + 'static; +#[define_opaque(C)] fn f3() -> &'static C { &[0] } type D = impl ?Sized; +#[define_opaque(D)] fn f4() -> &'static D { &1 } diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-struct.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-struct.rs index 1340a4214ee54..3fcb40df8324f 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-struct.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-struct.rs @@ -5,6 +5,7 @@ type Foo = Vec; +#[define_opaque(Foo)] fn make_foo() -> Foo { vec![true, false] } diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs index 8e90f96995324..32b39c8823d5c 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs @@ -6,20 +6,17 @@ #![feature(type_alias_impl_trait)] #![allow(dead_code)] -mod foo { - pub trait MyTrait {} +pub trait MyTrait {} - impl MyTrait for bool {} +impl MyTrait for bool {} - pub type Foo = impl MyTrait; +pub type Foo = impl MyTrait; - pub fn make_foo() -> Foo { - true - } +#[define_opaque(Foo)] +pub fn make_foo() -> Foo { + true } -use foo::*; - struct Blah { my_foo: Foo, my_u8: u8, diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-1.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-1.rs index 19986247d40df..53b7667aa9f1d 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-1.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-1.rs @@ -1,9 +1,10 @@ #![feature(type_alias_impl_trait)] -//@ known-bug: #109268 type Foo = impl Fn() -> Foo; +#[define_opaque(Foo)] fn crash(x: Foo) -> Foo { + //~^ ERROR overflow evaluating the requirement `>::Output == Foo` x } diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.rs index 761cc83af5106..d0c62d290698e 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.rs @@ -1,13 +1,13 @@ #![feature(type_alias_impl_trait)] -//@ known-bug: #109268 pub trait Bar { type Item; } type Foo = impl Bar; - +#[define_opaque(Foo)] fn crash(x: Foo) -> Foo { + //~^ ERROR overflow evaluating the requirement `>::Item == Foo` x } diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.stderr index 3d0f1d30ca2b0..40bd6517c06d8 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.stderr +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-2.stderr @@ -1,5 +1,5 @@ error[E0275]: overflow evaluating the requirement `>::Item == Foo` - --> $DIR/type-alias-impl-trait-with-cycle-error-2.rs:10:21 + --> $DIR/type-alias-impl-trait-with-cycle-error-2.rs:9:21 | LL | fn crash(x: Foo) -> Foo { | ^^^ diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.rs index 52942afd63922..de3d23b83a293 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.rs @@ -1,9 +1,9 @@ #![feature(type_alias_impl_trait)] -//@ known-bug: #109268 type Foo<'a> = impl Fn() -> Foo<'a>; - +#[define_opaque(Foo)] fn crash<'a>(_: &'a (), x: Foo<'a>) -> Foo<'a> { + //~^ ERROR overflow evaluating the requirement ` as FnOnce<()>>::Output == Foo<'a>` x } diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.stderr index 675689bac4295..f9e26fde1bda8 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.stderr +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error-3.stderr @@ -1,5 +1,5 @@ error[E0275]: overflow evaluating the requirement ` as FnOnce<()>>::Output == Foo<'a>` - --> $DIR/type-alias-impl-trait-with-cycle-error-3.rs:6:40 + --> $DIR/type-alias-impl-trait-with-cycle-error-3.rs:5:40 | LL | fn crash<'a>(_: &'a (), x: Foo<'a>) -> Foo<'a> { | ^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.rs index 8ca279eec921b..115df16bd3eac 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.rs @@ -3,6 +3,7 @@ type Foo = impl 'static; //~^ ERROR: at least one trait must be specified +#[define_opaque(Foo)] fn foo() -> Foo { "foo" } diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.stderr index 3f7acd3383010..da8fc595f6a6b 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.stderr +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.stderr @@ -5,7 +5,7 @@ LL | type Foo = impl 'static; | ^^^^^^^^^^^^ error: at least one trait must be specified - --> $DIR/type-alias-impl-trait-with-no-traits.rs:10:13 + --> $DIR/type-alias-impl-trait-with-no-traits.rs:11:13 | LL | fn bar() -> impl 'static { | ^^^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait.rs index 0fe653ac471a1..ff11fb4fcaf07 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait.rs @@ -17,6 +17,7 @@ fn main() { // single definition type Foo = impl std::fmt::Display; +#[define_opaque(Foo)] fn foo() -> Foo { "foo" } @@ -24,30 +25,36 @@ fn foo() -> Foo { // two definitions type Bar = impl std::fmt::Display; +#[define_opaque(Bar)] fn bar1() -> Bar { "bar1" } +#[define_opaque(Bar)] fn bar2() -> Bar { "bar2" } type MyIter = impl Iterator; +#[define_opaque(MyIter)] fn my_iter(t: T) -> MyIter { std::iter::once(t) } +#[define_opaque(MyIter)] fn my_iter2(t: T) -> MyIter { std::iter::once(t) } // param names should not have an effect! +#[define_opaque(MyIter)] fn my_iter3(u: U) -> MyIter { std::iter::once(u) } // param position should not have an effect! +#[define_opaque(MyIter)] fn my_iter4(_: U, v: V) -> MyIter { std::iter::once(v) } @@ -55,6 +62,7 @@ fn my_iter4(_: U, v: V) -> MyIter { // param names should not have an effect! type MyOtherIter = impl Iterator; +#[define_opaque(MyOtherIter)] fn my_other_iter(u: U) -> MyOtherIter { std::iter::once(u) } @@ -62,18 +70,18 @@ fn my_other_iter(u: U) -> MyOtherIter { trait Trait {} type GenericBound<'a, T: Trait + 'a> = impl Sized + 'a; +#[define_opaque(GenericBound)] fn generic_bound<'a, T: Trait + 'a>(t: T) -> GenericBound<'a, T> { t } -mod pass_through { - pub type Passthrough = impl Sized + 'static; +pub type Passthrough = impl Sized + 'static; - fn define_passthrough(t: T) -> Passthrough { - t - } +#[define_opaque(Passthrough)] +fn define_passthrough(t: T) -> Passthrough { + t } -fn use_passthrough(x: pass_through::Passthrough) -> pass_through::Passthrough { +fn use_passthrough(x: Passthrough) -> Passthrough { x } diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait2.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait2.rs index 65e2feaf7954d..e87fda8159bfd 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait2.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait2.rs @@ -14,71 +14,74 @@ fn main() { assert_eq!(my_iter(42u8).collect::>(), vec![42u8]); } -use defining_use_scope::*; +// single definition +pub type Foo = impl std::fmt::Display; -mod defining_use_scope { - // single definition - pub type Foo = impl std::fmt::Display; - - pub fn foo() -> Foo { - "foo" - } - - // two definitions - pub type Bar = impl std::fmt::Display; +#[define_opaque(Foo)] +pub fn foo() -> Foo { + "foo" +} - pub fn bar1() -> Bar { - "bar1" - } +// two definitions +pub type Bar = impl std::fmt::Display; - pub fn bar2() -> Bar { - "bar2" - } +#[define_opaque(Bar)] +pub fn bar1() -> Bar { + "bar1" +} - pub type MyIter = impl Iterator; +#[define_opaque(Bar)] +pub fn bar2() -> Bar { + "bar2" +} - pub fn my_iter(t: T) -> MyIter { - std::iter::once(t) - } +pub type MyIter = impl Iterator; - fn my_iter2(t: T) -> MyIter { - std::iter::once(t) - } +#[define_opaque(MyIter)] +pub fn my_iter(t: T) -> MyIter { + std::iter::once(t) +} - // param names should not have an effect! - fn my_iter3(u: U) -> MyIter { - std::iter::once(u) - } +#[define_opaque(MyIter)] +fn my_iter2(t: T) -> MyIter { + std::iter::once(t) +} - // param position should not have an effect! - fn my_iter4(_: U, v: V) -> MyIter { - std::iter::once(v) - } +// param names should not have an effect! +#[define_opaque(MyIter)] +fn my_iter3(u: U) -> MyIter { + std::iter::once(u) +} - // param names should not have an effect! - type MyOtherIter = impl Iterator; +// param position should not have an effect! +#[define_opaque(MyIter)] +fn my_iter4(_: U, v: V) -> MyIter { + std::iter::once(v) +} - fn my_other_iter(u: U) -> MyOtherIter { - std::iter::once(u) - } +// param names should not have an effect! +type MyOtherIter = impl Iterator; - trait Trait {} - type GenericBound<'a, T: Trait + 'a> = impl Sized + 'a; +#[define_opaque(MyOtherIter)] +fn my_other_iter(u: U) -> MyOtherIter { + std::iter::once(u) +} - fn generic_bound<'a, T: Trait + 'a>(t: T) -> GenericBound<'a, T> { - t - } +trait Trait {} +type GenericBound<'a, T: Trait + 'a> = impl Sized + 'a; - mod pass_through { - pub type Passthrough = impl Sized + 'static; +#[define_opaque(GenericBound)] +fn generic_bound<'a, T: Trait + 'a>(t: T) -> GenericBound<'a, T> { + t +} - fn define_passthrough(t: T) -> Passthrough { - t - } - } +pub type Passthrough = impl Sized + 'static; - fn use_passthrough(x: pass_through::Passthrough) -> pass_through::Passthrough { - x - } +#[define_opaque(Passthrough)] +fn define_passthrough(t: T) -> Passthrough { + t +} +fn use_passthrough(x: Passthrough) -> Passthrough { + x } diff --git a/tests/ui/type-alias-impl-trait/type-alias-nested-impl-trait.rs b/tests/ui/type-alias-impl-trait/type-alias-nested-impl-trait.rs index af575a4ff36ba..20209c7b28c64 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-nested-impl-trait.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-nested-impl-trait.rs @@ -2,9 +2,10 @@ #![feature(type_alias_impl_trait)] -use std::iter::{once, Chain}; +use std::iter::{Chain, once}; type I = Chain>; +#[define_opaque(I)] fn test2>(x: A) -> I { x.chain(once("5")) } diff --git a/tests/ui/type-alias-impl-trait/type_of_a_let.current.stderr b/tests/ui/type-alias-impl-trait/type_of_a_let.current.stderr index 7cf2fe42da8b0..d4a2c23f7a77f 100644 --- a/tests/ui/type-alias-impl-trait/type_of_a_let.current.stderr +++ b/tests/ui/type-alias-impl-trait/type_of_a_let.current.stderr @@ -1,5 +1,5 @@ error[E0382]: use of moved value: `x` - --> $DIR/type_of_a_let.rs:21:16 + --> $DIR/type_of_a_let.rs:23:16 | LL | let x: Foo = 22_u32; | - move occurs because `x` has type `Foo`, which does not implement the `Copy` trait @@ -9,7 +9,7 @@ LL | same_type((x, y)); | ^ value used here after move error[E0382]: use of moved value: `y` - --> $DIR/type_of_a_let.rs:22:6 + --> $DIR/type_of_a_let.rs:24:6 | LL | let y: Foo = x; | - move occurs because `y` has type `Foo`, which does not implement the `Copy` trait diff --git a/tests/ui/type-alias-impl-trait/type_of_a_let.rs b/tests/ui/type-alias-impl-trait/type_of_a_let.rs index cc8caf886cfc3..9479b1084df12 100644 --- a/tests/ui/type-alias-impl-trait/type_of_a_let.rs +++ b/tests/ui/type-alias-impl-trait/type_of_a_let.rs @@ -10,11 +10,13 @@ use std::fmt::Debug; type Foo = impl Debug; +#[define_opaque(Foo)] fn foo1() -> (u32, Foo) { let x: Foo = 22_u32; (x, todo!()) } +#[define_opaque(Foo)] fn foo2() -> (u32, Foo) { let x: Foo = 22_u32; let y: Foo = x; diff --git a/tests/ui/type-alias-impl-trait/unbounded_opaque_type.rs b/tests/ui/type-alias-impl-trait/unbounded_opaque_type.rs index 09ff006acbddb..063c4deaf06ee 100644 --- a/tests/ui/type-alias-impl-trait/unbounded_opaque_type.rs +++ b/tests/ui/type-alias-impl-trait/unbounded_opaque_type.rs @@ -2,12 +2,9 @@ #![feature(type_alias_impl_trait)] -mod opaque { - pub type Opaque = impl Sized; - fn defining() -> Opaque {} -} - -use opaque::Opaque; +pub type Opaque = impl Sized; +#[define_opaque(Opaque)] +fn defining() -> Opaque {} struct Ss<'a, T>(&'a Opaque); diff --git a/tests/ui/type-alias-impl-trait/unconstrained-due-to-bad-pattern.rs b/tests/ui/type-alias-impl-trait/unconstrained-due-to-bad-pattern.rs index ae3d317ab4649..3f4d9d244ec34 100644 --- a/tests/ui/type-alias-impl-trait/unconstrained-due-to-bad-pattern.rs +++ b/tests/ui/type-alias-impl-trait/unconstrained-due-to-bad-pattern.rs @@ -3,6 +3,7 @@ type Tait = impl Copy; // Make sure that this TAIT isn't considered unconstrained... +#[define_opaque(Tait)] fn empty_opaque() -> Tait { if false { match empty_opaque() {} diff --git a/tests/ui/type-alias-impl-trait/unconstrained-due-to-bad-pattern.stderr b/tests/ui/type-alias-impl-trait/unconstrained-due-to-bad-pattern.stderr index 5c9a4688105ff..549e3bc8dc146 100644 --- a/tests/ui/type-alias-impl-trait/unconstrained-due-to-bad-pattern.stderr +++ b/tests/ui/type-alias-impl-trait/unconstrained-due-to-bad-pattern.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: type `Tait` is non-empty - --> $DIR/unconstrained-due-to-bad-pattern.rs:8:15 + --> $DIR/unconstrained-due-to-bad-pattern.rs:9:15 | LL | match empty_opaque() {} | ^^^^^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/unconstrained-impl-param.rs b/tests/ui/type-alias-impl-trait/unconstrained-impl-param.rs index b3510067047b6..415021a1808c2 100644 --- a/tests/ui/type-alias-impl-trait/unconstrained-impl-param.rs +++ b/tests/ui/type-alias-impl-trait/unconstrained-impl-param.rs @@ -3,6 +3,7 @@ use std::fmt::Display; type Opaque = impl Sized + 'static; +#[define_opaque(Opaque)] fn define() -> Opaque {} trait Trait { diff --git a/tests/ui/type-alias-impl-trait/unconstrained-impl-param.stderr b/tests/ui/type-alias-impl-trait/unconstrained-impl-param.stderr index 6206f169c5b9e..7a86685787ced 100644 --- a/tests/ui/type-alias-impl-trait/unconstrained-impl-param.stderr +++ b/tests/ui/type-alias-impl-trait/unconstrained-impl-param.stderr @@ -1,5 +1,5 @@ error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates - --> $DIR/unconstrained-impl-param.rs:11:6 + --> $DIR/unconstrained-impl-param.rs:12:6 | LL | impl<'a> Trait for Opaque<&'a str> { | ^^ unconstrained lifetime parameter diff --git a/tests/ui/type-alias-impl-trait/under-binder.rs b/tests/ui/type-alias-impl-trait/under-binder.rs index caf21d64027dc..bf8635b7d81f1 100644 --- a/tests/ui/type-alias-impl-trait/under-binder.rs +++ b/tests/ui/type-alias-impl-trait/under-binder.rs @@ -2,6 +2,7 @@ type Opaque<'a> = impl Sized + 'a; +#[define_opaque(Opaque)] fn test(f: fn(u8)) -> fn(Opaque<'_>) { f //~ ERROR E0792 } diff --git a/tests/ui/type-alias-impl-trait/under-binder.stderr b/tests/ui/type-alias-impl-trait/under-binder.stderr index f4a121ce440d7..0589f9ae11cd1 100644 --- a/tests/ui/type-alias-impl-trait/under-binder.stderr +++ b/tests/ui/type-alias-impl-trait/under-binder.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/under-binder.rs:6:5 + --> $DIR/under-binder.rs:7:5 | LL | type Opaque<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter diff --git a/tests/ui/type-alias-impl-trait/underconstrained_generic.rs b/tests/ui/type-alias-impl-trait/underconstrained_generic.rs index aa537dfc9176c..dc2d37b9d0535 100644 --- a/tests/ui/type-alias-impl-trait/underconstrained_generic.rs +++ b/tests/ui/type-alias-impl-trait/underconstrained_generic.rs @@ -18,11 +18,10 @@ impl ProofForConversion for () { type Converter = impl ProofForConversion; +#[define_opaque(Converter)] fn _defining_use() -> Converter { () //~^ ERROR the trait bound `T: Trait` is not satisfied } - -fn main() { -} +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/underconstrained_generic.stderr b/tests/ui/type-alias-impl-trait/underconstrained_generic.stderr index e50949ed8f369..e05c3094d53a1 100644 --- a/tests/ui/type-alias-impl-trait/underconstrained_generic.stderr +++ b/tests/ui/type-alias-impl-trait/underconstrained_generic.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `T: Trait` is not satisfied - --> $DIR/underconstrained_generic.rs:22:5 + --> $DIR/underconstrained_generic.rs:23:5 | LL | () | ^^ the trait `Trait` is not implemented for `T` @@ -17,7 +17,7 @@ note: required by a bound in an opaque type LL | type Converter = impl ProofForConversion; | ^^^^^^^^^^^^^^^^^^^^^ note: this definition site has more where clauses than the opaque type - --> $DIR/underconstrained_generic.rs:21:1 + --> $DIR/underconstrained_generic.rs:22:1 | LL | fn _defining_use() -> Converter { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/underconstrained_lifetime.rs b/tests/ui/type-alias-impl-trait/underconstrained_lifetime.rs index e8e7dd0ea0822..f4611f33d23bd 100644 --- a/tests/ui/type-alias-impl-trait/underconstrained_lifetime.rs +++ b/tests/ui/type-alias-impl-trait/underconstrained_lifetime.rs @@ -15,6 +15,7 @@ impl<'a, 'b> ProofForConversion<'a, 'b> for &'b &'a () { type Converter<'a, 'b> = impl ProofForConversion<'a, 'b>; // Even _defining_use with an explicit `'a: 'b` compiles fine, too. +#[define_opaque(Converter)] fn _defining_use<'a, 'b>(x: &'b &'a ()) -> Converter<'a, 'b> { x //~^ ERROR reference has a longer lifetime than the data it references diff --git a/tests/ui/type-alias-impl-trait/underconstrained_lifetime.stderr b/tests/ui/type-alias-impl-trait/underconstrained_lifetime.stderr index 7c07578d887e3..6a1f2b49397a9 100644 --- a/tests/ui/type-alias-impl-trait/underconstrained_lifetime.stderr +++ b/tests/ui/type-alias-impl-trait/underconstrained_lifetime.stderr @@ -1,5 +1,5 @@ error[E0491]: in type `&'b &'a ()`, reference has a longer lifetime than the data it references - --> $DIR/underconstrained_lifetime.rs:19:5 + --> $DIR/underconstrained_lifetime.rs:20:5 | LL | x | ^ diff --git a/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.rs b/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.rs index 56d975355c3e7..2dab81cba6f6a 100644 --- a/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.rs +++ b/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.rs @@ -5,6 +5,7 @@ mod foo { pub trait T {} pub type Alias<'a> = impl T; + //~^ ERROR: unconstrained opaque type fn bar() { super::with_positive(|&n| ()); //~^ ERROR mismatched types diff --git a/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.stderr b/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.stderr index 279bd3bca5abe..b58eae671209b 100644 --- a/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.stderr +++ b/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.stderr @@ -1,9 +1,17 @@ +error: unconstrained opaque type + --> $DIR/underef-index-out-of-bounds-121472.rs:7:26 + | +LL | pub type Alias<'a> = impl T; + | ^^^^^^ + | + = note: `Alias` must be used in combination with a concrete type within the same crate + error[E0308]: mismatched types - --> $DIR/underef-index-out-of-bounds-121472.rs:9:31 + --> $DIR/underef-index-out-of-bounds-121472.rs:10:31 | LL | pub type Alias<'a> = impl T; | ------ the expected opaque type -LL | fn bar() { +... LL | super::with_positive(|&n| ()); | ^^ | | @@ -18,6 +26,6 @@ LL - super::with_positive(|&n| ()); LL + super::with_positive(|n| ()); | -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/unnameable_type.rs b/tests/ui/type-alias-impl-trait/unnameable_type.rs index 5813f529dea19..0b97cc2941f6b 100644 --- a/tests/ui/type-alias-impl-trait/unnameable_type.rs +++ b/tests/ui/type-alias-impl-trait/unnameable_type.rs @@ -16,6 +16,7 @@ use private::Trait; // downstream type MyPrivate = impl Sized; impl Trait for u32 { + #[define_opaque(MyPrivate)] fn dont_define_this(private: MyPrivate) { //~^ ERROR: incompatible type for trait let _: () = private; diff --git a/tests/ui/type-alias-impl-trait/unnameable_type.stderr b/tests/ui/type-alias-impl-trait/unnameable_type.stderr index 25dc41df41930..6e4e024ce691b 100644 --- a/tests/ui/type-alias-impl-trait/unnameable_type.stderr +++ b/tests/ui/type-alias-impl-trait/unnameable_type.stderr @@ -1,9 +1,9 @@ error[E0053]: method `dont_define_this` has an incompatible type for trait - --> $DIR/unnameable_type.rs:19:34 + --> $DIR/unnameable_type.rs:20:34 | LL | type MyPrivate = impl Sized; | ---------- the found opaque type -LL | impl Trait for u32 { +... LL | fn dont_define_this(private: MyPrivate) { | ^^^^^^^^^ expected `Private`, found opaque type | diff --git a/tests/ui/type-alias-impl-trait/unused_generic_param.rs b/tests/ui/type-alias-impl-trait/unused_generic_param.rs index b675bc2e6225f..556e70d1c9624 100644 --- a/tests/ui/type-alias-impl-trait/unused_generic_param.rs +++ b/tests/ui/type-alias-impl-trait/unused_generic_param.rs @@ -7,16 +7,19 @@ fn main() {} type PartiallyDefined = impl Sized; +#[define_opaque(PartiallyDefined)] fn partially_defined(_: T) -> PartiallyDefined { 4u32 } type PartiallyDefined2 = impl Sized; +#[define_opaque(PartiallyDefined2)] fn partially_defined2(_: T) -> PartiallyDefined2 { 4u32 } +#[define_opaque(PartiallyDefined2)] fn partially_defined22(_: T) -> PartiallyDefined2 { 4u32 } diff --git a/tests/ui/type-alias-impl-trait/variance.rs b/tests/ui/type-alias-impl-trait/variance.rs index 40e8ec0129a32..d9140695dae28 100644 --- a/tests/ui/type-alias-impl-trait/variance.rs +++ b/tests/ui/type-alias-impl-trait/variance.rs @@ -5,17 +5,17 @@ trait Captures<'a> {} impl Captures<'_> for T {} -type NotCapturedEarly<'a> = impl Sized; //~ ['a: *, 'a: o] +type NotCapturedEarly<'a> = impl Sized; //~ ERROR ['a: *, 'a: o] //~^ ERROR: unconstrained opaque type -type CapturedEarly<'a> = impl Sized + Captures<'a>; //~ ['a: *, 'a: o] +type CapturedEarly<'a> = impl Sized + Captures<'a>; //~ ERROR ['a: *, 'a: o] //~^ ERROR: unconstrained opaque type -type NotCapturedLate<'a> = dyn for<'b> Iterator; //~ ['a: *, 'a: o, 'b: o] +type NotCapturedLate<'a> = dyn for<'b> Iterator; //~ ERROR ['a: *, 'a: o, 'b: o] //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type //~| ERROR: unconstrained opaque type -type Captured<'a> = dyn for<'b> Iterator>; //~ ['a: *, 'a: o, 'b: o] +type Captured<'a> = dyn for<'b> Iterator>; //~ ERROR ['a: *, 'a: o, 'b: o] //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type //~| ERROR: unconstrained opaque type @@ -31,24 +31,24 @@ trait Foo<'i> { } impl<'i> Foo<'i> for &'i () { - type ImplicitCapture<'a> = impl Sized; //~ ['i: *, 'a: *, 'i: o, 'a: o] + type ImplicitCapture<'a> = impl Sized; //~ ERROR ['i: *, 'a: *, 'i: o, 'a: o] //~^ ERROR: unconstrained opaque type - type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ ['i: *, 'a: *, 'i: o, 'a: o] + type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ ERROR ['i: *, 'a: *, 'i: o, 'a: o] //~^ ERROR: unconstrained opaque type - type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ ['i: *, 'a: *, 'i: o, 'a: o] + type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ ERROR ['i: *, 'a: *, 'i: o, 'a: o] //~^ ERROR: unconstrained opaque type } impl<'i> Foo<'i> for () { - type ImplicitCapture<'a> = impl Sized; //~ ['i: *, 'a: *, 'i: o, 'a: o] + type ImplicitCapture<'a> = impl Sized; //~ ERROR ['i: *, 'a: *, 'i: o, 'a: o] //~^ ERROR: unconstrained opaque type - type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ ['i: *, 'a: *, 'i: o, 'a: o] + type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ ERROR ['i: *, 'a: *, 'i: o, 'a: o] //~^ ERROR: unconstrained opaque type - type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ ['i: *, 'a: *, 'i: o, 'a: o] + type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ ERROR ['i: *, 'a: *, 'i: o, 'a: o] //~^ ERROR: unconstrained opaque type } @@ -59,19 +59,20 @@ impl<'a> Nesting<'a> for &'a () { type Output = &'a (); } type NestedDeeply<'a> = - impl Nesting< //~ ['a: *, 'a: o] + impl Nesting< //~ ERROR ['a: *, 'a: o] 'a, - Output = impl Nesting< //~ ['a: *, 'a: o] + Output = impl Nesting< //~ ERROR ['a: *, 'a: o] 'a, - Output = impl Nesting< //~ ['a: *, 'a: o] + Output = impl Nesting< //~ ERROR ['a: *, 'a: o] 'a, - Output = impl Nesting< //~ ['a: *, 'a: o] + Output = impl Nesting< //~ ERROR ['a: *, 'a: o] 'a, - Output = impl Nesting<'a> //~ ['a: *, 'a: o] + Output = impl Nesting<'a> //~ ERROR ['a: *, 'a: o] > >, >, >; +#[define_opaque(NestedDeeply)] fn test<'a>() -> NestedDeeply<'a> { &() } diff --git a/tests/ui/type-alias-impl-trait/variance.stderr b/tests/ui/type-alias-impl-trait/variance.stderr index 79ce8148f19a4..138e4080c29d3 100644 --- a/tests/ui/type-alias-impl-trait/variance.stderr +++ b/tests/ui/type-alias-impl-trait/variance.stderr @@ -28,7 +28,7 @@ error: unconstrained opaque type LL | type NotCapturedEarly<'a> = impl Sized; | ^^^^^^^^^^ | - = note: `NotCapturedEarly` must be used in combination with a concrete type within the same module + = note: `NotCapturedEarly` must be used in combination with a concrete type within the same crate error: unconstrained opaque type --> $DIR/variance.rs:11:26 @@ -36,7 +36,7 @@ error: unconstrained opaque type LL | type CapturedEarly<'a> = impl Sized + Captures<'a>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `CapturedEarly` must be used in combination with a concrete type within the same module + = note: `CapturedEarly` must be used in combination with a concrete type within the same crate error: unconstrained opaque type --> $DIR/variance.rs:14:56 @@ -44,7 +44,7 @@ error: unconstrained opaque type LL | type NotCapturedLate<'a> = dyn for<'b> Iterator; | ^^^^^^^^^^ | - = note: `NotCapturedLate` must be used in combination with a concrete type within the same module + = note: `NotCapturedLate` must be used in combination with a concrete type within the same crate error: unconstrained opaque type --> $DIR/variance.rs:18:49 @@ -52,7 +52,7 @@ error: unconstrained opaque type LL | type Captured<'a> = dyn for<'b> Iterator>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `Captured` must be used in combination with a concrete type within the same module + = note: `Captured` must be used in combination with a concrete type within the same crate error: unconstrained opaque type --> $DIR/variance.rs:22:27 @@ -60,7 +60,7 @@ error: unconstrained opaque type LL | type Bar<'a, 'b: 'b, T> = impl Sized; | ^^^^^^^^^^ | - = note: `Bar` must be used in combination with a concrete type within the same module + = note: `Bar` must be used in combination with a concrete type within the same crate error: unconstrained opaque type --> $DIR/variance.rs:34:32 diff --git a/tests/ui/type-alias-impl-trait/weird-return-types.rs b/tests/ui/type-alias-impl-trait/weird-return-types.rs index 29d4faa7ba908..05668c836f9e3 100644 --- a/tests/ui/type-alias-impl-trait/weird-return-types.rs +++ b/tests/ui/type-alias-impl-trait/weird-return-types.rs @@ -4,11 +4,12 @@ #![feature(type_alias_impl_trait)] #![allow(dead_code)] -use std::future::Future; use std::fmt::Debug; +use std::future::Future; type Foo = impl Debug; +#[define_opaque(Foo)] fn f() -> impl Future { async move { 22_u32 } } diff --git a/tests/ui/type-alias-impl-trait/wf-check-definition-site.rs b/tests/ui/type-alias-impl-trait/wf-check-definition-site.rs index 19dd4c1793677..fd5edcc89eec0 100644 --- a/tests/ui/type-alias-impl-trait/wf-check-definition-site.rs +++ b/tests/ui/type-alias-impl-trait/wf-check-definition-site.rs @@ -14,6 +14,7 @@ struct Static(T); type OpaqueRet<'a> = impl Sized + 'a; +#[define_opaque(OpaqueRet)] fn test_return<'a>(msg: Static<&'static u8>) -> OpaqueRet<'a> { msg } @@ -23,9 +24,9 @@ fn test_rpit<'a>(msg: Static<&'static u8>) -> impl Sized + 'a { } type OpaqueAssign<'a> = impl Sized + 'a; -fn test_assign<'a>(msg: Static<&'static u8>) -> Option> { +#[define_opaque(OpaqueAssign)] +fn test_assign<'a>(msg: Static<&'static u8>) { let _: OpaqueAssign<'a> = msg; - None } // `OpaqueRef<'a, T> = Ref<'a, T>`, vs @@ -33,6 +34,7 @@ fn test_assign<'a>(msg: Static<&'static u8>) -> Option> { trait RefAt<'a>: 'a {} struct Ref<'a, T: RefAt<'a>>(&'a T); type OpaqueRef<'a, T: RefAt<'static>> = impl Sized + 'a; +#[define_opaque(OpaqueRef)] fn test_trait<'a, T: RefAt<'static>>(msg: Ref<'static, T>) -> OpaqueRef<'a, T> { msg } diff --git a/tests/ui/type-alias-impl-trait/wf-check-fn-def.rs b/tests/ui/type-alias-impl-trait/wf-check-fn-def.rs index 449e9fbd0d847..6de2c0ccc37c0 100644 --- a/tests/ui/type-alias-impl-trait/wf-check-fn-def.rs +++ b/tests/ui/type-alias-impl-trait/wf-check-fn-def.rs @@ -6,8 +6,11 @@ trait Bar { type FooFn = impl FnOnce(B); +#[define_opaque(FooFn)] fn foo() -> FooFn { - fn mop(bar: B) { bar.bar() } + fn mop(bar: B) { + bar.bar() + } mop // NOTE: no function pointer, but function zst item //~^ ERROR the trait bound `B: Bar` is not satisfied } diff --git a/tests/ui/type-alias-impl-trait/wf-check-fn-def.stderr b/tests/ui/type-alias-impl-trait/wf-check-fn-def.stderr index 9046a8a76b82b..b52ed3f43f482 100644 --- a/tests/ui/type-alias-impl-trait/wf-check-fn-def.stderr +++ b/tests/ui/type-alias-impl-trait/wf-check-fn-def.stderr @@ -1,13 +1,13 @@ error[E0277]: the trait bound `B: Bar` is not satisfied - --> $DIR/wf-check-fn-def.rs:11:5 + --> $DIR/wf-check-fn-def.rs:14:5 | LL | mop // NOTE: no function pointer, but function zst item | ^^^ the trait `Bar` is not implemented for `B` | note: required by a bound in `mop` - --> $DIR/wf-check-fn-def.rs:10:15 + --> $DIR/wf-check-fn-def.rs:11:15 | -LL | fn mop(bar: B) { bar.bar() } +LL | fn mop(bar: B) { | ^^^ required by this bound in `mop` help: consider restricting type parameter `B` with trait `Bar` | diff --git a/tests/ui/type-alias-impl-trait/wf-check-fn-ptrs.rs b/tests/ui/type-alias-impl-trait/wf-check-fn-ptrs.rs index 1484d9fd0734f..fd9ed53375652 100644 --- a/tests/ui/type-alias-impl-trait/wf-check-fn-ptrs.rs +++ b/tests/ui/type-alias-impl-trait/wf-check-fn-ptrs.rs @@ -8,8 +8,11 @@ trait Bar { type FooFn = impl FnOnce(B); +#[define_opaque(FooFn)] fn foo() -> FooFn { - fn mop(bar: B) { bar.bar() } + fn mop(bar: B) { + bar.bar() + } mop as fn(B) // function pointers don't have any obligations on them, // thus the above compiles. It's obviously unsound to just diff --git a/tests/ui/type-alias-impl-trait/wf-nested.rs b/tests/ui/type-alias-impl-trait/wf-nested.rs index 56c524c6db066..074ea8703a40e 100644 --- a/tests/ui/type-alias-impl-trait/wf-nested.rs +++ b/tests/ui/type-alias-impl-trait/wf-nested.rs @@ -19,7 +19,6 @@ impl Trait<&'static T> for () { type Out = IsStatic; } - // We could theoretically allow this (and previously did), as even // though the nested opaque is not well-formed, it can only be // used by normalizing the projection @@ -27,6 +26,7 @@ impl Trait<&'static T> for () { // Assuming that we check that this projection is well-formed, the wf // of the nested opaque is implied. type OuterOpaque1 = impl Trait<&'static T, Out = impl Sized>; +#[define_opaque(OuterOpaque1)] fn define() -> OuterOpaque1 {} //~^ ERROR `T` may not live long enough @@ -38,6 +38,7 @@ fn define_rpit() -> impl Trait<&'static T, Out = impl Sized> {} // soundness. type InnerOpaque = impl Sized; type OuterOpaque2 = impl Trait<&'static T, Out = InnerOpaque>; +#[define_opaque(OuterOpaque2)] fn define_nested_rpit() -> OuterOpaque2 {} //~^ ERROR the parameter type `T` may not live long enough diff --git a/tests/ui/type-alias-impl-trait/wf-nested.stderr b/tests/ui/type-alias-impl-trait/wf-nested.stderr index 6d50e11c5da84..d7ac5146c1e72 100644 --- a/tests/ui/type-alias-impl-trait/wf-nested.stderr +++ b/tests/ui/type-alias-impl-trait/wf-nested.stderr @@ -27,7 +27,7 @@ LL | fn define_rpit() -> impl Trait<&'static T, Out = impl Sized> {} | +++++++++ error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:41:47 + --> $DIR/wf-nested.rs:42:47 | LL | fn define_nested_rpit() -> OuterOpaque2 {} | ^^ diff --git a/tests/ui/type-alias-impl-trait/wf_check_closures.rs b/tests/ui/type-alias-impl-trait/wf_check_closures.rs index 2c70696ffcf48..c633909f51388 100644 --- a/tests/ui/type-alias-impl-trait/wf_check_closures.rs +++ b/tests/ui/type-alias-impl-trait/wf_check_closures.rs @@ -6,8 +6,9 @@ trait Bar { type FooFn = impl FnOnce(); +#[define_opaque(FooFn)] fn foo(bar: B) -> FooFn { - move || { bar.bar() } + move || bar.bar() //~^ ERROR the trait bound `B: Bar` is not satisfied } diff --git a/tests/ui/type-alias-impl-trait/wf_check_closures.stderr b/tests/ui/type-alias-impl-trait/wf_check_closures.stderr index 4156f0ca96aea..57e40445c87cf 100644 --- a/tests/ui/type-alias-impl-trait/wf_check_closures.stderr +++ b/tests/ui/type-alias-impl-trait/wf_check_closures.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `B: Bar` is not satisfied - --> $DIR/wf_check_closures.rs:10:5 + --> $DIR/wf_check_closures.rs:11:5 | -LL | move || { bar.bar() } - | ^^^^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `B` +LL | move || bar.bar() + | ^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `B` | note: required by a bound in `foo` - --> $DIR/wf_check_closures.rs:9:11 + --> $DIR/wf_check_closures.rs:10:11 | LL | fn foo(bar: B) -> FooFn { | ^^^ required by this bound in `foo` diff --git a/tests/ui/type-alias/type-param.rs b/tests/ui/type-alias/type-param.rs new file mode 100644 index 0000000000000..f8e73518bad6a --- /dev/null +++ b/tests/ui/type-alias/type-param.rs @@ -0,0 +1,11 @@ +//@ run-pass +// This is a smoke test to ensure that type aliases with type parameters +// are accepted by the compiler and that the parameters are correctly +// resolved in the aliased item type. + +#![allow(dead_code)] + +type Foo = extern "C" fn(T) -> bool; +type Bar = fn(T) -> bool; + +fn main() {} diff --git a/tests/ui/type-inference/regression-issue-81317.rs b/tests/ui/type-inference/regression-issue-81317.rs new file mode 100644 index 0000000000000..0b1266e6a0fd2 --- /dev/null +++ b/tests/ui/type-inference/regression-issue-81317.rs @@ -0,0 +1,71 @@ +// Regression test for #81317: type can no longer be infered as of 1.49 +// +// The problem is that the xor operator and the index.into() call +// each have two candidate impls that could apply +// { S as BitXor, S as BitXor<&'a S> } for xor and +// { T::I as Into, T::I as Into } for index.into() +// previously inference was able to infer that the only valid combination was +// S as BitXor and T::I as Into +// +// after rust-lang/rust#73905 this is no longer infered +// +// the error message could be better e.g. +// when iv is unused or has an an explicitly specified type S +// there is currently the following help message +// +// error[E0284]: type annotations needed +// --> src/main.rs:13:24 +// | +// 44 | let iv = S ^ index.into(); +// | - ^^^^ +// | | +// | type must be known at this point +// | +// = note: cannot satisfy `>::Output == _` +// help: try using a fully qualified path to specify the expected types +// | +// 44 - let iv = S ^ index.into(); +// 44 + let iv = S ^ <::I as Into>::into(index); +// +// this is better as it's actually sufficent to fix the problem, +// while just specifying the type of iv as currently suggested is insufficent +// +//@ check-fail + +use std::ops::BitXor; + +pub struct S; + +pub trait P { + type I: Into + Into; +} + +pub fn decrypt_portion(index: T::I) { + let iv = S ^ index.into(); + //~^ ERROR type annotations needed + &iv.to_bytes_be(); +} + +impl S { + fn to_bytes_be(&self) -> &[u8] { + &[] + } +} + +impl BitXor for S { + type Output = S; + + fn bitxor(self, _rhs: Self) -> Self::Output { + S + } +} + +impl<'a> BitXor<&'a S> for S { + type Output = S; + + fn bitxor(self, _rhs: &'a S) -> Self::Output { + S + } +} + +fn main() {} diff --git a/tests/ui/type-inference/regression-issue-81317.stderr b/tests/ui/type-inference/regression-issue-81317.stderr new file mode 100644 index 0000000000000..fcd3fca06e18b --- /dev/null +++ b/tests/ui/type-inference/regression-issue-81317.stderr @@ -0,0 +1,17 @@ +error[E0282]: type annotations needed + --> $DIR/regression-issue-81317.rs:44:9 + | +LL | let iv = S ^ index.into(); + | ^^ +LL | +LL | &iv.to_bytes_be(); + | -- type must be known at this point + | +help: consider giving `iv` an explicit type + | +LL | let iv: /* Type */ = S ^ index.into(); + | ++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/type-param.rs b/tests/ui/type-param.rs deleted file mode 100644 index e7cf0e5446bcf..0000000000000 --- a/tests/ui/type-param.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass - -#![allow(non_camel_case_types)] -#![allow(dead_code)] - - - -type lteq = extern "C" fn(T) -> bool; - -pub fn main() { } diff --git a/tests/ui/type/ascription/issue-47666.stderr b/tests/ui/type/ascription/issue-47666.stderr index fd825e86675d3..6568845fe5de2 100644 --- a/tests/ui/type/ascription/issue-47666.stderr +++ b/tests/ui/type/ascription/issue-47666.stderr @@ -4,7 +4,6 @@ error: path separator must be a double colon LL | let _ = Option:Some(vec![0, 1]); | ^ | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 help: use a double colon instead | LL | let _ = Option::Some(vec![0, 1]); diff --git a/tests/ui/type/ascription/issue-54516.stderr b/tests/ui/type/ascription/issue-54516.stderr index 64fdc1fa24a63..925080e9050b6 100644 --- a/tests/ui/type/ascription/issue-54516.stderr +++ b/tests/ui/type/ascription/issue-54516.stderr @@ -4,7 +4,6 @@ error: path separator must be a double colon LL | println!("{}", std::mem:size_of::>()); | ^ | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 help: use a double colon instead | LL | println!("{}", std::mem::size_of::>()); diff --git a/tests/ui/type/ascription/issue-60933.stderr b/tests/ui/type/ascription/issue-60933.stderr index c68394d0504af..7b55935b35ba2 100644 --- a/tests/ui/type/ascription/issue-60933.stderr +++ b/tests/ui/type/ascription/issue-60933.stderr @@ -4,7 +4,6 @@ error: path separator must be a double colon LL | let _: usize = std::mem:size_of::(); | ^ | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 help: use a double colon instead | LL | let _: usize = std::mem::size_of::(); diff --git a/tests/ui/type/issue-103271.rs b/tests/ui/type/issue-103271.rs index 7cd76286a929e..98cfaaf5cff7b 100644 --- a/tests/ui/type/issue-103271.rs +++ b/tests/ui/type/issue-103271.rs @@ -1,15 +1,15 @@ fn main() { let iter_fun = <&[u32]>::iter; //~^ ERROR no function or associated item named `iter` found for reference `&[u32]` in the current scope [E0599] - //~| function or associated item not found in `&[u32]` + //~| NOTE function or associated item not found in `&[u32]` //~| HELP the function `iter` is implemented on `[u32]` for item in iter_fun(&[1,1]) { let x: &u32 = item; assert_eq!(x, &1); } let iter_fun2 = <(&[u32])>::iter; - //~^ no function or associated item named `iter` found for reference `&[u32]` in the current scope [E0599] - //~| function or associated item not found in `&[u32]` + //~^ ERROR no function or associated item named `iter` found for reference `&[u32]` in the current scope [E0599] + //~| NOTE function or associated item not found in `&[u32]` //~| HELP the function `iter` is implemented on `[u32]` for item2 in iter_fun2(&[1,1]) { let x: &u32 = item2; diff --git a/tests/ui/type/issue-91268.rs b/tests/ui/type/issue-91268.rs index 16d5b24114565..6297880702e85 100644 --- a/tests/ui/type/issue-91268.rs +++ b/tests/ui/type/issue-91268.rs @@ -1,5 +1,6 @@ -//@ error-pattern: this file contains an unclosed delimiter // ignore-tidy-trailing-newlines // `ţ` must be the last character in this file, it cannot be followed by a newline + +//~vv ERROR this file contains an unclosed delimiter fn main() { 0: u8(ţ \ No newline at end of file diff --git a/tests/ui/type/issue-91268.stderr b/tests/ui/type/issue-91268.stderr index 395559442d1c4..db7b5a188cf8b 100644 --- a/tests/ui/type/issue-91268.stderr +++ b/tests/ui/type/issue-91268.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-91268.rs:5:12 + --> $DIR/issue-91268.rs:6:12 | LL | fn main() { | - unclosed delimiter diff --git a/tests/ui/type/missing-let-in-binding.stderr b/tests/ui/type/missing-let-in-binding.stderr index a9d766e4c3cf5..dee3d56dc51e9 100644 --- a/tests/ui/type/missing-let-in-binding.stderr +++ b/tests/ui/type/missing-let-in-binding.stderr @@ -4,7 +4,6 @@ error: expected identifier, found `:` LL | _foo: i32 = 4; | ^ expected identifier | - = note: type ascription syntax has been removed, see issue #101728 help: you might have meant to introduce a new binding | LL | let _foo: i32 = 4; diff --git a/tests/ui/type/pattern_types/const_block.rs b/tests/ui/type/pattern_types/const_block.rs deleted file mode 100644 index ed19b10671af0..0000000000000 --- a/tests/ui/type/pattern_types/const_block.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![feature(pattern_types)] -#![feature(pattern_type_macro)] -#![feature(inline_const_pat)] -//@ check-pass - -use std::pat::pattern_type; - -fn bar(x: pattern_type!(u32 is 0..=const{ 5 + 5 })) {} - -fn main() {} diff --git a/tests/ui/type/pattern_types/derives.rs b/tests/ui/type/pattern_types/derives.rs index 3878c47554d98..a3959b3831779 100644 --- a/tests/ui/type/pattern_types/derives.rs +++ b/tests/ui/type/pattern_types/derives.rs @@ -1,4 +1,5 @@ -//! Check that pattern types don't implement traits of their base automatically +//! Check that pattern types don't implement traits of their base automatically. +//! Exceptions are `Clone` and `Copy`, which have builtin impls for pattern types. #![feature(pattern_types)] #![feature(pattern_type_macro)] diff --git a/tests/ui/type/pattern_types/derives.stderr b/tests/ui/type/pattern_types/derives.stderr index 9d4baef621be3..2d83684b152ef 100644 --- a/tests/ui/type/pattern_types/derives.stderr +++ b/tests/ui/type/pattern_types/derives.stderr @@ -1,13 +1,11 @@ error[E0369]: binary operation `==` cannot be applied to type `(i32) is 0..=999999999` - --> $DIR/derives.rs:10:20 + --> $DIR/derives.rs:11:20 | LL | #[derive(Clone, Copy, PartialEq)] | --------- in this derive macro expansion LL | #[repr(transparent)] LL | struct Nanoseconds(NanoI32); | ^^^^^^^ - | - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/type/pattern_types/derives_fail.rs b/tests/ui/type/pattern_types/derives_fail.rs new file mode 100644 index 0000000000000..a3fbad6672071 --- /dev/null +++ b/tests/ui/type/pattern_types/derives_fail.rs @@ -0,0 +1,26 @@ +//! Check that pattern types don't implement traits of their base automatically. +//! Exceptions are `Clone` and `Copy`, which have bultin impls for pattern types. + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)] +#[repr(transparent)] +struct Nanoseconds(NanoI32); +//~^ ERROR: the trait bound `(i32) is 0..=999999999: Eq` is not satisfied +//~| ERROR: `(i32) is 0..=999999999` doesn't implement `Debug` +//~| ERROR: the trait bound `(i32) is 0..=999999999: Ord` is not satisfied +//~| ERROR: the trait bound `(i32) is 0..=999999999: Hash` is not satisfied +//~| ERROR: the trait bound `(i32) is 0..=999999999: Default` is not satisfied +//~| ERROR: can't compare `(i32) is 0..=999999999` with `_` +//~| ERROR: `==` cannot be applied + +type NanoI32 = crate::pattern_type!(i32 is 0..=999_999_999); + +fn main() { + let x = Nanoseconds(unsafe { std::mem::transmute(42) }); + let y = x.clone(); + if y == x {} +} diff --git a/tests/ui/type/pattern_types/derives_fail.stderr b/tests/ui/type/pattern_types/derives_fail.stderr new file mode 100644 index 0000000000000..78bef726341d6 --- /dev/null +++ b/tests/ui/type/pattern_types/derives_fail.stderr @@ -0,0 +1,74 @@ +error[E0369]: binary operation `==` cannot be applied to type `(i32) is 0..=999999999` + --> $DIR/derives_fail.rs:11:20 + | +LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)] + | --------- in this derive macro expansion +LL | #[repr(transparent)] +LL | struct Nanoseconds(NanoI32); + | ^^^^^^^ + +error[E0277]: the trait bound `(i32) is 0..=999999999: Eq` is not satisfied + --> $DIR/derives_fail.rs:11:20 + | +LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)] + | -- in this derive macro expansion +LL | #[repr(transparent)] +LL | struct Nanoseconds(NanoI32); + | ^^^^^^^ the trait `Eq` is not implemented for `(i32) is 0..=999999999` + | +note: required by a bound in `AssertParamIsEq` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + +error[E0277]: `(i32) is 0..=999999999` doesn't implement `Debug` + --> $DIR/derives_fail.rs:11:20 + | +LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)] + | ----- in this derive macro expansion +LL | #[repr(transparent)] +LL | struct Nanoseconds(NanoI32); + | ^^^^^^^ `(i32) is 0..=999999999` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = help: the trait `Debug` is not implemented for `(i32) is 0..=999999999` + +error[E0277]: the trait bound `(i32) is 0..=999999999: Ord` is not satisfied + --> $DIR/derives_fail.rs:11:20 + | +LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)] + | --- in this derive macro expansion +LL | #[repr(transparent)] +LL | struct Nanoseconds(NanoI32); + | ^^^^^^^ the trait `Ord` is not implemented for `(i32) is 0..=999999999` + +error[E0277]: can't compare `(i32) is 0..=999999999` with `_` + --> $DIR/derives_fail.rs:11:20 + | +LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)] + | ---------- in this derive macro expansion +LL | #[repr(transparent)] +LL | struct Nanoseconds(NanoI32); + | ^^^^^^^ no implementation for `(i32) is 0..=999999999 < _` and `(i32) is 0..=999999999 > _` + | + = help: the trait `PartialOrd<_>` is not implemented for `(i32) is 0..=999999999` + +error[E0277]: the trait bound `(i32) is 0..=999999999: Hash` is not satisfied + --> $DIR/derives_fail.rs:11:20 + | +LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)] + | ---- in this derive macro expansion +LL | #[repr(transparent)] +LL | struct Nanoseconds(NanoI32); + | ^^^^^^^ the trait `Hash` is not implemented for `(i32) is 0..=999999999` + +error[E0277]: the trait bound `(i32) is 0..=999999999: Default` is not satisfied + --> $DIR/derives_fail.rs:11:20 + | +LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)] + | ------- in this derive macro expansion +LL | #[repr(transparent)] +LL | struct Nanoseconds(NanoI32); + | ^^^^^^^ the trait `Default` is not implemented for `(i32) is 0..=999999999` + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0277, E0369. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/type/pattern_types/feature-gate-pattern_types.rs b/tests/ui/type/pattern_types/feature-gate-pattern_types.rs index b4f4bd656f5ff..a51741675bbd6 100644 --- a/tests/ui/type/pattern_types/feature-gate-pattern_types.rs +++ b/tests/ui/type/pattern_types/feature-gate-pattern_types.rs @@ -3,13 +3,13 @@ use std::pat::pattern_type; type NonNullU32 = pattern_type!(u32 is 1..); -//~^ use of unstable library feature `pattern_type_macro` +//~^ ERROR use of unstable library feature `pattern_type_macro` type Percent = pattern_type!(u32 is 0..=100); -//~^ use of unstable library feature `pattern_type_macro` +//~^ ERROR use of unstable library feature `pattern_type_macro` type Negative = pattern_type!(i32 is ..=0); -//~^ use of unstable library feature `pattern_type_macro` +//~^ ERROR use of unstable library feature `pattern_type_macro` type Positive = pattern_type!(i32 is 0..); -//~^ use of unstable library feature `pattern_type_macro` +//~^ ERROR use of unstable library feature `pattern_type_macro` type Always = pattern_type!(Option is Some(_)); -//~^ use of unstable library feature `pattern_type_macro` +//~^ ERROR use of unstable library feature `pattern_type_macro` //~| ERROR pattern not supported in pattern types diff --git a/tests/ui/type/pattern_types/literals.rs b/tests/ui/type/pattern_types/literals.rs new file mode 100644 index 0000000000000..b2a83a2a8bdce --- /dev/null +++ b/tests/ui/type/pattern_types/literals.rs @@ -0,0 +1,139 @@ +//! Check where literals can be used to initialize pattern types and where not. + +#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +fn out_of_range() -> pattern_type!(u32 is 1..) { + 0 + //~^ ERROR mismatched types +} + +fn at_range_start() -> pattern_type!(u32 is 1..) { + 1 +} + +fn in_range() -> pattern_type!(u32 is 1..) { + 2 +} + +fn negative_lit_on_unsigned_ty() -> pattern_type!(u32 is 1..) { + -3 + //~^ ERROR: cannot apply unary operator `-` to type `(u32) is 1..` +} + +fn negative_lit_in_range() -> pattern_type!(i8 is -5..5) { + -2 + //~^ ERROR: cannot apply unary operator `-` to type `(i8) is -5..=4` +} + +fn positive_lit_in_range_of_signed() -> pattern_type!(i8 is -5..5) { + 2 +} + +fn negative_lit_at_range_start() -> pattern_type!(i8 is -5..5) { + -5 + //~^ ERROR mismatched types +} + +fn positive_lit_at_range_end() -> pattern_type!(i8 is -5..5) { + 4 +} + +fn lit_one_beyond_range_end() -> pattern_type!(i8 is -5..5) { + 5 + //~^ ERROR mismatched types +} + +fn wrong_lit_kind() -> pattern_type!(u32 is 1..) { + '3' + //~^ ERROR mismatched types +} + +fn char_lit_in_range() -> pattern_type!(char is 'a'..'z') { + 'b' + //~^ ERROR mismatched types +} + +fn char_lit_out_of_range() -> pattern_type!(char is 'a'..'z') { + 'A' + //~^ ERROR mismatched types +} + +fn lit_at_unsigned_range_inclusive_end() -> pattern_type!(u32 is 0..=1) { + 1 +} + +fn single_element_range() -> pattern_type!(u32 is 0..=0) { + 0 +} + +fn lit_oob_single_element_range() -> pattern_type!(u32 is 0..=0) { + 1 + //~^ ERROR mismatched types +} + +fn lit_oob_single_element_range_exclusive() -> pattern_type!(u32 is 0..1) { + 1 + //~^ ERROR mismatched types +} + +fn single_element_range_exclusive() -> pattern_type!(u32 is 0..1) { + 0 +} + +fn empty_range_at_base_type_min() -> pattern_type!(u32 is 0..0) { + //~^ ERROR evaluation of constant value failed + 0 +} + +fn empty_range_at_base_type_min2() -> pattern_type!(u32 is 0..0) { + //~^ ERROR evaluation of constant value failed + 1 +} + +fn empty_range() -> pattern_type!(u32 is 1..1) { + 0 + //~^ ERROR mismatched types +} + +fn empty_range2() -> pattern_type!(u32 is 1..1) { + 1 + //~^ ERROR mismatched types +} + +fn wraparound_range_at_base_ty_end() -> pattern_type!(u32 is 1..0) { + //~^ ERROR evaluation of constant value failed + 1 +} + +fn wraparound_range_at_base_ty_end2() -> pattern_type!(u32 is 1..0) { + //~^ ERROR evaluation of constant value failed + 0 +} + +fn wraparound_range_at_base_ty_end3() -> pattern_type!(u32 is 1..0) { + //~^ ERROR evaluation of constant value failed + 2 +} + +fn wraparound_range() -> pattern_type!(u32 is 2..1) { + 1 + //~^ ERROR mismatched types +} + +fn lit_in_wraparound_range() -> pattern_type!(u32 is 2..1) { + 0 + //~^ ERROR mismatched types +} + +fn lit_at_wraparound_range_start() -> pattern_type!(u32 is 2..1) { + 2 + //~^ ERROR mismatched types +} + +fn main() {} + +//~? ERROR pattern type ranges cannot wrap: 1..=0 +//~? ERROR pattern type ranges cannot wrap: 2..=0 diff --git a/tests/ui/type/pattern_types/literals.stderr b/tests/ui/type/pattern_types/literals.stderr new file mode 100644 index 0000000000000..5c926742f3cdb --- /dev/null +++ b/tests/ui/type/pattern_types/literals.stderr @@ -0,0 +1,193 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/literals.rs:86:62 + | +LL | fn empty_range_at_base_type_min() -> pattern_type!(u32 is 0..0) { + | ^ evaluation panicked: exclusive range end at minimum value of type + +error[E0080]: evaluation of constant value failed + --> $DIR/literals.rs:91:63 + | +LL | fn empty_range_at_base_type_min2() -> pattern_type!(u32 is 0..0) { + | ^ evaluation panicked: exclusive range end at minimum value of type + +error[E0080]: evaluation of constant value failed + --> $DIR/literals.rs:106:65 + | +LL | fn wraparound_range_at_base_ty_end() -> pattern_type!(u32 is 1..0) { + | ^ evaluation panicked: exclusive range end at minimum value of type + +error[E0080]: evaluation of constant value failed + --> $DIR/literals.rs:111:66 + | +LL | fn wraparound_range_at_base_ty_end2() -> pattern_type!(u32 is 1..0) { + | ^ evaluation panicked: exclusive range end at minimum value of type + +error[E0080]: evaluation of constant value failed + --> $DIR/literals.rs:116:66 + | +LL | fn wraparound_range_at_base_ty_end3() -> pattern_type!(u32 is 1..0) { + | ^ evaluation panicked: exclusive range end at minimum value of type + +error[E0308]: mismatched types + --> $DIR/literals.rs:9:5 + | +LL | fn out_of_range() -> pattern_type!(u32 is 1..) { + | ------------------------- expected `(u32) is 1..` because of return type +LL | 0 + | ^ expected `(u32) is 1..`, found integer + | + = note: expected pattern type `(u32) is 1..` + found type `{integer}` + +error[E0600]: cannot apply unary operator `-` to type `(u32) is 1..` + --> $DIR/literals.rs:22:5 + | +LL | -3 + | ^^ cannot apply unary operator `-` + +error[E0600]: cannot apply unary operator `-` to type `(i8) is -5..=4` + --> $DIR/literals.rs:27:5 + | +LL | -2 + | ^^ cannot apply unary operator `-` + +error[E0308]: mismatched types + --> $DIR/literals.rs:36:5 + | +LL | fn negative_lit_at_range_start() -> pattern_type!(i8 is -5..5) { + | -------------------------- expected `(i8) is -5..=4` because of return type +LL | -5 + | ^^ expected `(i8) is -5..=4`, found integer + | + = note: expected pattern type `(i8) is -5..=4` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/literals.rs:45:5 + | +LL | fn lit_one_beyond_range_end() -> pattern_type!(i8 is -5..5) { + | -------------------------- expected `(i8) is -5..=4` because of return type +LL | 5 + | ^ expected `(i8) is -5..=4`, found integer + | + = note: expected pattern type `(i8) is -5..=4` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/literals.rs:50:5 + | +LL | fn wrong_lit_kind() -> pattern_type!(u32 is 1..) { + | ------------------------- expected `(u32) is 1..` because of return type +LL | '3' + | ^^^ expected `(u32) is 1..`, found `char` + | + = note: expected pattern type `(u32) is 1..` + found type `char` + +error[E0308]: mismatched types + --> $DIR/literals.rs:55:5 + | +LL | fn char_lit_in_range() -> pattern_type!(char is 'a'..'z') { + | ------------------------------- expected `(char) is 'a'..='y'` because of return type +LL | 'b' + | ^^^ expected `(char) is 'a'..='y'`, found `char` + | + = note: expected pattern type `(char) is 'a'..='y'` + found type `char` + +error[E0308]: mismatched types + --> $DIR/literals.rs:60:5 + | +LL | fn char_lit_out_of_range() -> pattern_type!(char is 'a'..'z') { + | ------------------------------- expected `(char) is 'a'..='y'` because of return type +LL | 'A' + | ^^^ expected `(char) is 'a'..='y'`, found `char` + | + = note: expected pattern type `(char) is 'a'..='y'` + found type `char` + +error[E0308]: mismatched types + --> $DIR/literals.rs:73:5 + | +LL | fn lit_oob_single_element_range() -> pattern_type!(u32 is 0..=0) { + | --------------------------- expected `(u32) is 0..=0` because of return type +LL | 1 + | ^ expected `(u32) is 0..=0`, found integer + | + = note: expected pattern type `(u32) is 0..=0` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/literals.rs:78:5 + | +LL | fn lit_oob_single_element_range_exclusive() -> pattern_type!(u32 is 0..1) { + | -------------------------- expected `(u32) is 0..=0` because of return type +LL | 1 + | ^ expected `(u32) is 0..=0`, found integer + | + = note: expected pattern type `(u32) is 0..=0` + found type `{integer}` + +error: pattern type ranges cannot wrap: 1..=0 + +error[E0308]: mismatched types + --> $DIR/literals.rs:97:5 + | +LL | fn empty_range() -> pattern_type!(u32 is 1..1) { + | -------------------------- expected `(u32) is 1..=0` because of return type +LL | 0 + | ^ expected `(u32) is 1..=0`, found integer + | + = note: expected pattern type `(u32) is 1..=0` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/literals.rs:102:5 + | +LL | fn empty_range2() -> pattern_type!(u32 is 1..1) { + | -------------------------- expected `(u32) is 1..=0` because of return type +LL | 1 + | ^ expected `(u32) is 1..=0`, found integer + | + = note: expected pattern type `(u32) is 1..=0` + found type `{integer}` + +error: pattern type ranges cannot wrap: 2..=0 + +error[E0308]: mismatched types + --> $DIR/literals.rs:122:5 + | +LL | fn wraparound_range() -> pattern_type!(u32 is 2..1) { + | -------------------------- expected `(u32) is 2..=0` because of return type +LL | 1 + | ^ expected `(u32) is 2..=0`, found integer + | + = note: expected pattern type `(u32) is 2..=0` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/literals.rs:127:5 + | +LL | fn lit_in_wraparound_range() -> pattern_type!(u32 is 2..1) { + | -------------------------- expected `(u32) is 2..=0` because of return type +LL | 0 + | ^ expected `(u32) is 2..=0`, found integer + | + = note: expected pattern type `(u32) is 2..=0` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/literals.rs:132:5 + | +LL | fn lit_at_wraparound_range_start() -> pattern_type!(u32 is 2..1) { + | -------------------------- expected `(u32) is 2..=0` because of return type +LL | 2 + | ^ expected `(u32) is 2..=0`, found integer + | + = note: expected pattern type `(u32) is 2..=0` + found type `{integer}` + +error: aborting due to 22 previous errors + +Some errors have detailed explanations: E0080, E0308, E0600. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/type/pattern_types/matching.rs b/tests/ui/type/pattern_types/matching.rs new file mode 100644 index 0000000000000..b8463a8e82298 --- /dev/null +++ b/tests/ui/type/pattern_types/matching.rs @@ -0,0 +1,26 @@ +#![feature(pattern_types, pattern_type_macro, structural_match)] + +//@ check-pass + +use std::marker::StructuralPartialEq; +use std::pat::pattern_type; + +struct Thing(pattern_type!(u32 is 1..)); + +impl StructuralPartialEq for Thing {} +impl PartialEq for Thing { + fn eq(&self, other: &Thing) -> bool { + unsafe { std::mem::transmute::<_, u32>(self.0) == std::mem::transmute::<_, u32>(other.0) } + } +} + +impl Eq for Thing {} + +const TWO: Thing = Thing(2); + +const _: () = match TWO { + TWO => {} + _ => unreachable!(), +}; + +fn main() {} diff --git a/tests/ui/type/pattern_types/matching_fail.rs b/tests/ui/type/pattern_types/matching_fail.rs new file mode 100644 index 0000000000000..8e2c741e3e0ab --- /dev/null +++ b/tests/ui/type/pattern_types/matching_fail.rs @@ -0,0 +1,25 @@ +#![feature(pattern_types, pattern_type_macro, structural_match)] + +use std::pat::pattern_type; + +const THREE: pattern_type!(u32 is 1..) = 3; + +const _: () = match THREE { + THREE => {} + //~^ ERROR non-structural type + _ => unreachable!(), +}; + +const _: () = match THREE { + 3 => {} + //~^ ERROR mismatched types + _ => unreachable!(), +}; + +const _: () = match 3 { + THREE => {} + //~^ ERROR mismatched types + _ => unreachable!(), +}; + +fn main() {} diff --git a/tests/ui/type/pattern_types/matching_fail.stderr b/tests/ui/type/pattern_types/matching_fail.stderr new file mode 100644 index 0000000000000..446180d80f24b --- /dev/null +++ b/tests/ui/type/pattern_types/matching_fail.stderr @@ -0,0 +1,43 @@ +error: constant of non-structural type `(u32) is 1..` in a pattern + --> $DIR/matching_fail.rs:8:5 + | +LL | const THREE: pattern_type!(u32 is 1..) = 3; + | -------------------------------------- constant defined here +... +LL | THREE => {} + | ^^^^^ constant of non-structural type + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + +error[E0308]: mismatched types + --> $DIR/matching_fail.rs:14:5 + | +LL | const _: () = match THREE { + | ----- this expression has type `(u32) is 1..` +LL | 3 => {} + | ^ expected `(u32) is 1..`, found integer + | + = note: expected pattern type `(u32) is 1..` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/matching_fail.rs:20:5 + | +LL | const THREE: pattern_type!(u32 is 1..) = 3; + | -------------------------------------- constant defined here +... +LL | const _: () = match 3 { + | - this expression has type `{integer}` +LL | THREE => {} + | ^^^^^ + | | + | expected integer, found `(u32) is 1..` + | `THREE` is interpreted as a constant, not a new binding + | help: introduce a new binding instead: `other_three` + | + = note: expected type `{integer}` + found pattern type `(u32) is 1..` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type/pattern_types/nested.rs b/tests/ui/type/pattern_types/nested.rs index 9ca9c7923dedd..fd950d7329148 100644 --- a/tests/ui/type/pattern_types/nested.rs +++ b/tests/ui/type/pattern_types/nested.rs @@ -1,6 +1,6 @@ //! Check that pattern types can only have specific base types -#![feature(pattern_types)] +#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)] #![feature(pattern_type_macro)] use std::pat::pattern_type; @@ -14,14 +14,16 @@ const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); // We want to get the most narrowest version that a pattern could be const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); //~^ ERROR: not a valid base type for range patterns -//~| ERROR: mismatched types +//~| ERROR: cannot apply unary operator `-` to type `(i32) is 1..` const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); //~^ ERROR: not a valid base type for range patterns +//~| ERROR: not a valid base type for range patterns //~| ERROR: mismatched types const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); //~^ ERROR: not a valid base type for range patterns +//~| ERROR: not a valid base type for range patterns //~| ERROR: mismatched types const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); diff --git a/tests/ui/type/pattern_types/nested.stderr b/tests/ui/type/pattern_types/nested.stderr index b753b0a9c9baa..bb206d9db3db8 100644 --- a/tests/ui/type/pattern_types/nested.stderr +++ b/tests/ui/type/pattern_types/nested.stderr @@ -1,96 +1,181 @@ -error: `(u32) is 1..=` is not a valid base type for range patterns - --> $DIR/nested.rs:10:34 +error[E0308]: mismatched types + --> $DIR/nested.rs:10:63 | LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ expected `(u32) is 1..`, found integer | -note: range patterns only support integers - --> $DIR/nested.rs:10:63 + = note: expected pattern type `(u32) is 1..` + found type `{integer}` + +error[E0277]: `(u32) is 1..` is not a valid base type for range patterns + --> $DIR/nested.rs:10:34 | LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `(u32) is 1..` + = help: the following other types implement trait `core::pat::RangePattern`: + char + i128 + i16 + i32 + i64 + i8 + isize + u128 + and 5 others -error: `(i32) is 1..=` is not a valid base type for range patterns +error[E0277]: `(i32) is 1..` is not a valid base type for range patterns --> $DIR/nested.rs:15:35 | LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: range patterns only support integers - --> $DIR/nested.rs:15:64 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..` + = help: the following other types implement trait `core::pat::RangePattern`: + char + i128 + i16 + i32 + i64 + i8 + isize + u128 + and 5 others + +error[E0600]: cannot apply unary operator `-` to type `(i32) is 1..` + --> $DIR/nested.rs:15:67 | LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); - | ^^^^^ + | ^^ cannot apply unary operator `-` -error: `(i32) is 1..=` is not a valid base type for range patterns +error[E0277]: `(i32) is 1..` is not a valid base type for range patterns --> $DIR/nested.rs:19:35 | LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: range patterns only support integers - --> $DIR/nested.rs:19:64 - | -LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..` + = help: the following other types implement trait `core::pat::RangePattern`: + char + i128 + i16 + i32 + i64 + i8 + isize + u128 + and 5 others -error: `()` is not a valid base type for range patterns - --> $DIR/nested.rs:23:35 +error[E0308]: mismatched types + --> $DIR/nested.rs:19:66 | -LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); - | ^^ +LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); + | ^ + | | + | expected `(i32) is 1..`, found integer + | arguments to this function are incorrect | -note: range patterns only support integers - --> $DIR/nested.rs:23:41 + = note: expected pattern type `(i32) is 1..` + found type `{integer}` +help: the return type of this call is `{integer}` due to the type of the argument passed + --> $DIR/nested.rs:19:66 | -LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); - | ^^^ +LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); + | ^ this argument influences the return type of `RangeSub` +note: method defined here + --> $SRC_DIR/core/src/pat.rs:LL:COL -error: `f32` is not a valid base type for range patterns - --> $DIR/nested.rs:27:35 - | -LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); - | ^^^ - | -note: range patterns only support integers - --> $DIR/nested.rs:27:42 +error[E0277]: `(i32) is 1..` is not a valid base type for range patterns + --> $DIR/nested.rs:19:66 | -LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); - | ^^^^^^^^^^ +LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); + | ^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `(i32) is 1..` + = help: the following other types implement trait `core::pat::RangePattern`: + char + i128 + i16 + i32 + i64 + i8 + isize + u128 + and 5 others -error[E0308]: mismatched types - --> $DIR/nested.rs:10:63 - | -LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); - | ^ expected `(u32) is 1..=`, found integer +error[E0277]: `()` is not a valid base type for range patterns + --> $DIR/nested.rs:24:35 | - = note: expected pattern type `(u32) is 1..=` - found type `{integer}` +LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); + | ^^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `()` + = help: the following other types implement trait `core::pat::RangePattern`: + char + i128 + i16 + i32 + i64 + i8 + isize + u128 + and 5 others error[E0308]: mismatched types - --> $DIR/nested.rs:15:67 - | -LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); - | ^^ expected `(i32) is 1..=`, found integer + --> $DIR/nested.rs:24:43 | - = note: expected pattern type `(i32) is 1..=` - found type `{integer}` - -error[E0308]: mismatched types - --> $DIR/nested.rs:19:66 +LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); + | ^ + | | + | expected `()`, found integer + | arguments to this function are incorrect | -LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); - | ^ expected `(i32) is 1..=`, found integer +help: the return type of this call is `{integer}` due to the type of the argument passed + --> $DIR/nested.rs:24:43 | - = note: expected pattern type `(i32) is 1..=` - found type `{integer}` +LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); + | ^ this argument influences the return type of `RangeSub` +note: method defined here + --> $SRC_DIR/core/src/pat.rs:LL:COL -error[E0308]: mismatched types - --> $DIR/nested.rs:23:43 +error[E0277]: `()` is not a valid base type for range patterns + --> $DIR/nested.rs:24:43 | LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); - | ^ expected `()`, found integer + | ^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `()` + = help: the following other types implement trait `core::pat::RangePattern`: + char + i128 + i16 + i32 + i64 + i8 + isize + u128 + and 5 others + +error[E0277]: `f32` is not a valid base type for range patterns + --> $DIR/nested.rs:29:49 + | +LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); + | ^^^ only integer types and `char` are supported + | + = help: the trait `core::pat::RangePattern` is not implemented for `f32` + = help: the following other types implement trait `core::pat::RangePattern`: + i128 + i16 + i32 + i64 + i8 + isize + u128 + u16 + and 4 others -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0277, E0308, E0600. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/type/pattern_types/or_patterns.rs b/tests/ui/type/pattern_types/or_patterns.rs new file mode 100644 index 0000000000000..25cb1867047aa --- /dev/null +++ b/tests/ui/type/pattern_types/or_patterns.rs @@ -0,0 +1,45 @@ +//! Demonstrate some use cases of or patterns + +//@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$SOME_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" + +#![feature( + pattern_type_macro, + pattern_types, + rustc_attrs, + const_trait_impl, + pattern_type_range_trait +)] + +use std::pat::pattern_type; + +#[rustc_layout(debug)] +type NonNullI8 = pattern_type!(i8 is ..0 | 1..); +//~^ ERROR: layout_of + +#[rustc_layout(debug)] +type NonNegOneI8 = pattern_type!(i8 is ..-1 | 0..); +//~^ ERROR: layout_of + +fn main() { + let _: NonNullI8 = 42; + let _: NonNullI8 = 1; + let _: NonNullI8 = 0; + //~^ ERROR: mismatched types + let _: NonNullI8 = -1; + //~^ ERROR: cannot apply unary operator + let _: NonNullI8 = -128; + //~^ ERROR: cannot apply unary operator + let _: NonNullI8 = 127; + + let _: NonNegOneI8 = 42; + let _: NonNegOneI8 = 1; + let _: NonNegOneI8 = 0; + let _: NonNegOneI8 = -1; + //~^ ERROR: cannot apply unary operator + let _: NonNegOneI8 = -2; + //~^ ERROR: cannot apply unary operator + let _: NonNegOneI8 = -128; + //~^ ERROR: cannot apply unary operator + let _: NonNegOneI8 = 127; +} diff --git a/tests/ui/type/pattern_types/or_patterns.stderr b/tests/ui/type/pattern_types/or_patterns.stderr new file mode 100644 index 0000000000000..58ca585f4a9a3 --- /dev/null +++ b/tests/ui/type/pattern_types/or_patterns.stderr @@ -0,0 +1,123 @@ +error[E0308]: mismatched types + --> $DIR/or_patterns.rs:27:24 + | +LL | let _: NonNullI8 = 0; + | --------- ^ expected `(i8) is (i8::MIN..=-1 | 1..)`, found integer + | | + | expected due to this + | + = note: expected pattern type `(i8) is (i8::MIN..=-1 | 1..)` + found type `{integer}` + +error[E0600]: cannot apply unary operator `-` to type `(i8) is (i8::MIN..=-1 | 1..)` + --> $DIR/or_patterns.rs:29:24 + | +LL | let _: NonNullI8 = -1; + | ^^ cannot apply unary operator `-` + +error[E0600]: cannot apply unary operator `-` to type `(i8) is (i8::MIN..=-1 | 1..)` + --> $DIR/or_patterns.rs:31:24 + | +LL | let _: NonNullI8 = -128; + | ^^^^ cannot apply unary operator `-` + +error[E0600]: cannot apply unary operator `-` to type `(i8) is (i8::MIN..=-2 | 0..)` + --> $DIR/or_patterns.rs:38:26 + | +LL | let _: NonNegOneI8 = -1; + | ^^ cannot apply unary operator `-` + +error[E0600]: cannot apply unary operator `-` to type `(i8) is (i8::MIN..=-2 | 0..)` + --> $DIR/or_patterns.rs:40:26 + | +LL | let _: NonNegOneI8 = -2; + | ^^ cannot apply unary operator `-` + +error[E0600]: cannot apply unary operator `-` to type `(i8) is (i8::MIN..=-2 | 0..)` + --> $DIR/or_patterns.rs:42:26 + | +LL | let _: NonNegOneI8 = -128; + | ^^^^ cannot apply unary operator `-` + +error: layout_of((i8) is (i8::MIN..=-1 | 1..)) = Layout { + size: Size(1 bytes), + align: AbiAndPrefAlign { + abi: Align(1 bytes), + pref: $SOME_ALIGN, + }, + backend_repr: Scalar( + Initialized { + value: Int( + I8, + true, + ), + valid_range: 1..=255, + }, + ), + fields: Primitive, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I8, + true, + ), + valid_range: 1..=255, + }, + ), + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + } + --> $DIR/or_patterns.rs:17:1 + | +LL | type NonNullI8 = pattern_type!(i8 is ..0 | 1..); + | ^^^^^^^^^^^^^^ + +error: layout_of((i8) is (i8::MIN..=-2 | 0..)) = Layout { + size: Size(1 bytes), + align: AbiAndPrefAlign { + abi: Align(1 bytes), + pref: $SOME_ALIGN, + }, + backend_repr: Scalar( + Initialized { + value: Int( + I8, + true, + ), + valid_range: 0..=254, + }, + ), + fields: Primitive, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I8, + true, + ), + valid_range: 0..=254, + }, + ), + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + } + --> $DIR/or_patterns.rs:21:1 + | +LL | type NonNegOneI8 = pattern_type!(i8 is ..-1 | 0..); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0308, E0600. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/type/pattern_types/or_patterns_invalid.rs b/tests/ui/type/pattern_types/or_patterns_invalid.rs new file mode 100644 index 0000000000000..d341927601d50 --- /dev/null +++ b/tests/ui/type/pattern_types/or_patterns_invalid.rs @@ -0,0 +1,26 @@ +//! Demonstrate some use cases of or patterns + +#![feature( + pattern_type_macro, + pattern_types, + rustc_attrs, + const_trait_impl, + pattern_type_range_trait +)] + +use std::pat::pattern_type; + +fn main() { + //~? ERROR: only non-overlapping pattern type ranges are allowed at present + let not_adjacent: pattern_type!(i8 is -127..0 | 1..) = unsafe { std::mem::transmute(0) }; + + //~? ERROR: one pattern needs to end at `i8::MAX`, but was 29 instead + let not_wrapping: pattern_type!(i8 is 10..20 | 20..30) = unsafe { std::mem::transmute(0) }; + + //~? ERROR: only signed integer base types are allowed for or-pattern pattern types + let not_signed: pattern_type!(u8 is 10.. | 0..5) = unsafe { std::mem::transmute(0) }; + + //~? ERROR: allowed are two range patterns that are directly connected + let not_simple_enough_for_mvp: pattern_type!(i8 is ..0 | 1..10 | 10..) = + unsafe { std::mem::transmute(0) }; +} diff --git a/tests/ui/type/pattern_types/or_patterns_invalid.stderr b/tests/ui/type/pattern_types/or_patterns_invalid.stderr new file mode 100644 index 0000000000000..6964788a6c245 --- /dev/null +++ b/tests/ui/type/pattern_types/or_patterns_invalid.stderr @@ -0,0 +1,10 @@ +error: only non-overlapping pattern type ranges are allowed at present + +error: one pattern needs to end at `i8::MAX`, but was 29 instead + +error: only signed integer base types are allowed for or-pattern pattern types at present + +error: the only or-pattern types allowed are two range patterns that are directly connected at their overflow site + +error: aborting due to 4 previous errors + diff --git a/tests/ui/type/pattern_types/range_patterns.rs b/tests/ui/type/pattern_types/range_patterns.rs index 446a33195c8bd..21c1454d6cd53 100644 --- a/tests/ui/type/pattern_types/range_patterns.rs +++ b/tests/ui/type/pattern_types/range_patterns.rs @@ -1,4 +1,4 @@ -#![feature(pattern_types, rustc_attrs)] +#![feature(pattern_types, rustc_attrs, const_trait_impl, pattern_type_range_trait)] #![feature(pattern_type_macro)] #![allow(incomplete_features)] @@ -18,6 +18,29 @@ type A = Option; //~ ERROR layout_of #[rustc_layout(debug)] struct NonZeroU32New(pattern_type!(u32 is 1..)); //~ ERROR layout_of +#[rustc_layout(debug)] +type EMPTY = pattern_type!(u32 is 1..1); //~ ERROR unknown layout + +#[rustc_layout(debug)] +type WRAP = pattern_type!(u32 is 1..0); //~ ERROR unknown layout +//~^ ERROR: evaluation of constant value failed + +#[rustc_layout(debug)] +type WRAP2 = pattern_type!(u32 is 5..2); //~ ERROR unknown layout + +#[rustc_layout(debug)] +type SIGN = pattern_type!(i8 is -10..=10); //~ ERROR layout_of + +#[rustc_layout(debug)] +type MIN = pattern_type!(i8 is -128..=0); //~ ERROR layout_of + +#[rustc_layout(debug)] +type SignedWrap = pattern_type!(i8 is 120..=-120); //~ ERROR unknown layout + fn main() { let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(42_u32) }; } + +//~? ERROR pattern type ranges cannot wrap: 1..=0 +//~? ERROR pattern type ranges cannot wrap: 5..=1 +//~? ERROR pattern type ranges cannot wrap: 120..=-120 diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr index 690592ba0b8da..a05995a33f9ba 100644 --- a/tests/ui/type/pattern_types/range_patterns.stderr +++ b/tests/ui/type/pattern_types/range_patterns.stderr @@ -4,7 +4,7 @@ error: layout_of(NonZero) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -44,13 +44,13 @@ error: layout_of(NonZero) = Layout { LL | type X = std::num::NonZeroU32; | ^^^^^^ -error: layout_of((u32) is 1..=) = Layout { +error: layout_of((u32) is 1..) = Layout { size: Size(4 bytes), align: AbiAndPrefAlign { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -83,13 +83,13 @@ error: layout_of((u32) is 1..=) = Layout { LL | type Y = pattern_type!(u32 is 1..); | ^^^^^^ -error: layout_of(Option<(u32) is 1..=>) = Layout { +error: layout_of(Option<(u32) is 1..>) = Layout { size: Size(4 bytes), align: AbiAndPrefAlign { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -129,7 +129,7 @@ error: layout_of(Option<(u32) is 1..=>) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -151,7 +151,7 @@ error: layout_of(Option<(u32) is 1..=>) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -203,7 +203,7 @@ error: layout_of(Option>) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -243,7 +243,7 @@ error: layout_of(Option>) = Layout { abi: Align(1 bytes), pref: $SOME_ALIGN, }, - abi: Memory { + backend_repr: Memory { sized: true, }, fields: Arbitrary { @@ -265,7 +265,7 @@ error: layout_of(Option>) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -317,7 +317,7 @@ error: layout_of(NonZeroU32New) = Layout { abi: Align(4 bytes), pref: $SOME_ALIGN, }, - abi: Scalar( + backend_repr: Scalar( Initialized { value: Int( I32, @@ -357,5 +357,120 @@ error: layout_of(NonZeroU32New) = Layout { LL | struct NonZeroU32New(pattern_type!(u32 is 1..)); | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: pattern type ranges cannot wrap: 1..=0 +error: the type has an unknown layout + --> $DIR/range_patterns.rs:22:1 + | +LL | type EMPTY = pattern_type!(u32 is 1..1); + | ^^^^^^^^^^ + +error[E0080]: evaluation of constant value failed + --> $DIR/range_patterns.rs:25:37 + | +LL | type WRAP = pattern_type!(u32 is 1..0); + | ^ evaluation panicked: exclusive range end at minimum value of type + +error: the type has an unknown layout + --> $DIR/range_patterns.rs:25:1 + | +LL | type WRAP = pattern_type!(u32 is 1..0); + | ^^^^^^^^^ + +error: pattern type ranges cannot wrap: 5..=1 + +error: the type has an unknown layout + --> $DIR/range_patterns.rs:29:1 + | +LL | type WRAP2 = pattern_type!(u32 is 5..2); + | ^^^^^^^^^^ + +error: layout_of((i8) is -10..=10) = Layout { + size: Size(1 bytes), + align: AbiAndPrefAlign { + abi: Align(1 bytes), + pref: $SOME_ALIGN, + }, + backend_repr: Scalar( + Initialized { + value: Int( + I8, + true, + ), + valid_range: (..=10) | (246..), + }, + ), + fields: Primitive, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I8, + true, + ), + valid_range: (..=10) | (246..), + }, + ), + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + } + --> $DIR/range_patterns.rs:32:1 + | +LL | type SIGN = pattern_type!(i8 is -10..=10); + | ^^^^^^^^^ + +error: layout_of((i8) is i8::MIN..=0) = Layout { + size: Size(1 bytes), + align: AbiAndPrefAlign { + abi: Align(1 bytes), + pref: $SOME_ALIGN, + }, + backend_repr: Scalar( + Initialized { + value: Int( + I8, + true, + ), + valid_range: (..=0) | (128..), + }, + ), + fields: Primitive, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I8, + true, + ), + valid_range: (..=0) | (128..), + }, + ), + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + } + --> $DIR/range_patterns.rs:35:1 + | +LL | type MIN = pattern_type!(i8 is -128..=0); + | ^^^^^^^^ + +error: pattern type ranges cannot wrap: 120..=-120 + +error: the type has an unknown layout + --> $DIR/range_patterns.rs:38:1 + | +LL | type SignedWrap = pattern_type!(i8 is 120..=-120); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/type/pattern_types/range_patterns_unusable.stderr b/tests/ui/type/pattern_types/range_patterns_unusable.stderr index 8377d417452bb..7daa41d708179 100644 --- a/tests/ui/type/pattern_types/range_patterns_unusable.stderr +++ b/tests/ui/type/pattern_types/range_patterns_unusable.stderr @@ -4,7 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | let _: Option = unsafe { std::mem::transmute(z) }; | ^^^^^^^^^^^^^^^^^^^ | - = note: source type: `Option<(u32) is 1..=>` (32 bits) + = note: source type: `Option<(u32) is 1..>` (32 bits) = note: target type: `Option` (64 bits) error: aborting due to 1 previous error diff --git a/tests/ui/type/pattern_types/range_patterns_unusable_math.rs b/tests/ui/type/pattern_types/range_patterns_unusable_math.rs index ece4009e1e7d8..6125063699be9 100644 --- a/tests/ui/type/pattern_types/range_patterns_unusable_math.rs +++ b/tests/ui/type/pattern_types/range_patterns_unusable_math.rs @@ -11,5 +11,5 @@ type Z = Option; fn main() { let x: Y = unsafe { std::mem::transmute(42_u32) }; - let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..=` + let x = x + 1_u32; //~ ERROR cannot add `u32` to `(u32) is 1..` } diff --git a/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr b/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr index 373615e3714e5..a64f1db31766f 100644 --- a/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr +++ b/tests/ui/type/pattern_types/range_patterns_unusable_math.stderr @@ -1,10 +1,10 @@ -error[E0369]: cannot add `u32` to `(u32) is 1..=` +error[E0369]: cannot add `u32` to `(u32) is 1..` --> $DIR/range_patterns_unusable_math.rs:14:15 | LL | let x = x + 1_u32; | - ^ ----- u32 | | - | (u32) is 1..= + | (u32) is 1.. error: aborting due to 1 previous error diff --git a/tests/ui/type/pattern_types/reverse_range.rs b/tests/ui/type/pattern_types/reverse_range.rs index 6a245615f1ac0..e698e9dd541d5 100644 --- a/tests/ui/type/pattern_types/reverse_range.rs +++ b/tests/ui/type/pattern_types/reverse_range.rs @@ -1,14 +1,11 @@ //! Check that the range start must be smaller than the range end -//@ known-bug: unknown -//@ failure-status: 101 -//@ normalize-stderr: "note: .*\n\n" -> "" -//@ normalize-stderr: "thread 'rustc' panicked.*\n" -> "" -//@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " -//@ rustc-env:RUST_BACKTRACE=0 - -#![feature(pattern_types)] +#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)] #![feature(pattern_type_macro)] use std::pat::pattern_type; const NONE: pattern_type!(u8 is 1..0) = unsafe { std::mem::transmute(3_u8) }; +//~^ NOTE: exclusive range end at minimum value of type +//~| ERROR: evaluation of constant value failed + +fn main() {} diff --git a/tests/ui/type/pattern_types/reverse_range.stderr b/tests/ui/type/pattern_types/reverse_range.stderr index b714ca7d9ab96..90f8ef3261a0b 100644 --- a/tests/ui/type/pattern_types/reverse_range.stderr +++ b/tests/ui/type/pattern_types/reverse_range.stderr @@ -1,17 +1,9 @@ -error[E0601]: `main` function not found in crate `reverse_range` - --> $DIR/reverse_range.rs:14:78 +error[E0080]: evaluation of constant value failed + --> $DIR/reverse_range.rs:7:36 | LL | const NONE: pattern_type!(u8 is 1..0) = unsafe { std::mem::transmute(3_u8) }; - | ^ consider adding a `main` function to `$DIR/reverse_range.rs` + | ^ evaluation panicked: exclusive range end at minimum value of type - -assertion failed: end <= max_value -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [eval_to_allocation_raw] const-evaluating + checking `NONE` -#1 [eval_to_const_value_raw] simplifying constant for the type system `NONE` -... and 1 other queries... use `env RUST_BACKTRACE=1` to see the full query stack error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0601`. +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/type/pattern_types/validity.rs b/tests/ui/type/pattern_types/validity.rs index 77a4e72f6755a..c61bb71ac252f 100644 --- a/tests/ui/type/pattern_types/validity.rs +++ b/tests/ui/type/pattern_types/validity.rs @@ -1,6 +1,8 @@ //! Check that pattern types have their validity checked +// Strip out raw byte dumps to make tests platform-independent: +//@ normalize-stderr: "([[:xdigit:]]{2}\s){4,8}\s+│\s.{4,8}" -> "HEX_DUMP" -#![feature(pattern_types)] +#![feature(pattern_types, const_trait_impl, pattern_type_range_trait)] #![feature(pattern_type_macro)] use std::pat::pattern_type; diff --git a/tests/ui/type/pattern_types/validity.stderr b/tests/ui/type/pattern_types/validity.stderr index 5bc18cfba3f7d..b990ec2d3682f 100644 --- a/tests/ui/type/pattern_types/validity.stderr +++ b/tests/ui/type/pattern_types/validity.stderr @@ -1,22 +1,22 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/validity.rs:8:1 + --> $DIR/validity.rs:10:1 | LL | const BAD: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - 00 00 00 00 │ .... + HEX_DUMP } error[E0080]: evaluation of constant value failed - --> $DIR/validity.rs:11:1 + --> $DIR/validity.rs:13:1 | LL | const BAD_UNINIT: pattern_type!(u32 is 1..) = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory error[E0080]: evaluation of constant value failed - --> $DIR/validity.rs:15:1 + --> $DIR/validity.rs:17:1 | LL | const BAD_PTR: pattern_type!(usize is 1..) = unsafe { std::mem::transmute(&42) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer @@ -25,53 +25,53 @@ LL | const BAD_PTR: pattern_type!(usize is 1..) = unsafe { std::mem::transmute(& = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: it is undefined behavior to use this value - --> $DIR/validity.rs:18:1 + --> $DIR/validity.rs:20:1 | LL | const BAD_AGGREGATE: (pattern_type!(u32 is 1..), u32) = (unsafe { std::mem::transmute(0) }, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0, but expected something greater or equal to 1 | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - 00 00 00 00 00 00 00 00 │ ........ + HEX_DUMP } error[E0080]: it is undefined behavior to use this value - --> $DIR/validity.rs:24:1 + --> $DIR/validity.rs:26:1 | LL | const BAD_FOO: Foo = Foo(Bar(unsafe { std::mem::transmute(0) })); | ^^^^^^^^^^^^^^^^^^ constructing invalid value at .0.0: encountered 0, but expected something greater or equal to 1 | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - 00 00 00 00 │ .... + HEX_DUMP } error[E0080]: evaluation of constant value failed - --> $DIR/validity.rs:27:1 + --> $DIR/validity.rs:29:1 | LL | const CHAR_UNINIT: pattern_type!(char is 'A'..'Z') = | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory error[E0080]: it is undefined behavior to use this value - --> $DIR/validity.rs:31:1 + --> $DIR/validity.rs:33:1 | LL | const CHAR_OOB_PAT: pattern_type!(char is 'A'..'Z') = unsafe { std::mem::transmute('a') }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 97, but expected something in the range 65..=89 | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - 61 00 00 00 │ a... + HEX_DUMP } error[E0080]: it is undefined behavior to use this value - --> $DIR/validity.rs:34:1 + --> $DIR/validity.rs:36:1 | LL | const CHAR_OOB: pattern_type!(char is 'A'..'Z') = unsafe { std::mem::transmute(u32::MAX) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0xffffffff, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ff ff ff ff │ .... + HEX_DUMP } error: aborting due to 8 previous errors diff --git a/tests/ui/type/type-arg-out-of-scope.rs b/tests/ui/type/type-arg-out-of-scope.rs index 3f8a6ff101652..05ed54ec832f2 100644 --- a/tests/ui/type/type-arg-out-of-scope.rs +++ b/tests/ui/type/type-arg-out-of-scope.rs @@ -1,5 +1,6 @@ -//@ error-pattern:can't use generic parameters from outer item fn foo(x: T) { fn bar(f: Box T>) { } + //~^ ERROR can't use generic parameters from outer item + //~| ERROR can't use generic parameters from outer item } fn main() { foo(1); } diff --git a/tests/ui/type/type-arg-out-of-scope.stderr b/tests/ui/type/type-arg-out-of-scope.stderr index 8665001e24319..fcaaca1770f67 100644 --- a/tests/ui/type/type-arg-out-of-scope.stderr +++ b/tests/ui/type/type-arg-out-of-scope.stderr @@ -1,5 +1,5 @@ error[E0401]: can't use generic parameters from outer item - --> $DIR/type-arg-out-of-scope.rs:3:29 + --> $DIR/type-arg-out-of-scope.rs:2:29 | LL | fn foo(x: T) { | - type parameter from outer item @@ -9,7 +9,7 @@ LL | fn bar(f: Box T>) { } | help: try introducing a local generic parameter here: `` error[E0401]: can't use generic parameters from outer item - --> $DIR/type-arg-out-of-scope.rs:3:35 + --> $DIR/type-arg-out-of-scope.rs:2:35 | LL | fn foo(x: T) { | - type parameter from outer item diff --git a/tests/ui/type/type-ascription-instead-of-statement-end.stderr b/tests/ui/type/type-ascription-instead-of-statement-end.stderr index 34759b413d89d..82b7fd23a4d9c 100644 --- a/tests/ui/type/type-ascription-instead-of-statement-end.stderr +++ b/tests/ui/type/type-ascription-instead-of-statement-end.stderr @@ -4,7 +4,6 @@ error: statements are terminated with a semicolon LL | println!("test"): | ^ | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 help: use a semicolon instead | LL - println!("test"): diff --git a/tests/ui/type/type-ascription-precedence.stderr b/tests/ui/type/type-ascription-precedence.stderr index 09cdc370309dc..f7ae612ef60fb 100644 --- a/tests/ui/type/type-ascription-precedence.stderr +++ b/tests/ui/type/type-ascription-precedence.stderr @@ -33,8 +33,6 @@ error: expected identifier, found `:` | LL | S .. S: S; | ^ expected identifier - | - = note: type ascription syntax has been removed, see issue #101728 error: expected one of `.`, `;`, `?`, `}`, or an operator, found `:` --> $DIR/type-ascription-precedence.rs:53:13 diff --git a/tests/ui/type/type-ascription-with-fn-call.stderr b/tests/ui/type/type-ascription-with-fn-call.stderr index 4222762373dcc..803c9f1c302ee 100644 --- a/tests/ui/type/type-ascription-with-fn-call.stderr +++ b/tests/ui/type/type-ascription-with-fn-call.stderr @@ -4,7 +4,6 @@ error: statements are terminated with a semicolon LL | f() : | ^ | - = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 help: use a semicolon instead | LL - f() : diff --git a/tests/ui/type/type-check/point-at-inference-4.rs b/tests/ui/type/type-check/point-at-inference-4.rs index 258374f299e24..504e6aa6db085 100644 --- a/tests/ui/type/type-check/point-at-inference-4.rs +++ b/tests/ui/type/type-check/point-at-inference-4.rs @@ -3,7 +3,6 @@ struct S(Option<(A, B)>); impl S { fn infer(&self, a: A, b: B) {} //~^ NOTE method defined here - //~| NOTE } fn main() { diff --git a/tests/ui/type/type-check/point-at-inference-4.stderr b/tests/ui/type/type-check/point-at-inference-4.stderr index adfb0cebf26ec..8630f75d11906 100644 --- a/tests/ui/type/type-check/point-at-inference-4.stderr +++ b/tests/ui/type/type-check/point-at-inference-4.stderr @@ -1,5 +1,5 @@ error[E0061]: this method takes 2 arguments but 1 argument was supplied - --> $DIR/point-at-inference-4.rs:11:7 + --> $DIR/point-at-inference-4.rs:10:7 | LL | s.infer(0i32); | ^^^^^------ argument #2 is missing @@ -15,7 +15,7 @@ LL | s.infer(0i32, /* b */); | +++++++++ error[E0308]: mismatched types - --> $DIR/point-at-inference-4.rs:18:24 + --> $DIR/point-at-inference-4.rs:17:24 | LL | s.infer(0i32); | - ---- this argument has type `i32`... diff --git a/tests/ui/type/type-mismatch-multiple.rs b/tests/ui/type/type-mismatch-multiple.rs index 55d6ceef1ded0..faae52cad233d 100644 --- a/tests/ui/type/type-mismatch-multiple.rs +++ b/tests/ui/type/type-mismatch-multiple.rs @@ -1,7 +1,9 @@ // Checking that the compiler reports multiple type errors at once +//@ dont-require-annotations: NOTE + fn main() { let a: bool = 1; let b: i32 = true; } //~^ ERROR mismatched types -//~| expected `bool`, found integer +//~| NOTE expected `bool`, found integer //~| ERROR mismatched types -//~| expected `i32`, found `bool` +//~| NOTE expected `i32`, found `bool` diff --git a/tests/ui/type/type-mismatch-multiple.stderr b/tests/ui/type/type-mismatch-multiple.stderr index 2e8654d319640..50078e5b03a0f 100644 --- a/tests/ui/type/type-mismatch-multiple.stderr +++ b/tests/ui/type/type-mismatch-multiple.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/type-mismatch-multiple.rs:3:27 + --> $DIR/type-mismatch-multiple.rs:5:27 | LL | fn main() { let a: bool = 1; let b: i32 = true; } | ---- ^ expected `bool`, found integer @@ -7,7 +7,7 @@ LL | fn main() { let a: bool = 1; let b: i32 = true; } | expected due to this error[E0308]: mismatched types - --> $DIR/type-mismatch-multiple.rs:3:43 + --> $DIR/type-mismatch-multiple.rs:5:43 | LL | fn main() { let a: bool = 1; let b: i32 = true; } | --- ^^^^ expected `i32`, found `bool` diff --git a/tests/ui/type/type-parameter-names.rs b/tests/ui/type/type-parameter-names.rs index b54a3fae0c1a1..fbd1885f543fe 100644 --- a/tests/ui/type/type-parameter-names.rs +++ b/tests/ui/type/type-parameter-names.rs @@ -1,12 +1,14 @@ // Test that we print out the names of type parameters correctly in // our error messages. +//@ dont-require-annotations: NOTE + fn foo(x: Foo) -> Bar { x //~^ ERROR mismatched types -//~| expected type parameter `Bar`, found type parameter `Foo` -//~| expected type parameter `Bar` -//~| found type parameter `Foo` +//~| NOTE expected type parameter `Bar`, found type parameter `Foo` +//~| NOTE expected type parameter `Bar` +//~| NOTE found type parameter `Foo` } fn main() {} diff --git a/tests/ui/type/type-parameter-names.stderr b/tests/ui/type/type-parameter-names.stderr index be9000a99e44f..795a260688c4b 100644 --- a/tests/ui/type/type-parameter-names.stderr +++ b/tests/ui/type/type-parameter-names.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/type-parameter-names.rs:5:5 + --> $DIR/type-parameter-names.rs:7:5 | LL | fn foo(x: Foo) -> Bar { | --- --- --- expected `Bar` because of return type diff --git a/tests/ui/type/type-params-in-different-spaces-1.rs b/tests/ui/type/type-params-in-different-spaces-1.rs index 6efd14d378532..2cde1de1c0dc1 100644 --- a/tests/ui/type/type-params-in-different-spaces-1.rs +++ b/tests/ui/type/type-params-in-different-spaces-1.rs @@ -1,11 +1,13 @@ +//@ dont-require-annotations: NOTE + use std::ops::Add; trait BrokenAdd: Copy + Add { fn broken_add(&self, rhs: T) -> Self { *self + rhs //~ ERROR mismatched types - //~| expected type parameter `Self`, found type parameter `T` - //~| expected type parameter `Self` - //~| found type parameter `T` + //~| NOTE expected type parameter `Self`, found type parameter `T` + //~| NOTE expected type parameter `Self` + //~| NOTE found type parameter `T` } } diff --git a/tests/ui/type/type-params-in-different-spaces-1.stderr b/tests/ui/type/type-params-in-different-spaces-1.stderr index 1d0e097fdc323..e9480b0e58fd0 100644 --- a/tests/ui/type/type-params-in-different-spaces-1.stderr +++ b/tests/ui/type/type-params-in-different-spaces-1.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/type-params-in-different-spaces-1.rs:5:17 + --> $DIR/type-params-in-different-spaces-1.rs:7:17 | LL | trait BrokenAdd: Copy + Add { | ---------------------------------------- expected type parameter diff --git a/tests/ui/type_length_limit.rs b/tests/ui/type_length_limit.rs index b629455aced7e..87f5ffd76d7a6 100644 --- a/tests/ui/type_length_limit.rs +++ b/tests/ui/type_length_limit.rs @@ -2,9 +2,6 @@ //@ compile-flags: -Copt-level=0 -Zenforce-type-length-limit //~^^ ERROR reached the type-length limit -// The regex below normalizes the long type file name to make it suitable for compare-modes. -//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" - // Test that the type length limit can be changed. // The exact type depends on optimizations, so disable them. diff --git a/tests/ui/type_length_limit.stderr b/tests/ui/type_length_limit.stderr index d913b661c6f07..198d133c08c88 100644 --- a/tests/ui/type_length_limit.stderr +++ b/tests/ui/type_length_limit.stderr @@ -1,11 +1,11 @@ error: reached the type-length limit while instantiating `std::mem::drop::>` - --> $DIR/type_length_limit.rs:35:5 + --> $DIR/type_length_limit.rs:32:5 | LL | drop::>(None); | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a `#![type_length_limit="4010"]` attribute to your crate - = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit.long-type.txt' error: reached the type-length limit while instantiating `<{closure@rt::lang_start<()>::{closure#0}} as FnMut<()>>::call_mut` | diff --git a/tests/ui/typeck/attempted-access-non-fatal.rs b/tests/ui/typeck/attempted-access-non-fatal.rs index 15deb9e2f609a..ba391cfa486dc 100644 --- a/tests/ui/typeck/attempted-access-non-fatal.rs +++ b/tests/ui/typeck/attempted-access-non-fatal.rs @@ -1,10 +1,10 @@ // Check that bogus field access is non-fatal fn main() { let x = 0; - let _ = x.foo; //~ `{integer}` is a primitive type and therefore doesn't have fields [E0610] - let _ = x.bar; //~ `{integer}` is a primitive type and therefore doesn't have fields [E0610] - let _ = 0.f; //~ `{integer}` is a primitive type and therefore doesn't have fields [E0610] - let _ = 2.l; //~ `{integer}` is a primitive type and therefore doesn't have fields [E0610] - let _ = 12.F; //~ `{integer}` is a primitive type and therefore doesn't have fields [E0610] - let _ = 34.L; //~ `{integer}` is a primitive type and therefore doesn't have fields [E0610] + let _ = x.foo; //~ ERROR `{integer}` is a primitive type and therefore doesn't have fields [E0610] + let _ = x.bar; //~ ERROR `{integer}` is a primitive type and therefore doesn't have fields [E0610] + let _ = 0.f; //~ ERROR `{integer}` is a primitive type and therefore doesn't have fields [E0610] + let _ = 2.l; //~ ERROR `{integer}` is a primitive type and therefore doesn't have fields [E0610] + let _ = 12.F; //~ ERROR `{integer}` is a primitive type and therefore doesn't have fields [E0610] + let _ = 34.L; //~ ERROR `{integer}` is a primitive type and therefore doesn't have fields [E0610] } diff --git a/tests/ui/typeck/autoderef-with-param-env-error.rs b/tests/ui/typeck/autoderef-with-param-env-error.rs index ec96c61c63e3a..d68f2d492ffe1 100644 --- a/tests/ui/typeck/autoderef-with-param-env-error.rs +++ b/tests/ui/typeck/autoderef-with-param-env-error.rs @@ -1,7 +1,7 @@ fn foo() where T: Send, - //~^ cannot find type `T` in this scope + //~^ ERROR cannot find type `T` in this scope { let s = "abc".to_string(); } diff --git a/tests/ui/typeck/auxiliary/suggest-trait-reexported-as-not-doc-visible-a.rs b/tests/ui/typeck/auxiliary/suggest-trait-reexported-as-not-doc-visible-a.rs new file mode 100644 index 0000000000000..87d83663626b5 --- /dev/null +++ b/tests/ui/typeck/auxiliary/suggest-trait-reexported-as-not-doc-visible-a.rs @@ -0,0 +1,5 @@ +//@ edition: 2021 + +pub trait Foo { + fn foo(); +} diff --git a/tests/ui/typeck/auxiliary/suggest-trait-reexported-as-not-doc-visible-b.rs b/tests/ui/typeck/auxiliary/suggest-trait-reexported-as-not-doc-visible-b.rs new file mode 100644 index 0000000000000..9e90e0f0b356c --- /dev/null +++ b/tests/ui/typeck/auxiliary/suggest-trait-reexported-as-not-doc-visible-b.rs @@ -0,0 +1,14 @@ +// ignore-tidy-linelength +//@ edition: 2021 +//@ aux-crate:suggest_trait_reexported_as_not_doc_visible_a=suggest-trait-reexported-as-not-doc-visible-a.rs + +pub struct Bar; + +impl __DocHidden::Foo for Bar { + fn foo() {} +} + +#[doc(hidden)] +pub mod __DocHidden { + pub use suggest_trait_reexported_as_not_doc_visible_a::Foo; +} diff --git a/tests/ui/typeck/cyclic_type_ice.stderr b/tests/ui/typeck/cyclic_type_ice.stderr index 4dc02a53c0211..645766becbf72 100644 --- a/tests/ui/typeck/cyclic_type_ice.stderr +++ b/tests/ui/typeck/cyclic_type_ice.stderr @@ -23,7 +23,7 @@ LL | let f = |_, _| (); help: provide the argument | LL - f(f); -LL + f(/* */, /* */); +LL + f(/* _ */, /* _ */); | error: aborting due to 2 previous errors diff --git a/tests/ui/typeck/gather-locals-twice.rs b/tests/ui/typeck/gather-locals-twice.rs new file mode 100644 index 0000000000000..e9351146f4564 --- /dev/null +++ b/tests/ui/typeck/gather-locals-twice.rs @@ -0,0 +1,7 @@ +// Regression test for . + +fn main() { + () += { let x; }; + //~^ ERROR binary assignment operation `+=` cannot be applied to type `()` + //~| ERROR invalid left-hand side of assignment +} diff --git a/tests/ui/typeck/gather-locals-twice.stderr b/tests/ui/typeck/gather-locals-twice.stderr new file mode 100644 index 0000000000000..7598ef8e7ea4c --- /dev/null +++ b/tests/ui/typeck/gather-locals-twice.stderr @@ -0,0 +1,20 @@ +error[E0368]: binary assignment operation `+=` cannot be applied to type `()` + --> $DIR/gather-locals-twice.rs:4:5 + | +LL | () += { let x; }; + | --^^^^^^^^^^^^^^ + | | + | cannot use `+=` on type `()` + +error[E0067]: invalid left-hand side of assignment + --> $DIR/gather-locals-twice.rs:4:8 + | +LL | () += { let x; }; + | -- ^^ + | | + | cannot assign to this expression + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0067, E0368. +For more information about an error, try `rustc --explain E0067`. diff --git a/tests/ui/typeck/issue-16338.rs b/tests/ui/typeck/issue-16338.rs index 321b3576cd61f..6c02eb93890b5 100644 --- a/tests/ui/typeck/issue-16338.rs +++ b/tests/ui/typeck/issue-16338.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + struct Slice { data: *const T, len: usize, @@ -6,5 +8,5 @@ struct Slice { fn main() { let Slice { data: data, len: len } = "foo"; //~^ ERROR mismatched types - //~| found struct `Slice<_>` + //~| NOTE found struct `Slice<_>` } diff --git a/tests/ui/typeck/issue-16338.stderr b/tests/ui/typeck/issue-16338.stderr index e24b9bfa3c041..8c5c6ad7b8cc6 100644 --- a/tests/ui/typeck/issue-16338.stderr +++ b/tests/ui/typeck/issue-16338.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-16338.rs:7:9 + --> $DIR/issue-16338.rs:9:9 | LL | let Slice { data: data, len: len } = "foo"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ----- this expression has type `&str` diff --git a/tests/ui/typeck/issue-46112.rs b/tests/ui/typeck/issue-46112.rs index cc8029771d6d4..a3fe03b17c109 100644 --- a/tests/ui/typeck/issue-46112.rs +++ b/tests/ui/typeck/issue-46112.rs @@ -6,4 +6,4 @@ extern crate xcrate_issue_46112_rexport_core; fn test(r: Result, &'static str>) { } fn main() { test(Ok(())); } -//~^ mismatched types +//~^ ERROR mismatched types diff --git a/tests/ui/typeck/issue-65611.rs b/tests/ui/typeck/issue-65611.rs index 7645311496d8c..0dae75927a86d 100644 --- a/tests/ui/typeck/issue-65611.rs +++ b/tests/ui/typeck/issue-65611.rs @@ -58,6 +58,5 @@ fn main() { let mut buffer = ArrayVec::new(); let x = buffer.last().unwrap().0.clone(); //~^ ERROR type annotations needed - //~| ERROR no field `0` on type `&_` buffer.reverse(); } diff --git a/tests/ui/typeck/issue-65611.stderr b/tests/ui/typeck/issue-65611.stderr index 2278450a6d8eb..52f0f0cffff9b 100644 --- a/tests/ui/typeck/issue-65611.stderr +++ b/tests/ui/typeck/issue-65611.stderr @@ -4,13 +4,6 @@ error[E0282]: type annotations needed LL | let x = buffer.last().unwrap().0.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` -error[E0609]: no field `0` on type `&_` - --> $DIR/issue-65611.rs:59:36 - | -LL | let x = buffer.last().unwrap().0.clone(); - | ^ unknown field - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0282, E0609. -For more information about an error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/typeck/issue-79040.rs b/tests/ui/typeck/issue-79040.rs index f8e38e7867d52..fe8dc45346186 100644 --- a/tests/ui/typeck/issue-79040.rs +++ b/tests/ui/typeck/issue-79040.rs @@ -1,6 +1,6 @@ fn main() { const FOO = "hello" + 1; //~^ ERROR cannot add `{integer}` to `&str` - //~| missing type for `const` item + //~| ERROR missing type for `const` item println!("{}", FOO); } diff --git a/tests/ui/typeck/issue-83693.rs b/tests/ui/typeck/issue-83693.rs index 02a0bb30d7ce1..68a1416517a73 100644 --- a/tests/ui/typeck/issue-83693.rs +++ b/tests/ui/typeck/issue-83693.rs @@ -8,7 +8,7 @@ impl F { fn call() { ::call //~^ ERROR: cannot find type `TestResult` in this scope [E0412] - //~| associated item constraints are not allowed here [E0229] + //~| ERROR associated item constraints are not allowed here [E0229] } } diff --git a/tests/ui/typeck/issue-84768.stderr b/tests/ui/typeck/issue-84768.stderr index 72784ba59c9b4..8a2200cbf684b 100644 --- a/tests/ui/typeck/issue-84768.stderr +++ b/tests/ui/typeck/issue-84768.stderr @@ -20,7 +20,7 @@ help: the return type of this call is `{integer}` due to the type of the argumen LL | ::call_once(f, 1) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^ | | - | this argument influences the return type of `FnOnce` + | this argument influences the return type of `call_once` note: method defined here --> $SRC_DIR/core/src/ops/function.rs:LL:COL diff --git a/tests/ui/typeck/issue-90027-async-fn-return-suggestion.rs b/tests/ui/typeck/issue-90027-async-fn-return-suggestion.rs index 0be1237749fcc..dd833957a70ae 100644 --- a/tests/ui/typeck/issue-90027-async-fn-return-suggestion.rs +++ b/tests/ui/typeck/issue-90027-async-fn-return-suggestion.rs @@ -1,4 +1,5 @@ //@ edition:2018 +//@ dont-require-annotations: SUGGESTION async fn hello() { //~ HELP try adding a return type 0 diff --git a/tests/ui/typeck/issue-90027-async-fn-return-suggestion.stderr b/tests/ui/typeck/issue-90027-async-fn-return-suggestion.stderr index c46f4ec1ec301..3680df25f0ba5 100644 --- a/tests/ui/typeck/issue-90027-async-fn-return-suggestion.stderr +++ b/tests/ui/typeck/issue-90027-async-fn-return-suggestion.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-90027-async-fn-return-suggestion.rs:4:5 + --> $DIR/issue-90027-async-fn-return-suggestion.rs:5:5 | LL | async fn hello() { | - help: try adding a return type: `-> i32` @@ -7,7 +7,7 @@ LL | 0 | ^ expected `()`, found integer error[E0308]: mismatched types - --> $DIR/issue-90027-async-fn-return-suggestion.rs:9:5 + --> $DIR/issue-90027-async-fn-return-suggestion.rs:10:5 | LL | async fn world() -> () { | -- expected `()` because of return type @@ -15,13 +15,13 @@ LL | 0 | ^ expected `()`, found integer error[E0308]: mismatched types - --> $DIR/issue-90027-async-fn-return-suggestion.rs:14:5 + --> $DIR/issue-90027-async-fn-return-suggestion.rs:15:5 | LL | hello() | ^^^^^^^ expected `()`, found future | note: calling an async function returns a future - --> $DIR/issue-90027-async-fn-return-suggestion.rs:14:5 + --> $DIR/issue-90027-async-fn-return-suggestion.rs:15:5 | LL | hello() | ^^^^^^^ diff --git a/tests/ui/typeck/issue-91334.rs b/tests/ui/typeck/issue-91334.rs index ec0d4ad70f15e..c820014ff937b 100644 --- a/tests/ui/typeck/issue-91334.rs +++ b/tests/ui/typeck/issue-91334.rs @@ -1,7 +1,7 @@ // Regression test for the ICE described in issue #91334. -//@ error-pattern: this file contains an unclosed delimiter - #![feature(coroutines)] +//~vv ERROR mismatched closing delimiter: `)` +//~v ERROR this file contains an unclosed delimiter fn f(){||yield(((){), diff --git a/tests/ui/typeck/minus-string.rs b/tests/ui/typeck/minus-string.rs new file mode 100644 index 0000000000000..1c0f73a37132c --- /dev/null +++ b/tests/ui/typeck/minus-string.rs @@ -0,0 +1,7 @@ +// Regression test for issue #813. +// This ensures that the unary negation operator `-` cannot be applied to an owned `String`. +// Previously, due to a type-checking bug, this was mistakenly accepted by the compiler. + +fn main() { + -"foo".to_string(); //~ ERROR cannot apply unary operator `-` to type `String` +} diff --git a/tests/ui/typeck/minus-string.stderr b/tests/ui/typeck/minus-string.stderr new file mode 100644 index 0000000000000..d2ebcd01ff9c1 --- /dev/null +++ b/tests/ui/typeck/minus-string.stderr @@ -0,0 +1,14 @@ +error[E0600]: cannot apply unary operator `-` to type `String` + --> $DIR/minus-string.rs:6:5 + | +LL | -"foo".to_string(); + | ^^^^^^^^^^^^^^^^^^ cannot apply unary operator `-` + | +note: the foreign item type `String` doesn't implement `Neg` + --> $SRC_DIR/alloc/src/string.rs:LL:COL + | + = note: not implement `Neg` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0600`. diff --git a/tests/ui/typeck/no-type-for-node-ice.rs b/tests/ui/typeck/no-type-for-node-ice.rs index d0cfdbf504dea..ed56272e63cc9 100644 --- a/tests/ui/typeck/no-type-for-node-ice.rs +++ b/tests/ui/typeck/no-type-for-node-ice.rs @@ -1,5 +1,5 @@ // Related issues: #20401, #20506, #20614, #20752, #20829, #20846, #20885, #20886 fn main() { - "".homura[""]; //~ no field `homura` on type `&'static str` + "".homura[""]; //~ ERROR no field `homura` on type `&'static str` } diff --git a/tests/ui/typeck/output-type-mismatch.rs b/tests/ui/typeck/output-type-mismatch.rs index d5691c9c3535d..0d7c7d1dfe178 100644 --- a/tests/ui/typeck/output-type-mismatch.rs +++ b/tests/ui/typeck/output-type-mismatch.rs @@ -1,5 +1,3 @@ -//@ error-pattern: mismatched types - fn f() { } -fn main() { let i: isize; i = f(); } +fn main() { let i: isize; i = f(); } //~ ERROR mismatched types diff --git a/tests/ui/typeck/output-type-mismatch.stderr b/tests/ui/typeck/output-type-mismatch.stderr index c6df6650654a6..ba50cd4e6d751 100644 --- a/tests/ui/typeck/output-type-mismatch.stderr +++ b/tests/ui/typeck/output-type-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/output-type-mismatch.rs:5:31 + --> $DIR/output-type-mismatch.rs:3:31 | LL | fn main() { let i: isize; i = f(); } | ----- ^^^ expected `isize`, found `()` diff --git a/tests/ui/typeck/pointer-arith-assign.fixed b/tests/ui/typeck/pointer-arith-assign.fixed new file mode 100644 index 0000000000000..907208579e7c6 --- /dev/null +++ b/tests/ui/typeck/pointer-arith-assign.fixed @@ -0,0 +1,20 @@ +//@ run-rustfix +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_assignments)] + +fn test_add_assign_raw_pointer() { + let mut arr = [0u8; 10]; + let mut _ptr = arr.as_mut_ptr(); + + _ptr = _ptr.wrapping_add(2); //~ ERROR binary assignment operation `+=` cannot be applied to type `*mut u8` [E0368] +} + +fn test_sub_assign_raw_pointer() { + let mut arr = [0u8; 10]; + let mut _ptr = arr.as_mut_ptr(); + + _ptr = _ptr.wrapping_sub(2); //~ ERROR binary assignment operation `-=` cannot be applied to type `*mut u8` [E0368] +} + +fn main() {} diff --git a/tests/ui/typeck/pointer-arith-assign.rs b/tests/ui/typeck/pointer-arith-assign.rs new file mode 100644 index 0000000000000..0f4ef6ab74c0c --- /dev/null +++ b/tests/ui/typeck/pointer-arith-assign.rs @@ -0,0 +1,20 @@ +//@ run-rustfix +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_assignments)] + +fn test_add_assign_raw_pointer() { + let mut arr = [0u8; 10]; + let mut _ptr = arr.as_mut_ptr(); + + _ptr += 2; //~ ERROR binary assignment operation `+=` cannot be applied to type `*mut u8` [E0368] +} + +fn test_sub_assign_raw_pointer() { + let mut arr = [0u8; 10]; + let mut _ptr = arr.as_mut_ptr(); + + _ptr -= 2; //~ ERROR binary assignment operation `-=` cannot be applied to type `*mut u8` [E0368] +} + +fn main() {} diff --git a/tests/ui/typeck/pointer-arith-assign.stderr b/tests/ui/typeck/pointer-arith-assign.stderr new file mode 100644 index 0000000000000..a2bebe5e24757 --- /dev/null +++ b/tests/ui/typeck/pointer-arith-assign.stderr @@ -0,0 +1,31 @@ +error[E0368]: binary assignment operation `+=` cannot be applied to type `*mut u8` + --> $DIR/pointer-arith-assign.rs:10:5 + | +LL | _ptr += 2; + | ----^^^^^ + | | + | cannot use `+=` on type `*mut u8` + | +help: consider using `add` or `wrapping_add` to do pointer arithmetic + | +LL - _ptr += 2; +LL + _ptr = _ptr.wrapping_add(2); + | + +error[E0368]: binary assignment operation `-=` cannot be applied to type `*mut u8` + --> $DIR/pointer-arith-assign.rs:17:5 + | +LL | _ptr -= 2; + | ----^^^^^ + | | + | cannot use `-=` on type `*mut u8` + | +help: consider using `sub` or `wrapping_sub` to do pointer arithmetic + | +LL - _ptr -= 2; +LL + _ptr = _ptr.wrapping_sub(2); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0368`. diff --git a/tests/ui/typeck/suggest-trait-reexported-as-not-doc-visible.rs b/tests/ui/typeck/suggest-trait-reexported-as-not-doc-visible.rs new file mode 100644 index 0000000000000..3cb775e85ac6b --- /dev/null +++ b/tests/ui/typeck/suggest-trait-reexported-as-not-doc-visible.rs @@ -0,0 +1,11 @@ +// ignore-tidy-linelength +//@ edition: 2021 +//@ aux-crate:suggest_trait_reexported_as_not_doc_visible_a=suggest-trait-reexported-as-not-doc-visible-a.rs +//@ aux-crate:suggest_trait_reexported_as_not_doc_visible_b=suggest-trait-reexported-as-not-doc-visible-b.rs + +use suggest_trait_reexported_as_not_doc_visible_b::Bar; + +fn main() { + Bar::foo(); + //~^ ERROR: no function or associated item named `foo` found for struct `Bar` in the current scope [E0599] +} diff --git a/tests/ui/typeck/suggest-trait-reexported-as-not-doc-visible.stderr b/tests/ui/typeck/suggest-trait-reexported-as-not-doc-visible.stderr new file mode 100644 index 0000000000000..9128ee6844469 --- /dev/null +++ b/tests/ui/typeck/suggest-trait-reexported-as-not-doc-visible.stderr @@ -0,0 +1,15 @@ +error[E0599]: no function or associated item named `foo` found for struct `Bar` in the current scope + --> $DIR/suggest-trait-reexported-as-not-doc-visible.rs:9:10 + | +LL | Bar::foo(); + | ^^^ function or associated item not found in `Bar` + | + = help: items from traits can only be used if the trait is in scope +help: trait `Foo` which provides `foo` is implemented but not in scope; perhaps you want to import it + | +LL + use suggest_trait_reexported_as_not_doc_visible_a::Foo; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.rs b/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.rs new file mode 100644 index 0000000000000..d71a7ff1d3d4d --- /dev/null +++ b/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.rs @@ -0,0 +1,56 @@ +trait Reencode { + type Error; + fn tag_index(&mut self, tag: u32) -> Result; +} + +struct Reencoder; +impl Reencode for Reencoder { + type Error = &'static str; + fn tag_index(&mut self, tag: u32) -> Result { + Ok(tag) + } +} + + +enum Operator { + Suspend { tag_index: u32 }, +} + +enum Instruction { + Suspend { tag_index: u32 }, +} + + +macro_rules! for_each_operator { + ($m:ident) => { + $m! { + Suspend { tag_index: u32 } => visit_suspend + } + }; +} + + +fn process(op: &Operator, reencoder: &mut T) -> Instruction { + macro_rules! translate { + (Suspend { tag_index: $ty:ty } => $visit:ident) => { + match op { + Operator::Suspend { tag_index } => { + let tag_index = reencoder.tag_index(*tag_index); + + // KEY POINT: Using field shorthand syntax where the compiler gets confused + // Here tag_index is a Result but we're using it where u32 is expected + Instruction::Suspend { tag_index } //~ ERROR mismatched types [E0308] + } + } + }; + } + + for_each_operator!(translate) +} + +fn main() { + let mut reencoder = Reencoder; + let op = Operator::Suspend { tag_index: 1 }; + + let _ = process(&op, &mut reencoder); +} diff --git a/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.stderr b/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.stderr new file mode 100644 index 0000000000000..12537754d8029 --- /dev/null +++ b/tests/ui/typeck/suggestions/macro-shorthand-issue-140659.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/macro-shorthand-issue-140659.rs:42:44 + | +LL | Instruction::Suspend { tag_index } + | ^^^^^^^^^ expected `u32`, found `Result::Error>` +... +LL | for_each_operator!(translate) + | ----------------------------- in this macro invocation + | + = note: expected type `u32` + found enum `Result::Error>` + = note: this error originates in the macro `translate` which comes from the expansion of the macro `for_each_operator` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed b/tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.fixed similarity index 100% rename from tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed rename to tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.fixed diff --git a/tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs b/tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.rs similarity index 100% rename from tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs rename to tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.rs diff --git a/tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr b/tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.stderr similarity index 100% rename from tests/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr rename to tests/ui/typeck/suggestions/suggest-adding-missing-zero-to-floating-point-number.stderr diff --git a/tests/ui/typeck/suggest-arg-comma-delete-ice.rs b/tests/ui/typeck/suggestions/suggest-arg-comma-delete-ice.rs similarity index 100% rename from tests/ui/typeck/suggest-arg-comma-delete-ice.rs rename to tests/ui/typeck/suggestions/suggest-arg-comma-delete-ice.rs diff --git a/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr b/tests/ui/typeck/suggestions/suggest-arg-comma-delete-ice.stderr similarity index 100% rename from tests/ui/typeck/suggest-arg-comma-delete-ice.stderr rename to tests/ui/typeck/suggestions/suggest-arg-comma-delete-ice.stderr diff --git a/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.fixed b/tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.fixed similarity index 100% rename from tests/ui/typeck/suggest-box-on-divergent-if-else-arms.fixed rename to tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.fixed diff --git a/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.rs b/tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.rs similarity index 100% rename from tests/ui/typeck/suggest-box-on-divergent-if-else-arms.rs rename to tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.rs diff --git a/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.stderr b/tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.stderr similarity index 100% rename from tests/ui/typeck/suggest-box-on-divergent-if-else-arms.stderr rename to tests/ui/typeck/suggestions/suggest-box-on-divergent-if-else-arms.stderr diff --git a/tests/ui/typeck/suggest-similar-impls-for-root-obligation.rs b/tests/ui/typeck/suggestions/suggest-similar-impls-for-root-obligation.rs similarity index 100% rename from tests/ui/typeck/suggest-similar-impls-for-root-obligation.rs rename to tests/ui/typeck/suggestions/suggest-similar-impls-for-root-obligation.rs diff --git a/tests/ui/typeck/suggest-similar-impls-for-root-obligation.stderr b/tests/ui/typeck/suggestions/suggest-similar-impls-for-root-obligation.stderr similarity index 100% rename from tests/ui/typeck/suggest-similar-impls-for-root-obligation.stderr rename to tests/ui/typeck/suggestions/suggest-similar-impls-for-root-obligation.stderr diff --git a/tests/ui/typeck/suppressed-error.rs b/tests/ui/typeck/suppressed-error.rs index 1e39be4608091..b7e5b22e9db13 100644 --- a/tests/ui/typeck/suppressed-error.rs +++ b/tests/ui/typeck/suppressed-error.rs @@ -1,8 +1,9 @@ fn main() { let (x, y) = (); //~^ ERROR mismatched types -//~| expected unit type `()` -//~| found tuple `(_, _)` -//~| expected `()`, found +//~| NOTE expected unit type `()` +//~| NOTE found tuple `(_, _)` +//~| NOTE expected `()`, found +//~| NOTE this expression has type `()` return x; } diff --git a/tests/ui/typeck/tag-that-dare-not-speak-its-name.rs b/tests/ui/typeck/tag-that-dare-not-speak-its-name.rs index 0e76ec246d7c3..79aa1f27a78bc 100644 --- a/tests/ui/typeck/tag-that-dare-not-speak-its-name.rs +++ b/tests/ui/typeck/tag-that-dare-not-speak-its-name.rs @@ -10,7 +10,8 @@ fn main() { let y; let x : char = last(y); //~^ ERROR mismatched types - //~| expected type `char` - //~| found enum `Option<_>` - //~| expected `char`, found `Option<_>` + //~| NOTE expected type `char` + //~| NOTE found enum `Option<_>` + //~| NOTE expected `char`, found `Option<_>` + //~| NOTE expected due to this } diff --git a/tests/ui/typeck/terr-in-field.rs b/tests/ui/typeck/terr-in-field.rs index cfe350ef86db2..1b8f76f40cbe6 100644 --- a/tests/ui/typeck/terr-in-field.rs +++ b/tests/ui/typeck/terr-in-field.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + struct Foo { a: isize, b: isize, @@ -11,7 +13,7 @@ struct Bar { fn want_foo(f: Foo) {} fn have_bar(b: Bar) { want_foo(b); //~ ERROR mismatched types - //~| expected `Foo`, found `Bar` + //~| NOTE expected `Foo`, found `Bar` } fn main() {} diff --git a/tests/ui/typeck/terr-in-field.stderr b/tests/ui/typeck/terr-in-field.stderr index adc336db50164..37e55af84f7a7 100644 --- a/tests/ui/typeck/terr-in-field.stderr +++ b/tests/ui/typeck/terr-in-field.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/terr-in-field.rs:13:14 + --> $DIR/terr-in-field.rs:15:14 | LL | want_foo(b); | -------- ^ expected `Foo`, found `Bar` @@ -7,7 +7,7 @@ LL | want_foo(b); | arguments to this function are incorrect | note: function defined here - --> $DIR/terr-in-field.rs:11:4 + --> $DIR/terr-in-field.rs:13:4 | LL | fn want_foo(f: Foo) {} | ^^^^^^^^ ------ diff --git a/tests/ui/typeck/terr-sorts.rs b/tests/ui/typeck/terr-sorts.rs index c1e2f7daee5ec..439d27162f837 100644 --- a/tests/ui/typeck/terr-sorts.rs +++ b/tests/ui/typeck/terr-sorts.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + struct Foo { a: isize, b: isize, @@ -8,8 +10,8 @@ type Bar = Box; fn want_foo(f: Foo) {} fn have_bar(b: Bar) { want_foo(b); //~ ERROR mismatched types - //~| expected struct `Foo` - //~| found struct `Box` + //~| NOTE expected struct `Foo` + //~| NOTE found struct `Box` } fn main() {} diff --git a/tests/ui/typeck/terr-sorts.stderr b/tests/ui/typeck/terr-sorts.stderr index 59d9392c236ec..0f649f2f03575 100644 --- a/tests/ui/typeck/terr-sorts.stderr +++ b/tests/ui/typeck/terr-sorts.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/terr-sorts.rs:10:14 + --> $DIR/terr-sorts.rs:12:14 | LL | want_foo(b); | -------- ^ expected `Foo`, found `Box` @@ -9,7 +9,7 @@ LL | want_foo(b); = note: expected struct `Foo` found struct `Box` note: function defined here - --> $DIR/terr-sorts.rs:8:4 + --> $DIR/terr-sorts.rs:10:4 | LL | fn want_foo(f: Foo) {} | ^^^^^^^^ ------ diff --git a/tests/ui/typeck/typeck_type_placeholder_item.rs b/tests/ui/typeck/typeck_type_placeholder_item.rs index 9f1bfd7909e38..d7351f2e51a8d 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.rs +++ b/tests/ui/typeck/typeck_type_placeholder_item.rs @@ -181,6 +181,7 @@ trait Trait {} impl Trait for Struct {} type Y = impl Trait<_>; //~^ ERROR the placeholder `_` is not allowed within types on item signatures for type aliases +#[define_opaque(Y)] fn foo() -> Y { Struct } diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index c5bf9a47e9134..7184244f5dc9d 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/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:206:5 + --> $DIR/typeck_type_placeholder_item.rs:207:5 | LL | const C: _; | ^^^^^^^^^^- @@ -439,6 +439,18 @@ LL | fn fn_test13(x: _) -> (i32, _) { (x, x) } | | not allowed in type signatures | help: replace with the correct return type: `(i32, i32)` +error[E0121]: the placeholder `_` is not allowed within types on item signatures for structs + --> $DIR/typeck_type_placeholder_item.rs:154:21 + | +LL | struct BadStruct<_>(_); + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL - struct BadStruct<_>(_); +LL + struct BadStruct(T); + | + error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:140:31 | @@ -515,18 +527,6 @@ LL - fn assoc_fn_test3() -> _; LL + fn assoc_fn_test3() -> T; | -error[E0121]: the placeholder `_` is not allowed within types on item signatures for structs - --> $DIR/typeck_type_placeholder_item.rs:154:21 - | -LL | struct BadStruct<_>(_); - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL - struct BadStruct<_>(_); -LL + struct BadStruct(T); - | - error[E0121]: the placeholder `_` is not allowed within types on item signatures for implementations --> $DIR/typeck_type_placeholder_item.rs:159:15 | @@ -578,13 +578,13 @@ LL | type Y = impl Trait<_>; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants - --> $DIR/typeck_type_placeholder_item.rs:206:14 + --> $DIR/typeck_type_placeholder_item.rs:207:14 | LL | const C: _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants - --> $DIR/typeck_type_placeholder_item.rs:194:14 + --> $DIR/typeck_type_placeholder_item.rs:195:14 | LL | const D: _ = 42; | ^ not allowed in type signatures @@ -596,13 +596,13 @@ LL + const D: i32 = 42; | error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants - --> $DIR/typeck_type_placeholder_item.rs:209:14 + --> $DIR/typeck_type_placeholder_item.rs:210:14 | LL | const D: _ = 42; | ^ not allowed in type signatures error[E0046]: not all trait items implemented, missing: `F` - --> $DIR/typeck_type_placeholder_item.rs:200:1 + --> $DIR/typeck_type_placeholder_item.rs:201:1 | LL | type F: std::ops::Fn(_); | ----------------------- `F` from trait @@ -611,7 +611,7 @@ LL | impl Qux for Struct { | ^^^^^^^^^^^^^^^^^^^ missing `F` in implementation error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/typeck_type_placeholder_item.rs:217:31 + --> $DIR/typeck_type_placeholder_item.rs:218:31 | LL | fn value() -> Option<&'static _> { | ----------------^- @@ -620,7 +620,7 @@ LL | fn value() -> Option<&'static _> { | help: replace with the correct return type: `Option<&'static u8>` error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/typeck_type_placeholder_item.rs:222:17 + --> $DIR/typeck_type_placeholder_item.rs:223:17 | LL | const _: Option<_> = map(value); | ^ not allowed in type signatures @@ -632,7 +632,7 @@ LL + const _: Option = map(value); | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/typeck_type_placeholder_item.rs:226:31 + --> $DIR/typeck_type_placeholder_item.rs:227:31 | LL | fn evens_squared(n: usize) -> _ { | ^ @@ -641,13 +641,13 @@ LL | fn evens_squared(n: usize) -> _ { | help: replace with an appropriate return type: `impl Iterator` error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/typeck_type_placeholder_item.rs:231:10 + --> $DIR/typeck_type_placeholder_item.rs:232:10 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^ not allowed in type signatures | -note: however, the inferred type `Map, {closure@typeck_type_placeholder_item.rs:231:29}>, {closure@typeck_type_placeholder_item.rs:231:49}>` cannot be named - --> $DIR/typeck_type_placeholder_item.rs:231:14 +note: however, the inferred type `Map, {closure@typeck_type_placeholder_item.rs:232:29}>, {closure@typeck_type_placeholder_item.rs:232:49}>` cannot be named + --> $DIR/typeck_type_placeholder_item.rs:232:14 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -695,53 +695,53 @@ LL + fn fn_test10(&self, _x : T) { } | error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types - --> $DIR/typeck_type_placeholder_item.rs:202:14 + --> $DIR/typeck_type_placeholder_item.rs:203:14 | LL | type A = _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types - --> $DIR/typeck_type_placeholder_item.rs:204:14 + --> $DIR/typeck_type_placeholder_item.rs:205:14 | LL | type B = _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types - --> $DIR/typeck_type_placeholder_item.rs:190:14 + --> $DIR/typeck_type_placeholder_item.rs:191:14 | LL | type B = _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants - --> $DIR/typeck_type_placeholder_item.rs:192:14 + --> $DIR/typeck_type_placeholder_item.rs:193:14 | LL | const C: _; | ^ not allowed in type signatures error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types - --> $DIR/typeck_type_placeholder_item.rs:197:26 + --> $DIR/typeck_type_placeholder_item.rs:198:26 | LL | type F: std::ops::Fn(_); | ^ not allowed in type signatures error[E0015]: cannot call non-const function `map::` in constants - --> $DIR/typeck_type_placeholder_item.rs:222:22 + --> $DIR/typeck_type_placeholder_item.rs:223:22 | LL | const _: Option<_> = map(value); | ^^^^^^^^^^ | = note: calls in constants are limited to constant functions, tuple structs and tuple variants -error[E0015]: cannot call non-const method ` as Iterator>::filter::<{closure@$DIR/typeck_type_placeholder_item.rs:231:29: 231:32}>` in constants - --> $DIR/typeck_type_placeholder_item.rs:231:22 +error[E0015]: cannot call non-const method ` as Iterator>::filter::<{closure@$DIR/typeck_type_placeholder_item.rs:232:29: 232:32}>` in constants + --> $DIR/typeck_type_placeholder_item.rs:232:22 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^^^^^^^^^ | = note: calls in constants are limited to constant functions, tuple structs and tuple variants -error[E0015]: cannot call non-const method `, {closure@$DIR/typeck_type_placeholder_item.rs:231:29: 231:32}> as Iterator>::map::` in constants - --> $DIR/typeck_type_placeholder_item.rs:231:45 +error[E0015]: cannot call non-const method `, {closure@$DIR/typeck_type_placeholder_item.rs:232:29: 232:32}> as Iterator>::map::` in constants + --> $DIR/typeck_type_placeholder_item.rs:232:45 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/typeck/typeck_type_placeholder_mismatch.rs b/tests/ui/typeck/typeck_type_placeholder_mismatch.rs index 718b6deed1bb1..cedda4a636e6a 100644 --- a/tests/ui/typeck/typeck_type_placeholder_mismatch.rs +++ b/tests/ui/typeck/typeck_type_placeholder_mismatch.rs @@ -1,6 +1,8 @@ // This test checks that genuine type errors with partial // type hints are understandable. +//@ dont-require-annotations: NOTE + use std::marker::PhantomData; struct Foo(PhantomData); @@ -12,16 +14,16 @@ pub fn main() { fn test1() { let x: Foo<_> = Bar::(PhantomData); //~^ ERROR mismatched types - //~| expected struct `Foo<_>` - //~| found struct `Bar` - //~| expected `Foo<_>`, found `Bar` + //~| NOTE expected struct `Foo<_>` + //~| NOTE found struct `Bar` + //~| NOTE expected `Foo<_>`, found `Bar` let y: Foo = x; } fn test2() { let x: Foo<_> = Bar::(PhantomData); //~^ ERROR mismatched types - //~| expected struct `Foo<_>` - //~| found struct `Bar` - //~| expected `Foo<_>`, found `Bar` + //~| NOTE expected struct `Foo<_>` + //~| NOTE found struct `Bar` + //~| NOTE expected `Foo<_>`, found `Bar` } diff --git a/tests/ui/typeck/typeck_type_placeholder_mismatch.stderr b/tests/ui/typeck/typeck_type_placeholder_mismatch.stderr index bf8e0bbb519c4..c6e42ececd23f 100644 --- a/tests/ui/typeck/typeck_type_placeholder_mismatch.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/typeck_type_placeholder_mismatch.rs:13:21 + --> $DIR/typeck_type_placeholder_mismatch.rs:15:21 | LL | let x: Foo<_> = Bar::(PhantomData); | ------ ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Foo<_>`, found `Bar` @@ -10,7 +10,7 @@ LL | let x: Foo<_> = Bar::(PhantomData); found struct `Bar` error[E0308]: mismatched types - --> $DIR/typeck_type_placeholder_mismatch.rs:22:21 + --> $DIR/typeck_type_placeholder_mismatch.rs:24:21 | LL | let x: Foo<_> = Bar::(PhantomData); | ------ ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Foo<_>`, found `Bar` diff --git a/tests/ui/typeck/while-type-error.rs b/tests/ui/typeck/while-type-error.rs index ca3b8921f5400..abc607516e60d 100644 --- a/tests/ui/typeck/while-type-error.rs +++ b/tests/ui/typeck/while-type-error.rs @@ -1,3 +1 @@ -//@ error-pattern: mismatched types - -fn main() { while main { } } +fn main() { while main { } } //~ ERROR mismatched types diff --git a/tests/ui/typeck/while-type-error.stderr b/tests/ui/typeck/while-type-error.stderr index b67ec561531fe..4bd86aafc1770 100644 --- a/tests/ui/typeck/while-type-error.stderr +++ b/tests/ui/typeck/while-type-error.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/while-type-error.rs:3:19 + --> $DIR/while-type-error.rs:1:19 | LL | fn main() { while main { } } | ^^^^ expected `bool`, found fn item diff --git a/tests/ui/typeck/wrong-ret-type.rs b/tests/ui/typeck/wrong-ret-type.rs index b83aefad1e9f6..2d6751a823cfd 100644 --- a/tests/ui/typeck/wrong-ret-type.rs +++ b/tests/ui/typeck/wrong-ret-type.rs @@ -1,3 +1,2 @@ -//@ error-pattern: mismatched types -fn mk_int() -> usize { let i: isize = 3; return i; } +fn mk_int() -> usize { let i: isize = 3; return i; } //~ ERROR mismatched types fn main() { } diff --git a/tests/ui/typeck/wrong-ret-type.stderr b/tests/ui/typeck/wrong-ret-type.stderr index 33a094ce95d30..918eb2d5c8898 100644 --- a/tests/ui/typeck/wrong-ret-type.stderr +++ b/tests/ui/typeck/wrong-ret-type.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/wrong-ret-type.rs:2:49 + --> $DIR/wrong-ret-type.rs:1:49 | LL | fn mk_int() -> usize { let i: isize = 3; return i; } | ----- ^ expected `usize`, found `isize` diff --git a/tests/ui/typeof/type_mismatch.rs b/tests/ui/typeof/type_mismatch.rs index 3f8339fa5beaf..a3444d6c2078c 100644 --- a/tests/ui/typeof/type_mismatch.rs +++ b/tests/ui/typeof/type_mismatch.rs @@ -1,9 +1,12 @@ // Test that using typeof results in the correct type mismatch errors instead of always assuming // `usize`, in addition to the pre-existing "typeof is reserved and unimplemented" error + +//@ dont-require-annotations: NOTE + fn main() { const a: u8 = 1; let b: typeof(a) = 1i8; //~^ ERROR `typeof` is a reserved keyword but unimplemented //~| ERROR mismatched types - //~| expected `u8`, found `i8` + //~| NOTE expected `u8`, found `i8` } diff --git a/tests/ui/typeof/type_mismatch.stderr b/tests/ui/typeof/type_mismatch.stderr index d5494922b1661..6a414c11f384c 100644 --- a/tests/ui/typeof/type_mismatch.stderr +++ b/tests/ui/typeof/type_mismatch.stderr @@ -1,5 +1,5 @@ error[E0516]: `typeof` is a reserved keyword but unimplemented - --> $DIR/type_mismatch.rs:5:12 + --> $DIR/type_mismatch.rs:8:12 | LL | let b: typeof(a) = 1i8; | ^^^^^^^^^ reserved keyword @@ -11,7 +11,7 @@ LL + let b: u8 = 1i8; | error[E0308]: mismatched types - --> $DIR/type_mismatch.rs:5:24 + --> $DIR/type_mismatch.rs:8:24 | LL | let b: typeof(a) = 1i8; | --------- ^^^ expected `u8`, found `i8` diff --git a/tests/ui/ufcs/ufcs-explicit-self-bad.rs b/tests/ui/ufcs/ufcs-explicit-self-bad.rs index 29586dccfc950..7039ab30428f2 100644 --- a/tests/ui/ufcs/ufcs-explicit-self-bad.rs +++ b/tests/ui/ufcs/ufcs-explicit-self-bad.rs @@ -1,13 +1,13 @@ +//@ dont-require-annotations: NOTE + struct Foo { f: isize, } - - impl Foo { fn foo(self: isize, x: isize) -> isize { //~^ ERROR invalid `self` parameter type - self.f + x //~ ERROR: doesn't have fields + self.f + x //~ ERROR doesn't have fields } } @@ -39,13 +39,13 @@ impl<'a, T> SomeTrait for &'a Bar { //~| ERROR has an incompatible type for trait fn dummy3(self: &&Bar) {} //~^ ERROR mismatched `self` parameter type - //~| expected reference `&'a Bar<_>` - //~| found reference `&Bar<_>` - //~| lifetime mismatch + //~| NOTE expected reference `&'a Bar<_>` + //~| NOTE found reference `&Bar<_>` + //~| NOTE lifetime mismatch //~| ERROR mismatched `self` parameter type - //~| expected reference `&'a Bar<_>` - //~| found reference `&Bar<_>` - //~| lifetime mismatch + //~| NOTE expected reference `&'a Bar<_>` + //~| NOTE found reference `&Bar<_>` + //~| NOTE lifetime mismatch } fn main() { diff --git a/tests/ui/ufcs/ufcs-qpath-self-mismatch.stderr b/tests/ui/ufcs/ufcs-qpath-self-mismatch.stderr index 0507183488391..edee7c4f5a15b 100644 --- a/tests/ui/ufcs/ufcs-qpath-self-mismatch.stderr +++ b/tests/ui/ufcs/ufcs-qpath-self-mismatch.stderr @@ -25,7 +25,7 @@ help: the return type of this call is `u32` due to the type of the argument pass LL | >::add(1u32, 2); | ^^^^^^^^^^^^^^^^^^^^^^^----^^^^ | | - | this argument influences the return type of `Add` + | this argument influences the return type of `add` note: method defined here --> $SRC_DIR/core/src/ops/arith.rs:LL:COL help: change the type of the numeric literal from `u32` to `i32` @@ -48,7 +48,7 @@ help: the return type of this call is `u32` due to the type of the argument pass LL | >::add(1, 2u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^----^ | | - | this argument influences the return type of `Add` + | this argument influences the return type of `add` note: method defined here --> $SRC_DIR/core/src/ops/arith.rs:LL:COL help: change the type of the numeric literal from `u32` to `i32` diff --git a/tests/ui/unboxed-closures/arg-constrained-after-closure-inference.rs b/tests/ui/unboxed-closures/arg-constrained-after-closure-inference.rs new file mode 100644 index 0000000000000..343a27616d1d2 --- /dev/null +++ b/tests/ui/unboxed-closures/arg-constrained-after-closure-inference.rs @@ -0,0 +1,16 @@ +#![feature(unboxed_closures)] + +//@ check-pass + +// Regression test for #131758. We only know the type of `x` after closure upvar +// inference is done, even if we don't need to structurally resolve the type of `x`. + +trait Foo {} + +impl> Foo for T {} + +fn baz(_: T) {} + +fn main() { + baz(|x| ()); +} diff --git a/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.rs b/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.rs index 6765da42132b7..c57e665724b42 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.rs +++ b/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.rs @@ -15,6 +15,6 @@ fn doit(val: T, f: &F) pub fn main() { doit(0, &|x, y| { x.set(y); - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough }); } diff --git a/tests/ui/uninhabited/auxiliary/staged-api.rs b/tests/ui/uninhabited/auxiliary/staged-api.rs new file mode 100644 index 0000000000000..342ecf020ea5e --- /dev/null +++ b/tests/ui/uninhabited/auxiliary/staged-api.rs @@ -0,0 +1,8 @@ +#![feature(staged_api)] +#![stable(feature = "stable", since = "1.0.0")] + +#[stable(feature = "stable", since = "1.0.0")] +pub struct Foo { + #[unstable(feature = "unstable", issue = "none")] + pub field: T, +} diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.rs b/tests/ui/uninhabited/uninhabited-irrefutable.rs index cbaa989600331..3f7414e596bf1 100644 --- a/tests/ui/uninhabited/uninhabited-irrefutable.rs +++ b/tests/ui/uninhabited/uninhabited-irrefutable.rs @@ -29,7 +29,7 @@ fn main() { let x: Foo = Foo::D(123, 456); let Foo::D(_y, _z) = x; //~^ ERROR refutable pattern in local binding - //~| `Foo::A(_)` not covered + //~| NOTE `Foo::A(_)` not covered //~| NOTE `let` bindings require an "irrefutable pattern" //~| NOTE for more information //~| NOTE pattern `Foo::A(_)` is currently uninhabited diff --git a/tests/ui/uninhabited/uninhabited-pin-field.rs b/tests/ui/uninhabited/uninhabited-pin-field.rs new file mode 100644 index 0000000000000..3d0d9a7a4f807 --- /dev/null +++ b/tests/ui/uninhabited/uninhabited-pin-field.rs @@ -0,0 +1,10 @@ +use std::pin::Pin; + +enum Void {} + +fn demo(x: Pin) { + match x {} + //~^ ERROR non-exhaustive patterns +} + +fn main() {} diff --git a/tests/ui/uninhabited/uninhabited-pin-field.stderr b/tests/ui/uninhabited/uninhabited-pin-field.stderr new file mode 100644 index 0000000000000..93254ca9b9868 --- /dev/null +++ b/tests/ui/uninhabited/uninhabited-pin-field.stderr @@ -0,0 +1,19 @@ +error[E0004]: non-exhaustive patterns: type `Pin` is non-empty + --> $DIR/uninhabited-pin-field.rs:6:11 + | +LL | match x {} + | ^ + | +note: `Pin` defined here + --> $SRC_DIR/core/src/pin.rs:LL:COL + = note: the matched value is of type `Pin` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + | +LL ~ match x { +LL + _ => todo!(), +LL ~ } + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/uninhabited/uninhabited-unstable-field.current.stderr b/tests/ui/uninhabited/uninhabited-unstable-field.current.stderr new file mode 100644 index 0000000000000..9e0feb4c473f8 --- /dev/null +++ b/tests/ui/uninhabited/uninhabited-unstable-field.current.stderr @@ -0,0 +1,22 @@ +error[E0004]: non-exhaustive patterns: type `Foo` is non-empty + --> $DIR/uninhabited-unstable-field.rs:13:11 + | +LL | match x {} + | ^ + | +note: `Foo` defined here + --> $DIR/auxiliary/staged-api.rs:5:1 + | +LL | pub struct Foo { + | ^^^^^^^^^^^^^^^^^ + = note: the matched value is of type `Foo` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + | +LL ~ match x { +LL + _ => todo!(), +LL ~ } + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/uninhabited/uninhabited-unstable-field.exhaustive.stderr b/tests/ui/uninhabited/uninhabited-unstable-field.exhaustive.stderr new file mode 100644 index 0000000000000..9e0feb4c473f8 --- /dev/null +++ b/tests/ui/uninhabited/uninhabited-unstable-field.exhaustive.stderr @@ -0,0 +1,22 @@ +error[E0004]: non-exhaustive patterns: type `Foo` is non-empty + --> $DIR/uninhabited-unstable-field.rs:13:11 + | +LL | match x {} + | ^ + | +note: `Foo` defined here + --> $DIR/auxiliary/staged-api.rs:5:1 + | +LL | pub struct Foo { + | ^^^^^^^^^^^^^^^^^ + = note: the matched value is of type `Foo` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + | +LL ~ match x { +LL + _ => todo!(), +LL ~ } + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/uninhabited/uninhabited-unstable-field.rs b/tests/ui/uninhabited/uninhabited-unstable-field.rs new file mode 100644 index 0000000000000..9b507c518ab64 --- /dev/null +++ b/tests/ui/uninhabited/uninhabited-unstable-field.rs @@ -0,0 +1,29 @@ +//@ aux-build: staged-api.rs +//@ revisions: current exhaustive + +#![feature(exhaustive_patterns)] + +extern crate staged_api; + +use staged_api::Foo; + +enum Void {} + +fn demo(x: Foo) { + match x {} + //~^ ERROR non-exhaustive patterns +} + +// Ensure that the pattern is not considered unreachable. +fn demo2(x: Foo) { + match x { + Foo { .. } => {} + } +} + +// Same as above, but for wildcard. +fn demo3(x: Foo) { + match x { _ => {} } +} + +fn main() {} diff --git a/tests/ui/union/union-derive-clone.stderr b/tests/ui/union/union-derive-clone.stderr index a2b81f0dba1c3..679ab6a38e493 100644 --- a/tests/ui/union/union-derive-clone.stderr +++ b/tests/ui/union/union-derive-clone.stderr @@ -6,7 +6,6 @@ LL | #[derive(Clone)] | note: required by a bound in `AssertParamIsCopy` --> $SRC_DIR/core/src/clone.rs:LL:COL - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `U1` with `#[derive(Copy)]` | LL + #[derive(Copy)] diff --git a/tests/ui/union/union-derive-eq.current.stderr b/tests/ui/union/union-derive-eq.current.stderr index 151ceebe1ba67..a0339687dad48 100644 --- a/tests/ui/union/union-derive-eq.current.stderr +++ b/tests/ui/union/union-derive-eq.current.stderr @@ -9,7 +9,6 @@ LL | a: PartialEqNotEq, | note: required by a bound in `AssertParamIsEq` --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]` | LL + #[derive(Eq)] diff --git a/tests/ui/union/union-derive-eq.next.stderr b/tests/ui/union/union-derive-eq.next.stderr index 151ceebe1ba67..a0339687dad48 100644 --- a/tests/ui/union/union-derive-eq.next.stderr +++ b/tests/ui/union/union-derive-eq.next.stderr @@ -9,7 +9,6 @@ LL | a: PartialEqNotEq, | note: required by a bound in `AssertParamIsEq` --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]` | LL + #[derive(Eq)] diff --git a/tests/ui/unknown-unstable-lints/deny-unstable-lint-command-line.rs b/tests/ui/unknown-unstable-lints/deny-unstable-lint-command-line.rs index 7bd4df4722074..6005bc96ad6d1 100644 --- a/tests/ui/unknown-unstable-lints/deny-unstable-lint-command-line.rs +++ b/tests/ui/unknown-unstable-lints/deny-unstable-lint-command-line.rs @@ -1,6 +1,7 @@ +//~ ERROR unknown lint: `test_unstable_lint` +//~^ NOTE the `test_unstable_lint` lint is unstable //@ check-fail //@ compile-flags: -Dunknown_lints -Atest_unstable_lint -//@ error-pattern: unknown lint: `test_unstable_lint` -//@ error-pattern: the `test_unstable_lint` lint is unstable +//@ dont-require-annotations: NOTE fn main() {} diff --git a/tests/ui/unknown-unstable-lints/warn-unknown-unstable-lint-command-line.rs b/tests/ui/unknown-unstable-lints/warn-unknown-unstable-lint-command-line.rs index 995f65ef83dae..c32a21a016324 100644 --- a/tests/ui/unknown-unstable-lints/warn-unknown-unstable-lint-command-line.rs +++ b/tests/ui/unknown-unstable-lints/warn-unknown-unstable-lint-command-line.rs @@ -1,6 +1,7 @@ +//~ WARN unknown lint: `test_unstable_lint` +//~^ NOTE the `test_unstable_lint` lint is unstable //@ check-pass //@ compile-flags: -Wunknown_lints -Atest_unstable_lint -//@ error-pattern: unknown lint: `test_unstable_lint` -//@ error-pattern: the `test_unstable_lint` lint is unstable +//@ dont-require-annotations: NOTE fn main() {} diff --git a/tests/ui/unpretty/avoid-crash.rs b/tests/ui/unpretty/avoid-crash.rs index 7fcabfe6a8d43..64fa778bed47e 100644 --- a/tests/ui/unpretty/avoid-crash.rs +++ b/tests/ui/unpretty/avoid-crash.rs @@ -2,3 +2,5 @@ //@ compile-flags: -o. -Zunpretty=ast-tree fn main() {} + +//~? ERROR failed to write `.` due to error diff --git a/tests/ui/unpretty/bad-literal.rs b/tests/ui/unpretty/bad-literal.rs index 37377898b14f0..0ec1d7b07f1f7 100644 --- a/tests/ui/unpretty/bad-literal.rs +++ b/tests/ui/unpretty/bad-literal.rs @@ -1,5 +1,6 @@ //@ compile-flags: -Zunpretty=hir //@ check-fail +//@ edition: 2015 // In #100948 this caused an ICE with -Zunpretty=hir. fn main() { diff --git a/tests/ui/unpretty/bad-literal.stderr b/tests/ui/unpretty/bad-literal.stderr index b6259484f67d5..fd1801a87f209 100644 --- a/tests/ui/unpretty/bad-literal.stderr +++ b/tests/ui/unpretty/bad-literal.stderr @@ -1,5 +1,5 @@ error: invalid suffix `u` for number literal - --> $DIR/bad-literal.rs:6:5 + --> $DIR/bad-literal.rs:7:5 | LL | 1u; | ^^ invalid suffix `u` diff --git a/tests/ui/unpretty/bad-literal.stdout b/tests/ui/unpretty/bad-literal.stdout index c5272711d6e93..06116a4ab5534 100644 --- a/tests/ui/unpretty/bad-literal.stdout +++ b/tests/ui/unpretty/bad-literal.stdout @@ -4,6 +4,7 @@ use ::std::prelude::rust_2015::*; extern crate std; //@ compile-flags: -Zunpretty=hir //@ check-fail +//@ edition: 2015 // In #100948 this caused an ICE with -Zunpretty=hir. fn main() { diff --git a/tests/ui/unpretty/debug-fmt-hir.rs b/tests/ui/unpretty/debug-fmt-hir.rs index c19f3c4c0c57e..c79349de44424 100644 --- a/tests/ui/unpretty/debug-fmt-hir.rs +++ b/tests/ui/unpretty/debug-fmt-hir.rs @@ -1,5 +1,6 @@ //@ compile-flags: -Zunpretty=hir //@ check-pass +//@ edition: 2015 use std::fmt; diff --git a/tests/ui/unpretty/debug-fmt-hir.stdout b/tests/ui/unpretty/debug-fmt-hir.stdout index 2c9c96de9d142..dc18675ea8031 100644 --- a/tests/ui/unpretty/debug-fmt-hir.stdout +++ b/tests/ui/unpretty/debug-fmt-hir.stdout @@ -4,6 +4,7 @@ use ::std::prelude::rust_2015::*; extern crate std; //@ compile-flags: -Zunpretty=hir //@ check-pass +//@ edition: 2015 use std::fmt; diff --git a/tests/ui/unpretty/deprecated-attr.rs b/tests/ui/unpretty/deprecated-attr.rs new file mode 100644 index 0000000000000..0c80203e9652f --- /dev/null +++ b/tests/ui/unpretty/deprecated-attr.rs @@ -0,0 +1,18 @@ +//@ compile-flags: -Zunpretty=hir +//@ check-pass +//@ edition: 2015 + +#[deprecated] +pub struct PlainDeprecated; + +#[deprecated = "here's why this is deprecated"] +pub struct DirectNote; + +#[deprecated(note = "here's why this is deprecated")] +pub struct ExplicitNote; + +#[deprecated(since = "1.2.3", note = "here's why this is deprecated")] +pub struct SinceAndNote; + +#[deprecated(note = "here's why this is deprecated", since = "1.2.3")] +pub struct FlippedOrder; diff --git a/tests/ui/unpretty/deprecated-attr.stdout b/tests/ui/unpretty/deprecated-attr.stdout new file mode 100644 index 0000000000000..97d863b2e943c --- /dev/null +++ b/tests/ui/unpretty/deprecated-attr.stdout @@ -0,0 +1,26 @@ +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; +//@ compile-flags: -Zunpretty=hir +//@ check-pass +//@ edition: 2015 + +#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] +struct PlainDeprecated; + +#[attr = Deprecation {deprecation: Deprecation {since: Unspecified, note: +"here's why this is deprecated"}}] +struct DirectNote; + +#[attr = Deprecation {deprecation: Deprecation {since: Unspecified, note: +"here's why this is deprecated"}}] +struct ExplicitNote; + +#[attr = Deprecation {deprecation: Deprecation {since: NonStandard("1.2.3"), +note: "here's why this is deprecated"}}] +struct SinceAndNote; + +#[attr = Deprecation {deprecation: Deprecation {since: NonStandard("1.2.3"), +note: "here's why this is deprecated"}}] +struct FlippedOrder; diff --git a/tests/ui/unpretty/diagnostic-attr.rs b/tests/ui/unpretty/diagnostic-attr.rs new file mode 100644 index 0000000000000..4ef85c71f90e2 --- /dev/null +++ b/tests/ui/unpretty/diagnostic-attr.rs @@ -0,0 +1,14 @@ +//@ compile-flags: -Zunpretty=hir +//@ check-pass +//@ edition: 2015 + +#[diagnostic::on_unimplemented( + message = "My Message for `ImportantTrait<{A}>` implemented for `{Self}`", + label = "My Label", + note = "Note 1", + note = "Note 2" +)] +pub trait ImportantTrait {} + +#[diagnostic::do_not_recommend] +impl ImportantTrait for T where T: Clone {} diff --git a/tests/ui/unpretty/diagnostic-attr.stdout b/tests/ui/unpretty/diagnostic-attr.stdout new file mode 100644 index 0000000000000..81d71b91d8154 --- /dev/null +++ b/tests/ui/unpretty/diagnostic-attr.stdout @@ -0,0 +1,17 @@ +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; +//@ compile-flags: -Zunpretty=hir +//@ check-pass +//@ edition: 2015 + +#[diagnostic::on_unimplemented(message = +"My Message for `ImportantTrait<{A}>` implemented for `{Self}`", label = +"My Label", note = "Note 1", note = "Note 2")] +trait ImportantTrait { } + +#[diagnostic::do_not_recommend] +impl ImportantTrait for T where T: Clone + {#![diagnostic::do_not_recommend] +} diff --git a/tests/ui/unpretty/exhaustive-asm.expanded.stdout b/tests/ui/unpretty/exhaustive-asm.expanded.stdout new file mode 100644 index 0000000000000..92829b0ab15bd --- /dev/null +++ b/tests/ui/unpretty/exhaustive-asm.expanded.stdout @@ -0,0 +1,33 @@ +#![feature(prelude_import)] +#[prelude_import] +use std::prelude::rust_2024::*; +#[macro_use] +extern crate std; +//@ revisions: expanded hir +//@[expanded]compile-flags: -Zunpretty=expanded +//@[expanded]check-pass +//@[hir]compile-flags: -Zunpretty=hir +//@[hir]check-pass +//@ edition:2024 +//@ only-x86_64 +// +// asm parts of exhaustive.rs. Separate because we only run this on x86_64. + +mod expressions { + /// ExprKind::InlineAsm + fn expr_inline_asm() { + let x; + asm!("mov {1}, {0}\nshl {1}, 1\nshl {0}, 2\nadd {0}, {1}", + inout(reg) + x, + out(reg) + _); + } +} + +mod items { + /// ItemKind::GlobalAsm + mod item_global_asm { + global_asm! (".globl my_asm_func"); + } +} diff --git a/tests/ui/unpretty/exhaustive-asm.hir.stdout b/tests/ui/unpretty/exhaustive-asm.hir.stdout new file mode 100644 index 0000000000000..810db69bff161 --- /dev/null +++ b/tests/ui/unpretty/exhaustive-asm.hir.stdout @@ -0,0 +1,32 @@ +#[prelude_import] +use std::prelude::rust_2024::*; +#[macro_use] +extern crate std; +//@ revisions: expanded hir +//@[expanded]compile-flags: -Zunpretty=expanded +//@[expanded]check-pass +//@[hir]compile-flags: -Zunpretty=hir +//@[hir]check-pass +//@ edition:2024 +//@ only-x86_64 +// +// asm parts of exhaustive.rs. Separate because we only run this on x86_64. + +mod expressions { + /// ExprKind::InlineAsm + fn expr_inline_asm() { + let x; + asm!("mov {1}, {0}\nshl {1}, 1\nshl {0}, 2\nadd {0}, {1}", + inout(reg) + x, + out(reg) + _); + } +} + +mod items { + /// ItemKind::GlobalAsm + mod item_global_asm {/// ItemKind::GlobalAsm + global_asm! (".globl my_asm_func"); + } +} diff --git a/tests/ui/unpretty/exhaustive-asm.rs b/tests/ui/unpretty/exhaustive-asm.rs new file mode 100644 index 0000000000000..74a45447a20f1 --- /dev/null +++ b/tests/ui/unpretty/exhaustive-asm.rs @@ -0,0 +1,31 @@ +//@ revisions: expanded hir +//@[expanded]compile-flags: -Zunpretty=expanded +//@[expanded]check-pass +//@[hir]compile-flags: -Zunpretty=hir +//@[hir]check-pass +//@ edition:2024 +//@ only-x86_64 +// +// asm parts of exhaustive.rs. Separate because we only run this on x86_64. + +mod expressions { + /// ExprKind::InlineAsm + fn expr_inline_asm() { + let x; + core::arch::asm!( + "mov {tmp}, {x}", + "shl {tmp}, 1", + "shl {x}, 2", + "add {x}, {tmp}", + x = inout(reg) x, + tmp = out(reg) _, + ); + } +} + +mod items { + /// ItemKind::GlobalAsm + mod item_global_asm { + core::arch::global_asm!(".globl my_asm_func"); + } +} diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout new file mode 100644 index 0000000000000..9712ba58e627f --- /dev/null +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -0,0 +1,651 @@ +#![feature(prelude_import)] +//@ revisions: expanded hir +//@[expanded]compile-flags: -Zunpretty=expanded +//@[expanded]check-pass +//@[hir]compile-flags: -Zunpretty=hir +//@[hir]check-fail +//@ edition:2024 + +// Note: the HIR revision includes a `.stderr` file because there are some +// errors that only occur once we get past the AST. + +#![feature(auto_traits)] +#![feature(box_patterns)] +#![feature(builtin_syntax)] +#![feature(concat_idents)] +#![feature(const_trait_impl)] +#![feature(decl_macro)] +#![feature(deref_patterns)] +#![feature(dyn_star)] +#![feature(explicit_tail_calls)] +#![feature(gen_blocks)] +#![feature(more_qualified_paths)] +#![feature(never_patterns)] +#![feature(never_type)] +#![feature(pattern_types)] +#![feature(pattern_type_macro)] +#![feature(prelude_import)] +#![feature(specialization)] +#![feature(trace_macros)] +#![feature(trait_alias)] +#![feature(try_blocks)] +#![feature(yeet_expr)] +#![allow(incomplete_features)] +#[prelude_import] +use std::prelude::rust_2024::*; +#[macro_use] +extern crate std; + +#[prelude_import] +use self::prelude::*; + +mod prelude { + pub use std::prelude::rust_2024::*; + + pub type T = _; + + pub trait Trait { + const CONST: (); + } +} + +mod attributes { + //! inner single-line doc comment + /*! + * inner multi-line doc comment + */ + #![doc = "inner doc attribute"] + #![allow(dead_code, unused_variables)] + #![no_std] + + /// outer single-line doc comment + /** + * outer multi-line doc comment + */ + #[doc = "outer doc attribute"] + #[doc = "macro"] + #[allow()] + #[repr(C)] + struct Struct; +} + +mod expressions { + /// ExprKind::Array + fn expr_array() { + []; + [true]; + [true]; + [true, true]; + ["long........................................................................"]; + ["long............................................................", + true]; + } + + /// ExprKind::ConstBlock + fn expr_const_block() { + const {}; + const { 1 }; + const { + struct S; + }; + } + + /// ExprKind::Call + fn expr_call() { + let f; + f(); + f::(); + f::<1>(); + f::<'static, u8, 1>(); + f(true); + f(true); + ()(); + } + + /// ExprKind::MethodCall + fn expr_method_call() { + let x; + x.f(); + x.f::(); + x.collect::>(); + } + + /// ExprKind::Tup + fn expr_tup() { (); (true,); (true, false); (true, false); } + + /// ExprKind::Binary + fn expr_binary() { + let (a, b, c, d, x, y); + true || false; + true || false && false; + a < 1 && 2 < b && c > 3 && 4 > d; + a & b & !c; + a + b * c - d + -1 * -2 - -3; + x = !y; + } + + /// ExprKind::Unary + fn expr_unary() { let expr; *expr; !expr; -expr; } + + /// ExprKind::Lit + fn expr_lit() { 'x'; 1_000_i8; 1.00000000000000000000001; } + + /// ExprKind::Cast + fn expr_cast() { let expr; expr as T; expr as T; } + + /// ExprKind::Type + fn expr_type() { let expr; builtin # type_ascribe(expr, T); } + + /// ExprKind::Let + fn expr_let() { + let b; + if let Some(a) = b {} + if let _ = true && false {} + if let _ = (true && false) {} + } + + /// ExprKind::If + fn expr_if() { + if true {} + if !true {} + if let true = true {} else {} + if true {} else if false {} + if true {} else if false {} else {} + if true { return; } else if false { 0 } else { 0 } + } + + /// ExprKind::While + fn expr_while() { + while false {} + 'a: while false {} + while let true = true {} + } + + /// ExprKind::ForLoop + fn expr_for_loop() { let x; for _ in x {} 'a: for _ in x {} } + + /// ExprKind::Loop + fn expr_loop() { loop {} 'a: loop {} } + + /// ExprKind::Match + fn expr_match() { + let value; + match value {} + match value { ok => 1, } + match value { ok => 1, err => 0, } + } + + /// ExprKind::Closure + fn expr_closure() { + let value; + || {}; + |x| {}; + |x: u8| {}; + || (); + move || value; + async || value; + async move || value; + static || value; + static move || value; + (static async || value); + (static async move || value); + || -> u8 { value }; + 1 + (|| {}); + } + + /// ExprKind::Block + fn expr_block() { + {} + unsafe {} + 'a: {} + + #[allow()] + {} + { + #![allow()] + } + } + + /// ExprKind::Gen + fn expr_gen() { + async {}; + async move {}; + gen {}; + gen move {}; + async gen {}; + async gen move {}; + } + + /// ExprKind::Await + fn expr_await() { + let fut; + fut.await; + } + + /// ExprKind::TryBlock + fn expr_try_block() { try {} try { return; } } + + /// ExprKind::Assign + fn expr_assign() { let expr; expr = true; } + + /// ExprKind::AssignOp + fn expr_assign_op() { let expr; expr += true; } + + /// ExprKind::Field + fn expr_field() { let expr; expr.field; expr.0; } + + /// ExprKind::Index + fn expr_index() { let expr; expr[true]; } + + /// ExprKind::Range + fn expr_range() { + let (lo, hi); + ..; + ..hi; + lo..; + lo..hi; + lo..hi; + ..=hi; + lo..=hi; + -2..=-1; + } + + /// ExprKind::Underscore + fn expr_underscore() { + _; + } + + /// ExprKind::Path + fn expr_path() { + let x; + crate::expressions::expr_path; + crate::expressions::expr_path::<'static>; + ::default; + ::default::<>; + x::(); + x::(T, T) -> T; + crate::() -> ()::expressions::() -> ()::expr_path; + core::()::marker::()::PhantomData; + } + + /// ExprKind::AddrOf + fn expr_addr_of() { + let expr; + &expr; + &mut expr; + &raw const expr; + &raw mut expr; + } + + /// ExprKind::Break + fn expr_break() { 'a: { break; break 'a; break true; break 'a true; } } + + /// ExprKind::Continue + fn expr_continue() { 'a: { continue; continue 'a; } } + + /// ExprKind::Ret + fn expr_ret() { return; return true; } + + + /// ExprKind::InlineAsm: see exhaustive-asm.rs + /// ExprKind::OffsetOf + fn expr_offset_of() { + + + + + + + + + + + + + + // ... + + + + + + // concat_idents is deprecated + + + + + { builtin # offset_of(T, field) }; + } + /// ExprKind::MacCall + fn expr_mac_call() { "..."; "..."; "..."; } + /// ExprKind::Struct + fn expr_struct() { + struct Struct {} + let (x, base); + Struct {}; + ::Owned {}; + Struct { .. }; + Struct { ..base }; + Struct { x }; + Struct { x, ..base }; + Struct { x: true }; + Struct { x: true, .. }; + Struct { x: true, ..base }; + Struct { 0: true, ..base }; + } + /// ExprKind::Repeat + fn expr_repeat() { [(); 0]; } + /// ExprKind::Paren + fn expr_paren() { let expr; (expr); } + /// ExprKind::Try + fn expr_try() { let expr; expr?; } + /// ExprKind::Yield + fn expr_yield() { yield; yield true; } + /// ExprKind::Yeet + fn expr_yeet() { do yeet; do yeet 0; } + /// ExprKind::Become + fn expr_become() { become true; } + /// ExprKind::IncludedBytes + fn expr_include_bytes() { + b"data for include_bytes in ../expanded-exhaustive.rs\n"; + } + /// ExprKind::FormatArgs + fn expr_format_args() { + let expr; + format_args!(""); + format_args!("{0}", expr); + } +} +mod items { + /// ItemKind::ExternCrate + mod item_extern_crate { + extern crate core; + extern crate self as unpretty; + pub extern crate core as _; + } + /// ItemKind::Use + mod item_use { + use crate::{expressions, items::item_use}; + pub use core::*; + } + /// ItemKind::Static + mod item_static { + pub static A: () = {}; + static mut B: () = {}; + } + /// ItemKind::Const + mod item_const { + pub const A: () = {}; + trait TraitItems { + const B: (); + const C: () = {}; + } + } + /// ItemKind::Fn + mod item_fn { + pub const unsafe extern "C" fn f() {} + pub async unsafe extern "C" fn g() {} + fn h<'a, T>() where T: 'a {} + trait TraitItems { + unsafe extern "C" fn f(); + } + impl TraitItems for _ { + default unsafe extern "C" fn f() {} + } + } + /// ItemKind::Mod + mod item_mod { } + /// ItemKind::ForeignMod + mod item_foreign_mod { + unsafe extern "C++" {} + unsafe extern "C" {} + } + /// ItemKind::GlobalAsm: see exhaustive-asm.rs + /// ItemKind::TyAlias + mod item_ty_alias { + pub type Type<'a> where T: 'a = T; + } + /// ItemKind::Enum + mod item_enum { + pub enum Void {} + enum Empty { Unit, Tuple(), Struct {}, } + enum Generic<'a, T> where T: 'a { + Tuple(T), + Struct { + t: T, + }, + } + } + /// ItemKind::Struct + mod item_struct { + pub struct Unit; + struct Tuple(); + struct Newtype(Unit); + struct Struct {} + struct Generic<'a, T> where T: 'a { + t: T, + } + } + /// ItemKind::Union + mod item_union { + union Generic<'a, T> where T: 'a { + t: T, + } + } + /// ItemKind::Trait + mod item_trait { + pub unsafe auto trait Send {} + trait Trait<'a>: Sized where Self: 'a {} + } + /// ItemKind::TraitAlias + mod item_trait_alias { + pub trait Trait = Sized where for<'a> T: 'a; + } + /// ItemKind::Impl + mod item_impl { + impl () {} + impl () {} + impl Default for () {} + impl const Default for () {} + } + /// ItemKind::MacCall + mod item_mac_call { } + /// ItemKind::MacroDef + mod item_macro_def { + macro_rules! mac { () => {...}; } + pub macro stringify { () => {} } + } + /// ItemKind::Delegation + mod item_delegation { + /*! FIXME: todo */ + } + /// ItemKind::DelegationMac + mod item_delegation_mac { + /*! FIXME: todo */ + } +} +mod patterns { + /// PatKind::Missing + fn pat_missing() { let _: fn(u32, T, &str); } + /// PatKind::Wild + fn pat_wild() { let _; } + /// PatKind::Ident + fn pat_ident() { + let x; + let ref x; + let mut x; + let ref mut x; + let ref mut x @ _; + } + /// PatKind::Struct + fn pat_struct() { + let T {}; + let T:: {}; + let T::<'static> {}; + let T { x }; + let T { x: _x }; + let T { .. }; + let T { x, .. }; + let T { x: _x, .. }; + let T { 0: _x, .. }; + let ::Owned {}; + } + /// PatKind::TupleStruct + fn pat_tuple_struct() { + struct Tuple(); + let Tuple(); + let Tuple::(); + let Tuple::<'static>(); + let Tuple(x); + let Tuple(..); + let Tuple(x, ..); + } + /// PatKind::Or + fn pat_or() { let (true | false); let (true); let (true | false); } + /// PatKind::Path + fn pat_path() { + let core::marker::PhantomData; + let core::marker::PhantomData::; + let core::marker::PhantomData::<'static>; + let ::CONST; + } + /// PatKind::Tuple + fn pat_tuple() { let (); let (true,); let (true, false); } + /// PatKind::Box + fn pat_box() { let box pat; } + /// PatKind::Deref + fn pat_deref() { let deref!(pat); } + /// PatKind::Ref + fn pat_ref() { let &pat; let &mut pat; } + /// PatKind::Expr + fn pat_expr() { let 1_000_i8; let -""; } + /// PatKind::Range + fn pat_range() { let ..1; let 0..; let 0..1; let 0..=1; let -2..=-1; } + /// PatKind::Slice + fn pat_slice() { let []; let [true]; let [true]; let [true, false]; } + /// PatKind::Rest + fn pat_rest() { let ..; } + /// PatKind::Never + fn pat_never() { let !; let Some(!); } + /// PatKind::Paren + fn pat_paren() { let (pat); } + /// PatKind::MacCall + fn pat_mac_call() { let ""; let ""; let ""; } +} +mod statements { + /// StmtKind::Let + fn stmt_let() { + let _; + let _ = true; + let _: T = true; + let _ = true else { return; }; + } + /// StmtKind::Item + fn stmt_item() { + struct Struct {} + struct Unit; + } + /// StmtKind::Expr + fn stmt_expr() { () } + /// StmtKind::Semi + fn stmt_semi() { 1 + 1; } + /// StmtKind::Empty + fn stmt_empty() { ; } + /// StmtKind::MacCall + fn stmt_mac_call() { "..."; "..."; "..."; } +} +mod types { + /// TyKind::Slice + fn ty_slice() { let _: [T]; } + /// TyKind::Array + fn ty_array() { let _: [T; 0]; } + /// TyKind::Ptr + fn ty_ptr() { let _: *const T; let _: *mut T; } + /// TyKind::Ref + fn ty_ref() { + let _: &T; + let _: &mut T; + let _: &'static T; + let _: &'static mut [T]; + let _: &T>>>; + let _: &T>>>; + } + /// TyKind::BareFn + fn ty_bare_fn() { + let _: fn(); + let _: fn() -> (); + let _: fn(T); + let _: fn(t: T); + let _: fn(); + let _: for<'a> fn(); + } + /// TyKind::Never + fn ty_never() { let _: !; } + /// TyKind::Tup + fn ty_tup() { let _: (); let _: (T,); let _: (T, T); } + /// TyKind::Path + fn ty_path() { + let _: T; + let _: T<'static>; + let _: T; + let _: T; + let _: T() -> !; + let _: ::Owned; + } + /// TyKind::TraitObject + fn ty_trait_object() { + let _: dyn Send; + let _: dyn Send + 'static; + let _: dyn 'static + Send; + let _: dyn for<'a> Send; + let _: dyn* Send; + } + /// TyKind::ImplTrait + const fn ty_impl_trait() { + let _: impl Send; + let _: impl Send + 'static; + let _: impl 'static + Send; + let _: impl ?Sized; + let _: impl ~const Clone; + let _: impl for<'a> Send; + } + /// TyKind::Paren + fn ty_paren() { let _: (T); } + /// TyKind::Typeof + fn ty_typeof() { + /*! unused for now */ + } + /// TyKind::Infer + fn ty_infer() { let _: _; } + /// TyKind::ImplicitSelf + fn ty_implicit_self() { + /*! there is no syntax for this */ + } + /// TyKind::MacCall + #[expect(deprecated)] + fn ty_mac_call() { let _: T; let _: T; let _: T; } + /// TyKind::CVarArgs + fn ty_c_var_args() { + /*! FIXME: todo */ + } + /// TyKind::Pat + fn ty_pat() { let _: u32 is const 1..; } +} +mod visibilities { + /// VisibilityKind::Public + mod visibility_public { + pub struct Pub; + } + /// VisibilityKind::Restricted + mod visibility_restricted { + pub(crate) struct PubCrate; + pub(self) struct PubSelf; + pub(super) struct PubSuper; + pub(in crate) struct PubInCrate; + pub(in self) struct PubInSelf; + pub(in super) struct PubInSuper; + pub(in crate::visibilities) struct PubInCrateVisibilities; + pub(in self::super) struct PubInSelfSuper; + pub(in super::visibility_restricted) struct PubInSuperMod; + } +} diff --git a/tests/ui/unpretty/exhaustive.hir.stderr b/tests/ui/unpretty/exhaustive.hir.stderr new file mode 100644 index 0000000000000..58f7ff0f59812 --- /dev/null +++ b/tests/ui/unpretty/exhaustive.hir.stderr @@ -0,0 +1,172 @@ +error[E0697]: closures cannot be static + --> $DIR/exhaustive.rs:211:9 + | +LL | static || value; + | ^^^^^^^^^ + +error[E0697]: closures cannot be static + --> $DIR/exhaustive.rs:212:9 + | +LL | static move || value; + | ^^^^^^^^^^^^^^ + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/exhaustive.rs:241:13 + | +LL | fn expr_await() { + | --------------- this is not `async` +LL | let fut; +LL | fut.await; + | ^^^^^ only allowed inside `async` functions and blocks + +error: in expressions, `_` can only be used on the left-hand side of an assignment + --> $DIR/exhaustive.rs:290:9 + | +LL | _; + | ^ `_` not allowed here + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:300:9 + | +LL | x::(); + | ^^^^^ only `Fn` traits may use parentheses + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:301:9 + | +LL | x::(T, T) -> T; + | ^^^^^^^^^^^^^^ only `Fn` traits may use parentheses + | +help: use angle brackets instead + | +LL - x::(T, T) -> T; +LL + x:: -> T; + | + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:302:9 + | +LL | crate::() -> ()::expressions::() -> ()::expr_path; + | ^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:302:26 + | +LL | crate::() -> ()::expressions::() -> ()::expr_path; + | ^^^^^^^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:305:9 + | +LL | core::()::marker::()::PhantomData; + | ^^^^^^^^ only `Fn` traits may use parentheses + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:305:19 + | +LL | core::()::marker::()::PhantomData; + | ^^^^^^^^^^ only `Fn` traits may use parentheses + +error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks + --> $DIR/exhaustive.rs:392:9 + | +LL | yield; + | ^^^^^ + | +help: use `#[coroutine]` to make this closure a coroutine + | +LL | #[coroutine] fn expr_yield() { + | ++++++++++++ + +error[E0703]: invalid ABI: found `C++` + --> $DIR/exhaustive.rs:472:23 + | +LL | unsafe extern "C++" {} + | ^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error: `..` patterns are not allowed here + --> $DIR/exhaustive.rs:679:13 + | +LL | let ..; + | ^^ + | + = note: only allowed in tuple, tuple struct, and slice patterns + +error[E0214]: parenthesized type parameters may only be used with a `Fn` trait + --> $DIR/exhaustive.rs:794:16 + | +LL | let _: T() -> !; + | ^^^^^^^^ only `Fn` traits may use parentheses + +error[E0562]: `impl Trait` is not allowed in the type of variable bindings + --> $DIR/exhaustive.rs:809:16 + | +LL | let _: impl Send; + | ^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + = note: see issue #63065 for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0562]: `impl Trait` is not allowed in the type of variable bindings + --> $DIR/exhaustive.rs:810:16 + | +LL | let _: impl Send + 'static; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + = note: see issue #63065 for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0562]: `impl Trait` is not allowed in the type of variable bindings + --> $DIR/exhaustive.rs:811:16 + | +LL | let _: impl 'static + Send; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + = note: see issue #63065 for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0562]: `impl Trait` is not allowed in the type of variable bindings + --> $DIR/exhaustive.rs:812:16 + | +LL | let _: impl ?Sized; + | ^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + = note: see issue #63065 for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0562]: `impl Trait` is not allowed in the type of variable bindings + --> $DIR/exhaustive.rs:813:16 + | +LL | let _: impl ~const Clone; + | ^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + = note: see issue #63065 for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0562]: `impl Trait` is not allowed in the type of variable bindings + --> $DIR/exhaustive.rs:814:16 + | +LL | let _: impl for<'a> Send; + | ^^^^^^^^^^^^^^^^^ + | + = note: `impl Trait` is only allowed in arguments and return types of functions and methods + = note: see issue #63065 for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 20 previous errors + +Some errors have detailed explanations: E0214, E0562, E0697, E0703, E0728. +For more information about an error, try `rustc --explain E0214`. diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout new file mode 100644 index 0000000000000..c20f123b16e83 --- /dev/null +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -0,0 +1,724 @@ +//@ revisions: expanded hir +//@[expanded]compile-flags: -Zunpretty=expanded +//@[expanded]check-pass +//@[hir]compile-flags: -Zunpretty=hir +//@[hir]check-fail +//@ edition:2024 + +// Note: the HIR revision includes a `.stderr` file because there are some +// errors that only occur once we get past the AST. + +#![feature(auto_traits)] +#![feature(box_patterns)] +#![feature(builtin_syntax)] +#![feature(concat_idents)] +#![feature(const_trait_impl)] +#![feature(decl_macro)] +#![feature(deref_patterns)] +#![feature(dyn_star)] +#![feature(explicit_tail_calls)] +#![feature(gen_blocks)] +#![feature(more_qualified_paths)] +#![feature(never_patterns)] +#![feature(never_type)] +#![feature(pattern_types)] +#![feature(pattern_type_macro)] +#![feature(prelude_import)] +#![feature(specialization)] +#![feature(trace_macros)] +#![feature(trait_alias)] +#![feature(try_blocks)] +#![feature(yeet_expr)] +#![allow(incomplete_features)] +#[prelude_import] +use std::prelude::rust_2024::*; +#[macro_use] +extern crate std; + +#[prelude_import] +use self::prelude::*; + +mod prelude { + use std::prelude::rust_2024::*; + + type T = _; + + trait Trait { + const + CONST: + (); + } +} + +//! inner single-line doc comment +/*! + * inner multi-line doc comment + */ +#[doc = "inner doc attribute"] +#[allow(dead_code, unused_variables)] +#[no_std] +mod attributes {//! inner single-line doc comment + /*! + * inner multi-line doc comment + */ + #![doc = "inner doc attribute"] + #![allow(dead_code, unused_variables)] + #![no_std] + + /// outer single-line doc comment + /** + * outer multi-line doc comment + */ + #[doc = "outer doc attribute"] + #[doc = "macro"] + #[allow()] + #[attr = Repr([ReprC])] + struct Struct; +} + +mod expressions { + /// ExprKind::Array + fn expr_array() { + []; + [true]; + [true]; + [true, true]; + ["long........................................................................"]; + ["long............................................................", + true]; + } + + /// ExprKind::ConstBlock + fn expr_const_block() { + const { }; + const { 1 }; + const + { + struct S; + }; + } + + /// ExprKind::Call + fn expr_call() { + let f; + f(); + f::(); + f::<1>(); + f::<'static, u8, 1>(); + f(true); + f(true); + ()(); + } + + /// ExprKind::MethodCall + fn expr_method_call() { + let x; + x.f(); + x.f::(); + x.collect::>(); + } + + /// ExprKind::Tup + fn expr_tup() { (); (true,); (true, false); (true, false); } + + /// ExprKind::Binary + fn expr_binary() { + let (a, b, c, d, x, y); + true || false; + true || false && false; + a < 1 && 2 < b && c > 3 && 4 > d; + a & b & !c; + a + b * c - d + -1 * -2 - -3; + x = !y; + } + + /// ExprKind::Unary + fn expr_unary() { let expr; *expr; !expr; -expr; } + + /// ExprKind::Lit + fn expr_lit() { 'x'; 1000i8; 1.00000000000000000000001; } + + /// ExprKind::Cast + fn expr_cast() { let expr; expr as T; expr as T; } + + /// ExprKind::Type + fn expr_type() { let expr; type_ascribe!(expr, T); } + + /// ExprKind::Let + fn expr_let() { + let b; + if let Some(a) = b { } + if let _ = true && false { } + if let _ = (true && false) { } + } + + /// ExprKind::If + fn expr_if() { + if true { } + if !true { } + if let true = true { } else { } + if true { } else if false { } + if true { } else if false { } else { } + if true { return; } else if false { 0 } else { 0 } + } + + /// ExprKind::While + fn expr_while() { + loop { if false { } else { break; } } + 'a: loop { if false { } else { break; } } + loop { if let true = true { } else { break; } } + } + + /// ExprKind::ForLoop + fn expr_for_loop() { + let x; + { + let _t = + match #[lang = "into_iter"](x) { + mut iter => + loop { + match #[lang = "next"](&mut iter) { + #[lang = "None"] {} => break, + #[lang = "Some"] { 0: _ } => { } + } + }, + }; + _t + }; + { + let _t = + match #[lang = "into_iter"](x) { + mut iter => + 'a: loop { + match #[lang = "next"](&mut iter) { + #[lang = "None"] {} => break, + #[lang = "Some"] { 0: _ } => { } + } + }, + }; + _t + } + } + + /// ExprKind::Loop + fn expr_loop() { loop { } 'a: loop { } } + + /// ExprKind::Match + fn expr_match() { + let value; + match value { } + match value { ok => 1, } + match value { ok => 1, err => 0, } + } + + /// ExprKind::Closure + fn expr_closure() { + let value; + || { }; + |x| { }; + |x: u8| { }; + || (); + move || value; + || |mut _task_context: ResumeTy| { { let _t = value; _t } }; + move || |mut _task_context: ResumeTy| { { let _t = value; _t } }; + || value; + move || value; + || |mut _task_context: ResumeTy| { { let _t = value; _t } }; + move || |mut _task_context: ResumeTy| { { let _t = value; _t } }; + || -> u8 { value }; + 1 + (|| { }); + } + + /// ExprKind::Block + fn expr_block() { + { } + unsafe { } + 'a: { } + #[allow()] + { } + #[allow()] + { } + } + + /// ExprKind::Gen + fn expr_gen() { + |mut _task_context: ResumeTy| { }; + move |mut _task_context: ResumeTy| { }; + || { }; + move || { }; + |mut _task_context: ResumeTy| { }; + move |mut _task_context: ResumeTy| { }; + } + + /// ExprKind::Await + fn expr_await() { + let fut; + { + fut; + (/*ERROR*/) + }; + } + + /// ExprKind::TryBlock + fn expr_try_block() { + { #[lang = "from_output"](()) } + { return; #[lang = "from_output"](()) } + } + + /// ExprKind::Assign + fn expr_assign() { let expr; expr = true; } + + /// ExprKind::AssignOp + fn expr_assign_op() { let expr; expr += true; } + + /// ExprKind::Field + fn expr_field() { let expr; expr.field; expr.0; } + + /// ExprKind::Index + fn expr_index() { let expr; expr[true]; } + + /// ExprKind::Range + fn expr_range() { + let (lo, hi); + #[lang = "RangeFull"] { }; + #[lang = "RangeTo"] { end: hi }; + #[lang = "RangeFrom"] { start: lo }; + #[lang = "Range"] { start: lo, end: hi }; + #[lang = "Range"] { start: lo, end: hi }; + #[lang = "RangeToInclusive"] { end: hi }; + #[lang = "range_inclusive_new"](lo, hi); + #[lang = "range_inclusive_new"](-2, -1); + } + + /// ExprKind::Underscore + fn expr_underscore() { + (/*ERROR*/); + } + + /// ExprKind::Path + fn expr_path() { + let x; + crate::expressions::expr_path; + crate::expressions::expr_path::<'static>; + ::default; + ::default; + x; + x::; + crate::expressions::expr_path; + core::marker::PhantomData; + } + + /// ExprKind::AddrOf + fn expr_addr_of() { + let expr; + &expr; + &mut expr; + &raw const expr; + &raw mut expr; + } + + /// ExprKind::Break + fn expr_break() { 'a: { break; break 'a; break true; break 'a true; } } + + /// ExprKind::Continue + fn expr_continue() { 'a: { continue; continue 'a; } } + + /// ExprKind::Ret + fn expr_ret() { return; return true; } + + + /// ExprKind::InlineAsm: see exhaustive-asm.rs + /// ExprKind::OffsetOf + fn expr_offset_of() { + + + + + + + + + + + + + + // ... + + + + + + // concat_idents is deprecated + + + + + { offset_of!(T, field) }; + } + /// ExprKind::MacCall + fn expr_mac_call() { "..."; "..."; "..."; } + /// ExprKind::Struct + fn expr_struct() { + struct Struct { + } + let (x, base); + Struct { }; + ::Owned { }; + Struct { .. }; + Struct { ..base }; + Struct { x }; + Struct { x, ..base }; + Struct { x: true }; + Struct { x: true, .. }; + Struct { x: true, ..base }; + Struct { 0: true, ..base }; + } + /// ExprKind::Repeat + fn expr_repeat() { [(); 0]; } + /// ExprKind::Paren + fn expr_paren() { let expr; expr; } + /// ExprKind::Try + fn expr_try() { + let expr; + match #[lang = "branch"](expr) { + #[lang = "Break"] { 0: residual } => #[allow(unreachable_code)] + return #[lang = "from_residual"](residual), + #[lang = "Continue"] { 0: val } => #[allow(unreachable_code)] + val, + }; + } + /// ExprKind::Yield + fn expr_yield() { yield (); yield true; } + /// ExprKind::Yeet + fn expr_yeet() { + return #[lang = "from_yeet"](()); + return #[lang = "from_yeet"](0); + } + /// ExprKind::Become + fn expr_become() { become true; } + /// ExprKind::IncludedBytes + fn expr_include_bytes() { + b"data for include_bytes in ../expanded-exhaustive.rs\n"; + } + /// ExprKind::FormatArgs + fn expr_format_args() { + let expr; + format_arguments::new_const(&[]); + format_arguments::new_v1(&[""], + &[format_argument::new_display(&expr)]); + } +} +mod items { + /// ItemKind::ExternCrate + mod item_extern_crate {/// ItemKind::ExternCrate + extern crate core; + extern crate self as unpretty; + extern crate core as _; + } + /// ItemKind::Use + mod item_use {/// ItemKind::Use + use ::{}; + use crate::expressions; + use crate::items::item_use; + use core::*; + } + /// ItemKind::Static + mod item_static {/// ItemKind::Static + static A: () = { }; + static mut B: () = { }; + } + /// ItemKind::Const + mod item_const {/// ItemKind::Const + const A: () = { }; + trait TraitItems { + const + B: + (); + const + C: + () + = + { }; + } + } + /// ItemKind::Fn + mod item_fn {/// ItemKind::Fn + const unsafe extern "C" fn f() { } + async unsafe extern "C" fn g() + -> + /*impl Trait*/ |mut _task_context: ResumeTy| + { { let _t = { }; _t } } + fn h<'a, T>() where T: 'a { } + trait TraitItems { + unsafe extern "C" fn f(); + } + impl TraitItems for _ { + unsafe extern "C" fn f() { } + } + } + /// ItemKind::Mod + mod item_mod {/// ItemKind::Mod + } + /// ItemKind::ForeignMod + mod item_foreign_mod {/// ItemKind::ForeignMod + extern "Rust" { } + extern "C" { } + } + /// ItemKind::GlobalAsm: see exhaustive-asm.rs + /// ItemKind::TyAlias + mod item_ty_alias {/// ItemKind::GlobalAsm: see exhaustive-asm.rs + /// ItemKind::TyAlias + type Type<'a> where T: 'a = T; + } + /// ItemKind::Enum + mod item_enum {/// ItemKind::Enum + enum Void { } + enum Empty { + Unit, + Tuple(), + Struct { + }, + } + enum Generic<'a, T> where T: 'a { + Tuple(T), + Struct { + t: T, + }, + } + } + /// ItemKind::Struct + mod item_struct {/// ItemKind::Struct + struct Unit; + struct Tuple(); + struct Newtype(Unit); + struct Struct { + } + struct Generic<'a, T> where T: 'a { + t: T, + } + } + /// ItemKind::Union + mod item_union {/// ItemKind::Union + union Generic<'a, T> where T: 'a { + t: T, + } + } + /// ItemKind::Trait + mod item_trait {/// ItemKind::Trait + auto unsafe trait Send { } + trait Trait<'a>: Sized where Self: 'a { } + } + /// ItemKind::TraitAlias + mod item_trait_alias {/// ItemKind::TraitAlias + trait Trait = Sized where for<'a> T: 'a; + } + /// ItemKind::Impl + mod item_impl {/// ItemKind::Impl + impl () { } + impl () { } + impl Default for () { } + impl const Default for () { } + } + /// ItemKind::MacCall + mod item_mac_call {/// ItemKind::MacCall + } + /// ItemKind::MacroDef + mod item_macro_def {/// ItemKind::MacroDef + macro_rules! mac { () => {...}; } + macro stringify { () => {} } + } + /// ItemKind::Delegation + /*! FIXME: todo */ + mod item_delegation {/// ItemKind::Delegation + /*! FIXME: todo */ + } + /// ItemKind::DelegationMac + /*! FIXME: todo */ + mod item_delegation_mac {/// ItemKind::DelegationMac + /*! FIXME: todo */ + } +} +mod patterns { + /// PatKind::Missing + fn pat_missing() { let _: for fn(u32, T, &'_ str); } + /// PatKind::Wild + fn pat_wild() { let _; } + /// PatKind::Ident + fn pat_ident() { + let x; + let ref x; + let mut x; + let ref mut x; + let ref mut x@_; + } + /// PatKind::Struct + fn pat_struct() { + let T {}; + let T:: {}; + let T::<'static> {}; + let T { x }; + let T { x: _x }; + let T { .. }; + let T { x, .. }; + let T { x: _x, .. }; + let T { 0: _x, .. }; + let ::Owned {}; + } + /// PatKind::TupleStruct + fn pat_tuple_struct() { + struct Tuple(); + let Tuple(); + let Tuple::(); + let Tuple::<'static>(); + let Tuple(x); + let Tuple(..); + let Tuple(x, ..); + } + /// PatKind::Or + fn pat_or() { let true | false; let true; let true | false; } + /// PatKind::Path + fn pat_path() { + let core::marker::PhantomData; + let core::marker::PhantomData::; + let core::marker::PhantomData::<'static>; + let ::CONST; + } + /// PatKind::Tuple + fn pat_tuple() { let (); let (true,); let (true, false); } + /// PatKind::Box + fn pat_box() { let box pat; } + /// PatKind::Deref + fn pat_deref() { let deref!(pat); } + /// PatKind::Ref + fn pat_ref() { let &pat; let &mut pat; } + /// PatKind::Expr + fn pat_expr() { let 1000i8; let -""; } + /// PatKind::Range + fn pat_range() { let ..1; let 0...; let 0..1; let 0...1; let -2...-1; } + /// PatKind::Slice + fn pat_slice() { let []; let [true]; let [true]; let [true, false]; } + /// PatKind::Rest + fn pat_rest() { let _; } + /// PatKind::Never + fn pat_never() { let !; let Some(!); } + /// PatKind::Paren + fn pat_paren() { let pat; } + /// PatKind::MacCall + fn pat_mac_call() { let ""; let ""; let ""; } +} +mod statements { + /// StmtKind::Let + fn stmt_let() { + let _; + let _ = true; + let _: T = true; + let _ = true else { return; }; + } + /// StmtKind::Item + fn stmt_item() { + struct Struct { + } + struct Unit; + } + /// StmtKind::Expr + fn stmt_expr() { () } + /// StmtKind::Semi + fn stmt_semi() { 1 + 1; } + /// StmtKind::Empty + fn stmt_empty() { } + /// StmtKind::MacCall + fn stmt_mac_call() { "..."; "..."; "..."; } +} +mod types { + /// TyKind::Slice + fn ty_slice() { let _: [T]; } + /// TyKind::Array + fn ty_array() { let _: [T; 0]; } + /// TyKind::Ptr + fn ty_ptr() { let _: *const T; let _: *mut T; } + /// TyKind::Ref + fn ty_ref() { + let _: &T; + let _: &mut T; + let _: &'static T; + let _: &'static mut [T]; + let _: &T>>>; + let _: &T>>>; + } + /// TyKind::BareFn + fn ty_bare_fn() { + let _: fn(); + let _: fn() -> (); + let _: fn(T); + let _: fn(t: T); + let _: fn(); + let _: for<'a> fn(); + } + /// TyKind::Never + fn ty_never() { let _: !; } + /// TyKind::Tup + fn ty_tup() { let _: (); let _: (T,); let _: (T, T); } + /// TyKind::Path + fn ty_path() { + let _: T; + let _: T<'static>; + let _: T; + let _: T; + let _: T; + let _: ::Owned; + } + /// TyKind::TraitObject + fn ty_trait_object() { + let _: dyn Send; + let _: dyn Send + 'static; + let _: dyn Send + 'static; + let _: dyn for<'a> Send; + let _: dyn* Send; + } + /// TyKind::ImplTrait + const fn ty_impl_trait() { + let _: (/*ERROR*/); + let _: (/*ERROR*/); + let _: (/*ERROR*/); + let _: (/*ERROR*/); + let _: (/*ERROR*/); + let _: (/*ERROR*/); + } + /// TyKind::Paren + fn ty_paren() { let _: T; } + /// TyKind::Typeof + /*! unused for now */ + fn ty_typeof() { } + /// TyKind::Infer + fn ty_infer() { let _: _; } + /// TyKind::ImplicitSelf + /*! there is no syntax for this */ + fn ty_implicit_self() { } + /// TyKind::MacCall + #[expect(deprecated)] + fn ty_mac_call() { let _: T; let _: T; let _: T; } + /// TyKind::CVarArgs + /*! FIXME: todo */ + fn ty_c_var_args() { } + /// TyKind::Pat + fn ty_pat() { let _: u32 is 1..=RangeMax; } +} +mod visibilities { + /// VisibilityKind::Public + mod visibility_public {/// VisibilityKind::Public + struct Pub; + } + /// VisibilityKind::Restricted + mod visibility_restricted {/// VisibilityKind::Restricted + struct PubCrate; + struct PubSelf; + struct PubSuper; + struct PubInCrate; + struct PubInSelf; + struct PubInSuper; + struct PubInCrateVisibilities; + struct PubInSelfSuper; + struct PubInSuperMod; + } +} diff --git a/tests/ui/unpretty/exhaustive.rs b/tests/ui/unpretty/exhaustive.rs new file mode 100644 index 0000000000000..60ad3564689d7 --- /dev/null +++ b/tests/ui/unpretty/exhaustive.rs @@ -0,0 +1,874 @@ +//@ revisions: expanded hir +//@[expanded]compile-flags: -Zunpretty=expanded +//@[expanded]check-pass +//@[hir]compile-flags: -Zunpretty=hir +//@[hir]check-fail +//@ edition:2024 + +// Note: the HIR revision includes a `.stderr` file because there are some +// errors that only occur once we get past the AST. + +#![feature(auto_traits)] +#![feature(box_patterns)] +#![feature(builtin_syntax)] +#![feature(concat_idents)] +#![feature(const_trait_impl)] +#![feature(decl_macro)] +#![feature(deref_patterns)] +#![feature(dyn_star)] +#![feature(explicit_tail_calls)] +#![feature(gen_blocks)] +#![feature(more_qualified_paths)] +#![feature(never_patterns)] +#![feature(never_type)] +#![feature(pattern_types)] +#![feature(pattern_type_macro)] +#![feature(prelude_import)] +#![feature(specialization)] +#![feature(trace_macros)] +#![feature(trait_alias)] +#![feature(try_blocks)] +#![feature(yeet_expr)] +#![allow(incomplete_features)] + +#[prelude_import] +use self::prelude::*; + +mod prelude { + pub use std::prelude::rust_2024::*; + + pub type T = _; + + pub trait Trait { + const CONST: (); + } +} + +mod attributes { + //! inner single-line doc comment + /*! + * inner multi-line doc comment + */ + #![doc = "inner doc attribute"] + #![allow(dead_code, unused_variables)] + #![no_std] + + /// outer single-line doc comment + /** + * outer multi-line doc comment + */ + #[doc = "outer doc attribute"] + #[doc = concat!("mac", "ro")] + #[allow()] + #[repr(C)] + struct Struct; +} + +mod expressions { + /// ExprKind::Array + fn expr_array() { + []; + [true]; + [true,]; + [true, true]; + ["long........................................................................"]; + ["long............................................................", true]; + } + + /// ExprKind::ConstBlock + fn expr_const_block() { + const {}; + const { 1 }; + const { struct S; }; + } + + /// ExprKind::Call + fn expr_call() { + let f; + f(); + f::(); + f::<1>(); + f::<'static, u8, 1>(); + f(true); + f(true,); + ()(); + } + + /// ExprKind::MethodCall + fn expr_method_call() { + let x; + x.f(); + x.f::(); + x.collect::>(); + } + + /// ExprKind::Tup + fn expr_tup() { + (); + (true,); + (true, false); + (true, false,); + } + + /// ExprKind::Binary + fn expr_binary() { + let (a, b, c, d, x, y); + true || false; + true || false && false; + a < 1 && 2 < b && c > 3 && 4 > d; + a & b & !c; + a + b * c - d + -1 * -2 - -3; + x = !y; + } + + /// ExprKind::Unary + fn expr_unary() { + let expr; + *expr; + !expr; + -expr; + } + + /// ExprKind::Lit + fn expr_lit() { + 'x'; + 1_000_i8; + 1.00000000000000000000001; + } + + /// ExprKind::Cast + fn expr_cast() { + let expr; + expr as T; + expr as T; + } + + /// ExprKind::Type + fn expr_type() { + let expr; + builtin # type_ascribe(expr, T); + } + + /// ExprKind::Let + fn expr_let() { + let b; + if let Some(a) = b {} + if let _ = true && false {} + if let _ = (true && false) {} + } + + /// ExprKind::If + fn expr_if() { + if true {} + if !true {} + if let true = true {} else {} + if true {} else if false {} + if true {} else if false {} else {} + if true { return; } else if false { 0 } else { 0 } + } + + /// ExprKind::While + fn expr_while() { + while false {} + 'a: while false {} + while let true = true {} + } + + /// ExprKind::ForLoop + fn expr_for_loop() { + let x; + for _ in x {} + 'a: for _ in x {} + } + + /// ExprKind::Loop + fn expr_loop() { + loop {} + 'a: loop {} + } + + /// ExprKind::Match + fn expr_match() { + let value; + match value {} + match value { ok => 1 } + match value { + ok => 1, + err => 0, + } + } + + /// ExprKind::Closure + fn expr_closure() { + let value; + || {}; + |x| {}; + |x: u8| {}; + || (); + move || value; + async || value; + async move || value; + static || value; //[hir]~ ERROR closures cannot be static + static move || value; //[hir]~ ERROR closures cannot be static + (static async || value); + (static async move || value); + || -> u8 { value }; + 1 + || {}; + } + + /// ExprKind::Block + fn expr_block() { + {} + unsafe {} + 'a: {} + #[allow()] {} + { #![allow()] } + } + + /// ExprKind::Gen + fn expr_gen() { + async {}; + async move {}; + gen {}; + gen move {}; + async gen {}; + async gen move {}; + } + + /// ExprKind::Await + fn expr_await() { + let fut; + fut.await; //[hir]~ ERROR `await` is only allowed + } + + /// ExprKind::TryBlock + fn expr_try_block() { + try {} + try { return; } + } + + /// ExprKind::Assign + fn expr_assign() { + let expr; + expr = true; + } + + /// ExprKind::AssignOp + fn expr_assign_op() { + let expr; + expr += true; + } + + /// ExprKind::Field + fn expr_field() { + let expr; + expr.field; + expr.0; + } + + /// ExprKind::Index + fn expr_index() { + let expr; + expr[true]; + } + + /// ExprKind::Range + fn expr_range() { + let (lo, hi); + ..; + ..hi; + lo..; + lo..hi; + lo .. hi; + ..=hi; + lo..=hi; + -2..=-1; + } + + /// ExprKind::Underscore + fn expr_underscore() { + _; //[hir]~ ERROR in expressions, `_` can only + } + + /// ExprKind::Path + fn expr_path() { + let x; + crate::expressions::expr_path; + crate::expressions::expr_path::<'static>; + ::default; + ::default::<>; + x::(); //[hir]~ ERROR parenthesized type parameters + x::(T, T) -> T; //[hir]~ ERROR parenthesized type parameters + crate::() -> ()::expressions::() -> ()::expr_path; + //[hir]~^ ERROR parenthesized type parameters + //[hir]~| ERROR parenthesized type parameters + core::()::marker::()::PhantomData; + //[hir]~^ ERROR parenthesized type parameters + //[hir]~| ERROR parenthesized type parameters + } + + /// ExprKind::AddrOf + fn expr_addr_of() { + let expr; + &expr; + &mut expr; + &raw const expr; + &raw mut expr; + } + + /// ExprKind::Break + fn expr_break() { + 'a: { + break; + break 'a; + break true; + break 'a true; + } + } + + /// ExprKind::Continue + fn expr_continue() { + 'a: { + continue; + continue 'a; + } + } + + /// ExprKind::Ret + fn expr_ret() { + return; + return true; + } + + /// ExprKind::InlineAsm: see exhaustive-asm.rs + + /// ExprKind::OffsetOf + fn expr_offset_of() { + core::mem::offset_of!(T, field); + } + + /// ExprKind::MacCall + fn expr_mac_call() { + stringify!(...); + stringify![...]; + stringify! { ... }; + } + + /// ExprKind::Struct + fn expr_struct() { + struct Struct {} + let (x, base); + Struct {}; + ::Owned {}; + Struct { .. }; + Struct { .. base }; + Struct { x }; + Struct { x, ..base }; + Struct { x: true }; + Struct { x: true, .. }; + Struct { x: true, ..base }; + Struct { 0: true, ..base }; + } + + /// ExprKind::Repeat + fn expr_repeat() { + [(); 0]; + } + + /// ExprKind::Paren + fn expr_paren() { + let expr; + (expr); + } + + /// ExprKind::Try + fn expr_try() { + let expr; + expr?; + } + + /// ExprKind::Yield + fn expr_yield() { + yield; //[hir]~ ERROR `yield` can only be used + yield true; + } + + /// ExprKind::Yeet + fn expr_yeet() { + do yeet; + do yeet 0; + } + + /// ExprKind::Become + fn expr_become() { + become true; + } + + /// ExprKind::IncludedBytes + fn expr_include_bytes() { + include_bytes!("auxiliary/data.txt"); + } + + /// ExprKind::FormatArgs + fn expr_format_args() { + let expr; + format_args!(""); + format_args!("{}", expr); + } +} + +mod items { + /// ItemKind::ExternCrate + mod item_extern_crate { + extern crate core; + extern crate self as unpretty; + pub extern crate core as _; + } + + /// ItemKind::Use + mod item_use { + use crate::{expressions, items::item_use}; + pub use core::*; + } + + /// ItemKind::Static + mod item_static { + pub static A: () = {}; + static mut B: () = {}; + } + + /// ItemKind::Const + mod item_const { + pub const A: () = {}; + + trait TraitItems { + const B: (); + const C: () = {}; + } + } + + /// ItemKind::Fn + mod item_fn { + pub const unsafe extern "C" fn f() {} + pub async unsafe extern "C" fn g() {} + fn h<'a, T>() where T: 'a {} + + trait TraitItems { + unsafe extern "C" fn f(); + } + + impl TraitItems for _ { + default unsafe extern "C" fn f() {} + } + } + + /// ItemKind::Mod + mod item_mod { + // ... + } + + /// ItemKind::ForeignMod + mod item_foreign_mod { + unsafe extern "C++" {} //[hir]~ ERROR invalid ABI + unsafe extern "C" {} + } + + /// ItemKind::GlobalAsm: see exhaustive-asm.rs + + /// ItemKind::TyAlias + mod item_ty_alias { + pub type Type<'a> where T: 'a, = T; + } + + /// ItemKind::Enum + mod item_enum { + pub enum Void {} + enum Empty { + Unit, + Tuple(), + Struct {}, + } + enum Generic<'a, T> + where + T: 'a, + { + Tuple(T), + Struct { t: T }, + } + } + + /// ItemKind::Struct + mod item_struct { + pub struct Unit; + struct Tuple(); + struct Newtype(Unit); + struct Struct {} + struct Generic<'a, T> + where + T: 'a, + { + t: T, + } + } + + /// ItemKind::Union + mod item_union { + union Generic<'a, T> + where + T: 'a, + { + t: T, + } + } + + /// ItemKind::Trait + mod item_trait { + pub unsafe auto trait Send {} + trait Trait<'a>: Sized + where + Self: 'a, + { + } + } + + /// ItemKind::TraitAlias + mod item_trait_alias { + pub trait Trait = Sized where for<'a> T: 'a; + } + + /// ItemKind::Impl + mod item_impl { + impl () {} + impl () {} + impl Default for () {} + impl const Default for () {} + } + + /// ItemKind::MacCall + mod item_mac_call { + trace_macros!(false); + trace_macros![false]; + trace_macros! { false } + } + + /// ItemKind::MacroDef + mod item_macro_def { + macro_rules! mac { () => {...}; } + pub macro stringify() {} + } + + /// ItemKind::Delegation + mod item_delegation { + /*! FIXME: todo */ + } + + /// ItemKind::DelegationMac + mod item_delegation_mac { + /*! FIXME: todo */ + } +} + +mod patterns { + /// PatKind::Missing + fn pat_missing() { + let _: fn(u32, T, &str); + } + + /// PatKind::Wild + fn pat_wild() { + let _; + } + + /// PatKind::Ident + fn pat_ident() { + let x; + let ref x; + let mut x; + let ref mut x; + let ref mut x @ _; + } + + /// PatKind::Struct + fn pat_struct() { + let T {}; + let T:: {}; + let T::<'static> {}; + let T { x }; + let T { x: _x }; + let T { .. }; + let T { x, .. }; + let T { x: _x, .. }; + let T { 0: _x, .. }; + let ::Owned {}; + } + + /// PatKind::TupleStruct + fn pat_tuple_struct() { + struct Tuple(); + let Tuple(); + let Tuple::(); + let Tuple::<'static>(); + let Tuple(x); + let Tuple(..); + let Tuple(x, ..); + } + + /// PatKind::Or + fn pat_or() { + let (true | false); + let (| true); + let (|true| false); + } + + /// PatKind::Path + fn pat_path() { + let core::marker::PhantomData; + let core::marker::PhantomData::; + let core::marker::PhantomData::<'static>; + let ::CONST; + } + + /// PatKind::Tuple + fn pat_tuple() { + let (); + let (true,); + let (true, false); + } + + /// PatKind::Box + fn pat_box() { + let box pat; + } + + /// PatKind::Deref + fn pat_deref() { + let deref!(pat); + } + + /// PatKind::Ref + fn pat_ref() { + let &pat; + let &mut pat; + } + + /// PatKind::Expr + fn pat_expr() { + let 1_000_i8; + let -""; + } + + /// PatKind::Range + fn pat_range() { + let ..1; + let 0..; + let 0..1; + let 0..=1; + let -2..=-1; + } + + /// PatKind::Slice + fn pat_slice() { + let []; + let [true]; + let [true,]; + let [true, false]; + } + + /// PatKind::Rest + fn pat_rest() { + let ..; //[hir]~ ERROR `..` patterns are not allowed here + } + + /// PatKind::Never + fn pat_never() { + let !; + let Some(!); + } + + /// PatKind::Paren + fn pat_paren() { + let (pat); + } + + /// PatKind::MacCall + fn pat_mac_call() { + let stringify!(); + let stringify![]; + let stringify! {}; + } +} + +mod statements { + /// StmtKind::Let + fn stmt_let() { + let _; + let _ = true; + let _: T = true; + let _ = true else { return; }; + } + + /// StmtKind::Item + fn stmt_item() { + struct Struct {} + struct Unit; + } + + /// StmtKind::Expr + fn stmt_expr() { + () + } + + /// StmtKind::Semi + fn stmt_semi() { + 1 + 1; + } + + /// StmtKind::Empty + fn stmt_empty() { + ; + } + + /// StmtKind::MacCall + fn stmt_mac_call() { + stringify!(...); + stringify![...]; + stringify! { ... }; + } +} + +mod types { + /// TyKind::Slice + fn ty_slice() { + let _: [T]; + } + + /// TyKind::Array + fn ty_array() { + let _: [T; 0]; + } + + /// TyKind::Ptr + fn ty_ptr() { + let _: *const T; + let _: *mut T; + } + + /// TyKind::Ref + fn ty_ref() { + let _: &T; + let _: &mut T; + let _: &'static T; + let _: &'static mut [T]; + let _: &T>>>; + let _: &T > > >; + } + + /// TyKind::BareFn + fn ty_bare_fn() { + let _: fn(); + let _: fn() -> (); + let _: fn(T); + let _: fn(t: T); + let _: for<> fn(); + let _: for<'a> fn(); + } + + /// TyKind::Never + fn ty_never() { + let _: !; + } + + /// TyKind::Tup + fn ty_tup() { + let _: (); + let _: (T,); + let _: (T, T); + } + + /// TyKind::Path + fn ty_path() { + let _: T; + let _: T<'static>; + let _: T; + let _: T::; + let _: T() -> !; //[hir]~ ERROR parenthesized type parameters + let _: ::Owned; + } + + /// TyKind::TraitObject + fn ty_trait_object() { + let _: dyn Send; + let _: dyn Send + 'static; + let _: dyn 'static + Send; + let _: dyn for<'a> Send; + let _: dyn* Send; + } + + /// TyKind::ImplTrait + const fn ty_impl_trait() { + let _: impl Send; //[hir]~ ERROR `impl Trait` is not allowed + let _: impl Send + 'static; //[hir]~ ERROR `impl Trait` is not allowed + let _: impl 'static + Send; //[hir]~ ERROR `impl Trait` is not allowed + let _: impl ?Sized; //[hir]~ ERROR `impl Trait` is not allowed + let _: impl ~const Clone; //[hir]~ ERROR `impl Trait` is not allowed + let _: impl for<'a> Send; //[hir]~ ERROR `impl Trait` is not allowed + } + + /// TyKind::Paren + fn ty_paren() { + let _: (T); + } + + /// TyKind::Typeof + fn ty_typeof() { + /*! unused for now */ + } + + /// TyKind::Infer + fn ty_infer() { + let _: _; + } + + /// TyKind::ImplicitSelf + fn ty_implicit_self() { + /*! there is no syntax for this */ + } + + /// TyKind::MacCall + #[expect(deprecated)] // concat_idents is deprecated + fn ty_mac_call() { + let _: concat_idents!(T); + let _: concat_idents![T]; + let _: concat_idents! { T }; + } + + /// TyKind::CVarArgs + fn ty_c_var_args() { + /*! FIXME: todo */ + } + + /// TyKind::Pat + fn ty_pat() { + let _: core::pattern_type!(u32 is 1..); + } +} + +mod visibilities { + /// VisibilityKind::Public + mod visibility_public { + pub struct Pub; + } + + /// VisibilityKind::Restricted + mod visibility_restricted { + pub(crate) struct PubCrate; + pub(self) struct PubSelf; + pub(super) struct PubSuper; + pub(in crate) struct PubInCrate; + pub(in self) struct PubInSelf; + pub(in super) struct PubInSuper; + pub(in crate::visibilities) struct PubInCrateVisibilities; + pub(in self::super) struct PubInSelfSuper; + pub(in super::visibility_restricted) struct PubInSuperMod; + } +} diff --git a/tests/ui/unpretty/expanded-exhaustive.rs b/tests/ui/unpretty/expanded-exhaustive.rs deleted file mode 100644 index 31af323ecdab5..0000000000000 --- a/tests/ui/unpretty/expanded-exhaustive.rs +++ /dev/null @@ -1,873 +0,0 @@ -//@ compile-flags: -Zunpretty=expanded -//@ edition:2024 -//@ check-pass - -#![feature(auto_traits)] -#![feature(box_patterns)] -#![feature(builtin_syntax)] -#![feature(concat_idents)] -#![feature(const_trait_impl)] -#![feature(decl_macro)] -#![feature(deref_patterns)] -#![feature(dyn_star)] -#![feature(explicit_tail_calls)] -#![feature(gen_blocks)] -#![feature(let_chains)] -#![feature(more_qualified_paths)] -#![feature(never_patterns)] -#![feature(never_type)] -#![feature(pattern_types)] -#![feature(pattern_type_macro)] -#![feature(prelude_import)] -#![feature(specialization)] -#![feature(trace_macros)] -#![feature(trait_alias)] -#![feature(try_blocks)] -#![feature(yeet_expr)] -#![allow(incomplete_features)] - -#[prelude_import] -use self::prelude::*; - -mod prelude { - pub use std::prelude::rust_2024::*; - - pub type T = _; - - pub trait Trait { - const CONST: (); - } -} - -mod attributes { - //! inner single-line doc comment - /*! - * inner multi-line doc comment - */ - #![doc = "inner doc attribute"] - #![allow(dead_code, unused_variables)] - #![no_std] - - /// outer single-line doc comment - /** - * outer multi-line doc comment - */ - #[doc = "outer doc attribute"] - #[doc = concat!("mac", "ro")] - #[allow()] - #[repr(C)] - struct Struct; -} - -mod expressions { - /// ExprKind::Array - fn expr_array() { - []; - [true]; - [true,]; - [true, true]; - ["long........................................................................"]; - ["long............................................................", true]; - } - - /// ExprKind::ConstBlock - fn expr_const_block() { - const {}; - const { 1 }; - const { struct S; }; - } - - /// ExprKind::Call - fn expr_call() { - let f; - f(); - f::(); - f::<1>(); - f::<'static, u8, 1>(); - f(true); - f(true,); - ()(); - } - - /// ExprKind::MethodCall - fn expr_method_call() { - let x; - x.f(); - x.f::(); - x.collect::>(); - } - - /// ExprKind::Tup - fn expr_tup() { - (); - (true,); - (true, false); - (true, false,); - } - - /// ExprKind::Binary - fn expr_binary() { - let (a, b, c, d, x, y); - true || false; - true || false && false; - a < 1 && 2 < b && c > 3 && 4 > d; - a & b & !c; - a + b * c - d + -1 * -2 - -3; - x = !y; - } - - /// ExprKind::Unary - fn expr_unary() { - let expr; - *expr; - !expr; - -expr; - } - - /// ExprKind::Lit - fn expr_lit() { - 'x'; - 1_000_i8; - 1.00000000000000000000001; - } - - /// ExprKind::Cast - fn expr_cast() { - let expr; - expr as T; - expr as T; - } - - /// ExprKind::Type - fn expr_type() { - let expr; - builtin # type_ascribe(expr, T); - } - - /// ExprKind::Let - fn expr_let() { - let b; - if let Some(a) = b {} - if let _ = true && false {} - if let _ = (true && false) {} - } - - /// ExprKind::If - fn expr_if() { - if true {} - if !true {} - if let true = true {} else {} - if true {} else if false {} - if true {} else if false {} else {} - if true { return; } else if false { 0 } else { 0 } - } - - /// ExprKind::While - fn expr_while() { - while false {} - 'a: while false {} - while let true = true {} - } - - /// ExprKind::ForLoop - fn expr_for_loop() { - let x; - for _ in x {} - 'a: for _ in x {} - } - - /// ExprKind::Loop - fn expr_loop() { - loop {} - 'a: loop {} - } - - /// ExprKind::Match - fn expr_match() { - let value; - match value {} - match value { ok => 1 } - match value { - ok => 1, - err => 0, - } - } - - /// ExprKind::Closure - fn expr_closure() { - let value; - || {}; - |x| {}; - |x: u8| {}; - || (); - move || value; - async || value; - async move || value; - static || value; - static move || value; - (static async || value); - (static async move || value); - || -> u8 { value }; - 1 + || {}; - } - - /// ExprKind::Block - fn expr_block() { - {} - unsafe {} - 'a: {} - #[allow()] {} - { #![allow()] } - } - - /// ExprKind::Gen - fn expr_gen() { - async {}; - async move {}; - gen {}; - gen move {}; - async gen {}; - async gen move {}; - } - - /// ExprKind::Await - fn expr_await() { - let fut; - fut.await; - } - - /// ExprKind::TryBlock - fn expr_try_block() { - try {} - try { return; } - } - - /// ExprKind::Assign - fn expr_assign() { - let expr; - expr = true; - } - - /// ExprKind::AssignOp - fn expr_assign_op() { - let expr; - expr += true; - } - - /// ExprKind::Field - fn expr_field() { - let expr; - expr.field; - expr.0; - } - - /// ExprKind::Index - fn expr_index() { - let expr; - expr[true]; - } - - /// ExprKind::Range - fn expr_range() { - let (lo, hi); - ..; - ..hi; - lo..; - lo..hi; - lo .. hi; - ..=hi; - lo..=hi; - -2..=-1; - } - - /// ExprKind::Underscore - fn expr_underscore() { - _; - } - - /// ExprKind::Path - fn expr_path() { - let x; - crate::expressions::expr_path; - crate::expressions::expr_path::<'static>; - ::default; - ::default::<>; - x::(); - x::(T, T) -> T; - crate::() -> ()::expressions::() -> ()::expr_path; - core::()::marker::()::PhantomData; - } - - /// ExprKind::AddrOf - fn expr_addr_of() { - let expr; - &expr; - &mut expr; - &raw const expr; - &raw mut expr; - } - - /// ExprKind::Break - fn expr_break() { - 'a: { - break; - break 'a; - break true; - break 'a true; - } - } - - /// ExprKind::Continue - fn expr_continue() { - 'a: { - continue; - continue 'a; - } - } - - /// ExprKind::Ret - fn expr_ret() { - return; - return true; - } - - /// ExprKind::InlineAsm - fn expr_inline_asm() { - let x; - core::arch::asm!( - "mov {tmp}, {x}", - "shl {tmp}, 1", - "shl {x}, 2", - "add {x}, {tmp}", - x = inout(reg) x, - tmp = out(reg) _, - ); - } - - /// ExprKind::OffsetOf - fn expr_offset_of() { - core::mem::offset_of!(T, field); - } - - /// ExprKind::MacCall - fn expr_mac_call() { - stringify!(...); - stringify![...]; - stringify! { ... }; - } - - /// ExprKind::Struct - fn expr_struct() { - struct Struct {} - let (x, base); - Struct {}; - ::Owned {}; - Struct { .. }; - Struct { .. base }; - Struct { x }; - Struct { x, ..base }; - Struct { x: true }; - Struct { x: true, .. }; - Struct { x: true, ..base }; - Struct { 0: true, ..base }; - } - - /// ExprKind::Repeat - fn expr_repeat() { - [(); 0]; - } - - /// ExprKind::Paren - fn expr_paren() { - let expr; - (expr); - } - - /// ExprKind::Try - fn expr_try() { - let expr; - expr?; - } - - /// ExprKind::Yield - fn expr_yield() { - yield; - yield true; - } - - /// ExprKind::Yeet - fn expr_yeet() { - do yeet; - do yeet 0; - } - - /// ExprKind::Become - fn expr_become() { - become true; - } - - /// ExprKind::IncludedBytes - fn expr_include_bytes() { - include_bytes!("auxiliary/data.txt"); - } - - /// ExprKind::FormatArgs - fn expr_format_args() { - let expr; - format_args!(""); - format_args!("{}", expr); - } -} - -mod items { - /// ItemKind::ExternCrate - mod item_extern_crate { - extern crate core; - extern crate self as unpretty; - pub extern crate core as _; - } - - /// ItemKind::Use - mod item_use { - use crate::{expressions, items::item_use}; - pub use core::*; - } - - /// ItemKind::Static - mod item_static { - pub static A: () = {}; - static mut B: () = {}; - } - - /// ItemKind::Const - mod item_const { - pub const A: () = {}; - - trait TraitItems { - const B: (); - const C: () = {}; - } - } - - /// ItemKind::Fn - mod item_fn { - pub const unsafe extern "C" fn f() {} - pub async unsafe extern "C" fn g() {} - fn h<'a, T>() where T: 'a {} - - trait TraitItems { - unsafe extern "C" fn f(); - } - - impl TraitItems for _ { - default unsafe extern "C" fn f() {} - } - } - - /// ItemKind::Mod - mod item_mod { - // ... - } - - /// ItemKind::ForeignMod - mod item_foreign_mod { - unsafe extern "C++" {} - unsafe extern "C" {} - } - - /// ItemKind::GlobalAsm - mod item_global_asm { - core::arch::global_asm!(".globl my_asm_func"); - } - - /// ItemKind::TyAlias - mod item_ty_alias { - pub type Type<'a> where T: 'a, = T; - } - - /// ItemKind::Enum - mod item_enum { - pub enum Void {} - enum Empty { - Unit, - Tuple(), - Struct {}, - } - enum Generic<'a, T> - where - T: 'a, - { - Tuple(T), - Struct { t: T }, - } - } - - /// ItemKind::Struct - mod item_struct { - pub struct Unit; - struct Tuple(); - struct Newtype(Unit); - struct Struct {} - struct Generic<'a, T> - where - T: 'a, - { - t: T, - } - } - - /// ItemKind::Union - mod item_union { - union Generic<'a, T> - where - T: 'a, - { - t: T, - } - } - - /// ItemKind::Trait - mod item_trait { - pub unsafe auto trait Send {} - trait Trait<'a>: Sized - where - Self: 'a, - { - } - } - - /// ItemKind::TraitAlias - mod item_trait_alias { - pub trait Trait = Sized where for<'a> T: 'a; - } - - /// ItemKind::Impl - mod item_impl { - impl () {} - impl () {} - impl Default for () {} - impl const Default for () {} - } - - /// ItemKind::MacCall - mod item_mac_call { - trace_macros!(false); - trace_macros![false]; - trace_macros! { false } - } - - /// ItemKind::MacroDef - mod item_macro_def { - macro_rules! mac { () => {...}; } - pub macro stringify() {} - } - - /// ItemKind::Delegation - mod item_delegation { - /*! FIXME: todo */ - } - - /// ItemKind::DelegationMac - mod item_delegation_mac { - /*! FIXME: todo */ - } -} - -mod patterns { - /// PatKind::Wild - fn pat_wild() { - let _; - } - - /// PatKind::Ident - fn pat_ident() { - let x; - let ref x; - let mut x; - let ref mut x; - let ref mut x @ _; - } - - /// PatKind::Struct - fn pat_struct() { - let T {}; - let T:: {}; - let T::<'static> {}; - let T { x }; - let T { x: _x }; - let T { .. }; - let T { x, .. }; - let T { x: _x, .. }; - let T { 0: _x, .. }; - let ::Owned {}; - } - - /// PatKind::TupleStruct - fn pat_tuple_struct() { - struct Tuple(); - let Tuple(); - let Tuple::(); - let Tuple::<'static>(); - let Tuple(x); - let Tuple(..); - let Tuple(x, ..); - } - - /// PatKind::Or - fn pat_or() { - let (true | false); - let (| true); - let (|true| false); - } - - /// PatKind::Path - fn pat_path() { - let core::marker::PhantomData; - let core::marker::PhantomData::; - let core::marker::PhantomData::<'static>; - let ::CONST; - } - - /// PatKind::Tuple - fn pat_tuple() { - let (); - let (true,); - let (true, false); - } - - /// PatKind::Box - fn pat_box() { - let box pat; - } - - /// PatKind::Deref - fn pat_deref() { - let deref!(pat); - } - - /// PatKind::Ref - fn pat_ref() { - let &pat; - let &mut pat; - } - - /// PatKind::Expr - fn pat_expr() { - let 1_000_i8; - let -""; - } - - /// PatKind::Range - fn pat_range() { - let ..1; - let 0..; - let 0..1; - let 0..=1; - let -2..=-1; - } - - /// PatKind::Slice - fn pat_slice() { - let []; - let [true]; - let [true,]; - let [true, false]; - } - - /// PatKind::Rest - fn pat_rest() { - let ..; - } - - /// PatKind::Never - fn pat_never() { - let !; - let Some(!); - } - - /// PatKind::Paren - fn pat_paren() { - let (pat); - } - - /// PatKind::MacCall - fn pat_mac_call() { - let stringify!(); - let stringify![]; - let stringify! {}; - } -} - -mod statements { - /// StmtKind::Let - fn stmt_let() { - let _; - let _ = true; - let _: T = true; - let _ = true else { return; }; - } - - /// StmtKind::Item - fn stmt_item() { - struct Struct {} - struct Unit; - } - - /// StmtKind::Expr - fn stmt_expr() { - () - } - - /// StmtKind::Semi - fn stmt_semi() { - 1 + 1; - } - - /// StmtKind::Empty - fn stmt_empty() { - ; - } - - /// StmtKind::MacCall - fn stmt_mac_call() { - stringify!(...); - stringify![...]; - stringify! { ... }; - } -} - -mod types { - /// TyKind::Slice - fn ty_slice() { - let _: [T]; - } - - /// TyKind::Array - fn ty_array() { - let _: [T; 0]; - } - - /// TyKind::Ptr - fn ty_ptr() { - let _: *const T; - let _: *mut T; - } - - /// TyKind::Ref - fn ty_ref() { - let _: &T; - let _: &mut T; - let _: &'static T; - let _: &'static mut [T]; - let _: &T>>>; - let _: &T > > >; - } - - /// TyKind::BareFn - fn ty_bare_fn() { - let _: fn(); - let _: fn() -> (); - let _: fn(T); - let _: fn(t: T); - let _: for<> fn(); - let _: for<'a> fn(); - } - - /// TyKind::Never - fn ty_never() { - let _: !; - } - - /// TyKind::Tup - fn ty_tup() { - let _: (); - let _: (T,); - let _: (T, T); - } - - /// TyKind::Path - fn ty_path() { - let _: T; - let _: T<'static>; - let _: T; - let _: T::; - let _: T() -> !; - let _: ::Owned; - } - - /// TyKind::TraitObject - fn ty_trait_object() { - let _: dyn Send; - let _: dyn Send + 'static; - let _: dyn 'static + Send; - let _: dyn for<'a> Send; - let _: dyn* Send; - } - - /// TyKind::ImplTrait - const fn ty_impl_trait() { - let _: impl Send; - let _: impl Send + 'static; - let _: impl 'static + Send; - let _: impl ?Sized; - let _: impl ~const Clone; - let _: impl for<'a> Send; - } - - /// TyKind::Paren - fn ty_paren() { - let _: (T); - } - - /// TyKind::Typeof - fn ty_typeof() { - /*! unused for now */ - } - - /// TyKind::Infer - fn ty_infer() { - let _: _; - } - - /// TyKind::ImplicitSelf - fn ty_implicit_self() { - /*! there is no syntax for this */ - } - - /// TyKind::MacCall - fn ty_mac_call() { - let _: concat_idents!(T); - let _: concat_idents![T]; - let _: concat_idents! { T }; - } - - /// TyKind::CVarArgs - fn ty_c_var_args() { - /*! FIXME: todo */ - } - - /// TyKind::Pat - fn ty_pat() { - let _: core::pattern_type!(u32 is 1..); - } -} - -mod visibilities { - /// VisibilityKind::Public - mod visibility_public { - pub struct Pub; - } - - /// VisibilityKind::Restricted - mod visibility_restricted { - pub(crate) struct PubCrate; - pub(self) struct PubSelf; - pub(super) struct PubSuper; - pub(in crate) struct PubInCrate; - pub(in self) struct PubInSelf; - pub(in super) struct PubInSuper; - pub(in crate::visibilities) struct PubInCrateVisibilities; - pub(in self::super) struct PubInSelfSuper; - pub(in super::visibility_restricted) struct PubInSuperMod; - } -} diff --git a/tests/ui/unpretty/expanded-exhaustive.stdout b/tests/ui/unpretty/expanded-exhaustive.stdout deleted file mode 100644 index 19ae66f7a07aa..0000000000000 --- a/tests/ui/unpretty/expanded-exhaustive.stdout +++ /dev/null @@ -1,700 +0,0 @@ -#![feature(prelude_import)] -//@ compile-flags: -Zunpretty=expanded -//@ edition:2024 -//@ check-pass - -#![feature(auto_traits)] -#![feature(box_patterns)] -#![feature(builtin_syntax)] -#![feature(concat_idents)] -#![feature(const_trait_impl)] -#![feature(decl_macro)] -#![feature(deref_patterns)] -#![feature(dyn_star)] -#![feature(explicit_tail_calls)] -#![feature(gen_blocks)] -#![feature(let_chains)] -#![feature(more_qualified_paths)] -#![feature(never_patterns)] -#![feature(never_type)] -#![feature(pattern_types)] -#![feature(pattern_type_macro)] -#![feature(prelude_import)] -#![feature(specialization)] -#![feature(trace_macros)] -#![feature(trait_alias)] -#![feature(try_blocks)] -#![feature(yeet_expr)] -#![allow(incomplete_features)] -#[prelude_import] -use std::prelude::rust_2024::*; -#[macro_use] -extern crate std; - -#[prelude_import] -use self::prelude::*; - -mod prelude { - pub use std::prelude::rust_2024::*; - - pub type T = _; - - pub trait Trait { - const CONST: (); - } -} - -mod attributes { - //! inner single-line doc comment - /*! - * inner multi-line doc comment - */ - #![doc = "inner doc attribute"] - #![allow(dead_code, unused_variables)] - #![no_std] - - /// outer single-line doc comment - /** - * outer multi-line doc comment - */ - #[doc = "outer doc attribute"] - #[doc = "macro"] - #[allow()] - #[repr(C)] - struct Struct; -} - -mod expressions { - /// ExprKind::Array - fn expr_array() { - []; - [true]; - [true]; - [true, true]; - ["long........................................................................"]; - ["long............................................................", - true]; - } - - /// ExprKind::ConstBlock - fn expr_const_block() { - const {}; - const { 1 }; - const { - struct S; - }; - } - - /// ExprKind::Call - fn expr_call() { - let f; - f(); - f::(); - f::<1>(); - f::<'static, u8, 1>(); - f(true); - f(true); - ()(); - } - - /// ExprKind::MethodCall - fn expr_method_call() { - let x; - x.f(); - x.f::(); - x.collect::>(); - } - - /// ExprKind::Tup - fn expr_tup() { (); (true,); (true, false); (true, false); } - - /// ExprKind::Binary - fn expr_binary() { - let (a, b, c, d, x, y); - true || false; - true || false && false; - a < 1 && 2 < b && c > 3 && 4 > d; - a & b & !c; - a + b * c - d + -1 * -2 - -3; - x = !y; - } - - /// ExprKind::Unary - fn expr_unary() { let expr; *expr; !expr; -expr; } - - /// ExprKind::Lit - fn expr_lit() { 'x'; 1_000_i8; 1.00000000000000000000001; } - - /// ExprKind::Cast - fn expr_cast() { let expr; expr as T; expr as T; } - - /// ExprKind::Type - fn expr_type() { let expr; builtin # type_ascribe(expr, T); } - - /// ExprKind::Let - fn expr_let() { - let b; - if let Some(a) = b {} - if let _ = true && false {} - if let _ = (true && false) {} - } - - /// ExprKind::If - fn expr_if() { - if true {} - if !true {} - if let true = true {} else {} - if true {} else if false {} - if true {} else if false {} else {} - if true { return; } else if false { 0 } else { 0 } - } - - /// ExprKind::While - fn expr_while() { - while false {} - 'a: while false {} - while let true = true {} - } - - /// ExprKind::ForLoop - fn expr_for_loop() { let x; for _ in x {} 'a: for _ in x {} } - - /// ExprKind::Loop - fn expr_loop() { loop {} 'a: loop {} } - - /// ExprKind::Match - fn expr_match() { - let value; - match value {} - match value { ok => 1, } - match value { ok => 1, err => 0, } - } - - /// ExprKind::Closure - fn expr_closure() { - let value; - || {}; - |x| {}; - |x: u8| {}; - || (); - move || value; - async || value; - async move || value; - static || value; - static move || value; - (static async || value); - (static async move || value); - || -> u8 { value }; - 1 + (|| {}); - } - - /// ExprKind::Block - fn expr_block() { - {} - unsafe {} - 'a: {} - - #[allow()] - {} - { - #![allow()] - } - } - - /// ExprKind::Gen - fn expr_gen() { - async {}; - async move {}; - gen {}; - gen move {}; - async gen {}; - async gen move {}; - } - - /// ExprKind::Await - fn expr_await() { let fut; fut.await; } - - /// ExprKind::TryBlock - fn expr_try_block() { try {} try { return; } } - - /// ExprKind::Assign - fn expr_assign() { let expr; expr = true; } - - /// ExprKind::AssignOp - fn expr_assign_op() { let expr; expr += true; } - - /// ExprKind::Field - fn expr_field() { let expr; expr.field; expr.0; } - - /// ExprKind::Index - fn expr_index() { let expr; expr[true]; } - - /// ExprKind::Range - fn expr_range() { - let (lo, hi); - ..; - ..hi; - lo..; - lo..hi; - lo..hi; - ..=hi; - lo..=hi; - -2..=-1; - } - - /// ExprKind::Underscore - fn expr_underscore() { _; } - - /// ExprKind::Path - fn expr_path() { - let x; - crate::expressions::expr_path; - crate::expressions::expr_path::<'static>; - ::default; - ::default::<>; - x::(); - x::(T, T) -> T; - crate::() -> ()::expressions::() -> ()::expr_path; - core::()::marker::()::PhantomData; - } - - /// ExprKind::AddrOf - fn expr_addr_of() { - let expr; - &expr; - &mut expr; - &raw const expr; - &raw mut expr; - } - - /// ExprKind::Break - fn expr_break() { 'a: { break; break 'a; break true; break 'a true; } } - - /// ExprKind::Continue - fn expr_continue() { 'a: { continue; continue 'a; } } - - /// ExprKind::Ret - fn expr_ret() { return; return true; } - - /// ExprKind::InlineAsm - fn expr_inline_asm() { - let x; - asm!("mov {1}, {0}\nshl {1}, 1\nshl {0}, 2\nadd {0}, {1}", - inout(reg) - x, - out(reg) - _); - } - - /// ExprKind::OffsetOf - fn expr_offset_of() { - - - - - - - - - - - - - - - - - - - - // ... - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { builtin # offset_of(T, field) }; - } - /// ExprKind::MacCall - fn expr_mac_call() { "..."; "..."; "..."; } - /// ExprKind::Struct - fn expr_struct() { - struct Struct {} - let (x, base); - Struct {}; - ::Owned {}; - Struct { .. }; - Struct { ..base }; - Struct { x }; - Struct { x, ..base }; - Struct { x: true }; - Struct { x: true, .. }; - Struct { x: true, ..base }; - Struct { 0: true, ..base }; - } - /// ExprKind::Repeat - fn expr_repeat() { [(); 0]; } - /// ExprKind::Paren - fn expr_paren() { let expr; (expr); } - /// ExprKind::Try - fn expr_try() { let expr; expr?; } - /// ExprKind::Yield - fn expr_yield() { yield; yield true; } - /// ExprKind::Yeet - fn expr_yeet() { do yeet; do yeet 0; } - /// ExprKind::Become - fn expr_become() { become true; } - /// ExprKind::IncludedBytes - fn expr_include_bytes() { - b"data for include_bytes in ../expanded-exhaustive.rs\n"; - } - /// ExprKind::FormatArgs - fn expr_format_args() { - let expr; - format_args!(""); - format_args!("{0}", expr); - } -} -mod items { - /// ItemKind::ExternCrate - mod item_extern_crate { - extern crate core; - extern crate self as unpretty; - pub extern crate core as _; - } - /// ItemKind::Use - mod item_use { - use crate::{expressions, items::item_use}; - pub use core::*; - } - /// ItemKind::Static - mod item_static { - pub static A: () = {}; - static mut B: () = {}; - } - /// ItemKind::Const - mod item_const { - pub const A: () = {}; - trait TraitItems { - const B: (); - const C: () = {}; - } - } - /// ItemKind::Fn - mod item_fn { - pub const unsafe extern "C" fn f() {} - pub async unsafe extern "C" fn g() {} - fn h<'a, T>() where T: 'a {} - trait TraitItems { - unsafe extern "C" fn f(); - } - impl TraitItems for _ { - default unsafe extern "C" fn f() {} - } - } - /// ItemKind::Mod - mod item_mod { } - /// ItemKind::ForeignMod - mod item_foreign_mod { - unsafe extern "C++" {} - unsafe extern "C" {} - } - /// ItemKind::GlobalAsm - mod item_global_asm { - global_asm! (".globl my_asm_func"); - } - /// ItemKind::TyAlias - mod item_ty_alias { - pub type Type<'a> where T: 'a = T; - } - /// ItemKind::Enum - mod item_enum { - pub enum Void {} - enum Empty { Unit, Tuple(), Struct {}, } - enum Generic<'a, T> where T: 'a { - Tuple(T), - Struct { - t: T, - }, - } - } - /// ItemKind::Struct - mod item_struct { - pub struct Unit; - struct Tuple(); - struct Newtype(Unit); - struct Struct {} - struct Generic<'a, T> where T: 'a { - t: T, - } - } - /// ItemKind::Union - mod item_union { - union Generic<'a, T> where T: 'a { - t: T, - } - } - /// ItemKind::Trait - mod item_trait { - pub unsafe auto trait Send {} - trait Trait<'a>: Sized where Self: 'a {} - } - /// ItemKind::TraitAlias - mod item_trait_alias { - pub trait Trait = Sized where for<'a> T: 'a; - } - /// ItemKind::Impl - mod item_impl { - impl () {} - impl () {} - impl Default for () {} - impl const Default for () {} - } - /// ItemKind::MacCall - mod item_mac_call { } - /// ItemKind::MacroDef - mod item_macro_def { - macro_rules! mac { () => { ... }; } - pub macro stringify { () => {} } - } - /// ItemKind::Delegation - mod item_delegation { - /*! FIXME: todo */ - } - /// ItemKind::DelegationMac - mod item_delegation_mac { - /*! FIXME: todo */ - } -} -mod patterns { - /// PatKind::Wild - fn pat_wild() { let _; } - /// PatKind::Ident - fn pat_ident() { - let x; - let ref x; - let mut x; - let ref mut x; - let ref mut x @ _; - } - /// PatKind::Struct - fn pat_struct() { - let T {}; - let T:: {}; - let T::<'static> {}; - let T { x }; - let T { x: _x }; - let T { .. }; - let T { x, .. }; - let T { x: _x, .. }; - let T { 0: _x, .. }; - let ::Owned {}; - } - /// PatKind::TupleStruct - fn pat_tuple_struct() { - struct Tuple(); - let Tuple(); - let Tuple::(); - let Tuple::<'static>(); - let Tuple(x); - let Tuple(..); - let Tuple(x, ..); - } - /// PatKind::Or - fn pat_or() { let (true | false); let (true); let (true | false); } - /// PatKind::Path - fn pat_path() { - let core::marker::PhantomData; - let core::marker::PhantomData::; - let core::marker::PhantomData::<'static>; - let ::CONST; - } - /// PatKind::Tuple - fn pat_tuple() { let (); let (true,); let (true, false); } - /// PatKind::Box - fn pat_box() { let box pat; } - /// PatKind::Deref - fn pat_deref() { let deref!(pat); } - /// PatKind::Ref - fn pat_ref() { let &pat; let &mut pat; } - /// PatKind::Expr - fn pat_expr() { let 1_000_i8; let -""; } - /// PatKind::Range - fn pat_range() { let ..1; let 0..; let 0..1; let 0..=1; let -2..=-1; } - /// PatKind::Slice - fn pat_slice() { let []; let [true]; let [true]; let [true, false]; } - /// PatKind::Rest - fn pat_rest() { let ..; } - /// PatKind::Never - fn pat_never() { let !; let Some(!); } - /// PatKind::Paren - fn pat_paren() { let (pat); } - /// PatKind::MacCall - fn pat_mac_call() { let ""; let ""; let ""; } -} -mod statements { - /// StmtKind::Let - fn stmt_let() { - let _; - let _ = true; - let _: T = true; - let _ = true else { return; }; - } - /// StmtKind::Item - fn stmt_item() { - struct Struct {} - struct Unit; - } - /// StmtKind::Expr - fn stmt_expr() { () } - /// StmtKind::Semi - fn stmt_semi() { 1 + 1; } - /// StmtKind::Empty - fn stmt_empty() { ; } - /// StmtKind::MacCall - fn stmt_mac_call() { "..."; "..."; "..."; } -} -mod types { - /// TyKind::Slice - fn ty_slice() { let _: [T]; } - /// TyKind::Array - fn ty_array() { let _: [T; 0]; } - /// TyKind::Ptr - fn ty_ptr() { let _: *const T; let _: *mut T; } - /// TyKind::Ref - fn ty_ref() { - let _: &T; - let _: &mut T; - let _: &'static T; - let _: &'static mut [T]; - let _: &T>>>; - let _: &T>>>; - } - /// TyKind::BareFn - fn ty_bare_fn() { - let _: fn(); - let _: fn() -> (); - let _: fn(T); - let _: fn(t: T); - let _: fn(); - let _: for<'a> fn(); - } - /// TyKind::Never - fn ty_never() { let _: !; } - /// TyKind::Tup - fn ty_tup() { let _: (); let _: (T,); let _: (T, T); } - /// TyKind::Path - fn ty_path() { - let _: T; - let _: T<'static>; - let _: T; - let _: T; - let _: T() -> !; - let _: ::Owned; - } - /// TyKind::TraitObject - fn ty_trait_object() { - let _: dyn Send; - let _: dyn Send + 'static; - let _: dyn 'static + Send; - let _: dyn for<'a> Send; - let _: dyn* Send; - } - /// TyKind::ImplTrait - const fn ty_impl_trait() { - let _: impl Send; - let _: impl Send + 'static; - let _: impl 'static + Send; - let _: impl ?Sized; - let _: impl ~const Clone; - let _: impl for<'a> Send; - } - /// TyKind::Paren - fn ty_paren() { let _: (T); } - /// TyKind::Typeof - fn ty_typeof() { - /*! unused for now */ - } - /// TyKind::Infer - fn ty_infer() { let _: _; } - /// TyKind::ImplicitSelf - fn ty_implicit_self() { - /*! there is no syntax for this */ - } - /// TyKind::MacCall - fn ty_mac_call() { let _: T; let _: T; let _: T; } - /// TyKind::CVarArgs - fn ty_c_var_args() { - /*! FIXME: todo */ - } - /// TyKind::Pat - fn ty_pat() { let _: u32 is const 1..; } -} -mod visibilities { - /// VisibilityKind::Public - mod visibility_public { - pub struct Pub; - } - /// VisibilityKind::Restricted - mod visibility_restricted { - pub(crate) struct PubCrate; - pub(self) struct PubSelf; - pub(super) struct PubSuper; - pub(in crate) struct PubInCrate; - pub(in self) struct PubInSelf; - pub(in super) struct PubInSuper; - pub(in crate::visibilities) struct PubInCrateVisibilities; - pub(in self::super) struct PubInSelfSuper; - pub(in super::visibility_restricted) struct PubInSuperMod; - } -} diff --git a/tests/ui/unpretty/expanded-interpolation.rs b/tests/ui/unpretty/expanded-interpolation.rs deleted file mode 100644 index 1dc72c67f511e..0000000000000 --- a/tests/ui/unpretty/expanded-interpolation.rs +++ /dev/null @@ -1,126 +0,0 @@ -//@ compile-flags: -Zunpretty=expanded -//@ check-pass - -// This test covers the AST pretty-printer's insertion of parentheses in some -// macro metavariable edge cases. Synthetic parentheses (i.e. not appearing in -// the syntax tree) need to be printed in order for the printed code to be valid -// Rust syntax. We also test negative cases: the pretty-printer should not be -// synthesizing parentheses indiscriminately; only where necessary. - -#![feature(let_chains)] -#![feature(if_let_guard)] - -macro_rules! expr { - ($expr:expr) => { $expr }; -} - -macro_rules! stmt { - ($stmt:stmt) => { $stmt }; -} - -fn break_labeled_loop() { - let no_paren = 'outer: loop { - break 'outer expr!('inner: loop { break 'inner 1; } + 1); - }; - - let paren_around_break_value = 'outer: loop { - break expr!('inner: loop { break 'inner 1; } + 1); - }; - - macro_rules! breaking { - ($value:expr) => { - break $value - }; - } - - let paren_around_break_value = loop { - breaking!('inner: loop { break 'inner 1; } + 1); - }; -} - -fn if_let() { - macro_rules! if_let { - ($pat:pat, $expr:expr) => { - if let $pat = $expr {} - }; - } - - if let no_paren = true && false {} - if_let!(paren_around_binary, true && false); - if_let!(no_paren, true); - - struct Struct {} - match () { - _ if let no_paren = Struct {} => {} - } -} - -fn let_else() { - let no_paren = expr!(1 + 1) else { return; }; - let paren_around_loop = expr!(loop {}) else { return; }; -} - -fn local() { - macro_rules! let_expr_minus_one { - ($pat:pat, $expr:expr) => { - let $pat = $expr - 1; - }; - } - - let void; - let_expr_minus_one!(no_paren, match void {}); - - macro_rules! let_expr_else_return { - ($pat:pat, $expr:expr) => { - let $pat = $expr else { return; }; - }; - } - - let_expr_else_return!(no_paren, void()); -} - -fn match_arm() { - macro_rules! match_arm { - ($pat:pat, $expr:expr) => { - match () { $pat => $expr } - }; - } - - match_arm!(no_paren, 1 - 1); - match_arm!(paren_around_brace, { 1 } - 1); -} - -/// https://github.com/rust-lang/rust/issues/98790 -fn stmt_boundary() { - macro_rules! expr_as_stmt { - ($expr:expr) => { - stmt!($expr) - }; - } - - let paren_around_match; - expr_as_stmt!(match paren_around_match {} | true); - - macro_rules! minus_one { - ($expr:expr) => { - expr_as_stmt!($expr - 1) - }; - } - - let (no_paren, paren_around_loop); - minus_one!(no_paren); - minus_one!(match paren_around_match {}); - minus_one!(match paren_around_match {}()); - minus_one!(match paren_around_match {}[0]); - minus_one!(loop { break paren_around_loop; }); -} - -fn vis_inherited() { - macro_rules! vis_inherited { - ($vis:vis struct) => { - $vis struct Struct; - }; - } - - vis_inherited!(struct); -} diff --git a/tests/ui/unpretty/expanded-interpolation.stdout b/tests/ui/unpretty/expanded-interpolation.stdout deleted file mode 100644 index 556e57dbd9210..0000000000000 --- a/tests/ui/unpretty/expanded-interpolation.stdout +++ /dev/null @@ -1,105 +0,0 @@ -#![feature(prelude_import)] -#![no_std] -//@ compile-flags: -Zunpretty=expanded -//@ check-pass - -// This test covers the AST pretty-printer's insertion of parentheses in some -// macro metavariable edge cases. Synthetic parentheses (i.e. not appearing in -// the syntax tree) need to be printed in order for the printed code to be valid -// Rust syntax. We also test negative cases: the pretty-printer should not be -// synthesizing parentheses indiscriminately; only where necessary. - -#![feature(let_chains)] -#![feature(if_let_guard)] -#[prelude_import] -use ::std::prelude::rust_2015::*; -#[macro_use] -extern crate std; - -macro_rules! expr { ($expr:expr) => { $expr }; } - -macro_rules! stmt { ($stmt:stmt) => { $stmt }; } - -fn break_labeled_loop() { - let no_paren = - 'outer: loop { break 'outer 'inner: loop { break 'inner 1; } + 1; }; - - let paren_around_break_value = - 'outer: loop { break ('inner: loop { break 'inner 1; } + 1); }; - - macro_rules! breaking { ($value:expr) => { break $value }; } - - let paren_around_break_value = - loop { break ('inner: loop { break 'inner 1; } + 1); }; -} - -fn if_let() { - macro_rules! if_let { - ($pat:pat, $expr:expr) => { if let $pat = $expr {} }; - } - - if let no_paren = true && false {} - if let paren_around_binary = (true && false) {}; - if let no_paren = true {}; - - struct Struct {} - match () { _ if let no_paren = Struct {} => {} } -} - -fn let_else() { - let no_paren = 1 + 1 else { return; }; - let paren_around_loop = (loop {}) else { return; }; -} - -fn local() { - macro_rules! let_expr_minus_one { - ($pat:pat, $expr:expr) => { let $pat = $expr - 1; }; - } - - let void; - let no_paren = match void {} - 1; - - macro_rules! let_expr_else_return { - ($pat:pat, $expr:expr) => { let $pat = $expr else { return; }; }; - } - let - - no_paren = void() else { return; }; -} - -fn match_arm() { - macro_rules! match_arm { - ($pat:pat, $expr:expr) => { match () { $pat => $expr } }; - } - match () { - - - no_paren => 1 - 1, - }; - match () { paren_around_brace => ({ 1 }) - 1, }; -} - -/// https://github.com/rust-lang/rust/issues/98790 -fn stmt_boundary() { - macro_rules! expr_as_stmt { ($expr:expr) => { stmt!($expr) }; } - - let paren_around_match; - (match paren_around_match {}) | true; - - macro_rules! minus_one { ($expr:expr) => { expr_as_stmt!($expr - 1) }; } - - let (no_paren, paren_around_loop); - no_paren - 1; - (match paren_around_match {}) - 1; - (match paren_around_match {})() - 1; - (match paren_around_match {})[0] - 1; - (loop { break paren_around_loop; }) - 1; -} - -fn vis_inherited() { - macro_rules! vis_inherited { - ($vis:vis struct) => { $vis struct Struct; }; - } - struct Struct; - -} diff --git a/tests/ui/unpretty/flattened-format-args.rs b/tests/ui/unpretty/flattened-format-args.rs index 772f44cc268ec..ab065f494dc00 100644 --- a/tests/ui/unpretty/flattened-format-args.rs +++ b/tests/ui/unpretty/flattened-format-args.rs @@ -1,5 +1,6 @@ //@ compile-flags: -Zunpretty=hir -Zflatten-format-args=yes //@ check-pass +//@ edition: 2015 fn main() { let x = 1; diff --git a/tests/ui/unpretty/flattened-format-args.stdout b/tests/ui/unpretty/flattened-format-args.stdout index 2de1cdd96b5b7..a5d943281ad8d 100644 --- a/tests/ui/unpretty/flattened-format-args.stdout +++ b/tests/ui/unpretty/flattened-format-args.stdout @@ -4,6 +4,7 @@ use ::std::prelude::rust_2015::*; extern crate std; //@ compile-flags: -Zunpretty=hir -Zflatten-format-args=yes //@ check-pass +//@ edition: 2015 fn main() { let x = 1; diff --git a/tests/ui/unpretty/hir-tree.rs b/tests/ui/unpretty/hir-tree.rs index 3388c60c42535..41ac51641c5e1 100644 --- a/tests/ui/unpretty/hir-tree.rs +++ b/tests/ui/unpretty/hir-tree.rs @@ -3,8 +3,9 @@ //@ check-stdout //@ dont-check-compiler-stdout //@ dont-check-compiler-stderr -//@ regex-error-pattern: Hello, Rustaceans! fn main() { println!("Hello, Rustaceans!"); } + +//~? RAW Hello, Rustaceans! diff --git a/tests/ui/unpretty/interpolation-expanded.rs b/tests/ui/unpretty/interpolation-expanded.rs new file mode 100644 index 0000000000000..95280f97dac14 --- /dev/null +++ b/tests/ui/unpretty/interpolation-expanded.rs @@ -0,0 +1,126 @@ +//@ compile-flags: -Zunpretty=expanded +//@ edition:2024 +//@ check-pass + +// This test covers the AST pretty-printer's insertion of parentheses in some +// macro metavariable edge cases. Synthetic parentheses (i.e. not appearing in +// the syntax tree) need to be printed in order for the printed code to be valid +// Rust syntax. We also test negative cases: the pretty-printer should not be +// synthesizing parentheses indiscriminately; only where necessary. + +#![feature(if_let_guard)] + +macro_rules! expr { + ($expr:expr) => { $expr }; +} + +macro_rules! stmt { + ($stmt:stmt) => { $stmt }; +} + +fn break_labeled_loop() { + let no_paren = 'outer: loop { + break 'outer expr!('inner: loop { break 'inner 1; } + 1); + }; + + let paren_around_break_value = 'outer: loop { + break expr!('inner: loop { break 'inner 1; } + 1); + }; + + macro_rules! breaking { + ($value:expr) => { + break $value + }; + } + + let paren_around_break_value = loop { + breaking!('inner: loop { break 'inner 1; } + 1); + }; +} + +fn if_let() { + macro_rules! if_let { + ($pat:pat, $expr:expr) => { + if let $pat = $expr {} + }; + } + + if let no_paren = true && false {} + if_let!(paren_around_binary, true && false); + if_let!(no_paren, true); + + struct Struct {} + match () { + _ if let no_paren = Struct {} => {} + } +} + +fn let_else() { + let no_paren = expr!(1 + 1) else { return; }; + let paren_around_loop = expr!(loop {}) else { return; }; +} + +fn local() { + macro_rules! let_expr_minus_one { + ($pat:pat, $expr:expr) => { + let $pat = $expr - 1; + }; + } + + let void; + let_expr_minus_one!(no_paren, match void {}); + + macro_rules! let_expr_else_return { + ($pat:pat, $expr:expr) => { + let $pat = $expr else { return; }; + }; + } + + let_expr_else_return!(no_paren, void()); +} + +fn match_arm() { + macro_rules! match_arm { + ($pat:pat, $expr:expr) => { + match () { $pat => $expr } + }; + } + + match_arm!(no_paren, 1 - 1); + match_arm!(paren_around_brace, { 1 } - 1); +} + +/// https://github.com/rust-lang/rust/issues/98790 +fn stmt_boundary() { + macro_rules! expr_as_stmt { + ($expr:expr) => { + stmt!($expr) + }; + } + + let paren_around_match; + expr_as_stmt!(match paren_around_match {} | true); + + macro_rules! minus_one { + ($expr:expr) => { + expr_as_stmt!($expr - 1) + }; + } + + let (no_paren, paren_around_loop); + minus_one!(no_paren); + minus_one!(match paren_around_match {}); + minus_one!(match paren_around_match {}()); + minus_one!(match paren_around_match {}[0]); + minus_one!(loop { break paren_around_loop; }); +} + +fn vis_inherited() { + macro_rules! vis_inherited { + ($vis:vis struct) => { + $vis struct Struct; + }; + } + + vis_inherited!(struct); +} diff --git a/tests/ui/unpretty/interpolation-expanded.stdout b/tests/ui/unpretty/interpolation-expanded.stdout new file mode 100644 index 0000000000000..d46b46b67f41f --- /dev/null +++ b/tests/ui/unpretty/interpolation-expanded.stdout @@ -0,0 +1,104 @@ +#![feature(prelude_import)] +//@ compile-flags: -Zunpretty=expanded +//@ edition:2024 +//@ check-pass + +// This test covers the AST pretty-printer's insertion of parentheses in some +// macro metavariable edge cases. Synthetic parentheses (i.e. not appearing in +// the syntax tree) need to be printed in order for the printed code to be valid +// Rust syntax. We also test negative cases: the pretty-printer should not be +// synthesizing parentheses indiscriminately; only where necessary. + +#![feature(if_let_guard)] +#[prelude_import] +use std::prelude::rust_2024::*; +#[macro_use] +extern crate std; + +macro_rules! expr { ($expr:expr) => { $expr }; } + +macro_rules! stmt { ($stmt:stmt) => { $stmt }; } + +fn break_labeled_loop() { + let no_paren = + 'outer: loop { break 'outer 'inner: loop { break 'inner 1; } + 1; }; + + let paren_around_break_value = + 'outer: loop { break ('inner: loop { break 'inner 1; } + 1); }; + + macro_rules! breaking { ($value:expr) => { break $value }; } + + let paren_around_break_value = + loop { break ('inner: loop { break 'inner 1; } + 1); }; +} + +fn if_let() { + macro_rules! if_let { + ($pat:pat, $expr:expr) => { if let $pat = $expr {} }; + } + + if let no_paren = true && false {} + if let paren_around_binary = (true && false) {}; + if let no_paren = true {}; + + struct Struct {} + match () { _ if let no_paren = Struct {} => {} } +} + +fn let_else() { + let no_paren = 1 + 1 else { return; }; + let paren_around_loop = (loop {}) else { return; }; +} + +fn local() { + macro_rules! let_expr_minus_one { + ($pat:pat, $expr:expr) => { let $pat = $expr - 1; }; + } + + let void; + let no_paren = match void {} - 1; + + macro_rules! let_expr_else_return { + ($pat:pat, $expr:expr) => { let $pat = $expr else { return; }; }; + } + let + + no_paren = void() else { return; }; +} + +fn match_arm() { + macro_rules! match_arm { + ($pat:pat, $expr:expr) => { match () { $pat => $expr } }; + } + match () { + + + no_paren => 1 - 1, + }; + match () { paren_around_brace => ({ 1 }) - 1, }; +} + +/// https://github.com/rust-lang/rust/issues/98790 +fn stmt_boundary() { + macro_rules! expr_as_stmt { ($expr:expr) => { stmt!($expr) }; } + + let paren_around_match; + (match paren_around_match {}) | true; + + macro_rules! minus_one { ($expr:expr) => { expr_as_stmt!($expr - 1) }; } + + let (no_paren, paren_around_loop); + no_paren - 1; + (match paren_around_match {}) - 1; + (match paren_around_match {})() - 1; + (match paren_around_match {})[0] - 1; + (loop { break paren_around_loop; }) - 1; +} + +fn vis_inherited() { + macro_rules! vis_inherited { + ($vis:vis struct) => { $vis struct Struct; }; + } + struct Struct; + +} diff --git a/tests/ui/unpretty/let-else-hir.rs b/tests/ui/unpretty/let-else-hir.rs index 9c231189659af..786c84a09dddf 100644 --- a/tests/ui/unpretty/let-else-hir.rs +++ b/tests/ui/unpretty/let-else-hir.rs @@ -1,5 +1,6 @@ //@ compile-flags: -Zunpretty=hir //@ check-pass +//@ edition: 2015 diff --git a/tests/ui/unpretty/let-else-hir.stdout b/tests/ui/unpretty/let-else-hir.stdout index a2ffa5de5673c..a6dd943ec1b7e 100644 --- a/tests/ui/unpretty/let-else-hir.stdout +++ b/tests/ui/unpretty/let-else-hir.stdout @@ -4,6 +4,7 @@ use ::std::prelude::rust_2015::*; extern crate std; //@ compile-flags: -Zunpretty=hir //@ check-pass +//@ edition: 2015 diff --git a/tests/ui/unpretty/self-hir.rs b/tests/ui/unpretty/self-hir.rs index 448d828d4446f..70e0ba589fb8f 100644 --- a/tests/ui/unpretty/self-hir.rs +++ b/tests/ui/unpretty/self-hir.rs @@ -1,5 +1,6 @@ //@ compile-flags: -Zunpretty=hir //@ check-pass +//@ edition: 2015 pub struct Bar { a: String, diff --git a/tests/ui/unpretty/self-hir.stdout b/tests/ui/unpretty/self-hir.stdout index 4da080dc611e8..a9e80b1f59201 100644 --- a/tests/ui/unpretty/self-hir.stdout +++ b/tests/ui/unpretty/self-hir.stdout @@ -4,6 +4,7 @@ use ::std::prelude::rust_2015::*; extern crate std; //@ compile-flags: -Zunpretty=hir //@ check-pass +//@ edition: 2015 struct Bar { a: String, diff --git a/tests/ui/unpretty/unpretty-expr-fn-arg.rs b/tests/ui/unpretty/unpretty-expr-fn-arg.rs index 7f496e773c28e..b2ab2e0911e52 100644 --- a/tests/ui/unpretty/unpretty-expr-fn-arg.rs +++ b/tests/ui/unpretty/unpretty-expr-fn-arg.rs @@ -6,6 +6,7 @@ //@ check-pass //@ compile-flags: -Zunpretty=hir,typed +//@ edition: 2015 #![allow(dead_code)] fn main() {} diff --git a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout index 43aa93c83bd31..fd2e794fcac89 100644 --- a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout +++ b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout @@ -6,6 +6,7 @@ //@ check-pass //@ compile-flags: -Zunpretty=hir,typed +//@ edition: 2015 #![allow(dead_code)] #[prelude_import] use ::std::prelude::rust_2015::*; diff --git a/tests/ui/unresolved/unresolved-import.rs b/tests/ui/unresolved/unresolved-import.rs index 763e9496734dd..b0fdcf970155b 100644 --- a/tests/ui/unresolved/unresolved-import.rs +++ b/tests/ui/unresolved/unresolved-import.rs @@ -31,6 +31,8 @@ mod food { mod zug { pub mod baz { + //~^ NOTE module `food::zug::baz` exists but is inaccessible + //~| NOTE not accessible pub struct Foobar; } } diff --git a/tests/ui/unresolved/unresolved-import.stderr b/tests/ui/unresolved/unresolved-import.stderr index c65fe841001d7..4001695459a90 100644 --- a/tests/ui/unresolved/unresolved-import.stderr +++ b/tests/ui/unresolved/unresolved-import.stderr @@ -26,6 +26,12 @@ LL | use food::baz; | | | | | help: a similar name exists in the module: `bag` | no `baz` in `food` + | +note: module `food::zug::baz` exists but is inaccessible + --> $DIR/unresolved-import.rs:33:9 + | +LL | pub mod baz { + | ^^^^^^^^^^^ not accessible error[E0432]: unresolved import `food::beens` --> $DIR/unresolved-import.rs:19:12 @@ -37,13 +43,13 @@ LL | use food::{beens as Foo}; | help: a similar name exists in the module: `beans` error[E0432]: unresolved import `MyEnum` - --> $DIR/unresolved-import.rs:44:9 + --> $DIR/unresolved-import.rs:46:9 | LL | use MyEnum::*; | ^^^^^^ help: a similar path exists: `self::MyEnum` error[E0432]: unresolved import `Enum` - --> $DIR/unresolved-import.rs:55:9 + --> $DIR/unresolved-import.rs:57:9 | LL | use Enum::*; | ^^^^ help: a similar path exists: `self::Enum` diff --git a/tests/ui/unsafe-binders/lifetime-resolution.rs b/tests/ui/unsafe-binders/lifetime-resolution.rs index b352acfadf284..d243154011947 100644 --- a/tests/ui/unsafe-binders/lifetime-resolution.rs +++ b/tests/ui/unsafe-binders/lifetime-resolution.rs @@ -9,7 +9,7 @@ fn foo<'a>() { fn inner<'b>() { let outer: unsafe<> &'a &'b (); - //~^ can't use generic parameters from outer item + //~^ ERROR can't use generic parameters from outer item } } diff --git a/tests/ui/unsafe-binders/type-mismatch.rs b/tests/ui/unsafe-binders/type-mismatch.rs new file mode 100644 index 0000000000000..9ac4e817c28b3 --- /dev/null +++ b/tests/ui/unsafe-binders/type-mismatch.rs @@ -0,0 +1,9 @@ +#![feature(unsafe_binders)] +//~^ WARN the feature `unsafe_binders` is incomplete + +fn main() { + let x: unsafe<> i32 = 0; + //~^ ERROR mismatched types + let x: unsafe<'a> &'a i32 = &0; + //~^ ERROR mismatched types +} diff --git a/tests/ui/unsafe-binders/type-mismatch.stderr b/tests/ui/unsafe-binders/type-mismatch.stderr new file mode 100644 index 0000000000000..e694b5d464d8e --- /dev/null +++ b/tests/ui/unsafe-binders/type-mismatch.stderr @@ -0,0 +1,34 @@ +warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-mismatch.rs:1:12 + | +LL | #![feature(unsafe_binders)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #130516 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/type-mismatch.rs:5:27 + | +LL | let x: unsafe<> i32 = 0; + | ------------ ^ expected `unsafe<> i32`, found integer + | | + | expected due to this + | + = note: expected unsafe binder `unsafe<> i32` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/type-mismatch.rs:7:33 + | +LL | let x: unsafe<'a> &'a i32 = &0; + | ------------------ ^^ expected `unsafe<'a> &i32`, found `&{integer}` + | | + | expected due to this + | + = note: expected unsafe binder `unsafe<'a> &'a i32` + found reference `&{integer}` + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/unsafe-fields/unsafe-fields.rs b/tests/ui/unsafe-fields/unsafe-fields.rs index 637471582d7e4..cb86479bb20d3 100644 --- a/tests/ui/unsafe-fields/unsafe-fields.rs +++ b/tests/ui/unsafe-fields/unsafe-fields.rs @@ -17,7 +17,7 @@ fn f(a: A) { } struct WithInvalidUnsafeField { - unsafe unsafe_noncopy_field: Vec, //~ ERROR + unsafe unsafe_noncopy_field: Vec, } struct WithManuallyDropUnsafeField { diff --git a/tests/ui/unsafe-fields/unsafe-fields.stderr b/tests/ui/unsafe-fields/unsafe-fields.stderr index a1c5d2b44cdf1..d0e2dc16a13d5 100644 --- a/tests/ui/unsafe-fields/unsafe-fields.stderr +++ b/tests/ui/unsafe-fields/unsafe-fields.stderr @@ -1,15 +1,3 @@ -error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be unsafe - --> $DIR/unsafe-fields.rs:20:5 - | -LL | unsafe unsafe_noncopy_field: Vec, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: unsafe fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` -help: wrap the field type in `ManuallyDrop<...>` - | -LL | unsafe unsafe_noncopy_field: std::mem::ManuallyDrop>, - | +++++++++++++++++++++++ + - error[E0133]: use of unsafe field is unsafe and requires unsafe block --> $DIR/unsafe-fields.rs:15:30 | @@ -69,7 +57,6 @@ LL | &raw const self.unsafe_field | = note: unsafe fields may carry library invariants -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0133, E0740. -For more information about an error, try `rustc --explain E0133`. +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/unsafe/const_pat_in_layout_restricted.rs b/tests/ui/unsafe/const_pat_in_layout_restricted.rs deleted file mode 100644 index 2ba173ee49962..0000000000000 --- a/tests/ui/unsafe/const_pat_in_layout_restricted.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Check that ref mut patterns within a const pattern don't get considered -// unsafe because they're within a pattern for a layout constrained stuct. -//@ check-pass - -#![feature(rustc_attrs)] -#![feature(inline_const_pat)] - -#[rustc_layout_scalar_valid_range_start(3)] -struct Gt2(i32); - -fn main() { - match unsafe { Gt2(5) } { - Gt2( - const { - || match () { - ref mut y => (), - }; - 4 - }, - ) => (), - _ => (), - } -} diff --git a/tests/ui/unsafe/unsafe-trait-impl.rs b/tests/ui/unsafe/unsafe-trait-impl.rs index 9fd9ff65288d9..dc7e119ed7a5d 100644 --- a/tests/ui/unsafe/unsafe-trait-impl.rs +++ b/tests/ui/unsafe/unsafe-trait-impl.rs @@ -1,5 +1,7 @@ // Check that safe fns are not a subtype of unsafe fns. +//@ dont-require-annotations: NOTE + trait Foo { unsafe fn len(&self) -> u32; } @@ -7,8 +9,8 @@ trait Foo { impl Foo for u32 { fn len(&self) -> u32 { *self } //~^ ERROR method `len` has an incompatible type for trait - //~| expected signature `unsafe fn(&_) -> _` - //~| found signature `fn(&_) -> _` + //~| NOTE expected signature `unsafe fn(&_) -> _` + //~| NOTE found signature `fn(&_) -> _` } fn main() { } diff --git a/tests/ui/unsafe/unsafe-trait-impl.stderr b/tests/ui/unsafe/unsafe-trait-impl.stderr index b9f2e1cc869d5..8febbb736634d 100644 --- a/tests/ui/unsafe/unsafe-trait-impl.stderr +++ b/tests/ui/unsafe/unsafe-trait-impl.stderr @@ -1,11 +1,11 @@ error[E0053]: method `len` has an incompatible type for trait - --> $DIR/unsafe-trait-impl.rs:8:5 + --> $DIR/unsafe-trait-impl.rs:10:5 | LL | fn len(&self) -> u32 { *self } | ^^^^^^^^^^^^^^^^^^^^ expected unsafe fn, found safe fn | note: type in trait - --> $DIR/unsafe-trait-impl.rs:4:5 + --> $DIR/unsafe-trait-impl.rs:6:5 | LL | unsafe fn len(&self) -> u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unsafe/unsafe-unstable-const-fn.rs b/tests/ui/unsafe/unsafe-unstable-const-fn.rs index 5398721484a01..dd1fd8463773e 100644 --- a/tests/ui/unsafe/unsafe-unstable-const-fn.rs +++ b/tests/ui/unsafe/unsafe-unstable-const-fn.rs @@ -5,7 +5,7 @@ #[rustc_const_unstable(feature = "const_foo", issue = "none")] const fn unstable(a: *const i32, b: i32) -> bool { *a == b - //~^ dereference of raw pointer is unsafe + //~^ ERROR dereference of raw pointer is unsafe } fn main() {} diff --git a/tests/ui/unsized-locals/issue-30276-feature-flagged.rs b/tests/ui/unsized-locals/issue-30276-feature-flagged.rs index 635d34f822914..8b5b321ec4989 100644 --- a/tests/ui/unsized-locals/issue-30276-feature-flagged.rs +++ b/tests/ui/unsized-locals/issue-30276-feature-flagged.rs @@ -5,4 +5,5 @@ struct Test([i32]); fn main() { let _x: fn(_) -> Test = Test; -} //~^the size for values of type `[i32]` cannot be known at compilation time + //~^ ERROR the size for values of type `[i32]` cannot be known at compilation time +} diff --git a/tests/ui/unsized-locals/issue-30276.rs b/tests/ui/unsized-locals/issue-30276.rs index 9c4bf062a40e9..6b67ebbec1c0c 100644 --- a/tests/ui/unsized-locals/issue-30276.rs +++ b/tests/ui/unsized-locals/issue-30276.rs @@ -2,4 +2,5 @@ struct Test([i32]); fn main() { let _x: fn(_) -> Test = Test; -} //~^the size for values of type `[i32]` cannot be known at compilation time + //~^ ERROR the size for values of type `[i32]` cannot be known at compilation time +} diff --git a/tests/ui/unsized-locals/rust-call.rs b/tests/ui/unsized-locals/rust-call.rs deleted file mode 100644 index ff4075aa4c03c..0000000000000 --- a/tests/ui/unsized-locals/rust-call.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(unsized_tuple_coercion)] -#![feature(unboxed_closures)] -#![feature(unsized_fn_params)] - -fn bad() -> extern "rust-call" fn(([u8],)) { todo!() } - -fn main() { - let f = bad(); - let slice: Box<([u8],)> = Box::new(([1; 8],)); - f(*slice); - //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time -} diff --git a/tests/ui/unsized-locals/rust-call.stderr b/tests/ui/unsized-locals/rust-call.stderr deleted file mode 100644 index 9eb0f3dabcca1..0000000000000 --- a/tests/ui/unsized-locals/rust-call.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/rust-call.rs:10:7 - | -LL | f(*slice); - | ^^^^^^ doesn't have a size known at compile-time - | - = help: within `([u8],)`, the trait `Sized` is not implemented for `[u8]` - = note: required because it appears within the type `([u8],)` - = note: argument required to be sized due to `extern "rust-call"` ABI - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/unsized-locals/unsized-exprs-rpass.rs b/tests/ui/unsized-locals/unsized-exprs-rpass.rs index a5aada20d3ebe..861583efc401d 100644 --- a/tests/ui/unsized-locals/unsized-exprs-rpass.rs +++ b/tests/ui/unsized-locals/unsized-exprs-rpass.rs @@ -1,6 +1,6 @@ //@ run-pass #![allow(incomplete_features, unused_braces, unused_parens)] -#![feature(unsized_tuple_coercion, unsized_locals, unsized_fn_params)] +#![feature(unsized_locals, unsized_fn_params)] struct A(#[allow(dead_code)] X); @@ -8,9 +8,6 @@ fn udrop(_x: T) {} fn foo() -> Box<[u8]> { Box::new(*b"foo") } -fn tfoo() -> Box<(i32, [u8])> { - Box::new((42, *b"foo")) -} fn afoo() -> Box> { Box::new(A(*b"foo")) } @@ -27,7 +24,6 @@ fn main() { udrop::<[u8]>(if true { *foo() } else { *foo() }); udrop::<[u8]>({ *foo() }); udrop::<[u8]>((*foo())); - udrop::<[u8]>((*tfoo()).1); *afoo() + 42; udrop as fn([u8]); } diff --git a/tests/ui/unsized-locals/unsized-exprs.rs b/tests/ui/unsized-locals/unsized-exprs.rs index 1729b9ffa8670..8fe5dcba8a903 100644 --- a/tests/ui/unsized-locals/unsized-exprs.rs +++ b/tests/ui/unsized-locals/unsized-exprs.rs @@ -1,4 +1,4 @@ -#![feature(unsized_tuple_coercion, unsized_fn_params)] +#![feature(unsized_fn_params)] struct A(X); @@ -6,9 +6,6 @@ fn udrop(_x: T) {} fn foo() -> Box<[u8]> { Box::new(*b"foo") } -fn tfoo() -> Box<(i32, [u8])> { - Box::new((42, *b"foo")) -} fn afoo() -> Box> { Box::new(A(*b"foo")) } @@ -19,8 +16,6 @@ impl std::ops::Add for A<[u8]> { } fn main() { - udrop::<(i32, [u8])>((42, *foo())); - //~^ERROR E0277 udrop::>(A { 0: *foo() }); //~^ERROR E0277 udrop::>(A(*foo())); diff --git a/tests/ui/unsized-locals/unsized-exprs.stderr b/tests/ui/unsized-locals/unsized-exprs.stderr index 8a2ecf0f6c331..1b61254870f67 100644 --- a/tests/ui/unsized-locals/unsized-exprs.stderr +++ b/tests/ui/unsized-locals/unsized-exprs.stderr @@ -1,15 +1,5 @@ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/unsized-exprs.rs:22:26 - | -LL | udrop::<(i32, [u8])>((42, *foo())); - | ^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = 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 - -error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/unsized-exprs.rs:24:22 + --> $DIR/unsized-exprs.rs:19:22 | LL | udrop::>(A { 0: *foo() }); | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -23,7 +13,7 @@ LL | struct A(X); = note: structs must have a statically known size to be initialized error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/unsized-exprs.rs:26:22 + --> $DIR/unsized-exprs.rs:21:22 | LL | udrop::>(A(*foo())); | ^^^^^^^^^ doesn't have a size known at compile-time @@ -36,6 +26,6 @@ LL | struct A(X); | ^ = note: the return type of a function must have a statically known size -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/unsized-locals/unsized-exprs2.rs b/tests/ui/unsized-locals/unsized-exprs2.rs index 127d8717e5806..7706c8b8b8f24 100644 --- a/tests/ui/unsized-locals/unsized-exprs2.rs +++ b/tests/ui/unsized-locals/unsized-exprs2.rs @@ -1,4 +1,4 @@ -#![feature(unsized_tuple_coercion, unsized_fn_params)] +#![feature(unsized_fn_params)] struct A(X); @@ -6,9 +6,6 @@ fn udrop(_x: T) {} fn foo() -> Box<[u8]> { Box::new(*b"foo") } -fn tfoo() -> Box<(i32, [u8])> { - Box::new((42, *b"foo")) -} fn afoo() -> Box> { Box::new(A(*b"foo")) } diff --git a/tests/ui/unsized-locals/unsized-exprs2.stderr b/tests/ui/unsized-locals/unsized-exprs2.stderr index 47b6d72acc7f1..79f0d93d4214d 100644 --- a/tests/ui/unsized-locals/unsized-exprs2.stderr +++ b/tests/ui/unsized-locals/unsized-exprs2.stderr @@ -1,5 +1,5 @@ error[E0508]: cannot move out of type `[u8]`, a non-copy slice - --> $DIR/unsized-exprs2.rs:22:5 + --> $DIR/unsized-exprs2.rs:19:5 | LL | udrop::<[u8]>(foo()[..]); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unsized/unsized-tuple-impls.rs b/tests/ui/unsized/unsized-tuple-impls.rs deleted file mode 100644 index a76e2df575077..0000000000000 --- a/tests/ui/unsized/unsized-tuple-impls.rs +++ /dev/null @@ -1,21 +0,0 @@ -//@ run-pass - -#![feature(unsized_tuple_coercion)] - -use std::collections::HashSet; - -fn main() { - let x : &(i32, i32, [i32]) = &(0, 1, [2, 3]); - let y : &(i32, i32, [i32]) = &(0, 1, [2, 3, 4]); - let mut a = [y, x]; - a.sort(); - assert_eq!(a, [x, y]); - - assert_eq!(&format!("{:?}", a), "[(0, 1, [2, 3]), (0, 1, [2, 3, 4])]"); - - let mut h = HashSet::new(); - h.insert(x); - h.insert(y); - assert!(h.contains(x)); - assert!(h.contains(y)); -} diff --git a/tests/ui/use/auxiliary/extern-use-primitive-type-lib.rs b/tests/ui/use/auxiliary/extern-use-primitive-type-lib.rs index 18aa329ae36b8..c21eea743e92c 100644 --- a/tests/ui/use/auxiliary/extern-use-primitive-type-lib.rs +++ b/tests/ui/use/auxiliary/extern-use-primitive-type-lib.rs @@ -1,3 +1,3 @@ -//@ compile-flags: --edition=2018 +//@ edition: 2018 pub use u32; diff --git a/tests/ui/use/import_trait_associated_item_bad.rs b/tests/ui/use/import_trait_associated_item_bad.rs new file mode 100644 index 0000000000000..d3d2a8e83cdde --- /dev/null +++ b/tests/ui/use/import_trait_associated_item_bad.rs @@ -0,0 +1,18 @@ +#![feature(import_trait_associated_functions)] +#![feature(min_generic_const_args)] +#![allow(incomplete_features)] + +trait Trait { + type AssocTy; + const CONST: usize; +} + +use Trait::AssocTy; +type Alias1 = AssocTy; //~ ERROR ambiguous associated type +type Alias2 = self::AssocTy; //~ ERROR ambiguous associated type + +use Trait::CONST; +type Alias3 = [u8; CONST]; //~ ERROR ambiguous associated constant +type Alias4 = [u8; self::CONST]; //~ ERROR ambiguous associated constant + +fn main() {} diff --git a/tests/ui/use/import_trait_associated_item_bad.stderr b/tests/ui/use/import_trait_associated_item_bad.stderr new file mode 100644 index 0000000000000..d5cd5d37bd714 --- /dev/null +++ b/tests/ui/use/import_trait_associated_item_bad.stderr @@ -0,0 +1,49 @@ +error[E0223]: ambiguous associated type + --> $DIR/import_trait_associated_item_bad.rs:11:15 + | +LL | type Alias1 = AssocTy; + | ^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL | type Alias1 = ::AssocTy; + | ++++++++++++++++++++ + +error[E0223]: ambiguous associated type + --> $DIR/import_trait_associated_item_bad.rs:12:15 + | +LL | type Alias2 = self::AssocTy; + | ^^^^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL - type Alias2 = self::AssocTy; +LL + type Alias2 = ::AssocTy; + | + +error[E0223]: ambiguous associated constant + --> $DIR/import_trait_associated_item_bad.rs:15:20 + | +LL | type Alias3 = [u8; CONST]; + | ^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL | type Alias3 = [u8; ::CONST]; + | ++++++++++++++++++++ + +error[E0223]: ambiguous associated constant + --> $DIR/import_trait_associated_item_bad.rs:16:20 + | +LL | type Alias4 = [u8; self::CONST]; + | ^^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL - type Alias4 = [u8; self::CONST]; +LL + type Alias4 = [u8; ::CONST]; + | + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/use/import_trait_associated_item_glob.rs b/tests/ui/use/import_trait_associated_item_glob.rs new file mode 100644 index 0000000000000..8a712514ed598 --- /dev/null +++ b/tests/ui/use/import_trait_associated_item_glob.rs @@ -0,0 +1,17 @@ +//@ check-pass + +#![feature(import_trait_associated_functions)] + +trait Trait: Default { + fn f() -> Self { Default::default() } + fn g() -> Self { Default::default() } +} + +impl Trait for u8 {} + +use Trait::*; + +fn main() { + let _: u8 = f(); + let _: u8 = g(); +} diff --git a/tests/ui/use/use-from-trait-xc.rs b/tests/ui/use/use-from-trait-xc.rs index b030892aa2695..220bf8cad46f3 100644 --- a/tests/ui/use/use-from-trait-xc.rs +++ b/tests/ui/use/use-from-trait-xc.rs @@ -6,7 +6,7 @@ use use_from_trait_xc::Trait::foo; //~^ ERROR `use` associated items of traits is unstable [E0658] use use_from_trait_xc::Trait::Assoc; -//~^ ERROR `Assoc` is not directly importable +//~^ ERROR `use` associated items of traits is unstable [E0658] use use_from_trait_xc::Trait::CONST; //~^ ERROR `use` associated items of traits is unstable [E0658] diff --git a/tests/ui/use/use-from-trait-xc.stderr b/tests/ui/use/use-from-trait-xc.stderr index 0f8440aa53079..c8ee29b18d0c4 100644 --- a/tests/ui/use/use-from-trait-xc.stderr +++ b/tests/ui/use/use-from-trait-xc.stderr @@ -8,11 +8,15 @@ LL | use use_from_trait_xc::Trait::foo; = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0253]: `Assoc` is not directly importable +error[E0658]: `use` associated items of traits is unstable --> $DIR/use-from-trait-xc.rs:8:5 | LL | use use_from_trait_xc::Trait::Assoc; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be imported directly + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `use` associated items of traits is unstable --> $DIR/use-from-trait-xc.rs:11:5 @@ -74,5 +78,5 @@ LL | struct Foo; error: aborting due to 9 previous errors -Some errors have detailed explanations: E0253, E0432, E0603, E0658. -For more information about an error, try `rustc --explain E0253`. +Some errors have detailed explanations: E0432, E0603, E0658. +For more information about an error, try `rustc --explain E0432`. diff --git a/tests/ui/use/use-from-trait.rs b/tests/ui/use/use-from-trait.rs index 89b7aaa4ba38e..cab661a29e8eb 100644 --- a/tests/ui/use/use-from-trait.rs +++ b/tests/ui/use/use-from-trait.rs @@ -1,5 +1,5 @@ use Trait::foo; //~ ERROR `use` associated items of traits is unstable [E0658] -use Trait::Assoc; //~ ERROR `Assoc` is not directly importable +use Trait::Assoc; //~ ERROR `use` associated items of traits is unstable [E0658] use Trait::C; //~ ERROR `use` associated items of traits is unstable [E0658] use Foo::new; //~ ERROR unresolved import `Foo` [E0432] diff --git a/tests/ui/use/use-from-trait.stderr b/tests/ui/use/use-from-trait.stderr index 2dd78a3545298..63cfcc207504b 100644 --- a/tests/ui/use/use-from-trait.stderr +++ b/tests/ui/use/use-from-trait.stderr @@ -8,11 +8,15 @@ LL | use Trait::foo; = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0253]: `Assoc` is not directly importable +error[E0658]: `use` associated items of traits is unstable --> $DIR/use-from-trait.rs:2:5 | LL | use Trait::Assoc; - | ^^^^^^^^^^^^ cannot be imported directly + | ^^^^^^^^^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `use` associated items of traits is unstable --> $DIR/use-from-trait.rs:3:5 @@ -38,5 +42,5 @@ LL | use Foo::C2; error: aborting due to 5 previous errors -Some errors have detailed explanations: E0253, E0432, E0658. -For more information about an error, try `rustc --explain E0253`. +Some errors have detailed explanations: E0432, E0658. +For more information about an error, try `rustc --explain E0432`. diff --git a/tests/ui/use/use-keyword.rs b/tests/ui/use/use-keyword.rs index 840cddcb907cc..95f3036516760 100644 --- a/tests/ui/use/use-keyword.rs +++ b/tests/ui/use/use-keyword.rs @@ -7,10 +7,10 @@ mod a { //~^ ERROR `self` imports are only allowed within a { } list use super as B; //~^ ERROR unresolved import `super` [E0432] - //~| no `super` in the root + //~| NOTE no `super` in the root use super::{self as C}; //~^ ERROR unresolved import `super` [E0432] - //~| no `super` in the root + //~| NOTE no `super` in the root } } diff --git a/tests/ui/use/use-meta-mismatch.rs b/tests/ui/use/use-meta-mismatch.rs index 2c5ae9cd9a1c5..8d15e46375c3c 100644 --- a/tests/ui/use/use-meta-mismatch.rs +++ b/tests/ui/use/use-meta-mismatch.rs @@ -1,5 +1,3 @@ -//@ error-pattern:can't find crate for `fake_crate` - -extern crate fake_crate as extra; +extern crate fake_crate as extra; //~ ERROR can't find crate for `fake_crate` fn main() { } diff --git a/tests/ui/use/use-meta-mismatch.stderr b/tests/ui/use/use-meta-mismatch.stderr index b793229e5fddc..ffc471fa7224b 100644 --- a/tests/ui/use/use-meta-mismatch.stderr +++ b/tests/ui/use/use-meta-mismatch.stderr @@ -1,5 +1,5 @@ error[E0463]: can't find crate for `fake_crate` - --> $DIR/use-meta-mismatch.rs:3:1 + --> $DIR/use-meta-mismatch.rs:1:1 | LL | extern crate fake_crate as extra; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't find crate diff --git a/tests/ui/use/use-mod/use-mod-2.rs b/tests/ui/use/use-mod/use-mod-2.rs index 9373a62ba36ec..57ff135c4d85a 100644 --- a/tests/ui/use/use-mod/use-mod-2.rs +++ b/tests/ui/use/use-mod/use-mod-2.rs @@ -1,11 +1,11 @@ mod foo { use self::{self}; //~^ ERROR unresolved import `self` [E0432] - //~| no `self` in the root + //~| NOTE no `self` in the root use super::{self}; //~^ ERROR unresolved import `super` [E0432] - //~| no `super` in the root + //~| NOTE no `super` in the root } fn main() {} diff --git a/tests/ui/used.rs b/tests/ui/used.rs deleted file mode 100644 index f008724f428b3..0000000000000 --- a/tests/ui/used.rs +++ /dev/null @@ -1,16 +0,0 @@ -#[used] -static FOO: u32 = 0; // OK - -#[used] //~ ERROR attribute must be applied to a `static` variable -fn foo() {} - -#[used] //~ ERROR attribute must be applied to a `static` variable -struct Foo {} - -#[used] //~ ERROR attribute must be applied to a `static` variable -trait Bar {} - -#[used] //~ ERROR attribute must be applied to a `static` variable -impl Bar for Foo {} - -fn main() {} diff --git a/tests/ui/used.stderr b/tests/ui/used.stderr deleted file mode 100644 index c586dc722932f..0000000000000 --- a/tests/ui/used.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: attribute must be applied to a `static` variable - --> $DIR/used.rs:4:1 - | -LL | #[used] - | ^^^^^^^ -LL | fn foo() {} - | ----------- but this is a function - -error: attribute must be applied to a `static` variable - --> $DIR/used.rs:7:1 - | -LL | #[used] - | ^^^^^^^ -LL | struct Foo {} - | ------------- but this is a struct - -error: attribute must be applied to a `static` variable - --> $DIR/used.rs:10:1 - | -LL | #[used] - | ^^^^^^^ -LL | trait Bar {} - | ------------ but this is a trait - -error: attribute must be applied to a `static` variable - --> $DIR/used.rs:13:1 - | -LL | #[used] - | ^^^^^^^ -LL | impl Bar for Foo {} - | ------------------- but this is a implementation block - -error: aborting due to 4 previous errors - diff --git a/tests/ui/utf8_idents.rs b/tests/ui/utf8_idents.rs deleted file mode 100644 index 0c34529d2de43..0000000000000 --- a/tests/ui/utf8_idents.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ check-pass -// -#![allow(mixed_script_confusables, non_camel_case_types)] - -fn foo< - 'β, - γ ->() {} - -struct X { - δ: usize -} - -pub fn main() { - let α = 0.00001f64; -} diff --git a/tests/ui/variance/variance-btree-invariant-types.rs b/tests/ui/variance/variance-btree-invariant-types.rs index 09c93d0013cce..9e457c7fae80f 100644 --- a/tests/ui/variance/variance-btree-invariant-types.rs +++ b/tests/ui/variance/variance-btree-invariant-types.rs @@ -2,78 +2,78 @@ use std::collections::btree_map::{IterMut, OccupiedEntry, RangeMut, VacantEntry} fn iter_cov_key<'a, 'new>(v: IterMut<'a, &'static (), ()>) -> IterMut<'a, &'new (), ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn iter_cov_val<'a, 'new>(v: IterMut<'a, (), &'static ()>) -> IterMut<'a, (), &'new ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn iter_contra_key<'a, 'new>(v: IterMut<'a, &'new (), ()>) -> IterMut<'a, &'static (), ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn iter_contra_val<'a, 'new>(v: IterMut<'a, (), &'new ()>) -> IterMut<'a, (), &'static ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn range_cov_key<'a, 'new>(v: RangeMut<'a, &'static (), ()>) -> RangeMut<'a, &'new (), ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn range_cov_val<'a, 'new>(v: RangeMut<'a, (), &'static ()>) -> RangeMut<'a, (), &'new ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn range_contra_key<'a, 'new>(v: RangeMut<'a, &'new (), ()>) -> RangeMut<'a, &'static (), ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn range_contra_val<'a, 'new>(v: RangeMut<'a, (), &'new ()>) -> RangeMut<'a, (), &'static ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn occ_cov_key<'a, 'new>(v: OccupiedEntry<'a, &'static (), ()>) -> OccupiedEntry<'a, &'new (), ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn occ_cov_val<'a, 'new>(v: OccupiedEntry<'a, (), &'static ()>) -> OccupiedEntry<'a, (), &'new ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn occ_contra_key<'a, 'new>(v: OccupiedEntry<'a, &'new (), ()>) -> OccupiedEntry<'a, &'static (), ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn occ_contra_val<'a, 'new>(v: OccupiedEntry<'a, (), &'new ()>) -> OccupiedEntry<'a, (), &'static ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn vac_cov_key<'a, 'new>(v: VacantEntry<'a, &'static (), ()>) -> VacantEntry<'a, &'new (), ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn vac_cov_val<'a, 'new>(v: VacantEntry<'a, (), &'static ()>) -> VacantEntry<'a, (), &'new ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn vac_contra_key<'a, 'new>(v: VacantEntry<'a, &'new (), ()>) -> VacantEntry<'a, &'static (), ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } fn vac_contra_val<'a, 'new>(v: VacantEntry<'a, (), &'new ()>) -> VacantEntry<'a, (), &'static ()> { v - //~^ lifetime may not live long enough + //~^ ERROR lifetime may not live long enough } diff --git a/tests/ui/variance/variance-uniquearc.rs b/tests/ui/variance/variance-uniquearc.rs new file mode 100644 index 0000000000000..0358951238869 --- /dev/null +++ b/tests/ui/variance/variance-uniquearc.rs @@ -0,0 +1,27 @@ +// regression test of https://github.com/rust-lang/rust/pull/133572#issuecomment-2543007164 +// see also the test for UniqueRc` in variance-uniquerc.rs +// +// inline comments explain how this code *would* compile if UniqueArc was still covariant + +#![feature(unique_rc_arc)] + +use std::sync::UniqueArc; + +fn extend_lifetime<'a, 'b>(x: &'a str) -> &'b str { + let r = UniqueArc::new(""); // UniqueArc<&'static str> + let w = UniqueArc::downgrade(&r); // Weak<&'static str> + let mut r = r; // [IF COVARIANT]: ==>> UniqueArc<&'a str> + *r = x; // assign the &'a str + let _r = UniqueArc::into_arc(r); // Arc<&'a str>, but we only care to activate the weak + let r = w.upgrade().unwrap(); // Arc<&'static str> + *r // &'static str, coerces to &'b str + //~^ ERROR lifetime may not live long enough +} + +fn main() { + let s = String::from("Hello World!"); + let r = extend_lifetime(&s); + println!("{r}"); + drop(s); + println!("{r}"); +} diff --git a/tests/ui/variance/variance-uniquearc.stderr b/tests/ui/variance/variance-uniquearc.stderr new file mode 100644 index 0000000000000..55076dae7324b --- /dev/null +++ b/tests/ui/variance/variance-uniquearc.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/variance-uniquearc.rs:17:5 + | +LL | fn extend_lifetime<'a, 'b>(x: &'a str) -> &'b str { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | *r // &'static str, coerces to &'b str + | ^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to 1 previous error + diff --git a/tests/ui/variance/variance-uniquerc.rs b/tests/ui/variance/variance-uniquerc.rs index 0c395ab06eaa3..2e9738f66dcb8 100644 --- a/tests/ui/variance/variance-uniquerc.rs +++ b/tests/ui/variance/variance-uniquerc.rs @@ -1,5 +1,5 @@ // regression test of https://github.com/rust-lang/rust/pull/133572#issuecomment-2543007164 -// we should also test UniqueArc once implemented +// see also the test for UniqueArc in variance-uniquearc.rs // // inline comments explain how this code *would* compile if UniqueRc was still covariant diff --git a/tests/ui/wasm/wasm-import-module.rs b/tests/ui/wasm/wasm-import-module.rs index bff08847d37dd..2b3bca9a411bb 100644 --- a/tests/ui/wasm/wasm-import-module.rs +++ b/tests/ui/wasm/wasm-import-module.rs @@ -15,7 +15,7 @@ extern "C" {} #[link(wasm_import_module = "foo", kind = "dylib")] //~ ERROR: `wasm_import_module` is incompatible with other arguments extern "C" {} -#[link(wasm_import_module = "foo", cfg(FALSE))] //~ ERROR: `wasm_import_module` is incompatible with other arguments +#[link(wasm_import_module = "foo", cfg(false))] //~ ERROR: `wasm_import_module` is incompatible with other arguments extern "C" {} fn main() {} diff --git a/tests/ui/wasm/wasm-import-module.stderr b/tests/ui/wasm/wasm-import-module.stderr index e792c33e91a9d..84f437941a799 100644 --- a/tests/ui/wasm/wasm-import-module.stderr +++ b/tests/ui/wasm/wasm-import-module.stderr @@ -31,7 +31,7 @@ LL | #[link(wasm_import_module = "foo", kind = "dylib")] error: `wasm_import_module` is incompatible with other arguments in `#[link]` attributes --> $DIR/wasm-import-module.rs:18:8 | -LL | #[link(wasm_import_module = "foo", cfg(FALSE))] +LL | #[link(wasm_import_module = "foo", cfg(false))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/tests/ui/weird-exprs.rs b/tests/ui/weird-exprs.rs index 55a8d580a8b7b..b24e754a4ec98 100644 --- a/tests/ui/weird-exprs.rs +++ b/tests/ui/weird-exprs.rs @@ -127,13 +127,13 @@ fn special_characters() { } fn punch_card() -> impl std::fmt::Debug { - ..=..=.. .. .. .. .. .. .. .. .. .. .. ..=.. .. - ..=.. ..=.. .. .. .. .. .. .. .. .. ..=..=..=.. - ..=.. ..=.. ..=.. ..=.. .. ..=..=.. .. ..=.. .. + ..=..=.. .. .. .. .. .. .. .. .. .. .. .. .. .. + ..=.. ..=.. .. .. .. .. .. .. .. .. .. ..=.. .. + ..=.. ..=.. ..=.. ..=.. .. ..=..=.. ..=..=..=.. ..=..=.. .. ..=.. ..=.. ..=.. .. .. .. ..=.. .. ..=.. ..=.. ..=.. ..=.. .. ..=.. .. .. ..=.. .. ..=.. ..=.. ..=.. ..=.. .. .. ..=.. .. ..=.. .. - ..=.. ..=.. .. ..=..=.. ..=..=.. .. .. ..=.. .. + ..=.. ..=.. .. ..=..=.. ..=..=.. .. .. ..=..=.. } fn r#match() { diff --git a/tests/ui/wf/check-wf-of-normalized-signature.rs b/tests/ui/wf/check-wf-of-normalized-signature.rs new file mode 100644 index 0000000000000..5fda69601d992 --- /dev/null +++ b/tests/ui/wf/check-wf-of-normalized-signature.rs @@ -0,0 +1,24 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for . +// Ensure that we check the well-formedness of `::Output` after normalizing +// the type to `()`, since we only imply outlives bounds from the normalized signature, so we +// don't know (e.g.) that `&mut T` is WF. + + +trait Mode { + type Output; + fn from_mut(_r: &mut Self::Output) -> Self::Output<&mut T>; +} + +struct Check; + +impl Mode for Check { + type Output = (); + fn from_mut(_r: &mut Self::Output) -> Self::Output<&mut T> {} +} + +fn main() {} diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr index 3b4de0753af0b..59eef0c63278a 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr @@ -53,6 +53,12 @@ LL | trait Trait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information +error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:6:12 + | +LL | fn fnc(&self) -> Trait { + | ^^^^^^^^^^^^^^^^^^^^ + warning: trait objects without an explicit `dyn` are deprecated --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:6:44 | @@ -66,12 +72,6 @@ help: if this is a dyn-compatible trait, use `dyn` LL | fn fnc(&self) -> dyn Trait { | +++ -error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:6:12 - | -LL | fn fnc(&self) -> Trait { - | ^^^^^^^^^^^^^^^^^^^^ - warning: trait objects without an explicit `dyn` are deprecated --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:6:21 | diff --git a/tests/ui/wf/issue-95665.rs b/tests/ui/wf/issue-95665.rs index 67923cbb2d6b5..422ffd11ee3f9 100644 --- a/tests/ui/wf/issue-95665.rs +++ b/tests/ui/wf/issue-95665.rs @@ -12,7 +12,7 @@ pub struct Struct { extern "C" { static VAR: Struct; - //~^ 14:17: 14:27: the trait bound `u8: Trait` is not satisfied [E0277] + //~^ ERROR the trait bound `u8: Trait` is not satisfied [E0277] } fn main() {} diff --git a/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs b/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs index 0a847853b121e..7871612dc8b75 100644 --- a/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs +++ b/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs @@ -1,6 +1,7 @@ fn main() { [0; ..10]; //~^ ERROR mismatched types - //~| expected type `usize` - //~| found struct `RangeTo<{integer}>` + //~| NOTE expected type `usize` + //~| NOTE found struct `RangeTo<{integer}>` + //~| NOTE expected `usize`, found `RangeTo<{integer}> } diff --git a/tests/ui/wf/wf-normalization-sized.next.stderr b/tests/ui/wf/wf-normalization-sized.next.stderr index 83b56bb6b19ad..804dd0a252d13 100644 --- a/tests/ui/wf/wf-normalization-sized.next.stderr +++ b/tests/ui/wf/wf-normalization-sized.next.stderr @@ -7,28 +7,8 @@ LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = (); = 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 `[[[[[u8]]]]]` cannot be known at compilation time - --> $DIR/wf-normalization-sized.rs:19:11 - | -LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = (); - | ^^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `[[[[[u8]]]]]` - = note: slice and array elements must have `Sized` type - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/wf-normalization-sized.rs:22:11 - | -LL | const _: as WellUnformed>::RequestNormalize = (); - | ^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `str` -note: required by an implicit `Sized` bound in `Vec` - --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL - error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/wf-normalization-sized.rs:22:11 + --> $DIR/wf-normalization-sized.rs:21:11 | LL | const _: as WellUnformed>::RequestNormalize = (); | ^^^^^^^^ doesn't have a size known at compile-time @@ -36,8 +16,7 @@ LL | const _: as WellUnformed>::RequestNormalize = (); = help: the trait `Sized` is not implemented for `str` note: required by an implicit `Sized` bound in `Vec` --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/wf-normalization-sized.rs b/tests/ui/wf/wf-normalization-sized.rs index 80b2c8803ff17..e695fd93fb0ec 100644 --- a/tests/ui/wf/wf-normalization-sized.rs +++ b/tests/ui/wf/wf-normalization-sized.rs @@ -17,10 +17,8 @@ impl WellUnformed for T { } const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = (); -//[next]~^ the size for values of type `[[[[[u8]]]]]` cannot be known at compilation time -//[next]~| the size for values of type `[[[[[u8]]]]]` cannot be known at compilation time +//[next]~^ ERROR the size for values of type `[[[[[u8]]]]]` cannot be known at compilation time const _: as WellUnformed>::RequestNormalize = (); -//[next]~^ the size for values of type `str` cannot be known at compilation time -//[next]~| the size for values of type `str` cannot be known at compilation time +//[next]~^ ERROR the size for values of type `str` cannot be known at compilation time fn main() {} diff --git a/tests/ui/where-clauses/cfg_attribute.a.stderr b/tests/ui/where-clauses/cfg_attribute.a.stderr new file mode 100644 index 0000000000000..0ede890eb44f2 --- /dev/null +++ b/tests/ui/where-clauses/cfg_attribute.a.stderr @@ -0,0 +1,288 @@ +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:32:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:43:11 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:54:11 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:66:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:76:11 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:87:11 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:100:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:114:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:129:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:144:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:155:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:165:11 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:178:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:32:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:35:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:66:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:69:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:100:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:103:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:114:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:117:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:129:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:132:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:144:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:147:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:155:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:158:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:178:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:181:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:43:9 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:46:9 + | +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:54:9 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:57:9 + | +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:76:9 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:79:9 + | +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:87:9 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:90:9 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:165:9 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:168:9 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: aborting due to 39 previous errors + diff --git a/tests/ui/where-clauses/cfg_attribute.b.stderr b/tests/ui/where-clauses/cfg_attribute.b.stderr new file mode 100644 index 0000000000000..0ede890eb44f2 --- /dev/null +++ b/tests/ui/where-clauses/cfg_attribute.b.stderr @@ -0,0 +1,288 @@ +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:32:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:43:11 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:54:11 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:66:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:76:11 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:87:11 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:100:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:114:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:129:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:144:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:155:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:165:11 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/cfg_attribute.rs:178:7 + | +LL | #[derive(Clone)] ():, + | ^^^^^^ not a non-macro attribute + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:32:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:35:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:66:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:69:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:100:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:103:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:114:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:117:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:129:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:132:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:144:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:147:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:155:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:158:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:178:5 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:181:5 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:43:9 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:46:9 + | +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:54:9 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:57:9 + | +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:76:9 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:79:9 + | +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:87:9 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:90:9 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:165:9 + | +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/cfg_attribute.rs:168:9 + | +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: aborting due to 39 previous errors + diff --git a/tests/ui/where-clauses/cfg_attribute.rs b/tests/ui/where-clauses/cfg_attribute.rs new file mode 100644 index 0000000000000..8cbca0bee75dd --- /dev/null +++ b/tests/ui/where-clauses/cfg_attribute.rs @@ -0,0 +1,183 @@ +//@ revisions: a b + +#![crate_type = "lib"] +#![feature(alloc_error_handler)] +#![feature(cfg_accessible)] +#![feature(cfg_eval)] +#![feature(custom_test_frameworks)] +#![feature(derive_const)] +#![feature(where_clause_attrs)] +#![allow(soft_unstable)] + +use std::marker::PhantomData; + +#[cfg(a)] +trait TraitA {} + +#[cfg(b)] +trait TraitB {} + +#[cfg_attr(a, cfg(a))] +trait TraitAA {} + +#[cfg_attr(b, cfg(b))] +trait TraitBB {} + +trait A +where + #[cfg(a)] T: TraitA, + #[cfg(b)] T: TraitB, + #[cfg_attr(a, cfg(a))] T: TraitAA, + #[cfg_attr(b, cfg(b))] T: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():, //~ ERROR most attributes are not supported in `where` clauses +{ + type B + where + #[cfg(a)] U: TraitA, + #[cfg(b)] U: TraitB, + #[cfg_attr(a, cfg(a))] U: TraitAA, + #[cfg_attr(b, cfg(b))] U: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():; //~ ERROR most attributes are not supported in `where` clauses + + fn foo(&self) + where + #[cfg(a)] U: TraitA, + #[cfg(b)] U: TraitB, + #[cfg_attr(a, cfg(a))] U: TraitAA, + #[cfg_attr(b, cfg(b))] U: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():; //~ ERROR most attributes are not supported in `where` clauses +} + +impl A for T +where + #[cfg(a)] T: TraitA, + #[cfg(b)] T: TraitB, + #[cfg_attr(a, cfg(a))] T: TraitAA, + #[cfg_attr(b, cfg(b))] T: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():, //~ ERROR most attributes are not supported in `where` clauses +{ + type B = () where + #[cfg(a)] U: TraitA, + #[cfg(b)] U: TraitB, + #[cfg_attr(a, cfg(a))] U: TraitAA, + #[cfg_attr(b, cfg(b))] U: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():; //~ ERROR most attributes are not supported in `where` clauses + + fn foo(&self) + where + #[cfg(a)] U: TraitA, + #[cfg(b)] U: TraitB, + #[cfg_attr(a, cfg(a))] U: TraitAA, + #[cfg_attr(b, cfg(b))] U: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():, //~ ERROR most attributes are not supported in `where` clauses + {} +} + +struct C +where + #[cfg(a)] T: TraitA, + #[cfg(b)] T: TraitB, + #[cfg_attr(a, cfg(a))] T: TraitAA, + #[cfg_attr(b, cfg(b))] T: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():, //~ ERROR most attributes are not supported in `where` clauses +{ + _t: PhantomData, +} + +union D +where + #[cfg(a)] T: TraitA, + #[cfg(b)] T: TraitB, + #[cfg_attr(a, cfg(a))] T: TraitAA, + #[cfg_attr(b, cfg(b))] T: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():, //~ ERROR most attributes are not supported in `where` clauses +{ + + _t: PhantomData, +} + +enum E +where + #[cfg(a)] T: TraitA, + #[cfg(b)] T: TraitB, + #[cfg_attr(a, cfg(a))] T: TraitAA, + #[cfg_attr(b, cfg(b))] T: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():, //~ ERROR most attributes are not supported in `where` clauses +{ + E(PhantomData), +} + +#[allow(type_alias_bounds)] +type F +where + #[cfg(a)] T: TraitA, + #[cfg(b)] T: TraitB, + #[cfg_attr(a, cfg(a))] T: TraitAA, + #[cfg_attr(b, cfg(b))] T: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():, //~ ERROR most attributes are not supported in `where` clauses += T; + +impl C where + #[cfg(a)] T: TraitA, + #[cfg(b)] T: TraitB, + #[cfg_attr(a, cfg(a))] T: TraitAA, + #[cfg_attr(b, cfg(b))] T: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():, //~ ERROR most attributes are not supported in `where` clauses +{ + fn new() where + #[cfg(a)] U: TraitA, + #[cfg(b)] U: TraitB, + #[cfg_attr(a, cfg(a))] U: TraitAA, + #[cfg_attr(b, cfg(b))] U: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():, //~ ERROR most attributes are not supported in `where` clauses + {} +} + +fn foo() +where + #[cfg(a)] T: TraitA, + #[cfg(b)] T: TraitB, + #[cfg_attr(a, cfg(a))] T: TraitAA, + #[cfg_attr(b, cfg(b))] T: TraitBB, + #[derive(Clone)] ():, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] ():, //~ ERROR most attributes are not supported in `where` clauses +{ +} diff --git a/tests/ui/impl-trait/method-suggestion-no-duplication.rs b/tests/ui/where-clauses/method-suggestion-no-duplication.rs similarity index 100% rename from tests/ui/impl-trait/method-suggestion-no-duplication.rs rename to tests/ui/where-clauses/method-suggestion-no-duplication.rs diff --git a/tests/ui/impl-trait/method-suggestion-no-duplication.stderr b/tests/ui/where-clauses/method-suggestion-no-duplication.stderr similarity index 100% rename from tests/ui/impl-trait/method-suggestion-no-duplication.stderr rename to tests/ui/where-clauses/method-suggestion-no-duplication.stderr diff --git a/tests/ui/where-clauses/unsupported_attribute.rs b/tests/ui/where-clauses/unsupported_attribute.rs new file mode 100644 index 0000000000000..33128b383b991 --- /dev/null +++ b/tests/ui/where-clauses/unsupported_attribute.rs @@ -0,0 +1,36 @@ +#![crate_type = "lib"] +#![feature(alloc_error_handler)] +#![feature(cfg_accessible)] +#![feature(cfg_eval)] +#![feature(custom_test_frameworks)] +#![feature(derive_const)] +#![feature(where_clause_attrs)] +#![allow(soft_unstable)] + +trait Trait {} + +fn foo<'a, T>() +where + #[doc = "doc"] T: Trait, //~ ERROR most attributes are not supported in `where` clauses + #[doc = "doc"] 'a: 'static, //~ ERROR most attributes are not supported in `where` clauses + #[ignore] T: Trait, //~ ERROR most attributes are not supported in `where` clauses + #[ignore] 'a: 'static, //~ ERROR most attributes are not supported in `where` clauses + #[should_panic] T: Trait, //~ ERROR most attributes are not supported in `where` clauses + #[should_panic] 'a: 'static, //~ ERROR most attributes are not supported in `where` clauses + #[macro_use] T: Trait, //~ ERROR most attributes are not supported in `where` clauses + #[macro_use] 'a: 'static, //~ ERROR most attributes are not supported in `where` clauses + #[allow(unused)] T: Trait, //~ ERROR most attributes are not supported in `where` clauses + #[allow(unused)] 'a: 'static, //~ ERROR most attributes are not supported in `where` clauses + #[deprecated] T: Trait, //~ ERROR most attributes are not supported in `where` clauses + #[deprecated] 'a: 'static, //~ ERROR most attributes are not supported in `where` clauses + #[automatically_derived] T: Trait, //~ ERROR most attributes are not supported in `where` clauses + #[automatically_derived] 'a: 'static, //~ ERROR most attributes are not supported in `where` clauses + #[derive(Clone)] T: Trait, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[derive(Clone)] 'a: 'static, + //~^ ERROR most attributes are not supported in `where` clauses + //~| ERROR expected non-macro attribute, found attribute macro `derive` + #[rustfmt::skip] T: Trait, //~ ERROR most attributes are not supported in `where` clauses + #[rustfmt::skip] 'a: 'static, //~ ERROR most attributes are not supported in `where` clauses +{} diff --git a/tests/ui/where-clauses/unsupported_attribute.stderr b/tests/ui/where-clauses/unsupported_attribute.stderr new file mode 100644 index 0000000000000..ecb28039f8851 --- /dev/null +++ b/tests/ui/where-clauses/unsupported_attribute.stderr @@ -0,0 +1,158 @@ +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/unsupported_attribute.rs:28:7 + | +LL | #[derive(Clone)] T: Trait, + | ^^^^^^ not a non-macro attribute + +error: expected non-macro attribute, found attribute macro `derive` + --> $DIR/unsupported_attribute.rs:31:7 + | +LL | #[derive(Clone)] 'a: 'static, + | ^^^^^^ not a non-macro attribute + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:14:5 + | +LL | #[doc = "doc"] T: Trait, + | ^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:15:5 + | +LL | #[doc = "doc"] 'a: 'static, + | ^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:16:5 + | +LL | #[ignore] T: Trait, + | ^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:17:5 + | +LL | #[ignore] 'a: 'static, + | ^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:18:5 + | +LL | #[should_panic] T: Trait, + | ^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:19:5 + | +LL | #[should_panic] 'a: 'static, + | ^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:20:5 + | +LL | #[macro_use] T: Trait, + | ^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:21:5 + | +LL | #[macro_use] 'a: 'static, + | ^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:22:5 + | +LL | #[allow(unused)] T: Trait, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:23:5 + | +LL | #[allow(unused)] 'a: 'static, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:24:5 + | +LL | #[deprecated] T: Trait, + | ^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:25:5 + | +LL | #[deprecated] 'a: 'static, + | ^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:26:5 + | +LL | #[automatically_derived] T: Trait, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:27:5 + | +LL | #[automatically_derived] 'a: 'static, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:28:5 + | +LL | #[derive(Clone)] T: Trait, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:31:5 + | +LL | #[derive(Clone)] 'a: 'static, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:34:5 + | +LL | #[rustfmt::skip] T: Trait, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:35:5 + | +LL | #[rustfmt::skip] 'a: 'static, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: aborting due to 20 previous errors + diff --git a/tests/ui/windows-subsystem/windows-subsystem-invalid.rs b/tests/ui/windows-subsystem/windows-subsystem-invalid.rs index c6a6dd00a921a..4dadfc6fd5d74 100644 --- a/tests/ui/windows-subsystem/windows-subsystem-invalid.rs +++ b/tests/ui/windows-subsystem/windows-subsystem-invalid.rs @@ -1,5 +1,5 @@ -//@ error-pattern: invalid windows subsystem `wrong`, only `windows` and `console` are allowed - #![windows_subsystem = "wrong"] fn main() {} + +//~? ERROR invalid windows subsystem `wrong` diff --git a/tests/ui/write-fmt-errors.rs b/tests/ui/write-fmt-errors.rs index 1dafb9a784b3a..b48fa3f11ccb1 100644 --- a/tests/ui/write-fmt-errors.rs +++ b/tests/ui/write-fmt-errors.rs @@ -4,7 +4,7 @@ #![feature(io_error_uncategorized)] use std::fmt; -use std::io::{self, Error, Write, sink}; +use std::io::{self, Error, Write}; use std::panic::catch_unwind; struct ErrorDisplay; @@ -33,7 +33,7 @@ fn main() { assert!(res.is_err(), "writer error did not propagate"); // Test that the error from the formatter is detected. - let res = catch_unwind(|| write!(sink(), "{} {} {}", 1, ErrorDisplay, "bar")); + let res = catch_unwind(|| write!(vec![], "{} {} {}", 1, ErrorDisplay, "bar")); let err = res.expect_err("formatter error did not lead to panic").downcast::<&str>().unwrap(); assert!( err.contains("formatting trait implementation returned an error"), diff --git a/triagebot.toml b/triagebot.toml index ed352984fb120..fd7a861bc92dc 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -18,6 +18,7 @@ allow-unauthenticated = [ "WG-*", "-Z*", "beta-nominated", + "CI-spurious-*", "const-hack", "llvm-*", "needs-fcp", @@ -46,7 +47,6 @@ add_labels = ["S-waiting-on-review"] [glacier] [ping.icebreakers-llvm] -alias = ["llvm", "llvms"] message = """\ Hey LLVM ICE-breakers! This bug has been identified as a good "LLVM ICE-breaking candidate". In case it's useful, here are some @@ -133,7 +133,7 @@ In case it's useful, here are some [instructions] for tackling these sorts of is [instructions]: https://rustc-dev-guide.rust-lang.org/notification-groups/rust-for-linux.html """ -label = "O-rfl" +label = "A-rust-for-linux" [ping.wasm] alias = ["webassembly"] @@ -174,8 +174,8 @@ label = "O-emscripten" [ping.relnotes-interest-group] message = """\ -Hi relnotes-interest-group, this PR adds release notes. Could you review this PR -if you have time? Thanks <3 +Hi relnotes-interest-group, this issue/PR could use some help in reviewing / +adjusting release notes. Could you take a look if available? Thanks <3 """ [prioritize] @@ -207,7 +207,7 @@ trigger_files = [ "tests/rustdoc", "tests/rustdoc-ui", "tests/rustdoc-gui", - "tests/rustdoc-js", + "tests/rustdoc-js/", "tests/rustdoc-js-std", "tests/rustdoc-json", @@ -252,7 +252,16 @@ trigger_files = [ "compiler/rustc_passes/src/check_attr.rs", "compiler/rustc_attr_parsing", "compiler/rustc_attr_data_structures", - "compiler/rustc_attr_validation", +] + +[autolabel."F-autodiff"] +trigger_files = [ + "src/tools/enzyme", + "src/doc/unstable-book/src/compiler-flags/autodiff.md", + "compiler/rustc_ast/src/expand/autodiff_attrs.rs", + "compiler/rustc_monomorphize/src/partitioning/autodiff.rs", + "compiler/rustc_codegen_llvm/src/builder/autodiff.rs", + "compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs", ] [autolabel."T-rustdoc-frontend"] @@ -274,7 +283,7 @@ trigger_files = [ [autolabel."A-rustdoc-search"] trigger_files = [ "src/librustdoc/html/static/js/search.js", - "tests/rustdoc-js", + "tests/rustdoc-js/", "tests/rustdoc-js-std", ] @@ -308,6 +317,23 @@ exclude_labels = [ "T-*", ] +trigger_labels = [ + "D-*", + "A-diagnostics", +] + +[autolabel."A-diagnostics"] + +trigger_labels = [ + "D-*", +] + +[autolabel."A-lints"] + +trigger_labels = [ + "L-*", +] + [autolabel."T-libs"] trigger_files = [ "library/alloc", @@ -316,7 +342,6 @@ trigger_files = [ "library/panic_unwind", "library/std", "library/stdarch", - "library/term", "library/test", ] exclude_labels = [ @@ -331,7 +356,7 @@ trigger_files = [ [autolabel."O-apple"] trigger_files = [ "library/std/src/os/darwin", - "library/std/src/sys/pal/unix/thread_parking/darwin.rs", + "library/std/src/sys/sync/thread_parking/darwin.rs", "compiler/rustc_target/src/spec/base/apple", ] @@ -408,7 +433,8 @@ trigger_files = [ [autolabel."O-wasm"] trigger_files = [ "library/std/src/sys/pal/wasm", - "library/std/src/os/wasm" + "library/std/src/os/wasi", + "library/std/src/os/wasip2" ] [autolabel."O-windows"] @@ -421,7 +447,7 @@ trigger_files = [ trigger_files = [ "Cargo.toml", "configure", - "config.example.toml", + "bootstrap.example.toml", "src/bootstrap", "src/build_helper", "src/tools/rust-installer", @@ -499,7 +525,6 @@ trigger_files = [ "CONTRIBUTING.md", "INSTALL.md", "REUSE.toml", - ".reuse", ".mailmap", ".git-blame-ignore-revs", ".editorconfig" @@ -524,7 +549,6 @@ exclude_labels = [ [autolabel."WG-trait-system-refactor"] trigger_files = [ - "compiler/rustc_middle/src/traits/solve", "compiler/rustc_next_trait_solver", "compiler/rustc_trait_selection/src/solve", "compiler/rustc_type_ir/src/solve", @@ -561,18 +585,23 @@ trigger_files = [ "src/tools/compiletest" ] +[autolabel."A-test-infra-minicore"] +trigger_files = [ + "tests/auxiliary/minicore.rs", +] + [autolabel."A-rustc-dev-guide"] trigger_files = [ "src/doc/rustc-dev-guide", ] [notify-zulip."I-prioritize"] -zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts +zulip_stream = 245100 # #t-compiler/prioritization/alerts topic = "#{number} {title}" message_on_add = """\ @*WG-prioritization/alerts* issue #{number} has been requested for prioritization. -# [Procedure](https://forge.rust-lang.org/compiler/prioritization/procedure.html#assign-priority-to-unprioritized-issues-with-i-prioritize-label) +# [Procedure](https://forge.rust-lang.org/compiler/prioritization.html) - Priority? - Regression? - Notify people/groups? @@ -582,10 +611,7 @@ message_on_remove = "Issue #{number}'s prioritization request has been removed." message_on_close = "Issue #{number} has been closed while requested for prioritization." message_on_reopen = "Issue #{number} has been reopened." -# FIXME: Patch triagebot to support `notify-zulip.

    {} + +struct Container { + data: T, +} + +struct Desugared { + callback: Box MyFn<&'a Container<&'a u8>>>, +} + +fn test(callback: Box MyFn>>) -> Desugared { + Desugared { callback } + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/fn/coerce-suggestion-infer-region.stderr b/tests/ui/fn/coerce-suggestion-infer-region.stderr new file mode 100644 index 0000000000000..9dd0fcf76ce19 --- /dev/null +++ b/tests/ui/fn/coerce-suggestion-infer-region.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/coerce-suggestion-infer-region.rs:22:17 + | +LL | Desugared { callback } + | ^^^^^^^^ expected `Box>>`, found `Box>>` + | + = note: expected struct `Box<(dyn for<'a> MyFn<&'a Container<&'a u8>> + 'static)>` + found struct `Box<(dyn for<'a> MyFn> + 'static)>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/fn/fn-bad-block-type.rs b/tests/ui/fn/fn-bad-block-type.rs index c7ad462f14319..47ddc583c8774 100644 --- a/tests/ui/fn/fn-bad-block-type.rs +++ b/tests/ui/fn/fn-bad-block-type.rs @@ -1,5 +1,3 @@ -//@ error-pattern:mismatched types - -fn f() -> isize { true } +fn f() -> isize { true } //~ ERROR mismatched types fn main() { } diff --git a/tests/ui/fn/fn-bad-block-type.stderr b/tests/ui/fn/fn-bad-block-type.stderr index 6917bea659153..287caee45821e 100644 --- a/tests/ui/fn/fn-bad-block-type.stderr +++ b/tests/ui/fn/fn-bad-block-type.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/fn-bad-block-type.rs:3:19 + --> $DIR/fn-bad-block-type.rs:1:19 | LL | fn f() -> isize { true } | ----- ^^^^ expected `isize`, found `bool` diff --git a/tests/ui/fn/fn-item-type.rs b/tests/ui/fn/fn-item-type.rs index c094a34b207d9..c4c090bfda7c4 100644 --- a/tests/ui/fn/fn-item-type.rs +++ b/tests/ui/fn/fn-item-type.rs @@ -1,6 +1,8 @@ // Test that the types of distinct fn items are not compatible by // default. See also `run-pass/fn-item-type-*.rs`. +//@ dont-require-annotations: NOTE + fn foo(x: isize) -> isize { x * 2 } @@ -21,31 +23,31 @@ impl Foo for T { fn main() { eq(foo::, bar::); //~^ ERROR mismatched types - //~| expected fn item `fn(_) -> _ {foo::}` - //~| found fn item `fn(_) -> _ {bar::}` - //~| expected fn item, found a different fn item - //~| different fn items have unique types, even if their signatures are the same + //~| NOTE expected fn item `fn(_) -> _ {foo::}` + //~| NOTE found fn item `fn(_) -> _ {bar::}` + //~| NOTE expected fn item, found a different fn item + //~| NOTE different fn items have unique types, even if their signatures are the same eq(foo::, foo::); //~^ ERROR mismatched types - //~| expected `u8`, found `i8` - //~| different fn items have unique types, even if their signatures are the same + //~| NOTE expected `u8`, found `i8` + //~| NOTE different fn items have unique types, even if their signatures are the same eq(bar::, bar::>); //~^ ERROR mismatched types - //~| found fn item `fn(_) -> _ {bar::>}` - //~| expected `String`, found `Vec` + //~| NOTE found fn item `fn(_) -> _ {bar::>}` + //~| NOTE expected `String`, found `Vec` // Make sure we distinguish between trait methods correctly. eq(::foo, ::foo); //~^ ERROR mismatched types - //~| expected `u8`, found `u16` - //~| different fn items have unique types, even if their signatures are the same + //~| NOTE expected `u8`, found `u16` + //~| NOTE different fn items have unique types, even if their signatures are the same eq(foo::, bar:: as fn(isize) -> isize); //~^ ERROR mismatched types - //~| found fn pointer `fn(_) -> _` - //~| expected fn item, found fn pointer + //~| NOTE found fn pointer `fn(_) -> _` + //~| NOTE expected fn item, found fn pointer eq(foo:: as fn(isize) -> isize, bar::); // ok! } diff --git a/tests/ui/fn/fn-item-type.stderr b/tests/ui/fn/fn-item-type.stderr index 5cc529543d2ef..ea202f96a8ebe 100644 --- a/tests/ui/fn/fn-item-type.stderr +++ b/tests/ui/fn/fn-item-type.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/fn-item-type.rs:22:19 + --> $DIR/fn-item-type.rs:24:19 | LL | eq(foo::, bar::); | -- --------- ^^^^^^^^^ expected fn item, found a different fn item @@ -11,7 +11,7 @@ LL | eq(foo::, bar::); found fn item `fn(_) -> _ {bar::}` = note: different fn items have unique types, even if their signatures are the same note: function defined here - --> $DIR/fn-item-type.rs:11:4 + --> $DIR/fn-item-type.rs:13:4 | LL | fn eq(x: T, y: T) {} | ^^ - ---- ---- this parameter needs to match the fn item type of `x` @@ -21,7 +21,7 @@ LL | fn eq(x: T, y: T) {} = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize` error[E0308]: mismatched types - --> $DIR/fn-item-type.rs:29:19 + --> $DIR/fn-item-type.rs:31:19 | LL | eq(foo::, foo::); | -- --------- ^^^^^^^^^ expected `u8`, found `i8` @@ -33,7 +33,7 @@ LL | eq(foo::, foo::); found fn item `fn(_) -> _ {foo::}` = note: different fn items have unique types, even if their signatures are the same note: function defined here - --> $DIR/fn-item-type.rs:11:4 + --> $DIR/fn-item-type.rs:13:4 | LL | fn eq(x: T, y: T) {} | ^^ - ---- ---- this parameter needs to match the fn item type of `x` @@ -43,7 +43,7 @@ LL | fn eq(x: T, y: T) {} = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize` error[E0308]: mismatched types - --> $DIR/fn-item-type.rs:34:23 + --> $DIR/fn-item-type.rs:36:23 | LL | eq(bar::, bar::>); | -- ------------- ^^^^^^^^^^^^^^ expected `String`, found `Vec` @@ -55,7 +55,7 @@ LL | eq(bar::, bar::>); found fn item `fn(_) -> _ {bar::>}` = note: different fn items have unique types, even if their signatures are the same note: function defined here - --> $DIR/fn-item-type.rs:11:4 + --> $DIR/fn-item-type.rs:13:4 | LL | fn eq(x: T, y: T) {} | ^^ - ---- ---- this parameter needs to match the fn item type of `x` @@ -65,7 +65,7 @@ LL | fn eq(x: T, y: T) {} = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize` error[E0308]: mismatched types - --> $DIR/fn-item-type.rs:40:26 + --> $DIR/fn-item-type.rs:42:26 | LL | eq(::foo, ::foo); | -- ---------------- ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16` @@ -77,7 +77,7 @@ LL | eq(::foo, ::foo); found fn item `fn() {::foo}` = note: different fn items have unique types, even if their signatures are the same note: function defined here - --> $DIR/fn-item-type.rs:11:4 + --> $DIR/fn-item-type.rs:13:4 | LL | fn eq(x: T, y: T) {} | ^^ - ---- ---- this parameter needs to match the fn item type of `x` @@ -87,7 +87,7 @@ LL | fn eq(x: T, y: T) {} = help: consider casting both fn items to fn pointers using `as fn()` error[E0308]: mismatched types - --> $DIR/fn-item-type.rs:45:19 + --> $DIR/fn-item-type.rs:47:19 | LL | eq(foo::, bar:: as fn(isize) -> isize); | -- --------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer @@ -99,7 +99,7 @@ LL | eq(foo::, bar:: as fn(isize) -> isize); found fn pointer `fn(_) -> _` = help: consider casting the fn item to a fn pointer: `foo:: as fn(isize) -> isize` note: function defined here - --> $DIR/fn-item-type.rs:11:4 + --> $DIR/fn-item-type.rs:13:4 | LL | fn eq(x: T, y: T) {} | ^^ - ---- ---- this parameter needs to match the fn item type of `x` diff --git a/tests/ui/fn/fn-pointer-mismatch.rs b/tests/ui/fn/fn-pointer-mismatch.rs index 1c50d8b0f8b2d..3854c8eab688f 100644 --- a/tests/ui/fn/fn-pointer-mismatch.rs +++ b/tests/ui/fn/fn-pointer-mismatch.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: NOTE + fn foo(x: u32) -> u32 { x * 2 } @@ -10,7 +12,7 @@ fn bar(x: u32) -> u32 { fn foobar(n: u32) -> u32 { let g = if n % 2 == 0 { &foo } else { &bar }; //~^ ERROR `if` and `else` have incompatible types - //~| different fn items have unique types, even if their signatures are the same + //~| NOTE different fn items have unique types, even if their signatures are the same g(n) } @@ -22,33 +24,33 @@ fn main() { let mut a = foo; a = bar; //~^ ERROR mismatched types - //~| expected fn item `fn(_) -> _ {foo}` - //~| found fn item `fn(_) -> _ {bar}` - //~| different fn items have unique types, even if their signatures are the same + //~| NOTE expected fn item `fn(_) -> _ {foo}` + //~| NOTE found fn item `fn(_) -> _ {bar}` + //~| NOTE different fn items have unique types, even if their signatures are the same // display note even when boxed let mut b = Box::new(foo); b = Box::new(bar); //~^ ERROR mismatched types - //~| different fn items have unique types, even if their signatures are the same + //~| NOTE different fn items have unique types, even if their signatures are the same // suggest removing reference let c: fn(u32) -> u32 = &foo; //~^ ERROR mismatched types - //~| expected fn pointer `fn(_) -> _` - //~| found reference `&fn(_) -> _ {foo}` + //~| NOTE expected fn pointer `fn(_) -> _` + //~| NOTE found reference `&fn(_) -> _ {foo}` // suggest using reference let d: &fn(u32) -> u32 = foo; //~^ ERROR mismatched types - //~| expected reference `&fn(_) -> _` - //~| found fn item `fn(_) -> _ {foo}` + //~| NOTE expected reference `&fn(_) -> _` + //~| NOTE found fn item `fn(_) -> _ {foo}` // suggest casting with reference let e: &fn(u32) -> u32 = &foo; //~^ ERROR mismatched types - //~| expected reference `&fn(_) -> _` - //~| found reference `&fn(_) -> _ {foo}` + //~| NOTE expected reference `&fn(_) -> _` + //~| NOTE found reference `&fn(_) -> _ {foo}` // OK let mut z: fn(u32) -> u32 = foo as fn(u32) -> u32; diff --git a/tests/ui/fn/fn-pointer-mismatch.stderr b/tests/ui/fn/fn-pointer-mismatch.stderr index 051eb94546795..87a2e39913dd1 100644 --- a/tests/ui/fn/fn-pointer-mismatch.stderr +++ b/tests/ui/fn/fn-pointer-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: `if` and `else` have incompatible types - --> $DIR/fn-pointer-mismatch.rs:11:43 + --> $DIR/fn-pointer-mismatch.rs:13:43 | LL | let g = if n % 2 == 0 { &foo } else { &bar }; | ---- ^^^^ expected `&fn(u32) -> u32 {foo}`, found `&fn(u32) -> u32 {bar}` @@ -12,7 +12,7 @@ LL | let g = if n % 2 == 0 { &foo } else { &bar }; = help: consider casting both fn items to fn pointers using `as fn(u32) -> u32` error[E0308]: mismatched types - --> $DIR/fn-pointer-mismatch.rs:23:9 + --> $DIR/fn-pointer-mismatch.rs:25:9 | LL | let mut a = foo; | --- expected due to this value @@ -25,7 +25,7 @@ LL | a = bar; = help: consider casting both fn items to fn pointers using `as fn(u32) -> u32` error[E0308]: mismatched types - --> $DIR/fn-pointer-mismatch.rs:31:18 + --> $DIR/fn-pointer-mismatch.rs:33:18 | LL | b = Box::new(bar); | -------- ^^^ expected fn item, found a different fn item @@ -40,7 +40,7 @@ note: associated function defined here = help: consider casting both fn items to fn pointers using `as fn(u32) -> u32` error[E0308]: mismatched types - --> $DIR/fn-pointer-mismatch.rs:36:29 + --> $DIR/fn-pointer-mismatch.rs:38:29 | LL | let c: fn(u32) -> u32 = &foo; | -------------- ^^^^ expected fn pointer, found `&fn(u32) -> u32 {foo}` @@ -56,7 +56,7 @@ LL + let c: fn(u32) -> u32 = foo; | error[E0308]: mismatched types - --> $DIR/fn-pointer-mismatch.rs:42:30 + --> $DIR/fn-pointer-mismatch.rs:44:30 | LL | let d: &fn(u32) -> u32 = foo; | --------------- ^^^ expected `&fn(u32) -> u32`, found fn item @@ -71,7 +71,7 @@ LL | let d: &fn(u32) -> u32 = &foo; | + error[E0308]: mismatched types - --> $DIR/fn-pointer-mismatch.rs:48:30 + --> $DIR/fn-pointer-mismatch.rs:50:30 | LL | let e: &fn(u32) -> u32 = &foo; | --------------- ^^^^ expected `&fn(u32) -> u32`, found `&fn(u32) -> u32 {foo}` diff --git a/tests/ui/fn/fn-trait-formatting.rs b/tests/ui/fn/fn-trait-formatting.rs index 61a8791ab1f3e..be74d1ca9be3c 100644 --- a/tests/ui/fn/fn-trait-formatting.rs +++ b/tests/ui/fn/fn-trait-formatting.rs @@ -1,20 +1,20 @@ -fn needs_fn(x: F) where F: Fn(isize) -> isize {} - +//@ dont-require-annotations: NOTE +fn needs_fn(x: F) where F: Fn(isize) -> isize {} fn main() { let _: () = Box::new(|_: isize| {}) as Box; //~^ ERROR mismatched types - //~| expected unit type `()` - //~| found struct `Box` + //~| NOTE expected unit type `()` + //~| NOTE found struct `Box` let _: () = Box::new(|_: isize, isize| {}) as Box; //~^ ERROR mismatched types - //~| expected unit type `()` - //~| found struct `Box` + //~| NOTE expected unit type `()` + //~| NOTE found struct `Box` let _: () = Box::new(|| -> isize { unimplemented!() }) as Box isize>; //~^ ERROR mismatched types - //~| expected unit type `()` - //~| found struct `Box isize>` + //~| NOTE expected unit type `()` + //~| NOTE found struct `Box isize>` needs_fn(1); //~^ ERROR expected a `Fn(isize)` closure, found `{integer}` diff --git a/tests/ui/fn/fn-trait-formatting.stderr b/tests/ui/fn/fn-trait-formatting.stderr index 9fdef49c5ef94..bfd86106bf923 100644 --- a/tests/ui/fn/fn-trait-formatting.stderr +++ b/tests/ui/fn/fn-trait-formatting.stderr @@ -49,7 +49,7 @@ LL | needs_fn(1); | = help: the trait `Fn(isize)` is not implemented for `{integer}` note: required by a bound in `needs_fn` - --> $DIR/fn-trait-formatting.rs:1:31 + --> $DIR/fn-trait-formatting.rs:3:31 | LL | fn needs_fn(x: F) where F: Fn(isize) -> isize {} | ^^^^^^^^^^^^^^^^^^ required by this bound in `needs_fn` diff --git a/tests/ui/fn/fn-trait-use-named-params-issue-140169.rs b/tests/ui/fn/fn-trait-use-named-params-issue-140169.rs new file mode 100644 index 0000000000000..218450abd4998 --- /dev/null +++ b/tests/ui/fn/fn-trait-use-named-params-issue-140169.rs @@ -0,0 +1,12 @@ +fn f1(_: fn(a: u8)) {} +fn f2(_: impl Fn(u8, vvvv: u8)) {} //~ ERROR `Trait(...)` syntax does not support named parameters +fn f3(_: impl Fn(aaaa: u8, u8)) {} //~ ERROR `Trait(...)` syntax does not support named parameters +fn f4(_: impl Fn(aaaa: u8, vvvv: u8)) {} +//~^ ERROR `Trait(...)` syntax does not support named parameters +//~| ERROR `Trait(...)` syntax does not support named parameters +fn f5(_: impl Fn(u8, ...)) {} +//~^ ERROR `Trait(...)` syntax does not support c_variadic parameters +fn f6(_: impl Fn(u8, #[allow(unused_attributes)] u8)) {} +//~^ ERROR `Trait(...)` syntax does not support attributes in parameters + +fn main(){} diff --git a/tests/ui/fn/fn-trait-use-named-params-issue-140169.stderr b/tests/ui/fn/fn-trait-use-named-params-issue-140169.stderr new file mode 100644 index 0000000000000..b72d5b7b3bc48 --- /dev/null +++ b/tests/ui/fn/fn-trait-use-named-params-issue-140169.stderr @@ -0,0 +1,38 @@ +error: `Trait(...)` syntax does not support named parameters + --> $DIR/fn-trait-use-named-params-issue-140169.rs:2:22 + | +LL | fn f2(_: impl Fn(u8, vvvv: u8)) {} + | ^^^^ help: remove the parameter name + +error: `Trait(...)` syntax does not support named parameters + --> $DIR/fn-trait-use-named-params-issue-140169.rs:3:18 + | +LL | fn f3(_: impl Fn(aaaa: u8, u8)) {} + | ^^^^ help: remove the parameter name + +error: `Trait(...)` syntax does not support named parameters + --> $DIR/fn-trait-use-named-params-issue-140169.rs:4:18 + | +LL | fn f4(_: impl Fn(aaaa: u8, vvvv: u8)) {} + | ^^^^ help: remove the parameter name + +error: `Trait(...)` syntax does not support named parameters + --> $DIR/fn-trait-use-named-params-issue-140169.rs:4:28 + | +LL | fn f4(_: impl Fn(aaaa: u8, vvvv: u8)) {} + | ^^^^ help: remove the parameter name + +error: `Trait(...)` syntax does not support c_variadic parameters + --> $DIR/fn-trait-use-named-params-issue-140169.rs:7:22 + | +LL | fn f5(_: impl Fn(u8, ...)) {} + | ^^^ help: remove the `...` + +error: `Trait(...)` syntax does not support attributes in parameters + --> $DIR/fn-trait-use-named-params-issue-140169.rs:9:22 + | +LL | fn f6(_: impl Fn(u8, #[allow(unused_attributes)] u8)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the attributes + +error: aborting due to 6 previous errors + diff --git a/tests/ui/fn/fn_def_opaque_coercion.rs b/tests/ui/fn/fn_def_opaque_coercion.rs index 0a8810cf4f8ac..8235878c59d7a 100644 --- a/tests/ui/fn/fn_def_opaque_coercion.rs +++ b/tests/ui/fn/fn_def_opaque_coercion.rs @@ -11,6 +11,7 @@ fn foo(t: T) -> T { type F = impl Sized; +#[define_opaque(F)] fn f(a: F) { let mut x = foo::; x = foo::<()>; @@ -20,6 +21,7 @@ fn f(a: F) { type G = impl Sized; +#[define_opaque(G)] fn g(a: G) { let x = foo::<()>; let _: () = x(a); @@ -27,6 +29,7 @@ fn g(a: G) { type H = impl Sized; +#[define_opaque(H)] fn h(a: H) { let x = foo::; let _: H = x(()); @@ -34,6 +37,7 @@ fn h(a: H) { type I = impl Sized; +#[define_opaque(I)] fn i(a: I) { let mut x = foo::<()>; x = foo::; @@ -43,6 +47,7 @@ fn i(a: I) { type J = impl Sized; +#[define_opaque(J)] fn j(a: J) { let x = match true { true => foo::, diff --git a/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.rs b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.rs index 5250e3a3d93e6..e090b7039b8d7 100644 --- a/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.rs +++ b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.rs @@ -13,6 +13,7 @@ fn bar(t: T) -> T { type F = impl Sized; +#[define_opaque(F)] fn f(a: F) { let mut x = bar::; x = foo::<()>; //~ ERROR: mismatched types @@ -22,6 +23,7 @@ fn f(a: F) { type I = impl Sized; +#[define_opaque(I)] fn i(a: I) { let mut x = bar::<()>; x = foo::; //~ ERROR: mismatched types @@ -31,6 +33,7 @@ fn i(a: I) { type J = impl Sized; +#[define_opaque(J)] fn j(a: J) { let x = match true { true => bar::, diff --git a/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr index 5000601e90f86..4a0991d0eb3cb 100644 --- a/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr +++ b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/fn_def_opaque_coercion_to_fn_ptr.rs:18:9 + --> $DIR/fn_def_opaque_coercion_to_fn_ptr.rs:19:9 | LL | type F = impl Sized; | ---------- the expected opaque type @@ -13,7 +13,7 @@ LL | x = foo::<()>; found fn item `fn(()) -> () {foo::<()>}` error[E0308]: mismatched types - --> $DIR/fn_def_opaque_coercion_to_fn_ptr.rs:27:9 + --> $DIR/fn_def_opaque_coercion_to_fn_ptr.rs:29:9 | LL | fn foo(t: T) -> T { | -------------------- function `foo` defined here diff --git a/tests/ui/fn/signature-error-reporting-under-verbose.rs b/tests/ui/fn/signature-error-reporting-under-verbose.rs index 4a72da7897896..0047d452b7a68 100644 --- a/tests/ui/fn/signature-error-reporting-under-verbose.rs +++ b/tests/ui/fn/signature-error-reporting-under-verbose.rs @@ -4,7 +4,6 @@ fn foo(_: i32, _: i32) {} fn needs_ptr(_: fn(i32, u32)) {} //~^ NOTE function defined here -//~| NOTE fn main() { needs_ptr(foo); diff --git a/tests/ui/fn/signature-error-reporting-under-verbose.stderr b/tests/ui/fn/signature-error-reporting-under-verbose.stderr index 6c6a313c4df71..d9ccec23fcb61 100644 --- a/tests/ui/fn/signature-error-reporting-under-verbose.stderr +++ b/tests/ui/fn/signature-error-reporting-under-verbose.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/signature-error-reporting-under-verbose.rs:10:15 + --> $DIR/signature-error-reporting-under-verbose.rs:9:15 | LL | needs_ptr(foo); | --------- ^^^ expected fn pointer, found fn item diff --git a/tests/ui/fn/suggest-return-closure.rs b/tests/ui/fn/suggest-return-closure.rs index 30e25ca8edccd..67be8de92431b 100644 --- a/tests/ui/fn/suggest-return-closure.rs +++ b/tests/ui/fn/suggest-return-closure.rs @@ -19,6 +19,7 @@ fn fn_mut() -> _ { let x = String::new(); //~^ HELP: consider changing this to be mutable //~| NOTE binding `x` declared here + //~| SUGGESTION mut |c| { //~ NOTE: value captured here x.push(c); //~^ ERROR: does not live long enough diff --git a/tests/ui/fn/suggest-return-closure.stderr b/tests/ui/fn/suggest-return-closure.stderr index 45c12b548e678..1860d1ca5d927 100644 --- a/tests/ui/fn/suggest-return-closure.stderr +++ b/tests/ui/fn/suggest-return-closure.stderr @@ -21,7 +21,7 @@ LL | fn fn_mut() -> _ { = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/suggest-return-closure.rs:32:13 + --> $DIR/suggest-return-closure.rs:33:13 | LL | fn fun() -> _ { | ^ @@ -32,7 +32,7 @@ LL | fn fun() -> _ { = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable - --> $DIR/suggest-return-closure.rs:23:9 + --> $DIR/suggest-return-closure.rs:24:9 | LL | x.push(c); | ^ cannot borrow as mutable @@ -43,7 +43,7 @@ LL | let mut x = String::new(); | +++ error[E0597]: `x` does not live long enough - --> $DIR/suggest-return-closure.rs:23:9 + --> $DIR/suggest-return-closure.rs:24:9 | LL | let x = String::new(); | - binding `x` declared here diff --git a/tests/ui/fn/trait-fn-generic-mismatch.rs b/tests/ui/fn/trait-fn-generic-mismatch.rs new file mode 100644 index 0000000000000..dc8222e967e4c --- /dev/null +++ b/tests/ui/fn/trait-fn-generic-mismatch.rs @@ -0,0 +1,12 @@ +fn retry() -> impl Sized {} + +struct Core(T); + +impl Core { //~ ERROR cannot find type `XXX` in this scope + pub fn spawn(self) {} +} + +fn main() { + let core = Core(1); + core.spawn(retry()); //~ ERROR this method takes 0 arguments but 1 argument was supplied +} diff --git a/tests/ui/fn/trait-fn-generic-mismatch.stderr b/tests/ui/fn/trait-fn-generic-mismatch.stderr new file mode 100644 index 0000000000000..8384d74e225aa --- /dev/null +++ b/tests/ui/fn/trait-fn-generic-mismatch.stderr @@ -0,0 +1,32 @@ +error[E0412]: cannot find type `XXX` in this scope + --> $DIR/trait-fn-generic-mismatch.rs:5:11 + | +LL | impl Core { + | ^^^ not found in this scope + | +help: you might be missing a type parameter + | +LL | impl Core { + | +++++ + +error[E0061]: this method takes 0 arguments but 1 argument was supplied + --> $DIR/trait-fn-generic-mismatch.rs:11:10 + | +LL | core.spawn(retry()); + | ^^^^^ ------- unexpected argument of type `impl Sized` + | +note: method defined here + --> $DIR/trait-fn-generic-mismatch.rs:6:12 + | +LL | pub fn spawn(self) {} + | ^^^^^ +help: remove the extra argument + | +LL - core.spawn(retry()); +LL + core.spawn(); + | + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0061, E0412. +For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.rs b/tests/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.rs index 7f1625c9265ae..97cf3e9d51cd6 100644 --- a/tests/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.rs +++ b/tests/ui/foreign/issue-74120-lowering-of-ffi-block-bodies.rs @@ -5,7 +5,7 @@ extern "C" { fn f() { - //~^ incorrect function inside `extern` block + //~^ ERROR incorrect function inside `extern` block fn g() {} } } diff --git a/tests/ui/foreign/issue-91370-foreign-fn-block-impl.rs b/tests/ui/foreign/issue-91370-foreign-fn-block-impl.rs index e8634de86ea49..5317531cecd2e 100644 --- a/tests/ui/foreign/issue-91370-foreign-fn-block-impl.rs +++ b/tests/ui/foreign/issue-91370-foreign-fn-block-impl.rs @@ -1,10 +1,11 @@ // Regression test for issue #91370. extern "C" { - //~^ `extern` blocks define existing foreign functions + //~^ NOTE `extern` blocks define existing foreign functions fn f() { - //~^ incorrect function inside `extern` block - //~| cannot have a body + //~^ ERROR incorrect function inside `extern` block + //~| NOTE cannot have a body + //~| NOTE for more information, visit https://doc.rust-lang.org/std/keyword.extern.html impl Copy for u8 {} } } diff --git a/tests/ui/foreign/issue-91370-foreign-fn-block-impl.stderr b/tests/ui/foreign/issue-91370-foreign-fn-block-impl.stderr index 155fdf9d09ab0..ad6aff5d8c125 100644 --- a/tests/ui/foreign/issue-91370-foreign-fn-block-impl.stderr +++ b/tests/ui/foreign/issue-91370-foreign-fn-block-impl.stderr @@ -8,9 +8,7 @@ LL | fn f() { | ________^___- | | | | | cannot have a body -LL | | -LL | | -LL | | impl Copy for u8 {} +... | LL | | } | |_____- help: remove the invalid body: `;` | diff --git a/tests/ui/frontmatter/auxiliary/lib.rs b/tests/ui/frontmatter/auxiliary/lib.rs new file mode 100644 index 0000000000000..ff7094f410f5f --- /dev/null +++ b/tests/ui/frontmatter/auxiliary/lib.rs @@ -0,0 +1,6 @@ +---something +--- + +pub fn foo(x: i32) -> i32 { + -x +} diff --git a/tests/ui/frontmatter/auxiliary/makro.rs b/tests/ui/frontmatter/auxiliary/makro.rs new file mode 100644 index 0000000000000..78e7417afb5f6 --- /dev/null +++ b/tests/ui/frontmatter/auxiliary/makro.rs @@ -0,0 +1,8 @@ +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro] +pub fn check(_: TokenStream) -> TokenStream { + assert!("---\n---".parse::().unwrap().is_empty()); + Default::default() +} diff --git a/tests/ui/frontmatter/dot-in-infostring-leading.rs b/tests/ui/frontmatter/dot-in-infostring-leading.rs new file mode 100644 index 0000000000000..0d3d699644e9c --- /dev/null +++ b/tests/ui/frontmatter/dot-in-infostring-leading.rs @@ -0,0 +1,9 @@ +---.toml +//~^ ERROR: invalid infostring for frontmatter +--- + +// infostrings cannot have leading dots + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/dot-in-infostring-leading.stderr b/tests/ui/frontmatter/dot-in-infostring-leading.stderr new file mode 100644 index 0000000000000..bc86bd80eece8 --- /dev/null +++ b/tests/ui/frontmatter/dot-in-infostring-leading.stderr @@ -0,0 +1,10 @@ +error: invalid infostring for frontmatter + --> $DIR/dot-in-infostring-leading.rs:1:4 + | +LL | ---.toml + | ^^^^^ + | + = note: frontmatter infostrings must be a single identifier immediately following the opening + +error: aborting due to 1 previous error + diff --git a/tests/ui/frontmatter/dot-in-infostring-non-leading.rs b/tests/ui/frontmatter/dot-in-infostring-non-leading.rs new file mode 100644 index 0000000000000..a4d17bb6e813a --- /dev/null +++ b/tests/ui/frontmatter/dot-in-infostring-non-leading.rs @@ -0,0 +1,9 @@ +---Cargo.toml +--- + +// infostrings can contain dots as long as a dot isn't the first character. +//@ check-pass + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/escape.rs b/tests/ui/frontmatter/escape.rs new file mode 100644 index 0000000000000..0e6f064607e82 --- /dev/null +++ b/tests/ui/frontmatter/escape.rs @@ -0,0 +1,14 @@ +---- + +--- + +---- + +//@ check-pass + +// This test checks that longer dashes for opening and closing can be used to +// escape sequences such as three dashes inside the frontmatter block. + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/extra-after-end.rs b/tests/ui/frontmatter/extra-after-end.rs new file mode 100644 index 0000000000000..de2cf4cc85efd --- /dev/null +++ b/tests/ui/frontmatter/extra-after-end.rs @@ -0,0 +1,7 @@ +--- +---cargo +//~^ ERROR: extra characters after frontmatter close are not allowed + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/extra-after-end.stderr b/tests/ui/frontmatter/extra-after-end.stderr new file mode 100644 index 0000000000000..c2770fdfd41ff --- /dev/null +++ b/tests/ui/frontmatter/extra-after-end.stderr @@ -0,0 +1,8 @@ +error: extra characters after frontmatter close are not allowed + --> $DIR/extra-after-end.rs:2:1 + | +LL | ---cargo + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/frontmatter/frontmatter-after-tokens.rs b/tests/ui/frontmatter/frontmatter-after-tokens.rs new file mode 100644 index 0000000000000..6683991dc4af4 --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-after-tokens.rs @@ -0,0 +1,10 @@ +#![feature(frontmatter)] + +--- +//~^ ERROR: expected item, found `-` +// FIXME(frontmatter): make this diagnostic better +--- + +// frontmatters must be at the start of a file. This test ensures that. + +fn main() {} diff --git a/tests/ui/frontmatter/frontmatter-after-tokens.stderr b/tests/ui/frontmatter/frontmatter-after-tokens.stderr new file mode 100644 index 0000000000000..919456924d027 --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-after-tokens.stderr @@ -0,0 +1,10 @@ +error: expected item, found `-` + --> $DIR/frontmatter-after-tokens.rs:3:1 + | +LL | --- + | ^ expected item + | + = note: for a full list of items that can appear in modules, see + +error: aborting due to 1 previous error + diff --git a/tests/ui/frontmatter/frontmatter-non-lexible-tokens.rs b/tests/ui/frontmatter/frontmatter-non-lexible-tokens.rs new file mode 100644 index 0000000000000..ea042edef06b2 --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-non-lexible-tokens.rs @@ -0,0 +1,12 @@ +---uwu +🏳️‍⚧️ +--- + +//@ check-pass + +#![feature(frontmatter)] + +// check that frontmatter blocks can have tokens that are otherwise not accepted by +// the lexer as Rust code. + +fn main() {} diff --git a/tests/ui/frontmatter/frontmatter-whitespace-1.rs b/tests/ui/frontmatter/frontmatter-whitespace-1.rs new file mode 100644 index 0000000000000..8b6e2d1af8499 --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-whitespace-1.rs @@ -0,0 +1,10 @@ + --- +//~^ ERROR: invalid preceding whitespace for frontmatter opening + --- +//~^ ERROR: invalid preceding whitespace for frontmatter close + +#![feature(frontmatter)] + +// check that whitespaces should not precede the frontmatter opening or close. + +fn main() {} diff --git a/tests/ui/frontmatter/frontmatter-whitespace-1.stderr b/tests/ui/frontmatter/frontmatter-whitespace-1.stderr new file mode 100644 index 0000000000000..37ece27acb225 --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-whitespace-1.stderr @@ -0,0 +1,26 @@ +error: invalid preceding whitespace for frontmatter opening + --> $DIR/frontmatter-whitespace-1.rs:1:1 + | +LL | --- + | ^^^^^ + | +note: frontmatter opening should not be preceded by whitespace + --> $DIR/frontmatter-whitespace-1.rs:1:1 + | +LL | --- + | ^^ + +error: invalid preceding whitespace for frontmatter close + --> $DIR/frontmatter-whitespace-1.rs:3:1 + | +LL | --- + | ^^^^^ + | +note: frontmatter close should not be preceded by whitespace + --> $DIR/frontmatter-whitespace-1.rs:3:1 + | +LL | --- + | ^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/frontmatter/frontmatter-whitespace-2.rs b/tests/ui/frontmatter/frontmatter-whitespace-2.rs new file mode 100644 index 0000000000000..e8c100849b407 --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-whitespace-2.rs @@ -0,0 +1,15 @@ +---cargo + +//@ compile-flags: --crate-type lib + +#![feature(frontmatter)] + +fn foo(x: i32) -> i32 { + ---x + //~^ ERROR: invalid preceding whitespace for frontmatter close + //~| ERROR: extra characters after frontmatter close are not allowed +} +//~^ ERROR: unexpected closing delimiter: `}` + +// this test is for the weird case that valid Rust code can have three dashes +// within them and get treated as a frontmatter close. diff --git a/tests/ui/frontmatter/frontmatter-whitespace-2.stderr b/tests/ui/frontmatter/frontmatter-whitespace-2.stderr new file mode 100644 index 0000000000000..ada6af0ec04ce --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-whitespace-2.stderr @@ -0,0 +1,26 @@ +error: invalid preceding whitespace for frontmatter close + --> $DIR/frontmatter-whitespace-2.rs:8:1 + | +LL | ---x + | ^^^^^^^^ + | +note: frontmatter close should not be preceded by whitespace + --> $DIR/frontmatter-whitespace-2.rs:8:1 + | +LL | ---x + | ^^^^ + +error: extra characters after frontmatter close are not allowed + --> $DIR/frontmatter-whitespace-2.rs:8:1 + | +LL | ---x + | ^^^^^^^^ + +error: unexpected closing delimiter: `}` + --> $DIR/frontmatter-whitespace-2.rs:11:1 + | +LL | } + | ^ unexpected closing delimiter + +error: aborting due to 3 previous errors + diff --git a/tests/ui/frontmatter/frontmatter-whitespace-3.rs b/tests/ui/frontmatter/frontmatter-whitespace-3.rs new file mode 100644 index 0000000000000..95e0981e2ae8b --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-whitespace-3.rs @@ -0,0 +1,16 @@ + + +---cargo +--- + +// please note the whitespace characters after the first four lines. +// This ensures that we accept whitespaces before the frontmatter, after +// the frontmatter opening and the frontmatter close. + +//@ check-pass +// ignore-tidy-end-whitespace +// ignore-tidy-leading-newlines + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/frontmatter-whitespace-4.rs b/tests/ui/frontmatter/frontmatter-whitespace-4.rs new file mode 100644 index 0000000000000..3bda3227838c7 --- /dev/null +++ b/tests/ui/frontmatter/frontmatter-whitespace-4.rs @@ -0,0 +1,9 @@ +--- cargo +--- + +//@ check-pass +// A frontmatter infostring can have leading whitespace. + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/included-frontmatter.rs b/tests/ui/frontmatter/included-frontmatter.rs new file mode 100644 index 0000000000000..57616cd1228c1 --- /dev/null +++ b/tests/ui/frontmatter/included-frontmatter.rs @@ -0,0 +1,12 @@ +#![feature(frontmatter)] + +//@ check-pass + +include!("auxiliary/lib.rs"); + +// auxiliary/lib.rs contains a frontmatter. Ensure that we can use them in an +// `include!` macro. + +fn main() { + foo(1); +} diff --git a/tests/ui/frontmatter/infostring-fail.rs b/tests/ui/frontmatter/infostring-fail.rs new file mode 100644 index 0000000000000..91542f62f1a50 --- /dev/null +++ b/tests/ui/frontmatter/infostring-fail.rs @@ -0,0 +1,9 @@ +---cargo,clippy +//~^ ERROR: invalid infostring for frontmatter +--- + +// infostrings can only be a single identifier. + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/infostring-fail.stderr b/tests/ui/frontmatter/infostring-fail.stderr new file mode 100644 index 0000000000000..6b264abc90f11 --- /dev/null +++ b/tests/ui/frontmatter/infostring-fail.stderr @@ -0,0 +1,10 @@ +error: invalid infostring for frontmatter + --> $DIR/infostring-fail.rs:1:4 + | +LL | ---cargo,clippy + | ^^^^^^^^^^^^ + | + = note: frontmatter infostrings must be a single identifier immediately following the opening + +error: aborting due to 1 previous error + diff --git a/tests/ui/frontmatter/mismatch-1.rs b/tests/ui/frontmatter/mismatch-1.rs new file mode 100644 index 0000000000000..37e2b0af6b187 --- /dev/null +++ b/tests/ui/frontmatter/mismatch-1.rs @@ -0,0 +1,10 @@ +---cargo +//~^ ERROR: frontmatter close does not match the opening +---- + +// there must be the same number of dashes for both the opening and the close +// of the frontmatter. + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/mismatch-1.stderr b/tests/ui/frontmatter/mismatch-1.stderr new file mode 100644 index 0000000000000..b6e29294d9e14 --- /dev/null +++ b/tests/ui/frontmatter/mismatch-1.stderr @@ -0,0 +1,16 @@ +error: frontmatter close does not match the opening + --> $DIR/mismatch-1.rs:1:1 + | +LL | ---cargo + | ^-- + | | + | _the opening here has 3 dashes... + | | +LL | | +LL | | ---- + | |_---^ + | | + | ...while the close has 4 dashes + +error: aborting due to 1 previous error + diff --git a/tests/ui/frontmatter/mismatch-2.rs b/tests/ui/frontmatter/mismatch-2.rs new file mode 100644 index 0000000000000..422abe1d6bc99 --- /dev/null +++ b/tests/ui/frontmatter/mismatch-2.rs @@ -0,0 +1,8 @@ +----cargo +//~^ ERROR: frontmatter close does not match the opening +---cargo +//~^ ERROR: extra characters after frontmatter close are not allowed + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/mismatch-2.stderr b/tests/ui/frontmatter/mismatch-2.stderr new file mode 100644 index 0000000000000..90bb7b80bcea0 --- /dev/null +++ b/tests/ui/frontmatter/mismatch-2.stderr @@ -0,0 +1,22 @@ +error: frontmatter close does not match the opening + --> $DIR/mismatch-2.rs:1:1 + | +LL | ----cargo + | ^--- + | | + | _the opening here has 4 dashes... + | | +LL | | +LL | | ---cargo + | |_---____^ + | | + | ...while the close has 3 dashes + +error: extra characters after frontmatter close are not allowed + --> $DIR/mismatch-2.rs:3:1 + | +LL | ---cargo + | ^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/frontmatter/multifrontmatter-2.rs b/tests/ui/frontmatter/multifrontmatter-2.rs new file mode 100644 index 0000000000000..33cc30cb46554 --- /dev/null +++ b/tests/ui/frontmatter/multifrontmatter-2.rs @@ -0,0 +1,12 @@ +--- + --- +//~^ ERROR: invalid preceding whitespace for frontmatter close + + --- +//~^ ERROR: expected item, found `-` +// FIXME(frontmatter): make this diagnostic better +--- + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/multifrontmatter-2.stderr b/tests/ui/frontmatter/multifrontmatter-2.stderr new file mode 100644 index 0000000000000..ed9ac4029e277 --- /dev/null +++ b/tests/ui/frontmatter/multifrontmatter-2.stderr @@ -0,0 +1,22 @@ +error: invalid preceding whitespace for frontmatter close + --> $DIR/multifrontmatter-2.rs:2:1 + | +LL | --- + | ^^^^ + | +note: frontmatter close should not be preceded by whitespace + --> $DIR/multifrontmatter-2.rs:2:1 + | +LL | --- + | ^ + +error: expected item, found `-` + --> $DIR/multifrontmatter-2.rs:5:2 + | +LL | --- + | ^ expected item + | + = note: for a full list of items that can appear in modules, see + +error: aborting due to 2 previous errors + diff --git a/tests/ui/frontmatter/multifrontmatter.rs b/tests/ui/frontmatter/multifrontmatter.rs new file mode 100644 index 0000000000000..f3afa47bd3832 --- /dev/null +++ b/tests/ui/frontmatter/multifrontmatter.rs @@ -0,0 +1,13 @@ +--- +--- + +--- +//~^ ERROR: expected item, found `-` +// FIXME(frontmatter): make this diagnostic better +--- + +// test that we do not parse another frontmatter block after the first one. + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/multifrontmatter.stderr b/tests/ui/frontmatter/multifrontmatter.stderr new file mode 100644 index 0000000000000..2e9d1cee9ddb4 --- /dev/null +++ b/tests/ui/frontmatter/multifrontmatter.stderr @@ -0,0 +1,10 @@ +error: expected item, found `-` + --> $DIR/multifrontmatter.rs:4:1 + | +LL | --- + | ^ expected item + | + = note: for a full list of items that can appear in modules, see + +error: aborting due to 1 previous error + diff --git a/tests/ui/frontmatter/proc-macro-observer.rs b/tests/ui/frontmatter/proc-macro-observer.rs new file mode 100644 index 0000000000000..bafbe912032eb --- /dev/null +++ b/tests/ui/frontmatter/proc-macro-observer.rs @@ -0,0 +1,12 @@ +//@ check-pass +//@ proc-macro: makro.rs +//@ edition: 2021 + +#![feature(frontmatter)] + +makro::check!(); + +// checks that a proc-macro cannot observe frontmatter tokens. +// see auxiliary/makro.rs for how it is tested. + +fn main() {} diff --git a/tests/ui/frontmatter/shebang.rs b/tests/ui/frontmatter/shebang.rs new file mode 100644 index 0000000000000..abd983f219bdd --- /dev/null +++ b/tests/ui/frontmatter/shebang.rs @@ -0,0 +1,13 @@ +#!/usr/bin/env -S cargo -Zscript +--- +[dependencies] +clap = "4" +--- + +//@ check-pass + +// Shebangs on a file can precede a frontmatter. + +#![feature(frontmatter)] + +fn main () {} diff --git a/tests/ui/frontmatter/unclosed-1.rs b/tests/ui/frontmatter/unclosed-1.rs new file mode 100644 index 0000000000000..d8b52b3e69c7c --- /dev/null +++ b/tests/ui/frontmatter/unclosed-1.rs @@ -0,0 +1,10 @@ +----cargo +//~^ ERROR: unclosed frontmatter + +// This test checks that the #! characters can help us recover a frontmatter +// close. There should not be a "missing `main` function" error as the rest +// are properly parsed. + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/unclosed-1.stderr b/tests/ui/frontmatter/unclosed-1.stderr new file mode 100644 index 0000000000000..04031d128398c --- /dev/null +++ b/tests/ui/frontmatter/unclosed-1.stderr @@ -0,0 +1,16 @@ +error: unclosed frontmatter + --> $DIR/unclosed-1.rs:1:1 + | +LL | / ----cargo +... | +LL | | + | |_^ + | +note: frontmatter opening here was not closed + --> $DIR/unclosed-1.rs:1:1 + | +LL | ----cargo + | ^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/frontmatter/unclosed-2.rs b/tests/ui/frontmatter/unclosed-2.rs new file mode 100644 index 0000000000000..add266dce5b0a --- /dev/null +++ b/tests/ui/frontmatter/unclosed-2.rs @@ -0,0 +1,15 @@ +----cargo +//~^ ERROR: unclosed frontmatter +//~| ERROR: frontmatters are experimental + +//@ compile-flags: --crate-type lib + +// Leading whitespace on the feature line prevents recovery. However +// the dashes quoted will not be used for recovery and the entire file +// should be treated as within the frontmatter block. + + #![feature(frontmatter)] + +fn foo() -> &str { + "----" +} diff --git a/tests/ui/frontmatter/unclosed-2.stderr b/tests/ui/frontmatter/unclosed-2.stderr new file mode 100644 index 0000000000000..0a4022c1557b7 --- /dev/null +++ b/tests/ui/frontmatter/unclosed-2.stderr @@ -0,0 +1,31 @@ +error: unclosed frontmatter + --> $DIR/unclosed-2.rs:1:1 + | +LL | / ----cargo +... | +LL | | "----" +LL | | } + | |__^ + | +note: frontmatter opening here was not closed + --> $DIR/unclosed-2.rs:1:1 + | +LL | ----cargo + | ^^^^ + +error[E0658]: frontmatters are experimental + --> $DIR/unclosed-2.rs:1:1 + | +LL | / ----cargo +... | +LL | | "----" +LL | | } + | |__^ + | + = note: see issue #136889 for more information + = help: add `#![feature(frontmatter)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/frontmatter/unclosed-3.rs b/tests/ui/frontmatter/unclosed-3.rs new file mode 100644 index 0000000000000..75f3fbda675ce --- /dev/null +++ b/tests/ui/frontmatter/unclosed-3.rs @@ -0,0 +1,16 @@ +----cargo +//~^ ERROR: frontmatter close does not match the opening + +//@ compile-flags: --crate-type lib + +// Unfortunate recovery situation. Not really preventable with improving the +// recovery strategy, but this type of code is rare enough already. + + #![feature(frontmatter)] + +fn foo(x: i32) -> i32 { + ---x + //~^ ERROR: invalid preceding whitespace for frontmatter close + //~| ERROR: extra characters after frontmatter close are not allowed +} +//~^ ERROR: unexpected closing delimiter: `}` diff --git a/tests/ui/frontmatter/unclosed-3.stderr b/tests/ui/frontmatter/unclosed-3.stderr new file mode 100644 index 0000000000000..cd69cb0004080 --- /dev/null +++ b/tests/ui/frontmatter/unclosed-3.stderr @@ -0,0 +1,41 @@ +error: invalid preceding whitespace for frontmatter close + --> $DIR/unclosed-3.rs:12:1 + | +LL | ---x + | ^^^^^^^^ + | +note: frontmatter close should not be preceded by whitespace + --> $DIR/unclosed-3.rs:12:1 + | +LL | ---x + | ^^^^ + +error: frontmatter close does not match the opening + --> $DIR/unclosed-3.rs:1:1 + | +LL | ----cargo + | ^--- + | | + | _the opening here has 4 dashes... + | | +... | +LL | | fn foo(x: i32) -> i32 { +LL | | ---x + | |_---____^ + | | + | ...while the close has 3 dashes + +error: extra characters after frontmatter close are not allowed + --> $DIR/unclosed-3.rs:12:1 + | +LL | ---x + | ^^^^^^^^ + +error: unexpected closing delimiter: `}` + --> $DIR/unclosed-3.rs:15:1 + | +LL | } + | ^ unexpected closing delimiter + +error: aborting due to 4 previous errors + diff --git a/tests/ui/frontmatter/unclosed-4.rs b/tests/ui/frontmatter/unclosed-4.rs new file mode 100644 index 0000000000000..41f6461db634d --- /dev/null +++ b/tests/ui/frontmatter/unclosed-4.rs @@ -0,0 +1,9 @@ +----cargo +//~^ ERROR: unclosed frontmatter + +//! Similarly, a module-level content should allow for recovery as well (as +//! per unclosed-1.rs) + +#![feature(frontmatter)] + +fn main() {} diff --git a/tests/ui/frontmatter/unclosed-4.stderr b/tests/ui/frontmatter/unclosed-4.stderr new file mode 100644 index 0000000000000..b3ba56937bbad --- /dev/null +++ b/tests/ui/frontmatter/unclosed-4.stderr @@ -0,0 +1,16 @@ +error: unclosed frontmatter + --> $DIR/unclosed-4.rs:1:1 + | +LL | / ----cargo +LL | | +LL | | + | |_^ + | +note: frontmatter opening here was not closed + --> $DIR/unclosed-4.rs:1:1 + | +LL | ----cargo + | ^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/frontmatter/unclosed-5.rs b/tests/ui/frontmatter/unclosed-5.rs new file mode 100644 index 0000000000000..9abbcfff94c76 --- /dev/null +++ b/tests/ui/frontmatter/unclosed-5.rs @@ -0,0 +1,10 @@ +----cargo +//~^ ERROR: unclosed frontmatter +//~| ERROR: frontmatters are experimental + +// Similarly, a use statement should allow for recovery as well (as +// per unclosed-1.rs) + +use std::env; + +fn main() {} diff --git a/tests/ui/frontmatter/unclosed-5.stderr b/tests/ui/frontmatter/unclosed-5.stderr new file mode 100644 index 0000000000000..e904014a175a5 --- /dev/null +++ b/tests/ui/frontmatter/unclosed-5.stderr @@ -0,0 +1,29 @@ +error: unclosed frontmatter + --> $DIR/unclosed-5.rs:1:1 + | +LL | / ----cargo +... | +LL | | + | |_^ + | +note: frontmatter opening here was not closed + --> $DIR/unclosed-5.rs:1:1 + | +LL | ----cargo + | ^^^^ + +error[E0658]: frontmatters are experimental + --> $DIR/unclosed-5.rs:1:1 + | +LL | / ----cargo +... | +LL | | + | |_^ + | + = note: see issue #136889 for more information + = help: add `#![feature(frontmatter)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/fully-qualified-type/fully-qualified-type-name2.rs b/tests/ui/fully-qualified-type/fully-qualified-type-name2.rs index f26d3be66303d..983d694c44089 100644 --- a/tests/ui/fully-qualified-type/fully-qualified-type-name2.rs +++ b/tests/ui/fully-qualified-type/fully-qualified-type-name2.rs @@ -1,5 +1,7 @@ // Test that we use fully-qualified type names in error messages. +//@ dont-require-annotations: NOTE + mod x { pub enum Foo { } } @@ -11,7 +13,7 @@ mod y { fn bar(x: x::Foo) -> y::Foo { return x; //~^ ERROR mismatched types - //~| expected `y::Foo`, found `x::Foo` + //~| NOTE expected `y::Foo`, found `x::Foo` } fn main() { diff --git a/tests/ui/fully-qualified-type/fully-qualified-type-name2.stderr b/tests/ui/fully-qualified-type/fully-qualified-type-name2.stderr index af716916548a9..90e54dfea2f78 100644 --- a/tests/ui/fully-qualified-type/fully-qualified-type-name2.stderr +++ b/tests/ui/fully-qualified-type/fully-qualified-type-name2.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/fully-qualified-type-name2.rs:12:12 + --> $DIR/fully-qualified-type-name2.rs:14:12 | LL | fn bar(x: x::Foo) -> y::Foo { | ------ expected `y::Foo` because of return type @@ -8,12 +8,12 @@ LL | return x; | = note: `x::Foo` and `y::Foo` have similar names, but are actually distinct types note: `x::Foo` is defined in module `crate::x` of the current crate - --> $DIR/fully-qualified-type-name2.rs:4:5 + --> $DIR/fully-qualified-type-name2.rs:6:5 | LL | pub enum Foo { } | ^^^^^^^^^^^^ note: `y::Foo` is defined in module `crate::y` of the current crate - --> $DIR/fully-qualified-type-name2.rs:8:5 + --> $DIR/fully-qualified-type-name2.rs:10:5 | LL | pub enum Foo { } | ^^^^^^^^^^^^ diff --git a/tests/ui/fully-qualified-type/fully-qualified-type-name4.rs b/tests/ui/fully-qualified-type/fully-qualified-type-name4.rs index 41f07bab80966..4fb4bb314df99 100644 --- a/tests/ui/fully-qualified-type/fully-qualified-type-name4.rs +++ b/tests/ui/fully-qualified-type/fully-qualified-type-name4.rs @@ -2,12 +2,12 @@ use std::option::Option; -fn bar(x: usize) -> Option { +fn bar(x: usize) -> Option { //~ NOTE expected `Option` because of return type return x; //~^ ERROR mismatched types - //~| expected enum `Option` - //~| found type `usize` - //~| expected `Option`, found `usize` + //~| NOTE expected enum `Option` + //~| NOTE found type `usize` + //~| NOTE expected `Option`, found `usize` } fn main() { diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs index 5f2e134109e22..6ba3c4c65d070 100644 --- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs @@ -13,7 +13,7 @@ fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefT fn build_expression( ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { - //[next]~^^ expected a `Fn(::RefType<'_>, ::RefType<'_>)` closure + //[next]~^^ ERROR expected a `Fn(::RefType<'_>, ::RefType<'_>)` closure cmp_eq //~^ ERROR type annotations needed } diff --git a/tests/ui/generic-associated-types/bugs/hrtb-implied-3.rs b/tests/ui/generic-associated-types/bugs/hrtb-implied-3.rs index bc9e6c8aea85e..8f2b5bf8f5268 100644 --- a/tests/ui/generic-associated-types/bugs/hrtb-implied-3.rs +++ b/tests/ui/generic-associated-types/bugs/hrtb-implied-3.rs @@ -17,7 +17,7 @@ where fn fails(iter: &str) { trivial_bound(iter); - //~^ borrowed data escapes + //~^ ERROR borrowed data escapes } fn main() {} diff --git a/tests/ui/generic-associated-types/collectivity-regression.rs b/tests/ui/generic-associated-types/collectivity-regression.rs index 54154f9d1fc8d..d499a9ca8580c 100644 --- a/tests/ui/generic-associated-types/collectivity-regression.rs +++ b/tests/ui/generic-associated-types/collectivity-regression.rs @@ -11,7 +11,7 @@ where for<'a> T: Get = ()>, { || { - //~^ `T` does not live long enough + //~^ ERROR `T` does not live long enough // // FIXME(#98437). This regressed at some point and // probably should work. diff --git a/tests/ui/generic-associated-types/const_params_have_right_type.stderr b/tests/ui/generic-associated-types/const_params_have_right_type.stderr index 78992112a7c53..a3d3a66a05c4f 100644 --- a/tests/ui/generic-associated-types/const_params_have_right_type.stderr +++ b/tests/ui/generic-associated-types/const_params_have_right_type.stderr @@ -1,4 +1,4 @@ -error[E0053]: type `Foo` has an incompatible generic parameter for trait `Trait` +error[E0053]: associated type `Foo` has an incompatible generic parameter for trait `Trait` --> $DIR/const_params_have_right_type.rs:6:14 | LL | trait Trait { diff --git a/tests/ui/generic-associated-types/gat-in-trait-path.rs b/tests/ui/generic-associated-types/gat-in-trait-path.rs index 24cae213e0aa4..7523803eacff8 100644 --- a/tests/ui/generic-associated-types/gat-in-trait-path.rs +++ b/tests/ui/generic-associated-types/gat-in-trait-path.rs @@ -20,11 +20,11 @@ impl Foo for Fooer { } fn f(_arg : Box Foo = &'a ()>>) {} -//~^ the trait `Foo` is not dyn compatible +//~^ ERROR the trait `Foo` is not dyn compatible fn main() { let foo = Fooer(5); f(Box::new(foo)); - //~^ the trait `Foo` is not dyn compatible - //~| the trait `Foo` is not dyn compatible + //~^ ERROR the trait `Foo` is not dyn compatible + //~| ERROR the trait `Foo` is not dyn compatible } diff --git a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs index 85661c1b84480..294fb6743fbcd 100644 --- a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs +++ b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs @@ -3,7 +3,7 @@ trait X { } fn foo<'a>(arg: Box>) {} - //~^ ERROR: lifetime in trait object type must be followed by `+` + //~^ ERROR: lifetimes must be followed by `+` to form a trait object type //~| ERROR: parenthesized generic arguments cannot be used //~| ERROR associated type takes 0 generic arguments but 1 generic argument //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments diff --git a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr index 99300ea1cb7f1..e18d8198c9455 100644 --- a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr +++ b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr @@ -1,8 +1,13 @@ -error: lifetime in trait object type must be followed by `+` +error: lifetimes must be followed by `+` to form a trait object type --> $DIR/gat-trait-path-parenthesised-args.rs:5:29 | LL | fn foo<'a>(arg: Box>) {} | ^^ + | +help: consider adding a trait bound after the potential lifetime bound + | +LL | fn foo<'a>(arg: Box>) {} + | +++++++++++++ error: parenthesized generic arguments cannot be used in associated type constraints --> $DIR/gat-trait-path-parenthesised-args.rs:5:27 diff --git a/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs b/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs index 96a0f2f40bf49..82ffa0221b997 100644 --- a/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs +++ b/tests/ui/generic-associated-types/guide-inference-in-gat-arg-deeper.rs @@ -1,5 +1,9 @@ -// Fix for . //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Fix for . trait Tr { type Gat; diff --git a/tests/ui/generic-associated-types/impl_bounds.stderr b/tests/ui/generic-associated-types/impl_bounds.stderr index 291f0ade439eb..7847bbd813cd5 100644 --- a/tests/ui/generic-associated-types/impl_bounds.stderr +++ b/tests/ui/generic-associated-types/impl_bounds.stderr @@ -41,7 +41,6 @@ LL | trait Foo { ... LL | type C where Self: Clone; | ^ this trait's associated type doesn't have the requirement `Fooy: Copy` - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` with trait `Copy` | LL | impl Foo for Fooy { @@ -58,15 +57,14 @@ note: required for `Fooy` to implement `Copy` | LL | #[derive(Copy, Clone)] | ^^^^ unsatisfied trait bound introduced in this `derive` macro -note: the requirement `Fooy: Copy` appears on the `impl`'s method `d` but not on the corresponding trait's method +note: the requirement `Fooy: Copy` appears on the `impl`'s associated function `d` but not on the corresponding trait's associated function --> $DIR/impl_bounds.rs:7:8 | LL | trait Foo { | --- in this trait ... LL | fn d() where Self: Clone; - | ^ this trait's method doesn't have the requirement `Fooy: Copy` - = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) + | ^ this trait's associated function doesn't have the requirement `Fooy: Copy` help: consider restricting type parameter `T` with trait `Copy` | LL | impl Foo for Fooy { diff --git a/tests/ui/generic-associated-types/issue-102114.current.stderr b/tests/ui/generic-associated-types/issue-102114.current.stderr index 03471d08d746f..5aace1eeaa2f6 100644 --- a/tests/ui/generic-associated-types/issue-102114.current.stderr +++ b/tests/ui/generic-associated-types/issue-102114.current.stderr @@ -1,4 +1,4 @@ -error[E0049]: type `B` has 1 type parameter but its trait declaration has 0 type parameters +error[E0049]: associated type `B` has 1 type parameter but its trait declaration has 0 type parameters --> $DIR/issue-102114.rs:15:12 | LL | type B<'b>; diff --git a/tests/ui/generic-associated-types/issue-102114.next.stderr b/tests/ui/generic-associated-types/issue-102114.next.stderr index 03471d08d746f..5aace1eeaa2f6 100644 --- a/tests/ui/generic-associated-types/issue-102114.next.stderr +++ b/tests/ui/generic-associated-types/issue-102114.next.stderr @@ -1,4 +1,4 @@ -error[E0049]: type `B` has 1 type parameter but its trait declaration has 0 type parameters +error[E0049]: associated type `B` has 1 type parameter but its trait declaration has 0 type parameters --> $DIR/issue-102114.rs:15:12 | LL | type B<'b>; diff --git a/tests/ui/generic-associated-types/issue-74816.rs b/tests/ui/generic-associated-types/issue-74816.rs index e2f4ddc7485c3..6ec0b3261739c 100644 --- a/tests/ui/generic-associated-types/issue-74816.rs +++ b/tests/ui/generic-associated-types/issue-74816.rs @@ -11,7 +11,7 @@ trait Trait1 { trait Trait2 { type Associated: Trait1 = Self; //~^ ERROR: the trait bound `Self: Trait1` is not satisfied - //~| the size for values of type `Self` cannot be known + //~| ERROR the size for values of type `Self` cannot be known } impl Trait2 for () {} diff --git a/tests/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs b/tests/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs index 1c94067029daf..f913efd75779b 100644 --- a/tests/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs +++ b/tests/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs @@ -12,7 +12,7 @@ pub trait B { impl B for () { // `'a` doesn't match implicit `'static`: suggest `'_` - type T<'a> = Box; //~ incompatible lifetime on type + type T<'a> = Box; //~ ERROR incompatible lifetime on type } trait C {} @@ -22,7 +22,7 @@ pub trait D { } impl D for () { // `'a` doesn't match explicit `'static`: we *should* suggest removing `'static` - type T<'a> = Box; //~ incompatible lifetime on type + type T<'a> = Box; //~ ERROR incompatible lifetime on type } trait E {} @@ -32,7 +32,7 @@ pub trait F { } impl F for () { // `'a` doesn't match explicit `'static`: suggest `'_` - type T<'a> = (Box, Box); //~ incompatible lifetime on type + type T<'a> = (Box, Box); //~ ERROR incompatible lifetime on type } fn main() {} diff --git a/tests/ui/generic-associated-types/issue-86787.rs b/tests/ui/generic-associated-types/issue-86787.rs index 88cdd472696bd..43c193b91d05f 100644 --- a/tests/ui/generic-associated-types/issue-86787.rs +++ b/tests/ui/generic-associated-types/issue-86787.rs @@ -8,7 +8,7 @@ enum Either { pub trait HasChildrenOf { type T; type TRef<'a>; - //~^ missing required + //~^ ERROR missing required fn ref_children<'a>(&'a self) -> Vec>; fn take_children(self) -> Vec; diff --git a/tests/ui/generic-associated-types/issue-87258_a.rs b/tests/ui/generic-associated-types/issue-87258_a.rs index 6f737b21f53cd..2ea77bbca768c 100644 --- a/tests/ui/generic-associated-types/issue-87258_a.rs +++ b/tests/ui/generic-associated-types/issue-87258_a.rs @@ -15,8 +15,8 @@ pub trait Trait2 { impl<'c, S: Trait2> Trait2 for &'c mut S { type FooFuture<'a> = impl Trait1; - //~^ ERROR unconstrained opaque type fn foo<'a>() -> Self::FooFuture<'a> { + //~^ ERROR item does not constrain `<&'c mut S as Trait2>::FooFuture::{opaque#0}` Struct(unimplemented!()) } } diff --git a/tests/ui/generic-associated-types/issue-87258_a.stderr b/tests/ui/generic-associated-types/issue-87258_a.stderr index 01f2a92f94a6b..f175c15bd02b2 100644 --- a/tests/ui/generic-associated-types/issue-87258_a.stderr +++ b/tests/ui/generic-associated-types/issue-87258_a.stderr @@ -1,10 +1,15 @@ -error: unconstrained opaque type +error: item does not constrain `<&'c mut S as Trait2>::FooFuture::{opaque#0}` + --> $DIR/issue-87258_a.rs:18:8 + | +LL | fn foo<'a>() -> Self::FooFuture<'a> { + | ^^^ + | + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/issue-87258_a.rs:17:26 | LL | type FooFuture<'a> = impl Trait1; | ^^^^^^^^^^^ - | - = note: `FooFuture` must be used in combination with a concrete type within the same impl error: aborting due to 1 previous error diff --git a/tests/ui/generic-associated-types/issue-87258_b.rs b/tests/ui/generic-associated-types/issue-87258_b.rs index 7b7610b21c7dc..8b820e75f6043 100644 --- a/tests/ui/generic-associated-types/issue-87258_b.rs +++ b/tests/ui/generic-associated-types/issue-87258_b.rs @@ -14,11 +14,12 @@ pub trait Trait2 { } type Helper<'xenon, 'yttrium, KABOOM: Trait2> = impl Trait1; -//~^ ERROR unconstrained opaque type impl<'c, S: Trait2> Trait2 for &'c mut S { type FooFuture<'a> = Helper<'c, 'a, S>; + #[define_opaque(Helper)] fn foo<'a>() -> Self::FooFuture<'a> { + //~^ ERROR item does not constrain `Helper::{opaque#0}` Struct(unimplemented!()) } } diff --git a/tests/ui/generic-associated-types/issue-87258_b.stderr b/tests/ui/generic-associated-types/issue-87258_b.stderr index 73f984dcfb868..56abcef0d3776 100644 --- a/tests/ui/generic-associated-types/issue-87258_b.stderr +++ b/tests/ui/generic-associated-types/issue-87258_b.stderr @@ -1,10 +1,15 @@ -error: unconstrained opaque type +error: item does not constrain `Helper::{opaque#0}` + --> $DIR/issue-87258_b.rs:21:8 + | +LL | fn foo<'a>() -> Self::FooFuture<'a> { + | ^^^ + | + = note: consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` +note: this opaque type is supposed to be constrained --> $DIR/issue-87258_b.rs:16:49 | LL | type Helper<'xenon, 'yttrium, KABOOM: Trait2> = impl Trait1; | ^^^^^^^^^^^ - | - = note: `Helper` must be used in combination with a concrete type within the same module error: aborting due to 1 previous error diff --git a/tests/ui/generic-associated-types/issue-88287.rs b/tests/ui/generic-associated-types/issue-88287.rs index 5d64ad8eeae0c..ce0b9827d1103 100644 --- a/tests/ui/generic-associated-types/issue-88287.rs +++ b/tests/ui/generic-associated-types/issue-88287.rs @@ -30,6 +30,7 @@ where A: SearchableResource + ?Sized + 'f, Self: 'f; + #[define_opaque(SearchFutureTy)] fn search<'c>(&'c self, _client: &'c ()) -> Self::Future<'c, Self, Criteria> { async move { todo!() } //~^ ERROR: the size for values of type `A` cannot be known at compilation time diff --git a/tests/ui/generic-associated-types/issue-88287.stderr b/tests/ui/generic-associated-types/issue-88287.stderr index 54ecc5cfcd810..71cd6677e63fb 100644 --- a/tests/ui/generic-associated-types/issue-88287.stderr +++ b/tests/ui/generic-associated-types/issue-88287.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `A` cannot be known at compilation time - --> $DIR/issue-88287.rs:34:9 + --> $DIR/issue-88287.rs:35:9 | LL | type SearchFutureTy<'f, A, B: 'f> | - this type parameter needs to be `Sized` diff --git a/tests/ui/generic-associated-types/issue-88360.fixed b/tests/ui/generic-associated-types/issue-88360.fixed index 2ebc459f19745..f24a86b240b87 100644 --- a/tests/ui/generic-associated-types/issue-88360.fixed +++ b/tests/ui/generic-associated-types/issue-88360.fixed @@ -14,7 +14,7 @@ where { fn copy(&self) -> Self::Gat<'_> where T: Copy { self.test() - //~^ mismatched types + //~^ ERROR mismatched types } } diff --git a/tests/ui/generic-associated-types/issue-88360.rs b/tests/ui/generic-associated-types/issue-88360.rs index 011061dd86136..12c18061d0435 100644 --- a/tests/ui/generic-associated-types/issue-88360.rs +++ b/tests/ui/generic-associated-types/issue-88360.rs @@ -14,7 +14,7 @@ where { fn copy(&self) -> Self::Gat<'_> where T: Copy { *self.test() - //~^ mismatched types + //~^ ERROR mismatched types } } diff --git a/tests/ui/generic-associated-types/issue-90014-tait.stderr b/tests/ui/generic-associated-types/issue-90014-tait.stderr index 09c2903ab0281..fffe5b93e5a98 100644 --- a/tests/ui/generic-associated-types/issue-90014-tait.stderr +++ b/tests/ui/generic-associated-types/issue-90014-tait.stderr @@ -1,3 +1,11 @@ +error: unconstrained opaque type + --> $DIR/issue-90014-tait.rs:15:20 + | +LL | type Fut<'a> = impl Future; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Fut` must be used in combination with a concrete type within the same impl + error[E0308]: mismatched types --> $DIR/issue-90014-tait.rs:18:9 | @@ -11,12 +19,7 @@ LL | async { () } | = note: expected opaque type `Foo<'_>::Fut<'a>` found `async` block `{async block@$DIR/issue-90014-tait.rs:18:9: 18:14}` -note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/issue-90014-tait.rs:17:8 - | -LL | fn make_fut<'a>(&'a self) -> Self::Fut<'a> { - | ^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/generic-associated-types/issue-90014-tait2.rs b/tests/ui/generic-associated-types/issue-90014-tait2.rs index 3f7a9ff63c312..472a0d500c886 100644 --- a/tests/ui/generic-associated-types/issue-90014-tait2.rs +++ b/tests/ui/generic-associated-types/issue-90014-tait2.rs @@ -20,6 +20,7 @@ impl<'x, T: 'x> Trait<'x> for (T,) { } impl Foo<'_> { + #[define_opaque(Fut)] fn make_fut(&self) -> Box Trait<'a, Thing = Fut<'a>>> { Box::new((async { () },)) //~^ ERROR expected generic lifetime parameter, found `'a` diff --git a/tests/ui/generic-associated-types/issue-90014-tait2.stderr b/tests/ui/generic-associated-types/issue-90014-tait2.stderr index aa427d42649ae..bf944aac79758 100644 --- a/tests/ui/generic-associated-types/issue-90014-tait2.stderr +++ b/tests/ui/generic-associated-types/issue-90014-tait2.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/issue-90014-tait2.rs:24:9 + --> $DIR/issue-90014-tait2.rs:25:9 | LL | type Fut<'a> = impl Future; | -- this generic parameter must be used with a generic lifetime parameter diff --git a/tests/ui/generic-associated-types/missing-where-clause-on-trait.rs b/tests/ui/generic-associated-types/missing-where-clause-on-trait.rs index c8a9246631107..5354421f4b9b7 100644 --- a/tests/ui/generic-associated-types/missing-where-clause-on-trait.rs +++ b/tests/ui/generic-associated-types/missing-where-clause-on-trait.rs @@ -5,7 +5,7 @@ trait Foo { } impl Foo for () { type Assoc<'a, 'b> = () where 'a: 'b; - //~^ impl has stricter requirements than trait + //~^ ERROR impl has stricter requirements than trait } fn main() {} diff --git a/tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs b/tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs new file mode 100644 index 0000000000000..0c25c64224b1d --- /dev/null +++ b/tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs @@ -0,0 +1,33 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative#202. We have +// to make sure we don't constrain ambiguous GAT args when normalizing +// via where bounds or item bounds. + +trait Trait { + type Assoc; +} + +fn ret(x: U) -> ::Assoc { + loop {} +} + +fn where_bound = u32>>() { + let inf = Default::default(); + let x = ret::(inf); + let _: i32 = inf; +} + +trait ItemBound { + type Bound: Trait = u32>; +} +fn item_bound() { + let inf = Default::default(); + let x = ret::(inf); + let _: i32 = inf; +} + +fn main() {} diff --git a/tests/ui/generic-associated-types/parameter_number_and_kind_impl.rs b/tests/ui/generic-associated-types/parameter_number_and_kind_impl.rs index c1381025ac2a8..a39a7aacc7b37 100644 --- a/tests/ui/generic-associated-types/parameter_number_and_kind_impl.rs +++ b/tests/ui/generic-associated-types/parameter_number_and_kind_impl.rs @@ -12,11 +12,11 @@ struct Fooy; impl Foo for Fooy { type A = u32; - //~^ ERROR lifetime parameters or bounds on type `A` do not match the trait declaration + //~^ ERROR lifetime parameters or bounds on associated type `A` do not match the trait declaration type B<'a, T> = Vec; //~^ ERROR type `B` has 1 type parameter but its trait declaration has 0 type parameters type C<'a> = u32; - //~^ ERROR lifetime parameters or bounds on type `C` do not match the trait declaration + //~^ ERROR lifetime parameters or bounds on associated type `C` do not match the trait declaration } struct Fooer; @@ -25,7 +25,7 @@ impl Foo for Fooer { type A = u32; //~^ ERROR type `A` has 1 type parameter but its trait declaration has 0 type parameters type B<'a> = u32; - //~^ ERROR lifetime parameters or bounds on type `B` do not match the trait declaration + //~^ ERROR lifetime parameters or bounds on associated type `B` do not match the trait declaration type C = T; //~^ ERROR type `C` has 1 type parameter but its trait declaration has 0 type parameters } diff --git a/tests/ui/generic-associated-types/parameter_number_and_kind_impl.stderr b/tests/ui/generic-associated-types/parameter_number_and_kind_impl.stderr index fdd6d305ab27e..f7c4a07589c24 100644 --- a/tests/ui/generic-associated-types/parameter_number_and_kind_impl.stderr +++ b/tests/ui/generic-associated-types/parameter_number_and_kind_impl.stderr @@ -1,13 +1,13 @@ -error[E0195]: lifetime parameters or bounds on type `A` do not match the trait declaration +error[E0195]: lifetime parameters or bounds on associated type `A` do not match the trait declaration --> $DIR/parameter_number_and_kind_impl.rs:14:11 | LL | type A<'a>; - | ---- lifetimes in impl do not match this type in trait + | ---- lifetimes in impl do not match this associated type in trait ... LL | type A = u32; - | ^ lifetimes do not match type in trait + | ^ lifetimes do not match associated type in trait -error[E0049]: type `B` has 1 type parameter but its trait declaration has 0 type parameters +error[E0049]: associated type `B` has 1 type parameter but its trait declaration has 0 type parameters --> $DIR/parameter_number_and_kind_impl.rs:16:12 | LL | type B<'a, 'b>; @@ -20,16 +20,16 @@ LL | type B<'a, T> = Vec; | | | found 1 type parameter -error[E0195]: lifetime parameters or bounds on type `C` do not match the trait declaration +error[E0195]: lifetime parameters or bounds on associated type `C` do not match the trait declaration --> $DIR/parameter_number_and_kind_impl.rs:18:11 | LL | type C; - | - lifetimes in impl do not match this type in trait + | - lifetimes in impl do not match this associated type in trait ... LL | type C<'a> = u32; - | ^^^^ lifetimes do not match type in trait + | ^^^^ lifetimes do not match associated type in trait -error[E0049]: type `A` has 1 type parameter but its trait declaration has 0 type parameters +error[E0049]: associated type `A` has 1 type parameter but its trait declaration has 0 type parameters --> $DIR/parameter_number_and_kind_impl.rs:25:12 | LL | type A<'a>; @@ -38,16 +38,16 @@ LL | type A<'a>; LL | type A = u32; | ^ found 1 type parameter -error[E0195]: lifetime parameters or bounds on type `B` do not match the trait declaration +error[E0195]: lifetime parameters or bounds on associated type `B` do not match the trait declaration --> $DIR/parameter_number_and_kind_impl.rs:27:11 | LL | type B<'a, 'b>; - | -------- lifetimes in impl do not match this type in trait + | -------- lifetimes in impl do not match this associated type in trait ... LL | type B<'a> = u32; - | ^^^^ lifetimes do not match type in trait + | ^^^^ lifetimes do not match associated type in trait -error[E0049]: type `C` has 1 type parameter but its trait declaration has 0 type parameters +error[E0049]: associated type `C` has 1 type parameter but its trait declaration has 0 type parameters --> $DIR/parameter_number_and_kind_impl.rs:29:12 | LL | type C; diff --git a/tests/ui/generic-associated-types/self-outlives-lint.rs b/tests/ui/generic-associated-types/self-outlives-lint.rs index 699b3a8c5092d..2e58a72fb5c23 100644 --- a/tests/ui/generic-associated-types/self-outlives-lint.rs +++ b/tests/ui/generic-associated-types/self-outlives-lint.rs @@ -5,7 +5,7 @@ use std::fmt::Debug; // We have a `&'a self`, so we need a `Self: 'a` trait Iterable { type Item<'x>; - //~^ missing required + //~^ ERROR missing required fn iter<'a>(&'a self) -> Self::Item<'a>; } @@ -21,7 +21,7 @@ impl Iterable for T { // We have a `&'a T`, so we need a `T: 'x` trait Deserializer { type Out<'x>; - //~^ missing required + //~^ ERROR missing required fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a>; } @@ -35,14 +35,14 @@ impl Deserializer for () { // We have a `&'b T` and a `'b: 'a`, so it is implied that `T: 'a`. Therefore, we need a `T: 'x` trait Deserializer2 { type Out<'x>; - //~^ missing required + //~^ ERROR missing required fn deserialize2<'a, 'b: 'a>(&self, input1: &'b T) -> Self::Out<'a>; } // We have a `&'a T` and a `&'b U`, so we need a `T: 'x` and a `U: 'y` trait Deserializer3 { type Out<'x, 'y>; - //~^ missing required + //~^ ERROR missing required fn deserialize2<'a, 'b>(&self, input: &'a T, input2: &'b U) -> Self::Out<'a, 'b>; } @@ -57,7 +57,7 @@ struct Wrap(T); // We pass `Wrap` and we see `&'z Wrap`, so we require `D: 'x` trait Des { type Out<'x, D>; - //~^ missing required + //~^ ERROR missing required fn des<'z, T>(&self, data: &'z Wrap) -> Self::Out<'z, Wrap>; } /* @@ -73,7 +73,7 @@ impl Des for () { // implied bound that `T: 'z`, so we require `D: 'x` trait Des2 { type Out<'x, D>; - //~^ missing required + //~^ ERROR missing required fn des<'z, T>(&self, data: &'z Wrap) -> Self::Out<'z, T>; } /* @@ -88,7 +88,7 @@ impl Des2 for () { // We see `&'z T`, so we require `D: 'x` trait Des3 { type Out<'x, D>; - //~^ missing required + //~^ ERROR missing required fn des<'z, T>(&self, data: &'z T) -> Self::Out<'z, T>; } /* @@ -110,7 +110,7 @@ trait NoGat<'a> { // FIXME: we require two bounds (`where Self: 'a, Self: 'b`) when we should only require one trait TraitLifetime<'a> { type Bar<'b>; - //~^ missing required + //~^ ERROR missing required fn method(&'a self) -> Self::Bar<'a>; } @@ -118,14 +118,14 @@ trait TraitLifetime<'a> { // FIXME: we require two bounds (`where Self: 'a, Self: 'b`) when we should only require one trait TraitLifetimeWhere<'a> where Self: 'a { type Bar<'b>; - //~^ missing required + //~^ ERROR missing required fn method(&'a self) -> Self::Bar<'a>; } // Explicit bound instead of implicit; we want to still error trait ExplicitBound { type Bar<'b>; - //~^ missing required + //~^ ERROR missing required fn method<'b>(&self, token: &'b ()) -> Self::Bar<'b> where Self: 'b; } @@ -138,15 +138,15 @@ trait NotInReturn { // We obviously error for `Iterator`, but we should also error for `Item` trait IterableTwo { type Item<'a>; - //~^ missing required + //~^ ERROR missing required type Iterator<'a>: Iterator>; - //~^ missing required + //~^ ERROR missing required fn iter<'a>(&'a self) -> Self::Iterator<'a>; } trait IterableTwoWhere { type Item<'a>; - //~^ missing required + //~^ ERROR missing required type Iterator<'a>: Iterator> where Self: 'a; fn iter<'a>(&'a self) -> Self::Iterator<'a>; } @@ -155,7 +155,7 @@ trait IterableTwoWhere { // because of `&'x &'y`, so we require that `'b: 'a`. trait RegionOutlives { type Bar<'a, 'b>; - //~^ missing required + //~^ ERROR missing required fn foo<'x, 'y>(&self, input: &'x &'y ()) -> Self::Bar<'x, 'y>; } @@ -171,7 +171,7 @@ impl Foo for () { // Similar to the above, except with explicit bounds trait ExplicitRegionOutlives<'ctx> { type Fut<'out>; - //~^ missing required + //~^ ERROR missing required fn test<'out>(ctx: &'ctx i32) -> Self::Fut<'out> where @@ -211,7 +211,7 @@ trait StaticReturnAndTakes<'a> { // We require bounds when the GAT appears in the inputs trait Input { type Item<'a>; - //~^ missing required + //~^ ERROR missing required fn takes_item<'a>(&'a self, item: Self::Item<'a>); } diff --git a/tests/ui/generic-associated-types/trait-objects.rs b/tests/ui/generic-associated-types/trait-objects.rs index ed324b562e1c8..256cfee4c8098 100644 --- a/tests/ui/generic-associated-types/trait-objects.rs +++ b/tests/ui/generic-associated-types/trait-objects.rs @@ -6,10 +6,10 @@ trait StreamingIterator { } fn min_size(x: &mut dyn for<'a> StreamingIterator = &'a i32>) -> usize { - //~^ the trait `StreamingIterator` is not dyn compatible + //~^ ERROR the trait `StreamingIterator` is not dyn compatible x.size_hint().0 - //~^ the trait `StreamingIterator` is not dyn compatible - //~| the trait `StreamingIterator` is not dyn compatible + //~^ ERROR the trait `StreamingIterator` is not dyn compatible + //~| ERROR the trait `StreamingIterator` is not dyn compatible } fn main() {} diff --git a/tests/ui/generic-associated-types/type-param-defaults.rs b/tests/ui/generic-associated-types/type-param-defaults.rs index a9c8c5c12d9e8..eea54c460734c 100644 --- a/tests/ui/generic-associated-types/type-param-defaults.rs +++ b/tests/ui/generic-associated-types/type-param-defaults.rs @@ -4,17 +4,17 @@ trait Trait { type Assoc; - //~^ defaults for type parameters are only allowed + //~^ ERROR defaults for type parameters are only allowed } impl Trait for () { type Assoc = u64; - //~^ defaults for type parameters are only allowed + //~^ ERROR defaults for type parameters are only allowed } impl Trait for u32 { type Assoc = T; - //~^ defaults for type parameters are only allowed + //~^ ERROR defaults for type parameters are only allowed } trait Other {} diff --git a/tests/ui/generic-const-items/assoc-const-missing-type.rs b/tests/ui/generic-const-items/assoc-const-missing-type.rs index 0c94a4262ef47..dde47cf993ea0 100644 --- a/tests/ui/generic-const-items/assoc-const-missing-type.rs +++ b/tests/ui/generic-const-items/assoc-const-missing-type.rs @@ -14,7 +14,7 @@ impl Trait for () { //~| ERROR mismatched types const Q = ""; //~^ ERROR missing type for `const` item - //~| ERROR lifetime parameters or bounds on const `Q` do not match the trait declaration + //~| ERROR lifetime parameters or bounds on associated const `Q` do not match the trait declaration } fn main() {} diff --git a/tests/ui/generic-const-items/assoc-const-missing-type.stderr b/tests/ui/generic-const-items/assoc-const-missing-type.stderr index 5af119dffa7c8..9f6db575ec239 100644 --- a/tests/ui/generic-const-items/assoc-const-missing-type.stderr +++ b/tests/ui/generic-const-items/assoc-const-missing-type.stderr @@ -15,14 +15,14 @@ error: missing type for `const` item LL | const K = (); | ^ help: provide a type for the associated constant: `()` -error[E0195]: lifetime parameters or bounds on const `Q` do not match the trait declaration +error[E0195]: lifetime parameters or bounds on associated const `Q` do not match the trait declaration --> $DIR/assoc-const-missing-type.rs:15:12 | LL | const Q<'a>: &'a str; - | ---- lifetimes in impl do not match this const in trait + | ---- lifetimes in impl do not match this associated const in trait ... LL | const Q = ""; - | ^ lifetimes do not match const in trait + | ^ lifetimes do not match associated const in trait error: missing type for `const` item --> $DIR/assoc-const-missing-type.rs:15:12 diff --git a/tests/ui/generic-const-items/compare-impl-item.rs b/tests/ui/generic-const-items/compare-impl-item.rs index 21c958a0abec2..b301cd0dae0c9 100644 --- a/tests/ui/generic-const-items/compare-impl-item.rs +++ b/tests/ui/generic-const-items/compare-impl-item.rs @@ -22,7 +22,7 @@ impl

    (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, mutbl) if is_self_ty(ty) => ByRawPointer(mutbl), - _ if self_arg_ty.boxed_ty().is_some_and(is_self_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. @@ -1679,6 +1596,42 @@ pub fn fold_list<'tcx, F, L, T>( list: L, folder: &mut F, intern: impl FnOnce(TyCtxt<'tcx>, &[T]) -> L, +) -> L +where + F: TypeFolder>, + L: AsRef<[T]>, + T: TypeFoldable> + PartialEq + Copy, +{ + let slice = list.as_ref(); + let mut iter = slice.iter().copied(); + // Look for the first element that changed + match iter.by_ref().enumerate().find_map(|(i, t)| { + let new_t = t.fold_with(folder); + if new_t != t { Some((i, new_t)) } else { None } + }) { + Some((i, new_t)) => { + // An element changed, prepare to intern the resulting list + let mut new_list = SmallVec::<[_; 8]>::with_capacity(slice.len()); + new_list.extend_from_slice(&slice[..i]); + new_list.push(new_t); + for t in iter { + new_list.push(t.fold_with(folder)) + } + intern(folder.cx(), &new_list) + } + None => list, + } +} + +/// Does the equivalent of +/// ```ignore (illustrative) +/// let v = self.iter().map(|p| p.try_fold_with(folder)).collect::>(); +/// folder.tcx().intern_*(&v) +/// ``` +pub fn try_fold_list<'tcx, F, L, T>( + list: L, + folder: &mut F, + intern: impl FnOnce(TyCtxt<'tcx>, &[T]) -> L, ) -> Result where F: FallibleTypeFolder>, @@ -1751,10 +1704,7 @@ pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { /// the compiler to make some assumptions about its shape; if the user doesn't use a feature gate, they may /// cause an ICE that we otherwise may want to prevent. pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - if tcx.features().intrinsics() - && (matches!(tcx.fn_sig(def_id).skip_binder().abi(), ExternAbi::RustIntrinsic) - || tcx.has_attr(def_id, sym::rustc_intrinsic)) - { + if tcx.features().intrinsics() && tcx.has_attr(def_id, sym::rustc_intrinsic) { let must_be_overridden = match tcx.hir_node_by_def_id(def_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { has_body, .. }, .. }) => { !has_body diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 95256b55bb4a1..f804217459915 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -1,10 +1,11 @@ use std::ops::ControlFlow; use rustc_data_structures::fx::FxIndexSet; -use rustc_type_ir::fold::TypeFoldable; -pub use rustc_type_ir::visit::*; +use rustc_type_ir::TypeFoldable; -use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags}; +use crate::ty::{ + self, Binder, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, +}; /////////////////////////////////////////////////////////////////////////// // Region folder @@ -65,7 +66,7 @@ impl<'tcx> TyCtxt<'tcx> { { type Result = ControlFlow<()>; - fn visit_binder>>( + fn visit_binder>>( &mut self, t: &Binder<'tcx, T>, ) -> Self::Result { @@ -76,7 +77,7 @@ impl<'tcx> TyCtxt<'tcx> { } fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result { - match *r { + match r.kind() { ty::ReBound(debruijn, _) if debruijn < self.outer_index => { ControlFlow::Continue(()) } @@ -138,7 +139,7 @@ impl<'tcx> TyCtxt<'tcx> { { let mut collector = LateBoundRegionsCollector::new(just_constrained); let value = value.skip_binder(); - let value = if just_constrained { self.expand_weak_alias_tys(value) } else { value }; + let value = if just_constrained { self.expand_free_alias_tys(value) } else { value }; value.visit_with(&mut collector); collector.regions } @@ -167,7 +168,7 @@ impl LateBoundRegionsCollector { } impl<'tcx> TypeVisitor> for LateBoundRegionsCollector { - fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) { + fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) { self.current_index.shift_in(1); t.super_visit_with(self); self.current_index.shift_out(1); @@ -181,8 +182,8 @@ impl<'tcx> TypeVisitor> for LateBoundRegionsCollector { ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) => { return; } - // All weak alias types should've been expanded beforehand. - ty::Alias(ty::Weak, _) => bug!("unexpected weak alias type"), + // All free alias types should've been expanded beforehand. + ty::Alias(ty::Free, _) => bug!("unexpected free alias type"), _ => {} } } @@ -204,7 +205,7 @@ impl<'tcx> TypeVisitor> for LateBoundRegionsCollector { } fn visit_region(&mut self, r: ty::Region<'tcx>) { - if let ty::ReBound(debruijn, br) = *r { + if let ty::ReBound(debruijn, br) = r.kind() { if debruijn == self.current_index { self.regions.insert(br.kind); } @@ -230,9 +231,7 @@ impl MaxUniverse { impl<'tcx> TypeVisitor> for MaxUniverse { fn visit_ty(&mut self, t: Ty<'tcx>) { if let ty::Placeholder(placeholder) = t.kind() { - self.max_universe = ty::UniverseIndex::from_u32( - self.max_universe.as_u32().max(placeholder.universe.as_u32()), - ); + self.max_universe = self.max_universe.max(placeholder.universe); } t.super_visit_with(self) @@ -240,19 +239,15 @@ impl<'tcx> TypeVisitor> for MaxUniverse { fn visit_const(&mut self, c: ty::consts::Const<'tcx>) { if let ty::ConstKind::Placeholder(placeholder) = c.kind() { - self.max_universe = ty::UniverseIndex::from_u32( - self.max_universe.as_u32().max(placeholder.universe.as_u32()), - ); + self.max_universe = self.max_universe.max(placeholder.universe); } c.super_visit_with(self) } fn visit_region(&mut self, r: ty::Region<'tcx>) { - if let ty::RePlaceholder(placeholder) = *r { - self.max_universe = ty::UniverseIndex::from_u32( - self.max_universe.as_u32().max(placeholder.universe.as_u32()), - ); + if let ty::RePlaceholder(placeholder) = r.kind() { + self.max_universe = self.max_universe.max(placeholder.universe); } } } diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs deleted file mode 100644 index 3e8a3d1a28923..0000000000000 --- a/compiler/rustc_middle/src/ty/walk.rs +++ /dev/null @@ -1,217 +0,0 @@ -//! An iterator over the type substructure. -//! WARNING: this does not keep track of the region depth. - -use rustc_data_structures::sso::SsoHashSet; -use smallvec::{SmallVec, smallvec}; -use tracing::debug; - -use crate::ty::{self, GenericArg, GenericArgKind, Ty}; - -// The TypeWalker's stack is hot enough that it's worth going to some effort to -// avoid heap allocations. -type TypeWalkerStack<'tcx> = SmallVec<[GenericArg<'tcx>; 8]>; - -pub struct TypeWalker<'tcx> { - stack: TypeWalkerStack<'tcx>, - last_subtree: usize, - pub visited: SsoHashSet>, -} - -/// An iterator for walking the type tree. -/// -/// It's very easy to produce a deeply -/// nested type tree with a lot of -/// identical subtrees. In order to work efficiently -/// in this situation walker only visits each type once. -/// It maintains a set of visited types and -/// skips any types that are already there. -impl<'tcx> TypeWalker<'tcx> { - pub fn new(root: GenericArg<'tcx>) -> Self { - Self { stack: smallvec![root], last_subtree: 1, visited: SsoHashSet::new() } - } - - /// Skips the subtree corresponding to the last type - /// returned by `next()`. - /// - /// Example: Imagine you are walking `Foo, usize>`. - /// - /// ```ignore (illustrative) - /// let mut iter: TypeWalker = ...; - /// iter.next(); // yields Foo - /// iter.next(); // yields Bar - /// iter.skip_current_subtree(); // skips i32 - /// iter.next(); // yields usize - /// ``` - pub fn skip_current_subtree(&mut self) { - self.stack.truncate(self.last_subtree); - } -} - -impl<'tcx> Iterator for TypeWalker<'tcx> { - type Item = GenericArg<'tcx>; - - fn next(&mut self) -> Option> { - debug!("next(): stack={:?}", self.stack); - loop { - let next = self.stack.pop()?; - self.last_subtree = self.stack.len(); - if self.visited.insert(next) { - push_inner(&mut self.stack, next); - debug!("next: stack={:?}", self.stack); - return Some(next); - } - } - } -} - -impl<'tcx> GenericArg<'tcx> { - /// Iterator that walks `self` and any types reachable from - /// `self`, in depth-first order. Note that just walks the types - /// that appear in `self`, it does not descend into the fields of - /// structs or variants. For example: - /// - /// ```text - /// isize => { isize } - /// Foo> => { Foo>, Bar, isize } - /// [isize] => { [isize], isize } - /// ``` - pub fn walk(self) -> TypeWalker<'tcx> { - TypeWalker::new(self) - } -} - -impl<'tcx> Ty<'tcx> { - /// Iterator that walks `self` and any types reachable from - /// `self`, in depth-first order. Note that just walks the types - /// that appear in `self`, it does not descend into the fields of - /// structs or variants. For example: - /// - /// ```text - /// isize => { isize } - /// Foo> => { Foo>, Bar, isize } - /// [isize] => { [isize], isize } - /// ``` - pub fn walk(self) -> TypeWalker<'tcx> { - TypeWalker::new(self.into()) - } -} - -impl<'tcx> ty::Const<'tcx> { - /// Iterator that walks `self` and any types reachable from - /// `self`, in depth-first order. Note that just walks the types - /// that appear in `self`, it does not descend into the fields of - /// structs or variants. For example: - /// - /// ```text - /// isize => { isize } - /// Foo> => { Foo>, Bar, isize } - /// [isize] => { [isize], isize } - /// ``` - pub fn walk(self) -> TypeWalker<'tcx> { - TypeWalker::new(self.into()) - } -} - -/// We push `GenericArg`s on the stack in reverse order so as to -/// maintain a pre-order traversal. As of the time of this -/// writing, the fact that the traversal is pre-order is not -/// known to be significant to any code, but it seems like the -/// natural order one would expect (basically, the order of the -/// 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() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Infer(_) - | ty::Param(_) - | ty::Never - | ty::Error(_) - | ty::Placeholder(..) - | ty::Bound(..) - | ty::Foreign(..) => {} - - ty::Pat(ty, pat) => { - match *pat { - ty::PatternKind::Range { start, end, include_end: _ } => { - stack.extend(end.map(Into::into)); - stack.extend(start.map(Into::into)); - } - } - stack.push(ty.into()); - } - ty::Array(ty, len) => { - stack.push(len.into()); - stack.push(ty.into()); - } - ty::Slice(ty) => { - stack.push(ty.into()); - } - ty::RawPtr(ty, _) => { - stack.push(ty.into()); - } - ty::Ref(lt, ty, _) => { - stack.push(ty.into()); - stack.push(lt.into()); - } - ty::Alias(_, data) => { - stack.extend(data.args.iter().rev()); - } - ty::Dynamic(obj, lt, _) => { - stack.push(lt.into()); - stack.extend(obj.iter().rev().flat_map(|predicate| { - let (args, opt_ty) = match predicate.skip_binder() { - ty::ExistentialPredicate::Trait(tr) => (tr.args, None), - ty::ExistentialPredicate::Projection(p) => (p.args, Some(p.term)), - ty::ExistentialPredicate::AutoTrait(_) => - // Empty iterator - { - (ty::GenericArgs::empty(), None) - } - }; - - args.iter().rev().chain(opt_ty.map(|term| match term.unpack() { - ty::TermKind::Ty(ty) => ty.into(), - ty::TermKind::Const(ct) => ct.into(), - })) - })); - } - ty::Adt(_, args) - | ty::Closure(_, args) - | ty::CoroutineClosure(_, args) - | ty::Coroutine(_, args) - | ty::CoroutineWitness(_, args) - | ty::FnDef(_, args) => { - stack.extend(args.iter().rev()); - } - ty::Tuple(ts) => stack.extend(ts.iter().rev().map(GenericArg::from)), - ty::FnPtr(sig_tys, _hdr) => { - stack.extend( - sig_tys.skip_binder().inputs_and_output.iter().rev().map(|ty| ty.into()), - ); - } - ty::UnsafeBinder(bound_ty) => { - stack.push(bound_ty.skip_binder().into()); - } - }, - GenericArgKind::Lifetime(_) => {} - GenericArgKind::Const(parent_ct) => match parent_ct.kind() { - ty::ConstKind::Infer(_) - | ty::ConstKind::Param(_) - | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Bound(..) - | ty::ConstKind::Error(_) => {} - - ty::ConstKind::Value(cv) => stack.push(cv.ty.into()), - - ty::ConstKind::Expr(expr) => stack.extend(expr.args().iter().rev()), - ty::ConstKind::Unevaluated(ct) => { - stack.extend(ct.args.iter().rev()); - } - }, - } -} diff --git a/compiler/rustc_middle/src/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs index 7dda68b8393a1..c4357fae10463 100644 --- a/compiler/rustc_middle/src/util/bug.rs +++ b/compiler/rustc_middle/src/util/bug.rs @@ -49,7 +49,7 @@ fn opt_span_bug_fmt>( pub fn trigger_delayed_bug(tcx: TyCtxt<'_>, key: rustc_hir::def_id::DefId) { tcx.dcx().span_delayed_bug( tcx.def_span(key), - "delayed bug triggered by #[rustc_error(delayed_bug_from_inside_query)]", + "delayed bug triggered by #[rustc_delayed_bug_from_inside_query]", ); } diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 9450ce7ec444e..4d70a70873267 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -88,7 +88,7 @@ impl<'tcx> Value> for Representability { if info.query.dep_kind == dep_kinds::representability && let Some(field_id) = info.query.def_id && let Some(field_id) = field_id.as_local() - && let Some(DefKind::Field) = info.query.def_kind + && let Some(DefKind::Field) = info.query.info.def_kind { let parent_id = tcx.parent(field_id.to_def_id()); let item_id = match tcx.def_kind(parent_id) { @@ -138,18 +138,26 @@ impl<'tcx> Value> for &[ty::Variance] { cycle_error: &CycleError, _guar: ErrorGuaranteed, ) -> Self { - if let Some(frame) = cycle_error.cycle.get(0) - && frame.query.dep_kind == dep_kinds::variances_of - && let Some(def_id) = frame.query.def_id - { - let n = tcx.generics_of(def_id).own_params.len(); - vec![ty::Bivariant; n].leak() - } else { - span_bug!( - cycle_error.usage.as_ref().unwrap().0, - "only `variances_of` returns `&[ty::Variance]`" - ); - } + search_for_cycle_permutation( + &cycle_error.cycle, + |cycle| { + if let Some(frame) = cycle.get(0) + && frame.query.dep_kind == dep_kinds::variances_of + && let Some(def_id) = frame.query.def_id + { + let n = tcx.generics_of(def_id).own_params.len(); + ControlFlow::Break(vec![ty::Bivariant; n].leak()) + } else { + ControlFlow::Continue(()) + } + }, + || { + span_bug!( + cycle_error.usage.as_ref().unwrap().0, + "only `variances_of` returns `&[ty::Variance]`" + ) + }, + ) } } @@ -216,7 +224,7 @@ impl<'tcx, T> Value> for Result> continue; }; let frame_span = - frame.query.default_span(cycle[(i + 1) % cycle.len()].span); + frame.query.info.default_span(cycle[(i + 1) % cycle.len()].span); if frame_span.is_dummy() { continue; } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index d70d70a31a4a7..1534865cdd346 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -5,14 +5,12 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -either = "1.5.0" itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_mir_build/src/builder/block.rs b/compiler/rustc_mir_build/src/builder/block.rs index 7c76e02fcef6a..a71196f79d78d 100644 --- a/compiler/rustc_mir_build/src/builder/block.rs +++ b/compiler/rustc_mir_build/src/builder/block.rs @@ -199,19 +199,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None, Some((Some(&destination), initializer_span)), ); - this.visit_primary_bindings( - pattern, - UserTypeProjections::none(), - &mut |this, _, _, node, span, _, _| { - this.storage_live_binding( - block, - node, - span, - OutsideGuard, - ScheduleDrops::Yes, - ); - }, - ); + this.visit_primary_bindings(pattern, &mut |this, node, span| { + this.storage_live_binding( + block, + node, + span, + OutsideGuard, + ScheduleDrops::Yes, + ); + }); let else_block_span = this.thir[*else_block].span; let (matching, failure) = this.in_if_then_scope(last_remainder_scope, else_block_span, |this| { @@ -295,20 +291,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }); 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, - ScheduleDrops::Yes, - ); - this.schedule_drop_for_binding(node, span, OutsideGuard); - }, - ) + this.visit_primary_bindings(pattern, &mut |this, node, span| { + this.storage_live_binding( + block, + node, + span, + OutsideGuard, + ScheduleDrops::Yes, + ); + this.schedule_drop_for_binding(node, span, OutsideGuard); + }) } // Enter the visibility scope, after evaluating the initializer. diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index bfc16816e2e5e..902a6e7f115be 100644 --- a/compiler/rustc_mir_build/src/builder/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -103,8 +103,9 @@ fn parse_attribute(attr: &Attribute) -> MirPhase { let mut dialect: Option = None; let mut phase: Option = None; + // Not handling errors properly for this internal attribute; will just abort on errors. for nested in meta_items { - let name = nested.name_or_empty(); + let name = nested.name().unwrap(); let value = nested.value_str().unwrap().as_str().to_string(); match name.as_str() { "dialect" => { diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 19669021eefb4..494ee33fd8b9f 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -69,6 +69,8 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { target: self.parse_return_to(args[1])?, unwind: self.parse_unwind_action(args[2])?, replace: false, + drop: None, + async_fut: None, }) }, @call(mir_call, args) => { @@ -145,7 +147,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let arm = &self.thir[*arm]; let value = match arm.pattern.kind { PatKind::Constant { value } => value, - PatKind::ExpandedConstant { ref subpattern, def_id: _, is_inline: false } + PatKind::ExpandedConstant { ref subpattern, def_id: _ } if let PatKind::Constant { value } = subpattern.kind => { value diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index 84c9297e65898..64d092e035451 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -105,19 +105,20 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx> return Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar)); } - let trunc = |n, width: ty::UintTy| { - let width = width - .normalize(tcx.data_layout.pointer_size.bits().try_into().unwrap()) - .bit_width() - .unwrap(); - let width = Size::from_bits(width); + let lit_ty = match *ty.kind() { + ty::Pat(base, _) => base, + _ => ty, + }; + + let trunc = |n| { + let width = lit_ty.primitive_size(tcx); trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); let result = width.truncate(n); trace!("trunc result: {}", result); ConstValue::Scalar(Scalar::from_uint(result, width)) }; - let value = match (lit, ty.kind()) { + let value = match (lit, 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_bytes_byte_aligned_immutable(s.as_bytes()); @@ -144,11 +145,10 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx> (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1))) } - (ast::LitKind::Int(n, _), ty::Uint(ui)) if !neg => trunc(n.get(), *ui), - (ast::LitKind::Int(n, _), ty::Int(i)) => trunc( - if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() }, - i.to_unsigned(), - ), + (ast::LitKind::Int(n, _), ty::Uint(_)) if !neg => trunc(n.get()), + (ast::LitKind::Int(n, _), ty::Int(_)) => { + trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() }) + } (ast::LitKind::Float(n, _), ty::Float(fty)) => { parse_float_into_constval(*n, *fty, neg).unwrap() } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 581f45db6c483..fbe530811567f 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -159,7 +159,7 @@ fn find_capture_matching_projections<'a, 'tcx>( ) -> Option<(usize, &'a Capture<'tcx>)> { let hir_projections = convert_to_hir_projections_and_truncate_for_capture(projections); - upvars.get_by_key_enumerated(var_hir_id.0).find(|(_, capture)| { + upvars.get_by_key_enumerated(var_hir_id.0.local_id).find(|(_, capture)| { let possible_ancestor_proj_kinds: Vec<_> = capture.captured_place.place.projections.iter().map(|proj| proj.kind).collect(); is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections) @@ -258,7 +258,7 @@ impl<'tcx> PlaceBuilder<'tcx> { self.projection ), PlaceBase::Upvar { var_hir_id, closure_def_id: _ } => span_bug!( - cx.tcx.hir().span(var_hir_id.0), + cx.tcx.hir_span(var_hir_id.0), "could not resolve upvar: {var_hir_id:?} + {:?}", self.projection ), @@ -582,6 +582,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Yield { .. } | ExprKind::ThreadLocalRef(_) | ExprKind::Call { .. } + | ExprKind::ByUse { .. } | ExprKind::WrapUnsafeBinder { .. } => { // these are not places, so we need to make a temporary. debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place))); diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index 2c9a1de7f9978..5a97b08db28d6 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -416,7 +416,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Some(mir_place) = place_builder.try_to_place(this) { this.cfg.push_fake_read( block, - this.source_info(this.tcx.hir().span(*hir_id)), + this.source_info(this.tcx.hir_span(*hir_id)), *cause, mir_place, ); @@ -572,6 +572,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); block.and(Rvalue::Use(operand)) } + + ExprKind::ByUse { expr, span: _ } => { + let operand = unpack!( + block = + this.as_operand(block, scope, expr, LocalInfo::Boring, NeedsTemporary::No) + ); + block.and(Rvalue::Use(operand)) + } } } @@ -754,6 +762,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { target: success, unwind: UnwindAction::Continue, replace: false, + drop: None, + async_fut: None, }, ); this.diverge_from(block); diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index ca55d36bfc659..34524aed40678 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -56,6 +56,7 @@ impl Category { | ExprKind::RawBorrow { .. } | ExprKind::Yield { .. } | ExprKind::Call { .. } + | ExprKind::ByUse { .. } | ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::Into)), ExprKind::Array { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 72443e2f60ddc..a9a07997410c0 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -4,11 +4,14 @@ use rustc_ast::{AsmMacro, InlineAsmOptions}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; +use rustc_hir::lang_items::LangItem; use rustc_middle::mir::*; use rustc_middle::span_bug; use rustc_middle::thir::*; -use rustc_middle::ty::CanonicalUserTypeAnnotation; +use rustc_middle::ty::{CanonicalUserTypeAnnotation, Ty}; +use rustc_span::DUMMY_SP; use rustc_span::source_map::Spanned; +use rustc_trait_selection::infer::InferCtxtExt; use tracing::{debug, instrument}; use crate::builder::expr::category::{Category, RvalueFunc}; @@ -289,6 +292,57 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.diverge_from(block); success.unit() } + ExprKind::ByUse { expr, span } => { + let place = unpack!(block = this.as_place(block, expr)); + let ty = place.ty(&this.local_decls, this.tcx).ty; + + if this.tcx.type_is_copy_modulo_regions(this.infcx.typing_env(this.param_env), ty) { + this.cfg.push_assign( + block, + source_info, + destination, + Rvalue::Use(Operand::Copy(place)), + ); + block.unit() + } else if this.infcx.type_is_use_cloned_modulo_regions(this.param_env, ty) { + // Convert `expr.use` to a call like `Clone::clone(&expr)` + let success = this.cfg.start_new_block(); + let clone_trait = this.tcx.require_lang_item(LangItem::Clone, None); + let clone_fn = this.tcx.associated_item_def_ids(clone_trait)[0]; + let func = Operand::function_handle(this.tcx, clone_fn, [ty.into()], expr_span); + let ref_ty = Ty::new_imm_ref(this.tcx, this.tcx.lifetimes.re_erased, ty); + let ref_place = this.temp(ref_ty, span); + this.cfg.push_assign( + block, + source_info, + ref_place, + Rvalue::Ref(this.tcx.lifetimes.re_erased, BorrowKind::Shared, place), + ); + this.cfg.terminate( + block, + source_info, + TerminatorKind::Call { + func, + args: [Spanned { node: Operand::Move(ref_place), span: DUMMY_SP }] + .into(), + destination, + target: Some(success), + unwind: UnwindAction::Unreachable, + call_source: CallSource::Use, + fn_span: expr_span, + }, + ); + success.unit() + } else { + this.cfg.push_assign( + block, + source_info, + destination, + Rvalue::Use(Operand::Move(place)), + ); + block.unit() + } + } ExprKind::Use { source } => this.expr_into_dest(destination, block, source), ExprKind::Borrow { arg, borrow_kind } => { // We don't do this in `as_rvalue` because we use `as_place` diff --git a/compiler/rustc_mir_build/src/builder/expr/stmt.rs b/compiler/rustc_mir_build/src/builder/expr/stmt.rs index 7f8a0a34c3123..2dff26f02f3a1 100644 --- a/compiler/rustc_mir_build/src/builder/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/builder/expr/stmt.rs @@ -78,8 +78,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // 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) + block = this.build_binary_op( + block, + op.into(), + expr_span, + lhs_ty, + Operand::Copy(lhs), + rhs + ) ); this.cfg.push_assign(block, source_info, lhs, result); diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 10b43390eb288..3a7854a5e118d 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -1,32 +1,31 @@ use std::sync::Arc; +use rustc_hir::ByRef; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use crate::builder::Builder; use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder}; -use crate::builder::matches::{FlatPat, MatchPairTree, TestCase}; +use crate::builder::matches::{FlatPat, MatchPairTree, PatternExtraData, TestCase}; impl<'a, 'tcx> Builder<'a, 'tcx> { - /// Builds and returns [`MatchPairTree`] subtrees, one for each pattern in + /// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in /// `subpatterns`, representing the fields of a [`PatKind::Variant`] or /// [`PatKind::Leaf`]. /// /// Used internally by [`MatchPairTree::for_pattern`]. fn field_match_pairs( &mut self, + match_pairs: &mut Vec>, + extra_data: &mut PatternExtraData<'tcx>, place: PlaceBuilder<'tcx>, subpatterns: &[FieldPat<'tcx>], - ) -> Vec> { - subpatterns - .iter() - .map(|fieldpat| { - let place = - place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty)); - MatchPairTree::for_pattern(place, &fieldpat.pattern, self) - }) - .collect() + ) { + for fieldpat in subpatterns { + let place = place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty)); + MatchPairTree::for_pattern(place, &fieldpat.pattern, self, match_pairs, extra_data); + } } /// Builds [`MatchPairTree`] subtrees for the prefix/middle/suffix parts of an @@ -36,6 +35,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn prefix_slice_suffix( &mut self, match_pairs: &mut Vec>, + extra_data: &mut PatternExtraData<'tcx>, place: &PlaceBuilder<'tcx>, prefix: &[Pat<'tcx>], opt_slice: &Option>>, @@ -56,11 +56,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ((prefix.len() + suffix.len()).try_into().unwrap(), false) }; - match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { + for (idx, subpattern) in prefix.iter().enumerate() { let elem = ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false }; - MatchPairTree::for_pattern(place.clone_project(elem), subpattern, self) - })); + let place = place.clone_project(elem); + MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data) + } if let Some(subslice_pat) = opt_slice { let suffix_len = suffix.len() as u64; @@ -69,10 +70,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { to: if exact_size { min_length - suffix_len } else { suffix_len }, from_end: !exact_size, }); - match_pairs.push(MatchPairTree::for_pattern(subslice, subslice_pat, self)); + MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data); } - match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| { + for (idx, subpattern) in suffix.iter().rev().enumerate() { let end_offset = (idx + 1) as u64; let elem = ProjectionElem::ConstantIndex { offset: if exact_size { min_length - end_offset } else { end_offset }, @@ -80,192 +81,236 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { from_end: !exact_size, }; let place = place.clone_project(elem); - MatchPairTree::for_pattern(place, subpattern, self) - })); + MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data) + } } } impl<'tcx> MatchPairTree<'tcx> { /// Recursively builds a match pair tree for the given pattern and its /// subpatterns. - pub(in crate::builder) fn for_pattern( + pub(super) fn for_pattern( mut place_builder: PlaceBuilder<'tcx>, pattern: &Pat<'tcx>, cx: &mut Builder<'_, 'tcx>, - ) -> MatchPairTree<'tcx> { + match_pairs: &mut Vec, // Newly-created nodes are added to this vector + extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here + ) { // Force the place type to the pattern's type. // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? if let Some(resolved) = place_builder.resolve_upvar(cx) { place_builder = resolved; } - // Only add the OpaqueCast projection if the given place is an opaque type and the - // expected type from the pattern is not. - let may_need_cast = match place_builder.base() { - PlaceBase::Local(local) => { - let ty = - Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx).ty; - ty != pattern.ty && ty.has_opaque_types() + if !cx.tcx.next_trait_solver_globally() { + // Only add the OpaqueCast projection if the given place is an opaque type and the + // expected type from the pattern is not. + let may_need_cast = match place_builder.base() { + PlaceBase::Local(local) => { + let ty = + Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx) + .ty; + ty != pattern.ty && ty.has_opaque_types() + } + _ => true, + }; + if may_need_cast { + place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty)); } - _ => true, - }; - if may_need_cast { - place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty)); } let place = place_builder.try_to_place(cx); - let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None }; let mut subpairs = Vec::new(); let test_case = match pattern.kind { - PatKind::Wild | PatKind::Error(_) => default_irrefutable(), + PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None, - PatKind::Or { ref pats } => TestCase::Or { + PatKind::Or { ref pats } => Some(TestCase::Or { pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(), - }, + }), PatKind::Range(ref range) => { if range.is_full_range(cx.tcx) == Some(true) { - default_irrefutable() + None } else { - TestCase::Range(Arc::clone(range)) + Some(TestCase::Range(Arc::clone(range))) } } - PatKind::Constant { value } => TestCase::Constant { value }, + PatKind::Constant { value } => Some(TestCase::Constant { value }), PatKind::AscribeUserType { ascription: Ascription { ref annotation, variance }, ref subpattern, .. } => { + MatchPairTree::for_pattern( + place_builder, + subpattern, + cx, + &mut subpairs, + extra_data, + ); + // Apply the type ascription to the value at `match_pair.place` - let ascription = place.map(|source| super::Ascription { - annotation: annotation.clone(), - source, - variance, - }); + if let Some(source) = place { + let annotation = annotation.clone(); + extra_data.ascriptions.push(super::Ascription { source, annotation, variance }); + } - subpairs.push(MatchPairTree::for_pattern(place_builder, subpattern, cx)); - TestCase::Irrefutable { ascription, binding: None } + None } PatKind::Binding { mode, var, ref subpattern, .. } => { - let binding = place.map(|source| super::Binding { - span: pattern.span, - source, - var_id: var, - binding_mode: mode, - }); + // In order to please the borrow checker, when lowering a pattern + // like `x @ subpat` we must establish any bindings in `subpat` + // before establishing the binding for `x`. + // + // For example (from #69971): + // + // ```ignore (illustrative) + // struct NonCopyStruct { + // copy_field: u32, + // } + // + // fn foo1(x: NonCopyStruct) { + // let y @ NonCopyStruct { copy_field: z } = x; + // // the above should turn into + // let z = x.copy_field; + // let y = x; + // } + // ``` + // First, recurse into the subpattern, if any. if let Some(subpattern) = subpattern.as_ref() { // this is the `x @ P` case; have to keep matching against `P` now - subpairs.push(MatchPairTree::for_pattern(place_builder, subpattern, cx)); + MatchPairTree::for_pattern( + place_builder, + subpattern, + cx, + &mut subpairs, + extra_data, + ); + } + + // Then push this binding, after any bindings in the subpattern. + if let Some(source) = place { + extra_data.bindings.push(super::Binding { + span: pattern.span, + source, + var_id: var, + binding_mode: mode, + }); } - TestCase::Irrefutable { ascription: None, binding } - } - PatKind::ExpandedConstant { subpattern: ref pattern, def_id: _, is_inline: false } => { - subpairs.push(MatchPairTree::for_pattern(place_builder, pattern, cx)); - default_irrefutable() + None } - PatKind::ExpandedConstant { subpattern: ref pattern, def_id, is_inline: true } => { - // Apply a type ascription for the inline constant to the value at `match_pair.place` - let ascription = place.map(|source| { - let span = pattern.span; - let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id()); - let args = ty::InlineConstArgs::new( - cx.tcx, - ty::InlineConstArgsParts { - parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id), - ty: cx.infcx.next_ty_var(span), - }, - ) - .args; - let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::new( - ty::UserTypeKind::TypeOf(def_id, ty::UserArgs { args, user_self_ty: None }), - )); - let annotation = ty::CanonicalUserTypeAnnotation { - inferred_ty: pattern.ty, - span, - user_ty: Box::new(user_ty), - }; - super::Ascription { annotation, source, variance: ty::Contravariant } - }); - subpairs.push(MatchPairTree::for_pattern(place_builder, pattern, cx)); - TestCase::Irrefutable { ascription, binding: None } + PatKind::ExpandedConstant { subpattern: ref pattern, .. } => { + MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs, extra_data); + None } PatKind::Array { ref prefix, ref slice, ref suffix } => { - cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix); - default_irrefutable() + cx.prefix_slice_suffix( + &mut subpairs, + extra_data, + &place_builder, + prefix, + slice, + suffix, + ); + None } PatKind::Slice { ref prefix, ref slice, ref suffix } => { - cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix); + cx.prefix_slice_suffix( + &mut subpairs, + extra_data, + &place_builder, + prefix, + slice, + suffix, + ); if prefix.is_empty() && slice.is_some() && suffix.is_empty() { - default_irrefutable() + None } else { - TestCase::Slice { + Some(TestCase::Slice { len: prefix.len() + suffix.len(), variable_length: slice.is_some(), - } + }) } } PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => { let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)` - subpairs = cx.field_match_pairs(downcast_place, subpatterns); + cx.field_match_pairs(&mut subpairs, extra_data, downcast_place, subpatterns); let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| { i == variant_index - || !v - .inhabited_predicate(cx.tcx, adt_def) - .instantiate(cx.tcx, args) - .apply_ignore_module(cx.tcx, cx.infcx.typing_env(cx.param_env)) - }) && (adt_def.did().is_local() - || !adt_def.is_variant_list_non_exhaustive()); - if irrefutable { - default_irrefutable() - } else { - TestCase::Variant { adt_def, variant_index } - } + || !v.inhabited_predicate(cx.tcx, adt_def).instantiate(cx.tcx, args).apply( + cx.tcx, + cx.infcx.typing_env(cx.param_env), + cx.def_id.into(), + ) + }) && !adt_def.variant_list_has_applicable_non_exhaustive(); + if irrefutable { None } else { Some(TestCase::Variant { adt_def, variant_index }) } } PatKind::Leaf { ref subpatterns } => { - subpairs = cx.field_match_pairs(place_builder, subpatterns); - default_irrefutable() + cx.field_match_pairs(&mut subpairs, extra_data, place_builder, subpatterns); + None } - PatKind::Deref { ref subpattern } => { - subpairs.push(MatchPairTree::for_pattern(place_builder.deref(), subpattern, cx)); - default_irrefutable() + PatKind::Deref { ref subpattern } + | PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => { + if cfg!(debug_assertions) && matches!(pattern.kind, PatKind::DerefPattern { .. }) { + // Only deref patterns on boxes can be lowered using a built-in deref. + debug_assert!(pattern.ty.is_box()); + } + + MatchPairTree::for_pattern( + place_builder.deref(), + subpattern, + cx, + &mut subpairs, + extra_data, + ); + None } - PatKind::DerefPattern { ref subpattern, mutability } => { + PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(mutability) } => { // Create a new temporary for each deref pattern. // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls? let temp = cx.temp( Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability), pattern.span, ); - subpairs.push(MatchPairTree::for_pattern( + MatchPairTree::for_pattern( PlaceBuilder::from(temp).deref(), subpattern, cx, - )); - TestCase::Deref { temp, mutability } + &mut subpairs, + extra_data, + ); + Some(TestCase::Deref { temp, mutability }) } - PatKind::Never => TestCase::Never, + PatKind::Never => Some(TestCase::Never), }; - MatchPairTree { - place, - test_case, - subpairs, - pattern_ty: pattern.ty, - pattern_span: pattern.span, + if let Some(test_case) = test_case { + // This pattern is refutable, so push a new match-pair node. + match_pairs.push(MatchPairTree { + place, + test_case, + subpairs, + pattern_ty: pattern.ty, + pattern_span: pattern.span, + }) + } else { + // This pattern is irrefutable, so it doesn't need its own match-pair node. + // Just push its refutable subpatterns instead, if any. + match_pairs.extend(subpairs); } } } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index d05d5b151ff48..977d4f3e931b5 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -5,20 +5,26 @@ //! This also includes code for pattern bindings in `let` statements and //! function parameters. +use std::assert_matches::assert_matches; +use std::borrow::Borrow; +use std::mem; +use std::sync::Arc; + use rustc_abi::VariantIdx; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_hir::{BindingMode, ByRef}; +use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node}; use rustc_middle::bug; use rustc_middle::middle::region; use rustc_middle::mir::{self, *}; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; -use rustc_span::{BytePos, Pos, Span, Symbol}; +use rustc_span::{BytePos, Pos, Span, Symbol, sym}; use tracing::{debug, instrument}; use crate::builder::ForGuard::{self, OutsideGuard, RefWithinGuard}; use crate::builder::expr::as_place::PlaceBuilder; +use crate::builder::matches::user_ty::ProjectedUserTypesNode; use crate::builder::scope::DropKind; use crate::builder::{ BlockAnd, BlockAndExtension, Builder, GuardFrame, GuardFrameLocal, LocalsForNode, @@ -26,15 +32,10 @@ use crate::builder::{ // helper functions, broken out by category: mod match_pair; -mod simplify; mod test; +mod user_ty; mod util; -use std::assert_matches::assert_matches; -use std::borrow::Borrow; -use std::mem; -use std::sync::Arc; - /// Arguments to [`Builder::then_else_break_inner`] that are usually forwarded /// to recursive invocations. #[derive(Clone, Copy)] @@ -756,24 +757,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard: Option, opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, ) -> Option { - self.visit_primary_bindings( + self.visit_primary_bindings_special( pattern, - UserTypeProjections::none(), - &mut |this, name, mode, var, span, ty, user_ty| { - if visibility_scope.is_none() { - visibility_scope = - Some(this.new_source_scope(scope_span, LintLevel::Inherited)); - } + &ProjectedUserTypesNode::None, + &mut |this, name, mode, var, span, ty, user_tys| { + let vis_scope = *visibility_scope + .get_or_insert_with(|| this.new_source_scope(scope_span, LintLevel::Inherited)); let source_info = SourceInfo { span, scope: this.source_scope }; - let visibility_scope = visibility_scope.unwrap(); + let user_tys = user_tys.build_user_type_projections(); + this.declare_binding( source_info, - visibility_scope, + vis_scope, name, mode, var, ty, - user_ty, + user_tys, ArmHasGuard(guard.is_some()), opt_match_place.map(|(x, y)| (x.cloned(), y)), pattern.span, @@ -849,13 +849,35 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - /// 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. + /// Visits all of the "primary" bindings in a pattern, i.e. the leftmost + /// occurrence of each variable bound by the pattern. + /// See [`PatKind::Binding::is_primary`] for more context. + /// + /// This variant provides only the limited subset of binding data needed + /// by its callers, and should be a "pure" visit without side-effects. pub(super) fn visit_primary_bindings( &mut self, pattern: &Pat<'tcx>, - pattern_user_ty: UserTypeProjections, + f: &mut impl FnMut(&mut Self, LocalVarId, Span), + ) { + pattern.walk_always(|pat| { + if let PatKind::Binding { var, is_primary: true, .. } = pat.kind { + f(self, var, pat.span); + } + }) + } + + /// Visits all of the "primary" bindings in a pattern, while preparing + /// additional user-type-annotation data needed by `declare_bindings`. + /// + /// This also has the side-effect of pushing all user type annotations + /// onto `canonical_user_type_annotations`, so that they end up in MIR + /// even if they aren't associated with any bindings. + #[instrument(level = "debug", skip(self, f))] + fn visit_primary_bindings_special( + &mut self, + pattern: &Pat<'tcx>, + user_tys: &ProjectedUserTypesNode<'_>, f: &mut impl FnMut( &mut Self, Symbol, @@ -863,20 +885,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { LocalVarId, Span, Ty<'tcx>, - UserTypeProjections, + &ProjectedUserTypesNode<'_>, ), ) { - debug!( - "visit_primary_bindings: pattern={:?} pattern_user_ty={:?}", - pattern, pattern_user_ty - ); + // Avoid having to write the full method name at each recursive call. + let visit_subpat = |this: &mut Self, subpat, user_tys: &_, f: &mut _| { + this.visit_primary_bindings_special(subpat, user_tys, f) + }; + match pattern.kind { PatKind::Binding { name, mode, var, ty, ref subpattern, is_primary, .. } => { if is_primary { - f(self, name, mode, var, pattern.span, ty, pattern_user_ty.clone()); + f(self, name, mode, var, pattern.span, ty, user_tys); } if let Some(subpattern) = subpattern.as_ref() { - self.visit_primary_bindings(subpattern, pattern_user_ty, f); + visit_subpat(self, subpattern, user_tys, f); } } @@ -885,32 +908,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let from = u64::try_from(prefix.len()).unwrap(); let to = u64::try_from(suffix.len()).unwrap(); for subpattern in prefix.iter() { - self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f); + visit_subpat(self, subpattern, &user_tys.index(), f); } if let Some(subpattern) = slice { - self.visit_primary_bindings( - subpattern, - pattern_user_ty.clone().subslice(from, to), - f, - ); + visit_subpat(self, subpattern, &user_tys.subslice(from, to), f); } for subpattern in suffix.iter() { - self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f); + visit_subpat(self, subpattern, &user_tys.index(), f); } } PatKind::Constant { .. } | PatKind::Range { .. } + | PatKind::Missing | PatKind::Wild | PatKind::Never | PatKind::Error(_) => {} PatKind::Deref { ref subpattern } => { - self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f); + visit_subpat(self, subpattern, &user_tys.deref(), f); } PatKind::DerefPattern { ref subpattern, .. } => { - self.visit_primary_bindings(subpattern, UserTypeProjections::none(), f); + visit_subpat(self, subpattern, &ProjectedUserTypesNode::None, f); } PatKind::AscribeUserType { @@ -926,28 +946,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Note that the variance doesn't apply here, as we are tracking the effect // of `user_ty` on any bindings contained with subpattern. + // Caution: Pushing this user type here is load-bearing even for + // patterns containing no bindings, to ensure that the type ends + // up represented in MIR _somewhere_. let base_user_ty = self.canonical_user_type_annotations.push(annotation.clone()); - let subpattern_user_ty = pattern_user_ty.push_user_type(base_user_ty); - self.visit_primary_bindings(subpattern, subpattern_user_ty, f) + let subpattern_user_tys = user_tys.push_user_type(base_user_ty); + visit_subpat(self, subpattern, &subpattern_user_tys, f) } PatKind::ExpandedConstant { ref subpattern, .. } => { - self.visit_primary_bindings(subpattern, pattern_user_ty, f) + visit_subpat(self, subpattern, user_tys, 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); + let subpattern_user_tys = user_tys.leaf(subpattern.field); + debug!("visit_primary_bindings: subpattern_user_tys={subpattern_user_tys:?}"); + visit_subpat(self, &subpattern.pattern, &subpattern_user_tys, f); } } PatKind::Variant { adt_def, args: _, 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); + let subpattern_user_tys = + user_tys.variant(adt_def, variant_index, subpattern.field); + visit_subpat(self, &subpattern.pattern, &subpattern_user_tys, f); } } PatKind::Or { ref pats } => { @@ -956,7 +979,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // `let (x | y) = ...`, the primary binding of `y` occurs in // the right subpattern for subpattern in pats.iter() { - self.visit_primary_bindings(subpattern, pattern_user_ty.clone(), f); + visit_subpat(self, subpattern, user_tys, f); } } } @@ -987,18 +1010,16 @@ impl<'tcx> PatternExtraData<'tcx> { } /// A pattern in a form suitable for lowering the match tree, with all irrefutable -/// patterns simplified away, and or-patterns sorted to the end. +/// patterns simplified away. /// -/// Here, "flat" indicates that the pattern's match pairs have been recursively -/// simplified by [`Builder::simplify_match_pairs`]. They are not necessarily -/// flat in an absolute sense. +/// Here, "flat" indicates that irrefutable nodes in the pattern tree have been +/// recursively replaced with their refutable subpatterns. They are not +/// necessarily flat in an absolute sense. /// /// Will typically be incorporated into a [`Candidate`]. #[derive(Debug, Clone)] struct FlatPat<'tcx> { /// To match the pattern, all of these must be satisfied... - // Invariant: all the match pairs are recursively simplified. - // Invariant: or-patterns must be sorted to the end. match_pairs: Vec>, extra_data: PatternExtraData<'tcx>, @@ -1008,17 +1029,15 @@ impl<'tcx> FlatPat<'tcx> { /// Creates a `FlatPat` containing a simplified [`MatchPairTree`] list/forest /// for the given pattern. fn new(place: PlaceBuilder<'tcx>, pattern: &Pat<'tcx>, cx: &mut Builder<'_, 'tcx>) -> Self { - // First, recursively build a tree of match pairs for the given pattern. - let mut match_pairs = vec![MatchPairTree::for_pattern(place, pattern, cx)]; + // Recursively build a tree of match pairs for the given pattern. + let mut match_pairs = vec![]; let mut extra_data = PatternExtraData { span: pattern.span, bindings: Vec::new(), ascriptions: Vec::new(), is_never: pattern.is_never_pattern(), }; - // Recursively remove irrefutable match pairs, while recording their - // bindings/ascriptions, and sort or-patterns after other match pairs. - cx.simplify_match_pairs(&mut match_pairs, &mut extra_data); + MatchPairTree::for_pattern(place, pattern, cx, &mut match_pairs, &mut extra_data); Self { match_pairs, extra_data } } @@ -1055,7 +1074,6 @@ struct Candidate<'tcx> { /// (see [`Builder::test_remaining_match_pairs_after_or`]). /// /// Invariants: - /// - All [`TestCase::Irrefutable`] patterns have been removed by simplification. /// - All or-patterns ([`TestCase::Or`]) have been sorted to the end. match_pairs: Vec>, @@ -1126,7 +1144,7 @@ impl<'tcx> Candidate<'tcx> { /// Incorporates an already-simplified [`FlatPat`] into a new candidate. fn from_flat_pat(flat_pat: FlatPat<'tcx>, has_guard: bool) -> Self { - Candidate { + let mut this = Candidate { match_pairs: flat_pat.match_pairs, extra_data: flat_pat.extra_data, has_guard, @@ -1135,7 +1153,14 @@ impl<'tcx> Candidate<'tcx> { otherwise_block: None, pre_binding_block: None, false_edge_start_block: None, - } + }; + this.sort_match_pairs(); + this + } + + /// Restores the invariant that or-patterns must be sorted to the end. + fn sort_match_pairs(&mut self) { + self.match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. })); } /// Returns whether the first match pair of this candidate is an or-pattern. @@ -1227,17 +1252,11 @@ struct Ascription<'tcx> { /// - [`Builder::pick_test_for_match_pair`] (to choose a test) /// - [`Builder::sort_candidate`] (to see how the test interacts with a match pair) /// -/// Two variants are unlike the others and deserve special mention: -/// -/// - [`Self::Irrefutable`] is only used temporarily when building a [`MatchPairTree`]. -/// They are then flattened away by [`Builder::simplify_match_pairs`], with any -/// bindings/ascriptions incorporated into the enclosing [`FlatPat`]. -/// - [`Self::Or`] are not tested directly like the other variants. Instead they -/// participate in or-pattern expansion, where they are transformed into subcandidates. -/// - See [`Builder::expand_and_match_or_candidates`]. +/// Note that or-patterns are not tested directly like the other variants. +/// Instead they participate in or-pattern expansion, where they are transformed into +/// subcandidates. See [`Builder::expand_and_match_or_candidates`]. #[derive(Debug, Clone)] enum TestCase<'tcx> { - Irrefutable { binding: Option>, ascription: Option> }, Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx }, Constant { value: mir::Const<'tcx> }, Range(Arc>), @@ -1265,15 +1284,11 @@ pub(crate) struct MatchPairTree<'tcx> { /// --- /// This can be `None` if it referred to a non-captured place in a closure. /// - /// Invariant: Can only be `None` when `test_case` is `Irrefutable`. - /// Therefore this must be `Some(_)` after simplification. + /// Invariant: Can only be `None` when `test_case` is `Or`. + /// Therefore this must be `Some(_)` after or-pattern expansion. place: Option>, /// ... must pass this test... - /// - /// --- - /// Invariant: after creation and simplification in [`FlatPat::new`], - /// this must not be [`TestCase::Irrefutable`]. test_case: TestCase<'tcx>, /// ... and these subpairs must match. @@ -1318,8 +1333,8 @@ enum TestKind<'tcx> { Eq { value: 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`. + // types and `&[T]` types are converted back into patterns, so this can + // only be `&str`, `f32` or `f64`. ty: Ty<'tcx>, }, @@ -2764,7 +2779,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { mode: BindingMode, var_id: LocalVarId, var_ty: Ty<'tcx>, - user_ty: UserTypeProjections, + user_ty: Option>, has_guard: ArmHasGuard, opt_match_place: Option<(Option>, Span)>, pat_span: Span, @@ -2774,7 +2789,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let local = LocalDecl { mutability: mode.1, ty: var_ty, - user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) }, + user_ty, source_info, local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(BindingForm::Var( VarBindingForm { @@ -2790,13 +2805,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { )))), }; let for_arm_body = self.local_decls.push(local); - self.var_debug_info.push(VarDebugInfo { - name, - source_info: debug_source_info, - value: VarDebugInfoContents::Place(for_arm_body.into()), - composite: None, - argument_index: None, - }); + if self.should_emit_debug_info_for_binding(name, var_id) { + self.var_debug_info.push(VarDebugInfo { + name, + source_info: debug_source_info, + value: VarDebugInfoContents::Place(for_arm_body.into()), + composite: None, + argument_index: None, + }); + } 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 @@ -2809,13 +2826,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { BindingForm::RefForGuard, ))), }); - self.var_debug_info.push(VarDebugInfo { - name, - source_info: debug_source_info, - value: VarDebugInfoContents::Place(ref_for_guard.into()), - composite: None, - argument_index: None, - }); + if self.should_emit_debug_info_for_binding(name, var_id) { + self.var_debug_info.push(VarDebugInfo { + name, + source_info: debug_source_info, + value: VarDebugInfoContents::Place(ref_for_guard.into()), + composite: None, + argument_index: None, + }); + } LocalsForNode::ForGuard { ref_for_guard, for_arm_body } } else { LocalsForNode::One(for_arm_body) @@ -2823,4 +2842,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!(?locals); self.var_indices.insert(var_id, locals); } + + /// Some bindings are introduced when producing HIR from the AST and don't + /// actually exist in the source. Skip producing debug info for those when + /// we can recognize them. + fn should_emit_debug_info_for_binding(&self, name: Symbol, var_id: LocalVarId) -> bool { + // For now we only recognize the output of desugaring assigns. + if name != sym::lhs { + return true; + } + + let tcx = self.tcx; + for (_, node) in tcx.hir_parent_iter(var_id.0) { + // FIXME(khuey) at what point is it safe to bail on the iterator? + // Can we stop at the first non-Pat node? + if matches!(node, Node::LetStmt(&LetStmt { source: LocalSource::AssignDesugar(_), .. })) + { + return false; + } + } + + true + } } diff --git a/compiler/rustc_mir_build/src/builder/matches/simplify.rs b/compiler/rustc_mir_build/src/builder/matches/simplify.rs deleted file mode 100644 index 15c860151dc9e..0000000000000 --- a/compiler/rustc_mir_build/src/builder/matches/simplify.rs +++ /dev/null @@ -1,71 +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_match_pairs` 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 std::mem; - -use tracing::{debug, instrument}; - -use crate::builder::Builder; -use crate::builder::matches::{MatchPairTree, PatternExtraData, TestCase}; - -impl<'a, 'tcx> Builder<'a, 'tcx> { - /// Simplify a list of match pairs so they all require a test. Stores relevant bindings and - /// ascriptions in `extra_data`. - #[instrument(skip(self), level = "debug")] - pub(super) fn simplify_match_pairs( - &mut self, - match_pairs: &mut Vec>, - extra_data: &mut PatternExtraData<'tcx>, - ) { - // In order to please the borrow checker, in a pattern like `x @ pat` we must lower the - // bindings in `pat` before `x`. E.g. (#69971): - // - // struct NonCopyStruct { - // copy_field: u32, - // } - // - // fn foo1(x: NonCopyStruct) { - // let y @ NonCopyStruct { copy_field: z } = x; - // // the above should turn into - // let z = x.copy_field; - // let y = x; - // } - // - // We therefore lower bindings from left-to-right, except we lower the `x` in `x @ pat` - // after any bindings in `pat`. This doesn't work for or-patterns: the current structure of - // match lowering forces us to lower bindings inside or-patterns last. - for mut match_pair in mem::take(match_pairs) { - self.simplify_match_pairs(&mut match_pair.subpairs, extra_data); - if let TestCase::Irrefutable { binding, ascription } = match_pair.test_case { - if let Some(binding) = binding { - extra_data.bindings.push(binding); - } - if let Some(ascription) = ascription { - extra_data.ascriptions.push(ascription); - } - // Simplifiable pattern; we replace it with its already simplified subpairs. - match_pairs.append(&mut match_pair.subpairs); - } else { - // Unsimplifiable pattern; we keep it. - match_pairs.push(match_pair); - } - } - - // 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. - match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. })); - debug!(simplified = ?match_pairs, "simplify_match_pairs"); - } -} diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index a7a028bff97f7..210b9cce581fd 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -11,7 +11,6 @@ use std::sync::Arc; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::{LangItem, RangeEnd}; use rustc_middle::mir::*; -use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -55,12 +54,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Or-patterns are not tested directly; instead they are expanded into subcandidates, // which are then distinguished by testing whatever non-or patterns they contain. TestCase::Or { .. } => bug!("or-patterns should have already been handled"), - - TestCase::Irrefutable { .. } => span_bug!( - match_pair.pattern_span, - "simplifiable pattern found: {:?}", - match_pair.pattern_span - ), }; Test { span: match_pair.pattern_span, kind } @@ -147,12 +140,35 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let success_block = target_block(TestBranch::Success); let fail_block = target_block(TestBranch::Failure); - let expect_ty = value.ty(); - let expect = self.literal_operand(test.span, value); + let mut expect_ty = value.ty(); + let mut expect = self.literal_operand(test.span, value); let mut place = place; let mut block = block; match ty.kind() { + ty::Str => { + // String literal patterns may have type `str` if `deref_patterns` is + // enabled, in order to allow `deref!("..."): String`. In this case, `value` + // is of type `&str`, so we compare it to `&place`. + if !tcx.features().deref_patterns() { + span_bug!( + test.span, + "matching on `str` went through without enabling deref_patterns" + ); + } + let re_erased = tcx.lifetimes.re_erased; + let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_); + let ref_place = self.temp(ref_str_ty, test.span); + // `let ref_place: &str = &place;` + self.cfg.push_assign( + block, + self.source_info(test.span), + ref_place, + Rvalue::Ref(re_erased, BorrowKind::Shared, place), + ); + place = ref_place; + ty = ref_str_ty; + } ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => { if !tcx.features().string_deref_patterns() { span_bug!( @@ -181,24 +197,58 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { place = ref_str; ty = ref_str_ty; } + &ty::Pat(base, _) => { + assert_eq!(ty, value.ty()); + assert!(base.is_trivially_pure_clone_copy()); + + let transmuted_place = self.temp(base, test.span); + self.cfg.push_assign( + block, + self.source_info(scrutinee_span), + transmuted_place, + Rvalue::Cast(CastKind::Transmute, Operand::Copy(place), base), + ); + + let transmuted_expect = self.temp(base, test.span); + self.cfg.push_assign( + block, + self.source_info(test.span), + transmuted_expect, + Rvalue::Cast(CastKind::Transmute, expect, base), + ); + + place = transmuted_place; + expect = Operand::Copy(transmuted_expect); + ty = base; + expect_ty = base; + } _ => {} } + assert_eq!(expect_ty, ty); if !ty.is_scalar() { // Use `PartialEq::eq` instead of `BinOp::Eq` // (the binop can only handle primitives) - self.non_scalar_compare( + // Make sure that we do *not* call any user-defined code here. + // The only type that can end up here is string literals, which have their + // comparison defined in `core`. + // (Interestingly this means that exhaustiveness analysis relies, for soundness, + // on the `PartialEq` impl for `str` to b correct!) + match *ty.kind() { + ty::Ref(_, deref_ty, _) if deref_ty == self.tcx.types.str_ => {} + _ => { + span_bug!(source_info.span, "invalid type for non-scalar compare: {ty}") + } + }; + self.string_compare( block, success_block, fail_block, source_info, expect, - expect_ty, Operand::Copy(place), - ty, ); } else { - assert_eq!(expect_ty, ty); self.compare( block, success_block, @@ -376,97 +426,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); } - /// Compare two values using `::eq`. - /// If the values are already references, just call it directly, otherwise - /// take a reference to the values first and then call it. - fn non_scalar_compare( + /// Compare two values of type `&str` using `::eq`. + fn string_compare( &mut self, block: BasicBlock, success_block: BasicBlock, fail_block: BasicBlock, source_info: SourceInfo, - mut expect: Operand<'tcx>, - expect_ty: Ty<'tcx>, - mut val: Operand<'tcx>, - mut ty: Ty<'tcx>, + expect: Operand<'tcx>, + val: Operand<'tcx>, ) { - // 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(expect_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.tcx; - // make both a slice - ty = Ty::new_imm_ref(tcx, *region, Ty::new_slice(tcx, *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::PointerCoercion( - PointerCoercion::Unsize, - CoercionSource::Implicit, - ), - val, - ty, - ), - ); - val = Operand::Copy(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::PointerCoercion( - PointerCoercion::Unsize, - CoercionSource::Implicit, - ), - expect, - ty, - ), - ); - expect = Operand::Move(slice); - } - } - } - - // Figure out the type on which we are calling `PartialEq`. This involves an extra wrapping - // reference: we can only compare two `&T`, and then compare_ty will be `T`. - // Make sure that we do *not* call any user-defined code here. - // The only types that can end up here are string and byte literals, - // which have their comparison defined in `core`. - // (Interestingly this means that exhaustiveness analysis relies, for soundness, - // on the `PartialEq` impls for `str` and `[u8]` to b correct!) - let compare_ty = match *ty.kind() { - ty::Ref(_, deref_ty, _) - if deref_ty == self.tcx.types.str_ || deref_ty != self.tcx.types.u8 => - { - deref_ty - } - _ => span_bug!(source_info.span, "invalid type for non-scalar compare: {}", ty), - }; - + let str_ty = self.tcx.types.str_; let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span)); - let method = trait_method(self.tcx, eq_def_id, sym::eq, [compare_ty, compare_ty]); + let method = trait_method(self.tcx, eq_def_id, sym::eq, [str_ty, str_ty]); let bool_ty = self.tcx.types.bool; let eq_result = self.temp(bool_ty, source_info.span); @@ -769,7 +741,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let match_pair = candidate.match_pairs.remove(match_pair_index); candidate.match_pairs.extend(match_pair.subpairs); // Move or-patterns to the end. - candidate.match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. })); + candidate.sort_match_pairs(); } ret @@ -791,7 +763,7 @@ fn trait_method<'tcx>( let item = tcx .associated_items(trait_def_id) .filter_by_name_unhygienic(method_name) - .find(|item| item.kind == ty::AssocKind::Fn) + .find(|item| item.is_fn()) .expect("trait method not found"); let method_ty = Ty::new_fn_def(tcx, item.def_id, args); diff --git a/compiler/rustc_mir_build/src/builder/matches/user_ty.rs b/compiler/rustc_mir_build/src/builder/matches/user_ty.rs new file mode 100644 index 0000000000000..df9f93ac328a6 --- /dev/null +++ b/compiler/rustc_mir_build/src/builder/matches/user_ty.rs @@ -0,0 +1,140 @@ +//! Helper code for building a linked list of user-type projections on the +//! stack while visiting a THIR pattern. +//! +//! This avoids having to repeatedly clone a partly-built [`UserTypeProjections`] +//! at every step of the traversal, which is what the previous code was doing. + +use std::assert_matches::assert_matches; +use std::iter; + +use rustc_abi::{FieldIdx, VariantIdx}; +use rustc_middle::mir::{ProjectionElem, UserTypeProjection, UserTypeProjections}; +use rustc_middle::ty::{AdtDef, UserTypeAnnotationIndex}; +use rustc_span::Symbol; + +/// One of a list of "operations" that can be used to lazily build projections +/// of user-specified types. +#[derive(Clone, Debug)] +pub(crate) enum ProjectedUserTypesOp { + PushUserType { base: UserTypeAnnotationIndex }, + + Index, + Subslice { from: u64, to: u64 }, + Deref, + Leaf { field: FieldIdx }, + Variant { name: Symbol, variant: VariantIdx, field: FieldIdx }, +} + +#[derive(Debug)] +pub(crate) enum ProjectedUserTypesNode<'a> { + None, + Chain { parent: &'a Self, op: ProjectedUserTypesOp }, +} + +impl<'a> ProjectedUserTypesNode<'a> { + pub(crate) fn push_user_type(&'a self, base: UserTypeAnnotationIndex) -> Self { + // Pushing a base user type always causes the chain to become non-empty. + Self::Chain { parent: self, op: ProjectedUserTypesOp::PushUserType { base } } + } + + /// Push another projection op onto the chain, but only if it is already non-empty. + fn maybe_push(&'a self, op_fn: impl FnOnce() -> ProjectedUserTypesOp) -> Self { + match self { + Self::None => Self::None, + Self::Chain { .. } => Self::Chain { parent: self, op: op_fn() }, + } + } + + pub(crate) fn index(&'a self) -> Self { + self.maybe_push(|| ProjectedUserTypesOp::Index) + } + + pub(crate) fn subslice(&'a self, from: u64, to: u64) -> Self { + self.maybe_push(|| ProjectedUserTypesOp::Subslice { from, to }) + } + + pub(crate) fn deref(&'a self) -> Self { + self.maybe_push(|| ProjectedUserTypesOp::Deref) + } + + pub(crate) fn leaf(&'a self, field: FieldIdx) -> Self { + self.maybe_push(|| ProjectedUserTypesOp::Leaf { field }) + } + + pub(crate) fn variant( + &'a self, + adt_def: AdtDef<'_>, + variant: VariantIdx, + field: FieldIdx, + ) -> Self { + self.maybe_push(|| { + let name = adt_def.variant(variant).name; + ProjectedUserTypesOp::Variant { name, variant, field } + }) + } + + /// Traverses the chain of nodes to yield each op in the chain. + /// Because this walks from child node to parent node, the ops are + /// naturally yielded in "reverse" order. + fn iter_ops_reversed(&'a self) -> impl Iterator { + let mut next = self; + iter::from_fn(move || match next { + Self::None => None, + Self::Chain { parent, op } => { + next = parent; + Some(op) + } + }) + } + + /// Assembles this chain of user-type projections into a proper data structure. + pub(crate) fn build_user_type_projections(&self) -> Option> { + // If we know there's nothing to do, just return None immediately. + if matches!(self, Self::None) { + return None; + } + + let ops_reversed = self.iter_ops_reversed().cloned().collect::>(); + // The "first" op should always be `PushUserType`. + // Other projections are only added if there is at least one user type. + assert_matches!(ops_reversed.last(), Some(ProjectedUserTypesOp::PushUserType { .. })); + + let mut projections = vec![]; + for op in ops_reversed.into_iter().rev() { + match op { + ProjectedUserTypesOp::PushUserType { base } => { + projections.push(UserTypeProjection { base, projs: vec![] }) + } + + ProjectedUserTypesOp::Index => { + for p in &mut projections { + p.projs.push(ProjectionElem::Index(())) + } + } + ProjectedUserTypesOp::Subslice { from, to } => { + for p in &mut projections { + p.projs.push(ProjectionElem::Subslice { from, to, from_end: true }) + } + } + ProjectedUserTypesOp::Deref => { + for p in &mut projections { + p.projs.push(ProjectionElem::Deref) + } + } + ProjectedUserTypesOp::Leaf { field } => { + for p in &mut projections { + p.projs.push(ProjectionElem::Field(field, ())) + } + } + ProjectedUserTypesOp::Variant { name, variant, field } => { + for p in &mut projections { + p.projs.push(ProjectionElem::Downcast(Some(name), variant)); + p.projs.push(ProjectionElem::Field(field, ())); + } + } + } + } + + Some(Box::new(UserTypeProjections { contents: projections })) + } +} diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 949559549345f..127b191e3353d 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -13,7 +13,7 @@ use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Node}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node}; use rustc_index::bit_set::GrowableBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -37,7 +37,7 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>( .map(|captured_place| { let name = captured_place.to_symbol(); match captured_place.info.capture_kind { - ty::UpvarCapture::ByValue => name, + ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => name, ty::UpvarCapture::ByRef(..) => Symbol::intern(&format!("_ref__{name}")), } }) @@ -48,11 +48,11 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>( /// this directly; instead use the cached version via `mir_built`. pub fn build_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> { tcx.ensure_done().thir_abstract_const(def); - if let Err(e) = tcx.check_match(def) { + if let Err(e) = tcx.ensure_ok().check_match(def) { return construct_error(tcx, def, e); } - if let Err(err) = tcx.check_tail_calls(def) { + if let Err(err) = tcx.ensure_ok().check_tail_calls(def) { return construct_error(tcx, def, err); } @@ -66,8 +66,7 @@ pub fn build_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> { } }; - // this must run before MIR dump, because - // "not all control paths return a value" is reported here. + // Checking liveness after building the THIR ensures there were no typeck errors. // // maybe move the check to a MIR pass? tcx.ensure_ok().check_liveness(def); @@ -221,7 +220,7 @@ struct Builder<'a, 'tcx> { coverage_info: Option, } -type CaptureMap<'tcx> = SortedIndexMultiMap>; +type CaptureMap<'tcx> = SortedIndexMultiMap>; #[derive(Debug)] struct Capture<'tcx> { @@ -451,13 +450,9 @@ fn construct_fn<'tcx>( let span = tcx.def_span(fn_def); let fn_id = tcx.local_def_id_to_hir_id(fn_def); - // The representation of thir for `-Zunpretty=thir-tree` relies on - // the entry expression being the last element of `thir.exprs`. - assert_eq!(expr.as_usize(), thir.exprs.len() - 1); - // Figure out what primary body this item has. let body = tcx.hir_body_owned_by(fn_def); - let span_with_body = tcx.hir().span_with_body(fn_id); + let span_with_body = tcx.hir_span_with_body(fn_id); let return_ty_span = tcx .hir_fn_decl_by_hir_id(fn_id) .unwrap_or_else(|| span_bug!(span, "can't build MIR for {:?}", fn_def)) @@ -485,7 +480,7 @@ fn construct_fn<'tcx>( }; if let Some(custom_mir_attr) = - tcx.hir().attrs(fn_id).iter().find(|attr| attr.name_or_empty() == sym::custom_mir) + tcx.hir_attrs(fn_id).iter().find(|attr| attr.has_name(sym::custom_mir)) { return custom::build_custom_mir( tcx, @@ -563,7 +558,7 @@ fn construct_const<'a, 'tcx>( // Figure out what primary body this item has. let (span, const_ty_span) = match tcx.hir_node(hir_id) { Node::Item(hir::Item { - kind: hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _, _), + kind: hir::ItemKind::Static(_, ty, _, _) | hir::ItemKind::Const(_, ty, _, _), span, .. }) @@ -612,7 +607,8 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) - | DefKind::AssocConst | DefKind::AnonConst | DefKind::InlineConst - | DefKind::Static { .. } => (vec![], tcx.type_of(def_id).instantiate_identity(), None), + | DefKind::Static { .. } + | DefKind::GlobalAsm => (vec![], tcx.type_of(def_id).instantiate_identity(), None), DefKind::Ctor(..) | DefKind::Fn | DefKind::AssocFn => { let sig = tcx.liberate_late_bound_regions( def_id.to_def_id(), @@ -740,7 +736,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { coroutine: Option>>, ) -> Builder<'a, 'tcx> { let tcx = infcx.tcx; - let attrs = tcx.hir().attrs(hir_id); + let attrs = tcx.hir_attrs(hir_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. @@ -852,6 +848,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let capture_tys = upvar_args.upvar_tys(); let tcx = self.tcx; + let mut upvar_owner = None; self.upvars = tcx .closure_captures(self.def_id) .iter() @@ -865,13 +862,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, _ => bug!("Expected an upvar"), }; + let upvar_base = upvar_owner.get_or_insert(var_id.owner); + assert_eq!(*upvar_base, var_id.owner); + let var_id = var_id.local_id; let mutability = captured_place.mutability; let mut projs = closure_env_projs.clone(); projs.push(ProjectionElem::Field(FieldIdx::new(i), ty)); match capture { - ty::UpvarCapture::ByValue => {} + ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {} ty::UpvarCapture::ByRef(..) => { projs.push(ProjectionElem::Deref); } @@ -993,7 +993,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.source_scope = source_scope; } - if self.tcx.intrinsic(self.def_id).is_some_and(|i| i.must_be_overridden) { + if self.tcx.intrinsic(self.def_id).is_some_and(|i| i.must_be_overridden) + || self.tcx.is_sdylib_interface_build() + { let source_info = self.source_info(rustc_span::DUMMY_SP); self.cfg.terminate(block, source_info, TerminatorKind::Unreachable); self.cfg.start_new_block().unit() diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 8156123949121..2a30777e98c4f 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -89,6 +89,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::{ExprId, LintLevel}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint::Level; use rustc_span::source_map::Spanned; @@ -305,27 +306,25 @@ impl DropTree { } /// Builds the MIR for a given drop tree. - /// - /// `blocks` should have the same length as `self.drops`, and may have its - /// first value set to some already existing block. fn build_mir<'tcx, T: DropTreeBuilder<'tcx>>( &mut self, cfg: &mut CFG<'tcx>, - blocks: &mut IndexVec>, - ) { + root_node: Option, + ) -> IndexVec> { debug!("DropTree::build_mir(drops = {:#?})", self); - assert_eq!(blocks.len(), self.drops.len()); - self.assign_blocks::(cfg, blocks); - self.link_blocks(cfg, blocks) + let mut blocks = self.assign_blocks::(cfg, root_node); + self.link_blocks(cfg, &mut blocks); + + blocks } /// Assign blocks for all of the drops in the drop tree that need them. fn assign_blocks<'tcx, T: DropTreeBuilder<'tcx>>( &mut self, cfg: &mut CFG<'tcx>, - blocks: &mut IndexVec>, - ) { + root_node: Option, + ) -> IndexVec> { // StorageDead statements can share blocks with each other and also with // a Drop terminator. We iterate through the drops to find which drops // need their own block. @@ -342,8 +341,11 @@ impl DropTree { Own, } + let mut blocks = IndexVec::from_elem(None, &self.drops); + blocks[ROOT_NODE] = root_node; + let mut needs_block = IndexVec::from_elem(Block::None, &self.drops); - if blocks[ROOT_NODE].is_some() { + if root_node.is_some() { // In some cases (such as drops for `continue`) the root node // already has a block. In this case, make sure that we don't // override it. @@ -385,6 +387,8 @@ impl DropTree { debug!("assign_blocks: blocks = {:#?}", blocks); assert!(entry_points.is_empty()); + + blocks } fn link_blocks<'tcx>( @@ -402,6 +406,8 @@ impl DropTree { unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup), place: drop_node.data.local.into(), replace: false, + drop: None, + async_fut: None, }; cfg.terminate(block, drop_node.data.source_info, terminator); } @@ -845,6 +851,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { target: next, unwind: UnwindAction::Continue, replace: false, + drop: None, + async_fut: None, }, ); block = next; @@ -876,6 +884,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.unit() } + fn is_async_drop_impl( + tcx: TyCtxt<'tcx>, + local_decls: &IndexVec>, + typing_env: ty::TypingEnv<'tcx>, + local: Local, + ) -> bool { + let ty = local_decls[local].ty; + if ty.is_async_drop(tcx, typing_env) || ty.is_coroutine() { + return true; + } + ty.needs_async_drop(tcx, typing_env) + } + fn is_async_drop(&self, local: Local) -> bool { + Self::is_async_drop_impl(self.tcx, &self.local_decls, self.typing_env(), local) + } + fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock { // If we are emitting a `drop` statement, we need to have the cached // diverge cleanup pads ready in case that drop panics. @@ -884,14 +908,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let unwind_to = if needs_cleanup { self.diverge_cleanup() } else { DropIdx::MAX }; let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes"); + let has_async_drops = is_coroutine + && scope.drops.iter().any(|v| v.kind == DropKind::Value && self.is_async_drop(v.local)); + let dropline_to = if has_async_drops { Some(self.diverge_dropline()) } else { None }; + let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes"); + let typing_env = self.typing_env(); build_scope_drops( &mut self.cfg, &mut self.scopes.unwind_drops, + &mut self.scopes.coroutine_drops, scope, block, unwind_to, + dropline_to, is_coroutine && needs_cleanup, self.arg_count, + |v: Local| Self::is_async_drop_impl(self.tcx, &self.local_decls, typing_env, v), ) .into_block() } @@ -942,14 +974,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { assert_eq!(orig_id.owner, self.hir_id.owner); let mut id = orig_id; - let hir = self.tcx.hir(); loop { if id == self.hir_id { // This is a moderately common case, mostly hit for previously unseen nodes. break; } - if hir.attrs(id).iter().any(|attr| Level::from_attr(attr).is_some()) { + if self.tcx.hir_attrs(id).iter().any(|attr| Level::from_attr(attr).is_some()) { // This is a rare case. It's for a node path that doesn't reach the root due to an // intervening lint level attribute. This result doesn't get cached. return id; @@ -1308,22 +1339,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.scopes.unwind_drops.add_entry_point(start, next_drop); } - /// Sets up a path that performs all required cleanup for dropping a - /// coroutine, starting from the given block that ends in - /// [TerminatorKind::Yield]. - /// - /// This path terminates in CoroutineDrop. - pub(crate) fn coroutine_drop_cleanup(&mut self, yield_block: BasicBlock) { + /// Returns the [DropIdx] for the innermost drop for dropline (coroutine drop path). + /// The `DropIdx` will be created if it doesn't already exist. + fn diverge_dropline(&mut self) -> DropIdx { + // It is okay to use dummy span because the getting scope index on the topmost scope + // must always succeed. + self.diverge_dropline_target(self.scopes.topmost(), DUMMY_SP) + } + + /// Similar to diverge_cleanup_target, but for dropline (coroutine drop path) + fn diverge_dropline_target(&mut self, target_scope: region::Scope, span: Span) -> DropIdx { debug_assert!( - matches!( - self.cfg.block_data(yield_block).terminator().kind, - TerminatorKind::Yield { .. } - ), - "coroutine_drop_cleanup called on block with non-yield terminator." + self.coroutine.is_some(), + "diverge_dropline_target is valid only for coroutine" ); - let (uncached_scope, mut cached_drop) = self - .scopes - .scopes + let target = self.scopes.scope_index(target_scope, span); + let (uncached_scope, mut cached_drop) = self.scopes.scopes[..=target] .iter() .enumerate() .rev() @@ -1332,13 +1363,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) .unwrap_or((0, ROOT_NODE)); - for scope in &mut self.scopes.scopes[uncached_scope..] { + if uncached_scope > target { + return cached_drop; + } + + for scope in &mut self.scopes.scopes[uncached_scope..=target] { for drop in &scope.drops { cached_drop = self.scopes.coroutine_drops.add_drop(*drop, cached_drop); } scope.cached_coroutine_drop_block = Some(cached_drop); } + cached_drop + } + + /// Sets up a path that performs all required cleanup for dropping a + /// coroutine, starting from the given block that ends in + /// [TerminatorKind::Yield]. + /// + /// This path terminates in CoroutineDrop. + pub(crate) fn coroutine_drop_cleanup(&mut self, yield_block: BasicBlock) { + debug_assert!( + matches!( + self.cfg.block_data(yield_block).terminator().kind, + TerminatorKind::Yield { .. } + ), + "coroutine_drop_cleanup called on block with non-yield terminator." + ); + let cached_drop = self.diverge_dropline(); self.scopes.coroutine_drops.add_entry_point(yield_block, cached_drop); } @@ -1369,6 +1421,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { target: assign, unwind: UnwindAction::Cleanup(assign_unwind), replace: true, + drop: None, + async_fut: None, }, ); self.diverge_from(block); @@ -1430,18 +1484,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// * `unwind_to`, describes the drops that would occur at this point in the code if a /// panic occurred (a subset of the drops in `scope`, since we sometimes elide StorageDead and other /// instructions on unwinding) +/// * `dropline_to`, describes the drops that would occur at this point in the code if a +/// coroutine drop occurred. /// * `storage_dead_on_unwind`, if true, then we should emit `StorageDead` even when unwinding /// * `arg_count`, number of MIR local variables corresponding to fn arguments (used to assert that we don't drop those) -fn build_scope_drops<'tcx>( +fn build_scope_drops<'tcx, F>( cfg: &mut CFG<'tcx>, unwind_drops: &mut DropTree, + coroutine_drops: &mut DropTree, scope: &Scope, block: BasicBlock, unwind_to: DropIdx, + dropline_to: Option, storage_dead_on_unwind: bool, arg_count: usize, -) -> BlockAnd<()> { - debug!("build_scope_drops({:?} -> {:?})", block, scope); + is_async_drop: F, +) -> BlockAnd<()> +where + F: Fn(Local) -> bool, +{ + debug!("build_scope_drops({:?} -> {:?}), dropline_to={:?}", block, scope, dropline_to); // Build up the drops in evaluation order. The end result will // look like: @@ -1474,6 +1536,9 @@ fn build_scope_drops<'tcx>( // will branch to `drops[n]`. let mut block = block; + // `dropline_to` indicates what needs to be dropped should coroutine drop occur. + let mut dropline_to = dropline_to; + for drop_data in scope.drops.iter().rev() { let source_info = drop_data.source_info; let local = drop_data.local; @@ -1490,15 +1555,27 @@ fn build_scope_drops<'tcx>( debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); unwind_to = unwind_drops.drops[unwind_to].next; + if let Some(idx) = dropline_to { + debug_assert_eq!(coroutine_drops.drops[idx].data.local, drop_data.local); + debug_assert_eq!(coroutine_drops.drops[idx].data.kind, drop_data.kind); + dropline_to = Some(coroutine_drops.drops[idx].next); + } + // If the operand has been moved, and we are not on an unwind // path, then don't generate the drop. (We only take this into // account for non-unwind paths so as not to disturb the // caching mechanism.) - if scope.moved_locals.iter().any(|&o| o == local) { + if scope.moved_locals.contains(&local) { continue; } unwind_drops.add_entry_point(block, unwind_to); + if let Some(to) = dropline_to + && is_async_drop(local) + { + coroutine_drops.add_entry_point(block, to); + } + let next = cfg.start_new_block(); cfg.terminate( block, @@ -1508,6 +1585,8 @@ fn build_scope_drops<'tcx>( target: next, unwind: UnwindAction::Continue, replace: false, + drop: None, + async_fut: None, }, ); block = next; @@ -1528,7 +1607,7 @@ fn build_scope_drops<'tcx>( // path, then don't generate the drop. (We only take this into // account for non-unwind paths so as not to disturb the // caching mechanism.) - if scope.moved_locals.iter().any(|&o| o == local) { + if scope.moved_locals.contains(&local) { continue; } @@ -1554,6 +1633,11 @@ fn build_scope_drops<'tcx>( debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); unwind_to = unwind_drops.drops[unwind_to].next; } + if let Some(idx) = dropline_to { + debug_assert_eq!(coroutine_drops.drops[idx].data.local, drop_data.local); + debug_assert_eq!(coroutine_drops.drops[idx].data.kind, drop_data.kind); + dropline_to = Some(coroutine_drops.drops[idx].next); + } // Only temps and vars need their storage dead. assert!(local.index() > arg_count); cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(local) }); @@ -1575,10 +1659,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { span: Span, continue_block: Option, ) -> Option> { - let mut blocks = IndexVec::from_elem(None, &drops.drops); - blocks[ROOT_NODE] = continue_block; - - drops.build_mir::(&mut self.cfg, &mut blocks); + let blocks = drops.build_mir::(&mut self.cfg, continue_block); let is_coroutine = self.coroutine.is_some(); // Link the exit drop tree to unwind drop tree. @@ -1612,6 +1693,39 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { } } } + // Link the exit drop tree to dropline drop tree (coroutine drop path) for async drops + if is_coroutine + && drops.drops.iter().any(|DropNode { data, next: _ }| { + data.kind == DropKind::Value && self.is_async_drop(data.local) + }) + { + let dropline_target = self.diverge_dropline_target(else_scope, span); + let mut dropline_indices = IndexVec::from_elem_n(dropline_target, 1); + for (drop_idx, drop_data) in drops.drops.iter_enumerated().skip(1) { + match drop_data.data.kind { + DropKind::Storage | DropKind::ForLint => { + let coroutine_drop = self + .scopes + .coroutine_drops + .add_drop(drop_data.data, dropline_indices[drop_data.next]); + dropline_indices.push(coroutine_drop); + } + DropKind::Value => { + let coroutine_drop = self + .scopes + .coroutine_drops + .add_drop(drop_data.data, dropline_indices[drop_data.next]); + if self.is_async_drop(drop_data.data.local) { + self.scopes.coroutine_drops.add_entry_point( + blocks[drop_idx].unwrap(), + dropline_indices[drop_data.next], + ); + } + dropline_indices.push(coroutine_drop); + } + } + } + } blocks[ROOT_NODE].map(BasicBlock::unit) } @@ -1634,8 +1748,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { let drops = &mut self.scopes.coroutine_drops; let cfg = &mut self.cfg; let fn_span = self.fn_span; - let mut blocks = IndexVec::from_elem(None, &drops.drops); - drops.build_mir::(cfg, &mut blocks); + let blocks = drops.build_mir::(cfg, None); if let Some(root_block) = blocks[ROOT_NODE] { cfg.terminate( root_block, @@ -1657,9 +1770,11 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { // to be captured by the coroutine. I'm not sure how important this // optimization is, but it is here. for (drop_idx, drop_node) in drops.drops.iter_enumerated() { - if let DropKind::Value = drop_node.data.kind { + if let DropKind::Value = drop_node.data.kind + && let Some(bb) = blocks[drop_idx] + { debug_assert!(drop_node.next < drops.drops.next_index()); - drops.entry_points.push((drop_node.next, blocks[drop_idx].unwrap())); + drops.entry_points.push((drop_node.next, bb)); } } Self::build_unwind_tree(cfg, drops, fn_span, resume_block); @@ -1671,9 +1786,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { fn_span: Span, resume_block: &mut Option, ) { - let mut blocks = IndexVec::from_elem(None, &drops.drops); - blocks[ROOT_NODE] = *resume_block; - drops.build_mir::(cfg, &mut blocks); + let blocks = drops.build_mir::(cfg, *resume_block); if let (None, Some(resume)) = (*resume_block, blocks[ROOT_NODE]) { cfg.terminate(resume, SourceInfo::outermost(fn_span), TerminatorKind::UnwindResume); @@ -1713,6 +1826,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for CoroutineDrop { let term = cfg.block_data_mut(from).terminator_mut(); if let TerminatorKind::Yield { ref mut drop, .. } = term.kind { *drop = Some(to); + } else if let TerminatorKind::Drop { ref mut drop, .. } = term.kind { + *drop = Some(to); } else { span_bug!( term.source_info.span, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index c2eafd0a74e65..9d0681b19b9d5 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -195,7 +195,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node. fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool { - self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow + self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).level == Level::Allow } /// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body. @@ -292,8 +292,10 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { }); } BlockSafety::ExplicitUnsafe(hir_id) => { - let used = - matches!(self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id), (Level::Allow, _)); + let used = matches!( + self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id).level, + Level::Allow + ); self.in_safety_context( SafetyContext::UnsafeBlock { span: block.span, @@ -313,6 +315,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { fn visit_pat(&mut self, pat: &'a Pat<'tcx>) { if self.in_union_destructure { match pat.kind { + PatKind::Missing => unreachable!(), // binding to a variable allows getting stuff out of variable PatKind::Binding { .. } // match is conditional on having this value @@ -401,9 +404,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_pat(self, pat); self.inside_adt = old_inside_adt; } - PatKind::ExpandedConstant { def_id, is_inline, .. } => { + PatKind::ExpandedConstant { def_id, .. } => { if let Some(def) = def_id.as_local() - && *is_inline + && matches!(self.tcx.def_kind(def_id), DefKind::InlineConst) { self.visit_inner_body(def); } @@ -451,6 +454,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { | ExprKind::Tuple { .. } | ExprKind::Unary { .. } | ExprKind::Call { .. } + | ExprKind::ByUse { .. } | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } | ExprKind::Break { .. } @@ -560,13 +564,17 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } } ExprKind::InlineAsm(box InlineAsmExpr { - asm_macro: AsmMacro::Asm | AsmMacro::NakedAsm, + asm_macro: asm_macro @ (AsmMacro::Asm | AsmMacro::NakedAsm), ref operands, template: _, options: _, line_spans: _, }) => { - self.requires_unsafe(expr.span, UseOfInlineAssembly); + // The `naked` attribute and the `naked_asm!` block form one atomic unit of + // unsafety, and `naked_asm!` does not itself need to be wrapped in an unsafe block. + if let AsmMacro::Asm = asm_macro { + self.requires_unsafe(expr.span, UseOfInlineAssembly); + } // For inline asm, do not use `walk_expr`, since we want to handle the label block // specially. @@ -752,6 +760,11 @@ impl UnsafeOpKind { span: Span, suggest_unsafe_block: bool, ) { + if tcx.hir_opt_delegation_sig_id(hir_id.owner.def_id).is_some() { + // The body of the delegation item is synthesized, so it makes no sense + // to emit this lint. + return; + } let parent_id = tcx.hir_get_parent_item(hir_id); let parent_owner = tcx.hir_owner_node(parent_id); let should_suggest = parent_owner.fn_sig().is_some_and(|sig| { @@ -937,7 +950,7 @@ impl UnsafeOpKind { } }); let unsafe_not_inherited_note = if let Some((id, _)) = note_non_inherited { - let span = tcx.hir().span(id); + let span = tcx.hir_span(id); let span = tcx.sess.source_map().guess_head_span(span); Some(UnsafeNotInheritedNote { span }) } else { @@ -1135,8 +1148,9 @@ impl UnsafeOpKind { pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { // Closures and inline consts are handled by their owner, if it has a body + assert!(!tcx.is_typeck_child(def.to_def_id())); // Also, don't safety check custom MIR - if tcx.is_typeck_child(def.to_def_id()) || tcx.has_attr(def, sym::custom_mir) { + if tcx.has_attr(def, sym::custom_mir) { return; } diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 17b22f25dbb0a..ae09db5023527 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, - MultiSpan, SubdiagMessageOp, Subdiagnostic, pluralize, + MultiSpan, Subdiagnostic, pluralize, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; @@ -546,11 +546,7 @@ pub(crate) struct UnsafeNotInheritedLintNote { } impl Subdiagnostic for UnsafeNotInheritedLintNote { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.span_note(self.signature_span, fluent::mir_build_unsafe_fn_safe_body); let body_start = self.body_span.shrink_to_lo(); let body_end = self.body_span.shrink_to_hi(); @@ -613,9 +609,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo diag.span_note(span, fluent::mir_build_def_note); } - let is_variant_list_non_exhaustive = matches!(self.ty.kind(), - ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local()); - if is_variant_list_non_exhaustive { + let is_non_exhaustive = matches!(self.ty.kind(), + ty::Adt(def, _) if def.variant_list_has_applicable_non_exhaustive()); + if is_non_exhaustive { diag.note(fluent::mir_build_non_exhaustive_type_note); } else { diag.note(fluent::mir_build_type_note); @@ -1031,11 +1027,7 @@ pub(crate) struct Variant { } impl<'tcx> Subdiagnostic for AdtDefinedHere<'tcx> { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("ty", self.ty); let mut spans = MultiSpan::from(self.adt_def_span); @@ -1117,11 +1109,7 @@ pub(crate) struct Rust2024IncompatiblePatSugg { } impl Subdiagnostic for Rust2024IncompatiblePatSugg { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { // Format and emit explanatory notes about default binding modes. Reversing the spans' order // means if we have nested spans, the innermost ones will be visited first. for (span, def_br_mutbl) in self.default_mode_labels.into_iter().rev() { diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index fa5db32d9134c..8c7003b778761 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -6,9 +6,7 @@ #![feature(assert_matches)] #![feature(box_patterns)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(try_blocks)] -#![warn(unreachable_pub)] // tidy-alphabetical-end // The `builder` module used to be named `build`, but that was causing GitHub's diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index b3210813703ca..b4fa55e1c1fdb 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -37,13 +37,23 @@ pub(crate) fn lit_to_const<'tcx>( let str_bytes = s.as_str().as_bytes(); ty::ValTree::from_raw_bytes(tcx, str_bytes) } + (ast::LitKind::Str(s, _), ty::Str) if tcx.features().deref_patterns() => { + // String literal patterns may have type `str` if `deref_patterns` is enabled, in order + // to allow `deref!("..."): String`. + let str_bytes = s.as_str().as_bytes(); + ty::ValTree::from_raw_bytes(tcx, str_bytes) + } (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) - if matches!(inner_ty.kind(), ty::Slice(_)) => + if matches!(inner_ty.kind(), ty::Slice(_) | ty::Array(..)) => { let bytes = data as &[u8]; ty::ValTree::from_raw_bytes(tcx, bytes) } - (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { + (ast::LitKind::ByteStr(data, _), ty::Slice(_) | ty::Array(..)) + if tcx.features().deref_patterns() => + { + // Byte string literal patterns may have type `[u8]` or `[u8; N]` if `deref_patterns` is + // enabled, in order to allow, e.g., `deref!(b"..."): Vec`. let bytes = data as &[u8]; ty::ValTree::from_raw_bytes(tcx, bytes) } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 7139516702e99..fde23413972b6 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -9,7 +9,7 @@ use rustc_middle::hir::place::{ Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind, }; use rustc_middle::middle::region; -use rustc_middle::mir::{self, BinOp, BorrowKind, UnOp}; +use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, UnOp}; use rustc_middle::thir::*; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCoercion, @@ -191,7 +191,7 @@ impl<'tcx> ThirBuildCx<'tcx> { let pointer_target = ExprKind::Field { lhs: self.thir.exprs.push(expr), variant_index: FIRST_VARIANT, - name: FieldIdx::from(0u32), + name: FieldIdx::ZERO, }; let arg = Expr { temp_lifetime, ty: pin_ty, span, kind: pointer_target }; let arg = self.thir.exprs.push(arg); @@ -226,7 +226,7 @@ impl<'tcx> ThirBuildCx<'tcx> { adt_def: self.tcx.adt_def(pin_did), variant_index: FIRST_VARIANT, args, - fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]), + fields: Box::new([FieldExpr { name: FieldIdx::ZERO, expr }]), user_ty: None, base: AdtExprBase::None, })); @@ -464,6 +464,10 @@ impl<'tcx> ThirBuildCx<'tcx> { } } + hir::ExprKind::Use(expr, span) => { + ExprKind::ByUse { expr: self.mirror_expr(expr), span } + } + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, arg) => { ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg: self.mirror_expr(arg) } } @@ -485,7 +489,7 @@ impl<'tcx> ThirBuildCx<'tcx> { self.overloaded_operator(expr, Box::new([lhs, rhs])) } else { ExprKind::AssignOp { - op: bin_op(op.node), + op: assign_op(op.node), lhs: self.mirror_expr(lhs), rhs: self.mirror_expr(rhs), } @@ -648,7 +652,7 @@ impl<'tcx> ThirBuildCx<'tcx> { } }, - hir::ExprKind::Closure { .. } => { + hir::ExprKind::Closure(hir::Closure { .. }) => { let closure_ty = self.typeck_results.expr_ty(expr); let (def_id, args, movability) = match *closure_ty.kind() { ty::Closure(def_id, args) => (def_id, UpvarArgs::Closure(args), None), @@ -1037,7 +1041,7 @@ impl<'tcx> ThirBuildCx<'tcx> { "Should have already errored about late bound consts: {def_id:?}" ); }; - let name = self.tcx.hir().name(hir_id); + let name = self.tcx.hir_name(hir_id); let param = ty::ParamConst::new(index, name); ExprKind::ConstParam { param, def_id } @@ -1248,6 +1252,17 @@ impl<'tcx> ThirBuildCx<'tcx> { match upvar_capture { ty::UpvarCapture::ByValue => captured_place_expr, + ty::UpvarCapture::ByUse => { + let span = captured_place_expr.span; + let expr_id = self.thir.exprs.push(captured_place_expr); + + Expr { + temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + ty: upvar_ty, + span: closure_expr.span, + kind: ExprKind::ByUse { expr: expr_id, span }, + } + } ty::UpvarCapture::ByRef(upvar_borrow) => { let borrow_kind = match upvar_borrow { ty::BorrowKind::Immutable => BorrowKind::Shared, @@ -1332,3 +1347,18 @@ fn bin_op(op: hir::BinOpKind) -> BinOp { _ => bug!("no equivalent for ast binop {:?}", op), } } + +fn assign_op(op: hir::AssignOpKind) -> AssignOp { + match op { + hir::AssignOpKind::AddAssign => AssignOp::AddAssign, + hir::AssignOpKind::SubAssign => AssignOp::SubAssign, + hir::AssignOpKind::MulAssign => AssignOp::MulAssign, + hir::AssignOpKind::DivAssign => AssignOp::DivAssign, + hir::AssignOpKind::RemAssign => AssignOp::RemAssign, + hir::AssignOpKind::BitXorAssign => AssignOp::BitXorAssign, + hir::AssignOpKind::BitAndAssign => AssignOp::BitAndAssign, + hir::AssignOpKind::BitOrAssign => AssignOp::BitOrAssign, + hir::AssignOpKind::ShlAssign => AssignOp::ShlAssign, + hir::AssignOpKind::ShrAssign => AssignOp::ShrAssign, + } +} diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 2e069cae4269f..8c817605847aa 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -27,8 +27,8 @@ pub(crate) fn thir_body( if let Some(reported) = cx.typeck_results.tainted_by_errors { return Err(reported); } - let expr = cx.mirror_expr(body.value); + // Lower the params before the body's expression so errors from params are shown first. let owner_id = tcx.local_def_id_to_hir_id(owner_def); if let Some(fn_decl) = tcx.hir_fn_decl_by_hir_id(owner_id) { let closure_env_param = cx.closure_env_param(owner_def, owner_id); @@ -48,6 +48,7 @@ pub(crate) fn thir_body( } } + let expr = cx.mirror_expr(body.value); Ok((tcx.alloc_steal_thir(cx.thir), expr)) } @@ -73,7 +74,6 @@ struct ThirBuildCx<'tcx> { impl<'tcx> ThirBuildCx<'tcx> { fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self { let typeck_results = tcx.typeck(def); - let hir = tcx.hir(); let hir_id = tcx.local_def_id_to_hir_id(def); let body_type = match tcx.hir_body_owner_kind(def) { @@ -111,10 +111,10 @@ impl<'tcx> ThirBuildCx<'tcx> { typeck_results, rvalue_scopes: &typeck_results.rvalue_scopes, body_owner: def.to_def_id(), - apply_adjustments: hir - .attrs(hir_id) + apply_adjustments: tcx + .hir_attrs(hir_id) .iter() - .all(|attr| attr.name_or_empty() != rustc_span::sym::custom_mir), + .all(|attr| !attr.has_name(rustc_span::sym::custom_mir)), } } @@ -207,7 +207,7 @@ impl<'tcx> ThirBuildCx<'tcx> { &self, hir_id: HirId, ) -> Option> { - crate::thir::util::user_args_applied_to_ty_of_hir_id(self.typeck_results, hir_id) + crate::thir::util::user_args_applied_to_ty_of_hir_id(self.tcx, self.typeck_results, hir_id) } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 954d0cf97abef..78583a402fe9a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -345,6 +345,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { | Borrow { .. } | Box { .. } | Call { .. } + | ByUse { .. } | Closure { .. } | ConstBlock { .. } | ConstParam { .. } @@ -675,7 +676,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { unpeeled_pat = subpattern; } - if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } = unpeeled_pat.kind + if let PatKind::ExpandedConstant { def_id, .. } = unpeeled_pat.kind && let DefKind::Const = self.tcx.def_kind(def_id) && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) // We filter out paths with multiple path::segments. @@ -1024,7 +1025,7 @@ fn find_fallback_pattern_typo<'tcx>( pat: &Pat<'tcx>, lint: &mut UnreachablePattern<'_>, ) { - if let (Level::Allow, _) = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id) { + if let Level::Allow = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id).level { // This is because we use `with_no_trimmed_paths` later, so if we never emit the lint we'd // ICE. At the same time, we don't really need to do all of this if we won't emit anything. return; @@ -1042,8 +1043,7 @@ fn find_fallback_pattern_typo<'tcx>( for item in cx.tcx.hir_crate_items(()).free_items() { if let DefKind::Use = cx.tcx.def_kind(item.owner_id) { // Look for consts being re-exported. - let item = cx.tcx.hir().expect_item(item.owner_id.def_id); - let use_name = item.ident.name; + let item = cx.tcx.hir_expect_item(item.owner_id.def_id); let hir::ItemKind::Use(path, _) = item.kind else { continue; }; @@ -1063,8 +1063,9 @@ fn find_fallback_pattern_typo<'tcx>( { // The const is accessible only through the re-export, point at // the `use`. - imported.push(use_name); - imported_spans.push(item.ident.span); + let ident = item.kind.ident().unwrap(); + imported.push(ident.name); + imported_spans.push(ident.span); } } } @@ -1173,7 +1174,7 @@ fn report_arm_reachability<'p, 'tcx>( for (arm, is_useful) in report.arm_usefulness.iter() { if let Usefulness::Redundant(explanation) = is_useful { let hir_id = arm.arm_data; - let arm_span = cx.tcx.hir().span(hir_id); + let arm_span = cx.tcx.hir_span(hir_id); let whole_arm_span = if is_match_arm { // If the arm is followed by a comma, extend the span to include it. let with_whitespace = sm.span_extend_while_whitespace(arm_span); @@ -1295,7 +1296,8 @@ fn report_non_exhaustive_match<'p, 'tcx>( for &arm in arms { let arm = &thir.arms[arm]; - if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } = arm.pattern.kind + if let PatKind::ExpandedConstant { def_id, .. } = arm.pattern.kind + && !matches!(cx.tcx.def_kind(def_id), DefKind::InlineConst) && let Ok(snippet) = cx.tcx.sess.source_map().span_to_snippet(arm.pattern.span) // We filter out paths with multiple path::segments. && snippet.chars().all(|c| c.is_alphanumeric() || c == '_') diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 667d59d858e3a..b7d203e3cd78c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -58,25 +58,13 @@ struct ConstToPat<'tcx> { span: Span, id: hir::HirId, - treat_byte_string_as_slice: bool, - c: ty::Const<'tcx>, } impl<'tcx> ConstToPat<'tcx> { fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self { trace!(?pat_ctxt.typeck_results.hir_owner); - ConstToPat { - tcx: pat_ctxt.tcx, - typing_env: pat_ctxt.typing_env, - span, - id, - treat_byte_string_as_slice: pat_ctxt - .typeck_results - .treat_byte_string_as_slice - .contains(&id.local_id), - c, - } + ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, id, c } } fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { @@ -108,8 +96,6 @@ impl<'tcx> ConstToPat<'tcx> { uv: ty::UnevaluatedConst<'tcx>, ty: Ty<'tcx>, ) -> Box> { - trace!(self.treat_byte_string_as_slice); - // It's not *technically* correct to be revealing opaque types here as borrowcheck has // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). @@ -196,7 +182,10 @@ impl<'tcx> ConstToPat<'tcx> { } } - inlined_const_as_pat + // Wrap the pattern in a marker node to indicate that it is the result of lowering a + // constant. This is used for diagnostics, and for unsafety checking of inline const blocks. + let kind = PatKind::ExpandedConstant { subpattern: inlined_const_as_pat, def_id: uv.def }; + Box::new(Pat { kind, ty, span: self.span }) } fn field_pats( @@ -291,6 +280,16 @@ impl<'tcx> ConstToPat<'tcx> { slice: None, suffix: Box::new([]), }, + ty::Str => { + // String literal patterns may have type `str` if `deref_patterns` is enabled, in + // order to allow `deref!("..."): String`. Since we need a `&str` for the comparison + // when lowering to MIR in `Builder::perform_test`, treat the constant as a `&str`. + // This works because `str` and `&str` have the same valtree representation. + let ref_str_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty); + PatKind::Constant { + value: mir::Const::Ty(ref_str_ty, ty::Const::new_value(tcx, cv, ref_str_ty)), + } + } ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() { // `&str` is represented as a valtree, let's keep using this // optimization for now. @@ -307,21 +306,8 @@ impl<'tcx> ConstToPat<'tcx> { ty, ); } else { - // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when - // matching against references, you can only use byte string literals. - // The typechecker has a special case for byte string literals, by treating them - // as slices. This means we turn `&[T; N]` constants into slice patterns, which - // has no negative effects on pattern matching, even if we're actually matching on - // arrays. - let pointee_ty = match *pointee_ty.kind() { - ty::Array(elem_ty, _) if self.treat_byte_string_as_slice => { - Ty::new_slice(tcx, elem_ty) - } - _ => *pointee_ty, - }; // References have the same valtree representation as their pointee. - let subpattern = self.valtree_to_pat(cv, pointee_ty); - PatKind::Deref { subpattern } + PatKind::Deref { subpattern: self.valtree_to_pat(cv, *pointee_ty) } } } }, diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs index bd7787b643d57..12c457f13fc12 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/migration.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs @@ -4,8 +4,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::MultiSpan; use rustc_hir::{BindingMode, ByRef, HirId, Mutability}; use rustc_lint as lint; -use rustc_middle::span_bug; -use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, Ty, TyCtxt}; +use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, TyCtxt}; use rustc_span::{Ident, Span}; use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg}; @@ -87,19 +86,18 @@ impl<'a> PatMigration<'a> { } /// Tracks when we're lowering a pattern that implicitly dereferences the scrutinee. - /// This should only be called when the pattern type adjustments list `adjustments` is - /// non-empty. Returns the prior default binding mode; this should be followed by a call to - /// [`PatMigration::leave_ref`] to restore it when we leave the pattern. + /// This should only be called when the pattern type adjustments list `adjustments` contains an + /// implicit deref of a reference type. Returns the prior default binding mode; this should be + /// followed by a call to [`PatMigration::leave_ref`] to restore it when we leave the pattern. pub(super) fn visit_implicit_derefs<'tcx>( &mut self, pat_span: Span, - adjustments: &[Ty<'tcx>], + adjustments: &[ty::adjustment::PatAdjustment<'tcx>], ) -> Option<(Span, Mutability)> { - let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| { - let &ty::Ref(_, _, mutbl) = ref_ty.kind() else { - span_bug!(pat_span, "pattern implicitly dereferences a non-ref type"); - }; - mutbl + // Implicitly dereferencing references changes the default binding mode, but implicit derefs + // of smart pointers do not. Thus, we only consider implicit derefs of reference types. + let implicit_deref_mutbls = adjustments.iter().filter_map(|adjust| { + if let &ty::Ref(_, _, mutbl) = adjust.source.kind() { Some(mutbl) } else { None } }); if !self.info.suggest_eliding_modes { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 8dc3f998e0916..fcd106d78e253 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -11,16 +11,18 @@ use rustc_abi::{FieldIdx, Integer}; use rustc_errors::codes::*; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::{self as hir, RangeEnd}; +use rustc_hir::{self as hir, LangItem, RangeEnd}; use rustc_index::Idx; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::thir::{ Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, }; +use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment}; use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypingMode}; use rustc_middle::{bug, span_bug}; -use rustc_span::def_id::LocalDefId; +use rustc_span::def_id::DefId; use rustc_span::{ErrorGuaranteed, Span}; use tracing::{debug, instrument}; @@ -62,13 +64,15 @@ pub(super) fn pat_from_hir<'a, 'tcx>( impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box> { - let adjustments: &[Ty<'tcx>] = + let adjustments: &[PatAdjustment<'tcx>] = self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); // Track the default binding mode for the Rust 2024 migration suggestion. + // Implicitly dereferencing references changes the default binding mode, but implicit deref + // patterns do not. Only track binding mode changes if a ref type is in the adjustments. let mut opt_old_mode_span = None; if let Some(s) = &mut self.rust_2024_migration - && !adjustments.is_empty() + && adjustments.iter().any(|adjust| adjust.kind == PatAdjust::BuiltinDeref) { opt_old_mode_span = s.visit_implicit_derefs(pat.span, adjustments); } @@ -101,17 +105,21 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { _ => self.lower_pattern_unadjusted(pat), }; - let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, ref_ty| { - debug!("{:?}: wrapping pattern with type {:?}", thir_pat, ref_ty); - Box::new(Pat { - span: thir_pat.span, - ty: *ref_ty, - kind: PatKind::Deref { subpattern: thir_pat }, - }) + let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, adjust| { + debug!("{:?}: wrapping pattern with adjustment {:?}", thir_pat, adjust); + let span = thir_pat.span; + let kind = match adjust.kind { + PatAdjust::BuiltinDeref => PatKind::Deref { subpattern: thir_pat }, + PatAdjust::OverloadedDeref => { + let borrow = self.typeck_results.deref_pat_borrow_mode(adjust.source, pat); + PatKind::DerefPattern { subpattern: thir_pat, borrow } + } + }; + Box::new(Pat { span, ty: adjust.source, kind }) }); if let Some(s) = &mut self.rust_2024_migration - && !adjustments.is_empty() + && adjustments.iter().any(|adjust| adjust.kind == PatAdjust::BuiltinDeref) { s.leave_ref(opt_old_mode_span); } @@ -124,13 +132,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { expr: Option<&'tcx hir::PatExpr<'tcx>>, // Out-parameters collecting extra data to be reapplied by the caller ascriptions: &mut Vec>, - inline_consts: &mut Vec, + expanded_consts: &mut Vec, ) -> Result>, ErrorGuaranteed> { let Some(expr) = expr else { return Ok(None) }; // Lower the endpoint into a temporary `PatKind` that will then be // deconstructed to obtain the constant value and other data. - let mut kind: PatKind<'tcx> = self.lower_pat_expr(expr); + let mut kind: PatKind<'tcx> = self.lower_pat_expr(expr, None); // Unpeel any ascription or inline-const wrapper nodes. loop { @@ -139,10 +147,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { ascriptions.push(ascription); kind = subpattern.kind; } - PatKind::ExpandedConstant { is_inline, def_id, subpattern } => { - if is_inline { - inline_consts.extend(def_id.as_local()); - } + PatKind::ExpandedConstant { def_id, subpattern } => { + expanded_consts.push(def_id); kind = subpattern.kind; } _ => break, @@ -221,10 +227,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // Collect extra data while lowering the endpoints, to be reapplied later. let mut ascriptions = vec![]; - let mut inline_consts = vec![]; + let mut expanded_consts = vec![]; let mut lower_endpoint = - |expr| self.lower_pattern_range_endpoint(expr, &mut ascriptions, &mut inline_consts); + |expr| self.lower_pattern_range_endpoint(expr, &mut ascriptions, &mut expanded_consts); let lo = lower_endpoint(lo_expr)?.unwrap_or(PatRangeBoundary::NegInfinity); let hi = lower_endpoint(hi_expr)?.unwrap_or(PatRangeBoundary::PosInfinity); @@ -269,17 +275,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated // constants somewhere. Have them on the range pattern. for ascription in ascriptions { - kind = PatKind::AscribeUserType { - ascription, - subpattern: Box::new(Pat { span, ty, kind }), - }; + let subpattern = Box::new(Pat { span, ty, kind }); + kind = PatKind::AscribeUserType { ascription, subpattern }; } - for def in inline_consts { - kind = PatKind::ExpandedConstant { - def_id: def.to_def_id(), - is_inline: true, - subpattern: Box::new(Pat { span, ty, kind }), - }; + for def_id in expanded_consts { + let subpattern = Box::new(Pat { span, ty, kind }); + kind = PatKind::ExpandedConstant { def_id, subpattern }; } Ok(kind) } @@ -290,11 +291,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let mut span = pat.span; let kind = match pat.kind { + hir::PatKind::Missing => PatKind::Missing, + hir::PatKind::Wild => PatKind::Wild, hir::PatKind::Never => PatKind::Never, - hir::PatKind::Expr(value) => self.lower_pat_expr(value), + hir::PatKind::Expr(value) => self.lower_pat_expr(value, Some(ty)), hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => { let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref()); @@ -303,9 +306,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } hir::PatKind::Deref(subpattern) => { - let mutable = self.typeck_results.pat_has_ref_mut_binding(subpattern); - let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; - PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), mutability } + let borrow = self.typeck_results.deref_pat_borrow_mode(ty, subpattern); + PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), borrow } } hir::PatKind::Ref(subpattern, _) => { // Track the default binding mode for the Rust 2024 migration suggestion. @@ -539,7 +541,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { &self, hir_id: hir::HirId, ) -> Option> { - crate::thir::util::user_args_applied_to_ty_of_hir_id(self.typeck_results, hir_id) + crate::thir::util::user_args_applied_to_ty_of_hir_id(self.tcx, self.typeck_results, hir_id) } /// Takes a HIR Path. If the path is a constant, evaluates it and feeds @@ -551,8 +553,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let res = self.typeck_results.qpath_res(qpath, id); let (def_id, user_ty) = match res { - Res::Def(DefKind::Const, def_id) => (def_id, None), - Res::Def(DefKind::AssocConst, def_id) => { + Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { (def_id, self.typeck_results.user_provided_types().get(id)) } @@ -566,16 +567,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // Lower the named constant to a THIR pattern. let args = self.typeck_results.node_args(id); + // FIXME(mgca): we will need to special case IACs here to have type system compatible + // generic args, instead of how we represent them in body expressions. let c = ty::Const::new_unevaluated(self.tcx, ty::UnevaluatedConst { def: def_id, args }); - let subpattern = self.const_to_pat(c, ty, id, span); - - // Wrap the pattern in a marker node to indicate that it is the result - // of lowering a named constant. This marker is used for improved - // diagnostics in some situations, but has no effect at runtime. - let mut pattern = { - let kind = PatKind::ExpandedConstant { subpattern, def_id, is_inline: false }; - Box::new(Pat { span, ty, kind }) - }; + let mut pattern = self.const_to_pat(c, ty, id, span); // If this is an associated constant with an explicit user-written // type, add an ascription node (e.g. ` as MyTrait>::CONST`). @@ -612,38 +607,83 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let ty = tcx.typeck(def_id).node_type(block.hir_id); let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()); - let parent_args = - tcx.erase_regions(ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id)); + let parent_args = ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id); let args = ty::InlineConstArgs::new(tcx, ty::InlineConstArgsParts { parent_args, ty }).args; - debug_assert!(!args.has_free_regions()); - let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), args }; - let subpattern = self.const_to_pat(ty::Const::new_unevaluated(self.tcx, ct), ty, id, span); - - // Wrap the pattern in a marker node to indicate that it is the result - // of lowering an inline const block. - PatKind::ExpandedConstant { subpattern, def_id: def_id.to_def_id(), is_inline: true } + let c = ty::Const::new_unevaluated(self.tcx, ct); + let pattern = self.const_to_pat(c, ty, id, span); + + // Apply a type ascription for the inline constant. + let annotation = { + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let args = ty::InlineConstArgs::new( + tcx, + ty::InlineConstArgsParts { parent_args, ty: infcx.next_ty_var(span) }, + ) + .args; + infcx.canonicalize_user_type_annotation(ty::UserType::new(ty::UserTypeKind::TypeOf( + def_id.to_def_id(), + ty::UserArgs { args, user_self_ty: None }, + ))) + }; + let annotation = + CanonicalUserTypeAnnotation { user_ty: Box::new(annotation), span, inferred_ty: ty }; + PatKind::AscribeUserType { + subpattern: pattern, + ascription: Ascription { + annotation, + // Note that we use `Contravariant` here. See the `variance` field documentation + // for details. + variance: ty::Contravariant, + }, + } } /// Lowers the kinds of "expression" that can appear in a HIR pattern: /// - Paths (e.g. `FOO`, `foo::BAR`, `Option::None`) /// - Inline const blocks (e.g. `const { 1 + 1 }`) /// - Literals, possibly negated (e.g. `-128u8`, `"hello"`) - fn lower_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) -> PatKind<'tcx> { - let (lit, neg) = match &expr.kind { - hir::PatExprKind::Path(qpath) => { - return self.lower_path(qpath, expr.hir_id, expr.span).kind; - } + fn lower_pat_expr( + &mut self, + expr: &'tcx hir::PatExpr<'tcx>, + pat_ty: Option>, + ) -> PatKind<'tcx> { + match &expr.kind { + hir::PatExprKind::Path(qpath) => self.lower_path(qpath, expr.hir_id, expr.span).kind, hir::PatExprKind::ConstBlock(anon_const) => { - return self.lower_inline_const(anon_const, expr.hir_id, expr.span); + self.lower_inline_const(anon_const, expr.hir_id, expr.span) } - hir::PatExprKind::Lit { lit, negated } => (lit, *negated), - }; - - let ct_ty = self.typeck_results.node_type(expr.hir_id); - let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg }; - let constant = self.tcx.at(expr.span).lit_to_const(lit_input); - self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind + hir::PatExprKind::Lit { lit, negated } => { + // We handle byte string literal patterns by using the pattern's type instead of the + // literal's type in `const_to_pat`: if the literal `b"..."` matches on a slice reference, + // the pattern's type will be `&[u8]` whereas the literal's type is `&[u8; 3]`; using the + // pattern's type means we'll properly translate it to a slice reference pattern. This works + // because slices and arrays have the same valtree representation. + // HACK: As an exception, use the literal's type if `pat_ty` is `String`; this can happen if + // `string_deref_patterns` is enabled. There's a special case for that when lowering to MIR. + // FIXME(deref_patterns): This hack won't be necessary once `string_deref_patterns` is + // superseded by a more general implementation of deref patterns. + let ct_ty = match pat_ty { + Some(pat_ty) + if let ty::Adt(def, _) = *pat_ty.kind() + && self.tcx.is_lang_item(def.did(), LangItem::String) => + { + if !self.tcx.features().string_deref_patterns() { + span_bug!( + expr.span, + "matching on `String` went through without enabling string_deref_patterns" + ); + } + self.typeck_results.node_type(expr.hir_id) + } + Some(pat_ty) => pat_ty, + None => self.typeck_results.node_type(expr.hir_id), + }; + let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg: *negated }; + let constant = self.tcx.at(expr.span).lit_to_const(lit_input); + self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind + } + } } } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index cd56d93afcf75..db9547a481fb4 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -8,10 +8,10 @@ use rustc_span::def_id::LocalDefId; /// Create a THIR tree for debugging. pub fn thir_tree(tcx: TyCtxt<'_>, owner_def: LocalDefId) -> String { match super::cx::thir_body(tcx, owner_def) { - Ok((thir, _)) => { + Ok((thir, expr)) => { let thir = thir.steal(); let mut printer = ThirPrinter::new(&thir); - printer.print(); + printer.print(expr); printer.into_buffer() } Err(_) => "error".into(), @@ -58,7 +58,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { } } - fn print(&mut self) { + fn print(&mut self, body_expr: ExprId) { print_indented!(self, "params: [", 0); for param in self.thir.params.iter() { self.print_param(param, 1); @@ -66,8 +66,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "]", 0); print_indented!(self, "body:", 0); - let expr = ExprId::from_usize(self.thir.exprs.len() - 1); - self.print_expr(expr, 1); + self.print_expr(body_expr, 1); } fn into_buffer(self) -> String { @@ -246,6 +245,13 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "}", depth_lvl); } + ByUse { expr, span } => { + print_indented!(self, "ByUse {", depth_lvl); + print_indented!(self, "expr:", depth_lvl + 1); + self.print_expr(*expr, depth_lvl + 2); + print_indented!(self, format!("span: {:?}", span), depth_lvl + 1); + print_indented!(self, "}", depth_lvl); + } Deref { arg } => { print_indented!(self, "Deref {", depth_lvl); self.print_expr(*arg, depth_lvl + 1); @@ -657,6 +663,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "kind: PatKind {", depth_lvl); match pat_kind { + PatKind::Missing => unreachable!(), PatKind::Wild => { print_indented!(self, "Wild", depth_lvl + 1); } @@ -733,10 +740,9 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, format!("value: {:?}", value), depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } - PatKind::ExpandedConstant { def_id, is_inline, subpattern } => { + PatKind::ExpandedConstant { def_id, subpattern } => { print_indented!(self, "ExpandedConstant {", depth_lvl + 1); print_indented!(self, format!("def_id: {def_id:?}"), depth_lvl + 2); - print_indented!(self, format!("is_inline: {is_inline:?}"), depth_lvl + 2); print_indented!(self, "subpattern:", depth_lvl + 2); self.print_pat(subpattern, depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs index 60a47a94e3a1f..457957f5fce95 100644 --- a/compiler/rustc_mir_build/src/thir/util.rs +++ b/compiler/rustc_mir_build/src/thir/util.rs @@ -1,12 +1,16 @@ +use std::assert_matches::assert_matches; + use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_middle::bug; -use rustc_middle::ty::{self, CanonicalUserType}; +use rustc_middle::ty::{self, CanonicalUserType, TyCtxt}; use tracing::debug; /// Looks up the type associated with this hir-id and applies the /// user-given generic parameters; the hir-id must map to a suitable /// type. pub(crate) fn user_args_applied_to_ty_of_hir_id<'tcx>( + tcx: TyCtxt<'tcx>, typeck_results: &ty::TypeckResults<'tcx>, hir_id: hir::HirId, ) -> Option> { @@ -16,7 +20,23 @@ pub(crate) fn user_args_applied_to_ty_of_hir_id<'tcx>( let ty = typeck_results.node_type(hir_id); match ty.kind() { ty::Adt(adt_def, ..) => { + // This "fixes" user type annotations for tupled ctor patterns for ADTs. + // That's because `type_of(ctor_did)` returns a FnDef, but we actually + // want to be annotating the type of the ADT itself. It's a bit goofy, + // but it's easier to adjust this here rather than in the path lowering + // code for patterns in HIR. if let ty::UserTypeKind::TypeOf(did, _) = &mut user_ty.value.kind { + // This is either already set up correctly (struct, union, enum, or variant), + // or needs adjusting (ctor). Make sure we don't start adjusting other + // user annotations like consts or fn calls. + assert_matches!( + tcx.def_kind(*did), + DefKind::Ctor(..) + | DefKind::Struct + | DefKind::Enum + | DefKind::Union + | DefKind::Variant + ); *did = adt_def.did(); } Some(user_ty) diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index c46ae9775cf68..3f6e7a0661921 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -1,5 +1,6 @@ //! Random access inspection of the results of a dataflow analysis. +use std::borrow::Cow; use std::cmp::Ordering; use std::ops::{Deref, DerefMut}; @@ -9,38 +10,30 @@ use rustc_middle::mir::{self, BasicBlock, Location}; use super::{Analysis, Direction, Effect, EffectIndex, Results}; -/// Some `ResultsCursor`s want to own a `Results`, and some want to borrow a `Results`, either -/// mutable or immutably. This type allows all of the above. It's similar to `Cow`. -pub enum ResultsHandle<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - BorrowedMut(&'a mut Results<'tcx, A>), - Owned(Results<'tcx, A>), +/// Some `ResultsCursor`s want to own an `Analysis`, and some want to borrow an `Analysis`, either +/// mutable or immutably. This type allows all of the above. It's similar to `Cow`, but `Cow` +/// doesn't allow mutable borrowing. +enum CowMut<'a, T> { + BorrowedMut(&'a mut T), + Owned(T), } -impl<'tcx, A> Deref for ResultsHandle<'_, 'tcx, A> -where - A: Analysis<'tcx>, -{ - type Target = Results<'tcx, A>; +impl Deref for CowMut<'_, T> { + type Target = T; - fn deref(&self) -> &Results<'tcx, A> { + fn deref(&self) -> &T { match self { - ResultsHandle::BorrowedMut(borrowed) => borrowed, - ResultsHandle::Owned(owned) => owned, + CowMut::BorrowedMut(borrowed) => borrowed, + CowMut::Owned(owned) => owned, } } } -impl<'tcx, A> DerefMut for ResultsHandle<'_, 'tcx, A> -where - A: Analysis<'tcx>, -{ - fn deref_mut(&mut self) -> &mut Results<'tcx, A> { +impl DerefMut for CowMut<'_, T> { + fn deref_mut(&mut self) -> &mut T { match self { - ResultsHandle::BorrowedMut(borrowed) => borrowed, - ResultsHandle::Owned(owned) => owned, + CowMut::BorrowedMut(borrowed) => borrowed, + CowMut::Owned(owned) => owned, } } } @@ -60,7 +53,8 @@ where A: Analysis<'tcx>, { body: &'mir mir::Body<'tcx>, - results: ResultsHandle<'mir, 'tcx, A>, + analysis: CowMut<'mir, A>, + results: Cow<'mir, Results>, state: A::Domain, pos: CursorPosition, @@ -88,11 +82,15 @@ where self.body } - /// Returns a new cursor that can inspect `results`. - pub fn new(body: &'mir mir::Body<'tcx>, results: ResultsHandle<'mir, 'tcx, A>) -> Self { - let bottom_value = results.analysis.bottom_value(body); + fn new( + body: &'mir mir::Body<'tcx>, + analysis: CowMut<'mir, A>, + results: Cow<'mir, Results>, + ) -> Self { + let bottom_value = analysis.bottom_value(body); ResultsCursor { body, + analysis, results, // Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that @@ -107,6 +105,24 @@ where } } + /// Returns a new cursor that takes ownership of and inspects analysis results. + pub fn new_owning( + body: &'mir mir::Body<'tcx>, + analysis: A, + results: Results, + ) -> Self { + Self::new(body, CowMut::Owned(analysis), Cow::Owned(results)) + } + + /// Returns a new cursor that borrows and inspects analysis results. + pub fn new_borrowing( + body: &'mir mir::Body<'tcx>, + analysis: &'mir mut A, + results: &'mir Results, + ) -> Self { + Self::new(body, CowMut::BorrowedMut(analysis), Cow::Borrowed(results)) + } + /// Allows inspection of unreachable basic blocks even with `debug_assertions` enabled. #[cfg(test)] pub(crate) fn allow_unreachable(&mut self) { @@ -114,24 +130,9 @@ where self.reachable_blocks.insert_all() } - /// Returns the underlying `Results`. - pub fn results(&self) -> &Results<'tcx, A> { - &self.results - } - - /// Returns the underlying `Results`. - pub fn mut_results(&mut self) -> &mut Results<'tcx, A> { - &mut self.results - } - /// Returns the `Analysis` used to generate the underlying `Results`. pub fn analysis(&self) -> &A { - &self.results.analysis - } - - /// Returns the `Analysis` used to generate the underlying `Results`. - pub fn mut_analysis(&mut self) -> &mut A { - &mut self.results.analysis + &self.analysis } /// Resets the cursor to hold the entry set for the given basic block. @@ -143,7 +144,7 @@ where #[cfg(debug_assertions)] assert!(self.reachable_blocks.contains(block)); - self.state.clone_from(self.results.entry_set_for_block(block)); + self.state.clone_from(&self.results[block]); self.pos = CursorPosition::block_entry(block); self.state_needs_reset = false; } @@ -235,7 +236,7 @@ where let target_effect_index = effect.at_index(target.statement_index); A::Direction::apply_effects_in_range( - &mut self.results.analysis, + &mut *self.analysis, &mut self.state, target.block, block_data, @@ -251,7 +252,7 @@ where /// 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(&mut A, &mut A::Domain)) { - f(&mut self.results.analysis, &mut self.state); + f(&mut self.analysis, &mut self.state); self.state_needs_reset = true; } } diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 3d7f9e2d8e71b..e955e38ad10fa 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -5,7 +5,7 @@ use rustc_middle::mir::{ }; use super::visitor::ResultsVisitor; -use super::{Analysis, Effect, EffectIndex, Results}; +use super::{Analysis, Effect, EffectIndex}; pub trait Direction { const IS_FORWARD: bool; @@ -36,14 +36,14 @@ pub trait Direction { A: Analysis<'tcx>; /// Called by `ResultsVisitor` to recompute the analysis domain values for - /// all locations in a basic block (starting from the entry value stored - /// in `Results`) and to visit them with `vis`. + /// all locations in a basic block (starting from `entry_state` and to + /// visit them with `vis`. fn visit_results_in_block<'mir, 'tcx, A>( state: &mut A::Domain, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - results: &mut Results<'tcx, A>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, + analysis: &mut A, + vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>; } @@ -211,28 +211,26 @@ impl Direction for Backward { state: &mut A::Domain, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - results: &mut Results<'tcx, A>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, + analysis: &mut A, + vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>, { - state.clone_from(results.entry_set_for_block(block)); - vis.visit_block_end(state); let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); - results.analysis.apply_early_terminator_effect(state, term, loc); - vis.visit_after_early_terminator_effect(results, state, term, loc); - results.analysis.apply_primary_terminator_effect(state, term, loc); - vis.visit_after_primary_terminator_effect(results, state, term, loc); + analysis.apply_early_terminator_effect(state, term, loc); + vis.visit_after_early_terminator_effect(analysis, state, term, loc); + analysis.apply_primary_terminator_effect(state, term, loc); + vis.visit_after_primary_terminator_effect(analysis, state, term, loc); for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() { let loc = Location { block, statement_index }; - results.analysis.apply_early_statement_effect(state, stmt, loc); - vis.visit_after_early_statement_effect(results, state, stmt, loc); - results.analysis.apply_primary_statement_effect(state, stmt, loc); - vis.visit_after_primary_statement_effect(results, state, stmt, loc); + analysis.apply_early_statement_effect(state, stmt, loc); + vis.visit_after_early_statement_effect(analysis, state, stmt, loc); + analysis.apply_primary_statement_effect(state, stmt, loc); + vis.visit_after_primary_statement_effect(analysis, state, stmt, loc); } vis.visit_block_start(state); @@ -393,29 +391,27 @@ impl Direction for Forward { state: &mut A::Domain, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - results: &mut Results<'tcx, A>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, + analysis: &mut A, + vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>, { - state.clone_from(results.entry_set_for_block(block)); - vis.visit_block_start(state); for (statement_index, stmt) in block_data.statements.iter().enumerate() { let loc = Location { block, statement_index }; - results.analysis.apply_early_statement_effect(state, stmt, loc); - vis.visit_after_early_statement_effect(results, state, stmt, loc); - results.analysis.apply_primary_statement_effect(state, stmt, loc); - vis.visit_after_primary_statement_effect(results, state, stmt, loc); + analysis.apply_early_statement_effect(state, stmt, loc); + vis.visit_after_early_statement_effect(analysis, state, stmt, loc); + analysis.apply_primary_statement_effect(state, stmt, loc); + vis.visit_after_primary_statement_effect(analysis, state, stmt, loc); } let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); - results.analysis.apply_early_terminator_effect(state, term, loc); - vis.visit_after_early_terminator_effect(results, state, term, loc); - results.analysis.apply_primary_terminator_effect(state, term, loc); - vis.visit_after_primary_terminator_effect(results, state, term, loc); + analysis.apply_early_terminator_effect(state, term, loc); + vis.visit_after_early_terminator_effect(analysis, state, term, loc); + analysis.apply_primary_terminator_effect(state, term, loc); + vis.visit_after_primary_terminator_effect(analysis, state, term, loc); vis.visit_block_end(state); } diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 448fad2dc3ece..a7d5422a3d721 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -21,7 +21,9 @@ use tracing::debug; use {rustc_ast as ast, rustc_graphviz as dot}; use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; -use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor}; +use super::{ + Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor, visit_results, +}; use crate::errors::{ DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter, }; @@ -32,7 +34,8 @@ use crate::errors::{ pub(super) fn write_graphviz_results<'tcx, A>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - results: &mut Results<'tcx, A>, + analysis: &mut A, + results: &Results, pass_name: Option<&'static str>, ) -> std::io::Result<()> where @@ -77,7 +80,7 @@ where let mut buf = Vec::new(); - let graphviz = Formatter::new(body, results, style); + let graphviz = Formatter::new(body, analysis, results, style); let mut render_opts = vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())]; if tcx.sess.opts.unstable_opts.graphviz_dark_mode { @@ -109,27 +112,29 @@ impl RustcMirAttrs { .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.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() { - Some(_) => Ok(path), - None => { - tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() }); + let attr_result = match attr.name() { + Some(name @ sym::borrowck_graphviz_postflow) => { + Self::set_field(&mut ret.basename_and_suffix, tcx, name, &attr, |s| { + let path = PathBuf::from(s.to_string()); + match path.file_name() { + Some(_) => Ok(path), + None => { + tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() }); + Err(()) + } + } + }) + } + Some(name @ sym::borrowck_graphviz_format) => { + Self::set_field(&mut ret.formatter, tcx, name, &attr, |s| match s { + sym::two_phase => Ok(s), + _ => { + tcx.dcx().emit_err(UnknownFormatter { span: attr.span() }); Err(()) } - } - }) - } 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), - _ => { - tcx.dcx().emit_err(UnknownFormatter { span: attr.span() }); - Err(()) - } - }) - } else { - Ok(()) + }) + } + _ => Ok(()), }; result = result.and(attr_result); @@ -141,12 +146,12 @@ impl RustcMirAttrs { fn set_field( field: &mut Option, tcx: TyCtxt<'_>, + name: Symbol, attr: &ast::MetaItemInner, mapper: impl FnOnce(Symbol) -> Result, ) -> Result<(), ()> { if field.is_some() { - tcx.dcx() - .emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() }); + tcx.dcx().emit_err(DuplicateValuesFor { span: attr.span(), name }); return Err(()); } @@ -156,7 +161,7 @@ impl RustcMirAttrs { Ok(()) } else { tcx.dcx() - .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() }); + .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name().unwrap() }); Err(()) } } @@ -199,11 +204,13 @@ struct Formatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { + body: &'mir Body<'tcx>, // The `RefCell` is used because `::node_label` - // takes `&self`, but it needs to modify the cursor. This is also the + // takes `&self`, but it needs to modify the analysis. This is also the // reason for the `Formatter`/`BlockFormatter` split; `BlockFormatter` has // the operations that involve the mutation, i.e. within the `borrow_mut`. - cursor: RefCell>, + analysis: RefCell<&'mir mut A>, + results: &'mir Results, style: OutputStyle, reachable: DenseBitSet, } @@ -214,15 +221,12 @@ where { fn new( body: &'mir Body<'tcx>, - results: &'mir mut Results<'tcx, A>, + analysis: &'mir mut A, + results: &'mir Results, style: OutputStyle, ) -> Self { let reachable = traversal::reachable_as_bitset(body); - Formatter { cursor: results.as_results_cursor(body).into(), style, reachable } - } - - fn body(&self) -> &'mir Body<'tcx> { - self.cursor.borrow().body() + Formatter { body, analysis: analysis.into(), results, style, reachable } } } @@ -251,7 +255,7 @@ where type Edge = CfgEdge; fn graph_id(&self) -> dot::Id<'_> { - let name = graphviz_safe_def_name(self.body().source.def_id()); + let name = graphviz_safe_def_name(self.body.source.def_id()); dot::Id::new(format!("graph_for_def_id_{name}")).unwrap() } @@ -260,10 +264,16 @@ where } fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { - let mut cursor = self.cursor.borrow_mut(); - let mut fmt = - BlockFormatter { cursor: &mut cursor, style: self.style, bg: Background::Light }; - let label = fmt.write_node_label(*block).unwrap(); + let analysis = &mut **self.analysis.borrow_mut(); + + let diffs = StateDiffCollector::run(self.body, *block, analysis, self.results, self.style); + + let mut fmt = BlockFormatter { + cursor: ResultsCursor::new_borrowing(self.body, analysis, self.results), + style: self.style, + bg: Background::Light, + }; + let label = fmt.write_node_label(*block, diffs).unwrap(); dot::LabelText::html(String::from_utf8(label).unwrap()) } @@ -273,7 +283,7 @@ where } fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> { - let label = &self.body()[e.source].terminator().kind.fmt_successor_labels()[e.index]; + let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index]; dot::LabelText::label(label.clone()) } } @@ -286,7 +296,7 @@ where type Edge = CfgEdge; fn nodes(&self) -> dot::Nodes<'_, Self::Node> { - self.body() + self.body .basic_blocks .indices() .filter(|&idx| self.reachable.contains(idx)) @@ -295,10 +305,10 @@ where } fn edges(&self) -> dot::Edges<'_, Self::Edge> { - let body = self.body(); - body.basic_blocks + self.body + .basic_blocks .indices() - .flat_map(|bb| dataflow_successors(body, bb)) + .flat_map(|bb| dataflow_successors(self.body, bb)) .collect::>() .into() } @@ -308,20 +318,20 @@ where } fn target(&self, edge: &Self::Edge) -> Self::Node { - self.body()[edge.source].terminator().successors().nth(edge.index).unwrap() + self.body[edge.source].terminator().successors().nth(edge.index).unwrap() } } -struct BlockFormatter<'a, 'mir, 'tcx, A> +struct BlockFormatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { - cursor: &'a mut ResultsCursor<'mir, 'tcx, A>, + cursor: ResultsCursor<'mir, 'tcx, A>, bg: Background, style: OutputStyle, } -impl<'tcx, A> BlockFormatter<'_, '_, 'tcx, A> +impl<'tcx, A> BlockFormatter<'_, 'tcx, A> where A: Analysis<'tcx>, A::Domain: DebugWithContext, @@ -334,7 +344,11 @@ where bg } - fn write_node_label(&mut self, block: BasicBlock) -> io::Result> { + fn write_node_label( + &mut self, + block: BasicBlock, + diffs: StateDiffCollector, + ) -> io::Result> { use std::io::Write; // Sample output: @@ -390,7 +404,7 @@ where self.write_row_with_full_state(w, "", "(on start)")?; // D + E: Statement and terminator transfer functions - self.write_statements_and_terminator(w, block)?; + self.write_statements_and_terminator(w, block, diffs)?; // F: State at end of block @@ -573,14 +587,8 @@ where &mut self, w: &mut impl io::Write, block: BasicBlock, + diffs: StateDiffCollector, ) -> io::Result<()> { - let diffs = StateDiffCollector::run( - self.cursor.body(), - block, - self.cursor.mut_results(), - self.style, - ); - let mut diffs_before = diffs.before.map(|v| v.into_iter()); let mut diffs_after = diffs.after.into_iter(); @@ -689,7 +697,8 @@ impl StateDiffCollector { fn run<'tcx, A>( body: &Body<'tcx>, block: BasicBlock, - results: &mut Results<'tcx, A>, + analysis: &mut A, + results: &Results, style: OutputStyle, ) -> Self where @@ -697,17 +706,17 @@ impl StateDiffCollector { D: DebugWithContext, { let mut collector = StateDiffCollector { - prev_state: results.analysis.bottom_value(body), + prev_state: analysis.bottom_value(body), after: vec![], before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]), }; - results.visit_with(body, std::iter::once(block), &mut collector); + visit_results(body, std::iter::once(block), analysis, results, &mut collector); collector } } -impl<'tcx, A> ResultsVisitor<'_, 'tcx, A> for StateDiffCollector +impl<'tcx, A> ResultsVisitor<'tcx, A> for StateDiffCollector where A: Analysis<'tcx>, A::Domain: DebugWithContext, @@ -726,49 +735,49 @@ where fn visit_after_early_statement_effect( &mut self, - results: &mut Results<'tcx, A>, + analysis: &mut A, state: &A::Domain, _statement: &mir::Statement<'tcx>, _location: Location, ) { if let Some(before) = self.before.as_mut() { - before.push(diff_pretty(state, &self.prev_state, &results.analysis)); + before.push(diff_pretty(state, &self.prev_state, analysis)); self.prev_state.clone_from(state) } } fn visit_after_primary_statement_effect( &mut self, - results: &mut Results<'tcx, A>, + analysis: &mut A, state: &A::Domain, _statement: &mir::Statement<'tcx>, _location: Location, ) { - self.after.push(diff_pretty(state, &self.prev_state, &results.analysis)); + self.after.push(diff_pretty(state, &self.prev_state, analysis)); self.prev_state.clone_from(state) } fn visit_after_early_terminator_effect( &mut self, - results: &mut Results<'tcx, A>, + analysis: &mut A, state: &A::Domain, _terminator: &mir::Terminator<'tcx>, _location: Location, ) { if let Some(before) = self.before.as_mut() { - before.push(diff_pretty(state, &self.prev_state, &results.analysis)); + before.push(diff_pretty(state, &self.prev_state, analysis)); self.prev_state.clone_from(state) } } fn visit_after_primary_terminator_effect( &mut self, - results: &mut Results<'tcx, A>, + analysis: &mut A, state: &A::Domain, _terminator: &mir::Terminator<'tcx>, _location: Location, ) { - self.after.push(diff_pretty(state, &self.prev_state, &results.analysis)); + self.after.push(diff_pretty(state, &self.prev_state, analysis)); self.prev_state.clone_from(state) } } diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 09f6cdb5c4a72..9cadec100b534 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -58,8 +58,9 @@ mod visitor; pub use self::cursor::ResultsCursor; pub use self::direction::{Backward, Direction, Forward}; pub use self::lattice::{JoinSemiLattice, MaybeReachable}; -pub use self::results::{EntryStates, Results}; -pub use self::visitor::{ResultsVisitor, visit_results}; +pub(crate) use self::results::AnalysisAndResults; +pub use self::results::Results; +pub use self::visitor::{ResultsVisitor, visit_reachable_results, visit_results}; /// Analysis domains are all bitsets of various kinds. This trait holds /// operations needed by all of them. @@ -247,17 +248,15 @@ pub trait Analysis<'tcx> { tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>, pass_name: Option<&'static str>, - ) -> Results<'tcx, Self> + ) -> AnalysisAndResults<'tcx, Self> where Self: Sized, Self::Domain: DebugWithContext, { - let mut entry_states = - IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len()); - self.initialize_start_block(body, &mut entry_states[mir::START_BLOCK]); + let mut results = IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len()); + self.initialize_start_block(body, &mut results[mir::START_BLOCK]); - if Self::Direction::IS_BACKWARD && entry_states[mir::START_BLOCK] != self.bottom_value(body) - { + if Self::Direction::IS_BACKWARD && results[mir::START_BLOCK] != self.bottom_value(body) { bug!("`initialize_start_block` is not yet supported for backward dataflow analyses"); } @@ -280,10 +279,9 @@ pub trait Analysis<'tcx> { // every iteration. let mut state = self.bottom_value(body); while let Some(bb) = dirty_queue.pop() { - // Set the state to the entry state of the block. - // This is equivalent to `state = entry_states[bb].clone()`, - // but it saves an allocation, thus improving compile times. - state.clone_from(&entry_states[bb]); + // Set the state to the entry state of the block. This is equivalent to `state = + // results[bb].clone()`, but it saves an allocation, thus improving compile times. + state.clone_from(&results[bb]); Self::Direction::apply_effects_in_block( &mut self, @@ -292,7 +290,7 @@ pub trait Analysis<'tcx> { bb, &body[bb], |target: BasicBlock, state: &Self::Domain| { - let set_changed = entry_states[target].join(state); + let set_changed = results[target].join(state); if set_changed { dirty_queue.insert(target); } @@ -300,16 +298,14 @@ pub trait Analysis<'tcx> { ); } - let mut results = Results { analysis: self, entry_states }; - if tcx.sess.opts.unstable_opts.dump_mir_dataflow { - let res = write_graphviz_results(tcx, body, &mut results, pass_name); + let res = write_graphviz_results(tcx, body, &mut self, &results, pass_name); if let Err(e) = res { error!("Failed to write graphviz dataflow results: {}", e); } } - results + AnalysisAndResults { analysis: self, results } } } diff --git a/compiler/rustc_mir_dataflow/src/framework/results.rs b/compiler/rustc_mir_dataflow/src/framework/results.rs index 8e2c3afddb352..7b7e981d3a554 100644 --- a/compiler/rustc_mir_dataflow/src/framework/results.rs +++ b/compiler/rustc_mir_dataflow/src/framework/results.rs @@ -1,63 +1,30 @@ //! Dataflow analysis results. use rustc_index::IndexVec; -use rustc_middle::mir::{BasicBlock, Body, traversal}; +use rustc_middle::mir::{BasicBlock, Body}; -use super::{Analysis, ResultsCursor, ResultsVisitor, visit_results}; -use crate::framework::cursor::ResultsHandle; +use super::{Analysis, ResultsCursor}; -pub type EntryStates<'tcx, A> = IndexVec>::Domain>; +/// The results of a dataflow analysis that has converged to fixpoint. It only holds the domain +/// values at the entry of each basic block. Domain values in other parts of the block are +/// recomputed on the fly by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls). +pub type Results = IndexVec; -/// A dataflow analysis that has converged to fixpoint. It only holds the domain values at the -/// entry of each basic block. Domain values in other parts of the block are recomputed on the fly -/// by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls). -#[derive(Clone)] -pub struct Results<'tcx, A> +/// Utility type used in a few places where it's convenient to bundle an analysis with its results. +pub struct AnalysisAndResults<'tcx, A> where A: Analysis<'tcx>, { pub analysis: A, - pub entry_states: EntryStates<'tcx, A>, + pub results: Results, } -impl<'tcx, A> Results<'tcx, A> +impl<'tcx, A> AnalysisAndResults<'tcx, A> where A: Analysis<'tcx>, { - /// Creates a `ResultsCursor` that mutably borrows the `Results`, which is appropriate when the - /// `Results` is also used outside the cursor. - pub fn as_results_cursor<'mir>( - &'mir mut self, - body: &'mir Body<'tcx>, - ) -> ResultsCursor<'mir, 'tcx, A> { - ResultsCursor::new(body, ResultsHandle::BorrowedMut(self)) - } - - /// Creates a `ResultsCursor` that takes ownership of the `Results`. + /// Creates a `ResultsCursor` that takes ownership of `self`. pub fn into_results_cursor<'mir>(self, body: &'mir Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> { - ResultsCursor::new(body, ResultsHandle::Owned(self)) - } - - /// Gets the dataflow state for the given block. - pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain { - &self.entry_states[block] - } - - pub fn visit_with<'mir>( - &mut self, - body: &'mir Body<'tcx>, - blocks: impl IntoIterator, - vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, - ) { - visit_results(body, blocks, self, vis) - } - - pub fn visit_reachable_with<'mir>( - &mut self, - body: &'mir Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, - ) { - let blocks = traversal::reachable(body); - visit_results(body, blocks.map(|(bb, _)| bb), self, vis) + ResultsCursor::new_owning(body, self.analysis, self.results) } } diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index ae0f1179e6fac..8602bb5576523 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -79,7 +79,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> { /// /// 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_states`, not from actually running `MockAnalysis` to fixpoint. +/// `mock_results`, not from actually running `MockAnalysis` to fixpoint. struct MockAnalysis<'tcx, D> { body: &'tcx mir::Body<'tcx>, dir: PhantomData, @@ -96,7 +96,7 @@ impl MockAnalysis<'_, D> { ret } - fn mock_entry_states(&self) -> IndexVec> { + fn mock_results(&self) -> IndexVec> { let empty = self.bottom_value(self.body); let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks); @@ -255,7 +255,7 @@ fn test_cursor(analysis: MockAnalysis<'_, D>) { let body = analysis.body; let mut cursor = - Results { entry_states: analysis.mock_entry_states(), analysis }.into_results_cursor(body); + AnalysisAndResults { results: analysis.mock_results(), analysis }.into_results_cursor(body); cursor.allow_unreachable(); diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index a03aecee7be12..fbb9e4108726d 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -1,4 +1,4 @@ -use rustc_middle::mir::{self, BasicBlock, Location}; +use rustc_middle::mir::{self, BasicBlock, Location, traversal}; use super::{Analysis, Direction, Results}; @@ -7,12 +7,13 @@ use super::{Analysis, Direction, Results}; pub fn visit_results<'mir, 'tcx, A>( body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator, - results: &mut Results<'tcx, A>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, + analysis: &mut A, + results: &Results, + vis: &mut impl ResultsVisitor<'tcx, A>, ) where A: Analysis<'tcx>, { - let mut state = results.analysis.bottom_value(body); + let mut state = analysis.bottom_value(body); #[cfg(debug_assertions)] let reachable_blocks = mir::traversal::reachable_as_bitset(body); @@ -22,14 +23,28 @@ pub fn visit_results<'mir, 'tcx, A>( assert!(reachable_blocks.contains(block)); let block_data = &body[block]; - A::Direction::visit_results_in_block(&mut state, block, block_data, results, vis); + state.clone_from(&results[block]); + A::Direction::visit_results_in_block(&mut state, block, block_data, analysis, vis); } } +/// Like `visit_results`, but only for reachable blocks. +pub fn visit_reachable_results<'mir, 'tcx, A>( + body: &'mir mir::Body<'tcx>, + analysis: &mut A, + results: &Results, + vis: &mut impl ResultsVisitor<'tcx, A>, +) where + A: Analysis<'tcx>, +{ + let blocks = traversal::reachable(body).map(|(bb, _)| bb); + visit_results(body, blocks, analysis, results, vis) +} + /// A visitor over the results of an `Analysis`. Use this when you want to inspect domain values in /// many or all locations; use `ResultsCursor` if you want to inspect domain values only in certain /// locations. -pub trait ResultsVisitor<'mir, 'tcx, A> +pub trait ResultsVisitor<'tcx, A> where A: Analysis<'tcx>, { @@ -38,9 +53,9 @@ where /// Called after the "early" effect of the given statement is applied to `state`. fn visit_after_early_statement_effect( &mut self, - _results: &mut Results<'tcx, A>, + _analysis: &mut A, _state: &A::Domain, - _statement: &'mir mir::Statement<'tcx>, + _statement: &mir::Statement<'tcx>, _location: Location, ) { } @@ -48,9 +63,9 @@ where /// Called after the "primary" effect of the given statement is applied to `state`. fn visit_after_primary_statement_effect( &mut self, - _results: &mut Results<'tcx, A>, + _analysis: &mut A, _state: &A::Domain, - _statement: &'mir mir::Statement<'tcx>, + _statement: &mir::Statement<'tcx>, _location: Location, ) { } @@ -58,9 +73,9 @@ where /// Called after the "early" effect of the given terminator is applied to `state`. fn visit_after_early_terminator_effect( &mut self, - _results: &mut Results<'tcx, A>, + _analysis: &mut A, _state: &A::Domain, - _terminator: &'mir mir::Terminator<'tcx>, + _terminator: &mir::Terminator<'tcx>, _location: Location, ) { } @@ -70,9 +85,9 @@ where /// The `call_return_effect` (if one exists) will *not* be applied to `state`. fn visit_after_primary_terminator_effect( &mut self, - _results: &mut Results<'tcx, A>, + _analysis: &mut A, _state: &A::Domain, - _terminator: &'mir mir::Terminator<'tcx>, + _terminator: &mir::Terminator<'tcx>, _location: Location, ) { } diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index f5ffc42d52ab0..18165b0b9bd08 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -376,7 +376,14 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { // the result of `is_unwind_dead`. let mut edges = terminator.edges(); if self.skip_unreachable_unwind - && let mir::TerminatorKind::Drop { target, unwind, place, replace: _ } = terminator.kind + && let mir::TerminatorKind::Drop { + target, + unwind, + place, + replace: _, + drop: _, + async_fut: _, + } = terminator.kind && matches!(unwind, mir::UnwindAction::Cleanup(_)) && self.is_unwind_dead(place, state) { diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index a8a56baa1ffc0..2e8c916544160 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -4,10 +4,8 @@ #![feature(box_patterns)] #![feature(exact_size_is_empty)] #![feature(file_buffered)] -#![feature(let_chains)] #![feature(never_type)] #![feature(try_blocks)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use rustc_middle::ty; @@ -19,8 +17,8 @@ pub use self::drop_flag_effects::{ move_path_children_matching, on_all_children_bits, on_lookup_result_bits, }; pub use self::framework::{ - Analysis, Backward, Direction, EntryStates, Forward, GenKill, JoinSemiLattice, MaybeReachable, - Results, ResultsCursor, ResultsVisitor, fmt, graphviz, lattice, visit_results, + Analysis, Backward, Direction, Forward, GenKill, JoinSemiLattice, MaybeReachable, Results, + ResultsCursor, ResultsVisitor, fmt, graphviz, lattice, visit_reachable_results, visit_results, }; use self::move_paths::MoveData; diff --git a/compiler/rustc_mir_dataflow/src/points.rs b/compiler/rustc_mir_dataflow/src/points.rs index 5d2a78acbf526..70d1a34b5fb13 100644 --- a/compiler/rustc_mir_dataflow/src/points.rs +++ b/compiler/rustc_mir_dataflow/src/points.rs @@ -98,7 +98,8 @@ rustc_index::newtype_index! { pub fn save_as_intervals<'tcx, N, A>( elements: &DenseLocationMap, body: &mir::Body<'tcx>, - mut results: Results<'tcx, A>, + mut analysis: A, + results: Results, ) -> SparseIntervalMatrix where N: Idx, @@ -109,7 +110,8 @@ where visit_results( body, body.basic_blocks.reverse_postorder().iter().copied(), - &mut results, + &mut analysis, + &results, &mut visitor, ); visitor.values @@ -120,14 +122,14 @@ struct Visitor<'a, N: Idx> { values: SparseIntervalMatrix, } -impl<'mir, 'tcx, A, N> ResultsVisitor<'mir, 'tcx, A> for Visitor<'_, N> +impl<'tcx, A, N> ResultsVisitor<'tcx, A> for Visitor<'_, N> where A: Analysis<'tcx, Domain = DenseBitSet>, N: Idx, { - fn visit_after_primary_statement_effect( + fn visit_after_primary_statement_effect<'mir>( &mut self, - _results: &mut Results<'tcx, A>, + _analysis: &mut A, state: &A::Domain, _statement: &'mir mir::Statement<'tcx>, location: Location, @@ -139,9 +141,9 @@ where }); } - fn visit_after_primary_terminator_effect( + fn visit_after_primary_terminator_effect<'mir>( &mut self, - _results: &mut Results<'tcx, A>, + _analysis: &mut A, state: &A::Domain, _terminator: &'mir mir::Terminator<'tcx>, location: Location, diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 399141aa9212e..303fc767b9a38 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -39,23 +39,23 @@ pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let move_data = MoveData::gather_moves(body, tcx, |_| true); if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { - let flow_inits = - MaybeInitializedPlaces::new(tcx, body, &move_data).iterate_to_fixpoint(tcx, body, None); - - sanity_check_via_rustc_peek(tcx, flow_inits.into_results_cursor(body)); + let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) + .iterate_to_fixpoint(tcx, body, None) + .into_results_cursor(body); + sanity_check_via_rustc_peek(tcx, flow_inits); } if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() { let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data) - .iterate_to_fixpoint(tcx, body, None); - - sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body)); + .iterate_to_fixpoint(tcx, body, None) + .into_results_cursor(body); + sanity_check_via_rustc_peek(tcx, flow_uninits); } if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() { - let flow_liveness = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None); - - sanity_check_via_rustc_peek(tcx, flow_liveness.into_results_cursor(body)); + let flow_liveness = + MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body); + sanity_check_via_rustc_peek(tcx, flow_liveness); } if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() { diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 36fb1c2b36d02..83fd8ccba60e5 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -405,6 +405,9 @@ impl<'tcx> Map<'tcx> { if exclude.contains(local) { continue; } + if decl.ty.is_async_drop_in_place_coroutine(tcx) { + continue; + } // Create a place for the local. debug_assert!(self.locals[local].is_none()); diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index fb8d0ac5e74ac..a7d0b3acbe494 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -10,7 +10,7 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_const_eval = { path = "../rustc_const_eval" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -26,7 +26,6 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -rustc_type_ir = { path = "../rustc_type_ir" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index 5628f4c9381b3..a1264471a2df5 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -84,3 +84,4 @@ mir_transform_undefined_transmute = pointers cannot be transmuted to integers du .help = for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html mir_transform_unknown_pass_name = MIR pass `{$name}` is unknown and will be ignored +mir_transform_unnecessary_transmute = unnecessary transmute diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs index 55694cacd92e0..bacff287859d0 100644 --- a/compiler/rustc_mir_transform/src/add_call_guards.rs +++ b/compiler/rustc_mir_transform/src/add_call_guards.rs @@ -32,14 +32,27 @@ pub(super) use self::AddCallGuards::*; impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let mut pred_count: IndexVec<_, _> = - body.basic_blocks.predecessors().iter().map(|ps| ps.len()).collect(); - pred_count[START_BLOCK] += 1; + let mut pred_count = IndexVec::from_elem(0u8, &body.basic_blocks); + for (_, data) in body.basic_blocks.iter_enumerated() { + for succ in data.terminator().successors() { + pred_count[succ] = pred_count[succ].saturating_add(1); + } + } // We need a place to store the new blocks generated let mut new_blocks = Vec::new(); let cur_len = body.basic_blocks.len(); + let mut new_block = |source_info: SourceInfo, is_cleanup: bool, target: BasicBlock| { + let block = BasicBlockData { + statements: vec![], + is_cleanup, + terminator: Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), + }; + let idx = cur_len + new_blocks.len(); + new_blocks.push(block); + BasicBlock::new(idx) + }; for block in body.basic_blocks_mut() { match block.terminator { @@ -47,25 +60,34 @@ impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { kind: TerminatorKind::Call { target: Some(ref mut destination), unwind, .. }, source_info, }) if pred_count[*destination] > 1 - && (matches!( - unwind, - UnwindAction::Cleanup(_) | UnwindAction::Terminate(_) - ) || self == &AllCallEdges) => + && (generates_invoke(unwind) || self == &AllCallEdges) => { // It's a critical edge, break it - let call_guard = BasicBlockData { - statements: vec![], - is_cleanup: block.is_cleanup, - terminator: Some(Terminator { - source_info, - kind: TerminatorKind::Goto { target: *destination }, - }), - }; - - // Get the index it will be when inserted into the MIR - let idx = cur_len + new_blocks.len(); - new_blocks.push(call_guard); - *destination = BasicBlock::new(idx); + *destination = new_block(source_info, block.is_cleanup, *destination); + } + Some(Terminator { + kind: + TerminatorKind::InlineAsm { + asm_macro: InlineAsmMacro::Asm, + ref mut targets, + ref operands, + unwind, + .. + }, + source_info, + }) if self == &CriticalCallEdges => { + let has_outputs = operands.iter().any(|op| { + matches!(op, InlineAsmOperand::InOut { .. } | InlineAsmOperand::Out { .. }) + }); + let has_labels = + operands.iter().any(|op| matches!(op, InlineAsmOperand::Label { .. })); + if has_outputs && (has_labels || generates_invoke(unwind)) { + for target in targets.iter_mut() { + if pred_count[*target] > 1 { + *target = new_block(source_info, block.is_cleanup, *target); + } + } + } } _ => {} } @@ -80,3 +102,11 @@ impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { true } } + +/// Returns true if this unwind action is code generated as an invoke as opposed to a call. +fn generates_invoke(unwind: UnwindAction) -> bool { + match unwind { + UnwindAction::Continue | UnwindAction::Unreachable => false, + UnwindAction::Cleanup(_) | UnwindAction::Terminate(_) => true, + } +} diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs index b33326cb873df..a414d120e68b5 100644 --- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs +++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs @@ -83,7 +83,9 @@ fn add_move_for_packed_drop<'tcx>( is_cleanup: bool, ) { debug!("add_move_for_packed_drop({:?} @ {:?})", terminator, loc); - let TerminatorKind::Drop { ref place, target, unwind, replace } = terminator.kind else { + let TerminatorKind::Drop { ref place, target, unwind, replace, drop, async_fut } = + terminator.kind + else { unreachable!(); }; @@ -106,6 +108,8 @@ fn add_move_for_packed_drop<'tcx>( target: storage_dead_block, unwind, replace, + drop, + async_fut, }, ); } diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs index b5cd41334598b..92ee80eaa353e 100644 --- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs +++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs @@ -32,7 +32,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for SubTypeChecker<'a, 'tcx> { let mut rval_ty = rvalue.ty(self.local_decls, self.tcx); // Not erasing this causes `Free Regions` errors in validator, // when rval is `ReStatic`. - rval_ty = self.tcx.erase_regions_ty(rval_ty); + rval_ty = self.tcx.erase_regions(rval_ty); place_ty = self.tcx.erase_regions(place_ty); if place_ty != rval_ty { let temp = self diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index b70cca1484070..8f88613b79f35 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -1,3 +1,4 @@ +use rustc_abi::Align; use rustc_index::IndexVec; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::PlaceContext; @@ -5,16 +6,12 @@ use rustc_middle::mir::*; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_session::Session; -use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers}; +use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers}; pub(super) struct CheckAlignment; impl<'tcx> crate::MirPass<'tcx> for CheckAlignment { fn is_enabled(&self, sess: &Session) -> bool { - // FIXME(#112480) MSVC and rustc disagree on minimum stack alignment on x86 Windows - if sess.target.llvm_target == "i686-pc-windows-msvc" { - return false; - } sess.ub_checks() } @@ -22,15 +19,15 @@ impl<'tcx> crate::MirPass<'tcx> for CheckAlignment { // Skip trivially aligned place types. let excluded_pointees = [tcx.types.bool, tcx.types.i8, tcx.types.u8]; - // We have to exclude borrows here: in `&x.field`, the exact - // requirement is that the final reference must be aligned, but - // `check_pointers` would check that `x` is aligned, which would be wrong. + // When checking the alignment of references to field projections (`&(*ptr).a`), + // we need to make sure that the reference is aligned according to the field type + // and not to the pointer type. check_pointers( tcx, body, &excluded_pointees, insert_alignment_check, - BorrowCheckMode::ExcludeBorrows, + BorrowedFieldProjectionMode::FollowProjections, ); } @@ -87,6 +84,33 @@ fn insert_alignment_check<'tcx>( ))), }); + // If this target does not have reliable alignment, further limit the mask by anding it with + // the mask for the highest reliable alignment. + #[allow(irrefutable_let_patterns)] + if let max_align = tcx.sess.target.max_reliable_alignment() + && max_align < Align::MAX + { + let max_mask = max_align.bytes() - 1; + let max_mask = Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + user_ty: None, + const_: Const::Val( + ConstValue::Scalar(Scalar::from_target_usize(max_mask, &tcx)), + tcx.types.usize, + ), + })); + stmts.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + alignment_mask, + Rvalue::BinaryOp( + BinOp::BitAnd, + Box::new((Operand::Copy(alignment_mask), max_mask)), + ), + ))), + }); + } + // BitAnd the alignment mask with the pointer let alignment_bits = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); diff --git a/compiler/rustc_mir_transform/src/check_call_recursion.rs b/compiler/rustc_mir_transform/src/check_call_recursion.rs index e49723a6c39db..cace4cd6bba56 100644 --- a/compiler/rustc_mir_transform/src/check_call_recursion.rs +++ b/compiler/rustc_mir_transform/src/check_call_recursion.rs @@ -3,6 +3,7 @@ use std::ops::ControlFlow; use rustc_data_structures::graph::iterate::{ NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, }; +use rustc_hir::LangItem; use rustc_hir::def::DefKind; use rustc_middle::mir::{self, BasicBlock, BasicBlocks, Body, Terminator, TerminatorKind}; use rustc_middle::ty::{self, GenericArg, GenericArgs, Instance, Ty, TyCtxt}; @@ -44,8 +45,7 @@ impl<'tcx> MirLint<'tcx> for CheckDropRecursion { if let DefKind::AssocFn = tcx.def_kind(def_id) && let Some(trait_ref) = tcx.impl_of_method(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id)) - && let Some(drop_trait) = tcx.lang_items().drop_trait() - && drop_trait == trait_ref.instantiate_identity().def_id + && tcx.is_lang_item(trait_ref.instantiate_identity().def_id, LangItem::Drop) // avoid erroneous `Drop` impls from causing ICEs below && let sig = tcx.fn_sig(def_id).instantiate_identity() && sig.inputs().skip_binder().len() == 1 diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs index ceea72c6755a0..375db17fb73a0 100644 --- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs +++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs @@ -53,9 +53,13 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> { // // #[const_mutation_allowed] // pub const LOG: Log = Log { msg: "" }; - match self.tcx.calculate_dtor(def_id, |_, _| Ok(())) { - Some(_) => None, - None => Some(def_id), + // FIXME: this should not be checking for `Drop` impls, + // but whether it or any field has a Drop impl (`needs_drop`) + // as fields' Drop impls may make this observable, too. + match self.tcx.type_of(def_id).skip_binder().ty_adt_def().map(|adt| adt.has_dtor(self.tcx)) + { + Some(true) => None, + Some(false) | None => Some(def_id), } } diff --git a/compiler/rustc_mir_transform/src/check_inline.rs b/compiler/rustc_mir_transform/src/check_inline.rs index 83c3cda5a5059..14d9532894fd4 100644 --- a/compiler/rustc_mir_transform/src/check_inline.rs +++ b/compiler/rustc_mir_transform/src/check_inline.rs @@ -1,7 +1,7 @@ //! Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its //! definition alone (irrespective of any specific caller). -use rustc_attr_parsing::InlineAttr; +use rustc_attr_data_structures::InlineAttr; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::{Body, TerminatorKind}; diff --git a/compiler/rustc_mir_transform/src/check_null.rs b/compiler/rustc_mir_transform/src/check_null.rs index 543e1845e6558..ad74e335bd9eb 100644 --- a/compiler/rustc_mir_transform/src/check_null.rs +++ b/compiler/rustc_mir_transform/src/check_null.rs @@ -4,7 +4,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_session::Session; -use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers}; +use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers}; pub(super) struct CheckNull; @@ -14,7 +14,13 @@ impl<'tcx> crate::MirPass<'tcx> for CheckNull { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - check_pointers(tcx, body, &[], insert_null_check, BorrowCheckMode::IncludeBorrows); + check_pointers( + tcx, + body, + &[], + insert_null_check, + BorrowedFieldProjectionMode::NoFollowProjections, + ); } fn is_required(&self) -> bool { diff --git a/compiler/rustc_mir_transform/src/check_pointers.rs b/compiler/rustc_mir_transform/src/check_pointers.rs index 2d04b62193585..bf94f1aad24b3 100644 --- a/compiler/rustc_mir_transform/src/check_pointers.rs +++ b/compiler/rustc_mir_transform/src/check_pointers.rs @@ -12,13 +12,13 @@ pub(crate) struct PointerCheck<'tcx> { pub(crate) assert_kind: Box>>, } -/// Indicates whether we insert the checks for borrow places of a raw pointer. -/// Concretely places with [MutatingUseContext::Borrow] or -/// [NonMutatingUseContext::SharedBorrow]. +/// When checking for borrows of field projections (`&(*ptr).a`), we might want +/// to check for the field type (type of `.a` in the example). This enum defines +/// the variations (pass the pointer [Ty] or the field [Ty]). #[derive(Copy, Clone)] -pub(crate) enum BorrowCheckMode { - IncludeBorrows, - ExcludeBorrows, +pub(crate) enum BorrowedFieldProjectionMode { + FollowProjections, + NoFollowProjections, } /// Utility for adding a check for read/write on every sized, raw pointer. @@ -27,8 +27,8 @@ pub(crate) enum BorrowCheckMode { /// new basic block directly before the pointer access. (Read/write accesses /// are determined by the `PlaceContext` of the MIR visitor.) Then calls /// `on_finding` to insert the actual logic for a pointer check (e.g. check for -/// alignment). A check can choose to be inserted for (mutable) borrows of -/// raw pointers via the `borrow_check_mode` parameter. +/// alignment). A check can choose to follow borrows of field projections via +/// the `field_projection_mode` parameter. /// /// This utility takes care of the right order of blocks, the only thing a /// caller must do in `on_finding` is: @@ -45,7 +45,7 @@ pub(crate) fn check_pointers<'tcx, F>( body: &mut Body<'tcx>, excluded_pointees: &[Ty<'tcx>], on_finding: F, - borrow_check_mode: BorrowCheckMode, + field_projection_mode: BorrowedFieldProjectionMode, ) where F: Fn( /* tcx: */ TyCtxt<'tcx>, @@ -82,7 +82,7 @@ pub(crate) fn check_pointers<'tcx, F>( local_decls, typing_env, excluded_pointees, - borrow_check_mode, + field_projection_mode, ); finder.visit_statement(statement, location); @@ -128,7 +128,7 @@ struct PointerFinder<'a, 'tcx> { typing_env: ty::TypingEnv<'tcx>, pointers: Vec<(Place<'tcx>, Ty<'tcx>, PlaceContext)>, excluded_pointees: &'a [Ty<'tcx>], - borrow_check_mode: BorrowCheckMode, + field_projection_mode: BorrowedFieldProjectionMode, } impl<'a, 'tcx> PointerFinder<'a, 'tcx> { @@ -137,7 +137,7 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> { local_decls: &'a mut LocalDecls<'tcx>, typing_env: ty::TypingEnv<'tcx>, excluded_pointees: &'a [Ty<'tcx>], - borrow_check_mode: BorrowCheckMode, + field_projection_mode: BorrowedFieldProjectionMode, ) -> Self { PointerFinder { tcx, @@ -145,7 +145,7 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> { typing_env, excluded_pointees, pointers: Vec::new(), - borrow_check_mode, + field_projection_mode, } } @@ -163,15 +163,14 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> { MutatingUseContext::Store | MutatingUseContext::Call | MutatingUseContext::Yield - | MutatingUseContext::Drop, + | MutatingUseContext::Drop + | MutatingUseContext::Borrow, ) => true, PlaceContext::NonMutatingUse( - NonMutatingUseContext::Copy | NonMutatingUseContext::Move, + NonMutatingUseContext::Copy + | NonMutatingUseContext::Move + | NonMutatingUseContext::SharedBorrow, ) => true, - PlaceContext::MutatingUse(MutatingUseContext::Borrow) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => { - matches!(self.borrow_check_mode, BorrowCheckMode::IncludeBorrows) - } _ => false, } } @@ -183,19 +182,29 @@ impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> { return; } - // Since Deref projections must come first and only once, the pointer for an indirect place - // is the Local that the Place is based on. + // Get the place and type we visit. let pointer = Place::from(place.local); - let pointer_ty = self.local_decls[place.local].ty; + let pointer_ty = pointer.ty(self.local_decls, self.tcx).ty; // We only want to check places based on raw pointers - if !pointer_ty.is_raw_ptr() { + let &ty::RawPtr(mut pointee_ty, _) = pointer_ty.kind() else { trace!("Indirect, but not based on an raw ptr, not checking {:?}", place); return; + }; + + // If we see a borrow of a field projection, we want to pass the field type to the + // check and not the pointee type. + if matches!(self.field_projection_mode, BorrowedFieldProjectionMode::FollowProjections) + && matches!( + context, + PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) + ) + { + // Naturally, the field type is type of the initial place we look at. + pointee_ty = place.ty(self.local_decls, self.tcx).ty; } - let pointee_ty = - pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer"); // Ideally we'd support this in the future, but for now we are limited to sized types. if !pointee_ty.is_sized(self.tcx, self.typing_env) { trace!("Raw pointer, but pointee is not known to be sized: {:?}", pointer_ty); @@ -207,6 +216,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> { ty::Array(ty, _) => *ty, _ => pointee_ty, }; + // Check if we excluded this pointee type from the check. if self.excluded_pointees.contains(&element_ty) { trace!("Skipping pointer for type: {:?}", pointee_ty); return; diff --git a/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs b/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs index ed3b1ae4f42f1..daddb5dedbcf9 100644 --- a/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs +++ b/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs @@ -42,7 +42,7 @@ impl<'a, 'tcx> UndefinedTransmutesChecker<'a, 'tcx> { if self.tcx.is_const_fn(def_id) || matches!( self.tcx.opt_associated_item(def_id), - Some(AssocItem { kind: AssocKind::Const, .. }) + Some(AssocItem { kind: AssocKind::Const { .. }, .. }) ) { let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder(); diff --git a/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs b/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs new file mode 100644 index 0000000000000..8da17a056e31b --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs @@ -0,0 +1,124 @@ +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind}; +use rustc_middle::ty::*; +use rustc_session::lint::builtin::UNNECESSARY_TRANSMUTES; +use rustc_span::source_map::Spanned; +use rustc_span::{Span, sym}; + +use crate::errors::UnnecessaryTransmute as Error; + +/// Check for transmutes that overlap with stdlib methods. +/// For example, transmuting `[u8; 4]` to `u32`. +pub(super) struct CheckUnnecessaryTransmutes; + +impl<'tcx> crate::MirLint<'tcx> for CheckUnnecessaryTransmutes { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let mut checker = UnnecessaryTransmuteChecker { body, tcx }; + checker.visit_body(body); + } +} + +struct UnnecessaryTransmuteChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, +} + +impl<'a, 'tcx> UnnecessaryTransmuteChecker<'a, 'tcx> { + fn is_unnecessary_transmute( + &self, + function: &Operand<'tcx>, + arg: String, + span: Span, + ) -> Option { + let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder(); + let [input] = fn_sig.inputs() else { return None }; + + let err = |sugg| Error { span, sugg, help: None }; + + Some(match (input.kind(), fn_sig.output().kind()) { + // dont check the length; transmute does that for us. + // [u8; _] => primitive + (Array(t, _), Uint(_) | Float(_) | Int(_)) if *t.kind() == Uint(UintTy::U8) => Error { + sugg: format!("{}::from_ne_bytes({arg})", fn_sig.output()), + help: Some( + "there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order", + ), + span, + }, + // primitive => [u8; _] + (Uint(_) | Float(_) | Int(_), Array(t, _)) if *t.kind() == Uint(UintTy::U8) => Error { + sugg: format!("{input}::to_ne_bytes({arg})"), + help: Some( + "there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order", + ), + span, + }, + // char → u32 + (Char, Uint(UintTy::U32)) => err(format!("u32::from({arg})")), + // char (→ u32) → i32 + (Char, Int(IntTy::I32)) => err(format!("u32::from({arg}).cast_signed()")), + // u32 → char + (Uint(UintTy::U32), Char) => Error { + sugg: format!("char::from_u32_unchecked({arg})"), + help: Some("consider `char::from_u32(…).unwrap()`"), + span, + }, + // i32 → char + (Int(IntTy::I32), Char) => Error { + sugg: format!("char::from_u32_unchecked(i32::cast_unsigned({arg}))"), + help: Some("consider `char::from_u32(i32::cast_unsigned(…)).unwrap()`"), + span, + }, + // uNN → iNN + (Uint(ty), Int(_)) => err(format!("{}::cast_signed({arg})", ty.name_str())), + // iNN → uNN + (Int(ty), Uint(_)) => err(format!("{}::cast_unsigned({arg})", ty.name_str())), + // fNN → xsize + (Float(ty), Uint(UintTy::Usize)) => { + err(format!("{}::to_bits({arg}) as usize", ty.name_str())) + } + (Float(ty), Int(IntTy::Isize)) => { + err(format!("{}::to_bits({arg}) as isize", ty.name_str())) + } + // fNN (→ uNN) → iNN + (Float(ty), Int(..)) => err(format!("{}::to_bits({arg}).cast_signed()", ty.name_str())), + // fNN → uNN + (Float(ty), Uint(..)) => err(format!("{}::to_bits({arg})", ty.name_str())), + // xsize → fNN + (Uint(UintTy::Usize) | Int(IntTy::Isize), Float(ty)) => { + err(format!("{}::from_bits({arg} as _)", ty.name_str(),)) + } + // iNN (→ uNN) → fNN + (Int(int_ty), Float(ty)) => err(format!( + "{}::from_bits({}::cast_unsigned({arg}))", + ty.name_str(), + int_ty.name_str() + )), + // uNN → fNN + (Uint(_), Float(ty)) => err(format!("{}::from_bits({arg})", ty.name_str())), + // bool → { x8 } + (Bool, Int(..) | Uint(..)) => err(format!("({arg}) as {}", fn_sig.output())), + // u8 → bool + (Uint(_), Bool) => err(format!("({arg} == 1)")), + _ => return None, + }) + } +} + +impl<'tcx> Visitor<'tcx> for UnnecessaryTransmuteChecker<'_, 'tcx> { + // Check each block's terminator for calls to pointer to integer transmutes + // in const functions or associated constants and emit a lint. + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + if let TerminatorKind::Call { func, args, .. } = &terminator.kind + && let [Spanned { span: arg, .. }] = **args + && let Some((func_def_id, _)) = func.const_fn_def() + && self.tcx.is_intrinsic(func_def_id, sym::transmute) + && let span = self.body.source_info(location).span + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(arg) + && let Some(lint) = self.is_unnecessary_transmute(func, snippet, span) + && let Some(hir_id) = terminator.source_info.scope.lint_root(&self.body.source_scopes) + { + self.tcx.emit_node_span_lint(UNNECESSARY_TRANSMUTES, hir_id, span, lint); + } + } +} diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index cb84401985735..4be67b873f737 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -35,7 +35,8 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck { // MIR building, and are not needed after InstrumentCoverage. CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. }, ) - | StatementKind::FakeRead(..) => statement.make_nop(), + | StatementKind::FakeRead(..) + | StatementKind::BackwardIncompatibleDropHint { .. } => statement.make_nop(), StatementKind::Assign(box ( _, Rvalue::Cast( diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 04d96f117072f..cddb2f8477858 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -51,9 +51,15 @@ //! Otherwise it drops all the values in scope at the last suspension point. mod by_move_body; +mod drop; use std::{iter, ops}; pub(super) use by_move_body::coroutine_by_move_body_def_id; +use drop::{ + cleanup_async_drops, create_coroutine_drop_shim, create_coroutine_drop_shim_async, + create_coroutine_drop_shim_proxy_async, elaborate_coroutine_drops, expand_async_drops, + has_expandable_async_drops, insert_clean_drop, +}; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::pluralize; @@ -64,6 +70,7 @@ use rustc_index::bit_set::{BitMatrix, DenseBitSet, GrowableBitSet}; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; +use rustc_middle::ty::util::Discr; use rustc_middle::ty::{ self, CoroutineArgs, CoroutineArgsExt, GenericArgsRef, InstanceKind, Ty, TyCtxt, TypingMode, }; @@ -72,9 +79,13 @@ use rustc_mir_dataflow::impls::{ MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive, always_storage_live_locals, }; -use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor}; +use rustc_mir_dataflow::{ + Analysis, Results, ResultsCursor, ResultsVisitor, visit_reachable_results, +}; use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::{Span, sym}; +use rustc_span::source_map::dummy_spanned; +use rustc_span::symbol::sym; +use rustc_span::{DUMMY_SP, Span}; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::TyCtxtInferExt as _; @@ -159,6 +170,7 @@ fn replace_base<'tcx>(place: &mut Place<'tcx>, new_base: Place<'tcx>, tcx: TyCtx } const SELF_ARG: Local = Local::from_u32(1); +const CTX_ARG: Local = Local::from_u32(2); /// A `yield` point in the coroutine. struct SuspensionPoint<'tcx> { @@ -203,7 +215,7 @@ struct TransformVisitor<'tcx> { impl<'tcx> TransformVisitor<'tcx> { fn insert_none_ret_block(&self, body: &mut Body<'tcx>) -> BasicBlock { - let block = BasicBlock::new(body.basic_blocks.len()); + let block = body.basic_blocks.next_index(); let source_info = SourceInfo::outermost(body.span); let none_value = match self.coroutine_kind { @@ -539,15 +551,15 @@ fn replace_local<'tcx>( /// The async lowering step and the type / lifetime inference / checking are /// still using the `ResumeTy` indirection for the time being, and that indirection /// is removed here. After this transform, the coroutine body only knows about `&mut Context<'_>`. -fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { +fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> Ty<'tcx> { let context_mut_ref = Ty::new_task_context(tcx); // replace the type of the `resume` argument - replace_resume_ty_local(tcx, body, Local::new(2), context_mut_ref); + replace_resume_ty_local(tcx, body, CTX_ARG, context_mut_ref); let get_context_def_id = tcx.require_lang_item(LangItem::GetContext, None); - for bb in START_BLOCK..body.basic_blocks.next_index() { + for bb in body.basic_blocks.indices() { let bb_data = &body[bb]; if bb_data.is_cleanup { continue; @@ -556,11 +568,11 @@ fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { match &bb_data.terminator().kind { TerminatorKind::Call { func, .. } => { let func_ty = func.ty(body, tcx); - if let ty::FnDef(def_id, _) = *func_ty.kind() { - if def_id == get_context_def_id { - let local = eliminate_get_context_call(&mut body[bb]); - replace_resume_ty_local(tcx, body, local, context_mut_ref); - } + if let ty::FnDef(def_id, _) = *func_ty.kind() + && def_id == get_context_def_id + { + let local = eliminate_get_context_call(&mut body[bb]); + replace_resume_ty_local(tcx, body, local, context_mut_ref); } } TerminatorKind::Yield { resume_arg, .. } => { @@ -569,6 +581,7 @@ fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { _ => {} } } + context_mut_ref } fn eliminate_get_context_call<'tcx>(bb_data: &mut BasicBlockData<'tcx>) -> Local { @@ -669,18 +682,29 @@ fn locals_live_across_suspend_points<'tcx>( .iterate_to_fixpoint(tcx, body, None) .into_results_cursor(body); - // Calculate the MIR locals which have been previously - // borrowed (even if they are still active). - let borrowed_locals_results = - MaybeBorrowedLocals.iterate_to_fixpoint(tcx, body, Some("coroutine")); - - let mut borrowed_locals_cursor = borrowed_locals_results.clone().into_results_cursor(body); + // Calculate the MIR locals that have been previously borrowed (even if they are still active). + let borrowed_locals = MaybeBorrowedLocals.iterate_to_fixpoint(tcx, body, Some("coroutine")); + let mut borrowed_locals_analysis1 = borrowed_locals.analysis; + let mut borrowed_locals_analysis2 = borrowed_locals_analysis1.clone(); // trivial + let borrowed_locals_cursor1 = ResultsCursor::new_borrowing( + body, + &mut borrowed_locals_analysis1, + &borrowed_locals.results, + ); + let mut borrowed_locals_cursor2 = ResultsCursor::new_borrowing( + body, + &mut borrowed_locals_analysis2, + &borrowed_locals.results, + ); // Calculate the MIR locals that we need to keep storage around for. - let mut requires_storage_results = - MaybeRequiresStorage::new(borrowed_locals_results.into_results_cursor(body)) - .iterate_to_fixpoint(tcx, body, None); - let mut requires_storage_cursor = requires_storage_results.as_results_cursor(body); + let mut requires_storage = + MaybeRequiresStorage::new(borrowed_locals_cursor1).iterate_to_fixpoint(tcx, body, None); + let mut requires_storage_cursor = ResultsCursor::new_borrowing( + body, + &mut requires_storage.analysis, + &requires_storage.results, + ); // Calculate the liveness of MIR locals ignoring borrows. let mut liveness = @@ -709,8 +733,8 @@ fn locals_live_across_suspend_points<'tcx>( // If a borrow is converted to a raw reference, we must also assume that it lives // forever. Note that the final liveness is still bounded by the storage liveness // of the local, which happens using the `intersect` operation below. - borrowed_locals_cursor.seek_before_primary_effect(loc); - live_locals.union(borrowed_locals_cursor.get()); + borrowed_locals_cursor2.seek_before_primary_effect(loc); + live_locals.union(borrowed_locals_cursor2.get()); } // Store the storage liveness for later use so we can restore the state @@ -752,7 +776,8 @@ fn locals_live_across_suspend_points<'tcx>( body, &saved_locals, always_live_locals.clone(), - requires_storage_results, + &mut requires_storage.analysis, + &requires_storage.results, ); LivenessInfo { @@ -817,7 +842,8 @@ fn compute_storage_conflicts<'mir, 'tcx>( body: &'mir Body<'tcx>, saved_locals: &'mir CoroutineSavedLocals, always_live_locals: DenseBitSet, - mut requires_storage: Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>, + analysis: &mut MaybeRequiresStorage<'mir, 'tcx>, + results: &Results>, ) -> BitMatrix { assert_eq!(body.local_decls.len(), saved_locals.domain_size()); @@ -837,7 +863,7 @@ fn compute_storage_conflicts<'mir, 'tcx>( eligible_storage_live: DenseBitSet::new_empty(body.local_decls.len()), }; - requires_storage.visit_reachable_with(body, &mut visitor); + visit_reachable_results(body, analysis, results, &mut visitor); let local_conflicts = visitor.local_conflicts; @@ -875,14 +901,14 @@ struct StorageConflictVisitor<'a, 'tcx> { eligible_storage_live: DenseBitSet, } -impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> +impl<'a, 'tcx> ResultsVisitor<'tcx, MaybeRequiresStorage<'a, 'tcx>> for StorageConflictVisitor<'a, 'tcx> { fn visit_after_early_statement_effect( &mut self, - _results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>, + _analysis: &mut MaybeRequiresStorage<'a, 'tcx>, state: &DenseBitSet, - _statement: &'a Statement<'tcx>, + _statement: &Statement<'tcx>, loc: Location, ) { self.apply_state(state, loc); @@ -890,9 +916,9 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> fn visit_after_early_terminator_effect( &mut self, - _results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>, + _analysis: &mut MaybeRequiresStorage<'a, 'tcx>, state: &DenseBitSet, - _terminator: &'a Terminator<'tcx>, + _terminator: &Terminator<'tcx>, loc: Location, ) { self.apply_state(state, loc); @@ -1036,9 +1062,8 @@ fn insert_switch<'tcx>( body: &mut Body<'tcx>, cases: Vec<(usize, BasicBlock)>, transform: &TransformVisitor<'tcx>, - default: TerminatorKind<'tcx>, + default_block: BasicBlock, ) { - let default_block = insert_term_block(body, default); let (assign, discr) = transform.get_discr(body); let switch_targets = SwitchTargets::new(cases.iter().map(|(i, bb)| ((*i) as u128, *bb)), default_block); @@ -1054,129 +1079,44 @@ fn insert_switch<'tcx>( }, ); - let blocks = body.basic_blocks_mut().iter_mut(); - - for target in blocks.flat_map(|b| b.terminator_mut().successors_mut()) { - *target = BasicBlock::new(target.index() + 1); - } -} - -fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - use crate::elaborate_drop::{Unwind, elaborate_drop}; - use crate::patch::MirPatch; - use crate::shim::DropShimElaborator; - - // Note that `elaborate_drops` only drops the upvars of a coroutine, and - // this is ok because `open_drop` can only be reached within that own - // coroutine's resume function. - let typing_env = body.typing_env(tcx); - - let mut elaborator = DropShimElaborator { body, patch: MirPatch::new(body), tcx, typing_env }; - - for (block, block_data) in body.basic_blocks.iter_enumerated() { - let (target, unwind, source_info) = match block_data.terminator() { - Terminator { - source_info, - kind: TerminatorKind::Drop { place, target, unwind, replace: _ }, - } => { - if let Some(local) = place.as_local() - && local == SELF_ARG - { - (target, unwind, source_info) - } else { - continue; - } - } - _ => continue, - }; - let unwind = if block_data.is_cleanup { - Unwind::InCleanup - } else { - Unwind::To(match *unwind { - UnwindAction::Cleanup(tgt) => tgt, - UnwindAction::Continue => elaborator.patch.resume_block(), - UnwindAction::Unreachable => elaborator.patch.unreachable_cleanup_block(), - UnwindAction::Terminate(reason) => elaborator.patch.terminate_block(reason), - }) - }; - elaborate_drop( - &mut elaborator, - *source_info, - Place::from(SELF_ARG), - (), - *target, - unwind, - block, - ); + for b in body.basic_blocks_mut().iter_mut() { + b.terminator_mut().successors_mut(|target| *target += 1); } - elaborator.patch.apply(body); } -fn create_coroutine_drop_shim<'tcx>( - tcx: TyCtxt<'tcx>, - transform: &TransformVisitor<'tcx>, - coroutine_ty: Ty<'tcx>, - body: &Body<'tcx>, - drop_clean: BasicBlock, -) -> Body<'tcx> { - let mut body = body.clone(); - // Take the coroutine info out of the body, since the drop shim is - // not a coroutine body itself; it just has its drop built out of it. - let _ = body.coroutine.take(); - // Make sure the resume argument is not included here, since we're - // building a body for `drop_in_place`. - body.arg_count = 1; - +fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock { let source_info = SourceInfo::outermost(body.span); + body.basic_blocks_mut().push(BasicBlockData { + statements: Vec::new(), + terminator: Some(Terminator { source_info, kind }), + is_cleanup: false, + }) +} - let mut cases = create_cases(&mut body, transform, Operation::Drop); - - cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean)); - - // The returned state and the poisoned state fall through to the default - // case which is just to return - - insert_switch(&mut body, cases, transform, TerminatorKind::Return); - - for block in body.basic_blocks_mut() { - let kind = &mut block.terminator_mut().kind; - if let TerminatorKind::CoroutineDrop = *kind { - *kind = TerminatorKind::Return; - } +fn return_poll_ready_assign<'tcx>(tcx: TyCtxt<'tcx>, source_info: SourceInfo) -> Statement<'tcx> { + // Poll::Ready(()) + let poll_def_id = tcx.require_lang_item(LangItem::Poll, None); + let args = tcx.mk_args(&[tcx.types.unit.into()]); + let val = Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + user_ty: None, + const_: Const::zero_sized(tcx.types.unit), + })); + let ready_val = Rvalue::Aggregate( + Box::new(AggregateKind::Adt(poll_def_id, VariantIdx::from_usize(0), args, None, None)), + IndexVec::from_raw(vec![val]), + ); + Statement { + kind: StatementKind::Assign(Box::new((Place::return_place(), ready_val))), + source_info, } - - // Replace the return variable - body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.types.unit, source_info); - - make_coroutine_state_argument_indirect(tcx, &mut body); - - // Change the coroutine argument from &mut to *mut - body.local_decls[SELF_ARG] = - LocalDecl::with_source_info(Ty::new_mut_ptr(tcx, coroutine_ty), source_info); - - // Make sure we remove dead blocks to remove - // unrelated code from the resume part of the function - simplify::remove_dead_blocks(&mut body); - - // Update the body's def to become the drop glue. - let coroutine_instance = body.source.instance; - let drop_in_place = tcx.require_lang_item(LangItem::DropInPlace, None); - let drop_instance = InstanceKind::DropGlue(drop_in_place, Some(coroutine_ty)); - - // Temporary change MirSource to coroutine's instance so that dump_mir produces more sensible - // filename. - body.source.instance = coroutine_instance; - dump_mir(tcx, false, "coroutine_drop", &0, &body, |_, _| Ok(())); - body.source.instance = drop_instance; - - body } -fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock { +fn insert_poll_ready_block<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> BasicBlock { let source_info = SourceInfo::outermost(body.span); body.basic_blocks_mut().push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { source_info, kind }), + statements: [return_poll_ready_assign(tcx, source_info)].to_vec(), + terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), is_cleanup: false, }) } @@ -1186,7 +1126,7 @@ fn insert_panic_block<'tcx>( body: &mut Body<'tcx>, message: AssertMessage<'tcx>, ) -> BasicBlock { - let assert_block = BasicBlock::new(body.basic_blocks.len()); + let assert_block = body.basic_blocks.next_index(); let kind = TerminatorKind::Assert { cond: Operand::Constant(Box::new(ConstOperand { span: body.span, @@ -1209,14 +1149,8 @@ fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, typing_env: ty::Typing } // If there's a return terminator the function may return. - for block in body.basic_blocks.iter() { - if let TerminatorKind::Return = block.terminator().kind { - return true; - } - } - + body.basic_blocks.iter().any(|block| matches!(block.terminator().kind, TerminatorKind::Return)) // Otherwise the function can't return. - false } fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { @@ -1262,45 +1196,50 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { false } +// Poison the coroutine when it unwinds +fn generate_poison_block_and_redirect_unwinds_there<'tcx>( + transform: &TransformVisitor<'tcx>, + body: &mut Body<'tcx>, +) { + let source_info = SourceInfo::outermost(body.span); + let poison_block = body.basic_blocks_mut().push(BasicBlockData { + statements: vec![ + transform.set_discr(VariantIdx::new(CoroutineArgs::POISONED), source_info), + ], + terminator: Some(Terminator { source_info, kind: TerminatorKind::UnwindResume }), + is_cleanup: true, + }); + + for (idx, block) in body.basic_blocks_mut().iter_enumerated_mut() { + let source_info = block.terminator().source_info; + + if let TerminatorKind::UnwindResume = block.terminator().kind { + // An existing `Resume` terminator is redirected to jump to our dedicated + // "poisoning block" above. + if idx != poison_block { + *block.terminator_mut() = + Terminator { source_info, kind: TerminatorKind::Goto { target: poison_block } }; + } + } else if !block.is_cleanup + // Any terminators that *can* unwind but don't have an unwind target set are also + // pointed at our poisoning block (unless they're part of the cleanup path). + && let Some(unwind @ UnwindAction::Continue) = block.terminator_mut().unwind_mut() + { + *unwind = UnwindAction::Cleanup(poison_block); + } + } +} + fn create_coroutine_resume_function<'tcx>( tcx: TyCtxt<'tcx>, transform: TransformVisitor<'tcx>, body: &mut Body<'tcx>, can_return: bool, + can_unwind: bool, ) { - let can_unwind = can_unwind(tcx, body); - // Poison the coroutine when it unwinds if can_unwind { - let source_info = SourceInfo::outermost(body.span); - let poison_block = body.basic_blocks_mut().push(BasicBlockData { - statements: vec![ - transform.set_discr(VariantIdx::new(CoroutineArgs::POISONED), source_info), - ], - terminator: Some(Terminator { source_info, kind: TerminatorKind::UnwindResume }), - is_cleanup: true, - }); - - for (idx, block) in body.basic_blocks_mut().iter_enumerated_mut() { - let source_info = block.terminator().source_info; - - if let TerminatorKind::UnwindResume = block.terminator().kind { - // An existing `Resume` terminator is redirected to jump to our dedicated - // "poisoning block" above. - if idx != poison_block { - *block.terminator_mut() = Terminator { - source_info, - kind: TerminatorKind::Goto { target: poison_block }, - }; - } - } else if !block.is_cleanup { - // Any terminators that *can* unwind but don't have an unwind target set are also - // pointed at our poisoning block (unless they're part of the cleanup path). - if let Some(unwind @ UnwindAction::Continue) = block.terminator_mut().unwind_mut() { - *unwind = UnwindAction::Cleanup(poison_block); - } - } - } + generate_poison_block_and_redirect_unwinds_there(&transform, body); } let mut cases = create_cases(body, &transform, Operation::Resume); @@ -1325,7 +1264,13 @@ fn create_coroutine_resume_function<'tcx>( let block = match transform.coroutine_kind { CoroutineKind::Desugared(CoroutineDesugaring::Async, _) | CoroutineKind::Coroutine(_) => { - insert_panic_block(tcx, body, ResumedAfterReturn(transform.coroutine_kind)) + // For `async_drop_in_place::{closure}` we just keep return Poll::Ready, + // because async drop of such coroutine keeps polling original coroutine + if tcx.is_async_drop_in_place_coroutine(body.source.def_id()) { + insert_poll_ready_block(tcx, body) + } else { + insert_panic_block(tcx, body, ResumedAfterReturn(transform.coroutine_kind)) + } } CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _) | CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => { @@ -1335,17 +1280,20 @@ fn create_coroutine_resume_function<'tcx>( cases.insert(1, (CoroutineArgs::RETURNED, block)); } - insert_switch(body, cases, &transform, TerminatorKind::Unreachable); + let default_block = insert_term_block(body, TerminatorKind::Unreachable); + insert_switch(body, cases, &transform, default_block); make_coroutine_state_argument_indirect(tcx, body); match transform.coroutine_kind { + CoroutineKind::Coroutine(_) + | CoroutineKind::Desugared(CoroutineDesugaring::Async | CoroutineDesugaring::AsyncGen, _) => + { + make_coroutine_state_argument_pinned(tcx, body); + } // Iterator::next doesn't accept a pinned argument, // unlike for all other coroutine kinds. CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {} - _ => { - make_coroutine_state_argument_pinned(tcx, body); - } } // Make sure we remove dead blocks to remove @@ -1357,25 +1305,6 @@ fn create_coroutine_resume_function<'tcx>( dump_mir(tcx, false, "coroutine_resume", &0, body, |_, _| Ok(())); } -fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock { - let return_block = insert_term_block(body, TerminatorKind::Return); - - let term = TerminatorKind::Drop { - place: Place::from(SELF_ARG), - target: return_block, - unwind: UnwindAction::Continue, - replace: false, - }; - let source_info = SourceInfo::outermost(body.span); - - // Create a block to destroy an unresumed coroutines. This can only destroy upvars. - body.basic_blocks_mut().push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { source_info, kind: term }), - is_cleanup: false, - }) -} - /// An operation that can be performed on a coroutine. #[derive(PartialEq, Copy, Clone)] enum Operation { @@ -1408,8 +1337,7 @@ fn create_cases<'tcx>( let mut statements = Vec::new(); // Create StorageLive instructions for locals with live storage - for i in 0..(body.local_decls.len()) { - let l = Local::new(i); + for l in body.local_decls.indices() { let needs_storage_live = point.storage_liveness.contains(l) && !transform.remap.contains(l) && !transform.always_live_locals.contains(l); @@ -1421,7 +1349,7 @@ fn create_cases<'tcx>( if operation == Operation::Resume { // Move the resume argument to the destination place of the `Yield` terminator - let resume_arg = Local::new(2); // 0 = return, 1 = self + let resume_arg = CTX_ARG; statements.push(Statement { source_info, kind: StatementKind::Assign(Box::new(( @@ -1528,22 +1456,19 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { }; let old_ret_ty = body.return_ty(); - assert!(body.coroutine_drop().is_none()); + assert!(body.coroutine_drop().is_none() && body.coroutine_drop_async().is_none()); + + dump_mir(tcx, false, "coroutine_before", &0, body, |_, _| Ok(())); // The first argument is the coroutine type passed by value let coroutine_ty = body.local_decls.raw[1].ty; let coroutine_kind = body.coroutine_kind().unwrap(); // Get the discriminant type and args which typeck computed - let (discr_ty, movable) = match *coroutine_ty.kind() { - ty::Coroutine(_, args) => { - let args = args.as_coroutine(); - (args.discr_ty(tcx), coroutine_kind.movability() == hir::Movability::Movable) - } - _ => { - tcx.dcx().span_bug(body.span, format!("unexpected coroutine type {coroutine_ty}")); - } + let ty::Coroutine(_, args) = coroutine_ty.kind() else { + tcx.dcx().span_bug(body.span, format!("unexpected coroutine type {coroutine_ty}")); }; + let discr_ty = args.as_coroutine().discr_ty(tcx); let new_ret_ty = match coroutine_kind { CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => { @@ -1577,19 +1502,32 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { // RETURN_PLACE then is a fresh unused local with type ret_ty. let old_ret_local = replace_local(RETURN_PLACE, new_ret_ty, body, tcx); + // We need to insert clean drop for unresumed state and perform drop elaboration + // (finally in open_drop_for_tuple) before async drop expansion. + // Async drops, produced by this drop elaboration, will be expanded, + // and corresponding futures kept in layout. + let has_async_drops = matches!( + coroutine_kind, + CoroutineKind::Desugared(CoroutineDesugaring::Async | CoroutineDesugaring::AsyncGen, _) + ) && has_expandable_async_drops(tcx, body, coroutine_ty); + // Replace all occurrences of `ResumeTy` with `&mut Context<'_>` within async bodies. if matches!( coroutine_kind, CoroutineKind::Desugared(CoroutineDesugaring::Async | CoroutineDesugaring::AsyncGen, _) ) { - transform_async_context(tcx, body); + let context_mut_ref = transform_async_context(tcx, body); + expand_async_drops(tcx, body, context_mut_ref, coroutine_kind, coroutine_ty); + dump_mir(tcx, false, "coroutine_async_drop_expand", &0, body, |_, _| Ok(())); + } else { + cleanup_async_drops(body); } // We also replace the resume argument and insert an `Assign`. // This is needed because the resume argument `_2` might be live across a `yield`, in which // case there is no `Assign` to it that the transform can turn into a store to the coroutine // state. After the yield the slot in the coroutine state would then be uninitialized. - let resume_local = Local::new(2); + let resume_local = CTX_ARG; let resume_ty = body.local_decls[resume_local].ty; let old_resume_local = replace_local(resume_local, resume_ty, body, tcx); @@ -1610,6 +1548,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { let always_live_locals = always_storage_live_locals(body); + let movable = coroutine_kind.movability() == hir::Movability::Movable; let liveness_info = locals_live_across_suspend_points(tcx, body, &always_live_locals, movable); @@ -1669,10 +1608,14 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { body.coroutine.as_mut().unwrap().resume_ty = None; body.coroutine.as_mut().unwrap().coroutine_layout = Some(layout); + // FIXME: Drops, produced by insert_clean_drop + elaborate_coroutine_drops, + // are currently sync only. To allow async for them, we need to move those calls + // before expand_async_drops, and fix the related problems. + // // Insert `drop(coroutine_struct)` which is used to drop upvars for coroutines in // the unresumed state. // This is expanded to a drop ladder in `elaborate_coroutine_drops`. - let drop_clean = insert_clean_drop(body); + let drop_clean = insert_clean_drop(tcx, body, has_async_drops); dump_mir(tcx, false, "coroutine_pre-elab", &0, body, |_, _| Ok(())); @@ -1683,13 +1626,32 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { dump_mir(tcx, false, "coroutine_post-transform", &0, body, |_, _| Ok(())); - // Create a copy of our MIR and use it to create the drop shim for the coroutine - let drop_shim = create_coroutine_drop_shim(tcx, &transform, coroutine_ty, body, drop_clean); + let can_unwind = can_unwind(tcx, body); - body.coroutine.as_mut().unwrap().coroutine_drop = Some(drop_shim); + // Create a copy of our MIR and use it to create the drop shim for the coroutine + if has_async_drops { + // If coroutine has async drops, generating async drop shim + let mut drop_shim = + create_coroutine_drop_shim_async(tcx, &transform, body, drop_clean, can_unwind); + // Run derefer to fix Derefs that are not in the first place + deref_finder(tcx, &mut drop_shim); + body.coroutine.as_mut().unwrap().coroutine_drop_async = Some(drop_shim); + } else { + // If coroutine has no async drops, generating sync drop shim + let mut drop_shim = + create_coroutine_drop_shim(tcx, &transform, coroutine_ty, body, drop_clean); + // Run derefer to fix Derefs that are not in the first place + deref_finder(tcx, &mut drop_shim); + body.coroutine.as_mut().unwrap().coroutine_drop = Some(drop_shim); + + // For coroutine with sync drop, generating async proxy for `future_drop_poll` call + let mut proxy_shim = create_coroutine_drop_shim_proxy_async(tcx, body); + deref_finder(tcx, &mut proxy_shim); + body.coroutine.as_mut().unwrap().coroutine_drop_proxy_async = Some(proxy_shim); + } // Create the Coroutine::resume / Future::poll function - create_coroutine_resume_function(tcx, transform, body, can_return); + create_coroutine_resume_function(tcx, transform, body, can_return, can_unwind); // Run derefer to fix Derefs that are not in the first place deref_finder(tcx, body); diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 0f5fcb0d8eb99..0a839d91404ec 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -73,12 +73,12 @@ use rustc_data_structures::unord::UnordMap; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::definitions::DisambiguatorState; use rustc_middle::bug; use rustc_middle::hir::place::{Projection, ProjectionKind}; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::{self, dump_mir}; use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::kw; pub(crate) fn coroutine_by_move_body_def_id<'tcx>( tcx: TyCtxt<'tcx>, @@ -170,7 +170,7 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( // this when building the field projection in the MIR body later on. let mut parent_capture_ty = parent_capture.place.ty(); parent_capture_ty = match parent_capture.info.capture_kind { - ty::UpvarCapture::ByValue => parent_capture_ty, + ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => parent_capture_ty, ty::UpvarCapture::ByRef(kind) => Ty::new_ref( tcx, tcx.lifetimes.re_erased, @@ -179,7 +179,7 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( ), }; - ( + Some(( FieldIdx::from_usize(child_field_idx + num_args), ( FieldIdx::from_usize(parent_field_idx + num_args), @@ -187,9 +187,10 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( peel_deref, child_precise_captures, ), - ) + )) }, ) + .flatten() .collect(); if coroutine_kind == ty::ClosureKind::FnOnce { @@ -213,12 +214,21 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( let mut by_move_body = body.clone(); MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body); - // This will always be `{closure#1}`, since the original coroutine is `{closure#0}`. - let body_def = tcx.create_def(parent_def_id, kw::Empty, DefKind::SyntheticCoroutineBody); + // This path is unique since we're in a query so we'll only be called once with `parent_def_id` + // and this is the only location creating `SyntheticCoroutineBody`. + let body_def = tcx.create_def( + parent_def_id, + None, + DefKind::SyntheticCoroutineBody, + None, + &mut DisambiguatorState::new(), + ); by_move_body.source = mir::MirSource::from_instance(InstanceKind::Item(body_def.def_id().to_def_id())); dump_mir(tcx, false, "built", &"after", &by_move_body, |_, _| Ok(())); + // Feed HIR because we try to access this body's attrs in the inliner. + body_def.feed_hir(); // Inherited from the by-ref coroutine. body_def.codegen_fn_attrs(tcx.codegen_fn_attrs(coroutine_def_id).clone()); body_def.coverage_attr_on(tcx.coverage_attr_on(coroutine_def_id)); @@ -313,10 +323,46 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> { self.super_place(place, context, location); } + fn visit_statement(&mut self, statement: &mut mir::Statement<'tcx>, location: mir::Location) { + // Remove fake borrows of closure captures if that capture has been + // replaced with a by-move version of that capture. + // + // For example, imagine we capture `Foo` in the parent and `&Foo` + // in the child. We will emit two fake borrows like: + // + // ``` + // _2 = &fake shallow (*(_1.0: &Foo)); + // _3 = &fake shallow (_1.0: &Foo); + // ``` + // + // However, since this transform is responsible for replacing + // `_1.0: &Foo` with `_1.0: Foo`, that makes the second fake borrow + // obsolete, and we should replace it with a nop. + // + // As a side-note, we don't actually even care about fake borrows + // here at all since they're fully a MIR borrowck artifact, and we + // don't need to borrowck by-move MIR bodies. But it's best to preserve + // as much as we can between these two bodies :) + if let mir::StatementKind::Assign(box (_, rvalue)) = &statement.kind + && let mir::Rvalue::Ref(_, mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow), place) = + rvalue + && let mir::PlaceRef { + local: ty::CAPTURE_STRUCT_LOCAL, + projection: [mir::ProjectionElem::Field(idx, _)], + } = place.as_ref() + && let Some(&(_, _, true, _)) = self.field_remapping.get(&idx) + { + statement.kind = mir::StatementKind::Nop; + } + + self.super_statement(statement, location); + } + fn visit_local_decl(&mut self, local: mir::Local, local_decl: &mut mir::LocalDecl<'tcx>) { // Replace the type of the self arg. if local == ty::CAPTURE_STRUCT_LOCAL { local_decl.ty = self.by_move_coroutine_ty; } + self.super_local_decl(local, local_decl); } } diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs new file mode 100644 index 0000000000000..6b266da5a69e5 --- /dev/null +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -0,0 +1,725 @@ +//! Drops and async drops related logic for coroutine transformation pass + +use super::*; + +// Fix return Poll::Pending statement into Poll<()>::Pending for async drop function +struct FixReturnPendingVisitor<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> MutVisitor<'tcx> for FixReturnPendingVisitor<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_assign( + &mut self, + place: &mut Place<'tcx>, + rvalue: &mut Rvalue<'tcx>, + _location: Location, + ) { + if place.local != RETURN_PLACE { + return; + } + + // Converting `_0 = Poll::::Pending` to `_0 = Poll::<()>::Pending` + if let Rvalue::Aggregate(kind, _) = rvalue { + if let AggregateKind::Adt(_, _, ref mut args, _, _) = **kind { + *args = self.tcx.mk_args(&[self.tcx.types.unit.into()]); + } + } + } +} + +// rv = call fut.poll() +fn build_poll_call<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + poll_unit_place: &Place<'tcx>, + switch_block: BasicBlock, + fut_pin_place: &Place<'tcx>, + fut_ty: Ty<'tcx>, + context_ref_place: &Place<'tcx>, + unwind: UnwindAction, +) -> BasicBlock { + let poll_fn = tcx.require_lang_item(LangItem::FuturePoll, None); + let poll_fn = Ty::new_fn_def(tcx, poll_fn, [fut_ty]); + let poll_fn = Operand::Constant(Box::new(ConstOperand { + span: DUMMY_SP, + user_ty: None, + const_: Const::zero_sized(poll_fn), + })); + let call = TerminatorKind::Call { + func: poll_fn.clone(), + args: [ + dummy_spanned(Operand::Move(*fut_pin_place)), + dummy_spanned(Operand::Move(*context_ref_place)), + ] + .into(), + destination: *poll_unit_place, + target: Some(switch_block), + unwind, + call_source: CallSource::Misc, + fn_span: DUMMY_SP, + }; + insert_term_block(body, call) +} + +// pin_fut = Pin::new_unchecked(&mut fut) +fn build_pin_fut<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + fut_place: Place<'tcx>, + unwind: UnwindAction, +) -> (BasicBlock, Place<'tcx>) { + let span = body.span; + let source_info = SourceInfo::outermost(span); + let fut_ty = fut_place.ty(&body.local_decls, tcx).ty; + let fut_ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, fut_ty); + let fut_ref_place = Place::from(body.local_decls.push(LocalDecl::new(fut_ref_ty, span))); + let pin_fut_new_unchecked_fn = Ty::new_fn_def( + tcx, + tcx.require_lang_item(LangItem::PinNewUnchecked, Some(span)), + [fut_ref_ty], + ); + let fut_pin_ty = pin_fut_new_unchecked_fn.fn_sig(tcx).output().skip_binder(); + let fut_pin_place = Place::from(body.local_decls.push(LocalDecl::new(fut_pin_ty, span))); + let pin_fut_new_unchecked_fn = Operand::Constant(Box::new(ConstOperand { + span, + user_ty: None, + const_: Const::zero_sized(pin_fut_new_unchecked_fn), + })); + + let storage_live = + Statement { source_info, kind: StatementKind::StorageLive(fut_pin_place.local) }; + + let fut_ref_assign = Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + fut_ref_place, + Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + fut_place, + ), + ))), + }; + + // call Pin::new_unchecked(&mut fut) + let pin_fut_bb = body.basic_blocks_mut().push(BasicBlockData { + statements: [storage_live, fut_ref_assign].to_vec(), + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::Call { + func: pin_fut_new_unchecked_fn, + args: [dummy_spanned(Operand::Move(fut_ref_place))].into(), + destination: fut_pin_place, + target: None, // will be fixed later + unwind, + call_source: CallSource::Misc, + fn_span: span, + }, + }), + is_cleanup: false, + }); + (pin_fut_bb, fut_pin_place) +} + +// Build Poll switch for async drop +// match rv { +// Ready() => ready_block +// Pending => yield_block +//} +fn build_poll_switch<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + poll_enum: Ty<'tcx>, + poll_unit_place: &Place<'tcx>, + ready_block: BasicBlock, + yield_block: BasicBlock, +) -> BasicBlock { + let poll_enum_adt = poll_enum.ty_adt_def().unwrap(); + + let Discr { val: poll_ready_discr, ty: poll_discr_ty } = poll_enum + .discriminant_for_variant( + tcx, + poll_enum_adt.variant_index_with_id(tcx.require_lang_item(LangItem::PollReady, None)), + ) + .unwrap(); + let poll_pending_discr = poll_enum + .discriminant_for_variant( + tcx, + poll_enum_adt.variant_index_with_id(tcx.require_lang_item(LangItem::PollPending, None)), + ) + .unwrap() + .val; + let source_info = SourceInfo::outermost(body.span); + let poll_discr_place = + Place::from(body.local_decls.push(LocalDecl::new(poll_discr_ty, source_info.span))); + let discr_assign = Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + poll_discr_place, + Rvalue::Discriminant(*poll_unit_place), + ))), + }; + let unreachable_block = insert_term_block(body, TerminatorKind::Unreachable); + body.basic_blocks_mut().push(BasicBlockData { + statements: [discr_assign].to_vec(), + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::SwitchInt { + discr: Operand::Move(poll_discr_place), + targets: SwitchTargets::new( + [(poll_ready_discr, ready_block), (poll_pending_discr, yield_block)] + .into_iter(), + unreachable_block, + ), + }, + }), + is_cleanup: false, + }) +} + +// Gather blocks, reachable through 'drop' targets of Yield and Drop terminators (chained) +fn gather_dropline_blocks<'tcx>(body: &mut Body<'tcx>) -> DenseBitSet { + let mut dropline: DenseBitSet = DenseBitSet::new_empty(body.basic_blocks.len()); + for (bb, data) in traversal::reverse_postorder(body) { + if dropline.contains(bb) { + data.terminator().successors().for_each(|v| { + dropline.insert(v); + }); + } else { + match data.terminator().kind { + TerminatorKind::Yield { drop: Some(v), .. } => { + dropline.insert(v); + } + TerminatorKind::Drop { drop: Some(v), .. } => { + dropline.insert(v); + } + _ => (), + } + } + } + dropline +} + +/// Cleanup all async drops (reset to sync) +pub(super) fn cleanup_async_drops<'tcx>(body: &mut Body<'tcx>) { + for block in body.basic_blocks_mut() { + if let TerminatorKind::Drop { + place: _, + target: _, + unwind: _, + replace: _, + ref mut drop, + ref mut async_fut, + } = block.terminator_mut().kind + { + if drop.is_some() || async_fut.is_some() { + *drop = None; + *async_fut = None; + } + } + } +} + +pub(super) fn has_expandable_async_drops<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + coroutine_ty: Ty<'tcx>, +) -> bool { + for bb in START_BLOCK..body.basic_blocks.next_index() { + // Drops in unwind path (cleanup blocks) are not expanded to async drops, only sync drops in unwind path + if body[bb].is_cleanup { + continue; + } + let TerminatorKind::Drop { place, target: _, unwind: _, replace: _, drop: _, async_fut } = + body[bb].terminator().kind + else { + continue; + }; + let place_ty = place.ty(&body.local_decls, tcx).ty; + if place_ty == coroutine_ty { + continue; + } + if async_fut.is_none() { + continue; + } + return true; + } + return false; +} + +/// Expand Drop terminator for async drops into mainline poll-switch and dropline poll-switch +pub(super) fn expand_async_drops<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + context_mut_ref: Ty<'tcx>, + coroutine_kind: hir::CoroutineKind, + coroutine_ty: Ty<'tcx>, +) { + let dropline = gather_dropline_blocks(body); + // Clean drop and async_fut fields if potentially async drop is not expanded (stays sync) + let remove_asyncness = |block: &mut BasicBlockData<'tcx>| { + if let TerminatorKind::Drop { + place: _, + target: _, + unwind: _, + replace: _, + ref mut drop, + ref mut async_fut, + } = block.terminator_mut().kind + { + *drop = None; + *async_fut = None; + } + }; + for bb in START_BLOCK..body.basic_blocks.next_index() { + // Drops in unwind path (cleanup blocks) are not expanded to async drops, only sync drops in unwind path + if body[bb].is_cleanup { + remove_asyncness(&mut body[bb]); + continue; + } + let TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut } = + body[bb].terminator().kind + else { + continue; + }; + + let place_ty = place.ty(&body.local_decls, tcx).ty; + if place_ty == coroutine_ty { + remove_asyncness(&mut body[bb]); + continue; + } + + let Some(fut_local) = async_fut else { + remove_asyncness(&mut body[bb]); + continue; + }; + + let is_dropline_bb = dropline.contains(bb); + + if !is_dropline_bb && drop.is_none() { + remove_asyncness(&mut body[bb]); + continue; + } + + let fut_place = Place::from(fut_local); + let fut_ty = fut_place.ty(&body.local_decls, tcx).ty; + + // poll-code: + // state_call_drop: + // #bb_pin: fut_pin = Pin::new_unchecked(&mut fut) + // #bb_call: rv = call fut.poll() (or future_drop_poll(fut) for internal future drops) + // #bb_check: match (rv) + // pending => return rv (yield) + // ready => *continue_bb|drop_bb* + + // Compute Poll<> (aka Poll with void return) + let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)); + let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()])); + let poll_decl = LocalDecl::new(poll_enum, body.span); + let poll_unit_place = Place::from(body.local_decls.push(poll_decl)); + + // First state-loop yield for mainline + let context_ref_place = + Place::from(body.local_decls.push(LocalDecl::new(context_mut_ref, body.span))); + let source_info = body[bb].terminator.as_ref().unwrap().source_info; + let arg = Rvalue::Use(Operand::Move(Place::from(CTX_ARG))); + body[bb].statements.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new((context_ref_place, arg))), + }); + let yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind` replaced later to yield + let switch_block = + build_poll_switch(tcx, body, poll_enum, &poll_unit_place, target, yield_block); + let (pin_bb, fut_pin_place) = + build_pin_fut(tcx, body, fut_place.clone(), UnwindAction::Continue); + let call_bb = build_poll_call( + tcx, + body, + &poll_unit_place, + switch_block, + &fut_pin_place, + fut_ty, + &context_ref_place, + unwind, + ); + + // Second state-loop yield for transition to dropline (when coroutine async drop started) + let mut dropline_transition_bb: Option = None; + let mut dropline_yield_bb: Option = None; + let mut dropline_context_ref: Option> = None; + let mut dropline_call_bb: Option = None; + if !is_dropline_bb { + let context_ref_place2: Place<'_> = + Place::from(body.local_decls.push(LocalDecl::new(context_mut_ref, body.span))); + let drop_yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind` replaced later to yield + let drop_switch_block = build_poll_switch( + tcx, + body, + poll_enum, + &poll_unit_place, + drop.unwrap(), + drop_yield_block, + ); + let (pin_bb2, fut_pin_place2) = + build_pin_fut(tcx, body, fut_place, UnwindAction::Continue); + let drop_call_bb = build_poll_call( + tcx, + body, + &poll_unit_place, + drop_switch_block, + &fut_pin_place2, + fut_ty, + &context_ref_place2, + unwind, + ); + dropline_transition_bb = Some(pin_bb2); + dropline_yield_bb = Some(drop_yield_block); + dropline_context_ref = Some(context_ref_place2); + dropline_call_bb = Some(drop_call_bb); + } + + // value needed only for return-yields or gen-coroutines, so just const here + let value = Operand::Constant(Box::new(ConstOperand { + span: body.span, + user_ty: None, + const_: Const::from_bool(tcx, false), + })); + use rustc_middle::mir::AssertKind::ResumedAfterDrop; + let panic_bb = insert_panic_block(tcx, body, ResumedAfterDrop(coroutine_kind)); + + if is_dropline_bb { + body[yield_block].terminator_mut().kind = TerminatorKind::Yield { + value: value.clone(), + resume: panic_bb, + resume_arg: context_ref_place, + drop: Some(pin_bb), + }; + } else { + body[yield_block].terminator_mut().kind = TerminatorKind::Yield { + value: value.clone(), + resume: pin_bb, + resume_arg: context_ref_place, + drop: dropline_transition_bb, + }; + body[dropline_yield_bb.unwrap()].terminator_mut().kind = TerminatorKind::Yield { + value, + resume: panic_bb, + resume_arg: dropline_context_ref.unwrap(), + drop: dropline_transition_bb, + }; + } + + if let TerminatorKind::Call { ref mut target, .. } = body[pin_bb].terminator_mut().kind { + *target = Some(call_bb); + } else { + bug!() + } + if !is_dropline_bb { + if let TerminatorKind::Call { ref mut target, .. } = + body[dropline_transition_bb.unwrap()].terminator_mut().kind + { + *target = dropline_call_bb; + } else { + bug!() + } + } + + body[bb].terminator_mut().kind = TerminatorKind::Goto { target: pin_bb }; + } +} + +pub(super) fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + use crate::elaborate_drop::{Unwind, elaborate_drop}; + use crate::patch::MirPatch; + use crate::shim::DropShimElaborator; + + // Note that `elaborate_drops` only drops the upvars of a coroutine, and + // this is ok because `open_drop` can only be reached within that own + // coroutine's resume function. + let typing_env = body.typing_env(tcx); + + let mut elaborator = DropShimElaborator { + body, + patch: MirPatch::new(body), + tcx, + typing_env, + produce_async_drops: false, + }; + + for (block, block_data) in body.basic_blocks.iter_enumerated() { + let (target, unwind, source_info, dropline) = match block_data.terminator() { + Terminator { + source_info, + kind: TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut: _ }, + } => { + if let Some(local) = place.as_local() + && local == SELF_ARG + { + (target, unwind, source_info, *drop) + } else { + continue; + } + } + _ => continue, + }; + let unwind = if block_data.is_cleanup { + Unwind::InCleanup + } else { + Unwind::To(match *unwind { + UnwindAction::Cleanup(tgt) => tgt, + UnwindAction::Continue => elaborator.patch.resume_block(), + UnwindAction::Unreachable => elaborator.patch.unreachable_cleanup_block(), + UnwindAction::Terminate(reason) => elaborator.patch.terminate_block(reason), + }) + }; + elaborate_drop( + &mut elaborator, + *source_info, + Place::from(SELF_ARG), + (), + *target, + unwind, + block, + dropline, + ); + } + elaborator.patch.apply(body); +} + +pub(super) fn insert_clean_drop<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + has_async_drops: bool, +) -> BasicBlock { + let source_info = SourceInfo::outermost(body.span); + let return_block = if has_async_drops { + insert_poll_ready_block(tcx, body) + } else { + insert_term_block(body, TerminatorKind::Return) + }; + + // FIXME: When move insert_clean_drop + elaborate_coroutine_drops before async drops expand, + // also set dropline here: + // let dropline = if has_async_drops { Some(return_block) } else { None }; + let dropline = None; + + let term = TerminatorKind::Drop { + place: Place::from(SELF_ARG), + target: return_block, + unwind: UnwindAction::Continue, + replace: false, + drop: dropline, + async_fut: None, + }; + + // Create a block to destroy an unresumed coroutines. This can only destroy upvars. + body.basic_blocks_mut().push(BasicBlockData { + statements: Vec::new(), + terminator: Some(Terminator { source_info, kind: term }), + is_cleanup: false, + }) +} + +pub(super) fn create_coroutine_drop_shim<'tcx>( + tcx: TyCtxt<'tcx>, + transform: &TransformVisitor<'tcx>, + coroutine_ty: Ty<'tcx>, + body: &Body<'tcx>, + drop_clean: BasicBlock, +) -> Body<'tcx> { + let mut body = body.clone(); + // Take the coroutine info out of the body, since the drop shim is + // not a coroutine body itself; it just has its drop built out of it. + let _ = body.coroutine.take(); + // Make sure the resume argument is not included here, since we're + // building a body for `drop_in_place`. + body.arg_count = 1; + + let source_info = SourceInfo::outermost(body.span); + + let mut cases = create_cases(&mut body, transform, Operation::Drop); + + cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean)); + + // The returned state and the poisoned state fall through to the default + // case which is just to return + + let default_block = insert_term_block(&mut body, TerminatorKind::Return); + insert_switch(&mut body, cases, transform, default_block); + + for block in body.basic_blocks_mut() { + let kind = &mut block.terminator_mut().kind; + if let TerminatorKind::CoroutineDrop = *kind { + *kind = TerminatorKind::Return; + } + } + + // Replace the return variable + body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.types.unit, source_info); + + make_coroutine_state_argument_indirect(tcx, &mut body); + + // Change the coroutine argument from &mut to *mut + body.local_decls[SELF_ARG] = + LocalDecl::with_source_info(Ty::new_mut_ptr(tcx, coroutine_ty), source_info); + + // Make sure we remove dead blocks to remove + // unrelated code from the resume part of the function + simplify::remove_dead_blocks(&mut body); + + // Update the body's def to become the drop glue. + let coroutine_instance = body.source.instance; + let drop_in_place = tcx.require_lang_item(LangItem::DropInPlace, None); + let drop_instance = InstanceKind::DropGlue(drop_in_place, Some(coroutine_ty)); + + // Temporary change MirSource to coroutine's instance so that dump_mir produces more sensible + // filename. + body.source.instance = coroutine_instance; + dump_mir(tcx, false, "coroutine_drop", &0, &body, |_, _| Ok(())); + body.source.instance = drop_instance; + + // Creating a coroutine drop shim happens on `Analysis(PostCleanup) -> Runtime(Initial)` + // but the pass manager doesn't update the phase of the coroutine drop shim. Update the + // phase of the drop shim so that later on when we run the pass manager on the shim, in + // the `mir_shims` query, we don't ICE on the intra-pass validation before we've updated + // the phase of the body from analysis. + body.phase = MirPhase::Runtime(RuntimePhase::Initial); + + body +} + +// Create async drop shim function to drop coroutine itself +pub(super) fn create_coroutine_drop_shim_async<'tcx>( + tcx: TyCtxt<'tcx>, + transform: &TransformVisitor<'tcx>, + body: &Body<'tcx>, + drop_clean: BasicBlock, + can_unwind: bool, +) -> Body<'tcx> { + let mut body = body.clone(); + // Take the coroutine info out of the body, since the drop shim is + // not a coroutine body itself; it just has its drop built out of it. + let _ = body.coroutine.take(); + + FixReturnPendingVisitor { tcx }.visit_body(&mut body); + + // Poison the coroutine when it unwinds + if can_unwind { + generate_poison_block_and_redirect_unwinds_there(transform, &mut body); + } + + let source_info = SourceInfo::outermost(body.span); + + let mut cases = create_cases(&mut body, transform, Operation::Drop); + + cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean)); + + use rustc_middle::mir::AssertKind::ResumedAfterPanic; + // Panic when resumed on the returned or poisoned state + if can_unwind { + cases.insert( + 1, + ( + CoroutineArgs::POISONED, + insert_panic_block(tcx, &mut body, ResumedAfterPanic(transform.coroutine_kind)), + ), + ); + } + + // RETURNED state also goes to default_block with `return Ready<()>`. + // For fully-polled coroutine, async drop has nothing to do. + let default_block = insert_poll_ready_block(tcx, &mut body); + insert_switch(&mut body, cases, transform, default_block); + + for block in body.basic_blocks_mut() { + let kind = &mut block.terminator_mut().kind; + if let TerminatorKind::CoroutineDrop = *kind { + *kind = TerminatorKind::Return; + block.statements.push(return_poll_ready_assign(tcx, source_info)); + } + } + + // Replace the return variable: Poll to Poll<()> + let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)); + let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()])); + body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info); + + make_coroutine_state_argument_indirect(tcx, &mut body); + + match transform.coroutine_kind { + // Iterator::next doesn't accept a pinned argument, + // unlike for all other coroutine kinds. + CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {} + _ => { + make_coroutine_state_argument_pinned(tcx, &mut body); + } + } + + // Make sure we remove dead blocks to remove + // unrelated code from the resume part of the function + simplify::remove_dead_blocks(&mut body); + + pm::run_passes_no_validate( + tcx, + &mut body, + &[&abort_unwinding_calls::AbortUnwindingCalls], + None, + ); + + dump_mir(tcx, false, "coroutine_drop_async", &0, &body, |_, _| Ok(())); + + body +} + +// Create async drop shim proxy function for future_drop_poll +// It is just { call coroutine_drop(); return Poll::Ready(); } +pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, +) -> Body<'tcx> { + let mut body = body.clone(); + // Take the coroutine info out of the body, since the drop shim is + // not a coroutine body itself; it just has its drop built out of it. + let _ = body.coroutine.take(); + let basic_blocks: IndexVec> = IndexVec::new(); + body.basic_blocks = BasicBlocks::new(basic_blocks); + body.var_debug_info.clear(); + + // Keeping return value and args + body.local_decls.truncate(1 + body.arg_count); + + let source_info = SourceInfo::outermost(body.span); + + // Replace the return variable: Poll to Poll<()> + let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)); + let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()])); + body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info); + + // call coroutine_drop() + let call_bb = body.basic_blocks_mut().push(BasicBlockData { + statements: Vec::new(), + terminator: None, + is_cleanup: false, + }); + + // return Poll::Ready() + let ret_bb = insert_poll_ready_block(tcx, &mut body); + + let kind = TerminatorKind::Drop { + place: Place::from(SELF_ARG), + target: ret_bb, + unwind: UnwindAction::Continue, + replace: false, + drop: None, + async_fut: None, + }; + body.basic_blocks_mut()[call_bb].terminator = Some(Terminator { source_info, kind }); + + dump_mir(tcx, false, "coroutine_drop_proxy_async", &0, &body, |_, _| Ok(())); + + body +} diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index b23d8b9e73729..00a8293966b04 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -37,29 +37,11 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> { /// and even the full `Inline` doesn't call `visit_body`, so there's nowhere /// to put this logic in the visitor. pub(super) fn add_function_level_costs(&mut self) { - fn is_call_like(bbd: &BasicBlockData<'_>) -> bool { - use TerminatorKind::*; - match bbd.terminator().kind { - Call { .. } | TailCall { .. } | Drop { .. } | Assert { .. } | InlineAsm { .. } => { - true - } - - Goto { .. } - | SwitchInt { .. } - | UnwindResume - | UnwindTerminate(_) - | Return - | Unreachable => false, - - Yield { .. } | CoroutineDrop | FalseEdge { .. } | FalseUnwind { .. } => { - unreachable!() - } - } - } - // If the only has one Call (or similar), inlining isn't increasing the total // number of calls, so give extra encouragement to inlining that. - if self.callee_body.basic_blocks.iter().filter(|bbd| is_call_like(bbd)).count() == 1 { + if self.callee_body.basic_blocks.iter().filter(|bbd| is_call_like(bbd.terminator())).count() + == 1 + { self.bonus += CALL_PENALTY; } } @@ -193,3 +175,26 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { } } } + +/// A terminator that's more call-like (might do a bunch of work, might panic, etc) +/// than it is goto-/return-like (no side effects, etc). +/// +/// Used to treat multi-call functions (which could inline exponentially) +/// different from those that only do one or none of these "complex" things. +pub(super) fn is_call_like(terminator: &Terminator<'_>) -> bool { + use TerminatorKind::*; + match terminator.kind { + Call { .. } | TailCall { .. } | Drop { .. } | Assert { .. } | InlineAsm { .. } => true, + + Goto { .. } + | SwitchInt { .. } + | UnwindResume + | UnwindTerminate(_) + | Return + | Unreachable => false, + + Yield { .. } | CoroutineDrop | FalseEdge { .. } | FalseUnwind { .. } => { + unreachable!() + } + } +} diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index d83c0d40a7e54..b4b4d0416fb99 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -91,12 +91,12 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>( // When debugging flag `-Zcoverage-options=no-mir-spans` is set, we need // to give the same treatment to _all_ functions, because `llvm-cov` // seems to ignore functions that don't have any ordinary code spans. - if let Some(span) = hir_info.fn_sig_span_extended { + if let Some(span) = hir_info.fn_sig_span { code_mappings.push(CodeMapping { span, bcb: START_BCB }); } } else { // Extract coverage spans from MIR statements/terminators as normal. - extract_refined_covspans(mir_body, hir_info, graph, &mut code_mappings); + extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut code_mappings); } branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, graph)); diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 1ccae0fd7fe95..702c62eddc7fb 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -1,8 +1,7 @@ -pub(super) mod query; - mod counters; mod graph; mod mappings; +pub(super) mod query; mod spans; #[cfg(test)] mod tests; @@ -90,7 +89,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: // Use the coverage graph to prepare intermediate data that will eventually // be used to assign physical counters and counter expressions to points in - // the control-flow graph + // the control-flow graph. let BcbCountersData { node_flow_data, priority_list } = counters::prepare_bcb_counters_data(&graph); @@ -107,7 +106,6 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { function_source_hash: hir_info.function_source_hash, - body_span: hir_info.body_span, node_flow_data, priority_list, @@ -270,12 +268,13 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb struct ExtractedHirInfo { function_source_hash: u64, is_async_fn: bool, - /// The span of the function's signature, extended to the start of `body_span`. + /// The span of the function's signature, if available. /// Must have the same context and filename as the body span. - fn_sig_span_extended: Option, + fn_sig_span: Option, body_span: Span, - /// "Holes" are regions within the body span that should not be included in - /// coverage spans for this function (e.g. closures and nested items). + /// "Holes" are regions within the function body (or its expansions) that + /// should not be included in coverage spans for this function + /// (e.g. closures and nested items). hole_spans: Vec, } @@ -309,30 +308,20 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir // The actual signature span is only used if it has the same context and // filename as the body, and precedes the body. - let fn_sig_span_extended = maybe_fn_sig - .map(|fn_sig| fn_sig.span) - .filter(|&fn_sig_span| { - let source_map = tcx.sess.source_map(); - let file_idx = |span: Span| source_map.lookup_source_file_idx(span.lo()); - - fn_sig_span.eq_ctxt(body_span) - && fn_sig_span.hi() <= body_span.lo() - && file_idx(fn_sig_span) == file_idx(body_span) - }) - // If so, extend it to the start of the body span. - .map(|fn_sig_span| fn_sig_span.with_hi(body_span.lo())); + let fn_sig_span = maybe_fn_sig.map(|fn_sig| fn_sig.span).filter(|&fn_sig_span| { + let source_map = tcx.sess.source_map(); + let file_idx = |span: Span| source_map.lookup_source_file_idx(span.lo()); + + fn_sig_span.eq_ctxt(body_span) + && fn_sig_span.hi() <= body_span.lo() + && file_idx(fn_sig_span) == file_idx(body_span) + }); let function_source_hash = hash_mir_source(tcx, hir_body); - let hole_spans = extract_hole_spans_from_hir(tcx, body_span, hir_body); + let hole_spans = extract_hole_spans_from_hir(tcx, hir_body); - ExtractedHirInfo { - function_source_hash, - is_async_fn, - fn_sig_span_extended, - body_span, - hole_spans, - } + ExtractedHirInfo { function_source_hash, is_async_fn, fn_sig_span, body_span, hole_spans } } fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx hir::Body<'tcx>) -> u64 { @@ -341,14 +330,9 @@ fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx hir::Body<'tcx>) -> tcx.hir_owner_nodes(owner).opt_hash_including_bodies.unwrap().to_smaller_hash().as_u64() } -fn extract_hole_spans_from_hir<'tcx>( - tcx: TyCtxt<'tcx>, - body_span: Span, // Usually `hir_body.value.span`, but not always - hir_body: &hir::Body<'tcx>, -) -> Vec { +fn extract_hole_spans_from_hir<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &hir::Body<'tcx>) -> Vec { struct HolesVisitor<'tcx> { tcx: TyCtxt<'tcx>, - body_span: Span, hole_spans: Vec, } @@ -388,14 +372,11 @@ fn extract_hole_spans_from_hir<'tcx>( } impl HolesVisitor<'_> { fn visit_hole_span(&mut self, hole_span: Span) { - // Discard any holes that aren't directly visible within the body span. - if self.body_span.contains(hole_span) && self.body_span.eq_ctxt(hole_span) { - self.hole_spans.push(hole_span); - } + self.hole_spans.push(hole_span); } } - let mut visitor = HolesVisitor { tcx, body_span, hole_spans: vec![] }; + let mut visitor = HolesVisitor { tcx, hole_spans: vec![] }; visitor.visit_body(hir_body); visitor.hole_spans diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index b9ed6984ddb21..ddeae093df5b7 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,30 +1,57 @@ -use std::collections::VecDeque; - use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir; -use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span}; -use tracing::{debug, debug_span, instrument}; +use rustc_middle::ty::TyCtxt; +use rustc_span::source_map::SourceMap; +use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span}; +use tracing::instrument; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; -use crate::coverage::spans::from_mir::{ - ExtractedCovspans, Hole, SpanFromMir, extract_covspans_from_mir, -}; -use crate::coverage::{ExtractedHirInfo, mappings}; +use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir, SpanFromMir}; +use crate::coverage::{ExtractedHirInfo, mappings, unexpand}; mod from_mir; -pub(super) fn extract_refined_covspans( - mir_body: &mir::Body<'_>, +pub(super) fn extract_refined_covspans<'tcx>( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, hir_info: &ExtractedHirInfo, graph: &CoverageGraph, code_mappings: &mut impl Extend, ) { - let ExtractedCovspans { mut covspans } = extract_covspans_from_mir(mir_body, hir_info, graph); + let &ExtractedHirInfo { body_span, .. } = hir_info; + + let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph); + let mut covspans = raw_spans + .into_iter() + .filter_map(|RawSpanFromMir { raw_span, bcb }| try { + let (span, expn_kind) = + unexpand::unexpand_into_body_span_with_expn_kind(raw_span, body_span)?; + // Discard any spans that fill the entire body, because they tend + // to represent compiler-inserted code, e.g. implicitly returning `()`. + if span.source_equal(body_span) { + return None; + }; + SpanFromMir { span, expn_kind, bcb } + }) + .collect::>(); + + // Only proceed if we found at least one usable span. + if covspans.is_empty() { + return; + } + + // Also add the function signature span, if available. + // Otherwise, add a fake span at the start of the body, to avoid an ugly + // gap between the start of the body and the first real span. + // FIXME: Find a more principled way to solve this problem. + covspans.push(SpanFromMir::for_fn_sig( + hir_info.fn_sig_span.unwrap_or_else(|| body_span.shrink_to_lo()), + )); // First, perform the passes that need macro information. covspans.sort_by(|a, b| graph.cmp_in_dominator_order(a.bcb, b.bcb)); remove_unwanted_expansion_spans(&mut covspans); - split_visible_macro_spans(&mut covspans); + shrink_visible_macro_spans(tcx, &mut covspans); // We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`. let mut covspans = covspans.into_iter().map(SpanFromMir::into_covspan).collect::>(); @@ -43,30 +70,38 @@ pub(super) fn extract_refined_covspans( covspans.dedup_by(|b, a| a.span.source_equal(b.span)); // Sort the holes, and merge overlapping/adjacent holes. - let mut holes = hir_info.hole_spans.iter().map(|&span| Hole { span }).collect::>(); + let mut holes = hir_info + .hole_spans + .iter() + .copied() + // Discard any holes that aren't directly visible within the body span. + .filter(|&hole_span| body_span.contains(hole_span) && body_span.eq_ctxt(hole_span)) + .map(|span| Hole { span }) + .collect::>(); holes.sort_by(|a, b| compare_spans(a.span, b.span)); holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b)); - // Split the covspans into separate buckets that don't overlap any holes. - let buckets = divide_spans_into_buckets(covspans, &holes); + // Discard any span that overlaps with a hole. + discard_spans_overlapping_holes(&mut covspans, &holes); - for mut covspans in buckets { - // Make sure each individual bucket is internally sorted. - covspans.sort_by(compare_covspans); - let _span = debug_span!("processing bucket", ?covspans).entered(); + // Discard spans that overlap in unwanted ways. + let mut covspans = remove_unwanted_overlapping_spans(covspans); - let mut covspans = remove_unwanted_overlapping_spans(covspans); - debug!(?covspans, "after removing overlaps"); + // For all empty spans, either enlarge them to be non-empty, or discard them. + let source_map = tcx.sess.source_map(); + covspans.retain_mut(|covspan| { + let Some(span) = ensure_non_empty_span(source_map, covspan.span) else { return false }; + covspan.span = span; + true + }); - // Do one last merge pass, to simplify the output. - covspans.dedup_by(|b, a| a.merge_if_eligible(b)); - debug!(?covspans, "after merge"); + // Merge covspans that can be merged. + covspans.dedup_by(|b, a| a.merge_if_eligible(b)); - code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| { - // Each span produced by the refiner represents an ordinary code region. - mappings::CodeMapping { span, bcb } - })); - } + code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| { + // Each span produced by the refiner represents an ordinary code region. + mappings::CodeMapping { span, bcb } + })); } /// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate @@ -96,96 +131,48 @@ fn remove_unwanted_expansion_spans(covspans: &mut Vec) { } /// When a span corresponds to a macro invocation that is visible from the -/// function body, split it into two parts. The first part covers just the -/// macro name plus `!`, and the second part covers the rest of the macro -/// invocation. This seems to give better results for code that uses macros. -fn split_visible_macro_spans(covspans: &mut Vec) { - let mut extra_spans = vec![]; - - covspans.retain(|covspan| { - let Some(ExpnKind::Macro(MacroKind::Bang, visible_macro)) = covspan.expn_kind else { - return true; - }; - - let split_len = visible_macro.as_str().len() as u32 + 1; - let (before, after) = covspan.span.split_at(split_len); - if !covspan.span.contains(before) || !covspan.span.contains(after) { - // Something is unexpectedly wrong with the split point. - // The debug assertion in `split_at` will have already caught this, - // but in release builds it's safer to do nothing and maybe get a - // bug report for unexpected coverage, rather than risk an ICE. - return true; +/// function body, truncate it to just the macro name plus `!`. +/// This seems to give better results for code that uses macros. +fn shrink_visible_macro_spans(tcx: TyCtxt<'_>, covspans: &mut Vec) { + let source_map = tcx.sess.source_map(); + + for covspan in covspans { + if matches!(covspan.expn_kind, Some(ExpnKind::Macro(MacroKind::Bang, _))) { + covspan.span = source_map.span_through_char(covspan.span, '!'); } - - extra_spans.push(SpanFromMir::new(before, covspan.expn_kind.clone(), covspan.bcb)); - extra_spans.push(SpanFromMir::new(after, covspan.expn_kind.clone(), covspan.bcb)); - false // Discard the original covspan that we just split. - }); - - // The newly-split spans are added at the end, so any previous sorting - // is not preserved. - covspans.extend(extra_spans); + } } -/// Uses the holes to divide the given covspans into buckets, such that: -/// - No span in any hole overlaps a bucket (truncating the spans if necessary). -/// - The spans in each bucket are strictly after all spans in previous buckets, -/// and strictly before all spans in subsequent buckets. +/// Discard all covspans that overlap a hole. /// -/// The resulting buckets are sorted relative to each other, but might not be -/// internally sorted. -#[instrument(level = "debug")] -fn divide_spans_into_buckets(input_covspans: Vec, holes: &[Hole]) -> Vec> { - debug_assert!(input_covspans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le())); +/// The lists of covspans and holes must be sorted, and any holes that overlap +/// with each other must have already been merged. +fn discard_spans_overlapping_holes(covspans: &mut Vec, holes: &[Hole]) { + debug_assert!(covspans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le())); debug_assert!(holes.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le())); + debug_assert!(holes.array_windows().all(|[a, b]| !a.span.overlaps_or_adjacent(b.span))); + + let mut curr_hole = 0usize; + let mut overlaps_hole = |covspan: &Covspan| -> bool { + while let Some(hole) = holes.get(curr_hole) { + // Both lists are sorted, so we can permanently skip any holes that + // end before the start of the current span. + if hole.span.hi() <= covspan.span.lo() { + curr_hole += 1; + continue; + } - // Now we're ready to start carving holes out of the initial coverage spans, - // and grouping them in buckets separated by the holes. - - let mut input_covspans = VecDeque::from(input_covspans); - let mut fragments = vec![]; - - // For each hole: - // - Identify the spans that are entirely or partly before the hole. - // - Put those spans in a corresponding bucket, truncated to the start of the hole. - // - If one of those spans also extends after the hole, put the rest of it - // in a "fragments" vector that is processed by the next hole. - let mut buckets = (0..holes.len()).map(|_| vec![]).collect::>(); - for (hole, bucket) in holes.iter().zip(&mut buckets) { - let fragments_from_prev = std::mem::take(&mut fragments); - - // Only inspect spans that precede or overlap this hole, - // leaving the rest to be inspected by later holes. - // (This relies on the spans and holes both being sorted.) - let relevant_input_covspans = - drain_front_while(&mut input_covspans, |c| c.span.lo() < hole.span.hi()); - - for covspan in fragments_from_prev.into_iter().chain(relevant_input_covspans) { - let (before, after) = covspan.split_around_hole_span(hole.span); - bucket.extend(before); - fragments.extend(after); + return hole.span.overlaps(covspan.span); } - } - - // After finding the spans before each hole, any remaining fragments/spans - // form their own final bucket, after the final hole. - // (If there were no holes, this will just be all of the initial spans.) - fragments.extend(input_covspans); - buckets.push(fragments); - buckets -} + // No holes left, so this covspan doesn't overlap with any holes. + false + }; -/// Similar to `.drain(..)`, but stops just before it would remove an item not -/// satisfying the predicate. -fn drain_front_while<'a, T>( - queue: &'a mut VecDeque, - mut pred_fn: impl FnMut(&T) -> bool, -) -> impl Iterator { - std::iter::from_fn(move || if pred_fn(queue.front()?) { queue.pop_front() } else { None }) + covspans.retain(|covspan| !overlaps_hole(covspan)); } -/// Takes one of the buckets of (sorted) spans extracted from MIR, and "refines" +/// Takes a list of sorted spans extracted from MIR, and "refines" /// those spans by removing spans that overlap in unwanted ways. #[instrument(level = "debug")] fn remove_unwanted_overlapping_spans(sorted_spans: Vec) -> Vec { @@ -225,35 +212,21 @@ struct Covspan { } impl Covspan { - /// Splits this covspan into 0-2 parts: - /// - The part that is strictly before the hole span, if any. - /// - The part that is strictly after the hole span, if any. - fn split_around_hole_span(&self, hole_span: Span) -> (Option, Option) { - let before = try { - let span = self.span.trim_end(hole_span)?; - Self { span, ..*self } - }; - let after = try { - let span = self.span.trim_start(hole_span)?; - Self { span, ..*self } - }; - - (before, after) - } - - /// If `self` and `other` can be merged (i.e. they have the same BCB), - /// mutates `self.span` to also include `other.span` and returns true. + /// If `self` and `other` can be merged, mutates `self.span` to also + /// include `other.span` and returns true. /// - /// Note that compatible covspans can be merged even if their underlying - /// spans are not overlapping/adjacent; any space between them will also be - /// part of the merged covspan. + /// Two covspans can be merged if they have the same BCB, and they are + /// overlapping or adjacent. fn merge_if_eligible(&mut self, other: &Self) -> bool { - if self.bcb != other.bcb { - return false; + let eligible_for_merge = + |a: &Self, b: &Self| (a.bcb == b.bcb) && a.span.overlaps_or_adjacent(b.span); + + if eligible_for_merge(self, other) { + self.span = self.span.to(other.span); + true + } else { + false } - - self.span = self.span.to(other.span); - true } } @@ -268,3 +241,26 @@ fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering { // - Both have the same start and span A extends further right .then_with(|| Ord::cmp(&a.hi(), &b.hi()).reverse()) } + +fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option { + if !span.is_empty() { + return Some(span); + } + + // The span is empty, so try to enlarge it to cover an adjacent '{' or '}'. + source_map + .span_to_source(span, |src, start, end| try { + // Adjusting span endpoints by `BytePos(1)` is normally a bug, + // but in this case we have specifically checked that the character + // we're skipping over is one of two specific ASCII characters, so + // adjusting by exactly 1 byte is correct. + if src.as_bytes().get(end).copied() == Some(b'{') { + Some(span.with_hi(span.hi() + BytePos(1))) + } else if start > 0 && src.as_bytes()[start - 1] == b'}' { + Some(span.with_lo(span.lo() - BytePos(1))) + } else { + None + } + }) + .ok()? +} diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 73b68d7155cfb..804cd8ab3f7d3 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -1,3 +1,5 @@ +use std::iter; + use rustc_middle::bug; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::{ @@ -5,87 +7,50 @@ use rustc_middle::mir::{ }; use rustc_span::{ExpnKind, Span}; -use crate::coverage::ExtractedHirInfo; -use crate::coverage::graph::{ - BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, -}; +use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB}; use crate::coverage::spans::Covspan; -use crate::coverage::unexpand::unexpand_into_body_span_with_expn_kind; -pub(crate) struct ExtractedCovspans { - pub(crate) covspans: Vec, +#[derive(Debug)] +pub(crate) struct RawSpanFromMir { + /// A span that has been extracted from a MIR statement/terminator, but + /// hasn't been "unexpanded", so it might not lie within the function body + /// span and might be part of an expansion with a different context. + pub(crate) raw_span: Span, + pub(crate) bcb: BasicCoverageBlock, } -/// Traverses the MIR body to produce an initial collection of coverage-relevant -/// spans, each associated with a node in the coverage graph (BCB) and possibly -/// other metadata. -pub(crate) fn extract_covspans_from_mir( - mir_body: &mir::Body<'_>, - hir_info: &ExtractedHirInfo, +/// Generates an initial set of coverage spans from the statements and +/// terminators in the function's MIR body, each associated with its +/// corresponding node in the coverage graph. +/// +/// This is necessarily an inexact process, because MIR isn't designed to +/// capture source spans at the level of detail we would want for coverage, +/// but it's good enough to be better than nothing. +pub(crate) fn extract_raw_spans_from_mir<'tcx>( + mir_body: &mir::Body<'tcx>, graph: &CoverageGraph, -) -> ExtractedCovspans { - let &ExtractedHirInfo { body_span, .. } = hir_info; - - let mut covspans = vec![]; +) -> Vec { + let mut raw_spans = vec![]; + // We only care about blocks that are part of the coverage graph. for (bcb, bcb_data) in graph.iter_enumerated() { - bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data, &mut covspans); - } + let make_raw_span = |raw_span: Span| RawSpanFromMir { raw_span, bcb }; - // Only add the signature span if we found at least one span in the body. - if !covspans.is_empty() { - // If there is no usable signature span, add a fake one (before refinement) - // to avoid an ugly gap between the body start and the first real span. - // FIXME: Find a more principled way to solve this problem. - let fn_sig_span = hir_info.fn_sig_span_extended.unwrap_or_else(|| body_span.shrink_to_lo()); - covspans.push(SpanFromMir::for_fn_sig(fn_sig_span)); - } + // A coverage graph node can consist of multiple basic blocks. + for &bb in &bcb_data.basic_blocks { + let bb_data = &mir_body[bb]; - ExtractedCovspans { covspans } -} + let statements = bb_data.statements.iter(); + raw_spans.extend(statements.filter_map(filtered_statement_span).map(make_raw_span)); -// Generate a set of coverage spans from the filtered set of `Statement`s and `Terminator`s of -// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One coverage span is generated -// for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will -// merge some coverage spans, at which point a coverage span may represent multiple -// `Statement`s and/or `Terminator`s.) -fn bcb_to_initial_coverage_spans<'a, 'tcx>( - mir_body: &'a mir::Body<'tcx>, - body_span: Span, - bcb: BasicCoverageBlock, - bcb_data: &'a BasicCoverageBlockData, - initial_covspans: &mut Vec, -) { - for &bb in &bcb_data.basic_blocks { - let data = &mir_body[bb]; - - let unexpand = move |expn_span| { - unexpand_into_body_span_with_expn_kind(expn_span, body_span) - // Discard any spans that fill the entire body, because they tend - // to represent compiler-inserted code, e.g. implicitly returning `()`. - .filter(|(span, _)| !span.source_equal(body_span)) - }; - - let mut extract_statement_span = |statement| { - let expn_span = filtered_statement_span(statement)?; - let (span, expn_kind) = unexpand(expn_span)?; - - initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb)); - Some(()) - }; - for statement in data.statements.iter() { - extract_statement_span(statement); + // There's only one terminator, but wrap it in an iterator to + // mirror the handling of statements. + let terminator = iter::once(bb_data.terminator()); + raw_spans.extend(terminator.filter_map(filtered_terminator_span).map(make_raw_span)); } - - let mut extract_terminator_span = |terminator| { - let expn_span = filtered_terminator_span(terminator)?; - let (span, expn_kind) = unexpand(expn_span)?; - - initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb)); - Some(()) - }; - extract_terminator_span(data.terminator()); } + + raw_spans } /// If the MIR `Statement` has a span contributive to computing coverage spans, @@ -155,22 +120,20 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option { // an `if condition { block }` has a span that includes the executed block, if true, // but for coverage, the code region executed, up to *and* through the SwitchInt, // actually stops before the if's block.) - TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG + TerminatorKind::Unreachable | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::SwitchInt { .. } - // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`. | TerminatorKind::FalseEdge { .. } | TerminatorKind::Goto { .. } => None, // Call `func` operand can have a more specific span when part of a chain of calls - TerminatorKind::Call { ref func, .. } - | TerminatorKind::TailCall { ref func, .. } => { + TerminatorKind::Call { ref func, .. } | TerminatorKind::TailCall { ref func, .. } => { let mut span = terminator.source_info.span; - if let mir::Operand::Constant(box constant) = func { - if constant.span.lo() > span.lo() { - span = span.with_lo(constant.span.lo()); - } + if let mir::Operand::Constant(constant) = func + && span.contains(constant.span) + { + span = constant.span; } Some(span) } @@ -182,9 +145,7 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option { | TerminatorKind::Yield { .. } | TerminatorKind::CoroutineDrop | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::InlineAsm { .. } => { - Some(terminator.source_info.span) - } + | TerminatorKind::InlineAsm { .. } => Some(terminator.source_info.span), } } @@ -219,7 +180,7 @@ pub(crate) struct SpanFromMir { } impl SpanFromMir { - fn for_fn_sig(fn_sig_span: Span) -> Self { + pub(crate) fn for_fn_sig(fn_sig_span: Span) -> Self { Self::new(fn_sig_span, None, START_BCB) } diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 4f45d9588a890..727d4a126d21e 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -1,4 +1,4 @@ -use rustc_attr_parsing::InlineAttr; +use rustc_attr_data_structures::InlineAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir::visit::Visitor; diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 90173da17f0fc..99b95e7312bde 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -23,7 +23,7 @@ use rustc_mir_dataflow::lattice::{FlatSet, HasBottom}; use rustc_mir_dataflow::value_analysis::{ Map, PlaceIndex, State, TrackElem, ValueOrPlace, debug_with_context, }; -use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor}; +use rustc_mir_dataflow::{Analysis, ResultsVisitor, visit_reachable_results}; use rustc_span::DUMMY_SP; use tracing::{debug, debug_span, instrument}; @@ -61,13 +61,14 @@ impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp { let map = Map::new(tcx, body, place_limit); // Perform the actual dataflow analysis. - let analysis = ConstAnalysis::new(tcx, body, map); - let mut results = - debug_span!("analyze").in_scope(|| analysis.iterate_to_fixpoint(tcx, body, None)); + let mut const_ = debug_span!("analyze") + .in_scope(|| ConstAnalysis::new(tcx, body, map).iterate_to_fixpoint(tcx, body, None)); // Collect results and patch the body afterwards. let mut visitor = Collector::new(tcx, &body.local_decls); - debug_span!("collect").in_scope(|| results.visit_reachable_with(body, &mut visitor)); + debug_span!("collect").in_scope(|| { + visit_reachable_results(body, &mut const_.analysis, &const_.results, &mut visitor) + }); let mut patch = visitor.patch; debug_span!("patch").in_scope(|| patch.visit_body_preserves_cfg(body)); } @@ -958,13 +959,13 @@ fn try_write_constant<'tcx>( interp_ok(()) } -impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> { - #[instrument(level = "trace", skip(self, results, statement))] +impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> { + #[instrument(level = "trace", skip(self, analysis, statement))] fn visit_after_early_statement_effect( &mut self, - results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>, + analysis: &mut ConstAnalysis<'_, 'tcx>, state: &State>, - statement: &'mir Statement<'tcx>, + statement: &Statement<'tcx>, location: Location, ) { match &statement.kind { @@ -972,8 +973,8 @@ impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collect OperandCollector { state, visitor: self, - ecx: &mut results.analysis.ecx, - map: &results.analysis.map, + ecx: &mut analysis.ecx, + map: &analysis.map, } .visit_rvalue(rvalue, location); } @@ -981,12 +982,12 @@ impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collect } } - #[instrument(level = "trace", skip(self, results, statement))] + #[instrument(level = "trace", skip(self, analysis, statement))] fn visit_after_primary_statement_effect( &mut self, - results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>, + analysis: &mut ConstAnalysis<'_, 'tcx>, state: &State>, - statement: &'mir Statement<'tcx>, + statement: &Statement<'tcx>, location: Location, ) { match statement.kind { @@ -994,12 +995,9 @@ impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collect // Don't overwrite the assignment if it already uses a constant (to keep the span). } StatementKind::Assign(box (place, _)) => { - if let Some(value) = self.try_make_constant( - &mut results.analysis.ecx, - place, - state, - &results.analysis.map, - ) { + if let Some(value) = + self.try_make_constant(&mut analysis.ecx, place, state, &analysis.map) + { self.patch.assignments.insert(location, value); } } @@ -1009,18 +1007,13 @@ impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collect fn visit_after_early_terminator_effect( &mut self, - results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>, + analysis: &mut ConstAnalysis<'_, 'tcx>, state: &State>, - terminator: &'mir Terminator<'tcx>, + terminator: &Terminator<'tcx>, location: Location, ) { - OperandCollector { - state, - visitor: self, - ecx: &mut results.analysis.ecx, - map: &results.analysis.map, - } - .visit_terminator(terminator, location); + OperandCollector { state, visitor: self, ecx: &mut analysis.ecx, map: &analysis.map } + .visit_terminator(terminator, location); } } diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index 049f13ce96d74..a0db8bdb7ed88 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -80,35 +80,6 @@ impl<'tcx> Visitor<'tcx> for DeduceReadOnly { // `f` passes. Note that function arguments are the only situation in which this problem can // arise: every other use of `move` in MIR doesn't actually write to the value it moves // from. - // - // Anyway, right now this situation doesn't actually arise in practice. Instead, the MIR for - // that function looks like this: - // - // fn f(_1: BigStruct) -> () { - // let mut _0: (); - // let mut _2: BigStruct; - // bb0: { - // _2 = move _1; - // _0 = g(move _2) -> bb1; - // } - // ... - // } - // - // Because of that extra move that MIR construction inserts, `x` (i.e. `_1`) can *in - // practice* safely be marked `readonly`. - // - // To handle the possibility that other optimizations (for example, destination propagation) - // might someday generate MIR like the first example above, we panic upon seeing an argument - // to *our* function that is directly moved into *another* function as an argument. Having - // eliminated that problematic case, we can safely treat moves as copies in this analysis. - // - // In the future, if MIR optimizations cause arguments of a caller to be directly moved into - // the argument of a callee, we can just add that argument to `mutated_args` instead of - // panicking. - // - // Note that, because the problematic MIR is never actually generated, we can't add a test - // case for this. - if let TerminatorKind::Call { ref args, .. } = terminator.kind { for arg in args { if let Operand::Move(place) = arg.node { diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 7395ad496dbdf..4c94a6c524e00 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -171,7 +171,7 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { let live = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, Some("MaybeLiveLocals-DestProp")); let points = DenseLocationMap::new(body); - let mut live = save_as_intervals(&points, body, live); + let mut live = save_as_intervals(&points, body, live.analysis, live.results); // In order to avoid having to collect data for every single pair of locals in the body, we // do not allow doing more than one merge for places that are derived from the same local at diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 57f7893be1b8c..c7feb9e949b4d 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -103,9 +103,8 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { let mut should_cleanup = false; // Also consider newly generated bbs in the same pass - for i in 0..body.basic_blocks.len() { + for parent in body.basic_blocks.indices() { let bbs = &*body.basic_blocks; - let parent = BasicBlock::from_usize(i); let Some(opt_data) = evaluate_candidate(tcx, body, parent) else { continue }; trace!("SUCCESS: found optimization possibility to apply: {opt_data:?}"); @@ -224,7 +223,7 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { // Since this optimization adds new basic blocks and invalidates others, // clean up the cfg to make it nicer for other passes if should_cleanup { - simplify_cfg(body); + simplify_cfg(tcx, body); } } diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 0d8cf524661c8..14f7c2a263b62 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -4,12 +4,12 @@ use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_hir::lang_items::LangItem; use rustc_index::Idx; use rustc_middle::mir::*; -use rustc_middle::span_bug; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; +use rustc_middle::ty::{self, GenericArg, GenericArgsRef, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug, traits}; use rustc_span::DUMMY_SP; -use rustc_span::source_map::Spanned; +use rustc_span::source_map::{Spanned, dummy_spanned}; use tracing::{debug, instrument}; use crate::patch::MirPatch; @@ -94,6 +94,9 @@ pub(crate) trait DropElaborator<'a, 'tcx>: fmt::Debug { fn body(&self) -> &'a Body<'tcx>; fn tcx(&self) -> TyCtxt<'tcx>; fn typing_env(&self) -> ty::TypingEnv<'tcx>; + fn allow_async_drops(&self) -> bool; + + fn terminator_loc(&self, bb: BasicBlock) -> Location; // Drop logic @@ -149,6 +152,7 @@ where path: D::Path, succ: BasicBlock, unwind: Unwind, + dropline: Option, } /// "Elaborates" a drop of `place`/`path` and patches `bb`'s terminator to execute it. @@ -167,11 +171,12 @@ pub(crate) fn elaborate_drop<'b, 'tcx, D>( succ: BasicBlock, unwind: Unwind, bb: BasicBlock, + dropline: Option, ) where D: DropElaborator<'b, 'tcx>, 'tcx: 'b, { - DropCtxt { elaborator, source_info, place, path, succ, unwind }.elaborate_drop(bb) + DropCtxt { elaborator, source_info, place, path, succ, unwind, dropline }.elaborate_drop(bb) } impl<'a, 'b, 'tcx, D> DropCtxt<'a, 'b, 'tcx, D> @@ -195,6 +200,236 @@ where self.elaborator.tcx() } + // Generates three blocks: + // * #1:pin_obj_bb: call Pin::new_unchecked(&mut obj) + // * #2:call_drop_bb: fut = call obj.() OR call async_drop_in_place(obj) + // * #3:drop_term_bb: drop (obj, fut, ...) + // We keep async drop unexpanded to poll-loop here, to expand it later, at StateTransform - + // into states expand. + // call_destructor_only - to call only AsyncDrop::drop, not full async_drop_in_place glue + fn build_async_drop( + &mut self, + place: Place<'tcx>, + drop_ty: Ty<'tcx>, + bb: Option, + succ: BasicBlock, + unwind: Unwind, + dropline: Option, + call_destructor_only: bool, + ) -> BasicBlock { + let tcx = self.tcx(); + let span = self.source_info.span; + + let pin_obj_bb = bb.unwrap_or_else(|| { + self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + // Temporary terminator, will be replaced by patch + source_info: self.source_info, + kind: TerminatorKind::Return, + }), + is_cleanup: false, + }) + }); + + let (fut_ty, drop_fn_def_id, trait_args) = if call_destructor_only { + // Resolving obj.() + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::AsyncDrop, Some(span)), + [drop_ty], + ); + let (drop_trait, trait_args) = match tcx.codegen_select_candidate( + ty::TypingEnv::fully_monomorphized().as_query_input(trait_ref), + ) { + Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData { + impl_def_id, + args, + .. + })) => (*impl_def_id, *args), + impl_source => { + span_bug!(span, "invalid `AsyncDrop` impl_source: {:?}", impl_source); + } + }; + // impl_item_refs may be empty if drop fn is not implemented in 'impl AsyncDrop for ...' + // (#140974). + // Such code will report error, so just generate sync drop here and return + let Some(drop_fn_def_id) = + tcx.associated_item_def_ids(drop_trait).into_iter().nth(0).copied() + else { + tcx.dcx().span_delayed_bug( + self.elaborator.body().span, + "AsyncDrop type without correct `async fn drop(...)`.", + ); + self.elaborator.patch().patch_terminator( + pin_obj_bb, + TerminatorKind::Drop { + place, + target: succ, + unwind: unwind.into_action(), + replace: false, + drop: None, + async_fut: None, + }, + ); + return pin_obj_bb; + }; + let drop_fn = Ty::new_fn_def(tcx, drop_fn_def_id, trait_args); + let sig = drop_fn.fn_sig(tcx); + let sig = tcx.instantiate_bound_regions_with_erased(sig); + (sig.output(), drop_fn_def_id, trait_args) + } else { + // Resolving async_drop_in_place function for drop_ty + let drop_fn_def_id = tcx.require_lang_item(LangItem::AsyncDropInPlace, Some(span)); + let trait_args = tcx.mk_args(&[drop_ty.into()]); + let sig = tcx.fn_sig(drop_fn_def_id).instantiate(tcx, trait_args); + let sig = tcx.instantiate_bound_regions_with_erased(sig); + (sig.output(), drop_fn_def_id, trait_args) + }; + + let fut = Place::from(self.new_temp(fut_ty)); + + // #1:pin_obj_bb >>> obj_ref = &mut obj + let obj_ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, drop_ty); + let obj_ref_place = Place::from(self.new_temp(obj_ref_ty)); + + let term_loc = self.elaborator.terminator_loc(pin_obj_bb); + self.elaborator.patch().add_assign( + term_loc, + obj_ref_place, + Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + place, + ), + ); + + // pin_obj_place preparation + let pin_obj_new_unchecked_fn = Ty::new_fn_def( + tcx, + tcx.require_lang_item(LangItem::PinNewUnchecked, Some(span)), + [GenericArg::from(obj_ref_ty)], + ); + let pin_obj_ty = pin_obj_new_unchecked_fn.fn_sig(tcx).output().no_bound_vars().unwrap(); + let pin_obj_place = Place::from(self.new_temp(pin_obj_ty)); + let pin_obj_new_unchecked_fn = Operand::Constant(Box::new(ConstOperand { + span, + user_ty: None, + const_: Const::zero_sized(pin_obj_new_unchecked_fn), + })); + + // #3:drop_term_bb + let drop_term_bb = self.new_block( + unwind, + TerminatorKind::Drop { + place, + target: succ, + unwind: unwind.into_action(), + replace: false, + drop: dropline, + async_fut: Some(fut.local), + }, + ); + + // #2:call_drop_bb + let mut call_statements = Vec::new(); + let drop_arg = if call_destructor_only { + pin_obj_place + } else { + let ty::Adt(adt_def, adt_args) = pin_obj_ty.kind() else { + bug!(); + }; + let obj_ptr_ty = Ty::new_mut_ptr(tcx, drop_ty); + let unwrap_ty = adt_def.non_enum_variant().fields[FieldIdx::ZERO].ty(tcx, adt_args); + let obj_ref_place = Place::from(self.new_temp(unwrap_ty)); + call_statements.push(self.assign( + obj_ref_place, + Rvalue::Use(Operand::Copy(tcx.mk_place_field( + pin_obj_place, + FieldIdx::ZERO, + unwrap_ty, + ))), + )); + + let obj_ptr_place = Place::from(self.new_temp(obj_ptr_ty)); + + let addr = Rvalue::RawPtr(RawPtrKind::Mut, tcx.mk_place_deref(obj_ref_place)); + call_statements.push(self.assign(obj_ptr_place, addr)); + obj_ptr_place + }; + call_statements.push(Statement { + source_info: self.source_info, + kind: StatementKind::StorageLive(fut.local), + }); + + let call_drop_bb = self.new_block_with_statements( + unwind, + call_statements, + TerminatorKind::Call { + func: Operand::function_handle(tcx, drop_fn_def_id, trait_args, span), + args: [Spanned { node: Operand::Move(drop_arg), span: DUMMY_SP }].into(), + destination: fut, + target: Some(drop_term_bb), + unwind: unwind.into_action(), + call_source: CallSource::Misc, + fn_span: self.source_info.span, + }, + ); + + // StorageDead(fut) in self.succ block (at the begin) + self.elaborator.patch().add_statement( + Location { block: self.succ, statement_index: 0 }, + StatementKind::StorageDead(fut.local), + ); + + // #1:pin_obj_bb >>> call Pin::new_unchecked(&mut obj) + self.elaborator.patch().patch_terminator( + pin_obj_bb, + TerminatorKind::Call { + func: pin_obj_new_unchecked_fn, + args: [dummy_spanned(Operand::Move(obj_ref_place))].into(), + destination: pin_obj_place, + target: Some(call_drop_bb), + unwind: unwind.into_action(), + call_source: CallSource::Misc, + fn_span: span, + }, + ); + pin_obj_bb + } + + fn build_drop(&mut self, bb: BasicBlock) { + let drop_ty = self.place_ty(self.place); + if self.tcx().features().async_drop() + && self.elaborator.body().coroutine.is_some() + && self.elaborator.allow_async_drops() + && !self.elaborator.patch_ref().block(self.elaborator.body(), bb).is_cleanup + && drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env()) + { + self.build_async_drop( + self.place, + drop_ty, + Some(bb), + self.succ, + self.unwind, + self.dropline, + false, + ); + } else { + self.elaborator.patch().patch_terminator( + bb, + TerminatorKind::Drop { + place: self.place, + target: self.succ, + unwind: self.unwind.into_action(), + replace: false, + drop: None, + async_fut: None, + }, + ); + } + } + /// This elaborates a single drop instruction, located at `bb`, and /// patches over it. /// @@ -222,15 +457,7 @@ where .patch_terminator(bb, TerminatorKind::Goto { target: self.succ }); } DropStyle::Static => { - self.elaborator.patch().patch_terminator( - bb, - TerminatorKind::Drop { - place: self.place, - target: self.succ, - unwind: self.unwind.into_action(), - replace: false, - }, - ); + self.build_drop(bb); } DropStyle::Conditional => { let drop_bb = self.complete_drop(self.succ, self.unwind); @@ -258,31 +485,27 @@ where ) -> Vec<(Place<'tcx>, Option)> { variant .fields - .iter() - .enumerate() - .map(|(i, f)| { - let field = FieldIdx::new(i); - let subpath = self.elaborator.field_subpath(variant_path, field); + .iter_enumerated() + .map(|(field_idx, field)| { + let subpath = self.elaborator.field_subpath(variant_path, field_idx); let tcx = self.tcx(); assert_eq!(self.elaborator.typing_env().typing_mode, ty::TypingMode::PostAnalysis); - // The type error for normalization may have been in dropck: see - // `compute_drop_data` in rustc_borrowck, in which case we wouldn't have - // deleted the MIR body and could have an error here as well. - let field_ty = match tcx - .try_normalize_erasing_regions(self.elaborator.typing_env(), f.ty(tcx, args)) - { + let field_ty = match tcx.try_normalize_erasing_regions( + self.elaborator.typing_env(), + field.ty(tcx, args), + ) { Ok(t) => t, Err(_) => Ty::new_error( self.tcx(), - self.elaborator - .body() - .tainted_by_errors - .expect("Error in drop elaboration not found by dropck."), + self.tcx().dcx().span_delayed_bug( + self.elaborator.body().span, + "Error normalizing in drop elaboration.", + ), ), }; - (tcx.mk_place_field(base_place, field, field_ty), subpath) + (tcx.mk_place_field(base_place, field_idx, field_ty), subpath) }) .collect() } @@ -293,6 +516,7 @@ where path: Option, succ: BasicBlock, unwind: Unwind, + dropline: Option, ) -> BasicBlock { if let Some(path) = path { debug!("drop_subpath: for std field {:?}", place); @@ -304,6 +528,7 @@ where place, succ, unwind, + dropline, } .elaborated_drop_block() } else { @@ -315,6 +540,7 @@ where place, succ, unwind, + dropline, // Using `self.path` here to condition the drop on // our own drop flag. path: self.path, @@ -329,25 +555,36 @@ where /// /// `unwind_ladder` is such a list of steps in reverse order, /// which is called if the matching step of the drop glue panics. + /// + /// `dropline_ladder` is a similar list of steps in reverse order, + /// which is called if the matching step of the drop glue will contain async drop + /// (expanded later to Yield) and the containing coroutine will be dropped at this point. fn drop_halfladder( &mut self, unwind_ladder: &[Unwind], + dropline_ladder: &[Option], mut succ: BasicBlock, fields: &[(Place<'tcx>, Option)], ) -> Vec { iter::once(succ) - .chain(fields.iter().rev().zip(unwind_ladder).map(|(&(place, path), &unwind_succ)| { - succ = self.drop_subpath(place, path, succ, unwind_succ); - succ - })) + .chain(itertools::izip!(fields.iter().rev(), unwind_ladder, dropline_ladder).map( + |(&(place, path), &unwind_succ, &dropline_to)| { + succ = self.drop_subpath(place, path, succ, unwind_succ, dropline_to); + succ + }, + )) .collect() } - fn drop_ladder_bottom(&mut self) -> (BasicBlock, Unwind) { + fn drop_ladder_bottom(&mut self) -> (BasicBlock, Unwind, Option) { // 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. - (self.drop_flag_reset_block(DropFlagMode::Shallow, self.succ, self.unwind), self.unwind) + ( + self.drop_flag_reset_block(DropFlagMode::Shallow, self.succ, self.unwind), + self.unwind, + self.dropline, + ) } /// Creates a full drop ladder, consisting of 2 connected half-drop-ladders @@ -365,6 +602,22 @@ where /// .c2: /// ELAB(drop location.2 [target=`self.unwind`]) /// + /// For possible-async drops in coroutines we also need dropline ladder + /// .d0 (mainline): + /// ELAB(drop location.0 [target=.d1, unwind=.c1, drop=.e1]) + /// .d1 (mainline): + /// ELAB(drop location.1 [target=.d2, unwind=.c2, drop=.e2]) + /// .d2 (mainline): + /// ELAB(drop location.2 [target=`self.succ`, unwind=`self.unwind`, drop=`self.drop`]) + /// .c1 (unwind): + /// ELAB(drop location.1 [target=.c2]) + /// .c2 (unwind): + /// ELAB(drop location.2 [target=`self.unwind`]) + /// .e1 (dropline): + /// ELAB(drop location.1 [target=.e2, unwind=.c2]) + /// .e2 (dropline): + /// ELAB(drop location.2 [target=`self.drop`, unwind=`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( @@ -372,8 +625,13 @@ where fields: Vec<(Place<'tcx>, Option)>, succ: BasicBlock, unwind: Unwind, - ) -> (BasicBlock, Unwind) { + dropline: Option, + ) -> (BasicBlock, Unwind, Option) { debug!("drop_ladder({:?}, {:?})", self, fields); + assert!( + if unwind.is_cleanup() { dropline.is_none() } else { true }, + "Dropline is set for cleanup drop ladder" + ); let mut fields = fields; fields.retain(|&(place, _)| { @@ -382,17 +640,28 @@ where debug!("drop_ladder - fields needing drop: {:?}", fields); + let dropline_ladder: Vec> = vec![None; fields.len() + 1]; 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); + let unwind_ladder: Vec<_> = if let Unwind::To(succ) = unwind { + let halfladder = self.drop_halfladder(&unwind_ladder, &dropline_ladder, succ, &fields); halfladder.into_iter().map(Unwind::To).collect() } else { unwind_ladder }; + let dropline_ladder: Vec<_> = if let Some(succ) = dropline { + let halfladder = self.drop_halfladder(&unwind_ladder, &dropline_ladder, succ, &fields); + halfladder.into_iter().map(Some).collect() + } else { + dropline_ladder + }; - let normal_ladder = self.drop_halfladder(&unwind_ladder, succ, &fields); + let normal_ladder = self.drop_halfladder(&unwind_ladder, &dropline_ladder, succ, &fields); - (*normal_ladder.last().unwrap(), *unwind_ladder.last().unwrap()) + ( + *normal_ladder.last().unwrap(), + *unwind_ladder.last().unwrap(), + *dropline_ladder.last().unwrap(), + ) } fn open_drop_for_tuple(&mut self, tys: &[Ty<'tcx>]) -> BasicBlock { @@ -409,8 +678,8 @@ where }) .collect(); - let (succ, unwind) = self.drop_ladder_bottom(); - self.drop_ladder(fields, succ, unwind).0 + let (succ, unwind, dropline) = self.drop_ladder_bottom(); + self.drop_ladder(fields, succ, unwind, dropline).0 } /// Drops the T contained in a `Box` if it has not been moved out of @@ -421,6 +690,7 @@ where args: GenericArgsRef<'tcx>, succ: BasicBlock, unwind: Unwind, + dropline: Option, ) -> BasicBlock { // drop glue is sent straight to codegen // box cannot be directly dereferenced @@ -437,7 +707,7 @@ where let interior = self.tcx().mk_place_deref(Place::from(ptr_local)); let interior_path = self.elaborator.deref_subpath(self.path); - let do_drop_bb = self.drop_subpath(interior, interior_path, succ, unwind); + let do_drop_bb = self.drop_subpath(interior, interior_path, succ, unwind, dropline); let setup_bbd = BasicBlockData { statements: vec![self.assign( @@ -472,19 +742,22 @@ where let skip_contents = adt.is_union() || adt.is_manually_drop(); let contents_drop = if skip_contents { - (self.succ, self.unwind) + (self.succ, self.unwind, self.dropline) } else { self.open_drop_for_adt_contents(adt, args) }; if adt.is_box() { // we need to drop the inside of the box before running the destructor - let succ = self.destructor_call_block(contents_drop); + let succ = self.destructor_call_block_sync((contents_drop.0, contents_drop.1)); let unwind = contents_drop .1 - .map(|unwind| self.destructor_call_block((unwind, Unwind::InCleanup))); + .map(|unwind| self.destructor_call_block_sync((unwind, Unwind::InCleanup))); + let dropline = contents_drop + .2 + .map(|dropline| self.destructor_call_block_sync((dropline, contents_drop.1))); - self.open_drop_for_box_contents(adt, args, succ, unwind) + self.open_drop_for_box_contents(adt, args, succ, unwind, dropline) } else if adt.has_dtor(self.tcx()) { self.destructor_call_block(contents_drop) } else { @@ -496,14 +769,14 @@ where &mut self, adt: ty::AdtDef<'tcx>, args: GenericArgsRef<'tcx>, - ) -> (BasicBlock, Unwind) { - let (succ, unwind) = self.drop_ladder_bottom(); + ) -> (BasicBlock, Unwind, Option) { + let (succ, unwind, dropline) = self.drop_ladder_bottom(); if !adt.is_enum() { let fields = self.move_paths_for_fields(self.place, self.path, adt.variant(FIRST_VARIANT), args); - self.drop_ladder(fields, succ, unwind) + self.drop_ladder(fields, succ, unwind, dropline) } else { - self.open_drop_for_multivariant(adt, args, succ, unwind) + self.open_drop_for_multivariant(adt, args, succ, unwind, dropline) } } @@ -513,11 +786,14 @@ where args: GenericArgsRef<'tcx>, succ: BasicBlock, unwind: Unwind, - ) -> (BasicBlock, Unwind) { + dropline: Option, + ) -> (BasicBlock, Unwind, Option) { 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 dropline_blocks = + if dropline.is_none() { None } else { Some(Vec::with_capacity(adt.variants().len())) }; let mut have_otherwise_with_drop_glue = false; let mut have_otherwise = false; @@ -555,11 +831,16 @@ where 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); + let dropline_ladder: Vec> = vec![None; fields.len() + 1]; + let halfladder = + self.drop_halfladder(&unwind_ladder, &dropline_ladder, unwind, &fields); unwind_blocks.push(halfladder.last().cloned().unwrap()); } - let (normal, _) = self.drop_ladder(fields, succ, unwind); + let (normal, _, drop_bb) = self.drop_ladder(fields, succ, unwind, dropline); normal_blocks.push(normal); + if dropline.is_some() { + dropline_blocks.as_mut().unwrap().push(drop_bb.unwrap()); + } } else { have_otherwise = true; @@ -599,6 +880,9 @@ where Unwind::InCleanup, ) }), + dropline.map(|dropline| { + self.adt_switch_block(adt, dropline_blocks.unwrap(), &values, dropline, unwind) + }), ) } @@ -638,8 +922,8 @@ where 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); + fn destructor_call_block_sync(&mut self, (succ, unwind): (BasicBlock, Unwind)) -> BasicBlock { + debug!("destructor_call_block_sync({:?}, {:?})", self, succ); let tcx = self.tcx(); let drop_trait = tcx.require_lang_item(LangItem::Drop, None); let drop_fn = tcx.associated_item_def_ids(drop_trait)[0]; @@ -687,6 +971,30 @@ where self.drop_flag_test_block(destructor_block, succ, unwind) } + fn destructor_call_block( + &mut self, + (succ, unwind, dropline): (BasicBlock, Unwind, Option), + ) -> BasicBlock { + debug!("destructor_call_block({:?}, {:?})", self, succ); + let ty = self.place_ty(self.place); + if self.tcx().features().async_drop() + && self.elaborator.body().coroutine.is_some() + && self.elaborator.allow_async_drops() + && !unwind.is_cleanup() + && ty.is_async_drop(self.tcx(), self.elaborator.typing_env()) + { + let destructor_block = + self.build_async_drop(self.place, ty, None, succ, unwind, dropline, true); + + let block_start = Location { block: destructor_block, statement_index: 0 }; + self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); + + self.drop_flag_test_block(destructor_block, succ, unwind) + } else { + self.destructor_call_block_sync((succ, unwind)) + } + } + /// Create a loop that drops an array: /// /// ```text @@ -705,6 +1013,7 @@ where len: Local, ety: Ty<'tcx>, unwind: Unwind, + dropline: Option, ) -> BasicBlock { let copy = |place: Place<'tcx>| Operand::Copy(place); let move_ = |place: Place<'tcx>| Operand::Move(place); @@ -748,16 +1057,35 @@ where }; 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_action(), - replace: false, - }, - ); - + let place = tcx.mk_place_deref(ptr); + if self.tcx().features().async_drop() + && self.elaborator.body().coroutine.is_some() + && self.elaborator.allow_async_drops() + && !unwind.is_cleanup() + && ety.needs_async_drop(self.tcx(), self.elaborator.typing_env()) + { + self.build_async_drop( + place, + ety, + Some(drop_block), + loop_block, + unwind, + dropline, + false, + ); + } else { + self.elaborator.patch().patch_terminator( + drop_block, + TerminatorKind::Drop { + place, + target: loop_block, + unwind: unwind.into_action(), + replace: false, + drop: None, + async_fut: None, + }, + ); + } loop_block } @@ -824,8 +1152,8 @@ where (tcx.mk_place_elem(self.place, project), path) }) .collect::>(); - let (succ, unwind) = self.drop_ladder_bottom(); - return self.drop_ladder(fields, succ, unwind).0; + let (succ, unwind, dropline) = self.drop_ladder_bottom(); + return self.drop_ladder(fields, succ, unwind, dropline).0; } } @@ -859,7 +1187,7 @@ where &mut self.place, Place::from(slice_ptr).project_deeper(&[PlaceElem::Deref], tcx), ); - let slice_block = self.drop_loop_pair_for_slice(ety); + let slice_block = self.drop_loop_trio_for_slice(ety); self.place = array_place; delegate_block.terminator = Some(Terminator { @@ -869,18 +1197,22 @@ where self.elaborator.patch().new_block(delegate_block) } - /// Creates a pair of drop-loops of `place`, which drops its contents, even - /// in the case of 1 panic. - fn drop_loop_pair_for_slice(&mut self, ety: Ty<'tcx>) -> BasicBlock { - debug!("drop_loop_pair_for_slice({:?})", ety); + /// Creates a trio of drop-loops of `place`, which drops its contents, even + /// in the case of 1 panic or in the case of coroutine drop + fn drop_loop_trio_for_slice(&mut self, ety: Ty<'tcx>) -> BasicBlock { + debug!("drop_loop_trio_for_slice({:?})", ety); let tcx = self.tcx(); let len = self.new_temp(tcx.types.usize); let cur = self.new_temp(tcx.types.usize); - let unwind = - self.unwind.map(|unwind| self.drop_loop(unwind, cur, len, ety, Unwind::InCleanup)); + let unwind = self + .unwind + .map(|unwind| self.drop_loop(unwind, cur, len, ety, Unwind::InCleanup, None)); - let loop_block = self.drop_loop(self.succ, cur, len, ety, unwind); + let dropline = + self.dropline.map(|dropline| self.drop_loop(dropline, cur, len, ety, unwind, None)); + + let loop_block = self.drop_loop(self.succ, cur, len, ety, unwind, dropline); let [PlaceElem::Deref] = self.place.projection.as_slice() else { span_bug!( @@ -944,7 +1276,7 @@ where let size = size.try_to_target_usize(self.tcx()); self.open_drop_for_array(ty, *ety, size) } - ty::Slice(ety) => self.drop_loop_pair_for_slice(*ety), + ty::Slice(ety) => self.drop_loop_trio_for_slice(*ety), _ => span_bug!(self.source_info.span, "open drop from non-ADT `{:?}`", ty), } @@ -981,21 +1313,53 @@ where fn elaborated_drop_block(&mut self) -> BasicBlock { debug!("elaborated_drop_block({:?})", self); - let blk = self.drop_block(self.succ, self.unwind); + let blk = self.drop_block_simple(self.succ, self.unwind); self.elaborate_drop(blk); blk } - fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { + fn drop_block_simple(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { let block = TerminatorKind::Drop { place: self.place, target, unwind: unwind.into_action(), replace: false, + drop: self.dropline, + async_fut: None, }; self.new_block(unwind, block) } + fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { + let drop_ty = self.place_ty(self.place); + if self.tcx().features().async_drop() + && self.elaborator.body().coroutine.is_some() + && self.elaborator.allow_async_drops() + && !unwind.is_cleanup() + && drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env()) + { + self.build_async_drop( + self.place, + drop_ty, + None, + self.succ, + unwind, + self.dropline, + false, + ) + } else { + let block = TerminatorKind::Drop { + place: self.place, + target, + unwind: unwind.into_action(), + replace: false, + drop: None, + async_fut: None, + }; + 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) @@ -1037,6 +1401,19 @@ where }) } + fn new_block_with_statements( + &mut self, + unwind: Unwind, + statements: Vec>, + k: TerminatorKind<'tcx>, + ) -> BasicBlock { + self.elaborator.patch().new_block(BasicBlockData { + statements, + 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) } diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 530c72ca549a6..42c8cb0b906e8 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -158,6 +158,14 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for ElaborateDropsCtxt<'a, 'tcx> { self.env.typing_env } + fn allow_async_drops(&self) -> bool { + true + } + + fn terminator_loc(&self, bb: BasicBlock) -> Location { + self.patch.terminator_loc(self.body, bb) + } + #[instrument(level = "debug", skip(self), ret)] fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle { let ((maybe_init, maybe_uninit), multipart) = match mode { @@ -328,7 +336,9 @@ impl<'a, 'tcx> ElaborateDropsCtxt<'a, 'tcx> { // This function should mirror what `collect_drop_flags` does. for (bb, data) in self.body.basic_blocks.iter_enumerated() { let terminator = data.terminator(); - let TerminatorKind::Drop { place, target, unwind, replace } = terminator.kind else { + let TerminatorKind::Drop { place, target, unwind, replace, drop, async_fut: _ } = + terminator.kind + else { continue; }; @@ -364,7 +374,16 @@ impl<'a, 'tcx> ElaborateDropsCtxt<'a, 'tcx> { } }; self.init_data.seek_before(self.body.terminator_loc(bb)); - elaborate_drop(self, terminator.source_info, place, path, target, unwind, bb) + elaborate_drop( + self, + terminator.source_info, + place, + path, + target, + unwind, + bb, + drop, + ) } LookupResult::Parent(None) => {} LookupResult::Parent(Some(_)) => { diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 29698b0c2e445..5b03a4987ed71 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -158,6 +158,26 @@ pub(crate) struct MustNotSuspendReason { pub reason: String, } +pub(crate) struct UnnecessaryTransmute { + pub span: Span, + pub sugg: String, + pub help: Option<&'static str>, +} + +// Needed for def_path_str +impl<'a> LintDiagnostic<'a, ()> for UnnecessaryTransmute { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { + diag.primary_message(fluent::mir_transform_unnecessary_transmute); + diag.span_suggestion( + self.span, + "replace this with", + self.sugg, + lint::Applicability::MachineApplicable, + ); + self.help.map(|help| diag.help(help)); + } +} + #[derive(LintDiagnostic)] #[diag(mir_transform_undefined_transmute)] #[note] diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index 7b3553e7afd06..abbff1c48dd9a 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -53,11 +53,7 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { // Rust calls cannot themselves create foreign unwinds. // We assume this is true for intrinsics as well. - if let ExternAbi::RustIntrinsic - | ExternAbi::Rust - | ExternAbi::RustCall - | ExternAbi::RustCold = sig.abi() - { + if sig.abi().is_rustic_abi() { continue; }; diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 981dedd5b5c7e..209e818e9e32d 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -3,14 +3,16 @@ //! MIR may contain repeated and/or redundant computations. The objective of this pass is to detect //! such redundancies and re-use the already-computed result when possible. //! -//! In a first pass, we compute a symbolic representation of values that are assigned to SSA -//! locals. This symbolic representation is defined by the `Value` enum. Each produced instance of -//! `Value` is interned as a `VnIndex`, which allows us to cheaply compute identical values. -//! //! From those assignments, we construct a mapping `VnIndex -> Vec<(Local, Location)>` of available //! values, the locals in which they are stored, and the assignment location. //! -//! In a second pass, we traverse all (non SSA) assignments `x = rvalue` and operands. For each +//! We traverse all assignments `x = rvalue` and operands. +//! +//! For each SSA one, we compute a symbolic representation of values that are assigned to SSA +//! locals. This symbolic representation is defined by the `Value` enum. Each produced instance of +//! `Value` is interned as a `VnIndex`, which allows us to cheaply compute identical values. +//! +//! For each non-SSA //! one, we compute the `VnIndex` of the rvalue. If this `VnIndex` is associated to a constant, we //! replace the rvalue/operand by that constant. Otherwise, if there is an SSA local `y` //! associated to this `VnIndex`, and if its definition location strictly dominates the assignment @@ -91,7 +93,7 @@ use rustc_const_eval::interpret::{ ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar, intern_const_alloc_for_constprop, }; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxIndexSet, MutableValues}; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; @@ -107,7 +109,7 @@ use rustc_span::def_id::DefId; use smallvec::SmallVec; use tracing::{debug, instrument, trace}; -use crate::ssa::{AssignedValue, SsaLocals}; +use crate::ssa::SsaLocals; pub(super) struct GVN; @@ -126,31 +128,11 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { let dominators = body.basic_blocks.dominators().clone(); let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls); - ssa.for_each_assignment_mut( - body.basic_blocks.as_mut_preserves_cfg(), - |local, value, location| { - let value = match value { - // We do not know anything of this assigned value. - AssignedValue::Arg | AssignedValue::Terminator => None, - // Try to get some insight. - AssignedValue::Rvalue(rvalue) => { - let value = state.simplify_rvalue(rvalue, location); - // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark - // `local` as reusable if we have an exact type match. - if state.local_decls[local].ty != rvalue.ty(state.local_decls, tcx) { - return; - } - value - } - }; - // `next_opaque` is `Some`, so `new_opaque` must return `Some`. - let value = value.or_else(|| state.new_opaque()).unwrap(); - state.assign(local, value); - }, - ); - // Stop creating opaques during replacement as it is useless. - state.next_opaque = None; + for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) { + let opaque = state.new_opaque(); + state.assign(local, opaque); + } let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec(); for bb in reverse_postorder { @@ -250,14 +232,14 @@ struct VnState<'body, 'tcx> { locals: IndexVec>, /// Locals that are assigned that value. // This vector does not hold all the values of `VnIndex` that we create. - // It stops at the largest value created in the first phase of collecting assignments. rev_locals: IndexVec>, values: FxIndexSet>, /// Values evaluated as constants if possible. evaluated: IndexVec>>, /// Counter to generate different values. - /// This is an option to stop creating opaques during replacement. - next_opaque: Option, + next_opaque: usize, + /// Cache the deref values. + derefs: Vec, /// Cache the value of the `unsized_locals` features, to avoid fetching it repeatedly in a loop. feature_unsized_locals: bool, ssa: &'body SsaLocals, @@ -289,7 +271,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { rev_locals: IndexVec::with_capacity(num_values), values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()), evaluated: IndexVec::with_capacity(num_values), - next_opaque: Some(1), + next_opaque: 1, + derefs: Vec::new(), feature_unsized_locals: tcx.features().unsized_locals(), ssa, dominators, @@ -310,32 +293,31 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let evaluated = self.eval_to_const(index); let _index = self.evaluated.push(evaluated); debug_assert_eq!(index, _index); - // No need to push to `rev_locals` if we finished listing assignments. - if self.next_opaque.is_some() { - let _index = self.rev_locals.push(SmallVec::new()); - debug_assert_eq!(index, _index); - } + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); } index } + fn next_opaque(&mut self) -> usize { + let next_opaque = self.next_opaque; + self.next_opaque += 1; + next_opaque + } + /// Create a new `Value` for which we have no information at all, except that it is distinct /// from all the others. #[instrument(level = "trace", skip(self), ret)] - fn new_opaque(&mut self) -> Option { - let next_opaque = self.next_opaque.as_mut()?; - let value = Value::Opaque(*next_opaque); - *next_opaque += 1; - Some(self.insert(value)) + fn new_opaque(&mut self) -> VnIndex { + let value = Value::Opaque(self.next_opaque()); + self.insert(value) } /// Create a new `Value::Address` distinct from all the others. #[instrument(level = "trace", skip(self), ret)] - fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> Option { - let next_opaque = self.next_opaque.as_mut()?; - let value = Value::Address { place, kind, provenance: *next_opaque }; - *next_opaque += 1; - Some(self.insert(value)) + fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex { + let value = Value::Address { place, kind, provenance: self.next_opaque() }; + self.insert(value) } fn get(&self, index: VnIndex) -> &Value<'tcx> { @@ -345,6 +327,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { /// Record that `local` is assigned `value`. `local` must be SSA. #[instrument(level = "trace", skip(self))] fn assign(&mut self, local: Local, value: VnIndex) { + debug_assert!(self.ssa.is_ssa(local)); self.locals[local] = Some(value); // Only register the value if its type is `Sized`, as we will emit copies of it. @@ -355,21 +338,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - fn insert_constant(&mut self, value: Const<'tcx>) -> Option { + fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { let disambiguator = if value.is_deterministic() { // The constant is deterministic, no need to disambiguate. 0 } else { // Multiple mentions of this constant will yield different values, // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. - let next_opaque = self.next_opaque.as_mut()?; - let disambiguator = *next_opaque; - *next_opaque += 1; + let disambiguator = self.next_opaque(); // `disambiguator: 0` means deterministic. debug_assert_ne!(disambiguator, 0); disambiguator }; - Some(self.insert(Value::Constant { value, disambiguator })) + self.insert(Value::Constant { value, disambiguator }) } fn insert_bool(&mut self, flag: bool) -> VnIndex { @@ -390,6 +371,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values)) } + fn insert_deref(&mut self, value: VnIndex) -> VnIndex { + let value = self.insert(Value::Projection(value, ProjectionElem::Deref)); + self.derefs.push(value); + value + } + + fn invalidate_derefs(&mut self) { + for deref in std::mem::take(&mut self.derefs) { + let opaque = self.next_opaque(); + *self.values.get_index_mut2(deref.index()).unwrap() = Value::Opaque(opaque); + } + } + #[instrument(level = "trace", skip(self), ret)] fn eval_to_const(&mut self, value: VnIndex) -> Option> { use Value::*; @@ -648,15 +642,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let proj = match proj { ProjectionElem::Deref => { let ty = place.ty(self.local_decls, self.tcx).ty; - // unsound: https://github.com/rust-lang/rust/issues/130853 - if self.tcx.sess.opts.unstable_opts.unsound_mir_opts - && let Some(Mutability::Not) = ty.ref_mutability() + if let Some(Mutability::Not) = ty.ref_mutability() && let Some(pointee_ty) = ty.builtin_deref(true) && pointee_ty.is_freeze(self.tcx, self.typing_env()) { // An immutable borrow `_x` always points to the same value for the // lifetime of the borrow, so we can merge all instances of `*_x`. - ProjectionElem::Deref + return Some(self.insert_deref(value)); } else { return None; } @@ -830,7 +822,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { location: Location, ) -> Option { match *operand { - Operand::Constant(ref constant) => self.insert_constant(constant.const_), + Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)), Operand::Copy(ref mut place) | Operand::Move(ref mut place) => { let value = self.simplify_place_value(place, location)?; if let Some(const_) = self.try_as_constant(value) { @@ -844,6 +836,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn simplify_rvalue( &mut self, + lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option { @@ -863,17 +856,23 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Value::Repeat(op, amount) } Rvalue::NullaryOp(op, ty) => Value::NullaryOp(op, ty), - Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location), + Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location), Rvalue::Ref(_, borrow_kind, ref mut place) => { self.simplify_place_projection(place, location); - return self.new_pointer(*place, AddressKind::Ref(borrow_kind)); + return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind))); } Rvalue::RawPtr(mutbl, ref mut place) => { self.simplify_place_projection(place, location); - return self.new_pointer(*place, AddressKind::Address(mutbl)); + return Some(self.new_pointer(*place, AddressKind::Address(mutbl))); } - Rvalue::WrapUnsafeBinder(ref mut op, _) => { - return self.simplify_operand(op, location); + Rvalue::WrapUnsafeBinder(ref mut op, ty) => { + let value = self.simplify_operand(op, location)?; + Value::Cast { + kind: CastKind::Transmute, + value, + from: op.ty(self.local_decls, self.tcx), + to: ty, + } } // Operations. @@ -945,6 +944,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_aggregate_to_copy( &mut self, + lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, fields: &[VnIndex], @@ -982,31 +982,26 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - let tcx = self.tcx; - let mut projection = SmallVec::<[PlaceElem<'tcx>; 1]>::new(); - loop { - if let Some(local) = self.try_as_local(copy_from_local_value, location) { - projection.reverse(); - let place = Place { local, projection: tcx.mk_place_elems(projection.as_slice()) }; - if rvalue.ty(self.local_decls, tcx) == place.ty(self.local_decls, tcx).ty { - self.reused_locals.insert(local); - *rvalue = Rvalue::Use(Operand::Copy(place)); - return Some(copy_from_value); - } - return None; - } else if let Value::Projection(pointer, proj) = *self.get(copy_from_local_value) - && let Some(proj) = self.try_as_place_elem(proj, location) - { - projection.push(proj); - copy_from_local_value = pointer; - } else { - return None; + // Allow introducing places with non-constant offsets, as those are still better than + // reconstructing an aggregate. + if let Some(place) = self.try_as_place(copy_from_local_value, location, true) + && rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty + { + // Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments. + // FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections. + if lhs.as_local().is_some() { + self.reused_locals.insert(place.local); + *rvalue = Rvalue::Use(Operand::Copy(place)); } + return Some(copy_from_local_value); } + + None } fn simplify_aggregate( &mut self, + lhs: &Place<'tcx>, rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option { @@ -1028,7 +1023,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if is_zst { let ty = rvalue.ty(self.local_decls, tcx); - return self.insert_constant(Const::zero_sized(ty)); + return Some(self.insert_constant(Const::zero_sized(ty))); } } @@ -1057,11 +1052,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } }; - let fields: Option> = field_ops + let mut fields: Vec<_> = field_ops .iter_mut() - .map(|op| self.simplify_operand(op, location).or_else(|| self.new_opaque())) + .map(|op| self.simplify_operand(op, location).unwrap_or_else(|| self.new_opaque())) .collect(); - let mut fields = fields?; if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty { let mut was_updated = false; @@ -1101,11 +1095,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - // unsound: https://github.com/rust-lang/rust/issues/132353 - if tcx.sess.opts.unstable_opts.unsound_mir_opts - && let AggregateTy::Def(_, _) = ty + if let AggregateTy::Def(_, _) = ty && let Some(value) = - self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index) + self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index) { return Some(value); } @@ -1189,7 +1181,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind() && let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() => { - return self.insert_constant(Const::Ty(self.tcx.types.usize, *len)); + return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); } _ => Value::UnaryOp(op, arg_index), }; @@ -1385,7 +1377,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_), _) = kind { // Each reification of a generic fn may get a different pointer. // Do not try to merge them. - return self.new_opaque(); + return Some(self.new_opaque()); } let mut was_ever_updated = false; @@ -1501,7 +1493,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Trivial case: we are fetching a statically known length. let place_ty = place.ty(self.local_decls, self.tcx).ty; if let ty::Array(_, len) = place_ty.kind() { - return self.insert_constant(Const::Ty(self.tcx.types.usize, *len)); + return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); } let mut inner = self.simplify_place_value(place, location)?; @@ -1523,7 +1515,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && let Some(to) = to.builtin_deref(true) && let ty::Slice(..) = to.kind() { - return self.insert_constant(Const::Ty(self.tcx.types.usize, *len)); + return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); } // Fallback: a symbolic `Len`. @@ -1677,14 +1669,14 @@ fn op_to_prop_const<'tcx>( } impl<'tcx> VnState<'_, 'tcx> { - /// If either [`Self::try_as_constant`] as [`Self::try_as_local`] succeeds, + /// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds, /// returns that result as an [`Operand`]. fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option> { if let Some(const_) = self.try_as_constant(index) { Some(Operand::Constant(Box::new(const_))) - } else if let Some(local) = self.try_as_local(index, location) { - self.reused_locals.insert(local); - Some(Operand::Copy(local.into())) + } else if let Some(place) = self.try_as_place(index, location, false) { + self.reused_locals.insert(place.local); + Some(Operand::Copy(place)) } else { None } @@ -1717,6 +1709,35 @@ impl<'tcx> VnState<'_, 'tcx> { Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ }) } + /// Construct a place which holds the same value as `index` and for which all locals strictly + /// dominate `loc`. If you used this place, add its base local to `reused_locals` to remove + /// storage statements. + #[instrument(level = "trace", skip(self), ret)] + fn try_as_place( + &mut self, + mut index: VnIndex, + loc: Location, + allow_complex_projection: bool, + ) -> Option> { + let mut projection = SmallVec::<[PlaceElem<'tcx>; 1]>::new(); + loop { + if let Some(local) = self.try_as_local(index, loc) { + projection.reverse(); + let place = + Place { local, projection: self.tcx.mk_place_elems(projection.as_slice()) }; + return Some(place); + } else if let Value::Projection(pointer, proj) = *self.get(index) + && (allow_complex_projection || proj.is_stable_offset()) + && let Some(proj) = self.try_as_place_elem(proj, loc) + { + projection.push(proj); + index = pointer; + } else { + return None; + } + } + } + /// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`, /// return it. If you used this local, add it to `reused_locals` to remove storage statements. fn try_as_local(&mut self, index: VnIndex, loc: Location) -> Option { @@ -1733,41 +1754,71 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { self.tcx } - fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, location: Location) { + fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { self.simplify_place_projection(place, location); + if context.is_mutating_use() && !place.projection.is_empty() { + // Non-local mutation maybe invalidate deref. + self.invalidate_derefs(); + } + self.super_place(place, context, location); } fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) { self.simplify_operand(operand, location); + self.super_operand(operand, location); } fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) { if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind { self.simplify_place_projection(lhs, location); - // Do not try to simplify a constant, it's already in canonical shape. - if matches!(rvalue, Rvalue::Use(Operand::Constant(_))) { - return; + let value = self.simplify_rvalue(lhs, rvalue, location); + let value = if let Some(local) = lhs.as_local() + && self.ssa.is_ssa(local) + // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark + // `local` as reusable if we have an exact type match. + && self.local_decls[local].ty == rvalue.ty(self.local_decls, self.tcx) + { + let value = value.unwrap_or_else(|| self.new_opaque()); + self.assign(local, value); + Some(value) + } else { + value + }; + if let Some(value) = value { + if let Some(const_) = self.try_as_constant(value) { + *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_))); + } else if let Some(place) = self.try_as_place(value, location, false) + && *rvalue != Rvalue::Use(Operand::Move(place)) + && *rvalue != Rvalue::Use(Operand::Copy(place)) + { + *rvalue = Rvalue::Use(Operand::Copy(place)); + self.reused_locals.insert(place.local); + } } + } + self.super_statement(stmt, location); + } - let value = lhs - .as_local() - .and_then(|local| self.locals[local]) - .or_else(|| self.simplify_rvalue(rvalue, location)); - let Some(value) = value else { return }; - - if let Some(const_) = self.try_as_constant(value) { - *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_))); - } else if let Some(local) = self.try_as_local(value, location) - && *rvalue != Rvalue::Use(Operand::Move(local.into())) + fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { + if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator { + if let Some(local) = destination.as_local() + && self.ssa.is_ssa(local) { - *rvalue = Rvalue::Use(Operand::Copy(local.into())); - self.reused_locals.insert(local); + let opaque = self.new_opaque(); + self.assign(local, opaque); } - - return; } - self.super_statement(stmt, location); + // Function calls and ASM may invalidate (nested) derefs. We must handle them carefully. + // Currently, only preserving derefs for trivial terminators like SwitchInt and Goto. + let safe_to_preserve_derefs = matches!( + terminator.kind, + TerminatorKind::SwitchInt { .. } | TerminatorKind::Goto { .. } + ); + if !safe_to_preserve_derefs { + self.invalidate_derefs(); + } + self.super_terminator(terminator, location); } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 0183ba19475c1..f48dba9663a52 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -1,10 +1,11 @@ //! Inlining pass for MIR functions. +use std::assert_matches::debug_assert_matches; use std::iter; use std::ops::{Range, RangeFrom}; use rustc_abi::{ExternAbi, FieldIdx}; -use rustc_attr_parsing::{InlineAttr, OptimizeAttr}; +use rustc_attr_data_structures::{InlineAttr, OptimizeAttr}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_index::Idx; @@ -18,7 +19,7 @@ use rustc_session::config::{DebugInfo, OptLevel}; use rustc_span::source_map::Spanned; use tracing::{debug, instrument, trace, trace_span}; -use crate::cost_checker::CostChecker; +use crate::cost_checker::{CostChecker, is_call_like}; use crate::deref_separator::deref_finder; use crate::simplify::simplify_cfg; use crate::validate::validate_types; @@ -26,6 +27,7 @@ use crate::{check_inline, util}; pub(crate) mod cycle; +const HISTORY_DEPTH_LIMIT: usize = 20; const TOP_DOWN_DEPTH_LIMIT: usize = 5; #[derive(Clone, Debug)] @@ -61,7 +63,7 @@ impl<'tcx> crate::MirPass<'tcx> for Inline { let _guard = span.enter(); if inline::>(tcx, body) { debug!("running simplify cfg on {:?}", body.source); - simplify_cfg(body); + simplify_cfg(tcx, body); deref_finder(tcx, body); } } @@ -97,7 +99,7 @@ impl<'tcx> crate::MirPass<'tcx> for ForceInline { let _guard = span.enter(); if inline::>(tcx, body) { debug!("running simplify cfg on {:?}", body.source); - simplify_cfg(body); + simplify_cfg(tcx, body); deref_finder(tcx, body); } } @@ -117,6 +119,11 @@ trait Inliner<'tcx> { /// Should inlining happen for a given callee? fn should_inline_for_callee(&self, def_id: DefId) -> bool; + fn check_codegen_attributes_extra( + &self, + callee_attrs: &CodegenFnAttrs, + ) -> Result<(), &'static str>; + fn check_caller_mir_body(&self, body: &Body<'tcx>) -> bool; /// Returns inlining decision that is based on the examination of callee MIR body. @@ -128,10 +135,6 @@ trait Inliner<'tcx> { callee_attrs: &CodegenFnAttrs, ) -> Result<(), &'static str>; - // How many callsites in a body are we allowed to inline? We need to limit this in order - // to prevent super-linear growth in MIR size. - fn inline_limit_for_block(&self) -> Option; - /// Called when inlining succeeds. fn on_inline_success( &mut self, @@ -142,9 +145,6 @@ trait Inliner<'tcx> { /// Called when inlining failed or was not performed. fn on_inline_failure(&self, callsite: &CallSite<'tcx>, reason: &'static str); - - /// Called when the inline limit for a body is reached. - fn on_inline_limit_reached(&self) -> bool; } struct ForceInliner<'tcx> { @@ -191,6 +191,14 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> { ForceInline::should_run_pass_for_callee(self.tcx(), def_id) } + fn check_codegen_attributes_extra( + &self, + callee_attrs: &CodegenFnAttrs, + ) -> Result<(), &'static str> { + debug_assert_matches!(callee_attrs.inline, InlineAttr::Force { .. }); + Ok(()) + } + fn check_caller_mir_body(&self, _: &Body<'tcx>) -> bool { true } @@ -224,10 +232,6 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> { } } - fn inline_limit_for_block(&self) -> Option { - Some(usize::MAX) - } - fn on_inline_success( &mut self, callsite: &CallSite<'tcx>, @@ -261,10 +265,6 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> { justification: justification.map(|sym| crate::errors::ForceInlineJustification { sym }), }); } - - fn on_inline_limit_reached(&self) -> bool { - false - } } struct NormalInliner<'tcx> { @@ -278,6 +278,10 @@ struct NormalInliner<'tcx> { /// The number of `DefId`s is finite, so checking history is enough /// to ensure that we do not loop endlessly while inlining. history: Vec, + /// How many (multi-call) callsites have we inlined for the top-level call? + /// + /// We need to limit this in order to prevent super-linear growth in MIR size. + top_down_counter: usize, /// Indicates that the caller body has been modified. changed: bool, /// Indicates that the caller is #[inline] and just calls another function, @@ -285,6 +289,12 @@ struct NormalInliner<'tcx> { caller_is_inline_forwarder: bool, } +impl<'tcx> NormalInliner<'tcx> { + fn past_depth_limit(&self) -> bool { + self.history.len() > HISTORY_DEPTH_LIMIT || self.top_down_counter > TOP_DOWN_DEPTH_LIMIT + } +} + impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> { fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &Body<'tcx>) -> Self { let typing_env = body.typing_env(tcx); @@ -295,6 +305,7 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> { typing_env, def_id, history: Vec::new(), + top_down_counter: 0, changed: false, caller_is_inline_forwarder: matches!( codegen_fn_attrs.inline, @@ -327,6 +338,17 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> { true } + fn check_codegen_attributes_extra( + &self, + callee_attrs: &CodegenFnAttrs, + ) -> Result<(), &'static str> { + if self.past_depth_limit() && matches!(callee_attrs.inline, InlineAttr::None) { + Err("Past depth limit so not inspecting unmarked callee") + } else { + Ok(()) + } + } + fn check_caller_mir_body(&self, body: &Body<'tcx>) -> bool { // Avoid inlining into coroutines, 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 @@ -351,7 +373,11 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> { return Err("body has errors"); } - let mut threshold = if self.caller_is_inline_forwarder { + if self.past_depth_limit() && callee_body.basic_blocks.len() > 1 { + return Err("Not inlining multi-block body as we're past a depth limit"); + } + + let mut threshold = if self.caller_is_inline_forwarder || self.past_depth_limit() { tcx.sess.opts.unstable_opts.inline_mir_forwarder_threshold.unwrap_or(30) } else if tcx.cross_crate_inlinable(callsite.callee.def_id()) { tcx.sess.opts.unstable_opts.inline_mir_hint_threshold.unwrap_or(100) @@ -387,7 +413,15 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> { let term = blk.terminator(); let caller_attrs = tcx.codegen_fn_attrs(self.caller_def_id()); - if let TerminatorKind::Drop { ref place, target, unwind, replace: _ } = term.kind { + if let TerminatorKind::Drop { + ref place, + target, + unwind, + replace: _, + drop: _, + async_fut: _, + } = term.kind + { work_list.push(target); // If the place doesn't actually need dropping, treat it like a regular goto. @@ -431,14 +465,6 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> { } } - fn inline_limit_for_block(&self) -> Option { - match self.history.len() { - 0 => Some(usize::MAX), - 1..=TOP_DOWN_DEPTH_LIMIT => Some(1), - _ => None, - } - } - fn on_inline_success( &mut self, callsite: &CallSite<'tcx>, @@ -447,13 +473,21 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> { ) { self.changed = true; + let new_calls_count = new_blocks + .clone() + .filter(|&bb| is_call_like(caller_body.basic_blocks[bb].terminator())) + .count(); + if new_calls_count > 1 { + self.top_down_counter += 1; + } + self.history.push(callsite.callee.def_id()); process_blocks(self, caller_body, new_blocks); self.history.pop(); - } - fn on_inline_limit_reached(&self) -> bool { - true + if self.history.is_empty() { + self.top_down_counter = 0; + } } fn on_inline_failure(&self, _: &CallSite<'tcx>, _: &'static str) {} @@ -482,8 +516,6 @@ fn process_blocks<'tcx, I: Inliner<'tcx>>( caller_body: &mut Body<'tcx>, blocks: Range, ) { - let Some(inline_limit) = inliner.inline_limit_for_block() else { return }; - let mut inlined_count = 0; for bb in blocks { let bb_data = &caller_body[bb]; if bb_data.is_cleanup { @@ -505,13 +537,6 @@ fn process_blocks<'tcx, I: Inliner<'tcx>>( Ok(new_blocks) => { debug!("inlined {}", callsite.callee); inliner.on_inline_success(&callsite, caller_body, new_blocks); - - inlined_count += 1; - if inlined_count == inline_limit { - if inliner.on_inline_limit_reached() { - return; - } - } } } } @@ -584,6 +609,7 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>( let callee_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id()); check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?; check_codegen_attributes(inliner, callsite, callee_attrs)?; + inliner.check_codegen_attributes_extra(callee_attrs)?; let terminator = caller_body[callsite.block].terminator.as_ref().unwrap(); let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() }; @@ -708,6 +734,20 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>( debug!("still needs substitution"); return Err("implementation limitation -- HACK for dropping polymorphic type"); } + InstanceKind::AsyncDropGlue(_, ty) | InstanceKind::AsyncDropGlueCtorShim(_, ty) => { + return if ty.still_further_specializable() { + Err("still needs substitution") + } else { + Ok(()) + }; + } + InstanceKind::FutureDropPollShim(_, ty, ty2) => { + return if ty.still_further_specializable() || ty2.still_further_specializable() { + Err("still needs substitution") + } else { + Ok(()) + }; + } // This cannot result in an immediate cycle since the callee MIR is a shim, which does // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we @@ -721,8 +761,7 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>( | InstanceKind::DropGlue(..) | InstanceKind::CloneShim(..) | InstanceKind::ThreadLocalShim(..) - | InstanceKind::FnPtrAddrShim(..) - | InstanceKind::AsyncDropGlueCtorShim(..) => return Ok(()), + | InstanceKind::FnPtrAddrShim(..) => return Ok(()), } if inliner.tcx().is_constructor(callee_def_id) { @@ -770,6 +809,8 @@ fn check_codegen_attributes<'tcx, I: Inliner<'tcx>>( return Err("has DoNotOptimize attribute"); } + inliner.check_codegen_attributes_extra(callee_attrs)?; + // Reachability pass defines which functions are eligible for inlining. Generally inlining // other functions is incorrect because they could reference symbols that aren't exported. let is_generic = callsite.callee.args.non_erasable_generics().next().is_some(); @@ -883,9 +924,9 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( let mut integrator = Integrator { args: &args, - new_locals: Local::new(caller_body.local_decls.len()).., - new_scopes: SourceScope::new(caller_body.source_scopes.len()).., - new_blocks: BasicBlock::new(caller_body.basic_blocks.len()).., + new_locals: caller_body.local_decls.next_index().., + new_scopes: caller_body.source_scopes.next_index().., + new_blocks: caller_body.basic_blocks.next_index().., destination: destination_local, callsite_scope: caller_body.source_scopes[callsite.source_info.scope].clone(), callsite, @@ -1149,7 +1190,7 @@ impl Integrator<'_, '_> { if idx < self.args.len() { self.args[idx] } else { - Local::new(self.new_locals.start.index() + (idx - self.args.len())) + self.new_locals.start + (idx - self.args.len()) } }; trace!("mapping local `{:?}` to `{:?}`", local, new); @@ -1157,13 +1198,13 @@ impl Integrator<'_, '_> { } fn map_scope(&self, scope: SourceScope) -> SourceScope { - let new = SourceScope::new(self.new_scopes.start.index() + scope.index()); + let new = self.new_scopes.start + scope.index(); trace!("mapping scope `{:?}` to `{:?}`", scope, new); new } fn map_block(&self, block: BasicBlock) -> BasicBlock { - let new = BasicBlock::new(self.new_blocks.start.index() + block.index()); + let new = self.new_blocks.start + block.index(); trace!("mapping block `{:?}` to `{:?}`", block, new); new } @@ -1325,8 +1366,8 @@ fn try_instance_mir<'tcx>( tcx: TyCtxt<'tcx>, instance: InstanceKind<'tcx>, ) -> Result<&'tcx Body<'tcx>, &'static str> { - if let ty::InstanceKind::DropGlue(_, Some(ty)) - | ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) = instance + if let ty::InstanceKind::DropGlue(_, Some(ty)) | ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) = + instance && let ty::Adt(def, args) = ty.kind() { let fields = def.all_fields(); diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index a40768300f5d0..292278800f8a2 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -95,7 +95,10 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( InstanceKind::FnPtrAddrShim(..) => { continue; } - InstanceKind::DropGlue(..) | InstanceKind::AsyncDropGlueCtorShim(..) => { + InstanceKind::DropGlue(..) + | InstanceKind::FutureDropPollShim(..) + | InstanceKind::AsyncDropGlue(..) + | InstanceKind::AsyncDropGlueCtorShim(..) => { // FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this // needs some more analysis. diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index da346dfc48c1d..5f0c55ddc092d 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -10,7 +10,6 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, layout}; use rustc_span::{DUMMY_SP, Symbol, sym}; use crate::simplify::simplify_duplicate_switch_targets; -use crate::take_array; pub(super) enum InstSimplify { BeforeInline, @@ -39,26 +38,26 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { attr::contains_name(tcx.hir_krate_attrs(), sym::rustc_preserve_ub_checks); for block in body.basic_blocks.as_mut() { for statement in block.statements.iter_mut() { - match statement.kind { - StatementKind::Assign(box (_place, ref mut rvalue)) => { - if !preserve_ub_checks { - ctx.simplify_ub_check(rvalue); - } - ctx.simplify_bool_cmp(rvalue); - ctx.simplify_ref_deref(rvalue); - ctx.simplify_ptr_aggregate(rvalue); - ctx.simplify_cast(rvalue); - ctx.simplify_repeated_aggregate(rvalue); - ctx.simplify_repeat_once(rvalue); - } - _ => {} + let StatementKind::Assign(box (.., rvalue)) = &mut statement.kind else { + continue; + }; + + if !preserve_ub_checks { + ctx.simplify_ub_check(rvalue); } + ctx.simplify_bool_cmp(rvalue); + ctx.simplify_ref_deref(rvalue); + ctx.simplify_ptr_aggregate(rvalue); + ctx.simplify_cast(rvalue); + ctx.simplify_repeated_aggregate(rvalue); + ctx.simplify_repeat_once(rvalue); } - ctx.simplify_primitive_clone(block.terminator.as_mut().unwrap(), &mut block.statements); - ctx.simplify_intrinsic_assert(block.terminator.as_mut().unwrap()); - ctx.simplify_nounwind_call(block.terminator.as_mut().unwrap()); - simplify_duplicate_switch_targets(block.terminator.as_mut().unwrap()); + let terminator = block.terminator.as_mut().unwrap(); + ctx.simplify_primitive_clone(terminator, &mut block.statements); + ctx.simplify_intrinsic_assert(terminator); + ctx.simplify_nounwind_call(terminator); + simplify_duplicate_switch_targets(terminator); } } @@ -78,20 +77,20 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { /// GVN can also do this optimization, but GVN is only run at mir-opt-level 2 so having this in /// InstSimplify helps unoptimized builds. fn simplify_repeated_aggregate(&self, rvalue: &mut Rvalue<'tcx>) { - let Rvalue::Aggregate(box AggregateKind::Array(_), fields) = rvalue else { + let Rvalue::Aggregate(box AggregateKind::Array(_), fields) = &*rvalue else { return; }; if fields.len() < 5 { return; } - let first = &fields[rustc_abi::FieldIdx::ZERO]; + let (first, rest) = fields[..].split_first().unwrap(); let Operand::Constant(first) = first else { return; }; let Ok(first_val) = first.const_.eval(self.tcx, self.typing_env, first.span) else { return; }; - if fields.iter().all(|field| { + if rest.iter().all(|field| { let Operand::Constant(field) = field else { return false; }; @@ -105,43 +104,34 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { /// Transform boolean comparisons into logical operations. fn simplify_bool_cmp(&self, rvalue: &mut Rvalue<'tcx>) { - match rvalue { - Rvalue::BinaryOp(op @ (BinOp::Eq | BinOp::Ne), box (a, b)) => { - let new = match (op, self.try_eval_bool(a), self.try_eval_bool(b)) { - // Transform "Eq(a, true)" ==> "a" - (BinOp::Eq, _, Some(true)) => Some(Rvalue::Use(a.clone())), + let Rvalue::BinaryOp(op @ (BinOp::Eq | BinOp::Ne), box (a, b)) = &*rvalue else { return }; + *rvalue = match (op, self.try_eval_bool(a), self.try_eval_bool(b)) { + // Transform "Eq(a, true)" ==> "a" + (BinOp::Eq, _, Some(true)) => Rvalue::Use(a.clone()), - // Transform "Ne(a, false)" ==> "a" - (BinOp::Ne, _, Some(false)) => Some(Rvalue::Use(a.clone())), + // Transform "Ne(a, false)" ==> "a" + (BinOp::Ne, _, Some(false)) => Rvalue::Use(a.clone()), - // Transform "Eq(true, b)" ==> "b" - (BinOp::Eq, Some(true), _) => Some(Rvalue::Use(b.clone())), + // Transform "Eq(true, b)" ==> "b" + (BinOp::Eq, Some(true), _) => Rvalue::Use(b.clone()), - // Transform "Ne(false, b)" ==> "b" - (BinOp::Ne, Some(false), _) => Some(Rvalue::Use(b.clone())), + // Transform "Ne(false, b)" ==> "b" + (BinOp::Ne, Some(false), _) => Rvalue::Use(b.clone()), - // Transform "Eq(false, b)" ==> "Not(b)" - (BinOp::Eq, Some(false), _) => Some(Rvalue::UnaryOp(UnOp::Not, b.clone())), + // Transform "Eq(false, b)" ==> "Not(b)" + (BinOp::Eq, Some(false), _) => Rvalue::UnaryOp(UnOp::Not, b.clone()), - // Transform "Ne(true, b)" ==> "Not(b)" - (BinOp::Ne, Some(true), _) => Some(Rvalue::UnaryOp(UnOp::Not, b.clone())), + // Transform "Ne(true, b)" ==> "Not(b)" + (BinOp::Ne, Some(true), _) => Rvalue::UnaryOp(UnOp::Not, b.clone()), - // Transform "Eq(a, false)" ==> "Not(a)" - (BinOp::Eq, _, Some(false)) => Some(Rvalue::UnaryOp(UnOp::Not, a.clone())), + // Transform "Eq(a, false)" ==> "Not(a)" + (BinOp::Eq, _, Some(false)) => Rvalue::UnaryOp(UnOp::Not, a.clone()), - // Transform "Ne(a, true)" ==> "Not(a)" - (BinOp::Ne, _, Some(true)) => Some(Rvalue::UnaryOp(UnOp::Not, a.clone())), + // Transform "Ne(a, true)" ==> "Not(a)" + (BinOp::Ne, _, Some(true)) => Rvalue::UnaryOp(UnOp::Not, a.clone()), - _ => None, - }; - - if let Some(new) = new { - *rvalue = new; - } - } - - _ => {} - } + _ => return, + }; } fn try_eval_bool(&self, a: &Operand<'_>) -> Option { @@ -151,64 +141,58 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { /// Transform `&(*a)` ==> `a`. fn simplify_ref_deref(&self, rvalue: &mut Rvalue<'tcx>) { - if let Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) = rvalue { - if let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() { - if rvalue.ty(self.local_decls, self.tcx) != base.ty(self.local_decls, self.tcx).ty { - return; - } - - *rvalue = Rvalue::Use(Operand::Copy(Place { - local: base.local, - projection: self.tcx.mk_place_elems(base.projection), - })); - } + if let Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) = rvalue + && let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() + && rvalue.ty(self.local_decls, self.tcx) == base.ty(self.local_decls, self.tcx).ty + { + *rvalue = Rvalue::Use(Operand::Copy(Place { + local: base.local, + projection: self.tcx.mk_place_elems(base.projection), + })); } } /// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`. fn simplify_ptr_aggregate(&self, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue + && let meta_ty = fields.raw[1].ty(self.local_decls, self.tcx) + && meta_ty.is_unit() { - let meta_ty = fields.raw[1].ty(self.local_decls, self.tcx); - if meta_ty.is_unit() { - // The mutable borrows we're holding prevent printing `rvalue` here - let mut fields = std::mem::take(fields); - let _meta = fields.pop().unwrap(); - let data = fields.pop().unwrap(); - let ptr_ty = Ty::new_ptr(self.tcx, *pointee_ty, *mutability); - *rvalue = Rvalue::Cast(CastKind::PtrToPtr, data, ptr_ty); - } + // The mutable borrows we're holding prevent printing `rvalue` here + let mut fields = std::mem::take(fields); + let _meta = fields.pop().unwrap(); + let data = fields.pop().unwrap(); + let ptr_ty = Ty::new_ptr(self.tcx, *pointee_ty, *mutability); + *rvalue = Rvalue::Cast(CastKind::PtrToPtr, data, ptr_ty); } } fn simplify_ub_check(&self, rvalue: &mut Rvalue<'tcx>) { - if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue { - let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks()); - let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None }; - *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant))); - } + let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue else { return }; + + let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks()); + let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None }; + *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant))); } fn simplify_cast(&self, rvalue: &mut Rvalue<'tcx>) { - if let Rvalue::Cast(kind, operand, cast_ty) = rvalue { - let operand_ty = operand.ty(self.local_decls, self.tcx); - if operand_ty == *cast_ty { - *rvalue = Rvalue::Use(operand.clone()); - } else if *kind == CastKind::Transmute { - // Transmuting an integer to another integer is just a signedness cast - if let (ty::Int(int), ty::Uint(uint)) | (ty::Uint(uint), ty::Int(int)) = - (operand_ty.kind(), cast_ty.kind()) - && int.bit_width() == uint.bit_width() - { - // The width check isn't strictly necessary, as different widths - // are UB and thus we'd be allowed to turn it into a cast anyway. - // But let's keep the UB around for codegen to exploit later. - // (If `CastKind::Transmute` ever becomes *not* UB for mismatched sizes, - // then the width check is necessary for big-endian correctness.) - *kind = CastKind::IntToInt; - return; - } - } + let Rvalue::Cast(kind, operand, cast_ty) = rvalue else { return }; + + let operand_ty = operand.ty(self.local_decls, self.tcx); + if operand_ty == *cast_ty { + *rvalue = Rvalue::Use(operand.clone()); + } else if *kind == CastKind::Transmute + // Transmuting an integer to another integer is just a signedness cast + && let (ty::Int(int), ty::Uint(uint)) | (ty::Uint(uint), ty::Int(int)) = + (operand_ty.kind(), cast_ty.kind()) + && int.bit_width() == uint.bit_width() + { + // The width check isn't strictly necessary, as different widths + // are UB and thus we'd be allowed to turn it into a cast anyway. + // But let's keep the UB around for codegen to exploit later. + // (If `CastKind::Transmute` ever becomes *not* UB for mismatched sizes, + // then the width check is necessary for big-endian correctness.) + *kind = CastKind::IntToInt; } } @@ -229,7 +213,9 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { terminator: &mut Terminator<'tcx>, statements: &mut Vec>, ) { - let TerminatorKind::Call { func, args, destination, target, .. } = &mut terminator.kind + let TerminatorKind::Call { + func, args, destination, target: Some(destination_block), .. + } = &terminator.kind else { return; }; @@ -237,15 +223,8 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { // It's definitely not a clone if there are multiple arguments let [arg] = &args[..] else { return }; - let Some(destination_block) = *target else { return }; - // Only bother looking more if it's easy to know what we're calling - let Some((fn_def_id, fn_args)) = func.const_fn_def() else { return }; - - // Clone needs one arg, so we can cheaply rule out other stuff - if fn_args.len() != 1 { - return; - } + let Some((fn_def_id, ..)) = func.const_fn_def() else { return }; // These types are easily available from locals, so check that before // doing DefId lookups to figure out what we're actually calling. @@ -253,15 +232,12 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { let ty::Ref(_region, inner_ty, Mutability::Not) = *arg_ty.kind() else { return }; - if !inner_ty.is_trivially_pure_clone_copy() { - return; - } - - if !self.tcx.is_lang_item(fn_def_id, LangItem::CloneFn) { + if !self.tcx.is_lang_item(fn_def_id, LangItem::CloneFn) + || !inner_ty.is_trivially_pure_clone_copy() + { return; } - let Ok([arg]) = take_array(args) else { return }; let Some(arg_place) = arg.node.place() else { return }; statements.push(Statement { @@ -273,11 +249,11 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { )), ))), }); - terminator.kind = TerminatorKind::Goto { target: destination_block }; + terminator.kind = TerminatorKind::Goto { target: *destination_block }; } fn simplify_nounwind_call(&self, terminator: &mut Terminator<'tcx>) { - let TerminatorKind::Call { func, unwind, .. } = &mut terminator.kind else { + let TerminatorKind::Call { ref func, ref mut unwind, .. } = terminator.kind else { return; }; @@ -290,7 +266,7 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { ty::FnDef(..) => body_ty.fn_sig(self.tcx).abi(), ty::Closure(..) => ExternAbi::RustCall, ty::Coroutine(..) => ExternAbi::Rust, - _ => bug!("unexpected body ty: {:?}", body_ty), + _ => bug!("unexpected body ty: {body_ty:?}"), }; if !layout::fn_can_unwind(self.tcx, Some(def_id), body_abi) { @@ -299,10 +275,9 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { } fn simplify_intrinsic_assert(&self, terminator: &mut Terminator<'tcx>) { - let TerminatorKind::Call { func, target, .. } = &mut terminator.kind else { - return; - }; - let Some(target_block) = target else { + let TerminatorKind::Call { ref func, target: ref mut target @ Some(target_block), .. } = + terminator.kind + else { return; }; let func_ty = func.ty(self.local_decls, self.tcx); @@ -310,12 +285,10 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { return; }; // The intrinsics we are interested in have one generic parameter - if args.is_empty() { - return; - } + let [arg, ..] = args[..] else { return }; let known_is_valid = - intrinsic_assert_panics(self.tcx, self.typing_env, args[0], intrinsic_name); + intrinsic_assert_panics(self.tcx, self.typing_env, arg, intrinsic_name); match known_is_valid { // We don't know the layout or it's not validity assertion at all, don't touch it None => {} @@ -325,7 +298,7 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { } Some(false) => { // If we know the assert does not panic, turn the call into a Goto - terminator.kind = TerminatorKind::Goto { target: *target_block }; + terminator.kind = TerminatorKind::Goto { target: target_block }; } } } @@ -346,9 +319,7 @@ fn resolve_rust_intrinsic<'tcx>( tcx: TyCtxt<'tcx>, func_ty: Ty<'tcx>, ) -> Option<(Symbol, GenericArgsRef<'tcx>)> { - if let ty::FnDef(def_id, args) = *func_ty.kind() { - let intrinsic = tcx.intrinsic(def_id)?; - return Some((intrinsic.name, args)); - } - None + let ty::FnDef(def_id, args) = *func_ty.kind() else { return None }; + let intrinsic = tcx.intrinsic(def_id)?; + Some((intrinsic.name, args)) } diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 0a72a9d669fe6..31b361ec1a929 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -150,14 +150,6 @@ impl Condition { fn matches(&self, value: ScalarInt) -> bool { (self.value == value) == (self.polarity == Polarity::Eq) } - - fn inv(mut self) -> Self { - self.polarity = match self.polarity { - Polarity::Eq => Polarity::Ne, - Polarity::Ne => Polarity::Eq, - }; - self - } } #[derive(Copy, Clone, Debug)] @@ -180,8 +172,13 @@ impl<'a> ConditionSet<'a> { self.iter().filter(move |c| c.matches(value)) } - fn map(self, arena: &'a DroplessArena, f: impl Fn(Condition) -> Condition) -> ConditionSet<'a> { - ConditionSet(arena.alloc_from_iter(self.iter().map(f))) + fn map( + self, + arena: &'a DroplessArena, + f: impl Fn(Condition) -> Option, + ) -> Option> { + let set = arena.try_alloc_from_iter(self.iter().map(|c| f(c).ok_or(()))).ok()?; + Some(ConditionSet(set)) } } @@ -202,9 +199,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { debug!(?discr, ?bb); let discr_ty = discr.ty(self.body, self.tcx).ty; - let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { - return; - }; + let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return }; let Some(discr) = self.map.find(discr.as_ref()) else { return }; debug!(?discr); @@ -227,7 +222,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { let conds = ConditionSet(conds); state.insert_value_idx(discr, conds, &self.map); - self.find_opportunity(bb, state, cost, 0); + self.find_opportunity(bb, state, cost, 0) } /// Recursively walk statements backwards from this bb's terminator to find threading @@ -495,19 +490,22 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { } } } - // Transfer the conditions on the copy rhs, after inversing polarity. + // Transfer the conditions on the copy rhs, after inverting the value of the condition. Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => { - if !place.ty(self.body, self.tcx).ty.is_bool() { - // Constructing the conditions by inverting the polarity - // of equality is only correct for bools. That is to say, - // `!a == b` is not `a != b` for integers greater than 1 bit. - return; - } + let layout = self.ecx.layout_of(place.ty(self.body, self.tcx).ty).unwrap(); let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return }; let Some(place) = self.map.find(place.as_ref()) else { return }; - // FIXME: I think This could be generalized to not bool if we - // actually perform a logical not on the condition's value. - let conds = conditions.map(self.arena, Condition::inv); + let Some(conds) = conditions.map(self.arena, |mut cond| { + cond.value = self + .ecx + .unary_op(UnOp::Not, &ImmTy::from_scalar_int(cond.value, layout)) + .discard_err()? + .to_scalar_int() + .discard_err()?; + Some(cond) + }) else { + return; + }; state.insert_value_idx(place, conds, &self.map); } // We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`. @@ -535,11 +533,15 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { else { return; }; - let conds = conditions.map(self.arena, |c| Condition { - value, - polarity: if c.matches(equals) { Polarity::Eq } else { Polarity::Ne }, - ..c - }); + let Some(conds) = conditions.map(self.arena, |c| { + Some(Condition { + value, + polarity: if c.matches(equals) { Polarity::Eq } else { Polarity::Ne }, + ..c + }) + }) else { + return; + }; state.insert_value_idx(place, conds, &self.map); } @@ -576,17 +578,17 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { else { return; }; - self.process_immediate(bb, discr_target, discr, state); + self.process_immediate(bb, discr_target, discr, state) } // If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`. StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume( Operand::Copy(place) | Operand::Move(place), )) => { let Some(conditions) = state.try_get(place.as_ref(), &self.map) else { return }; - conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity); + conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity) } StatementKind::Assign(box (lhs_place, rhs)) => { - self.process_assign(bb, lhs_place, rhs, state); + self.process_assign(bb, lhs_place, rhs, state) } _ => {} } @@ -632,7 +634,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { if let Some(place_to_flood) = place_to_flood { state.flood_with(place_to_flood.as_ref(), &self.map, ConditionSet::BOTTOM); } - self.find_opportunity(bb, state, cost.clone(), depth + 1); + self.find_opportunity(bb, state, cost.clone(), depth + 1) } #[instrument(level = "trace", skip(self))] @@ -755,12 +757,12 @@ impl OpportunitySet { // Replace `succ` by `new_succ` where it appears. let mut num_edges = 0; - for s in basic_blocks[current].terminator_mut().successors_mut() { + basic_blocks[current].terminator_mut().successors_mut(|s| { if *s == succ { *s = new_succ; num_edges += 1; } - } + }); // Update predecessors with the new block. let _new_succ = self.predecessors.push(num_edges); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index f8db8de4e82c9..481c794190925 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -888,7 +888,14 @@ impl CanConstProp { }; for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { let ty = body.local_decls[local].ty; - if ty.is_union() { + if ty.is_async_drop_in_place_coroutine(tcx) { + // No const propagation for async drop coroutine (AsyncDropGlue). + // Otherwise, tcx.layout_of(typing_env.as_query_input(ty)) will be called + // (early layout request for async drop coroutine) to calculate layout size. + // Layout for `async_drop_in_place::{closure}` may only be known with known T. + *val = ConstPropMode::NoPropagation; + continue; + } else if ty.is_union() { // Unions are incompatible with the current implementation of // const prop because Rust has no concept of an active // variant of a union diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 04c9375b83176..10dbb3437dcbf 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -7,12 +7,11 @@ #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] -#![feature(let_chains)] #![feature(map_try_insert)] #![feature(never_type)] #![feature(try_blocks)] +#![feature(vec_deque_pop_if)] #![feature(yeet_expr)] -#![warn(unreachable_pub)] // tidy-alphabetical-end use hir::ConstContext; @@ -125,6 +124,7 @@ declare_passes! { mod check_null : CheckNull; mod check_packed_ref : CheckPackedRef; mod check_undefined_transmutes : CheckUndefinedTransmutes; + mod check_unnecessary_transmutes: CheckUnnecessaryTransmutes; // This pass is public to allow external drivers to perform MIR cleanup pub mod cleanup_post_borrowck : CleanupPostBorrowck; @@ -316,6 +316,10 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { // All body-owners have MIR associated with them. let mut set: FxIndexSet<_> = tcx.hir_body_owners().collect(); + // Remove the fake bodies for `global_asm!`, since they're not useful + // to be emitted (`--emit=mir`) or encoded (in metadata). + set.retain(|&def_id| !matches!(tcx.def_kind(def_id), DefKind::GlobalAsm)); + // Coroutine-closures (e.g. async closures) have an additional by-move MIR // body that isn't in the HIR. for body_owner in tcx.hir_body_owners() { @@ -387,6 +391,7 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), &Lint(check_undefined_transmutes::CheckUndefinedTransmutes), + &Lint(check_unnecessary_transmutes::CheckUnnecessaryTransmutes), // What we need to do constant evaluation. &simplify::SimplifyCfg::Initial, &Lint(sanity_check::SanityCheck), @@ -493,8 +498,11 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & } // We only need to borrowck non-synthetic MIR. - let tainted_by_errors = - if !tcx.is_synthetic_mir(def) { tcx.mir_borrowck(def).tainted_by_errors } else { None }; + let tainted_by_errors = if !tcx.is_synthetic_mir(def) { + tcx.mir_borrowck(tcx.typeck_root_def_id(def.to_def_id()).expect_local()).err() + } else { + None + }; let is_fn_like = tcx.def_kind(def).is_fn_like(); if is_fn_like { @@ -513,6 +521,24 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & body.tainted_by_errors = Some(error_reported); } + // Also taint the body if it's within a top-level item that is not well formed. + // + // We do this check here and not during `mir_promoted` because that may result + // in borrowck cycles if WF requires looking into an opaque hidden type. + let root = tcx.typeck_root_def_id(def.to_def_id()); + match tcx.def_kind(root) { + DefKind::Fn + | DefKind::AssocFn + | DefKind::Static { .. } + | DefKind::Const + | DefKind::AssocConst => { + if let Err(guar) = tcx.ensure_ok().check_well_formed(root.expect_local()) { + body.tainted_by_errors = Some(guar); + } + } + _ => {} + } + run_analysis_to_runtime_passes(tcx, &mut body); tcx.alloc_steal_mir(body) @@ -625,7 +651,7 @@ fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } } -fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { +pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { fn o1(x: T) -> WithMinOptLevel { WithMinOptLevel(1, x) } @@ -674,8 +700,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // Now, we need to shrink the generated MIR. &ref_prop::ReferencePropagation, &sroa::ScalarReplacementOfAggregates, - &match_branches::MatchBranchSimplification, - // inst combine is after MatchBranchSimplification to clean up Ne(_1, false) &multiple_return_terminators::MultipleReturnTerminators, // After simplifycfg, it allows us to discover new opportunities for peephole // optimizations. @@ -684,6 +708,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &dead_store_elimination::DeadStoreElimination::Initial, &gvn::GVN, &simplify::SimplifyLocals::AfterGVN, + &match_branches::MatchBranchSimplification, &dataflow_const_prop::DataflowConstProp, &single_use_consts::SingleUseConsts, &o1(simplify_branches::SimplifyConstCondition::AfterConstProp), @@ -772,7 +797,7 @@ fn promoted_mir(tcx: TyCtxt<'_>, def: LocalDefId) -> &IndexVec(left: &Place<'tcx>, right: &Place<'tcx>) -> bool { @@ -135,6 +134,8 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> { target: _, unwind: _, replace: _, + drop: _, + async_fut: _, } = &terminator.kind && place_has_common_prefix(dropped_place, self.place) { @@ -199,7 +200,7 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< // and, for each block, the vector of locations. // // We group them per-block because they tend to scheduled in the same drop ladder block. - let mut bid_per_block = IndexMap::default(); + let mut bid_per_block = FxIndexMap::default(); let mut bid_places = UnordSet::new(); let mut ty_dropped_components = UnordMap::default(); @@ -234,8 +235,9 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< // When we encounter a DROP of some place P we only care // about the drop if `P` may be initialized. let move_data = MoveData::gather_moves(body, tcx, |_| true); - let maybe_init = MaybeInitializedPlaces::new(tcx, body, &move_data); - let mut maybe_init = maybe_init.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body); + let mut maybe_init = MaybeInitializedPlaces::new(tcx, body, &move_data) + .iterate_to_fixpoint(tcx, body, None) + .into_results_cursor(body); let mut block_drop_value_info = IndexVec::from_elem_n(MovePathIndexAtBlock::Unknown, body.basic_blocks.len()); for (&block, candidates) in &bid_per_block { @@ -455,8 +457,8 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< } /// Extract binding names if available for diagnosis -fn collect_user_names(body: &Body<'_>) -> IndexMap { - let mut names = IndexMap::default(); +fn collect_user_names(body: &Body<'_>) -> FxIndexMap { + let mut names = FxIndexMap::default(); for var_debug_info in &body.var_debug_info { if let mir::VarDebugInfoContents::Place(place) = &var_debug_info.value && let Some(local) = place.local_or_deref_local() @@ -470,9 +472,9 @@ fn collect_user_names(body: &Body<'_>) -> IndexMap { /// Assign names for anonymous or temporary values for diagnosis fn assign_observables_names( locals: impl IntoIterator, - user_names: &IndexMap, -) -> IndexMap { - let mut names = IndexMap::default(); + user_names: &FxIndexMap, +) -> FxIndexMap { + let mut names = FxIndexMap::default(); let mut assigned_names = FxHashSet::default(); let mut idx = 0u64; let mut fresh_name = || { @@ -513,23 +515,17 @@ struct LocalLabel<'a> { /// A custom `Subdiagnostic` implementation so that the notes are delivered in a specific order impl Subdiagnostic for LocalLabel<'_> { - fn add_to_diag_with< - G: rustc_errors::EmissionGuarantee, - F: rustc_errors::SubdiagMessageOp, - >( - self, - diag: &mut rustc_errors::Diag<'_, G>, - f: &F, - ) { + fn add_to_diag(self, diag: &mut rustc_errors::Diag<'_, G>) { diag.arg("name", self.name); diag.arg("is_generated_name", self.is_generated_name); diag.arg("is_dropped_first_edition_2024", self.is_dropped_first_edition_2024); - let msg = f(diag, crate::fluent_generated::mir_transform_tail_expr_local.into()); + let msg = diag.eagerly_translate(crate::fluent_generated::mir_transform_tail_expr_local); diag.span_label(self.span, msg); for dtor in self.destructors { - dtor.add_to_diag_with(diag, f); + dtor.add_to_diag(diag); } - let msg = f(diag, crate::fluent_generated::mir_transform_label_local_epilogue); + let msg = + diag.eagerly_translate(crate::fluent_generated::mir_transform_label_local_epilogue); diag.span_label(self.span, msg); } } diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 9db37bf5a0721..8c0c309689902 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -5,7 +5,6 @@ use rustc_index::IndexSlice; use rustc_middle::mir::*; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; -use rustc_type_ir::TyKind::*; use tracing::instrument; use super::simplify::simplify_cfg; @@ -20,33 +19,33 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let typing_env = body.typing_env(tcx); - let mut should_cleanup = false; - for i in 0..body.basic_blocks.len() { - let bbs = &*body.basic_blocks; - let bb_idx = BasicBlock::from_usize(i); - match bbs[bb_idx].terminator().kind { + let mut apply_patch = false; + let mut patch = MirPatch::new(body); + for (bb, bb_data) in body.basic_blocks.iter_enumerated() { + match &bb_data.terminator().kind { TerminatorKind::SwitchInt { - discr: ref _discr @ (Operand::Copy(_) | Operand::Move(_)), - ref targets, + discr: Operand::Copy(_) | Operand::Move(_), + targets, .. // We require that the possible target blocks don't contain this block. - } if !targets.all_targets().contains(&bb_idx) => {} + } if !targets.all_targets().contains(&bb) => {} // Only optimize switch int statements _ => continue, }; - if SimplifyToIf.simplify(tcx, body, bb_idx, typing_env).is_some() { - should_cleanup = true; + if SimplifyToIf.simplify(tcx, body, &mut patch, bb, typing_env).is_some() { + apply_patch = true; continue; } - if SimplifyToExp::default().simplify(tcx, body, bb_idx, typing_env).is_some() { - should_cleanup = true; + if SimplifyToExp::default().simplify(tcx, body, &mut patch, bb, typing_env).is_some() { + apply_patch = true; continue; } } - if should_cleanup { - simplify_cfg(body); + if apply_patch { + patch.apply(body); + simplify_cfg(tcx, body); } } @@ -62,21 +61,21 @@ trait SimplifyMatch<'tcx> { fn simplify( &mut self, tcx: TyCtxt<'tcx>, - body: &mut Body<'tcx>, + body: &Body<'tcx>, + patch: &mut MirPatch<'tcx>, switch_bb_idx: BasicBlock, typing_env: ty::TypingEnv<'tcx>, ) -> Option<()> { let bbs = &body.basic_blocks; - let (discr, targets) = match bbs[switch_bb_idx].terminator().kind { - TerminatorKind::SwitchInt { ref discr, ref targets, .. } => (discr, targets), - _ => unreachable!(), + let TerminatorKind::SwitchInt { discr, targets, .. } = + &bbs[switch_bb_idx].terminator().kind + else { + unreachable!(); }; let discr_ty = discr.ty(body.local_decls(), tcx); self.can_simplify(tcx, targets, typing_env, bbs, discr_ty)?; - let mut patch = MirPatch::new(body); - // Take ownership of items now that we know we can optimize. let discr = discr.clone(); @@ -89,19 +88,9 @@ trait SimplifyMatch<'tcx> { let parent_end = Location { block: switch_bb_idx, statement_index }; patch.add_statement(parent_end, StatementKind::StorageLive(discr_local)); patch.add_assign(parent_end, Place::from(discr_local), Rvalue::Use(discr)); - self.new_stmts( - tcx, - targets, - typing_env, - &mut patch, - parent_end, - bbs, - discr_local, - discr_ty, - ); + self.new_stmts(tcx, targets, typing_env, patch, parent_end, bbs, discr_local, discr_ty); patch.add_statement(parent_end, StatementKind::StorageDead(discr_local)); patch.patch_terminator(switch_bb_idx, bbs[first].terminator().kind.clone()); - patch.apply(body); Some(()) } @@ -293,13 +282,13 @@ fn can_cast( ) -> bool { let from_scalar = ScalarInt::try_from_uint(src_val.into(), src_layout.size).unwrap(); let v = match src_layout.ty.kind() { - Uint(_) => from_scalar.to_uint(src_layout.size), - Int(_) => from_scalar.to_int(src_layout.size) as u128, + ty::Uint(_) => from_scalar.to_uint(src_layout.size), + ty::Int(_) => from_scalar.to_int(src_layout.size) as u128, _ => unreachable!("invalid int"), }; let size = match *cast_ty.kind() { - Int(t) => Integer::from_int_ty(&tcx, t).size(), - Uint(t) => Integer::from_uint_ty(&tcx, t).size(), + ty::Int(t) => Integer::from_int_ty(&tcx, t).size(), + ty::Uint(t) => Integer::from_uint_ty(&tcx, t).size(), _ => unreachable!("invalid int"), }; let v = size.truncate(v); diff --git a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs index c63bfdcee8559..f59b849e85c62 100644 --- a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs +++ b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs @@ -18,19 +18,17 @@ impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators { // find basic blocks with no statement and a return terminator let mut bbs_simple_returns = DenseBitSet::new_empty(body.basic_blocks.len()); let bbs = body.basic_blocks_mut(); - for idx in bbs.indices() { - if bbs[idx].statements.is_empty() - && bbs[idx].terminator().kind == TerminatorKind::Return - { + for (idx, bb) in bbs.iter_enumerated() { + if bb.statements.is_empty() && bb.terminator().kind == TerminatorKind::Return { bbs_simple_returns.insert(idx); } } for bb in bbs { - if let TerminatorKind::Goto { target } = bb.terminator().kind { - if bbs_simple_returns.contains(target) { - bb.terminator_mut().kind = TerminatorKind::Return; - } + if let TerminatorKind::Goto { target } = bb.terminator().kind + && bbs_simple_returns.contains(target) + { + bb.terminator_mut().kind = TerminatorKind::Return; } } diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index 6a177faeac81f..a872eae15f185 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -148,6 +148,23 @@ impl<'tcx> MirPatch<'tcx> { self.term_patch_map[bb].is_some() } + /// Universal getter for block data, either it is in 'old' blocks or in patched ones + pub(crate) fn block<'a>( + &'a self, + body: &'a Body<'tcx>, + bb: BasicBlock, + ) -> &'a BasicBlockData<'tcx> { + match bb.index().checked_sub(body.basic_blocks.len()) { + Some(new) => &self.new_blocks[new], + None => &body[bb], + } + } + + pub(crate) fn terminator_loc(&self, body: &Body<'tcx>, bb: BasicBlock) -> Location { + let offset = self.block(body, bb).statements.len(); + Location { block: bb, statement_index: offset } + } + /// Queues the addition of a new temporary with additional local info. pub(crate) fn new_local_with_info( &mut self, @@ -181,7 +198,7 @@ impl<'tcx> MirPatch<'tcx> { /// Queues the addition of a new basic block. pub(crate) fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock { - let block = BasicBlock::new(self.term_patch_map.len()); + let block = self.term_patch_map.next_index(); debug!("MirPatch: new_block: {:?}: {:?}", block, data); self.new_blocks.push(data); self.term_patch_map.push(None); @@ -276,10 +293,7 @@ impl<'tcx> MirPatch<'tcx> { } pub(crate) fn source_info_for_location(&self, body: &Body<'tcx>, loc: Location) -> SourceInfo { - let data = match loc.block.index().checked_sub(body.basic_blocks.len()) { - Some(new) => &self.new_blocks[new], - None => &body[loc.block], - }; + let data = self.block(body, loc.block); Self::source_info_for_index(data, loc) } } diff --git a/compiler/rustc_mir_transform/src/post_analysis_normalize.rs b/compiler/rustc_mir_transform/src/post_analysis_normalize.rs index 76c2f082c0bfc..5599dee4ccad3 100644 --- a/compiler/rustc_mir_transform/src/post_analysis_normalize.rs +++ b/compiler/rustc_mir_transform/src/post_analysis_normalize.rs @@ -39,20 +39,22 @@ impl<'tcx> MutVisitor<'tcx> for PostAnalysisNormalizeVisitor<'tcx> { _context: PlaceContext, _location: Location, ) { - // Performance optimization: don't reintern if there is no `OpaqueCast` to remove. - if place.projection.iter().all(|elem| !matches!(elem, ProjectionElem::OpaqueCast(_))) { - return; + if !self.tcx.next_trait_solver_globally() { + // `OpaqueCast` projections are only needed if there are opaque types on which projections + // are performed. After the `PostAnalysisNormalize` pass, all opaque types are replaced with their + // hidden types, so we don't need these projections anymore. + // + // Performance optimization: don't reintern if there is no `OpaqueCast` to remove. + if place.projection.iter().any(|elem| matches!(elem, ProjectionElem::OpaqueCast(_))) { + place.projection = self.tcx.mk_place_elems( + &place + .projection + .into_iter() + .filter(|elem| !matches!(elem, ProjectionElem::OpaqueCast(_))) + .collect::>(), + ); + }; } - // `OpaqueCast` projections are only needed if there are opaque types on which projections - // are performed. After the `PostAnalysisNormalize` pass, all opaque types are replaced with their - // hidden types, so we don't need these projections anymore. - place.projection = self.tcx.mk_place_elems( - &place - .projection - .into_iter() - .filter(|elem| !matches!(elem, ProjectionElem::OpaqueCast(_))) - .collect::>(), - ); self.super_place(place, _context, _location); } diff --git a/compiler/rustc_mir_transform/src/prettify.rs b/compiler/rustc_mir_transform/src/prettify.rs index 8ccfbe2f194b4..8217feff24eca 100644 --- a/compiler/rustc_mir_transform/src/prettify.rs +++ b/compiler/rustc_mir_transform/src/prettify.rs @@ -115,9 +115,7 @@ impl<'tcx> MutVisitor<'tcx> for BasicBlockUpdater<'tcx> { } fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, _location: Location) { - for succ in terminator.successors_mut() { - *succ = self.map[*succ]; - } + terminator.successors_mut(|succ| *succ = self.map[*succ]); } } diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index c8d8dc147e94f..47d4383097008 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -18,7 +18,7 @@ use either::{Left, Right}; use rustc_const_eval::check_consts::{ConstCx, qualifs}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_index::{Idx, IndexSlice, IndexVec}; +use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::{self, GenericArgs, List, Ty, TyCtxt, TypeVisitableExt}; @@ -864,17 +864,21 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { new_temp } - fn promote_candidate(mut self, candidate: Candidate, next_promoted_id: usize) -> Body<'tcx> { + fn promote_candidate( + mut self, + candidate: Candidate, + next_promoted_index: Promoted, + ) -> Body<'tcx> { let def = self.source.source.def_id(); let (mut rvalue, promoted_op) = { let promoted = &mut self.promoted; - let promoted_id = Promoted::new(next_promoted_id); let tcx = self.tcx; let mut promoted_operand = |ty, span| { promoted.span = span; promoted.local_decls[RETURN_PLACE] = LocalDecl::new(ty, span); let args = tcx.erase_regions(GenericArgs::identity_for_item(tcx, def)); - let uneval = mir::UnevaluatedConst { def, args, promoted: Some(promoted_id) }; + let uneval = + mir::UnevaluatedConst { def, args, promoted: Some(next_promoted_index) }; ConstOperand { span, user_ty: None, const_: Const::Unevaluated(uneval, ty) } }; @@ -1034,7 +1038,7 @@ fn promote_candidates<'tcx>( required_consts: Vec::new(), }; - let mut promoted = promoter.promote_candidate(candidate, promotions.len()); + let mut promoted = promoter.promote_candidate(candidate, promotions.next_index()); promoted.source.promoted = Some(promotions.next_index()); promotions.push(promoted); } diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 1dd34005d6641..797056ad52d4a 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -58,13 +58,13 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { } } - for target in body[bb].terminator_mut().successors_mut() { + body[bb].terminator_mut().successors_mut(|target| { if *target != resume_block && nop_landing_pads.contains(*target) { debug!(" folding noop jump to {:?} to resume block", target); *target = resume_block; jumps_folded += 1; } - } + }); let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); if is_nop_landing_pad { diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs index 15fe77d53195a..cb598ceb4dfea 100644 --- a/compiler/rustc_mir_transform/src/remove_place_mention.rs +++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs @@ -8,7 +8,7 @@ pub(super) struct RemovePlaceMention; impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - !sess.opts.unstable_opts.mir_keep_place_mention + !sess.opts.unstable_opts.mir_preserve_ub } fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index 8a8cdafc69070..43f80508e4a87 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -35,7 +35,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops { // if we applied optimizations, we potentially have some cfg to cleanup to // make it easier for further passes if should_simplify { - simplify_cfg(body); + simplify_cfg(tcx, body); } } diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 78d94a038671d..c4dc8638b26ab 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -59,6 +59,11 @@ fn trivially_zst<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option { | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => Some(false), + ty::Coroutine(def_id, _) => { + // For async_drop_in_place::{closure} this is load bearing, not just a perf fix, + // because we don't want to compute the layout before mir analysis is done + if tcx.is_async_drop_in_place_coroutine(*def_id) { Some(false) } else { None } + } // check `layout_of` to see (including unreachable things we won't actually see) _ => None, } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 34074a84e28b6..9688ac8ed2e27 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -6,14 +6,14 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_index::{Idx, IndexVec}; +use rustc_middle::mir::visit::{MutVisitor, PlaceContext}; use rustc_middle::mir::*; use rustc_middle::query::Providers; -use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{ self, CoroutineArgs, CoroutineArgsExt, EarlyBinder, GenericArgs, Ty, TyCtxt, }; use rustc_middle::{bug, span_bug}; -use rustc_span::source_map::Spanned; +use rustc_span::source_map::{Spanned, dummy_spanned}; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; @@ -21,7 +21,8 @@ use crate::elaborate_drop::{DropElaborator, DropFlagMode, DropStyle, Unwind, ela use crate::patch::MirPatch; use crate::{ abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, inline, - instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify, + instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, + run_optimization_passes, simplify, }; mod async_destructor_ctor; @@ -30,6 +31,40 @@ pub(super) fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; } +// Replace Pin<&mut ImplCoroutine> accesses (_1.0) into Pin<&mut ProxyCoroutine> acceses +struct FixProxyFutureDropVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + replace_to: Local, +} + +impl<'tcx> MutVisitor<'tcx> for FixProxyFutureDropVisitor<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_place( + &mut self, + place: &mut Place<'tcx>, + _context: PlaceContext, + _location: Location, + ) { + if place.local == Local::from_u32(1) { + if place.projection.len() == 1 { + assert!(matches!( + place.projection.first(), + Some(ProjectionElem::Field(FieldIdx::ZERO, _)) + )); + *place = Place::from(self.replace_to); + } else if place.projection.len() == 2 { + assert!(matches!(place.projection[0], ProjectionElem::Field(FieldIdx::ZERO, _))); + assert!(matches!(place.projection[1], ProjectionElem::Deref)); + *place = + Place::from(self.replace_to).project_deeper(&[ProjectionElem::Deref], self.tcx); + } + } + } +} + fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body<'tcx> { debug!("make_shim({:?})", instance); @@ -67,7 +102,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< let call_mut = tcx .associated_items(fn_mut) .in_definition_order() - .find(|it| it.kind == ty::AssocKind::Fn) + .find(|it| it.is_fn()) .unwrap() .def_id; @@ -130,8 +165,53 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< ty::InstanceKind::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance), ty::InstanceKind::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty), ty::InstanceKind::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty), + ty::InstanceKind::FutureDropPollShim(def_id, proxy_ty, impl_ty) => { + let mut body = + async_destructor_ctor::build_future_drop_poll_shim(tcx, def_id, proxy_ty, impl_ty); + + pm::run_passes( + tcx, + &mut body, + &[ + &mentioned_items::MentionedItems, + &abort_unwinding_calls::AbortUnwindingCalls, + &add_call_guards::CriticalCallEdges, + ], + Some(MirPhase::Runtime(RuntimePhase::PostCleanup)), + pm::Optimizations::Allowed, + ); + run_optimization_passes(tcx, &mut body); + debug!("make_shim({:?}) = {:?}", instance, body); + return body; + } + ty::InstanceKind::AsyncDropGlue(def_id, ty) => { + let mut body = async_destructor_ctor::build_async_drop_shim(tcx, def_id, ty); + + // Main pass required here is StateTransform to convert sync drop ladder + // into coroutine. + // Others are minimal passes as for sync drop glue shim + pm::run_passes( + tcx, + &mut body, + &[ + &mentioned_items::MentionedItems, + &abort_unwinding_calls::AbortUnwindingCalls, + &add_call_guards::CriticalCallEdges, + &simplify::SimplifyCfg::MakeShim, + &crate::coroutine::StateTransform, + ], + Some(MirPhase::Runtime(RuntimePhase::PostCleanup)), + pm::Optimizations::Allowed, + ); + run_optimization_passes(tcx, &mut body); + debug!("make_shim({:?}) = {:?}", instance, body); + return body; + } + ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) => { - async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty) + let body = async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty); + debug!("make_shim({:?}) = {:?}", instance, body); + return body; } ty::InstanceKind::Virtual(..) => { bug!("InstanceKind::Virtual ({:?}) is for direct calls only", instance) @@ -215,6 +295,42 @@ fn local_decls_for_sig<'tcx>( .collect() } +fn dropee_emit_retag<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + mut dropee_ptr: Place<'tcx>, + span: Span, +) -> Place<'tcx> { + if tcx.sess.opts.unstable_opts.mir_emit_retag { + let source_info = SourceInfo::outermost(span); + // We want to treat the function argument as if it was passed by `&mut`. As such, we + // generate + // ``` + // temp = &mut *arg; + // Retag(temp, FnEntry) + // ``` + // It's important that we do this first, before anything that depends on `dropee_ptr` + // has been put into the body. + let reborrow = Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + tcx.mk_place_deref(dropee_ptr), + ); + let ref_ty = reborrow.ty(body.local_decls(), tcx); + dropee_ptr = body.local_decls.push(LocalDecl::new(ref_ty, span)).into(); + let new_statements = [ + StatementKind::Assign(Box::new((dropee_ptr, reborrow))), + StatementKind::Retag(RetagKind::FnEntry, Box::new(dropee_ptr)), + ]; + for s in new_statements { + body.basic_blocks_mut()[START_BLOCK] + .statements + .push(Statement { source_info, kind: s }); + } + } + dropee_ptr +} + fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) -> Body<'tcx> { debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty); @@ -248,39 +364,19 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); // The first argument (index 0), but add 1 for the return value. - let mut dropee_ptr = Place::from(Local::new(1 + 0)); - if tcx.sess.opts.unstable_opts.mir_emit_retag { - // We want to treat the function argument as if it was passed by `&mut`. As such, we - // generate - // ``` - // temp = &mut *arg; - // Retag(temp, FnEntry) - // ``` - // It's important that we do this first, before anything that depends on `dropee_ptr` - // has been put into the body. - let reborrow = Rvalue::Ref( - tcx.lifetimes.re_erased, - BorrowKind::Mut { kind: MutBorrowKind::Default }, - tcx.mk_place_deref(dropee_ptr), - ); - let ref_ty = reborrow.ty(body.local_decls(), tcx); - dropee_ptr = body.local_decls.push(LocalDecl::new(ref_ty, span)).into(); - let new_statements = [ - StatementKind::Assign(Box::new((dropee_ptr, reborrow))), - StatementKind::Retag(RetagKind::FnEntry, Box::new(dropee_ptr)), - ]; - for s in new_statements { - body.basic_blocks_mut()[START_BLOCK] - .statements - .push(Statement { source_info, kind: s }); - } - } + let dropee_ptr = Place::from(Local::new(1 + 0)); + let dropee_ptr = dropee_emit_retag(tcx, &mut body, dropee_ptr, span); if ty.is_some() { let patch = { let typing_env = ty::TypingEnv::post_analysis(tcx, def_id); - let mut elaborator = - DropShimElaborator { body: &body, patch: MirPatch::new(&body), tcx, typing_env }; + let mut elaborator = DropShimElaborator { + body: &body, + patch: MirPatch::new(&body), + tcx, + typing_env, + produce_async_drops: false, + }; let dropee = tcx.mk_place_deref(dropee_ptr); let resume_block = elaborator.patch.resume_block(); elaborate_drop( @@ -291,6 +387,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) return_block, Unwind::To(resume_block), START_BLOCK, + None, ); elaborator.patch }; @@ -339,6 +436,7 @@ pub(super) struct DropShimElaborator<'a, 'tcx> { pub patch: MirPatch<'tcx>, pub tcx: TyCtxt<'tcx>, pub typing_env: ty::TypingEnv<'tcx>, + pub produce_async_drops: bool, } impl fmt::Debug for DropShimElaborator<'_, '_> { @@ -366,6 +464,13 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { self.typing_env } + fn terminator_loc(&self, bb: BasicBlock) -> Location { + self.patch.terminator_loc(self.body, bb) + } + fn allow_async_drops(&self) -> bool { + self.produce_async_drops + } + fn drop_style(&self, _path: Self::Path, mode: DropFlagMode) -> DropStyle { match mode { DropFlagMode::Shallow => { @@ -622,6 +727,8 @@ impl<'tcx> CloneShimBuilder<'tcx> { target: unwind, unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup), replace: false, + drop: None, + async_fut: None, }, /* is_cleanup */ true, ); @@ -718,12 +825,6 @@ fn build_call_shim<'tcx>( let def_id = instance.def_id(); - let rpitit_shim = if let ty::InstanceKind::ReifyShim(..) = instance { - tcx.return_position_impl_trait_in_trait_shim_data(def_id) - } else { - None - }; - let sig = tcx.fn_sig(def_id); let sig = sig.map_bound(|sig| tcx.instantiate_bound_regions_with_erased(sig)); @@ -779,30 +880,7 @@ fn build_call_shim<'tcx>( let mut local_decls = local_decls_for_sig(&sig, span); let source_info = SourceInfo::outermost(span); - let mut destination = Place::return_place(); - if let Some((rpitit_def_id, fn_args)) = rpitit_shim { - let rpitit_args = - fn_args.instantiate_identity().extend_to(tcx, rpitit_def_id, |param, _| { - match param.kind { - ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - ty::GenericParamDefKind::Type { .. } - | ty::GenericParamDefKind::Const { .. } => { - unreachable!("rpitit should have no addition ty/ct") - } - } - }); - let dyn_star_ty = Ty::new_dynamic( - tcx, - tcx.item_bounds_to_existential_predicates(rpitit_def_id, rpitit_args), - tcx.lifetimes.re_erased, - ty::DynStar, - ); - destination = local_decls.push(local_decls[RETURN_PLACE].clone()).into(); - local_decls[RETURN_PLACE].ty = dyn_star_ty; - let mut inputs_and_output = sig.inputs_and_output.to_vec(); - *inputs_and_output.last_mut().unwrap() = dyn_star_ty; - sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); - } + let destination = Place::return_place(); let rcvr_place = || { assert!(rcvr_adjustment.is_some()); @@ -916,28 +994,14 @@ fn build_call_shim<'tcx>( target: BasicBlock::new(2), unwind: UnwindAction::Continue, replace: false, + drop: None, + async_fut: None, }, false, ); } // BB #1/#2 - return - // NOTE: If this is an RPITIT in dyn, we also want to coerce - // the return type of the function into a `dyn*`. - let stmts = if rpitit_shim.is_some() { - vec![Statement { - source_info, - kind: StatementKind::Assign(Box::new(( - Place::return_place(), - Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::DynStar, CoercionSource::Implicit), - Operand::Move(destination), - sig.output(), - ), - ))), - }] - } else { - vec![] - }; + let stmts = vec![]; block(&mut blocks, stmts, TerminatorKind::Return, false); if let Some(Adjustment::RefMut) = rcvr_adjustment { // BB #3 - drop if closure panics @@ -949,6 +1013,8 @@ fn build_call_shim<'tcx>( target: BasicBlock::new(4), unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup), replace: false, + drop: None, + async_fut: None, }, /* is_cleanup */ true, ); diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index 94b1b4b1855b5..7976b65aae7b0 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -1,639 +1,430 @@ -use std::iter; - -use itertools::Itertools; -use rustc_abi::{FieldIdx, VariantIdx}; -use rustc_const_eval::interpret; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Safety}; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir::*; -use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::util::{AsyncDropGlueMorphology, Discr}; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::{bug, span_bug}; -use rustc_span::source_map::respan; -use rustc_span::{Span, Symbol}; -use rustc_target::spec::PanicStrategy; -use tracing::debug; - -use super::{local_decls_for_sig, new_body}; +use rustc_middle::mir::{ + BasicBlock, BasicBlockData, Body, Local, LocalDecl, MirSource, Operand, Place, Rvalue, + SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, +}; +use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; + +use super::*; +use crate::patch::MirPatch; pub(super) fn build_async_destructor_ctor_shim<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, - ty: Option>, + ty: Ty<'tcx>, ) -> Body<'tcx> { - debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty); - - AsyncDestructorCtorShimBuilder::new(tcx, def_id, ty).build() + debug!("build_async_destructor_ctor_shim(def_id={:?}, ty={:?})", def_id, ty); + debug_assert_eq!(Some(def_id), tcx.lang_items().async_drop_in_place_fn()); + let generic_body = tcx.optimized_mir(def_id); + let args = tcx.mk_args(&[ty.into()]); + let mut body = EarlyBinder::bind(generic_body.clone()).instantiate(tcx, args); + + // Minimal shim passes except MentionedItems, + // it causes error "mentioned_items for DefId(...async_drop_in_place...) have already been set + pm::run_passes( + tcx, + &mut body, + &[ + &simplify::SimplifyCfg::MakeShim, + &abort_unwinding_calls::AbortUnwindingCalls, + &add_call_guards::CriticalCallEdges, + ], + None, + pm::Optimizations::Allowed, + ); + body } -/// Builder for async_drop_in_place shim. Functions as a stack machine -/// to build up an expression using combinators. Stack contains pairs -/// of locals and types. Combinator is a not yet instantiated pair of a -/// function and a type, is considered to be an operator which consumes -/// operands from the stack by instantiating its function and its type -/// with operand types and moving locals into the function call. Top -/// pair is considered to be the last operand. -// FIXME: add mir-opt tests -struct AsyncDestructorCtorShimBuilder<'tcx> { +// build_drop_shim analog for async drop glue (for generated coroutine poll function) +pub(super) fn build_async_drop_shim<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, - self_ty: Option>, - span: Span, - source_info: SourceInfo, - typing_env: ty::TypingEnv<'tcx>, - - stack: Vec>, - last_bb: BasicBlock, - top_cleanup_bb: Option, - - locals: IndexVec>, - bbs: IndexVec>, -} - -#[derive(Clone, Copy)] -enum SurfaceDropKind { - Async, - Sync, -} - -impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { - const SELF_PTR: Local = Local::from_u32(1); - const INPUT_COUNT: usize = 1; - const MAX_STACK_LEN: usize = 2; - - fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Option>) -> Self { - let args = if let Some(ty) = self_ty { - tcx.mk_args(&[ty.into()]) + ty: Ty<'tcx>, +) -> Body<'tcx> { + debug!("build_async_drop_shim(def_id={:?}, ty={:?})", def_id, ty); + let ty::Coroutine(_, parent_args) = ty.kind() else { + bug!(); + }; + let typing_env = ty::TypingEnv::fully_monomorphized(); + + let drop_ty = parent_args.first().unwrap().expect_ty(); + let drop_ptr_ty = Ty::new_mut_ptr(tcx, drop_ty); + + assert!(tcx.is_coroutine(def_id)); + let coroutine_kind = tcx.coroutine_kind(def_id).unwrap(); + + assert!(matches!( + coroutine_kind, + CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Fn) + )); + + let needs_async_drop = drop_ty.needs_async_drop(tcx, typing_env); + let needs_sync_drop = !needs_async_drop && drop_ty.needs_drop(tcx, typing_env); + + let resume_adt = tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, None)); + let resume_ty = Ty::new_adt(tcx, resume_adt, ty::List::empty()); + + let fn_sig = ty::Binder::dummy(tcx.mk_fn_sig( + [ty, resume_ty], + tcx.types.unit, + false, + Safety::Safe, + ExternAbi::Rust, + )); + let sig = tcx.instantiate_bound_regions_with_erased(fn_sig); + + assert!(!drop_ty.is_coroutine()); + let span = tcx.def_span(def_id); + let source_info = SourceInfo::outermost(span); + + // The first argument (index 0), but add 1 for the return value. + let coroutine_layout = Place::from(Local::new(1 + 0)); + let coroutine_layout_dropee = + tcx.mk_place_field(coroutine_layout, FieldIdx::new(0), drop_ptr_ty); + + let return_block = BasicBlock::new(1); + let mut blocks = IndexVec::with_capacity(2); + let block = |blocks: &mut IndexVec<_, _>, kind| { + blocks.push(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { source_info, kind }), + is_cleanup: false, + }) + }; + block( + &mut blocks, + if needs_sync_drop { + TerminatorKind::Drop { + place: tcx.mk_place_deref(coroutine_layout_dropee), + target: return_block, + unwind: UnwindAction::Continue, + replace: false, + drop: None, + async_fut: None, + } } else { - ty::GenericArgs::identity_for_item(tcx, def_id) - }; - let sig = tcx.fn_sig(def_id).instantiate(tcx, args); - let sig = tcx.instantiate_bound_regions_with_erased(sig); - let span = tcx.def_span(def_id); + TerminatorKind::Goto { target: return_block } + }, + ); + block(&mut blocks, TerminatorKind::Return); + + let source = MirSource::from_instance(ty::InstanceKind::AsyncDropGlue(def_id, ty)); + let mut body = + new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); + + body.coroutine = Some(Box::new(CoroutineInfo::initial( + coroutine_kind, + parent_args.as_coroutine().yield_ty(), + parent_args.as_coroutine().resume_ty(), + ))); + body.phase = MirPhase::Runtime(RuntimePhase::Initial); + if !needs_async_drop { + // Returning noop body for types without `need async drop` + // (or sync Drop in case of !`need async drop` && `need drop`) + return body; + } - let source_info = SourceInfo::outermost(span); + let mut dropee_ptr = Place::from(body.local_decls.push(LocalDecl::new(drop_ptr_ty, span))); + let st_kind = StatementKind::Assign(Box::new(( + dropee_ptr, + Rvalue::Use(Operand::Move(coroutine_layout_dropee)), + ))); + body.basic_blocks_mut()[START_BLOCK].statements.push(Statement { source_info, kind: st_kind }); + dropee_ptr = dropee_emit_retag(tcx, &mut body, dropee_ptr, span); - debug_assert_eq!(sig.inputs().len(), Self::INPUT_COUNT); - let locals = local_decls_for_sig(&sig, span); + let dropline = body.basic_blocks.last_index(); - // Usual case: noop() + unwind resume + return - let mut bbs = IndexVec::with_capacity(3); - let typing_env = ty::TypingEnv::post_analysis(tcx, def_id); - AsyncDestructorCtorShimBuilder { + let patch = { + let mut elaborator = DropShimElaborator { + body: &body, + patch: MirPatch::new(&body), tcx, - def_id, - self_ty, - span, - source_info, typing_env, - - stack: Vec::with_capacity(Self::MAX_STACK_LEN), - last_bb: bbs.push(BasicBlockData::new(None, false)), - top_cleanup_bb: match tcx.sess.panic_strategy() { - PanicStrategy::Unwind => { - // Don't drop input arg because it's just a pointer - Some(bbs.push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { - source_info, - kind: TerminatorKind::UnwindResume, - }), - is_cleanup: true, - })) - } - PanicStrategy::Abort => None, - }, - - locals, - bbs, - } - } - - fn build(self) -> Body<'tcx> { - let (tcx, Some(self_ty)) = (self.tcx, self.self_ty) else { - return self.build_zst_output(); - }; - match self_ty.async_drop_glue_morphology(tcx) { - AsyncDropGlueMorphology::Noop => span_bug!( - self.span, - "async drop glue shim generator encountered type with noop async drop glue morphology" - ), - AsyncDropGlueMorphology::DeferredDropInPlace => { - return self.build_deferred_drop_in_place(); - } - AsyncDropGlueMorphology::Custom => (), - } - - let surface_drop_kind = || { - let adt_def = self_ty.ty_adt_def()?; - if adt_def.async_destructor(tcx).is_some() { - Some(SurfaceDropKind::Async) - } else if adt_def.destructor(tcx).is_some() { - Some(SurfaceDropKind::Sync) - } else { - None - } + produce_async_drops: true, }; + let dropee = tcx.mk_place_deref(dropee_ptr); + let resume_block = elaborator.patch.resume_block(); + elaborate_drop( + &mut elaborator, + source_info, + dropee, + (), + return_block, + Unwind::To(resume_block), + START_BLOCK, + dropline, + ); + elaborator.patch + }; + patch.apply(&mut body); - match self_ty.kind() { - ty::Array(elem_ty, _) => self.build_slice(true, *elem_ty), - ty::Slice(elem_ty) => self.build_slice(false, *elem_ty), - - ty::Tuple(elem_tys) => self.build_chain(None, elem_tys.iter()), - ty::Adt(adt_def, args) if adt_def.is_struct() => { - let field_tys = adt_def.non_enum_variant().fields.iter().map(|f| f.ty(tcx, args)); - self.build_chain(surface_drop_kind(), field_tys) - } - ty::Closure(_, args) => self.build_chain(None, args.as_closure().upvar_tys().iter()), - ty::CoroutineClosure(_, args) => { - self.build_chain(None, args.as_coroutine_closure().upvar_tys().iter()) - } + body +} - ty::Adt(adt_def, args) if adt_def.is_enum() => { - self.build_enum(*adt_def, *args, surface_drop_kind()) - } +// * For async drop a "normal" coroutine: +// `async_drop_in_place::{closure}.poll()` is converted into `T.future_drop_poll()`. +// Every coroutine has its `poll` (calculate yourself a little further) +// and its `future_drop_poll` (drop yourself a little further). +// +// * For async drop of "async drop coroutine" (`async_drop_in_place::{closure}`): +// Correct drop of such coroutine means normal execution of nested async drop. +// async_drop(async_drop(T))::future_drop_poll() => async_drop(T)::poll(). +pub(super) fn build_future_drop_poll_shim<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + proxy_ty: Ty<'tcx>, + impl_ty: Ty<'tcx>, +) -> Body<'tcx> { + let instance = ty::InstanceKind::FutureDropPollShim(def_id, proxy_ty, impl_ty); + let ty::Coroutine(coroutine_def_id, _) = impl_ty.kind() else { + bug!("build_future_drop_poll_shim not for coroutine impl type: ({:?})", instance); + }; - ty::Adt(adt_def, _) => { - assert!(adt_def.is_union()); - match surface_drop_kind().unwrap() { - SurfaceDropKind::Async => self.build_fused_async_surface(), - SurfaceDropKind::Sync => self.build_fused_sync_surface(), - } - } + let span = tcx.def_span(def_id); - ty::Bound(..) - | ty::Foreign(_) - | ty::Placeholder(_) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) | ty::TyVar(_)) - | ty::Param(_) - | ty::Alias(..) => { - bug!("Building async destructor for unexpected type: {self_ty:?}") - } - - _ => { - bug!( - "Building async destructor constructor shim is not yet implemented for type: {self_ty:?}" - ) - } - } + if tcx.is_async_drop_in_place_coroutine(*coroutine_def_id) { + build_adrop_for_adrop_shim(tcx, proxy_ty, impl_ty, span, instance) + } else { + build_adrop_for_coroutine_shim(tcx, proxy_ty, impl_ty, span, instance) } +} - fn build_enum( - mut self, - adt_def: ty::AdtDef<'tcx>, - args: ty::GenericArgsRef<'tcx>, - surface_drop: Option, - ) -> Body<'tcx> { - let tcx = self.tcx; - - let surface = match surface_drop { - None => None, - Some(kind) => { - self.put_self(); - Some(match kind { - SurfaceDropKind::Async => self.combine_async_surface(), - SurfaceDropKind::Sync => self.combine_sync_surface(), - }) - } - }; - - let mut other = None; - for (variant_idx, discr) in adt_def.discriminants(tcx) { - let variant = adt_def.variant(variant_idx); - - let mut chain = None; - for (field_idx, field) in variant.fields.iter_enumerated() { - let field_ty = field.ty(tcx, args); - self.put_variant_field(variant.name, variant_idx, field_idx, field_ty); - let defer = self.combine_defer(field_ty); - chain = Some(match chain { - None => defer, - Some(chain) => self.combine_chain(chain, defer), - }) - } - let variant_dtor = chain.unwrap_or_else(|| self.put_noop()); - - other = Some(match other { - None => variant_dtor, - Some(other) => { - self.put_self(); - self.put_discr(discr); - self.combine_either(other, variant_dtor) - } - }); - } - let variants_dtor = other.unwrap_or_else(|| self.put_noop()); - - let dtor = match surface { - None => variants_dtor, - Some(surface) => self.combine_chain(surface, variants_dtor), - }; - self.combine_fuse(dtor); - self.return_() - } +// For async drop a "normal" coroutine: +// `async_drop_in_place::{closure}.poll()` is converted into `T.future_drop_poll()`. +// Every coroutine has its `poll` (calculate yourself a little further) +// and its `future_drop_poll` (drop yourself a little further). +fn build_adrop_for_coroutine_shim<'tcx>( + tcx: TyCtxt<'tcx>, + proxy_ty: Ty<'tcx>, + impl_ty: Ty<'tcx>, + span: Span, + instance: ty::InstanceKind<'tcx>, +) -> Body<'tcx> { + let ty::Coroutine(coroutine_def_id, impl_args) = impl_ty.kind() else { + bug!("build_adrop_for_coroutine_shim not for coroutine impl type: ({:?})", instance); + }; + let proxy_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, proxy_ty); + // taking _1.0 (impl from Pin) + let pin_proxy_layout_local = Local::new(1); + let source_info = SourceInfo::outermost(span); + // converting `(_1: Pin<&mut CorLayout>, _2: &mut Context<'_>) -> Poll<()>` + // into `(_1: Pin<&mut ProxyLayout>, _2: &mut Context<'_>) -> Poll<()>` + // let mut _x: &mut CorLayout = &*_1.0.0; + // Replace old _1.0 accesses into _x accesses; + let body = tcx.optimized_mir(*coroutine_def_id).future_drop_poll().unwrap(); + let mut body: Body<'tcx> = EarlyBinder::bind(body.clone()).instantiate(tcx, impl_args); + body.source.instance = instance; + body.phase = MirPhase::Runtime(RuntimePhase::Initial); + body.var_debug_info.clear(); + let pin_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Pin, Some(span))); + let args = tcx.mk_args(&[proxy_ref.into()]); + let pin_proxy_ref = Ty::new_adt(tcx, pin_adt_ref, args); + + let cor_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, impl_ty); + + let proxy_ref_local = body.local_decls.push(LocalDecl::new(proxy_ref, span)); + let cor_ref_local = body.local_decls.push(LocalDecl::new(cor_ref, span)); + + FixProxyFutureDropVisitor { tcx, replace_to: cor_ref_local }.visit_body(&mut body); + // Now changing first arg from Pin<&mut ImplCoroutine> to Pin<&mut ProxyCoroutine> + body.local_decls[pin_proxy_layout_local] = LocalDecl::new(pin_proxy_ref, span); - fn build_chain(mut self, surface_drop: Option, elem_tys: I) -> Body<'tcx> - where - I: Iterator> + ExactSizeIterator, { - let surface = match surface_drop { - None => None, - Some(kind) => { - self.put_self(); - Some(match kind { - SurfaceDropKind::Async => self.combine_async_surface(), - SurfaceDropKind::Sync => self.combine_sync_surface(), - }) - } - }; - - let mut chain = None; - for (field_idx, field_ty) in elem_tys.enumerate().map(|(i, ty)| (FieldIdx::new(i), ty)) { - self.put_field(field_idx, field_ty); - let defer = self.combine_defer(field_ty); - chain = Some(match chain { - None => defer, - Some(chain) => self.combine_chain(chain, defer), - }) - } - let chain = chain.unwrap_or_else(|| self.put_noop()); - - let dtor = match surface { - None => chain, - Some(surface) => self.combine_chain(surface, chain), - }; - self.combine_fuse(dtor); - self.return_() - } - - fn build_zst_output(mut self) -> Body<'tcx> { - self.put_zst_output(); - self.return_() - } - - fn build_deferred_drop_in_place(mut self) -> Body<'tcx> { - self.put_self(); - let deferred = self.combine_deferred_drop_in_place(); - self.combine_fuse(deferred); - self.return_() - } - - fn build_fused_async_surface(mut self) -> Body<'tcx> { - self.put_self(); - let surface = self.combine_async_surface(); - self.combine_fuse(surface); - self.return_() - } - - fn build_fused_sync_surface(mut self) -> Body<'tcx> { - self.put_self(); - let surface = self.combine_sync_surface(); - self.combine_fuse(surface); - self.return_() - } - - fn build_slice(mut self, is_array: bool, elem_ty: Ty<'tcx>) -> Body<'tcx> { - if is_array { - self.put_array_as_slice(elem_ty) - } else { - self.put_self() - } - let dtor = self.combine_slice(elem_ty); - self.combine_fuse(dtor); - self.return_() - } - - fn put_zst_output(&mut self) { - let return_ty = self.locals[RETURN_PLACE].ty; - self.put_operand(Operand::Constant(Box::new(ConstOperand { - span: self.span, - user_ty: None, - const_: Const::zero_sized(return_ty), - }))); - } - - /// Puts `to_drop: *mut Self` on top of the stack. - fn put_self(&mut self) { - self.put_operand(Operand::Copy(Self::SELF_PTR.into())) - } - - /// Given that `Self is [ElemTy; N]` puts `to_drop: *mut [ElemTy]` - /// on top of the stack. - fn put_array_as_slice(&mut self, elem_ty: Ty<'tcx>) { - let slice_ptr_ty = Ty::new_mut_ptr(self.tcx, Ty::new_slice(self.tcx, elem_ty)); - self.put_temp_rvalue(Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::Unsize, CoercionSource::Implicit), - Operand::Copy(Self::SELF_PTR.into()), - slice_ptr_ty, - )) - } - - /// If given Self is a struct puts `to_drop: *mut FieldTy` on top - /// of the stack. - fn put_field(&mut self, field: FieldIdx, field_ty: Ty<'tcx>) { - let place = Place { - local: Self::SELF_PTR, - projection: self - .tcx - .mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]), - }; - self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place)) - } - - /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of - /// the stack. - fn put_variant_field( - &mut self, - variant_sym: Symbol, - variant: VariantIdx, - field: FieldIdx, - field_ty: Ty<'tcx>, - ) { - let place = Place { - local: Self::SELF_PTR, - projection: self.tcx.mk_place_elems(&[ - PlaceElem::Deref, - PlaceElem::Downcast(Some(variant_sym), variant), - PlaceElem::Field(field, field_ty), - ]), - }; - self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place)) - } - - /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of - /// the stack. - fn put_discr(&mut self, discr: Discr<'tcx>) { - let (size, _) = discr.ty.int_size_and_signed(self.tcx); - self.put_operand(Operand::const_from_scalar( - self.tcx, - discr.ty, - interpret::Scalar::from_uint(discr.val, size), - self.span, - )); - } - - /// Puts `x: RvalueType` on top of the stack. - fn put_temp_rvalue(&mut self, rvalue: Rvalue<'tcx>) { - let last_bb = &mut self.bbs[self.last_bb]; - debug_assert!(last_bb.terminator.is_none()); - let source_info = self.source_info; - - let local_ty = rvalue.ty(&self.locals, self.tcx); - // We need to create a new local to be able to "consume" it with - // a combinator - let local = self.locals.push(LocalDecl::with_source_info(local_ty, source_info)); - last_bb.statements.extend_from_slice(&[ - Statement { source_info, kind: StatementKind::StorageLive(local) }, + let mut idx: usize = 0; + // _proxy = _1.0 : Pin<&ProxyLayout> ==> &ProxyLayout + let proxy_ref_place = Place::from(pin_proxy_layout_local) + .project_deeper(&[PlaceElem::Field(FieldIdx::ZERO, proxy_ref)], tcx); + body.basic_blocks_mut()[START_BLOCK].statements.insert( + idx, Statement { source_info, - kind: StatementKind::Assign(Box::new((local.into(), rvalue))), + kind: StatementKind::Assign(Box::new(( + Place::from(proxy_ref_local), + Rvalue::CopyForDeref(proxy_ref_place), + ))), }, - ]); - - self.put_operand(Operand::Move(local.into())); - } - - /// Puts operand on top of the stack. - fn put_operand(&mut self, operand: Operand<'tcx>) { - if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb { - let source_info = self.source_info; - match &operand { - Operand::Copy(_) | Operand::Constant(_) => { - *top_cleanup_bb = self.bbs.push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { - source_info, - kind: TerminatorKind::Goto { target: *top_cleanup_bb }, - }), - is_cleanup: true, - }); - } - Operand::Move(place) => { - let local = place.as_local().unwrap(); - *top_cleanup_bb = self.bbs.push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { - source_info, - kind: if self.locals[local].ty.needs_drop(self.tcx, self.typing_env) { - TerminatorKind::Drop { - place: local.into(), - target: *top_cleanup_bb, - unwind: UnwindAction::Terminate( - UnwindTerminateReason::InCleanup, - ), - replace: false, - } - } else { - TerminatorKind::Goto { target: *top_cleanup_bb } - }, - }), - is_cleanup: true, - }); - } - }; - } - self.stack.push(operand); - } - - /// Puts `noop: async_drop::Noop` on top of the stack - fn put_noop(&mut self) -> Ty<'tcx> { - self.apply_combinator(0, LangItem::AsyncDropNoop, &[]) - } - - fn combine_async_surface(&mut self) -> Ty<'tcx> { - self.apply_combinator(1, LangItem::SurfaceAsyncDropInPlace, &[self.self_ty.unwrap().into()]) - } - - fn combine_sync_surface(&mut self) -> Ty<'tcx> { - self.apply_combinator( - 1, - LangItem::AsyncDropSurfaceDropInPlace, - &[self.self_ty.unwrap().into()], - ) - } - - fn combine_deferred_drop_in_place(&mut self) -> Ty<'tcx> { - self.apply_combinator( - 1, - LangItem::AsyncDropDeferredDropInPlace, - &[self.self_ty.unwrap().into()], - ) - } - - fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> { - self.apply_combinator(1, LangItem::AsyncDropFuse, &[inner_future_ty.into()]) - } - - fn combine_slice(&mut self, elem_ty: Ty<'tcx>) -> Ty<'tcx> { - self.apply_combinator(1, LangItem::AsyncDropSlice, &[elem_ty.into()]) - } - - fn combine_defer(&mut self, to_drop_ty: Ty<'tcx>) -> Ty<'tcx> { - self.apply_combinator(1, LangItem::AsyncDropDefer, &[to_drop_ty.into()]) - } - - fn combine_chain(&mut self, first: Ty<'tcx>, second: Ty<'tcx>) -> Ty<'tcx> { - self.apply_combinator(2, LangItem::AsyncDropChain, &[first.into(), second.into()]) - } - - fn combine_either(&mut self, other: Ty<'tcx>, matched: Ty<'tcx>) -> Ty<'tcx> { - self.apply_combinator( - 4, - LangItem::AsyncDropEither, - &[other.into(), matched.into(), self.self_ty.unwrap().into()], - ) - } - - fn return_(mut self) -> Body<'tcx> { - let last_bb = &mut self.bbs[self.last_bb]; - debug_assert!(last_bb.terminator.is_none()); - let source_info = self.source_info; - - let (1, Some(output)) = (self.stack.len(), self.stack.pop()) else { - span_bug!( - self.span, - "async destructor ctor shim builder finished with invalid number of stack items: expected 1 found {}", - self.stack.len(), - ) - }; - #[cfg(debug_assertions)] - if let Some(ty) = self.self_ty { - debug_assert_eq!( - output.ty(&self.locals, self.tcx), - ty.async_destructor_ty(self.tcx), - "output async destructor types did not match for type: {ty:?}", - ); - } - - let dead_storage = match &output { - Operand::Move(place) => Some(Statement { - source_info, - kind: StatementKind::StorageDead(place.as_local().unwrap()), - }), - _ => None, - }; - - last_bb.statements.extend( - iter::once(Statement { - source_info, - kind: StatementKind::Assign(Box::new((RETURN_PLACE.into(), Rvalue::Use(output)))), - }) - .chain(dead_storage), ); - - last_bb.terminator = Some(Terminator { source_info, kind: TerminatorKind::Return }); - - let source = MirSource::from_instance(ty::InstanceKind::AsyncDropGlueCtorShim( - self.def_id, - self.self_ty, - )); - new_body(source, self.bbs, self.locals, Self::INPUT_COUNT, self.span) - } - - fn apply_combinator( - &mut self, - arity: usize, - function: LangItem, - args: &[ty::GenericArg<'tcx>], - ) -> Ty<'tcx> { - let function = self.tcx.require_lang_item(function, Some(self.span)); - let operands_split = self - .stack - .len() - .checked_sub(arity) - .expect("async destructor ctor shim combinator tried to consume too many items"); - let operands = &self.stack[operands_split..]; - - let func_ty = Ty::new_fn_def(self.tcx, function, args.iter().copied()); - let func_sig = func_ty.fn_sig(self.tcx).no_bound_vars().unwrap(); - #[cfg(debug_assertions)] - operands.iter().zip(func_sig.inputs()).for_each(|(operand, expected_ty)| { - let operand_ty = operand.ty(&self.locals, self.tcx); - if operand_ty == *expected_ty { - return; - } - - // If projection of Discriminant then compare with `Ty::discriminant_ty` - if let ty::Alias(ty::Projection, ty::AliasTy { args, def_id, .. }) = expected_ty.kind() - && self.tcx.is_lang_item(*def_id, LangItem::Discriminant) - && args.first().unwrap().as_type().unwrap().discriminant_ty(self.tcx) == operand_ty - { - return; + idx += 1; + let mut cor_ptr_local = proxy_ref_local; + proxy_ty.find_async_drop_impl_coroutine(tcx, |ty| { + if ty != proxy_ty { + let ty_ptr = Ty::new_mut_ptr(tcx, ty); + let impl_ptr_place = Place::from(cor_ptr_local).project_deeper( + &[PlaceElem::Deref, PlaceElem::Field(FieldIdx::ZERO, ty_ptr)], + tcx, + ); + cor_ptr_local = body.local_decls.push(LocalDecl::new(ty_ptr, span)); + // _cor_ptr = _proxy.0.0 (... .0) + body.basic_blocks_mut()[START_BLOCK].statements.insert( + idx, + Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + Place::from(cor_ptr_local), + Rvalue::CopyForDeref(impl_ptr_place), + ))), + }, + ); + idx += 1; } - - span_bug!( - self.span, - "Operand type and combinator argument type are not equal. - operand_ty: {:?} - argument_ty: {:?} -", - operand_ty, - expected_ty - ); - }); - - let target = self.bbs.push(BasicBlockData { - statements: operands - .iter() - .rev() - .filter_map(|o| { - if let Operand::Move(Place { local, projection }) = o { - assert!(projection.is_empty()); - Some(Statement { - source_info: self.source_info, - kind: StatementKind::StorageDead(*local), - }) - } else { - None - } - }) - .collect(), - terminator: None, - is_cleanup: false, }); - let dest_ty = func_sig.output(); - let dest = - self.locals.push(LocalDecl::with_source_info(dest_ty, self.source_info).immutable()); - - let unwind = if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb { - for _ in 0..arity { - *top_cleanup_bb = - self.bbs[*top_cleanup_bb].terminator().successors().exactly_one().ok().unwrap(); - } - UnwindAction::Cleanup(*top_cleanup_bb) - } else { - UnwindAction::Unreachable - }; + // _cor_ref = &*cor_ptr + let reborrow = Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + tcx.mk_place_deref(Place::from(cor_ptr_local)), + ); + body.basic_blocks_mut()[START_BLOCK].statements.insert( + idx, + Statement { + source_info, + kind: StatementKind::Assign(Box::new((Place::from(cor_ref_local), reborrow))), + }, + ); + } + body +} - let last_bb = &mut self.bbs[self.last_bb]; - debug_assert!(last_bb.terminator.is_none()); - last_bb.statements.push(Statement { - source_info: self.source_info, - kind: StatementKind::StorageLive(dest), - }); - last_bb.terminator = Some(Terminator { - source_info: self.source_info, +// When dropping async drop coroutine, we continue its execution. +// async_drop(async_drop(T))::future_drop_poll() => async_drop(T)::poll() +fn build_adrop_for_adrop_shim<'tcx>( + tcx: TyCtxt<'tcx>, + proxy_ty: Ty<'tcx>, + impl_ty: Ty<'tcx>, + span: Span, + instance: ty::InstanceKind<'tcx>, +) -> Body<'tcx> { + let source_info = SourceInfo::outermost(span); + let proxy_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, proxy_ty); + // taking _1.0 (impl from Pin) + let pin_proxy_layout_local = Local::new(1); + let proxy_ref_place = Place::from(pin_proxy_layout_local) + .project_deeper(&[PlaceElem::Field(FieldIdx::ZERO, proxy_ref)], tcx); + let cor_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, impl_ty); + + // ret_ty = `Poll<()>` + let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)); + let ret_ty = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()])); + // env_ty = `Pin<&mut proxy_ty>` + let pin_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Pin, None)); + let env_ty = Ty::new_adt(tcx, pin_adt_ref, tcx.mk_args(&[proxy_ref.into()])); + // sig = `fn (Pin<&mut proxy_ty>, &mut Context) -> Poll<()>` + let sig = tcx.mk_fn_sig( + [env_ty, Ty::new_task_context(tcx)], + ret_ty, + false, + hir::Safety::Safe, + ExternAbi::Rust, + ); + // This function will be called with pinned proxy coroutine layout. + // We need to extract `Arg0.0` to get proxy layout, and then get `.0` + // further to receive impl coroutine (may be needed) + let mut locals = local_decls_for_sig(&sig, span); + let mut blocks = IndexVec::with_capacity(3); + + let proxy_ref_local = locals.push(LocalDecl::new(proxy_ref, span)); + + let call_bb = BasicBlock::new(1); + let return_bb = BasicBlock::new(2); + + let mut statements = Vec::new(); + + statements.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + Place::from(proxy_ref_local), + Rvalue::CopyForDeref(proxy_ref_place), + ))), + }); + + let mut cor_ptr_local = proxy_ref_local; + proxy_ty.find_async_drop_impl_coroutine(tcx, |ty| { + if ty != proxy_ty { + let ty_ptr = Ty::new_mut_ptr(tcx, ty); + let impl_ptr_place = Place::from(cor_ptr_local) + .project_deeper(&[PlaceElem::Deref, PlaceElem::Field(FieldIdx::ZERO, ty_ptr)], tcx); + cor_ptr_local = locals.push(LocalDecl::new(ty_ptr, span)); + // _cor_ptr = _proxy.0.0 (... .0) + statements.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + Place::from(cor_ptr_local), + Rvalue::CopyForDeref(impl_ptr_place), + ))), + }); + } + }); + + // convert impl coroutine ptr into ref + let reborrow = Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + tcx.mk_place_deref(Place::from(cor_ptr_local)), + ); + let cor_ref_place = Place::from(locals.push(LocalDecl::new(cor_ref, span))); + statements.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new((cor_ref_place, reborrow))), + }); + + // cor_pin_ty = `Pin<&mut cor_ref>` + let cor_pin_ty = Ty::new_adt(tcx, pin_adt_ref, tcx.mk_args(&[cor_ref.into()])); + let cor_pin_place = Place::from(locals.push(LocalDecl::new(cor_pin_ty, span))); + + let pin_fn = tcx.require_lang_item(LangItem::PinNewUnchecked, Some(span)); + // call Pin::new_unchecked(&mut impl_cor) + blocks.push(BasicBlockData { + statements, + terminator: Some(Terminator { + source_info, kind: TerminatorKind::Call { - func: Operand::Constant(Box::new(ConstOperand { - span: self.span, - user_ty: None, - const_: Const::Val(ConstValue::ZeroSized, func_ty), - })), - destination: dest.into(), - target: Some(target), - unwind, + func: Operand::function_handle(tcx, pin_fn, [cor_ref.into()], span), + args: [dummy_spanned(Operand::Move(cor_ref_place))].into(), + destination: cor_pin_place, + target: Some(call_bb), + unwind: UnwindAction::Continue, call_source: CallSource::Misc, - fn_span: self.span, - args: self.stack.drain(operands_split..).map(|o| respan(self.span, o)).collect(), + fn_span: span, }, - }); - - self.put_operand(Operand::Move(dest.into())); - self.last_bb = target; - - dest_ty - } + }), + is_cleanup: false, + }); + // When dropping async drop coroutine, we continue its execution: + // we call impl::poll (impl_layout, ctx) + let poll_fn = tcx.require_lang_item(LangItem::FuturePoll, None); + let resume_ctx = Place::from(Local::new(2)); + blocks.push(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::Call { + func: Operand::function_handle(tcx, poll_fn, [impl_ty.into()], span), + args: [ + dummy_spanned(Operand::Move(cor_pin_place)), + dummy_spanned(Operand::Move(resume_ctx)), + ] + .into(), + destination: Place::return_place(), + target: Some(return_bb), + unwind: UnwindAction::Continue, + call_source: CallSource::Misc, + fn_span: span, + }, + }), + is_cleanup: false, + }); + blocks.push(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), + is_cleanup: false, + }); + + let source = MirSource::from_instance(instance); + let mut body = new_body(source, blocks, locals, sig.inputs().len(), span); + body.phase = MirPhase::Runtime(RuntimePhase::Initial); + return body; } diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 84905f4a400f3..8f88228d9bbd8 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -26,6 +26,13 @@ //! 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. +//! +//! **WARNING**: This is one of the few optimizations that runs on built and analysis MIR, and +//! so its effects may affect the type-checking, borrow-checking, and other analysis of MIR. +//! We must be extremely careful to only apply optimizations that preserve UB and all +//! non-determinism, since changes here can affect which programs compile in an insta-stable way. +//! The normal logic that a program with UB can be changed to do anything does not apply to +//! pre-"runtime" MIR! use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; @@ -66,8 +73,8 @@ impl SimplifyCfg { } } -pub(super) fn simplify_cfg(body: &mut Body<'_>) { - CfgSimplifier::new(body).simplify(); +pub(super) fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + CfgSimplifier::new(tcx, body).simplify(); remove_dead_blocks(body); // FIXME: Should probably be moved into some kind of pass manager @@ -79,9 +86,9 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg { self.name() } - fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!("SimplifyCfg({:?}) - simplifying {:?}", self.name(), body.source); - simplify_cfg(body); + simplify_cfg(tcx, body); } fn is_required(&self) -> bool { @@ -90,12 +97,13 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg { } struct CfgSimplifier<'a, 'tcx> { + preserve_switch_reads: bool, basic_blocks: &'a mut IndexSlice>, pred_count: IndexVec, } impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { - fn new(body: &'a mut Body<'tcx>) -> Self { + fn new(tcx: TyCtxt<'tcx>, 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 @@ -110,9 +118,12 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { } } + // Preserve `SwitchInt` reads on built and analysis MIR, or if `-Zmir-preserve-ub`. + let preserve_switch_reads = matches!(body.phase, MirPhase::Built | MirPhase::Analysis(_)) + || tcx.sess.opts.unstable_opts.mir_preserve_ub; let basic_blocks = body.basic_blocks_mut(); - CfgSimplifier { basic_blocks, pred_count } + CfgSimplifier { preserve_switch_reads, basic_blocks, pred_count } } fn simplify(mut self) { @@ -136,9 +147,8 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { 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); - } + terminator + .successors_mut(|successor| self.collapse_goto_chain(successor, &mut changed)); let mut inner_changed = true; merged_blocks.clear(); @@ -253,9 +263,15 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // 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, + // Removing a `SwitchInt` terminator may remove reads that result in UB, + // so we must not apply this optimization before borrowck or when + // `-Zmir-preserve-ub` is set. + if self.preserve_switch_reads { + return false; + } + + let TerminatorKind::SwitchInt { .. } = terminator.kind else { + return false; }; let first_succ = { @@ -358,9 +374,7 @@ pub(super) fn remove_dead_blocks(body: &mut Body<'_>) { } for block in basic_blocks { - for target in block.terminator_mut().successors_mut() { - *target = replacements[target.index()]; - } + block.terminator_mut().successors_mut(|target| *target = replacements[target.index()]); } } @@ -597,20 +611,6 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { self.tcx } - fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) { - if let StatementKind::BackwardIncompatibleDropHint { place, reason: _ } = - &mut statement.kind - { - self.visit_local( - &mut place.local, - PlaceContext::MutatingUse(MutatingUseContext::Store), - location, - ); - } else { - self.super_statement(statement, location); - } - } - fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) { *l = self.map[*l].unwrap(); } diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index 3d512fb064ec4..edd0cabca49a4 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -32,12 +32,6 @@ pub(super) struct SsaLocals { borrowed_locals: DenseBitSet, } -pub(super) enum AssignedValue<'a, 'tcx> { - Arg, - Rvalue(&'a mut Rvalue<'tcx>), - Terminator, -} - impl SsaLocals { pub(super) fn new<'tcx>( tcx: TyCtxt<'tcx>, @@ -152,38 +146,6 @@ impl SsaLocals { }) } - pub(super) fn for_each_assignment_mut<'tcx>( - &self, - basic_blocks: &mut IndexSlice>, - mut f: impl FnMut(Local, AssignedValue<'_, 'tcx>, Location), - ) { - for &local in &self.assignment_order { - match self.assignments[local] { - Set1::One(DefLocation::Argument) => f( - local, - AssignedValue::Arg, - Location { block: START_BLOCK, statement_index: 0 }, - ), - Set1::One(DefLocation::Assignment(loc)) => { - let bb = &mut basic_blocks[loc.block]; - // `loc` must point to a direct assignment to `local`. - let stmt = &mut bb.statements[loc.statement_index]; - let StatementKind::Assign(box (target, ref mut rvalue)) = stmt.kind else { - bug!() - }; - assert_eq!(target.as_local(), Some(local)); - f(local, AssignedValue::Rvalue(rvalue), loc) - } - Set1::One(DefLocation::CallReturn { call, .. }) => { - let bb = &mut basic_blocks[call]; - let loc = Location { block: call, statement_index: bb.statements.len() }; - f(local, AssignedValue::Terminator, loc) - } - _ => {} - } - } - } - /// Compute the equivalence classes for locals, based on copy statements. /// /// The returned vector maps each local to the one it copies. In the following case: diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 4ac3a268c9c33..c8aa7588d03ab 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1,7 +1,7 @@ //! Validates the MIR to ensure that invariants are upheld. use rustc_abi::{ExternAbi, FIRST_VARIANT, Size}; -use rustc_attr_parsing::InlineAttr; +use rustc_attr_data_structures::InlineAttr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::LangItem; use rustc_index::IndexVec; @@ -12,12 +12,12 @@ use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::adjustment::PointerCoercion; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, CoroutineArgsExt, InstanceKind, ScalarInt, Ty, TyCtxt, TypeVisitableExt, Variance, + self, CoroutineArgsExt, InstanceKind, ScalarInt, Ty, TyCtxt, TypeVisitableExt, Upcast, Variance, }; use rustc_middle::{bug, span_bug}; use rustc_trait_selection::traits::ObligationCtxt; -use rustc_type_ir::Upcast; use crate::util::{self, is_within_packed}; @@ -57,7 +57,7 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { ty::Coroutine(..) => ExternAbi::Rust, // No need to do MIR validation on error bodies ty::Error(_) => return, - _ => span_bug!(body.span, "unexpected body ty: {body_ty:?}"), + _ => span_bug!(body.span, "unexpected body ty: {body_ty}"), }; ty::layout::fn_can_unwind(tcx, Some(def_id), body_abi) @@ -221,12 +221,11 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> { // Check for cycles let mut stack = FxHashSet::default(); - for i in 0..parent.len() { - let mut bb = BasicBlock::from_usize(i); + for (mut bb, parent) in parent.iter_enumerated_mut() { stack.clear(); stack.insert(bb); loop { - let Some(parent) = parent[bb].take() else { break }; + let Some(parent) = parent.take() else { break }; let no_cycle = stack.insert(parent); if !no_cycle { self.fail( @@ -373,9 +372,12 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { ); } } - TerminatorKind::Drop { target, unwind, .. } => { + TerminatorKind::Drop { target, unwind, drop, .. } => { self.check_edge(location, *target, EdgeKind::Normal); self.check_unwind_edge(location, *unwind); + if let Some(drop) = drop { + self.check_edge(location, *drop, EdgeKind::Normal); + } } TerminatorKind::Call { func, args, .. } | TerminatorKind::TailCall { func, args, .. } => { @@ -544,7 +546,13 @@ pub(super) fn validate_types<'tcx>( caller_body: &Body<'tcx>, ) -> Vec<(Location, String)> { let mut type_checker = TypeChecker { body, caller_body, tcx, typing_env, failures: Vec::new() }; - type_checker.visit_body(body); + // The type checker formats a bunch of strings with type names in it, but these strings + // are not always going to be encountered on the error path since the inliner also uses + // the validator, and there are certain kinds of inlining (even for valid code) that + // can cause validation errors (mostly around where clauses and rigid projections). + with_no_trimmed_paths!({ + type_checker.visit_body(body); + }); type_checker.failures } @@ -656,7 +664,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ProjectionElem::Index(index) => { let index_ty = self.body.local_decls[index].ty; if index_ty != self.tcx.types.usize { - self.fail(location, format!("bad index ({index_ty:?} != usize)")) + self.fail(location, format!("bad index ({index_ty} != usize)")) } } ProjectionElem::Deref @@ -665,10 +673,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let base_ty = place_ref.ty(&self.body.local_decls, self.tcx).ty; if base_ty.is_box() { - self.fail( - location, - format!("{base_ty:?} dereferenced after ElaborateBoxDerefs"), - ) + self.fail(location, format!("{base_ty} dereferenced after ElaborateBoxDerefs")) } } ProjectionElem::Field(f, ty) => { @@ -681,7 +686,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { this.fail( location, format!( - "Field projection `{place_ref:?}.{f:?}` specified type `{ty:?}`, but actual type is `{f_ty:?}`" + "Field projection `{place_ref:?}.{f:?}` specified type `{ty}`, but actual type is `{f_ty}`" ) ) } @@ -745,7 +750,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // since we may be in the process of computing this MIR in the // first place. let layout = if def_id == self.caller_body.source.def_id() { - self.caller_body.coroutine_layout_raw() + self.caller_body + .coroutine_layout_raw() + .or_else(|| self.tcx.coroutine_layout(def_id, args).ok()) } else if self.tcx.needs_coroutine_by_move_body_def_id(def_id) && let ty::ClosureKind::FnOnce = args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap() @@ -755,7 +762,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // Same if this is the by-move body of a coroutine-closure. self.caller_body.coroutine_layout_raw() } else { - self.tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()) + self.tcx.coroutine_layout(def_id, args).ok() }; let Some(layout) = layout else { @@ -807,7 +814,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail( location, format!( - "Failed subtyping {ty:#?} and {:#?}", + "Failed subtyping {ty} and {}", place_ref.ty(&self.body.local_decls, self.tcx).ty ), ) @@ -827,7 +834,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail( location, format!( - "Cannot unwrap unsafe binder {binder_ty:?} into type {unwrapped_ty:?}" + "Cannot unwrap unsafe binder {binder_ty:?} into type {unwrapped_ty}" ), ); } @@ -842,7 +849,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { if ty.is_union() || ty.is_enum() { self.fail( START_BLOCK.start_location(), - format!("invalid type {ty:?} in debuginfo for {:?}", debuginfo.name), + format!("invalid type {ty} in debuginfo for {:?}", debuginfo.name), ); } if projection.is_empty() { @@ -1065,15 +1072,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { if !self.mir_assign_valid_types(a, b) { self.fail( location, - format!("Cannot {op:?} compare incompatible types {a:?} and {b:?}"), + format!("Cannot {op:?} compare incompatible types {a} and {b}"), ); } } else if a != b { self.fail( location, - format!( - "Cannot perform binary op {op:?} on unequal types {a:?} and {b:?}" - ), + format!("Cannot perform binary op {op:?} on unequal types {a} and {b}"), ); } } @@ -1082,7 +1087,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Offset => { check_kinds!(a, "Cannot offset non-pointer type {:?}", ty::RawPtr(..)); if b != self.tcx.types.isize && b != self.tcx.types.usize { - self.fail(location, format!("Cannot offset by non-isize type {b:?}")); + self.fail(location, format!("Cannot offset by non-isize type {b}")); } } Eq | Lt | Le | Ne | Ge | Gt => { @@ -1314,7 +1319,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { { self.fail( location, - format!("Cannot transmute from non-`Sized` type {op_ty:?}"), + format!("Cannot transmute from non-`Sized` type {op_ty}"), ); } if !self @@ -1341,7 +1346,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } Rvalue::NullaryOp(NullOp::OffsetOf(indices), container) => { let fail_out_of_bounds = |this: &mut Self, location, field, ty| { - this.fail(location, format!("Out of bounds field {field:?} for {ty:?}")); + this.fail(location, format!("Out of bounds field {field:?} for {ty}")); }; let mut current_ty = *container; @@ -1375,7 +1380,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { _ => { self.fail( location, - format!("Cannot get offset ({variant:?}, {field:?}) from type {current_ty:?}"), + format!("Cannot get offset ({variant:?}, {field:?}) from type {current_ty}"), ); return; } @@ -1404,7 +1409,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { if !self.mir_assign_valid_types(unwrapped_ty, binder_inner_ty) { self.fail( location, - format!("Cannot wrap {unwrapped_ty:?} into unsafe binder {binder_ty:?}"), + format!("Cannot wrap {unwrapped_ty} into unsafe binder {binder_ty:?}"), ); } } @@ -1490,24 +1495,27 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // since CopyNonOverlapping is parametrized by 1 type, // we only need to check that they are equal and not keep an extra parameter. if !self.mir_assign_valid_types(op_src_ty, op_dst_ty) { - self.fail(location, format!("bad arg ({op_src_ty:?} != {op_dst_ty:?})")); + self.fail(location, format!("bad arg ({op_src_ty} != {op_dst_ty})")); } let op_cnt_ty = count.ty(&self.body.local_decls, self.tcx); if op_cnt_ty != self.tcx.types.usize { - self.fail(location, format!("bad arg ({op_cnt_ty:?} != usize)")) + self.fail(location, format!("bad arg ({op_cnt_ty} != usize)")) } } StatementKind::SetDiscriminant { place, .. } => { if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`SetDiscriminant`is not allowed until deaggregation"); } - let pty = place.ty(&self.body.local_decls, self.tcx).ty.kind(); - if !matches!(pty, ty::Adt(..) | ty::Coroutine(..) | ty::Alias(ty::Opaque, ..)) { + let pty = place.ty(&self.body.local_decls, self.tcx).ty; + if !matches!( + pty.kind(), + ty::Adt(..) | ty::Coroutine(..) | ty::Alias(ty::Opaque, ..) + ) { self.fail( location, format!( - "`SetDiscriminant` is only allowed on ADTs and coroutines, not {pty:?}" + "`SetDiscriminant` is only allowed on ADTs and coroutines, not {pty}" ), ); } @@ -1556,7 +1564,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { if ScalarInt::try_from_uint(value, size).is_none() { self.fail( location, - format!("the value {value:#x} is not a proper {switch_ty:?}"), + format!("the value {value:#x} is not a proper {switch_ty}"), ) } } diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 36b76d261de68..063fc8f1c7494 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl index bdeab12ab503b..35bedf4318ffa 100644 --- a/compiler/rustc_monomorphize/messages.ftl +++ b/compiler/rustc_monomorphize/messages.ftl @@ -48,7 +48,7 @@ monomorphize_large_assignments = .note = The current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` monomorphize_no_optimized_mir = - missing optimized MIR for an item in the crate `{$crate_name}` + missing optimized MIR for `{$instance}` in the crate `{$crate_name}` .note = missing optimized MIR for this item (was the crate `{$crate_name}` compiled with `--emit=metadata`?) monomorphize_recursion_limit = @@ -60,7 +60,11 @@ monomorphize_start_not_found = using `fn main` requires the standard library monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined -monomorphize_unknown_cgu_collection_mode = - unknown codegen-item collection mode '{$mode}', falling back to 'lazy' mode +monomorphize_wasm_c_abi_transition = + this function {$is_call -> + [true] call + *[false] definition + } involves an argument of type `{$ty}` which is affected by the wasm ABI transition + .help = the "C" ABI Rust uses on wasm32-unknown-unknown will change to align with the standard "C" ABI for this target monomorphize_written_to_path = the full type name has been written to '{$path}' diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 08df0dedc6b0c..b3d7eaf332b25 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -205,11 +205,12 @@ //! this is not implemented however: a mono item will be produced //! regardless of whether it is actually needed or not. +use std::cell::OnceCell; use std::path::PathBuf; -use rustc_attr_parsing::InlineAttr; +use rustc_attr_data_structures::InlineAttr; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::sync::{LRef, MTLock, par_for_each_in}; +use rustc_data_structures::sync::{MTLock, par_for_each_in}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -225,13 +226,13 @@ use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::print::{shrunk_instance_name, with_no_trimmed_paths}; use rustc_middle::ty::{ - self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Interner, Ty, TyCtxt, - TypeFoldable, TypeVisitableExt, VtblEntry, + self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable, + TypeVisitableExt, VtblEntry, }; use rustc_middle::util::Providers; use rustc_middle::{bug, span_bug}; use rustc_session::Limit; -use rustc_session::config::EntryFnType; +use rustc_session::config::{DebugInfo, EntryFnType}; use rustc_span::source_map::{Spanned, dummy_spanned, respan}; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument, trace}; @@ -348,6 +349,27 @@ impl<'tcx> Extend>> for MonoItems<'tcx> { } } +fn collect_items_root<'tcx>( + tcx: TyCtxt<'tcx>, + starting_item: Spanned>, + state: &SharedState<'tcx>, + recursion_limit: Limit, +) { + if !state.visited.lock_mut().insert(starting_item.node) { + // We've been here already, no need to search again. + return; + } + let mut recursion_depths = DefIdMap::default(); + collect_items_rec( + tcx, + starting_item, + state, + &mut recursion_depths, + recursion_limit, + CollectionMode::UsedItems, + ); +} + /// Collect all monomorphized items reachable from `starting_point`, and emit a note diagnostic if a /// post-monomorphization error is encountered during a collection step. /// @@ -357,29 +379,11 @@ impl<'tcx> Extend>> for MonoItems<'tcx> { fn collect_items_rec<'tcx>( tcx: TyCtxt<'tcx>, starting_item: Spanned>, - state: LRef<'_, SharedState<'tcx>>, + state: &SharedState<'tcx>, recursion_depths: &mut DefIdMap, recursion_limit: Limit, mode: CollectionMode, ) { - if mode == CollectionMode::UsedItems { - if !state.visited.lock_mut().insert(starting_item.node) { - // We've been here already, no need to search again. - return; - } - } else { - if state.visited.lock().contains(&starting_item.node) { - // We've already done a *full* visit on this one, no need to do the "mention" visit. - return; - } - if !state.mentioned.lock_mut().insert(starting_item.node) { - // We've been here already, no need to search again. - return; - } - // There's some risk that we first do a 'mention' visit and then a full visit. But there's no - // harm in that, the mention visit will trigger all the queries and the results are cached. - } - let mut used_items = MonoItems::new(); let mut mentioned_items = MonoItems::new(); let recursion_depth_reset; @@ -536,6 +540,20 @@ fn collect_items_rec<'tcx>( state.usage_map.lock_mut().record_used(starting_item.node, &used_items); } + { + let mut visited = OnceCell::default(); + if mode == CollectionMode::UsedItems { + used_items + .items + .retain(|k, _| visited.get_mut_or_init(|| state.visited.lock_mut()).insert(*k)); + } + + let mut mentioned = OnceCell::default(); + mentioned_items.items.retain(|k, _| { + !visited.get_or_init(|| state.visited.lock()).contains(k) + && mentioned.get_mut_or_init(|| state.mentioned.lock_mut()).insert(*k) + }); + } if mode == CollectionMode::MentionedItems { assert!(used_items.is_empty(), "'mentioned' collection should never encounter used items"); } else { @@ -688,7 +706,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { let target_ty = self.monomorphize(target_ty); let source_ty = self.monomorphize(source_ty); let (source_ty, target_ty) = - find_vtable_types_for_unsizing(self.tcx.at(span), source_ty, target_ty); + find_tails_for_unsizing(self.tcx.at(span), source_ty, target_ty); // This could also be a different Unsize instruction, like // from a fixed sized array to a slice. But we are only // interested in things that produce a vtable. @@ -913,7 +931,7 @@ fn visit_instance_use<'tcx>( // We explicitly skip this otherwise to ensure we get a linker error // if anyone tries to call this intrinsic and the codegen backend did not // override the implementation. - let instance = ty::Instance::new(instance.def_id(), instance.args); + let instance = ty::Instance::new_raw(instance.def_id(), instance.args); if tcx.should_codegen_locally(instance) { output.push(create_fn_mono_item(tcx, instance, source)); } @@ -929,14 +947,16 @@ fn visit_instance_use<'tcx>( ty::InstanceKind::ThreadLocalShim(..) => { bug!("{:?} being reified", instance); } - ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) => { + ty::InstanceKind::DropGlue(_, None) => { // Don't need to emit noop drop glue if we are calling directly. if !is_direct_call { output.push(create_fn_mono_item(tcx, instance, source)); } } ty::InstanceKind::DropGlue(_, Some(_)) - | ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) + | ty::InstanceKind::FutureDropPollShim(..) + | ty::InstanceKind::AsyncDropGlue(_, _) + | ty::InstanceKind::AsyncDropGlueCtorShim(_, _) | ty::InstanceKind::VTableShim(..) | ty::InstanceKind::ReifyShim(..) | ty::InstanceKind::ClosureOnceShim { .. } @@ -967,7 +987,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> { // `#[rustc_force_inline]` items should never be codegened. This should be caught by // the MIR validator. - tcx.delay_bug("attempt to codegen `#[rustc_force_inline]` item"); + tcx.dcx().delayed_bug("attempt to codegen `#[rustc_force_inline]` item"); } if def_id.is_local() { @@ -989,6 +1009,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> tcx.dcx().emit_fatal(NoOptimizedMir { span: tcx.def_span(def_id), crate_name: tcx.crate_name(def_id.krate), + instance: instance.to_string(), }); } @@ -1036,36 +1057,35 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> /// /// Finally, there is also the case of custom unsizing coercions, e.g., for /// smart pointers such as `Rc` and `Arc`. -fn find_vtable_types_for_unsizing<'tcx>( +fn find_tails_for_unsizing<'tcx>( tcx: TyCtxtAt<'tcx>, source_ty: Ty<'tcx>, target_ty: Ty<'tcx>, ) -> (Ty<'tcx>, Ty<'tcx>) { - let ptr_vtable = |inner_source: Ty<'tcx>, inner_target: Ty<'tcx>| { - let typing_env = ty::TypingEnv::fully_monomorphized(); - if tcx.type_has_metadata(inner_source, typing_env) { - (inner_source, inner_target) - } else { - tcx.struct_lockstep_tails_for_codegen(inner_source, inner_target, typing_env) - } - }; + let typing_env = ty::TypingEnv::fully_monomorphized(); + debug_assert!(!source_ty.has_param(), "{source_ty} should be fully monomorphic"); + debug_assert!(!target_ty.has_param(), "{target_ty} should be fully monomorphic"); match (source_ty.kind(), target_ty.kind()) { - (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _)) - | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => ptr_vtable(a, b), + ( + &ty::Ref(_, source_pointee, _), + &ty::Ref(_, target_pointee, _) | &ty::RawPtr(target_pointee, _), + ) + | (&ty::RawPtr(source_pointee, _), &ty::RawPtr(target_pointee, _)) => { + tcx.struct_lockstep_tails_for_codegen(source_pointee, target_pointee, typing_env) + } + + // `Box` could go through the ADT code below, b/c it'll unpeel to `Unique`, + // and eventually bottom out in a raw ref, but we can micro-optimize it here. (_, _) if let Some(source_boxed) = source_ty.boxed_ty() && let Some(target_boxed) = target_ty.boxed_ty() => { - ptr_vtable(source_boxed, target_boxed) + tcx.struct_lockstep_tails_for_codegen(source_boxed, target_boxed, typing_env) } - // T as dyn* Trait - (_, &ty::Dynamic(_, _, ty::DynStar)) => ptr_vtable(source_ty, target_ty), - (&ty::Adt(source_adt_def, source_args), &ty::Adt(target_adt_def, target_args)) => { assert_eq!(source_adt_def, target_adt_def); - let CustomCoerceUnsized::Struct(coerce_index) = match crate::custom_coerce_unsize_info(tcx, source_ty, target_ty) { Ok(ccu) => ccu, @@ -1074,21 +1094,23 @@ fn find_vtable_types_for_unsizing<'tcx>( return (e, e); } }; + let coerce_field = &source_adt_def.non_enum_variant().fields[coerce_index]; + // We're getting a possibly unnormalized type, so normalize it. + let source_field = + tcx.normalize_erasing_regions(typing_env, coerce_field.ty(*tcx, source_args)); + let target_field = + tcx.normalize_erasing_regions(typing_env, coerce_field.ty(*tcx, target_args)); + find_tails_for_unsizing(tcx, source_field, target_field) + } - let source_fields = &source_adt_def.non_enum_variant().fields; - let target_fields = &target_adt_def.non_enum_variant().fields; - - assert!( - coerce_index.index() < source_fields.len() - && source_fields.len() == target_fields.len() - ); + // `T` as `dyn* Trait` unsizes *directly*. + // + // FIXME(dyn_star): This case is a bit awkward, b/c we're not really computing + // a tail here. We probably should handle this separately in the *caller* of + // this function, rather than returning something that is semantically different + // than what we return above. + (_, &ty::Dynamic(_, _, ty::DynStar)) => (source_ty, target_ty), - find_vtable_types_for_unsizing( - tcx, - source_fields[coerce_index].ty(*tcx, source_args), - target_fields[coerce_index].ty(*tcx, target_args), - ) - } _ => bug!( "find_vtable_types_for_unsizing: invalid coercion {:?} -> {:?}", source_ty, @@ -1235,6 +1257,11 @@ fn collect_items_of_instance<'tcx>( }; if mode == CollectionMode::UsedItems { + if tcx.sess.opts.debuginfo == DebugInfo::Full { + for var_debug_info in &body.var_debug_info { + collector.visit_var_debug_info(var_debug_info); + } + } for (bb, data) in traversal::mono_reachable(body, tcx, instance) { collector.visit_basic_block_data(bb, data) } @@ -1302,7 +1329,7 @@ fn visit_mentioned_item<'tcx>( } MentionedItem::UnsizeCast { source_ty, target_ty } => { let (source_ty, target_ty) = - find_vtable_types_for_unsizing(tcx.at(span), source_ty, target_ty); + find_tails_for_unsizing(tcx.at(span), source_ty, target_ty); // This could also be a different Unsize instruction, like // from a fixed sized array to a slice. But we are only // interested in things that produce a vtable. @@ -1493,7 +1520,7 @@ impl<'v> RootCollector<'_, 'v> { ty::Closure(def_id, args) | ty::Coroutine(def_id, args) | ty::CoroutineClosure(def_id, args) => { - Instance::new(def_id, self.tcx.erase_regions(args)) + Instance::new_raw(def_id, self.tcx.erase_regions(args)) } _ => unreachable!(), }; @@ -1671,30 +1698,18 @@ pub(crate) fn collect_crate_mono_items<'tcx>( debug!("building mono item graph, beginning at roots"); - let mut state = SharedState { + let state = SharedState { visited: MTLock::new(UnordSet::default()), mentioned: MTLock::new(UnordSet::default()), usage_map: MTLock::new(UsageMap::new()), }; let recursion_limit = tcx.recursion_limit(); - { - let state: LRef<'_, _> = &mut state; - - tcx.sess.time("monomorphization_collector_graph_walk", || { - par_for_each_in(roots, |root| { - let mut recursion_depths = DefIdMap::default(); - collect_items_rec( - tcx, - dummy_spanned(root), - state, - &mut recursion_depths, - recursion_limit, - CollectionMode::UsedItems, - ); - }); + tcx.sess.time("monomorphization_collector_graph_walk", || { + par_for_each_in(roots, |root| { + collect_items_root(tcx, dummy_spanned(*root), &state, recursion_limit); }); - } + }); // The set of MonoItems was created in an inherently indeterministic order because // of parallelism. We sort it here to ensure that the output is deterministic. diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 8dafbbca905f7..acf77b5916e4c 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -24,6 +24,7 @@ pub(crate) struct NoOptimizedMir { #[note] pub span: Span, pub crate_name: Symbol, + pub instance: String, } #[derive(LintDiagnostic)] @@ -64,15 +65,10 @@ pub(crate) struct EncounteredErrorWhileInstantiating { pub(crate) struct StartNotFound; #[derive(Diagnostic)] -#[diag(monomorphize_unknown_cgu_collection_mode)] -pub(crate) struct UnknownCguCollectionMode<'a> { - pub mode: &'a str, -} - -#[derive(LintDiagnostic)] #[diag(monomorphize_abi_error_disabled_vector_type)] #[help] pub(crate) struct AbiErrorDisabledVectorType<'a> { + #[primary_span] #[label] pub span: Span, pub required_feature: &'a str, @@ -81,9 +77,10 @@ pub(crate) struct AbiErrorDisabledVectorType<'a> { pub is_call: bool, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag(monomorphize_abi_error_unsupported_vector_type)] pub(crate) struct AbiErrorUnsupportedVectorType<'a> { + #[primary_span] #[label] pub span: Span, pub ty: Ty<'a>, @@ -103,3 +100,12 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { /// Whether this is a problem at a call site or at a declaration. pub is_call: bool, } + +#[derive(LintDiagnostic)] +#[diag(monomorphize_wasm_c_abi_transition)] +#[help] +pub(crate) struct WasmCAbiTransition<'a> { + pub ty: Ty<'a>, + /// Whether this is a problem at a call site or at a declaration. + pub is_call: bool, +} diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 714b64b3a231c..5c66017bc6113 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -3,8 +3,7 @@ #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] -#![feature(let_chains)] -#![warn(unreachable_pub)] +#![feature(once_cell_get_mut)] // tidy-alphabetical-end use rustc_hir::lang_items::LangItem; diff --git a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs index 9f26da7d5c64b..cfeaee0777610 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs @@ -1,13 +1,15 @@ //! This module ensures that if a function's ABI requires a particular target feature, //! that target feature is enabled both on the callee and all callers. -use rustc_abi::{BackendRepr, ExternAbi, RegKind}; -use rustc_hir::CRATE_HIR_ID; -use rustc_middle::mir::{self, traversal}; -use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt}; -use rustc_session::lint::builtin::ABI_UNSUPPORTED_VECTOR_TYPES; +use rustc_abi::{BackendRepr, RegKind}; +use rustc_hir::{CRATE_HIR_ID, HirId}; +use rustc_middle::mir::{self, Location, traversal}; +use rustc_middle::ty::layout::LayoutCx; +use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt, TypingEnv}; +use rustc_session::lint::builtin::WASM_C_ABI; use rustc_span::def_id::DefId; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; -use rustc_target::callconv::{Conv, FnAbi, PassMode}; +use rustc_target::callconv::{ArgAbi, Conv, FnAbi, PassMode}; +use rustc_target::spec::{HasWasmCAbiOpt, WasmCAbi}; use crate::errors; @@ -26,13 +28,15 @@ fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool { /// for a certain function. /// `is_call` indicates whether this is a call-site check or a definition-site check; /// this is only relevant for the wording in the emitted error. -fn do_check_abi<'tcx>( +fn do_check_simd_vector_abi<'tcx>( tcx: TyCtxt<'tcx>, abi: &FnAbi<'tcx, Ty<'tcx>>, def_id: DefId, is_call: bool, - span: impl Fn() -> Span, + loc: impl Fn() -> (Span, HirId), ) { + // We check this on all functions, including those using the "Rust" ABI. + // For the "Rust" ABI it would be a bug if the lint ever triggered, but better safe than sorry. let feature_def = tcx.sess.target.features_for_correct_vector_abi(); let codegen_attrs = tcx.codegen_fn_attrs(def_id); let have_feature = |feat: Symbol| { @@ -46,41 +50,32 @@ fn do_check_abi<'tcx>( let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) { Some((_, feature)) => feature, None => { - let span = span(); - tcx.emit_node_span_lint( - ABI_UNSUPPORTED_VECTOR_TYPES, - CRATE_HIR_ID, + let (span, _hir_id) = loc(); + tcx.dcx().emit_err(errors::AbiErrorUnsupportedVectorType { span, - errors::AbiErrorUnsupportedVectorType { - span, - ty: arg_abi.layout.ty, - is_call, - }, - ); + ty: arg_abi.layout.ty, + is_call, + }); continue; } }; if !have_feature(Symbol::intern(feature)) { // Emit error. - let span = span(); - tcx.emit_node_span_lint( - ABI_UNSUPPORTED_VECTOR_TYPES, - CRATE_HIR_ID, + let (span, _hir_id) = loc(); + tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType { span, - errors::AbiErrorDisabledVectorType { - span, - required_feature: feature, - ty: arg_abi.layout.ty, - is_call, - }, - ); + required_feature: feature, + ty: arg_abi.layout.ty, + is_call, + }); } } } // The `vectorcall` ABI is special in that it requires SSE2 no matter which types are being passed. if abi.conv == Conv::X86VectorCall && !have_feature(sym::sse2) { + let (span, _hir_id) = loc(); tcx.dcx().emit_err(errors::AbiRequiredTargetFeature { - span: span(), + span, required_feature: "sse2", abi: "vectorcall", is_call, @@ -88,6 +83,72 @@ fn do_check_abi<'tcx>( } } +/// Determines whether the given argument is passed the same way on the old and new wasm ABIs. +fn wasm_abi_safe<'tcx>(tcx: TyCtxt<'tcx>, arg: &ArgAbi<'tcx, Ty<'tcx>>) -> bool { + if matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) { + return true; + } + + // Both the old and the new ABIs treat vector types like `v128` the same + // way. + if uses_vector_registers(&arg.mode, &arg.layout.backend_repr) { + return true; + } + + // This matches `unwrap_trivial_aggregate` in the wasm ABI logic. + if arg.layout.is_aggregate() { + let cx = LayoutCx::new(tcx, TypingEnv::fully_monomorphized()); + if let Some(unit) = arg.layout.homogeneous_aggregate(&cx).ok().and_then(|ha| ha.unit()) { + let size = arg.layout.size; + // Ensure there's just a single `unit` element in `arg`. + if unit.size == size { + return true; + } + } + } + + // Zero-sized types are dropped in both ABIs, so they're safe + if arg.layout.is_zst() { + return true; + } + + false +} + +/// Warns against usage of `extern "C"` on wasm32-unknown-unknown that is affected by the +/// ABI transition. +fn do_check_wasm_abi<'tcx>( + tcx: TyCtxt<'tcx>, + abi: &FnAbi<'tcx, Ty<'tcx>>, + is_call: bool, + loc: impl Fn() -> (Span, HirId), +) { + // Only proceed for `extern "C" fn` on wasm32-unknown-unknown (same check as what `adjust_for_foreign_abi` uses to call `compute_wasm_abi_info`), + // and only proceed if `wasm_c_abi_opt` indicates we should emit the lint. + if !(tcx.sess.target.arch == "wasm32" + && tcx.sess.target.os == "unknown" + && tcx.wasm_c_abi_opt() == WasmCAbi::Legacy { with_lint: true } + && abi.conv == Conv::C) + { + return; + } + // Warn against all types whose ABI will change. Return values are not affected by this change. + for arg_abi in abi.args.iter() { + if wasm_abi_safe(tcx, arg_abi) { + continue; + } + let (span, hir_id) = loc(); + tcx.emit_node_span_lint( + WASM_C_ABI, + hir_id, + span, + errors::WasmCAbiTransition { ty: arg_abi.layout.ty, is_call }, + ); + // Let's only warn once per function. + break; + } +} + /// Checks that the ABI of a given instance of a function does not contain vector-passed arguments /// or return values for which the corresponding target feature is not enabled. fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { @@ -98,13 +159,15 @@ fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { // function. return; }; - do_check_abi( - tcx, - abi, - instance.def_id(), - /*is_call*/ false, - || tcx.def_span(instance.def_id()), - ) + let loc = || { + let def_id = instance.def_id(); + ( + tcx.def_span(def_id), + def_id.as_local().map(|did| tcx.local_def_id_to_hir_id(did)).unwrap_or(CRATE_HIR_ID), + ) + }; + do_check_simd_vector_abi(tcx, abi, instance.def_id(), /*is_call*/ false, loc); + do_check_wasm_abi(tcx, abi, /*is_call*/ false, loc); } /// Checks that a call expression does not try to pass a vector-passed argument which requires a @@ -112,11 +175,11 @@ fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { fn check_call_site_abi<'tcx>( tcx: TyCtxt<'tcx>, callee: Ty<'tcx>, - span: Span, caller: InstanceKind<'tcx>, + loc: impl Fn() -> (Span, HirId) + Copy, ) { - if callee.fn_sig(tcx).abi() == ExternAbi::Rust { - // "Rust" ABI never passes arguments in vector registers. + if callee.fn_sig(tcx).abi().is_rustic_abi() { + // we directly handle the soundness of Rust ABIs return; } let typing_env = ty::TypingEnv::fully_monomorphized(); @@ -141,7 +204,8 @@ fn check_call_site_abi<'tcx>( // ABI failed to compute; this will not get through codegen. return; }; - do_check_abi(tcx, callee_abi, caller.def_id(), /*is_call*/ true, || span); + do_check_simd_vector_abi(tcx, callee_abi, caller.def_id(), /*is_call*/ true, loc); + do_check_wasm_abi(tcx, callee_abi, /*is_call*/ true, loc); } fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) { @@ -157,7 +221,19 @@ fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &m ty::TypingEnv::fully_monomorphized(), ty::EarlyBinder::bind(callee_ty), ); - check_call_site_abi(tcx, callee_ty, *fn_span, body.source.instance); + check_call_site_abi(tcx, callee_ty, body.source.instance, || { + let loc = Location { + block: bb, + statement_index: body.basic_blocks[bb].statements.len(), + }; + ( + *fn_span, + body.source_info(loc) + .scope + .lint_root(&body.source_scopes) + .unwrap_or(CRATE_HIR_ID), + ) + }); } _ => {} } diff --git a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs index 838bfdab1ea59..7251ef478c6f1 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs @@ -3,7 +3,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; use rustc_middle::mir::visit::Visitor as MirVisitor; use rustc_middle::mir::{self, Location, traversal}; -use rustc_middle::ty::{self, AssocKind, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, AssocTag, Instance, Ty, TyCtxt, TypeFoldable}; use rustc_session::Limit; use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; use rustc_span::source_map::Spanned; @@ -148,11 +148,7 @@ impl<'tcx> MoveCheckVisitor<'tcx> { span: Span, ) { let source_info = self.body.source_info(location); - for reported_span in &self.move_size_spans { - if reported_span.overlaps(span) { - return; - } - } + let lint_root = source_info.scope.lint_root(&self.body.source_scopes); let Some(lint_root) = lint_root else { // This happens when the issue is in a function from a foreign crate that @@ -162,22 +158,43 @@ impl<'tcx> MoveCheckVisitor<'tcx> { // but correct span? This would make the lint at least accept crate-level lint attributes. return; }; + + // If the source scope is inlined by the MIR inliner, report the lint on the call site. + let reported_span = self + .body + .source_scopes + .get(source_info.scope) + .and_then(|source_scope_data| source_scope_data.inlined) + .map(|(_, call_site)| call_site) + .unwrap_or(span); + + for previously_reported_span in &self.move_size_spans { + if previously_reported_span.overlaps(reported_span) { + return; + } + } + self.tcx.emit_node_span_lint( LARGE_ASSIGNMENTS, lint_root, - span, - LargeAssignmentsLint { span, size: too_large_size.bytes(), limit: limit as u64 }, + reported_span, + LargeAssignmentsLint { + span: reported_span, + size: too_large_size.bytes(), + limit: limit as u64, + }, ); - self.move_size_spans.push(span); + + self.move_size_spans.push(reported_span); } } fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option { for impl_def_id in tcx.inherent_impls(def_id) { - if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind( + if let Some(new) = tcx.associated_items(impl_def_id).find_by_ident_and_kind( tcx, fn_ident, - AssocKind::Fn, + AssocTag::Fn, def_id, ) { return Some(new.def_id); diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index b1b6f10e0fe2c..b4169a060d4d6 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -100,6 +100,7 @@ use std::fs::{self, File}; use std::io::Write; use std::path::{Path, PathBuf}; +use rustc_attr_data_structures::InlineAttr; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sync; use rustc_data_structures::unord::{UnordMap, UnordSet}; @@ -124,7 +125,7 @@ use rustc_target::spec::SymbolVisibility; use tracing::debug; use crate::collector::{self, MonoItemCollectionStrategy, UsageMap}; -use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownCguCollectionMode}; +use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined}; struct PartitioningCx<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -223,7 +224,7 @@ where match mono_item.instantiation_mode(cx.tcx) { InstantiationMode::GloballyShared { .. } => {} InstantiationMode::LocalCopy => { - if Some(mono_item.def_id()) != cx.tcx.lang_items().start_fn() { + if !cx.tcx.is_lang_item(mono_item.def_id(), LangItem::Start) { continue; } } @@ -254,8 +255,9 @@ where always_export_generics, ); - // We can't differentiate something that got inlined. + // We can't differentiate a function that got inlined. let autodiff_active = cfg!(llvm_enzyme) + && matches!(mono_item, MonoItem::Fn(_)) && cx .tcx .codegen_fn_attrs(mono_item.def_id()) @@ -643,6 +645,8 @@ fn characteristic_def_id_of_mono_item<'tcx>( | ty::InstanceKind::CloneShim(..) | ty::InstanceKind::ThreadLocalShim(..) | ty::InstanceKind::FnPtrAddrShim(..) + | ty::InstanceKind::FutureDropPollShim(..) + | ty::InstanceKind::AsyncDropGlue(..) | ty::InstanceKind::AsyncDropGlueCtorShim(..) => return None, }; @@ -795,7 +799,9 @@ fn mono_item_visibility<'tcx>( let def_id = match instance.def { InstanceKind::Item(def_id) | InstanceKind::DropGlue(def_id, Some(_)) - | InstanceKind::AsyncDropGlueCtorShim(def_id, Some(_)) => def_id, + | InstanceKind::FutureDropPollShim(def_id, _, _) + | InstanceKind::AsyncDropGlue(def_id, _) + | InstanceKind::AsyncDropGlueCtorShim(def_id, _) => def_id, // We match the visibility of statics here InstanceKind::ThreadLocalShim(def_id) => { @@ -811,7 +817,6 @@ fn mono_item_visibility<'tcx>( | InstanceKind::ClosureOnceShim { .. } | InstanceKind::ConstructCoroutineInClosureShim { .. } | InstanceKind::DropGlue(..) - | InstanceKind::AsyncDropGlueCtorShim(..) | InstanceKind::CloneShim(..) | InstanceKind::FnPtrAddrShim(..) => return Visibility::Hidden, }; @@ -841,8 +846,7 @@ fn mono_item_visibility<'tcx>( return if is_generic && (always_export_generics || (can_export_generics - && tcx.codegen_fn_attrs(def_id).inline - == rustc_attr_parsing::InlineAttr::Never)) + && tcx.codegen_fn_attrs(def_id).inline == InlineAttr::Never)) { // If it is an upstream monomorphization and we export generics, we must make // it available to downstream crates. @@ -855,8 +859,7 @@ fn mono_item_visibility<'tcx>( if is_generic { if always_export_generics - || (can_export_generics - && tcx.codegen_fn_attrs(def_id).inline == rustc_attr_parsing::InlineAttr::Never) + || (can_export_generics && tcx.codegen_fn_attrs(def_id).inline == InlineAttr::Never) { if tcx.is_unreachable_local_definition(def_id) { // This instance cannot be used from another crate. @@ -1123,27 +1126,10 @@ where } fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitions<'_> { - let collection_strategy = match tcx.sess.opts.unstable_opts.print_mono_items { - Some(ref s) => { - let mode = s.to_lowercase(); - let mode = mode.trim(); - if mode == "eager" { - MonoItemCollectionStrategy::Eager - } else { - if mode != "lazy" { - tcx.dcx().emit_warn(UnknownCguCollectionMode { mode }); - } - - MonoItemCollectionStrategy::Lazy - } - } - None => { - if tcx.sess.link_dead_code() { - MonoItemCollectionStrategy::Eager - } else { - MonoItemCollectionStrategy::Lazy - } - } + let collection_strategy = if tcx.sess.link_dead_code() { + MonoItemCollectionStrategy::Eager + } else { + MonoItemCollectionStrategy::Lazy }; let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_strategy); @@ -1205,7 +1191,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio } } - if tcx.sess.opts.unstable_opts.print_mono_items.is_some() { + if tcx.sess.opts.unstable_opts.print_mono_items { let mut item_to_cgus: UnordMap<_, Vec<_>> = Default::default(); for cgu in codegen_units { diff --git a/compiler/rustc_monomorphize/src/partitioning/autodiff.rs b/compiler/rustc_monomorphize/src/partitioning/autodiff.rs index 0c855508434e0..22d593b80b895 100644 --- a/compiler/rustc_monomorphize/src/partitioning/autodiff.rs +++ b/compiler/rustc_monomorphize/src/partitioning/autodiff.rs @@ -2,7 +2,7 @@ use rustc_ast::expand::autodiff_attrs::{AutoDiffItem, DiffActivity}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; use rustc_middle::mir::mono::MonoItem; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_middle::ty::{self, Instance, PseudoCanonicalInput, Ty, TyCtxt, TypingEnv}; use rustc_symbol_mangling::symbol_name_for_instance_in_crate; use tracing::{debug, trace}; @@ -12,39 +12,61 @@ fn adjust_activity_to_abi<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>, da: &mut Vec if !matches!(fn_ty.kind(), ty::FnDef(..)) { bug!("expected fn def for autodiff, got {:?}", fn_ty); } - let fnc_binder: ty::Binder<'_, ty::FnSig<'_>> = fn_ty.fn_sig(tcx); - // If rustc compiles the unmodified primal, we know that this copy of the function - // also has correct lifetimes. We know that Enzyme won't free the shadow too early - // (or actually at all), so let's strip lifetimes when computing the layout. - let x = tcx.instantiate_bound_regions_with_erased(fnc_binder); + // We don't actually pass the types back into the type system. + // All we do is decide how to handle the arguments. + let sig = fn_ty.fn_sig(tcx).skip_binder(); + let mut new_activities = vec![]; let mut new_positions = vec![]; - for (i, ty) in x.inputs().iter().enumerate() { + for (i, ty) in sig.inputs().iter().enumerate() { if let Some(inner_ty) = ty.builtin_deref(true) { - if ty.is_fn_ptr() { - // FIXME(ZuseZ4): add a nicer error, or just figure out how to support them, - // since Enzyme itself can handle them. - tcx.dcx().err("function pointers are currently not supported in autodiff"); - } if inner_ty.is_slice() { + // Now we need to figure out the size of each slice element in memory to allow + // safety checks and usability improvements in the backend. + let sty = match inner_ty.builtin_index() { + Some(sty) => sty, + None => { + panic!("slice element type unknown"); + } + }; + let pci = PseudoCanonicalInput { + typing_env: TypingEnv::fully_monomorphized(), + value: sty, + }; + + let layout = tcx.layout_of(pci); + let elem_size = match layout { + Ok(layout) => layout.size, + Err(_) => { + bug!("autodiff failed to compute slice element size"); + } + }; + let elem_size: u32 = elem_size.bytes() as u32; + // We know that the length will be passed as extra arg. if !da.is_empty() { // We are looking at a slice. The length of that slice will become an // extra integer on llvm level. Integers are always const. // However, if the slice get's duplicated, we want to know to later check the // size. So we mark the new size argument as FakeActivitySize. + // There is one FakeActivitySize per slice, so for convenience we store the + // slice element size in bytes in it. We will use the size in the backend. let activity = match da[i] { DiffActivity::DualOnly | DiffActivity::Dual + | DiffActivity::Dualv | DiffActivity::DuplicatedOnly - | DiffActivity::Duplicated => DiffActivity::FakeActivitySize, + | DiffActivity::Duplicated => { + DiffActivity::FakeActivitySize(Some(elem_size)) + } DiffActivity::Const => DiffActivity::Const, _ => bug!("unexpected activity for ptr/ref"), }; new_activities.push(activity); new_positions.push(i + 1); } + continue; } } diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index 63aa60f2f26b9..36d53901d9e82 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -9,7 +9,6 @@ derive-where = "1.2.7" rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } -rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_type_ir = { path = "../rustc_type_ir", default-features = false } rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } tracing = "0.1" @@ -20,7 +19,6 @@ default = ["nightly"] nightly = [ "dep:rustc_data_structures", "dep:rustc_macros", - "dep:rustc_serialize", "rustc_index/nightly", "rustc_type_ir/nightly", ] diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 9cae7f27947b4..bbb4a162027dc 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -1,13 +1,11 @@ use std::cmp::Ordering; use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack}; -use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::solve::{Goal, QueryInput}; -use rustc_type_ir::visit::TypeVisitableExt; use rustc_type_ir::{ self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, InferCtxtLike, - Interner, + Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use crate::delegate::SolverDelegate; diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs index 408742747c2a1..f8215a228f5b6 100644 --- a/compiler/rustc_next_trait_solver/src/coherence.rs +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -3,8 +3,10 @@ use std::ops::ControlFlow; use derive_where::derive_where; use rustc_type_ir::inherent::*; -use rustc_type_ir::visit::{TypeVisitable, TypeVisitableExt, TypeVisitor}; -use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; +use rustc_type_ir::{ + self as ty, InferCtxtLike, Interner, TrivialTypeTraversalImpls, TypeVisitable, + TypeVisitableExt, TypeVisitor, +}; use tracing::instrument; /// Whether we do the orphan check relative to this crate or to some remote crate. @@ -94,6 +96,8 @@ pub fn trait_ref_is_local_or_fundamental(tcx: I, trait_ref: ty::Tra trait_ref.def_id.is_local() || tcx.trait_is_fundamental(trait_ref.def_id) } +TrivialTypeTraversalImpls! { IsFirstInputType, } + #[derive(Debug, Copy, Clone)] pub enum IsFirstInputType { No, diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 850d86d91e88f..90a7c2e9f7879 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -1,8 +1,7 @@ use std::ops::Deref; -use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::solve::{Certainty, Goal, NoSolution}; -use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; +use rustc_type_ir::{self as ty, InferCtxtLike, Interner, TypeFoldable}; pub trait SolverDelegate: Deref + Sized { type Infcx: InferCtxtLike; @@ -37,13 +36,9 @@ pub trait SolverDelegate: Deref + Sized { fn well_formed_goals( &self, param_env: ::ParamEnv, - arg: ::GenericArg, + term: ::Term, ) -> Option::Predicate>>>; - fn clone_opaque_types_for_query_response( - &self, - ) -> Vec<(ty::OpaqueTypeKey, ::Ty)>; - fn make_deduplicated_outlives_constraints( &self, ) -> Vec::GenericArg>>; @@ -62,16 +57,6 @@ pub trait SolverDelegate: Deref + Sized { span: ::Span, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> ::GenericArg; - - // FIXME: Can we implement this in terms of `add` and `inject`? - fn insert_hidden_type( - &self, - opaque_type_key: ty::OpaqueTypeKey, - param_env: ::ParamEnv, - hidden_ty: ::Ty, - goals: &mut Vec::Predicate>>, - ) -> Result<(), NoSolution>; - fn add_item_bounds_for_hidden_type( &self, def_id: ::DefId, @@ -81,15 +66,6 @@ pub trait SolverDelegate: Deref + Sized { goals: &mut Vec::Predicate>>, ); - fn inject_new_hidden_type_unchecked( - &self, - key: ty::OpaqueTypeKey, - hidden_ty: ::Ty, - span: ::Span, - ); - - fn reset_opaque_types(&self); - fn fetch_eligible_assoc_item( &self, goal_trait_ref: ty::TraitRef, diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index d67ae2550d967..92cdc28a37b4b 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -6,7 +6,7 @@ // tidy-alphabetical-start #![allow(rustc::usage_of_type_ir_inherent)] -#![warn(unreachable_pub)] +#![allow(rustc::usage_of_type_ir_traits)] // tidy-alphabetical-end pub mod canonicalizer; diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index 71c87714745ee..992c5ddf504ef 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -1,8 +1,9 @@ use rustc_type_ir::data_structures::DelayedMap; -use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; -use rustc_type_ir::visit::TypeVisitableExt; -use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; +use rustc_type_ir::{ + self as ty, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, +}; use crate::delegate::SolverDelegate; diff --git a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs index 0fc313e33b323..f7bd460094328 100644 --- a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs +++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs @@ -16,6 +16,7 @@ //! relate them structurally. use rustc_type_ir::inherent::*; +use rustc_type_ir::solve::GoalSource; use rustc_type_ir::{self as ty, Interner}; use tracing::{instrument, trace}; @@ -49,7 +50,10 @@ where // Structurally normalize the lhs. let lhs = if let Some(alias) = lhs.to_alias_term() { let term = self.next_term_infer_of_kind(lhs); - self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term })); + self.add_goal( + GoalSource::TypeRelating, + goal.with(cx, ty::NormalizesTo { alias, term }), + ); term } else { lhs @@ -58,7 +62,10 @@ where // Structurally normalize the rhs. let rhs = if let Some(alias) = rhs.to_alias_term() { let term = self.next_term_infer_of_kind(rhs); - self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term })); + self.add_goal( + GoalSource::TypeRelating, + goal.with(cx, ty::NormalizesTo { alias, term }), + ); term } else { rhs diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index bfb590e87679d..542e212e1bfb1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -2,20 +2,25 @@ pub(super) mod structural_traits; +use std::cell::Cell; +use std::ops::ControlFlow; + use derive_where::derive_where; -use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; -use rustc_type_ir::visit::TypeVisitableExt as _; -use rustc_type_ir::{self as ty, Interner, TypingMode, Upcast as _, elaborate}; +use rustc_type_ir::{ + self as ty, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _, + TypeVisitor, TypingMode, Upcast as _, elaborate, +}; use tracing::{debug, instrument}; use super::trait_goals::TraitGoalProvenVia; +use super::{has_only_region_constraints, inspect}; use crate::delegate::SolverDelegate; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource, - MaybeCause, NoSolution, QueryResult, + MaybeCause, NoSolution, ParamEnvSource, QueryResult, }; enum AliasBoundKind { @@ -48,18 +53,6 @@ where fn trait_def_id(self, cx: I) -> I::DefId; - /// Try equating an assumption predicate against a goal's predicate. If it - /// holds, then execute the `then` callback, which should do any additional - /// work, then produce a response (typically by executing - /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]). - fn probe_and_match_goal_against_assumption( - ecx: &mut EvalCtxt<'_, D>, - source: CandidateSource, - goal: Goal, - assumption: I::Clause, - then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, - ) -> Result, NoSolution>; - /// Consider a clause, which consists of a "assumption" and some "requirements", /// to satisfy a goal. If the requirements hold, then attempt to satisfy our /// goal by equating it with the assumption. @@ -92,16 +85,20 @@ where let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else { panic!("expected object type in `probe_and_consider_object_bound_candidate`"); }; - ecx.add_goals( - GoalSource::ImplWhereBound, - structural_traits::predicates_for_object_candidate( - ecx, - goal.param_env, - goal.predicate.trait_ref(cx), - bounds, - ), - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + match structural_traits::predicates_for_object_candidate( + ecx, + goal.param_env, + goal.predicate.trait_ref(cx), + bounds, + ) { + Ok(requirements) => { + ecx.add_goals(GoalSource::ImplWhereBound, requirements); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + Err(_) => { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + } }) } @@ -114,6 +111,66 @@ where alias_ty: ty::AliasTy, ) -> Vec>; + fn probe_and_consider_param_env_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + ) -> Result, NoSolution> { + Self::fast_reject_assumption(ecx, goal, assumption)?; + + // Dealing with `ParamEnv` candidates is a bit of a mess as we need to lazily + // check whether the candidate is global while considering normalization. + // + // We need to write into `source` inside of `match_assumption`, but need to access it + // in `probe` even if the candidate does not apply before we get there. We handle this + // by using a `Cell` here. We only ever write into it inside of `match_assumption`. + let source = Cell::new(CandidateSource::ParamEnv(ParamEnvSource::Global)); + ecx.probe(|result: &QueryResult| inspect::ProbeKind::TraitCandidate { + source: source.get(), + result: *result, + }) + .enter(|ecx| { + Self::match_assumption(ecx, goal, assumption, |ecx| { + source.set(ecx.characterize_param_env_assumption(goal.param_env, assumption)?); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + }) + .map(|result| Candidate { source: source.get(), result }) + } + + /// Try equating an assumption predicate against a goal's predicate. If it + /// holds, then execute the `then` callback, which should do any additional + /// work, then produce a response (typically by executing + /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]). + fn probe_and_match_goal_against_assumption( + ecx: &mut EvalCtxt<'_, D>, + source: CandidateSource, + goal: Goal, + assumption: I::Clause, + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> Result, NoSolution> { + Self::fast_reject_assumption(ecx, goal, assumption)?; + + ecx.probe_trait_candidate(source) + .enter(|ecx| Self::match_assumption(ecx, goal, assumption, then)) + } + + /// Try to reject the assumption based off of simple heuristics, such as [`ty::ClauseKind`] + /// and `DefId`. + fn fast_reject_assumption( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + ) -> Result<(), NoSolution>; + + /// Relate the goal and assumption. + fn match_assumption( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> QueryResult; + fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, @@ -251,11 +308,6 @@ where goal: Goal, ) -> Result, NoSolution>; - fn consider_builtin_async_destruct_candidate( - ecx: &mut EvalCtxt<'_, D>, - goal: Goal, - ) -> Result, NoSolution>; - fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, @@ -284,6 +336,21 @@ where ) -> Vec>; } +/// Allows callers of `assemble_and_evaluate_candidates` to choose whether to limit +/// candidate assembly to param-env and alias-bound candidates. +/// +/// On top of being a micro-optimization, as it avoids doing unnecessary work when +/// a param-env trait bound candidate shadows impls for normalization, this is also +/// required to prevent query cycles due to RPITIT inference. See the issue at: +/// . +pub(super) enum AssembleCandidatesFrom { + All, + /// Only assemble candidates from the environment and alias bounds, ignoring + /// user-written and built-in impls. We only expect `ParamEnv` and `AliasBound` + /// candidates to be assembled. + EnvAndBounds, +} + impl EvalCtxt<'_, D> where D: SolverDelegate, @@ -292,6 +359,7 @@ where pub(super) fn assemble_and_evaluate_candidates>( &mut self, goal: Goal, + assemble_from: AssembleCandidatesFrom, ) -> Vec> { let Ok(normalized_self_ty) = self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty()) @@ -318,16 +386,18 @@ where } } - self.assemble_impl_candidates(goal, &mut candidates); - - self.assemble_builtin_impl_candidates(goal, &mut candidates); - self.assemble_alias_bound_candidates(goal, &mut candidates); - - self.assemble_object_bound_candidates(goal, &mut candidates); - self.assemble_param_env_candidates(goal, &mut candidates); + match assemble_from { + AssembleCandidatesFrom::All => { + self.assemble_impl_candidates(goal, &mut candidates); + self.assemble_builtin_impl_candidates(goal, &mut candidates); + self.assemble_object_bound_candidates(goal, &mut candidates); + } + AssembleCandidatesFrom::EnvAndBounds => {} + } + candidates } @@ -454,9 +524,6 @@ where Some(TraitSolverLangItem::DiscriminantKind) => { G::consider_builtin_discriminant_kind_candidate(self, goal) } - Some(TraitSolverLangItem::AsyncDestruct) => { - G::consider_builtin_async_destruct_candidate(self, goal) - } Some(TraitSolverLangItem::Destruct) => { G::consider_builtin_destruct_candidate(self, goal) } @@ -485,14 +552,8 @@ where goal: Goal, candidates: &mut Vec>, ) { - for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() { - candidates.extend(G::probe_and_consider_implied_clause( - self, - CandidateSource::ParamEnv(i), - goal, - assumption, - [], - )); + for assumption in goal.param_env.caller_bounds().iter() { + candidates.extend(G::probe_and_consider_param_env_candidate(self, goal, assumption)); } } @@ -573,7 +634,7 @@ where } ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty), - ty::Alias(ty::Inherent | ty::Weak, _) => { + ty::Alias(ty::Inherent | ty::Free, _) => { self.cx().delay_bug(format!("could not normalize {self_ty:?}, it is not WF")); return; } @@ -749,7 +810,73 @@ where } }) } +} + +pub(super) enum AllowInferenceConstraints { + Yes, + No, +} + +impl EvalCtxt<'_, D> +where + D: SolverDelegate, + I: Interner, +{ + /// Check whether we can ignore impl candidates due to specialization. + /// + /// This is only necessary for `feature(specialization)` and seems quite ugly. + pub(super) fn filter_specialized_impls( + &mut self, + allow_inference_constraints: AllowInferenceConstraints, + candidates: &mut Vec>, + ) { + match self.typing_mode() { + TypingMode::Coherence => return, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => {} + } + + let mut i = 0; + 'outer: while i < candidates.len() { + let CandidateSource::Impl(victim_def_id) = candidates[i].source else { + i += 1; + continue; + }; + + for (j, c) in candidates.iter().enumerate() { + if i == j { + continue; + } + + let CandidateSource::Impl(other_def_id) = c.source else { + continue; + }; + + // See if we can toss out `victim` based on specialization. + // + // While this requires us to know *for sure* that the `lhs` impl applies + // we still use modulo regions here. This is fine as specialization currently + // assumes that specializing impls have to be always applicable, meaning that + // the only allowed region constraints may be constraints also present on the default impl. + if matches!(allow_inference_constraints, AllowInferenceConstraints::Yes) + || has_only_region_constraints(c.result) + { + if self.cx().impl_specializes(other_def_id, victim_def_id) { + candidates.remove(i); + continue 'outer; + } + } + } + + i += 1; + } + } + /// Assemble and merge candidates for goals which are related to an underlying trait + /// goal. Right now, this is normalizes-to and host effect goals. + /// /// We sadly can't simply take all possible candidates for normalization goals /// and check whether they result in the same constraints. We want to make sure /// that trying to normalize an alias doesn't result in constraints which aren't @@ -778,54 +905,63 @@ where /// /// See trait-system-refactor-initiative#124 for more details. #[instrument(level = "debug", skip(self, inject_normalize_to_rigid_candidate), ret)] - pub(super) fn merge_candidates( + pub(super) fn assemble_and_merge_candidates>( &mut self, proven_via: Option, - candidates: Vec>, + goal: Goal, inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, ) -> QueryResult { let Some(proven_via) = proven_via else { // We don't care about overflow. If proving the trait goal overflowed, then // it's enough to report an overflow error for that, we don't also have to // overflow during normalization. - return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Ambiguity)); + // + // We use `forced_ambiguity` here over `make_ambiguous_response_no_constraints` + // because the former will also record a built-in candidate in the inspector. + return self.forced_ambiguity(MaybeCause::Ambiguity).map(|cand| cand.result); }; match proven_via { - // Even when a trait bound has been proven using a where-bound, we - // still need to consider alias-bounds for normalization, see - // tests/ui/next-solver/alias-bound-shadowed-by-env.rs. - // - // FIXME(const_trait_impl): should this behavior also be used by - // constness checking. Doing so is *at least theoretically* breaking, - // see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754 TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => { - let mut candidates_from_env_and_bounds: Vec<_> = candidates + // Even when a trait bound has been proven using a where-bound, we + // still need to consider alias-bounds for normalization, see + // `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`. + let candidates_from_env_and_bounds: Vec<_> = self + .assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds); + + // We still need to prefer where-bounds over alias-bounds however. + // See `tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs`. + let mut considered_candidates: Vec<_> = if candidates_from_env_and_bounds .iter() - .filter(|c| { - matches!( - c.source, - CandidateSource::AliasBound | CandidateSource::ParamEnv(_) - ) - }) - .map(|c| c.result) - .collect(); + .any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) + { + candidates_from_env_and_bounds + .into_iter() + .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_))) + .map(|c| c.result) + .collect() + } else { + candidates_from_env_and_bounds.into_iter().map(|c| c.result).collect() + }; // If the trait goal has been proven by using the environment, we want to treat // aliases as rigid if there are no applicable projection bounds in the environment. - if candidates_from_env_and_bounds.is_empty() { + if considered_candidates.is_empty() { if let Ok(response) = inject_normalize_to_rigid_candidate(self) { - candidates_from_env_and_bounds.push(response); + considered_candidates.push(response); } } - if let Some(response) = self.try_merge_responses(&candidates_from_env_and_bounds) { + if let Some(response) = self.try_merge_responses(&considered_candidates) { Ok(response) } else { - self.flounder(&candidates_from_env_and_bounds) + self.flounder(&considered_candidates) } } TraitGoalProvenVia::Misc => { + let mut candidates = + self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); + // Prefer "orphaned" param-env normalization predicates, which are used // (for example, and ideally only) when proving item bounds for an impl. let candidates_from_env: Vec<_> = candidates @@ -837,6 +973,13 @@ where return Ok(response); } + // We drop specialized impls to allow normalization via a final impl here. In case + // the specializing impl has different inference constraints from the specialized + // impl, proving the trait goal is already ambiguous, so we never get here. This + // means we can just ignore inference constraints and don't have to special-case + // constraining the normalized-to `term`. + self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates); + let responses: Vec<_> = candidates.iter().map(|c| c.result).collect(); if let Some(response) = self.try_merge_responses(&responses) { Ok(response) @@ -846,4 +989,88 @@ where } } } + + /// Compute whether a param-env assumption is global or non-global after normalizing it. + /// + /// This is necessary because, for example, given: + /// + /// ```ignore,rust + /// where + /// T: Trait, + /// i32: From, + /// ``` + /// + /// The `i32: From` bound is non-global before normalization, but is global after. + /// Since the old trait solver normalized param-envs eagerly, we want to emulate this + /// behavior lazily. + fn characterize_param_env_assumption( + &mut self, + param_env: I::ParamEnv, + assumption: I::Clause, + ) -> Result, NoSolution> { + // FIXME: This should be fixed, but it also requires changing the behavior + // in the old solver which is currently relied on. + if assumption.has_bound_vars() { + return Ok(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)); + } + + match assumption.visit_with(&mut FindParamInClause { ecx: self, param_env }) { + ControlFlow::Break(Err(NoSolution)) => Err(NoSolution), + ControlFlow::Break(Ok(())) => Ok(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)), + ControlFlow::Continue(()) => Ok(CandidateSource::ParamEnv(ParamEnvSource::Global)), + } + } +} + +struct FindParamInClause<'a, 'b, D: SolverDelegate, I: Interner> { + ecx: &'a mut EvalCtxt<'b, D>, + param_env: I::ParamEnv, +} + +impl TypeVisitor for FindParamInClause<'_, '_, D, I> +where + D: SolverDelegate, + I: Interner, +{ + type Result = ControlFlow>; + + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { + self.ecx.enter_forall(t.clone(), |ecx, v| { + v.visit_with(&mut FindParamInClause { ecx, param_env: self.param_env }) + }) + } + + fn visit_ty(&mut self, ty: I::Ty) -> Self::Result { + let Ok(ty) = self.ecx.structurally_normalize_ty(self.param_env, ty) else { + return ControlFlow::Break(Err(NoSolution)); + }; + + if let ty::Placeholder(_) = ty.kind() { + ControlFlow::Break(Ok(())) + } else { + ty.super_visit_with(self) + } + } + + fn visit_const(&mut self, ct: I::Const) -> Self::Result { + let Ok(ct) = self.ecx.structurally_normalize_const(self.param_env, ct) else { + return ControlFlow::Break(Err(NoSolution)); + }; + + if let ty::ConstKind::Placeholder(_) = ct.kind() { + ControlFlow::Break(Ok(())) + } else { + ct.super_visit_with(self) + } + } + + fn visit_region(&mut self, r: I::Region) -> Self::Result { + match self.ecx.eager_resolve_region(r).kind() { + ty::ReStatic | ty::ReError(_) => ControlFlow::Continue(()), + ty::ReVar(_) | ty::RePlaceholder(_) => ControlFlow::Break(Ok(())), + ty::ReErased | ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReBound(..) => { + unreachable!() + } + } + } } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 93804b14125dd..2a2b462a36cbd 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -3,10 +3,13 @@ use derive_where::derive_where; use rustc_type_ir::data_structures::HashMap; -use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; -use rustc_type_ir::{self as ty, Interner, Movability, Mutability, Upcast as _, elaborate}; +use rustc_type_ir::solve::inspect::ProbeKind; +use rustc_type_ir::{ + self as ty, FallibleTypeFolder, Interner, Movability, Mutability, TypeFoldable, + TypeSuperFoldable, Upcast as _, elaborate, +}; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::instrument; @@ -35,13 +38,17 @@ where | ty::Never | ty::Char => Ok(ty::Binder::dummy(vec![])), + // This branch is only for `experimental_default_bounds`. + // Other foreign types were rejected earlier in + // `disqualify_auto_trait_candidate_due_to_possible_impl`. + ty::Foreign(..) => Ok(ty::Binder::dummy(vec![])), + // Treat `str` like it's defined as `struct str([u8]);` ty::Str => Ok(ty::Binder::dummy(vec![Ty::new_slice(cx, Ty::new_u8(cx))])), ty::Dynamic(..) | ty::Param(..) - | ty::Foreign(..) - | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) + | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => { @@ -717,6 +724,9 @@ pub(in crate::solve) fn const_conditions_for_destruct( let destruct_def_id = cx.require_lang_item(TraitSolverLangItem::Destruct); match self_ty.kind() { + // `ManuallyDrop` is trivially `~const Destruct` as we do not run any drop glue on it. + ty::Adt(adt_def, _) if adt_def.is_manually_drop() => Ok(vec![]), + // An ADT is `~const Destruct` only if all of the fields are, // *and* if there is a `Drop` impl, that `Drop` impl is also `~const`. ty::Adt(adt_def, args) => { @@ -816,22 +826,16 @@ pub(in crate::solve) fn const_conditions_for_destruct( /// impl Baz for dyn Foo {} /// ``` /// -/// However, in order to make such impls well-formed, we need to do an +/// However, in order to make such impls non-cyclical, we need to do an /// additional step of eagerly folding the associated types in the where /// clauses of the impl. In this example, that means replacing /// `::Bar` with `Ty` in the first impl. -/// -// FIXME: This is only necessary as `::Assoc: ItemBound` -// bounds in impls are trivially proven using the item bound candidates. -// This is unsound in general and once that is fixed, we don't need to -// normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9 -// for more details. pub(in crate::solve) fn predicates_for_object_candidate( - ecx: &EvalCtxt<'_, D>, + ecx: &mut EvalCtxt<'_, D>, param_env: I::ParamEnv, trait_ref: ty::TraitRef, object_bounds: I::BoundExistentialPredicates, -) -> Vec> +) -> Result>, Ambiguous> where D: SolverDelegate, I: Interner, @@ -865,72 +869,130 @@ where .extend(cx.item_bounds(associated_type_def_id).iter_instantiated(cx, trait_ref.args)); } - let mut replace_projection_with = HashMap::default(); + let mut replace_projection_with: HashMap<_, Vec<_>> = HashMap::default(); for bound in object_bounds.iter() { if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() { + // FIXME: We *probably* should replace this with a dummy placeholder, + // b/c don't want to replace literal instances of this dyn type that + // show up in the bounds, but just ones that come from substituting + // `Self` with the dyn type. let proj = proj.with_self_ty(cx, trait_ref.self_ty()); - let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj)); - assert_eq!( - old_ty, - None, - "{:?} has two generic parameters: {:?} and {:?}", - proj.projection_term, - proj.term, - old_ty.unwrap() - ); + replace_projection_with.entry(proj.def_id()).or_default().push(bound.rebind(proj)); } } - let mut folder = - ReplaceProjectionWith { ecx, param_env, mapping: replace_projection_with, nested: vec![] }; - let folded_requirements = requirements.fold_with(&mut folder); + let mut folder = ReplaceProjectionWith { + ecx, + param_env, + self_ty: trait_ref.self_ty(), + mapping: &replace_projection_with, + nested: vec![], + }; - folder + let requirements = requirements.try_fold_with(&mut folder)?; + Ok(folder .nested .into_iter() - .chain(folded_requirements.into_iter().map(|clause| Goal::new(cx, param_env, clause))) - .collect() + .chain(requirements.into_iter().map(|clause| Goal::new(cx, param_env, clause))) + .collect()) } -struct ReplaceProjectionWith<'a, D: SolverDelegate, I: Interner> { - ecx: &'a EvalCtxt<'a, D>, +struct ReplaceProjectionWith<'a, 'b, I: Interner, D: SolverDelegate> { + ecx: &'a mut EvalCtxt<'b, D>, param_env: I::ParamEnv, - mapping: HashMap>>, + self_ty: I::Ty, + mapping: &'a HashMap>>>, nested: Vec>, } -impl, I: Interner> TypeFolder - for ReplaceProjectionWith<'_, D, I> +impl ReplaceProjectionWith<'_, '_, I, D> +where + D: SolverDelegate, + I: Interner, +{ + fn projection_may_match( + &mut self, + source_projection: ty::Binder>, + target_projection: ty::AliasTerm, + ) -> bool { + source_projection.item_def_id() == target_projection.def_id + && self + .ecx + .probe(|_| ProbeKind::ProjectionCompatibility) + .enter(|ecx| -> Result<_, NoSolution> { + let source_projection = ecx.instantiate_binder_with_infer(source_projection); + ecx.eq(self.param_env, source_projection.projection_term, target_projection)?; + ecx.try_evaluate_added_goals() + }) + .is_ok() + } + + /// Try to replace an alias with the term present in the projection bounds of the self type. + /// Returns `Ok` if this alias is not eligible to be replaced, or bail with + /// `Err(Ambiguous)` if it's uncertain which projection bound to replace the term with due + /// to multiple bounds applying. + fn try_eagerly_replace_alias( + &mut self, + alias_term: ty::AliasTerm, + ) -> Result, Ambiguous> { + if alias_term.self_ty() != self.self_ty { + return Ok(None); + } + + let Some(replacements) = self.mapping.get(&alias_term.def_id) else { + return Ok(None); + }; + + // This is quite similar to the `projection_may_match` we use in unsizing, + // but here we want to unify a projection predicate against an alias term + // so we can replace it with the projection predicate's term. + let mut matching_projections = replacements + .iter() + .filter(|source_projection| self.projection_may_match(**source_projection, alias_term)); + let Some(replacement) = matching_projections.next() else { + // This shouldn't happen. + panic!("could not replace {alias_term:?} with term from from {:?}", self.self_ty); + }; + // FIXME: This *may* have issues with duplicated projections. + if matching_projections.next().is_some() { + // If there's more than one projection that we can unify here, then we + // need to stall until inference constrains things so that there's only + // one choice. + return Err(Ambiguous); + } + + let replacement = self.ecx.instantiate_binder_with_infer(*replacement); + self.nested.extend( + self.ecx + .eq_and_get_goals(self.param_env, alias_term, replacement.projection_term) + .expect("expected to be able to unify goal projection with dyn's projection"), + ); + + Ok(Some(replacement.term)) + } +} + +/// Marker for bailing with ambiguity. +pub(crate) struct Ambiguous; + +impl FallibleTypeFolder for ReplaceProjectionWith<'_, '_, I, D> +where + D: SolverDelegate, + I: Interner, { + type Error = Ambiguous; + fn cx(&self) -> I { self.ecx.cx() } - fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + fn try_fold_ty(&mut self, ty: I::Ty) -> Result { if let ty::Alias(ty::Projection, alias_ty) = ty.kind() { - if let Some(replacement) = self.mapping.get(&alias_ty.def_id) { - // We may have a case where our object type's projection bound is higher-ranked, - // but the where clauses we instantiated are not. We can solve this by instantiating - // the binder at the usage site. - let proj = self.ecx.instantiate_binder_with_infer(*replacement); - // FIXME: Technically this equate could be fallible... - self.nested.extend( - self.ecx - .eq_and_get_goals( - self.param_env, - alias_ty, - proj.projection_term.expect_ty(self.ecx.cx()), - ) - .expect( - "expected to be able to unify goal projection with dyn's projection", - ), - ); - proj.term.expect_ty() - } else { - ty.super_fold_with(self) + if let Some(term) = self.try_eagerly_replace_alias(alias_ty.into())? { + return Ok(term.expect_ty()); } - } else { - ty.super_fold_with(self) } + + ty.try_super_fold_with(self) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 0b61c368d8e8d..8413c2abbb969 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -36,39 +36,39 @@ where self.def_id() } - fn probe_and_match_goal_against_assumption( + fn fast_reject_assumption( ecx: &mut EvalCtxt<'_, D>, - source: rustc_type_ir::solve::CandidateSource, goal: Goal, - assumption: ::Clause, - then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, - ) -> Result, NoSolution> { + assumption: I::Clause, + ) -> Result<(), NoSolution> { if let Some(host_clause) = assumption.as_host_effect_clause() { if host_clause.def_id() == goal.predicate.def_id() && host_clause.constness().satisfies(goal.predicate.constness) { - if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( goal.predicate.trait_ref.args, host_clause.skip_binder().trait_ref.args, ) { - return Err(NoSolution); + return Ok(()); } - - ecx.probe_trait_candidate(source).enter(|ecx| { - let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause); - ecx.eq( - goal.param_env, - goal.predicate.trait_ref, - assumption_trait_pred.trait_ref, - )?; - then(ecx) - }) - } else { - Err(NoSolution) } - } else { - Err(NoSolution) } + + Err(NoSolution) + } + + fn match_assumption( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> QueryResult { + let host_clause = assumption.as_host_effect_clause().unwrap(); + + let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause); + ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?; + + then(ecx) } /// Register additional assumptions for aliases corresponding to `~const` item bounds. @@ -124,7 +124,7 @@ where fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - impl_def_id: ::DefId, + impl_def_id: I::DefId, ) -> Result, NoSolution> { let cx = ecx.cx(); @@ -178,7 +178,7 @@ where fn consider_error_guaranteed_candidate( ecx: &mut EvalCtxt<'_, D>, - _guar: ::ErrorGuaranteed, + _guar: I::ErrorGuaranteed, ) -> Result, NoSolution> { ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) @@ -336,13 +336,6 @@ where unreachable!("DiscriminantKind is not const") } - fn consider_builtin_async_destruct_candidate( - _ecx: &mut EvalCtxt<'_, D>, - _goal: Goal, - ) -> Result, NoSolution> { - unreachable!("AsyncDestruct is not const") - } - fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, @@ -399,12 +392,11 @@ where &mut self, goal: Goal>, ) -> QueryResult { - let candidates = self.assemble_and_evaluate_candidates(goal); let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { let trait_goal: Goal> = goal.with(ecx.cx(), goal.predicate.trait_ref); ecx.compute_trait_goal(trait_goal) })?; - self.merge_candidates(proven_via, candidates, |_ecx| Err(NoSolution)) + self.assemble_and_merge_candidates(proven_via, goal, |_ecx| Err(NoSolution)) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index ce53a3968c7a4..36f68808a2c8c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -12,16 +12,17 @@ use std::iter; use rustc_index::IndexVec; -use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::solver_relating::RelateExt; -use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner}; +use rustc_type_ir::{ + self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, +}; use tracing::{debug, instrument, trace}; use crate::canonicalizer::Canonicalizer; use crate::delegate::SolverDelegate; use crate::resolve::EagerResolver; -use crate::solve::eval_ctxt::{CurrentGoalKind, NestedGoals}; +use crate::solve::eval_ctxt::CurrentGoalKind; use crate::solve::{ CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal, MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput, @@ -55,7 +56,10 @@ where &self, goal: Goal, ) -> (Vec, CanonicalInput) { - let opaque_types = self.delegate.clone_opaque_types_for_query_response(); + // We only care about one entry per `OpaqueTypeKey` here, + // so we only canonicalize the lookup table and ignore + // duplicate entries. + let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = (goal, opaque_types).fold_with(&mut EagerResolver::new(self.delegate)); @@ -80,12 +84,19 @@ where /// the values inferred while solving the instantiated goal. /// - `external_constraints`: additional constraints which aren't expressible /// using simple unification of inference variables. + /// + /// This takes the `shallow_certainty` which represents whether we're confident + /// that the final result of the current goal only depends on the nested goals. + /// + /// In case this is `Certainy::Maybe`, there may still be additional nested goals + /// or inference constraints required for this candidate to be hold. The candidate + /// always requires all already added constraints and nested goals. #[instrument(level = "trace", skip(self), ret)] pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( &mut self, - certainty: Certainty, + shallow_certainty: Certainty, ) -> QueryResult { - self.inspect.make_canonical_response(certainty); + self.inspect.make_canonical_response(shallow_certainty); let goals_certainty = self.try_evaluate_added_goals()?; assert_eq!( @@ -102,32 +113,33 @@ where NoSolution })?; - // When normalizing, we've replaced the expected term with an unconstrained - // inference variable. This means that we dropped information which could - // have been important. We handle this by instead returning the nested goals - // to the caller, where they are then handled. - // - // As we return all ambiguous nested goals, we can ignore the certainty returned - // by `try_evaluate_added_goals()`. - let (certainty, normalization_nested_goals) = match self.current_goal_kind { - CurrentGoalKind::NormalizesTo => { - let NestedGoals { normalizes_to_goals, goals } = - std::mem::take(&mut self.nested_goals); - if cfg!(debug_assertions) { - assert!(normalizes_to_goals.is_empty()); + let (certainty, normalization_nested_goals) = + match (self.current_goal_kind, shallow_certainty) { + // When normalizing, we've replaced the expected term with an unconstrained + // inference variable. This means that we dropped information which could + // have been important. We handle this by instead returning the nested goals + // to the caller, where they are then handled. We only do so if we do not + // need to recompute the `NormalizesTo` goal afterwards to avoid repeatedly + // uplifting its nested goals. This is the case if the `shallow_certainty` is + // `Certainty::Yes`. + (CurrentGoalKind::NormalizesTo, Certainty::Yes) => { + let goals = std::mem::take(&mut self.nested_goals); + // As we return all ambiguous nested goals, we can ignore the certainty + // returned by `self.try_evaluate_added_goals()`. if goals.is_empty() { assert!(matches!(goals_certainty, Certainty::Yes)); } + (Certainty::Yes, NestedNormalizationGoals(goals)) } - (certainty, NestedNormalizationGoals(goals)) - } - CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { - let certainty = certainty.unify_with(goals_certainty); - (certainty, NestedNormalizationGoals::empty()) - } - }; + _ => { + let certainty = shallow_certainty.and(goals_certainty); + (certainty, NestedNormalizationGoals::empty()) + } + }; - if let Certainty::Maybe(cause @ MaybeCause::Overflow { .. }) = certainty { + if let Certainty::Maybe(cause @ MaybeCause::Overflow { keep_constraints: false, .. }) = + certainty + { // If we have overflow, it's probable that we're substituting a type // into itself infinitely and any partial substitutions in the query // response are probably not useful anyways, so just return an empty @@ -183,6 +195,7 @@ where debug!(?num_non_region_vars, "too many inference variables -> overflow"); return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { suggest_increasing_limit: true, + keep_constraints: false, })); } } @@ -234,19 +247,15 @@ where Default::default() }; - ExternalConstraintsData { - region_constraints, - opaque_types: self - .delegate - .clone_opaque_types_for_query_response() - .into_iter() - // Only return *newly defined* opaque types. - .filter(|(a, _)| { - self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a) - }) - .collect(), - normalization_nested_goals, - } + // We only return *newly defined* opaque types from canonical queries. + // + // Constraints for any existing opaque types are already tracked by changes + // to the `var_values`. + let opaque_types = self + .delegate + .clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries); + + ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } } /// After calling a canonical query, we apply the constraints returned @@ -354,7 +363,7 @@ where // exist at all (see the FIXME at the start of this method), we have to deal with // them for now. delegate.instantiate_canonical_var_with_infer(info, span, |idx| { - ty::UniverseIndex::from(prev_universe.index() + idx.index()) + prev_universe + idx.index() }) } else if info.is_existential() { // As an optimization we sometimes avoid creating a new inference variable here. @@ -424,7 +433,17 @@ where fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey, I::Ty)]) { for &(key, ty) in opaque_types { - self.delegate.inject_new_hidden_type_unchecked(key, ty, self.origin_span); + let prev = self.delegate.register_hidden_type_in_storage(key, ty, self.origin_span); + // We eagerly resolve inference variables when computing the query response. + // This can cause previously distinct opaque type keys to now be structurally equal. + // + // To handle this, we store any duplicate entries in a separate list to check them + // at the end of typeck/borrowck. We could alternatively eagerly equate the hidden + // types here. However, doing so is difficult as it may result in nested goals and + // any errors may make it harder to track the control flow for diagnostics. + if let Some(prev) = prev { + self.delegate.add_duplicate_opaque_type(key, prev, self.origin_span); + } } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index b0bbec489471d..fc5dad9a3edf5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1,28 +1,29 @@ +use std::mem; use std::ops::ControlFlow; -use derive_where::derive_where; #[cfg(feature = "nightly")] -use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_macros::HashStable_NoContext; use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack}; use rustc_type_ir::fast_reject::DeepRejectCtxt; -use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::search_graph::PathKind; -use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; -use rustc_type_ir::{self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypingMode}; -use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; -use tracing::{instrument, trace}; +use rustc_type_ir::{ + self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + TypingMode, +}; +use tracing::{debug, instrument, trace}; +use super::has_only_region_constraints; use crate::coherence; use crate::delegate::SolverDelegate; use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; use crate::solve::{ CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluationKind, GoalSource, - HasChanged, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput, - QueryResult, + HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, }; pub(super) mod canonical; @@ -97,8 +98,6 @@ where current_goal_kind: CurrentGoalKind, pub(super) var_values: CanonicalVarValues, - predefined_opaques_in_body: I::PredefinedOpaques, - /// The highest universe index nameable by the caller. /// /// When we enter a new binder inside of the query we create new universes @@ -109,10 +108,14 @@ where /// if we have a coinductive cycle and because that's the only way we can return /// new placeholders to the caller. pub(super) max_input_universe: ty::UniverseIndex, + /// The opaque types from the canonical input. We only need to return opaque types + /// which have been added to the storage while evaluating this goal. + pub(super) initial_opaque_types_storage_num_entries: + ::OpaqueTypeStorageEntries, pub(super) search_graph: &'a mut SearchGraph, - nested_goals: NestedGoals, + nested_goals: Vec<(GoalSource, Goal)>, pub(super) origin_span: I::Span, @@ -127,35 +130,6 @@ where pub(super) inspect: ProofTreeBuilder, } -#[derive_where(Clone, Debug, Default; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] -struct NestedGoals { - /// These normalizes-to goals are treated specially during the evaluation - /// loop. In each iteration we take the RHS of the projection, replace it with - /// a fresh inference variable, and only after evaluating that goal do we - /// equate the fresh inference variable with the actual RHS of the predicate. - /// - /// This is both to improve caching, and to avoid using the RHS of the - /// projection predicate to influence the normalizes-to candidate we select. - /// - /// Forgetting to replace the RHS with a fresh inference variable when we evaluate - /// this goal results in an ICE.. - pub normalizes_to_goals: Vec>>, - /// The rest of the goals which have not yet processed or remain ambiguous. - pub goals: Vec<(GoalSource, Goal)>, -} - -impl NestedGoals { - fn new() -> Self { - Self { normalizes_to_goals: Vec::new(), goals: Vec::new() } - } - - fn is_empty(&self) -> bool { - self.normalizes_to_goals.is_empty() && self.goals.is_empty() - } -} - #[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum GenerateProofTree { @@ -271,12 +245,44 @@ where /// and will need to clearly document it in the rustc-dev-guide before /// stabilization. pub(super) fn step_kind_for_source(&self, source: GoalSource) -> PathKind { - match (self.current_goal_kind, source) { - (_, GoalSource::NormalizeGoal(step_kind)) => step_kind, - (CurrentGoalKind::CoinductiveTrait, GoalSource::ImplWhereBound) => { - PathKind::Coinductive - } - _ => PathKind::Inductive, + match source { + // We treat these goals as unknown for now. It is likely that most miscellaneous + // nested goals will be converted to an inductive variant in the future. + // + // Having unknown cycles is always the safer option, as changing that to either + // succeed or hard error is backwards compatible. If we incorrectly treat a cycle + // as inductive even though it should not be, it may be unsound during coherence and + // fixing it may cause inference breakage or introduce ambiguity. + GoalSource::Misc => PathKind::Unknown, + GoalSource::NormalizeGoal(path_kind) => path_kind, + GoalSource::ImplWhereBound => match self.current_goal_kind { + // We currently only consider a cycle coinductive if it steps + // into a where-clause of a coinductive trait. + CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive, + // While normalizing via an impl does step into a where-clause of + // an impl, accessing the associated item immediately steps out of + // it again. This means cycles/recursive calls are not guarded + // by impls used for normalization. + // + // See tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs + // for how this can go wrong. + CurrentGoalKind::NormalizesTo => PathKind::Inductive, + // We probably want to make all traits coinductive in the future, + // so we treat cycles involving where-clauses of not-yet coinductive + // traits as ambiguous for now. + CurrentGoalKind::Misc => PathKind::Unknown, + }, + // Relating types is always unproductive. If we were to map proof trees to + // corecursive functions as explained in #136824, relating types never + // introduces a constructor which could cause the recursion to be guarded. + GoalSource::TypeRelating => PathKind::Inductive, + // Instantiating a higher ranked goal can never cause the recursion to be + // guarded and is therefore unproductive. + GoalSource::InstantiateHigherRanked => PathKind::Inductive, + // These goal sources are likely unproductive and can be changed to + // `PathKind::Inductive`. Keeping them as unknown until we're confident + // about this and have an example where it is necessary. + GoalSource::AliasBoundConstCondition | GoalSource::AliasWellFormed => PathKind::Unknown, } } @@ -295,15 +301,13 @@ where let mut ecx = EvalCtxt { delegate, search_graph: &mut search_graph, - nested_goals: NestedGoals::new(), + nested_goals: Default::default(), inspect: ProofTreeBuilder::new_maybe_root(generate_proof_tree), // Only relevant when canonicalizing the response, // which we don't do within this evaluation context. - predefined_opaques_in_body: delegate - .cx() - .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()), max_input_universe: ty::UniverseIndex::ROOT, + initial_opaque_types_storage_num_entries: Default::default(), variables: Default::default(), var_values: CanonicalVarValues::dummy(), current_goal_kind: CurrentGoalKind::Misc, @@ -337,31 +341,41 @@ where canonical_goal_evaluation: &mut ProofTreeBuilder, f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal) -> R, ) -> R { - let (ref delegate, input, var_values) = - SolverDelegate::build_with_canonical(cx, &canonical_input); + let (ref delegate, input, var_values) = D::build_with_canonical(cx, &canonical_input); + for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { + let prev = delegate.register_hidden_type_in_storage(key, ty, I::Span::dummy()); + // It may be possible that two entries in the opaque type storage end up + // with the same key after resolving contained inference variables. + // + // We could put them in the duplicate list but don't have to. The opaques we + // encounter here are already tracked in the caller, so there's no need to + // also store them here. We'd take them out when computing the query response + // and then discard them, as they're already present in the input. + // + // Ideally we'd drop duplicate opaque type definitions when computing + // the canonical input. This is more annoying to implement and may cause a + // perf regression, so we do it inside of the query for now. + if let Some(prev) = prev { + debug!(?key, ?ty, ?prev, "ignore duplicate in `opaque_types_storage`"); + } + } + + let initial_opaque_types_storage_num_entries = delegate.opaque_types_storage_num_entries(); let mut ecx = EvalCtxt { delegate, variables: canonical_input.canonical.variables, var_values, current_goal_kind: CurrentGoalKind::from_query_input(cx, input), - predefined_opaques_in_body: input.predefined_opaques_in_body, max_input_universe: canonical_input.canonical.max_universe, + initial_opaque_types_storage_num_entries, search_graph, - nested_goals: NestedGoals::new(), + nested_goals: Default::default(), origin_span: I::Span::dummy(), tainted: Ok(()), inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values), }; - for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { - ecx.delegate.inject_new_hidden_type_unchecked(key, ty, ecx.origin_span); - } - - if !ecx.nested_goals.is_empty() { - panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals); - } - let result = f(&mut ecx, input.goal); ecx.inspect.probe_final_state(ecx.delegate, ecx.max_input_universe); canonical_goal_evaluation.goal_evaluation_step(ecx.inspect); @@ -471,13 +485,8 @@ where Ok(response) => response, }; - let has_changed = if !response.value.var_values.is_identity_modulo_regions() - || !response.value.external_constraints.opaque_types.is_empty() - { - HasChanged::Yes - } else { - HasChanged::No - }; + let has_changed = + if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; let (normalization_nested_goals, certainty) = self.instantiate_and_apply_query_response(goal.param_env, orig_values, response); @@ -528,8 +537,8 @@ where ty::PredicateKind::DynCompatible(trait_def_id) => { self.compute_dyn_compatible_goal(trait_def_id) } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - self.compute_well_formed_goal(Goal { param_env, predicate: arg }) + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + self.compute_well_formed_goal(Goal { param_env, predicate: term }) } ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct }) @@ -591,78 +600,83 @@ where /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`. fn evaluate_added_goals_step(&mut self) -> Result, NoSolution> { let cx = self.cx(); - let mut goals = core::mem::take(&mut self.nested_goals); - // If this loop did not result in any progress, what's our final certainty. let mut unchanged_certainty = Some(Certainty::Yes); - for goal in goals.normalizes_to_goals { - // Replace the goal with an unconstrained infer var, so the - // RHS does not affect projection candidate assembly. - let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term); - let unconstrained_goal = goal.with( - cx, - ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs }, - ); - - let (NestedNormalizationGoals(nested_goals), _, certainty) = self.evaluate_goal_raw( - GoalEvaluationKind::Nested, - GoalSource::Misc, - unconstrained_goal, - )?; - // Add the nested goals from normalization to our own nested goals. - trace!(?nested_goals); - goals.goals.extend(nested_goals); - - // Finally, equate the goal's RHS with the unconstrained var. - // - // SUBTLE: - // We structurally relate aliases here. This is necessary - // as we otherwise emit a nested `AliasRelate` goal in case the - // returned term is a rigid alias, resulting in overflow. + for (source, goal) in mem::take(&mut self.nested_goals) { + // We treat normalizes-to goals specially here. In each iteration we take the + // RHS of the projection, replace it with a fresh inference variable, and only + // after evaluating that goal do we equate the fresh inference variable with the + // actual RHS of the predicate. // - // It is correct as both `goal.predicate.term` and `unconstrained_rhs` - // start out as an unconstrained inference variable so any aliases get - // fully normalized when instantiating it. + // This is both to improve caching, and to avoid using the RHS of the + // projection predicate to influence the normalizes-to candidate we select. // - // FIXME: Strictly speaking this may be incomplete if the normalized-to - // type contains an ambiguous alias referencing bound regions. We should - // consider changing this to only use "shallow structural equality". - self.eq_structurally_relating_aliases( - goal.param_env, - goal.predicate.term, - unconstrained_rhs, - )?; - - // We only look at the `projection_ty` part here rather than - // looking at the "has changed" return from evaluate_goal, - // because we expect the `unconstrained_rhs` part of the predicate - // to have changed -- that means we actually normalized successfully! - let with_resolved_vars = self.resolve_vars_if_possible(goal); - if goal.predicate.alias != with_resolved_vars.predicate.alias { - unchanged_certainty = None; - } - - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - self.nested_goals.normalizes_to_goals.push(with_resolved_vars); - unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + // Forgetting to replace the RHS with a fresh inference variable when we evaluate + // this goal results in an ICE. + if let Some(pred) = goal.predicate.as_normalizes_to() { + // We should never encounter higher-ranked normalizes-to goals. + let pred = pred.no_bound_vars().unwrap(); + // Replace the goal with an unconstrained infer var, so the + // RHS does not affect projection candidate assembly. + let unconstrained_rhs = self.next_term_infer_of_kind(pred.term); + let unconstrained_goal = + goal.with(cx, ty::NormalizesTo { alias: pred.alias, term: unconstrained_rhs }); + + let (NestedNormalizationGoals(nested_goals), _, certainty) = + self.evaluate_goal_raw(GoalEvaluationKind::Nested, source, unconstrained_goal)?; + // Add the nested goals from normalization to our own nested goals. + trace!(?nested_goals); + self.nested_goals.extend(nested_goals); + + // Finally, equate the goal's RHS with the unconstrained var. + // + // SUBTLE: + // We structurally relate aliases here. This is necessary + // as we otherwise emit a nested `AliasRelate` goal in case the + // returned term is a rigid alias, resulting in overflow. + // + // It is correct as both `goal.predicate.term` and `unconstrained_rhs` + // start out as an unconstrained inference variable so any aliases get + // fully normalized when instantiating it. + // + // FIXME: Strictly speaking this may be incomplete if the normalized-to + // type contains an ambiguous alias referencing bound regions. We should + // consider changing this to only use "shallow structural equality". + self.eq_structurally_relating_aliases( + goal.param_env, + pred.term, + unconstrained_rhs, + )?; + + // We only look at the `projection_ty` part here rather than + // looking at the "has changed" return from evaluate_goal, + // because we expect the `unconstrained_rhs` part of the predicate + // to have changed -- that means we actually normalized successfully! + let with_resolved_vars = self.resolve_vars_if_possible(goal); + if pred.alias != goal.predicate.as_normalizes_to().unwrap().skip_binder().alias { + unchanged_certainty = None; } - } - } - for (source, goal) in goals.goals { - let (has_changed, certainty) = - self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?; - if has_changed == HasChanged::Yes { - unchanged_certainty = None; - } + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.nested_goals.push((source, with_resolved_vars)); + unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); + } + } + } else { + let (has_changed, certainty) = + self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?; + if has_changed == HasChanged::Yes { + unchanged_certainty = None; + } - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - self.nested_goals.goals.push((source, goal)); - unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.nested_goals.push((source, goal)); + unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); + } } } } @@ -679,23 +693,12 @@ where self.delegate.cx() } - #[instrument(level = "trace", skip(self))] - pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal>) { - goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new( - self, - GoalSource::Misc, - goal.param_env, - )); - self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal); - self.nested_goals.normalizes_to_goals.push(goal); - } - #[instrument(level = "debug", skip(self))] pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal) { goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, source, goal.param_env)); self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); - self.nested_goals.goals.push((source, goal)); + self.nested_goals.push((source, goal)); } #[instrument(level = "trace", skip(self, goals))] @@ -939,7 +942,17 @@ where rhs: T, ) -> Result<(), NoSolution> { let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?; - self.add_goals(GoalSource::Misc, goals); + for &goal in goals.iter() { + let source = match goal.predicate.kind().skip_binder() { + ty::PredicateKind::Subtype { .. } | ty::PredicateKind::AliasRelate(..) => { + GoalSource::TypeRelating + } + // FIXME(-Znext-solver=coinductive): should these WF goals also be unproductive? + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => GoalSource::Misc, + p => unreachable!("unexpected nested goal in `relate`: {p:?}"), + }; + self.add_goal(source, goal); + } Ok(()) } @@ -982,6 +995,14 @@ where self.delegate.resolve_vars_if_possible(value) } + pub(super) fn eager_resolve_region(&self, r: I::Region) -> I::Region { + if let ty::ReVar(vid) = r.kind() { + self.delegate.opportunistic_resolve_lt_var(vid) + } else { + r + } + } + pub(super) fn fresh_args_for_item(&mut self, def_id: I::DefId) -> I::GenericArgs { let args = self.delegate.fresh_args_for_item(def_id); for arg in args.iter() { @@ -1003,9 +1024,9 @@ where pub(super) fn well_formed_goals( &self, param_env: I::ParamEnv, - arg: I::GenericArg, + term: I::Term, ) -> Option>> { - self.delegate.well_formed_goals(param_env, arg) + self.delegate.well_formed_goals(param_env, term) } pub(super) fn trait_ref_is_knowable( @@ -1028,16 +1049,12 @@ where self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id) } - pub(super) fn insert_hidden_type( + pub(super) fn register_hidden_type_in_storage( &mut self, opaque_type_key: ty::OpaqueTypeKey, - param_env: I::ParamEnv, hidden_ty: I::Ty, - ) -> Result<(), NoSolution> { - let mut goals = Vec::new(); - self.delegate.insert_hidden_type(opaque_type_key, param_env, hidden_ty, &mut goals)?; - self.add_goals(GoalSource::Misc, goals); - Ok(()) + ) -> Option { + self.delegate.register_hidden_type_in_storage(opaque_type_key, hidden_ty, self.origin_span) } pub(super) fn add_item_bounds_for_hidden_type( @@ -1064,14 +1081,17 @@ where &mut self, key: ty::OpaqueTypeKey, ) -> Option<(ty::OpaqueTypeKey, I::Ty)> { - let mut matching = - self.delegate.clone_opaque_types_for_query_response().into_iter().filter( - |(candidate_key, _)| { - candidate_key.def_id == key.def_id - && DeepRejectCtxt::relate_rigid_rigid(self.cx()) - .args_may_unify(candidate_key.args, key.args) - }, - ); + // We shouldn't have any duplicate entries when using + // this function during `TypingMode::Analysis`. + let duplicate_entries = self.delegate.clone_duplicate_opaque_types(); + assert!(duplicate_entries.is_empty(), "unexpected duplicates: {duplicate_entries:?}"); + let mut matching = self.delegate.clone_opaque_types_lookup_table().into_iter().filter( + |(candidate_key, _)| { + candidate_key.def_id == key.def_id + && DeepRejectCtxt::relate_rigid_rigid(self.cx()) + .args_may_unify(candidate_key.args, key.args) + }, + ); let first = matching.next(); let second = matching.next(); assert_eq!(second, None); diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index 0a9e7fafaea62..ed0cedc407746 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -26,32 +26,33 @@ where I: Interner, { pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T) -> T { - let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self; + let ProbeCtxt { ecx: outer, probe_kind, _result } = self; - let delegate = outer_ecx.delegate; - let max_input_universe = outer_ecx.max_input_universe; - let mut nested_ecx = EvalCtxt { + let delegate = outer.delegate; + let max_input_universe = outer.max_input_universe; + let mut nested = EvalCtxt { delegate, - variables: outer_ecx.variables, - var_values: outer_ecx.var_values, - current_goal_kind: outer_ecx.current_goal_kind, - predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body, + variables: outer.variables, + var_values: outer.var_values, + current_goal_kind: outer.current_goal_kind, max_input_universe, - search_graph: outer_ecx.search_graph, - nested_goals: outer_ecx.nested_goals.clone(), - origin_span: outer_ecx.origin_span, - tainted: outer_ecx.tainted, - inspect: outer_ecx.inspect.take_and_enter_probe(), + initial_opaque_types_storage_num_entries: outer + .initial_opaque_types_storage_num_entries, + search_graph: outer.search_graph, + nested_goals: outer.nested_goals.clone(), + origin_span: outer.origin_span, + tainted: outer.tainted, + inspect: outer.inspect.take_and_enter_probe(), }; - let r = nested_ecx.delegate.probe(|| { - let r = f(&mut nested_ecx); - nested_ecx.inspect.probe_final_state(delegate, max_input_universe); + let r = nested.delegate.probe(|| { + let r = f(&mut nested); + nested.inspect.probe_final_state(delegate, max_input_universe); r }); - if !nested_ecx.inspect.is_noop() { + if !nested.inspect.is_noop() { let probe_kind = probe_kind(&r); - nested_ecx.inspect.probe_kind(probe_kind); - outer_ecx.inspect = nested_ecx.inspect.finish_probe(); + nested.inspect.probe_kind(probe_kind); + outer.inspect = nested.inspect.finish_probe(); } r } diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index 1607fbb1b6a7b..f22b275bc44a2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -412,20 +412,6 @@ impl, I: Interner> ProofTreeBuilder { } } - pub(crate) fn add_normalizes_to_goal( - &mut self, - delegate: &D, - max_input_universe: ty::UniverseIndex, - goal: Goal>, - ) { - self.add_goal( - delegate, - max_input_universe, - GoalSource::Misc, - goal.with(delegate.cx(), goal.predicate), - ); - } - pub(crate) fn add_goal( &mut self, delegate: &D, diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 1fa35b60304cf..8173146e2fe24 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -70,6 +70,17 @@ fn has_no_inference_or_external_constraints( && normalization_nested_goals.is_empty() } +fn has_only_region_constraints(response: ty::Canonical>) -> bool { + let ExternalConstraintsData { + region_constraints: _, + ref opaque_types, + ref normalization_nested_goals, + } = *response.value.external_constraints; + response.value.var_values.is_identity_modulo_regions() + && opaque_types.is_empty() + && normalization_nested_goals.is_empty() +} + impl<'a, D, I> EvalCtxt<'a, D> where D: SolverDelegate, @@ -126,7 +137,7 @@ where } #[instrument(level = "trace", skip(self))] - fn compute_well_formed_goal(&mut self, goal: Goal) -> QueryResult { + fn compute_well_formed_goal(&mut self, goal: Goal) -> QueryResult { match self.well_formed_goals(goal.param_env, goal.predicate) { Some(goals) => { self.add_goals(GoalSource::Misc, goals); @@ -242,16 +253,18 @@ where } fn bail_with_ambiguity(&mut self, responses: &[CanonicalResponse]) -> CanonicalResponse { - debug_assert!(!responses.is_empty()); - if let Certainty::Maybe(maybe_cause) = - responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| { - certainty.unify_with(response.value.certainty) - }) - { - self.make_ambiguous_response_no_constraints(maybe_cause) - } else { - panic!("expected flounder response to be ambiguous") - } + debug_assert!(responses.len() > 1); + let maybe_cause = responses.iter().fold(MaybeCause::Ambiguity, |maybe_cause, response| { + // Pull down the certainty of `Certainty::Yes` to ambiguity when combining + // these responses, b/c we're combining more than one response and this we + // don't know which one applies. + let candidate = match response.value.certainty { + Certainty::Yes => MaybeCause::Ambiguity, + Certainty::Maybe(candidate) => candidate, + }; + maybe_cause.or(candidate) + }); + self.make_ambiguous_response_no_constraints(maybe_cause) } /// If we fail to merge responses we flounder and return overflow or ambiguity. @@ -313,7 +326,9 @@ where ty::AliasRelationDirection::Equate, ), ); - self.add_goal(GoalSource::Misc, alias_relate_goal); + // We normalize the self type to be able to relate it with + // types from candidates. + self.add_goal(GoalSource::TypeRelating, alias_relate_goal); self.try_evaluate_added_goals()?; Ok(self.resolve_vars_if_possible(normalized_term)) } else { @@ -327,7 +342,8 @@ where TypingMode::Coherence | TypingMode::PostAnalysis => false, // During analysis, opaques are rigid unless they may be defined by // the current body. - TypingMode::Analysis { defining_opaque_types: non_rigid_opaques } + TypingMode::Analysis { defining_opaque_types_and_generators: non_rigid_opaques } + | TypingMode::Borrowck { defining_opaque_types: non_rigid_opaques } | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => { !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id)) } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs new file mode 100644 index 0000000000000..8aa6e4a3d7118 --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs @@ -0,0 +1,43 @@ +//! Computes a normalizes-to (projection) goal for inherent associated types, +//! `#![feature(lazy_type_alias)]` and `#![feature(type_alias_impl_trait)]`. +//! +//! Since a free alias is never ambiguous, this just computes the `type_of` of +//! the alias and registers the where-clauses of the type alias. + +use rustc_type_ir::{self as ty, Interner}; + +use crate::delegate::SolverDelegate; +use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult}; + +impl EvalCtxt<'_, D> +where + D: SolverDelegate, + I: Interner, +{ + pub(super) fn normalize_free_alias( + &mut self, + goal: Goal>, + ) -> QueryResult { + let cx = self.cx(); + let free_alias = goal.predicate.alias; + + // Check where clauses + self.add_goals( + GoalSource::Misc, + cx.predicates_of(free_alias.def_id) + .iter_instantiated(cx, free_alias.args) + .map(|pred| goal.with(cx, pred)), + ); + + let actual = if free_alias.kind(cx).is_type() { + cx.type_of(free_alias.def_id).instantiate(cx, free_alias.args) + } else { + // FIXME(mgca): once const items are actual aliases defined as equal to type system consts + // this should instead return that. + panic!("normalizing free const aliases in the type system is unsupported"); + }; + + self.instantiate_normalizes_to_term(goal, actual.into()); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } +} diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 1d1ff09ee4104..2640238f5a904 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -15,12 +15,12 @@ where D: SolverDelegate, I: Interner, { - pub(super) fn normalize_inherent_associated_type( + pub(super) fn normalize_inherent_associated_term( &mut self, goal: Goal>, ) -> QueryResult { let cx = self.cx(); - let inherent = goal.predicate.alias.expect_ty(cx); + let inherent = goal.predicate.alias; let impl_def_id = cx.parent(inherent.def_id); let impl_args = self.fresh_args_for_item(impl_def_id); @@ -48,8 +48,13 @@ where .map(|pred| goal.with(cx, pred)), ); - let normalized = cx.type_of(inherent.def_id).instantiate(cx, inherent_args); - self.instantiate_normalizes_to_term(goal, normalized.into()); + let normalized = if inherent.kind(cx).is_type() { + cx.type_of(inherent.def_id).instantiate(cx, inherent_args).into() + } else { + // FIXME(mgca): Properly handle IACs in the type system + panic!("normalizing inherent associated consts in the type system is unsupported"); + }; + self.instantiate_normalizes_to_term(goal, normalized); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index de6d21da0f592..2fddc0044cb9e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -1,12 +1,12 @@ mod anon_const; +mod free_alias; mod inherent; mod opaque_types; -mod weak_types; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; -use rustc_type_ir::{self as ty, Interner, NormalizesTo, Upcast as _}; +use rustc_type_ir::{self as ty, Interner, NormalizesTo, PredicateKind, Upcast as _}; use tracing::instrument; use crate::delegate::SolverDelegate; @@ -32,27 +32,29 @@ where let cx = self.cx(); match goal.predicate.alias.kind(cx) { ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { - let candidates = self.assemble_and_evaluate_candidates(goal); let trait_ref = goal.predicate.alias.trait_ref(cx); let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { let trait_goal: Goal> = goal.with(cx, trait_ref); ecx.compute_trait_goal(trait_goal) })?; - self.merge_candidates(proven_via, candidates, |ecx| { + self.assemble_and_merge_candidates(proven_via, goal, |ecx| { ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| { this.structurally_instantiate_normalizes_to_term( goal, goal.predicate.alias, ); - this.add_goal(GoalSource::AliasWellFormed, goal.with(cx, trait_ref)); this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) }) } - ty::AliasTermKind::InherentTy => self.normalize_inherent_associated_type(goal), + ty::AliasTermKind::InherentTy | ty::AliasTermKind::InherentConst => { + self.normalize_inherent_associated_term(goal) + } ty::AliasTermKind::OpaqueTy => self.normalize_opaque_type(goal), - ty::AliasTermKind::WeakTy => self.normalize_weak_type(goal), + ty::AliasTermKind::FreeTy | ty::AliasTermKind::FreeConst => { + self.normalize_free_alias(goal) + } ty::AliasTermKind::UnevaluatedConst => self.normalize_anon_const(goal), } } @@ -104,50 +106,80 @@ where self.trait_def_id(cx) } - fn probe_and_match_goal_against_assumption( + fn fast_reject_assumption( ecx: &mut EvalCtxt<'_, D>, - source: CandidateSource, goal: Goal, assumption: I::Clause, - then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, - ) -> Result, NoSolution> { + ) -> Result<(), NoSolution> { if let Some(projection_pred) = assumption.as_projection_clause() { if projection_pred.item_def_id() == goal.predicate.def_id() { - let cx = ecx.cx(); - if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( goal.predicate.alias.args, projection_pred.skip_binder().projection_term.args, ) { - return Err(NoSolution); + return Ok(()); } - ecx.probe_trait_candidate(source).enter(|ecx| { - let assumption_projection_pred = - ecx.instantiate_binder_with_infer(projection_pred); - ecx.eq( - goal.param_env, - goal.predicate.alias, - assumption_projection_pred.projection_term, - )?; - - ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term); - - // Add GAT where clauses from the trait's definition - // FIXME: We don't need these, since these are the type's own WF obligations. - ecx.add_goals( - GoalSource::Misc, - cx.own_predicates_of(goal.predicate.def_id()) - .iter_instantiated(cx, goal.predicate.alias.args) - .map(|pred| goal.with(cx, pred)), - ); - - then(ecx) - }) - } else { - Err(NoSolution) } - } else { - Err(NoSolution) } + + Err(NoSolution) + } + + fn match_assumption( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> QueryResult { + let cx = ecx.cx(); + // FIXME(generic_associated_types): Addresses aggressive inference in #92917. + // + // If this type is a GAT with currently unconstrained arguments, we do not + // want to normalize it via a candidate which only applies for a specific + // instantiation. We could otherwise keep the GAT as rigid and succeed this way. + // See tests/ui/generic-associated-types/no-incomplete-gat-arg-inference.rs. + // + // This only avoids normalization if the GAT arguments are fully unconstrained. + // This is quite arbitrary but fixing it causes some ambiguity, see #125196. + match goal.predicate.alias.kind(cx) { + ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { + for arg in goal.predicate.alias.own_args(cx).iter() { + let Some(term) = arg.as_term() else { + continue; + }; + let term = ecx.structurally_normalize_term(goal.param_env, term)?; + if term.is_infer() { + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + } + } + ty::AliasTermKind::OpaqueTy + | ty::AliasTermKind::InherentTy + | ty::AliasTermKind::InherentConst + | ty::AliasTermKind::FreeTy + | ty::AliasTermKind::FreeConst + | ty::AliasTermKind::UnevaluatedConst => {} + } + + let projection_pred = assumption.as_projection_clause().unwrap(); + + let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); + ecx.eq(goal.param_env, goal.predicate.alias, assumption_projection_pred.projection_term)?; + + ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term); + + // Add GAT where clauses from the trait's definition + // FIXME: We don't need these, since these are the type's own WF obligations. + ecx.add_goals( + GoalSource::AliasWellFormed, + cx.own_predicates_of(goal.predicate.def_id()) + .iter_instantiated(cx, goal.predicate.alias.args) + .map(|pred| goal.with(cx, pred)), + ); + + then(ecx) } fn consider_additional_alias_assumptions( @@ -196,10 +228,16 @@ where .map(|pred| goal.with(cx, pred)); ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + // Bail if the nested goals don't hold here. This is to avoid unnecessarily + // computing the `type_of` query for associated types that never apply, as + // this may result in query cycles in the case of RPITITs. + // See . + ecx.try_evaluate_added_goals()?; + // Add GAT where clauses from the trait's definition. // FIXME: We don't need these, since these are the type's own WF obligations. ecx.add_goals( - GoalSource::Misc, + GoalSource::AliasWellFormed, cx.own_predicates_of(goal.predicate.def_id()) .iter_instantiated(cx, goal.predicate.alias.args) .map(|pred| goal.with(cx, pred)), @@ -215,9 +253,6 @@ where ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }; - // In case the associated item is hidden due to specialization, we have to - // return ambiguity this would otherwise be incomplete, resulting in - // unsoundness during coherence (#105782). let target_item_def_id = match ecx.fetch_eligible_assoc_item( goal_trait_ref, goal.predicate.def_id(), @@ -225,14 +260,78 @@ where ) { Ok(Some(target_item_def_id)) => target_item_def_id, Ok(None) => { - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + match ecx.typing_mode() { + // In case the associated item is hidden due to specialization, + // normalizing this associated item is always ambiguous. Treating + // the associated item as rigid would be incomplete and allow for + // overlapping impls, see #105782. + // + // As this ambiguity is unavoidable we emit a nested ambiguous + // goal instead of using `Certainty::AMBIGUOUS`. This allows us to + // return the nested goals to the parent `AliasRelate` goal. This + // would be relevant if any of the nested goals refer to the `term`. + // This is not the case here and we only prefer adding an ambiguous + // nested goal for consistency. + ty::TypingMode::Coherence => { + ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + // Outside of coherence, we treat the associated item as rigid instead. + ty::TypingMode::Analysis { .. } + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } + | ty::TypingMode::PostAnalysis => { + ecx.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + }; } Err(guar) => return error_response(ecx, guar), }; if !cx.has_item_definition(target_item_def_id) { - return error_response(ecx, cx.delay_bug("missing item")); + // If the impl is missing an item, it's either because the user forgot to + // provide it, or the user is not *obligated* to provide it (because it + // has a trivially false `Sized` predicate). If it's the latter, we cannot + // delay a bug because we can have trivially false where clauses, so we + // treat it as rigid. + if cx.impl_self_is_guaranteed_unsized(impl_def_id) { + match ecx.typing_mode() { + // Trying to normalize such associated items is always ambiguous + // during coherence to avoid cyclic reasoning. See the example in + // tests/ui/traits/trivial-unsized-projection-in-coherence.rs. + // + // As this ambiguity is unavoidable we emit a nested ambiguous + // goal instead of using `Certainty::AMBIGUOUS`. This allows us to + // return the nested goals to the parent `AliasRelate` goal. This + // would be relevant if any of the nested goals refer to the `term`. + // This is not the case here and we only prefer adding an ambiguous + // nested goal for consistency. + ty::TypingMode::Coherence => { + ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + ty::TypingMode::Analysis { .. } + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } + | ty::TypingMode::PostAnalysis => { + ecx.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + } + } else { + return error_response(ecx, cx.delay_bug("missing item")); + } } let target_container_def_id = cx.parent(target_item_def_id); @@ -268,6 +367,8 @@ where cx.type_of(target_item_def_id).map_bound(|ty| ty.into()) } ty::AliasTermKind::ProjectionConst => { + // FIXME(mgca): once const items are actual aliases defined as equal to type system consts + // this should instead return that. if cx.features().associated_const_equality() { panic!("associated const projection is not supported yet") } else { @@ -846,66 +947,6 @@ where }) } - fn consider_builtin_async_destruct_candidate( - ecx: &mut EvalCtxt<'_, D>, - goal: Goal, - ) -> Result, NoSolution> { - let self_ty = goal.predicate.self_ty(); - let async_destructor_ty = match self_ty.kind() { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Array(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) - | ty::Never - | ty::Adt(_, _) - | ty::Str - | ty::Slice(_) - | ty::Tuple(_) - | ty::Error(_) => self_ty.async_destructor_ty(ecx.cx()), - - ty::UnsafeBinder(_) => { - // FIXME(unsafe_binders): Instantiate the binder with placeholders I guess. - todo!() - } - - // Given an alias, parameter, or placeholder we add an impl candidate normalizing to a rigid - // alias. In case there's a where-bound further constraining this alias it is preferred over - // this impl candidate anyways. It's still a bit scuffed. - ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { - return ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); - } - - ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) - | ty::Foreign(..) - | ty::Bound(..) => panic!( - "unexpected self ty `{:?}` when normalizing `::AsyncDestructor`", - goal.predicate.self_ty() - ), - - ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => panic!( - "`consider_builtin_async_destruct_candidate` is not yet implemented for type: {self_ty:?}" - ), - }; - - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.eq(goal.param_env, goal.predicate.term, async_destructor_ty.into()) - .expect("expected goal term to be fully unconstrained"); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } - fn consider_builtin_destruct_candidate( _ecx: &mut EvalCtxt<'_, D>, goal: Goal, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index 60c20762a30ab..df3ad1e468bb8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -2,9 +2,9 @@ //! behaves differently depending on the current `TypingMode`. use rustc_index::bit_set::GrowableBitSet; -use rustc_type_ir::fold::fold_regions; use rustc_type_ir::inherent::*; -use rustc_type_ir::{self as ty, Interner, TypingMode}; +use rustc_type_ir::solve::GoalSource; +use rustc_type_ir::{self as ty, Interner, TypingMode, fold_regions}; use crate::delegate::SolverDelegate; use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult, inspect}; @@ -32,13 +32,18 @@ where goal.param_env, expected, ); - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + // Trying to normalize an opaque type during coherence is always ambiguous. + // We add a nested ambiguous goal here instead of using `Certainty::AMBIGUOUS`. + // This allows us to return the nested goals to the parent `AliasRelate` goal. + // This can then allow nested goals to fail after we've constrained the `term`. + self.add_goal(GoalSource::Misc, goal.with(cx, ty::PredicateKind::Ambiguous)); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - TypingMode::Analysis { defining_opaque_types } => { + TypingMode::Analysis { defining_opaque_types_and_generators } => { let Some(def_id) = opaque_ty .def_id .as_local() - .filter(|&def_id| defining_opaque_types.contains(&def_id)) + .filter(|&def_id| defining_opaque_types_and_generators.contains(&def_id)) else { self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); @@ -87,8 +92,44 @@ where } // Otherwise, define a new opaque type - // FIXME: should we use `inject_hidden_type_unchecked` here? - self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?; + let prev = self.register_hidden_type_in_storage(opaque_type_key, expected); + assert_eq!(prev, None); + self.add_item_bounds_for_hidden_type( + def_id.into(), + opaque_ty.args, + goal.param_env, + expected, + ); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + // Very similar to `TypingMode::Analysis` with some notably differences: + // - we accept opaque types even if they have non-universal arguments + // - we do a structural lookup instead of semantically unifying regions + // - the hidden type starts out as the type from HIR typeck with fresh region + // variables instead of a fully unconstrained inference variable + TypingMode::Borrowck { defining_opaque_types } => { + let Some(def_id) = opaque_ty + .def_id + .as_local() + .filter(|&def_id| defining_opaque_types.contains(&def_id)) + else { + self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + }; + + let opaque_type_key = ty::OpaqueTypeKey { def_id, args: opaque_ty.args }; + let actual = self + .register_hidden_type_in_storage(opaque_type_key, expected) + .unwrap_or_else(|| { + let actual = + cx.type_of_opaque_hir_typeck(def_id).instantiate(cx, opaque_ty.args); + let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() { + ty::ReErased => self.next_region_var(), + _ => re, + }); + actual + }); + self.eq(goal.param_env, expected, actual)?; self.add_item_bounds_for_hidden_type( def_id.into(), opaque_ty.args, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs deleted file mode 100644 index 14e68dd52b6c1..0000000000000 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! Computes a normalizes-to (projection) goal for inherent associated types, -//! `#![feature(lazy_type_alias)]` and `#![feature(type_alias_impl_trait)]`. -//! -//! Since a weak alias is never ambiguous, this just computes the `type_of` of -//! the alias and registers the where-clauses of the type alias. - -use rustc_type_ir::{self as ty, Interner}; - -use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult}; - -impl EvalCtxt<'_, D> -where - D: SolverDelegate, - I: Interner, -{ - pub(super) fn normalize_weak_type( - &mut self, - goal: Goal>, - ) -> QueryResult { - let cx = self.cx(); - let weak_ty = goal.predicate.alias; - - // Check where clauses - self.add_goals( - GoalSource::Misc, - cx.predicates_of(weak_ty.def_id) - .iter_instantiated(cx, weak_ty.args) - .map(|pred| goal.with(cx, pred)), - ); - - let actual = cx.type_of(weak_ty.def_id).instantiate(cx, weak_ty.args); - self.instantiate_normalizes_to_term(goal, actual.into()); - - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } -} diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs index d945288007174..944d5f0e042d7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs @@ -24,7 +24,8 @@ where ty::AliasRelationDirection::Equate, ), ); - self.add_goal(GoalSource::Misc, goal); + // A projection goal holds if the alias is equal to the expected term. + self.add_goal(GoalSource::TypeRelating, goal); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 67eb442d2cce7..ecffbbff7a2dc 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -1,9 +1,9 @@ use std::convert::Infallible; use std::marker::PhantomData; -use rustc_type_ir::Interner; use rustc_type_ir::search_graph::{self, PathKind}; -use rustc_type_ir::solve::{CanonicalInput, Certainty, QueryResult}; +use rustc_type_ir::solve::{CanonicalInput, Certainty, NoSolution, QueryResult}; +use rustc_type_ir::{Interner, TypingMode}; use super::inspect::ProofTreeBuilder; use super::{FIXPOINT_STEP_LIMIT, has_no_inference_or_external_constraints}; @@ -47,7 +47,25 @@ where ) -> QueryResult { match kind { PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes), - PathKind::Inductive => response_no_constraints(cx, input, Certainty::overflow(false)), + PathKind::Unknown => response_no_constraints(cx, input, Certainty::overflow(false)), + // Even though we know these cycles to be unproductive, we still return + // overflow during coherence. This is both as we are not 100% confident in + // the implementation yet and any incorrect errors would be unsound there. + // The affected cases are also fairly artificial and not necessarily desirable + // so keeping this as ambiguity is fine for now. + // + // See `tests/ui/traits/next-solver/cycles/unproductive-in-coherence.rs` for an + // example where this would matter. We likely should change these cycles to `NoSolution` + // even in coherence once this is a bit more settled. + PathKind::Inductive => match input.typing_mode { + TypingMode::Coherence => { + response_no_constraints(cx, input, Certainty::overflow(false)) + } + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => Err(NoSolution), + }, } } @@ -57,12 +75,7 @@ where input: CanonicalInput, result: QueryResult, ) -> bool { - match kind { - PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes) == result, - PathKind::Inductive => { - response_no_constraints(cx, input, Certainty::overflow(false)) == result - } - } + Self::initial_provisional_result(cx, kind, input) == result } fn on_stack_overflow( diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 1b18142c24276..7c4e1dc2c12e3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -5,19 +5,19 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::solve::CanonicalResponse; -use rustc_type_ir::visit::TypeVisitableExt as _; use rustc_type_ir::{ - self as ty, Interner, Movability, TraitPredicate, TypingMode, Upcast as _, elaborate, + self as ty, Interner, Movability, TraitPredicate, TypeVisitableExt as _, TypingMode, + Upcast as _, elaborate, }; -use tracing::{instrument, trace}; +use tracing::{debug, instrument, trace}; use crate::delegate::SolverDelegate; use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; -use crate::solve::assembly::{self, Candidate}; +use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate}; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, - NoSolution, QueryResult, + NoSolution, ParamEnvSource, QueryResult, has_only_region_constraints, }; impl assembly::GoalKind for TraitPredicate @@ -72,6 +72,7 @@ where (ty::ImplPolarity::Reservation, _) => match ecx.typing_mode() { TypingMode::Coherence => Certainty::AMBIGUOUS, TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } | TypingMode::PostAnalysis => return Err(NoSolution), }, @@ -124,45 +125,46 @@ where .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } - fn probe_and_match_goal_against_assumption( + fn fast_reject_assumption( ecx: &mut EvalCtxt<'_, D>, - source: CandidateSource, goal: Goal, assumption: I::Clause, - then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, - ) -> Result, NoSolution> { + ) -> Result<(), NoSolution> { if let Some(trait_clause) = assumption.as_trait_clause() { if trait_clause.def_id() == goal.predicate.def_id() && trait_clause.polarity() == goal.predicate.polarity { - if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( goal.predicate.trait_ref.args, trait_clause.skip_binder().trait_ref.args, ) { - return Err(NoSolution); + return Ok(()); } - - ecx.probe_trait_candidate(source).enter(|ecx| { - let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); - ecx.eq( - goal.param_env, - goal.predicate.trait_ref, - assumption_trait_pred.trait_ref, - )?; - then(ecx) - }) - } else { - Err(NoSolution) } - } else { - Err(NoSolution) } + + Err(NoSolution) + } + + fn match_assumption( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, + ) -> QueryResult { + let trait_clause = assumption.as_trait_clause().unwrap(); + + let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); + ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?; + + then(ecx) } fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, ) -> Result, NoSolution> { + let cx = ecx.cx(); if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -173,20 +175,42 @@ where // Only consider auto impls of unsafe traits when there are no unsafe // fields. - if ecx.cx().trait_is_unsafe(goal.predicate.def_id()) + if cx.trait_is_unsafe(goal.predicate.def_id()) && goal.predicate.self_ty().has_unsafe_fields() { return Err(NoSolution); } - // We only look into opaque types during analysis for opaque types - // outside of their defining scope. Doing so for opaques in the - // defining scope may require calling `typeck` on the same item we're - // currently type checking, which will result in a fatal cycle that - // ideally we want to avoid, since we can make progress on this goal - // via an alias bound or a locally-inferred hidden type instead. + // We leak the implemented auto traits of opaques outside of their defining scope. + // This depends on `typeck` of the defining scope of that opaque, which may result in + // fatal query cycles. + // + // We only get to this point if we're outside of the defining scope as we'd otherwise + // be able to normalize the opaque type. We may also cycle in case `typeck` of a defining + // scope relies on the current context, e.g. either because it also leaks auto trait + // bounds of opaques defined in the current context or by evaluating the current item. + // + // To avoid this we don't try to leak auto trait bounds if they can also be proven via + // item bounds of the opaque. These bounds are always applicable as auto traits must not + // have any generic parameters. They would also get preferred over the impl candidate + // when merging candidates anyways. + // + // See tests/ui/impl-trait/auto-trait-leakage/avoid-query-cycle-via-item-bound.rs. if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() { debug_assert!(ecx.opaque_type_is_rigid(opaque_ty.def_id)); + for item_bound in cx.item_self_bounds(opaque_ty.def_id).skip_binder() { + if item_bound + .as_trait_clause() + .is_some_and(|b| b.def_id() == goal.predicate.def_id()) + { + return Err(NoSolution); + } + } + } + + // We need to make sure to stall any coroutines we are inferring to avoid query cycles. + if let Some(cand) = ecx.try_stall_coroutine_witness(goal.predicate.self_ty()) { + return cand; } ecx.probe_and_evaluate_goal_for_constituent_tys( @@ -240,6 +264,11 @@ where return Err(NoSolution); } + // We need to make sure to stall any coroutines we are inferring to avoid query cycles. + if let Some(cand) = ecx.try_stall_coroutine_witness(goal.predicate.self_ty()) { + return cand; + } + ecx.probe_and_evaluate_goal_for_constituent_tys( CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, @@ -570,19 +599,6 @@ where .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } - fn consider_builtin_async_destruct_candidate( - ecx: &mut EvalCtxt<'_, D>, - goal: Goal, - ) -> Result, NoSolution> { - if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); - } - - // `AsyncDestruct` is automatically implemented for every type. - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) - .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) - } - fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, @@ -786,13 +802,6 @@ where ) } - // `(A, B, T)` -> `(A, B, U)` where `T: Unsize` - (ty::Tuple(a_tys), ty::Tuple(b_tys)) - if a_tys.len() == b_tys.len() && !a_tys.is_empty() => - { - result_to_single(ecx.consider_builtin_tuple_unsize(goal, a_tys, b_tys)) - } - _ => vec![], } }) @@ -932,7 +941,7 @@ where target_projection: ty::Binder>| { source_projection.item_def_id() == target_projection.item_def_id() && ecx - .probe(|_| ProbeKind::UpcastProjectionCompatibility) + .probe(|_| ProbeKind::ProjectionCompatibility) .enter(|ecx| -> Result<_, NoSolution> { ecx.enter_forall(target_projection, |ecx, target_projection| { let source_projection = @@ -1084,48 +1093,6 @@ where .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } - /// We generate the following builtin impl for tuples of all sizes. - /// - /// This impl is still unstable and we emit a feature error when it - /// when it is used by a coercion. - /// ```ignore (builtin impl example) - /// impl Unsize<(T, V)> for (T, U) - /// where - /// U: Unsize, - /// {} - /// ``` - fn consider_builtin_tuple_unsize( - &mut self, - goal: Goal, - a_tys: I::Tys, - b_tys: I::Tys, - ) -> Result, NoSolution> { - let cx = self.cx(); - let Goal { predicate: (_a_ty, b_ty), .. } = goal; - - let (&a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); - let b_last_ty = b_tys.last().unwrap(); - - // Instantiate just the tail field of B., and require that they're equal. - let unsized_a_ty = Ty::new_tup_from_iter(cx, a_rest_tys.iter().copied().chain([b_last_ty])); - self.eq(goal.param_env, unsized_a_ty, b_ty)?; - - // Similar to ADTs, require that we can unsize the tail. - self.add_goal( - GoalSource::ImplWhereBound, - goal.with( - cx, - ty::TraitRef::new( - cx, - cx.require_lang_item(TraitSolverLangItem::Unsize), - [a_last_ty, b_last_ty], - ), - ), - ); - self.probe_builtin_trait_candidate(BuiltinImplSource::TupleUnsizing) - .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) - } - // Return `Some` if there is an impl (built-in or user provided) that may // hold for the self type of the goal, which for coherence and soundness // purposes must disqualify the built-in auto impl assembled by considering @@ -1135,6 +1102,25 @@ where goal: Goal>, ) -> Option, NoSolution>> { let self_ty = goal.predicate.self_ty(); + let check_impls = || { + let mut disqualifying_impl = None; + self.cx().for_each_relevant_impl( + goal.predicate.def_id(), + goal.predicate.self_ty(), + |impl_def_id| { + disqualifying_impl = Some(impl_def_id); + }, + ); + if let Some(def_id) = disqualifying_impl { + trace!(?def_id, ?goal, "disqualified auto-trait implementation"); + // No need to actually consider the candidate here, + // since we do that in `consider_impl_candidate`. + return Some(Err(NoSolution)); + } else { + None + } + }; + match self_ty.kind() { // Stall int and float vars until they are resolved to a concrete // numerical type. That's because the check for impls below treats @@ -1145,12 +1131,16 @@ where Some(self.forced_ambiguity(MaybeCause::Ambiguity)) } + // Backward compatibility for default auto traits. + // Test: ui/traits/default_auto_traits/extern-types.rs + ty::Foreign(..) if self.cx().is_default_trait(goal.predicate.def_id()) => check_impls(), + // These types cannot be structurally decomposed into constituent // types, and therefore have no built-in auto impl. ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection | ty::Weak | ty::Inherent, ..) + | ty::Alias(ty::Projection | ty::Free | ty::Inherent, ..) | ty::Placeholder(..) => Some(Err(NoSolution)), ty::Infer(_) | ty::Bound(_, _) => panic!("unexpected type `{self_ty:?}`"), @@ -1205,24 +1195,7 @@ where | ty::Never | ty::Tuple(_) | ty::Adt(_, _) - | ty::UnsafeBinder(_) => { - let mut disqualifying_impl = None; - self.cx().for_each_relevant_impl( - goal.predicate.def_id(), - goal.predicate.self_ty(), - |impl_def_id| { - disqualifying_impl = Some(impl_def_id); - }, - ); - if let Some(def_id) = disqualifying_impl { - trace!(?def_id, ?goal, "disqualified auto-trait implementation"); - // No need to actually consider the candidate here, - // since we do that in `consider_impl_candidate`. - return Some(Err(NoSolution)); - } else { - None - } - } + | ty::UnsafeBinder(_) => check_impls(), ty::Error(_) => None, } } @@ -1280,10 +1253,49 @@ where D: SolverDelegate, I: Interner, { + /// FIXME(#57893): For backwards compatability with the old trait solver implementation, + /// we need to handle overlap between builtin and user-written impls for trait objects. + /// + /// This overlap is unsound in general and something which we intend to fix separately. + /// To avoid blocking the stabilization of the trait solver, we add this hack to avoid + /// breakage in cases which are *mostly fine*™. Importantly, this preference is strictly + /// weaker than the old behavior. + /// + /// We only prefer builtin over user-written impls if there are no inference constraints. + /// Importantly, we also only prefer the builtin impls for trait goals, and not during + /// normalization. This means the only case where this special-case results in exploitable + /// unsoundness should be lifetime dependent user-written impls. + pub(super) fn unsound_prefer_builtin_dyn_impl(&mut self, candidates: &mut Vec>) { + match self.typing_mode() { + TypingMode::Coherence => return, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => {} + } + + if candidates + .iter() + .find(|c| { + matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Object(_))) + }) + .is_some_and(|c| has_only_region_constraints(c.result)) + { + candidates.retain(|c| { + if matches!(c.source, CandidateSource::Impl(_)) { + debug!(?c, "unsoundly dropping impl in favor of builtin dyn-candidate"); + false + } else { + true + } + }); + } + } + + #[instrument(level = "debug", skip(self), ret)] pub(super) fn merge_trait_candidates( &mut self, - goal: Goal>, - candidates: Vec>, + mut candidates: Vec>, ) -> Result<(CanonicalResponse, Option), NoSolution> { if let TypingMode::Coherence = self.typing_mode() { let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect(); @@ -1310,40 +1322,15 @@ where // If there are non-global where-bounds, prefer where-bounds // (including global ones) over everything else. - let has_non_global_where_bounds = candidates.iter().any(|c| match c.source { - CandidateSource::ParamEnv(idx) => { - let where_bound = goal.param_env.caller_bounds().get(idx).unwrap(); - let ty::ClauseKind::Trait(trait_pred) = where_bound.kind().skip_binder() else { - unreachable!("expected trait-bound: {where_bound:?}"); - }; - - if trait_pred.has_bound_vars() || !trait_pred.is_global() { - return true; - } - - // We don't consider a trait-bound global if it has a projection bound. - // - // See ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs - // for an example where this is necessary. - for p in goal.param_env.caller_bounds().iter() { - if let ty::ClauseKind::Projection(proj) = p.kind().skip_binder() { - if proj.projection_term.trait_ref(self.cx()) == trait_pred.trait_ref { - return true; - } - } - } - - false - } - _ => false, - }); + let has_non_global_where_bounds = candidates + .iter() + .any(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::NonGlobal))); if has_non_global_where_bounds { let where_bounds: Vec<_> = candidates .iter() .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_))) .map(|c| c.result) .collect(); - return if let Some(response) = self.try_merge_responses(&where_bounds) { Ok((response, Some(TraitGoalProvenVia::ParamEnv))) } else { @@ -1364,9 +1351,27 @@ where }; } + self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates); + self.unsound_prefer_builtin_dyn_impl(&mut candidates); + + // If there are *only* global where bounds, then make sure to return that this + // is still reported as being proven-via the param-env so that rigid projections + // operate correctly. Otherwise, drop all global where-bounds before merging the + // remaining candidates. + let proven_via = if candidates + .iter() + .all(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global))) + { + TraitGoalProvenVia::ParamEnv + } else { + candidates + .retain(|c| !matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global))); + TraitGoalProvenVia::Misc + }; + let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect(); if let Some(response) = self.try_merge_responses(&all_candidates) { - Ok((response, Some(TraitGoalProvenVia::Misc))) + Ok((response, Some(proven_via))) } else { self.flounder(&all_candidates).map(|r| (r, None)) } @@ -1377,7 +1382,31 @@ where &mut self, goal: Goal>, ) -> Result<(CanonicalResponse, Option), NoSolution> { - let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_trait_candidates(goal, candidates) + let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); + self.merge_trait_candidates(candidates) + } + + fn try_stall_coroutine_witness( + &mut self, + self_ty: I::Ty, + ) -> Option, NoSolution>> { + if let ty::CoroutineWitness(def_id, _) = self_ty.kind() { + match self.typing_mode() { + TypingMode::Analysis { + defining_opaque_types_and_generators: stalled_generators, + } => { + if def_id.as_local().is_some_and(|def_id| stalled_generators.contains(&def_id)) + { + return Some(self.forced_ambiguity(MaybeCause::Ambiguity)); + } + } + TypingMode::Coherence + | TypingMode::PostAnalysis + | TypingMode::Borrowck { defining_opaque_types: _ } + | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {} + } + } + + None } } diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index c9dcab0c871dd..6504081f0b9ce 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" +rustc-literal-escaper = "0.0.2" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 1d5b59421706f..a6919afef12cf 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -26,6 +26,11 @@ parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 20 parse_async_move_order_incorrect = the order of `move` and `async` is incorrect .suggestion = try switching the order +parse_async_use_block_in_2015 = `async use` blocks are only allowed in Rust 2018 or later + +parse_async_use_order_incorrect = the order of `use` and `async` is incorrect + .suggestion = try switching the order + parse_at_dot_dot_in_struct_pattern = `@ ..` is not supported in struct patterns .suggestion = bind to each field separately or, if you don't need them, just remove `{$ident} @` @@ -241,9 +246,9 @@ parse_expected_struct_field = expected one of `,`, `:`, or `{"}"}`, found `{$tok parse_expected_trait_in_trait_impl_found_type = expected a trait, found type -parse_expr_rarrow_call = `->` used for field access or method call +parse_expr_rarrow_call = `->` is not valid syntax for field accesses and method calls .suggestion = try using `.` instead - .help = the `.` operator will dereference the value if needed + .help = the `.` operator will automatically dereference the value, except if the value is a raw pointer parse_extern_crate_name_with_dashes = crate name using dashes are not valid in `extern crate` statements .label = dash-separated idents are not valid @@ -292,6 +297,19 @@ parse_forgot_paren = perhaps you forgot parentheses? parse_found_expr_would_be_stmt = expected expression, found `{$token}` .label = expected expression +parse_frontmatter_extra_characters_after_close = extra characters after frontmatter close are not allowed +parse_frontmatter_invalid_close_preceding_whitespace = invalid preceding whitespace for frontmatter close + .note = frontmatter close should not be preceded by whitespace +parse_frontmatter_invalid_infostring = invalid infostring for frontmatter + .note = frontmatter infostrings must be a single identifier immediately following the opening +parse_frontmatter_invalid_opening_preceding_whitespace = invalid preceding whitespace for frontmatter opening + .note = frontmatter opening should not be preceded by whitespace +parse_frontmatter_length_mismatch = frontmatter close does not match the opening + .label_opening = the opening here has {$len_opening} dashes... + .label_close = ...while the close has {$len_close} dashes +parse_frontmatter_unclosed = unclosed frontmatter + .note = frontmatter opening here was not closed + parse_function_body_equals_expr = function body cannot be `= expression;` .suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;` @@ -348,6 +366,9 @@ parse_incorrect_use_of_await = incorrect use of `await` parse_incorrect_use_of_await_postfix_suggestion = `await` is a postfix operation +parse_incorrect_use_of_use = incorrect use of `use` + .parentheses_suggestion = `use` is not a method call, try removing the parentheses + parse_incorrect_visibility_restriction = incorrect visibility restriction .help = some possible visibility restrictions are: `pub(crate)`: visible only on the current crate @@ -535,7 +556,7 @@ parse_maybe_recover_from_bad_qpath_stage_2 = .suggestion = types that don't start with an identifier need to be surrounded with angle brackets in qualified paths parse_maybe_recover_from_bad_type_plus = - expected a path on the left-hand side of `+`, not `{$ty}` + expected a path on the left-hand side of `+` parse_maybe_report_ambiguous_plus = ambiguous `+` in a type @@ -634,7 +655,9 @@ parse_mut_on_nested_ident_pattern = `mut` must be attached to each individual bi .suggestion = add `mut` to each binding parse_mut_on_non_ident_pattern = `mut` must be followed by a named binding .suggestion = remove the `mut` prefix -parse_need_plus_after_trait_object_lifetime = lifetime in trait object type must be followed by `+` + +parse_need_plus_after_trait_object_lifetime = lifetimes must be followed by `+` to form a trait object type + .suggestion = consider adding a trait bound after the potential lifetime bound parse_nested_adt = `{$kw_str}` definition cannot be nested inside `{$keyword}` .suggestion = consider creating a new `{$kw_str}` definition instead of nesting @@ -665,8 +688,10 @@ parse_note_pattern_alternatives_use_single_vert = alternatives in or-patterns ar parse_nul_in_c_str = null characters in C string literals are not supported -parse_or_pattern_not_allowed_in_fn_parameters = top-level or-patterns are not allowed in function parameters -parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allowed in `let` bindings +parse_or_in_let_chain = `||` operators are not supported in let chain conditions + +parse_or_pattern_not_allowed_in_fn_parameters = function parameters require top-level or-patterns in parentheses +parse_or_pattern_not_allowed_in_let_binding = `let` bindings require top-level or-patterns in parentheses parse_out_of_range_hex_escape = out of range hex escape .label = must be a character in the range [\x00-\x7f] @@ -690,6 +715,16 @@ parse_parenthesized_lifetime_suggestion = remove the parentheses parse_path_double_colon = path separator must be a double colon .suggestion = use a double colon instead + +parse_path_found_attribute_in_params = `Trait(...)` syntax does not support attributes in parameters + .suggestion = remove the attributes + +parse_path_found_c_variadic_params = `Trait(...)` syntax does not support c_variadic parameters + .suggestion = remove the `...` + +parse_path_found_named_params = `Trait(...)` syntax does not support named parameters + .suggestion = remove the parameter name + parse_pattern_method_param_without_body = patterns aren't allowed in methods without bodies .suggestion = give this argument a name or use an underscore to ignore it @@ -749,10 +784,6 @@ parse_struct_literal_body_without_path = struct literal body without path .suggestion = you might have forgotten to add the struct literal inside the block -parse_struct_literal_needing_parens = - invalid struct literal - .suggestion = you might need to surround the struct literal with parentheses - parse_struct_literal_not_allowed_here = struct literals are not allowed here .suggestion = surround the struct literal with parentheses @@ -784,7 +815,6 @@ parse_switch_ref_box_order = switch the order of `ref` and `box` .suggestion = swap them parse_ternary_operator = Rust has no ternary operator - .help = use an `if-else` expression instead parse_tilde_is_not_unary_operator = `~` cannot be used as a unary operator .suggestion = use `!` to perform bitwise not @@ -802,9 +832,6 @@ parse_trait_alias_cannot_be_unsafe = trait aliases cannot be `unsafe` parse_transpose_dyn_or_impl = `for<...>` expected after `{$kw}`, not before .suggestion = move `{$kw}` before the `for<...>` -parse_type_ascription_removed = - if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 - parse_unclosed_unicode_escape = unterminated unicode escape .label = missing a closing `{"}"}` .terminate = terminate the unicode escape @@ -834,8 +861,6 @@ parse_unexpected_expr_in_pat_const_sugg = consider extracting the expression int parse_unexpected_expr_in_pat_create_guard_sugg = consider moving the expression to a match arm guard -parse_unexpected_expr_in_pat_inline_const_sugg = consider wrapping the expression in an inline `const` (requires `{"#"}![feature(inline_const_pat)]`) - parse_unexpected_expr_in_pat_update_guard_sugg = consider moving the expression to the match arm guard parse_unexpected_if_with_if = unexpected `if` in the condition expression @@ -856,7 +881,7 @@ parse_unexpected_parentheses_in_match_arm_pattern = unexpected parentheses surro parse_unexpected_self_in_generic_parameters = unexpected keyword `Self` in generic parameters .note = you cannot use `Self` as a generic parameter because it is reserved for associated items -parse_unexpected_token_after_dot = unexpected token: `{$actual}` +parse_unexpected_token_after_dot = unexpected token: {$actual} parse_unexpected_token_after_label = expected `while`, `for`, `loop` or `{"{"}` after a label .suggestion_remove_label = consider removing the label @@ -892,6 +917,7 @@ parse_unknown_prefix = prefix `{$prefix}` is unknown .label = unknown prefix .note = prefixed identifiers and literals are reserved since Rust 2021 .suggestion_br = use `br` for a raw byte string + .suggestion_cr = use `cr` for a raw C-string .suggestion_str = if you meant to write a string literal, use double quotes .suggestion_whitespace = consider inserting whitespace here @@ -936,6 +962,8 @@ parse_use_empty_block_not_semi = expected { "`{}`" }, found `;` parse_use_eq_instead = unexpected `==` .suggestion = try using `=` instead +parse_use_if_else = use an `if-else` expression instead + parse_use_let_not_auto = write `let` instead of `auto` to introduce a new variable parse_use_let_not_var = write `let` instead of `var` to introduce a new variable diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 8d2fd595942c6..31a48b22cfee1 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -7,8 +7,7 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{Path, Visibility}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, SubdiagMessageOp, - Subdiagnostic, + Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, Subdiagnostic, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::errors::ExprParenthesesNeeded; @@ -30,7 +29,6 @@ pub(crate) struct AmbiguousPlus { #[derive(Diagnostic)] #[diag(parse_maybe_recover_from_bad_type_plus, code = E0178)] pub(crate) struct BadTypePlus { - pub ty: String, #[primary_span] pub span: Span, #[subdiagnostic] @@ -106,6 +104,19 @@ pub(crate) struct IncorrectUseOfAwait { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_incorrect_use_of_use)] +pub(crate) struct IncorrectUseOfUse { + #[primary_span] + #[suggestion( + parse_parentheses_suggestion, + style = "verbose", + code = "", + applicability = "machine-applicable" + )] + pub span: Span, +} + #[derive(Subdiagnostic)] #[multipart_suggestion( parse_incorrect_use_of_await_postfix_suggestion, @@ -425,10 +436,28 @@ pub(crate) enum IfExpressionMissingThenBlockSub { #[derive(Diagnostic)] #[diag(parse_ternary_operator)] -#[help] pub(crate) struct TernaryOperator { #[primary_span] pub span: Span, + /// If we have a span for the condition expression, suggest the if/else + #[subdiagnostic] + pub sugg: Option, + /// Otherwise, just print the suggestion message + #[help(parse_use_if_else)] + pub no_sugg: bool, +} + +#[derive(Subdiagnostic, Copy, Clone)] +#[multipart_suggestion(parse_use_if_else, applicability = "maybe-incorrect", style = "verbose")] +pub(crate) struct TernaryOperatorSuggestion { + #[suggestion_part(code = "if ")] + pub before_cond: Span, + #[suggestion_part(code = "{{")] + pub question: Span, + #[suggestion_part(code = "}} else {{")] + pub colon: Span, + #[suggestion_part(code = " }}")] + pub end: Span, } #[derive(Subdiagnostic)] @@ -467,6 +496,13 @@ pub(crate) struct ExpectedExpressionFoundLet { pub comparison: Option, } +#[derive(Diagnostic)] +#[diag(parse_or_in_let_chain)] +pub(crate) struct OrInLetChain { + #[primary_span] + pub span: Span, +} + #[derive(Subdiagnostic, Clone, Copy)] #[multipart_suggestion( parse_maybe_missing_let, @@ -717,6 +753,61 @@ pub(crate) struct FoundExprWouldBeStmt { pub suggestion: ExprParenthesesNeeded, } +#[derive(Diagnostic)] +#[diag(parse_frontmatter_extra_characters_after_close)] +pub(crate) struct FrontmatterExtraCharactersAfterClose { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_frontmatter_invalid_infostring)] +#[note] +pub(crate) struct FrontmatterInvalidInfostring { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_frontmatter_invalid_opening_preceding_whitespace)] +pub(crate) struct FrontmatterInvalidOpeningPrecedingWhitespace { + #[primary_span] + pub span: Span, + #[note] + pub note_span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_frontmatter_unclosed)] +pub(crate) struct FrontmatterUnclosed { + #[primary_span] + pub span: Span, + #[note] + pub note_span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_frontmatter_invalid_close_preceding_whitespace)] +pub(crate) struct FrontmatterInvalidClosingPrecedingWhitespace { + #[primary_span] + pub span: Span, + #[note] + pub note_span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_frontmatter_length_mismatch)] +pub(crate) struct FrontmatterLengthMismatch { + #[primary_span] + pub span: Span, + #[label(parse_label_opening)] + pub opening: Span, + #[label(parse_label_close)] + pub close: Span, + pub len_opening: usize, + pub len_close: usize, +} + #[derive(Diagnostic)] #[diag(parse_leading_plus_not_supported)] pub(crate) struct LeadingPlusNotSupported { @@ -797,16 +888,16 @@ pub(crate) enum WrapInParentheses { #[derive(Diagnostic)] #[diag(parse_array_brackets_instead_of_braces)] -pub(crate) struct ArrayBracketsInsteadOfSpaces { +pub(crate) struct ArrayBracketsInsteadOfBraces { #[primary_span] pub span: Span, #[subdiagnostic] - pub sub: ArrayBracketsInsteadOfSpacesSugg, + pub sub: ArrayBracketsInsteadOfBracesSugg, } #[derive(Subdiagnostic)] #[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")] -pub(crate) struct ArrayBracketsInsteadOfSpacesSugg { +pub(crate) struct ArrayBracketsInsteadOfBracesSugg { #[suggestion_part(code = "[")] pub left: Span, #[suggestion_part(code = "]")] @@ -1259,24 +1350,6 @@ pub(crate) struct StructLiteralBodyWithoutPathSugg { pub after: Span, } -#[derive(Diagnostic)] -#[diag(parse_struct_literal_needing_parens)] -pub(crate) struct StructLiteralNeedingParens { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub sugg: StructLiteralNeedingParensSugg, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] -pub(crate) struct StructLiteralNeedingParensSugg { - #[suggestion_part(code = "(")] - pub before: Span, - #[suggestion_part(code = ")")] - pub after: Span, -} - #[derive(Diagnostic)] #[diag(parse_unmatched_angle_brackets)] pub(crate) struct UnmatchedAngleBrackets { @@ -1499,6 +1572,14 @@ pub(crate) struct AsyncMoveOrderIncorrect { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_async_use_order_incorrect)] +pub(crate) struct AsyncUseOrderIncorrect { + #[primary_span] + #[suggestion(style = "verbose", code = "async use", applicability = "maybe-incorrect")] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_double_colon_in_bound)] pub(crate) struct DoubleColonInBound { @@ -1548,11 +1629,7 @@ pub(crate) struct FnTraitMissingParen { } impl Subdiagnostic for FnTraitMissingParen { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _: &F, - ) { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren); diag.span_suggestion_short( self.span.shrink_to_hi(), @@ -1587,6 +1664,30 @@ pub(crate) struct ExpectedFnPathFoundFnKeyword { pub fn_token_span: Span, } +#[derive(Diagnostic)] +#[diag(parse_path_found_named_params)] +pub(crate) struct FnPathFoundNamedParams { + #[primary_span] + #[suggestion(applicability = "machine-applicable", code = "")] + pub named_param_span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_path_found_c_variadic_params)] +pub(crate) struct PathFoundCVariadicParams { + #[primary_span] + #[suggestion(applicability = "machine-applicable", code = "")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_path_found_attribute_in_params)] +pub(crate) struct PathFoundAttributeInParams { + #[primary_span] + #[suggestion(applicability = "machine-applicable", code = "")] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_path_double_colon)] pub(crate) struct PathSingleColon { @@ -1595,9 +1696,6 @@ pub(crate) struct PathSingleColon { #[suggestion(applicability = "machine-applicable", code = ":", style = "verbose")] pub suggestion: Span, - - #[note(parse_type_ascription_removed)] - pub type_ascription: bool, } #[derive(Diagnostic)] @@ -1614,9 +1712,6 @@ pub(crate) struct ColonAsSemi { #[primary_span] #[suggestion(applicability = "machine-applicable", code = ";", style = "verbose")] pub span: Span, - - #[note(parse_type_ascription_removed)] - pub type_ascription: bool, } #[derive(Diagnostic)] @@ -1667,6 +1762,13 @@ pub(crate) struct AsyncMoveBlockIn2015 { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_async_use_block_in_2015)] +pub(crate) struct AsyncUseBlockIn2015 { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_async_bound_modifier_in_2015)] pub(crate) struct AsyncBoundModifierIn2015 { @@ -1686,10 +1788,10 @@ pub(crate) struct SelfArgumentPointer { #[derive(Diagnostic)] #[diag(parse_unexpected_token_after_dot)] -pub(crate) struct UnexpectedTokenAfterDot<'a> { +pub(crate) struct UnexpectedTokenAfterDot { #[primary_span] pub span: Span, - pub actual: Cow<'a, str>, + pub actual: String, } #[derive(Diagnostic)] @@ -2142,6 +2244,13 @@ pub(crate) enum UnknownPrefixSugg { style = "verbose" )] UseBr(#[primary_span] Span), + #[suggestion( + parse_suggestion_cr, + code = "cr", + applicability = "maybe-incorrect", + style = "verbose" + )] + UseCr(#[primary_span] Span), #[suggestion( parse_suggestion_whitespace, code = " ", @@ -2759,17 +2868,6 @@ pub(crate) enum UnexpectedExpressionInPatternSugg { /// The statement's block's indentation. indentation: String, }, - - #[multipart_suggestion( - parse_unexpected_expr_in_pat_inline_const_sugg, - applicability = "maybe-incorrect" - )] - InlineConst { - #[suggestion_part(code = "const {{ ")] - start_span: Span, - #[suggestion_part(code = " }}")] - end_span: Span, - }, } #[derive(Diagnostic)] @@ -2807,6 +2905,8 @@ pub(crate) struct ReturnTypesUseThinArrow { pub(crate) struct NeedPlusAfterTraitObjectLifetime { #[primary_span] pub span: Span, + #[suggestion(code = " + /* Trait */", applicability = "has-placeholders")] + pub suggestion: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs index e1f19beb53aee..0b97d4e6993bb 100644 --- a/compiler/rustc_parse/src/lexer/diagnostics.rs +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -1,14 +1,17 @@ use rustc_ast::token::Delimiter; use rustc_errors::Diag; +use rustc_session::parse::ParseSess; use rustc_span::Span; use rustc_span::source_map::SourceMap; use super::UnmatchedDelim; +use crate::errors::MismatchedClosingDelimiter; +use crate::pprust; #[derive(Default)] pub(super) struct TokenTreeDiagInfo { /// Stack of open delimiters and their spans. Used for error message. - pub open_braces: Vec<(Delimiter, Span)>, + pub open_delimiters: Vec<(Delimiter, Span)>, pub unmatched_delims: Vec, /// Used only for error recovery when arriving to EOF with mismatched braces. @@ -108,7 +111,7 @@ pub(super) fn report_suspicious_mismatch_block( } else { // If there is no suspicious span, give the last properly closed block may help if let Some(parent) = diag_info.matching_block_spans.last() - && diag_info.open_braces.last().is_none() + && diag_info.open_delimiters.last().is_none() && diag_info.empty_block_spans.iter().all(|&sp| sp != parent.0.to(parent.1)) { err.span_label(parent.0, "this opening brace..."); @@ -116,3 +119,24 @@ pub(super) fn report_suspicious_mismatch_block( } } } + +pub(crate) fn make_unclosed_delims_error( + unmatched: UnmatchedDelim, + psess: &ParseSess, +) -> Option> { + // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to + // `unmatched_delims` only for error recovery in the `Parser`. + let found_delim = unmatched.found_delim?; + let mut spans = vec![unmatched.found_span]; + if let Some(sp) = unmatched.unclosed_span { + spans.push(sp); + }; + let err = psess.dcx().create_err(MismatchedClosingDelimiter { + spans, + delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind()).to_string(), + unmatched: unmatched.found_span, + opening_candidate: unmatched.candidate_span, + unclosed: unmatched.unclosed_span, + }); + Some(err) +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 792e2cc26ef1c..78c5742414b81 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -1,25 +1,28 @@ use std::ops::Range; +use diagnostics::make_unclosed_delims_error; use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::unicode::contains_text_flow_control_chars; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, StashKey}; -use rustc_lexer::unescape::{self, EscapeError, Mode}; -use rustc_lexer::{Base, Cursor, DocStyle, LiteralKind, RawStrError}; +use rustc_lexer::{ + Base, Cursor, DocStyle, FrontmatterAllowed, LiteralKind, RawStrError, is_whitespace, +}; +use rustc_literal_escaper::{EscapeError, Mode, unescape_mixed, unescape_unicode}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, }; use rustc_session::parse::ParseSess; -use rustc_span::{BytePos, Pos, Span, Symbol}; +use rustc_span::{BytePos, Pos, Span, Symbol, sym}; use tracing::debug; +use crate::errors; use crate::lexer::diagnostics::TokenTreeDiagInfo; use crate::lexer::unicode_chars::UNICODE_ARRAY; -use crate::{errors, make_unclosed_delims_error}; mod diagnostics; mod tokentrees; @@ -55,7 +58,7 @@ pub(crate) fn lex_token_trees<'psess, 'src>( start_pos = start_pos + BytePos::from_usize(shebang_len); } - let cursor = Cursor::new(src); + let cursor = Cursor::new(src, FrontmatterAllowed::Yes); let mut lexer = Lexer { psess, start_pos, @@ -192,6 +195,11 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let content = self.str_from_to(content_start, content_end); self.cook_doc_comment(content_start, content, CommentKind::Block, doc_style) } + rustc_lexer::TokenKind::Frontmatter { has_invalid_preceding_whitespace, invalid_infostring } => { + self.validate_frontmatter(start, has_invalid_preceding_whitespace, invalid_infostring); + preceded_by_whitespace = true; + continue; + } rustc_lexer::TokenKind::Whitespace => { preceded_by_whitespace = true; continue; @@ -255,8 +263,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // was consumed. let lit_start = start + BytePos(prefix_len); self.pos = lit_start; - self.cursor = Cursor::new(&str_before[prefix_len as usize..]); - + self.cursor = Cursor::new(&str_before[prefix_len as usize..], FrontmatterAllowed::No); self.report_unknown_prefix(start); let prefix_span = self.mk_sp(start, lit_start); return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace); @@ -361,7 +368,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // Reset the state so we just lex the `'r`. let lt_start = start + BytePos(2); self.pos = lt_start; - self.cursor = Cursor::new(&str_before[2 as usize..]); + self.cursor = Cursor::new(&str_before[2 as usize..], FrontmatterAllowed::No); let lifetime_name = self.str_from(start); let ident = Symbol::intern(lifetime_name); @@ -371,12 +378,12 @@ impl<'psess, 'src> Lexer<'psess, 'src> { rustc_lexer::TokenKind::Semi => token::Semi, rustc_lexer::TokenKind::Comma => token::Comma, rustc_lexer::TokenKind::Dot => token::Dot, - rustc_lexer::TokenKind::OpenParen => token::OpenDelim(Delimiter::Parenthesis), - rustc_lexer::TokenKind::CloseParen => token::CloseDelim(Delimiter::Parenthesis), - rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(Delimiter::Brace), - rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(Delimiter::Brace), - rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(Delimiter::Bracket), - rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(Delimiter::Bracket), + rustc_lexer::TokenKind::OpenParen => token::OpenParen, + rustc_lexer::TokenKind::CloseParen => token::CloseParen, + rustc_lexer::TokenKind::OpenBrace => token::OpenBrace, + rustc_lexer::TokenKind::CloseBrace => token::CloseBrace, + rustc_lexer::TokenKind::OpenBracket => token::OpenBracket, + rustc_lexer::TokenKind::CloseBracket => token::CloseBracket, rustc_lexer::TokenKind::At => token::At, rustc_lexer::TokenKind::Pound => token::Pound, rustc_lexer::TokenKind::Tilde => token::Tilde, @@ -384,17 +391,17 @@ impl<'psess, 'src> Lexer<'psess, 'src> { 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::Bang => token::Bang, 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::Minus => token::Minus, + rustc_lexer::TokenKind::And => token::And, + rustc_lexer::TokenKind::Or => token::Or, + rustc_lexer::TokenKind::Plus => token::Plus, + rustc_lexer::TokenKind::Star => token::Star, + rustc_lexer::TokenKind::Slash => token::Slash, + rustc_lexer::TokenKind::Caret => token::Caret, + rustc_lexer::TokenKind::Percent => token::Percent, rustc_lexer::TokenKind::Unknown | rustc_lexer::TokenKind::InvalidIdent => { // Don't emit diagnostics for sequences of the same invalid token @@ -474,6 +481,91 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } } + fn validate_frontmatter( + &self, + start: BytePos, + has_invalid_preceding_whitespace: bool, + invalid_infostring: bool, + ) { + let s = self.str_from(start); + let real_start = s.find("---").unwrap(); + let frontmatter_opening_pos = BytePos(real_start as u32) + start; + let s_new = &s[real_start..]; + let within = s_new.trim_start_matches('-'); + let len_opening = s_new.len() - within.len(); + + let frontmatter_opening_end_pos = frontmatter_opening_pos + BytePos(len_opening as u32); + if has_invalid_preceding_whitespace { + let line_start = + BytePos(s[..real_start].rfind("\n").map_or(0, |i| i as u32 + 1)) + start; + let span = self.mk_sp(line_start, frontmatter_opening_end_pos); + let label_span = self.mk_sp(line_start, frontmatter_opening_pos); + self.dcx().emit_err(errors::FrontmatterInvalidOpeningPrecedingWhitespace { + span, + note_span: label_span, + }); + } + + if invalid_infostring { + let line_end = s[real_start..].find('\n').unwrap_or(s[real_start..].len()); + let span = self.mk_sp( + frontmatter_opening_end_pos, + frontmatter_opening_pos + BytePos(line_end as u32), + ); + self.dcx().emit_err(errors::FrontmatterInvalidInfostring { span }); + } + + let last_line_start = within.rfind('\n').map_or(0, |i| i + 1); + let last_line = &within[last_line_start..]; + let last_line_trimmed = last_line.trim_start_matches(is_whitespace); + let last_line_start_pos = frontmatter_opening_end_pos + BytePos(last_line_start as u32); + + let frontmatter_span = self.mk_sp(frontmatter_opening_pos, self.pos); + self.psess.gated_spans.gate(sym::frontmatter, frontmatter_span); + + if !last_line_trimmed.starts_with("---") { + let label_span = self.mk_sp(frontmatter_opening_pos, frontmatter_opening_end_pos); + self.dcx().emit_err(errors::FrontmatterUnclosed { + span: frontmatter_span, + note_span: label_span, + }); + return; + } + + if last_line_trimmed.len() != last_line.len() { + let line_end = last_line_start_pos + BytePos(last_line.len() as u32); + let span = self.mk_sp(last_line_start_pos, line_end); + let whitespace_end = + last_line_start_pos + BytePos((last_line.len() - last_line_trimmed.len()) as u32); + let label_span = self.mk_sp(last_line_start_pos, whitespace_end); + self.dcx().emit_err(errors::FrontmatterInvalidClosingPrecedingWhitespace { + span, + note_span: label_span, + }); + } + + let rest = last_line_trimmed.trim_start_matches('-'); + let len_close = last_line_trimmed.len() - rest.len(); + if len_close != len_opening { + let span = self.mk_sp(frontmatter_opening_pos, self.pos); + let opening = self.mk_sp(frontmatter_opening_pos, frontmatter_opening_end_pos); + let last_line_close_pos = last_line_start_pos + BytePos(len_close as u32); + let close = self.mk_sp(last_line_start_pos, last_line_close_pos); + self.dcx().emit_err(errors::FrontmatterLengthMismatch { + span, + opening, + close, + len_opening, + len_close, + }); + } + + if !rest.trim_matches(is_whitespace).is_empty() { + let span = self.mk_sp(last_line_start_pos, self.pos); + self.dcx().emit_err(errors::FrontmatterExtraCharactersAfterClose { span }); + } + } + fn cook_doc_comment( &self, content_start: BytePos, @@ -789,13 +881,14 @@ impl<'psess, 'src> Lexer<'psess, 'src> { fn report_unknown_prefix(&self, start: BytePos) { let prefix_span = self.mk_sp(start, self.pos); let prefix = self.str_from_to(start, self.pos); - let expn_data = prefix_span.ctxt().outer_expn_data(); if expn_data.edition.at_least_rust_2021() { // In Rust 2021, this is a hard error. let sugg = if prefix == "rb" { Some(errors::UnknownPrefixSugg::UseBr(prefix_span)) + } else if prefix == "rc" { + Some(errors::UnknownPrefixSugg::UseCr(prefix_span)) } else if expn_data.is_root() { if self.cursor.first() == '\'' && let Some(start) = self.last_lifetime @@ -838,7 +931,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let space_pos = start + BytePos(1); let space_span = self.mk_sp(space_pos, space_pos); - let mut cursor = Cursor::new(str_before); + let mut cursor = Cursor::new(str_before, FrontmatterAllowed::No); let (is_string, span, unterminated) = match cursor.guarded_double_quoted_string() { Some(rustc_lexer::GuardedStr { n_hashes, terminated, token_len }) => { @@ -904,7 +997,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // For backwards compatibility, roll back to after just the first `#` // and return the `Pound` token. self.pos = start + BytePos(1); - self.cursor = Cursor::new(&str_before[1..]); + self.cursor = Cursor::new(&str_before[1..], FrontmatterAllowed::No); token::Pound } } @@ -970,9 +1063,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { postfix_len: u32, ) -> (token::LitKind, Symbol) { self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| { - unescape::unescape_unicode(src, mode, &mut |span, result| { - callback(span, result.map(drop)) - }) + unescape_unicode(src, mode, &mut |span, result| callback(span, result.map(drop))) }) } @@ -986,9 +1077,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { postfix_len: u32, ) -> (token::LitKind, Symbol) { self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| { - unescape::unescape_mixed(src, mode, &mut |span, result| { - callback(span, result.map(drop)) - }) + unescape_mixed(src, mode, &mut |span, result| callback(span, result.map(drop))) }) } } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index b3f83a320241e..fbea958dcc598 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -18,38 +18,33 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let mut buf = Vec::new(); loop { - match self.token.kind { - token::OpenDelim(delim) => { - // Invisible delimiters cannot occur here because `TokenTreesReader` parses - // code directly from strings, with no macro expansion involved. - debug_assert!(!matches!(delim, Delimiter::Invisible(_))); - buf.push(match self.lex_token_tree_open_delim(delim) { - Ok(val) => val, - Err(errs) => return Err(errs), - }) - } - token::CloseDelim(delim) => { - // Invisible delimiters cannot occur here because `TokenTreesReader` parses - // code directly from strings, with no macro expansion involved. - debug_assert!(!matches!(delim, Delimiter::Invisible(_))); - return if is_delimited { - Ok((open_spacing, TokenStream::new(buf))) - } else { - Err(vec![self.close_delim_err(delim)]) - }; - } - token::Eof => { - return if is_delimited { - Err(vec![self.eof_err()]) - } else { - Ok((open_spacing, TokenStream::new(buf))) - }; - } - _ => { - // Get the next normal token. - let (this_tok, this_spacing) = self.bump(); - buf.push(TokenTree::Token(this_tok, this_spacing)); - } + if let Some(delim) = self.token.kind.open_delim() { + // Invisible delimiters cannot occur here because `TokenTreesReader` parses + // code directly from strings, with no macro expansion involved. + debug_assert!(!matches!(delim, Delimiter::Invisible(_))); + buf.push(match self.lex_token_tree_open_delim(delim) { + Ok(val) => val, + Err(errs) => return Err(errs), + }) + } else if let Some(delim) = self.token.kind.close_delim() { + // Invisible delimiters cannot occur here because `TokenTreesReader` parses + // code directly from strings, with no macro expansion involved. + debug_assert!(!matches!(delim, Delimiter::Invisible(_))); + return if is_delimited { + Ok((open_spacing, TokenStream::new(buf))) + } else { + Err(vec![self.close_delim_err(delim)]) + }; + } else if self.token.kind == token::Eof { + return if is_delimited { + Err(vec![self.eof_err()]) + } else { + Ok((open_spacing, TokenStream::new(buf))) + }; + } else { + // Get the next normal token. + let (this_tok, this_spacing) = self.bump(); + buf.push(TokenTree::Token(this_tok, this_spacing)); } } } @@ -59,8 +54,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let mut err = self.dcx().struct_span_err(self.token.span, msg); let unclosed_delimiter_show_limit = 5; - let len = usize::min(unclosed_delimiter_show_limit, self.diag_info.open_braces.len()); - for &(_, span) in &self.diag_info.open_braces[..len] { + let len = usize::min(unclosed_delimiter_show_limit, self.diag_info.open_delimiters.len()); + for &(_, span) in &self.diag_info.open_delimiters[..len] { err.span_label(span, "unclosed delimiter"); self.diag_info.unmatched_delims.push(UnmatchedDelim { found_delim: None, @@ -70,19 +65,19 @@ impl<'psess, 'src> Lexer<'psess, 'src> { }); } - if let Some((_, span)) = self.diag_info.open_braces.get(unclosed_delimiter_show_limit) - && self.diag_info.open_braces.len() >= unclosed_delimiter_show_limit + 2 + if let Some((_, span)) = self.diag_info.open_delimiters.get(unclosed_delimiter_show_limit) + && self.diag_info.open_delimiters.len() >= unclosed_delimiter_show_limit + 2 { err.span_label( *span, format!( "another {} unclosed delimiters begin from here", - self.diag_info.open_braces.len() - unclosed_delimiter_show_limit + self.diag_info.open_delimiters.len() - unclosed_delimiter_show_limit ), ); } - if let Some((delim, _)) = self.diag_info.open_braces.last() { + if let Some((delim, _)) = self.diag_info.open_delimiters.last() { report_suspicious_mismatch_block( &mut err, &self.diag_info, @@ -100,7 +95,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // The span for beginning of the delimited section. let pre_span = self.token.span; - self.diag_info.open_braces.push((open_delim, self.token.span)); + self.diag_info.open_delimiters.push((open_delim, self.token.span)); // Lex the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user @@ -111,14 +106,15 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let delim_span = DelimSpan::from_pair(pre_span, self.token.span); let sm = self.psess.source_map(); - let close_spacing = match self.token.kind { - // Correct delimiter. - token::CloseDelim(close_delim) if close_delim == open_delim => { - let (open_brace, open_brace_span) = self.diag_info.open_braces.pop().unwrap(); - let close_brace_span = self.token.span; + let close_spacing = if let Some(close_delim) = self.token.kind.close_delim() { + if close_delim == open_delim { + // Correct delimiter. + let (open_delimiter, open_delimiter_span) = + self.diag_info.open_delimiters.pop().unwrap(); + let close_delimiter_span = self.token.span; if tts.is_empty() && close_delim == Delimiter::Brace { - let empty_block_span = open_brace_span.to(close_brace_span); + let empty_block_span = open_delimiter_span.to(close_delimiter_span); if !sm.is_multiline(empty_block_span) { // Only track if the block is in the form of `{}`, otherwise it is // likely that it was written on purpose. @@ -127,16 +123,17 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } // only add braces - if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, open_delim) { + if let (Delimiter::Brace, Delimiter::Brace) = (open_delimiter, open_delim) { // Add all the matching spans, we will sort by span later - self.diag_info.matching_block_spans.push((open_brace_span, close_brace_span)); + self.diag_info + .matching_block_spans + .push((open_delimiter_span, close_delimiter_span)); } // Move past the closing delimiter. self.bump_minimal() - } - // Incorrect delimiter. - token::CloseDelim(close_delim) => { + } else { + // Incorrect delimiter. let mut unclosed_delimiter = None; let mut candidate = None; @@ -146,18 +143,18 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // This is a conservative error: only report the last unclosed // delimiter. The previous unclosed delimiters could actually be // closed! The lexer just hasn't gotten to them yet. - if let Some(&(_, sp)) = self.diag_info.open_braces.last() { + if let Some(&(_, sp)) = self.diag_info.open_delimiters.last() { unclosed_delimiter = Some(sp); }; - for (brace, brace_span) in &self.diag_info.open_braces { - if same_indentation_level(sm, self.token.span, *brace_span) - && brace == &close_delim + for (delimiter, delimiter_span) in &self.diag_info.open_delimiters { + if same_indentation_level(sm, self.token.span, *delimiter_span) + && delimiter == &close_delim { // high likelihood of these two corresponding - candidate = Some(*brace_span); + candidate = Some(*delimiter_span); } } - let (_, _) = self.diag_info.open_braces.pop().unwrap(); + let (_, _) = self.diag_info.open_delimiters.pop().unwrap(); self.diag_info.unmatched_delims.push(UnmatchedDelim { found_delim: Some(close_delim), found_span: self.token.span, @@ -165,7 +162,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { candidate_span: candidate, }); } else { - self.diag_info.open_braces.pop(); + self.diag_info.open_delimiters.pop(); } // If the incorrect delimiter matches an earlier opening @@ -175,21 +172,20 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // fn foo() { // bar(baz( // } // Incorrect delimiter but matches the earlier `{` - if !self.diag_info.open_braces.iter().any(|&(b, _)| b == close_delim) { + if !self.diag_info.open_delimiters.iter().any(|&(d, _)| d == close_delim) { self.bump_minimal() } else { // The choice of value here doesn't matter. Spacing::Alone } } - token::Eof => { - // Silently recover, the EOF token will be seen again - // and an error emitted then. Thus we don't pop from - // self.open_braces here. The choice of spacing value here - // doesn't matter. - Spacing::Alone - } - _ => unreachable!(), + } else { + assert_eq!(self.token.kind, token::Eof); + // Silently recover, the EOF token will be seen again + // and an error emitted then. Thus we don't pop from + // self.open_delimiters here. The choice of spacing value here + // doesn't matter. + Spacing::Alone }; let spacing = DelimSpacing::new(open_spacing, close_spacing); diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 2e066f0179c3f..ec59a1a01314e 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -4,7 +4,7 @@ use std::iter::once; use std::ops::Range; use rustc_errors::{Applicability, DiagCtxtHandle, ErrorGuaranteed}; -use rustc_lexer::unescape::{EscapeError, Mode}; +use rustc_literal_escaper::{EscapeError, Mode}; use rustc_span::{BytePos, Span}; use tracing::debug; diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index 648a352efd9bd..751d13af4331b 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -5,7 +5,7 @@ use rustc_span::{BytePos, Pos, Span, kw}; use super::Lexer; use crate::errors::TokenSubstitution; -use crate::token::{self, Delimiter}; +use crate::token; #[rustfmt::skip] // for line breaks pub(super) static UNICODE_ARRAY: &[(char, &str, &str)] = &[ @@ -308,24 +308,24 @@ pub(super) static UNICODE_ARRAY: &[(char, &str, &str)] = &[ const ASCII_ARRAY: &[(&str, &str, Option)] = &[ (" ", "Space", None), ("_", "Underscore", Some(token::Ident(kw::Underscore, token::IdentIsRaw::No))), - ("-", "Minus/Hyphen", Some(token::BinOp(token::Minus))), + ("-", "Minus/Hyphen", Some(token::Minus)), (",", "Comma", Some(token::Comma)), (";", "Semicolon", Some(token::Semi)), (":", "Colon", Some(token::Colon)), - ("!", "Exclamation Mark", Some(token::Not)), + ("!", "Exclamation Mark", Some(token::Bang)), ("?", "Question Mark", Some(token::Question)), (".", "Period", Some(token::Dot)), - ("(", "Left Parenthesis", Some(token::OpenDelim(Delimiter::Parenthesis))), - (")", "Right Parenthesis", Some(token::CloseDelim(Delimiter::Parenthesis))), - ("[", "Left Square Bracket", Some(token::OpenDelim(Delimiter::Bracket))), - ("]", "Right Square Bracket", Some(token::CloseDelim(Delimiter::Bracket))), - ("{", "Left Curly Brace", Some(token::OpenDelim(Delimiter::Brace))), - ("}", "Right Curly Brace", Some(token::CloseDelim(Delimiter::Brace))), - ("*", "Asterisk", Some(token::BinOp(token::Star))), - ("/", "Slash", Some(token::BinOp(token::Slash))), + ("(", "Left Parenthesis", Some(token::OpenParen)), + (")", "Right Parenthesis", Some(token::CloseParen)), + ("[", "Left Square Bracket", Some(token::OpenBracket)), + ("]", "Right Square Bracket", Some(token::CloseBracket)), + ("{", "Left Curly Brace", Some(token::OpenBrace)), + ("}", "Right Curly Brace", Some(token::CloseBrace)), + ("*", "Asterisk", Some(token::Star)), + ("/", "Slash", Some(token::Slash)), ("\\", "Backslash", None), - ("&", "Ampersand", Some(token::BinOp(token::And))), - ("+", "Plus Sign", Some(token::BinOp(token::Plus))), + ("&", "Ampersand", Some(token::And)), + ("+", "Plus Sign", Some(token::Plus)), ("<", "Less-Than Sign", Some(token::Lt)), ("=", "Equals Sign", Some(token::Eq)), ("==", "Double Equals Sign", Some(token::EqEq)), @@ -376,7 +376,7 @@ pub(super) fn check_for_substitution( ascii_name, }) }; - (token.clone(), sugg) + (*token, sugg) } /// Extract string if found at current position with given delimiters diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 1a104ff5e3375..3ab726d9d9d64 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -4,15 +4,13 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![feature(array_windows)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(debug_closure_helpers)] #![feature(if_let_guard)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(string_from_utf8_lossy_owned)] -#![warn(unreachable_pub)] +#![recursion_limit = "256"] // tidy-alphabetical-end use std::path::{Path, PathBuf}; @@ -33,7 +31,7 @@ pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); #[macro_use] pub mod parser; -use parser::{Parser, make_unclosed_delims_error}; +use parser::Parser; pub mod lexer; pub mod validate_attr; diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 98fdd35190d8a..41d3889c44838 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -1,5 +1,6 @@ use rustc_ast as ast; use rustc_ast::token::{self, MetaVarKind}; +use rustc_ast::tokenstream::ParserRange; use rustc_ast::{Attribute, attr}; use rustc_errors::codes::*; use rustc_errors::{Diag, PResult}; @@ -8,8 +9,7 @@ use thin_vec::ThinVec; use tracing::debug; use super::{ - AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, ParserRange, PathStyle, Trailing, - UsePreAttrPos, + AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle, Trailing, UsePreAttrPos, }; use crate::{errors, exp, fluent_generated as fluent}; @@ -127,12 +127,29 @@ impl<'a> Parser<'a> { let lo = self.token.span; // Attributes can't have attributes of their own [Editor's note: not with that attitude] self.collect_tokens_no_attrs(|this| { + let pound_hi = this.token.span.hi(); assert!(this.eat(exp!(Pound)), "parse_attribute called in non-attribute position"); + let not_lo = this.token.span.lo(); let style = - if this.eat(exp!(Not)) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer }; + if this.eat(exp!(Bang)) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer }; - this.expect(exp!(OpenBracket))?; + let mut bracket_res = this.expect(exp!(OpenBracket)); + // If `#!` is not followed by `[` + if let Err(err) = &mut bracket_res + && style == ast::AttrStyle::Inner + && pound_hi == not_lo + { + err.note( + "the token sequence `#!` here looks like the start of \ + a shebang interpreter directive but it is not", + ); + err.help( + "if you meant this to be a shebang interpreter directive, \ + move it to the very start of the file", + ); + } + bracket_res?; let item = this.parse_attr_item(ForceCollect::No)?; this.expect(exp!(CloseBracket))?; let attr_sp = lo.to(this.prev_token.span); @@ -312,7 +329,7 @@ impl<'a> Parser<'a> { loop { let start_pos = self.num_bump_calls; // Only try to parse if it is an inner attribute (has `!`). - let attr = if self.check(exp!(Pound)) && self.look_ahead(1, |t| t == &token::Not) { + let attr = if self.check(exp!(Pound)) && self.look_ahead(1, |t| t == &token::Bang) { Some(self.parse_attribute(InnerAttrPolicy::Permitted)?) } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind { if attr_style == ast::AttrStyle::Inner { diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index cff998fa13790..44fdf146f9c73 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -1,21 +1,18 @@ use std::borrow::Cow; -use std::{iter, mem}; +use std::mem; -use rustc_ast::token::{Delimiter, Token, TokenKind}; +use rustc_ast::token::Token; use rustc_ast::tokenstream::{ - AttrTokenStream, AttrTokenTree, AttrsTarget, DelimSpacing, DelimSpan, LazyAttrTokenStream, - Spacing, ToAttrTokenStream, + AttrsTarget, LazyAttrTokenStream, NodeRange, ParserRange, Spacing, TokenCursor, }; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, HasTokens}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::PResult; use rustc_session::parse::ParseSess; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, sym}; +use thin_vec::ThinVec; -use super::{ - Capturing, FlatToken, ForceCollect, NodeRange, NodeReplacement, Parser, ParserRange, - TokenCursor, Trailing, -}; +use super::{Capturing, ForceCollect, Parser, Trailing}; // When collecting tokens, this fully captures the start point. Usually its // just after outer attributes, but occasionally it's before. @@ -94,99 +91,10 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool { }) } -// From a value of this type we can reconstruct the `TokenStream` seen by the -// `f` callback passed to a call to `Parser::collect_tokens`, by -// replaying the getting of the tokens. This saves us producing a `TokenStream` -// if it is never needed, e.g. a captured `macro_rules!` argument that is never -// passed to a proc macro. In practice, token stream creation happens rarely -// compared to calls to `collect_tokens` (see some statistics in #78736) so we -// are doing as little up-front work as possible. -// -// This also makes `Parser` very cheap to clone, since -// there is no intermediate collection buffer to clone. -struct LazyAttrTokenStreamImpl { - start_token: (Token, Spacing), - cursor_snapshot: TokenCursor, - num_calls: u32, - break_last_token: u32, - node_replacements: Box<[NodeReplacement]>, -} - -impl ToAttrTokenStream for LazyAttrTokenStreamImpl { - fn to_attr_token_stream(&self) -> AttrTokenStream { - // The token produced by the final call to `{,inlined_}next` was not - // actually consumed by the callback. The combination of chaining the - // initial token and using `take` produces the desired result - we - // produce an empty `TokenStream` if no calls were made, and omit the - // final token otherwise. - let mut cursor_snapshot = self.cursor_snapshot.clone(); - let tokens = iter::once(FlatToken::Token(self.start_token.clone())) - .chain(iter::repeat_with(|| FlatToken::Token(cursor_snapshot.next()))) - .take(self.num_calls as usize); - - if self.node_replacements.is_empty() { - make_attr_token_stream(tokens, self.break_last_token) - } else { - let mut tokens: Vec<_> = tokens.collect(); - let mut node_replacements = self.node_replacements.to_vec(); - node_replacements.sort_by_key(|(range, _)| range.0.start); - - #[cfg(debug_assertions)] - for [(node_range, tokens), (next_node_range, next_tokens)] in - node_replacements.array_windows() - { - assert!( - node_range.0.end <= next_node_range.0.start - || node_range.0.end >= next_node_range.0.end, - "Node ranges should be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", - node_range, - tokens, - next_node_range, - next_tokens, - ); - } - - // Process the replace ranges, starting from the highest start - // position and working our way back. If have tokens like: - // - // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` - // - // Then we will generate replace ranges for both - // the `#[cfg(FALSE)] field: bool` and the entire - // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` - // - // By starting processing from the replace range with the greatest - // start position, we ensure that any (outer) replace range which - // encloses another (inner) replace range will fully overwrite the - // inner range's replacement. - for (node_range, target) in node_replacements.into_iter().rev() { - assert!( - !node_range.0.is_empty(), - "Cannot replace an empty node range: {:?}", - node_range.0 - ); - - // Replace the tokens in range with zero or one `FlatToken::AttrsTarget`s, plus - // enough `FlatToken::Empty`s to fill up the rest of the range. This keeps the - // total length of `tokens` constant throughout the replacement process, allowing - // us to do all replacements without adjusting indices. - let target_len = target.is_some() as usize; - tokens.splice( - (node_range.0.start as usize)..(node_range.0.end as usize), - target.into_iter().map(|target| FlatToken::AttrsTarget(target)).chain( - iter::repeat(FlatToken::Empty).take(node_range.0.len() - target_len), - ), - ); - } - make_attr_token_stream(tokens.into_iter(), self.break_last_token) - } - } -} - impl<'a> Parser<'a> { pub(super) fn collect_pos(&self) -> CollectPos { CollectPos { - start_token: (self.token.clone(), self.token_spacing), + start_token: (self.token, self.token_spacing), cursor_snapshot: self.token_cursor.clone(), start_pos: self.num_bump_calls, } @@ -387,10 +295,10 @@ impl<'a> Parser<'a> { // This is hot enough for `deep-vector` that checking the conditions for an empty iterator // is measurably faster than actually executing the iterator. - let node_replacements: Box<[_]> = if parser_replacements_start == parser_replacements_end + let node_replacements = if parser_replacements_start == parser_replacements_end && inner_attr_parser_replacements.is_empty() { - Box::new([]) + ThinVec::new() } else { // Grab any replace ranges that occur *inside* the current AST node. Convert them // from `ParserRange` form to `NodeRange` form. We will perform the actual @@ -429,13 +337,13 @@ impl<'a> Parser<'a> { // - `attrs`: includes the outer and the inner attr. // - `tokens`: lazy tokens for `g` (with its inner attr deleted). - let tokens = LazyAttrTokenStream::new(LazyAttrTokenStreamImpl { - start_token: collect_pos.start_token, - cursor_snapshot: collect_pos.cursor_snapshot, + let tokens = LazyAttrTokenStream::new_pending( + collect_pos.start_token, + collect_pos.cursor_snapshot, num_calls, - break_last_token: self.break_last_token, + self.break_last_token, node_replacements, - }); + ); let mut tokens_used = false; // If in "definite capture mode" we need to register a replace range @@ -483,71 +391,6 @@ impl<'a> Parser<'a> { } } -/// Converts a flattened iterator of tokens (including open and close delimiter tokens) into an -/// `AttrTokenStream`, creating an `AttrTokenTree::Delimited` for each matching pair of open and -/// close delims. -fn make_attr_token_stream( - iter: impl Iterator, - break_last_token: u32, -) -> AttrTokenStream { - #[derive(Debug)] - struct FrameData { - // This is `None` for the first frame, `Some` for all others. - open_delim_sp: Option<(Delimiter, Span, Spacing)>, - inner: Vec, - } - // The stack always has at least one element. Storing it separately makes for shorter code. - let mut stack_top = FrameData { open_delim_sp: None, inner: vec![] }; - let mut stack_rest = vec![]; - for flat_token in iter { - match flat_token { - FlatToken::Token((Token { kind: TokenKind::OpenDelim(delim), span }, spacing)) => { - stack_rest.push(mem::replace( - &mut stack_top, - FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] }, - )); - } - FlatToken::Token((Token { kind: TokenKind::CloseDelim(delim), span }, spacing)) => { - let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap()); - let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap(); - assert!( - open_delim.eq_ignoring_invisible_origin(&delim), - "Mismatched open/close delims: open={open_delim:?} close={span:?}" - ); - let dspan = DelimSpan::from_pair(open_sp, span); - let dspacing = DelimSpacing::new(open_spacing, spacing); - let stream = AttrTokenStream::new(frame_data.inner); - let delimited = AttrTokenTree::Delimited(dspan, dspacing, delim, stream); - stack_top.inner.push(delimited); - } - FlatToken::Token((token, spacing)) => { - stack_top.inner.push(AttrTokenTree::Token(token, spacing)) - } - FlatToken::AttrsTarget(target) => { - stack_top.inner.push(AttrTokenTree::AttrsTarget(target)) - } - FlatToken::Empty => {} - } - } - - if break_last_token > 0 { - let last_token = stack_top.inner.pop().unwrap(); - if let AttrTokenTree::Token(last_token, spacing) = last_token { - let (unglued, _) = last_token.kind.break_two_token_op(break_last_token).unwrap(); - - // Tokens are always ASCII chars, so we can use byte arithmetic here. - let mut first_span = last_token.span.shrink_to_lo(); - first_span = - first_span.with_hi(first_span.lo() + rustc_span::BytePos(break_last_token)); - - stack_top.inner.push(AttrTokenTree::Token(Token::new(unglued, first_span), spacing)); - } else { - panic!("Unexpected last token {last_token:?}") - } - } - AttrTokenStream::new(stack_top.inner) -} - /// Tokens are needed if: /// - any non-single-segment attributes (other than doc comments) are present, /// e.g. `rustfmt::skip`; or @@ -562,14 +405,3 @@ fn needs_tokens(attrs: &[ast::Attribute]) -> bool { } }) } - -// Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(target_pointer_width = "64")] -mod size_asserts { - use rustc_data_structures::static_assert_size; - - use super::*; - // tidy-alphabetical-start - static_assert_size!(LazyAttrTokenStreamImpl, 96); - // tidy-alphabetical-end -} diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 02bcb1fbf7028..6277dde7c974c 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -4,7 +4,7 @@ use std::ops::{Deref, DerefMut}; use ast::token::IdentIsRaw; use rustc_ast as ast; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Lit, LitKind, Token, TokenKind}; +use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block, @@ -31,19 +31,19 @@ use super::{ SeqSep, TokenType, }; use crate::errors::{ - AddParen, AmbiguousPlus, AsyncMoveBlockIn2015, AttributeOnParamType, AwaitSuggestion, - BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi, ComparisonOperatorsCannotBeChained, - ComparisonOperatorsCannotBeChainedSugg, ConstGenericWithoutBraces, - ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything, DocCommentOnParamType, - DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, + AddParen, AmbiguousPlus, AsyncMoveBlockIn2015, AsyncUseBlockIn2015, AttributeOnParamType, + AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi, + ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg, + ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything, + DocCommentOnParamType, DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait, - IncorrectSemicolon, IncorrectUseOfAwait, PatternMethodParamWithoutBody, QuestionMarkInType, - QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath, - StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg, - SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, - UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, - UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType, + IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, PatternMethodParamWithoutBody, + QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath, + StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, + TernaryOperator, TernaryOperatorSuggestion, UnexpectedConstInGenericParam, + UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, + UseEqInstead, WrapType, }; use crate::parser::attr::InnerAttrPolicy; use crate::{exp, fluent_generated as fluent}; @@ -305,10 +305,10 @@ impl<'a> Parser<'a> { TokenKind::Comma, TokenKind::Semi, TokenKind::PathSep, - TokenKind::OpenDelim(Delimiter::Brace), - TokenKind::OpenDelim(Delimiter::Parenthesis), - TokenKind::CloseDelim(Delimiter::Brace), - TokenKind::CloseDelim(Delimiter::Parenthesis), + TokenKind::OpenBrace, + TokenKind::OpenParen, + TokenKind::CloseBrace, + TokenKind::CloseParen, ]; if let TokenKind::DocComment(..) = self.prev_token.kind && valid_follow.contains(&self.token.kind) @@ -323,7 +323,7 @@ impl<'a> Parser<'a> { let mut recovered_ident = None; // we take this here so that the correct original token is retained in // the diagnostic, regardless of eager recovery. - let bad_token = self.token.clone(); + let bad_token = self.token; // suggest prepending a keyword in identifier position with `r#` let suggest_raw = if let Some((ident, IdentIsRaw::No)) = self.token.ident() @@ -383,7 +383,7 @@ impl<'a> Parser<'a> { // if the previous token is a valid keyword // that might use a generic, then suggest a correct // generic placement (later on) - let maybe_keyword = self.prev_token.clone(); + let maybe_keyword = self.prev_token; if valid_prev_keywords.into_iter().any(|x| maybe_keyword.is_keyword(x)) { // if we have a valid keyword, attempt to parse generics // also obtain the keywords symbol @@ -498,7 +498,7 @@ impl<'a> Parser<'a> { // If the user is trying to write a ternary expression, recover it and // return an Err to prevent a cascade of irrelevant diagnostics. if self.prev_token == token::Question - && let Err(e) = self.maybe_recover_from_ternary_operator() + && let Err(e) = self.maybe_recover_from_ternary_operator(None) { return Err(e); } @@ -508,7 +508,7 @@ impl<'a> Parser<'a> { } 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 == token::CloseDelim(Delimiter::Parenthesis) + && self.prev_token == token::CloseParen { // Likely typo: The current token is on a new line and is expected to be // `.`, `;`, `?`, or an operator after a close delimiter token. @@ -519,8 +519,7 @@ impl<'a> Parser<'a> { // ^ // https://github.com/rust-lang/rust/issues/72253 } else if self.look_ahead(1, |t| { - t == &token::CloseDelim(Delimiter::Brace) - || t.can_begin_expr() && *t != token::Colon + t == &token::CloseBrace || t.can_begin_expr() && *t != token::Colon }) && [token::Comma, token::Colon].contains(&self.token.kind) { // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is @@ -531,14 +530,14 @@ impl<'a> Parser<'a> { // let y = 42; let guar = self.dcx().emit_err(ExpectedSemi { span: self.token.span, - token: self.token.clone(), + token: self.token, unexpected_token_label: None, sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span), }); self.bump(); return Ok(guar); } else if self.look_ahead(0, |t| { - t == &token::CloseDelim(Delimiter::Brace) + t == &token::CloseBrace || ((t.can_begin_expr() || t.can_begin_item()) && t != &token::Semi && t != &token::Pound) @@ -556,7 +555,7 @@ impl<'a> Parser<'a> { let span = self.prev_token.span.shrink_to_hi(); let guar = self.dcx().emit_err(ExpectedSemi { span, - token: self.token.clone(), + token: self.token, unexpected_token_label: Some(self.token.span), sugg: ExpectedSemiSugg::AddSemi(span), }); @@ -572,10 +571,17 @@ impl<'a> Parser<'a> { return Err(self.dcx().create_err(UseEqInstead { span: self.token.span })); } - if self.token.is_keyword(kw::Move) && self.prev_token.is_keyword(kw::Async) { - // The 2015 edition is in use because parsing of `async move` has failed. + if (self.token.is_keyword(kw::Move) || self.token.is_keyword(kw::Use)) + && self.prev_token.is_keyword(kw::Async) + { + // The 2015 edition is in use because parsing of `async move` or `async use` has failed. let span = self.prev_token.span.to(self.token.span); - return Err(self.dcx().create_err(AsyncMoveBlockIn2015 { span })); + if self.token.is_keyword(kw::Move) { + return Err(self.dcx().create_err(AsyncMoveBlockIn2015 { span })); + } else { + // kw::Use + return Err(self.dcx().create_err(AsyncUseBlockIn2015 { span })); + } } let expect = tokens_to_string(&expected); @@ -603,6 +609,8 @@ impl<'a> Parser<'a> { // FIXME: translation requires list formatting (for `expect`) let mut err = self.dcx().struct_span_err(self.token.span, msg_exp); + self.label_expected_raw_ref(&mut err); + // Look for usages of '=>' where '>=' was probably intended if self.token == token::FatArrow && expected.iter().any(|tok| matches!(tok, TokenType::Operator | TokenType::Le)) @@ -667,8 +675,7 @@ impl<'a> Parser<'a> { // `pub` may be used for an item or `pub(crate)` if self.prev_token.is_ident_named(sym::public) - && (self.token.can_begin_item() - || self.token == TokenKind::OpenDelim(Delimiter::Parenthesis)) + && (self.token.can_begin_item() || self.token == TokenKind::OpenParen) { err.span_suggestion_short( self.prev_token.span, @@ -744,6 +751,25 @@ impl<'a> Parser<'a> { Err(err) } + /// Adds a label when `&raw EXPR` was written instead of `&raw const EXPR`/`&raw mut EXPR`. + /// + /// Given that not all parser diagnostics flow through `expected_one_of_not_found`, this + /// label may need added to other diagnostics emission paths as needed. + pub(super) fn label_expected_raw_ref(&mut self, err: &mut Diag<'_>) { + if self.prev_token.is_keyword(kw::Raw) + && self.expected_token_types.contains(TokenType::KwMut) + && self.expected_token_types.contains(TokenType::KwConst) + && self.token.can_begin_expr() + { + err.span_suggestions( + self.prev_token.span.shrink_to_hi(), + "`&raw` must be followed by `const` or `mut` to be a raw reference expression", + [" const".to_string(), " mut".to_string()], + Applicability::MaybeIncorrect, + ); + } + } + /// Checks if the current token or the previous token are misspelled keywords /// and adds a helpful suggestion. fn check_for_misspelled_kw(&self, err: &mut Diag<'_>, expected: &[TokenType]) { @@ -795,7 +821,7 @@ impl<'a> Parser<'a> { let span = self.prev_token.span.shrink_to_hi(); let mut err = self.dcx().create_err(ExpectedSemi { span, - token: self.token.clone(), + token: self.token, unexpected_token_label: Some(self.token.span), sugg: ExpectedSemiSugg::AddSemi(span), }); @@ -816,9 +842,7 @@ impl<'a> Parser<'a> { if expr.attrs.len() == 1 { "this attribute" } else { "these attributes" }, ), ); - if self.token == token::Pound - && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket)) - { + if self.token == token::Pound && self.look_ahead(1, |t| *t == token::OpenBracket) { // We have // #[attr] // expr @@ -942,7 +966,6 @@ impl<'a> Parser<'a> { lo: Span, s: BlockCheckMode, maybe_struct_name: token::Token, - can_be_struct_literal: bool, ) -> Option>> { if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) { // We might be having a struct literal where people forgot to include the path: @@ -968,47 +991,23 @@ impl<'a> Parser<'a> { // fn foo() -> Foo { Path { // field: value, // } } - let guar = err.delay_as_bug(); + err.cancel(); self.restore_snapshot(snapshot); - let mut tail = self.mk_block( + let guar = self.dcx().emit_err(StructLiteralBodyWithoutPath { + span: expr.span, + sugg: StructLiteralBodyWithoutPathSugg { + before: expr.span.shrink_to_lo(), + after: expr.span.shrink_to_hi(), + }, + }); + Ok(self.mk_block( thin_vec![self.mk_stmt_err(expr.span, guar)], s, lo.to(self.prev_token.span), - ); - tail.could_be_bare_literal = true; - if maybe_struct_name.is_ident() && can_be_struct_literal { - // Account for `if Example { a: one(), }.is_pos() {}`. - // expand `before` so that we take care of module path such as: - // `foo::Bar { ... } ` - // we expect to suggest `(foo::Bar { ... })` instead of `foo::(Bar { ... })` - let sm = self.psess.source_map(); - let before = maybe_struct_name.span.shrink_to_lo(); - if let Ok(extend_before) = sm.span_extend_prev_while(before, |t| { - t.is_alphanumeric() || t == ':' || t == '_' - }) { - Err(self.dcx().create_err(StructLiteralNeedingParens { - span: maybe_struct_name.span.to(expr.span), - sugg: StructLiteralNeedingParensSugg { - before: extend_before.shrink_to_lo(), - after: expr.span.shrink_to_hi(), - }, - })) - } else { - return None; - } - } else { - self.dcx().emit_err(StructLiteralBodyWithoutPath { - span: expr.span, - sugg: StructLiteralBodyWithoutPathSugg { - before: expr.span.shrink_to_lo(), - after: expr.span.shrink_to_hi(), - }, - }); - Ok(tail) - } + )) } (Err(err), Ok(tail)) => { - // We have a block tail that contains a somehow valid type ascription expr. + // We have a block tail that contains a somehow valid expr. err.cancel(); Ok(tail) } @@ -1018,10 +1017,7 @@ impl<'a> Parser<'a> { self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes); Err(err) } - (Ok(_), Ok(mut tail)) => { - tail.could_be_bare_literal = true; - Ok(tail) - } + (Ok(_), Ok(tail)) => Ok(tail), }); } None @@ -1038,9 +1034,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P> { err.span_label(lo.to(decl_hi), "while parsing the body of this closure"); let guar = match before.kind { - token::OpenDelim(Delimiter::Brace) - if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => - { + token::OpenBrace if token.kind != token::OpenBrace => { // `{ || () }` should have been `|| { () }` err.multipart_suggestion( "you might have meant to open the body of the closure, instead of enclosing \ @@ -1055,9 +1049,7 @@ impl<'a> Parser<'a> { self.eat_to_tokens(&[exp!(CloseBrace)]); guar } - token::OpenDelim(Delimiter::Parenthesis) - if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => - { + token::OpenParen if token.kind != token::OpenBrace => { // We are within a function call or tuple, we can emit the error // and recover. self.eat_to_tokens(&[exp!(CloseParen), exp!(Comma)]); @@ -1072,7 +1064,7 @@ impl<'a> Parser<'a> { ); err.emit() } - _ if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => { + _ if token.kind != token::OpenBrace => { // We don't have a heuristic to correctly identify where the block // should be closed. err.multipart_suggestion_verbose( @@ -1167,7 +1159,7 @@ impl<'a> Parser<'a> { 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) { + if *t == token::Shr { number_of_shr += 1; true } else if *t == token::Gt { @@ -1222,11 +1214,11 @@ impl<'a> Parser<'a> { 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 == token::BinOp(token::Shr) || self.token == token::Gt { + while self.token == token::Shr || self.token == token::Gt { trailing_span = trailing_span.to(self.token.span); self.bump(); } - if self.token == token::OpenDelim(Delimiter::Parenthesis) { + if self.token == token::OpenParen { // Recover from bad turbofish: `foo.collect::Vec<_>()`. segment.args = Some(AngleBracketedArgs { args, span }.into()); @@ -1468,13 +1460,10 @@ impl<'a> Parser<'a> { let snapshot = self.create_snapshot_for_diagnostic(); self.bump(); // So far we have parsed `foo(` or `foo< bar >::`, so we rewind the // parser and bail out. self.restore_snapshot(snapshot); @@ -1512,7 +1501,7 @@ impl<'a> Parser<'a> { Err(self.dcx().create_err(err)) } } - } else if self.token == token::OpenDelim(Delimiter::Parenthesis) { + } else if self.token == token::OpenParen { // We have high certainty that this was a bad turbofish at this point. // `foo< bar >(` if let ExprKind::Binary(o, ..) = inner_op.kind @@ -1572,10 +1561,7 @@ impl<'a> Parser<'a> { self.bump(); // `(` // Consume the fn call arguments. - let modifiers = [ - (token::OpenDelim(Delimiter::Parenthesis), 1), - (token::CloseDelim(Delimiter::Parenthesis), -1), - ]; + let modifiers = [(token::OpenParen, 1), (token::CloseParen, -1)]; self.consume_tts(1, &modifiers); if self.token == token::Eof { @@ -1617,12 +1603,18 @@ impl<'a> Parser<'a> { /// Rust has no ternary operator (`cond ? then : else`). Parse it and try /// to recover from it if `then` and `else` are valid expressions. Returns /// an err if this appears to be a ternary expression. - pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> PResult<'a, ()> { + /// If we have the span of the condition, we can provide a better error span + /// and code suggestion. + pub(super) fn maybe_recover_from_ternary_operator( + &mut self, + cond: Option, + ) -> PResult<'a, ()> { if self.prev_token != token::Question { return PResult::Ok(()); } - let lo = self.prev_token.span.lo(); + let question = self.prev_token.span; + let lo = cond.unwrap_or(question).lo(); let snapshot = self.create_snapshot_for_diagnostic(); if match self.parse_expr() { @@ -1635,11 +1627,20 @@ impl<'a> Parser<'a> { } } { if self.eat_noexpect(&token::Colon) { + let colon = self.prev_token.span; match self.parse_expr() { - Ok(_) => { - return Err(self - .dcx() - .create_err(TernaryOperator { span: self.token.span.with_lo(lo) })); + Ok(expr) => { + let sugg = cond.map(|cond| TernaryOperatorSuggestion { + before_cond: cond.shrink_to_lo(), + question, + colon, + end: expr.span.shrink_to_hi(), + }); + return Err(self.dcx().create_err(TernaryOperator { + span: self.prev_token.span.with_lo(lo), + sugg, + no_sugg: sugg.is_none(), + })); } Err(err) => { err.cancel(); @@ -1659,19 +1660,19 @@ impl<'a> Parser<'a> { self.bump(); // `+` let _bounds = self.parse_generic_bounds()?; - let sum_span = ty.span.to(self.prev_token.span); - let sub = match &ty.kind { TyKind::Ref(_lifetime, mut_ty) => { let lo = mut_ty.ty.span.shrink_to_lo(); let hi = self.prev_token.span.shrink_to_hi(); BadTypePlusSub::AddParen { suggestion: AddParen { lo, hi } } } - TyKind::Ptr(..) | TyKind::BareFn(..) => BadTypePlusSub::ForgotParen { span: sum_span }, - _ => BadTypePlusSub::ExpectPath { span: sum_span }, + TyKind::Ptr(..) | TyKind::BareFn(..) => { + BadTypePlusSub::ForgotParen { span: ty.span.to(self.prev_token.span) } + } + _ => BadTypePlusSub::ExpectPath { span: ty.span }, }; - self.dcx().emit_err(BadTypePlus { ty: pprust::ty_to_string(ty), span: sum_span, sub }); + self.dcx().emit_err(BadTypePlus { span: ty.span, sub }); Ok(()) } @@ -1945,10 +1946,7 @@ impl<'a> Parser<'a> { && self.token == token::Colon && self.look_ahead(1, |next| line_idx(self.token.span) < line_idx(next.span)) { - self.dcx().emit_err(ColonAsSemi { - span: self.token.span, - type_ascription: self.psess.unstable_features.is_nightly_build(), - }); + self.dcx().emit_err(ColonAsSemi { span: self.token.span }); self.bump(); return true; } @@ -1962,7 +1960,7 @@ impl<'a> Parser<'a> { &mut self, await_sp: Span, ) -> PResult<'a, P> { - let (hi, expr, is_question) = if self.token == token::Not { + let (hi, expr, is_question) = if self.token == token::Bang { // Handle `await!()`. self.recover_await_macro()? } else { @@ -1974,7 +1972,7 @@ impl<'a> Parser<'a> { } fn recover_await_macro(&mut self) -> PResult<'a, (Span, P, bool)> { - self.expect(exp!(Not))?; + self.expect(exp!(Bang))?; self.expect(exp!(OpenParen))?; let expr = self.parse_expr()?; self.expect(exp!(CloseParen))?; @@ -1983,7 +1981,7 @@ impl<'a> Parser<'a> { fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P, bool)> { let is_question = self.eat(exp!(Question)); // Handle `await? `. - let expr = if self.token == token::OpenDelim(Delimiter::Brace) { + let expr = if self.token == token::OpenBrace { // Handle `await { }`. // This needs to be handled separately from the next arm to avoid // interpreting `await { }?` as `?.await`. @@ -1992,7 +1990,7 @@ impl<'a> Parser<'a> { self.parse_expr() } .map_err(|mut err| { - err.span_label(await_sp, "while parsing this incorrect await expression"); + err.span_label(await_sp, format!("while parsing this incorrect await expression")); err })?; Ok((expr.span, expr, is_question)) @@ -2019,9 +2017,7 @@ impl<'a> Parser<'a> { /// If encountering `future.await()`, consumes and emits an error. pub(super) fn recover_from_await_method_call(&mut self) { - if self.token == token::OpenDelim(Delimiter::Parenthesis) - && self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis)) - { + if self.token == token::OpenParen && self.look_ahead(1, |t| t == &token::CloseParen) { // future.await() let lo = self.token.span; self.bump(); // ( @@ -2031,11 +2027,24 @@ impl<'a> Parser<'a> { self.dcx().emit_err(IncorrectUseOfAwait { span }); } } + /// + /// If encountering `x.use()`, consumes and emits an error. + pub(super) fn recover_from_use(&mut self) { + if self.token == token::OpenParen && self.look_ahead(1, |t| t == &token::CloseParen) { + // var.use() + let lo = self.token.span; + self.bump(); // ( + let span = lo.to(self.token.span); + self.bump(); // ) + + self.dcx().emit_err(IncorrectUseOfUse { span }); + } + } 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(Delimiter::Parenthesis)); //check for ( + let is_questionmark = self.look_ahead(1, |t| t == &token::Bang); //check for ! + let is_open = self.look_ahead(2, |t| t == &token::OpenParen); //check for ( if is_try && is_questionmark && is_open { let lo = self.token.span; @@ -2043,7 +2052,7 @@ impl<'a> Parser<'a> { 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(Delimiter::Parenthesis); //check if the block is empty + let is_empty = self.token == token::CloseParen; //check if the block is empty self.consume_block(exp!(OpenParen), exp!(CloseParen), ConsumeClosingDelim::No); //eat the block let hi = self.token.span; self.bump(); //remove ) @@ -2086,7 +2095,7 @@ impl<'a> Parser<'a> { ast::GenericBound::Trait(poly) => Some(poly), _ => None, }) - .last() + .next_back() { err.span_suggestion_verbose( poly.span.shrink_to_hi(), @@ -2138,7 +2147,7 @@ impl<'a> Parser<'a> { loop { debug!("recover_stmt_ loop {:?}", self.token); match self.token.kind { - token::OpenDelim(Delimiter::Brace) => { + token::OpenBrace => { brace_depth += 1; self.bump(); if break_on_block == BlockMode::Break && brace_depth == 1 && bracket_depth == 0 @@ -2146,11 +2155,11 @@ impl<'a> Parser<'a> { in_block = true; } } - token::OpenDelim(Delimiter::Bracket) => { + token::OpenBracket => { bracket_depth += 1; self.bump(); } - token::CloseDelim(Delimiter::Brace) => { + token::CloseBrace => { if brace_depth == 0 { debug!("recover_stmt_ return - close delim {:?}", self.token); break; @@ -2162,7 +2171,7 @@ impl<'a> Parser<'a> { break; } } - token::CloseDelim(Delimiter::Bracket) => { + token::CloseBracket => { bracket_depth -= 1; if bracket_depth < 0 { bracket_depth = 0; @@ -2209,12 +2218,10 @@ impl<'a> Parser<'a> { if let token::DocComment(..) = self.token.kind { self.dcx().emit_err(DocCommentOnParamType { span: self.token.span }); self.bump(); - } else if self.token == token::Pound - && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket)) - { + } else if self.token == token::Pound && self.look_ahead(1, |t| *t == token::OpenBracket) { let lo = self.token.span; // Skip every token until next possible arg. - while self.token != token::CloseDelim(Delimiter::Bracket) { + while self.token != token::CloseBracket { self.bump(); } let sp = lo.to(self.token.span); @@ -2233,9 +2240,7 @@ impl<'a> Parser<'a> { // 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(Delimiter::Parenthesis) - }) + && self.look_ahead(1, |t| *t == token::Comma || *t == token::CloseParen) { // `fn foo(String s) {}` let ident = self.parse_ident().unwrap(); @@ -2251,7 +2256,7 @@ impl<'a> Parser<'a> { } else if require_name && (self.token == token::Comma || self.token == token::Lt - || self.token == token::CloseDelim(Delimiter::Parenthesis)) + || self.token == token::CloseParen) { let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)"; @@ -2613,8 +2618,7 @@ impl<'a> Parser<'a> { || self.token == TokenKind::Dot; // This will be true when a trait object type `Foo +` or a path which was a `const fn` with // type params has been parsed. - let was_op = - matches!(self.prev_token.kind, token::BinOp(token::Plus | token::Shr) | token::Gt); + let was_op = matches!(self.prev_token.kind, token::Plus | token::Shr | token::Gt); if !is_op_or_dot && !was_op { // We perform these checks and early return to avoid taking a snapshot unnecessarily. return Err(err); @@ -2854,27 +2858,44 @@ impl<'a> Parser<'a> { first_pat } - pub(crate) fn maybe_recover_unexpected_block_label(&mut self) -> bool { + /// If `loop_header` is `Some` and an unexpected block label is encountered, + /// it is suggested to be moved just before `loop_header`, else it is suggested to be removed. + pub(crate) fn maybe_recover_unexpected_block_label( + &mut self, + loop_header: Option, + ) -> bool { // Check for `'a : {` if !(self.check_lifetime() && self.look_ahead(1, |t| *t == token::Colon) - && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace))) + && self.look_ahead(2, |t| *t == token::OpenBrace)) { return false; } let label = self.eat_label().expect("just checked if a label exists"); self.bump(); // eat `:` let span = label.ident.span.to(self.prev_token.span); - self.dcx() + let mut diag = self + .dcx() .struct_span_err(span, "block label not supported here") - .with_span_label(span, "not supported here") - .with_tool_only_span_suggestion( + .with_span_label(span, "not supported here"); + if let Some(loop_header) = loop_header { + diag.multipart_suggestion( + "if you meant to label the loop, move this label before the loop", + vec![ + (label.ident.span.until(self.token.span), String::from("")), + (loop_header.shrink_to_lo(), format!("{}: ", label.ident)), + ], + Applicability::MachineApplicable, + ); + } else { + diag.tool_only_span_suggestion( label.ident.span.until(self.token.span), "remove this block label", "", Applicability::MachineApplicable, - ) - .emit(); + ); + } + diag.emit(); true } @@ -2992,8 +3013,7 @@ impl<'a> Parser<'a> { pub(super) fn recover_vcs_conflict_marker(&mut self) { // <<<<<<< - let Some(start) = self.conflict_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) - else { + let Some(start) = self.conflict_marker(&TokenKind::Shl, &TokenKind::Lt) else { return; }; let mut spans = Vec::with_capacity(3); @@ -3008,15 +3028,13 @@ impl<'a> Parser<'a> { if self.token == TokenKind::Eof { break; } - if let Some(span) = self.conflict_marker(&TokenKind::OrOr, &TokenKind::BinOp(token::Or)) - { + if let Some(span) = self.conflict_marker(&TokenKind::OrOr, &TokenKind::Or) { middlediff3 = Some(span); } if let Some(span) = self.conflict_marker(&TokenKind::EqEq, &TokenKind::Eq) { middle = Some(span); } - if let Some(span) = self.conflict_marker(&TokenKind::BinOp(token::Shr), &TokenKind::Gt) - { + if let Some(span) = self.conflict_marker(&TokenKind::Shr, &TokenKind::Gt) { spans.push(span); end = Some(span); break; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index ef29ac2719d9c..2a7910a6af4dd 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -4,28 +4,29 @@ use core::mem; use core::ops::{Bound, ControlFlow}; use ast::mut_visit::{self, MutVisitor}; -use ast::token::{IdentIsRaw, MetaVarKind}; +use ast::token::IdentIsRaw; use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment, Recovered}; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, Token, TokenKind}; use rustc_ast::tokenstream::TokenTree; use rustc_ast::util::case::Case; use rustc_ast::util::classify; use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par}; use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{ - self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy, - ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, FnRetTy, Label, MacCall, - MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, + self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind, + BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, + FnRetTy, Label, MacCall, MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, + UnOp, UnsafeBinderCastKind, YieldKind, }; -use rustc_ast_pretty::pprust; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic}; -use rustc_lexer::unescape::unescape_char; +use rustc_literal_escaper::unescape_char; use rustc_macros::Subdiagnostic; use rustc_session::errors::{ExprParenthesesNeeded, report_lit_error}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; +use rustc_span::edition::Edition; use rustc_span::source_map::{self, Spanned}; use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; @@ -239,8 +240,8 @@ impl<'a> Parser<'a> { self.bump(); } - if self.prev_token == token::BinOp(token::Plus) - && self.token == token::BinOp(token::Plus) + if self.prev_token == token::Plus + && self.token == token::Plus && self.prev_token.span.between(self.token.span).is_empty() { let op_span = self.prev_token.span.to(self.token.span); @@ -250,8 +251,8 @@ impl<'a> Parser<'a> { continue; } - if self.prev_token == token::BinOp(token::Minus) - && self.token == token::BinOp(token::Minus) + if self.prev_token == token::Minus + && self.token == token::Minus && self.prev_token.span.between(self.token.span).is_empty() && !self.look_ahead(1, |tok| tok.can_begin_expr()) { @@ -344,7 +345,7 @@ impl<'a> Parser<'a> { fn error_found_expr_would_be_stmt(&self, lhs: &Expr) { self.dcx().emit_err(errors::FoundExprWouldBeStmt { span: self.token.span, - token: self.token.clone(), + token: self.token, suggestion: ExprParenthesesNeeded::surrounding(lhs.span), }); } @@ -359,7 +360,7 @@ impl<'a> Parser<'a> { ( Some( AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge) - | AssocOp::AssignOp(BinOpKind::Shr), + | AssocOp::AssignOp(AssignOpKind::ShrAssign), ), _, ) if self.restrictions.contains(Restrictions::CONST_EXPR) => { @@ -417,7 +418,7 @@ impl<'a> Parser<'a> { cur_op_span: Span, ) -> PResult<'a, P> { let rhs = if self.is_at_start_of_range_notation_rhs() { - let maybe_lt = self.token.clone(); + let maybe_lt = self.token; let attrs = self.parse_outer_attributes()?; Some( self.parse_expr_assoc_with(Bound::Excluded(prec), attrs) @@ -436,7 +437,7 @@ impl<'a> Parser<'a> { 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(Delimiter::Brace) { + if self.token == token::OpenBrace { return !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); } true @@ -505,23 +506,23 @@ impl<'a> Parser<'a> { // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() match this.token.uninterpolate().kind { // `!expr` - token::Not => make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Not)), + token::Bang => make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Not)), // `~expr` token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)), // `-expr` - token::BinOp(token::Minus) => { + token::Minus => { make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Neg)) } // `*expr` - token::BinOp(token::Star) => { + token::Star => { make_it!(this, attrs, |this, _| this.parse_expr_unary(lo, UnOp::Deref)) } // `&expr` and `&&expr` - token::BinOp(token::And) | token::AndAnd => { + token::And | token::AndAnd => { make_it!(this, attrs, |this, _| this.parse_expr_borrow(lo)) } // `+lit` - token::BinOp(token::Plus) if this.look_ahead(1, |tok| tok.is_numeric_lit()) => { + token::Plus if this.look_ahead(1, |tok| tok.is_numeric_lit()) => { let mut err = errors::LeadingPlusNotSupported { span: lo, remove_plus: None, @@ -541,11 +542,9 @@ impl<'a> Parser<'a> { this.parse_expr_prefix(attrs) } // Recover from `++x`: - token::BinOp(token::Plus) - if this.look_ahead(1, |t| *t == token::BinOp(token::Plus)) => - { - let starts_stmt = this.prev_token == token::Semi - || this.prev_token == token::CloseDelim(Delimiter::Brace); + token::Plus if this.look_ahead(1, |t| *t == token::Plus) => { + let starts_stmt = + this.prev_token == token::Semi || this.prev_token == token::CloseBrace; let pre_span = this.token.span.to(this.look_ahead(1, |t| t.span)); // Eat both `+`s. this.bump(); @@ -567,7 +566,11 @@ impl<'a> Parser<'a> { fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, P)> { self.bump(); let attrs = self.parse_outer_attributes()?; - let expr = self.parse_expr_prefix(attrs)?; + let expr = if self.token.is_range_separator() { + self.parse_expr_prefix_range(attrs) + } else { + self.parse_expr_prefix(attrs) + }?; let span = self.interpolated_or_expr_span(&expr); Ok((lo.to(span), expr)) } @@ -602,14 +605,14 @@ impl<'a> Parser<'a> { // 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(), + _ => t.is_metavar_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)> { - let negated_token = self.look_ahead(1, |t| t.clone()); + let negated_token = self.look_ahead(1, |t| *t); let sub_diag = if negated_token.is_numeric_lit() { errors::NotAsNegationOperatorSub::SuggestNotBitwise @@ -635,7 +638,12 @@ impl<'a> Parser<'a> { /// Returns the span of expr if it was not interpolated, or the span of the interpolated token. fn interpolated_or_expr_span(&self, expr: &Expr) -> Span { match self.prev_token.kind { - TokenKind::NtIdent(..) | TokenKind::NtLifetime(..) | TokenKind::Interpolated(..) => { + token::NtIdent(..) | token::NtLifetime(..) => self.prev_token.span, + token::CloseInvisible(InvisibleOrigin::MetaVar(_)) => { + // `expr.span` is the interpolated span, because invisible open + // and close delims both get marked with the same span, one + // that covers the entire thing between them. (See + // `rustc_expand::mbe::transcribe::transcribe`.) self.prev_token.span } _ => expr.span, @@ -723,14 +731,12 @@ impl<'a> Parser<'a> { suggestion, }) } - token::BinOp(token::Shl) => { - self.dcx().emit_err(errors::ShiftInterpretedAsGeneric { - shift: self.token.span, - r#type: path, - args: args_span, - suggestion, - }) - } + token::Shl => self.dcx().emit_err(errors::ShiftInterpretedAsGeneric { + shift: self.token.span, + r#type: path, + args: args_span, + suggestion, + }), _ => { // We can end up here even without `<` being the next token, for // example because `parse_ty_no_plus` returns `Err` on keywords, @@ -778,6 +784,7 @@ impl<'a> Parser<'a> { ExprKind::MethodCall(_) => "a method call", ExprKind::Call(_, _) => "a function call", ExprKind::Await(_, _) => "`.await`", + ExprKind::Use(_, _) => "`.use`", ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", ExprKind::Err(_) => return Ok(with_postfix), _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), @@ -821,6 +828,18 @@ impl<'a> Parser<'a> { if let Some(lt) = lifetime { self.error_remove_borrow_lifetime(span, lt.ident.span.until(expr.span)); } + + // Add expected tokens if we parsed `&raw` as an expression. + // This will make sure we see "expected `const`, `mut`", and + // guides recovery in case we write `&raw expr`. + if borrow_kind == ast::BorrowKind::Ref + && mutbl == ast::Mutability::Not + && matches!(&expr.kind, ExprKind::Path(None, p) if p.is_ident(kw::Raw)) + { + self.expected_token_types.insert(TokenType::KwMut); + self.expected_token_types.insert(TokenType::KwConst); + } + Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr))) } @@ -894,8 +913,8 @@ impl<'a> Parser<'a> { return Ok(e); } e = match self.token.kind { - token::OpenDelim(Delimiter::Parenthesis) => self.parse_expr_fn_call(lo, e), - token::OpenDelim(Delimiter::Bracket) => self.parse_expr_index(lo, e)?, + token::OpenParen => self.parse_expr_fn_call(lo, e), + token::OpenBracket => self.parse_expr_index(lo, e)?, _ => return Ok(e), } } @@ -977,12 +996,30 @@ impl<'a> Parser<'a> { } fn error_unexpected_after_dot(&self) { - let actual = pprust::token_to_string(&self.token); + let actual = super::token_descr(&self.token); let span = self.token.span; let sm = self.psess.source_map(); let (span, actual) = match (&self.token.kind, self.subparser_name) { - (token::Eof, Some(_)) if let Ok(actual) = sm.span_to_snippet(sm.next_point(span)) => { - (span.shrink_to_hi(), actual.into()) + (token::Eof, Some(_)) if let Ok(snippet) = sm.span_to_snippet(sm.next_point(span)) => { + (span.shrink_to_hi(), format!("`{}`", snippet)) + } + (token::CloseInvisible(InvisibleOrigin::MetaVar(_)), _) => { + // No need to report an error. This case will only occur when parsing a pasted + // metavariable, and we should have emitted an error when parsing the macro call in + // the first place. E.g. in this code: + // ``` + // macro_rules! m { ($e:expr) => { $e }; } + // + // fn main() { + // let f = 1; + // m!(f.); + // } + // ``` + // we'll get an error "unexpected token: `)` when parsing the `m!(f.)`, so we don't + // want to issue a second error when parsing the expansion `«f.»` (where `«`/`»` + // represent the invisible delimiters). + self.dcx().span_delayed_bug(span, "bad dot expr in metavariable"); + return; } _ => (span, actual), }; @@ -1166,7 +1203,7 @@ impl<'a> Parser<'a> { } } - if matches!(self.token.kind, token::CloseDelim(..) | token::Comma) { + if self.token.kind.close_delim().is_some() || self.token.kind == token::Comma { break; } else if trailing_dot.is_none() { // This loop should only repeat if there is a trailing dot. @@ -1196,7 +1233,7 @@ impl<'a> Parser<'a> { /// Parse a function call expression, `expr(...)`. fn parse_expr_fn_call(&mut self, lo: Span, fun: P) -> P { - let snapshot = if self.token == token::OpenDelim(Delimiter::Parenthesis) { + let snapshot = if self.token == token::OpenParen { Some((self.create_snapshot_for_diagnostic(), fun.kind.clone())) } else { None @@ -1292,10 +1329,16 @@ impl<'a> Parser<'a> { /// 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().at_least_rust_2018() && self.eat_keyword(exp!(Await)) { + if self.token_uninterpolated_span().at_least_rust_2018() && self.eat_keyword(exp!(Await)) { return Ok(self.mk_await_expr(self_arg, lo)); } + if self.eat_keyword(exp!(Use)) { + let use_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::ergonomic_clones, use_span); + return Ok(self.mk_use_expr(self_arg, lo)); + } + // Post-fix match if self.eat_keyword(exp!(Match)) { let match_span = self.prev_token.span; @@ -1303,6 +1346,15 @@ impl<'a> Parser<'a> { return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix); } + // Parse a postfix `yield`. + if self.eat_keyword(exp!(Yield)) { + let yield_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::yield_expr, yield_span); + return Ok( + self.mk_expr(lo.to(yield_span), ExprKind::Yield(YieldKind::Postfix(self_arg))) + ); + } + let fn_span_lo = self.token.span; let mut seg = self.parse_path_segment(PathStyle::Expr, None)?; self.check_trailing_angle_brackets(&seg, &[exp!(OpenParen)]); @@ -1345,23 +1397,34 @@ impl<'a> Parser<'a> { maybe_recover_from_interpolated_ty_qpath!(self, true); let span = self.token.span; - if let token::Interpolated(nt) = &self.token.kind { - match &**nt { - token::NtExpr(e) | token::NtLiteral(e) => { - let e = e.clone(); - self.bump(); - return Ok(e); + if let Some(expr) = self.eat_metavar_seq_with_matcher( + |mv_kind| matches!(mv_kind, MetaVarKind::Expr { .. }), + |this| { + // Force collection (as opposed to just `parse_expr`) is required to avoid the + // attribute duplication seen in #138478. + let expr = this.parse_expr_force_collect(); + // FIXME(nnethercote) Sometimes with expressions we get a trailing comma, possibly + // related to the FIXME in `collect_tokens_for_expr`. Examples are the multi-line + // `assert_eq!` calls involving arguments annotated with `#[rustfmt::skip]` in + // `compiler/rustc_index/src/bit_set/tests.rs`. + if this.token.kind == token::Comma { + this.bump(); } - token::NtBlock(block) => { - let block = block.clone(); - self.bump(); - return Ok(self.mk_expr(self.prev_token.span, ExprKind::Block(block, None))); - } - _ => {} - }; - } else if let Some(path) = self.eat_metavar_seq(MetaVarKind::Path, |this| { - this.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type)) - }) { + expr + }, + ) { + return Ok(expr); + } else if let Some(lit) = + self.eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus()) + { + return Ok(lit); + } else if let Some(block) = + self.eat_metavar_seq(MetaVarKind::Block, |this| this.parse_block()) + { + return Ok(self.mk_expr(span, ExprKind::Block(block, None))); + } else if let Some(path) = + self.eat_metavar_seq(MetaVarKind::Path, |this| this.parse_path(PathStyle::Type)) + { return Ok(self.mk_expr(span, ExprKind::Path(None, path))); } @@ -1397,6 +1460,7 @@ impl<'a> Parser<'a> { } else if this.check_path() { this.parse_expr_path_start() } else if this.check_keyword(exp!(Move)) + || this.check_keyword(exp!(Use)) || this.check_keyword(exp!(Static)) || this.check_const_closure() { @@ -1454,9 +1518,9 @@ impl<'a> Parser<'a> { this.parse_expr_let(restrictions) } else if this.eat_keyword(exp!(Underscore)) { Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore)) - } else if this.token.uninterpolated_span().at_least_rust_2018() { + } else if this.token_uninterpolated_span().at_least_rust_2018() { // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. - if this.token.uninterpolated_span().at_least_rust_2024() + if this.token_uninterpolated_span().at_least_rust_2024() // check for `gen {}` and `gen move {}` // or `async gen {}` and `async gen move {}` && (this.is_gen_block(kw::Gen, 0) @@ -1555,7 +1619,7 @@ impl<'a> Parser<'a> { } fn parse_expr_path_start(&mut self) -> PResult<'a, P> { - let maybe_eq_tok = self.prev_token.clone(); + let maybe_eq_tok = self.prev_token; let (qself, path) = if self.eat_lt() { let lt_span = self.prev_token.span; let (qself, path) = self.parse_qpath(PathStyle::Expr).map_err(|mut err| { @@ -1574,7 +1638,7 @@ impl<'a> Parser<'a> { }; // `!`, as an operator, is prefix, so we know this isn't that. - let (span, kind) = if self.eat(exp!(Not)) { + let (span, kind) = if self.eat(exp!(Bang)) { // MACRO INVOCATION expression if qself.is_some() { self.dcx().emit_err(errors::MacroInvocationWithQualifiedPath(path.span)); @@ -1613,14 +1677,11 @@ impl<'a> Parser<'a> { self.parse_expr_for(label, lo) } else if self.eat_keyword(exp!(Loop)) { self.parse_expr_loop(label, lo) - } else if self.check_noexpect(&token::OpenDelim(Delimiter::Brace)) - || self.token.is_whole_block() - { + } else if self.check_noexpect(&token::OpenBrace) || self.token.is_metavar_block() { self.parse_expr_block(label, lo, BlockCheckMode::Default) } else if !ate_colon && self.may_recover() - && (matches!(self.token.kind, token::CloseDelim(_) | token::Comma) - || self.token.is_punct()) + && (self.token.kind.close_delim().is_some() || self.token.is_punct()) && could_be_unclosed_char_literal(label_.ident) { let (lit, _) = @@ -1815,19 +1876,21 @@ impl<'a> Parser<'a> { }, }); Some(lexpr) - } else if self.token != token::OpenDelim(Delimiter::Brace) + } else if self.token != token::OpenBrace || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) { let mut expr = self.parse_expr_opt()?; if let Some(expr) = &mut expr { if label.is_some() - && matches!( - expr.kind, + && match &expr.kind { ExprKind::While(_, _, None) - | ExprKind::ForLoop { label: None, .. } - | ExprKind::Loop(_, None, _) - | ExprKind::Block(_, None) - ) + | ExprKind::ForLoop { label: None, .. } + | ExprKind::Loop(_, None, _) => true, + ExprKind::Block(block, None) => { + matches!(block.rules, BlockCheckMode::Default) + } + _ => false, + } { self.psess.buffer_lint( BREAK_WITH_LABEL_AND_LOOP, @@ -1877,7 +1940,7 @@ impl<'a> Parser<'a> { /// Parse `"yield" expr?`. fn parse_expr_yield(&mut self) -> PResult<'a, P> { let lo = self.prev_token.span; - let kind = ExprKind::Yield(self.parse_expr_opt()?); + let kind = ExprKind::Yield(YieldKind::Prefix(self.parse_expr_opt()?)); let span = lo.to(self.prev_token.span); self.psess.gated_spans.gate(sym::yield_expr, span); let expr = self.mk_expr(span, kind); @@ -1951,7 +2014,7 @@ impl<'a> Parser<'a> { // Eat tokens until the macro call ends. if self.may_recover() { - while !matches!(self.token.kind, token::CloseDelim(..) | token::Eof) { + while !self.token.kind.is_close_delim_or_eof() { self.bump(); } } @@ -2016,7 +2079,7 @@ impl<'a> Parser<'a> { &mut self, mk_lit_char: impl FnOnce(Symbol, Span) -> L, ) -> PResult<'a, L> { - let token = self.token.clone(); + let token = self.token; let err = |self_: &Self| { let msg = format!("unexpected token: {}", super::token_descr(&token)); self_.dcx().struct_span_err(token.span, msg) @@ -2045,87 +2108,110 @@ impl<'a> Parser<'a> { .or_else(|()| self.handle_missing_lit(Parser::mk_meta_item_lit_char)) } - fn recover_after_dot(&mut self) -> Option { - let mut recovered = None; + fn recover_after_dot(&mut self) { 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| { + let recovered = self.look_ahead(1, |next_token| { + // If it's an integer that looks like a float, then recover as such. + // + // We will never encounter the exponent part of a floating + // point literal here, since there's no use of the exponent + // syntax that also constitutes a valid integer, so we need + // not check for that. if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = next_token.kind + && suffix.is_none_or(|s| s == sym::f32 || s == sym::f64) + && symbol.as_str().chars().all(|c| c.is_numeric() || c == '_') + && self.token.span.hi() == next_token.span.lo() { - // If this integer looks like a float, then recover as such. - // - // We will never encounter the exponent part of a floating - // point literal here, since there's no use of the exponent - // syntax that also constitutes a valid integer, so we need - // not check for that. - if suffix.is_none_or(|s| s == sym::f32 || s == sym::f64) - && symbol.as_str().chars().all(|c| c.is_numeric() || c == '_') - && 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))); - } + let s = String::from("0.") + symbol.as_str(); + let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix); + Some(Token::new(kind, self.token.span.to(next_token.span))) + } else { + None } - None }); - if let Some(token) = &recovered { - self.bump(); + if let Some(recovered) = recovered { self.dcx().emit_err(errors::FloatLiteralRequiresIntegerPart { - span: token.span, - suggestion: token.span.shrink_to_lo(), + span: recovered.span, + suggestion: recovered.span.shrink_to_lo(), }); + self.bump(); + self.token = recovered; } } + } - recovered + /// Keep this in sync with `Token::can_begin_literal_maybe_minus` and + /// `Lit::from_token` (excluding unary negation). + fn eat_token_lit(&mut self) -> Option { + let check_expr = |expr: P| { + if let ast::ExprKind::Lit(token_lit) = expr.kind { + Some(token_lit) + } else if let ast::ExprKind::Unary(UnOp::Neg, inner) = &expr.kind + && let ast::Expr { kind: ast::ExprKind::Lit(_), .. } = **inner + { + None + } else { + panic!("unexpected reparsed expr/literal: {:?}", expr.kind); + } + }; + match self.token.uninterpolate().kind { + token::Ident(name, IdentIsRaw::No) if name.is_bool_lit() => { + self.bump(); + Some(token::Lit::new(token::Bool, name, None)) + } + token::Literal(token_lit) => { + self.bump(); + Some(token_lit) + } + token::OpenInvisible(InvisibleOrigin::MetaVar(MetaVarKind::Literal)) => { + let lit = self + .eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus()) + .expect("metavar seq literal"); + check_expr(lit) + } + token::OpenInvisible(InvisibleOrigin::MetaVar( + mv_kind @ MetaVarKind::Expr { can_begin_literal_maybe_minus: true, .. }, + )) => { + let expr = self + .eat_metavar_seq(mv_kind, |this| this.parse_expr()) + .expect("metavar seq expr"); + check_expr(expr) + } + _ => None, + } } /// Matches `lit = true | false | token_lit`. /// Returns `None` if the next token is not a literal. - pub(super) fn parse_opt_token_lit(&mut self) -> Option<(token::Lit, Span)> { - let recovered = self.recover_after_dot(); - let token = recovered.as_ref().unwrap_or(&self.token); - let span = token.span; - - token::Lit::from_token(token).map(|token_lit| { - self.bump(); - (token_lit, span) - }) + fn parse_opt_token_lit(&mut self) -> Option<(token::Lit, Span)> { + self.recover_after_dot(); + let span = self.token.span; + self.eat_token_lit().map(|token_lit| (token_lit, span)) } /// Matches `lit = true | false | token_lit`. /// Returns `None` if the next token is not a literal. - pub(super) fn parse_opt_meta_item_lit(&mut self) -> Option { - let recovered = self.recover_after_dot(); - let token = recovered.as_ref().unwrap_or(&self.token); - match token::Lit::from_token(token) { - Some(lit) => { - match MetaItemLit::from_token_lit(lit, token.span) { - Ok(lit) => { - self.bump(); - Some(lit) - } - Err(err) => { - let span = token.uninterpolated_span(); - self.bump(); - let guar = report_lit_error(self.psess, 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(guar), symbol, lit.suffix); - Some( - MetaItemLit::from_token_lit(lit, span) - .unwrap_or_else(|_| unreachable!()), - ) - } + fn parse_opt_meta_item_lit(&mut self) -> Option { + self.recover_after_dot(); + let span = self.token.span; + let uninterpolated_span = self.token_uninterpolated_span(); + self.eat_token_lit().map(|token_lit| { + match MetaItemLit::from_token_lit(token_lit, span) { + Ok(lit) => lit, + Err(err) => { + let guar = report_lit_error(&self.psess, err, token_lit, uninterpolated_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(token_lit.kind, token_lit.symbol, None); + let symbol = Symbol::intern(&suffixless_lit.to_string()); + let token_lit = token::Lit::new(token::Err(guar), symbol, token_lit.suffix); + MetaItemLit::from_token_lit(token_lit, uninterpolated_span).unwrap() } } - None => None, - } + }) } pub(super) fn expect_no_tuple_index_suffix(&self, span: Span, suffix: Symbol) { @@ -2149,9 +2235,10 @@ impl<'a> Parser<'a> { /// 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> { - if let token::Interpolated(nt) = &self.token.kind { - match &**nt { - // FIXME(nnethercote) The `NtExpr` case should only match if + if let Some(expr) = self.eat_metavar_seq_with_matcher( + |mv_kind| matches!(mv_kind, MetaVarKind::Expr { .. }), + |this| { + // FIXME(nnethercote) The `expr` case should only match if // `e` is an `ExprKind::Lit` or an `ExprKind::Unary` containing // an `UnOp::Neg` and an `ExprKind::Lit`, like how // `can_begin_literal_maybe_minus` works. But this method has @@ -2161,13 +2248,14 @@ impl<'a> Parser<'a> { // `ExprKind::Path` must be accepted when parsing range // patterns. That requires some care. So for now, we continue // being less strict here than we should be. - token::NtExpr(e) | token::NtLiteral(e) => { - let e = e.clone(); - self.bump(); - return Ok(e); - } - _ => {} - }; + this.parse_expr() + }, + ) { + return Ok(expr); + } else if let Some(lit) = + self.eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus()) + { + return Ok(lit); } let lo = self.token.span; @@ -2183,7 +2271,9 @@ impl<'a> Parser<'a> { } fn is_array_like_block(&mut self) -> bool { - self.look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_))) + self.token.kind == TokenKind::OpenBrace + && self + .look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_))) && self.look_ahead(2, |t| t == &token::Comma) && self.look_ahead(3, |t| t.can_begin_expr()) } @@ -2195,9 +2285,9 @@ impl<'a> Parser<'a> { let mut snapshot = self.create_snapshot_for_diagnostic(); match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) { Ok(arr) => { - let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfSpaces { + let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfBraces { span: arr.span, - sub: errors::ArrayBracketsInsteadOfSpacesSugg { + sub: errors::ArrayBracketsInsteadOfBracesSugg { left: lo, right: snapshot.prev_token.span, }, @@ -2234,8 +2324,8 @@ impl<'a> Parser<'a> { |p| p.parse_expr(), ) { Ok(_) - // When the close delim is `)`, `token.kind` is expected to be `token::CloseDelim(Delimiter::Parenthesis)`, - // but the actual `token.kind` is `token::CloseDelim(Delimiter::Bracket)`. + // When the close delim is `)`, `token.kind` is expected to be `token::CloseParen`, + // but the actual `token.kind` is `token::CloseBracket`. // This is because the `token.kind` of the close delim is treated as the same as // that of the open delim in `TokenTreesReader::parse_token_tree`, even if the delimiters of them are different. // Therefore, `token.kind` should not be compared here. @@ -2268,7 +2358,7 @@ impl<'a> Parser<'a> { } } - if self.token.is_whole_block() { + if self.token.is_metavar_block() { self.dcx().emit_err(errors::InvalidBlockMacroSegment { span: self.token.span, context: lo.to(self.token.span), @@ -2279,7 +2369,7 @@ impl<'a> Parser<'a> { }); } - let (attrs, blk) = self.parse_block_common(lo, blk_mode, true)?; + let (attrs, blk) = self.parse_block_common(lo, blk_mode, None)?; Ok(self.mk_expr_with_attrs(blk.span, ExprKind::Block(blk, opt_label), attrs)) } @@ -2293,7 +2383,7 @@ impl<'a> Parser<'a> { fn parse_expr_closure(&mut self) -> PResult<'a, P> { let lo = self.token.span; - let before = self.prev_token.clone(); + let before = self.prev_token; let binder = if self.check_keyword(exp!(For)) { let lo = self.token.span; let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?; @@ -2311,7 +2401,7 @@ impl<'a> Parser<'a> { let movability = if self.eat_keyword(exp!(Static)) { Movability::Static } else { Movability::Movable }; - let coroutine_kind = if self.token.uninterpolated_span().at_least_rust_2018() { + let coroutine_kind = if self.token_uninterpolated_span().at_least_rust_2018() { self.parse_coroutine_kind(Case::Sensitive) } else { None @@ -2320,23 +2410,21 @@ impl<'a> Parser<'a> { let capture_clause = self.parse_capture_clause()?; let (fn_decl, fn_arg_span) = self.parse_fn_block_decl()?; let decl_hi = self.prev_token.span; - let mut body = match fn_decl.output { + let mut body = match &fn_decl.output { + // No return type. FnRetTy::Default(_) => { let restrictions = self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET; - let prev = self.prev_token.clone(); - let token = self.token.clone(); + let prev = self.prev_token; + let token = self.token; let attrs = self.parse_outer_attributes()?; match self.parse_expr_res(restrictions, attrs) { Ok((expr, _)) => expr, Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?, } } - _ => { - // If an explicit return type is given, require a block to appear (RFC 968). - let body_lo = self.token.span; - self.parse_expr_block(None, body_lo, BlockCheckMode::Default)? - } + // Explicit return type (`->`) needs block `-> T { }`. + FnRetTy::Ty(ty) => self.parse_closure_block_body(ty.span)?, }; match coroutine_kind { @@ -2388,7 +2476,50 @@ impl<'a> Parser<'a> { Ok(closure) } - /// Parses an optional `move` prefix to a closure-like construct. + /// If an explicit return type is given, require a block to appear (RFC 968). + fn parse_closure_block_body(&mut self, ret_span: Span) -> PResult<'a, P> { + if self.may_recover() + && self.token.can_begin_expr() + && self.token.kind != TokenKind::OpenBrace + && !self.token.is_metavar_block() + { + let snapshot = self.create_snapshot_for_diagnostic(); + let restrictions = + self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET; + let tok = self.token.clone(); + match self.parse_expr_res(restrictions, AttrWrapper::empty()) { + Ok((expr, _)) => { + let descr = super::token_descr(&tok); + let mut diag = self + .dcx() + .struct_span_err(tok.span, format!("expected `{{`, found {descr}")); + diag.span_label( + ret_span, + "explicit return type requires closure body to be enclosed in braces", + ); + diag.multipart_suggestion_verbose( + "wrap the expression in curly braces", + vec![ + (expr.span.shrink_to_lo(), "{ ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MachineApplicable, + ); + diag.emit(); + return Ok(expr); + } + Err(diag) => { + diag.cancel(); + self.restore_snapshot(snapshot); + } + } + } + + let body_lo = self.token.span; + self.parse_expr_block(None, body_lo, BlockCheckMode::Default) + } + + /// Parses an optional `move` or `use` prefix to a closure-like construct. fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> { if self.eat_keyword(exp!(Move)) { let move_kw_span = self.prev_token.span; @@ -2401,6 +2532,16 @@ impl<'a> Parser<'a> { } else { Ok(CaptureBy::Value { move_kw: move_kw_span }) } + } else if self.eat_keyword(exp!(Use)) { + let use_kw_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::ergonomic_clones, use_kw_span); + // Check for `use async` and recover + if self.check_keyword(exp!(Async)) { + let use_async_span = self.token.span.with_lo(self.prev_token.span.data().lo); + Err(self.dcx().create_err(errors::AsyncUseOrderIncorrect { span: use_async_span })) + } else { + Ok(CaptureBy::Use { use_kw: use_kw_span }) + } } else { Ok(CaptureBy::Ref) } @@ -2462,7 +2603,10 @@ impl<'a> Parser<'a> { /// Parses an `if` expression (`if` token already eaten). fn parse_expr_if(&mut self) -> PResult<'a, P> { let lo = self.prev_token.span; - let cond = self.parse_expr_cond()?; + // Scoping code checks the top level edition of the `if`; let's match it here. + // The `CondChecker` also checks the edition of the `let` itself, just to make sure. + let let_chains_policy = LetChainsPolicy::EditionDependent { current_edition: lo.edition() }; + let cond = self.parse_expr_cond(let_chains_policy)?; self.parse_if_after_cond(lo, cond) } @@ -2522,7 +2666,7 @@ impl<'a> Parser<'a> { } } else { let attrs = self.parse_outer_attributes()?; // For recovery. - let maybe_fatarrow = self.token.clone(); + let maybe_fatarrow = self.token; let block = if self.check(exp!(OpenBrace)) { self.parse_block()? } else if let Some(block) = recover_block_from_condition(self) { @@ -2571,17 +2715,17 @@ impl<'a> Parser<'a> { } /// Parses the condition of a `if` or `while` expression. - fn parse_expr_cond(&mut self) -> PResult<'a, P> { + /// + /// The specified `edition` in `let_chains_policy` should be that of the whole `if` construct, + /// i.e. the same span we use to later decide whether the drop behaviour should be that of + /// edition `..=2021` or that of `2024..`. + // Public because it is used in rustfmt forks such as https://github.com/tucant/rustfmt/blob/30c83df9e1db10007bdd16dafce8a86b404329b2/src/parse/macros/html.rs#L57 for custom if expressions. + pub fn parse_expr_cond(&mut self, let_chains_policy: LetChainsPolicy) -> PResult<'a, P> { let attrs = self.parse_outer_attributes()?; let (mut cond, _) = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?; - CondChecker::new(self).visit_expr(&mut cond); - - if let ExprKind::Let(_, _, _, Recovered::No) = cond.kind { - // Remove the last feature gating of a `let` expression since it's stable. - self.psess.gated_spans.ungate_last(sym::let_chains, cond.span); - } + CondChecker::new(self, let_chains_policy).visit_expr(&mut cond); Ok(cond) } @@ -2595,7 +2739,7 @@ impl<'a> Parser<'a> { missing_let: None, comparison: None, }; - if self.prev_token == token::BinOp(token::Or) { + if self.prev_token == token::Or { // This was part of a closure, the that part of the parser recover. return Err(self.dcx().create_err(err)); } else { @@ -2741,7 +2885,7 @@ impl<'a> Parser<'a> { } fn parse_for_head(&mut self) -> PResult<'a, (P, P)> { - let begin_paren = if self.token == token::OpenDelim(Delimiter::Parenthesis) { + let begin_paren = if self.token == token::OpenParen { // Record whether we are about to parse `for (`. // This is used below for recovery in case of `for ( $stuff ) $block` // in which case we will suggest `for $stuff $block`. @@ -2775,7 +2919,7 @@ impl<'a> Parser<'a> { return Err(err); } }; - return if self.token == token::CloseDelim(Delimiter::Parenthesis) { + return if self.token == token::CloseParen { // We know for sure we have seen `for ($SOMETHING in $EXPR)`, so we recover the // parser state and emit a targeted suggestion. let span = vec![start_span, self.token.span]; @@ -2808,7 +2952,7 @@ impl<'a> Parser<'a> { /// Parses `for await? in ` (`for` token already eaten). fn parse_expr_for(&mut self, opt_label: Option, + } + + let obj = B { foo: Cell::new(true), bar: RefCell::new(A { baz: 2 }) }; + check_round_trip(vec![obj]); +} diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs deleted file mode 100644 index aef55d15e60de..0000000000000 --- a/compiler/rustc_serialize/tests/leb128.rs +++ /dev/null @@ -1,95 +0,0 @@ -use rustc_serialize::Decoder; -use rustc_serialize::leb128::*; -use rustc_serialize::opaque::{MAGIC_END_BYTES, MemDecoder}; - -macro_rules! impl_test_unsigned_leb128 { - ($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => { - #[test] - fn $test_name() { - // Test 256 evenly spaced values of integer range, - // integer max value, and some "random" numbers. - let mut values = Vec::new(); - - let increment = (1 as $int_ty) << ($int_ty::BITS - 8); - values.extend((0..256).map(|i| $int_ty::MIN + i * increment)); - - values.push($int_ty::MAX); - - values.extend( - (-500..500).map(|i| (i as $int_ty).wrapping_mul(0x12345789ABCDEFu64 as $int_ty)), - ); - - let mut stream = Vec::new(); - - let mut buf = Default::default(); - for &x in &values { - let n = $write_fn_name(&mut buf, x); - stream.extend(&buf[..n]); - } - let stream_end = stream.len(); - stream.extend(MAGIC_END_BYTES); - - let mut decoder = MemDecoder::new(&stream, 0).unwrap(); - for &expected in &values { - let actual = $read_fn_name(&mut decoder); - assert_eq!(expected, actual); - } - assert_eq!(stream_end, decoder.position()); - } - }; -} - -impl_test_unsigned_leb128!(test_u16_leb128, write_u16_leb128, read_u16_leb128, u16); -impl_test_unsigned_leb128!(test_u32_leb128, write_u32_leb128, read_u32_leb128, u32); -impl_test_unsigned_leb128!(test_u64_leb128, write_u64_leb128, read_u64_leb128, u64); -impl_test_unsigned_leb128!(test_u128_leb128, write_u128_leb128, read_u128_leb128, u128); -impl_test_unsigned_leb128!(test_usize_leb128, write_usize_leb128, read_usize_leb128, usize); - -macro_rules! impl_test_signed_leb128 { - ($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => { - #[test] - fn $test_name() { - // Test 256 evenly spaced values of integer range, - // integer max value, and some "random" numbers. - let mut values = Vec::new(); - - let mut value = $int_ty::MIN; - let increment = (1 as $int_ty) << ($int_ty::BITS - 8); - - for _ in 0..256 { - values.push(value); - // The addition in the last loop iteration overflows. - value = value.wrapping_add(increment); - } - - values.push($int_ty::MAX); - - values.extend( - (-500..500).map(|i| (i as $int_ty).wrapping_mul(0x12345789ABCDEFi64 as $int_ty)), - ); - - let mut stream = Vec::new(); - - let mut buf = Default::default(); - for &x in &values { - let n = $write_fn_name(&mut buf, x); - stream.extend(&buf[..n]); - } - let stream_end = stream.len(); - stream.extend(MAGIC_END_BYTES); - - let mut decoder = MemDecoder::new(&stream, 0).unwrap(); - for &expected in &values { - let actual = $read_fn_name(&mut decoder); - assert_eq!(expected, actual); - } - assert_eq!(stream_end, decoder.position()); - } - }; -} - -impl_test_signed_leb128!(test_i16_leb128, write_i16_leb128, read_i16_leb128, i16); -impl_test_signed_leb128!(test_i32_leb128, write_i32_leb128, read_i32_leb128, i32); -impl_test_signed_leb128!(test_i64_leb128, write_i64_leb128, read_i64_leb128, i64); -impl_test_signed_leb128!(test_i128_leb128, write_i128_leb128, read_i128_leb128, i128); -impl_test_signed_leb128!(test_isize_leb128, write_isize_leb128, read_isize_leb128, isize); diff --git a/compiler/rustc_serialize/tests/opaque.rs b/compiler/rustc_serialize/tests/opaque.rs deleted file mode 100644 index 0543e176ae5aa..0000000000000 --- a/compiler/rustc_serialize/tests/opaque.rs +++ /dev/null @@ -1,296 +0,0 @@ -#![allow(rustc::internal)] - -use std::fmt::Debug; -use std::fs; - -use rustc_macros::{Decodable_Generic, Encodable_Generic}; -use rustc_serialize::opaque::{FileEncoder, MemDecoder}; -use rustc_serialize::{Decodable, Encodable}; - -#[derive(PartialEq, Clone, Debug, Encodable_Generic, Decodable_Generic)] -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, - p: bool, - q: Option, -} - -fn check_round_trip< - T: Encodable + for<'a> Decodable> + PartialEq + Debug, ->( - values: Vec, -) { - let tmpfile = tempfile::NamedTempFile::new().unwrap(); - let tmpfile = tmpfile.path(); - - let mut encoder = FileEncoder::new(&tmpfile).unwrap(); - for value in &values { - Encodable::encode(value, &mut encoder); - } - encoder.finish().unwrap(); - - let data = fs::read(&tmpfile).unwrap(); - let mut decoder = MemDecoder::new(&data[..], 0).unwrap(); - for value in values { - let decoded = Decodable::decode(&mut decoder); - 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, 111, 3333, 55555, 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, -100, 0, 101, 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_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(), - 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(), - p: true, - q: Some(1234567), - }]); -} - -#[derive(PartialEq, Clone, Debug, Encodable_Generic, Decodable_Generic)] -enum Enum { - Variant1, - Variant2(usize, u32), - Variant3 { a: i32, b: char, c: bool }, -} - -#[test] -fn test_enum() { - check_round_trip(vec![ - Enum::Variant1, - Enum::Variant2(1, 25), - 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, 5u32)]); - check_round_trip(vec![(9i8, 10u16, 15i64)]); - check_round_trip(vec![(-12i16, 11u8, 12usize)]); - check_round_trip(vec![(1234567isize, 100000000000000u64, 99999999999999i64)]); - check_round_trip(vec![(String::new(), "some string".to_string())]); -} - -#[test] -fn test_unit_like_struct() { - #[derive(Encodable_Generic, Decodable_Generic, PartialEq, Debug)] - struct UnitLikeStruct; - - check_round_trip(vec![UnitLikeStruct]); -} - -#[test] -fn test_box() { - #[derive(Encodable_Generic, Decodable_Generic, PartialEq, Debug)] - struct A { - foo: Box<[bool]>, - } - - let obj = A { foo: Box::new([true, false]) }; - check_round_trip(vec![obj]); -} - -#[test] -fn test_cell() { - use std::cell::{Cell, RefCell}; - - #[derive(Encodable_Generic, Decodable_Generic, PartialEq, Debug)] - struct A { - baz: isize, - } - - #[derive(Encodable_Generic, Decodable_Generic, PartialEq, Debug)] - struct B { - foo: Cell, - bar: RefCell, - } - - let obj = B { foo: Cell::new(true), bar: RefCell::new(A { baz: 2 }) }; - check_round_trip(vec![obj]); -} diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index a087725d34dd1..f0ee19e3c6774 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" # tidy-alphabetical-start bitflags = "2.4.1" getopts = "0.2" +rand = "0.9.0" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } @@ -32,7 +33,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.59.0" +version = "0.61.0" features = [ "Win32_Foundation", "Win32_System_LibraryLoader", diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 74b8087e07769..528c52eace7cb 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -1,3 +1,9 @@ +session_apple_deployment_target_invalid = + failed to parse deployment target specified in {$env_var}: {$error} + +session_apple_deployment_target_too_low = + deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min} + session_binary_float_literal_not_supported = binary float literal is not supported session_branch_protection_requires_aarch64 = `-Zbranch-protection` is only supported on aarch64 @@ -94,6 +100,8 @@ session_sanitizer_cfi_requires_lto = `-Zsanitizer=cfi` requires `-Clto` or `-Cli session_sanitizer_cfi_requires_single_codegen_unit = `-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1` +session_sanitizer_kcfi_arity_requires_kcfi = `-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi` + session_sanitizer_kcfi_requires_panic_abort = `-Z sanitizer=kcfi` requires `-C panic=abort` session_sanitizer_not_supported = {$us} sanitizer is not supported for this target diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index b4597ae2515d4..80603b4a1567f 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -72,7 +72,9 @@ pub struct TypeSizeInfo { #[derive(Default)] pub struct CodeStats { - type_sizes: Lock>, + /// The hash set that actually holds all the type size information. + /// The field is public for use in external tools. See #139876. + pub type_sizes: Lock>, } impl CodeStats { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 7586c5766b585..144aeb5c369cc 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -14,6 +14,7 @@ use std::str::{self, FromStr}; use std::sync::LazyLock; use std::{cmp, fmt, fs, iter}; +use externs::{ExternOpt, split_extern_opt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey}; use rustc_errors::emitter::HumanReadableErrorType; @@ -23,7 +24,8 @@ use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION}; use rustc_span::source_map::FilePathMapping; use rustc_span::{ - FileName, FileNameDisplayPreference, RealFileName, SourceFileHashAlgorithm, Symbol, sym, + FileName, FileNameDisplayPreference, FileNameEmbeddablePreference, RealFileName, + SourceFileHashAlgorithm, Symbol, sym, }; use rustc_target::spec::{ FramePointer, LinkSelfContainedComponents, LinkerFeatures, SplitDebuginfo, Target, TargetTuple, @@ -39,9 +41,38 @@ use crate::utils::CanonicalizedPath; use crate::{EarlyDiagCtxt, HashStableContext, Session, filesearch, lint}; mod cfg; +mod externs; mod native_libs; pub mod sigpipe; +pub const PRINT_KINDS: &[(&str, PrintKind)] = &[ + // tidy-alphabetical-start + ("all-target-specs-json", PrintKind::AllTargetSpecsJson), + ("calling-conventions", PrintKind::CallingConventions), + ("cfg", PrintKind::Cfg), + ("check-cfg", PrintKind::CheckCfg), + ("code-models", PrintKind::CodeModels), + ("crate-name", PrintKind::CrateName), + ("crate-root-lint-levels", PrintKind::CrateRootLintLevels), + ("deployment-target", PrintKind::DeploymentTarget), + ("file-names", PrintKind::FileNames), + ("host-tuple", PrintKind::HostTuple), + ("link-args", PrintKind::LinkArgs), + ("native-static-libs", PrintKind::NativeStaticLibs), + ("relocation-models", PrintKind::RelocationModels), + ("split-debuginfo", PrintKind::SplitDebuginfo), + ("stack-protector-strategies", PrintKind::StackProtectorStrategies), + ("supported-crate-types", PrintKind::SupportedCrateTypes), + ("sysroot", PrintKind::Sysroot), + ("target-cpus", PrintKind::TargetCPUs), + ("target-features", PrintKind::TargetFeatures), + ("target-libdir", PrintKind::TargetLibdir), + ("target-list", PrintKind::TargetList), + ("target-spec-json", PrintKind::TargetSpecJson), + ("tls-models", PrintKind::TlsModels), + // tidy-alphabetical-end +]; + /// The different settings that the `-C strip` flag can have. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum Strip { @@ -164,6 +195,11 @@ pub struct CoverageOptions { /// regression tests for #133606, because we don't have an easy way to /// reproduce it from actual source code. pub discard_all_spans_in_codegen: bool, + + /// `-Zcoverage-options=inject-unused-local-file`: During codegen, add an + /// extra dummy entry to each function's local file table, to exercise the + /// code that checks for local file IDs with no mapping regions. + pub inject_unused_local_file: bool, } /// Controls whether branch coverage or MC/DC coverage is enabled. @@ -209,11 +245,17 @@ pub enum AutoDiff { PrintPerf, /// Print intermediate IR generation steps PrintSteps, - /// Print the whole module, before running opts. + /// Print the module, before running autodiff. PrintModBefore, - /// Print the module after Enzyme differentiated everything. + /// Print the module after running autodiff. PrintModAfter, + /// Print the module after running autodiff and optimizations. + PrintModFinal, + /// Print all passes scheduled by LLVM + PrintPasses, + /// Disable extra opt run after running autodiff + NoPostopt, /// Enzyme's loose type debug helper (can cause incorrect gradients!!) /// Usable in cases where Enzyme errors with `can not deduce type of X`. LooseTypes, @@ -536,129 +578,214 @@ impl FromStr for SplitDwarfKind { } } -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, HashStable_Generic)] -#[derive(Encodable, Decodable)] -pub enum OutputType { - /// This is the optimized bitcode, which could be either pre-LTO or non-LTO bitcode, - /// depending on the specific request type. - Bitcode, - /// This is the summary or index data part of the ThinLTO bitcode. - ThinLinkBitcode, - Assembly, - LlvmAssembly, - Mir, - Metadata, - Object, - Exe, - DepInfo, -} +macro_rules! define_output_types { + ( + $( + $(#[doc = $doc:expr])* + $Variant:ident => { + shorthand: $shorthand:expr, + extension: $extension:expr, + description: $description:expr, + default_filename: $default_filename:expr, + is_text: $is_text:expr, + compatible_with_cgus_and_single_output: $compatible:expr + } + ),* $(,)? + ) => { + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, HashStable_Generic)] + #[derive(Encodable, Decodable)] + pub enum OutputType { + $( + $(#[doc = $doc])* + $Variant, + )* + } -impl StableOrd for OutputType { - const CAN_USE_UNSTABLE_SORT: bool = true; - // Trivial C-Style enums have a stable sort order across compilation sessions. - const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); -} + impl StableOrd for OutputType { + const CAN_USE_UNSTABLE_SORT: bool = true; -impl ToStableHashKey for OutputType { - type KeyType = Self; + // Trivial C-Style enums have a stable sort order across compilation sessions. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); + } - fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { - *self - } -} + impl ToStableHashKey for OutputType { + type KeyType = Self; -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::ThinLinkBitcode - | OutputType::Assembly - | OutputType::LlvmAssembly - | OutputType::Mir - | OutputType::Object => false, + fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { + *self + } } - } - pub fn shorthand(&self) -> &'static str { - match *self { - OutputType::Bitcode => "llvm-bc", - OutputType::ThinLinkBitcode => "thin-link-bitcode", - 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, - "thin-link-bitcode" => OutputType::ThinLinkBitcode, - "obj" => OutputType::Object, - "metadata" => OutputType::Metadata, - "link" => OutputType::Exe, - "dep-info" => OutputType::DepInfo, - _ => return None, - }) - } - fn shorthands_display() -> String { - format!( - "`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`", - OutputType::Bitcode.shorthand(), - OutputType::ThinLinkBitcode.shorthand(), - OutputType::Assembly.shorthand(), - OutputType::LlvmAssembly.shorthand(), - OutputType::Mir.shorthand(), - OutputType::Object.shorthand(), - OutputType::Metadata.shorthand(), - OutputType::Exe.shorthand(), - OutputType::DepInfo.shorthand(), - ) - } + impl OutputType { + pub fn iter_all() -> impl Iterator { + static ALL_VARIANTS: &[OutputType] = &[ + $( + OutputType::$Variant, + )* + ]; + ALL_VARIANTS.iter().copied() + } + + fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool { + match *self { + $( + OutputType::$Variant => $compatible, + )* + } + } + + pub fn shorthand(&self) -> &'static str { + match *self { + $( + OutputType::$Variant => $shorthand, + )* + } + } + + fn from_shorthand(shorthand: &str) -> Option { + match shorthand { + $( + s if s == $shorthand => Some(OutputType::$Variant), + )* + _ => None, + } + } + + fn shorthands_display() -> String { + let shorthands = vec![ + $( + format!("`{}`", $shorthand), + )* + ]; + shorthands.join(", ") + } + + pub fn extension(&self) -> &'static str { + match *self { + $( + OutputType::$Variant => $extension, + )* + } + } + + pub fn is_text_output(&self) -> bool { + match *self { + $( + OutputType::$Variant => $is_text, + )* + } + } + + pub fn description(&self) -> &'static str { + match *self { + $( + OutputType::$Variant => $description, + )* + } + } + + pub fn default_filename(&self) -> &'static str { + match *self { + $( + OutputType::$Variant => $default_filename, + )* + } + } - pub fn extension(&self) -> &'static str { - match *self { - OutputType::Bitcode => "bc", - OutputType::ThinLinkBitcode => "indexing.o", - OutputType::Assembly => "s", - OutputType::LlvmAssembly => "ll", - OutputType::Mir => "mir", - OutputType::Object => "o", - OutputType::Metadata => "rmeta", - OutputType::DepInfo => "d", - OutputType::Exe => "", - } - } - pub fn is_text_output(&self) -> bool { - match *self { - OutputType::Assembly - | OutputType::LlvmAssembly - | OutputType::Mir - | OutputType::DepInfo => true, - OutputType::Bitcode - | OutputType::ThinLinkBitcode - | OutputType::Object - | OutputType::Metadata - | OutputType::Exe => false, } } } +define_output_types! { + Assembly => { + shorthand: "asm", + extension: "s", + description: "Generates a file with the crate's assembly code", + default_filename: "CRATE_NAME.s", + is_text: true, + compatible_with_cgus_and_single_output: false + }, + #[doc = "This is the optimized bitcode, which could be either pre-LTO or non-LTO bitcode,"] + #[doc = "depending on the specific request type."] + Bitcode => { + shorthand: "llvm-bc", + extension: "bc", + description: "Generates a binary file containing the LLVM bitcode", + default_filename: "CRATE_NAME.bc", + is_text: false, + compatible_with_cgus_and_single_output: false + }, + DepInfo => { + shorthand: "dep-info", + extension: "d", + description: "Generates a file with Makefile syntax that indicates all the source files that were loaded to generate the crate", + default_filename: "CRATE_NAME.d", + is_text: true, + compatible_with_cgus_and_single_output: true + }, + Exe => { + shorthand: "link", + extension: "", + description: "Generates the crates specified by --crate-type. This is the default if --emit is not specified", + default_filename: "(platform and crate-type dependent)", + is_text: false, + compatible_with_cgus_and_single_output: true + }, + LlvmAssembly => { + shorthand: "llvm-ir", + extension: "ll", + description: "Generates a file containing LLVM IR", + default_filename: "CRATE_NAME.ll", + is_text: true, + compatible_with_cgus_and_single_output: false + }, + Metadata => { + shorthand: "metadata", + extension: "rmeta", + description: "Generates a file containing metadata about the crate", + default_filename: "libCRATE_NAME.rmeta", + is_text: false, + compatible_with_cgus_and_single_output: true + }, + Mir => { + shorthand: "mir", + extension: "mir", + description: "Generates a file containing rustc's mid-level intermediate representation", + default_filename: "CRATE_NAME.mir", + is_text: true, + compatible_with_cgus_and_single_output: false + }, + Object => { + shorthand: "obj", + extension: "o", + description: "Generates a native object file", + default_filename: "CRATE_NAME.o", + is_text: false, + compatible_with_cgus_and_single_output: false + }, + #[doc = "This is the summary or index data part of the ThinLTO bitcode."] + ThinLinkBitcode => { + shorthand: "thin-link-bitcode", + extension: "indexing.o", + description: "Generates the ThinLTO summary as bitcode", + default_filename: "CRATE_NAME.indexing.o", + is_text: false, + compatible_with_cgus_and_single_output: false + }, +} + /// The type of diagnostics output to generate. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum ErrorOutputType { /// Output meant for the consumption of humans. - HumanReadable(HumanReadableErrorType, ColorConfig), + #[default] + HumanReadable { + kind: HumanReadableErrorType = HumanReadableErrorType::Default, + color_config: ColorConfig = ColorConfig::Auto, + }, /// 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). @@ -670,12 +797,6 @@ pub enum ErrorOutputType { }, } -impl Default for ErrorOutputType { - fn default() -> Self { - Self::HumanReadable(HumanReadableErrorType::Default, ColorConfig::Auto) - } -} - #[derive(Clone, Hash, Debug)] pub enum ResolveDocLinks { /// Do not resolve doc links. @@ -849,41 +970,40 @@ pub struct PrintRequest { #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum PrintKind { + // tidy-alphabetical-start + AllTargetSpecsJson, + CallingConventions, + Cfg, + CheckCfg, + CodeModels, + CrateName, + CrateRootLintLevels, + DeploymentTarget, FileNames, HostTuple, + LinkArgs, + NativeStaticLibs, + RelocationModels, + SplitDebuginfo, + StackProtectorStrategies, + SupportedCrateTypes, Sysroot, - TargetLibdir, - CrateName, - Cfg, - CheckCfg, - CallingConventions, - TargetList, TargetCPUs, TargetFeatures, - RelocationModels, - CodeModels, + TargetLibdir, + TargetList, + TargetSpecJson, TlsModels, - TargetSpec, - AllTargetSpecs, - NativeStaticLibs, - StackProtectorStrategies, - LinkArgs, - SplitDebuginfo, - DeploymentTarget, + // tidy-alphabetical-end } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)] pub struct NextSolverConfig { /// Whether the new trait solver should be enabled in coherence. - pub coherence: bool, + pub coherence: bool = true, /// Whether the new trait solver should be enabled everywhere. /// This is only `true` if `coherence` is also enabled. - pub globally: bool, -} -impl Default for NextSolverConfig { - fn default() -> Self { - NextSolverConfig { coherence: true, globally: false } - } + pub globally: bool = false, } #[derive(Clone)] @@ -988,11 +1108,14 @@ impl OutFileName { &self, outputs: &OutputFilenames, flavor: OutputType, - codegen_unit_name: Option<&str>, + codegen_unit_name: &str, + invocation_temp: Option<&str>, ) -> PathBuf { match *self { OutFileName::Real(ref path) => path.clone(), - OutFileName::Stdout => outputs.temp_path(flavor, codegen_unit_name), + OutFileName::Stdout => { + outputs.temp_path_for_cgu(flavor, codegen_unit_name, invocation_temp) + } } } @@ -1052,6 +1175,10 @@ impl OutputFilenames { .unwrap_or_else(|| OutFileName::Real(self.output_path(flavor))) } + pub fn interface_path(&self) -> PathBuf { + self.out_directory.join(format!("lib{}.rs", self.crate_stem)) + } + /// Gets the output path where a compilation artifact of the given type /// should be placed on disk. fn output_path(&self, flavor: OutputType) -> PathBuf { @@ -1067,40 +1194,59 @@ impl OutputFilenames { /// 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 { + pub fn temp_path_for_cgu( + &self, + flavor: OutputType, + codegen_unit_name: &str, + invocation_temp: Option<&str>, + ) -> PathBuf { let extension = flavor.extension(); - self.temp_path_ext(extension, codegen_unit_name) + self.temp_path_ext_for_cgu(extension, codegen_unit_name, invocation_temp) } /// Like `temp_path`, but specifically for dwarf objects. - pub fn temp_path_dwo(&self, codegen_unit_name: Option<&str>) -> PathBuf { - self.temp_path_ext(DWARF_OBJECT_EXT, codegen_unit_name) + pub fn temp_path_dwo_for_cgu( + &self, + codegen_unit_name: &str, + invocation_temp: Option<&str>, + ) -> PathBuf { + self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp) } /// 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(); + pub fn temp_path_ext_for_cgu( + &self, + ext: &str, + codegen_unit_name: &str, + invocation_temp: Option<&str>, + ) -> PathBuf { + let mut extension = codegen_unit_name.to_string(); - if let Some(codegen_unit_name) = codegen_unit_name { - extension.push_str(codegen_unit_name); + // Append `.{invocation_temp}` to ensure temporary files are unique. + if let Some(rng) = invocation_temp { + extension.push('.'); + extension.push_str(rng); } + // FIXME: This is sketchy that we're not appending `.rcgu` when the ext is empty. + // Append `.rcgu.{ext}`. if !ext.is_empty() { - if !extension.is_empty() { - extension.push('.'); - extension.push_str(RUST_CGU_EXT); - extension.push('.'); - } - + extension.push('.'); + extension.push_str(RUST_CGU_EXT); + extension.push('.'); extension.push_str(ext); } let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory); - self.with_directory_and_extension(temps_directory, &extension) } + pub fn temp_path_for_diagnostic(&self, ext: &str) -> PathBuf { + let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory); + self.with_directory_and_extension(temps_directory, &ext) + } + pub fn with_extension(&self, extension: &str) -> PathBuf { self.with_directory_and_extension(&self.out_directory, extension) } @@ -1117,10 +1263,11 @@ impl OutputFilenames { &self, split_debuginfo_kind: SplitDebuginfo, split_dwarf_kind: SplitDwarfKind, - cgu_name: Option<&str>, + cgu_name: &str, + invocation_temp: Option<&str>, ) -> Option { - let obj_out = self.temp_path(OutputType::Object, cgu_name); - let dwo_out = self.temp_path_dwo(cgu_name); + let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp); + let dwo_out = self.temp_path_dwo_for_cgu(cgu_name, invocation_temp); match (split_debuginfo_kind, split_dwarf_kind) { (SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None, // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes @@ -1179,6 +1326,11 @@ fn file_path_mapping( } else { FileNameDisplayPreference::Local }, + if unstable_opts.remap_path_scope.is_all() { + FileNameEmbeddablePreference::RemappedOnly + } else { + FileNameEmbeddablePreference::LocalAndRemapped + }, ) } @@ -1195,7 +1347,7 @@ impl Default for Options { describe_lints: false, output_types: OutputTypes(BTreeMap::new()), search_paths: vec![], - maybe_sysroot: None, + sysroot: filesearch::materialize_sysroot(None), target_triple: TargetTuple::from_tuple(host_tuple()), test: false, incremental: None, @@ -1315,13 +1467,17 @@ pub enum CrateType { Staticlib, Cdylib, ProcMacro, + Sdylib, } impl CrateType { pub fn has_metadata(self) -> bool { match self { CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true, - CrateType::Executable | CrateType::Cdylib | CrateType::Staticlib => false, + CrateType::Executable + | CrateType::Cdylib + | CrateType::Staticlib + | CrateType::Sdylib => false, } } } @@ -1400,10 +1556,12 @@ pub fn build_target_config( } target } - Err(e) => early_dcx.early_fatal(format!( - "Error loading target specification: {e}. \ - Run `rustc --print target-list` for a list of built-in targets" - )), + Err(e) => { + let mut err = + early_dcx.early_struct_fatal(format!("error loading target specification: {e}")); + err.help("run `rustc --print target-list` for a list of built-in targets"); + err.emit(); + } } } @@ -1508,8 +1666,35 @@ The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE ) }); +static PRINT_HELP: LazyLock = LazyLock::new(|| { + format!( + "Compiler information to print on stdout (or to a file)\n\ + INFO may be one of <{}>.", + PRINT_KINDS.iter().map(|(name, _)| format!("{name}")).collect::>().join("|") + ) +}); + +static EMIT_HELP: LazyLock = LazyLock::new(|| { + let mut result = + String::from("Comma separated list of types of output for the compiler to emit.\n"); + result.push_str("Each TYPE has the default FILE name:\n"); + + for output in OutputType::iter_all() { + result.push_str(&format!("* {} - {}\n", output.shorthand(), output.default_filename())); + } + + result +}); + /// Returns all rustc command line options, including metadata for /// each option, such as whether the option is stable. +/// +/// # Option style guidelines +/// +/// - ``: Indicates a required parameter +/// - `[param]`: Indicates an optional parameter +/// - `|`: Indicates a mutually exclusive option +/// - `*`: a list element with description pub fn rustc_optgroups() -> Vec { use OptionKind::{Flag, FlagMulti, Multi, Opt}; use OptionStability::{Stable, Unstable}; @@ -1524,18 +1709,18 @@ pub fn rustc_optgroups() -> Vec { "", "cfg", "Configure the compilation environment.\n\ - SPEC supports the syntax `NAME[=\"VALUE\"]`.", - "SPEC", + SPEC supports the syntax `[=\"\"]`.", + "", ), - opt(Stable, Multi, "", "check-cfg", "Provide list of expected cfgs for checking", "SPEC"), + opt(Stable, Multi, "", "check-cfg", "Provide list of expected cfgs for checking", ""), opt( Stable, Multi, "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", + The optional KIND can be one of (default: all).", + "[=]", ), opt( Stable, @@ -1544,63 +1729,46 @@ pub fn rustc_optgroups() -> Vec { "", "Link the generated crate(s) to the specified native\n\ library NAME. The optional KIND can be one of\n\ - static, framework, or dylib (the default).\n\ + (default: dylib).\n\ Optional comma separated MODIFIERS\n\ - (bundle|verbatim|whole-archive|as-needed)\n\ + \n\ may be specified each with a prefix of either '+' to\n\ enable or '-' to disable.", - "[KIND[:MODIFIERS]=]NAME[:RENAME]", + "[[:]=][:]", ), make_crate_type_option(), - opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", "NAME"), + opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", ""), opt(Stable, Opt, "", "edition", &EDITION_STRING, EDITION_NAME_LIST), - opt( - Stable, - Multi, - "", - "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( - Stable, - Multi, - "", - "print", - "Compiler information to print on stdout", - "[crate-name|file-names|sysroot|target-libdir|cfg|check-cfg|calling-conventions|\ - target-list|target-cpus|target-features|relocation-models|code-models|\ - tls-models|target-spec-json|all-target-specs-json|native-static-libs|\ - stack-protector-strategies|link-args|deployment-target]", - ), + opt(Stable, Multi, "", "emit", &EMIT_HELP, "[=]"), + opt(Stable, Multi, "", "print", &PRINT_HELP, "[=]"), opt(Stable, FlagMulti, "g", "", "Equivalent to -C debuginfo=2", ""), opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=3", ""), - opt(Stable, Opt, "o", "", "Write output to ", "FILENAME"), - opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in